xref: /titanic_52/usr/src/lib/libcpc/sparc/conf_ultra.c (revision 1a7c1b724419d3cb5fa6eea75123c6b2060ba31b)
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]  and [4].  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  */
57 
58 #define	V_US12		(1u << 0)	/* specific to UltraSPARC 1 and 2 */
59 #define	V_US3		(1u << 1)	/* specific to UltraSPARC 3 */
60 #define	V_US3_PLUS	(1u << 2)	/* specific to UltraSPARC 3 PLUS */
61 #define	V_US3_I		(1u << 3)	/* specific to UltraSPARC-IIIi */
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 };
74 
75 struct nametable {
76 	const uint_t	ver;
77 	const uint8_t	bits;
78 	const char	*name;
79 };
80 
81 /*
82  * Definitions for counter 0
83  */
84 
85 #define	USall_EVENTS_0(v)					\
86 	{v,		0x0,	"Cycle_cnt"},			\
87 	{v,		0x1,	"Instr_cnt"},			\
88 	{v,		0x2,	"Dispatch0_IC_miss"},		\
89 	{v,		0x8,	"IC_ref"},			\
90 	{v,		0x9,	"DC_rd"},			\
91 	{v,		0xa,	"DC_wr"},			\
92 	{v,		0xc,	"EC_ref"},			\
93 	{v,		0xe,	"EC_snoop_inv"}
94 
95 static const struct nametable US12_names0[] = {
96 	USall_EVENTS_0(V_US12),
97 	{V_US12,	0x3,	"Dispatch0_storeBuf"},
98 	{V_US12,	0xb,	"Load_use"},
99 	{V_US12,	0xd,	"EC_write_hit_RDO"},
100 	{V_US12,	0xf,	"EC_rd_hit"},
101 	{V_END}
102 };
103 
104 #define	US3all_EVENTS_0(v)					\
105 	{v,		0x3,	"Dispatch0_br_target"},		\
106 	{v,		0x4,	"Dispatch0_2nd_br"},		\
107 	{v,		0x5,	"Rstall_storeQ"},		\
108 	{v,		0x6,	"Rstall_IU_use"},		\
109 	{v,		0xd,	"EC_write_hit_RTO"},		\
110 	{v,		0xf,	"EC_rd_miss"},			\
111 	{v,		0x10,	"PC_port0_rd"},			\
112 	{v,		0x11,	"SI_snoop"},			\
113 	{v,		0x12,	"SI_ciq_flow"},			\
114 	{v,		0x13,	"SI_owned"},			\
115 	{v,		0x14,	"SW_count_0"},			\
116 	{v,		0x15,	"IU_Stat_Br_miss_taken"},	\
117 	{v,		0x16,	"IU_Stat_Br_count_taken"},	\
118 	{v,		0x17,	"Dispatch_rs_mispred"},		\
119 	{v,		0x18,	"FA_pipe_completion"}
120 
121 #define	US3_MC_EVENTS_0(v)					\
122 	{v,		0x20,	"MC_reads_0"},			\
123 	{v,		0x21,	"MC_reads_1"},			\
124 	{v,		0x22,	"MC_reads_2"},			\
125 	{v,		0x23,	"MC_reads_3"},			\
126 	{v,		0x24,	"MC_stalls_0"},			\
127 	{v,		0x25,	"MC_stalls_2"}
128 
129 #define	US3_I_MC_EVENTS_0(v)					\
130 	{v,		0x20,	"MC_read_dispatched"},		\
131 	{v,		0x21,	"MC_write_dispatched"},		\
132 	{v,		0x22,	"MC_read_returned_to_JBU"},	\
133 	{v,		0x23,	"MC_msl_busy_stall"},		\
134 	{v,		0x24,	"MC_mdb_overflow_stall"},	\
135 	{v,		0x25,	"MC_miu_spec_request"}
136 
137 static const struct nametable US3_names0[] = {
138 	USall_EVENTS_0(V_US3),
139 	US3all_EVENTS_0(V_US3),
140 	US3_MC_EVENTS_0(V_US3),
141 	{V_END}
142 };
143 
144 static const struct nametable US3_PLUS_names0[] = {
145 	USall_EVENTS_0(V_US3_PLUS),
146 	US3all_EVENTS_0(V_US3_PLUS),
147 	US3_MC_EVENTS_0(V_US3_PLUS),
148 	{V_US3_PLUS,	0x19,	"EC_wb_remote"},
149 	{V_US3_PLUS,	0x1a,	"EC_miss_local"},
150 	{V_US3_PLUS,	0x1b,	"EC_miss_mtag_remote"},
151 	{V_END}
152 };
153 
154 static const struct nametable US3_I_names0[] = {
155 	USall_EVENTS_0(V_US3_I),
156 	US3all_EVENTS_0(V_US3_I),
157 	US3_I_MC_EVENTS_0(V_US3_I),
158 	{V_US3_PLUS,	0x19,	"EC_wb_remote"},
159 	{V_US3_PLUS,	0x1a,	"EC_miss_local"},
160 	{V_US3_PLUS,	0x1b,	"EC_miss_mtag_remote"},
161 	{V_END}
162 };
163 
164 #undef	USall_EVENTS_0
165 #undef	US3all_EVENTS_0
166 
167 #define	USall_EVENTS_1(v)					\
168 	{v,		0x0,	"Cycle_cnt"},			\
169 	{v,		0x1,	"Instr_cnt"},			\
170 	{v,		0x2,	"Dispatch0_mispred"},		\
171 	{v,		0xd,	"EC_wb"},			\
172 	{v,		0xe,	"EC_snoop_cb"}
173 
174 static const struct nametable US12_names1[] = {
175 	USall_EVENTS_1(V_US12),
176 	{V_US12,	0x3,	"Dispatch0_FP_use"},
177 	{V_US12,	0x8,	"IC_hit"},
178 	{V_US12,	0x9,	"DC_rd_hit"},
179 	{V_US12,	0xa,	"DC_wr_hit"},
180 	{V_US12,	0xb,	"Load_use_RAW"},
181 	{V_US12,	0xc,	"EC_hit"},
182 	{V_US12,	0xf,	"EC_ic_hit"},
183 	{V_END}
184 };
185 
186 #define	US3all_EVENTS_1(v)					\
187 	{v,		0x3,	"IC_miss_cancelled"},		\
188 	{v,		0x5,	"Re_FPU_bypass"},		\
189 	{v,		0x6,	"Re_DC_miss"},			\
190 	{v,		0x7,	"Re_EC_miss"},			\
191 	{v,		0x8,	"IC_miss"},			\
192 	{v,		0x9,	"DC_rd_miss"},			\
193 	{v,		0xa,	"DC_wr_miss"},			\
194 	{v,		0xb,	"Rstall_FP_use"},		\
195 	{v,		0xc,	"EC_misses"},			\
196 	{v,		0xf,	"EC_ic_miss"},			\
197 	{v,		0x10,	"Re_PC_miss"},			\
198 	{v,		0x11,	"ITLB_miss"},			\
199 	{v,		0x12,	"DTLB_miss"},			\
200 	{v,		0x13,	"WC_miss"},			\
201 	{v,		0x14,	"WC_snoop_cb"},			\
202 	{v,		0x15,	"WC_scrubbed"},			\
203 	{v,		0x16,	"WC_wb_wo_read"},		\
204 	{v,		0x18,	"PC_soft_hit"},			\
205 	{v,		0x19,	"PC_snoop_inv"},		\
206 	{v,		0x1a,	"PC_hard_hit"},			\
207 	{v,		0x1b,	"PC_port1_rd"},			\
208 	{v,		0x1c,	"SW_count_1"},			\
209 	{v,		0x1d,	"IU_Stat_Br_miss_untaken"},	\
210 	{v,		0x1e,	"IU_Stat_Br_count_untaken"},	\
211 	{v,		0x1f,	"PC_MS_misses"},		\
212 	{v,		0x26,	"Re_RAW_miss"},			\
213 	{v,		0x27,	"FM_pipe_completion"}
214 
215 #define	US3_MC_EVENTS_1(v)					\
216 	{v,		0x20,	"MC_writes_0"},			\
217 	{v,		0x21,	"MC_writes_1"},			\
218 	{v,		0x22,	"MC_writes_2"},			\
219 	{v,		0x23,	"MC_writes_3"},			\
220 	{v,		0x24,	"MC_stalls_1"},			\
221 	{v,		0x25,	"MC_stalls_3"}
222 
223 #define	US3_I_MC_EVENTS_1(v)					\
224 	{v,		0x20,	"MC_open_bank_cmds"},		\
225 	{v,		0x21,	"MC_reads"},			\
226 	{v,		0x22,	"MC_writes"},			\
227 	{v,		0x23,	"MC_page_close_stall"}
228 
229 static const struct nametable US3_names1[] = {
230 	USall_EVENTS_1(V_US3),
231 	US3all_EVENTS_1(V_US3),
232 	US3_MC_EVENTS_1(V_US3),
233 	{V_US3,		0x4,	"Re_endian_miss"},
234 	{V_END}
235 };
236 
237 static const struct nametable US3_PLUS_names1[] = {
238 	USall_EVENTS_1(V_US3_PLUS),
239 	US3all_EVENTS_1(V_US3_PLUS),
240 	US3_MC_EVENTS_1(V_US3_PLUS),
241 	{V_US3_PLUS,	0x4,	"Re_DC_missovhd"},
242 	{V_US3_PLUS,	0x28,	"EC_miss_mtag_remote"},
243 	{V_US3_PLUS,	0x29,	"EC_miss_remote"},
244 	{V_END}
245 };
246 
247 static const struct nametable US3_I_names1[] = {
248 	USall_EVENTS_1(V_US3_I),
249 	US3all_EVENTS_1(V_US3_I),
250 	US3_I_MC_EVENTS_1(V_US3_I),
251 	{V_US3_I,	0x4,	"Re_DC_missovhd"},
252 	{V_END}
253 };
254 #undef	USall_EVENTS_1
255 #undef	US3all_EVENTS_1
256 
257 static const struct nametable *US12_names[2] = {
258 	US12_names0,
259 	US12_names1
260 };
261 
262 static const struct nametable *US3_names[2] = {
263 	US3_names0,
264 	US3_names1
265 };
266 
267 static const struct nametable *US3_PLUS_names[2] = {
268 	US3_PLUS_names0,
269 	US3_PLUS_names1
270 };
271 
272 static const struct nametable *US3_I_names[2] = {
273 	US3_I_names0,
274 	US3_I_names1
275 };
276 
277 #define	MAPCPUVER(cpuver)	(cpuvermap[(cpuver) - CPC_ULTRA1])
278 
279 static int
280 validargs(int cpuver, int regno)
281 {
282 	if (regno < 0 || regno > 1)
283 		return (0);
284 	cpuver -= CPC_ULTRA1;
285 	if (cpuver < 0 ||
286 	    cpuver >= sizeof (cpuvermap) / sizeof (cpuvermap[0]))
287 		return (0);
288 	return (1);
289 }
290 
291 /*ARGSUSED*/
292 static int
293 versionmatch(int cpuver, int regno, const struct nametable *n)
294 {
295 	if (!validargs(cpuver, regno) || n->ver != MAPCPUVER(cpuver))
296 		return (0);
297 	return (1);
298 }
299 
300 static const struct nametable *
301 getnametable(int cpuver, int regno)
302 {
303 	const struct nametable *n;
304 
305 	if (!validargs(cpuver, regno))
306 		return (NULL);
307 
308 	switch (MAPCPUVER(cpuver)) {
309 	case V_US12:
310 		n = US12_names[regno];
311 		break;
312 	case V_US3:
313 		n = US3_names[regno];
314 		break;
315 	case V_US3_PLUS:
316 		n = US3_PLUS_names[regno];
317 		break;
318 	case V_US3_I:
319 		n = US3_I_names[regno];
320 		break;
321 	default:
322 		n = NULL;
323 		break;
324 	}
325 	return (n);
326 }
327 
328 void
329 cpc_walk_names(int cpuver, int regno, void *arg,
330     void (*action)(void *, int, const char *, uint8_t))
331 {
332 	const struct nametable *n;
333 
334 	if ((n = getnametable(cpuver, regno)) == NULL)
335 		return;
336 	for (; n->ver != V_END; n++)
337 		if (versionmatch(cpuver, regno, n))
338 			action(arg, regno, n->name, n->bits);
339 }
340 
341 const char *
342 __cpc_reg_to_name(int cpuver, int regno, uint8_t bits)
343 {
344 	const struct nametable *n;
345 
346 	if ((n = getnametable(cpuver, regno)) == NULL)
347 		return (NULL);
348 	for (; n->ver != V_END; n++)
349 		if (bits == n->bits && versionmatch(cpuver, regno, n))
350 			return (n->name);
351 	return (NULL);
352 }
353 
354 /*
355  * Register names can be specified as strings or even as numbers
356  */
357 int
358 __cpc_name_to_reg(int cpuver, int regno, const char *name, uint8_t *bits)
359 {
360 	const struct nametable *n;
361 	char *eptr = NULL;
362 	long value;
363 
364 	if ((n = getnametable(cpuver, regno)) == NULL || name == NULL)
365 		return (-1);
366 
367 	for (; n->ver != V_END; n++)
368 		if (strcmp(name, n->name) == 0 &&
369 		    versionmatch(cpuver, regno, n)) {
370 			*bits = n->bits;
371 			return (0);
372 		}
373 
374 	value = strtol(name, &eptr, 0);
375 	if (name != eptr && value >= 0 && value <= UINT8_MAX) {
376 		*bits = (uint8_t)value;
377 		return (0);
378 	}
379 
380 	return (-1);
381 }
382 
383 const char *
384 cpc_getcciname(int cpuver)
385 {
386 	if (validargs(cpuver, 0))
387 		switch (MAPCPUVER(cpuver)) {
388 		case V_US12:
389 			return ("UltraSPARC I&II");
390 		case V_US3:
391 			return ("UltraSPARC III");
392 		case V_US3_PLUS:
393 			return ("UltraSPARC III+ & IV");
394 		case V_US3_I:
395 			return ("UltraSPARC IIIi & IIIi+");
396 		default:
397 			break;
398 		}
399 	return (NULL);
400 }
401 
402 #define	CPU_REF_URL " Documentation for Sun processors can be found at: " \
403 			"http://www.sun.com/processors/manuals"
404 
405 const char *
406 cpc_getcpuref(int cpuver)
407 {
408 	if (validargs(cpuver, 0))
409 		switch (MAPCPUVER(cpuver)) {
410 		case V_US12:
411 			return (gettext(
412 			    "See the \"UltraSPARC I/II User\'s Manual\" "
413 			    "(Part No. 802-7220-02) "
414 			    "for descriptions of these events." CPU_REF_URL));
415 		case V_US3:
416 		case V_US3_PLUS:
417 			return (gettext(
418 			    "See the \"UltraSPARC III Cu User's Manual\" "
419 			    "for descriptions of these events." CPU_REF_URL));
420 		case V_US3_I:
421 			return (gettext(
422 			    "See the \"UltraSPARC IIIi User's Manual\"  "
423 			    "for descriptions of these events." CPU_REF_URL));
424 		default:
425 			break;
426 		}
427 	return (NULL);
428 }
429 
430 /*
431  * This is a functional interface to allow CPUs with fewer %pic registers
432  * to share the same data structure as those with more %pic registers
433  * within the same instruction family.
434  */
435 uint_t
436 cpc_getnpic(int cpuver)
437 {
438 	/*LINTED*/
439 	cpc_event_t *event;
440 
441 	switch (cpuver) {
442 	case CPC_ULTRA1:
443 	case CPC_ULTRA2:
444 	case CPC_ULTRA3:
445 	case CPC_ULTRA3_PLUS:
446 	case CPC_ULTRA3_I:
447 		return (sizeof (event->ce_pic) / sizeof (event->ce_pic[0]));
448 	default:
449 		return (0);
450 	}
451 }
452 
453 /*
454  * Compares the given string against the list of all known CPU node names, and
455  * returns the CPC CPU version code if there is a match. If there is no match,
456  * returns -1.
457  */
458 static int
459 node2ver(char *node)
460 {
461 	if (strcmp(node, "SUNW,UltraSPARC") == 0 ||
462 	    strcmp(node, "SUNW,UltraSPARC-II") == 0 ||
463 	    strcmp(node, "SUNW,UltraSPARC-IIi") == 0 ||
464 	    strcmp(node, "SUNW,UltraSPARC-IIe") == 0) {
465 		return (CPC_ULTRA1);
466 	} else if (strcmp(node, "SUNW,UltraSPARC-III") == 0)
467 		return (CPC_ULTRA3);
468 	else if (strcmp(node, "SUNW,UltraSPARC-III+") == 0 ||
469 	    strcmp(node, "SUNW,UltraSPARC-IV") == 0 ||
470 	    strcmp(node, "SUNW,UltraSPARC-IV+") == 0)
471 		return (CPC_ULTRA3_PLUS);
472 	else if (strcmp(node, "SUNW,UltraSPARC-IIIi") == 0 ||
473 	    strcmp(node, "SUNW,UltraSPARC-IIIi+") == 0)
474 		return (CPC_ULTRA3_I);
475 
476 	return (-1);
477 }
478 
479 static int
480 cpc_get_cpu_ver(di_node_t di_node, void *arg)
481 {
482 	char		*node_name, *compatible_array;
483 	int		n_names, i, found = 0;
484 	int		*ver = arg;
485 
486 	node_name = di_node_name(di_node);
487 	if (node_name != NULL) {
488 		if ((*ver = node2ver(node_name)) != -1)
489 			found = 1;
490 		else if (strncmp(node_name, "cpu", 4) == 0) {
491 			/*
492 			 * CPU nodes associated with CMP use the generic name
493 			 * of "cpu".  We must look at the compatible property
494 			 * in order to find the implementation specific name.
495 			 */
496 			if ((n_names = di_compatible_names(di_node,
497 			    &compatible_array)) > 0) {
498 				for (i = 0; i < n_names; i++) {
499 					if ((*ver = node2ver(compatible_array))
500 					    != -1) {
501 						found = 1;
502 						break;
503 					}
504 					compatible_array +=
505 					    strlen(compatible_array) + 1;
506 				}
507 			}
508 		}
509 	}
510 
511 	if (found == 0)
512 		return (DI_WALK_CONTINUE);
513 
514 	return (DI_WALK_TERMINATE);
515 }
516 
517 /*
518  * Return the version of the current processor.
519  *
520  * Version -1 is defined as 'not performance counter capable'
521  *
522  * XXX  A better solution would be to use the di_prom_props for the cpu
523  * devinfo nodes. That way we could look at the 'device-type', 'sparc-version'
524  * and 'implementation#' properties in order to determine which version of
525  * UltraSPARC we are running on.
526  *
527  * The problem with this is that di_prom_init() requires root access to
528  * open /dev/openprom and cputrack is not a root-only application so
529  * we have to settle for the di_props that we can see as non-root users.
530  */
531 int
532 cpc_getcpuver(void)
533 {
534 	static int ver = -1;
535 
536 	if (ver == -1) {
537 		di_node_t	di_root_node;
538 
539 		if ((di_root_node = di_init("/", DINFOCPYALL)) == DI_NODE_NIL)
540 			return (-1);
541 
542 		(void) di_walk_node(di_root_node, DI_WALK_CLDFIRST,
543 			(void *)&ver, cpc_get_cpu_ver);
544 
545 		di_fini(di_root_node);
546 	}
547 	return (ver);
548 }
549