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