xref: /illumos-gate/usr/src/cmd/prtconf/pdevinfo.c (revision bdb9230ac765cb7af3fc1f4119caf2c5720dceb3)
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 
774 	(void) printf("\n");
775 
776 	if (opts.o_verbose)  {
777 		if (dump_prop_list(&sysprop_dumpops, "System", ilev + 1,
778 		    node, DDI_DEV_T_ANY, NULL)) {
779 			(void) dump_prop_list(&globprop_dumpops, NULL, ilev + 1,
780 			    node, DDI_DEV_T_ANY, NULL);
781 		} else {
782 			(void) dump_prop_list(&globprop_dumpops,
783 			    "System software", ilev + 1,
784 			    node, DDI_DEV_T_ANY, NULL);
785 		}
786 		(void) dump_prop_list(&drvprop_dumpops, "Driver", ilev + 1,
787 		    node, DDI_DEV_T_NONE, NULL);
788 
789 		printed = dump_prop_list(&hwprop_dumpops, "Hardware",
790 		    ilev + 1, node, DDI_DEV_T_ANY, &compat_printed);
791 
792 		/* Ensure that 'compatible' is printed under Hardware header */
793 		if (!compat_printed)
794 			(void) dump_compatible(printed ? NULL : "Hardware",
795 			    ilev + 1, node);
796 
797 		dump_priv_data(ilev + 1, node);
798 		dump_pathing_data(ilev + 1, node);
799 		dump_link_data(ilev + 1, node, devlink_hdl);
800 		dump_minor_data(ilev + 1, node, devlink_hdl);
801 	}
802 
803 	if (opts.o_target)
804 		return (DI_WALK_CONTINUE);
805 
806 	if (!opts.o_pseudodevs && (strcmp(di_node_name(node), "pseudo") == 0))
807 		return (DI_WALK_PRUNECHILD);
808 
809 	return (DI_WALK_CONTINUE);
810 }
811 
812 /* _error([no_perror, ] fmt [, arg ...]) */
813 static int
814 _error(const char *opt_noperror, ...)
815 {
816 	int saved_errno;
817 	va_list ap;
818 	int no_perror = 0;
819 	const char *fmt;
820 
821 	saved_errno = errno;
822 
823 	(void) fprintf(stderr, "%s: ", opts.o_progname);
824 
825 	va_start(ap, opt_noperror);
826 	if (opt_noperror == NULL) {
827 		no_perror = 1;
828 		fmt = va_arg(ap, char *);
829 	} else
830 		fmt = opt_noperror;
831 	(void) vfprintf(stderr, fmt, ap);
832 	va_end(ap);
833 
834 	if (no_perror)
835 		(void) fprintf(stderr, "\n");
836 	else {
837 		(void) fprintf(stderr, ": ");
838 		errno = saved_errno;
839 		perror("");
840 	}
841 
842 	return (-1);
843 }
844 
845 
846 /*
847  * The rest of the routines handle printing the raw prom devinfo (-p option).
848  *
849  * 128 is the size of the largest (currently) property name
850  * 16k - MAXNAMESZ - sizeof (int) is the size of the largest
851  * (currently) property value that is allowed.
852  * the sizeof (uint_t) is from struct openpromio
853  */
854 
855 #define	MAXNAMESZ	128
856 #define	MAXVALSIZE	(16384 - MAXNAMESZ - sizeof (uint_t))
857 #define	BUFSIZE		(MAXNAMESZ + MAXVALSIZE + sizeof (uint_t))
858 typedef union {
859 	char buf[BUFSIZE];
860 	struct openpromio opp;
861 } Oppbuf;
862 
863 static int prom_fd;
864 static uchar_t *prom_snapshot;
865 
866 static int
867 is_openprom(void)
868 {
869 	Oppbuf	oppbuf;
870 	struct openpromio *opp = &(oppbuf.opp);
871 	unsigned int i;
872 
873 	opp->oprom_size = MAXVALSIZE;
874 	if (ioctl(prom_fd, OPROMGETCONS, opp) < 0)
875 		exit(_error("OPROMGETCONS"));
876 
877 	i = (unsigned int)((unsigned char)opp->oprom_array[0]);
878 	return ((i & OPROMCONS_OPENPROM) == OPROMCONS_OPENPROM);
879 }
880 
881 int
882 do_prominfo(void)
883 {
884 	uint_t arg = opts.o_verbose;
885 
886 	if (promopen(O_RDONLY))  {
887 		exit(_error("openeepr device open failed"));
888 	}
889 
890 	if (is_openprom() == 0)  {
891 		(void) fprintf(stderr, "System architecture does not "
892 		    "support this option of this command.\n");
893 		return (1);
894 	}
895 
896 	/* OPROMSNAPSHOT returns size in arg */
897 	if (ioctl(prom_fd, OPROMSNAPSHOT, &arg) < 0)
898 		exit(_error("OPROMSNAPSHOT"));
899 
900 	if (arg == 0)
901 		return (1);
902 
903 	if ((prom_snapshot = malloc(arg)) == NULL)
904 		exit(_error("failed to allocate memory"));
905 
906 	/* copy out the snapshot for printing */
907 	/*LINTED*/
908 	*(uint_t *)prom_snapshot = arg;
909 	if (ioctl(prom_fd, OPROMCOPYOUT, prom_snapshot) < 0)
910 		exit(_error("OPROMCOPYOUT"));
911 
912 	promclose();
913 
914 	/* print out information */
915 	walk(prom_snapshot, arg, 0);
916 	free(prom_snapshot);
917 
918 	return (0);
919 }
920 
921 static void
922 walk(uchar_t *buf, uint_t size, int level)
923 {
924 	int error;
925 	nvlist_t *nvl, *cnvl;
926 	nvpair_t *child = NULL;
927 	uchar_t *cbuf = NULL;
928 	uint_t csize;
929 
930 	/* Expand to an nvlist */
931 	if (nvlist_unpack((char *)buf, size, &nvl, 0))
932 		exit(_error("error processing snapshot"));
933 
934 	/* print current node */
935 	dump_node(nvl, level);
936 
937 	/* print children */
938 	error = nvlist_lookup_byte_array(nvl, "@child", &cbuf, &csize);
939 	if ((error == ENOENT) || (cbuf == NULL))
940 		return;		/* no child exists */
941 
942 	if (error || nvlist_unpack((char *)cbuf, csize, &cnvl, 0))
943 		exit(_error("error processing snapshot"));
944 
945 	while (child = nvlist_next_nvpair(cnvl, child)) {
946 		char *name = nvpair_name(child);
947 		data_type_t type = nvpair_type(child);
948 		uchar_t *nodebuf;
949 		uint_t nodesize;
950 		if (strcmp("node", name) != 0) {
951 			dprintf("unexpected nvpair name %s != name\n", name);
952 			continue;
953 		}
954 		if (type != DATA_TYPE_BYTE_ARRAY) {
955 			dprintf("unexpected nvpair type %d, not byte array \n",
956 			    type);
957 			continue;
958 		}
959 
960 		(void) nvpair_value_byte_array(child,
961 		    (uchar_t **)&nodebuf, &nodesize);
962 		walk(nodebuf, nodesize, level + 1);
963 	}
964 
965 	nvlist_free(nvl);
966 }
967 
968 /*
969  * Print all properties and values
970  */
971 static void
972 dump_node(nvlist_t *nvl, int level)
973 {
974 	int id = 0;
975 	char *name = NULL;
976 	nvpair_t *nvp = NULL;
977 
978 	indent_to_level(level);
979 	(void) printf("Node");
980 	if (!opts.o_verbose) {
981 		if (nvlist_lookup_string(nvl, "name", &name))
982 			(void) printf("data not available");
983 		else
984 			(void) printf(" '%s'", name);
985 		(void) putchar('\n');
986 		return;
987 	}
988 	(void) nvlist_lookup_int32(nvl, "@nodeid", &id);
989 	(void) printf(" %#08x\n", id);
990 
991 	while (nvp = nvlist_next_nvpair(nvl, nvp)) {
992 		name = nvpair_name(nvp);
993 		if (name[0] == '@')
994 			continue;
995 
996 		print_one(nvp, level + 1);
997 	}
998 	(void) putchar('\n');
999 }
1000 
1001 static const char *
1002 path_state_name(di_path_state_t st)
1003 {
1004 	switch (st) {
1005 		case DI_PATH_STATE_ONLINE:
1006 			return ("online");
1007 		case DI_PATH_STATE_STANDBY:
1008 			return ("standby");
1009 		case DI_PATH_STATE_OFFLINE:
1010 			return ("offline");
1011 		case DI_PATH_STATE_FAULT:
1012 			return ("faulted");
1013 	}
1014 	return ("unknown");
1015 }
1016 
1017 /*
1018  * Print all phci's each client is connected to.
1019  */
1020 static void
1021 dump_pathing_data(int ilev, di_node_t node)
1022 {
1023 	di_path_t	pi = DI_PATH_NIL;
1024 	di_node_t	phci_node;
1025 	char		*phci_path;
1026 	int		path_instance;
1027 	int		firsttime = 1;
1028 
1029 	if (node == DI_PATH_NIL)
1030 		return;
1031 
1032 	while ((pi = di_path_client_next_path(node, pi)) != DI_PATH_NIL) {
1033 		if (firsttime) {
1034 			indent_to_level(ilev);
1035 			firsttime = 0;
1036 			ilev++;
1037 			(void) printf("Paths from multipath bus adapters:\n");
1038 		}
1039 
1040 		/*
1041 		 * Print the path instance and full "pathinfo" path, which is
1042 		 * the same as the /devices devifo path had the device been
1043 		 * enumerated under pHCI.
1044 		 */
1045 		phci_node = di_path_phci_node(pi);
1046 		phci_path = di_devfs_path(phci_node);
1047 		path_instance = di_path_instance(pi);
1048 		if (phci_path) {
1049 			if (path_instance > 0) {
1050 				indent_to_level(ilev);
1051 				(void) printf("Path %d: %s/%s@%s\n",
1052 				    path_instance, phci_path,
1053 				    di_node_name(node),
1054 				    di_path_bus_addr(pi));
1055 			}
1056 			di_devfs_path_free(phci_path);
1057 		}
1058 
1059 		/* print phci driver, instance, and path state information */
1060 		indent_to_level(ilev);
1061 		(void) printf("%s#%d (%s)\n", di_driver_name(phci_node),
1062 		    di_instance(phci_node), path_state_name(di_path_state(pi)));
1063 		(void) dump_prop_list(&pathprop_dumpops, NULL, ilev + 1,
1064 		    pi, DDI_DEV_T_ANY, NULL);
1065 	}
1066 }
1067 
1068 static int
1069 dump_minor_data_links(di_devlink_t devlink, void *arg)
1070 {
1071 	int ilev = (intptr_t)arg;
1072 	indent_to_level(ilev);
1073 	(void) printf("dev_link=%s\n", di_devlink_path(devlink));
1074 	return (DI_WALK_CONTINUE);
1075 }
1076 
1077 static void
1078 dump_minor_data_paths(int ilev, di_minor_t minor,
1079     di_devlink_handle_t devlink_hdl)
1080 {
1081 	char	*path, *type;
1082 	int	spec_type;
1083 
1084 	/* get the path to the device and the minor node name */
1085 	if ((path = di_devfs_minor_path(minor)) == NULL)
1086 		exit(_error("failed to allocate memory"));
1087 
1088 	/* display the path to this minor node */
1089 	indent_to_level(ilev);
1090 	(void) printf("dev_path=%s\n", path);
1091 
1092 	if (devlink_hdl != NULL) {
1093 
1094 		/* get the device minor node information */
1095 		spec_type = di_minor_spectype(minor);
1096 		switch (di_minor_type(minor)) {
1097 			case DDM_MINOR:
1098 				type = "minor";
1099 				break;
1100 			case DDM_ALIAS:
1101 				type = "alias";
1102 				break;
1103 			case DDM_DEFAULT:
1104 				type = "default";
1105 				break;
1106 			case DDM_INTERNAL_PATH:
1107 				type = "internal";
1108 				break;
1109 			default:
1110 				type = "unknown";
1111 				break;
1112 		}
1113 
1114 		/* display the device minor node information */
1115 		indent_to_level(ilev + 1);
1116 		(void) printf("spectype=%s type=%s\n",
1117 		    (spec_type == S_IFBLK) ? "blk" : "chr", type);
1118 
1119 		/* display all the devlinks for this device minor node */
1120 		(void) di_devlink_walk(devlink_hdl, NULL, path,
1121 		    0, (void *)(intptr_t)(ilev + 1), dump_minor_data_links);
1122 	}
1123 
1124 	di_devfs_path_free(path);
1125 }
1126 
1127 static void
1128 create_minor_list(di_node_t node)
1129 {
1130 	di_minor_t	minor, minor_head, minor_tail, minor_prev, minor_walk;
1131 	int		major;
1132 
1133 	/* if there are no minor nodes, bail */
1134 	if (di_minor_next(node, DI_MINOR_NIL) == DI_MINOR_NIL)
1135 		return;
1136 
1137 	/*
1138 	 * here we want to create lists of minor nodes with the same
1139 	 * dev_t.  to do this we first sort all the minor nodes by devt.
1140 	 *
1141 	 * the algorithm used here is a bubble sort, so performance sucks.
1142 	 * but it's probably ok here because most device instances don't
1143 	 * have that many minor nodes.  also we're doing this as we're
1144 	 * displaying each node so it doesn't look like we're pausing
1145 	 * output for a long time.
1146 	 */
1147 	major = di_driver_major(node);
1148 	minor_head = minor_tail = minor = DI_MINOR_NIL;
1149 	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
1150 		dev_t	dev = di_minor_devt(minor);
1151 
1152 		/* skip /pseudo/clone@0 minor nodes */
1153 		if (major != major(dev))
1154 			continue;
1155 
1156 		minor_ptr_set(minor, DI_MINOR_NIL);
1157 		if (minor_head == DI_MINOR_NIL) {
1158 			/* this is the first minor node we're looking at */
1159 			minor_head = minor_tail = minor;
1160 			continue;
1161 		}
1162 
1163 		/*
1164 		 * if the new dev is less than the old dev, update minor_head
1165 		 * so it points to the beginning of the list.  ie it points
1166 		 * to the node with the lowest dev value
1167 		 */
1168 		if (dev <= di_minor_devt(minor_head)) {
1169 			minor_ptr_set(minor, minor_head);
1170 			minor_head = minor;
1171 			continue;
1172 		}
1173 
1174 		minor_prev = minor_head;
1175 		minor_walk = minor_ptr(minor_head);
1176 		while ((minor_walk != DI_MINOR_NIL) &&
1177 		    (dev > di_minor_devt(minor_walk))) {
1178 			minor_prev = minor_walk;
1179 			minor_walk = minor_ptr(minor_walk);
1180 		}
1181 		minor_ptr_set(minor, minor_walk);
1182 		minor_ptr_set(minor_prev, minor);
1183 		if (minor_walk == NULL)
1184 			minor_tail = minor;
1185 	}
1186 
1187 	/* check if there were any non /pseudo/clone@0 nodes.  if not, bail */
1188 	if (minor_head == DI_MINOR_NIL)
1189 		return;
1190 
1191 	/*
1192 	 * now that we have a list of minor nodes sorted by devt
1193 	 * we walk through the list and break apart the entire list
1194 	 * to create circular lists of minor nodes with matching devts.
1195 	 */
1196 	minor_prev = minor_head;
1197 	minor_walk = minor_ptr(minor_head);
1198 	while (minor_walk != DI_MINOR_NIL) {
1199 		if (di_minor_devt(minor_prev) != di_minor_devt(minor_walk)) {
1200 			minor_ptr_set(minor_prev, minor_head);
1201 			minor_head = minor_walk;
1202 		}
1203 		minor_prev = minor_walk;
1204 		minor_walk = minor_ptr(minor_walk);
1205 	}
1206 	minor_ptr_set(minor_tail, minor_head);
1207 }
1208 
1209 static void
1210 link_lnode_disp(di_link_t link, uint_t endpoint, int ilev,
1211     di_devlink_handle_t devlink_hdl)
1212 {
1213 	di_lnode_t	lnode;
1214 	char		*name, *path;
1215 	int		displayed_path, spec_type;
1216 	di_node_t	node = DI_NODE_NIL;
1217 	dev_t		devt = DDI_DEV_T_NONE;
1218 
1219 	lnode = di_link_to_lnode(link, endpoint);
1220 
1221 	indent_to_level(ilev);
1222 	name = di_lnode_name(lnode);
1223 	spec_type = di_link_spectype(link);
1224 
1225 	(void) printf("mod=%s", name);
1226 
1227 	/*
1228 	 * if we're displaying the source of a link, we should display
1229 	 * the target access mode.  (either block or char.)
1230 	 */
1231 	if (endpoint == DI_LINK_SRC)
1232 		(void) printf(" accesstype=%s",
1233 		    (spec_type == S_IFBLK) ? "blk" : "chr");
1234 
1235 	/*
1236 	 * check if the lnode is bound to a specific device
1237 	 * minor node (i.e.  if it's bound to a dev_t) and
1238 	 * if so display the dev_t value and any possible
1239 	 * minor node pathing information.
1240 	 */
1241 	displayed_path = 0;
1242 	if (di_lnode_devt(lnode, &devt) == 0) {
1243 		di_minor_t	minor = DI_MINOR_NIL;
1244 
1245 		(void) printf(" dev=(%u,%u)\n",
1246 		    (uint_t)major(devt), (uint_t)minor(devt));
1247 
1248 		/* display paths to the src devt minor node */
1249 		while (minor = di_minor_next(node, minor)) {
1250 			if (devt != di_minor_devt(minor))
1251 				continue;
1252 
1253 			if ((endpoint == DI_LINK_TGT) &&
1254 			    (spec_type != di_minor_spectype(minor)))
1255 				continue;
1256 
1257 			dump_minor_data_paths(ilev + 1, minor, devlink_hdl);
1258 			displayed_path = 1;
1259 		}
1260 	} else {
1261 		(void) printf("\n");
1262 	}
1263 
1264 	if (displayed_path)
1265 		return;
1266 
1267 	/*
1268 	 * This device lnode is not did not have any minor node
1269 	 * pathing information so display the path to device node.
1270 	 */
1271 	node = di_lnode_devinfo(lnode);
1272 	if ((path = di_devfs_path(node)) == NULL)
1273 		exit(_error("failed to allocate memory"));
1274 
1275 	indent_to_level(ilev + 1);
1276 	(void) printf("dev_path=%s\n", path);
1277 	di_devfs_path_free(path);
1278 }
1279 
1280 static void
1281 dump_minor_link_data(int ilev, di_node_t node, dev_t devt,
1282     di_devlink_handle_t devlink_hdl)
1283 {
1284 	int		first = 1;
1285 	di_link_t	link;
1286 
1287 	link = DI_LINK_NIL;
1288 	while (link = di_link_next_by_node(node, link, DI_LINK_TGT)) {
1289 		di_lnode_t	tgt_lnode;
1290 		dev_t		tgt_devt = DDI_DEV_T_NONE;
1291 
1292 		tgt_lnode = di_link_to_lnode(link, DI_LINK_TGT);
1293 
1294 		if (di_lnode_devt(tgt_lnode, &tgt_devt) != 0)
1295 			continue;
1296 
1297 		if (devt != tgt_devt)
1298 			continue;
1299 
1300 		if (first) {
1301 			first = 0;
1302 			indent_to_level(ilev);
1303 			(void) printf("Device Minor Layered Under:\n");
1304 		}
1305 
1306 		/* displayed this lnode */
1307 		lnode_displayed_set(tgt_lnode);
1308 		link_lnode_disp(link, DI_LINK_SRC, ilev + 1, devlink_hdl);
1309 	}
1310 
1311 	link = DI_LINK_NIL;
1312 	while (link = di_link_next_by_node(node, link, DI_LINK_SRC)) {
1313 		di_lnode_t	src_lnode;
1314 		dev_t		src_devt = DDI_DEV_T_NONE;
1315 
1316 		src_lnode = di_link_to_lnode(link, DI_LINK_SRC);
1317 
1318 		if (di_lnode_devt(src_lnode, &src_devt) != 0)
1319 			continue;
1320 
1321 		if (devt != src_devt)
1322 			continue;
1323 
1324 		if (first) {
1325 			first = 0;
1326 			indent_to_level(ilev);
1327 			(void) printf("Device Minor Layered Over:\n");
1328 		}
1329 
1330 		/* displayed this lnode */
1331 		lnode_displayed_set(src_lnode);
1332 		link_lnode_disp(link, DI_LINK_TGT, ilev + 1, devlink_hdl);
1333 	}
1334 }
1335 
1336 static void
1337 dump_minor_data(int ilev, di_node_t node, di_devlink_handle_t devlink_hdl)
1338 {
1339 	di_minor_t	minor, minor_next;
1340 	di_lnode_t	lnode;
1341 	di_link_t	link;
1342 	int		major, firstminor = 1;
1343 
1344 	/*
1345 	 * first go through and mark all lnodes and minor nodes for this
1346 	 * node as undisplayed
1347 	 */
1348 	lnode = DI_LNODE_NIL;
1349 	while (lnode = di_lnode_next(node, lnode))
1350 		lnode_displayed_clear(lnode);
1351 	minor = DI_MINOR_NIL;
1352 	while (minor = di_minor_next(node, minor)) {
1353 		minor_displayed_clear(minor);
1354 	}
1355 
1356 	/*
1357 	 * when we display the minor nodes we want to coalesce nodes
1358 	 * that have the same dev_t.  we do this by creating circular
1359 	 * lists of minor nodes with the same devt.
1360 	 */
1361 	create_minor_list(node);
1362 
1363 	/* now we display the driver defined minor nodes */
1364 	major = di_driver_major(node);
1365 	minor = DI_MINOR_NIL;
1366 	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
1367 		dev_t	devt;
1368 
1369 		/*
1370 		 * skip /pseudo/clone@0 minor nodes.
1371 		 * these are only created for DLPIv2 network devices.
1372 		 * since these minor nodes are associated with a driver
1373 		 * and are only bound to a device instance after they
1374 		 * are opened and attached we don't print them out
1375 		 * here.
1376 		 */
1377 		devt = di_minor_devt(minor);
1378 		if (major != major(devt))
1379 			continue;
1380 
1381 		/* skip nodes that may have already been displayed */
1382 		if (minor_displayed(minor))
1383 			continue;
1384 
1385 		if (firstminor) {
1386 			firstminor = 0;
1387 			indent_to_level(ilev++);
1388 			(void) printf("Device Minor Nodes:\n");
1389 		}
1390 
1391 		/* display the device minor node information */
1392 		indent_to_level(ilev);
1393 		(void) printf("dev=(%u,%u)\n",
1394 		    (uint_t)major(devt), (uint_t)minor(devt));
1395 
1396 		minor_next = minor;
1397 		do {
1398 			/* display device minor node path info */
1399 			minor_displayed_set(minor_next);
1400 			dump_minor_data_paths(ilev + 1, minor_next,
1401 			    devlink_hdl);
1402 
1403 			/* get a pointer to the next node */
1404 			minor_next = minor_ptr(minor_next);
1405 		} while (minor_next != minor);
1406 
1407 		/* display who has this device minor node open */
1408 		dump_minor_link_data(ilev + 1, node, devt, devlink_hdl);
1409 
1410 		/* display properties associated with this devt */
1411 		(void) dump_prop_list(&drvprop_dumpops, "Minor",
1412 		    ilev + 1, node, devt, NULL);
1413 	}
1414 
1415 	/*
1416 	 * now go through all the target lnodes for this node and
1417 	 * if they haven't yet been displayed, display them now.
1418 	 *
1419 	 * this happens in the case of clone opens when an "official"
1420 	 * minor node does not exist for the opened devt
1421 	 */
1422 	link = DI_LINK_NIL;
1423 	while (link = di_link_next_by_node(node, link, DI_LINK_TGT)) {
1424 		dev_t		devt;
1425 
1426 		lnode = di_link_to_lnode(link, DI_LINK_TGT);
1427 
1428 		/* if we've already displayed this target lnode, skip it */
1429 		if (lnode_displayed(lnode))
1430 			continue;
1431 
1432 		if (firstminor) {
1433 			firstminor = 0;
1434 			indent_to_level(ilev++);
1435 			(void) printf("Device Minor Nodes:\n");
1436 		}
1437 
1438 		/* display the device minor node information */
1439 		indent_to_level(ilev);
1440 		(void) di_lnode_devt(lnode, &devt);
1441 		(void) printf("dev=(%u,%u)\n",
1442 		    (uint_t)major(devt), (uint_t)minor(devt));
1443 
1444 		indent_to_level(ilev + 1);
1445 		(void) printf("dev_path=<clone>\n");
1446 
1447 		/* display who has this cloned device minor node open */
1448 		dump_minor_link_data(ilev + 1, node, devt, devlink_hdl);
1449 
1450 		/* mark node as displayed */
1451 		lnode_displayed_set(lnode);
1452 	}
1453 }
1454 
1455 static void
1456 dump_link_data(int ilev, di_node_t node, di_devlink_handle_t devlink_hdl)
1457 {
1458 	int		first = 1;
1459 	di_link_t	link;
1460 
1461 	link = DI_LINK_NIL;
1462 	while (link = di_link_next_by_node(node, link, DI_LINK_SRC)) {
1463 		di_lnode_t	src_lnode;
1464 		dev_t		src_devt = DDI_DEV_T_NONE;
1465 
1466 		src_lnode = di_link_to_lnode(link, DI_LINK_SRC);
1467 
1468 		/*
1469 		 * here we only want to print out layering information
1470 		 * if we are the source and our source lnode is not
1471 		 * associated with any particular dev_t.  (which means
1472 		 * we won't display this link while dumping minor node
1473 		 * info.)
1474 		 */
1475 		if (di_lnode_devt(src_lnode, &src_devt) != -1)
1476 			continue;
1477 
1478 		if (first) {
1479 			first = 0;
1480 			indent_to_level(ilev);
1481 			(void) printf("Device Layered Over:\n");
1482 		}
1483 
1484 		/* displayed this lnode */
1485 		link_lnode_disp(link, DI_LINK_TGT, ilev + 1, devlink_hdl);
1486 	}
1487 }
1488 
1489 /*
1490  * certain 'known' property names may contain 'composite' strings.
1491  * Handle them here, and print them as 'string1' + 'string2' ...
1492  */
1493 static int
1494 print_composite_string(const char *var, char *value, int size)
1495 {
1496 	char *p, *q;
1497 	char *firstp;
1498 
1499 	if ((strcmp(var, "version") != 0) &&
1500 	    (strcmp(var, "compatible") != 0))
1501 		return (0);	/* Not a known composite string */
1502 
1503 	/*
1504 	 * Verify that each string in the composite string is non-NULL,
1505 	 * is within the bounds of the property length, and contains
1506 	 * printable characters or white space. Otherwise let the
1507 	 * caller deal with it.
1508 	 */
1509 	for (firstp = p = value; p < (value + size); p += strlen(p) + 1) {
1510 		if (strlen(p) == 0)
1511 			return (0);		/* NULL string */
1512 		for (q = p; *q; q++) {
1513 			if (!(isascii(*q) && (isprint(*q) || isspace(*q))))
1514 				return (0);	/* Not printable or space */
1515 		}
1516 		if (q > (firstp + size))
1517 			return (0);		/* Out of bounds */
1518 	}
1519 
1520 	for (firstp = p = value; p < (value + size); p += strlen(p) + 1) {
1521 		if (p == firstp)
1522 			(void) printf("'%s'", p);
1523 		else
1524 			(void) printf(" + '%s'", p);
1525 	}
1526 	(void) putchar('\n');
1527 	return (1);
1528 }
1529 
1530 /*
1531  * Print one property and its value. Handle the verbose case.
1532  */
1533 static void
1534 print_one(nvpair_t *nvp, int level)
1535 {
1536 	int i;
1537 	int endswap = 0;
1538 	uint_t valsize;
1539 	char *value;
1540 	char *var = nvpair_name(nvp);
1541 
1542 	indent_to_level(level);
1543 	(void) printf("%s: ", var);
1544 
1545 	switch (nvpair_type(nvp)) {
1546 	case DATA_TYPE_BOOLEAN:
1547 		(void) printf(" \n");
1548 		return;
1549 	case DATA_TYPE_BYTE_ARRAY:
1550 		if (nvpair_value_byte_array(nvp, (uchar_t **)&value,
1551 		    &valsize)) {
1552 			(void) printf("data not available.\n");
1553 			return;
1554 		}
1555 		valsize--;	/* take out null added by driver */
1556 
1557 		/*
1558 		 * Do not print valsize > MAXVALSIZE, to be compatible
1559 		 * with old behavior. E.g. intel's eisa-nvram property
1560 		 * has a size of 65 K.
1561 		 */
1562 		if (valsize > MAXVALSIZE) {
1563 			(void) printf(" \n");
1564 			return;
1565 		}
1566 		break;
1567 	default:
1568 		(void) printf("data type unexpected.\n");
1569 		return;
1570 	}
1571 
1572 	/*
1573 	 * Handle printing verbosely
1574 	 */
1575 	if (print_composite_string(var, value, valsize)) {
1576 		return;
1577 	}
1578 
1579 	if (!unprintable(value, valsize)) {
1580 		(void) printf(" '%s'\n", value);
1581 		return;
1582 	}
1583 
1584 	(void) printf(" ");
1585 #ifdef	__x86
1586 	/*
1587 	 * Due to backwards compatibility constraints x86 int
1588 	 * properties are not in big-endian (ieee 1275) byte order.
1589 	 * If we have a property that is a multiple of 4 bytes,
1590 	 * let's assume it is an array of ints and print the bytes
1591 	 * in little endian order to make things look nicer for
1592 	 * the user.
1593 	 */
1594 	endswap = (valsize % 4) == 0;
1595 #endif	/* __x86 */
1596 	for (i = 0; i < valsize; i++) {
1597 		int out;
1598 		if (i && (i % 4 == 0))
1599 			(void) putchar('.');
1600 		if (endswap)
1601 			out = value[i + (3 - 2 * (i % 4))] & 0xff;
1602 		else
1603 			out = value[i] & 0xff;
1604 
1605 		(void) printf("%02x", out);
1606 	}
1607 	(void) putchar('\n');
1608 }
1609 
1610 static int
1611 unprintable(char *value, int size)
1612 {
1613 	int i;
1614 
1615 	/*
1616 	 * Is this just a zero?
1617 	 */
1618 	if (size == 0 || value[0] == '\0')
1619 		return (1);
1620 	/*
1621 	 * If any character is unprintable, or if a null appears
1622 	 * anywhere except at the end of a string, the whole
1623 	 * property is "unprintable".
1624 	 */
1625 	for (i = 0; i < size; ++i) {
1626 		if (value[i] == '\0')
1627 			return (i != (size - 1));
1628 		if (!isascii(value[i]) || iscntrl(value[i]))
1629 			return (1);
1630 	}
1631 	return (0);
1632 }
1633 
1634 static int
1635 promopen(int oflag)
1636 {
1637 	for (;;)  {
1638 		if ((prom_fd = open(opts.o_promdev, oflag)) < 0)  {
1639 			if (errno == EAGAIN)   {
1640 				(void) sleep(5);
1641 				continue;
1642 			}
1643 			if (errno == ENXIO)
1644 				return (-1);
1645 			if (getzoneid() == GLOBAL_ZONEID) {
1646 				_exit(_error("cannot open %s",
1647 				    opts.o_promdev));
1648 			}
1649 			/* not an error if this isn't the global zone */
1650 			(void) _error(NULL, "openprom facility not available");
1651 			exit(0);
1652 		} else
1653 			return (0);
1654 	}
1655 }
1656 
1657 static void
1658 promclose(void)
1659 {
1660 	if (close(prom_fd) < 0)
1661 		exit(_error("close error on %s", opts.o_promdev));
1662 }
1663 
1664 /*
1665  * Get and print the name of the frame buffer device.
1666  */
1667 int
1668 do_fbname(void)
1669 {
1670 	int	retval;
1671 	char fbuf_path[MAXPATHLEN];
1672 
1673 	retval =  modctl(MODGETFBNAME, (caddr_t)fbuf_path);
1674 
1675 	if (retval == 0) {
1676 		(void) printf("%s\n", fbuf_path);
1677 	} else {
1678 		if (retval == EFAULT) {
1679 			(void) fprintf(stderr,
1680 			"Error copying fb path to userland\n");
1681 		} else {
1682 			(void) fprintf(stderr,
1683 			"Console output device is not a frame buffer\n");
1684 		}
1685 		return (1);
1686 	}
1687 	return (0);
1688 }
1689 
1690 /*
1691  * Get and print the PROM version.
1692  */
1693 int
1694 do_promversion(void)
1695 {
1696 	Oppbuf	oppbuf;
1697 	struct openpromio *opp = &(oppbuf.opp);
1698 
1699 	if (promopen(O_RDONLY))  {
1700 		(void) fprintf(stderr, "Cannot open openprom device\n");
1701 		return (1);
1702 	}
1703 
1704 	opp->oprom_size = MAXVALSIZE;
1705 	if (ioctl(prom_fd, OPROMGETVERSION, opp) < 0)
1706 		exit(_error("OPROMGETVERSION"));
1707 
1708 	(void) printf("%s\n", opp->oprom_array);
1709 	promclose();
1710 	return (0);
1711 }
1712 
1713 int
1714 do_prom_version64(void)
1715 {
1716 #ifdef	sparc
1717 	Oppbuf	oppbuf;
1718 	struct openpromio *opp = &(oppbuf.opp);
1719 	/*LINTED*/
1720 	struct openprom_opr64 *opr = (struct openprom_opr64 *)opp->oprom_array;
1721 
1722 	static const char msg[] =
1723 	    "NOTICE: The firmware on this system does not support the "
1724 	    "64-bit OS.\n"
1725 	    "\tPlease upgrade to at least the following version:\n"
1726 	    "\t\t%s\n\n";
1727 
1728 	if (promopen(O_RDONLY))  {
1729 		(void) fprintf(stderr, "Cannot open openprom device\n");
1730 		return (-1);
1731 	}
1732 
1733 	opp->oprom_size = MAXVALSIZE;
1734 	if (ioctl(prom_fd, OPROMREADY64, opp) < 0)
1735 		exit(_error("OPROMREADY64"));
1736 
1737 	if (opr->return_code == 0)
1738 		return (0);
1739 
1740 	(void) printf(msg, opr->message);
1741 
1742 	promclose();
1743 	return (opr->return_code);
1744 #else
1745 	return (0);
1746 #endif
1747 }
1748 
1749 int
1750 do_productinfo(void)
1751 {
1752 	di_node_t root, next_node;
1753 	di_prom_handle_t promh;
1754 	static const char *root_prop[] = { "name", "model", "banner-name",
1755 					"compatible" };
1756 	static const char *root_propv[] = { "name", "model", "banner-name",
1757 					"compatible", "idprom" };
1758 	static const char *oprom_prop[] = { "model", "version" };
1759 
1760 
1761 	root = di_init("/", DINFOCPYALL);
1762 
1763 	if (root == DI_NODE_NIL) {
1764 		(void) fprintf(stderr, "di_init() failed\n");
1765 		return (1);
1766 	}
1767 
1768 	promh = di_prom_init();
1769 
1770 	if (promh == DI_PROM_HANDLE_NIL) {
1771 		(void) fprintf(stderr, "di_prom_init() failed\n");
1772 		return (1);
1773 	}
1774 
1775 	if (opts.o_verbose) {
1776 		dump_prodinfo(promh, root, root_propv, "root",
1777 		    NUM_ELEMENTS(root_propv));
1778 
1779 		/* Get model and version properties under node "openprom" */
1780 		next_node = find_node_by_name(promh, root, "openprom");
1781 		if (next_node != DI_NODE_NIL)
1782 			dump_prodinfo(promh, next_node, oprom_prop,
1783 			    "openprom", NUM_ELEMENTS(oprom_prop));
1784 
1785 	} else
1786 		dump_prodinfo(promh, root, root_prop, "root",
1787 		    NUM_ELEMENTS(root_prop));
1788 	di_prom_fini(promh);
1789 	di_fini(root);
1790 	return (0);
1791 }
1792 
1793 di_node_t
1794 find_node_by_name(di_prom_handle_t promh, di_node_t parent,
1795 		char *node_name)
1796 {
1797 	di_node_t next_node;
1798 	uchar_t *prop_valp;
1799 
1800 	for (next_node = di_child_node(parent); next_node != DI_NODE_NIL;
1801 	    next_node = di_sibling_node(next_node)) {
1802 		int len;
1803 
1804 		len = get_propval_by_name(promh, next_node, "name", &prop_valp);
1805 		if ((len != -1) && (strcmp((char *)prop_valp, node_name) == 0))
1806 			return (next_node);
1807 	}
1808 	return (DI_NODE_NIL);
1809 }
1810 
1811 
1812 int
1813 get_propval_by_name(di_prom_handle_t promh, di_node_t node, const char *name,
1814 			uchar_t **valp)
1815 {
1816 	int len;
1817 	uchar_t *bufp;
1818 
1819 	len = di_prom_prop_lookup_bytes(promh, node, name,
1820 	    (uchar_t **)&bufp);
1821 	if (len != -1) {
1822 		*valp = (uchar_t *)malloc(len);
1823 		(void) memcpy(*valp, bufp, len);
1824 	}
1825 	return (len);
1826 }
1827 
1828 
1829 static void
1830 dump_prodinfo(di_prom_handle_t promh, di_node_t node, const char **propstr,
1831 		char *node_name, int num)
1832 {
1833 	int out, len, index1, index, endswap = 0;
1834 	uchar_t *prop_valp;
1835 
1836 	for (index1 = 0; index1 < num; index1++) {
1837 		len = get_propval_by_name(promh, node, propstr[index1],
1838 		    &prop_valp);
1839 		if (len != -1) {
1840 			if (strcmp(node_name, "root"))
1841 				(void) printf("%s ", node_name);
1842 
1843 			(void) printf("%s: ", propstr[index1]);
1844 
1845 			if (print_composite_string((const char *)
1846 			    propstr[index1], (char *)prop_valp, len)) {
1847 				free(prop_valp);
1848 				continue;
1849 			}
1850 
1851 			if (!unprintable((char *)prop_valp, len)) {
1852 				(void) printf(" %s\n", (char *)prop_valp);
1853 				free(prop_valp);
1854 				continue;
1855 			}
1856 
1857 			(void) printf(" ");
1858 #ifdef  __x86
1859 			endswap = (len % 4) == 0;
1860 #endif  /* __x86 */
1861 			for (index = 0; index < len; index++) {
1862 				if (index && (index % 4 == 0))
1863 					(void) putchar('.');
1864 				if (endswap)
1865 					out = prop_valp[index +
1866 					    (3 - 2 * (index % 4))] & 0xff;
1867 				else
1868 					out = prop_valp[index] & 0xff;
1869 				(void) printf("%02x", out);
1870 			}
1871 			(void) putchar('\n');
1872 			free(prop_valp);
1873 		}
1874 	}
1875 }
1876 
1877 static int
1878 dump_compatible(char *name, int ilev, di_node_t node)
1879 {
1880 	int	ncompat;
1881 	char	*compat_array;
1882 	char	*p, *q;
1883 	int	i;
1884 
1885 	if (node == DI_PATH_NIL)
1886 		return (0);
1887 
1888 	ncompat = di_compatible_names(node, &compat_array);
1889 	if (ncompat <= 0)
1890 		return (0);	/* no 'compatible' available */
1891 
1892 	/* verify integrety of compat_array */
1893 	for (i = 0, p = compat_array; i < ncompat; i++, p += strlen(p) + 1) {
1894 		if (strlen(p) == 0)
1895 			return (0);		/* NULL string */
1896 		for (q = p; *q; q++) {
1897 			if (!(isascii(*q) && (isprint(*q) || isspace(*q))))
1898 				return (0);	/* Not printable or space */
1899 		}
1900 	}
1901 
1902 	/* If name is non-NULL, produce header */
1903 	if (name) {
1904 		indent_to_level(ilev);
1905 		(void) printf("%s properties:\n", name);
1906 	}
1907 	ilev++;
1908 
1909 	/* process like a string array property */
1910 	indent_to_level(ilev);
1911 	(void) printf("name='compatible' type=string items=%d\n", ncompat);
1912 	indent_to_level(ilev);
1913 	(void) printf("    value=");
1914 	for (i = 0, p = compat_array; i < (ncompat - 1);
1915 	    i++, p += strlen(p) + 1)
1916 		(void) printf("'%s' + ", p);
1917 	(void) printf("'%s'", p);
1918 	(void) putchar('\n');
1919 	return (1);
1920 }
1921