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