1 2 #include "x86/msr.h" 3 #include "x86/processor.h" 4 #include "x86/apic-defs.h" 5 #include "x86/apic.h" 6 #include "x86/desc.h" 7 #include "x86/isr.h" 8 #include "alloc.h" 9 10 #include "libcflat.h" 11 #include <stdint.h> 12 13 #define FIXED_CNT_INDEX 32 14 #define PC_VECTOR 32 15 16 #define EVNSEL_EVENT_SHIFT 0 17 #define EVNTSEL_UMASK_SHIFT 8 18 #define EVNTSEL_USR_SHIFT 16 19 #define EVNTSEL_OS_SHIFT 17 20 #define EVNTSEL_EDGE_SHIFT 18 21 #define EVNTSEL_PC_SHIFT 19 22 #define EVNTSEL_INT_SHIFT 20 23 #define EVNTSEL_EN_SHIF 22 24 #define EVNTSEL_INV_SHIF 23 25 #define EVNTSEL_CMASK_SHIFT 24 26 27 #define EVNTSEL_EN (1 << EVNTSEL_EN_SHIF) 28 #define EVNTSEL_USR (1 << EVNTSEL_USR_SHIFT) 29 #define EVNTSEL_OS (1 << EVNTSEL_OS_SHIFT) 30 #define EVNTSEL_PC (1 << EVNTSEL_PC_SHIFT) 31 #define EVNTSEL_INT (1 << EVNTSEL_INT_SHIFT) 32 #define EVNTSEL_INV (1 << EVNTSEL_INV_SHIF) 33 34 #define N 1000000 35 36 // These values match the number of instructions and branches in the 37 // assembly block in check_emulated_instr(). 38 #define EXPECTED_INSTR 17 39 #define EXPECTED_BRNCH 5 40 41 typedef struct { 42 uint32_t ctr; 43 uint32_t config; 44 uint64_t count; 45 int idx; 46 } pmu_counter_t; 47 48 struct pmu_event { 49 const char *name; 50 uint32_t unit_sel; 51 int min; 52 int max; 53 } gp_events[] = { 54 {"core cycles", 0x003c, 1*N, 50*N}, 55 {"instructions", 0x00c0, 10*N, 10.2*N}, 56 {"ref cycles", 0x013c, 1*N, 30*N}, 57 {"llc references", 0x4f2e, 1, 2*N}, 58 {"llc misses", 0x412e, 1, 1*N}, 59 {"branches", 0x00c4, 1*N, 1.1*N}, 60 {"branch misses", 0x00c5, 0, 0.1*N}, 61 }, fixed_events[] = { 62 {"fixed 1", MSR_CORE_PERF_FIXED_CTR0, 10*N, 10.2*N}, 63 {"fixed 2", MSR_CORE_PERF_FIXED_CTR0 + 1, 1*N, 30*N}, 64 {"fixed 3", MSR_CORE_PERF_FIXED_CTR0 + 2, 0.1*N, 30*N} 65 }; 66 67 #define PMU_CAP_FW_WRITES (1ULL << 13) 68 static u64 gp_counter_base = MSR_IA32_PERFCTR0; 69 70 char *buf; 71 72 static inline void loop(void) 73 { 74 unsigned long tmp, tmp2, tmp3; 75 76 asm volatile("1: mov (%1), %2; add $64, %1; nop; nop; nop; nop; nop; nop; nop; loop 1b" 77 : "=c"(tmp), "=r"(tmp2), "=r"(tmp3): "0"(N), "1"(buf)); 78 79 } 80 81 volatile uint64_t irq_received; 82 83 static void cnt_overflow(isr_regs_t *regs) 84 { 85 irq_received++; 86 apic_write(APIC_EOI, 0); 87 } 88 89 static bool check_irq(void) 90 { 91 int i; 92 irq_received = 0; 93 irq_enable(); 94 for (i = 0; i < 100000 && !irq_received; i++) 95 asm volatile("pause"); 96 irq_disable(); 97 return irq_received; 98 } 99 100 static bool is_gp(pmu_counter_t *evt) 101 { 102 return evt->ctr < MSR_CORE_PERF_FIXED_CTR0 || 103 evt->ctr >= MSR_IA32_PMC0; 104 } 105 106 static int event_to_global_idx(pmu_counter_t *cnt) 107 { 108 return cnt->ctr - (is_gp(cnt) ? gp_counter_base : 109 (MSR_CORE_PERF_FIXED_CTR0 - FIXED_CNT_INDEX)); 110 } 111 112 static struct pmu_event* get_counter_event(pmu_counter_t *cnt) 113 { 114 if (is_gp(cnt)) { 115 int i; 116 117 for (i = 0; i < sizeof(gp_events)/sizeof(gp_events[0]); i++) 118 if (gp_events[i].unit_sel == (cnt->config & 0xffff)) 119 return &gp_events[i]; 120 } else 121 return &fixed_events[cnt->ctr - MSR_CORE_PERF_FIXED_CTR0]; 122 123 return (void*)0; 124 } 125 126 static void global_enable(pmu_counter_t *cnt) 127 { 128 cnt->idx = event_to_global_idx(cnt); 129 130 wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, rdmsr(MSR_CORE_PERF_GLOBAL_CTRL) | 131 (1ull << cnt->idx)); 132 } 133 134 static void global_disable(pmu_counter_t *cnt) 135 { 136 wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, rdmsr(MSR_CORE_PERF_GLOBAL_CTRL) & 137 ~(1ull << cnt->idx)); 138 } 139 140 static void __start_event(pmu_counter_t *evt, uint64_t count) 141 { 142 evt->count = count; 143 wrmsr(evt->ctr, evt->count); 144 if (is_gp(evt)) 145 wrmsr(MSR_P6_EVNTSEL0 + event_to_global_idx(evt), 146 evt->config | EVNTSEL_EN); 147 else { 148 uint32_t ctrl = rdmsr(MSR_CORE_PERF_FIXED_CTR_CTRL); 149 int shift = (evt->ctr - MSR_CORE_PERF_FIXED_CTR0) * 4; 150 uint32_t usrospmi = 0; 151 152 if (evt->config & EVNTSEL_OS) 153 usrospmi |= (1 << 0); 154 if (evt->config & EVNTSEL_USR) 155 usrospmi |= (1 << 1); 156 if (evt->config & EVNTSEL_INT) 157 usrospmi |= (1 << 3); // PMI on overflow 158 ctrl = (ctrl & ~(0xf << shift)) | (usrospmi << shift); 159 wrmsr(MSR_CORE_PERF_FIXED_CTR_CTRL, ctrl); 160 } 161 global_enable(evt); 162 apic_write(APIC_LVTPC, PC_VECTOR); 163 } 164 165 static void start_event(pmu_counter_t *evt) 166 { 167 __start_event(evt, 0); 168 } 169 170 static void stop_event(pmu_counter_t *evt) 171 { 172 global_disable(evt); 173 if (is_gp(evt)) 174 wrmsr(MSR_P6_EVNTSEL0 + event_to_global_idx(evt), 175 evt->config & ~EVNTSEL_EN); 176 else { 177 uint32_t ctrl = rdmsr(MSR_CORE_PERF_FIXED_CTR_CTRL); 178 int shift = (evt->ctr - MSR_CORE_PERF_FIXED_CTR0) * 4; 179 wrmsr(MSR_CORE_PERF_FIXED_CTR_CTRL, ctrl & ~(0xf << shift)); 180 } 181 evt->count = rdmsr(evt->ctr); 182 } 183 184 static noinline void measure_many(pmu_counter_t *evt, int count) 185 { 186 int i; 187 for (i = 0; i < count; i++) 188 start_event(&evt[i]); 189 loop(); 190 for (i = 0; i < count; i++) 191 stop_event(&evt[i]); 192 } 193 194 static void measure_one(pmu_counter_t *evt) 195 { 196 measure_many(evt, 1); 197 } 198 199 static noinline void __measure(pmu_counter_t *evt, uint64_t count) 200 { 201 __start_event(evt, count); 202 loop(); 203 stop_event(evt); 204 } 205 206 static bool verify_event(uint64_t count, struct pmu_event *e) 207 { 208 // printf("%d <= %ld <= %d\n", e->min, count, e->max); 209 return count >= e->min && count <= e->max; 210 211 } 212 213 static bool verify_counter(pmu_counter_t *cnt) 214 { 215 return verify_event(cnt->count, get_counter_event(cnt)); 216 } 217 218 static void check_gp_counter(struct pmu_event *evt) 219 { 220 int nr_gp_counters = pmu_nr_gp_counters(); 221 pmu_counter_t cnt = { 222 .ctr = gp_counter_base, 223 .config = EVNTSEL_OS | EVNTSEL_USR | evt->unit_sel, 224 }; 225 int i; 226 227 for (i = 0; i < nr_gp_counters; i++, cnt.ctr++) { 228 measure_one(&cnt); 229 report(verify_event(cnt.count, evt), "%s-%d", evt->name, i); 230 } 231 } 232 233 static void check_gp_counters(void) 234 { 235 int i; 236 237 for (i = 0; i < sizeof(gp_events)/sizeof(gp_events[0]); i++) 238 if (pmu_gp_counter_is_available(i)) 239 check_gp_counter(&gp_events[i]); 240 else 241 printf("GP event '%s' is disabled\n", 242 gp_events[i].name); 243 } 244 245 static void check_fixed_counters(void) 246 { 247 int nr_fixed_counters = pmu_nr_fixed_counters(); 248 pmu_counter_t cnt = { 249 .config = EVNTSEL_OS | EVNTSEL_USR, 250 }; 251 int i; 252 253 for (i = 0; i < nr_fixed_counters; i++) { 254 cnt.ctr = fixed_events[i].unit_sel; 255 measure_one(&cnt); 256 report(verify_event(cnt.count, &fixed_events[i]), "fixed-%d", i); 257 } 258 } 259 260 static void check_counters_many(void) 261 { 262 int nr_fixed_counters = pmu_nr_fixed_counters(); 263 int nr_gp_counters = pmu_nr_gp_counters(); 264 pmu_counter_t cnt[10]; 265 int i, n; 266 267 for (i = 0, n = 0; n < nr_gp_counters; i++) { 268 if (!pmu_gp_counter_is_available(i)) 269 continue; 270 271 cnt[n].ctr = gp_counter_base + n; 272 cnt[n].config = EVNTSEL_OS | EVNTSEL_USR | 273 gp_events[i % ARRAY_SIZE(gp_events)].unit_sel; 274 n++; 275 } 276 for (i = 0; i < nr_fixed_counters; i++) { 277 cnt[n].ctr = fixed_events[i].unit_sel; 278 cnt[n].config = EVNTSEL_OS | EVNTSEL_USR; 279 n++; 280 } 281 282 measure_many(cnt, n); 283 284 for (i = 0; i < n; i++) 285 if (!verify_counter(&cnt[i])) 286 break; 287 288 report(i == n, "all counters"); 289 } 290 291 static uint64_t measure_for_overflow(pmu_counter_t *cnt) 292 { 293 __measure(cnt, 0); 294 /* 295 * To generate overflow, i.e. roll over to '0', the initial count just 296 * needs to be preset to the negative expected count. However, as per 297 * Intel's SDM, the preset count needs to be incremented by 1 to ensure 298 * the overflow interrupt is generated immediately instead of possibly 299 * waiting for the overflow to propagate through the counter. 300 */ 301 assert(cnt->count > 1); 302 return 1 - cnt->count; 303 } 304 305 static void check_counter_overflow(void) 306 { 307 int nr_gp_counters = pmu_nr_gp_counters(); 308 uint64_t overflow_preset; 309 int i; 310 pmu_counter_t cnt = { 311 .ctr = gp_counter_base, 312 .config = EVNTSEL_OS | EVNTSEL_USR | gp_events[1].unit_sel /* instructions */, 313 }; 314 overflow_preset = measure_for_overflow(&cnt); 315 316 /* clear status before test */ 317 wrmsr(MSR_CORE_PERF_GLOBAL_OVF_CTRL, rdmsr(MSR_CORE_PERF_GLOBAL_STATUS)); 318 319 report_prefix_push("overflow"); 320 321 for (i = 0; i < nr_gp_counters + 1; i++, cnt.ctr++) { 322 uint64_t status; 323 int idx; 324 325 cnt.count = overflow_preset; 326 if (gp_counter_base == MSR_IA32_PMC0) 327 cnt.count &= (1ull << pmu_gp_counter_width()) - 1; 328 329 if (i == nr_gp_counters) { 330 cnt.ctr = fixed_events[0].unit_sel; 331 cnt.count = measure_for_overflow(&cnt); 332 cnt.count &= (1ull << pmu_fixed_counter_width()) - 1; 333 } 334 335 if (i % 2) 336 cnt.config |= EVNTSEL_INT; 337 else 338 cnt.config &= ~EVNTSEL_INT; 339 idx = event_to_global_idx(&cnt); 340 __measure(&cnt, cnt.count); 341 report(cnt.count == 1, "cntr-%d", i); 342 status = rdmsr(MSR_CORE_PERF_GLOBAL_STATUS); 343 report(status & (1ull << idx), "status-%d", i); 344 wrmsr(MSR_CORE_PERF_GLOBAL_OVF_CTRL, status); 345 status = rdmsr(MSR_CORE_PERF_GLOBAL_STATUS); 346 report(!(status & (1ull << idx)), "status clear-%d", i); 347 report(check_irq() == (i % 2), "irq-%d", i); 348 } 349 350 report_prefix_pop(); 351 } 352 353 static void check_gp_counter_cmask(void) 354 { 355 pmu_counter_t cnt = { 356 .ctr = gp_counter_base, 357 .config = EVNTSEL_OS | EVNTSEL_USR | gp_events[1].unit_sel /* instructions */, 358 }; 359 cnt.config |= (0x2 << EVNTSEL_CMASK_SHIFT); 360 measure_one(&cnt); 361 report(cnt.count < gp_events[1].min, "cmask"); 362 } 363 364 static void do_rdpmc_fast(void *ptr) 365 { 366 pmu_counter_t *cnt = ptr; 367 uint32_t idx = (uint32_t)cnt->idx | (1u << 31); 368 369 if (!is_gp(cnt)) 370 idx |= 1 << 30; 371 372 cnt->count = rdpmc(idx); 373 } 374 375 376 static void check_rdpmc(void) 377 { 378 int fixed_counter_width = pmu_fixed_counter_width(); 379 int nr_fixed_counters = pmu_nr_fixed_counters(); 380 u8 gp_counter_width = pmu_gp_counter_width(); 381 int nr_gp_counters = pmu_nr_gp_counters(); 382 uint64_t val = 0xff0123456789ull; 383 bool exc; 384 int i; 385 386 report_prefix_push("rdpmc"); 387 388 for (i = 0; i < nr_gp_counters; i++) { 389 uint64_t x; 390 pmu_counter_t cnt = { 391 .ctr = gp_counter_base + i, 392 .idx = i 393 }; 394 395 /* 396 * Without full-width writes, only the low 32 bits are writable, 397 * and the value is sign-extended. 398 */ 399 if (gp_counter_base == MSR_IA32_PERFCTR0) 400 x = (uint64_t)(int64_t)(int32_t)val; 401 else 402 x = (uint64_t)(int64_t)val; 403 404 /* Mask according to the number of supported bits */ 405 x &= (1ull << gp_counter_width) - 1; 406 407 wrmsr(gp_counter_base + i, val); 408 report(rdpmc(i) == x, "cntr-%d", i); 409 410 exc = test_for_exception(GP_VECTOR, do_rdpmc_fast, &cnt); 411 if (exc) 412 report_skip("fast-%d", i); 413 else 414 report(cnt.count == (u32)val, "fast-%d", i); 415 } 416 for (i = 0; i < nr_fixed_counters; i++) { 417 uint64_t x = val & ((1ull << fixed_counter_width) - 1); 418 pmu_counter_t cnt = { 419 .ctr = MSR_CORE_PERF_FIXED_CTR0 + i, 420 .idx = i 421 }; 422 423 wrmsr(MSR_CORE_PERF_FIXED_CTR0 + i, x); 424 report(rdpmc(i | (1 << 30)) == x, "fixed cntr-%d", i); 425 426 exc = test_for_exception(GP_VECTOR, do_rdpmc_fast, &cnt); 427 if (exc) 428 report_skip("fixed fast-%d", i); 429 else 430 report(cnt.count == (u32)x, "fixed fast-%d", i); 431 } 432 433 report_prefix_pop(); 434 } 435 436 static void check_running_counter_wrmsr(void) 437 { 438 uint64_t status; 439 uint64_t count; 440 pmu_counter_t evt = { 441 .ctr = gp_counter_base, 442 .config = EVNTSEL_OS | EVNTSEL_USR | gp_events[1].unit_sel, 443 }; 444 445 report_prefix_push("running counter wrmsr"); 446 447 start_event(&evt); 448 loop(); 449 wrmsr(gp_counter_base, 0); 450 stop_event(&evt); 451 report(evt.count < gp_events[1].min, "cntr"); 452 453 /* clear status before overflow test */ 454 wrmsr(MSR_CORE_PERF_GLOBAL_OVF_CTRL, 455 rdmsr(MSR_CORE_PERF_GLOBAL_STATUS)); 456 457 start_event(&evt); 458 459 count = -1; 460 if (gp_counter_base == MSR_IA32_PMC0) 461 count &= (1ull << pmu_gp_counter_width()) - 1; 462 463 wrmsr(gp_counter_base, count); 464 465 loop(); 466 stop_event(&evt); 467 status = rdmsr(MSR_CORE_PERF_GLOBAL_STATUS); 468 report(status & 1, "status"); 469 470 report_prefix_pop(); 471 } 472 473 static void check_emulated_instr(void) 474 { 475 uint64_t status, instr_start, brnch_start; 476 pmu_counter_t brnch_cnt = { 477 .ctr = MSR_IA32_PERFCTR0, 478 /* branch instructions */ 479 .config = EVNTSEL_OS | EVNTSEL_USR | gp_events[5].unit_sel, 480 }; 481 pmu_counter_t instr_cnt = { 482 .ctr = MSR_IA32_PERFCTR0 + 1, 483 /* instructions */ 484 .config = EVNTSEL_OS | EVNTSEL_USR | gp_events[1].unit_sel, 485 }; 486 report_prefix_push("emulated instruction"); 487 488 wrmsr(MSR_CORE_PERF_GLOBAL_OVF_CTRL, 489 rdmsr(MSR_CORE_PERF_GLOBAL_STATUS)); 490 491 start_event(&brnch_cnt); 492 start_event(&instr_cnt); 493 494 brnch_start = -EXPECTED_BRNCH; 495 instr_start = -EXPECTED_INSTR; 496 wrmsr(MSR_IA32_PERFCTR0, brnch_start); 497 wrmsr(MSR_IA32_PERFCTR0 + 1, instr_start); 498 // KVM_FEP is a magic prefix that forces emulation so 499 // 'KVM_FEP "jne label\n"' just counts as a single instruction. 500 asm volatile( 501 "mov $0x0, %%eax\n" 502 "cmp $0x0, %%eax\n" 503 KVM_FEP "jne label\n" 504 KVM_FEP "jne label\n" 505 KVM_FEP "jne label\n" 506 KVM_FEP "jne label\n" 507 KVM_FEP "jne label\n" 508 "mov $0xa, %%eax\n" 509 "cpuid\n" 510 "mov $0xa, %%eax\n" 511 "cpuid\n" 512 "mov $0xa, %%eax\n" 513 "cpuid\n" 514 "mov $0xa, %%eax\n" 515 "cpuid\n" 516 "mov $0xa, %%eax\n" 517 "cpuid\n" 518 "label:\n" 519 : 520 : 521 : "eax", "ebx", "ecx", "edx"); 522 523 wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, 0); 524 525 stop_event(&brnch_cnt); 526 stop_event(&instr_cnt); 527 528 // Check that the end count - start count is at least the expected 529 // number of instructions and branches. 530 report(instr_cnt.count - instr_start >= EXPECTED_INSTR, 531 "instruction count"); 532 report(brnch_cnt.count - brnch_start >= EXPECTED_BRNCH, 533 "branch count"); 534 // Additionally check that those counters overflowed properly. 535 status = rdmsr(MSR_CORE_PERF_GLOBAL_STATUS); 536 report(status & 1, "branch counter overflow"); 537 report(status & 2, "instruction counter overflow"); 538 539 report_prefix_pop(); 540 } 541 542 static void check_counters(void) 543 { 544 if (is_fep_available()) 545 check_emulated_instr(); 546 547 check_gp_counters(); 548 check_fixed_counters(); 549 check_rdpmc(); 550 check_counters_many(); 551 check_counter_overflow(); 552 check_gp_counter_cmask(); 553 check_running_counter_wrmsr(); 554 } 555 556 static void do_unsupported_width_counter_write(void *index) 557 { 558 wrmsr(MSR_IA32_PMC0 + *((int *) index), 0xffffff0123456789ull); 559 } 560 561 static void check_gp_counters_write_width(void) 562 { 563 u64 val_64 = 0xffffff0123456789ull; 564 u64 val_32 = val_64 & ((1ull << 32) - 1); 565 u64 val_max_width = val_64 & ((1ull << pmu_gp_counter_width()) - 1); 566 int nr_gp_counters = pmu_nr_gp_counters(); 567 int i; 568 569 /* 570 * MSR_IA32_PERFCTRn supports 64-bit writes, 571 * but only the lowest 32 bits are valid. 572 */ 573 for (i = 0; i < nr_gp_counters; i++) { 574 wrmsr(MSR_IA32_PERFCTR0 + i, val_32); 575 assert(rdmsr(MSR_IA32_PERFCTR0 + i) == val_32); 576 assert(rdmsr(MSR_IA32_PMC0 + i) == val_32); 577 578 wrmsr(MSR_IA32_PERFCTR0 + i, val_max_width); 579 assert(rdmsr(MSR_IA32_PERFCTR0 + i) == val_32); 580 assert(rdmsr(MSR_IA32_PMC0 + i) == val_32); 581 582 wrmsr(MSR_IA32_PERFCTR0 + i, val_64); 583 assert(rdmsr(MSR_IA32_PERFCTR0 + i) == val_32); 584 assert(rdmsr(MSR_IA32_PMC0 + i) == val_32); 585 } 586 587 /* 588 * MSR_IA32_PMCn supports writing values up to GP counter width, 589 * and only the lowest bits of GP counter width are valid. 590 */ 591 for (i = 0; i < nr_gp_counters; i++) { 592 wrmsr(MSR_IA32_PMC0 + i, val_32); 593 assert(rdmsr(MSR_IA32_PMC0 + i) == val_32); 594 assert(rdmsr(MSR_IA32_PERFCTR0 + i) == val_32); 595 596 wrmsr(MSR_IA32_PMC0 + i, val_max_width); 597 assert(rdmsr(MSR_IA32_PMC0 + i) == val_max_width); 598 assert(rdmsr(MSR_IA32_PERFCTR0 + i) == val_max_width); 599 600 report(test_for_exception(GP_VECTOR, 601 do_unsupported_width_counter_write, &i), 602 "writing unsupported width to MSR_IA32_PMC%d raises #GP", i); 603 } 604 } 605 606 /* 607 * Per the SDM, reference cycles are currently implemented using the 608 * core crystal clock, TSC, or bus clock. Calibrate to the TSC 609 * frequency to set reasonable expectations. 610 */ 611 static void set_ref_cycle_expectations(void) 612 { 613 pmu_counter_t cnt = { 614 .ctr = MSR_IA32_PERFCTR0, 615 .config = EVNTSEL_OS | EVNTSEL_USR | gp_events[2].unit_sel, 616 }; 617 uint64_t tsc_delta; 618 uint64_t t0, t1, t2, t3; 619 620 /* Bit 2 enumerates the availability of reference cycles events. */ 621 if (!pmu_nr_gp_counters() || !pmu_gp_counter_is_available(2)) 622 return; 623 624 wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, 0); 625 626 t0 = fenced_rdtsc(); 627 start_event(&cnt); 628 t1 = fenced_rdtsc(); 629 630 /* 631 * This loop has to run long enough to dominate the VM-exit 632 * costs for playing with the PMU MSRs on start and stop. 633 * 634 * On a 2.6GHz Ice Lake, with the TSC frequency at 104 times 635 * the core crystal clock, this function calculated a guest 636 * TSC : ref cycles ratio of around 105 with ECX initialized 637 * to one billion. 638 */ 639 asm volatile("loop ." : "+c"((int){1000000000ull})); 640 641 t2 = fenced_rdtsc(); 642 stop_event(&cnt); 643 t3 = fenced_rdtsc(); 644 645 tsc_delta = ((t2 - t1) + (t3 - t0)) / 2; 646 647 if (!tsc_delta) 648 return; 649 650 gp_events[2].min = (gp_events[2].min * cnt.count) / tsc_delta; 651 gp_events[2].max = (gp_events[2].max * cnt.count) / tsc_delta; 652 } 653 654 int main(int ac, char **av) 655 { 656 setup_vm(); 657 handle_irq(PC_VECTOR, cnt_overflow); 658 buf = malloc(N*64); 659 660 if (!pmu_version()) { 661 report_skip("No pmu is detected!"); 662 return report_summary(); 663 } 664 665 if (pmu_version() == 1) { 666 report_skip("PMU version 1 is not supported."); 667 return report_summary(); 668 } 669 670 set_ref_cycle_expectations(); 671 672 printf("PMU version: %d\n", pmu_version()); 673 printf("GP counters: %d\n", pmu_nr_gp_counters()); 674 printf("GP counter width: %d\n", pmu_gp_counter_width()); 675 printf("Mask length: %d\n", pmu_gp_counter_mask_length()); 676 printf("Fixed counters: %d\n", pmu_nr_fixed_counters()); 677 printf("Fixed counter width: %d\n", pmu_fixed_counter_width()); 678 679 apic_write(APIC_LVTPC, PC_VECTOR); 680 681 check_counters(); 682 683 if (this_cpu_perf_capabilities() & PMU_CAP_FW_WRITES) { 684 gp_counter_base = MSR_IA32_PMC0; 685 report_prefix_push("full-width writes"); 686 check_counters(); 687 check_gp_counters_write_width(); 688 report_prefix_pop(); 689 } 690 691 return report_summary(); 692 } 693