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