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