xref: /linux/tools/testing/selftests/kvm/lib/ucall_common.c (revision f4b0c4b508364fde023e4f7b9f23f7e38c663dfe)
1 // SPDX-License-Identifier: GPL-2.0-only
2 #include "linux/types.h"
3 #include "linux/bitmap.h"
4 #include "linux/atomic.h"
5 
6 #include "kvm_util.h"
7 #include "ucall_common.h"
8 
9 
10 #define GUEST_UCALL_FAILED -1
11 
12 struct ucall_header {
13 	DECLARE_BITMAP(in_use, KVM_MAX_VCPUS);
14 	struct ucall ucalls[KVM_MAX_VCPUS];
15 };
16 
ucall_nr_pages_required(uint64_t page_size)17 int ucall_nr_pages_required(uint64_t page_size)
18 {
19 	return align_up(sizeof(struct ucall_header), page_size) / page_size;
20 }
21 
22 /*
23  * ucall_pool holds per-VM values (global data is duplicated by each VM), it
24  * must not be accessed from host code.
25  */
26 static struct ucall_header *ucall_pool;
27 
ucall_init(struct kvm_vm * vm,vm_paddr_t mmio_gpa)28 void ucall_init(struct kvm_vm *vm, vm_paddr_t mmio_gpa)
29 {
30 	struct ucall_header *hdr;
31 	struct ucall *uc;
32 	vm_vaddr_t vaddr;
33 	int i;
34 
35 	vaddr = vm_vaddr_alloc_shared(vm, sizeof(*hdr), KVM_UTIL_MIN_VADDR,
36 				      MEM_REGION_DATA);
37 	hdr = (struct ucall_header *)addr_gva2hva(vm, vaddr);
38 	memset(hdr, 0, sizeof(*hdr));
39 
40 	for (i = 0; i < KVM_MAX_VCPUS; ++i) {
41 		uc = &hdr->ucalls[i];
42 		uc->hva = uc;
43 	}
44 
45 	write_guest_global(vm, ucall_pool, (struct ucall_header *)vaddr);
46 
47 	ucall_arch_init(vm, mmio_gpa);
48 }
49 
ucall_alloc(void)50 static struct ucall *ucall_alloc(void)
51 {
52 	struct ucall *uc;
53 	int i;
54 
55 	if (!ucall_pool)
56 		goto ucall_failed;
57 
58 	for (i = 0; i < KVM_MAX_VCPUS; ++i) {
59 		if (!test_and_set_bit(i, ucall_pool->in_use)) {
60 			uc = &ucall_pool->ucalls[i];
61 			memset(uc->args, 0, sizeof(uc->args));
62 			return uc;
63 		}
64 	}
65 
66 ucall_failed:
67 	/*
68 	 * If the vCPU cannot grab a ucall structure, make a bare ucall with a
69 	 * magic value to signal to get_ucall() that things went sideways.
70 	 * GUEST_ASSERT() depends on ucall_alloc() and so cannot be used here.
71 	 */
72 	ucall_arch_do_ucall(GUEST_UCALL_FAILED);
73 	return NULL;
74 }
75 
ucall_free(struct ucall * uc)76 static void ucall_free(struct ucall *uc)
77 {
78 	/* Beware, here be pointer arithmetic.  */
79 	clear_bit(uc - ucall_pool->ucalls, ucall_pool->in_use);
80 }
81 
ucall_assert(uint64_t cmd,const char * exp,const char * file,unsigned int line,const char * fmt,...)82 void ucall_assert(uint64_t cmd, const char *exp, const char *file,
83 		  unsigned int line, const char *fmt, ...)
84 {
85 	struct ucall *uc;
86 	va_list va;
87 
88 	uc = ucall_alloc();
89 	uc->cmd = cmd;
90 
91 	WRITE_ONCE(uc->args[GUEST_ERROR_STRING], (uint64_t)(exp));
92 	WRITE_ONCE(uc->args[GUEST_FILE], (uint64_t)(file));
93 	WRITE_ONCE(uc->args[GUEST_LINE], line);
94 
95 	va_start(va, fmt);
96 	guest_vsnprintf(uc->buffer, UCALL_BUFFER_LEN, fmt, va);
97 	va_end(va);
98 
99 	ucall_arch_do_ucall((vm_vaddr_t)uc->hva);
100 
101 	ucall_free(uc);
102 }
103 
ucall_fmt(uint64_t cmd,const char * fmt,...)104 void ucall_fmt(uint64_t cmd, const char *fmt, ...)
105 {
106 	struct ucall *uc;
107 	va_list va;
108 
109 	uc = ucall_alloc();
110 	uc->cmd = cmd;
111 
112 	va_start(va, fmt);
113 	guest_vsnprintf(uc->buffer, UCALL_BUFFER_LEN, fmt, va);
114 	va_end(va);
115 
116 	ucall_arch_do_ucall((vm_vaddr_t)uc->hva);
117 
118 	ucall_free(uc);
119 }
120 
ucall(uint64_t cmd,int nargs,...)121 void ucall(uint64_t cmd, int nargs, ...)
122 {
123 	struct ucall *uc;
124 	va_list va;
125 	int i;
126 
127 	uc = ucall_alloc();
128 
129 	WRITE_ONCE(uc->cmd, cmd);
130 
131 	nargs = min(nargs, UCALL_MAX_ARGS);
132 
133 	va_start(va, nargs);
134 	for (i = 0; i < nargs; ++i)
135 		WRITE_ONCE(uc->args[i], va_arg(va, uint64_t));
136 	va_end(va);
137 
138 	ucall_arch_do_ucall((vm_vaddr_t)uc->hva);
139 
140 	ucall_free(uc);
141 }
142 
get_ucall(struct kvm_vcpu * vcpu,struct ucall * uc)143 uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
144 {
145 	struct ucall ucall;
146 	void *addr;
147 
148 	if (!uc)
149 		uc = &ucall;
150 
151 	addr = ucall_arch_get_ucall(vcpu);
152 	if (addr) {
153 		TEST_ASSERT(addr != (void *)GUEST_UCALL_FAILED,
154 			    "Guest failed to allocate ucall struct");
155 
156 		memcpy(uc, addr, sizeof(*uc));
157 		vcpu_run_complete_io(vcpu);
158 	} else {
159 		memset(uc, 0, sizeof(*uc));
160 	}
161 
162 	return uc->cmd;
163 }
164