From e6d7bd2aa4f185881714a6103e9e672b0dbd12ab Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Fri, 16 Aug 2024 12:40:20 +0300 Subject: [PATCH 1/2] Add test for writing canonical values to various msrs Signed-off-by: Maxim Levitsky --- lib/x86/asm/setup.h | 1 + lib/x86/processor.h | 5 + lib/x86/vm.h | 1 + x86/Makefile.x86_64 | 1 + x86/cstart64.S | 35 +++++++ x86/msr_canonical.c | 236 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 279 insertions(+) create mode 100644 x86/msr_canonical.c diff --git a/lib/x86/asm/setup.h b/lib/x86/asm/setup.h index 458eac858..399ced1f9 100644 --- a/lib/x86/asm/setup.h +++ b/lib/x86/asm/setup.h @@ -14,6 +14,7 @@ unsigned long setup_tss(u8 *stacktop); efi_status_t setup_efi(efi_bootinfo_t *efi_bootinfo); void setup_5level_page_table(void); +void setup_4level_page_table(void); #endif /* CONFIG_EFI */ void save_id(void); diff --git a/lib/x86/processor.h b/lib/x86/processor.h index da1ed6628..d478eff91 100644 --- a/lib/x86/processor.h +++ b/lib/x86/processor.h @@ -468,6 +468,11 @@ static inline int rdmsr_safe(u32 index, uint64_t *val) return rdreg64_safe("rdmsr", index, val); } +static inline int rdmsr_fep_safe(u32 index, uint64_t *val) +{ + return __rdreg64_safe(KVM_FEP, "rdmsr", index, val); +} + static inline int wrmsr_safe(u32 index, u64 val) { return wrreg64_safe("wrmsr", index, val); diff --git a/lib/x86/vm.h b/lib/x86/vm.h index cf39787aa..60ace1a84 100644 --- a/lib/x86/vm.h +++ b/lib/x86/vm.h @@ -8,6 +8,7 @@ #include "asm/bitops.h" void setup_5level_page_table(void); +void setup_4level_page_table(void); struct pte_search { int level; diff --git a/x86/Makefile.x86_64 b/x86/Makefile.x86_64 index 2771a6fad..1bc1c10b0 100644 --- a/x86/Makefile.x86_64 +++ b/x86/Makefile.x86_64 @@ -38,6 +38,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)/msr_canonical.$(exe) ifeq ($(CONFIG_EFI),y) tests += $(TEST_DIR)/amd_sev.$(exe) diff --git a/x86/cstart64.S b/x86/cstart64.S index 4dff11027..a91d55d00 100644 --- a/x86/cstart64.S +++ b/x86/cstart64.S @@ -92,6 +92,27 @@ switch_to_5level: call enter_long_mode jmpl $8, $lvl5 + +switch_to_4level: + mov %cr0, %eax + btr $31, %eax + mov %eax, %cr0 + + mov $ptl4, %eax + mov %eax, pt_root + + /* Disable CR4.LA57 */ + mov %cr4, %eax + btr $12, %eax + mov %eax, %cr4 + + mov $0x10, %ax + mov %ax, %ss + + call enter_long_mode + jmpl $8, $lvl5 + + smp_stacktop: .long stacktop - 4096 .align 16 @@ -139,3 +160,17 @@ setup_5level_page_table: lretq lvl5: retq + + +.globl setup_4level_page_table +setup_4level_page_table: + /* Check if 4-level paging has already enabled */ + mov %cr4, %rax + test $0x1000, %eax + jz lvl4 + + pushq $32 + pushq $switch_to_4level + lretq +lvl4: + retq diff --git a/x86/msr_canonical.c b/x86/msr_canonical.c new file mode 100644 index 000000000..89e809d90 --- /dev/null +++ b/x86/msr_canonical.c @@ -0,0 +1,236 @@ +#include "libcflat.h" + +#include "apic.h" +#include "processor.h" +#include "msr.h" +#include "x86/vm.h" +#include "asm/setup.h" + +static ulong msr_list[] = { + /*MSR_GS_BASE - tests needs this msr for _safe macros */ + MSR_IA32_SYSENTER_ESP, + MSR_IA32_SYSENTER_EIP, + MSR_FS_BASE, + MSR_KERNEL_GS_BASE, + MSR_LSTAR, + MSR_CSTAR +}; + +#define TEST_VALUE0 0xffffffceb1600000 +#define TEST_VALUE1 0xff4547ceb1600000 +#define TEST_VALUE2 0xfa4547ceb1600000 + + +static void test_msrs0(void) +{ + int i; + + for (i = 0 ; i < ARRAY_SIZE(msr_list) ; i++) { + + u64 value = rdmsr(msr_list[i]); + u64 value1; + int vector; + + // write test value via kvm + vector = wrmsr_fep_safe(msr_list[i], TEST_VALUE0); + + if (vector) + report_fail("%d Fail to write msr via emulation", i); + else { + // read test value via hardware + if (rdmsr(msr_list[i]) != TEST_VALUE0) + report_fail("%d: Wrong msr value set via emulation", i); + } + + // restore the original value + wrmsr(msr_list[i], value); + + // now write test value via hardware + wrmsr(msr_list[i], TEST_VALUE0); + + // now read test value via kvm + vector = rdmsr_fep_safe(msr_list[i], &value1); + + if (vector) + report_fail("%d Fail to read msr via emulation\n", i); + else { + if( value1 != TEST_VALUE0) + report_fail("%d: Wrong value read via emulation", i); + } + + + // restore the original value + wrmsr(msr_list[i], value); + } +} + + + + +/* + * write non canonical for 4 level but canonical for 5 level paging + * value to the set of tested msrs + */ +static void test_msrs1(u64 test_value) +{ + int i, vector1, vector2; + + for (i = 0 ; i < ARRAY_SIZE(msr_list) ; i++) { + + u64 value = rdmsr(msr_list[i]); + + vector1 = wrmsr_safe(msr_list[i], test_value); + wrmsr(msr_list[i], value); + + vector2 = wrmsr_fep_safe(msr_list[i], test_value); + wrmsr(msr_list[i], value); + + printf("%d: hw exception: %d kvm exception %d\n", i, vector1, vector2); + + } +} + + +/* + * write non canonical for 4 level but canonical for 5 level paging + * value to the set of tested msrs while using 5 level paging, + * and then switch to 4 level paging + * + * + */ +static void test_msrs2(void) +{ + int i; + + setup_5level_page_table(); + + for (i = 0 ; i < ARRAY_SIZE(msr_list) ; i++) { + wrmsr(msr_list[i], TEST_VALUE1); + } + + setup_4level_page_table(); + + for (i = 0 ; i < ARRAY_SIZE(msr_list) ; i++) + if (rdmsr(msr_list[i]) != TEST_VALUE1) + report_fail("MSR %i didn't preserve value when switching back to 4 level paging", i); + +} + + +static void test_lldt_host(u64 value) +{ + u16 orignal_ldt = sldt(); + + set_gdt_entry(FIRST_SPARE_SEL, value, 0x100, 0x82, 0); + lldt(FIRST_SPARE_SEL); + lldt(orignal_ldt); +} + +static void test_ltr_host(u64 value) +{ + size_t tss_offset; + + set_gdt_entry(FIRST_SPARE_SEL, value, 0x100, 0x89, 0); + ltr(FIRST_SPARE_SEL); + + /* restore TSS*/ + tss_offset = setup_tss(NULL); + load_gdt_tss(tss_offset); +} + +static void test_lgdt_host(u64 value) +{ + struct descriptor_table_ptr dt_ptr; + u64 orig_base; + + sgdt(&dt_ptr); + orig_base = dt_ptr.base; + + dt_ptr.base = value; + lgdt(&dt_ptr); + + dt_ptr.base = orig_base; + lgdt(&dt_ptr); +} + +static void test_lidt_host(u64 value) +{ + struct descriptor_table_ptr dt_ptr; + u64 orig_base; + + sidt(&dt_ptr); + orig_base = dt_ptr.base; + + dt_ptr.base = value; + lidt(&dt_ptr); + + dt_ptr.base = orig_base; + lidt(&dt_ptr); +} + + +static void test_special_bases(u64 value) +{ + test_lgdt_host(value); + test_lidt_host(value); + + test_lldt_host(value); + test_ltr_host(value); + + printf("Special bases test done for %lx\n", value); +} + + +int main(int argc, char **argv) +{ +#ifndef CONFIG_EFI + + printf("Basic msr test\n"); + test_msrs0(); + + printf("testing msrs with 4 level paging\n"); + test_msrs1(TEST_VALUE0); + printf("\n"); + + printf("testing msrs with 4 level paging (4 level non canonical value)\n"); + test_msrs1(TEST_VALUE1); + printf("\n"); + + printf("testing msrs with 4 level paging (fully non canonical value)\n"); + test_msrs1(TEST_VALUE2); + printf("\n"); + + + setup_5level_page_table(); + + printf("testing msrs with 5 level paging\n"); + test_msrs1(TEST_VALUE0); + printf("\n"); + + + printf("testing msrs with 5 level paging (4 level non canonical value)\n"); + test_msrs1(TEST_VALUE1); + printf("\n"); + + printf("testing msrs with 5 level paging (fully non canonical value)\n"); + test_msrs1(TEST_VALUE2); + printf("\n"); + + + printf("testing that msrs remain with non canonical values after switch to 4 level paging\n"); + test_msrs2(); + + setup_5level_page_table(); + + test_special_bases(TEST_VALUE0); + test_special_bases(TEST_VALUE1); + + setup_4level_page_table(); + + test_special_bases(TEST_VALUE0); + test_special_bases(TEST_VALUE1); + +#endif + return report_summary(); +} + -- 2.40.1