libnvmm.c revision 1.5
1/* $NetBSD: libnvmm.c,v 1.5 2018/12/15 13:39:43 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 44#include "nvmm.h" 45 46typedef struct __area { 47 LIST_ENTRY(__area) list; 48 gpaddr_t gpa; 49 uintptr_t hva; 50 size_t size; 51} area_t; 52 53typedef LIST_HEAD(, __area) area_list_t; 54 55static int nvmm_fd = -1; 56static size_t nvmm_page_size = 0; 57 58/* -------------------------------------------------------------------------- */ 59 60static bool 61__area_isvalid(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa, 62 size_t size) 63{ 64 area_list_t *areas = mach->areas; 65 area_t *ent; 66 67 LIST_FOREACH(ent, areas, list) { 68 /* Collision on GPA */ 69 if (gpa >= ent->gpa && gpa < ent->gpa + ent->size) { 70 return false; 71 } 72 if (gpa + size > ent->gpa && 73 gpa + size <= ent->gpa + ent->size) { 74 return false; 75 } 76 if (gpa <= ent->gpa && gpa + size >= ent->gpa + ent->size) { 77 return false; 78 } 79 } 80 81 return true; 82} 83 84static int 85__area_add(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa, size_t size) 86{ 87 area_list_t *areas = mach->areas; 88 area_t *area; 89 90 if (!__area_isvalid(mach, hva, gpa, size)) { 91 errno = EINVAL; 92 return -1; 93 } 94 95 area = malloc(sizeof(*area)); 96 if (area == NULL) 97 return -1; 98 area->gpa = gpa; 99 area->hva = hva; 100 area->size = size; 101 102 LIST_INSERT_HEAD(areas, area, list); 103 104 return 0; 105} 106 107static int 108__area_delete(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa, 109 size_t size) 110{ 111 area_list_t *areas = mach->areas; 112 area_t *ent, *nxt; 113 114 LIST_FOREACH_SAFE(ent, areas, list, nxt) { 115 if (hva == ent->hva && gpa == ent->gpa && size == ent->size) { 116 LIST_REMOVE(ent, list); 117 free(ent); 118 return 0; 119 } 120 } 121 122 return -1; 123} 124 125static void 126__area_remove_all(struct nvmm_machine *mach) 127{ 128 area_list_t *areas = mach->areas; 129 area_t *ent; 130 131 while ((ent = LIST_FIRST(areas)) != NULL) { 132 LIST_REMOVE(ent, list); 133 free(ent); 134 } 135 136 free(areas); 137} 138 139/* -------------------------------------------------------------------------- */ 140 141static int 142nvmm_init(void) 143{ 144 if (nvmm_fd != -1) 145 return 0; 146 nvmm_fd = open("/dev/nvmm", O_RDWR); 147 if (nvmm_fd == -1) 148 return -1; 149 nvmm_page_size = sysconf(_SC_PAGESIZE); 150 return 0; 151} 152 153int 154nvmm_capability(struct nvmm_capability *cap) 155{ 156 struct nvmm_ioc_capability args; 157 int ret; 158 159 if (nvmm_init() == -1) { 160 return -1; 161 } 162 163 ret = ioctl(nvmm_fd, NVMM_IOC_CAPABILITY, &args); 164 if (ret == -1) 165 return -1; 166 167 memcpy(cap, &args.cap, sizeof(args.cap)); 168 169 return 0; 170} 171 172int 173nvmm_machine_create(struct nvmm_machine *mach) 174{ 175 struct nvmm_ioc_machine_create args; 176 area_list_t *areas; 177 int ret; 178 179 if (nvmm_init() == -1) { 180 return -1; 181 } 182 183 areas = calloc(1, sizeof(*areas)); 184 if (areas == NULL) 185 return -1; 186 187 ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_CREATE, &args); 188 if (ret == -1) { 189 free(areas); 190 return -1; 191 } 192 193 memset(mach, 0, sizeof(*mach)); 194 LIST_INIT(areas); 195 mach->areas = areas; 196 mach->machid = args.machid; 197 198 return 0; 199} 200 201int 202nvmm_machine_destroy(struct nvmm_machine *mach) 203{ 204 struct nvmm_ioc_machine_destroy args; 205 int ret; 206 207 if (nvmm_init() == -1) { 208 return -1; 209 } 210 211 args.machid = mach->machid; 212 213 ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_DESTROY, &args); 214 if (ret == -1) 215 return -1; 216 217 __area_remove_all(mach); 218 219 return 0; 220} 221 222int 223nvmm_machine_configure(struct nvmm_machine *mach, uint64_t op, void *conf) 224{ 225 struct nvmm_ioc_machine_configure args; 226 int ret; 227 228 if (nvmm_init() == -1) { 229 return -1; 230 } 231 232 args.machid = mach->machid; 233 args.op = op; 234 args.conf = conf; 235 236 ret = ioctl(nvmm_fd, NVMM_IOC_MACHINE_CONFIGURE, &args); 237 if (ret == -1) 238 return -1; 239 240 return 0; 241} 242 243int 244nvmm_vcpu_create(struct nvmm_machine *mach, nvmm_cpuid_t cpuid) 245{ 246 struct nvmm_ioc_vcpu_create args; 247 int ret; 248 249 if (nvmm_init() == -1) { 250 return -1; 251 } 252 253 args.machid = mach->machid; 254 args.cpuid = cpuid; 255 256 ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_CREATE, &args); 257 if (ret == -1) 258 return -1; 259 260 return 0; 261} 262 263int 264nvmm_vcpu_destroy(struct nvmm_machine *mach, nvmm_cpuid_t cpuid) 265{ 266 struct nvmm_ioc_vcpu_destroy args; 267 int ret; 268 269 if (nvmm_init() == -1) { 270 return -1; 271 } 272 273 args.machid = mach->machid; 274 args.cpuid = cpuid; 275 276 ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_DESTROY, &args); 277 if (ret == -1) 278 return -1; 279 280 return 0; 281} 282 283int 284nvmm_vcpu_setstate(struct nvmm_machine *mach, nvmm_cpuid_t cpuid, 285 void *state, uint64_t flags) 286{ 287 struct nvmm_ioc_vcpu_setstate args; 288 int ret; 289 290 if (nvmm_init() == -1) { 291 return -1; 292 } 293 294 args.machid = mach->machid; 295 args.cpuid = cpuid; 296 args.state = state; 297 args.flags = flags; 298 299 ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_SETSTATE, &args); 300 if (ret == -1) 301 return -1; 302 303 return 0; 304} 305 306int 307nvmm_vcpu_getstate(struct nvmm_machine *mach, nvmm_cpuid_t cpuid, 308 void *state, uint64_t flags) 309{ 310 struct nvmm_ioc_vcpu_getstate args; 311 int ret; 312 313 if (nvmm_init() == -1) { 314 return -1; 315 } 316 317 args.machid = mach->machid; 318 args.cpuid = cpuid; 319 args.state = state; 320 args.flags = flags; 321 322 ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_GETSTATE, &args); 323 if (ret == -1) 324 return -1; 325 326 return 0; 327} 328 329int 330nvmm_vcpu_inject(struct nvmm_machine *mach, nvmm_cpuid_t cpuid, 331 struct nvmm_event *event) 332{ 333 struct nvmm_ioc_vcpu_inject args; 334 int ret; 335 336 if (nvmm_init() == -1) { 337 return -1; 338 } 339 340 args.machid = mach->machid; 341 args.cpuid = cpuid; 342 memcpy(&args.event, event, sizeof(args.event)); 343 344 ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_INJECT, &args); 345 if (ret == -1) 346 return -1; 347 348 return 0; 349} 350 351int 352nvmm_vcpu_run(struct nvmm_machine *mach, nvmm_cpuid_t cpuid, 353 struct nvmm_exit *exit) 354{ 355 struct nvmm_ioc_vcpu_run args; 356 int ret; 357 358 if (nvmm_init() == -1) { 359 return -1; 360 } 361 362 args.machid = mach->machid; 363 args.cpuid = cpuid; 364 memset(&args.exit, 0, sizeof(args.exit)); 365 366 ret = ioctl(nvmm_fd, NVMM_IOC_VCPU_RUN, &args); 367 if (ret == -1) 368 return -1; 369 370 memcpy(exit, &args.exit, sizeof(args.exit)); 371 372 return 0; 373} 374 375int 376nvmm_gpa_map(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa, 377 size_t size, int flags) 378{ 379 struct nvmm_ioc_gpa_map args; 380 int ret; 381 382 if (nvmm_init() == -1) { 383 return -1; 384 } 385 386 ret = __area_add(mach, hva, gpa, size); 387 if (ret == -1) 388 return -1; 389 390 args.machid = mach->machid; 391 args.hva = hva; 392 args.gpa = gpa; 393 args.size = size; 394 args.flags = flags; 395 396 ret = ioctl(nvmm_fd, NVMM_IOC_GPA_MAP, &args); 397 if (ret == -1) { 398 /* Can't recover. */ 399 abort(); 400 } 401 402 return 0; 403} 404 405int 406nvmm_gpa_unmap(struct nvmm_machine *mach, uintptr_t hva, gpaddr_t gpa, 407 size_t size) 408{ 409 struct nvmm_ioc_gpa_unmap args; 410 int ret; 411 412 if (nvmm_init() == -1) { 413 return -1; 414 } 415 416 ret = __area_delete(mach, hva, gpa, size); 417 if (ret == -1) 418 return -1; 419 420 args.machid = mach->machid; 421 args.gpa = gpa; 422 args.size = size; 423 424 ret = ioctl(nvmm_fd, NVMM_IOC_GPA_UNMAP, &args); 425 if (ret == -1) { 426 /* Can't recover. */ 427 abort(); 428 } 429 430 return 0; 431} 432 433int 434nvmm_hva_map(struct nvmm_machine *mach, uintptr_t hva, size_t size) 435{ 436 struct nvmm_ioc_hva_map args; 437 int ret; 438 439 if (nvmm_init() == -1) { 440 return -1; 441 } 442 443 args.machid = mach->machid; 444 args.hva = hva; 445 args.size = size; 446 447 ret = ioctl(nvmm_fd, NVMM_IOC_HVA_MAP, &args); 448 if (ret == -1) 449 return -1; 450 451 return 0; 452} 453 454int 455nvmm_hva_unmap(struct nvmm_machine *mach, uintptr_t hva, size_t size) 456{ 457 struct nvmm_ioc_hva_map args; 458 int ret; 459 460 if (nvmm_init() == -1) { 461 return -1; 462 } 463 464 args.machid = mach->machid; 465 args.hva = hva; 466 args.size = size; 467 468 ret = ioctl(nvmm_fd, NVMM_IOC_HVA_MAP, &args); 469 if (ret == -1) 470 return -1; 471 472 return 0; 473} 474 475/* 476 * nvmm_gva_to_gpa(): architecture-specific. 477 */ 478 479int 480nvmm_gpa_to_hva(struct nvmm_machine *mach, gpaddr_t gpa, uintptr_t *hva) 481{ 482 area_list_t *areas = mach->areas; 483 area_t *ent; 484 485 if (gpa % nvmm_page_size != 0) { 486 errno = EINVAL; 487 return -1; 488 } 489 490 LIST_FOREACH(ent, areas, list) { 491 if (gpa >= ent->gpa && gpa < ent->gpa + ent->size) { 492 *hva = ent->hva + (gpa - ent->gpa); 493 return 0; 494 } 495 } 496 497 errno = ENOENT; 498 return -1; 499} 500 501/* 502 * nvmm_assist_io(): architecture-specific. 503 */ 504