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
error_check(Sys_tree * tree,struct system_kstat_data * kstats)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
disp_fail_parts(Sys_tree * tree)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
display_hp_fail_fault(Sys_tree * tree,struct system_kstat_data * kstats)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
display_diaginfo(int flag,Prom_node * root,Sys_tree * tree,struct system_kstat_data * kstats)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
display_pci(Board_node * bnode)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
dt_disp_asic_revs(Sys_tree * tree)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
display_sabre_pci(Board_node * board)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
display_dev_node(Prom_node * np,int depth)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
display_sbus(Board_node * board)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
get_machine_type(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