From e14288f50896caaafbdd8e66d58fe757f237b13c Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Mon, 22 Jul 2024 11:09:40 -0400 Subject: [PATCH 2/2] vmx: add test for canonical checks on various fields Signed-off-by: Maxim Levitsky --- x86/vmx_tests.c | 151 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c index ffe7064c9..8f9784360 100644 --- a/x86/vmx_tests.c +++ b/x86/vmx_tests.c @@ -10732,6 +10732,7 @@ static void handle_exception_in_l1(u32 vector) vmcs_write(EXC_BITMAP, old_eb); } + static void vmx_exception_test(void) { struct vmx_exception_test *t; @@ -10754,6 +10755,155 @@ static void vmx_exception_test(void) test_set_guest_finished(); } + +#define TEST_VALUE_CANONICAL 0xffffffceb1600000 +#define TEST_VALUE_5CANONICAL 0xff4547ceb1600000 + +static void vmx_canonical_test_guest(void) +{ + while (true) { + vmcall(); + } +} + +static int get_host_value(u64 vmcs_field, u64 *value) +{ + struct descriptor_table_ptr dt_ptr; + + switch(vmcs_field) { + case HOST_SYSENTER_ESP: + *value = rdmsr(MSR_IA32_SYSENTER_ESP); + break; + case HOST_SYSENTER_EIP: + *value = rdmsr(MSR_IA32_SYSENTER_EIP); + break; + case HOST_BASE_FS: + *value = rdmsr(MSR_FS_BASE); + break; + case HOST_BASE_GS: + *value = rdmsr(MSR_GS_BASE); + break; + case HOST_BASE_GDTR: + sgdt(&dt_ptr); + *value = dt_ptr.base; + break; + case HOST_BASE_IDTR: + sidt(&dt_ptr); + *value = dt_ptr.base; + break; + case HOST_BASE_TR: + *value = get_gdt_entry_base(get_tss_descr()); + /* value might not reflect the actual base if changed by VMX */ + return 1; + default: + assert(0); + } + return 0; +} + +static void set_host_value(u64 vmcs_field, u64 value) +{ + struct descriptor_table_ptr dt_ptr; + + switch(vmcs_field) { + case HOST_SYSENTER_ESP: + wrmsr(MSR_IA32_SYSENTER_ESP, value); + break; + case HOST_SYSENTER_EIP: + wrmsr(MSR_IA32_SYSENTER_EIP, value); + break; + case HOST_BASE_FS: + wrmsr(MSR_FS_BASE, value); + break; + case HOST_BASE_GS: + wrmsr(MSR_GS_BASE, value); + break; + case HOST_BASE_GDTR: + sgdt(&dt_ptr); + dt_ptr.base = value; + lgdt(&dt_ptr); + break; + case HOST_BASE_IDTR: + sidt(&dt_ptr); + dt_ptr.base = value; + lidt(&dt_ptr); + break; + case HOST_BASE_TR: + /* set the base and clear the busy bit */ + set_gdt_entry(FIRST_SPARE_SEL, value, 0x200, 0x89, 0); + ltr(FIRST_SPARE_SEL); + break; + } +} + +static void do_vmx_canonical_test_one_field(const char* name, u64 field) +{ + /* backup the msr and field values */ + u64 host_org_value, test_value; + u64 field_org_value = vmcs_read(field); + + get_host_value(field, &host_org_value); + + /* write 57-canonical value on the host and check that it was written */ + set_host_value(field, TEST_VALUE_5CANONICAL); + if (!get_host_value(field, &test_value)) { + report(test_value == TEST_VALUE_5CANONICAL, "%s: HOST value is set to test value directly", name); + } + + /* write 57-canonical value via VMLANUCH/VMRESUME instruction*/ + set_host_value(field, TEST_VALUE_CANONICAL); + vmcs_write(field, TEST_VALUE_5CANONICAL); + + enter_guest(); + skip_exit_vmcall(); + + if (!get_host_value(field, &test_value)) { + /* check that now msr value is the same as the field value*/ + report(test_value == TEST_VALUE_5CANONICAL, "%s: HOST value is set to test value via VMLAUNCH/VMRESUME", name); + } + + /* Restore original values */ + vmcs_write(field, field_org_value); + set_host_value(field, host_org_value); +} + +#define vmx_canonical_test_one_field(field) \ + do_vmx_canonical_test_one_field(#field, field); + + + +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 vmx_canonical_test(void) +{ + report(!(read_cr4() & X86_CR4_LA57), "4 level paging"); + + test_set_guest(vmx_canonical_test_guest); + + test_lldt_host(TEST_VALUE_5CANONICAL); + + vmx_canonical_test_one_field(HOST_SYSENTER_ESP); + vmx_canonical_test_one_field(HOST_SYSENTER_EIP); + + vmx_canonical_test_one_field(HOST_BASE_FS); + vmx_canonical_test_one_field(HOST_BASE_GS); + + vmx_canonical_test_one_field(HOST_BASE_GDTR); + vmx_canonical_test_one_field(HOST_BASE_IDTR); + + vmx_canonical_test_one_field(HOST_BASE_TR); + + + test_set_guest_finished(); +} + enum Vid_op { VID_OP_SET_ISR, VID_OP_NOP, @@ -11262,5 +11412,6 @@ struct vmx_test vmx_tests[] = { TEST(vmx_pf_invvpid_test), TEST(vmx_pf_vpid_test), TEST(vmx_exception_test), + TEST(vmx_canonical_test), { NULL, NULL, NULL, NULL, NULL, {0} }, }; -- 2.40.1