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 */
69static 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
78struct 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
98static 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
140static 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
147static 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
202static 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
212static 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
232static 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
287static 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
295static 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
305static 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
313static 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
374static const struct nametable *US12_names[2] = {
375	US12_names0,
376	US12_names1
377};
378
379static const struct nametable *US3_names[2] = {
380	US3_names0,
381	US3_names1
382};
383
384static const struct nametable *US3_PLUS_names[2] = {
385	US3_PLUS_names0,
386	US3_PLUS_names1
387};
388
389static const struct nametable *US3_I_names[2] = {
390	US3_I_names0,
391	US3_I_names1
392};
393
394static 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
401static int
402validargs(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*/
414static int
415versionmatch(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
422static const struct nametable *
423getnametable(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
453void
454cpc_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
466const 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 */
482int
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
508const char *
509cpc_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
532const char *
533cpc_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 */
567uint_t
568cpc_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 */
591static int
592node2ver(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
613static int
614cpc_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 */
665int
666cpc_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