Home | History | Annotate | Download | only in prtconf
      1 /*
      2  * CDDL HEADER START
      3  *
      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.
      7  *
      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.
     12  *
     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]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 /*
     27  * For machines that support the openprom, fetch and print the list
     28  * of devices that the kernel has fetched from the prom or conjured up.
     29  */
     30 
     31 #include <stdio.h>
     32 #include <stdarg.h>
     33 #include <stdlib.h>
     34 #include <fcntl.h>
     35 #include <ctype.h>
     36 #include <strings.h>
     37 #include <unistd.h>
     38 #include <stropts.h>
     39 #include <sys/types.h>
     40 #include <sys/mkdev.h>
     41 #include <sys/sunddi.h>
     42 #include <sys/openpromio.h>
     43 #include <sys/modctl.h>
     44 #include <sys/stat.h>
     45 #include <zone.h>
     46 #include <libnvpair.h>
     47 #include "prtconf.h"
     48 
     49 
     50 typedef char *(*dump_propname_t)(void *);
     51 typedef int (*dump_proptype_t)(void *);
     52 typedef int (*dump_propints_t)(void *, int **);
     53 typedef int (*dump_propint64_t)(void *, int64_t **);
     54 typedef int (*dump_propstrings_t)(void *, char **);
     55 typedef int (*dump_propbytes_t)(void *, uchar_t **);
     56 typedef int (*dump_proprawdata_t)(void *, uchar_t **);
     57 
     58 typedef struct dumpops_common {
     59 	dump_propname_t doc_propname;
     60 	dump_proptype_t doc_proptype;
     61 	dump_propints_t doc_propints;
     62 	dump_propint64_t doc_propint64;
     63 	dump_propstrings_t doc_propstrings;
     64 	dump_propbytes_t doc_propbytes;
     65 	dump_proprawdata_t doc_proprawdata;
     66 } dumpops_common_t;
     67 
     68 static const dumpops_common_t prop_dumpops = {
     69 	(dump_propname_t)di_prop_name,
     70 	(dump_proptype_t)di_prop_type,
     71 	(dump_propints_t)di_prop_ints,
     72 	(dump_propint64_t)di_prop_int64,
     73 	(dump_propstrings_t)di_prop_strings,
     74 	(dump_propbytes_t)di_prop_bytes,
     75 	(dump_proprawdata_t)di_prop_rawdata
     76 }, pathprop_common_dumpops = {
     77 	(dump_propname_t)di_path_prop_name,
     78 	(dump_proptype_t)di_path_prop_type,
     79 	(dump_propints_t)di_path_prop_ints,
     80 	(dump_propint64_t)di_path_prop_int64s,
     81 	(dump_propstrings_t)di_path_prop_strings,
     82 	(dump_propbytes_t)di_path_prop_bytes,
     83 	(dump_proprawdata_t)di_path_prop_bytes
     84 };
     85 
     86 typedef void *(*dump_nextprop_t)(void *, void *);
     87 typedef dev_t (*dump_propdevt_t)(void *);
     88 
     89 typedef struct dumpops {
     90 	const dumpops_common_t *dop_common;
     91 	dump_nextprop_t dop_nextprop;
     92 	dump_propdevt_t dop_propdevt;
     93 } dumpops_t;
     94 
     95 typedef struct di_args {
     96 	di_prom_handle_t	prom_hdl;
     97 	di_devlink_handle_t	devlink_hdl;
     98 } di_arg_t;
     99 
    100 static const dumpops_t sysprop_dumpops = {
    101 	&prop_dumpops,
    102 	(dump_nextprop_t)di_prop_sys_next,
    103 	NULL
    104 }, globprop_dumpops = {
    105 	&prop_dumpops,
    106 	(dump_nextprop_t)di_prop_global_next,
    107 	NULL
    108 }, drvprop_dumpops = {
    109 	&prop_dumpops,
    110 	(dump_nextprop_t)di_prop_drv_next,
    111 	(dump_propdevt_t)di_prop_devt
    112 }, hwprop_dumpops = {
    113 	&prop_dumpops,
    114 	(dump_nextprop_t)di_prop_hw_next,
    115 	NULL
    116 }, pathprop_dumpops = {
    117 	&pathprop_common_dumpops,
    118 	(dump_nextprop_t)di_path_prop_next,
    119 	NULL
    120 };
    121 
    122 #define	PROPNAME(ops) (ops->dop_common->doc_propname)
    123 #define	PROPTYPE(ops) (ops->dop_common->doc_proptype)
    124 #define	PROPINTS(ops) (ops->dop_common->doc_propints)
    125 #define	PROPINT64(ops) (ops->dop_common->doc_propint64)
    126 #define	PROPSTRINGS(ops) (ops->dop_common->doc_propstrings)
    127 #define	PROPBYTES(ops) (ops->dop_common->doc_propbytes)
    128 #define	PROPRAWDATA(ops) (ops->dop_common->doc_proprawdata)
    129 #define	NEXTPROP(ops) (ops->dop_nextprop)
    130 #define	PROPDEVT(ops) (ops->dop_propdevt)
    131 #define	NUM_ELEMENTS(A) (sizeof (A) / sizeof (A[0]))
    132 
    133 static int prop_type_guess(const dumpops_t *, void *, void **, int *);
    134 static void walk_driver(di_node_t, di_arg_t *);
    135 static int dump_devs(di_node_t, void *);
    136 static int dump_prop_list(const dumpops_t *, const char *,
    137 				int, void *, dev_t, int *);
    138 static int _error(const char *, ...);
    139 static int is_openprom();
    140 static void walk(uchar_t *, uint_t, int);
    141 static void dump_node(nvlist_t *, int);
    142 static void dump_prodinfo(di_prom_handle_t, di_node_t, const char **,
    143 				char *, int);
    144 static di_node_t find_node_by_name(di_prom_handle_t, di_node_t, char *);
    145 static int get_propval_by_name(di_prom_handle_t, di_node_t,
    146 				const char *, uchar_t **);
    147 static int dump_compatible(char *, int, di_node_t);
    148 static void dump_pathing_data(int, di_node_t);
    149 static void dump_minor_data(int, di_node_t, di_devlink_handle_t);
    150 static void dump_link_data(int, di_node_t, di_devlink_handle_t);
    151 static int print_composite_string(const char *, char *, int);
    152 static void print_one(nvpair_t *, int);
    153 static int unprintable(char *, int);
    154 static int promopen(int);
    155 static void promclose();
    156 static di_node_t find_target_node(di_node_t);
    157 static void node_display_set(di_node_t);
    158 
    159 void
    160 prtconf_devinfo(void)
    161 {
    162 	struct di_priv_data	fetch;
    163 	di_arg_t		di_arg;
    164 	di_prom_handle_t	prom_hdl = DI_PROM_HANDLE_NIL;
    165 	di_devlink_handle_t	devlink_hdl = NULL;
    166 	di_node_t		root_node;
    167 	uint_t			flag;
    168 	char			*rootpath;
    169 
    170 	dprintf("verbosemode %s\n", opts.o_verbose ? "on" : "off");
    171 
    172 	/* determine what info we need to get from kernel */
    173 	flag = DINFOSUBTREE;
    174 	rootpath = "/";
    175 
    176 	if (opts.o_target) {
    177 		flag |= (DINFOMINOR | DINFOPATH);
    178 	}
    179 
    180 	if (opts.o_pciid) {
    181 		flag |= DINFOPROP;
    182 		if ((prom_hdl = di_prom_init()) == DI_PROM_HANDLE_NIL)
    183 			exit(_error("di_prom_init() failed."));
    184 	}
    185 
    186 	if (opts.o_forcecache) {
    187 		if (dbg.d_forceload) {
    188 			exit(_error(NULL, "option combination not supported"));
    189 		}
    190 		if (strcmp(rootpath, "/") != 0) {
    191 			exit(_error(NULL, "invalid root path for option"));
    192 		}
    193 		flag = DINFOCACHE;
    194 	} else if (opts.o_verbose) {
    195 		flag |= (DINFOPROP | DINFOMINOR |
    196 		    DINFOPRIVDATA | DINFOPATH | DINFOLYR);
    197 	}
    198 
    199 	if (dbg.d_forceload) {
    200 		flag |= DINFOFORCE;
    201 	}
    202 
    203 	if (opts.o_verbose) {
    204 		init_priv_data(&fetch);
    205 		root_node = di_init_impl(rootpath, flag, &fetch);
    206 
    207 		/* get devlink (aka aliases) data */
    208 		if ((devlink_hdl = di_devlink_init(NULL, 0)) == NULL)
    209 			exit(_error("di_devlink_init() failed."));
    210 	} else
    211 		root_node = di_init(rootpath, flag);
    212 
    213 	if (root_node == DI_NODE_NIL) {
    214 		(void) _error(NULL, "devinfo facility not available");
    215 		/* not an error if this isn't the global zone */
    216 		if (getzoneid() == GLOBAL_ZONEID)
    217 			exit(-1);
    218 		else
    219 			exit(0);
    220 	}
    221 
    222 	di_arg.prom_hdl = prom_hdl;
    223 	di_arg.devlink_hdl = devlink_hdl;
    224 
    225 	/*
    226 	 * ...and walk all nodes to report them out...
    227 	 */
    228 	if (dbg.d_bydriver) {
    229 		opts.o_target = 0;
    230 		walk_driver(root_node, &di_arg);
    231 		if (prom_hdl != DI_PROM_HANDLE_NIL)
    232 			di_prom_fini(prom_hdl);
    233 		if (devlink_hdl != NULL)
    234 			(void) di_devlink_fini(&devlink_hdl);
    235 		di_fini(root_node);
    236 		return;
    237 	}
    238 
    239 	if (opts.o_target) {
    240 		di_node_t target_node, node;
    241 
    242 		target_node = find_target_node(root_node);
    243 		if (target_node == DI_NODE_NIL) {
    244 			(void) fprintf(stderr, "%s: "
    245 			    "invalid device path specified\n",
    246 			    opts.o_progname);
    247 			exit(1);
    248 		}
    249 
    250 		/* mark the target node so we display it */
    251 		node_display_set(target_node);
    252 
    253 		if (opts.o_ancestors) {
    254 			/*
    255 			 * mark the ancestors of this node so we display
    256 			 * them as well
    257 			 */
    258 			node = target_node;
    259 			while (node = di_parent_node(node))
    260 				node_display_set(node);
    261 		} else {
    262 			/*
    263 			 * when we display device tree nodes the indentation
    264 			 * level is based off of tree depth.
    265 			 *
    266 			 * here we increment o_target to reflect the
    267 			 * depth of the target node in the tree.  we do
    268 			 * this so that when we calculate the indentation
    269 			 * level we can subtract o_target so that the
    270 			 * target node starts with an indentation of zero.
    271 			 */
    272 			node = target_node;
    273 			while (node = di_parent_node(node))
    274 				opts.o_target++;
    275 		}
    276 
    277 		if (opts.o_children) {
    278 			/*
    279 			 * mark the children of this node so we display
    280 			 * them as well
    281 			 */
    282 			(void) di_walk_node(target_node, DI_WALK_CLDFIRST,
    283 			    (void *)1,
    284 			    (int (*)(di_node_t, void *))
    285 			    node_display_set);
    286 		}
    287 	}
    288 
    289 	(void) di_walk_node(root_node, DI_WALK_CLDFIRST, &di_arg,
    290 	    dump_devs);
    291 
    292 	if (prom_hdl != DI_PROM_HANDLE_NIL)
    293 		di_prom_fini(prom_hdl);
    294 	if (devlink_hdl != NULL)
    295 		(void) di_devlink_fini(&devlink_hdl);
    296 	di_fini(root_node);
    297 }
    298 
    299 /*
    300  * utility routines
    301  */
    302 static int
    303 i_find_target_node(di_node_t node, void *arg)
    304 {
    305 	di_node_t *target = (di_node_t *)arg;
    306 
    307 	if (opts.o_devices_path != NULL) {
    308 		char *path;
    309 
    310 		if ((path = di_devfs_path(node)) == NULL)
    311 			exit(_error("failed to allocate memory"));
    312 
    313 		if (strcmp(opts.o_devices_path, path) == 0) {
    314 			di_devfs_path_free(path);
    315 			*target = node;
    316 			return (DI_WALK_TERMINATE);
    317 		}
    318 
    319 		di_devfs_path_free(path);
    320 	} else if (opts.o_devt != DDI_DEV_T_NONE) {
    321 		di_minor_t	minor = DI_MINOR_NIL;
    322 
    323 		while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
    324 			if (opts.o_devt == di_minor_devt(minor)) {
    325 				*target = node;
    326 				return (DI_WALK_TERMINATE);
    327 			}
    328 		}
    329 	} else {
    330 		/* we should never get here */
    331 		exit(_error(NULL, "internal error"));
    332 	}
    333 	return (DI_WALK_CONTINUE);
    334 }
    335 
    336 static di_node_t
    337 find_target_node(di_node_t root_node)
    338 {
    339 	di_node_t target = DI_NODE_NIL;
    340 
    341 	/* special case to allow displaying of the root node */
    342 	if (opts.o_devices_path != NULL) {
    343 		if (strlen(opts.o_devices_path) == 0)
    344 			return (root_node);
    345 		if (strcmp(opts.o_devices_path, ".") == 0)
    346 			return (root_node);
    347 	}
    348 
    349 	(void) di_walk_node(root_node, DI_WALK_CLDFIRST, &target,
    350 	    i_find_target_node);
    351 	return (target);
    352 }
    353 
    354 #define	NODE_DISPLAY		(1<<0)
    355 
    356 static long
    357 node_display(di_node_t node)
    358 {
    359 	long data = (long)di_node_private_get(node);
    360 	return (data & NODE_DISPLAY);
    361 }
    362 
    363 static void
    364 node_display_set(di_node_t node)
    365 {
    366 	long data = (long)di_node_private_get(node);
    367 	data |= NODE_DISPLAY;
    368 	di_node_private_set(node, (void *)data);
    369 }
    370 
    371 #define	LNODE_DISPLAYED		(1<<0)
    372 
    373 static long
    374 lnode_displayed(di_lnode_t lnode)
    375 {
    376 	long data = (long)di_lnode_private_get(lnode);
    377 	return (data & LNODE_DISPLAYED);
    378 }
    379 
    380 static void
    381 lnode_displayed_set(di_lnode_t lnode)
    382 {
    383 	long data = (long)di_lnode_private_get(lnode);
    384 	data |= LNODE_DISPLAYED;
    385 	di_lnode_private_set(lnode, (void *)data);
    386 }
    387 
    388 static void
    389 lnode_displayed_clear(di_lnode_t lnode)
    390 {
    391 	long data = (long)di_lnode_private_get(lnode);
    392 	data &= ~LNODE_DISPLAYED;
    393 	di_lnode_private_set(lnode, (void *)data);
    394 }
    395 
    396 #define	MINOR_DISPLAYED		(1<<0)
    397 #define	MINOR_PTR		(~(0x3))
    398 
    399 static long
    400 minor_displayed(di_minor_t minor)
    401 {
    402 	long data = (long)di_minor_private_get(minor);
    403 	return (data & MINOR_DISPLAYED);
    404 }
    405 
    406 static void
    407 minor_displayed_set(di_minor_t minor)
    408 {
    409 	long data = (long)di_minor_private_get(minor);
    410 	data |= MINOR_DISPLAYED;
    411 	di_minor_private_set(minor, (void *)data);
    412 }
    413 
    414 static void
    415 minor_displayed_clear(di_minor_t minor)
    416 {
    417 	long data = (long)di_minor_private_get(minor);
    418 	data &= ~MINOR_DISPLAYED;
    419 	di_minor_private_set(minor, (void *)data);
    420 }
    421 
    422 static void *
    423 minor_ptr(di_minor_t minor)
    424 {
    425 	long data = (long)di_minor_private_get(minor);
    426 	return ((void *)(data & MINOR_PTR));
    427 }
    428 
    429 static void
    430 minor_ptr_set(di_minor_t minor, void *ptr)
    431 {
    432 	long data = (long)di_minor_private_get(minor);
    433 	data = (data & ~MINOR_PTR) | (((long)ptr) & MINOR_PTR);
    434 	di_minor_private_set(minor, (void *)data);
    435 }
    436 
    437 /*
    438  * In this comment typed properties are those of type DI_PROP_TYPE_UNDEF_IT,
    439  * DI_PROP_TYPE_BOOLEAN, DI_PROP_TYPE_INT, DI_PROP_TYPE_INT64,
    440  * DI_PROP_TYPE_BYTE, and DI_PROP_TYPE_STRING.
    441  *
    442  * The guessing algorithm is:
    443  * 1. If the property is typed and the type is consistent with the value of
    444  *    the property, then the property is of that type. If the type is not
    445  *    consistent with value of the property, then the type is treated as
    446  *    alien to prtconf.
    447  * 2. If the property is of type DI_PROP_TYPE_UNKNOWN the following steps
    448  *    are carried out.
    449  *    a. If the value of the property is consistent with a string property,
    450  *       the type of the property is DI_PROP_TYPE_STRING.
    451  *    b. Otherwise, if the value of the property is consistent with an integer
    452  *       property, the type of the property is DI_PROP_TYPE_INT.
    453  *    c. Otherwise, the property type is treated as alien to prtconf.
    454  * 3. If the property type is alien to prtconf, then the property value is
    455  *    read by the appropriate routine for untyped properties and the following
    456  *    steps are carried out.
    457  *    a. If the length that the property routine returned is zero, the
    458  *       property is of type DI_PROP_TYPE_BOOLEAN.
    459  *    b. Otherwise, if the length that the property routine returned is
    460  *       positive, then the property value is treated as raw data of type
    461  *       DI_PROP_TYPE_UNKNOWN.
    462  *    c. Otherwise, if the length that the property routine returned is
    463  *       negative, then there is some internal inconsistency and this is
    464  *       treated as an error and no type is determined.
    465  */
    466 static int
    467 prop_type_guess(const dumpops_t *propops, void *prop, void **prop_data,
    468     int *prop_type)
    469 {
    470 	int len, type;
    471 
    472 	type = PROPTYPE(propops)(prop);
    473 	switch (type) {
    474 	case DI_PROP_TYPE_UNDEF_IT:
    475 	case DI_PROP_TYPE_BOOLEAN:
    476 		*prop_data = NULL;
    477 		*prop_type = type;
    478 		return (0);
    479 	case DI_PROP_TYPE_INT:
    480 		len = PROPINTS(propops)(prop, (int **)prop_data);
    481 		break;
    482 	case DI_PROP_TYPE_INT64:
    483 		len = PROPINT64(propops)(prop, (int64_t **)prop_data);
    484 		break;
    485 	case DI_PROP_TYPE_BYTE:
    486 		len = PROPBYTES(propops)(prop, (uchar_t **)prop_data);
    487 		break;
    488 	case DI_PROP_TYPE_STRING:
    489 		len = PROPSTRINGS(propops)(prop, (char **)prop_data);
    490 		break;
    491 	case DI_PROP_TYPE_UNKNOWN:
    492 		len = PROPSTRINGS(propops)(prop, (char **)prop_data);
    493 		if ((len > 0) && ((*(char **)prop_data)[0] != 0)) {
    494 			*prop_type = DI_PROP_TYPE_STRING;
    495 			return (len);
    496 		}
    497 
    498 		len = PROPINTS(propops)(prop, (int **)prop_data);
    499 		type = DI_PROP_TYPE_INT;
    500 
    501 		break;
    502 	default:
    503 		len = -1;
    504 	}
    505 
    506 	if (len > 0) {
    507 		*prop_type = type;
    508 		return (len);
    509 	}
    510 
    511 	len = PROPRAWDATA(propops)(prop, (uchar_t **)prop_data);
    512 	if (len < 0) {
    513 		return (-1);
    514 	} else if (len == 0) {
    515 		*prop_type = DI_PROP_TYPE_BOOLEAN;
    516 		return (0);
    517 	}
    518 
    519 	*prop_type = DI_PROP_TYPE_UNKNOWN;
    520 	return (len);
    521 }
    522 
    523 /*
    524  * Returns 0 if nothing is printed, 1 otherwise
    525  */
    526 static int
    527 dump_prop_list(const dumpops_t *dumpops, const char *name, int ilev,
    528     void *node, dev_t dev, int *compat_printed)
    529 {
    530 	void		*prop = DI_PROP_NIL, *prop_data;
    531 	di_minor_t	minor;
    532 	char		*p;
    533 	int		i, prop_type, nitems;
    534 	dev_t		pdev;
    535 	int		nprop = 0;
    536 
    537 	if (compat_printed)
    538 		*compat_printed = 0;
    539 
    540 	while ((prop = NEXTPROP(dumpops)(node, prop)) != DI_PROP_NIL) {
    541 
    542 		/* Skip properties a dev_t oriented caller is not requesting */
    543 		if (PROPDEVT(dumpops)) {
    544 			pdev = PROPDEVT(dumpops)(prop);
    545 
    546 			if (dev == DDI_DEV_T_ANY) {
    547 				/*
    548 				 * Caller requesting print all properties
    549 				 */
    550 				goto print;
    551 			} else if (dev == DDI_DEV_T_NONE) {
    552 				/*
    553 				 * Caller requesting print of properties
    554 				 * associated with devinfo (not minor).
    555 				 */
    556 				if ((pdev == DDI_DEV_T_ANY) ||
    557 				    (pdev == DDI_DEV_T_NONE))
    558 					goto print;
    559 
    560 				/*
    561 				 * Property has a minor association, see if
    562 				 * we have a minor with this dev_t. If there
    563 				 * is no such minor we print the property now
    564 				 * so it gets displayed.
    565 				 */
    566 				minor = DI_MINOR_NIL;
    567 				while ((minor = di_minor_next((di_node_t)node,
    568 				    minor)) != DI_MINOR_NIL) {
    569 					if (di_minor_devt(minor) == pdev)
    570 						break;
    571 				}
    572 				if (minor == DI_MINOR_NIL)
    573 					goto print;
    574 			} else if (dev == pdev) {
    575 				/*
    576 				 * Caller requesting print of properties
    577 				 * associated with a specific matching minor
    578 				 * node.
    579 				 */
    580 				goto print;
    581 			}
    582 
    583 			/* otherwise skip print */
    584 			continue;
    585 		}
    586 
    587 print:		nitems = prop_type_guess(dumpops, prop, &prop_data, &prop_type);
    588 		if (nitems < 0)
    589 			continue;
    590 
    591 		if (nprop == 0) {
    592 			if (name) {
    593 				indent_to_level(ilev);
    594 				(void) printf("%s properties:\n", name);
    595 			}
    596 			ilev++;
    597 		}
    598 		nprop++;
    599 
    600 		indent_to_level(ilev);
    601 		(void) printf("name='%s' type=", PROPNAME(dumpops)(prop));
    602 
    603 		/* report 'compatible' as processed */
    604 		if (compat_printed &&
    605 		    (strcmp(PROPNAME(dumpops)(prop), "compatible") == 0))
    606 			*compat_printed = 1;
    607 
    608 		switch (prop_type) {
    609 		case DI_PROP_TYPE_UNDEF_IT:
    610 			(void) printf("undef");
    611 			break;
    612 		case DI_PROP_TYPE_BOOLEAN:
    613 			(void) printf("boolean");
    614 			break;
    615 		case DI_PROP_TYPE_INT:
    616 			(void) printf("int");
    617 			break;
    618 		case DI_PROP_TYPE_INT64:
    619 			(void) printf("int64");
    620 			break;
    621 		case DI_PROP_TYPE_BYTE:
    622 			(void) printf("byte");
    623 			break;
    624 		case DI_PROP_TYPE_STRING:
    625 			(void) printf("string");
    626 			break;
    627 		case DI_PROP_TYPE_UNKNOWN:
    628 			(void) printf("unknown");
    629 			break;
    630 		default:
    631 			/* Should never be here */
    632 			(void) printf("0x%x", prop_type);
    633 		}
    634 
    635 		if (nitems != 0)
    636 			(void) printf(" items=%i", nitems);
    637 
    638 		/* print the major and minor numbers for a device property */
    639 		if (PROPDEVT(dumpops)) {
    640 			if ((pdev == DDI_DEV_T_NONE) ||
    641 			    (pdev == DDI_DEV_T_ANY)) {
    642 				(void) printf(" dev=none");
    643 			} else {
    644 				(void) printf(" dev=(%u,%u)",
    645 				    (uint_t)major(pdev), (uint_t)minor(pdev));
    646 			}
    647 		}
    648 
    649 		(void) putchar('\n');
    650 
    651 		if (nitems == 0)
    652 			continue;
    653 
    654 		indent_to_level(ilev);
    655 
    656 		(void) printf("    value=");
    657 
    658 		switch (prop_type) {
    659 		case DI_PROP_TYPE_INT:
    660 			for (i = 0; i < nitems - 1; i++)
    661 				(void) printf("%8.8x.", ((int *)prop_data)[i]);
    662 			(void) printf("%8.8x", ((int *)prop_data)[i]);
    663 			break;
    664 		case DI_PROP_TYPE_INT64:
    665 			for (i = 0; i < nitems - 1; i++)
    666 				(void) printf("%16.16llx.",
    667 				    ((long long *)prop_data)[i]);
    668 			(void) printf("%16.16llx", ((long long *)prop_data)[i]);
    669 			break;
    670 		case DI_PROP_TYPE_STRING:
    671 			p = (char *)prop_data;
    672 			for (i = 0; i < nitems - 1; i++) {
    673 				(void) printf("'%s' + ", p);
    674 				p += strlen(p) + 1;
    675 			}
    676 			(void) printf("'%s'", p);
    677 			break;
    678 		default:
    679 			for (i = 0; i < nitems - 1; i++)
    680 				(void) printf("%2.2x.",
    681 				    ((uint8_t *)prop_data)[i]);
    682 			(void) printf("%2.2x", ((uint8_t *)prop_data)[i]);
    683 		}
    684 
    685 		(void) putchar('\n');
    686 	}
    687 
    688 	return (nprop ? 1 : 0);
    689 }
    690 
    691 /*
    692  * walk_driver is a debugging facility.
    693  */
    694 static void
    695 walk_driver(di_node_t root, di_arg_t *di_arg)
    696 {
    697 	di_node_t node;
    698 
    699 	node = di_drv_first_node(dbg.d_drivername, root);
    700 
    701 	while (node != DI_NODE_NIL) {
    702 		(void) dump_devs(node, di_arg);
    703 		node = di_drv_next_node(node);
    704 	}
    705 }
    706 
    707 /*
    708  * print out information about this node, returns appropriate code.
    709  */
    710 /*ARGSUSED1*/
    711 static int
    712 dump_devs(di_node_t node, void *arg)
    713 {
    714 	di_arg_t		*di_arg = arg;
    715 	di_devlink_handle_t	devlink_hdl = di_arg->devlink_hdl;
    716 	int			ilev = 0;	/* indentation level */
    717 	char			*driver_name;
    718 	di_node_t		root_node, tmp;
    719 	int			compat_printed;
    720 	int			printed;
    721 
    722 	if (dbg.d_debug) {
    723 		char *path = di_devfs_path(node);
    724 		dprintf("Dump node %s\n", path);
    725 		di_devfs_path_free(path);
    726 	}
    727 
    728 	if (dbg.d_bydriver) {
    729 		ilev = 1;
    730 	} else {
    731 		/* figure out indentation level */
    732 		tmp = node;
    733 		while ((tmp = di_parent_node(tmp)) != DI_NODE_NIL)
    734 			ilev++;
    735 
    736 		if (opts.o_target && !opts.o_ancestors) {
    737 			ilev -= opts.o_target - 1;
    738 		}
    739 	}
    740 
    741 	if (opts.o_target && !node_display(node)) {
    742 		/*
    743 		 * if we're only displaying certain nodes and this one
    744 		 * isn't flagged, skip it.
    745 		 */
    746 		return (DI_WALK_CONTINUE);
    747 	}
    748 
    749 	indent_to_level(ilev);
    750 
    751 	(void) printf("%s", di_node_name(node));
    752 	if (opts.o_pciid)
    753 		(void) print_pciid(node, di_arg->prom_hdl);
    754 
    755 	/*
    756 	 * if this node does not have an instance number or is the
    757 	 * root node (1229946), we don't print an instance number
    758 	 */
    759 	root_node = tmp = node;
    760 	while ((tmp = di_parent_node(tmp)) != DI_NODE_NIL)
    761 		root_node = tmp;
    762 	if ((di_instance(node) >= 0) && (node != root_node))
    763 		(void) printf(", instance #%d", di_instance(node));
    764 
    765 	if (opts.o_drv_name) {
    766 		driver_name = di_driver_name(node);
    767 		if (driver_name != NULL)
    768 			(void) printf(" (driver name: %s)", driver_name);
    769 	} else if (di_retired(node)) {
    770 		(void) printf(" (retired)");
    771 	} else if (di_state(node) & DI_DRIVER_DETACHED)
    772 		(void) printf(" (driver not attached)");
    773 	(void) printf("\n");
    774 
    775 	if (opts.o_verbose)  {
    776 		if (dump_prop_list(&sysprop_dumpops, "System", ilev + 1,
    777 		    node, DDI_DEV_T_ANY, NULL)) {
    778 			(void) dump_prop_list(&globprop_dumpops, NULL, ilev + 1,
    779 			    node, DDI_DEV_T_ANY, NULL);
    780 		} else {
    781 			(void) dump_prop_list(&globprop_dumpops,
    782 			    "System software", ilev + 1,
    783 			    node, DDI_DEV_T_ANY, NULL);
    784 		}
    785 		(void) dump_prop_list(&drvprop_dumpops, "Driver", ilev + 1,
    786 		    node, DDI_DEV_T_NONE, NULL);
    787 
    788 		printed = dump_prop_list(&hwprop_dumpops, "Hardware",
    789 		    ilev + 1, node, DDI_DEV_T_ANY, &compat_printed);
    790 
    791 		/* Ensure that 'compatible' is printed under Hardware header */
    792 		if (!compat_printed)
    793 			(void) dump_compatible(printed ? NULL : "Hardware",
    794 			    ilev + 1, node);
    795 
    796 		dump_priv_data(ilev + 1, node);
    797 		dump_pathing_data(ilev + 1, node);
    798 		dump_link_data(ilev + 1, node, devlink_hdl);
    799 		dump_minor_data(ilev + 1, node, devlink_hdl);
    800 	}
    801 
    802 	if (opts.o_target)
    803 		return (DI_WALK_CONTINUE);
    804 
    805 	if (!opts.o_pseudodevs && (strcmp(di_node_name(node), "pseudo") == 0))
    806 		return (DI_WALK_PRUNECHILD);
    807 
    808 	return (DI_WALK_CONTINUE);
    809 }
    810 
    811 /* _error([no_perror, ] fmt [, arg ...]) */
    812 static int
    813 _error(const char *opt_noperror, ...)
    814 {
    815 	int saved_errno;
    816 	va_list ap;
    817 	int no_perror = 0;
    818 	const char *fmt;
    819 
    820 	saved_errno = errno;
    821 
    822 	(void) fprintf(stderr, "%s: ", opts.o_progname);
    823 
    824 	va_start(ap, opt_noperror);
    825 	if (opt_noperror == NULL) {
    826 		no_perror = 1;
    827 		fmt = va_arg(ap, char *);
    828 	} else
    829 		fmt = opt_noperror;
    830 	(void) vfprintf(stderr, fmt, ap);
    831 	va_end(ap);
    832 
    833 	if (no_perror)
    834 		(void) fprintf(stderr, "\n");
    835 	else {
    836 		(void) fprintf(stderr, ": ");
    837 		errno = saved_errno;
    838 		perror("");
    839 	}
    840 
    841 	return (-1);
    842 }
    843 
    844 
    845 /*
    846  * The rest of the routines handle printing the raw prom devinfo (-p option).
    847  *
    848  * 128 is the size of the largest (currently) property name
    849  * 16k - MAXNAMESZ - sizeof (int) is the size of the largest
    850  * (currently) property value that is allowed.
    851  * the sizeof (uint_t) is from struct openpromio
    852  */
    853 
    854 #define	MAXNAMESZ	128
    855 #define	MAXVALSIZE	(16384 - MAXNAMESZ - sizeof (uint_t))
    856 #define	BUFSIZE		(MAXNAMESZ + MAXVALSIZE + sizeof (uint_t))
    857 typedef union {
    858 	char buf[BUFSIZE];
    859 	struct openpromio opp;
    860 } Oppbuf;
    861 
    862 static int prom_fd;
    863 static uchar_t *prom_snapshot;
    864 
    865 static int
    866 is_openprom(void)
    867 {
    868 	Oppbuf	oppbuf;
    869 	struct openpromio *opp = &(oppbuf.opp);
    870 	unsigned int i;
    871 
    872 	opp->oprom_size = MAXVALSIZE;
    873 	if (ioctl(prom_fd, OPROMGETCONS, opp) < 0)
    874 		exit(_error("OPROMGETCONS"));
    875 
    876 	i = (unsigned int)((unsigned char)opp->oprom_array[0]);
    877 	return ((i & OPROMCONS_OPENPROM) == OPROMCONS_OPENPROM);
    878 }
    879 
    880 int
    881 do_prominfo(void)
    882 {
    883 	uint_t arg = opts.o_verbose;
    884 
    885 	if (promopen(O_RDONLY))  {
    886 		exit(_error("openeepr device open failed"));
    887 	}
    888 
    889 	if (is_openprom() == 0)  {
    890 		(void) fprintf(stderr, "System architecture does not "
    891 		    "support this option of this command.\n");
    892 		return (1);
    893 	}
    894 
    895 	/* OPROMSNAPSHOT returns size in arg */
    896 	if (ioctl(prom_fd, OPROMSNAPSHOT, &arg) < 0)
    897 		exit(_error("OPROMSNAPSHOT"));
    898 
    899 	if (arg == 0)
    900 		return (1);
    901 
    902 	if ((prom_snapshot = malloc(arg)) == NULL)
    903 		exit(_error("failed to allocate memory"));
    904 
    905 	/* copy out the snapshot for printing */
    906 	/*LINTED*/
    907 	*(uint_t *)prom_snapshot = arg;
    908 	if (ioctl(prom_fd, OPROMCOPYOUT, prom_snapshot) < 0)
    909 		exit(_error("OPROMCOPYOUT"));
    910 
    911 	promclose();
    912 
    913 	/* print out information */
    914 	walk(prom_snapshot, arg, 0);
    915 	free(prom_snapshot);
    916 
    917 	return (0);
    918 }
    919 
    920 static void
    921 walk(uchar_t *buf, uint_t size, int level)
    922 {
    923 	int error;
    924 	nvlist_t *nvl, *cnvl;
    925 	nvpair_t *child = NULL;
    926 	uchar_t *cbuf = NULL;
    927 	uint_t csize;
    928 
    929 	/* Expand to an nvlist */
    930 	if (nvlist_unpack((char *)buf, size, &nvl, 0))
    931 		exit(_error("error processing snapshot"));
    932 
    933 	/* print current node */
    934 	dump_node(nvl, level);
    935 
    936 	/* print children */
    937 	error = nvlist_lookup_byte_array(nvl, "@child", &cbuf, &csize);
    938 	if ((error == ENOENT) || (cbuf == NULL))
    939 		return;		/* no child exists */
    940 
    941 	if (error || nvlist_unpack((char *)cbuf, csize, &cnvl, 0))
    942 		exit(_error("error processing snapshot"));
    943 
    944 	while (child = nvlist_next_nvpair(cnvl, child)) {
    945 		char *name = nvpair_name(child);
    946 		data_type_t type = nvpair_type(child);
    947 		uchar_t *nodebuf;
    948 		uint_t nodesize;
    949 		if (strcmp("node", name) != 0) {
    950 			dprintf("unexpected nvpair name %s != name\n", name);
    951 			continue;
    952 		}
    953 		if (type != DATA_TYPE_BYTE_ARRAY) {
    954 			dprintf("unexpected nvpair type %d, not byte array \n",
    955 			    type);
    956 			continue;
    957 		}
    958 
    959 		(void) nvpair_value_byte_array(child,
    960 		    (uchar_t **)&nodebuf, &nodesize);
    961 		walk(nodebuf, nodesize, level + 1);
    962 	}
    963 
    964 	nvlist_free(nvl);
    965 }
    966 
    967 /*
    968  * Print all properties and values
    969  */
    970 static void
    971 dump_node(nvlist_t *nvl, int level)
    972 {
    973 	int id = 0;
    974 	char *name = NULL;
    975 	nvpair_t *nvp = NULL;
    976 
    977 	indent_to_level(level);
    978 	(void) printf("Node");
    979 	if (!opts.o_verbose) {
    980 		if (nvlist_lookup_string(nvl, "name", &name))
    981 			(void) printf("data not available");
    982 		else
    983 			(void) printf(" '%s'", name);
    984 		(void) putchar('\n');
    985 		return;
    986 	}
    987 	(void) nvlist_lookup_int32(nvl, "@nodeid", &id);
    988 	(void) printf(" %#08x\n", id);
    989 
    990 	while (nvp = nvlist_next_nvpair(nvl, nvp)) {
    991 		name = nvpair_name(nvp);
    992 		if (name[0] == '@')
    993 			continue;
    994 
    995 		print_one(nvp, level + 1);
    996 	}
    997 	(void) putchar('\n');
    998 }
    999 
   1000 static const char *
   1001 path_state_name(di_path_state_t st)
   1002 {
   1003 	switch (st) {
   1004 		case DI_PATH_STATE_ONLINE:
   1005 			return ("online");
   1006 		case DI_PATH_STATE_STANDBY:
   1007 			return ("standby");
   1008 		case DI_PATH_STATE_OFFLINE:
   1009 			return ("offline");
   1010 		case DI_PATH_STATE_FAULT:
   1011 			return ("faulted");
   1012 	}
   1013 	return ("unknown");
   1014 }
   1015 
   1016 /*
   1017  * Print all phci's each client is connected to.
   1018  */
   1019 static void
   1020 dump_pathing_data(int ilev, di_node_t node)
   1021 {
   1022 	di_path_t	pi = DI_PATH_NIL;
   1023 	di_node_t	phci_node;
   1024 	char		*phci_path;
   1025 	int		path_instance;
   1026 	int		firsttime = 1;
   1027 
   1028 	if (node == DI_PATH_NIL)
   1029 		return;
   1030 
   1031 	while ((pi = di_path_client_next_path(node, pi)) != DI_PATH_NIL) {
   1032 
   1033 		/* It is not really a path if we failed to capture the pHCI */
   1034 		phci_node = di_path_phci_node(pi);
   1035 		if (phci_node == DI_NODE_NIL)
   1036 			continue;
   1037 
   1038 		/* Print header for the first path */
   1039 		if (firsttime) {
   1040 			indent_to_level(ilev);
   1041 			firsttime = 0;
   1042 			ilev++;
   1043 			(void) printf("Paths from multipath bus adapters:\n");
   1044 		}
   1045 
   1046 		/*
   1047 		 * Print the path instance and full "pathinfo" path, which is
   1048 		 * the same as the /devices devifo path had the device been
   1049 		 * enumerated under pHCI.
   1050 		 */
   1051 		phci_path = di_devfs_path(phci_node);
   1052 		if (phci_path) {
   1053 			path_instance = di_path_instance(pi);
   1054 			if (path_instance > 0) {
   1055 				indent_to_level(ilev);
   1056 				(void) printf("Path %d: %s/%s@%s\n",
   1057 				    path_instance, phci_path,
   1058 				    di_node_name(node),
   1059 				    di_path_bus_addr(pi));
   1060 			}
   1061 			di_devfs_path_free(phci_path);
   1062 		}
   1063 
   1064 		/* print phci driver, instance, and path state information */
   1065 		indent_to_level(ilev);
   1066 		(void) printf("%s#%d (%s)\n", di_driver_name(phci_node),
   1067 		    di_instance(phci_node), path_state_name(di_path_state(pi)));
   1068 
   1069 		(void) dump_prop_list(&pathprop_dumpops, NULL, ilev + 1,
   1070 		    pi, DDI_DEV_T_ANY, NULL);
   1071 	}
   1072 }
   1073 
   1074 static int
   1075 dump_minor_data_links(di_devlink_t devlink, void *arg)
   1076 {
   1077 	int ilev = (intptr_t)arg;
   1078 	indent_to_level(ilev);
   1079 	(void) printf("dev_link=%s\n", di_devlink_path(devlink));
   1080 	return (DI_WALK_CONTINUE);
   1081 }
   1082 
   1083 static void
   1084 dump_minor_data_paths(int ilev, di_minor_t minor,
   1085     di_devlink_handle_t devlink_hdl)
   1086 {
   1087 	char	*path, *type;
   1088 	int	spec_type;
   1089 
   1090 	/* get the path to the device and the minor node name */
   1091 	if ((path = di_devfs_minor_path(minor)) == NULL)
   1092 		exit(_error("failed to allocate memory"));
   1093 
   1094 	/* display the path to this minor node */
   1095 	indent_to_level(ilev);
   1096 	(void) printf("dev_path=%s\n", path);
   1097 
   1098 	if (devlink_hdl != NULL) {
   1099 
   1100 		/* get the device minor node information */
   1101 		spec_type = di_minor_spectype(minor);
   1102 		switch (di_minor_type(minor)) {
   1103 			case DDM_MINOR:
   1104 				type = "minor";
   1105 				break;
   1106 			case DDM_ALIAS:
   1107 				type = "alias";
   1108 				break;
   1109 			case DDM_DEFAULT:
   1110 				type = "default";
   1111 				break;
   1112 			case DDM_INTERNAL_PATH:
   1113 				type = "internal";
   1114 				break;
   1115 			default:
   1116 				type = "unknown";
   1117 				break;
   1118 		}
   1119 
   1120 		/* display the device minor node information */
   1121 		indent_to_level(ilev + 1);
   1122 		(void) printf("spectype=%s type=%s\n",
   1123 		    (spec_type == S_IFBLK) ? "blk" : "chr", type);
   1124 
   1125 		/* display all the devlinks for this device minor node */
   1126 		(void) di_devlink_walk(devlink_hdl, NULL, path,
   1127 		    0, (void *)(intptr_t)(ilev + 1), dump_minor_data_links);
   1128 	}
   1129 
   1130 	di_devfs_path_free(path);
   1131 }
   1132 
   1133 static void
   1134 create_minor_list(di_node_t node)
   1135 {
   1136 	di_minor_t	minor, minor_head, minor_tail, minor_prev, minor_walk;
   1137 	int		major;
   1138 
   1139 	/* if there are no minor nodes, bail */
   1140 	if (di_minor_next(node, DI_MINOR_NIL) == DI_MINOR_NIL)
   1141 		return;
   1142 
   1143 	/*
   1144 	 * here we want to create lists of minor nodes with the same
   1145 	 * dev_t.  to do this we first sort all the minor nodes by devt.
   1146 	 *
   1147 	 * the algorithm used here is a bubble sort, so performance sucks.
   1148 	 * but it's probably ok here because most device instances don't
   1149 	 * have that many minor nodes.  also we're doing this as we're
   1150 	 * displaying each node so it doesn't look like we're pausing
   1151 	 * output for a long time.
   1152 	 */
   1153 	major = di_driver_major(node);
   1154 	minor_head = minor_tail = minor = DI_MINOR_NIL;
   1155 	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
   1156 		dev_t	dev = di_minor_devt(minor);
   1157 
   1158 		/* skip /pseudo/clone@0 minor nodes */
   1159 		if (major != major(dev))
   1160 			continue;
   1161 
   1162 		minor_ptr_set(minor, DI_MINOR_NIL);
   1163 		if (minor_head == DI_MINOR_NIL) {
   1164 			/* this is the first minor node we're looking at */
   1165 			minor_head = minor_tail = minor;
   1166 			continue;
   1167 		}
   1168 
   1169 		/*
   1170 		 * if the new dev is less than the old dev, update minor_head
   1171 		 * so it points to the beginning of the list.  ie it points
   1172 		 * to the node with the lowest dev value
   1173 		 */
   1174 		if (dev <= di_minor_devt(minor_head)) {
   1175 			minor_ptr_set(minor, minor_head);
   1176 			minor_head = minor;
   1177 			continue;
   1178 		}
   1179 
   1180 		minor_prev = minor_head;
   1181 		minor_walk = minor_ptr(minor_head);
   1182 		while ((minor_walk != DI_MINOR_NIL) &&
   1183 		    (dev > di_minor_devt(minor_walk))) {
   1184 			minor_prev = minor_walk;
   1185 			minor_walk = minor_ptr(minor_walk);
   1186 		}
   1187 		minor_ptr_set(minor, minor_walk);
   1188 		minor_ptr_set(minor_prev, minor);
   1189 		if (minor_walk == NULL)
   1190 			minor_tail = minor;
   1191 	}
   1192 
   1193 	/* check if there were any non /pseudo/clone@0 nodes.  if not, bail */
   1194 	if (minor_head == DI_MINOR_NIL)
   1195 		return;
   1196 
   1197 	/*
   1198 	 * now that we have a list of minor nodes sorted by devt
   1199 	 * we walk through the list and break apart the entire list
   1200 	 * to create circular lists of minor nodes with matching devts.
   1201 	 */
   1202 	minor_prev = minor_head;
   1203 	minor_walk = minor_ptr(minor_head);
   1204 	while (minor_walk != DI_MINOR_NIL) {
   1205 		if (di_minor_devt(minor_prev) != di_minor_devt(minor_walk)) {
   1206 			minor_ptr_set(minor_prev, minor_head);
   1207 			minor_head = minor_walk;
   1208 		}
   1209 		minor_prev = minor_walk;
   1210 		minor_walk = minor_ptr(minor_walk);
   1211 	}
   1212 	minor_ptr_set(minor_tail, minor_head);
   1213 }
   1214 
   1215 static void
   1216 link_lnode_disp(di_link_t link, uint_t endpoint, int ilev,
   1217     di_devlink_handle_t devlink_hdl)
   1218 {
   1219 	di_lnode_t	lnode;
   1220 	char		*name, *path;
   1221 	int		displayed_path, spec_type;
   1222 	di_node_t	node = DI_NODE_NIL;
   1223 	dev_t		devt = DDI_DEV_T_NONE;
   1224 
   1225 	lnode = di_link_to_lnode(link, endpoint);
   1226 
   1227 	indent_to_level(ilev);
   1228 	name = di_lnode_name(lnode);
   1229 	spec_type = di_link_spectype(link);
   1230 
   1231 	(void) printf("mod=%s", name);
   1232 
   1233 	/*
   1234 	 * if we're displaying the source of a link, we should display
   1235 	 * the target access mode.  (either block or char.)
   1236 	 */
   1237 	if (endpoint == DI_LINK_SRC)
   1238 		(void) printf(" accesstype=%s",
   1239 		    (spec_type == S_IFBLK) ? "blk" : "chr");
   1240 
   1241 	/*
   1242 	 * check if the lnode is bound to a specific device
   1243 	 * minor node (i.e.  if it's bound to a dev_t) and
   1244 	 * if so display the dev_t value and any possible
   1245 	 * minor node pathing information.
   1246 	 */
   1247 	displayed_path = 0;
   1248 	if (di_lnode_devt(lnode, &devt) == 0) {
   1249 		di_minor_t	minor = DI_MINOR_NIL;
   1250 
   1251 		(void) printf(" dev=(%u,%u)\n",
   1252 		    (uint_t)major(devt), (uint_t)minor(devt));
   1253 
   1254 		/* display paths to the src devt minor node */
   1255 		while (minor = di_minor_next(node, minor)) {
   1256 			if (devt != di_minor_devt(minor))
   1257 				continue;
   1258 
   1259 			if ((endpoint == DI_LINK_TGT) &&
   1260 			    (spec_type != di_minor_spectype(minor)))
   1261 				continue;
   1262 
   1263 			dump_minor_data_paths(ilev + 1, minor, devlink_hdl);
   1264 			displayed_path = 1;
   1265 		}
   1266 	} else {
   1267 		(void) printf("\n");
   1268 	}
   1269 
   1270 	if (displayed_path)
   1271 		return;
   1272 
   1273 	/*
   1274 	 * This device lnode is not did not have any minor node
   1275 	 * pathing information so display the path to device node.
   1276 	 */
   1277 	node = di_lnode_devinfo(lnode);
   1278 	if ((path = di_devfs_path(node)) == NULL)
   1279 		exit(_error("failed to allocate memory"));
   1280 
   1281 	indent_to_level(ilev + 1);
   1282 	(void) printf("dev_path=%s\n", path);
   1283 	di_devfs_path_free(path);
   1284 }
   1285 
   1286 static void
   1287 dump_minor_link_data(int ilev, di_node_t node, dev_t devt,
   1288     di_devlink_handle_t devlink_hdl)
   1289 {
   1290 	int		first = 1;
   1291 	di_link_t	link;
   1292 
   1293 	link = DI_LINK_NIL;
   1294 	while (link = di_link_next_by_node(node, link, DI_LINK_TGT)) {
   1295 		di_lnode_t	tgt_lnode;
   1296 		dev_t		tgt_devt = DDI_DEV_T_NONE;
   1297 
   1298 		tgt_lnode = di_link_to_lnode(link, DI_LINK_TGT);
   1299 
   1300 		if (di_lnode_devt(tgt_lnode, &tgt_devt) != 0)
   1301 			continue;
   1302 
   1303 		if (devt != tgt_devt)
   1304 			continue;
   1305 
   1306 		if (first) {
   1307 			first = 0;
   1308 			indent_to_level(ilev);
   1309 			(void) printf("Device Minor Layered Under:\n");
   1310 		}
   1311 
   1312 		/* displayed this lnode */
   1313 		lnode_displayed_set(tgt_lnode);
   1314 		link_lnode_disp(link, DI_LINK_SRC, ilev + 1, devlink_hdl);
   1315 	}
   1316 
   1317 	link = DI_LINK_NIL;
   1318 	while (link = di_link_next_by_node(node, link, DI_LINK_SRC)) {
   1319 		di_lnode_t	src_lnode;
   1320 		dev_t		src_devt = DDI_DEV_T_NONE;
   1321 
   1322 		src_lnode = di_link_to_lnode(link, DI_LINK_SRC);
   1323 
   1324 		if (di_lnode_devt(src_lnode, &src_devt) != 0)
   1325 			continue;
   1326 
   1327 		if (devt != src_devt)
   1328 			continue;
   1329 
   1330 		if (first) {
   1331 			first = 0;
   1332 			indent_to_level(ilev);
   1333 			(void) printf("Device Minor Layered Over:\n");
   1334 		}
   1335 
   1336 		/* displayed this lnode */
   1337 		lnode_displayed_set(src_lnode);
   1338 		link_lnode_disp(link, DI_LINK_TGT, ilev + 1, devlink_hdl);
   1339 	}
   1340 }
   1341 
   1342 static void
   1343 dump_minor_data(int ilev, di_node_t node, di_devlink_handle_t devlink_hdl)
   1344 {
   1345 	di_minor_t	minor, minor_next;
   1346 	di_lnode_t	lnode;
   1347 	di_link_t	link;
   1348 	int		major, firstminor = 1;
   1349 
   1350 	/*
   1351 	 * first go through and mark all lnodes and minor nodes for this
   1352 	 * node as undisplayed
   1353 	 */
   1354 	lnode = DI_LNODE_NIL;
   1355 	while (lnode = di_lnode_next(node, lnode))
   1356 		lnode_displayed_clear(lnode);
   1357 	minor = DI_MINOR_NIL;
   1358 	while (minor = di_minor_next(node, minor)) {
   1359 		minor_displayed_clear(minor);
   1360 	}
   1361 
   1362 	/*
   1363 	 * when we display the minor nodes we want to coalesce nodes
   1364 	 * that have the same dev_t.  we do this by creating circular
   1365 	 * lists of minor nodes with the same devt.
   1366 	 */
   1367 	create_minor_list(node);
   1368 
   1369 	/* now we display the driver defined minor nodes */
   1370 	major = di_driver_major(node);
   1371 	minor = DI_MINOR_NIL;
   1372 	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
   1373 		dev_t	devt;
   1374 
   1375 		/*
   1376 		 * skip /pseudo/clone@0 minor nodes.
   1377 		 * these are only created for DLPIv2 network devices.
   1378 		 * since these minor nodes are associated with a driver
   1379 		 * and are only bound to a device instance after they
   1380 		 * are opened and attached we don't print them out
   1381 		 * here.
   1382 		 */
   1383 		devt = di_minor_devt(minor);
   1384 		if (major != major(devt))
   1385 			continue;
   1386 
   1387 		/* skip nodes that may have already been displayed */
   1388 		if (minor_displayed(minor))
   1389 			continue;
   1390 
   1391 		if (firstminor) {
   1392 			firstminor = 0;
   1393 			indent_to_level(ilev++);
   1394 			(void) printf("Device Minor Nodes:\n");
   1395 		}
   1396 
   1397 		/* display the device minor node information */
   1398 		indent_to_level(ilev);
   1399 		(void) printf("dev=(%u,%u)\n",
   1400 		    (uint_t)major(devt), (uint_t)minor(devt));
   1401 
   1402 		minor_next = minor;
   1403 		do {
   1404 			/* display device minor node path info */
   1405 			minor_displayed_set(minor_next);
   1406 			dump_minor_data_paths(ilev + 1, minor_next,
   1407 			    devlink_hdl);
   1408 
   1409 			/* get a pointer to the next node */
   1410 			minor_next = minor_ptr(minor_next);
   1411 		} while (minor_next != minor);
   1412 
   1413 		/* display who has this device minor node open */
   1414 		dump_minor_link_data(ilev + 1, node, devt, devlink_hdl);
   1415 
   1416 		/* display properties associated with this devt */
   1417 		(void) dump_prop_list(&drvprop_dumpops, "Minor",
   1418 		    ilev + 1, node, devt, NULL);
   1419 	}
   1420 
   1421 	/*
   1422 	 * now go through all the target lnodes for this node and
   1423 	 * if they haven't yet been displayed, display them now.
   1424 	 *
   1425 	 * this happens in the case of clone opens when an "official"
   1426 	 * minor node does not exist for the opened devt
   1427 	 */
   1428 	link = DI_LINK_NIL;
   1429 	while (link = di_link_next_by_node(node, link, DI_LINK_TGT)) {
   1430 		dev_t		devt;
   1431 
   1432 		lnode = di_link_to_lnode(link, DI_LINK_TGT);
   1433 
   1434 		/* if we've already displayed this target lnode, skip it */
   1435 		if (lnode_displayed(lnode))
   1436 			continue;
   1437 
   1438 		if (firstminor) {
   1439 			firstminor = 0;
   1440 			indent_to_level(ilev++);
   1441 			(void) printf("Device Minor Nodes:\n");
   1442 		}
   1443 
   1444 		/* display the device minor node information */
   1445 		indent_to_level(ilev);
   1446 		(void) di_lnode_devt(lnode, &devt);
   1447 		(void) printf("dev=(%u,%u)\n",
   1448 		    (uint_t)major(devt), (uint_t)minor(devt));
   1449 
   1450 		indent_to_level(ilev + 1);
   1451 		(void) printf("dev_path=<clone>\n");
   1452 
   1453 		/* display who has this cloned device minor node open */
   1454 		dump_minor_link_data(ilev + 1, node, devt, devlink_hdl);
   1455 
   1456 		/* mark node as displayed */
   1457 		lnode_displayed_set(lnode);
   1458 	}
   1459 }
   1460 
   1461 static void
   1462 dump_link_data(int ilev, di_node_t node, di_devlink_handle_t devlink_hdl)
   1463 {
   1464 	int		first = 1;
   1465 	di_link_t	link;
   1466 
   1467 	link = DI_LINK_NIL;
   1468 	while (link = di_link_next_by_node(node, link, DI_LINK_SRC)) {
   1469 		di_lnode_t	src_lnode;
   1470 		dev_t		src_devt = DDI_DEV_T_NONE;
   1471 
   1472 		src_lnode = di_link_to_lnode(link, DI_LINK_SRC);
   1473 
   1474 		/*
   1475 		 * here we only want to print out layering information
   1476 		 * if we are the source and our source lnode is not
   1477 		 * associated with any particular dev_t.  (which means
   1478 		 * we won't display this link while dumping minor node
   1479 		 * info.)
   1480 		 */
   1481 		if (di_lnode_devt(src_lnode, &src_devt) != -1)
   1482 			continue;
   1483 
   1484 		if (first) {
   1485 			first = 0;
   1486 			indent_to_level(ilev);
   1487 			(void) printf("Device Layered Over:\n");
   1488 		}
   1489 
   1490 		/* displayed this lnode */
   1491 		link_lnode_disp(link, DI_LINK_TGT, ilev + 1, devlink_hdl);
   1492 	}
   1493 }
   1494 
   1495 /*
   1496  * certain 'known' property names may contain 'composite' strings.
   1497  * Handle them here, and print them as 'string1' + 'string2' ...
   1498  */
   1499 static int
   1500 print_composite_string(const char *var, char *value, int size)
   1501 {
   1502 	char *p, *q;
   1503 	char *firstp;
   1504 
   1505 	if ((strcmp(var, "version") != 0) &&
   1506 	    (strcmp(var, "compatible") != 0))
   1507 		return (0);	/* Not a known composite string */
   1508 
   1509 	/*
   1510 	 * Verify that each string in the composite string is non-NULL,
   1511 	 * is within the bounds of the property length, and contains
   1512 	 * printable characters or white space. Otherwise let the
   1513 	 * caller deal with it.
   1514 	 */
   1515 	for (firstp = p = value; p < (value + size); p += strlen(p) + 1) {
   1516 		if (strlen(p) == 0)
   1517 			return (0);		/* NULL string */
   1518 		for (q = p; *q; q++) {
   1519 			if (!(isascii(*q) && (isprint(*q) || isspace(*q))))
   1520 				return (0);	/* Not printable or space */
   1521 		}
   1522 		if (q > (firstp + size))
   1523 			return (0);		/* Out of bounds */
   1524 	}
   1525 
   1526 	for (firstp = p = value; p < (value + size); p += strlen(p) + 1) {
   1527 		if (p == firstp)
   1528 			(void) printf("'%s'", p);
   1529 		else
   1530 			(void) printf(" + '%s'", p);
   1531 	}
   1532 	(void) putchar('\n');
   1533 	return (1);
   1534 }
   1535 
   1536 /*
   1537  * Print one property and its value. Handle the verbose case.
   1538  */
   1539 static void
   1540 print_one(nvpair_t *nvp, int level)
   1541 {
   1542 	int i;
   1543 	int endswap = 0;
   1544 	uint_t valsize;
   1545 	char *value;
   1546 	char *var = nvpair_name(nvp);
   1547 
   1548 	indent_to_level(level);
   1549 	(void) printf("%s: ", var);
   1550 
   1551 	switch (nvpair_type(nvp)) {
   1552 	case DATA_TYPE_BOOLEAN:
   1553 		(void) printf(" \n");
   1554 		return;
   1555 	case DATA_TYPE_BYTE_ARRAY:
   1556 		if (nvpair_value_byte_array(nvp, (uchar_t **)&value,
   1557 		    &valsize)) {
   1558 			(void) printf("data not available.\n");
   1559 			return;
   1560 		}
   1561 		valsize--;	/* take out null added by driver */
   1562 
   1563 		/*
   1564 		 * Do not print valsize > MAXVALSIZE, to be compatible
   1565 		 * with old behavior. E.g. intel's eisa-nvram property
   1566 		 * has a size of 65 K.
   1567 		 */
   1568 		if (valsize > MAXVALSIZE) {
   1569 			(void) printf(" \n");
   1570 			return;
   1571 		}
   1572 		break;
   1573 	default:
   1574 		(void) printf("data type unexpected.\n");
   1575 		return;
   1576 	}
   1577 
   1578 	/*
   1579 	 * Handle printing verbosely
   1580 	 */
   1581 	if (print_composite_string(var, value, valsize)) {
   1582 		return;
   1583 	}
   1584 
   1585 	if (!unprintable(value, valsize)) {
   1586 		(void) printf(" '%s'\n", value);
   1587 		return;
   1588 	}
   1589 
   1590 	(void) printf(" ");
   1591 #ifdef	__x86
   1592 	/*
   1593 	 * Due to backwards compatibility constraints x86 int
   1594 	 * properties are not in big-endian (ieee 1275) byte order.
   1595 	 * If we have a property that is a multiple of 4 bytes,
   1596 	 * let's assume it is an array of ints and print the bytes
   1597 	 * in little endian order to make things look nicer for
   1598 	 * the user.
   1599 	 */
   1600 	endswap = (valsize % 4) == 0;
   1601 #endif	/* __x86 */
   1602 	for (i = 0; i < valsize; i++) {
   1603 		int out;
   1604 		if (i && (i % 4 == 0))
   1605 			(void) putchar('.');
   1606 		if (endswap)
   1607 			out = value[i + (3 - 2 * (i % 4))] & 0xff;
   1608 		else
   1609 			out = value[i] & 0xff;
   1610 
   1611 		(void) printf("%02x", out);
   1612 	}
   1613 	(void) putchar('\n');
   1614 }
   1615 
   1616 static int
   1617 unprintable(char *value, int size)
   1618 {
   1619 	int i;
   1620 
   1621 	/*
   1622 	 * Is this just a zero?
   1623 	 */
   1624 	if (size == 0 || value[0] == '\0')
   1625 		return (1);
   1626 	/*
   1627 	 * If any character is unprintable, or if a null appears
   1628 	 * anywhere except at the end of a string, the whole
   1629 	 * property is "unprintable".
   1630 	 */
   1631 	for (i = 0; i < size; ++i) {
   1632 		if (value[i] == '\0')
   1633 			return (i != (size - 1));
   1634 		if (!isascii(value[i]) || iscntrl(value[i]))
   1635 			return (1);
   1636 	}
   1637 	return (0);
   1638 }
   1639 
   1640 static int
   1641 promopen(int oflag)
   1642 {
   1643 	for (;;)  {
   1644 		if ((prom_fd = open(opts.o_promdev, oflag)) < 0)  {
   1645 			if (errno == EAGAIN)   {
   1646 				(void) sleep(5);
   1647 				continue;
   1648 			}
   1649 			if (errno == ENXIO)
   1650 				return (-1);
   1651 			if (getzoneid() == GLOBAL_ZONEID) {
   1652 				_exit(_error("cannot open %s",
   1653 				    opts.o_promdev));
   1654 			}
   1655 			/* not an error if this isn't the global zone */
   1656 			(void) _error(NULL, "openprom facility not available");
   1657 			exit(0);
   1658 		} else
   1659 			return (0);
   1660 	}
   1661 }
   1662 
   1663 static void
   1664 promclose(void)
   1665 {
   1666 	if (close(prom_fd) < 0)
   1667 		exit(_error("close error on %s", opts.o_promdev));
   1668 }
   1669 
   1670 /*
   1671  * Get and print the name of the frame buffer device.
   1672  */
   1673 int
   1674 do_fbname(void)
   1675 {
   1676 	int	retval;
   1677 	char fbuf_path[MAXPATHLEN];
   1678 
   1679 	retval =  modctl(MODGETFBNAME, (caddr_t)fbuf_path);
   1680 
   1681 	if (retval == 0) {
   1682 		(void) printf("%s\n", fbuf_path);
   1683 	} else {
   1684 		if (retval == EFAULT) {
   1685 			(void) fprintf(stderr,
   1686 			"Error copying fb path to userland\n");
   1687 		} else {
   1688 			(void) fprintf(stderr,
   1689 			"Console output device is not a frame buffer\n");
   1690 		}
   1691 		return (1);
   1692 	}
   1693 	return (0);
   1694 }
   1695 
   1696 /*
   1697  * Get and print the PROM version.
   1698  */
   1699 int
   1700 do_promversion(void)
   1701 {
   1702 	Oppbuf	oppbuf;
   1703 	struct openpromio *opp = &(oppbuf.opp);
   1704 
   1705 	if (promopen(O_RDONLY))  {
   1706 		(void) fprintf(stderr, "Cannot open openprom device\n");
   1707 		return (1);
   1708 	}
   1709 
   1710 	opp->oprom_size = MAXVALSIZE;
   1711 	if (ioctl(prom_fd, OPROMGETVERSION, opp) < 0)
   1712 		exit(_error("OPROMGETVERSION"));
   1713 
   1714 	(void) printf("%s\n", opp->oprom_array);
   1715 	promclose();
   1716 	return (0);
   1717 }
   1718 
   1719 int
   1720 do_prom_version64(void)
   1721 {
   1722 #ifdef	sparc
   1723 	Oppbuf	oppbuf;
   1724 	struct openpromio *opp = &(oppbuf.opp);
   1725 	/*LINTED*/
   1726 	struct openprom_opr64 *opr = (struct openprom_opr64 *)opp->oprom_array;
   1727 
   1728 	static const char msg[] =
   1729 	    "NOTICE: The firmware on this system does not support the "
   1730 	    "64-bit OS.\n"
   1731 	    "\tPlease upgrade to at least the following version:\n"
   1732 	    "\t\t%s\n\n";
   1733 
   1734 	if (promopen(O_RDONLY))  {
   1735 		(void) fprintf(stderr, "Cannot open openprom device\n");
   1736 		return (-1);
   1737 	}
   1738 
   1739 	opp->oprom_size = MAXVALSIZE;
   1740 	if (ioctl(prom_fd, OPROMREADY64, opp) < 0)
   1741 		exit(_error("OPROMREADY64"));
   1742 
   1743 	if (opr->return_code == 0)
   1744 		return (0);
   1745 
   1746 	(void) printf(msg, opr->message);
   1747 
   1748 	promclose();
   1749 	return (opr->return_code);
   1750 #else
   1751 	return (0);
   1752 #endif
   1753 }
   1754 
   1755 int
   1756 do_productinfo(void)
   1757 {
   1758 	di_node_t root, next_node;
   1759 	di_prom_handle_t promh;
   1760 	static const char *root_prop[] = { "name", "model", "banner-name",
   1761 					"compatible" };
   1762 	static const char *root_propv[] = { "name", "model", "banner-name",
   1763 					"compatible", "idprom" };
   1764 	static const char *oprom_prop[] = { "model", "version" };
   1765 
   1766 
   1767 	root = di_init("/", DINFOCPYALL);
   1768 
   1769 	if (root == DI_NODE_NIL) {
   1770 		(void) fprintf(stderr, "di_init() failed\n");
   1771 		return (1);
   1772 	}
   1773 
   1774 	promh = di_prom_init();
   1775 
   1776 	if (promh == DI_PROM_HANDLE_NIL) {
   1777 		(void) fprintf(stderr, "di_prom_init() failed\n");
   1778 		return (1);
   1779 	}
   1780 
   1781 	if (opts.o_verbose) {
   1782 		dump_prodinfo(promh, root, root_propv, "root",
   1783 		    NUM_ELEMENTS(root_propv));
   1784 
   1785 		/* Get model and version properties under node "openprom" */
   1786 		next_node = find_node_by_name(promh, root, "openprom");
   1787 		if (next_node != DI_NODE_NIL)
   1788 			dump_prodinfo(promh, next_node, oprom_prop,
   1789 			    "openprom", NUM_ELEMENTS(oprom_prop));
   1790 
   1791 	} else
   1792 		dump_prodinfo(promh, root, root_prop, "root",
   1793 		    NUM_ELEMENTS(root_prop));
   1794 	di_prom_fini(promh);
   1795 	di_fini(root);
   1796 	return (0);
   1797 }
   1798 
   1799 di_node_t
   1800 find_node_by_name(di_prom_handle_t promh, di_node_t parent,
   1801 		char *node_name)
   1802 {
   1803 	di_node_t next_node;
   1804 	uchar_t *prop_valp;
   1805 
   1806 	for (next_node = di_child_node(parent); next_node != DI_NODE_NIL;
   1807 	    next_node = di_sibling_node(next_node)) {
   1808 		int len;
   1809 
   1810 		len = get_propval_by_name(promh, next_node, "name", &prop_valp);
   1811 		if ((len != -1) && (strcmp((char *)prop_valp, node_name) == 0))
   1812 			return (next_node);
   1813 	}
   1814 	return (DI_NODE_NIL);
   1815 }
   1816 
   1817 
   1818 int
   1819 get_propval_by_name(di_prom_handle_t promh, di_node_t node, const char *name,
   1820 			uchar_t **valp)
   1821 {
   1822 	int len;
   1823 	uchar_t *bufp;
   1824 
   1825 	len = di_prom_prop_lookup_bytes(promh, node, name,
   1826 	    (uchar_t **)&bufp);
   1827 	if (len != -1) {
   1828 		*valp = (uchar_t *)malloc(len);
   1829 		(void) memcpy(*valp, bufp, len);
   1830 	}
   1831 	return (len);
   1832 }
   1833 
   1834 
   1835 static void
   1836 dump_prodinfo(di_prom_handle_t promh, di_node_t node, const char **propstr,
   1837 		char *node_name, int num)
   1838 {
   1839 	int out, len, index1, index, endswap = 0;
   1840 	uchar_t *prop_valp;
   1841 
   1842 	for (index1 = 0; index1 < num; index1++) {
   1843 		len = get_propval_by_name(promh, node, propstr[index1],
   1844 		    &prop_valp);
   1845 		if (len != -1) {
   1846 			if (strcmp(node_name, "root"))
   1847 				(void) printf("%s ", node_name);
   1848 
   1849 			(void) printf("%s: ", propstr[index1]);
   1850 
   1851 			if (print_composite_string((const char *)
   1852 			    propstr[index1], (char *)prop_valp, len)) {
   1853 				free(prop_valp);
   1854 				continue;
   1855 			}
   1856 
   1857 			if (!unprintable((char *)prop_valp, len)) {
   1858 				(void) printf(" %s\n", (char *)prop_valp);
   1859 				free(prop_valp);
   1860 				continue;
   1861 			}
   1862 
   1863 			(void) printf(" ");
   1864 #ifdef  __x86
   1865 			endswap = (len % 4) == 0;
   1866 #endif  /* __x86 */
   1867 			for (index = 0; index < len; index++) {
   1868 				if (index && (index % 4 == 0))
   1869 					(void) putchar('.');
   1870 				if (endswap)
   1871 					out = prop_valp[index +
   1872 					    (3 - 2 * (index % 4))] & 0xff;
   1873 				else
   1874 					out = prop_valp[index] & 0xff;
   1875 				(void) printf("%02x", out);
   1876 			}
   1877 			(void) putchar('\n');
   1878 			free(prop_valp);
   1879 		}
   1880 	}
   1881 }
   1882 
   1883 static int
   1884 dump_compatible(char *name, int ilev, di_node_t node)
   1885 {
   1886 	int	ncompat;
   1887 	char	*compat_array;
   1888 	char	*p, *q;
   1889 	int	i;
   1890 
   1891 	if (node == DI_PATH_NIL)
   1892 		return (0);
   1893 
   1894 	ncompat = di_compatible_names(node, &compat_array);
   1895 	if (ncompat <= 0)
   1896 		return (0);	/* no 'compatible' available */
   1897 
   1898 	/* verify integrety of compat_array */
   1899 	for (i = 0, p = compat_array; i < ncompat; i++, p += strlen(p) + 1) {
   1900 		if (strlen(p) == 0)
   1901 			return (0);		/* NULL string */
   1902 		for (q = p; *q; q++) {
   1903 			if (!(isascii(*q) && (isprint(*q) || isspace(*q))))
   1904 				return (0);	/* Not printable or space */
   1905 		}
   1906 	}
   1907 
   1908 	/* If name is non-NULL, produce header */
   1909 	if (name) {
   1910 		indent_to_level(ilev);
   1911 		(void) printf("%s properties:\n", name);
   1912 	}
   1913 	ilev++;
   1914 
   1915 	/* process like a string array property */
   1916 	indent_to_level(ilev);
   1917 	(void) printf("name='compatible' type=string items=%d\n", ncompat);
   1918 	indent_to_level(ilev);
   1919 	(void) printf("    value=");
   1920 	for (i = 0, p = compat_array; i < (ncompat - 1);
   1921 	    i++, p += strlen(p) + 1)
   1922 		(void) printf("'%s' + ", p);
   1923 	(void) printf("'%s'", p);
   1924 	(void) putchar('\n');
   1925 	return (1);
   1926 }
   1927