/*
* 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.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#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);
}