Add vmentry failed handler to vmx framework to catch direct fail of vmentry. When vmlaunch/vmresume directly fail to the next instruction, a entry failed handler is used to handle this failure. Resume failure from entry failed handler will cause entry double fail and directly exit to L1. Signed-off-by: Arthur Chunqi Li <yzt356@xxxxxxxxx> --- lib/x86/vm.h | 3 +++ x86/vmx.c | 34 ++++++++++++++++++++-------------- x86/vmx.h | 15 +++++++++++++-- x86/vmx_tests.c | 31 +++++++++++++++++++++---------- 4 files changed, 57 insertions(+), 26 deletions(-) diff --git a/lib/x86/vm.h b/lib/x86/vm.h index 6e0ce2b..c8565b5 100644 --- a/lib/x86/vm.h +++ b/lib/x86/vm.h @@ -19,7 +19,10 @@ #define X86_CR0_PE 0x00000001 #define X86_CR0_MP 0x00000002 #define X86_CR0_TS 0x00000008 +#define X86_CR0_ET 0x00000010 #define X86_CR0_WP 0x00010000 +#define X86_CR0_NW 0x20000000 +#define X86_CR0_CD 0x40000000 #define X86_CR0_PG 0x80000000 #define X86_CR4_VMXE 0x00000001 #define X86_CR4_TSD 0x00000004 diff --git a/x86/vmx.c b/x86/vmx.c index 9db4ef4..6a2bf44 100644 --- a/x86/vmx.c +++ b/x86/vmx.c @@ -44,14 +44,6 @@ void report(const char *name, int result) } } -static int make_vmcs_current(struct vmcs *vmcs) -{ - bool ret; - - asm volatile ("vmptrld %1; setbe %0" : "=q" (ret) : "m" (vmcs) : "cc"); - return ret; -} - /* entry_sysenter */ asm( ".align 4, 0x90\n\t" @@ -631,6 +623,7 @@ static int exit_handler() static int vmx_run() { u32 ret = 0, fail = 0; + bool entry_double_fail = false; while (1) { asm volatile ( @@ -657,28 +650,41 @@ static int vmx_run() ); if (fail) - ret = launched ? VMX_TEST_RESUME_ERR : - VMX_TEST_LAUNCH_ERR; + if (entry_double_fail) + ret = launched ? VMX_TEST_RESUME_ERR : + VMX_TEST_LAUNCH_ERR; + else { + ret = current->entry_failed_handler(launched); + if (ret == VMX_TEST_RESUME) { + entry_double_fail = true; + host_rflags &= ~(X86_EFLAGS_ZF | + X86_EFLAGS_CF); + } + } else { launched = 1; + entry_double_fail = false; ret = exit_handler(); } if (ret != VMX_TEST_RESUME) break; + ret = fail = 0; } launched = 0; switch (ret) { case VMX_TEST_VMEXIT: return 0; case VMX_TEST_LAUNCH_ERR: - printf("%s : vmlaunch failed.\n", __func__); + printf("%s : vmlaunch failed, entry_double_fail=%d.\n", + __func__, entry_double_fail); if ((!(host_rflags & X86_EFLAGS_CF) && !(host_rflags & X86_EFLAGS_ZF)) || ((host_rflags & X86_EFLAGS_CF) && (host_rflags & X86_EFLAGS_ZF))) printf("\tvmlaunch set wrong flags\n"); report("test vmlaunch", 0); break; case VMX_TEST_RESUME_ERR: - printf("%s : vmresume failed.\n", __func__); + printf("%s : vmresume failed, entry_double_fail=%d.\n", + __func__, entry_double_fail); if ((!(host_rflags & X86_EFLAGS_CF) && !(host_rflags & X86_EFLAGS_ZF)) || ((host_rflags & X86_EFLAGS_CF) && (host_rflags & X86_EFLAGS_ZF))) printf("\tvmresume set wrong flags\n"); @@ -700,12 +706,12 @@ static int test_run(struct vmx_test *test) return 1; } init_vmcs(&(test->vmcs)); + current = test; /* Directly call test->init is ok here, init_vmcs has done vmcs init, vmclear and vmptrld*/ if (test->init) - test->init(test->vmcs); + test->init(); test->exits = 0; - current = test; regs = test->guest_regs; vmcs_write(GUEST_RFLAGS, regs.rflags | 0x2); launched = 0; diff --git a/x86/vmx.h b/x86/vmx.h index dc1ebdf..469b4dc 100644 --- a/x86/vmx.h +++ b/x86/vmx.h @@ -4,7 +4,8 @@ #include "libcflat.h" struct vmcs { - u32 revision_id; /* vmcs revision identifier */ + u32 revision_id:31, /* vmcs revision identifier */ + shadow:1; /* shadow-VMCS indicator */ u32 abort; /* VMX-abort indicator */ /* VMCS data */ char data[0]; @@ -32,10 +33,11 @@ struct regs { struct vmx_test { const char *name; - void (*init)(struct vmcs *vmcs); + void (*init)(); void (*guest_main)(); int (*exit_handler)(); void (*syscall_handler)(u64 syscall_no); + int (*entry_failed_handler)(); struct regs guest_regs; struct vmcs *vmcs; int exits; @@ -378,6 +380,7 @@ enum Ctrl1 { CPU_URG = 1ul << 7, CPU_WBINVD = 1ul << 6, CPU_RDRAND = 1ul << 11, + CPU_SHADOW = 1ul << 14, }; #define SAVE_GPR \ @@ -512,6 +515,14 @@ extern union vmx_ctrl_exit ctrl_exit_rev; extern union vmx_ctrl_ent ctrl_enter_rev; extern union vmx_ept_vpid ept_vpid; +static int make_vmcs_current(struct vmcs *vmcs) +{ + bool ret; + + asm volatile ("vmptrld %1; setbe %0" : "=q" (ret) : "m" (vmcs) : "cc"); + return ret; +} + static inline int vmcs_clear(struct vmcs *vmcs) { bool ret; diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c index 0759e10..e95e6b8 100644 --- a/x86/vmx_tests.c +++ b/x86/vmx_tests.c @@ -73,6 +73,12 @@ void basic_syscall_handler(u64 syscall_no) { } +int basic_entry_failed_handler() +{ + return launched ? VMX_TEST_RESUME_ERR : + VMX_TEST_LAUNCH_ERR; +} + void vmenter_main() { u64 rax; @@ -1111,22 +1117,27 @@ static int ept_exit_handler() basic_* just implement some basic functions */ struct vmx_test vmx_tests[] = { { "null", basic_init, basic_guest_main, basic_exit_handler, - basic_syscall_handler, {0} }, + basic_syscall_handler, basic_entry_failed_handler, {0} }, { "vmenter", basic_init, vmenter_main, vmenter_exit_handler, - basic_syscall_handler, {0} }, + basic_syscall_handler, basic_entry_failed_handler, {0} }, { "preemption timer", preemption_timer_init, preemption_timer_main, - preemption_timer_exit_handler, basic_syscall_handler, {0} }, + preemption_timer_exit_handler, basic_syscall_handler, + basic_entry_failed_handler, {0} }, { "control field PAT", test_ctrl_pat_init, test_ctrl_pat_main, - test_ctrl_pat_exit_handler, basic_syscall_handler, {0} }, + test_ctrl_pat_exit_handler, basic_syscall_handler, + basic_entry_failed_handler, {0} }, { "control field EFER", test_ctrl_efer_init, test_ctrl_efer_main, - test_ctrl_efer_exit_handler, basic_syscall_handler, {0} }, + test_ctrl_efer_exit_handler, basic_syscall_handler, + basic_entry_failed_handler, {0} }, { "CR shadowing", basic_init, cr_shadowing_main, - cr_shadowing_exit_handler, basic_syscall_handler, {0} }, + cr_shadowing_exit_handler, basic_syscall_handler, + basic_entry_failed_handler, {0} }, { "I/O bitmap", iobmp_init, iobmp_main, iobmp_exit_handler, - basic_syscall_handler, {0} }, + basic_syscall_handler, basic_entry_failed_handler, {0} }, { "instruction intercept", insn_intercept_init, insn_intercept_main, - insn_intercept_exit_handler, basic_syscall_handler, {0} }, + insn_intercept_exit_handler, basic_syscall_handler, + basic_entry_failed_handler, {0} }, { "EPT framework", ept_init, ept_main, ept_exit_handler, - basic_syscall_handler, {0} }, - { NULL, NULL, NULL, NULL, NULL, {0} }, + basic_syscall_handler, basic_entry_failed_handler, {0} }, + { NULL, NULL, NULL, NULL, NULL, NULL, {0} }, }; -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html