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 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 static int 160 nvmm_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 175 int 176 nvmm_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 194 int 195 nvmm_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 233 int 234 nvmm_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 251 int 252 nvmm_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 274 int 275 nvmm_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 298 int 299 nvmm_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 318 int 319 nvmm_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 336 int 337 nvmm_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 361 out: 362 nvmm_arch_copystate(state, &comm->state, flags); 363 return 0; 364 } 365 366 int 367 nvmm_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 383 int 384 nvmm_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 403 int 404 nvmm_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 429 int 430 nvmm_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 453 int 454 nvmm_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 470 int 471 nvmm_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 491 int 492 nvmm_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 518 int 519 nvmm_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