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