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