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