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