root/usr/src/lib/libprtdiag_psr/sparc/lw8/common/lw8.c
/*
 * 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 (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <alloca.h>
#include <errno.h>
#include <libintl.h>
#include <sys/utsname.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/openpromio.h>
#include <sys/ddi.h>
#include <syslog.h>
#include <fcntl.h>
#include <dirent.h>
#include <unistd.h>
#include <locale.h>
#include <picl.h>
#include "pdevinfo.h"
#include "display.h"
#include "display_sun4u.h"
#include "picldefs.h"
#include "libprtdiag.h"

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

#define EM_INIT_FAIL            dgettext(TEXT_DOMAIN,\
        "picl_initialize failed: %s\n")
#define EM_GET_ROOT_FAIL        dgettext(TEXT_DOMAIN,\
        "Getting root node failed: %s\n")
#define EM_PRTDIAG_FAIL         dgettext(TEXT_DOMAIN, "Prtdiag failed!\n")

#define SIGN_ON_MSG     dgettext(TEXT_DOMAIN,\
        "System Configuration: Oracle Corporation ")
#define SYSCLK_FREQ_MSG dgettext(TEXT_DOMAIN,\
        "System clock frequency: %d MHZ\n")
#define MEM_SIZE_MSG    dgettext(TEXT_DOMAIN, "Memory size: ")
#define FFB_DOUBLE_BUF  dgettext(TEXT_DOMAIN, "FFB, Double Buffered")
#define FFB_SINGLE_BUF  dgettext(TEXT_DOMAIN, "FFB, Single Buffered")

#define DEFAULT_BOARD_NUM       0
#define DEFAULT_PORTID          0
#define CLK_FREQ_66MHZ          66
#define USB                     -1
#define HUB                     -2

/* bus id */
#define SBUS_TYPE               0
#define PCI_TYPE                1
#define UPA_TYPE                2

#define UPA_NAME                "upa"

/*
 * PICL classes
 */
#define PICL_CLASS_OPTIONS              "options"

/*
 * Property names
 */

#define OBP_PROP_REG                    "reg"
#define OBP_PROP_CLOCK_FREQ             "clock-frequency"
#define OBP_PROP_BOARD_NUM              "board#"
#define OBP_PROP_REVISION_ID            "revision-id"
#define OBP_PROP_VERSION_NUM            "version#"
#define OBP_PROP_BOARD_TYPE             "board_type"
#define OBP_PROP_ECACHE_SIZE            "ecache-size"
#define OBP_PROP_L2_CACHE_SIZE          "l2-cache-size"
#define OBP_PROP_L3_CACHE_SIZE          "l3-cache-size"
#define OBP_PROP_IMPLEMENTATION         "implementation#"
#define OBP_PROP_MASK                   "mask#"
#define OBP_PROP_COMPATIBLE             "compatible"
#define OBP_PROP_STATUS                 "status"
#define OBP_PROP_BANNER_NAME            "banner-name"
#define OBP_PROP_MODEL                  "model"
#define OBP_PROP_66MHZ_CAPABLE          "66mhz-capable"
#define OBP_PROP_FBC_REG_ID             "fbc_reg_id"
#define OBP_PROP_VERSION                "version"

#define PROP_POWERFAIL_TIME             "powerfail-time"
#define PICL_PROP_LOW_WARNING_THRESHOLD "LowWarningThreshold"

#define DEFAULT_LINE_WIDTH              85
#define HEADING_SYMBOL                  "="

#define MAX_IWAYS                       32

typedef struct bank_list {
        picl_nodehdl_t          nodeh;
        uint32_t                iway_count;
        uint32_t                iway[MAX_IWAYS];
        struct bank_list        *next;
} bank_list_t;

typedef struct {
        uint64_t        base;
        uint64_t        size;
        int             ifactor;
        int             bank_count;
} seg_info_t;

static struct io_card   *io_card_list = NULL; /* The head of the IO card list */
static bank_list_t      *mem_banks = NULL;
static  int             mem_xfersize;
static  int             no_xfer_size = 0;

static char *io_device_table[] = {
        "block",
        "disk",
        "cdrom",
        "floppy",
        "tape",
        "network",
        "display",
        "serial",
        "parallel",
        "scsi",
        "scsi-2",
        "scsi-3",
        "ide",
        "fcal",
        "keyboard",
        "mouse",
        "dma"
};

#define NIODEVICE       sizeof (io_device_table) / sizeof (io_device_table[0])

static char *bus_table[] = {
        "ebus",
        "isa",
        "pmu"
};

#define NBUS    sizeof (bus_table) / sizeof (bus_table[0])

/* prtdiag exit codes */
#define PD_SUCCESS              0
#define PD_SYSTEM_FAILURE       1
#define PD_INTERNAL_FAILURE     2

/*
 * Use of global assumes do_prominfo only called from main in prtdiag and
 * access does not need to be multi-thread safe.
 */
static int      exit_code = PD_SUCCESS;

/*
 * This function is called from every location where a status value is output.
 * It checks the status arg and sets exit_code if an error is detected.
 * The status is typically returned from a PICL query. A case-insensitive
 * string comparison is done to check for any status that starts with "fail"
 * or "fault".
 */
static void
set_exit_code(char *status)
{
        if (status == NULL)
                return;

        if (strncasecmp(status, "fail", 4) == 0 ||
            strncasecmp(status, "fault", 5) == 0)
                exit_code = PD_SYSTEM_FAILURE;
}

/*
 * check if it is an IO deice
 */
static int
is_io_device(char *device_class)
{
        int i;

        for (i = 0; i < NIODEVICE; i++) {
                if (strcmp(device_class, io_device_table[i]) == 0)
                        return (1);
        }

        return (0);
}

/*
 * check if it is a bus
 */
static int
is_bus(char *device_class)
{
        int i;

        for (i = 0; i < NBUS; i++) {
                if (strcmp(device_class, bus_table[i]) == 0)
                        return (1);
        }

        return (0);
}

/*
 * search children to get the node by the nodename
 */
static int
picldiag_get_node_by_name(picl_nodehdl_t rooth, char *name,
    picl_nodehdl_t *nodeh)
{
        picl_nodehdl_t  childh;
        int             err;
        char            *nodename;

        nodename = alloca(strlen(name) + 1);
        if (nodename == NULL)
                return (PICL_FAILURE);

        err = picl_get_propval_by_name(rooth, PICL_PROP_CHILD, &childh,
            sizeof (picl_nodehdl_t));

        while (err == PICL_SUCCESS) {
                err = picl_get_propval_by_name(childh, PICL_PROP_NAME,
                    nodename, (strlen(name) + 1));
                if (err != PICL_SUCCESS) {
                        err = picl_get_propval_by_name(childh, PICL_PROP_PEER,
                            &childh, sizeof (picl_nodehdl_t));
                        continue;
                }

                if (strcmp(nodename, name) == 0) {
                        *nodeh = childh;
                        return (PICL_SUCCESS);
                }

                err = picl_get_propval_by_name(childh, PICL_PROP_PEER,
                    &childh, sizeof (picl_nodehdl_t));
        }

        return (err);
}

/*
 * get the value by the property name of the string prop
 * Caller must free the outbuf
 */
static int
picldiag_get_string_propval(picl_nodehdl_t modh, char *prop_name, char **outbuf)
{
        int             err;
        picl_prophdl_t  proph;
        picl_propinfo_t pinfo;
        char            *prop_value;

        err = picl_get_propinfo_by_name(modh, prop_name, &pinfo, &proph);
        if (err != PICL_SUCCESS)
                return (err);

        /*
         * If it is not a string prop, return NULL
         */
        if (pinfo.type != PICL_PTYPE_CHARSTRING)
                return (PICL_FAILURE);

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

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

        *outbuf = prop_value;
        return (PICL_SUCCESS);
}


/*
 * return the value as a signed integer
 */

static int64_t
picldiag_get_int_propval(picl_nodehdl_t modh, char *prop_name, int *ret)
{
        int             err;
        picl_prophdl_t  proph;
        picl_propinfo_t pinfo;
        int8_t          int8v;
        int16_t         int16v;
        int32_t         int32v;
        int64_t         int64v;

        err = picl_get_propinfo_by_name(modh, prop_name, &pinfo, &proph);
        if (err != PICL_SUCCESS) {
                *ret = err;
                return (0);
        }

        /*
         * If it is not an int or uint  prop, return failure
         */
        if ((pinfo.type != PICL_PTYPE_INT) &&
            (pinfo.type != PICL_PTYPE_UNSIGNED_INT)) {
                *ret = PICL_FAILURE;
                return (0);
        }

        switch (pinfo.size) {
        case sizeof (int8_t):
                err = picl_get_propval(proph, &int8v, sizeof (int8v));
                *ret = err;
                return (int8v);
        case sizeof (int16_t):
                err = picl_get_propval(proph, &int16v, sizeof (int16v));
                *ret = err;
                return (int16v);
        case sizeof (int32_t):
                err = picl_get_propval(proph, &int32v, sizeof (int32v));
                *ret = err;
                return (int32v);
        case sizeof (int64_t):
                err = picl_get_propval(proph, &int64v, sizeof (int64v));
                *ret = err;
                return (int64v);
        default:        /* not supported size */
                *ret = PICL_FAILURE;
                return (0);
        }
}

/*
 * return the value of the uint prop
 */
static uint64_t
picldiag_get_uint_propval(picl_nodehdl_t modh, char *prop_name, int *ret)
{
        int             err;
        picl_prophdl_t  proph;
        picl_propinfo_t pinfo;
        uint8_t         uint8v;
        uint16_t        uint16v;
        uint32_t        uint32v;
        uint64_t        uint64v;

        err = picl_get_propinfo_by_name(modh, prop_name, &pinfo, &proph);
        if (err != PICL_SUCCESS) {
                *ret = err;
                return (0);
        }

        /*
         * If it is not an int or uint prop, return failure
         */
        if ((pinfo.type != PICL_PTYPE_INT) &&
            (pinfo.type != PICL_PTYPE_UNSIGNED_INT)) {
                *ret = PICL_FAILURE;
                return (0);
        }

        /* uint prop */

        switch (pinfo.size) {
        case sizeof (uint8_t):
                err = picl_get_propval(proph, &uint8v, sizeof (uint8v));
                *ret = err;
                return (uint8v);
        case sizeof (uint16_t):
                err = picl_get_propval(proph, &uint16v, sizeof (uint16v));
                *ret = err;
                return (uint16v);
        case sizeof (uint32_t):
                err = picl_get_propval(proph, &uint32v, sizeof (uint32v));
                *ret = err;
                return (uint32v);
        case sizeof (uint64_t):
                err = picl_get_propval(proph, &uint64v, sizeof (uint64v));
                *ret = err;
                return (uint64v);
        default:        /* not supported size */
                *ret = PICL_FAILURE;
                return (0);
        }
}

/*
 * return the value of the float prop
 */
static float
picldiag_get_float_propval(picl_nodehdl_t modh, char *prop_name, int *ret)
{
        int             err;
        picl_prophdl_t  proph;
        picl_propinfo_t pinfo;
        float           floatv;

        err = picl_get_propinfo_by_name(modh, prop_name, &pinfo, &proph);
        if (err != PICL_SUCCESS) {
                *ret = err;
                return ((float)0);
        }

        /*
         * If it is not a float prop, return failure
         */
        if (pinfo.type != PICL_PTYPE_FLOAT) {
                *ret = PICL_FAILURE;
                return ((float)0);
        }

        *ret = picl_get_propval(proph, &floatv, sizeof (floatv));
        return (floatv);
}

/*
 * get the clock frequency
 */
static int
picldiag_get_clock_freq(picl_nodehdl_t modh, uint32_t *freq)
{
#define ROUND_TO_MHZ(x) (((x) + 500000)/ 1000000)
        int             err;
        uint64_t        clk_freq;

        clk_freq = picldiag_get_uint_propval(modh, OBP_PROP_CLOCK_FREQ, &err);
        if (err != PICL_SUCCESS)
                return (err);

        *freq = ROUND_TO_MHZ(clk_freq);

        return (PICL_SUCCESS);
}

/*
 * get the clock frequency from parent
 */
static int
picldiag_get_clock_from_parent(picl_nodehdl_t nodeh, uint32_t *clk)
{
        picl_nodehdl_t  parenth;
        int             err;


        err = picl_get_propval_by_name(nodeh, PICL_PROP_PARENT,
            &parenth, sizeof (parenth));

        while (err == PICL_SUCCESS) {
                err = picldiag_get_clock_freq(parenth, clk);
                if (err != PICL_PROPNOTFOUND)
                        return (err);

                err = picl_get_propval_by_name(parenth, PICL_PROP_PARENT,
                    &parenth, sizeof (parenth));
        }

        return (err);
}

/*
 * get _fru_parent prop
 * If not found, then travese superiors (parent nodes) until
 * a _fru_parent property is found.
 * If not found, no fru parent
 */
static int
picldiag_get_fru_parent(picl_nodehdl_t nodeh, picl_nodehdl_t *fruparenth)
{
        picl_nodehdl_t  fruh;
        int             err;

        /* find fru parent */
        err = picl_get_propval_by_name(nodeh, PICL_REFPROP_FRU_PARENT,
            &fruh, sizeof (fruh));

        if (err != PICL_SUCCESS)
                err = picl_get_propval_by_name(nodeh, PICL_REFPROP_LOC_PARENT,
                    &fruh, sizeof (fruh));

        while (err == PICL_PROPNOTFOUND) {
                err = picl_get_propval_by_name(nodeh, PICL_PROP_PARENT,
                    &nodeh, sizeof (nodeh));
                if (err != PICL_SUCCESS)
                        return (err);

                err = picl_get_propval_by_name(nodeh, PICL_REFPROP_FRU_PARENT,
                    &fruh, sizeof (fruh));
                if (err != PICL_SUCCESS)
                        err = picl_get_propval_by_name(nodeh,
                            PICL_REFPROP_LOC_PARENT, &fruh, sizeof (fruh));
        }

        if (err == PICL_SUCCESS)
                *fruparenth = fruh;

        return (err);
}

/*
 * get label
 *
 * To get the label, use the following algorithm:
 * Lookup "Label" property in the fru node itself. If no
 * Label found, then traverse superiors (parent nodes) until
 * a Label property is found.
 * if not found, then no label
 */
static int
picldiag_get_label(picl_nodehdl_t nodeh, char **label)
{
        int             err;

        err = picldiag_get_string_propval(nodeh, PICL_PROP_LABEL, label);

        while (err == PICL_PROPNOTFOUND) {
                err = picl_get_propval_by_name(nodeh, PICL_PROP_PARENT,
                    &nodeh, sizeof (nodeh));
                if (err != PICL_SUCCESS)
                        return (err);

                err = picldiag_get_string_propval(nodeh, PICL_PROP_LABEL,
                    label);
        }

        return (err);
}

/*
 * get combined label
 *
 * like picldiag_get_label, except concatenates the labels of parent locations
 * eg SB0/P3 for processor P3 on system board SB0
 *
 * if caller specifies non-zero label length, label will be cut to specified
 * length.
 * negative length is left justified, non-negative length is right justified
 */
static int
picldiag_get_combined_label(picl_nodehdl_t nodeh, char **label, int lablen)
{
        int     err;
        char    *ptr;
        char    *ptr1 = NULL;
        char    *ptr2;
        int     len;

        err = picldiag_get_string_propval(nodeh, PICL_PROP_LABEL, &ptr1);
        if (err != PICL_PROPNOTFOUND && err != PICL_SUCCESS)
                return (err);

        for (;;) {
                err = picl_get_propval_by_name(nodeh, PICL_PROP_PARENT,
                    &nodeh, sizeof (nodeh));
                if (err == PICL_PROPNOTFOUND)
                        break;
                if (err != PICL_SUCCESS)
                        return (err);

                err = picldiag_get_string_propval(nodeh, PICL_PROP_LABEL, &ptr);
                if (err == PICL_SUCCESS) {
                        if (ptr1 == NULL) {
                                ptr1 = ptr;
                        } else {
                                ptr2 = malloc(strlen(ptr1) + strlen(ptr) + 2);
                                if (ptr2 == NULL)
                                        return (PICL_FAILURE);
                                (void) strcpy(ptr2, ptr);
                                (void) strcat(ptr2, "/");
                                (void) strcat(ptr2, ptr1);
                                (void) free(ptr);
                                (void) free(ptr1);
                                ptr1 = ptr2;
                        }
                } else if (err != PICL_PROPNOTFOUND) {
                        return (err);
                }
        }

        if (ptr1 == NULL)
                return (PICL_PROPNOTFOUND);

        len = strlen(ptr1);
        /* if no string truncation is desired or required */
        if ((lablen == 0) || (len <= abs(lablen))) {
                *label = ptr1;
                return (PICL_SUCCESS);
        }

        /* string truncation is required; alloc space for (lablen + \0) */
        ptr = malloc(abs(lablen) + 1);
        if (ptr == 0)
                return (PICL_FAILURE);
        if (lablen > 0) {
                /* right justification; label = "+<string>\0" */
                strcpy(ptr, "+");
                strncat(ptr, ptr1 + len - lablen + 1, lablen + 1);
        } else {
                /* left justification; label = "<string>+\0" */
                strncpy(ptr, ptr1, abs(lablen) - 1);
                strcat(ptr, "+");
        }

        *label = ptr;
        return (PICL_SUCCESS);
}

/*
 * return the first compatible value
 */
static int
picldiag_get_first_compatible_value(picl_nodehdl_t nodeh, char **outbuf)
{
        int             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 the header in the center
 */
static void
logprintf_header(char *header, size_t line_width)
{
        size_t  start_pos;
        size_t  i;

        log_printf("\n");
        start_pos = (line_width - strlen(header) - 2) / 2;

        for (i = 0; i < start_pos; i++)
                log_printf("%s", HEADING_SYMBOL);

        log_printf(" %s ", header);

        for (i = 0; i < start_pos; i++)
                log_printf("%s", HEADING_SYMBOL);

        log_printf("\n");
}

/*
 * print the size
 */
static void
logprintf_size(uint64_t size)
{
#define SIZE_FIELD      11

        uint64_t        kbyte = 1024;
        uint64_t        mbyte = 1024 * 1024;
        uint64_t        gbyte = 1024 * 1024 * 1024;
        uint64_t        residue;
        char            buf[SIZE_FIELD];

        if (size >= gbyte) {
                residue = size % gbyte;
                if (residue == 0)
                        snprintf(buf, sizeof (buf), "%dGB",
                            (int)(size / gbyte));
                else
                        snprintf(buf, sizeof (buf), "%.2fGB",
                            (float)size / gbyte);
        } else if (size >= mbyte) {
                residue = size % mbyte;
                if (residue == 0)
                        snprintf(buf, sizeof (buf), "%dMB",
                            (int)(size / mbyte));
                else
                        snprintf(buf, sizeof (buf), "%.2fMB",
                            (float)size / mbyte);
        } else {
                residue = size % kbyte;
                if (residue == 0)
                        snprintf(buf, sizeof (buf), "%dKB",
                            (int)(size / kbyte));
                else
                        snprintf(buf, sizeof (buf), "%.2fKB",
                            (float)size / kbyte);
        }

        log_printf("%-10s ", buf);
}

/*
 * display platform banner
 */
static int
display_platform_banner(picl_nodehdl_t plafh)
{
        char    *platform;
        char    *banner_name;
        int     err;

        /*
         * get PICL_PROP_MACHINE and PICL_PROP_BANNER_NAME
         */
        log_printf(SIGN_ON_MSG);
        err = picldiag_get_string_propval(plafh, PICL_PROP_MACHINE,
            &platform);
        if (err != PICL_SUCCESS)
                return (err);
        log_printf(" %s", platform);
        free(platform);

        err = picldiag_get_string_propval(plafh, OBP_PROP_BANNER_NAME,
            &banner_name);
        if (err != PICL_SUCCESS)
                return (err);
        log_printf(" %s", banner_name);
        free(banner_name);

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

/*
 * display the clock frequency
 */
static int
display_system_clock(picl_nodehdl_t plafh)
{
        uint32_t        system_clk;
        int             err;

        err = picldiag_get_clock_freq(plafh, &system_clk);
        if (err != PICL_SUCCESS)
                return (err);

        log_printf(SYSCLK_FREQ_MSG, system_clk);

        return (PICL_SUCCESS);
}

/*
 * callback function to display the memory size
 */
/*ARGSUSED*/
static int
memory_callback(picl_nodehdl_t memh, void *args)
{
        uint64_t        mem_size;
        int             err;

        log_printf(MEM_SIZE_MSG);
        mem_size = picldiag_get_uint_propval(memh, PICL_PROP_SIZE, &err);
        if (err == PICL_SUCCESS)
                logprintf_size(mem_size);
        log_printf("\n");
        no_xfer_size = 0;
        mem_xfersize = picldiag_get_uint_propval(memh, PICL_PROP_TRANSFER_SIZE,
            &err);
        if (err == PICL_PROPNOTFOUND)
                no_xfer_size = 1;
        return (PICL_WALK_TERMINATE);
}

/*
 * callback function to print cpu information
 */
/*ARGSUSED*/
static int
cpu_callback(picl_nodehdl_t nodeh, void *args)
{
        int             err;
        int             id0, id1;
        uint64_t        uintval;
        uint64_t        decoded_mask;
        uint32_t        freq;
        char            *impl_name;
        char            *status;
        picl_prophdl_t  parenth;
        picl_prophdl_t  peerh;
        char            *label;
        int             is_cmp = 0;
        int             is_cheetah = 0;

        /*
         * first check the implementation. If it's a CMP
         * we need to combine info from both cores.
         */

        impl_name = NULL;
        uintval = picldiag_get_uint_propval(nodeh,
            OBP_PROP_IMPLEMENTATION, &err);
        if (err == PICL_SUCCESS) {
                if (CPU_IMPL_IS_CMP(uintval)) {
                        is_cmp = 1;
                        err = picldiag_get_first_compatible_value(nodeh,
                            &impl_name);
                        if (err != PICL_SUCCESS)
                                impl_name = NULL;
                } else if (IS_CHEETAH(uintval) || IS_CHEETAH_PLUS(uintval)) {
                        is_cheetah = 1;
                        err = picldiag_get_string_propval(nodeh, PICL_PROP_NAME,
                            &impl_name);
                        if (err != PICL_SUCCESS)
                                impl_name = NULL;
                }
        } else {
                err = picldiag_get_string_propval(nodeh,
                    PICL_PROP_NAME, &impl_name);
                if (err != PICL_SUCCESS)
                impl_name = NULL;
        }

        /*
         * In the CMP case, combine info from both cores. If we
         * are being called with the first core handle, we display
         * info on the sibling core handle too. If we are being
         * called with the second core hanlde as an argument, simply
         * return.
         */

        if (is_cmp) {
                err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER,
                    &peerh, sizeof (picl_nodehdl_t));
                if (err != PICL_SUCCESS)
                        return (PICL_WALK_CONTINUE);
                id1 = picldiag_get_uint_propval(peerh, PICL_PROP_ID, &err);
                if (err == PICL_PROPNOTFOUND)
                        return (PICL_WALK_CONTINUE);
                else if (err != PICL_SUCCESS)
                        return (err);
        }

        /*
         * If no ID is found, return
         */
        id0 = picldiag_get_uint_propval(nodeh, PICL_PROP_ID, &err);
        if (err == PICL_PROPNOTFOUND)
                return (PICL_WALK_CONTINUE);
        else if (err != PICL_SUCCESS)
                return (err);
        if (is_cmp) {
                log_printf("%3d,%3d  ", id0, id1);
        } else {
                log_printf("%7d  ", id0);
        }

        /*
         * If no freq is found, return
         */
        err = picldiag_get_clock_freq(nodeh, &freq);
        if (err == PICL_PROPNOTFOUND)
                return (PICL_WALK_CONTINUE);
        else if (err != PICL_SUCCESS)
                return (err);
        log_printf("%4d MHz  ", freq);

        /* Ecache size */

        if (is_cmp) {
                uintval = picldiag_get_uint_propval(nodeh,
                    OBP_PROP_L3_CACHE_SIZE, &err);
                if (err == PICL_SUCCESS) {
                        /*
                         * Panther L3 is logically shared, so the total E$
                         * size is equal to the core size.
                         */
                        logprintf_size(uintval);
                } else {
                        uintval = picldiag_get_uint_propval(nodeh,
                            OBP_PROP_L2_CACHE_SIZE, &err);
                        if (err == PICL_SUCCESS) {
                                /*
                                 * Jaguar has a split E$, so the total size
                                 * is the sum of both cores.
                                 */
                                logprintf_size(uintval * 2);
                        } else
                                log_printf(" -          ");
                }
        } else {
                uintval = picldiag_get_uint_propval(nodeh,
                    OBP_PROP_ECACHE_SIZE, &err);
                if (err == PICL_SUCCESS)
                        logprintf_size(uintval);
                else
                        log_printf(" -          ");
        }

        /* Implementation */
        if (impl_name != NULL)
                log_printf(" %-20s ", impl_name);
        else
                log_printf("  <unknown>           ");

        /* CPU Mask */
        uintval = picldiag_get_uint_propval(nodeh, OBP_PROP_MASK, &err);
        if (err == PICL_PROPNOTFOUND)
                log_printf("  -     ");
        else if (err == PICL_SUCCESS) {
                if (is_cheetah) {
                        decoded_mask = REMAP_CHEETAH_MASK(uintval);
                } else {
                        decoded_mask = uintval;
                }
                log_printf("%2lld.%-2lld   ", (decoded_mask >> 4) & 0xf,
                    decoded_mask & 0xf);
        } else
                return (err);

        /*
         * Status - if the node has a status property then display that
         * otherwise display the State property
         */
        err = picldiag_get_string_propval(nodeh, OBP_PROP_STATUS, &status);
        if (err == PICL_SUCCESS) {
                log_printf("%-12s", status);
                set_exit_code(status);
                free(status);
        } else if (err != PICL_PROPNOTFOUND && err !=
            PICL_PROPVALUNAVAILABLE && err != PICL_ENDOFLIST) {
                return (err);
        } else {
                err = picldiag_get_string_propval(nodeh,
                    PICL_PROP_STATE, &status);
                if (err == PICL_SUCCESS) {
                        log_printf("%-12s", status);
                        set_exit_code(status);
                        free(status);
                } else if (err != PICL_PROPNOTFOUND && err !=
                    PICL_PROPVALUNAVAILABLE && err !=
                    PICL_ENDOFLIST) {
                        return (err);
                } else {
                        log_printf("unknown    ");
                }
        }

        /*
         * Location: use label of fru parent
         */
        err = picldiag_get_fru_parent(nodeh, &parenth);
        if (err == PICL_PROPNOTFOUND) {
                log_printf(" -      ");
        } else if (err == PICL_SUCCESS) {
                err = picldiag_get_combined_label(parenth, &label, 12);
                if (err == PICL_PROPNOTFOUND)
                        log_printf(" -      ");
                else if (err == PICL_SUCCESS) {
                        log_printf("%s", label);
                        free(label);
                } else
                        return (err);
        } else
                return (err);

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

/*
 * display cpu information
 */
static int
display_cpu_info(picl_nodehdl_t plafh)
{
        int     err;

        /*
         * Display the table header for CPUs . Then display the CPU
         * frequency, cache size, and processor revision  on all the boards.
         */
        logprintf_header(dgettext(TEXT_DOMAIN, "CPUs"), DEFAULT_LINE_WIDTH);
        log_printf("               E$          CPU                  "
            "CPU\n");
        log_printf("CPU      Freq      Size        Implementation       "
            "Mask    Status      Location\n");
        log_printf("-------  --------  ----------  -------------------  "
            "-----   ------      --------\n");

        err = picl_walk_tree_by_class(plafh, PICL_CLASS_CPU, PICL_CLASS_CPU,
            cpu_callback);
        return (err);
}

/*
 * Inserts an io_card structure into the list.
 */
static void
add_io_card(uint32_t board, uint32_t bus_id, uint32_t slot, char *label,
    uint32_t freq, char *name, char *model, char *status, char *devfs_path)
{
        struct io_card card;

        card.display = 1;
        card.board = board;
        switch (bus_id) {
        case SBUS_TYPE:
                strlcpy(card.bus_type, SBUS_NAME, MAXSTRLEN);
                break;
        case PCI_TYPE:
                strlcpy(card.bus_type, PCI_NAME, MAXSTRLEN);
                break;
        case UPA_TYPE:
                strlcpy(card.bus_type, UPA_NAME, MAXSTRLEN);
                break;
        default: /* won't reach here */
                strlcpy(card.bus_type, "", MAXSTRLEN);
                break;
        }
        if (label == NULL)
                card.slot = slot;
        else {
                card.slot = PCI_SLOT_IS_STRING;
                (void) strlcpy(card.slot_str, label, MAXSTRLEN);
        }
        card.freq = freq;
        card.status[0] = '\0';
        card.name[0] = '\0';
        card.model[0] = '\0';
        card.notes[0] = '\0';
        if (status != NULL)
                strlcpy(card.status, status, MAXSTRLEN);
        if (name != NULL)
                strlcpy(card.name, name, MAXSTRLEN);
        if (model != NULL)
                strlcpy(card.model, model, MAXSTRLEN);
        if (status != NULL)
                strlcpy(card.status, status, MAXSTRLEN);
        if (devfs_path != NULL)
                strlcpy(card.notes, devfs_path, MAXSTRLEN);

        io_card_list = insert_io_card(io_card_list, &card);
}

static void
append_to_bank_list(bank_list_t *newptr)
{
        bank_list_t     *ptr;

        if (mem_banks == NULL) {
                mem_banks = newptr;
                return;
        }
        ptr = mem_banks;
        while (ptr->next != NULL)
                ptr = ptr->next;

        ptr->next = newptr;
}

static void
free_bank_list(void)
{
        bank_list_t     *ptr;
        bank_list_t     *tmp;

        for (ptr = mem_banks; ptr != NULL; ptr = tmp) {
                tmp = ptr->next;
                free(ptr);
        }
        mem_banks = NULL;
}


/*
 * print label for memory module
 */
static int
logprintf_memory_module_label(picl_nodehdl_t moduleh)
{
        picl_nodehdl_t  fruparenth;
        int             err;
        char            *label;

        err = picldiag_get_fru_parent(moduleh, &fruparenth);
        if (err == PICL_PROPNOTFOUND) {
                log_printf("-");
                return (PICL_SUCCESS);
        } else if (err != PICL_SUCCESS)
                return (err);

        err = picldiag_get_combined_label(fruparenth, &label, 30);
        if (err == PICL_PROPNOTFOUND)
                log_printf("-");
        else if (err == PICL_SUCCESS) {
                log_printf("%-15s", label);
                free(label);
        } else
                return (err);

        return (PICL_SUCCESS);
}

/*
 * print the bank id and add the bank handle in the bank list
 * return the head of the bank list
 */
static int
membank_callback(picl_nodehdl_t bankh, void *args)
{
        int             err;
        int64_t         id;
        uint64_t        match;
        uint64_t        mask;
        int             i;
        bank_list_t     *newptr;
        seg_info_t      *segp = args;

        /*
         * print the bank id in the segment table contains column
         */
        id = picldiag_get_uint_propval(bankh, PICL_PROP_ID, &err);
        if (segp->bank_count > 0)
                log_printf(",");
        if (err == PICL_PROPNOTFOUND)
                log_printf("-");
        else if (err == PICL_SUCCESS)
                log_printf("%-lld", id);
        else
                return (err);
        segp->bank_count++;

        /*
         * Save the bank information for later (print_bank_table)
         */
        newptr = malloc(sizeof (*newptr));
        if (newptr == NULL)
                return (PICL_FAILURE);

        newptr->nodeh = bankh;
        newptr->iway_count = 0;
        newptr->next = NULL;
        append_to_bank_list(newptr);

        /*
         * Compute the way numbers for the bank
         */
        if (no_xfer_size)
                return (PICL_WALK_CONTINUE);

        match = picldiag_get_uint_propval(bankh, PICL_PROP_ADDRESSMATCH, &err);
        if (err == PICL_PROPNOTFOUND)
                return (PICL_WALK_CONTINUE);
        else if (err != PICL_SUCCESS)
                return (err);

        mask = picldiag_get_uint_propval(bankh, PICL_PROP_ADDRESSMASK, &err);
        if (err == PICL_PROPNOTFOUND)
                return (PICL_WALK_CONTINUE);
        else if (err != PICL_SUCCESS)
                return (err);

        i = 0;
        while ((i < segp->ifactor) && (newptr->iway_count < MAX_IWAYS)) {
                if (((segp->base + i * mem_xfersize) & mask) == match)
                        newptr->iway[newptr->iway_count++] = i;
                ++i;
        }
        return (PICL_WALK_CONTINUE);
}


/*
 * find the memory bank and add the bank handle in the bank list
 * return the head of the bank list
 */
static int
logprintf_bankinfo(picl_nodehdl_t segh, seg_info_t *segp)
{
        int             err;

        log_printf("BankIDs ");
        /*
         * find memory-bank
         */
        segp->bank_count = 0;
        err = picl_walk_tree_by_class(segh, PICL_CLASS_MEMORY_BANK, segp,
            membank_callback);
        log_printf("\n");
        return (err);
}

/*
 * print the label of memory module or the memory module bank ids
 */
static int
logprintf_seg_contains_col(picl_nodehdl_t nodeh, seg_info_t *segp)
{
        picl_nodehdl_t  moduleh;
        int             err;

        /*
         * find memory-module if referenced directly from the memory-segment
         * (ie no memory banks)
         */
        err = picl_get_propval_by_name(nodeh, PICL_REFPROP_MEMORY_MODULE,
            &moduleh, sizeof (moduleh));
        if ((err != PICL_SUCCESS) && (err != PICL_PROPNOTFOUND))
                return (err);
        if (err == PICL_SUCCESS) {
                err = logprintf_memory_module_label(moduleh);
                log_printf("\n");
                return (err);
        }

        /*
         * memory-module not referenced directly from the memory segment
         * so list memory banks instead
         */
        err = logprintf_bankinfo(nodeh, segp);
        return (err);
}

/*
 * find all memory modules under the given memory module group
 * and print its label
 */
static int
logprintf_memory_module_group_info(picl_nodehdl_t memgrph, uint64_t mcid)
{
        int             err;
        int64_t         id;
        picl_nodehdl_t  moduleh;
        char            piclclass[PICL_CLASSNAMELEN_MAX];
        picl_nodehdl_t  fruparenth;
        char            *status;

        id = picldiag_get_uint_propval(memgrph, PICL_PROP_ID, &err);
        if (err == PICL_PROPNOTFOUND)
                id = -1;
        else if (err != PICL_SUCCESS)
                return (err);

        err = picl_get_propval_by_name(memgrph, PICL_PROP_CHILD, &moduleh,
            sizeof (picl_nodehdl_t));

        while (err == PICL_SUCCESS) {
                /* controller id */
                log_printf("%-8lld       ", mcid);

                /* group id */
                if (id == -1) {
                        log_printf("-         ");
                } else {
                        log_printf("%-8lld ", id);
                }

                err = picl_get_propval_by_name(moduleh, PICL_PROP_CLASSNAME,
                    piclclass, sizeof (piclclass));
                if (err != PICL_SUCCESS)
                        return (err);

                if (strcmp(piclclass, PICL_CLASS_MEMORY_MODULE) == 0) {
                        err = logprintf_memory_module_label(moduleh);
                        if (err != PICL_SUCCESS)
                                return (err);
                }

                err = picldiag_get_fru_parent(moduleh, &fruparenth);
                if (err == PICL_SUCCESS) {
                        err = picldiag_get_string_propval(fruparenth,
                            PICL_PROP_OPERATIONAL_STATUS, &status);
                        if (err == PICL_SUCCESS) {
                                log_printf("%s", status);
                                set_exit_code(status);
                                free(status);
                        } else if (err != PICL_PROPNOTFOUND)
                                return (err);
                } else if (err != PICL_PROPNOTFOUND)
                        return (err);

                err = picl_get_propval_by_name(moduleh, PICL_PROP_PEER,
                    &moduleh, sizeof (picl_nodehdl_t));

                log_printf("\n");
        }
        if (err == PICL_PROPNOTFOUND)
                return (PICL_SUCCESS);
        return (err);
}

/*
 * search children to find memory module group under memory-controller
 */
static int
find_memory_module_group(picl_nodehdl_t mch, int *print_header)
{
        picl_nodehdl_t  memgrph;
        uint64_t        mcid;
        int             err;
        char            piclclass[PICL_CLASSNAMELEN_MAX];

        mcid = picldiag_get_uint_propval(mch, OBP_PROP_PORTID, &err);
        if (err == PICL_PROPNOTFOUND)
                mcid = DEFAULT_PORTID;
        else if (err != PICL_SUCCESS)
                return (err);

        err = picl_get_propval_by_name(mch, PICL_PROP_CHILD,
            &memgrph, sizeof (picl_nodehdl_t));
        while (err == PICL_SUCCESS) {
                err = picl_get_propval_by_name(memgrph,
                    PICL_PROP_CLASSNAME, piclclass, sizeof (piclclass));
                if (err != PICL_SUCCESS)
                        return (err);

                if (strcmp(piclclass, PICL_CLASS_MEMORY_MODULE_GROUP) == 0) {
                        if (*print_header == 1) {
                                log_printf(
                                    dgettext(TEXT_DOMAIN,
                                    "\nMemory Module Groups:\n"));
                                log_printf("--------------------------");
                                log_printf("------------------------\n");
                                log_printf("ControllerID   GroupID  Labels");
                                log_printf("         Status\n");
                                log_printf("--------------------------");
                                log_printf("------------------------\n");
                                *print_header = 0;
                        }
                        err = logprintf_memory_module_group_info(memgrph, mcid);
                        if (err != PICL_SUCCESS)
                                return (err);
                }

                err = picl_get_propval_by_name(memgrph, PICL_PROP_PEER,
                    &memgrph, sizeof (picl_nodehdl_t));
        }
        if (err == PICL_PROPNOTFOUND)
                return (PICL_SUCCESS);
        return (err);
}

/*
 * print memory module group table per memory-controller
 */
static int
print_memory_module_group_table(picl_nodehdl_t plafh)
{
        picl_nodehdl_t  mch;
        int             err;
        char            piclclass[PICL_CLASSNAMELEN_MAX];
        int             print_header;

        print_header = 1;

        /*
         * find memory-controller
         */
        err = picl_get_propval_by_name(plafh, PICL_PROP_CHILD, &mch,
            sizeof (picl_nodehdl_t));
        while (err == PICL_SUCCESS) {
                err = picl_get_propval_by_name(mch, PICL_PROP_CLASSNAME,
                    piclclass, sizeof (piclclass));
                if (err != PICL_SUCCESS)
                        return (err);

                if (strcmp(piclclass, PICL_CLASS_MEMORY_CONTROLLER) != 0) {
                        err = print_memory_module_group_table(mch);
                        if (err != PICL_SUCCESS)
                                return (err);
                        err = picl_get_propval_by_name(mch, PICL_PROP_PEER,
                            &mch, sizeof (picl_nodehdl_t));
                        continue;
                }

                err = find_memory_module_group(mch, &print_header);
                if (err != PICL_SUCCESS)
                        return (err);

                err = picl_get_propval_by_name(mch, PICL_PROP_PEER,
                    &mch, sizeof (picl_nodehdl_t));
        }
        if (err == PICL_PROPNOTFOUND)
                return (PICL_SUCCESS);

        return (err);
}

/*
 * print bank table
 */
static int
print_bank_table(void)
{
        bank_list_t     *ptr;
        picl_nodehdl_t  bankh;
        picl_nodehdl_t  memgrph;
        picl_nodehdl_t  mch;
        int             err;
        int32_t         i;
        uint64_t        size;
        int             id;

        log_printf(dgettext(TEXT_DOMAIN, "\nBank Table:\n"));
        log_printf("---------------------------------------");
        log_printf("--------------------\n");
        log_printf(dgettext(TEXT_DOMAIN, "           Physical Location\n"));
        log_printf(dgettext(TEXT_DOMAIN, "ID       ControllerID  GroupID   "));
        log_printf(dgettext(TEXT_DOMAIN, "Size       Interleave Way\n"));
        log_printf("---------------------------------------");
        log_printf("--------------------\n");

        for (ptr = mem_banks; ptr != NULL; ptr = ptr->next) {
                bankh = ptr->nodeh;
                id = picldiag_get_uint_propval(bankh, PICL_PROP_ID, &err);
                if (err != PICL_SUCCESS)
                        log_printf("%-8s ", "-");
                else
                        log_printf("%-8d ", id);

                /* find memory-module-group */
                err = picl_get_propval_by_name(bankh,
                    PICL_REFPROP_MEMORY_MODULE_GROUP, &memgrph,
                    sizeof (memgrph));
                if (err == PICL_PROPNOTFOUND) {
                        log_printf("%-8s      ", "-");
                        log_printf("%-8s  ", "-");
                } else if (err != PICL_SUCCESS)
                        return (err);
                else {
                        /*
                         * get controller id
                         */
                        err = picl_get_propval_by_name(memgrph,
                            PICL_PROP_PARENT, &mch, sizeof (picl_nodehdl_t));
                        if (err != PICL_SUCCESS)
                                return (err);

                        id = picldiag_get_uint_propval(mch, OBP_PROP_PORTID,
                            &err);
                        if (err == PICL_PROPNOTFOUND)
                                id = DEFAULT_PORTID; /* use default */
                        else if (err != PICL_SUCCESS)
                                return (err);

                        log_printf("%-8d      ", id);

                        /* get group id */
                        id = picldiag_get_uint_propval(memgrph, PICL_PROP_ID,
                            &err);
                        if (err == PICL_PROPNOTFOUND)
                                log_printf("-          ");
                        else if (err == PICL_SUCCESS)
                                log_printf("%-8d  ", id);
                        else
                                return (err);
                }

                size = picldiag_get_uint_propval(bankh, PICL_PROP_SIZE, &err);
                if (err == PICL_PROPNOTFOUND)
                        log_printf("-            ");
                else if (err == PICL_SUCCESS)
                        logprintf_size(size);
                else
                        return (err);

                log_printf("     ");
                for (i = 0; i < ptr->iway_count; i++) {
                        if (i != 0)
                                log_printf(",");
                        log_printf("%d", ptr->iway[i]);
                }

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

/*
 * callback function to print segment, add the bank in the list and
 * return the bank list
 */
/* ARGSUSED */
static int
memseg_callback(picl_nodehdl_t segh, void *args)
{
        seg_info_t      seginfo;
        int             err;

        /* get base address */
        seginfo.base = picldiag_get_uint_propval(segh, PICL_PROP_BASEADDRESS,
            &err);
        if (err == PICL_PROPNOTFOUND) {
                log_printf("-\n");
                return (PICL_WALK_CONTINUE);
        } else if (err == PICL_SUCCESS)
                log_printf("0x%-16llx ", seginfo.base);
        else
                return (err);

        /* get size */
        seginfo.size = picldiag_get_uint_propval(segh, PICL_PROP_SIZE, &err);
        if (err == PICL_PROPNOTFOUND) {
                log_printf("-\n");
                return (PICL_WALK_CONTINUE);
        } else if (err == PICL_SUCCESS)
                logprintf_size(seginfo.size);
        else
                return (err);

        /* get interleave factor */
        seginfo.ifactor = picldiag_get_uint_propval(segh,
            PICL_PROP_INTERLEAVE_FACTOR, &err);

        if (err == PICL_PROPNOTFOUND) {
                log_printf("       -\n");
                return (PICL_WALK_CONTINUE);
        } else if (err == PICL_SUCCESS)
                log_printf("       %-2d          ", seginfo.ifactor);
        else
                return (err);

        seginfo.bank_count = 0;
        err = logprintf_seg_contains_col(segh, &seginfo);
        if (err != PICL_SUCCESS)
                return (err);
        return (PICL_WALK_CONTINUE);
}

/*
 * search children to find memory-segment and set up the bank list
 */
static int
find_segments(picl_nodehdl_t plafh)
{
        int             err;

        log_printf(dgettext(TEXT_DOMAIN, "Segment Table:\n"));
        log_printf("------------------------------");
        log_printf("-----------------------------------------\n");
        log_printf(dgettext(TEXT_DOMAIN, "Base Address       Size       "));
        log_printf(dgettext(TEXT_DOMAIN, "Interleave Factor  Contains\n"));
        log_printf("------------------------------");
        log_printf("-----------------------------------------\n");

        err = picl_walk_tree_by_class(plafh, PICL_CLASS_MEMORY_SEGMENT,
            NULL, memseg_callback);
        return (err);
}

/*
 * display memory configuration
 */
static int
display_memory_config(picl_nodehdl_t plafh)
{
        int             err;

        logprintf_header(dgettext(TEXT_DOMAIN, "Memory Configuration"),
            DEFAULT_LINE_WIDTH);

        mem_banks = NULL;
        err = find_segments(plafh);

        if ((err == PICL_SUCCESS) && (mem_banks != NULL))
                print_bank_table();

        free_bank_list();

        return (print_memory_module_group_table(plafh));
}

/*
 * print the hub device
 */
static int
logprintf_hub_devices(picl_nodehdl_t hubh)
{
        char            *name;
        int             portnum;
        int             err;

        err = picldiag_get_string_propval(hubh, PICL_PROP_NAME, &name);
        if (err != PICL_SUCCESS)
                return (err);
        log_printf("%-12.12s  ", name);
        free(name);

        err = picl_get_propval_by_name(hubh, OBP_PROP_REG,
            &portnum, sizeof (portnum));
        if (err == PICL_PROPNOTFOUND)
                log_printf("-\n");
        else if (err == PICL_SUCCESS)
                log_printf("%3d\n", portnum);
        else
                return (err);

        return (PICL_SUCCESS);
}

/*
 * callback functions to display hub devices
 */
/* ARGSUSED */
static int
print_usb_devices(picl_nodehdl_t hubh, void *arg)
{
        picl_nodehdl_t  chdh;
        char            *rootname;
        int             type = *(int *)arg;
        int             hubnum;
        int             err;

        err = picl_get_propval_by_name(hubh, PICL_PROP_CHILD, &chdh,
            sizeof (picl_nodehdl_t));

        /* print header */
        if (err == PICL_SUCCESS) {
                err = picldiag_get_string_propval(hubh, PICL_PROP_NAME,
                    &rootname);
                if (err != PICL_SUCCESS)
                        return (err);

                if (type == USB) {
                        log_printf("\n===============================");
                        log_printf(dgettext(TEXT_DOMAIN,
                            " %s Devices "), rootname);
                } else {
                        /* Get its hub number */
                        err = picl_get_propval_by_name(hubh,
                            OBP_PROP_REG, &hubnum, sizeof (hubnum));
                        if ((err != PICL_SUCCESS) &&
                            (err != PICL_PROPNOTFOUND)) {
                                free(rootname);
                                return (err);
                        }

                        log_printf("\n===============================");
                        if (err == PICL_SUCCESS)
                                log_printf(dgettext(TEXT_DOMAIN,
                                    " %s#%d Devices "),
                                    rootname, hubnum);
                        else
                                log_printf(dgettext(TEXT_DOMAIN,
                                    " %s Devices "), rootname);
                }

                log_printf("===============================\n\n");
                log_printf("Name          Port#\n");
                log_printf("------------  -----\n");
                free(rootname);

                do {
                        logprintf_hub_devices(chdh);

                        err = picl_get_propval_by_name(chdh, PICL_PROP_PEER,
                            &chdh, sizeof (picl_nodehdl_t));
                } while (err == PICL_SUCCESS);
        }


        if (err == PICL_PROPNOTFOUND)
                return (PICL_WALK_CONTINUE);
        return (err);
}

/*
 * callback functions to display usb devices
 */
/* ARGSUSED */
static int
usb_callback(picl_nodehdl_t usbh, void *args)
{
        int             err;
        int             type;

        type = USB;
        err = print_usb_devices(usbh, &type);
        if (err != PICL_WALK_CONTINUE)
                return (err);
        type = HUB;
        err = picl_walk_tree_by_class(usbh, NULL, &type, print_usb_devices);
        if (err == PICL_SUCCESS)
                err = PICL_WALK_CONTINUE;
        return (err);
}


/*
 * find usb devices and print its information
 */
static int
display_usb_devices(picl_nodehdl_t plafh)
{
        int err;

        /*
         * get the usb node
         */
        err = picl_walk_tree_by_class(plafh, PICL_CLASS_USB, NULL,
            usb_callback);
        return (err);
}



/*
 * If nodeh is the io device, add it into the io list and return
 * If it is not an io device and it has the subtree, traverse the subtree
 * and add all leaf io devices
 */
static int
add_io_leaves(picl_nodehdl_t nodeh, char *parentname, uint32_t board,
    uint32_t bus_id, uint64_t slot, uint32_t freq, char *model, char *status)
{
        picl_nodehdl_t  childh;
        picl_prophdl_t  proph;
        picl_propinfo_t pinfo;
        int             err;
        char            *nameval;
        char            piclclass[PICL_CLASSNAMELEN_MAX];
        char            nodename[MAXSTRLEN];
        char            name[MAXSTRLEN];
        char            *devfs_path;
        char            *compatible;
        picl_nodehdl_t  fruparenth;
        char            *label;
        char            binding_name[MAXSTRLEN];

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

        nameval = alloca(pinfo.size);
        if (nameval == NULL)
                return (PICL_FAILURE);

        err = picl_get_propval(proph, nameval, pinfo.size);
        if (err != PICL_SUCCESS)
                return (err);

        (void) strlcpy(nodename, nameval, MAXSTRLEN);

        err = picl_get_propval_by_name(nodeh, PICL_PROP_CLASSNAME,
            piclclass, sizeof (piclclass));
        if (err != PICL_SUCCESS)
                return (err);

        /* 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_PROPNOTFOUND) {
                /*
                 * if compatible prop is found, name will be
                 * <nodename>-<compatible>
                 */
                err = picldiag_get_first_compatible_value(nodeh, &compatible);
                if (err == PICL_SUCCESS) {
                        strlcat(nodename, "-", MAXSTRLEN);
                        strlcat(nodename, compatible, MAXSTRLEN);
                        free(compatible);
                } else if (err != PICL_PROPNOTFOUND) {
                        return (err);
                }
        } else if (err != PICL_SUCCESS) {
                return (err);
        } else if (strcmp(nodename, binding_name) != 0) {
                if (strcmp(nodename, piclclass) == 0) {
                        /*
                         * nodename same as binding name -
                         * no need to display twice
                         */
                        strlcpy(nodename, binding_name, MAXSTRLEN);
                } else {
                        strlcat(nodename, "-", MAXSTRLEN);
                        strlcat(nodename, binding_name, MAXSTRLEN);
                }
        }

        /*
         * If it is an immediate child under pci/sbus/upa and not
         * a bus node, add it to the io list.
         * If it is a child under sub-bus and it is in an io
         * device, add it to the io list.
         */
        if (((parentname == NULL) && (!is_bus(piclclass))) ||
            ((parentname != NULL) && (is_io_device(piclclass)))) {
                if (parentname == NULL)
                        (void) snprintf(name, MAXSTRLEN, "%s", nodename);
                else
                        (void) snprintf(name, MAXSTRLEN, "%s/%s", parentname,
                            nodename);

                /*
                 * append the class if its class is not a generic
                 * obp-device class
                 */
                if (strcmp(piclclass, PICL_CLASS_OBP_DEVICE))
                        (void) snprintf(name, MAXSTRLEN, "%s (%s)", name,
                            piclclass);

                err = picldiag_get_fru_parent(nodeh, &fruparenth);
                if (err == PICL_PROPNOTFOUND) {
                        label = NULL;
                } else if (err != PICL_SUCCESS) {
                        return (err);
                } else {
                        err = picldiag_get_combined_label(fruparenth, &label,
                            15);
                        if (err == PICL_PROPNOTFOUND)
                                label = NULL;
                        else if (err != PICL_SUCCESS)
                                return (err);
                }
                /* devfs-path */
                err =  picldiag_get_string_propval(nodeh, PICL_PROP_DEVFS_PATH,
                    &devfs_path);
                if (err == PICL_PROPNOTFOUND)
                        devfs_path = NULL;
                else if (err != PICL_SUCCESS)
                        return (err);

                add_io_card(board, bus_id, slot, label, freq, name,
                    model, status, devfs_path);
                if (label != NULL)
                        free(label);
                if (devfs_path != NULL)
                        free(devfs_path);
                return (PICL_SUCCESS);
        }

        /*
         * If there is any child, Go through each child.
         */

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

        /* there is a child */
        while (err == PICL_SUCCESS) {
                if (parentname == NULL)
                        (void) strlcpy(name, nodename, MAXSTRLEN);
                else
                        (void) snprintf(name, MAXSTRLEN, "%s/%s", parentname,
                            nodename);

                err = add_io_leaves(childh, name, board, bus_id, slot, freq,
                    model, status);
                if (err != PICL_SUCCESS)
                        return (err);
                /*
                 * get next child
                 */
                err = picl_get_propval_by_name(childh, PICL_PROP_PEER,
                    &childh, sizeof (picl_nodehdl_t));
        }

        if (err == PICL_PROPNOTFOUND)
                return (PICL_SUCCESS);
        return (err);
}

/*
 * callback function to add all io devices under sbus in io list
 */
/*ARGSUSED*/
static int
sbus_callback(picl_nodehdl_t sbush, void *args)
{
        picl_nodehdl_t  nodeh;
        int             err;
        uint32_t        boardnum;
        uint32_t        bus_id;
        uint32_t        slot;
        uint32_t        freq;
        char            *model;
        char            *status;

        /* Fill in common infomation */
        bus_id = SBUS_TYPE;

        err = picldiag_get_clock_freq(sbush, &freq);
        if (err == PICL_PROPNOTFOUND)
                return (PICL_WALK_CONTINUE);
        else if (err != PICL_SUCCESS)
                return (err);
        /*
         * If no board# is found, set boardnum to 0
         */
        boardnum = picldiag_get_uint_propval(sbush, OBP_PROP_BOARD_NUM, &err);
        if (err == PICL_PROPNOTFOUND)
                boardnum = DEFAULT_BOARD_NUM;
        else if (err != PICL_SUCCESS)
                return (err);

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

        while (err == PICL_SUCCESS) {
                slot = picldiag_get_uint_propval(nodeh,
                    PICL_PROP_SLOT, &err);
                if (err == PICL_PROPNOTFOUND) {
                        err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER,
                            &nodeh, sizeof (picl_nodehdl_t));
                        continue;
                } else if (err != PICL_SUCCESS)
                        return (err);

                err =  picldiag_get_string_propval(nodeh, OBP_PROP_MODEL,
                    &model);
                if (err == PICL_PROPNOTFOUND)
                        model = NULL;
                else if (err != PICL_SUCCESS)
                        return (err);

                err = picldiag_get_string_propval(nodeh, OBP_PROP_STATUS,
                    &status);
                if (err == PICL_PROPNOTFOUND) {
                        status = malloc(5);
                        strncpy(status, "okay", 5);
                } else if (err != PICL_SUCCESS)
                        return (err);

                err = add_io_leaves(nodeh, NULL, boardnum, bus_id, slot, freq,
                    model, status);
                if (model != NULL)
                        free(model);
                if (status != NULL)
                        free(status);
                if (err != PICL_SUCCESS)
                        return (err);

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

/*
 * add all io devices under pci in io list
 */
/* ARGSUSED */
static int
pci_callback(picl_nodehdl_t pcih, void *args)
{
        picl_nodehdl_t  nodeh;
        int             err;
        char            piclclass[PICL_CLASSNAMELEN_MAX];
        uint32_t        boardnum;
        uint32_t        bus_id;
        uint32_t        slot;
        uint32_t        freq;
        char            *model;
        char            *status;

        /* Fill in common infomation */
        bus_id = PCI_TYPE;

        /*
         * Check if it has the freq, if not,
         * If not, use its parent's freq
         * if its parent's freq is not found, return
         */
        err = picldiag_get_clock_freq(pcih, &freq);
        if (err == PICL_PROPNOTFOUND) {
                err = picldiag_get_clock_from_parent(pcih, &freq);
                if (err == PICL_PROPNOTFOUND)
                        return (PICL_WALK_CONTINUE);
                else if (err != PICL_SUCCESS)
                        return (err);
        } else if (err != PICL_SUCCESS)
                return (err);

        /*
         * If no board# is found, set boardnum to 0
         */
        boardnum = picldiag_get_uint_propval(pcih, OBP_PROP_BOARD_NUM, &err);
        if (err == PICL_PROPNOTFOUND)
                boardnum = DEFAULT_BOARD_NUM;
        else if (err != PICL_SUCCESS)
                return (err);

        /* 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,
                    piclclass, sizeof (piclclass));
                if (err != PICL_SUCCESS)
                        return (err);

                /*
                 * Skip PCI bridge and USB devices because they will be
                 * processed later
                 */
                if ((strcmp(piclclass, PICL_CLASS_PCI) == 0) ||
                    (strcmp(piclclass, PICL_CLASS_USB) == 0)) {
                        err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER,
                            &nodeh, sizeof (picl_nodehdl_t));
                        continue;
                }

                /* Get the device id for pci card */
                slot = picldiag_get_uint_propval(nodeh,
                    PICL_PROP_DEVICE_ID, &err);
                if (err == PICL_PROPNOTFOUND) {
                        err = picl_get_propval_by_name(nodeh, PICL_PROP_PEER,
                            &nodeh, sizeof (picl_nodehdl_t));
                        continue;
                } else if (err != PICL_SUCCESS)
                        return (err);

                /* Get the model of this card */
                err = picldiag_get_string_propval(nodeh, OBP_PROP_MODEL,
                    &model);
                if (err == PICL_PROPNOTFOUND)
                        model = NULL;
                else if (err != PICL_SUCCESS)
                        return (err);

                err = picldiag_get_string_propval(nodeh, OBP_PROP_STATUS,
                    &status);
                if (err == PICL_PROPNOTFOUND) {
                        status = malloc(5);
                        strncpy(status, "okay", 5);
                } else if (err != PICL_SUCCESS)
                        return (err);

                err = add_io_leaves(nodeh, NULL, boardnum, bus_id, slot,
                    freq, model, status);

                if (model != NULL)
                        free(model);

                if (status != NULL)
                        free(status);

                if (err != PICL_SUCCESS)
                        return (err);

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

        }

        if (err == PICL_PROPNOTFOUND)
                return (PICL_WALK_CONTINUE);

        return (err);
}

/*
 * add io devices in io list
 * Its slot number is drived from upa-portid
 */
static int
add_io_devices(picl_nodehdl_t nodeh)
{
        int             err;
        uint64_t        board_type;
        char            piclclass[PICL_CLASSNAMELEN_MAX];
        char            name[MAXSTRLEN];
        char            *devfs_path;
        char            *nameval;
        uint32_t        boardnum;
        uint32_t        bus_id;
        uint32_t        slot;
        uint32_t        freq;
        char            *model;
        char            *status;
        picl_prophdl_t  proph;
        picl_propinfo_t pinfo;
        picl_nodehdl_t  fruparenth;
        char            *label;


        bus_id = UPA_TYPE;

        /*
         * If clock frequency can't be found from its parent, don't add
         */
        err = picldiag_get_clock_from_parent(nodeh, &freq);
        if (err == PICL_PROPNOTFOUND)
                return (PICL_SUCCESS);
        else if (err != PICL_SUCCESS)
                return (err);

        /*
         * If no board# is found, set boardnum to 0
         */
        boardnum = picldiag_get_uint_propval(nodeh, OBP_PROP_BOARD_NUM, &err);
        if (err == PICL_PROPNOTFOUND)
                boardnum = DEFAULT_BOARD_NUM;
        else if (err != PICL_SUCCESS)
                return (err);

        /*
         * get upa portid as slot number
         * If upa portid is not found, don't add the card.
         */
        slot = picldiag_get_uint_propval(nodeh, OBP_PROP_UPA_PORTID,
            &err);
        if (err == PICL_PROPNOTFOUND)
                return (PICL_SUCCESS);
        else if (err != PICL_SUCCESS)
                return (err);

        /* Get the model of this card */
        err = picldiag_get_string_propval(nodeh, OBP_PROP_MODEL, &model);
        if (err == PICL_PROPNOTFOUND)
                model = NULL;
        else if (err != PICL_SUCCESS)
                return (err);

        /*
         * check if it is a ffb device
         * If it's a ffb device, append its board type to name
         * otherwise, use its nodename
         */
        err = picl_get_prop_by_name(nodeh, PICL_PROP_FFB_BOARD_REV, &proph);
        if (err == PICL_PROPNOTFOUND) {
                err = picl_get_propinfo_by_name(nodeh, PICL_PROP_NAME,
                    &pinfo, &proph);
                if (err != PICL_SUCCESS)
                        return (err);

                nameval = alloca(pinfo.size);
                if (nameval == NULL)
                        return (PICL_FAILURE);

                err = picl_get_propval(proph, nameval, pinfo.size);
                if (err != PICL_SUCCESS)
                        return (err);

                (void) strlcpy(name, nameval, MAXSTRLEN);
        } else if (err == PICL_SUCCESS) {
                /* Find out if it's single or double buffered */
                board_type = picldiag_get_uint_propval(nodeh,
                    OBP_PROP_BOARD_TYPE, &err);
                if (err == PICL_PROPNOTFOUND)
                        (void) strlcpy(name, FFB_NAME, sizeof (name));
                if (err == PICL_SUCCESS) {
                        if (board_type & FFB_B_BUFF)
                                (void) strlcpy(name, FFB_DOUBLE_BUF,
                                    sizeof (name));
                        else
                                (void) strlcpy(name, FFB_SINGLE_BUF,
                                    sizeof (name));
                } else
                        return (err);
        } else
                return (err);

        err = picl_get_propval_by_name(nodeh, PICL_PROP_CLASSNAME,
            piclclass, sizeof (piclclass));
        if (err != PICL_SUCCESS)
                return (err);

        (void) snprintf(name, sizeof (name), "%s (%s)", name, piclclass);

        err = picldiag_get_string_propval(nodeh, OBP_PROP_STATUS, &status);
        if (err == PICL_PROPNOTFOUND) {
                status = malloc(5);
                strncpy(status, "okay", 5);
        } else if (err != PICL_SUCCESS)
                return (err);

        err = picldiag_get_fru_parent(nodeh, &fruparenth);
        if (err == PICL_PROPNOTFOUND) {
                label = NULL;
        } else if (err != PICL_SUCCESS) {
                return (err);
        } else {
                err = picldiag_get_combined_label(fruparenth, &label, 15);
                if (err == PICL_PROPNOTFOUND)
                        label = NULL;
                else if (err != PICL_SUCCESS)
                        return (err);
        }
        /* devfs-path */
        err =  picldiag_get_string_propval(nodeh, PICL_PROP_DEVFS_PATH,
            &devfs_path);
        if (err == PICL_PROPNOTFOUND)
                devfs_path = NULL;
        else if (err != PICL_SUCCESS)
                return (err);

        add_io_card(boardnum, bus_id, slot, label, freq, name, model, status,
            devfs_path);
        if (label != NULL)
                free(label);
        if (model != NULL)
                free(model);
        if (status != NULL)
                free(status);
        if (devfs_path != NULL)
                free(devfs_path);

        return (PICL_SUCCESS);
}

/*
 * loop through all children and add io devices in io list
 */
static int
process_io_leaves(picl_nodehdl_t rooth)
{
        picl_nodehdl_t  nodeh;
        char            classval[PICL_CLASSNAMELEN_MAX];
        int             err;

        err = picl_get_propval_by_name(rooth, PICL_PROP_CHILD, &nodeh,
            sizeof (picl_nodehdl_t));
        while (err == PICL_SUCCESS) {
                err = picl_get_propval_by_name(nodeh, PICL_PROP_CLASSNAME,
                    classval, sizeof (classval));
                if (err != PICL_SUCCESS)
                        return (err);

                if (is_io_device(classval))
                        err = add_io_devices(nodeh);

                if (err != PICL_SUCCESS)
                        return (err);

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

        if (err == PICL_PROPNOTFOUND)
                return (PICL_SUCCESS);

        return (err);
}

/*
 * callback function to add all io devices under upa in io list
 */
/*ARGSUSED*/
static int
upa_callback(picl_nodehdl_t upah, void *args)
{
        int             err;

        err = process_io_leaves(upah);

        if (err == PICL_SUCCESS)
                return (PICL_WALK_CONTINUE);
        return (err);
}

/*
 * display ffb hardware configuration
 */
/* ARGSUSED */
static int
ffbconfig_callback(picl_nodehdl_t ffbh, void *arg)
{
        int             err;
        uint64_t        board_rev;
        uint64_t        fbc_ver;
        char            *dac_ver;
        char            *fbram_ver;

        /*
         * If it has PICL_PROP_FFB_BOARD_REV, it is a ffb device
         * Otherwise, return.
         */
        board_rev = picldiag_get_uint_propval(ffbh, PICL_PROP_FFB_BOARD_REV,
            &err);
        if (err == PICL_PROPNOTFOUND)
                return (PICL_WALK_CONTINUE);
        else if (err != PICL_SUCCESS)
                return (err);

        log_printf("FFB Hardware Configuration:\n");
        log_printf("-----------------------------------\n");
        log_printf("Board rev: %lld\n", board_rev);

        fbc_ver = picldiag_get_uint_propval(ffbh, OBP_PROP_FBC_REG_ID,
            &err);
        if (err == PICL_SUCCESS)
                log_printf("FBC version: 0x%llx\n", fbc_ver);
        else if (err != PICL_PROPNOTFOUND)
                return (err);

        err = picldiag_get_string_propval(ffbh, PICL_PROP_FFB_DAC_VER,
            &dac_ver);
        if (err == PICL_SUCCESS) {
                log_printf("DAC: %s\n", dac_ver);
                free(dac_ver);
        } else if (err != PICL_PROPNOTFOUND)
                return (err);

        err = picldiag_get_string_propval(ffbh, PICL_PROP_FFB_FBRAM_VER,
            &fbram_ver);
        if (err  == PICL_SUCCESS) {
                log_printf("3DRAM: %s\n", fbram_ver);
                free(fbram_ver);
        } else if (err != PICL_PROPNOTFOUND)
                return (err);

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

/*
 * find all io devices and add them in the io list
 */
static int
gather_io_cards(picl_nodehdl_t plafh)
{
        int             err;

        /*
         * look for io devices under the immediate children of platform
         */
        err = process_io_leaves(plafh);

        if (err != PICL_SUCCESS)
                return (err);

        err = picl_walk_tree_by_class(plafh, PICL_CLASS_SBUS,
            PICL_CLASS_SBUS, sbus_callback);
        if (err != PICL_SUCCESS)
                return (err);
        err = picl_walk_tree_by_class(plafh, PICL_CLASS_PCI,
            PICL_CLASS_PCI, pci_callback);
        if (err != PICL_SUCCESS)
                return (err);
        err = picl_walk_tree_by_class(plafh, PICL_CLASS_UPA,
            PICL_CLASS_UPA, upa_callback);
        return (err);
}

static void
picldiag_display_io_cards(struct io_card *list)
{
        static int banner = 0; /* Have we printed the column headings? */
        struct io_card *p;

        if (list == NULL)
                return;

        if (banner == 0) {
                log_printf("Bus   Freq      Slot +  Name +\n", 0);
                log_printf("Type  MHz       Status  "
                    "Path                          "
                    "Model", 0);
                log_printf("\n", 0);
                log_printf("----  ----  ----------  "
                    "----------------------------  "
                    "--------------------", 0);
                log_printf("\n", 0);
                banner = 1;
        }

        for (p = list; p != NULL; p = p -> next) {
                log_printf("%-4s  ", p->bus_type, 0);
                log_printf("%3d   ", p->freq, 0);
                /*
                 * We check to see if it's an int or
                 * a char string to display for slot.
                 */
                if (p->slot == PCI_SLOT_IS_STRING)
                        log_printf("%10s  ", p->slot_str, 0);
                else
                        log_printf("%10d  ", p->slot, 0);

                log_printf("%-28.28s", p->name, 0);
                if (strlen(p->name) > 28)
                        log_printf("+ ", 0);
                else
                        log_printf("  ", 0);
                log_printf("%-19.19s", p->model, 0);
                if (strlen(p->model) > 19)
                        log_printf("+", 0);
                log_printf("\n", 0);
                log_printf("            %10s  ", p->status, 0);
                set_exit_code(p->status);
                if (strlen(p->notes) > 0)
                        log_printf("%s", p->notes, 0);
                log_printf("\n\n", 0);
        }
}

/*
 * display all io devices
 */
static int
display_io_device_info(picl_nodehdl_t plafh)
{
        int     err;

        err = gather_io_cards(plafh);
        if (err != PICL_SUCCESS)
                return (err);

        logprintf_header(dgettext(TEXT_DOMAIN, "IO Devices"),
            DEFAULT_LINE_WIDTH);

        picldiag_display_io_cards(io_card_list);

        free_io_cards(io_card_list);

        return (PICL_SUCCESS);
}

/*
 * print fan device information
 */
static int
logprintf_fan_info(picl_nodehdl_t fanh)
{
        int             err;
        char            *label;
        char            *unit;
        int64_t         speed;
        int64_t         min_speed;
        picl_nodehdl_t  fruph;

        err = picldiag_get_fru_parent(fanh, &fruph);
        if (err != PICL_SUCCESS)
                return (err);

        err = picldiag_get_combined_label(fruph, &label, 14);
        if (err != PICL_SUCCESS)
                return (err);

        log_printf("%-14s ", label);
        free(label);

        err = picldiag_get_label(fanh, &label);
        if (err == PICL_SUCCESS) {
                log_printf("%-14s  ", label);
                free(label);
        } else if (err == PICL_PROPNOTFOUND || err == PICL_PROPVALUNAVAILABLE) {
                log_printf("  -           ");
        } else
                return (err);

        speed = picldiag_get_uint_propval(fanh, PICL_PROP_FAN_SPEED, &err);
        if (err == PICL_SUCCESS) {
                min_speed = picldiag_get_uint_propval(fanh,
                    PICL_PROP_LOW_WARNING_THRESHOLD, &err);
                if (err != PICL_SUCCESS)
                        min_speed = 1;
                if (speed < min_speed) {
                        log_printf("failed (%lld", speed);
                        err = picldiag_get_string_propval(fanh,
                            PICL_PROP_FAN_SPEED_UNIT, &unit);
                        if (err == PICL_SUCCESS) {
                                log_printf("%s", unit);
                                free(unit);
                        }
                        log_printf(")");
                        exit_code = PD_SYSTEM_FAILURE;
                } else {
                        log_printf("okay");
                }
        } else {
                err = picldiag_get_string_propval(fanh,
                    PICL_PROP_FAN_SPEED_UNIT, &unit);
                if (err == PICL_SUCCESS) {
                        log_printf("%-12s ", unit);
                        free(unit);
                }
        }

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

static int
fan_callback(picl_nodehdl_t fanh, void *arg)
{
        int     *countp = arg;
        int             err;

        if (*countp == 0) {
                log_printf(dgettext(TEXT_DOMAIN, "Fan Status:\n"));
                log_printf("---------------------------------------\n");
                log_printf("Location       Sensor          Status          \n");
                log_printf("---------------------------------------\n");
        }
        *countp += 1;
        err = logprintf_fan_info(fanh);
        if (err == PICL_SUCCESS)
                return (PICL_WALK_CONTINUE);
        return (err);
}

/*
 * callback function search children to find fan device and print its speed
 */
static int
display_fan_speed(picl_nodehdl_t plafh)
{
        int             err;
        int             print_header;

        print_header = 0;
        err = picl_walk_tree_by_class(plafh, PICL_CLASS_FAN,
            &print_header, fan_callback);
        return (err);
}

/*
 * print temperature sensor information
 */
static int
logprintf_temp_info(picl_nodehdl_t temph)
{
        int             err;
        char            *label;
        int64_t         temperature;
        int64_t         threshold;
        picl_nodehdl_t  fruph;
        char            *status = "unknown";
        int             got_temp = 0;

        err = picldiag_get_fru_parent(temph, &fruph);
        if (err != PICL_SUCCESS)
                return (err);

        err = picldiag_get_combined_label(fruph, &label, 14);
        if (err != PICL_SUCCESS)
                return (err);

        log_printf("%-14s ", label);
        free(label);

        err = picldiag_get_label(temph, &label);
        if (err != PICL_SUCCESS)
                return (err);
        log_printf("%-14s ", label);
        free(label);

        temperature = picldiag_get_int_propval(temph, PICL_PROP_TEMPERATURE,
            &err);
        if (err == PICL_SUCCESS) {
                got_temp = 1;
                status = "okay";
        } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) {
                return (err);
        }

        threshold = picldiag_get_int_propval(temph, PICL_PROP_LOW_WARNING,
            &err);
        if (err == PICL_SUCCESS) {
                if (got_temp && temperature < threshold)
                        status = "warning";
        } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) {
                return (err);
        }

        threshold = picldiag_get_int_propval(temph, PICL_PROP_LOW_SHUTDOWN,
            &err);
        if (err == PICL_SUCCESS) {
                if (got_temp && temperature < threshold)
                        status = "failed";
        } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) {
                return (err);
        }

        threshold = picldiag_get_int_propval(temph, PICL_PROP_HIGH_WARNING,
            &err);
        if (err == PICL_SUCCESS) {
                if (got_temp && temperature > threshold)
                        status = "warning";
        } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) {
                return (err);
        }

        threshold = picldiag_get_int_propval(temph, PICL_PROP_HIGH_SHUTDOWN,
            &err);
        if (err == PICL_SUCCESS) {
                if (got_temp && temperature > threshold)
                        status = "failed";
        } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) {
                return (err);
        }

        err = picldiag_get_string_propval(temph, PICL_PROP_CONDITION, &status);
        if (err == PICL_SUCCESS) {
                log_printf("%s", status);
                set_exit_code(status);
                free(status);
        } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) {
                return (err);
        } else {
                log_printf("%s ", status);
                set_exit_code(status);
                if (strcmp(status, "failed") == 0 ||
                    strcmp(status, "warning") == 0)
                        log_printf("(%.2lldC)", temperature);
        }

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

static int
temp_callback(picl_nodehdl_t temph, void *arg)
{
        int             err;
        int     *countp = arg;

        if (*countp == 0) {
                log_printf("\n");
                log_printf("---------------------------------------\n");
                log_printf(dgettext(TEXT_DOMAIN, "Temperature sensors:\n"));
                log_printf("------------------------------------\n");
                log_printf("Location       Sensor         Status\n");
                log_printf("------------------------------------\n");
        }
        *countp += 1;
        err = logprintf_temp_info(temph);
        if (err == PICL_SUCCESS)
                return (PICL_WALK_CONTINUE);
        return (err);
}

/*
 * callback function search children to find temp sensors and print the temp
 */
/* ARGSUSED */
static int
display_temp(picl_nodehdl_t plafh)
{
        int             err;
        int             print_header;

        print_header = 0;
        err = picl_walk_tree_by_class(plafh, PICL_CLASS_TEMPERATURE_SENSOR,
            &print_header, temp_callback);
        if (err != PICL_SUCCESS)
                return (err);
        err = picl_walk_tree_by_class(plafh, PICL_CLASS_TEMPERATURE_INDICATOR,
            &print_header, temp_callback);
        return (err);
}

/*
 * print current sensor information
 */
static int
logprintf_current_info(picl_nodehdl_t currenth)
{
        int             err;
        char            *label;
        float           current;
        float           threshold;
        picl_nodehdl_t  fruph;
        char            *status = "unknown";
        int             got_current = 0;

        err = picldiag_get_fru_parent(currenth, &fruph);
        if (err != PICL_SUCCESS)
                return (err);

        err = picldiag_get_combined_label(fruph, &label, 10);
        if (err != PICL_SUCCESS)
                return (err);

        log_printf("%-10s ", label);
        free(label);

        err = picldiag_get_label(currenth, &label);
        if (err != PICL_SUCCESS)
                return (err);
        log_printf("%-10s  ", label);
        free(label);

        current = picldiag_get_float_propval(currenth, PICL_PROP_CURRENT, &err);
        if (err == PICL_SUCCESS) {
                status = "okay";
                got_current = 1;
        } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) {
                return (err);
        }

        threshold = picldiag_get_float_propval(currenth, PICL_PROP_LOW_WARNING,
            &err);
        if (err == PICL_SUCCESS) {
                if (got_current && current < threshold)
                        status = "warning";
        } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) {
                return (err);
        }

        threshold = picldiag_get_float_propval(currenth, PICL_PROP_LOW_SHUTDOWN,
            &err);
        if (err == PICL_SUCCESS) {
                if (got_current && current < threshold)
                        status = "failed";
        } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) {
                return (err);
        }

        threshold = picldiag_get_float_propval(currenth, PICL_PROP_HIGH_WARNING,
            &err);
        if (err == PICL_SUCCESS) {
                if (got_current && current > threshold)
                        status = "warning";
        } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) {
                return (err);
        }

        threshold = picldiag_get_float_propval(currenth,
            PICL_PROP_HIGH_SHUTDOWN, &err);
        if (err == PICL_SUCCESS) {
                if (got_current && current > threshold)
                        status = "failed";
        } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) {
                return (err);
        }

        err = picldiag_get_string_propval(currenth,
            PICL_PROP_CONDITION, &status);
        if (err == PICL_SUCCESS) {
                log_printf(" %s", status);
                set_exit_code(status);
                free(status);
        } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) {
                return (err);
        } else {
                log_printf("%s ", status);
                set_exit_code(status);
                if (strcmp(status, "failed") == 0 ||
                    strcmp(status, "warning") == 0)
                        log_printf("(%.2fA)", current);
        }

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

static int
current_callback(picl_nodehdl_t currh, void *arg)
{
        int             err;
        int     *countp = arg;

        if (*countp == 0) {
                log_printf("------------------------------------\n");
                log_printf(dgettext(TEXT_DOMAIN, "Current sensors:\n"));
                log_printf("------------------------------\n");
                log_printf("Location  Sensor        Status\n");
                log_printf("------------------------------\n");
        }
        *countp += 1;
        err = logprintf_current_info(currh);
        if (err == PICL_SUCCESS)
                return (PICL_WALK_CONTINUE);
        return (err);
}

/*
 * callback function search children to find curr sensors and print the curr
 */
/* ARGSUSED */
static int
display_current(picl_nodehdl_t plafh)
{
        int             err;
        int             print_header;

        print_header = 0;
        err = picl_walk_tree_by_class(plafh, PICL_CLASS_CURRENT_SENSOR,
            &print_header, current_callback);
        if (err != PICL_SUCCESS)
                return (err);
        err = picl_walk_tree_by_class(plafh, PICL_CLASS_CURRENT_INDICATOR,
            &print_header, current_callback);
        return (err);
}

/*
 * print voltage sensor information
 */
static int
logprintf_voltage_info(picl_nodehdl_t voltageh)
{
        int             err;
        char            *label;
        float           voltage;
        float           threshold;
        picl_nodehdl_t  fruph;
        char            *status = "unknown";
        int             got_voltage = 0;

        err = picldiag_get_fru_parent(voltageh, &fruph);
        if (err != PICL_SUCCESS)
                return (err);

        err = picldiag_get_combined_label(fruph, &label, 10);
        if (err != PICL_SUCCESS)
                return (err);

        log_printf("%-10s ", label);
        free(label);

        err = picldiag_get_label(voltageh, &label);
        if (err != PICL_SUCCESS)
                return (err);
        log_printf("%-12s  ", label);
        free(label);

        voltage = picldiag_get_float_propval(voltageh, PICL_PROP_VOLTAGE, &err);
        if (err == PICL_SUCCESS) {
                status = "okay";
                got_voltage = 1;
        } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) {
                return (err);
        }

        threshold = picldiag_get_float_propval(voltageh, PICL_PROP_LOW_WARNING,
            &err);
        if (err == PICL_SUCCESS) {
                if (got_voltage && voltage < threshold)
                        status = "warning";
        } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) {
                return (err);
        }

        threshold = picldiag_get_float_propval(voltageh, PICL_PROP_LOW_SHUTDOWN,
            &err);
        if (err == PICL_SUCCESS) {
                if (got_voltage && voltage < threshold)
                        status = "failed";
        } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) {
                return (err);
        }

        threshold = picldiag_get_float_propval(voltageh, PICL_PROP_HIGH_WARNING,
            &err);
        if (err == PICL_SUCCESS) {
                if (got_voltage && voltage > threshold)
                        status = "warning";
        } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) {
                return (err);
        }

        threshold = picldiag_get_float_propval(voltageh,
            PICL_PROP_HIGH_SHUTDOWN, &err);
        if (err == PICL_SUCCESS) {
                if (got_voltage && voltage > threshold)
                        status = "failed";
        } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) {
                return (err);
        }

        err = picldiag_get_string_propval(voltageh,
            PICL_PROP_CONDITION, &status);
        if (err == PICL_SUCCESS) {
                log_printf("%s", status);
                set_exit_code(status);
                free(status);
        } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) {
                return (err);
        } else {
                log_printf("%s ", status);
                set_exit_code(status);
                if (strcmp(status, "warning") == 0 ||
                    strcmp(status, "failed") == 0)
                        log_printf("(%.2fV)", voltage);
        }

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

static int
voltage_callback(picl_nodehdl_t voltageh, void *arg)
{
        int     *countp = arg;
        int             err;

        if (*countp == 0) {
                log_printf("--------------------------------\n");
                log_printf(dgettext(TEXT_DOMAIN, "Voltage sensors:\n"));
                log_printf("-------------------------------\n");
                log_printf("Location   Sensor        Status\n");
                log_printf("-------------------------------\n");
        }
        *countp += 1;
        err = logprintf_voltage_info(voltageh);
        if (err == PICL_SUCCESS)
                return (PICL_WALK_CONTINUE);
        return (err);
}

/*
 * callback function search children to find voltage sensors and print voltage
 */
/* ARGSUSED */
static int
display_voltage(picl_nodehdl_t plafh)
{
        int             err;
        int             print_header;

        print_header = 0;
        err = picl_walk_tree_by_class(plafh, PICL_CLASS_VOLTAGE_SENSOR,
            &print_header, voltage_callback);
        if (err != PICL_SUCCESS)
                return (err);
        err = picl_walk_tree_by_class(plafh, PICL_CLASS_VOLTAGE_INDICATOR,
            &print_header, voltage_callback);
        return (err);
}

/*
 * print led device information
 */
static int
logprintf_led_info(picl_nodehdl_t ledh)
{
        int             err;
        char            *label;
        char            *state;
        char            *color;
        picl_nodehdl_t  fruph;

        err = picldiag_get_fru_parent(ledh, &fruph);
        if (err != PICL_SUCCESS)
                return (err);

        err = picldiag_get_combined_label(fruph, &label, 10);
        if (err != PICL_SUCCESS) {
                log_printf("      -    ", label);
        } else {
                log_printf("%-10s ", label);
                free(label);
        }

        err = picldiag_get_label(ledh, &label);
        if (err != PICL_SUCCESS)
                return (err);
        log_printf("%-20s  ", label);
        free(label);

        err = picldiag_get_string_propval(ledh, PICL_PROP_STATE, &state);
        if (err == PICL_PROPNOTFOUND || err == PICL_PROPVALUNAVAILABLE) {
                log_printf("     -     ");
        } else if (err != PICL_SUCCESS) {
                return (err);
        } else {
                log_printf("%-10s  ", state);
                free(state);
        }

        err = picldiag_get_string_propval(ledh, PICL_PROP_COLOR, &color);
        if (err == PICL_PROPNOTFOUND || err == PICL_PROPVALUNAVAILABLE) {
                log_printf("\n");
        } else if (err != PICL_SUCCESS) {
                return (err);
        } else {
                log_printf("%-16s\n", color);
                free(color);
        }

        return (PICL_SUCCESS);
}

static int
led_callback(picl_nodehdl_t ledh, void *arg)
{
        int             *countp = arg;
        int             err;

        if (*countp == 0) {

                log_printf("--------------------------------------"
                    "------------\n");
                log_printf(dgettext(TEXT_DOMAIN, "Led State:\n"));
                log_printf("--------------------------------------"
                    "------------\n");
                log_printf("Location   Led                   State"
                    "       Color\n");
                log_printf("--------------------------------------"
                    "------------\n");
        }
        *countp += 1;
        err = logprintf_led_info(ledh);
        if (err == PICL_SUCCESS)
                return (PICL_WALK_CONTINUE);
        return (err);
}

/*
 * callback function search children to find led devices and print status
 */
/* ARGSUSED */
static int
display_led_status(picl_nodehdl_t plafh)
{
        int             print_header;

        print_header = 0;
        picl_walk_tree_by_class(plafh, PICL_CLASS_LED,
            &print_header, led_callback);
        return (PICL_SUCCESS);
}

/*
 * print keyswitch device information
 */
static int
logprintf_keyswitch_info(picl_nodehdl_t keyswitchh, picl_nodehdl_t fruph)
{
        int             err;
        char            *label;
        char            *state;

        err = picldiag_get_combined_label(fruph, &label, 10);
        if (err != PICL_SUCCESS) {
                log_printf("%-14s", "     -");
        } else {
                log_printf("%-14s ", label);
                free(label);
        }

        err = picldiag_get_label(keyswitchh, &label);
        if (err != PICL_SUCCESS)
                return (err);
        log_printf("%-11s ", label);
        free(label);

        err = picldiag_get_string_propval(keyswitchh, PICL_PROP_STATE, &state);
        if (err == PICL_PROPNOTFOUND || err == PICL_PROPVALUNAVAILABLE) {
                log_printf("     -\n");
        } else if (err != PICL_SUCCESS) {
                return (err);
        } else {
                log_printf("%s\n", state);
                free(state);
        }

        return (PICL_SUCCESS);
}

static int
keyswitch_callback(picl_nodehdl_t keyswitchh, void *arg)
{
        int             *countp = arg;
        int             err;
        picl_nodehdl_t  fruph;

        /*
         * Tamale simulates a key-switch on ENxS. So the presence of a
         * node of class keyswitch is not sufficient. If it has a fru parent
         * or location parent, then believe it.
         */
        err = picl_get_propval_by_name(keyswitchh, PICL_REFPROP_FRU_PARENT,
            &fruph, sizeof (fruph));
        if (err == PICL_PROPNOTFOUND) {
                err = picl_get_propval_by_name(keyswitchh,
                    PICL_REFPROP_LOC_PARENT, &fruph, sizeof (fruph));
        }
        if (err == PICL_PROPNOTFOUND || err == PICL_PROPVALUNAVAILABLE)
                return (PICL_WALK_CONTINUE);
        if (err != PICL_SUCCESS)
                return (err);

        if (*countp == 0) {
                log_printf("-----------------------------------------\n");
                log_printf(dgettext(TEXT_DOMAIN, "Keyswitch:\n"));
                log_printf("-----------------------------------------\n");
                log_printf(dgettext(TEXT_DOMAIN,
                    "Location       Keyswitch   State\n"));
                log_printf("-----------------------------------------\n");
        }
        *countp += 1;
        err = logprintf_keyswitch_info(keyswitchh, fruph);
        if (err == PICL_SUCCESS)
                return (PICL_WALK_CONTINUE);
        return (err);
}

/*
 * search children to find keyswitch device(s) and print status
 */
/* ARGSUSED */
static int
display_keyswitch(picl_nodehdl_t plafh)
{
        int             print_header = 0;

        picl_walk_tree_by_class(plafh, PICL_CLASS_KEYSWITCH,
            &print_header, keyswitch_callback);
        return (PICL_SUCCESS);
}

/*
 * display environment status
 */
static int
display_envctrl_status(picl_nodehdl_t plafh)
{
        logprintf_header(dgettext(TEXT_DOMAIN, "Environmental Status"),
            DEFAULT_LINE_WIDTH);

        display_fan_speed(plafh);
        display_temp(plafh);
        display_current(plafh);
        display_voltage(plafh);
        display_keyswitch(plafh);
        display_led_status(plafh);

        return (PICL_SUCCESS);
}

/*
 * print fru operational status
 */
static int
logprintf_fru_oper_status(picl_nodehdl_t fruh, int *countp)
{
        int             err;
        char            *label;
        char            *status;

        err = picldiag_get_combined_label(fruh, &label, 15);
        if (err != PICL_SUCCESS)
                return (PICL_WALK_CONTINUE);

        err = picldiag_get_string_propval(fruh,
            PICL_PROP_OPERATIONAL_STATUS, &status);
        if (err == PICL_SUCCESS) {
                if (*countp == 0) {
                        logprintf_header(dgettext(TEXT_DOMAIN,
                            "FRU Operational Status"),
                            DEFAULT_LINE_WIDTH);
                        log_printf("-------------------------\n");
                        log_printf(dgettext(TEXT_DOMAIN,
                            "Fru Operational Status:\n"));
                        log_printf("-------------------------\n");
                        log_printf("Location        Status   \n");
                        log_printf("-------------------------\n");
                }
                *countp += 1;
                log_printf("%-15s ", label);
                free(label);
                log_printf("%s\n", status);
                set_exit_code(status);
                free(status);
        } else if (err != PICL_PROPNOTFOUND && err != PICL_PROPVALUNAVAILABLE) {
                free(label);
                return (err);
        } else {
                free(label);
        }
        return (PICL_WALK_CONTINUE);
}

static int
fru_oper_status_callback(picl_nodehdl_t fruh, void *arg)
{
        int err;

        err = logprintf_fru_oper_status(fruh, (int *)arg);
        return (err);
}

/*
 * display fru operational status
 */
static int
display_fru_oper_status(picl_nodehdl_t frutreeh)
{
        int             print_header;

        print_header = 0;
        picl_walk_tree_by_class(frutreeh, PICL_CLASS_FRU,
            &print_header, fru_oper_status_callback);
        return (PICL_SUCCESS);
}

/*
 * check if the node having the version prop
 * If yes, print its nodename and version
 */
/* ARGSUSED */
static int
asicrev_callback(picl_nodehdl_t nodeh, void *arg)
{
        uint32_t        version;
        char            *name;
        char            *model;
        char            *status;
        int             err;

        version = picldiag_get_uint_propval(nodeh, OBP_PROP_VERSION_NUM,
            &err);
        if (err == PICL_PROPNOTFOUND)
                return (PICL_WALK_CONTINUE);
        else if (err != PICL_SUCCESS)
                return (err);

        /* devfs-path */
        err =  picldiag_get_string_propval(nodeh, PICL_PROP_DEVFS_PATH, &name);
        if (err == PICL_PROPNOTFOUND)
                name = NULL;
        else if (err != PICL_SUCCESS)
                return (err);

        /* model */
        err =  picldiag_get_string_propval(nodeh, PICL_PROP_BINDING_NAME,
            &model);
        if (err == PICL_PROPNOTFOUND)
                model = NULL;
        else if (err != PICL_SUCCESS)
                return (err);

        /* status */
        err = picldiag_get_string_propval(nodeh, OBP_PROP_STATUS, &status);
        if (err == PICL_PROPNOTFOUND)
                status = NULL;
        else if (err != PICL_SUCCESS)
                return (err);

        /*
         * Display the data
         */

        /* name */
        if (name != NULL) {
                log_printf("%-22s ", name);
                free(name);
        } else
                log_printf("%-22s ", "unknown");
        /* model */
        if (model != NULL) {
                log_printf("%-15s  ", model);
                free(model);
        } else
                log_printf("%-15s  ", "unknown");
        /* status */
        if (status == NULL)
                log_printf("%-15s  ", "okay");
        else {
                log_printf("%-15s  ", status);
                set_exit_code(status);
                free(status);
        }
        /* revision */
        log_printf("  %-4d\n",  version);

        return (PICL_WALK_CONTINUE);
}

/*
 * traverse the tree to display asic revision id for ebus
 */
/* ARGSUSED */
static int
ebus_callback(picl_nodehdl_t ebush, void *arg)
{
        uint32_t        id;
        char            *name;
        int             err;
        char            *model;
        char            *status;

        id = picldiag_get_uint_propval(ebush, OBP_PROP_REVISION_ID, &err);
        if (err == PICL_PROPNOTFOUND)
                return (PICL_WALK_CONTINUE);
        else if (err != PICL_SUCCESS)
                return (err);

        /* devfs-path */
        err =  picldiag_get_string_propval(ebush, PICL_PROP_DEVFS_PATH, &name);
        if (err == PICL_PROPNOTFOUND)
                name = NULL;
        else if (err != PICL_SUCCESS)
                return (err);

        /* model */
        err =  picldiag_get_string_propval(ebush, PICL_PROP_BINDING_NAME,
            &model);
        if (err == PICL_PROPNOTFOUND)
                model = NULL;
        else if (err != PICL_SUCCESS)
                return (err);

        /* status */
        err = picldiag_get_string_propval(ebush, OBP_PROP_STATUS, &status);
        if (err == PICL_PROPNOTFOUND)
                status = NULL;
        else if (err != PICL_SUCCESS)
                return (err);

        /*
         * Display the data
         */

        /* name */
        if (name != NULL) {
                log_printf("%-22s ", name);
                free(name);
        } else
                log_printf("%-22s ", "unknown");
        /* model */
        if (model != NULL) {
                log_printf("%-15s  ", model);
                free(model);
        } else
                log_printf("%-15s  ", "unknown");
        /* status */
        if (status == NULL)
                log_printf("%-15s  ", "okay");
        else {
                log_printf("%-15s  ", status);
                set_exit_code(status);
                free(status);
        }
        /* revision */
        log_printf("  %-4d\n",  id);

        return (PICL_WALK_CONTINUE);
}

/*
 * display asic revision id
 */
static int
display_hw_revisions(picl_nodehdl_t plafh)
{
        int     err;

        /* Print the header */
        logprintf_header(dgettext(TEXT_DOMAIN, "HW Revisions"),
            DEFAULT_LINE_WIDTH);

        log_printf("ASIC Revisions:\n");
        log_printf("-----------------------------");
        log_printf("--------------------------------------\n");
        log_printf("Path                   Device");
        log_printf("           Status             Revision\n");
        log_printf("-----------------------------");
        log_printf("--------------------------------------\n");

        err = picl_walk_tree_by_class(plafh, NULL, NULL, asicrev_callback);
        if (err != PICL_SUCCESS)
                return (err);

        err = picl_walk_tree_by_class(plafh, PICL_CLASS_EBUS,
            NULL, ebus_callback);
        if (err != PICL_SUCCESS)
                return (err);

        log_printf("\n");

        err = picl_walk_tree_by_class(plafh, PICL_CLASS_DISPLAY,
            NULL, ffbconfig_callback);
        return (err);
}

/*
 * find the options node and its powerfail_time prop
 * If found, display the list of latest powerfail.
 */
/* ARGSUSED */
static int
options_callback(picl_nodehdl_t nodeh, void *arg)
{
        time_t          value;
        char            *failtime;
        int             err;

        err = picldiag_get_string_propval(nodeh, PROP_POWERFAIL_TIME,
            &failtime);
        if (err == PICL_PROPNOTFOUND)
                return (PICL_WALK_TERMINATE);
        else if (err != PICL_SUCCESS)
                return (err);

        value = (time_t)atoi(failtime);
        free(failtime);
        if (value == 0)
                return (PICL_WALK_TERMINATE);

        log_printf(dgettext(TEXT_DOMAIN, "Most recent AC Power Failure:\n"));
        log_printf("=============================\n");
        log_printf("%s", ctime(&value));
        log_printf("\n");
        return (PICL_WALK_TERMINATE);
}

/*
 * display the OBP and POST prom revisions
 */
/* ARGSUSED */
static int
flashprom_callback(picl_nodehdl_t flashpromh, void *arg)
{
        picl_prophdl_t  proph;
        picl_prophdl_t  tblh;
        picl_prophdl_t  rowproph;
        picl_propinfo_t pinfo;
        char            *prom_version = NULL;
        char            *obp_version = NULL;
        int             err;

        err = picl_get_propinfo_by_name(flashpromh, OBP_PROP_VERSION,
            &pinfo, &proph);
        if (err == PICL_PROPNOTFOUND)
                return (PICL_WALK_TERMINATE);
        else if (err != PICL_SUCCESS)
                return (err);

        log_printf(dgettext(TEXT_DOMAIN, "System PROM revisions:\n"));
        log_printf("----------------------\n");

        /*
         * If it's a table prop, the first element is OBP revision
         * The second one is POST revision.
         * If it's a charstring prop, the value will be only OBP revision
         */
        if (pinfo.type == PICL_PTYPE_CHARSTRING) {
                prom_version = alloca(pinfo.size);
                if (prom_version == NULL)
                        return (PICL_FAILURE);
                err = picl_get_propval(proph, prom_version, pinfo.size);
                if (err != PICL_SUCCESS)
                        return (err);
                log_printf("%s\n", prom_version);
        }

        if (pinfo.type != PICL_PTYPE_TABLE)     /* not supported type */
                return (PICL_WALK_TERMINATE);

        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) {
                /* get first row */
                err = picl_get_propinfo(rowproph, &pinfo);
                if (err != PICL_SUCCESS)
                        return (err);

                prom_version = alloca(pinfo.size);
                if (prom_version == NULL)
                        return (PICL_FAILURE);

                err = picl_get_propval(rowproph, prom_version, pinfo.size);
                if (err != PICL_SUCCESS)
                        return (err);
                log_printf("%s\n", prom_version);

                /* get second row */
                err = picl_get_next_by_col(rowproph, &rowproph);
                if (err == PICL_SUCCESS) {
                        err = picl_get_propinfo(rowproph, &pinfo);
                        if (err != PICL_SUCCESS)
                                return (err);

                        obp_version = alloca(pinfo.size);
                        if (obp_version == NULL)
                                return (PICL_FAILURE);
                        err = picl_get_propval(rowproph, obp_version,
                            pinfo.size);
                        if (err != PICL_SUCCESS)
                                return (err);
                        log_printf("%s\n", obp_version);
                }
        }

        return (PICL_WALK_TERMINATE);
}

static int
display_system_info(int serrlog, int log_flag, picl_nodehdl_t rooth)
{
        int             err;
        picl_nodehdl_t plafh;
        picl_nodehdl_t frutreeh;

        err = picldiag_get_node_by_name(rooth, PICL_NODE_PLATFORM, &plafh);
        if (err != PICL_SUCCESS)
                return (err);

        if (!log_flag) {
                err = display_platform_banner(plafh);
                if (err != PICL_SUCCESS)
                        return (err);

                err = display_system_clock(plafh);
                if (err != PICL_SUCCESS)
                        return (err);

                err = picl_walk_tree_by_class(plafh, PICL_CLASS_MEMORY,
                    PICL_CLASS_MEMORY, memory_callback);
                if (err != PICL_SUCCESS)
                        return (err);

                err = display_cpu_info(plafh);
                if (err != PICL_SUCCESS)
                        return (err);

                err = display_io_device_info(plafh);
                if (err != PICL_SUCCESS)
                        return (err);

                err = display_memory_config(plafh);
                if (err != PICL_SUCCESS)
                        return (err);

                err = display_usb_devices(plafh);
                if (err != PICL_SUCCESS)
                        return (err);
        }

        if (serrlog) {
                err = picl_walk_tree_by_class(rooth, PICL_CLASS_OPTIONS,
                    NULL, options_callback);
                if (err != PICL_SUCCESS)
                        return (err);

                err = display_envctrl_status(plafh);
                if (err != PICL_SUCCESS)
                        return (err);

                err = picldiag_get_node_by_name(rooth, PICL_NODE_FRUTREE,
                    &frutreeh);
                if (err != PICL_SUCCESS)
                        return (err);

                err = display_fru_oper_status(frutreeh);
                if (err != PICL_SUCCESS)
                        return (err);

                err = display_hw_revisions(plafh);
                if (err != PICL_SUCCESS)
                        return (err);

                err = picl_walk_tree_by_class(plafh, PICL_CLASS_FLASHPROM,
                    NULL, flashprom_callback);
                if (err != PICL_SUCCESS)
                        return (err);
        }

        return (PICL_SUCCESS);
}

/*
 * do_prominfo is called from main in prtdiag. It returns PD_SYSTEM_FAILURE if
 * any system failure is detected, PD_INTERNAL_FAILURE for internal errors and
 * PD_SUCCESS otherwise. main uses the return value as the exit code.
 */
/* ARGSUSED */
int
do_prominfo(int serrlog, char *pgname, int log_flag, int prt_flag)
{
        int             err;
        char            *errstr;
        int             done;
        picl_nodehdl_t  rooth;

        err = picl_initialize();
        if (err != PICL_SUCCESS) {
                fprintf(stderr, EM_INIT_FAIL, picl_strerror(err));
                return (PD_INTERNAL_FAILURE);
        }

        do {
                done = 1;
                err = picl_get_root(&rooth);
                if (err != PICL_SUCCESS) {
                        fprintf(stderr, EM_GET_ROOT_FAIL, picl_strerror(err));
                        return (PD_INTERNAL_FAILURE);
                }

                err = display_system_info(serrlog, log_flag, rooth);

                if ((err == PICL_STALEHANDLE) || (err == PICL_INVALIDHANDLE))
                        done = 0;
        } while (!done);

        if (err != PICL_SUCCESS) {
                errstr = picl_strerror(err);
                fprintf(stderr, EM_PRTDIAG_FAIL);
                fprintf(stderr, "%s\n", errstr? errstr : " ");
                exit_code = PD_INTERNAL_FAILURE;
        }

        (void) picl_shutdown();

        return (exit_code);
}