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