xref: /freebsd-src/usr.sbin/bhyveload/bhyveload.c (revision 8bf0882e186effbd3d742ce17613cf9dfb81ce0c)
1c487da1eSNeel Natu /*-
2eebd9d53SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
31de7b4b8SPedro F. Giffuni  *
4c487da1eSNeel Natu  * Copyright (c) 2011 NetApp, Inc.
5c487da1eSNeel Natu  * All rights reserved.
6c487da1eSNeel Natu  *
7c487da1eSNeel Natu  * Redistribution and use in source and binary forms, with or without
8c487da1eSNeel Natu  * modification, are permitted provided that the following conditions
9c487da1eSNeel Natu  * are met:
10c487da1eSNeel Natu  * 1. Redistributions of source code must retain the above copyright
11c487da1eSNeel Natu  *    notice, this list of conditions and the following disclaimer.
12c487da1eSNeel Natu  * 2. Redistributions in binary form must reproduce the above copyright
13c487da1eSNeel Natu  *    notice, this list of conditions and the following disclaimer in the
14c487da1eSNeel Natu  *    documentation and/or other materials provided with the distribution.
15c487da1eSNeel Natu  *
16c487da1eSNeel Natu  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
17c487da1eSNeel Natu  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18c487da1eSNeel Natu  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19c487da1eSNeel Natu  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
20c487da1eSNeel Natu  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21c487da1eSNeel Natu  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22c487da1eSNeel Natu  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23c487da1eSNeel Natu  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24c487da1eSNeel Natu  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25c487da1eSNeel Natu  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26c487da1eSNeel Natu  * SUCH DAMAGE.
27c487da1eSNeel Natu  */
28c487da1eSNeel Natu 
29c487da1eSNeel Natu /*-
30c487da1eSNeel Natu  * Copyright (c) 2011 Google, Inc.
31c487da1eSNeel Natu  * All rights reserved.
32c487da1eSNeel Natu  *
33c487da1eSNeel Natu  * Redistribution and use in source and binary forms, with or without
34c487da1eSNeel Natu  * modification, are permitted provided that the following conditions
35c487da1eSNeel Natu  * are met:
36c487da1eSNeel Natu  * 1. Redistributions of source code must retain the above copyright
37c487da1eSNeel Natu  *    notice, this list of conditions and the following disclaimer.
38c487da1eSNeel Natu  * 2. Redistributions in binary form must reproduce the above copyright
39c487da1eSNeel Natu  *    notice, this list of conditions and the following disclaimer in the
40c487da1eSNeel Natu  *    documentation and/or other materials provided with the distribution.
41c487da1eSNeel Natu  *
42c487da1eSNeel Natu  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
43c487da1eSNeel Natu  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
44c487da1eSNeel Natu  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
45c487da1eSNeel Natu  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
46c487da1eSNeel Natu  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
47c487da1eSNeel Natu  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
48c487da1eSNeel Natu  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
49c487da1eSNeel Natu  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
50c487da1eSNeel Natu  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
51c487da1eSNeel Natu  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
52c487da1eSNeel Natu  * SUCH DAMAGE.
53c487da1eSNeel Natu  */
54c487da1eSNeel Natu 
55c487da1eSNeel Natu #include <sys/cdefs.h>
56c487da1eSNeel Natu #include <sys/ioctl.h>
57c487da1eSNeel Natu #include <sys/stat.h>
58a10c6f55SNeel Natu #include <sys/disk.h>
59b6afa84bSNeel Natu #include <sys/queue.h>
60c487da1eSNeel Natu 
61c487da1eSNeel Natu #include <machine/specialreg.h>
62c487da1eSNeel Natu #include <machine/vmm.h>
63c487da1eSNeel Natu 
647d9ef309SJohn Baldwin #include <assert.h>
65c487da1eSNeel Natu #include <dirent.h>
66c487da1eSNeel Natu #include <dlfcn.h>
67c487da1eSNeel Natu #include <errno.h>
68200758f1SNeel Natu #include <err.h>
69c487da1eSNeel Natu #include <fcntl.h>
70c487da1eSNeel Natu #include <getopt.h>
71b5331f4dSNeel Natu #include <libgen.h>
72c487da1eSNeel Natu #include <limits.h>
73d3d381b2SKyle Evans #include <setjmp.h>
74c487da1eSNeel Natu #include <stdio.h>
75c487da1eSNeel Natu #include <stdlib.h>
76c487da1eSNeel Natu #include <string.h>
77200758f1SNeel Natu #include <sysexits.h>
78c487da1eSNeel Natu #include <termios.h>
79c487da1eSNeel Natu #include <unistd.h>
80c487da1eSNeel Natu 
81*8bf0882eSKyle Evans #include <capsicum_helpers.h>
82c487da1eSNeel Natu #include <vmmapi.h>
83c487da1eSNeel Natu 
84c487da1eSNeel Natu #include "userboot.h"
85c487da1eSNeel Natu 
86c487da1eSNeel Natu #define	MB	(1024 * 1024UL)
87c487da1eSNeel Natu #define	GB	(1024 * 1024 * 1024UL)
88c487da1eSNeel Natu #define	BSP	0
89c487da1eSNeel Natu 
90cf087c12SPeter Grehan #define	NDISKS	32
91cf087c12SPeter Grehan 
92c487da1eSNeel Natu static struct termios term, oldterm;
93cf087c12SPeter Grehan static int disk_fd[NDISKS];
94cf087c12SPeter Grehan static int ndisks;
956380102cSPeter Grehan static int consin_fd, consout_fd;
966779d44bSKyle Evans static int hostbase_fd = -1;
97c487da1eSNeel Natu 
98d3d381b2SKyle Evans static void *loader_hdl;
99d3d381b2SKyle Evans static char *loader;
100bf7c4fcbSKyle Evans static int explicit_loader_fd;
101d3d381b2SKyle Evans static jmp_buf jb;
102d3d381b2SKyle Evans 
103b060ba50SNeel Natu static char *vmname, *progname;
104c487da1eSNeel Natu static struct vmctx *ctx;
1057d9ef309SJohn Baldwin static struct vcpu *vcpu;
106c487da1eSNeel Natu 
107c487da1eSNeel Natu static uint64_t gdtbase, cr3, rsp;
108c487da1eSNeel Natu 
109c487da1eSNeel Natu static void cb_exit(void *arg, int v);
110c487da1eSNeel Natu 
111c487da1eSNeel Natu /*
112c487da1eSNeel Natu  * Console i/o callbacks
113c487da1eSNeel Natu  */
114c487da1eSNeel Natu 
115c487da1eSNeel Natu static void
116ad43dd69SMark Johnston cb_putc(void *arg __unused, int ch)
117c487da1eSNeel Natu {
118c487da1eSNeel Natu 	char c = ch;
119c487da1eSNeel Natu 
1206380102cSPeter Grehan 	(void) write(consout_fd, &c, 1);
121c487da1eSNeel Natu }
122c487da1eSNeel Natu 
123c487da1eSNeel Natu static int
124ad43dd69SMark Johnston cb_getc(void *arg __unused)
125c487da1eSNeel Natu {
126c487da1eSNeel Natu 	char c;
127c487da1eSNeel Natu 
1286380102cSPeter Grehan 	if (read(consin_fd, &c, 1) == 1)
129c487da1eSNeel Natu 		return (c);
130c487da1eSNeel Natu 	return (-1);
131c487da1eSNeel Natu }
132c487da1eSNeel Natu 
133c487da1eSNeel Natu static int
134ad43dd69SMark Johnston cb_poll(void *arg __unused)
135c487da1eSNeel Natu {
136c487da1eSNeel Natu 	int n;
137c487da1eSNeel Natu 
1386380102cSPeter Grehan 	if (ioctl(consin_fd, FIONREAD, &n) >= 0)
139c487da1eSNeel Natu 		return (n > 0);
140c487da1eSNeel Natu 	return (0);
141c487da1eSNeel Natu }
142c487da1eSNeel Natu 
143c487da1eSNeel Natu /*
144c487da1eSNeel Natu  * Host filesystem i/o callbacks
145c487da1eSNeel Natu  */
146c487da1eSNeel Natu 
147c487da1eSNeel Natu struct cb_file {
148c487da1eSNeel Natu 	int cf_isdir;
149c487da1eSNeel Natu 	size_t cf_size;
150c487da1eSNeel Natu 	struct stat cf_stat;
151c487da1eSNeel Natu 	union {
152c487da1eSNeel Natu 		int fd;
153c487da1eSNeel Natu 		DIR *dir;
154c487da1eSNeel Natu 	} cf_u;
155c487da1eSNeel Natu };
156c487da1eSNeel Natu 
157c487da1eSNeel Natu static int
158ad43dd69SMark Johnston cb_open(void *arg __unused, const char *filename, void **hp)
159c487da1eSNeel Natu {
160c487da1eSNeel Natu 	struct cb_file *cf;
1616779d44bSKyle Evans 	struct stat sb;
1626779d44bSKyle Evans 	int fd, flags;
163c487da1eSNeel Natu 
1646779d44bSKyle Evans 	cf = NULL;
1656779d44bSKyle Evans 	fd = -1;
1666779d44bSKyle Evans 	flags = O_RDONLY | O_RESOLVE_BENEATH;
1676779d44bSKyle Evans 	if (hostbase_fd == -1)
168c487da1eSNeel Natu 		return (ENOENT);
169c487da1eSNeel Natu 
1706779d44bSKyle Evans 	/* Absolute paths are relative to our hostbase, chop off leading /. */
1716779d44bSKyle Evans 	if (filename[0] == '/')
1726779d44bSKyle Evans 		filename++;
1736779d44bSKyle Evans 
1746779d44bSKyle Evans 	/* Lookup of /, use . instead. */
1756779d44bSKyle Evans 	if (filename[0] == '\0')
1766779d44bSKyle Evans 		filename = ".";
1776779d44bSKyle Evans 
1786779d44bSKyle Evans 	if (fstatat(hostbase_fd, filename, &sb, AT_RESOLVE_BENEATH) < 0)
179c487da1eSNeel Natu 		return (errno);
1806779d44bSKyle Evans 
1816779d44bSKyle Evans 	if (!S_ISDIR(sb.st_mode) && !S_ISREG(sb.st_mode))
1826779d44bSKyle Evans 		return (EINVAL);
1836779d44bSKyle Evans 
1846779d44bSKyle Evans 	if (S_ISDIR(sb.st_mode))
1856779d44bSKyle Evans 		flags |= O_DIRECTORY;
1866779d44bSKyle Evans 
1876779d44bSKyle Evans 	/* May be opening the root dir */
1886779d44bSKyle Evans 	fd = openat(hostbase_fd, filename, flags);
1896779d44bSKyle Evans 	if (fd < 0)
1906779d44bSKyle Evans 		return (errno);
1916779d44bSKyle Evans 
1926779d44bSKyle Evans 	cf = malloc(sizeof(struct cb_file));
1936779d44bSKyle Evans 	if (cf == NULL) {
1946779d44bSKyle Evans 		close(fd);
1956779d44bSKyle Evans 		return (ENOMEM);
196c487da1eSNeel Natu 	}
197c487da1eSNeel Natu 
1986779d44bSKyle Evans 	cf->cf_stat = sb;
1998f61276dSPedro F. Giffuni 	cf->cf_size = cf->cf_stat.st_size;
2006779d44bSKyle Evans 
201c487da1eSNeel Natu 	if (S_ISDIR(cf->cf_stat.st_mode)) {
202c487da1eSNeel Natu 		cf->cf_isdir = 1;
2036779d44bSKyle Evans 		cf->cf_u.dir = fdopendir(fd);
2046779d44bSKyle Evans 		if (cf->cf_u.dir == NULL) {
2056779d44bSKyle Evans 			close(fd);
206c487da1eSNeel Natu 			free(cf);
2076779d44bSKyle Evans 			return (ENOMEM);
2086779d44bSKyle Evans 		}
2096779d44bSKyle Evans 	} else {
2106779d44bSKyle Evans 		assert(S_ISREG(cf->cf_stat.st_mode));
2116779d44bSKyle Evans 		cf->cf_isdir = 0;
2126779d44bSKyle Evans 		cf->cf_u.fd = fd;
2136779d44bSKyle Evans 	}
2146779d44bSKyle Evans 	*hp = cf;
2156779d44bSKyle Evans 	return (0);
216c487da1eSNeel Natu }
217c487da1eSNeel Natu 
218c487da1eSNeel Natu static int
219ad43dd69SMark Johnston cb_close(void *arg __unused, void *h)
220c487da1eSNeel Natu {
221c487da1eSNeel Natu 	struct cb_file *cf = h;
222c487da1eSNeel Natu 
223c487da1eSNeel Natu 	if (cf->cf_isdir)
224c487da1eSNeel Natu 		closedir(cf->cf_u.dir);
225c487da1eSNeel Natu 	else
226c487da1eSNeel Natu 		close(cf->cf_u.fd);
227c487da1eSNeel Natu 	free(cf);
228c487da1eSNeel Natu 
229c487da1eSNeel Natu 	return (0);
230c487da1eSNeel Natu }
231c487da1eSNeel Natu 
232c487da1eSNeel Natu static int
233ad43dd69SMark Johnston cb_isdir(void *arg __unused, void *h)
234c487da1eSNeel Natu {
235c487da1eSNeel Natu 	struct cb_file *cf = h;
236c487da1eSNeel Natu 
237c487da1eSNeel Natu 	return (cf->cf_isdir);
238c487da1eSNeel Natu }
239c487da1eSNeel Natu 
240c487da1eSNeel Natu static int
241ad43dd69SMark Johnston cb_read(void *arg __unused, void *h, void *buf, size_t size, size_t *resid)
242c487da1eSNeel Natu {
243c487da1eSNeel Natu 	struct cb_file *cf = h;
244c487da1eSNeel Natu 	ssize_t sz;
245c487da1eSNeel Natu 
246c487da1eSNeel Natu 	if (cf->cf_isdir)
247c487da1eSNeel Natu 		return (EINVAL);
248c487da1eSNeel Natu 	sz = read(cf->cf_u.fd, buf, size);
249c487da1eSNeel Natu 	if (sz < 0)
250c487da1eSNeel Natu 		return (EINVAL);
251c487da1eSNeel Natu 	*resid = size - sz;
252c487da1eSNeel Natu 	return (0);
253c487da1eSNeel Natu }
254c487da1eSNeel Natu 
255c487da1eSNeel Natu static int
256ad43dd69SMark Johnston cb_readdir(void *arg __unused, void *h, uint32_t *fileno_return,
257ad43dd69SMark Johnston     uint8_t *type_return, size_t *namelen_return, char *name)
258c487da1eSNeel Natu {
259c487da1eSNeel Natu 	struct cb_file *cf = h;
260c487da1eSNeel Natu 	struct dirent *dp;
261c487da1eSNeel Natu 
262c487da1eSNeel Natu 	if (!cf->cf_isdir)
263c487da1eSNeel Natu 		return (EINVAL);
264c487da1eSNeel Natu 
265c487da1eSNeel Natu 	dp = readdir(cf->cf_u.dir);
266c487da1eSNeel Natu 	if (!dp)
267c487da1eSNeel Natu 		return (ENOENT);
268c487da1eSNeel Natu 
269c487da1eSNeel Natu 	/*
270c487da1eSNeel Natu 	 * Note: d_namlen is in the range 0..255 and therefore less
271c487da1eSNeel Natu 	 * than PATH_MAX so we don't need to test before copying.
272c487da1eSNeel Natu 	 */
273c487da1eSNeel Natu 	*fileno_return = dp->d_fileno;
274c487da1eSNeel Natu 	*type_return = dp->d_type;
275c487da1eSNeel Natu 	*namelen_return = dp->d_namlen;
276c487da1eSNeel Natu 	memcpy(name, dp->d_name, dp->d_namlen);
277c487da1eSNeel Natu 	name[dp->d_namlen] = 0;
278c487da1eSNeel Natu 
279c487da1eSNeel Natu 	return (0);
280c487da1eSNeel Natu }
281c487da1eSNeel Natu 
282c487da1eSNeel Natu static int
283ad43dd69SMark Johnston cb_seek(void *arg __unused, void *h, uint64_t offset, int whence)
284c487da1eSNeel Natu {
285c487da1eSNeel Natu 	struct cb_file *cf = h;
286c487da1eSNeel Natu 
287c487da1eSNeel Natu 	if (cf->cf_isdir)
288c487da1eSNeel Natu 		return (EINVAL);
289c487da1eSNeel Natu 	if (lseek(cf->cf_u.fd, offset, whence) < 0)
290c487da1eSNeel Natu 		return (errno);
291c487da1eSNeel Natu 	return (0);
292c487da1eSNeel Natu }
293c487da1eSNeel Natu 
294c487da1eSNeel Natu static int
295ad43dd69SMark Johnston cb_stat(void *arg __unused, void *h, struct stat *sbp)
296c487da1eSNeel Natu {
297c487da1eSNeel Natu 	struct cb_file *cf = h;
298c487da1eSNeel Natu 
29953f151f9SSimon J. Gerraty 	memset(sbp, 0, sizeof(struct stat));
30053f151f9SSimon J. Gerraty 	sbp->st_mode = cf->cf_stat.st_mode;
30153f151f9SSimon J. Gerraty 	sbp->st_uid = cf->cf_stat.st_uid;
30253f151f9SSimon J. Gerraty 	sbp->st_gid = cf->cf_stat.st_gid;
30353f151f9SSimon J. Gerraty 	sbp->st_size = cf->cf_stat.st_size;
30453f151f9SSimon J. Gerraty 	sbp->st_mtime = cf->cf_stat.st_mtime;
30553f151f9SSimon J. Gerraty 	sbp->st_dev = cf->cf_stat.st_dev;
30653f151f9SSimon J. Gerraty 	sbp->st_ino = cf->cf_stat.st_ino;
30753f151f9SSimon J. Gerraty 
308c487da1eSNeel Natu 	return (0);
309c487da1eSNeel Natu }
310c487da1eSNeel Natu 
311c487da1eSNeel Natu /*
312c487da1eSNeel Natu  * Disk image i/o callbacks
313c487da1eSNeel Natu  */
314c487da1eSNeel Natu 
315c487da1eSNeel Natu static int
316ad43dd69SMark Johnston cb_diskread(void *arg __unused, int unit, uint64_t from, void *to, size_t size,
317c487da1eSNeel Natu     size_t *resid)
318c487da1eSNeel Natu {
319c487da1eSNeel Natu 	ssize_t n;
320c487da1eSNeel Natu 
321cf087c12SPeter Grehan 	if (unit < 0 || unit >= ndisks)
322c487da1eSNeel Natu 		return (EIO);
323cf087c12SPeter Grehan 	n = pread(disk_fd[unit], to, size, from);
324c487da1eSNeel Natu 	if (n < 0)
325c487da1eSNeel Natu 		return (errno);
326c487da1eSNeel Natu 	*resid = size - n;
327c487da1eSNeel Natu 	return (0);
328c487da1eSNeel Natu }
329c487da1eSNeel Natu 
330a10c6f55SNeel Natu static int
331ad43dd69SMark Johnston cb_diskwrite(void *arg __unused, int unit, uint64_t offset, void *src,
332ad43dd69SMark Johnston     size_t size, size_t *resid)
333cc71ff72SConrad Meyer {
334cc71ff72SConrad Meyer 	ssize_t n;
335cc71ff72SConrad Meyer 
336cc71ff72SConrad Meyer 	if (unit < 0 || unit >= ndisks)
337cc71ff72SConrad Meyer 		return (EIO);
338cc71ff72SConrad Meyer 	n = pwrite(disk_fd[unit], src, size, offset);
339cc71ff72SConrad Meyer 	if (n < 0)
340cc71ff72SConrad Meyer 		return (errno);
341cc71ff72SConrad Meyer 	*resid = size - n;
342cc71ff72SConrad Meyer 	return (0);
343cc71ff72SConrad Meyer }
344cc71ff72SConrad Meyer 
345cc71ff72SConrad Meyer static int
346ad43dd69SMark Johnston cb_diskioctl(void *arg __unused, int unit, u_long cmd, void *data)
347a10c6f55SNeel Natu {
348a10c6f55SNeel Natu 	struct stat sb;
349a10c6f55SNeel Natu 
350cf087c12SPeter Grehan 	if (unit < 0 || unit >= ndisks)
351a10c6f55SNeel Natu 		return (EBADF);
352a10c6f55SNeel Natu 
353a10c6f55SNeel Natu 	switch (cmd) {
354a10c6f55SNeel Natu 	case DIOCGSECTORSIZE:
355a10c6f55SNeel Natu 		*(u_int *)data = 512;
356a10c6f55SNeel Natu 		break;
357a10c6f55SNeel Natu 	case DIOCGMEDIASIZE:
3586589ee29SAndriy Gapon 		if (fstat(disk_fd[unit], &sb) != 0)
359a10c6f55SNeel Natu 			return (ENOTTY);
3606589ee29SAndriy Gapon 		if (S_ISCHR(sb.st_mode) &&
3616589ee29SAndriy Gapon 		    ioctl(disk_fd[unit], DIOCGMEDIASIZE, &sb.st_size) != 0)
3626589ee29SAndriy Gapon 				return (ENOTTY);
3636589ee29SAndriy Gapon 		*(off_t *)data = sb.st_size;
364a10c6f55SNeel Natu 		break;
365a10c6f55SNeel Natu 	default:
366a10c6f55SNeel Natu 		return (ENOTTY);
367a10c6f55SNeel Natu 	}
368a10c6f55SNeel Natu 
369a10c6f55SNeel Natu 	return (0);
370a10c6f55SNeel Natu }
371a10c6f55SNeel Natu 
372c487da1eSNeel Natu /*
373c487da1eSNeel Natu  * Guest virtual machine i/o callbacks
374c487da1eSNeel Natu  */
375c487da1eSNeel Natu static int
376ad43dd69SMark Johnston cb_copyin(void *arg __unused, const void *from, uint64_t to, size_t size)
377c487da1eSNeel Natu {
378b060ba50SNeel Natu 	char *ptr;
379c487da1eSNeel Natu 
380c487da1eSNeel Natu 	to &= 0x7fffffff;
381b060ba50SNeel Natu 
382b060ba50SNeel Natu 	ptr = vm_map_gpa(ctx, to, size);
383b060ba50SNeel Natu 	if (ptr == NULL)
384c487da1eSNeel Natu 		return (EFAULT);
385c487da1eSNeel Natu 
386b060ba50SNeel Natu 	memcpy(ptr, from, size);
387c487da1eSNeel Natu 	return (0);
388c487da1eSNeel Natu }
389c487da1eSNeel Natu 
390c487da1eSNeel Natu static int
391ad43dd69SMark Johnston cb_copyout(void *arg __unused, uint64_t from, void *to, size_t size)
392c487da1eSNeel Natu {
393b060ba50SNeel Natu 	char *ptr;
394c487da1eSNeel Natu 
395c487da1eSNeel Natu 	from &= 0x7fffffff;
396b060ba50SNeel Natu 
397b060ba50SNeel Natu 	ptr = vm_map_gpa(ctx, from, size);
398b060ba50SNeel Natu 	if (ptr == NULL)
399c487da1eSNeel Natu 		return (EFAULT);
400c487da1eSNeel Natu 
401b060ba50SNeel Natu 	memcpy(to, ptr, size);
402c487da1eSNeel Natu 	return (0);
403c487da1eSNeel Natu }
404c487da1eSNeel Natu 
405c487da1eSNeel Natu static void
406ad43dd69SMark Johnston cb_setreg(void *arg __unused, int r, uint64_t v)
407c487da1eSNeel Natu {
408c487da1eSNeel Natu 	int error;
409c487da1eSNeel Natu 	enum vm_reg_name vmreg;
410c487da1eSNeel Natu 
411c487da1eSNeel Natu 	vmreg = VM_REG_LAST;
412c487da1eSNeel Natu 
413c487da1eSNeel Natu 	switch (r) {
414c487da1eSNeel Natu 	case 4:
415c487da1eSNeel Natu 		vmreg = VM_REG_GUEST_RSP;
416c487da1eSNeel Natu 		rsp = v;
417c487da1eSNeel Natu 		break;
418c487da1eSNeel Natu 	default:
419c487da1eSNeel Natu 		break;
420c487da1eSNeel Natu 	}
421c487da1eSNeel Natu 
422c487da1eSNeel Natu 	if (vmreg == VM_REG_LAST) {
423c487da1eSNeel Natu 		printf("test_setreg(%d): not implemented\n", r);
424c487da1eSNeel Natu 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
425c487da1eSNeel Natu 	}
426c487da1eSNeel Natu 
4277d9ef309SJohn Baldwin 	error = vm_set_register(vcpu, vmreg, v);
428c487da1eSNeel Natu 	if (error) {
429c487da1eSNeel Natu 		perror("vm_set_register");
430c487da1eSNeel Natu 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
431c487da1eSNeel Natu 	}
432c487da1eSNeel Natu }
433c487da1eSNeel Natu 
434c487da1eSNeel Natu static void
435ad43dd69SMark Johnston cb_setmsr(void *arg __unused, int r, uint64_t v)
436c487da1eSNeel Natu {
437c487da1eSNeel Natu 	int error;
438c487da1eSNeel Natu 	enum vm_reg_name vmreg;
439c487da1eSNeel Natu 
440c487da1eSNeel Natu 	vmreg = VM_REG_LAST;
441c487da1eSNeel Natu 
442c487da1eSNeel Natu 	switch (r) {
443c487da1eSNeel Natu 	case MSR_EFER:
444c487da1eSNeel Natu 		vmreg = VM_REG_GUEST_EFER;
445c487da1eSNeel Natu 		break;
446c487da1eSNeel Natu 	default:
447c487da1eSNeel Natu 		break;
448c487da1eSNeel Natu 	}
449c487da1eSNeel Natu 
450c487da1eSNeel Natu 	if (vmreg == VM_REG_LAST) {
451c487da1eSNeel Natu 		printf("test_setmsr(%d): not implemented\n", r);
452c487da1eSNeel Natu 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
453c487da1eSNeel Natu 	}
454c487da1eSNeel Natu 
4557d9ef309SJohn Baldwin 	error = vm_set_register(vcpu, vmreg, v);
456c487da1eSNeel Natu 	if (error) {
457c487da1eSNeel Natu 		perror("vm_set_msr");
458c487da1eSNeel Natu 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
459c487da1eSNeel Natu 	}
460c487da1eSNeel Natu }
461c487da1eSNeel Natu 
462c487da1eSNeel Natu static void
463ad43dd69SMark Johnston cb_setcr(void *arg __unused, int r, uint64_t v)
464c487da1eSNeel Natu {
465c487da1eSNeel Natu 	int error;
466c487da1eSNeel Natu 	enum vm_reg_name vmreg;
467c487da1eSNeel Natu 
468c487da1eSNeel Natu 	vmreg = VM_REG_LAST;
469c487da1eSNeel Natu 
470c487da1eSNeel Natu 	switch (r) {
471c487da1eSNeel Natu 	case 0:
472c487da1eSNeel Natu 		vmreg = VM_REG_GUEST_CR0;
473c487da1eSNeel Natu 		break;
474c487da1eSNeel Natu 	case 3:
475c487da1eSNeel Natu 		vmreg = VM_REG_GUEST_CR3;
476c487da1eSNeel Natu 		cr3 = v;
477c487da1eSNeel Natu 		break;
478c487da1eSNeel Natu 	case 4:
479c487da1eSNeel Natu 		vmreg = VM_REG_GUEST_CR4;
480c487da1eSNeel Natu 		break;
481c487da1eSNeel Natu 	default:
482c487da1eSNeel Natu 		break;
483c487da1eSNeel Natu 	}
484c487da1eSNeel Natu 
485c487da1eSNeel Natu 	if (vmreg == VM_REG_LAST) {
486c487da1eSNeel Natu 		printf("test_setcr(%d): not implemented\n", r);
487c487da1eSNeel Natu 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
488c487da1eSNeel Natu 	}
489c487da1eSNeel Natu 
4907d9ef309SJohn Baldwin 	error = vm_set_register(vcpu, vmreg, v);
491c487da1eSNeel Natu 	if (error) {
492c487da1eSNeel Natu 		perror("vm_set_cr");
493c487da1eSNeel Natu 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
494c487da1eSNeel Natu 	}
495c487da1eSNeel Natu }
496c487da1eSNeel Natu 
497c487da1eSNeel Natu static void
498ad43dd69SMark Johnston cb_setgdt(void *arg __unused, uint64_t base, size_t size)
499c487da1eSNeel Natu {
500c487da1eSNeel Natu 	int error;
501c487da1eSNeel Natu 
5027d9ef309SJohn Baldwin 	error = vm_set_desc(vcpu, VM_REG_GUEST_GDTR, base, size - 1, 0);
503c487da1eSNeel Natu 	if (error != 0) {
504c487da1eSNeel Natu 		perror("vm_set_desc(gdt)");
505c487da1eSNeel Natu 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
506c487da1eSNeel Natu 	}
507c487da1eSNeel Natu 
508c487da1eSNeel Natu 	gdtbase = base;
509c487da1eSNeel Natu }
510c487da1eSNeel Natu 
511c487da1eSNeel Natu static void
512ad43dd69SMark Johnston cb_exec(void *arg __unused, uint64_t rip)
513c487da1eSNeel Natu {
514c487da1eSNeel Natu 	int error;
515c487da1eSNeel Natu 
51600f3efe1SJohn Baldwin 	if (cr3 == 0)
5177d9ef309SJohn Baldwin 		error = vm_setup_freebsd_registers_i386(vcpu, rip, gdtbase,
51800f3efe1SJohn Baldwin 		    rsp);
51900f3efe1SJohn Baldwin 	else
5207d9ef309SJohn Baldwin 		error = vm_setup_freebsd_registers(vcpu, rip, cr3, gdtbase,
52100f3efe1SJohn Baldwin 		    rsp);
522c487da1eSNeel Natu 	if (error) {
523c487da1eSNeel Natu 		perror("vm_setup_freebsd_registers");
524c487da1eSNeel Natu 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
525c487da1eSNeel Natu 	}
526c487da1eSNeel Natu 
527c487da1eSNeel Natu 	cb_exit(NULL, 0);
528c487da1eSNeel Natu }
529c487da1eSNeel Natu 
530c487da1eSNeel Natu /*
531c487da1eSNeel Natu  * Misc
532c487da1eSNeel Natu  */
533c487da1eSNeel Natu 
534c487da1eSNeel Natu static void
535ad43dd69SMark Johnston cb_delay(void *arg __unused, int usec)
536c487da1eSNeel Natu {
537c487da1eSNeel Natu 
538c487da1eSNeel Natu 	usleep(usec);
539c487da1eSNeel Natu }
540c487da1eSNeel Natu 
541c487da1eSNeel Natu static void
542ad43dd69SMark Johnston cb_exit(void *arg __unused, int v)
543c487da1eSNeel Natu {
544c487da1eSNeel Natu 
5456380102cSPeter Grehan 	tcsetattr(consout_fd, TCSAFLUSH, &oldterm);
546c487da1eSNeel Natu 	exit(v);
547c487da1eSNeel Natu }
548c487da1eSNeel Natu 
549c487da1eSNeel Natu static void
550ad43dd69SMark Johnston cb_getmem(void *arg __unused, uint64_t *ret_lowmem, uint64_t *ret_highmem)
551c487da1eSNeel Natu {
552c487da1eSNeel Natu 
553be679db4SNeel Natu 	*ret_lowmem = vm_get_lowmem_size(ctx);
554be679db4SNeel Natu 	*ret_highmem = vm_get_highmem_size(ctx);
555c487da1eSNeel Natu }
556c487da1eSNeel Natu 
557b6afa84bSNeel Natu struct env {
558cb37fc82SWarner Losh 	char *str;	/* name=value */
559b6afa84bSNeel Natu 	SLIST_ENTRY(env) next;
560b6afa84bSNeel Natu };
561b6afa84bSNeel Natu 
562b6afa84bSNeel Natu static SLIST_HEAD(envhead, env) envhead;
563b6afa84bSNeel Natu 
564b6afa84bSNeel Natu static void
565ad43dd69SMark Johnston addenv(const char *str)
566b6afa84bSNeel Natu {
567b6afa84bSNeel Natu 	struct env *env;
568b6afa84bSNeel Natu 
569b6afa84bSNeel Natu 	env = malloc(sizeof(struct env));
570ad43dd69SMark Johnston 	if (env == NULL)
571ad43dd69SMark Johnston 		err(EX_OSERR, "malloc");
572ad43dd69SMark Johnston 	env->str = strdup(str);
573ad43dd69SMark Johnston 	if (env->str == NULL)
574ad43dd69SMark Johnston 		err(EX_OSERR, "strdup");
575b6afa84bSNeel Natu 	SLIST_INSERT_HEAD(&envhead, env, next);
576b6afa84bSNeel Natu }
577b6afa84bSNeel Natu 
578e8e6a5f9SWarner Losh static char *
579ad43dd69SMark Johnston cb_getenv(void *arg __unused, int num)
580c3e9ce33SNeel Natu {
581b6afa84bSNeel Natu 	int i;
582b6afa84bSNeel Natu 	struct env *env;
583c3e9ce33SNeel Natu 
584b6afa84bSNeel Natu 	i = 0;
585b6afa84bSNeel Natu 	SLIST_FOREACH(env, &envhead, next) {
586b6afa84bSNeel Natu 		if (i == num)
587b6afa84bSNeel Natu 			return (env->str);
588b6afa84bSNeel Natu 		i++;
589b6afa84bSNeel Natu 	}
590c3e9ce33SNeel Natu 
591c3e9ce33SNeel Natu 	return (NULL);
592c3e9ce33SNeel Natu }
593c3e9ce33SNeel Natu 
59448f337b8SMarcel Moolenaar static int
5957d9ef309SJohn Baldwin cb_vm_set_register(void *arg __unused, int vcpuid, int reg, uint64_t val)
59648f337b8SMarcel Moolenaar {
59748f337b8SMarcel Moolenaar 
5987d9ef309SJohn Baldwin 	assert(vcpuid == BSP);
5997d9ef309SJohn Baldwin 	return (vm_set_register(vcpu, reg, val));
60048f337b8SMarcel Moolenaar }
60148f337b8SMarcel Moolenaar 
60248f337b8SMarcel Moolenaar static int
6037d9ef309SJohn Baldwin cb_vm_set_desc(void *arg __unused, int vcpuid, int reg, uint64_t base,
604ad43dd69SMark Johnston     u_int limit, u_int access)
60548f337b8SMarcel Moolenaar {
60648f337b8SMarcel Moolenaar 
6077d9ef309SJohn Baldwin 	assert(vcpuid == BSP);
6087d9ef309SJohn Baldwin 	return (vm_set_desc(vcpu, reg, base, limit, access));
60948f337b8SMarcel Moolenaar }
61048f337b8SMarcel Moolenaar 
611d3d381b2SKyle Evans static void
612ad43dd69SMark Johnston cb_swap_interpreter(void *arg __unused, const char *interp_req)
613d3d381b2SKyle Evans {
614d3d381b2SKyle Evans 
615d3d381b2SKyle Evans 	/*
616d3d381b2SKyle Evans 	 * If the user specified a loader but we detected a mismatch, we should
617d3d381b2SKyle Evans 	 * not try to pivot to a different loader on them.
618d3d381b2SKyle Evans 	 */
619d3d381b2SKyle Evans 	free(loader);
620bf7c4fcbSKyle Evans 	if (explicit_loader_fd != -1) {
621d3d381b2SKyle Evans 		perror("requested loader interpreter does not match guest userboot");
622d3d381b2SKyle Evans 		cb_exit(NULL, 1);
623d3d381b2SKyle Evans 	}
624d3d381b2SKyle Evans 	if (interp_req == NULL || *interp_req == '\0') {
625d3d381b2SKyle Evans 		perror("guest failed to request an interpreter");
626d3d381b2SKyle Evans 		cb_exit(NULL, 1);
627d3d381b2SKyle Evans 	}
628d3d381b2SKyle Evans 
629bf7c4fcbSKyle Evans 	if (asprintf(&loader, "userboot_%s.so", interp_req) == -1)
630d3d381b2SKyle Evans 		err(EX_OSERR, "malloc");
631d3d381b2SKyle Evans 	longjmp(jb, 1);
632d3d381b2SKyle Evans }
633d3d381b2SKyle Evans 
634a10c6f55SNeel Natu static struct loader_callbacks cb = {
635c487da1eSNeel Natu 	.getc = cb_getc,
636c487da1eSNeel Natu 	.putc = cb_putc,
637c487da1eSNeel Natu 	.poll = cb_poll,
638c487da1eSNeel Natu 
639c487da1eSNeel Natu 	.open = cb_open,
640c487da1eSNeel Natu 	.close = cb_close,
641c487da1eSNeel Natu 	.isdir = cb_isdir,
642c487da1eSNeel Natu 	.read = cb_read,
643c487da1eSNeel Natu 	.readdir = cb_readdir,
644c487da1eSNeel Natu 	.seek = cb_seek,
645c487da1eSNeel Natu 	.stat = cb_stat,
646c487da1eSNeel Natu 
647c487da1eSNeel Natu 	.diskread = cb_diskread,
648cc71ff72SConrad Meyer 	.diskwrite = cb_diskwrite,
649a10c6f55SNeel Natu 	.diskioctl = cb_diskioctl,
650c487da1eSNeel Natu 
651c487da1eSNeel Natu 	.copyin = cb_copyin,
652c487da1eSNeel Natu 	.copyout = cb_copyout,
653c487da1eSNeel Natu 	.setreg = cb_setreg,
654c487da1eSNeel Natu 	.setmsr = cb_setmsr,
655c487da1eSNeel Natu 	.setcr = cb_setcr,
656c487da1eSNeel Natu 	.setgdt = cb_setgdt,
657c487da1eSNeel Natu 	.exec = cb_exec,
658c487da1eSNeel Natu 
659c487da1eSNeel Natu 	.delay = cb_delay,
660c487da1eSNeel Natu 	.exit = cb_exit,
661c487da1eSNeel Natu 	.getmem = cb_getmem,
662c3e9ce33SNeel Natu 
663c3e9ce33SNeel Natu 	.getenv = cb_getenv,
66448f337b8SMarcel Moolenaar 
66548f337b8SMarcel Moolenaar 	/* Version 4 additions */
66648f337b8SMarcel Moolenaar 	.vm_set_register = cb_vm_set_register,
66748f337b8SMarcel Moolenaar 	.vm_set_desc = cb_vm_set_desc,
668d3d381b2SKyle Evans 
669d3d381b2SKyle Evans 	/* Version 5 additions */
670d3d381b2SKyle Evans 	.swap_interpreter = cb_swap_interpreter,
671c487da1eSNeel Natu };
672c487da1eSNeel Natu 
6736380102cSPeter Grehan static int
6746380102cSPeter Grehan altcons_open(char *path)
6756380102cSPeter Grehan {
6766380102cSPeter Grehan 	struct stat sb;
6776380102cSPeter Grehan 	int err;
6786380102cSPeter Grehan 	int fd;
6796380102cSPeter Grehan 
6806380102cSPeter Grehan 	/*
6816380102cSPeter Grehan 	 * Allow stdio to be passed in so that the same string
6826380102cSPeter Grehan 	 * can be used for the bhyveload console and bhyve com-port
6836380102cSPeter Grehan 	 * parameters
6846380102cSPeter Grehan 	 */
6856380102cSPeter Grehan 	if (!strcmp(path, "stdio"))
6866380102cSPeter Grehan 		return (0);
6876380102cSPeter Grehan 
6886380102cSPeter Grehan 	err = stat(path, &sb);
6896380102cSPeter Grehan 	if (err == 0) {
6906380102cSPeter Grehan 		if (!S_ISCHR(sb.st_mode))
6916380102cSPeter Grehan 			err = ENOTSUP;
6926380102cSPeter Grehan 		else {
6936380102cSPeter Grehan 			fd = open(path, O_RDWR | O_NONBLOCK);
6946380102cSPeter Grehan 			if (fd < 0)
6956380102cSPeter Grehan 				err = errno;
6966380102cSPeter Grehan 			else
6976380102cSPeter Grehan 				consin_fd = consout_fd = fd;
6986380102cSPeter Grehan 		}
6996380102cSPeter Grehan 	}
7006380102cSPeter Grehan 
7016380102cSPeter Grehan 	return (err);
7026380102cSPeter Grehan }
7036380102cSPeter Grehan 
704cf087c12SPeter Grehan static int
705cf087c12SPeter Grehan disk_open(char *path)
706cf087c12SPeter Grehan {
707a0bc451fSSean Chittenden 	int fd;
708cf087c12SPeter Grehan 
7090db293c1SAllan Jude 	if (ndisks >= NDISKS)
710cf087c12SPeter Grehan 		return (ERANGE);
711cf087c12SPeter Grehan 
7125a023bd2SRobert Wing 	fd = open(path, O_RDWR);
713a0bc451fSSean Chittenden 	if (fd < 0)
714a0bc451fSSean Chittenden 		return (errno);
715cf087c12SPeter Grehan 
716cf087c12SPeter Grehan 	disk_fd[ndisks] = fd;
717cf087c12SPeter Grehan 	ndisks++;
718cf087c12SPeter Grehan 
719a0bc451fSSean Chittenden 	return (0);
720cf087c12SPeter Grehan }
721cf087c12SPeter Grehan 
722c487da1eSNeel Natu static void
723c487da1eSNeel Natu usage(void)
724c487da1eSNeel Natu {
725c487da1eSNeel Natu 
726b060ba50SNeel Natu 	fprintf(stderr,
7279b1aa8d6SNeel Natu 	    "usage: %s [-S][-c <console-device>] [-d <disk-path>] [-e <name=value>]\n"
7286ee52c65SRoman Bogorodskiy 	    "       %*s [-h <host-path>] [-m memsize[K|k|M|m|G|g|T|t]] <vmname>\n",
7296380102cSPeter Grehan 	    progname,
730b5331f4dSNeel Natu 	    (int)strlen(progname), "");
731c487da1eSNeel Natu 	exit(1);
732c487da1eSNeel Natu }
733c487da1eSNeel Natu 
7346779d44bSKyle Evans static void
7356779d44bSKyle Evans hostbase_open(const char *base)
7366779d44bSKyle Evans {
7376779d44bSKyle Evans 
7386779d44bSKyle Evans 	if (hostbase_fd != -1)
7396779d44bSKyle Evans 		close(hostbase_fd);
7406779d44bSKyle Evans 	hostbase_fd = open(base, O_DIRECTORY | O_PATH);
7416779d44bSKyle Evans 	if (hostbase_fd == -1)
7426779d44bSKyle Evans 		err(EX_OSERR, "open");
7436779d44bSKyle Evans }
7446779d44bSKyle Evans 
745bf7c4fcbSKyle Evans static void
746bf7c4fcbSKyle Evans loader_open(int bootfd)
747bf7c4fcbSKyle Evans {
748bf7c4fcbSKyle Evans 	int fd;
749bf7c4fcbSKyle Evans 
750bf7c4fcbSKyle Evans 	if (loader == NULL) {
751bf7c4fcbSKyle Evans 		loader = strdup("userboot.so");
752bf7c4fcbSKyle Evans 		if (loader == NULL)
753bf7c4fcbSKyle Evans 			err(EX_OSERR, "malloc");
754bf7c4fcbSKyle Evans 	}
755bf7c4fcbSKyle Evans 
756bf7c4fcbSKyle Evans 	assert(bootfd >= 0 || explicit_loader_fd >= 0);
757bf7c4fcbSKyle Evans 	if (explicit_loader_fd >= 0)
758bf7c4fcbSKyle Evans 		fd = explicit_loader_fd;
759bf7c4fcbSKyle Evans 	else
760bf7c4fcbSKyle Evans 		fd = openat(bootfd, loader, O_RDONLY | O_RESOLVE_BENEATH);
761bf7c4fcbSKyle Evans 	if (fd == -1)
762bf7c4fcbSKyle Evans 		err(EX_OSERR, "openat");
763bf7c4fcbSKyle Evans 
764bf7c4fcbSKyle Evans 	loader_hdl = fdlopen(fd, RTLD_LOCAL);
765bf7c4fcbSKyle Evans 	if (!loader_hdl)
766bf7c4fcbSKyle Evans 		errx(EX_OSERR, "dlopen: %s", dlerror());
767bf7c4fcbSKyle Evans }
768bf7c4fcbSKyle Evans 
769c487da1eSNeel Natu int
770c487da1eSNeel Natu main(int argc, char** argv)
771c487da1eSNeel Natu {
772a10c6f55SNeel Natu 	void (*func)(struct loader_callbacks *, void *, int, int);
773b060ba50SNeel Natu 	uint64_t mem_size;
774bf7c4fcbSKyle Evans 	int bootfd, opt, error, memflags, need_reinit;
775c487da1eSNeel Natu 
776bf7c4fcbSKyle Evans 	bootfd = -1;
777b5331f4dSNeel Natu 	progname = basename(argv[0]);
778c487da1eSNeel Natu 
7799b1aa8d6SNeel Natu 	memflags = 0;
780b060ba50SNeel Natu 	mem_size = 256 * MB;
781c487da1eSNeel Natu 
7826380102cSPeter Grehan 	consin_fd = STDIN_FILENO;
7836380102cSPeter Grehan 	consout_fd = STDOUT_FILENO;
7846380102cSPeter Grehan 
785568e3a8dSMarcel Moolenaar 	while ((opt = getopt(argc, argv, "CSc:d:e:h:l:m:")) != -1) {
786c487da1eSNeel Natu 		switch (opt) {
7876380102cSPeter Grehan 		case 'c':
7886380102cSPeter Grehan 			error = altcons_open(optarg);
7896380102cSPeter Grehan 			if (error != 0)
7906380102cSPeter Grehan 				errx(EX_USAGE, "Could not open '%s'", optarg);
7916380102cSPeter Grehan 			break;
792cf087c12SPeter Grehan 
793c487da1eSNeel Natu 		case 'd':
794cf087c12SPeter Grehan 			error = disk_open(optarg);
795cf087c12SPeter Grehan 			if (error != 0)
796cf087c12SPeter Grehan 				errx(EX_USAGE, "Could not open '%s'", optarg);
797c487da1eSNeel Natu 			break;
798c487da1eSNeel Natu 
799b6afa84bSNeel Natu 		case 'e':
800b6afa84bSNeel Natu 			addenv(optarg);
801b6afa84bSNeel Natu 			break;
802b6afa84bSNeel Natu 
803c487da1eSNeel Natu 		case 'h':
8046779d44bSKyle Evans 			hostbase_open(optarg);
805c487da1eSNeel Natu 			break;
806c487da1eSNeel Natu 
8078c96dcc1SMarcel Moolenaar 		case 'l':
8088c96dcc1SMarcel Moolenaar 			if (loader != NULL)
8098c96dcc1SMarcel Moolenaar 				errx(EX_USAGE, "-l can only be given once");
8108c96dcc1SMarcel Moolenaar 			loader = strdup(optarg);
8118c96dcc1SMarcel Moolenaar 			if (loader == NULL)
8128c96dcc1SMarcel Moolenaar 				err(EX_OSERR, "malloc");
813bf7c4fcbSKyle Evans 			explicit_loader_fd = open(loader, O_RDONLY);
814bf7c4fcbSKyle Evans 			if (explicit_loader_fd == -1)
815bf7c4fcbSKyle Evans 				err(EX_OSERR, "%s", loader);
8168c96dcc1SMarcel Moolenaar 			break;
8178c96dcc1SMarcel Moolenaar 
818c487da1eSNeel Natu 		case 'm':
819200758f1SNeel Natu 			error = vm_parse_memsize(optarg, &mem_size);
820200758f1SNeel Natu 			if (error != 0)
821200758f1SNeel Natu 				errx(EX_USAGE, "Invalid memsize '%s'", optarg);
822c487da1eSNeel Natu 			break;
823568e3a8dSMarcel Moolenaar 		case 'C':
824568e3a8dSMarcel Moolenaar 			memflags |= VM_MEM_F_INCORE;
825568e3a8dSMarcel Moolenaar 			break;
8269b1aa8d6SNeel Natu 		case 'S':
8279b1aa8d6SNeel Natu 			memflags |= VM_MEM_F_WIRED;
8289b1aa8d6SNeel Natu 			break;
829c487da1eSNeel Natu 		case '?':
830c487da1eSNeel Natu 			usage();
831c487da1eSNeel Natu 		}
832c487da1eSNeel Natu 	}
833c487da1eSNeel Natu 
834c487da1eSNeel Natu 	argc -= optind;
835c487da1eSNeel Natu 	argv += optind;
836c487da1eSNeel Natu 
837c487da1eSNeel Natu 	if (argc != 1)
838c487da1eSNeel Natu 		usage();
839c487da1eSNeel Natu 
840c487da1eSNeel Natu 	vmname = argv[0];
841c487da1eSNeel Natu 
8425fcf252fSNeel Natu 	need_reinit = 0;
843c487da1eSNeel Natu 	error = vm_create(vmname);
8445fcf252fSNeel Natu 	if (error) {
8455fcf252fSNeel Natu 		if (errno != EEXIST) {
846c487da1eSNeel Natu 			perror("vm_create");
847c487da1eSNeel Natu 			exit(1);
8485fcf252fSNeel Natu 		}
8495fcf252fSNeel Natu 		need_reinit = 1;
850c487da1eSNeel Natu 	}
851c487da1eSNeel Natu 
852c487da1eSNeel Natu 	ctx = vm_open(vmname);
853c487da1eSNeel Natu 	if (ctx == NULL) {
854c487da1eSNeel Natu 		perror("vm_open");
855c487da1eSNeel Natu 		exit(1);
856c487da1eSNeel Natu 	}
857c487da1eSNeel Natu 
858bf7c4fcbSKyle Evans 	/*
859bf7c4fcbSKyle Evans 	 * If we weren't given an explicit loader to use, we need to support the
860bf7c4fcbSKyle Evans 	 * guest requesting a different one.
861bf7c4fcbSKyle Evans 	 */
862bf7c4fcbSKyle Evans 	if (explicit_loader_fd == -1) {
863bf7c4fcbSKyle Evans 		bootfd = open("/boot", O_DIRECTORY | O_PATH);
864bf7c4fcbSKyle Evans 		if (bootfd == -1) {
865bf7c4fcbSKyle Evans 			perror("open");
866bf7c4fcbSKyle Evans 			exit(1);
867bf7c4fcbSKyle Evans 		}
868bf7c4fcbSKyle Evans 	}
869bf7c4fcbSKyle Evans 
8707d9ef309SJohn Baldwin 	vcpu = vm_vcpu_open(ctx, BSP);
8717d9ef309SJohn Baldwin 
872*8bf0882eSKyle Evans 	caph_cache_catpages();
873*8bf0882eSKyle Evans 	if (caph_enter() < 0) {
874*8bf0882eSKyle Evans 		perror("caph_enter");
875*8bf0882eSKyle Evans 		exit(1);
876*8bf0882eSKyle Evans 	}
877*8bf0882eSKyle Evans 
878d3d381b2SKyle Evans 	/*
879d3d381b2SKyle Evans 	 * setjmp in the case the guest wants to swap out interpreter,
880d3d381b2SKyle Evans 	 * cb_swap_interpreter will swap out loader as appropriate and set
881d3d381b2SKyle Evans 	 * need_reinit so that we end up in a clean state once again.
882d3d381b2SKyle Evans 	 */
883bf7c4fcbSKyle Evans 	if (setjmp(jb) != 0) {
884bf7c4fcbSKyle Evans 		dlclose(loader_hdl);
885bf7c4fcbSKyle Evans 		loader_hdl = NULL;
886bf7c4fcbSKyle Evans 
887bf7c4fcbSKyle Evans 		need_reinit = 1;
888bf7c4fcbSKyle Evans 	}
889d3d381b2SKyle Evans 
8905fcf252fSNeel Natu 	if (need_reinit) {
8915fcf252fSNeel Natu 		error = vm_reinit(ctx);
8925fcf252fSNeel Natu 		if (error) {
8935fcf252fSNeel Natu 			perror("vm_reinit");
8945fcf252fSNeel Natu 			exit(1);
8955fcf252fSNeel Natu 		}
8965fcf252fSNeel Natu 	}
8975fcf252fSNeel Natu 
8989b1aa8d6SNeel Natu 	vm_set_memflags(ctx, memflags);
899b060ba50SNeel Natu 	error = vm_setup_memory(ctx, mem_size, VM_MMAP_ALL);
900c487da1eSNeel Natu 	if (error) {
901b060ba50SNeel Natu 		perror("vm_setup_memory");
902c487da1eSNeel Natu 		exit(1);
903c487da1eSNeel Natu 	}
904c487da1eSNeel Natu 
905bf7c4fcbSKyle Evans 	loader_open(bootfd);
906d3d381b2SKyle Evans 	func = dlsym(loader_hdl, "loader_main");
9078c96dcc1SMarcel Moolenaar 	if (!func) {
9088c96dcc1SMarcel Moolenaar 		printf("%s\n", dlerror());
9098c96dcc1SMarcel Moolenaar 		free(loader);
9108c96dcc1SMarcel Moolenaar 		return (1);
9118c96dcc1SMarcel Moolenaar 	}
9128c96dcc1SMarcel Moolenaar 
9136380102cSPeter Grehan 	tcgetattr(consout_fd, &term);
914c487da1eSNeel Natu 	oldterm = term;
9156380102cSPeter Grehan 	cfmakeraw(&term);
9166380102cSPeter Grehan 	term.c_cflag |= CLOCAL;
9176380102cSPeter Grehan 
9186380102cSPeter Grehan 	tcsetattr(consout_fd, TCSAFLUSH, &term);
9196380102cSPeter Grehan 
920b6afa84bSNeel Natu 	addenv("smbios.bios.vendor=BHYVE");
921b6afa84bSNeel Natu 	addenv("boot_serial=1");
922b6afa84bSNeel Natu 
923d3d381b2SKyle Evans 	func(&cb, NULL, USERBOOT_VERSION_5, ndisks);
9248c96dcc1SMarcel Moolenaar 
9258c96dcc1SMarcel Moolenaar 	free(loader);
9268c96dcc1SMarcel Moolenaar 	return (0);
927c487da1eSNeel Natu }
928