more debug output
[linux-2.4.git] / drivers / sbus / char / openprom.c
1 /*
2  * Linux/SPARC PROM Configuration Driver
3  * Copyright (C) 1996 Thomas K. Dyas (tdyas@noc.rutgers.edu)
4  * Copyright (C) 1996 Eddie C. Dost  (ecd@skynet.be)
5  *
6  * This character device driver allows user programs to access the
7  * PROM device tree. It is compatible with the SunOS /dev/openprom
8  * driver and the NetBSD /dev/openprom driver. The SunOS eeprom
9  * utility works without any modifications.
10  *
11  * The driver uses a minor number under the misc device major. The
12  * file read/write mode determines the type of access to the PROM.
13  * Interrupts are disabled whenever the driver calls into the PROM for
14  * sanity's sake.
15  */
16
17 /* This program is free software; you can redistribute it and/or
18  * modify it under the terms of the GNU General Public License as
19  * published by the Free Software Foundation; either version 2 of the
20  * License, or (at your option) any later version.
21  *
22  * This program is distributed in the hope that it will be useful, but
23  * WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
25  * General Public License for more details.
26  *
27  * You should have received a copy of the GNU General Public License
28  * along with this program; if not, write to the Free Software
29  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
30  */
31
32 #define PROMLIB_INTERNAL
33
34 #include <linux/config.h>
35 #include <linux/module.h>
36 #include <linux/kernel.h>
37 #include <linux/sched.h>
38 #include <linux/errno.h>
39 #include <linux/slab.h>
40 #include <linux/string.h>
41 #include <linux/miscdevice.h>
42 #include <linux/init.h>
43 #include <asm/oplib.h>
44 #include <asm/system.h>
45 #include <asm/uaccess.h>
46 #include <asm/openpromio.h>
47 #ifdef CONFIG_PCI
48 #include <linux/pci.h>
49 #include <asm/pbm.h>
50 #endif
51
52 /* Private data kept by the driver for each descriptor. */
53 typedef struct openprom_private_data
54 {
55         int current_node;       /* Current node for SunOS ioctls. */
56         int lastnode;           /* Last valid node used by BSD ioctls. */
57 } DATA;
58
59 /* ID of the PROM node containing all of the EEPROM options. */
60 static int options_node = 0;
61
62 /*
63  * Copy an openpromio structure into kernel space from user space.
64  * This routine does error checking to make sure that all memory
65  * accesses are within bounds. A pointer to the allocated openpromio
66  * structure will be placed in "*opp_p". Return value is the length
67  * of the user supplied buffer.
68  */
69 static int copyin(struct openpromio *info, struct openpromio **opp_p)
70 {
71         int bufsize;
72
73         if (!info || !opp_p)
74                 return -EFAULT;
75
76         if (get_user(bufsize, &info->oprom_size))
77                 return -EFAULT;
78
79         if (bufsize == 0)
80                 return -EINVAL;
81
82         /* If the bufsize is too large, just limit it.
83          * Fix from Jason Rappleye.
84          */
85         if (bufsize > OPROMMAXPARAM)
86                 bufsize = OPROMMAXPARAM;
87
88         if (!(*opp_p = kmalloc(sizeof(int) + bufsize + 1, GFP_KERNEL)))
89                 return -ENOMEM;
90         memset(*opp_p, 0, sizeof(int) + bufsize + 1);
91
92         if (copy_from_user(&(*opp_p)->oprom_array,
93                            &info->oprom_array, bufsize)) {
94                 kfree(*opp_p);
95                 return -EFAULT;
96         }
97         return bufsize;
98 }
99
100 static int getstrings(struct openpromio *info, struct openpromio **opp_p)
101 {
102         int n, bufsize;
103         char c;
104
105         if (!info || !opp_p)
106                 return -EFAULT;
107
108         if (!(*opp_p = kmalloc(sizeof(int) + OPROMMAXPARAM + 1, GFP_KERNEL)))
109                 return -ENOMEM;
110
111         memset(*opp_p, 0, sizeof(int) + OPROMMAXPARAM + 1);
112         (*opp_p)->oprom_size = 0;
113
114         n = bufsize = 0;
115         while ((n < 2) && (bufsize < OPROMMAXPARAM)) {
116                 if (get_user(c, &info->oprom_array[bufsize])) {
117                         kfree(*opp_p);
118                         return -EFAULT;
119                 }
120                 if (c == '\0')
121                         n++;
122                 (*opp_p)->oprom_array[bufsize++] = c;
123         }
124         if (!n) {
125                 kfree(*opp_p);
126                 return -EINVAL;
127         }
128         return bufsize;
129 }
130
131 /*
132  * Copy an openpromio structure in kernel space back to user space.
133  */
134 static int copyout(void *info, struct openpromio *opp, int len)
135 {
136         if (copy_to_user(info, opp, len))
137                 return -EFAULT;
138         return 0;
139 }
140
141 /*
142  *      SunOS and Solaris /dev/openprom ioctl calls.
143  */
144 static int openprom_sunos_ioctl(struct inode * inode, struct file * file,
145                                 unsigned int cmd, unsigned long arg, int node)
146 {
147         DATA *data = (DATA *) file->private_data;
148         char buffer[OPROMMAXPARAM+1], *buf;
149         struct openpromio *opp;
150         unsigned long flags;
151         int bufsize, len, error = 0;
152         extern char saved_command_line[];
153         static int cnt;
154
155         if (cmd == OPROMSETOPT)
156                 bufsize = getstrings((void *)arg, &opp);
157         else
158                 bufsize = copyin((void *)arg, &opp);
159
160         if (bufsize < 0)
161                 return bufsize;
162
163         switch (cmd) {
164         case OPROMGETOPT:
165         case OPROMGETPROP:
166                 save_and_cli(flags);
167                 len = prom_getproplen(node, opp->oprom_array);
168                 restore_flags(flags);
169
170                 if (len <= 0 || len > bufsize) {
171                         error = copyout((void *)arg, opp, sizeof(int));
172                         break;
173                 }
174
175                 save_and_cli(flags);
176                 len = prom_getproperty(node, opp->oprom_array, buffer, bufsize);
177                 restore_flags(flags);
178
179                 memcpy(opp->oprom_array, buffer, len);
180                 opp->oprom_array[len] = '\0';
181                 opp->oprom_size = len;
182
183                 error = copyout((void *)arg, opp, sizeof(int) + bufsize);
184                 break;
185
186         case OPROMNXTOPT:
187         case OPROMNXTPROP:
188                 save_and_cli(flags);
189                 buf = prom_nextprop(node, opp->oprom_array, buffer);
190                 restore_flags(flags);
191
192                 len = strlen(buf);
193                 if (len == 0 || len + 1 > bufsize) {
194                         error = copyout((void *)arg, opp, sizeof(int));
195                         break;
196                 }
197
198                 memcpy(opp->oprom_array, buf, len);
199                 opp->oprom_array[len] = '\0';
200                 opp->oprom_size = ++len;
201
202                 error = copyout((void *)arg, opp, sizeof(int) + bufsize);
203                 break;
204
205         case OPROMSETOPT:
206         case OPROMSETOPT2:
207                 buf = opp->oprom_array + strlen(opp->oprom_array) + 1;
208                 len = opp->oprom_array + bufsize - buf;
209
210                 save_and_cli(flags);
211                 error = prom_setprop(options_node, opp->oprom_array,
212                                      buf, len);
213                 restore_flags(flags);
214
215                 if (error < 0)
216                         error = -EINVAL;
217                 break;
218
219         case OPROMNEXT:
220         case OPROMCHILD:
221         case OPROMSETCUR:
222                 if (bufsize < sizeof(int)) {
223                         error = -EINVAL;
224                         break;
225                 }
226
227                 node = *((int *) opp->oprom_array);
228
229                 save_and_cli(flags);
230                 switch (cmd) {
231                 case OPROMNEXT: node = __prom_getsibling(node); break;
232                 case OPROMCHILD: node = __prom_getchild(node); break;
233                 case OPROMSETCUR: break;
234                 }
235                 restore_flags(flags);
236
237                 data->current_node = node;
238                 *((int *)opp->oprom_array) = node;
239                 opp->oprom_size = sizeof(int);
240
241                 error = copyout((void *)arg, opp, bufsize + sizeof(int));
242                 break;
243
244         case OPROMPCI2NODE:
245                 error = -EINVAL;
246
247                 if (bufsize >= 2*sizeof(int)) {
248 #ifdef CONFIG_PCI
249                         struct pci_dev *pdev;
250                         struct pcidev_cookie *pcp;
251                         pdev = pci_find_slot (((int *) opp->oprom_array)[0],
252                                               ((int *) opp->oprom_array)[1]);
253
254                         pcp = pdev->sysdata;
255                         if (pcp != NULL && pcp->prom_node != -1 && pcp->prom_node) {
256                                 node = pcp->prom_node;
257                                 data->current_node = node;
258                                 *((int *)opp->oprom_array) = node;
259                                 opp->oprom_size = sizeof(int);
260                                 error = copyout((void *)arg, opp, bufsize + sizeof(int));
261                         }
262 #endif
263                 }
264                 break;
265
266         case OPROMPATH2NODE:
267                 save_and_cli(flags);
268                 node = prom_finddevice(opp->oprom_array);
269                 restore_flags(flags);
270                 data->current_node = node;
271                 *((int *)opp->oprom_array) = node;
272                 opp->oprom_size = sizeof(int);
273
274                 error = copyout((void *)arg, opp, bufsize + sizeof(int));
275                 break;
276
277         case OPROMGETBOOTARGS:
278                 buf = saved_command_line;
279
280                 len = strlen(buf);
281
282                 if (len > bufsize) {
283                         error = -EINVAL;
284                         break;
285                 }
286
287                 strcpy(opp->oprom_array, buf);
288                 opp->oprom_size = len;
289
290                 error = copyout((void *)arg, opp, bufsize + sizeof(int));
291                 break;
292
293         case OPROMU2P:
294         case OPROMGETCONS:
295         case OPROMGETFBNAME:
296                 if (cnt++ < 10)
297                         printk(KERN_INFO "openprom_sunos_ioctl: unimplemented ioctl\n");
298                 error = -EINVAL;
299                 break;
300         default:
301                 if (cnt++ < 10)
302                         printk(KERN_INFO "openprom_sunos_ioctl: cmd 0x%X, arg 0x%lX\n", cmd, arg);
303                 error = -EINVAL;
304                 break;
305         }
306
307         kfree(opp);
308         return error;
309 }
310
311
312 /* Return nonzero if a specific node is in the PROM device tree. */
313 static int intree(int root, int node)
314 {
315         for (; root != 0; root = prom_getsibling(root))
316                 if (root == node || intree(prom_getchild(root),node))
317                         return 1;
318         return 0;
319 }
320
321 /* Return nonzero if a specific node is "valid". */
322 static int goodnode(int n, DATA *data)
323 {
324         if (n == data->lastnode || n == prom_root_node || n == options_node)
325                 return 1;
326         if (n == 0 || n == -1 || !intree(prom_root_node,n))
327                 return 0;
328         data->lastnode = n;
329         return 1;
330 }
331
332 /* Copy in a whole string from userspace into kernelspace. */
333 static int copyin_string(char *user, size_t len, char **ptr)
334 {
335         char *tmp;
336
337         if ((ssize_t)len < 0 || (ssize_t)(len + 1) < 0)
338                 return -EINVAL;
339
340         tmp = kmalloc(len + 1, GFP_KERNEL);
341         if (!tmp)
342                 return -ENOMEM;
343
344         if(copy_from_user(tmp, user, len)) {
345                 kfree(tmp);
346                 return -EFAULT;
347         }
348
349         tmp[len] = '\0';
350
351         *ptr = tmp;
352
353         return 0;
354 }
355
356 /*
357  *      NetBSD /dev/openprom ioctl calls.
358  */
359 static int openprom_bsd_ioctl(struct inode * inode, struct file * file,
360                               unsigned int cmd, unsigned long arg)
361 {
362         DATA *data = (DATA *) file->private_data;
363         struct opiocdesc op;
364         unsigned long flags;
365         int error, node, len;
366         char *str, *tmp;
367         char buffer[64];
368         static int cnt;
369
370         switch (cmd) {
371         case OPIOCGET:
372                 if (copy_from_user(&op, (void *)arg, sizeof(op)))
373                         return -EFAULT;
374
375                 if (!goodnode(op.op_nodeid,data))
376                         return -EINVAL;
377
378                 error = copyin_string(op.op_name, op.op_namelen, &str);
379                 if (error)
380                         return error;
381
382                 save_and_cli(flags);
383                 len = prom_getproplen(op.op_nodeid,str);
384                 restore_flags(flags);
385
386                 if (len > op.op_buflen) {
387                         kfree(str);
388                         return -ENOMEM;
389                 }
390
391                 op.op_buflen = len;
392
393                 if (len <= 0) {
394                         kfree(str);
395                         /* Verified by the above copy_from_user */
396                         if (__copy_to_user((void *)arg, &op,
397                                        sizeof(op)))
398                                 return -EFAULT;
399                         return 0;
400                 }
401
402                 tmp = kmalloc(len + 1, GFP_KERNEL);
403                 if (!tmp) {
404                         kfree(str);
405                         return -ENOMEM;
406                 }
407
408                 save_and_cli(flags);
409                 prom_getproperty(op.op_nodeid, str, tmp, len);
410                 restore_flags(flags);
411
412                 tmp[len] = '\0';
413
414                 error = __copy_to_user((void *)arg, &op, sizeof(op));
415                 if (!error)
416                         error = copy_to_user(op.op_buf, tmp, len);
417
418                 kfree(tmp);
419                 kfree(str);
420
421                 return error;
422
423         case OPIOCNEXTPROP:
424                 if (copy_from_user(&op, (void *)arg, sizeof(op)))
425                         return -EFAULT;
426
427                 if (!goodnode(op.op_nodeid,data))
428                         return -EINVAL;
429
430                 error = copyin_string(op.op_name, op.op_namelen, &str);
431                 if (error)
432                         return error;
433
434                 save_and_cli(flags);
435                 tmp = prom_nextprop(op.op_nodeid,str,buffer);
436                 restore_flags(flags);
437
438                 if (tmp) {
439                         len = strlen(tmp);
440                         if (len > op.op_buflen)
441                                 len = op.op_buflen;
442                         else
443                                 op.op_buflen = len;
444                 } else {
445                         len = op.op_buflen = 0;
446                 }
447
448                 error = verify_area(VERIFY_WRITE, (void *)arg, sizeof(op));
449                 if (error) {
450                         kfree(str);
451                         return error;
452                 }
453
454                 error = verify_area(VERIFY_WRITE, op.op_buf, len);
455                 if (error) {
456                         kfree(str);
457                         return error;
458                 }
459
460                 error = __copy_to_user((void *)arg, &op, sizeof(op));
461                 if (!error) error = __copy_to_user(op.op_buf, tmp, len);
462
463                 kfree(str);
464
465                 return error;
466
467         case OPIOCSET:
468                 if (copy_from_user(&op, (void *)arg, sizeof(op)))
469                         return -EFAULT;
470
471                 if (!goodnode(op.op_nodeid,data))
472                         return -EINVAL;
473
474                 error = copyin_string(op.op_name, op.op_namelen, &str);
475                 if (error)
476                         return error;
477
478                 error = copyin_string(op.op_buf, op.op_buflen, &tmp);
479                 if (error) {
480                         kfree(str);
481                         return error;
482                 }
483
484                 save_and_cli(flags);
485                 len = prom_setprop(op.op_nodeid,str,tmp,op.op_buflen+1);
486                 restore_flags(flags);
487
488                 if (len != op.op_buflen)
489                         return -EINVAL;
490
491                 kfree(str);
492                 kfree(tmp);
493
494                 return 0;
495
496         case OPIOCGETOPTNODE:
497                 if (copy_to_user((void *)arg, &options_node, sizeof(int)))
498                         return -EFAULT;
499                 return 0;
500
501         case OPIOCGETNEXT:
502         case OPIOCGETCHILD:
503                 if (copy_from_user(&node, (void *)arg, sizeof(int)))
504                         return -EFAULT;
505
506                 save_and_cli(flags);
507                 if (cmd == OPIOCGETNEXT)
508                         node = __prom_getsibling(node);
509                 else
510                         node = __prom_getchild(node);
511                 restore_flags(flags);
512
513                 if (__copy_to_user((void *)arg, &node, sizeof(int)))
514                         return -EFAULT;
515
516                 return 0;
517
518         default:
519                 if (cnt++ < 10)
520                         printk(KERN_INFO "openprom_bsd_ioctl: cmd 0x%X\n", cmd);
521                 return -EINVAL;
522
523         }
524 }
525
526
527 /*
528  *      Handoff control to the correct ioctl handler.
529  */
530 static int openprom_ioctl(struct inode * inode, struct file * file,
531                           unsigned int cmd, unsigned long arg)
532 {
533         DATA *data = (DATA *) file->private_data;
534         static int cnt;
535
536         switch (cmd) {
537         case OPROMGETOPT:
538         case OPROMNXTOPT:
539                 if ((file->f_mode & FMODE_READ) == 0)
540                         return -EPERM;
541                 return openprom_sunos_ioctl(inode, file, cmd, arg,
542                                             options_node);
543
544         case OPROMSETOPT:
545         case OPROMSETOPT2:
546                 if ((file->f_mode & FMODE_WRITE) == 0)
547                         return -EPERM;
548                 return openprom_sunos_ioctl(inode, file, cmd, arg,
549                                             options_node);
550
551         case OPROMNEXT:
552         case OPROMCHILD:
553         case OPROMGETPROP:
554         case OPROMNXTPROP:
555                 if ((file->f_mode & FMODE_READ) == 0)
556                         return -EPERM;
557                 return openprom_sunos_ioctl(inode, file, cmd, arg,
558                                             data->current_node);
559
560         case OPROMU2P:
561         case OPROMGETCONS:
562         case OPROMGETFBNAME:
563         case OPROMGETBOOTARGS:
564         case OPROMSETCUR:
565         case OPROMPCI2NODE:
566         case OPROMPATH2NODE:
567                 if ((file->f_mode & FMODE_READ) == 0)
568                         return -EPERM;
569                 return openprom_sunos_ioctl(inode, file, cmd, arg, 0);
570
571         case OPIOCGET:
572         case OPIOCNEXTPROP:
573         case OPIOCGETOPTNODE:
574         case OPIOCGETNEXT:
575         case OPIOCGETCHILD:
576                 if ((file->f_mode & FMODE_READ) == 0)
577                         return -EBADF;
578                 return openprom_bsd_ioctl(inode,file,cmd,arg);
579
580         case OPIOCSET:
581                 if ((file->f_mode & FMODE_WRITE) == 0)
582                         return -EBADF;
583                 return openprom_bsd_ioctl(inode,file,cmd,arg);
584
585         default:
586                 if (cnt++ < 10)
587                         printk("openprom_ioctl: cmd 0x%X, arg 0x%lX\n", cmd, arg);
588                 return -EINVAL;
589         }
590 }
591
592 static int openprom_open(struct inode * inode, struct file * file)
593 {
594         DATA *data;
595
596         data = (DATA *) kmalloc(sizeof(DATA), GFP_KERNEL);
597         if (!data)
598                 return -ENOMEM;
599
600         data->current_node = prom_root_node;
601         data->lastnode = prom_root_node;
602         file->private_data = (void *)data;
603
604         return 0;
605 }
606
607 static int openprom_release(struct inode * inode, struct file * file)
608 {
609         kfree(file->private_data);
610         return 0;
611 }
612
613 static struct file_operations openprom_fops = {
614         owner:          THIS_MODULE,
615         llseek:         no_llseek,
616         ioctl:          openprom_ioctl,
617         open:           openprom_open,
618         release:        openprom_release,
619 };
620
621 static struct miscdevice openprom_dev = {
622         SUN_OPENPROM_MINOR, "openprom", &openprom_fops
623 };
624
625 EXPORT_NO_SYMBOLS;
626
627 static int __init openprom_init(void)
628 {
629         unsigned long flags;
630         int error;
631
632         error = misc_register(&openprom_dev);
633         if (error) {
634                 printk(KERN_ERR "openprom: unable to get misc minor\n");
635                 return error;
636         }
637
638         save_and_cli(flags);
639         options_node = prom_getchild(prom_root_node);
640         options_node = prom_searchsiblings(options_node,"options");
641         restore_flags(flags);
642
643         if (options_node == 0 || options_node == -1) {
644                 printk(KERN_ERR "openprom: unable to find options node\n");
645                 misc_deregister(&openprom_dev);
646                 return -EIO;
647         }
648
649         return 0;
650 }
651
652 static void __exit openprom_cleanup(void)
653 {
654         misc_deregister(&openprom_dev);
655 }
656
657 module_init(openprom_init);
658 module_exit(openprom_cleanup);
659 MODULE_LICENSE("GPL");