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