1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2011 NetApp, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 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 55 #include <sys/cdefs.h> 56 #include <sys/ioctl.h> 57 #include <sys/stat.h> 58 #include <sys/disk.h> 59 #include <sys/queue.h> 60 61 #include <machine/specialreg.h> 62 #include <machine/vmm.h> 63 64 #include <assert.h> 65 #include <dirent.h> 66 #include <dlfcn.h> 67 #include <errno.h> 68 #include <err.h> 69 #include <fcntl.h> 70 #include <getopt.h> 71 #include <libgen.h> 72 #include <limits.h> 73 #include <setjmp.h> 74 #include <stdio.h> 75 #include <stdlib.h> 76 #include <string.h> 77 #include <sysexits.h> 78 #include <termios.h> 79 #include <unistd.h> 80 81 #include <vmmapi.h> 82 83 #include "userboot.h" 84 85 #define MB (1024 * 1024UL) 86 #define GB (1024 * 1024 * 1024UL) 87 #define BSP 0 88 89 #define NDISKS 32 90 91 static struct termios term, oldterm; 92 static int disk_fd[NDISKS]; 93 static int ndisks; 94 static int consin_fd, consout_fd; 95 static int hostbase_fd = -1; 96 97 static void *loader_hdl; 98 static char *loader; 99 static int explicit_loader_fd; 100 static jmp_buf jb; 101 102 static char *vmname, *progname; 103 static struct vmctx *ctx; 104 static struct vcpu *vcpu; 105 106 static uint64_t gdtbase, cr3, rsp; 107 108 static void cb_exit(void *arg, int v); 109 110 /* 111 * Console i/o callbacks 112 */ 113 114 static void 115 cb_putc(void *arg __unused, int ch) 116 { 117 char c = ch; 118 119 (void) write(consout_fd, &c, 1); 120 } 121 122 static int 123 cb_getc(void *arg __unused) 124 { 125 char c; 126 127 if (read(consin_fd, &c, 1) == 1) 128 return (c); 129 return (-1); 130 } 131 132 static int 133 cb_poll(void *arg __unused) 134 { 135 int n; 136 137 if (ioctl(consin_fd, FIONREAD, &n) >= 0) 138 return (n > 0); 139 return (0); 140 } 141 142 /* 143 * Host filesystem i/o callbacks 144 */ 145 146 struct cb_file { 147 int cf_isdir; 148 size_t cf_size; 149 struct stat cf_stat; 150 union { 151 int fd; 152 DIR *dir; 153 } cf_u; 154 }; 155 156 static int 157 cb_open(void *arg __unused, const char *filename, void **hp) 158 { 159 struct cb_file *cf; 160 struct stat sb; 161 int fd, flags; 162 163 cf = NULL; 164 fd = -1; 165 flags = O_RDONLY | O_RESOLVE_BENEATH; 166 if (hostbase_fd == -1) 167 return (ENOENT); 168 169 /* Absolute paths are relative to our hostbase, chop off leading /. */ 170 if (filename[0] == '/') 171 filename++; 172 173 /* Lookup of /, use . instead. */ 174 if (filename[0] == '\0') 175 filename = "."; 176 177 if (fstatat(hostbase_fd, filename, &sb, AT_RESOLVE_BENEATH) < 0) 178 return (errno); 179 180 if (!S_ISDIR(sb.st_mode) && !S_ISREG(sb.st_mode)) 181 return (EINVAL); 182 183 if (S_ISDIR(sb.st_mode)) 184 flags |= O_DIRECTORY; 185 186 /* May be opening the root dir */ 187 fd = openat(hostbase_fd, filename, flags); 188 if (fd < 0) 189 return (errno); 190 191 cf = malloc(sizeof(struct cb_file)); 192 if (cf == NULL) { 193 close(fd); 194 return (ENOMEM); 195 } 196 197 cf->cf_stat = sb; 198 cf->cf_size = cf->cf_stat.st_size; 199 200 if (S_ISDIR(cf->cf_stat.st_mode)) { 201 cf->cf_isdir = 1; 202 cf->cf_u.dir = fdopendir(fd); 203 if (cf->cf_u.dir == NULL) { 204 close(fd); 205 free(cf); 206 return (ENOMEM); 207 } 208 } else { 209 assert(S_ISREG(cf->cf_stat.st_mode)); 210 cf->cf_isdir = 0; 211 cf->cf_u.fd = fd; 212 } 213 *hp = cf; 214 return (0); 215 } 216 217 static int 218 cb_close(void *arg __unused, void *h) 219 { 220 struct cb_file *cf = h; 221 222 if (cf->cf_isdir) 223 closedir(cf->cf_u.dir); 224 else 225 close(cf->cf_u.fd); 226 free(cf); 227 228 return (0); 229 } 230 231 static int 232 cb_isdir(void *arg __unused, void *h) 233 { 234 struct cb_file *cf = h; 235 236 return (cf->cf_isdir); 237 } 238 239 static int 240 cb_read(void *arg __unused, void *h, void *buf, size_t size, size_t *resid) 241 { 242 struct cb_file *cf = h; 243 ssize_t sz; 244 245 if (cf->cf_isdir) 246 return (EINVAL); 247 sz = read(cf->cf_u.fd, buf, size); 248 if (sz < 0) 249 return (EINVAL); 250 *resid = size - sz; 251 return (0); 252 } 253 254 static int 255 cb_readdir(void *arg __unused, void *h, uint32_t *fileno_return, 256 uint8_t *type_return, size_t *namelen_return, char *name) 257 { 258 struct cb_file *cf = h; 259 struct dirent *dp; 260 261 if (!cf->cf_isdir) 262 return (EINVAL); 263 264 dp = readdir(cf->cf_u.dir); 265 if (!dp) 266 return (ENOENT); 267 268 /* 269 * Note: d_namlen is in the range 0..255 and therefore less 270 * than PATH_MAX so we don't need to test before copying. 271 */ 272 *fileno_return = dp->d_fileno; 273 *type_return = dp->d_type; 274 *namelen_return = dp->d_namlen; 275 memcpy(name, dp->d_name, dp->d_namlen); 276 name[dp->d_namlen] = 0; 277 278 return (0); 279 } 280 281 static int 282 cb_seek(void *arg __unused, void *h, uint64_t offset, int whence) 283 { 284 struct cb_file *cf = h; 285 286 if (cf->cf_isdir) 287 return (EINVAL); 288 if (lseek(cf->cf_u.fd, offset, whence) < 0) 289 return (errno); 290 return (0); 291 } 292 293 static int 294 cb_stat(void *arg __unused, void *h, struct stat *sbp) 295 { 296 struct cb_file *cf = h; 297 298 memset(sbp, 0, sizeof(struct stat)); 299 sbp->st_mode = cf->cf_stat.st_mode; 300 sbp->st_uid = cf->cf_stat.st_uid; 301 sbp->st_gid = cf->cf_stat.st_gid; 302 sbp->st_size = cf->cf_stat.st_size; 303 sbp->st_mtime = cf->cf_stat.st_mtime; 304 sbp->st_dev = cf->cf_stat.st_dev; 305 sbp->st_ino = cf->cf_stat.st_ino; 306 307 return (0); 308 } 309 310 /* 311 * Disk image i/o callbacks 312 */ 313 314 static int 315 cb_diskread(void *arg __unused, int unit, uint64_t from, void *to, size_t size, 316 size_t *resid) 317 { 318 ssize_t n; 319 320 if (unit < 0 || unit >= ndisks) 321 return (EIO); 322 n = pread(disk_fd[unit], to, size, from); 323 if (n < 0) 324 return (errno); 325 *resid = size - n; 326 return (0); 327 } 328 329 static int 330 cb_diskwrite(void *arg __unused, int unit, uint64_t offset, void *src, 331 size_t size, size_t *resid) 332 { 333 ssize_t n; 334 335 if (unit < 0 || unit >= ndisks) 336 return (EIO); 337 n = pwrite(disk_fd[unit], src, size, offset); 338 if (n < 0) 339 return (errno); 340 *resid = size - n; 341 return (0); 342 } 343 344 static int 345 cb_diskioctl(void *arg __unused, int unit, u_long cmd, void *data) 346 { 347 struct stat sb; 348 349 if (unit < 0 || unit >= ndisks) 350 return (EBADF); 351 352 switch (cmd) { 353 case DIOCGSECTORSIZE: 354 *(u_int *)data = 512; 355 break; 356 case DIOCGMEDIASIZE: 357 if (fstat(disk_fd[unit], &sb) != 0) 358 return (ENOTTY); 359 if (S_ISCHR(sb.st_mode) && 360 ioctl(disk_fd[unit], DIOCGMEDIASIZE, &sb.st_size) != 0) 361 return (ENOTTY); 362 *(off_t *)data = sb.st_size; 363 break; 364 default: 365 return (ENOTTY); 366 } 367 368 return (0); 369 } 370 371 /* 372 * Guest virtual machine i/o callbacks 373 */ 374 static int 375 cb_copyin(void *arg __unused, const void *from, uint64_t to, size_t size) 376 { 377 char *ptr; 378 379 to &= 0x7fffffff; 380 381 ptr = vm_map_gpa(ctx, to, size); 382 if (ptr == NULL) 383 return (EFAULT); 384 385 memcpy(ptr, from, size); 386 return (0); 387 } 388 389 static int 390 cb_copyout(void *arg __unused, uint64_t from, void *to, size_t size) 391 { 392 char *ptr; 393 394 from &= 0x7fffffff; 395 396 ptr = vm_map_gpa(ctx, from, size); 397 if (ptr == NULL) 398 return (EFAULT); 399 400 memcpy(to, ptr, size); 401 return (0); 402 } 403 404 static void 405 cb_setreg(void *arg __unused, int r, uint64_t v) 406 { 407 int error; 408 enum vm_reg_name vmreg; 409 410 vmreg = VM_REG_LAST; 411 412 switch (r) { 413 case 4: 414 vmreg = VM_REG_GUEST_RSP; 415 rsp = v; 416 break; 417 default: 418 break; 419 } 420 421 if (vmreg == VM_REG_LAST) { 422 printf("test_setreg(%d): not implemented\n", r); 423 cb_exit(NULL, USERBOOT_EXIT_QUIT); 424 } 425 426 error = vm_set_register(vcpu, vmreg, v); 427 if (error) { 428 perror("vm_set_register"); 429 cb_exit(NULL, USERBOOT_EXIT_QUIT); 430 } 431 } 432 433 static void 434 cb_setmsr(void *arg __unused, int r, uint64_t v) 435 { 436 int error; 437 enum vm_reg_name vmreg; 438 439 vmreg = VM_REG_LAST; 440 441 switch (r) { 442 case MSR_EFER: 443 vmreg = VM_REG_GUEST_EFER; 444 break; 445 default: 446 break; 447 } 448 449 if (vmreg == VM_REG_LAST) { 450 printf("test_setmsr(%d): not implemented\n", r); 451 cb_exit(NULL, USERBOOT_EXIT_QUIT); 452 } 453 454 error = vm_set_register(vcpu, vmreg, v); 455 if (error) { 456 perror("vm_set_msr"); 457 cb_exit(NULL, USERBOOT_EXIT_QUIT); 458 } 459 } 460 461 static void 462 cb_setcr(void *arg __unused, int r, uint64_t v) 463 { 464 int error; 465 enum vm_reg_name vmreg; 466 467 vmreg = VM_REG_LAST; 468 469 switch (r) { 470 case 0: 471 vmreg = VM_REG_GUEST_CR0; 472 break; 473 case 3: 474 vmreg = VM_REG_GUEST_CR3; 475 cr3 = v; 476 break; 477 case 4: 478 vmreg = VM_REG_GUEST_CR4; 479 break; 480 default: 481 break; 482 } 483 484 if (vmreg == VM_REG_LAST) { 485 printf("test_setcr(%d): not implemented\n", r); 486 cb_exit(NULL, USERBOOT_EXIT_QUIT); 487 } 488 489 error = vm_set_register(vcpu, vmreg, v); 490 if (error) { 491 perror("vm_set_cr"); 492 cb_exit(NULL, USERBOOT_EXIT_QUIT); 493 } 494 } 495 496 static void 497 cb_setgdt(void *arg __unused, uint64_t base, size_t size) 498 { 499 int error; 500 501 error = vm_set_desc(vcpu, VM_REG_GUEST_GDTR, base, size - 1, 0); 502 if (error != 0) { 503 perror("vm_set_desc(gdt)"); 504 cb_exit(NULL, USERBOOT_EXIT_QUIT); 505 } 506 507 gdtbase = base; 508 } 509 510 static void 511 cb_exec(void *arg __unused, uint64_t rip) 512 { 513 int error; 514 515 if (cr3 == 0) 516 error = vm_setup_freebsd_registers_i386(vcpu, rip, gdtbase, 517 rsp); 518 else 519 error = vm_setup_freebsd_registers(vcpu, rip, cr3, gdtbase, 520 rsp); 521 if (error) { 522 perror("vm_setup_freebsd_registers"); 523 cb_exit(NULL, USERBOOT_EXIT_QUIT); 524 } 525 526 cb_exit(NULL, 0); 527 } 528 529 /* 530 * Misc 531 */ 532 533 static void 534 cb_delay(void *arg __unused, int usec) 535 { 536 537 usleep(usec); 538 } 539 540 static void 541 cb_exit(void *arg __unused, int v) 542 { 543 544 tcsetattr(consout_fd, TCSAFLUSH, &oldterm); 545 exit(v); 546 } 547 548 static void 549 cb_getmem(void *arg __unused, uint64_t *ret_lowmem, uint64_t *ret_highmem) 550 { 551 552 *ret_lowmem = vm_get_lowmem_size(ctx); 553 *ret_highmem = vm_get_highmem_size(ctx); 554 } 555 556 struct env { 557 char *str; /* name=value */ 558 SLIST_ENTRY(env) next; 559 }; 560 561 static SLIST_HEAD(envhead, env) envhead; 562 563 static void 564 addenv(const char *str) 565 { 566 struct env *env; 567 568 env = malloc(sizeof(struct env)); 569 if (env == NULL) 570 err(EX_OSERR, "malloc"); 571 env->str = strdup(str); 572 if (env->str == NULL) 573 err(EX_OSERR, "strdup"); 574 SLIST_INSERT_HEAD(&envhead, env, next); 575 } 576 577 static char * 578 cb_getenv(void *arg __unused, int num) 579 { 580 int i; 581 struct env *env; 582 583 i = 0; 584 SLIST_FOREACH(env, &envhead, next) { 585 if (i == num) 586 return (env->str); 587 i++; 588 } 589 590 return (NULL); 591 } 592 593 static int 594 cb_vm_set_register(void *arg __unused, int vcpuid, int reg, uint64_t val) 595 { 596 597 assert(vcpuid == BSP); 598 return (vm_set_register(vcpu, reg, val)); 599 } 600 601 static int 602 cb_vm_set_desc(void *arg __unused, int vcpuid, int reg, uint64_t base, 603 u_int limit, u_int access) 604 { 605 606 assert(vcpuid == BSP); 607 return (vm_set_desc(vcpu, reg, base, limit, access)); 608 } 609 610 static void 611 cb_swap_interpreter(void *arg __unused, const char *interp_req) 612 { 613 614 /* 615 * If the user specified a loader but we detected a mismatch, we should 616 * not try to pivot to a different loader on them. 617 */ 618 free(loader); 619 if (explicit_loader_fd != -1) { 620 perror("requested loader interpreter does not match guest userboot"); 621 cb_exit(NULL, 1); 622 } 623 if (interp_req == NULL || *interp_req == '\0') { 624 perror("guest failed to request an interpreter"); 625 cb_exit(NULL, 1); 626 } 627 628 if (asprintf(&loader, "userboot_%s.so", interp_req) == -1) 629 err(EX_OSERR, "malloc"); 630 longjmp(jb, 1); 631 } 632 633 static struct loader_callbacks cb = { 634 .getc = cb_getc, 635 .putc = cb_putc, 636 .poll = cb_poll, 637 638 .open = cb_open, 639 .close = cb_close, 640 .isdir = cb_isdir, 641 .read = cb_read, 642 .readdir = cb_readdir, 643 .seek = cb_seek, 644 .stat = cb_stat, 645 646 .diskread = cb_diskread, 647 .diskwrite = cb_diskwrite, 648 .diskioctl = cb_diskioctl, 649 650 .copyin = cb_copyin, 651 .copyout = cb_copyout, 652 .setreg = cb_setreg, 653 .setmsr = cb_setmsr, 654 .setcr = cb_setcr, 655 .setgdt = cb_setgdt, 656 .exec = cb_exec, 657 658 .delay = cb_delay, 659 .exit = cb_exit, 660 .getmem = cb_getmem, 661 662 .getenv = cb_getenv, 663 664 /* Version 4 additions */ 665 .vm_set_register = cb_vm_set_register, 666 .vm_set_desc = cb_vm_set_desc, 667 668 /* Version 5 additions */ 669 .swap_interpreter = cb_swap_interpreter, 670 }; 671 672 static int 673 altcons_open(char *path) 674 { 675 struct stat sb; 676 int err; 677 int fd; 678 679 /* 680 * Allow stdio to be passed in so that the same string 681 * can be used for the bhyveload console and bhyve com-port 682 * parameters 683 */ 684 if (!strcmp(path, "stdio")) 685 return (0); 686 687 err = stat(path, &sb); 688 if (err == 0) { 689 if (!S_ISCHR(sb.st_mode)) 690 err = ENOTSUP; 691 else { 692 fd = open(path, O_RDWR | O_NONBLOCK); 693 if (fd < 0) 694 err = errno; 695 else 696 consin_fd = consout_fd = fd; 697 } 698 } 699 700 return (err); 701 } 702 703 static int 704 disk_open(char *path) 705 { 706 int fd; 707 708 if (ndisks >= NDISKS) 709 return (ERANGE); 710 711 fd = open(path, O_RDWR); 712 if (fd < 0) 713 return (errno); 714 715 disk_fd[ndisks] = fd; 716 ndisks++; 717 718 return (0); 719 } 720 721 static void 722 usage(void) 723 { 724 725 fprintf(stderr, 726 "usage: %s [-S][-c <console-device>] [-d <disk-path>] [-e <name=value>]\n" 727 " %*s [-h <host-path>] [-m memsize[K|k|M|m|G|g|T|t]] <vmname>\n", 728 progname, 729 (int)strlen(progname), ""); 730 exit(1); 731 } 732 733 static void 734 hostbase_open(const char *base) 735 { 736 737 if (hostbase_fd != -1) 738 close(hostbase_fd); 739 hostbase_fd = open(base, O_DIRECTORY | O_PATH); 740 if (hostbase_fd == -1) 741 err(EX_OSERR, "open"); 742 } 743 744 static void 745 loader_open(int bootfd) 746 { 747 int fd; 748 749 if (loader == NULL) { 750 loader = strdup("userboot.so"); 751 if (loader == NULL) 752 err(EX_OSERR, "malloc"); 753 } 754 755 assert(bootfd >= 0 || explicit_loader_fd >= 0); 756 if (explicit_loader_fd >= 0) 757 fd = explicit_loader_fd; 758 else 759 fd = openat(bootfd, loader, O_RDONLY | O_RESOLVE_BENEATH); 760 if (fd == -1) 761 err(EX_OSERR, "openat"); 762 763 loader_hdl = fdlopen(fd, RTLD_LOCAL); 764 if (!loader_hdl) 765 errx(EX_OSERR, "dlopen: %s", dlerror()); 766 } 767 768 int 769 main(int argc, char** argv) 770 { 771 void (*func)(struct loader_callbacks *, void *, int, int); 772 uint64_t mem_size; 773 int bootfd, opt, error, memflags, need_reinit; 774 775 bootfd = -1; 776 progname = basename(argv[0]); 777 778 memflags = 0; 779 mem_size = 256 * MB; 780 781 consin_fd = STDIN_FILENO; 782 consout_fd = STDOUT_FILENO; 783 784 while ((opt = getopt(argc, argv, "CSc:d:e:h:l:m:")) != -1) { 785 switch (opt) { 786 case 'c': 787 error = altcons_open(optarg); 788 if (error != 0) 789 errx(EX_USAGE, "Could not open '%s'", optarg); 790 break; 791 792 case 'd': 793 error = disk_open(optarg); 794 if (error != 0) 795 errx(EX_USAGE, "Could not open '%s'", optarg); 796 break; 797 798 case 'e': 799 addenv(optarg); 800 break; 801 802 case 'h': 803 hostbase_open(optarg); 804 break; 805 806 case 'l': 807 if (loader != NULL) 808 errx(EX_USAGE, "-l can only be given once"); 809 loader = strdup(optarg); 810 if (loader == NULL) 811 err(EX_OSERR, "malloc"); 812 explicit_loader_fd = open(loader, O_RDONLY); 813 if (explicit_loader_fd == -1) 814 err(EX_OSERR, "%s", loader); 815 break; 816 817 case 'm': 818 error = vm_parse_memsize(optarg, &mem_size); 819 if (error != 0) 820 errx(EX_USAGE, "Invalid memsize '%s'", optarg); 821 break; 822 case 'C': 823 memflags |= VM_MEM_F_INCORE; 824 break; 825 case 'S': 826 memflags |= VM_MEM_F_WIRED; 827 break; 828 case '?': 829 usage(); 830 } 831 } 832 833 argc -= optind; 834 argv += optind; 835 836 if (argc != 1) 837 usage(); 838 839 vmname = argv[0]; 840 841 need_reinit = 0; 842 error = vm_create(vmname); 843 if (error) { 844 if (errno != EEXIST) { 845 perror("vm_create"); 846 exit(1); 847 } 848 need_reinit = 1; 849 } 850 851 ctx = vm_open(vmname); 852 if (ctx == NULL) { 853 perror("vm_open"); 854 exit(1); 855 } 856 857 /* 858 * If we weren't given an explicit loader to use, we need to support the 859 * guest requesting a different one. 860 */ 861 if (explicit_loader_fd == -1) { 862 bootfd = open("/boot", O_DIRECTORY | O_PATH); 863 if (bootfd == -1) { 864 perror("open"); 865 exit(1); 866 } 867 } 868 869 vcpu = vm_vcpu_open(ctx, BSP); 870 871 /* 872 * setjmp in the case the guest wants to swap out interpreter, 873 * cb_swap_interpreter will swap out loader as appropriate and set 874 * need_reinit so that we end up in a clean state once again. 875 */ 876 if (setjmp(jb) != 0) { 877 dlclose(loader_hdl); 878 loader_hdl = NULL; 879 880 need_reinit = 1; 881 } 882 883 if (need_reinit) { 884 error = vm_reinit(ctx); 885 if (error) { 886 perror("vm_reinit"); 887 exit(1); 888 } 889 } 890 891 vm_set_memflags(ctx, memflags); 892 error = vm_setup_memory(ctx, mem_size, VM_MMAP_ALL); 893 if (error) { 894 perror("vm_setup_memory"); 895 exit(1); 896 } 897 898 loader_open(bootfd); 899 func = dlsym(loader_hdl, "loader_main"); 900 if (!func) { 901 printf("%s\n", dlerror()); 902 free(loader); 903 return (1); 904 } 905 906 tcgetattr(consout_fd, &term); 907 oldterm = term; 908 cfmakeraw(&term); 909 term.c_cflag |= CLOCAL; 910 911 tcsetattr(consout_fd, TCSAFLUSH, &term); 912 913 addenv("smbios.bios.vendor=BHYVE"); 914 addenv("boot_serial=1"); 915 916 func(&cb, NULL, USERBOOT_VERSION_5, ndisks); 917 918 free(loader); 919 return (0); 920 } 921