Home | History | Annotate | Line # | Download | only in libnvmm
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