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