1 /*- 2 * Copyright (c) 2011 NetApp, Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 29 /*- 30 * Copyright (c) 2011 Google, Inc. 31 * All rights reserved. 32 * 33 * Redistribution and use in source and binary forms, with or without 34 * modification, are permitted provided that the following conditions 35 * are met: 36 * 1. Redistributions of source code must retain the above copyright 37 * notice, this list of conditions and the following disclaimer. 38 * 2. Redistributions in binary form must reproduce the above copyright 39 * notice, this list of conditions and the following disclaimer in the 40 * documentation and/or other materials provided with the distribution. 41 * 42 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 43 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 44 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 45 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 46 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 47 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 48 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 49 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 50 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 51 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 52 * SUCH DAMAGE. 53 * 54 * $FreeBSD$ 55 */ 56 57 #include <sys/cdefs.h> 58 __FBSDID("$FreeBSD$"); 59 60 #include <sys/ioctl.h> 61 #include <sys/stat.h> 62 #include <sys/disk.h> 63 64 #include <machine/specialreg.h> 65 #include <machine/vmm.h> 66 67 #include <dirent.h> 68 #include <dlfcn.h> 69 #include <errno.h> 70 #include <fcntl.h> 71 #include <getopt.h> 72 #include <limits.h> 73 #include <stdio.h> 74 #include <stdlib.h> 75 #include <string.h> 76 #include <termios.h> 77 #include <unistd.h> 78 79 #include <vmmapi.h> 80 81 #include "userboot.h" 82 83 #define MB (1024 * 1024UL) 84 #define GB (1024 * 1024 * 1024UL) 85 #define BSP 0 86 87 static char *host_base = "/"; 88 static struct termios term, oldterm; 89 static int disk_fd = -1; 90 91 static char *vmname, *progname, *membase; 92 static uint64_t lowmem, highmem; 93 static struct vmctx *ctx; 94 95 static uint64_t gdtbase, cr3, rsp; 96 97 static void cb_exit(void *arg, int v); 98 99 /* 100 * Console i/o callbacks 101 */ 102 103 static void 104 cb_putc(void *arg, int ch) 105 { 106 char c = ch; 107 108 write(1, &c, 1); 109 } 110 111 static int 112 cb_getc(void *arg) 113 { 114 char c; 115 116 if (read(0, &c, 1) == 1) 117 return (c); 118 return (-1); 119 } 120 121 static int 122 cb_poll(void *arg) 123 { 124 int n; 125 126 if (ioctl(0, FIONREAD, &n) >= 0) 127 return (n > 0); 128 return (0); 129 } 130 131 /* 132 * Host filesystem i/o callbacks 133 */ 134 135 struct cb_file { 136 int cf_isdir; 137 size_t cf_size; 138 struct stat cf_stat; 139 union { 140 int fd; 141 DIR *dir; 142 } cf_u; 143 }; 144 145 static int 146 cb_open(void *arg, const char *filename, void **hp) 147 { 148 struct stat st; 149 struct cb_file *cf; 150 char path[PATH_MAX]; 151 152 if (!host_base) 153 return (ENOENT); 154 155 strlcpy(path, host_base, PATH_MAX); 156 if (path[strlen(path) - 1] == '/') 157 path[strlen(path) - 1] = 0; 158 strlcat(path, filename, PATH_MAX); 159 cf = malloc(sizeof(struct cb_file)); 160 if (stat(path, &cf->cf_stat) < 0) { 161 free(cf); 162 return (errno); 163 } 164 165 cf->cf_size = st.st_size; 166 if (S_ISDIR(cf->cf_stat.st_mode)) { 167 cf->cf_isdir = 1; 168 cf->cf_u.dir = opendir(path); 169 if (!cf->cf_u.dir) 170 goto out; 171 *hp = cf; 172 return (0); 173 } 174 if (S_ISREG(cf->cf_stat.st_mode)) { 175 cf->cf_isdir = 0; 176 cf->cf_u.fd = open(path, O_RDONLY); 177 if (cf->cf_u.fd < 0) 178 goto out; 179 *hp = cf; 180 return (0); 181 } 182 183 out: 184 free(cf); 185 return (EINVAL); 186 } 187 188 static int 189 cb_close(void *arg, void *h) 190 { 191 struct cb_file *cf = h; 192 193 if (cf->cf_isdir) 194 closedir(cf->cf_u.dir); 195 else 196 close(cf->cf_u.fd); 197 free(cf); 198 199 return (0); 200 } 201 202 static int 203 cb_isdir(void *arg, void *h) 204 { 205 struct cb_file *cf = h; 206 207 return (cf->cf_isdir); 208 } 209 210 static int 211 cb_read(void *arg, void *h, void *buf, size_t size, size_t *resid) 212 { 213 struct cb_file *cf = h; 214 ssize_t sz; 215 216 if (cf->cf_isdir) 217 return (EINVAL); 218 sz = read(cf->cf_u.fd, buf, size); 219 if (sz < 0) 220 return (EINVAL); 221 *resid = size - sz; 222 return (0); 223 } 224 225 static int 226 cb_readdir(void *arg, void *h, uint32_t *fileno_return, uint8_t *type_return, 227 size_t *namelen_return, char *name) 228 { 229 struct cb_file *cf = h; 230 struct dirent *dp; 231 232 if (!cf->cf_isdir) 233 return (EINVAL); 234 235 dp = readdir(cf->cf_u.dir); 236 if (!dp) 237 return (ENOENT); 238 239 /* 240 * Note: d_namlen is in the range 0..255 and therefore less 241 * than PATH_MAX so we don't need to test before copying. 242 */ 243 *fileno_return = dp->d_fileno; 244 *type_return = dp->d_type; 245 *namelen_return = dp->d_namlen; 246 memcpy(name, dp->d_name, dp->d_namlen); 247 name[dp->d_namlen] = 0; 248 249 return (0); 250 } 251 252 static int 253 cb_seek(void *arg, void *h, uint64_t offset, int whence) 254 { 255 struct cb_file *cf = h; 256 257 if (cf->cf_isdir) 258 return (EINVAL); 259 if (lseek(cf->cf_u.fd, offset, whence) < 0) 260 return (errno); 261 return (0); 262 } 263 264 static int 265 cb_stat(void *arg, void *h, int *mode, int *uid, int *gid, uint64_t *size) 266 { 267 struct cb_file *cf = h; 268 269 *mode = cf->cf_stat.st_mode; 270 *uid = cf->cf_stat.st_uid; 271 *gid = cf->cf_stat.st_gid; 272 *size = cf->cf_stat.st_size; 273 return (0); 274 } 275 276 /* 277 * Disk image i/o callbacks 278 */ 279 280 static int 281 cb_diskread(void *arg, int unit, uint64_t from, void *to, size_t size, 282 size_t *resid) 283 { 284 ssize_t n; 285 286 if (unit != 0 || disk_fd == -1) 287 return (EIO); 288 n = pread(disk_fd, to, size, from); 289 if (n < 0) 290 return (errno); 291 *resid = size - n; 292 return (0); 293 } 294 295 static int 296 cb_diskioctl(void *arg, int unit, u_long cmd, void *data) 297 { 298 struct stat sb; 299 300 if (unit != 0 || disk_fd == -1) 301 return (EBADF); 302 303 switch (cmd) { 304 case DIOCGSECTORSIZE: 305 *(u_int *)data = 512; 306 break; 307 case DIOCGMEDIASIZE: 308 if (fstat(disk_fd, &sb) == 0) 309 *(off_t *)data = sb.st_size; 310 else 311 return (ENOTTY); 312 break; 313 default: 314 return (ENOTTY); 315 } 316 317 return (0); 318 } 319 320 /* 321 * Guest virtual machine i/o callbacks 322 */ 323 static int 324 cb_copyin(void *arg, const void *from, uint64_t to, size_t size) 325 { 326 327 to &= 0x7fffffff; 328 if (to > lowmem) 329 return (EFAULT); 330 if (to + size > lowmem) 331 size = lowmem - to; 332 333 memcpy(&membase[to], from, size); 334 335 return (0); 336 } 337 338 static int 339 cb_copyout(void *arg, uint64_t from, void *to, size_t size) 340 { 341 342 from &= 0x7fffffff; 343 if (from > lowmem) 344 return (EFAULT); 345 if (from + size > lowmem) 346 size = lowmem - from; 347 348 memcpy(to, &membase[from], size); 349 350 return (0); 351 } 352 353 static void 354 cb_setreg(void *arg, int r, uint64_t v) 355 { 356 int error; 357 enum vm_reg_name vmreg; 358 359 vmreg = VM_REG_LAST; 360 361 switch (r) { 362 case 4: 363 vmreg = VM_REG_GUEST_RSP; 364 rsp = v; 365 break; 366 default: 367 break; 368 } 369 370 if (vmreg == VM_REG_LAST) { 371 printf("test_setreg(%d): not implemented\n", r); 372 cb_exit(NULL, USERBOOT_EXIT_QUIT); 373 } 374 375 error = vm_set_register(ctx, BSP, vmreg, v); 376 if (error) { 377 perror("vm_set_register"); 378 cb_exit(NULL, USERBOOT_EXIT_QUIT); 379 } 380 } 381 382 static void 383 cb_setmsr(void *arg, int r, uint64_t v) 384 { 385 int error; 386 enum vm_reg_name vmreg; 387 388 vmreg = VM_REG_LAST; 389 390 switch (r) { 391 case MSR_EFER: 392 vmreg = VM_REG_GUEST_EFER; 393 break; 394 default: 395 break; 396 } 397 398 if (vmreg == VM_REG_LAST) { 399 printf("test_setmsr(%d): not implemented\n", r); 400 cb_exit(NULL, USERBOOT_EXIT_QUIT); 401 } 402 403 error = vm_set_register(ctx, BSP, vmreg, v); 404 if (error) { 405 perror("vm_set_msr"); 406 cb_exit(NULL, USERBOOT_EXIT_QUIT); 407 } 408 } 409 410 static void 411 cb_setcr(void *arg, int r, uint64_t v) 412 { 413 int error; 414 enum vm_reg_name vmreg; 415 416 vmreg = VM_REG_LAST; 417 418 switch (r) { 419 case 0: 420 vmreg = VM_REG_GUEST_CR0; 421 break; 422 case 3: 423 vmreg = VM_REG_GUEST_CR3; 424 cr3 = v; 425 break; 426 case 4: 427 vmreg = VM_REG_GUEST_CR4; 428 break; 429 default: 430 break; 431 } 432 433 if (vmreg == VM_REG_LAST) { 434 printf("test_setcr(%d): not implemented\n", r); 435 cb_exit(NULL, USERBOOT_EXIT_QUIT); 436 } 437 438 error = vm_set_register(ctx, BSP, vmreg, v); 439 if (error) { 440 perror("vm_set_cr"); 441 cb_exit(NULL, USERBOOT_EXIT_QUIT); 442 } 443 } 444 445 static void 446 cb_setgdt(void *arg, uint64_t base, size_t size) 447 { 448 int error; 449 450 error = vm_set_desc(ctx, BSP, VM_REG_GUEST_GDTR, base, size - 1, 0); 451 if (error != 0) { 452 perror("vm_set_desc(gdt)"); 453 cb_exit(NULL, USERBOOT_EXIT_QUIT); 454 } 455 456 gdtbase = base; 457 } 458 459 static void 460 cb_exec(void *arg, uint64_t rip) 461 { 462 int error; 463 464 error = vm_setup_freebsd_registers(ctx, BSP, rip, cr3, gdtbase, rsp); 465 if (error) { 466 perror("vm_setup_freebsd_registers"); 467 cb_exit(NULL, USERBOOT_EXIT_QUIT); 468 } 469 470 cb_exit(NULL, 0); 471 } 472 473 /* 474 * Misc 475 */ 476 477 static void 478 cb_delay(void *arg, int usec) 479 { 480 481 usleep(usec); 482 } 483 484 static void 485 cb_exit(void *arg, int v) 486 { 487 488 tcsetattr(0, TCSAFLUSH, &oldterm); 489 exit(v); 490 } 491 492 static void 493 cb_getmem(void *arg, uint64_t *ret_lowmem, uint64_t *ret_highmem) 494 { 495 496 *ret_lowmem = lowmem; 497 *ret_highmem = highmem; 498 } 499 500 static const char * 501 cb_getenv(void *arg, int num) 502 { 503 int max; 504 505 static const char * var[] = { 506 "smbios.bios.vendor=BHYVE", 507 "boot_serial=1", 508 NULL 509 }; 510 511 max = sizeof(var) / sizeof(var[0]); 512 513 if (num < max) 514 return (var[num]); 515 else 516 return (NULL); 517 } 518 519 static struct loader_callbacks cb = { 520 .getc = cb_getc, 521 .putc = cb_putc, 522 .poll = cb_poll, 523 524 .open = cb_open, 525 .close = cb_close, 526 .isdir = cb_isdir, 527 .read = cb_read, 528 .readdir = cb_readdir, 529 .seek = cb_seek, 530 .stat = cb_stat, 531 532 .diskread = cb_diskread, 533 .diskioctl = cb_diskioctl, 534 535 .copyin = cb_copyin, 536 .copyout = cb_copyout, 537 .setreg = cb_setreg, 538 .setmsr = cb_setmsr, 539 .setcr = cb_setcr, 540 .setgdt = cb_setgdt, 541 .exec = cb_exec, 542 543 .delay = cb_delay, 544 .exit = cb_exit, 545 .getmem = cb_getmem, 546 547 .getenv = cb_getenv, 548 }; 549 550 static void 551 usage(void) 552 { 553 554 printf("usage: %s [-d <disk image path>] [-h <host filesystem path>] " 555 "[-m <lowmem>][-M <highmem>] " 556 "<vmname>\n", progname); 557 exit(1); 558 } 559 560 int 561 main(int argc, char** argv) 562 { 563 void *h; 564 void (*func)(struct loader_callbacks *, void *, int, int); 565 int opt, error; 566 char *disk_image; 567 568 progname = argv[0]; 569 570 lowmem = 128 * MB; 571 highmem = 0; 572 disk_image = NULL; 573 574 while ((opt = getopt(argc, argv, "d:h:m:M:")) != -1) { 575 switch (opt) { 576 case 'd': 577 disk_image = optarg; 578 break; 579 580 case 'h': 581 host_base = optarg; 582 break; 583 584 case 'm': 585 lowmem = strtoul(optarg, NULL, 0) * MB; 586 break; 587 588 case 'M': 589 highmem = strtoul(optarg, NULL, 0) * MB; 590 break; 591 592 case '?': 593 usage(); 594 } 595 } 596 597 argc -= optind; 598 argv += optind; 599 600 if (argc != 1) 601 usage(); 602 603 vmname = argv[0]; 604 605 error = vm_create(vmname); 606 if (error != 0 && errno != EEXIST) { 607 perror("vm_create"); 608 exit(1); 609 610 } 611 612 ctx = vm_open(vmname); 613 if (ctx == NULL) { 614 perror("vm_open"); 615 exit(1); 616 } 617 618 error = vm_setup_memory(ctx, 0, lowmem, &membase); 619 if (error) { 620 perror("vm_setup_memory(lowmem)"); 621 exit(1); 622 } 623 624 if (highmem != 0) { 625 error = vm_setup_memory(ctx, 4 * GB, highmem, NULL); 626 if (error) { 627 perror("vm_setup_memory(highmem)"); 628 exit(1); 629 } 630 } 631 632 tcgetattr(0, &term); 633 oldterm = term; 634 term.c_lflag &= ~(ICANON|ECHO); 635 term.c_iflag &= ~ICRNL; 636 tcsetattr(0, TCSAFLUSH, &term); 637 h = dlopen("/boot/userboot.so", RTLD_LOCAL); 638 if (!h) { 639 printf("%s\n", dlerror()); 640 return (1); 641 } 642 func = dlsym(h, "loader_main"); 643 if (!func) { 644 printf("%s\n", dlerror()); 645 return (1); 646 } 647 648 if (disk_image) { 649 disk_fd = open(disk_image, O_RDONLY); 650 } 651 func(&cb, NULL, USERBOOT_VERSION_3, disk_fd >= 0); 652 } 653