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 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 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 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 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 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 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 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 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 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 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 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 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 * 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 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 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 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 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 * 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 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 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 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 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 * 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 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 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 * 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 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 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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