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);
727