File: /usr/src/linux/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 tmp = kmalloc(len + 1, GFP_KERNEL);
338 if (!tmp)
339 return -ENOMEM;
340
341 if(copy_from_user(tmp, user, len)) {
342 kfree(tmp);
343 return -EFAULT;
344 }
345
346 tmp[len] = '\0';
347
348 *ptr = tmp;
349
350 return 0;
351 }
352
353 /*
354 * NetBSD /dev/openprom ioctl calls.
355 */
356 static int openprom_bsd_ioctl(struct inode * inode, struct file * file,
357 unsigned int cmd, unsigned long arg)
358 {
359 DATA *data = (DATA *) file->private_data;
360 struct opiocdesc op;
361 unsigned long flags;
362 int error, node, len;
363 char *str, *tmp;
364 char buffer[64];
365 static int cnt;
366
367 switch (cmd) {
368 case OPIOCGET:
369 if (copy_from_user(&op, (void *)arg, sizeof(op)))
370 return -EFAULT;
371
372 if (!goodnode(op.op_nodeid,data))
373 return -EINVAL;
374
375 error = copyin_string(op.op_name, op.op_namelen, &str);
376 if (error)
377 return error;
378
379 save_and_cli(flags);
380 len = prom_getproplen(op.op_nodeid,str);
381 restore_flags(flags);
382
383 if (len > op.op_buflen) {
384 kfree(str);
385 return -ENOMEM;
386 }
387
388 op.op_buflen = len;
389
390 if (len <= 0) {
391 kfree(str);
392 /* Verified by the above copy_from_user */
393 if (__copy_to_user((void *)arg, &op,
394 sizeof(op)))
395 return -EFAULT;
396 return 0;
397 }
398
399 tmp = kmalloc(len + 1, GFP_KERNEL);
400 if (!tmp) {
401 kfree(str);
402 return -ENOMEM;
403 }
404
405 save_and_cli(flags);
406 prom_getproperty(op.op_nodeid, str, tmp, len);
407 restore_flags(flags);
408
409 tmp[len] = '\0';
410
411 error = __copy_to_user((void *)arg, &op, sizeof(op));
412 if (!error)
413 error = copy_to_user(op.op_buf, tmp, len);
414
415 kfree(tmp);
416 kfree(str);
417
418 return error;
419
420 case OPIOCNEXTPROP:
421 if (copy_from_user(&op, (void *)arg, sizeof(op)))
422 return -EFAULT;
423
424 if (!goodnode(op.op_nodeid,data))
425 return -EINVAL;
426
427 error = copyin_string(op.op_name, op.op_namelen, &str);
428 if (error)
429 return error;
430
431 save_and_cli(flags);
432 tmp = prom_nextprop(op.op_nodeid,str,buffer);
433 restore_flags(flags);
434
435 if (tmp) {
436 len = strlen(tmp);
437 if (len > op.op_buflen)
438 len = op.op_buflen;
439 else
440 op.op_buflen = len;
441 } else {
442 len = op.op_buflen = 0;
443 }
444
445 error = verify_area(VERIFY_WRITE, (void *)arg, sizeof(op));
446 if (error) {
447 kfree(str);
448 return error;
449 }
450
451 error = verify_area(VERIFY_WRITE, op.op_buf, len);
452 if (error) {
453 kfree(str);
454 return error;
455 }
456
457 error = __copy_to_user((void *)arg, &op, sizeof(op));
458 if (!error) error = __copy_to_user(op.op_buf, tmp, len);
459
460 kfree(str);
461
462 return error;
463
464 case OPIOCSET:
465 if (copy_from_user(&op, (void *)arg, sizeof(op)))
466 return -EFAULT;
467
468 if (!goodnode(op.op_nodeid,data))
469 return -EINVAL;
470
471 error = copyin_string(op.op_name, op.op_namelen, &str);
472 if (error)
473 return error;
474
475 error = copyin_string(op.op_buf, op.op_buflen, &tmp);
476 if (error) {
477 kfree(str);
478 return error;
479 }
480
481 save_and_cli(flags);
482 len = prom_setprop(op.op_nodeid,str,tmp,op.op_buflen+1);
483 restore_flags(flags);
484
485 if (len != op.op_buflen)
486 return -EINVAL;
487
488 kfree(str);
489 kfree(tmp);
490
491 return 0;
492
493 case OPIOCGETOPTNODE:
494 if (copy_to_user((void *)arg, &options_node, sizeof(int)))
495 return -EFAULT;
496 return 0;
497
498 case OPIOCGETNEXT:
499 case OPIOCGETCHILD:
500 if (copy_from_user(&node, (void *)arg, sizeof(int)))
501 return -EFAULT;
502
503 save_and_cli(flags);
504 if (cmd == OPIOCGETNEXT)
505 node = __prom_getsibling(node);
506 else
507 node = __prom_getchild(node);
508 restore_flags(flags);
509
510 if (__copy_to_user((void *)arg, &node, sizeof(int)))
511 return -EFAULT;
512
513 return 0;
514
515 default:
516 if (cnt++ < 10)
517 printk(KERN_INFO "openprom_bsd_ioctl: cmd 0x%X\n", cmd);
518 return -EINVAL;
519
520 }
521 }
522
523
524 /*
525 * Handoff control to the correct ioctl handler.
526 */
527 static int openprom_ioctl(struct inode * inode, struct file * file,
528 unsigned int cmd, unsigned long arg)
529 {
530 DATA *data = (DATA *) file->private_data;
531 static int cnt;
532
533 switch (cmd) {
534 case OPROMGETOPT:
535 case OPROMNXTOPT:
536 if ((file->f_mode & FMODE_READ) == 0)
537 return -EPERM;
538 return openprom_sunos_ioctl(inode, file, cmd, arg,
539 options_node);
540
541 case OPROMSETOPT:
542 case OPROMSETOPT2:
543 if ((file->f_mode & FMODE_WRITE) == 0)
544 return -EPERM;
545 return openprom_sunos_ioctl(inode, file, cmd, arg,
546 options_node);
547
548 case OPROMNEXT:
549 case OPROMCHILD:
550 case OPROMGETPROP:
551 case OPROMNXTPROP:
552 if ((file->f_mode & FMODE_READ) == 0)
553 return -EPERM;
554 return openprom_sunos_ioctl(inode, file, cmd, arg,
555 data->current_node);
556
557 case OPROMU2P:
558 case OPROMGETCONS:
559 case OPROMGETFBNAME:
560 case OPROMGETBOOTARGS:
561 case OPROMSETCUR:
562 case OPROMPCI2NODE:
563 case OPROMPATH2NODE:
564 if ((file->f_mode & FMODE_READ) == 0)
565 return -EPERM;
566 return openprom_sunos_ioctl(inode, file, cmd, arg, 0);
567
568 case OPIOCGET:
569 case OPIOCNEXTPROP:
570 case OPIOCGETOPTNODE:
571 case OPIOCGETNEXT:
572 case OPIOCGETCHILD:
573 if ((file->f_mode & FMODE_READ) == 0)
574 return -EBADF;
575 return openprom_bsd_ioctl(inode,file,cmd,arg);
576
577 case OPIOCSET:
578 if ((file->f_mode & FMODE_WRITE) == 0)
579 return -EBADF;
580 return openprom_bsd_ioctl(inode,file,cmd,arg);
581
582 default:
583 if (cnt++ < 10)
584 printk("openprom_ioctl: cmd 0x%X, arg 0x%lX\n", cmd, arg);
585 return -EINVAL;
586 }
587 }
588
589 static int openprom_open(struct inode * inode, struct file * file)
590 {
591 DATA *data;
592
593 data = (DATA *) kmalloc(sizeof(DATA), GFP_KERNEL);
594 if (!data)
595 return -ENOMEM;
596
597 data->current_node = prom_root_node;
598 data->lastnode = prom_root_node;
599 file->private_data = (void *)data;
600
601 return 0;
602 }
603
604 static int openprom_release(struct inode * inode, struct file * file)
605 {
606 kfree(file->private_data);
607 return 0;
608 }
609
610 static struct file_operations openprom_fops = {
611 owner: THIS_MODULE,
612 llseek: no_llseek,
613 ioctl: openprom_ioctl,
614 open: openprom_open,
615 release: openprom_release,
616 };
617
618 static struct miscdevice openprom_dev = {
619 SUN_OPENPROM_MINOR, "openprom", &openprom_fops
620 };
621
622 EXPORT_NO_SYMBOLS;
623
624 static int __init openprom_init(void)
625 {
626 unsigned long flags;
627 int error;
628
629 error = misc_register(&openprom_dev);
630 if (error) {
631 printk(KERN_ERR "openprom: unable to get misc minor\n");
632 return error;
633 }
634
635 save_and_cli(flags);
636 options_node = prom_getchild(prom_root_node);
637 options_node = prom_searchsiblings(options_node,"options");
638 restore_flags(flags);
639
640 if (options_node == 0 || options_node == -1) {
641 printk(KERN_ERR "openprom: unable to find options node\n");
642 misc_deregister(&openprom_dev);
643 return -EIO;
644 }
645
646 return 0;
647 }
648
649 static void __exit openprom_cleanup(void)
650 {
651 misc_deregister(&openprom_dev);
652 }
653
654 module_init(openprom_init);
655 module_exit(openprom_cleanup);
656