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