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
prtconf_devinfo(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
i_find_target_node(di_node_t node,void * arg)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
find_target_node(di_node_t root_node)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
node_display(di_node_t node)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
node_display_set(di_node_t node)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
lnode_displayed(di_lnode_t lnode)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
lnode_displayed_set(di_lnode_t lnode)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
lnode_displayed_clear(di_lnode_t lnode)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
minor_displayed(di_minor_t minor)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
minor_displayed_set(di_minor_t minor)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
minor_displayed_clear(di_minor_t minor)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 *
minor_ptr(di_minor_t minor)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
minor_ptr_set(di_minor_t minor,void * ptr)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
prop_type_guess(const dumpops_t * propops,void * prop,void ** prop_data,int * prop_type)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
dump_prop_list(const dumpops_t * dumpops,const char * name,int ilev,void * node,dev_t dev,int * compat_printed)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
walk_driver(di_node_t root,di_arg_t * di_arg)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
dump_devs(di_node_t node,void * arg)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
_error(const char * opt_noperror,...)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
is_openprom(void)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
do_prominfo(void)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
walk(uchar_t * buf,uint_t size,int level)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
dump_node(nvlist_t * nvl,int level)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 *
path_state_name(di_path_state_t st)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
dump_pathing_data(int ilev,di_node_t node)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
dump_minor_data_links(di_devlink_t devlink,void * arg)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
dump_minor_data_paths(int ilev,di_minor_t minor,di_devlink_handle_t devlink_hdl)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
create_minor_list(di_node_t node)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
link_lnode_disp(di_link_t link,uint_t endpoint,int ilev,di_devlink_handle_t devlink_hdl)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
dump_minor_link_data(int ilev,di_node_t node,dev_t devt,di_devlink_handle_t devlink_hdl)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
dump_minor_data(int ilev,di_node_t node,di_devlink_handle_t devlink_hdl)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
dump_link_data(int ilev,di_node_t node,di_devlink_handle_t devlink_hdl)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
print_composite_string(const char * var,char * value,int size)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
print_one(nvpair_t * nvp,int level)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
unprintable(char * value,int size)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
promopen(int oflag)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
promclose(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
do_fbname(void)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
do_promversion(void)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
do_prom_version64(void)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
do_productinfo(void)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
find_node_by_name(di_prom_handle_t promh,di_node_t parent,char * node_name)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
get_propval_by_name(di_prom_handle_t promh,di_node_t node,const char * name,uchar_t ** valp)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
dump_prodinfo(di_prom_handle_t promh,di_node_t node,const char ** propstr,char * node_name,int num)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
dump_compatible(char * name,int ilev,di_node_t node)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