Skip to content

Commit 6783ca4

Browse files
committed
KVM: selftests: Add a shameful hack to preserve/clobber GPRs across ucall
Preserve or clobber all GPRs (except RIP and RSP, as they're saved and restored via the VMCS) when performing a ucall on x86 to fudge around a horrific long-standing bug in selftests' nested VMX support where L2's GPRs are not preserved across a nested VM-Exit. I.e. if a test triggers a nested VM-Exit to L1 in response to a ucall, e.g. GUEST_SYNC(), then L2's GPR state can be corrupted. The issues manifests as an unexpected #GP in clear_bit() when running the hyperv_evmcs test due to RBX being used to track the ucall object, and RBX being clobbered by the nested VM-Exit. The problematic hyperv_evmcs testcase is where L0 (test's host userspace) injects an NMI in response to GUEST_SYNC(8) from L2, but the bug could "randomly" manifest in any test that induces a nested VM-Exit from L0. The bug hasn't caused failures in the past due to sheer dumb luck. The obvious fix is to rework the nVMX helpers to save/restore L2 GPRs across VM-Exit and VM-Enter, but that is a much bigger task and carries its own risks, e.g. nSVM does save/restore GPRs, but not in a thread-safe manner, and there is a _lot_ of cleanup that can be done to unify code for doing VM-Enter on nVMX, nSVM, and eVMCS. Link: https://lore.kernel.org/r/20230729003643.1053367-4-seanjc@google.com Signed-off-by: Sean Christopherson <seanjc@google.com>
1 parent b145c58 commit 6783ca4

File tree

1 file changed

+30
-2
lines changed
  • tools/testing/selftests/kvm/lib/x86_64

1 file changed

+30
-2
lines changed

tools/testing/selftests/kvm/lib/x86_64/ucall.c

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,36 @@ void ucall_arch_init(struct kvm_vm *vm, vm_paddr_t mmio_gpa)
1414

1515
void ucall_arch_do_ucall(vm_vaddr_t uc)
1616
{
17-
asm volatile("in %[port], %%al"
18-
: : [port] "d" (UCALL_PIO_PORT), "D" (uc) : "rax", "memory");
17+
/*
18+
* FIXME: Revert this hack (the entire commit that added it) once nVMX
19+
* preserves L2 GPRs across a nested VM-Exit. If a ucall from L2, e.g.
20+
* to do a GUEST_SYNC(), lands the vCPU in L1, any and all GPRs can be
21+
* clobbered by L1. Save and restore non-volatile GPRs (clobbering RBP
22+
* in particular is problematic) along with RDX and RDI (which are
23+
* inputs), and clobber volatile GPRs. *sigh*
24+
*/
25+
#define HORRIFIC_L2_UCALL_CLOBBER_HACK \
26+
"rcx", "rsi", "r8", "r9", "r10", "r11"
27+
28+
asm volatile("push %%rbp\n\t"
29+
"push %%r15\n\t"
30+
"push %%r14\n\t"
31+
"push %%r13\n\t"
32+
"push %%r12\n\t"
33+
"push %%rbx\n\t"
34+
"push %%rdx\n\t"
35+
"push %%rdi\n\t"
36+
"in %[port], %%al\n\t"
37+
"pop %%rdi\n\t"
38+
"pop %%rdx\n\t"
39+
"pop %%rbx\n\t"
40+
"pop %%r12\n\t"
41+
"pop %%r13\n\t"
42+
"pop %%r14\n\t"
43+
"pop %%r15\n\t"
44+
"pop %%rbp\n\t"
45+
: : [port] "d" (UCALL_PIO_PORT), "D" (uc) : "rax", "memory",
46+
HORRIFIC_L2_UCALL_CLOBBER_HACK);
1947
}
2048

2149
void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu)

0 commit comments

Comments
 (0)