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