xref: /freebsd/usr.sbin/bhyveload/bhyveload.c (revision 058ac3e8063366dafa634d9107642e12b038bf09)
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, 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
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
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
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
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
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
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
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
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
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
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
521 cb_delay(void *arg, int usec)
522 {
523 
524 	usleep(usec);
525 }
526 
527 static void
528 cb_exit(void *arg, int v)
529 {
530 
531 	tcsetattr(consout_fd, TCSAFLUSH, &oldterm);
532 	exit(v);
533 }
534 
535 static void
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
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 *
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
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
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
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
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
686 disk_open(char *path)
687 {
688 	int fd;
689 
690 	if (ndisks >= NDISKS)
691 		return (ERANGE);
692 
693 	fd = open(path, O_RDWR);
694 	if (fd < 0)
695 		return (errno);
696 
697 	disk_fd[ndisks] = fd;
698 	ndisks++;
699 
700 	return (0);
701 }
702 
703 static 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
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