103831d35Sstevel /*
203831d35Sstevel  * CDDL HEADER START
303831d35Sstevel  *
403831d35Sstevel  * The contents of this file are subject to the terms of the
503831d35Sstevel  * Common Development and Distribution License, Version 1.0 only
603831d35Sstevel  * (the "License").  You may not use this file except in compliance
703831d35Sstevel  * with the License.
803831d35Sstevel  *
903831d35Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
1003831d35Sstevel  * or http://www.opensolaris.org/os/licensing.
1103831d35Sstevel  * See the License for the specific language governing permissions
1203831d35Sstevel  * and limitations under the License.
1303831d35Sstevel  *
1403831d35Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
1503831d35Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1603831d35Sstevel  * If applicable, add the following below this CDDL HEADER, with the
1703831d35Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
1803831d35Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
1903831d35Sstevel  *
2003831d35Sstevel  * CDDL HEADER END
2103831d35Sstevel  */
2203831d35Sstevel /*
2303831d35Sstevel  * Copyright 1999-2002 Sun Microsystems, Inc.  All rights reserved.
2403831d35Sstevel  * Use is subject to license terms.
25*2bcbf80cSPeter Tribble  * Copyright 2020 Peter Tribble.
2603831d35Sstevel  *
2703831d35Sstevel  * Tazmo Platform specific functions.
2803831d35Sstevel  *
29aa5636e5SPeter Tribble  *	called when :
30aa5636e5SPeter Tribble  *	machine_type == MTYPE_TAZMO
3103831d35Sstevel  *
3203831d35Sstevel  */
3303831d35Sstevel 
3403831d35Sstevel #include <stdio.h>
3503831d35Sstevel #include <stdlib.h>
3603831d35Sstevel #include <unistd.h>
3703831d35Sstevel #include <ctype.h>
3803831d35Sstevel #include <string.h>
3903831d35Sstevel #include <kvm.h>
4003831d35Sstevel #include <varargs.h>
4103831d35Sstevel #include <errno.h>
4203831d35Sstevel #include <time.h>
4303831d35Sstevel #include <dirent.h>
4403831d35Sstevel #include <fcntl.h>
4503831d35Sstevel #include <sys/param.h>
4603831d35Sstevel #include <sys/stat.h>
4703831d35Sstevel #include <sys/types.h>
4803831d35Sstevel #include <sys/utsname.h>
4903831d35Sstevel #include <sys/openpromio.h>
5003831d35Sstevel #include <kstat.h>
5103831d35Sstevel #include <libintl.h>
5203831d35Sstevel #include <syslog.h>
5303831d35Sstevel #include <sys/dkio.h>
5403831d35Sstevel #include "pdevinfo.h"
5503831d35Sstevel #include "display.h"
5603831d35Sstevel #include "pdevinfo_sun4u.h"
5703831d35Sstevel #include "display_sun4u.h"
5803831d35Sstevel #include "libprtdiag.h"
5903831d35Sstevel 
6003831d35Sstevel #if !defined(TEXT_DOMAIN)
6103831d35Sstevel #define	TEXT_DOMAIN	"SYS_TEST"
6203831d35Sstevel #endif
6303831d35Sstevel 
6403831d35Sstevel extern	int	print_flag;
6503831d35Sstevel 
6603831d35Sstevel /*
6703831d35Sstevel  * these functions will overlay the symbol table of libprtdiag
6803831d35Sstevel  * at runtime (workgroup server systems only)
6903831d35Sstevel  */
7003831d35Sstevel int	error_check(Sys_tree *tree, struct system_kstat_data *kstats);
71*2bcbf80cSPeter Tribble void	display_memoryconf(Sys_tree *tree);
7203831d35Sstevel int	disp_fail_parts(Sys_tree *tree);
7303831d35Sstevel void	display_hp_fail_fault(Sys_tree *tree, struct system_kstat_data *kstats);
7403831d35Sstevel void	display_diaginfo(int flag, Prom_node *root, Sys_tree *tree,
7503831d35Sstevel 				struct system_kstat_data *kstats);
7603831d35Sstevel void	display_boardnum(int num);
77aa5636e5SPeter Tribble void	display_pci(Board_node *);
7803831d35Sstevel void	display_io_cards(struct io_card *list);
79aa5636e5SPeter Tribble void	display_ffb(Board_node *, int);
8003831d35Sstevel void	read_platform_kstats(Sys_tree *tree,
8103831d35Sstevel 		struct system_kstat_data *sys_kstat,
82*2bcbf80cSPeter Tribble 		struct envctrl_kstat_data *ep);
8303831d35Sstevel 
8403831d35Sstevel /* local functions */
8503831d35Sstevel static	int disp_envctrl_status(Sys_tree *, struct system_kstat_data *);
8603831d35Sstevel static	void check_disk_presence(Sys_tree *, int *, int *, int *);
8703831d35Sstevel static	void modify_device_path(char *, char *);
8803831d35Sstevel static	int disk_present(char *);
8903831d35Sstevel static	void tazjav_disp_asic_revs(Sys_tree *);
90aa5636e5SPeter Tribble static	int tazmo_physical_slot(Prom_node *, Prom_node *, int, char *);
9103831d35Sstevel static	Prom_node *dev_next_node_sibling(Prom_node *root, char *name);
9203831d35Sstevel 
9303831d35Sstevel 
9403831d35Sstevel int
error_check(Sys_tree * tree,struct system_kstat_data * kstats)9503831d35Sstevel error_check(Sys_tree *tree, struct system_kstat_data *kstats)
9603831d35Sstevel {
9703831d35Sstevel 	int exit_code = 0;	/* init to all OK */
9803831d35Sstevel 
9903831d35Sstevel #ifdef	lint
10003831d35Sstevel 	kstats = kstats;
10103831d35Sstevel #endif
10203831d35Sstevel 	/*
10303831d35Sstevel 	 * silently check for any types of machine errors
10403831d35Sstevel 	 */
10503831d35Sstevel 	print_flag = 0;
10603831d35Sstevel 	if (disp_fail_parts(tree) || disp_envctrl_status(tree, kstats)) {
10703831d35Sstevel 		/* set exit_code to show failures */
10803831d35Sstevel 		exit_code = 1;
10903831d35Sstevel 	}
11003831d35Sstevel 	print_flag = 1;
11103831d35Sstevel 
11203831d35Sstevel 	return (exit_code);
11303831d35Sstevel }
11403831d35Sstevel 
11503831d35Sstevel /* Search for and return the node's sibling */
11603831d35Sstevel static Prom_node *
dev_next_node_sibling(Prom_node * root,char * name)11703831d35Sstevel dev_next_node_sibling(Prom_node *root, char *name)
11803831d35Sstevel {
11903831d35Sstevel 	if (root == NULL)
12003831d35Sstevel 		return (NULL);
12103831d35Sstevel 
12203831d35Sstevel 	/* look at your siblings */
12303831d35Sstevel 	if (dev_find_node(root->sibling, name) != NULL)
12403831d35Sstevel 		return (root->sibling);
12503831d35Sstevel 
12603831d35Sstevel 	return (NULL);  /* not found */
12703831d35Sstevel }
12803831d35Sstevel 
12903831d35Sstevel /*
13003831d35Sstevel  * This function displays memory configurations specific to Tazmo/Javelin.
13103831d35Sstevel  * The PROM device tree is read to obtain this information.
13203831d35Sstevel  * Some of the information obtained is memory interleave factor,
13303831d35Sstevel  * DIMM sizes, DIMM socket names.
13403831d35Sstevel  */
13503831d35Sstevel void
display_memoryconf(Sys_tree * tree)136*2bcbf80cSPeter Tribble display_memoryconf(Sys_tree *tree)
13703831d35Sstevel {
13803831d35Sstevel 	Board_node *bnode;
13903831d35Sstevel 	Prom_node *memory;
14003831d35Sstevel 	Prom_node *bank;
14103831d35Sstevel 	Prom_node *dimm;
14203831d35Sstevel 	uint_t *preg;
14303831d35Sstevel 	uint_t interlv;
14403831d35Sstevel 	unsigned long size = 0;
14503831d35Sstevel 	int bank_count = 0;
14603831d35Sstevel 	char *sock_name;
14703831d35Sstevel 	char *status;
14803831d35Sstevel 	Prop *status_prop;
14903831d35Sstevel 	char interleave[8];
15003831d35Sstevel 	int total_size = 0;
15103831d35Sstevel 
15203831d35Sstevel 	log_printf("\n", 0);
15303831d35Sstevel 	log_printf("=========================", 0);
15403831d35Sstevel 	log_printf(dgettext(TEXT_DOMAIN, " Memory "), 0);
15503831d35Sstevel 	log_printf("=========================", 0);
15603831d35Sstevel 	log_printf("\n", 0);
15703831d35Sstevel 	log_printf("\n", 0);
15803831d35Sstevel 	bnode = tree->bd_list;
15903831d35Sstevel 	memory = dev_find_node(bnode->nodes, "memory");
16003831d35Sstevel 	preg = (uint_t *)(get_prop_val(find_prop(memory, "interleave")));
16103831d35Sstevel 	if (preg) {
16203831d35Sstevel 		interlv = preg[4];
16303831d35Sstevel 		log_printf("Memory Interleave Factor = %d-way\n\n", interlv, 0);
16403831d35Sstevel 	}
16503831d35Sstevel 	log_printf("       Interlv.  Socket   Size\n", 0);
16603831d35Sstevel 	log_printf("Bank    Group     Name    (MB)  Status\n", 0);
16703831d35Sstevel 	log_printf("----    -----    ------   ----  ------\n", 0);
16803831d35Sstevel 
16903831d35Sstevel 	dimm = bnode->nodes;
17003831d35Sstevel 	for (bank = dev_find_node(bnode->nodes, "bank"); bank != NULL;
171aa5636e5SPeter Tribble 	    bank = dev_next_node(bank, "bank")) {
17203831d35Sstevel 		int bank_size = 0;
17303831d35Sstevel 		uint_t *reg_prop;
17403831d35Sstevel 
17503831d35Sstevel 		preg = (uint_t *)(get_prop_val(
176aa5636e5SPeter Tribble 		    find_prop(bank, "bank-interleave")));
17703831d35Sstevel 
17803831d35Sstevel 		reg_prop = (uint_t *)(get_prop_val(
179aa5636e5SPeter Tribble 		    find_prop(bank, "reg")));
18003831d35Sstevel 
18103831d35Sstevel 		/*
18203831d35Sstevel 		 * Skip empty banks
18303831d35Sstevel 		 */
18403831d35Sstevel 		if (((reg_prop[2]<<12) + (reg_prop[3]>>20)) == 0) {
18503831d35Sstevel 			bank_count++;
18603831d35Sstevel 			continue;
18703831d35Sstevel 		}
18803831d35Sstevel 
18903831d35Sstevel 		if (preg) {
19003831d35Sstevel 			interlv = preg[2];
19103831d35Sstevel 			(void) sprintf(interleave, " %d ", interlv);
19203831d35Sstevel 			bank_size = (preg[0]<<12) + (preg[1]>>20);
19303831d35Sstevel 		} else {
19403831d35Sstevel 			(void) sprintf(interleave, "%s", "none");
19503831d35Sstevel 			preg = (uint_t *)(get_prop_val(find_prop(bank, "reg")));
19603831d35Sstevel 			if (preg) {
19703831d35Sstevel 				bank_size = (preg[2]<<12) + (preg[3]>>20);
19803831d35Sstevel 			}
19903831d35Sstevel 		}
20003831d35Sstevel 		for (dimm = dev_find_node(bank, "dimm"); dimm != NULL;
201aa5636e5SPeter Tribble 		    dimm = dev_next_node_sibling(dimm, "dimm")) {
20203831d35Sstevel 			char dimm_status[16];
20303831d35Sstevel 
20403831d35Sstevel 			sock_name = (char *)(get_prop_val(
205aa5636e5SPeter Tribble 			    find_prop(dimm, "socket-name")));
20603831d35Sstevel 			preg = (uint_t *)(get_prop_val(find_prop(dimm, "reg")));
20703831d35Sstevel 			size = (preg[2]<<12) + (preg[3]>>20);
20803831d35Sstevel 			if ((status_prop = find_prop(dimm, "status")) == NULL) {
20903831d35Sstevel 				(void) sprintf(dimm_status, "%s", "OK");
21003831d35Sstevel 			} else {
21103831d35Sstevel 				status = (char *)(get_prop_val(status_prop));
21203831d35Sstevel 				(void) sprintf(dimm_status, "%s", status);
21303831d35Sstevel 			}
21403831d35Sstevel 			log_printf("%3d     %5s    %6s  %4d  %6s\n",
215aa5636e5SPeter Tribble 			    bank_count, interleave, sock_name,
216aa5636e5SPeter Tribble 			    size, dimm_status, 0);
21703831d35Sstevel 		}
21803831d35Sstevel 		total_size += bank_size;
21903831d35Sstevel 		bank_count++;
22003831d35Sstevel 	}
22103831d35Sstevel 	log_printf("\n", 0);
22203831d35Sstevel }
22303831d35Sstevel 
22403831d35Sstevel /*
22503831d35Sstevel  * disp_fail_parts
22603831d35Sstevel  *
22703831d35Sstevel  * Display the failed parts in the system. This function looks for
22803831d35Sstevel  * the status property in all PROM nodes. On systems where
22903831d35Sstevel  * the PROM does not supports passing diagnostic information
23003831d35Sstevel  * thruogh the device tree, this routine will be silent.
23103831d35Sstevel  */
23203831d35Sstevel int
disp_fail_parts(Sys_tree * tree)23303831d35Sstevel disp_fail_parts(Sys_tree *tree)
23403831d35Sstevel {
23503831d35Sstevel 	int exit_code;
23603831d35Sstevel 	int system_failed = 0;
23703831d35Sstevel 	Board_node *bnode = tree->bd_list;
23803831d35Sstevel 	Prom_node *pnode;
23903831d35Sstevel 	char *fru;
24003831d35Sstevel 	char *sock_name;
24103831d35Sstevel 	char slot_str[MAXSTRLEN];
24203831d35Sstevel 
24303831d35Sstevel 	exit_code = 0;
24403831d35Sstevel 
24503831d35Sstevel 	/* go through all of the boards looking for failed units. */
24603831d35Sstevel 	while (bnode != NULL) {
24703831d35Sstevel 		/* find failed chips */
24803831d35Sstevel 		pnode = find_failed_node(bnode->nodes);
24903831d35Sstevel 		if ((pnode != NULL) && !system_failed) {
25003831d35Sstevel 			system_failed = 1;
25103831d35Sstevel 			exit_code = 1;
25203831d35Sstevel 			if (print_flag == 0) {
25303831d35Sstevel 				return (exit_code);
25403831d35Sstevel 			}
25503831d35Sstevel 			log_printf("\n", 0);
25603831d35Sstevel 			log_printf(dgettext(TEXT_DOMAIN, "Failed Field "
257aa5636e5SPeter Tribble 			    "Replaceable Units (FRU) in System:\n"), 0);
25803831d35Sstevel 			log_printf("=========================="
259aa5636e5SPeter Tribble 			    "====================\n", 0);
26003831d35Sstevel 		}
26103831d35Sstevel 
26203831d35Sstevel 		while (pnode != NULL) {
26303831d35Sstevel 			void *value;
26403831d35Sstevel 			char *name;		/* node name string */
26503831d35Sstevel 			char *type;		/* node type string */
26603831d35Sstevel 
26703831d35Sstevel 			value = get_prop_val(find_prop(pnode, "status"));
26803831d35Sstevel 			name = get_node_name(pnode);
26903831d35Sstevel 
27003831d35Sstevel 			/* sanity check of data retreived from PROM */
27103831d35Sstevel 			if ((value == NULL) || (name == NULL)) {
27203831d35Sstevel 				pnode = next_failed_node(pnode);
27303831d35Sstevel 				continue;
27403831d35Sstevel 			}
27503831d35Sstevel 
27603831d35Sstevel 
27703831d35Sstevel 			log_printf(dgettext(TEXT_DOMAIN, "%s unavailable :\n"),
278aa5636e5SPeter Tribble 			    name, 0);
27903831d35Sstevel 
28003831d35Sstevel 			log_printf(dgettext(TEXT_DOMAIN,
281aa5636e5SPeter Tribble 			    "\tPROM fault string: %s\n"),
282aa5636e5SPeter Tribble 			    value, 0);
28303831d35Sstevel 
28403831d35Sstevel 			log_printf(dgettext(TEXT_DOMAIN,
285aa5636e5SPeter Tribble 			    "\tFailed Field Replaceable Unit is "), 0);
28603831d35Sstevel 
28703831d35Sstevel 			/*
28803831d35Sstevel 			 * Determine whether FRU is CPU module, system
28903831d35Sstevel 			 * board, or SBus card.
29003831d35Sstevel 			 */
29103831d35Sstevel 			if ((name != NULL) && (strstr(name, "sbus"))) {
29203831d35Sstevel 
29303831d35Sstevel 				log_printf(dgettext(TEXT_DOMAIN, "SBus "
294aa5636e5SPeter Tribble 				    "Card %d\n"), get_sbus_slot(pnode), 0);
29503831d35Sstevel 
29603831d35Sstevel 			} else if (((name = get_node_name(pnode)) !=
29703831d35Sstevel 			    NULL) && (strstr(name, "pci"))) {
29803831d35Sstevel 
29903831d35Sstevel 				log_printf(dgettext(TEXT_DOMAIN,
300aa5636e5SPeter Tribble 				    "system board\n"), 0);
30103831d35Sstevel 
30203831d35Sstevel 			} else if (((name = get_node_name(pnode)) !=
30303831d35Sstevel 			    NULL) && (strstr(name, "ffb"))) {
30403831d35Sstevel 
30503831d35Sstevel 				log_printf(dgettext(TEXT_DOMAIN,
306aa5636e5SPeter Tribble 				    "FFB Card %d\n"),
307aa5636e5SPeter Tribble 				    tazmo_physical_slot(
308aa5636e5SPeter Tribble 				    dev_find_node(bnode->nodes, "slot2dev"),
309aa5636e5SPeter Tribble 				    pnode, -1, slot_str), 0);
31003831d35Sstevel 
31103831d35Sstevel 			} else if (((name = get_node_name(pnode->parent)) !=
31203831d35Sstevel 			    NULL) && (strstr(name, "pci"))) {
31303831d35Sstevel 
31403831d35Sstevel 				(void) tazmo_physical_slot(
315aa5636e5SPeter Tribble 				    NULL,
316aa5636e5SPeter Tribble 				    pnode->parent,
317aa5636e5SPeter Tribble 				    get_pci_device(pnode),
318aa5636e5SPeter Tribble 				    slot_str);
31903831d35Sstevel 				log_printf(dgettext(TEXT_DOMAIN,
320aa5636e5SPeter Tribble 				    "PCI Card in %s\n"), slot_str, 0);
32103831d35Sstevel 
32203831d35Sstevel 			} else if (((type = get_node_type(pnode)) != NULL) &&
32303831d35Sstevel 			    (strstr(type, "cpu"))) {
32403831d35Sstevel 
32503831d35Sstevel 				log_printf(
326aa5636e5SPeter Tribble 				    dgettext(TEXT_DOMAIN,
327aa5636e5SPeter Tribble 				    "UltraSPARC module Module %d\n"),
328aa5636e5SPeter Tribble 				    get_id(pnode));
32903831d35Sstevel 
33003831d35Sstevel 			} else if (((type = get_node_type(pnode)) != NULL) &&
33103831d35Sstevel 			    (strstr(type, "memory-module"))) {
33203831d35Sstevel 
33303831d35Sstevel 				fru = (char *)(get_prop_val(
334aa5636e5SPeter Tribble 				    find_prop(pnode, "fru")));
33503831d35Sstevel 				sock_name = (char *)(get_prop_val(
336aa5636e5SPeter Tribble 				    find_prop(pnode, "socket-name")));
33703831d35Sstevel 				log_printf(
338aa5636e5SPeter Tribble 				    dgettext(TEXT_DOMAIN,
339aa5636e5SPeter Tribble 				    "%s in socket %s\n"),
340aa5636e5SPeter Tribble 				    fru, sock_name, 0);
34103831d35Sstevel 			}
34203831d35Sstevel 			pnode = next_failed_node(pnode);
34303831d35Sstevel 		}
34403831d35Sstevel 		bnode = bnode->next;
34503831d35Sstevel 	}
34603831d35Sstevel 
34703831d35Sstevel 	if (!system_failed) {
34803831d35Sstevel 		log_printf("\n", 0);
34903831d35Sstevel 		log_printf(dgettext(TEXT_DOMAIN,
350aa5636e5SPeter Tribble 		    "No failures found in System\n"), 0);
35103831d35Sstevel 		log_printf("===========================\n", 0);
35203831d35Sstevel 	}
35303831d35Sstevel 
35403831d35Sstevel 	if (system_failed)
35503831d35Sstevel 		return (1);
35603831d35Sstevel 	else
35703831d35Sstevel 		return (0);
35803831d35Sstevel }
35903831d35Sstevel 
36003831d35Sstevel void
display_hp_fail_fault(Sys_tree * tree,struct system_kstat_data * kstats)36103831d35Sstevel display_hp_fail_fault(Sys_tree *tree, struct system_kstat_data *kstats)
36203831d35Sstevel {
36303831d35Sstevel #ifdef lint
36403831d35Sstevel 	kstats = kstats;
36503831d35Sstevel #endif
36603831d35Sstevel 	/* Display failed units */
36703831d35Sstevel 	(void) disp_fail_parts(tree);
36803831d35Sstevel }
36903831d35Sstevel 
37003831d35Sstevel 
37103831d35Sstevel void
display_diaginfo(int flag,Prom_node * root,Sys_tree * tree,struct system_kstat_data * kstats)37203831d35Sstevel display_diaginfo(int flag, Prom_node *root, Sys_tree *tree,
373aa5636e5SPeter Tribble     struct system_kstat_data *kstats)
37403831d35Sstevel {
37503831d35Sstevel 	/*
37603831d35Sstevel 	 * Now display the last powerfail time and the fatal hardware
37703831d35Sstevel 	 * reset information. We do this under a couple of conditions.
37803831d35Sstevel 	 * First if the user asks for it. The second is iof the user
37903831d35Sstevel 	 * told us to do logging, and we found a system failure.
38003831d35Sstevel 	 */
38103831d35Sstevel 	if (flag) {
38203831d35Sstevel 		/*
38303831d35Sstevel 		 * display time of latest powerfail. Not all systems
38403831d35Sstevel 		 * have this capability. For those that do not, this
38503831d35Sstevel 		 * is just a no-op.
38603831d35Sstevel 		 */
38703831d35Sstevel 		disp_powerfail(root);
38803831d35Sstevel 
38903831d35Sstevel 		(void) disp_envctrl_status(tree, kstats);
39003831d35Sstevel 
39103831d35Sstevel 		tazjav_disp_asic_revs(tree);
39203831d35Sstevel 
39303831d35Sstevel 		platform_disp_prom_version(tree);
39403831d35Sstevel 	}
39503831d35Sstevel 	return;
39603831d35Sstevel 
39703831d35Sstevel }
39803831d35Sstevel 
39903831d35Sstevel /* ARGSUSED */
40003831d35Sstevel void
display_boardnum(int num)40103831d35Sstevel display_boardnum(int num)
40203831d35Sstevel {
40303831d35Sstevel 	log_printf("SYS   ", 0);
40403831d35Sstevel }
40503831d35Sstevel 
40603831d35Sstevel 
40703831d35Sstevel 
40803831d35Sstevel /*
40903831d35Sstevel  * display_pci
41003831d35Sstevel  * Display all the PCI IO cards on this board.
41103831d35Sstevel  */
41203831d35Sstevel 
41303831d35Sstevel /* ARGSUSED */
41403831d35Sstevel void
display_pci(Board_node * board)41503831d35Sstevel display_pci(Board_node *board)
41603831d35Sstevel {
41703831d35Sstevel 	struct io_card *card_list = NULL;
41803831d35Sstevel 	struct io_card card;
41903831d35Sstevel 	void *value;
42003831d35Sstevel 	Prom_node *pci;
42103831d35Sstevel 	Prom_node *card_node;
42203831d35Sstevel 
42303831d35Sstevel 	if (board == NULL)
42403831d35Sstevel 		return;
42503831d35Sstevel 
42603831d35Sstevel 	/* Initialize all the common information */
42703831d35Sstevel 	card.display = 1;
42803831d35Sstevel 	card.board = board->board_num;
42903831d35Sstevel 	(void) sprintf(card.bus_type, "PCI");
43003831d35Sstevel 
43103831d35Sstevel 	for (pci = dev_find_node(board->nodes, PCI_NAME); pci != NULL;
43203831d35Sstevel 	    pci = dev_next_node(pci, PCI_NAME)) {
43303831d35Sstevel 		char *name;
43403831d35Sstevel 		Prom_node *prev_parent = NULL;
43503831d35Sstevel 		int prev_device = -1;
43603831d35Sstevel 		int pci_pci_bridge = 0;
43703831d35Sstevel 
43803831d35Sstevel 		/*
43903831d35Sstevel 		 * If we have reached a pci-to-pci bridge node,
44003831d35Sstevel 		 * we are one level below the 'pci' nodes level
44103831d35Sstevel 		 * in the device tree. To get back to that level,
44203831d35Sstevel 		 * the search should continue with the sibling of
44303831d35Sstevel 		 * the parent or else the remaining 'pci' cards
44403831d35Sstevel 		 * will not show up in the output.
44503831d35Sstevel 		 */
44603831d35Sstevel 		if (find_prop(pci, "upa-portid") == NULL) {
44703831d35Sstevel 			if ((pci->parent->sibling != NULL) &&
448aa5636e5SPeter Tribble 			    (strcmp(get_prop_val(
449aa5636e5SPeter Tribble 			    find_prop(pci->parent->sibling,
450aa5636e5SPeter Tribble 			    "name")), PCI_NAME) == 0))
45103831d35Sstevel 				pci = pci->parent->sibling;
45203831d35Sstevel 			else {
45303831d35Sstevel 				pci = pci->parent->sibling;
45403831d35Sstevel 				continue;
45503831d35Sstevel 			}
45603831d35Sstevel 		}
45703831d35Sstevel 
45803831d35Sstevel 		/* Skip all failed nodes for now */
45903831d35Sstevel 		if (node_failed(pci))
46003831d35Sstevel 			continue;
46103831d35Sstevel 
46203831d35Sstevel 		/* Fill in frequency */
46303831d35Sstevel 		value = get_prop_val(find_prop(pci, "clock-frequency"));
46403831d35Sstevel 		if (value == NULL)
46503831d35Sstevel 			card.freq = -1;
46603831d35Sstevel 		else
46703831d35Sstevel 			card.freq = ((*(int *)value) + 500000) / 1000000;
46803831d35Sstevel 
46903831d35Sstevel 		/* Walk through the PSYCHO children */
47003831d35Sstevel 		card_node = pci->child;
47103831d35Sstevel 		while (card_node != NULL) {
47203831d35Sstevel 			Prop *compat = NULL;
47303831d35Sstevel 
47403831d35Sstevel 			/* If it doesn't have a name, skip it */
47503831d35Sstevel 			name = (char *)get_prop_val(
476aa5636e5SPeter Tribble 			    find_prop(card_node, "name"));
47703831d35Sstevel 			if (name == NULL) {
47803831d35Sstevel 				card_node = card_node->sibling;
47903831d35Sstevel 				continue;
48003831d35Sstevel 			}
48103831d35Sstevel 
48203831d35Sstevel 			/*
48303831d35Sstevel 			 * If this is a PCI bridge, then display its
48403831d35Sstevel 			 * children.
48503831d35Sstevel 			 */
48603831d35Sstevel 			if (strcmp(name, "pci") == 0) {
48703831d35Sstevel 				card_node = card_node->child;
48803831d35Sstevel 				pci_pci_bridge = 1;
48903831d35Sstevel 				continue;
49003831d35Sstevel 			}
49103831d35Sstevel 
49203831d35Sstevel 			/* Get the slot number for this card */
49303831d35Sstevel 			if (pci_pci_bridge) {
49403831d35Sstevel 				card.slot = tazmo_physical_slot(
495aa5636e5SPeter Tribble 				    dev_find_node(board->nodes, "slot2dev"),
496aa5636e5SPeter Tribble 				    pci,
497aa5636e5SPeter Tribble 				    get_pci_to_pci_device(card_node->parent),
498aa5636e5SPeter Tribble 				    card.slot_str);
49903831d35Sstevel 			} else
50003831d35Sstevel 				card.slot = tazmo_physical_slot(
501aa5636e5SPeter Tribble 				    dev_find_node(board->nodes, "slot2dev"),
502aa5636e5SPeter Tribble 				    pci,
503aa5636e5SPeter Tribble 				    get_pci_device(card_node),
504aa5636e5SPeter Tribble 				    card.slot_str);
50503831d35Sstevel 
50603831d35Sstevel 			/*
50703831d35Sstevel 			 * Check that duplicate devices are not reported
50803831d35Sstevel 			 * on Tazmo.
50903831d35Sstevel 			 */
51003831d35Sstevel 			if ((card_node->parent == prev_parent) &&
511aa5636e5SPeter Tribble 			    (get_pci_device(card_node) == prev_device) &&
512aa5636e5SPeter Tribble 			    (pci_pci_bridge == 0))
51303831d35Sstevel 					card.slot = -1;
51403831d35Sstevel 			prev_parent = card_node->parent;
51503831d35Sstevel 			prev_device = get_pci_device(card_node);
51603831d35Sstevel 
51703831d35Sstevel 
51803831d35Sstevel 			if (card.slot == -1 || strstr(name, "ebus")) {
51903831d35Sstevel 				card_node = card_node->sibling;
52003831d35Sstevel 				continue;
52103831d35Sstevel 			}
52203831d35Sstevel 
52303831d35Sstevel 			/* XXX - Don't know how to get status for PCI cards */
52403831d35Sstevel 			card.status[0] = '\0';
52503831d35Sstevel 
52603831d35Sstevel 			/* Get the model of this card */
52703831d35Sstevel 			value = get_prop_val(find_prop(card_node, "model"));
52803831d35Sstevel 			if (value == NULL)
52903831d35Sstevel 				card.model[0] = '\0';
53003831d35Sstevel 			else
53103831d35Sstevel 				(void) sprintf(card.model, "%s",
532aa5636e5SPeter Tribble 				    (char *)value);
53303831d35Sstevel 
53403831d35Sstevel 			/*
53503831d35Sstevel 			 * Check if further processing is necessary to display
53603831d35Sstevel 			 * this card uniquely.
53703831d35Sstevel 			 */
53803831d35Sstevel 			distinguish_identical_io_cards(name, card_node, &card);
53903831d35Sstevel 
54003831d35Sstevel 			/*
54103831d35Sstevel 			 * If we haven't figured out the frequency yet,
54203831d35Sstevel 			 * try and get it from the card.
54303831d35Sstevel 			 */
54403831d35Sstevel 			value = get_prop_val(find_prop(pci, "clock-frequency"));
54503831d35Sstevel 			if (value != NULL && card.freq == -1)
54603831d35Sstevel 				card.freq = ((*(int *)value) + 500000)
547aa5636e5SPeter Tribble 				    / 1000000;
54803831d35Sstevel 
54903831d35Sstevel 
55003831d35Sstevel 			value = get_prop_val(find_prop(card_node,
551aa5636e5SPeter Tribble 			    "compatible"));
55203831d35Sstevel 
55303831d35Sstevel 			/*
55403831d35Sstevel 			 * On Tazmo, we would like to print out the last
55503831d35Sstevel 			 * string of the "compatible" property if it exists.
55603831d35Sstevel 			 * The IEEE 1275 spec. states that this last string
55703831d35Sstevel 			 * will be the classcode name.
55803831d35Sstevel 			 */
55903831d35Sstevel 			if (value != NULL) {
56003831d35Sstevel 				char *tval;
56103831d35Sstevel 				int index;
56203831d35Sstevel 				const int always = 1;
56303831d35Sstevel 
56403831d35Sstevel 				tval = (char *)value;
56503831d35Sstevel 				index = 0;
56603831d35Sstevel 				compat = find_prop(card_node, "compatible");
56703831d35Sstevel 				while (always) {
56803831d35Sstevel 					if ((strlen(tval) + 1) ==
569aa5636e5SPeter Tribble 					    (compat->size - index))
57003831d35Sstevel 						break;
57103831d35Sstevel 					index += strlen(tval) + 1;
57203831d35Sstevel 					tval += strlen(tval) + 1;
57303831d35Sstevel 				}
57403831d35Sstevel 				value = (void *)tval;
57503831d35Sstevel 			}
57603831d35Sstevel 
57703831d35Sstevel 			if (value != NULL)
57803831d35Sstevel 				(void) sprintf(card.name, "%s-%s",
579aa5636e5SPeter Tribble 				    (char *)name, (char *)value);
58003831d35Sstevel 			else
58103831d35Sstevel 				(void) sprintf(card.name, "%s",
582aa5636e5SPeter Tribble 				    (char *)name);
58303831d35Sstevel 
58403831d35Sstevel 			if (card.freq != -1)
58503831d35Sstevel 				card_list = insert_io_card(card_list, &card);
58603831d35Sstevel 
58703831d35Sstevel 			/*
58803831d35Sstevel 			 * If we are done with the children of the pci bridge,
58903831d35Sstevel 			 * we must continue with the remaining siblings of
59003831d35Sstevel 			 * the pci-to-pci bridge.
59103831d35Sstevel 			 */
59203831d35Sstevel 			if ((card_node->sibling == NULL) && pci_pci_bridge) {
59303831d35Sstevel 				card_node = card_node->parent->sibling;
59403831d35Sstevel 				pci_pci_bridge = 0;
59503831d35Sstevel 			} else
59603831d35Sstevel 				card_node = card_node->sibling;
59703831d35Sstevel 		}
59803831d35Sstevel 	}
59903831d35Sstevel 
60003831d35Sstevel 	display_io_cards(card_list);
60103831d35Sstevel 	free_io_cards(card_list);
60203831d35Sstevel }
60303831d35Sstevel 
60403831d35Sstevel 
60503831d35Sstevel /*
60603831d35Sstevel  * Print out all the io cards in the list.  Also print the column
60703831d35Sstevel  * headers if told to do so.
60803831d35Sstevel  */
60903831d35Sstevel void
display_io_cards(struct io_card * list)61003831d35Sstevel display_io_cards(struct io_card *list)
61103831d35Sstevel {
61203831d35Sstevel 	static int banner = 0; /* Have we printed the column headings? */
61303831d35Sstevel 	struct io_card *p;
61403831d35Sstevel 
61503831d35Sstevel 	if (list == NULL)
61603831d35Sstevel 		return;
61703831d35Sstevel 
61803831d35Sstevel 	if (banner == 0) {
61903831d35Sstevel 		log_printf("     Bus   Freq\n", 0);
62003831d35Sstevel 		log_printf("Brd  Type  MHz   Slot  "
621aa5636e5SPeter Tribble 		    "Name                              Model", 0);
62203831d35Sstevel 		log_printf("\n", 0);
62303831d35Sstevel 		log_printf("---  ----  ----  ----  "
624aa5636e5SPeter Tribble 		    "--------------------------------  "
625aa5636e5SPeter Tribble 		    "----------------------", 0);
62603831d35Sstevel 		log_printf("\n", 0);
62703831d35Sstevel 		banner = 1;
62803831d35Sstevel 	}
62903831d35Sstevel 
63003831d35Sstevel 	for (p = list; p != NULL; p = p -> next) {
63103831d35Sstevel 		log_printf("SYS   ", p->board, 0);
63203831d35Sstevel 		log_printf("%-4s  ", p->bus_type, 0);
63303831d35Sstevel 		log_printf("%3d   ", p->freq, 0);
63403831d35Sstevel 		log_printf("%3d   ", p->slot, 0);
63503831d35Sstevel 		log_printf("%-32.32s", p->name, 0);
63603831d35Sstevel 		if (strlen(p->name) > 32)
63703831d35Sstevel 			log_printf("+ ", 0);
63803831d35Sstevel 		else
63903831d35Sstevel 			log_printf("  ", 0);
64003831d35Sstevel 		log_printf("%-22.22s", p->model, 0);
64103831d35Sstevel 		if (strlen(p->model) > 22)
64203831d35Sstevel 			log_printf("+", 0);
64303831d35Sstevel 		log_printf("\n", 0);
64403831d35Sstevel 	}
64503831d35Sstevel }
64603831d35Sstevel 
64703831d35Sstevel /*
64803831d35Sstevel  * display_ffb
64903831d35Sstevel  * Display all FFBs on this board.  It can either be in tabular format,
65003831d35Sstevel  * or a more verbose format.
65103831d35Sstevel  */
65203831d35Sstevel void
display_ffb(Board_node * board,int table)65303831d35Sstevel display_ffb(Board_node *board, int table)
65403831d35Sstevel {
65503831d35Sstevel 	Prom_node *ffb;
65603831d35Sstevel 	void *value;
65703831d35Sstevel 	struct io_card *card_list = NULL;
65803831d35Sstevel 	struct io_card card;
65903831d35Sstevel 
66003831d35Sstevel 	if (board == NULL)
66103831d35Sstevel 		return;
66203831d35Sstevel 
66303831d35Sstevel 	/* Fill in common information */
66403831d35Sstevel 	card.display = 1;
66503831d35Sstevel 	card.board = board->board_num;
66603831d35Sstevel 	(void) sprintf(card.bus_type, "UPA");
66703831d35Sstevel 	card.freq = sys_clk;
66803831d35Sstevel 
66903831d35Sstevel 	for (ffb = dev_find_node(board->nodes, FFB_NAME); ffb != NULL;
67003831d35Sstevel 	    ffb = dev_next_node(ffb, FFB_NAME)) {
67103831d35Sstevel 		if (table == 1) {
67203831d35Sstevel 			/* Print out in table format */
67303831d35Sstevel 
67403831d35Sstevel 			/* XXX - Get the slot number (hack) */
67503831d35Sstevel 			card.slot = tazmo_physical_slot(
676aa5636e5SPeter Tribble 			    dev_find_node(board->nodes, "slot2dev"),
677aa5636e5SPeter Tribble 			    ffb,
678aa5636e5SPeter Tribble 			    -1,
679aa5636e5SPeter Tribble 			    card.slot_str);
68003831d35Sstevel 
68103831d35Sstevel 			/* Find out if it's single or double buffered */
68203831d35Sstevel 			(void) sprintf(card.name, "FFB");
68303831d35Sstevel 			value = get_prop_val(find_prop(ffb, "board_type"));
68403831d35Sstevel 			if (value != NULL)
68503831d35Sstevel 				if ((*(int *)value) & FFB_B_BUFF)
68603831d35Sstevel 					(void) sprintf(card.name,
687aa5636e5SPeter Tribble 					    "FFB, Double Buffered");
68803831d35Sstevel 				else
68903831d35Sstevel 					(void) sprintf(card.name,
690aa5636e5SPeter Tribble 					    "FFB, Single Buffered");
69103831d35Sstevel 
69203831d35Sstevel 			/* Print model number */
69303831d35Sstevel 			card.model[0] = '\0';
69403831d35Sstevel 			value = get_prop_val(find_prop(ffb, "model"));
69503831d35Sstevel 			if (value != NULL)
69603831d35Sstevel 				(void) sprintf(card.model, "%s",
697aa5636e5SPeter Tribble 				    (char *)value);
69803831d35Sstevel 
69903831d35Sstevel 			card_list = insert_io_card(card_list, &card);
70003831d35Sstevel 		} else {
70103831d35Sstevel 			/* print in long format */
70203831d35Sstevel 			char device[MAXSTRLEN];
70303831d35Sstevel 			int fd = -1;
70403831d35Sstevel 			struct dirent *direntp;
70503831d35Sstevel 			DIR *dirp;
70603831d35Sstevel 			union strap_un strap;
70703831d35Sstevel 			struct ffb_sys_info fsi;
70803831d35Sstevel 
70903831d35Sstevel 			/* Find the device node using upa address */
71003831d35Sstevel 			value = get_prop_val(find_prop(ffb, "upa-portid"));
71103831d35Sstevel 			if (value == NULL)
712aa5636e5SPeter Tribble 				continue;
71303831d35Sstevel 
71403831d35Sstevel 			(void) sprintf(device, "%s@%x", FFB_NAME,
715aa5636e5SPeter Tribble 			    *(int *)value);
71603831d35Sstevel 			if ((dirp = opendir("/devices")) == NULL)
71703831d35Sstevel 				continue;
71803831d35Sstevel 
71903831d35Sstevel 			while ((direntp = readdir(dirp)) != NULL) {
72003831d35Sstevel 				if (strstr(direntp->d_name, device) != NULL) {
72103831d35Sstevel 					(void) sprintf(device, "/devices/%s",
722aa5636e5SPeter Tribble 					    direntp->d_name);
72303831d35Sstevel 					fd = open(device, O_RDWR, 0666);
72403831d35Sstevel 					break;
72503831d35Sstevel 				}
72603831d35Sstevel 			}
72703831d35Sstevel 			(void) closedir(dirp);
72803831d35Sstevel 
72903831d35Sstevel 			if (fd == -1)
73003831d35Sstevel 				continue;
73103831d35Sstevel 
73203831d35Sstevel 			if (ioctl(fd, FFB_SYS_INFO, &fsi) < 0)
73303831d35Sstevel 				continue;
73403831d35Sstevel 
73503831d35Sstevel 			log_printf("FFB Hardware Configuration:\n", 0);
73603831d35Sstevel 			log_printf("-----------------------------------\n", 0);
73703831d35Sstevel 
73803831d35Sstevel 			strap.ffb_strap_bits = fsi.ffb_strap_bits;
73903831d35Sstevel 			log_printf("\tBoard rev: %d\n",
740aa5636e5SPeter Tribble 			    (int)strap.fld.board_rev, 0);
74103831d35Sstevel 			log_printf("\tFBC version: 0x%x\n", fsi.fbc_version, 0);
74203831d35Sstevel 			log_printf("\tDAC: %s\n",
743aa5636e5SPeter Tribble 			    fmt_manf_id(fsi.dac_version, device), 0);
74403831d35Sstevel 			log_printf("\t3DRAM: %s\n",
745aa5636e5SPeter Tribble 			    fmt_manf_id(fsi.fbram_version, device), 0);
74603831d35Sstevel 			log_printf("\n", 0);
74703831d35Sstevel 		}
74803831d35Sstevel 	}
74903831d35Sstevel 
75003831d35Sstevel 	display_io_cards(card_list);
75103831d35Sstevel 	free_io_cards(card_list);
75203831d35Sstevel }
75303831d35Sstevel 
75403831d35Sstevel /*
75503831d35Sstevel  * This module does the reading and interpreting of tazmo system
756*2bcbf80cSPeter Tribble  * kstats. These kstats are created by the envctrl driver:
75703831d35Sstevel  */
75803831d35Sstevel void
read_platform_kstats(Sys_tree * tree,struct system_kstat_data * sys_kstat,struct envctrl_kstat_data * ep)75903831d35Sstevel read_platform_kstats(Sys_tree *tree, struct system_kstat_data *sys_kstat,
760*2bcbf80cSPeter Tribble     struct envctrl_kstat_data *ep)
76103831d35Sstevel {
76203831d35Sstevel 	kstat_ctl_t		*kc;
76303831d35Sstevel 	kstat_t			*ksp;
76403831d35Sstevel 
76503831d35Sstevel 	if ((kc = kstat_open()) == NULL) {
76603831d35Sstevel 		return;
76703831d35Sstevel 	}
76803831d35Sstevel 
76903831d35Sstevel 	ep = &sys_kstat->env_data;
77003831d35Sstevel 
77103831d35Sstevel 	/* Read the power supply kstats */
77203831d35Sstevel 	ksp = kstat_lookup(kc, ENVCTRL_MODULE_NAME, INSTANCE_0,
773aa5636e5SPeter Tribble 	    ENVCTRL_KSTAT_PSNAME);
77403831d35Sstevel 
77503831d35Sstevel 	if (ksp != NULL && (kstat_read(kc, ksp, NULL) != -1)) {
77603831d35Sstevel 		(void) memcpy(ep->ps_kstats, ksp->ks_data,
777aa5636e5SPeter Tribble 		    MAX_DEVS * sizeof (envctrl_ps_t));
77803831d35Sstevel 	} else {
77903831d35Sstevel 		sys_kstat->envctrl_kstat_ok = B_FALSE;
78003831d35Sstevel 		return;
78103831d35Sstevel 	}
78203831d35Sstevel 
78303831d35Sstevel 	/* Read the fan status kstats */
78403831d35Sstevel 	ksp = kstat_lookup(kc, ENVCTRL_MODULE_NAME, INSTANCE_0,
785aa5636e5SPeter Tribble 	    ENVCTRL_KSTAT_FANSTAT);
78603831d35Sstevel 
78703831d35Sstevel 	if (ksp != NULL && (kstat_read(kc, ksp, NULL) != -1)) {
78803831d35Sstevel 		(void) memcpy(ep->fan_kstats, ksp->ks_data,
789aa5636e5SPeter Tribble 		    ksp->ks_ndata * sizeof (envctrl_fan_t));
79003831d35Sstevel 	} else {
79103831d35Sstevel 		sys_kstat->envctrl_kstat_ok = B_FALSE;
79203831d35Sstevel 		return;
79303831d35Sstevel 	}
79403831d35Sstevel 
79503831d35Sstevel 	/* Read the enclosure kstats */
79603831d35Sstevel 	ksp = kstat_lookup(kc, ENVCTRL_MODULE_NAME, INSTANCE_0,
797aa5636e5SPeter Tribble 	    ENVCTRL_KSTAT_ENCL);
79803831d35Sstevel 
79903831d35Sstevel 	if (ksp != NULL && (kstat_read(kc, ksp, NULL) != -1)) {
80003831d35Sstevel 		(void) memcpy(ep->encl_kstats, ksp->ks_data,
801aa5636e5SPeter Tribble 		    ksp->ks_ndata * sizeof (envctrl_encl_t));
80203831d35Sstevel 	} else {
80303831d35Sstevel 		sys_kstat->envctrl_kstat_ok = B_FALSE;
80403831d35Sstevel 		return;
80503831d35Sstevel 	}
80603831d35Sstevel 
80703831d35Sstevel 	sys_kstat->envctrl_kstat_ok = B_TRUE;
80803831d35Sstevel }
80903831d35Sstevel 
81003831d35Sstevel /*
81103831d35Sstevel  * Walk the PROM device tree and build the system tree and root tree.
81203831d35Sstevel  * Nodes that have a board number property are placed in the board
81303831d35Sstevel  * structures for easier processing later. Child nodes are placed
81403831d35Sstevel  * under their parents. ffb (Fusion Frame Buffer) nodes are handled
81503831d35Sstevel  * specially, because they do not contain board number properties.
81603831d35Sstevel  * This was requested from OBP, but was not granted. So this code
81703831d35Sstevel  * must parse the MID of the FFB to find the board#.
81803831d35Sstevel  */
81903831d35Sstevel Prom_node *
walk(Sys_tree * tree,Prom_node * root,int id)82003831d35Sstevel walk(Sys_tree *tree, Prom_node *root, int id)
82103831d35Sstevel {
82203831d35Sstevel 	register int curnode;
82303831d35Sstevel 	Prom_node *pnode;
82403831d35Sstevel 	char *name;
82503831d35Sstevel 	char *type;
82603831d35Sstevel 	char *model;
82703831d35Sstevel 	int board_node = 0;
82803831d35Sstevel 
82903831d35Sstevel 	/* allocate a node for this level */
83003831d35Sstevel 	if ((pnode = (Prom_node *) malloc(sizeof (struct prom_node))) ==
83103831d35Sstevel 	    NULL) {
83203831d35Sstevel 		perror("malloc");
83303831d35Sstevel 		exit(2);	/* program errors cause exit 2 */
83403831d35Sstevel 	}
83503831d35Sstevel 
83603831d35Sstevel 	/* assign parent Prom_node */
83703831d35Sstevel 	pnode->parent = root;
83803831d35Sstevel 	pnode->sibling = NULL;
83903831d35Sstevel 	pnode->child = NULL;
84003831d35Sstevel 
84103831d35Sstevel 	/* read properties for this node */
84203831d35Sstevel 	dump_node(pnode);
84303831d35Sstevel 
84403831d35Sstevel 	/*
84503831d35Sstevel 	 * Place a node in a 'board' if it has 'board'-ness. The definition
84603831d35Sstevel 	 * is that all nodes that are children of root should have a
84703831d35Sstevel 	 * board# property. But the PROM tree does not exactly follow
84803831d35Sstevel 	 * this. This is where we start hacking. The name 'ffb' can
84903831d35Sstevel 	 * change, so watch out for this.
85003831d35Sstevel 	 *
85103831d35Sstevel 	 * The UltraSPARC, sbus, pci and ffb nodes will exit in
85203831d35Sstevel 	 * the desktops and will not have board# properties. These
85303831d35Sstevel 	 * cases must be handled here.
85403831d35Sstevel 	 *
85503831d35Sstevel 	 * PCI to PCI bridges also have the name "pci", but with different
85603831d35Sstevel 	 * model property values.  They should not be put under 'board'.
85703831d35Sstevel 	 */
85803831d35Sstevel 	name = get_node_name(pnode);
85903831d35Sstevel 	type = get_node_type(pnode);
86003831d35Sstevel 	model = (char *)get_prop_val(find_prop(pnode, "model"));
86103831d35Sstevel #ifdef DEBUG
86203831d35Sstevel 	if (name != NULL)
86303831d35Sstevel 		printf("name=%s ", name);
86403831d35Sstevel 	if (type != NULL)
86503831d35Sstevel 		printf("type=%s ", type);
86603831d35Sstevel 	if (model != NULL)
86703831d35Sstevel 		printf("model=%s", model);
86803831d35Sstevel 	printf("\n");
86903831d35Sstevel 
87003831d35Sstevel 	if (model == NULL)
87103831d35Sstevel 		model = "";
87203831d35Sstevel #endif
87303831d35Sstevel 	if (type == NULL)
87403831d35Sstevel 		type = "";
87503831d35Sstevel 	if (name != NULL) {
87603831d35Sstevel 		if (has_board_num(pnode)) {
87703831d35Sstevel 			add_node(tree, pnode);
87803831d35Sstevel 			board_node = 1;
87903831d35Sstevel #ifdef DEBUG
88003831d35Sstevel 			printf("ADDED BOARD name=%s type=%s model=%s\n",
881aa5636e5SPeter Tribble 			    name, type, model);
88203831d35Sstevel #endif
88303831d35Sstevel 		} else if ((strcmp(name, FFB_NAME)  == 0)		||
88403831d35Sstevel 		    (strcmp(type, "cpu") == 0)				||
88503831d35Sstevel 
88603831d35Sstevel 		    ((strcmp(name, "pci") == 0) && (model != NULL) &&
887aa5636e5SPeter Tribble 		    (strcmp(model, "SUNW,psycho") == 0))		||
88803831d35Sstevel 
88903831d35Sstevel 		    ((strcmp(name, "pci") == 0) && (model != NULL) &&
890aa5636e5SPeter Tribble 		    (strcmp(model, "SUNW,sabre") == 0))			||
89103831d35Sstevel 
89203831d35Sstevel 		    (strcmp(name, "counter-timer") == 0)		||
89303831d35Sstevel 		    (strcmp(name, "sbus") == 0)				||
89403831d35Sstevel 		    (strcmp(name, "memory") == 0)			||
89503831d35Sstevel 		    (strcmp(name, "mc") == 0)				||
89603831d35Sstevel 		    (strcmp(name, "associations") == 0)) {
89703831d35Sstevel 			add_node(tree, pnode);
89803831d35Sstevel 			board_node = 1;
89903831d35Sstevel #ifdef DEBUG
90003831d35Sstevel 			printf("ADDED BOARD name=%s type=%s model=%s\n",
901aa5636e5SPeter Tribble 			    name, type, model);
90203831d35Sstevel #endif
90303831d35Sstevel 		}
90403831d35Sstevel #ifdef DEBUG
90503831d35Sstevel 		else
90603831d35Sstevel 			printf("node not added: name=%s type=%s\n", name, type);
90703831d35Sstevel #endif
90803831d35Sstevel 	}
90903831d35Sstevel 
91003831d35Sstevel 	if (curnode = child(id)) {
91103831d35Sstevel 		pnode->child = walk(tree, pnode, curnode);
91203831d35Sstevel 	}
91303831d35Sstevel 
91403831d35Sstevel 	if (curnode = next(id)) {
91503831d35Sstevel 		if (board_node) {
91603831d35Sstevel 			return (walk(tree, root, curnode));
91703831d35Sstevel 		} else {
91803831d35Sstevel 			pnode->sibling = walk(tree, root, curnode);
91903831d35Sstevel 		}
92003831d35Sstevel 	}
92103831d35Sstevel 
92203831d35Sstevel 	if (board_node) {
92303831d35Sstevel 		return (NULL);
92403831d35Sstevel 	} else {
92503831d35Sstevel 		return (pnode);
92603831d35Sstevel 	}
92703831d35Sstevel }
92803831d35Sstevel 
92903831d35Sstevel /*
93003831d35Sstevel  * local functions
93103831d35Sstevel  */
93203831d35Sstevel 
93303831d35Sstevel /*
93403831d35Sstevel  * disp_envctrl_status
93503831d35Sstevel  *
93603831d35Sstevel  * This routine displays the environmental status passed up from
93703831d35Sstevel  * device drivers via kstats. The kstat names are defined in
93803831d35Sstevel  * kernel header files included by this module.
93903831d35Sstevel  */
94003831d35Sstevel static int
disp_envctrl_status(Sys_tree * tree,struct system_kstat_data * sys_kstats)94103831d35Sstevel disp_envctrl_status(Sys_tree *tree, struct system_kstat_data *sys_kstats)
94203831d35Sstevel {
94303831d35Sstevel 	int exit_code = 0;
94403831d35Sstevel 	int possible_failure;
94503831d35Sstevel 	int i;
94603831d35Sstevel 	uchar_t val;
94703831d35Sstevel 	char fan_type[16];
94803831d35Sstevel 	char state[48];
94903831d35Sstevel 	char name[16];
95003831d35Sstevel 	envctrl_ps_t ps;
95103831d35Sstevel 	envctrl_fan_t fan;
95203831d35Sstevel 	envctrl_encl_t encl;
95303831d35Sstevel 	struct envctrl_kstat_data *ep;
95403831d35Sstevel 	uchar_t fsp_value;
95503831d35Sstevel 	int i4slot_backplane_value = -1;
95603831d35Sstevel 	int i8slot_backplane_value = -1;
95703831d35Sstevel 	int j8slot_backplane_value = -1;
95803831d35Sstevel 	static int first_8disk_bp = 0;
95903831d35Sstevel 	static int second_8disk_bp = 0;
96003831d35Sstevel 	static int first_4disk_bp = 0;
96103831d35Sstevel 
96203831d35Sstevel 	if (sys_kstats->envctrl_kstat_ok == 0) {
96303831d35Sstevel 		log_printf("\n", 0);
96403831d35Sstevel 		log_printf(dgettext(TEXT_DOMAIN, "Environmental information "
965aa5636e5SPeter Tribble 		    "is not available\n"), 0);
96603831d35Sstevel 		log_printf(dgettext(TEXT_DOMAIN, "Environmental driver may "
967aa5636e5SPeter Tribble 		    "not be installed\n"), 0);
96803831d35Sstevel 		log_printf("\n", 0);
96903831d35Sstevel 		return (1);
97003831d35Sstevel 	}
97103831d35Sstevel 
97203831d35Sstevel 	ep = &sys_kstats->env_data;
97303831d35Sstevel 
97403831d35Sstevel 	check_disk_presence(tree, &first_4disk_bp, &first_8disk_bp,
975aa5636e5SPeter Tribble 	    &second_8disk_bp);
97603831d35Sstevel 
97703831d35Sstevel 	log_printf("\n", 0);
97803831d35Sstevel 	log_printf("=========================", 0);
97903831d35Sstevel 	log_printf(dgettext(TEXT_DOMAIN, " Environmental Status "), 0);
98003831d35Sstevel 	log_printf("=========================", 0);
98103831d35Sstevel 	log_printf("\n", 0);
98203831d35Sstevel 	log_printf("\n", 0);
98303831d35Sstevel 
98403831d35Sstevel 	log_printf("System Temperatures (Celsius):\n", 0);
98503831d35Sstevel 	log_printf("------------------------------\n", 0);
98603831d35Sstevel 	for (i = 0; i < MAX_DEVS; i++) {
98703831d35Sstevel 		encl = ep->encl_kstats[i];
98803831d35Sstevel 		switch (encl.type) {
98903831d35Sstevel 		case ENVCTRL_ENCL_AMBTEMPR:
99003831d35Sstevel 			if (encl.instance == I2C_NODEV)
991aa5636e5SPeter Tribble 				continue;
99203831d35Sstevel 			(void) sprintf(name, "%s", "AMBIENT");
99303831d35Sstevel 			log_printf("%s    %d", name, encl.value);
99403831d35Sstevel 			if (encl.value > MAX_AMB_TEMP) {
99503831d35Sstevel 				log_printf("    WARNING\n", 0);
99603831d35Sstevel 				exit_code = 1;
99703831d35Sstevel 			} else
99803831d35Sstevel 				log_printf("\n", 0);
99903831d35Sstevel 			break;
100003831d35Sstevel 		case ENVCTRL_ENCL_CPUTEMPR:
100103831d35Sstevel 			if (encl.instance == I2C_NODEV)
1002aa5636e5SPeter Tribble 				continue;
100303831d35Sstevel 			(void) sprintf(name, "%s %d", "CPU", encl.instance);
100403831d35Sstevel 			log_printf("%s      %d", name, encl.value);
100503831d35Sstevel 			if (encl.value > MAX_CPU_TEMP) {
100603831d35Sstevel 				log_printf("    WARNING\n", 0);
100703831d35Sstevel 				exit_code = 1;
100803831d35Sstevel 			} else
100903831d35Sstevel 				log_printf("\n", 0);
101003831d35Sstevel 			break;
101103831d35Sstevel 		case ENVCTRL_ENCL_FSP:
101203831d35Sstevel 			if (encl.instance == I2C_NODEV)
1013aa5636e5SPeter Tribble 				continue;
101403831d35Sstevel 			val = encl.value & ENVCTRL_FSP_KEYMASK;
101503831d35Sstevel 			fsp_value = encl.value;
101603831d35Sstevel 			switch (val) {
101703831d35Sstevel 			case ENVCTRL_FSP_KEYOFF:
101803831d35Sstevel 				(void) sprintf(state, "%s", "Off");
101903831d35Sstevel 				break;
102003831d35Sstevel 			case ENVCTRL_FSP_KEYON:
102103831d35Sstevel 				(void) sprintf(state, "%s", "On");
102203831d35Sstevel 				break;
102303831d35Sstevel 			case ENVCTRL_FSP_KEYDIAG:
102403831d35Sstevel 				(void) sprintf(state, "%s", "Diagnostic");
102503831d35Sstevel 				break;
102603831d35Sstevel 			case ENVCTRL_FSP_KEYLOCKED:
102703831d35Sstevel 				(void) sprintf(state, "%s", "Secure");
102803831d35Sstevel 				break;
102903831d35Sstevel 			default:
103003831d35Sstevel 				(void) sprintf(state, "%s", "Broken!");
103103831d35Sstevel 				exit_code = 1;
103203831d35Sstevel 				break;
103303831d35Sstevel 			}
103403831d35Sstevel 			break;
103503831d35Sstevel 		case ENVCTRL_ENCL_BACKPLANE4:
103603831d35Sstevel 		case ENVCTRL_ENCL_BACKPLANE8:
103703831d35Sstevel 			if (encl.instance == I2C_NODEV)
103803831d35Sstevel 				continue;
103903831d35Sstevel 			switch (encl.instance) {
104003831d35Sstevel 			case 0:
104103831d35Sstevel 				i4slot_backplane_value =
104203831d35Sstevel 				    encl.value & ENVCTRL_4SLOT_BACKPLANE;
104303831d35Sstevel 				break;
104403831d35Sstevel 			case 1:
104503831d35Sstevel 				i8slot_backplane_value =
104603831d35Sstevel 				    encl.value & ENVCTRL_8SLOT_BACKPLANE;
104703831d35Sstevel 				break;
104803831d35Sstevel 			case 2:
104903831d35Sstevel 				j8slot_backplane_value =
105003831d35Sstevel 				    encl.value & ENVCTRL_8SLOT_BACKPLANE;
105103831d35Sstevel 				break;
105203831d35Sstevel 			}
105303831d35Sstevel 		default:
105403831d35Sstevel 			break;
105503831d35Sstevel 		}
105603831d35Sstevel 	}
105703831d35Sstevel 
105803831d35Sstevel 	log_printf("=================================\n\n", 0);
105903831d35Sstevel 	log_printf("Front Status Panel:\n", 0);
106003831d35Sstevel 	log_printf("-------------------\n", 0);
106103831d35Sstevel 	log_printf("Keyswitch position is in %s mode.\n", state);
106203831d35Sstevel 	log_printf("\n", 0);
106303831d35Sstevel 	val = fsp_value & (ENVCTRL_FSP_DISK_ERR | ENVCTRL_FSP_PS_ERR |
1064aa5636e5SPeter Tribble 	    ENVCTRL_FSP_TEMP_ERR | ENVCTRL_FSP_GEN_ERR |
1065aa5636e5SPeter Tribble 	    ENVCTRL_FSP_ACTIVE);
106603831d35Sstevel 	log_printf("System LED Status:    POWER     GENERAL ERROR  "
1067aa5636e5SPeter Tribble 	    "    ACTIVITY\n", 0);
106803831d35Sstevel 	log_printf("                      [ ON]         [%3s]      "
1069aa5636e5SPeter Tribble 	    "     [%3s]\n", val & ENVCTRL_FSP_GEN_ERR ? "ON" : "OFF",
1070aa5636e5SPeter Tribble 	    val & ENVCTRL_FSP_ACTIVE ? "ON" : "OFF");
107103831d35Sstevel 	log_printf("                    DISK ERROR  "
1072aa5636e5SPeter Tribble 	    "THERMAL ERROR  POWER SUPPLY ERROR\n", 0);
107303831d35Sstevel 	log_printf("                      [%3s]         [%3s]      "
1074aa5636e5SPeter Tribble 	    "     [%3s]\n", val & ENVCTRL_FSP_DISK_ERR ? "ON" : "OFF",
1075aa5636e5SPeter Tribble 	    val & ENVCTRL_FSP_TEMP_ERR ? "ON" : "OFF",
1076aa5636e5SPeter Tribble 	    val & ENVCTRL_FSP_PS_ERR ? "ON" : "OFF");
107703831d35Sstevel 	log_printf("\n", 0);
107803831d35Sstevel 	/* record error conditions */
107903831d35Sstevel 	if (val & (ENVCTRL_FSP_GEN_ERR | ENVCTRL_FSP_DISK_ERR |
1080aa5636e5SPeter Tribble 	    ENVCTRL_FSP_TEMP_ERR | ENVCTRL_FSP_PS_ERR)) {
108103831d35Sstevel 		exit_code = 1;
108203831d35Sstevel 	}
108303831d35Sstevel 
108403831d35Sstevel 
108503831d35Sstevel 	log_printf("Disk LED Status:	OK = GREEN	ERROR = YELLOW\n", 0);
108603831d35Sstevel 
108703831d35Sstevel 	if (j8slot_backplane_value != -1) {
108803831d35Sstevel 		log_printf("		DISK 18: %7s	DISK 19: %7s\n",
108903831d35Sstevel 		    second_8disk_bp & ENVCTRL_DISK_6 ?
109003831d35Sstevel 		    j8slot_backplane_value & ENVCTRL_DISK_6 ? "[ERROR]" : "[OK]"
1091aa5636e5SPeter Tribble 		    : "[EMPTY]", second_8disk_bp & ENVCTRL_DISK_7 ?
109203831d35Sstevel 		    j8slot_backplane_value & ENVCTRL_DISK_7 ? "[ERROR]" : "[OK]"
1093aa5636e5SPeter Tribble 		    : "[EMPTY]");
109403831d35Sstevel 		log_printf("		DISK 16: %7s	DISK 17: %7s\n",
109503831d35Sstevel 		    second_8disk_bp & ENVCTRL_DISK_4 ?
109603831d35Sstevel 		    j8slot_backplane_value & ENVCTRL_DISK_4 ? "[ERROR]" : "[OK]"
1097aa5636e5SPeter Tribble 		    : "[EMPTY]", second_8disk_bp & ENVCTRL_DISK_5 ?
109803831d35Sstevel 		    j8slot_backplane_value & ENVCTRL_DISK_5 ? "[ERROR]" : "[OK]"
1099aa5636e5SPeter Tribble 		    : "[EMPTY]");
110003831d35Sstevel 		log_printf("		DISK 14: %7s	DISK 15: %7s\n",
110103831d35Sstevel 		    second_8disk_bp & ENVCTRL_DISK_2 ?
110203831d35Sstevel 		    j8slot_backplane_value & ENVCTRL_DISK_2 ? "[ERROR]" : "[OK]"
1103aa5636e5SPeter Tribble 		    : "[EMPTY]", second_8disk_bp & ENVCTRL_DISK_3 ?
110403831d35Sstevel 		    j8slot_backplane_value & ENVCTRL_DISK_3 ? "[ERROR]" : "[OK]"
1105aa5636e5SPeter Tribble 		    : "[EMPTY]");
110603831d35Sstevel 		log_printf("		DISK 12: %7s	DISK 13: %7s\n",
110703831d35Sstevel 		    second_8disk_bp & ENVCTRL_DISK_0 ?
110803831d35Sstevel 		    j8slot_backplane_value & ENVCTRL_DISK_0 ? "[ERROR]" : "[OK]"
1109aa5636e5SPeter Tribble 		    : "[EMPTY]", second_8disk_bp & ENVCTRL_DISK_1 ?
111003831d35Sstevel 		    j8slot_backplane_value & ENVCTRL_DISK_1 ? "[ERROR]" : "[OK]"
1111aa5636e5SPeter Tribble 		    : "[EMPTY]");
111203831d35Sstevel 	}
111303831d35Sstevel 	if (i8slot_backplane_value != -1) {
111403831d35Sstevel 		log_printf("		DISK 10: %7s	DISK 11: %7s\n",
111503831d35Sstevel 		    first_8disk_bp & ENVCTRL_DISK_6 ?
111603831d35Sstevel 		    i8slot_backplane_value & ENVCTRL_DISK_6 ? "[ERROR]" : "[OK]"
1117aa5636e5SPeter Tribble 		    : "[EMPTY]", first_8disk_bp & ENVCTRL_DISK_7 ?
111803831d35Sstevel 		    i8slot_backplane_value & ENVCTRL_DISK_7 ? "[ERROR]" : "[OK]"
1119aa5636e5SPeter Tribble 		    : "[EMPTY]");
112003831d35Sstevel 		log_printf("		DISK  8: %7s	DISK  9: %7s\n",
112103831d35Sstevel 		    first_8disk_bp & ENVCTRL_DISK_4 ?
112203831d35Sstevel 		    i8slot_backplane_value & ENVCTRL_DISK_4 ? "[ERROR]" : "[OK]"
1123aa5636e5SPeter Tribble 		    : "[EMPTY]", first_8disk_bp & ENVCTRL_DISK_5 ?
112403831d35Sstevel 		    i8slot_backplane_value & ENVCTRL_DISK_5 ? "[ERROR]" : "[OK]"
1125aa5636e5SPeter Tribble 		    : "[EMPTY]");
112603831d35Sstevel 		log_printf("		DISK  6: %7s	DISK  7: %7s\n",
112703831d35Sstevel 		    first_8disk_bp & ENVCTRL_DISK_2 ?
112803831d35Sstevel 		    i8slot_backplane_value & ENVCTRL_DISK_2 ? "[ERROR]" : "[OK]"
1129aa5636e5SPeter Tribble 		    : "[EMPTY]", first_8disk_bp & ENVCTRL_DISK_3 ?
113003831d35Sstevel 		    i8slot_backplane_value & ENVCTRL_DISK_3 ? "[ERROR]" : "[OK]"
1131aa5636e5SPeter Tribble 		    : "[EMPTY]");
113203831d35Sstevel 		log_printf("		DISK  4: %7s	DISK  5: %7s\n",
113303831d35Sstevel 		    first_8disk_bp & ENVCTRL_DISK_0 ?
113403831d35Sstevel 		    i8slot_backplane_value & ENVCTRL_DISK_0 ? "[ERROR]" : "[OK]"
1135aa5636e5SPeter Tribble 		    : "[EMPTY]", first_8disk_bp & ENVCTRL_DISK_1 ?
113603831d35Sstevel 		    i8slot_backplane_value & ENVCTRL_DISK_1 ? "[ERROR]" : "[OK]"
1137aa5636e5SPeter Tribble 		    : "[EMPTY]");
113803831d35Sstevel 	}
113903831d35Sstevel 	if (i4slot_backplane_value != -1) {
114003831d35Sstevel 		log_printf("		DISK  2: %7s	DISK  3: %7s\n",
114103831d35Sstevel 		    first_4disk_bp & ENVCTRL_DISK_2 ?
114203831d35Sstevel 		    i4slot_backplane_value & ENVCTRL_DISK_2 ? "[ERROR]" : "[OK]"
1143aa5636e5SPeter Tribble 		    : "[EMPTY]", first_4disk_bp & ENVCTRL_DISK_3 ?
114403831d35Sstevel 		    i4slot_backplane_value & ENVCTRL_DISK_3 ? "[ERROR]" : "[OK]"
1145aa5636e5SPeter Tribble 		    : "[EMPTY]");
114603831d35Sstevel 		log_printf("		DISK  0: %7s	DISK  1: %7s\n",
114703831d35Sstevel 		    first_4disk_bp & ENVCTRL_DISK_0 ?
114803831d35Sstevel 		    i4slot_backplane_value & ENVCTRL_DISK_0 ? "[ERROR]" : "[OK]"
1149aa5636e5SPeter Tribble 		    : "[EMPTY]", first_4disk_bp & ENVCTRL_DISK_1 ?
115003831d35Sstevel 		    i4slot_backplane_value & ENVCTRL_DISK_1 ? "[ERROR]" : "[OK]"
1151aa5636e5SPeter Tribble 		    : "[EMPTY]");
115203831d35Sstevel 	}
115303831d35Sstevel 
115403831d35Sstevel 	log_printf("=================================\n", 0);
115503831d35Sstevel 	log_printf("\n", 0);
115603831d35Sstevel 
115703831d35Sstevel 	log_printf("Fans:\n", 0);
115803831d35Sstevel 	log_printf("-----\n", 0);
115903831d35Sstevel 
116003831d35Sstevel 	log_printf("Fan Bank   Speed    Status\n", 0);
116103831d35Sstevel 	log_printf("--------   -----    ------\n", 0);
116203831d35Sstevel 
116303831d35Sstevel 	for (i = 0; i < MAX_DEVS; i++) {
116403831d35Sstevel 		fan = ep->fan_kstats[i];
116503831d35Sstevel 
116603831d35Sstevel 		if (fan.instance == I2C_NODEV)
116703831d35Sstevel 			continue;
116803831d35Sstevel 
116903831d35Sstevel 		switch (fan.type) {
117003831d35Sstevel 		case ENVCTRL_FAN_TYPE_CPU:
117103831d35Sstevel 			(void) sprintf(fan_type, "%s", "CPU");
117203831d35Sstevel 			break;
117303831d35Sstevel 		case ENVCTRL_FAN_TYPE_PS:
117403831d35Sstevel 			(void) sprintf(fan_type, "%s", "PWR");
117503831d35Sstevel 			break;
117603831d35Sstevel 		case ENVCTRL_FAN_TYPE_AFB:
117703831d35Sstevel 			(void) sprintf(fan_type, "%s", "AFB");
117803831d35Sstevel 			break;
117903831d35Sstevel 		}
118003831d35Sstevel 		if (fan.fans_ok == B_TRUE) {
118103831d35Sstevel 			(void) sprintf(state, "%s", "  OK  ");
118203831d35Sstevel 		} else {
118303831d35Sstevel 			(void) sprintf(state, "FAILED (FAN# %d)",
1184aa5636e5SPeter Tribble 			    fan.fanflt_num);
118503831d35Sstevel 			/* we know fan.instance != I2C_NODEV */
118603831d35Sstevel 			exit_code = 1;
118703831d35Sstevel 		}
118803831d35Sstevel 		if (fan.instance != I2C_NODEV)
118903831d35Sstevel 			log_printf("%s          %d     %s\n", fan_type,
1190aa5636e5SPeter Tribble 			    fan.fanspeed, state);
119103831d35Sstevel 	}
119203831d35Sstevel 
119303831d35Sstevel 
119403831d35Sstevel 	log_printf("\n", 0);
119503831d35Sstevel 
119603831d35Sstevel 	log_printf("\n", 0);
119703831d35Sstevel 	log_printf("Power Supplies:\n", 0);
119803831d35Sstevel 	log_printf("---------------\n", 0);
119903831d35Sstevel 	log_printf("Supply     Rating    Temp    Status\n", 0);
120003831d35Sstevel 	log_printf("------     ------    ----    ------\n", 0);
120103831d35Sstevel 	for (i = 0; i < MAX_DEVS; i++) {
120203831d35Sstevel 		ps = ep->ps_kstats[i];
120303831d35Sstevel 		if (ps.curr_share_ok == B_TRUE &&
120403831d35Sstevel 		    ps.limit_ok == B_TRUE && ps.ps_ok == B_TRUE) {
120503831d35Sstevel 			(void) sprintf(state, "%s", "  OK  ");
120603831d35Sstevel 			possible_failure = 0;
120703831d35Sstevel 		} else {
120803831d35Sstevel 			if (ps.ps_ok != B_TRUE) {
120903831d35Sstevel 				(void) sprintf(state, "%s",
1210aa5636e5SPeter Tribble 				    "FAILED: DC Power Failure");
121103831d35Sstevel 				possible_failure = 1;
121203831d35Sstevel 			} else if (ps.curr_share_ok != B_TRUE) {
121303831d35Sstevel 				(void) sprintf(state, "%s",
1214aa5636e5SPeter Tribble 				    "WARNING: Current Share Imbalance");
121503831d35Sstevel 				possible_failure = 1;
121603831d35Sstevel 			} else if (ps.limit_ok != B_TRUE) {
121703831d35Sstevel 				(void) sprintf(state, "%s",
1218aa5636e5SPeter Tribble 				    "WARNING: Current Overload");
121903831d35Sstevel 				possible_failure = 1;
122003831d35Sstevel 			}
122103831d35Sstevel 		}
122203831d35Sstevel 
122303831d35Sstevel 		if (ps.instance != I2C_NODEV && ps.ps_rating != 0) {
122403831d35Sstevel 			log_printf(" %2d        %4d W     %2d     %s\n",
122503831d35Sstevel 			    ps.instance, ps.ps_rating, ps.ps_tempr, state);
122603831d35Sstevel 			if (possible_failure)
122703831d35Sstevel 				exit_code = 1;
122803831d35Sstevel 		}
122903831d35Sstevel 	}
123003831d35Sstevel 
123103831d35Sstevel 	return (exit_code);
123203831d35Sstevel }
123303831d35Sstevel 
123403831d35Sstevel /*
123503831d35Sstevel  * This function will return a bitmask for each of the 4 disk backplane
123603831d35Sstevel  * and the two 8 disk backplanes. It creates this mask by first obtaining
123703831d35Sstevel  * the PROM path of the controller for each slot using the "slot2dev"
123803831d35Sstevel  * node in the PROM tree. It then modifies the PROM path to obtain a
123903831d35Sstevel  * physical device path to the controller. The presence of the controller
124003831d35Sstevel  * is determined by trying to open the controller device and reading
124103831d35Sstevel  * some information from the device. Currently only supported on Tazmo.
124203831d35Sstevel  */
124303831d35Sstevel static void
check_disk_presence(Sys_tree * tree,int * i4disk,int * i8disk,int * j8disk)124403831d35Sstevel check_disk_presence(Sys_tree *tree, int *i4disk, int *i8disk, int *j8disk)
124503831d35Sstevel {
124603831d35Sstevel 	Board_node *bnode;
124703831d35Sstevel 	Prom_node *slot2disk = NULL;
124803831d35Sstevel 	Prop *slotprop;
124903831d35Sstevel 	char *devpath_p;
125003831d35Sstevel 	char devpath[MAXSTRLEN];
125103831d35Sstevel 	char slotx[16] = "";
125203831d35Sstevel 	int slot;
125303831d35Sstevel 	int slot_ptr = 0;
125403831d35Sstevel 
125503831d35Sstevel 	bnode = tree->bd_list;
125603831d35Sstevel 	*i4disk = *i8disk, *j8disk = 0;
125703831d35Sstevel 
125803831d35Sstevel 	slot2disk = dev_find_node(bnode->nodes, "slot2disk");
125903831d35Sstevel 
126003831d35Sstevel 	for (slot = 0; slot < 20; slot++) {
126103831d35Sstevel 		(void) sprintf(slotx, "slot#%d", slot);
126203831d35Sstevel 		if ((slotprop = find_prop(slot2disk, slotx)) != NULL)
126303831d35Sstevel 			if ((devpath_p = (char *)(get_prop_val(slotprop)))
1264aa5636e5SPeter Tribble 			    != NULL) {
126503831d35Sstevel 				modify_device_path(devpath_p, devpath);
126603831d35Sstevel 				if (disk_present(devpath)) {
126703831d35Sstevel 					if (slot < 4)
126803831d35Sstevel 						*i4disk |= 1 << slot_ptr;
126903831d35Sstevel 					else if (slot < 12)
127003831d35Sstevel 						*i8disk |= 1 << slot_ptr;
127103831d35Sstevel 					else if (slot < 20)
127203831d35Sstevel 						*j8disk |= 1 << slot_ptr;
127303831d35Sstevel 				}
127403831d35Sstevel 			}
127503831d35Sstevel 		if ((slot == 3) || (slot == 11))
127603831d35Sstevel 			slot_ptr = 0;
127703831d35Sstevel 		else
127803831d35Sstevel 			slot_ptr++;
127903831d35Sstevel 	}
128003831d35Sstevel }
128103831d35Sstevel 
128203831d35Sstevel 
128303831d35Sstevel 
128403831d35Sstevel /*
128503831d35Sstevel  * modify_device_path
128603831d35Sstevel  *
128703831d35Sstevel  * This function modifies a string from the slot2disk association
128803831d35Sstevel  * PROM node to a physical device path name. For example if the
128903831d35Sstevel  * slot2disk association value is  "/pci@1f,4000/scsi@3/disk@1",
129003831d35Sstevel  * the equivalent physical device path will be
129103831d35Sstevel  * "/devices/pci@1f,4000/scsi@3/sd@1,0:c,raw".
129203831d35Sstevel  * We use this path to attempt to probe the disk to check for its
129303831d35Sstevel  * presence in the enclosure. We access the 'c' partition
129403831d35Sstevel  * which represents the entire disk.
129503831d35Sstevel  */
129603831d35Sstevel static void
modify_device_path(char * oldpath,char * newpath)129703831d35Sstevel modify_device_path(char *oldpath, char *newpath)
129803831d35Sstevel {
129903831d35Sstevel 	char *changeptr;
130003831d35Sstevel 	long target;
130103831d35Sstevel 	char targetstr[16];
130203831d35Sstevel 
130303831d35Sstevel 	(void) strcpy(newpath, "/devices");
130403831d35Sstevel 	changeptr = strstr(oldpath, "disk@");
130503831d35Sstevel 	/*
130603831d35Sstevel 	 * The assumption here is that nothing but the
130703831d35Sstevel 	 * target id follows the disk@ substring.
130803831d35Sstevel 	 */
130903831d35Sstevel 	target = strtol(changeptr+5, NULL, 16);
131003831d35Sstevel 	(void) strncat(newpath, oldpath, changeptr - oldpath);
131103831d35Sstevel 	(void) sprintf(targetstr, "sd@%ld,0:c,raw", target);
131203831d35Sstevel 	(void) strcat(newpath, targetstr);
131303831d35Sstevel }
131403831d35Sstevel 
131503831d35Sstevel /*
131603831d35Sstevel  * Returns 0 if the device at devpath is not *physically* present.  If it is,
131703831d35Sstevel  * then info on that device is placed in the dkinfop buffer, and 1 is returned.
131803831d35Sstevel  * Keep in mind that ioctl(DKIOCINFO)'s CDROMs owned by vold fail, so only
131903831d35Sstevel  * the dki_ctype field is set in that case.
132003831d35Sstevel  */
132103831d35Sstevel static int
disk_present(char * devpath)132203831d35Sstevel disk_present(char *devpath)
132303831d35Sstevel {
132403831d35Sstevel 	int		search_file;
132503831d35Sstevel 	struct stat	stbuf;
132603831d35Sstevel 	struct dk_cinfo dkinfo;
132703831d35Sstevel 
132803831d35Sstevel 	/*
132903831d35Sstevel 	 * Attempt to open the disk.  If it fails, skip it.
133003831d35Sstevel 	 */
133103831d35Sstevel 	if ((search_file = open(devpath, O_RDONLY | O_NDELAY)) < 0)
133203831d35Sstevel 		return (0);
133303831d35Sstevel 
133403831d35Sstevel 	/*
133503831d35Sstevel 	 * Must be a character device
133603831d35Sstevel 	 */
133703831d35Sstevel 	if (fstat(search_file, &stbuf) == -1 || !S_ISCHR(stbuf.st_mode)) {
133803831d35Sstevel 		(void) close(search_file);
133903831d35Sstevel 		return (0);
134003831d35Sstevel 	}
134103831d35Sstevel 
134203831d35Sstevel 	/*
134303831d35Sstevel 	 * Attempt to read the configuration info on the disk.
134403831d35Sstevel 	 * If it fails, we assume the disk's not there.
134503831d35Sstevel 	 * Note we must close the file for the disk before we
134603831d35Sstevel 	 * continue.
134703831d35Sstevel 	 */
134803831d35Sstevel 	if (ioctl(search_file, DKIOCINFO, &dkinfo) < 0) {
134903831d35Sstevel 		(void) close(search_file);
135003831d35Sstevel 		return (0);
135103831d35Sstevel 	}
135203831d35Sstevel 	(void) close(search_file);
135303831d35Sstevel 	return (1);
135403831d35Sstevel }
135503831d35Sstevel 
135603831d35Sstevel void
tazjav_disp_asic_revs(Sys_tree * tree)135703831d35Sstevel tazjav_disp_asic_revs(Sys_tree *tree)
135803831d35Sstevel {
135903831d35Sstevel 	Board_node *bnode;
136003831d35Sstevel 	Prom_node *pnode;
136103831d35Sstevel 	char *name;
136203831d35Sstevel 	int *version;
136303831d35Sstevel 	char *model;
136403831d35Sstevel 
136503831d35Sstevel 	/* Print the header */
136603831d35Sstevel 	log_printf("\n", 0);
136703831d35Sstevel 	log_printf("=========================", 0);
136803831d35Sstevel 	log_printf(dgettext(TEXT_DOMAIN, " HW Revisions "), 0);
136903831d35Sstevel 	log_printf("=========================", 0);
137003831d35Sstevel 	log_printf("\n", 0);
137103831d35Sstevel 	log_printf("\n", 0);
137203831d35Sstevel 
137303831d35Sstevel 	bnode = tree->bd_list;
137403831d35Sstevel 
137503831d35Sstevel 	log_printf("ASIC Revisions:\n", 0);
137603831d35Sstevel 	log_printf("---------------\n", 0);
137703831d35Sstevel 
137803831d35Sstevel 	/* Find sysio and print rev */
137903831d35Sstevel 	for (pnode = dev_find_node(bnode->nodes, "sbus"); pnode != NULL;
138003831d35Sstevel 	    pnode = dev_next_node(pnode, "sbus")) {
138103831d35Sstevel 		version = (int *)get_prop_val(find_prop(pnode, "version#"));
138203831d35Sstevel 		name = get_prop_val(find_prop(pnode, "name"));
138303831d35Sstevel 
138403831d35Sstevel 		if ((version != NULL) && (name != NULL)) {
138503831d35Sstevel 			log_printf("SBus: %s Rev %d\n",
1386aa5636e5SPeter Tribble 			    name, *version, 0);
138703831d35Sstevel 		}
138803831d35Sstevel 	}
138903831d35Sstevel 
139003831d35Sstevel 	/* Find Psycho and print rev */
139103831d35Sstevel 	for (pnode = dev_find_node(bnode->nodes, "pci"); pnode != NULL;
139203831d35Sstevel 	    pnode = dev_next_node(pnode, "pci")) {
139303831d35Sstevel 		Prom_node *parsib = pnode->parent->sibling;
139403831d35Sstevel 
139503831d35Sstevel 		if (find_prop(pnode, "upa-portid") == NULL) {
139603831d35Sstevel 			if ((parsib != NULL) &&
1397aa5636e5SPeter Tribble 			    (strcmp(get_prop_val(
1398aa5636e5SPeter Tribble 			    find_prop(parsib, "name")),
1399aa5636e5SPeter Tribble 			    PCI_NAME) == 0))
140003831d35Sstevel 				pnode = parsib;
140103831d35Sstevel 			else {
140203831d35Sstevel 				pnode = parsib;
140303831d35Sstevel 				continue;
140403831d35Sstevel 			}
140503831d35Sstevel 		}
140603831d35Sstevel 
140703831d35Sstevel 		version = (int *)get_prop_val(find_prop(pnode, "version#"));
140803831d35Sstevel 		name = get_prop_val(find_prop(pnode, "name"));
140903831d35Sstevel 
141003831d35Sstevel 		if ((version != NULL) && (name != NULL))
141103831d35Sstevel 			if (get_pci_bus(pnode) == 0)
141203831d35Sstevel 				log_printf("STP2223BGA: Rev %d\n", *version, 0);
141303831d35Sstevel 	}
141403831d35Sstevel 
141503831d35Sstevel 	/* Find Cheerio and print rev */
141603831d35Sstevel 	for (pnode = dev_find_node(bnode->nodes, "ebus"); pnode != NULL;
141703831d35Sstevel 	    pnode = dev_next_node(pnode, "ebus")) {
141803831d35Sstevel 		version = (int *)get_prop_val(find_prop(pnode, "revision-id"));
141903831d35Sstevel 		name = get_prop_val(find_prop(pnode, "name"));
142003831d35Sstevel 
142103831d35Sstevel 		if ((version != NULL) && (name != NULL))
142203831d35Sstevel 			log_printf("STP2003QFP: Rev %d\n", *version, 0);
142303831d35Sstevel 	}
142403831d35Sstevel 
142503831d35Sstevel 	/* Find System Controller and print rev */
142603831d35Sstevel 	for (pnode = dev_find_node(bnode->nodes, "sc"); pnode != NULL;
142703831d35Sstevel 	    pnode = dev_next_node(pnode, "sc")) {
142803831d35Sstevel 		version = (int *)get_prop_val(find_prop(pnode, "version#"));
142903831d35Sstevel 		model = (char *)get_prop_val(find_prop(pnode, "model"));
143003831d35Sstevel 		name = get_prop_val(find_prop(pnode, "name"));
143103831d35Sstevel 
143203831d35Sstevel 		if ((version != NULL) && (name != NULL)) {
143303831d35Sstevel 			if ((strcmp(model, "SUNW,sc-marvin") == 0))
143403831d35Sstevel 				log_printf("STP2205BGA: Rev %d\n", *version, 0);
143503831d35Sstevel 		}
143603831d35Sstevel 	}
143703831d35Sstevel 
143803831d35Sstevel 	/* Find the FEPS and print rev */
143903831d35Sstevel 	for (pnode = dev_find_node(bnode->nodes, "SUNW,hme"); pnode != NULL;
144003831d35Sstevel 	    pnode = dev_next_node(pnode, "SUNW,hme")) {
144103831d35Sstevel 		version = (int *)get_prop_val(find_prop(pnode,	"hm-rev"));
144203831d35Sstevel 		name = get_prop_val(find_prop(pnode, "name"));
144303831d35Sstevel 
144403831d35Sstevel 		if ((version != NULL) && (name != NULL)) {
144503831d35Sstevel 			log_printf("FEPS: %s Rev ", name);
144603831d35Sstevel 			if (*version == 0xa0) {
144703831d35Sstevel 				log_printf("2.0\n", 0);
144803831d35Sstevel 			} else if (*version == 0x20) {
144903831d35Sstevel 				log_printf("2.1\n", 0);
145003831d35Sstevel 			} else {
145103831d35Sstevel 				log_printf("%x\n", *version, 0);
145203831d35Sstevel 			}
145303831d35Sstevel 		}
145403831d35Sstevel 	}
145503831d35Sstevel 	log_printf("\n", 0);
145603831d35Sstevel 
145703831d35Sstevel 	if (dev_find_node(bnode->nodes, FFB_NAME) != NULL) {
145803831d35Sstevel 		display_ffb(bnode, 0);
145903831d35Sstevel 	}
146003831d35Sstevel }
146103831d35Sstevel 
146203831d35Sstevel 
146303831d35Sstevel /*
146403831d35Sstevel  * Determine the physical PCI slot based on which Psycho is the parent
146503831d35Sstevel  * of the PCI card.
146603831d35Sstevel  */
146703831d35Sstevel static int
tazmo_physical_slot(Prom_node * slotd,Prom_node * parent,int device,char * str)146803831d35Sstevel tazmo_physical_slot(Prom_node *slotd, Prom_node *parent, int device, char *str)
146903831d35Sstevel {
147003831d35Sstevel 	int *upa_id = NULL;
147103831d35Sstevel 	int *reg = NULL;
147203831d35Sstevel 	int offset;
147303831d35Sstevel 	char controller[MAXSTRLEN];
147403831d35Sstevel 	char *name;
147503831d35Sstevel 	Prop *prop;
147603831d35Sstevel 	char *devpath_p;
147703831d35Sstevel 	char slotx[16] = "";
147803831d35Sstevel 	int *slot_names_mask;
147903831d35Sstevel 	char *slot_names;
148003831d35Sstevel 	int shift = 0;
148103831d35Sstevel 	int slot;
148203831d35Sstevel 	int slots, start_slot;
148303831d35Sstevel 
148403831d35Sstevel 	/*
148503831d35Sstevel 	 * If slotd != NULL, then we must return the physical PCI slot
148603831d35Sstevel 	 * number based on the information in the slot2dev associations
148703831d35Sstevel 	 * node. This routine is called from display_pci() with slotd
148803831d35Sstevel 	 * != NULL. If so, we return without obtaining the slot name.
148903831d35Sstevel 	 * If slotd == NULL, we look for the slot name through the
149003831d35Sstevel 	 * slot-names property in the bus node.
149103831d35Sstevel 	 */
149203831d35Sstevel 
149303831d35Sstevel 	if (slotd != NULL) {
149403831d35Sstevel 		(void) strcpy(str, "");
149503831d35Sstevel 		if ((prop = find_prop(parent, "upa-portid")) != NULL)
149603831d35Sstevel 			upa_id = (int *)(get_prop_val(prop));
149703831d35Sstevel 		if ((prop = find_prop(parent, "reg")) != NULL)
149803831d35Sstevel 			reg = (int *)(get_prop_val(prop));
149903831d35Sstevel 		if ((prop = find_prop(parent, "name")) != NULL)
150003831d35Sstevel 			name = (char *)(get_prop_val(prop));
150103831d35Sstevel 		if ((upa_id == NULL) || (reg == NULL)) {
150203831d35Sstevel 			return (-1);
150303831d35Sstevel 		}
150403831d35Sstevel 		offset = reg[1];
150503831d35Sstevel 		if (strcmp(name, "pci") == 0) {
150603831d35Sstevel 			(void) sprintf(controller, "/pci@%x,%x/*@%x,*",
1507aa5636e5SPeter Tribble 			    *upa_id, offset, device);
150803831d35Sstevel 			slots = 20;
150903831d35Sstevel 		} else if (strcmp(name, "SUNW,ffb") == 0) {
151003831d35Sstevel 			(void) sprintf(controller, "/*@%x,0", *upa_id);
151103831d35Sstevel 			slots = 2;
151203831d35Sstevel 		}
151303831d35Sstevel 
151403831d35Sstevel 		start_slot = 1;
151503831d35Sstevel 		for (slot = start_slot; slot <= slots; slot++) {
151603831d35Sstevel 			if (strcmp(name, "pci") == 0)
151703831d35Sstevel 				(void) sprintf(slotx, "pci-slot#%d", slot);
151803831d35Sstevel 			else if (strcmp(name, "SUNW,ffb") == 0)
151903831d35Sstevel 				(void) sprintf(slotx, "graphics#%d", slot);
152003831d35Sstevel 			if ((prop = find_prop(slotd, slotx)) != NULL)
152103831d35Sstevel 				if ((devpath_p = (char *)(get_prop_val
1522aa5636e5SPeter Tribble 				    (prop))) != NULL)
1523aa5636e5SPeter Tribble 					if (strcmp(devpath_p, controller) == 0)
152403831d35Sstevel 						return (slot);
152503831d35Sstevel 		}
152603831d35Sstevel 		return (-1);
152703831d35Sstevel 	}
152803831d35Sstevel 
152903831d35Sstevel 	/*
153003831d35Sstevel 	 * Get slot-names property from parent node.
153103831d35Sstevel 	 * This property consists of a 32 bit mask indicating which
153203831d35Sstevel 	 * devices are relevant to this bus node. Following are a
153303831d35Sstevel 	 * number of strings depending on how many bits are set in the
153403831d35Sstevel 	 * bit mask; the first string gives the label that is printed
153503831d35Sstevel 	 * on the chassis for the smallest device number, and so on.
153603831d35Sstevel 	 */
153703831d35Sstevel 
153803831d35Sstevel 	prop = find_prop(parent, "slot-names");
153903831d35Sstevel 	if (prop == NULL) {
154003831d35Sstevel 		(void) strcpy(str, "");
154103831d35Sstevel 		return (-1);
154203831d35Sstevel 	}
154303831d35Sstevel 	slot_names_mask = (int *)(get_prop_val(prop));
154403831d35Sstevel 	slot_names = (char *)slot_names_mask;
154503831d35Sstevel 
154603831d35Sstevel 	slot = 1;
154703831d35Sstevel 	slot_names += 4;	/* Skip the 4 byte bitmask */
154803831d35Sstevel 
154903831d35Sstevel 	while (shift < 32) {
155003831d35Sstevel 		/*
155103831d35Sstevel 		 * Shift through the bitmask looking to see if the
155203831d35Sstevel 		 * bit corresponding to "device" is set. If so, copy
155303831d35Sstevel 		 * the correcsponding string to the provided pointer.
155403831d35Sstevel 		 */
155503831d35Sstevel 		if (*slot_names_mask & slot) {
155603831d35Sstevel 			if (shift == device) {
155703831d35Sstevel 				(void) strcpy(str, slot_names);
155803831d35Sstevel 				return (0);
155903831d35Sstevel 			}
156003831d35Sstevel 			slot_names += strlen(slot_names)+1;
156103831d35Sstevel 		}
156203831d35Sstevel 		shift++;
156303831d35Sstevel 		slot = slot << 1;
156403831d35Sstevel 	}
156503831d35Sstevel 	return (-1);
156603831d35Sstevel }
1567