According to section "UNRESTRICTED GUESTS" in SDM vol 3c, if the "unrestricted guest" secondary VM-execution control is set, guests can run in unpaged protected mode or in real mode. This patch tests vmetnry of an unrestricted guest in unpaged protected mode. Co-developed-by: Jim Mattson <jmattson@xxxxxxxxxx> Signed-off-by: Krish Sadhukhan <krish.sadhukhan@xxxxxxxxxx> --- x86/vmx.c | 2 +- x86/vmx.h | 1 + x86/vmx_tests.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/x86/vmx.c b/x86/vmx.c index 07415b4..1a84a74 100644 --- a/x86/vmx.c +++ b/x86/vmx.c @@ -1699,7 +1699,7 @@ static void test_vmx_caps(void) } /* This function can only be called in guest */ -static void __attribute__((__used__)) hypercall(u32 hypercall_no) +void __attribute__((__used__)) hypercall(u32 hypercall_no) { u64 val = 0; val = (hypercall_no & HYPERCALL_MASK) | HYPERCALL_BIT; diff --git a/x86/vmx.h b/x86/vmx.h index d1c2436..e29301e 100644 --- a/x86/vmx.h +++ b/x86/vmx.h @@ -895,6 +895,7 @@ bool ept_ad_bits_supported(void); void __enter_guest(u8 abort_flag, struct vmentry_result *result); void enter_guest(void); void enter_guest_with_bad_controls(void); +void hypercall(u32 hypercall_no); typedef void (*test_guest_func)(void); typedef void (*test_teardown_func)(void *data); diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c index 22f0c7b..c35d162 100644 --- a/x86/vmx_tests.c +++ b/x86/vmx_tests.c @@ -8029,6 +8029,53 @@ static void vmx_guest_state_area_test(void) enter_guest(); } +extern void unrestricted_guest_main(void); +asm (".code32\n" + "unrestricted_guest_main:\n" + "vmcall\n" + "nop\n" + "mov $1, %edi\n" + "call hypercall\n" + ".code64\n"); + +static void setup_unrestricted_guest(void) +{ + vmcs_write(GUEST_CR0, vmcs_read(GUEST_CR0) & ~(X86_CR0_PG)); + vmcs_write(ENT_CONTROLS, vmcs_read(ENT_CONTROLS) & ~ENT_GUEST_64); + vmcs_write(GUEST_EFER, vmcs_read(GUEST_EFER) & ~EFER_LMA); + vmcs_write(GUEST_RIP, virt_to_phys(unrestricted_guest_main)); +} + +static void unsetup_unrestricted_guest(void) +{ + vmcs_write(GUEST_CR0, vmcs_read(GUEST_CR0) | X86_CR0_PG); + vmcs_write(ENT_CONTROLS, vmcs_read(ENT_CONTROLS) | ENT_GUEST_64); + vmcs_write(GUEST_EFER, vmcs_read(GUEST_EFER) | EFER_LMA); + vmcs_write(GUEST_RIP, (u64) phys_to_virt(vmcs_read(GUEST_RIP))); + vmcs_write(GUEST_RSP, (u64) phys_to_virt(vmcs_read(GUEST_RSP))); +} + +/* + * If "unrestricted guest" secondary VM-execution control is set, guests + * can run in unpaged protected mode. + */ +static void vmentry_unrestricted_guest_test(void) +{ + test_set_guest(unrestricted_guest_main); + setup_unrestricted_guest(); + if (setup_ept(false)) + test_skip("EPT not supported"); + vmcs_write(CPU_EXEC_CTRL1, vmcs_read(CPU_EXEC_CTRL1) | CPU_URG); + test_guest_state("Unrestricted guest test", false, CPU_URG, "CPU_URG"); + + /* + * Let the guest finish execution as a regular guest + */ + unsetup_unrestricted_guest(); + vmcs_write(CPU_EXEC_CTRL1, vmcs_read(CPU_EXEC_CTRL1) & ~CPU_URG); + enter_guest(); +} + static bool valid_vmcs_for_vmentry(void) { struct vmcs *current_vmcs = NULL; @@ -10234,6 +10281,7 @@ struct vmx_test vmx_tests[] = { TEST(vmx_host_state_area_test), TEST(vmx_guest_state_area_test), TEST(vmentry_movss_shadow_test), + TEST(vmentry_unrestricted_guest_test), /* APICv tests */ TEST(vmx_eoi_bitmap_ioapic_scan_test), TEST(vmx_hlt_with_rvi_test), -- 2.18.4