In preparation for introducing nested-svm as well.
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
@section index-in-development In Development
-@subpage test-vvmx - Nested VT-x tests.
+@subpage test-nested-vmx - Nested VT-x tests.
*/
--- /dev/null
+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
--- /dev/null
+nestedhvm = 1
--- /dev/null
+/**
+ * @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:
+ */
--- /dev/null
+#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:
+ */
--- /dev/null
+#ifndef VVMX_TEST_H
+#define VVMX_TEST_H
+
+#include <xtf.h>
+
+#include <arch/vmx.h>
+
+/*
+ * 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 */
--- /dev/null
+#include "test.h"
+
+#include <arch/decode.h>
+
+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, "<bad>");
+}
+
+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:
+ */
--- /dev/null
+#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:
+ */
+++ /dev/null
-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
+++ /dev/null
-nestedhvm = 1
+++ /dev/null
-/**
- * @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:
- */
+++ /dev/null
-#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:
- */
+++ /dev/null
-#ifndef VVMX_TEST_H
-#define VVMX_TEST_H
-
-#include <xtf.h>
-
-#include <arch/vmx.h>
-
-/*
- * 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 */
+++ /dev/null
-#include "test.h"
-
-#include <arch/decode.h>
-
-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, "<bad>");
-}
-
-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:
- */
+++ /dev/null
-#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:
- */