xref: /illumos-gate/usr/src/cmd/prtconf/pdevinfo.c (revision 74a8d72a557428e11e39b309193987d43fc7d66e)
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 dump_prop_list_common(const dumpops_t *, int, void *);
132 static void walk_driver(di_node_t, di_devlink_handle_t);
133 static int dump_devs(di_node_t, void *);
134 static int dump_prop_list(const dumpops_t *, const char *, int, di_node_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 /*
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 static void
506 dump_prop_list_common(const dumpops_t *dumpops, int ilev, void *node)
507 {
508 	void *prop = DI_PROP_NIL, *prop_data;
509 	char *p;
510 	int i, prop_type, nitems;
511 
512 	while ((prop = NEXTPROP(dumpops)(node, prop)) != DI_PROP_NIL) {
513 		nitems = prop_type_guess(dumpops, prop, &prop_data, &prop_type);
514 		if (nitems < 0)
515 			continue;
516 
517 		indent_to_level(ilev);
518 		(void) printf("name='%s' type=", PROPNAME(dumpops)(prop));
519 
520 		switch (prop_type) {
521 		case DI_PROP_TYPE_UNDEF_IT:
522 			(void) printf("undef");
523 			break;
524 		case DI_PROP_TYPE_BOOLEAN:
525 			(void) printf("boolean");
526 			break;
527 		case DI_PROP_TYPE_INT:
528 			(void) printf("int");
529 			break;
530 		case DI_PROP_TYPE_INT64:
531 			(void) printf("int64");
532 			break;
533 		case DI_PROP_TYPE_BYTE:
534 			(void) printf("byte");
535 			break;
536 		case DI_PROP_TYPE_STRING:
537 			(void) printf("string");
538 			break;
539 		case DI_PROP_TYPE_UNKNOWN:
540 			(void) printf("unknown");
541 			break;
542 		default:
543 			/* Should never be here */
544 			(void) printf("0x%x", prop_type);
545 		}
546 
547 		if (nitems != 0)
548 			(void) printf(" items=%i", nitems);
549 
550 		/* print the major and minor numbers for a device property */
551 		if (PROPDEVT(dumpops) != NULL) {
552 			dev_t dev;
553 
554 			dev = PROPDEVT(dumpops)(prop);
555 			if (dev != DDI_DEV_T_NONE) {
556 				(void) printf(" dev=(%u,%u)",
557 				    (uint_t)major(dev), (uint_t)minor(dev));
558 			} else {
559 				(void) printf(" dev=none");
560 			}
561 		}
562 
563 		(void) putchar('\n');
564 
565 		if (nitems == 0)
566 			continue;
567 
568 		indent_to_level(ilev);
569 
570 		(void) printf("    value=");
571 
572 		switch (prop_type) {
573 		case DI_PROP_TYPE_INT:
574 			for (i = 0; i < nitems - 1; i++)
575 				(void) printf("%8.8x.", ((int *)prop_data)[i]);
576 			(void) printf("%8.8x", ((int *)prop_data)[i]);
577 			break;
578 		case DI_PROP_TYPE_INT64:
579 			for (i = 0; i < nitems - 1; i++)
580 				(void) printf("%16.16llx.",
581 				    ((long long *)prop_data)[i]);
582 			(void) printf("%16.16llx", ((long long *)prop_data)[i]);
583 			break;
584 		case DI_PROP_TYPE_STRING:
585 			p = (char *)prop_data;
586 			for (i = 0; i < nitems - 1; i++) {
587 				(void) printf("'%s' + ", p);
588 				p += strlen(p) + 1;
589 			}
590 			(void) printf("'%s'", p);
591 			break;
592 		default:
593 			for (i = 0; i < nitems - 1; i++)
594 				(void) printf("%2.2x.",
595 				    ((uint8_t *)prop_data)[i]);
596 			(void) printf("%2.2x", ((uint8_t *)prop_data)[i]);
597 		}
598 
599 		(void) putchar('\n');
600 	}
601 }
602 
603 /*
604  * walk_driver is a debugging facility.
605  */
606 static void
607 walk_driver(di_node_t root, di_devlink_handle_t devlink_hdl)
608 {
609 	di_node_t node;
610 
611 	node = di_drv_first_node(dbg.d_drivername, root);
612 
613 	while (node != DI_NODE_NIL) {
614 		(void) dump_devs(node, devlink_hdl);
615 		node = di_drv_next_node(node);
616 	}
617 }
618 
619 /*
620  * print out information about this node, returns appropriate code.
621  */
622 /*ARGSUSED1*/
623 static int
624 dump_devs(di_node_t node, void *arg)
625 {
626 	di_devlink_handle_t	devlink_hdl = (di_devlink_handle_t)arg;
627 	int			ilev = 0;	/* indentation level */
628 	char			*driver_name;
629 	di_node_t		root_node, tmp;
630 
631 	if (dbg.d_debug) {
632 		char *path = di_devfs_path(node);
633 		dprintf("Dump node %s\n", path);
634 		di_devfs_path_free(path);
635 	}
636 
637 	if (dbg.d_bydriver) {
638 		ilev = 1;
639 	} else {
640 		/* figure out indentation level */
641 		tmp = node;
642 		while ((tmp = di_parent_node(tmp)) != DI_NODE_NIL)
643 			ilev++;
644 
645 		if (opts.o_target && !opts.o_ancestors) {
646 			ilev -= opts.o_target - 1;
647 		}
648 	}
649 
650 	if (opts.o_target && !node_display(node)) {
651 		/*
652 		 * if we're only displaying certain nodes and this one
653 		 * isn't flagged, skip it.
654 		 */
655 		return (DI_WALK_CONTINUE);
656 	}
657 
658 	indent_to_level(ilev);
659 
660 	(void) printf("%s", di_node_name(node));
661 
662 	/*
663 	 * if this node does not have an instance number or is the
664 	 * root node (1229946), we don't print an instance number
665 	 */
666 	root_node = tmp = node;
667 	while ((tmp = di_parent_node(tmp)) != DI_NODE_NIL)
668 		root_node = tmp;
669 	if ((di_instance(node) >= 0) && (node != root_node))
670 		(void) printf(", instance #%d", di_instance(node));
671 
672 	if (opts.o_drv_name) {
673 		driver_name = di_driver_name(node);
674 		if (driver_name != NULL)
675 			(void) printf(" (driver name: %s)", driver_name);
676 	} else if (di_retired(node)) {
677 		(void) printf(" (retired)");
678 	} else if (di_state(node) & DI_DRIVER_DETACHED)
679 		(void) printf(" (driver not attached)");
680 
681 	(void) printf("\n");
682 
683 	if (opts.o_verbose)  {
684 		if (dump_prop_list(&sysprop_dumpops, "System", ilev + 1,
685 		    node)) {
686 			(void) dump_prop_list(&globprop_dumpops, NULL, ilev + 1,
687 			    node);
688 		} else {
689 			(void) dump_prop_list(&globprop_dumpops,
690 			    "System software", ilev + 1, node);
691 		}
692 		(void) dump_prop_list(&drvprop_dumpops, "Driver", ilev + 1,
693 		    node);
694 		(void) dump_prop_list(&hwprop_dumpops, "Hardware", ilev + 1,
695 		    node);
696 		dump_priv_data(ilev + 1, node);
697 		dump_pathing_data(ilev + 1, node);
698 		dump_link_data(ilev + 1, node, devlink_hdl);
699 		dump_minor_data(ilev + 1, node, devlink_hdl);
700 	}
701 
702 	if (opts.o_target)
703 		return (DI_WALK_CONTINUE);
704 
705 	if (!opts.o_pseudodevs && (strcmp(di_node_name(node), "pseudo") == 0))
706 		return (DI_WALK_PRUNECHILD);
707 
708 	return (DI_WALK_CONTINUE);
709 }
710 
711 /*
712  * Returns 0 if nothing is printed, 1 otherwise
713  */
714 static int
715 dump_prop_list(const dumpops_t *dumpops, const char *name, int ilev,
716     di_node_t node)
717 {
718 	if (NEXTPROP(dumpops)(node, DI_PROP_NIL) == DI_PROP_NIL)
719 		return (0);
720 
721 	if (name != NULL)  {
722 		indent_to_level(ilev);
723 		(void) printf("%s properties:\n", name);
724 	}
725 
726 	dump_prop_list_common(dumpops, ilev + 1, node);
727 	return (1);
728 }
729 
730 
731 /* _error([no_perror, ] fmt [, arg ...]) */
732 static int
733 _error(const char *opt_noperror, ...)
734 {
735 	int saved_errno;
736 	va_list ap;
737 	int no_perror = 0;
738 	const char *fmt;
739 
740 	saved_errno = errno;
741 
742 	(void) fprintf(stderr, "%s: ", opts.o_progname);
743 
744 	va_start(ap, opt_noperror);
745 	if (opt_noperror == NULL) {
746 		no_perror = 1;
747 		fmt = va_arg(ap, char *);
748 	} else
749 		fmt = opt_noperror;
750 	(void) vfprintf(stderr, fmt, ap);
751 	va_end(ap);
752 
753 	if (no_perror)
754 		(void) fprintf(stderr, "\n");
755 	else {
756 		(void) fprintf(stderr, ": ");
757 		errno = saved_errno;
758 		perror("");
759 	}
760 
761 	return (-1);
762 }
763 
764 
765 /*
766  * The rest of the routines handle printing the raw prom devinfo (-p option).
767  *
768  * 128 is the size of the largest (currently) property name
769  * 16k - MAXNAMESZ - sizeof (int) is the size of the largest
770  * (currently) property value that is allowed.
771  * the sizeof (uint_t) is from struct openpromio
772  */
773 
774 #define	MAXNAMESZ	128
775 #define	MAXVALSIZE	(16384 - MAXNAMESZ - sizeof (uint_t))
776 #define	BUFSIZE		(MAXNAMESZ + MAXVALSIZE + sizeof (uint_t))
777 typedef union {
778 	char buf[BUFSIZE];
779 	struct openpromio opp;
780 } Oppbuf;
781 
782 static int prom_fd;
783 static uchar_t *prom_snapshot;
784 
785 static int
786 is_openprom(void)
787 {
788 	Oppbuf	oppbuf;
789 	struct openpromio *opp = &(oppbuf.opp);
790 	unsigned int i;
791 
792 	opp->oprom_size = MAXVALSIZE;
793 	if (ioctl(prom_fd, OPROMGETCONS, opp) < 0)
794 		exit(_error("OPROMGETCONS"));
795 
796 	i = (unsigned int)((unsigned char)opp->oprom_array[0]);
797 	return ((i & OPROMCONS_OPENPROM) == OPROMCONS_OPENPROM);
798 }
799 
800 int
801 do_prominfo(void)
802 {
803 	uint_t arg = opts.o_verbose;
804 
805 	if (promopen(O_RDONLY))  {
806 		exit(_error("openeepr device open failed"));
807 	}
808 
809 	if (is_openprom() == 0)  {
810 		(void) fprintf(stderr, "System architecture does not "
811 		    "support this option of this command.\n");
812 		return (1);
813 	}
814 
815 	/* OPROMSNAPSHOT returns size in arg */
816 	if (ioctl(prom_fd, OPROMSNAPSHOT, &arg) < 0)
817 		exit(_error("OPROMSNAPSHOT"));
818 
819 	if (arg == 0)
820 		return (1);
821 
822 	if ((prom_snapshot = malloc(arg)) == NULL)
823 		exit(_error("failed to allocate memory"));
824 
825 	/* copy out the snapshot for printing */
826 	/*LINTED*/
827 	*(uint_t *)prom_snapshot = arg;
828 	if (ioctl(prom_fd, OPROMCOPYOUT, prom_snapshot) < 0)
829 		exit(_error("OPROMCOPYOUT"));
830 
831 	promclose();
832 
833 	/* print out information */
834 	walk(prom_snapshot, arg, 0);
835 	free(prom_snapshot);
836 
837 	return (0);
838 }
839 
840 static void
841 walk(uchar_t *buf, uint_t size, int level)
842 {
843 	int error;
844 	nvlist_t *nvl, *cnvl;
845 	nvpair_t *child = NULL;
846 	uchar_t *cbuf = NULL;
847 	uint_t csize;
848 
849 	/* Expand to an nvlist */
850 	if (nvlist_unpack((char *)buf, size, &nvl, 0))
851 		exit(_error("error processing snapshot"));
852 
853 	/* print current node */
854 	dump_node(nvl, level);
855 
856 	/* print children */
857 	error = nvlist_lookup_byte_array(nvl, "@child", &cbuf, &csize);
858 	if ((error == ENOENT) || (cbuf == NULL))
859 		return;		/* no child exists */
860 
861 	if (error || nvlist_unpack((char *)cbuf, csize, &cnvl, 0))
862 		exit(_error("error processing snapshot"));
863 
864 	while (child = nvlist_next_nvpair(cnvl, child)) {
865 		char *name = nvpair_name(child);
866 		data_type_t type = nvpair_type(child);
867 		uchar_t *nodebuf;
868 		uint_t nodesize;
869 		if (strcmp("node", name) != 0) {
870 			dprintf("unexpected nvpair name %s != name\n", name);
871 			continue;
872 		}
873 		if (type != DATA_TYPE_BYTE_ARRAY) {
874 			dprintf("unexpected nvpair type %d, not byte array \n",
875 			    type);
876 			continue;
877 		}
878 
879 		(void) nvpair_value_byte_array(child,
880 		    (uchar_t **)&nodebuf, &nodesize);
881 		walk(nodebuf, nodesize, level + 1);
882 	}
883 
884 	nvlist_free(nvl);
885 }
886 
887 /*
888  * Print all properties and values
889  */
890 static void
891 dump_node(nvlist_t *nvl, int level)
892 {
893 	int id = 0;
894 	char *name = NULL;
895 	nvpair_t *nvp = NULL;
896 
897 	indent_to_level(level);
898 	(void) printf("Node");
899 	if (!opts.o_verbose) {
900 		if (nvlist_lookup_string(nvl, "name", &name))
901 			(void) printf("data not available");
902 		else
903 			(void) printf(" '%s'", name);
904 		(void) putchar('\n');
905 		return;
906 	}
907 	(void) nvlist_lookup_int32(nvl, "@nodeid", &id);
908 	(void) printf(" %#08x\n", id);
909 
910 	while (nvp = nvlist_next_nvpair(nvl, nvp)) {
911 		name = nvpair_name(nvp);
912 		if (name[0] == '@')
913 			continue;
914 
915 		print_one(nvp, level + 1);
916 	}
917 	(void) putchar('\n');
918 }
919 
920 static const char *
921 path_state_name(di_path_state_t st)
922 {
923 	switch (st) {
924 		case DI_PATH_STATE_ONLINE:
925 			return ("online");
926 		case DI_PATH_STATE_STANDBY:
927 			return ("standby");
928 		case DI_PATH_STATE_OFFLINE:
929 			return ("offline");
930 		case DI_PATH_STATE_FAULT:
931 			return ("faulted");
932 	}
933 	return ("unknown");
934 }
935 
936 /*
937  * Print all phci's each client is connected to.
938  */
939 static void
940 dump_pathing_data(int ilev, di_node_t node)
941 {
942 	di_path_t	pi = DI_PATH_NIL;
943 	di_node_t	phci_node;
944 	char		*phci_path;
945 	int		path_instance;
946 	int		firsttime = 1;
947 
948 	if (node == DI_PATH_NIL)
949 		return;
950 
951 	while ((pi = di_path_client_next_path(node, pi)) != DI_PATH_NIL) {
952 		if (firsttime) {
953 			indent_to_level(ilev);
954 			firsttime = 0;
955 			ilev++;
956 			(void) printf("Paths from multipath bus adapters:\n");
957 		}
958 
959 		/*
960 		 * Print the path instance and full "pathinfo" path, which is
961 		 * the same as the /devices devifo path had the device been
962 		 * enumerated under pHCI.
963 		 */
964 		phci_node = di_path_phci_node(pi);
965 		phci_path = di_devfs_path(phci_node);
966 		path_instance = di_path_instance(pi);
967 		if (phci_path) {
968 			if (path_instance > 0) {
969 				indent_to_level(ilev);
970 				(void) printf("Path %d: %s/%s@%s\n",
971 				    path_instance, phci_path,
972 				    di_node_name(node),
973 				    di_path_bus_addr(pi));
974 			}
975 			di_devfs_path_free(phci_path);
976 		}
977 
978 		/* print phci driver, instance, and path state information */
979 		indent_to_level(ilev);
980 		(void) printf("%s#%d (%s)\n", di_driver_name(phci_node),
981 		    di_instance(phci_node), path_state_name(di_path_state(pi)));
982 
983 		dump_prop_list_common(&pathprop_dumpops, ilev + 1, pi);
984 	}
985 }
986 
987 static int
988 dump_minor_data_links(di_devlink_t devlink, void *arg)
989 {
990 	int ilev = (intptr_t)arg;
991 	indent_to_level(ilev);
992 	(void) printf("dev_link=%s\n", di_devlink_path(devlink));
993 	return (DI_WALK_CONTINUE);
994 }
995 
996 static void
997 dump_minor_data_paths(int ilev, di_minor_t minor,
998     di_devlink_handle_t devlink_hdl)
999 {
1000 	char	*path, *type;
1001 	int	spec_type;
1002 
1003 	/* get the path to the device and the minor node name */
1004 	if ((path = di_devfs_minor_path(minor)) == NULL)
1005 		exit(_error("failed to allocate memory"));
1006 
1007 	/* display the path to this minor node */
1008 	indent_to_level(ilev);
1009 	(void) printf("dev_path=%s\n", path);
1010 
1011 	if (devlink_hdl != NULL) {
1012 
1013 		/* get the device minor node information */
1014 		spec_type = di_minor_spectype(minor);
1015 		switch (di_minor_type(minor)) {
1016 			case DDM_MINOR:
1017 				type = "minor";
1018 				break;
1019 			case DDM_ALIAS:
1020 				type = "alias";
1021 				break;
1022 			case DDM_DEFAULT:
1023 				type = "default";
1024 				break;
1025 			case DDM_INTERNAL_PATH:
1026 				type = "internal";
1027 				break;
1028 			default:
1029 				type = "unknown";
1030 				break;
1031 		}
1032 
1033 		/* display the device minor node information */
1034 		indent_to_level(ilev + 1);
1035 		(void) printf("spectype=%s type=%s\n",
1036 		    (spec_type == S_IFBLK) ? "blk" : "chr", type);
1037 
1038 		/* display all the devlinks for this device minor node */
1039 		(void) di_devlink_walk(devlink_hdl, NULL, path,
1040 		    0, (void *)(intptr_t)(ilev + 1), dump_minor_data_links);
1041 	}
1042 
1043 	di_devfs_path_free(path);
1044 }
1045 
1046 static void
1047 create_minor_list(di_node_t node)
1048 {
1049 	di_minor_t	minor, minor_head, minor_tail, minor_prev, minor_walk;
1050 	int		major;
1051 
1052 	/* if there are no minor nodes, bail */
1053 	if (di_minor_next(node, DI_MINOR_NIL) == DI_MINOR_NIL)
1054 		return;
1055 
1056 	/*
1057 	 * here we want to create lists of minor nodes with the same
1058 	 * dev_t.  to do this we first sort all the minor nodes by devt.
1059 	 *
1060 	 * the algorithm used here is a bubble sort, so performance sucks.
1061 	 * but it's probably ok here because most device instances don't
1062 	 * have that many minor nodes.  also we're doing this as we're
1063 	 * displaying each node so it doesn't look like we're pausing
1064 	 * output for a long time.
1065 	 */
1066 	major = di_driver_major(node);
1067 	minor_head = minor_tail = minor = DI_MINOR_NIL;
1068 	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
1069 		dev_t	dev = di_minor_devt(minor);
1070 
1071 		/* skip /pseudo/clone@0 minor nodes */
1072 		if (major != major(dev))
1073 			continue;
1074 
1075 		minor_ptr_set(minor, DI_MINOR_NIL);
1076 		if (minor_head == DI_MINOR_NIL) {
1077 			/* this is the first minor node we're looking at */
1078 			minor_head = minor_tail = minor;
1079 			continue;
1080 		}
1081 
1082 		/*
1083 		 * if the new dev is less than the old dev, update minor_head
1084 		 * so it points to the beginning of the list.  ie it points
1085 		 * to the node with the lowest dev value
1086 		 */
1087 		if (dev <= di_minor_devt(minor_head)) {
1088 			minor_ptr_set(minor, minor_head);
1089 			minor_head = minor;
1090 			continue;
1091 		}
1092 
1093 		minor_prev = minor_head;
1094 		minor_walk = minor_ptr(minor_head);
1095 		while ((minor_walk != DI_MINOR_NIL) &&
1096 		    (dev > di_minor_devt(minor_walk))) {
1097 			minor_prev = minor_walk;
1098 			minor_walk = minor_ptr(minor_walk);
1099 		}
1100 		minor_ptr_set(minor, minor_walk);
1101 		minor_ptr_set(minor_prev, minor);
1102 		if (minor_walk == NULL)
1103 			minor_tail = minor;
1104 	}
1105 
1106 	/* check if there were any non /pseudo/clone@0 nodes.  if not, bail */
1107 	if (minor_head == DI_MINOR_NIL)
1108 		return;
1109 
1110 	/*
1111 	 * now that we have a list of minor nodes sorted by devt
1112 	 * we walk through the list and break apart the entire list
1113 	 * to create circular lists of minor nodes with matching devts.
1114 	 */
1115 	minor_prev = minor_head;
1116 	minor_walk = minor_ptr(minor_head);
1117 	while (minor_walk != DI_MINOR_NIL) {
1118 		if (di_minor_devt(minor_prev) != di_minor_devt(minor_walk)) {
1119 			minor_ptr_set(minor_prev, minor_head);
1120 			minor_head = minor_walk;
1121 		}
1122 		minor_prev = minor_walk;
1123 		minor_walk = minor_ptr(minor_walk);
1124 	}
1125 	minor_ptr_set(minor_tail, minor_head);
1126 }
1127 
1128 static void
1129 link_lnode_disp(di_link_t link, uint_t endpoint, int ilev,
1130     di_devlink_handle_t devlink_hdl)
1131 {
1132 	di_lnode_t	lnode;
1133 	char		*name, *path;
1134 	int		displayed_path, spec_type;
1135 	di_node_t	node = DI_NODE_NIL;
1136 	dev_t		devt = DDI_DEV_T_NONE;
1137 
1138 	lnode = di_link_to_lnode(link, endpoint);
1139 
1140 	indent_to_level(ilev);
1141 	name = di_lnode_name(lnode);
1142 	spec_type = di_link_spectype(link);
1143 
1144 	(void) printf("mod=%s", name);
1145 
1146 	/*
1147 	 * if we're displaying the source of a link, we should display
1148 	 * the target access mode.  (either block or char.)
1149 	 */
1150 	if (endpoint == DI_LINK_SRC)
1151 		(void) printf(" accesstype=%s",
1152 		    (spec_type == S_IFBLK) ? "blk" : "chr");
1153 
1154 	/*
1155 	 * check if the lnode is bound to a specific device
1156 	 * minor node (i.e.  if it's bound to a dev_t) and
1157 	 * if so display the dev_t value and any possible
1158 	 * minor node pathing information.
1159 	 */
1160 	displayed_path = 0;
1161 	if (di_lnode_devt(lnode, &devt) == 0) {
1162 		di_minor_t	minor = DI_MINOR_NIL;
1163 
1164 		(void) printf(" dev=(%u,%u)\n",
1165 		    (uint_t)major(devt), (uint_t)minor(devt));
1166 
1167 		/* display paths to the src devt minor node */
1168 		while (minor = di_minor_next(node, minor)) {
1169 			if (devt != di_minor_devt(minor))
1170 				continue;
1171 
1172 			if ((endpoint == DI_LINK_TGT) &&
1173 			    (spec_type != di_minor_spectype(minor)))
1174 				continue;
1175 
1176 			dump_minor_data_paths(ilev + 1, minor, devlink_hdl);
1177 			displayed_path = 1;
1178 		}
1179 	} else {
1180 		(void) printf("\n");
1181 	}
1182 
1183 	if (displayed_path)
1184 		return;
1185 
1186 	/*
1187 	 * This device lnode is not did not have any minor node
1188 	 * pathing information so display the path to device node.
1189 	 */
1190 	node = di_lnode_devinfo(lnode);
1191 	if ((path = di_devfs_path(node)) == NULL)
1192 		exit(_error("failed to allocate memory"));
1193 
1194 	indent_to_level(ilev + 1);
1195 	(void) printf("dev_path=%s\n", path);
1196 	di_devfs_path_free(path);
1197 }
1198 
1199 static void
1200 dump_minor_link_data(int ilev, di_node_t node, dev_t devt,
1201     di_devlink_handle_t devlink_hdl)
1202 {
1203 	int		first = 1;
1204 	di_link_t	link;
1205 
1206 	link = DI_LINK_NIL;
1207 	while (link = di_link_next_by_node(node, link, DI_LINK_TGT)) {
1208 		di_lnode_t	tgt_lnode;
1209 		dev_t		tgt_devt = DDI_DEV_T_NONE;
1210 
1211 		tgt_lnode = di_link_to_lnode(link, DI_LINK_TGT);
1212 
1213 		if (di_lnode_devt(tgt_lnode, &tgt_devt) != 0)
1214 			continue;
1215 
1216 		if (devt != tgt_devt)
1217 			continue;
1218 
1219 		if (first) {
1220 			first = 0;
1221 			indent_to_level(ilev);
1222 			(void) printf("Device Minor Layered Under:\n");
1223 		}
1224 
1225 		/* displayed this lnode */
1226 		lnode_displayed_set(tgt_lnode);
1227 		link_lnode_disp(link, DI_LINK_SRC, ilev + 1, devlink_hdl);
1228 	}
1229 
1230 	link = DI_LINK_NIL;
1231 	while (link = di_link_next_by_node(node, link, DI_LINK_SRC)) {
1232 		di_lnode_t	src_lnode;
1233 		dev_t		src_devt = DDI_DEV_T_NONE;
1234 
1235 		src_lnode = di_link_to_lnode(link, DI_LINK_SRC);
1236 
1237 		if (di_lnode_devt(src_lnode, &src_devt) != 0)
1238 			continue;
1239 
1240 		if (devt != src_devt)
1241 			continue;
1242 
1243 		if (first) {
1244 			first = 0;
1245 			indent_to_level(ilev);
1246 			(void) printf("Device Minor Layered Over:\n");
1247 		}
1248 
1249 		/* displayed this lnode */
1250 		lnode_displayed_set(src_lnode);
1251 		link_lnode_disp(link, DI_LINK_TGT, ilev + 1, devlink_hdl);
1252 	}
1253 }
1254 
1255 static void
1256 dump_minor_data(int ilev, di_node_t node, di_devlink_handle_t devlink_hdl)
1257 {
1258 	di_minor_t	minor, minor_next;
1259 	di_lnode_t	lnode;
1260 	di_link_t	link;
1261 	int		major, firstminor = 1;
1262 
1263 	/*
1264 	 * first go through and mark all lnodes and minor nodes for this
1265 	 * node as undisplayed
1266 	 */
1267 	lnode = DI_LNODE_NIL;
1268 	while (lnode = di_lnode_next(node, lnode))
1269 		lnode_displayed_clear(lnode);
1270 	minor = DI_MINOR_NIL;
1271 	while (minor = di_minor_next(node, minor)) {
1272 		minor_displayed_clear(minor);
1273 	}
1274 
1275 	/*
1276 	 * when we display the minor nodes we want to coalesce nodes
1277 	 * that have the same dev_t.  we do this by creating circular
1278 	 * lists of minor nodes with the same devt.
1279 	 */
1280 	create_minor_list(node);
1281 
1282 	/* now we display the driver defined minor nodes */
1283 	major = di_driver_major(node);
1284 	minor = DI_MINOR_NIL;
1285 	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
1286 		dev_t	devt;
1287 
1288 		/*
1289 		 * skip /pseudo/clone@0 minor nodes.
1290 		 * these are only created for DLPIv2 network devices.
1291 		 * since these minor nodes are associated with a driver
1292 		 * and are only bound to a device instance after they
1293 		 * are opened and attached we don't print them out
1294 		 * here.
1295 		 */
1296 		devt = di_minor_devt(minor);
1297 		if (major != major(devt))
1298 			continue;
1299 
1300 		/* skip nodes that may have already been displayed */
1301 		if (minor_displayed(minor))
1302 			continue;
1303 
1304 		if (firstminor) {
1305 			firstminor = 0;
1306 			indent_to_level(ilev++);
1307 			(void) printf("Device Minor Nodes:\n");
1308 		}
1309 
1310 		/* display the device minor node information */
1311 		indent_to_level(ilev);
1312 		(void) printf("dev=(%u,%u)\n",
1313 		    (uint_t)major(devt), (uint_t)minor(devt));
1314 
1315 		minor_next = minor;
1316 		do {
1317 			/* display device minor node path info */
1318 			minor_displayed_set(minor_next);
1319 			dump_minor_data_paths(ilev + 1, minor_next,
1320 			    devlink_hdl);
1321 
1322 			/* get a pointer to the next node */
1323 			minor_next = minor_ptr(minor_next);
1324 		} while (minor_next != minor);
1325 
1326 		/* display who has this device minor node open */
1327 		dump_minor_link_data(ilev + 1, node, devt, devlink_hdl);
1328 	}
1329 
1330 	/*
1331 	 * now go through all the target lnodes for this node and
1332 	 * if they haven't yet been displayed, display them now.
1333 	 *
1334 	 * this happens in the case of clone opens when an "official"
1335 	 * minor node does not exist for the opened devt
1336 	 */
1337 	link = DI_LINK_NIL;
1338 	while (link = di_link_next_by_node(node, link, DI_LINK_TGT)) {
1339 		dev_t		devt;
1340 
1341 		lnode = di_link_to_lnode(link, DI_LINK_TGT);
1342 
1343 		/* if we've already displayed this target lnode, skip it */
1344 		if (lnode_displayed(lnode))
1345 			continue;
1346 
1347 		if (firstminor) {
1348 			firstminor = 0;
1349 			indent_to_level(ilev++);
1350 			(void) printf("Device Minor Nodes:\n");
1351 		}
1352 
1353 		/* display the device minor node information */
1354 		indent_to_level(ilev);
1355 		(void) di_lnode_devt(lnode, &devt);
1356 		(void) printf("dev=(%u,%u)\n",
1357 		    (uint_t)major(devt), (uint_t)minor(devt));
1358 
1359 		indent_to_level(ilev + 1);
1360 		(void) printf("dev_path=<clone>\n");
1361 
1362 		/* display who has this cloned device minor node open */
1363 		dump_minor_link_data(ilev + 1, node, devt, devlink_hdl);
1364 
1365 		/* mark node as displayed */
1366 		lnode_displayed_set(lnode);
1367 	}
1368 }
1369 
1370 static void
1371 dump_link_data(int ilev, di_node_t node, di_devlink_handle_t devlink_hdl)
1372 {
1373 	int		first = 1;
1374 	di_link_t	link;
1375 
1376 	link = DI_LINK_NIL;
1377 	while (link = di_link_next_by_node(node, link, DI_LINK_SRC)) {
1378 		di_lnode_t	src_lnode;
1379 		dev_t		src_devt = DDI_DEV_T_NONE;
1380 
1381 		src_lnode = di_link_to_lnode(link, DI_LINK_SRC);
1382 
1383 		/*
1384 		 * here we only want to print out layering information
1385 		 * if we are the source and our source lnode is not
1386 		 * associated with any particular dev_t.  (which means
1387 		 * we won't display this link while dumping minor node
1388 		 * info.)
1389 		 */
1390 		if (di_lnode_devt(src_lnode, &src_devt) != -1)
1391 			continue;
1392 
1393 		if (first) {
1394 			first = 0;
1395 			indent_to_level(ilev);
1396 			(void) printf("Device Layered Over:\n");
1397 		}
1398 
1399 		/* displayed this lnode */
1400 		link_lnode_disp(link, DI_LINK_TGT, ilev + 1, devlink_hdl);
1401 	}
1402 }
1403 
1404 /*
1405  * certain 'known' property names may contain 'composite' strings.
1406  * Handle them here, and print them as 'string1' + 'string2' ...
1407  */
1408 static int
1409 print_composite_string(const char *var, char *value, int size)
1410 {
1411 	char *p, *q;
1412 	char *firstp;
1413 
1414 	if ((strcmp(var, "version") != 0) &&
1415 	    (strcmp(var, "compatible") != 0))
1416 		return (0);	/* Not a known composite string */
1417 
1418 	/*
1419 	 * Verify that each string in the composite string is non-NULL,
1420 	 * is within the bounds of the property length, and contains
1421 	 * printable characters or white space. Otherwise let the
1422 	 * caller deal with it.
1423 	 */
1424 	for (firstp = p = value; p < (value + size); p += strlen(p) + 1) {
1425 		if (strlen(p) == 0)
1426 			return (0);		/* NULL string */
1427 		for (q = p; *q; q++) {
1428 			if (!(isascii(*q) && (isprint(*q) || isspace(*q))))
1429 				return (0);	/* Not printable or space */
1430 		}
1431 		if (q > (firstp + size))
1432 			return (0);		/* Out of bounds */
1433 	}
1434 
1435 	for (firstp = p = value; p < (value + size); p += strlen(p) + 1) {
1436 		if (p == firstp)
1437 			(void) printf("'%s'", p);
1438 		else
1439 			(void) printf(" + '%s'", p);
1440 	}
1441 	(void) putchar('\n');
1442 	return (1);
1443 }
1444 
1445 /*
1446  * Print one property and its value. Handle the verbose case.
1447  */
1448 static void
1449 print_one(nvpair_t *nvp, int level)
1450 {
1451 	int i;
1452 	int endswap = 0;
1453 	uint_t valsize;
1454 	char *value;
1455 	char *var = nvpair_name(nvp);
1456 
1457 	indent_to_level(level);
1458 	(void) printf("%s: ", var);
1459 
1460 	switch (nvpair_type(nvp)) {
1461 	case DATA_TYPE_BOOLEAN:
1462 		(void) printf(" \n");
1463 		return;
1464 	case DATA_TYPE_BYTE_ARRAY:
1465 		if (nvpair_value_byte_array(nvp, (uchar_t **)&value,
1466 		    &valsize)) {
1467 			(void) printf("data not available.\n");
1468 			return;
1469 		}
1470 		valsize--;	/* take out null added by driver */
1471 
1472 		/*
1473 		 * Do not print valsize > MAXVALSIZE, to be compatible
1474 		 * with old behavior. E.g. intel's eisa-nvram property
1475 		 * has a size of 65 K.
1476 		 */
1477 		if (valsize > MAXVALSIZE) {
1478 			(void) printf(" \n");
1479 			return;
1480 		}
1481 		break;
1482 	default:
1483 		(void) printf("data type unexpected.\n");
1484 		return;
1485 	}
1486 
1487 	/*
1488 	 * Handle printing verbosely
1489 	 */
1490 	if (print_composite_string(var, value, valsize)) {
1491 		return;
1492 	}
1493 
1494 	if (!unprintable(value, valsize)) {
1495 		(void) printf(" '%s'\n", value);
1496 		return;
1497 	}
1498 
1499 	(void) printf(" ");
1500 #ifdef	__x86
1501 	/*
1502 	 * Due to backwards compatibility constraints x86 int
1503 	 * properties are not in big-endian (ieee 1275) byte order.
1504 	 * If we have a property that is a multiple of 4 bytes,
1505 	 * let's assume it is an array of ints and print the bytes
1506 	 * in little endian order to make things look nicer for
1507 	 * the user.
1508 	 */
1509 	endswap = (valsize % 4) == 0;
1510 #endif	/* __x86 */
1511 	for (i = 0; i < valsize; i++) {
1512 		int out;
1513 		if (i && (i % 4 == 0))
1514 			(void) putchar('.');
1515 		if (endswap)
1516 			out = value[i + (3 - 2 * (i % 4))] & 0xff;
1517 		else
1518 			out = value[i] & 0xff;
1519 
1520 		(void) printf("%02x", out);
1521 	}
1522 	(void) putchar('\n');
1523 }
1524 
1525 static int
1526 unprintable(char *value, int size)
1527 {
1528 	int i;
1529 
1530 	/*
1531 	 * Is this just a zero?
1532 	 */
1533 	if (size == 0 || value[0] == '\0')
1534 		return (1);
1535 	/*
1536 	 * If any character is unprintable, or if a null appears
1537 	 * anywhere except at the end of a string, the whole
1538 	 * property is "unprintable".
1539 	 */
1540 	for (i = 0; i < size; ++i) {
1541 		if (value[i] == '\0')
1542 			return (i != (size - 1));
1543 		if (!isascii(value[i]) || iscntrl(value[i]))
1544 			return (1);
1545 	}
1546 	return (0);
1547 }
1548 
1549 static int
1550 promopen(int oflag)
1551 {
1552 	for (;;)  {
1553 		if ((prom_fd = open(opts.o_promdev, oflag)) < 0)  {
1554 			if (errno == EAGAIN)   {
1555 				(void) sleep(5);
1556 				continue;
1557 			}
1558 			if (errno == ENXIO)
1559 				return (-1);
1560 			if (getzoneid() == GLOBAL_ZONEID) {
1561 				_exit(_error("cannot open %s",
1562 				    opts.o_promdev));
1563 			}
1564 			/* not an error if this isn't the global zone */
1565 			(void) _error(NULL, "openprom facility not available");
1566 			exit(0);
1567 		} else
1568 			return (0);
1569 	}
1570 }
1571 
1572 static void
1573 promclose(void)
1574 {
1575 	if (close(prom_fd) < 0)
1576 		exit(_error("close error on %s", opts.o_promdev));
1577 }
1578 
1579 /*
1580  * Get and print the name of the frame buffer device.
1581  */
1582 int
1583 do_fbname(void)
1584 {
1585 	int	retval;
1586 	char fbuf_path[MAXPATHLEN];
1587 
1588 	retval =  modctl(MODGETFBNAME, (caddr_t)fbuf_path);
1589 
1590 	if (retval == 0) {
1591 		(void) printf("%s\n", fbuf_path);
1592 	} else {
1593 		if (retval == EFAULT) {
1594 			(void) fprintf(stderr,
1595 			"Error copying fb path to userland\n");
1596 		} else {
1597 			(void) fprintf(stderr,
1598 			"Console output device is not a frame buffer\n");
1599 		}
1600 		return (1);
1601 	}
1602 	return (0);
1603 }
1604 
1605 /*
1606  * Get and print the PROM version.
1607  */
1608 int
1609 do_promversion(void)
1610 {
1611 	Oppbuf	oppbuf;
1612 	struct openpromio *opp = &(oppbuf.opp);
1613 
1614 	if (promopen(O_RDONLY))  {
1615 		(void) fprintf(stderr, "Cannot open openprom device\n");
1616 		return (1);
1617 	}
1618 
1619 	opp->oprom_size = MAXVALSIZE;
1620 	if (ioctl(prom_fd, OPROMGETVERSION, opp) < 0)
1621 		exit(_error("OPROMGETVERSION"));
1622 
1623 	(void) printf("%s\n", opp->oprom_array);
1624 	promclose();
1625 	return (0);
1626 }
1627 
1628 int
1629 do_prom_version64(void)
1630 {
1631 #ifdef	sparc
1632 	Oppbuf	oppbuf;
1633 	struct openpromio *opp = &(oppbuf.opp);
1634 	/*LINTED*/
1635 	struct openprom_opr64 *opr = (struct openprom_opr64 *)opp->oprom_array;
1636 
1637 	static const char msg[] =
1638 	    "NOTICE: The firmware on this system does not support the "
1639 	    "64-bit OS.\n"
1640 	    "\tPlease upgrade to at least the following version:\n"
1641 	    "\t\t%s\n\n";
1642 
1643 	if (promopen(O_RDONLY))  {
1644 		(void) fprintf(stderr, "Cannot open openprom device\n");
1645 		return (-1);
1646 	}
1647 
1648 	opp->oprom_size = MAXVALSIZE;
1649 	if (ioctl(prom_fd, OPROMREADY64, opp) < 0)
1650 		exit(_error("OPROMREADY64"));
1651 
1652 	if (opr->return_code == 0)
1653 		return (0);
1654 
1655 	(void) printf(msg, opr->message);
1656 
1657 	promclose();
1658 	return (opr->return_code);
1659 #else
1660 	return (0);
1661 #endif
1662 }
1663 
1664 int
1665 do_productinfo(void)
1666 {
1667 	di_node_t root, next_node;
1668 	di_prom_handle_t promh;
1669 	static const char *root_prop[] = { "name", "model", "banner-name",
1670 					"compatible" };
1671 	static const char *root_propv[] = { "name", "model", "banner-name",
1672 					"compatible", "idprom" };
1673 	static const char *oprom_prop[] = { "model", "version" };
1674 
1675 
1676 	root = di_init("/", DINFOCPYALL);
1677 
1678 	if (root == DI_NODE_NIL) {
1679 		(void) fprintf(stderr, "di_init() failed\n");
1680 		return (1);
1681 	}
1682 
1683 	promh = di_prom_init();
1684 
1685 	if (promh == DI_PROM_HANDLE_NIL) {
1686 		(void) fprintf(stderr, "di_prom_init() failed\n");
1687 		return (1);
1688 	}
1689 
1690 	if (opts.o_verbose) {
1691 		dump_prodinfo(promh, root, root_propv, "root",
1692 		    NUM_ELEMENTS(root_propv));
1693 
1694 		/* Get model and version properties under node "openprom" */
1695 		next_node = find_node_by_name(promh, root, "openprom");
1696 		if (next_node != DI_NODE_NIL)
1697 			dump_prodinfo(promh, next_node, oprom_prop,
1698 			    "openprom", NUM_ELEMENTS(oprom_prop));
1699 
1700 	} else
1701 		dump_prodinfo(promh, root, root_prop, "root",
1702 		    NUM_ELEMENTS(root_prop));
1703 	di_prom_fini(promh);
1704 	di_fini(root);
1705 	return (0);
1706 }
1707 
1708 di_node_t
1709 find_node_by_name(di_prom_handle_t promh, di_node_t parent,
1710 		char *node_name)
1711 {
1712 	di_node_t next_node;
1713 	uchar_t *prop_valp;
1714 
1715 	for (next_node = di_child_node(parent); next_node != DI_NODE_NIL;
1716 	    next_node = di_sibling_node(next_node)) {
1717 		int len;
1718 
1719 		len = get_propval_by_name(promh, next_node, "name", &prop_valp);
1720 		if ((len != -1) && (strcmp((char *)prop_valp, node_name) == 0))
1721 			return (next_node);
1722 	}
1723 	return (DI_NODE_NIL);
1724 }
1725 
1726 
1727 int
1728 get_propval_by_name(di_prom_handle_t promh, di_node_t node, const char *name,
1729 			uchar_t **valp)
1730 {
1731 	int len;
1732 	uchar_t *bufp;
1733 
1734 	len = di_prom_prop_lookup_bytes(promh, node, name,
1735 	    (uchar_t **)&bufp);
1736 	if (len != -1) {
1737 		*valp = (uchar_t *)malloc(len);
1738 		(void) memcpy(*valp, bufp, len);
1739 	}
1740 	return (len);
1741 }
1742 
1743 
1744 static void
1745 dump_prodinfo(di_prom_handle_t promh, di_node_t node, const char **propstr,
1746 		char *node_name, int num)
1747 {
1748 	int out, len, index1, index, endswap = 0;
1749 	uchar_t *prop_valp;
1750 
1751 	for (index1 = 0; index1 < num; index1++) {
1752 		len = get_propval_by_name(promh, node, propstr[index1],
1753 		    &prop_valp);
1754 		if (len != -1) {
1755 			if (strcmp(node_name, "root"))
1756 				(void) printf("%s ", node_name);
1757 
1758 			(void) printf("%s: ", propstr[index1]);
1759 
1760 			if (print_composite_string((const char *)
1761 			    propstr[index1], (char *)prop_valp, len)) {
1762 				free(prop_valp);
1763 				continue;
1764 			}
1765 
1766 			if (!unprintable((char *)prop_valp, len)) {
1767 				(void) printf(" %s\n", (char *)prop_valp);
1768 				free(prop_valp);
1769 				continue;
1770 			}
1771 
1772 			(void) printf(" ");
1773 #ifdef  __x86
1774 			endswap = (len % 4) == 0;
1775 #endif  /* __x86 */
1776 			for (index = 0; index < len; index++) {
1777 				if (index && (index % 4 == 0))
1778 					(void) putchar('.');
1779 				if (endswap)
1780 					out = prop_valp[index +
1781 					    (3 - 2 * (index % 4))] & 0xff;
1782 				else
1783 					out = prop_valp[index] & 0xff;
1784 				(void) printf("%02x", out);
1785 			}
1786 			(void) putchar('\n');
1787 			free(prop_valp);
1788 		}
1789 	}
1790 }
1791