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 */
copyin(struct openpromio * info,struct openpromio ** opp_p)69 static int copyin(struct openpromio *info, struct openpromio **opp_p)
70 {
71 unsigned 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
getstrings(struct openpromio * info,struct openpromio ** opp_p)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 */
copyout(void * info,struct openpromio * opp,int len)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 */
openprom_sunos_ioctl(struct inode * inode,struct file * file,unsigned int cmd,unsigned long arg,int node)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. */
intree(int root,int node)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". */
goodnode(int n,DATA * data)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. */
copyin_string(char * user,size_t len,char ** ptr)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 */
openprom_bsd_ioctl(struct inode * inode,struct file * file,unsigned int cmd,unsigned long arg)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 */
openprom_ioctl(struct inode * inode,struct file * file,unsigned int cmd,unsigned long arg)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
openprom_open(struct inode * inode,struct file * file)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
openprom_release(struct inode * inode,struct file * file)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
openprom_init(void)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
openprom_cleanup(void)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");
660