view usr/src/lib/libprtdiag/common/display_sun4v.c @ 5028:71f71fe9e4b9

6594328 Solaris "faulted" CPU status is not reported by PICL
author fw157321
date Tue, 11 Sep 2007 16:36:53 -0700
parents 7b72c4ee69c0
children e2b8f7b9f1c5
line wrap: on
line source

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <kvm.h>
#include <varargs.h>
#include <time.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <sys/openpromio.h>
#include <libintl.h>
#include <syslog.h>
#include <sys/dkio.h>
#include <sys/systeminfo.h>
#include <picldefs.h>
#include <math.h>
#include <errno.h>
#include "pdevinfo.h"
#include "display.h"
#include "display_sun4v.h"
#include "libprtdiag.h"

#if !defined(TEXT_DOMAIN)
#define	TEXT_DOMAIN	"SYS_TEST"
#endif

#define	MOTHERBOARD			"MB"
#define	NETWORK				"network"
#define	PCIE_COMPATIBLE_STR		"pciex"
#define	PCIX_COMPATIBLE_STR		"pci"
#define	SUN4V_MACHINE			"sun4v"
#define	PARENT_NAMES			10

/*
 * Additional OBP properties
 */
#define	OBP_PROP_COMPATIBLE		"compatible"
#define	OBP_PROP_MODEL			"model"
#define	OBP_PROP_SLOT_NAMES		"slot-names"

#define	PICL_NODE_PHYSICAL_PLATFORM	"physical-platform"
#define	PICL_NODE_CHASSIS		"chassis"
#define	MEMORY_SIZE_FIELD		11
#define	INVALID_THRESHOLD		1000000

/*
 * Additional picl classes
 */
#ifndef	PICL_CLASS_SUN4V
#define	PICL_CLASS_SUN4V		"sun4v"
#endif

#ifndef	PICL_PROP_NAC
#define	PICL_PROP_NAC			"nac"
#endif

extern int sys_clk;
extern picl_errno_t sun4v_get_node_by_name(picl_nodehdl_t, char *,
	picl_nodehdl_t *);

static picl_nodehdl_t rooth = 0, phyplatformh = 0;
static picl_nodehdl_t chassish = 0;
static int class_node_found;
static int syserrlog;
static int all_status_ok;

/* local functions */
static int sun4v_get_first_compatible_value(picl_nodehdl_t, char **);
static void sun4v_display_memory_conf(picl_nodehdl_t);
static void sun4v_disp_env_status();
static void sun4v_env_print_fan_sensors();
static void sun4v_env_print_fan_indicators();
static void sun4v_env_print_temp_sensors();
static void sun4v_env_print_temp_indicators();
static void sun4v_env_print_current_sensors();
static void sun4v_env_print_current_indicators();
static void sun4v_env_print_voltage_sensors();
static void sun4v_env_print_voltage_indicators();
static void sun4v_env_print_LEDs();
static void sun4v_print_fru_status();
static void sun4v_print_fw_rev();
static void sun4v_print_chassis_serial_no();

int
sun4v_display(Sys_tree *tree, Prom_node *root, int log,
	picl_nodehdl_t plafh)
{
	void *value;		/* used for opaque PROM data */
	struct mem_total memory_total;	/* Total memory in system */
	struct grp_info grps;	/* Info on all groups in system */
	char machine[MAXSTRLEN];

	if (sysinfo(SI_MACHINE, machine, sizeof (machine)) == -1)
		return (1);
	if (strncmp(machine, SUN4V_MACHINE, strlen(SUN4V_MACHINE)) != 0)
		return (1);

	sys_clk = -1;  /* System clock freq. (in MHz) */

	/*
	 * Now display the machine's configuration. We do this if we
	 * are not logging.
	 */
	if (!logging) {
		struct utsname uts_buf;

		/*
		 * Display system banner
		 */
		(void) uname(&uts_buf);

		log_printf(dgettext(TEXT_DOMAIN, "System Configuration:  "
		    "Sun Microsystems  %s %s\n"), uts_buf.machine,
		    get_prop_val(find_prop(root, "banner-name")), 0);

		/* display system clock frequency */
		value = get_prop_val(find_prop(root, "clock-frequency"));
		if (value != NULL) {
			sys_clk = ((*((int *)value)) + 500000) / 1000000;
			log_printf(dgettext(TEXT_DOMAIN, "System clock "
			    "frequency: %d MHz\n"), sys_clk, 0);
		}

		/* Display the Memory Size */
		display_memorysize(tree, NULL, &grps, &memory_total);

		/* Display the CPU devices */
		sun4v_display_cpu_devices(plafh);

		/* Display the Memory configuration */
		class_node_found = 0;
		sun4v_display_memory_conf(plafh);

		/* Display all the IO cards. */
		(void) sun4v_display_pci(plafh);
		sun4v_display_diaginfo((log || (logging)), root, plafh);

		if (picl_get_root(&rooth) != PICL_SUCCESS)
			return (1);
		if (sun4v_get_node_by_name(rooth, PICL_NODE_PHYSICAL_PLATFORM,
		    &phyplatformh) != PICL_SUCCESS)
			return (1);

		if (picl_find_node(phyplatformh, PICL_PROP_CLASSNAME,
		    PICL_PTYPE_CHARSTRING, (void *)PICL_CLASS_CHASSIS,
		    strlen(PICL_CLASS_CHASSIS), &chassish) != PICL_SUCCESS)
			return (1);

		syserrlog = log;
		sun4v_disp_env_status();
	}
	return (0);
}

static void
get_bus_type(picl_nodehdl_t nodeh, struct io_card *card)
{
	char *compatible;

	(void) strlcpy(card->bus_type, "PCIX", sizeof (card->bus_type));
	if (sun4v_get_first_compatible_value(nodeh, &compatible)
	    == PICL_SUCCESS) {
		if (strncmp(compatible, PCIE_COMPATIBLE_STR,
		    strlen(PCIE_COMPATIBLE_STR)) == 0)
			(void) strlcpy(card->bus_type, "PCIE",
			    sizeof (card->bus_type));
		free(compatible);
	}
}

static picl_errno_t
get_slot_label(picl_nodehdl_t nodeh, struct io_card *card)
{
	char val[PICL_PROPNAMELEN_MAX];
	picl_errno_t err;

	err = picl_get_propval_by_name(nodeh, PICL_PROP_LABEL, val,
	    sizeof (val));
	if (err != PICL_SUCCESS)
		return (err);

	(void) strlcpy(card->slot_str, val, sizeof (card->slot_str));
	card->slot = -1;
	return (PICL_SUCCESS);
}

static void
get_slot_number(picl_nodehdl_t nodeh, struct io_card *card)
{
	picl_errno_t err;
	picl_prophdl_t proph;
	picl_propinfo_t pinfo;
	picl_nodehdl_t pnodeh;
	uint8_t *pval;
	uint32_t dev_mask;
	char uaddr[MAXSTRLEN];
	int i;

	if (get_slot_label(nodeh, card) == PICL_SUCCESS)
		return;
	err = PICL_SUCCESS;
	while (err == PICL_SUCCESS) {
		if (picl_get_propval_by_name(nodeh, PICL_PROP_PARENT, &pnodeh,
		    sizeof (pnodeh)) != PICL_SUCCESS) {
			(void) strlcpy(card->slot_str, MOTHERBOARD,
			    sizeof (card->slot_str));
			card->slot = -1;
			return;
		}
		if (picl_get_propinfo_by_name(pnodeh, OBP_PROP_SLOT_NAMES,
		    &pinfo, &proph) == PICL_SUCCESS) {
			break;
		}
		nodeh = pnodeh;
	}
	if (picl_get_propval_by_name(nodeh, PICL_PROP_UNIT_ADDRESS, uaddr,
	    sizeof (uaddr)) != PICL_SUCCESS) {
		(void) strlcpy(card->slot_str, MOTHERBOARD,
		    sizeof (card->slot_str));
		card->slot = -1;
		return;
	}
	pval = (uint8_t *)malloc(pinfo.size);
	if (!pval) {
		(void) strlcpy(card->slot_str, MOTHERBOARD,
		    sizeof (card->slot_str));
		card->slot = -1;
		return;
	}
	if (picl_get_propval(proph, pval, pinfo.size) != PICL_SUCCESS) {
		(void) strlcpy(card->slot_str, MOTHERBOARD,
		    sizeof (card->slot_str));
		card->slot = -1;
		free(pval);
		return;
	}

	dev_mask = 0;
	for (i = 0; i < sizeof (dev_mask); i++)
		dev_mask |= (*(pval+i) << 8*(sizeof (dev_mask)-1-i));
	for (i = 0; i < sizeof (uaddr) && uaddr[i] != '\0'; i++) {
		if (uaddr[i] == ',') {
			uaddr[i] = '\0';
			break;
		}
	}
	card->slot = atol(uaddr);
	if (((1 << card->slot) & dev_mask) == 0) {
		(void) strlcpy(card->slot_str, MOTHERBOARD,
		    sizeof (card->slot_str));
		card->slot = -1;
	} else {
		char *p = (char *)(pval+sizeof (dev_mask));
		int shift = sizeof (uint32_t)*8-1-card->slot;
		uint32_t x = (dev_mask << shift) >> shift;
		int count = 0;	/* count # of 1's in x */
		int i = 0;
		while (x != 0) {
			count++;
			x &= x-1;
		}
		while (count > 1) {
			while (p[i++] != '\0')
				;
			count--;
		}
		(void) strlcpy(card->slot_str, (char *)(p+i),
		    sizeof (card->slot_str));
	}
	free(pval);
}

/*
 * add all io devices under pci in io list
 */
/* ARGSUSED */
static int
sun4v_pci_callback(picl_nodehdl_t pcih, void *args)
{
	char path[PICL_PROPNAMELEN_MAX];
	char class[PICL_CLASSNAMELEN_MAX];
	char name[PICL_PROPNAMELEN_MAX];
	char model[PICL_PROPNAMELEN_MAX];
	char binding_name[PICL_PROPNAMELEN_MAX];
	char val[PICL_PROPNAMELEN_MAX];
	char *compatible;
	picl_errno_t err;
	picl_nodehdl_t nodeh;
	struct io_card pci_card;

	/* Walk through the children */

	err = picl_get_propval_by_name(pcih, PICL_PROP_CHILD, &nodeh,
	    sizeof (picl_nodehdl_t));

	while (err == PICL_SUCCESS) {
		err = picl_get_propval_by_name(nodeh, PICL_PROP_CLASSNAME,
		    class, sizeof (class));
		if (err !=  PICL_SUCCESS)
			return (err);

		if (args) {
			char *val = args;
			if (strcmp(class, val) == 0) {
				err = picl_get_propval_by_name(nodeh,
				    PICL_PROP_PEER, &nodeh,
				    sizeof (picl_nodehdl_t));
				continue;
			} else if (strcmp(val, PICL_CLASS_PCIEX) == 0 &&
			    strcmp(class, PICL_CLASS_PCI) == 0) {
				err = picl_get_propval_by_name(nodeh,
				    PICL_PROP_PEER, &nodeh,
				    sizeof (picl_nodehdl_t));
				continue;
			} else if (strcmp(val, PICL_CLASS_PCI) == 0 &&
			    strcmp(class, PICL_CLASS_PCIEX) == 0) {
				err = picl_get_propval_by_name(nodeh,
				    PICL_PROP_PEER, &nodeh,
				    sizeof (picl_nodehdl_t));
				continue;
			}
		}

		err = picl_get_propval_by_name(nodeh, PICL_PROP_DEVFS_PATH,
		    path, sizeof (path));
		if (err != PICL_SUCCESS)
			return (err);

		(void) strlcpy(pci_card.notes, path, sizeof (pci_card.notes));

		get_bus_type(nodeh, &pci_card);
		get_slot_number(nodeh, &pci_card);

		err = picl_get_propval_by_name(nodeh, PICL_PROP_NAME, name,
		    sizeof (name));
		if (err == PICL_PROPNOTFOUND)
			(void) strlcpy(name, "", sizeof (name));
		else if (err != PICL_SUCCESS)
			return (err);

		err = picl_get_propval_by_name(nodeh, PICL_PROP_STATUS, val,
		    sizeof (val));
		if (err == PICL_PROPNOTFOUND)
			(void) strlcpy(val, "", sizeof (val));
		else if (err != PICL_SUCCESS)
			return (err);

		/* Figure NAC name */
		if (pci_card.slot != -1)
			(void) snprintf(pci_card.status,
			    sizeof (pci_card.status),
			    "%s%d", pci_card.slot_str,
			    pci_card.slot);
		else
			(void) snprintf(pci_card.status,
			    sizeof (pci_card.status),
			    "%s", pci_card.slot_str);

		/*
		 * Get the name of this card. If binding_name is found,
		 * name will be <nodename>-<binding_name>.
		 */
		err = picl_get_propval_by_name(nodeh, PICL_PROP_BINDING_NAME,
		    binding_name, sizeof (binding_name));
		if (err == PICL_SUCCESS) {
			if (strcmp(name, binding_name) != 0) {
				(void) strlcat(name, "-", sizeof (name));
				(void) strlcat(name, binding_name,
				    sizeof (name));
			}
		} else if (err == PICL_PROPNOTFOUND) {
			/*
			 * if compatible prop is not found, name will be
			 * <nodename>-<compatible>
			 */
			err = sun4v_get_first_compatible_value(nodeh,
			    &compatible);
			if (err == PICL_SUCCESS) {
				(void) strlcat(name, "-", sizeof (name));
				(void) strlcat(name, compatible,
				    sizeof (name));
				free(compatible);
			}
		} else
			return (err);

		(void) strlcpy(pci_card.name, name, sizeof (pci_card.name));

		/* Get the model of this card */

		err = picl_get_propval_by_name(nodeh, OBP_PROP_MODEL,
		    model, sizeof (model));
		if (err == PICL_PROPNOTFOUND)
			(void) strlcpy(model, "", sizeof (model));
		else if (err != PICL_SUCCESS)
			return (err);
		(void) strlcpy(pci_card.model, model, sizeof (pci_card.model));

		/* Print NAC name */
		log_printf("%-18s", pci_card.status);
		/* Print IO Type */
		log_printf("%-6s", pci_card.bus_type);
		/* Printf Card Name */
		log_printf("%-44s", pci_card.name);
		/* Print Card Model */
		log_printf("%-8s", pci_card.model);
		log_printf("\n");
		/* Print Status */
		log_printf("%-18s", val);
		/* Print IO Type */
		log_printf("%-6s", "");
		/* Print Parent Path */
		log_printf("%-44s", pci_card.notes);
		log_printf("\n");

		err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER, &nodeh,
		    sizeof (picl_nodehdl_t));
	}
	return (PICL_WALK_CONTINUE);
}

/*
 * display_pci
 * Display all the PCI IO cards on this board.
 */
void
sun4v_display_pci(picl_nodehdl_t plafh)
{
	char *fmt = "%-17s %-5s %-44s %-8s";
	/* Have we printed the column headings? */
	static int banner = FALSE;

	if (banner == FALSE) {
		log_printf("\n");
		log_printf("================================");
		log_printf(" IO Devices ");
		log_printf("================================");
		log_printf("\n");
		log_printf(fmt, "Slot +", "Bus", "Name +", "Model", 0);
		log_printf("\n");
		log_printf(fmt, "Status", "Type", "Path", "", 0);
		log_printf("\n");
		log_printf("---------------------------------"
		    "-------------------------------------------\n");
		banner = TRUE;
	}

	(void) picl_walk_tree_by_class(plafh, PICL_CLASS_PCIEX,
	    PICL_CLASS_PCIEX, sun4v_pci_callback);
	(void) picl_walk_tree_by_class(plafh, PICL_CLASS_PCI,
	    PICL_CLASS_PCI, sun4v_pci_callback);
	(void) picl_walk_tree_by_class(plafh, PICL_CLASS_SUN4V,
	    PICL_CLASS_SUN4V, sun4v_pci_callback);
}

/*
 * return the first compatible value
 */
static int
sun4v_get_first_compatible_value(picl_nodehdl_t nodeh, char **outbuf)
{
	picl_errno_t err;
	picl_prophdl_t proph;
	picl_propinfo_t pinfo;
	picl_prophdl_t tblh;
	picl_prophdl_t rowproph;
	char *pval;

	err = picl_get_propinfo_by_name(nodeh, OBP_PROP_COMPATIBLE,
	    &pinfo, &proph);
	if (err != PICL_SUCCESS)
		return (err);

	if (pinfo.type == PICL_PTYPE_CHARSTRING) {
		pval = malloc(pinfo.size);
		if (pval == NULL)
			return (PICL_FAILURE);
		err = picl_get_propval(proph, pval, pinfo.size);
		if (err != PICL_SUCCESS) {
			free(pval);
			return (err);
		}
		*outbuf = pval;
		return (PICL_SUCCESS);
	}

	if (pinfo.type != PICL_PTYPE_TABLE)
		return (PICL_FAILURE);

	/* get first string from table */
	err = picl_get_propval(proph, &tblh, pinfo.size);
	if (err != PICL_SUCCESS)
		return (err);

	err = picl_get_next_by_row(tblh, &rowproph);
	if (err != PICL_SUCCESS)
		return (err);

	err = picl_get_propinfo(rowproph, &pinfo);
	if (err != PICL_SUCCESS)
		return (err);

	pval = malloc(pinfo.size);
	if (pval == NULL)
		return (PICL_FAILURE);

	err = picl_get_propval(rowproph, pval, pinfo.size);
	if (err != PICL_SUCCESS) {
		free(pval);
		return (err);
	}

	*outbuf = pval;
	return (PICL_SUCCESS);
}

/*
 * print size of a memory segment
 */
static void
print_memory_segment_size(uint64_t size)
{
	uint64_t kbyte = 1024;
	uint64_t mbyte = kbyte * kbyte;
	uint64_t gbyte = kbyte * mbyte;
	char buf[MEMORY_SIZE_FIELD];

	if (size >= gbyte) {
		if (size % gbyte == 0)
			(void) snprintf(buf, sizeof (buf), "%d GB",
			    (int)(size / gbyte));
		else
			(void) snprintf(buf, sizeof (buf), "%.2f GB",
			    (float)size / gbyte);
	} else if (size >= mbyte) {
		if (size % mbyte == 0)
			(void) snprintf(buf, sizeof (buf), "%d MB",
			    (int)(size / mbyte));
		else
			(void) snprintf(buf, sizeof (buf), "%.2f MB",
			    (float)size / mbyte);
	} else {
		if (size % kbyte == 0)
			(void) snprintf(buf, sizeof (buf), "%d KB",
			    (int)(size / kbyte));
		else
			(void) snprintf(buf, sizeof (buf), "%.2f KB",
			    (float)size / kbyte);
	}
	log_printf("%-7s ", buf);
}

/*
 * Enumerate banks and dimms within a memory segment.  We're handed
 * the first bank within the segment - we assume there are dimms
 * (memory-module) nodes underneath.
 */
static void
print_memory_segment_contain(picl_nodehdl_t bank_nodeh)
{
	char val[PICL_PROPNAMELEN_MAX];
	picl_nodehdl_t module_nodeh;
	int flag = 0;

	do {
		if (picl_get_propval_by_name(bank_nodeh, PICL_PROP_CHILD,
		    &module_nodeh, sizeof (picl_nodehdl_t)) != PICL_SUCCESS)
			continue;
		do {
			if (picl_get_propval_by_name(module_nodeh,
			    PICL_PROP_NAC, val, sizeof (val)) !=
			    PICL_SUCCESS)
				continue;
			else {
				if (!flag) {
					log_printf("%-30s\n", val);
					flag = 1;
				} else
					log_printf("%57s\n", val);
			}
		} while (picl_get_propval_by_name(module_nodeh, PICL_PROP_PEER,
		    &module_nodeh, sizeof (picl_nodehdl_t)) ==
		    PICL_SUCCESS);
	} while (picl_get_propval_by_name(bank_nodeh, PICL_PROP_PEER,
	    &bank_nodeh, sizeof (picl_nodehdl_t)) == PICL_SUCCESS);
}

/*
 * Search node where _class=="memory-segment"
 * print "Base Address", "Size", etc
 */
/*ARGSUSED*/
static int
sun4v_memory_conf_callback(picl_nodehdl_t nodeh, void *args)
{
	uint64_t base;
	uint64_t size;
	uint64_t ifactor;
	picl_errno_t err = PICL_SUCCESS;

	if (class_node_found == 0) {
		class_node_found = 1;
		return (PICL_WALK_TERMINATE);
	}
	while (err == PICL_SUCCESS) {
		err = picl_get_propval_by_name(nodeh, PICL_PROP_BASEADDRESS,
		    &base, sizeof (base));
		if (err !=  PICL_SUCCESS)
			break;
		err = picl_get_propval_by_name(nodeh, PICL_PROP_SIZE,
		    &size, sizeof (size));
		if (err !=  PICL_SUCCESS)
			break;
		err = picl_get_propval_by_name(nodeh,
		    PICL_PROP_INTERLEAVE_FACTOR, &ifactor,
		    sizeof (ifactor));
		if (err !=  PICL_SUCCESS)
			break;
		log_printf("%-13llx", base);
		print_memory_segment_size(size);
		log_printf("%-18lld", ifactor);
		err = picl_get_propval_by_name(nodeh, PICL_PROP_CHILD,
		    &nodeh, sizeof (nodeh));
		if (err ==  PICL_SUCCESS)
			print_memory_segment_contain(nodeh);
		log_printf("\n");
		err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER, &nodeh,
		    sizeof (picl_nodehdl_t));
	}

	return (PICL_WALK_CONTINUE);
}

/*ARGSUSED*/
void
sun4v_display_memory_conf(picl_nodehdl_t plafh)
{
	char *fmt = "%-12s %-7s %-9s %-20s";
	(void) picl_walk_tree_by_class(plafh, PICL_CLASS_MEMORY_SEGMENT,
	    NULL, sun4v_memory_conf_callback);
	if (class_node_found == 0)
		return;
	log_printf("\n");
	log_printf("============================");
	log_printf(" Memory Configuration ");
	log_printf("============================");
	log_printf("\n");
	log_printf("Segment Table:\n");
	log_printf(
	    "---------------------------------------------------------\n");
	log_printf(fmt, "Base Address", "Size", "Interleave Factor",
	    "Contains", 0);
	log_printf("\n");
	log_printf(
	    "---------------------------------------------------------\n");
	(void) picl_walk_tree_by_class(plafh, PICL_CLASS_MEMORY_SEGMENT,
	    NULL, sun4v_memory_conf_callback);
}

void
sun4v_display_cpu_devices(picl_nodehdl_t plafh)
{
	char *fmt = "%-12s %-5s %-8s %-19s %-5s";

	/*
	 * Display the table header for CPUs . Then display the CPU
	 * frequency, cache size, and processor revision of all cpus.
	 */
	log_printf(dgettext(TEXT_DOMAIN,
	    "\n"
	    "========================="
	    " CPUs "
	    "==============================================="
	    "\n"
	    "\n"));
	log_printf(fmt, "", "", "", "CPU", "CPU", 0);
	log_printf("\n");
	log_printf(fmt, "Location", "CPU", "Freq",
	    "Implementation", "State", 0);
	log_printf("\n");
	log_printf(fmt, "------------", "-----", "--------",
	    "-------------------", "-----", 0);
	log_printf("\n");

	(void) picl_walk_tree_by_class(plafh, "cpu", "cpu", sun4v_display_cpus);
}

/*
 * Display the CPUs present on this board.
 */
/*ARGSUSED*/
int
sun4v_display_cpus(picl_nodehdl_t cpuh, void* args)
{
	int status;
	picl_prophdl_t proph;
	picl_prophdl_t tblh;
	picl_prophdl_t rowproph;
	picl_propinfo_t propinfo;
	int *int_value;
	int cpuid;
	char *comp_value;
	char *no_prop_value = "   ";
	char freq_str[MAXSTRLEN];
	char fru_name[MAXSTRLEN];
	char state[MAXSTRLEN];

	/*
	 * Get cpuid property and print it and the NAC name
	 */
	status = picl_get_propinfo_by_name(cpuh, "cpuid", &propinfo, &proph);
	if (status == PICL_SUCCESS) {
		status = picl_get_propval(proph, &cpuid, sizeof (cpuid));
		if (status != PICL_SUCCESS) {
			log_printf("%-13s", no_prop_value);
			log_printf("%-6s", no_prop_value);
		} else {
			(void) snprintf(fru_name, sizeof (fru_name), "%s%d",
			    CPU_STRAND_NAC, cpuid);
			log_printf("%-13s", fru_name);
			log_printf("%-6d", cpuid);
		}
	} else {
		log_printf("%-13s", no_prop_value);
		log_printf("%-6s", no_prop_value);
	}

clock_freq:
	status = picl_get_propinfo_by_name(cpuh, "clock-frequency", &propinfo,
	    &proph);
	if (status == PICL_SUCCESS) {
		int_value = malloc(propinfo.size);
		if (int_value == NULL) {
			log_printf("%-9s", no_prop_value);
			goto compatible;
		}
		status = picl_get_propval(proph, int_value, propinfo.size);
		if (status != PICL_SUCCESS) {
			log_printf("%-9s", no_prop_value);
		} else {
			/* Running frequency */
			(void) snprintf(freq_str, sizeof (freq_str), "%d MHz",
			    CLK_FREQ_TO_MHZ(*int_value));
			log_printf("%-9s", freq_str);
		}
		free(int_value);
	} else
		log_printf("%-9s", no_prop_value);

compatible:
	status = picl_get_propinfo_by_name(cpuh, "compatible", &propinfo,
	    &proph);
	if (status == PICL_SUCCESS) {
		if (propinfo.type == PICL_PTYPE_CHARSTRING) {
			/*
			 * Compatible Property only has 1 value
			 */
			comp_value = malloc(propinfo.size);
			if (comp_value == NULL) {
				log_printf("%-20s", no_prop_value, 0);
				goto state;
			}
			status = picl_get_propval(proph, comp_value,
			    propinfo.size);
			if (status != PICL_SUCCESS)
				log_printf("%-20s", no_prop_value, 0);
			else
				log_printf("%-20s", comp_value, 0);
			free(comp_value);
		} else if (propinfo.type == PICL_PTYPE_TABLE) {
			/*
			 * Compatible Property has multiple values
			 */
			status = picl_get_propval(proph, &tblh, propinfo.size);
			if (status != PICL_SUCCESS) {
				log_printf("%-20s", no_prop_value, 0);
				goto state;
			}
			status = picl_get_next_by_row(tblh, &rowproph);
			if (status != PICL_SUCCESS) {
				log_printf("%-20s", no_prop_value, 0);
				goto state;
			}

			status = picl_get_propinfo(rowproph, &propinfo);
			if (status != PICL_SUCCESS) {
				log_printf("%-20s", no_prop_value, 0);
				goto state;
			}

			comp_value = malloc(propinfo.size);
			if (comp_value == NULL) {
				log_printf("%-20s", no_prop_value, 0);
				goto state;
			}
			status = picl_get_propval(rowproph, comp_value,
			    propinfo.size);
			if (status != PICL_SUCCESS)
				log_printf("%-20s", no_prop_value, 0);
			else
				log_printf("%-20s", comp_value, 0);
			free(comp_value);
		}
	} else
		log_printf("%-20s", no_prop_value, 0);

state:
	status = picl_get_propinfo_by_name(cpuh, PICL_PROP_STATE,
	    &propinfo, &proph);
	if (status == PICL_SUCCESS) {
		status = picl_get_propval(proph, state, sizeof (state));
		if (status != PICL_SUCCESS) {
			log_printf("%-9s", no_prop_value);
		} else {
			log_printf("%-9s", state);
		}
	} else
		log_printf("%-9s", no_prop_value);

done:
	log_printf("\n");
	return (PICL_WALK_CONTINUE);
}

void
sun4v_display_diaginfo(int flag, Prom_node *root, picl_nodehdl_t plafh)
{
#ifdef	lint
	flag = flag;
	root = root;
	plafh = plafh;
#endif
	/*
	 * This function is intentionally empty
	 */
}

void
display_boardnum(int num)
{
	log_printf("%2d   ", num, 0);
}

static void
sun4v_disp_env_status()
{
	if (phyplatformh == 0)
		return;
	log_printf("\n");
	log_printf("============================");
	log_printf(" Environmental Status ");
	log_printf("============================");
	log_printf("\n");

	class_node_found = 0;
	all_status_ok = 1;
	sun4v_env_print_fan_sensors();

	class_node_found = 0;
	all_status_ok = 1;
	sun4v_env_print_fan_indicators();

	class_node_found = 0;
	all_status_ok = 1;
	sun4v_env_print_temp_sensors();

	class_node_found = 0;
	all_status_ok = 1;
	sun4v_env_print_temp_indicators();

	class_node_found = 0;
	all_status_ok = 1;
	sun4v_env_print_current_sensors();

	class_node_found = 0;
	all_status_ok = 1;
	sun4v_env_print_current_indicators();

	class_node_found = 0;
	all_status_ok = 1;
	sun4v_env_print_voltage_sensors();

	class_node_found = 0;
	all_status_ok = 1;
	sun4v_env_print_voltage_indicators();

	class_node_found = 0;
	sun4v_env_print_LEDs();

	class_node_found = 0;
	all_status_ok = 1;
	sun4v_print_fru_status();

	class_node_found = 0;
	sun4v_print_fw_rev();

	sun4v_print_chassis_serial_no();
}

/*ARGSUSED*/
static int
sun4v_env_print_sensor_callback(picl_nodehdl_t nodeh, void *args)
{
	char val[PICL_PROPNAMELEN_MAX];
	picl_nodehdl_t parenth;
	char *names[PARENT_NAMES];
	char *base_units[PICL_PROPNAMELEN_MAX];
	char *loc;
	int i;
	char *prop;
	picl_errno_t err;
	int32_t lo_warning, lo_shutdown;
	int32_t hi_warning, hi_shutdown;
	int32_t current_val;
	int32_t exponent;
	double display_val;
	typedef enum {SENSOR_OK, SENSOR_WARN, SENSOR_FAILED,
	    SENSOR_DISABLED, SENSOR_UNKNOWN} sensor_status_t;
	sensor_status_t sensor_status = SENSOR_OK;

	if (class_node_found == 0) {
		class_node_found = 1;
		return (PICL_WALK_TERMINATE);
	}

	prop = (char *)args;
	if (!prop) {
		sensor_status = SENSOR_UNKNOWN;
		all_status_ok = 0;
	} else {
		err = picl_get_propval_by_name(nodeh,
		    PICL_PROP_OPERATIONAL_STATUS, val,
		    sizeof (val));
		if (err == PICL_SUCCESS) {
			if (strcmp(val, "disabled") == 0) {
				sensor_status = SENSOR_DISABLED;
			}
		}
	}

	if (sensor_status != SENSOR_DISABLED &&
	    sensor_status != SENSOR_UNKNOWN) {
		if (picl_get_propval_by_name(nodeh, prop, &current_val,
		    sizeof (current_val)) != PICL_SUCCESS) {
			sensor_status = SENSOR_UNKNOWN;
		}
		if (picl_get_propval_by_name(nodeh, PICL_PROP_LOW_WARNING,
		    &lo_warning, sizeof (lo_warning)) != PICL_SUCCESS)
			lo_warning = INVALID_THRESHOLD;
		if (picl_get_propval_by_name(nodeh, PICL_PROP_LOW_SHUTDOWN,
		    &lo_shutdown, sizeof (lo_shutdown)) != PICL_SUCCESS)
			lo_shutdown = INVALID_THRESHOLD;
		if (picl_get_propval_by_name(nodeh, PICL_PROP_HIGH_WARNING,
		    &hi_warning, sizeof (hi_warning)) != PICL_SUCCESS)
			hi_warning = INVALID_THRESHOLD;
		if (picl_get_propval_by_name(nodeh, PICL_PROP_HIGH_SHUTDOWN,
		    &hi_shutdown, sizeof (hi_shutdown)) != PICL_SUCCESS)
			hi_shutdown = INVALID_THRESHOLD;

		if ((lo_shutdown != INVALID_THRESHOLD &&
		    current_val <= lo_shutdown) ||
		    (hi_shutdown != INVALID_THRESHOLD &&
		    current_val >= hi_shutdown)) {
			sensor_status = SENSOR_FAILED;
		} else if ((lo_warning != INVALID_THRESHOLD &&
		    current_val <= lo_warning) ||
		    (hi_warning != INVALID_THRESHOLD &&
		    current_val >= hi_warning)) {
			sensor_status = SENSOR_WARN;
		} else {
			sensor_status = SENSOR_OK;
		}
	}

	if (syserrlog == 0) {
		if (sensor_status != SENSOR_OK && all_status_ok == 1) {
			all_status_ok = 0;
			return (PICL_WALK_TERMINATE);
		}
		if (sensor_status == SENSOR_OK) {
			return (PICL_WALK_CONTINUE);
		}
	}

	/*
	 * If we're here then prtdiag was invoked with "-v" or we have
	 * a sensor that is beyond a threshold, so give them a book to
	 * read instead of the Cliff Notes.
	 */
	err = picl_get_propval_by_name(nodeh, PICL_PROP_PARENT, &parenth,
	    sizeof (parenth));
	if (err != PICL_SUCCESS) {
		log_printf("\n");
		return (PICL_WALK_CONTINUE);
	}

	/* gather up the path name for the sensor */
	if ((loc = (char *)malloc(PICL_PROPNAMELEN_MAX*PARENT_NAMES)) != NULL) {
		for (i = 0; i < PARENT_NAMES; i++) {
			if ((names[i] = (char *)malloc(PICL_PROPNAMELEN_MAX)) ==
			    NULL) {
				while (--i > -1)
					free(names[i]);
				free(loc);
				loc = NULL;
			}
		}
	}
	i = 0;
	if (loc != 0) {
		while (err == PICL_SUCCESS) {
			if (parenth == phyplatformh)
				break;
			err = picl_get_propval_by_name(parenth, PICL_PROP_NAME,
			    names[i++], PICL_PROPNAMELEN_MAX);
			if (err != PICL_SUCCESS) {
				i--;
				break;
			}
			if (i == PARENT_NAMES)
				break;
			err = picl_get_propval_by_name(parenth,
			    PICL_PROP_PARENT, &parenth, sizeof (parenth));
		}
		loc[0] = '\0';
		if (--i > -1) {
			(void) strlcat(loc, names[i],
			    PICL_PROPNAMELEN_MAX * PARENT_NAMES);
		}
		while (--i > -1) {
			(void) strlcat(loc, "/", PICL_PROPNAMELEN_MAX *
			    PARENT_NAMES);
			(void) strlcat(loc, names[i],
			    PICL_PROPNAMELEN_MAX * PARENT_NAMES);
		}
		log_printf("%-35s", loc);
		for (i = 0; i < PARENT_NAMES; i++)
			free(names[i]);
		free(loc);
	} else {
		log_printf("%-35s", " ");
	}
	err = picl_get_propval_by_name(nodeh, PICL_PROP_LABEL, val,
	    sizeof (val));
	if (err == PICL_SUCCESS)
		log_printf("%-15s", val);

	/*
	 * Get the exponent if present, and do a little math so that
	 * if we need to we can print a normalized value for the
	 * sensor reading.
	 */
	if (picl_get_propval_by_name(nodeh, PICL_PROP_EXPONENT,
	    &exponent, sizeof (exponent)) != PICL_SUCCESS)
		exponent = 0;
	if (exponent == 0)
		display_val = (double)current_val;
	else
		display_val = (double)current_val *
		    pow((double)10, (double)exponent);
	err = picl_get_propval_by_name(nodeh, PICL_PROP_BASE_UNITS,
	    base_units, sizeof (base_units));
	if (err != PICL_SUCCESS)
		base_units[0] = '\0';

	switch (sensor_status) {
	case SENSOR_FAILED:
		log_printf("%-s", "failed (");
		log_printf("%-.*f", abs(exponent), display_val);
		log_printf("%-s %s", base_units, ")");
		break;
	case SENSOR_WARN:
		log_printf("%-s", "warning (");
		log_printf("%-.*f", abs(exponent), display_val);
		log_printf("%-s %s", base_units, ")");
		break;
	case SENSOR_DISABLED:
		log_printf("%-s", "disabled");
		break;
	case SENSOR_OK:
		log_printf("%-s", "ok");
		break;
	default:
		log_printf("%-s", "unknown");
		break;
	}

	log_printf("\n");
	return (PICL_WALK_CONTINUE);
}

/*ARGSUSED*/
static int
sun4v_env_print_indicator_callback(picl_nodehdl_t nodeh, void *args)
{
	char current_val[PICL_PROPNAMELEN_MAX];
	char expected_val[PICL_PROPNAMELEN_MAX];
	char label[PICL_PROPNAMELEN_MAX];
	picl_nodehdl_t parenth;
	char *names[PARENT_NAMES];
	char *loc;
	int i = 0;
	char *prop = (char *)args;
	picl_errno_t err = PICL_SUCCESS;
	typedef enum {SENSOR_OK, SENSOR_WARN, SENSOR_FAILED,
	    SENSOR_DISABLED, SENSOR_UNKNOWN} sensor_status_t;
	sensor_status_t sensor_status = SENSOR_OK;

	if (class_node_found == 0) {
		class_node_found = 1;
		return (PICL_WALK_TERMINATE);
	}

	prop = (char *)args;
	if (!prop) {
		sensor_status = SENSOR_UNKNOWN;
		all_status_ok = 0;
	} else {
		err = picl_get_propval_by_name(nodeh,
		    PICL_PROP_OPERATIONAL_STATUS, current_val,
		    sizeof (current_val));
		if (err == PICL_SUCCESS) {
			if (strcmp(current_val, "disabled") == 0) {
				sensor_status = SENSOR_DISABLED;
			}
		}
	}

	if (sensor_status != SENSOR_DISABLED &&
	    sensor_status != SENSOR_UNKNOWN) {
		if (picl_get_propval_by_name(nodeh, prop, &current_val,
		    sizeof (current_val)) != PICL_SUCCESS) {
			(void) strlcpy(current_val, "unknown",
			    sizeof (current_val));
			sensor_status = SENSOR_UNKNOWN;
		} else {
			if (picl_get_propval_by_name(nodeh, PICL_PROP_EXPECTED,
			    &expected_val, sizeof (expected_val)) !=
			    PICL_SUCCESS) {
				sensor_status = SENSOR_UNKNOWN;
			} else {
				if (strncmp(current_val, expected_val,
				    sizeof (current_val)) == 0) {
					sensor_status = SENSOR_OK;
				} else {
					sensor_status = SENSOR_FAILED;
				}
			}
		}
	}

	if (syserrlog == 0) {
		if (sensor_status != SENSOR_OK && all_status_ok == 1) {
			all_status_ok = 0;
			return (PICL_WALK_TERMINATE);
		}
		if (sensor_status == SENSOR_OK) {
			return (PICL_WALK_CONTINUE);
		}
	}

	/*
	 * If we're here then prtdiag was invoked with "-v" or we have
	 * a sensor that is beyond a threshold, so give them a book to
	 * read instead of the Cliff Notes.
	 */
	err = picl_get_propval_by_name(nodeh, PICL_PROP_PARENT, &parenth,
	    sizeof (parenth));
	if (err != PICL_SUCCESS) {
		log_printf("\n");
		return (PICL_WALK_CONTINUE);
	}
	if ((loc = (char *)malloc(PICL_PROPNAMELEN_MAX*PARENT_NAMES)) != NULL) {
		for (i = 0; i < PARENT_NAMES; i++) {
			if ((names[i] = (char *)malloc(PICL_PROPNAMELEN_MAX)) ==
			    NULL) {
				while (--i > -1)
					free(names[i]);
				free(loc);
				loc = NULL;
			}
		}
	}
	i = 0;
	if (loc) {
		while (err == PICL_SUCCESS) {
			if (parenth == phyplatformh)
				break;
			err = picl_get_propval_by_name(parenth, PICL_PROP_NAME,
			    names[i++], PICL_PROPNAMELEN_MAX);
			if (err != PICL_SUCCESS) {
				i--;
				break;
			}
			if (i == PARENT_NAMES)
				break;
			err = picl_get_propval_by_name(parenth,
			    PICL_PROP_PARENT, &parenth, sizeof (parenth));
		}
		loc[0] = '\0';
		if (--i > -1) {
			(void) strlcat(loc, names[i],
			    PICL_PROPNAMELEN_MAX * PARENT_NAMES);
		}
		while (--i > -1) {
			(void) strlcat(loc, "/", PICL_PROPNAMELEN_MAX *
			    PARENT_NAMES);
			(void) strlcat(loc, names[i],
			    PICL_PROPNAMELEN_MAX * PARENT_NAMES);
		}
		log_printf("%-35s", loc);
		for (i = 0; i < PARENT_NAMES; i++)
			free(names[i]);
		free(loc);
	} else {
		log_printf("%-35s", "");
	}

	err = picl_get_propval_by_name(nodeh, PICL_PROP_LABEL, label,
	    sizeof (label));
	if (err != PICL_SUCCESS)
		(void) strlcpy(label, "", sizeof (label));
	log_printf("%-15s", label);

	log_printf("%-8s", current_val);

	log_printf("\n");
	return (PICL_WALK_CONTINUE);
}

static void
sun4v_env_print_fan_sensors()
{
	char *fmt = "%-34s %-14s %-10s\n";
	/*
	 * If there isn't any fan sensor node, return now.
	 */
	(void) picl_walk_tree_by_class(phyplatformh,
	    PICL_CLASS_RPM_SENSOR, (void *)PICL_PROP_SPEED,
	    sun4v_env_print_sensor_callback);
	if (!class_node_found)
		return;
	log_printf("Fan sensors:\n");
	if (syserrlog == 0) {
		(void) picl_walk_tree_by_class(phyplatformh,
		    PICL_CLASS_RPM_SENSOR,
		    PICL_PROP_SPEED, sun4v_env_print_sensor_callback);
		if (all_status_ok) {
			log_printf("All fan sensors are OK.\n");
			return;
		}
	}
	log_printf("-------------------------------------------------"
	    "-----------\n");
	log_printf(fmt, "Location", "Sensor", "Status", 0);
	log_printf("-------------------------------------------------"
	    "-----------\n");
	(void) picl_walk_tree_by_class(phyplatformh, PICL_CLASS_RPM_SENSOR,
	    PICL_PROP_SPEED, sun4v_env_print_sensor_callback);
}

static void
sun4v_env_print_fan_indicators()
{
	char *fmt = "%-34s %-14s %-10s\n";
	(void) picl_walk_tree_by_class(phyplatformh,
	    PICL_CLASS_RPM_INDICATOR, (void *)PICL_PROP_CONDITION,
	    sun4v_env_print_indicator_callback);
	if (!class_node_found)
		return;
	log_printf("\nFan indicators:\n");
	if (syserrlog == 0) {
		(void) picl_walk_tree_by_class(phyplatformh,
		    PICL_CLASS_RPM_INDICATOR,
		    (void *)PICL_PROP_CONDITION,
		    sun4v_env_print_indicator_callback);
		if (all_status_ok) {
			log_printf("All fan indicators are OK.\n");
			return;
		}
	}
	log_printf("-------------------------------------------------"
	    "-----------\n");
	log_printf(fmt, "Location", "Sensor", "Condition", 0);
	log_printf("-------------------------------------------------"
	    "-----------\n");
	(void) picl_walk_tree_by_class(phyplatformh, PICL_CLASS_RPM_INDICATOR,
	    (void *)PICL_PROP_CONDITION, sun4v_env_print_indicator_callback);
}

static void
sun4v_env_print_temp_sensors()
{
	char *fmt = "%-34s %-14s %-10s\n";
	(void) picl_walk_tree_by_class(phyplatformh,
	    PICL_CLASS_TEMPERATURE_SENSOR,
	    (void *)PICL_PROP_TEMPERATURE,
	    sun4v_env_print_sensor_callback);
	if (!class_node_found)
		return;

	log_printf("\nTemperature sensors:\n");
	if (syserrlog == 0) {
		(void) picl_walk_tree_by_class(phyplatformh,
		    PICL_CLASS_TEMPERATURE_SENSOR,
		    PICL_PROP_TEMPERATURE, sun4v_env_print_sensor_callback);
		if (all_status_ok) {
			log_printf("All temperature sensors are OK.\n");
			return;
		}
	}
	log_printf("-------------------------------------------------"
	    "-----------\n");
	log_printf(fmt, "Location", "Sensor", "Status", 0);
	log_printf("-------------------------------------------------"
	    "-----------\n");
	(void) picl_walk_tree_by_class(phyplatformh,
	    PICL_CLASS_TEMPERATURE_SENSOR,
	    (void *)PICL_PROP_TEMPERATURE, sun4v_env_print_sensor_callback);
}

static void
sun4v_env_print_temp_indicators()
{
	char *fmt = "%-34s %-14s %-8s\n";
	(void) picl_walk_tree_by_class(phyplatformh,
	    PICL_CLASS_TEMPERATURE_INDICATOR, (void *)PICL_PROP_CONDITION,
	    sun4v_env_print_indicator_callback);
	if (!class_node_found)
		return;
	log_printf("\nTemperature indicators:\n");
	if (syserrlog == 0) {
		(void) picl_walk_tree_by_class(phyplatformh,
		    PICL_CLASS_TEMPERATURE_INDICATOR,
		    (void *)PICL_PROP_CONDITION,
		    sun4v_env_print_indicator_callback);
		if (all_status_ok) {
			log_printf("All temperature indicators are OK.\n");
			return;
		}
	}
	log_printf("-------------------------------------------------"
	    "-----------\n");
	log_printf(fmt, "Location", "Indicator", "Condition", 0);
	log_printf("-------------------------------------------------"
	    "-----------\n");
	(void) picl_walk_tree_by_class(phyplatformh,
	    PICL_CLASS_TEMPERATURE_INDICATOR,
	    (void *)PICL_PROP_CONDITION,
	    sun4v_env_print_indicator_callback);
}

static void
sun4v_env_print_current_sensors()
{
	char *fmt = "%-34s %-14s %-10s\n";
	(void) picl_walk_tree_by_class(phyplatformh, PICL_CLASS_CURRENT_SENSOR,
	    (void *)PICL_PROP_CURRENT, sun4v_env_print_sensor_callback);
	if (!class_node_found)
		return;
	log_printf("\nCurrent sensors:\n");
	if (syserrlog == 0) {
		(void) picl_walk_tree_by_class(phyplatformh,
		    PICL_CLASS_CURRENT_SENSOR,
		    PICL_PROP_CURRENT, sun4v_env_print_sensor_callback);
		if (all_status_ok) {
			log_printf("All current sensors are OK.\n");
			return;
		}
	}
	log_printf("-------------------------------------------------"
	    "-----------\n");
	log_printf(fmt, "Location", "Sensor", "Status", 0);
	log_printf("-------------------------------------------------"
	    "-----------\n");
	(void) picl_walk_tree_by_class(phyplatformh,
	    PICL_CLASS_CURRENT_SENSOR, (void *)PICL_PROP_CURRENT,
	    sun4v_env_print_sensor_callback);
}

static void
sun4v_env_print_current_indicators()
{
	char *fmt = "%-34s %-14s %-8s\n";
	(void) picl_walk_tree_by_class(phyplatformh,
	    PICL_CLASS_CURRENT_INDICATOR,
	    (void *)PICL_PROP_CONDITION,
	    sun4v_env_print_indicator_callback);
	if (!class_node_found)
		return;
	log_printf("\nCurrent indicators:\n");
	if (syserrlog == 0) {
		(void) picl_walk_tree_by_class(phyplatformh,
		    PICL_CLASS_CURRENT_INDICATOR, (void *)PICL_PROP_CONDITION,
		    sun4v_env_print_indicator_callback);
		if (all_status_ok) {
			log_printf("All current indicators are OK.\n");
			return;
		}
	}
	log_printf("-------------------------------------------------"
	    "-----------\n");
	log_printf(fmt, "Location", "Indicator", "Condition", 0);
	log_printf("-------------------------------------------------"
	    "-----------\n");
	(void) picl_walk_tree_by_class(phyplatformh,
	    PICL_CLASS_CURRENT_INDICATOR,
	    (void *)PICL_PROP_CONDITION,
	    sun4v_env_print_indicator_callback);
}

static void
sun4v_env_print_voltage_sensors()
{
	char *fmt = "%-34s %-14s %-10s\n";
	(void) picl_walk_tree_by_class(phyplatformh,
	    PICL_CLASS_VOLTAGE_SENSOR,
	    PICL_PROP_VOLTAGE,
	    sun4v_env_print_sensor_callback);
	if (!class_node_found)
		return;
	log_printf("\nVoltage sensors:\n");
	if (syserrlog == 0) {
		(void) picl_walk_tree_by_class(phyplatformh,
		    PICL_CLASS_VOLTAGE_SENSOR,
		    PICL_PROP_VOLTAGE, sun4v_env_print_sensor_callback);
		if (all_status_ok) {
			log_printf("All voltage sensors are OK.\n");
			return;
		}
	}
	log_printf("-------------------------------------------------"
	    "-----------\n");
	log_printf(fmt, "Location", "Sensor", "Status", 0);
	log_printf("-------------------------------------------------"
	    "-----------\n");
	(void) picl_walk_tree_by_class(phyplatformh,
	    PICL_CLASS_VOLTAGE_SENSOR,
	    (void *)PICL_PROP_VOLTAGE,
	    sun4v_env_print_sensor_callback);
}

static void
sun4v_env_print_voltage_indicators()
{
	char *fmt = "%-34s %-14s %-8s\n";
	(void) picl_walk_tree_by_class(phyplatformh,
	    PICL_CLASS_VOLTAGE_INDICATOR,
	    (void *)PICL_PROP_CONDITION,
	    sun4v_env_print_indicator_callback);
	if (!class_node_found)
		return;
	log_printf("\nVoltage indicators:\n");
	if (syserrlog == 0) {
		(void) picl_walk_tree_by_class(phyplatformh,
		    PICL_CLASS_VOLTAGE_INDICATOR, (void *)PICL_PROP_CONDITION,
		    sun4v_env_print_indicator_callback);
		if (all_status_ok) {
			log_printf("All voltage indicators are OK.\n");
			return;
		}
	}
	log_printf("-------------------------------------------------"
	    "-----------\n");
	log_printf(fmt, "Location", "Indicator", "Condition", 0);
	log_printf("-------------------------------------------------"
	    "-----------\n");
	(void) picl_walk_tree_by_class(phyplatformh,
	    PICL_CLASS_VOLTAGE_INDICATOR,
	    (void *)PICL_PROP_CONDITION,
	    sun4v_env_print_indicator_callback);
}

static void
sun4v_env_print_LEDs()
{
	char *fmt = "%-34s %-14s %-8s\n";
	if (syserrlog == 0)
		return;
	(void) picl_walk_tree_by_class(phyplatformh, PICL_CLASS_LED,
	    (void *)PICL_PROP_STATE, sun4v_env_print_indicator_callback);
	if (!class_node_found)
		return;
	log_printf("\nLEDs:\n");
	log_printf("-------------------------------------------------"
	    "-----------\n");
	log_printf(fmt, "Location", "LED", "State", 0);
	log_printf("-------------------------------------------------"
	    "-----------\n");
	(void) picl_walk_tree_by_class(phyplatformh, PICL_CLASS_LED,
	    (void *)PICL_PROP_STATE, sun4v_env_print_indicator_callback);
}

/*ARGSUSED*/
static int
sun4v_print_fru_status_callback(picl_nodehdl_t nodeh, void *args)
{
	char label[PICL_PROPNAMELEN_MAX];
	char status[PICL_PROPNAMELEN_MAX];
	picl_errno_t err;
	picl_prophdl_t proph;
	picl_nodehdl_t parenth;
	char *names[PARENT_NAMES];
	char *loc;
	int i;

	if (!class_node_found) {
		class_node_found = 1;
		return (PICL_WALK_TERMINATE);
	}
	err = picl_get_prop_by_name(nodeh, PICL_PROP_IS_FRU, &proph);
	if (err != PICL_SUCCESS)
		return (PICL_WALK_CONTINUE);
	err = picl_get_propval_by_name(nodeh, PICL_PROP_LABEL, label,
	    sizeof (label));
	if (err != PICL_SUCCESS)
		return (PICL_WALK_CONTINUE);
	err = picl_get_propval_by_name(nodeh, PICL_PROP_OPERATIONAL_STATUS,
	    status, sizeof (status));
	if (err != PICL_SUCCESS)
		return (PICL_WALK_CONTINUE);
	if (syserrlog == 0) {
		if (strcmp(status, "disabled") == 0) {
			if (all_status_ok) {
				all_status_ok = 0;
				return (PICL_WALK_TERMINATE);
			}
		} else
			return (PICL_WALK_CONTINUE);
	}
	err = picl_get_propval_by_name(nodeh, PICL_PROP_PARENT, &parenth,
	    sizeof (parenth));
	if (err != PICL_SUCCESS) {
		log_printf("\n");
		return (PICL_WALK_CONTINUE);
	}
	if ((loc = (char *)malloc(PICL_PROPNAMELEN_MAX*PARENT_NAMES)) == NULL)
		return (PICL_WALK_TERMINATE);
	for (i = 0; i < PARENT_NAMES; i++)
		if ((names[i] = (char *)malloc(PICL_PROPNAMELEN_MAX)) == NULL) {
			while (--i > -1)
				free(names[i]);
			free(loc);
			return (PICL_WALK_TERMINATE);
		}
	i = 0;
	while (err == PICL_SUCCESS) {
		if (parenth == phyplatformh)
			break;
		err = picl_get_propval_by_name(parenth, PICL_PROP_NAME,
		    names[i++], PICL_PROPNAMELEN_MAX);
		if (err != PICL_SUCCESS) {
			i--;
			break;
		}
		if (i == PARENT_NAMES)
			break;
		err = picl_get_propval_by_name(parenth, PICL_PROP_PARENT,
		    &parenth, sizeof (parenth));
	}
	loc[0] = '\0';
	if (--i > -1) {
		(void) strlcat(loc, names[i],
		    PICL_PROPNAMELEN_MAX * PARENT_NAMES);
	}
	while (--i > -1) {
		(void) strlcat(loc, "/", PICL_PROPNAMELEN_MAX * PARENT_NAMES);
		(void) strlcat(loc, names[i],
		    PICL_PROPNAMELEN_MAX * PARENT_NAMES);
	}
	log_printf("%-35s", loc);
	for (i = 0; i < PARENT_NAMES; i++)
		free(names[i]);
	free(loc);
	log_printf("%-10s", label);
	log_printf("%-9s", status);
	log_printf("\n");
	return (PICL_WALK_CONTINUE);
}

static void
sun4v_print_fru_status()
{
	char *fmt = "%-34s %-9s %-8s\n";
	(void) picl_walk_tree_by_class(phyplatformh, NULL, NULL,
	    sun4v_print_fru_status_callback);
	if (!class_node_found)
		return;
	log_printf("\n");
	log_printf("============================");
	log_printf(" FRU Status ");
	log_printf("============================");
	log_printf("\n");

	if (syserrlog == 0) {
		(void) picl_walk_tree_by_class(phyplatformh,
		    NULL, NULL,
		    sun4v_print_fru_status_callback);
		if (all_status_ok) {
			log_printf("All FRUs are enabled.\n");
			return;
		}
	}
	log_printf(fmt, "Location", "Name", "Status", 0);
	log_printf("------------------------------------------------------\n");
	(void) picl_walk_tree_by_class(phyplatformh, NULL, NULL,
	    sun4v_print_fru_status_callback);
}

/*ARGSUSED*/
static int
sun4v_print_fw_rev_callback(picl_nodehdl_t nodeh, void *args)
{
	char label[PICL_PROPNAMELEN_MAX];
	char rev[PICL_PROPNAMELEN_MAX];
	picl_errno_t err;

	if (!class_node_found) {
		class_node_found = 1;
		return (PICL_WALK_TERMINATE);
	}
	err = picl_get_propval_by_name(nodeh, PICL_PROP_LABEL, label,
	    sizeof (label));
	if (err != PICL_SUCCESS)
		return (PICL_WALK_CONTINUE);
	err = picl_get_propval_by_name(nodeh, PICL_PROP_FW_REVISION, rev,
	    sizeof (rev));
	if (err != PICL_SUCCESS)
		return (PICL_WALK_CONTINUE);
	if (strlen(rev) == 0)
		return (PICL_WALK_CONTINUE);
	log_printf("%-21s", label);
	log_printf("%-35s", rev);
	log_printf("\n");
	return (PICL_WALK_CONTINUE);
}

static void
sun4v_print_fw_rev()
{
	char *fmt = "%-20s %-10s\n";
	if (syserrlog == 0)
		return;
	(void) picl_walk_tree_by_class(phyplatformh, NULL, NULL,
	    sun4v_print_fw_rev_callback);
	if (!class_node_found)
		return;
	log_printf("\n");
	log_printf("============================");
	log_printf(" FW Version ");
	log_printf("============================");
	log_printf("\n");
	log_printf(fmt, "Name", "Version", 0);
	log_printf("-------------------------------------------------"
	    "-----------\n");
	(void) picl_walk_tree_by_class(phyplatformh, NULL, NULL,
	    sun4v_print_fw_rev_callback);
}

static void
sun4v_print_chassis_serial_no()
{
	char val[PICL_PROPNAMELEN_MAX];
	picl_errno_t err;
	if (syserrlog == 0 || chassish == 0)
		return;

	log_printf("\n");
	log_printf("Chassis Serial Number");
	log_printf("\n");
	log_printf("---------------------\n");
	err = picl_get_propval_by_name(chassish, PICL_PROP_SERIAL_NUMBER,
	    val, sizeof (val));
	if (err == PICL_SUCCESS)
		log_printf("%s", val);
	log_printf("\n");
}