libnvmm.c revision 1.13 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