xref: /titanic_51/usr/src/lib/libprtdiag_psr/sparc/sunfire/common/sunfire.c (revision 03831d35f7499c87d51205817c93e9a8d42c4bae)
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  * Sunfire Platform specific functions.
27  *
28  * 	called when :
29  *	machine_type == MTYPE_SUNFIRE
30  */
31 
32 #pragma ident	"%Z%%M%	%I%	%E% SMI"
33 
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <ctype.h>
38 #include <string.h>
39 #include <kvm.h>
40 #include <varargs.h>
41 #include <time.h>
42 #include <dirent.h>
43 #include <fcntl.h>
44 #include <errno.h>
45 #include <sys/param.h>
46 #include <sys/stat.h>
47 #include <sys/types.h>
48 #include <sys/utsname.h>
49 #include <sys/openpromio.h>
50 #include <libintl.h>
51 #include <syslog.h>
52 #include <sys/dkio.h>
53 #include "pdevinfo.h"
54 #include "display.h"
55 #include "pdevinfo_sun4u.h"
56 #include "display_sun4u.h"
57 #include "libprtdiag.h"
58 
59 #if !defined(TEXT_DOMAIN)
60 #define	TEXT_DOMAIN	"SYS_TEST"
61 #endif
62 
63 /* Macros for manipulating UPA IDs and board numbers on Sunfire. */
64 #define	bd_to_upa(bd) ((bd) << 1)
65 #define	upa_to_bd(upa)  ((upa) >> 1)
66 
67 #define	MAX_MSGS	64
68 
69 extern	int	print_flag;
70 
71 /*
72  * these functions will overlay the symbol table of libprtdiag
73  * at runtime (sunfire systems only)
74  */
75 int	error_check(Sys_tree *tree, struct system_kstat_data *kstats);
76 void	display_memoryconf(Sys_tree *tree, struct grp_info *grps);
77 int	disp_fail_parts(Sys_tree *tree);
78 void	display_memorysize(Sys_tree *tree, struct system_kstat_data *kstats,
79 		struct grp_info *grps, struct mem_total *memory_total);
80 void	display_hp_fail_fault(Sys_tree *tree, struct system_kstat_data *kstats);
81 void	display_diaginfo(int flag, Prom_node *root, Sys_tree *tree,
82 		struct system_kstat_data *kstats);
83 void	display_mid(int mid);
84 void 	display_pci(Board_node *);
85 void 	display_ffb(Board_node *, int);
86 void	add_node(Sys_tree *, Prom_node *);
87 void	resolve_board_types(Sys_tree *);
88 
89 /* local functions */
90 static	void build_mem_tables(Sys_tree *, struct system_kstat_data *,
91 		struct grp_info *);
92 static	void get_mem_total(struct mem_total *, struct grp_info *);
93 static	int disp_fault_list(Sys_tree *, struct system_kstat_data *);
94 static	int disp_err_log(struct system_kstat_data *);
95 static	int disp_env_status(struct system_kstat_data *);
96 static	int disp_keysw_and_leds(struct system_kstat_data *);
97 static	void sunfire_disp_prom_versions(Sys_tree *);
98 static	void erase_msgs(char **);
99 static	void display_msgs(char **msgs, int board);
100 static	void sunfire_disp_asic_revs(Sys_tree *, struct system_kstat_data *);
101 static	void display_hp_boards(struct system_kstat_data *);
102 static	int disp_parts(char **, u_longlong_t, int);
103 /*
104  * Error analysis routines. These routines decode data from specified
105  * error registers. They are meant to be used for decoding the fatal
106  * hardware reset data passed to the kernel by sun4u POST.
107  */
108 static int analyze_cpu(char **, int, u_longlong_t);
109 static int analyze_ac(char **, u_longlong_t);
110 static int analyze_dc(int, char **, u_longlong_t);
111 
112 #define	RESERVED_STR	"Reserved"
113 
114 #define	MAX_PARTS	5
115 #define	MAX_FRUS	5
116 
117 #define	MAXSTRLEN	256
118 
119 /* Define special bits */
120 #define	UPA_PORT_A	0x1
121 #define	UPA_PORT_B	0x2
122 
123 
124 /*
125  * These defines comne from async.h, but it does not get exported from
126  * uts/sun4u/sys, so they must be redefined.
127  */
128 #define	P_AFSR_ISAP	0x0000000040000000ULL /* incoming addr. parity err */
129 #define	P_AFSR_ETP	0x0000000020000000ULL /* ecache tag parity */
130 #define	P_AFSR_ETS	0x00000000000F0000ULL /* cache tag parity syndrome */
131 #define	ETS_SHIFT	16
132 
133 /* List of parts possible */
134 #define	RSVD_PART	1
135 #define	UPA_PART	2
136 #define	UPA_A_PART	3
137 #define	UPA_B_PART	4
138 #define	SOFTWARE_PART	5
139 #define	AC_PART		6
140 #define	AC_ANY_PART	7
141 #define	DTAG_PART	8
142 #define	DTAG_A_PART	9
143 #define	DTAG_B_PART	10
144 #define	FHC_PART	11
145 #define	BOARD_PART	12
146 #define	BOARD_ANY_PART	13
147 #define	BOARD_CONN_PART	14
148 #define	BACK_PIN_PART	15
149 #define	BACK_TERM_PART	16
150 #define	CPU_PART	17
151 
152 /* List of possible parts */
153 static char *part_str[] = {
154 	"",			/* 0, a placeholder for indexing */
155 	"",			/* 1, reserved strings shouldn't be printed */
156 	"UPA devices",					/* 2 */
157 	"UPA Port A device",				/* 3 */
158 	"UPA Port B device",				/* 4 */
159 	"Software error",				/* 5 */
160 	"Address Controller",				/* 6 */
161 	"Undetermined Address Controller in system",	/* 7 */
162 	"Data Tags",					/* 8 */
163 	"Data Tags for UPA Port A",			/* 9 */
164 	"Data Tags for UPA Port B",			/* 10 */
165 	"Firehose Controller",				/* 11 */
166 	"This Board",					/* 12 */
167 	"Undetermined Board in system",			/* 13 */
168 	"Board Connector",				/* 14 */
169 	"Centerplane pins ",				/* 15 */
170 	"Centerplane terminators",			/* 16 */
171 	"CPU",						/* 17 */
172 };
173 
174 /* Ecache parity error messages. Tells which bits are bad. */
175 static char *ecache_parity[] = {
176 	"Bits 7:0 ",
177 	"Bits 15:8 ",
178 	"Bits 21:16 ",
179 	"Bits 24:22 "
180 };
181 
182 
183 struct ac_error {
184 	char *error;
185 	int part[MAX_PARTS];
186 };
187 
188 typedef struct ac_error ac_err;
189 
190 /*
191  * Hardware error register meanings, failed parts and FRUs. The
192  * following strings are indexed for the bit positions of the
193  * corresponding bits in the hardware. The code checks bit x of
194  * the hardware error register and prints out string[x] if the bit
195  * is turned on.
196  *
197  * This database of parts which are probably failed and which FRU's
198  * to replace was based on knowledge of the Sunfire Programmers Spec.
199  * and discussions with the hardware designers. The order of the part
200  * lists and consequently the FRU lists are in the order of most
201  * likely cause first.
202  */
203 static ac_err ac_errors[] = {
204 	{							/* 0 */
205 		"UPA Port A Error",
206 		{ UPA_A_PART, 0, 0, 0, 0 },
207 	},
208 	{							/* 1 */
209 		"UPA Port B Error",
210 		{ UPA_B_PART, 0, 0, 0, 0 },
211 	},
212 	{							/* 2 */
213 		NULL,
214 		{ RSVD_PART, 0, 0, 0, 0 },
215 	},
216 	{							/* 3 */
217 		NULL,
218 		{ RSVD_PART, 0, 0, 0, 0 },
219 	},
220 	{							/* 4 */
221 		"UPA Interrupt to unmapped destination",
222 		{ BOARD_PART, 0, 0, 0, 0 },
223 	},
224 	{							/* 5 */
225 		"UPA Non-cacheable write to unmapped destination",
226 		{ BOARD_PART, 0, 0, 0, 0 },
227 	},
228 	{							/* 6 */
229 		"UPA Cacheable write to unmapped destination",
230 		{ BOARD_PART, 0, 0, 0, 0 },
231 	},
232 	{							/* 7 */
233 		"Illegal Write Received",
234 		{ BOARD_PART, 0, 0, 0, 0 },
235 	},
236 	{							/* 8 */
237 		"Local Writeback match with line in state S",
238 		{ AC_PART, DTAG_PART, 0, 0, 0 },
239 	},
240 	{							/* 9 */
241 		"Local Read match with valid line in Tags",
242 		{ AC_PART, DTAG_PART, 0, 0, 0 },
243 	},
244 	{							/* 10 */
245 		NULL,
246 		{ RSVD_PART, 0, 0, 0, 0 },
247 	},
248 	{							/* 11 */
249 		NULL,
250 		{ RSVD_PART, 0, 0, 0, 0 },
251 	},
252 	{							/* 12 */
253 		"Tag and Victim were valid during lookup",
254 		{ AC_PART, DTAG_PART, 0, 0, 0 },
255 	},
256 	{							/* 13 */
257 		"Local Writeback matches a victim in state S",
258 		{ AC_PART, CPU_PART, 0, 0, 0 },
259 	},
260 	{							/* 14 */
261 		"Local Read matches valid line in victim buffer",
262 		{ AC_PART, CPU_PART, 0, 0, 0 },
263 	},
264 	{							/* 15 */
265 		"Local Read victim bit set and victim is S state",
266 		{ AC_PART, CPU_PART, 0, 0, 0 },
267 	},
268 	{							/* 16 */
269 		"Local Read Victim bit set and Valid Victim Buffer",
270 		{ AC_PART, CPU_PART, 0, 0, 0 },
271 	},
272 	{							/* 17 */
273 		NULL,
274 		{ RSVD_PART, 0, 0, 0, 0 },
275 	},
276 	{							/* 18 */
277 		NULL,
278 		{ RSVD_PART, 0, 0, 0, 0 },
279 	},
280 	{							/* 19 */
281 		NULL,
282 		{ RSVD_PART, 0, 0, 0, 0 },
283 	},
284 	{							/* 20 */
285 		"UPA Transaction received in Sleep mode",
286 		{ AC_PART, 0, 0, 0, 0 },
287 	},
288 	{							/* 21 */
289 		"P_FERR error P_REPLY received from UPA Port",
290 		{ CPU_PART, AC_PART, 0, 0, 0 },
291 	},
292 	{							/* 22 */
293 		"Illegal P_REPLY received from UPA Port",
294 		{ CPU_PART, AC_PART, 0, 0, 0 },
295 	},
296 	{							/* 23 */
297 		NULL,
298 		{ RSVD_PART, 0, 0, 0, 0 },
299 	},
300 	{							/* 24 */
301 		"Timeout on a UPA Master Port",
302 		{ AC_ANY_PART, BOARD_ANY_PART, 0, 0, 0 },
303 	},
304 	{							/* 25 */
305 		NULL,
306 		{ RSVD_PART, 0, 0, 0, 0 },
307 	},
308 	{							/* 26 */
309 		NULL,
310 		{ RSVD_PART, 0, 0, 0, 0 },
311 	},
312 	{							/* 27 */
313 		NULL,
314 		{ RSVD_PART, 0, 0, 0, 0 },
315 	},
316 	{							/* 28 */
317 		"Coherent Transactions Queue Overflow Error",
318 		{ BACK_PIN_PART, BOARD_CONN_PART, AC_PART, AC_ANY_PART, 0 },
319 	},
320 	{							/* 29 */
321 		"Non-cacheable Request Queue Overflow Error",
322 		{ AC_PART, AC_ANY_PART, 0, 0, 0 },
323 	},
324 	{							/* 30 */
325 		"Non-cacheable Reply Queue Overflow Error",
326 		{ AC_PART, 0, 0, 0, 0 },
327 	},
328 	{							/* 31 */
329 		"PREQ Queue Overflow Error",
330 		{ CPU_PART, AC_PART, 0, 0, 0 },
331 	},
332 	{							/* 32 */
333 		"Foreign DID CAM Overflow Error",
334 		{ AC_PART, AC_ANY_PART, 0, 0, 0 },
335 	},
336 	{							/* 33 */
337 		"FT->UPA Queue Overflow Error",
338 		{ BACK_PIN_PART, BOARD_CONN_PART, AC_PART, AC_ANY_PART, 0 },
339 	},
340 	{							/* 34 */
341 		NULL,
342 		{ RSVD_PART, 0, 0, 0, 0 },
343 	},
344 	{							/* 35 */
345 		NULL,
346 		{ RSVD_PART, 0, 0, 0, 0 },
347 	},
348 	{							/* 36 */
349 		"UPA Port B Dtag Parity Error",
350 		{ DTAG_B_PART, AC_PART, 0, 0, 0 },
351 	},
352 	{							/* 37 */
353 		"UPA Port A Dtag Parity Error",
354 		{ DTAG_A_PART, AC_PART, 0, 0, 0 },
355 	},
356 	{							/* 38 */
357 		NULL,
358 		{ RSVD_PART, 0, 0, 0, 0 },
359 	},
360 	{							/* 39 */
361 		NULL,
362 		{ RSVD_PART, 0, 0, 0, 0 },
363 	},
364 	{							/* 40 */
365 		"UPA Bus Parity Error",
366 		{ UPA_PART, AC_PART, 0, 0, 0 },
367 	},
368 	{							/* 41 */
369 		"Data ID Line Mismatch",
370 		{ BACK_PIN_PART, BOARD_CONN_PART, AC_PART, 0, 0 },
371 	},
372 	{							/* 42 */
373 		"Arbitration Line Mismatch",
374 		{ BACK_PIN_PART, BOARD_CONN_PART, AC_PART, 0, 0 },
375 	},
376 	{							/* 43 */
377 		"Shared Line Parity Mismatch",
378 		{ BACK_PIN_PART, BOARD_CONN_PART, AC_PART, 0, 0 },
379 	},
380 	{							/* 44 */
381 		"FireTruck Control Line Parity Error",
382 		{ AC_PART, BACK_PIN_PART, 0, 0, 0 },
383 	},
384 	{							/* 45 */
385 		"FireTruck Address Bus Parity Error",
386 		{ AC_PART, BACK_PIN_PART, 0, 0, 0 },
387 	},
388 	{							/* 46 */
389 		"Internal RAM Parity Error",
390 		{ AC_PART, 0, 0, 0, 0 },
391 	},
392 	{							/* 47 */
393 		NULL,
394 		{ RSVD_PART, 0, 0, 0, 0 },
395 	},
396 	{							/* 48 */
397 		"Internal Hardware Error",
398 		{ AC_PART, 0, 0, 0, 0 },
399 	},
400 	{							/* 49 */
401 		"FHC Communications Error",
402 		{ FHC_PART, AC_PART, 0, 0, 0 },
403 	},
404 	/* Bits 50-63 are reserved in this implementation. */
405 };
406 
407 
408 #define	MAX_BITS (sizeof (ac_errors)/ sizeof (ac_err))
409 
410 /*
411  * There are only two error bits in the DC shadow chain that are
412  * important. They indicate an overflow error and a parity error,
413  * respectively. The other bits are not error bits and should not
414  * be checked for.
415  */
416 #define	DC_OVERFLOW	0x2
417 #define	DC_PARITY	0x4
418 
419 static char dc_overflow_txt[] = "Board %d DC %d Overflow Error";
420 static char dc_parity_txt[] = "Board %d DC %d Parity Error";
421 
422 /* defines for the sysio */
423 #define	UPA_APERR	0x4
424 
425 int
426 error_check(Sys_tree *tree, struct system_kstat_data *kstats)
427 {
428 	int exit_code = 0;	/* init to all OK */
429 
430 	/*
431 	 * silently check for any types of machine errors
432 	 */
433 	print_flag = 0;
434 	if (disp_fail_parts(tree) || disp_fault_list(tree, kstats) ||
435 		disp_err_log(kstats) || disp_env_status(kstats)) {
436 		/* set exit_code to show failures */
437 		exit_code = 1;
438 	}
439 	print_flag = 1;
440 
441 	return (exit_code);
442 }
443 
444 /*
445  * disp_fail_parts
446  *
447  * Display the failed parts in the system. This function looks for
448  * the status property in all PROM nodes. On systems where
449  * the PROM does not supports passing diagnostic information
450  * thruogh the device tree, this routine will be silent.
451  */
452 int
453 disp_fail_parts(Sys_tree *tree)
454 {
455 	int exit_code;
456 	int system_failed = 0;
457 	Board_node *bnode = tree->bd_list;
458 	Prom_node *pnode;
459 
460 	exit_code = 0;
461 
462 	/* go through all of the boards looking for failed units. */
463 	while (bnode != NULL) {
464 		/* find failed chips */
465 		pnode = find_failed_node(bnode->nodes);
466 		if ((pnode != NULL) && !system_failed) {
467 			system_failed = 1;
468 			exit_code = 1;
469 			if (print_flag == 0) {
470 				return (exit_code);
471 			}
472 			log_printf("\n", 0);
473 			log_printf(dgettext(TEXT_DOMAIN,
474 				"Failed Field Replaceable Units (FRU) "
475 				"in System:\n"), 0);
476 			log_printf("=========================="
477 				"====================\n", 0);
478 		}
479 
480 		while (pnode != NULL) {
481 			void *value;
482 			char *name;		/* node name string */
483 			char *type;		/* node type string */
484 			char *board_type = NULL;
485 
486 			value = get_prop_val(find_prop(pnode, "status"));
487 			name = get_node_name(pnode);
488 
489 			/* sanity check of data retreived from PROM */
490 			if ((value == NULL) || (name == NULL)) {
491 				pnode = next_failed_node(pnode);
492 				continue;
493 			}
494 
495 			/* Find the board type of this board */
496 			if (bnode->board_type == CPU_BOARD) {
497 				board_type = "CPU";
498 			} else {
499 				board_type = "IO";
500 			}
501 
502 			log_printf(dgettext(TEXT_DOMAIN,
503 				"%s unavailable on %s Board #%d\n"),
504 				name, board_type, bnode->board_num, 0);
505 
506 			log_printf(dgettext(TEXT_DOMAIN,
507 				"\tPROM fault string: %s\n"), value, 0);
508 
509 			log_printf(dgettext(TEXT_DOMAIN,
510 				"\tFailed Field Replaceable Unit is "), 0);
511 
512 			/*
513 			 * Determine whether FRU is CPU module, system
514 			 * board, or SBus card.
515 			 */
516 			if ((name != NULL) && (strstr(name, "sbus"))) {
517 
518 				log_printf(dgettext(TEXT_DOMAIN,
519 					"SBus Card %d\n"),
520 					get_sbus_slot(pnode), 0);
521 
522 			} else if (((name = get_node_name(pnode->parent)) !=
523 			    NULL) && (strstr(name, "pci"))) {
524 
525 				log_printf(dgettext(TEXT_DOMAIN,
526 					"PCI Card %d"),
527 					get_pci_device(pnode), 0);
528 
529 			} else if (((type = get_node_type(pnode)) != NULL) &&
530 			    (strstr(type, "cpu"))) {
531 
532 				log_printf(dgettext(TEXT_DOMAIN,
533 					"UltraSPARC module "
534 					"Board %d Module %d\n"),
535 						get_id(pnode) >> 1,
536 						get_id(pnode) & 0x1);
537 
538 			} else {
539 				log_printf(dgettext(TEXT_DOMAIN,
540 					"%s board %d\n"), board_type,
541 					bnode->board_num, 0);
542 			}
543 			pnode = next_failed_node(pnode);
544 		}
545 		bnode = bnode->next;
546 	}
547 
548 	if (!system_failed) {
549 		log_printf("\n", 0);
550 		log_printf(dgettext(TEXT_DOMAIN,
551 			"No failures found in System\n"), 0);
552 		log_printf("===========================\n", 0);
553 	}
554 
555 	if (system_failed)
556 		return (1);
557 	else
558 		return (0);
559 }
560 
561 void
562 display_memorysize(Sys_tree *tree, struct system_kstat_data *kstats,
563 	struct grp_info *grps, struct mem_total *memory_total) {
564 
565 	/* Build the memory group tables and interleave data */
566 	build_mem_tables(tree, kstats, grps);
567 
568 	/* display total usable installed memory */
569 	get_mem_total(memory_total, grps);
570 	(void) log_printf(dgettext(TEXT_DOMAIN,
571 		"Memory size: %4dMb\n"), memory_total->dram, 0);
572 
573 	/* We display the NVSIMM size totals separately. */
574 	if (memory_total->nvsimm != 0) {
575 		(void) log_printf(dgettext(TEXT_DOMAIN,
576 			"NVSIMM size: %4dMb\n"), memory_total->nvsimm);
577 	}
578 }
579 
580 /*
581  * This routine displays the memory configuration for all boards in the
582  * system.
583  */
584 void
585 display_memoryconf(Sys_tree *tree, struct grp_info *grps)
586 {
587 	int group;
588 	char *status_str[] = {  "Unknown", " Empty ", " Failed", " Active",
589 				" Spare " };
590 	char *cond_str[] = {    " Unknown  ", "    OK    ", " Failing  ",
591 				"  Failed  ", " Uninit.  " };
592 
593 #ifdef lint
594 	tree = tree;
595 #endif
596 	/* Print the header for the memory section. */
597 	log_printf("\n", 0);
598 	log_printf("=========================", 0);
599 	log_printf(dgettext(TEXT_DOMAIN, " Memory "), 0);
600 	log_printf("=========================", 0);
601 	log_printf("\n", 0);
602 	log_printf("\n", 0);
603 	log_printf("                                              Intrlv.  "
604 		"Intrlv.\n", 0);
605 	log_printf("Brd   Bank   MB    Status   Condition  Speed   Factor  "
606 		" With\n", 0);
607 	log_printf("---  -----  ----  -------  ----------  -----  -------  "
608 		"-------\n", 0);
609 
610 	/* Print the Memory groups information. */
611 	for (group = 0; group < MAX_GROUPS; group++) {
612 		struct grp *grp;
613 
614 		grp = &grps->grp[group];
615 
616 		/* If this board is not a CPU or MEM board, skip it. */
617 		if ((grp->type != MEM_BOARD) && (grp->type != CPU_BOARD)) {
618 			continue;
619 		}
620 
621 		if (grp->valid) {
622 			log_printf("%2d   ", grp->board, 0);
623 			log_printf("  %1d    ", grp->group, 0);
624 			log_printf("%4d  ", grp->size, 0);
625 			log_printf("%7s  ", status_str[grp->status], 0);
626 			log_printf("%10s  ", cond_str[grp->condition], 0);
627 			log_printf("%3dns  ", grp->speed, 0);
628 			log_printf("%3d-way  ", grp->factor, 0);
629 			if (grp->factor > 1) {
630 				log_printf("%4c", grp->groupid, 0);
631 			}
632 			log_printf("\n", 0);
633 		}
634 	}
635 
636 }
637 
638 
639 void
640 display_hp_fail_fault(Sys_tree *tree, struct system_kstat_data *kstats)
641 {
642 	/* Display Hot plugged, disabled and failed boards */
643 	(void) display_hp_boards(kstats);
644 
645 	/* Display failed units */
646 	(void) disp_fail_parts(tree);
647 
648 	/* Display fault info */
649 	(void) disp_fault_list(tree, kstats);
650 }
651 
652 void
653 display_diaginfo(int flag, Prom_node *root, Sys_tree *tree,
654 	struct system_kstat_data *kstats)
655 {
656 	/*
657 	 * Now display the last powerfail time and the fatal hardware
658 	 * reset information. We do this under a couple of conditions.
659 	 * First if the user asks for it. The second is iof the user
660 	 * told us to do logging, and we found a system failure.
661 	 */
662 	if (flag) {
663 		/*
664 		 * display time of latest powerfail. Not all systems
665 		 * have this capability. For those that do not, this
666 		 * is just a no-op.
667 		 */
668 		disp_powerfail(root);
669 
670 		/* Display system environmental conditions. */
671 		(void) disp_env_status(kstats);
672 
673 		/* Display ASIC Chip revs for all boards. */
674 		sunfire_disp_asic_revs(tree, kstats);
675 
676 		/* Print the PROM revisions here */
677 		sunfire_disp_prom_versions(tree);
678 
679 		/*
680 		 * Display the latest system fatal hardware
681 		 * error data, if any. The system holds this
682 		 * data in SRAM, so it does not persist
683 		 * across power-on resets.
684 		 */
685 		(void) disp_err_log(kstats);
686 	}
687 }
688 
689 void
690 display_mid(int mid)
691 {
692 	log_printf("  %2d     ", mid % 2, 0);
693 }
694 
695 /*
696  * display_pci
697  * Call the generic psycho version of this function.
698  */
699 void
700 display_pci(Board_node *board)
701 {
702 	display_psycho_pci(board);
703 }
704 
705 /*
706  * display_ffb
707  * Display all FFBs on this board.  It can either be in tabular format,
708  * or a more verbose format.
709  */
710 void
711 display_ffb(Board_node *board, int table)
712 {
713 	Prom_node *ffb;
714 	void *value;
715 	struct io_card *card_list = NULL;
716 	struct io_card card;
717 
718 	if (board == NULL)
719 		return;
720 
721 	/* Fill in common information */
722 	card.display = 1;
723 	card.board = board->board_num;
724 	(void) sprintf(card.bus_type, "UPA");
725 	card.freq = sys_clk;
726 
727 	for (ffb = dev_find_node(board->nodes, FFB_NAME); ffb != NULL;
728 	    ffb = dev_next_node(ffb, FFB_NAME)) {
729 		if (table == 1) {
730 			/* Print out in table format */
731 
732 			/* XXX - Get the slot number (hack) */
733 			card.slot = get_id(ffb);
734 
735 			/* Find out if it's single or double buffered */
736 			(void) sprintf(card.name, "FFB");
737 			value = get_prop_val(find_prop(ffb, "board_type"));
738 			if (value != NULL)
739 				if ((*(int *)value) & FFB_B_BUFF)
740 					(void) sprintf(card.name, "FFB, "
741 						"Double Buffered");
742 				else
743 					(void) sprintf(card.name, "FFB, "
744 						"Single Buffered");
745 
746 			/* Print model number */
747 			card.model[0] = '\0';
748 			value = get_prop_val(find_prop(ffb, "model"));
749 			if (value != NULL)
750 				(void) sprintf(card.model, "%s",
751 					(char *)value);
752 
753 			card_list = insert_io_card(card_list, &card);
754 		} else {
755 			/* print in long format */
756 			char device[MAXSTRLEN];
757 			int fd = -1;
758 			struct dirent *direntp;
759 			DIR *dirp;
760 			union strap_un strap;
761 			struct ffb_sys_info fsi;
762 
763 			/* Find the device node using upa address */
764 			value = get_prop_val(find_prop(ffb, "upa-portid"));
765 			if (value == NULL)
766 			    continue;
767 
768 			(void) sprintf(device, "%s@%x", FFB_NAME,
769 				*(int *)value);
770 			if ((dirp = opendir("/devices")) == NULL)
771 				continue;
772 
773 			while ((direntp = readdir(dirp)) != NULL) {
774 				if (strstr(direntp->d_name, device) != NULL) {
775 					(void) sprintf(device, "/devices/%s",
776 						direntp->d_name);
777 					fd = open(device, O_RDWR, 0666);
778 					break;
779 				}
780 			}
781 			(void) closedir(dirp);
782 
783 			if (fd == -1)
784 				continue;
785 
786 			if (ioctl(fd, FFB_SYS_INFO, &fsi) < 0)
787 				continue;
788 
789 			log_printf("Board %d FFB Hardware Configuration:\n",
790 				board->board_num, 0);
791 			log_printf("-----------------------------------\n", 0);
792 
793 			strap.ffb_strap_bits = fsi.ffb_strap_bits;
794 			log_printf("\tBoard rev: %d\n",
795 				(int)strap.fld.board_rev, 0);
796 			log_printf("\tFBC version: 0x%x\n", fsi.fbc_version, 0);
797 			log_printf("\tDAC: %s\n",
798 				fmt_manf_id(fsi.dac_version, device), 0);
799 			log_printf("\t3DRAM: %s\n",
800 				fmt_manf_id(fsi.fbram_version, device), 0);
801 			log_printf("\n", 0);
802 		}
803 	}
804 
805 	display_io_cards(card_list);
806 	free_io_cards(card_list);
807 }
808 
809 /*
810  * add_node
811  *
812  * This function adds a board node to the board structure where that
813  * that node's physical component lives.
814  */
815 void
816 add_node(Sys_tree *root, Prom_node *pnode)
817 {
818 	int board;
819 	Board_node *bnode;
820 	char *name = get_node_name(pnode);
821 	Prom_node *p;
822 
823 	/* add this node to the Board list of the appropriate board */
824 	if ((board = get_board_num(pnode)) == -1) {
825 		void *value;
826 
827 		/*
828 		 * if it is a server, pci nodes and ffb nodes never have
829 		 * board number properties and software can find the board
830 		 * number from the reg property. It is derived from the
831 		 * high word of the 'reg' property, which contains the
832 		 * mid.
833 		 */
834 		if ((name != NULL) &&
835 		    ((strcmp(name, FFB_NAME) == 0) ||
836 		    (strcmp(name, "pci") == 0) ||
837 		    (strcmp(name, "counter-timer") == 0))) {
838 			/* extract the board number from the 'reg' prop. */
839 			if ((value = get_prop_val(find_prop(pnode,
840 			    "reg"))) == NULL) {
841 				(void) printf("add_node() no reg property\n");
842 				exit(2);
843 			}
844 			board = (*(int *)value - 0x1c0) / 4;
845 		}
846 	}
847 
848 	/* find the node with the same board number */
849 	if ((bnode = find_board(root, board)) == NULL) {
850 		bnode = insert_board(root, board);
851 		bnode->board_type = UNKNOWN_BOARD;
852 	}
853 
854 	/* now attach this prom node to the board list */
855 	/* Insert this node at the end of the list */
856 	pnode->sibling = NULL;
857 	if (bnode->nodes == NULL)
858 		bnode->nodes = pnode;
859 	else {
860 		p = bnode->nodes;
861 		while (p->sibling != NULL)
862 			p = p->sibling;
863 		p->sibling = pnode;
864 	}
865 
866 }
867 
868 /*
869  * Function resolve_board_types
870  *
871  * After the tree is walked and all the information is gathered, this
872  * function is called to resolve the type of each board.
873  */
874 void
875 resolve_board_types(Sys_tree *tree)
876 {
877 	Board_node *bnode;
878 	Prom_node *pnode;
879 	char *type;
880 
881 	bnode = tree->bd_list;
882 	while (bnode != NULL) {
883 		bnode->board_type = UNKNOWN_BOARD;
884 
885 		pnode = dev_find_node(bnode->nodes, "fhc");
886 		type = get_prop_val(find_prop(pnode, "board-type"));
887 		if (type == NULL) {
888 			bnode = bnode->next;
889 			continue;
890 		}
891 
892 		if (strcmp(type, CPU_BD_NAME) == 0) {
893 			bnode->board_type = CPU_BOARD;
894 		} else if (strcmp(type, MEM_BD_NAME) == 0) {
895 			bnode->board_type = MEM_BOARD;
896 		} else if (strcmp(type, DISK_BD_NAME) == 0) {
897 			bnode->board_type = DISK_BOARD;
898 		} else if (strcmp(type, IO_SBUS_FFB_BD_NAME) == 0) {
899 			bnode->board_type = IO_SBUS_FFB_BOARD;
900 		} else if (strcmp(type, IO_2SBUS_BD_NAME) == 0) {
901 			bnode->board_type = IO_2SBUS_BOARD;
902 		} else if (strcmp(type, IO_PCI_BD_NAME) == 0) {
903 			bnode->board_type = IO_PCI_BOARD;
904 		} else if (strcmp(type, IO_2SBUS_SOCPLUS_BD_NAME) == 0) {
905 			bnode->board_type = IO_2SBUS_SOCPLUS_BOARD;
906 		} else if (strcmp(type, IO_SBUS_FFB_SOCPLUS_BD_NAME) == 0) {
907 			bnode->board_type = IO_SBUS_FFB_SOCPLUS_BOARD;
908 		}
909 
910 		bnode = bnode->next;
911 	}
912 
913 }
914 
915 /*
916  * local functions
917  */
918 
919 static void
920 sunfire_disp_prom_versions(Sys_tree *tree)
921 {
922 	Board_node *bnode;
923 
924 	/* Display Prom revision header */
925 	log_printf("System Board PROM revisions:\n", 0);
926 	log_printf("----------------------------\n", 0);
927 
928 	/* For each board, print the POST and OBP versions */
929 	for (bnode = tree->bd_list; bnode != NULL; bnode = bnode->next) {
930 		Prom_node *flashprom;   /* flashprom device node */
931 
932 		/* find a flashprom node for this board */
933 		flashprom = dev_find_node(bnode->nodes, "flashprom");
934 
935 		/* If no flashprom node found, continue */
936 		if (flashprom == NULL)
937 			continue;
938 
939 		/* flashprom node found, display board# */
940 		log_printf("Board %2d: ", bnode->board_num, 0);
941 
942 		disp_prom_version(flashprom);
943 	}
944 }
945 
946 
947 /*
948  * functions that are only needed inside this library
949  */
950 
951 /*
952  * build_mem_tables
953  *
954  * This routine builds the memory table which tells how much memory
955  * is present in each SIMM group of each board, what the interleave
956  * factors are, and the group ID of the interleave group.
957  *
958  * The algorithms used are:
959  *	First fill in the sizes of groups.
960  *	Next build lists of all groups with same physical base.
961  *	From #of members in each list, interleave factor is
962  *	determined.
963  *	All members of a certain list get the same interleave
964  *	group ID.
965  */
966 static void
967 build_mem_tables(Sys_tree *tree,
968 		struct system_kstat_data *kstats,
969 		struct grp_info *grps)
970 {
971 	struct mem_inter inter_grps;	/* temp structure for interleaves */
972 	struct inter_grp *intrp;
973 	int group;
974 	int i;
975 
976 	/* initialize the interleave lists */
977 	for (i = 0, intrp = &inter_grps.i_grp[0]; i < MAX_GROUPS; i++,
978 	    intrp++) {
979 		intrp->valid = 0;
980 		intrp->count = 0;
981 		intrp->groupid = '\0';
982 		intrp->base = 0;
983 	}
984 
985 	for (group = 0; group < MAX_GROUPS; group++) {
986 		int found;
987 		int board;
988 		struct grp *grp;
989 		struct bd_kstat_data *bksp;
990 		uchar_t simm_reg;
991 		Board_node *bnode;
992 
993 		board = group/2;
994 		bksp = &kstats->bd_ksp_list[board];
995 		grp = &grps->grp[group];
996 		grp->group = group % 2;
997 
998 		/*
999 		 * Copy the board type field into the group record.
1000 		 */
1001 		if ((bnode = find_board(tree, board)) != NULL) {
1002 			grp->type = bnode->board_type;
1003 		} else {
1004 			grp->type = UNKNOWN_BOARD;
1005 			continue;
1006 		}
1007 
1008 		/* Make sure we have kstats for this board */
1009 		if (bksp->ac_kstats_ok == 0) {
1010 			/* Mark this group as invalid and move to next one */
1011 			grp->valid = 0;
1012 			continue;
1013 		}
1014 
1015 		/* Find the bank status property */
1016 		if (bksp->ac_memstat_ok) {
1017 			grp->status = bksp->mem_stat[grp->group].status;
1018 			grp->condition = bksp->mem_stat[grp->group].condition;
1019 		} else {
1020 			grp->status = StUnknown;
1021 			grp->condition = ConUnknown;
1022 		}
1023 
1024 		switch (grp->status) {
1025 		case StBad:
1026 		case StActive:
1027 		case StSpare:
1028 			break;
1029 		default:
1030 			grp->status = StUnknown;
1031 			break;
1032 		}
1033 
1034 		switch (grp->condition) {
1035 		case ConOK:
1036 		case ConFailing:
1037 		case ConFailed:
1038 		case ConTest:
1039 		case ConBad:
1040 			break;
1041 		default:
1042 			grp->condition = ConUnknown;
1043 			break;
1044 		}
1045 
1046 		/* base the group size off of the simmstat kstat. */
1047 		if (bksp->simmstat_kstats_ok == 0) {
1048 			grp->valid = 0;
1049 			continue;
1050 		}
1051 
1052 		/* Is it bank 0 or bank 1 */
1053 		if (grp->group == 0) {
1054 			simm_reg = bksp->simm_status[0];
1055 		} else {
1056 			simm_reg = bksp->simm_status[1];
1057 		}
1058 
1059 		/* Now decode the size field. */
1060 		switch (simm_reg & 0x1f) {
1061 		case MEM_SIZE_64M:
1062 			grp->size = 64;
1063 			break;
1064 		case MEM_SIZE_256M:
1065 			grp->size = 256;
1066 			break;
1067 		case MEM_SIZE_1G:
1068 			grp->size = 1024;
1069 			break;
1070 		case MEM_SIZE_2G:
1071 			grp->size = 2048;
1072 			break;
1073 		default:
1074 			grp->valid = 0;
1075 			continue;
1076 		}
1077 
1078 		/* Decode the speed field */
1079 		switch ((simm_reg & 0x60) >> 5) {
1080 		case MEM_SPEED_50ns:
1081 			grp->speed = 50;
1082 			break;
1083 		case MEM_SPEED_60ns:
1084 			grp->speed = 60;
1085 			break;
1086 		case MEM_SPEED_70ns:
1087 			grp->speed = 70;
1088 			break;
1089 		case MEM_SPEED_80ns:
1090 			grp->speed = 80;
1091 			break;
1092 		}
1093 
1094 		grp->valid = 1;
1095 		grp->base = GRP_BASE(bksp->ac_memdecode[grp->group]);
1096 		grp->board = board;
1097 		if (grp->group == 0) {
1098 			grp->factor = INTLV0(bksp->ac_memctl);
1099 		} else {	/* assume it is group 1 */
1100 			grp->factor = INTLV1(bksp->ac_memctl);
1101 		}
1102 		grp->groupid = '\0';	/* Not in a group yet */
1103 
1104 		/*
1105 		 * find the interleave list this group belongs on. If the
1106 		 * interleave list corresponding to this base address is
1107 		 * not found, then create a new one.
1108 		 */
1109 
1110 		i = 0;
1111 		intrp = &inter_grps.i_grp[0];
1112 		found = 0;
1113 		while ((i < MAX_GROUPS) && !found && (intrp->valid != 0)) {
1114 			if ((intrp->valid != 0) &&
1115 			    (intrp->base == grp->base)) {
1116 				grp->groupid = intrp->groupid;
1117 				intrp->count++;
1118 				found = 1;
1119 			}
1120 			i++;
1121 			intrp++;
1122 		}
1123 		/*
1124 		 * We did not find a matching base. So now i and intrp
1125 		 * now point to the next interleave group in the list.
1126 		 */
1127 		if (!found) {
1128 			intrp->count++;
1129 			intrp->valid = 1;
1130 			intrp->groupid = 'A' + (char)i;
1131 			intrp->base = grp->base;
1132 			grp->groupid = intrp->groupid;
1133 		}
1134 	}
1135 }
1136 
1137 
1138 static void
1139 get_mem_total(struct mem_total *mem_total, struct grp_info *grps)
1140 {
1141 	struct grp *grp;
1142 	int i;
1143 
1144 	/* Start with total of zero */
1145 	mem_total->dram = 0;
1146 	mem_total->nvsimm = 0;
1147 
1148 	/* For now we ignore NVSIMMs. We might want to fix this later. */
1149 	for (i = 0, grp = &grps->grp[0]; i < MAX_GROUPS; i++, grp++) {
1150 		if (grp->valid == 1 && grp->status == StActive) {
1151 			mem_total->dram += grp->size;
1152 		}
1153 	}
1154 }
1155 
1156 static int
1157 disp_fault_list(Sys_tree *tree, struct system_kstat_data *kstats)
1158 {
1159 	struct ft_list *ftp;
1160 	int i;
1161 	int result = 0;
1162 	time_t t;
1163 
1164 	if (!kstats->ft_kstat_ok) {
1165 		return (result);
1166 	}
1167 
1168 	for (i = 0, ftp = kstats->ft_array; i < kstats->nfaults; i++, ftp++) {
1169 		if (!result) {
1170 			log_printf("\n", 0);
1171 			log_printf("Detected System Faults\n", 0);
1172 			log_printf("======================\n", 0);
1173 		}
1174 		result = 1;
1175 		if (ftp->fclass == FT_BOARD) {
1176 			log_printf("Board %d fault: %s\n", ftp->unit,
1177 				ftp->msg, 0);
1178 
1179 			/*
1180 			 * If the fault on this board is PROM inherited, see
1181 			 * if we can find some failed component information
1182 			 * in the PROM device tree. The general solution
1183 			 * would be to fix the fhc driver and have it put in
1184 			 * more descriptive messages, but that's for another
1185 			 * day.
1186 			 */
1187 
1188 			if (ftp->type == FT_PROM) {
1189 				Board_node *bn;
1190 				Prom_node *pn;
1191 				char *str;
1192 
1193 				bn = find_board(tree, ftp->unit);
1194 				/*
1195 				 * If any nodes under this board have a
1196 				 * status containing "fail", print it out.
1197 				 */
1198 				pn = find_failed_node(bn->nodes);
1199 				while (pn) {
1200 					str = get_prop_val(find_prop(pn,
1201 						"status"));
1202 					if (str != NULL) {
1203 						log_printf("Fault: %s\n", str,
1204 							0);
1205 					}
1206 
1207 					pn = next_failed_node(pn);
1208 				}
1209 			}
1210 		} else if ((ftp->type == FT_CORE_PS) || (ftp->type == FT_PPS)) {
1211 			log_printf("Unit %d %s failure\n", ftp->unit,
1212 				ftp->msg, 0);
1213 		} else if ((ftp->type == FT_OVERTEMP) &&
1214 		    (ftp->fclass == FT_SYSTEM)) {
1215 			log_printf("Clock board %s\n", ftp->msg, 0);
1216 		} else {
1217 			log_printf("%s failure\n", ftp->msg, 0);
1218 		}
1219 
1220 		t = (time_t)ftp->create_time;
1221 		log_printf("\tDetected %s",
1222 			asctime(localtime(&t)), 0);
1223 	}
1224 
1225 	if (!result) {
1226 		log_printf("\n", 0);
1227 		log_printf("No System Faults found\n", 0);
1228 		log_printf("======================\n", 0);
1229 	}
1230 
1231 	log_printf("\n", 0);
1232 
1233 	return (result);
1234 }
1235 
1236 
1237 /*
1238  * disp_err_log
1239  *
1240  * Display the fatal hardware reset system error logs. These logs are
1241  * collected by POST and passed up through the kernel to userland.
1242  * They will not necessarily be present in all systems. Their form
1243  * might also be different in different systems.
1244  *
1245  * NOTE - We are comparing POST defined board types here. Do not confuse
1246  * them with kernel board types. The structure being analyzed in this
1247  * function is created by POST. All the defines for it are in reset_info.h,
1248  * which was ported from POST header files.
1249  */
1250 static int
1251 disp_err_log(struct system_kstat_data *kstats)
1252 {
1253 	int exit_code = 0;
1254 	int i;
1255 	struct reset_info *rst_info;
1256 	struct board_info *bdp;
1257 	char *err_msgs[MAX_MSGS]; /* holds all messages for a system board */
1258 	int msg_idx;		/* current msg number */
1259 	int count;		/* number added by last analyze call */
1260 	char **msgs;
1261 
1262 	/* start by initializing the err_msgs array to all NULLs */
1263 	for (i = 0; i < MAX_MSGS; i++) {
1264 		err_msgs[i] = NULL;
1265 	}
1266 
1267 	/* First check to see that the reset-info kstats are present. */
1268 	if (kstats->reset_kstats_ok == 0) {
1269 		return (exit_code);
1270 	}
1271 
1272 	rst_info = &kstats->reset_info;
1273 
1274 	/* Everything is OK, so print out time/date stamp first */
1275 	log_printf("\n", 0);
1276 	log_printf(
1277 		dgettext(TEXT_DOMAIN,
1278 			"Analysis of most recent Fatal Hardware Watchdog:\n"),
1279 			0);
1280 	log_printf("======================================================\n",
1281 		0);
1282 	log_printf("Log Date: %s\n",
1283 		get_time(&kstats->reset_info.tod_timestamp[0]), 0);
1284 
1285 	/* initialize the vector and the message index. */
1286 	msgs = err_msgs;
1287 	msg_idx = 0;
1288 
1289 	/* Loop Through all of the boards. */
1290 	bdp = &rst_info->bd_reset_info[0];
1291 	for (i = 0; i < MAX_BOARDS; i++, bdp++) {
1292 
1293 		/* Is there data for this board? */
1294 		if ((bdp->board_desc & BD_STATE_MASK) == BD_NOT_PRESENT) {
1295 			continue;
1296 		}
1297 
1298 		/* If it is a CPU Board, look for CPU data. */
1299 		if (BOARD_TYPE(bdp->board_desc) == CPU_TYPE) {
1300 			/* analyze CPU 0 if present */
1301 			if (bdp->board_desc & CPU0_OK) {
1302 				count = analyze_cpu(msgs, 0,
1303 					bdp->cpu[0].afsr);
1304 				msgs += count;
1305 				msg_idx += count;
1306 			}
1307 
1308 			/* analyze CPU1 if present. */
1309 			if (bdp->board_desc & CPU1_OK) {
1310 				count = analyze_cpu(msgs, 1,
1311 					bdp->cpu[1].afsr);
1312 				msgs += count;
1313 				msg_idx += count;
1314 			}
1315 		}
1316 
1317 		/* Always Analyze the AC and the DCs on a board. */
1318 		count = analyze_ac(msgs, bdp->ac_error_status);
1319 		msgs += count;
1320 		msg_idx += count;
1321 
1322 		count = analyze_dc(i, msgs, bdp->dc_shadow_chain);
1323 		msgs += count;
1324 		msg_idx += count;
1325 
1326 		if (msg_idx != 0)
1327 			display_msgs(err_msgs, i);
1328 
1329 		erase_msgs(err_msgs);
1330 
1331 		/* If any messages are logged, we have errors */
1332 		if (msg_idx != 0) {
1333 			exit_code = 1;
1334 		}
1335 
1336 		/* reset the vector and the message index */
1337 		msg_idx = 0;
1338 		msgs = &err_msgs[0];
1339 	}
1340 
1341 	return (exit_code);
1342 }
1343 
1344 static void
1345 erase_msgs(char **msgs)
1346 {
1347 	int i;
1348 
1349 	for (i = 0; (*msgs != NULL) && (i < MAX_MSGS); i++, msgs++) {
1350 		free(*msgs);
1351 		*msgs = NULL;
1352 	}
1353 }
1354 
1355 
1356 static void
1357 display_msgs(char **msgs, int board)
1358 {
1359 	int i;
1360 
1361 	/* display the header for this board */
1362 	print_header(board);
1363 
1364 	for (i = 0; (*msgs != NULL) && (i < MAX_MSGS); i++, msgs++) {
1365 		log_printf(*msgs, 0);
1366 	}
1367 }
1368 
1369 
1370 
1371 /*
1372  * disp_keysw_and_leds
1373  *
1374  * This routine displays the position of the keyswitch and the front panel
1375  * system LEDs. The keyswitch can be in either normal, diagnostic, or
1376  * secure position. The three front panel LEDs are of importance because
1377  * the center LED indicates component failure on the system.
1378  */
1379 static int
1380 disp_keysw_and_leds(struct system_kstat_data *kstats)
1381 {
1382 	int board;
1383 	int diag_mode = 0;
1384 	int secure_mode = 0;
1385 	int result = 0;
1386 
1387 	/* Check the first valid board to determeine the diag bit */
1388 	/* Find the first valid board */
1389 	for (board = 0; board < MAX_BOARDS; board++) {
1390 		if (kstats->bd_ksp_list[board].fhc_kstats_ok != 0) {
1391 			/* If this was successful, break out of loop */
1392 			if ((kstats->bd_ksp_list[board].fhc_bsr &
1393 			    FHC_DIAG_MODE) == 0)
1394 				diag_mode = 1;
1395 			break;
1396 		}
1397 	}
1398 
1399 	/*
1400 	 * Check the register on the clock-board to determine the
1401 	 * secure bit.
1402 	 */
1403 	if (kstats->sys_kstats_ok) {
1404 		/* The secure bit is negative logic. */
1405 		if (kstats->keysw_status == KEY_SECURE) {
1406 			secure_mode = 1;
1407 		}
1408 	}
1409 
1410 	/*
1411 	 * The system cannot be in diag and secure mode. This is
1412 	 * illegal.
1413 	 */
1414 	if (secure_mode && diag_mode) {
1415 		result = 2;
1416 		return (result);
1417 	}
1418 
1419 	/* Now print the keyswitch position. */
1420 	log_printf("Keyswitch position is in ", 0);
1421 
1422 	if (diag_mode) {
1423 		log_printf("Diagnostic Mode\n");
1424 	} else if (secure_mode) {
1425 		log_printf("Secure Mode\n", 0);
1426 	} else {
1427 		log_printf("Normal Mode\n");
1428 	}
1429 
1430 	/* display the redundant power status */
1431 	if (kstats->sys_kstats_ok) {
1432 		log_printf("System Power Status: ", 0);
1433 
1434 		switch (kstats->power_state) {
1435 		case REDUNDANT:
1436 			log_printf("Redundant\n", 0);
1437 			break;
1438 
1439 		case MINIMUM:
1440 			log_printf("Minimum Available\n", 0);
1441 			break;
1442 
1443 		case BELOW_MINIMUM:
1444 			log_printf("Insufficient Power Available\n", 0);
1445 			break;
1446 
1447 		default:
1448 			log_printf("Unknown\n", 0);
1449 			break;
1450 		}
1451 	}
1452 
1453 	if (kstats->sys_kstats_ok) {
1454 		/*
1455 		 * If the center LED is on, then we return a non-zero
1456 		 * result.
1457 		 */
1458 		log_printf("System LED Status:    GREEN     YELLOW     "
1459 			"GREEN\n", 0);
1460 		if ((kstats->sysctrl & SYS_LED_MID) != 0) {
1461 			log_printf("WARNING                ", 0);
1462 		} else {
1463 			log_printf("Normal                 ", 0);
1464 		}
1465 
1466 		/*
1467 		 * Left LED is negative logic, center and right LEDs
1468 		 * are positive logic.
1469 		 */
1470 		if ((kstats->sysctrl & SYS_LED_LEFT) == 0) {
1471 			log_printf("ON ", 0);
1472 		} else {
1473 			log_printf("OFF", 0);
1474 		}
1475 
1476 		log_printf("       ", 0);
1477 		if ((kstats->sysctrl & SYS_LED_MID) != 0) {
1478 			log_printf("ON ", 0);
1479 		} else {
1480 			log_printf("OFF", 0);
1481 		}
1482 
1483 		log_printf("       BLINKING", 0);
1484 	}
1485 
1486 	log_printf("\n", 0);
1487 	return (result);
1488 }
1489 
1490 /*
1491  * disp_env_status
1492  *
1493  * This routine displays the environmental status passed up from
1494  * device drivers via kstats. The kstat names are defined in
1495  * kernel header files included by this module.
1496  */
1497 static int
1498 disp_env_status(struct system_kstat_data *kstats)
1499 {
1500 	struct bd_kstat_data *bksp;
1501 	int exit_code = 0;
1502 	int i;
1503 	uchar_t curr_temp;
1504 	int is4slot = 0;
1505 
1506 	/*
1507 	 * Define some message arrays to make life simpler.  These
1508 	 * messages correspond to definitions in <sys/fhc.c> for
1509 	 * temperature trend (enum temp_trend) and temperature state
1510 	 * (enum temp_state).
1511 	 */
1512 	static char *temp_trend_msg[] = {	"unknown",
1513 						"rapidly falling",
1514 						"falling",
1515 						"stable",
1516 						"rising",
1517 						"rapidly rising",
1518 						"unknown (noisy)"
1519 					};
1520 	static char *temp_state_msg[] = {	"   OK    ",
1521 						"WARNING  ",
1522 						" DANGER  "
1523 					};
1524 
1525 	log_printf("\n", 0);
1526 	log_printf("=========================", 0);
1527 	log_printf(dgettext(TEXT_DOMAIN, " Environmental Status "), 0);
1528 	log_printf("=========================", 0);
1529 	log_printf("\n", 0);
1530 
1531 	exit_code = disp_keysw_and_leds(kstats);
1532 
1533 	if (!kstats->sys_kstats_ok) {
1534 		log_printf(dgettext(TEXT_DOMAIN,
1535 			"*** Error: Unavailable ***\n\n"));
1536 		return (1);
1537 	}
1538 
1539 	/*
1540 	 * for purposes within this routine,
1541 	 * 5 slot behaves the same as a 4 slot
1542 	 */
1543 	if (SYS_TYPE(kstats->sysstat1) == SYS_4_SLOT)
1544 		is4slot = 1;
1545 
1546 	log_printf("\n", 0);
1547 	log_printf("\nFans:\n", 0);
1548 	log_printf("-----\n", 0);
1549 
1550 	log_printf("Unit   Status\n", 0);
1551 	log_printf("----   ------\n", 0);
1552 
1553 	log_printf("%-4s    ", is4slot ? "Disk" : "Rack", 0);
1554 	/* Check the status of the Rack Fans */
1555 	if ((kstats->fan_status & SYS_RACK_FANFAIL) == 0) {
1556 		log_printf("OK\n", 0);
1557 	} else {
1558 		log_printf("FAIL\n", 0);
1559 		exit_code = 1;
1560 	}
1561 
1562 	if (!is4slot) {
1563 		/*
1564 		 * keyswitch and ac box are on 8 & 16 slot only
1565 		 */
1566 		/* Check the status of the Keyswitch Fan assembly. */
1567 		log_printf("%-4s    ", "Key", 0);
1568 		if ((kstats->fan_status & SYS_KEYSW_FAN_OK) != 0) {
1569 			log_printf("OK\n", 0);
1570 		} else {
1571 			log_printf("FAIL\n", 0);
1572 			exit_code = 1;
1573 		}
1574 
1575 		log_printf("%-4s    ", "AC", 0);
1576 		if ((kstats->fan_status & SYS_AC_FAN_OK) != 0) {
1577 			log_printf("OK\n", 0);
1578 		} else {
1579 			log_printf("FAIL\n", 0);
1580 			exit_code = 1;
1581 		}
1582 	} else {
1583 		/*
1584 		 * peripheral fan is on 4 slot only
1585 		 * XXX might want to indicate transient states too
1586 		 */
1587 		if (kstats->psstat_kstat_ok) {
1588 			if (kstats->ps_shadow[SYS_P_FAN_INDEX] == PS_OK) {
1589 				log_printf("PPS     OK\n", 0);
1590 			} else if (kstats->ps_shadow[SYS_P_FAN_INDEX] ==
1591 			    PS_FAIL) {
1592 				log_printf("PPS     FAIL\n", 0);
1593 				exit_code = 1;
1594 			}
1595 		}
1596 	}
1597 
1598 	log_printf("\n", 0);
1599 
1600 
1601 	log_printf("System Temperatures (Celsius):\n", 0);
1602 	log_printf("------------------------------\n", 0);
1603 	log_printf("Brd   State   Current  Min  Max  Trend\n", 0);
1604 	log_printf("---  -------  -------  ---  ---  -----\n", 0);
1605 
1606 	for (i = 0, bksp = &kstats->bd_ksp_list[0]; i < MAX_BOARDS;
1607 	    i++, bksp++) {
1608 
1609 		/* Make sure we have kstats for this board first */
1610 		if (!bksp->temp_kstat_ok) {
1611 			continue;
1612 		}
1613 		log_printf("%2d   ", i, 0);
1614 
1615 		/* Print the current state of the temperature */
1616 		log_printf("%s", temp_state_msg[bksp->tempstat.state], 0);
1617 		/* Set exit code for WARNING and DANGER */
1618 		if (bksp->tempstat.state != 0)
1619 			exit_code = 1;
1620 
1621 		/* Print the current temperature */
1622 		curr_temp = bksp->tempstat.l1[bksp->tempstat.index % L1_SZ];
1623 		log_printf("   %2d    ", curr_temp, 0);
1624 
1625 		/* Print the minimum recorded temperature */
1626 		log_printf(" %2d  ", bksp->tempstat.min, 0);
1627 
1628 		/* Print the maximum recorded temperature */
1629 		log_printf(" %2d  ", bksp->tempstat.max, 0);
1630 
1631 		/* Print the current trend in temperature (if available) */
1632 		if (bksp->tempstat.version < 2)
1633 		    log_printf("unknown\n", 0);
1634 		else
1635 		    log_printf("%s\n", temp_trend_msg[bksp->tempstat.trend], 0);
1636 	}
1637 	if (kstats->temp_kstat_ok) {
1638 		log_printf("CLK  ", 0);
1639 
1640 		/* Print the current state of the temperature */
1641 		log_printf("%s", temp_state_msg[kstats->tempstat.state], 0);
1642 		/* Set exit code for WARNING or DANGER */
1643 		if (kstats->tempstat.state != 0)
1644 			exit_code = 1;
1645 
1646 		/* Print the current temperature */
1647 		curr_temp = kstats->tempstat.l1[kstats->tempstat.index % L1_SZ];
1648 		log_printf("   %2d    ", curr_temp, 0);
1649 
1650 		/* Print the minimum recorded temperature */
1651 		log_printf(" %2d  ", kstats->tempstat.min, 0);
1652 
1653 		/* Print the maximum recorded temperature */
1654 		log_printf(" %2d  ", kstats->tempstat.max, 0);
1655 
1656 		/* Print the current trend in temperature (if available) */
1657 		if (kstats->tempstat.version < 2)
1658 			log_printf("unknown\n\n", 0);
1659 		else
1660 			log_printf("%s\n\n",
1661 				temp_trend_msg[kstats->tempstat.trend], 0);
1662 	} else {
1663 		log_printf("\n");
1664 	}
1665 
1666 	log_printf("\n", 0);
1667 	log_printf("Power Supplies:\n", 0);
1668 	log_printf("---------------\n", 0);
1669 	log_printf("Supply                        Status\n", 0);
1670 	log_printf("---------                     ------\n", 0);
1671 	if (kstats->psstat_kstat_ok) {
1672 		for (i = 0; i < SYS_PS_COUNT; i++) {
1673 			char *ps, *state;
1674 
1675 			/* skip core power supplies that are not present */
1676 			if (i <= SYS_PPS0_INDEX && kstats->ps_shadow[i] ==
1677 			    PS_OUT)
1678 				continue;
1679 
1680 			/* Display the unit Number */
1681 			switch (i) {
1682 			case 0: ps = "0"; break;
1683 			case 1: ps = "1"; break;
1684 			case 2: ps = "2"; break;
1685 			case 3: ps = "3"; break;
1686 			case 4: ps = "4"; break;
1687 			case 5: ps = "5"; break;
1688 			case 6: ps = "6"; break;
1689 			case 7: ps = is4slot ? "2nd PPS" : "7"; break;
1690 
1691 			case SYS_PPS0_INDEX: ps = "PPS"; break;
1692 			case SYS_CLK_33_INDEX: ps = "    System 3.3v"; break;
1693 			case SYS_CLK_50_INDEX: ps = "    System 5.0v"; break;
1694 			case SYS_V5_P_INDEX: ps = "    Peripheral 5.0v"; break;
1695 			case SYS_V12_P_INDEX: ps = "    Peripheral 12v"; break;
1696 			case SYS_V5_AUX_INDEX: ps = "    Auxiliary 5.0v"; break;
1697 			case SYS_V5_P_PCH_INDEX: ps =
1698 				"    Peripheral 5.0v precharge";
1699 				break;
1700 			case SYS_V12_P_PCH_INDEX: ps =
1701 				"    Peripheral 12v precharge";
1702 				break;
1703 			case SYS_V3_PCH_INDEX: ps =
1704 				"    System 3.3v precharge"; break;
1705 			case SYS_V5_PCH_INDEX: ps =
1706 				"    System 5.0v precharge"; break;
1707 
1708 			/* skip the peripheral fan here */
1709 			case SYS_P_FAN_INDEX:
1710 				continue;
1711 			}
1712 
1713 			/* what is the state? */
1714 			switch (kstats->ps_shadow[i]) {
1715 			case PS_OK:
1716 				state = "OK";
1717 				break;
1718 
1719 			case PS_FAIL:
1720 				state = "FAIL";
1721 				exit_code = 1;
1722 				break;
1723 
1724 			/* XXX is this an exit_code condition? */
1725 			case PS_OUT:
1726 				state = "PPS Out";
1727 				exit_code = 1;
1728 				break;
1729 
1730 			case PS_UNKNOWN:
1731 				state = "Unknown";
1732 				break;
1733 
1734 			default:
1735 				state = "Illegal State";
1736 				break;
1737 			}
1738 
1739 			log_printf("%-32s %s\n", ps, state, 0);
1740 		}
1741 	}
1742 
1743 	/* Check status of the system AC Power Source */
1744 	log_printf("%-32s ", "AC Power", 0);
1745 	if ((kstats->sysstat2 & SYS_AC_FAIL) == 0) {
1746 		log_printf("OK\n", 0);
1747 	} else {
1748 		log_printf("failed\n", 0);
1749 		exit_code = 1;
1750 	}
1751 	log_printf("\n", 0);
1752 
1753 	return (exit_code);
1754 }
1755 
1756 
1757 /*
1758  * Many of the ASICs present in fusion machines have implementation and
1759  * version numbers stored in the OBP device tree. These codes are displayed
1760  * in this routine in an effort to aid Engineering and Field service
1761  * in detecting old ASICs which may have bugs in them.
1762  */
1763 static void
1764 sunfire_disp_asic_revs(Sys_tree *tree, struct system_kstat_data *kstats)
1765 {
1766 	Board_node *bnode;
1767 	Prom_node *pnode;
1768 	int isplusbrd;
1769 	char *board_str[] = {   "Uninitialized", "Unknown", "CPU",
1770 				"Memory", "Dual-SBus", "UPA-SBus",
1771 				"Dual-PCI", "Disk", "Clock",
1772 				"Dual-SBus-SOC+", "UPA-SBus-SOC+"};
1773 
1774 	/* Print the header */
1775 	log_printf("\n", 0);
1776 	log_printf("=========================", 0);
1777 	log_printf(" HW Revisions ", 0);
1778 	log_printf("=========================", 0);
1779 	log_printf("\n", 0);
1780 	log_printf("\n", 0);
1781 
1782 	/* Else this is a Sunfire or campfire */
1783 	log_printf("ASIC Revisions:\n", 0);
1784 	log_printf("---------------\n", 0);
1785 
1786 	/* Display Firetruck ASIC Revisions first */
1787 	log_printf("Brd  FHC  AC  SBus0  SBus1  PCI0  PCI1  FEPS", 0);
1788 	log_printf("  Board Type      Attributes", 0);
1789 	log_printf("\n", 0);
1790 	log_printf("---  ---  --  -----  -----  ----  ----  ----", 0);
1791 	log_printf("  ----------      ----------", 0);
1792 	log_printf("\n", 0);
1793 
1794 	/*
1795 	 * Display all of the FHC, AC, and chip revisions for the entire
1796 	 * machine. The AC anf FHC chip revs are available  from the device
1797 	 * tree that was read out of the PROM, but the DC chip revs will be
1798 	 * read via a kstat. The interfaces for this are not completely
1799 	 * available at this time.
1800 	 */
1801 	bnode = tree->bd_list;
1802 	while (bnode != NULL) {
1803 		int *version;
1804 		int upa = bd_to_upa(bnode->board_num);
1805 
1806 		/* Display the header with the board number */
1807 		log_printf("%2d   ", bnode->board_num, 0);
1808 
1809 		/* display the FHC version */
1810 		if ((pnode = dev_find_node(bnode->nodes, "fhc")) == NULL) {
1811 			log_printf("     ", 0);
1812 		} else {
1813 			if ((version = (int *)get_prop_val(find_prop(pnode,
1814 			    "version#"))) == NULL) {
1815 				log_printf("     ", 0);
1816 			} else {
1817 				log_printf(" %d   ", *version, 0);
1818 			}
1819 		}
1820 
1821 		/* display the AC version */
1822 		if ((pnode = dev_find_node(bnode->nodes, "ac")) == NULL) {
1823 			log_printf("    ", 0);
1824 		} else {
1825 			if ((version = (int *)get_prop_val(find_prop(pnode,
1826 			    "version#"))) == NULL) {
1827 				log_printf("    ", 0);
1828 			} else {
1829 				log_printf(" %d  ", *version, 0);
1830 			}
1831 		}
1832 
1833 		/* Find sysio 0 on board and print rev */
1834 		if ((pnode = find_device(bnode, upa, "sbus")) == NULL) {
1835 			log_printf("       ", 0);
1836 		} else {
1837 			if ((version = (int *)get_prop_val(find_prop(pnode,
1838 			    "version#"))) == NULL) {
1839 				log_printf("       ", 0);
1840 			} else {
1841 				log_printf("  %d    ", *version, 0);
1842 			}
1843 		}
1844 
1845 		/* Find sysio 1 on board and print rev */
1846 		if ((pnode = find_device(bnode, upa+1, "sbus")) == NULL) {
1847 			log_printf("       ", 0);
1848 		} else {
1849 			if ((version = (int *)get_prop_val(find_prop(pnode,
1850 			    "version#"))) == NULL) {
1851 				log_printf("       ", 0);
1852 			} else {
1853 				log_printf("  %d    ", *version, 0);
1854 			}
1855 		}
1856 
1857 		/* Find Psycho 0 on board and print rev */
1858 		if ((pnode = find_device(bnode, upa, "pci")) == NULL) {
1859 			log_printf("      ", 0);
1860 		} else {
1861 			if ((version = (int *)get_prop_val(find_prop(pnode,
1862 			    "version#"))) == NULL) {
1863 				log_printf("      ", 0);
1864 			} else {
1865 				log_printf(" %d    ", *version, 0);
1866 			}
1867 		}
1868 
1869 		/* Find Psycho 1 on board and print rev */
1870 		if ((pnode = find_device(bnode, upa+1, "pci")) == NULL) {
1871 			log_printf("      ", 0);
1872 		} else {
1873 			if ((version = (int *)get_prop_val(find_prop(pnode,
1874 			    "version#"))) == NULL) {
1875 				log_printf("      ", 0);
1876 			} else {
1877 				log_printf(" %d    ", *version, 0);
1878 			}
1879 		}
1880 
1881 		/* Find the FEPS on board and print rev */
1882 		if ((pnode = dev_find_node(bnode->nodes, "SUNW,hme")) != NULL) {
1883 			if ((version = (int *)get_prop_val(find_prop(pnode,
1884 			    "hm-rev"))) != NULL) {
1885 				if (*version == 0xa0) {
1886 					log_printf(" 2.0  ", 0);
1887 				} else if (*version == 0x20) {
1888 					log_printf(" 2.1  ", 0);
1889 				} else {
1890 					log_printf(" %2x   ", *version, 0);
1891 				}
1892 			}
1893 		} else
1894 			log_printf("      ", 0);
1895 
1896 		/* print out the board type */
1897 		isplusbrd = ISPLUSBRD(kstats->bd_ksp_list
1898 				[bnode->board_num].fhc_bsr);
1899 
1900 		log_printf("%-16s", board_str[bnode->board_type], 0);
1901 		if (isplusbrd)
1902 			log_printf("100MHz Capable", 0);
1903 		else
1904 			log_printf("84MHz Capable", 0);
1905 
1906 		log_printf("\n", 0);
1907 		bnode = bnode->next;
1908 	}
1909 	log_printf("\n", 0);
1910 
1911 	/* Now display the FFB board component revisions */
1912 	for (bnode = tree->bd_list; bnode != NULL; bnode = bnode->next) {
1913 		display_ffb(bnode, 0);
1914 	}
1915 }
1916 
1917 static void
1918 display_hp_boards(struct system_kstat_data *kstats)
1919 {
1920 	int i;
1921 	int j;
1922 	int hp_found = 0;
1923 	struct hp_info *hp;
1924 	char *state;
1925 
1926 	for (i = 0, hp = &kstats->hp_info[0]; i < MAX_BOARDS; i++, hp++) {
1927 		if (!hp->kstat_ok) {
1928 			continue;
1929 		}
1930 
1931 		hp_found = 1;
1932 	}
1933 
1934 	/* return if there are no hotplug boards in the system. */
1935 	if (!hp_found) {
1936 		return;
1937 	}
1938 
1939 	if (hp_found != 0) {
1940 		log_printf("\n", 0);
1941 		log_printf("Detached Boards\n", 0);
1942 		log_printf("===============\n", 0);
1943 		log_printf("  Slot  State       Type           Info\n", 0);
1944 		log_printf("  ----  ---------   ------         ----"
1945 			"-------------------------------------\n", 0);
1946 	}
1947 
1948 	/* Display all detached boards */
1949 	for (i = 0, hp = &kstats->hp_info[0]; i < MAX_BOARDS; i++, hp++) {
1950 		struct cpu_info *cpu;
1951 
1952 		if (hp->kstat_ok == 0) {
1953 			continue;
1954 		}
1955 
1956 
1957 		switch (hp->bd_info.state) {
1958 		case UNKNOWN_STATE:
1959 			state = "unknown";
1960 			break;
1961 
1962 		case ACTIVE_STATE:
1963 			state = "active";
1964 			break;
1965 
1966 		case LOWPOWER_STATE:
1967 			state = "low-power";
1968 			break;
1969 
1970 		case HOTPLUG_STATE:
1971 			state = "hot-plug";
1972 			break;
1973 
1974 		case DISABLED_STATE:
1975 			state = "disabled";
1976 			break;
1977 
1978 		case FAILED_STATE:
1979 			state = "failed";
1980 			break;
1981 
1982 		default:
1983 			state = "unknown";
1984 			break;
1985 		}
1986 
1987 		log_printf("   %2d   %9s   ", i, state, 0);
1988 
1989 		switch (hp->bd_info.type) {
1990 		case MEM_BOARD:
1991 			log_printf("%-14s ", MEM_BD_NAME, 0);
1992 			break;
1993 
1994 		case CPU_BOARD:
1995 			log_printf("%-14s ", CPU_BD_NAME, 0);
1996 
1997 			/* Cannot display CPU info for disabled boards */
1998 			if ((hp->bd_info.state == DISABLED_STATE) ||
1999 			    (hp->bd_info.state == FAILED_STATE)) {
2000 				break;
2001 			}
2002 
2003 			/* Display both CPUs if present */
2004 			cpu = &hp->bd_info.bd.cpu[0];
2005 			for (j = 0; j < 2; j++, cpu++) {
2006 				log_printf("CPU %d: ", j, 0);
2007 				/* Print the rated speed of the CPU. */
2008 				if (cpu->cpu_speed > 1) {
2009 					log_printf("%3d MHz", cpu->cpu_speed,
2010 						0);
2011 				} else {
2012 					log_printf("no CPU       ", 0);
2013 					continue;
2014 				}
2015 
2016 				/* Display the size of the cache */
2017 				if (cpu->cache_size != 0) {
2018 					log_printf(" %0.1fM ",
2019 						(float)cpu->cache_size /
2020 						(float)(1024*1024), 0);
2021 				} else {
2022 					log_printf("    ", 0);
2023 				}
2024 			}
2025 			break;
2026 
2027 		case IO_2SBUS_BOARD:
2028 			log_printf("%-14s ", IO_2SBUS_BD_NAME, 0);
2029 			break;
2030 
2031 		case IO_2SBUS_SOCPLUS_BOARD:
2032 			log_printf("%-14s ", IO_2SBUS_SOCPLUS_BD_NAME, 0);
2033 			break;
2034 
2035 		case IO_SBUS_FFB_BOARD:
2036 			log_printf("%-14s ", IO_SBUS_FFB_BD_NAME, 0);
2037 			switch (hp->bd_info.bd.io2.ffb_size) {
2038 			case FFB_SINGLE:
2039 				log_printf("Single buffered FFB", 0);
2040 				break;
2041 
2042 			case FFB_DOUBLE:
2043 				log_printf("Double buffered FFB", 0);
2044 				break;
2045 
2046 			case FFB_NOT_FOUND:
2047 				log_printf("No FFB installed", 0);
2048 				break;
2049 
2050 			default:
2051 				log_printf("Illegal FFB size", 0);
2052 				break;
2053 			}
2054 			break;
2055 
2056 		case IO_SBUS_FFB_SOCPLUS_BOARD:
2057 			log_printf("%-14s ", IO_SBUS_FFB_SOCPLUS_BD_NAME, 0);
2058 			switch (hp->bd_info.bd.io2.ffb_size) {
2059 			case FFB_SINGLE:
2060 				log_printf("Single buffered FFB", 0);
2061 				break;
2062 
2063 			case FFB_DOUBLE:
2064 				log_printf("Double buffered FFB", 0);
2065 				break;
2066 
2067 			case FFB_NOT_FOUND:
2068 				log_printf("No FFB installed", 0);
2069 				break;
2070 
2071 			default:
2072 				log_printf("Illegal FFB size", 0);
2073 				break;
2074 			}
2075 			break;
2076 
2077 		case IO_PCI_BOARD:
2078 			log_printf("%-14s ", IO_PCI_BD_NAME, 0);
2079 			break;
2080 
2081 		case DISK_BOARD:
2082 			log_printf("%-14s ", "disk", 0);
2083 			for (j = 0; j < 2; j++) {
2084 				log_printf("Disk %d:", j, 0);
2085 				if (hp->bd_info.bd.dsk.disk_pres[j]) {
2086 					log_printf(" Target: %2d   ",
2087 						hp->bd_info.bd.dsk.disk_id[j],
2088 						0);
2089 				} else {
2090 					log_printf(" no disk      ", 0);
2091 				}
2092 			}
2093 			break;
2094 
2095 		case UNKNOWN_BOARD:
2096 		case UNINIT_BOARD:
2097 		default:
2098 			log_printf("UNKNOWN ", 0);
2099 			break;
2100 		}
2101 		log_printf("\n");
2102 	}
2103 }
2104 
2105 /*
2106  * Analysis functions:
2107  *
2108  * Most of the Fatal error data analyzed from error registers is not
2109  * very complicated. This is because the FRUs for errors detected by
2110  * most parts is either a CPU module, a FFB, or the system board
2111  * itself.
2112  * The analysis of the Address Controller errors is the most complicated.
2113  * These errors can be caused by other boards as well as the local board.
2114  */
2115 
2116 /*
2117  * analyze_cpu
2118  *
2119  * Analyze the CPU MFSR passed in and determine what type of fatal
2120  * hardware errors occurred at the time of the crash. This function
2121  * returns a pointer to a string to the calling routine.
2122  */
2123 static int
2124 analyze_cpu(char **msgs, int cpu_id, u_longlong_t afsr)
2125 {
2126 	int count = 0;
2127 	int i;
2128 	int syndrome;
2129 	char msgbuf[MAXSTRLEN];
2130 
2131 	if (msgs == NULL) {
2132 		return (count);
2133 	}
2134 
2135 	if (afsr & P_AFSR_ETP) {
2136 		(void) sprintf(msgbuf, "CPU %d Ecache Tag Parity Error, ",
2137 			cpu_id);
2138 
2139 		/* extract syndrome for afsr */
2140 		syndrome = (afsr & P_AFSR_ETS) >> ETS_SHIFT;
2141 
2142 		/* now concat the parity syndrome msg */
2143 		for (i = 0; i < 4; i++) {
2144 			if ((0x1 << i)  & syndrome) {
2145 				(void) strcat(msgbuf, ecache_parity[i]);
2146 			}
2147 		}
2148 		(void) strcat(msgbuf, "\n");
2149 		*msgs++ = strdup(msgbuf);
2150 		count++;
2151 	}
2152 
2153 	if (afsr & P_AFSR_ISAP) {
2154 		(void) sprintf(msgbuf,
2155 			"CPU %d Incoming System Address Parity Error\n",
2156 			cpu_id);
2157 		*msgs++ = strdup(msgbuf);
2158 		count++;
2159 	}
2160 
2161 	return (count);
2162 }
2163 
2164 /*
2165  * analyze_ac
2166  *
2167  * This function checks the AC error register passed in and checks
2168  * for any errors that occured during the fatal hardware reset.
2169  */
2170 static int
2171 analyze_ac(char **msgs, u_longlong_t ac_error)
2172 {
2173 	int i;
2174 	int count = 0;
2175 	char msgbuf[MAXSTRLEN];
2176 	int tmp_cnt;
2177 
2178 	if (msgs == NULL) {
2179 		return (count);
2180 	}
2181 
2182 	for (i = 2; i < MAX_BITS; i++) {
2183 		if ((((u_longlong_t)0x1 << i) & ac_error) != 0) {
2184 			if (ac_errors[i].error != NULL) {
2185 				(void) sprintf(msgbuf, "AC: %s\n",
2186 					ac_errors[i].error);
2187 				*msgs++ = strdup(msgbuf);
2188 				count++;
2189 
2190 				/* display the part that might cause this */
2191 				tmp_cnt = disp_parts(msgs, ac_error, i);
2192 				count += tmp_cnt;
2193 				msgs += tmp_cnt;
2194 			}
2195 		}
2196 	}
2197 
2198 	return (count);
2199 }
2200 
2201 /*
2202  * analyze_dc
2203  *
2204  * This routine checks the DC shdow chain and tries to determine
2205  * what type of error might have caused the fatal hardware reset
2206  * error.
2207  */
2208 static int
2209 analyze_dc(int board, char **msgs, u_longlong_t dc_error)
2210 {
2211 	int i;
2212 	int count = 0;
2213 	char msgbuf[MAXSTRLEN];
2214 
2215 	if (msgs == NULL) {
2216 		return (count);
2217 	}
2218 
2219 	/*
2220 	 * The DC scan data is contained in 8 bytes, one byte per
2221 	 * DC. There are 8 DCs on a system board.
2222 	 */
2223 
2224 	for (i = 0; i < 8; i++) {
2225 		if (dc_error & DC_OVERFLOW) {
2226 			(void) sprintf(msgbuf, dc_overflow_txt, board, i);
2227 			*msgs++ = strdup(msgbuf);
2228 			count++;
2229 		}
2230 
2231 		if (dc_error & DC_PARITY) {
2232 			(void) sprintf(msgbuf, dc_parity_txt, board, i);
2233 			*msgs++ = strdup(msgbuf);
2234 			count++;
2235 		}
2236 		dc_error = dc_error >> 8;	/* shift over to next byte */
2237 	}
2238 
2239 	return (count);
2240 }
2241 
2242 static int
2243 disp_parts(char **msgs, u_longlong_t ac_error, int type)
2244 {
2245 	int count = 0;
2246 	int part;
2247 	char msgbuf[MAXSTRLEN];
2248 	int i;
2249 
2250 	if (msgs == NULL) {
2251 		return (count);
2252 	}
2253 
2254 	(void) sprintf(msgbuf, "\tThe error could be caused by:\n");
2255 	*msgs++ = strdup(msgbuf);
2256 	count++;
2257 
2258 	for (i = 0; (i < MAX_FRUS) && ac_errors[type].part[i]; i++) {
2259 		part = ac_errors[type].part[i];
2260 
2261 		if (part == UPA_PART) {
2262 			if (ac_error & UPA_PORT_A) {
2263 				part = UPA_A_PART;
2264 			} else if (ac_error & UPA_PORT_B) {
2265 				part = UPA_B_PART;
2266 			}
2267 		}
2268 
2269 		if (part == DTAG_PART) {
2270 			if (ac_error & UPA_PORT_A) {
2271 				part = DTAG_A_PART;
2272 			} else if (ac_error & UPA_PORT_B) {
2273 				part = DTAG_B_PART;
2274 			}
2275 		}
2276 
2277 		(void) sprintf(msgbuf, "\t\t%s\n", part_str[part]);
2278 
2279 		*msgs++ = strdup(msgbuf);
2280 		count++;
2281 	}
2282 
2283 	return (count);
2284 }
2285