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