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.
27 * Ported from 4.1.1_PSRA: "@(#)openprom.c 1.19 91/02/19 SMI";
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>
43 #include <sys/cmn_err.h>
45 #include <sys/openpromio.h>
48 #include <sys/modctl.h>
49 #include <sys/debug.h>
50 #include <sys/autoconf.h>
52 #include <sys/sunddi.h>
53 #include <sys/promif.h>
54 #include <sys/sysmacros.h> /* offsetof */
55 #include <sys/nvpair.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
,
89 static int opattach(dev_info_t
*, ddi_attach_cmd_t cmd
);
90 static int opdetach(dev_info_t
*, ddi_detach_cmd_t cmd
);
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 */
108 opromioctl
, /* ioctl */
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, */
122 nulldev
, /* identify */
124 opattach
, /* attach */
125 opdetach
, /* detach */
127 &openeepr_cb_ops
, /* driver operations */
128 NULL
, /* bus operations */
130 ddi_quiesce_not_needed
, /* quiesce */
134 * Module linkage information for the kernel.
136 static struct modldrv modldrv
= {
138 "OPENPROM/NVRAM Driver",
142 static struct modlinkage modlinkage
= {
153 mutex_init(&oprom_lock
, NULL
, MUTEX_DRIVER
, NULL
);
155 error
= mod_install(&modlinkage
);
157 mutex_destroy(&oprom_lock
);
165 _info(struct modinfo
*modinfop
)
167 return (mod_info(&modlinkage
, modinfop
));
175 error
= mod_remove(&modlinkage
);
179 mutex_destroy(&oprom_lock
);
183 static dev_info_t
*opdip
;
184 static pnode_t options_nodeid
;
188 opinfo(dev_info_t
*dip
, ddi_info_cmd_t infocmd
, void *arg
, void **result
)
190 int error
= DDI_FAILURE
;
193 case DDI_INFO_DEVT2DEVINFO
:
194 *result
= (void *)opdip
;
197 case DDI_INFO_DEVT2INSTANCE
:
198 /* All dev_t's map to the same, single instance */
210 opattach(dev_info_t
*dip
, ddi_attach_cmd_t cmd
)
215 if (prom_is_openprom()) {
216 options_nodeid
= prom_optionsnode();
218 options_nodeid
= OBP_BADNODE
;
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
);
231 return (DDI_FAILURE
);
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
);
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.
254 opromopen(dev_t
*devp
, int flag
, int otyp
, cred_t
*credp
)
257 struct oprom_state
*st
= oprom_state
;
259 if (getminor(*devp
) != 0)
262 mutex_enter(&oprom_lock
);
263 for (m
= 0; m
< MAX_OPENS
; m
++)
264 if (st
->already_open
)
267 st
->already_open
= 1;
271 st
->current_id
= (pnode_t
)0;
272 ASSERT(st
->snapshot
== NULL
&& st
->size
== 0);
273 ASSERT(st
->ioc_state
== IOC_IDLE
);
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)
294 *devp
= makedevice(getmajor(*devp
), (minor_t
)m
);
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);
308 kmem_free(st
->snapshot
, st
->size
);
311 st
->ioc_state
= IOC_IDLE
;
313 mutex_enter(&oprom_lock
);
314 st
->already_open
= 0;
315 mutex_exit(&oprom_lock
);
322 get_bootpath_prop(char *bootpath
)
324 if (root_is_ramdisk
) {
325 if (BOP_GETPROP(bootops
, "bootarchive", bootpath
) == -1)
327 (void) strlcat(bootpath
, ":a", BO_MAXOBJNAME
);
329 if ((BOP_GETPROP(bootops
, "bootpath", bootpath
) == -1) ||
330 strlen(bootpath
) == 0) {
331 if (BOP_GETPROP(bootops
,
332 "boot-path", bootpath
) == -1)
335 if (memcmp(bootpath
, BP_ISCSI_DISK
,
336 strlen(BP_ISCSI_DISK
)) == 0) {
337 get_iscsi_bootpath_vhci(bootpath
);
344 struct opromioctl_args
{
345 struct oprom_state
*st
;
353 opromioctl_cb(void *avp
, int has_changed
)
355 struct opromioctl_args
*argp
= avp
;
359 struct oprom_state
*st
;
360 struct openpromio
*opp
;
366 char propname
[OBP_MAXPROPNAME
];
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
;
387 * and weed out unsupported commands on x86 platform
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)
398 kmem_free(opp
, sizeof (uint_t
) + 1);
401 valsize
= prom_asr_list_keys_len();
402 if (copyin((void *)arg
, &userbufsize
, sizeof (uint_t
)) != 0)
404 if (valsize
> userbufsize
)
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);
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)
417 kmem_free(valbuf
, valsize
+ 1);
418 kmem_free(opp
, valsize
+ sizeof (uint_t
) + 1);
421 valsize
= prom_asr_export_len();
422 if (copyin((void *)arg
, &userbufsize
, sizeof (uint_t
)) != 0)
424 if (valsize
> userbufsize
)
426 valbuf
= (char *)kmem_zalloc(valsize
+ 1, KM_SLEEP
);
427 if (prom_asr_export((caddr_t
)valbuf
) == -1) {
428 kmem_free(valbuf
, valsize
+ 1);
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)
437 kmem_free(valbuf
, valsize
+ 1);
438 kmem_free(opp
, valsize
+ sizeof (uint_t
) + 1);
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)
447 kmem_free(opp
, sizeof (uint_t
) + 1);
452 if ((mode
& FREAD
) == 0) {
455 node_id
= options_nodeid
;
460 #if !defined(__i386) && !defined(__amd64)
462 node_id
= options_nodeid
;
465 #endif /* !__i386 && !__amd64 */
471 case OPROMGETPROPLEN
:
474 if ((mode
& FREAD
) == 0) {
477 node_id
= st
->current_id
;
480 if (st
->snapshot
== NULL
)
485 case OPROMGETBOOTARGS
:
486 case OPROMGETBOOTPATH
:
487 case OPROMGETVERSION
:
489 case OPROMPROM2DEVNAME
:
490 #if !defined(__i386) && !defined(__amd64)
492 case OPROMDEV2PROMNAME
:
494 #endif /* !__i386 && !__amd64 */
495 if ((mode
& FREAD
) == 0) {
505 * Deal with SNAPSHOT and COPYOUT ioctls first
509 return (oprom_copyout(st
, arg
));
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)
524 if (userbufsize
== 0 || userbufsize
> OPROMMAXPARAM
)
527 opp
= (struct openpromio
*)kmem_zalloc(
528 userbufsize
+ sizeof (uint_t
) + 1, KM_SLEEP
);
537 case OPROMGETPROPLEN
:
539 if ((prom_is_openprom() == 0) ||
540 (node_id
== OBP_NONODE
) || (node_id
== OBP_BADNODE
)) {
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) {
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))
561 if (cmd
== OPROMGETPROPLEN
) {
562 int proplen
= valsize
;
564 if (userbufsize
< sizeof (int)) {
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
,
574 opp
->oprom_size
= valsize
;
575 if (valsize
< userbufsize
)
576 ++valsize
; /* Forces NULL termination */
577 /* If space permits */
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.
587 (void) strcpy(opp
->oprom_array
, "");
590 if (copyout(opp
, (void *)arg
, (valsize
+ sizeof (uint_t
))) != 0)
596 if ((prom_is_openprom() == 0) ||
597 (node_id
== OBP_NONODE
) || (node_id
== OBP_BADNODE
)) {
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) {
609 valbuf
= (char *)prom_nextprop(node_id
, opp
->oprom_array
,
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",
620 valsize
= strlen(valbuf
);
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
,
632 if (copyout(opp
, (void *)arg
, valsize
+ sizeof (uint_t
)) != 0)
640 if (prom_is_openprom() == 0 ||
641 userbufsize
< sizeof (pnode_t
)) {
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) {
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",
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
);
673 st
->current_id
= node_id
;
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)
687 * Is openboot supported on this machine?
688 * This ioctl used to return the console device,
689 * information; this is now done via modctl()
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)
716 case OPROMGETBOOTARGS
: {
717 extern char kern_bootargs
[];
719 valsize
= strlen(kern_bootargs
) + 1;
720 if (valsize
> userbufsize
) {
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)
732 case OPROMGETBOOTPATH
: {
733 #if defined(__sparc) && defined(_OBP)
735 char bpath
[OBP_MAXPATHLEN
];
736 if (get_bootpath_prop(bpath
) != 0) {
740 valsize
= strlen(bpath
) + 1;
741 if (valsize
> userbufsize
) {
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
) {
755 (void) strcpy(opp
->oprom_array
, saved_cmdline
);
757 opp
->oprom_size
= valsize
- 1;
758 if (copyout(opp
, (void *)arg
, valsize
+ sizeof (uint_t
)) != 0)
764 * convert a prom device path to an equivalent devfs path
766 case OPROMPROM2DEVNAME
: {
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) {
777 dev_name
= kmem_alloc(MAXPATHLEN
, KM_SLEEP
);
779 error
= i_promname_to_devname(opp
->oprom_array
, dev_name
);
781 kmem_free(dev_name
, MAXPATHLEN
);
784 valsize
= opp
->oprom_size
= strlen(dev_name
);
785 if (++valsize
> userbufsize
) {
786 kmem_free(dev_name
, MAXPATHLEN
);
790 (void) strcpy(opp
->oprom_array
, dev_name
);
791 if (copyout(opp
, (void *)arg
, sizeof (uint_t
) + valsize
) != 0)
794 kmem_free(dev_name
, MAXPATHLEN
);
799 * Convert a prom device path name to a driver name
801 case OPROMPATH2DRV
: {
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) {
814 * convert path to a driver binding name
816 maj
= path_to_major((char *)opp
->oprom_array
);
817 if (maj
== DDI_MAJOR_T_NONE
) {
823 * resolve any aliases
825 if ((drv_name
= ddi_major_to_name(maj
)) == NULL
) {
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)
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
);
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)
867 #if !defined(__i386) && !defined(__amd64)
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)
888 * Convert a logical or physical device path to prom device path
890 case OPROMDEV2PROMNAME
: {
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) {
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
,
910 kmem_free(prom_name
, userbufsize
);
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')) {
922 opp
->oprom_size
= valsize
;
924 kmem_free(prom_name
, userbufsize
);
925 if (copyout(opp
, (void *)arg
, sizeof (uint_t
) + valsize
) != 0)
936 if ((prom_is_openprom() == 0) ||
937 (node_id
== OBP_NONODE
) || (node_id
== OBP_BADNODE
)) {
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) {
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' */
962 if ((namebuflen
+ 1 + valbuflen
+ 1) > userbufsize
) {
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)
974 else if (prom_setprop(node_id
, opp
->oprom_array
,
975 valbuf
, valsize
) < 0)
982 struct openprom_opr64
*opr
=
983 (struct openprom_opr64
*)opp
->oprom_array
;
987 if (userbufsize
< sizeof (*opr
)) {
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)
1011 } /* case OPROMREADY64 */
1012 #endif /* !__i386 && !__amd64 */
1013 } /* switch (cmd) */
1015 kmem_free(opp
, userbufsize
+ sizeof (uint_t
) + 1);
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
)
1030 st
= &oprom_state
[getminor(dev
)];
1031 ASSERT(st
->already_open
);
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.
1048 oprom_copyinstr(intptr_t arg
, char *buf
, size_t bufsize
, size_t maxsize
)
1053 if ((error
= copyinstr(((caddr_t
)arg
+ sizeof (uint_t
)),
1054 buf
, bufsize
, &actual_len
)) != 0) {
1057 if ((actual_len
== 0) || (actual_len
> maxsize
)) {
1065 * Check pnode_t passed in from userland
1068 oprom_checknodeid(pnode_t node_id
, pnode_t current_id
)
1071 pnode_t id
[OBP_STACKDEPTH
];
1079 if (node_id
== OBP_BADNODE
) {
1082 if ((current_id
!= OBP_BADNODE
) && ((node_id
== current_id
) ||
1083 (node_id
== prom_nextnode(current_id
)) ||
1084 (node_id
== prom_childnode(current_id
)))) {
1089 * long path: walk from root till we find node_id
1092 id
[0] = prom_nextnode((pnode_t
)0);
1095 if (id
[depth
- 1] == node_id
)
1096 return (1); /* node_id found */
1098 if (id
[depth
] = prom_childnode(id
[depth
- 1])) {
1104 ((id
[depth
- 1] = prom_nextnode(id
[depth
- 1])) == 0))
1107 return (0); /* node_id not found */
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
));
1119 oprom_snapshot(struct oprom_state
*st
, intptr_t arg
)
1123 if (oprom_setstate(st
, IOC_SNAP
) == -1)
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
);
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
;
1140 (void) oprom_setstate(st
, IOC_IDLE
);
1144 (void) oprom_setstate(st
, IOC_DONE
);
1149 oprom_copyout(struct oprom_state
*st
, intptr_t arg
)
1154 if (oprom_setstate(st
, IOC_COPY
) == -1)
1157 /* copyin size and copyout snapshot */
1158 if (copyin((void *)arg
, &size
, sizeof (uint_t
)) != 0)
1160 else if (size
< st
->size
)
1162 else if (copyout(st
->snapshot
, (void *)arg
, st
->size
) != 0)
1167 * on error keep the snapshot until a successful
1168 * copyout or when the driver is closed.
1170 (void) oprom_setstate(st
, IOC_DONE
);
1174 kmem_free(st
->snapshot
, st
->size
);
1175 st
->snapshot
= NULL
;
1177 (void) oprom_setstate(st
, IOC_IDLE
);
1182 * Copy all properties of nodeid into a single packed nvlist
1185 oprom_copyprop(pnode_t nodeid
, uint_t flag
, nvlist_t
*nvl
)
1188 char *propname
, *propval
, *buf1
, *buf2
;
1190 ASSERT(nvl
!= NULL
);
1193 * non verbose mode, get the "name" property only
1196 proplen
= prom_getproplen(nodeid
, "name");
1199 "failed to get the name of openprom node 0x%x",
1201 (void) nvlist_add_string(nvl
, "name", "");
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);
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
);
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
);
1224 /* boolean property */
1225 (void) nvlist_add_boolean(nvl
, propname
);
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
);
1244 * Copy all children and descendents into a a packed nvlist
1247 oprom_copychild(pnode_t nodeid
, uint_t flag
, char **buf
, size_t *size
)
1250 pnode_t child
= prom_childnode(nodeid
);
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
)) {
1261 cmn_err(CE_WARN
, "failed to copy nodeid 0x%x", child
);
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
);
1276 * Copy a node into a packed nvlist
1279 oprom_copynode(pnode_t nodeid
, uint_t flag
, char **buf
, size_t *size
)
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
);
1293 if (error
= oprom_copyprop(nodeid
, flag
, nvl
))
1297 error
= oprom_copychild(nodeid
, flag
, &childlist
, &childsize
);
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
);
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
1325 oprom_setstate(struct oprom_state
*st
, int16_t new_state
)
1329 mutex_enter(&oprom_lock
);
1330 switch (new_state
) {
1335 if (st
->ioc_state
!= IOC_IDLE
)
1339 if (st
->ioc_state
!= IOC_DONE
)
1347 st
->ioc_state
= new_state
;
1349 cmn_err(CE_NOTE
, "incorrect state transition from %d to %d",
1350 st
->ioc_state
, new_state
);
1351 mutex_exit(&oprom_lock
);