1a9f8b16fSGleb Natapov 2a9f8b16fSGleb Natapov #include "x86/msr.h" 3a9f8b16fSGleb Natapov #include "x86/processor.h" 4a9f8b16fSGleb Natapov #include "x86/apic-defs.h" 5a9f8b16fSGleb Natapov #include "x86/apic.h" 6a9f8b16fSGleb Natapov #include "x86/desc.h" 7a9f8b16fSGleb Natapov #include "x86/isr.h" 8dcda215bSPaolo Bonzini #include "alloc.h" 9a9f8b16fSGleb Natapov 10a9f8b16fSGleb Natapov #include "libcflat.h" 11a9f8b16fSGleb Natapov #include <stdint.h> 12a9f8b16fSGleb Natapov 13a9f8b16fSGleb Natapov #define FIXED_CNT_INDEX 32 14a9f8b16fSGleb Natapov #define PC_VECTOR 32 15a9f8b16fSGleb Natapov 16a9f8b16fSGleb Natapov #define EVNSEL_EVENT_SHIFT 0 17a9f8b16fSGleb Natapov #define EVNTSEL_UMASK_SHIFT 8 18a9f8b16fSGleb Natapov #define EVNTSEL_USR_SHIFT 16 19a9f8b16fSGleb Natapov #define EVNTSEL_OS_SHIFT 17 20a9f8b16fSGleb Natapov #define EVNTSEL_EDGE_SHIFT 18 21a9f8b16fSGleb Natapov #define EVNTSEL_PC_SHIFT 19 22a9f8b16fSGleb Natapov #define EVNTSEL_INT_SHIFT 20 23a9f8b16fSGleb Natapov #define EVNTSEL_EN_SHIF 22 24a9f8b16fSGleb Natapov #define EVNTSEL_INV_SHIF 23 25a9f8b16fSGleb Natapov #define EVNTSEL_CMASK_SHIFT 24 26a9f8b16fSGleb Natapov 27a9f8b16fSGleb Natapov #define EVNTSEL_EN (1 << EVNTSEL_EN_SHIF) 28a9f8b16fSGleb Natapov #define EVNTSEL_USR (1 << EVNTSEL_USR_SHIFT) 29a9f8b16fSGleb Natapov #define EVNTSEL_OS (1 << EVNTSEL_OS_SHIFT) 30a9f8b16fSGleb Natapov #define EVNTSEL_PC (1 << EVNTSEL_PC_SHIFT) 31a9f8b16fSGleb Natapov #define EVNTSEL_INT (1 << EVNTSEL_INT_SHIFT) 32a9f8b16fSGleb Natapov #define EVNTSEL_INV (1 << EVNTSEL_INV_SHIF) 33a9f8b16fSGleb Natapov 34a9f8b16fSGleb Natapov #define N 1000000 35a9f8b16fSGleb Natapov 3620cf9147SJim Mattson // These values match the number of instructions and branches in the 3720cf9147SJim Mattson // assembly block in check_emulated_instr(). 3820cf9147SJim Mattson #define EXPECTED_INSTR 17 3920cf9147SJim Mattson #define EXPECTED_BRNCH 5 4020cf9147SJim Mattson 41a9f8b16fSGleb Natapov typedef struct { 42a9f8b16fSGleb Natapov uint32_t ctr; 43a9f8b16fSGleb Natapov uint32_t config; 44a9f8b16fSGleb Natapov uint64_t count; 45a9f8b16fSGleb Natapov int idx; 46a9f8b16fSGleb Natapov } pmu_counter_t; 47a9f8b16fSGleb Natapov 48a9f8b16fSGleb Natapov struct pmu_event { 49797d79a2SThomas Huth const char *name; 50a9f8b16fSGleb Natapov uint32_t unit_sel; 51a9f8b16fSGleb Natapov int min; 52a9f8b16fSGleb Natapov int max; 53a9f8b16fSGleb Natapov } gp_events[] = { 54a9f8b16fSGleb Natapov {"core cycles", 0x003c, 1*N, 50*N}, 55a9f8b16fSGleb Natapov {"instructions", 0x00c0, 10*N, 10.2*N}, 56290f4213SJim Mattson {"ref cycles", 0x013c, 1*N, 30*N}, 57290f4213SJim Mattson {"llc references", 0x4f2e, 1, 2*N}, 58a9f8b16fSGleb Natapov {"llc misses", 0x412e, 1, 1*N}, 59a9f8b16fSGleb Natapov {"branches", 0x00c4, 1*N, 1.1*N}, 60a9f8b16fSGleb Natapov {"branch misses", 0x00c5, 0, 0.1*N}, 61a9f8b16fSGleb Natapov }, fixed_events[] = { 62a9f8b16fSGleb Natapov {"fixed 1", MSR_CORE_PERF_FIXED_CTR0, 10*N, 10.2*N}, 63a9f8b16fSGleb Natapov {"fixed 2", MSR_CORE_PERF_FIXED_CTR0 + 1, 1*N, 30*N}, 640ef1f6a8SPaolo Bonzini {"fixed 3", MSR_CORE_PERF_FIXED_CTR0 + 2, 0.1*N, 30*N} 65a9f8b16fSGleb Natapov }; 66a9f8b16fSGleb Natapov 6722f2901aSLike Xu #define PMU_CAP_FW_WRITES (1ULL << 13) 6822f2901aSLike Xu static u64 gp_counter_base = MSR_IA32_PERFCTR0; 6922f2901aSLike Xu 70a9f8b16fSGleb Natapov char *buf; 71a9f8b16fSGleb Natapov 727db17e21SThomas Huth static inline void loop(void) 73a9f8b16fSGleb Natapov { 74a9f8b16fSGleb Natapov unsigned long tmp, tmp2, tmp3; 75a9f8b16fSGleb Natapov 76a9f8b16fSGleb Natapov asm volatile("1: mov (%1), %2; add $64, %1; nop; nop; nop; nop; nop; nop; nop; loop 1b" 77a9f8b16fSGleb Natapov : "=c"(tmp), "=r"(tmp2), "=r"(tmp3): "0"(N), "1"(buf)); 78a9f8b16fSGleb Natapov 79a9f8b16fSGleb Natapov } 80a9f8b16fSGleb Natapov 81a9f8b16fSGleb Natapov volatile uint64_t irq_received; 82a9f8b16fSGleb Natapov 83a9f8b16fSGleb Natapov static void cnt_overflow(isr_regs_t *regs) 84a9f8b16fSGleb Natapov { 85a9f8b16fSGleb Natapov irq_received++; 86a9f8b16fSGleb Natapov apic_write(APIC_EOI, 0); 87a9f8b16fSGleb Natapov } 88a9f8b16fSGleb Natapov 89a9f8b16fSGleb Natapov static bool check_irq(void) 90a9f8b16fSGleb Natapov { 91a9f8b16fSGleb Natapov int i; 92a9f8b16fSGleb Natapov irq_received = 0; 93a9f8b16fSGleb Natapov irq_enable(); 94a9f8b16fSGleb Natapov for (i = 0; i < 100000 && !irq_received; i++) 95a9f8b16fSGleb Natapov asm volatile("pause"); 96a9f8b16fSGleb Natapov irq_disable(); 97a9f8b16fSGleb Natapov return irq_received; 98a9f8b16fSGleb Natapov } 99a9f8b16fSGleb Natapov 100a9f8b16fSGleb Natapov static bool is_gp(pmu_counter_t *evt) 101a9f8b16fSGleb Natapov { 10222f2901aSLike Xu return evt->ctr < MSR_CORE_PERF_FIXED_CTR0 || 10322f2901aSLike Xu evt->ctr >= MSR_IA32_PMC0; 104a9f8b16fSGleb Natapov } 105a9f8b16fSGleb Natapov 106a9f8b16fSGleb Natapov static int event_to_global_idx(pmu_counter_t *cnt) 107a9f8b16fSGleb Natapov { 10822f2901aSLike Xu return cnt->ctr - (is_gp(cnt) ? gp_counter_base : 109a9f8b16fSGleb Natapov (MSR_CORE_PERF_FIXED_CTR0 - FIXED_CNT_INDEX)); 110a9f8b16fSGleb Natapov } 111a9f8b16fSGleb Natapov 112a9f8b16fSGleb Natapov static struct pmu_event* get_counter_event(pmu_counter_t *cnt) 113a9f8b16fSGleb Natapov { 114a9f8b16fSGleb Natapov if (is_gp(cnt)) { 115a9f8b16fSGleb Natapov int i; 116a9f8b16fSGleb Natapov 117a9f8b16fSGleb Natapov for (i = 0; i < sizeof(gp_events)/sizeof(gp_events[0]); i++) 118a9f8b16fSGleb Natapov if (gp_events[i].unit_sel == (cnt->config & 0xffff)) 119a9f8b16fSGleb Natapov return &gp_events[i]; 120a9f8b16fSGleb Natapov } else 121a9f8b16fSGleb Natapov return &fixed_events[cnt->ctr - MSR_CORE_PERF_FIXED_CTR0]; 122a9f8b16fSGleb Natapov 123a9f8b16fSGleb Natapov return (void*)0; 124a9f8b16fSGleb Natapov } 125a9f8b16fSGleb Natapov 126a9f8b16fSGleb Natapov static void global_enable(pmu_counter_t *cnt) 127a9f8b16fSGleb Natapov { 128a9f8b16fSGleb Natapov cnt->idx = event_to_global_idx(cnt); 129a9f8b16fSGleb Natapov 130a9f8b16fSGleb Natapov wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, rdmsr(MSR_CORE_PERF_GLOBAL_CTRL) | 131a9f8b16fSGleb Natapov (1ull << cnt->idx)); 132a9f8b16fSGleb Natapov } 133a9f8b16fSGleb Natapov 134a9f8b16fSGleb Natapov static void global_disable(pmu_counter_t *cnt) 135a9f8b16fSGleb Natapov { 136a9f8b16fSGleb Natapov wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, rdmsr(MSR_CORE_PERF_GLOBAL_CTRL) & 137a9f8b16fSGleb Natapov ~(1ull << cnt->idx)); 138a9f8b16fSGleb Natapov } 139a9f8b16fSGleb Natapov 140e9e7577bSLike Xu static void __start_event(pmu_counter_t *evt, uint64_t count) 141a9f8b16fSGleb Natapov { 142e9e7577bSLike Xu evt->count = count; 143a9f8b16fSGleb Natapov wrmsr(evt->ctr, evt->count); 144a9f8b16fSGleb Natapov if (is_gp(evt)) 145a9f8b16fSGleb Natapov wrmsr(MSR_P6_EVNTSEL0 + event_to_global_idx(evt), 146a9f8b16fSGleb Natapov evt->config | EVNTSEL_EN); 147a9f8b16fSGleb Natapov else { 148a9f8b16fSGleb Natapov uint32_t ctrl = rdmsr(MSR_CORE_PERF_FIXED_CTR_CTRL); 149a9f8b16fSGleb Natapov int shift = (evt->ctr - MSR_CORE_PERF_FIXED_CTR0) * 4; 150a9f8b16fSGleb Natapov uint32_t usrospmi = 0; 151a9f8b16fSGleb Natapov 152a9f8b16fSGleb Natapov if (evt->config & EVNTSEL_OS) 153a9f8b16fSGleb Natapov usrospmi |= (1 << 0); 154a9f8b16fSGleb Natapov if (evt->config & EVNTSEL_USR) 155a9f8b16fSGleb Natapov usrospmi |= (1 << 1); 156a9f8b16fSGleb Natapov if (evt->config & EVNTSEL_INT) 157a9f8b16fSGleb Natapov usrospmi |= (1 << 3); // PMI on overflow 158a9f8b16fSGleb Natapov ctrl = (ctrl & ~(0xf << shift)) | (usrospmi << shift); 159a9f8b16fSGleb Natapov wrmsr(MSR_CORE_PERF_FIXED_CTR_CTRL, ctrl); 160a9f8b16fSGleb Natapov } 161a9f8b16fSGleb Natapov global_enable(evt); 162bb6ede96SNadav Amit apic_write(APIC_LVTPC, PC_VECTOR); 163a9f8b16fSGleb Natapov } 164a9f8b16fSGleb Natapov 165e9e7577bSLike Xu static void start_event(pmu_counter_t *evt) 166e9e7577bSLike Xu { 167e9e7577bSLike Xu __start_event(evt, 0); 168e9e7577bSLike Xu } 169e9e7577bSLike Xu 170a9f8b16fSGleb Natapov static void stop_event(pmu_counter_t *evt) 171a9f8b16fSGleb Natapov { 172a9f8b16fSGleb Natapov global_disable(evt); 173a9f8b16fSGleb Natapov if (is_gp(evt)) 174a9f8b16fSGleb Natapov wrmsr(MSR_P6_EVNTSEL0 + event_to_global_idx(evt), 175a9f8b16fSGleb Natapov evt->config & ~EVNTSEL_EN); 176a9f8b16fSGleb Natapov else { 177a9f8b16fSGleb Natapov uint32_t ctrl = rdmsr(MSR_CORE_PERF_FIXED_CTR_CTRL); 178a9f8b16fSGleb Natapov int shift = (evt->ctr - MSR_CORE_PERF_FIXED_CTR0) * 4; 179a9f8b16fSGleb Natapov wrmsr(MSR_CORE_PERF_FIXED_CTR_CTRL, ctrl & ~(0xf << shift)); 180a9f8b16fSGleb Natapov } 181a9f8b16fSGleb Natapov evt->count = rdmsr(evt->ctr); 182a9f8b16fSGleb Natapov } 183a9f8b16fSGleb Natapov 184*8554261fSLike Xu static noinline void measure_many(pmu_counter_t *evt, int count) 185a9f8b16fSGleb Natapov { 186a9f8b16fSGleb Natapov int i; 187a9f8b16fSGleb Natapov for (i = 0; i < count; i++) 188a9f8b16fSGleb Natapov start_event(&evt[i]); 189a9f8b16fSGleb Natapov loop(); 190a9f8b16fSGleb Natapov for (i = 0; i < count; i++) 191a9f8b16fSGleb Natapov stop_event(&evt[i]); 192a9f8b16fSGleb Natapov } 193a9f8b16fSGleb Natapov 194*8554261fSLike Xu static void measure_one(pmu_counter_t *evt) 195*8554261fSLike Xu { 196*8554261fSLike Xu measure_many(evt, 1); 197*8554261fSLike Xu } 198*8554261fSLike Xu 199e9e7577bSLike Xu static noinline void __measure(pmu_counter_t *evt, uint64_t count) 200e9e7577bSLike Xu { 201e9e7577bSLike Xu __start_event(evt, count); 202e9e7577bSLike Xu loop(); 203e9e7577bSLike Xu stop_event(evt); 204e9e7577bSLike Xu } 205e9e7577bSLike Xu 206a9f8b16fSGleb Natapov static bool verify_event(uint64_t count, struct pmu_event *e) 207a9f8b16fSGleb Natapov { 208290f4213SJim Mattson // printf("%d <= %ld <= %d\n", e->min, count, e->max); 209a9f8b16fSGleb Natapov return count >= e->min && count <= e->max; 210a9f8b16fSGleb Natapov 211a9f8b16fSGleb Natapov } 212a9f8b16fSGleb Natapov 213a9f8b16fSGleb Natapov static bool verify_counter(pmu_counter_t *cnt) 214a9f8b16fSGleb Natapov { 215a9f8b16fSGleb Natapov return verify_event(cnt->count, get_counter_event(cnt)); 216a9f8b16fSGleb Natapov } 217a9f8b16fSGleb Natapov 218a9f8b16fSGleb Natapov static void check_gp_counter(struct pmu_event *evt) 219a9f8b16fSGleb Natapov { 2202719b92cSYang Weijiang int nr_gp_counters = pmu_nr_gp_counters(); 221a9f8b16fSGleb Natapov pmu_counter_t cnt = { 22222f2901aSLike Xu .ctr = gp_counter_base, 223a9f8b16fSGleb Natapov .config = EVNTSEL_OS | EVNTSEL_USR | evt->unit_sel, 224a9f8b16fSGleb Natapov }; 225a9f8b16fSGleb Natapov int i; 226a9f8b16fSGleb Natapov 2272719b92cSYang Weijiang for (i = 0; i < nr_gp_counters; i++, cnt.ctr++) { 228*8554261fSLike Xu measure_one(&cnt); 229a299895bSThomas Huth report(verify_event(cnt.count, evt), "%s-%d", evt->name, i); 230a9f8b16fSGleb Natapov } 231a9f8b16fSGleb Natapov } 232a9f8b16fSGleb Natapov 233a9f8b16fSGleb Natapov static void check_gp_counters(void) 234a9f8b16fSGleb Natapov { 235a9f8b16fSGleb Natapov int i; 236a9f8b16fSGleb Natapov 237a9f8b16fSGleb Natapov for (i = 0; i < sizeof(gp_events)/sizeof(gp_events[0]); i++) 2382719b92cSYang Weijiang if (pmu_gp_counter_is_available(i)) 239a9f8b16fSGleb Natapov check_gp_counter(&gp_events[i]); 240a9f8b16fSGleb Natapov else 241a9f8b16fSGleb Natapov printf("GP event '%s' is disabled\n", 242a9f8b16fSGleb Natapov gp_events[i].name); 243a9f8b16fSGleb Natapov } 244a9f8b16fSGleb Natapov 245a9f8b16fSGleb Natapov static void check_fixed_counters(void) 246a9f8b16fSGleb Natapov { 2472719b92cSYang Weijiang int nr_fixed_counters = pmu_nr_fixed_counters(); 248a9f8b16fSGleb Natapov pmu_counter_t cnt = { 249a9f8b16fSGleb Natapov .config = EVNTSEL_OS | EVNTSEL_USR, 250a9f8b16fSGleb Natapov }; 251a9f8b16fSGleb Natapov int i; 252a9f8b16fSGleb Natapov 2532719b92cSYang Weijiang for (i = 0; i < nr_fixed_counters; i++) { 254a9f8b16fSGleb Natapov cnt.ctr = fixed_events[i].unit_sel; 255*8554261fSLike Xu measure_one(&cnt); 2562719b92cSYang Weijiang report(verify_event(cnt.count, &fixed_events[i]), "fixed-%d", i); 257a9f8b16fSGleb Natapov } 258a9f8b16fSGleb Natapov } 259a9f8b16fSGleb Natapov 260a9f8b16fSGleb Natapov static void check_counters_many(void) 261a9f8b16fSGleb Natapov { 2622719b92cSYang Weijiang int nr_fixed_counters = pmu_nr_fixed_counters(); 2632719b92cSYang Weijiang int nr_gp_counters = pmu_nr_gp_counters(); 264a9f8b16fSGleb Natapov pmu_counter_t cnt[10]; 265a9f8b16fSGleb Natapov int i, n; 266a9f8b16fSGleb Natapov 2672719b92cSYang Weijiang for (i = 0, n = 0; n < nr_gp_counters; i++) { 2682719b92cSYang Weijiang if (!pmu_gp_counter_is_available(i)) 269a9f8b16fSGleb Natapov continue; 270a9f8b16fSGleb Natapov 27122f2901aSLike Xu cnt[n].ctr = gp_counter_base + n; 2724ac45293SWei Huang cnt[n].config = EVNTSEL_OS | EVNTSEL_USR | 2734ac45293SWei Huang gp_events[i % ARRAY_SIZE(gp_events)].unit_sel; 274a9f8b16fSGleb Natapov n++; 275a9f8b16fSGleb Natapov } 2762719b92cSYang Weijiang for (i = 0; i < nr_fixed_counters; i++) { 277a9f8b16fSGleb Natapov cnt[n].ctr = fixed_events[i].unit_sel; 278a9f8b16fSGleb Natapov cnt[n].config = EVNTSEL_OS | EVNTSEL_USR; 279a9f8b16fSGleb Natapov n++; 280a9f8b16fSGleb Natapov } 281a9f8b16fSGleb Natapov 282*8554261fSLike Xu measure_many(cnt, n); 283a9f8b16fSGleb Natapov 284a9f8b16fSGleb Natapov for (i = 0; i < n; i++) 285a9f8b16fSGleb Natapov if (!verify_counter(&cnt[i])) 286a9f8b16fSGleb Natapov break; 287a9f8b16fSGleb Natapov 288a299895bSThomas Huth report(i == n, "all counters"); 289a9f8b16fSGleb Natapov } 290a9f8b16fSGleb Natapov 291a9f8b16fSGleb Natapov static void check_counter_overflow(void) 292a9f8b16fSGleb Natapov { 2932719b92cSYang Weijiang int nr_gp_counters = pmu_nr_gp_counters(); 294a9f8b16fSGleb Natapov uint64_t count; 295a9f8b16fSGleb Natapov int i; 296a9f8b16fSGleb Natapov pmu_counter_t cnt = { 29722f2901aSLike Xu .ctr = gp_counter_base, 298a9f8b16fSGleb Natapov .config = EVNTSEL_OS | EVNTSEL_USR | gp_events[1].unit_sel /* instructions */, 299a9f8b16fSGleb Natapov }; 300e9e7577bSLike Xu __measure(&cnt, 0); 301a9f8b16fSGleb Natapov count = cnt.count; 302a9f8b16fSGleb Natapov 303a9f8b16fSGleb Natapov /* clear status before test */ 304a9f8b16fSGleb Natapov wrmsr(MSR_CORE_PERF_GLOBAL_OVF_CTRL, rdmsr(MSR_CORE_PERF_GLOBAL_STATUS)); 305a9f8b16fSGleb Natapov 3065bba1769SAndrew Jones report_prefix_push("overflow"); 3075bba1769SAndrew Jones 3082719b92cSYang Weijiang for (i = 0; i < nr_gp_counters + 1; i++, cnt.ctr++) { 309a9f8b16fSGleb Natapov uint64_t status; 310a9f8b16fSGleb Natapov int idx; 31133cfc1b0SNadav Amit 31233cfc1b0SNadav Amit cnt.count = 1 - count; 31322f2901aSLike Xu if (gp_counter_base == MSR_IA32_PMC0) 3142719b92cSYang Weijiang cnt.count &= (1ull << pmu_gp_counter_width()) - 1; 31533cfc1b0SNadav Amit 3162719b92cSYang Weijiang if (i == nr_gp_counters) { 317a9f8b16fSGleb Natapov cnt.ctr = fixed_events[0].unit_sel; 3182719b92cSYang Weijiang cnt.count &= (1ull << pmu_fixed_counter_width()) - 1; 31933cfc1b0SNadav Amit } 32033cfc1b0SNadav Amit 321a9f8b16fSGleb Natapov if (i % 2) 322a9f8b16fSGleb Natapov cnt.config |= EVNTSEL_INT; 323a9f8b16fSGleb Natapov else 324a9f8b16fSGleb Natapov cnt.config &= ~EVNTSEL_INT; 325a9f8b16fSGleb Natapov idx = event_to_global_idx(&cnt); 326e9e7577bSLike Xu __measure(&cnt, cnt.count); 327a299895bSThomas Huth report(cnt.count == 1, "cntr-%d", i); 328a9f8b16fSGleb Natapov status = rdmsr(MSR_CORE_PERF_GLOBAL_STATUS); 329a299895bSThomas Huth report(status & (1ull << idx), "status-%d", i); 330a9f8b16fSGleb Natapov wrmsr(MSR_CORE_PERF_GLOBAL_OVF_CTRL, status); 331a9f8b16fSGleb Natapov status = rdmsr(MSR_CORE_PERF_GLOBAL_STATUS); 332a299895bSThomas Huth report(!(status & (1ull << idx)), "status clear-%d", i); 333a299895bSThomas Huth report(check_irq() == (i % 2), "irq-%d", i); 334a9f8b16fSGleb Natapov } 3355bba1769SAndrew Jones 3365bba1769SAndrew Jones report_prefix_pop(); 337a9f8b16fSGleb Natapov } 338a9f8b16fSGleb Natapov 339a9f8b16fSGleb Natapov static void check_gp_counter_cmask(void) 340a9f8b16fSGleb Natapov { 341a9f8b16fSGleb Natapov pmu_counter_t cnt = { 34222f2901aSLike Xu .ctr = gp_counter_base, 343a9f8b16fSGleb Natapov .config = EVNTSEL_OS | EVNTSEL_USR | gp_events[1].unit_sel /* instructions */, 344a9f8b16fSGleb Natapov }; 345a9f8b16fSGleb Natapov cnt.config |= (0x2 << EVNTSEL_CMASK_SHIFT); 346*8554261fSLike Xu measure_one(&cnt); 347a299895bSThomas Huth report(cnt.count < gp_events[1].min, "cmask"); 348a9f8b16fSGleb Natapov } 349a9f8b16fSGleb Natapov 350ca1b9de9SNadav Amit static void do_rdpmc_fast(void *ptr) 351ca1b9de9SNadav Amit { 352ca1b9de9SNadav Amit pmu_counter_t *cnt = ptr; 353ca1b9de9SNadav Amit uint32_t idx = (uint32_t)cnt->idx | (1u << 31); 354ca1b9de9SNadav Amit 355ca1b9de9SNadav Amit if (!is_gp(cnt)) 356ca1b9de9SNadav Amit idx |= 1 << 30; 357ca1b9de9SNadav Amit 358ca1b9de9SNadav Amit cnt->count = rdpmc(idx); 359ca1b9de9SNadav Amit } 360ca1b9de9SNadav Amit 361ca1b9de9SNadav Amit 362a9f8b16fSGleb Natapov static void check_rdpmc(void) 363a9f8b16fSGleb Natapov { 3642719b92cSYang Weijiang int fixed_counter_width = pmu_fixed_counter_width(); 3652719b92cSYang Weijiang int nr_fixed_counters = pmu_nr_fixed_counters(); 3662719b92cSYang Weijiang u8 gp_counter_width = pmu_gp_counter_width(); 3672719b92cSYang Weijiang int nr_gp_counters = pmu_nr_gp_counters(); 36822f2901aSLike Xu uint64_t val = 0xff0123456789ull; 369ca1b9de9SNadav Amit bool exc; 370a9f8b16fSGleb Natapov int i; 371a9f8b16fSGleb Natapov 3725bba1769SAndrew Jones report_prefix_push("rdpmc"); 3735bba1769SAndrew Jones 3742719b92cSYang Weijiang for (i = 0; i < nr_gp_counters; i++) { 37533cfc1b0SNadav Amit uint64_t x; 376ca1b9de9SNadav Amit pmu_counter_t cnt = { 37722f2901aSLike Xu .ctr = gp_counter_base + i, 378ca1b9de9SNadav Amit .idx = i 379ca1b9de9SNadav Amit }; 38033cfc1b0SNadav Amit 38133cfc1b0SNadav Amit /* 38222f2901aSLike Xu * Without full-width writes, only the low 32 bits are writable, 38322f2901aSLike Xu * and the value is sign-extended. 38433cfc1b0SNadav Amit */ 38522f2901aSLike Xu if (gp_counter_base == MSR_IA32_PERFCTR0) 38633cfc1b0SNadav Amit x = (uint64_t)(int64_t)(int32_t)val; 38722f2901aSLike Xu else 38822f2901aSLike Xu x = (uint64_t)(int64_t)val; 38933cfc1b0SNadav Amit 39033cfc1b0SNadav Amit /* Mask according to the number of supported bits */ 3912719b92cSYang Weijiang x &= (1ull << gp_counter_width) - 1; 39233cfc1b0SNadav Amit 39322f2901aSLike Xu wrmsr(gp_counter_base + i, val); 394a299895bSThomas Huth report(rdpmc(i) == x, "cntr-%d", i); 395ca1b9de9SNadav Amit 396ca1b9de9SNadav Amit exc = test_for_exception(GP_VECTOR, do_rdpmc_fast, &cnt); 397ca1b9de9SNadav Amit if (exc) 398ca1b9de9SNadav Amit report_skip("fast-%d", i); 399ca1b9de9SNadav Amit else 400a299895bSThomas Huth report(cnt.count == (u32)val, "fast-%d", i); 401a9f8b16fSGleb Natapov } 4022719b92cSYang Weijiang for (i = 0; i < nr_fixed_counters; i++) { 4032719b92cSYang Weijiang uint64_t x = val & ((1ull << fixed_counter_width) - 1); 404ca1b9de9SNadav Amit pmu_counter_t cnt = { 405ca1b9de9SNadav Amit .ctr = MSR_CORE_PERF_FIXED_CTR0 + i, 406ca1b9de9SNadav Amit .idx = i 407ca1b9de9SNadav Amit }; 40833cfc1b0SNadav Amit 40933cfc1b0SNadav Amit wrmsr(MSR_CORE_PERF_FIXED_CTR0 + i, x); 410a299895bSThomas Huth report(rdpmc(i | (1 << 30)) == x, "fixed cntr-%d", i); 411ca1b9de9SNadav Amit 412ca1b9de9SNadav Amit exc = test_for_exception(GP_VECTOR, do_rdpmc_fast, &cnt); 413ca1b9de9SNadav Amit if (exc) 414ca1b9de9SNadav Amit report_skip("fixed fast-%d", i); 415ca1b9de9SNadav Amit else 416a299895bSThomas Huth report(cnt.count == (u32)x, "fixed fast-%d", i); 417a9f8b16fSGleb Natapov } 4185bba1769SAndrew Jones 4195bba1769SAndrew Jones report_prefix_pop(); 420a9f8b16fSGleb Natapov } 421a9f8b16fSGleb Natapov 422ddade902SEric Hankland static void check_running_counter_wrmsr(void) 423ddade902SEric Hankland { 42459ca1413SEric Hankland uint64_t status; 42522f2901aSLike Xu uint64_t count; 426ddade902SEric Hankland pmu_counter_t evt = { 42722f2901aSLike Xu .ctr = gp_counter_base, 428ddade902SEric Hankland .config = EVNTSEL_OS | EVNTSEL_USR | gp_events[1].unit_sel, 429ddade902SEric Hankland }; 430ddade902SEric Hankland 43159ca1413SEric Hankland report_prefix_push("running counter wrmsr"); 43259ca1413SEric Hankland 433ddade902SEric Hankland start_event(&evt); 434ddade902SEric Hankland loop(); 43522f2901aSLike Xu wrmsr(gp_counter_base, 0); 436ddade902SEric Hankland stop_event(&evt); 43759ca1413SEric Hankland report(evt.count < gp_events[1].min, "cntr"); 43859ca1413SEric Hankland 43959ca1413SEric Hankland /* clear status before overflow test */ 44059ca1413SEric Hankland wrmsr(MSR_CORE_PERF_GLOBAL_OVF_CTRL, 44159ca1413SEric Hankland rdmsr(MSR_CORE_PERF_GLOBAL_STATUS)); 44259ca1413SEric Hankland 44359ca1413SEric Hankland start_event(&evt); 44422f2901aSLike Xu 44522f2901aSLike Xu count = -1; 44622f2901aSLike Xu if (gp_counter_base == MSR_IA32_PMC0) 4472719b92cSYang Weijiang count &= (1ull << pmu_gp_counter_width()) - 1; 44822f2901aSLike Xu 44922f2901aSLike Xu wrmsr(gp_counter_base, count); 45022f2901aSLike Xu 45159ca1413SEric Hankland loop(); 45259ca1413SEric Hankland stop_event(&evt); 45359ca1413SEric Hankland status = rdmsr(MSR_CORE_PERF_GLOBAL_STATUS); 45459ca1413SEric Hankland report(status & 1, "status"); 45559ca1413SEric Hankland 45659ca1413SEric Hankland report_prefix_pop(); 457ddade902SEric Hankland } 458ddade902SEric Hankland 45920cf9147SJim Mattson static void check_emulated_instr(void) 46020cf9147SJim Mattson { 46120cf9147SJim Mattson uint64_t status, instr_start, brnch_start; 46220cf9147SJim Mattson pmu_counter_t brnch_cnt = { 46320cf9147SJim Mattson .ctr = MSR_IA32_PERFCTR0, 46420cf9147SJim Mattson /* branch instructions */ 46520cf9147SJim Mattson .config = EVNTSEL_OS | EVNTSEL_USR | gp_events[5].unit_sel, 46620cf9147SJim Mattson }; 46720cf9147SJim Mattson pmu_counter_t instr_cnt = { 46820cf9147SJim Mattson .ctr = MSR_IA32_PERFCTR0 + 1, 46920cf9147SJim Mattson /* instructions */ 47020cf9147SJim Mattson .config = EVNTSEL_OS | EVNTSEL_USR | gp_events[1].unit_sel, 47120cf9147SJim Mattson }; 47220cf9147SJim Mattson report_prefix_push("emulated instruction"); 47320cf9147SJim Mattson 47420cf9147SJim Mattson wrmsr(MSR_CORE_PERF_GLOBAL_OVF_CTRL, 47520cf9147SJim Mattson rdmsr(MSR_CORE_PERF_GLOBAL_STATUS)); 47620cf9147SJim Mattson 47720cf9147SJim Mattson start_event(&brnch_cnt); 47820cf9147SJim Mattson start_event(&instr_cnt); 47920cf9147SJim Mattson 48020cf9147SJim Mattson brnch_start = -EXPECTED_BRNCH; 48120cf9147SJim Mattson instr_start = -EXPECTED_INSTR; 48220cf9147SJim Mattson wrmsr(MSR_IA32_PERFCTR0, brnch_start); 48320cf9147SJim Mattson wrmsr(MSR_IA32_PERFCTR0 + 1, instr_start); 48420cf9147SJim Mattson // KVM_FEP is a magic prefix that forces emulation so 48520cf9147SJim Mattson // 'KVM_FEP "jne label\n"' just counts as a single instruction. 48620cf9147SJim Mattson asm volatile( 48720cf9147SJim Mattson "mov $0x0, %%eax\n" 48820cf9147SJim Mattson "cmp $0x0, %%eax\n" 48920cf9147SJim Mattson KVM_FEP "jne label\n" 49020cf9147SJim Mattson KVM_FEP "jne label\n" 49120cf9147SJim Mattson KVM_FEP "jne label\n" 49220cf9147SJim Mattson KVM_FEP "jne label\n" 49320cf9147SJim Mattson KVM_FEP "jne label\n" 49420cf9147SJim Mattson "mov $0xa, %%eax\n" 49520cf9147SJim Mattson "cpuid\n" 49620cf9147SJim Mattson "mov $0xa, %%eax\n" 49720cf9147SJim Mattson "cpuid\n" 49820cf9147SJim Mattson "mov $0xa, %%eax\n" 49920cf9147SJim Mattson "cpuid\n" 50020cf9147SJim Mattson "mov $0xa, %%eax\n" 50120cf9147SJim Mattson "cpuid\n" 50220cf9147SJim Mattson "mov $0xa, %%eax\n" 50320cf9147SJim Mattson "cpuid\n" 50420cf9147SJim Mattson "label:\n" 50520cf9147SJim Mattson : 50620cf9147SJim Mattson : 50720cf9147SJim Mattson : "eax", "ebx", "ecx", "edx"); 50820cf9147SJim Mattson 50920cf9147SJim Mattson wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, 0); 51020cf9147SJim Mattson 51120cf9147SJim Mattson stop_event(&brnch_cnt); 51220cf9147SJim Mattson stop_event(&instr_cnt); 51320cf9147SJim Mattson 51420cf9147SJim Mattson // Check that the end count - start count is at least the expected 51520cf9147SJim Mattson // number of instructions and branches. 51620cf9147SJim Mattson report(instr_cnt.count - instr_start >= EXPECTED_INSTR, 51720cf9147SJim Mattson "instruction count"); 51820cf9147SJim Mattson report(brnch_cnt.count - brnch_start >= EXPECTED_BRNCH, 51920cf9147SJim Mattson "branch count"); 52020cf9147SJim Mattson // Additionally check that those counters overflowed properly. 52120cf9147SJim Mattson status = rdmsr(MSR_CORE_PERF_GLOBAL_STATUS); 5224070b9c6SLike Xu report(status & 1, "branch counter overflow"); 5234070b9c6SLike Xu report(status & 2, "instruction counter overflow"); 52420cf9147SJim Mattson 52520cf9147SJim Mattson report_prefix_pop(); 52620cf9147SJim Mattson } 52720cf9147SJim Mattson 52822f2901aSLike Xu static void check_counters(void) 52922f2901aSLike Xu { 53000dca75cSLike Xu if (is_fep_available()) 53100dca75cSLike Xu check_emulated_instr(); 53200dca75cSLike Xu 53322f2901aSLike Xu check_gp_counters(); 53422f2901aSLike Xu check_fixed_counters(); 53522f2901aSLike Xu check_rdpmc(); 53622f2901aSLike Xu check_counters_many(); 53722f2901aSLike Xu check_counter_overflow(); 53822f2901aSLike Xu check_gp_counter_cmask(); 53922f2901aSLike Xu check_running_counter_wrmsr(); 54022f2901aSLike Xu } 54122f2901aSLike Xu 54222f2901aSLike Xu static void do_unsupported_width_counter_write(void *index) 54322f2901aSLike Xu { 54422f2901aSLike Xu wrmsr(MSR_IA32_PMC0 + *((int *) index), 0xffffff0123456789ull); 54522f2901aSLike Xu } 54622f2901aSLike Xu 54722f2901aSLike Xu static void check_gp_counters_write_width(void) 54822f2901aSLike Xu { 54922f2901aSLike Xu u64 val_64 = 0xffffff0123456789ull; 5504b74c718SThomas Huth u64 val_32 = val_64 & ((1ull << 32) - 1); 5512719b92cSYang Weijiang u64 val_max_width = val_64 & ((1ull << pmu_gp_counter_width()) - 1); 5522719b92cSYang Weijiang int nr_gp_counters = pmu_nr_gp_counters(); 55322f2901aSLike Xu int i; 55422f2901aSLike Xu 55522f2901aSLike Xu /* 55622f2901aSLike Xu * MSR_IA32_PERFCTRn supports 64-bit writes, 55722f2901aSLike Xu * but only the lowest 32 bits are valid. 55822f2901aSLike Xu */ 5592719b92cSYang Weijiang for (i = 0; i < nr_gp_counters; i++) { 56022f2901aSLike Xu wrmsr(MSR_IA32_PERFCTR0 + i, val_32); 56122f2901aSLike Xu assert(rdmsr(MSR_IA32_PERFCTR0 + i) == val_32); 56222f2901aSLike Xu assert(rdmsr(MSR_IA32_PMC0 + i) == val_32); 56322f2901aSLike Xu 56422f2901aSLike Xu wrmsr(MSR_IA32_PERFCTR0 + i, val_max_width); 56522f2901aSLike Xu assert(rdmsr(MSR_IA32_PERFCTR0 + i) == val_32); 56622f2901aSLike Xu assert(rdmsr(MSR_IA32_PMC0 + i) == val_32); 56722f2901aSLike Xu 56822f2901aSLike Xu wrmsr(MSR_IA32_PERFCTR0 + i, val_64); 56922f2901aSLike Xu assert(rdmsr(MSR_IA32_PERFCTR0 + i) == val_32); 57022f2901aSLike Xu assert(rdmsr(MSR_IA32_PMC0 + i) == val_32); 57122f2901aSLike Xu } 57222f2901aSLike Xu 57322f2901aSLike Xu /* 5744340720eSLike Xu * MSR_IA32_PMCn supports writing values up to GP counter width, 57522f2901aSLike Xu * and only the lowest bits of GP counter width are valid. 57622f2901aSLike Xu */ 5772719b92cSYang Weijiang for (i = 0; i < nr_gp_counters; i++) { 57822f2901aSLike Xu wrmsr(MSR_IA32_PMC0 + i, val_32); 57922f2901aSLike Xu assert(rdmsr(MSR_IA32_PMC0 + i) == val_32); 58022f2901aSLike Xu assert(rdmsr(MSR_IA32_PERFCTR0 + i) == val_32); 58122f2901aSLike Xu 58222f2901aSLike Xu wrmsr(MSR_IA32_PMC0 + i, val_max_width); 58322f2901aSLike Xu assert(rdmsr(MSR_IA32_PMC0 + i) == val_max_width); 58422f2901aSLike Xu assert(rdmsr(MSR_IA32_PERFCTR0 + i) == val_max_width); 58522f2901aSLike Xu 58622f2901aSLike Xu report(test_for_exception(GP_VECTOR, 58722f2901aSLike Xu do_unsupported_width_counter_write, &i), 58822f2901aSLike Xu "writing unsupported width to MSR_IA32_PMC%d raises #GP", i); 58922f2901aSLike Xu } 59022f2901aSLike Xu } 59122f2901aSLike Xu 592290f4213SJim Mattson /* 593290f4213SJim Mattson * Per the SDM, reference cycles are currently implemented using the 594290f4213SJim Mattson * core crystal clock, TSC, or bus clock. Calibrate to the TSC 595290f4213SJim Mattson * frequency to set reasonable expectations. 596290f4213SJim Mattson */ 597290f4213SJim Mattson static void set_ref_cycle_expectations(void) 598290f4213SJim Mattson { 599290f4213SJim Mattson pmu_counter_t cnt = { 600290f4213SJim Mattson .ctr = MSR_IA32_PERFCTR0, 601290f4213SJim Mattson .config = EVNTSEL_OS | EVNTSEL_USR | gp_events[2].unit_sel, 602290f4213SJim Mattson }; 603290f4213SJim Mattson uint64_t tsc_delta; 604290f4213SJim Mattson uint64_t t0, t1, t2, t3; 605290f4213SJim Mattson 6062719b92cSYang Weijiang /* Bit 2 enumerates the availability of reference cycles events. */ 6072719b92cSYang Weijiang if (!pmu_nr_gp_counters() || !pmu_gp_counter_is_available(2)) 608290f4213SJim Mattson return; 609290f4213SJim Mattson 610290f4213SJim Mattson wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, 0); 611290f4213SJim Mattson 612290f4213SJim Mattson t0 = fenced_rdtsc(); 613290f4213SJim Mattson start_event(&cnt); 614290f4213SJim Mattson t1 = fenced_rdtsc(); 615290f4213SJim Mattson 616290f4213SJim Mattson /* 617290f4213SJim Mattson * This loop has to run long enough to dominate the VM-exit 618290f4213SJim Mattson * costs for playing with the PMU MSRs on start and stop. 619290f4213SJim Mattson * 620290f4213SJim Mattson * On a 2.6GHz Ice Lake, with the TSC frequency at 104 times 621290f4213SJim Mattson * the core crystal clock, this function calculated a guest 622290f4213SJim Mattson * TSC : ref cycles ratio of around 105 with ECX initialized 623290f4213SJim Mattson * to one billion. 624290f4213SJim Mattson */ 625290f4213SJim Mattson asm volatile("loop ." : "+c"((int){1000000000ull})); 626290f4213SJim Mattson 627290f4213SJim Mattson t2 = fenced_rdtsc(); 628290f4213SJim Mattson stop_event(&cnt); 629290f4213SJim Mattson t3 = fenced_rdtsc(); 630290f4213SJim Mattson 631290f4213SJim Mattson tsc_delta = ((t2 - t1) + (t3 - t0)) / 2; 632290f4213SJim Mattson 633290f4213SJim Mattson if (!tsc_delta) 634290f4213SJim Mattson return; 635290f4213SJim Mattson 636290f4213SJim Mattson gp_events[2].min = (gp_events[2].min * cnt.count) / tsc_delta; 637290f4213SJim Mattson gp_events[2].max = (gp_events[2].max * cnt.count) / tsc_delta; 638290f4213SJim Mattson } 639290f4213SJim Mattson 640a9f8b16fSGleb Natapov int main(int ac, char **av) 641a9f8b16fSGleb Natapov { 642a9f8b16fSGleb Natapov setup_vm(); 643a9f8b16fSGleb Natapov handle_irq(PC_VECTOR, cnt_overflow); 644dcda215bSPaolo Bonzini buf = malloc(N*64); 645a9f8b16fSGleb Natapov 6462719b92cSYang Weijiang if (!pmu_version()) { 6472719b92cSYang Weijiang report_skip("No pmu is detected!"); 64832b9603cSRadim Krčmář return report_summary(); 649a9f8b16fSGleb Natapov } 65070972e21SNadav Amit 6512719b92cSYang Weijiang if (pmu_version() == 1) { 6522719b92cSYang Weijiang report_skip("PMU version 1 is not supported."); 65370972e21SNadav Amit return report_summary(); 65470972e21SNadav Amit } 65570972e21SNadav Amit 656290f4213SJim Mattson set_ref_cycle_expectations(); 657290f4213SJim Mattson 6582719b92cSYang Weijiang printf("PMU version: %d\n", pmu_version()); 6592719b92cSYang Weijiang printf("GP counters: %d\n", pmu_nr_gp_counters()); 6602719b92cSYang Weijiang printf("GP counter width: %d\n", pmu_gp_counter_width()); 6612719b92cSYang Weijiang printf("Mask length: %d\n", pmu_gp_counter_mask_length()); 6622719b92cSYang Weijiang printf("Fixed counters: %d\n", pmu_nr_fixed_counters()); 6632719b92cSYang Weijiang printf("Fixed counter width: %d\n", pmu_fixed_counter_width()); 6640ef1f6a8SPaolo Bonzini 665a9f8b16fSGleb Natapov apic_write(APIC_LVTPC, PC_VECTOR); 666a9f8b16fSGleb Natapov 667afa714b2SPaolo Bonzini check_counters(); 66820cf9147SJim Mattson 669c3cde0a5SLike Xu if (this_cpu_perf_capabilities() & PMU_CAP_FW_WRITES) { 67022f2901aSLike Xu gp_counter_base = MSR_IA32_PMC0; 67122f2901aSLike Xu report_prefix_push("full-width writes"); 67222f2901aSLike Xu check_counters(); 67322f2901aSLike Xu check_gp_counters_write_width(); 674d7714e16SLike Xu report_prefix_pop(); 67522f2901aSLike Xu } 676a9f8b16fSGleb Natapov 677f3cdd159SJan Kiszka return report_summary(); 678a9f8b16fSGleb Natapov } 679