162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <linux/types.h> 362306a36Sopenharmony_ci#include <linux/interrupt.h> 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include <asm/xen/hypercall.h> 662306a36Sopenharmony_ci#include <xen/xen.h> 762306a36Sopenharmony_ci#include <xen/page.h> 862306a36Sopenharmony_ci#include <xen/interface/xen.h> 962306a36Sopenharmony_ci#include <xen/interface/vcpu.h> 1062306a36Sopenharmony_ci#include <xen/interface/xenpmu.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "xen-ops.h" 1362306a36Sopenharmony_ci#include "pmu.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci/* x86_pmu.handle_irq definition */ 1662306a36Sopenharmony_ci#include "../events/perf_event.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define XENPMU_IRQ_PROCESSING 1 1962306a36Sopenharmony_cistruct xenpmu { 2062306a36Sopenharmony_ci /* Shared page between hypervisor and domain */ 2162306a36Sopenharmony_ci struct xen_pmu_data *xenpmu_data; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci uint8_t flags; 2462306a36Sopenharmony_ci}; 2562306a36Sopenharmony_cistatic DEFINE_PER_CPU(struct xenpmu, xenpmu_shared); 2662306a36Sopenharmony_ci#define get_xenpmu_data() (this_cpu_ptr(&xenpmu_shared)->xenpmu_data) 2762306a36Sopenharmony_ci#define get_xenpmu_flags() (this_cpu_ptr(&xenpmu_shared)->flags) 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* Macro for computing address of a PMU MSR bank */ 3062306a36Sopenharmony_ci#define field_offset(ctxt, field) ((void *)((uintptr_t)ctxt + \ 3162306a36Sopenharmony_ci (uintptr_t)ctxt->field)) 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* AMD PMU */ 3462306a36Sopenharmony_ci#define F15H_NUM_COUNTERS 6 3562306a36Sopenharmony_ci#define F10H_NUM_COUNTERS 4 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic __read_mostly uint32_t amd_counters_base; 3862306a36Sopenharmony_cistatic __read_mostly uint32_t amd_ctrls_base; 3962306a36Sopenharmony_cistatic __read_mostly int amd_msr_step; 4062306a36Sopenharmony_cistatic __read_mostly int k7_counters_mirrored; 4162306a36Sopenharmony_cistatic __read_mostly int amd_num_counters; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* Intel PMU */ 4462306a36Sopenharmony_ci#define MSR_TYPE_COUNTER 0 4562306a36Sopenharmony_ci#define MSR_TYPE_CTRL 1 4662306a36Sopenharmony_ci#define MSR_TYPE_GLOBAL 2 4762306a36Sopenharmony_ci#define MSR_TYPE_ARCH_COUNTER 3 4862306a36Sopenharmony_ci#define MSR_TYPE_ARCH_CTRL 4 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* Number of general pmu registers (CPUID.EAX[0xa].EAX[8..15]) */ 5162306a36Sopenharmony_ci#define PMU_GENERAL_NR_SHIFT 8 5262306a36Sopenharmony_ci#define PMU_GENERAL_NR_BITS 8 5362306a36Sopenharmony_ci#define PMU_GENERAL_NR_MASK (((1 << PMU_GENERAL_NR_BITS) - 1) \ 5462306a36Sopenharmony_ci << PMU_GENERAL_NR_SHIFT) 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/* Number of fixed pmu registers (CPUID.EDX[0xa].EDX[0..4]) */ 5762306a36Sopenharmony_ci#define PMU_FIXED_NR_SHIFT 0 5862306a36Sopenharmony_ci#define PMU_FIXED_NR_BITS 5 5962306a36Sopenharmony_ci#define PMU_FIXED_NR_MASK (((1 << PMU_FIXED_NR_BITS) - 1) \ 6062306a36Sopenharmony_ci << PMU_FIXED_NR_SHIFT) 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* Alias registers (0x4c1) for full-width writes to PMCs */ 6362306a36Sopenharmony_ci#define MSR_PMC_ALIAS_MASK (~(MSR_IA32_PERFCTR0 ^ MSR_IA32_PMC0)) 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci#define INTEL_PMC_TYPE_SHIFT 30 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic __read_mostly int intel_num_arch_counters, intel_num_fixed_counters; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic void xen_pmu_arch_init(void) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) { 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci switch (boot_cpu_data.x86) { 7562306a36Sopenharmony_ci case 0x15: 7662306a36Sopenharmony_ci amd_num_counters = F15H_NUM_COUNTERS; 7762306a36Sopenharmony_ci amd_counters_base = MSR_F15H_PERF_CTR; 7862306a36Sopenharmony_ci amd_ctrls_base = MSR_F15H_PERF_CTL; 7962306a36Sopenharmony_ci amd_msr_step = 2; 8062306a36Sopenharmony_ci k7_counters_mirrored = 1; 8162306a36Sopenharmony_ci break; 8262306a36Sopenharmony_ci case 0x10: 8362306a36Sopenharmony_ci case 0x12: 8462306a36Sopenharmony_ci case 0x14: 8562306a36Sopenharmony_ci case 0x16: 8662306a36Sopenharmony_ci default: 8762306a36Sopenharmony_ci amd_num_counters = F10H_NUM_COUNTERS; 8862306a36Sopenharmony_ci amd_counters_base = MSR_K7_PERFCTR0; 8962306a36Sopenharmony_ci amd_ctrls_base = MSR_K7_EVNTSEL0; 9062306a36Sopenharmony_ci amd_msr_step = 1; 9162306a36Sopenharmony_ci k7_counters_mirrored = 0; 9262306a36Sopenharmony_ci break; 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci } else if (boot_cpu_data.x86_vendor == X86_VENDOR_HYGON) { 9562306a36Sopenharmony_ci amd_num_counters = F10H_NUM_COUNTERS; 9662306a36Sopenharmony_ci amd_counters_base = MSR_K7_PERFCTR0; 9762306a36Sopenharmony_ci amd_ctrls_base = MSR_K7_EVNTSEL0; 9862306a36Sopenharmony_ci amd_msr_step = 1; 9962306a36Sopenharmony_ci k7_counters_mirrored = 0; 10062306a36Sopenharmony_ci } else { 10162306a36Sopenharmony_ci uint32_t eax, ebx, ecx, edx; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci cpuid(0xa, &eax, &ebx, &ecx, &edx); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci intel_num_arch_counters = (eax & PMU_GENERAL_NR_MASK) >> 10662306a36Sopenharmony_ci PMU_GENERAL_NR_SHIFT; 10762306a36Sopenharmony_ci intel_num_fixed_counters = (edx & PMU_FIXED_NR_MASK) >> 10862306a36Sopenharmony_ci PMU_FIXED_NR_SHIFT; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic inline uint32_t get_fam15h_addr(u32 addr) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci switch (addr) { 11562306a36Sopenharmony_ci case MSR_K7_PERFCTR0: 11662306a36Sopenharmony_ci case MSR_K7_PERFCTR1: 11762306a36Sopenharmony_ci case MSR_K7_PERFCTR2: 11862306a36Sopenharmony_ci case MSR_K7_PERFCTR3: 11962306a36Sopenharmony_ci return MSR_F15H_PERF_CTR + (addr - MSR_K7_PERFCTR0); 12062306a36Sopenharmony_ci case MSR_K7_EVNTSEL0: 12162306a36Sopenharmony_ci case MSR_K7_EVNTSEL1: 12262306a36Sopenharmony_ci case MSR_K7_EVNTSEL2: 12362306a36Sopenharmony_ci case MSR_K7_EVNTSEL3: 12462306a36Sopenharmony_ci return MSR_F15H_PERF_CTL + (addr - MSR_K7_EVNTSEL0); 12562306a36Sopenharmony_ci default: 12662306a36Sopenharmony_ci break; 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci return addr; 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic inline bool is_amd_pmu_msr(unsigned int msr) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD && 13562306a36Sopenharmony_ci boot_cpu_data.x86_vendor != X86_VENDOR_HYGON) 13662306a36Sopenharmony_ci return false; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if ((msr >= MSR_F15H_PERF_CTL && 13962306a36Sopenharmony_ci msr < MSR_F15H_PERF_CTR + (amd_num_counters * 2)) || 14062306a36Sopenharmony_ci (msr >= MSR_K7_EVNTSEL0 && 14162306a36Sopenharmony_ci msr < MSR_K7_PERFCTR0 + amd_num_counters)) 14262306a36Sopenharmony_ci return true; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci return false; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic bool is_intel_pmu_msr(u32 msr_index, int *type, int *index) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci u32 msr_index_pmc; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL && 15262306a36Sopenharmony_ci boot_cpu_data.x86_vendor != X86_VENDOR_CENTAUR && 15362306a36Sopenharmony_ci boot_cpu_data.x86_vendor != X86_VENDOR_ZHAOXIN) 15462306a36Sopenharmony_ci return false; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci switch (msr_index) { 15762306a36Sopenharmony_ci case MSR_CORE_PERF_FIXED_CTR_CTRL: 15862306a36Sopenharmony_ci case MSR_IA32_DS_AREA: 15962306a36Sopenharmony_ci case MSR_IA32_PEBS_ENABLE: 16062306a36Sopenharmony_ci *type = MSR_TYPE_CTRL; 16162306a36Sopenharmony_ci return true; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci case MSR_CORE_PERF_GLOBAL_CTRL: 16462306a36Sopenharmony_ci case MSR_CORE_PERF_GLOBAL_STATUS: 16562306a36Sopenharmony_ci case MSR_CORE_PERF_GLOBAL_OVF_CTRL: 16662306a36Sopenharmony_ci *type = MSR_TYPE_GLOBAL; 16762306a36Sopenharmony_ci return true; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci default: 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci if ((msr_index >= MSR_CORE_PERF_FIXED_CTR0) && 17262306a36Sopenharmony_ci (msr_index < MSR_CORE_PERF_FIXED_CTR0 + 17362306a36Sopenharmony_ci intel_num_fixed_counters)) { 17462306a36Sopenharmony_ci *index = msr_index - MSR_CORE_PERF_FIXED_CTR0; 17562306a36Sopenharmony_ci *type = MSR_TYPE_COUNTER; 17662306a36Sopenharmony_ci return true; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if ((msr_index >= MSR_P6_EVNTSEL0) && 18062306a36Sopenharmony_ci (msr_index < MSR_P6_EVNTSEL0 + intel_num_arch_counters)) { 18162306a36Sopenharmony_ci *index = msr_index - MSR_P6_EVNTSEL0; 18262306a36Sopenharmony_ci *type = MSR_TYPE_ARCH_CTRL; 18362306a36Sopenharmony_ci return true; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci msr_index_pmc = msr_index & MSR_PMC_ALIAS_MASK; 18762306a36Sopenharmony_ci if ((msr_index_pmc >= MSR_IA32_PERFCTR0) && 18862306a36Sopenharmony_ci (msr_index_pmc < MSR_IA32_PERFCTR0 + 18962306a36Sopenharmony_ci intel_num_arch_counters)) { 19062306a36Sopenharmony_ci *type = MSR_TYPE_ARCH_COUNTER; 19162306a36Sopenharmony_ci *index = msr_index_pmc - MSR_IA32_PERFCTR0; 19262306a36Sopenharmony_ci return true; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci return false; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic bool xen_intel_pmu_emulate(unsigned int msr, u64 *val, int type, 19962306a36Sopenharmony_ci int index, bool is_read) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci uint64_t *reg = NULL; 20262306a36Sopenharmony_ci struct xen_pmu_intel_ctxt *ctxt; 20362306a36Sopenharmony_ci uint64_t *fix_counters; 20462306a36Sopenharmony_ci struct xen_pmu_cntr_pair *arch_cntr_pair; 20562306a36Sopenharmony_ci struct xen_pmu_data *xenpmu_data = get_xenpmu_data(); 20662306a36Sopenharmony_ci uint8_t xenpmu_flags = get_xenpmu_flags(); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (!xenpmu_data || !(xenpmu_flags & XENPMU_IRQ_PROCESSING)) 21062306a36Sopenharmony_ci return false; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci ctxt = &xenpmu_data->pmu.c.intel; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci switch (msr) { 21562306a36Sopenharmony_ci case MSR_CORE_PERF_GLOBAL_OVF_CTRL: 21662306a36Sopenharmony_ci reg = &ctxt->global_ovf_ctrl; 21762306a36Sopenharmony_ci break; 21862306a36Sopenharmony_ci case MSR_CORE_PERF_GLOBAL_STATUS: 21962306a36Sopenharmony_ci reg = &ctxt->global_status; 22062306a36Sopenharmony_ci break; 22162306a36Sopenharmony_ci case MSR_CORE_PERF_GLOBAL_CTRL: 22262306a36Sopenharmony_ci reg = &ctxt->global_ctrl; 22362306a36Sopenharmony_ci break; 22462306a36Sopenharmony_ci case MSR_CORE_PERF_FIXED_CTR_CTRL: 22562306a36Sopenharmony_ci reg = &ctxt->fixed_ctrl; 22662306a36Sopenharmony_ci break; 22762306a36Sopenharmony_ci default: 22862306a36Sopenharmony_ci switch (type) { 22962306a36Sopenharmony_ci case MSR_TYPE_COUNTER: 23062306a36Sopenharmony_ci fix_counters = field_offset(ctxt, fixed_counters); 23162306a36Sopenharmony_ci reg = &fix_counters[index]; 23262306a36Sopenharmony_ci break; 23362306a36Sopenharmony_ci case MSR_TYPE_ARCH_COUNTER: 23462306a36Sopenharmony_ci arch_cntr_pair = field_offset(ctxt, arch_counters); 23562306a36Sopenharmony_ci reg = &arch_cntr_pair[index].counter; 23662306a36Sopenharmony_ci break; 23762306a36Sopenharmony_ci case MSR_TYPE_ARCH_CTRL: 23862306a36Sopenharmony_ci arch_cntr_pair = field_offset(ctxt, arch_counters); 23962306a36Sopenharmony_ci reg = &arch_cntr_pair[index].control; 24062306a36Sopenharmony_ci break; 24162306a36Sopenharmony_ci default: 24262306a36Sopenharmony_ci return false; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (reg) { 24762306a36Sopenharmony_ci if (is_read) 24862306a36Sopenharmony_ci *val = *reg; 24962306a36Sopenharmony_ci else { 25062306a36Sopenharmony_ci *reg = *val; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (msr == MSR_CORE_PERF_GLOBAL_OVF_CTRL) 25362306a36Sopenharmony_ci ctxt->global_status &= (~(*val)); 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci return true; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci return false; 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic bool xen_amd_pmu_emulate(unsigned int msr, u64 *val, bool is_read) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci uint64_t *reg = NULL; 26462306a36Sopenharmony_ci int i, off = 0; 26562306a36Sopenharmony_ci struct xen_pmu_amd_ctxt *ctxt; 26662306a36Sopenharmony_ci uint64_t *counter_regs, *ctrl_regs; 26762306a36Sopenharmony_ci struct xen_pmu_data *xenpmu_data = get_xenpmu_data(); 26862306a36Sopenharmony_ci uint8_t xenpmu_flags = get_xenpmu_flags(); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if (!xenpmu_data || !(xenpmu_flags & XENPMU_IRQ_PROCESSING)) 27162306a36Sopenharmony_ci return false; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci if (k7_counters_mirrored && 27462306a36Sopenharmony_ci ((msr >= MSR_K7_EVNTSEL0) && (msr <= MSR_K7_PERFCTR3))) 27562306a36Sopenharmony_ci msr = get_fam15h_addr(msr); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci ctxt = &xenpmu_data->pmu.c.amd; 27862306a36Sopenharmony_ci for (i = 0; i < amd_num_counters; i++) { 27962306a36Sopenharmony_ci if (msr == amd_ctrls_base + off) { 28062306a36Sopenharmony_ci ctrl_regs = field_offset(ctxt, ctrls); 28162306a36Sopenharmony_ci reg = &ctrl_regs[i]; 28262306a36Sopenharmony_ci break; 28362306a36Sopenharmony_ci } else if (msr == amd_counters_base + off) { 28462306a36Sopenharmony_ci counter_regs = field_offset(ctxt, counters); 28562306a36Sopenharmony_ci reg = &counter_regs[i]; 28662306a36Sopenharmony_ci break; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci off += amd_msr_step; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (reg) { 29262306a36Sopenharmony_ci if (is_read) 29362306a36Sopenharmony_ci *val = *reg; 29462306a36Sopenharmony_ci else 29562306a36Sopenharmony_ci *reg = *val; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci return true; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci return false; 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic bool pmu_msr_chk_emulated(unsigned int msr, uint64_t *val, bool is_read, 30362306a36Sopenharmony_ci bool *emul) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci int type, index = 0; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (is_amd_pmu_msr(msr)) 30862306a36Sopenharmony_ci *emul = xen_amd_pmu_emulate(msr, val, is_read); 30962306a36Sopenharmony_ci else if (is_intel_pmu_msr(msr, &type, &index)) 31062306a36Sopenharmony_ci *emul = xen_intel_pmu_emulate(msr, val, type, index, is_read); 31162306a36Sopenharmony_ci else 31262306a36Sopenharmony_ci return false; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci return true; 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cibool pmu_msr_read(unsigned int msr, uint64_t *val, int *err) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci bool emulated; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci if (!pmu_msr_chk_emulated(msr, val, true, &emulated)) 32262306a36Sopenharmony_ci return false; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci if (!emulated) { 32562306a36Sopenharmony_ci *val = err ? native_read_msr_safe(msr, err) 32662306a36Sopenharmony_ci : native_read_msr(msr); 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci return true; 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cibool pmu_msr_write(unsigned int msr, uint32_t low, uint32_t high, int *err) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci uint64_t val = ((uint64_t)high << 32) | low; 33562306a36Sopenharmony_ci bool emulated; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (!pmu_msr_chk_emulated(msr, &val, false, &emulated)) 33862306a36Sopenharmony_ci return false; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (!emulated) { 34162306a36Sopenharmony_ci if (err) 34262306a36Sopenharmony_ci *err = native_write_msr_safe(msr, low, high); 34362306a36Sopenharmony_ci else 34462306a36Sopenharmony_ci native_write_msr(msr, low, high); 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci return true; 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic unsigned long long xen_amd_read_pmc(int counter) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci struct xen_pmu_amd_ctxt *ctxt; 35362306a36Sopenharmony_ci uint64_t *counter_regs; 35462306a36Sopenharmony_ci struct xen_pmu_data *xenpmu_data = get_xenpmu_data(); 35562306a36Sopenharmony_ci uint8_t xenpmu_flags = get_xenpmu_flags(); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci if (!xenpmu_data || !(xenpmu_flags & XENPMU_IRQ_PROCESSING)) { 35862306a36Sopenharmony_ci uint32_t msr; 35962306a36Sopenharmony_ci int err; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci msr = amd_counters_base + (counter * amd_msr_step); 36262306a36Sopenharmony_ci return native_read_msr_safe(msr, &err); 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci ctxt = &xenpmu_data->pmu.c.amd; 36662306a36Sopenharmony_ci counter_regs = field_offset(ctxt, counters); 36762306a36Sopenharmony_ci return counter_regs[counter]; 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistatic unsigned long long xen_intel_read_pmc(int counter) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci struct xen_pmu_intel_ctxt *ctxt; 37362306a36Sopenharmony_ci uint64_t *fixed_counters; 37462306a36Sopenharmony_ci struct xen_pmu_cntr_pair *arch_cntr_pair; 37562306a36Sopenharmony_ci struct xen_pmu_data *xenpmu_data = get_xenpmu_data(); 37662306a36Sopenharmony_ci uint8_t xenpmu_flags = get_xenpmu_flags(); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci if (!xenpmu_data || !(xenpmu_flags & XENPMU_IRQ_PROCESSING)) { 37962306a36Sopenharmony_ci uint32_t msr; 38062306a36Sopenharmony_ci int err; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci if (counter & (1 << INTEL_PMC_TYPE_SHIFT)) 38362306a36Sopenharmony_ci msr = MSR_CORE_PERF_FIXED_CTR0 + (counter & 0xffff); 38462306a36Sopenharmony_ci else 38562306a36Sopenharmony_ci msr = MSR_IA32_PERFCTR0 + counter; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci return native_read_msr_safe(msr, &err); 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci ctxt = &xenpmu_data->pmu.c.intel; 39162306a36Sopenharmony_ci if (counter & (1 << INTEL_PMC_TYPE_SHIFT)) { 39262306a36Sopenharmony_ci fixed_counters = field_offset(ctxt, fixed_counters); 39362306a36Sopenharmony_ci return fixed_counters[counter & 0xffff]; 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci arch_cntr_pair = field_offset(ctxt, arch_counters); 39762306a36Sopenharmony_ci return arch_cntr_pair[counter].counter; 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ciunsigned long long xen_read_pmc(int counter) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL) 40362306a36Sopenharmony_ci return xen_amd_read_pmc(counter); 40462306a36Sopenharmony_ci else 40562306a36Sopenharmony_ci return xen_intel_read_pmc(counter); 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ciint pmu_apic_update(uint32_t val) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci int ret; 41162306a36Sopenharmony_ci struct xen_pmu_data *xenpmu_data = get_xenpmu_data(); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci if (!xenpmu_data) { 41462306a36Sopenharmony_ci pr_warn_once("%s: pmudata not initialized\n", __func__); 41562306a36Sopenharmony_ci return -EINVAL; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci xenpmu_data->pmu.l.lapic_lvtpc = val; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci if (get_xenpmu_flags() & XENPMU_IRQ_PROCESSING) 42162306a36Sopenharmony_ci return 0; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci ret = HYPERVISOR_xenpmu_op(XENPMU_lvtpc_set, NULL); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci return ret; 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci/* perf callbacks */ 42962306a36Sopenharmony_cistatic unsigned int xen_guest_state(void) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci const struct xen_pmu_data *xenpmu_data = get_xenpmu_data(); 43262306a36Sopenharmony_ci unsigned int state = 0; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (!xenpmu_data) { 43562306a36Sopenharmony_ci pr_warn_once("%s: pmudata not initialized\n", __func__); 43662306a36Sopenharmony_ci return state; 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if (!xen_initial_domain() || (xenpmu_data->domain_id >= DOMID_SELF)) 44062306a36Sopenharmony_ci return state; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci state |= PERF_GUEST_ACTIVE; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci if (xenpmu_data->pmu.pmu_flags & PMU_SAMPLE_PV) { 44562306a36Sopenharmony_ci if (xenpmu_data->pmu.pmu_flags & PMU_SAMPLE_USER) 44662306a36Sopenharmony_ci state |= PERF_GUEST_USER; 44762306a36Sopenharmony_ci } else if (xenpmu_data->pmu.r.regs.cpl & 3) { 44862306a36Sopenharmony_ci state |= PERF_GUEST_USER; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci return state; 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic unsigned long xen_get_guest_ip(void) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci const struct xen_pmu_data *xenpmu_data = get_xenpmu_data(); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci if (!xenpmu_data) { 45962306a36Sopenharmony_ci pr_warn_once("%s: pmudata not initialized\n", __func__); 46062306a36Sopenharmony_ci return 0; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci return xenpmu_data->pmu.r.regs.ip; 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cistatic struct perf_guest_info_callbacks xen_guest_cbs = { 46762306a36Sopenharmony_ci .state = xen_guest_state, 46862306a36Sopenharmony_ci .get_ip = xen_get_guest_ip, 46962306a36Sopenharmony_ci}; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci/* Convert registers from Xen's format to Linux' */ 47262306a36Sopenharmony_cistatic void xen_convert_regs(const struct xen_pmu_regs *xen_regs, 47362306a36Sopenharmony_ci struct pt_regs *regs, uint64_t pmu_flags) 47462306a36Sopenharmony_ci{ 47562306a36Sopenharmony_ci regs->ip = xen_regs->ip; 47662306a36Sopenharmony_ci regs->cs = xen_regs->cs; 47762306a36Sopenharmony_ci regs->sp = xen_regs->sp; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci if (pmu_flags & PMU_SAMPLE_PV) { 48062306a36Sopenharmony_ci if (pmu_flags & PMU_SAMPLE_USER) 48162306a36Sopenharmony_ci regs->cs |= 3; 48262306a36Sopenharmony_ci else 48362306a36Sopenharmony_ci regs->cs &= ~3; 48462306a36Sopenharmony_ci } else { 48562306a36Sopenharmony_ci if (xen_regs->cpl) 48662306a36Sopenharmony_ci regs->cs |= 3; 48762306a36Sopenharmony_ci else 48862306a36Sopenharmony_ci regs->cs &= ~3; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci} 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ciirqreturn_t xen_pmu_irq_handler(int irq, void *dev_id) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci int err, ret = IRQ_NONE; 49562306a36Sopenharmony_ci struct pt_regs regs = {0}; 49662306a36Sopenharmony_ci const struct xen_pmu_data *xenpmu_data = get_xenpmu_data(); 49762306a36Sopenharmony_ci uint8_t xenpmu_flags = get_xenpmu_flags(); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (!xenpmu_data) { 50062306a36Sopenharmony_ci pr_warn_once("%s: pmudata not initialized\n", __func__); 50162306a36Sopenharmony_ci return ret; 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci this_cpu_ptr(&xenpmu_shared)->flags = 50562306a36Sopenharmony_ci xenpmu_flags | XENPMU_IRQ_PROCESSING; 50662306a36Sopenharmony_ci xen_convert_regs(&xenpmu_data->pmu.r.regs, ®s, 50762306a36Sopenharmony_ci xenpmu_data->pmu.pmu_flags); 50862306a36Sopenharmony_ci if (x86_pmu.handle_irq(®s)) 50962306a36Sopenharmony_ci ret = IRQ_HANDLED; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci /* Write out cached context to HW */ 51262306a36Sopenharmony_ci err = HYPERVISOR_xenpmu_op(XENPMU_flush, NULL); 51362306a36Sopenharmony_ci this_cpu_ptr(&xenpmu_shared)->flags = xenpmu_flags; 51462306a36Sopenharmony_ci if (err) { 51562306a36Sopenharmony_ci pr_warn_once("%s: failed hypercall, err: %d\n", __func__, err); 51662306a36Sopenharmony_ci return IRQ_NONE; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci return ret; 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_cibool is_xen_pmu; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_civoid xen_pmu_init(int cpu) 52562306a36Sopenharmony_ci{ 52662306a36Sopenharmony_ci int err; 52762306a36Sopenharmony_ci struct xen_pmu_params xp; 52862306a36Sopenharmony_ci unsigned long pfn; 52962306a36Sopenharmony_ci struct xen_pmu_data *xenpmu_data; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(struct xen_pmu_data) > PAGE_SIZE); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci if (xen_hvm_domain() || (cpu != 0 && !is_xen_pmu)) 53462306a36Sopenharmony_ci return; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci xenpmu_data = (struct xen_pmu_data *)get_zeroed_page(GFP_KERNEL); 53762306a36Sopenharmony_ci if (!xenpmu_data) { 53862306a36Sopenharmony_ci pr_err("VPMU init: No memory\n"); 53962306a36Sopenharmony_ci return; 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci pfn = virt_to_pfn(xenpmu_data); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci xp.val = pfn_to_mfn(pfn); 54462306a36Sopenharmony_ci xp.vcpu = cpu; 54562306a36Sopenharmony_ci xp.version.maj = XENPMU_VER_MAJ; 54662306a36Sopenharmony_ci xp.version.min = XENPMU_VER_MIN; 54762306a36Sopenharmony_ci err = HYPERVISOR_xenpmu_op(XENPMU_init, &xp); 54862306a36Sopenharmony_ci if (err) 54962306a36Sopenharmony_ci goto fail; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci per_cpu(xenpmu_shared, cpu).xenpmu_data = xenpmu_data; 55262306a36Sopenharmony_ci per_cpu(xenpmu_shared, cpu).flags = 0; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci if (!is_xen_pmu) { 55562306a36Sopenharmony_ci is_xen_pmu = true; 55662306a36Sopenharmony_ci perf_register_guest_info_callbacks(&xen_guest_cbs); 55762306a36Sopenharmony_ci xen_pmu_arch_init(); 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci return; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_cifail: 56362306a36Sopenharmony_ci if (err == -EOPNOTSUPP || err == -ENOSYS) 56462306a36Sopenharmony_ci pr_info_once("VPMU disabled by hypervisor.\n"); 56562306a36Sopenharmony_ci else 56662306a36Sopenharmony_ci pr_info_once("Could not initialize VPMU for cpu %d, error %d\n", 56762306a36Sopenharmony_ci cpu, err); 56862306a36Sopenharmony_ci free_pages((unsigned long)xenpmu_data, 0); 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_civoid xen_pmu_finish(int cpu) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci struct xen_pmu_params xp; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci if (xen_hvm_domain()) 57662306a36Sopenharmony_ci return; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci xp.vcpu = cpu; 57962306a36Sopenharmony_ci xp.version.maj = XENPMU_VER_MAJ; 58062306a36Sopenharmony_ci xp.version.min = XENPMU_VER_MIN; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci (void)HYPERVISOR_xenpmu_op(XENPMU_finish, &xp); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci free_pages((unsigned long)per_cpu(xenpmu_shared, cpu).xenpmu_data, 0); 58562306a36Sopenharmony_ci per_cpu(xenpmu_shared, cpu).xenpmu_data = NULL; 58662306a36Sopenharmony_ci} 587