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
cb_putc(void * arg,int ch)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
cb_getc(void * arg)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
cb_poll(void * arg)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
cb_open(void * arg,const char * filename,void ** hp)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
cb_close(void * arg,void * h)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
cb_isdir(void * arg,void * h)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
cb_read(void * arg,void * h,void * buf,size_t size,size_t * resid)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
cb_readdir(void * arg,void * h,uint32_t * fileno_return,uint8_t * type_return,size_t * namelen_return,char * name)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
cb_seek(void * arg,void * h,uint64_t offset,int whence)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
cb_stat(void * arg,void * h,struct stat * sbp)281 cb_stat(void *arg, void *h, struct stat *sbp)
282 {
283 struct cb_file *cf = h;
284
285 memset(sbp, 0, sizeof(struct stat));
286 sbp->st_mode = cf->cf_stat.st_mode;
287 sbp->st_uid = cf->cf_stat.st_uid;
288 sbp->st_gid = cf->cf_stat.st_gid;
289 sbp->st_size = cf->cf_stat.st_size;
290 sbp->st_mtime = cf->cf_stat.st_mtime;
291 sbp->st_dev = cf->cf_stat.st_dev;
292 sbp->st_ino = cf->cf_stat.st_ino;
293
294 return (0);
295 }
296
297 /*
298 * Disk image i/o callbacks
299 */
300
301 static int
cb_diskread(void * arg,int unit,uint64_t from,void * to,size_t size,size_t * resid)302 cb_diskread(void *arg, int unit, uint64_t from, void *to, size_t size,
303 size_t *resid)
304 {
305 ssize_t n;
306
307 if (unit < 0 || unit >= ndisks)
308 return (EIO);
309 n = pread(disk_fd[unit], to, size, from);
310 if (n < 0)
311 return (errno);
312 *resid = size - n;
313 return (0);
314 }
315
316 static int
cb_diskwrite(void * arg,int unit,uint64_t offset,void * src,size_t size,size_t * resid)317 cb_diskwrite(void *arg, int unit, uint64_t offset, void *src, size_t size,
318 size_t *resid)
319 {
320 ssize_t n;
321
322 if (unit < 0 || unit >= ndisks)
323 return (EIO);
324 n = pwrite(disk_fd[unit], src, size, offset);
325 if (n < 0)
326 return (errno);
327 *resid = size - n;
328 return (0);
329 }
330
331 static int
cb_diskioctl(void * arg,int unit,u_long cmd,void * data)332 cb_diskioctl(void *arg, int unit, u_long cmd, void *data)
333 {
334 struct stat sb;
335
336 if (unit < 0 || unit >= ndisks)
337 return (EBADF);
338
339 switch (cmd) {
340 case DIOCGSECTORSIZE:
341 *(u_int *)data = 512;
342 break;
343 case DIOCGMEDIASIZE:
344 if (fstat(disk_fd[unit], &sb) != 0)
345 return (ENOTTY);
346 if (S_ISCHR(sb.st_mode) &&
347 ioctl(disk_fd[unit], DIOCGMEDIASIZE, &sb.st_size) != 0)
348 return (ENOTTY);
349 *(off_t *)data = sb.st_size;
350 break;
351 default:
352 return (ENOTTY);
353 }
354
355 return (0);
356 }
357
358 /*
359 * Guest virtual machine i/o callbacks
360 */
361 static int
cb_copyin(void * arg,const void * from,uint64_t to,size_t size)362 cb_copyin(void *arg, const void *from, uint64_t to, size_t size)
363 {
364 char *ptr;
365
366 to &= 0x7fffffff;
367
368 ptr = vm_map_gpa(ctx, to, size);
369 if (ptr == NULL)
370 return (EFAULT);
371
372 memcpy(ptr, from, size);
373 return (0);
374 }
375
376 static int
cb_copyout(void * arg,uint64_t from,void * to,size_t size)377 cb_copyout(void *arg, uint64_t from, void *to, size_t size)
378 {
379 char *ptr;
380
381 from &= 0x7fffffff;
382
383 ptr = vm_map_gpa(ctx, from, size);
384 if (ptr == NULL)
385 return (EFAULT);
386
387 memcpy(to, ptr, size);
388 return (0);
389 }
390
391 static void
cb_setreg(void * arg,int r,uint64_t v)392 cb_setreg(void *arg, int r, uint64_t v)
393 {
394 int error;
395 enum vm_reg_name vmreg;
396
397 vmreg = VM_REG_LAST;
398
399 switch (r) {
400 case 4:
401 vmreg = VM_REG_GUEST_RSP;
402 rsp = v;
403 break;
404 default:
405 break;
406 }
407
408 if (vmreg == VM_REG_LAST) {
409 printf("test_setreg(%d): not implemented\n", r);
410 cb_exit(NULL, USERBOOT_EXIT_QUIT);
411 }
412
413 error = vm_set_register(ctx, BSP, vmreg, v);
414 if (error) {
415 perror("vm_set_register");
416 cb_exit(NULL, USERBOOT_EXIT_QUIT);
417 }
418 }
419
420 static void
cb_setmsr(void * arg,int r,uint64_t v)421 cb_setmsr(void *arg, int r, uint64_t v)
422 {
423 int error;
424 enum vm_reg_name vmreg;
425
426 vmreg = VM_REG_LAST;
427
428 switch (r) {
429 case MSR_EFER:
430 vmreg = VM_REG_GUEST_EFER;
431 break;
432 default:
433 break;
434 }
435
436 if (vmreg == VM_REG_LAST) {
437 printf("test_setmsr(%d): not implemented\n", r);
438 cb_exit(NULL, USERBOOT_EXIT_QUIT);
439 }
440
441 error = vm_set_register(ctx, BSP, vmreg, v);
442 if (error) {
443 perror("vm_set_msr");
444 cb_exit(NULL, USERBOOT_EXIT_QUIT);
445 }
446 }
447
448 static void
cb_setcr(void * arg,int r,uint64_t v)449 cb_setcr(void *arg, int r, uint64_t v)
450 {
451 int error;
452 enum vm_reg_name vmreg;
453
454 vmreg = VM_REG_LAST;
455
456 switch (r) {
457 case 0:
458 vmreg = VM_REG_GUEST_CR0;
459 break;
460 case 3:
461 vmreg = VM_REG_GUEST_CR3;
462 cr3 = v;
463 break;
464 case 4:
465 vmreg = VM_REG_GUEST_CR4;
466 break;
467 default:
468 break;
469 }
470
471 if (vmreg == VM_REG_LAST) {
472 printf("test_setcr(%d): not implemented\n", r);
473 cb_exit(NULL, USERBOOT_EXIT_QUIT);
474 }
475
476 error = vm_set_register(ctx, BSP, vmreg, v);
477 if (error) {
478 perror("vm_set_cr");
479 cb_exit(NULL, USERBOOT_EXIT_QUIT);
480 }
481 }
482
483 static void
cb_setgdt(void * arg,uint64_t base,size_t size)484 cb_setgdt(void *arg, uint64_t base, size_t size)
485 {
486 int error;
487
488 error = vm_set_desc(ctx, BSP, VM_REG_GUEST_GDTR, base, size - 1, 0);
489 if (error != 0) {
490 perror("vm_set_desc(gdt)");
491 cb_exit(NULL, USERBOOT_EXIT_QUIT);
492 }
493
494 gdtbase = base;
495 }
496
497 static void
cb_exec(void * arg,uint64_t rip)498 cb_exec(void *arg, uint64_t rip)
499 {
500 int error;
501
502 if (cr3 == 0)
503 error = vm_setup_freebsd_registers_i386(ctx, BSP, rip, gdtbase,
504 rsp);
505 else
506 error = vm_setup_freebsd_registers(ctx, BSP, rip, cr3, gdtbase,
507 rsp);
508 if (error) {
509 perror("vm_setup_freebsd_registers");
510 cb_exit(NULL, USERBOOT_EXIT_QUIT);
511 }
512
513 cb_exit(NULL, 0);
514 }
515
516 /*
517 * Misc
518 */
519
520 static void
cb_delay(void * arg,int usec)521 cb_delay(void *arg, int usec)
522 {
523
524 usleep(usec);
525 }
526
527 static void
cb_exit(void * arg,int v)528 cb_exit(void *arg, int v)
529 {
530
531 tcsetattr(consout_fd, TCSAFLUSH, &oldterm);
532 exit(v);
533 }
534
535 static void
cb_getmem(void * arg,uint64_t * ret_lowmem,uint64_t * ret_highmem)536 cb_getmem(void *arg, uint64_t *ret_lowmem, uint64_t *ret_highmem)
537 {
538
539 *ret_lowmem = vm_get_lowmem_size(ctx);
540 *ret_highmem = vm_get_highmem_size(ctx);
541 }
542
543 struct env {
544 char *str; /* name=value */
545 SLIST_ENTRY(env) next;
546 };
547
548 static SLIST_HEAD(envhead, env) envhead;
549
550 static void
addenv(char * str)551 addenv(char *str)
552 {
553 struct env *env;
554
555 env = malloc(sizeof(struct env));
556 env->str = str;
557 SLIST_INSERT_HEAD(&envhead, env, next);
558 }
559
560 static char *
cb_getenv(void * arg,int num)561 cb_getenv(void *arg, int num)
562 {
563 int i;
564 struct env *env;
565
566 i = 0;
567 SLIST_FOREACH(env, &envhead, next) {
568 if (i == num)
569 return (env->str);
570 i++;
571 }
572
573 return (NULL);
574 }
575
576 static int
cb_vm_set_register(void * arg,int vcpu,int reg,uint64_t val)577 cb_vm_set_register(void *arg, int vcpu, int reg, uint64_t val)
578 {
579
580 return (vm_set_register(ctx, vcpu, reg, val));
581 }
582
583 static int
cb_vm_set_desc(void * arg,int vcpu,int reg,uint64_t base,u_int limit,u_int access)584 cb_vm_set_desc(void *arg, int vcpu, int reg, uint64_t base, u_int limit,
585 u_int access)
586 {
587
588 return (vm_set_desc(ctx, vcpu, reg, base, limit, access));
589 }
590
591 static void
cb_swap_interpreter(void * arg,const char * interp_req)592 cb_swap_interpreter(void *arg, const char *interp_req)
593 {
594
595 /*
596 * If the user specified a loader but we detected a mismatch, we should
597 * not try to pivot to a different loader on them.
598 */
599 free(loader);
600 if (explicit_loader == 1) {
601 perror("requested loader interpreter does not match guest userboot");
602 cb_exit(NULL, 1);
603 }
604 if (interp_req == NULL || *interp_req == '\0') {
605 perror("guest failed to request an interpreter");
606 cb_exit(NULL, 1);
607 }
608
609 if (asprintf(&loader, "/boot/userboot_%s.so", interp_req) == -1)
610 err(EX_OSERR, "malloc");
611 need_reinit = 1;
612 longjmp(jb, 1);
613 }
614
615 static struct loader_callbacks cb = {
616 .getc = cb_getc,
617 .putc = cb_putc,
618 .poll = cb_poll,
619
620 .open = cb_open,
621 .close = cb_close,
622 .isdir = cb_isdir,
623 .read = cb_read,
624 .readdir = cb_readdir,
625 .seek = cb_seek,
626 .stat = cb_stat,
627
628 .diskread = cb_diskread,
629 .diskwrite = cb_diskwrite,
630 .diskioctl = cb_diskioctl,
631
632 .copyin = cb_copyin,
633 .copyout = cb_copyout,
634 .setreg = cb_setreg,
635 .setmsr = cb_setmsr,
636 .setcr = cb_setcr,
637 .setgdt = cb_setgdt,
638 .exec = cb_exec,
639
640 .delay = cb_delay,
641 .exit = cb_exit,
642 .getmem = cb_getmem,
643
644 .getenv = cb_getenv,
645
646 /* Version 4 additions */
647 .vm_set_register = cb_vm_set_register,
648 .vm_set_desc = cb_vm_set_desc,
649
650 /* Version 5 additions */
651 .swap_interpreter = cb_swap_interpreter,
652 };
653
654 static int
altcons_open(char * path)655 altcons_open(char *path)
656 {
657 struct stat sb;
658 int err;
659 int fd;
660
661 /*
662 * Allow stdio to be passed in so that the same string
663 * can be used for the bhyveload console and bhyve com-port
664 * parameters
665 */
666 if (!strcmp(path, "stdio"))
667 return (0);
668
669 err = stat(path, &sb);
670 if (err == 0) {
671 if (!S_ISCHR(sb.st_mode))
672 err = ENOTSUP;
673 else {
674 fd = open(path, O_RDWR | O_NONBLOCK);
675 if (fd < 0)
676 err = errno;
677 else
678 consin_fd = consout_fd = fd;
679 }
680 }
681
682 return (err);
683 }
684
685 static int
disk_open(char * path)686 disk_open(char *path)
687 {
688 int fd;
689
690 if (ndisks >= NDISKS)
691 return (ERANGE);
692
693 fd = open(path, O_RDONLY);
694 if (fd < 0)
695 return (errno);
696
697 disk_fd[ndisks] = fd;
698 ndisks++;
699
700 return (0);
701 }
702
703 static void
usage(void)704 usage(void)
705 {
706
707 fprintf(stderr,
708 "usage: %s [-S][-c <console-device>] [-d <disk-path>] [-e <name=value>]\n"
709 " %*s [-h <host-path>] [-m memsize[K|k|M|m|G|g|T|t]] <vmname>\n",
710 progname,
711 (int)strlen(progname), "");
712 exit(1);
713 }
714
715 int
main(int argc,char ** argv)716 main(int argc, char** argv)
717 {
718 void (*func)(struct loader_callbacks *, void *, int, int);
719 uint64_t mem_size;
720 int opt, error, memflags;
721
722 progname = basename(argv[0]);
723
724 memflags = 0;
725 mem_size = 256 * MB;
726
727 consin_fd = STDIN_FILENO;
728 consout_fd = STDOUT_FILENO;
729
730 while ((opt = getopt(argc, argv, "CSc:d:e:h:l:m:")) != -1) {
731 switch (opt) {
732 case 'c':
733 error = altcons_open(optarg);
734 if (error != 0)
735 errx(EX_USAGE, "Could not open '%s'", optarg);
736 break;
737
738 case 'd':
739 error = disk_open(optarg);
740 if (error != 0)
741 errx(EX_USAGE, "Could not open '%s'", optarg);
742 break;
743
744 case 'e':
745 addenv(optarg);
746 break;
747
748 case 'h':
749 host_base = optarg;
750 break;
751
752 case 'l':
753 if (loader != NULL)
754 errx(EX_USAGE, "-l can only be given once");
755 loader = strdup(optarg);
756 if (loader == NULL)
757 err(EX_OSERR, "malloc");
758 explicit_loader = 1;
759 break;
760
761 case 'm':
762 error = vm_parse_memsize(optarg, &mem_size);
763 if (error != 0)
764 errx(EX_USAGE, "Invalid memsize '%s'", optarg);
765 break;
766 case 'C':
767 memflags |= VM_MEM_F_INCORE;
768 break;
769 case 'S':
770 memflags |= VM_MEM_F_WIRED;
771 break;
772 case '?':
773 usage();
774 }
775 }
776
777 argc -= optind;
778 argv += optind;
779
780 if (argc != 1)
781 usage();
782
783 vmname = argv[0];
784
785 need_reinit = 0;
786 error = vm_create(vmname);
787 if (error) {
788 if (errno != EEXIST) {
789 perror("vm_create");
790 exit(1);
791 }
792 need_reinit = 1;
793 }
794
795 ctx = vm_open(vmname);
796 if (ctx == NULL) {
797 perror("vm_open");
798 exit(1);
799 }
800
801 /*
802 * setjmp in the case the guest wants to swap out interpreter,
803 * cb_swap_interpreter will swap out loader as appropriate and set
804 * need_reinit so that we end up in a clean state once again.
805 */
806 setjmp(jb);
807
808 if (need_reinit) {
809 error = vm_reinit(ctx);
810 if (error) {
811 perror("vm_reinit");
812 exit(1);
813 }
814 }
815
816 vm_set_memflags(ctx, memflags);
817 error = vm_setup_memory(ctx, mem_size, VM_MMAP_ALL);
818 if (error) {
819 perror("vm_setup_memory");
820 exit(1);
821 }
822
823 if (loader == NULL) {
824 loader = strdup("/boot/userboot.so");
825 if (loader == NULL)
826 err(EX_OSERR, "malloc");
827 }
828 if (loader_hdl != NULL)
829 dlclose(loader_hdl);
830 loader_hdl = dlopen(loader, RTLD_LOCAL);
831 if (!loader_hdl) {
832 printf("%s\n", dlerror());
833 free(loader);
834 return (1);
835 }
836 func = dlsym(loader_hdl, "loader_main");
837 if (!func) {
838 printf("%s\n", dlerror());
839 free(loader);
840 return (1);
841 }
842
843 tcgetattr(consout_fd, &term);
844 oldterm = term;
845 cfmakeraw(&term);
846 term.c_cflag |= CLOCAL;
847
848 tcsetattr(consout_fd, TCSAFLUSH, &term);
849
850 addenv("smbios.bios.vendor=BHYVE");
851 addenv("boot_serial=1");
852
853 func(&cb, NULL, USERBOOT_VERSION_5, ndisks);
854
855 free(loader);
856 return (0);
857 }
858