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