libnvmm.c revision 1.3 1 1.3 maxv /* $NetBSD: libnvmm.c,v 1.3 2018/11/29 19:55:20 maxv Exp $ */
2 1.1 maxv
3 1.1 maxv /*
4 1.1 maxv * Copyright (c) 2018 The NetBSD Foundation, Inc.
5 1.1 maxv * All rights reserved.
6 1.1 maxv *
7 1.1 maxv * This code is derived from software contributed to The NetBSD Foundation
8 1.1 maxv * by Maxime Villard.
9 1.1 maxv *
10 1.1 maxv * Redistribution and use in source and binary forms, with or without
11 1.1 maxv * modification, are permitted provided that the following conditions
12 1.1 maxv * are met:
13 1.1 maxv * 1. Redistributions of source code must retain the above copyright
14 1.1 maxv * notice, this list of conditions and the following disclaimer.
15 1.1 maxv * 2. Redistributions in binary form must reproduce the above copyright
16 1.1 maxv * notice, this list of conditions and the following disclaimer in the
17 1.1 maxv * documentation and/or other materials provided with the distribution.
18 1.1 maxv *
19 1.1 maxv * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 1.1 maxv * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 1.1 maxv * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 1.1 maxv * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 1.1 maxv * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 1.1 maxv * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 1.1 maxv * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 1.1 maxv * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 1.1 maxv * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 1.1 maxv * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 1.1 maxv * POSSIBILITY OF SUCH DAMAGE.
30 1.1 maxv */
31 1.1 maxv
32 1.1 maxv #include <sys/cdefs.h>
33 1.1 maxv
34 1.1 maxv #include <stdio.h>
35 1.1 maxv #include <stdlib.h>
36 1.1 maxv #include <string.h>
37 1.1 maxv #include <unistd.h>
38 1.1 maxv #include <fcntl.h>
39 1.1 maxv #include <errno.h>
40 1.1 maxv #include <sys/ioctl.h>
41 1.1 maxv #include <sys/mman.h>
42 1.3 maxv #include <sys/queue.h>
43 1.1 maxv
44 1.1 maxv #include "nvmm.h"
45 1.1 maxv
46 1.3 maxv typedef struct __area {
47 1.3 maxv LIST_ENTRY(__area) list;
48 1.3 maxv gpaddr_t gpa;
49 1.3 maxv uintptr_t hva;
50 1.3 maxv size_t size;
51 1.3 maxv } area_t;
52 1.3 maxv
53 1.3 maxv typedef LIST_HEAD(, __area) area_list_t;
54 1.3 maxv
55 1.1 maxv static int nvmm_fd = -1;
56 1.1 maxv static size_t nvmm_page_size = 0;
57 1.1 maxv
58 1.1 maxv /* -------------------------------------------------------------------------- */
59 1.1 maxv
60 1.1 maxv static int
61 1.3 maxv __area_unmap(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa,
62 1.3 maxv size_t size)
63 1.3 maxv {
64 1.3 maxv struct nvmm_ioc_gpa_unmap args;
65 1.3 maxv int ret;
66 1.3 maxv
67 1.3 maxv args.machid = mach->machid;
68 1.3 maxv args.gpa = gpa;
69 1.3 maxv args.size = size;
70 1.3 maxv
71 1.3 maxv ret = ioctl(nvmm_fd, NVMM_IOC_GPA_UNMAP, &args);
72 1.3 maxv if (ret == -1)
73 1.3 maxv return -1;
74 1.3 maxv
75 1.3 maxv ret = munmap((void *)hva, size);
76 1.3 maxv
77 1.3 maxv return ret;
78 1.3 maxv }
79 1.3 maxv
80 1.3 maxv static int
81 1.3 maxv __area_dig_hole(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa,
82 1.1 maxv size_t size)
83 1.1 maxv {
84 1.3 maxv area_list_t *areas = mach->areas;
85 1.3 maxv area_t *ent, *tmp, *nxt;
86 1.3 maxv size_t diff;
87 1.3 maxv
88 1.3 maxv LIST_FOREACH_SAFE(ent, areas, list, nxt) {
89 1.3 maxv /* Case 1. */
90 1.3 maxv if ((gpa < ent->gpa) && (gpa + size > ent->gpa)) {
91 1.3 maxv diff = (gpa + size) - ent->gpa;
92 1.3 maxv if (__area_unmap(mach, ent->hva, ent->gpa, diff) == -1) {
93 1.3 maxv return -1;
94 1.3 maxv }
95 1.3 maxv ent->gpa += diff;
96 1.3 maxv ent->hva += diff;
97 1.3 maxv ent->size -= diff;
98 1.1 maxv }
99 1.3 maxv
100 1.3 maxv /* Case 2. */
101 1.3 maxv if ((gpa >= ent->gpa) && (gpa + size <= ent->gpa + ent->size)) {
102 1.3 maxv /* First half. */
103 1.3 maxv tmp = malloc(sizeof(*tmp));
104 1.3 maxv tmp->gpa = ent->gpa;
105 1.3 maxv tmp->hva = ent->hva;
106 1.3 maxv tmp->size = (gpa - ent->gpa);
107 1.3 maxv LIST_INSERT_BEFORE(ent, tmp, list);
108 1.3 maxv /* Second half. */
109 1.3 maxv ent->gpa += tmp->size;
110 1.3 maxv ent->hva += tmp->size;
111 1.3 maxv ent->size -= tmp->size;
112 1.3 maxv diff = size;
113 1.3 maxv if (__area_unmap(mach, ent->hva, ent->gpa, diff) == -1) {
114 1.3 maxv return -1;
115 1.3 maxv }
116 1.3 maxv ent->gpa += diff;
117 1.3 maxv ent->hva += diff;
118 1.3 maxv ent->size -= diff;
119 1.1 maxv }
120 1.3 maxv
121 1.3 maxv /* Case 3. */
122 1.3 maxv if ((gpa < ent->gpa + ent->size) &&
123 1.3 maxv (gpa + size > ent->gpa + ent->size)) {
124 1.3 maxv diff = (ent->gpa + ent->size) - gpa;
125 1.3 maxv if (__area_unmap(mach, hva, gpa, diff) == -1) {
126 1.3 maxv return -1;
127 1.3 maxv }
128 1.3 maxv ent->size -= diff;
129 1.3 maxv }
130 1.3 maxv
131 1.3 maxv /* Case 4. */
132 1.3 maxv if ((gpa < ent->gpa + ent->size) &&
133 1.3 maxv (gpa + size > ent->gpa + ent->size)) {
134 1.3 maxv if (__area_unmap(mach, ent->hva, ent->gpa, ent->size) == -1) {
135 1.3 maxv return -1;
136 1.3 maxv }
137 1.3 maxv LIST_REMOVE(ent, list);
138 1.3 maxv free(ent);
139 1.1 maxv }
140 1.1 maxv }
141 1.1 maxv
142 1.3 maxv return 0;
143 1.3 maxv }
144 1.3 maxv
145 1.3 maxv static int
146 1.3 maxv __area_add(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa, size_t size)
147 1.3 maxv {
148 1.3 maxv area_list_t *areas = mach->areas;
149 1.3 maxv area_t *area;
150 1.3 maxv int ret;
151 1.3 maxv
152 1.3 maxv area = malloc(sizeof(*area));
153 1.3 maxv if (area == NULL)
154 1.1 maxv return -1;
155 1.1 maxv area->gpa = gpa;
156 1.1 maxv area->hva = hva;
157 1.1 maxv area->size = size;
158 1.1 maxv
159 1.3 maxv ret = __area_dig_hole(mach, hva, gpa, size);
160 1.3 maxv if (ret == -1) {
161 1.3 maxv free(area);
162 1.3 maxv return -1;
163 1.3 maxv }
164 1.3 maxv
165 1.3 maxv LIST_INSERT_HEAD(areas, area, list);
166 1.1 maxv return 0;
167 1.1 maxv }
168 1.1 maxv
169 1.3 maxv static void
170 1.3 maxv __area_remove_all(struct nvmm_machine *mach)
171 1.1 maxv {
172 1.3 maxv area_list_t *areas = mach->areas;
173 1.3 maxv area_t *ent;
174 1.1 maxv
175 1.3 maxv while ((ent = LIST_FIRST(areas)) != NULL) {
176 1.3 maxv LIST_REMOVE(ent, list);
177 1.3 maxv free(ent);
178 1.1 maxv }
179 1.1 maxv
180 1.3 maxv free(areas);
181 1.1 maxv }
182 1.1 maxv
183 1.1 maxv /* -------------------------------------------------------------------------- */
184 1.1 maxv
185 1.1 maxv static int
186 1.1 maxv nvmm_init(void)
187 1.1 maxv {
188 1.1 maxv if (nvmm_fd != -1)
189 1.1 maxv return 0;
190 1.1 maxv nvmm_fd = open("/dev/nvmm", O_RDWR);
191 1.1 maxv if (nvmm_fd == -1)
192 1.1 maxv return -1;
193 1.1 maxv nvmm_page_size = sysconf(_SC_PAGESIZE);
194 1.1 maxv return 0;
195 1.1 maxv }
196 1.1 maxv
197 1.1 maxv int
198 1.1 maxv nvmm_capability(struct nvmm_capability *cap)
199 1.1 maxv {
200 1.1 maxv struct nvmm_ioc_capability args;
201 1.1 maxv int ret;
202 1.1 maxv
203 1.1 maxv if (nvmm_init() == -1) {
204 1.1 maxv return -1;
205 1.1 maxv }
206 1.1 maxv
207 1.1 maxv ret = ioctl(nvmm_fd, NVMM_IOC_CAPABILITY, &args);
208 1.1 maxv if (ret == -1)
209 1.1 maxv return -1;
210 1.1 maxv
211 1.1 maxv memcpy(cap, &args.cap, sizeof(args.cap));
212 1.1 maxv
213 1.1 maxv return 0;
214 1.1 maxv }
215 1.1 maxv
216 1.1 maxv int
217 1.1 maxv nvmm_machine_create(struct nvmm_machine *mach)
218 1.1 maxv {
219 1.1 maxv struct nvmm_ioc_machine_create args;
220 1.3 maxv area_list_t *areas;
221 1.1 maxv int ret;
222 1.1 maxv
223 1.1 maxv if (nvmm_init() == -1) {
224 1.1 maxv return -1;
225 1.1 maxv }
226 1.1 maxv
227 1.3 maxv areas = calloc(1, sizeof(*areas));
228 1.3 maxv if (areas == NULL)
229 1.3 maxv return -1;
230 1.3 maxv
231 1.1 maxv ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_CREATE, &args);
232 1.3 maxv if (ret == -1) {
233 1.3 maxv free(areas);
234 1.1 maxv return -1;
235 1.3 maxv }
236 1.1 maxv
237 1.1 maxv memset(mach, 0, sizeof(*mach));
238 1.3 maxv LIST_INIT(areas);
239 1.3 maxv mach->areas = areas;
240 1.1 maxv mach->machid = args.machid;
241 1.1 maxv
242 1.1 maxv return 0;
243 1.1 maxv }
244 1.1 maxv
245 1.1 maxv int
246 1.1 maxv nvmm_machine_destroy(struct nvmm_machine *mach)
247 1.1 maxv {
248 1.1 maxv struct nvmm_ioc_machine_destroy args;
249 1.1 maxv int ret;
250 1.1 maxv
251 1.1 maxv if (nvmm_init() == -1) {
252 1.1 maxv return -1;
253 1.1 maxv }
254 1.1 maxv
255 1.1 maxv args.machid = mach->machid;
256 1.1 maxv
257 1.1 maxv ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_DESTROY, &args);
258 1.1 maxv if (ret == -1)
259 1.1 maxv return -1;
260 1.1 maxv
261 1.3 maxv __area_remove_all(mach);
262 1.1 maxv
263 1.1 maxv return 0;
264 1.1 maxv }
265 1.1 maxv
266 1.1 maxv int
267 1.1 maxv nvmm_machine_configure(struct nvmm_machine *mach, uint64_t op, void *conf)
268 1.1 maxv {
269 1.1 maxv struct nvmm_ioc_machine_configure args;
270 1.1 maxv int ret;
271 1.1 maxv
272 1.1 maxv if (nvmm_init() == -1) {
273 1.1 maxv return -1;
274 1.1 maxv }
275 1.1 maxv
276 1.1 maxv args.machid = mach->machid;
277 1.1 maxv args.op = op;
278 1.1 maxv args.conf = conf;
279 1.1 maxv
280 1.1 maxv ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_CONFIGURE, &args);
281 1.1 maxv if (ret == -1)
282 1.1 maxv return -1;
283 1.1 maxv
284 1.1 maxv return 0;
285 1.1 maxv }
286 1.1 maxv
287 1.1 maxv int
288 1.1 maxv nvmm_vcpu_create(struct nvmm_machine *mach, nvmm_cpuid_t cpuid)
289 1.1 maxv {
290 1.1 maxv struct nvmm_ioc_vcpu_create args;
291 1.1 maxv int ret;
292 1.1 maxv
293 1.1 maxv if (nvmm_init() == -1) {
294 1.1 maxv return -1;
295 1.1 maxv }
296 1.1 maxv
297 1.1 maxv args.machid = mach->machid;
298 1.1 maxv args.cpuid = cpuid;
299 1.1 maxv
300 1.1 maxv ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_CREATE, &args);
301 1.1 maxv if (ret == -1)
302 1.1 maxv return -1;
303 1.1 maxv
304 1.1 maxv return 0;
305 1.1 maxv }
306 1.1 maxv
307 1.1 maxv int
308 1.1 maxv nvmm_vcpu_destroy(struct nvmm_machine *mach, nvmm_cpuid_t cpuid)
309 1.1 maxv {
310 1.1 maxv struct nvmm_ioc_vcpu_destroy args;
311 1.1 maxv int ret;
312 1.1 maxv
313 1.1 maxv if (nvmm_init() == -1) {
314 1.1 maxv return -1;
315 1.1 maxv }
316 1.1 maxv
317 1.1 maxv args.machid = mach->machid;
318 1.1 maxv args.cpuid = cpuid;
319 1.1 maxv
320 1.1 maxv ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_DESTROY, &args);
321 1.1 maxv if (ret == -1)
322 1.1 maxv return -1;
323 1.1 maxv
324 1.1 maxv return 0;
325 1.1 maxv }
326 1.1 maxv
327 1.1 maxv int
328 1.1 maxv nvmm_vcpu_setstate(struct nvmm_machine *mach, nvmm_cpuid_t cpuid,
329 1.1 maxv void *state, uint64_t flags)
330 1.1 maxv {
331 1.1 maxv struct nvmm_ioc_vcpu_setstate args;
332 1.1 maxv int ret;
333 1.1 maxv
334 1.1 maxv if (nvmm_init() == -1) {
335 1.1 maxv return -1;
336 1.1 maxv }
337 1.1 maxv
338 1.1 maxv args.machid = mach->machid;
339 1.1 maxv args.cpuid = cpuid;
340 1.1 maxv args.state = state;
341 1.1 maxv args.flags = flags;
342 1.1 maxv
343 1.1 maxv ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_SETSTATE, &args);
344 1.1 maxv if (ret == -1)
345 1.1 maxv return -1;
346 1.1 maxv
347 1.1 maxv return 0;
348 1.1 maxv }
349 1.1 maxv
350 1.1 maxv int
351 1.1 maxv nvmm_vcpu_getstate(struct nvmm_machine *mach, nvmm_cpuid_t cpuid,
352 1.1 maxv void *state, uint64_t flags)
353 1.1 maxv {
354 1.1 maxv struct nvmm_ioc_vcpu_getstate args;
355 1.1 maxv int ret;
356 1.1 maxv
357 1.1 maxv if (nvmm_init() == -1) {
358 1.1 maxv return -1;
359 1.1 maxv }
360 1.1 maxv
361 1.1 maxv args.machid = mach->machid;
362 1.1 maxv args.cpuid = cpuid;
363 1.1 maxv args.state = state;
364 1.1 maxv args.flags = flags;
365 1.1 maxv
366 1.1 maxv ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_GETSTATE, &args);
367 1.1 maxv if (ret == -1)
368 1.1 maxv return -1;
369 1.1 maxv
370 1.1 maxv return 0;
371 1.1 maxv }
372 1.1 maxv
373 1.1 maxv int
374 1.1 maxv nvmm_vcpu_inject(struct nvmm_machine *mach, nvmm_cpuid_t cpuid,
375 1.1 maxv struct nvmm_event *event)
376 1.1 maxv {
377 1.1 maxv struct nvmm_ioc_vcpu_inject args;
378 1.1 maxv int ret;
379 1.1 maxv
380 1.1 maxv if (nvmm_init() == -1) {
381 1.1 maxv return -1;
382 1.1 maxv }
383 1.1 maxv
384 1.1 maxv args.machid = mach->machid;
385 1.1 maxv args.cpuid = cpuid;
386 1.1 maxv memcpy(&args.event, event, sizeof(args.event));
387 1.1 maxv
388 1.1 maxv ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_INJECT, &args);
389 1.1 maxv if (ret == -1)
390 1.1 maxv return -1;
391 1.1 maxv
392 1.1 maxv return 0;
393 1.1 maxv }
394 1.1 maxv
395 1.1 maxv int
396 1.1 maxv nvmm_vcpu_run(struct nvmm_machine *mach, nvmm_cpuid_t cpuid,
397 1.1 maxv struct nvmm_exit *exit)
398 1.1 maxv {
399 1.1 maxv struct nvmm_ioc_vcpu_run args;
400 1.1 maxv int ret;
401 1.1 maxv
402 1.1 maxv if (nvmm_init() == -1) {
403 1.1 maxv return -1;
404 1.1 maxv }
405 1.1 maxv
406 1.1 maxv args.machid = mach->machid;
407 1.1 maxv args.cpuid = cpuid;
408 1.1 maxv memset(&args.exit, 0, sizeof(args.exit));
409 1.1 maxv
410 1.1 maxv ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_RUN, &args);
411 1.1 maxv if (ret == -1)
412 1.1 maxv return -1;
413 1.1 maxv
414 1.1 maxv memcpy(exit, &args.exit, sizeof(args.exit));
415 1.1 maxv
416 1.1 maxv return 0;
417 1.1 maxv }
418 1.1 maxv
419 1.1 maxv int
420 1.1 maxv nvmm_gpa_map(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa,
421 1.1 maxv size_t size, int flags)
422 1.1 maxv {
423 1.1 maxv struct nvmm_ioc_gpa_map args;
424 1.1 maxv int ret;
425 1.1 maxv
426 1.1 maxv if (nvmm_init() == -1) {
427 1.1 maxv return -1;
428 1.1 maxv }
429 1.1 maxv
430 1.3 maxv ret = __area_add(mach, hva, gpa, size);
431 1.3 maxv if (ret == -1)
432 1.3 maxv return -1;
433 1.3 maxv
434 1.1 maxv args.machid = mach->machid;
435 1.1 maxv args.hva = hva;
436 1.1 maxv args.gpa = gpa;
437 1.1 maxv args.size = size;
438 1.1 maxv args.flags = flags;
439 1.1 maxv
440 1.1 maxv ret = ioctl(nvmm_fd, NVMM_IOC_GPA_MAP, &args);
441 1.1 maxv if (ret == -1) {
442 1.3 maxv /* Can't recover. */
443 1.3 maxv abort();
444 1.1 maxv }
445 1.1 maxv
446 1.1 maxv return 0;
447 1.1 maxv }
448 1.1 maxv
449 1.1 maxv int
450 1.1 maxv nvmm_gpa_unmap(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa,
451 1.1 maxv size_t size)
452 1.1 maxv {
453 1.1 maxv if (nvmm_init() == -1) {
454 1.1 maxv return -1;
455 1.1 maxv }
456 1.1 maxv
457 1.3 maxv return __area_dig_hole(mach, hva, gpa, size);
458 1.1 maxv }
459 1.1 maxv
460 1.1 maxv /*
461 1.1 maxv * nvmm_gva_to_gpa(): architecture-specific.
462 1.1 maxv */
463 1.1 maxv
464 1.1 maxv int
465 1.1 maxv nvmm_gpa_to_hva(struct nvmm_machine *mach, gpaddr_t gpa, uintptr_t *hva)
466 1.1 maxv {
467 1.3 maxv area_list_t *areas = mach->areas;
468 1.3 maxv area_t *ent;
469 1.1 maxv
470 1.1 maxv if (gpa % nvmm_page_size != 0) {
471 1.1 maxv errno = EINVAL;
472 1.1 maxv return -1;
473 1.1 maxv }
474 1.1 maxv
475 1.3 maxv LIST_FOREACH(ent, areas, list) {
476 1.3 maxv if (gpa < ent->gpa) {
477 1.1 maxv continue;
478 1.1 maxv }
479 1.3 maxv if (gpa >= ent->gpa + ent->size) {
480 1.1 maxv continue;
481 1.1 maxv }
482 1.1 maxv
483 1.3 maxv *hva = ent->hva + (gpa - ent->gpa);
484 1.1 maxv return 0;
485 1.1 maxv }
486 1.1 maxv
487 1.1 maxv errno = ENOENT;
488 1.1 maxv return -1;
489 1.1 maxv }
490 1.1 maxv
491 1.1 maxv /*
492 1.1 maxv * nvmm_assist_io(): architecture-specific.
493 1.1 maxv */
494