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