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