xref: /dragonfly/lib/libnvmm/libnvmm.c (revision 634ba020)
1 /*	$NetBSD: libnvmm.c,v 1.14.4.1 2019/11/10 12:58:30 martin Exp $	*/
2 
3 /*
4  * Copyright (c) 2018-2019 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Maxime Villard.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <fcntl.h>
39 #include <errno.h>
40 #include <sys/ioctl.h>
41 #include <sys/mman.h>
42 #include <sys/queue.h>
43 #include <machine/vmparam.h>
44 
45 #include "nvmm.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, 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_RDONLY | 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 (nvmm_fd != -1)
186 		return 0;
187 	nvmm_fd = open("/dev/nvmm", O_WRONLY | O_CLOEXEC);
188 	if (nvmm_fd == -1)
189 		return -1;
190 	if (nvmm_capability(&__capability) == -1) {
191 		close(nvmm_fd);
192 		nvmm_fd = -1;
193 		return -1;
194 	}
195 	if (__capability.version != NVMM_KERN_VERSION) {
196 		close(nvmm_fd);
197 		nvmm_fd = -1;
198 		errno = EPROGMISMATCH;
199 		return -1;
200 	}
201 
202 	return 0;
203 }
204 
205 int
206 nvmm_capability(struct nvmm_capability *cap)
207 {
208 	struct nvmm_ioc_capability args;
209 	int ret;
210 
211 	ret = ioctl(nvmm_fd, NVMM_IOC_CAPABILITY, &args);
212 	if (ret == -1)
213 		return -1;
214 
215 	memcpy(cap, &args.cap, sizeof(args.cap));
216 
217 	return 0;
218 }
219 
220 int
221 nvmm_machine_create(struct nvmm_machine *mach)
222 {
223 	struct nvmm_ioc_machine_create args;
224 	struct nvmm_comm_page **pages;
225 	area_list_t *areas;
226 	int ret;
227 
228 	areas = calloc(1, sizeof(*areas));
229 	if (areas == NULL)
230 		return -1;
231 
232 	pages = calloc(__capability.max_vcpus, sizeof(*pages));
233 	if (pages == NULL) {
234 		free(areas);
235 		return -1;
236 	}
237 
238 	ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_CREATE, &args);
239 	if (ret == -1) {
240 		free(areas);
241 		return -1;
242 	}
243 
244 	LIST_INIT(areas);
245 
246 	memset(mach, 0, sizeof(*mach));
247 	mach->machid = args.machid;
248 	mach->pages = pages;
249 	mach->areas = areas;
250 
251 	return 0;
252 }
253 
254 int
255 nvmm_machine_destroy(struct nvmm_machine *mach)
256 {
257 	struct nvmm_ioc_machine_destroy args;
258 	int ret;
259 
260 	args.machid = mach->machid;
261 
262 	ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_DESTROY, &args);
263 	if (ret == -1)
264 		return -1;
265 
266 	__area_remove_all(mach);
267 	free(mach->pages);
268 
269 	return 0;
270 }
271 
272 int
273 nvmm_machine_configure(struct nvmm_machine *mach, uint64_t op, void *conf)
274 {
275 	struct nvmm_ioc_machine_configure args;
276 	int ret;
277 
278 	args.machid = mach->machid;
279 	args.op = op;
280 	args.conf = conf;
281 
282 	ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_CONFIGURE, &args);
283 	if (ret == -1)
284 		return -1;
285 
286 	return 0;
287 }
288 
289 int
290 nvmm_vcpu_create(struct nvmm_machine *mach, nvmm_cpuid_t cpuid,
291     struct nvmm_vcpu *vcpu)
292 {
293 	struct nvmm_ioc_vcpu_create args;
294 	struct nvmm_comm_page *comm;
295 	int ret;
296 
297 	args.machid = mach->machid;
298 	args.cpuid = cpuid;
299 
300 	ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_CREATE, &args);
301 	if (ret == -1)
302 		return -1;
303 
304 	comm = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FILE,
305 	    nvmm_fd, NVMM_COMM_OFF(mach->machid, cpuid));
306 	if (comm == MAP_FAILED)
307 		return -1;
308 
309 	mach->pages[cpuid] = comm;
310 
311 	vcpu->cpuid = cpuid;
312 	vcpu->state = &comm->state;
313 	vcpu->event = &comm->event;
314 	vcpu->exit = malloc(sizeof(*vcpu->exit));
315 
316 	return 0;
317 }
318 
319 int
320 nvmm_vcpu_destroy(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu)
321 {
322 	struct nvmm_ioc_vcpu_destroy args;
323 	struct nvmm_comm_page *comm;
324 	int ret;
325 
326 	args.machid = mach->machid;
327 	args.cpuid = vcpu->cpuid;
328 
329 	ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_DESTROY, &args);
330 	if (ret == -1)
331 		return -1;
332 
333 	comm = mach->pages[vcpu->cpuid];
334 	munmap(comm, PAGE_SIZE);
335 	free(vcpu->exit);
336 
337 	return 0;
338 }
339 
340 int
341 nvmm_vcpu_configure(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu,
342     uint64_t op, void *conf)
343 {
344 	struct nvmm_ioc_vcpu_configure args;
345 	int ret;
346 
347 	switch (op) {
348 	case NVMM_VCPU_CONF_CALLBACKS:
349 		memcpy(&vcpu->cbs, conf, sizeof(vcpu->cbs));
350 		return 0;
351 	}
352 
353 	args.machid = mach->machid;
354 	args.cpuid = vcpu->cpuid;
355 	args.op = op;
356 	args.conf = conf;
357 
358 	ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_CONFIGURE, &args);
359 	if (ret == -1)
360 		return -1;
361 
362 	return 0;
363 }
364 
365 int
366 nvmm_vcpu_setstate(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu,
367     uint64_t flags)
368 {
369 	struct nvmm_comm_page *comm;
370 
371 	comm = mach->pages[vcpu->cpuid];
372 	comm->state_commit |= flags;
373 	comm->state_cached |= flags;
374 
375 	return 0;
376 }
377 
378 int
379 nvmm_vcpu_getstate(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu,
380     uint64_t flags)
381 {
382 	struct nvmm_ioc_vcpu_getstate args;
383 	struct nvmm_comm_page *comm;
384 	int ret;
385 
386 	comm = mach->pages[vcpu->cpuid];
387 
388 	if (__predict_true((flags & ~comm->state_cached) == 0)) {
389 		return 0;
390 	}
391 	comm->state_wanted = flags & ~comm->state_cached;
392 
393 	args.machid = mach->machid;
394 	args.cpuid = vcpu->cpuid;
395 
396 	ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_GETSTATE, &args);
397 	if (ret == -1)
398 		return -1;
399 
400 	return 0;
401 }
402 
403 int
404 nvmm_vcpu_inject(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu)
405 {
406 	struct nvmm_comm_page *comm;
407 
408 	comm = mach->pages[vcpu->cpuid];
409 	comm->event_commit = true;
410 
411 	return 0;
412 }
413 
414 int
415 nvmm_vcpu_run(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu)
416 {
417 	struct nvmm_ioc_vcpu_run args;
418 	int ret;
419 
420 	args.machid = mach->machid;
421 	args.cpuid = vcpu->cpuid;
422 	memset(&args.exit, 0, sizeof(args.exit));
423 
424 	ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_RUN, &args);
425 	if (ret == -1)
426 		return -1;
427 
428 	/* No comm support yet, just copy. */
429 	memcpy(vcpu->exit, &args.exit, sizeof(args.exit));
430 
431 	return 0;
432 }
433 
434 int
435 nvmm_gpa_map(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa,
436     size_t size, int prot)
437 {
438 	struct nvmm_ioc_gpa_map args;
439 	int ret;
440 
441 	ret = __area_add(mach, hva, gpa, size, prot);
442 	if (ret == -1)
443 		return -1;
444 
445 	args.machid = mach->machid;
446 	args.hva = hva;
447 	args.gpa = gpa;
448 	args.size = size;
449 	args.prot = prot;
450 
451 	ret = ioctl(nvmm_fd, NVMM_IOC_GPA_MAP, &args);
452 	if (ret == -1) {
453 		/* Can't recover. */
454 		abort();
455 	}
456 
457 	return 0;
458 }
459 
460 int
461 nvmm_gpa_unmap(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa,
462     size_t size)
463 {
464 	struct nvmm_ioc_gpa_unmap args;
465 	int ret;
466 
467 	ret = __area_delete(mach, hva, gpa, size);
468 	if (ret == -1)
469 		return -1;
470 
471 	args.machid = mach->machid;
472 	args.gpa = gpa;
473 	args.size = size;
474 
475 	ret = ioctl(nvmm_fd, NVMM_IOC_GPA_UNMAP, &args);
476 	if (ret == -1) {
477 		/* Can't recover. */
478 		abort();
479 	}
480 
481 	return 0;
482 }
483 
484 int
485 nvmm_hva_map(struct nvmm_machine *mach, uintptr_t hva, size_t size)
486 {
487 	struct nvmm_ioc_hva_map args;
488 	int ret;
489 
490 	args.machid = mach->machid;
491 	args.hva = hva;
492 	args.size = size;
493 
494 	ret = ioctl(nvmm_fd, NVMM_IOC_HVA_MAP, &args);
495 	if (ret == -1)
496 		return -1;
497 
498 	return 0;
499 }
500 
501 int
502 nvmm_hva_unmap(struct nvmm_machine *mach, uintptr_t hva, size_t size)
503 {
504 	struct nvmm_ioc_hva_unmap args;
505 	int ret;
506 
507 	args.machid = mach->machid;
508 	args.hva = hva;
509 	args.size = size;
510 
511 	ret = ioctl(nvmm_fd, NVMM_IOC_HVA_UNMAP, &args);
512 	if (ret == -1)
513 		return -1;
514 
515 	return 0;
516 }
517 
518 /*
519  * nvmm_gva_to_gpa(): architecture-specific.
520  */
521 
522 int
523 nvmm_gpa_to_hva(struct nvmm_machine *mach, gpaddr_t gpa, uintptr_t *hva,
524     nvmm_prot_t *prot)
525 {
526 	area_list_t *areas = mach->areas;
527 	area_t *ent;
528 
529 	LIST_FOREACH(ent, areas, list) {
530 		if (gpa >= ent->gpa && gpa < ent->gpa + ent->size) {
531 			*hva = ent->hva + (gpa - ent->gpa);
532 			*prot = ent->prot;
533 			return 0;
534 		}
535 	}
536 
537 	errno = ENOENT;
538 	return -1;
539 }
540 
541 /*
542  * nvmm_assist_io(): architecture-specific.
543  */
544 
545 /*
546  * nvmm_assist_mem(): architecture-specific.
547  */
548 
549 int
550 nvmm_ctl(int op, void *data, size_t size)
551 {
552 	struct nvmm_ioc_ctl args;
553 	int ret;
554 
555 	args.op = op;
556 	args.data = data;
557 	args.size = size;
558 
559 	ret = ioctl(nvmm_fd, NVMM_IOC_CTL, &args);
560 	if (ret == -1)
561 		return -1;
562 
563 	return 0;
564 }
565