libnvmm.c revision 1.13
1/* $NetBSD: libnvmm.c,v 1.13 2019/05/11 07:31:57 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->npages = __capability.max_vcpus; 228 mach->areas = areas; 229 230 return 0; 231} 232 233int 234nvmm_machine_destroy(struct nvmm_machine *mach) 235{ 236 struct nvmm_ioc_machine_destroy args; 237 int ret; 238 239 args.machid = mach->machid; 240 241 ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_DESTROY, &args); 242 if (ret == -1) 243 return -1; 244 245 __area_remove_all(mach); 246 free(mach->pages); 247 248 return 0; 249} 250 251int 252nvmm_machine_configure(struct nvmm_machine *mach, uint64_t op, void *conf) 253{ 254 struct nvmm_ioc_machine_configure args; 255 int ret; 256 257 switch (op) { 258 case NVMM_MACH_CONF_CALLBACKS: 259 memcpy(&mach->cbs, conf, sizeof(mach->cbs)); 260 return 0; 261 } 262 263 args.machid = mach->machid; 264 args.op = op; 265 args.conf = conf; 266 267 ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_CONFIGURE, &args); 268 if (ret == -1) 269 return -1; 270 271 return 0; 272} 273 274int 275nvmm_vcpu_create(struct nvmm_machine *mach, nvmm_cpuid_t cpuid) 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 return 0; 296} 297 298int 299nvmm_vcpu_destroy(struct nvmm_machine *mach, nvmm_cpuid_t cpuid) 300{ 301 struct nvmm_ioc_vcpu_destroy args; 302 struct nvmm_comm_page *comm; 303 int ret; 304 305 args.machid = mach->machid; 306 args.cpuid = cpuid; 307 308 ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_DESTROY, &args); 309 if (ret == -1) 310 return -1; 311 312 comm = mach->pages[cpuid]; 313 munmap(comm, PAGE_SIZE); 314 315 return 0; 316} 317 318int 319nvmm_vcpu_setstate(struct nvmm_machine *mach, nvmm_cpuid_t cpuid, 320 void *state, uint64_t flags) 321{ 322 struct nvmm_comm_page *comm; 323 324 if (__predict_false(cpuid >= mach->npages)) { 325 return -1; 326 } 327 comm = mach->pages[cpuid]; 328 329 nvmm_arch_copystate(&comm->state, state, flags); 330 comm->state_commit |= flags; 331 comm->state_cached |= flags; 332 333 return 0; 334} 335 336int 337nvmm_vcpu_getstate(struct nvmm_machine *mach, nvmm_cpuid_t cpuid, 338 void *state, uint64_t flags) 339{ 340 struct nvmm_ioc_vcpu_getstate args; 341 struct nvmm_comm_page *comm; 342 int ret; 343 344 if (__predict_false(cpuid >= mach->npages)) { 345 return -1; 346 } 347 comm = mach->pages[cpuid]; 348 349 if (__predict_true((flags & ~comm->state_cached) == 0)) { 350 goto out; 351 } 352 comm->state_wanted = flags & ~comm->state_cached; 353 354 args.machid = mach->machid; 355 args.cpuid = cpuid; 356 357 ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_GETSTATE, &args); 358 if (ret == -1) 359 return -1; 360 361out: 362 nvmm_arch_copystate(state, &comm->state, flags); 363 return 0; 364} 365 366int 367nvmm_vcpu_inject(struct nvmm_machine *mach, nvmm_cpuid_t cpuid, 368 struct nvmm_event *event) 369{ 370 struct nvmm_comm_page *comm; 371 372 if (__predict_false(cpuid >= mach->npages)) { 373 return -1; 374 } 375 comm = mach->pages[cpuid]; 376 377 memcpy(&comm->event, event, sizeof(comm->event)); 378 comm->event_commit = true; 379 380 return 0; 381} 382 383int 384nvmm_vcpu_run(struct nvmm_machine *mach, nvmm_cpuid_t cpuid, 385 struct nvmm_exit *exit) 386{ 387 struct nvmm_ioc_vcpu_run args; 388 int ret; 389 390 args.machid = mach->machid; 391 args.cpuid = cpuid; 392 memset(&args.exit, 0, sizeof(args.exit)); 393 394 ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_RUN, &args); 395 if (ret == -1) 396 return -1; 397 398 memcpy(exit, &args.exit, sizeof(args.exit)); 399 400 return 0; 401} 402 403int 404nvmm_gpa_map(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa, 405 size_t size, int prot) 406{ 407 struct nvmm_ioc_gpa_map args; 408 int ret; 409 410 ret = __area_add(mach, hva, gpa, size, prot); 411 if (ret == -1) 412 return -1; 413 414 args.machid = mach->machid; 415 args.hva = hva; 416 args.gpa = gpa; 417 args.size = size; 418 args.prot = prot; 419 420 ret = ioctl(nvmm_fd, NVMM_IOC_GPA_MAP, &args); 421 if (ret == -1) { 422 /* Can't recover. */ 423 abort(); 424 } 425 426 return 0; 427} 428 429int 430nvmm_gpa_unmap(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa, 431 size_t size) 432{ 433 struct nvmm_ioc_gpa_unmap args; 434 int ret; 435 436 ret = __area_delete(mach, hva, gpa, size); 437 if (ret == -1) 438 return -1; 439 440 args.machid = mach->machid; 441 args.gpa = gpa; 442 args.size = size; 443 444 ret = ioctl(nvmm_fd, NVMM_IOC_GPA_UNMAP, &args); 445 if (ret == -1) { 446 /* Can't recover. */ 447 abort(); 448 } 449 450 return 0; 451} 452 453int 454nvmm_hva_map(struct nvmm_machine *mach, uintptr_t hva, size_t size) 455{ 456 struct nvmm_ioc_hva_map args; 457 int ret; 458 459 args.machid = mach->machid; 460 args.hva = hva; 461 args.size = size; 462 463 ret = ioctl(nvmm_fd, NVMM_IOC_HVA_MAP, &args); 464 if (ret == -1) 465 return -1; 466 467 return 0; 468} 469 470int 471nvmm_hva_unmap(struct nvmm_machine *mach, uintptr_t hva, size_t size) 472{ 473 struct nvmm_ioc_hva_unmap args; 474 int ret; 475 476 args.machid = mach->machid; 477 args.hva = hva; 478 args.size = size; 479 480 ret = ioctl(nvmm_fd, NVMM_IOC_HVA_UNMAP, &args); 481 if (ret == -1) 482 return -1; 483 484 return 0; 485} 486 487/* 488 * nvmm_gva_to_gpa(): architecture-specific. 489 */ 490 491int 492nvmm_gpa_to_hva(struct nvmm_machine *mach, gpaddr_t gpa, uintptr_t *hva, 493 nvmm_prot_t *prot) 494{ 495 area_list_t *areas = mach->areas; 496 area_t *ent; 497 498 LIST_FOREACH(ent, areas, list) { 499 if (gpa >= ent->gpa && gpa < ent->gpa + ent->size) { 500 *hva = ent->hva + (gpa - ent->gpa); 501 *prot = ent->prot; 502 return 0; 503 } 504 } 505 506 errno = ENOENT; 507 return -1; 508} 509 510/* 511 * nvmm_assist_io(): architecture-specific. 512 */ 513 514/* 515 * nvmm_assist_mem(): architecture-specific. 516 */ 517 518int 519nvmm_ctl(int op, void *data, size_t size) 520{ 521 struct nvmm_ioc_ctl args; 522 int ret; 523 524 if (nvmm_init() == -1) { 525 return -1; 526 } 527 528 args.op = op; 529 args.data = data; 530 args.size = size; 531 532 ret = ioctl(nvmm_fd, NVMM_IOC_CTL, &args); 533 if (ret == -1) 534 return -1; 535 536 return 0; 537} 538