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)
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.
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
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.
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.
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.
32 #define PROMLIB_INTERNAL
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>
48 #include <linux/pci.h>
52 /* Private data kept by the driver for each descriptor. */
53 typedef struct openprom_private_data
55 int current_node; /* Current node for SunOS ioctls. */
56 int lastnode; /* Last valid node used by BSD ioctls. */
59 /* ID of the PROM node containing all of the EEPROM options. */
60 static int options_node = 0;
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.
69 static int copyin(struct openpromio *info, struct openpromio **opp_p)
76 if (get_user(bufsize, &info->oprom_size))
82 /* If the bufsize is too large, just limit it.
83 * Fix from Jason Rappleye.
85 if (bufsize > OPROMMAXPARAM)
86 bufsize = OPROMMAXPARAM;
88 if (!(*opp_p = kmalloc(sizeof(int) + bufsize + 1, GFP_KERNEL)))
90 memset(*opp_p, 0, sizeof(int) + bufsize + 1);
92 if (copy_from_user(&(*opp_p)->oprom_array,
93 &info->oprom_array, bufsize)) {
100 static int getstrings(struct openpromio *info, struct openpromio **opp_p)
108 if (!(*opp_p = kmalloc(sizeof(int) + OPROMMAXPARAM + 1, GFP_KERNEL)))
111 memset(*opp_p, 0, sizeof(int) + OPROMMAXPARAM + 1);
112 (*opp_p)->oprom_size = 0;
115 while ((n < 2) && (bufsize < OPROMMAXPARAM)) {
116 if (get_user(c, &info->oprom_array[bufsize])) {
122 (*opp_p)->oprom_array[bufsize++] = c;
132 * Copy an openpromio structure in kernel space back to user space.
134 static int copyout(void *info, struct openpromio *opp, int len)
136 if (copy_to_user(info, opp, len))
142 * SunOS and Solaris /dev/openprom ioctl calls.
144 static int openprom_sunos_ioctl(struct inode * inode, struct file * file,
145 unsigned int cmd, unsigned long arg, int node)
147 DATA *data = (DATA *) file->private_data;
148 char buffer[OPROMMAXPARAM+1], *buf;
149 struct openpromio *opp;
151 int bufsize, len, error = 0;
152 extern char saved_command_line[];
155 if (cmd == OPROMSETOPT)
156 bufsize = getstrings((void *)arg, &opp);
158 bufsize = copyin((void *)arg, &opp);
167 len = prom_getproplen(node, opp->oprom_array);
168 restore_flags(flags);
170 if (len <= 0 || len > bufsize) {
171 error = copyout((void *)arg, opp, sizeof(int));
176 len = prom_getproperty(node, opp->oprom_array, buffer, bufsize);
177 restore_flags(flags);
179 memcpy(opp->oprom_array, buffer, len);
180 opp->oprom_array[len] = '\0';
181 opp->oprom_size = len;
183 error = copyout((void *)arg, opp, sizeof(int) + bufsize);
189 buf = prom_nextprop(node, opp->oprom_array, buffer);
190 restore_flags(flags);
193 if (len == 0 || len + 1 > bufsize) {
194 error = copyout((void *)arg, opp, sizeof(int));
198 memcpy(opp->oprom_array, buf, len);
199 opp->oprom_array[len] = '\0';
200 opp->oprom_size = ++len;
202 error = copyout((void *)arg, opp, sizeof(int) + bufsize);
207 buf = opp->oprom_array + strlen(opp->oprom_array) + 1;
208 len = opp->oprom_array + bufsize - buf;
211 error = prom_setprop(options_node, opp->oprom_array,
213 restore_flags(flags);
222 if (bufsize < sizeof(int)) {
227 node = *((int *) opp->oprom_array);
231 case OPROMNEXT: node = __prom_getsibling(node); break;
232 case OPROMCHILD: node = __prom_getchild(node); break;
233 case OPROMSETCUR: break;
235 restore_flags(flags);
237 data->current_node = node;
238 *((int *)opp->oprom_array) = node;
239 opp->oprom_size = sizeof(int);
241 error = copyout((void *)arg, opp, bufsize + sizeof(int));
247 if (bufsize >= 2*sizeof(int)) {
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]);
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));
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);
274 error = copyout((void *)arg, opp, bufsize + sizeof(int));
277 case OPROMGETBOOTARGS:
278 buf = saved_command_line;
287 strcpy(opp->oprom_array, buf);
288 opp->oprom_size = len;
290 error = copyout((void *)arg, opp, bufsize + sizeof(int));
297 printk(KERN_INFO "openprom_sunos_ioctl: unimplemented ioctl\n");
302 printk(KERN_INFO "openprom_sunos_ioctl: cmd 0x%X, arg 0x%lX\n", cmd, arg);
312 /* Return nonzero if a specific node is in the PROM device tree. */
313 static int intree(int root, int node)
315 for (; root != 0; root = prom_getsibling(root))
316 if (root == node || intree(prom_getchild(root),node))
321 /* Return nonzero if a specific node is "valid". */
322 static int goodnode(int n, DATA *data)
324 if (n == data->lastnode || n == prom_root_node || n == options_node)
326 if (n == 0 || n == -1 || !intree(prom_root_node,n))
332 /* Copy in a whole string from userspace into kernelspace. */
333 static int copyin_string(char *user, size_t len, char **ptr)
337 if ((ssize_t)len < 0 || (ssize_t)(len + 1) < 0)
340 tmp = kmalloc(len + 1, GFP_KERNEL);
344 if(copy_from_user(tmp, user, len)) {
357 * NetBSD /dev/openprom ioctl calls.
359 static int openprom_bsd_ioctl(struct inode * inode, struct file * file,
360 unsigned int cmd, unsigned long arg)
362 DATA *data = (DATA *) file->private_data;
365 int error, node, len;
372 if (copy_from_user(&op, (void *)arg, sizeof(op)))
375 if (!goodnode(op.op_nodeid,data))
378 error = copyin_string(op.op_name, op.op_namelen, &str);
383 len = prom_getproplen(op.op_nodeid,str);
384 restore_flags(flags);
386 if (len > op.op_buflen) {
395 /* Verified by the above copy_from_user */
396 if (__copy_to_user((void *)arg, &op,
402 tmp = kmalloc(len + 1, GFP_KERNEL);
409 prom_getproperty(op.op_nodeid, str, tmp, len);
410 restore_flags(flags);
414 error = __copy_to_user((void *)arg, &op, sizeof(op));
416 error = copy_to_user(op.op_buf, tmp, len);
424 if (copy_from_user(&op, (void *)arg, sizeof(op)))
427 if (!goodnode(op.op_nodeid,data))
430 error = copyin_string(op.op_name, op.op_namelen, &str);
435 tmp = prom_nextprop(op.op_nodeid,str,buffer);
436 restore_flags(flags);
440 if (len > op.op_buflen)
445 len = op.op_buflen = 0;
448 error = verify_area(VERIFY_WRITE, (void *)arg, sizeof(op));
454 error = verify_area(VERIFY_WRITE, op.op_buf, len);
460 error = __copy_to_user((void *)arg, &op, sizeof(op));
461 if (!error) error = __copy_to_user(op.op_buf, tmp, len);
468 if (copy_from_user(&op, (void *)arg, sizeof(op)))
471 if (!goodnode(op.op_nodeid,data))
474 error = copyin_string(op.op_name, op.op_namelen, &str);
478 error = copyin_string(op.op_buf, op.op_buflen, &tmp);
485 len = prom_setprop(op.op_nodeid,str,tmp,op.op_buflen+1);
486 restore_flags(flags);
488 if (len != op.op_buflen)
496 case OPIOCGETOPTNODE:
497 if (copy_to_user((void *)arg, &options_node, sizeof(int)))
503 if (copy_from_user(&node, (void *)arg, sizeof(int)))
507 if (cmd == OPIOCGETNEXT)
508 node = __prom_getsibling(node);
510 node = __prom_getchild(node);
511 restore_flags(flags);
513 if (__copy_to_user((void *)arg, &node, sizeof(int)))
520 printk(KERN_INFO "openprom_bsd_ioctl: cmd 0x%X\n", cmd);
528 * Handoff control to the correct ioctl handler.
530 static int openprom_ioctl(struct inode * inode, struct file * file,
531 unsigned int cmd, unsigned long arg)
533 DATA *data = (DATA *) file->private_data;
539 if ((file->f_mode & FMODE_READ) == 0)
541 return openprom_sunos_ioctl(inode, file, cmd, arg,
546 if ((file->f_mode & FMODE_WRITE) == 0)
548 return openprom_sunos_ioctl(inode, file, cmd, arg,
555 if ((file->f_mode & FMODE_READ) == 0)
557 return openprom_sunos_ioctl(inode, file, cmd, arg,
563 case OPROMGETBOOTARGS:
567 if ((file->f_mode & FMODE_READ) == 0)
569 return openprom_sunos_ioctl(inode, file, cmd, arg, 0);
573 case OPIOCGETOPTNODE:
576 if ((file->f_mode & FMODE_READ) == 0)
578 return openprom_bsd_ioctl(inode,file,cmd,arg);
581 if ((file->f_mode & FMODE_WRITE) == 0)
583 return openprom_bsd_ioctl(inode,file,cmd,arg);
587 printk("openprom_ioctl: cmd 0x%X, arg 0x%lX\n", cmd, arg);
592 static int openprom_open(struct inode * inode, struct file * file)
596 data = (DATA *) kmalloc(sizeof(DATA), GFP_KERNEL);
600 data->current_node = prom_root_node;
601 data->lastnode = prom_root_node;
602 file->private_data = (void *)data;
607 static int openprom_release(struct inode * inode, struct file * file)
609 kfree(file->private_data);
613 static struct file_operations openprom_fops = {
616 ioctl: openprom_ioctl,
618 release: openprom_release,
621 static struct miscdevice openprom_dev = {
622 SUN_OPENPROM_MINOR, "openprom", &openprom_fops
627 static int __init openprom_init(void)
632 error = misc_register(&openprom_dev);
634 printk(KERN_ERR "openprom: unable to get misc minor\n");
639 options_node = prom_getchild(prom_root_node);
640 options_node = prom_searchsiblings(options_node,"options");
641 restore_flags(flags);
643 if (options_node == 0 || options_node == -1) {
644 printk(KERN_ERR "openprom: unable to find options node\n");
645 misc_deregister(&openprom_dev);
652 static void __exit openprom_cleanup(void)
654 misc_deregister(&openprom_dev);
657 module_init(openprom_init);
658 module_exit(openprom_cleanup);
659 MODULE_LICENSE("GPL");