xref: /netbsd-src/lib/libnvmm/libnvmm.c (revision f3cfa6f6ce31685c6c4a758bc430e69eb99f50a4)
1 /*	$NetBSD: libnvmm.c,v 1.13 2019/05/11 07:31:57 maxv Exp $	*/
2 
3 /*
4  * Copyright (c) 2018 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 static int
160 nvmm_init(void)
161 {
162 	if (nvmm_fd != -1)
163 		return 0;
164 	nvmm_fd = open("/dev/nvmm", O_RDWR);
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 	return 0;
173 }
174 
175 int
176 nvmm_capability(struct nvmm_capability *cap)
177 {
178 	struct nvmm_ioc_capability args;
179 	int ret;
180 
181 	if (nvmm_init() == -1) {
182 		return -1;
183 	}
184 
185 	ret = ioctl(nvmm_fd, NVMM_IOC_CAPABILITY, &args);
186 	if (ret == -1)
187 		return -1;
188 
189 	memcpy(cap, &args.cap, sizeof(args.cap));
190 
191 	return 0;
192 }
193 
194 int
195 nvmm_machine_create(struct nvmm_machine *mach)
196 {
197 	struct nvmm_ioc_machine_create args;
198 	struct nvmm_comm_page **pages;
199 	area_list_t *areas;
200 	int ret;
201 
202 	if (nvmm_init() == -1) {
203 		return -1;
204 	}
205 
206 	areas = calloc(1, sizeof(*areas));
207 	if (areas == NULL)
208 		return -1;
209 
210 	pages = calloc(__capability.max_vcpus, sizeof(*pages));
211 	if (pages == NULL) {
212 		free(areas);
213 		return -1;
214 	}
215 
216 	ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_CREATE, &args);
217 	if (ret == -1) {
218 		free(areas);
219 		return -1;
220 	}
221 
222 	LIST_INIT(areas);
223 
224 	memset(mach, 0, sizeof(*mach));
225 	mach->machid = args.machid;
226 	mach->pages = pages;
227 	mach->npages = __capability.max_vcpus;
228 	mach->areas = areas;
229 
230 	return 0;
231 }
232 
233 int
234 nvmm_machine_destroy(struct nvmm_machine *mach)
235 {
236 	struct nvmm_ioc_machine_destroy args;
237 	int ret;
238 
239 	args.machid = mach->machid;
240 
241 	ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_DESTROY, &args);
242 	if (ret == -1)
243 		return -1;
244 
245 	__area_remove_all(mach);
246 	free(mach->pages);
247 
248 	return 0;
249 }
250 
251 int
252 nvmm_machine_configure(struct nvmm_machine *mach, uint64_t op, void *conf)
253 {
254 	struct nvmm_ioc_machine_configure args;
255 	int ret;
256 
257 	switch (op) {
258 	case NVMM_MACH_CONF_CALLBACKS:
259 		memcpy(&mach->cbs, conf, sizeof(mach->cbs));
260 		return 0;
261 	}
262 
263 	args.machid = mach->machid;
264 	args.op = op;
265 	args.conf = conf;
266 
267 	ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_CONFIGURE, &args);
268 	if (ret == -1)
269 		return -1;
270 
271 	return 0;
272 }
273 
274 int
275 nvmm_vcpu_create(struct nvmm_machine *mach, nvmm_cpuid_t cpuid)
276 {
277 	struct nvmm_ioc_vcpu_create args;
278 	struct nvmm_comm_page *comm;
279 	int ret;
280 
281 	args.machid = mach->machid;
282 	args.cpuid = cpuid;
283 
284 	ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_CREATE, &args);
285 	if (ret == -1)
286 		return -1;
287 
288 	comm = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FILE,
289 	    nvmm_fd, NVMM_COMM_OFF(mach->machid, cpuid));
290 	if (comm == MAP_FAILED)
291 		return -1;
292 
293 	mach->pages[cpuid] = comm;
294 
295 	return 0;
296 }
297 
298 int
299 nvmm_vcpu_destroy(struct nvmm_machine *mach, nvmm_cpuid_t cpuid)
300 {
301 	struct nvmm_ioc_vcpu_destroy args;
302 	struct nvmm_comm_page *comm;
303 	int ret;
304 
305 	args.machid = mach->machid;
306 	args.cpuid = cpuid;
307 
308 	ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_DESTROY, &args);
309 	if (ret == -1)
310 		return -1;
311 
312 	comm = mach->pages[cpuid];
313 	munmap(comm, PAGE_SIZE);
314 
315 	return 0;
316 }
317 
318 int
319 nvmm_vcpu_setstate(struct nvmm_machine *mach, nvmm_cpuid_t cpuid,
320     void *state, uint64_t flags)
321 {
322 	struct nvmm_comm_page *comm;
323 
324 	if (__predict_false(cpuid >= mach->npages)) {
325 		return -1;
326 	}
327 	comm = mach->pages[cpuid];
328 
329 	nvmm_arch_copystate(&comm->state, state, flags);
330 	comm->state_commit |= flags;
331 	comm->state_cached |= flags;
332 
333 	return 0;
334 }
335 
336 int
337 nvmm_vcpu_getstate(struct nvmm_machine *mach, nvmm_cpuid_t cpuid,
338     void *state, uint64_t flags)
339 {
340 	struct nvmm_ioc_vcpu_getstate args;
341 	struct nvmm_comm_page *comm;
342 	int ret;
343 
344 	if (__predict_false(cpuid >= mach->npages)) {
345 		return -1;
346 	}
347 	comm = mach->pages[cpuid];
348 
349 	if (__predict_true((flags & ~comm->state_cached) == 0)) {
350 		goto out;
351 	}
352 	comm->state_wanted = flags & ~comm->state_cached;
353 
354 	args.machid = mach->machid;
355 	args.cpuid = cpuid;
356 
357 	ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_GETSTATE, &args);
358 	if (ret == -1)
359 		return -1;
360 
361 out:
362 	nvmm_arch_copystate(state, &comm->state, flags);
363 	return 0;
364 }
365 
366 int
367 nvmm_vcpu_inject(struct nvmm_machine *mach, nvmm_cpuid_t cpuid,
368     struct nvmm_event *event)
369 {
370 	struct nvmm_comm_page *comm;
371 
372 	if (__predict_false(cpuid >= mach->npages)) {
373 		return -1;
374 	}
375 	comm = mach->pages[cpuid];
376 
377 	memcpy(&comm->event, event, sizeof(comm->event));
378 	comm->event_commit = true;
379 
380 	return 0;
381 }
382 
383 int
384 nvmm_vcpu_run(struct nvmm_machine *mach, nvmm_cpuid_t cpuid,
385     struct nvmm_exit *exit)
386 {
387 	struct nvmm_ioc_vcpu_run args;
388 	int ret;
389 
390 	args.machid = mach->machid;
391 	args.cpuid = cpuid;
392 	memset(&args.exit, 0, sizeof(args.exit));
393 
394 	ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_RUN, &args);
395 	if (ret == -1)
396 		return -1;
397 
398 	memcpy(exit, &args.exit, sizeof(args.exit));
399 
400 	return 0;
401 }
402 
403 int
404 nvmm_gpa_map(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa,
405     size_t size, int prot)
406 {
407 	struct nvmm_ioc_gpa_map args;
408 	int ret;
409 
410 	ret = __area_add(mach, hva, gpa, size, prot);
411 	if (ret == -1)
412 		return -1;
413 
414 	args.machid = mach->machid;
415 	args.hva = hva;
416 	args.gpa = gpa;
417 	args.size = size;
418 	args.prot = prot;
419 
420 	ret = ioctl(nvmm_fd, NVMM_IOC_GPA_MAP, &args);
421 	if (ret == -1) {
422 		/* Can't recover. */
423 		abort();
424 	}
425 
426 	return 0;
427 }
428 
429 int
430 nvmm_gpa_unmap(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa,
431     size_t size)
432 {
433 	struct nvmm_ioc_gpa_unmap args;
434 	int ret;
435 
436 	ret = __area_delete(mach, hva, gpa, size);
437 	if (ret == -1)
438 		return -1;
439 
440 	args.machid = mach->machid;
441 	args.gpa = gpa;
442 	args.size = size;
443 
444 	ret = ioctl(nvmm_fd, NVMM_IOC_GPA_UNMAP, &args);
445 	if (ret == -1) {
446 		/* Can't recover. */
447 		abort();
448 	}
449 
450 	return 0;
451 }
452 
453 int
454 nvmm_hva_map(struct nvmm_machine *mach, uintptr_t hva, size_t size)
455 {
456 	struct nvmm_ioc_hva_map args;
457 	int ret;
458 
459 	args.machid = mach->machid;
460 	args.hva = hva;
461 	args.size = size;
462 
463 	ret = ioctl(nvmm_fd, NVMM_IOC_HVA_MAP, &args);
464 	if (ret == -1)
465 		return -1;
466 
467 	return 0;
468 }
469 
470 int
471 nvmm_hva_unmap(struct nvmm_machine *mach, uintptr_t hva, size_t size)
472 {
473 	struct nvmm_ioc_hva_unmap args;
474 	int ret;
475 
476 	args.machid = mach->machid;
477 	args.hva = hva;
478 	args.size = size;
479 
480 	ret = ioctl(nvmm_fd, NVMM_IOC_HVA_UNMAP, &args);
481 	if (ret == -1)
482 		return -1;
483 
484 	return 0;
485 }
486 
487 /*
488  * nvmm_gva_to_gpa(): architecture-specific.
489  */
490 
491 int
492 nvmm_gpa_to_hva(struct nvmm_machine *mach, gpaddr_t gpa, uintptr_t *hva,
493     nvmm_prot_t *prot)
494 {
495 	area_list_t *areas = mach->areas;
496 	area_t *ent;
497 
498 	LIST_FOREACH(ent, areas, list) {
499 		if (gpa >= ent->gpa && gpa < ent->gpa + ent->size) {
500 			*hva = ent->hva + (gpa - ent->gpa);
501 			*prot = ent->prot;
502 			return 0;
503 		}
504 	}
505 
506 	errno = ENOENT;
507 	return -1;
508 }
509 
510 /*
511  * nvmm_assist_io(): architecture-specific.
512  */
513 
514 /*
515  * nvmm_assist_mem(): architecture-specific.
516  */
517 
518 int
519 nvmm_ctl(int op, void *data, size_t size)
520 {
521 	struct nvmm_ioc_ctl args;
522 	int ret;
523 
524 	if (nvmm_init() == -1) {
525 		return -1;
526 	}
527 
528 	args.op = op;
529 	args.data = data;
530 	args.size = size;
531 
532 	ret = ioctl(nvmm_fd, NVMM_IOC_CTL, &args);
533 	if (ret == -1)
534 		return -1;
535 
536 	return 0;
537 }
538