1 /* $NetBSD: libnvmm.c,v 1.19 2020/09/05 07:22:25 maxv Exp $ */ 2 3 /* 4 * Copyright (c) 2018-2020 Maxime Villard, m00nbsd.net 5 * All rights reserved. 6 * 7 * This code is part of the NVMM hypervisor. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 23 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 25 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 #include <sys/cdefs.h> 32 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <unistd.h> 37 #include <fcntl.h> 38 #include <errno.h> 39 #include <sys/ioctl.h> 40 #include <sys/mman.h> 41 #include <sys/queue.h> 42 #include <machine/vmparam.h> 43 44 #include "nvmm.h" 45 46 static struct nvmm_capability __capability; 47 48 #ifdef __x86_64__ 49 #include "libnvmm_x86.c" 50 #endif 51 52 typedef struct __area { 53 LIST_ENTRY(__area) list; 54 gpaddr_t gpa; 55 uintptr_t hva; 56 size_t size; 57 nvmm_prot_t prot; 58 } area_t; 59 60 typedef LIST_HEAD(, __area) area_list_t; 61 62 static int nvmm_fd = -1; 63 64 /* -------------------------------------------------------------------------- */ 65 66 static bool 67 __area_isvalid(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa, 68 size_t size) 69 { 70 area_list_t *areas = mach->areas; 71 area_t *ent; 72 73 LIST_FOREACH(ent, areas, list) { 74 /* Collision on GPA */ 75 if (gpa >= ent->gpa && gpa < ent->gpa + ent->size) { 76 return false; 77 } 78 if (gpa + size > ent->gpa && 79 gpa + size <= ent->gpa + ent->size) { 80 return false; 81 } 82 if (gpa <= ent->gpa && gpa + size >= ent->gpa + ent->size) { 83 return false; 84 } 85 } 86 87 return true; 88 } 89 90 static int 91 __area_add(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa, size_t size, 92 int prot) 93 { 94 area_list_t *areas = mach->areas; 95 nvmm_prot_t nprot; 96 area_t *area; 97 98 nprot = 0; 99 if (prot & PROT_READ) 100 nprot |= NVMM_PROT_READ; 101 if (prot & PROT_WRITE) 102 nprot |= NVMM_PROT_WRITE; 103 if (prot & PROT_EXEC) 104 nprot |= NVMM_PROT_EXEC; 105 106 if (!__area_isvalid(mach, hva, gpa, size)) { 107 errno = EINVAL; 108 return -1; 109 } 110 111 area = malloc(sizeof(*area)); 112 if (area == NULL) 113 return -1; 114 area->gpa = gpa; 115 area->hva = hva; 116 area->size = size; 117 area->prot = nprot; 118 119 LIST_INSERT_HEAD(areas, area, list); 120 121 return 0; 122 } 123 124 static int 125 __area_delete(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa, 126 size_t size) 127 { 128 area_list_t *areas = mach->areas; 129 area_t *ent, *nxt; 130 131 LIST_FOREACH_SAFE(ent, areas, list, nxt) { 132 if (hva == ent->hva && gpa == ent->gpa && size == ent->size) { 133 LIST_REMOVE(ent, list); 134 free(ent); 135 return 0; 136 } 137 } 138 139 return -1; 140 } 141 142 static void 143 __area_remove_all(struct nvmm_machine *mach) 144 { 145 area_list_t *areas = mach->areas; 146 area_t *ent; 147 148 while ((ent = LIST_FIRST(areas)) != NULL) { 149 LIST_REMOVE(ent, list); 150 free(ent); 151 } 152 153 free(areas); 154 } 155 156 /* -------------------------------------------------------------------------- */ 157 158 int 159 nvmm_init(void) 160 { 161 if (nvmm_fd != -1) 162 return 0; 163 nvmm_fd = open("/dev/nvmm", O_RDONLY | O_CLOEXEC); 164 if (nvmm_fd == -1) 165 return -1; 166 if (nvmm_capability(&__capability) == -1) { 167 close(nvmm_fd); 168 nvmm_fd = -1; 169 return -1; 170 } 171 if (__capability.version != NVMM_KERN_VERSION) { 172 close(nvmm_fd); 173 nvmm_fd = -1; 174 errno = EPROGMISMATCH; 175 return -1; 176 } 177 178 return 0; 179 } 180 181 int 182 nvmm_root_init(void) 183 { 184 if (nvmm_fd != -1) 185 return 0; 186 nvmm_fd = open("/dev/nvmm", O_WRONLY | O_CLOEXEC); 187 if (nvmm_fd == -1) 188 return -1; 189 if (nvmm_capability(&__capability) == -1) { 190 close(nvmm_fd); 191 nvmm_fd = -1; 192 return -1; 193 } 194 if (__capability.version != NVMM_KERN_VERSION) { 195 close(nvmm_fd); 196 nvmm_fd = -1; 197 errno = EPROGMISMATCH; 198 return -1; 199 } 200 201 return 0; 202 } 203 204 int 205 nvmm_capability(struct nvmm_capability *cap) 206 { 207 struct nvmm_ioc_capability args; 208 int ret; 209 210 ret = ioctl(nvmm_fd, NVMM_IOC_CAPABILITY, &args); 211 if (ret == -1) 212 return -1; 213 214 memcpy(cap, &args.cap, sizeof(args.cap)); 215 216 return 0; 217 } 218 219 int 220 nvmm_machine_create(struct nvmm_machine *mach) 221 { 222 struct nvmm_ioc_machine_create args; 223 struct nvmm_comm_page **pages; 224 area_list_t *areas; 225 int ret; 226 227 areas = calloc(1, sizeof(*areas)); 228 if (areas == NULL) 229 return -1; 230 231 pages = calloc(__capability.max_vcpus, sizeof(*pages)); 232 if (pages == NULL) { 233 free(areas); 234 return -1; 235 } 236 237 ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_CREATE, &args); 238 if (ret == -1) { 239 free(areas); 240 return -1; 241 } 242 243 LIST_INIT(areas); 244 245 memset(mach, 0, sizeof(*mach)); 246 mach->machid = args.machid; 247 mach->pages = pages; 248 mach->areas = areas; 249 250 return 0; 251 } 252 253 int 254 nvmm_machine_destroy(struct nvmm_machine *mach) 255 { 256 struct nvmm_ioc_machine_destroy args; 257 int ret; 258 259 args.machid = mach->machid; 260 261 ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_DESTROY, &args); 262 if (ret == -1) 263 return -1; 264 265 __area_remove_all(mach); 266 free(mach->pages); 267 268 return 0; 269 } 270 271 int 272 nvmm_machine_configure(struct nvmm_machine *mach, uint64_t op, void *conf) 273 { 274 struct nvmm_ioc_machine_configure args; 275 int ret; 276 277 args.machid = mach->machid; 278 args.op = op; 279 args.conf = conf; 280 281 ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_CONFIGURE, &args); 282 if (ret == -1) 283 return -1; 284 285 return 0; 286 } 287 288 int 289 nvmm_vcpu_create(struct nvmm_machine *mach, nvmm_cpuid_t cpuid, 290 struct nvmm_vcpu *vcpu) 291 { 292 struct nvmm_ioc_vcpu_create args; 293 struct nvmm_comm_page *comm; 294 int ret; 295 296 args.machid = mach->machid; 297 args.cpuid = cpuid; 298 299 ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_CREATE, &args); 300 if (ret == -1) 301 return -1; 302 303 comm = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FILE, 304 nvmm_fd, NVMM_COMM_OFF(mach->machid, cpuid)); 305 if (comm == MAP_FAILED) 306 return -1; 307 308 mach->pages[cpuid] = comm; 309 310 vcpu->cpuid = cpuid; 311 vcpu->state = &comm->state; 312 vcpu->event = &comm->event; 313 vcpu->exit = malloc(sizeof(*vcpu->exit)); 314 315 return 0; 316 } 317 318 int 319 nvmm_vcpu_destroy(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu) 320 { 321 struct nvmm_ioc_vcpu_destroy args; 322 struct nvmm_comm_page *comm; 323 int ret; 324 325 args.machid = mach->machid; 326 args.cpuid = vcpu->cpuid; 327 328 ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_DESTROY, &args); 329 if (ret == -1) 330 return -1; 331 332 comm = mach->pages[vcpu->cpuid]; 333 munmap(comm, PAGE_SIZE); 334 free(vcpu->exit); 335 336 return 0; 337 } 338 339 int 340 nvmm_vcpu_configure(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu, 341 uint64_t op, void *conf) 342 { 343 struct nvmm_ioc_vcpu_configure args; 344 int ret; 345 346 switch (op) { 347 case NVMM_VCPU_CONF_CALLBACKS: 348 memcpy(&vcpu->cbs, conf, sizeof(vcpu->cbs)); 349 return 0; 350 } 351 352 args.machid = mach->machid; 353 args.cpuid = vcpu->cpuid; 354 args.op = op; 355 args.conf = conf; 356 357 ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_CONFIGURE, &args); 358 if (ret == -1) 359 return -1; 360 361 return 0; 362 } 363 364 int 365 nvmm_vcpu_setstate(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu, 366 uint64_t flags) 367 { 368 struct nvmm_comm_page *comm; 369 370 comm = mach->pages[vcpu->cpuid]; 371 comm->state_commit |= flags; 372 comm->state_cached |= flags; 373 374 return 0; 375 } 376 377 int 378 nvmm_vcpu_getstate(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu, 379 uint64_t flags) 380 { 381 struct nvmm_ioc_vcpu_getstate args; 382 struct nvmm_comm_page *comm; 383 int ret; 384 385 comm = mach->pages[vcpu->cpuid]; 386 387 if (__predict_true((flags & ~comm->state_cached) == 0)) { 388 return 0; 389 } 390 comm->state_wanted = flags & ~comm->state_cached; 391 392 args.machid = mach->machid; 393 args.cpuid = vcpu->cpuid; 394 395 ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_GETSTATE, &args); 396 if (ret == -1) 397 return -1; 398 399 return 0; 400 } 401 402 int 403 nvmm_vcpu_inject(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu) 404 { 405 struct nvmm_comm_page *comm; 406 407 comm = mach->pages[vcpu->cpuid]; 408 comm->event_commit = true; 409 410 return 0; 411 } 412 413 int 414 nvmm_vcpu_run(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu) 415 { 416 struct nvmm_ioc_vcpu_run args; 417 int ret; 418 419 args.machid = mach->machid; 420 args.cpuid = vcpu->cpuid; 421 memset(&args.exit, 0, sizeof(args.exit)); 422 423 ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_RUN, &args); 424 if (ret == -1) 425 return -1; 426 427 /* No comm support yet, just copy. */ 428 memcpy(vcpu->exit, &args.exit, sizeof(args.exit)); 429 430 return 0; 431 } 432 433 int 434 nvmm_gpa_map(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa, 435 size_t size, int prot) 436 { 437 struct nvmm_ioc_gpa_map args; 438 int ret; 439 440 ret = __area_add(mach, hva, gpa, size, prot); 441 if (ret == -1) 442 return -1; 443 444 args.machid = mach->machid; 445 args.hva = hva; 446 args.gpa = gpa; 447 args.size = size; 448 args.prot = prot; 449 450 ret = ioctl(nvmm_fd, NVMM_IOC_GPA_MAP, &args); 451 if (ret == -1) { 452 /* Can't recover. */ 453 abort(); 454 } 455 456 return 0; 457 } 458 459 int 460 nvmm_gpa_unmap(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa, 461 size_t size) 462 { 463 struct nvmm_ioc_gpa_unmap args; 464 int ret; 465 466 ret = __area_delete(mach, hva, gpa, size); 467 if (ret == -1) 468 return -1; 469 470 args.machid = mach->machid; 471 args.gpa = gpa; 472 args.size = size; 473 474 ret = ioctl(nvmm_fd, NVMM_IOC_GPA_UNMAP, &args); 475 if (ret == -1) { 476 /* Can't recover. */ 477 abort(); 478 } 479 480 return 0; 481 } 482 483 int 484 nvmm_hva_map(struct nvmm_machine *mach, uintptr_t hva, size_t size) 485 { 486 struct nvmm_ioc_hva_map args; 487 int ret; 488 489 args.machid = mach->machid; 490 args.hva = hva; 491 args.size = size; 492 493 ret = ioctl(nvmm_fd, NVMM_IOC_HVA_MAP, &args); 494 if (ret == -1) 495 return -1; 496 497 return 0; 498 } 499 500 int 501 nvmm_hva_unmap(struct nvmm_machine *mach, uintptr_t hva, size_t size) 502 { 503 struct nvmm_ioc_hva_unmap args; 504 int ret; 505 506 args.machid = mach->machid; 507 args.hva = hva; 508 args.size = size; 509 510 ret = ioctl(nvmm_fd, NVMM_IOC_HVA_UNMAP, &args); 511 if (ret == -1) 512 return -1; 513 514 return 0; 515 } 516 517 /* 518 * nvmm_gva_to_gpa(): architecture-specific. 519 */ 520 521 int 522 nvmm_gpa_to_hva(struct nvmm_machine *mach, gpaddr_t gpa, uintptr_t *hva, 523 nvmm_prot_t *prot) 524 { 525 area_list_t *areas = mach->areas; 526 area_t *ent; 527 528 LIST_FOREACH(ent, areas, list) { 529 if (gpa >= ent->gpa && gpa < ent->gpa + ent->size) { 530 *hva = ent->hva + (gpa - ent->gpa); 531 *prot = ent->prot; 532 return 0; 533 } 534 } 535 536 errno = ENOENT; 537 return -1; 538 } 539 540 /* 541 * nvmm_assist_io(): architecture-specific. 542 */ 543 544 /* 545 * nvmm_assist_mem(): architecture-specific. 546 */ 547 548 int 549 nvmm_ctl(int op, void *data, size_t size) 550 { 551 struct nvmm_ioc_ctl args; 552 int ret; 553 554 args.op = op; 555 args.data = data; 556 args.size = size; 557 558 ret = ioctl(nvmm_fd, NVMM_IOC_CTL, &args); 559 if (ret == -1) 560 return -1; 561 562 return 0; 563 } 564