[kvm-unit-tests PATCH v1] x86: Add test case for LAM_SUP

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

 



This unit test covers:
1. CR4.LAM_SUP toggle has expected behavior according to LAM status.
2. Memory access (here is strcpy() for test example) with supervisor mode
address containing LAM meta data, behave as expected per LAM status.
3. MMIO memory access with supervisor mode address containing LAM meta
data, behave as expected per LAM status.

In x86/unittests.cfg, add 2 test cases/guest conf, with and without LAM.

Note:
LAM_U57 is covered by running kselftests/x86/lam in guest; and
exepecting LAM_U48 test will be complemented there when Kernel supports it.

LAM feature spec: https://cdrdv2.intel.com/v1/dl/getContent/671368, Chap 10
LINEAR ADDRESS MASKING (LAM)

Signed-off-by: Robert Hoo <robert.hu@xxxxxxxxxxxxxxx>
---
 lib/x86/processor.h |   3 +
 x86/Makefile.x86_64 |   1 +
 x86/lam_sup.c       | 170 ++++++++++++++++++++++++++++++++++++++++++++
 x86/unittests.cfg   |  10 +++
 4 files changed, 184 insertions(+)
 create mode 100644 x86/lam_sup.c

diff --git a/lib/x86/processor.h b/lib/x86/processor.h
index 3d58ef7..c6b1db6 100644
--- a/lib/x86/processor.h
+++ b/lib/x86/processor.h
@@ -105,6 +105,8 @@
 #define X86_CR4_CET		BIT(X86_CR4_CET_BIT)
 #define X86_CR4_PKS_BIT		(24)
 #define X86_CR4_PKS		BIT(X86_CR4_PKS_BIT)
+#define X86_CR4_LAM_SUP_BIT	(28)
+#define X86_CR4_LAM_SUP	BIT(X86_CR4_LAM_SUP_BIT)
 
 #define X86_EFLAGS_CF_BIT	(0)
 #define X86_EFLAGS_CF		BIT(X86_EFLAGS_CF_BIT)
@@ -248,6 +250,7 @@ static inline bool is_intel(void)
 #define	X86_FEATURE_SPEC_CTRL		(CPUID(0x7, 0, EDX, 26))
 #define	X86_FEATURE_ARCH_CAPABILITIES	(CPUID(0x7, 0, EDX, 29))
 #define	X86_FEATURE_PKS			(CPUID(0x7, 0, ECX, 31))
+#define	X86_FEATURE_LAM			(CPUID(0x7, 1, EAX, 26))
 
 /*
  * Extended Leafs, a.k.a. AMD defined
diff --git a/x86/Makefile.x86_64 b/x86/Makefile.x86_64
index f483dea..af626cc 100644
--- a/x86/Makefile.x86_64
+++ b/x86/Makefile.x86_64
@@ -34,6 +34,7 @@ tests += $(TEST_DIR)/rdpru.$(exe)
 tests += $(TEST_DIR)/pks.$(exe)
 tests += $(TEST_DIR)/pmu_lbr.$(exe)
 tests += $(TEST_DIR)/pmu_pebs.$(exe)
+tests += $(TEST_DIR)/lam_sup.$(exe)
 
 ifeq ($(CONFIG_EFI),y)
 tests += $(TEST_DIR)/amd_sev.$(exe)
diff --git a/x86/lam_sup.c b/x86/lam_sup.c
new file mode 100644
index 0000000..67d5b5e
--- /dev/null
+++ b/x86/lam_sup.c
@@ -0,0 +1,170 @@
+/*
+ * Intel LAM_SUP unit test
+ *
+ * Copyright (C) 2023 Intel
+ *
+ * Author: Robert Hoo <robert.hu@xxxxxxxxxxxxxxx>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or
+ * later.
+ */
+
+#include "libcflat.h"
+#include "processor.h"
+#include "desc.h"
+#include "vmalloc.h"
+#include "alloc_page.h"
+#include "vm.h"
+#include "asm/io.h"
+#include "ioram.h"
+
+#define LAM57_BITS 6
+#define LAM48_BITS 15
+#define LAM57_MASK	GENMASK_ULL(62, 57)
+#define LAM48_MASK	GENMASK_ULL(62, 48)
+
+static int gp_count;
+static jmp_buf jbuf;
+
+static int get_lam_bits(void)
+{
+	if (this_cpu_has(X86_FEATURE_LA57) && read_cr4() & X86_CR4_LA57)
+		return LAM57_BITS;
+	else
+		return LAM48_BITS;
+}
+
+/* According to LAM mode, set metadata in high bits */
+static u64 set_metadata(u64 src, unsigned long lam)
+{
+	u64 metadata;
+
+	switch (lam) {
+	case LAM57_BITS: /* Set metadata in bits 62:57 */
+		metadata = (rdtsc() & ((1UL << LAM57_BITS) - 1)) << 57;
+		metadata |= (src & ~(LAM57_MASK));
+		break;
+	case LAM48_BITS: /* Set metadata in bits 62:48 */
+		metadata = (rdtsc() & ((1UL << LAM48_BITS) - 1)) << 48;
+		metadata |= (src & ~(LAM48_MASK));
+		break;
+	default:
+		metadata = src;
+		break;
+	}
+
+	return metadata;
+}
+
+static void handle_gp(struct ex_regs *regs)
+{
+	report_info("#GP caught, error_code = %ld\n", regs->error_code);
+	gp_count++;
+	longjmp(jbuf, 1);
+}
+
+/* Refer to emulator.c */
+static void test_mov(void *mem)
+{
+	unsigned long t1, t2;
+
+	// test mov reg, r/m and mov r/m, reg
+	t1 = 0x123456789abcdefull & -1ul;
+	asm volatile("mov %[t1], (%[mem])\n\t"
+		     "mov (%[mem]), %[t2]"
+		     : [t2]"=r"(t2)
+		     : [t1]"r"(t1), [mem]"r"(mem)
+		     : "memory");
+}
+
+
+int main(int ac, char **av)
+{
+	unsigned long cr4;
+	volatile bool lam_enumerated;
+	int vector, expect_vector;
+	u64 *ptr;
+	int lam_bits;
+	void *vaddr, *mem;
+	phys_addr_t paddr;
+	handler old;
+
+	lam_enumerated = this_cpu_has(X86_FEATURE_LAM);
+	if (!lam_enumerated)
+		report_info("This CPU doesn't support LAM feature\n");
+	else
+		report_info("This CPU supports LAM feature\n");
+
+	expect_vector = lam_enumerated ? 0 : GP_VECTOR;
+
+	/* Set CR4.LAM_SUP */
+	cr4 = read_cr4();
+	vector = write_cr4_safe(cr4 | X86_CR4_LAM_SUP);
+
+	if (lam_enumerated)
+		report(vector == expect_vector && (cr4 | X86_CR4_LAM_SUP) == read_cr4(),
+		       "Set CR4.LAM_SUP");
+	else
+		report(vector == expect_vector, "Set CR4.LAM_SUP");
+
+	/* Clear CR4.LAM_SUP */
+	cr4 = read_cr4();
+	vector = write_cr4_safe(cr4 & ~X86_CR4_LAM_SUP);
+	expect_vector = 0;
+	report(vector == expect_vector && (cr4 & ~X86_CR4_LAM_SUP) == read_cr4(),
+	       "Clear CR4.LAM_SUP");
+
+	/* Re-set CR4.LAM_SUP for next tests */
+	cr4 = read_cr4();
+	vector = write_cr4_safe(cr4 | X86_CR4_LAM_SUP);
+	expect_vector = lam_enumerated ? 0 : GP_VECTOR;
+	if (lam_enumerated)
+		report(vector == expect_vector && (cr4 | X86_CR4_LAM_SUP) == read_cr4(),
+			"Re-enable CR4.LAM_SUP");
+	else
+		report(vector == expect_vector, "Re-enable CR4.LAM_SUP");
+
+	/* Try access Supervisor mode address with meta data */
+	setup_vm();
+	vaddr = alloc_vpage();
+	paddr = virt_to_phys(alloc_page());
+
+	install_page(current_page_table(), paddr, vaddr);
+	old = handle_exception(GP_VECTOR, handle_gp);
+
+	strcpy((char *)vaddr, "LAM SUP Test origin string.");
+
+	lam_bits = get_lam_bits();
+	ptr = (u64 *)set_metadata((u64)vaddr, lam_bits);
+
+	if (setjmp(jbuf) == 0)
+		strcpy((char *)ptr, "LAM SUP Test NEW string.");
+
+	if (lam_enumerated && (read_cr4() & X86_CR4_LAM_SUP))
+		report(gp_count == 0, "strcpy with tagged addr succeed");
+	else
+		report(gp_count > 0, "strcpy with tagged addr cause #GP");
+
+	/* emulator coverage. referred to emulator.c */
+	gp_count = 0;
+	mem = alloc_vpage();
+	install_page((void *)read_cr3(), IORAM_BASE_PHYS, mem);
+
+	ptr = (u64 *)set_metadata((u64)mem, lam_bits);
+	if (setjmp(jbuf) == 0)
+		test_mov(ptr);
+
+	if (lam_enumerated && (read_cr4() & X86_CR4_LAM_SUP))
+		report(gp_count == 0, "MMIO cpy test for emulator succeed");
+	else
+		report(gp_count > 0, "MMIO cpy test for emulator cause #GP");
+
+	/*
+	 * Restore old #GP handler, though mostly likely effectively
+	 * unnecessary, for symmetry and conservativeness.
+	 */
+	handle_exception(GP_VECTOR, old);
+
+	return report_summary();
+}
+
diff --git a/x86/unittests.cfg b/x86/unittests.cfg
index f324e32..08a9b20 100644
--- a/x86/unittests.cfg
+++ b/x86/unittests.cfg
@@ -478,3 +478,13 @@ file = cet.flat
 arch = x86_64
 smp = 2
 extra_params = -enable-kvm -m 2048 -cpu host
+
+[intel-lam]
+file = lam_sup.flat
+arch = x86_64
+extra_params = -enable-kvm -cpu host
+
+[intel-no-lam]
+file = lam_sup.flat
+arch = x86_64
+extra_params = -enable-kvm -cpu host,-lam

base-commit: e3c5c3ef2524c58023073c0fadde2e8ae3c04ec6
-- 
2.31.1




[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