xref: /openbmc/linux/drivers/sbus/char/openprom.c (revision 58e16d792a6a8c6b750f637a4649967fcac853dc)
1*74ba9207SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * Linux/SPARC PROM Configuration Driver
41da177e4SLinus Torvalds  * Copyright (C) 1996 Thomas K. Dyas (tdyas@noc.rutgers.edu)
51da177e4SLinus Torvalds  * Copyright (C) 1996 Eddie C. Dost  (ecd@skynet.be)
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * This character device driver allows user programs to access the
81da177e4SLinus Torvalds  * PROM device tree. It is compatible with the SunOS /dev/openprom
91da177e4SLinus Torvalds  * driver and the NetBSD /dev/openprom driver. The SunOS eeprom
101da177e4SLinus Torvalds  * utility works without any modifications.
111da177e4SLinus Torvalds  *
121da177e4SLinus Torvalds  * The driver uses a minor number under the misc device major. The
131da177e4SLinus Torvalds  * file read/write mode determines the type of access to the PROM.
141da177e4SLinus Torvalds  * Interrupts are disabled whenever the driver calls into the PROM for
151da177e4SLinus Torvalds  * sanity's sake.
161da177e4SLinus Torvalds  */
171da177e4SLinus Torvalds 
181da177e4SLinus Torvalds 
191da177e4SLinus Torvalds #include <linux/module.h>
201da177e4SLinus Torvalds #include <linux/kernel.h>
211da177e4SLinus Torvalds #include <linux/errno.h>
221da177e4SLinus Torvalds #include <linux/slab.h>
23a3108ca2SArnd Bergmann #include <linux/mutex.h>
241da177e4SLinus Torvalds #include <linux/string.h>
251da177e4SLinus Torvalds #include <linux/miscdevice.h>
261da177e4SLinus Torvalds #include <linux/init.h>
271da177e4SLinus Torvalds #include <linux/fs.h>
281da177e4SLinus Torvalds #include <asm/oplib.h>
298e48aec7SDavid S. Miller #include <asm/prom.h>
307c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
311da177e4SLinus Torvalds #include <asm/openpromio.h>
321da177e4SLinus Torvalds #ifdef CONFIG_PCI
331da177e4SLinus Torvalds #include <linux/pci.h>
341da177e4SLinus Torvalds #endif
351da177e4SLinus Torvalds 
368e48aec7SDavid S. Miller MODULE_AUTHOR("Thomas K. Dyas (tdyas@noc.rutgers.edu) and Eddie C. Dost  (ecd@skynet.be)");
378e48aec7SDavid S. Miller MODULE_DESCRIPTION("OPENPROM Configuration Driver");
388e48aec7SDavid S. Miller MODULE_LICENSE("GPL");
398e48aec7SDavid S. Miller MODULE_VERSION("1.0");
401c339eb1SScott James Remnant MODULE_ALIAS_MISCDEV(SUN_OPENPROM_MINOR);
418e48aec7SDavid S. Miller 
421da177e4SLinus Torvalds /* Private data kept by the driver for each descriptor. */
431da177e4SLinus Torvalds typedef struct openprom_private_data
441da177e4SLinus Torvalds {
458e48aec7SDavid S. Miller 	struct device_node *current_node; /* Current node for SunOS ioctls. */
468e48aec7SDavid S. Miller 	struct device_node *lastnode; /* Last valid node used by BSD ioctls. */
471da177e4SLinus Torvalds } DATA;
481da177e4SLinus Torvalds 
491da177e4SLinus Torvalds /* ID of the PROM node containing all of the EEPROM options. */
50a3108ca2SArnd Bergmann static DEFINE_MUTEX(openprom_mutex);
518e48aec7SDavid S. Miller static struct device_node *options_node;
521da177e4SLinus Torvalds 
531da177e4SLinus Torvalds /*
541da177e4SLinus Torvalds  * Copy an openpromio structure into kernel space from user space.
551da177e4SLinus Torvalds  * This routine does error checking to make sure that all memory
561da177e4SLinus Torvalds  * accesses are within bounds. A pointer to the allocated openpromio
571da177e4SLinus Torvalds  * structure will be placed in "*opp_p". Return value is the length
581da177e4SLinus Torvalds  * of the user supplied buffer.
591da177e4SLinus Torvalds  */
copyin(struct openpromio __user * info,struct openpromio ** opp_p)601da177e4SLinus Torvalds static int copyin(struct openpromio __user *info, struct openpromio **opp_p)
611da177e4SLinus Torvalds {
621da177e4SLinus Torvalds 	unsigned int bufsize;
631da177e4SLinus Torvalds 
641da177e4SLinus Torvalds 	if (!info || !opp_p)
651da177e4SLinus Torvalds 		return -EFAULT;
661da177e4SLinus Torvalds 
671da177e4SLinus Torvalds 	if (get_user(bufsize, &info->oprom_size))
681da177e4SLinus Torvalds 		return -EFAULT;
691da177e4SLinus Torvalds 
701da177e4SLinus Torvalds 	if (bufsize == 0)
711da177e4SLinus Torvalds 		return -EINVAL;
721da177e4SLinus Torvalds 
731da177e4SLinus Torvalds 	/* If the bufsize is too large, just limit it.
741da177e4SLinus Torvalds 	 * Fix from Jason Rappleye.
751da177e4SLinus Torvalds 	 */
761da177e4SLinus Torvalds 	if (bufsize > OPROMMAXPARAM)
771da177e4SLinus Torvalds 		bufsize = OPROMMAXPARAM;
781da177e4SLinus Torvalds 
798e48aec7SDavid S. Miller 	if (!(*opp_p = kzalloc(sizeof(int) + bufsize + 1, GFP_KERNEL)))
801da177e4SLinus Torvalds 		return -ENOMEM;
811da177e4SLinus Torvalds 
821da177e4SLinus Torvalds 	if (copy_from_user(&(*opp_p)->oprom_array,
831da177e4SLinus Torvalds 			   &info->oprom_array, bufsize)) {
841da177e4SLinus Torvalds 		kfree(*opp_p);
851da177e4SLinus Torvalds 		return -EFAULT;
861da177e4SLinus Torvalds 	}
871da177e4SLinus Torvalds 	return bufsize;
881da177e4SLinus Torvalds }
891da177e4SLinus Torvalds 
getstrings(struct openpromio __user * info,struct openpromio ** opp_p)901da177e4SLinus Torvalds static int getstrings(struct openpromio __user *info, struct openpromio **opp_p)
911da177e4SLinus Torvalds {
921da177e4SLinus Torvalds 	int n, bufsize;
931da177e4SLinus Torvalds 	char c;
941da177e4SLinus Torvalds 
951da177e4SLinus Torvalds 	if (!info || !opp_p)
961da177e4SLinus Torvalds 		return -EFAULT;
971da177e4SLinus Torvalds 
988e48aec7SDavid S. Miller 	if (!(*opp_p = kzalloc(sizeof(int) + OPROMMAXPARAM + 1, GFP_KERNEL)))
991da177e4SLinus Torvalds 		return -ENOMEM;
1001da177e4SLinus Torvalds 
1011da177e4SLinus Torvalds 	(*opp_p)->oprom_size = 0;
1021da177e4SLinus Torvalds 
1031da177e4SLinus Torvalds 	n = bufsize = 0;
1041da177e4SLinus Torvalds 	while ((n < 2) && (bufsize < OPROMMAXPARAM)) {
1051da177e4SLinus Torvalds 		if (get_user(c, &info->oprom_array[bufsize])) {
1061da177e4SLinus Torvalds 			kfree(*opp_p);
1071da177e4SLinus Torvalds 			return -EFAULT;
1081da177e4SLinus Torvalds 		}
1091da177e4SLinus Torvalds 		if (c == '\0')
1101da177e4SLinus Torvalds 			n++;
1111da177e4SLinus Torvalds 		(*opp_p)->oprom_array[bufsize++] = c;
1121da177e4SLinus Torvalds 	}
1131da177e4SLinus Torvalds 	if (!n) {
1141da177e4SLinus Torvalds 		kfree(*opp_p);
1151da177e4SLinus Torvalds 		return -EINVAL;
1161da177e4SLinus Torvalds 	}
1171da177e4SLinus Torvalds 	return bufsize;
1181da177e4SLinus Torvalds }
1191da177e4SLinus Torvalds 
1201da177e4SLinus Torvalds /*
1211da177e4SLinus Torvalds  * Copy an openpromio structure in kernel space back to user space.
1221da177e4SLinus Torvalds  */
copyout(void __user * info,struct openpromio * opp,int len)1231da177e4SLinus Torvalds static int copyout(void __user *info, struct openpromio *opp, int len)
1241da177e4SLinus Torvalds {
1251da177e4SLinus Torvalds 	if (copy_to_user(info, opp, len))
1261da177e4SLinus Torvalds 		return -EFAULT;
1271da177e4SLinus Torvalds 	return 0;
1281da177e4SLinus Torvalds }
1291da177e4SLinus Torvalds 
opromgetprop(void __user * argp,struct device_node * dp,struct openpromio * op,int bufsize)1308e48aec7SDavid S. Miller static int opromgetprop(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize)
1318e48aec7SDavid S. Miller {
132ccf0dec6SStephen Rothwell 	const void *pval;
1338e48aec7SDavid S. Miller 	int len;
1348e48aec7SDavid S. Miller 
135b9b64e6eSDavid S. Miller 	if (!dp ||
136b9b64e6eSDavid S. Miller 	    !(pval = of_get_property(dp, op->oprom_array, &len)) ||
137b9b64e6eSDavid S. Miller 	    len <= 0 || len > bufsize)
1388e48aec7SDavid S. Miller 		return copyout(argp, op, sizeof(int));
1398e48aec7SDavid S. Miller 
1408e48aec7SDavid S. Miller 	memcpy(op->oprom_array, pval, len);
1418e48aec7SDavid S. Miller 	op->oprom_array[len] = '\0';
1428e48aec7SDavid S. Miller 	op->oprom_size = len;
1438e48aec7SDavid S. Miller 
1448e48aec7SDavid S. Miller 	return copyout(argp, op, sizeof(int) + bufsize);
1458e48aec7SDavid S. Miller }
1468e48aec7SDavid S. Miller 
opromnxtprop(void __user * argp,struct device_node * dp,struct openpromio * op,int bufsize)1478e48aec7SDavid S. Miller static int opromnxtprop(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize)
1488e48aec7SDavid S. Miller {
1498e48aec7SDavid S. Miller 	struct property *prop;
1508e48aec7SDavid S. Miller 	int len;
1518e48aec7SDavid S. Miller 
152b9b64e6eSDavid S. Miller 	if (!dp)
153b9b64e6eSDavid S. Miller 		return copyout(argp, op, sizeof(int));
1548e48aec7SDavid S. Miller 	if (op->oprom_array[0] == '\0') {
1558e48aec7SDavid S. Miller 		prop = dp->properties;
1568e48aec7SDavid S. Miller 		if (!prop)
1578e48aec7SDavid S. Miller 			return copyout(argp, op, sizeof(int));
1588e48aec7SDavid S. Miller 		len = strlen(prop->name);
1598e48aec7SDavid S. Miller 	} else {
1608e48aec7SDavid S. Miller 		prop = of_find_property(dp, op->oprom_array, NULL);
1618e48aec7SDavid S. Miller 
1628e48aec7SDavid S. Miller 		if (!prop ||
1638e48aec7SDavid S. Miller 		    !prop->next ||
1648e48aec7SDavid S. Miller 		    (len = strlen(prop->next->name)) + 1 > bufsize)
1658e48aec7SDavid S. Miller 			return copyout(argp, op, sizeof(int));
1668e48aec7SDavid S. Miller 
1678e48aec7SDavid S. Miller 		prop = prop->next;
1688e48aec7SDavid S. Miller 	}
1698e48aec7SDavid S. Miller 
1708e48aec7SDavid S. Miller 	memcpy(op->oprom_array, prop->name, len);
1718e48aec7SDavid S. Miller 	op->oprom_array[len] = '\0';
1728e48aec7SDavid S. Miller 	op->oprom_size = ++len;
1738e48aec7SDavid S. Miller 
1748e48aec7SDavid S. Miller 	return copyout(argp, op, sizeof(int) + bufsize);
1758e48aec7SDavid S. Miller }
1768e48aec7SDavid S. Miller 
opromsetopt(struct device_node * dp,struct openpromio * op,int bufsize)1778e48aec7SDavid S. Miller static int opromsetopt(struct device_node *dp, struct openpromio *op, int bufsize)
1788e48aec7SDavid S. Miller {
1798e48aec7SDavid S. Miller 	char *buf = op->oprom_array + strlen(op->oprom_array) + 1;
1808e48aec7SDavid S. Miller 	int len = op->oprom_array + bufsize - buf;
1818e48aec7SDavid S. Miller 
1828e48aec7SDavid S. Miller 	return of_set_property(options_node, op->oprom_array, buf, len);
1838e48aec7SDavid S. Miller }
1848e48aec7SDavid S. Miller 
opromnext(void __user * argp,unsigned int cmd,struct device_node * dp,struct openpromio * op,int bufsize,DATA * data)1858e48aec7SDavid S. Miller static int opromnext(void __user *argp, unsigned int cmd, struct device_node *dp, struct openpromio *op, int bufsize, DATA *data)
1868e48aec7SDavid S. Miller {
1878e48aec7SDavid S. Miller 	phandle ph;
1888e48aec7SDavid S. Miller 
1898e48aec7SDavid S. Miller 	BUILD_BUG_ON(sizeof(phandle) != sizeof(int));
1908e48aec7SDavid S. Miller 
1918e48aec7SDavid S. Miller 	if (bufsize < sizeof(phandle))
1928e48aec7SDavid S. Miller 		return -EINVAL;
1938e48aec7SDavid S. Miller 
1948e48aec7SDavid S. Miller 	ph = *((int *) op->oprom_array);
1958e48aec7SDavid S. Miller 	if (ph) {
1968e48aec7SDavid S. Miller 		dp = of_find_node_by_phandle(ph);
1978e48aec7SDavid S. Miller 		if (!dp)
1988e48aec7SDavid S. Miller 			return -EINVAL;
1998e48aec7SDavid S. Miller 
2008e48aec7SDavid S. Miller 		switch (cmd) {
2018e48aec7SDavid S. Miller 		case OPROMNEXT:
2028e48aec7SDavid S. Miller 			dp = dp->sibling;
2038e48aec7SDavid S. Miller 			break;
2048e48aec7SDavid S. Miller 
2058e48aec7SDavid S. Miller 		case OPROMCHILD:
2068e48aec7SDavid S. Miller 			dp = dp->child;
2078e48aec7SDavid S. Miller 			break;
2088e48aec7SDavid S. Miller 
2098e48aec7SDavid S. Miller 		case OPROMSETCUR:
2108e48aec7SDavid S. Miller 		default:
2118e48aec7SDavid S. Miller 			break;
212da201161SPeter Senna Tschudin 		}
2138e48aec7SDavid S. Miller 	} else {
2148e48aec7SDavid S. Miller 		/* Sibling of node zero is the root node.  */
2158e48aec7SDavid S. Miller 		if (cmd != OPROMNEXT)
2168e48aec7SDavid S. Miller 			return -EINVAL;
2178e48aec7SDavid S. Miller 
2188e48aec7SDavid S. Miller 		dp = of_find_node_by_path("/");
2198e48aec7SDavid S. Miller 	}
2208e48aec7SDavid S. Miller 
2218e48aec7SDavid S. Miller 	ph = 0;
2228e48aec7SDavid S. Miller 	if (dp)
2236016a363SGrant Likely 		ph = dp->phandle;
2248e48aec7SDavid S. Miller 
2258e48aec7SDavid S. Miller 	data->current_node = dp;
2268e48aec7SDavid S. Miller 	*((int *) op->oprom_array) = ph;
2278e48aec7SDavid S. Miller 	op->oprom_size = sizeof(phandle);
2288e48aec7SDavid S. Miller 
2298e48aec7SDavid S. Miller 	return copyout(argp, op, bufsize + sizeof(int));
2308e48aec7SDavid S. Miller }
2318e48aec7SDavid S. Miller 
oprompci2node(void __user * argp,struct device_node * dp,struct openpromio * op,int bufsize,DATA * data)2328e48aec7SDavid S. Miller static int oprompci2node(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize, DATA *data)
2338e48aec7SDavid S. Miller {
2348e48aec7SDavid S. Miller 	int err = -EINVAL;
2358e48aec7SDavid S. Miller 
2368e48aec7SDavid S. Miller 	if (bufsize >= 2*sizeof(int)) {
2378e48aec7SDavid S. Miller #ifdef CONFIG_PCI
2388e48aec7SDavid S. Miller 		struct pci_dev *pdev;
239fa449bd6SDavid S. Miller 		struct device_node *dp;
240fa449bd6SDavid S. Miller 
241c7923732SSinan Kaya 		pdev = pci_get_domain_bus_and_slot(0,
242c7923732SSinan Kaya 						((int *) op->oprom_array)[0],
2438e48aec7SDavid S. Miller 						((int *) op->oprom_array)[1]);
2448e48aec7SDavid S. Miller 
245fa449bd6SDavid S. Miller 		dp = pci_device_to_OF_node(pdev);
2468e48aec7SDavid S. Miller 		data->current_node = dp;
2476016a363SGrant Likely 		*((int *)op->oprom_array) = dp->phandle;
2488e48aec7SDavid S. Miller 		op->oprom_size = sizeof(int);
2498e48aec7SDavid S. Miller 		err = copyout(argp, op, bufsize + sizeof(int));
250fa449bd6SDavid S. Miller 
2517e9f3346SAlan Cox 		pci_dev_put(pdev);
2528e48aec7SDavid S. Miller #endif
2538e48aec7SDavid S. Miller 	}
2548e48aec7SDavid S. Miller 
2558e48aec7SDavid S. Miller 	return err;
2568e48aec7SDavid S. Miller }
2578e48aec7SDavid S. Miller 
oprompath2node(void __user * argp,struct device_node * dp,struct openpromio * op,int bufsize,DATA * data)2588e48aec7SDavid S. Miller static int oprompath2node(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize, DATA *data)
2598e48aec7SDavid S. Miller {
260b9b64e6eSDavid S. Miller 	phandle ph = 0;
261b9b64e6eSDavid S. Miller 
2628e48aec7SDavid S. Miller 	dp = of_find_node_by_path(op->oprom_array);
263b9b64e6eSDavid S. Miller 	if (dp)
2646016a363SGrant Likely 		ph = dp->phandle;
2658e48aec7SDavid S. Miller 	data->current_node = dp;
266b9b64e6eSDavid S. Miller 	*((int *)op->oprom_array) = ph;
2678e48aec7SDavid S. Miller 	op->oprom_size = sizeof(int);
2688e48aec7SDavid S. Miller 
2698e48aec7SDavid S. Miller 	return copyout(argp, op, bufsize + sizeof(int));
2708e48aec7SDavid S. Miller }
2718e48aec7SDavid S. Miller 
opromgetbootargs(void __user * argp,struct openpromio * op,int bufsize)2728e48aec7SDavid S. Miller static int opromgetbootargs(void __user *argp, struct openpromio *op, int bufsize)
2738e48aec7SDavid S. Miller {
2748e48aec7SDavid S. Miller 	char *buf = saved_command_line;
2758e48aec7SDavid S. Miller 	int len = strlen(buf);
2768e48aec7SDavid S. Miller 
2778e48aec7SDavid S. Miller 	if (len > bufsize)
2788e48aec7SDavid S. Miller 		return -EINVAL;
2798e48aec7SDavid S. Miller 
2808e48aec7SDavid S. Miller 	strcpy(op->oprom_array, buf);
2818e48aec7SDavid S. Miller 	op->oprom_size = len;
2828e48aec7SDavid S. Miller 
2838e48aec7SDavid S. Miller 	return copyout(argp, op, bufsize + sizeof(int));
2848e48aec7SDavid S. Miller }
2858e48aec7SDavid S. Miller 
2861da177e4SLinus Torvalds /*
2871da177e4SLinus Torvalds  *	SunOS and Solaris /dev/openprom ioctl calls.
2881da177e4SLinus Torvalds  */
openprom_sunos_ioctl(struct file * file,unsigned int cmd,unsigned long arg,struct device_node * dp)28955929332SArnd Bergmann static long openprom_sunos_ioctl(struct file * file,
2908e48aec7SDavid S. Miller 				 unsigned int cmd, unsigned long arg,
2918e48aec7SDavid S. Miller 				 struct device_node *dp)
2921da177e4SLinus Torvalds {
2938e48aec7SDavid S. Miller 	DATA *data = file->private_data;
29432e5897dSDavid S. Miller 	struct openpromio *opp = NULL;
2958e48aec7SDavid S. Miller 	int bufsize, error = 0;
2961da177e4SLinus Torvalds 	static int cnt;
2971da177e4SLinus Torvalds 	void __user *argp = (void __user *)arg;
2981da177e4SLinus Torvalds 
2991da177e4SLinus Torvalds 	if (cmd == OPROMSETOPT)
3001da177e4SLinus Torvalds 		bufsize = getstrings(argp, &opp);
3011da177e4SLinus Torvalds 	else
3021da177e4SLinus Torvalds 		bufsize = copyin(argp, &opp);
3031da177e4SLinus Torvalds 
3041da177e4SLinus Torvalds 	if (bufsize < 0)
3051da177e4SLinus Torvalds 		return bufsize;
3061da177e4SLinus Torvalds 
307a3108ca2SArnd Bergmann 	mutex_lock(&openprom_mutex);
30855929332SArnd Bergmann 
3091da177e4SLinus Torvalds 	switch (cmd) {
3101da177e4SLinus Torvalds 	case OPROMGETOPT:
3111da177e4SLinus Torvalds 	case OPROMGETPROP:
3128e48aec7SDavid S. Miller 		error = opromgetprop(argp, dp, opp, bufsize);
3131da177e4SLinus Torvalds 		break;
3141da177e4SLinus Torvalds 
3151da177e4SLinus Torvalds 	case OPROMNXTOPT:
3161da177e4SLinus Torvalds 	case OPROMNXTPROP:
3178e48aec7SDavid S. Miller 		error = opromnxtprop(argp, dp, opp, bufsize);
3181da177e4SLinus Torvalds 		break;
3191da177e4SLinus Torvalds 
3201da177e4SLinus Torvalds 	case OPROMSETOPT:
3211da177e4SLinus Torvalds 	case OPROMSETOPT2:
3228e48aec7SDavid S. Miller 		error = opromsetopt(dp, opp, bufsize);
3231da177e4SLinus Torvalds 		break;
3241da177e4SLinus Torvalds 
3251da177e4SLinus Torvalds 	case OPROMNEXT:
3261da177e4SLinus Torvalds 	case OPROMCHILD:
3271da177e4SLinus Torvalds 	case OPROMSETCUR:
3288e48aec7SDavid S. Miller 		error = opromnext(argp, cmd, dp, opp, bufsize, data);
3291da177e4SLinus Torvalds 		break;
3301da177e4SLinus Torvalds 
3311da177e4SLinus Torvalds 	case OPROMPCI2NODE:
3328e48aec7SDavid S. Miller 		error = oprompci2node(argp, dp, opp, bufsize, data);
3331da177e4SLinus Torvalds 		break;
3341da177e4SLinus Torvalds 
3351da177e4SLinus Torvalds 	case OPROMPATH2NODE:
3368e48aec7SDavid S. Miller 		error = oprompath2node(argp, dp, opp, bufsize, data);
3371da177e4SLinus Torvalds 		break;
3381da177e4SLinus Torvalds 
3391da177e4SLinus Torvalds 	case OPROMGETBOOTARGS:
3408e48aec7SDavid S. Miller 		error = opromgetbootargs(argp, opp, bufsize);
3411da177e4SLinus Torvalds 		break;
3421da177e4SLinus Torvalds 
3431da177e4SLinus Torvalds 	case OPROMU2P:
3441da177e4SLinus Torvalds 	case OPROMGETCONS:
3451da177e4SLinus Torvalds 	case OPROMGETFBNAME:
3461da177e4SLinus Torvalds 		if (cnt++ < 10)
3471da177e4SLinus Torvalds 			printk(KERN_INFO "openprom_sunos_ioctl: unimplemented ioctl\n");
3481da177e4SLinus Torvalds 		error = -EINVAL;
3491da177e4SLinus Torvalds 		break;
3501da177e4SLinus Torvalds 	default:
3511da177e4SLinus Torvalds 		if (cnt++ < 10)
3521da177e4SLinus Torvalds 			printk(KERN_INFO "openprom_sunos_ioctl: cmd 0x%X, arg 0x%lX\n", cmd, arg);
3531da177e4SLinus Torvalds 		error = -EINVAL;
3541da177e4SLinus Torvalds 		break;
3551da177e4SLinus Torvalds 	}
3561da177e4SLinus Torvalds 
3571da177e4SLinus Torvalds 	kfree(opp);
358a3108ca2SArnd Bergmann 	mutex_unlock(&openprom_mutex);
35955929332SArnd Bergmann 
3601da177e4SLinus Torvalds 	return error;
3611da177e4SLinus Torvalds }
3621da177e4SLinus Torvalds 
get_node(phandle n,DATA * data)3638e48aec7SDavid S. Miller static struct device_node *get_node(phandle n, DATA *data)
3641da177e4SLinus Torvalds {
3658e48aec7SDavid S. Miller 	struct device_node *dp = of_find_node_by_phandle(n);
3661da177e4SLinus Torvalds 
3678e48aec7SDavid S. Miller 	if (dp)
3688e48aec7SDavid S. Miller 		data->lastnode = dp;
3698e48aec7SDavid S. Miller 
3708e48aec7SDavid S. Miller 	return dp;
3711da177e4SLinus Torvalds }
3721da177e4SLinus Torvalds 
3731da177e4SLinus Torvalds /* Copy in a whole string from userspace into kernelspace. */
copyin_string(char __user * user,size_t len)37421916a4aSSam Ravnborg static char * copyin_string(char __user *user, size_t len)
3751da177e4SLinus Torvalds {
3761da177e4SLinus Torvalds 	if ((ssize_t)len < 0 || (ssize_t)(len + 1) < 0)
37721916a4aSSam Ravnborg 		return ERR_PTR(-EINVAL);
3781da177e4SLinus Torvalds 
37921916a4aSSam Ravnborg 	return memdup_user_nul(user, len);
3801da177e4SLinus Torvalds }
3811da177e4SLinus Torvalds 
3821da177e4SLinus Torvalds /*
3831da177e4SLinus Torvalds  *	NetBSD /dev/openprom ioctl calls.
3841da177e4SLinus Torvalds  */
opiocget(void __user * argp,DATA * data)3858e48aec7SDavid S. Miller static int opiocget(void __user *argp, DATA *data)
3868e48aec7SDavid S. Miller {
3878e48aec7SDavid S. Miller 	struct opiocdesc op;
3888e48aec7SDavid S. Miller 	struct device_node *dp;
3898e48aec7SDavid S. Miller 	char *str;
390ccf0dec6SStephen Rothwell 	const void *pval;
3918e48aec7SDavid S. Miller 	int err, len;
3928e48aec7SDavid S. Miller 
3938e48aec7SDavid S. Miller 	if (copy_from_user(&op, argp, sizeof(op)))
3948e48aec7SDavid S. Miller 		return -EFAULT;
3958e48aec7SDavid S. Miller 
3968e48aec7SDavid S. Miller 	dp = get_node(op.op_nodeid, data);
3978e48aec7SDavid S. Miller 
39821916a4aSSam Ravnborg 	str = copyin_string(op.op_name, op.op_namelen);
39921916a4aSSam Ravnborg 	if (IS_ERR(str))
40021916a4aSSam Ravnborg 		return PTR_ERR(str);
4018e48aec7SDavid S. Miller 
4028e48aec7SDavid S. Miller 	pval = of_get_property(dp, str, &len);
4038e48aec7SDavid S. Miller 	err = 0;
4048e48aec7SDavid S. Miller 	if (!pval || len > op.op_buflen) {
4058e48aec7SDavid S. Miller 		err = -EINVAL;
4068e48aec7SDavid S. Miller 	} else {
4078e48aec7SDavid S. Miller 		op.op_buflen = len;
4088e48aec7SDavid S. Miller 		if (copy_to_user(argp, &op, sizeof(op)) ||
4098e48aec7SDavid S. Miller 		    copy_to_user(op.op_buf, pval, len))
4108e48aec7SDavid S. Miller 			err = -EFAULT;
4118e48aec7SDavid S. Miller 	}
4128e48aec7SDavid S. Miller 	kfree(str);
4138e48aec7SDavid S. Miller 
4148e48aec7SDavid S. Miller 	return err;
4158e48aec7SDavid S. Miller }
4168e48aec7SDavid S. Miller 
opiocnextprop(void __user * argp,DATA * data)4178e48aec7SDavid S. Miller static int opiocnextprop(void __user *argp, DATA *data)
4188e48aec7SDavid S. Miller {
4198e48aec7SDavid S. Miller 	struct opiocdesc op;
4208e48aec7SDavid S. Miller 	struct device_node *dp;
4218e48aec7SDavid S. Miller 	struct property *prop;
4228e48aec7SDavid S. Miller 	char *str;
42321916a4aSSam Ravnborg 	int len;
4248e48aec7SDavid S. Miller 
4258e48aec7SDavid S. Miller 	if (copy_from_user(&op, argp, sizeof(op)))
4268e48aec7SDavid S. Miller 		return -EFAULT;
4278e48aec7SDavid S. Miller 
4288e48aec7SDavid S. Miller 	dp = get_node(op.op_nodeid, data);
4298e48aec7SDavid S. Miller 	if (!dp)
4308e48aec7SDavid S. Miller 		return -EINVAL;
4318e48aec7SDavid S. Miller 
43221916a4aSSam Ravnborg 	str = copyin_string(op.op_name, op.op_namelen);
43321916a4aSSam Ravnborg 	if (IS_ERR(str))
43421916a4aSSam Ravnborg 		return PTR_ERR(str);
4358e48aec7SDavid S. Miller 
4368e48aec7SDavid S. Miller 	if (str[0] == '\0') {
4378e48aec7SDavid S. Miller 		prop = dp->properties;
4388e48aec7SDavid S. Miller 	} else {
4398e48aec7SDavid S. Miller 		prop = of_find_property(dp, str, NULL);
4408e48aec7SDavid S. Miller 		if (prop)
4418e48aec7SDavid S. Miller 			prop = prop->next;
4428e48aec7SDavid S. Miller 	}
4438e48aec7SDavid S. Miller 	kfree(str);
4448e48aec7SDavid S. Miller 
4458e48aec7SDavid S. Miller 	if (!prop)
4468e48aec7SDavid S. Miller 		len = 0;
4478e48aec7SDavid S. Miller 	else
4488e48aec7SDavid S. Miller 		len = prop->length;
4498e48aec7SDavid S. Miller 
4508e48aec7SDavid S. Miller 	if (len > op.op_buflen)
4518e48aec7SDavid S. Miller 		len = op.op_buflen;
4528e48aec7SDavid S. Miller 
4538e48aec7SDavid S. Miller 	if (copy_to_user(argp, &op, sizeof(op)))
4548e48aec7SDavid S. Miller 		return -EFAULT;
4558e48aec7SDavid S. Miller 
4568e48aec7SDavid S. Miller 	if (len &&
4578e48aec7SDavid S. Miller 	    copy_to_user(op.op_buf, prop->value, len))
4588e48aec7SDavid S. Miller 		return -EFAULT;
4598e48aec7SDavid S. Miller 
4608e48aec7SDavid S. Miller 	return 0;
4618e48aec7SDavid S. Miller }
4628e48aec7SDavid S. Miller 
opiocset(void __user * argp,DATA * data)4638e48aec7SDavid S. Miller static int opiocset(void __user *argp, DATA *data)
4648e48aec7SDavid S. Miller {
4658e48aec7SDavid S. Miller 	struct opiocdesc op;
4668e48aec7SDavid S. Miller 	struct device_node *dp;
4678e48aec7SDavid S. Miller 	char *str, *tmp;
4688e48aec7SDavid S. Miller 	int err;
4698e48aec7SDavid S. Miller 
4708e48aec7SDavid S. Miller 	if (copy_from_user(&op, argp, sizeof(op)))
4718e48aec7SDavid S. Miller 		return -EFAULT;
4728e48aec7SDavid S. Miller 
4738e48aec7SDavid S. Miller 	dp = get_node(op.op_nodeid, data);
4748e48aec7SDavid S. Miller 	if (!dp)
4758e48aec7SDavid S. Miller 		return -EINVAL;
4768e48aec7SDavid S. Miller 
47721916a4aSSam Ravnborg 	str = copyin_string(op.op_name, op.op_namelen);
47821916a4aSSam Ravnborg 	if (IS_ERR(str))
47921916a4aSSam Ravnborg 		return PTR_ERR(str);
4808e48aec7SDavid S. Miller 
48121916a4aSSam Ravnborg 	tmp = copyin_string(op.op_buf, op.op_buflen);
48221916a4aSSam Ravnborg 	if (IS_ERR(tmp)) {
4838e48aec7SDavid S. Miller 		kfree(str);
48421916a4aSSam Ravnborg 		return PTR_ERR(tmp);
4858e48aec7SDavid S. Miller 	}
4868e48aec7SDavid S. Miller 
4878e48aec7SDavid S. Miller 	err = of_set_property(dp, str, tmp, op.op_buflen);
4888e48aec7SDavid S. Miller 
4898e48aec7SDavid S. Miller 	kfree(str);
4908e48aec7SDavid S. Miller 	kfree(tmp);
4918e48aec7SDavid S. Miller 
4928e48aec7SDavid S. Miller 	return err;
4938e48aec7SDavid S. Miller }
4948e48aec7SDavid S. Miller 
opiocgetnext(unsigned int cmd,void __user * argp)4958e48aec7SDavid S. Miller static int opiocgetnext(unsigned int cmd, void __user *argp)
4968e48aec7SDavid S. Miller {
4978e48aec7SDavid S. Miller 	struct device_node *dp;
4988e48aec7SDavid S. Miller 	phandle nd;
4998e48aec7SDavid S. Miller 
5008e48aec7SDavid S. Miller 	BUILD_BUG_ON(sizeof(phandle) != sizeof(int));
5018e48aec7SDavid S. Miller 
5028e48aec7SDavid S. Miller 	if (copy_from_user(&nd, argp, sizeof(phandle)))
5038e48aec7SDavid S. Miller 		return -EFAULT;
5048e48aec7SDavid S. Miller 
5058e48aec7SDavid S. Miller 	if (nd == 0) {
5068e48aec7SDavid S. Miller 		if (cmd != OPIOCGETNEXT)
5078e48aec7SDavid S. Miller 			return -EINVAL;
5088e48aec7SDavid S. Miller 		dp = of_find_node_by_path("/");
5098e48aec7SDavid S. Miller 	} else {
5108e48aec7SDavid S. Miller 		dp = of_find_node_by_phandle(nd);
5118e48aec7SDavid S. Miller 		nd = 0;
5128e48aec7SDavid S. Miller 		if (dp) {
5138e48aec7SDavid S. Miller 			if (cmd == OPIOCGETNEXT)
5148e48aec7SDavid S. Miller 				dp = dp->sibling;
5158e48aec7SDavid S. Miller 			else
5168e48aec7SDavid S. Miller 				dp = dp->child;
5178e48aec7SDavid S. Miller 		}
5188e48aec7SDavid S. Miller 	}
5198e48aec7SDavid S. Miller 	if (dp)
5206016a363SGrant Likely 		nd = dp->phandle;
5218e48aec7SDavid S. Miller 	if (copy_to_user(argp, &nd, sizeof(phandle)))
5228e48aec7SDavid S. Miller 		return -EFAULT;
5238e48aec7SDavid S. Miller 
5248e48aec7SDavid S. Miller 	return 0;
5258e48aec7SDavid S. Miller }
5268e48aec7SDavid S. Miller 
openprom_bsd_ioctl(struct file * file,unsigned int cmd,unsigned long arg)52755929332SArnd Bergmann static int openprom_bsd_ioctl(struct file * file,
5281da177e4SLinus Torvalds 			      unsigned int cmd, unsigned long arg)
5291da177e4SLinus Torvalds {
53033cfe65aSJoe Perches 	DATA *data = file->private_data;
5311da177e4SLinus Torvalds 	void __user *argp = (void __user *)arg;
5328e48aec7SDavid S. Miller 	int err;
5331da177e4SLinus Torvalds 
534a3108ca2SArnd Bergmann 	mutex_lock(&openprom_mutex);
5351da177e4SLinus Torvalds 	switch (cmd) {
5361da177e4SLinus Torvalds 	case OPIOCGET:
5378e48aec7SDavid S. Miller 		err = opiocget(argp, data);
5388e48aec7SDavid S. Miller 		break;
5391da177e4SLinus Torvalds 
5401da177e4SLinus Torvalds 	case OPIOCNEXTPROP:
5418e48aec7SDavid S. Miller 		err = opiocnextprop(argp, data);
5428e48aec7SDavid S. Miller 		break;
5431da177e4SLinus Torvalds 
5441da177e4SLinus Torvalds 	case OPIOCSET:
5458e48aec7SDavid S. Miller 		err = opiocset(argp, data);
5468e48aec7SDavid S. Miller 		break;
5471da177e4SLinus Torvalds 
5481da177e4SLinus Torvalds 	case OPIOCGETOPTNODE:
5498e48aec7SDavid S. Miller 		BUILD_BUG_ON(sizeof(phandle) != sizeof(int));
5508e48aec7SDavid S. Miller 
55155929332SArnd Bergmann 		err = 0;
5526016a363SGrant Likely 		if (copy_to_user(argp, &options_node->phandle, sizeof(phandle)))
55355929332SArnd Bergmann 			err = -EFAULT;
55455929332SArnd Bergmann 		break;
5551da177e4SLinus Torvalds 
5561da177e4SLinus Torvalds 	case OPIOCGETNEXT:
5571da177e4SLinus Torvalds 	case OPIOCGETCHILD:
5588e48aec7SDavid S. Miller 		err = opiocgetnext(cmd, argp);
5598e48aec7SDavid S. Miller 		break;
5601da177e4SLinus Torvalds 
5611da177e4SLinus Torvalds 	default:
56255929332SArnd Bergmann 		err = -EINVAL;
56355929332SArnd Bergmann 		break;
564da201161SPeter Senna Tschudin 	}
565a3108ca2SArnd Bergmann 	mutex_unlock(&openprom_mutex);
5668e48aec7SDavid S. Miller 
5678e48aec7SDavid S. Miller 	return err;
5681da177e4SLinus Torvalds }
5691da177e4SLinus Torvalds 
5701da177e4SLinus Torvalds 
5711da177e4SLinus Torvalds /*
5721da177e4SLinus Torvalds  *	Handoff control to the correct ioctl handler.
5731da177e4SLinus Torvalds  */
openprom_ioctl(struct file * file,unsigned int cmd,unsigned long arg)57455929332SArnd Bergmann static long openprom_ioctl(struct file * file,
5751da177e4SLinus Torvalds 			   unsigned int cmd, unsigned long arg)
5761da177e4SLinus Torvalds {
57733cfe65aSJoe Perches 	DATA *data = file->private_data;
5781da177e4SLinus Torvalds 
5791da177e4SLinus Torvalds 	switch (cmd) {
5801da177e4SLinus Torvalds 	case OPROMGETOPT:
5811da177e4SLinus Torvalds 	case OPROMNXTOPT:
5821da177e4SLinus Torvalds 		if ((file->f_mode & FMODE_READ) == 0)
5831da177e4SLinus Torvalds 			return -EPERM;
58455929332SArnd Bergmann 		return openprom_sunos_ioctl(file, cmd, arg,
5851da177e4SLinus Torvalds 					    options_node);
5861da177e4SLinus Torvalds 
5871da177e4SLinus Torvalds 	case OPROMSETOPT:
5881da177e4SLinus Torvalds 	case OPROMSETOPT2:
5891da177e4SLinus Torvalds 		if ((file->f_mode & FMODE_WRITE) == 0)
5901da177e4SLinus Torvalds 			return -EPERM;
59155929332SArnd Bergmann 		return openprom_sunos_ioctl(file, cmd, arg,
5921da177e4SLinus Torvalds 					    options_node);
5931da177e4SLinus Torvalds 
5941da177e4SLinus Torvalds 	case OPROMNEXT:
5951da177e4SLinus Torvalds 	case OPROMCHILD:
5961da177e4SLinus Torvalds 	case OPROMGETPROP:
5971da177e4SLinus Torvalds 	case OPROMNXTPROP:
5981da177e4SLinus Torvalds 		if ((file->f_mode & FMODE_READ) == 0)
5991da177e4SLinus Torvalds 			return -EPERM;
60055929332SArnd Bergmann 		return openprom_sunos_ioctl(file, cmd, arg,
6011da177e4SLinus Torvalds 					    data->current_node);
6021da177e4SLinus Torvalds 
6031da177e4SLinus Torvalds 	case OPROMU2P:
6041da177e4SLinus Torvalds 	case OPROMGETCONS:
6051da177e4SLinus Torvalds 	case OPROMGETFBNAME:
6061da177e4SLinus Torvalds 	case OPROMGETBOOTARGS:
6071da177e4SLinus Torvalds 	case OPROMSETCUR:
6081da177e4SLinus Torvalds 	case OPROMPCI2NODE:
6091da177e4SLinus Torvalds 	case OPROMPATH2NODE:
6101da177e4SLinus Torvalds 		if ((file->f_mode & FMODE_READ) == 0)
6111da177e4SLinus Torvalds 			return -EPERM;
61255929332SArnd Bergmann 		return openprom_sunos_ioctl(file, cmd, arg, NULL);
6131da177e4SLinus Torvalds 
6141da177e4SLinus Torvalds 	case OPIOCGET:
6151da177e4SLinus Torvalds 	case OPIOCNEXTPROP:
6161da177e4SLinus Torvalds 	case OPIOCGETOPTNODE:
6171da177e4SLinus Torvalds 	case OPIOCGETNEXT:
6181da177e4SLinus Torvalds 	case OPIOCGETCHILD:
6191da177e4SLinus Torvalds 		if ((file->f_mode & FMODE_READ) == 0)
6201da177e4SLinus Torvalds 			return -EBADF;
62155929332SArnd Bergmann 		return openprom_bsd_ioctl(file,cmd,arg);
6221da177e4SLinus Torvalds 
6231da177e4SLinus Torvalds 	case OPIOCSET:
6241da177e4SLinus Torvalds 		if ((file->f_mode & FMODE_WRITE) == 0)
6251da177e4SLinus Torvalds 			return -EBADF;
62655929332SArnd Bergmann 		return openprom_bsd_ioctl(file,cmd,arg);
6271da177e4SLinus Torvalds 
6281da177e4SLinus Torvalds 	default:
6291da177e4SLinus Torvalds 		return -EINVAL;
6308e48aec7SDavid S. Miller 	};
6311da177e4SLinus Torvalds }
6321da177e4SLinus Torvalds 
openprom_compat_ioctl(struct file * file,unsigned int cmd,unsigned long arg)633b31023fcSChristoph Hellwig static long openprom_compat_ioctl(struct file *file, unsigned int cmd,
634b31023fcSChristoph Hellwig 		unsigned long arg)
635b31023fcSChristoph Hellwig {
636b31023fcSChristoph Hellwig 	long rval = -ENOTTY;
637b31023fcSChristoph Hellwig 
638b31023fcSChristoph Hellwig 	/*
639b31023fcSChristoph Hellwig 	 * SunOS/Solaris only, the NetBSD one's have embedded pointers in
640b31023fcSChristoph Hellwig 	 * the arg which we'd need to clean up...
641b31023fcSChristoph Hellwig 	 */
642b31023fcSChristoph Hellwig 	switch (cmd) {
643b31023fcSChristoph Hellwig 	case OPROMGETOPT:
644b31023fcSChristoph Hellwig 	case OPROMSETOPT:
645b31023fcSChristoph Hellwig 	case OPROMNXTOPT:
646b31023fcSChristoph Hellwig 	case OPROMSETOPT2:
647b31023fcSChristoph Hellwig 	case OPROMNEXT:
648b31023fcSChristoph Hellwig 	case OPROMCHILD:
649b31023fcSChristoph Hellwig 	case OPROMGETPROP:
650b31023fcSChristoph Hellwig 	case OPROMNXTPROP:
651b31023fcSChristoph Hellwig 	case OPROMU2P:
652b31023fcSChristoph Hellwig 	case OPROMGETCONS:
653b31023fcSChristoph Hellwig 	case OPROMGETFBNAME:
654b31023fcSChristoph Hellwig 	case OPROMGETBOOTARGS:
655b31023fcSChristoph Hellwig 	case OPROMSETCUR:
656b31023fcSChristoph Hellwig 	case OPROMPCI2NODE:
657b31023fcSChristoph Hellwig 	case OPROMPATH2NODE:
65855929332SArnd Bergmann 		rval = openprom_ioctl(file, cmd, arg);
659b31023fcSChristoph Hellwig 		break;
660b31023fcSChristoph Hellwig 	}
661d5a858bcSDavid S. Miller 
662d5a858bcSDavid S. Miller 	return rval;
663b31023fcSChristoph Hellwig }
664b31023fcSChristoph Hellwig 
openprom_open(struct inode * inode,struct file * file)6651da177e4SLinus Torvalds static int openprom_open(struct inode * inode, struct file * file)
6661da177e4SLinus Torvalds {
6671da177e4SLinus Torvalds 	DATA *data;
6681da177e4SLinus Torvalds 
6698e48aec7SDavid S. Miller 	data = kmalloc(sizeof(DATA), GFP_KERNEL);
6701da177e4SLinus Torvalds 	if (!data)
6711da177e4SLinus Torvalds 		return -ENOMEM;
6721da177e4SLinus Torvalds 
673a3108ca2SArnd Bergmann 	mutex_lock(&openprom_mutex);
6748e48aec7SDavid S. Miller 	data->current_node = of_find_node_by_path("/");
6758e48aec7SDavid S. Miller 	data->lastnode = data->current_node;
6761da177e4SLinus Torvalds 	file->private_data = (void *) data;
677a3108ca2SArnd Bergmann 	mutex_unlock(&openprom_mutex);
6781da177e4SLinus Torvalds 
6791da177e4SLinus Torvalds 	return 0;
6801da177e4SLinus Torvalds }
6811da177e4SLinus Torvalds 
openprom_release(struct inode * inode,struct file * file)6821da177e4SLinus Torvalds static int openprom_release(struct inode * inode, struct file * file)
6831da177e4SLinus Torvalds {
6841da177e4SLinus Torvalds 	kfree(file->private_data);
6851da177e4SLinus Torvalds 	return 0;
6861da177e4SLinus Torvalds }
6871da177e4SLinus Torvalds 
68800977a59SArjan van de Ven static const struct file_operations openprom_fops = {
6891da177e4SLinus Torvalds 	.owner =	THIS_MODULE,
6901da177e4SLinus Torvalds 	.llseek =	no_llseek,
69155929332SArnd Bergmann 	.unlocked_ioctl = openprom_ioctl,
692d5a858bcSDavid S. Miller 	.compat_ioctl =	openprom_compat_ioctl,
6931da177e4SLinus Torvalds 	.open =		openprom_open,
6941da177e4SLinus Torvalds 	.release =	openprom_release,
6951da177e4SLinus Torvalds };
6961da177e4SLinus Torvalds 
6971da177e4SLinus Torvalds static struct miscdevice openprom_dev = {
6988e48aec7SDavid S. Miller 	.minor		= SUN_OPENPROM_MINOR,
6998e48aec7SDavid S. Miller 	.name		= "openprom",
7008e48aec7SDavid S. Miller 	.fops		= &openprom_fops,
7011da177e4SLinus Torvalds };
7021da177e4SLinus Torvalds 
openprom_init(void)7031da177e4SLinus Torvalds static int __init openprom_init(void)
7041da177e4SLinus Torvalds {
7058e48aec7SDavid S. Miller 	int err;
7061da177e4SLinus Torvalds 
7078e48aec7SDavid S. Miller 	err = misc_register(&openprom_dev);
7088e48aec7SDavid S. Miller 	if (err)
7098e48aec7SDavid S. Miller 		return err;
7108e48aec7SDavid S. Miller 
711df58f37bSRob Herring 	options_node = of_get_child_by_name(of_find_node_by_path("/"), "options");
7128e48aec7SDavid S. Miller 	if (!options_node) {
7131da177e4SLinus Torvalds 		misc_deregister(&openprom_dev);
7141da177e4SLinus Torvalds 		return -EIO;
7151da177e4SLinus Torvalds 	}
7161da177e4SLinus Torvalds 
7171da177e4SLinus Torvalds 	return 0;
7181da177e4SLinus Torvalds }
7191da177e4SLinus Torvalds 
openprom_cleanup(void)7201da177e4SLinus Torvalds static void __exit openprom_cleanup(void)
7211da177e4SLinus Torvalds {
7221da177e4SLinus Torvalds 	misc_deregister(&openprom_dev);
7231da177e4SLinus Torvalds }
7241da177e4SLinus Torvalds 
7251da177e4SLinus Torvalds module_init(openprom_init);
7261da177e4SLinus Torvalds module_exit(openprom_cleanup);
727