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