xref: /linux/tools/testing/selftests/kvm/lib/x86_64/ucall.c (revision 4b132aacb0768ac1e652cf517097ea6f237214b9)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * ucall support. A ucall is a "hypercall to userspace".
4  *
5  * Copyright (C) 2018, Red Hat, Inc.
6  */
7 #include "kvm_util.h"
8 
9 #define UCALL_PIO_PORT ((uint16_t)0x1000)
10 
11 void ucall_arch_do_ucall(vm_vaddr_t uc)
12 {
13 	/*
14 	 * FIXME: Revert this hack (the entire commit that added it) once nVMX
15 	 * preserves L2 GPRs across a nested VM-Exit.  If a ucall from L2, e.g.
16 	 * to do a GUEST_SYNC(), lands the vCPU in L1, any and all GPRs can be
17 	 * clobbered by L1.  Save and restore non-volatile GPRs (clobbering RBP
18 	 * in particular is problematic) along with RDX and RDI (which are
19 	 * inputs), and clobber volatile GPRs. *sigh*
20 	 */
21 #define HORRIFIC_L2_UCALL_CLOBBER_HACK	\
22 	"rcx", "rsi", "r8", "r9", "r10", "r11"
23 
24 	asm volatile("push %%rbp\n\t"
25 		     "push %%r15\n\t"
26 		     "push %%r14\n\t"
27 		     "push %%r13\n\t"
28 		     "push %%r12\n\t"
29 		     "push %%rbx\n\t"
30 		     "push %%rdx\n\t"
31 		     "push %%rdi\n\t"
32 		     "in %[port], %%al\n\t"
33 		     "pop %%rdi\n\t"
34 		     "pop %%rdx\n\t"
35 		     "pop %%rbx\n\t"
36 		     "pop %%r12\n\t"
37 		     "pop %%r13\n\t"
38 		     "pop %%r14\n\t"
39 		     "pop %%r15\n\t"
40 		     "pop %%rbp\n\t"
41 		: : [port] "d" (UCALL_PIO_PORT), "D" (uc) : "rax", "memory",
42 		     HORRIFIC_L2_UCALL_CLOBBER_HACK);
43 }
44 
45 void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu)
46 {
47 	struct kvm_run *run = vcpu->run;
48 
49 	if (run->exit_reason == KVM_EXIT_IO && run->io.port == UCALL_PIO_PORT) {
50 		struct kvm_regs regs;
51 
52 		vcpu_regs_get(vcpu, &regs);
53 		return (void *)regs.rdi;
54 	}
55 	return NULL;
56 }
57