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