bmake-ify mega_sas
[unleashed.git] / usr / src / uts / common / io / openprom.c
blobee36de4aeca782c381b67f67522691f2b4b7f23b
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.
25 * Copyright 2014 Garrett D'Amore <garrett@damore.org>
29 * Ported from 4.1.1_PSRA: "@(#)openprom.c 1.19 91/02/19 SMI";
31 * Porting notes:
33 * OPROMU2P unsupported after SunOS 4.x.
35 * Only one of these devices per system is allowed.
39 * Openprom eeprom options/devinfo driver.
42 #include <sys/types.h>
43 #include <sys/errno.h>
44 #include <sys/file.h>
45 #include <sys/cmn_err.h>
46 #include <sys/kmem.h>
47 #include <sys/openpromio.h>
48 #include <sys/conf.h>
49 #include <sys/stat.h>
50 #include <sys/modctl.h>
51 #include <sys/debug.h>
52 #include <sys/autoconf.h>
53 #include <sys/ddi.h>
54 #include <sys/sunddi.h>
55 #include <sys/promif.h>
56 #include <sys/sysmacros.h> /* offsetof */
57 #include <sys/nvpair.h>
58 #include <sys/zone.h>
59 #include <sys/consplat.h>
60 #include <sys/bootconf.h>
61 #include <sys/systm.h>
62 #include <sys/bootprops.h>
64 #define MAX_OPENS 32 /* Up to this many simultaneous opens */
66 #define IOC_IDLE 0 /* snapshot ioctl states */
67 #define IOC_SNAP 1 /* snapshot in progress */
68 #define IOC_DONE 2 /* snapshot done, but not copied out */
69 #define IOC_COPY 3 /* copyout in progress */
72 * XXX Make this dynamic.. or (better still) make the interface stateless
74 static struct oprom_state {
75 pnode_t current_id; /* node we're fetching props from */
76 int16_t already_open; /* if true, this instance is 'active' */
77 int16_t ioc_state; /* snapshot ioctl state */
78 char *snapshot; /* snapshot of all prom nodes */
79 size_t size; /* size of snapshot */
80 prom_generation_cookie_t tree_gen;
81 } oprom_state[MAX_OPENS];
83 static kmutex_t oprom_lock; /* serialize instance assignment */
85 static int opromopen(dev_t *, int, int, cred_t *);
86 static int opromioctl(dev_t, int, intptr_t, int, cred_t *, int *);
87 static int opromclose(dev_t, int, int, cred_t *);
89 static int opinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
90 void **result);
91 static int opattach(dev_info_t *, ddi_attach_cmd_t cmd);
92 static int opdetach(dev_info_t *, ddi_detach_cmd_t cmd);
94 /* help functions */
95 static int oprom_checknodeid(pnode_t, pnode_t);
96 static int oprom_copyinstr(intptr_t, char *, size_t, size_t);
97 static int oprom_copynode(pnode_t, uint_t, char **, size_t *);
98 static int oprom_snapshot(struct oprom_state *, intptr_t);
99 static int oprom_copyout(struct oprom_state *, intptr_t);
100 static int oprom_setstate(struct oprom_state *, int16_t);
102 static struct cb_ops openeepr_cb_ops = {
103 opromopen, /* open */
104 opromclose, /* close */
105 nodev, /* strategy */
106 nodev, /* print */
107 nodev, /* dump */
108 nodev, /* read */
109 nodev, /* write */
110 opromioctl, /* ioctl */
111 nodev, /* devmap */
112 nodev, /* mmap */
113 nodev, /* segmap */
114 nochpoll, /* poll */
115 ddi_prop_op, /* prop_op */
116 NULL, /* streamtab */
117 D_NEW | D_MP /* Driver compatibility flag */
120 static struct dev_ops openeepr_ops = {
121 DEVO_REV, /* devo_rev, */
122 0, /* refcnt */
123 opinfo, /* info */
124 nulldev, /* identify */
125 nulldev, /* probe */
126 opattach, /* attach */
127 opdetach, /* detach */
128 nodev, /* reset */
129 &openeepr_cb_ops, /* driver operations */
130 NULL, /* bus operations */
131 NULL, /* power */
132 ddi_quiesce_not_needed, /* quiesce */
136 * Module linkage information for the kernel.
138 static struct modldrv modldrv = {
139 &mod_driverops,
140 "OPENPROM/NVRAM Driver",
141 &openeepr_ops
144 static struct modlinkage modlinkage = {
145 MODREV_1,
146 &modldrv,
147 NULL
151 _init(void)
153 int error;
155 mutex_init(&oprom_lock, NULL, MUTEX_DRIVER, NULL);
157 error = mod_install(&modlinkage);
158 if (error != 0) {
159 mutex_destroy(&oprom_lock);
160 return (error);
163 return (0);
167 _info(struct modinfo *modinfop)
169 return (mod_info(&modlinkage, modinfop));
173 _fini(void)
175 int error;
177 error = mod_remove(&modlinkage);
178 if (error != 0)
179 return (error);
181 mutex_destroy(&oprom_lock);
182 return (0);
185 static dev_info_t *opdip;
186 static pnode_t options_nodeid;
188 /*ARGSUSED*/
189 static int
190 opinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
192 int error = DDI_FAILURE;
194 switch (infocmd) {
195 case DDI_INFO_DEVT2DEVINFO:
196 *result = (void *)opdip;
197 error = DDI_SUCCESS;
198 break;
199 case DDI_INFO_DEVT2INSTANCE:
200 /* All dev_t's map to the same, single instance */
201 *result = NULL;
202 error = DDI_SUCCESS;
203 break;
204 default:
205 break;
208 return (error);
211 static int
212 opattach(dev_info_t *dip, ddi_attach_cmd_t cmd)
214 switch (cmd) {
216 case DDI_ATTACH:
217 if (prom_is_openprom()) {
218 options_nodeid = prom_optionsnode();
219 } else {
220 options_nodeid = OBP_BADNODE;
223 opdip = dip;
225 if (ddi_create_minor_node(dip, "openprom", S_IFCHR,
226 0, DDI_PSEUDO, 0) == DDI_FAILURE) {
227 return (DDI_FAILURE);
230 return (DDI_SUCCESS);
232 default:
233 return (DDI_FAILURE);
237 static int
238 opdetach(dev_info_t *dip, ddi_detach_cmd_t cmd)
240 if (cmd != DDI_DETACH)
241 return (DDI_FAILURE);
243 ddi_remove_minor_node(dip, NULL);
244 opdip = NULL;
246 return (DDI_SUCCESS);
250 * Allow multiple opens by tweaking the dev_t such that it looks like each
251 * open is getting a different minor device. Each minor gets a separate
252 * entry in the oprom_state[] table.
254 /*ARGSUSED*/
255 static int
256 opromopen(dev_t *devp, int flag, int otyp, cred_t *credp)
258 int m;
259 struct oprom_state *st = oprom_state;
261 if (getminor(*devp) != 0)
262 return (ENXIO);
264 mutex_enter(&oprom_lock);
265 for (m = 0; m < MAX_OPENS; m++)
266 if (st->already_open)
267 st++;
268 else {
269 st->already_open = 1;
271 * It's ours.
273 st->current_id = (pnode_t)0;
274 ASSERT(st->snapshot == NULL && st->size == 0);
275 ASSERT(st->ioc_state == IOC_IDLE);
276 break;
278 mutex_exit(&oprom_lock);
280 if (m == MAX_OPENS) {
282 * "Thank you for calling, but all our lines are
283 * busy at the moment.."
285 * We could get sophisticated here, and go into a
286 * sleep-retry loop .. but hey, I just can't see
287 * that many processes sitting in this driver.
289 * (And if it does become possible, then we should
290 * change the interface so that the 'state' is held
291 * external to the driver)
293 return (EAGAIN);
296 *devp = makedevice(getmajor(*devp), (minor_t)m);
298 return (0);
301 /*ARGSUSED*/
302 static int
303 opromclose(dev_t dev, int flag, int otype, cred_t *cred_p)
305 struct oprom_state *st;
307 st = &oprom_state[getminor(dev)];
308 ASSERT(getminor(dev) < MAX_OPENS && st->already_open != 0);
309 if (st->snapshot) {
310 kmem_free(st->snapshot, st->size);
311 st->snapshot = NULL;
312 st->size = 0;
313 st->ioc_state = IOC_IDLE;
315 mutex_enter(&oprom_lock);
316 st->already_open = 0;
317 mutex_exit(&oprom_lock);
319 return (0);
323 struct opromioctl_args {
324 struct oprom_state *st;
325 int cmd;
326 intptr_t arg;
327 int mode;
330 /*ARGSUSED*/
331 static int
332 opromioctl_cb(void *avp, int has_changed)
334 struct opromioctl_args *argp = avp;
335 int cmd;
336 intptr_t arg;
337 int mode;
338 struct oprom_state *st;
339 struct openpromio *opp;
340 int valsize;
341 char *valbuf;
342 int error = 0;
343 uint_t userbufsize;
344 pnode_t node_id;
345 char propname[OBP_MAXPROPNAME];
347 st = argp->st;
348 cmd = argp->cmd;
349 arg = argp->arg;
350 mode = argp->mode;
352 if (has_changed) {
354 * The prom tree has changed since we last used current_id,
355 * so we need to check it.
357 if ((st->current_id != OBP_NONODE) &&
358 (st->current_id != OBP_BADNODE)) {
359 if (oprom_checknodeid(st->current_id, OBP_NONODE) == 0)
360 st->current_id = OBP_BADNODE;
365 * Check permissions
366 * and weed out unsupported commands on x86 platform
368 switch (cmd) {
369 #if !defined(__i386) && !defined(__amd64)
370 case OPROMLISTKEYSLEN:
371 valsize = prom_asr_list_keys_len();
372 opp = kmem_zalloc(sizeof (uint_t) + 1, KM_SLEEP);
373 opp->oprom_size = valsize;
374 if (copyout(opp, (void *)arg, (sizeof (uint_t))) != 0)
375 error = EFAULT;
376 kmem_free(opp, sizeof (uint_t) + 1);
377 break;
378 case OPROMLISTKEYS:
379 valsize = prom_asr_list_keys_len();
380 if (copyin((void *)arg, &userbufsize, sizeof (uint_t)) != 0)
381 return (EFAULT);
382 if (valsize > userbufsize)
383 return (EINVAL);
384 valbuf = kmem_zalloc(valsize + 1, KM_SLEEP);
385 if (prom_asr_list_keys((caddr_t)valbuf) == -1) {
386 kmem_free(valbuf, valsize + 1);
387 return (EFAULT);
389 opp = kmem_zalloc(valsize + sizeof (uint_t) + 1, KM_SLEEP);
390 opp->oprom_size = valsize;
391 bcopy(valbuf, opp->oprom_array, valsize);
392 if (copyout(opp, (void *)arg, (valsize + sizeof (uint_t))) != 0)
393 error = EFAULT;
394 kmem_free(valbuf, valsize + 1);
395 kmem_free(opp, valsize + sizeof (uint_t) + 1);
396 break;
397 case OPROMEXPORT:
398 valsize = prom_asr_export_len();
399 if (copyin((void *)arg, &userbufsize, sizeof (uint_t)) != 0)
400 return (EFAULT);
401 if (valsize > userbufsize)
402 return (EINVAL);
403 valbuf = kmem_zalloc(valsize + 1, KM_SLEEP);
404 if (prom_asr_export((caddr_t)valbuf) == -1) {
405 kmem_free(valbuf, valsize + 1);
406 return (EFAULT);
408 opp = kmem_zalloc(valsize + sizeof (uint_t) + 1, KM_SLEEP);
409 opp->oprom_size = valsize;
410 bcopy(valbuf, opp->oprom_array, valsize);
411 if (copyout(opp, (void *)arg, (valsize + sizeof (uint_t))) != 0)
412 error = EFAULT;
413 kmem_free(valbuf, valsize + 1);
414 kmem_free(opp, valsize + sizeof (uint_t) + 1);
415 break;
416 case OPROMEXPORTLEN:
417 valsize = prom_asr_export_len();
418 opp = kmem_zalloc(sizeof (uint_t) + 1, KM_SLEEP);
419 opp->oprom_size = valsize;
420 if (copyout(opp, (void *)arg, (sizeof (uint_t))) != 0)
421 error = EFAULT;
422 kmem_free(opp, sizeof (uint_t) + 1);
423 break;
424 #endif
425 case OPROMGETOPT:
426 case OPROMNXTOPT:
427 if ((mode & FREAD) == 0) {
428 return (EPERM);
430 node_id = options_nodeid;
431 break;
433 case OPROMSETOPT:
434 case OPROMSETOPT2:
435 #if !defined(__i386) && !defined(__amd64)
436 if (mode & FWRITE) {
437 node_id = options_nodeid;
438 break;
440 #endif /* !__i386 && !__amd64 */
441 return (EPERM);
443 case OPROMNEXT:
444 case OPROMCHILD:
445 case OPROMGETPROP:
446 case OPROMGETPROPLEN:
447 case OPROMNXTPROP:
448 case OPROMSETNODEID:
449 if ((mode & FREAD) == 0) {
450 return (EPERM);
452 node_id = st->current_id;
453 break;
454 case OPROMCOPYOUT:
455 if (st->snapshot == NULL)
456 return (EINVAL);
457 /*FALLTHROUGH*/
458 case OPROMSNAPSHOT:
459 case OPROMGETCONS:
460 case OPROMGETBOOTARGS:
461 case OPROMGETBOOTPATH:
462 case OPROMGETVERSION:
463 case OPROMPATH2DRV:
464 case OPROMPROM2DEVNAME:
465 #if !defined(__i386) && !defined(__amd64)
466 case OPROMGETFBNAME:
467 case OPROMDEV2PROMNAME:
468 case OPROMREADY64:
469 #endif /* !__i386 && !__amd64 */
470 if ((mode & FREAD) == 0) {
471 return (EPERM);
473 break;
475 default:
476 return (EINVAL);
480 * Deal with SNAPSHOT and COPYOUT ioctls first
482 switch (cmd) {
483 case OPROMCOPYOUT:
484 return (oprom_copyout(st, arg));
486 case OPROMSNAPSHOT:
487 return (oprom_snapshot(st, arg));
491 * Copy in user argument length and allocation memory
493 * NB do not copyin the entire buffer we may not need
494 * to. userbufsize can be as big as 32 K.
496 if (copyin((void *)arg, &userbufsize, sizeof (uint_t)) != 0)
497 return (EFAULT);
499 if (userbufsize == 0 || userbufsize > OPROMMAXPARAM)
500 return (EINVAL);
502 opp = kmem_zalloc(userbufsize + sizeof (uint_t) + 1, KM_SLEEP);
505 * Execute command
507 switch (cmd) {
509 case OPROMGETOPT:
510 case OPROMGETPROP:
511 case OPROMGETPROPLEN:
513 if ((prom_is_openprom() == 0) ||
514 (node_id == OBP_NONODE) || (node_id == OBP_BADNODE)) {
515 error = EINVAL;
516 break;
520 * The argument, a NULL terminated string, is a prop name.
522 if ((error = oprom_copyinstr(arg, opp->oprom_array,
523 (size_t)userbufsize, OBP_MAXPROPNAME)) != 0) {
524 break;
526 (void) strcpy(propname, opp->oprom_array);
527 valsize = prom_getproplen(node_id, propname);
530 * 4010173: 'name' is a property, but not an option.
532 if ((cmd == OPROMGETOPT) && (strcmp("name", propname) == 0))
533 valsize = -1;
535 if (cmd == OPROMGETPROPLEN) {
536 int proplen = valsize;
538 if (userbufsize < sizeof (int)) {
539 error = EINVAL;
540 break;
542 opp->oprom_size = valsize = sizeof (int);
543 bcopy(&proplen, opp->oprom_array, valsize);
544 } else if (valsize > 0 && valsize <= userbufsize) {
545 bzero(opp->oprom_array, valsize + 1);
546 (void) prom_getprop(node_id, propname,
547 opp->oprom_array);
548 opp->oprom_size = valsize;
549 if (valsize < userbufsize)
550 ++valsize; /* Forces NULL termination */
551 /* If space permits */
552 } else {
554 * XXX: There is no error code if the buf is too small.
555 * which is consistent with the current behavior.
557 * NB: This clause also handles the non-error
558 * zero length (boolean) property value case.
560 opp->oprom_size = 0;
561 (void) strcpy(opp->oprom_array, "");
562 valsize = 1;
564 if (copyout(opp, (void *)arg, (valsize + sizeof (uint_t))) != 0)
565 error = EFAULT;
566 break;
568 case OPROMNXTOPT:
569 case OPROMNXTPROP:
570 if ((prom_is_openprom() == 0) ||
571 (node_id == OBP_NONODE) || (node_id == OBP_BADNODE)) {
572 error = EINVAL;
573 break;
577 * The argument, a NULL terminated string, is a prop name.
579 if ((error = oprom_copyinstr(arg, opp->oprom_array,
580 (size_t)userbufsize, OBP_MAXPROPNAME)) != 0) {
581 break;
583 valbuf = (char *)prom_nextprop(node_id, opp->oprom_array,
584 propname);
585 valsize = strlen(valbuf);
588 * 4010173: 'name' is a property, but it's not an option.
590 if ((cmd == OPROMNXTOPT) && valsize &&
591 (strcmp(valbuf, "name") == 0)) {
592 valbuf = (char *)prom_nextprop(node_id, "name",
593 propname);
594 valsize = strlen(valbuf);
597 if (valsize == 0) {
598 opp->oprom_size = 0;
599 } else if (++valsize <= userbufsize) {
600 opp->oprom_size = valsize;
601 bzero((caddr_t)opp->oprom_array, (size_t)valsize);
602 bcopy((caddr_t)valbuf, (caddr_t)opp->oprom_array,
603 (size_t)valsize);
606 if (copyout(opp, (void *)arg, valsize + sizeof (uint_t)) != 0)
607 error = EFAULT;
608 break;
610 case OPROMNEXT:
611 case OPROMCHILD:
612 case OPROMSETNODEID:
614 if (prom_is_openprom() == 0 ||
615 userbufsize < sizeof (pnode_t)) {
616 error = EINVAL;
617 break;
621 * The argument is a phandle. (aka pnode_t)
623 if (copyin(((caddr_t)arg + sizeof (uint_t)),
624 opp->oprom_array, sizeof (pnode_t)) != 0) {
625 error = EFAULT;
626 break;
630 * If pnode_t from userland is garbage, we
631 * could confuse the PROM.
633 node_id = *(pnode_t *)opp->oprom_array;
634 if (oprom_checknodeid(node_id, st->current_id) == 0) {
635 cmn_err(CE_NOTE, "!nodeid 0x%x not found",
636 (int)node_id);
637 error = EINVAL;
638 break;
641 if (cmd == OPROMNEXT)
642 st->current_id = prom_nextnode(node_id);
643 else if (cmd == OPROMCHILD)
644 st->current_id = prom_childnode(node_id);
645 else {
646 /* OPROMSETNODEID */
647 st->current_id = node_id;
648 break;
651 opp->oprom_size = sizeof (pnode_t);
652 *(pnode_t *)opp->oprom_array = st->current_id;
654 if (copyout(opp, (void *)arg,
655 sizeof (pnode_t) + sizeof (uint_t)) != 0)
656 error = EFAULT;
657 break;
659 case OPROMGETCONS:
661 * Is openboot supported on this machine?
662 * This ioctl used to return the console device,
663 * information; this is now done via modctl()
664 * in libdevinfo.
666 opp->oprom_size = sizeof (char);
668 opp->oprom_array[0] |= prom_is_openprom() ?
669 OPROMCONS_OPENPROM : 0;
672 * The rest of the info is needed by Install to
673 * decide if graphics should be started.
675 if ((getzoneid() == GLOBAL_ZONEID) &&
676 plat_stdin_is_keyboard()) {
677 opp->oprom_array[0] |= OPROMCONS_STDIN_IS_KBD;
680 if ((getzoneid() == GLOBAL_ZONEID) &&
681 plat_stdout_is_framebuffer()) {
682 opp->oprom_array[0] |= OPROMCONS_STDOUT_IS_FB;
685 if (copyout(opp, (void *)arg,
686 sizeof (char) + sizeof (uint_t)) != 0)
687 error = EFAULT;
688 break;
690 case OPROMGETBOOTARGS: {
691 extern char kern_bootargs[];
693 valsize = strlen(kern_bootargs) + 1;
694 if (valsize > userbufsize) {
695 error = EINVAL;
696 break;
698 (void) strcpy(opp->oprom_array, kern_bootargs);
699 opp->oprom_size = valsize - 1;
701 if (copyout(opp, (void *)arg, valsize + sizeof (uint_t)) != 0)
702 error = EFAULT;
703 break;
706 case OPROMGETBOOTPATH: {
707 #if defined(__sparc) && defined(_OBP)
709 char bpath[OBP_MAXPATHLEN];
710 if (get_bootpath_prop(bpath) != 0) {
711 error = EINVAL;
712 break;
714 valsize = strlen(bpath) + 1;
715 if (valsize > userbufsize) {
716 error = EINVAL;
717 break;
719 (void) strcpy(opp->oprom_array, bpath);
721 #elif defined(__i386) || defined(__amd64)
723 extern char saved_cmdline[];
724 valsize = strlen(saved_cmdline) + 1;
725 if (valsize > userbufsize) {
726 error = EINVAL;
727 break;
729 (void) strcpy(opp->oprom_array, saved_cmdline);
730 #endif
731 opp->oprom_size = valsize - 1;
732 if (copyout(opp, (void *)arg, valsize + sizeof (uint_t)) != 0)
733 error = EFAULT;
734 break;
738 * convert a prom device path to an equivalent devfs path
740 case OPROMPROM2DEVNAME: {
741 char *dev_name;
744 * The input argument, a pathname, is a NULL terminated string.
746 if ((error = oprom_copyinstr(arg, opp->oprom_array,
747 (size_t)userbufsize, MAXPATHLEN)) != 0) {
748 break;
751 dev_name = kmem_alloc(MAXPATHLEN, KM_SLEEP);
753 error = i_promname_to_devname(opp->oprom_array, dev_name);
754 if (error != 0) {
755 kmem_free(dev_name, MAXPATHLEN);
756 break;
758 valsize = opp->oprom_size = strlen(dev_name);
759 if (++valsize > userbufsize) {
760 kmem_free(dev_name, MAXPATHLEN);
761 error = EINVAL;
762 break;
764 (void) strcpy(opp->oprom_array, dev_name);
765 if (copyout(opp, (void *)arg, sizeof (uint_t) + valsize) != 0)
766 error = EFAULT;
768 kmem_free(dev_name, MAXPATHLEN);
769 break;
773 * Convert a prom device path name to a driver name
775 case OPROMPATH2DRV: {
776 char *drv_name;
777 major_t maj;
780 * The input argument, a pathname, is a NULL terminated string.
782 if ((error = oprom_copyinstr(arg, opp->oprom_array,
783 (size_t)userbufsize, MAXPATHLEN)) != 0) {
784 break;
788 * convert path to a driver binding name
790 maj = path_to_major((char *)opp->oprom_array);
791 if (maj == DDI_MAJOR_T_NONE) {
792 error = EINVAL;
793 break;
797 * resolve any aliases
799 if ((drv_name = ddi_major_to_name(maj)) == NULL) {
800 error = EINVAL;
801 break;
804 (void) strcpy(opp->oprom_array, drv_name);
805 opp->oprom_size = strlen(drv_name);
806 if (copyout(opp, (void *)arg,
807 sizeof (uint_t) + opp->oprom_size + 1) != 0)
808 error = EFAULT;
809 break;
812 case OPROMGETVERSION:
814 * Get a string representing the running version of the
815 * prom. How to create such a string is platform dependent,
816 * so we just defer to a promif function. If no such
817 * association exists, the promif implementation
818 * may copy the string "unknown" into the given buffer,
819 * and return its length (incl. NULL terminator).
821 * We expect prom_version_name to return the actual
822 * length of the string, but copy at most userbufsize
823 * bytes into the given buffer, including NULL termination.
826 valsize = prom_version_name(opp->oprom_array, userbufsize);
827 if (valsize < 0) {
828 error = EINVAL;
829 break;
833 * copyout only the part of the user buffer we need to.
835 if (copyout(opp, (void *)arg,
836 (size_t)(min((uint_t)valsize, userbufsize) +
837 sizeof (uint_t))) != 0)
838 error = EFAULT;
839 break;
841 #if !defined(__i386) && !defined(__amd64)
842 case OPROMGETFBNAME:
844 * Return stdoutpath, if it's a frame buffer.
845 * Yes, we are comparing a possibly longer string against
846 * the size we're really going to copy, but so what?
848 if ((getzoneid() == GLOBAL_ZONEID) &&
849 (prom_stdout_is_framebuffer() != 0) &&
850 (userbufsize > strlen(prom_stdoutpath()))) {
851 prom_strip_options(prom_stdoutpath(),
852 opp->oprom_array); /* strip options and copy */
853 valsize = opp->oprom_size = strlen(opp->oprom_array);
854 if (copyout(opp, (void *)arg,
855 valsize + 1 + sizeof (uint_t)) != 0)
856 error = EFAULT;
857 } else
858 error = EINVAL;
859 break;
862 * Convert a logical or physical device path to prom device path
864 case OPROMDEV2PROMNAME: {
865 char *prom_name;
868 * The input argument, a pathname, is a NULL terminated string.
870 if ((error = oprom_copyinstr(arg, opp->oprom_array,
871 (size_t)userbufsize, MAXPATHLEN)) != 0) {
872 break;
875 prom_name = kmem_alloc(userbufsize, KM_SLEEP);
878 * convert the devfs path to an equivalent prom path
880 error = i_devname_to_promname(opp->oprom_array, prom_name,
881 userbufsize);
883 if (error != 0) {
884 kmem_free(prom_name, userbufsize);
885 break;
888 for (valsize = 0; valsize < userbufsize; valsize++) {
889 opp->oprom_array[valsize] = prom_name[valsize];
891 if ((valsize > 0) && (prom_name[valsize] == '\0') &&
892 (prom_name[valsize-1] == '\0')) {
893 break;
896 opp->oprom_size = valsize;
898 kmem_free(prom_name, userbufsize);
899 if (copyout(opp, (void *)arg, sizeof (uint_t) + valsize) != 0)
900 error = EFAULT;
902 break;
905 case OPROMSETOPT:
906 case OPROMSETOPT2: {
907 int namebuflen;
908 int valbuflen;
910 if ((prom_is_openprom() == 0) ||
911 (node_id == OBP_NONODE) || (node_id == OBP_BADNODE)) {
912 error = EINVAL;
913 break;
917 * The arguments are a property name and a value.
918 * Copy in the entire user buffer.
920 if (copyin(((caddr_t)arg + sizeof (uint_t)),
921 opp->oprom_array, userbufsize) != 0) {
922 error = EFAULT;
923 break;
927 * The property name is the first string, value second
929 namebuflen = strlen(opp->oprom_array);
930 valbuf = opp->oprom_array + namebuflen + 1;
931 valbuflen = strlen(valbuf);
933 if (cmd == OPROMSETOPT) {
934 valsize = valbuflen + 1; /* +1 for the '\0' */
935 } else {
936 if ((namebuflen + 1 + valbuflen + 1) > userbufsize) {
937 error = EINVAL;
938 break;
940 valsize = (opp->oprom_array + userbufsize) - valbuf;
944 * 4010173: 'name' is not an option, but it is a property.
946 if (strcmp(opp->oprom_array, "name") == 0)
947 error = EINVAL;
948 else if (prom_setprop(node_id, opp->oprom_array,
949 valbuf, valsize) < 0)
950 error = EINVAL;
952 break;
955 case OPROMREADY64: {
956 struct openprom_opr64 *opr =
957 (struct openprom_opr64 *)opp->oprom_array;
958 int i;
959 pnode_t id;
961 if (userbufsize < sizeof (*opr)) {
962 error = EINVAL;
963 break;
966 valsize = userbufsize -
967 offsetof(struct openprom_opr64, message);
969 i = prom_version_check(opr->message, valsize, &id);
970 opr->return_code = i;
971 opr->nodeid = (int)id;
973 valsize = offsetof(struct openprom_opr64, message);
974 valsize += strlen(opr->message) + 1;
977 * copyout only the part of the user buffer we need to.
979 if (copyout(opp, (void *)arg,
980 (size_t)(min((uint_t)valsize, userbufsize) +
981 sizeof (uint_t))) != 0)
982 error = EFAULT;
983 break;
985 } /* case OPROMREADY64 */
987 #endif /* !__i386 && !__amd64 */
988 } /* switch (cmd) */
990 kmem_free(opp, userbufsize + sizeof (uint_t) + 1);
991 return (error);
994 /*ARGSUSED*/
995 static int
996 opromioctl(dev_t dev, int cmd, intptr_t arg, int mode,
997 cred_t *credp, int *rvalp)
999 struct oprom_state *st;
1000 struct opromioctl_args arg_block;
1002 if (getminor(dev) >= MAX_OPENS)
1003 return (ENXIO);
1005 st = &oprom_state[getminor(dev)];
1006 ASSERT(st->already_open);
1007 arg_block.st = st;
1008 arg_block.cmd = cmd;
1009 arg_block.arg = arg;
1010 arg_block.mode = mode;
1011 return (prom_tree_access(opromioctl_cb, &arg_block, &st->tree_gen));
1015 * Copyin string and verify the actual string length is less than maxsize
1016 * specified by the caller.
1018 * Currently, maxsize is either OBP_MAXPROPNAME for property names
1019 * or MAXPATHLEN for device path names. userbufsize is specified
1020 * by the userland caller.
1022 static int
1023 oprom_copyinstr(intptr_t arg, char *buf, size_t bufsize, size_t maxsize)
1025 int error;
1026 size_t actual_len;
1028 if ((error = copyinstr(((caddr_t)arg + sizeof (uint_t)),
1029 buf, bufsize, &actual_len)) != 0) {
1030 return (error);
1032 if ((actual_len == 0) || (actual_len > maxsize)) {
1033 return (EINVAL);
1036 return (0);
1040 * Check pnode_t passed in from userland
1042 static int
1043 oprom_checknodeid(pnode_t node_id, pnode_t current_id)
1045 int depth;
1046 pnode_t id[OBP_STACKDEPTH];
1049 * optimized path
1051 if (node_id == 0) {
1052 return (1);
1054 if (node_id == OBP_BADNODE) {
1055 return (0);
1057 if ((current_id != OBP_BADNODE) && ((node_id == current_id) ||
1058 (node_id == prom_nextnode(current_id)) ||
1059 (node_id == prom_childnode(current_id)))) {
1060 return (1);
1064 * long path: walk from root till we find node_id
1066 depth = 1;
1067 id[0] = prom_nextnode((pnode_t)0);
1069 while (depth) {
1070 if (id[depth - 1] == node_id)
1071 return (1); /* node_id found */
1073 if (id[depth] = prom_childnode(id[depth - 1])) {
1074 depth++;
1075 continue;
1078 while (depth &&
1079 ((id[depth - 1] = prom_nextnode(id[depth - 1])) == 0))
1080 depth--;
1082 return (0); /* node_id not found */
1085 static int
1086 oprom_copytree(struct oprom_state *st, uint_t flag)
1088 ASSERT(st->snapshot == NULL && st->size == 0);
1089 return (oprom_copynode(
1090 prom_nextnode(0), flag, &st->snapshot, &st->size));
1093 static int
1094 oprom_snapshot(struct oprom_state *st, intptr_t arg)
1096 uint_t flag;
1098 if (oprom_setstate(st, IOC_SNAP) == -1)
1099 return (EBUSY);
1101 /* copyin flag and create snapshot */
1102 if ((copyin((void *)arg, &flag, sizeof (uint_t)) != 0) ||
1103 (oprom_copytree(st, flag) != 0)) {
1104 (void) oprom_setstate(st, IOC_IDLE);
1105 return (EFAULT);
1109 /* copyout the size of the snapshot */
1110 flag = (uint_t)st->size;
1111 if (copyout(&flag, (void *)arg, sizeof (uint_t)) != 0) {
1112 kmem_free(st->snapshot, st->size);
1113 st->snapshot = NULL;
1114 st->size = 0;
1115 (void) oprom_setstate(st, IOC_IDLE);
1116 return (EFAULT);
1119 (void) oprom_setstate(st, IOC_DONE);
1120 return (0);
1123 static int
1124 oprom_copyout(struct oprom_state *st, intptr_t arg)
1126 int error = 0;
1127 uint_t size;
1129 if (oprom_setstate(st, IOC_COPY) == -1)
1130 return (EBUSY);
1132 /* copyin size and copyout snapshot */
1133 if (copyin((void *)arg, &size, sizeof (uint_t)) != 0)
1134 error = EFAULT;
1135 else if (size < st->size)
1136 error = EINVAL;
1137 else if (copyout(st->snapshot, (void *)arg, st->size) != 0)
1138 error = EFAULT;
1140 if (error) {
1142 * on error keep the snapshot until a successful
1143 * copyout or when the driver is closed.
1145 (void) oprom_setstate(st, IOC_DONE);
1146 return (error);
1149 kmem_free(st->snapshot, st->size);
1150 st->snapshot = NULL;
1151 st->size = 0;
1152 (void) oprom_setstate(st, IOC_IDLE);
1153 return (0);
1157 * Copy all properties of nodeid into a single packed nvlist
1159 static int
1160 oprom_copyprop(pnode_t nodeid, uint_t flag, nvlist_t *nvl)
1162 int proplen;
1163 char *propname, *propval, *buf1, *buf2;
1165 ASSERT(nvl != NULL);
1168 * non verbose mode, get the "name" property only
1170 if (flag == 0) {
1171 proplen = prom_getproplen(nodeid, "name");
1172 if (proplen <= 0) {
1173 cmn_err(CE_WARN,
1174 "failed to get the name of openprom node 0x%x",
1175 nodeid);
1176 (void) nvlist_add_string(nvl, "name", "");
1177 return (0);
1179 propval = kmem_zalloc(proplen + 1, KM_SLEEP);
1180 (void) prom_getprop(nodeid, "name", propval);
1181 (void) nvlist_add_string(nvl, "name", propval);
1182 kmem_free(propval, proplen + 1);
1183 return (0);
1187 * Ask for first property by passing a NULL string
1189 buf1 = kmem_alloc(OBP_MAXPROPNAME, KM_SLEEP);
1190 buf2 = kmem_zalloc(OBP_MAXPROPNAME, KM_SLEEP);
1191 buf1[0] = '\0';
1192 while (propname = (char *)prom_nextprop(nodeid, buf1, buf2)) {
1193 if (strlen(propname) == 0)
1194 break; /* end of prop list */
1195 (void) strcpy(buf1, propname);
1197 proplen = prom_getproplen(nodeid, propname);
1198 if (proplen == 0) {
1199 /* boolean property */
1200 (void) nvlist_add_boolean(nvl, propname);
1201 continue;
1203 /* add 1 for null termination in case of a string */
1204 propval = kmem_zalloc(proplen + 1, KM_SLEEP);
1205 (void) prom_getprop(nodeid, propname, propval);
1206 (void) nvlist_add_byte_array(nvl, propname,
1207 (uchar_t *)propval, proplen + 1);
1208 kmem_free(propval, proplen + 1);
1209 bzero(buf2, OBP_MAXPROPNAME);
1212 kmem_free(buf1, OBP_MAXPROPNAME);
1213 kmem_free(buf2, OBP_MAXPROPNAME);
1215 return (0);
1219 * Copy all children and descendents into a a packed nvlist
1221 static int
1222 oprom_copychild(pnode_t nodeid, uint_t flag, char **buf, size_t *size)
1224 nvlist_t *nvl;
1225 pnode_t child = prom_childnode(nodeid);
1227 if (child == 0)
1228 return (0);
1230 (void) nvlist_alloc(&nvl, 0, KM_SLEEP);
1231 while (child != 0) {
1232 char *nodebuf = NULL;
1233 size_t nodesize = 0;
1234 if (oprom_copynode(child, flag, &nodebuf, &nodesize)) {
1235 nvlist_free(nvl);
1236 cmn_err(CE_WARN, "failed to copy nodeid 0x%x", child);
1237 return (-1);
1239 (void) nvlist_add_byte_array(nvl, "node",
1240 (uchar_t *)nodebuf, nodesize);
1241 kmem_free(nodebuf, nodesize);
1242 child = prom_nextnode(child);
1245 (void) nvlist_pack(nvl, buf, size, NV_ENCODE_NATIVE, KM_SLEEP);
1246 nvlist_free(nvl);
1247 return (0);
1251 * Copy a node into a packed nvlist
1253 static int
1254 oprom_copynode(pnode_t nodeid, uint_t flag, char **buf, size_t *size)
1256 int error = 0;
1257 nvlist_t *nvl;
1258 char *childlist = NULL;
1259 size_t childsize = 0;
1261 (void) nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP);
1262 ASSERT(nvl != NULL);
1264 /* @nodeid -- @ is not a legal char in a 1275 property name */
1265 (void) nvlist_add_int32(nvl, "@nodeid", (int32_t)nodeid);
1267 /* properties */
1268 if (error = oprom_copyprop(nodeid, flag, nvl))
1269 goto fail;
1271 /* children */
1272 error = oprom_copychild(nodeid, flag, &childlist, &childsize);
1273 if (error != 0)
1274 goto fail;
1275 if (childlist != NULL) {
1276 (void) nvlist_add_byte_array(nvl, "@child",
1277 (uchar_t *)childlist, (uint_t)childsize);
1278 kmem_free(childlist, childsize);
1281 /* pack into contiguous buffer */
1282 error = nvlist_pack(nvl, buf, size, NV_ENCODE_NATIVE, KM_SLEEP);
1284 fail:
1285 nvlist_free(nvl);
1286 return (error);
1290 * The driver is stateful across OPROMSNAPSHOT and OPROMCOPYOUT.
1291 * This function encapsulates the state machine:
1293 * -> IOC_IDLE -> IOC_SNAP -> IOC_DONE -> IOC_COPY ->
1294 * | SNAPSHOT COPYOUT |
1295 * --------------------------------------------------
1297 * Returns 0 on success and -1 on failure
1299 static int
1300 oprom_setstate(struct oprom_state *st, int16_t new_state)
1302 int ret = 0;
1304 mutex_enter(&oprom_lock);
1305 switch (new_state) {
1306 case IOC_IDLE:
1307 case IOC_DONE:
1308 break;
1309 case IOC_SNAP:
1310 if (st->ioc_state != IOC_IDLE)
1311 ret = -1;
1312 break;
1313 case IOC_COPY:
1314 if (st->ioc_state != IOC_DONE)
1315 ret = -1;
1316 break;
1317 default:
1318 ret = -1;
1321 if (ret == 0)
1322 st->ioc_state = new_state;
1323 else
1324 cmn_err(CE_NOTE, "incorrect state transition from %d to %d",
1325 st->ioc_state, new_state);
1326 mutex_exit(&oprom_lock);
1327 return (ret);