1 // SPDX-License-Identifier: GPL-2.0-only
2 #include "kvm_util.h"
3 #include "linux/types.h"
4 #include "linux/bitmap.h"
5 #include "linux/atomic.h"
6 
7 struct ucall_header {
8 	DECLARE_BITMAP(in_use, KVM_MAX_VCPUS);
9 	struct ucall ucalls[KVM_MAX_VCPUS];
10 };
11 
12 /*
13  * ucall_pool holds per-VM values (global data is duplicated by each VM), it
14  * must not be accessed from host code.
15  */
16 static struct ucall_header *ucall_pool;
17 
18 void ucall_init(struct kvm_vm *vm, vm_paddr_t mmio_gpa)
19 {
20 	struct ucall_header *hdr;
21 	struct ucall *uc;
22 	vm_vaddr_t vaddr;
23 	int i;
24 
25 	vaddr = __vm_vaddr_alloc(vm, sizeof(*hdr), KVM_UTIL_MIN_VADDR, MEM_REGION_DATA);
26 	hdr = (struct ucall_header *)addr_gva2hva(vm, vaddr);
27 	memset(hdr, 0, sizeof(*hdr));
28 
29 	for (i = 0; i < KVM_MAX_VCPUS; ++i) {
30 		uc = &hdr->ucalls[i];
31 		uc->hva = uc;
32 	}
33 
34 	write_guest_global(vm, ucall_pool, (struct ucall_header *)vaddr);
35 
36 	ucall_arch_init(vm, mmio_gpa);
37 }
38 
39 static struct ucall *ucall_alloc(void)
40 {
41 	struct ucall *uc;
42 	int i;
43 
44 	GUEST_ASSERT(ucall_pool);
45 
46 	for (i = 0; i < KVM_MAX_VCPUS; ++i) {
47 		if (!test_and_set_bit(i, ucall_pool->in_use)) {
48 			uc = &ucall_pool->ucalls[i];
49 			memset(uc->args, 0, sizeof(uc->args));
50 			return uc;
51 		}
52 	}
53 
54 	GUEST_ASSERT(0);
55 	return NULL;
56 }
57 
58 static void ucall_free(struct ucall *uc)
59 {
60 	/* Beware, here be pointer arithmetic.  */
61 	clear_bit(uc - ucall_pool->ucalls, ucall_pool->in_use);
62 }
63 
64 void ucall(uint64_t cmd, int nargs, ...)
65 {
66 	struct ucall *uc;
67 	va_list va;
68 	int i;
69 
70 	uc = ucall_alloc();
71 
72 	WRITE_ONCE(uc->cmd, cmd);
73 
74 	nargs = min(nargs, UCALL_MAX_ARGS);
75 
76 	va_start(va, nargs);
77 	for (i = 0; i < nargs; ++i)
78 		WRITE_ONCE(uc->args[i], va_arg(va, uint64_t));
79 	va_end(va);
80 
81 	ucall_arch_do_ucall((vm_vaddr_t)uc->hva);
82 
83 	ucall_free(uc);
84 }
85 
86 uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
87 {
88 	struct ucall ucall;
89 	void *addr;
90 
91 	if (!uc)
92 		uc = &ucall;
93 
94 	addr = ucall_arch_get_ucall(vcpu);
95 	if (addr) {
96 		memcpy(uc, addr, sizeof(*uc));
97 		vcpu_run_complete_io(vcpu);
98 	} else {
99 		memset(uc, 0, sizeof(*uc));
100 	}
101 
102 	return uc->cmd;
103 }
104