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