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