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