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]
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";
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>
45 #include <sys/cmn_err.h>
47 #include <sys/openpromio.h>
50 #include <sys/modctl.h>
51 #include <sys/debug.h>
52 #include <sys/autoconf.h>
54 #include <sys/sunddi.h>
55 #include <sys/promif.h>
56 #include <sys/sysmacros.h> /* offsetof */
57 #include <sys/nvpair.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
,
91 static int opattach(dev_info_t
*, ddi_attach_cmd_t cmd
);
92 static int opdetach(dev_info_t
*, ddi_detach_cmd_t cmd
);
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 */
110 opromioctl
, /* ioctl */
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, */
124 nulldev
, /* identify */
126 opattach
, /* attach */
127 opdetach
, /* detach */
129 &openeepr_cb_ops
, /* driver operations */
130 NULL
, /* bus operations */
132 ddi_quiesce_not_needed
, /* quiesce */
136 * Module linkage information for the kernel.
138 static struct modldrv modldrv
= {
140 "OPENPROM/NVRAM Driver",
144 static struct modlinkage modlinkage
= {
155 mutex_init(&oprom_lock
, NULL
, MUTEX_DRIVER
, NULL
);
157 error
= mod_install(&modlinkage
);
159 mutex_destroy(&oprom_lock
);
167 _info(struct modinfo
*modinfop
)
169 return (mod_info(&modlinkage
, modinfop
));
177 error
= mod_remove(&modlinkage
);
181 mutex_destroy(&oprom_lock
);
185 static dev_info_t
*opdip
;
186 static pnode_t options_nodeid
;
190 opinfo(dev_info_t
*dip
, ddi_info_cmd_t infocmd
, void *arg
, void **result
)
192 int error
= DDI_FAILURE
;
195 case DDI_INFO_DEVT2DEVINFO
:
196 *result
= (void *)opdip
;
199 case DDI_INFO_DEVT2INSTANCE
:
200 /* All dev_t's map to the same, single instance */
212 opattach(dev_info_t
*dip
, ddi_attach_cmd_t cmd
)
217 if (prom_is_openprom()) {
218 options_nodeid
= prom_optionsnode();
220 options_nodeid
= OBP_BADNODE
;
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
);
233 return (DDI_FAILURE
);
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
);
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.
256 opromopen(dev_t
*devp
, int flag
, int otyp
, cred_t
*credp
)
259 struct oprom_state
*st
= oprom_state
;
261 if (getminor(*devp
) != 0)
264 mutex_enter(&oprom_lock
);
265 for (m
= 0; m
< MAX_OPENS
; m
++)
266 if (st
->already_open
)
269 st
->already_open
= 1;
273 st
->current_id
= (pnode_t
)0;
274 ASSERT(st
->snapshot
== NULL
&& st
->size
== 0);
275 ASSERT(st
->ioc_state
== IOC_IDLE
);
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)
296 *devp
= makedevice(getmajor(*devp
), (minor_t
)m
);
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);
310 kmem_free(st
->snapshot
, st
->size
);
313 st
->ioc_state
= IOC_IDLE
;
315 mutex_enter(&oprom_lock
);
316 st
->already_open
= 0;
317 mutex_exit(&oprom_lock
);
323 struct opromioctl_args
{
324 struct oprom_state
*st
;
332 opromioctl_cb(void *avp
, int has_changed
)
334 struct opromioctl_args
*argp
= avp
;
338 struct oprom_state
*st
;
339 struct openpromio
*opp
;
345 char propname
[OBP_MAXPROPNAME
];
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
;
366 * and weed out unsupported commands on x86 platform
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)
376 kmem_free(opp
, sizeof (uint_t
) + 1);
379 valsize
= prom_asr_list_keys_len();
380 if (copyin((void *)arg
, &userbufsize
, sizeof (uint_t
)) != 0)
382 if (valsize
> userbufsize
)
384 valbuf
= kmem_zalloc(valsize
+ 1, KM_SLEEP
);
385 if (prom_asr_list_keys((caddr_t
)valbuf
) == -1) {
386 kmem_free(valbuf
, valsize
+ 1);
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)
394 kmem_free(valbuf
, valsize
+ 1);
395 kmem_free(opp
, valsize
+ sizeof (uint_t
) + 1);
398 valsize
= prom_asr_export_len();
399 if (copyin((void *)arg
, &userbufsize
, sizeof (uint_t
)) != 0)
401 if (valsize
> userbufsize
)
403 valbuf
= kmem_zalloc(valsize
+ 1, KM_SLEEP
);
404 if (prom_asr_export((caddr_t
)valbuf
) == -1) {
405 kmem_free(valbuf
, valsize
+ 1);
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)
413 kmem_free(valbuf
, valsize
+ 1);
414 kmem_free(opp
, valsize
+ sizeof (uint_t
) + 1);
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)
422 kmem_free(opp
, sizeof (uint_t
) + 1);
427 if ((mode
& FREAD
) == 0) {
430 node_id
= options_nodeid
;
435 #if !defined(__i386) && !defined(__amd64)
437 node_id
= options_nodeid
;
440 #endif /* !__i386 && !__amd64 */
446 case OPROMGETPROPLEN
:
449 if ((mode
& FREAD
) == 0) {
452 node_id
= st
->current_id
;
455 if (st
->snapshot
== NULL
)
460 case OPROMGETBOOTARGS
:
461 case OPROMGETBOOTPATH
:
462 case OPROMGETVERSION
:
464 case OPROMPROM2DEVNAME
:
465 #if !defined(__i386) && !defined(__amd64)
467 case OPROMDEV2PROMNAME
:
469 #endif /* !__i386 && !__amd64 */
470 if ((mode
& FREAD
) == 0) {
480 * Deal with SNAPSHOT and COPYOUT ioctls first
484 return (oprom_copyout(st
, arg
));
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)
499 if (userbufsize
== 0 || userbufsize
> OPROMMAXPARAM
)
502 opp
= kmem_zalloc(userbufsize
+ sizeof (uint_t
) + 1, KM_SLEEP
);
511 case OPROMGETPROPLEN
:
513 if ((prom_is_openprom() == 0) ||
514 (node_id
== OBP_NONODE
) || (node_id
== OBP_BADNODE
)) {
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) {
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))
535 if (cmd
== OPROMGETPROPLEN
) {
536 int proplen
= valsize
;
538 if (userbufsize
< sizeof (int)) {
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
,
548 opp
->oprom_size
= valsize
;
549 if (valsize
< userbufsize
)
550 ++valsize
; /* Forces NULL termination */
551 /* If space permits */
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.
561 (void) strcpy(opp
->oprom_array
, "");
564 if (copyout(opp
, (void *)arg
, (valsize
+ sizeof (uint_t
))) != 0)
570 if ((prom_is_openprom() == 0) ||
571 (node_id
== OBP_NONODE
) || (node_id
== OBP_BADNODE
)) {
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) {
583 valbuf
= (char *)prom_nextprop(node_id
, opp
->oprom_array
,
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",
594 valsize
= strlen(valbuf
);
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
,
606 if (copyout(opp
, (void *)arg
, valsize
+ sizeof (uint_t
)) != 0)
614 if (prom_is_openprom() == 0 ||
615 userbufsize
< sizeof (pnode_t
)) {
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) {
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",
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
);
647 st
->current_id
= node_id
;
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)
661 * Is openboot supported on this machine?
662 * This ioctl used to return the console device,
663 * information; this is now done via modctl()
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)
690 case OPROMGETBOOTARGS
: {
691 extern char kern_bootargs
[];
693 valsize
= strlen(kern_bootargs
) + 1;
694 if (valsize
> userbufsize
) {
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)
706 case OPROMGETBOOTPATH
: {
707 #if defined(__sparc) && defined(_OBP)
709 char bpath
[OBP_MAXPATHLEN
];
710 if (get_bootpath_prop(bpath
) != 0) {
714 valsize
= strlen(bpath
) + 1;
715 if (valsize
> userbufsize
) {
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
) {
729 (void) strcpy(opp
->oprom_array
, saved_cmdline
);
731 opp
->oprom_size
= valsize
- 1;
732 if (copyout(opp
, (void *)arg
, valsize
+ sizeof (uint_t
)) != 0)
738 * convert a prom device path to an equivalent devfs path
740 case OPROMPROM2DEVNAME
: {
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) {
751 dev_name
= kmem_alloc(MAXPATHLEN
, KM_SLEEP
);
753 error
= i_promname_to_devname(opp
->oprom_array
, dev_name
);
755 kmem_free(dev_name
, MAXPATHLEN
);
758 valsize
= opp
->oprom_size
= strlen(dev_name
);
759 if (++valsize
> userbufsize
) {
760 kmem_free(dev_name
, MAXPATHLEN
);
764 (void) strcpy(opp
->oprom_array
, dev_name
);
765 if (copyout(opp
, (void *)arg
, sizeof (uint_t
) + valsize
) != 0)
768 kmem_free(dev_name
, MAXPATHLEN
);
773 * Convert a prom device path name to a driver name
775 case OPROMPATH2DRV
: {
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) {
788 * convert path to a driver binding name
790 maj
= path_to_major((char *)opp
->oprom_array
);
791 if (maj
== DDI_MAJOR_T_NONE
) {
797 * resolve any aliases
799 if ((drv_name
= ddi_major_to_name(maj
)) == NULL
) {
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)
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
);
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)
841 #if !defined(__i386) && !defined(__amd64)
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)
862 * Convert a logical or physical device path to prom device path
864 case OPROMDEV2PROMNAME
: {
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) {
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
,
884 kmem_free(prom_name
, userbufsize
);
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')) {
896 opp
->oprom_size
= valsize
;
898 kmem_free(prom_name
, userbufsize
);
899 if (copyout(opp
, (void *)arg
, sizeof (uint_t
) + valsize
) != 0)
910 if ((prom_is_openprom() == 0) ||
911 (node_id
== OBP_NONODE
) || (node_id
== OBP_BADNODE
)) {
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) {
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' */
936 if ((namebuflen
+ 1 + valbuflen
+ 1) > userbufsize
) {
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)
948 else if (prom_setprop(node_id
, opp
->oprom_array
,
949 valbuf
, valsize
) < 0)
956 struct openprom_opr64
*opr
=
957 (struct openprom_opr64
*)opp
->oprom_array
;
961 if (userbufsize
< sizeof (*opr
)) {
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)
985 } /* case OPROMREADY64 */
987 #endif /* !__i386 && !__amd64 */
990 kmem_free(opp
, userbufsize
+ sizeof (uint_t
) + 1);
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
)
1005 st
= &oprom_state
[getminor(dev
)];
1006 ASSERT(st
->already_open
);
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.
1023 oprom_copyinstr(intptr_t arg
, char *buf
, size_t bufsize
, size_t maxsize
)
1028 if ((error
= copyinstr(((caddr_t
)arg
+ sizeof (uint_t
)),
1029 buf
, bufsize
, &actual_len
)) != 0) {
1032 if ((actual_len
== 0) || (actual_len
> maxsize
)) {
1040 * Check pnode_t passed in from userland
1043 oprom_checknodeid(pnode_t node_id
, pnode_t current_id
)
1046 pnode_t id
[OBP_STACKDEPTH
];
1054 if (node_id
== OBP_BADNODE
) {
1057 if ((current_id
!= OBP_BADNODE
) && ((node_id
== current_id
) ||
1058 (node_id
== prom_nextnode(current_id
)) ||
1059 (node_id
== prom_childnode(current_id
)))) {
1064 * long path: walk from root till we find node_id
1067 id
[0] = prom_nextnode((pnode_t
)0);
1070 if (id
[depth
- 1] == node_id
)
1071 return (1); /* node_id found */
1073 if (id
[depth
] = prom_childnode(id
[depth
- 1])) {
1079 ((id
[depth
- 1] = prom_nextnode(id
[depth
- 1])) == 0))
1082 return (0); /* node_id not found */
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
));
1094 oprom_snapshot(struct oprom_state
*st
, intptr_t arg
)
1098 if (oprom_setstate(st
, IOC_SNAP
) == -1)
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
);
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
;
1115 (void) oprom_setstate(st
, IOC_IDLE
);
1119 (void) oprom_setstate(st
, IOC_DONE
);
1124 oprom_copyout(struct oprom_state
*st
, intptr_t arg
)
1129 if (oprom_setstate(st
, IOC_COPY
) == -1)
1132 /* copyin size and copyout snapshot */
1133 if (copyin((void *)arg
, &size
, sizeof (uint_t
)) != 0)
1135 else if (size
< st
->size
)
1137 else if (copyout(st
->snapshot
, (void *)arg
, st
->size
) != 0)
1142 * on error keep the snapshot until a successful
1143 * copyout or when the driver is closed.
1145 (void) oprom_setstate(st
, IOC_DONE
);
1149 kmem_free(st
->snapshot
, st
->size
);
1150 st
->snapshot
= NULL
;
1152 (void) oprom_setstate(st
, IOC_IDLE
);
1157 * Copy all properties of nodeid into a single packed nvlist
1160 oprom_copyprop(pnode_t nodeid
, uint_t flag
, nvlist_t
*nvl
)
1163 char *propname
, *propval
, *buf1
, *buf2
;
1165 ASSERT(nvl
!= NULL
);
1168 * non verbose mode, get the "name" property only
1171 proplen
= prom_getproplen(nodeid
, "name");
1174 "failed to get the name of openprom node 0x%x",
1176 (void) nvlist_add_string(nvl
, "name", "");
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);
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
);
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
);
1199 /* boolean property */
1200 (void) nvlist_add_boolean(nvl
, propname
);
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
);
1219 * Copy all children and descendents into a a packed nvlist
1222 oprom_copychild(pnode_t nodeid
, uint_t flag
, char **buf
, size_t *size
)
1225 pnode_t child
= prom_childnode(nodeid
);
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
)) {
1236 cmn_err(CE_WARN
, "failed to copy nodeid 0x%x", child
);
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
);
1251 * Copy a node into a packed nvlist
1254 oprom_copynode(pnode_t nodeid
, uint_t flag
, char **buf
, size_t *size
)
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
);
1268 if (error
= oprom_copyprop(nodeid
, flag
, nvl
))
1272 error
= oprom_copychild(nodeid
, flag
, &childlist
, &childsize
);
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
);
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
1300 oprom_setstate(struct oprom_state
*st
, int16_t new_state
)
1304 mutex_enter(&oprom_lock
);
1305 switch (new_state
) {
1310 if (st
->ioc_state
!= IOC_IDLE
)
1314 if (st
->ioc_state
!= IOC_DONE
)
1322 st
->ioc_state
= new_state
;
1324 cmn_err(CE_NOTE
, "incorrect state transition from %d to %d",
1325 st
->ioc_state
, new_state
);
1326 mutex_exit(&oprom_lock
);