xref: /illumos-gate/usr/src/lib/libprtdiag_psr/sparc/tazmo/common/tazmo.c (revision 63f91fbc3c024870d86dc3332a4a0080fb29bc40)
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  * Tazmo Platform specific functions.
28  *
29  *	called when :
30  *	machine_type == MTYPE_TAZMO
31  *
32  */
33 
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <ctype.h>
38 #include <string.h>
39 #include <kvm.h>
40 #include <varargs.h>
41 #include <errno.h>
42 #include <time.h>
43 #include <dirent.h>
44 #include <fcntl.h>
45 #include <sys/param.h>
46 #include <sys/stat.h>
47 #include <sys/types.h>
48 #include <sys/utsname.h>
49 #include <sys/openpromio.h>
50 #include <kstat.h>
51 #include <libintl.h>
52 #include <syslog.h>
53 #include <sys/dkio.h>
54 #include "pdevinfo.h"
55 #include "display.h"
56 #include "pdevinfo_sun4u.h"
57 #include "display_sun4u.h"
58 #include "libprtdiag.h"
59 
60 #if !defined(TEXT_DOMAIN)
61 #define	TEXT_DOMAIN	"SYS_TEST"
62 #endif
63 
64 extern	int	print_flag;
65 
66 /*
67  * these functions will overlay the symbol table of libprtdiag
68  * at runtime (workgroup server systems only)
69  */
70 int	error_check(Sys_tree *tree, struct system_kstat_data *kstats);
71 void	display_memoryconf(Sys_tree *tree);
72 int	disp_fail_parts(Sys_tree *tree);
73 void	display_hp_fail_fault(Sys_tree *tree, struct system_kstat_data *kstats);
74 void	display_diaginfo(int flag, Prom_node *root, Sys_tree *tree,
75 				struct system_kstat_data *kstats);
76 void	display_boardnum(int num);
77 void	display_pci(Board_node *);
78 void	display_io_cards(struct io_card *list);
79 void	display_ffb(Board_node *, int);
80 void	read_platform_kstats(Sys_tree *tree,
81 		struct system_kstat_data *sys_kstat,
82 		struct envctrl_kstat_data *ep);
83 
84 /* local functions */
85 static	int disp_envctrl_status(Sys_tree *, struct system_kstat_data *);
86 static	void check_disk_presence(Sys_tree *, int *, int *, int *);
87 static	void modify_device_path(char *, char *);
88 static	int disk_present(char *);
89 static	void tazjav_disp_asic_revs(Sys_tree *);
90 static	int tazmo_physical_slot(Prom_node *, Prom_node *, int, char *);
91 static	Prom_node *dev_next_node_sibling(Prom_node *root, char *name);
92 
93 
94 int
95 error_check(Sys_tree *tree, struct system_kstat_data *kstats)
96 {
97 	int exit_code = 0;	/* init to all OK */
98 
99 #ifdef	lint
100 	kstats = kstats;
101 #endif
102 	/*
103 	 * silently check for any types of machine errors
104 	 */
105 	print_flag = 0;
106 	if (disp_fail_parts(tree) || disp_envctrl_status(tree, kstats)) {
107 		/* set exit_code to show failures */
108 		exit_code = 1;
109 	}
110 	print_flag = 1;
111 
112 	return (exit_code);
113 }
114 
115 /* Search for and return the node's sibling */
116 static Prom_node *
117 dev_next_node_sibling(Prom_node *root, char *name)
118 {
119 	if (root == NULL)
120 		return (NULL);
121 
122 	/* look at your siblings */
123 	if (dev_find_node(root->sibling, name) != NULL)
124 		return (root->sibling);
125 
126 	return (NULL);  /* not found */
127 }
128 
129 /*
130  * This function displays memory configurations specific to Tazmo/Javelin.
131  * The PROM device tree is read to obtain this information.
132  * Some of the information obtained is memory interleave factor,
133  * DIMM sizes, DIMM socket names.
134  */
135 void
136 display_memoryconf(Sys_tree *tree)
137 {
138 	Board_node *bnode;
139 	Prom_node *memory;
140 	Prom_node *bank;
141 	Prom_node *dimm;
142 	uint_t *preg;
143 	uint_t interlv;
144 	unsigned long size = 0;
145 	int bank_count = 0;
146 	char *sock_name;
147 	char *status;
148 	Prop *status_prop;
149 	char interleave[8];
150 	int total_size = 0;
151 
152 	log_printf("\n", 0);
153 	log_printf("=========================", 0);
154 	log_printf(dgettext(TEXT_DOMAIN, " Memory "), 0);
155 	log_printf("=========================", 0);
156 	log_printf("\n", 0);
157 	log_printf("\n", 0);
158 	bnode = tree->bd_list;
159 	memory = dev_find_node(bnode->nodes, "memory");
160 	preg = (uint_t *)(get_prop_val(find_prop(memory, "interleave")));
161 	if (preg) {
162 		interlv = preg[4];
163 		log_printf("Memory Interleave Factor = %d-way\n\n", interlv, 0);
164 	}
165 	log_printf("       Interlv.  Socket   Size\n", 0);
166 	log_printf("Bank    Group     Name    (MB)  Status\n", 0);
167 	log_printf("----    -----    ------   ----  ------\n", 0);
168 
169 	dimm = bnode->nodes;
170 	for (bank = dev_find_node(bnode->nodes, "bank"); bank != NULL;
171 	    bank = dev_next_node(bank, "bank")) {
172 		int bank_size = 0;
173 		uint_t *reg_prop;
174 
175 		preg = (uint_t *)(get_prop_val(
176 		    find_prop(bank, "bank-interleave")));
177 
178 		reg_prop = (uint_t *)(get_prop_val(
179 		    find_prop(bank, "reg")));
180 
181 		/*
182 		 * Skip empty banks
183 		 */
184 		if (((reg_prop[2]<<12) + (reg_prop[3]>>20)) == 0) {
185 			bank_count++;
186 			continue;
187 		}
188 
189 		if (preg) {
190 			interlv = preg[2];
191 			(void) sprintf(interleave, " %d ", interlv);
192 			bank_size = (preg[0]<<12) + (preg[1]>>20);
193 		} else {
194 			(void) sprintf(interleave, "%s", "none");
195 			preg = (uint_t *)(get_prop_val(find_prop(bank, "reg")));
196 			if (preg) {
197 				bank_size = (preg[2]<<12) + (preg[3]>>20);
198 			}
199 		}
200 		for (dimm = dev_find_node(bank, "dimm"); dimm != NULL;
201 		    dimm = dev_next_node_sibling(dimm, "dimm")) {
202 			char dimm_status[16];
203 
204 			sock_name = (char *)(get_prop_val(
205 			    find_prop(dimm, "socket-name")));
206 			preg = (uint_t *)(get_prop_val(find_prop(dimm, "reg")));
207 			size = (preg[2]<<12) + (preg[3]>>20);
208 			if ((status_prop = find_prop(dimm, "status")) == NULL) {
209 				(void) sprintf(dimm_status, "%s", "OK");
210 			} else {
211 				status = (char *)(get_prop_val(status_prop));
212 				(void) sprintf(dimm_status, "%s", status);
213 			}
214 			log_printf("%3d     %5s    %6s  %4d  %6s\n",
215 			    bank_count, interleave, sock_name,
216 			    size, dimm_status, 0);
217 		}
218 		total_size += bank_size;
219 		bank_count++;
220 	}
221 	log_printf("\n", 0);
222 }
223 
224 /*
225  * disp_fail_parts
226  *
227  * Display the failed parts in the system. This function looks for
228  * the status property in all PROM nodes. On systems where
229  * the PROM does not supports passing diagnostic information
230  * thruogh the device tree, this routine will be silent.
231  */
232 int
233 disp_fail_parts(Sys_tree *tree)
234 {
235 	int exit_code;
236 	int system_failed = 0;
237 	Board_node *bnode = tree->bd_list;
238 	Prom_node *pnode;
239 	char *fru;
240 	char *sock_name;
241 	char slot_str[MAXSTRLEN];
242 
243 	exit_code = 0;
244 
245 	/* go through all of the boards looking for failed units. */
246 	while (bnode != NULL) {
247 		/* find failed chips */
248 		pnode = find_failed_node(bnode->nodes);
249 		if ((pnode != NULL) && !system_failed) {
250 			system_failed = 1;
251 			exit_code = 1;
252 			if (print_flag == 0) {
253 				return (exit_code);
254 			}
255 			log_printf("\n", 0);
256 			log_printf(dgettext(TEXT_DOMAIN, "Failed Field "
257 			    "Replaceable Units (FRU) in System:\n"), 0);
258 			log_printf("=========================="
259 			    "====================\n", 0);
260 		}
261 
262 		while (pnode != NULL) {
263 			void *value;
264 			char *name;		/* node name string */
265 			char *type;		/* node type string */
266 
267 			value = get_prop_val(find_prop(pnode, "status"));
268 			name = get_node_name(pnode);
269 
270 			/* sanity check of data retreived from PROM */
271 			if ((value == NULL) || (name == NULL)) {
272 				pnode = next_failed_node(pnode);
273 				continue;
274 			}
275 
276 
277 			log_printf(dgettext(TEXT_DOMAIN, "%s unavailable :\n"),
278 			    name, 0);
279 
280 			log_printf(dgettext(TEXT_DOMAIN,
281 			    "\tPROM fault string: %s\n"),
282 			    value, 0);
283 
284 			log_printf(dgettext(TEXT_DOMAIN,
285 			    "\tFailed Field Replaceable Unit is "), 0);
286 
287 			/*
288 			 * Determine whether FRU is CPU module, system
289 			 * board, or SBus card.
290 			 */
291 			if ((name != NULL) && (strstr(name, "sbus"))) {
292 
293 				log_printf(dgettext(TEXT_DOMAIN, "SBus "
294 				    "Card %d\n"), get_sbus_slot(pnode), 0);
295 
296 			} else if (((name = get_node_name(pnode)) !=
297 			    NULL) && (strstr(name, "pci"))) {
298 
299 				log_printf(dgettext(TEXT_DOMAIN,
300 				    "system board\n"), 0);
301 
302 			} else if (((name = get_node_name(pnode)) !=
303 			    NULL) && (strstr(name, "ffb"))) {
304 
305 				log_printf(dgettext(TEXT_DOMAIN,
306 				    "FFB Card %d\n"),
307 				    tazmo_physical_slot(
308 				    dev_find_node(bnode->nodes, "slot2dev"),
309 				    pnode, -1, slot_str), 0);
310 
311 			} else if (((name = get_node_name(pnode->parent)) !=
312 			    NULL) && (strstr(name, "pci"))) {
313 
314 				(void) tazmo_physical_slot(
315 				    NULL,
316 				    pnode->parent,
317 				    get_pci_device(pnode),
318 				    slot_str);
319 				log_printf(dgettext(TEXT_DOMAIN,
320 				    "PCI Card in %s\n"), slot_str, 0);
321 
322 			} else if (((type = get_node_type(pnode)) != NULL) &&
323 			    (strstr(type, "cpu"))) {
324 
325 				log_printf(
326 				    dgettext(TEXT_DOMAIN,
327 				    "UltraSPARC module Module %d\n"),
328 				    get_id(pnode));
329 
330 			} else if (((type = get_node_type(pnode)) != NULL) &&
331 			    (strstr(type, "memory-module"))) {
332 
333 				fru = (char *)(get_prop_val(
334 				    find_prop(pnode, "fru")));
335 				sock_name = (char *)(get_prop_val(
336 				    find_prop(pnode, "socket-name")));
337 				log_printf(
338 				    dgettext(TEXT_DOMAIN,
339 				    "%s in socket %s\n"),
340 				    fru, sock_name, 0);
341 			}
342 			pnode = next_failed_node(pnode);
343 		}
344 		bnode = bnode->next;
345 	}
346 
347 	if (!system_failed) {
348 		log_printf("\n", 0);
349 		log_printf(dgettext(TEXT_DOMAIN,
350 		    "No failures found in System\n"), 0);
351 		log_printf("===========================\n", 0);
352 	}
353 
354 	if (system_failed)
355 		return (1);
356 	else
357 		return (0);
358 }
359 
360 void
361 display_hp_fail_fault(Sys_tree *tree, struct system_kstat_data *kstats)
362 {
363 #ifdef lint
364 	kstats = kstats;
365 #endif
366 	/* Display failed units */
367 	(void) disp_fail_parts(tree);
368 }
369 
370 
371 void
372 display_diaginfo(int flag, Prom_node *root, Sys_tree *tree,
373     struct system_kstat_data *kstats)
374 {
375 	/*
376 	 * Now display the last powerfail time and the fatal hardware
377 	 * reset information. We do this under a couple of conditions.
378 	 * First if the user asks for it. The second is iof the user
379 	 * told us to do logging, and we found a system failure.
380 	 */
381 	if (flag) {
382 		/*
383 		 * display time of latest powerfail. Not all systems
384 		 * have this capability. For those that do not, this
385 		 * is just a no-op.
386 		 */
387 		disp_powerfail(root);
388 
389 		(void) disp_envctrl_status(tree, kstats);
390 
391 		tazjav_disp_asic_revs(tree);
392 
393 		platform_disp_prom_version(tree);
394 	}
395 	return;
396 
397 }
398 
399 /* ARGSUSED */
400 void
401 display_boardnum(int num)
402 {
403 	log_printf("SYS   ", 0);
404 }
405 
406 
407 
408 /*
409  * display_pci
410  * Display all the PCI IO cards on this board.
411  */
412 
413 /* ARGSUSED */
414 void
415 display_pci(Board_node *board)
416 {
417 	struct io_card *card_list = NULL;
418 	struct io_card card;
419 	void *value;
420 	Prom_node *pci;
421 	Prom_node *card_node;
422 
423 	if (board == NULL)
424 		return;
425 
426 	/* Initialize all the common information */
427 	card.display = 1;
428 	card.board = board->board_num;
429 	(void) sprintf(card.bus_type, "PCI");
430 
431 	for (pci = dev_find_node(board->nodes, PCI_NAME); pci != NULL;
432 	    pci = dev_next_node(pci, PCI_NAME)) {
433 		char *name;
434 		Prom_node *prev_parent = NULL;
435 		int prev_device = -1;
436 		int pci_pci_bridge = 0;
437 
438 		/*
439 		 * If we have reached a pci-to-pci bridge node,
440 		 * we are one level below the 'pci' nodes level
441 		 * in the device tree. To get back to that level,
442 		 * the search should continue with the sibling of
443 		 * the parent or else the remaining 'pci' cards
444 		 * will not show up in the output.
445 		 */
446 		if (find_prop(pci, "upa-portid") == NULL) {
447 			if ((pci->parent->sibling != NULL) &&
448 			    (strcmp(get_prop_val(
449 			    find_prop(pci->parent->sibling,
450 			    "name")), PCI_NAME) == 0))
451 				pci = pci->parent->sibling;
452 			else {
453 				pci = pci->parent->sibling;
454 				continue;
455 			}
456 		}
457 
458 		/* Skip all failed nodes for now */
459 		if (node_failed(pci))
460 			continue;
461 
462 		/* Fill in frequency */
463 		value = get_prop_val(find_prop(pci, "clock-frequency"));
464 		if (value == NULL)
465 			card.freq = -1;
466 		else
467 			card.freq = ((*(int *)value) + 500000) / 1000000;
468 
469 		/* Walk through the PSYCHO children */
470 		card_node = pci->child;
471 		while (card_node != NULL) {
472 			Prop *compat = NULL;
473 
474 			/* If it doesn't have a name, skip it */
475 			name = (char *)get_prop_val(
476 			    find_prop(card_node, "name"));
477 			if (name == NULL) {
478 				card_node = card_node->sibling;
479 				continue;
480 			}
481 
482 			/*
483 			 * If this is a PCI bridge, then display its
484 			 * children.
485 			 */
486 			if (strcmp(name, "pci") == 0) {
487 				card_node = card_node->child;
488 				pci_pci_bridge = 1;
489 				continue;
490 			}
491 
492 			/* Get the slot number for this card */
493 			if (pci_pci_bridge) {
494 				card.slot = tazmo_physical_slot(
495 				    dev_find_node(board->nodes, "slot2dev"),
496 				    pci,
497 				    get_pci_to_pci_device(card_node->parent),
498 				    card.slot_str);
499 			} else
500 				card.slot = tazmo_physical_slot(
501 				    dev_find_node(board->nodes, "slot2dev"),
502 				    pci,
503 				    get_pci_device(card_node),
504 				    card.slot_str);
505 
506 			/*
507 			 * Check that duplicate devices are not reported
508 			 * on Tazmo.
509 			 */
510 			if ((card_node->parent == prev_parent) &&
511 			    (get_pci_device(card_node) == prev_device) &&
512 			    (pci_pci_bridge == 0))
513 					card.slot = -1;
514 			prev_parent = card_node->parent;
515 			prev_device = get_pci_device(card_node);
516 
517 
518 			if (card.slot == -1 || strstr(name, "ebus")) {
519 				card_node = card_node->sibling;
520 				continue;
521 			}
522 
523 			/* XXX - Don't know how to get status for PCI cards */
524 			card.status[0] = '\0';
525 
526 			/* Get the model of this card */
527 			value = get_prop_val(find_prop(card_node, "model"));
528 			if (value == NULL)
529 				card.model[0] = '\0';
530 			else
531 				(void) sprintf(card.model, "%s",
532 				    (char *)value);
533 
534 			/*
535 			 * Check if further processing is necessary to display
536 			 * this card uniquely.
537 			 */
538 			distinguish_identical_io_cards(name, card_node, &card);
539 
540 			/*
541 			 * If we haven't figured out the frequency yet,
542 			 * try and get it from the card.
543 			 */
544 			value = get_prop_val(find_prop(pci, "clock-frequency"));
545 			if (value != NULL && card.freq == -1)
546 				card.freq = ((*(int *)value) + 500000)
547 				    / 1000000;
548 
549 
550 			value = get_prop_val(find_prop(card_node,
551 			    "compatible"));
552 
553 			/*
554 			 * On Tazmo, we would like to print out the last
555 			 * string of the "compatible" property if it exists.
556 			 * The IEEE 1275 spec. states that this last string
557 			 * will be the classcode name.
558 			 */
559 			if (value != NULL) {
560 				char *tval;
561 				int index;
562 				const int always = 1;
563 
564 				tval = (char *)value;
565 				index = 0;
566 				compat = find_prop(card_node, "compatible");
567 				while (always) {
568 					if ((strlen(tval) + 1) ==
569 					    (compat->size - index))
570 						break;
571 					index += strlen(tval) + 1;
572 					tval += strlen(tval) + 1;
573 				}
574 				value = (void *)tval;
575 			}
576 
577 			if (value != NULL)
578 				(void) sprintf(card.name, "%s-%s",
579 				    (char *)name, (char *)value);
580 			else
581 				(void) sprintf(card.name, "%s",
582 				    (char *)name);
583 
584 			if (card.freq != -1)
585 				card_list = insert_io_card(card_list, &card);
586 
587 			/*
588 			 * If we are done with the children of the pci bridge,
589 			 * we must continue with the remaining siblings of
590 			 * the pci-to-pci bridge.
591 			 */
592 			if ((card_node->sibling == NULL) && pci_pci_bridge) {
593 				card_node = card_node->parent->sibling;
594 				pci_pci_bridge = 0;
595 			} else
596 				card_node = card_node->sibling;
597 		}
598 	}
599 
600 	display_io_cards(card_list);
601 	free_io_cards(card_list);
602 }
603 
604 
605 /*
606  * Print out all the io cards in the list.  Also print the column
607  * headers if told to do so.
608  */
609 void
610 display_io_cards(struct io_card *list)
611 {
612 	static int banner = 0; /* Have we printed the column headings? */
613 	struct io_card *p;
614 
615 	if (list == NULL)
616 		return;
617 
618 	if (banner == 0) {
619 		log_printf("     Bus   Freq\n", 0);
620 		log_printf("Brd  Type  MHz   Slot  "
621 		    "Name                              Model", 0);
622 		log_printf("\n", 0);
623 		log_printf("---  ----  ----  ----  "
624 		    "--------------------------------  "
625 		    "----------------------", 0);
626 		log_printf("\n", 0);
627 		banner = 1;
628 	}
629 
630 	for (p = list; p != NULL; p = p -> next) {
631 		log_printf("SYS   ", p->board, 0);
632 		log_printf("%-4s  ", p->bus_type, 0);
633 		log_printf("%3d   ", p->freq, 0);
634 		log_printf("%3d   ", p->slot, 0);
635 		log_printf("%-32.32s", p->name, 0);
636 		if (strlen(p->name) > 32)
637 			log_printf("+ ", 0);
638 		else
639 			log_printf("  ", 0);
640 		log_printf("%-22.22s", p->model, 0);
641 		if (strlen(p->model) > 22)
642 			log_printf("+", 0);
643 		log_printf("\n", 0);
644 	}
645 }
646 
647 /*
648  * display_ffb
649  * Display all FFBs on this board.  It can either be in tabular format,
650  * or a more verbose format.
651  */
652 void
653 display_ffb(Board_node *board, int table)
654 {
655 	Prom_node *ffb;
656 	void *value;
657 	struct io_card *card_list = NULL;
658 	struct io_card card;
659 
660 	if (board == NULL)
661 		return;
662 
663 	/* Fill in common information */
664 	card.display = 1;
665 	card.board = board->board_num;
666 	(void) sprintf(card.bus_type, "UPA");
667 	card.freq = sys_clk;
668 
669 	for (ffb = dev_find_node(board->nodes, FFB_NAME); ffb != NULL;
670 	    ffb = dev_next_node(ffb, FFB_NAME)) {
671 		if (table == 1) {
672 			/* Print out in table format */
673 
674 			/* XXX - Get the slot number (hack) */
675 			card.slot = tazmo_physical_slot(
676 			    dev_find_node(board->nodes, "slot2dev"),
677 			    ffb,
678 			    -1,
679 			    card.slot_str);
680 
681 			/* Find out if it's single or double buffered */
682 			(void) sprintf(card.name, "FFB");
683 			value = get_prop_val(find_prop(ffb, "board_type"));
684 			if (value != NULL)
685 				if ((*(int *)value) & FFB_B_BUFF)
686 					(void) sprintf(card.name,
687 					    "FFB, Double Buffered");
688 				else
689 					(void) sprintf(card.name,
690 					    "FFB, Single Buffered");
691 
692 			/* Print model number */
693 			card.model[0] = '\0';
694 			value = get_prop_val(find_prop(ffb, "model"));
695 			if (value != NULL)
696 				(void) sprintf(card.model, "%s",
697 				    (char *)value);
698 
699 			card_list = insert_io_card(card_list, &card);
700 		} else {
701 			/* print in long format */
702 			char device[MAXSTRLEN];
703 			int fd = -1;
704 			struct dirent *direntp;
705 			DIR *dirp;
706 			union strap_un strap;
707 			struct ffb_sys_info fsi;
708 
709 			/* Find the device node using upa address */
710 			value = get_prop_val(find_prop(ffb, "upa-portid"));
711 			if (value == NULL)
712 				continue;
713 
714 			(void) sprintf(device, "%s@%x", FFB_NAME,
715 			    *(int *)value);
716 			if ((dirp = opendir("/devices")) == NULL)
717 				continue;
718 
719 			while ((direntp = readdir(dirp)) != NULL) {
720 				if (strstr(direntp->d_name, device) != NULL) {
721 					(void) sprintf(device, "/devices/%s",
722 					    direntp->d_name);
723 					fd = open(device, O_RDWR, 0666);
724 					break;
725 				}
726 			}
727 			(void) closedir(dirp);
728 
729 			if (fd == -1)
730 				continue;
731 
732 			if (ioctl(fd, FFB_SYS_INFO, &fsi) < 0)
733 				continue;
734 
735 			log_printf("FFB Hardware Configuration:\n", 0);
736 			log_printf("-----------------------------------\n", 0);
737 
738 			strap.ffb_strap_bits = fsi.ffb_strap_bits;
739 			log_printf("\tBoard rev: %d\n",
740 			    (int)strap.fld.board_rev, 0);
741 			log_printf("\tFBC version: 0x%x\n", fsi.fbc_version, 0);
742 			log_printf("\tDAC: %s\n",
743 			    fmt_manf_id(fsi.dac_version, device), 0);
744 			log_printf("\t3DRAM: %s\n",
745 			    fmt_manf_id(fsi.fbram_version, device), 0);
746 			log_printf("\n", 0);
747 		}
748 	}
749 
750 	display_io_cards(card_list);
751 	free_io_cards(card_list);
752 }
753 
754 /*
755  * This module does the reading and interpreting of tazmo system
756  * kstats. These kstats are created by the envctrl driver:
757  */
758 void
759 read_platform_kstats(Sys_tree *tree, struct system_kstat_data *sys_kstat,
760     struct envctrl_kstat_data *ep)
761 {
762 	kstat_ctl_t		*kc;
763 	kstat_t			*ksp;
764 
765 	if ((kc = kstat_open()) == NULL) {
766 		return;
767 	}
768 
769 	ep = &sys_kstat->env_data;
770 
771 	/* Read the power supply kstats */
772 	ksp = kstat_lookup(kc, ENVCTRL_MODULE_NAME, INSTANCE_0,
773 	    ENVCTRL_KSTAT_PSNAME);
774 
775 	if (ksp != NULL && (kstat_read(kc, ksp, NULL) != -1)) {
776 		(void) memcpy(ep->ps_kstats, ksp->ks_data,
777 		    MAX_DEVS * sizeof (envctrl_ps_t));
778 	} else {
779 		sys_kstat->envctrl_kstat_ok = B_FALSE;
780 		return;
781 	}
782 
783 	/* Read the fan status kstats */
784 	ksp = kstat_lookup(kc, ENVCTRL_MODULE_NAME, INSTANCE_0,
785 	    ENVCTRL_KSTAT_FANSTAT);
786 
787 	if (ksp != NULL && (kstat_read(kc, ksp, NULL) != -1)) {
788 		(void) memcpy(ep->fan_kstats, ksp->ks_data,
789 		    ksp->ks_ndata * sizeof (envctrl_fan_t));
790 	} else {
791 		sys_kstat->envctrl_kstat_ok = B_FALSE;
792 		return;
793 	}
794 
795 	/* Read the enclosure kstats */
796 	ksp = kstat_lookup(kc, ENVCTRL_MODULE_NAME, INSTANCE_0,
797 	    ENVCTRL_KSTAT_ENCL);
798 
799 	if (ksp != NULL && (kstat_read(kc, ksp, NULL) != -1)) {
800 		(void) memcpy(ep->encl_kstats, ksp->ks_data,
801 		    ksp->ks_ndata * sizeof (envctrl_encl_t));
802 	} else {
803 		sys_kstat->envctrl_kstat_ok = B_FALSE;
804 		return;
805 	}
806 
807 	sys_kstat->envctrl_kstat_ok = B_TRUE;
808 }
809 
810 /*
811  * Walk the PROM device tree and build the system tree and root tree.
812  * Nodes that have a board number property are placed in the board
813  * structures for easier processing later. Child nodes are placed
814  * under their parents. ffb (Fusion Frame Buffer) nodes are handled
815  * specially, because they do not contain board number properties.
816  * This was requested from OBP, but was not granted. So this code
817  * must parse the MID of the FFB to find the board#.
818  */
819 Prom_node *
820 walk(Sys_tree *tree, Prom_node *root, int id)
821 {
822 	register int curnode;
823 	Prom_node *pnode;
824 	char *name;
825 	char *type;
826 	char *model;
827 	int board_node = 0;
828 
829 	/* allocate a node for this level */
830 	if ((pnode = (Prom_node *) malloc(sizeof (struct prom_node))) ==
831 	    NULL) {
832 		perror("malloc");
833 		exit(2);	/* program errors cause exit 2 */
834 	}
835 
836 	/* assign parent Prom_node */
837 	pnode->parent = root;
838 	pnode->sibling = NULL;
839 	pnode->child = NULL;
840 
841 	/* read properties for this node */
842 	dump_node(pnode);
843 
844 	/*
845 	 * Place a node in a 'board' if it has 'board'-ness. The definition
846 	 * is that all nodes that are children of root should have a
847 	 * board# property. But the PROM tree does not exactly follow
848 	 * this. This is where we start hacking. The name 'ffb' can
849 	 * change, so watch out for this.
850 	 *
851 	 * The UltraSPARC, sbus, pci and ffb nodes will exit in
852 	 * the desktops and will not have board# properties. These
853 	 * cases must be handled here.
854 	 *
855 	 * PCI to PCI bridges also have the name "pci", but with different
856 	 * model property values.  They should not be put under 'board'.
857 	 */
858 	name = get_node_name(pnode);
859 	type = get_node_type(pnode);
860 	model = (char *)get_prop_val(find_prop(pnode, "model"));
861 #ifdef DEBUG
862 	if (name != NULL)
863 		printf("name=%s ", name);
864 	if (type != NULL)
865 		printf("type=%s ", type);
866 	if (model != NULL)
867 		printf("model=%s", model);
868 	printf("\n");
869 
870 	if (model == NULL)
871 		model = "";
872 #endif
873 	if (type == NULL)
874 		type = "";
875 	if (name != NULL) {
876 		if (has_board_num(pnode)) {
877 			add_node(tree, pnode);
878 			board_node = 1;
879 #ifdef DEBUG
880 			printf("ADDED BOARD name=%s type=%s model=%s\n",
881 			    name, type, model);
882 #endif
883 		} else if ((strcmp(name, FFB_NAME)  == 0)		||
884 		    (strcmp(type, "cpu") == 0)				||
885 
886 		    ((strcmp(name, "pci") == 0) && (model != NULL) &&
887 		    (strcmp(model, "SUNW,psycho") == 0))		||
888 
889 		    ((strcmp(name, "pci") == 0) && (model != NULL) &&
890 		    (strcmp(model, "SUNW,sabre") == 0))			||
891 
892 		    (strcmp(name, "counter-timer") == 0)		||
893 		    (strcmp(name, "sbus") == 0)				||
894 		    (strcmp(name, "memory") == 0)			||
895 		    (strcmp(name, "mc") == 0)				||
896 		    (strcmp(name, "associations") == 0)) {
897 			add_node(tree, pnode);
898 			board_node = 1;
899 #ifdef DEBUG
900 			printf("ADDED BOARD name=%s type=%s model=%s\n",
901 			    name, type, model);
902 #endif
903 		}
904 #ifdef DEBUG
905 		else
906 			printf("node not added: name=%s type=%s\n", name, type);
907 #endif
908 	}
909 
910 	if (curnode = child(id)) {
911 		pnode->child = walk(tree, pnode, curnode);
912 	}
913 
914 	if (curnode = next(id)) {
915 		if (board_node) {
916 			return (walk(tree, root, curnode));
917 		} else {
918 			pnode->sibling = walk(tree, root, curnode);
919 		}
920 	}
921 
922 	if (board_node) {
923 		return (NULL);
924 	} else {
925 		return (pnode);
926 	}
927 }
928 
929 /*
930  * local functions
931  */
932 
933 /*
934  * disp_envctrl_status
935  *
936  * This routine displays the environmental status passed up from
937  * device drivers via kstats. The kstat names are defined in
938  * kernel header files included by this module.
939  */
940 static int
941 disp_envctrl_status(Sys_tree *tree, struct system_kstat_data *sys_kstats)
942 {
943 	int exit_code = 0;
944 	int possible_failure;
945 	int i;
946 	uchar_t val;
947 	char fan_type[16];
948 	char state[48];
949 	char name[16];
950 	envctrl_ps_t ps;
951 	envctrl_fan_t fan;
952 	envctrl_encl_t encl;
953 	struct envctrl_kstat_data *ep;
954 	uchar_t fsp_value;
955 	int i4slot_backplane_value = -1;
956 	int i8slot_backplane_value = -1;
957 	int j8slot_backplane_value = -1;
958 	static int first_8disk_bp = 0;
959 	static int second_8disk_bp = 0;
960 	static int first_4disk_bp = 0;
961 
962 	if (sys_kstats->envctrl_kstat_ok == 0) {
963 		log_printf("\n", 0);
964 		log_printf(dgettext(TEXT_DOMAIN, "Environmental information "
965 		    "is not available\n"), 0);
966 		log_printf(dgettext(TEXT_DOMAIN, "Environmental driver may "
967 		    "not be installed\n"), 0);
968 		log_printf("\n", 0);
969 		return (1);
970 	}
971 
972 	ep = &sys_kstats->env_data;
973 
974 	check_disk_presence(tree, &first_4disk_bp, &first_8disk_bp,
975 	    &second_8disk_bp);
976 
977 	log_printf("\n", 0);
978 	log_printf("=========================", 0);
979 	log_printf(dgettext(TEXT_DOMAIN, " Environmental Status "), 0);
980 	log_printf("=========================", 0);
981 	log_printf("\n", 0);
982 	log_printf("\n", 0);
983 
984 	log_printf("System Temperatures (Celsius):\n", 0);
985 	log_printf("------------------------------\n", 0);
986 	for (i = 0; i < MAX_DEVS; i++) {
987 		encl = ep->encl_kstats[i];
988 		switch (encl.type) {
989 		case ENVCTRL_ENCL_AMBTEMPR:
990 			if (encl.instance == I2C_NODEV)
991 				continue;
992 			(void) sprintf(name, "%s", "AMBIENT");
993 			log_printf("%s    %d", name, encl.value);
994 			if (encl.value > MAX_AMB_TEMP) {
995 				log_printf("    WARNING\n", 0);
996 				exit_code = 1;
997 			} else
998 				log_printf("\n", 0);
999 			break;
1000 		case ENVCTRL_ENCL_CPUTEMPR:
1001 			if (encl.instance == I2C_NODEV)
1002 				continue;
1003 			(void) sprintf(name, "%s %d", "CPU", encl.instance);
1004 			log_printf("%s      %d", name, encl.value);
1005 			if (encl.value > MAX_CPU_TEMP) {
1006 				log_printf("    WARNING\n", 0);
1007 				exit_code = 1;
1008 			} else
1009 				log_printf("\n", 0);
1010 			break;
1011 		case ENVCTRL_ENCL_FSP:
1012 			if (encl.instance == I2C_NODEV)
1013 				continue;
1014 			val = encl.value & ENVCTRL_FSP_KEYMASK;
1015 			fsp_value = encl.value;
1016 			switch (val) {
1017 			case ENVCTRL_FSP_KEYOFF:
1018 				(void) sprintf(state, "%s", "Off");
1019 				break;
1020 			case ENVCTRL_FSP_KEYON:
1021 				(void) sprintf(state, "%s", "On");
1022 				break;
1023 			case ENVCTRL_FSP_KEYDIAG:
1024 				(void) sprintf(state, "%s", "Diagnostic");
1025 				break;
1026 			case ENVCTRL_FSP_KEYLOCKED:
1027 				(void) sprintf(state, "%s", "Secure");
1028 				break;
1029 			default:
1030 				(void) sprintf(state, "%s", "Broken!");
1031 				exit_code = 1;
1032 				break;
1033 			}
1034 			break;
1035 		case ENVCTRL_ENCL_BACKPLANE4:
1036 		case ENVCTRL_ENCL_BACKPLANE8:
1037 			if (encl.instance == I2C_NODEV)
1038 				continue;
1039 			switch (encl.instance) {
1040 			case 0:
1041 				i4slot_backplane_value =
1042 				    encl.value & ENVCTRL_4SLOT_BACKPLANE;
1043 				break;
1044 			case 1:
1045 				i8slot_backplane_value =
1046 				    encl.value & ENVCTRL_8SLOT_BACKPLANE;
1047 				break;
1048 			case 2:
1049 				j8slot_backplane_value =
1050 				    encl.value & ENVCTRL_8SLOT_BACKPLANE;
1051 				break;
1052 			}
1053 		default:
1054 			break;
1055 		}
1056 	}
1057 
1058 	log_printf("=================================\n\n", 0);
1059 	log_printf("Front Status Panel:\n", 0);
1060 	log_printf("-------------------\n", 0);
1061 	log_printf("Keyswitch position is in %s mode.\n", state);
1062 	log_printf("\n", 0);
1063 	val = fsp_value & (ENVCTRL_FSP_DISK_ERR | ENVCTRL_FSP_PS_ERR |
1064 	    ENVCTRL_FSP_TEMP_ERR | ENVCTRL_FSP_GEN_ERR |
1065 	    ENVCTRL_FSP_ACTIVE);
1066 	log_printf("System LED Status:    POWER     GENERAL ERROR  "
1067 	    "    ACTIVITY\n", 0);
1068 	log_printf("                      [ ON]         [%3s]      "
1069 	    "     [%3s]\n", val & ENVCTRL_FSP_GEN_ERR ? "ON" : "OFF",
1070 	    val & ENVCTRL_FSP_ACTIVE ? "ON" : "OFF");
1071 	log_printf("                    DISK ERROR  "
1072 	    "THERMAL ERROR  POWER SUPPLY ERROR\n", 0);
1073 	log_printf("                      [%3s]         [%3s]      "
1074 	    "     [%3s]\n", val & ENVCTRL_FSP_DISK_ERR ? "ON" : "OFF",
1075 	    val & ENVCTRL_FSP_TEMP_ERR ? "ON" : "OFF",
1076 	    val & ENVCTRL_FSP_PS_ERR ? "ON" : "OFF");
1077 	log_printf("\n", 0);
1078 	/* record error conditions */
1079 	if (val & (ENVCTRL_FSP_GEN_ERR | ENVCTRL_FSP_DISK_ERR |
1080 	    ENVCTRL_FSP_TEMP_ERR | ENVCTRL_FSP_PS_ERR)) {
1081 		exit_code = 1;
1082 	}
1083 
1084 
1085 	log_printf("Disk LED Status:	OK = GREEN	ERROR = YELLOW\n", 0);
1086 
1087 	if (j8slot_backplane_value != -1) {
1088 		log_printf("		DISK 18: %7s	DISK 19: %7s\n",
1089 		    second_8disk_bp & ENVCTRL_DISK_6 ?
1090 		    j8slot_backplane_value & ENVCTRL_DISK_6 ? "[ERROR]" : "[OK]"
1091 		    : "[EMPTY]", second_8disk_bp & ENVCTRL_DISK_7 ?
1092 		    j8slot_backplane_value & ENVCTRL_DISK_7 ? "[ERROR]" : "[OK]"
1093 		    : "[EMPTY]");
1094 		log_printf("		DISK 16: %7s	DISK 17: %7s\n",
1095 		    second_8disk_bp & ENVCTRL_DISK_4 ?
1096 		    j8slot_backplane_value & ENVCTRL_DISK_4 ? "[ERROR]" : "[OK]"
1097 		    : "[EMPTY]", second_8disk_bp & ENVCTRL_DISK_5 ?
1098 		    j8slot_backplane_value & ENVCTRL_DISK_5 ? "[ERROR]" : "[OK]"
1099 		    : "[EMPTY]");
1100 		log_printf("		DISK 14: %7s	DISK 15: %7s\n",
1101 		    second_8disk_bp & ENVCTRL_DISK_2 ?
1102 		    j8slot_backplane_value & ENVCTRL_DISK_2 ? "[ERROR]" : "[OK]"
1103 		    : "[EMPTY]", second_8disk_bp & ENVCTRL_DISK_3 ?
1104 		    j8slot_backplane_value & ENVCTRL_DISK_3 ? "[ERROR]" : "[OK]"
1105 		    : "[EMPTY]");
1106 		log_printf("		DISK 12: %7s	DISK 13: %7s\n",
1107 		    second_8disk_bp & ENVCTRL_DISK_0 ?
1108 		    j8slot_backplane_value & ENVCTRL_DISK_0 ? "[ERROR]" : "[OK]"
1109 		    : "[EMPTY]", second_8disk_bp & ENVCTRL_DISK_1 ?
1110 		    j8slot_backplane_value & ENVCTRL_DISK_1 ? "[ERROR]" : "[OK]"
1111 		    : "[EMPTY]");
1112 	}
1113 	if (i8slot_backplane_value != -1) {
1114 		log_printf("		DISK 10: %7s	DISK 11: %7s\n",
1115 		    first_8disk_bp & ENVCTRL_DISK_6 ?
1116 		    i8slot_backplane_value & ENVCTRL_DISK_6 ? "[ERROR]" : "[OK]"
1117 		    : "[EMPTY]", first_8disk_bp & ENVCTRL_DISK_7 ?
1118 		    i8slot_backplane_value & ENVCTRL_DISK_7 ? "[ERROR]" : "[OK]"
1119 		    : "[EMPTY]");
1120 		log_printf("		DISK  8: %7s	DISK  9: %7s\n",
1121 		    first_8disk_bp & ENVCTRL_DISK_4 ?
1122 		    i8slot_backplane_value & ENVCTRL_DISK_4 ? "[ERROR]" : "[OK]"
1123 		    : "[EMPTY]", first_8disk_bp & ENVCTRL_DISK_5 ?
1124 		    i8slot_backplane_value & ENVCTRL_DISK_5 ? "[ERROR]" : "[OK]"
1125 		    : "[EMPTY]");
1126 		log_printf("		DISK  6: %7s	DISK  7: %7s\n",
1127 		    first_8disk_bp & ENVCTRL_DISK_2 ?
1128 		    i8slot_backplane_value & ENVCTRL_DISK_2 ? "[ERROR]" : "[OK]"
1129 		    : "[EMPTY]", first_8disk_bp & ENVCTRL_DISK_3 ?
1130 		    i8slot_backplane_value & ENVCTRL_DISK_3 ? "[ERROR]" : "[OK]"
1131 		    : "[EMPTY]");
1132 		log_printf("		DISK  4: %7s	DISK  5: %7s\n",
1133 		    first_8disk_bp & ENVCTRL_DISK_0 ?
1134 		    i8slot_backplane_value & ENVCTRL_DISK_0 ? "[ERROR]" : "[OK]"
1135 		    : "[EMPTY]", first_8disk_bp & ENVCTRL_DISK_1 ?
1136 		    i8slot_backplane_value & ENVCTRL_DISK_1 ? "[ERROR]" : "[OK]"
1137 		    : "[EMPTY]");
1138 	}
1139 	if (i4slot_backplane_value != -1) {
1140 		log_printf("		DISK  2: %7s	DISK  3: %7s\n",
1141 		    first_4disk_bp & ENVCTRL_DISK_2 ?
1142 		    i4slot_backplane_value & ENVCTRL_DISK_2 ? "[ERROR]" : "[OK]"
1143 		    : "[EMPTY]", first_4disk_bp & ENVCTRL_DISK_3 ?
1144 		    i4slot_backplane_value & ENVCTRL_DISK_3 ? "[ERROR]" : "[OK]"
1145 		    : "[EMPTY]");
1146 		log_printf("		DISK  0: %7s	DISK  1: %7s\n",
1147 		    first_4disk_bp & ENVCTRL_DISK_0 ?
1148 		    i4slot_backplane_value & ENVCTRL_DISK_0 ? "[ERROR]" : "[OK]"
1149 		    : "[EMPTY]", first_4disk_bp & ENVCTRL_DISK_1 ?
1150 		    i4slot_backplane_value & ENVCTRL_DISK_1 ? "[ERROR]" : "[OK]"
1151 		    : "[EMPTY]");
1152 	}
1153 
1154 	log_printf("=================================\n", 0);
1155 	log_printf("\n", 0);
1156 
1157 	log_printf("Fans:\n", 0);
1158 	log_printf("-----\n", 0);
1159 
1160 	log_printf("Fan Bank   Speed    Status\n", 0);
1161 	log_printf("--------   -----    ------\n", 0);
1162 
1163 	for (i = 0; i < MAX_DEVS; i++) {
1164 		fan = ep->fan_kstats[i];
1165 
1166 		if (fan.instance == I2C_NODEV)
1167 			continue;
1168 
1169 		switch (fan.type) {
1170 		case ENVCTRL_FAN_TYPE_CPU:
1171 			(void) sprintf(fan_type, "%s", "CPU");
1172 			break;
1173 		case ENVCTRL_FAN_TYPE_PS:
1174 			(void) sprintf(fan_type, "%s", "PWR");
1175 			break;
1176 		case ENVCTRL_FAN_TYPE_AFB:
1177 			(void) sprintf(fan_type, "%s", "AFB");
1178 			break;
1179 		}
1180 		if (fan.fans_ok == B_TRUE) {
1181 			(void) sprintf(state, "%s", "  OK  ");
1182 		} else {
1183 			(void) sprintf(state, "FAILED (FAN# %d)",
1184 			    fan.fanflt_num);
1185 			/* we know fan.instance != I2C_NODEV */
1186 			exit_code = 1;
1187 		}
1188 		if (fan.instance != I2C_NODEV)
1189 			log_printf("%s          %d     %s\n", fan_type,
1190 			    fan.fanspeed, state);
1191 	}
1192 
1193 
1194 	log_printf("\n", 0);
1195 
1196 	log_printf("\n", 0);
1197 	log_printf("Power Supplies:\n", 0);
1198 	log_printf("---------------\n", 0);
1199 	log_printf("Supply     Rating    Temp    Status\n", 0);
1200 	log_printf("------     ------    ----    ------\n", 0);
1201 	for (i = 0; i < MAX_DEVS; i++) {
1202 		ps = ep->ps_kstats[i];
1203 		if (ps.curr_share_ok == B_TRUE &&
1204 		    ps.limit_ok == B_TRUE && ps.ps_ok == B_TRUE) {
1205 			(void) sprintf(state, "%s", "  OK  ");
1206 			possible_failure = 0;
1207 		} else {
1208 			if (ps.ps_ok != B_TRUE) {
1209 				(void) sprintf(state, "%s",
1210 				    "FAILED: DC Power Failure");
1211 				possible_failure = 1;
1212 			} else if (ps.curr_share_ok != B_TRUE) {
1213 				(void) sprintf(state, "%s",
1214 				    "WARNING: Current Share Imbalance");
1215 				possible_failure = 1;
1216 			} else if (ps.limit_ok != B_TRUE) {
1217 				(void) sprintf(state, "%s",
1218 				    "WARNING: Current Overload");
1219 				possible_failure = 1;
1220 			}
1221 		}
1222 
1223 		if (ps.instance != I2C_NODEV && ps.ps_rating != 0) {
1224 			log_printf(" %2d        %4d W     %2d     %s\n",
1225 			    ps.instance, ps.ps_rating, ps.ps_tempr, state);
1226 			if (possible_failure)
1227 				exit_code = 1;
1228 		}
1229 	}
1230 
1231 	return (exit_code);
1232 }
1233 
1234 /*
1235  * This function will return a bitmask for each of the 4 disk backplane
1236  * and the two 8 disk backplanes. It creates this mask by first obtaining
1237  * the PROM path of the controller for each slot using the "slot2dev"
1238  * node in the PROM tree. It then modifies the PROM path to obtain a
1239  * physical device path to the controller. The presence of the controller
1240  * is determined by trying to open the controller device and reading
1241  * some information from the device. Currently only supported on Tazmo.
1242  */
1243 static void
1244 check_disk_presence(Sys_tree *tree, int *i4disk, int *i8disk, int *j8disk)
1245 {
1246 	Board_node *bnode;
1247 	Prom_node *slot2disk = NULL;
1248 	Prop *slotprop;
1249 	char *devpath_p;
1250 	char devpath[MAXSTRLEN];
1251 	char slotx[16] = "";
1252 	int slot;
1253 	int slot_ptr = 0;
1254 
1255 	bnode = tree->bd_list;
1256 	*i4disk = *i8disk, *j8disk = 0;
1257 
1258 	slot2disk = dev_find_node(bnode->nodes, "slot2disk");
1259 
1260 	for (slot = 0; slot < 20; slot++) {
1261 		(void) sprintf(slotx, "slot#%d", slot);
1262 		if ((slotprop = find_prop(slot2disk, slotx)) != NULL)
1263 			if ((devpath_p = (char *)(get_prop_val(slotprop)))
1264 			    != NULL) {
1265 				modify_device_path(devpath_p, devpath);
1266 				if (disk_present(devpath)) {
1267 					if (slot < 4)
1268 						*i4disk |= 1 << slot_ptr;
1269 					else if (slot < 12)
1270 						*i8disk |= 1 << slot_ptr;
1271 					else if (slot < 20)
1272 						*j8disk |= 1 << slot_ptr;
1273 				}
1274 			}
1275 		if ((slot == 3) || (slot == 11))
1276 			slot_ptr = 0;
1277 		else
1278 			slot_ptr++;
1279 	}
1280 }
1281 
1282 
1283 
1284 /*
1285  * modify_device_path
1286  *
1287  * This function modifies a string from the slot2disk association
1288  * PROM node to a physical device path name. For example if the
1289  * slot2disk association value is  "/pci@1f,4000/scsi@3/disk@1",
1290  * the equivalent physical device path will be
1291  * "/devices/pci@1f,4000/scsi@3/sd@1,0:c,raw".
1292  * We use this path to attempt to probe the disk to check for its
1293  * presence in the enclosure. We access the 'c' partition
1294  * which represents the entire disk.
1295  */
1296 static void
1297 modify_device_path(char *oldpath, char *newpath)
1298 {
1299 	char *changeptr;
1300 	long target;
1301 	char targetstr[16];
1302 
1303 	(void) strcpy(newpath, "/devices");
1304 	changeptr = strstr(oldpath, "disk@");
1305 	/*
1306 	 * The assumption here is that nothing but the
1307 	 * target id follows the disk@ substring.
1308 	 */
1309 	target = strtol(changeptr+5, NULL, 16);
1310 	(void) strncat(newpath, oldpath, changeptr - oldpath);
1311 	(void) sprintf(targetstr, "sd@%ld,0:c,raw", target);
1312 	(void) strcat(newpath, targetstr);
1313 }
1314 
1315 /*
1316  * Returns 0 if the device at devpath is not *physically* present.  If it is,
1317  * then info on that device is placed in the dkinfop buffer, and 1 is returned.
1318  * Keep in mind that ioctl(DKIOCINFO)'s CDROMs owned by vold fail, so only
1319  * the dki_ctype field is set in that case.
1320  */
1321 static int
1322 disk_present(char *devpath)
1323 {
1324 	int		search_file;
1325 	struct stat	stbuf;
1326 	struct dk_cinfo dkinfo;
1327 
1328 	/*
1329 	 * Attempt to open the disk.  If it fails, skip it.
1330 	 */
1331 	if ((search_file = open(devpath, O_RDONLY | O_NDELAY)) < 0)
1332 		return (0);
1333 
1334 	/*
1335 	 * Must be a character device
1336 	 */
1337 	if (fstat(search_file, &stbuf) == -1 || !S_ISCHR(stbuf.st_mode)) {
1338 		(void) close(search_file);
1339 		return (0);
1340 	}
1341 
1342 	/*
1343 	 * Attempt to read the configuration info on the disk.
1344 	 * If it fails, we assume the disk's not there.
1345 	 * Note we must close the file for the disk before we
1346 	 * continue.
1347 	 */
1348 	if (ioctl(search_file, DKIOCINFO, &dkinfo) < 0) {
1349 		(void) close(search_file);
1350 		return (0);
1351 	}
1352 	(void) close(search_file);
1353 	return (1);
1354 }
1355 
1356 void
1357 tazjav_disp_asic_revs(Sys_tree *tree)
1358 {
1359 	Board_node *bnode;
1360 	Prom_node *pnode;
1361 	char *name;
1362 	int *version;
1363 	char *model;
1364 
1365 	/* Print the header */
1366 	log_printf("\n", 0);
1367 	log_printf("=========================", 0);
1368 	log_printf(dgettext(TEXT_DOMAIN, " HW Revisions "), 0);
1369 	log_printf("=========================", 0);
1370 	log_printf("\n", 0);
1371 	log_printf("\n", 0);
1372 
1373 	bnode = tree->bd_list;
1374 
1375 	log_printf("ASIC Revisions:\n", 0);
1376 	log_printf("---------------\n", 0);
1377 
1378 	/* Find sysio and print rev */
1379 	for (pnode = dev_find_node(bnode->nodes, "sbus"); pnode != NULL;
1380 	    pnode = dev_next_node(pnode, "sbus")) {
1381 		version = (int *)get_prop_val(find_prop(pnode, "version#"));
1382 		name = get_prop_val(find_prop(pnode, "name"));
1383 
1384 		if ((version != NULL) && (name != NULL)) {
1385 			log_printf("SBus: %s Rev %d\n",
1386 			    name, *version, 0);
1387 		}
1388 	}
1389 
1390 	/* Find Psycho and print rev */
1391 	for (pnode = dev_find_node(bnode->nodes, "pci"); pnode != NULL;
1392 	    pnode = dev_next_node(pnode, "pci")) {
1393 		Prom_node *parsib = pnode->parent->sibling;
1394 
1395 		if (find_prop(pnode, "upa-portid") == NULL) {
1396 			if ((parsib != NULL) &&
1397 			    (strcmp(get_prop_val(
1398 			    find_prop(parsib, "name")),
1399 			    PCI_NAME) == 0))
1400 				pnode = parsib;
1401 			else {
1402 				pnode = parsib;
1403 				continue;
1404 			}
1405 		}
1406 
1407 		version = (int *)get_prop_val(find_prop(pnode, "version#"));
1408 		name = get_prop_val(find_prop(pnode, "name"));
1409 
1410 		if ((version != NULL) && (name != NULL))
1411 			if (get_pci_bus(pnode) == 0)
1412 				log_printf("STP2223BGA: Rev %d\n", *version, 0);
1413 	}
1414 
1415 	/* Find Cheerio and print rev */
1416 	for (pnode = dev_find_node(bnode->nodes, "ebus"); pnode != NULL;
1417 	    pnode = dev_next_node(pnode, "ebus")) {
1418 		version = (int *)get_prop_val(find_prop(pnode, "revision-id"));
1419 		name = get_prop_val(find_prop(pnode, "name"));
1420 
1421 		if ((version != NULL) && (name != NULL))
1422 			log_printf("STP2003QFP: Rev %d\n", *version, 0);
1423 	}
1424 
1425 	/* Find System Controller and print rev */
1426 	for (pnode = dev_find_node(bnode->nodes, "sc"); pnode != NULL;
1427 	    pnode = dev_next_node(pnode, "sc")) {
1428 		version = (int *)get_prop_val(find_prop(pnode, "version#"));
1429 		model = (char *)get_prop_val(find_prop(pnode, "model"));
1430 		name = get_prop_val(find_prop(pnode, "name"));
1431 
1432 		if ((version != NULL) && (name != NULL)) {
1433 			if ((strcmp(model, "SUNW,sc-marvin") == 0))
1434 				log_printf("STP2205BGA: Rev %d\n", *version, 0);
1435 		}
1436 	}
1437 
1438 	/* Find the FEPS and print rev */
1439 	for (pnode = dev_find_node(bnode->nodes, "SUNW,hme"); pnode != NULL;
1440 	    pnode = dev_next_node(pnode, "SUNW,hme")) {
1441 		version = (int *)get_prop_val(find_prop(pnode,	"hm-rev"));
1442 		name = get_prop_val(find_prop(pnode, "name"));
1443 
1444 		if ((version != NULL) && (name != NULL)) {
1445 			log_printf("FEPS: %s Rev ", name);
1446 			if (*version == 0xa0) {
1447 				log_printf("2.0\n", 0);
1448 			} else if (*version == 0x20) {
1449 				log_printf("2.1\n", 0);
1450 			} else {
1451 				log_printf("%x\n", *version, 0);
1452 			}
1453 		}
1454 	}
1455 	log_printf("\n", 0);
1456 
1457 	if (dev_find_node(bnode->nodes, FFB_NAME) != NULL) {
1458 		display_ffb(bnode, 0);
1459 	}
1460 }
1461 
1462 
1463 /*
1464  * Determine the physical PCI slot based on which Psycho is the parent
1465  * of the PCI card.
1466  */
1467 static int
1468 tazmo_physical_slot(Prom_node *slotd, Prom_node *parent, int device, char *str)
1469 {
1470 	int *upa_id = NULL;
1471 	int *reg = NULL;
1472 	int offset;
1473 	char controller[MAXSTRLEN];
1474 	char *name;
1475 	Prop *prop;
1476 	char *devpath_p;
1477 	char slotx[16] = "";
1478 	int *slot_names_mask;
1479 	char *slot_names;
1480 	int shift = 0;
1481 	int slot;
1482 	int slots, start_slot;
1483 
1484 	/*
1485 	 * If slotd != NULL, then we must return the physical PCI slot
1486 	 * number based on the information in the slot2dev associations
1487 	 * node. This routine is called from display_pci() with slotd
1488 	 * != NULL. If so, we return without obtaining the slot name.
1489 	 * If slotd == NULL, we look for the slot name through the
1490 	 * slot-names property in the bus node.
1491 	 */
1492 
1493 	if (slotd != NULL) {
1494 		(void) strcpy(str, "");
1495 		if ((prop = find_prop(parent, "upa-portid")) != NULL)
1496 			upa_id = (int *)(get_prop_val(prop));
1497 		if ((prop = find_prop(parent, "reg")) != NULL)
1498 			reg = (int *)(get_prop_val(prop));
1499 		if ((prop = find_prop(parent, "name")) != NULL)
1500 			name = (char *)(get_prop_val(prop));
1501 		if ((upa_id == NULL) || (reg == NULL)) {
1502 			return (-1);
1503 		}
1504 		offset = reg[1];
1505 		if (strcmp(name, "pci") == 0) {
1506 			(void) sprintf(controller, "/pci@%x,%x/*@%x,*",
1507 			    *upa_id, offset, device);
1508 			slots = 20;
1509 		} else if (strcmp(name, "SUNW,ffb") == 0) {
1510 			(void) sprintf(controller, "/*@%x,0", *upa_id);
1511 			slots = 2;
1512 		}
1513 
1514 		start_slot = 1;
1515 		for (slot = start_slot; slot <= slots; slot++) {
1516 			if (strcmp(name, "pci") == 0)
1517 				(void) sprintf(slotx, "pci-slot#%d", slot);
1518 			else if (strcmp(name, "SUNW,ffb") == 0)
1519 				(void) sprintf(slotx, "graphics#%d", slot);
1520 			if ((prop = find_prop(slotd, slotx)) != NULL)
1521 				if ((devpath_p = (char *)(get_prop_val
1522 				    (prop))) != NULL)
1523 					if (strcmp(devpath_p, controller) == 0)
1524 						return (slot);
1525 		}
1526 		return (-1);
1527 	}
1528 
1529 	/*
1530 	 * Get slot-names property from parent node.
1531 	 * This property consists of a 32 bit mask indicating which
1532 	 * devices are relevant to this bus node. Following are a
1533 	 * number of strings depending on how many bits are set in the
1534 	 * bit mask; the first string gives the label that is printed
1535 	 * on the chassis for the smallest device number, and so on.
1536 	 */
1537 
1538 	prop = find_prop(parent, "slot-names");
1539 	if (prop == NULL) {
1540 		(void) strcpy(str, "");
1541 		return (-1);
1542 	}
1543 	slot_names_mask = (int *)(get_prop_val(prop));
1544 	slot_names = (char *)slot_names_mask;
1545 
1546 	slot = 1;
1547 	slot_names += 4;	/* Skip the 4 byte bitmask */
1548 
1549 	while (shift < 32) {
1550 		/*
1551 		 * Shift through the bitmask looking to see if the
1552 		 * bit corresponding to "device" is set. If so, copy
1553 		 * the correcsponding string to the provided pointer.
1554 		 */
1555 		if (*slot_names_mask & slot) {
1556 			if (shift == device) {
1557 				(void) strcpy(str, slot_names);
1558 				return (0);
1559 			}
1560 			slot_names += strlen(slot_names)+1;
1561 		}
1562 		shift++;
1563 		slot = slot << 1;
1564 	}
1565 	return (-1);
1566 }
1567