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 #include <sys/queue.h> 64 65 #include <machine/specialreg.h> 66 #include <machine/vmm.h> 67 68 #include <dirent.h> 69 #include <dlfcn.h> 70 #include <errno.h> 71 #include <err.h> 72 #include <fcntl.h> 73 #include <getopt.h> 74 #include <limits.h> 75 #include <stdio.h> 76 #include <stdlib.h> 77 #include <string.h> 78 #include <sysexits.h> 79 #include <termios.h> 80 #include <unistd.h> 81 82 #include <vmmapi.h> 83 84 #include "userboot.h" 85 86 #define MB (1024 * 1024UL) 87 #define GB (1024 * 1024 * 1024UL) 88 #define BSP 0 89 90 static char *host_base = "/"; 91 static struct termios term, oldterm; 92 static int disk_fd = -1; 93 94 static char *vmname, *progname; 95 static struct vmctx *ctx; 96 97 static uint64_t gdtbase, cr3, rsp; 98 99 static void cb_exit(void *arg, int v); 100 101 /* 102 * Console i/o callbacks 103 */ 104 105 static void 106 cb_putc(void *arg, int ch) 107 { 108 char c = ch; 109 110 write(1, &c, 1); 111 } 112 113 static int 114 cb_getc(void *arg) 115 { 116 char c; 117 118 if (read(0, &c, 1) == 1) 119 return (c); 120 return (-1); 121 } 122 123 static int 124 cb_poll(void *arg) 125 { 126 int n; 127 128 if (ioctl(0, FIONREAD, &n) >= 0) 129 return (n > 0); 130 return (0); 131 } 132 133 /* 134 * Host filesystem i/o callbacks 135 */ 136 137 struct cb_file { 138 int cf_isdir; 139 size_t cf_size; 140 struct stat cf_stat; 141 union { 142 int fd; 143 DIR *dir; 144 } cf_u; 145 }; 146 147 static int 148 cb_open(void *arg, const char *filename, void **hp) 149 { 150 struct stat st; 151 struct cb_file *cf; 152 char path[PATH_MAX]; 153 154 if (!host_base) 155 return (ENOENT); 156 157 strlcpy(path, host_base, PATH_MAX); 158 if (path[strlen(path) - 1] == '/') 159 path[strlen(path) - 1] = 0; 160 strlcat(path, filename, PATH_MAX); 161 cf = malloc(sizeof(struct cb_file)); 162 if (stat(path, &cf->cf_stat) < 0) { 163 free(cf); 164 return (errno); 165 } 166 167 cf->cf_size = st.st_size; 168 if (S_ISDIR(cf->cf_stat.st_mode)) { 169 cf->cf_isdir = 1; 170 cf->cf_u.dir = opendir(path); 171 if (!cf->cf_u.dir) 172 goto out; 173 *hp = cf; 174 return (0); 175 } 176 if (S_ISREG(cf->cf_stat.st_mode)) { 177 cf->cf_isdir = 0; 178 cf->cf_u.fd = open(path, O_RDONLY); 179 if (cf->cf_u.fd < 0) 180 goto out; 181 *hp = cf; 182 return (0); 183 } 184 185 out: 186 free(cf); 187 return (EINVAL); 188 } 189 190 static int 191 cb_close(void *arg, void *h) 192 { 193 struct cb_file *cf = h; 194 195 if (cf->cf_isdir) 196 closedir(cf->cf_u.dir); 197 else 198 close(cf->cf_u.fd); 199 free(cf); 200 201 return (0); 202 } 203 204 static int 205 cb_isdir(void *arg, void *h) 206 { 207 struct cb_file *cf = h; 208 209 return (cf->cf_isdir); 210 } 211 212 static int 213 cb_read(void *arg, void *h, void *buf, size_t size, size_t *resid) 214 { 215 struct cb_file *cf = h; 216 ssize_t sz; 217 218 if (cf->cf_isdir) 219 return (EINVAL); 220 sz = read(cf->cf_u.fd, buf, size); 221 if (sz < 0) 222 return (EINVAL); 223 *resid = size - sz; 224 return (0); 225 } 226 227 static int 228 cb_readdir(void *arg, void *h, uint32_t *fileno_return, uint8_t *type_return, 229 size_t *namelen_return, char *name) 230 { 231 struct cb_file *cf = h; 232 struct dirent *dp; 233 234 if (!cf->cf_isdir) 235 return (EINVAL); 236 237 dp = readdir(cf->cf_u.dir); 238 if (!dp) 239 return (ENOENT); 240 241 /* 242 * Note: d_namlen is in the range 0..255 and therefore less 243 * than PATH_MAX so we don't need to test before copying. 244 */ 245 *fileno_return = dp->d_fileno; 246 *type_return = dp->d_type; 247 *namelen_return = dp->d_namlen; 248 memcpy(name, dp->d_name, dp->d_namlen); 249 name[dp->d_namlen] = 0; 250 251 return (0); 252 } 253 254 static int 255 cb_seek(void *arg, void *h, uint64_t offset, int whence) 256 { 257 struct cb_file *cf = h; 258 259 if (cf->cf_isdir) 260 return (EINVAL); 261 if (lseek(cf->cf_u.fd, offset, whence) < 0) 262 return (errno); 263 return (0); 264 } 265 266 static int 267 cb_stat(void *arg, void *h, int *mode, int *uid, int *gid, uint64_t *size) 268 { 269 struct cb_file *cf = h; 270 271 *mode = cf->cf_stat.st_mode; 272 *uid = cf->cf_stat.st_uid; 273 *gid = cf->cf_stat.st_gid; 274 *size = cf->cf_stat.st_size; 275 return (0); 276 } 277 278 /* 279 * Disk image i/o callbacks 280 */ 281 282 static int 283 cb_diskread(void *arg, int unit, uint64_t from, void *to, size_t size, 284 size_t *resid) 285 { 286 ssize_t n; 287 288 if (unit != 0 || disk_fd == -1) 289 return (EIO); 290 n = pread(disk_fd, to, size, from); 291 if (n < 0) 292 return (errno); 293 *resid = size - n; 294 return (0); 295 } 296 297 static int 298 cb_diskioctl(void *arg, int unit, u_long cmd, void *data) 299 { 300 struct stat sb; 301 302 if (unit != 0 || disk_fd == -1) 303 return (EBADF); 304 305 switch (cmd) { 306 case DIOCGSECTORSIZE: 307 *(u_int *)data = 512; 308 break; 309 case DIOCGMEDIASIZE: 310 if (fstat(disk_fd, &sb) == 0) 311 *(off_t *)data = sb.st_size; 312 else 313 return (ENOTTY); 314 break; 315 default: 316 return (ENOTTY); 317 } 318 319 return (0); 320 } 321 322 /* 323 * Guest virtual machine i/o callbacks 324 */ 325 static int 326 cb_copyin(void *arg, const void *from, uint64_t to, size_t size) 327 { 328 char *ptr; 329 330 to &= 0x7fffffff; 331 332 ptr = vm_map_gpa(ctx, to, size); 333 if (ptr == NULL) 334 return (EFAULT); 335 336 memcpy(ptr, from, size); 337 return (0); 338 } 339 340 static int 341 cb_copyout(void *arg, uint64_t from, void *to, size_t size) 342 { 343 char *ptr; 344 345 from &= 0x7fffffff; 346 347 ptr = vm_map_gpa(ctx, from, size); 348 if (ptr == NULL) 349 return (EFAULT); 350 351 memcpy(to, ptr, size); 352 return (0); 353 } 354 355 static void 356 cb_setreg(void *arg, int r, uint64_t v) 357 { 358 int error; 359 enum vm_reg_name vmreg; 360 361 vmreg = VM_REG_LAST; 362 363 switch (r) { 364 case 4: 365 vmreg = VM_REG_GUEST_RSP; 366 rsp = v; 367 break; 368 default: 369 break; 370 } 371 372 if (vmreg == VM_REG_LAST) { 373 printf("test_setreg(%d): not implemented\n", r); 374 cb_exit(NULL, USERBOOT_EXIT_QUIT); 375 } 376 377 error = vm_set_register(ctx, BSP, vmreg, v); 378 if (error) { 379 perror("vm_set_register"); 380 cb_exit(NULL, USERBOOT_EXIT_QUIT); 381 } 382 } 383 384 static void 385 cb_setmsr(void *arg, int r, uint64_t v) 386 { 387 int error; 388 enum vm_reg_name vmreg; 389 390 vmreg = VM_REG_LAST; 391 392 switch (r) { 393 case MSR_EFER: 394 vmreg = VM_REG_GUEST_EFER; 395 break; 396 default: 397 break; 398 } 399 400 if (vmreg == VM_REG_LAST) { 401 printf("test_setmsr(%d): not implemented\n", r); 402 cb_exit(NULL, USERBOOT_EXIT_QUIT); 403 } 404 405 error = vm_set_register(ctx, BSP, vmreg, v); 406 if (error) { 407 perror("vm_set_msr"); 408 cb_exit(NULL, USERBOOT_EXIT_QUIT); 409 } 410 } 411 412 static void 413 cb_setcr(void *arg, int r, uint64_t v) 414 { 415 int error; 416 enum vm_reg_name vmreg; 417 418 vmreg = VM_REG_LAST; 419 420 switch (r) { 421 case 0: 422 vmreg = VM_REG_GUEST_CR0; 423 break; 424 case 3: 425 vmreg = VM_REG_GUEST_CR3; 426 cr3 = v; 427 break; 428 case 4: 429 vmreg = VM_REG_GUEST_CR4; 430 break; 431 default: 432 break; 433 } 434 435 if (vmreg == VM_REG_LAST) { 436 printf("test_setcr(%d): not implemented\n", r); 437 cb_exit(NULL, USERBOOT_EXIT_QUIT); 438 } 439 440 error = vm_set_register(ctx, BSP, vmreg, v); 441 if (error) { 442 perror("vm_set_cr"); 443 cb_exit(NULL, USERBOOT_EXIT_QUIT); 444 } 445 } 446 447 static void 448 cb_setgdt(void *arg, uint64_t base, size_t size) 449 { 450 int error; 451 452 error = vm_set_desc(ctx, BSP, VM_REG_GUEST_GDTR, base, size - 1, 0); 453 if (error != 0) { 454 perror("vm_set_desc(gdt)"); 455 cb_exit(NULL, USERBOOT_EXIT_QUIT); 456 } 457 458 gdtbase = base; 459 } 460 461 static void 462 cb_exec(void *arg, uint64_t rip) 463 { 464 int error; 465 466 error = vm_setup_freebsd_registers(ctx, BSP, rip, cr3, gdtbase, rsp); 467 if (error) { 468 perror("vm_setup_freebsd_registers"); 469 cb_exit(NULL, USERBOOT_EXIT_QUIT); 470 } 471 472 cb_exit(NULL, 0); 473 } 474 475 /* 476 * Misc 477 */ 478 479 static void 480 cb_delay(void *arg, int usec) 481 { 482 483 usleep(usec); 484 } 485 486 static void 487 cb_exit(void *arg, int v) 488 { 489 490 tcsetattr(0, TCSAFLUSH, &oldterm); 491 exit(v); 492 } 493 494 static void 495 cb_getmem(void *arg, uint64_t *ret_lowmem, uint64_t *ret_highmem) 496 { 497 498 vm_get_memory_seg(ctx, 0, ret_lowmem, NULL); 499 vm_get_memory_seg(ctx, 4 * GB, ret_highmem, NULL); 500 } 501 502 struct env { 503 const char *str; /* name=value */ 504 SLIST_ENTRY(env) next; 505 }; 506 507 static SLIST_HEAD(envhead, env) envhead; 508 509 static void 510 addenv(const char *str) 511 { 512 struct env *env; 513 514 env = malloc(sizeof(struct env)); 515 env->str = str; 516 SLIST_INSERT_HEAD(&envhead, env, next); 517 } 518 519 static const char * 520 cb_getenv(void *arg, int num) 521 { 522 int i; 523 struct env *env; 524 525 i = 0; 526 SLIST_FOREACH(env, &envhead, next) { 527 if (i == num) 528 return (env->str); 529 i++; 530 } 531 532 return (NULL); 533 } 534 535 static struct loader_callbacks cb = { 536 .getc = cb_getc, 537 .putc = cb_putc, 538 .poll = cb_poll, 539 540 .open = cb_open, 541 .close = cb_close, 542 .isdir = cb_isdir, 543 .read = cb_read, 544 .readdir = cb_readdir, 545 .seek = cb_seek, 546 .stat = cb_stat, 547 548 .diskread = cb_diskread, 549 .diskioctl = cb_diskioctl, 550 551 .copyin = cb_copyin, 552 .copyout = cb_copyout, 553 .setreg = cb_setreg, 554 .setmsr = cb_setmsr, 555 .setcr = cb_setcr, 556 .setgdt = cb_setgdt, 557 .exec = cb_exec, 558 559 .delay = cb_delay, 560 .exit = cb_exit, 561 .getmem = cb_getmem, 562 563 .getenv = cb_getenv, 564 }; 565 566 static void 567 usage(void) 568 { 569 570 fprintf(stderr, 571 "usage: %s [-m mem-size][-d <disk-path>] [-h <host-path>] " 572 "[-e <name=value>] <vmname>\n", progname); 573 exit(1); 574 } 575 576 int 577 main(int argc, char** argv) 578 { 579 void *h; 580 void (*func)(struct loader_callbacks *, void *, int, int); 581 uint64_t mem_size; 582 int opt, error; 583 char *disk_image; 584 585 progname = argv[0]; 586 587 mem_size = 256 * MB; 588 disk_image = NULL; 589 590 while ((opt = getopt(argc, argv, "d:e:h:m:")) != -1) { 591 switch (opt) { 592 case 'd': 593 disk_image = optarg; 594 break; 595 596 case 'e': 597 addenv(optarg); 598 break; 599 600 case 'h': 601 host_base = optarg; 602 break; 603 604 case 'm': 605 error = vm_parse_memsize(optarg, &mem_size); 606 if (error != 0) 607 errx(EX_USAGE, "Invalid memsize '%s'", optarg); 608 break; 609 case '?': 610 usage(); 611 } 612 } 613 614 argc -= optind; 615 argv += optind; 616 617 if (argc != 1) 618 usage(); 619 620 vmname = argv[0]; 621 622 error = vm_create(vmname); 623 if (error != 0 && errno != EEXIST) { 624 perror("vm_create"); 625 exit(1); 626 627 } 628 629 ctx = vm_open(vmname); 630 if (ctx == NULL) { 631 perror("vm_open"); 632 exit(1); 633 } 634 635 error = vm_setup_memory(ctx, mem_size, VM_MMAP_ALL); 636 if (error) { 637 perror("vm_setup_memory"); 638 exit(1); 639 } 640 641 tcgetattr(0, &term); 642 oldterm = term; 643 term.c_lflag &= ~(ICANON|ECHO); 644 term.c_iflag &= ~ICRNL; 645 tcsetattr(0, TCSAFLUSH, &term); 646 h = dlopen("/boot/userboot.so", RTLD_LOCAL); 647 if (!h) { 648 printf("%s\n", dlerror()); 649 return (1); 650 } 651 func = dlsym(h, "loader_main"); 652 if (!func) { 653 printf("%s\n", dlerror()); 654 return (1); 655 } 656 657 if (disk_image) { 658 disk_fd = open(disk_image, O_RDONLY); 659 } 660 661 addenv("smbios.bios.vendor=BHYVE"); 662 addenv("boot_serial=1"); 663 664 func(&cb, NULL, USERBOOT_VERSION_3, disk_fd >= 0); 665 } 666