libnvmm.c revision 1.17
1/* $NetBSD: libnvmm.c,v 1.17 2019/10/27 07:08:15 maxv Exp $ */ 2 3/* 4 * Copyright (c) 2018-2019 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 159int 160nvmm_init(void) 161{ 162 if (nvmm_fd != -1) 163 return 0; 164 nvmm_fd = open("/dev/nvmm", O_RDONLY | O_CLOEXEC); 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 if (__capability.version != NVMM_KERN_VERSION) { 173 close(nvmm_fd); 174 nvmm_fd = -1; 175 errno = EPROGMISMATCH; 176 return -1; 177 } 178 179 return 0; 180} 181 182int 183nvmm_capability(struct nvmm_capability *cap) 184{ 185 struct nvmm_ioc_capability args; 186 int ret; 187 188 ret = ioctl(nvmm_fd, NVMM_IOC_CAPABILITY, &args); 189 if (ret == -1) 190 return -1; 191 192 memcpy(cap, &args.cap, sizeof(args.cap)); 193 194 return 0; 195} 196 197int 198nvmm_machine_create(struct nvmm_machine *mach) 199{ 200 struct nvmm_ioc_machine_create args; 201 struct nvmm_comm_page **pages; 202 area_list_t *areas; 203 int ret; 204 205 areas = calloc(1, sizeof(*areas)); 206 if (areas == NULL) 207 return -1; 208 209 pages = calloc(__capability.max_vcpus, sizeof(*pages)); 210 if (pages == NULL) { 211 free(areas); 212 return -1; 213 } 214 215 ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_CREATE, &args); 216 if (ret == -1) { 217 free(areas); 218 return -1; 219 } 220 221 LIST_INIT(areas); 222 223 memset(mach, 0, sizeof(*mach)); 224 mach->machid = args.machid; 225 mach->pages = pages; 226 mach->areas = areas; 227 228 return 0; 229} 230 231int 232nvmm_machine_destroy(struct nvmm_machine *mach) 233{ 234 struct nvmm_ioc_machine_destroy args; 235 int ret; 236 237 args.machid = mach->machid; 238 239 ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_DESTROY, &args); 240 if (ret == -1) 241 return -1; 242 243 __area_remove_all(mach); 244 free(mach->pages); 245 246 return 0; 247} 248 249int 250nvmm_machine_configure(struct nvmm_machine *mach, uint64_t op, void *conf) 251{ 252 struct nvmm_ioc_machine_configure args; 253 int ret; 254 255 args.machid = mach->machid; 256 args.op = op; 257 args.conf = conf; 258 259 ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_CONFIGURE, &args); 260 if (ret == -1) 261 return -1; 262 263 return 0; 264} 265 266int 267nvmm_vcpu_create(struct nvmm_machine *mach, nvmm_cpuid_t cpuid, 268 struct nvmm_vcpu *vcpu) 269{ 270 struct nvmm_ioc_vcpu_create args; 271 struct nvmm_comm_page *comm; 272 int ret; 273 274 args.machid = mach->machid; 275 args.cpuid = cpuid; 276 277 ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_CREATE, &args); 278 if (ret == -1) 279 return -1; 280 281 comm = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FILE, 282 nvmm_fd, NVMM_COMM_OFF(mach->machid, cpuid)); 283 if (comm == MAP_FAILED) 284 return -1; 285 286 mach->pages[cpuid] = comm; 287 288 vcpu->cpuid = cpuid; 289 vcpu->state = &comm->state; 290 vcpu->event = &comm->event; 291 vcpu->exit = malloc(sizeof(*vcpu->exit)); 292 293 return 0; 294} 295 296int 297nvmm_vcpu_destroy(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu) 298{ 299 struct nvmm_ioc_vcpu_destroy args; 300 struct nvmm_comm_page *comm; 301 int ret; 302 303 args.machid = mach->machid; 304 args.cpuid = vcpu->cpuid; 305 306 ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_DESTROY, &args); 307 if (ret == -1) 308 return -1; 309 310 comm = mach->pages[vcpu->cpuid]; 311 munmap(comm, PAGE_SIZE); 312 free(vcpu->exit); 313 314 return 0; 315} 316 317int 318nvmm_vcpu_configure(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu, 319 uint64_t op, void *conf) 320{ 321 struct nvmm_ioc_vcpu_configure args; 322 int ret; 323 324 switch (op) { 325 case NVMM_VCPU_CONF_CALLBACKS: 326 memcpy(&vcpu->cbs, conf, sizeof(vcpu->cbs)); 327 return 0; 328 } 329 330 args.machid = mach->machid; 331 args.cpuid = vcpu->cpuid; 332 args.op = op; 333 args.conf = conf; 334 335 ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_CONFIGURE, &args); 336 if (ret == -1) 337 return -1; 338 339 return 0; 340} 341 342int 343nvmm_vcpu_setstate(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu, 344 uint64_t flags) 345{ 346 struct nvmm_comm_page *comm; 347 348 comm = mach->pages[vcpu->cpuid]; 349 comm->state_commit |= flags; 350 comm->state_cached |= flags; 351 352 return 0; 353} 354 355int 356nvmm_vcpu_getstate(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu, 357 uint64_t flags) 358{ 359 struct nvmm_ioc_vcpu_getstate args; 360 struct nvmm_comm_page *comm; 361 int ret; 362 363 comm = mach->pages[vcpu->cpuid]; 364 365 if (__predict_true((flags & ~comm->state_cached) == 0)) { 366 return 0; 367 } 368 comm->state_wanted = flags & ~comm->state_cached; 369 370 args.machid = mach->machid; 371 args.cpuid = vcpu->cpuid; 372 373 ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_GETSTATE, &args); 374 if (ret == -1) 375 return -1; 376 377 return 0; 378} 379 380int 381nvmm_vcpu_inject(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu) 382{ 383 struct nvmm_comm_page *comm; 384 385 comm = mach->pages[vcpu->cpuid]; 386 comm->event_commit = true; 387 388 return 0; 389} 390 391int 392nvmm_vcpu_run(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu) 393{ 394 struct nvmm_ioc_vcpu_run args; 395 int ret; 396 397 args.machid = mach->machid; 398 args.cpuid = vcpu->cpuid; 399 memset(&args.exit, 0, sizeof(args.exit)); 400 401 ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_RUN, &args); 402 if (ret == -1) 403 return -1; 404 405 /* No comm support yet, just copy. */ 406 memcpy(vcpu->exit, &args.exit, sizeof(args.exit)); 407 408 return 0; 409} 410 411int 412nvmm_gpa_map(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa, 413 size_t size, int prot) 414{ 415 struct nvmm_ioc_gpa_map args; 416 int ret; 417 418 ret = __area_add(mach, hva, gpa, size, prot); 419 if (ret == -1) 420 return -1; 421 422 args.machid = mach->machid; 423 args.hva = hva; 424 args.gpa = gpa; 425 args.size = size; 426 args.prot = prot; 427 428 ret = ioctl(nvmm_fd, NVMM_IOC_GPA_MAP, &args); 429 if (ret == -1) { 430 /* Can't recover. */ 431 abort(); 432 } 433 434 return 0; 435} 436 437int 438nvmm_gpa_unmap(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa, 439 size_t size) 440{ 441 struct nvmm_ioc_gpa_unmap args; 442 int ret; 443 444 ret = __area_delete(mach, hva, gpa, size); 445 if (ret == -1) 446 return -1; 447 448 args.machid = mach->machid; 449 args.gpa = gpa; 450 args.size = size; 451 452 ret = ioctl(nvmm_fd, NVMM_IOC_GPA_UNMAP, &args); 453 if (ret == -1) { 454 /* Can't recover. */ 455 abort(); 456 } 457 458 return 0; 459} 460 461int 462nvmm_hva_map(struct nvmm_machine *mach, uintptr_t hva, size_t size) 463{ 464 struct nvmm_ioc_hva_map args; 465 int ret; 466 467 args.machid = mach->machid; 468 args.hva = hva; 469 args.size = size; 470 471 ret = ioctl(nvmm_fd, NVMM_IOC_HVA_MAP, &args); 472 if (ret == -1) 473 return -1; 474 475 return 0; 476} 477 478int 479nvmm_hva_unmap(struct nvmm_machine *mach, uintptr_t hva, size_t size) 480{ 481 struct nvmm_ioc_hva_unmap args; 482 int ret; 483 484 args.machid = mach->machid; 485 args.hva = hva; 486 args.size = size; 487 488 ret = ioctl(nvmm_fd, NVMM_IOC_HVA_UNMAP, &args); 489 if (ret == -1) 490 return -1; 491 492 return 0; 493} 494 495/* 496 * nvmm_gva_to_gpa(): architecture-specific. 497 */ 498 499int 500nvmm_gpa_to_hva(struct nvmm_machine *mach, gpaddr_t gpa, uintptr_t *hva, 501 nvmm_prot_t *prot) 502{ 503 area_list_t *areas = mach->areas; 504 area_t *ent; 505 506 LIST_FOREACH(ent, areas, list) { 507 if (gpa >= ent->gpa && gpa < ent->gpa + ent->size) { 508 *hva = ent->hva + (gpa - ent->gpa); 509 *prot = ent->prot; 510 return 0; 511 } 512 } 513 514 errno = ENOENT; 515 return -1; 516} 517 518/* 519 * nvmm_assist_io(): architecture-specific. 520 */ 521 522/* 523 * nvmm_assist_mem(): architecture-specific. 524 */ 525 526int 527nvmm_ctl(int op, void *data, size_t size) 528{ 529 struct nvmm_ioc_ctl args; 530 int ret; 531 532 args.op = op; 533 args.data = data; 534 args.size = size; 535 536 ret = ioctl(nvmm_fd, NVMM_IOC_CTL, &args); 537 if (ret == -1) 538 return -1; 539 540 return 0; 541} 542