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 36a9f8b16fSGleb Natapov typedef struct { 37a9f8b16fSGleb Natapov uint32_t ctr; 38a9f8b16fSGleb Natapov uint32_t config; 39a9f8b16fSGleb Natapov uint64_t count; 40a9f8b16fSGleb Natapov int idx; 41a9f8b16fSGleb Natapov } pmu_counter_t; 42a9f8b16fSGleb Natapov 43a9f8b16fSGleb Natapov union cpuid10_eax { 44a9f8b16fSGleb Natapov struct { 45a9f8b16fSGleb Natapov unsigned int version_id:8; 46a9f8b16fSGleb Natapov unsigned int num_counters:8; 47a9f8b16fSGleb Natapov unsigned int bit_width:8; 48a9f8b16fSGleb Natapov unsigned int mask_length:8; 49a9f8b16fSGleb Natapov } split; 50a9f8b16fSGleb Natapov unsigned int full; 51a9f8b16fSGleb Natapov } eax; 52a9f8b16fSGleb Natapov 53a9f8b16fSGleb Natapov union cpuid10_ebx { 54a9f8b16fSGleb Natapov struct { 55a9f8b16fSGleb Natapov unsigned int no_unhalted_core_cycles:1; 56a9f8b16fSGleb Natapov unsigned int no_instructions_retired:1; 57a9f8b16fSGleb Natapov unsigned int no_unhalted_reference_cycles:1; 58a9f8b16fSGleb Natapov unsigned int no_llc_reference:1; 59a9f8b16fSGleb Natapov unsigned int no_llc_misses:1; 60a9f8b16fSGleb Natapov unsigned int no_branch_instruction_retired:1; 61a9f8b16fSGleb Natapov unsigned int no_branch_misses_retired:1; 62a9f8b16fSGleb Natapov } split; 63a9f8b16fSGleb Natapov unsigned int full; 64a9f8b16fSGleb Natapov } ebx; 65a9f8b16fSGleb Natapov 66a9f8b16fSGleb Natapov union cpuid10_edx { 67a9f8b16fSGleb Natapov struct { 68a9f8b16fSGleb Natapov unsigned int num_counters_fixed:5; 69a9f8b16fSGleb Natapov unsigned int bit_width_fixed:8; 70a9f8b16fSGleb Natapov unsigned int reserved:19; 71a9f8b16fSGleb Natapov } split; 72a9f8b16fSGleb Natapov unsigned int full; 73a9f8b16fSGleb Natapov } edx; 74a9f8b16fSGleb Natapov 75a9f8b16fSGleb Natapov struct pmu_event { 76797d79a2SThomas Huth const char *name; 77a9f8b16fSGleb Natapov uint32_t unit_sel; 78a9f8b16fSGleb Natapov int min; 79a9f8b16fSGleb Natapov int max; 80a9f8b16fSGleb Natapov } gp_events[] = { 81a9f8b16fSGleb Natapov {"core cycles", 0x003c, 1*N, 50*N}, 82a9f8b16fSGleb Natapov {"instructions", 0x00c0, 10*N, 10.2*N}, 834779578bSGleb Natapov {"ref cycles", 0x013c, 0.1*N, 30*N}, 844779578bSGleb Natapov {"llc refference", 0x4f2e, 1, 2*N}, 85a9f8b16fSGleb Natapov {"llc misses", 0x412e, 1, 1*N}, 86a9f8b16fSGleb Natapov {"branches", 0x00c4, 1*N, 1.1*N}, 87a9f8b16fSGleb Natapov {"branch misses", 0x00c5, 0, 0.1*N}, 88a9f8b16fSGleb Natapov }, fixed_events[] = { 89a9f8b16fSGleb Natapov {"fixed 1", MSR_CORE_PERF_FIXED_CTR0, 10*N, 10.2*N}, 90a9f8b16fSGleb Natapov {"fixed 2", MSR_CORE_PERF_FIXED_CTR0 + 1, 1*N, 30*N}, 910ef1f6a8SPaolo Bonzini {"fixed 3", MSR_CORE_PERF_FIXED_CTR0 + 2, 0.1*N, 30*N} 92a9f8b16fSGleb Natapov }; 93a9f8b16fSGleb Natapov 9422f2901aSLike Xu #define PMU_CAP_FW_WRITES (1ULL << 13) 9522f2901aSLike Xu static u64 gp_counter_base = MSR_IA32_PERFCTR0; 9622f2901aSLike Xu 970ef1f6a8SPaolo Bonzini static int num_counters; 98a9f8b16fSGleb Natapov 99a9f8b16fSGleb Natapov char *buf; 100a9f8b16fSGleb Natapov 1017db17e21SThomas Huth static inline void loop(void) 102a9f8b16fSGleb Natapov { 103a9f8b16fSGleb Natapov unsigned long tmp, tmp2, tmp3; 104a9f8b16fSGleb Natapov 105a9f8b16fSGleb Natapov asm volatile("1: mov (%1), %2; add $64, %1; nop; nop; nop; nop; nop; nop; nop; loop 1b" 106a9f8b16fSGleb Natapov : "=c"(tmp), "=r"(tmp2), "=r"(tmp3): "0"(N), "1"(buf)); 107a9f8b16fSGleb Natapov 108a9f8b16fSGleb Natapov } 109a9f8b16fSGleb Natapov 110a9f8b16fSGleb Natapov volatile uint64_t irq_received; 111a9f8b16fSGleb Natapov 112a9f8b16fSGleb Natapov static void cnt_overflow(isr_regs_t *regs) 113a9f8b16fSGleb Natapov { 114a9f8b16fSGleb Natapov irq_received++; 115a9f8b16fSGleb Natapov apic_write(APIC_EOI, 0); 116a9f8b16fSGleb Natapov } 117a9f8b16fSGleb Natapov 118a9f8b16fSGleb Natapov static bool check_irq(void) 119a9f8b16fSGleb Natapov { 120a9f8b16fSGleb Natapov int i; 121a9f8b16fSGleb Natapov irq_received = 0; 122a9f8b16fSGleb Natapov irq_enable(); 123a9f8b16fSGleb Natapov for (i = 0; i < 100000 && !irq_received; i++) 124a9f8b16fSGleb Natapov asm volatile("pause"); 125a9f8b16fSGleb Natapov irq_disable(); 126a9f8b16fSGleb Natapov return irq_received; 127a9f8b16fSGleb Natapov } 128a9f8b16fSGleb Natapov 129a9f8b16fSGleb Natapov static bool is_gp(pmu_counter_t *evt) 130a9f8b16fSGleb Natapov { 13122f2901aSLike Xu return evt->ctr < MSR_CORE_PERF_FIXED_CTR0 || 13222f2901aSLike Xu evt->ctr >= MSR_IA32_PMC0; 133a9f8b16fSGleb Natapov } 134a9f8b16fSGleb Natapov 135a9f8b16fSGleb Natapov static int event_to_global_idx(pmu_counter_t *cnt) 136a9f8b16fSGleb Natapov { 13722f2901aSLike Xu return cnt->ctr - (is_gp(cnt) ? gp_counter_base : 138a9f8b16fSGleb Natapov (MSR_CORE_PERF_FIXED_CTR0 - FIXED_CNT_INDEX)); 139a9f8b16fSGleb Natapov } 140a9f8b16fSGleb Natapov 141a9f8b16fSGleb Natapov static struct pmu_event* get_counter_event(pmu_counter_t *cnt) 142a9f8b16fSGleb Natapov { 143a9f8b16fSGleb Natapov if (is_gp(cnt)) { 144a9f8b16fSGleb Natapov int i; 145a9f8b16fSGleb Natapov 146a9f8b16fSGleb Natapov for (i = 0; i < sizeof(gp_events)/sizeof(gp_events[0]); i++) 147a9f8b16fSGleb Natapov if (gp_events[i].unit_sel == (cnt->config & 0xffff)) 148a9f8b16fSGleb Natapov return &gp_events[i]; 149a9f8b16fSGleb Natapov } else 150a9f8b16fSGleb Natapov return &fixed_events[cnt->ctr - MSR_CORE_PERF_FIXED_CTR0]; 151a9f8b16fSGleb Natapov 152a9f8b16fSGleb Natapov return (void*)0; 153a9f8b16fSGleb Natapov } 154a9f8b16fSGleb Natapov 155a9f8b16fSGleb Natapov static void global_enable(pmu_counter_t *cnt) 156a9f8b16fSGleb Natapov { 157a9f8b16fSGleb Natapov cnt->idx = event_to_global_idx(cnt); 158a9f8b16fSGleb Natapov 159a9f8b16fSGleb Natapov wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, rdmsr(MSR_CORE_PERF_GLOBAL_CTRL) | 160a9f8b16fSGleb Natapov (1ull << cnt->idx)); 161a9f8b16fSGleb Natapov } 162a9f8b16fSGleb Natapov 163a9f8b16fSGleb Natapov static void global_disable(pmu_counter_t *cnt) 164a9f8b16fSGleb Natapov { 165a9f8b16fSGleb Natapov wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, rdmsr(MSR_CORE_PERF_GLOBAL_CTRL) & 166a9f8b16fSGleb Natapov ~(1ull << cnt->idx)); 167a9f8b16fSGleb Natapov } 168a9f8b16fSGleb Natapov 169a9f8b16fSGleb Natapov 170a9f8b16fSGleb Natapov static void start_event(pmu_counter_t *evt) 171a9f8b16fSGleb Natapov { 172a9f8b16fSGleb Natapov wrmsr(evt->ctr, evt->count); 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 uint32_t usrospmi = 0; 180a9f8b16fSGleb Natapov 181a9f8b16fSGleb Natapov if (evt->config & EVNTSEL_OS) 182a9f8b16fSGleb Natapov usrospmi |= (1 << 0); 183a9f8b16fSGleb Natapov if (evt->config & EVNTSEL_USR) 184a9f8b16fSGleb Natapov usrospmi |= (1 << 1); 185a9f8b16fSGleb Natapov if (evt->config & EVNTSEL_INT) 186a9f8b16fSGleb Natapov usrospmi |= (1 << 3); // PMI on overflow 187a9f8b16fSGleb Natapov ctrl = (ctrl & ~(0xf << shift)) | (usrospmi << shift); 188a9f8b16fSGleb Natapov wrmsr(MSR_CORE_PERF_FIXED_CTR_CTRL, ctrl); 189a9f8b16fSGleb Natapov } 190a9f8b16fSGleb Natapov global_enable(evt); 191bb6ede96SNadav Amit apic_write(APIC_LVTPC, PC_VECTOR); 192a9f8b16fSGleb Natapov } 193a9f8b16fSGleb Natapov 194a9f8b16fSGleb Natapov static void stop_event(pmu_counter_t *evt) 195a9f8b16fSGleb Natapov { 196a9f8b16fSGleb Natapov global_disable(evt); 197a9f8b16fSGleb Natapov if (is_gp(evt)) 198a9f8b16fSGleb Natapov wrmsr(MSR_P6_EVNTSEL0 + event_to_global_idx(evt), 199a9f8b16fSGleb Natapov evt->config & ~EVNTSEL_EN); 200a9f8b16fSGleb Natapov else { 201a9f8b16fSGleb Natapov uint32_t ctrl = rdmsr(MSR_CORE_PERF_FIXED_CTR_CTRL); 202a9f8b16fSGleb Natapov int shift = (evt->ctr - MSR_CORE_PERF_FIXED_CTR0) * 4; 203a9f8b16fSGleb Natapov wrmsr(MSR_CORE_PERF_FIXED_CTR_CTRL, ctrl & ~(0xf << shift)); 204a9f8b16fSGleb Natapov } 205a9f8b16fSGleb Natapov evt->count = rdmsr(evt->ctr); 206a9f8b16fSGleb Natapov } 207a9f8b16fSGleb Natapov 208a9f8b16fSGleb Natapov static void measure(pmu_counter_t *evt, int count) 209a9f8b16fSGleb Natapov { 210a9f8b16fSGleb Natapov int i; 211a9f8b16fSGleb Natapov for (i = 0; i < count; i++) 212a9f8b16fSGleb Natapov start_event(&evt[i]); 213a9f8b16fSGleb Natapov loop(); 214a9f8b16fSGleb Natapov for (i = 0; i < count; i++) 215a9f8b16fSGleb Natapov stop_event(&evt[i]); 216a9f8b16fSGleb Natapov } 217a9f8b16fSGleb Natapov 218a9f8b16fSGleb Natapov static bool verify_event(uint64_t count, struct pmu_event *e) 219a9f8b16fSGleb Natapov { 220a9f8b16fSGleb Natapov // printf("%lld >= %lld <= %lld\n", e->min, count, e->max); 221a9f8b16fSGleb Natapov return count >= e->min && count <= e->max; 222a9f8b16fSGleb Natapov 223a9f8b16fSGleb Natapov } 224a9f8b16fSGleb Natapov 225a9f8b16fSGleb Natapov static bool verify_counter(pmu_counter_t *cnt) 226a9f8b16fSGleb Natapov { 227a9f8b16fSGleb Natapov return verify_event(cnt->count, get_counter_event(cnt)); 228a9f8b16fSGleb Natapov } 229a9f8b16fSGleb Natapov 230a9f8b16fSGleb Natapov static void check_gp_counter(struct pmu_event *evt) 231a9f8b16fSGleb Natapov { 232a9f8b16fSGleb Natapov pmu_counter_t cnt = { 23322f2901aSLike Xu .ctr = gp_counter_base, 234a9f8b16fSGleb Natapov .config = EVNTSEL_OS | EVNTSEL_USR | evt->unit_sel, 235a9f8b16fSGleb Natapov }; 236a9f8b16fSGleb Natapov int i; 237a9f8b16fSGleb Natapov 2380ef1f6a8SPaolo Bonzini for (i = 0; i < num_counters; i++, cnt.ctr++) { 239a9f8b16fSGleb Natapov cnt.count = 0; 240a9f8b16fSGleb Natapov measure(&cnt, 1); 241a299895bSThomas Huth report(verify_event(cnt.count, evt), "%s-%d", evt->name, i); 242a9f8b16fSGleb Natapov } 243a9f8b16fSGleb Natapov } 244a9f8b16fSGleb Natapov 245a9f8b16fSGleb Natapov static void check_gp_counters(void) 246a9f8b16fSGleb Natapov { 247a9f8b16fSGleb Natapov int i; 248a9f8b16fSGleb Natapov 249a9f8b16fSGleb Natapov for (i = 0; i < sizeof(gp_events)/sizeof(gp_events[0]); i++) 250a9f8b16fSGleb Natapov if (!(ebx.full & (1 << i))) 251a9f8b16fSGleb Natapov check_gp_counter(&gp_events[i]); 252a9f8b16fSGleb Natapov else 253a9f8b16fSGleb Natapov printf("GP event '%s' is disabled\n", 254a9f8b16fSGleb Natapov gp_events[i].name); 255a9f8b16fSGleb Natapov } 256a9f8b16fSGleb Natapov 257a9f8b16fSGleb Natapov static void check_fixed_counters(void) 258a9f8b16fSGleb Natapov { 259a9f8b16fSGleb Natapov pmu_counter_t cnt = { 260a9f8b16fSGleb Natapov .config = EVNTSEL_OS | EVNTSEL_USR, 261a9f8b16fSGleb Natapov }; 262a9f8b16fSGleb Natapov int i; 263a9f8b16fSGleb Natapov 264a9f8b16fSGleb Natapov for (i = 0; i < edx.split.num_counters_fixed; i++) { 265a9f8b16fSGleb Natapov cnt.count = 0; 266a9f8b16fSGleb Natapov cnt.ctr = fixed_events[i].unit_sel; 267a9f8b16fSGleb Natapov measure(&cnt, 1); 268a299895bSThomas Huth report(verify_event(cnt.count, &fixed_events[i]), "fixed-%d", 269a299895bSThomas Huth i); 270a9f8b16fSGleb Natapov } 271a9f8b16fSGleb Natapov } 272a9f8b16fSGleb Natapov 273a9f8b16fSGleb Natapov static void check_counters_many(void) 274a9f8b16fSGleb Natapov { 275a9f8b16fSGleb Natapov pmu_counter_t cnt[10]; 276a9f8b16fSGleb Natapov int i, n; 277a9f8b16fSGleb Natapov 2780ef1f6a8SPaolo Bonzini for (i = 0, n = 0; n < num_counters; i++) { 279a9f8b16fSGleb Natapov if (ebx.full & (1 << i)) 280a9f8b16fSGleb Natapov continue; 281a9f8b16fSGleb Natapov 282a9f8b16fSGleb Natapov cnt[n].count = 0; 28322f2901aSLike Xu cnt[n].ctr = gp_counter_base + n; 2844ac45293SWei Huang cnt[n].config = EVNTSEL_OS | EVNTSEL_USR | 2854ac45293SWei Huang gp_events[i % ARRAY_SIZE(gp_events)].unit_sel; 286a9f8b16fSGleb Natapov n++; 287a9f8b16fSGleb Natapov } 288a9f8b16fSGleb Natapov for (i = 0; i < edx.split.num_counters_fixed; i++) { 289a9f8b16fSGleb Natapov cnt[n].count = 0; 290a9f8b16fSGleb Natapov cnt[n].ctr = fixed_events[i].unit_sel; 291a9f8b16fSGleb Natapov cnt[n].config = EVNTSEL_OS | EVNTSEL_USR; 292a9f8b16fSGleb Natapov n++; 293a9f8b16fSGleb Natapov } 294a9f8b16fSGleb Natapov 295a9f8b16fSGleb Natapov measure(cnt, n); 296a9f8b16fSGleb Natapov 297a9f8b16fSGleb Natapov for (i = 0; i < n; i++) 298a9f8b16fSGleb Natapov if (!verify_counter(&cnt[i])) 299a9f8b16fSGleb Natapov break; 300a9f8b16fSGleb Natapov 301a299895bSThomas Huth report(i == n, "all counters"); 302a9f8b16fSGleb Natapov } 303a9f8b16fSGleb Natapov 304a9f8b16fSGleb Natapov static void check_counter_overflow(void) 305a9f8b16fSGleb Natapov { 306a9f8b16fSGleb Natapov uint64_t count; 307a9f8b16fSGleb Natapov int i; 308a9f8b16fSGleb Natapov pmu_counter_t cnt = { 30922f2901aSLike Xu .ctr = gp_counter_base, 310a9f8b16fSGleb Natapov .config = EVNTSEL_OS | EVNTSEL_USR | gp_events[1].unit_sel /* instructions */, 311a9f8b16fSGleb Natapov .count = 0, 312a9f8b16fSGleb Natapov }; 313a9f8b16fSGleb Natapov measure(&cnt, 1); 314a9f8b16fSGleb Natapov count = cnt.count; 315a9f8b16fSGleb Natapov 316a9f8b16fSGleb Natapov /* clear status before test */ 317a9f8b16fSGleb Natapov wrmsr(MSR_CORE_PERF_GLOBAL_OVF_CTRL, rdmsr(MSR_CORE_PERF_GLOBAL_STATUS)); 318a9f8b16fSGleb Natapov 3195bba1769SAndrew Jones report_prefix_push("overflow"); 3205bba1769SAndrew Jones 3210ef1f6a8SPaolo Bonzini for (i = 0; i < num_counters + 1; i++, cnt.ctr++) { 322a9f8b16fSGleb Natapov uint64_t status; 323a9f8b16fSGleb Natapov int idx; 32433cfc1b0SNadav Amit 32533cfc1b0SNadav Amit cnt.count = 1 - count; 32622f2901aSLike Xu if (gp_counter_base == MSR_IA32_PMC0) 327a4ff5dceSNadav Amit cnt.count &= (1ull << eax.split.bit_width) - 1; 32833cfc1b0SNadav Amit 32933cfc1b0SNadav Amit if (i == num_counters) { 330a9f8b16fSGleb Natapov cnt.ctr = fixed_events[0].unit_sel; 331a4ff5dceSNadav Amit cnt.count &= (1ull << edx.split.bit_width_fixed) - 1; 33233cfc1b0SNadav Amit } 33333cfc1b0SNadav Amit 334a9f8b16fSGleb Natapov if (i % 2) 335a9f8b16fSGleb Natapov cnt.config |= EVNTSEL_INT; 336a9f8b16fSGleb Natapov else 337a9f8b16fSGleb Natapov cnt.config &= ~EVNTSEL_INT; 338a9f8b16fSGleb Natapov idx = event_to_global_idx(&cnt); 339a9f8b16fSGleb Natapov measure(&cnt, 1); 340a299895bSThomas Huth report(cnt.count == 1, "cntr-%d", i); 341a9f8b16fSGleb Natapov status = rdmsr(MSR_CORE_PERF_GLOBAL_STATUS); 342a299895bSThomas Huth report(status & (1ull << idx), "status-%d", i); 343a9f8b16fSGleb Natapov wrmsr(MSR_CORE_PERF_GLOBAL_OVF_CTRL, status); 344a9f8b16fSGleb Natapov status = rdmsr(MSR_CORE_PERF_GLOBAL_STATUS); 345a299895bSThomas Huth report(!(status & (1ull << idx)), "status clear-%d", i); 346a299895bSThomas Huth report(check_irq() == (i % 2), "irq-%d", i); 347a9f8b16fSGleb Natapov } 3485bba1769SAndrew Jones 3495bba1769SAndrew Jones report_prefix_pop(); 350a9f8b16fSGleb Natapov } 351a9f8b16fSGleb Natapov 352a9f8b16fSGleb Natapov static void check_gp_counter_cmask(void) 353a9f8b16fSGleb Natapov { 354a9f8b16fSGleb Natapov pmu_counter_t cnt = { 35522f2901aSLike Xu .ctr = gp_counter_base, 356a9f8b16fSGleb Natapov .config = EVNTSEL_OS | EVNTSEL_USR | gp_events[1].unit_sel /* instructions */, 357a9f8b16fSGleb Natapov .count = 0, 358a9f8b16fSGleb Natapov }; 359a9f8b16fSGleb Natapov cnt.config |= (0x2 << EVNTSEL_CMASK_SHIFT); 360a9f8b16fSGleb Natapov measure(&cnt, 1); 361a299895bSThomas Huth report(cnt.count < gp_events[1].min, "cmask"); 362a9f8b16fSGleb Natapov } 363a9f8b16fSGleb Natapov 364ca1b9de9SNadav Amit static void do_rdpmc_fast(void *ptr) 365ca1b9de9SNadav Amit { 366ca1b9de9SNadav Amit pmu_counter_t *cnt = ptr; 367ca1b9de9SNadav Amit uint32_t idx = (uint32_t)cnt->idx | (1u << 31); 368ca1b9de9SNadav Amit 369ca1b9de9SNadav Amit if (!is_gp(cnt)) 370ca1b9de9SNadav Amit idx |= 1 << 30; 371ca1b9de9SNadav Amit 372ca1b9de9SNadav Amit cnt->count = rdpmc(idx); 373ca1b9de9SNadav Amit } 374ca1b9de9SNadav Amit 375ca1b9de9SNadav Amit 376a9f8b16fSGleb Natapov static void check_rdpmc(void) 377a9f8b16fSGleb Natapov { 37822f2901aSLike Xu uint64_t val = 0xff0123456789ull; 379ca1b9de9SNadav Amit bool exc; 380a9f8b16fSGleb Natapov int i; 381a9f8b16fSGleb Natapov 3825bba1769SAndrew Jones report_prefix_push("rdpmc"); 3835bba1769SAndrew Jones 3840ef1f6a8SPaolo Bonzini for (i = 0; i < num_counters; i++) { 38533cfc1b0SNadav Amit uint64_t x; 386ca1b9de9SNadav Amit pmu_counter_t cnt = { 38722f2901aSLike Xu .ctr = gp_counter_base + i, 388ca1b9de9SNadav Amit .idx = i 389ca1b9de9SNadav Amit }; 39033cfc1b0SNadav Amit 39133cfc1b0SNadav Amit /* 39222f2901aSLike Xu * Without full-width writes, only the low 32 bits are writable, 39322f2901aSLike Xu * and the value is sign-extended. 39433cfc1b0SNadav Amit */ 39522f2901aSLike Xu if (gp_counter_base == MSR_IA32_PERFCTR0) 39633cfc1b0SNadav Amit x = (uint64_t)(int64_t)(int32_t)val; 39722f2901aSLike Xu else 39822f2901aSLike Xu x = (uint64_t)(int64_t)val; 39933cfc1b0SNadav Amit 40033cfc1b0SNadav Amit /* Mask according to the number of supported bits */ 40133cfc1b0SNadav Amit x &= (1ull << eax.split.bit_width) - 1; 40233cfc1b0SNadav Amit 40322f2901aSLike Xu wrmsr(gp_counter_base + i, val); 404a299895bSThomas Huth report(rdpmc(i) == x, "cntr-%d", i); 405ca1b9de9SNadav Amit 406ca1b9de9SNadav Amit exc = test_for_exception(GP_VECTOR, do_rdpmc_fast, &cnt); 407ca1b9de9SNadav Amit if (exc) 408ca1b9de9SNadav Amit report_skip("fast-%d", i); 409ca1b9de9SNadav Amit else 410a299895bSThomas Huth report(cnt.count == (u32)val, "fast-%d", i); 411a9f8b16fSGleb Natapov } 412a9f8b16fSGleb Natapov for (i = 0; i < edx.split.num_counters_fixed; i++) { 41333cfc1b0SNadav Amit uint64_t x = val & ((1ull << edx.split.bit_width_fixed) - 1); 414ca1b9de9SNadav Amit pmu_counter_t cnt = { 415ca1b9de9SNadav Amit .ctr = MSR_CORE_PERF_FIXED_CTR0 + i, 416ca1b9de9SNadav Amit .idx = i 417ca1b9de9SNadav Amit }; 41833cfc1b0SNadav Amit 41933cfc1b0SNadav Amit wrmsr(MSR_CORE_PERF_FIXED_CTR0 + i, x); 420a299895bSThomas Huth report(rdpmc(i | (1 << 30)) == x, "fixed cntr-%d", i); 421ca1b9de9SNadav Amit 422ca1b9de9SNadav Amit exc = test_for_exception(GP_VECTOR, do_rdpmc_fast, &cnt); 423ca1b9de9SNadav Amit if (exc) 424ca1b9de9SNadav Amit report_skip("fixed fast-%d", i); 425ca1b9de9SNadav Amit else 426a299895bSThomas Huth report(cnt.count == (u32)x, "fixed fast-%d", i); 427a9f8b16fSGleb Natapov } 4285bba1769SAndrew Jones 4295bba1769SAndrew Jones report_prefix_pop(); 430a9f8b16fSGleb Natapov } 431a9f8b16fSGleb Natapov 432ddade902SEric Hankland static void check_running_counter_wrmsr(void) 433ddade902SEric Hankland { 43459ca1413SEric Hankland uint64_t status; 43522f2901aSLike Xu uint64_t count; 436ddade902SEric Hankland pmu_counter_t evt = { 43722f2901aSLike Xu .ctr = gp_counter_base, 438ddade902SEric Hankland .config = EVNTSEL_OS | EVNTSEL_USR | gp_events[1].unit_sel, 439ddade902SEric Hankland .count = 0, 440ddade902SEric Hankland }; 441ddade902SEric Hankland 44259ca1413SEric Hankland report_prefix_push("running counter wrmsr"); 44359ca1413SEric Hankland 444ddade902SEric Hankland start_event(&evt); 445ddade902SEric Hankland loop(); 44622f2901aSLike Xu wrmsr(gp_counter_base, 0); 447ddade902SEric Hankland stop_event(&evt); 44859ca1413SEric Hankland report(evt.count < gp_events[1].min, "cntr"); 44959ca1413SEric Hankland 45059ca1413SEric Hankland /* clear status before overflow test */ 45159ca1413SEric Hankland wrmsr(MSR_CORE_PERF_GLOBAL_OVF_CTRL, 45259ca1413SEric Hankland rdmsr(MSR_CORE_PERF_GLOBAL_STATUS)); 45359ca1413SEric Hankland 45459ca1413SEric Hankland evt.count = 0; 45559ca1413SEric Hankland start_event(&evt); 45622f2901aSLike Xu 45722f2901aSLike Xu count = -1; 45822f2901aSLike Xu if (gp_counter_base == MSR_IA32_PMC0) 459a4ff5dceSNadav Amit count &= (1ull << eax.split.bit_width) - 1; 46022f2901aSLike Xu 46122f2901aSLike Xu wrmsr(gp_counter_base, count); 46222f2901aSLike Xu 46359ca1413SEric Hankland loop(); 46459ca1413SEric Hankland stop_event(&evt); 46559ca1413SEric Hankland status = rdmsr(MSR_CORE_PERF_GLOBAL_STATUS); 46659ca1413SEric Hankland report(status & 1, "status"); 46759ca1413SEric Hankland 46859ca1413SEric Hankland report_prefix_pop(); 469ddade902SEric Hankland } 470ddade902SEric Hankland 47122f2901aSLike Xu static void check_counters(void) 47222f2901aSLike Xu { 47322f2901aSLike Xu check_gp_counters(); 47422f2901aSLike Xu check_fixed_counters(); 47522f2901aSLike Xu check_rdpmc(); 47622f2901aSLike Xu check_counters_many(); 47722f2901aSLike Xu check_counter_overflow(); 47822f2901aSLike Xu check_gp_counter_cmask(); 47922f2901aSLike Xu check_running_counter_wrmsr(); 48022f2901aSLike Xu } 48122f2901aSLike Xu 48222f2901aSLike Xu static void do_unsupported_width_counter_write(void *index) 48322f2901aSLike Xu { 48422f2901aSLike Xu wrmsr(MSR_IA32_PMC0 + *((int *) index), 0xffffff0123456789ull); 48522f2901aSLike Xu } 48622f2901aSLike Xu 48722f2901aSLike Xu static void check_gp_counters_write_width(void) 48822f2901aSLike Xu { 48922f2901aSLike Xu u64 val_64 = 0xffffff0123456789ull; 4904b74c718SThomas Huth u64 val_32 = val_64 & ((1ull << 32) - 1); 491a4ff5dceSNadav Amit u64 val_max_width = val_64 & ((1ull << eax.split.bit_width) - 1); 49222f2901aSLike Xu int i; 49322f2901aSLike Xu 49422f2901aSLike Xu /* 49522f2901aSLike Xu * MSR_IA32_PERFCTRn supports 64-bit writes, 49622f2901aSLike Xu * but only the lowest 32 bits are valid. 49722f2901aSLike Xu */ 49822f2901aSLike Xu for (i = 0; i < num_counters; i++) { 49922f2901aSLike Xu wrmsr(MSR_IA32_PERFCTR0 + i, val_32); 50022f2901aSLike Xu assert(rdmsr(MSR_IA32_PERFCTR0 + i) == val_32); 50122f2901aSLike Xu assert(rdmsr(MSR_IA32_PMC0 + i) == val_32); 50222f2901aSLike Xu 50322f2901aSLike Xu wrmsr(MSR_IA32_PERFCTR0 + i, val_max_width); 50422f2901aSLike Xu assert(rdmsr(MSR_IA32_PERFCTR0 + i) == val_32); 50522f2901aSLike Xu assert(rdmsr(MSR_IA32_PMC0 + i) == val_32); 50622f2901aSLike Xu 50722f2901aSLike Xu wrmsr(MSR_IA32_PERFCTR0 + i, val_64); 50822f2901aSLike Xu assert(rdmsr(MSR_IA32_PERFCTR0 + i) == val_32); 50922f2901aSLike Xu assert(rdmsr(MSR_IA32_PMC0 + i) == val_32); 51022f2901aSLike Xu } 51122f2901aSLike Xu 51222f2901aSLike Xu /* 51322f2901aSLike Xu * MSR_IA32_PMCn supports writing values ​​up to GP counter width, 51422f2901aSLike Xu * and only the lowest bits of GP counter width are valid. 51522f2901aSLike Xu */ 51622f2901aSLike Xu for (i = 0; i < num_counters; i++) { 51722f2901aSLike Xu wrmsr(MSR_IA32_PMC0 + i, val_32); 51822f2901aSLike Xu assert(rdmsr(MSR_IA32_PMC0 + i) == val_32); 51922f2901aSLike Xu assert(rdmsr(MSR_IA32_PERFCTR0 + i) == val_32); 52022f2901aSLike Xu 52122f2901aSLike Xu wrmsr(MSR_IA32_PMC0 + i, val_max_width); 52222f2901aSLike Xu assert(rdmsr(MSR_IA32_PMC0 + i) == val_max_width); 52322f2901aSLike Xu assert(rdmsr(MSR_IA32_PERFCTR0 + i) == val_max_width); 52422f2901aSLike Xu 52522f2901aSLike Xu report(test_for_exception(GP_VECTOR, 52622f2901aSLike Xu do_unsupported_width_counter_write, &i), 52722f2901aSLike Xu "writing unsupported width to MSR_IA32_PMC%d raises #GP", i); 52822f2901aSLike Xu } 52922f2901aSLike Xu } 53022f2901aSLike Xu 531a9f8b16fSGleb Natapov int main(int ac, char **av) 532a9f8b16fSGleb Natapov { 533a9f8b16fSGleb Natapov struct cpuid id = cpuid(10); 534a9f8b16fSGleb Natapov 535a9f8b16fSGleb Natapov setup_vm(); 536a9f8b16fSGleb Natapov handle_irq(PC_VECTOR, cnt_overflow); 537dcda215bSPaolo Bonzini buf = malloc(N*64); 538a9f8b16fSGleb Natapov 539a9f8b16fSGleb Natapov eax.full = id.a; 540a9f8b16fSGleb Natapov ebx.full = id.b; 541a9f8b16fSGleb Natapov edx.full = id.d; 542a9f8b16fSGleb Natapov 543a9f8b16fSGleb Natapov if (!eax.split.version_id) { 544a9f8b16fSGleb Natapov printf("No pmu is detected!\n"); 54532b9603cSRadim Krčmář return report_summary(); 546a9f8b16fSGleb Natapov } 547*70972e21SNadav Amit 548*70972e21SNadav Amit if (eax.split.version_id == 1) { 549*70972e21SNadav Amit printf("PMU version 1 is not supported\n"); 550*70972e21SNadav Amit return report_summary(); 551*70972e21SNadav Amit } 552*70972e21SNadav Amit 553a9f8b16fSGleb Natapov printf("PMU version: %d\n", eax.split.version_id); 554a9f8b16fSGleb Natapov printf("GP counters: %d\n", eax.split.num_counters); 555a9f8b16fSGleb Natapov printf("GP counter width: %d\n", eax.split.bit_width); 556a9f8b16fSGleb Natapov printf("Mask length: %d\n", eax.split.mask_length); 557a9f8b16fSGleb Natapov printf("Fixed counters: %d\n", edx.split.num_counters_fixed); 558a9f8b16fSGleb Natapov printf("Fixed counter width: %d\n", edx.split.bit_width_fixed); 559a9f8b16fSGleb Natapov 5600ef1f6a8SPaolo Bonzini num_counters = eax.split.num_counters; 5610ef1f6a8SPaolo Bonzini 562a9f8b16fSGleb Natapov apic_write(APIC_LVTPC, PC_VECTOR); 563a9f8b16fSGleb Natapov 56422f2901aSLike Xu check_counters(); 56522f2901aSLike Xu 56622f2901aSLike Xu if (rdmsr(MSR_IA32_PERF_CAPABILITIES) & PMU_CAP_FW_WRITES) { 56722f2901aSLike Xu gp_counter_base = MSR_IA32_PMC0; 56822f2901aSLike Xu report_prefix_push("full-width writes"); 56922f2901aSLike Xu check_counters(); 57022f2901aSLike Xu check_gp_counters_write_width(); 57122f2901aSLike Xu } 572a9f8b16fSGleb Natapov 573f3cdd159SJan Kiszka return report_summary(); 574a9f8b16fSGleb Natapov } 575