libnvmm.c revision 1.14
1/* $NetBSD: libnvmm.c,v 1.14 2019/06/08 07:27:44 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#include <machine/vmparam.h> 44 45#include "nvmm.h" 46 47static struct nvmm_capability __capability; 48 49#ifdef __x86_64__ 50#include "libnvmm_x86.c" 51#endif 52 53typedef struct __area { 54 LIST_ENTRY(__area) list; 55 gpaddr_t gpa; 56 uintptr_t hva; 57 size_t size; 58 nvmm_prot_t prot; 59} area_t; 60 61typedef LIST_HEAD(, __area) area_list_t; 62 63static int nvmm_fd = -1; 64 65/* -------------------------------------------------------------------------- */ 66 67static bool 68__area_isvalid(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa, 69 size_t size) 70{ 71 area_list_t *areas = mach->areas; 72 area_t *ent; 73 74 LIST_FOREACH(ent, areas, list) { 75 /* Collision on GPA */ 76 if (gpa >= ent->gpa && gpa < ent->gpa + ent->size) { 77 return false; 78 } 79 if (gpa + size > ent->gpa && 80 gpa + size <= ent->gpa + ent->size) { 81 return false; 82 } 83 if (gpa <= ent->gpa && gpa + size >= ent->gpa + ent->size) { 84 return false; 85 } 86 } 87 88 return true; 89} 90 91static int 92__area_add(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa, size_t size, 93 int prot) 94{ 95 area_list_t *areas = mach->areas; 96 nvmm_prot_t nprot; 97 area_t *area; 98 99 nprot = 0; 100 if (prot & PROT_READ) 101 nprot |= NVMM_PROT_READ; 102 if (prot & PROT_WRITE) 103 nprot |= NVMM_PROT_WRITE; 104 if (prot & PROT_EXEC) 105 nprot |= NVMM_PROT_EXEC; 106 107 if (!__area_isvalid(mach, hva, gpa, size)) { 108 errno = EINVAL; 109 return -1; 110 } 111 112 area = malloc(sizeof(*area)); 113 if (area == NULL) 114 return -1; 115 area->gpa = gpa; 116 area->hva = hva; 117 area->size = size; 118 area->prot = nprot; 119 120 LIST_INSERT_HEAD(areas, area, list); 121 122 return 0; 123} 124 125static int 126__area_delete(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa, 127 size_t size) 128{ 129 area_list_t *areas = mach->areas; 130 area_t *ent, *nxt; 131 132 LIST_FOREACH_SAFE(ent, areas, list, nxt) { 133 if (hva == ent->hva && gpa == ent->gpa && size == ent->size) { 134 LIST_REMOVE(ent, list); 135 free(ent); 136 return 0; 137 } 138 } 139 140 return -1; 141} 142 143static void 144__area_remove_all(struct nvmm_machine *mach) 145{ 146 area_list_t *areas = mach->areas; 147 area_t *ent; 148 149 while ((ent = LIST_FIRST(areas)) != NULL) { 150 LIST_REMOVE(ent, list); 151 free(ent); 152 } 153 154 free(areas); 155} 156 157/* -------------------------------------------------------------------------- */ 158 159static int 160nvmm_init(void) 161{ 162 if (nvmm_fd != -1) 163 return 0; 164 nvmm_fd = open("/dev/nvmm", O_RDWR); 165 if (nvmm_fd == -1) 166 return -1; 167 if (nvmm_capability(&__capability) == -1) { 168 close(nvmm_fd); 169 nvmm_fd = -1; 170 return -1; 171 } 172 return 0; 173} 174 175int 176nvmm_capability(struct nvmm_capability *cap) 177{ 178 struct nvmm_ioc_capability args; 179 int ret; 180 181 if (nvmm_init() == -1) { 182 return -1; 183 } 184 185 ret = ioctl(nvmm_fd, NVMM_IOC_CAPABILITY, &args); 186 if (ret == -1) 187 return -1; 188 189 memcpy(cap, &args.cap, sizeof(args.cap)); 190 191 return 0; 192} 193 194int 195nvmm_machine_create(struct nvmm_machine *mach) 196{ 197 struct nvmm_ioc_machine_create args; 198 struct nvmm_comm_page **pages; 199 area_list_t *areas; 200 int ret; 201 202 if (nvmm_init() == -1) { 203 return -1; 204 } 205 206 areas = calloc(1, sizeof(*areas)); 207 if (areas == NULL) 208 return -1; 209 210 pages = calloc(__capability.max_vcpus, sizeof(*pages)); 211 if (pages == NULL) { 212 free(areas); 213 return -1; 214 } 215 216 ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_CREATE, &args); 217 if (ret == -1) { 218 free(areas); 219 return -1; 220 } 221 222 LIST_INIT(areas); 223 224 memset(mach, 0, sizeof(*mach)); 225 mach->machid = args.machid; 226 mach->pages = pages; 227 mach->areas = areas; 228 229 return 0; 230} 231 232int 233nvmm_machine_destroy(struct nvmm_machine *mach) 234{ 235 struct nvmm_ioc_machine_destroy args; 236 int ret; 237 238 args.machid = mach->machid; 239 240 ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_DESTROY, &args); 241 if (ret == -1) 242 return -1; 243 244 __area_remove_all(mach); 245 free(mach->pages); 246 247 return 0; 248} 249 250int 251nvmm_machine_configure(struct nvmm_machine *mach, uint64_t op, void *conf) 252{ 253 struct nvmm_ioc_machine_configure args; 254 int ret; 255 256 switch (op) { 257 case NVMM_MACH_CONF_CALLBACKS: 258 memcpy(&mach->cbs, conf, sizeof(mach->cbs)); 259 return 0; 260 } 261 262 args.machid = mach->machid; 263 args.op = op; 264 args.conf = conf; 265 266 ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_CONFIGURE, &args); 267 if (ret == -1) 268 return -1; 269 270 return 0; 271} 272 273int 274nvmm_vcpu_create(struct nvmm_machine *mach, nvmm_cpuid_t cpuid, 275 struct nvmm_vcpu *vcpu) 276{ 277 struct nvmm_ioc_vcpu_create args; 278 struct nvmm_comm_page *comm; 279 int ret; 280 281 args.machid = mach->machid; 282 args.cpuid = cpuid; 283 284 ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_CREATE, &args); 285 if (ret == -1) 286 return -1; 287 288 comm = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FILE, 289 nvmm_fd, NVMM_COMM_OFF(mach->machid, cpuid)); 290 if (comm == MAP_FAILED) 291 return -1; 292 293 mach->pages[cpuid] = comm; 294 295 vcpu->cpuid = cpuid; 296 vcpu->state = &comm->state; 297 vcpu->event = &comm->event; 298 vcpu->exit = malloc(sizeof(*vcpu->exit)); 299 300 return 0; 301} 302 303int 304nvmm_vcpu_destroy(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu) 305{ 306 struct nvmm_ioc_vcpu_destroy args; 307 struct nvmm_comm_page *comm; 308 int ret; 309 310 args.machid = mach->machid; 311 args.cpuid = vcpu->cpuid; 312 313 ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_DESTROY, &args); 314 if (ret == -1) 315 return -1; 316 317 comm = mach->pages[vcpu->cpuid]; 318 munmap(comm, PAGE_SIZE); 319 free(vcpu->exit); 320 321 return 0; 322} 323 324int 325nvmm_vcpu_setstate(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu, 326 uint64_t flags) 327{ 328 struct nvmm_comm_page *comm; 329 330 comm = mach->pages[vcpu->cpuid]; 331 comm->state_commit |= flags; 332 comm->state_cached |= flags; 333 334 return 0; 335} 336 337int 338nvmm_vcpu_getstate(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu, 339 uint64_t flags) 340{ 341 struct nvmm_ioc_vcpu_getstate args; 342 struct nvmm_comm_page *comm; 343 int ret; 344 345 comm = mach->pages[vcpu->cpuid]; 346 347 if (__predict_true((flags & ~comm->state_cached) == 0)) { 348 return 0; 349 } 350 comm->state_wanted = flags & ~comm->state_cached; 351 352 args.machid = mach->machid; 353 args.cpuid = vcpu->cpuid; 354 355 ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_GETSTATE, &args); 356 if (ret == -1) 357 return -1; 358 359 return 0; 360} 361 362int 363nvmm_vcpu_inject(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu) 364{ 365 struct nvmm_comm_page *comm; 366 367 comm = mach->pages[vcpu->cpuid]; 368 comm->event_commit = true; 369 370 return 0; 371} 372 373int 374nvmm_vcpu_run(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu) 375{ 376 struct nvmm_ioc_vcpu_run args; 377 int ret; 378 379 args.machid = mach->machid; 380 args.cpuid = vcpu->cpuid; 381 memset(&args.exit, 0, sizeof(args.exit)); 382 383 ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_RUN, &args); 384 if (ret == -1) 385 return -1; 386 387 /* No comm support yet, just copy. */ 388 memcpy(vcpu->exit, &args.exit, sizeof(args.exit)); 389 390 return 0; 391} 392 393int 394nvmm_gpa_map(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa, 395 size_t size, int prot) 396{ 397 struct nvmm_ioc_gpa_map args; 398 int ret; 399 400 ret = __area_add(mach, hva, gpa, size, prot); 401 if (ret == -1) 402 return -1; 403 404 args.machid = mach->machid; 405 args.hva = hva; 406 args.gpa = gpa; 407 args.size = size; 408 args.prot = prot; 409 410 ret = ioctl(nvmm_fd, NVMM_IOC_GPA_MAP, &args); 411 if (ret == -1) { 412 /* Can't recover. */ 413 abort(); 414 } 415 416 return 0; 417} 418 419int 420nvmm_gpa_unmap(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa, 421 size_t size) 422{ 423 struct nvmm_ioc_gpa_unmap args; 424 int ret; 425 426 ret = __area_delete(mach, hva, gpa, size); 427 if (ret == -1) 428 return -1; 429 430 args.machid = mach->machid; 431 args.gpa = gpa; 432 args.size = size; 433 434 ret = ioctl(nvmm_fd, NVMM_IOC_GPA_UNMAP, &args); 435 if (ret == -1) { 436 /* Can't recover. */ 437 abort(); 438 } 439 440 return 0; 441} 442 443int 444nvmm_hva_map(struct nvmm_machine *mach, uintptr_t hva, size_t size) 445{ 446 struct nvmm_ioc_hva_map args; 447 int ret; 448 449 args.machid = mach->machid; 450 args.hva = hva; 451 args.size = size; 452 453 ret = ioctl(nvmm_fd, NVMM_IOC_HVA_MAP, &args); 454 if (ret == -1) 455 return -1; 456 457 return 0; 458} 459 460int 461nvmm_hva_unmap(struct nvmm_machine *mach, uintptr_t hva, size_t size) 462{ 463 struct nvmm_ioc_hva_unmap args; 464 int ret; 465 466 args.machid = mach->machid; 467 args.hva = hva; 468 args.size = size; 469 470 ret = ioctl(nvmm_fd, NVMM_IOC_HVA_UNMAP, &args); 471 if (ret == -1) 472 return -1; 473 474 return 0; 475} 476 477/* 478 * nvmm_gva_to_gpa(): architecture-specific. 479 */ 480 481int 482nvmm_gpa_to_hva(struct nvmm_machine *mach, gpaddr_t gpa, uintptr_t *hva, 483 nvmm_prot_t *prot) 484{ 485 area_list_t *areas = mach->areas; 486 area_t *ent; 487 488 LIST_FOREACH(ent, areas, list) { 489 if (gpa >= ent->gpa && gpa < ent->gpa + ent->size) { 490 *hva = ent->hva + (gpa - ent->gpa); 491 *prot = ent->prot; 492 return 0; 493 } 494 } 495 496 errno = ENOENT; 497 return -1; 498} 499 500/* 501 * nvmm_assist_io(): architecture-specific. 502 */ 503 504/* 505 * nvmm_assist_mem(): architecture-specific. 506 */ 507 508int 509nvmm_ctl(int op, void *data, size_t size) 510{ 511 struct nvmm_ioc_ctl args; 512 int ret; 513 514 if (nvmm_init() == -1) { 515 return -1; 516 } 517 518 args.op = op; 519 args.data = data; 520 args.size = size; 521 522 ret = ioctl(nvmm_fd, NVMM_IOC_CTL, &args); 523 if (ret == -1) 524 return -1; 525 526 return 0; 527} 528