From: Andrew Cooper Date: Mon, 12 Feb 2018 13:38:06 +0000 (+0000) Subject: Rename the vvmx test to nested-vmx X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=4c83300d0bcba9038438160b8b6cc4ad68d652bc;p=people%2Fandrewcoop%2Fxen-test-framework.git Rename the vvmx test to nested-vmx In preparation for introducing nested-svm as well. Signed-off-by: Andrew Cooper --- diff --git a/docs/all-tests.dox b/docs/all-tests.dox index f9ea7c5..78588c5 100644 --- a/docs/all-tests.dox +++ b/docs/all-tests.dox @@ -122,5 +122,5 @@ guest breakout. @section index-in-development In Development -@subpage test-vvmx - Nested VT-x tests. +@subpage test-nested-vmx - Nested VT-x tests. */ diff --git a/tests/nested-vmx/Makefile b/tests/nested-vmx/Makefile new file mode 100644 index 0000000..1ce0d1d --- /dev/null +++ b/tests/nested-vmx/Makefile @@ -0,0 +1,11 @@ +include $(ROOT)/build/common.mk + +NAME := nested-vmx +CATEGORY := in-development +TEST-ENVS := $(HVM_ENVIRONMENTS) + +TEST-EXTRA-CFG := extra.cfg.in + +obj-perenv += main.o msr.o util.o vmxon.o + +include $(ROOT)/build/gen.mk diff --git a/tests/nested-vmx/extra.cfg.in b/tests/nested-vmx/extra.cfg.in new file mode 100644 index 0000000..ae494f8 --- /dev/null +++ b/tests/nested-vmx/extra.cfg.in @@ -0,0 +1 @@ +nestedhvm = 1 diff --git a/tests/nested-vmx/main.c b/tests/nested-vmx/main.c new file mode 100644 index 0000000..6d46642 --- /dev/null +++ b/tests/nested-vmx/main.c @@ -0,0 +1,43 @@ +/** + * @file tests/nested-vmx/main.c + * @ref test-nested-vmx + * + * @page test-nested-vmx Nested VT-x + * + * Functional testing of the VMX features in a nested-virt environment. + * + * @see tests/nested-vmx/main.c + */ +#include "test.h" + +const char test_title[] = "Nested VT-x testing"; + +void test_main(void) +{ + if ( !cpu_has_vmx ) + return xtf_skip("Skip: VT-x not available\n"); + + if ( !vendor_is_intel ) + xtf_warning("Warning: VT-x found on non-Intel processor\n"); + + test_msr_vmx(); + + if ( xtf_status_reported() ) + return; /* No point continuing if the basic MSRs aren't working. */ + + vmx_collect_data(); + + test_vmxon(); + + xtf_success(NULL); +} + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tests/nested-vmx/msr.c b/tests/nested-vmx/msr.c new file mode 100644 index 0000000..0c82160 --- /dev/null +++ b/tests/nested-vmx/msr.c @@ -0,0 +1,70 @@ +#include "test.h" + +/* + * Guest MSR_FEATURE_CONTROL is set by Xen hypervisor instead by guest + * firmware or hvmloader, so this test checks whether bits in + * MSR_FEATURE_CONTROL are set correctly and does not require they are all + * zero. + */ +static void test_msr_feature_control(void) +{ + msr_feature_control_t feat; + + if ( rdmsr_safe(MSR_FEATURE_CONTROL, &feat.raw) ) + return xtf_failure("Fail: Fault when reading MSR_FEATURE_CONTROL\n"); + + if ( !cpu_has_smx && feat.vmxon_inside_smx ) + xtf_failure("Fail: FEATURE_CONTROL.VMXON_INSIDE_SMX is set but SMX is not supported\n"); + + if ( !feat.vmxon_outside_smx ) + xtf_failure("Fail: FEATURE_CONTROL.VMXON_OUTSIDE_SMX is not set\n"); + + /* VMXON should be unusable if LOCK isn't set. */ + if ( !feat.lock ) + xtf_failure("Fail: FEATURE_CONTROL.LOCK is not set\n"); + + /* Because LOCK is set, the MSR should be read-only. */ + if ( !wrmsr_safe(MSR_FEATURE_CONTROL, feat.raw) ) + xtf_failure("Fail: Successfully wrote to MSR_FEATURE_CONTROL\n"); +} + +static void test_msr_vmx_basic(void) +{ + msr_vmx_basic_t basic; + + if ( rdmsr_safe(MSR_VMX_BASIC, &basic.raw) ) + return xtf_failure("Fail: Fault when reading MSR_VMX_BASIC\n"); + + if ( basic.mbz ) + xtf_failure("Fail: MSR_VMX_BASIC[31] is not 0\n"); + + if ( basic.vmcs_size == 0 ) + xtf_failure("Fail: VMCS size reported as 0\n"); + else if ( basic.vmcs_size > 4096 ) + xtf_failure("Fail: VMCS size (%u) exceeds 4096 limit\n", + basic.vmcs_size); + + if ( cpu_has_lm && basic.paddr_32bit ) + xtf_failure("Fail: Physical address width limited to 32 bits\n"); + + if ( !wrmsr_safe(MSR_VMX_BASIC, basic.raw) ) + xtf_failure("Fail: Successfully wrote to MSR_VMX_BASIC\n"); +} + +void test_msr_vmx(void) +{ + printk("Test: MSRs\n"); + + test_msr_feature_control(); + test_msr_vmx_basic(); +} + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tests/nested-vmx/test.h b/tests/nested-vmx/test.h new file mode 100644 index 0000000..5d9664f --- /dev/null +++ b/tests/nested-vmx/test.h @@ -0,0 +1,50 @@ +#ifndef VVMX_TEST_H +#define VVMX_TEST_H + +#include + +#include + +/* + * Extentions on top of regular EXINFO. + * + * Use EXINFO_AVAIL{0,1} for VMFail{Invalid,Valid}. (ab)use the fact that + * VMFailValid means no exception occured to stash the VMX Instruction Error + * code in the low bits, normally used for vector/error_code information. + */ +#define VMERR_SUCCESS 0 +#define VMERR_INVALID (EXINFO_EXPECTED | EXINFO_AVAIL0) +#define VMERR_VALID(x) (EXINFO_EXPECTED | EXINFO_AVAIL1 | ((x) & 0xffff)) + +/** + * Compare an expectation against what really happenend, printing + * human-readable information in case of a mismatch. + */ +void check(const char *func, exinfo_t got, exinfo_t exp); + +extern uint32_t vmcs_revid; /**< Hardware VMCS Revision ID. */ + +/** + * Collect real information about the VT-x environment, for use by test. + */ +void vmx_collect_data(void); + +/* Clear a VMCS, and set a specific revision id. */ +static inline void clear_vmcs(void *_vmcs, uint32_t rev) +{ + uint32_t *vmcs = _vmcs; + + memset(vmcs, 0, PAGE_SIZE); + vmcs[0] = rev; +} + +/* VMX instruction stubs, wrapped to return exinfo_t information. */ +exinfo_t stub_vmxon(uint64_t paddr); +exinfo_t stub_vmptrld(uint64_t paddr); +exinfo_t stub_vmxon_user(uint64_t paddr); + +/* Test routines. */ +void test_msr_vmx(void); +void test_vmxon(void); + +#endif /* VVMX_TEST_H */ diff --git a/tests/nested-vmx/util.c b/tests/nested-vmx/util.c new file mode 100644 index 0000000..5cf16c3 --- /dev/null +++ b/tests/nested-vmx/util.c @@ -0,0 +1,159 @@ +#include "test.h" + +#include + +static void decode_test_exinfo(char *str, size_t n, exinfo_t ex) +{ + if ( ex == VMERR_SUCCESS ) + { + strcpy(str, "VMsucceed"); + return; + } + + if ( ex == VMERR_INVALID ) + { + strcpy(str, "VMfailInvalid"); + return; + } + + unsigned int high = ex & ~0xffffff; + + if ( high == VMERR_VALID(0) ) + { + unsigned int low = ex & 0xffffff; + + snprintf(str, n, "VMfailValid(%u) %s", + low, vmx_insn_err_strerror(low)); + return; + } + + if ( high == EXINFO_EXPECTED ) + { + snprintf(str, n, "%pe", _p(ex)); + return; + } + + strcpy(str, ""); +} + +void check(const char *func, exinfo_t got, exinfo_t exp) +{ + char gotstr[48], expstr[48]; + + if ( got == exp ) + return; + + decode_test_exinfo(gotstr, ARRAY_SIZE(gotstr), got); + decode_test_exinfo(expstr, ARRAY_SIZE(expstr), exp); + + xtf_failure("Failure in %s()\n" + " Expected 0x%08x: %s\n" + " Got 0x%08x: %s\n", + func, exp, expstr, got, gotstr); +} + +uint32_t vmcs_revid; + +void vmx_collect_data(void) +{ + msr_vmx_basic_t basic = { rdmsr(MSR_VMX_BASIC) }; + + vmcs_revid = basic.vmcs_rev_id; +} + +/* + * Read the VM Instruction Error code from the VMCS. It is the callers + * responsibility to ensure that the VMCS is valid in context. + */ +static exinfo_t get_vmx_insn_err(void) +{ + unsigned long err; + + asm ("vmread %[field], %[value]" + : [value] "=rm" (err) + : [field] "r" (VMCS_VM_INSN_ERR + 0ul)); + + return VMERR_VALID(err); +} + +exinfo_t stub_vmxon(uint64_t paddr) +{ + exinfo_t ex = 0; + bool fail_valid = false, fail_invalid = false; + + asm volatile ("1: vmxon %[paddr];" + ASM_FLAG_OUT(, "setc %[fail_invalid];") + ASM_FLAG_OUT(, "setz %[fail_valid];") + "2:" + _ASM_EXTABLE_HANDLER(1b, 2b, ex_record_fault_edi) + : "+D" (ex), + ASM_FLAG_OUT("=@ccc", [fail_invalid] "+rm") (fail_invalid), + ASM_FLAG_OUT("=@ccz", [fail_valid] "+rm") (fail_valid) + : [paddr] "m" (paddr), + "X" (ex_record_fault_edi)); + + if ( fail_invalid ) + return VMERR_INVALID; + else if ( fail_valid ) + return get_vmx_insn_err(); + else + return ex; +} + +exinfo_t stub_vmptrld(uint64_t paddr) +{ + exinfo_t ex = 0; + bool fail_valid = false, fail_invalid = false; + + asm volatile ("1: vmptrld %[paddr];" + ASM_FLAG_OUT(, "setc %[fail_invalid];") + ASM_FLAG_OUT(, "setz %[fail_valid];") + "2:" + _ASM_EXTABLE_HANDLER(1b, 2b, ex_record_fault_edi) + : "+D" (ex), + ASM_FLAG_OUT("=@ccc", [fail_invalid] "+rm") (fail_invalid), + ASM_FLAG_OUT("=@ccz", [fail_valid] "+rm") (fail_valid) + : [paddr] "m" (paddr), + "X" (ex_record_fault_edi)); + + if ( fail_invalid ) + return VMERR_INVALID; + else if ( fail_valid ) + return get_vmx_insn_err(); + else + return ex; +} + +exinfo_t __user_text stub_vmxon_user(uint64_t paddr) +{ + exinfo_t ex = 0; + bool fail_valid = false, fail_invalid = false; + + asm volatile ("1: vmxon %[paddr];" + ASM_FLAG_OUT(, "setc %[fail_invalid];") + ASM_FLAG_OUT(, "setz %[fail_valid];") + "2:" + _ASM_EXTABLE_HANDLER(1b, 2b, ex_record_fault_edi) + : "+D" (ex), + ASM_FLAG_OUT("=@ccc", [fail_invalid] "+rm") (fail_invalid), + ASM_FLAG_OUT("=@ccz", [fail_valid] "+rm") (fail_valid) + : [paddr] "m" (paddr), + "X" (ex_record_fault_edi)); + + if ( fail_invalid ) + return VMERR_INVALID; + else if ( fail_valid ) + return get_vmx_insn_err(); + else + return ex; +} + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tests/nested-vmx/vmxon.c b/tests/nested-vmx/vmxon.c new file mode 100644 index 0000000..74b1182 --- /dev/null +++ b/tests/nested-vmx/vmxon.c @@ -0,0 +1,216 @@ +#include "test.h" + +/* vmxon region which shouldn't be latched in the hardware vmxon pointer. */ +static uint8_t vmxon_region_unused[PAGE_SIZE] __page_aligned_bss; + +/* vmxon region which gets latched in hardware. */ +static uint8_t vmxon_region_real[PAGE_SIZE] __page_aligned_bss; + +/* Loaded VMCS, to recover VM Instruction Errors. */ +static uint8_t vmcs[PAGE_SIZE] __page_aligned_bss; + +/** + * vmxon with CR4.VMXE cleared + * + * Expect: @#UD + */ +static void test_vmxon_novmxe(void) +{ + check(__func__, stub_vmxon(0), EXINFO_SYM(UD, 0)); +} + +/* + * Wrapper around stub_vmxon_user(), This stub should always fault for control + * or permission reasons, but pointing at a supervisor frame is useful to + * check that Xen doesn't dereference the instructions parameter. + */ +static unsigned long __user_text vmxon_in_user(void) +{ + return stub_vmxon_user(_u(vmxon_region_unused)); +} + +/** + * vmxon in CPL=3 outside of VMX operation + * + * Expect: @#GP(0) + */ +static void test_vmxon_novmxe_in_user(void) +{ + exinfo_t ex = exec_user(vmxon_in_user); + + check(__func__, ex, EXINFO_SYM(UD, 0)); +} + +/** + * vmxon in CPL=3 in VMX operation + * + * Expect: @#UD + */ +static void test_vmxon_in_user(void) +{ + exinfo_t ex = exec_user(vmxon_in_user); + + check(__func__, ex, EXINFO_SYM(GP, 0)); +} + +/** + * vmxon with a physical address that exceeds the maximum address width + * + * Expect: VMfailInvalid + */ +static void test_vmxon_overly_wide_paddr(void) +{ + exinfo_t ex = stub_vmxon(1ULL << maxphysaddr); + + check(__func__, ex, VMERR_INVALID); +} + +/** + * vmxon with an unaligned physical address + * + * Expect: VMfailInvalid + */ +static void test_vmxon_unaligned_paddr(void) +{ + exinfo_t ex = stub_vmxon(_u(vmxon_region_unused) | 0xff); + + check(__func__, ex, VMERR_INVALID); +} + +/** + * vmxon with VMCS revision ID mismatched with MSR_IA32_VMX_BASIC + * + * Expect: VMfailInvalid + */ +static void test_vmxon_mismatched_revid(void) +{ + clear_vmcs(vmxon_region_unused, vmcs_revid ^ 2); + exinfo_t ex = stub_vmxon(_u(vmxon_region_unused)); + + check(__func__, ex, VMERR_INVALID); +} + +/** + * vmxon with VMCS revision ID[31] set + * + * Expect: VMfailInvalid + */ +static void test_vmxon_revid_bit31(void) +{ + clear_vmcs(vmxon_region_unused, vmcs_revid | (1UL << 31)); + exinfo_t ex = stub_vmxon(_u(vmxon_region_unused)); + + check(__func__, ex, VMERR_INVALID); +} + +/** + * vmxon expected to succeed + * + * Expect: Success + */ +static void test_vmxon_correct(void) +{ + clear_vmcs(vmxon_region_real, vmcs_revid); + exinfo_t ex = stub_vmxon(_u(vmxon_region_real)); + + check(__func__, ex, VMERR_SUCCESS); +} + +/** + * vmxon in VMX root w/ CPL = 0 and w/o current VMCS + * + * Expect: VMfailInvalid + */ +static void test_vmxon_novmcs_in_root_cpl0(void) +{ + clear_vmcs(vmxon_region_unused, vmcs_revid); + exinfo_t ex = stub_vmxon(_u(vmxon_region_unused)); + + check(__func__, ex, VMERR_INVALID); +} + +/** + * vmxon in VMX root w/ CPL = 3 and w/o current VMCS + * + * Expect: @#GP(0) + */ +static void test_vmxon_novmcs_in_root_user(void) +{ + clear_vmcs(vmxon_region_unused, vmcs_revid); + exinfo_t ex = exec_user(vmxon_in_user); + + check(__func__, ex, EXINFO_SYM(GP, 0)); +} + +/** + * vmxon in VMX root w/ CPL = 0 and w/ current VMCS + * + * Expect: VMfailvalid() + */ +static void test_vmxon_in_root_cpl0(void) +{ + clear_vmcs(vmxon_region_unused, vmcs_revid); + exinfo_t ex = stub_vmxon(_u(vmxon_region_unused)); + + check(__func__, ex, VMERR_VALID(VMERR_VMXON_IN_ROOT)); +} + +/** + * vmxon in VMX root w/ CPL = 3 and w/ current VMCS + * + * Expect: @#GP(0) + */ +static void test_vmxon_in_root_user(void) +{ + clear_vmcs(vmxon_region_unused, vmcs_revid); + exinfo_t ex = exec_user(vmxon_in_user); + + check(__func__, ex, EXINFO_SYM(GP, 0)); +} + +void test_vmxon(void) +{ + unsigned long cr4 = read_cr4(); + exinfo_t ex; + + if ( cr4 & X86_CR4_VMXE ) + write_cr4(cr4 &= ~X86_CR4_VMXE); + + printk("Test: vmxon\n"); + + test_vmxon_novmxe(); + test_vmxon_novmxe_in_user(); + + write_cr4(cr4 |= X86_CR4_VMXE); + + test_vmxon_in_user(); + test_vmxon_overly_wide_paddr(); + test_vmxon_unaligned_paddr(); + test_vmxon_mismatched_revid(); + test_vmxon_revid_bit31(); + test_vmxon_correct(); + + /* Test should now be operating in VMX Root mode. */ + + test_vmxon_novmcs_in_root_cpl0(); + test_vmxon_novmcs_in_root_user(); + + /* Load a real VMCS to recover VM Instruction Errors. */ + clear_vmcs(vmcs, vmcs_revid); + ex = stub_vmptrld(_u(vmcs)); + if ( ex ) + return xtf_failure("Fail: unexpected vmptrld failure %08x\n", ex); + + test_vmxon_in_root_cpl0(); + test_vmxon_in_root_user(); +} + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tests/vvmx/Makefile b/tests/vvmx/Makefile deleted file mode 100644 index 2fc67c1..0000000 --- a/tests/vvmx/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -include $(ROOT)/build/common.mk - -NAME := vvmx -CATEGORY := in-development -TEST-ENVS := $(HVM_ENVIRONMENTS) - -TEST-EXTRA-CFG := extra.cfg.in - -obj-perenv += main.o msr.o util.o vmxon.o - -include $(ROOT)/build/gen.mk diff --git a/tests/vvmx/extra.cfg.in b/tests/vvmx/extra.cfg.in deleted file mode 100644 index ae494f8..0000000 --- a/tests/vvmx/extra.cfg.in +++ /dev/null @@ -1 +0,0 @@ -nestedhvm = 1 diff --git a/tests/vvmx/main.c b/tests/vvmx/main.c deleted file mode 100644 index 9e21337..0000000 --- a/tests/vvmx/main.c +++ /dev/null @@ -1,43 +0,0 @@ -/** - * @file tests/vvmx/main.c - * @ref test-vvmx - * - * @page test-vvmx vvmx - * - * Functional testing of the VMX features in a nested-virt environment. - * - * @see tests/vvmx/main.c - */ -#include "test.h" - -const char test_title[] = "Nested VT-x testing"; - -void test_main(void) -{ - if ( !cpu_has_vmx ) - return xtf_skip("Skip: VT-x not available\n"); - - if ( !vendor_is_intel ) - xtf_warning("Warning: VT-x found on non-Intel processor\n"); - - test_msr_vmx(); - - if ( xtf_status_reported() ) - return; /* No point continuing if the basic MSRs aren't working. */ - - vmx_collect_data(); - - test_vmxon(); - - xtf_success(NULL); -} - -/* - * Local variables: - * mode: C - * c-file-style: "BSD" - * c-basic-offset: 4 - * tab-width: 4 - * indent-tabs-mode: nil - * End: - */ diff --git a/tests/vvmx/msr.c b/tests/vvmx/msr.c deleted file mode 100644 index 0c82160..0000000 --- a/tests/vvmx/msr.c +++ /dev/null @@ -1,70 +0,0 @@ -#include "test.h" - -/* - * Guest MSR_FEATURE_CONTROL is set by Xen hypervisor instead by guest - * firmware or hvmloader, so this test checks whether bits in - * MSR_FEATURE_CONTROL are set correctly and does not require they are all - * zero. - */ -static void test_msr_feature_control(void) -{ - msr_feature_control_t feat; - - if ( rdmsr_safe(MSR_FEATURE_CONTROL, &feat.raw) ) - return xtf_failure("Fail: Fault when reading MSR_FEATURE_CONTROL\n"); - - if ( !cpu_has_smx && feat.vmxon_inside_smx ) - xtf_failure("Fail: FEATURE_CONTROL.VMXON_INSIDE_SMX is set but SMX is not supported\n"); - - if ( !feat.vmxon_outside_smx ) - xtf_failure("Fail: FEATURE_CONTROL.VMXON_OUTSIDE_SMX is not set\n"); - - /* VMXON should be unusable if LOCK isn't set. */ - if ( !feat.lock ) - xtf_failure("Fail: FEATURE_CONTROL.LOCK is not set\n"); - - /* Because LOCK is set, the MSR should be read-only. */ - if ( !wrmsr_safe(MSR_FEATURE_CONTROL, feat.raw) ) - xtf_failure("Fail: Successfully wrote to MSR_FEATURE_CONTROL\n"); -} - -static void test_msr_vmx_basic(void) -{ - msr_vmx_basic_t basic; - - if ( rdmsr_safe(MSR_VMX_BASIC, &basic.raw) ) - return xtf_failure("Fail: Fault when reading MSR_VMX_BASIC\n"); - - if ( basic.mbz ) - xtf_failure("Fail: MSR_VMX_BASIC[31] is not 0\n"); - - if ( basic.vmcs_size == 0 ) - xtf_failure("Fail: VMCS size reported as 0\n"); - else if ( basic.vmcs_size > 4096 ) - xtf_failure("Fail: VMCS size (%u) exceeds 4096 limit\n", - basic.vmcs_size); - - if ( cpu_has_lm && basic.paddr_32bit ) - xtf_failure("Fail: Physical address width limited to 32 bits\n"); - - if ( !wrmsr_safe(MSR_VMX_BASIC, basic.raw) ) - xtf_failure("Fail: Successfully wrote to MSR_VMX_BASIC\n"); -} - -void test_msr_vmx(void) -{ - printk("Test: MSRs\n"); - - test_msr_feature_control(); - test_msr_vmx_basic(); -} - -/* - * Local variables: - * mode: C - * c-file-style: "BSD" - * c-basic-offset: 4 - * tab-width: 4 - * indent-tabs-mode: nil - * End: - */ diff --git a/tests/vvmx/test.h b/tests/vvmx/test.h deleted file mode 100644 index 5d9664f..0000000 --- a/tests/vvmx/test.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef VVMX_TEST_H -#define VVMX_TEST_H - -#include - -#include - -/* - * Extentions on top of regular EXINFO. - * - * Use EXINFO_AVAIL{0,1} for VMFail{Invalid,Valid}. (ab)use the fact that - * VMFailValid means no exception occured to stash the VMX Instruction Error - * code in the low bits, normally used for vector/error_code information. - */ -#define VMERR_SUCCESS 0 -#define VMERR_INVALID (EXINFO_EXPECTED | EXINFO_AVAIL0) -#define VMERR_VALID(x) (EXINFO_EXPECTED | EXINFO_AVAIL1 | ((x) & 0xffff)) - -/** - * Compare an expectation against what really happenend, printing - * human-readable information in case of a mismatch. - */ -void check(const char *func, exinfo_t got, exinfo_t exp); - -extern uint32_t vmcs_revid; /**< Hardware VMCS Revision ID. */ - -/** - * Collect real information about the VT-x environment, for use by test. - */ -void vmx_collect_data(void); - -/* Clear a VMCS, and set a specific revision id. */ -static inline void clear_vmcs(void *_vmcs, uint32_t rev) -{ - uint32_t *vmcs = _vmcs; - - memset(vmcs, 0, PAGE_SIZE); - vmcs[0] = rev; -} - -/* VMX instruction stubs, wrapped to return exinfo_t information. */ -exinfo_t stub_vmxon(uint64_t paddr); -exinfo_t stub_vmptrld(uint64_t paddr); -exinfo_t stub_vmxon_user(uint64_t paddr); - -/* Test routines. */ -void test_msr_vmx(void); -void test_vmxon(void); - -#endif /* VVMX_TEST_H */ diff --git a/tests/vvmx/util.c b/tests/vvmx/util.c deleted file mode 100644 index 5cf16c3..0000000 --- a/tests/vvmx/util.c +++ /dev/null @@ -1,159 +0,0 @@ -#include "test.h" - -#include - -static void decode_test_exinfo(char *str, size_t n, exinfo_t ex) -{ - if ( ex == VMERR_SUCCESS ) - { - strcpy(str, "VMsucceed"); - return; - } - - if ( ex == VMERR_INVALID ) - { - strcpy(str, "VMfailInvalid"); - return; - } - - unsigned int high = ex & ~0xffffff; - - if ( high == VMERR_VALID(0) ) - { - unsigned int low = ex & 0xffffff; - - snprintf(str, n, "VMfailValid(%u) %s", - low, vmx_insn_err_strerror(low)); - return; - } - - if ( high == EXINFO_EXPECTED ) - { - snprintf(str, n, "%pe", _p(ex)); - return; - } - - strcpy(str, ""); -} - -void check(const char *func, exinfo_t got, exinfo_t exp) -{ - char gotstr[48], expstr[48]; - - if ( got == exp ) - return; - - decode_test_exinfo(gotstr, ARRAY_SIZE(gotstr), got); - decode_test_exinfo(expstr, ARRAY_SIZE(expstr), exp); - - xtf_failure("Failure in %s()\n" - " Expected 0x%08x: %s\n" - " Got 0x%08x: %s\n", - func, exp, expstr, got, gotstr); -} - -uint32_t vmcs_revid; - -void vmx_collect_data(void) -{ - msr_vmx_basic_t basic = { rdmsr(MSR_VMX_BASIC) }; - - vmcs_revid = basic.vmcs_rev_id; -} - -/* - * Read the VM Instruction Error code from the VMCS. It is the callers - * responsibility to ensure that the VMCS is valid in context. - */ -static exinfo_t get_vmx_insn_err(void) -{ - unsigned long err; - - asm ("vmread %[field], %[value]" - : [value] "=rm" (err) - : [field] "r" (VMCS_VM_INSN_ERR + 0ul)); - - return VMERR_VALID(err); -} - -exinfo_t stub_vmxon(uint64_t paddr) -{ - exinfo_t ex = 0; - bool fail_valid = false, fail_invalid = false; - - asm volatile ("1: vmxon %[paddr];" - ASM_FLAG_OUT(, "setc %[fail_invalid];") - ASM_FLAG_OUT(, "setz %[fail_valid];") - "2:" - _ASM_EXTABLE_HANDLER(1b, 2b, ex_record_fault_edi) - : "+D" (ex), - ASM_FLAG_OUT("=@ccc", [fail_invalid] "+rm") (fail_invalid), - ASM_FLAG_OUT("=@ccz", [fail_valid] "+rm") (fail_valid) - : [paddr] "m" (paddr), - "X" (ex_record_fault_edi)); - - if ( fail_invalid ) - return VMERR_INVALID; - else if ( fail_valid ) - return get_vmx_insn_err(); - else - return ex; -} - -exinfo_t stub_vmptrld(uint64_t paddr) -{ - exinfo_t ex = 0; - bool fail_valid = false, fail_invalid = false; - - asm volatile ("1: vmptrld %[paddr];" - ASM_FLAG_OUT(, "setc %[fail_invalid];") - ASM_FLAG_OUT(, "setz %[fail_valid];") - "2:" - _ASM_EXTABLE_HANDLER(1b, 2b, ex_record_fault_edi) - : "+D" (ex), - ASM_FLAG_OUT("=@ccc", [fail_invalid] "+rm") (fail_invalid), - ASM_FLAG_OUT("=@ccz", [fail_valid] "+rm") (fail_valid) - : [paddr] "m" (paddr), - "X" (ex_record_fault_edi)); - - if ( fail_invalid ) - return VMERR_INVALID; - else if ( fail_valid ) - return get_vmx_insn_err(); - else - return ex; -} - -exinfo_t __user_text stub_vmxon_user(uint64_t paddr) -{ - exinfo_t ex = 0; - bool fail_valid = false, fail_invalid = false; - - asm volatile ("1: vmxon %[paddr];" - ASM_FLAG_OUT(, "setc %[fail_invalid];") - ASM_FLAG_OUT(, "setz %[fail_valid];") - "2:" - _ASM_EXTABLE_HANDLER(1b, 2b, ex_record_fault_edi) - : "+D" (ex), - ASM_FLAG_OUT("=@ccc", [fail_invalid] "+rm") (fail_invalid), - ASM_FLAG_OUT("=@ccz", [fail_valid] "+rm") (fail_valid) - : [paddr] "m" (paddr), - "X" (ex_record_fault_edi)); - - if ( fail_invalid ) - return VMERR_INVALID; - else if ( fail_valid ) - return get_vmx_insn_err(); - else - return ex; -} - -/* - * Local variables: - * mode: C - * c-file-style: "BSD" - * c-basic-offset: 4 - * tab-width: 4 - * indent-tabs-mode: nil - * End: - */ diff --git a/tests/vvmx/vmxon.c b/tests/vvmx/vmxon.c deleted file mode 100644 index 74b1182..0000000 --- a/tests/vvmx/vmxon.c +++ /dev/null @@ -1,216 +0,0 @@ -#include "test.h" - -/* vmxon region which shouldn't be latched in the hardware vmxon pointer. */ -static uint8_t vmxon_region_unused[PAGE_SIZE] __page_aligned_bss; - -/* vmxon region which gets latched in hardware. */ -static uint8_t vmxon_region_real[PAGE_SIZE] __page_aligned_bss; - -/* Loaded VMCS, to recover VM Instruction Errors. */ -static uint8_t vmcs[PAGE_SIZE] __page_aligned_bss; - -/** - * vmxon with CR4.VMXE cleared - * - * Expect: @#UD - */ -static void test_vmxon_novmxe(void) -{ - check(__func__, stub_vmxon(0), EXINFO_SYM(UD, 0)); -} - -/* - * Wrapper around stub_vmxon_user(), This stub should always fault for control - * or permission reasons, but pointing at a supervisor frame is useful to - * check that Xen doesn't dereference the instructions parameter. - */ -static unsigned long __user_text vmxon_in_user(void) -{ - return stub_vmxon_user(_u(vmxon_region_unused)); -} - -/** - * vmxon in CPL=3 outside of VMX operation - * - * Expect: @#GP(0) - */ -static void test_vmxon_novmxe_in_user(void) -{ - exinfo_t ex = exec_user(vmxon_in_user); - - check(__func__, ex, EXINFO_SYM(UD, 0)); -} - -/** - * vmxon in CPL=3 in VMX operation - * - * Expect: @#UD - */ -static void test_vmxon_in_user(void) -{ - exinfo_t ex = exec_user(vmxon_in_user); - - check(__func__, ex, EXINFO_SYM(GP, 0)); -} - -/** - * vmxon with a physical address that exceeds the maximum address width - * - * Expect: VMfailInvalid - */ -static void test_vmxon_overly_wide_paddr(void) -{ - exinfo_t ex = stub_vmxon(1ULL << maxphysaddr); - - check(__func__, ex, VMERR_INVALID); -} - -/** - * vmxon with an unaligned physical address - * - * Expect: VMfailInvalid - */ -static void test_vmxon_unaligned_paddr(void) -{ - exinfo_t ex = stub_vmxon(_u(vmxon_region_unused) | 0xff); - - check(__func__, ex, VMERR_INVALID); -} - -/** - * vmxon with VMCS revision ID mismatched with MSR_IA32_VMX_BASIC - * - * Expect: VMfailInvalid - */ -static void test_vmxon_mismatched_revid(void) -{ - clear_vmcs(vmxon_region_unused, vmcs_revid ^ 2); - exinfo_t ex = stub_vmxon(_u(vmxon_region_unused)); - - check(__func__, ex, VMERR_INVALID); -} - -/** - * vmxon with VMCS revision ID[31] set - * - * Expect: VMfailInvalid - */ -static void test_vmxon_revid_bit31(void) -{ - clear_vmcs(vmxon_region_unused, vmcs_revid | (1UL << 31)); - exinfo_t ex = stub_vmxon(_u(vmxon_region_unused)); - - check(__func__, ex, VMERR_INVALID); -} - -/** - * vmxon expected to succeed - * - * Expect: Success - */ -static void test_vmxon_correct(void) -{ - clear_vmcs(vmxon_region_real, vmcs_revid); - exinfo_t ex = stub_vmxon(_u(vmxon_region_real)); - - check(__func__, ex, VMERR_SUCCESS); -} - -/** - * vmxon in VMX root w/ CPL = 0 and w/o current VMCS - * - * Expect: VMfailInvalid - */ -static void test_vmxon_novmcs_in_root_cpl0(void) -{ - clear_vmcs(vmxon_region_unused, vmcs_revid); - exinfo_t ex = stub_vmxon(_u(vmxon_region_unused)); - - check(__func__, ex, VMERR_INVALID); -} - -/** - * vmxon in VMX root w/ CPL = 3 and w/o current VMCS - * - * Expect: @#GP(0) - */ -static void test_vmxon_novmcs_in_root_user(void) -{ - clear_vmcs(vmxon_region_unused, vmcs_revid); - exinfo_t ex = exec_user(vmxon_in_user); - - check(__func__, ex, EXINFO_SYM(GP, 0)); -} - -/** - * vmxon in VMX root w/ CPL = 0 and w/ current VMCS - * - * Expect: VMfailvalid() - */ -static void test_vmxon_in_root_cpl0(void) -{ - clear_vmcs(vmxon_region_unused, vmcs_revid); - exinfo_t ex = stub_vmxon(_u(vmxon_region_unused)); - - check(__func__, ex, VMERR_VALID(VMERR_VMXON_IN_ROOT)); -} - -/** - * vmxon in VMX root w/ CPL = 3 and w/ current VMCS - * - * Expect: @#GP(0) - */ -static void test_vmxon_in_root_user(void) -{ - clear_vmcs(vmxon_region_unused, vmcs_revid); - exinfo_t ex = exec_user(vmxon_in_user); - - check(__func__, ex, EXINFO_SYM(GP, 0)); -} - -void test_vmxon(void) -{ - unsigned long cr4 = read_cr4(); - exinfo_t ex; - - if ( cr4 & X86_CR4_VMXE ) - write_cr4(cr4 &= ~X86_CR4_VMXE); - - printk("Test: vmxon\n"); - - test_vmxon_novmxe(); - test_vmxon_novmxe_in_user(); - - write_cr4(cr4 |= X86_CR4_VMXE); - - test_vmxon_in_user(); - test_vmxon_overly_wide_paddr(); - test_vmxon_unaligned_paddr(); - test_vmxon_mismatched_revid(); - test_vmxon_revid_bit31(); - test_vmxon_correct(); - - /* Test should now be operating in VMX Root mode. */ - - test_vmxon_novmcs_in_root_cpl0(); - test_vmxon_novmcs_in_root_user(); - - /* Load a real VMCS to recover VM Instruction Errors. */ - clear_vmcs(vmcs, vmcs_revid); - ex = stub_vmptrld(_u(vmcs)); - if ( ex ) - return xtf_failure("Fail: unexpected vmptrld failure %08x\n", ex); - - test_vmxon_in_root_cpl0(); - test_vmxon_in_root_user(); -} - -/* - * Local variables: - * mode: C - * c-file-style: "BSD" - * c-basic-offset: 4 - * tab-width: 4 - * indent-tabs-mode: nil - * End: - */