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