Signed-off-by: Paolo Bonzini <pbonzini@xxxxxxxxxx> Conflicts: x86/vmx_tests.c --- x86/vmx_tests.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 70 insertions(+), 17 deletions(-) diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c index 97aff6e..dba61a3 100644 --- a/x86/vmx_tests.c +++ b/x86/vmx_tests.c @@ -5030,19 +5030,23 @@ static void guest_state_test_main(void) asm volatile("fnop"); } +static void advance_guest_state_test(void) +{ + u32 reason = vmcs_read(EXI_REASON); + if (! (reason & 0x80000000)) { + u64 guest_rip = vmcs_read(GUEST_RIP); + u32 insn_len = vmcs_read(EXI_INST_LEN); + vmcs_write(GUEST_RIP, guest_rip + insn_len); + } +} + static void report_guest_state_test(const char *test, u32 xreason, u64 field, const char * field_name) { u32 reason = vmcs_read(EXI_REASON); - u64 guest_rip; - u32 insn_len; report("%s, %s %lx", reason == xreason, test, field_name, field); - - guest_rip = vmcs_read(GUEST_RIP); - insn_len = vmcs_read(EXI_INST_LEN); - if (! (reason & 0x80000021)) - vmcs_write(GUEST_RIP, guest_rip + insn_len); + advance_guest_state_test(); } /* @@ -6720,6 +6724,25 @@ static void test_host_ctl_regs(void) vmcs_write(HOST_CR3, cr3_saved); } +static void test_efer_vmlaunch(u32 fld, bool ok) +{ + if (fld == HOST_EFER) { + if (ok) + test_vmx_vmlaunch(0, false); + else + test_vmx_vmlaunch(VMXERR_ENTRY_INVALID_HOST_STATE_FIELD, false); + } else { + if (ok) { + enter_guest(); + report("vmlaunch succeeds", vmcs_read(EXI_REASON) == VMX_VMCALL); + } else { + enter_guest_with_invalid_guest_state(); + report("vmlaunch fails", vmcs_read(EXI_REASON) == (VMX_ENTRY_FAILURE | VMX_FAIL_STATE)); + } + advance_guest_state_test(); + } +} + static void test_efer_one(u32 fld, const char * fld_name, u64 efer, u32 ctrl_fld, u64 ctrl, int i, const char *efer_bit_name) @@ -6727,12 +6750,27 @@ static void test_efer_one(u32 fld, const char * fld_name, u64 efer, bool ok; ok = true; - if (ctrl & EXI_LOAD_EFER) { + if (ctrl_fld == EXI_CONTROLS && (ctrl & EXI_LOAD_EFER)) { if (!!(efer & EFER_LMA) != !!(ctrl & EXI_HOST_64)) ok = false; if (!!(efer & EFER_LME) != !!(ctrl & EXI_HOST_64)) ok = false; } + if (ctrl_fld == ENT_CONTROLS && (ctrl & ENT_LOAD_EFER)) { + /* Check LMA too since CR0.PG is set. */ + if (!!(efer & EFER_LMA) != !!(ctrl & ENT_GUEST_64)) + ok = false; + if (!!(efer & EFER_LME) != !!(ctrl & ENT_GUEST_64)) + ok = false; + } + + /* + * Skip the test if it would enter the guest in 32-bit mode. + * Perhaps write the test in assembly and make sure it + * can be run in either mode? + */ + if (fld == GUEST_EFER && ok && !(ctrl & ENT_GUEST_64)) + return; vmcs_write(ctrl_fld, ctrl); vmcs_write(fld, efer); @@ -6741,11 +6779,7 @@ static void test_efer_one(u32 fld, const char * fld_name, u64 efer, (i & 1) ? "on" : "off", (i & 2) ? "on" : "off"); - if (ok) - test_vmx_vmlaunch(0, false); - else - test_vmx_vmlaunch(VMXERR_ENTRY_INVALID_HOST_STATE_FIELD, - false); + test_efer_vmlaunch(fld, ok); report_prefix_pop(); } @@ -6792,7 +6826,7 @@ static void test_efer(u32 fld, const char * fld_name, u32 ctrl_fld, } report_prefix_pushf("%s %lx", fld_name, efer_saved); - test_vmx_vmlaunch(0, false); + test_efer_vmlaunch(fld, true); report_prefix_pop(); /* @@ -6804,7 +6838,7 @@ static void test_efer(u32 fld, const char * fld_name, u32 ctrl_fld, efer = efer_saved | (1ull << i); vmcs_write(fld, efer); report_prefix_pushf("%s %lx", fld_name, efer); - test_vmx_vmlaunch(0, false); + test_efer_vmlaunch(fld, true); report_prefix_pop(); } } @@ -6815,8 +6849,7 @@ static void test_efer(u32 fld, const char * fld_name, u32 ctrl_fld, efer = efer_saved | (1ull << i); vmcs_write(fld, efer); report_prefix_pushf("%s %lx", fld_name, efer); - test_vmx_vmlaunch(VMXERR_ENTRY_INVALID_HOST_STATE_FIELD, - false); + test_efer_vmlaunch(fld, false); report_prefix_pop(); } } @@ -6863,6 +6896,25 @@ static void test_host_efer(void) } /* + * If the 'load IA32_EFER' VM-enter control is 1, bits reserved in the + * IA32_EFER MSR must be 0 in the field for that register. In addition, + * the values of the LMA and LME bits in the field must each be that of + * the 'IA32e-mode guest' VM-exit control. + */ +static void test_guest_efer(void) +{ + if (!(ctrl_enter_rev.clr & ENT_LOAD_EFER)) { + printf("\"Load-IA32-EFER\" entry control not supported\n"); + return; + } + + vmcs_write(GUEST_EFER, rdmsr(MSR_EFER)); + test_efer(GUEST_EFER, "GUEST_EFER", ENT_CONTROLS, + ctrl_enter_rev.clr & ENT_LOAD_EFER, + ENT_GUEST_64); +} + +/* * PAT values higher than 8 are uninteresting since they're likely lumped * in with "8". We only test values above 8 one bit at a time, * in order to reduce the number of VM-Entries and keep the runtime reasonable. @@ -7230,6 +7282,7 @@ static void vmx_guest_state_area_test(void) test_set_guest(guest_state_test_main); test_load_guest_pat(); + test_guest_efer(); /* * Let the guest finish execution -- 1.8.3.1