xref: /illumos-gate/usr/src/cmd/prtconf/pdevinfo.c (revision 767007dff68ec7382116062a33e8ff5e89ce8664)
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 
877 static int
878 is_openprom(void)
879 {
880 	Oppbuf	oppbuf;
881 	struct openpromio *opp = &(oppbuf.opp);
882 	unsigned int i;
883 
884 	opp->oprom_size = MAXVALSIZE;
885 	if (ioctl(prom_fd, OPROMGETCONS, opp) < 0)
886 		err(-1, "OPROMGETCONS");
887 
888 	i = (unsigned int)((unsigned char)opp->oprom_array[0]);
889 	return ((i & OPROMCONS_OPENPROM) == OPROMCONS_OPENPROM);
890 }
891 
892 int
893 do_prominfo(void)
894 {
895 	uint_t arg = 0;
896 	uint_t *ptr;
897 	uchar_t *prom_snapshot;
898 
899 	if (promopen(O_RDONLY))  {
900 		err(-1, "openeepr device open failed");
901 	}
902 
903 	if (is_openprom() == 0)  {
904 		(void) fprintf(stderr, "System architecture does not "
905 		    "support this option of this command.\n");
906 		return (1);
907 	}
908 
909 	/*
910 	 * If we're eiher in verbose mode or asked to get device information,
911 	 * then we need to actually ask for verbose information from the prom by
912 	 * setting a non-zero value.
913 	 */
914 	if (opts.o_verbose != 0 || opts.o_pciid != 0) {
915 		arg = 1;
916 	}
917 
918 	/* OPROMSNAPSHOT returns size in arg */
919 	if (ioctl(prom_fd, OPROMSNAPSHOT, &arg) < 0)
920 		err(-1, "OPROMSNAPSHOT");
921 
922 	/*
923 	 * ioctl OPROMCOPYOUT is expecting buffer with at least
924 	 * sizeof (uint_t).
925 	 */
926 	if (arg < sizeof (uint_t))
927 		return (1);
928 
929 	if ((ptr = malloc(arg)) == NULL)
930 		err(-1, "failed to allocate memory");
931 
932 	/* copy out the snapshot for printing */
933 	*ptr = arg;
934 	prom_snapshot = (uchar_t *)ptr;
935 	if (ioctl(prom_fd, OPROMCOPYOUT, prom_snapshot) < 0)
936 		err(-1, "OPROMCOPYOUT");
937 
938 	promclose();
939 
940 	/* print out information */
941 	walk(prom_snapshot, arg, 0);
942 	free(ptr);
943 
944 	return (0);
945 }
946 
947 static void
948 walk(uchar_t *buf, uint_t size, int level)
949 {
950 	int error;
951 	nvlist_t *nvl, *cnvl;
952 	nvpair_t *child = NULL;
953 	uchar_t *cbuf = NULL;
954 	uint_t csize;
955 
956 	/* Expand to an nvlist */
957 	if (nvlist_unpack((char *)buf, size, &nvl, 0))
958 		err(-1, "error processing snapshot");
959 
960 	/* print current node */
961 	dump_node(nvl, level);
962 
963 	/* print children */
964 	error = nvlist_lookup_byte_array(nvl, "@child", &cbuf, &csize);
965 	if ((error == ENOENT) || (cbuf == NULL))
966 		return;		/* no child exists */
967 
968 	if (error || nvlist_unpack((char *)cbuf, csize, &cnvl, 0))
969 		err(-1, "error processing snapshot");
970 
971 	while ((child = nvlist_next_nvpair(cnvl, child)) != NULL) {
972 		char *name = nvpair_name(child);
973 		data_type_t type = nvpair_type(child);
974 		uchar_t *nodebuf;
975 		uint_t nodesize;
976 		if (strcmp("node", name) != 0) {
977 			dprintf("unexpected nvpair name %s != name\n", name);
978 			continue;
979 		}
980 		if (type != DATA_TYPE_BYTE_ARRAY) {
981 			dprintf("unexpected nvpair type %d, not byte array \n",
982 			    type);
983 			continue;
984 		}
985 
986 		(void) nvpair_value_byte_array(child,
987 		    (uchar_t **)&nodebuf, &nodesize);
988 		walk(nodebuf, nodesize, level + 1);
989 	}
990 
991 	nvlist_free(nvl);
992 }
993 
994 /*
995  * The encoding of the name property depends on whether we got verbose prom
996  * information or not. If we didn't, it'll just be a string in the nvlist_t.
997  * However, otherwise it'll end up being byte data that the kernel guarantees
998  * for 'name' is actually a null terminated string.
999  */
1000 static const char *
1001 prom_node_name(nvlist_t *nvl)
1002 {
1003 	char *str;
1004 	uchar_t *bval;
1005 	uint_t len;
1006 
1007 	if (nvlist_lookup_string(nvl, "name", &str) == 0) {
1008 		return (str);
1009 	}
1010 
1011 	if (nvlist_lookup_byte_array(nvl, "name", &bval, &len) == 0) {
1012 		if (bval[len - 1] == '\0')
1013 			return ((char *)bval);
1014 	}
1015 
1016 	return ("data not available");
1017 }
1018 
1019 /*
1020  * Given a node at a given level, try to determine if this is a PCI device. We
1021  * do this through a two step process mostly due to the fact that we don't have
1022  * easy linkage to the parent here and not all nodes have everything we expect.
1023  * This test is more similar to the pcieadm test than what we use for the normal
1024  * devinfo part.
1025  *
1026  * 1. Check the node name to see if it starts with pci with another character
1027  *   (to avoid the synthetic pci instances).
1028  * 2. Look at the compatible property for the class strings.
1029  */
1030 static boolean_t
1031 prom_is_pci(nvlist_t *nvl, const char *name)
1032 {
1033 	uchar_t *value;
1034 	uint_t len;
1035 
1036 	if (strncmp("pci", name, 3) == 0 && name[3] != '\0') {
1037 		return (B_TRUE);
1038 	}
1039 
1040 	/*
1041 	 * This is a composite string. Unlike with devinfo, we just have the
1042 	 * array of strings here and we have to manually make sure we don't
1043 	 * exceed the size as we don't have the total number of entries.
1044 	 */
1045 	if (nvlist_lookup_byte_array(nvl, "compatible", &value, &len) == 0) {
1046 		const char *str;
1047 
1048 		/*
1049 		 * Adjust by one to account or the extra NUL that the driver
1050 		 * inserts.
1051 		 */
1052 		len--;
1053 		for (str = (char *)value; str < ((char *)value + len);
1054 		    str += strlen(str) + 1) {
1055 			if (strncmp("pciclass,", str,
1056 			    sizeof ("pciclass,") - 1) == 0 ||
1057 			    strncmp("pciexclass,", str,
1058 			    sizeof ("pciexclass,") - 1) == 0) {
1059 				return (B_TRUE);
1060 			}
1061 		}
1062 	}
1063 
1064 	return (B_FALSE);
1065 }
1066 
1067 static boolean_t
1068 prom_extract_u16(nvlist_t *nvl, const char *name, uint16_t *valp)
1069 {
1070 	uchar_t *value;
1071 	uint_t len;
1072 	uint32_t u32;
1073 
1074 	if (nvlist_lookup_byte_array(nvl, name, &value, &len) != 0) {
1075 		return (B_FALSE);
1076 	}
1077 
1078 	/*
1079 	 * A uint32_t will be encoded as a 4-byte value followed by a NUL
1080 	 * regardless.
1081 	 */
1082 	if (len != 5 || value[4] != '\0') {
1083 		return (B_FALSE);
1084 	}
1085 
1086 	/*
1087 	 * The current PROM code puts values in the native-endianness for x86
1088 	 * and SPARC as opposed to always translating into what 1275 wants of
1089 	 * big endian. It is unclear what'll happen for subsequent platforms.
1090 	 */
1091 #if !defined(__x86)
1092 #error "determine endianness of the platform's openprom interface"
1093 #endif
1094 	(void) memcpy(&u32, value, sizeof (u32));
1095 	if (u32 > UINT16_MAX) {
1096 		return (B_FALSE);
1097 	}
1098 
1099 	*valp = (uint16_t)u32;
1100 	return (B_TRUE);
1101 }
1102 
1103 /*
1104  * Similar to the above, synthesize the device type as either pci or pciex based
1105  * on the compatible array. A PCI Express device will have their first entry
1106  * start with 'pciexXXXX,XXXX'. A device without that will just start with pci.
1107  */
1108 static const char *
1109 prom_pci_device_type(nvlist_t *nvl)
1110 {
1111 	uchar_t *value;
1112 	uint_t len;
1113 
1114 	if (nvlist_lookup_byte_array(nvl, "compatible", &value, &len) != 0) {
1115 		return (NULL);
1116 	}
1117 
1118 	if (strncmp("pciex", (char *)value, 5) == 0 && value[5] != '\0') {
1119 		return ("pciex");
1120 	}
1121 
1122 	if (strncmp("pci", (char *)value, 3) == 0 && value[3] != '\0') {
1123 		return ("pci");
1124 	}
1125 
1126 	return (NULL);
1127 }
1128 
1129 static void
1130 dump_pcidb(int level, uint16_t vid, uint16_t did, boolean_t do_sub,
1131     uint16_t svid, uint16_t sdid)
1132 {
1133 	const char *vstr = "unknown vendor";
1134 	const char *dstr = "unknown device";
1135 	const char *sstr = "unknown subsystem";
1136 	pcidb_vendor_t *pciv = NULL;
1137 	pcidb_device_t *pcid = NULL;
1138 	pcidb_subvd_t *pcis = NULL;
1139 
1140 	if (opts.o_pcidb == NULL)
1141 		return;
1142 
1143 	pciv = pcidb_lookup_vendor(opts.o_pcidb, vid);
1144 	if (pciv != NULL) {
1145 		vstr = pcidb_vendor_name(pciv);
1146 		pcid = pcidb_lookup_device_by_vendor(pciv, did);
1147 		if (pcid != NULL) {
1148 			dstr = pcidb_device_name(pcid);
1149 		}
1150 	}
1151 
1152 	indent_to_level(level);
1153 	(void) printf("vendor-name:  '%s'\n", vstr);
1154 	indent_to_level(level);
1155 	(void) printf("device-name:  '%s'\n", dstr);
1156 
1157 	if (!do_sub)
1158 		return;
1159 
1160 	if (pciv != NULL && pcid != NULL) {
1161 		pcis = pcidb_lookup_subvd_by_device(pcid, svid, sdid);
1162 		if (pcis != NULL) {
1163 			sstr = pcidb_subvd_name(pcis);
1164 		}
1165 	}
1166 
1167 	indent_to_level(level);
1168 	(void) printf("subsystem-name:  '%s'\n", sstr);
1169 }
1170 
1171 /*
1172  * Print all properties and values. When o_verbose is specified then rather than
1173  * printing the name of the node (and potentially the PCI ID and DB info), we
1174  * print the node name and instead include all that information in properties.
1175  * This mimics the behavior of the non-prom path.
1176  */
1177 static void
1178 dump_node(nvlist_t *nvl, int level)
1179 {
1180 	int id = 0;
1181 	const char *name;
1182 	nvpair_t *nvp = NULL;
1183 
1184 	indent_to_level(level);
1185 	name = prom_node_name(nvl);
1186 	(void) printf("Node");
1187 	if (!opts.o_verbose) {
1188 		(void) printf(" '%s'", name);
1189 
1190 		if (opts.o_pciid && prom_is_pci(nvl, name)) {
1191 			const char *dtype = prom_pci_device_type(nvl);
1192 			uint16_t vid, did;
1193 
1194 			if (prom_extract_u16(nvl, "vendor-id", &vid) &&
1195 			    prom_extract_u16(nvl, "device-id", &did) &&
1196 			    dtype != NULL) {
1197 				print_pciid(dtype, vid, did, opts.o_pcidb);
1198 			}
1199 
1200 		}
1201 	} else {
1202 		(void) nvlist_lookup_int32(nvl, "@nodeid", &id);
1203 		(void) printf(" %#08x\n", id);
1204 	}
1205 
1206 	if (!opts.o_verbose) {
1207 		(void) putchar('\n');
1208 		return;
1209 	}
1210 
1211 	while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
1212 		name = nvpair_name(nvp);
1213 		if (name[0] == '@')
1214 			continue;
1215 
1216 		print_one(nvp, level + 1);
1217 	}
1218 
1219 	/*
1220 	 * Go through and create synthetic properties for PCI devices like the
1221 	 * normal device tree path.
1222 	 */
1223 	if (prom_is_pci(nvl, name)) {
1224 		uint16_t vid = UINT16_MAX, did = UINT16_MAX;
1225 		uint16_t svid = UINT16_MAX, sdid = UINT16_MAX;
1226 		boolean_t valid_sub = B_FALSE;
1227 
1228 		if (prom_extract_u16(nvl, "subsystem-vendor-id", &svid) &&
1229 		    prom_extract_u16(nvl, "subsystem-id", &sdid)) {
1230 			valid_sub = B_TRUE;
1231 		}
1232 
1233 		if (prom_extract_u16(nvl, "vendor-id", &vid) &&
1234 		    prom_extract_u16(nvl, "device-id", &did)) {
1235 			dump_pcidb(level + 1, vid, did, valid_sub, svid, sdid);
1236 		}
1237 	}
1238 
1239 	(void) putchar('\n');
1240 }
1241 
1242 static const char *
1243 path_state_name(di_path_state_t st)
1244 {
1245 	switch (st) {
1246 		case DI_PATH_STATE_ONLINE:
1247 			return ("online");
1248 		case DI_PATH_STATE_STANDBY:
1249 			return ("standby");
1250 		case DI_PATH_STATE_OFFLINE:
1251 			return ("offline");
1252 		case DI_PATH_STATE_FAULT:
1253 			return ("faulted");
1254 		case DI_PATH_STATE_UNKNOWN:
1255 		default:
1256 			return ("unknown");
1257 	}
1258 }
1259 
1260 /*
1261  * Print all phci's each client is connected to.
1262  */
1263 static void
1264 dump_pathing_data(int ilev, di_node_t node)
1265 {
1266 	di_path_t	pi = DI_PATH_NIL;
1267 	di_node_t	phci_node;
1268 	char		*phci_path;
1269 	int		path_instance;
1270 	int		firsttime = 1;
1271 
1272 	if (node == DI_PATH_NIL)
1273 		return;
1274 
1275 	while ((pi = di_path_client_next_path(node, pi)) != DI_PATH_NIL) {
1276 
1277 		/* It is not really a path if we failed to capture the pHCI */
1278 		phci_node = di_path_phci_node(pi);
1279 		if (phci_node == DI_NODE_NIL)
1280 			continue;
1281 
1282 		/* Print header for the first path */
1283 		if (firsttime) {
1284 			indent_to_level(ilev);
1285 			firsttime = 0;
1286 			ilev++;
1287 			(void) printf("Paths from multipath bus adapters:\n");
1288 		}
1289 
1290 		/*
1291 		 * Print the path instance and full "pathinfo" path, which is
1292 		 * the same as the /devices devifo path had the device been
1293 		 * enumerated under pHCI.
1294 		 */
1295 		phci_path = di_devfs_path(phci_node);
1296 		if (phci_path) {
1297 			path_instance = di_path_instance(pi);
1298 			if (path_instance > 0) {
1299 				indent_to_level(ilev);
1300 				(void) printf("Path %d: %s/%s@%s\n",
1301 				    path_instance, phci_path,
1302 				    di_node_name(node),
1303 				    di_path_bus_addr(pi));
1304 			}
1305 			di_devfs_path_free(phci_path);
1306 		}
1307 
1308 		/* print phci driver, instance, and path state information */
1309 		indent_to_level(ilev);
1310 		(void) printf("%s#%d (%s)\n", di_driver_name(phci_node),
1311 		    di_instance(phci_node), path_state_name(di_path_state(pi)));
1312 
1313 		(void) dump_prop_list(&pathprop_dumpops, NULL, ilev + 1,
1314 		    pi, DDI_DEV_T_ANY, NULL);
1315 	}
1316 }
1317 
1318 static int
1319 dump_minor_data_links(di_devlink_t devlink, void *arg)
1320 {
1321 	int ilev = (intptr_t)arg;
1322 	indent_to_level(ilev);
1323 	(void) printf("dev_link=%s\n", di_devlink_path(devlink));
1324 	return (DI_WALK_CONTINUE);
1325 }
1326 
1327 static void
1328 dump_minor_data_paths(int ilev, di_minor_t minor,
1329     di_devlink_handle_t devlink_hdl)
1330 {
1331 	char	*path, *type;
1332 	int	spec_type;
1333 
1334 	/* get the path to the device and the minor node name */
1335 	if ((path = di_devfs_minor_path(minor)) == NULL)
1336 		err(-1, "failed to allocate memory");
1337 
1338 	/* display the path to this minor node */
1339 	indent_to_level(ilev);
1340 	(void) printf("dev_path=%s\n", path);
1341 
1342 	if (devlink_hdl != NULL) {
1343 
1344 		/* get the device minor node information */
1345 		spec_type = di_minor_spectype(minor);
1346 		switch (di_minor_type(minor)) {
1347 			case DDM_MINOR:
1348 				type = "minor";
1349 				break;
1350 			case DDM_ALIAS:
1351 				type = "alias";
1352 				break;
1353 			case DDM_DEFAULT:
1354 				type = "default";
1355 				break;
1356 			case DDM_INTERNAL_PATH:
1357 				type = "internal";
1358 				break;
1359 			default:
1360 				type = "unknown";
1361 				break;
1362 		}
1363 
1364 		/* display the device minor node information */
1365 		indent_to_level(ilev + 1);
1366 		(void) printf("spectype=%s type=%s\n",
1367 		    (spec_type == S_IFBLK) ? "blk" : "chr", type);
1368 
1369 		/* display all the devlinks for this device minor node */
1370 		(void) di_devlink_walk(devlink_hdl, NULL, path,
1371 		    0, (void *)(intptr_t)(ilev + 1), dump_minor_data_links);
1372 	}
1373 
1374 	di_devfs_path_free(path);
1375 }
1376 
1377 static void
1378 create_minor_list(di_node_t node)
1379 {
1380 	di_minor_t	minor, minor_head, minor_tail, minor_prev, minor_walk;
1381 	int		major;
1382 
1383 	/* if there are no minor nodes, bail */
1384 	if (di_minor_next(node, DI_MINOR_NIL) == DI_MINOR_NIL)
1385 		return;
1386 
1387 	/*
1388 	 * here we want to create lists of minor nodes with the same
1389 	 * dev_t.  to do this we first sort all the minor nodes by devt.
1390 	 *
1391 	 * the algorithm used here is a bubble sort, so performance sucks.
1392 	 * but it's probably ok here because most device instances don't
1393 	 * have that many minor nodes.  also we're doing this as we're
1394 	 * displaying each node so it doesn't look like we're pausing
1395 	 * output for a long time.
1396 	 */
1397 	major = di_driver_major(node);
1398 	minor_head = minor_tail = minor = DI_MINOR_NIL;
1399 	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
1400 		dev_t	dev = di_minor_devt(minor);
1401 
1402 		/* skip /pseudo/clone@0 minor nodes */
1403 		if (major != major(dev))
1404 			continue;
1405 
1406 		minor_ptr_set(minor, DI_MINOR_NIL);
1407 		if (minor_head == DI_MINOR_NIL) {
1408 			/* this is the first minor node we're looking at */
1409 			minor_head = minor_tail = minor;
1410 			continue;
1411 		}
1412 
1413 		/*
1414 		 * if the new dev is less than the old dev, update minor_head
1415 		 * so it points to the beginning of the list.  ie it points
1416 		 * to the node with the lowest dev value
1417 		 */
1418 		if (dev <= di_minor_devt(minor_head)) {
1419 			minor_ptr_set(minor, minor_head);
1420 			minor_head = minor;
1421 			continue;
1422 		}
1423 
1424 		minor_prev = minor_head;
1425 		minor_walk = minor_ptr(minor_head);
1426 		while ((minor_walk != DI_MINOR_NIL) &&
1427 		    (dev > di_minor_devt(minor_walk))) {
1428 			minor_prev = minor_walk;
1429 			minor_walk = minor_ptr(minor_walk);
1430 		}
1431 		minor_ptr_set(minor, minor_walk);
1432 		minor_ptr_set(minor_prev, minor);
1433 		if (minor_walk == NULL)
1434 			minor_tail = minor;
1435 	}
1436 
1437 	/* check if there were any non /pseudo/clone@0 nodes.  if not, bail */
1438 	if (minor_head == DI_MINOR_NIL)
1439 		return;
1440 
1441 	/*
1442 	 * now that we have a list of minor nodes sorted by devt
1443 	 * we walk through the list and break apart the entire list
1444 	 * to create circular lists of minor nodes with matching devts.
1445 	 */
1446 	minor_prev = minor_head;
1447 	minor_walk = minor_ptr(minor_head);
1448 	while (minor_walk != DI_MINOR_NIL) {
1449 		if (di_minor_devt(minor_prev) != di_minor_devt(minor_walk)) {
1450 			minor_ptr_set(minor_prev, minor_head);
1451 			minor_head = minor_walk;
1452 		}
1453 		minor_prev = minor_walk;
1454 		minor_walk = minor_ptr(minor_walk);
1455 	}
1456 	minor_ptr_set(minor_tail, minor_head);
1457 }
1458 
1459 static void
1460 link_lnode_disp(di_link_t link, uint_t endpoint, int ilev,
1461     di_devlink_handle_t devlink_hdl)
1462 {
1463 	di_lnode_t	lnode;
1464 	char		*name, *path;
1465 	int		displayed_path, spec_type;
1466 	di_node_t	node = DI_NODE_NIL;
1467 	dev_t		devt = DDI_DEV_T_NONE;
1468 
1469 	lnode = di_link_to_lnode(link, endpoint);
1470 
1471 	indent_to_level(ilev);
1472 	name = di_lnode_name(lnode);
1473 	spec_type = di_link_spectype(link);
1474 
1475 	(void) printf("mod=%s", name);
1476 
1477 	/*
1478 	 * if we're displaying the source of a link, we should display
1479 	 * the target access mode.  (either block or char.)
1480 	 */
1481 	if (endpoint == DI_LINK_SRC)
1482 		(void) printf(" accesstype=%s",
1483 		    (spec_type == S_IFBLK) ? "blk" : "chr");
1484 
1485 	/*
1486 	 * check if the lnode is bound to a specific device
1487 	 * minor node (i.e.  if it's bound to a dev_t) and
1488 	 * if so display the dev_t value and any possible
1489 	 * minor node pathing information.
1490 	 */
1491 	displayed_path = 0;
1492 	if (di_lnode_devt(lnode, &devt) == 0) {
1493 		di_minor_t	minor = DI_MINOR_NIL;
1494 
1495 		(void) printf(" dev=(%u,%u)\n",
1496 		    (uint_t)major(devt), (uint_t)minor(devt));
1497 
1498 		/* display paths to the src devt minor node */
1499 		while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
1500 			if (devt != di_minor_devt(minor))
1501 				continue;
1502 
1503 			if ((endpoint == DI_LINK_TGT) &&
1504 			    (spec_type != di_minor_spectype(minor)))
1505 				continue;
1506 
1507 			dump_minor_data_paths(ilev + 1, minor, devlink_hdl);
1508 			displayed_path = 1;
1509 		}
1510 	} else {
1511 		(void) printf("\n");
1512 	}
1513 
1514 	if (displayed_path)
1515 		return;
1516 
1517 	/*
1518 	 * This device lnode is not did not have any minor node
1519 	 * pathing information so display the path to device node.
1520 	 */
1521 	node = di_lnode_devinfo(lnode);
1522 	if ((path = di_devfs_path(node)) == NULL)
1523 		err(-1, "failed to allocate memory");
1524 
1525 	indent_to_level(ilev + 1);
1526 	(void) printf("dev_path=%s\n", path);
1527 	di_devfs_path_free(path);
1528 }
1529 
1530 static void
1531 dump_minor_link_data(int ilev, di_node_t node, dev_t devt,
1532     di_devlink_handle_t devlink_hdl)
1533 {
1534 	int		first = 1;
1535 	di_link_t	link;
1536 
1537 	link = DI_LINK_NIL;
1538 	while ((link = di_link_next_by_node(node, link, DI_LINK_TGT)) !=
1539 	    DI_LINK_NIL) {
1540 		di_lnode_t	tgt_lnode;
1541 		dev_t		tgt_devt = DDI_DEV_T_NONE;
1542 
1543 		tgt_lnode = di_link_to_lnode(link, DI_LINK_TGT);
1544 
1545 		if (di_lnode_devt(tgt_lnode, &tgt_devt) != 0)
1546 			continue;
1547 
1548 		if (devt != tgt_devt)
1549 			continue;
1550 
1551 		if (first) {
1552 			first = 0;
1553 			indent_to_level(ilev);
1554 			(void) printf("Device Minor Layered Under:\n");
1555 		}
1556 
1557 		/* displayed this lnode */
1558 		lnode_displayed_set(tgt_lnode);
1559 		link_lnode_disp(link, DI_LINK_SRC, ilev + 1, devlink_hdl);
1560 	}
1561 
1562 	link = DI_LINK_NIL;
1563 	while ((link = di_link_next_by_node(node, link, DI_LINK_SRC)) !=
1564 	    DI_LINK_NIL) {
1565 		di_lnode_t	src_lnode;
1566 		dev_t		src_devt = DDI_DEV_T_NONE;
1567 
1568 		src_lnode = di_link_to_lnode(link, DI_LINK_SRC);
1569 
1570 		if (di_lnode_devt(src_lnode, &src_devt) != 0)
1571 			continue;
1572 
1573 		if (devt != src_devt)
1574 			continue;
1575 
1576 		if (first) {
1577 			first = 0;
1578 			indent_to_level(ilev);
1579 			(void) printf("Device Minor Layered Over:\n");
1580 		}
1581 
1582 		/* displayed this lnode */
1583 		lnode_displayed_set(src_lnode);
1584 		link_lnode_disp(link, DI_LINK_TGT, ilev + 1, devlink_hdl);
1585 	}
1586 }
1587 
1588 static void
1589 dump_minor_data(int ilev, di_node_t node, di_devlink_handle_t devlink_hdl)
1590 {
1591 	di_minor_t	minor, minor_next;
1592 	di_lnode_t	lnode;
1593 	di_link_t	link;
1594 	int		major, firstminor = 1;
1595 
1596 	/*
1597 	 * first go through and mark all lnodes and minor nodes for this
1598 	 * node as undisplayed
1599 	 */
1600 	lnode = DI_LNODE_NIL;
1601 	while ((lnode = di_lnode_next(node, lnode)) != DI_LNODE_NIL)
1602 		lnode_displayed_clear(lnode);
1603 	minor = DI_MINOR_NIL;
1604 	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
1605 		minor_displayed_clear(minor);
1606 	}
1607 
1608 	/*
1609 	 * when we display the minor nodes we want to coalesce nodes
1610 	 * that have the same dev_t.  we do this by creating circular
1611 	 * lists of minor nodes with the same devt.
1612 	 */
1613 	create_minor_list(node);
1614 
1615 	/* now we display the driver defined minor nodes */
1616 	major = di_driver_major(node);
1617 	minor = DI_MINOR_NIL;
1618 	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
1619 		dev_t	devt;
1620 
1621 		/*
1622 		 * skip /pseudo/clone@0 minor nodes.
1623 		 * these are only created for DLPIv2 network devices.
1624 		 * since these minor nodes are associated with a driver
1625 		 * and are only bound to a device instance after they
1626 		 * are opened and attached we don't print them out
1627 		 * here.
1628 		 */
1629 		devt = di_minor_devt(minor);
1630 		if (major != major(devt))
1631 			continue;
1632 
1633 		/* skip nodes that may have already been displayed */
1634 		if (minor_displayed(minor))
1635 			continue;
1636 
1637 		if (firstminor) {
1638 			firstminor = 0;
1639 			indent_to_level(ilev++);
1640 			(void) printf("Device Minor Nodes:\n");
1641 		}
1642 
1643 		/* display the device minor node information */
1644 		indent_to_level(ilev);
1645 		(void) printf("dev=(%u,%u)\n",
1646 		    (uint_t)major(devt), (uint_t)minor(devt));
1647 
1648 		minor_next = minor;
1649 		do {
1650 			/* display device minor node path info */
1651 			minor_displayed_set(minor_next);
1652 			dump_minor_data_paths(ilev + 1, minor_next,
1653 			    devlink_hdl);
1654 
1655 			/* get a pointer to the next node */
1656 			minor_next = minor_ptr(minor_next);
1657 		} while (minor_next != minor);
1658 
1659 		/* display who has this device minor node open */
1660 		dump_minor_link_data(ilev + 1, node, devt, devlink_hdl);
1661 
1662 		/* display properties associated with this devt */
1663 		(void) dump_prop_list(&drvprop_dumpops, "Minor",
1664 		    ilev + 1, node, devt, NULL);
1665 	}
1666 
1667 	/*
1668 	 * now go through all the target lnodes for this node and
1669 	 * if they haven't yet been displayed, display them now.
1670 	 *
1671 	 * this happens in the case of clone opens when an "official"
1672 	 * minor node does not exist for the opened devt
1673 	 */
1674 	link = DI_LINK_NIL;
1675 	while ((link = di_link_next_by_node(node, link, DI_LINK_TGT)) !=
1676 	    DI_LINK_NIL) {
1677 		dev_t		devt;
1678 
1679 		lnode = di_link_to_lnode(link, DI_LINK_TGT);
1680 
1681 		/* if we've already displayed this target lnode, skip it */
1682 		if (lnode_displayed(lnode))
1683 			continue;
1684 
1685 		if (firstminor) {
1686 			firstminor = 0;
1687 			indent_to_level(ilev++);
1688 			(void) printf("Device Minor Nodes:\n");
1689 		}
1690 
1691 		/* display the device minor node information */
1692 		indent_to_level(ilev);
1693 		(void) di_lnode_devt(lnode, &devt);
1694 		(void) printf("dev=(%u,%u)\n",
1695 		    (uint_t)major(devt), (uint_t)minor(devt));
1696 
1697 		indent_to_level(ilev + 1);
1698 		(void) printf("dev_path=<clone>\n");
1699 
1700 		/* display who has this cloned device minor node open */
1701 		dump_minor_link_data(ilev + 1, node, devt, devlink_hdl);
1702 
1703 		/* mark node as displayed */
1704 		lnode_displayed_set(lnode);
1705 	}
1706 }
1707 
1708 static void
1709 dump_link_data(int ilev, di_node_t node, di_devlink_handle_t devlink_hdl)
1710 {
1711 	int		first = 1;
1712 	di_link_t	link;
1713 
1714 	link = DI_LINK_NIL;
1715 	while ((link = di_link_next_by_node(node, link, DI_LINK_SRC)) !=
1716 	    DI_LINK_NIL) {
1717 		di_lnode_t	src_lnode;
1718 		dev_t		src_devt = DDI_DEV_T_NONE;
1719 
1720 		src_lnode = di_link_to_lnode(link, DI_LINK_SRC);
1721 
1722 		/*
1723 		 * here we only want to print out layering information
1724 		 * if we are the source and our source lnode is not
1725 		 * associated with any particular dev_t.  (which means
1726 		 * we won't display this link while dumping minor node
1727 		 * info.)
1728 		 */
1729 		if (di_lnode_devt(src_lnode, &src_devt) != -1)
1730 			continue;
1731 
1732 		if (first) {
1733 			first = 0;
1734 			indent_to_level(ilev);
1735 			(void) printf("Device Layered Over:\n");
1736 		}
1737 
1738 		/* displayed this lnode */
1739 		link_lnode_disp(link, DI_LINK_TGT, ilev + 1, devlink_hdl);
1740 	}
1741 }
1742 
1743 /*
1744  * certain 'known' property names may contain 'composite' strings.
1745  * Handle them here, and print them as 'string1' + 'string2' ...
1746  */
1747 static int
1748 print_composite_string(const char *var, char *value, int size)
1749 {
1750 	char *p, *q;
1751 	char *firstp;
1752 
1753 	if ((strcmp(var, "version") != 0) &&
1754 	    (strcmp(var, "compatible") != 0))
1755 		return (0);	/* Not a known composite string */
1756 
1757 	/*
1758 	 * Verify that each string in the composite string is non-NULL,
1759 	 * is within the bounds of the property length, and contains
1760 	 * printable characters or white space. Otherwise let the
1761 	 * caller deal with it.
1762 	 */
1763 	for (firstp = p = value; p < (value + size); p += strlen(p) + 1) {
1764 		if (strlen(p) == 0)
1765 			return (0);		/* NULL string */
1766 		for (q = p; *q; q++) {
1767 			if (!(isascii(*q) && (isprint(*q) || isspace(*q))))
1768 				return (0);	/* Not printable or space */
1769 		}
1770 		if (q > (firstp + size))
1771 			return (0);		/* Out of bounds */
1772 	}
1773 
1774 	for (firstp = p = value; p < (value + size); p += strlen(p) + 1) {
1775 		if (p == firstp)
1776 			(void) printf("'%s'", p);
1777 		else
1778 			(void) printf(" + '%s'", p);
1779 	}
1780 	(void) putchar('\n');
1781 	return (1);
1782 }
1783 
1784 /*
1785  * Print one property and its value. Handle the verbose case.
1786  */
1787 static void
1788 print_one(nvpair_t *nvp, int level)
1789 {
1790 	int i;
1791 	int endswap = 0;
1792 	uint_t valsize;
1793 	char *value;
1794 	char *var = nvpair_name(nvp);
1795 
1796 	indent_to_level(level);
1797 	(void) printf("%s: ", var);
1798 
1799 	switch (nvpair_type(nvp)) {
1800 	case DATA_TYPE_BOOLEAN:
1801 		(void) printf(" \n");
1802 		return;
1803 	case DATA_TYPE_BYTE_ARRAY:
1804 		if (nvpair_value_byte_array(nvp, (uchar_t **)&value,
1805 		    &valsize)) {
1806 			(void) printf("data not available.\n");
1807 			return;
1808 		}
1809 		valsize--;	/* take out null added by driver */
1810 
1811 		/*
1812 		 * Do not print valsize > MAXVALSIZE, to be compatible
1813 		 * with old behavior. E.g. intel's eisa-nvram property
1814 		 * has a size of 65 K.
1815 		 */
1816 		if (valsize > MAXVALSIZE) {
1817 			(void) printf(" \n");
1818 			return;
1819 		}
1820 		break;
1821 	default:
1822 		(void) printf("data type unexpected.\n");
1823 		return;
1824 	}
1825 
1826 	/*
1827 	 * Handle printing verbosely
1828 	 */
1829 	if (print_composite_string(var, value, valsize)) {
1830 		return;
1831 	}
1832 
1833 	if (!unprintable(value, valsize)) {
1834 		(void) printf(" '%s'\n", value);
1835 		return;
1836 	}
1837 
1838 	(void) printf(" ");
1839 #ifdef	__x86
1840 	/*
1841 	 * Due to backwards compatibility constraints x86 int
1842 	 * properties are not in big-endian (ieee 1275) byte order.
1843 	 * If we have a property that is a multiple of 4 bytes,
1844 	 * let's assume it is an array of ints and print the bytes
1845 	 * in little endian order to make things look nicer for
1846 	 * the user.
1847 	 */
1848 	endswap = (valsize % 4) == 0;
1849 #endif	/* __x86 */
1850 	for (i = 0; i < valsize; i++) {
1851 		int out;
1852 		if (i && (i % 4 == 0))
1853 			(void) putchar('.');
1854 		if (endswap)
1855 			out = value[i + (3 - 2 * (i % 4))] & 0xff;
1856 		else
1857 			out = value[i] & 0xff;
1858 
1859 		(void) printf("%02x", out);
1860 	}
1861 	(void) putchar('\n');
1862 }
1863 
1864 static int
1865 unprintable(char *value, int size)
1866 {
1867 	int i;
1868 
1869 	/*
1870 	 * Is this just a zero?
1871 	 */
1872 	if (size == 0 || value[0] == '\0')
1873 		return (1);
1874 	/*
1875 	 * If any character is unprintable, or if a null appears
1876 	 * anywhere except at the end of a string, the whole
1877 	 * property is "unprintable".
1878 	 */
1879 	for (i = 0; i < size; ++i) {
1880 		if (value[i] == '\0')
1881 			return (i != (size - 1));
1882 		if (!isascii(value[i]) || iscntrl(value[i]))
1883 			return (1);
1884 	}
1885 	return (0);
1886 }
1887 
1888 static int
1889 promopen(int oflag)
1890 {
1891 	for (;;)  {
1892 		if ((prom_fd = open(opts.o_promdev, oflag)) < 0)  {
1893 			if (errno == EAGAIN)   {
1894 				(void) sleep(5);
1895 				continue;
1896 			}
1897 			if (errno == ENXIO)
1898 				return (-1);
1899 			if (getzoneid() == GLOBAL_ZONEID) {
1900 				err(-1, "cannot open %s", opts.o_promdev);
1901 			}
1902 			/* not an error if this isn't the global zone */
1903 			warnx("openprom facility not available");
1904 			exit(0);
1905 		} else
1906 			return (0);
1907 	}
1908 }
1909 
1910 static void
1911 promclose(void)
1912 {
1913 	if (close(prom_fd) < 0)
1914 		err(-1, "close error on %s", opts.o_promdev);
1915 }
1916 
1917 /*
1918  * Get and print the name of the frame buffer device.
1919  */
1920 int
1921 do_fbname(void)
1922 {
1923 	int	retval;
1924 	char fbuf_path[MAXPATHLEN];
1925 
1926 	retval =  modctl(MODGETFBNAME, (caddr_t)fbuf_path);
1927 
1928 	if (retval == 0) {
1929 		(void) printf("%s\n", fbuf_path);
1930 	} else {
1931 		if (retval == EFAULT) {
1932 			(void) fprintf(stderr,
1933 			"Error copying fb path to userland\n");
1934 		} else {
1935 			(void) fprintf(stderr,
1936 			"Console output device is not a frame buffer\n");
1937 		}
1938 		return (1);
1939 	}
1940 	return (0);
1941 }
1942 
1943 /*
1944  * Get and print the PROM version.
1945  */
1946 int
1947 do_promversion(void)
1948 {
1949 	Oppbuf	oppbuf;
1950 	struct openpromio *opp = &(oppbuf.opp);
1951 
1952 	if (promopen(O_RDONLY))  {
1953 		(void) fprintf(stderr, "Cannot open openprom device\n");
1954 		return (1);
1955 	}
1956 
1957 	opp->oprom_size = MAXVALSIZE;
1958 	if (ioctl(prom_fd, OPROMGETVERSION, opp) < 0)
1959 		err(-1, "OPROMGETVERSION");
1960 
1961 	(void) printf("%s\n", opp->oprom_array);
1962 	promclose();
1963 	return (0);
1964 }
1965 
1966 int
1967 do_productinfo(void)
1968 {
1969 	di_node_t root, next_node;
1970 	di_prom_handle_t promh;
1971 	static const char *root_prop[] = { "name", "model", "banner-name",
1972 					"compatible" };
1973 	static const char *root_propv[] = { "name", "model", "banner-name",
1974 					"compatible", "idprom" };
1975 	static const char *oprom_prop[] = { "model", "version" };
1976 
1977 
1978 	root = di_init("/", DINFOCPYALL);
1979 
1980 	if (root == DI_NODE_NIL) {
1981 		(void) fprintf(stderr, "di_init() failed\n");
1982 		return (1);
1983 	}
1984 
1985 	promh = di_prom_init();
1986 
1987 	if (promh == DI_PROM_HANDLE_NIL) {
1988 		(void) fprintf(stderr, "di_prom_init() failed\n");
1989 		return (1);
1990 	}
1991 
1992 	if (opts.o_verbose) {
1993 		dump_prodinfo(promh, root, root_propv, "root",
1994 		    NUM_ELEMENTS(root_propv));
1995 
1996 		/* Get model and version properties under node "openprom" */
1997 		next_node = find_node_by_name(promh, root, "openprom");
1998 		if (next_node != DI_NODE_NIL)
1999 			dump_prodinfo(promh, next_node, oprom_prop,
2000 			    "openprom", NUM_ELEMENTS(oprom_prop));
2001 
2002 	} else
2003 		dump_prodinfo(promh, root, root_prop, "root",
2004 		    NUM_ELEMENTS(root_prop));
2005 	di_prom_fini(promh);
2006 	di_fini(root);
2007 	return (0);
2008 }
2009 
2010 di_node_t
2011 find_node_by_name(di_prom_handle_t promh, di_node_t parent,
2012     char *node_name)
2013 {
2014 	di_node_t next_node;
2015 	uchar_t *prop_valp;
2016 
2017 	for (next_node = di_child_node(parent); next_node != DI_NODE_NIL;
2018 	    next_node = di_sibling_node(next_node)) {
2019 		int len;
2020 
2021 		len = get_propval_by_name(promh, next_node, "name", &prop_valp);
2022 		if ((len != -1) && (strcmp((char *)prop_valp, node_name) == 0))
2023 			return (next_node);
2024 	}
2025 	return (DI_NODE_NIL);
2026 }
2027 
2028 
2029 int
2030 get_propval_by_name(di_prom_handle_t promh, di_node_t node, const char *name,
2031     uchar_t **valp)
2032 {
2033 	int len;
2034 	uchar_t *bufp;
2035 
2036 	len = di_prom_prop_lookup_bytes(promh, node, name,
2037 	    (uchar_t **)&bufp);
2038 	if (len != -1) {
2039 		*valp = (uchar_t *)malloc(len);
2040 		(void) memcpy(*valp, bufp, len);
2041 	}
2042 	return (len);
2043 }
2044 
2045 
2046 static void
2047 dump_prodinfo(di_prom_handle_t promh, di_node_t node, const char **propstr,
2048     char *node_name, int num)
2049 {
2050 	int out, len, index1, index, endswap = 0;
2051 	uchar_t *prop_valp;
2052 
2053 	for (index1 = 0; index1 < num; index1++) {
2054 		len = get_propval_by_name(promh, node, propstr[index1],
2055 		    &prop_valp);
2056 		if (len != -1) {
2057 			if (strcmp(node_name, "root"))
2058 				(void) printf("%s ", node_name);
2059 
2060 			(void) printf("%s: ", propstr[index1]);
2061 
2062 			if (print_composite_string((const char *)
2063 			    propstr[index1], (char *)prop_valp, len)) {
2064 				free(prop_valp);
2065 				continue;
2066 			}
2067 
2068 			if (!unprintable((char *)prop_valp, len)) {
2069 				(void) printf(" %s\n", (char *)prop_valp);
2070 				free(prop_valp);
2071 				continue;
2072 			}
2073 
2074 			(void) printf(" ");
2075 #ifdef  __x86
2076 			endswap = (len % 4) == 0;
2077 #endif  /* __x86 */
2078 			for (index = 0; index < len; index++) {
2079 				if (index && (index % 4 == 0))
2080 					(void) putchar('.');
2081 				if (endswap)
2082 					out = prop_valp[index +
2083 					    (3 - 2 * (index % 4))] & 0xff;
2084 				else
2085 					out = prop_valp[index] & 0xff;
2086 				(void) printf("%02x", out);
2087 			}
2088 			(void) putchar('\n');
2089 			free(prop_valp);
2090 		}
2091 	}
2092 }
2093 
2094 static int
2095 dump_compatible(char *name, int ilev, di_node_t node)
2096 {
2097 	int	ncompat;
2098 	char	*compat_array;
2099 	char	*p, *q;
2100 	int	i;
2101 
2102 	if (node == DI_PATH_NIL)
2103 		return (0);
2104 
2105 	ncompat = di_compatible_names(node, &compat_array);
2106 	if (ncompat <= 0)
2107 		return (0);	/* no 'compatible' available */
2108 
2109 	/* verify integrety of compat_array */
2110 	for (i = 0, p = compat_array; i < ncompat; i++, p += strlen(p) + 1) {
2111 		if (strlen(p) == 0)
2112 			return (0);		/* NULL string */
2113 		for (q = p; *q; q++) {
2114 			if (!(isascii(*q) && (isprint(*q) || isspace(*q))))
2115 				return (0);	/* Not printable or space */
2116 		}
2117 	}
2118 
2119 	/* If name is non-NULL, produce header */
2120 	if (name) {
2121 		indent_to_level(ilev);
2122 		(void) printf("%s properties:\n", name);
2123 	}
2124 	ilev++;
2125 
2126 	/* process like a string array property */
2127 	indent_to_level(ilev);
2128 	(void) printf("name='compatible' type=string items=%d\n", ncompat);
2129 	indent_to_level(ilev);
2130 	(void) printf("    value=");
2131 	for (i = 0, p = compat_array; i < (ncompat - 1);
2132 	    i++, p += strlen(p) + 1)
2133 		(void) printf("'%s' + ", p);
2134 	(void) printf("'%s'", p);
2135 	(void) putchar('\n');
2136 	return (1);
2137 }
2138 
2139 static void
2140 dump_pciid(char *name, int ilev, di_node_t node)
2141 {
2142 	int *vid, *did, *svid, *sdid;
2143 	const char *vname, *dname, *sname;
2144 	pcidb_vendor_t *pciv;
2145 	pcidb_device_t *pcid;
2146 	pcidb_subvd_t *pcis;
2147 
2148 	const char *unov = "unknown vendor";
2149 	const char *unod = "unknown device";
2150 	const char *unos = "unknown subsystem";
2151 
2152 	if (opts.o_pcidb == NULL)
2153 		return;
2154 
2155 	vname = unov;
2156 	dname = unod;
2157 	sname = unos;
2158 
2159 	if (devinfo_is_pci(node) == NULL) {
2160 		return;
2161 	}
2162 
2163 	/*
2164 	 * All devices should have a vendor and device id, if we fail to find
2165 	 * one, then we're going to return right here and not print anything.
2166 	 *
2167 	 * We're going to also check for the subsystem-vendor-id and
2168 	 * subsystem-id. If we don't find one of them, we're going to assume
2169 	 * that this device does not have one. In that case, we will never
2170 	 * attempt to try and print anything related to that. If it does have
2171 	 * both, then we are going to look them up and print the appropriate
2172 	 * string if we find it or not.
2173 	 */
2174 	if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "vendor-id", &vid) <= 0)
2175 		return;
2176 
2177 	if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "device-id", &did) <= 0)
2178 		return;
2179 
2180 	if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "subsystem-vendor-id",
2181 	    &svid) <= 0 || di_prop_lookup_ints(DDI_DEV_T_ANY, node,
2182 	    "subsystem-id", &sdid) <= 0) {
2183 		svid = NULL;
2184 		sdid = NULL;
2185 		sname = NULL;
2186 	}
2187 
2188 	pciv = pcidb_lookup_vendor(opts.o_pcidb, vid[0]);
2189 	if (pciv == NULL)
2190 		goto print;
2191 	vname = pcidb_vendor_name(pciv);
2192 
2193 	pcid = pcidb_lookup_device_by_vendor(pciv, did[0]);
2194 	if (pcid == NULL)
2195 		goto print;
2196 	dname = pcidb_device_name(pcid);
2197 
2198 	if (svid != NULL) {
2199 		pcis = pcidb_lookup_subvd_by_device(pcid, svid[0], sdid[0]);
2200 		if (pcis == NULL)
2201 			goto print;
2202 		sname = pcidb_subvd_name(pcis);
2203 	}
2204 
2205 print:
2206 	/* If name is non-NULL, produce header */
2207 	if (name) {
2208 		indent_to_level(ilev);
2209 		(void) printf("%s properties:\n", name);
2210 	}
2211 	ilev++;
2212 
2213 	/* These are all going to be single string properties */
2214 	indent_to_level(ilev);
2215 	(void) printf("name='vendor-name' type=string items=1\n");
2216 	indent_to_level(ilev);
2217 	(void) printf("    value='%s'\n", vname);
2218 
2219 	indent_to_level(ilev);
2220 	(void) printf("name='device-name' type=string items=1\n");
2221 	indent_to_level(ilev);
2222 	(void) printf("    value='%s'\n", dname);
2223 
2224 	if (sname != NULL) {
2225 		indent_to_level(ilev);
2226 		(void) printf("name='subsystem-name' type=string items=1\n");
2227 		indent_to_level(ilev);
2228 		(void) printf("    value='%s'\n", sname);
2229 	}
2230 }
2231