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