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 1999-2002 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * Copyright 2020 Peter Tribble. 26 * 27 * Desktop Platform specific functions. 28 * 29 * Called when: 30 * machine_type == MTYPE_DARWIN && 31 * machine_type == MTYPE_DEFAULT 32 * 33 */ 34 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <unistd.h> 38 #include <ctype.h> 39 #include <string.h> 40 #include <kvm.h> 41 #include <varargs.h> 42 #include <errno.h> 43 #include <time.h> 44 #include <dirent.h> 45 #include <fcntl.h> 46 #include <sys/param.h> 47 #include <sys/stat.h> 48 #include <sys/types.h> 49 #include <sys/systeminfo.h> 50 #include <sys/utsname.h> 51 #include <sys/openpromio.h> 52 #include <kstat.h> 53 #include <libintl.h> 54 #include <syslog.h> 55 #include <sys/dkio.h> 56 #include "pdevinfo.h" 57 #include "display.h" 58 #include "pdevinfo_sun4u.h" 59 #include "display_sun4u.h" 60 #include "libprtdiag.h" 61 62 #if !defined(TEXT_DOMAIN) 63 #define TEXT_DOMAIN "SYS_TEST" 64 #endif 65 66 67 #define PCI_BUS(x) ((x >> 16) & 0xff) 68 69 /* 70 * State variable to signify the type of machine we're currently 71 * running on. Since prtdiag has come to be the dumping ground 72 * for lots of platform-specific routines, and machine architecture 73 * alone is not enough to determine our course of action, we need 74 * to enumerate the different machine types that we should worry 75 * about. 76 */ 77 enum machine_type { 78 MTYPE_DEFAULT = 0, /* Desktop-class machine */ 79 MTYPE_DARWIN = 1 80 }; 81 82 enum machine_type machine_type = MTYPE_DEFAULT; 83 84 extern int print_flag; 85 86 /* 87 * these functions will overlay the symbol table of libprtdiag 88 * at runtime (desktop systems only) 89 */ 90 int error_check(Sys_tree *tree, struct system_kstat_data *kstats); 91 int disp_fail_parts(Sys_tree *tree); 92 void display_hp_fail_fault(Sys_tree *tree, struct system_kstat_data *kstats); 93 void display_diaginfo(int flag, Prom_node *root, Sys_tree *tree, 94 struct system_kstat_data *kstats); 95 void display_pci(Board_node *bnode); 96 void display_sbus(Board_node *); 97 98 99 /* local functions */ 100 static void dt_disp_asic_revs(Sys_tree *); 101 static void display_sabre_pci(Board_node *); 102 static void display_dev_node(Prom_node *np, int depth); 103 static void get_machine_type(void); 104 105 int 106 error_check(Sys_tree *tree, struct system_kstat_data *kstats) 107 { 108 int exit_code = 0; /* init to all OK */ 109 110 #ifdef lint 111 kstats = kstats; 112 #endif 113 114 /* 115 * silently check for any types of machine errors 116 */ 117 print_flag = 0; 118 if (disp_fail_parts(tree)) { 119 /* set exit_code to show failures */ 120 exit_code = 1; 121 } 122 print_flag = 1; 123 124 return (exit_code); 125 } 126 127 128 /* 129 * disp_fail_parts 130 * 131 * Display the failed parts in the system. This function looks for 132 * the status property in all PROM nodes. On systems where 133 * the PROM does not support passing diagnostic information 134 * through the device tree, this routine will be silent. 135 */ 136 int 137 disp_fail_parts(Sys_tree *tree) 138 { 139 int exit_code; 140 int system_failed = 0; 141 Board_node *bnode = tree->bd_list; 142 Prom_node *pnode; 143 144 exit_code = 0; 145 146 /* go through all of the boards looking for failed units. */ 147 while (bnode != NULL) { 148 /* find failed chips */ 149 pnode = find_failed_node(bnode->nodes); 150 if ((pnode != NULL) && !system_failed) { 151 system_failed = 1; 152 exit_code = 1; 153 if (print_flag == 0) { 154 return (exit_code); 155 } 156 log_printf("\n", 0); 157 log_printf(dgettext(TEXT_DOMAIN, "Failed Field " 158 "Replaceable Units (FRU) in System:\n"), 0); 159 log_printf("==========================" 160 "====================\n", 0); 161 } 162 163 while (pnode != NULL) { 164 void *value; 165 char *name; /* node name string */ 166 char *type; /* node type string */ 167 char *board_type = NULL; 168 169 value = get_prop_val(find_prop(pnode, "status")); 170 name = get_node_name(pnode); 171 172 /* sanity check of data retreived from PROM */ 173 if ((value == NULL) || (name == NULL)) { 174 pnode = next_failed_node(pnode); 175 continue; 176 } 177 178 /* Find the board type of this board */ 179 if (bnode->board_type == CPU_BOARD) { 180 board_type = "CPU"; 181 } else { 182 board_type = "IO"; 183 } 184 185 log_printf(dgettext(TEXT_DOMAIN, "%s unavailable " 186 "on %s Board #%d\n"), name, board_type, 187 bnode->board_num, 0); 188 189 log_printf(dgettext(TEXT_DOMAIN, 190 "\tPROM fault string: %s\n"), value, 0); 191 192 log_printf(dgettext(TEXT_DOMAIN, 193 "\tFailed Field Replaceable Unit is "), 0); 194 195 /* 196 * Determine whether FRU is CPU module, system 197 * board, or SBus card. 198 */ 199 if ((name != NULL) && (strstr(name, "sbus"))) { 200 201 log_printf(dgettext(TEXT_DOMAIN, 202 "SBus Card %d\n"), 203 get_sbus_slot(pnode), 0); 204 205 } else if (((name = get_node_name(pnode->parent)) != 206 NULL) && (strstr(name, "pci"))) { 207 208 log_printf(dgettext(TEXT_DOMAIN, 209 "PCI Card %d"), 210 get_pci_device(pnode), 0); 211 212 } else if (((type = get_node_type(pnode)) != NULL) && 213 (strstr(type, "cpu"))) { 214 215 log_printf(dgettext(TEXT_DOMAIN, "UltraSPARC " 216 "module Board %d Module %d\n"), 0, 217 get_id(pnode)); 218 219 } else { 220 log_printf(dgettext(TEXT_DOMAIN, 221 "%s board %d\n"), board_type, 222 bnode->board_num, 0); 223 } 224 pnode = next_failed_node(pnode); 225 } 226 bnode = bnode->next; 227 } 228 229 if (!system_failed) { 230 log_printf("\n", 0); 231 log_printf(dgettext(TEXT_DOMAIN, 232 "No failures found in System\n"), 0); 233 log_printf("===========================\n", 0); 234 } 235 236 if (system_failed) 237 return (1); 238 else 239 return (0); 240 } 241 242 243 void 244 display_hp_fail_fault(Sys_tree *tree, struct system_kstat_data *kstats) 245 { 246 247 #ifdef lint 248 kstats = kstats; 249 #endif 250 /* Display failed units */ 251 (void) disp_fail_parts(tree); 252 } 253 254 void 255 display_diaginfo(int flag, Prom_node *root, Sys_tree *tree, 256 struct system_kstat_data *kstats) 257 { 258 259 #ifdef lint 260 kstats = kstats; 261 #endif 262 /* 263 * Now display the last powerfail time and the fatal hardware 264 * reset information. We do this under a couple of conditions. 265 * First if the user asks for it. The second is iof the user 266 * told us to do logging, and we found a system failure. 267 */ 268 if (flag) { 269 /* 270 * display time of latest powerfail. Not all systems 271 * have this capability. For those that do not, this 272 * is just a no-op. 273 */ 274 disp_powerfail(root); 275 276 dt_disp_asic_revs(tree); 277 278 platform_disp_prom_version(tree); 279 } 280 return; 281 282 } 283 284 void 285 display_pci(Board_node *bnode) 286 { 287 Prom_node *pci; 288 289 /* 290 * We have different routines for walking/displaying PCI 291 * devices depending on whether the PCI device is a 292 * Psycho or a Sabre. 293 */ 294 pci = dev_find_node_by_type(bnode->nodes, "model", "SUNW,psycho"); 295 if (pci != NULL) { 296 display_psycho_pci(bnode); 297 return; 298 } 299 300 pci = dev_find_node_by_type(bnode->nodes, "model", "SUNW,sabre"); 301 if (pci != NULL) { 302 display_sabre_pci(bnode); 303 return; 304 } 305 } 306 307 /* 308 * local functions 309 */ 310 311 void 312 dt_disp_asic_revs(Sys_tree *tree) 313 { 314 Board_node *bnode; 315 Prom_node *pnode; 316 char *name; 317 int *version; 318 319 /* Print the header */ 320 log_printf("\n", 0); 321 log_printf("=========================", 0); 322 log_printf(" HW Revisions ", 0); 323 log_printf("=========================", 0); 324 log_printf("\n", 0); 325 log_printf("\n", 0); 326 327 bnode = tree->bd_list; 328 329 log_printf("ASIC Revisions:\n", 0); 330 log_printf("---------------\n", 0); 331 332 /* Find sysio and print rev */ 333 for (pnode = dev_find_node(bnode->nodes, "sbus"); pnode != NULL; 334 pnode = dev_next_node(pnode, "sbus")) { 335 version = (int *)get_prop_val(find_prop(pnode, "version#")); 336 name = get_prop_val(find_prop(pnode, "name")); 337 338 if ((version != NULL) && (name != NULL)) { 339 log_printf("SBus: %s Rev %d\n", 340 name, *version, 0); 341 } 342 } 343 344 /* Find Psycho and print rev */ 345 for (pnode = dev_find_node(bnode->nodes, "pci"); pnode != NULL; 346 pnode = dev_next_node(pnode, "pci")) { 347 version = (int *)get_prop_val(find_prop(pnode, "version#")); 348 name = get_prop_val(find_prop(pnode, "name")); 349 350 if ((version != NULL) && (name != NULL)) 351 log_printf("PCI: %s Rev %d\n", 352 name, *version, 0); 353 } 354 355 /* Find Cheerio and print rev */ 356 for (pnode = dev_find_node(bnode->nodes, "ebus"); pnode != NULL; 357 pnode = dev_next_node(pnode, "ebus")) { 358 version = (int *)get_prop_val(find_prop(pnode, "revision-id")); 359 name = get_prop_val(find_prop(pnode, "name")); 360 361 if ((version != NULL) && (name != NULL)) 362 log_printf("Cheerio: %s Rev %d\n", name, *version, 0); 363 } 364 365 366 /* Find the FEPS and print rev */ 367 for (pnode = dev_find_node(bnode->nodes, "SUNW,hme"); pnode != NULL; 368 pnode = dev_next_node(pnode, "SUNW,hme")) { 369 version = (int *)get_prop_val(find_prop(pnode, "hm-rev")); 370 name = get_prop_val(find_prop(pnode, "name")); 371 372 if ((version != NULL) && (name != NULL)) { 373 log_printf("FEPS: %s Rev ", name); 374 if (*version == 0xa0) { 375 log_printf("2.0\n", 0); 376 } else if (*version == 0x20) { 377 log_printf("2.1\n", 0); 378 } else { 379 log_printf("%x\n", *version, 0); 380 } 381 } 382 } 383 log_printf("\n", 0); 384 385 display_ffb(bnode, 0); 386 } 387 388 /* 389 * print the header and call display_dev_node() to walk the device 390 * tree (darwin platform only). 391 */ 392 static void 393 display_sabre_pci(Board_node *board) 394 { 395 if (board == NULL) 396 return; 397 398 log_printf(" Bus# Freq\n", 0); 399 log_printf("Brd Type MHz Slot " 400 "Name Model", 0); 401 log_printf("\n", 0); 402 log_printf("--- ---- ---- ---- " 403 "-------------------------------- ----------------------", 0); 404 log_printf("\n", 0); 405 display_dev_node(board->nodes, 0); 406 log_printf("\n", 0); 407 } 408 409 410 /* 411 * Recursively traverse the device tree and use tree depth as filter. 412 * called by: display_sabre_pci() 413 */ 414 static void 415 display_dev_node(Prom_node *np, int depth) 416 { 417 char *name, *model, *compat, *regval; 418 unsigned int reghi; 419 420 if (!np) 421 return; 422 if (depth > 2) 423 return; 424 425 name = get_prop_val(find_prop(np, "name")); 426 model = get_prop_val(find_prop(np, "model")); 427 compat = get_prop_val(find_prop(np, "compatible")); 428 regval = get_prop_val(find_prop(np, "reg")); 429 430 if (!regval) 431 return; 432 else 433 reghi = *(int *)regval; 434 435 if (!model) 436 model = ""; 437 if (!name) 438 name = ""; 439 440 if (depth == 2) { 441 char buf[256]; 442 if (compat) 443 (void) sprintf(buf, "%s-%s", name, compat); 444 else 445 (void) sprintf(buf, "%s", name); 446 447 log_printf(" 0 PCI-%d 33 ", PCI_BUS(reghi), 0); 448 log_printf("%3d ", PCI_DEVICE(reghi), 0); 449 log_printf("%-32.32s", buf, 0); 450 log_printf(strlen(buf) > 32 ? "+ " : " ", 0); 451 log_printf("%-22.22s", model, 0); 452 log_printf(strlen(model) > 22 ? "+" : "", 0); 453 log_printf("\n", 0); 454 455 #ifdef DEBUG 456 if (!compat) 457 compat = ""; 458 printf("bus=%d slot=%d name=%s model=%s compat=%s\n", 459 PCI_BUS(reghi), PCI_DEVICE(reghi), name, model, compat); 460 #endif 461 } 462 463 if ((!strstr(name, "ebus")) && (!strstr(name, "ide"))) 464 display_dev_node(np->child, depth+1); 465 display_dev_node(np->sibling, depth); 466 } 467 468 /* 469 * display_sbus 470 * Display all the SBus IO cards on this board. 471 */ 472 void 473 display_sbus(Board_node *board) 474 { 475 struct io_card card; 476 struct io_card *card_list = NULL; 477 int freq; 478 int card_num; 479 void *value; 480 Prom_node *sbus; 481 Prom_node *card_node; 482 483 if (board == NULL) 484 return; 485 486 for (sbus = dev_find_node(board->nodes, SBUS_NAME); sbus != NULL; 487 sbus = dev_next_node(sbus, SBUS_NAME)) { 488 489 /* Skip failed nodes for now */ 490 if (node_failed(sbus)) 491 continue; 492 493 /* Calculate SBus frequency in MHz */ 494 value = get_prop_val(find_prop(sbus, "clock-frequency")); 495 if (value != NULL) 496 freq = ((*(int *)value) + 500000) / 1000000; 497 else 498 freq = -1; 499 500 for (card_node = sbus->child; card_node != NULL; 501 card_node = card_node->sibling) { 502 char *model; 503 char *name; 504 char *child_name; 505 506 card_num = get_sbus_slot(card_node); 507 if (card_num == -1) 508 continue; 509 510 /* Fill in card information */ 511 card.display = 1; 512 card.freq = freq; 513 card.board = board->board_num; 514 (void) sprintf(card.bus_type, "SBus"); 515 card.slot = card_num; 516 card.status[0] = '\0'; 517 518 /* Try and get card status */ 519 value = get_prop_val(find_prop(card_node, "status")); 520 if (value != NULL) 521 (void) strncpy(card.status, (char *)value, 522 MAXSTRLEN); 523 524 /* XXX - For now, don't display failed cards */ 525 if (strstr(card.status, "fail") != NULL) 526 continue; 527 528 /* 529 * sets the machine_type var if not already set 530 */ 531 get_machine_type(); 532 533 /* 534 * For desktops, the only high slot number that 535 * needs to be displayed is the # 14 slot. 536 */ 537 if (machine_type == MTYPE_DEFAULT && 538 card_num >= MX_SBUS_SLOTS && card_num != 14) { 539 continue; 540 } 541 542 /* Now gather all of the node names for that card */ 543 model = (char *)get_prop_val(find_prop(card_node, 544 "model")); 545 name = get_node_name(card_node); 546 547 if (name == NULL) 548 continue; 549 550 card.name[0] = '\0'; 551 card.model[0] = '\0'; 552 553 /* Figure out how we want to display the name */ 554 child_name = get_node_name(card_node->child); 555 if ((card_node->child != NULL) && 556 (child_name != NULL)) { 557 value = get_prop_val(find_prop(card_node->child, 558 "device_type")); 559 if (value != NULL) 560 (void) sprintf(card.name, "%s/%s (%s)", 561 name, child_name, 562 (char *)value); 563 else 564 (void) sprintf(card.name, "%s/%s", name, 565 child_name); 566 } else { 567 (void) strncpy(card.name, name, MAXSTRLEN); 568 } 569 570 if (model != NULL) 571 (void) strncpy(card.model, model, MAXSTRLEN); 572 573 card_list = insert_io_card(card_list, &card); 574 } 575 } 576 577 /* We're all done gathering card info, now print it out */ 578 display_io_cards(card_list); 579 free_io_cards(card_list); 580 } 581 582 static void 583 get_machine_type(void) 584 { 585 char name[MAXSTRLEN]; 586 587 machine_type = MTYPE_DEFAULT; 588 589 /* Figure out what kind of machine we're on */ 590 if (sysinfo(SI_PLATFORM, name, MAXSTRLEN) != -1) { 591 if (strcmp(name, "SUNW,Ultra-5_10") == 0) 592 machine_type = MTYPE_DARWIN; 593 } 594 } 595