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