libnvmm.c revision 1.3 1 /* $NetBSD: libnvmm.c,v 1.3 2018/11/29 19:55:20 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
44 #include "nvmm.h"
45
46 typedef struct __area {
47 LIST_ENTRY(__area) list;
48 gpaddr_t gpa;
49 uintptr_t hva;
50 size_t size;
51 } area_t;
52
53 typedef LIST_HEAD(, __area) area_list_t;
54
55 static int nvmm_fd = -1;
56 static size_t nvmm_page_size = 0;
57
58 /* -------------------------------------------------------------------------- */
59
60 static int
61 __area_unmap(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa,
62 size_t size)
63 {
64 struct nvmm_ioc_gpa_unmap args;
65 int ret;
66
67 args.machid = mach->machid;
68 args.gpa = gpa;
69 args.size = size;
70
71 ret = ioctl(nvmm_fd, NVMM_IOC_GPA_UNMAP, &args);
72 if (ret == -1)
73 return -1;
74
75 ret = munmap((void *)hva, size);
76
77 return ret;
78 }
79
80 static int
81 __area_dig_hole(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa,
82 size_t size)
83 {
84 area_list_t *areas = mach->areas;
85 area_t *ent, *tmp, *nxt;
86 size_t diff;
87
88 LIST_FOREACH_SAFE(ent, areas, list, nxt) {
89 /* Case 1. */
90 if ((gpa < ent->gpa) && (gpa + size > ent->gpa)) {
91 diff = (gpa + size) - ent->gpa;
92 if (__area_unmap(mach, ent->hva, ent->gpa, diff) == -1) {
93 return -1;
94 }
95 ent->gpa += diff;
96 ent->hva += diff;
97 ent->size -= diff;
98 }
99
100 /* Case 2. */
101 if ((gpa >= ent->gpa) && (gpa + size <= ent->gpa + ent->size)) {
102 /* First half. */
103 tmp = malloc(sizeof(*tmp));
104 tmp->gpa = ent->gpa;
105 tmp->hva = ent->hva;
106 tmp->size = (gpa - ent->gpa);
107 LIST_INSERT_BEFORE(ent, tmp, list);
108 /* Second half. */
109 ent->gpa += tmp->size;
110 ent->hva += tmp->size;
111 ent->size -= tmp->size;
112 diff = size;
113 if (__area_unmap(mach, ent->hva, ent->gpa, diff) == -1) {
114 return -1;
115 }
116 ent->gpa += diff;
117 ent->hva += diff;
118 ent->size -= diff;
119 }
120
121 /* Case 3. */
122 if ((gpa < ent->gpa + ent->size) &&
123 (gpa + size > ent->gpa + ent->size)) {
124 diff = (ent->gpa + ent->size) - gpa;
125 if (__area_unmap(mach, hva, gpa, diff) == -1) {
126 return -1;
127 }
128 ent->size -= diff;
129 }
130
131 /* Case 4. */
132 if ((gpa < ent->gpa + ent->size) &&
133 (gpa + size > ent->gpa + ent->size)) {
134 if (__area_unmap(mach, ent->hva, ent->gpa, ent->size) == -1) {
135 return -1;
136 }
137 LIST_REMOVE(ent, list);
138 free(ent);
139 }
140 }
141
142 return 0;
143 }
144
145 static int
146 __area_add(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa, size_t size)
147 {
148 area_list_t *areas = mach->areas;
149 area_t *area;
150 int ret;
151
152 area = malloc(sizeof(*area));
153 if (area == NULL)
154 return -1;
155 area->gpa = gpa;
156 area->hva = hva;
157 area->size = size;
158
159 ret = __area_dig_hole(mach, hva, gpa, size);
160 if (ret == -1) {
161 free(area);
162 return -1;
163 }
164
165 LIST_INSERT_HEAD(areas, area, list);
166 return 0;
167 }
168
169 static void
170 __area_remove_all(struct nvmm_machine *mach)
171 {
172 area_list_t *areas = mach->areas;
173 area_t *ent;
174
175 while ((ent = LIST_FIRST(areas)) != NULL) {
176 LIST_REMOVE(ent, list);
177 free(ent);
178 }
179
180 free(areas);
181 }
182
183 /* -------------------------------------------------------------------------- */
184
185 static int
186 nvmm_init(void)
187 {
188 if (nvmm_fd != -1)
189 return 0;
190 nvmm_fd = open("/dev/nvmm", O_RDWR);
191 if (nvmm_fd == -1)
192 return -1;
193 nvmm_page_size = sysconf(_SC_PAGESIZE);
194 return 0;
195 }
196
197 int
198 nvmm_capability(struct nvmm_capability *cap)
199 {
200 struct nvmm_ioc_capability args;
201 int ret;
202
203 if (nvmm_init() == -1) {
204 return -1;
205 }
206
207 ret = ioctl(nvmm_fd, NVMM_IOC_CAPABILITY, &args);
208 if (ret == -1)
209 return -1;
210
211 memcpy(cap, &args.cap, sizeof(args.cap));
212
213 return 0;
214 }
215
216 int
217 nvmm_machine_create(struct nvmm_machine *mach)
218 {
219 struct nvmm_ioc_machine_create args;
220 area_list_t *areas;
221 int ret;
222
223 if (nvmm_init() == -1) {
224 return -1;
225 }
226
227 areas = calloc(1, sizeof(*areas));
228 if (areas == NULL)
229 return -1;
230
231 ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_CREATE, &args);
232 if (ret == -1) {
233 free(areas);
234 return -1;
235 }
236
237 memset(mach, 0, sizeof(*mach));
238 LIST_INIT(areas);
239 mach->areas = areas;
240 mach->machid = args.machid;
241
242 return 0;
243 }
244
245 int
246 nvmm_machine_destroy(struct nvmm_machine *mach)
247 {
248 struct nvmm_ioc_machine_destroy args;
249 int ret;
250
251 if (nvmm_init() == -1) {
252 return -1;
253 }
254
255 args.machid = mach->machid;
256
257 ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_DESTROY, &args);
258 if (ret == -1)
259 return -1;
260
261 __area_remove_all(mach);
262
263 return 0;
264 }
265
266 int
267 nvmm_machine_configure(struct nvmm_machine *mach, uint64_t op, void *conf)
268 {
269 struct nvmm_ioc_machine_configure args;
270 int ret;
271
272 if (nvmm_init() == -1) {
273 return -1;
274 }
275
276 args.machid = mach->machid;
277 args.op = op;
278 args.conf = conf;
279
280 ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_CONFIGURE, &args);
281 if (ret == -1)
282 return -1;
283
284 return 0;
285 }
286
287 int
288 nvmm_vcpu_create(struct nvmm_machine *mach, nvmm_cpuid_t cpuid)
289 {
290 struct nvmm_ioc_vcpu_create args;
291 int ret;
292
293 if (nvmm_init() == -1) {
294 return -1;
295 }
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 return 0;
305 }
306
307 int
308 nvmm_vcpu_destroy(struct nvmm_machine *mach, nvmm_cpuid_t cpuid)
309 {
310 struct nvmm_ioc_vcpu_destroy args;
311 int ret;
312
313 if (nvmm_init() == -1) {
314 return -1;
315 }
316
317 args.machid = mach->machid;
318 args.cpuid = cpuid;
319
320 ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_DESTROY, &args);
321 if (ret == -1)
322 return -1;
323
324 return 0;
325 }
326
327 int
328 nvmm_vcpu_setstate(struct nvmm_machine *mach, nvmm_cpuid_t cpuid,
329 void *state, uint64_t flags)
330 {
331 struct nvmm_ioc_vcpu_setstate args;
332 int ret;
333
334 if (nvmm_init() == -1) {
335 return -1;
336 }
337
338 args.machid = mach->machid;
339 args.cpuid = cpuid;
340 args.state = state;
341 args.flags = flags;
342
343 ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_SETSTATE, &args);
344 if (ret == -1)
345 return -1;
346
347 return 0;
348 }
349
350 int
351 nvmm_vcpu_getstate(struct nvmm_machine *mach, nvmm_cpuid_t cpuid,
352 void *state, uint64_t flags)
353 {
354 struct nvmm_ioc_vcpu_getstate args;
355 int ret;
356
357 if (nvmm_init() == -1) {
358 return -1;
359 }
360
361 args.machid = mach->machid;
362 args.cpuid = cpuid;
363 args.state = state;
364 args.flags = flags;
365
366 ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_GETSTATE, &args);
367 if (ret == -1)
368 return -1;
369
370 return 0;
371 }
372
373 int
374 nvmm_vcpu_inject(struct nvmm_machine *mach, nvmm_cpuid_t cpuid,
375 struct nvmm_event *event)
376 {
377 struct nvmm_ioc_vcpu_inject args;
378 int ret;
379
380 if (nvmm_init() == -1) {
381 return -1;
382 }
383
384 args.machid = mach->machid;
385 args.cpuid = cpuid;
386 memcpy(&args.event, event, sizeof(args.event));
387
388 ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_INJECT, &args);
389 if (ret == -1)
390 return -1;
391
392 return 0;
393 }
394
395 int
396 nvmm_vcpu_run(struct nvmm_machine *mach, nvmm_cpuid_t cpuid,
397 struct nvmm_exit *exit)
398 {
399 struct nvmm_ioc_vcpu_run args;
400 int ret;
401
402 if (nvmm_init() == -1) {
403 return -1;
404 }
405
406 args.machid = mach->machid;
407 args.cpuid = cpuid;
408 memset(&args.exit, 0, sizeof(args.exit));
409
410 ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_RUN, &args);
411 if (ret == -1)
412 return -1;
413
414 memcpy(exit, &args.exit, sizeof(args.exit));
415
416 return 0;
417 }
418
419 int
420 nvmm_gpa_map(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa,
421 size_t size, int flags)
422 {
423 struct nvmm_ioc_gpa_map args;
424 int ret;
425
426 if (nvmm_init() == -1) {
427 return -1;
428 }
429
430 ret = __area_add(mach, hva, gpa, size);
431 if (ret == -1)
432 return -1;
433
434 args.machid = mach->machid;
435 args.hva = hva;
436 args.gpa = gpa;
437 args.size = size;
438 args.flags = flags;
439
440 ret = ioctl(nvmm_fd, NVMM_IOC_GPA_MAP, &args);
441 if (ret == -1) {
442 /* Can't recover. */
443 abort();
444 }
445
446 return 0;
447 }
448
449 int
450 nvmm_gpa_unmap(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa,
451 size_t size)
452 {
453 if (nvmm_init() == -1) {
454 return -1;
455 }
456
457 return __area_dig_hole(mach, hva, gpa, size);
458 }
459
460 /*
461 * nvmm_gva_to_gpa(): architecture-specific.
462 */
463
464 int
465 nvmm_gpa_to_hva(struct nvmm_machine *mach, gpaddr_t gpa, uintptr_t *hva)
466 {
467 area_list_t *areas = mach->areas;
468 area_t *ent;
469
470 if (gpa % nvmm_page_size != 0) {
471 errno = EINVAL;
472 return -1;
473 }
474
475 LIST_FOREACH(ent, areas, list) {
476 if (gpa < ent->gpa) {
477 continue;
478 }
479 if (gpa >= ent->gpa + ent->size) {
480 continue;
481 }
482
483 *hva = ent->hva + (gpa - ent->gpa);
484 return 0;
485 }
486
487 errno = ENOENT;
488 return -1;
489 }
490
491 /*
492 * nvmm_assist_io(): architecture-specific.
493 */
494