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