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
46typedef struct __area {
47	LIST_ENTRY(__area) list;
48	gpaddr_t gpa;
49	uintptr_t hva;
50	size_t size;
51} area_t;
52
53typedef LIST_HEAD(, __area) area_list_t;
54
55static int nvmm_fd = -1;
56static size_t nvmm_page_size = 0;
57
58/* -------------------------------------------------------------------------- */
59
60static 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
80static 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
145static 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
169static 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
185static int
186nvmm_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
197int
198nvmm_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
216int
217nvmm_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
245int
246nvmm_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
266int
267nvmm_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
287int
288nvmm_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
307int
308nvmm_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
327int
328nvmm_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
350int
351nvmm_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
373int
374nvmm_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
395int
396nvmm_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
419int
420nvmm_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
449int
450nvmm_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
464int
465nvmm_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