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