Re: [kvm-unit-tests PATCH] x86: Add Arch LBR unit-test application

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 6/8/2021 3:43 pm, Yang Weijiang wrote:
This unit-test app targets to check whehter Arch LBR is enabled

s/whehter/whether/

for guest. XSAVES/XRSTORS are used to accelerate LBR MSR save/restore.

This test just verifies that LBR records are not changed or lost before and after
the XSAVES/XRSTORS operations instead of measuring the acceleration.

Please add more sub-testcases:
- Sanity checks about valid MSR_ARCH_LBR_CTL_bitmask;
- On a software write to IA32_LBR_DEPTH, all LBR entries are reset to 0;
- LBR from the nested VM since you added the nested support (not in immediate need);
- Check the #DB/#SMI behavior about both leagcy and Arch LBR logging;


Signed-off-by: Yang Weijiang <weijiang.yang@xxxxxxxxx>
---
  x86/Makefile.x86_64 |   1 +
  x86/pmu_arch_lbr.c  | 221 ++++++++++++++++++++++++++++++++++++++++++++
  x86/unittests.cfg   |   6 ++
  3 files changed, 228 insertions(+)
  create mode 100644 x86/pmu_arch_lbr.c

diff --git a/x86/Makefile.x86_64 b/x86/Makefile.x86_64
index 8134952..0727830 100644
--- a/x86/Makefile.x86_64
+++ b/x86/Makefile.x86_64
@@ -24,6 +24,7 @@ tests += $(TEST_DIR)/vmware_backdoors.flat
  tests += $(TEST_DIR)/rdpru.flat
  tests += $(TEST_DIR)/pks.flat
  tests += $(TEST_DIR)/pmu_lbr.flat
+tests += $(TEST_DIR)/pmu_arch_lbr.flat

Why separate into two files considering some basic test codes can be reused ?

ifneq ($(fcf_protection_full),)
  tests += $(TEST_DIR)/cet.flat
diff --git a/x86/pmu_arch_lbr.c b/x86/pmu_arch_lbr.c
new file mode 100644
index 0000000..9a1e562
--- /dev/null
+++ b/x86/pmu_arch_lbr.c
@@ -0,0 +1,221 @@
+#include "asm-generic/page.h"
+#include "x86/processor.h"
+#include "x86/msr.h"
+#include "x86/desc.h"
+#include "bitops.h"
+
+#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
+#define MSR_ARCH_LBR_INFO_0             0x00001200
+
+#define MSR_IA32_XSS                    0x00000da0
+
+#define IA32_XSS_ARCH_LBR               (1UL << 15)
+#define CR4_OSXSAVE_BIT                 (1UL << 18)
+#define CPUID_EDX_ARCH_LBR              (1UL << 19)
+
+#define ARCH_LBR_CTL_BITS               0x3f0003
+#define MAX_LBR_DEPTH                   32
+
+#define XSAVES		".byte 0x48,0x0f,0xc7,0x2f\n\t"
+#define XRSTORS		".byte 0x48,0x0f,0xc7,0x1f\n\t"
+
+struct xstate_header {
+	u64 xfeatures;
+	u64 xcomp_bv;
+	u64 reserved[6];
+} __attribute__((packed));
+
+struct arch_lbr_entry {
+	u64 lbr_from;
+	u64 lbr_to;
+	u64 lbr_info;
+}__attribute__((packed));
+
+struct arch_lbr_struct {
+	u64 lbr_ctl;
+	u64 lbr_depth;
+	u64 ler_from;
+	u64 ler_to;
+	u64 ler_info;
+	struct arch_lbr_entry lbr_records[MAX_LBR_DEPTH];
+}__attribute__((packed));
+
+struct xsave_struct {
+	u8 fpu_sse[512];
+	struct xstate_header xstate_hdr;
+	struct arch_lbr_struct records;
+} __attribute__((packed));
+
+u8 __attribute__((__aligned__(64))) xsave_buffer[PAGE_SIZE];
+
+struct xsave_struct *test_buf = (struct xsave_struct *)xsave_buffer;
+
+u64 lbr_from[MAX_LBR_DEPTH], lbr_to[MAX_LBR_DEPTH], lbr_info[MAX_LBR_DEPTH];
+
+u64 lbr_ctl, lbr_depth;
+
+volatile int count;
+
+static __attribute__((noinline)) int compute_flag(int i)
+{
+	if (i % 10 < 4)
+		return i + 1;
+	return 0;
+}
+
+static __attribute__((noinline)) int lbr_test(void)
+{
+	int i;
+	int flag;
+	volatile double x = 1212121212, y = 121212;
+
+	for (i = 0; i < 200000000; i++) {
+		flag = compute_flag(i);
+		count++;
+		if (flag)
+			x += x / y + y / x;
+	}
+	return 0;
+}
+
+static inline void xrstors(struct xsave_struct *fx, unsigned long  mask)
+{
+        u32 lmask = mask;
+        u32 hmask = mask >> 32;
+
+        asm volatile(XRSTORS
+                     : : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask)
+                     : "memory");
+}
+
+static inline int xsaves(struct xsave_struct *fx, unsigned long mask)
+{
+        u32 lmask = mask;
+        u32 hmask = mask >> 32;
+	int err = 0;
+
+        asm volatile(XSAVES
+                     : [err] "=r" (err)  : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask)
+                     : "memory");
+	return err;
+}
+
+static void clear_lbr_records(void)
+{
+	int i;
+
+	for (i = 0; i < lbr_depth; ++i) {
+		wrmsr(MSR_ARCH_LBR_FROM_0 + i, 0);
+		wrmsr(MSR_ARCH_LBR_TO_0 + i, 0);
+		wrmsr(MSR_ARCH_LBR_INFO_0 + i, 0);
+	}
+}

"On a software write to IA32_LBR_DEPTH, all LBR entries are reset to 0."

+
+static bool check_xsaves_records(void)
+{
+	int i;
+	struct arch_lbr_entry *records = test_buf->records.lbr_records;
+
+	for (i = 0; i < lbr_depth; ++i) {
+		if (lbr_from[i] != (*(records + i)).lbr_from ||
+		    lbr_to[i] != (*(records + i)).lbr_to ||
+		    lbr_info[i] != (*(records + i)).lbr_info)
+			break;
+	}
+
+	return i == lbr_depth;
+}
+
+static bool check_msrs_records(void)
+{
+	int i;
+
+	for (i = 0; i < lbr_depth; ++i) {
+		if (lbr_from[i] != rdmsr(MSR_ARCH_LBR_FROM_0 + i) ||
+		    lbr_to[i] != rdmsr(MSR_ARCH_LBR_TO_0 + i) ||
+		    lbr_info[i] != rdmsr(MSR_ARCH_LBR_INFO_0 + i))
+			break;
+	}
+
+	return i == lbr_depth;
+}
+
+static void test_with_xsaves(void)
+{
+	u32 cr4;
+
+	/* Only test Arch LBR save/restore, ignore other features.*/
+	wrmsr(MSR_IA32_XSS, IA32_XSS_ARCH_LBR);
+
+	cr4 = read_cr4();
+	write_cr4(cr4 | CR4_OSXSAVE_BIT);
+
+	xsaves(test_buf, IA32_XSS_ARCH_LBR | 0x3);
+
+	report(check_xsaves_records(),
+	       "The LBR records in XSAVES area match the MSR values!");
+
+	clear_lbr_records();
+
+	xrstors(test_buf, IA32_XSS_ARCH_LBR | 0x3);
+
+	report(check_msrs_records(),
+	       "The restored LBR MSR values match the original ones!");
+}
+
+int main(int ac, char **av)
+{
+	struct cpuid id;
+	int i;
+
+	id = cpuid(0x7);
+	if (!(id.d & CPUID_EDX_ARCH_LBR)) {
+		printf("No Arch LBR is detected!\n");
+		return report_summary();
+	}
+
+	id = raw_cpuid(0xd, 1);
+	if (!(id.a & 0x8)) {
+		printf("XSAVES is not supported!.\n");
+		return report_summary();
+	}
+
+	setup_vm();
+
+	id = cpuid(0x1c);
+	lbr_depth = (fls(id.a & 0xff) + 1)*8;
+
+	wrmsr(MSR_ARCH_LBR_DEPTH, lbr_depth);

Need to check if all LBR entries are reset to 0 to avoid contamination.

+
+	lbr_ctl = ARCH_LBR_CTL_BITS;
+	wrmsr(MSR_ARCH_LBR_CTL, lbr_ctl);
+
+	lbr_test();
+
+	/* Disable Arch LBR sampling before run sanity checks. */
+	lbr_ctl &= ~0x1;
+	wrmsr(MSR_ARCH_LBR_CTL, lbr_ctl);
+
+	/*
+	 * LBR records are kept at this point, need to save them
+	 * ASAP, otherwise they could be reset to 0s.

How ? LBR has just been disabled, so why is it possible to reset it to 0s.

+	 */
+	for (i = 0; i < lbr_depth; ++i) {
+		if (!(lbr_from[i] = rdmsr(MSR_ARCH_LBR_FROM_0 + i)) ||
+		    !(lbr_to[i] = rdmsr(MSR_ARCH_LBR_TO_0 + i)) ||
+		    !(lbr_info[i] = rdmsr(MSR_ARCH_LBR_INFO_0 + i)))
+			break;
+	}
+
+	if (i != lbr_depth) {
+		printf("Invalid Arch LBR records.\n");
+		return report_summary();
+	}
+
+	test_with_xsaves();
+
+	return report_summary();
+}
diff --git a/x86/unittests.cfg b/x86/unittests.cfg
index d5efab0..88b2203 100644
--- a/x86/unittests.cfg
+++ b/x86/unittests.cfg
@@ -185,6 +185,12 @@ extra_params = -cpu host,migratable=no
  check = /sys/module/kvm/parameters/ignore_msrs=N
  check = /proc/sys/kernel/nmi_watchdog=0
+[pmu_arch_lbr]
+file = pmu_arch_lbr.flat
+extra_params = -cpu host,lbr-fmt=0x3f

Ugh, please keep using "migratable=no"

+check = /sys/module/kvm/parameters/ignore_msrs=N
+check = /proc/sys/kernel/nmi_watchdog=0

For nmi_watchdog=1, the Arch LBR test should pass as well.

+
  [vmware_backdoors]
  file = vmware_backdoors.flat
  extra_params = -machine vmport=on -cpu max




[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux