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