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