xref: /illumos-gate/usr/src/uts/common/io/openprom.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"	/* SVr4 */
28 
29 /*
30  * Ported from 4.1.1_PSRA: "@(#)openprom.c 1.19 91/02/19 SMI";
31  *
32  * Porting notes:
33  *
34  * OPROMU2P unsupported after SunOS 4.x.
35  *
36  * Only one of these devices per system is allowed.
37  */
38 
39 /*
40  * Openprom eeprom options/devinfo driver.
41  */
42 
43 #include <sys/types.h>
44 #include <sys/errno.h>
45 #include <sys/file.h>
46 #include <sys/cmn_err.h>
47 #include <sys/kmem.h>
48 #include <sys/openpromio.h>
49 #include <sys/conf.h>
50 #include <sys/stat.h>
51 #include <sys/modctl.h>
52 #include <sys/debug.h>
53 #include <sys/autoconf.h>
54 #include <sys/ddi.h>
55 #include <sys/sunddi.h>
56 #include <sys/promif.h>
57 #include <sys/sysmacros.h>	/* offsetof */
58 #include <sys/nvpair.h>
59 #include <sys/wanboot_impl.h>
60 #include <sys/zone.h>
61 
62 #define	MAX_OPENS	32	/* Up to this many simultaneous opens */
63 
64 #define	IOC_IDLE	0	/* snapshot ioctl states */
65 #define	IOC_SNAP	1	/* snapshot in progress */
66 #define	IOC_DONE	2	/* snapshot done, but not copied out */
67 #define	IOC_COPY	3	/* copyout in progress */
68 
69 extern int plat_stdout_is_framebuffer(void);
70 extern int plat_stdin_is_keyboard(void);
71 
72 /*
73  * XXX	Make this dynamic.. or (better still) make the interface stateless
74  */
75 static struct oprom_state {
76 	dnode_t	current_id;	/* node we're fetching props from */
77 	int16_t	already_open;	/* if true, this instance is 'active' */
78 	int16_t	ioc_state;	/* snapshot ioctl state */
79 	char	*snapshot;	/* snapshot of all prom nodes */
80 	size_t	size;		/* size of snapshot */
81 	prom_generation_cookie_t tree_gen;
82 } oprom_state[MAX_OPENS];
83 
84 static kmutex_t oprom_lock;	/* serialize instance assignment */
85 
86 static int opromopen(dev_t *, int, int, cred_t *);
87 static int opromioctl(dev_t, int, intptr_t, int, cred_t *, int *);
88 static int opromclose(dev_t, int, int, cred_t *);
89 
90 static int opinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
91 		void **result);
92 static int opattach(dev_info_t *, ddi_attach_cmd_t cmd);
93 static int opdetach(dev_info_t *, ddi_detach_cmd_t cmd);
94 
95 /* help functions */
96 static int oprom_checknodeid(dnode_t, dnode_t);
97 static int oprom_copyinstr(intptr_t, char *, size_t, size_t);
98 static int oprom_copynode(dnode_t, uint_t, char **, size_t *);
99 static int oprom_snapshot(struct oprom_state *, intptr_t);
100 static int oprom_copyout(struct oprom_state *, intptr_t);
101 static int oprom_setstate(struct oprom_state *, int16_t);
102 
103 static struct cb_ops openeepr_cb_ops = {
104 	opromopen,		/* open */
105 	opromclose,		/* close */
106 	nodev,			/* strategy */
107 	nodev,			/* print */
108 	nodev,			/* dump */
109 	nodev,			/* read */
110 	nodev,			/* write */
111 	opromioctl,		/* ioctl */
112 	nodev,			/* devmap */
113 	nodev,			/* mmap */
114 	nodev,			/* segmap */
115 	nochpoll,		/* poll */
116 	ddi_prop_op,		/* prop_op */
117 	NULL,			/* streamtab  */
118 	D_NEW | D_MP		/* Driver compatibility flag */
119 };
120 
121 static struct dev_ops openeepr_ops = {
122 	DEVO_REV,		/* devo_rev, */
123 	0,			/* refcnt  */
124 	opinfo,			/* info */
125 	nulldev,		/* identify */
126 	nulldev,		/* probe */
127 	opattach,		/* attach */
128 	opdetach,		/* detach */
129 	nodev,			/* reset */
130 	&openeepr_cb_ops,	/* driver operations */
131 	NULL			/* bus operations */
132 };
133 
134 /*
135  * Module linkage information for the kernel.
136  */
137 static struct modldrv modldrv = {
138 	&mod_driverops,
139 	"OPENPROM/NVRAM Driver v%I%",
140 	&openeepr_ops
141 };
142 
143 static struct modlinkage modlinkage = {
144 	MODREV_1,
145 	&modldrv,
146 	NULL
147 };
148 
149 int
150 _init(void)
151 {
152 	int	error;
153 
154 	mutex_init(&oprom_lock, NULL, MUTEX_DRIVER, NULL);
155 
156 	error = mod_install(&modlinkage);
157 	if (error != 0) {
158 		mutex_destroy(&oprom_lock);
159 		return (error);
160 	}
161 
162 	return (0);
163 }
164 
165 int
166 _info(struct modinfo *modinfop)
167 {
168 	return (mod_info(&modlinkage, modinfop));
169 }
170 
171 int
172 _fini(void)
173 {
174 	int	error;
175 
176 	error = mod_remove(&modlinkage);
177 	if (error != 0)
178 		return (error);
179 
180 	mutex_destroy(&oprom_lock);
181 	return (0);
182 }
183 
184 static dev_info_t *opdip;
185 static dnode_t options_nodeid;
186 
187 /*ARGSUSED*/
188 static int
189 opinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
190 {
191 	int error = DDI_FAILURE;
192 
193 	switch (infocmd) {
194 	case DDI_INFO_DEVT2DEVINFO:
195 		*result = (void *)opdip;
196 		error = DDI_SUCCESS;
197 		break;
198 	case DDI_INFO_DEVT2INSTANCE:
199 		/* All dev_t's map to the same, single instance */
200 		*result = (void *)0;
201 		error = DDI_SUCCESS;
202 		break;
203 	default:
204 		break;
205 	}
206 
207 	return (error);
208 }
209 
210 static int
211 opattach(dev_info_t *dip, ddi_attach_cmd_t cmd)
212 {
213 	switch (cmd) {
214 
215 	case DDI_ATTACH:
216 		if (prom_is_openprom()) {
217 			options_nodeid = prom_optionsnode();
218 		} else {
219 			options_nodeid = OBP_BADNODE;
220 		}
221 
222 		opdip = dip;
223 
224 		if (ddi_create_minor_node(dip, "openprom", S_IFCHR,
225 		    0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
226 			return (DDI_FAILURE);
227 		}
228 
229 		return (DDI_SUCCESS);
230 
231 	default:
232 		return (DDI_FAILURE);
233 	}
234 }
235 
236 static int
237 opdetach(dev_info_t *dip, ddi_detach_cmd_t cmd)
238 {
239 	if (cmd != DDI_DETACH)
240 		return (DDI_FAILURE);
241 
242 	ddi_remove_minor_node(dip, NULL);
243 	opdip = NULL;
244 
245 	return (DDI_SUCCESS);
246 }
247 
248 /*
249  * Allow multiple opens by tweaking the dev_t such that it looks like each
250  * open is getting a different minor device.  Each minor gets a separate
251  * entry in the oprom_state[] table.
252  */
253 /*ARGSUSED*/
254 static int
255 opromopen(dev_t *devp, int flag, int otyp, cred_t *credp)
256 {
257 	int m;
258 	struct oprom_state *st = oprom_state;
259 
260 	if (getminor(*devp) != 0)
261 		return (ENXIO);
262 
263 	mutex_enter(&oprom_lock);
264 	for (m = 0; m < MAX_OPENS; m++)
265 		if (st->already_open)
266 			st++;
267 		else {
268 			st->already_open = 1;
269 			/*
270 			 * It's ours.
271 			 */
272 			st->current_id = (dnode_t)0;
273 			ASSERT(st->snapshot == NULL && st->size == 0);
274 			ASSERT(st->ioc_state == IOC_IDLE);
275 			break;
276 		}
277 	mutex_exit(&oprom_lock);
278 
279 	if (m == MAX_OPENS)  {
280 		/*
281 		 * "Thank you for calling, but all our lines are
282 		 * busy at the moment.."
283 		 *
284 		 * We could get sophisticated here, and go into a
285 		 * sleep-retry loop .. but hey, I just can't see
286 		 * that many processes sitting in this driver.
287 		 *
288 		 * (And if it does become possible, then we should
289 		 * change the interface so that the 'state' is held
290 		 * external to the driver)
291 		 */
292 		return (EAGAIN);
293 	}
294 
295 	*devp = makedevice(getmajor(*devp), (minor_t)m);
296 
297 	return (0);
298 }
299 
300 /*ARGSUSED*/
301 static int
302 opromclose(dev_t dev, int flag, int otype, cred_t *cred_p)
303 {
304 	struct oprom_state *st;
305 
306 	st = &oprom_state[getminor(dev)];
307 	ASSERT(getminor(dev) < MAX_OPENS && st->already_open != 0);
308 	if (st->snapshot) {
309 		kmem_free(st->snapshot, st->size);
310 		st->snapshot = NULL;
311 		st->size = 0;
312 		st->ioc_state = IOC_IDLE;
313 	}
314 	mutex_enter(&oprom_lock);
315 	st->already_open = 0;
316 	mutex_exit(&oprom_lock);
317 
318 	return (0);
319 }
320 
321 struct opromioctl_args {
322 	struct oprom_state *st;
323 	int cmd;
324 	intptr_t arg;
325 	int mode;
326 };
327 
328 /*ARGSUSED*/
329 static int
330 opromioctl_cb(void *avp, int has_changed)
331 {
332 	struct opromioctl_args *argp = avp;
333 	int cmd;
334 	intptr_t arg;
335 	int mode;
336 	struct oprom_state *st;
337 	struct openpromio *opp;
338 	int valsize;
339 	char *valbuf;
340 	int error = 0;
341 	uint_t userbufsize;
342 	dnode_t node_id;
343 	char propname[OBP_MAXPROPNAME];
344 
345 	st = argp->st;
346 	cmd = argp->cmd;
347 	arg = argp->arg;
348 	mode = argp->mode;
349 
350 	if (has_changed) {
351 		/*
352 		 * The prom tree has changed since we last used current_id,
353 		 * so we need to check it.
354 		 */
355 		if ((st->current_id != OBP_NONODE) &&
356 		    (st->current_id != OBP_BADNODE)) {
357 			if (oprom_checknodeid(st->current_id, OBP_NONODE) == 0)
358 				st->current_id = OBP_BADNODE;
359 		}
360 	}
361 
362 	/*
363 	 * Check permissions
364 	 * and weed out unsupported commands on x86 platform
365 	 */
366 	switch (cmd) {
367 #if !defined(__i386) && !defined(__amd64)
368 	case OPROMLISTKEYSLEN:
369 		valsize = prom_asr_list_keys_len();
370 		opp = (struct openpromio *)kmem_zalloc(
371 		    sizeof (uint_t) + 1, KM_SLEEP);
372 		opp->oprom_size = valsize;
373 		if (copyout(opp, (void *)arg, (sizeof (uint_t))) != 0)
374 		    error = EFAULT;
375 		kmem_free(opp, sizeof (uint_t) + 1);
376 		break;
377 	case OPROMLISTKEYS:
378 		valsize = prom_asr_list_keys_len();
379 		if (copyin((void *)arg, &userbufsize, sizeof (uint_t)) != 0)
380 		    return (EFAULT);
381 		if (valsize > userbufsize)
382 		    return (EINVAL);
383 		valbuf = (char *)kmem_zalloc(valsize + 1, KM_SLEEP);
384 		if (prom_asr_list_keys((caddr_t)valbuf) == -1) {
385 			kmem_free(valbuf, valsize + 1);
386 			return (EFAULT);
387 		}
388 		opp = (struct openpromio *)kmem_zalloc(
389 		    valsize + sizeof (uint_t) + 1, KM_SLEEP);
390 		opp->oprom_size = valsize;
391 		bcopy(valbuf, opp->oprom_array, valsize);
392 		if (copyout(opp, (void *)arg, (valsize + sizeof (uint_t))) != 0)
393 		    error = EFAULT;
394 		kmem_free(valbuf, valsize + 1);
395 		kmem_free(opp, valsize + sizeof (uint_t) + 1);
396 		break;
397 	case OPROMEXPORT:
398 		valsize = prom_asr_export_len();
399 		if (copyin((void *)arg, &userbufsize, sizeof (uint_t)) != 0)
400 		    return (EFAULT);
401 		if (valsize > userbufsize)
402 		    return (EINVAL);
403 		valbuf = (char *)kmem_zalloc(valsize + 1, KM_SLEEP);
404 		if (prom_asr_export((caddr_t)valbuf) == -1) {
405 			kmem_free(valbuf, valsize + 1);
406 			return (EFAULT);
407 		}
408 		opp = (struct openpromio *)kmem_zalloc(
409 		    valsize + sizeof (uint_t) + 1, KM_SLEEP);
410 		opp->oprom_size = valsize;
411 		bcopy(valbuf, opp->oprom_array, valsize);
412 		if (copyout(opp, (void *)arg, (valsize + sizeof (uint_t))) != 0)
413 		    error = EFAULT;
414 		kmem_free(valbuf, valsize + 1);
415 		kmem_free(opp, valsize + sizeof (uint_t) + 1);
416 		break;
417 	case OPROMEXPORTLEN:
418 		valsize = prom_asr_export_len();
419 		opp = (struct openpromio *)kmem_zalloc(
420 		    sizeof (uint_t) + 1, KM_SLEEP);
421 		opp->oprom_size = valsize;
422 		if (copyout(opp, (void *)arg, (sizeof (uint_t))) != 0)
423 		    error = EFAULT;
424 		kmem_free(opp, sizeof (uint_t) + 1);
425 		break;
426 #endif
427 	case OPROMGETOPT:
428 	case OPROMNXTOPT:
429 		if ((mode & FREAD) == 0) {
430 			return (EPERM);
431 		}
432 		node_id = options_nodeid;
433 		break;
434 
435 	case OPROMSETOPT:
436 	case OPROMSETOPT2:
437 #if !defined(__i386) && !defined(__amd64)
438 		if (mode & FWRITE) {
439 			node_id = options_nodeid;
440 			break;
441 		}
442 #endif /* !__i386 && !__amd64 */
443 		return (EPERM);
444 
445 	case OPROMNEXT:
446 	case OPROMCHILD:
447 	case OPROMGETPROP:
448 	case OPROMGETPROPLEN:
449 	case OPROMNXTPROP:
450 	case OPROMSETNODEID:
451 		if ((mode & FREAD) == 0) {
452 			return (EPERM);
453 		}
454 		node_id = st->current_id;
455 		break;
456 	case OPROMCOPYOUT:
457 		if (st->snapshot == NULL)
458 			return (EINVAL);
459 		/*FALLTHROUGH*/
460 	case OPROMSNAPSHOT:
461 	case OPROMGETCONS:
462 	case OPROMGETBOOTARGS:
463 	case OPROMGETVERSION:
464 	case OPROMPATH2DRV:
465 	case OPROMPROM2DEVNAME:
466 #if !defined(__i386) && !defined(__amd64)
467 	case OPROMGETFBNAME:
468 	case OPROMDEV2PROMNAME:
469 	case OPROMREADY64:
470 #endif	/* !__i386 && !__amd64 */
471 		if ((mode & FREAD) == 0) {
472 			return (EPERM);
473 		}
474 		break;
475 
476 #if !defined(__i386) && !defined(__amd64)
477 	case WANBOOT_SETKEY:
478 		if (!(mode & FWRITE))
479 			return (EPERM);
480 		break;
481 #endif	/* !__i386 && !defined(__amd64) */
482 
483 	default:
484 		return (EINVAL);
485 	}
486 
487 	/*
488 	 * Deal with SNAPSHOT and COPYOUT ioctls first
489 	 */
490 	switch (cmd) {
491 	case OPROMCOPYOUT:
492 		return (oprom_copyout(st, arg));
493 
494 	case OPROMSNAPSHOT:
495 		return (oprom_snapshot(st, arg));
496 	}
497 
498 	/*
499 	 * Copy in user argument length and allocation memory
500 	 *
501 	 * NB do not copyin the entire buffer we may not need
502 	 *	to. userbufsize can be as big as 32 K.
503 	 */
504 	if (copyin((void *)arg, &userbufsize, sizeof (uint_t)) != 0)
505 		return (EFAULT);
506 
507 	if (userbufsize == 0 || userbufsize > OPROMMAXPARAM)
508 		return (EINVAL);
509 
510 	opp = (struct openpromio *)kmem_zalloc(
511 	    userbufsize + sizeof (uint_t) + 1, KM_SLEEP);
512 
513 	/*
514 	 * Execute command
515 	 */
516 	switch (cmd) {
517 
518 	case OPROMGETOPT:
519 	case OPROMGETPROP:
520 	case OPROMGETPROPLEN:
521 
522 		if ((prom_is_openprom() == 0) ||
523 		    (node_id == OBP_NONODE) || (node_id == OBP_BADNODE)) {
524 			error = EINVAL;
525 			break;
526 		}
527 
528 		/*
529 		 * The argument, a NULL terminated string, is a prop name.
530 		 */
531 		if ((error = oprom_copyinstr(arg, opp->oprom_array,
532 		    (size_t)userbufsize, OBP_MAXPROPNAME)) != 0) {
533 			break;
534 		}
535 		(void) strcpy(propname, opp->oprom_array);
536 		valsize = prom_getproplen(node_id, propname);
537 
538 		/*
539 		 * 4010173: 'name' is a property, but not an option.
540 		 */
541 		if ((cmd == OPROMGETOPT) && (strcmp("name", propname) == 0))
542 			valsize = -1;
543 
544 		if (cmd == OPROMGETPROPLEN)  {
545 			int proplen = valsize;
546 
547 			if (userbufsize < sizeof (int)) {
548 				error = EINVAL;
549 				break;
550 			}
551 			opp->oprom_size = valsize = sizeof (int);
552 			bcopy(&proplen, opp->oprom_array, valsize);
553 		} else if (valsize > 0 && valsize <= userbufsize) {
554 			bzero(opp->oprom_array, valsize + 1);
555 			(void) prom_getprop(node_id, propname,
556 			    opp->oprom_array);
557 			opp->oprom_size = valsize;
558 			if (valsize < userbufsize)
559 				++valsize;	/* Forces NULL termination */
560 						/* If space permits */
561 		} else {
562 			/*
563 			 * XXX: There is no error code if the buf is too small.
564 			 * which is consistent with the current behavior.
565 			 *
566 			 * NB: This clause also handles the non-error
567 			 * zero length (boolean) property value case.
568 			 */
569 			opp->oprom_size = 0;
570 			(void) strcpy(opp->oprom_array, "");
571 			valsize = 1;
572 		}
573 		if (copyout(opp, (void *)arg, (valsize + sizeof (uint_t))) != 0)
574 			error = EFAULT;
575 		break;
576 
577 	case OPROMNXTOPT:
578 	case OPROMNXTPROP:
579 		if ((prom_is_openprom() == 0) ||
580 		    (node_id == OBP_NONODE) || (node_id == OBP_BADNODE)) {
581 			error = EINVAL;
582 			break;
583 		}
584 
585 		/*
586 		 * The argument, a NULL terminated string, is a prop name.
587 		 */
588 		if ((error = oprom_copyinstr(arg, opp->oprom_array,
589 		    (size_t)userbufsize, OBP_MAXPROPNAME)) != 0) {
590 			break;
591 		}
592 		valbuf = (char *)prom_nextprop(node_id, opp->oprom_array,
593 		    propname);
594 		valsize = strlen(valbuf);
595 
596 		/*
597 		 * 4010173: 'name' is a property, but it's not an option.
598 		 */
599 		if ((cmd == OPROMNXTOPT) && valsize &&
600 		    (strcmp(valbuf, "name") == 0)) {
601 			valbuf = (char *)prom_nextprop(node_id, "name",
602 			    propname);
603 			valsize = strlen(valbuf);
604 		}
605 
606 		if (valsize == 0) {
607 			opp->oprom_size = 0;
608 		} else if (++valsize <= userbufsize) {
609 			opp->oprom_size = valsize;
610 			bzero((caddr_t)opp->oprom_array, (size_t)valsize);
611 			bcopy((caddr_t)valbuf, (caddr_t)opp->oprom_array,
612 			    (size_t)valsize);
613 		}
614 
615 		if (copyout(opp, (void *)arg, valsize + sizeof (uint_t)) != 0)
616 			error = EFAULT;
617 		break;
618 
619 	case OPROMNEXT:
620 	case OPROMCHILD:
621 	case OPROMSETNODEID:
622 
623 		if (prom_is_openprom() == 0 ||
624 		    userbufsize < sizeof (dnode_t)) {
625 			error = EINVAL;
626 			break;
627 		}
628 
629 		/*
630 		 * The argument is a phandle. (aka dnode_t)
631 		 */
632 		if (copyin(((caddr_t)arg + sizeof (uint_t)),
633 		    opp->oprom_array, sizeof (dnode_t)) != 0) {
634 			error = EFAULT;
635 			break;
636 		}
637 
638 		/*
639 		 * If dnode_t from userland is garbage, we
640 		 * could confuse the PROM.
641 		 */
642 		node_id = *(dnode_t *)opp->oprom_array;
643 		if (oprom_checknodeid(node_id, st->current_id) == 0) {
644 			cmn_err(CE_NOTE, "!nodeid 0x%x not found",
645 			    (int)node_id);
646 			error = EINVAL;
647 			break;
648 		}
649 
650 		if (cmd == OPROMNEXT)
651 			st->current_id = prom_nextnode(node_id);
652 		else if (cmd == OPROMCHILD)
653 			st->current_id = prom_childnode(node_id);
654 		else {
655 			/* OPROMSETNODEID */
656 			st->current_id = node_id;
657 			break;
658 		}
659 
660 		opp->oprom_size = sizeof (dnode_t);
661 		*(dnode_t *)opp->oprom_array = st->current_id;
662 
663 		if (copyout(opp, (void *)arg,
664 		    sizeof (dnode_t) + sizeof (uint_t)) != 0)
665 			error = EFAULT;
666 		break;
667 
668 	case OPROMGETCONS:
669 		/*
670 		 * Is openboot supported on this machine?
671 		 * This ioctl used to return the console device,
672 		 * information; this is now done via modctl()
673 		 * in libdevinfo.
674 		 */
675 		opp->oprom_size = sizeof (char);
676 
677 		opp->oprom_array[0] |= prom_is_openprom() ?
678 		    OPROMCONS_OPENPROM : 0;
679 
680 		/*
681 		 * The rest of the info is needed by Install to
682 		 * decide if graphics should be started.
683 		 */
684 		if ((getzoneid() == GLOBAL_ZONEID) &&
685 		    plat_stdin_is_keyboard()) {
686 			opp->oprom_array[0] |= OPROMCONS_STDIN_IS_KBD;
687 		}
688 
689 		if ((getzoneid() == GLOBAL_ZONEID) &&
690 		    plat_stdout_is_framebuffer()) {
691 			opp->oprom_array[0] |= OPROMCONS_STDOUT_IS_FB;
692 		}
693 
694 		if (copyout(opp, (void *)arg,
695 		    sizeof (char) + sizeof (uint_t)) != 0)
696 			error = EFAULT;
697 		break;
698 
699 	case OPROMGETBOOTARGS: {
700 		extern char kern_bootargs[];
701 
702 		valsize = strlen(kern_bootargs) + 1;
703 		if (valsize > userbufsize) {
704 			error = EINVAL;
705 			break;
706 		}
707 		(void) strcpy(opp->oprom_array, kern_bootargs);
708 		opp->oprom_size = valsize - 1;
709 
710 		if (copyout(opp, (void *)arg, valsize + sizeof (uint_t)) != 0)
711 			error = EFAULT;
712 	}	break;
713 
714 	/*
715 	 * convert a prom device path to an equivalent devfs path
716 	 */
717 	case OPROMPROM2DEVNAME: {
718 		char *dev_name;
719 
720 		/*
721 		 * The input argument, a pathname, is a NULL terminated string.
722 		 */
723 		if ((error = oprom_copyinstr(arg, opp->oprom_array,
724 		    (size_t)userbufsize, MAXPATHLEN)) != 0) {
725 			break;
726 		}
727 
728 		dev_name = kmem_alloc(MAXPATHLEN, KM_SLEEP);
729 
730 		error = i_promname_to_devname(opp->oprom_array, dev_name);
731 		if (error != 0) {
732 			kmem_free(dev_name, MAXPATHLEN);
733 			break;
734 		}
735 		valsize = opp->oprom_size = strlen(dev_name);
736 		if (++valsize > userbufsize) {
737 			kmem_free(dev_name, MAXPATHLEN);
738 			error = EINVAL;
739 			break;
740 		}
741 		(void) strcpy(opp->oprom_array, dev_name);
742 		if (copyout(opp, (void *)arg, sizeof (uint_t) + valsize) != 0)
743 			error = EFAULT;
744 
745 		kmem_free(dev_name, MAXPATHLEN);
746 	}	break;
747 
748 	/*
749 	 * Convert a prom device path name to a driver name
750 	 */
751 	case OPROMPATH2DRV: {
752 		char *drv_name;
753 		major_t maj;
754 
755 		/*
756 		 * The input argument, a pathname, is a NULL terminated string.
757 		 */
758 		if ((error = oprom_copyinstr(arg, opp->oprom_array,
759 		    (size_t)userbufsize, MAXPATHLEN)) != 0) {
760 			break;
761 		}
762 
763 		/*
764 		 * convert path to a driver binding name
765 		 */
766 		maj = path_to_major((char *)opp->oprom_array);
767 		if (maj == (major_t)-1) {
768 			error = EINVAL;
769 			break;
770 		}
771 
772 		/*
773 		 * resolve any aliases
774 		 */
775 		if ((drv_name = ddi_major_to_name(maj)) == NULL) {
776 			error = EINVAL;
777 			break;
778 		}
779 
780 		(void) strcpy(opp->oprom_array, drv_name);
781 		opp->oprom_size = strlen(drv_name);
782 		if (copyout(opp, (void *)arg,
783 		    sizeof (uint_t) + opp->oprom_size + 1) != 0)
784 			error = EFAULT;
785 	}	break;
786 
787 	case OPROMGETVERSION:
788 		/*
789 		 * Get a string representing the running version of the
790 		 * prom. How to create such a string is platform dependent,
791 		 * so we just defer to a promif function. If no such
792 		 * association exists, the promif implementation
793 		 * may copy the string "unknown" into the given buffer,
794 		 * and return its length (incl. NULL terminator).
795 		 *
796 		 * We expect prom_version_name to return the actual
797 		 * length of the string, but copy at most userbufsize
798 		 * bytes into the given buffer, including NULL termination.
799 		 */
800 
801 		valsize = prom_version_name(opp->oprom_array, userbufsize);
802 		if (valsize < 0) {
803 			error = EINVAL;
804 			break;
805 		}
806 
807 		/*
808 		 * copyout only the part of the user buffer we need to.
809 		 */
810 		if (copyout(opp, (void *)arg,
811 		    (size_t)(min((uint_t)valsize, userbufsize) +
812 		    sizeof (uint_t))) != 0)
813 			error = EFAULT;
814 		break;
815 
816 #if !defined(__i386) && !defined(__amd64)
817 	case OPROMGETFBNAME:
818 		/*
819 		 * Return stdoutpath, if it's a frame buffer.
820 		 * Yes, we are comparing a possibly longer string against
821 		 * the size we're really going to copy, but so what?
822 		 */
823 		if ((getzoneid() == GLOBAL_ZONEID) &&
824 		    (prom_stdout_is_framebuffer() != 0) &&
825 		    (userbufsize > strlen(prom_stdoutpath()))) {
826 			prom_strip_options(prom_stdoutpath(),
827 			    opp->oprom_array);	/* strip options and copy */
828 			valsize = opp->oprom_size = strlen(opp->oprom_array);
829 			if (copyout(opp, (void *)arg,
830 			    valsize + 1 + sizeof (uint_t)) != 0)
831 				error = EFAULT;
832 		} else
833 			error = EINVAL;
834 		break;
835 
836 	/*
837 	 * Convert a logical or physical device path to prom device path
838 	 */
839 	case OPROMDEV2PROMNAME: {
840 		char *prom_name;
841 
842 		/*
843 		 * The input argument, a pathname, is a NULL terminated string.
844 		 */
845 		if ((error = oprom_copyinstr(arg, opp->oprom_array,
846 		    (size_t)userbufsize, MAXPATHLEN)) != 0) {
847 			break;
848 		}
849 
850 		prom_name = kmem_alloc(userbufsize, KM_SLEEP);
851 
852 		/*
853 		 * convert the devfs path to an equivalent prom path
854 		 */
855 		error = i_devname_to_promname(opp->oprom_array, prom_name,
856 		    userbufsize);
857 
858 		if (error != 0) {
859 			kmem_free(prom_name, userbufsize);
860 			break;
861 		}
862 
863 		for (valsize = 0; valsize < userbufsize; valsize++) {
864 			opp->oprom_array[valsize] = prom_name[valsize];
865 
866 			if ((valsize > 0) && (prom_name[valsize] == '\0') &&
867 			    (prom_name[valsize-1] == '\0')) {
868 				break;
869 			}
870 		}
871 		opp->oprom_size = valsize;
872 
873 		kmem_free(prom_name, userbufsize);
874 		if (copyout(opp, (void *)arg, sizeof (uint_t) + valsize) != 0)
875 			error = EFAULT;
876 
877 	}	break;
878 
879 	case OPROMSETOPT:
880 	case OPROMSETOPT2: {
881 		int namebuflen;
882 		int valbuflen;
883 
884 		if ((prom_is_openprom() == 0) ||
885 		    (node_id == OBP_NONODE) || (node_id == OBP_BADNODE)) {
886 			error = EINVAL;
887 			break;
888 		}
889 
890 		/*
891 		 * The arguments are a property name and a value.
892 		 * Copy in the entire user buffer.
893 		 */
894 		if (copyin(((caddr_t)arg + sizeof (uint_t)),
895 		    opp->oprom_array, userbufsize) != 0) {
896 			error = EFAULT;
897 			break;
898 		}
899 
900 		/*
901 		 * The property name is the first string, value second
902 		 */
903 		namebuflen = strlen(opp->oprom_array);
904 		valbuf = opp->oprom_array + namebuflen + 1;
905 		valbuflen = strlen(valbuf);
906 
907 		if (cmd == OPROMSETOPT) {
908 			valsize = valbuflen + 1;  /* +1 for the '\0' */
909 		} else {
910 			if ((namebuflen + 1 + valbuflen + 1) > userbufsize) {
911 				error = EINVAL;
912 				break;
913 			}
914 			valsize = (opp->oprom_array + userbufsize) - valbuf;
915 		}
916 
917 		/*
918 		 * 4010173: 'name' is not an option, but it is a property.
919 		 */
920 		if (strcmp(opp->oprom_array, "name") == 0)
921 			error = EINVAL;
922 		else if (prom_setprop(node_id, opp->oprom_array,
923 		    valbuf, valsize) < 0)
924 			error = EINVAL;
925 
926 	}	break;
927 
928 	case OPROMREADY64: {
929 		struct openprom_opr64 *opr =
930 		    (struct openprom_opr64 *)opp->oprom_array;
931 		int i;
932 		dnode_t id;
933 
934 		if (userbufsize < sizeof (*opr)) {
935 			error = EINVAL;
936 			break;
937 		}
938 
939 		valsize = userbufsize -
940 		    offsetof(struct openprom_opr64, message);
941 
942 		i = prom_version_check(opr->message, valsize, &id);
943 		opr->return_code = i;
944 		opr->nodeid = (int)id;
945 
946 		valsize = offsetof(struct openprom_opr64, message);
947 		valsize += strlen(opr->message) + 1;
948 
949 		/*
950 		 * copyout only the part of the user buffer we need to.
951 		 */
952 		if (copyout(opp, (void *)arg,
953 		    (size_t)(min((uint_t)valsize, userbufsize) +
954 		    sizeof (uint_t))) != 0)
955 			error = EFAULT;
956 		break;
957 
958 	}	/* case OPROMREADY64 */
959 
960 	case WANBOOT_SETKEY: {
961 		struct wankeyio *wp;
962 		int reslen;
963 		int status;
964 		int rv;
965 		int i;
966 
967 		/*
968 		 * The argument is a struct wankeyio.  Validate it as best
969 		 * we can.
970 		 */
971 		if (userbufsize != (sizeof (struct wankeyio))) {
972 			error = EINVAL;
973 			break;
974 		}
975 		if (copyin(((caddr_t)arg + sizeof (uint_t)),
976 		    opp->oprom_array, sizeof (struct wankeyio)) != 0) {
977 			error = EFAULT;
978 			break;
979 		}
980 		wp = (struct wankeyio *)opp->oprom_array;
981 
982 		/* check for key name and key size overflow */
983 		for (i = 0; i < WANBOOT_MAXKEYNAMELEN; i++)
984 			if (wp->wk_keyname[i] == '\0')
985 				break;
986 		if ((i == WANBOOT_MAXKEYNAMELEN) ||
987 		    (wp->wk_keysize > WANBOOT_MAXKEYLEN)) {
988 			error = EINVAL;
989 			break;
990 		}
991 
992 		rv = prom_set_security_key(wp->wk_keyname, wp->wk_u.key,
993 		    wp->wk_keysize, &reslen, &status);
994 		if (rv)
995 			error = EIO;
996 		else
997 			switch (status) {
998 				case 0:
999 					error = 0;
1000 					break;
1001 
1002 				case -2:	/* out of key storage space */
1003 					error = ENOSPC;
1004 					break;
1005 
1006 				case -3:	/* key name or value too long */
1007 					error = EINVAL;
1008 					break;
1009 
1010 				case -4:	/* can't delete:  no such key */
1011 					error = ENOENT;
1012 					break;
1013 
1014 				case -1:	/* unspecified error */
1015 				default:	/* this should not happen */
1016 					error = EIO;
1017 					break;
1018 			}
1019 		break;
1020 	}	/* case WANBOOT_SETKEY */
1021 #endif	/* !__i386 && !__amd64 */
1022 	}	/* switch (cmd)	*/
1023 
1024 	kmem_free(opp, userbufsize + sizeof (uint_t) + 1);
1025 	return (error);
1026 }
1027 
1028 /*ARGSUSED*/
1029 static int
1030 opromioctl(dev_t dev, int cmd, intptr_t arg, int mode,
1031 	cred_t *credp, int *rvalp)
1032 {
1033 	struct oprom_state *st;
1034 	struct opromioctl_args arg_block;
1035 
1036 	if (getminor(dev) >= MAX_OPENS)
1037 		return (ENXIO);
1038 
1039 	st = &oprom_state[getminor(dev)];
1040 	ASSERT(st->already_open);
1041 	arg_block.st = st;
1042 	arg_block.cmd = cmd;
1043 	arg_block.arg = arg;
1044 	arg_block.mode = mode;
1045 	return (prom_tree_access(opromioctl_cb, &arg_block, &st->tree_gen));
1046 }
1047 
1048 /*
1049  * Copyin string and verify the actual string length is less than maxsize
1050  * specified by the caller.
1051  *
1052  * Currently, maxsize is either OBP_MAXPROPNAME for property names
1053  * or MAXPATHLEN for device path names. userbufsize is specified
1054  * by the userland caller.
1055  */
1056 static int
1057 oprom_copyinstr(intptr_t arg, char *buf, size_t bufsize, size_t maxsize)
1058 {
1059 	int error;
1060 	size_t actual_len;
1061 
1062 	if ((error = copyinstr(((caddr_t)arg + sizeof (uint_t)),
1063 	    buf, bufsize, &actual_len)) != 0) {
1064 		return (error);
1065 	}
1066 	if ((actual_len == 0) || (actual_len > maxsize)) {
1067 		return (EINVAL);
1068 	}
1069 
1070 	return (0);
1071 }
1072 
1073 /*
1074  * Check dnode_t passed in from userland
1075  */
1076 static int
1077 oprom_checknodeid(dnode_t node_id, dnode_t current_id)
1078 {
1079 	int depth;
1080 	dnode_t id[OBP_STACKDEPTH];
1081 
1082 	/*
1083 	 * optimized path
1084 	 */
1085 	if (node_id == 0) {
1086 		return (1);
1087 	}
1088 	if (node_id == OBP_BADNODE) {
1089 		return (0);
1090 	}
1091 	if ((current_id != OBP_BADNODE) && ((node_id == current_id) ||
1092 	    (node_id == prom_nextnode(current_id)) ||
1093 	    (node_id == prom_childnode(current_id)))) {
1094 		return (1);
1095 	}
1096 
1097 	/*
1098 	 * long path: walk from root till we find node_id
1099 	 */
1100 	depth = 1;
1101 	id[0] = prom_nextnode((dnode_t)0);
1102 
1103 	while (depth) {
1104 		if (id[depth - 1] == node_id)
1105 			return (1);	/* node_id found */
1106 
1107 		if (id[depth] = prom_childnode(id[depth - 1])) {
1108 			depth++;
1109 			continue;
1110 		}
1111 
1112 		while (depth &&
1113 		    ((id[depth - 1] = prom_nextnode(id[depth - 1])) == 0))
1114 			depth--;
1115 	}
1116 	return (0);	/* node_id not found */
1117 }
1118 
1119 static int
1120 oprom_copytree(struct oprom_state *st, uint_t flag)
1121 {
1122 	ASSERT(st->snapshot == NULL && st->size == 0);
1123 	return (oprom_copynode(
1124 	    prom_nextnode(0), flag, &st->snapshot, &st->size));
1125 }
1126 
1127 static int
1128 oprom_snapshot(struct oprom_state *st, intptr_t arg)
1129 {
1130 	uint_t flag;
1131 
1132 	if (oprom_setstate(st, IOC_SNAP) == -1)
1133 		return (EBUSY);
1134 
1135 	/* copyin flag and create snapshot */
1136 	if ((copyin((void *)arg, &flag, sizeof (uint_t)) != 0) ||
1137 	    (oprom_copytree(st, flag) != 0)) {
1138 		(void) oprom_setstate(st, IOC_IDLE);
1139 		return (EFAULT);
1140 	}
1141 
1142 
1143 	/* copyout the size of the snapshot */
1144 	flag = (uint_t)st->size;
1145 	if (copyout(&flag, (void *)arg, sizeof (uint_t)) != 0) {
1146 		kmem_free(st->snapshot, st->size);
1147 		st->snapshot = NULL;
1148 		st->size = 0;
1149 		(void) oprom_setstate(st, IOC_IDLE);
1150 		return (EFAULT);
1151 	}
1152 
1153 	(void) oprom_setstate(st, IOC_DONE);
1154 	return (0);
1155 }
1156 
1157 static int
1158 oprom_copyout(struct oprom_state *st, intptr_t arg)
1159 {
1160 	int error = 0;
1161 	uint_t size;
1162 
1163 	if (oprom_setstate(st, IOC_COPY) == -1)
1164 		return (EBUSY);
1165 
1166 	/* copyin size and copyout snapshot */
1167 	if (copyin((void *)arg, &size, sizeof (uint_t)) != 0)
1168 		error = EFAULT;
1169 	else if (size < st->size)
1170 		error = EINVAL;
1171 	else if (copyout(st->snapshot, (void *)arg, st->size) != 0)
1172 		error = EFAULT;
1173 
1174 	if (error) {
1175 		/*
1176 		 * on error keep the snapshot until a successful
1177 		 * copyout or when the driver is closed.
1178 		 */
1179 		(void) oprom_setstate(st, IOC_DONE);
1180 		return (error);
1181 	}
1182 
1183 	kmem_free(st->snapshot, st->size);
1184 	st->snapshot = NULL;
1185 	st->size = 0;
1186 	(void) oprom_setstate(st, IOC_IDLE);
1187 	return (0);
1188 }
1189 
1190 /*
1191  * Copy all properties of nodeid into a single packed nvlist
1192  */
1193 static int
1194 oprom_copyprop(dnode_t nodeid, uint_t flag, nvlist_t *nvl)
1195 {
1196 	int proplen;
1197 	char *propname, *propval, *buf1, *buf2;
1198 
1199 	ASSERT(nvl != NULL);
1200 
1201 	/*
1202 	 * non verbose mode, get the "name" property only
1203 	 */
1204 	if (flag == 0) {
1205 		proplen = prom_getproplen(nodeid, "name");
1206 		if (proplen <= 0) {
1207 			cmn_err(CE_WARN,
1208 			    "failed to get the name of openprom node 0x%x",
1209 			    nodeid);
1210 			(void) nvlist_add_string(nvl, "name", "");
1211 			return (0);
1212 		}
1213 		propval = kmem_zalloc(proplen + 1, KM_SLEEP);
1214 		(void) prom_getprop(nodeid, "name", propval);
1215 		(void) nvlist_add_string(nvl, "name", propval);
1216 		kmem_free(propval, proplen + 1);
1217 		return (0);
1218 	}
1219 
1220 	/*
1221 	 * Ask for first property by passing a NULL string
1222 	 */
1223 	buf1 = kmem_alloc(OBP_MAXPROPNAME, KM_SLEEP);
1224 	buf2 = kmem_zalloc(OBP_MAXPROPNAME, KM_SLEEP);
1225 	buf1[0] = '\0';
1226 	while (propname = (char *)prom_nextprop(nodeid, buf1, buf2)) {
1227 		if (strlen(propname) == 0)
1228 			break;		/* end of prop list */
1229 		(void) strcpy(buf1, propname);
1230 
1231 		proplen = prom_getproplen(nodeid, propname);
1232 		if (proplen == 0) {
1233 			/* boolean property */
1234 			(void) nvlist_add_boolean(nvl, propname);
1235 			continue;
1236 		}
1237 		/* add 1 for null termination in case of a string */
1238 		propval = kmem_zalloc(proplen + 1, KM_SLEEP);
1239 		(void) prom_getprop(nodeid, propname, propval);
1240 		(void) nvlist_add_byte_array(nvl, propname,
1241 		    (uchar_t *)propval, proplen + 1);
1242 		kmem_free(propval, proplen + 1);
1243 		bzero(buf2, OBP_MAXPROPNAME);
1244 	}
1245 
1246 	kmem_free(buf1, OBP_MAXPROPNAME);
1247 	kmem_free(buf2, OBP_MAXPROPNAME);
1248 
1249 	return (0);
1250 }
1251 
1252 /*
1253  * Copy all children and descendents into a a packed nvlist
1254  */
1255 static int
1256 oprom_copychild(dnode_t nodeid, uint_t flag, char **buf, size_t *size)
1257 {
1258 	nvlist_t *nvl;
1259 	dnode_t child = prom_childnode(nodeid);
1260 
1261 	if (child == 0)
1262 		return (0);
1263 
1264 	(void) nvlist_alloc(&nvl, 0, KM_SLEEP);
1265 	while (child != 0) {
1266 		char *nodebuf = NULL;
1267 		size_t nodesize = 0;
1268 		if (oprom_copynode(child, flag, &nodebuf, &nodesize)) {
1269 			nvlist_free(nvl);
1270 			cmn_err(CE_WARN, "failed to copy nodeid 0x%x", child);
1271 			return (-1);
1272 		}
1273 		(void) nvlist_add_byte_array(nvl, "node",
1274 		    (uchar_t *)nodebuf, nodesize);
1275 		kmem_free(nodebuf, nodesize);
1276 		child = prom_nextnode(child);
1277 	}
1278 
1279 	(void) nvlist_pack(nvl, buf, size, NV_ENCODE_NATIVE, KM_SLEEP);
1280 	nvlist_free(nvl);
1281 	return (0);
1282 }
1283 
1284 /*
1285  * Copy a node into a packed nvlist
1286  */
1287 static int
1288 oprom_copynode(dnode_t nodeid, uint_t flag, char **buf, size_t *size)
1289 {
1290 	int error = 0;
1291 	nvlist_t *nvl;
1292 	char *childlist = NULL;
1293 	size_t childsize = 0;
1294 
1295 	(void) nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP);
1296 	ASSERT(nvl != NULL);
1297 
1298 	/* @nodeid -- @ is not a legal char in a 1275 property name */
1299 	(void) nvlist_add_int32(nvl, "@nodeid", (int32_t)nodeid);
1300 
1301 	/* properties */
1302 	if (error = oprom_copyprop(nodeid, flag, nvl))
1303 		goto fail;
1304 
1305 	/* children */
1306 	error = oprom_copychild(nodeid, flag, &childlist, &childsize);
1307 	if (error != 0)
1308 		goto fail;
1309 	if (childlist != NULL) {
1310 		(void) nvlist_add_byte_array(nvl, "@child",
1311 		    (uchar_t *)childlist, (uint_t)childsize);
1312 		kmem_free(childlist, childsize);
1313 	}
1314 
1315 	/* pack into contiguous buffer */
1316 	error = nvlist_pack(nvl, buf, size, NV_ENCODE_NATIVE, KM_SLEEP);
1317 
1318 fail:
1319 	nvlist_free(nvl);
1320 	return (error);
1321 }
1322 
1323 /*
1324  * The driver is stateful across OPROMSNAPSHOT and OPROMCOPYOUT.
1325  * This function encapsulates the state machine:
1326  *
1327  *	-> IOC_IDLE -> IOC_SNAP -> IOC_DONE -> IOC_COPY ->
1328  *	|		SNAPSHOT		COPYOUT	 |
1329  *	--------------------------------------------------
1330  *
1331  * Returns 0 on success and -1 on failure
1332  */
1333 static int
1334 oprom_setstate(struct oprom_state *st, int16_t new_state)
1335 {
1336 	int ret = 0;
1337 
1338 	mutex_enter(&oprom_lock);
1339 	switch (new_state) {
1340 	case IOC_IDLE:
1341 	case IOC_DONE:
1342 		break;
1343 	case IOC_SNAP:
1344 		if (st->ioc_state != IOC_IDLE)
1345 			ret = -1;
1346 		break;
1347 	case IOC_COPY:
1348 		if (st->ioc_state != IOC_DONE)
1349 			ret = -1;
1350 		break;
1351 	default:
1352 		ret = -1;
1353 	}
1354 
1355 	if (ret == 0)
1356 		st->ioc_state = new_state;
1357 	else
1358 		cmn_err(CE_NOTE, "incorrect state transition from %d to %d",
1359 		    st->ioc_state, new_state);
1360 	mutex_exit(&oprom_lock);
1361 	return (ret);
1362 }
1363