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