root/usr/src/lib/libcpc/sparc/conf_ultra.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (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 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <sys/types.h>
#include <string.h>
#include <alloca.h>
#include <stdlib.h>
#include <stdio.h>
#include <libintl.h>
#include <libdevinfo.h>

#include "libcpc.h"
#include "libcpc_impl.h"

/*
 * Configuration data for UltraSPARC performance counters.
 *
 * Definitions taken from [1], [2], [3]  [4] and [5].  See the references to
 * understand what any of these settings actually means.
 *
 * Note that in the current draft of [2], there is some re-use
 * of existing bit assignments in the various fields of the %pcr
 * register - this may change before FCS.
 *
 * The following are the Internal Documents. Customers need to be
 * told about the Public docs in cpc_getcpuref().
 * [1] "UltraSPARC I & II User's Manual," January 1997.
 * [2] "UltraSPARC-III Programmer's Reference Manual," April 1999.
 * [3] "Cheetah+ Programmer's Reference Manual," November 2000.
 * [4] "UltraSPARC-IIIi Programmer's Reference Manual," November 2000.
 * [5] "UltraSPARC-IV+ Programmer's Reference Manual," October 2004.
 */

#define V_US12          (1u << 0)       /* specific to UltraSPARC 1 and 2 */
#define V_US3           (1u << 1)       /* specific to UltraSPARC 3 */
#define V_US3_PLUS      (1u << 2)       /* specific to UltraSPARC 3 PLUS */
#define V_US3_I         (1u << 3)       /* specific to UltraSPARC-IIIi */
#define V_US4_PLUS      (1u << 4)       /* specific to UltraSPARC-IV+ */
#define V_END           (1u << 31)

/*
 * map from "cpu version" to flag bits
 */
static const uint_t cpuvermap[] = {
        V_US12,                 /* CPC_ULTRA1 */
        V_US12,                 /* CPC_ULTRA2 */
        V_US3,                  /* CPC_ULTRA3 */
        V_US3_PLUS,             /* CPC_ULTRA3_PLUS */
        V_US3_I,                /* CPC_ULTRA3I */
        V_US4_PLUS              /* CPC_ULTRA4_PLUS */
};

struct nametable {
        const uint_t    ver;
        const uint8_t   bits;
        const char      *name;
};

/*
 * Definitions for counter 0
 */

#define USall_EVENTS_0(v)                                       \
        {v,             0x0,    "Cycle_cnt"},                   \
        {v,             0x1,    "Instr_cnt"},                   \
        {v,             0x2,    "Dispatch0_IC_miss"},           \
        {v,             0x8,    "IC_ref"},                      \
        {v,             0x9,    "DC_rd"},                       \
        {v,             0xa,    "DC_wr"},                       \
        {v,             0xc,    "EC_ref"},                      \
        {v,             0xe,    "EC_snoop_inv"}

static const struct nametable US12_names0[] = {
        USall_EVENTS_0(V_US12),
        {V_US12,        0x3,    "Dispatch0_storeBuf"},
        {V_US12,        0xb,    "Load_use"},
        {V_US12,        0xd,    "EC_write_hit_RDO"},
        {V_US12,        0xf,    "EC_rd_hit"},
        {V_END}
};

#define US3all_EVENTS_0(v)                                      \
        {v,             0x3,    "Dispatch0_br_target"},         \
        {v,             0x4,    "Dispatch0_2nd_br"},            \
        {v,             0x5,    "Rstall_storeQ"},               \
        {v,             0x6,    "Rstall_IU_use"},               \
        {v,             0xd,    "EC_write_hit_RTO"},            \
        {v,             0xf,    "EC_rd_miss"},                  \
        {v,             0x10,   "PC_port0_rd"},                 \
        {v,             0x11,   "SI_snoop"},                    \
        {v,             0x12,   "SI_ciq_flow"},                 \
        {v,             0x13,   "SI_owned"},                    \
        {v,             0x14,   "SW_count_0"},                  \
        {v,             0x15,   "IU_Stat_Br_miss_taken"},       \
        {v,             0x16,   "IU_Stat_Br_count_taken"},      \
        {v,             0x17,   "Dispatch_rs_mispred"},         \
        {v,             0x18,   "FA_pipe_completion"}

#define US3_MC_EVENTS_0(v)                                      \
        {v,             0x20,   "MC_reads_0"},                  \
        {v,             0x21,   "MC_reads_1"},                  \
        {v,             0x22,   "MC_reads_2"},                  \
        {v,             0x23,   "MC_reads_3"},                  \
        {v,             0x24,   "MC_stalls_0"},                 \
        {v,             0x25,   "MC_stalls_2"}

#define US3_I_MC_EVENTS_0(v)                                    \
        {v,             0x20,   "MC_read_dispatched"},          \
        {v,             0x21,   "MC_write_dispatched"},         \
        {v,             0x22,   "MC_read_returned_to_JBU"},     \
        {v,             0x23,   "MC_msl_busy_stall"},           \
        {v,             0x24,   "MC_mdb_overflow_stall"},       \
        {v,             0x25,   "MC_miu_spec_request"}

static const struct nametable US3_names0[] = {
        USall_EVENTS_0(V_US3),
        US3all_EVENTS_0(V_US3),
        US3_MC_EVENTS_0(V_US3),
        {V_END}
};

static const struct nametable US4_PLUS_names0[] = {
        {V_US4_PLUS,    0x0,   "Cycle_cnt"},
        {V_US4_PLUS,    0x1,   "Instr_cnt"},
        {V_US4_PLUS,    0x2,   "Dispatch0_IC_miss"},
        {V_US4_PLUS,    0x3,   "IU_stat_jmp_correct_pred"},
        {V_US4_PLUS,    0x4,   "Dispatch0_2nd_br"},
        {V_US4_PLUS,    0x5,   "Rstall_storeQ"},
        {V_US4_PLUS,    0x6,   "Rstall_IU_use"},
        {V_US4_PLUS,    0x7,   "IU_stat_ret_correct_pred"},
        {V_US4_PLUS,    0x8,   "IC_ref"},
        {V_US4_PLUS,    0x9,   "DC_rd"},
        {V_US4_PLUS,    0xa,   "Rstall_FP_use"},
        {V_US4_PLUS,    0xb,   "SW_pf_instr"},
        {V_US4_PLUS,    0xc,   "L2_ref"},
        {V_US4_PLUS,    0xd,   "L2_write_hit_RTO"},
        {V_US4_PLUS,    0xe,   "L2_snoop_inv_sh"},
        {V_US4_PLUS,    0xf,   "L2_rd_miss"},
        {V_US4_PLUS,    0x10,  "PC_rd"},
        {V_US4_PLUS,    0x11,  "SI_snoop_sh"},
        {V_US4_PLUS,    0x12,  "SI_ciq_flow_sh"},
        {V_US4_PLUS,    0x13,  "Re_DC_miss"},
        {V_US4_PLUS,    0x14,  "SW_count_NOP"},
        {V_US4_PLUS,    0x15,  "IU_stat_br_miss_taken"},
        {V_US4_PLUS,    0x16,  "IU_stat_br_count_untaken"},
        {V_US4_PLUS,    0x17,  "HW_pf_exec"},
        {V_US4_PLUS,    0x18,  "FA_pipe_completion"},
        {V_US4_PLUS,    0x19,  "SSM_L3_wb_remote"},
        {V_US4_PLUS,    0x1a,  "SSM_L3_miss_local"},
        {V_US4_PLUS,    0x1b,  "SSM_L3_miss_mtag_remote"},
        {V_US4_PLUS,    0x1c,  "SW_pf_str_trapped"},
        {V_US4_PLUS,    0x1d,  "SW_pf_PC_installed"},
        {V_US4_PLUS,    0x1e,  "IPB_to_IC_fill"},
        {V_US4_PLUS,    0x1f,  "L2_write_miss"},
        {V_US4_PLUS,    0x20,  "MC_reads_0_sh"},
        {V_US4_PLUS,    0x21,  "MC_reads_1_sh"},
        {V_US4_PLUS,    0x22,  "MC_reads_2_sh"},
        {V_US4_PLUS,    0x23,  "MC_reads_3_sh"},
        {V_US4_PLUS,    0x24,  "MC_stalls_0_sh"},
        {V_US4_PLUS,    0x25,  "MC_stalls_2_sh"},
        {V_US4_PLUS,    0x26,  "L2_hit_other_half"},
        {V_US4_PLUS,    0x28,  "L3_rd_miss"},
        {V_US4_PLUS,    0x29,  "Re_L2_miss"},
        {V_US4_PLUS,    0x2a,  "IC_miss_cancelled"},
        {V_US4_PLUS,    0x2b,  "DC_wr_miss"},
        {V_US4_PLUS,    0x2c,  "L3_hit_I_state_sh"},
        {V_US4_PLUS,    0x2d,  "SI_RTS_src_data"},
        {V_US4_PLUS,    0x2e,  "L2_IC_miss"},
        {V_US4_PLUS,    0x2f,  "SSM_new_transaction_sh"},
        {V_US4_PLUS,    0x30,  "L2_SW_pf_miss"},
        {V_US4_PLUS,    0x31,  "L2_wb"},
        {V_US4_PLUS,    0x32,  "L2_wb_sh"},
        {V_US4_PLUS,    0x33,  "L2_snoop_cb_sh"},
        {V_END}
};

static const struct nametable US3_PLUS_names0[] = {
        USall_EVENTS_0(V_US3_PLUS),
        US3all_EVENTS_0(V_US3_PLUS),
        US3_MC_EVENTS_0(V_US3_PLUS),
        {V_US3_PLUS,    0x19,   "EC_wb_remote"},
        {V_US3_PLUS,    0x1a,   "EC_miss_local"},
        {V_US3_PLUS,    0x1b,   "EC_miss_mtag_remote"},
        {V_END}
};

static const struct nametable US3_I_names0[] = {
        USall_EVENTS_0(V_US3_I),
        US3all_EVENTS_0(V_US3_I),
        US3_I_MC_EVENTS_0(V_US3_I),
        {V_US3_PLUS,    0x19,   "EC_wb_remote"},
        {V_US3_PLUS,    0x1a,   "EC_miss_local"},
        {V_US3_PLUS,    0x1b,   "EC_miss_mtag_remote"},
        {V_END}
};

#undef  USall_EVENTS_0
#undef  US3all_EVENTS_0

#define USall_EVENTS_1(v)                                       \
        {v,             0x0,    "Cycle_cnt"},                   \
        {v,             0x1,    "Instr_cnt"},                   \
        {v,             0x2,    "Dispatch0_mispred"},           \
        {v,             0xd,    "EC_wb"},                       \
        {v,             0xe,    "EC_snoop_cb"}

static const struct nametable US12_names1[] = {
        USall_EVENTS_1(V_US12),
        {V_US12,        0x3,    "Dispatch0_FP_use"},
        {V_US12,        0x8,    "IC_hit"},
        {V_US12,        0x9,    "DC_rd_hit"},
        {V_US12,        0xa,    "DC_wr_hit"},
        {V_US12,        0xb,    "Load_use_RAW"},
        {V_US12,        0xc,    "EC_hit"},
        {V_US12,        0xf,    "EC_ic_hit"},
        {V_END}
};

#define US3all_EVENTS_1(v)                                      \
        {v,             0x3,    "IC_miss_cancelled"},           \
        {v,             0x5,    "Re_FPU_bypass"},               \
        {v,             0x6,    "Re_DC_miss"},                  \
        {v,             0x7,    "Re_EC_miss"},                  \
        {v,             0x8,    "IC_miss"},                     \
        {v,             0x9,    "DC_rd_miss"},                  \
        {v,             0xa,    "DC_wr_miss"},                  \
        {v,             0xb,    "Rstall_FP_use"},               \
        {v,             0xc,    "EC_misses"},                   \
        {v,             0xf,    "EC_ic_miss"},                  \
        {v,             0x10,   "Re_PC_miss"},                  \
        {v,             0x11,   "ITLB_miss"},                   \
        {v,             0x12,   "DTLB_miss"},                   \
        {v,             0x13,   "WC_miss"},                     \
        {v,             0x14,   "WC_snoop_cb"},                 \
        {v,             0x15,   "WC_scrubbed"},                 \
        {v,             0x16,   "WC_wb_wo_read"},               \
        {v,             0x18,   "PC_soft_hit"},                 \
        {v,             0x19,   "PC_snoop_inv"},                \
        {v,             0x1a,   "PC_hard_hit"},                 \
        {v,             0x1b,   "PC_port1_rd"},                 \
        {v,             0x1c,   "SW_count_1"},                  \
        {v,             0x1d,   "IU_Stat_Br_miss_untaken"},     \
        {v,             0x1e,   "IU_Stat_Br_count_untaken"},    \
        {v,             0x1f,   "PC_MS_misses"},                \
        {v,             0x26,   "Re_RAW_miss"},                 \
        {v,             0x27,   "FM_pipe_completion"}

#define US3_MC_EVENTS_1(v)                                      \
        {v,             0x20,   "MC_writes_0"},                 \
        {v,             0x21,   "MC_writes_1"},                 \
        {v,             0x22,   "MC_writes_2"},                 \
        {v,             0x23,   "MC_writes_3"},                 \
        {v,             0x24,   "MC_stalls_1"},                 \
        {v,             0x25,   "MC_stalls_3"}

#define US3_I_MC_EVENTS_1(v)                                    \
        {v,             0x20,   "MC_open_bank_cmds"},           \
        {v,             0x21,   "MC_reads"},                    \
        {v,             0x22,   "MC_writes"},                   \
        {v,             0x23,   "MC_page_close_stall"}

static const struct nametable US3_names1[] = {
        USall_EVENTS_1(V_US3),
        US3all_EVENTS_1(V_US3),
        US3_MC_EVENTS_1(V_US3),
        {V_US3,         0x4,    "Re_endian_miss"},
        {V_END}
};

static const struct nametable US3_PLUS_names1[] = {
        USall_EVENTS_1(V_US3_PLUS),
        US3all_EVENTS_1(V_US3_PLUS),
        US3_MC_EVENTS_1(V_US3_PLUS),
        {V_US3_PLUS,    0x4,    "Re_DC_missovhd"},
        {V_US3_PLUS,    0x28,   "EC_miss_mtag_remote"},
        {V_US3_PLUS,    0x29,   "EC_miss_remote"},
        {V_END}
};

static const struct nametable US3_I_names1[] = {
        USall_EVENTS_1(V_US3_I),
        US3all_EVENTS_1(V_US3_I),
        US3_I_MC_EVENTS_1(V_US3_I),
        {V_US3_I,       0x4,    "Re_DC_missovhd"},
        {V_END}
};

static const struct nametable US4_PLUS_names1[] = {
        {V_US4_PLUS,    0x0,   "Cycle_cnt"},
        {V_US4_PLUS,    0x1,   "Instr_cnt"},
        {V_US4_PLUS,    0x2,   "Dispatch0_other"},
        {V_US4_PLUS,    0x3,   "DC_wr"},
        {V_US4_PLUS,    0x4,   "Re_DC_missovhd"},
        {V_US4_PLUS,    0x5,   "Re_FPU_bypass"},
        {V_US4_PLUS,    0x6,   "L3_write_hit_RTO"},
        {V_US4_PLUS,    0x7,   "L2L3_snoop_inv_sh"},
        {V_US4_PLUS,    0x8,   "IC_L2_req"},
        {V_US4_PLUS,    0x9,   "DC_rd_miss"},
        {V_US4_PLUS,    0xa,   "L2_hit_I_state_sh"},
        {V_US4_PLUS,    0xb,   "L3_write_miss_RTO"},
        {V_US4_PLUS,    0xc,   "L2_miss"},
        {V_US4_PLUS,    0xd,   "SI_owned_sh"},
        {V_US4_PLUS,    0xe,   "SI_RTO_src_data"},
        {V_US4_PLUS,    0xf,   "SW_pf_duplicate"},
        {V_US4_PLUS,    0x10,  "IU_stat_jmp_mispred"},
        {V_US4_PLUS,    0x11,  "ITLB_miss"},
        {V_US4_PLUS,    0x12,  "DTLB_miss"},
        {V_US4_PLUS,    0x13,  "WC_miss"},
        {V_US4_PLUS,    0x14,  "IC_fill"},
        {V_US4_PLUS,    0x15,  "IU_stat_ret_mispred"},
        {V_US4_PLUS,    0x16,  "Re_L3_miss"},
        {V_US4_PLUS,    0x17,  "Re_PFQ_full"},
        {V_US4_PLUS,    0x18,  "PC_soft_hit"},
        {V_US4_PLUS,    0x19,  "PC_inv"},
        {V_US4_PLUS,    0x1a,  "PC_hard_hit"},
        {V_US4_PLUS,    0x1b,  "IC_pf"},
        {V_US4_PLUS,    0x1c,  "SW_count_NOP"},
        {V_US4_PLUS,    0x1d,  "IU_stat_br_miss_untaken"},
        {V_US4_PLUS,    0x1e,  "IU_stat_br_count_taken"},
        {V_US4_PLUS,    0x1f,  "PC_miss"},
        {V_US4_PLUS,    0x20,  "MC_writes_0_sh"},
        {V_US4_PLUS,    0x21,  "MC_writes_1_sh"},
        {V_US4_PLUS,    0x22,  "MC_writes_2_sh"},
        {V_US4_PLUS,    0x23,  "MC_writes_3_sh"},
        {V_US4_PLUS,    0x24,  "MC_stalls_1_sh"},
        {V_US4_PLUS,    0x25,  "MC_stalls_3_sh"},
        {V_US4_PLUS,    0x26,  "Re_RAW_miss"},
        {V_US4_PLUS,    0x27,  "FM_pipe_completion"},
        {V_US4_PLUS,    0x28,  "SSM_L3_miss_mtag_remote"},
        {V_US4_PLUS,    0x29,  "SSM_L3_miss_remote"},
        {V_US4_PLUS,    0x2a,  "SW_pf_exec"},
        {V_US4_PLUS,    0x2b,  "SW_pf_str_exec"},
        {V_US4_PLUS,    0x2c,  "SW_pf_dropped"},
        {V_US4_PLUS,    0x2d,  "SW_pf_L2_installed"},
        {V_US4_PLUS,    0x2f,  "L2_HW_pf_miss"},
        {V_US4_PLUS,    0x31,  "L3_miss"},
        {V_US4_PLUS,    0x32,  "L3_IC_miss"},
        {V_US4_PLUS,    0x33,  "L3_SW_pf_miss"},
        {V_US4_PLUS,    0x34,  "L3_hit_other_half"},
        {V_US4_PLUS,    0x35,  "L3_wb"},
        {V_US4_PLUS,    0x36,  "L3_wb_sh"},
        {V_US4_PLUS,    0x37,  "L2L3_snoop_cb_sh"},
        {V_END}
};

#undef  USall_EVENTS_1
#undef  US3all_EVENTS_1

static const struct nametable *US12_names[2] = {
        US12_names0,
        US12_names1
};

static const struct nametable *US3_names[2] = {
        US3_names0,
        US3_names1
};

static const struct nametable *US3_PLUS_names[2] = {
        US3_PLUS_names0,
        US3_PLUS_names1
};

static const struct nametable *US3_I_names[2] = {
        US3_I_names0,
        US3_I_names1
};

static const struct nametable *US4_PLUS_names[2] = {
        US4_PLUS_names0,
        US4_PLUS_names1
};

#define MAPCPUVER(cpuver)       (cpuvermap[(cpuver) - CPC_ULTRA1])

static int
validargs(int cpuver, int regno)
{
        if (regno < 0 || regno > 1)
                return (0);
        cpuver -= CPC_ULTRA1;
        if (cpuver < 0 ||
            cpuver >= sizeof (cpuvermap) / sizeof (cpuvermap[0]))
                return (0);
        return (1);
}

/*ARGSUSED*/
static int
versionmatch(int cpuver, int regno, const struct nametable *n)
{
        if (!validargs(cpuver, regno) || n->ver != MAPCPUVER(cpuver))
                return (0);
        return (1);
}

static const struct nametable *
getnametable(int cpuver, int regno)
{
        const struct nametable *n;

        if (!validargs(cpuver, regno))
                return (NULL);

        switch (MAPCPUVER(cpuver)) {
        case V_US12:
                n = US12_names[regno];
                break;
        case V_US3:
                n = US3_names[regno];
                break;
        case V_US3_PLUS:
                n = US3_PLUS_names[regno];
                break;
        case V_US3_I:
                n = US3_I_names[regno];
                break;
        case V_US4_PLUS:
                n = US4_PLUS_names[regno];
                break;
        default:
                n = NULL;
                break;
        }
        return (n);
}

void
cpc_walk_names(int cpuver, int regno, void *arg,
    void (*action)(void *, int, const char *, uint8_t))
{
        const struct nametable *n;

        if ((n = getnametable(cpuver, regno)) == NULL)
                return;
        for (; n->ver != V_END; n++)
                if (versionmatch(cpuver, regno, n))
                        action(arg, regno, n->name, n->bits);
}

const char *
__cpc_reg_to_name(int cpuver, int regno, uint8_t bits)
{
        const struct nametable *n;

        if ((n = getnametable(cpuver, regno)) == NULL)
                return (NULL);
        for (; n->ver != V_END; n++)
                if (bits == n->bits && versionmatch(cpuver, regno, n))
                        return (n->name);
        return (NULL);
}

/*
 * Register names can be specified as strings or even as numbers
 */
int
__cpc_name_to_reg(int cpuver, int regno, const char *name, uint8_t *bits)
{
        const struct nametable *n;
        char *eptr = NULL;
        long value;

        if ((n = getnametable(cpuver, regno)) == NULL || name == NULL)
                return (-1);

        for (; n->ver != V_END; n++)
                if (strcmp(name, n->name) == 0 &&
                    versionmatch(cpuver, regno, n)) {
                        *bits = n->bits;
                        return (0);
                }

        value = strtol(name, &eptr, 0);
        if (name != eptr && value >= 0 && value <= UINT8_MAX) {
                *bits = (uint8_t)value;
                return (0);
        }

        return (-1);
}

const char *
cpc_getcciname(int cpuver)
{
        if (validargs(cpuver, 0))
                switch (MAPCPUVER(cpuver)) {
                case V_US12:
                        return ("UltraSPARC I&II");
                case V_US3:
                        return ("UltraSPARC III");
                case V_US3_PLUS:
                        return ("UltraSPARC III+ & IV");
                case V_US3_I:
                        return ("UltraSPARC IIIi & IIIi+");
                case V_US4_PLUS:
                        return ("UltraSPARC IV+");
                default:
                        break;
                }
        return (NULL);
}

#define CPU_REF_URL " Documentation for Sun processors can be found at: " \
                        "http://www.sun.com/processors/manuals"

const char *
cpc_getcpuref(int cpuver)
{
        if (validargs(cpuver, 0))
                switch (MAPCPUVER(cpuver)) {
                case V_US12:
                        return (gettext(
                            "See the \"UltraSPARC I/II User\'s Manual\" "
                            "(Part No. 802-7220-02) "
                            "for descriptions of these events." CPU_REF_URL));
                case V_US3:
                case V_US3_PLUS:
                        return (gettext(
                            "See the \"UltraSPARC III Cu User's Manual\" "
                            "for descriptions of these events." CPU_REF_URL));
                case V_US3_I:
                        return (gettext(
                            "See the \"UltraSPARC IIIi User's Manual\"  "
                            "for descriptions of these events." CPU_REF_URL));
                case V_US4_PLUS:
                        return (gettext(
                            "See the \"UltraSPARC IV User's Manual"
                            "Supplement\"  "
                            "for descriptions of these events." CPU_REF_URL));
                default:
                        break;
                }
        return (NULL);
}

/*
 * This is a functional interface to allow CPUs with fewer %pic registers
 * to share the same data structure as those with more %pic registers
 * within the same instruction family.
 */
uint_t
cpc_getnpic(int cpuver)
{
        /*LINTED*/
        cpc_event_t *event;

        switch (cpuver) {
        case CPC_ULTRA1:
        case CPC_ULTRA2:
        case CPC_ULTRA3:
        case CPC_ULTRA3_PLUS:
        case CPC_ULTRA3_I:
        case CPC_ULTRA4_PLUS:
                return (sizeof (event->ce_pic) / sizeof (event->ce_pic[0]));
        default:
                return (0);
        }
}

/*
 * Compares the given string against the list of all known CPU node names, and
 * returns the CPC CPU version code if there is a match. If there is no match,
 * returns -1.
 */
static int
node2ver(char *node)
{
        if (strcmp(node, "SUNW,UltraSPARC") == 0 ||
            strcmp(node, "SUNW,UltraSPARC-II") == 0 ||
            strcmp(node, "SUNW,UltraSPARC-IIi") == 0 ||
            strcmp(node, "SUNW,UltraSPARC-IIe") == 0) {
                return (CPC_ULTRA1);
        } else if (strcmp(node, "SUNW,UltraSPARC-III") == 0)
                return (CPC_ULTRA3);
        else if (strcmp(node, "SUNW,UltraSPARC-III+") == 0 ||
            strcmp(node, "SUNW,UltraSPARC-IV") == 0)
                return (CPC_ULTRA3_PLUS);
        else if (strcmp(node, "SUNW,UltraSPARC-IIIi") == 0 ||
            strcmp(node, "SUNW,UltraSPARC-IIIi+") == 0)
                return (CPC_ULTRA3_I);
        else if (strcmp(node, "SUNW,UltraSPARC-IV+") == 0)
                return (CPC_ULTRA4_PLUS);

        return (-1);
}

static int
cpc_get_cpu_ver(di_node_t di_node, void *arg)
{
        char            *node_name, *compatible_array;
        int             n_names, i, found = 0;
        int             *ver = arg;

        node_name = di_node_name(di_node);
        if (node_name != NULL) {
                if ((*ver = node2ver(node_name)) != -1)
                        found = 1;
                else if (strncmp(node_name, "cpu", 4) == 0) {
                        /*
                         * CPU nodes associated with CMP use the generic name
                         * of "cpu".  We must look at the compatible property
                         * in order to find the implementation specific name.
                         */
                        if ((n_names = di_compatible_names(di_node,
                            &compatible_array)) > 0) {
                                for (i = 0; i < n_names; i++) {
                                        if ((*ver = node2ver(compatible_array))
                                            != -1) {
                                                found = 1;
                                                break;
                                        }
                                        compatible_array +=
                                            strlen(compatible_array) + 1;
                                }
                        }
                }
        }

        if (found == 0)
                return (DI_WALK_CONTINUE);

        return (DI_WALK_TERMINATE);
}

/*
 * Return the version of the current processor.
 *
 * Version -1 is defined as 'not performance counter capable'
 *
 * XXX  A better solution would be to use the di_prom_props for the cpu
 * devinfo nodes. That way we could look at the 'device-type', 'sparc-version'
 * and 'implementation#' properties in order to determine which version of
 * UltraSPARC we are running on.
 *
 * The problem with this is that di_prom_init() requires root access to
 * open /dev/openprom and cputrack is not a root-only application so
 * we have to settle for the di_props that we can see as non-root users.
 */
int
cpc_getcpuver(void)
{
        static int ver = -1;

        if (ver == -1) {
                di_node_t       di_root_node;

                if ((di_root_node = di_init("/", DINFOCPYALL)) == DI_NODE_NIL)
                        return (-1);

                (void) di_walk_node(di_root_node, DI_WALK_CLDFIRST,
                        (void *)&ver, cpc_get_cpu_ver);

                di_fini(di_root_node);
        }
        return (ver);
}