diff mbox series

[kvm-unit-tests,2/6] x86: pmu: Add Freeze_LBRS_On_PMI test case

Message ID 20230901074052.640296-3-xiong.y.zhang@intel.com (mailing list archive)
State New, archived
Headers show
Series vPMU v5 test case | expand

Commit Message

Zhang, Xiong Y Sept. 1, 2023, 7:40 a.m. UTC
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@intel.com>
---
 lib/x86/msr.h |   3 ++
 x86/pmu_lbr.c | 109 +++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 111 insertions(+), 1 deletion(-)
diff mbox series

Patch

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();
 }