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