Once IA32_DEBUGCTL.FREEZE_LBR_ON_PMI is set, LBR stack will be frozen on PMI. This commit add a test case to check whether LBR stack is changed during PMI handler or not. In PMU v2, legacy Freeze LBRS on PMI is introduced, IA32_DEBUGCTL.FREEZE_LBR_ON_PMI will be cleared by processor when PMI happens, SW should set it again in PMI handler to enable LBR. In PMU v4, streamlined freeze LBRs on PMI is introduced, the new LBR_FRZ[bit 58] bit is added into IA32_PERF_GLOBAL_STATUS MSR, this bit is set by processor when PMI happens, this bit also serves as a control to enable LBR stack. SW should clear this bit in PMI handler to enable LBR. This commit checks legacy and streamlined FREEZE_LBR_ON_PMI feature, and their SW and HW sequence. Signed-off-by: Xiong Zhang <xiong.y.zhang@xxxxxxxxx> --- lib/x86/msr.h | 3 ++ x86/pmu_lbr.c | 109 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 111 insertions(+), 1 deletion(-) diff --git a/lib/x86/msr.h b/lib/x86/msr.h index 0e3fd03..9748436 100644 --- a/lib/x86/msr.h +++ b/lib/x86/msr.h @@ -430,6 +430,9 @@ #define MSR_CORE_PERF_GLOBAL_CTRL 0x0000038f #define MSR_CORE_PERF_GLOBAL_OVF_CTRL 0x00000390 +/* PERF_GLOBAL_OVF_CTRL bits */ +#define MSR_CORE_PERF_GLOBAL_OVF_CTRL_LBR_FREEZE (1ULL << 58) + /* AMD Performance Counter Global Status and Control MSRs */ #define MSR_AMD64_PERF_CNTR_GLOBAL_STATUS 0xc0000300 #define MSR_AMD64_PERF_CNTR_GLOBAL_CTL 0xc0000301 diff --git a/x86/pmu_lbr.c b/x86/pmu_lbr.c index 40b63fa..24220f0 100644 --- a/x86/pmu_lbr.c +++ b/x86/pmu_lbr.c @@ -2,11 +2,17 @@ #include "x86/processor.h" #include "x86/pmu.h" #include "x86/desc.h" +#include "x86/apic-defs.h" +#include "x86/apic.h" +#include "x86/isr.h" #define N 1000000 +#define MAX_LBR 64 volatile int count; u32 lbr_from, lbr_to; +int max; +bool pmi_received = false; static noinline int compute_flag(int i) { @@ -41,9 +47,102 @@ static bool test_init_lbr_from_exception(u64 index) return test_for_exception(GP_VECTOR, init_lbr, &index); } +static void pmi_handler(isr_regs_t *regs) +{ + uint64_t lbr_tos, from[MAX_LBR], to[MAX_LBR]; + uint64_t gbl_status, debugctl = 0, lbr_cur; + int i; + + pmi_received = true; + + if (pmu.version < 4) { + debugctl = rdmsr(MSR_IA32_DEBUGCTLMSR); + report((debugctl & DEBUGCTLMSR_LBR) == 0, + "The guest LBR_EN is cleared in guest PMI"); + gbl_status = rdmsr(pmu.msr_global_status); + report((gbl_status & BIT_ULL(0)) == BIT_ULL(0), + "GP counter 0 overflow."); + } else { + gbl_status = rdmsr(pmu.msr_global_status); + report((gbl_status & (MSR_CORE_PERF_GLOBAL_OVF_CTRL_LBR_FREEZE | BIT_ULL(0))) + == (MSR_CORE_PERF_GLOBAL_OVF_CTRL_LBR_FREEZE | BIT_ULL(0)), + "GP counter 0 overflow and LBR freeze."); + } + + lbr_tos = rdmsr(MSR_LBR_TOS); + for (i = 0; i < max; ++i) { + from[i] = rdmsr(lbr_from + i); + to[i] = rdmsr(lbr_to + i); + } + + lbr_test(); + + lbr_cur = rdmsr(MSR_LBR_TOS); + report(lbr_cur == lbr_tos, + "LBR TOS freezed in PMI, %lx -> %lx", lbr_tos, lbr_cur); + for (i = 0; i < max; ++i) { + lbr_cur = rdmsr(lbr_from + i); + report(lbr_cur == from[i], + "LBR from %d freezed in PMI, %lx -> %lx", i, from[i], lbr_cur); + lbr_cur = rdmsr(lbr_to + i); + report(lbr_cur == to[i], + "LBR to %d freezed in PMI, %lx -> %lx", i, to[i], lbr_cur); + } + + if (pmu.version < 4) { + debugctl |= DEBUGCTLMSR_LBR; + wrmsr(MSR_IA32_DEBUGCTLMSR, debugctl); + } + + wrmsr(pmu.msr_global_status_clr, gbl_status); + + apic_write(APIC_EOI, 0); +} + +/* GP counter 0 overflow after 2 instructions. */ +static void setup_gp_counter_0_overflow(void) +{ + uint64_t count, ctrl; + int i; + + count = (1ull << pmu.gp_counter_width) - 1 - 2; + wrmsr(pmu.msr_gp_counter_base, count); + + ctrl = EVNTSEL_EN | EVNTSEL_USR | EVNTSEL_OS | EVNTSEL_INT | 0xc0; + wrmsr(pmu.msr_gp_event_select_base, ctrl); + + wrmsr(pmu.msr_global_ctl, 0x1); + + apic_write(APIC_LVTPC, PMI_VECTOR); + + irq_enable(); + asm volatile("nop; nop; nop"); + for (i=0; i < 100000 && !pmi_received; i++) + asm volatile("pause"); + irq_disable(); +} + +static void stop_gp_counter_0(void) +{ + wrmsr(pmu.msr_global_ctl, 0); + wrmsr(pmu.msr_gp_event_select_base, 0); +} + +static void test_freeze_lbr_on_pmi(void) +{ + wrmsr(MSR_IA32_DEBUGCTLMSR, + DEBUGCTLMSR_LBR | DEBUGCTLMSR_FREEZE_LBRS_ON_PMI); + + setup_gp_counter_0_overflow(); + + stop_gp_counter_0(); + + wrmsr(MSR_IA32_DEBUGCTLMSR, 0); +} + int main(int ac, char **av) { - int max, i; + int i; setup_vm(); @@ -70,6 +169,12 @@ int main(int ac, char **av) printf("PMU version: %d\n", pmu.version); printf("LBR version: %ld\n", pmu_lbr_version()); + handle_irq(PMI_VECTOR, pmi_handler); + apic_write(APIC_LVTPC, PMI_VECTOR); + + if (pmu_has_full_writes()) + pmu.msr_gp_counter_base = MSR_IA32_PMC0; + /* Look for LBR from and to MSRs */ lbr_from = MSR_LBR_CORE_FROM; lbr_to = MSR_LBR_CORE_TO; @@ -104,5 +209,7 @@ int main(int ac, char **av) } report(i == max, "The guest LBR FROM_IP/TO_IP values are good."); + test_freeze_lbr_on_pmi(); + return report_summary(); } -- 2.34.1