• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) 2021 SUSE LLC <mdoucha@suse.cz>
4  *
5  * KVM host library for setting up and running virtual machine tests.
6  */
7 
8 #include <stdlib.h>
9 #include <errno.h>
10 
11 #define TST_NO_DEFAULT_MAIN
12 #include "tst_test.h"
13 #include "kvm_host.h"
14 
15 static struct tst_kvm_instance test_vm = { .vm_fd = -1 };
16 
17 const unsigned char tst_kvm_reset_code[VM_RESET_CODE_SIZE] = {
18 	0xea, 0x00, 0x10, 0x00, 0x00	/* JMP 0x1000 */
19 };
20 
tst_kvm_validate_result(int value)21 void tst_kvm_validate_result(int value)
22 {
23 	int ttype, valid_result[] = {TPASS, TFAIL, TBROK, TWARN, TINFO, TCONF};
24 	size_t i;
25 
26 	if (value == KVM_TNONE)
27 		tst_brk(TBROK, "KVM test did not return any result");
28 
29 	ttype = TTYPE_RESULT(value);
30 
31 	for (i = 0; i < ARRAY_SIZE(valid_result); i++) {
32 		if (ttype == valid_result[i])
33 			return;
34 	}
35 
36 	tst_brk(TBROK, "KVM test returned invalid result value %d", value);
37 }
38 
tst_kvm_get_phys_address(const struct tst_kvm_instance * inst,uint64_t addr)39 uint64_t tst_kvm_get_phys_address(const struct tst_kvm_instance *inst,
40 	uint64_t addr)
41 {
42 	struct kvm_translation trans = { .linear_address = addr };
43 
44 	TEST(ioctl(inst->vcpu_fd, KVM_TRANSLATE, &trans));
45 
46 	/* ioctl(KVM_TRANSLATE) is not implemented for this arch */
47 	if (TST_RET == -1 && TST_ERR == EINVAL)
48 		return addr;
49 
50 	if (TST_RET == -1)
51 		tst_brk(TBROK | TTERRNO, "ioctl(KVM_TRANSLATE) failed");
52 
53 	if (TST_RET) {
54 		tst_brk(TBROK | TTERRNO,
55 			"Invalid ioctl(KVM_TRANSLATE) return value");
56 	}
57 
58 	return trans.valid ? trans.physical_address : 0;
59 }
60 
tst_kvm_find_phys_memslot(const struct tst_kvm_instance * inst,uint64_t paddr)61 int tst_kvm_find_phys_memslot(const struct tst_kvm_instance *inst,
62 	uint64_t paddr)
63 {
64 	int i;
65 	uint64_t base;
66 
67 	for (i = 0; i < MAX_KVM_MEMSLOTS; i++) {
68 		if (!inst->ram[i].userspace_addr)
69 			continue;
70 
71 		base = inst->ram[i].guest_phys_addr;
72 
73 		if (paddr >= base && paddr - base < inst->ram[i].memory_size)
74 			return i;
75 	}
76 
77 	return -1;
78 }
79 
tst_kvm_find_memslot(const struct tst_kvm_instance * inst,uint64_t addr)80 int tst_kvm_find_memslot(const struct tst_kvm_instance *inst, uint64_t addr)
81 {
82 	addr = tst_kvm_get_phys_address(inst, addr);
83 
84 	if (!addr)
85 		return -1;
86 
87 	return tst_kvm_find_phys_memslot(inst, addr);
88 }
89 
tst_kvm_get_memptr(const struct tst_kvm_instance * inst,uint64_t addr)90 void *tst_kvm_get_memptr(const struct tst_kvm_instance *inst, uint64_t addr)
91 {
92 	int slot;
93 	char *ret;
94 
95 	addr = tst_kvm_get_phys_address(inst, addr);
96 
97 	if (!addr)
98 		return NULL;
99 
100 	slot = tst_kvm_find_phys_memslot(inst, addr);
101 
102 	if (slot < 0)
103 		return NULL;
104 
105 	ret = (char *)(uintptr_t)inst->ram[slot].userspace_addr;
106 	return ret + (addr - inst->ram[slot].guest_phys_addr);
107 }
108 
tst_kvm_print_result(const struct tst_kvm_instance * inst)109 void tst_kvm_print_result(const struct tst_kvm_instance *inst)
110 {
111 	int ttype;
112 	const struct tst_kvm_result *result = inst->result;
113 	const char *file;
114 
115 	tst_kvm_validate_result(result->result);
116 	ttype = TTYPE_RESULT(result->result);
117 	file = tst_kvm_get_memptr(inst, result->file_addr);
118 
119 	if (ttype == TBROK)
120 		tst_brk_(file, result->lineno, ttype, "%s", result->message);
121 	else
122 		tst_res_(file, result->lineno, ttype, "%s", result->message);
123 }
124 
tst_kvm_alloc_memory(struct tst_kvm_instance * inst,unsigned int slot,uint64_t baseaddr,size_t size,unsigned int flags)125 void *tst_kvm_alloc_memory(struct tst_kvm_instance *inst, unsigned int slot,
126 	uint64_t baseaddr, size_t size, unsigned int flags)
127 {
128 	size_t pagesize, offset;
129 	char *ret;
130 	struct kvm_userspace_memory_region memslot = {
131 		.slot = slot,
132 		.flags = flags
133 	};
134 
135 	if (slot >= MAX_KVM_MEMSLOTS)
136 		tst_brk(TBROK, "Invalid KVM memory slot %u", slot);
137 
138 	pagesize = SAFE_SYSCONF(_SC_PAGESIZE);
139 	offset = baseaddr % pagesize;
140 	size = LTP_ALIGN(size + offset, pagesize);
141 	ret = tst_alloc(size);
142 
143 	memslot.guest_phys_addr = baseaddr - offset;
144 	memslot.memory_size = size;
145 	memslot.userspace_addr = (uintptr_t)ret;
146 	SAFE_IOCTL(inst->vm_fd, KVM_SET_USER_MEMORY_REGION, &memslot);
147 	inst->ram[slot] = memslot;
148 	return ret;
149 }
150 
tst_kvm_get_cpuid(int sysfd)151 struct kvm_cpuid2 *tst_kvm_get_cpuid(int sysfd)
152 {
153 	unsigned int count;
154 	int result;
155 	struct kvm_cpuid2 *ret;
156 
157 	if (!SAFE_IOCTL(sysfd, KVM_CHECK_EXTENSION, KVM_CAP_EXT_CPUID))
158 		return NULL;
159 
160 	for (count = 8; count < 1 << 30; count *= 2) {
161 		ret = SAFE_MALLOC(sizeof(struct kvm_cpuid2) +
162 			count * sizeof(struct kvm_cpuid_entry2));
163 		ret->nent = count;
164 		errno = 0;
165 		result = ioctl(sysfd, KVM_GET_SUPPORTED_CPUID, ret);
166 
167 		if (!result)
168 			return ret;
169 
170 		free(ret);
171 
172 		if (errno != E2BIG)
173 			break;
174 	}
175 
176 	tst_brk(TBROK | TERRNO, "ioctl(KVM_GET_SUPPORTED_CPUID) failed");
177 	return NULL;
178 }
179 
tst_kvm_create_instance(struct tst_kvm_instance * inst,size_t ram_size)180 void tst_kvm_create_instance(struct tst_kvm_instance *inst, size_t ram_size)
181 {
182 	int sys_fd;
183 	size_t pagesize, result_pageaddr = KVM_RESULT_BASEADDR;
184 	char *buf, *reset_ptr;
185 	struct kvm_cpuid2 *cpuid_data;
186 	const size_t payload_size = kvm_payload_end - kvm_payload_start;
187 
188 	memset(inst, 0, sizeof(struct tst_kvm_instance));
189 	inst->vm_fd = -1;
190 	inst->vcpu_fd = -1;
191 	inst->vcpu_info = MAP_FAILED;
192 
193 	pagesize = SAFE_SYSCONF(_SC_PAGESIZE);
194 	result_pageaddr -= result_pageaddr % pagesize;
195 
196 	if (payload_size + MIN_FREE_RAM > ram_size - VM_KERNEL_BASEADDR) {
197 		ram_size = payload_size + MIN_FREE_RAM + VM_KERNEL_BASEADDR;
198 		ram_size = LTP_ALIGN(ram_size, 1024 * 1024);
199 		tst_res(TWARN, "RAM size increased to %zu bytes", ram_size);
200 	}
201 
202 	if (ram_size > result_pageaddr) {
203 		ram_size = result_pageaddr;
204 		tst_res(TWARN, "RAM size truncated to %zu bytes", ram_size);
205 	}
206 
207 	sys_fd = SAFE_OPEN("/dev/kvm", O_RDWR);
208 	inst->vcpu_info_size = SAFE_IOCTL(sys_fd, KVM_GET_VCPU_MMAP_SIZE, 0);
209 	inst->vm_fd = SAFE_IOCTL(sys_fd, KVM_CREATE_VM, 0);
210 	cpuid_data = tst_kvm_get_cpuid(sys_fd);
211 	SAFE_CLOSE(sys_fd);
212 
213 	inst->vcpu_fd = SAFE_IOCTL(inst->vm_fd, KVM_CREATE_VCPU, 0);
214 
215 	if (cpuid_data) {
216 		SAFE_IOCTL(inst->vcpu_fd, KVM_SET_CPUID2, cpuid_data);
217 		free(cpuid_data);
218 	}
219 
220 	inst->vcpu_info = SAFE_MMAP(NULL, inst->vcpu_info_size,
221 		PROT_READ | PROT_WRITE, MAP_SHARED, inst->vcpu_fd, 0);
222 
223 	buf = tst_kvm_alloc_memory(inst, 0, 0, ram_size, 0);
224 	memcpy(buf + VM_KERNEL_BASEADDR, kvm_payload_start, payload_size);
225 	buf = tst_kvm_alloc_memory(inst, 1, KVM_RESULT_BASEADDR,
226 		KVM_RESULT_SIZE, 0);
227 	memset(buf, 0, KVM_RESULT_SIZE);
228 
229 	reset_ptr = buf + (VM_RESET_BASEADDR % pagesize);
230 	memcpy(reset_ptr, tst_kvm_reset_code, sizeof(tst_kvm_reset_code));
231 	inst->result = (struct tst_kvm_result *)(buf +
232 		(KVM_RESULT_BASEADDR % pagesize));
233 	inst->result->result = KVM_TNONE;
234 	inst->result->message[0] = '\0';
235 }
236 
tst_kvm_run_instance(struct tst_kvm_instance * inst)237 void tst_kvm_run_instance(struct tst_kvm_instance *inst)
238 {
239 	struct kvm_regs regs;
240 
241 	while (1) {
242 		inst->result->result = KVM_TNONE;
243 		inst->result->message[0] = '\0';
244 		SAFE_IOCTL(inst->vcpu_fd, KVM_RUN, 0);
245 
246 		if (inst->vcpu_info->exit_reason != KVM_EXIT_HLT) {
247 			SAFE_IOCTL(inst->vcpu_fd, KVM_GET_REGS, &regs);
248 			tst_brk(TBROK,
249 				"Unexpected VM exit, RIP=0x%llx, reason=%u",
250 				regs.rip, inst->vcpu_info->exit_reason);
251 		}
252 
253 		if (inst->result->result == KVM_TEXIT)
254 			break;
255 
256 		tst_kvm_print_result(inst);
257 	}
258 }
259 
tst_kvm_destroy_instance(struct tst_kvm_instance * inst)260 void tst_kvm_destroy_instance(struct tst_kvm_instance *inst)
261 {
262 	if (inst->vm_fd < 0)
263 		return;
264 
265 	if (inst->vcpu_info != MAP_FAILED)
266 		SAFE_MUNMAP(inst->vcpu_info, inst->vcpu_info_size);
267 
268 	if (inst->vcpu_fd >= 0)
269 		SAFE_CLOSE(inst->vcpu_fd);
270 
271 	SAFE_CLOSE(inst->vm_fd);
272 	memset(inst->ram, 0, sizeof(inst->ram));
273 }
274 
tst_kvm_setup(void)275 void tst_kvm_setup(void)
276 {
277 
278 }
279 
tst_kvm_run(void)280 void tst_kvm_run(void)
281 {
282 	tst_kvm_create_instance(&test_vm, DEFAULT_RAM_SIZE);
283 	tst_kvm_run_instance(&test_vm);
284 	tst_kvm_destroy_instance(&test_vm);
285 	tst_free_all();
286 }
287 
tst_kvm_cleanup(void)288 void tst_kvm_cleanup(void)
289 {
290 	tst_kvm_destroy_instance(&test_vm);
291 }
292