diff options
author | Simon Smith <brigidsmith@google.com> | 2020-04-20 10:58:34 -0700 |
---|---|---|
committer | Paolo Bonzini <pbonzini@redhat.com> | 2020-05-04 12:30:41 -0400 |
commit | 3652250b7cc28b54ac997062882f97058f2a8488 (patch) | |
tree | cd3fb7861ddd790dce9222e17df1dd316fa04873 | |
parent | 7e7aa86f7418a8343de46583977f631e55fd02ed (diff) | |
download | kvm-unit-tests-3652250b7cc28b54ac997062882f97058f2a8488.tar.gz |
x86: nVMX: add new test for vmread/vmwrite flags preservation
This commit adds new unit tests for commit a4d956b93904 ("KVM: nVMX:
vmread should not set rflags to specify success in case of #PF")
The two new tests force a vmread and a vmwrite on an unmapped
address to cause a #PF and verify that the low byte of %rflags is
preserved and that %rip is not advanced. The commit fixed a
bug in vmread, but we include a test for vmwrite as well for
completeness.
Before the aforementioned commit, the ALU flags would be incorrectly
cleared and %rip would be advanced (for vmread).
Signed-off-by: Simon Smith <brigidsmith@google.com>
Reviewed-by: Jim Mattson <jmattson@google.com>
Reviewed-by: Peter Shier <pshier@google.com>
Reviewed-by: Krish Sadhukhan <krish.sadhukhan@oracle.com>
Reviewed-by: Oliver Upton <oupton@google.com>
Message-Id: <20200420175834.258122-1-brigidsmith@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
-rw-r--r-- | x86/vmx.c | 140 |
1 files changed, 140 insertions, 0 deletions
@@ -32,6 +32,7 @@ #include "processor.h" #include "alloc_page.h" #include "vm.h" +#include "vmalloc.h" #include "desc.h" #include "vmx.h" #include "msr.h" @@ -387,6 +388,141 @@ static void test_vmwrite_vmread(void) free_page(vmcs); } +ulong finish_fault; +u8 sentinel; +bool handler_called; + +static void pf_handler(struct ex_regs *regs) +{ + /* + * check that RIP was not improperly advanced and that the + * flags value was preserved. + */ + report(regs->rip < finish_fault, "RIP has not been advanced!"); + report(((u8)regs->rflags == ((sentinel | 2) & 0xd7)), + "The low byte of RFLAGS was preserved!"); + regs->rip = finish_fault; + handler_called = true; + +} + +static void prep_flags_test_env(void **vpage, struct vmcs **vmcs, handler *old) +{ + /* + * get an unbacked address that will cause a #PF + */ + *vpage = alloc_vpage(); + + /* + * set up VMCS so we have something to read from + */ + *vmcs = alloc_page(); + + memset(*vmcs, 0, PAGE_SIZE); + (*vmcs)->hdr.revision_id = basic.revision; + assert(!vmcs_clear(*vmcs)); + assert(!make_vmcs_current(*vmcs)); + + *old = handle_exception(PF_VECTOR, &pf_handler); +} + +static void test_read_sentinel(void) +{ + void *vpage; + struct vmcs *vmcs; + handler old; + + prep_flags_test_env(&vpage, &vmcs, &old); + + /* + * set the proper label + */ + extern char finish_read_fault; + + finish_fault = (ulong)&finish_read_fault; + + /* + * execute the vmread instruction that will cause a #PF + */ + handler_called = false; + asm volatile ("movb %[byte], %%ah\n\t" + "sahf\n\t" + "vmread %[enc], %[val]; finish_read_fault:" + : [val] "=m" (*(u64 *)vpage) + : [byte] "Krm" (sentinel), + [enc] "r" ((u64)GUEST_SEL_SS) + : "cc", "ah"); + report(handler_called, "The #PF handler was invoked"); + + /* + * restore the old #PF handler + */ + handle_exception(PF_VECTOR, old); +} + +static void test_vmread_flags_touch(void) +{ + /* + * set up the sentinel value in the flags register. we + * choose these two values because they candy-stripe + * the 5 flags that sahf sets. + */ + sentinel = 0x91; + test_read_sentinel(); + + sentinel = 0x45; + test_read_sentinel(); +} + +static void test_write_sentinel(void) +{ + void *vpage; + struct vmcs *vmcs; + handler old; + + prep_flags_test_env(&vpage, &vmcs, &old); + + /* + * set the proper label + */ + extern char finish_write_fault; + + finish_fault = (ulong)&finish_write_fault; + + /* + * execute the vmwrite instruction that will cause a #PF + */ + handler_called = false; + asm volatile ("movb %[byte], %%ah\n\t" + "sahf\n\t" + "vmwrite %[val], %[enc]; finish_write_fault:" + : [val] "=m" (*(u64 *)vpage) + : [byte] "Krm" (sentinel), + [enc] "r" ((u64)GUEST_SEL_SS) + : "cc", "ah"); + report(handler_called, "The #PF handler was invoked"); + + /* + * restore the old #PF handler + */ + handle_exception(PF_VECTOR, old); +} + +static void test_vmwrite_flags_touch(void) +{ + /* + * set up the sentinel value in the flags register. we + * choose these two values because they candy-stripe + * the 5 flags that sahf sets. + */ + sentinel = 0x91; + test_write_sentinel(); + + sentinel = 0x45; + test_write_sentinel(); +} + + static void test_vmcs_high(void) { struct vmcs *vmcs = alloc_page(); @@ -1988,6 +2124,10 @@ int main(int argc, const char *argv[]) test_vmcs_lifecycle(); if (test_wanted("test_vmx_caps", argv, argc)) test_vmx_caps(); + if (test_wanted("test_vmread_flags_touch", argv, argc)) + test_vmread_flags_touch(); + if (test_wanted("test_vmwrite_flags_touch", argv, argc)) + test_vmwrite_flags_touch(); /* Balance vmxon from test_vmxon. */ vmx_off(); |