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