9070 Remove wanboot from gate
[unleashed.git] / usr / src / uts / common / io / openprom.c
blob1b2ef95f51eaa83e9080f9a5c5589abb80d437de
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * Ported from 4.1.1_PSRA: "@(#)openprom.c 1.19 91/02/19 SMI";
29 * Porting notes:
31 * OPROMU2P unsupported after SunOS 4.x.
33 * Only one of these devices per system is allowed.
37 * Openprom eeprom options/devinfo driver.
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>
62 #define MAX_OPENS 32 /* Up to this many simultaneous opens */
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 */
70 * XXX Make this dynamic.. or (better still) make the interface stateless
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];
81 static kmutex_t oprom_lock; /* serialize instance assignment */
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 *);
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);
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);
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 */
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 */
134 * Module linkage information for the kernel.
136 static struct modldrv modldrv = {
137 &mod_driverops,
138 "OPENPROM/NVRAM Driver",
139 &openeepr_ops
142 static struct modlinkage modlinkage = {
143 MODREV_1,
144 &modldrv,
145 NULL
149 _init(void)
151 int error;
153 mutex_init(&oprom_lock, NULL, MUTEX_DRIVER, NULL);
155 error = mod_install(&modlinkage);
156 if (error != 0) {
157 mutex_destroy(&oprom_lock);
158 return (error);
161 return (0);
165 _info(struct modinfo *modinfop)
167 return (mod_info(&modlinkage, modinfop));
171 _fini(void)
173 int error;
175 error = mod_remove(&modlinkage);
176 if (error != 0)
177 return (error);
179 mutex_destroy(&oprom_lock);
180 return (0);
183 static dev_info_t *opdip;
184 static pnode_t options_nodeid;
186 /*ARGSUSED*/
187 static int
188 opinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
190 int error = DDI_FAILURE;
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;
206 return (error);
209 static int
210 opattach(dev_info_t *dip, ddi_attach_cmd_t cmd)
212 switch (cmd) {
214 case DDI_ATTACH:
215 if (prom_is_openprom()) {
216 options_nodeid = prom_optionsnode();
217 } else {
218 options_nodeid = OBP_BADNODE;
221 opdip = dip;
223 if (ddi_create_minor_node(dip, "openprom", S_IFCHR,
224 0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
225 return (DDI_FAILURE);
228 return (DDI_SUCCESS);
230 default:
231 return (DDI_FAILURE);
235 static int
236 opdetach(dev_info_t *dip, ddi_detach_cmd_t cmd)
238 if (cmd != DDI_DETACH)
239 return (DDI_FAILURE);
241 ddi_remove_minor_node(dip, NULL);
242 opdip = NULL;
244 return (DDI_SUCCESS);
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.
252 /*ARGSUSED*/
253 static int
254 opromopen(dev_t *devp, int flag, int otyp, cred_t *credp)
256 int m;
257 struct oprom_state *st = oprom_state;
259 if (getminor(*devp) != 0)
260 return (ENXIO);
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;
269 * It's ours.
271 st->current_id = (pnode_t)0;
272 ASSERT(st->snapshot == NULL && st->size == 0);
273 ASSERT(st->ioc_state == IOC_IDLE);
274 break;
276 mutex_exit(&oprom_lock);
278 if (m == MAX_OPENS) {
280 * "Thank you for calling, but all our lines are
281 * busy at the moment.."
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.
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)
291 return (EAGAIN);
294 *devp = makedevice(getmajor(*devp), (minor_t)m);
296 return (0);
299 /*ARGSUSED*/
300 static int
301 opromclose(dev_t dev, int flag, int otype, cred_t *cred_p)
303 struct oprom_state *st;
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;
313 mutex_enter(&oprom_lock);
314 st->already_open = 0;
315 mutex_exit(&oprom_lock);
317 return (0);
320 #ifdef __sparc
321 static int
322 get_bootpath_prop(char *bootpath)
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);
335 if (memcmp(bootpath, BP_ISCSI_DISK,
336 strlen(BP_ISCSI_DISK)) == 0) {
337 get_iscsi_bootpath_vhci(bootpath);
340 return (0);
342 #endif
344 struct opromioctl_args {
345 struct oprom_state *st;
346 int cmd;
347 intptr_t arg;
348 int mode;
351 /*ARGSUSED*/
352 static int
353 opromioctl_cb(void *avp, int has_changed)
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];
368 st = argp->st;
369 cmd = argp->cmd;
370 arg = argp->arg;
371 mode = argp->mode;
373 if (has_changed) {
375 * The prom tree has changed since we last used current_id,
376 * so we need to check it.
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;
386 * Check permissions
387 * and weed out unsupported commands on x86 platform
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);
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);
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);
455 node_id = options_nodeid;
456 break;
458 case OPROMSETOPT:
459 case OPROMSETOPT2:
460 #if !defined(__i386) && !defined(__amd64)
461 if (mode & FWRITE) {
462 node_id = options_nodeid;
463 break;
465 #endif /* !__i386 && !__amd64 */
466 return (EPERM);
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);
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);
498 break;
500 default:
501 return (EINVAL);
505 * Deal with SNAPSHOT and COPYOUT ioctls first
507 switch (cmd) {
508 case OPROMCOPYOUT:
509 return (oprom_copyout(st, arg));
511 case OPROMSNAPSHOT:
512 return (oprom_snapshot(st, arg));
516 * Copy in user argument length and allocation memory
518 * NB do not copyin the entire buffer we may not need
519 * to. userbufsize can be as big as 32 K.
521 if (copyin((void *)arg, &userbufsize, sizeof (uint_t)) != 0)
522 return (EFAULT);
524 if (userbufsize == 0 || userbufsize > OPROMMAXPARAM)
525 return (EINVAL);
527 opp = (struct openpromio *)kmem_zalloc(
528 userbufsize + sizeof (uint_t) + 1, KM_SLEEP);
531 * Execute command
533 switch (cmd) {
535 case OPROMGETOPT:
536 case OPROMGETPROP:
537 case OPROMGETPROPLEN:
539 if ((prom_is_openprom() == 0) ||
540 (node_id == OBP_NONODE) || (node_id == OBP_BADNODE)) {
541 error = EINVAL;
542 break;
546 * The argument, a NULL terminated string, is a prop name.
548 if ((error = oprom_copyinstr(arg, opp->oprom_array,
549 (size_t)userbufsize, OBP_MAXPROPNAME)) != 0) {
550 break;
552 (void) strcpy(propname, opp->oprom_array);
553 valsize = prom_getproplen(node_id, propname);
556 * 4010173: 'name' is a property, but not an option.
558 if ((cmd == OPROMGETOPT) && (strcmp("name", propname) == 0))
559 valsize = -1;
561 if (cmd == OPROMGETPROPLEN) {
562 int proplen = valsize;
564 if (userbufsize < sizeof (int)) {
565 error = EINVAL;
566 break;
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 {
580 * XXX: There is no error code if the buf is too small.
581 * which is consistent with the current behavior.
583 * NB: This clause also handles the non-error
584 * zero length (boolean) property value case.
586 opp->oprom_size = 0;
587 (void) strcpy(opp->oprom_array, "");
588 valsize = 1;
590 if (copyout(opp, (void *)arg, (valsize + sizeof (uint_t))) != 0)
591 error = EFAULT;
592 break;
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;
603 * The argument, a NULL terminated string, is a prop name.
605 if ((error = oprom_copyinstr(arg, opp->oprom_array,
606 (size_t)userbufsize, OBP_MAXPROPNAME)) != 0) {
607 break;
609 valbuf = (char *)prom_nextprop(node_id, opp->oprom_array,
610 propname);
611 valsize = strlen(valbuf);
614 * 4010173: 'name' is a property, but it's not an option.
616 if ((cmd == OPROMNXTOPT) && valsize &&
617 (strcmp(valbuf, "name") == 0)) {
618 valbuf = (char *)prom_nextprop(node_id, "name",
619 propname);
620 valsize = strlen(valbuf);
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);
632 if (copyout(opp, (void *)arg, valsize + sizeof (uint_t)) != 0)
633 error = EFAULT;
634 break;
636 case OPROMNEXT:
637 case OPROMCHILD:
638 case OPROMSETNODEID:
640 if (prom_is_openprom() == 0 ||
641 userbufsize < sizeof (pnode_t)) {
642 error = EINVAL;
643 break;
647 * The argument is a phandle. (aka pnode_t)
649 if (copyin(((caddr_t)arg + sizeof (uint_t)),
650 opp->oprom_array, sizeof (pnode_t)) != 0) {
651 error = EFAULT;
652 break;
656 * If pnode_t from userland is garbage, we
657 * could confuse the PROM.
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;
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;
677 opp->oprom_size = sizeof (pnode_t);
678 *(pnode_t *)opp->oprom_array = st->current_id;
680 if (copyout(opp, (void *)arg,
681 sizeof (pnode_t) + sizeof (uint_t)) != 0)
682 error = EFAULT;
683 break;
685 case OPROMGETCONS:
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.
692 opp->oprom_size = sizeof (char);
694 opp->oprom_array[0] |= prom_is_openprom() ?
695 OPROMCONS_OPENPROM : 0;
698 * The rest of the info is needed by Install to
699 * decide if graphics should be started.
701 if ((getzoneid() == GLOBAL_ZONEID) &&
702 plat_stdin_is_keyboard()) {
703 opp->oprom_array[0] |= OPROMCONS_STDIN_IS_KBD;
706 if ((getzoneid() == GLOBAL_ZONEID) &&
707 plat_stdout_is_framebuffer()) {
708 opp->oprom_array[0] |= OPROMCONS_STDOUT_IS_FB;
711 if (copyout(opp, (void *)arg,
712 sizeof (char) + sizeof (uint_t)) != 0)
713 error = EFAULT;
714 break;
716 case OPROMGETBOOTARGS: {
717 extern char kern_bootargs[];
719 valsize = strlen(kern_bootargs) + 1;
720 if (valsize > userbufsize) {
721 error = EINVAL;
722 break;
724 (void) strcpy(opp->oprom_array, kern_bootargs);
725 opp->oprom_size = valsize - 1;
727 if (copyout(opp, (void *)arg, valsize + sizeof (uint_t)) != 0)
728 error = EFAULT;
729 break;
732 case OPROMGETBOOTPATH: {
733 #if defined(__sparc) && defined(_OBP)
735 char bpath[OBP_MAXPATHLEN];
736 if (get_bootpath_prop(bpath) != 0) {
737 error = EINVAL;
738 break;
740 valsize = strlen(bpath) + 1;
741 if (valsize > userbufsize) {
742 error = EINVAL;
743 break;
745 (void) strcpy(opp->oprom_array, bpath);
747 #elif defined(__i386) || defined(__amd64)
749 extern char saved_cmdline[];
750 valsize = strlen(saved_cmdline) + 1;
751 if (valsize > userbufsize) {
752 error = EINVAL;
753 break;
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;
764 * convert a prom device path to an equivalent devfs path
766 case OPROMPROM2DEVNAME: {
767 char *dev_name;
770 * The input argument, a pathname, is a NULL terminated string.
772 if ((error = oprom_copyinstr(arg, opp->oprom_array,
773 (size_t)userbufsize, MAXPATHLEN)) != 0) {
774 break;
777 dev_name = kmem_alloc(MAXPATHLEN, KM_SLEEP);
779 error = i_promname_to_devname(opp->oprom_array, dev_name);
780 if (error != 0) {
781 kmem_free(dev_name, MAXPATHLEN);
782 break;
784 valsize = opp->oprom_size = strlen(dev_name);
785 if (++valsize > userbufsize) {
786 kmem_free(dev_name, MAXPATHLEN);
787 error = EINVAL;
788 break;
790 (void) strcpy(opp->oprom_array, dev_name);
791 if (copyout(opp, (void *)arg, sizeof (uint_t) + valsize) != 0)
792 error = EFAULT;
794 kmem_free(dev_name, MAXPATHLEN);
795 break;
799 * Convert a prom device path name to a driver name
801 case OPROMPATH2DRV: {
802 char *drv_name;
803 major_t maj;
806 * The input argument, a pathname, is a NULL terminated string.
808 if ((error = oprom_copyinstr(arg, opp->oprom_array,
809 (size_t)userbufsize, MAXPATHLEN)) != 0) {
810 break;
814 * convert path to a driver binding name
816 maj = path_to_major((char *)opp->oprom_array);
817 if (maj == DDI_MAJOR_T_NONE) {
818 error = EINVAL;
819 break;
823 * resolve any aliases
825 if ((drv_name = ddi_major_to_name(maj)) == NULL) {
826 error = EINVAL;
827 break;
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;
838 case OPROMGETVERSION:
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).
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.
852 valsize = prom_version_name(opp->oprom_array, userbufsize);
853 if (valsize < 0) {
854 error = EINVAL;
855 break;
859 * copyout only the part of the user buffer we need to.
861 if (copyout(opp, (void *)arg,
862 (size_t)(min((uint_t)valsize, userbufsize) +
863 sizeof (uint_t))) != 0)
864 error = EFAULT;
865 break;
867 #if !defined(__i386) && !defined(__amd64)
868 case OPROMGETFBNAME:
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?
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;
888 * Convert a logical or physical device path to prom device path
890 case OPROMDEV2PROMNAME: {
891 char *prom_name;
894 * The input argument, a pathname, is a NULL terminated string.
896 if ((error = oprom_copyinstr(arg, opp->oprom_array,
897 (size_t)userbufsize, MAXPATHLEN)) != 0) {
898 break;
901 prom_name = kmem_alloc(userbufsize, KM_SLEEP);
904 * convert the devfs path to an equivalent prom path
906 error = i_devname_to_promname(opp->oprom_array, prom_name,
907 userbufsize);
909 if (error != 0) {
910 kmem_free(prom_name, userbufsize);
911 break;
914 for (valsize = 0; valsize < userbufsize; valsize++) {
915 opp->oprom_array[valsize] = prom_name[valsize];
917 if ((valsize > 0) && (prom_name[valsize] == '\0') &&
918 (prom_name[valsize-1] == '\0')) {
919 break;
922 opp->oprom_size = valsize;
924 kmem_free(prom_name, userbufsize);
925 if (copyout(opp, (void *)arg, sizeof (uint_t) + valsize) != 0)
926 error = EFAULT;
928 break;
931 case OPROMSETOPT:
932 case OPROMSETOPT2: {
933 int namebuflen;
934 int valbuflen;
936 if ((prom_is_openprom() == 0) ||
937 (node_id == OBP_NONODE) || (node_id == OBP_BADNODE)) {
938 error = EINVAL;
939 break;
943 * The arguments are a property name and a value.
944 * Copy in the entire user buffer.
946 if (copyin(((caddr_t)arg + sizeof (uint_t)),
947 opp->oprom_array, userbufsize) != 0) {
948 error = EFAULT;
949 break;
953 * The property name is the first string, value second
955 namebuflen = strlen(opp->oprom_array);
956 valbuf = opp->oprom_array + namebuflen + 1;
957 valbuflen = strlen(valbuf);
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;
966 valsize = (opp->oprom_array + userbufsize) - valbuf;
970 * 4010173: 'name' is not an option, but it is a property.
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;
978 break;
981 case OPROMREADY64: {
982 struct openprom_opr64 *opr =
983 (struct openprom_opr64 *)opp->oprom_array;
984 int i;
985 pnode_t id;
987 if (userbufsize < sizeof (*opr)) {
988 error = EINVAL;
989 break;
992 valsize = userbufsize -
993 offsetof(struct openprom_opr64, message);
995 i = prom_version_check(opr->message, valsize, &id);
996 opr->return_code = i;
997 opr->nodeid = (int)id;
999 valsize = offsetof(struct openprom_opr64, message);
1000 valsize += strlen(opr->message) + 1;
1003 * copyout only the part of the user buffer we need to.
1005 if (copyout(opp, (void *)arg,
1006 (size_t)(min((uint_t)valsize, userbufsize) +
1007 sizeof (uint_t))) != 0)
1008 error = EFAULT;
1009 break;
1011 } /* case OPROMREADY64 */
1012 #endif /* !__i386 && !__amd64 */
1013 } /* switch (cmd) */
1015 kmem_free(opp, userbufsize + sizeof (uint_t) + 1);
1016 return (error);
1019 /*ARGSUSED*/
1020 static int
1021 opromioctl(dev_t dev, int cmd, intptr_t arg, int mode,
1022 cred_t *credp, int *rvalp)
1024 struct oprom_state *st;
1025 struct opromioctl_args arg_block;
1027 if (getminor(dev) >= MAX_OPENS)
1028 return (ENXIO);
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));
1040 * Copyin string and verify the actual string length is less than maxsize
1041 * specified by the caller.
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.
1047 static int
1048 oprom_copyinstr(intptr_t arg, char *buf, size_t bufsize, size_t maxsize)
1050 int error;
1051 size_t actual_len;
1053 if ((error = copyinstr(((caddr_t)arg + sizeof (uint_t)),
1054 buf, bufsize, &actual_len)) != 0) {
1055 return (error);
1057 if ((actual_len == 0) || (actual_len > maxsize)) {
1058 return (EINVAL);
1061 return (0);
1065 * Check pnode_t passed in from userland
1067 static int
1068 oprom_checknodeid(pnode_t node_id, pnode_t current_id)
1070 int depth;
1071 pnode_t id[OBP_STACKDEPTH];
1074 * optimized path
1076 if (node_id == 0) {
1077 return (1);
1079 if (node_id == OBP_BADNODE) {
1080 return (0);
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);
1089 * long path: walk from root till we find node_id
1091 depth = 1;
1092 id[0] = prom_nextnode((pnode_t)0);
1094 while (depth) {
1095 if (id[depth - 1] == node_id)
1096 return (1); /* node_id found */
1098 if (id[depth] = prom_childnode(id[depth - 1])) {
1099 depth++;
1100 continue;
1103 while (depth &&
1104 ((id[depth - 1] = prom_nextnode(id[depth - 1])) == 0))
1105 depth--;
1107 return (0); /* node_id not found */
1110 static int
1111 oprom_copytree(struct oprom_state *st, uint_t flag)
1113 ASSERT(st->snapshot == NULL && st->size == 0);
1114 return (oprom_copynode(
1115 prom_nextnode(0), flag, &st->snapshot, &st->size));
1118 static int
1119 oprom_snapshot(struct oprom_state *st, intptr_t arg)
1121 uint_t flag;
1123 if (oprom_setstate(st, IOC_SNAP) == -1)
1124 return (EBUSY);
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);
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);
1144 (void) oprom_setstate(st, IOC_DONE);
1145 return (0);
1148 static int
1149 oprom_copyout(struct oprom_state *st, intptr_t arg)
1151 int error = 0;
1152 uint_t size;
1154 if (oprom_setstate(st, IOC_COPY) == -1)
1155 return (EBUSY);
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;
1165 if (error) {
1167 * on error keep the snapshot until a successful
1168 * copyout or when the driver is closed.
1170 (void) oprom_setstate(st, IOC_DONE);
1171 return (error);
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);
1182 * Copy all properties of nodeid into a single packed nvlist
1184 static int
1185 oprom_copyprop(pnode_t nodeid, uint_t flag, nvlist_t *nvl)
1187 int proplen;
1188 char *propname, *propval, *buf1, *buf2;
1190 ASSERT(nvl != NULL);
1193 * non verbose mode, get the "name" property only
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);
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);
1212 * Ask for first property by passing a NULL string
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);
1222 proplen = prom_getproplen(nodeid, propname);
1223 if (proplen == 0) {
1224 /* boolean property */
1225 (void) nvlist_add_boolean(nvl, propname);
1226 continue;
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);
1237 kmem_free(buf1, OBP_MAXPROPNAME);
1238 kmem_free(buf2, OBP_MAXPROPNAME);
1240 return (0);
1244 * Copy all children and descendents into a a packed nvlist
1246 static int
1247 oprom_copychild(pnode_t nodeid, uint_t flag, char **buf, size_t *size)
1249 nvlist_t *nvl;
1250 pnode_t child = prom_childnode(nodeid);
1252 if (child == 0)
1253 return (0);
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);
1264 (void) nvlist_add_byte_array(nvl, "node",
1265 (uchar_t *)nodebuf, nodesize);
1266 kmem_free(nodebuf, nodesize);
1267 child = prom_nextnode(child);
1270 (void) nvlist_pack(nvl, buf, size, NV_ENCODE_NATIVE, KM_SLEEP);
1271 nvlist_free(nvl);
1272 return (0);
1276 * Copy a node into a packed nvlist
1278 static int
1279 oprom_copynode(pnode_t nodeid, uint_t flag, char **buf, size_t *size)
1281 int error = 0;
1282 nvlist_t *nvl;
1283 char *childlist = NULL;
1284 size_t childsize = 0;
1286 (void) nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP);
1287 ASSERT(nvl != NULL);
1289 /* @nodeid -- @ is not a legal char in a 1275 property name */
1290 (void) nvlist_add_int32(nvl, "@nodeid", (int32_t)nodeid);
1292 /* properties */
1293 if (error = oprom_copyprop(nodeid, flag, nvl))
1294 goto fail;
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);
1306 /* pack into contiguous buffer */
1307 error = nvlist_pack(nvl, buf, size, NV_ENCODE_NATIVE, KM_SLEEP);
1309 fail:
1310 nvlist_free(nvl);
1311 return (error);
1315 * The driver is stateful across OPROMSNAPSHOT and OPROMCOPYOUT.
1316 * This function encapsulates the state machine:
1318 * -> IOC_IDLE -> IOC_SNAP -> IOC_DONE -> IOC_COPY ->
1319 * | SNAPSHOT COPYOUT |
1320 * --------------------------------------------------
1322 * Returns 0 on success and -1 on failure
1324 static int
1325 oprom_setstate(struct oprom_state *st, int16_t new_state)
1327 int ret = 0;
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;
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);