1a9f8b16fSGleb Natapov 2a9f8b16fSGleb Natapov #include "x86/msr.h" 3a9f8b16fSGleb Natapov #include "x86/processor.h" 49f17508dSLike Xu #include "x86/pmu.h" 5a9f8b16fSGleb Natapov #include "x86/apic-defs.h" 6a9f8b16fSGleb Natapov #include "x86/apic.h" 7a9f8b16fSGleb Natapov #include "x86/desc.h" 8a9f8b16fSGleb Natapov #include "x86/isr.h" 9dcda215bSPaolo Bonzini #include "alloc.h" 10a9f8b16fSGleb Natapov 11a9f8b16fSGleb Natapov #include "libcflat.h" 12a9f8b16fSGleb Natapov #include <stdint.h> 13a9f8b16fSGleb Natapov 14a9f8b16fSGleb Natapov #define N 1000000 15a9f8b16fSGleb Natapov 1620cf9147SJim Mattson // These values match the number of instructions and branches in the 1720cf9147SJim Mattson // assembly block in check_emulated_instr(). 1820cf9147SJim Mattson #define EXPECTED_INSTR 17 1920cf9147SJim Mattson #define EXPECTED_BRNCH 5 2020cf9147SJim Mattson 21a9f8b16fSGleb Natapov typedef struct { 22a9f8b16fSGleb Natapov uint32_t ctr; 23a9f8b16fSGleb Natapov uint32_t config; 24a9f8b16fSGleb Natapov uint64_t count; 25a9f8b16fSGleb Natapov int idx; 26a9f8b16fSGleb Natapov } pmu_counter_t; 27a9f8b16fSGleb Natapov 28a9f8b16fSGleb Natapov struct pmu_event { 29797d79a2SThomas Huth const char *name; 30a9f8b16fSGleb Natapov uint32_t unit_sel; 31a9f8b16fSGleb Natapov int min; 32a9f8b16fSGleb Natapov int max; 33a9f8b16fSGleb Natapov } gp_events[] = { 34a9f8b16fSGleb Natapov {"core cycles", 0x003c, 1*N, 50*N}, 35a9f8b16fSGleb Natapov {"instructions", 0x00c0, 10*N, 10.2*N}, 36290f4213SJim Mattson {"ref cycles", 0x013c, 1*N, 30*N}, 37290f4213SJim Mattson {"llc references", 0x4f2e, 1, 2*N}, 38a9f8b16fSGleb Natapov {"llc misses", 0x412e, 1, 1*N}, 39a9f8b16fSGleb Natapov {"branches", 0x00c4, 1*N, 1.1*N}, 40a9f8b16fSGleb Natapov {"branch misses", 0x00c5, 0, 0.1*N}, 41a9f8b16fSGleb Natapov }, fixed_events[] = { 42a9f8b16fSGleb Natapov {"fixed 1", MSR_CORE_PERF_FIXED_CTR0, 10*N, 10.2*N}, 43a9f8b16fSGleb Natapov {"fixed 2", MSR_CORE_PERF_FIXED_CTR0 + 1, 1*N, 30*N}, 440ef1f6a8SPaolo Bonzini {"fixed 3", MSR_CORE_PERF_FIXED_CTR0 + 2, 0.1*N, 30*N} 45a9f8b16fSGleb Natapov }; 46a9f8b16fSGleb Natapov 47a9f8b16fSGleb Natapov char *buf; 48a9f8b16fSGleb Natapov 497db17e21SThomas Huth static inline void loop(void) 50a9f8b16fSGleb Natapov { 51a9f8b16fSGleb Natapov unsigned long tmp, tmp2, tmp3; 52a9f8b16fSGleb Natapov 53a9f8b16fSGleb Natapov asm volatile("1: mov (%1), %2; add $64, %1; nop; nop; nop; nop; nop; nop; nop; loop 1b" 54a9f8b16fSGleb Natapov : "=c"(tmp), "=r"(tmp2), "=r"(tmp3): "0"(N), "1"(buf)); 55a9f8b16fSGleb Natapov 56a9f8b16fSGleb Natapov } 57a9f8b16fSGleb Natapov 58a9f8b16fSGleb Natapov volatile uint64_t irq_received; 59a9f8b16fSGleb Natapov 60a9f8b16fSGleb Natapov static void cnt_overflow(isr_regs_t *regs) 61a9f8b16fSGleb Natapov { 62a9f8b16fSGleb Natapov irq_received++; 63a9f8b16fSGleb Natapov apic_write(APIC_EOI, 0); 64a9f8b16fSGleb Natapov } 65a9f8b16fSGleb Natapov 66a9f8b16fSGleb Natapov static bool check_irq(void) 67a9f8b16fSGleb Natapov { 68a9f8b16fSGleb Natapov int i; 69a9f8b16fSGleb Natapov irq_received = 0; 70a9f8b16fSGleb Natapov irq_enable(); 71a9f8b16fSGleb Natapov for (i = 0; i < 100000 && !irq_received; i++) 72a9f8b16fSGleb Natapov asm volatile("pause"); 73a9f8b16fSGleb Natapov irq_disable(); 74a9f8b16fSGleb Natapov return irq_received; 75a9f8b16fSGleb Natapov } 76a9f8b16fSGleb Natapov 77a9f8b16fSGleb Natapov static bool is_gp(pmu_counter_t *evt) 78a9f8b16fSGleb Natapov { 7922f2901aSLike Xu return evt->ctr < MSR_CORE_PERF_FIXED_CTR0 || 8022f2901aSLike Xu evt->ctr >= MSR_IA32_PMC0; 81a9f8b16fSGleb Natapov } 82a9f8b16fSGleb Natapov 83a9f8b16fSGleb Natapov static int event_to_global_idx(pmu_counter_t *cnt) 84a9f8b16fSGleb Natapov { 85cda64e80SLike Xu return cnt->ctr - (is_gp(cnt) ? pmu.msr_gp_counter_base : 86a9f8b16fSGleb Natapov (MSR_CORE_PERF_FIXED_CTR0 - FIXED_CNT_INDEX)); 87a9f8b16fSGleb Natapov } 88a9f8b16fSGleb Natapov 89a9f8b16fSGleb Natapov static struct pmu_event* get_counter_event(pmu_counter_t *cnt) 90a9f8b16fSGleb Natapov { 91a9f8b16fSGleb Natapov if (is_gp(cnt)) { 92a9f8b16fSGleb Natapov int i; 93a9f8b16fSGleb Natapov 94a9f8b16fSGleb Natapov for (i = 0; i < sizeof(gp_events)/sizeof(gp_events[0]); i++) 95a9f8b16fSGleb Natapov if (gp_events[i].unit_sel == (cnt->config & 0xffff)) 96a9f8b16fSGleb Natapov return &gp_events[i]; 97a9f8b16fSGleb Natapov } else 98a9f8b16fSGleb Natapov return &fixed_events[cnt->ctr - MSR_CORE_PERF_FIXED_CTR0]; 99a9f8b16fSGleb Natapov 100a9f8b16fSGleb Natapov return (void*)0; 101a9f8b16fSGleb Natapov } 102a9f8b16fSGleb Natapov 103a9f8b16fSGleb Natapov static void global_enable(pmu_counter_t *cnt) 104a9f8b16fSGleb Natapov { 105*62ba5036SLike Xu if (!this_cpu_has_perf_global_ctrl()) 106*62ba5036SLike Xu return; 107*62ba5036SLike Xu 108a9f8b16fSGleb Natapov cnt->idx = event_to_global_idx(cnt); 1098a2866d1SLike Xu wrmsr(pmu.msr_global_ctl, rdmsr(pmu.msr_global_ctl) | BIT_ULL(cnt->idx)); 110a9f8b16fSGleb Natapov } 111a9f8b16fSGleb Natapov 112a9f8b16fSGleb Natapov static void global_disable(pmu_counter_t *cnt) 113a9f8b16fSGleb Natapov { 114*62ba5036SLike Xu if (!this_cpu_has_perf_global_ctrl()) 115*62ba5036SLike Xu return; 116*62ba5036SLike Xu 1178a2866d1SLike Xu wrmsr(pmu.msr_global_ctl, rdmsr(pmu.msr_global_ctl) & ~BIT_ULL(cnt->idx)); 118a9f8b16fSGleb Natapov } 119a9f8b16fSGleb Natapov 120e9e7577bSLike Xu static void __start_event(pmu_counter_t *evt, uint64_t count) 121a9f8b16fSGleb Natapov { 122e9e7577bSLike Xu evt->count = count; 123a9f8b16fSGleb Natapov wrmsr(evt->ctr, evt->count); 124cda64e80SLike Xu if (is_gp(evt)) { 125cda64e80SLike Xu wrmsr(MSR_GP_EVENT_SELECTx(event_to_global_idx(evt)), 126a9f8b16fSGleb Natapov evt->config | EVNTSEL_EN); 127cda64e80SLike Xu } else { 128a9f8b16fSGleb Natapov uint32_t ctrl = rdmsr(MSR_CORE_PERF_FIXED_CTR_CTRL); 129a9f8b16fSGleb Natapov int shift = (evt->ctr - MSR_CORE_PERF_FIXED_CTR0) * 4; 130a9f8b16fSGleb Natapov uint32_t usrospmi = 0; 131a9f8b16fSGleb Natapov 132a9f8b16fSGleb Natapov if (evt->config & EVNTSEL_OS) 133a9f8b16fSGleb Natapov usrospmi |= (1 << 0); 134a9f8b16fSGleb Natapov if (evt->config & EVNTSEL_USR) 135a9f8b16fSGleb Natapov usrospmi |= (1 << 1); 136a9f8b16fSGleb Natapov if (evt->config & EVNTSEL_INT) 137a9f8b16fSGleb Natapov usrospmi |= (1 << 3); // PMI on overflow 138a9f8b16fSGleb Natapov ctrl = (ctrl & ~(0xf << shift)) | (usrospmi << shift); 139a9f8b16fSGleb Natapov wrmsr(MSR_CORE_PERF_FIXED_CTR_CTRL, ctrl); 140a9f8b16fSGleb Natapov } 141a9f8b16fSGleb Natapov global_enable(evt); 1425a2cb3e6SLike Xu apic_write(APIC_LVTPC, PMI_VECTOR); 143a9f8b16fSGleb Natapov } 144a9f8b16fSGleb Natapov 145e9e7577bSLike Xu static void start_event(pmu_counter_t *evt) 146e9e7577bSLike Xu { 147e9e7577bSLike Xu __start_event(evt, 0); 148e9e7577bSLike Xu } 149e9e7577bSLike Xu 150a9f8b16fSGleb Natapov static void stop_event(pmu_counter_t *evt) 151a9f8b16fSGleb Natapov { 152a9f8b16fSGleb Natapov global_disable(evt); 153cda64e80SLike Xu if (is_gp(evt)) { 154cda64e80SLike Xu wrmsr(MSR_GP_EVENT_SELECTx(event_to_global_idx(evt)), 155a9f8b16fSGleb Natapov evt->config & ~EVNTSEL_EN); 156cda64e80SLike Xu } else { 157a9f8b16fSGleb Natapov uint32_t ctrl = rdmsr(MSR_CORE_PERF_FIXED_CTR_CTRL); 158a9f8b16fSGleb Natapov int shift = (evt->ctr - MSR_CORE_PERF_FIXED_CTR0) * 4; 159a9f8b16fSGleb Natapov wrmsr(MSR_CORE_PERF_FIXED_CTR_CTRL, ctrl & ~(0xf << shift)); 160a9f8b16fSGleb Natapov } 161a9f8b16fSGleb Natapov evt->count = rdmsr(evt->ctr); 162a9f8b16fSGleb Natapov } 163a9f8b16fSGleb Natapov 1648554261fSLike Xu static noinline void measure_many(pmu_counter_t *evt, int count) 165a9f8b16fSGleb Natapov { 166a9f8b16fSGleb Natapov int i; 167a9f8b16fSGleb Natapov for (i = 0; i < count; i++) 168a9f8b16fSGleb Natapov start_event(&evt[i]); 169a9f8b16fSGleb Natapov loop(); 170a9f8b16fSGleb Natapov for (i = 0; i < count; i++) 171a9f8b16fSGleb Natapov stop_event(&evt[i]); 172a9f8b16fSGleb Natapov } 173a9f8b16fSGleb Natapov 1748554261fSLike Xu static void measure_one(pmu_counter_t *evt) 1758554261fSLike Xu { 1768554261fSLike Xu measure_many(evt, 1); 1778554261fSLike Xu } 1788554261fSLike Xu 179e9e7577bSLike Xu static noinline void __measure(pmu_counter_t *evt, uint64_t count) 180e9e7577bSLike Xu { 181e9e7577bSLike Xu __start_event(evt, count); 182e9e7577bSLike Xu loop(); 183e9e7577bSLike Xu stop_event(evt); 184e9e7577bSLike Xu } 185e9e7577bSLike Xu 186a9f8b16fSGleb Natapov static bool verify_event(uint64_t count, struct pmu_event *e) 187a9f8b16fSGleb Natapov { 188290f4213SJim Mattson // printf("%d <= %ld <= %d\n", e->min, count, e->max); 189a9f8b16fSGleb Natapov return count >= e->min && count <= e->max; 190a9f8b16fSGleb Natapov 191a9f8b16fSGleb Natapov } 192a9f8b16fSGleb Natapov 193a9f8b16fSGleb Natapov static bool verify_counter(pmu_counter_t *cnt) 194a9f8b16fSGleb Natapov { 195a9f8b16fSGleb Natapov return verify_event(cnt->count, get_counter_event(cnt)); 196a9f8b16fSGleb Natapov } 197a9f8b16fSGleb Natapov 198a9f8b16fSGleb Natapov static void check_gp_counter(struct pmu_event *evt) 199a9f8b16fSGleb Natapov { 200a9f8b16fSGleb Natapov pmu_counter_t cnt = { 201a9f8b16fSGleb Natapov .config = EVNTSEL_OS | EVNTSEL_USR | evt->unit_sel, 202a9f8b16fSGleb Natapov }; 203a9f8b16fSGleb Natapov int i; 204a9f8b16fSGleb Natapov 205cda64e80SLike Xu for (i = 0; i < pmu.nr_gp_counters; i++) { 206cda64e80SLike Xu cnt.ctr = MSR_GP_COUNTERx(i); 2078554261fSLike Xu measure_one(&cnt); 208a299895bSThomas Huth report(verify_event(cnt.count, evt), "%s-%d", evt->name, i); 209a9f8b16fSGleb Natapov } 210a9f8b16fSGleb Natapov } 211a9f8b16fSGleb Natapov 212a9f8b16fSGleb Natapov static void check_gp_counters(void) 213a9f8b16fSGleb Natapov { 214a9f8b16fSGleb Natapov int i; 215a9f8b16fSGleb Natapov 216a9f8b16fSGleb Natapov for (i = 0; i < sizeof(gp_events)/sizeof(gp_events[0]); i++) 2172719b92cSYang Weijiang if (pmu_gp_counter_is_available(i)) 218a9f8b16fSGleb Natapov check_gp_counter(&gp_events[i]); 219a9f8b16fSGleb Natapov else 220a9f8b16fSGleb Natapov printf("GP event '%s' is disabled\n", 221a9f8b16fSGleb Natapov gp_events[i].name); 222a9f8b16fSGleb Natapov } 223a9f8b16fSGleb Natapov 224a9f8b16fSGleb Natapov static void check_fixed_counters(void) 225a9f8b16fSGleb Natapov { 226a9f8b16fSGleb Natapov pmu_counter_t cnt = { 227a9f8b16fSGleb Natapov .config = EVNTSEL_OS | EVNTSEL_USR, 228a9f8b16fSGleb Natapov }; 229a9f8b16fSGleb Natapov int i; 230a9f8b16fSGleb Natapov 231414ee7d1SSean Christopherson for (i = 0; i < pmu.nr_fixed_counters; i++) { 232a9f8b16fSGleb Natapov cnt.ctr = fixed_events[i].unit_sel; 2338554261fSLike Xu measure_one(&cnt); 2342719b92cSYang Weijiang report(verify_event(cnt.count, &fixed_events[i]), "fixed-%d", i); 235a9f8b16fSGleb Natapov } 236a9f8b16fSGleb Natapov } 237a9f8b16fSGleb Natapov 238a9f8b16fSGleb Natapov static void check_counters_many(void) 239a9f8b16fSGleb Natapov { 240a9f8b16fSGleb Natapov pmu_counter_t cnt[10]; 241a9f8b16fSGleb Natapov int i, n; 242a9f8b16fSGleb Natapov 243414ee7d1SSean Christopherson for (i = 0, n = 0; n < pmu.nr_gp_counters; i++) { 2442719b92cSYang Weijiang if (!pmu_gp_counter_is_available(i)) 245a9f8b16fSGleb Natapov continue; 246a9f8b16fSGleb Natapov 247cda64e80SLike Xu cnt[n].ctr = MSR_GP_COUNTERx(n); 2484ac45293SWei Huang cnt[n].config = EVNTSEL_OS | EVNTSEL_USR | 2494ac45293SWei Huang gp_events[i % ARRAY_SIZE(gp_events)].unit_sel; 250a9f8b16fSGleb Natapov n++; 251a9f8b16fSGleb Natapov } 252414ee7d1SSean Christopherson for (i = 0; i < pmu.nr_fixed_counters; i++) { 253a9f8b16fSGleb Natapov cnt[n].ctr = fixed_events[i].unit_sel; 254a9f8b16fSGleb Natapov cnt[n].config = EVNTSEL_OS | EVNTSEL_USR; 255a9f8b16fSGleb Natapov n++; 256a9f8b16fSGleb Natapov } 257a9f8b16fSGleb Natapov 2588554261fSLike Xu measure_many(cnt, n); 259a9f8b16fSGleb Natapov 260a9f8b16fSGleb Natapov for (i = 0; i < n; i++) 261a9f8b16fSGleb Natapov if (!verify_counter(&cnt[i])) 262a9f8b16fSGleb Natapov break; 263a9f8b16fSGleb Natapov 264a299895bSThomas Huth report(i == n, "all counters"); 265a9f8b16fSGleb Natapov } 266a9f8b16fSGleb Natapov 2677ec3b67aSLike Xu static uint64_t measure_for_overflow(pmu_counter_t *cnt) 2687ec3b67aSLike Xu { 2697ec3b67aSLike Xu __measure(cnt, 0); 2707ec3b67aSLike Xu /* 2717ec3b67aSLike Xu * To generate overflow, i.e. roll over to '0', the initial count just 2727ec3b67aSLike Xu * needs to be preset to the negative expected count. However, as per 2737ec3b67aSLike Xu * Intel's SDM, the preset count needs to be incremented by 1 to ensure 2747ec3b67aSLike Xu * the overflow interrupt is generated immediately instead of possibly 2757ec3b67aSLike Xu * waiting for the overflow to propagate through the counter. 2767ec3b67aSLike Xu */ 2777ec3b67aSLike Xu assert(cnt->count > 1); 2787ec3b67aSLike Xu return 1 - cnt->count; 2797ec3b67aSLike Xu } 2807ec3b67aSLike Xu 281a9f8b16fSGleb Natapov static void check_counter_overflow(void) 282a9f8b16fSGleb Natapov { 2837ec3b67aSLike Xu uint64_t overflow_preset; 284a9f8b16fSGleb Natapov int i; 285a9f8b16fSGleb Natapov pmu_counter_t cnt = { 286cda64e80SLike Xu .ctr = MSR_GP_COUNTERx(0), 287a9f8b16fSGleb Natapov .config = EVNTSEL_OS | EVNTSEL_USR | gp_events[1].unit_sel /* instructions */, 288a9f8b16fSGleb Natapov }; 2897ec3b67aSLike Xu overflow_preset = measure_for_overflow(&cnt); 290a9f8b16fSGleb Natapov 291a9f8b16fSGleb Natapov /* clear status before test */ 292*62ba5036SLike Xu if (this_cpu_has_perf_global_status()) 2938a2866d1SLike Xu pmu_clear_global_status(); 294a9f8b16fSGleb Natapov 2955bba1769SAndrew Jones report_prefix_push("overflow"); 2965bba1769SAndrew Jones 297cda64e80SLike Xu for (i = 0; i < pmu.nr_gp_counters + 1; i++) { 298a9f8b16fSGleb Natapov uint64_t status; 299a9f8b16fSGleb Natapov int idx; 30033cfc1b0SNadav Amit 3017ec3b67aSLike Xu cnt.count = overflow_preset; 302cda64e80SLike Xu if (pmu_use_full_writes()) 303414ee7d1SSean Christopherson cnt.count &= (1ull << pmu.gp_counter_width) - 1; 30433cfc1b0SNadav Amit 305414ee7d1SSean Christopherson if (i == pmu.nr_gp_counters) { 306a9f8b16fSGleb Natapov cnt.ctr = fixed_events[0].unit_sel; 3077ec3b67aSLike Xu cnt.count = measure_for_overflow(&cnt); 308cda64e80SLike Xu cnt.count &= (1ull << pmu.gp_counter_width) - 1; 309cda64e80SLike Xu } else { 310cda64e80SLike Xu cnt.ctr = MSR_GP_COUNTERx(i); 31133cfc1b0SNadav Amit } 31233cfc1b0SNadav Amit 313a9f8b16fSGleb Natapov if (i % 2) 314a9f8b16fSGleb Natapov cnt.config |= EVNTSEL_INT; 315a9f8b16fSGleb Natapov else 316a9f8b16fSGleb Natapov cnt.config &= ~EVNTSEL_INT; 317a9f8b16fSGleb Natapov idx = event_to_global_idx(&cnt); 318e9e7577bSLike Xu __measure(&cnt, cnt.count); 319a299895bSThomas Huth report(cnt.count == 1, "cntr-%d", i); 320*62ba5036SLike Xu 321*62ba5036SLike Xu if (!this_cpu_has_perf_global_status()) 322*62ba5036SLike Xu continue; 323*62ba5036SLike Xu 3248a2866d1SLike Xu status = rdmsr(pmu.msr_global_status); 325a299895bSThomas Huth report(status & (1ull << idx), "status-%d", i); 3268a2866d1SLike Xu wrmsr(pmu.msr_global_status_clr, status); 3278a2866d1SLike Xu status = rdmsr(pmu.msr_global_status); 328a299895bSThomas Huth report(!(status & (1ull << idx)), "status clear-%d", i); 329a299895bSThomas Huth report(check_irq() == (i % 2), "irq-%d", i); 330a9f8b16fSGleb Natapov } 3315bba1769SAndrew Jones 3325bba1769SAndrew Jones report_prefix_pop(); 333a9f8b16fSGleb Natapov } 334a9f8b16fSGleb Natapov 335a9f8b16fSGleb Natapov static void check_gp_counter_cmask(void) 336a9f8b16fSGleb Natapov { 337a9f8b16fSGleb Natapov pmu_counter_t cnt = { 338cda64e80SLike Xu .ctr = MSR_GP_COUNTERx(0), 339a9f8b16fSGleb Natapov .config = EVNTSEL_OS | EVNTSEL_USR | gp_events[1].unit_sel /* instructions */, 340a9f8b16fSGleb Natapov }; 341a9f8b16fSGleb Natapov cnt.config |= (0x2 << EVNTSEL_CMASK_SHIFT); 3428554261fSLike Xu measure_one(&cnt); 343a299895bSThomas Huth report(cnt.count < gp_events[1].min, "cmask"); 344a9f8b16fSGleb Natapov } 345a9f8b16fSGleb Natapov 346ca1b9de9SNadav Amit static void do_rdpmc_fast(void *ptr) 347ca1b9de9SNadav Amit { 348ca1b9de9SNadav Amit pmu_counter_t *cnt = ptr; 349ca1b9de9SNadav Amit uint32_t idx = (uint32_t)cnt->idx | (1u << 31); 350ca1b9de9SNadav Amit 351ca1b9de9SNadav Amit if (!is_gp(cnt)) 352ca1b9de9SNadav Amit idx |= 1 << 30; 353ca1b9de9SNadav Amit 354ca1b9de9SNadav Amit cnt->count = rdpmc(idx); 355ca1b9de9SNadav Amit } 356ca1b9de9SNadav Amit 357ca1b9de9SNadav Amit 358a9f8b16fSGleb Natapov static void check_rdpmc(void) 359a9f8b16fSGleb Natapov { 36022f2901aSLike Xu uint64_t val = 0xff0123456789ull; 361ca1b9de9SNadav Amit bool exc; 362a9f8b16fSGleb Natapov int i; 363a9f8b16fSGleb Natapov 3645bba1769SAndrew Jones report_prefix_push("rdpmc"); 3655bba1769SAndrew Jones 366414ee7d1SSean Christopherson for (i = 0; i < pmu.nr_gp_counters; i++) { 36733cfc1b0SNadav Amit uint64_t x; 368ca1b9de9SNadav Amit pmu_counter_t cnt = { 369cda64e80SLike Xu .ctr = MSR_GP_COUNTERx(i), 370ca1b9de9SNadav Amit .idx = i 371ca1b9de9SNadav Amit }; 37233cfc1b0SNadav Amit 37333cfc1b0SNadav Amit /* 37422f2901aSLike Xu * Without full-width writes, only the low 32 bits are writable, 37522f2901aSLike Xu * and the value is sign-extended. 37633cfc1b0SNadav Amit */ 377cda64e80SLike Xu if (pmu.msr_gp_counter_base == MSR_IA32_PERFCTR0) 37833cfc1b0SNadav Amit x = (uint64_t)(int64_t)(int32_t)val; 37922f2901aSLike Xu else 38022f2901aSLike Xu x = (uint64_t)(int64_t)val; 38133cfc1b0SNadav Amit 38233cfc1b0SNadav Amit /* Mask according to the number of supported bits */ 383414ee7d1SSean Christopherson x &= (1ull << pmu.gp_counter_width) - 1; 38433cfc1b0SNadav Amit 385cda64e80SLike Xu wrmsr(MSR_GP_COUNTERx(i), val); 386a299895bSThomas Huth report(rdpmc(i) == x, "cntr-%d", i); 387ca1b9de9SNadav Amit 388ca1b9de9SNadav Amit exc = test_for_exception(GP_VECTOR, do_rdpmc_fast, &cnt); 389ca1b9de9SNadav Amit if (exc) 390ca1b9de9SNadav Amit report_skip("fast-%d", i); 391ca1b9de9SNadav Amit else 392a299895bSThomas Huth report(cnt.count == (u32)val, "fast-%d", i); 393a9f8b16fSGleb Natapov } 394414ee7d1SSean Christopherson for (i = 0; i < pmu.nr_fixed_counters; i++) { 395414ee7d1SSean Christopherson uint64_t x = val & ((1ull << pmu.fixed_counter_width) - 1); 396ca1b9de9SNadav Amit pmu_counter_t cnt = { 397ca1b9de9SNadav Amit .ctr = MSR_CORE_PERF_FIXED_CTR0 + i, 398ca1b9de9SNadav Amit .idx = i 399ca1b9de9SNadav Amit }; 40033cfc1b0SNadav Amit 4013f914933SLike Xu wrmsr(MSR_PERF_FIXED_CTRx(i), x); 402a299895bSThomas Huth report(rdpmc(i | (1 << 30)) == x, "fixed cntr-%d", i); 403ca1b9de9SNadav Amit 404ca1b9de9SNadav Amit exc = test_for_exception(GP_VECTOR, do_rdpmc_fast, &cnt); 405ca1b9de9SNadav Amit if (exc) 406ca1b9de9SNadav Amit report_skip("fixed fast-%d", i); 407ca1b9de9SNadav Amit else 408a299895bSThomas Huth report(cnt.count == (u32)x, "fixed fast-%d", i); 409a9f8b16fSGleb Natapov } 4105bba1769SAndrew Jones 4115bba1769SAndrew Jones report_prefix_pop(); 412a9f8b16fSGleb Natapov } 413a9f8b16fSGleb Natapov 414ddade902SEric Hankland static void check_running_counter_wrmsr(void) 415ddade902SEric Hankland { 41659ca1413SEric Hankland uint64_t status; 41722f2901aSLike Xu uint64_t count; 418ddade902SEric Hankland pmu_counter_t evt = { 419cda64e80SLike Xu .ctr = MSR_GP_COUNTERx(0), 420ddade902SEric Hankland .config = EVNTSEL_OS | EVNTSEL_USR | gp_events[1].unit_sel, 421ddade902SEric Hankland }; 422ddade902SEric Hankland 42359ca1413SEric Hankland report_prefix_push("running counter wrmsr"); 42459ca1413SEric Hankland 425ddade902SEric Hankland start_event(&evt); 426ddade902SEric Hankland loop(); 427cda64e80SLike Xu wrmsr(MSR_GP_COUNTERx(0), 0); 428ddade902SEric Hankland stop_event(&evt); 42959ca1413SEric Hankland report(evt.count < gp_events[1].min, "cntr"); 43059ca1413SEric Hankland 43159ca1413SEric Hankland /* clear status before overflow test */ 432*62ba5036SLike Xu if (this_cpu_has_perf_global_status()) 4338a2866d1SLike Xu pmu_clear_global_status(); 43459ca1413SEric Hankland 43559ca1413SEric Hankland start_event(&evt); 43622f2901aSLike Xu 43722f2901aSLike Xu count = -1; 438cda64e80SLike Xu if (pmu_use_full_writes()) 439414ee7d1SSean Christopherson count &= (1ull << pmu.gp_counter_width) - 1; 44022f2901aSLike Xu 441cda64e80SLike Xu wrmsr(MSR_GP_COUNTERx(0), count); 44222f2901aSLike Xu 44359ca1413SEric Hankland loop(); 44459ca1413SEric Hankland stop_event(&evt); 445*62ba5036SLike Xu 446*62ba5036SLike Xu if (this_cpu_has_perf_global_status()) { 4478a2866d1SLike Xu status = rdmsr(pmu.msr_global_status); 4488a2866d1SLike Xu report(status & 1, "status msr bit"); 449*62ba5036SLike Xu } 45059ca1413SEric Hankland 45159ca1413SEric Hankland report_prefix_pop(); 452ddade902SEric Hankland } 453ddade902SEric Hankland 45420cf9147SJim Mattson static void check_emulated_instr(void) 45520cf9147SJim Mattson { 45620cf9147SJim Mattson uint64_t status, instr_start, brnch_start; 45720cf9147SJim Mattson pmu_counter_t brnch_cnt = { 458cda64e80SLike Xu .ctr = MSR_GP_COUNTERx(0), 45920cf9147SJim Mattson /* branch instructions */ 46020cf9147SJim Mattson .config = EVNTSEL_OS | EVNTSEL_USR | gp_events[5].unit_sel, 46120cf9147SJim Mattson }; 46220cf9147SJim Mattson pmu_counter_t instr_cnt = { 463cda64e80SLike Xu .ctr = MSR_GP_COUNTERx(1), 46420cf9147SJim Mattson /* instructions */ 46520cf9147SJim Mattson .config = EVNTSEL_OS | EVNTSEL_USR | gp_events[1].unit_sel, 46620cf9147SJim Mattson }; 46720cf9147SJim Mattson report_prefix_push("emulated instruction"); 46820cf9147SJim Mattson 469*62ba5036SLike Xu if (this_cpu_has_perf_global_status()) 4708a2866d1SLike Xu pmu_clear_global_status(); 47120cf9147SJim Mattson 47220cf9147SJim Mattson start_event(&brnch_cnt); 47320cf9147SJim Mattson start_event(&instr_cnt); 47420cf9147SJim Mattson 47520cf9147SJim Mattson brnch_start = -EXPECTED_BRNCH; 47620cf9147SJim Mattson instr_start = -EXPECTED_INSTR; 477cda64e80SLike Xu wrmsr(MSR_GP_COUNTERx(0), brnch_start); 478cda64e80SLike Xu wrmsr(MSR_GP_COUNTERx(1), instr_start); 47920cf9147SJim Mattson // KVM_FEP is a magic prefix that forces emulation so 48020cf9147SJim Mattson // 'KVM_FEP "jne label\n"' just counts as a single instruction. 48120cf9147SJim Mattson asm volatile( 48220cf9147SJim Mattson "mov $0x0, %%eax\n" 48320cf9147SJim Mattson "cmp $0x0, %%eax\n" 48420cf9147SJim Mattson KVM_FEP "jne label\n" 48520cf9147SJim Mattson KVM_FEP "jne label\n" 48620cf9147SJim Mattson KVM_FEP "jne label\n" 48720cf9147SJim Mattson KVM_FEP "jne label\n" 48820cf9147SJim Mattson KVM_FEP "jne label\n" 48920cf9147SJim Mattson "mov $0xa, %%eax\n" 49020cf9147SJim Mattson "cpuid\n" 49120cf9147SJim Mattson "mov $0xa, %%eax\n" 49220cf9147SJim Mattson "cpuid\n" 49320cf9147SJim Mattson "mov $0xa, %%eax\n" 49420cf9147SJim Mattson "cpuid\n" 49520cf9147SJim Mattson "mov $0xa, %%eax\n" 49620cf9147SJim Mattson "cpuid\n" 49720cf9147SJim Mattson "mov $0xa, %%eax\n" 49820cf9147SJim Mattson "cpuid\n" 49920cf9147SJim Mattson "label:\n" 50020cf9147SJim Mattson : 50120cf9147SJim Mattson : 50220cf9147SJim Mattson : "eax", "ebx", "ecx", "edx"); 50320cf9147SJim Mattson 504*62ba5036SLike Xu if (this_cpu_has_perf_global_ctrl()) 5058a2866d1SLike Xu wrmsr(pmu.msr_global_ctl, 0); 50620cf9147SJim Mattson 50720cf9147SJim Mattson stop_event(&brnch_cnt); 50820cf9147SJim Mattson stop_event(&instr_cnt); 50920cf9147SJim Mattson 51020cf9147SJim Mattson // Check that the end count - start count is at least the expected 51120cf9147SJim Mattson // number of instructions and branches. 51220cf9147SJim Mattson report(instr_cnt.count - instr_start >= EXPECTED_INSTR, 51320cf9147SJim Mattson "instruction count"); 51420cf9147SJim Mattson report(brnch_cnt.count - brnch_start >= EXPECTED_BRNCH, 51520cf9147SJim Mattson "branch count"); 516*62ba5036SLike Xu if (this_cpu_has_perf_global_status()) { 51720cf9147SJim Mattson // Additionally check that those counters overflowed properly. 5188a2866d1SLike Xu status = rdmsr(pmu.msr_global_status); 5194070b9c6SLike Xu report(status & 1, "branch counter overflow"); 5204070b9c6SLike Xu report(status & 2, "instruction counter overflow"); 521*62ba5036SLike Xu } 52220cf9147SJim Mattson 52320cf9147SJim Mattson report_prefix_pop(); 52420cf9147SJim Mattson } 52520cf9147SJim Mattson 52622f2901aSLike Xu static void check_counters(void) 52722f2901aSLike Xu { 52800dca75cSLike Xu if (is_fep_available()) 52900dca75cSLike Xu check_emulated_instr(); 53000dca75cSLike Xu 53122f2901aSLike Xu check_gp_counters(); 53222f2901aSLike Xu check_fixed_counters(); 53322f2901aSLike Xu check_rdpmc(); 53422f2901aSLike Xu check_counters_many(); 53522f2901aSLike Xu check_counter_overflow(); 53622f2901aSLike Xu check_gp_counter_cmask(); 53722f2901aSLike Xu check_running_counter_wrmsr(); 53822f2901aSLike Xu } 53922f2901aSLike Xu 54022f2901aSLike Xu static void do_unsupported_width_counter_write(void *index) 54122f2901aSLike Xu { 54222f2901aSLike Xu wrmsr(MSR_IA32_PMC0 + *((int *) index), 0xffffff0123456789ull); 54322f2901aSLike Xu } 54422f2901aSLike Xu 54522f2901aSLike Xu static void check_gp_counters_write_width(void) 54622f2901aSLike Xu { 54722f2901aSLike Xu u64 val_64 = 0xffffff0123456789ull; 5484b74c718SThomas Huth u64 val_32 = val_64 & ((1ull << 32) - 1); 549414ee7d1SSean Christopherson u64 val_max_width = val_64 & ((1ull << pmu.gp_counter_width) - 1); 55022f2901aSLike Xu int i; 55122f2901aSLike Xu 55222f2901aSLike Xu /* 55322f2901aSLike Xu * MSR_IA32_PERFCTRn supports 64-bit writes, 55422f2901aSLike Xu * but only the lowest 32 bits are valid. 55522f2901aSLike Xu */ 556414ee7d1SSean Christopherson for (i = 0; i < pmu.nr_gp_counters; i++) { 55722f2901aSLike Xu wrmsr(MSR_IA32_PERFCTR0 + i, val_32); 55822f2901aSLike Xu assert(rdmsr(MSR_IA32_PERFCTR0 + i) == val_32); 55922f2901aSLike Xu assert(rdmsr(MSR_IA32_PMC0 + i) == val_32); 56022f2901aSLike Xu 56122f2901aSLike Xu wrmsr(MSR_IA32_PERFCTR0 + i, val_max_width); 56222f2901aSLike Xu assert(rdmsr(MSR_IA32_PERFCTR0 + i) == val_32); 56322f2901aSLike Xu assert(rdmsr(MSR_IA32_PMC0 + i) == val_32); 56422f2901aSLike Xu 56522f2901aSLike Xu wrmsr(MSR_IA32_PERFCTR0 + i, val_64); 56622f2901aSLike Xu assert(rdmsr(MSR_IA32_PERFCTR0 + i) == val_32); 56722f2901aSLike Xu assert(rdmsr(MSR_IA32_PMC0 + i) == val_32); 56822f2901aSLike Xu } 56922f2901aSLike Xu 57022f2901aSLike Xu /* 5714340720eSLike Xu * MSR_IA32_PMCn supports writing values up to GP counter width, 57222f2901aSLike Xu * and only the lowest bits of GP counter width are valid. 57322f2901aSLike Xu */ 574414ee7d1SSean Christopherson for (i = 0; i < pmu.nr_gp_counters; i++) { 57522f2901aSLike Xu wrmsr(MSR_IA32_PMC0 + i, val_32); 57622f2901aSLike Xu assert(rdmsr(MSR_IA32_PMC0 + i) == val_32); 57722f2901aSLike Xu assert(rdmsr(MSR_IA32_PERFCTR0 + i) == val_32); 57822f2901aSLike Xu 57922f2901aSLike Xu wrmsr(MSR_IA32_PMC0 + i, val_max_width); 58022f2901aSLike Xu assert(rdmsr(MSR_IA32_PMC0 + i) == val_max_width); 58122f2901aSLike Xu assert(rdmsr(MSR_IA32_PERFCTR0 + i) == val_max_width); 58222f2901aSLike Xu 58322f2901aSLike Xu report(test_for_exception(GP_VECTOR, 58422f2901aSLike Xu do_unsupported_width_counter_write, &i), 58522f2901aSLike Xu "writing unsupported width to MSR_IA32_PMC%d raises #GP", i); 58622f2901aSLike Xu } 58722f2901aSLike Xu } 58822f2901aSLike Xu 589290f4213SJim Mattson /* 590290f4213SJim Mattson * Per the SDM, reference cycles are currently implemented using the 591290f4213SJim Mattson * core crystal clock, TSC, or bus clock. Calibrate to the TSC 592290f4213SJim Mattson * frequency to set reasonable expectations. 593290f4213SJim Mattson */ 594290f4213SJim Mattson static void set_ref_cycle_expectations(void) 595290f4213SJim Mattson { 596290f4213SJim Mattson pmu_counter_t cnt = { 597290f4213SJim Mattson .ctr = MSR_IA32_PERFCTR0, 598290f4213SJim Mattson .config = EVNTSEL_OS | EVNTSEL_USR | gp_events[2].unit_sel, 599290f4213SJim Mattson }; 600290f4213SJim Mattson uint64_t tsc_delta; 601290f4213SJim Mattson uint64_t t0, t1, t2, t3; 602290f4213SJim Mattson 6032719b92cSYang Weijiang /* Bit 2 enumerates the availability of reference cycles events. */ 604414ee7d1SSean Christopherson if (!pmu.nr_gp_counters || !pmu_gp_counter_is_available(2)) 605290f4213SJim Mattson return; 606290f4213SJim Mattson 607*62ba5036SLike Xu if (this_cpu_has_perf_global_ctrl()) 6088a2866d1SLike Xu wrmsr(pmu.msr_global_ctl, 0); 609290f4213SJim Mattson 610290f4213SJim Mattson t0 = fenced_rdtsc(); 611290f4213SJim Mattson start_event(&cnt); 612290f4213SJim Mattson t1 = fenced_rdtsc(); 613290f4213SJim Mattson 614290f4213SJim Mattson /* 615290f4213SJim Mattson * This loop has to run long enough to dominate the VM-exit 616290f4213SJim Mattson * costs for playing with the PMU MSRs on start and stop. 617290f4213SJim Mattson * 618290f4213SJim Mattson * On a 2.6GHz Ice Lake, with the TSC frequency at 104 times 619290f4213SJim Mattson * the core crystal clock, this function calculated a guest 620290f4213SJim Mattson * TSC : ref cycles ratio of around 105 with ECX initialized 621290f4213SJim Mattson * to one billion. 622290f4213SJim Mattson */ 623290f4213SJim Mattson asm volatile("loop ." : "+c"((int){1000000000ull})); 624290f4213SJim Mattson 625290f4213SJim Mattson t2 = fenced_rdtsc(); 626290f4213SJim Mattson stop_event(&cnt); 627290f4213SJim Mattson t3 = fenced_rdtsc(); 628290f4213SJim Mattson 629290f4213SJim Mattson tsc_delta = ((t2 - t1) + (t3 - t0)) / 2; 630290f4213SJim Mattson 631290f4213SJim Mattson if (!tsc_delta) 632290f4213SJim Mattson return; 633290f4213SJim Mattson 634290f4213SJim Mattson gp_events[2].min = (gp_events[2].min * cnt.count) / tsc_delta; 635290f4213SJim Mattson gp_events[2].max = (gp_events[2].max * cnt.count) / tsc_delta; 636290f4213SJim Mattson } 637290f4213SJim Mattson 63885c21181SLike Xu static void check_invalid_rdpmc_gp(void) 63985c21181SLike Xu { 64085c21181SLike Xu uint64_t val; 64185c21181SLike Xu 64285c21181SLike Xu report(rdpmc_safe(64, &val) == GP_VECTOR, 64385c21181SLike Xu "Expected #GP on RDPMC(64)"); 64485c21181SLike Xu } 64585c21181SLike Xu 646a9f8b16fSGleb Natapov int main(int ac, char **av) 647a9f8b16fSGleb Natapov { 648a9f8b16fSGleb Natapov setup_vm(); 6495a2cb3e6SLike Xu handle_irq(PMI_VECTOR, cnt_overflow); 650dcda215bSPaolo Bonzini buf = malloc(N*64); 651a9f8b16fSGleb Natapov 65285c21181SLike Xu check_invalid_rdpmc_gp(); 65385c21181SLike Xu 654414ee7d1SSean Christopherson if (!pmu.version) { 65503041e97SLike Xu report_skip("No Intel Arch PMU is detected!"); 65632b9603cSRadim Krčmář return report_summary(); 657a9f8b16fSGleb Natapov } 65870972e21SNadav Amit 659290f4213SJim Mattson set_ref_cycle_expectations(); 660290f4213SJim Mattson 661414ee7d1SSean Christopherson printf("PMU version: %d\n", pmu.version); 662414ee7d1SSean Christopherson printf("GP counters: %d\n", pmu.nr_gp_counters); 663414ee7d1SSean Christopherson printf("GP counter width: %d\n", pmu.gp_counter_width); 664414ee7d1SSean Christopherson printf("Mask length: %d\n", pmu.gp_counter_mask_length); 665414ee7d1SSean Christopherson printf("Fixed counters: %d\n", pmu.nr_fixed_counters); 666414ee7d1SSean Christopherson printf("Fixed counter width: %d\n", pmu.fixed_counter_width); 6670ef1f6a8SPaolo Bonzini 6685a2cb3e6SLike Xu apic_write(APIC_LVTPC, PMI_VECTOR); 669a9f8b16fSGleb Natapov 670afa714b2SPaolo Bonzini check_counters(); 67120cf9147SJim Mattson 672879e7f07SLike Xu if (pmu_has_full_writes()) { 673cda64e80SLike Xu pmu.msr_gp_counter_base = MSR_IA32_PMC0; 674cda64e80SLike Xu 67522f2901aSLike Xu report_prefix_push("full-width writes"); 67622f2901aSLike Xu check_counters(); 67722f2901aSLike Xu check_gp_counters_write_width(); 678d7714e16SLike Xu report_prefix_pop(); 67922f2901aSLike Xu } 680a9f8b16fSGleb Natapov 681f3cdd159SJan Kiszka return report_summary(); 682a9f8b16fSGleb Natapov } 683