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