This unit-test is intended to test the KVM's support for the
Architectural LBRs which is a Architectural performance monitor
unit (PMU) feature on Intel processors.
If the LBR bit is set to 1 in the MSR_ARCH_LBR_CTL, the processor
will record a running trace of the most recent branches guest
taken in the LBR entries for guest to read.
Signed-off-by: Like Xu <[email protected]>
---
x86/pmu_lbr.c | 62 ++++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 52 insertions(+), 10 deletions(-)
diff --git a/x86/pmu_lbr.c b/x86/pmu_lbr.c
index 3bd9e9f..588aec8 100644
--- a/x86/pmu_lbr.c
+++ b/x86/pmu_lbr.c
@@ -6,6 +6,7 @@
#define MAX_NUM_LBR_ENTRY 32
#define DEBUGCTLMSR_LBR (1UL << 0)
#define PMU_CAP_LBR_FMT 0x3f
+#define KVM_ARCH_LBR_CTL_MASK 0x7f000f
#define MSR_LBR_NHM_FROM 0x00000680
#define MSR_LBR_NHM_TO 0x000006c0
@@ -13,6 +14,10 @@
#define MSR_LBR_CORE_TO 0x00000060
#define MSR_LBR_TOS 0x000001c9
#define MSR_LBR_SELECT 0x000001c8
+#define MSR_ARCH_LBR_CTL 0x000014ce
+#define MSR_ARCH_LBR_DEPTH 0x000014cf
+#define MSR_ARCH_LBR_FROM_0 0x00001500
+#define MSR_ARCH_LBR_TO_0 0x00001600
volatile int count;
@@ -66,6 +71,9 @@ int main(int ac, char **av)
struct cpuid id = cpuid(10);
u64 perf_cap;
int max, i;
+ bool arch_lbr = false;
+ u32 ctl_msr = MSR_IA32_DEBUGCTLMSR;
+ u64 ctl_value = DEBUGCTLMSR_LBR;
setup_vm();
perf_cap = rdmsr(MSR_IA32_PERF_CAPABILITIES);
@@ -80,8 +88,23 @@ int main(int ac, char **av)
return report_summary();
}
+ /*
+ * On processors that support Architectural LBRs,
+ * IA32_PERF_CAPABILITIES.LBR_FMT will have the value 03FH.
+ */
+ if (0x3f == (perf_cap & PMU_CAP_LBR_FMT)) {
+ arch_lbr = true;
+ ctl_msr = MSR_ARCH_LBR_CTL;
+ /* DEPTH defaults to the maximum number of LBRs entries. */
+ max = rdmsr(MSR_ARCH_LBR_DEPTH) - 1;
+ ctl_value = KVM_ARCH_LBR_CTL_MASK;
+ }
+
printf("PMU version: %d\n", eax.split.version_id);
- printf("LBR version: %ld\n", perf_cap & PMU_CAP_LBR_FMT);
+ if (!arch_lbr)
+ printf("LBR version: %ld\n", perf_cap & PMU_CAP_LBR_FMT);
+ else
+ printf("Architectural LBR depth: %d\n", max + 1);
/* Look for LBR from and to MSRs */
lbr_from = MSR_LBR_CORE_FROM;
@@ -90,27 +113,46 @@ int main(int ac, char **av)
lbr_from = MSR_LBR_NHM_FROM;
lbr_to = MSR_LBR_NHM_TO;
}
+ if (test_init_lbr_from_exception(0)) {
+ lbr_from = MSR_ARCH_LBR_FROM_0;
+ lbr_to = MSR_ARCH_LBR_TO_0;
+ }
if (test_init_lbr_from_exception(0)) {
printf("LBR on this platform is not supported!\n");
return report_summary();
}
- wrmsr(MSR_LBR_SELECT, 0);
- wrmsr(MSR_LBR_TOS, 0);
- for (max = 0; max < MAX_NUM_LBR_ENTRY; max++) {
- if (test_init_lbr_from_exception(max))
- break;
+ /* Reset the guest LBR entries. */
+ if (arch_lbr) {
+ /* On a software write to IA32_LBR_DEPTH, all LBR entries are reset to 0.*/
+ wrmsr(MSR_ARCH_LBR_DEPTH, max + 1);
+ } else {
+ wrmsr(MSR_LBR_SELECT, 0);
+ wrmsr(MSR_LBR_TOS, 0);
+ for (max = 0; max < MAX_NUM_LBR_ENTRY; max++) {
+ if (test_init_lbr_from_exception(max))
+ break;
+ }
}
-
report(max > 0, "The number of guest LBR entries is good.");
+ /* Check the guest LBR entries are initialized. */
+ for (i = 0; i < max; ++i) {
+ if (rdmsr(lbr_to + i) || rdmsr(lbr_from + i))
+ break;
+ }
+ report(i == max, "The guest LBR initialized FROM_IP/TO_IP values are good.");
+
/* Do some branch instructions. */
- wrmsr(MSR_IA32_DEBUGCTLMSR, DEBUGCTLMSR_LBR);
+ wrmsr(ctl_msr, ctl_value);
lbr_test();
- wrmsr(MSR_IA32_DEBUGCTLMSR, 0);
+ wrmsr(ctl_msr, 0);
- report(rdmsr(MSR_LBR_TOS) != 0, "The guest LBR MSR_LBR_TOS value is good.");
+ /* Check if the guest LBR has recorded some branches. */
+ if (!arch_lbr) {
+ report(rdmsr(MSR_LBR_TOS) != 0, "The guest LBR MSR_LBR_TOS value is good.");
+ }
for (i = 0; i < max; ++i) {
if (!rdmsr(lbr_to + i) || !rdmsr(lbr_from + i))
break;
--
2.29.2
On Wed, Mar 03, 2021, Like Xu wrote:
> This unit-test is intended to test the KVM's support for the
> Architectural LBRs which is a Architectural performance monitor
> unit (PMU) feature on Intel processors.
These really need negative testing, especially on the MSR values. IMO, negative
tests should be mandatory for merging arch LBR support in KVM, it shouldn't be
too much additional effort.