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