xref: /dflybsd-src/lib/libnvmm/libnvmm.c (revision 8f70d46c99a693ffe1b10d34c1715ccb6815d400)
1 /*	$NetBSD: libnvmm.c,v 1.20 2021/04/06 08:40:17 reinoud Exp $	*/
2 
3 /*
4  * Copyright (c) 2018-2020 Maxime Villard, m00nbsd.net
5  * All rights reserved.
6  *
7  * This code is part of the NVMM hypervisor.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
25  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 
31 #include <sys/cdefs.h>
32 #include <sys/ioctl.h>
33 #include <sys/mman.h>
34 #include <sys/queue.h>
35 
36 #include <inttypes.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include <fcntl.h>
42 #include <errno.h>
43 
44 #include "nvmm.h"
45 #include "nvmm_compat.h"
46 
47 static struct nvmm_capability __capability;
48 
49 #ifdef __x86_64__
50 #include "libnvmm_x86.c"
51 #endif
52 
53 typedef struct __area {
54 	LIST_ENTRY(__area) list;
55 	gpaddr_t gpa;
56 	uintptr_t hva;
57 	size_t size;
58 	nvmm_prot_t prot;
59 } area_t;
60 
61 typedef LIST_HEAD(, __area) area_list_t;
62 
63 static int nvmm_fd = -1;
64 
65 /* -------------------------------------------------------------------------- */
66 
67 static bool
68 __area_isvalid(struct nvmm_machine *mach, uintptr_t hva __unused, gpaddr_t gpa,
69     size_t size)
70 {
71 	area_list_t *areas = mach->areas;
72 	area_t *ent;
73 
74 	LIST_FOREACH(ent, areas, list) {
75 		/* Collision on GPA */
76 		if (gpa >= ent->gpa && gpa < ent->gpa + ent->size) {
77 			return false;
78 		}
79 		if (gpa + size > ent->gpa &&
80 		    gpa + size <= ent->gpa + ent->size) {
81 			return false;
82 		}
83 		if (gpa <= ent->gpa && gpa + size >= ent->gpa + ent->size) {
84 			return false;
85 		}
86 	}
87 
88 	return true;
89 }
90 
91 static int
92 __area_add(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa, size_t size,
93     int prot)
94 {
95 	area_list_t *areas = mach->areas;
96 	nvmm_prot_t nprot;
97 	area_t *area;
98 
99 	nprot = 0;
100 	if (prot & PROT_READ)
101 		nprot |= NVMM_PROT_READ;
102 	if (prot & PROT_WRITE)
103 		nprot |= NVMM_PROT_WRITE;
104 	if (prot & PROT_EXEC)
105 		nprot |= NVMM_PROT_EXEC;
106 
107 	if (!__area_isvalid(mach, hva, gpa, size)) {
108 		errno = EINVAL;
109 		return -1;
110 	}
111 
112 	area = malloc(sizeof(*area));
113 	if (area == NULL)
114 		return -1;
115 	area->gpa = gpa;
116 	area->hva = hva;
117 	area->size = size;
118 	area->prot = nprot;
119 
120 	LIST_INSERT_HEAD(areas, area, list);
121 
122 	return 0;
123 }
124 
125 static int
126 __area_delete(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa,
127     size_t size)
128 {
129 	area_list_t *areas = mach->areas;
130 	area_t *ent, *nxt;
131 
132 	LIST_FOREACH_SAFE(ent, areas, list, nxt) {
133 		if (hva == ent->hva && gpa == ent->gpa && size == ent->size) {
134 			LIST_REMOVE(ent, list);
135 			free(ent);
136 			return 0;
137 		}
138 	}
139 
140 	return -1;
141 }
142 
143 static void
144 __area_remove_all(struct nvmm_machine *mach)
145 {
146 	area_list_t *areas = mach->areas;
147 	area_t *ent;
148 
149 	while ((ent = LIST_FIRST(areas)) != NULL) {
150 		LIST_REMOVE(ent, list);
151 		free(ent);
152 	}
153 
154 	free(areas);
155 }
156 
157 /* -------------------------------------------------------------------------- */
158 
159 int
160 nvmm_init(void)
161 {
162 	if (nvmm_fd != -1)
163 		return 0;
164 	nvmm_fd = open("/dev/nvmm", O_RDWR | O_CLOEXEC);
165 	if (nvmm_fd == -1)
166 		return -1;
167 	if (nvmm_capability(&__capability) == -1) {
168 		close(nvmm_fd);
169 		nvmm_fd = -1;
170 		return -1;
171 	}
172 	if (__capability.version != NVMM_KERN_VERSION) {
173 		close(nvmm_fd);
174 		nvmm_fd = -1;
175 		errno = EPROGMISMATCH;
176 		return -1;
177 	}
178 
179 	return 0;
180 }
181 
182 int
183 nvmm_root_init(void)
184 {
185 	if (getuid() != 0) {
186 		errno = EACCES;
187 		return -1;
188 	}
189 
190 	return nvmm_init();
191 }
192 
193 int
194 nvmm_capability(struct nvmm_capability *cap)
195 {
196 	struct nvmm_ioc_capability args;
197 	int ret;
198 
199 	ret = ioctl(nvmm_fd, NVMM_IOC_CAPABILITY, &args);
200 	if (ret == -1)
201 		return -1;
202 
203 	memcpy(cap, &args.cap, sizeof(args.cap));
204 
205 	return 0;
206 }
207 
208 int
209 nvmm_machine_create(struct nvmm_machine *mach)
210 {
211 	struct nvmm_ioc_machine_create args;
212 	struct nvmm_comm_page **pages;
213 	area_list_t *areas;
214 	int ret;
215 
216 	areas = calloc(1, sizeof(*areas));
217 	if (areas == NULL)
218 		return -1;
219 
220 	pages = calloc(__capability.max_vcpus, sizeof(*pages));
221 	if (pages == NULL) {
222 		free(areas);
223 		return -1;
224 	}
225 
226 	ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_CREATE, &args);
227 	if (ret == -1) {
228 		free(areas);
229 		return -1;
230 	}
231 
232 	LIST_INIT(areas);
233 
234 	memset(mach, 0, sizeof(*mach));
235 	mach->machid = args.machid;
236 	mach->pages = pages;
237 	mach->areas = areas;
238 
239 	return 0;
240 }
241 
242 int
243 nvmm_machine_destroy(struct nvmm_machine *mach)
244 {
245 	struct nvmm_ioc_machine_destroy args;
246 	int ret;
247 
248 	args.machid = mach->machid;
249 
250 	ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_DESTROY, &args);
251 	if (ret == -1)
252 		return -1;
253 
254 	__area_remove_all(mach);
255 	free(mach->pages);
256 
257 	return 0;
258 }
259 
260 int
261 nvmm_machine_configure(struct nvmm_machine *mach, uint64_t op, void *conf)
262 {
263 	struct nvmm_ioc_machine_configure args;
264 	int ret;
265 
266 	args.machid = mach->machid;
267 	args.op = op;
268 	args.conf = conf;
269 
270 	ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_CONFIGURE, &args);
271 	if (ret == -1)
272 		return -1;
273 
274 	return 0;
275 }
276 
277 int
278 nvmm_vcpu_create(struct nvmm_machine *mach, nvmm_cpuid_t cpuid,
279     struct nvmm_vcpu *vcpu)
280 {
281 	struct nvmm_ioc_vcpu_create args;
282 	struct nvmm_comm_page *comm;
283 	int ret;
284 
285 	args.machid = mach->machid;
286 	args.cpuid = cpuid;
287 
288 	ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_CREATE, &args);
289 	if (ret == -1)
290 		return -1;
291 
292 	comm = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FILE,
293 	    nvmm_fd, NVMM_COMM_OFF(mach->machid, cpuid));
294 	if (comm == MAP_FAILED)
295 		return -1;
296 
297 	mach->pages[cpuid] = comm;
298 
299 	vcpu->cpuid = cpuid;
300 	vcpu->state = &comm->state;
301 	vcpu->event = &comm->event;
302 	vcpu->exit = malloc(sizeof(*vcpu->exit));
303 
304 	return 0;
305 }
306 
307 int
308 nvmm_vcpu_destroy(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu)
309 {
310 	struct nvmm_ioc_vcpu_destroy args;
311 	struct nvmm_comm_page *comm;
312 	int ret;
313 
314 	args.machid = mach->machid;
315 	args.cpuid = vcpu->cpuid;
316 
317 	ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_DESTROY, &args);
318 	if (ret == -1)
319 		return -1;
320 
321 	comm = mach->pages[vcpu->cpuid];
322 	munmap(comm, PAGE_SIZE);
323 	free(vcpu->exit);
324 
325 	return 0;
326 }
327 
328 int
329 nvmm_vcpu_configure(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu,
330     uint64_t op, void *conf)
331 {
332 	struct nvmm_ioc_vcpu_configure args;
333 	int ret;
334 
335 	switch (op) {
336 	case NVMM_VCPU_CONF_CALLBACKS:
337 		memcpy(&vcpu->cbs, conf, sizeof(vcpu->cbs));
338 		return 0;
339 	}
340 
341 	args.machid = mach->machid;
342 	args.cpuid = vcpu->cpuid;
343 	args.op = op;
344 	args.conf = conf;
345 
346 	ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_CONFIGURE, &args);
347 	if (ret == -1)
348 		return -1;
349 
350 	return 0;
351 }
352 
353 int
354 nvmm_vcpu_setstate(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu,
355     uint64_t flags)
356 {
357 	struct nvmm_comm_page *comm;
358 
359 	comm = mach->pages[vcpu->cpuid];
360 	comm->state_commit |= flags;
361 	comm->state_cached |= flags;
362 
363 	return 0;
364 }
365 
366 int
367 nvmm_vcpu_getstate(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu,
368     uint64_t flags)
369 {
370 	struct nvmm_ioc_vcpu_getstate args;
371 	struct nvmm_comm_page *comm;
372 	int ret;
373 
374 	comm = mach->pages[vcpu->cpuid];
375 
376 	if (__predict_true((flags & ~comm->state_cached) == 0)) {
377 		return 0;
378 	}
379 	comm->state_wanted = flags & ~comm->state_cached;
380 
381 	args.machid = mach->machid;
382 	args.cpuid = vcpu->cpuid;
383 
384 	ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_GETSTATE, &args);
385 	if (ret == -1)
386 		return -1;
387 
388 	return 0;
389 }
390 
391 int
392 nvmm_vcpu_inject(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu)
393 {
394 	struct nvmm_comm_page *comm;
395 
396 	comm = mach->pages[vcpu->cpuid];
397 	comm->event_commit = true;
398 
399 	return 0;
400 }
401 
402 int
403 nvmm_vcpu_run(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu)
404 {
405 	struct nvmm_ioc_vcpu_run args;
406 	int ret;
407 
408 	args.machid = mach->machid;
409 	args.cpuid = vcpu->cpuid;
410 	memset(&args.exit, 0, sizeof(args.exit));
411 
412 	ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_RUN, &args);
413 	if (ret == -1)
414 		return -1;
415 
416 	/* No comm support yet, just copy. */
417 	memcpy(vcpu->exit, &args.exit, sizeof(args.exit));
418 
419 	return 0;
420 }
421 
422 int
423 nvmm_gpa_map(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa,
424     size_t size, int prot)
425 {
426 	struct nvmm_ioc_gpa_map args;
427 	int ret;
428 
429 	ret = __area_add(mach, hva, gpa, size, prot);
430 	if (ret == -1)
431 		return -1;
432 
433 	args.machid = mach->machid;
434 	args.hva = hva;
435 	args.gpa = gpa;
436 	args.size = size;
437 	args.prot = prot;
438 
439 	ret = ioctl(nvmm_fd, NVMM_IOC_GPA_MAP, &args);
440 	if (ret == -1) {
441 		/* Can't recover. */
442 		abort();
443 	}
444 
445 	return 0;
446 }
447 
448 int
449 nvmm_gpa_unmap(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa,
450     size_t size)
451 {
452 	struct nvmm_ioc_gpa_unmap args;
453 	int ret;
454 
455 	ret = __area_delete(mach, hva, gpa, size);
456 	if (ret == -1)
457 		return -1;
458 
459 	args.machid = mach->machid;
460 	args.gpa = gpa;
461 	args.size = size;
462 
463 	ret = ioctl(nvmm_fd, NVMM_IOC_GPA_UNMAP, &args);
464 	if (ret == -1) {
465 		/* Can't recover. */
466 		abort();
467 	}
468 
469 	return 0;
470 }
471 
472 int
473 nvmm_hva_map(struct nvmm_machine *mach, uintptr_t hva, size_t size)
474 {
475 	struct nvmm_ioc_hva_map args;
476 	int ret;
477 
478 	args.machid = mach->machid;
479 	args.hva = hva;
480 	args.size = size;
481 
482 	ret = ioctl(nvmm_fd, NVMM_IOC_HVA_MAP, &args);
483 	if (ret == -1)
484 		return -1;
485 
486 	return 0;
487 }
488 
489 int
490 nvmm_hva_unmap(struct nvmm_machine *mach, uintptr_t hva, size_t size)
491 {
492 	struct nvmm_ioc_hva_unmap args;
493 	int ret;
494 
495 	args.machid = mach->machid;
496 	args.hva = hva;
497 	args.size = size;
498 
499 	ret = ioctl(nvmm_fd, NVMM_IOC_HVA_UNMAP, &args);
500 	if (ret == -1)
501 		return -1;
502 
503 	return 0;
504 }
505 
506 /*
507  * nvmm_gva_to_gpa(): architecture-specific.
508  */
509 
510 int
511 nvmm_gpa_to_hva(struct nvmm_machine *mach, gpaddr_t gpa, uintptr_t *hva,
512     nvmm_prot_t *prot)
513 {
514 	area_list_t *areas = mach->areas;
515 	area_t *ent;
516 
517 	LIST_FOREACH(ent, areas, list) {
518 		if (gpa >= ent->gpa && gpa < ent->gpa + ent->size) {
519 			*hva = ent->hva + (gpa - ent->gpa);
520 			*prot = ent->prot;
521 			return 0;
522 		}
523 	}
524 
525 	errno = ENOENT;
526 	return -1;
527 }
528 
529 /*
530  * nvmm_assist_io(): architecture-specific.
531  */
532 
533 /*
534  * nvmm_assist_mem(): architecture-specific.
535  */
536 
537 int
538 nvmm_ctl(int op, void *data, size_t size)
539 {
540 	struct nvmm_ioc_ctl args;
541 	int ret;
542 
543 	args.op = op;
544 	args.data = data;
545 	args.size = size;
546 
547 	ret = ioctl(nvmm_fd, NVMM_IOC_CTL, &args);
548 	if (ret == -1)
549 		return -1;
550 
551 	return 0;
552 }
553