Linux Audio

Check our new training course

Yocto / OpenEmbedded training

Feb 10-13, 2025
Register
Loading...
v6.8
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Linux/SPARC PROM Configuration Driver
  4 * Copyright (C) 1996 Thomas K. Dyas (tdyas@noc.rutgers.edu)
  5 * Copyright (C) 1996 Eddie C. Dost  (ecd@skynet.be)
  6 *
  7 * This character device driver allows user programs to access the
  8 * PROM device tree. It is compatible with the SunOS /dev/openprom
  9 * driver and the NetBSD /dev/openprom driver. The SunOS eeprom
 10 * utility works without any modifications.
 11 *
 12 * The driver uses a minor number under the misc device major. The
 13 * file read/write mode determines the type of access to the PROM.
 14 * Interrupts are disabled whenever the driver calls into the PROM for
 15 * sanity's sake.
 16 */
 17
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 18
 19#include <linux/module.h>
 20#include <linux/kernel.h>
 21#include <linux/errno.h>
 22#include <linux/slab.h>
 23#include <linux/mutex.h>
 24#include <linux/string.h>
 25#include <linux/miscdevice.h>
 26#include <linux/init.h>
 27#include <linux/fs.h>
 28#include <asm/oplib.h>
 29#include <asm/prom.h>
 30#include <linux/uaccess.h>
 31#include <asm/openpromio.h>
 32#ifdef CONFIG_PCI
 33#include <linux/pci.h>
 34#endif
 35
 36MODULE_AUTHOR("Thomas K. Dyas (tdyas@noc.rutgers.edu) and Eddie C. Dost  (ecd@skynet.be)");
 37MODULE_DESCRIPTION("OPENPROM Configuration Driver");
 38MODULE_LICENSE("GPL");
 39MODULE_VERSION("1.0");
 40MODULE_ALIAS_MISCDEV(SUN_OPENPROM_MINOR);
 41
 42/* Private data kept by the driver for each descriptor. */
 43typedef struct openprom_private_data
 44{
 45	struct device_node *current_node; /* Current node for SunOS ioctls. */
 46	struct device_node *lastnode; /* Last valid node used by BSD ioctls. */
 47} DATA;
 48
 49/* ID of the PROM node containing all of the EEPROM options. */
 50static DEFINE_MUTEX(openprom_mutex);
 51static struct device_node *options_node;
 52
 53/*
 54 * Copy an openpromio structure into kernel space from user space.
 55 * This routine does error checking to make sure that all memory
 56 * accesses are within bounds. A pointer to the allocated openpromio
 57 * structure will be placed in "*opp_p". Return value is the length
 58 * of the user supplied buffer.
 59 */
 60static int copyin(struct openpromio __user *info, struct openpromio **opp_p)
 61{
 62	unsigned int bufsize;
 63
 64	if (!info || !opp_p)
 65		return -EFAULT;
 66
 67	if (get_user(bufsize, &info->oprom_size))
 68		return -EFAULT;
 69
 70	if (bufsize == 0)
 71		return -EINVAL;
 72
 73	/* If the bufsize is too large, just limit it.
 74	 * Fix from Jason Rappleye.
 75	 */
 76	if (bufsize > OPROMMAXPARAM)
 77		bufsize = OPROMMAXPARAM;
 78
 79	if (!(*opp_p = kzalloc(sizeof(int) + bufsize + 1, GFP_KERNEL)))
 80		return -ENOMEM;
 81
 82	if (copy_from_user(&(*opp_p)->oprom_array,
 83			   &info->oprom_array, bufsize)) {
 84		kfree(*opp_p);
 85		return -EFAULT;
 86	}
 87	return bufsize;
 88}
 89
 90static int getstrings(struct openpromio __user *info, struct openpromio **opp_p)
 91{
 92	int n, bufsize;
 93	char c;
 94
 95	if (!info || !opp_p)
 96		return -EFAULT;
 97
 98	if (!(*opp_p = kzalloc(sizeof(int) + OPROMMAXPARAM + 1, GFP_KERNEL)))
 99		return -ENOMEM;
100
101	(*opp_p)->oprom_size = 0;
102
103	n = bufsize = 0;
104	while ((n < 2) && (bufsize < OPROMMAXPARAM)) {
105		if (get_user(c, &info->oprom_array[bufsize])) {
106			kfree(*opp_p);
107			return -EFAULT;
108		}
109		if (c == '\0')
110			n++;
111		(*opp_p)->oprom_array[bufsize++] = c;
112	}
113	if (!n) {
114		kfree(*opp_p);
115		return -EINVAL;
116	}
117	return bufsize;
118}
119
120/*
121 * Copy an openpromio structure in kernel space back to user space.
122 */
123static int copyout(void __user *info, struct openpromio *opp, int len)
124{
125	if (copy_to_user(info, opp, len))
126		return -EFAULT;
127	return 0;
128}
129
130static int opromgetprop(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize)
131{
132	const void *pval;
133	int len;
134
135	if (!dp ||
136	    !(pval = of_get_property(dp, op->oprom_array, &len)) ||
137	    len <= 0 || len > bufsize)
138		return copyout(argp, op, sizeof(int));
139
140	memcpy(op->oprom_array, pval, len);
141	op->oprom_array[len] = '\0';
142	op->oprom_size = len;
143
144	return copyout(argp, op, sizeof(int) + bufsize);
145}
146
147static int opromnxtprop(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize)
148{
149	struct property *prop;
150	int len;
151
152	if (!dp)
153		return copyout(argp, op, sizeof(int));
154	if (op->oprom_array[0] == '\0') {
155		prop = dp->properties;
156		if (!prop)
157			return copyout(argp, op, sizeof(int));
158		len = strlen(prop->name);
159	} else {
160		prop = of_find_property(dp, op->oprom_array, NULL);
161
162		if (!prop ||
163		    !prop->next ||
164		    (len = strlen(prop->next->name)) + 1 > bufsize)
165			return copyout(argp, op, sizeof(int));
166
167		prop = prop->next;
168	}
169
170	memcpy(op->oprom_array, prop->name, len);
171	op->oprom_array[len] = '\0';
172	op->oprom_size = ++len;
173
174	return copyout(argp, op, sizeof(int) + bufsize);
175}
176
177static int opromsetopt(struct device_node *dp, struct openpromio *op, int bufsize)
178{
179	char *buf = op->oprom_array + strlen(op->oprom_array) + 1;
180	int len = op->oprom_array + bufsize - buf;
181
182	return of_set_property(options_node, op->oprom_array, buf, len);
183}
184
185static int opromnext(void __user *argp, unsigned int cmd, struct device_node *dp, struct openpromio *op, int bufsize, DATA *data)
186{
187	phandle ph;
188
189	BUILD_BUG_ON(sizeof(phandle) != sizeof(int));
190
191	if (bufsize < sizeof(phandle))
192		return -EINVAL;
193
194	ph = *((int *) op->oprom_array);
195	if (ph) {
196		dp = of_find_node_by_phandle(ph);
197		if (!dp)
198			return -EINVAL;
199
200		switch (cmd) {
201		case OPROMNEXT:
202			dp = dp->sibling;
203			break;
204
205		case OPROMCHILD:
206			dp = dp->child;
207			break;
208
209		case OPROMSETCUR:
210		default:
211			break;
212		}
213	} else {
214		/* Sibling of node zero is the root node.  */
215		if (cmd != OPROMNEXT)
216			return -EINVAL;
217
218		dp = of_find_node_by_path("/");
219	}
220
221	ph = 0;
222	if (dp)
223		ph = dp->phandle;
224
225	data->current_node = dp;
226	*((int *) op->oprom_array) = ph;
227	op->oprom_size = sizeof(phandle);
228
229	return copyout(argp, op, bufsize + sizeof(int));
230}
231
232static int oprompci2node(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize, DATA *data)
233{
234	int err = -EINVAL;
235
236	if (bufsize >= 2*sizeof(int)) {
237#ifdef CONFIG_PCI
238		struct pci_dev *pdev;
239		struct device_node *dp;
240
241		pdev = pci_get_domain_bus_and_slot(0,
242						((int *) op->oprom_array)[0],
243						((int *) op->oprom_array)[1]);
244
245		dp = pci_device_to_OF_node(pdev);
246		data->current_node = dp;
247		*((int *)op->oprom_array) = dp->phandle;
248		op->oprom_size = sizeof(int);
249		err = copyout(argp, op, bufsize + sizeof(int));
250
251		pci_dev_put(pdev);
252#endif
253	}
254
255	return err;
256}
257
258static int oprompath2node(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize, DATA *data)
259{
260	phandle ph = 0;
261
262	dp = of_find_node_by_path(op->oprom_array);
263	if (dp)
264		ph = dp->phandle;
265	data->current_node = dp;
266	*((int *)op->oprom_array) = ph;
267	op->oprom_size = sizeof(int);
268
269	return copyout(argp, op, bufsize + sizeof(int));
270}
271
272static int opromgetbootargs(void __user *argp, struct openpromio *op, int bufsize)
273{
274	char *buf = saved_command_line;
275	int len = strlen(buf);
276
277	if (len > bufsize)
278		return -EINVAL;
279
280	strcpy(op->oprom_array, buf);
281	op->oprom_size = len;
282
283	return copyout(argp, op, bufsize + sizeof(int));
284}
285
286/*
287 *	SunOS and Solaris /dev/openprom ioctl calls.
288 */
289static long openprom_sunos_ioctl(struct file * file,
290				 unsigned int cmd, unsigned long arg,
291				 struct device_node *dp)
292{
293	DATA *data = file->private_data;
294	struct openpromio *opp = NULL;
295	int bufsize, error = 0;
296	static int cnt;
297	void __user *argp = (void __user *)arg;
298
299	if (cmd == OPROMSETOPT)
300		bufsize = getstrings(argp, &opp);
301	else
302		bufsize = copyin(argp, &opp);
303
304	if (bufsize < 0)
305		return bufsize;
306
307	mutex_lock(&openprom_mutex);
308
309	switch (cmd) {
310	case OPROMGETOPT:
311	case OPROMGETPROP:
312		error = opromgetprop(argp, dp, opp, bufsize);
313		break;
314
315	case OPROMNXTOPT:
316	case OPROMNXTPROP:
317		error = opromnxtprop(argp, dp, opp, bufsize);
318		break;
319
320	case OPROMSETOPT:
321	case OPROMSETOPT2:
322		error = opromsetopt(dp, opp, bufsize);
323		break;
324
325	case OPROMNEXT:
326	case OPROMCHILD:
327	case OPROMSETCUR:
328		error = opromnext(argp, cmd, dp, opp, bufsize, data);
329		break;
330
331	case OPROMPCI2NODE:
332		error = oprompci2node(argp, dp, opp, bufsize, data);
333		break;
334
335	case OPROMPATH2NODE:
336		error = oprompath2node(argp, dp, opp, bufsize, data);
337		break;
338
339	case OPROMGETBOOTARGS:
340		error = opromgetbootargs(argp, opp, bufsize);
341		break;
342
343	case OPROMU2P:
344	case OPROMGETCONS:
345	case OPROMGETFBNAME:
346		if (cnt++ < 10)
347			printk(KERN_INFO "openprom_sunos_ioctl: unimplemented ioctl\n");
348		error = -EINVAL;
349		break;
350	default:
351		if (cnt++ < 10)
352			printk(KERN_INFO "openprom_sunos_ioctl: cmd 0x%X, arg 0x%lX\n", cmd, arg);
353		error = -EINVAL;
354		break;
355	}
356
357	kfree(opp);
358	mutex_unlock(&openprom_mutex);
359
360	return error;
361}
362
363static struct device_node *get_node(phandle n, DATA *data)
364{
365	struct device_node *dp = of_find_node_by_phandle(n);
366
367	if (dp)
368		data->lastnode = dp;
369
370	return dp;
371}
372
373/* Copy in a whole string from userspace into kernelspace. */
374static char * copyin_string(char __user *user, size_t len)
375{
376	if ((ssize_t)len < 0 || (ssize_t)(len + 1) < 0)
377		return ERR_PTR(-EINVAL);
378
379	return memdup_user_nul(user, len);
380}
381
382/*
383 *	NetBSD /dev/openprom ioctl calls.
384 */
385static int opiocget(void __user *argp, DATA *data)
386{
387	struct opiocdesc op;
388	struct device_node *dp;
389	char *str;
390	const void *pval;
391	int err, len;
392
393	if (copy_from_user(&op, argp, sizeof(op)))
394		return -EFAULT;
395
396	dp = get_node(op.op_nodeid, data);
397
398	str = copyin_string(op.op_name, op.op_namelen);
399	if (IS_ERR(str))
400		return PTR_ERR(str);
401
402	pval = of_get_property(dp, str, &len);
403	err = 0;
404	if (!pval || len > op.op_buflen) {
405		err = -EINVAL;
406	} else {
407		op.op_buflen = len;
408		if (copy_to_user(argp, &op, sizeof(op)) ||
409		    copy_to_user(op.op_buf, pval, len))
410			err = -EFAULT;
411	}
412	kfree(str);
413
414	return err;
415}
416
417static int opiocnextprop(void __user *argp, DATA *data)
418{
419	struct opiocdesc op;
420	struct device_node *dp;
421	struct property *prop;
422	char *str;
423	int len;
424
425	if (copy_from_user(&op, argp, sizeof(op)))
426		return -EFAULT;
427
428	dp = get_node(op.op_nodeid, data);
429	if (!dp)
430		return -EINVAL;
431
432	str = copyin_string(op.op_name, op.op_namelen);
433	if (IS_ERR(str))
434		return PTR_ERR(str);
435
436	if (str[0] == '\0') {
437		prop = dp->properties;
438	} else {
439		prop = of_find_property(dp, str, NULL);
440		if (prop)
441			prop = prop->next;
442	}
443	kfree(str);
444
445	if (!prop)
446		len = 0;
447	else
448		len = prop->length;
449
450	if (len > op.op_buflen)
451		len = op.op_buflen;
452
453	if (copy_to_user(argp, &op, sizeof(op)))
454		return -EFAULT;
455
456	if (len &&
457	    copy_to_user(op.op_buf, prop->value, len))
458		return -EFAULT;
459
460	return 0;
461}
462
463static int opiocset(void __user *argp, DATA *data)
464{
465	struct opiocdesc op;
466	struct device_node *dp;
467	char *str, *tmp;
468	int err;
469
470	if (copy_from_user(&op, argp, sizeof(op)))
471		return -EFAULT;
472
473	dp = get_node(op.op_nodeid, data);
474	if (!dp)
475		return -EINVAL;
476
477	str = copyin_string(op.op_name, op.op_namelen);
478	if (IS_ERR(str))
479		return PTR_ERR(str);
480
481	tmp = copyin_string(op.op_buf, op.op_buflen);
482	if (IS_ERR(tmp)) {
483		kfree(str);
484		return PTR_ERR(tmp);
485	}
486
487	err = of_set_property(dp, str, tmp, op.op_buflen);
488
489	kfree(str);
490	kfree(tmp);
491
492	return err;
493}
494
495static int opiocgetnext(unsigned int cmd, void __user *argp)
496{
497	struct device_node *dp;
498	phandle nd;
499
500	BUILD_BUG_ON(sizeof(phandle) != sizeof(int));
501
502	if (copy_from_user(&nd, argp, sizeof(phandle)))
503		return -EFAULT;
504
505	if (nd == 0) {
506		if (cmd != OPIOCGETNEXT)
507			return -EINVAL;
508		dp = of_find_node_by_path("/");
509	} else {
510		dp = of_find_node_by_phandle(nd);
511		nd = 0;
512		if (dp) {
513			if (cmd == OPIOCGETNEXT)
514				dp = dp->sibling;
515			else
516				dp = dp->child;
517		}
518	}
519	if (dp)
520		nd = dp->phandle;
521	if (copy_to_user(argp, &nd, sizeof(phandle)))
522		return -EFAULT;
523
524	return 0;
525}
526
527static int openprom_bsd_ioctl(struct file * file,
528			      unsigned int cmd, unsigned long arg)
529{
530	DATA *data = file->private_data;
531	void __user *argp = (void __user *)arg;
532	int err;
533
534	mutex_lock(&openprom_mutex);
535	switch (cmd) {
536	case OPIOCGET:
537		err = opiocget(argp, data);
538		break;
539
540	case OPIOCNEXTPROP:
541		err = opiocnextprop(argp, data);
542		break;
543
544	case OPIOCSET:
545		err = opiocset(argp, data);
546		break;
547
548	case OPIOCGETOPTNODE:
549		BUILD_BUG_ON(sizeof(phandle) != sizeof(int));
550
551		err = 0;
552		if (copy_to_user(argp, &options_node->phandle, sizeof(phandle)))
553			err = -EFAULT;
554		break;
555
556	case OPIOCGETNEXT:
557	case OPIOCGETCHILD:
558		err = opiocgetnext(cmd, argp);
559		break;
560
561	default:
562		err = -EINVAL;
563		break;
564	}
565	mutex_unlock(&openprom_mutex);
566
567	return err;
568}
569
570
571/*
572 *	Handoff control to the correct ioctl handler.
573 */
574static long openprom_ioctl(struct file * file,
575			   unsigned int cmd, unsigned long arg)
576{
577	DATA *data = file->private_data;
578
579	switch (cmd) {
580	case OPROMGETOPT:
581	case OPROMNXTOPT:
582		if ((file->f_mode & FMODE_READ) == 0)
583			return -EPERM;
584		return openprom_sunos_ioctl(file, cmd, arg,
585					    options_node);
586
587	case OPROMSETOPT:
588	case OPROMSETOPT2:
589		if ((file->f_mode & FMODE_WRITE) == 0)
590			return -EPERM;
591		return openprom_sunos_ioctl(file, cmd, arg,
592					    options_node);
593
594	case OPROMNEXT:
595	case OPROMCHILD:
596	case OPROMGETPROP:
597	case OPROMNXTPROP:
598		if ((file->f_mode & FMODE_READ) == 0)
599			return -EPERM;
600		return openprom_sunos_ioctl(file, cmd, arg,
601					    data->current_node);
602
603	case OPROMU2P:
604	case OPROMGETCONS:
605	case OPROMGETFBNAME:
606	case OPROMGETBOOTARGS:
607	case OPROMSETCUR:
608	case OPROMPCI2NODE:
609	case OPROMPATH2NODE:
610		if ((file->f_mode & FMODE_READ) == 0)
611			return -EPERM;
612		return openprom_sunos_ioctl(file, cmd, arg, NULL);
613
614	case OPIOCGET:
615	case OPIOCNEXTPROP:
616	case OPIOCGETOPTNODE:
617	case OPIOCGETNEXT:
618	case OPIOCGETCHILD:
619		if ((file->f_mode & FMODE_READ) == 0)
620			return -EBADF;
621		return openprom_bsd_ioctl(file,cmd,arg);
622
623	case OPIOCSET:
624		if ((file->f_mode & FMODE_WRITE) == 0)
625			return -EBADF;
626		return openprom_bsd_ioctl(file,cmd,arg);
627
628	default:
629		return -EINVAL;
630	};
631}
632
633static long openprom_compat_ioctl(struct file *file, unsigned int cmd,
634		unsigned long arg)
635{
636	long rval = -ENOTTY;
637
638	/*
639	 * SunOS/Solaris only, the NetBSD one's have embedded pointers in
640	 * the arg which we'd need to clean up...
641	 */
642	switch (cmd) {
643	case OPROMGETOPT:
644	case OPROMSETOPT:
645	case OPROMNXTOPT:
646	case OPROMSETOPT2:
647	case OPROMNEXT:
648	case OPROMCHILD:
649	case OPROMGETPROP:
650	case OPROMNXTPROP:
651	case OPROMU2P:
652	case OPROMGETCONS:
653	case OPROMGETFBNAME:
654	case OPROMGETBOOTARGS:
655	case OPROMSETCUR:
656	case OPROMPCI2NODE:
657	case OPROMPATH2NODE:
658		rval = openprom_ioctl(file, cmd, arg);
659		break;
660	}
661
662	return rval;
663}
664
665static int openprom_open(struct inode * inode, struct file * file)
666{
667	DATA *data;
668
669	data = kmalloc(sizeof(DATA), GFP_KERNEL);
670	if (!data)
671		return -ENOMEM;
672
673	mutex_lock(&openprom_mutex);
674	data->current_node = of_find_node_by_path("/");
675	data->lastnode = data->current_node;
676	file->private_data = (void *) data;
677	mutex_unlock(&openprom_mutex);
678
679	return 0;
680}
681
682static int openprom_release(struct inode * inode, struct file * file)
683{
684	kfree(file->private_data);
685	return 0;
686}
687
688static const struct file_operations openprom_fops = {
689	.owner =	THIS_MODULE,
690	.llseek =	no_llseek,
691	.unlocked_ioctl = openprom_ioctl,
692	.compat_ioctl =	openprom_compat_ioctl,
693	.open =		openprom_open,
694	.release =	openprom_release,
695};
696
697static struct miscdevice openprom_dev = {
698	.minor		= SUN_OPENPROM_MINOR,
699	.name		= "openprom",
700	.fops		= &openprom_fops,
701};
702
703static int __init openprom_init(void)
704{
 
705	int err;
706
707	err = misc_register(&openprom_dev);
708	if (err)
709		return err;
710
711	options_node = of_get_child_by_name(of_find_node_by_path("/"), "options");
 
 
 
 
 
 
 
 
712	if (!options_node) {
713		misc_deregister(&openprom_dev);
714		return -EIO;
715	}
716
717	return 0;
718}
719
720static void __exit openprom_cleanup(void)
721{
722	misc_deregister(&openprom_dev);
723}
724
725module_init(openprom_init);
726module_exit(openprom_cleanup);
v4.17
 
  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#include <linux/module.h>
 33#include <linux/kernel.h>
 34#include <linux/errno.h>
 35#include <linux/slab.h>
 36#include <linux/mutex.h>
 37#include <linux/string.h>
 38#include <linux/miscdevice.h>
 39#include <linux/init.h>
 40#include <linux/fs.h>
 41#include <asm/oplib.h>
 42#include <asm/prom.h>
 43#include <linux/uaccess.h>
 44#include <asm/openpromio.h>
 45#ifdef CONFIG_PCI
 46#include <linux/pci.h>
 47#endif
 48
 49MODULE_AUTHOR("Thomas K. Dyas (tdyas@noc.rutgers.edu) and Eddie C. Dost  (ecd@skynet.be)");
 50MODULE_DESCRIPTION("OPENPROM Configuration Driver");
 51MODULE_LICENSE("GPL");
 52MODULE_VERSION("1.0");
 53MODULE_ALIAS_MISCDEV(SUN_OPENPROM_MINOR);
 54
 55/* Private data kept by the driver for each descriptor. */
 56typedef struct openprom_private_data
 57{
 58	struct device_node *current_node; /* Current node for SunOS ioctls. */
 59	struct device_node *lastnode; /* Last valid node used by BSD ioctls. */
 60} DATA;
 61
 62/* ID of the PROM node containing all of the EEPROM options. */
 63static DEFINE_MUTEX(openprom_mutex);
 64static struct device_node *options_node;
 65
 66/*
 67 * Copy an openpromio structure into kernel space from user space.
 68 * This routine does error checking to make sure that all memory
 69 * accesses are within bounds. A pointer to the allocated openpromio
 70 * structure will be placed in "*opp_p". Return value is the length
 71 * of the user supplied buffer.
 72 */
 73static int copyin(struct openpromio __user *info, struct openpromio **opp_p)
 74{
 75	unsigned int bufsize;
 76
 77	if (!info || !opp_p)
 78		return -EFAULT;
 79
 80	if (get_user(bufsize, &info->oprom_size))
 81		return -EFAULT;
 82
 83	if (bufsize == 0)
 84		return -EINVAL;
 85
 86	/* If the bufsize is too large, just limit it.
 87	 * Fix from Jason Rappleye.
 88	 */
 89	if (bufsize > OPROMMAXPARAM)
 90		bufsize = OPROMMAXPARAM;
 91
 92	if (!(*opp_p = kzalloc(sizeof(int) + bufsize + 1, GFP_KERNEL)))
 93		return -ENOMEM;
 94
 95	if (copy_from_user(&(*opp_p)->oprom_array,
 96			   &info->oprom_array, bufsize)) {
 97		kfree(*opp_p);
 98		return -EFAULT;
 99	}
100	return bufsize;
101}
102
103static int getstrings(struct openpromio __user *info, struct openpromio **opp_p)
104{
105	int n, bufsize;
106	char c;
107
108	if (!info || !opp_p)
109		return -EFAULT;
110
111	if (!(*opp_p = kzalloc(sizeof(int) + OPROMMAXPARAM + 1, GFP_KERNEL)))
112		return -ENOMEM;
113
114	(*opp_p)->oprom_size = 0;
115
116	n = bufsize = 0;
117	while ((n < 2) && (bufsize < OPROMMAXPARAM)) {
118		if (get_user(c, &info->oprom_array[bufsize])) {
119			kfree(*opp_p);
120			return -EFAULT;
121		}
122		if (c == '\0')
123			n++;
124		(*opp_p)->oprom_array[bufsize++] = c;
125	}
126	if (!n) {
127		kfree(*opp_p);
128		return -EINVAL;
129	}
130	return bufsize;
131}
132
133/*
134 * Copy an openpromio structure in kernel space back to user space.
135 */
136static int copyout(void __user *info, struct openpromio *opp, int len)
137{
138	if (copy_to_user(info, opp, len))
139		return -EFAULT;
140	return 0;
141}
142
143static int opromgetprop(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize)
144{
145	const void *pval;
146	int len;
147
148	if (!dp ||
149	    !(pval = of_get_property(dp, op->oprom_array, &len)) ||
150	    len <= 0 || len > bufsize)
151		return copyout(argp, op, sizeof(int));
152
153	memcpy(op->oprom_array, pval, len);
154	op->oprom_array[len] = '\0';
155	op->oprom_size = len;
156
157	return copyout(argp, op, sizeof(int) + bufsize);
158}
159
160static int opromnxtprop(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize)
161{
162	struct property *prop;
163	int len;
164
165	if (!dp)
166		return copyout(argp, op, sizeof(int));
167	if (op->oprom_array[0] == '\0') {
168		prop = dp->properties;
169		if (!prop)
170			return copyout(argp, op, sizeof(int));
171		len = strlen(prop->name);
172	} else {
173		prop = of_find_property(dp, op->oprom_array, NULL);
174
175		if (!prop ||
176		    !prop->next ||
177		    (len = strlen(prop->next->name)) + 1 > bufsize)
178			return copyout(argp, op, sizeof(int));
179
180		prop = prop->next;
181	}
182
183	memcpy(op->oprom_array, prop->name, len);
184	op->oprom_array[len] = '\0';
185	op->oprom_size = ++len;
186
187	return copyout(argp, op, sizeof(int) + bufsize);
188}
189
190static int opromsetopt(struct device_node *dp, struct openpromio *op, int bufsize)
191{
192	char *buf = op->oprom_array + strlen(op->oprom_array) + 1;
193	int len = op->oprom_array + bufsize - buf;
194
195	return of_set_property(options_node, op->oprom_array, buf, len);
196}
197
198static int opromnext(void __user *argp, unsigned int cmd, struct device_node *dp, struct openpromio *op, int bufsize, DATA *data)
199{
200	phandle ph;
201
202	BUILD_BUG_ON(sizeof(phandle) != sizeof(int));
203
204	if (bufsize < sizeof(phandle))
205		return -EINVAL;
206
207	ph = *((int *) op->oprom_array);
208	if (ph) {
209		dp = of_find_node_by_phandle(ph);
210		if (!dp)
211			return -EINVAL;
212
213		switch (cmd) {
214		case OPROMNEXT:
215			dp = dp->sibling;
216			break;
217
218		case OPROMCHILD:
219			dp = dp->child;
220			break;
221
222		case OPROMSETCUR:
223		default:
224			break;
225		}
226	} else {
227		/* Sibling of node zero is the root node.  */
228		if (cmd != OPROMNEXT)
229			return -EINVAL;
230
231		dp = of_find_node_by_path("/");
232	}
233
234	ph = 0;
235	if (dp)
236		ph = dp->phandle;
237
238	data->current_node = dp;
239	*((int *) op->oprom_array) = ph;
240	op->oprom_size = sizeof(phandle);
241
242	return copyout(argp, op, bufsize + sizeof(int));
243}
244
245static int oprompci2node(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize, DATA *data)
246{
247	int err = -EINVAL;
248
249	if (bufsize >= 2*sizeof(int)) {
250#ifdef CONFIG_PCI
251		struct pci_dev *pdev;
252		struct device_node *dp;
253
254		pdev = pci_get_domain_bus_and_slot(0,
255						((int *) op->oprom_array)[0],
256						((int *) op->oprom_array)[1]);
257
258		dp = pci_device_to_OF_node(pdev);
259		data->current_node = dp;
260		*((int *)op->oprom_array) = dp->phandle;
261		op->oprom_size = sizeof(int);
262		err = copyout(argp, op, bufsize + sizeof(int));
263
264		pci_dev_put(pdev);
265#endif
266	}
267
268	return err;
269}
270
271static int oprompath2node(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize, DATA *data)
272{
273	phandle ph = 0;
274
275	dp = of_find_node_by_path(op->oprom_array);
276	if (dp)
277		ph = dp->phandle;
278	data->current_node = dp;
279	*((int *)op->oprom_array) = ph;
280	op->oprom_size = sizeof(int);
281
282	return copyout(argp, op, bufsize + sizeof(int));
283}
284
285static int opromgetbootargs(void __user *argp, struct openpromio *op, int bufsize)
286{
287	char *buf = saved_command_line;
288	int len = strlen(buf);
289
290	if (len > bufsize)
291		return -EINVAL;
292
293	strcpy(op->oprom_array, buf);
294	op->oprom_size = len;
295
296	return copyout(argp, op, bufsize + sizeof(int));
297}
298
299/*
300 *	SunOS and Solaris /dev/openprom ioctl calls.
301 */
302static long openprom_sunos_ioctl(struct file * file,
303				 unsigned int cmd, unsigned long arg,
304				 struct device_node *dp)
305{
306	DATA *data = file->private_data;
307	struct openpromio *opp = NULL;
308	int bufsize, error = 0;
309	static int cnt;
310	void __user *argp = (void __user *)arg;
311
312	if (cmd == OPROMSETOPT)
313		bufsize = getstrings(argp, &opp);
314	else
315		bufsize = copyin(argp, &opp);
316
317	if (bufsize < 0)
318		return bufsize;
319
320	mutex_lock(&openprom_mutex);
321
322	switch (cmd) {
323	case OPROMGETOPT:
324	case OPROMGETPROP:
325		error = opromgetprop(argp, dp, opp, bufsize);
326		break;
327
328	case OPROMNXTOPT:
329	case OPROMNXTPROP:
330		error = opromnxtprop(argp, dp, opp, bufsize);
331		break;
332
333	case OPROMSETOPT:
334	case OPROMSETOPT2:
335		error = opromsetopt(dp, opp, bufsize);
336		break;
337
338	case OPROMNEXT:
339	case OPROMCHILD:
340	case OPROMSETCUR:
341		error = opromnext(argp, cmd, dp, opp, bufsize, data);
342		break;
343
344	case OPROMPCI2NODE:
345		error = oprompci2node(argp, dp, opp, bufsize, data);
346		break;
347
348	case OPROMPATH2NODE:
349		error = oprompath2node(argp, dp, opp, bufsize, data);
350		break;
351
352	case OPROMGETBOOTARGS:
353		error = opromgetbootargs(argp, opp, bufsize);
354		break;
355
356	case OPROMU2P:
357	case OPROMGETCONS:
358	case OPROMGETFBNAME:
359		if (cnt++ < 10)
360			printk(KERN_INFO "openprom_sunos_ioctl: unimplemented ioctl\n");
361		error = -EINVAL;
362		break;
363	default:
364		if (cnt++ < 10)
365			printk(KERN_INFO "openprom_sunos_ioctl: cmd 0x%X, arg 0x%lX\n", cmd, arg);
366		error = -EINVAL;
367		break;
368	}
369
370	kfree(opp);
371	mutex_unlock(&openprom_mutex);
372
373	return error;
374}
375
376static struct device_node *get_node(phandle n, DATA *data)
377{
378	struct device_node *dp = of_find_node_by_phandle(n);
379
380	if (dp)
381		data->lastnode = dp;
382
383	return dp;
384}
385
386/* Copy in a whole string from userspace into kernelspace. */
387static char * copyin_string(char __user *user, size_t len)
388{
389	if ((ssize_t)len < 0 || (ssize_t)(len + 1) < 0)
390		return ERR_PTR(-EINVAL);
391
392	return memdup_user_nul(user, len);
393}
394
395/*
396 *	NetBSD /dev/openprom ioctl calls.
397 */
398static int opiocget(void __user *argp, DATA *data)
399{
400	struct opiocdesc op;
401	struct device_node *dp;
402	char *str;
403	const void *pval;
404	int err, len;
405
406	if (copy_from_user(&op, argp, sizeof(op)))
407		return -EFAULT;
408
409	dp = get_node(op.op_nodeid, data);
410
411	str = copyin_string(op.op_name, op.op_namelen);
412	if (IS_ERR(str))
413		return PTR_ERR(str);
414
415	pval = of_get_property(dp, str, &len);
416	err = 0;
417	if (!pval || len > op.op_buflen) {
418		err = -EINVAL;
419	} else {
420		op.op_buflen = len;
421		if (copy_to_user(argp, &op, sizeof(op)) ||
422		    copy_to_user(op.op_buf, pval, len))
423			err = -EFAULT;
424	}
425	kfree(str);
426
427	return err;
428}
429
430static int opiocnextprop(void __user *argp, DATA *data)
431{
432	struct opiocdesc op;
433	struct device_node *dp;
434	struct property *prop;
435	char *str;
436	int len;
437
438	if (copy_from_user(&op, argp, sizeof(op)))
439		return -EFAULT;
440
441	dp = get_node(op.op_nodeid, data);
442	if (!dp)
443		return -EINVAL;
444
445	str = copyin_string(op.op_name, op.op_namelen);
446	if (IS_ERR(str))
447		return PTR_ERR(str);
448
449	if (str[0] == '\0') {
450		prop = dp->properties;
451	} else {
452		prop = of_find_property(dp, str, NULL);
453		if (prop)
454			prop = prop->next;
455	}
456	kfree(str);
457
458	if (!prop)
459		len = 0;
460	else
461		len = prop->length;
462
463	if (len > op.op_buflen)
464		len = op.op_buflen;
465
466	if (copy_to_user(argp, &op, sizeof(op)))
467		return -EFAULT;
468
469	if (len &&
470	    copy_to_user(op.op_buf, prop->value, len))
471		return -EFAULT;
472
473	return 0;
474}
475
476static int opiocset(void __user *argp, DATA *data)
477{
478	struct opiocdesc op;
479	struct device_node *dp;
480	char *str, *tmp;
481	int err;
482
483	if (copy_from_user(&op, argp, sizeof(op)))
484		return -EFAULT;
485
486	dp = get_node(op.op_nodeid, data);
487	if (!dp)
488		return -EINVAL;
489
490	str = copyin_string(op.op_name, op.op_namelen);
491	if (IS_ERR(str))
492		return PTR_ERR(str);
493
494	tmp = copyin_string(op.op_buf, op.op_buflen);
495	if (IS_ERR(tmp)) {
496		kfree(str);
497		return PTR_ERR(tmp);
498	}
499
500	err = of_set_property(dp, str, tmp, op.op_buflen);
501
502	kfree(str);
503	kfree(tmp);
504
505	return err;
506}
507
508static int opiocgetnext(unsigned int cmd, void __user *argp)
509{
510	struct device_node *dp;
511	phandle nd;
512
513	BUILD_BUG_ON(sizeof(phandle) != sizeof(int));
514
515	if (copy_from_user(&nd, argp, sizeof(phandle)))
516		return -EFAULT;
517
518	if (nd == 0) {
519		if (cmd != OPIOCGETNEXT)
520			return -EINVAL;
521		dp = of_find_node_by_path("/");
522	} else {
523		dp = of_find_node_by_phandle(nd);
524		nd = 0;
525		if (dp) {
526			if (cmd == OPIOCGETNEXT)
527				dp = dp->sibling;
528			else
529				dp = dp->child;
530		}
531	}
532	if (dp)
533		nd = dp->phandle;
534	if (copy_to_user(argp, &nd, sizeof(phandle)))
535		return -EFAULT;
536
537	return 0;
538}
539
540static int openprom_bsd_ioctl(struct file * file,
541			      unsigned int cmd, unsigned long arg)
542{
543	DATA *data = file->private_data;
544	void __user *argp = (void __user *)arg;
545	int err;
546
547	mutex_lock(&openprom_mutex);
548	switch (cmd) {
549	case OPIOCGET:
550		err = opiocget(argp, data);
551		break;
552
553	case OPIOCNEXTPROP:
554		err = opiocnextprop(argp, data);
555		break;
556
557	case OPIOCSET:
558		err = opiocset(argp, data);
559		break;
560
561	case OPIOCGETOPTNODE:
562		BUILD_BUG_ON(sizeof(phandle) != sizeof(int));
563
564		err = 0;
565		if (copy_to_user(argp, &options_node->phandle, sizeof(phandle)))
566			err = -EFAULT;
567		break;
568
569	case OPIOCGETNEXT:
570	case OPIOCGETCHILD:
571		err = opiocgetnext(cmd, argp);
572		break;
573
574	default:
575		err = -EINVAL;
576		break;
577	}
578	mutex_unlock(&openprom_mutex);
579
580	return err;
581}
582
583
584/*
585 *	Handoff control to the correct ioctl handler.
586 */
587static long openprom_ioctl(struct file * file,
588			   unsigned int cmd, unsigned long arg)
589{
590	DATA *data = file->private_data;
591
592	switch (cmd) {
593	case OPROMGETOPT:
594	case OPROMNXTOPT:
595		if ((file->f_mode & FMODE_READ) == 0)
596			return -EPERM;
597		return openprom_sunos_ioctl(file, cmd, arg,
598					    options_node);
599
600	case OPROMSETOPT:
601	case OPROMSETOPT2:
602		if ((file->f_mode & FMODE_WRITE) == 0)
603			return -EPERM;
604		return openprom_sunos_ioctl(file, cmd, arg,
605					    options_node);
606
607	case OPROMNEXT:
608	case OPROMCHILD:
609	case OPROMGETPROP:
610	case OPROMNXTPROP:
611		if ((file->f_mode & FMODE_READ) == 0)
612			return -EPERM;
613		return openprom_sunos_ioctl(file, cmd, arg,
614					    data->current_node);
615
616	case OPROMU2P:
617	case OPROMGETCONS:
618	case OPROMGETFBNAME:
619	case OPROMGETBOOTARGS:
620	case OPROMSETCUR:
621	case OPROMPCI2NODE:
622	case OPROMPATH2NODE:
623		if ((file->f_mode & FMODE_READ) == 0)
624			return -EPERM;
625		return openprom_sunos_ioctl(file, cmd, arg, NULL);
626
627	case OPIOCGET:
628	case OPIOCNEXTPROP:
629	case OPIOCGETOPTNODE:
630	case OPIOCGETNEXT:
631	case OPIOCGETCHILD:
632		if ((file->f_mode & FMODE_READ) == 0)
633			return -EBADF;
634		return openprom_bsd_ioctl(file,cmd,arg);
635
636	case OPIOCSET:
637		if ((file->f_mode & FMODE_WRITE) == 0)
638			return -EBADF;
639		return openprom_bsd_ioctl(file,cmd,arg);
640
641	default:
642		return -EINVAL;
643	};
644}
645
646static long openprom_compat_ioctl(struct file *file, unsigned int cmd,
647		unsigned long arg)
648{
649	long rval = -ENOTTY;
650
651	/*
652	 * SunOS/Solaris only, the NetBSD one's have embedded pointers in
653	 * the arg which we'd need to clean up...
654	 */
655	switch (cmd) {
656	case OPROMGETOPT:
657	case OPROMSETOPT:
658	case OPROMNXTOPT:
659	case OPROMSETOPT2:
660	case OPROMNEXT:
661	case OPROMCHILD:
662	case OPROMGETPROP:
663	case OPROMNXTPROP:
664	case OPROMU2P:
665	case OPROMGETCONS:
666	case OPROMGETFBNAME:
667	case OPROMGETBOOTARGS:
668	case OPROMSETCUR:
669	case OPROMPCI2NODE:
670	case OPROMPATH2NODE:
671		rval = openprom_ioctl(file, cmd, arg);
672		break;
673	}
674
675	return rval;
676}
677
678static int openprom_open(struct inode * inode, struct file * file)
679{
680	DATA *data;
681
682	data = kmalloc(sizeof(DATA), GFP_KERNEL);
683	if (!data)
684		return -ENOMEM;
685
686	mutex_lock(&openprom_mutex);
687	data->current_node = of_find_node_by_path("/");
688	data->lastnode = data->current_node;
689	file->private_data = (void *) data;
690	mutex_unlock(&openprom_mutex);
691
692	return 0;
693}
694
695static int openprom_release(struct inode * inode, struct file * file)
696{
697	kfree(file->private_data);
698	return 0;
699}
700
701static const struct file_operations openprom_fops = {
702	.owner =	THIS_MODULE,
703	.llseek =	no_llseek,
704	.unlocked_ioctl = openprom_ioctl,
705	.compat_ioctl =	openprom_compat_ioctl,
706	.open =		openprom_open,
707	.release =	openprom_release,
708};
709
710static struct miscdevice openprom_dev = {
711	.minor		= SUN_OPENPROM_MINOR,
712	.name		= "openprom",
713	.fops		= &openprom_fops,
714};
715
716static int __init openprom_init(void)
717{
718	struct device_node *dp;
719	int err;
720
721	err = misc_register(&openprom_dev);
722	if (err)
723		return err;
724
725	dp = of_find_node_by_path("/");
726	dp = dp->child;
727	while (dp) {
728		if (!strcmp(dp->name, "options"))
729			break;
730		dp = dp->sibling;
731	}
732	options_node = dp;
733
734	if (!options_node) {
735		misc_deregister(&openprom_dev);
736		return -EIO;
737	}
738
739	return 0;
740}
741
742static void __exit openprom_cleanup(void)
743{
744	misc_deregister(&openprom_dev);
745}
746
747module_init(openprom_init);
748module_exit(openprom_cleanup);