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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <string.h> 30 #include <fcntl.h> 31 #include <stdarg.h> 32 #include <errno.h> 33 #include <unistd.h> 34 #include <sys/utsname.h> 35 #include <sys/openpromio.h> 36 #include <libintl.h> 37 #include "pdevinfo.h" 38 #include "display.h" 39 #include "pdevinfo_sun4u.h" 40 41 /* 42 * For machines that support the openprom, fetch and print the list 43 * of devices that the kernel has fetched from the prom or conjured up. 44 * 45 */ 46 47 48 static int prom_fd; 49 extern char *progname; 50 extern char *promdev; 51 extern void getppdata(); 52 extern void printppdata(); 53 54 /* 55 * Define DPRINT for run-time debugging printf's... 56 * #define DPRINT 1 57 */ 58 59 #ifdef DPRINT 60 static char vdebug_flag = 1; 61 #define dprintf if (vdebug_flag) printf 62 static void dprint_dev_info(caddr_t, dev_info_t *); 63 #endif /* DPRINT */ 64 65 #if !defined(TEXT_DOMAIN) 66 #define TEXT_DOMAIN "SYS_TEST" 67 #endif 68 69 /*VARARGS1*/ 70 int 71 _error(char *fmt, ...) 72 { 73 int saved_errno; 74 va_list ap; 75 extern int errno; 76 saved_errno = errno; 77 78 if (progname) 79 (void) fprintf(stderr, "%s: ", progname); 80 81 va_start(ap, fmt); 82 83 (void) vfprintf(stderr, fmt, ap); 84 85 va_end(ap); 86 87 (void) fprintf(stderr, ": "); 88 errno = saved_errno; 89 perror(""); 90 91 return (2); 92 } 93 94 int 95 is_openprom(void) 96 { 97 Oppbuf oppbuf; 98 register struct openpromio *opp = &(oppbuf.opp); 99 register unsigned int i; 100 101 opp->oprom_size = MAXVALSIZE; 102 if (ioctl(prom_fd, OPROMGETCONS, opp) < 0) 103 exit(_error("OPROMGETCONS")); 104 105 i = (unsigned int)((unsigned char)opp->oprom_array[0]); 106 return ((i & OPROMCONS_OPENPROM) == OPROMCONS_OPENPROM); 107 } 108 109 /* 110 * Read all properties and values from nodes. 111 * Copy the properties read into the prom_node passsed in. 112 */ 113 void 114 dump_node(Prom_node *node) 115 { 116 Oppbuf oppbuf; 117 register struct openpromio *opp = &oppbuf.opp; 118 Prop *prop = NULL; /* tail of properties list */ 119 StaticProp *temp; 120 121 /* clear out pointers in pnode */ 122 node->props = NULL; 123 124 /* get first prop by asking for null string */ 125 (void) memset((void *) oppbuf.buf, 0, BUFSIZE); 126 127 /* allocate space for the property */ 128 if ((temp = malloc(sizeof (StaticProp))) == NULL) { 129 perror("malloc"); 130 exit(1); 131 } 132 133 opp->oprom_size = MAXPROPSIZE; 134 while (opp->oprom_size != 0) { 135 Prop *new; 136 int i; 137 char *tempp, *newp; 138 139 /* 140 * get property 141 */ 142 opp->oprom_size = MAXPROPSIZE; 143 144 if (ioctl(prom_fd, OPROMNXTPROP, opp) < 0) 145 exit(_error("OPROMNXTPROP")); 146 147 if (opp->oprom_size != 0) { 148 temp->name.opp.oprom_size = opp->oprom_size; 149 (void) strcpy(temp->name.opp.oprom_array, 150 opp->oprom_array); 151 152 (void) strcpy(temp->value.opp.oprom_array, 153 temp->name.opp.oprom_array); 154 getpropval(&temp->value.opp); 155 temp->size = temp->value.opp.oprom_size; 156 157 /* Now copy over temp's data to new. */ 158 if ((new = malloc(sizeof (Prop))) == NULL) { 159 perror("malloc"); 160 exit(1); 161 } 162 163 /* 164 * First copy over temp->name's data. The 165 * temp->name.opp.opio_u union always contains char[] 166 * (as opposed to an int or int []). 167 */ 168 new->name.opp.oprom_size = temp->name.opp.oprom_size; 169 170 if ((new->name.opp.oprom_array = 171 malloc(new->name.opp.oprom_size)) == NULL) { 172 perror("malloc"); 173 exit(1); 174 } 175 (void) strcpy(new->name.opp.oprom_array, 176 temp->name.opp.oprom_array); 177 178 new->name.opp.holds_array = 1; 179 180 /* 181 * Then copy over temp->value's data. 182 * temp->value.opp.opio_u could contain char[], int or 183 * int []. If *(temp->value.opp.oprom_array) is '\0', 184 * this indicates int or int []. int is the norm, but 185 * to be safe we assume int [] and copy over 186 * OPROM_NODE_SIZE int elements. 187 */ 188 new->value.opp.oprom_size = temp->value.opp.oprom_size; 189 190 if (*(temp->value.opp.oprom_array) == '\0') { 191 for (i = 0; i < OPROM_NODE_SIZE; i++) 192 new->value.opp.oprom_node[i] = 193 *(&temp->value.opp.oprom_node+i); 194 195 new->value.opp.holds_array = 0; 196 } else { 197 if ((new->value.opp.oprom_array = 198 malloc(new->value.opp.oprom_size)) 199 == NULL) { 200 perror("malloc"); 201 exit(1); 202 } 203 204 /* 205 * temp->value.opp.oprom_array can contain one 206 * or more embedded NULLs. These trip-up the 207 * standard string copying functions, so we do 208 * the copy by hand. temp->value.opp.oprom_array 209 * will be NULL-terminated. oprom_size includes 210 * this terminating NULL. 211 */ 212 newp = new->value.opp.oprom_array; 213 tempp = temp->value.opp.oprom_array; 214 for (i = new->value.opp.oprom_size; i > 0; i--) 215 *newp++ = *tempp++; 216 217 new->value.opp.holds_array = 1; 218 } 219 220 new->size = temp->size; 221 222 /* everything worked so link the property list */ 223 if (node->props == NULL) 224 node->props = new; 225 else if (prop != NULL) 226 prop->next = new; 227 prop = new; 228 prop->next = NULL; 229 } 230 } 231 free(temp); 232 } 233 234 int 235 promopen(int oflag) 236 { 237 /*CONSTCOND*/ 238 while (1) { 239 if ((prom_fd = open(promdev, oflag)) < 0) { 240 if (errno == EAGAIN) { 241 (void) sleep(5); 242 continue; 243 } 244 if (errno == ENXIO) 245 return (-1); 246 exit(_error(dgettext(TEXT_DOMAIN, "cannot open %s"), 247 promdev)); 248 } else 249 return (0); 250 } 251 /*NOTREACHED*/ 252 } 253 254 void 255 promclose(void) 256 { 257 if (close(prom_fd) < 0) 258 exit(_error(dgettext(TEXT_DOMAIN, "close error on %s"), 259 promdev)); 260 } 261 262 /* 263 * Read the value of the property from the PROM device tree 264 */ 265 void 266 getpropval(struct openpromio *opp) 267 { 268 opp->oprom_size = MAXVALSIZE; 269 270 if (ioctl(prom_fd, OPROMGETPROP, opp) < 0) 271 exit(_error("OPROMGETPROP")); 272 } 273 274 int 275 next(int id) 276 { 277 Oppbuf oppbuf; 278 register struct openpromio *opp = &(oppbuf.opp); 279 /* LINTED */ 280 int *ip = (int *)(opp->oprom_array); 281 282 (void) memset((void *) oppbuf.buf, 0, BUFSIZE); 283 284 opp->oprom_size = MAXVALSIZE; 285 *ip = id; 286 if (ioctl(prom_fd, OPROMNEXT, opp) < 0) 287 return (_error("OPROMNEXT")); 288 /* LINTED */ 289 return (*(int *)opp->oprom_array); 290 } 291 292 int 293 child(int id) 294 { 295 Oppbuf oppbuf; 296 register struct openpromio *opp = &(oppbuf.opp); 297 /* LINTED */ 298 int *ip = (int *)(opp->oprom_array); 299 300 (void) memset((void *) oppbuf.buf, 0, BUFSIZE); 301 opp->oprom_size = MAXVALSIZE; 302 *ip = id; 303 if (ioctl(prom_fd, OPROMCHILD, opp) < 0) 304 return (_error("OPROMCHILD")); 305 /* LINTED */ 306 return (*(int *)opp->oprom_array); 307 } 308 309 /* 310 * Check if the Prom node passed in contains a property called 311 * "board#". 312 */ 313 int 314 has_board_num(Prom_node *node) 315 { 316 Prop *prop = node->props; 317 318 /* 319 * walk thru all properties in this PROM node and look for 320 * board# prop 321 */ 322 while (prop != NULL) { 323 if (strcmp(prop->name.opp.oprom_array, "board#") == 0) 324 return (1); 325 326 prop = prop->next; 327 } 328 329 return (0); 330 } /* end of has_board_num() */ 331 332 /* 333 * Retrieve the value of the board number property from this Prom 334 * node. It has the type of int. 335 */ 336 int 337 get_board_num(Prom_node *node) 338 { 339 Prop *prop = node->props; 340 341 /* 342 * walk thru all properties in this PROM node and look for 343 * board# prop 344 */ 345 while (prop != NULL) { 346 if (strcmp(prop->name.opp.oprom_array, "board#") == 0) 347 return (prop->value.opp.oprom_node[0]); 348 349 prop = prop->next; 350 } 351 352 return (-1); 353 } /* end of get_board_num() */ 354 355 /* 356 * Find the requested board struct in the system device tree. 357 */ 358 Board_node * 359 find_board(Sys_tree *root, int board) 360 { 361 Board_node *bnode = root->bd_list; 362 363 while ((bnode != NULL) && (board != bnode->board_num)) 364 bnode = bnode->next; 365 366 return (bnode); 367 } /* end of find_board() */ 368 369 /* 370 * Add a board to the system list in order. Initialize all pointer 371 * fields to NULL. 372 */ 373 Board_node * 374 insert_board(Sys_tree *root, int board) 375 { 376 Board_node *bnode; 377 Board_node *temp = root->bd_list; 378 379 if ((bnode = (Board_node *) malloc(sizeof (Board_node))) == NULL) { 380 perror("malloc"); 381 exit(1); 382 } 383 bnode->nodes = NULL; 384 bnode->next = NULL; 385 bnode->board_num = board; 386 387 if (temp == NULL) 388 root->bd_list = bnode; 389 else if (temp->board_num > board) { 390 bnode->next = temp; 391 root->bd_list = bnode; 392 } else { 393 while ((temp->next != NULL) && (board > temp->next->board_num)) 394 temp = temp->next; 395 bnode->next = temp->next; 396 temp->next = bnode; 397 } 398 root->board_cnt++; 399 400 return (bnode); 401 } /* end of insert_board() */ 402 403 /* 404 * This function searches through the properties of the node passed in 405 * and returns a pointer to the value of the name property. 406 */ 407 char * 408 get_node_name(Prom_node *pnode) 409 { 410 Prop *prop; 411 412 if (pnode == NULL) { 413 return (NULL); 414 } 415 416 prop = pnode->props; 417 while (prop != NULL) { 418 if (strcmp("name", prop->name.opp.oprom_array) == 0) 419 return (prop->value.opp.oprom_array); 420 prop = prop->next; 421 } 422 return (NULL); 423 } /* end of get_node_name() */ 424 425 /* 426 * This function searches through the properties of the node passed in 427 * and returns a pointer to the value of the name property. 428 */ 429 char * 430 get_node_type(Prom_node *pnode) 431 { 432 Prop *prop; 433 434 if (pnode == NULL) { 435 return (NULL); 436 } 437 438 prop = pnode->props; 439 while (prop != NULL) { 440 if (strcmp("device_type", prop->name.opp.oprom_array) == 0) 441 return (prop->value.opp.oprom_array); 442 prop = prop->next; 443 } 444 return (NULL); 445 } /* end of get_node_type() */ 446 447 /* 448 * Do a depth-first walk of a device tree and 449 * return the first node with the name matching. 450 */ 451 452 Prom_node * 453 dev_find_node(Prom_node *root, char *name) 454 { 455 Prom_node *node; 456 457 node = dev_find_node_by_type(root, "name", name); 458 459 return (node); 460 } 461 462 Prom_node * 463 dev_next_node(Prom_node *root, char *name) 464 { 465 Prom_node *node; 466 467 node = dev_next_node_by_type(root, "name", name); 468 469 return (node); 470 } 471 472 /* 473 * Search for and return a node of the required type. If no node is found, 474 * then return NULL. 475 */ 476 Prom_node * 477 dev_find_type(Prom_node *root, char *type) 478 { 479 Prom_node *node; 480 481 node = dev_find_node_by_type(root, "device_type", type); 482 483 return (node); /* not found */ 484 } 485 486 /* 487 * Start from the current node and return the next node besides the 488 * current one which has the requested type property. 489 */ 490 Prom_node * 491 dev_next_type(Prom_node *root, char *type) 492 { 493 Prom_node *node; 494 495 node = dev_next_node_by_type(root, "device_type", type); 496 497 return (node); /* not found */ 498 } 499 500 /* 501 * Search a device tree and return the first failed node that is found. 502 * (has a 'status' property) 503 */ 504 Prom_node * 505 find_failed_node(Prom_node * root) 506 { 507 Prom_node *pnode; 508 509 if (root == NULL) 510 return (NULL); 511 512 if (node_failed(root)) { 513 return (root); 514 } 515 516 /* search the child */ 517 if ((pnode = find_failed_node(root->child)) != NULL) 518 return (pnode); 519 520 /* search the siblings */ 521 if ((pnode = find_failed_node(root->sibling)) != NULL) 522 return (pnode); 523 524 return (NULL); 525 } /* end of find_failed_node() */ 526 527 /* 528 * Start from the current node and return the next node besides 529 * the current one which is failed. (has a 'status' property) 530 */ 531 Prom_node * 532 next_failed_node(Prom_node * root) 533 { 534 Prom_node *pnode; 535 Prom_node *parent; 536 537 if (root == NULL) 538 return (NULL); 539 540 /* search the child */ 541 if ((pnode = find_failed_node(root->child)) != NULL) { 542 return (pnode); 543 } 544 545 /* search the siblings */ 546 if ((pnode = find_failed_node(root->sibling)) != NULL) { 547 return (pnode); 548 } 549 550 /* backtracking the search up through parents' siblings */ 551 parent = root->parent; 552 while (parent != NULL) { 553 if ((pnode = find_failed_node(parent->sibling)) != NULL) 554 return (pnode); 555 else 556 parent = parent->parent; 557 } 558 559 return (NULL); 560 } /* end of find_failed_node() */ 561 562 /* 563 * node_failed 564 * 565 * This function determines if the current Prom node is failed. This 566 * is defined by having a status property containing the token 'fail'. 567 */ 568 int 569 node_failed(Prom_node *node) 570 { 571 return (node_status(node, "fail")); 572 } 573 574 int 575 node_status(Prom_node *node, char *status) 576 { 577 void *value; 578 579 if (status == NULL) 580 return (0); 581 582 /* search the local node */ 583 if ((value = get_prop_val(find_prop(node, "status"))) != NULL) { 584 if ((value != NULL) && strstr((char *)value, status)) 585 return (1); 586 } 587 return (0); 588 } 589 590 /* 591 * Get a property's value. Must be void * since the property can 592 * be any data type. Caller must know the *PROPER* way to use this 593 * data. 594 */ 595 void * 596 get_prop_val(Prop *prop) 597 { 598 if (prop == NULL) 599 return (NULL); 600 601 if (prop->value.opp.holds_array) 602 return ((void *)(prop->value.opp.oprom_array)); 603 else 604 return ((void *)(&prop->value.opp.oprom_node[0])); 605 } /* end of get_prop_val() */ 606 607 /* 608 * Search a Prom node and retrieve the property with the correct 609 * name. 610 */ 611 Prop * 612 find_prop(Prom_node *pnode, char *name) 613 { 614 Prop *prop; 615 616 if (pnode == NULL) { 617 return (NULL); 618 } 619 620 if (pnode->props == NULL) { 621 (void) printf("%s", dgettext(TEXT_DOMAIN, "Prom node has " 622 "no properties\n")); 623 return (NULL); 624 } 625 626 prop = pnode->props; 627 while ((prop != NULL) && (strcmp(prop->name.opp.oprom_array, name))) 628 prop = prop->next; 629 630 return (prop); 631 } 632 633 /* 634 * This function adds a board node to the board structure where that 635 * that node's physical component lives. 636 */ 637 void 638 add_node(Sys_tree *root, Prom_node *pnode) 639 { 640 int board; 641 Board_node *bnode; 642 Prom_node *p; 643 644 /* add this node to the Board list of the appropriate board */ 645 if ((board = get_board_num(pnode)) == -1) { 646 /* board is 0 if not on Sunfire */ 647 board = 0; 648 } 649 650 /* find the node with the same board number */ 651 if ((bnode = find_board(root, board)) == NULL) { 652 bnode = insert_board(root, board); 653 bnode->board_type = UNKNOWN_BOARD; 654 } 655 656 /* now attach this prom node to the board list */ 657 /* Insert this node at the end of the list */ 658 pnode->sibling = NULL; 659 if (bnode->nodes == NULL) 660 bnode->nodes = pnode; 661 else { 662 p = bnode->nodes; 663 while (p->sibling != NULL) 664 p = p->sibling; 665 p->sibling = pnode; 666 } 667 668 } 669 670 /* 671 * Find the device on the current board with the requested device ID 672 * and name. If this rountine is passed a NULL pointer, it simply returns 673 * NULL. 674 */ 675 Prom_node * 676 find_device(Board_node *board, int id, char *name) 677 { 678 Prom_node *pnode; 679 int mask; 680 681 /* find the first cpu node */ 682 pnode = dev_find_node(board->nodes, name); 683 684 mask = 0x1F; 685 while (pnode != NULL) { 686 if ((get_id(pnode) & mask) == id) 687 return (pnode); 688 689 pnode = dev_next_node(pnode, name); 690 } 691 return (NULL); 692 } 693 694 Prom_node * 695 dev_find_node_by_type(Prom_node *root, char *type, char *property) 696 { 697 Prom_node *node; 698 char *type_prop; 699 700 if (root == NULL || property == NULL) 701 return (NULL); 702 703 type_prop = (char *)get_prop_val(find_prop(root, type)); 704 705 if (type_prop != NULL) { 706 if (strcmp(type_prop, property) == 0) { 707 return (root); 708 } 709 } 710 711 /* look at your children first */ 712 if ((node = dev_find_node_by_type(root->child, type, 713 property)) != NULL) 714 return (node); 715 716 /* now look at your siblings */ 717 if ((node = dev_find_node_by_type(root->sibling, type, 718 property)) != NULL) 719 return (node); 720 721 return (NULL); /* not found */ 722 } 723 724 Prom_node * 725 dev_next_node_by_type(Prom_node *root, char *type, char *property) 726 { 727 Prom_node *node; 728 729 if (root == NULL || property == NULL) 730 return (NULL); 731 732 /* look at your children first */ 733 if ((node = dev_find_node_by_type(root->child, type, 734 property)) != NULL) 735 return (node); 736 737 /* now look at your siblings */ 738 if ((node = dev_find_node_by_type(root->sibling, type, 739 property)) != NULL) 740 return (node); 741 742 /* now look at papa's siblings */ 743 if ((node = dev_find_node_by_type(root->parent->sibling, 744 type, property)) != NULL) 745 return (node); 746 747 return (NULL); /* not found */ 748 } 749 750 /* 751 * Do a depth-first walk of a device tree and 752 * return the first node with the matching compatible. 753 */ 754 Prom_node * 755 dev_find_node_by_compatible(Prom_node *root, char *compatible) 756 { 757 Prom_node *node; 758 Prop *prop; 759 char *compatible_array; 760 int size, nbytes; 761 762 if (root == NULL || compatible == NULL) 763 return (NULL); 764 765 if ((prop = find_prop(root, "compatible")) != NULL && 766 (compatible_array = (char *)get_prop_val(prop)) != NULL) { 767 /* 768 * The Prop structure returned by find_prop() is supposed 769 * to contain an indication of how big the value of the 770 * compatible property is. Since it is an array of strings 771 * this is our only means of determining just how many 772 * strings might be in this property. However, this size 773 * is often left as zero even though there is at least one 774 * string present. When this is the case, all we can do 775 * is examine the first string in the compatible property. 776 */ 777 778 for (size = prop->size; size >= 0; size -= nbytes) { 779 if (strcmp(compatible_array, compatible) == 0) 780 return (root); /* found a match */ 781 782 nbytes = strlen(compatible_array) + 1; 783 compatible_array += nbytes; 784 } 785 } 786 787 node = dev_find_node_by_compatible(root->child, compatible); 788 if (node != NULL) 789 return (node); 790 791 /* 792 * Note the very deliberate use of tail recursion here. A good 793 * compiler (such as Sun's) will recognize this and generate code 794 * that does not allocate another stack frame. Instead, it will 795 * overlay the existing stack frame with the new one, the only change 796 * having been to replace the original root with its sibling. 797 * This has the potential to create some confusion for anyone 798 * trying to debug this code from a core dump, since the stack 799 * trace will not reveal recursion on siblings, only on children. 800 */ 801 802 return (dev_find_node_by_compatible(root->sibling, compatible)); 803 } 804 805 /* 806 * Start from the current node and return the next node besides 807 * the current one which has the requested compatible property. 808 */ 809 Prom_node * 810 dev_next_node_by_compatible(Prom_node *root, char *compatible) 811 { 812 Prom_node *node; 813 814 if (root == NULL || compatible == NULL) 815 return (NULL); 816 817 node = dev_find_node_by_compatible(root->child, compatible); 818 if (node != NULL) 819 return (node); 820 821 /* 822 * More tail recursion. Even though it is a different function, 823 * this will overlay the current stack frame. Caveat exterminator. 824 */ 825 826 node = dev_find_node_by_compatible(root->sibling, compatible); 827 if (node != NULL) 828 return (node); 829 830 return (dev_find_node_by_compatible(root->parent->sibling, compatible)); 831 } 832