libnvmm.c revision 1.1 1 /* $NetBSD: libnvmm.c,v 1.1 2018/11/10 09:28:56 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
43 #include "nvmm.h"
44
45 static int nvmm_fd = -1;
46 static size_t nvmm_page_size = 0;
47
48 /* -------------------------------------------------------------------------- */
49
50 static int
51 _nvmm_area_add(struct nvmm_machine *mach, gpaddr_t gpa, uintptr_t hva,
52 size_t size)
53 {
54 struct nvmm_area *area;
55 void *ptr;
56 size_t i;
57
58 for (i = 0; i < mach->nareas; i++) {
59 if (gpa >= mach->areas[i].gpa &&
60 gpa < mach->areas[i].gpa + mach->areas[i].size) {
61 goto error;
62 }
63 if (gpa + size >= mach->areas[i].gpa &&
64 gpa + size < mach->areas[i].gpa + mach->areas[i].size) {
65 goto error;
66 }
67 if (gpa < mach->areas[i].gpa &&
68 gpa + size >= mach->areas[i].gpa + mach->areas[i].size) {
69 goto error;
70 }
71 }
72
73 mach->nareas++;
74 ptr = realloc(mach->areas, mach->nareas * sizeof(struct nvmm_area));
75 if (ptr == NULL)
76 return -1;
77 mach->areas = ptr;
78
79 area = &mach->areas[mach->nareas-1];
80 area->gpa = gpa;
81 area->hva = hva;
82 area->size = size;
83
84 return 0;
85
86 error:
87 errno = EEXIST;
88 return -1;
89 }
90
91 static int
92 _nvmm_area_delete(struct nvmm_machine *mach, gpaddr_t gpa, uintptr_t hva,
93 size_t size)
94 {
95 size_t i;
96
97 for (i = 0; i < mach->nareas; i++) {
98 if (gpa == mach->areas[i].gpa &&
99 hva == mach->areas[i].hva &&
100 size == mach->areas[i].size) {
101 break;
102 }
103 }
104 if (i == mach->nareas) {
105 errno = ENOENT;
106 return -1;
107 }
108
109 memcpy(&mach->areas[i], &mach->areas[i+1],
110 (mach->nareas - i - 1) * sizeof(struct nvmm_area));
111 mach->nareas--;
112
113 return 0;
114 }
115
116 /* -------------------------------------------------------------------------- */
117
118 static int
119 nvmm_init(void)
120 {
121 if (nvmm_fd != -1)
122 return 0;
123 nvmm_fd = open("/dev/nvmm", O_RDWR);
124 if (nvmm_fd == -1)
125 return -1;
126 nvmm_page_size = sysconf(_SC_PAGESIZE);
127 return 0;
128 }
129
130 int
131 nvmm_capability(struct nvmm_capability *cap)
132 {
133 struct nvmm_ioc_capability args;
134 int ret;
135
136 if (nvmm_init() == -1) {
137 return -1;
138 }
139
140 ret = ioctl(nvmm_fd, NVMM_IOC_CAPABILITY, &args);
141 if (ret == -1)
142 return -1;
143
144 memcpy(cap, &args.cap, sizeof(args.cap));
145
146 return 0;
147 }
148
149 int
150 nvmm_machine_create(struct nvmm_machine *mach)
151 {
152 struct nvmm_ioc_machine_create args;
153 int ret;
154
155 if (nvmm_init() == -1) {
156 return -1;
157 }
158
159 ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_CREATE, &args);
160 if (ret == -1)
161 return -1;
162
163 memset(mach, 0, sizeof(*mach));
164 mach->machid = args.machid;
165
166 return 0;
167 }
168
169 int
170 nvmm_machine_destroy(struct nvmm_machine *mach)
171 {
172 struct nvmm_ioc_machine_destroy args;
173 int ret;
174
175 if (nvmm_init() == -1) {
176 return -1;
177 }
178
179 args.machid = mach->machid;
180
181 ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_DESTROY, &args);
182 if (ret == -1)
183 return -1;
184
185 free(mach->areas);
186
187 return 0;
188 }
189
190 int
191 nvmm_machine_configure(struct nvmm_machine *mach, uint64_t op, void *conf)
192 {
193 struct nvmm_ioc_machine_configure args;
194 int ret;
195
196 if (nvmm_init() == -1) {
197 return -1;
198 }
199
200 args.machid = mach->machid;
201 args.op = op;
202 args.conf = conf;
203
204 ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_CONFIGURE, &args);
205 if (ret == -1)
206 return -1;
207
208 return 0;
209 }
210
211 int
212 nvmm_vcpu_create(struct nvmm_machine *mach, nvmm_cpuid_t cpuid)
213 {
214 struct nvmm_ioc_vcpu_create args;
215 int ret;
216
217 if (nvmm_init() == -1) {
218 return -1;
219 }
220
221 args.machid = mach->machid;
222 args.cpuid = cpuid;
223
224 ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_CREATE, &args);
225 if (ret == -1)
226 return -1;
227
228 return 0;
229 }
230
231 int
232 nvmm_vcpu_destroy(struct nvmm_machine *mach, nvmm_cpuid_t cpuid)
233 {
234 struct nvmm_ioc_vcpu_destroy args;
235 int ret;
236
237 if (nvmm_init() == -1) {
238 return -1;
239 }
240
241 args.machid = mach->machid;
242 args.cpuid = cpuid;
243
244 ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_DESTROY, &args);
245 if (ret == -1)
246 return -1;
247
248 return 0;
249 }
250
251 int
252 nvmm_vcpu_setstate(struct nvmm_machine *mach, nvmm_cpuid_t cpuid,
253 void *state, uint64_t flags)
254 {
255 struct nvmm_ioc_vcpu_setstate args;
256 int ret;
257
258 if (nvmm_init() == -1) {
259 return -1;
260 }
261
262 args.machid = mach->machid;
263 args.cpuid = cpuid;
264 args.state = state;
265 args.flags = flags;
266
267 ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_SETSTATE, &args);
268 if (ret == -1)
269 return -1;
270
271 return 0;
272 }
273
274 int
275 nvmm_vcpu_getstate(struct nvmm_machine *mach, nvmm_cpuid_t cpuid,
276 void *state, uint64_t flags)
277 {
278 struct nvmm_ioc_vcpu_getstate args;
279 int ret;
280
281 if (nvmm_init() == -1) {
282 return -1;
283 }
284
285 args.machid = mach->machid;
286 args.cpuid = cpuid;
287 args.state = state;
288 args.flags = flags;
289
290 ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_GETSTATE, &args);
291 if (ret == -1)
292 return -1;
293
294 return 0;
295 }
296
297 int
298 nvmm_vcpu_inject(struct nvmm_machine *mach, nvmm_cpuid_t cpuid,
299 struct nvmm_event *event)
300 {
301 struct nvmm_ioc_vcpu_inject args;
302 int ret;
303
304 if (nvmm_init() == -1) {
305 return -1;
306 }
307
308 args.machid = mach->machid;
309 args.cpuid = cpuid;
310 memcpy(&args.event, event, sizeof(args.event));
311
312 ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_INJECT, &args);
313 if (ret == -1)
314 return -1;
315
316 return 0;
317 }
318
319 int
320 nvmm_vcpu_run(struct nvmm_machine *mach, nvmm_cpuid_t cpuid,
321 struct nvmm_exit *exit)
322 {
323 struct nvmm_ioc_vcpu_run args;
324 int ret;
325
326 if (nvmm_init() == -1) {
327 return -1;
328 }
329
330 args.machid = mach->machid;
331 args.cpuid = cpuid;
332 memset(&args.exit, 0, sizeof(args.exit));
333
334 ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_RUN, &args);
335 if (ret == -1)
336 return -1;
337
338 memcpy(exit, &args.exit, sizeof(args.exit));
339
340 return 0;
341 }
342
343 int
344 nvmm_gpa_map(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa,
345 size_t size, int flags)
346 {
347 struct nvmm_ioc_gpa_map args;
348 int ret;
349
350 if (nvmm_init() == -1) {
351 return -1;
352 }
353
354 args.machid = mach->machid;
355 args.hva = hva;
356 args.gpa = gpa;
357 args.size = size;
358 args.flags = flags;
359
360 ret = ioctl(nvmm_fd, NVMM_IOC_GPA_MAP, &args);
361 if (ret == -1)
362 return -1;
363
364 ret = _nvmm_area_add(mach, gpa, hva, size);
365 if (ret == -1) {
366 nvmm_gpa_unmap(mach, hva, gpa, size);
367 return -1;
368 }
369
370 return 0;
371 }
372
373 int
374 nvmm_gpa_unmap(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa,
375 size_t size)
376 {
377 struct nvmm_ioc_gpa_unmap args;
378 int ret;
379
380 if (nvmm_init() == -1) {
381 return -1;
382 }
383
384 ret = _nvmm_area_delete(mach, gpa, hva, size);
385 if (ret == -1)
386 return -1;
387
388 args.machid = mach->machid;
389 args.gpa = gpa;
390 args.size = size;
391
392 ret = ioctl(nvmm_fd, NVMM_IOC_GPA_UNMAP, &args);
393 if (ret == -1)
394 return -1;
395
396 ret = munmap((void *)hva, size);
397
398 return ret;
399 }
400
401 /*
402 * nvmm_gva_to_gpa(): architecture-specific.
403 */
404
405 int
406 nvmm_gpa_to_hva(struct nvmm_machine *mach, gpaddr_t gpa, uintptr_t *hva)
407 {
408 size_t i;
409
410 if (gpa % nvmm_page_size != 0) {
411 errno = EINVAL;
412 return -1;
413 }
414
415 for (i = 0; i < mach->nareas; i++) {
416 if (gpa < mach->areas[i].gpa) {
417 continue;
418 }
419 if (gpa >= mach->areas[i].gpa + mach->areas[i].size) {
420 continue;
421 }
422
423 *hva = mach->areas[i].hva + (gpa - mach->areas[i].gpa);
424 return 0;
425 }
426
427 errno = ENOENT;
428 return -1;
429 }
430
431 /*
432 * nvmm_assist_io(): architecture-specific.
433 */
434