xref: /freebsd-src/usr.sbin/bhyveload/bhyveload.c (revision bf7c4fcbbb05ff99afde0744d013feeb35d77191)
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 
81c487da1eSNeel Natu #include <vmmapi.h>
82c487da1eSNeel Natu 
83c487da1eSNeel Natu #include "userboot.h"
84c487da1eSNeel Natu 
85c487da1eSNeel Natu #define	MB	(1024 * 1024UL)
86c487da1eSNeel Natu #define	GB	(1024 * 1024 * 1024UL)
87c487da1eSNeel Natu #define	BSP	0
88c487da1eSNeel Natu 
89cf087c12SPeter Grehan #define	NDISKS	32
90cf087c12SPeter Grehan 
91c487da1eSNeel Natu static struct termios term, oldterm;
92cf087c12SPeter Grehan static int disk_fd[NDISKS];
93cf087c12SPeter Grehan static int ndisks;
946380102cSPeter Grehan static int consin_fd, consout_fd;
956779d44bSKyle Evans static int hostbase_fd = -1;
96c487da1eSNeel Natu 
97d3d381b2SKyle Evans static void *loader_hdl;
98d3d381b2SKyle Evans static char *loader;
99*bf7c4fcbSKyle Evans static int explicit_loader_fd;
100d3d381b2SKyle Evans static jmp_buf jb;
101d3d381b2SKyle Evans 
102b060ba50SNeel Natu static char *vmname, *progname;
103c487da1eSNeel Natu static struct vmctx *ctx;
1047d9ef309SJohn Baldwin static struct vcpu *vcpu;
105c487da1eSNeel Natu 
106c487da1eSNeel Natu static uint64_t gdtbase, cr3, rsp;
107c487da1eSNeel Natu 
108c487da1eSNeel Natu static void cb_exit(void *arg, int v);
109c487da1eSNeel Natu 
110c487da1eSNeel Natu /*
111c487da1eSNeel Natu  * Console i/o callbacks
112c487da1eSNeel Natu  */
113c487da1eSNeel Natu 
114c487da1eSNeel Natu static void
115ad43dd69SMark Johnston cb_putc(void *arg __unused, int ch)
116c487da1eSNeel Natu {
117c487da1eSNeel Natu 	char c = ch;
118c487da1eSNeel Natu 
1196380102cSPeter Grehan 	(void) write(consout_fd, &c, 1);
120c487da1eSNeel Natu }
121c487da1eSNeel Natu 
122c487da1eSNeel Natu static int
123ad43dd69SMark Johnston cb_getc(void *arg __unused)
124c487da1eSNeel Natu {
125c487da1eSNeel Natu 	char c;
126c487da1eSNeel Natu 
1276380102cSPeter Grehan 	if (read(consin_fd, &c, 1) == 1)
128c487da1eSNeel Natu 		return (c);
129c487da1eSNeel Natu 	return (-1);
130c487da1eSNeel Natu }
131c487da1eSNeel Natu 
132c487da1eSNeel Natu static int
133ad43dd69SMark Johnston cb_poll(void *arg __unused)
134c487da1eSNeel Natu {
135c487da1eSNeel Natu 	int n;
136c487da1eSNeel Natu 
1376380102cSPeter Grehan 	if (ioctl(consin_fd, FIONREAD, &n) >= 0)
138c487da1eSNeel Natu 		return (n > 0);
139c487da1eSNeel Natu 	return (0);
140c487da1eSNeel Natu }
141c487da1eSNeel Natu 
142c487da1eSNeel Natu /*
143c487da1eSNeel Natu  * Host filesystem i/o callbacks
144c487da1eSNeel Natu  */
145c487da1eSNeel Natu 
146c487da1eSNeel Natu struct cb_file {
147c487da1eSNeel Natu 	int cf_isdir;
148c487da1eSNeel Natu 	size_t cf_size;
149c487da1eSNeel Natu 	struct stat cf_stat;
150c487da1eSNeel Natu 	union {
151c487da1eSNeel Natu 		int fd;
152c487da1eSNeel Natu 		DIR *dir;
153c487da1eSNeel Natu 	} cf_u;
154c487da1eSNeel Natu };
155c487da1eSNeel Natu 
156c487da1eSNeel Natu static int
157ad43dd69SMark Johnston cb_open(void *arg __unused, const char *filename, void **hp)
158c487da1eSNeel Natu {
159c487da1eSNeel Natu 	struct cb_file *cf;
1606779d44bSKyle Evans 	struct stat sb;
1616779d44bSKyle Evans 	int fd, flags;
162c487da1eSNeel Natu 
1636779d44bSKyle Evans 	cf = NULL;
1646779d44bSKyle Evans 	fd = -1;
1656779d44bSKyle Evans 	flags = O_RDONLY | O_RESOLVE_BENEATH;
1666779d44bSKyle Evans 	if (hostbase_fd == -1)
167c487da1eSNeel Natu 		return (ENOENT);
168c487da1eSNeel Natu 
1696779d44bSKyle Evans 	/* Absolute paths are relative to our hostbase, chop off leading /. */
1706779d44bSKyle Evans 	if (filename[0] == '/')
1716779d44bSKyle Evans 		filename++;
1726779d44bSKyle Evans 
1736779d44bSKyle Evans 	/* Lookup of /, use . instead. */
1746779d44bSKyle Evans 	if (filename[0] == '\0')
1756779d44bSKyle Evans 		filename = ".";
1766779d44bSKyle Evans 
1776779d44bSKyle Evans 	if (fstatat(hostbase_fd, filename, &sb, AT_RESOLVE_BENEATH) < 0)
178c487da1eSNeel Natu 		return (errno);
1796779d44bSKyle Evans 
1806779d44bSKyle Evans 	if (!S_ISDIR(sb.st_mode) && !S_ISREG(sb.st_mode))
1816779d44bSKyle Evans 		return (EINVAL);
1826779d44bSKyle Evans 
1836779d44bSKyle Evans 	if (S_ISDIR(sb.st_mode))
1846779d44bSKyle Evans 		flags |= O_DIRECTORY;
1856779d44bSKyle Evans 
1866779d44bSKyle Evans 	/* May be opening the root dir */
1876779d44bSKyle Evans 	fd = openat(hostbase_fd, filename, flags);
1886779d44bSKyle Evans 	if (fd < 0)
1896779d44bSKyle Evans 		return (errno);
1906779d44bSKyle Evans 
1916779d44bSKyle Evans 	cf = malloc(sizeof(struct cb_file));
1926779d44bSKyle Evans 	if (cf == NULL) {
1936779d44bSKyle Evans 		close(fd);
1946779d44bSKyle Evans 		return (ENOMEM);
195c487da1eSNeel Natu 	}
196c487da1eSNeel Natu 
1976779d44bSKyle Evans 	cf->cf_stat = sb;
1988f61276dSPedro F. Giffuni 	cf->cf_size = cf->cf_stat.st_size;
1996779d44bSKyle Evans 
200c487da1eSNeel Natu 	if (S_ISDIR(cf->cf_stat.st_mode)) {
201c487da1eSNeel Natu 		cf->cf_isdir = 1;
2026779d44bSKyle Evans 		cf->cf_u.dir = fdopendir(fd);
2036779d44bSKyle Evans 		if (cf->cf_u.dir == NULL) {
2046779d44bSKyle Evans 			close(fd);
205c487da1eSNeel Natu 			free(cf);
2066779d44bSKyle Evans 			return (ENOMEM);
2076779d44bSKyle Evans 		}
2086779d44bSKyle Evans 	} else {
2096779d44bSKyle Evans 		assert(S_ISREG(cf->cf_stat.st_mode));
2106779d44bSKyle Evans 		cf->cf_isdir = 0;
2116779d44bSKyle Evans 		cf->cf_u.fd = fd;
2126779d44bSKyle Evans 	}
2136779d44bSKyle Evans 	*hp = cf;
2146779d44bSKyle Evans 	return (0);
215c487da1eSNeel Natu }
216c487da1eSNeel Natu 
217c487da1eSNeel Natu static int
218ad43dd69SMark Johnston cb_close(void *arg __unused, void *h)
219c487da1eSNeel Natu {
220c487da1eSNeel Natu 	struct cb_file *cf = h;
221c487da1eSNeel Natu 
222c487da1eSNeel Natu 	if (cf->cf_isdir)
223c487da1eSNeel Natu 		closedir(cf->cf_u.dir);
224c487da1eSNeel Natu 	else
225c487da1eSNeel Natu 		close(cf->cf_u.fd);
226c487da1eSNeel Natu 	free(cf);
227c487da1eSNeel Natu 
228c487da1eSNeel Natu 	return (0);
229c487da1eSNeel Natu }
230c487da1eSNeel Natu 
231c487da1eSNeel Natu static int
232ad43dd69SMark Johnston cb_isdir(void *arg __unused, void *h)
233c487da1eSNeel Natu {
234c487da1eSNeel Natu 	struct cb_file *cf = h;
235c487da1eSNeel Natu 
236c487da1eSNeel Natu 	return (cf->cf_isdir);
237c487da1eSNeel Natu }
238c487da1eSNeel Natu 
239c487da1eSNeel Natu static int
240ad43dd69SMark Johnston cb_read(void *arg __unused, void *h, void *buf, size_t size, size_t *resid)
241c487da1eSNeel Natu {
242c487da1eSNeel Natu 	struct cb_file *cf = h;
243c487da1eSNeel Natu 	ssize_t sz;
244c487da1eSNeel Natu 
245c487da1eSNeel Natu 	if (cf->cf_isdir)
246c487da1eSNeel Natu 		return (EINVAL);
247c487da1eSNeel Natu 	sz = read(cf->cf_u.fd, buf, size);
248c487da1eSNeel Natu 	if (sz < 0)
249c487da1eSNeel Natu 		return (EINVAL);
250c487da1eSNeel Natu 	*resid = size - sz;
251c487da1eSNeel Natu 	return (0);
252c487da1eSNeel Natu }
253c487da1eSNeel Natu 
254c487da1eSNeel Natu static int
255ad43dd69SMark Johnston cb_readdir(void *arg __unused, void *h, uint32_t *fileno_return,
256ad43dd69SMark Johnston     uint8_t *type_return, size_t *namelen_return, char *name)
257c487da1eSNeel Natu {
258c487da1eSNeel Natu 	struct cb_file *cf = h;
259c487da1eSNeel Natu 	struct dirent *dp;
260c487da1eSNeel Natu 
261c487da1eSNeel Natu 	if (!cf->cf_isdir)
262c487da1eSNeel Natu 		return (EINVAL);
263c487da1eSNeel Natu 
264c487da1eSNeel Natu 	dp = readdir(cf->cf_u.dir);
265c487da1eSNeel Natu 	if (!dp)
266c487da1eSNeel Natu 		return (ENOENT);
267c487da1eSNeel Natu 
268c487da1eSNeel Natu 	/*
269c487da1eSNeel Natu 	 * Note: d_namlen is in the range 0..255 and therefore less
270c487da1eSNeel Natu 	 * than PATH_MAX so we don't need to test before copying.
271c487da1eSNeel Natu 	 */
272c487da1eSNeel Natu 	*fileno_return = dp->d_fileno;
273c487da1eSNeel Natu 	*type_return = dp->d_type;
274c487da1eSNeel Natu 	*namelen_return = dp->d_namlen;
275c487da1eSNeel Natu 	memcpy(name, dp->d_name, dp->d_namlen);
276c487da1eSNeel Natu 	name[dp->d_namlen] = 0;
277c487da1eSNeel Natu 
278c487da1eSNeel Natu 	return (0);
279c487da1eSNeel Natu }
280c487da1eSNeel Natu 
281c487da1eSNeel Natu static int
282ad43dd69SMark Johnston cb_seek(void *arg __unused, void *h, uint64_t offset, int whence)
283c487da1eSNeel Natu {
284c487da1eSNeel Natu 	struct cb_file *cf = h;
285c487da1eSNeel Natu 
286c487da1eSNeel Natu 	if (cf->cf_isdir)
287c487da1eSNeel Natu 		return (EINVAL);
288c487da1eSNeel Natu 	if (lseek(cf->cf_u.fd, offset, whence) < 0)
289c487da1eSNeel Natu 		return (errno);
290c487da1eSNeel Natu 	return (0);
291c487da1eSNeel Natu }
292c487da1eSNeel Natu 
293c487da1eSNeel Natu static int
294ad43dd69SMark Johnston cb_stat(void *arg __unused, void *h, struct stat *sbp)
295c487da1eSNeel Natu {
296c487da1eSNeel Natu 	struct cb_file *cf = h;
297c487da1eSNeel Natu 
29853f151f9SSimon J. Gerraty 	memset(sbp, 0, sizeof(struct stat));
29953f151f9SSimon J. Gerraty 	sbp->st_mode = cf->cf_stat.st_mode;
30053f151f9SSimon J. Gerraty 	sbp->st_uid = cf->cf_stat.st_uid;
30153f151f9SSimon J. Gerraty 	sbp->st_gid = cf->cf_stat.st_gid;
30253f151f9SSimon J. Gerraty 	sbp->st_size = cf->cf_stat.st_size;
30353f151f9SSimon J. Gerraty 	sbp->st_mtime = cf->cf_stat.st_mtime;
30453f151f9SSimon J. Gerraty 	sbp->st_dev = cf->cf_stat.st_dev;
30553f151f9SSimon J. Gerraty 	sbp->st_ino = cf->cf_stat.st_ino;
30653f151f9SSimon J. Gerraty 
307c487da1eSNeel Natu 	return (0);
308c487da1eSNeel Natu }
309c487da1eSNeel Natu 
310c487da1eSNeel Natu /*
311c487da1eSNeel Natu  * Disk image i/o callbacks
312c487da1eSNeel Natu  */
313c487da1eSNeel Natu 
314c487da1eSNeel Natu static int
315ad43dd69SMark Johnston cb_diskread(void *arg __unused, int unit, uint64_t from, void *to, size_t size,
316c487da1eSNeel Natu     size_t *resid)
317c487da1eSNeel Natu {
318c487da1eSNeel Natu 	ssize_t n;
319c487da1eSNeel Natu 
320cf087c12SPeter Grehan 	if (unit < 0 || unit >= ndisks)
321c487da1eSNeel Natu 		return (EIO);
322cf087c12SPeter Grehan 	n = pread(disk_fd[unit], to, size, from);
323c487da1eSNeel Natu 	if (n < 0)
324c487da1eSNeel Natu 		return (errno);
325c487da1eSNeel Natu 	*resid = size - n;
326c487da1eSNeel Natu 	return (0);
327c487da1eSNeel Natu }
328c487da1eSNeel Natu 
329a10c6f55SNeel Natu static int
330ad43dd69SMark Johnston cb_diskwrite(void *arg __unused, int unit, uint64_t offset, void *src,
331ad43dd69SMark Johnston     size_t size, size_t *resid)
332cc71ff72SConrad Meyer {
333cc71ff72SConrad Meyer 	ssize_t n;
334cc71ff72SConrad Meyer 
335cc71ff72SConrad Meyer 	if (unit < 0 || unit >= ndisks)
336cc71ff72SConrad Meyer 		return (EIO);
337cc71ff72SConrad Meyer 	n = pwrite(disk_fd[unit], src, size, offset);
338cc71ff72SConrad Meyer 	if (n < 0)
339cc71ff72SConrad Meyer 		return (errno);
340cc71ff72SConrad Meyer 	*resid = size - n;
341cc71ff72SConrad Meyer 	return (0);
342cc71ff72SConrad Meyer }
343cc71ff72SConrad Meyer 
344cc71ff72SConrad Meyer static int
345ad43dd69SMark Johnston cb_diskioctl(void *arg __unused, int unit, u_long cmd, void *data)
346a10c6f55SNeel Natu {
347a10c6f55SNeel Natu 	struct stat sb;
348a10c6f55SNeel Natu 
349cf087c12SPeter Grehan 	if (unit < 0 || unit >= ndisks)
350a10c6f55SNeel Natu 		return (EBADF);
351a10c6f55SNeel Natu 
352a10c6f55SNeel Natu 	switch (cmd) {
353a10c6f55SNeel Natu 	case DIOCGSECTORSIZE:
354a10c6f55SNeel Natu 		*(u_int *)data = 512;
355a10c6f55SNeel Natu 		break;
356a10c6f55SNeel Natu 	case DIOCGMEDIASIZE:
3576589ee29SAndriy Gapon 		if (fstat(disk_fd[unit], &sb) != 0)
358a10c6f55SNeel Natu 			return (ENOTTY);
3596589ee29SAndriy Gapon 		if (S_ISCHR(sb.st_mode) &&
3606589ee29SAndriy Gapon 		    ioctl(disk_fd[unit], DIOCGMEDIASIZE, &sb.st_size) != 0)
3616589ee29SAndriy Gapon 				return (ENOTTY);
3626589ee29SAndriy Gapon 		*(off_t *)data = sb.st_size;
363a10c6f55SNeel Natu 		break;
364a10c6f55SNeel Natu 	default:
365a10c6f55SNeel Natu 		return (ENOTTY);
366a10c6f55SNeel Natu 	}
367a10c6f55SNeel Natu 
368a10c6f55SNeel Natu 	return (0);
369a10c6f55SNeel Natu }
370a10c6f55SNeel Natu 
371c487da1eSNeel Natu /*
372c487da1eSNeel Natu  * Guest virtual machine i/o callbacks
373c487da1eSNeel Natu  */
374c487da1eSNeel Natu static int
375ad43dd69SMark Johnston cb_copyin(void *arg __unused, const void *from, uint64_t to, size_t size)
376c487da1eSNeel Natu {
377b060ba50SNeel Natu 	char *ptr;
378c487da1eSNeel Natu 
379c487da1eSNeel Natu 	to &= 0x7fffffff;
380b060ba50SNeel Natu 
381b060ba50SNeel Natu 	ptr = vm_map_gpa(ctx, to, size);
382b060ba50SNeel Natu 	if (ptr == NULL)
383c487da1eSNeel Natu 		return (EFAULT);
384c487da1eSNeel Natu 
385b060ba50SNeel Natu 	memcpy(ptr, from, size);
386c487da1eSNeel Natu 	return (0);
387c487da1eSNeel Natu }
388c487da1eSNeel Natu 
389c487da1eSNeel Natu static int
390ad43dd69SMark Johnston cb_copyout(void *arg __unused, uint64_t from, void *to, size_t size)
391c487da1eSNeel Natu {
392b060ba50SNeel Natu 	char *ptr;
393c487da1eSNeel Natu 
394c487da1eSNeel Natu 	from &= 0x7fffffff;
395b060ba50SNeel Natu 
396b060ba50SNeel Natu 	ptr = vm_map_gpa(ctx, from, size);
397b060ba50SNeel Natu 	if (ptr == NULL)
398c487da1eSNeel Natu 		return (EFAULT);
399c487da1eSNeel Natu 
400b060ba50SNeel Natu 	memcpy(to, ptr, size);
401c487da1eSNeel Natu 	return (0);
402c487da1eSNeel Natu }
403c487da1eSNeel Natu 
404c487da1eSNeel Natu static void
405ad43dd69SMark Johnston cb_setreg(void *arg __unused, int r, uint64_t v)
406c487da1eSNeel Natu {
407c487da1eSNeel Natu 	int error;
408c487da1eSNeel Natu 	enum vm_reg_name vmreg;
409c487da1eSNeel Natu 
410c487da1eSNeel Natu 	vmreg = VM_REG_LAST;
411c487da1eSNeel Natu 
412c487da1eSNeel Natu 	switch (r) {
413c487da1eSNeel Natu 	case 4:
414c487da1eSNeel Natu 		vmreg = VM_REG_GUEST_RSP;
415c487da1eSNeel Natu 		rsp = v;
416c487da1eSNeel Natu 		break;
417c487da1eSNeel Natu 	default:
418c487da1eSNeel Natu 		break;
419c487da1eSNeel Natu 	}
420c487da1eSNeel Natu 
421c487da1eSNeel Natu 	if (vmreg == VM_REG_LAST) {
422c487da1eSNeel Natu 		printf("test_setreg(%d): not implemented\n", r);
423c487da1eSNeel Natu 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
424c487da1eSNeel Natu 	}
425c487da1eSNeel Natu 
4267d9ef309SJohn Baldwin 	error = vm_set_register(vcpu, vmreg, v);
427c487da1eSNeel Natu 	if (error) {
428c487da1eSNeel Natu 		perror("vm_set_register");
429c487da1eSNeel Natu 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
430c487da1eSNeel Natu 	}
431c487da1eSNeel Natu }
432c487da1eSNeel Natu 
433c487da1eSNeel Natu static void
434ad43dd69SMark Johnston cb_setmsr(void *arg __unused, int r, uint64_t v)
435c487da1eSNeel Natu {
436c487da1eSNeel Natu 	int error;
437c487da1eSNeel Natu 	enum vm_reg_name vmreg;
438c487da1eSNeel Natu 
439c487da1eSNeel Natu 	vmreg = VM_REG_LAST;
440c487da1eSNeel Natu 
441c487da1eSNeel Natu 	switch (r) {
442c487da1eSNeel Natu 	case MSR_EFER:
443c487da1eSNeel Natu 		vmreg = VM_REG_GUEST_EFER;
444c487da1eSNeel Natu 		break;
445c487da1eSNeel Natu 	default:
446c487da1eSNeel Natu 		break;
447c487da1eSNeel Natu 	}
448c487da1eSNeel Natu 
449c487da1eSNeel Natu 	if (vmreg == VM_REG_LAST) {
450c487da1eSNeel Natu 		printf("test_setmsr(%d): not implemented\n", r);
451c487da1eSNeel Natu 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
452c487da1eSNeel Natu 	}
453c487da1eSNeel Natu 
4547d9ef309SJohn Baldwin 	error = vm_set_register(vcpu, vmreg, v);
455c487da1eSNeel Natu 	if (error) {
456c487da1eSNeel Natu 		perror("vm_set_msr");
457c487da1eSNeel Natu 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
458c487da1eSNeel Natu 	}
459c487da1eSNeel Natu }
460c487da1eSNeel Natu 
461c487da1eSNeel Natu static void
462ad43dd69SMark Johnston cb_setcr(void *arg __unused, int r, uint64_t v)
463c487da1eSNeel Natu {
464c487da1eSNeel Natu 	int error;
465c487da1eSNeel Natu 	enum vm_reg_name vmreg;
466c487da1eSNeel Natu 
467c487da1eSNeel Natu 	vmreg = VM_REG_LAST;
468c487da1eSNeel Natu 
469c487da1eSNeel Natu 	switch (r) {
470c487da1eSNeel Natu 	case 0:
471c487da1eSNeel Natu 		vmreg = VM_REG_GUEST_CR0;
472c487da1eSNeel Natu 		break;
473c487da1eSNeel Natu 	case 3:
474c487da1eSNeel Natu 		vmreg = VM_REG_GUEST_CR3;
475c487da1eSNeel Natu 		cr3 = v;
476c487da1eSNeel Natu 		break;
477c487da1eSNeel Natu 	case 4:
478c487da1eSNeel Natu 		vmreg = VM_REG_GUEST_CR4;
479c487da1eSNeel Natu 		break;
480c487da1eSNeel Natu 	default:
481c487da1eSNeel Natu 		break;
482c487da1eSNeel Natu 	}
483c487da1eSNeel Natu 
484c487da1eSNeel Natu 	if (vmreg == VM_REG_LAST) {
485c487da1eSNeel Natu 		printf("test_setcr(%d): not implemented\n", r);
486c487da1eSNeel Natu 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
487c487da1eSNeel Natu 	}
488c487da1eSNeel Natu 
4897d9ef309SJohn Baldwin 	error = vm_set_register(vcpu, vmreg, v);
490c487da1eSNeel Natu 	if (error) {
491c487da1eSNeel Natu 		perror("vm_set_cr");
492c487da1eSNeel Natu 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
493c487da1eSNeel Natu 	}
494c487da1eSNeel Natu }
495c487da1eSNeel Natu 
496c487da1eSNeel Natu static void
497ad43dd69SMark Johnston cb_setgdt(void *arg __unused, uint64_t base, size_t size)
498c487da1eSNeel Natu {
499c487da1eSNeel Natu 	int error;
500c487da1eSNeel Natu 
5017d9ef309SJohn Baldwin 	error = vm_set_desc(vcpu, VM_REG_GUEST_GDTR, base, size - 1, 0);
502c487da1eSNeel Natu 	if (error != 0) {
503c487da1eSNeel Natu 		perror("vm_set_desc(gdt)");
504c487da1eSNeel Natu 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
505c487da1eSNeel Natu 	}
506c487da1eSNeel Natu 
507c487da1eSNeel Natu 	gdtbase = base;
508c487da1eSNeel Natu }
509c487da1eSNeel Natu 
510c487da1eSNeel Natu static void
511ad43dd69SMark Johnston cb_exec(void *arg __unused, uint64_t rip)
512c487da1eSNeel Natu {
513c487da1eSNeel Natu 	int error;
514c487da1eSNeel Natu 
51500f3efe1SJohn Baldwin 	if (cr3 == 0)
5167d9ef309SJohn Baldwin 		error = vm_setup_freebsd_registers_i386(vcpu, rip, gdtbase,
51700f3efe1SJohn Baldwin 		    rsp);
51800f3efe1SJohn Baldwin 	else
5197d9ef309SJohn Baldwin 		error = vm_setup_freebsd_registers(vcpu, rip, cr3, gdtbase,
52000f3efe1SJohn Baldwin 		    rsp);
521c487da1eSNeel Natu 	if (error) {
522c487da1eSNeel Natu 		perror("vm_setup_freebsd_registers");
523c487da1eSNeel Natu 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
524c487da1eSNeel Natu 	}
525c487da1eSNeel Natu 
526c487da1eSNeel Natu 	cb_exit(NULL, 0);
527c487da1eSNeel Natu }
528c487da1eSNeel Natu 
529c487da1eSNeel Natu /*
530c487da1eSNeel Natu  * Misc
531c487da1eSNeel Natu  */
532c487da1eSNeel Natu 
533c487da1eSNeel Natu static void
534ad43dd69SMark Johnston cb_delay(void *arg __unused, int usec)
535c487da1eSNeel Natu {
536c487da1eSNeel Natu 
537c487da1eSNeel Natu 	usleep(usec);
538c487da1eSNeel Natu }
539c487da1eSNeel Natu 
540c487da1eSNeel Natu static void
541ad43dd69SMark Johnston cb_exit(void *arg __unused, int v)
542c487da1eSNeel Natu {
543c487da1eSNeel Natu 
5446380102cSPeter Grehan 	tcsetattr(consout_fd, TCSAFLUSH, &oldterm);
545c487da1eSNeel Natu 	exit(v);
546c487da1eSNeel Natu }
547c487da1eSNeel Natu 
548c487da1eSNeel Natu static void
549ad43dd69SMark Johnston cb_getmem(void *arg __unused, uint64_t *ret_lowmem, uint64_t *ret_highmem)
550c487da1eSNeel Natu {
551c487da1eSNeel Natu 
552be679db4SNeel Natu 	*ret_lowmem = vm_get_lowmem_size(ctx);
553be679db4SNeel Natu 	*ret_highmem = vm_get_highmem_size(ctx);
554c487da1eSNeel Natu }
555c487da1eSNeel Natu 
556b6afa84bSNeel Natu struct env {
557cb37fc82SWarner Losh 	char *str;	/* name=value */
558b6afa84bSNeel Natu 	SLIST_ENTRY(env) next;
559b6afa84bSNeel Natu };
560b6afa84bSNeel Natu 
561b6afa84bSNeel Natu static SLIST_HEAD(envhead, env) envhead;
562b6afa84bSNeel Natu 
563b6afa84bSNeel Natu static void
564ad43dd69SMark Johnston addenv(const char *str)
565b6afa84bSNeel Natu {
566b6afa84bSNeel Natu 	struct env *env;
567b6afa84bSNeel Natu 
568b6afa84bSNeel Natu 	env = malloc(sizeof(struct env));
569ad43dd69SMark Johnston 	if (env == NULL)
570ad43dd69SMark Johnston 		err(EX_OSERR, "malloc");
571ad43dd69SMark Johnston 	env->str = strdup(str);
572ad43dd69SMark Johnston 	if (env->str == NULL)
573ad43dd69SMark Johnston 		err(EX_OSERR, "strdup");
574b6afa84bSNeel Natu 	SLIST_INSERT_HEAD(&envhead, env, next);
575b6afa84bSNeel Natu }
576b6afa84bSNeel Natu 
577e8e6a5f9SWarner Losh static char *
578ad43dd69SMark Johnston cb_getenv(void *arg __unused, int num)
579c3e9ce33SNeel Natu {
580b6afa84bSNeel Natu 	int i;
581b6afa84bSNeel Natu 	struct env *env;
582c3e9ce33SNeel Natu 
583b6afa84bSNeel Natu 	i = 0;
584b6afa84bSNeel Natu 	SLIST_FOREACH(env, &envhead, next) {
585b6afa84bSNeel Natu 		if (i == num)
586b6afa84bSNeel Natu 			return (env->str);
587b6afa84bSNeel Natu 		i++;
588b6afa84bSNeel Natu 	}
589c3e9ce33SNeel Natu 
590c3e9ce33SNeel Natu 	return (NULL);
591c3e9ce33SNeel Natu }
592c3e9ce33SNeel Natu 
59348f337b8SMarcel Moolenaar static int
5947d9ef309SJohn Baldwin cb_vm_set_register(void *arg __unused, int vcpuid, int reg, uint64_t val)
59548f337b8SMarcel Moolenaar {
59648f337b8SMarcel Moolenaar 
5977d9ef309SJohn Baldwin 	assert(vcpuid == BSP);
5987d9ef309SJohn Baldwin 	return (vm_set_register(vcpu, reg, val));
59948f337b8SMarcel Moolenaar }
60048f337b8SMarcel Moolenaar 
60148f337b8SMarcel Moolenaar static int
6027d9ef309SJohn Baldwin cb_vm_set_desc(void *arg __unused, int vcpuid, int reg, uint64_t base,
603ad43dd69SMark Johnston     u_int limit, u_int access)
60448f337b8SMarcel Moolenaar {
60548f337b8SMarcel Moolenaar 
6067d9ef309SJohn Baldwin 	assert(vcpuid == BSP);
6077d9ef309SJohn Baldwin 	return (vm_set_desc(vcpu, reg, base, limit, access));
60848f337b8SMarcel Moolenaar }
60948f337b8SMarcel Moolenaar 
610d3d381b2SKyle Evans static void
611ad43dd69SMark Johnston cb_swap_interpreter(void *arg __unused, const char *interp_req)
612d3d381b2SKyle Evans {
613d3d381b2SKyle Evans 
614d3d381b2SKyle Evans 	/*
615d3d381b2SKyle Evans 	 * If the user specified a loader but we detected a mismatch, we should
616d3d381b2SKyle Evans 	 * not try to pivot to a different loader on them.
617d3d381b2SKyle Evans 	 */
618d3d381b2SKyle Evans 	free(loader);
619*bf7c4fcbSKyle Evans 	if (explicit_loader_fd != -1) {
620d3d381b2SKyle Evans 		perror("requested loader interpreter does not match guest userboot");
621d3d381b2SKyle Evans 		cb_exit(NULL, 1);
622d3d381b2SKyle Evans 	}
623d3d381b2SKyle Evans 	if (interp_req == NULL || *interp_req == '\0') {
624d3d381b2SKyle Evans 		perror("guest failed to request an interpreter");
625d3d381b2SKyle Evans 		cb_exit(NULL, 1);
626d3d381b2SKyle Evans 	}
627d3d381b2SKyle Evans 
628*bf7c4fcbSKyle Evans 	if (asprintf(&loader, "userboot_%s.so", interp_req) == -1)
629d3d381b2SKyle Evans 		err(EX_OSERR, "malloc");
630d3d381b2SKyle Evans 	longjmp(jb, 1);
631d3d381b2SKyle Evans }
632d3d381b2SKyle Evans 
633a10c6f55SNeel Natu static struct loader_callbacks cb = {
634c487da1eSNeel Natu 	.getc = cb_getc,
635c487da1eSNeel Natu 	.putc = cb_putc,
636c487da1eSNeel Natu 	.poll = cb_poll,
637c487da1eSNeel Natu 
638c487da1eSNeel Natu 	.open = cb_open,
639c487da1eSNeel Natu 	.close = cb_close,
640c487da1eSNeel Natu 	.isdir = cb_isdir,
641c487da1eSNeel Natu 	.read = cb_read,
642c487da1eSNeel Natu 	.readdir = cb_readdir,
643c487da1eSNeel Natu 	.seek = cb_seek,
644c487da1eSNeel Natu 	.stat = cb_stat,
645c487da1eSNeel Natu 
646c487da1eSNeel Natu 	.diskread = cb_diskread,
647cc71ff72SConrad Meyer 	.diskwrite = cb_diskwrite,
648a10c6f55SNeel Natu 	.diskioctl = cb_diskioctl,
649c487da1eSNeel Natu 
650c487da1eSNeel Natu 	.copyin = cb_copyin,
651c487da1eSNeel Natu 	.copyout = cb_copyout,
652c487da1eSNeel Natu 	.setreg = cb_setreg,
653c487da1eSNeel Natu 	.setmsr = cb_setmsr,
654c487da1eSNeel Natu 	.setcr = cb_setcr,
655c487da1eSNeel Natu 	.setgdt = cb_setgdt,
656c487da1eSNeel Natu 	.exec = cb_exec,
657c487da1eSNeel Natu 
658c487da1eSNeel Natu 	.delay = cb_delay,
659c487da1eSNeel Natu 	.exit = cb_exit,
660c487da1eSNeel Natu 	.getmem = cb_getmem,
661c3e9ce33SNeel Natu 
662c3e9ce33SNeel Natu 	.getenv = cb_getenv,
66348f337b8SMarcel Moolenaar 
66448f337b8SMarcel Moolenaar 	/* Version 4 additions */
66548f337b8SMarcel Moolenaar 	.vm_set_register = cb_vm_set_register,
66648f337b8SMarcel Moolenaar 	.vm_set_desc = cb_vm_set_desc,
667d3d381b2SKyle Evans 
668d3d381b2SKyle Evans 	/* Version 5 additions */
669d3d381b2SKyle Evans 	.swap_interpreter = cb_swap_interpreter,
670c487da1eSNeel Natu };
671c487da1eSNeel Natu 
6726380102cSPeter Grehan static int
6736380102cSPeter Grehan altcons_open(char *path)
6746380102cSPeter Grehan {
6756380102cSPeter Grehan 	struct stat sb;
6766380102cSPeter Grehan 	int err;
6776380102cSPeter Grehan 	int fd;
6786380102cSPeter Grehan 
6796380102cSPeter Grehan 	/*
6806380102cSPeter Grehan 	 * Allow stdio to be passed in so that the same string
6816380102cSPeter Grehan 	 * can be used for the bhyveload console and bhyve com-port
6826380102cSPeter Grehan 	 * parameters
6836380102cSPeter Grehan 	 */
6846380102cSPeter Grehan 	if (!strcmp(path, "stdio"))
6856380102cSPeter Grehan 		return (0);
6866380102cSPeter Grehan 
6876380102cSPeter Grehan 	err = stat(path, &sb);
6886380102cSPeter Grehan 	if (err == 0) {
6896380102cSPeter Grehan 		if (!S_ISCHR(sb.st_mode))
6906380102cSPeter Grehan 			err = ENOTSUP;
6916380102cSPeter Grehan 		else {
6926380102cSPeter Grehan 			fd = open(path, O_RDWR | O_NONBLOCK);
6936380102cSPeter Grehan 			if (fd < 0)
6946380102cSPeter Grehan 				err = errno;
6956380102cSPeter Grehan 			else
6966380102cSPeter Grehan 				consin_fd = consout_fd = fd;
6976380102cSPeter Grehan 		}
6986380102cSPeter Grehan 	}
6996380102cSPeter Grehan 
7006380102cSPeter Grehan 	return (err);
7016380102cSPeter Grehan }
7026380102cSPeter Grehan 
703cf087c12SPeter Grehan static int
704cf087c12SPeter Grehan disk_open(char *path)
705cf087c12SPeter Grehan {
706a0bc451fSSean Chittenden 	int fd;
707cf087c12SPeter Grehan 
7080db293c1SAllan Jude 	if (ndisks >= NDISKS)
709cf087c12SPeter Grehan 		return (ERANGE);
710cf087c12SPeter Grehan 
7115a023bd2SRobert Wing 	fd = open(path, O_RDWR);
712a0bc451fSSean Chittenden 	if (fd < 0)
713a0bc451fSSean Chittenden 		return (errno);
714cf087c12SPeter Grehan 
715cf087c12SPeter Grehan 	disk_fd[ndisks] = fd;
716cf087c12SPeter Grehan 	ndisks++;
717cf087c12SPeter Grehan 
718a0bc451fSSean Chittenden 	return (0);
719cf087c12SPeter Grehan }
720cf087c12SPeter Grehan 
721c487da1eSNeel Natu static void
722c487da1eSNeel Natu usage(void)
723c487da1eSNeel Natu {
724c487da1eSNeel Natu 
725b060ba50SNeel Natu 	fprintf(stderr,
7269b1aa8d6SNeel Natu 	    "usage: %s [-S][-c <console-device>] [-d <disk-path>] [-e <name=value>]\n"
7276ee52c65SRoman Bogorodskiy 	    "       %*s [-h <host-path>] [-m memsize[K|k|M|m|G|g|T|t]] <vmname>\n",
7286380102cSPeter Grehan 	    progname,
729b5331f4dSNeel Natu 	    (int)strlen(progname), "");
730c487da1eSNeel Natu 	exit(1);
731c487da1eSNeel Natu }
732c487da1eSNeel Natu 
7336779d44bSKyle Evans static void
7346779d44bSKyle Evans hostbase_open(const char *base)
7356779d44bSKyle Evans {
7366779d44bSKyle Evans 
7376779d44bSKyle Evans 	if (hostbase_fd != -1)
7386779d44bSKyle Evans 		close(hostbase_fd);
7396779d44bSKyle Evans 	hostbase_fd = open(base, O_DIRECTORY | O_PATH);
7406779d44bSKyle Evans 	if (hostbase_fd == -1)
7416779d44bSKyle Evans 		err(EX_OSERR, "open");
7426779d44bSKyle Evans }
7436779d44bSKyle Evans 
744*bf7c4fcbSKyle Evans static void
745*bf7c4fcbSKyle Evans loader_open(int bootfd)
746*bf7c4fcbSKyle Evans {
747*bf7c4fcbSKyle Evans 	int fd;
748*bf7c4fcbSKyle Evans 
749*bf7c4fcbSKyle Evans 	if (loader == NULL) {
750*bf7c4fcbSKyle Evans 		loader = strdup("userboot.so");
751*bf7c4fcbSKyle Evans 		if (loader == NULL)
752*bf7c4fcbSKyle Evans 			err(EX_OSERR, "malloc");
753*bf7c4fcbSKyle Evans 	}
754*bf7c4fcbSKyle Evans 
755*bf7c4fcbSKyle Evans 	assert(bootfd >= 0 || explicit_loader_fd >= 0);
756*bf7c4fcbSKyle Evans 	if (explicit_loader_fd >= 0)
757*bf7c4fcbSKyle Evans 		fd = explicit_loader_fd;
758*bf7c4fcbSKyle Evans 	else
759*bf7c4fcbSKyle Evans 		fd = openat(bootfd, loader, O_RDONLY | O_RESOLVE_BENEATH);
760*bf7c4fcbSKyle Evans 	if (fd == -1)
761*bf7c4fcbSKyle Evans 		err(EX_OSERR, "openat");
762*bf7c4fcbSKyle Evans 
763*bf7c4fcbSKyle Evans 	loader_hdl = fdlopen(fd, RTLD_LOCAL);
764*bf7c4fcbSKyle Evans 	if (!loader_hdl)
765*bf7c4fcbSKyle Evans 		errx(EX_OSERR, "dlopen: %s", dlerror());
766*bf7c4fcbSKyle Evans }
767*bf7c4fcbSKyle Evans 
768c487da1eSNeel Natu int
769c487da1eSNeel Natu main(int argc, char** argv)
770c487da1eSNeel Natu {
771a10c6f55SNeel Natu 	void (*func)(struct loader_callbacks *, void *, int, int);
772b060ba50SNeel Natu 	uint64_t mem_size;
773*bf7c4fcbSKyle Evans 	int bootfd, opt, error, memflags, need_reinit;
774c487da1eSNeel Natu 
775*bf7c4fcbSKyle Evans 	bootfd = -1;
776b5331f4dSNeel Natu 	progname = basename(argv[0]);
777c487da1eSNeel Natu 
7789b1aa8d6SNeel Natu 	memflags = 0;
779b060ba50SNeel Natu 	mem_size = 256 * MB;
780c487da1eSNeel Natu 
7816380102cSPeter Grehan 	consin_fd = STDIN_FILENO;
7826380102cSPeter Grehan 	consout_fd = STDOUT_FILENO;
7836380102cSPeter Grehan 
784568e3a8dSMarcel Moolenaar 	while ((opt = getopt(argc, argv, "CSc:d:e:h:l:m:")) != -1) {
785c487da1eSNeel Natu 		switch (opt) {
7866380102cSPeter Grehan 		case 'c':
7876380102cSPeter Grehan 			error = altcons_open(optarg);
7886380102cSPeter Grehan 			if (error != 0)
7896380102cSPeter Grehan 				errx(EX_USAGE, "Could not open '%s'", optarg);
7906380102cSPeter Grehan 			break;
791cf087c12SPeter Grehan 
792c487da1eSNeel Natu 		case 'd':
793cf087c12SPeter Grehan 			error = disk_open(optarg);
794cf087c12SPeter Grehan 			if (error != 0)
795cf087c12SPeter Grehan 				errx(EX_USAGE, "Could not open '%s'", optarg);
796c487da1eSNeel Natu 			break;
797c487da1eSNeel Natu 
798b6afa84bSNeel Natu 		case 'e':
799b6afa84bSNeel Natu 			addenv(optarg);
800b6afa84bSNeel Natu 			break;
801b6afa84bSNeel Natu 
802c487da1eSNeel Natu 		case 'h':
8036779d44bSKyle Evans 			hostbase_open(optarg);
804c487da1eSNeel Natu 			break;
805c487da1eSNeel Natu 
8068c96dcc1SMarcel Moolenaar 		case 'l':
8078c96dcc1SMarcel Moolenaar 			if (loader != NULL)
8088c96dcc1SMarcel Moolenaar 				errx(EX_USAGE, "-l can only be given once");
8098c96dcc1SMarcel Moolenaar 			loader = strdup(optarg);
8108c96dcc1SMarcel Moolenaar 			if (loader == NULL)
8118c96dcc1SMarcel Moolenaar 				err(EX_OSERR, "malloc");
812*bf7c4fcbSKyle Evans 			explicit_loader_fd = open(loader, O_RDONLY);
813*bf7c4fcbSKyle Evans 			if (explicit_loader_fd == -1)
814*bf7c4fcbSKyle Evans 				err(EX_OSERR, "%s", loader);
8158c96dcc1SMarcel Moolenaar 			break;
8168c96dcc1SMarcel Moolenaar 
817c487da1eSNeel Natu 		case 'm':
818200758f1SNeel Natu 			error = vm_parse_memsize(optarg, &mem_size);
819200758f1SNeel Natu 			if (error != 0)
820200758f1SNeel Natu 				errx(EX_USAGE, "Invalid memsize '%s'", optarg);
821c487da1eSNeel Natu 			break;
822568e3a8dSMarcel Moolenaar 		case 'C':
823568e3a8dSMarcel Moolenaar 			memflags |= VM_MEM_F_INCORE;
824568e3a8dSMarcel Moolenaar 			break;
8259b1aa8d6SNeel Natu 		case 'S':
8269b1aa8d6SNeel Natu 			memflags |= VM_MEM_F_WIRED;
8279b1aa8d6SNeel Natu 			break;
828c487da1eSNeel Natu 		case '?':
829c487da1eSNeel Natu 			usage();
830c487da1eSNeel Natu 		}
831c487da1eSNeel Natu 	}
832c487da1eSNeel Natu 
833c487da1eSNeel Natu 	argc -= optind;
834c487da1eSNeel Natu 	argv += optind;
835c487da1eSNeel Natu 
836c487da1eSNeel Natu 	if (argc != 1)
837c487da1eSNeel Natu 		usage();
838c487da1eSNeel Natu 
839c487da1eSNeel Natu 	vmname = argv[0];
840c487da1eSNeel Natu 
8415fcf252fSNeel Natu 	need_reinit = 0;
842c487da1eSNeel Natu 	error = vm_create(vmname);
8435fcf252fSNeel Natu 	if (error) {
8445fcf252fSNeel Natu 		if (errno != EEXIST) {
845c487da1eSNeel Natu 			perror("vm_create");
846c487da1eSNeel Natu 			exit(1);
8475fcf252fSNeel Natu 		}
8485fcf252fSNeel Natu 		need_reinit = 1;
849c487da1eSNeel Natu 	}
850c487da1eSNeel Natu 
851c487da1eSNeel Natu 	ctx = vm_open(vmname);
852c487da1eSNeel Natu 	if (ctx == NULL) {
853c487da1eSNeel Natu 		perror("vm_open");
854c487da1eSNeel Natu 		exit(1);
855c487da1eSNeel Natu 	}
856c487da1eSNeel Natu 
857*bf7c4fcbSKyle Evans 	/*
858*bf7c4fcbSKyle Evans 	 * If we weren't given an explicit loader to use, we need to support the
859*bf7c4fcbSKyle Evans 	 * guest requesting a different one.
860*bf7c4fcbSKyle Evans 	 */
861*bf7c4fcbSKyle Evans 	if (explicit_loader_fd == -1) {
862*bf7c4fcbSKyle Evans 		bootfd = open("/boot", O_DIRECTORY | O_PATH);
863*bf7c4fcbSKyle Evans 		if (bootfd == -1) {
864*bf7c4fcbSKyle Evans 			perror("open");
865*bf7c4fcbSKyle Evans 			exit(1);
866*bf7c4fcbSKyle Evans 		}
867*bf7c4fcbSKyle Evans 	}
868*bf7c4fcbSKyle Evans 
8697d9ef309SJohn Baldwin 	vcpu = vm_vcpu_open(ctx, BSP);
8707d9ef309SJohn Baldwin 
871d3d381b2SKyle Evans 	/*
872d3d381b2SKyle Evans 	 * setjmp in the case the guest wants to swap out interpreter,
873d3d381b2SKyle Evans 	 * cb_swap_interpreter will swap out loader as appropriate and set
874d3d381b2SKyle Evans 	 * need_reinit so that we end up in a clean state once again.
875d3d381b2SKyle Evans 	 */
876*bf7c4fcbSKyle Evans 	if (setjmp(jb) != 0) {
877*bf7c4fcbSKyle Evans 		dlclose(loader_hdl);
878*bf7c4fcbSKyle Evans 		loader_hdl = NULL;
879*bf7c4fcbSKyle Evans 
880*bf7c4fcbSKyle Evans 		need_reinit = 1;
881*bf7c4fcbSKyle Evans 	}
882d3d381b2SKyle Evans 
8835fcf252fSNeel Natu 	if (need_reinit) {
8845fcf252fSNeel Natu 		error = vm_reinit(ctx);
8855fcf252fSNeel Natu 		if (error) {
8865fcf252fSNeel Natu 			perror("vm_reinit");
8875fcf252fSNeel Natu 			exit(1);
8885fcf252fSNeel Natu 		}
8895fcf252fSNeel Natu 	}
8905fcf252fSNeel Natu 
8919b1aa8d6SNeel Natu 	vm_set_memflags(ctx, memflags);
892b060ba50SNeel Natu 	error = vm_setup_memory(ctx, mem_size, VM_MMAP_ALL);
893c487da1eSNeel Natu 	if (error) {
894b060ba50SNeel Natu 		perror("vm_setup_memory");
895c487da1eSNeel Natu 		exit(1);
896c487da1eSNeel Natu 	}
897c487da1eSNeel Natu 
898*bf7c4fcbSKyle Evans 	loader_open(bootfd);
899d3d381b2SKyle Evans 	func = dlsym(loader_hdl, "loader_main");
9008c96dcc1SMarcel Moolenaar 	if (!func) {
9018c96dcc1SMarcel Moolenaar 		printf("%s\n", dlerror());
9028c96dcc1SMarcel Moolenaar 		free(loader);
9038c96dcc1SMarcel Moolenaar 		return (1);
9048c96dcc1SMarcel Moolenaar 	}
9058c96dcc1SMarcel Moolenaar 
9066380102cSPeter Grehan 	tcgetattr(consout_fd, &term);
907c487da1eSNeel Natu 	oldterm = term;
9086380102cSPeter Grehan 	cfmakeraw(&term);
9096380102cSPeter Grehan 	term.c_cflag |= CLOCAL;
9106380102cSPeter Grehan 
9116380102cSPeter Grehan 	tcsetattr(consout_fd, TCSAFLUSH, &term);
9126380102cSPeter Grehan 
913b6afa84bSNeel Natu 	addenv("smbios.bios.vendor=BHYVE");
914b6afa84bSNeel Natu 	addenv("boot_serial=1");
915b6afa84bSNeel Natu 
916d3d381b2SKyle Evans 	func(&cb, NULL, USERBOOT_VERSION_5, ndisks);
9178c96dcc1SMarcel Moolenaar 
9188c96dcc1SMarcel Moolenaar 	free(loader);
9198c96dcc1SMarcel Moolenaar 	return (0);
920c487da1eSNeel Natu }
921