Home | History | Annotate | Download | only in sparc
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License, Version 1.0 only
      6  * (the "License").  You may not use this file except in compliance
      7  * with the License.
      8  *
      9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10  * or http://www.opensolaris.org/os/licensing.
     11  * See the License for the specific language governing permissions
     12  * and limitations under the License.
     13  *
     14  * When distributing Covered Code, include this CDDL HEADER in each
     15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16  * If applicable, add the following below this CDDL HEADER, with the
     17  * fields enclosed by brackets "[]" replaced with your own identifying
     18  * information: Portions Copyright [yyyy] [name of copyright owner]
     19  *
     20  * CDDL HEADER END
     21  */
     22 /*
     23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     28 
     29 #include <sys/types.h>
     30 #include <string.h>
     31 #include <alloca.h>
     32 #include <stdlib.h>
     33 #include <stdio.h>
     34 #include <libintl.h>
     35 #include <libdevinfo.h>
     36 
     37 #include "libcpc.h"
     38 #include "libcpc_impl.h"
     39 
     40 /*
     41  * Configuration data for UltraSPARC performance counters.
     42  *
     43  * Definitions taken from [1], [2], [3]  [4] and [5].  See the references to
     44  * understand what any of these settings actually means.
     45  *
     46  * Note that in the current draft of [2], there is some re-use
     47  * of existing bit assignments in the various fields of the %pcr
     48  * register - this may change before FCS.
     49  *
     50  * The following are the Internal Documents. Customers need to be
     51  * told about the Public docs in cpc_getcpuref().
     52  * [1] "UltraSPARC I & II User's Manual," January 1997.
     53  * [2] "UltraSPARC-III Programmer's Reference Manual," April 1999.
     54  * [3] "Cheetah+ Programmer's Reference Manual," November 2000.
     55  * [4] "UltraSPARC-IIIi Programmer's Reference Manual," November 2000.
     56  * [5] "UltraSPARC-IV+ Programmer's Reference Manual," October 2004.
     57  */
     58 
     59 #define	V_US12		(1u << 0)	/* specific to UltraSPARC 1 and 2 */
     60 #define	V_US3		(1u << 1)	/* specific to UltraSPARC 3 */
     61 #define	V_US3_PLUS	(1u << 2)	/* specific to UltraSPARC 3 PLUS */
     62 #define	V_US3_I		(1u << 3)	/* specific to UltraSPARC-IIIi */
     63 #define	V_US4_PLUS	(1u << 4)	/* specific to UltraSPARC-IV+ */
     64 #define	V_END		(1u << 31)
     65 
     66 /*
     67  * map from "cpu version" to flag bits
     68  */
     69 static const uint_t cpuvermap[] = {
     70 	V_US12,			/* CPC_ULTRA1 */
     71 	V_US12,			/* CPC_ULTRA2 */
     72 	V_US3,			/* CPC_ULTRA3 */
     73 	V_US3_PLUS,		/* CPC_ULTRA3_PLUS */
     74 	V_US3_I,		/* CPC_ULTRA3I */
     75 	V_US4_PLUS		/* CPC_ULTRA4_PLUS */
     76 };
     77 
     78 struct nametable {
     79 	const uint_t	ver;
     80 	const uint8_t	bits;
     81 	const char	*name;
     82 };
     83 
     84 /*
     85  * Definitions for counter 0
     86  */
     87 
     88 #define	USall_EVENTS_0(v)					\
     89 	{v,		0x0,	"Cycle_cnt"},			\
     90 	{v,		0x1,	"Instr_cnt"},			\
     91 	{v,		0x2,	"Dispatch0_IC_miss"},		\
     92 	{v,		0x8,	"IC_ref"},			\
     93 	{v,		0x9,	"DC_rd"},			\
     94 	{v,		0xa,	"DC_wr"},			\
     95 	{v,		0xc,	"EC_ref"},			\
     96 	{v,		0xe,	"EC_snoop_inv"}
     97 
     98 static const struct nametable US12_names0[] = {
     99 	USall_EVENTS_0(V_US12),
    100 	{V_US12,	0x3,	"Dispatch0_storeBuf"},
    101 	{V_US12,	0xb,	"Load_use"},
    102 	{V_US12,	0xd,	"EC_write_hit_RDO"},
    103 	{V_US12,	0xf,	"EC_rd_hit"},
    104 	{V_END}
    105 };
    106 
    107 #define	US3all_EVENTS_0(v)					\
    108 	{v,		0x3,	"Dispatch0_br_target"},		\
    109 	{v,		0x4,	"Dispatch0_2nd_br"},		\
    110 	{v,		0x5,	"Rstall_storeQ"},		\
    111 	{v,		0x6,	"Rstall_IU_use"},		\
    112 	{v,		0xd,	"EC_write_hit_RTO"},		\
    113 	{v,		0xf,	"EC_rd_miss"},			\
    114 	{v,		0x10,	"PC_port0_rd"},			\
    115 	{v,		0x11,	"SI_snoop"},			\
    116 	{v,		0x12,	"SI_ciq_flow"},			\
    117 	{v,		0x13,	"SI_owned"},			\
    118 	{v,		0x14,	"SW_count_0"},			\
    119 	{v,		0x15,	"IU_Stat_Br_miss_taken"},	\
    120 	{v,		0x16,	"IU_Stat_Br_count_taken"},	\
    121 	{v,		0x17,	"Dispatch_rs_mispred"},		\
    122 	{v,		0x18,	"FA_pipe_completion"}
    123 
    124 #define	US3_MC_EVENTS_0(v)					\
    125 	{v,		0x20,	"MC_reads_0"},			\
    126 	{v,		0x21,	"MC_reads_1"},			\
    127 	{v,		0x22,	"MC_reads_2"},			\
    128 	{v,		0x23,	"MC_reads_3"},			\
    129 	{v,		0x24,	"MC_stalls_0"},			\
    130 	{v,		0x25,	"MC_stalls_2"}
    131 
    132 #define	US3_I_MC_EVENTS_0(v)					\
    133 	{v,		0x20,	"MC_read_dispatched"},		\
    134 	{v,		0x21,	"MC_write_dispatched"},		\
    135 	{v,		0x22,	"MC_read_returned_to_JBU"},	\
    136 	{v,		0x23,	"MC_msl_busy_stall"},		\
    137 	{v,		0x24,	"MC_mdb_overflow_stall"},	\
    138 	{v,		0x25,	"MC_miu_spec_request"}
    139 
    140 static const struct nametable US3_names0[] = {
    141 	USall_EVENTS_0(V_US3),
    142 	US3all_EVENTS_0(V_US3),
    143 	US3_MC_EVENTS_0(V_US3),
    144 	{V_END}
    145 };
    146 
    147 static const struct nametable US4_PLUS_names0[] = {
    148 	{V_US4_PLUS,	0x0,   "Cycle_cnt"},
    149 	{V_US4_PLUS,	0x1,   "Instr_cnt"},
    150 	{V_US4_PLUS,	0x2,   "Dispatch0_IC_miss"},
    151 	{V_US4_PLUS,	0x3,   "IU_stat_jmp_correct_pred"},
    152 	{V_US4_PLUS,	0x4,   "Dispatch0_2nd_br"},
    153 	{V_US4_PLUS,	0x5,   "Rstall_storeQ"},
    154 	{V_US4_PLUS,	0x6,   "Rstall_IU_use"},
    155 	{V_US4_PLUS,	0x7,   "IU_stat_ret_correct_pred"},
    156 	{V_US4_PLUS,	0x8,   "IC_ref"},
    157 	{V_US4_PLUS,	0x9,   "DC_rd"},
    158 	{V_US4_PLUS,	0xa,   "Rstall_FP_use"},
    159 	{V_US4_PLUS,	0xb,   "SW_pf_instr"},
    160 	{V_US4_PLUS,	0xc,   "L2_ref"},
    161 	{V_US4_PLUS,	0xd,   "L2_write_hit_RTO"},
    162 	{V_US4_PLUS,	0xe,   "L2_snoop_inv_sh"},
    163 	{V_US4_PLUS,	0xf,   "L2_rd_miss"},
    164 	{V_US4_PLUS,	0x10,  "PC_rd"},
    165 	{V_US4_PLUS,	0x11,  "SI_snoop_sh"},
    166 	{V_US4_PLUS,	0x12,  "SI_ciq_flow_sh"},
    167 	{V_US4_PLUS,	0x13,  "Re_DC_miss"},
    168 	{V_US4_PLUS,	0x14,  "SW_count_NOP"},
    169 	{V_US4_PLUS,	0x15,  "IU_stat_br_miss_taken"},
    170 	{V_US4_PLUS,	0x16,  "IU_stat_br_count_untaken"},
    171 	{V_US4_PLUS,	0x17,  "HW_pf_exec"},
    172 	{V_US4_PLUS,	0x18,  "FA_pipe_completion"},
    173 	{V_US4_PLUS,	0x19,  "SSM_L3_wb_remote"},
    174 	{V_US4_PLUS,	0x1a,  "SSM_L3_miss_local"},
    175 	{V_US4_PLUS,	0x1b,  "SSM_L3_miss_mtag_remote"},
    176 	{V_US4_PLUS,	0x1c,  "SW_pf_str_trapped"},
    177 	{V_US4_PLUS,	0x1d,  "SW_pf_PC_installed"},
    178 	{V_US4_PLUS,	0x1e,  "IPB_to_IC_fill"},
    179 	{V_US4_PLUS,	0x1f,  "L2_write_miss"},
    180 	{V_US4_PLUS,	0x20,  "MC_reads_0_sh"},
    181 	{V_US4_PLUS,	0x21,  "MC_reads_1_sh"},
    182 	{V_US4_PLUS,	0x22,  "MC_reads_2_sh"},
    183 	{V_US4_PLUS,	0x23,  "MC_reads_3_sh"},
    184 	{V_US4_PLUS,	0x24,  "MC_stalls_0_sh"},
    185 	{V_US4_PLUS,	0x25,  "MC_stalls_2_sh"},
    186 	{V_US4_PLUS,	0x26,  "L2_hit_other_half"},
    187 	{V_US4_PLUS,	0x28,  "L3_rd_miss"},
    188 	{V_US4_PLUS,	0x29,  "Re_L2_miss"},
    189 	{V_US4_PLUS,	0x2a,  "IC_miss_cancelled"},
    190 	{V_US4_PLUS,	0x2b,  "DC_wr_miss"},
    191 	{V_US4_PLUS,	0x2c,  "L3_hit_I_state_sh"},
    192 	{V_US4_PLUS,	0x2d,  "SI_RTS_src_data"},
    193 	{V_US4_PLUS,	0x2e,  "L2_IC_miss"},
    194 	{V_US4_PLUS,	0x2f,  "SSM_new_transaction_sh"},
    195 	{V_US4_PLUS,	0x30,  "L2_SW_pf_miss"},
    196 	{V_US4_PLUS,	0x31,  "L2_wb"},
    197 	{V_US4_PLUS,	0x32,  "L2_wb_sh"},
    198 	{V_US4_PLUS,	0x33,  "L2_snoop_cb_sh"},
    199 	{V_END}
    200 };
    201 
    202 static const struct nametable US3_PLUS_names0[] = {
    203 	USall_EVENTS_0(V_US3_PLUS),
    204 	US3all_EVENTS_0(V_US3_PLUS),
    205 	US3_MC_EVENTS_0(V_US3_PLUS),
    206 	{V_US3_PLUS,	0x19,	"EC_wb_remote"},
    207 	{V_US3_PLUS,	0x1a,	"EC_miss_local"},
    208 	{V_US3_PLUS,	0x1b,	"EC_miss_mtag_remote"},
    209 	{V_END}
    210 };
    211 
    212 static const struct nametable US3_I_names0[] = {
    213 	USall_EVENTS_0(V_US3_I),
    214 	US3all_EVENTS_0(V_US3_I),
    215 	US3_I_MC_EVENTS_0(V_US3_I),
    216 	{V_US3_PLUS,	0x19,	"EC_wb_remote"},
    217 	{V_US3_PLUS,	0x1a,	"EC_miss_local"},
    218 	{V_US3_PLUS,	0x1b,	"EC_miss_mtag_remote"},
    219 	{V_END}
    220 };
    221 
    222 #undef	USall_EVENTS_0
    223 #undef	US3all_EVENTS_0
    224 
    225 #define	USall_EVENTS_1(v)					\
    226 	{v,		0x0,	"Cycle_cnt"},			\
    227 	{v,		0x1,	"Instr_cnt"},			\
    228 	{v,		0x2,	"Dispatch0_mispred"},		\
    229 	{v,		0xd,	"EC_wb"},			\
    230 	{v,		0xe,	"EC_snoop_cb"}
    231 
    232 static const struct nametable US12_names1[] = {
    233 	USall_EVENTS_1(V_US12),
    234 	{V_US12,	0x3,	"Dispatch0_FP_use"},
    235 	{V_US12,	0x8,	"IC_hit"},
    236 	{V_US12,	0x9,	"DC_rd_hit"},
    237 	{V_US12,	0xa,	"DC_wr_hit"},
    238 	{V_US12,	0xb,	"Load_use_RAW"},
    239 	{V_US12,	0xc,	"EC_hit"},
    240 	{V_US12,	0xf,	"EC_ic_hit"},
    241 	{V_END}
    242 };
    243 
    244 #define	US3all_EVENTS_1(v)					\
    245 	{v,		0x3,	"IC_miss_cancelled"},		\
    246 	{v,		0x5,	"Re_FPU_bypass"},		\
    247 	{v,		0x6,	"Re_DC_miss"},			\
    248 	{v,		0x7,	"Re_EC_miss"},			\
    249 	{v,		0x8,	"IC_miss"},			\
    250 	{v,		0x9,	"DC_rd_miss"},			\
    251 	{v,		0xa,	"DC_wr_miss"},			\
    252 	{v,		0xb,	"Rstall_FP_use"},		\
    253 	{v,		0xc,	"EC_misses"},			\
    254 	{v,		0xf,	"EC_ic_miss"},			\
    255 	{v,		0x10,	"Re_PC_miss"},			\
    256 	{v,		0x11,	"ITLB_miss"},			\
    257 	{v,		0x12,	"DTLB_miss"},			\
    258 	{v,		0x13,	"WC_miss"},			\
    259 	{v,		0x14,	"WC_snoop_cb"},			\
    260 	{v,		0x15,	"WC_scrubbed"},			\
    261 	{v,		0x16,	"WC_wb_wo_read"},		\
    262 	{v,		0x18,	"PC_soft_hit"},			\
    263 	{v,		0x19,	"PC_snoop_inv"},		\
    264 	{v,		0x1a,	"PC_hard_hit"},			\
    265 	{v,		0x1b,	"PC_port1_rd"},			\
    266 	{v,		0x1c,	"SW_count_1"},			\
    267 	{v,		0x1d,	"IU_Stat_Br_miss_untaken"},	\
    268 	{v,		0x1e,	"IU_Stat_Br_count_untaken"},	\
    269 	{v,		0x1f,	"PC_MS_misses"},		\
    270 	{v,		0x26,	"Re_RAW_miss"},			\
    271 	{v,		0x27,	"FM_pipe_completion"}
    272 
    273 #define	US3_MC_EVENTS_1(v)					\
    274 	{v,		0x20,	"MC_writes_0"},			\
    275 	{v,		0x21,	"MC_writes_1"},			\
    276 	{v,		0x22,	"MC_writes_2"},			\
    277 	{v,		0x23,	"MC_writes_3"},			\
    278 	{v,		0x24,	"MC_stalls_1"},			\
    279 	{v,		0x25,	"MC_stalls_3"}
    280 
    281 #define	US3_I_MC_EVENTS_1(v)					\
    282 	{v,		0x20,	"MC_open_bank_cmds"},		\
    283 	{v,		0x21,	"MC_reads"},			\
    284 	{v,		0x22,	"MC_writes"},			\
    285 	{v,		0x23,	"MC_page_close_stall"}
    286 
    287 static const struct nametable US3_names1[] = {
    288 	USall_EVENTS_1(V_US3),
    289 	US3all_EVENTS_1(V_US3),
    290 	US3_MC_EVENTS_1(V_US3),
    291 	{V_US3,		0x4,	"Re_endian_miss"},
    292 	{V_END}
    293 };
    294 
    295 static const struct nametable US3_PLUS_names1[] = {
    296 	USall_EVENTS_1(V_US3_PLUS),
    297 	US3all_EVENTS_1(V_US3_PLUS),
    298 	US3_MC_EVENTS_1(V_US3_PLUS),
    299 	{V_US3_PLUS,	0x4,	"Re_DC_missovhd"},
    300 	{V_US3_PLUS,	0x28,	"EC_miss_mtag_remote"},
    301 	{V_US3_PLUS,	0x29,	"EC_miss_remote"},
    302 	{V_END}
    303 };
    304 
    305 static const struct nametable US3_I_names1[] = {
    306 	USall_EVENTS_1(V_US3_I),
    307 	US3all_EVENTS_1(V_US3_I),
    308 	US3_I_MC_EVENTS_1(V_US3_I),
    309 	{V_US3_I,	0x4,	"Re_DC_missovhd"},
    310 	{V_END}
    311 };
    312 
    313 static const struct nametable US4_PLUS_names1[] = {
    314 	{V_US4_PLUS,	0x0,   "Cycle_cnt"},
    315 	{V_US4_PLUS,	0x1,   "Instr_cnt"},
    316 	{V_US4_PLUS,	0x2,   "Dispatch0_other"},
    317 	{V_US4_PLUS,	0x3,   "DC_wr"},
    318 	{V_US4_PLUS,	0x4,   "Re_DC_missovhd"},
    319 	{V_US4_PLUS,	0x5,   "Re_FPU_bypass"},
    320 	{V_US4_PLUS,	0x6,   "L3_write_hit_RTO"},
    321 	{V_US4_PLUS,	0x7,   "L2L3_snoop_inv_sh"},
    322 	{V_US4_PLUS,	0x8,   "IC_L2_req"},
    323 	{V_US4_PLUS,	0x9,   "DC_rd_miss"},
    324 	{V_US4_PLUS,	0xa,   "L2_hit_I_state_sh"},
    325 	{V_US4_PLUS,	0xb,   "L3_write_miss_RTO"},
    326 	{V_US4_PLUS,	0xc,   "L2_miss"},
    327 	{V_US4_PLUS,	0xd,   "SI_owned_sh"},
    328 	{V_US4_PLUS,	0xe,   "SI_RTO_src_data"},
    329 	{V_US4_PLUS,	0xf,   "SW_pf_duplicate"},
    330 	{V_US4_PLUS,	0x10,  "IU_stat_jmp_mispred"},
    331 	{V_US4_PLUS,	0x11,  "ITLB_miss"},
    332 	{V_US4_PLUS,	0x12,  "DTLB_miss"},
    333 	{V_US4_PLUS,	0x13,  "WC_miss"},
    334 	{V_US4_PLUS,	0x14,  "IC_fill"},
    335 	{V_US4_PLUS,	0x15,  "IU_stat_ret_mispred"},
    336 	{V_US4_PLUS,	0x16,  "Re_L3_miss"},
    337 	{V_US4_PLUS,	0x17,  "Re_PFQ_full"},
    338 	{V_US4_PLUS,	0x18,  "PC_soft_hit"},
    339 	{V_US4_PLUS,	0x19,  "PC_inv"},
    340 	{V_US4_PLUS,	0x1a,  "PC_hard_hit"},
    341 	{V_US4_PLUS,	0x1b,  "IC_pf"},
    342 	{V_US4_PLUS,	0x1c,  "SW_count_NOP"},
    343 	{V_US4_PLUS,	0x1d,  "IU_stat_br_miss_untaken"},
    344 	{V_US4_PLUS,	0x1e,  "IU_stat_br_count_taken"},
    345 	{V_US4_PLUS,	0x1f,  "PC_miss"},
    346 	{V_US4_PLUS,	0x20,  "MC_writes_0_sh"},
    347 	{V_US4_PLUS,	0x21,  "MC_writes_1_sh"},
    348 	{V_US4_PLUS,	0x22,  "MC_writes_2_sh"},
    349 	{V_US4_PLUS,	0x23,  "MC_writes_3_sh"},
    350 	{V_US4_PLUS,	0x24,  "MC_stalls_1_sh"},
    351 	{V_US4_PLUS,	0x25,  "MC_stalls_3_sh"},
    352 	{V_US4_PLUS,	0x26,  "Re_RAW_miss"},
    353 	{V_US4_PLUS,	0x27,  "FM_pipe_completion"},
    354 	{V_US4_PLUS,	0x28,  "SSM_L3_miss_mtag_remote"},
    355 	{V_US4_PLUS,	0x29,  "SSM_L3_miss_remote"},
    356 	{V_US4_PLUS,	0x2a,  "SW_pf_exec"},
    357 	{V_US4_PLUS,	0x2b,  "SW_pf_str_exec"},
    358 	{V_US4_PLUS,	0x2c,  "SW_pf_dropped"},
    359 	{V_US4_PLUS,	0x2d,  "SW_pf_L2_installed"},
    360 	{V_US4_PLUS,	0x2f,  "L2_HW_pf_miss"},
    361 	{V_US4_PLUS,	0x31,  "L3_miss"},
    362 	{V_US4_PLUS,	0x32,  "L3_IC_miss"},
    363 	{V_US4_PLUS,	0x33,  "L3_SW_pf_miss"},
    364 	{V_US4_PLUS,	0x34,  "L3_hit_other_half"},
    365 	{V_US4_PLUS,	0x35,  "L3_wb"},
    366 	{V_US4_PLUS,	0x36,  "L3_wb_sh"},
    367 	{V_US4_PLUS,	0x37,  "L2L3_snoop_cb_sh"},
    368 	{V_END}
    369 };
    370 
    371 #undef	USall_EVENTS_1
    372 #undef	US3all_EVENTS_1
    373 
    374 static const struct nametable *US12_names[2] = {
    375 	US12_names0,
    376 	US12_names1
    377 };
    378 
    379 static const struct nametable *US3_names[2] = {
    380 	US3_names0,
    381 	US3_names1
    382 };
    383 
    384 static const struct nametable *US3_PLUS_names[2] = {
    385 	US3_PLUS_names0,
    386 	US3_PLUS_names1
    387 };
    388 
    389 static const struct nametable *US3_I_names[2] = {
    390 	US3_I_names0,
    391 	US3_I_names1
    392 };
    393 
    394 static const struct nametable *US4_PLUS_names[2] = {
    395 	US4_PLUS_names0,
    396 	US4_PLUS_names1
    397 };
    398 
    399 #define	MAPCPUVER(cpuver)	(cpuvermap[(cpuver) - CPC_ULTRA1])
    400 
    401 static int
    402 validargs(int cpuver, int regno)
    403 {
    404 	if (regno < 0 || regno > 1)
    405 		return (0);
    406 	cpuver -= CPC_ULTRA1;
    407 	if (cpuver < 0 ||
    408 	    cpuver >= sizeof (cpuvermap) / sizeof (cpuvermap[0]))
    409 		return (0);
    410 	return (1);
    411 }
    412 
    413 /*ARGSUSED*/
    414 static int
    415 versionmatch(int cpuver, int regno, const struct nametable *n)
    416 {
    417 	if (!validargs(cpuver, regno) || n->ver != MAPCPUVER(cpuver))
    418 		return (0);
    419 	return (1);
    420 }
    421 
    422 static const struct nametable *
    423 getnametable(int cpuver, int regno)
    424 {
    425 	const struct nametable *n;
    426 
    427 	if (!validargs(cpuver, regno))
    428 		return (NULL);
    429 
    430 	switch (MAPCPUVER(cpuver)) {
    431 	case V_US12:
    432 		n = US12_names[regno];
    433 		break;
    434 	case V_US3:
    435 		n = US3_names[regno];
    436 		break;
    437 	case V_US3_PLUS:
    438 		n = US3_PLUS_names[regno];
    439 		break;
    440 	case V_US3_I:
    441 		n = US3_I_names[regno];
    442 		break;
    443 	case V_US4_PLUS:
    444 		n = US4_PLUS_names[regno];
    445 		break;
    446 	default:
    447 		n = NULL;
    448 		break;
    449 	}
    450 	return (n);
    451 }
    452 
    453 void
    454 cpc_walk_names(int cpuver, int regno, void *arg,
    455     void (*action)(void *, int, const char *, uint8_t))
    456 {
    457 	const struct nametable *n;
    458 
    459 	if ((n = getnametable(cpuver, regno)) == NULL)
    460 		return;
    461 	for (; n->ver != V_END; n++)
    462 		if (versionmatch(cpuver, regno, n))
    463 			action(arg, regno, n->name, n->bits);
    464 }
    465 
    466 const char *
    467 __cpc_reg_to_name(int cpuver, int regno, uint8_t bits)
    468 {
    469 	const struct nametable *n;
    470 
    471 	if ((n = getnametable(cpuver, regno)) == NULL)
    472 		return (NULL);
    473 	for (; n->ver != V_END; n++)
    474 		if (bits == n->bits && versionmatch(cpuver, regno, n))
    475 			return (n->name);
    476 	return (NULL);
    477 }
    478 
    479 /*
    480  * Register names can be specified as strings or even as numbers
    481  */
    482 int
    483 __cpc_name_to_reg(int cpuver, int regno, const char *name, uint8_t *bits)
    484 {
    485 	const struct nametable *n;
    486 	char *eptr = NULL;
    487 	long value;
    488 
    489 	if ((n = getnametable(cpuver, regno)) == NULL || name == NULL)
    490 		return (-1);
    491 
    492 	for (; n->ver != V_END; n++)
    493 		if (strcmp(name, n->name) == 0 &&
    494 		    versionmatch(cpuver, regno, n)) {
    495 			*bits = n->bits;
    496 			return (0);
    497 		}
    498 
    499 	value = strtol(name, &eptr, 0);
    500 	if (name != eptr && value >= 0 && value <= UINT8_MAX) {
    501 		*bits = (uint8_t)value;
    502 		return (0);
    503 	}
    504 
    505 	return (-1);
    506 }
    507 
    508 const char *
    509 cpc_getcciname(int cpuver)
    510 {
    511 	if (validargs(cpuver, 0))
    512 		switch (MAPCPUVER(cpuver)) {
    513 		case V_US12:
    514 			return ("UltraSPARC I&II");
    515 		case V_US3:
    516 			return ("UltraSPARC III");
    517 		case V_US3_PLUS:
    518 			return ("UltraSPARC III+ & IV");
    519 		case V_US3_I:
    520 			return ("UltraSPARC IIIi & IIIi+");
    521 		case V_US4_PLUS:
    522 			return ("UltraSPARC IV+");
    523 		default:
    524 			break;
    525 		}
    526 	return (NULL);
    527 }
    528 
    529 #define	CPU_REF_URL " Documentation for Sun processors can be found at: " \
    530 			"http://www.sun.com/processors/manuals"
    531 
    532 const char *
    533 cpc_getcpuref(int cpuver)
    534 {
    535 	if (validargs(cpuver, 0))
    536 		switch (MAPCPUVER(cpuver)) {
    537 		case V_US12:
    538 			return (gettext(
    539 			    "See the \"UltraSPARC I/II User\'s Manual\" "
    540 			    "(Part No. 802-7220-02) "
    541 			    "for descriptions of these events." CPU_REF_URL));
    542 		case V_US3:
    543 		case V_US3_PLUS:
    544 			return (gettext(
    545 			    "See the \"UltraSPARC III Cu User's Manual\" "
    546 			    "for descriptions of these events." CPU_REF_URL));
    547 		case V_US3_I:
    548 			return (gettext(
    549 			    "See the \"UltraSPARC IIIi User's Manual\"  "
    550 			    "for descriptions of these events." CPU_REF_URL));
    551 		case V_US4_PLUS:
    552 			return (gettext(
    553 			    "See the \"UltraSPARC IV User's Manual"
    554 			    "Supplement\"  "
    555 			    "for descriptions of these events." CPU_REF_URL));
    556 		default:
    557 			break;
    558 		}
    559 	return (NULL);
    560 }
    561 
    562 /*
    563  * This is a functional interface to allow CPUs with fewer %pic registers
    564  * to share the same data structure as those with more %pic registers
    565  * within the same instruction family.
    566  */
    567 uint_t
    568 cpc_getnpic(int cpuver)
    569 {
    570 	/*LINTED*/
    571 	cpc_event_t *event;
    572 
    573 	switch (cpuver) {
    574 	case CPC_ULTRA1:
    575 	case CPC_ULTRA2:
    576 	case CPC_ULTRA3:
    577 	case CPC_ULTRA3_PLUS:
    578 	case CPC_ULTRA3_I:
    579 	case CPC_ULTRA4_PLUS:
    580 		return (sizeof (event->ce_pic) / sizeof (event->ce_pic[0]));
    581 	default:
    582 		return (0);
    583 	}
    584 }
    585 
    586 /*
    587  * Compares the given string against the list of all known CPU node names, and
    588  * returns the CPC CPU version code if there is a match. If there is no match,
    589  * returns -1.
    590  */
    591 static int
    592 node2ver(char *node)
    593 {
    594 	if (strcmp(node, "SUNW,UltraSPARC") == 0 ||
    595 	    strcmp(node, "SUNW,UltraSPARC-II") == 0 ||
    596 	    strcmp(node, "SUNW,UltraSPARC-IIi") == 0 ||
    597 	    strcmp(node, "SUNW,UltraSPARC-IIe") == 0) {
    598 		return (CPC_ULTRA1);
    599 	} else if (strcmp(node, "SUNW,UltraSPARC-III") == 0)
    600 		return (CPC_ULTRA3);
    601 	else if (strcmp(node, "SUNW,UltraSPARC-III+") == 0 ||
    602 	    strcmp(node, "SUNW,UltraSPARC-IV") == 0)
    603 		return (CPC_ULTRA3_PLUS);
    604 	else if (strcmp(node, "SUNW,UltraSPARC-IIIi") == 0 ||
    605 	    strcmp(node, "SUNW,UltraSPARC-IIIi+") == 0)
    606 		return (CPC_ULTRA3_I);
    607 	else if (strcmp(node, "SUNW,UltraSPARC-IV+") == 0)
    608 		return (CPC_ULTRA4_PLUS);
    609 
    610 	return (-1);
    611 }
    612 
    613 static int
    614 cpc_get_cpu_ver(di_node_t di_node, void *arg)
    615 {
    616 	char		*node_name, *compatible_array;
    617 	int		n_names, i, found = 0;
    618 	int		*ver = arg;
    619 
    620 	node_name = di_node_name(di_node);
    621 	if (node_name != NULL) {
    622 		if ((*ver = node2ver(node_name)) != -1)
    623 			found = 1;
    624 		else if (strncmp(node_name, "cpu", 4) == 0) {
    625 			/*
    626 			 * CPU nodes associated with CMP use the generic name
    627 			 * of "cpu".  We must look at the compatible property
    628 			 * in order to find the implementation specific name.
    629 			 */
    630 			if ((n_names = di_compatible_names(di_node,
    631 			    &compatible_array)) > 0) {
    632 				for (i = 0; i < n_names; i++) {
    633 					if ((*ver = node2ver(compatible_array))
    634 					    != -1) {
    635 						found = 1;
    636 						break;
    637 					}
    638 					compatible_array +=
    639 					    strlen(compatible_array) + 1;
    640 				}
    641 			}
    642 		}
    643 	}
    644 
    645 	if (found == 0)
    646 		return (DI_WALK_CONTINUE);
    647 
    648 	return (DI_WALK_TERMINATE);
    649 }
    650 
    651 /*
    652  * Return the version of the current processor.
    653  *
    654  * Version -1 is defined as 'not performance counter capable'
    655  *
    656  * XXX  A better solution would be to use the di_prom_props for the cpu
    657  * devinfo nodes. That way we could look at the 'device-type', 'sparc-version'
    658  * and 'implementation#' properties in order to determine which version of
    659  * UltraSPARC we are running on.
    660  *
    661  * The problem with this is that di_prom_init() requires root access to
    662  * open /dev/openprom and cputrack is not a root-only application so
    663  * we have to settle for the di_props that we can see as non-root users.
    664  */
    665 int
    666 cpc_getcpuver(void)
    667 {
    668 	static int ver = -1;
    669 
    670 	if (ver == -1) {
    671 		di_node_t	di_root_node;
    672 
    673 		if ((di_root_node = di_init("/", DINFOCPYALL)) == DI_NODE_NIL)
    674 			return (-1);
    675 
    676 		(void) di_walk_node(di_root_node, DI_WALK_CLDFIRST,
    677 			(void *)&ver, cpc_get_cpu_ver);
    678 
    679 		di_fini(di_root_node);
    680 	}
    681 	return (ver);
    682 }
    683