This test tests canonical VM entry checks of various host state fields (mostly segment bases) in vmcs12. Signed-off-by: Maxim Levitsky <mlevitsk@xxxxxxxxxx> --- x86/vmx_tests.c | 183 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c index ffe7064c9..b6f3d634d 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,187 @@ static void vmx_exception_test(void) test_set_guest_finished(); } + +#define TEST_VALUE1 0xff45454545000000 +#define TEST_VALUE2 0xff55555555000000 + +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 int set_host_value(u64 vmcs_field, u64 value) +{ + struct descriptor_table_ptr dt_ptr; + + switch (vmcs_field) { + case HOST_SYSENTER_ESP: + return wrmsr_safe(MSR_IA32_SYSENTER_ESP, value); + case HOST_SYSENTER_EIP: + return wrmsr_safe(MSR_IA32_SYSENTER_EIP, value); + case HOST_BASE_FS: + return wrmsr_safe(MSR_FS_BASE, value); + case HOST_BASE_GS: + /* TODO: _safe variants assume per-cpu gs base*/ + wrmsr(MSR_GS_BASE, value); + return 0; + case HOST_BASE_GDTR: + sgdt(&dt_ptr); + dt_ptr.base = value; + lgdt(&dt_ptr); + return lgdt_fep_safe(&dt_ptr); + case HOST_BASE_IDTR: + sidt(&dt_ptr); + dt_ptr.base = value; + return lidt_fep_safe(&dt_ptr); + case HOST_BASE_TR: + /* Set the base and clear the busy bit */ + set_gdt_entry(FIRST_SPARE_SEL, value, 0x200, 0x89, 0); + return ltr_safe(FIRST_SPARE_SEL); + default: + assert(0); + } +} + +static void test_host_value_natively(const char *field_name, u64 vmcs_field, u64 value) +{ + int vector; + u64 actual_value; + + /* + * Set the register via host native interface (e.g lgdt) and check + * that we got no exception + */ + vector = set_host_value(vmcs_field, value); + if (vector) { + report(false, + "Exception %d when setting %s to 0x%lx via host", + vector, field_name, value); + return; + } + + /* + * Now check that the host value matches what we expect for fields + * that can be read back (these that we can't we assume that are correct) + */ + + if (get_host_value(vmcs_field, &actual_value)) + actual_value = value; + + report(actual_value == value, + "%s: HOST value is set to test value 0x%lx directly", + field_name, value); +} + +static void test_host_value_via_guest(const char *field_name, u64 vmcs_field, u64 value) +{ + u64 actual_value; + + /* Set host state field in the vmcs and do the VM entry + * Success of VM entry already shows that L0 accepted the value + */ + vmcs_write(vmcs_field, TEST_VALUE2); + enter_guest(); + skip_exit_vmcall(); + + /* + * Now check that the host value matches what we expect for fields + * that can be read back (these that we can't we assume that are correct) + */ + + if (get_host_value(vmcs_field, &actual_value)) + actual_value = value; + + /* Check that now the msr value is the same as the field value */ + report(actual_value == TEST_VALUE2, + "%s: HOST value is set to test value 0x%lx via VMLAUNCH/VMRESUME", + field_name, value); +} + +static void do_vmx_canonical_test_one_field(const char *field_name, u64 field) +{ + u64 host_org_value, field_org_value; + + /* Backup the current host value and vmcs field value values */ + get_host_value(field, &host_org_value); + field_org_value = vmcs_read(field); + + /* + * Write TEST_VALUE1 57-canonical value on the host + * and check that it was written correctly + */ + test_host_value_natively(field_name, field, TEST_VALUE1); + + /* + * Write TEST_VALUE2 57-canonical value on the host + * via VM entry/exit and check that it was written correctly + */ + test_host_value_via_guest(field_name, field, TEST_VALUE2); + + /* 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 vmx_canonical_test(void) +{ + report(!(read_cr4() & X86_CR4_LA57), "4 level paging"); + + if (!this_cpu_has(X86_FEATURE_LA57)) + test_skip("5 level paging not supported"); + + test_set_guest(vmx_canonical_test_guest); + + 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 +11444,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.26.3