xref: /onnv-gate/usr/src/cmd/prtconf/pdevinfo.c (revision 7261:487d2e19fab5)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
54453Scth  * Common Development and Distribution License (the "License").
64453Scth  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
226640Scth  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate /*
290Sstevel@tonic-gate  * For machines that support the openprom, fetch and print the list
300Sstevel@tonic-gate  * of devices that the kernel has fetched from the prom or conjured up.
310Sstevel@tonic-gate  */
320Sstevel@tonic-gate 
330Sstevel@tonic-gate #include <stdio.h>
340Sstevel@tonic-gate #include <stdarg.h>
350Sstevel@tonic-gate #include <stdlib.h>
360Sstevel@tonic-gate #include <fcntl.h>
370Sstevel@tonic-gate #include <ctype.h>
380Sstevel@tonic-gate #include <strings.h>
390Sstevel@tonic-gate #include <unistd.h>
400Sstevel@tonic-gate #include <stropts.h>
410Sstevel@tonic-gate #include <sys/types.h>
420Sstevel@tonic-gate #include <sys/mkdev.h>
430Sstevel@tonic-gate #include <sys/sunddi.h>
440Sstevel@tonic-gate #include <sys/openpromio.h>
450Sstevel@tonic-gate #include <sys/modctl.h>
460Sstevel@tonic-gate #include <sys/stat.h>
470Sstevel@tonic-gate #include <zone.h>
480Sstevel@tonic-gate #include <libnvpair.h>
490Sstevel@tonic-gate #include "prtconf.h"
500Sstevel@tonic-gate 
510Sstevel@tonic-gate 
520Sstevel@tonic-gate typedef char *(*dump_propname_t)(void *);
530Sstevel@tonic-gate typedef int (*dump_proptype_t)(void *);
540Sstevel@tonic-gate typedef int (*dump_propints_t)(void *, int **);
550Sstevel@tonic-gate typedef int (*dump_propint64_t)(void *, int64_t **);
560Sstevel@tonic-gate typedef int (*dump_propstrings_t)(void *, char **);
570Sstevel@tonic-gate typedef int (*dump_propbytes_t)(void *, uchar_t **);
580Sstevel@tonic-gate typedef int (*dump_proprawdata_t)(void *, uchar_t **);
590Sstevel@tonic-gate 
600Sstevel@tonic-gate typedef struct dumpops_common {
610Sstevel@tonic-gate 	dump_propname_t doc_propname;
620Sstevel@tonic-gate 	dump_proptype_t doc_proptype;
630Sstevel@tonic-gate 	dump_propints_t doc_propints;
640Sstevel@tonic-gate 	dump_propint64_t doc_propint64;
650Sstevel@tonic-gate 	dump_propstrings_t doc_propstrings;
660Sstevel@tonic-gate 	dump_propbytes_t doc_propbytes;
670Sstevel@tonic-gate 	dump_proprawdata_t doc_proprawdata;
680Sstevel@tonic-gate } dumpops_common_t;
690Sstevel@tonic-gate 
700Sstevel@tonic-gate static const dumpops_common_t prop_dumpops = {
710Sstevel@tonic-gate 	(dump_propname_t)di_prop_name,
720Sstevel@tonic-gate 	(dump_proptype_t)di_prop_type,
730Sstevel@tonic-gate 	(dump_propints_t)di_prop_ints,
740Sstevel@tonic-gate 	(dump_propint64_t)di_prop_int64,
750Sstevel@tonic-gate 	(dump_propstrings_t)di_prop_strings,
760Sstevel@tonic-gate 	(dump_propbytes_t)di_prop_bytes,
770Sstevel@tonic-gate 	(dump_proprawdata_t)di_prop_rawdata
780Sstevel@tonic-gate }, pathprop_common_dumpops = {
790Sstevel@tonic-gate 	(dump_propname_t)di_path_prop_name,
800Sstevel@tonic-gate 	(dump_proptype_t)di_path_prop_type,
810Sstevel@tonic-gate 	(dump_propints_t)di_path_prop_ints,
820Sstevel@tonic-gate 	(dump_propint64_t)di_path_prop_int64s,
830Sstevel@tonic-gate 	(dump_propstrings_t)di_path_prop_strings,
840Sstevel@tonic-gate 	(dump_propbytes_t)di_path_prop_bytes,
850Sstevel@tonic-gate 	(dump_proprawdata_t)di_path_prop_bytes
860Sstevel@tonic-gate };
870Sstevel@tonic-gate 
880Sstevel@tonic-gate typedef void *(*dump_nextprop_t)(void *, void *);
890Sstevel@tonic-gate typedef dev_t (*dump_propdevt_t)(void *);
900Sstevel@tonic-gate 
910Sstevel@tonic-gate typedef struct dumpops {
920Sstevel@tonic-gate 	const dumpops_common_t *dop_common;
930Sstevel@tonic-gate 	dump_nextprop_t dop_nextprop;
940Sstevel@tonic-gate 	dump_propdevt_t dop_propdevt;
950Sstevel@tonic-gate } dumpops_t;
960Sstevel@tonic-gate 
970Sstevel@tonic-gate static const dumpops_t sysprop_dumpops = {
980Sstevel@tonic-gate 	&prop_dumpops,
990Sstevel@tonic-gate 	(dump_nextprop_t)di_prop_sys_next,
1000Sstevel@tonic-gate 	NULL
1010Sstevel@tonic-gate }, globprop_dumpops = {
1020Sstevel@tonic-gate 	&prop_dumpops,
1030Sstevel@tonic-gate 	(dump_nextprop_t)di_prop_global_next,
1040Sstevel@tonic-gate 	NULL
1050Sstevel@tonic-gate }, drvprop_dumpops = {
1060Sstevel@tonic-gate 	&prop_dumpops,
1070Sstevel@tonic-gate 	(dump_nextprop_t)di_prop_drv_next,
1080Sstevel@tonic-gate 	(dump_propdevt_t)di_prop_devt
1090Sstevel@tonic-gate }, hwprop_dumpops = {
1100Sstevel@tonic-gate 	&prop_dumpops,
1110Sstevel@tonic-gate 	(dump_nextprop_t)di_prop_hw_next,
1120Sstevel@tonic-gate 	NULL
1130Sstevel@tonic-gate }, pathprop_dumpops = {
1140Sstevel@tonic-gate 	&pathprop_common_dumpops,
1150Sstevel@tonic-gate 	(dump_nextprop_t)di_path_prop_next,
1160Sstevel@tonic-gate 	NULL
1170Sstevel@tonic-gate };
1180Sstevel@tonic-gate 
1190Sstevel@tonic-gate #define	PROPNAME(ops) (ops->dop_common->doc_propname)
1200Sstevel@tonic-gate #define	PROPTYPE(ops) (ops->dop_common->doc_proptype)
1210Sstevel@tonic-gate #define	PROPINTS(ops) (ops->dop_common->doc_propints)
1220Sstevel@tonic-gate #define	PROPINT64(ops) (ops->dop_common->doc_propint64)
1230Sstevel@tonic-gate #define	PROPSTRINGS(ops) (ops->dop_common->doc_propstrings)
1240Sstevel@tonic-gate #define	PROPBYTES(ops) (ops->dop_common->doc_propbytes)
1250Sstevel@tonic-gate #define	PROPRAWDATA(ops) (ops->dop_common->doc_proprawdata)
1260Sstevel@tonic-gate #define	NEXTPROP(ops) (ops->dop_nextprop)
1270Sstevel@tonic-gate #define	PROPDEVT(ops) (ops->dop_propdevt)
1280Sstevel@tonic-gate #define	NUM_ELEMENTS(A) (sizeof (A) / sizeof (A[0]))
1290Sstevel@tonic-gate 
1300Sstevel@tonic-gate static int prop_type_guess(const dumpops_t *, void *, void **, int *);
1310Sstevel@tonic-gate static void walk_driver(di_node_t, di_devlink_handle_t);
1320Sstevel@tonic-gate static int dump_devs(di_node_t, void *);
1337224Scth static int dump_prop_list(const dumpops_t *, const char *,
134*7261Scth 				int, void *, dev_t, int *);
1350Sstevel@tonic-gate static int _error(const char *, ...);
1360Sstevel@tonic-gate static int is_openprom();
1370Sstevel@tonic-gate static void walk(uchar_t *, uint_t, int);
1380Sstevel@tonic-gate static void dump_node(nvlist_t *, int);
1390Sstevel@tonic-gate static void dump_prodinfo(di_prom_handle_t, di_node_t, const char **,
1400Sstevel@tonic-gate 				char *, int);
1410Sstevel@tonic-gate static di_node_t find_node_by_name(di_prom_handle_t, di_node_t, char *);
1420Sstevel@tonic-gate static int get_propval_by_name(di_prom_handle_t, di_node_t,
1430Sstevel@tonic-gate 				const char *, uchar_t **);
144*7261Scth static int dump_compatible(char *, int, di_node_t);
1450Sstevel@tonic-gate static void dump_pathing_data(int, di_node_t);
1460Sstevel@tonic-gate static void dump_minor_data(int, di_node_t, di_devlink_handle_t);
1470Sstevel@tonic-gate static void dump_link_data(int, di_node_t, di_devlink_handle_t);
1480Sstevel@tonic-gate static int print_composite_string(const char *, char *, int);
1490Sstevel@tonic-gate static void print_one(nvpair_t *, int);
1500Sstevel@tonic-gate static int unprintable(char *, int);
1510Sstevel@tonic-gate static int promopen(int);
1520Sstevel@tonic-gate static void promclose();
1530Sstevel@tonic-gate static di_node_t find_target_node(di_node_t);
1540Sstevel@tonic-gate static void node_display_set(di_node_t);
1550Sstevel@tonic-gate 
1560Sstevel@tonic-gate void
1570Sstevel@tonic-gate prtconf_devinfo(void)
1580Sstevel@tonic-gate {
1590Sstevel@tonic-gate 	struct di_priv_data	fetch;
1600Sstevel@tonic-gate 	di_devlink_handle_t	devlink_hdl = NULL;
1610Sstevel@tonic-gate 	di_node_t		root_node;
1620Sstevel@tonic-gate 	uint_t			flag;
1630Sstevel@tonic-gate 	char			*rootpath;
1640Sstevel@tonic-gate 
1650Sstevel@tonic-gate 	dprintf("verbosemode %s\n", opts.o_verbose ? "on" : "off");
1660Sstevel@tonic-gate 
1670Sstevel@tonic-gate 	/* determine what info we need to get from kernel */
1680Sstevel@tonic-gate 	flag = DINFOSUBTREE;
1690Sstevel@tonic-gate 	rootpath = "/";
1700Sstevel@tonic-gate 
1710Sstevel@tonic-gate 	if (opts.o_target) {
1720Sstevel@tonic-gate 		flag |= (DINFOMINOR | DINFOPATH);
1730Sstevel@tonic-gate 	}
1740Sstevel@tonic-gate 
1750Sstevel@tonic-gate 	if (opts.o_forcecache) {
1764453Scth 		if (dbg.d_forceload) {
1770Sstevel@tonic-gate 			exit(_error(NULL, "option combination not supported"));
1780Sstevel@tonic-gate 		}
1790Sstevel@tonic-gate 		if (strcmp(rootpath, "/") != 0) {
1800Sstevel@tonic-gate 			exit(_error(NULL, "invalid root path for option"));
1810Sstevel@tonic-gate 		}
1820Sstevel@tonic-gate 		flag = DINFOCACHE;
1834453Scth 	} else if (opts.o_verbose) {
1844453Scth 		flag |= (DINFOPROP | DINFOMINOR |
1854453Scth 		    DINFOPRIVDATA | DINFOPATH | DINFOLYR);
1860Sstevel@tonic-gate 	}
1870Sstevel@tonic-gate 
1880Sstevel@tonic-gate 	if (dbg.d_forceload) {
1890Sstevel@tonic-gate 		flag |= DINFOFORCE;
1900Sstevel@tonic-gate 	}
1910Sstevel@tonic-gate 
1920Sstevel@tonic-gate 	if (opts.o_verbose) {
1930Sstevel@tonic-gate 		init_priv_data(&fetch);
1940Sstevel@tonic-gate 		root_node = di_init_impl(rootpath, flag, &fetch);
1950Sstevel@tonic-gate 
1960Sstevel@tonic-gate 		/* get devlink (aka aliases) data */
1970Sstevel@tonic-gate 		if ((devlink_hdl = di_devlink_init(NULL, 0)) == NULL)
1980Sstevel@tonic-gate 			exit(_error("di_devlink_init() failed."));
1990Sstevel@tonic-gate 	} else
2000Sstevel@tonic-gate 		root_node = di_init(rootpath, flag);
2010Sstevel@tonic-gate 
2020Sstevel@tonic-gate 	if (root_node == DI_NODE_NIL) {
2030Sstevel@tonic-gate 		(void) _error(NULL, "devinfo facility not available");
2040Sstevel@tonic-gate 		/* not an error if this isn't the global zone */
2050Sstevel@tonic-gate 		if (getzoneid() == GLOBAL_ZONEID)
2060Sstevel@tonic-gate 			exit(-1);
2070Sstevel@tonic-gate 		else
2080Sstevel@tonic-gate 			exit(0);
2090Sstevel@tonic-gate 	}
2100Sstevel@tonic-gate 
2110Sstevel@tonic-gate 	/*
2120Sstevel@tonic-gate 	 * ...and walk all nodes to report them out...
2130Sstevel@tonic-gate 	 */
2140Sstevel@tonic-gate 	if (dbg.d_bydriver) {
2150Sstevel@tonic-gate 		opts.o_target = 0;
2160Sstevel@tonic-gate 		walk_driver(root_node, devlink_hdl);
2170Sstevel@tonic-gate 		if (devlink_hdl != NULL)
2180Sstevel@tonic-gate 			(void) di_devlink_fini(&devlink_hdl);
2190Sstevel@tonic-gate 		di_fini(root_node);
2200Sstevel@tonic-gate 		return;
2210Sstevel@tonic-gate 	}
2220Sstevel@tonic-gate 
2230Sstevel@tonic-gate 	if (opts.o_target) {
2240Sstevel@tonic-gate 		di_node_t target_node, node;
2250Sstevel@tonic-gate 
2260Sstevel@tonic-gate 		target_node = find_target_node(root_node);
2270Sstevel@tonic-gate 		if (target_node == DI_NODE_NIL) {
2280Sstevel@tonic-gate 			(void) fprintf(stderr, "%s: "
2294453Scth 			    "invalid device path specified\n",
2304453Scth 			    opts.o_progname);
2310Sstevel@tonic-gate 			exit(1);
2320Sstevel@tonic-gate 		}
2330Sstevel@tonic-gate 
2340Sstevel@tonic-gate 		/* mark the target node so we display it */
2350Sstevel@tonic-gate 		node_display_set(target_node);
2360Sstevel@tonic-gate 
2370Sstevel@tonic-gate 		if (opts.o_ancestors) {
2380Sstevel@tonic-gate 			/*
2390Sstevel@tonic-gate 			 * mark the ancestors of this node so we display
2400Sstevel@tonic-gate 			 * them as well
2410Sstevel@tonic-gate 			 */
2420Sstevel@tonic-gate 			node = target_node;
2430Sstevel@tonic-gate 			while (node = di_parent_node(node))
2440Sstevel@tonic-gate 				node_display_set(node);
2450Sstevel@tonic-gate 		} else {
2460Sstevel@tonic-gate 			/*
2470Sstevel@tonic-gate 			 * when we display device tree nodes the indentation
2480Sstevel@tonic-gate 			 * level is based off of tree depth.
2490Sstevel@tonic-gate 			 *
2500Sstevel@tonic-gate 			 * here we increment o_target to reflect the
2510Sstevel@tonic-gate 			 * depth of the target node in the tree.  we do
2520Sstevel@tonic-gate 			 * this so that when we calculate the indentation
2530Sstevel@tonic-gate 			 * level we can subtract o_target so that the
2540Sstevel@tonic-gate 			 * target node starts with an indentation of zero.
2550Sstevel@tonic-gate 			 */
2560Sstevel@tonic-gate 			node = target_node;
2570Sstevel@tonic-gate 			while (node = di_parent_node(node))
2580Sstevel@tonic-gate 				opts.o_target++;
2590Sstevel@tonic-gate 		}
2600Sstevel@tonic-gate 
2610Sstevel@tonic-gate 		if (opts.o_children) {
2620Sstevel@tonic-gate 			/*
2630Sstevel@tonic-gate 			 * mark the children of this node so we display
2640Sstevel@tonic-gate 			 * them as well
2650Sstevel@tonic-gate 			 */
2660Sstevel@tonic-gate 			(void) di_walk_node(target_node, DI_WALK_CLDFIRST,
2674453Scth 			    (void *)1,
2684453Scth 			    (int (*)(di_node_t, void *))
2694453Scth 			    node_display_set);
2700Sstevel@tonic-gate 		}
2710Sstevel@tonic-gate 	}
2720Sstevel@tonic-gate 
2730Sstevel@tonic-gate 	(void) di_walk_node(root_node, DI_WALK_CLDFIRST, devlink_hdl,
2744453Scth 	    dump_devs);
2750Sstevel@tonic-gate 
2760Sstevel@tonic-gate 	if (devlink_hdl != NULL)
2770Sstevel@tonic-gate 		(void) di_devlink_fini(&devlink_hdl);
2780Sstevel@tonic-gate 	di_fini(root_node);
2790Sstevel@tonic-gate }
2800Sstevel@tonic-gate 
2810Sstevel@tonic-gate /*
2820Sstevel@tonic-gate  * utility routines
2830Sstevel@tonic-gate  */
2840Sstevel@tonic-gate static int
2850Sstevel@tonic-gate i_find_target_node(di_node_t node, void *arg)
2860Sstevel@tonic-gate {
2870Sstevel@tonic-gate 	di_node_t *target = (di_node_t *)arg;
2880Sstevel@tonic-gate 
2890Sstevel@tonic-gate 	if (opts.o_devices_path != NULL) {
2900Sstevel@tonic-gate 		char *path;
2910Sstevel@tonic-gate 
2920Sstevel@tonic-gate 		if ((path = di_devfs_path(node)) == NULL)
2930Sstevel@tonic-gate 			exit(_error("failed to allocate memory"));
2940Sstevel@tonic-gate 
2950Sstevel@tonic-gate 		if (strcmp(opts.o_devices_path, path) == 0) {
2960Sstevel@tonic-gate 			di_devfs_path_free(path);
2970Sstevel@tonic-gate 			*target = node;
2980Sstevel@tonic-gate 			return (DI_WALK_TERMINATE);
2990Sstevel@tonic-gate 		}
3000Sstevel@tonic-gate 
3010Sstevel@tonic-gate 		di_devfs_path_free(path);
3020Sstevel@tonic-gate 	} else if (opts.o_devt != DDI_DEV_T_NONE) {
3030Sstevel@tonic-gate 		di_minor_t	minor = DI_MINOR_NIL;
3040Sstevel@tonic-gate 
3050Sstevel@tonic-gate 		while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
3060Sstevel@tonic-gate 			if (opts.o_devt == di_minor_devt(minor)) {
3070Sstevel@tonic-gate 				*target = node;
3080Sstevel@tonic-gate 				return (DI_WALK_TERMINATE);
3090Sstevel@tonic-gate 			}
3100Sstevel@tonic-gate 		}
3110Sstevel@tonic-gate 	} else {
3120Sstevel@tonic-gate 		/* we should never get here */
3130Sstevel@tonic-gate 		exit(_error(NULL, "internal error"));
3140Sstevel@tonic-gate 	}
3150Sstevel@tonic-gate 	return (DI_WALK_CONTINUE);
3160Sstevel@tonic-gate }
3170Sstevel@tonic-gate 
3180Sstevel@tonic-gate static di_node_t
3190Sstevel@tonic-gate find_target_node(di_node_t root_node)
3200Sstevel@tonic-gate {
3210Sstevel@tonic-gate 	di_node_t target = DI_NODE_NIL;
3220Sstevel@tonic-gate 
3230Sstevel@tonic-gate 	/* special case to allow displaying of the root node */
3240Sstevel@tonic-gate 	if (opts.o_devices_path != NULL) {
3250Sstevel@tonic-gate 		if (strlen(opts.o_devices_path) == 0)
3260Sstevel@tonic-gate 			return (root_node);
3270Sstevel@tonic-gate 		if (strcmp(opts.o_devices_path, ".") == 0)
3280Sstevel@tonic-gate 			return (root_node);
3290Sstevel@tonic-gate 	}
3300Sstevel@tonic-gate 
3310Sstevel@tonic-gate 	(void) di_walk_node(root_node, DI_WALK_CLDFIRST, &target,
3324453Scth 	    i_find_target_node);
3330Sstevel@tonic-gate 	return (target);
3340Sstevel@tonic-gate }
3350Sstevel@tonic-gate 
3360Sstevel@tonic-gate #define	NODE_DISPLAY		(1<<0)
3370Sstevel@tonic-gate 
3380Sstevel@tonic-gate static long
3390Sstevel@tonic-gate node_display(di_node_t node)
3400Sstevel@tonic-gate {
3410Sstevel@tonic-gate 	long data = (long)di_node_private_get(node);
3420Sstevel@tonic-gate 	return (data & NODE_DISPLAY);
3430Sstevel@tonic-gate }
3440Sstevel@tonic-gate 
3450Sstevel@tonic-gate static void
3460Sstevel@tonic-gate node_display_set(di_node_t node)
3470Sstevel@tonic-gate {
3480Sstevel@tonic-gate 	long data = (long)di_node_private_get(node);
3490Sstevel@tonic-gate 	data |= NODE_DISPLAY;
3500Sstevel@tonic-gate 	di_node_private_set(node, (void *)data);
3510Sstevel@tonic-gate }
3520Sstevel@tonic-gate 
3530Sstevel@tonic-gate #define	LNODE_DISPLAYED		(1<<0)
3540Sstevel@tonic-gate 
3550Sstevel@tonic-gate static long
3560Sstevel@tonic-gate lnode_displayed(di_lnode_t lnode)
3570Sstevel@tonic-gate {
3580Sstevel@tonic-gate 	long data = (long)di_lnode_private_get(lnode);
3590Sstevel@tonic-gate 	return (data & LNODE_DISPLAYED);
3600Sstevel@tonic-gate }
3610Sstevel@tonic-gate 
3620Sstevel@tonic-gate static void
3630Sstevel@tonic-gate lnode_displayed_set(di_lnode_t lnode)
3640Sstevel@tonic-gate {
3650Sstevel@tonic-gate 	long data = (long)di_lnode_private_get(lnode);
3660Sstevel@tonic-gate 	data |= LNODE_DISPLAYED;
3670Sstevel@tonic-gate 	di_lnode_private_set(lnode, (void *)data);
3680Sstevel@tonic-gate }
3690Sstevel@tonic-gate 
3700Sstevel@tonic-gate static void
3710Sstevel@tonic-gate lnode_displayed_clear(di_lnode_t lnode)
3720Sstevel@tonic-gate {
3730Sstevel@tonic-gate 	long data = (long)di_lnode_private_get(lnode);
3740Sstevel@tonic-gate 	data &= ~LNODE_DISPLAYED;
3750Sstevel@tonic-gate 	di_lnode_private_set(lnode, (void *)data);
3760Sstevel@tonic-gate }
3770Sstevel@tonic-gate 
3780Sstevel@tonic-gate #define	MINOR_DISPLAYED		(1<<0)
3790Sstevel@tonic-gate #define	MINOR_PTR		(~(0x3))
3800Sstevel@tonic-gate 
3810Sstevel@tonic-gate static long
3820Sstevel@tonic-gate minor_displayed(di_minor_t minor)
3830Sstevel@tonic-gate {
3840Sstevel@tonic-gate 	long data = (long)di_minor_private_get(minor);
3850Sstevel@tonic-gate 	return (data & MINOR_DISPLAYED);
3860Sstevel@tonic-gate }
3870Sstevel@tonic-gate 
3880Sstevel@tonic-gate static void
3890Sstevel@tonic-gate minor_displayed_set(di_minor_t minor)
3900Sstevel@tonic-gate {
3910Sstevel@tonic-gate 	long data = (long)di_minor_private_get(minor);
3920Sstevel@tonic-gate 	data |= MINOR_DISPLAYED;
3930Sstevel@tonic-gate 	di_minor_private_set(minor, (void *)data);
3940Sstevel@tonic-gate }
3950Sstevel@tonic-gate 
3960Sstevel@tonic-gate static void
3970Sstevel@tonic-gate minor_displayed_clear(di_minor_t minor)
3980Sstevel@tonic-gate {
3990Sstevel@tonic-gate 	long data = (long)di_minor_private_get(minor);
4000Sstevel@tonic-gate 	data &= ~MINOR_DISPLAYED;
4010Sstevel@tonic-gate 	di_minor_private_set(minor, (void *)data);
4020Sstevel@tonic-gate }
4030Sstevel@tonic-gate 
4040Sstevel@tonic-gate static void *
4050Sstevel@tonic-gate minor_ptr(di_minor_t minor)
4060Sstevel@tonic-gate {
4070Sstevel@tonic-gate 	long data = (long)di_minor_private_get(minor);
4080Sstevel@tonic-gate 	return ((void *)(data & MINOR_PTR));
4090Sstevel@tonic-gate }
4100Sstevel@tonic-gate 
4110Sstevel@tonic-gate static void
4120Sstevel@tonic-gate minor_ptr_set(di_minor_t minor, void *ptr)
4130Sstevel@tonic-gate {
4140Sstevel@tonic-gate 	long data = (long)di_minor_private_get(minor);
4150Sstevel@tonic-gate 	data = (data & ~MINOR_PTR) | (((long)ptr) & MINOR_PTR);
4160Sstevel@tonic-gate 	di_minor_private_set(minor, (void *)data);
4170Sstevel@tonic-gate }
4180Sstevel@tonic-gate 
4190Sstevel@tonic-gate /*
4200Sstevel@tonic-gate  * In this comment typed properties are those of type DI_PROP_TYPE_UNDEF_IT,
4210Sstevel@tonic-gate  * DI_PROP_TYPE_BOOLEAN, DI_PROP_TYPE_INT, DI_PROP_TYPE_INT64,
4220Sstevel@tonic-gate  * DI_PROP_TYPE_BYTE, and DI_PROP_TYPE_STRING.
4230Sstevel@tonic-gate  *
4240Sstevel@tonic-gate  * The guessing algorithm is:
4250Sstevel@tonic-gate  * 1. If the property is typed and the type is consistent with the value of
4260Sstevel@tonic-gate  *    the property, then the property is of that type. If the type is not
4270Sstevel@tonic-gate  *    consistent with value of the property, then the type is treated as
4280Sstevel@tonic-gate  *    alien to prtconf.
4290Sstevel@tonic-gate  * 2. If the property is of type DI_PROP_TYPE_UNKNOWN the following steps
4300Sstevel@tonic-gate  *    are carried out.
4310Sstevel@tonic-gate  *    a. If the value of the property is consistent with a string property,
4320Sstevel@tonic-gate  *       the type of the property is DI_PROP_TYPE_STRING.
4330Sstevel@tonic-gate  *    b. Otherwise, if the value of the property is consistent with an integer
4340Sstevel@tonic-gate  *       property, the type of the property is DI_PROP_TYPE_INT.
4350Sstevel@tonic-gate  *    c. Otherwise, the property type is treated as alien to prtconf.
4360Sstevel@tonic-gate  * 3. If the property type is alien to prtconf, then the property value is
4370Sstevel@tonic-gate  *    read by the appropriate routine for untyped properties and the following
4380Sstevel@tonic-gate  *    steps are carried out.
4390Sstevel@tonic-gate  *    a. If the length that the property routine returned is zero, the
4400Sstevel@tonic-gate  *       property is of type DI_PROP_TYPE_BOOLEAN.
4410Sstevel@tonic-gate  *    b. Otherwise, if the length that the property routine returned is
4420Sstevel@tonic-gate  *       positive, then the property value is treated as raw data of type
4430Sstevel@tonic-gate  *       DI_PROP_TYPE_UNKNOWN.
4440Sstevel@tonic-gate  *    c. Otherwise, if the length that the property routine returned is
4450Sstevel@tonic-gate  *       negative, then there is some internal inconsistency and this is
4460Sstevel@tonic-gate  *       treated as an error and no type is determined.
4470Sstevel@tonic-gate  */
4480Sstevel@tonic-gate static int
4490Sstevel@tonic-gate prop_type_guess(const dumpops_t *propops, void *prop, void **prop_data,
4500Sstevel@tonic-gate     int *prop_type)
4510Sstevel@tonic-gate {
4520Sstevel@tonic-gate 	int len, type;
4530Sstevel@tonic-gate 
4540Sstevel@tonic-gate 	type = PROPTYPE(propops)(prop);
4550Sstevel@tonic-gate 	switch (type) {
4560Sstevel@tonic-gate 	case DI_PROP_TYPE_UNDEF_IT:
4570Sstevel@tonic-gate 	case DI_PROP_TYPE_BOOLEAN:
4580Sstevel@tonic-gate 		*prop_data = NULL;
4590Sstevel@tonic-gate 		*prop_type = type;
4600Sstevel@tonic-gate 		return (0);
4610Sstevel@tonic-gate 	case DI_PROP_TYPE_INT:
4620Sstevel@tonic-gate 		len = PROPINTS(propops)(prop, (int **)prop_data);
4630Sstevel@tonic-gate 		break;
4640Sstevel@tonic-gate 	case DI_PROP_TYPE_INT64:
4650Sstevel@tonic-gate 		len = PROPINT64(propops)(prop, (int64_t **)prop_data);
4660Sstevel@tonic-gate 		break;
4670Sstevel@tonic-gate 	case DI_PROP_TYPE_BYTE:
4680Sstevel@tonic-gate 		len = PROPBYTES(propops)(prop, (uchar_t **)prop_data);
4690Sstevel@tonic-gate 		break;
4700Sstevel@tonic-gate 	case DI_PROP_TYPE_STRING:
4710Sstevel@tonic-gate 		len = PROPSTRINGS(propops)(prop, (char **)prop_data);
4720Sstevel@tonic-gate 		break;
4730Sstevel@tonic-gate 	case DI_PROP_TYPE_UNKNOWN:
4740Sstevel@tonic-gate 		len = PROPSTRINGS(propops)(prop, (char **)prop_data);
4750Sstevel@tonic-gate 		if ((len > 0) && ((*(char **)prop_data)[0] != 0)) {
4760Sstevel@tonic-gate 			*prop_type = DI_PROP_TYPE_STRING;
4770Sstevel@tonic-gate 			return (len);
4780Sstevel@tonic-gate 		}
4790Sstevel@tonic-gate 
4800Sstevel@tonic-gate 		len = PROPINTS(propops)(prop, (int **)prop_data);
4810Sstevel@tonic-gate 		type = DI_PROP_TYPE_INT;
4820Sstevel@tonic-gate 
4830Sstevel@tonic-gate 		break;
4840Sstevel@tonic-gate 	default:
4850Sstevel@tonic-gate 		len = -1;
4860Sstevel@tonic-gate 	}
4870Sstevel@tonic-gate 
4880Sstevel@tonic-gate 	if (len > 0) {
4890Sstevel@tonic-gate 		*prop_type = type;
4900Sstevel@tonic-gate 		return (len);
4910Sstevel@tonic-gate 	}
4920Sstevel@tonic-gate 
4930Sstevel@tonic-gate 	len = PROPRAWDATA(propops)(prop, (uchar_t **)prop_data);
4940Sstevel@tonic-gate 	if (len < 0) {
4950Sstevel@tonic-gate 		return (-1);
4960Sstevel@tonic-gate 	} else if (len == 0) {
4970Sstevel@tonic-gate 		*prop_type = DI_PROP_TYPE_BOOLEAN;
4980Sstevel@tonic-gate 		return (0);
4990Sstevel@tonic-gate 	}
5000Sstevel@tonic-gate 
5010Sstevel@tonic-gate 	*prop_type = DI_PROP_TYPE_UNKNOWN;
5020Sstevel@tonic-gate 	return (len);
5030Sstevel@tonic-gate }
5040Sstevel@tonic-gate 
5057224Scth /*
5067224Scth  * Returns 0 if nothing is printed, 1 otherwise
5077224Scth  */
5087224Scth static int
5097224Scth dump_prop_list(const dumpops_t *dumpops, const char *name, int ilev,
510*7261Scth     void *node, dev_t dev, int *compat_printed)
5110Sstevel@tonic-gate {
5127224Scth 	void		*prop = DI_PROP_NIL, *prop_data;
5137224Scth 	di_minor_t	minor;
5147224Scth 	char		*p;
5157224Scth 	int		i, prop_type, nitems;
5167224Scth 	dev_t		pdev;
5177224Scth 	int		nprop = 0;
5180Sstevel@tonic-gate 
519*7261Scth 	if (compat_printed)
520*7261Scth 		*compat_printed = 0;
521*7261Scth 
5220Sstevel@tonic-gate 	while ((prop = NEXTPROP(dumpops)(node, prop)) != DI_PROP_NIL) {
5237224Scth 
5247224Scth 		/* Skip properties a dev_t oriented caller is not requesting */
5257224Scth 		if (PROPDEVT(dumpops)) {
5267224Scth 			pdev = PROPDEVT(dumpops)(prop);
5277224Scth 
5287224Scth 			if (dev == DDI_DEV_T_ANY) {
5297224Scth 				/*
5307224Scth 				 * Caller requesting print all properties
5317224Scth 				 */
5327224Scth 				goto print;
5337224Scth 			} else if (dev == DDI_DEV_T_NONE) {
5347224Scth 				/*
5357224Scth 				 * Caller requesting print of properties
5367224Scth 				 * associated with devinfo (not minor).
5377224Scth 				 */
5387224Scth 				if ((pdev == DDI_DEV_T_ANY) ||
5397224Scth 				    (pdev == DDI_DEV_T_NONE))
5407224Scth 					goto print;
5417224Scth 
5427224Scth 				/*
5437224Scth 				 * Property has a minor association, see if
5447224Scth 				 * we have a minor with this dev_t. If there
5457224Scth 				 * is no such minor we print the property now
5467224Scth 				 * so it gets displayed.
5477224Scth 				 */
5487224Scth 				minor = DI_MINOR_NIL;
5497224Scth 				while ((minor = di_minor_next((di_node_t)node,
5507224Scth 				    minor)) != DI_MINOR_NIL) {
5517224Scth 					if (di_minor_devt(minor) == pdev)
5527224Scth 						break;
5537224Scth 				}
5547224Scth 				if (minor == DI_MINOR_NIL)
5557224Scth 					goto print;
5567224Scth 			} else if (dev == pdev) {
5577224Scth 				/*
5587224Scth 				 * Caller requesting print of properties
5597224Scth 				 * associated with a specific matching minor
5607224Scth 				 * node.
5617224Scth 				 */
5627224Scth 				goto print;
5637224Scth 			}
5647224Scth 
5657224Scth 			/* otherwise skip print */
5667224Scth 			continue;
5677224Scth 		}
5687224Scth 
5697224Scth print:		nitems = prop_type_guess(dumpops, prop, &prop_data, &prop_type);
5700Sstevel@tonic-gate 		if (nitems < 0)
5710Sstevel@tonic-gate 			continue;
5720Sstevel@tonic-gate 
5737224Scth 		if (nprop == 0) {
5747224Scth 			if (name) {
5757224Scth 				indent_to_level(ilev);
5767224Scth 				(void) printf("%s properties:\n", name);
5777224Scth 			}
5787224Scth 			ilev++;
5797224Scth 		}
5807224Scth 		nprop++;
5817224Scth 
5820Sstevel@tonic-gate 		indent_to_level(ilev);
5830Sstevel@tonic-gate 		(void) printf("name='%s' type=", PROPNAME(dumpops)(prop));
5840Sstevel@tonic-gate 
585*7261Scth 		/* report 'compatible' as processed */
586*7261Scth 		if (compat_printed &&
587*7261Scth 		    (strcmp(PROPNAME(dumpops)(prop), "compatible") == 0))
588*7261Scth 			*compat_printed = 1;
589*7261Scth 
5900Sstevel@tonic-gate 		switch (prop_type) {
5910Sstevel@tonic-gate 		case DI_PROP_TYPE_UNDEF_IT:
5920Sstevel@tonic-gate 			(void) printf("undef");
5930Sstevel@tonic-gate 			break;
5940Sstevel@tonic-gate 		case DI_PROP_TYPE_BOOLEAN:
5950Sstevel@tonic-gate 			(void) printf("boolean");
5960Sstevel@tonic-gate 			break;
5970Sstevel@tonic-gate 		case DI_PROP_TYPE_INT:
5980Sstevel@tonic-gate 			(void) printf("int");
5990Sstevel@tonic-gate 			break;
6000Sstevel@tonic-gate 		case DI_PROP_TYPE_INT64:
6010Sstevel@tonic-gate 			(void) printf("int64");
6020Sstevel@tonic-gate 			break;
6030Sstevel@tonic-gate 		case DI_PROP_TYPE_BYTE:
6040Sstevel@tonic-gate 			(void) printf("byte");
6050Sstevel@tonic-gate 			break;
6060Sstevel@tonic-gate 		case DI_PROP_TYPE_STRING:
6070Sstevel@tonic-gate 			(void) printf("string");
6080Sstevel@tonic-gate 			break;
6090Sstevel@tonic-gate 		case DI_PROP_TYPE_UNKNOWN:
6100Sstevel@tonic-gate 			(void) printf("unknown");
6110Sstevel@tonic-gate 			break;
6120Sstevel@tonic-gate 		default:
6130Sstevel@tonic-gate 			/* Should never be here */
6140Sstevel@tonic-gate 			(void) printf("0x%x", prop_type);
6150Sstevel@tonic-gate 		}
6160Sstevel@tonic-gate 
6170Sstevel@tonic-gate 		if (nitems != 0)
6180Sstevel@tonic-gate 			(void) printf(" items=%i", nitems);
6190Sstevel@tonic-gate 
6200Sstevel@tonic-gate 		/* print the major and minor numbers for a device property */
6217224Scth 		if (PROPDEVT(dumpops)) {
6227224Scth 			if ((pdev == DDI_DEV_T_NONE) ||
6237224Scth 			    (pdev == DDI_DEV_T_ANY)) {
6247224Scth 				(void) printf(" dev=none");
6257224Scth 			} else {
6260Sstevel@tonic-gate 				(void) printf(" dev=(%u,%u)",
6277224Scth 				    (uint_t)major(pdev), (uint_t)minor(pdev));
6280Sstevel@tonic-gate 			}
6290Sstevel@tonic-gate 		}
6300Sstevel@tonic-gate 
6310Sstevel@tonic-gate 		(void) putchar('\n');
6320Sstevel@tonic-gate 
6330Sstevel@tonic-gate 		if (nitems == 0)
6340Sstevel@tonic-gate 			continue;
6350Sstevel@tonic-gate 
6360Sstevel@tonic-gate 		indent_to_level(ilev);
6370Sstevel@tonic-gate 
6380Sstevel@tonic-gate 		(void) printf("    value=");
6390Sstevel@tonic-gate 
6400Sstevel@tonic-gate 		switch (prop_type) {
6410Sstevel@tonic-gate 		case DI_PROP_TYPE_INT:
6420Sstevel@tonic-gate 			for (i = 0; i < nitems - 1; i++)
6430Sstevel@tonic-gate 				(void) printf("%8.8x.", ((int *)prop_data)[i]);
6440Sstevel@tonic-gate 			(void) printf("%8.8x", ((int *)prop_data)[i]);
6450Sstevel@tonic-gate 			break;
6460Sstevel@tonic-gate 		case DI_PROP_TYPE_INT64:
6470Sstevel@tonic-gate 			for (i = 0; i < nitems - 1; i++)
6480Sstevel@tonic-gate 				(void) printf("%16.16llx.",
6490Sstevel@tonic-gate 				    ((long long *)prop_data)[i]);
6500Sstevel@tonic-gate 			(void) printf("%16.16llx", ((long long *)prop_data)[i]);
6510Sstevel@tonic-gate 			break;
6520Sstevel@tonic-gate 		case DI_PROP_TYPE_STRING:
6530Sstevel@tonic-gate 			p = (char *)prop_data;
6540Sstevel@tonic-gate 			for (i = 0; i < nitems - 1; i++) {
6550Sstevel@tonic-gate 				(void) printf("'%s' + ", p);
6560Sstevel@tonic-gate 				p += strlen(p) + 1;
6570Sstevel@tonic-gate 			}
6580Sstevel@tonic-gate 			(void) printf("'%s'", p);
6590Sstevel@tonic-gate 			break;
6600Sstevel@tonic-gate 		default:
6610Sstevel@tonic-gate 			for (i = 0; i < nitems - 1; i++)
6620Sstevel@tonic-gate 				(void) printf("%2.2x.",
6630Sstevel@tonic-gate 				    ((uint8_t *)prop_data)[i]);
6640Sstevel@tonic-gate 			(void) printf("%2.2x", ((uint8_t *)prop_data)[i]);
6650Sstevel@tonic-gate 		}
6660Sstevel@tonic-gate 
6670Sstevel@tonic-gate 		(void) putchar('\n');
6680Sstevel@tonic-gate 	}
6697224Scth 
6707224Scth 	return (nprop ? 1 : 0);
6710Sstevel@tonic-gate }
6720Sstevel@tonic-gate 
6730Sstevel@tonic-gate /*
6740Sstevel@tonic-gate  * walk_driver is a debugging facility.
6750Sstevel@tonic-gate  */
6760Sstevel@tonic-gate static void
6770Sstevel@tonic-gate walk_driver(di_node_t root, di_devlink_handle_t devlink_hdl)
6780Sstevel@tonic-gate {
6790Sstevel@tonic-gate 	di_node_t node;
6800Sstevel@tonic-gate 
6810Sstevel@tonic-gate 	node = di_drv_first_node(dbg.d_drivername, root);
6820Sstevel@tonic-gate 
6830Sstevel@tonic-gate 	while (node != DI_NODE_NIL) {
6840Sstevel@tonic-gate 		(void) dump_devs(node, devlink_hdl);
6850Sstevel@tonic-gate 		node = di_drv_next_node(node);
6860Sstevel@tonic-gate 	}
6870Sstevel@tonic-gate }
6880Sstevel@tonic-gate 
6890Sstevel@tonic-gate /*
6900Sstevel@tonic-gate  * print out information about this node, returns appropriate code.
6910Sstevel@tonic-gate  */
6920Sstevel@tonic-gate /*ARGSUSED1*/
6930Sstevel@tonic-gate static int
6940Sstevel@tonic-gate dump_devs(di_node_t node, void *arg)
6950Sstevel@tonic-gate {
6960Sstevel@tonic-gate 	di_devlink_handle_t	devlink_hdl = (di_devlink_handle_t)arg;
6970Sstevel@tonic-gate 	int			ilev = 0;	/* indentation level */
6980Sstevel@tonic-gate 	char			*driver_name;
6990Sstevel@tonic-gate 	di_node_t		root_node, tmp;
700*7261Scth 	int			compat_printed;
701*7261Scth 	int			printed;
7020Sstevel@tonic-gate 
7030Sstevel@tonic-gate 	if (dbg.d_debug) {
7040Sstevel@tonic-gate 		char *path = di_devfs_path(node);
7050Sstevel@tonic-gate 		dprintf("Dump node %s\n", path);
7060Sstevel@tonic-gate 		di_devfs_path_free(path);
7070Sstevel@tonic-gate 	}
7080Sstevel@tonic-gate 
7090Sstevel@tonic-gate 	if (dbg.d_bydriver) {
7100Sstevel@tonic-gate 		ilev = 1;
7110Sstevel@tonic-gate 	} else {
7120Sstevel@tonic-gate 		/* figure out indentation level */
7130Sstevel@tonic-gate 		tmp = node;
7140Sstevel@tonic-gate 		while ((tmp = di_parent_node(tmp)) != DI_NODE_NIL)
7150Sstevel@tonic-gate 			ilev++;
7160Sstevel@tonic-gate 
7170Sstevel@tonic-gate 		if (opts.o_target && !opts.o_ancestors) {
7180Sstevel@tonic-gate 			ilev -= opts.o_target - 1;
7190Sstevel@tonic-gate 		}
7200Sstevel@tonic-gate 	}
7210Sstevel@tonic-gate 
7220Sstevel@tonic-gate 	if (opts.o_target && !node_display(node)) {
7230Sstevel@tonic-gate 		/*
7240Sstevel@tonic-gate 		 * if we're only displaying certain nodes and this one
7250Sstevel@tonic-gate 		 * isn't flagged, skip it.
7260Sstevel@tonic-gate 		 */
7270Sstevel@tonic-gate 		return (DI_WALK_CONTINUE);
7280Sstevel@tonic-gate 	}
7290Sstevel@tonic-gate 
7300Sstevel@tonic-gate 	indent_to_level(ilev);
7310Sstevel@tonic-gate 
7320Sstevel@tonic-gate 	(void) printf("%s", di_node_name(node));
7330Sstevel@tonic-gate 
7340Sstevel@tonic-gate 	/*
7350Sstevel@tonic-gate 	 * if this node does not have an instance number or is the
7360Sstevel@tonic-gate 	 * root node (1229946), we don't print an instance number
7370Sstevel@tonic-gate 	 */
7380Sstevel@tonic-gate 	root_node = tmp = node;
7390Sstevel@tonic-gate 	while ((tmp = di_parent_node(tmp)) != DI_NODE_NIL)
7400Sstevel@tonic-gate 		root_node = tmp;
7410Sstevel@tonic-gate 	if ((di_instance(node) >= 0) && (node != root_node))
7420Sstevel@tonic-gate 		(void) printf(", instance #%d", di_instance(node));
7430Sstevel@tonic-gate 
7440Sstevel@tonic-gate 	if (opts.o_drv_name) {
7450Sstevel@tonic-gate 		driver_name = di_driver_name(node);
7460Sstevel@tonic-gate 		if (driver_name != NULL)
7470Sstevel@tonic-gate 			(void) printf(" (driver name: %s)", driver_name);
7484845Svikram 	} else if (di_retired(node)) {
7494845Svikram 		(void) printf(" (retired)");
7500Sstevel@tonic-gate 	} else if (di_state(node) & DI_DRIVER_DETACHED)
7510Sstevel@tonic-gate 		(void) printf(" (driver not attached)");
7520Sstevel@tonic-gate 
7530Sstevel@tonic-gate 	(void) printf("\n");
7540Sstevel@tonic-gate 
7550Sstevel@tonic-gate 	if (opts.o_verbose)  {
7560Sstevel@tonic-gate 		if (dump_prop_list(&sysprop_dumpops, "System", ilev + 1,
757*7261Scth 		    node, DDI_DEV_T_ANY, NULL)) {
7580Sstevel@tonic-gate 			(void) dump_prop_list(&globprop_dumpops, NULL, ilev + 1,
759*7261Scth 			    node, DDI_DEV_T_ANY, NULL);
7600Sstevel@tonic-gate 		} else {
7610Sstevel@tonic-gate 			(void) dump_prop_list(&globprop_dumpops,
762*7261Scth 			    "System software", ilev + 1,
763*7261Scth 			    node, DDI_DEV_T_ANY, NULL);
7640Sstevel@tonic-gate 		}
7650Sstevel@tonic-gate 		(void) dump_prop_list(&drvprop_dumpops, "Driver", ilev + 1,
766*7261Scth 		    node, DDI_DEV_T_NONE, NULL);
767*7261Scth 
768*7261Scth 		printed = dump_prop_list(&hwprop_dumpops, "Hardware",
769*7261Scth 		    ilev + 1, node, DDI_DEV_T_ANY, &compat_printed);
770*7261Scth 
771*7261Scth 		/* Ensure that 'compatible' is printed under Hardware header */
772*7261Scth 		if (!compat_printed)
773*7261Scth 			(void) dump_compatible(printed ? NULL : "Hardware",
774*7261Scth 			    ilev + 1, node);
775*7261Scth 
7760Sstevel@tonic-gate 		dump_priv_data(ilev + 1, node);
7770Sstevel@tonic-gate 		dump_pathing_data(ilev + 1, node);
7780Sstevel@tonic-gate 		dump_link_data(ilev + 1, node, devlink_hdl);
7790Sstevel@tonic-gate 		dump_minor_data(ilev + 1, node, devlink_hdl);
7800Sstevel@tonic-gate 	}
7810Sstevel@tonic-gate 
7820Sstevel@tonic-gate 	if (opts.o_target)
7830Sstevel@tonic-gate 		return (DI_WALK_CONTINUE);
7840Sstevel@tonic-gate 
7850Sstevel@tonic-gate 	if (!opts.o_pseudodevs && (strcmp(di_node_name(node), "pseudo") == 0))
7860Sstevel@tonic-gate 		return (DI_WALK_PRUNECHILD);
7870Sstevel@tonic-gate 
7880Sstevel@tonic-gate 	return (DI_WALK_CONTINUE);
7890Sstevel@tonic-gate }
7900Sstevel@tonic-gate 
7910Sstevel@tonic-gate /* _error([no_perror, ] fmt [, arg ...]) */
7920Sstevel@tonic-gate static int
7930Sstevel@tonic-gate _error(const char *opt_noperror, ...)
7940Sstevel@tonic-gate {
7950Sstevel@tonic-gate 	int saved_errno;
7960Sstevel@tonic-gate 	va_list ap;
7970Sstevel@tonic-gate 	int no_perror = 0;
7980Sstevel@tonic-gate 	const char *fmt;
7990Sstevel@tonic-gate 
8000Sstevel@tonic-gate 	saved_errno = errno;
8010Sstevel@tonic-gate 
8020Sstevel@tonic-gate 	(void) fprintf(stderr, "%s: ", opts.o_progname);
8030Sstevel@tonic-gate 
8040Sstevel@tonic-gate 	va_start(ap, opt_noperror);
8050Sstevel@tonic-gate 	if (opt_noperror == NULL) {
8060Sstevel@tonic-gate 		no_perror = 1;
8070Sstevel@tonic-gate 		fmt = va_arg(ap, char *);
8080Sstevel@tonic-gate 	} else
8090Sstevel@tonic-gate 		fmt = opt_noperror;
8100Sstevel@tonic-gate 	(void) vfprintf(stderr, fmt, ap);
8110Sstevel@tonic-gate 	va_end(ap);
8120Sstevel@tonic-gate 
8130Sstevel@tonic-gate 	if (no_perror)
8140Sstevel@tonic-gate 		(void) fprintf(stderr, "\n");
8150Sstevel@tonic-gate 	else {
8160Sstevel@tonic-gate 		(void) fprintf(stderr, ": ");
8170Sstevel@tonic-gate 		errno = saved_errno;
8180Sstevel@tonic-gate 		perror("");
8190Sstevel@tonic-gate 	}
8200Sstevel@tonic-gate 
8210Sstevel@tonic-gate 	return (-1);
8220Sstevel@tonic-gate }
8230Sstevel@tonic-gate 
8240Sstevel@tonic-gate 
8250Sstevel@tonic-gate /*
8260Sstevel@tonic-gate  * The rest of the routines handle printing the raw prom devinfo (-p option).
8270Sstevel@tonic-gate  *
8280Sstevel@tonic-gate  * 128 is the size of the largest (currently) property name
8290Sstevel@tonic-gate  * 16k - MAXNAMESZ - sizeof (int) is the size of the largest
8300Sstevel@tonic-gate  * (currently) property value that is allowed.
8310Sstevel@tonic-gate  * the sizeof (uint_t) is from struct openpromio
8320Sstevel@tonic-gate  */
8330Sstevel@tonic-gate 
8340Sstevel@tonic-gate #define	MAXNAMESZ	128
8350Sstevel@tonic-gate #define	MAXVALSIZE	(16384 - MAXNAMESZ - sizeof (uint_t))
8360Sstevel@tonic-gate #define	BUFSIZE		(MAXNAMESZ + MAXVALSIZE + sizeof (uint_t))
8370Sstevel@tonic-gate typedef union {
8380Sstevel@tonic-gate 	char buf[BUFSIZE];
8390Sstevel@tonic-gate 	struct openpromio opp;
8400Sstevel@tonic-gate } Oppbuf;
8410Sstevel@tonic-gate 
8420Sstevel@tonic-gate static int prom_fd;
8430Sstevel@tonic-gate static uchar_t *prom_snapshot;
8440Sstevel@tonic-gate 
8450Sstevel@tonic-gate static int
8460Sstevel@tonic-gate is_openprom(void)
8470Sstevel@tonic-gate {
8480Sstevel@tonic-gate 	Oppbuf	oppbuf;
8490Sstevel@tonic-gate 	struct openpromio *opp = &(oppbuf.opp);
8500Sstevel@tonic-gate 	unsigned int i;
8510Sstevel@tonic-gate 
8520Sstevel@tonic-gate 	opp->oprom_size = MAXVALSIZE;
8530Sstevel@tonic-gate 	if (ioctl(prom_fd, OPROMGETCONS, opp) < 0)
8540Sstevel@tonic-gate 		exit(_error("OPROMGETCONS"));
8550Sstevel@tonic-gate 
8560Sstevel@tonic-gate 	i = (unsigned int)((unsigned char)opp->oprom_array[0]);
8570Sstevel@tonic-gate 	return ((i & OPROMCONS_OPENPROM) == OPROMCONS_OPENPROM);
8580Sstevel@tonic-gate }
8590Sstevel@tonic-gate 
8600Sstevel@tonic-gate int
8610Sstevel@tonic-gate do_prominfo(void)
8620Sstevel@tonic-gate {
8630Sstevel@tonic-gate 	uint_t arg = opts.o_verbose;
8640Sstevel@tonic-gate 
8650Sstevel@tonic-gate 	if (promopen(O_RDONLY))  {
8660Sstevel@tonic-gate 		exit(_error("openeepr device open failed"));
8670Sstevel@tonic-gate 	}
8680Sstevel@tonic-gate 
8690Sstevel@tonic-gate 	if (is_openprom() == 0)  {
8700Sstevel@tonic-gate 		(void) fprintf(stderr, "System architecture does not "
8710Sstevel@tonic-gate 		    "support this option of this command.\n");
8720Sstevel@tonic-gate 		return (1);
8730Sstevel@tonic-gate 	}
8740Sstevel@tonic-gate 
8750Sstevel@tonic-gate 	/* OPROMSNAPSHOT returns size in arg */
8760Sstevel@tonic-gate 	if (ioctl(prom_fd, OPROMSNAPSHOT, &arg) < 0)
8770Sstevel@tonic-gate 		exit(_error("OPROMSNAPSHOT"));
8780Sstevel@tonic-gate 
8790Sstevel@tonic-gate 	if (arg == 0)
8800Sstevel@tonic-gate 		return (1);
8810Sstevel@tonic-gate 
8820Sstevel@tonic-gate 	if ((prom_snapshot = malloc(arg)) == NULL)
8830Sstevel@tonic-gate 		exit(_error("failed to allocate memory"));
8840Sstevel@tonic-gate 
8850Sstevel@tonic-gate 	/* copy out the snapshot for printing */
8860Sstevel@tonic-gate 	/*LINTED*/
8870Sstevel@tonic-gate 	*(uint_t *)prom_snapshot = arg;
8880Sstevel@tonic-gate 	if (ioctl(prom_fd, OPROMCOPYOUT, prom_snapshot) < 0)
8890Sstevel@tonic-gate 		exit(_error("OPROMCOPYOUT"));
8900Sstevel@tonic-gate 
8910Sstevel@tonic-gate 	promclose();
8920Sstevel@tonic-gate 
8930Sstevel@tonic-gate 	/* print out information */
8940Sstevel@tonic-gate 	walk(prom_snapshot, arg, 0);
8950Sstevel@tonic-gate 	free(prom_snapshot);
8960Sstevel@tonic-gate 
8970Sstevel@tonic-gate 	return (0);
8980Sstevel@tonic-gate }
8990Sstevel@tonic-gate 
9000Sstevel@tonic-gate static void
9010Sstevel@tonic-gate walk(uchar_t *buf, uint_t size, int level)
9020Sstevel@tonic-gate {
9030Sstevel@tonic-gate 	int error;
9040Sstevel@tonic-gate 	nvlist_t *nvl, *cnvl;
9050Sstevel@tonic-gate 	nvpair_t *child = NULL;
9060Sstevel@tonic-gate 	uchar_t *cbuf = NULL;
9070Sstevel@tonic-gate 	uint_t csize;
9080Sstevel@tonic-gate 
9090Sstevel@tonic-gate 	/* Expand to an nvlist */
9100Sstevel@tonic-gate 	if (nvlist_unpack((char *)buf, size, &nvl, 0))
9110Sstevel@tonic-gate 		exit(_error("error processing snapshot"));
9120Sstevel@tonic-gate 
9130Sstevel@tonic-gate 	/* print current node */
9140Sstevel@tonic-gate 	dump_node(nvl, level);
9150Sstevel@tonic-gate 
9160Sstevel@tonic-gate 	/* print children */
9170Sstevel@tonic-gate 	error = nvlist_lookup_byte_array(nvl, "@child", &cbuf, &csize);
9180Sstevel@tonic-gate 	if ((error == ENOENT) || (cbuf == NULL))
9190Sstevel@tonic-gate 		return;		/* no child exists */
9200Sstevel@tonic-gate 
9210Sstevel@tonic-gate 	if (error || nvlist_unpack((char *)cbuf, csize, &cnvl, 0))
9220Sstevel@tonic-gate 		exit(_error("error processing snapshot"));
9230Sstevel@tonic-gate 
9240Sstevel@tonic-gate 	while (child = nvlist_next_nvpair(cnvl, child)) {
9250Sstevel@tonic-gate 		char *name = nvpair_name(child);
9260Sstevel@tonic-gate 		data_type_t type = nvpair_type(child);
9270Sstevel@tonic-gate 		uchar_t *nodebuf;
9280Sstevel@tonic-gate 		uint_t nodesize;
9290Sstevel@tonic-gate 		if (strcmp("node", name) != 0) {
9300Sstevel@tonic-gate 			dprintf("unexpected nvpair name %s != name\n", name);
9310Sstevel@tonic-gate 			continue;
9320Sstevel@tonic-gate 		}
9330Sstevel@tonic-gate 		if (type != DATA_TYPE_BYTE_ARRAY) {
9340Sstevel@tonic-gate 			dprintf("unexpected nvpair type %d, not byte array \n",
9350Sstevel@tonic-gate 			    type);
9360Sstevel@tonic-gate 			continue;
9370Sstevel@tonic-gate 		}
9380Sstevel@tonic-gate 
9390Sstevel@tonic-gate 		(void) nvpair_value_byte_array(child,
9400Sstevel@tonic-gate 		    (uchar_t **)&nodebuf, &nodesize);
9410Sstevel@tonic-gate 		walk(nodebuf, nodesize, level + 1);
9420Sstevel@tonic-gate 	}
9430Sstevel@tonic-gate 
9440Sstevel@tonic-gate 	nvlist_free(nvl);
9450Sstevel@tonic-gate }
9460Sstevel@tonic-gate 
9470Sstevel@tonic-gate /*
9480Sstevel@tonic-gate  * Print all properties and values
9490Sstevel@tonic-gate  */
9500Sstevel@tonic-gate static void
9510Sstevel@tonic-gate dump_node(nvlist_t *nvl, int level)
9520Sstevel@tonic-gate {
9530Sstevel@tonic-gate 	int id = 0;
9540Sstevel@tonic-gate 	char *name = NULL;
9550Sstevel@tonic-gate 	nvpair_t *nvp = NULL;
9560Sstevel@tonic-gate 
9570Sstevel@tonic-gate 	indent_to_level(level);
9580Sstevel@tonic-gate 	(void) printf("Node");
9590Sstevel@tonic-gate 	if (!opts.o_verbose) {
9600Sstevel@tonic-gate 		if (nvlist_lookup_string(nvl, "name", &name))
9610Sstevel@tonic-gate 			(void) printf("data not available");
9620Sstevel@tonic-gate 		else
9630Sstevel@tonic-gate 			(void) printf(" '%s'", name);
9640Sstevel@tonic-gate 		(void) putchar('\n');
9650Sstevel@tonic-gate 		return;
9660Sstevel@tonic-gate 	}
9670Sstevel@tonic-gate 	(void) nvlist_lookup_int32(nvl, "@nodeid", &id);
9680Sstevel@tonic-gate 	(void) printf(" %#08x\n", id);
9690Sstevel@tonic-gate 
9700Sstevel@tonic-gate 	while (nvp = nvlist_next_nvpair(nvl, nvp)) {
9710Sstevel@tonic-gate 		name = nvpair_name(nvp);
9720Sstevel@tonic-gate 		if (name[0] == '@')
9730Sstevel@tonic-gate 			continue;
9740Sstevel@tonic-gate 
9750Sstevel@tonic-gate 		print_one(nvp, level + 1);
9760Sstevel@tonic-gate 	}
9770Sstevel@tonic-gate 	(void) putchar('\n');
9780Sstevel@tonic-gate }
9790Sstevel@tonic-gate 
9800Sstevel@tonic-gate static const char *
9810Sstevel@tonic-gate path_state_name(di_path_state_t st)
9820Sstevel@tonic-gate {
9830Sstevel@tonic-gate 	switch (st) {
9840Sstevel@tonic-gate 		case DI_PATH_STATE_ONLINE:
9850Sstevel@tonic-gate 			return ("online");
9860Sstevel@tonic-gate 		case DI_PATH_STATE_STANDBY:
9870Sstevel@tonic-gate 			return ("standby");
9880Sstevel@tonic-gate 		case DI_PATH_STATE_OFFLINE:
9890Sstevel@tonic-gate 			return ("offline");
9900Sstevel@tonic-gate 		case DI_PATH_STATE_FAULT:
9910Sstevel@tonic-gate 			return ("faulted");
9920Sstevel@tonic-gate 	}
9930Sstevel@tonic-gate 	return ("unknown");
9940Sstevel@tonic-gate }
9950Sstevel@tonic-gate 
9960Sstevel@tonic-gate /*
9970Sstevel@tonic-gate  * Print all phci's each client is connected to.
9980Sstevel@tonic-gate  */
9990Sstevel@tonic-gate static void
10000Sstevel@tonic-gate dump_pathing_data(int ilev, di_node_t node)
10010Sstevel@tonic-gate {
10026640Scth 	di_path_t	pi = DI_PATH_NIL;
10036640Scth 	di_node_t	phci_node;
10046640Scth 	char		*phci_path;
10056640Scth 	int		path_instance;
10066640Scth 	int		firsttime = 1;
10070Sstevel@tonic-gate 
10080Sstevel@tonic-gate 	if (node == DI_PATH_NIL)
10090Sstevel@tonic-gate 		return;
10100Sstevel@tonic-gate 
10116640Scth 	while ((pi = di_path_client_next_path(node, pi)) != DI_PATH_NIL) {
10120Sstevel@tonic-gate 		if (firsttime) {
10130Sstevel@tonic-gate 			indent_to_level(ilev);
10140Sstevel@tonic-gate 			firsttime = 0;
10150Sstevel@tonic-gate 			ilev++;
10160Sstevel@tonic-gate 			(void) printf("Paths from multipath bus adapters:\n");
10170Sstevel@tonic-gate 		}
10180Sstevel@tonic-gate 
10196640Scth 		/*
10206640Scth 		 * Print the path instance and full "pathinfo" path, which is
10216640Scth 		 * the same as the /devices devifo path had the device been
10226640Scth 		 * enumerated under pHCI.
10236640Scth 		 */
10246640Scth 		phci_node = di_path_phci_node(pi);
10256640Scth 		phci_path = di_devfs_path(phci_node);
10266640Scth 		path_instance = di_path_instance(pi);
10276640Scth 		if (phci_path) {
10286640Scth 			if (path_instance > 0) {
10296640Scth 				indent_to_level(ilev);
10306640Scth 				(void) printf("Path %d: %s/%s@%s\n",
10316640Scth 				    path_instance, phci_path,
10326640Scth 				    di_node_name(node),
10336640Scth 				    di_path_bus_addr(pi));
10346640Scth 			}
10356640Scth 			di_devfs_path_free(phci_path);
10366640Scth 		}
10376640Scth 
10386640Scth 		/* print phci driver, instance, and path state information */
10390Sstevel@tonic-gate 		indent_to_level(ilev);
10400Sstevel@tonic-gate 		(void) printf("%s#%d (%s)\n", di_driver_name(phci_node),
10410Sstevel@tonic-gate 		    di_instance(phci_node), path_state_name(di_path_state(pi)));
10427224Scth 		(void) dump_prop_list(&pathprop_dumpops, NULL, ilev + 1,
1043*7261Scth 		    pi, DDI_DEV_T_ANY, NULL);
10440Sstevel@tonic-gate 	}
10450Sstevel@tonic-gate }
10460Sstevel@tonic-gate 
10470Sstevel@tonic-gate static int
10480Sstevel@tonic-gate dump_minor_data_links(di_devlink_t devlink, void *arg)
10490Sstevel@tonic-gate {
10500Sstevel@tonic-gate 	int ilev = (intptr_t)arg;
10510Sstevel@tonic-gate 	indent_to_level(ilev);
10520Sstevel@tonic-gate 	(void) printf("dev_link=%s\n", di_devlink_path(devlink));
10530Sstevel@tonic-gate 	return (DI_WALK_CONTINUE);
10540Sstevel@tonic-gate }
10550Sstevel@tonic-gate 
10560Sstevel@tonic-gate static void
10570Sstevel@tonic-gate dump_minor_data_paths(int ilev, di_minor_t minor,
10580Sstevel@tonic-gate     di_devlink_handle_t devlink_hdl)
10590Sstevel@tonic-gate {
10600Sstevel@tonic-gate 	char	*path, *type;
10610Sstevel@tonic-gate 	int	spec_type;
10620Sstevel@tonic-gate 
10630Sstevel@tonic-gate 	/* get the path to the device and the minor node name */
10640Sstevel@tonic-gate 	if ((path = di_devfs_minor_path(minor)) == NULL)
10650Sstevel@tonic-gate 		exit(_error("failed to allocate memory"));
10660Sstevel@tonic-gate 
10670Sstevel@tonic-gate 	/* display the path to this minor node */
10680Sstevel@tonic-gate 	indent_to_level(ilev);
10690Sstevel@tonic-gate 	(void) printf("dev_path=%s\n", path);
10700Sstevel@tonic-gate 
10710Sstevel@tonic-gate 	if (devlink_hdl != NULL) {
10720Sstevel@tonic-gate 
10730Sstevel@tonic-gate 		/* get the device minor node information */
10740Sstevel@tonic-gate 		spec_type = di_minor_spectype(minor);
10750Sstevel@tonic-gate 		switch (di_minor_type(minor)) {
10760Sstevel@tonic-gate 			case DDM_MINOR:
10770Sstevel@tonic-gate 				type = "minor";
10780Sstevel@tonic-gate 				break;
10790Sstevel@tonic-gate 			case DDM_ALIAS:
10800Sstevel@tonic-gate 				type = "alias";
10810Sstevel@tonic-gate 				break;
10820Sstevel@tonic-gate 			case DDM_DEFAULT:
10830Sstevel@tonic-gate 				type = "default";
10840Sstevel@tonic-gate 				break;
10850Sstevel@tonic-gate 			case DDM_INTERNAL_PATH:
10860Sstevel@tonic-gate 				type = "internal";
10870Sstevel@tonic-gate 				break;
10880Sstevel@tonic-gate 			default:
10890Sstevel@tonic-gate 				type = "unknown";
10900Sstevel@tonic-gate 				break;
10910Sstevel@tonic-gate 		}
10920Sstevel@tonic-gate 
10930Sstevel@tonic-gate 		/* display the device minor node information */
10940Sstevel@tonic-gate 		indent_to_level(ilev + 1);
10950Sstevel@tonic-gate 		(void) printf("spectype=%s type=%s\n",
10964453Scth 		    (spec_type == S_IFBLK) ? "blk" : "chr", type);
10970Sstevel@tonic-gate 
10980Sstevel@tonic-gate 		/* display all the devlinks for this device minor node */
10990Sstevel@tonic-gate 		(void) di_devlink_walk(devlink_hdl, NULL, path,
11000Sstevel@tonic-gate 		    0, (void *)(intptr_t)(ilev + 1), dump_minor_data_links);
11010Sstevel@tonic-gate 	}
11020Sstevel@tonic-gate 
11030Sstevel@tonic-gate 	di_devfs_path_free(path);
11040Sstevel@tonic-gate }
11050Sstevel@tonic-gate 
11060Sstevel@tonic-gate static void
11070Sstevel@tonic-gate create_minor_list(di_node_t node)
11080Sstevel@tonic-gate {
11090Sstevel@tonic-gate 	di_minor_t	minor, minor_head, minor_tail, minor_prev, minor_walk;
11100Sstevel@tonic-gate 	int		major;
11110Sstevel@tonic-gate 
11120Sstevel@tonic-gate 	/* if there are no minor nodes, bail */
11130Sstevel@tonic-gate 	if (di_minor_next(node, DI_MINOR_NIL) == DI_MINOR_NIL)
11140Sstevel@tonic-gate 		return;
11150Sstevel@tonic-gate 
11160Sstevel@tonic-gate 	/*
11170Sstevel@tonic-gate 	 * here we want to create lists of minor nodes with the same
11180Sstevel@tonic-gate 	 * dev_t.  to do this we first sort all the minor nodes by devt.
11190Sstevel@tonic-gate 	 *
11200Sstevel@tonic-gate 	 * the algorithm used here is a bubble sort, so performance sucks.
11210Sstevel@tonic-gate 	 * but it's probably ok here because most device instances don't
11220Sstevel@tonic-gate 	 * have that many minor nodes.  also we're doing this as we're
11230Sstevel@tonic-gate 	 * displaying each node so it doesn't look like we're pausing
11240Sstevel@tonic-gate 	 * output for a long time.
11250Sstevel@tonic-gate 	 */
11260Sstevel@tonic-gate 	major = di_driver_major(node);
11270Sstevel@tonic-gate 	minor_head = minor_tail = minor = DI_MINOR_NIL;
11280Sstevel@tonic-gate 	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
11290Sstevel@tonic-gate 		dev_t	dev = di_minor_devt(minor);
11300Sstevel@tonic-gate 
11310Sstevel@tonic-gate 		/* skip /pseudo/clone@0 minor nodes */
11320Sstevel@tonic-gate 		if (major != major(dev))
11330Sstevel@tonic-gate 			continue;
11340Sstevel@tonic-gate 
11350Sstevel@tonic-gate 		minor_ptr_set(minor, DI_MINOR_NIL);
11360Sstevel@tonic-gate 		if (minor_head == DI_MINOR_NIL) {
11370Sstevel@tonic-gate 			/* this is the first minor node we're looking at */
11380Sstevel@tonic-gate 			minor_head = minor_tail = minor;
11390Sstevel@tonic-gate 			continue;
11400Sstevel@tonic-gate 		}
11410Sstevel@tonic-gate 
11420Sstevel@tonic-gate 		/*
11430Sstevel@tonic-gate 		 * if the new dev is less than the old dev, update minor_head
11440Sstevel@tonic-gate 		 * so it points to the beginning of the list.  ie it points
11450Sstevel@tonic-gate 		 * to the node with the lowest dev value
11460Sstevel@tonic-gate 		 */
11470Sstevel@tonic-gate 		if (dev <= di_minor_devt(minor_head)) {
11480Sstevel@tonic-gate 			minor_ptr_set(minor, minor_head);
11490Sstevel@tonic-gate 			minor_head = minor;
11500Sstevel@tonic-gate 			continue;
11510Sstevel@tonic-gate 		}
11520Sstevel@tonic-gate 
11530Sstevel@tonic-gate 		minor_prev = minor_head;
11540Sstevel@tonic-gate 		minor_walk = minor_ptr(minor_head);
11550Sstevel@tonic-gate 		while ((minor_walk != DI_MINOR_NIL) &&
11564453Scth 		    (dev > di_minor_devt(minor_walk))) {
11570Sstevel@tonic-gate 			minor_prev = minor_walk;
11580Sstevel@tonic-gate 			minor_walk = minor_ptr(minor_walk);
11590Sstevel@tonic-gate 		}
11600Sstevel@tonic-gate 		minor_ptr_set(minor, minor_walk);
11610Sstevel@tonic-gate 		minor_ptr_set(minor_prev, minor);
11620Sstevel@tonic-gate 		if (minor_walk == NULL)
11630Sstevel@tonic-gate 			minor_tail = minor;
11640Sstevel@tonic-gate 	}
11650Sstevel@tonic-gate 
11660Sstevel@tonic-gate 	/* check if there were any non /pseudo/clone@0 nodes.  if not, bail */
11670Sstevel@tonic-gate 	if (minor_head == DI_MINOR_NIL)
11680Sstevel@tonic-gate 		return;
11690Sstevel@tonic-gate 
11700Sstevel@tonic-gate 	/*
11710Sstevel@tonic-gate 	 * now that we have a list of minor nodes sorted by devt
11720Sstevel@tonic-gate 	 * we walk through the list and break apart the entire list
11730Sstevel@tonic-gate 	 * to create circular lists of minor nodes with matching devts.
11740Sstevel@tonic-gate 	 */
11750Sstevel@tonic-gate 	minor_prev = minor_head;
11760Sstevel@tonic-gate 	minor_walk = minor_ptr(minor_head);
11770Sstevel@tonic-gate 	while (minor_walk != DI_MINOR_NIL) {
11780Sstevel@tonic-gate 		if (di_minor_devt(minor_prev) != di_minor_devt(minor_walk)) {
11790Sstevel@tonic-gate 			minor_ptr_set(minor_prev, minor_head);
11800Sstevel@tonic-gate 			minor_head = minor_walk;
11810Sstevel@tonic-gate 		}
11820Sstevel@tonic-gate 		minor_prev = minor_walk;
11830Sstevel@tonic-gate 		minor_walk = minor_ptr(minor_walk);
11840Sstevel@tonic-gate 	}
11850Sstevel@tonic-gate 	minor_ptr_set(minor_tail, minor_head);
11860Sstevel@tonic-gate }
11870Sstevel@tonic-gate 
11880Sstevel@tonic-gate static void
11890Sstevel@tonic-gate link_lnode_disp(di_link_t link, uint_t endpoint, int ilev,
11900Sstevel@tonic-gate     di_devlink_handle_t devlink_hdl)
11910Sstevel@tonic-gate {
11920Sstevel@tonic-gate 	di_lnode_t	lnode;
11930Sstevel@tonic-gate 	char		*name, *path;
11940Sstevel@tonic-gate 	int		displayed_path, spec_type;
11950Sstevel@tonic-gate 	di_node_t	node = DI_NODE_NIL;
11960Sstevel@tonic-gate 	dev_t		devt = DDI_DEV_T_NONE;
11970Sstevel@tonic-gate 
11980Sstevel@tonic-gate 	lnode = di_link_to_lnode(link, endpoint);
11990Sstevel@tonic-gate 
12000Sstevel@tonic-gate 	indent_to_level(ilev);
12010Sstevel@tonic-gate 	name = di_lnode_name(lnode);
12020Sstevel@tonic-gate 	spec_type = di_link_spectype(link);
12030Sstevel@tonic-gate 
12040Sstevel@tonic-gate 	(void) printf("mod=%s", name);
12050Sstevel@tonic-gate 
12060Sstevel@tonic-gate 	/*
12070Sstevel@tonic-gate 	 * if we're displaying the source of a link, we should display
12080Sstevel@tonic-gate 	 * the target access mode.  (either block or char.)
12090Sstevel@tonic-gate 	 */
12100Sstevel@tonic-gate 	if (endpoint == DI_LINK_SRC)
12110Sstevel@tonic-gate 		(void) printf(" accesstype=%s",
12120Sstevel@tonic-gate 		    (spec_type == S_IFBLK) ? "blk" : "chr");
12130Sstevel@tonic-gate 
12140Sstevel@tonic-gate 	/*
12150Sstevel@tonic-gate 	 * check if the lnode is bound to a specific device
12160Sstevel@tonic-gate 	 * minor node (i.e.  if it's bound to a dev_t) and
12170Sstevel@tonic-gate 	 * if so display the dev_t value and any possible
12180Sstevel@tonic-gate 	 * minor node pathing information.
12190Sstevel@tonic-gate 	 */
12200Sstevel@tonic-gate 	displayed_path = 0;
12210Sstevel@tonic-gate 	if (di_lnode_devt(lnode, &devt) == 0) {
12220Sstevel@tonic-gate 		di_minor_t	minor = DI_MINOR_NIL;
12230Sstevel@tonic-gate 
12240Sstevel@tonic-gate 		(void) printf(" dev=(%u,%u)\n",
12250Sstevel@tonic-gate 		    (uint_t)major(devt), (uint_t)minor(devt));
12260Sstevel@tonic-gate 
12270Sstevel@tonic-gate 		/* display paths to the src devt minor node */
12280Sstevel@tonic-gate 		while (minor = di_minor_next(node, minor)) {
12290Sstevel@tonic-gate 			if (devt != di_minor_devt(minor))
12300Sstevel@tonic-gate 				continue;
12310Sstevel@tonic-gate 
12320Sstevel@tonic-gate 			if ((endpoint == DI_LINK_TGT) &&
12330Sstevel@tonic-gate 			    (spec_type != di_minor_spectype(minor)))
12340Sstevel@tonic-gate 				continue;
12350Sstevel@tonic-gate 
12360Sstevel@tonic-gate 			dump_minor_data_paths(ilev + 1, minor, devlink_hdl);
12370Sstevel@tonic-gate 			displayed_path = 1;
12380Sstevel@tonic-gate 		}
12390Sstevel@tonic-gate 	} else {
12400Sstevel@tonic-gate 		(void) printf("\n");
12410Sstevel@tonic-gate 	}
12420Sstevel@tonic-gate 
12430Sstevel@tonic-gate 	if (displayed_path)
12440Sstevel@tonic-gate 		return;
12450Sstevel@tonic-gate 
12460Sstevel@tonic-gate 	/*
12470Sstevel@tonic-gate 	 * This device lnode is not did not have any minor node
12480Sstevel@tonic-gate 	 * pathing information so display the path to device node.
12490Sstevel@tonic-gate 	 */
12500Sstevel@tonic-gate 	node = di_lnode_devinfo(lnode);
12510Sstevel@tonic-gate 	if ((path = di_devfs_path(node)) == NULL)
12520Sstevel@tonic-gate 		exit(_error("failed to allocate memory"));
12530Sstevel@tonic-gate 
12540Sstevel@tonic-gate 	indent_to_level(ilev + 1);
12550Sstevel@tonic-gate 	(void) printf("dev_path=%s\n", path);
12560Sstevel@tonic-gate 	di_devfs_path_free(path);
12570Sstevel@tonic-gate }
12580Sstevel@tonic-gate 
12590Sstevel@tonic-gate static void
12600Sstevel@tonic-gate dump_minor_link_data(int ilev, di_node_t node, dev_t devt,
12610Sstevel@tonic-gate     di_devlink_handle_t devlink_hdl)
12620Sstevel@tonic-gate {
12630Sstevel@tonic-gate 	int		first = 1;
12640Sstevel@tonic-gate 	di_link_t	link;
12650Sstevel@tonic-gate 
12660Sstevel@tonic-gate 	link = DI_LINK_NIL;
12670Sstevel@tonic-gate 	while (link = di_link_next_by_node(node, link, DI_LINK_TGT)) {
12680Sstevel@tonic-gate 		di_lnode_t	tgt_lnode;
12690Sstevel@tonic-gate 		dev_t		tgt_devt = DDI_DEV_T_NONE;
12700Sstevel@tonic-gate 
12710Sstevel@tonic-gate 		tgt_lnode = di_link_to_lnode(link, DI_LINK_TGT);
12720Sstevel@tonic-gate 
12730Sstevel@tonic-gate 		if (di_lnode_devt(tgt_lnode, &tgt_devt) != 0)
12740Sstevel@tonic-gate 			continue;
12750Sstevel@tonic-gate 
12760Sstevel@tonic-gate 		if (devt != tgt_devt)
12770Sstevel@tonic-gate 			continue;
12780Sstevel@tonic-gate 
12790Sstevel@tonic-gate 		if (first) {
12800Sstevel@tonic-gate 			first = 0;
12810Sstevel@tonic-gate 			indent_to_level(ilev);
12820Sstevel@tonic-gate 			(void) printf("Device Minor Layered Under:\n");
12830Sstevel@tonic-gate 		}
12840Sstevel@tonic-gate 
12850Sstevel@tonic-gate 		/* displayed this lnode */
12860Sstevel@tonic-gate 		lnode_displayed_set(tgt_lnode);
12870Sstevel@tonic-gate 		link_lnode_disp(link, DI_LINK_SRC, ilev + 1, devlink_hdl);
12880Sstevel@tonic-gate 	}
12890Sstevel@tonic-gate 
12900Sstevel@tonic-gate 	link = DI_LINK_NIL;
12910Sstevel@tonic-gate 	while (link = di_link_next_by_node(node, link, DI_LINK_SRC)) {
12920Sstevel@tonic-gate 		di_lnode_t	src_lnode;
12930Sstevel@tonic-gate 		dev_t		src_devt = DDI_DEV_T_NONE;
12940Sstevel@tonic-gate 
12950Sstevel@tonic-gate 		src_lnode = di_link_to_lnode(link, DI_LINK_SRC);
12960Sstevel@tonic-gate 
12970Sstevel@tonic-gate 		if (di_lnode_devt(src_lnode, &src_devt) != 0)
12980Sstevel@tonic-gate 			continue;
12990Sstevel@tonic-gate 
13000Sstevel@tonic-gate 		if (devt != src_devt)
13010Sstevel@tonic-gate 			continue;
13020Sstevel@tonic-gate 
13030Sstevel@tonic-gate 		if (first) {
13040Sstevel@tonic-gate 			first = 0;
13050Sstevel@tonic-gate 			indent_to_level(ilev);
13060Sstevel@tonic-gate 			(void) printf("Device Minor Layered Over:\n");
13070Sstevel@tonic-gate 		}
13080Sstevel@tonic-gate 
13090Sstevel@tonic-gate 		/* displayed this lnode */
13100Sstevel@tonic-gate 		lnode_displayed_set(src_lnode);
13110Sstevel@tonic-gate 		link_lnode_disp(link, DI_LINK_TGT, ilev + 1, devlink_hdl);
13120Sstevel@tonic-gate 	}
13130Sstevel@tonic-gate }
13140Sstevel@tonic-gate 
13150Sstevel@tonic-gate static void
13160Sstevel@tonic-gate dump_minor_data(int ilev, di_node_t node, di_devlink_handle_t devlink_hdl)
13170Sstevel@tonic-gate {
13180Sstevel@tonic-gate 	di_minor_t	minor, minor_next;
13190Sstevel@tonic-gate 	di_lnode_t	lnode;
13200Sstevel@tonic-gate 	di_link_t	link;
13210Sstevel@tonic-gate 	int		major, firstminor = 1;
13220Sstevel@tonic-gate 
13230Sstevel@tonic-gate 	/*
13240Sstevel@tonic-gate 	 * first go through and mark all lnodes and minor nodes for this
13250Sstevel@tonic-gate 	 * node as undisplayed
13260Sstevel@tonic-gate 	 */
13270Sstevel@tonic-gate 	lnode = DI_LNODE_NIL;
13280Sstevel@tonic-gate 	while (lnode = di_lnode_next(node, lnode))
13290Sstevel@tonic-gate 		lnode_displayed_clear(lnode);
13300Sstevel@tonic-gate 	minor = DI_MINOR_NIL;
13310Sstevel@tonic-gate 	while (minor = di_minor_next(node, minor)) {
13320Sstevel@tonic-gate 		minor_displayed_clear(minor);
13330Sstevel@tonic-gate 	}
13340Sstevel@tonic-gate 
13350Sstevel@tonic-gate 	/*
13360Sstevel@tonic-gate 	 * when we display the minor nodes we want to coalesce nodes
13370Sstevel@tonic-gate 	 * that have the same dev_t.  we do this by creating circular
13380Sstevel@tonic-gate 	 * lists of minor nodes with the same devt.
13390Sstevel@tonic-gate 	 */
13400Sstevel@tonic-gate 	create_minor_list(node);
13410Sstevel@tonic-gate 
13420Sstevel@tonic-gate 	/* now we display the driver defined minor nodes */
13430Sstevel@tonic-gate 	major = di_driver_major(node);
13440Sstevel@tonic-gate 	minor = DI_MINOR_NIL;
13450Sstevel@tonic-gate 	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
13460Sstevel@tonic-gate 		dev_t	devt;
13470Sstevel@tonic-gate 
13480Sstevel@tonic-gate 		/*
13490Sstevel@tonic-gate 		 * skip /pseudo/clone@0 minor nodes.
13500Sstevel@tonic-gate 		 * these are only created for DLPIv2 network devices.
13510Sstevel@tonic-gate 		 * since these minor nodes are associated with a driver
13520Sstevel@tonic-gate 		 * and are only bound to a device instance after they
13530Sstevel@tonic-gate 		 * are opened and attached we don't print them out
13540Sstevel@tonic-gate 		 * here.
13550Sstevel@tonic-gate 		 */
13560Sstevel@tonic-gate 		devt = di_minor_devt(minor);
13570Sstevel@tonic-gate 		if (major != major(devt))
13580Sstevel@tonic-gate 			continue;
13590Sstevel@tonic-gate 
13600Sstevel@tonic-gate 		/* skip nodes that may have already been displayed */
13610Sstevel@tonic-gate 		if (minor_displayed(minor))
13620Sstevel@tonic-gate 			continue;
13630Sstevel@tonic-gate 
13640Sstevel@tonic-gate 		if (firstminor) {
13650Sstevel@tonic-gate 			firstminor = 0;
13660Sstevel@tonic-gate 			indent_to_level(ilev++);
13670Sstevel@tonic-gate 			(void) printf("Device Minor Nodes:\n");
13680Sstevel@tonic-gate 		}
13690Sstevel@tonic-gate 
13700Sstevel@tonic-gate 		/* display the device minor node information */
13710Sstevel@tonic-gate 		indent_to_level(ilev);
13720Sstevel@tonic-gate 		(void) printf("dev=(%u,%u)\n",
13734453Scth 		    (uint_t)major(devt), (uint_t)minor(devt));
13740Sstevel@tonic-gate 
13750Sstevel@tonic-gate 		minor_next = minor;
13760Sstevel@tonic-gate 		do {
13770Sstevel@tonic-gate 			/* display device minor node path info */
13780Sstevel@tonic-gate 			minor_displayed_set(minor_next);
13790Sstevel@tonic-gate 			dump_minor_data_paths(ilev + 1, minor_next,
13804453Scth 			    devlink_hdl);
13810Sstevel@tonic-gate 
13820Sstevel@tonic-gate 			/* get a pointer to the next node */
13830Sstevel@tonic-gate 			minor_next = minor_ptr(minor_next);
13840Sstevel@tonic-gate 		} while (minor_next != minor);
13850Sstevel@tonic-gate 
13860Sstevel@tonic-gate 		/* display who has this device minor node open */
13870Sstevel@tonic-gate 		dump_minor_link_data(ilev + 1, node, devt, devlink_hdl);
13887224Scth 
13897224Scth 		/* display properties associated with this devt */
13907224Scth 		(void) dump_prop_list(&drvprop_dumpops, "Minor",
1391*7261Scth 		    ilev + 1, node, devt, NULL);
13920Sstevel@tonic-gate 	}
13930Sstevel@tonic-gate 
13940Sstevel@tonic-gate 	/*
13950Sstevel@tonic-gate 	 * now go through all the target lnodes for this node and
13960Sstevel@tonic-gate 	 * if they haven't yet been displayed, display them now.
13970Sstevel@tonic-gate 	 *
13980Sstevel@tonic-gate 	 * this happens in the case of clone opens when an "official"
13990Sstevel@tonic-gate 	 * minor node does not exist for the opened devt
14000Sstevel@tonic-gate 	 */
14010Sstevel@tonic-gate 	link = DI_LINK_NIL;
14020Sstevel@tonic-gate 	while (link = di_link_next_by_node(node, link, DI_LINK_TGT)) {
14030Sstevel@tonic-gate 		dev_t		devt;
14040Sstevel@tonic-gate 
14050Sstevel@tonic-gate 		lnode = di_link_to_lnode(link, DI_LINK_TGT);
14060Sstevel@tonic-gate 
14070Sstevel@tonic-gate 		/* if we've already displayed this target lnode, skip it */
14080Sstevel@tonic-gate 		if (lnode_displayed(lnode))
14090Sstevel@tonic-gate 			continue;
14100Sstevel@tonic-gate 
14110Sstevel@tonic-gate 		if (firstminor) {
14120Sstevel@tonic-gate 			firstminor = 0;
14130Sstevel@tonic-gate 			indent_to_level(ilev++);
14140Sstevel@tonic-gate 			(void) printf("Device Minor Nodes:\n");
14150Sstevel@tonic-gate 		}
14160Sstevel@tonic-gate 
14170Sstevel@tonic-gate 		/* display the device minor node information */
14180Sstevel@tonic-gate 		indent_to_level(ilev);
14190Sstevel@tonic-gate 		(void) di_lnode_devt(lnode, &devt);
14200Sstevel@tonic-gate 		(void) printf("dev=(%u,%u)\n",
14214453Scth 		    (uint_t)major(devt), (uint_t)minor(devt));
14220Sstevel@tonic-gate 
14230Sstevel@tonic-gate 		indent_to_level(ilev + 1);
14240Sstevel@tonic-gate 		(void) printf("dev_path=<clone>\n");
14250Sstevel@tonic-gate 
14260Sstevel@tonic-gate 		/* display who has this cloned device minor node open */
14270Sstevel@tonic-gate 		dump_minor_link_data(ilev + 1, node, devt, devlink_hdl);
14280Sstevel@tonic-gate 
14290Sstevel@tonic-gate 		/* mark node as displayed */
14300Sstevel@tonic-gate 		lnode_displayed_set(lnode);
14310Sstevel@tonic-gate 	}
14320Sstevel@tonic-gate }
14330Sstevel@tonic-gate 
14340Sstevel@tonic-gate static void
14350Sstevel@tonic-gate dump_link_data(int ilev, di_node_t node, di_devlink_handle_t devlink_hdl)
14360Sstevel@tonic-gate {
14370Sstevel@tonic-gate 	int		first = 1;
14380Sstevel@tonic-gate 	di_link_t	link;
14390Sstevel@tonic-gate 
14400Sstevel@tonic-gate 	link = DI_LINK_NIL;
14410Sstevel@tonic-gate 	while (link = di_link_next_by_node(node, link, DI_LINK_SRC)) {
14420Sstevel@tonic-gate 		di_lnode_t	src_lnode;
14430Sstevel@tonic-gate 		dev_t		src_devt = DDI_DEV_T_NONE;
14440Sstevel@tonic-gate 
14450Sstevel@tonic-gate 		src_lnode = di_link_to_lnode(link, DI_LINK_SRC);
14460Sstevel@tonic-gate 
14470Sstevel@tonic-gate 		/*
14480Sstevel@tonic-gate 		 * here we only want to print out layering information
14490Sstevel@tonic-gate 		 * if we are the source and our source lnode is not
14500Sstevel@tonic-gate 		 * associated with any particular dev_t.  (which means
14510Sstevel@tonic-gate 		 * we won't display this link while dumping minor node
14520Sstevel@tonic-gate 		 * info.)
14530Sstevel@tonic-gate 		 */
14540Sstevel@tonic-gate 		if (di_lnode_devt(src_lnode, &src_devt) != -1)
14550Sstevel@tonic-gate 			continue;
14560Sstevel@tonic-gate 
14570Sstevel@tonic-gate 		if (first) {
14580Sstevel@tonic-gate 			first = 0;
14590Sstevel@tonic-gate 			indent_to_level(ilev);
14600Sstevel@tonic-gate 			(void) printf("Device Layered Over:\n");
14610Sstevel@tonic-gate 		}
14620Sstevel@tonic-gate 
14630Sstevel@tonic-gate 		/* displayed this lnode */
14640Sstevel@tonic-gate 		link_lnode_disp(link, DI_LINK_TGT, ilev + 1, devlink_hdl);
14650Sstevel@tonic-gate 	}
14660Sstevel@tonic-gate }
14670Sstevel@tonic-gate 
14680Sstevel@tonic-gate /*
14690Sstevel@tonic-gate  * certain 'known' property names may contain 'composite' strings.
14700Sstevel@tonic-gate  * Handle them here, and print them as 'string1' + 'string2' ...
14710Sstevel@tonic-gate  */
14720Sstevel@tonic-gate static int
14730Sstevel@tonic-gate print_composite_string(const char *var, char *value, int size)
14740Sstevel@tonic-gate {
14750Sstevel@tonic-gate 	char *p, *q;
14760Sstevel@tonic-gate 	char *firstp;
14770Sstevel@tonic-gate 
14780Sstevel@tonic-gate 	if ((strcmp(var, "version") != 0) &&
14790Sstevel@tonic-gate 	    (strcmp(var, "compatible") != 0))
14800Sstevel@tonic-gate 		return (0);	/* Not a known composite string */
14810Sstevel@tonic-gate 
14820Sstevel@tonic-gate 	/*
14830Sstevel@tonic-gate 	 * Verify that each string in the composite string is non-NULL,
14840Sstevel@tonic-gate 	 * is within the bounds of the property length, and contains
14850Sstevel@tonic-gate 	 * printable characters or white space. Otherwise let the
14860Sstevel@tonic-gate 	 * caller deal with it.
14870Sstevel@tonic-gate 	 */
14880Sstevel@tonic-gate 	for (firstp = p = value; p < (value + size); p += strlen(p) + 1) {
14890Sstevel@tonic-gate 		if (strlen(p) == 0)
14900Sstevel@tonic-gate 			return (0);		/* NULL string */
14910Sstevel@tonic-gate 		for (q = p; *q; q++) {
14920Sstevel@tonic-gate 			if (!(isascii(*q) && (isprint(*q) || isspace(*q))))
14930Sstevel@tonic-gate 				return (0);	/* Not printable or space */
14940Sstevel@tonic-gate 		}
14950Sstevel@tonic-gate 		if (q > (firstp + size))
14960Sstevel@tonic-gate 			return (0);		/* Out of bounds */
14970Sstevel@tonic-gate 	}
14980Sstevel@tonic-gate 
14990Sstevel@tonic-gate 	for (firstp = p = value; p < (value + size); p += strlen(p) + 1) {
15000Sstevel@tonic-gate 		if (p == firstp)
15010Sstevel@tonic-gate 			(void) printf("'%s'", p);
15020Sstevel@tonic-gate 		else
15030Sstevel@tonic-gate 			(void) printf(" + '%s'", p);
15040Sstevel@tonic-gate 	}
15050Sstevel@tonic-gate 	(void) putchar('\n');
15060Sstevel@tonic-gate 	return (1);
15070Sstevel@tonic-gate }
15080Sstevel@tonic-gate 
15090Sstevel@tonic-gate /*
15100Sstevel@tonic-gate  * Print one property and its value. Handle the verbose case.
15110Sstevel@tonic-gate  */
15120Sstevel@tonic-gate static void
15130Sstevel@tonic-gate print_one(nvpair_t *nvp, int level)
15140Sstevel@tonic-gate {
15150Sstevel@tonic-gate 	int i;
15160Sstevel@tonic-gate 	int endswap = 0;
15170Sstevel@tonic-gate 	uint_t valsize;
15180Sstevel@tonic-gate 	char *value;
15190Sstevel@tonic-gate 	char *var = nvpair_name(nvp);
15200Sstevel@tonic-gate 
15210Sstevel@tonic-gate 	indent_to_level(level);
15220Sstevel@tonic-gate 	(void) printf("%s: ", var);
15230Sstevel@tonic-gate 
15240Sstevel@tonic-gate 	switch (nvpair_type(nvp)) {
15250Sstevel@tonic-gate 	case DATA_TYPE_BOOLEAN:
15260Sstevel@tonic-gate 		(void) printf(" \n");
15270Sstevel@tonic-gate 		return;
15280Sstevel@tonic-gate 	case DATA_TYPE_BYTE_ARRAY:
15290Sstevel@tonic-gate 		if (nvpair_value_byte_array(nvp, (uchar_t **)&value,
15300Sstevel@tonic-gate 		    &valsize)) {
15310Sstevel@tonic-gate 			(void) printf("data not available.\n");
15320Sstevel@tonic-gate 			return;
15330Sstevel@tonic-gate 		}
15340Sstevel@tonic-gate 		valsize--;	/* take out null added by driver */
15350Sstevel@tonic-gate 
15360Sstevel@tonic-gate 		/*
15370Sstevel@tonic-gate 		 * Do not print valsize > MAXVALSIZE, to be compatible
15380Sstevel@tonic-gate 		 * with old behavior. E.g. intel's eisa-nvram property
15390Sstevel@tonic-gate 		 * has a size of 65 K.
15400Sstevel@tonic-gate 		 */
15410Sstevel@tonic-gate 		if (valsize > MAXVALSIZE) {
15420Sstevel@tonic-gate 			(void) printf(" \n");
15430Sstevel@tonic-gate 			return;
15440Sstevel@tonic-gate 		}
15450Sstevel@tonic-gate 		break;
15460Sstevel@tonic-gate 	default:
15470Sstevel@tonic-gate 		(void) printf("data type unexpected.\n");
15480Sstevel@tonic-gate 		return;
15490Sstevel@tonic-gate 	}
15500Sstevel@tonic-gate 
15510Sstevel@tonic-gate 	/*
15520Sstevel@tonic-gate 	 * Handle printing verbosely
15530Sstevel@tonic-gate 	 */
15540Sstevel@tonic-gate 	if (print_composite_string(var, value, valsize)) {
15550Sstevel@tonic-gate 		return;
15560Sstevel@tonic-gate 	}
15570Sstevel@tonic-gate 
15580Sstevel@tonic-gate 	if (!unprintable(value, valsize)) {
15590Sstevel@tonic-gate 		(void) printf(" '%s'\n", value);
15600Sstevel@tonic-gate 		return;
15610Sstevel@tonic-gate 	}
15620Sstevel@tonic-gate 
15630Sstevel@tonic-gate 	(void) printf(" ");
15640Sstevel@tonic-gate #ifdef	__x86
15650Sstevel@tonic-gate 	/*
15660Sstevel@tonic-gate 	 * Due to backwards compatibility constraints x86 int
15670Sstevel@tonic-gate 	 * properties are not in big-endian (ieee 1275) byte order.
15680Sstevel@tonic-gate 	 * If we have a property that is a multiple of 4 bytes,
15690Sstevel@tonic-gate 	 * let's assume it is an array of ints and print the bytes
15700Sstevel@tonic-gate 	 * in little endian order to make things look nicer for
15710Sstevel@tonic-gate 	 * the user.
15720Sstevel@tonic-gate 	 */
15730Sstevel@tonic-gate 	endswap = (valsize % 4) == 0;
15740Sstevel@tonic-gate #endif	/* __x86 */
15750Sstevel@tonic-gate 	for (i = 0; i < valsize; i++) {
15760Sstevel@tonic-gate 		int out;
15770Sstevel@tonic-gate 		if (i && (i % 4 == 0))
15780Sstevel@tonic-gate 			(void) putchar('.');
15790Sstevel@tonic-gate 		if (endswap)
15800Sstevel@tonic-gate 			out = value[i + (3 - 2 * (i % 4))] & 0xff;
15810Sstevel@tonic-gate 		else
15820Sstevel@tonic-gate 			out = value[i] & 0xff;
15830Sstevel@tonic-gate 
15840Sstevel@tonic-gate 		(void) printf("%02x", out);
15850Sstevel@tonic-gate 	}
15860Sstevel@tonic-gate 	(void) putchar('\n');
15870Sstevel@tonic-gate }
15880Sstevel@tonic-gate 
15890Sstevel@tonic-gate static int
15900Sstevel@tonic-gate unprintable(char *value, int size)
15910Sstevel@tonic-gate {
15920Sstevel@tonic-gate 	int i;
15930Sstevel@tonic-gate 
15940Sstevel@tonic-gate 	/*
15950Sstevel@tonic-gate 	 * Is this just a zero?
15960Sstevel@tonic-gate 	 */
15970Sstevel@tonic-gate 	if (size == 0 || value[0] == '\0')
15980Sstevel@tonic-gate 		return (1);
15990Sstevel@tonic-gate 	/*
16000Sstevel@tonic-gate 	 * If any character is unprintable, or if a null appears
16010Sstevel@tonic-gate 	 * anywhere except at the end of a string, the whole
16020Sstevel@tonic-gate 	 * property is "unprintable".
16030Sstevel@tonic-gate 	 */
16040Sstevel@tonic-gate 	for (i = 0; i < size; ++i) {
16050Sstevel@tonic-gate 		if (value[i] == '\0')
16060Sstevel@tonic-gate 			return (i != (size - 1));
16070Sstevel@tonic-gate 		if (!isascii(value[i]) || iscntrl(value[i]))
16080Sstevel@tonic-gate 			return (1);
16090Sstevel@tonic-gate 	}
16100Sstevel@tonic-gate 	return (0);
16110Sstevel@tonic-gate }
16120Sstevel@tonic-gate 
16130Sstevel@tonic-gate static int
16140Sstevel@tonic-gate promopen(int oflag)
16150Sstevel@tonic-gate {
16160Sstevel@tonic-gate 	for (;;)  {
16170Sstevel@tonic-gate 		if ((prom_fd = open(opts.o_promdev, oflag)) < 0)  {
16180Sstevel@tonic-gate 			if (errno == EAGAIN)   {
16190Sstevel@tonic-gate 				(void) sleep(5);
16200Sstevel@tonic-gate 				continue;
16210Sstevel@tonic-gate 			}
16220Sstevel@tonic-gate 			if (errno == ENXIO)
16230Sstevel@tonic-gate 				return (-1);
16240Sstevel@tonic-gate 			if (getzoneid() == GLOBAL_ZONEID) {
16250Sstevel@tonic-gate 				_exit(_error("cannot open %s",
16260Sstevel@tonic-gate 				    opts.o_promdev));
16270Sstevel@tonic-gate 			}
16280Sstevel@tonic-gate 			/* not an error if this isn't the global zone */
16290Sstevel@tonic-gate 			(void) _error(NULL, "openprom facility not available");
16300Sstevel@tonic-gate 			exit(0);
16310Sstevel@tonic-gate 		} else
16320Sstevel@tonic-gate 			return (0);
16330Sstevel@tonic-gate 	}
16340Sstevel@tonic-gate }
16350Sstevel@tonic-gate 
16360Sstevel@tonic-gate static void
16370Sstevel@tonic-gate promclose(void)
16380Sstevel@tonic-gate {
16390Sstevel@tonic-gate 	if (close(prom_fd) < 0)
16400Sstevel@tonic-gate 		exit(_error("close error on %s", opts.o_promdev));
16410Sstevel@tonic-gate }
16420Sstevel@tonic-gate 
16430Sstevel@tonic-gate /*
16440Sstevel@tonic-gate  * Get and print the name of the frame buffer device.
16450Sstevel@tonic-gate  */
16460Sstevel@tonic-gate int
16470Sstevel@tonic-gate do_fbname(void)
16480Sstevel@tonic-gate {
16490Sstevel@tonic-gate 	int	retval;
16500Sstevel@tonic-gate 	char fbuf_path[MAXPATHLEN];
16510Sstevel@tonic-gate 
16520Sstevel@tonic-gate 	retval =  modctl(MODGETFBNAME, (caddr_t)fbuf_path);
16530Sstevel@tonic-gate 
16540Sstevel@tonic-gate 	if (retval == 0) {
16550Sstevel@tonic-gate 		(void) printf("%s\n", fbuf_path);
16560Sstevel@tonic-gate 	} else {
16570Sstevel@tonic-gate 		if (retval == EFAULT) {
16580Sstevel@tonic-gate 			(void) fprintf(stderr,
16590Sstevel@tonic-gate 			"Error copying fb path to userland\n");
16600Sstevel@tonic-gate 		} else {
16610Sstevel@tonic-gate 			(void) fprintf(stderr,
16620Sstevel@tonic-gate 			"Console output device is not a frame buffer\n");
16630Sstevel@tonic-gate 		}
16640Sstevel@tonic-gate 		return (1);
16650Sstevel@tonic-gate 	}
16660Sstevel@tonic-gate 	return (0);
16670Sstevel@tonic-gate }
16680Sstevel@tonic-gate 
16690Sstevel@tonic-gate /*
16700Sstevel@tonic-gate  * Get and print the PROM version.
16710Sstevel@tonic-gate  */
16720Sstevel@tonic-gate int
16730Sstevel@tonic-gate do_promversion(void)
16740Sstevel@tonic-gate {
16750Sstevel@tonic-gate 	Oppbuf	oppbuf;
16760Sstevel@tonic-gate 	struct openpromio *opp = &(oppbuf.opp);
16770Sstevel@tonic-gate 
16780Sstevel@tonic-gate 	if (promopen(O_RDONLY))  {
16790Sstevel@tonic-gate 		(void) fprintf(stderr, "Cannot open openprom device\n");
16800Sstevel@tonic-gate 		return (1);
16810Sstevel@tonic-gate 	}
16820Sstevel@tonic-gate 
16830Sstevel@tonic-gate 	opp->oprom_size = MAXVALSIZE;
16840Sstevel@tonic-gate 	if (ioctl(prom_fd, OPROMGETVERSION, opp) < 0)
16850Sstevel@tonic-gate 		exit(_error("OPROMGETVERSION"));
16860Sstevel@tonic-gate 
16870Sstevel@tonic-gate 	(void) printf("%s\n", opp->oprom_array);
16880Sstevel@tonic-gate 	promclose();
16890Sstevel@tonic-gate 	return (0);
16900Sstevel@tonic-gate }
16910Sstevel@tonic-gate 
16920Sstevel@tonic-gate int
16930Sstevel@tonic-gate do_prom_version64(void)
16940Sstevel@tonic-gate {
16950Sstevel@tonic-gate #ifdef	sparc
16960Sstevel@tonic-gate 	Oppbuf	oppbuf;
16970Sstevel@tonic-gate 	struct openpromio *opp = &(oppbuf.opp);
16980Sstevel@tonic-gate 	/*LINTED*/
16990Sstevel@tonic-gate 	struct openprom_opr64 *opr = (struct openprom_opr64 *)opp->oprom_array;
17000Sstevel@tonic-gate 
17010Sstevel@tonic-gate 	static const char msg[] =
17024453Scth 	    "NOTICE: The firmware on this system does not support the "
17034453Scth 	    "64-bit OS.\n"
17044453Scth 	    "\tPlease upgrade to at least the following version:\n"
17054453Scth 	    "\t\t%s\n\n";
17060Sstevel@tonic-gate 
17070Sstevel@tonic-gate 	if (promopen(O_RDONLY))  {
17080Sstevel@tonic-gate 		(void) fprintf(stderr, "Cannot open openprom device\n");
17090Sstevel@tonic-gate 		return (-1);
17100Sstevel@tonic-gate 	}
17110Sstevel@tonic-gate 
17120Sstevel@tonic-gate 	opp->oprom_size = MAXVALSIZE;
17130Sstevel@tonic-gate 	if (ioctl(prom_fd, OPROMREADY64, opp) < 0)
17140Sstevel@tonic-gate 		exit(_error("OPROMREADY64"));
17150Sstevel@tonic-gate 
17160Sstevel@tonic-gate 	if (opr->return_code == 0)
17170Sstevel@tonic-gate 		return (0);
17180Sstevel@tonic-gate 
17190Sstevel@tonic-gate 	(void) printf(msg, opr->message);
17200Sstevel@tonic-gate 
17210Sstevel@tonic-gate 	promclose();
17220Sstevel@tonic-gate 	return (opr->return_code);
17230Sstevel@tonic-gate #else
17240Sstevel@tonic-gate 	return (0);
17250Sstevel@tonic-gate #endif
17260Sstevel@tonic-gate }
17270Sstevel@tonic-gate 
17280Sstevel@tonic-gate int
17290Sstevel@tonic-gate do_productinfo(void)
17300Sstevel@tonic-gate {
17310Sstevel@tonic-gate 	di_node_t root, next_node;
17320Sstevel@tonic-gate 	di_prom_handle_t promh;
17330Sstevel@tonic-gate 	static const char *root_prop[] = { "name", "model", "banner-name",
17340Sstevel@tonic-gate 					"compatible" };
17350Sstevel@tonic-gate 	static const char *root_propv[] = { "name", "model", "banner-name",
17360Sstevel@tonic-gate 					"compatible", "idprom" };
17370Sstevel@tonic-gate 	static const char *oprom_prop[] = { "model", "version" };
17380Sstevel@tonic-gate 
17390Sstevel@tonic-gate 
17400Sstevel@tonic-gate 	root = di_init("/", DINFOCPYALL);
17410Sstevel@tonic-gate 
17420Sstevel@tonic-gate 	if (root == DI_NODE_NIL) {
17430Sstevel@tonic-gate 		(void) fprintf(stderr, "di_init() failed\n");
17440Sstevel@tonic-gate 		return (1);
17450Sstevel@tonic-gate 	}
17460Sstevel@tonic-gate 
17470Sstevel@tonic-gate 	promh = di_prom_init();
17480Sstevel@tonic-gate 
17490Sstevel@tonic-gate 	if (promh == DI_PROM_HANDLE_NIL) {
17500Sstevel@tonic-gate 		(void) fprintf(stderr, "di_prom_init() failed\n");
17510Sstevel@tonic-gate 		return (1);
17520Sstevel@tonic-gate 	}
17530Sstevel@tonic-gate 
17540Sstevel@tonic-gate 	if (opts.o_verbose) {
17550Sstevel@tonic-gate 		dump_prodinfo(promh, root, root_propv, "root",
17564453Scth 		    NUM_ELEMENTS(root_propv));
17570Sstevel@tonic-gate 
17580Sstevel@tonic-gate 		/* Get model and version properties under node "openprom" */
17590Sstevel@tonic-gate 		next_node = find_node_by_name(promh, root, "openprom");
17600Sstevel@tonic-gate 		if (next_node != DI_NODE_NIL)
17610Sstevel@tonic-gate 			dump_prodinfo(promh, next_node, oprom_prop,
17624453Scth 			    "openprom", NUM_ELEMENTS(oprom_prop));
17630Sstevel@tonic-gate 
17640Sstevel@tonic-gate 	} else
17650Sstevel@tonic-gate 		dump_prodinfo(promh, root, root_prop, "root",
17664453Scth 		    NUM_ELEMENTS(root_prop));
17670Sstevel@tonic-gate 	di_prom_fini(promh);
17680Sstevel@tonic-gate 	di_fini(root);
17690Sstevel@tonic-gate 	return (0);
17700Sstevel@tonic-gate }
17710Sstevel@tonic-gate 
17720Sstevel@tonic-gate di_node_t
17730Sstevel@tonic-gate find_node_by_name(di_prom_handle_t promh, di_node_t parent,
17740Sstevel@tonic-gate 		char *node_name)
17750Sstevel@tonic-gate {
17760Sstevel@tonic-gate 	di_node_t next_node;
17770Sstevel@tonic-gate 	uchar_t *prop_valp;
17780Sstevel@tonic-gate 
17794900Svb160487 	for (next_node = di_child_node(parent); next_node != DI_NODE_NIL;
17804900Svb160487 	    next_node = di_sibling_node(next_node)) {
17814900Svb160487 		int len;
17824900Svb160487 
17834900Svb160487 		len = get_propval_by_name(promh, next_node, "name", &prop_valp);
17844900Svb160487 		if ((len != -1) && (strcmp((char *)prop_valp, node_name) == 0))
17850Sstevel@tonic-gate 			return (next_node);
17860Sstevel@tonic-gate 	}
17870Sstevel@tonic-gate 	return (DI_NODE_NIL);
17880Sstevel@tonic-gate }
17890Sstevel@tonic-gate 
17900Sstevel@tonic-gate 
17910Sstevel@tonic-gate int
17920Sstevel@tonic-gate get_propval_by_name(di_prom_handle_t promh, di_node_t node, const char *name,
17930Sstevel@tonic-gate 			uchar_t **valp)
17940Sstevel@tonic-gate {
17950Sstevel@tonic-gate 	int len;
17960Sstevel@tonic-gate 	uchar_t *bufp;
17970Sstevel@tonic-gate 
17980Sstevel@tonic-gate 	len = di_prom_prop_lookup_bytes(promh, node, name,
17994453Scth 	    (uchar_t **)&bufp);
18000Sstevel@tonic-gate 	if (len != -1) {
18010Sstevel@tonic-gate 		*valp = (uchar_t *)malloc(len);
18020Sstevel@tonic-gate 		(void) memcpy(*valp, bufp, len);
18030Sstevel@tonic-gate 	}
18040Sstevel@tonic-gate 	return (len);
18050Sstevel@tonic-gate }
18060Sstevel@tonic-gate 
18070Sstevel@tonic-gate 
18080Sstevel@tonic-gate static void
18090Sstevel@tonic-gate dump_prodinfo(di_prom_handle_t promh, di_node_t node, const char **propstr,
18100Sstevel@tonic-gate 		char *node_name, int num)
18110Sstevel@tonic-gate {
18120Sstevel@tonic-gate 	int out, len, index1, index, endswap = 0;
18130Sstevel@tonic-gate 	uchar_t *prop_valp;
18140Sstevel@tonic-gate 
18150Sstevel@tonic-gate 	for (index1 = 0; index1 < num; index1++) {
18160Sstevel@tonic-gate 		len = get_propval_by_name(promh, node, propstr[index1],
18174453Scth 		    &prop_valp);
18180Sstevel@tonic-gate 		if (len != -1) {
18190Sstevel@tonic-gate 			if (strcmp(node_name, "root"))
18200Sstevel@tonic-gate 				(void) printf("%s ", node_name);
18210Sstevel@tonic-gate 
18220Sstevel@tonic-gate 			(void) printf("%s: ", propstr[index1]);
18230Sstevel@tonic-gate 
18240Sstevel@tonic-gate 			if (print_composite_string((const char *)
18254453Scth 			    propstr[index1], (char *)prop_valp, len)) {
18260Sstevel@tonic-gate 				free(prop_valp);
18270Sstevel@tonic-gate 				continue;
18280Sstevel@tonic-gate 			}
18290Sstevel@tonic-gate 
18300Sstevel@tonic-gate 			if (!unprintable((char *)prop_valp, len)) {
18310Sstevel@tonic-gate 				(void) printf(" %s\n", (char *)prop_valp);
18320Sstevel@tonic-gate 				free(prop_valp);
18330Sstevel@tonic-gate 				continue;
18340Sstevel@tonic-gate 			}
18350Sstevel@tonic-gate 
18360Sstevel@tonic-gate 			(void) printf(" ");
18370Sstevel@tonic-gate #ifdef  __x86
18380Sstevel@tonic-gate 			endswap = (len % 4) == 0;
18390Sstevel@tonic-gate #endif  /* __x86 */
18400Sstevel@tonic-gate 			for (index = 0; index < len; index++) {
18410Sstevel@tonic-gate 				if (index && (index % 4 == 0))
18420Sstevel@tonic-gate 					(void) putchar('.');
18430Sstevel@tonic-gate 				if (endswap)
18440Sstevel@tonic-gate 					out = prop_valp[index +
18454453Scth 					    (3 - 2 * (index % 4))] & 0xff;
18460Sstevel@tonic-gate 				else
18470Sstevel@tonic-gate 					out = prop_valp[index] & 0xff;
18480Sstevel@tonic-gate 				(void) printf("%02x", out);
18490Sstevel@tonic-gate 			}
18500Sstevel@tonic-gate 			(void) putchar('\n');
18510Sstevel@tonic-gate 			free(prop_valp);
18520Sstevel@tonic-gate 		}
18530Sstevel@tonic-gate 	}
18540Sstevel@tonic-gate }
1855*7261Scth 
1856*7261Scth static int
1857*7261Scth dump_compatible(char *name, int ilev, di_node_t node)
1858*7261Scth {
1859*7261Scth 	int	ncompat;
1860*7261Scth 	char	*compat_array;
1861*7261Scth 	char	*p, *q;
1862*7261Scth 	int	i;
1863*7261Scth 
1864*7261Scth 	if (node == DI_PATH_NIL)
1865*7261Scth 		return (0);
1866*7261Scth 
1867*7261Scth 	ncompat = di_compatible_names(node, &compat_array);
1868*7261Scth 	if (ncompat <= 0)
1869*7261Scth 		return (0);	/* no 'compatible' available */
1870*7261Scth 
1871*7261Scth 	/* verify integrety of compat_array */
1872*7261Scth 	for (i = 0, p = compat_array; i < ncompat; i++, p += strlen(p) + 1) {
1873*7261Scth 		if (strlen(p) == 0)
1874*7261Scth 			return (0);		/* NULL string */
1875*7261Scth 		for (q = p; *q; q++) {
1876*7261Scth 			if (!(isascii(*q) && (isprint(*q) || isspace(*q))))
1877*7261Scth 				return (0);	/* Not printable or space */
1878*7261Scth 		}
1879*7261Scth 	}
1880*7261Scth 
1881*7261Scth 	/* If name is non-NULL, produce header */
1882*7261Scth 	if (name) {
1883*7261Scth 		indent_to_level(ilev);
1884*7261Scth 		(void) printf("%s properties:\n", name);
1885*7261Scth 	}
1886*7261Scth 	ilev++;
1887*7261Scth 
1888*7261Scth 	/* process like a string array property */
1889*7261Scth 	indent_to_level(ilev);
1890*7261Scth 	(void) printf("name='compatible' type=string items=%d\n", ncompat);
1891*7261Scth 	indent_to_level(ilev);
1892*7261Scth 	(void) printf("    value=");
1893*7261Scth 	for (i = 0, p = compat_array; i < (ncompat - 1);
1894*7261Scth 	    i++, p += strlen(p) + 1)
1895*7261Scth 		(void) printf("'%s' + ", p);
1896*7261Scth 	(void) printf("'%s'", p);
1897*7261Scth 	(void) putchar('\n');
1898*7261Scth 	return (1);
1899*7261Scth }
1900