xref: /freebsd-src/usr.sbin/bhyveload/bhyveload.c (revision cc71ff723468de99c72d455ebc970c2354b4d459)
1c487da1eSNeel Natu /*-
21de7b4b8SPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD AND 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  * $FreeBSD$
29c487da1eSNeel Natu  */
30c487da1eSNeel Natu 
31c487da1eSNeel Natu /*-
32c487da1eSNeel Natu  * Copyright (c) 2011 Google, Inc.
33c487da1eSNeel Natu  * All rights reserved.
34c487da1eSNeel Natu  *
35c487da1eSNeel Natu  * Redistribution and use in source and binary forms, with or without
36c487da1eSNeel Natu  * modification, are permitted provided that the following conditions
37c487da1eSNeel Natu  * are met:
38c487da1eSNeel Natu  * 1. Redistributions of source code must retain the above copyright
39c487da1eSNeel Natu  *    notice, this list of conditions and the following disclaimer.
40c487da1eSNeel Natu  * 2. Redistributions in binary form must reproduce the above copyright
41c487da1eSNeel Natu  *    notice, this list of conditions and the following disclaimer in the
42c487da1eSNeel Natu  *    documentation and/or other materials provided with the distribution.
43c487da1eSNeel Natu  *
44c487da1eSNeel Natu  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
45c487da1eSNeel Natu  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
46c487da1eSNeel Natu  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
47c487da1eSNeel Natu  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
48c487da1eSNeel Natu  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
49c487da1eSNeel Natu  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
50c487da1eSNeel Natu  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
51c487da1eSNeel Natu  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
52c487da1eSNeel Natu  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
53c487da1eSNeel Natu  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
54c487da1eSNeel Natu  * SUCH DAMAGE.
55c487da1eSNeel Natu  *
56c487da1eSNeel Natu  * $FreeBSD$
57c487da1eSNeel Natu  */
58c487da1eSNeel Natu 
59c487da1eSNeel Natu #include <sys/cdefs.h>
60c487da1eSNeel Natu __FBSDID("$FreeBSD$");
61c487da1eSNeel Natu 
62c487da1eSNeel Natu #include <sys/ioctl.h>
63c487da1eSNeel Natu #include <sys/stat.h>
64a10c6f55SNeel Natu #include <sys/disk.h>
65b6afa84bSNeel Natu #include <sys/queue.h>
66c487da1eSNeel Natu 
67c487da1eSNeel Natu #include <machine/specialreg.h>
68c487da1eSNeel Natu #include <machine/vmm.h>
69c487da1eSNeel Natu 
70c487da1eSNeel Natu #include <dirent.h>
71c487da1eSNeel Natu #include <dlfcn.h>
72c487da1eSNeel Natu #include <errno.h>
73200758f1SNeel Natu #include <err.h>
74c487da1eSNeel Natu #include <fcntl.h>
75c487da1eSNeel Natu #include <getopt.h>
76b5331f4dSNeel Natu #include <libgen.h>
77c487da1eSNeel Natu #include <limits.h>
78d3d381b2SKyle Evans #include <setjmp.h>
79c487da1eSNeel Natu #include <stdio.h>
80c487da1eSNeel Natu #include <stdlib.h>
81c487da1eSNeel Natu #include <string.h>
82200758f1SNeel Natu #include <sysexits.h>
83c487da1eSNeel Natu #include <termios.h>
84c487da1eSNeel Natu #include <unistd.h>
85c487da1eSNeel Natu 
86c487da1eSNeel Natu #include <vmmapi.h>
87c487da1eSNeel Natu 
88c487da1eSNeel Natu #include "userboot.h"
89c487da1eSNeel Natu 
90c487da1eSNeel Natu #define	MB	(1024 * 1024UL)
91c487da1eSNeel Natu #define	GB	(1024 * 1024 * 1024UL)
92c487da1eSNeel Natu #define	BSP	0
93c487da1eSNeel Natu 
94cf087c12SPeter Grehan #define	NDISKS	32
95cf087c12SPeter Grehan 
96384305ffSPeter Grehan static char *host_base;
97c487da1eSNeel Natu static struct termios term, oldterm;
98cf087c12SPeter Grehan static int disk_fd[NDISKS];
99cf087c12SPeter Grehan static int ndisks;
1006380102cSPeter Grehan static int consin_fd, consout_fd;
101c487da1eSNeel Natu 
102d3d381b2SKyle Evans static int need_reinit;
103d3d381b2SKyle Evans 
104d3d381b2SKyle Evans static void *loader_hdl;
105d3d381b2SKyle Evans static char *loader;
106d3d381b2SKyle Evans static int explicit_loader;
107d3d381b2SKyle Evans static jmp_buf jb;
108d3d381b2SKyle Evans 
109b060ba50SNeel Natu static char *vmname, *progname;
110c487da1eSNeel Natu static struct vmctx *ctx;
111c487da1eSNeel Natu 
112c487da1eSNeel Natu static uint64_t gdtbase, cr3, rsp;
113c487da1eSNeel Natu 
114c487da1eSNeel Natu static void cb_exit(void *arg, int v);
115c487da1eSNeel Natu 
116c487da1eSNeel Natu /*
117c487da1eSNeel Natu  * Console i/o callbacks
118c487da1eSNeel Natu  */
119c487da1eSNeel Natu 
120c487da1eSNeel Natu static void
121c487da1eSNeel Natu cb_putc(void *arg, int ch)
122c487da1eSNeel Natu {
123c487da1eSNeel Natu 	char c = ch;
124c487da1eSNeel Natu 
1256380102cSPeter Grehan 	(void) write(consout_fd, &c, 1);
126c487da1eSNeel Natu }
127c487da1eSNeel Natu 
128c487da1eSNeel Natu static int
129c487da1eSNeel Natu cb_getc(void *arg)
130c487da1eSNeel Natu {
131c487da1eSNeel Natu 	char c;
132c487da1eSNeel Natu 
1336380102cSPeter Grehan 	if (read(consin_fd, &c, 1) == 1)
134c487da1eSNeel Natu 		return (c);
135c487da1eSNeel Natu 	return (-1);
136c487da1eSNeel Natu }
137c487da1eSNeel Natu 
138c487da1eSNeel Natu static int
139c487da1eSNeel Natu cb_poll(void *arg)
140c487da1eSNeel Natu {
141c487da1eSNeel Natu 	int n;
142c487da1eSNeel Natu 
1436380102cSPeter Grehan 	if (ioctl(consin_fd, FIONREAD, &n) >= 0)
144c487da1eSNeel Natu 		return (n > 0);
145c487da1eSNeel Natu 	return (0);
146c487da1eSNeel Natu }
147c487da1eSNeel Natu 
148c487da1eSNeel Natu /*
149c487da1eSNeel Natu  * Host filesystem i/o callbacks
150c487da1eSNeel Natu  */
151c487da1eSNeel Natu 
152c487da1eSNeel Natu struct cb_file {
153c487da1eSNeel Natu 	int cf_isdir;
154c487da1eSNeel Natu 	size_t cf_size;
155c487da1eSNeel Natu 	struct stat cf_stat;
156c487da1eSNeel Natu 	union {
157c487da1eSNeel Natu 		int fd;
158c487da1eSNeel Natu 		DIR *dir;
159c487da1eSNeel Natu 	} cf_u;
160c487da1eSNeel Natu };
161c487da1eSNeel Natu 
162c487da1eSNeel Natu static int
163c487da1eSNeel Natu cb_open(void *arg, const char *filename, void **hp)
164c487da1eSNeel Natu {
165c487da1eSNeel Natu 	struct cb_file *cf;
166c487da1eSNeel Natu 	char path[PATH_MAX];
167c487da1eSNeel Natu 
168c487da1eSNeel Natu 	if (!host_base)
169c487da1eSNeel Natu 		return (ENOENT);
170c487da1eSNeel Natu 
171c487da1eSNeel Natu 	strlcpy(path, host_base, PATH_MAX);
172c487da1eSNeel Natu 	if (path[strlen(path) - 1] == '/')
173c487da1eSNeel Natu 		path[strlen(path) - 1] = 0;
174c487da1eSNeel Natu 	strlcat(path, filename, PATH_MAX);
175c487da1eSNeel Natu 	cf = malloc(sizeof(struct cb_file));
176c487da1eSNeel Natu 	if (stat(path, &cf->cf_stat) < 0) {
177c487da1eSNeel Natu 		free(cf);
178c487da1eSNeel Natu 		return (errno);
179c487da1eSNeel Natu 	}
180c487da1eSNeel Natu 
1818f61276dSPedro F. Giffuni 	cf->cf_size = cf->cf_stat.st_size;
182c487da1eSNeel Natu 	if (S_ISDIR(cf->cf_stat.st_mode)) {
183c487da1eSNeel Natu 		cf->cf_isdir = 1;
184c487da1eSNeel Natu 		cf->cf_u.dir = opendir(path);
185c487da1eSNeel Natu 		if (!cf->cf_u.dir)
186c487da1eSNeel Natu 			goto out;
187c487da1eSNeel Natu 		*hp = cf;
188c487da1eSNeel Natu 		return (0);
189c487da1eSNeel Natu 	}
190c487da1eSNeel Natu 	if (S_ISREG(cf->cf_stat.st_mode)) {
191c487da1eSNeel Natu 		cf->cf_isdir = 0;
192c487da1eSNeel Natu 		cf->cf_u.fd = open(path, O_RDONLY);
193c487da1eSNeel Natu 		if (cf->cf_u.fd < 0)
194c487da1eSNeel Natu 			goto out;
195c487da1eSNeel Natu 		*hp = cf;
196c487da1eSNeel Natu 		return (0);
197c487da1eSNeel Natu 	}
198c487da1eSNeel Natu 
199c487da1eSNeel Natu out:
200c487da1eSNeel Natu 	free(cf);
201c487da1eSNeel Natu 	return (EINVAL);
202c487da1eSNeel Natu }
203c487da1eSNeel Natu 
204c487da1eSNeel Natu static int
205c487da1eSNeel Natu cb_close(void *arg, void *h)
206c487da1eSNeel Natu {
207c487da1eSNeel Natu 	struct cb_file *cf = h;
208c487da1eSNeel Natu 
209c487da1eSNeel Natu 	if (cf->cf_isdir)
210c487da1eSNeel Natu 		closedir(cf->cf_u.dir);
211c487da1eSNeel Natu 	else
212c487da1eSNeel Natu 		close(cf->cf_u.fd);
213c487da1eSNeel Natu 	free(cf);
214c487da1eSNeel Natu 
215c487da1eSNeel Natu 	return (0);
216c487da1eSNeel Natu }
217c487da1eSNeel Natu 
218c487da1eSNeel Natu static int
219c487da1eSNeel Natu cb_isdir(void *arg, void *h)
220c487da1eSNeel Natu {
221c487da1eSNeel Natu 	struct cb_file *cf = h;
222c487da1eSNeel Natu 
223c487da1eSNeel Natu 	return (cf->cf_isdir);
224c487da1eSNeel Natu }
225c487da1eSNeel Natu 
226c487da1eSNeel Natu static int
227c487da1eSNeel Natu cb_read(void *arg, void *h, void *buf, size_t size, size_t *resid)
228c487da1eSNeel Natu {
229c487da1eSNeel Natu 	struct cb_file *cf = h;
230c487da1eSNeel Natu 	ssize_t sz;
231c487da1eSNeel Natu 
232c487da1eSNeel Natu 	if (cf->cf_isdir)
233c487da1eSNeel Natu 		return (EINVAL);
234c487da1eSNeel Natu 	sz = read(cf->cf_u.fd, buf, size);
235c487da1eSNeel Natu 	if (sz < 0)
236c487da1eSNeel Natu 		return (EINVAL);
237c487da1eSNeel Natu 	*resid = size - sz;
238c487da1eSNeel Natu 	return (0);
239c487da1eSNeel Natu }
240c487da1eSNeel Natu 
241c487da1eSNeel Natu static int
242c487da1eSNeel Natu cb_readdir(void *arg, void *h, uint32_t *fileno_return, uint8_t *type_return,
243c487da1eSNeel Natu 	   size_t *namelen_return, char *name)
244c487da1eSNeel Natu {
245c487da1eSNeel Natu 	struct cb_file *cf = h;
246c487da1eSNeel Natu 	struct dirent *dp;
247c487da1eSNeel Natu 
248c487da1eSNeel Natu 	if (!cf->cf_isdir)
249c487da1eSNeel Natu 		return (EINVAL);
250c487da1eSNeel Natu 
251c487da1eSNeel Natu 	dp = readdir(cf->cf_u.dir);
252c487da1eSNeel Natu 	if (!dp)
253c487da1eSNeel Natu 		return (ENOENT);
254c487da1eSNeel Natu 
255c487da1eSNeel Natu 	/*
256c487da1eSNeel Natu 	 * Note: d_namlen is in the range 0..255 and therefore less
257c487da1eSNeel Natu 	 * than PATH_MAX so we don't need to test before copying.
258c487da1eSNeel Natu 	 */
259c487da1eSNeel Natu 	*fileno_return = dp->d_fileno;
260c487da1eSNeel Natu 	*type_return = dp->d_type;
261c487da1eSNeel Natu 	*namelen_return = dp->d_namlen;
262c487da1eSNeel Natu 	memcpy(name, dp->d_name, dp->d_namlen);
263c487da1eSNeel Natu 	name[dp->d_namlen] = 0;
264c487da1eSNeel Natu 
265c487da1eSNeel Natu 	return (0);
266c487da1eSNeel Natu }
267c487da1eSNeel Natu 
268c487da1eSNeel Natu static int
269c487da1eSNeel Natu cb_seek(void *arg, void *h, uint64_t offset, int whence)
270c487da1eSNeel Natu {
271c487da1eSNeel Natu 	struct cb_file *cf = h;
272c487da1eSNeel Natu 
273c487da1eSNeel Natu 	if (cf->cf_isdir)
274c487da1eSNeel Natu 		return (EINVAL);
275c487da1eSNeel Natu 	if (lseek(cf->cf_u.fd, offset, whence) < 0)
276c487da1eSNeel Natu 		return (errno);
277c487da1eSNeel Natu 	return (0);
278c487da1eSNeel Natu }
279c487da1eSNeel Natu 
280c487da1eSNeel Natu static int
28153f151f9SSimon J. Gerraty cb_stat(void *arg, void *h, struct stat *sbp)
282c487da1eSNeel Natu {
283c487da1eSNeel Natu 	struct cb_file *cf = h;
284c487da1eSNeel Natu 
28553f151f9SSimon J. Gerraty 	memset(sbp, 0, sizeof(struct stat));
28653f151f9SSimon J. Gerraty 	sbp->st_mode = cf->cf_stat.st_mode;
28753f151f9SSimon J. Gerraty 	sbp->st_uid = cf->cf_stat.st_uid;
28853f151f9SSimon J. Gerraty 	sbp->st_gid = cf->cf_stat.st_gid;
28953f151f9SSimon J. Gerraty 	sbp->st_size = cf->cf_stat.st_size;
29053f151f9SSimon J. Gerraty 	sbp->st_mtime = cf->cf_stat.st_mtime;
29153f151f9SSimon J. Gerraty 	sbp->st_dev = cf->cf_stat.st_dev;
29253f151f9SSimon J. Gerraty 	sbp->st_ino = cf->cf_stat.st_ino;
29353f151f9SSimon J. Gerraty 
294c487da1eSNeel Natu 	return (0);
295c487da1eSNeel Natu }
296c487da1eSNeel Natu 
297c487da1eSNeel Natu /*
298c487da1eSNeel Natu  * Disk image i/o callbacks
299c487da1eSNeel Natu  */
300c487da1eSNeel Natu 
301c487da1eSNeel Natu static int
302c487da1eSNeel Natu cb_diskread(void *arg, int unit, uint64_t from, void *to, size_t size,
303c487da1eSNeel Natu     size_t *resid)
304c487da1eSNeel Natu {
305c487da1eSNeel Natu 	ssize_t n;
306c487da1eSNeel Natu 
307cf087c12SPeter Grehan 	if (unit < 0 || unit >= ndisks)
308c487da1eSNeel Natu 		return (EIO);
309cf087c12SPeter Grehan 	n = pread(disk_fd[unit], to, size, from);
310c487da1eSNeel Natu 	if (n < 0)
311c487da1eSNeel Natu 		return (errno);
312c487da1eSNeel Natu 	*resid = size - n;
313c487da1eSNeel Natu 	return (0);
314c487da1eSNeel Natu }
315c487da1eSNeel Natu 
316a10c6f55SNeel Natu static int
317*cc71ff72SConrad Meyer cb_diskwrite(void *arg, int unit, uint64_t offset, void *src, size_t size,
318*cc71ff72SConrad Meyer     size_t *resid)
319*cc71ff72SConrad Meyer {
320*cc71ff72SConrad Meyer 	ssize_t n;
321*cc71ff72SConrad Meyer 
322*cc71ff72SConrad Meyer 	if (unit < 0 || unit >= ndisks)
323*cc71ff72SConrad Meyer 		return (EIO);
324*cc71ff72SConrad Meyer 	n = pwrite(disk_fd[unit], src, size, offset);
325*cc71ff72SConrad Meyer 	if (n < 0)
326*cc71ff72SConrad Meyer 		return (errno);
327*cc71ff72SConrad Meyer 	*resid = size - n;
328*cc71ff72SConrad Meyer 	return (0);
329*cc71ff72SConrad Meyer }
330*cc71ff72SConrad Meyer 
331*cc71ff72SConrad Meyer static int
332a10c6f55SNeel Natu cb_diskioctl(void *arg, int unit, u_long cmd, void *data)
333a10c6f55SNeel Natu {
334a10c6f55SNeel Natu 	struct stat sb;
335a10c6f55SNeel Natu 
336cf087c12SPeter Grehan 	if (unit < 0 || unit >= ndisks)
337a10c6f55SNeel Natu 		return (EBADF);
338a10c6f55SNeel Natu 
339a10c6f55SNeel Natu 	switch (cmd) {
340a10c6f55SNeel Natu 	case DIOCGSECTORSIZE:
341a10c6f55SNeel Natu 		*(u_int *)data = 512;
342a10c6f55SNeel Natu 		break;
343a10c6f55SNeel Natu 	case DIOCGMEDIASIZE:
3446589ee29SAndriy Gapon 		if (fstat(disk_fd[unit], &sb) != 0)
345a10c6f55SNeel Natu 			return (ENOTTY);
3466589ee29SAndriy Gapon 		if (S_ISCHR(sb.st_mode) &&
3476589ee29SAndriy Gapon 		    ioctl(disk_fd[unit], DIOCGMEDIASIZE, &sb.st_size) != 0)
3486589ee29SAndriy Gapon 				return (ENOTTY);
3496589ee29SAndriy Gapon 		*(off_t *)data = sb.st_size;
350a10c6f55SNeel Natu 		break;
351a10c6f55SNeel Natu 	default:
352a10c6f55SNeel Natu 		return (ENOTTY);
353a10c6f55SNeel Natu 	}
354a10c6f55SNeel Natu 
355a10c6f55SNeel Natu 	return (0);
356a10c6f55SNeel Natu }
357a10c6f55SNeel Natu 
358c487da1eSNeel Natu /*
359c487da1eSNeel Natu  * Guest virtual machine i/o callbacks
360c487da1eSNeel Natu  */
361c487da1eSNeel Natu static int
362c487da1eSNeel Natu cb_copyin(void *arg, const void *from, uint64_t to, size_t size)
363c487da1eSNeel Natu {
364b060ba50SNeel Natu 	char *ptr;
365c487da1eSNeel Natu 
366c487da1eSNeel Natu 	to &= 0x7fffffff;
367b060ba50SNeel Natu 
368b060ba50SNeel Natu 	ptr = vm_map_gpa(ctx, to, size);
369b060ba50SNeel Natu 	if (ptr == NULL)
370c487da1eSNeel Natu 		return (EFAULT);
371c487da1eSNeel Natu 
372b060ba50SNeel Natu 	memcpy(ptr, from, size);
373c487da1eSNeel Natu 	return (0);
374c487da1eSNeel Natu }
375c487da1eSNeel Natu 
376c487da1eSNeel Natu static int
377c487da1eSNeel Natu cb_copyout(void *arg, uint64_t from, void *to, size_t size)
378c487da1eSNeel Natu {
379b060ba50SNeel Natu 	char *ptr;
380c487da1eSNeel Natu 
381c487da1eSNeel Natu 	from &= 0x7fffffff;
382b060ba50SNeel Natu 
383b060ba50SNeel Natu 	ptr = vm_map_gpa(ctx, from, size);
384b060ba50SNeel Natu 	if (ptr == NULL)
385c487da1eSNeel Natu 		return (EFAULT);
386c487da1eSNeel Natu 
387b060ba50SNeel Natu 	memcpy(to, ptr, size);
388c487da1eSNeel Natu 	return (0);
389c487da1eSNeel Natu }
390c487da1eSNeel Natu 
391c487da1eSNeel Natu static void
392c487da1eSNeel Natu cb_setreg(void *arg, int r, uint64_t v)
393c487da1eSNeel Natu {
394c487da1eSNeel Natu 	int error;
395c487da1eSNeel Natu 	enum vm_reg_name vmreg;
396c487da1eSNeel Natu 
397c487da1eSNeel Natu 	vmreg = VM_REG_LAST;
398c487da1eSNeel Natu 
399c487da1eSNeel Natu 	switch (r) {
400c487da1eSNeel Natu 	case 4:
401c487da1eSNeel Natu 		vmreg = VM_REG_GUEST_RSP;
402c487da1eSNeel Natu 		rsp = v;
403c487da1eSNeel Natu 		break;
404c487da1eSNeel Natu 	default:
405c487da1eSNeel Natu 		break;
406c487da1eSNeel Natu 	}
407c487da1eSNeel Natu 
408c487da1eSNeel Natu 	if (vmreg == VM_REG_LAST) {
409c487da1eSNeel Natu 		printf("test_setreg(%d): not implemented\n", r);
410c487da1eSNeel Natu 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
411c487da1eSNeel Natu 	}
412c487da1eSNeel Natu 
413c487da1eSNeel Natu 	error = vm_set_register(ctx, BSP, vmreg, v);
414c487da1eSNeel Natu 	if (error) {
415c487da1eSNeel Natu 		perror("vm_set_register");
416c487da1eSNeel Natu 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
417c487da1eSNeel Natu 	}
418c487da1eSNeel Natu }
419c487da1eSNeel Natu 
420c487da1eSNeel Natu static void
421c487da1eSNeel Natu cb_setmsr(void *arg, int r, uint64_t v)
422c487da1eSNeel Natu {
423c487da1eSNeel Natu 	int error;
424c487da1eSNeel Natu 	enum vm_reg_name vmreg;
425c487da1eSNeel Natu 
426c487da1eSNeel Natu 	vmreg = VM_REG_LAST;
427c487da1eSNeel Natu 
428c487da1eSNeel Natu 	switch (r) {
429c487da1eSNeel Natu 	case MSR_EFER:
430c487da1eSNeel Natu 		vmreg = VM_REG_GUEST_EFER;
431c487da1eSNeel Natu 		break;
432c487da1eSNeel Natu 	default:
433c487da1eSNeel Natu 		break;
434c487da1eSNeel Natu 	}
435c487da1eSNeel Natu 
436c487da1eSNeel Natu 	if (vmreg == VM_REG_LAST) {
437c487da1eSNeel Natu 		printf("test_setmsr(%d): not implemented\n", r);
438c487da1eSNeel Natu 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
439c487da1eSNeel Natu 	}
440c487da1eSNeel Natu 
441c487da1eSNeel Natu 	error = vm_set_register(ctx, BSP, vmreg, v);
442c487da1eSNeel Natu 	if (error) {
443c487da1eSNeel Natu 		perror("vm_set_msr");
444c487da1eSNeel Natu 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
445c487da1eSNeel Natu 	}
446c487da1eSNeel Natu }
447c487da1eSNeel Natu 
448c487da1eSNeel Natu static void
449c487da1eSNeel Natu cb_setcr(void *arg, int r, uint64_t v)
450c487da1eSNeel Natu {
451c487da1eSNeel Natu 	int error;
452c487da1eSNeel Natu 	enum vm_reg_name vmreg;
453c487da1eSNeel Natu 
454c487da1eSNeel Natu 	vmreg = VM_REG_LAST;
455c487da1eSNeel Natu 
456c487da1eSNeel Natu 	switch (r) {
457c487da1eSNeel Natu 	case 0:
458c487da1eSNeel Natu 		vmreg = VM_REG_GUEST_CR0;
459c487da1eSNeel Natu 		break;
460c487da1eSNeel Natu 	case 3:
461c487da1eSNeel Natu 		vmreg = VM_REG_GUEST_CR3;
462c487da1eSNeel Natu 		cr3 = v;
463c487da1eSNeel Natu 		break;
464c487da1eSNeel Natu 	case 4:
465c487da1eSNeel Natu 		vmreg = VM_REG_GUEST_CR4;
466c487da1eSNeel Natu 		break;
467c487da1eSNeel Natu 	default:
468c487da1eSNeel Natu 		break;
469c487da1eSNeel Natu 	}
470c487da1eSNeel Natu 
471c487da1eSNeel Natu 	if (vmreg == VM_REG_LAST) {
472c487da1eSNeel Natu 		printf("test_setcr(%d): not implemented\n", r);
473c487da1eSNeel Natu 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
474c487da1eSNeel Natu 	}
475c487da1eSNeel Natu 
476c487da1eSNeel Natu 	error = vm_set_register(ctx, BSP, vmreg, v);
477c487da1eSNeel Natu 	if (error) {
478c487da1eSNeel Natu 		perror("vm_set_cr");
479c487da1eSNeel Natu 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
480c487da1eSNeel Natu 	}
481c487da1eSNeel Natu }
482c487da1eSNeel Natu 
483c487da1eSNeel Natu static void
484c487da1eSNeel Natu cb_setgdt(void *arg, uint64_t base, size_t size)
485c487da1eSNeel Natu {
486c487da1eSNeel Natu 	int error;
487c487da1eSNeel Natu 
488c487da1eSNeel Natu 	error = vm_set_desc(ctx, BSP, VM_REG_GUEST_GDTR, base, size - 1, 0);
489c487da1eSNeel Natu 	if (error != 0) {
490c487da1eSNeel Natu 		perror("vm_set_desc(gdt)");
491c487da1eSNeel Natu 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
492c487da1eSNeel Natu 	}
493c487da1eSNeel Natu 
494c487da1eSNeel Natu 	gdtbase = base;
495c487da1eSNeel Natu }
496c487da1eSNeel Natu 
497c487da1eSNeel Natu static void
498c487da1eSNeel Natu cb_exec(void *arg, uint64_t rip)
499c487da1eSNeel Natu {
500c487da1eSNeel Natu 	int error;
501c487da1eSNeel Natu 
50200f3efe1SJohn Baldwin 	if (cr3 == 0)
50300f3efe1SJohn Baldwin 		error = vm_setup_freebsd_registers_i386(ctx, BSP, rip, gdtbase,
50400f3efe1SJohn Baldwin 		    rsp);
50500f3efe1SJohn Baldwin 	else
50600f3efe1SJohn Baldwin 		error = vm_setup_freebsd_registers(ctx, BSP, rip, cr3, gdtbase,
50700f3efe1SJohn Baldwin 		    rsp);
508c487da1eSNeel Natu 	if (error) {
509c487da1eSNeel Natu 		perror("vm_setup_freebsd_registers");
510c487da1eSNeel Natu 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
511c487da1eSNeel Natu 	}
512c487da1eSNeel Natu 
513c487da1eSNeel Natu 	cb_exit(NULL, 0);
514c487da1eSNeel Natu }
515c487da1eSNeel Natu 
516c487da1eSNeel Natu /*
517c487da1eSNeel Natu  * Misc
518c487da1eSNeel Natu  */
519c487da1eSNeel Natu 
520c487da1eSNeel Natu static void
521c487da1eSNeel Natu cb_delay(void *arg, int usec)
522c487da1eSNeel Natu {
523c487da1eSNeel Natu 
524c487da1eSNeel Natu 	usleep(usec);
525c487da1eSNeel Natu }
526c487da1eSNeel Natu 
527c487da1eSNeel Natu static void
528c487da1eSNeel Natu cb_exit(void *arg, int v)
529c487da1eSNeel Natu {
530c487da1eSNeel Natu 
5316380102cSPeter Grehan 	tcsetattr(consout_fd, TCSAFLUSH, &oldterm);
532c487da1eSNeel Natu 	exit(v);
533c487da1eSNeel Natu }
534c487da1eSNeel Natu 
535c487da1eSNeel Natu static void
536c487da1eSNeel Natu cb_getmem(void *arg, uint64_t *ret_lowmem, uint64_t *ret_highmem)
537c487da1eSNeel Natu {
538c487da1eSNeel Natu 
539be679db4SNeel Natu 	*ret_lowmem = vm_get_lowmem_size(ctx);
540be679db4SNeel Natu 	*ret_highmem = vm_get_highmem_size(ctx);
541c487da1eSNeel Natu }
542c487da1eSNeel Natu 
543b6afa84bSNeel Natu struct env {
544cb37fc82SWarner Losh 	char *str;	/* name=value */
545b6afa84bSNeel Natu 	SLIST_ENTRY(env) next;
546b6afa84bSNeel Natu };
547b6afa84bSNeel Natu 
548b6afa84bSNeel Natu static SLIST_HEAD(envhead, env) envhead;
549b6afa84bSNeel Natu 
550b6afa84bSNeel Natu static void
551cb37fc82SWarner Losh addenv(char *str)
552b6afa84bSNeel Natu {
553b6afa84bSNeel Natu 	struct env *env;
554b6afa84bSNeel Natu 
555b6afa84bSNeel Natu 	env = malloc(sizeof(struct env));
556b6afa84bSNeel Natu 	env->str = str;
557b6afa84bSNeel Natu 	SLIST_INSERT_HEAD(&envhead, env, next);
558b6afa84bSNeel Natu }
559b6afa84bSNeel Natu 
560e8e6a5f9SWarner Losh static char *
561c3e9ce33SNeel Natu cb_getenv(void *arg, int num)
562c3e9ce33SNeel Natu {
563b6afa84bSNeel Natu 	int i;
564b6afa84bSNeel Natu 	struct env *env;
565c3e9ce33SNeel Natu 
566b6afa84bSNeel Natu 	i = 0;
567b6afa84bSNeel Natu 	SLIST_FOREACH(env, &envhead, next) {
568b6afa84bSNeel Natu 		if (i == num)
569b6afa84bSNeel Natu 			return (env->str);
570b6afa84bSNeel Natu 		i++;
571b6afa84bSNeel Natu 	}
572c3e9ce33SNeel Natu 
573c3e9ce33SNeel Natu 	return (NULL);
574c3e9ce33SNeel Natu }
575c3e9ce33SNeel Natu 
57648f337b8SMarcel Moolenaar static int
57748f337b8SMarcel Moolenaar cb_vm_set_register(void *arg, int vcpu, int reg, uint64_t val)
57848f337b8SMarcel Moolenaar {
57948f337b8SMarcel Moolenaar 
58048f337b8SMarcel Moolenaar 	return (vm_set_register(ctx, vcpu, reg, val));
58148f337b8SMarcel Moolenaar }
58248f337b8SMarcel Moolenaar 
58348f337b8SMarcel Moolenaar static int
58448f337b8SMarcel Moolenaar cb_vm_set_desc(void *arg, int vcpu, int reg, uint64_t base, u_int limit,
58548f337b8SMarcel Moolenaar     u_int access)
58648f337b8SMarcel Moolenaar {
58748f337b8SMarcel Moolenaar 
58848f337b8SMarcel Moolenaar 	return (vm_set_desc(ctx, vcpu, reg, base, limit, access));
58948f337b8SMarcel Moolenaar }
59048f337b8SMarcel Moolenaar 
591d3d381b2SKyle Evans static void
592d3d381b2SKyle Evans cb_swap_interpreter(void *arg, const char *interp_req)
593d3d381b2SKyle Evans {
594d3d381b2SKyle Evans 
595d3d381b2SKyle Evans 	/*
596d3d381b2SKyle Evans 	 * If the user specified a loader but we detected a mismatch, we should
597d3d381b2SKyle Evans 	 * not try to pivot to a different loader on them.
598d3d381b2SKyle Evans 	 */
599d3d381b2SKyle Evans 	free(loader);
600d3d381b2SKyle Evans 	if (explicit_loader == 1) {
601d3d381b2SKyle Evans 		perror("requested loader interpreter does not match guest userboot");
602d3d381b2SKyle Evans 		cb_exit(NULL, 1);
603d3d381b2SKyle Evans 	}
604d3d381b2SKyle Evans 	if (interp_req == NULL || *interp_req == '\0') {
605d3d381b2SKyle Evans 		perror("guest failed to request an interpreter");
606d3d381b2SKyle Evans 		cb_exit(NULL, 1);
607d3d381b2SKyle Evans 	}
608d3d381b2SKyle Evans 
609d3d381b2SKyle Evans 	if (asprintf(&loader, "/boot/userboot_%s.so", interp_req) == -1)
610d3d381b2SKyle Evans 		err(EX_OSERR, "malloc");
611d3d381b2SKyle Evans 	need_reinit = 1;
612d3d381b2SKyle Evans 	longjmp(jb, 1);
613d3d381b2SKyle Evans }
614d3d381b2SKyle Evans 
615a10c6f55SNeel Natu static struct loader_callbacks cb = {
616c487da1eSNeel Natu 	.getc = cb_getc,
617c487da1eSNeel Natu 	.putc = cb_putc,
618c487da1eSNeel Natu 	.poll = cb_poll,
619c487da1eSNeel Natu 
620c487da1eSNeel Natu 	.open = cb_open,
621c487da1eSNeel Natu 	.close = cb_close,
622c487da1eSNeel Natu 	.isdir = cb_isdir,
623c487da1eSNeel Natu 	.read = cb_read,
624c487da1eSNeel Natu 	.readdir = cb_readdir,
625c487da1eSNeel Natu 	.seek = cb_seek,
626c487da1eSNeel Natu 	.stat = cb_stat,
627c487da1eSNeel Natu 
628c487da1eSNeel Natu 	.diskread = cb_diskread,
629*cc71ff72SConrad Meyer 	.diskwrite = cb_diskwrite,
630a10c6f55SNeel Natu 	.diskioctl = cb_diskioctl,
631c487da1eSNeel Natu 
632c487da1eSNeel Natu 	.copyin = cb_copyin,
633c487da1eSNeel Natu 	.copyout = cb_copyout,
634c487da1eSNeel Natu 	.setreg = cb_setreg,
635c487da1eSNeel Natu 	.setmsr = cb_setmsr,
636c487da1eSNeel Natu 	.setcr = cb_setcr,
637c487da1eSNeel Natu 	.setgdt = cb_setgdt,
638c487da1eSNeel Natu 	.exec = cb_exec,
639c487da1eSNeel Natu 
640c487da1eSNeel Natu 	.delay = cb_delay,
641c487da1eSNeel Natu 	.exit = cb_exit,
642c487da1eSNeel Natu 	.getmem = cb_getmem,
643c3e9ce33SNeel Natu 
644c3e9ce33SNeel Natu 	.getenv = cb_getenv,
64548f337b8SMarcel Moolenaar 
64648f337b8SMarcel Moolenaar 	/* Version 4 additions */
64748f337b8SMarcel Moolenaar 	.vm_set_register = cb_vm_set_register,
64848f337b8SMarcel Moolenaar 	.vm_set_desc = cb_vm_set_desc,
649d3d381b2SKyle Evans 
650d3d381b2SKyle Evans 	/* Version 5 additions */
651d3d381b2SKyle Evans 	.swap_interpreter = cb_swap_interpreter,
652c487da1eSNeel Natu };
653c487da1eSNeel Natu 
6546380102cSPeter Grehan static int
6556380102cSPeter Grehan altcons_open(char *path)
6566380102cSPeter Grehan {
6576380102cSPeter Grehan 	struct stat sb;
6586380102cSPeter Grehan 	int err;
6596380102cSPeter Grehan 	int fd;
6606380102cSPeter Grehan 
6616380102cSPeter Grehan 	/*
6626380102cSPeter Grehan 	 * Allow stdio to be passed in so that the same string
6636380102cSPeter Grehan 	 * can be used for the bhyveload console and bhyve com-port
6646380102cSPeter Grehan 	 * parameters
6656380102cSPeter Grehan 	 */
6666380102cSPeter Grehan 	if (!strcmp(path, "stdio"))
6676380102cSPeter Grehan 		return (0);
6686380102cSPeter Grehan 
6696380102cSPeter Grehan 	err = stat(path, &sb);
6706380102cSPeter Grehan 	if (err == 0) {
6716380102cSPeter Grehan 		if (!S_ISCHR(sb.st_mode))
6726380102cSPeter Grehan 			err = ENOTSUP;
6736380102cSPeter Grehan 		else {
6746380102cSPeter Grehan 			fd = open(path, O_RDWR | O_NONBLOCK);
6756380102cSPeter Grehan 			if (fd < 0)
6766380102cSPeter Grehan 				err = errno;
6776380102cSPeter Grehan 			else
6786380102cSPeter Grehan 				consin_fd = consout_fd = fd;
6796380102cSPeter Grehan 		}
6806380102cSPeter Grehan 	}
6816380102cSPeter Grehan 
6826380102cSPeter Grehan 	return (err);
6836380102cSPeter Grehan }
6846380102cSPeter Grehan 
685cf087c12SPeter Grehan static int
686cf087c12SPeter Grehan disk_open(char *path)
687cf087c12SPeter Grehan {
688a0bc451fSSean Chittenden 	int fd;
689cf087c12SPeter Grehan 
6900db293c1SAllan Jude 	if (ndisks >= NDISKS)
691cf087c12SPeter Grehan 		return (ERANGE);
692cf087c12SPeter Grehan 
693cf087c12SPeter Grehan 	fd = open(path, O_RDONLY);
694a0bc451fSSean Chittenden 	if (fd < 0)
695a0bc451fSSean Chittenden 		return (errno);
696cf087c12SPeter Grehan 
697cf087c12SPeter Grehan 	disk_fd[ndisks] = fd;
698cf087c12SPeter Grehan 	ndisks++;
699cf087c12SPeter Grehan 
700a0bc451fSSean Chittenden 	return (0);
701cf087c12SPeter Grehan }
702cf087c12SPeter Grehan 
703c487da1eSNeel Natu static void
704c487da1eSNeel Natu usage(void)
705c487da1eSNeel Natu {
706c487da1eSNeel Natu 
707b060ba50SNeel Natu 	fprintf(stderr,
7089b1aa8d6SNeel Natu 	    "usage: %s [-S][-c <console-device>] [-d <disk-path>] [-e <name=value>]\n"
7096ee52c65SRoman Bogorodskiy 	    "       %*s [-h <host-path>] [-m memsize[K|k|M|m|G|g|T|t]] <vmname>\n",
7106380102cSPeter Grehan 	    progname,
711b5331f4dSNeel Natu 	    (int)strlen(progname), "");
712c487da1eSNeel Natu 	exit(1);
713c487da1eSNeel Natu }
714c487da1eSNeel Natu 
715c487da1eSNeel Natu int
716c487da1eSNeel Natu main(int argc, char** argv)
717c487da1eSNeel Natu {
718a10c6f55SNeel Natu 	void (*func)(struct loader_callbacks *, void *, int, int);
719b060ba50SNeel Natu 	uint64_t mem_size;
720d3d381b2SKyle Evans 	int opt, error, memflags;
721c487da1eSNeel Natu 
722b5331f4dSNeel Natu 	progname = basename(argv[0]);
723c487da1eSNeel Natu 
7249b1aa8d6SNeel Natu 	memflags = 0;
725b060ba50SNeel Natu 	mem_size = 256 * MB;
726c487da1eSNeel Natu 
7276380102cSPeter Grehan 	consin_fd = STDIN_FILENO;
7286380102cSPeter Grehan 	consout_fd = STDOUT_FILENO;
7296380102cSPeter Grehan 
730568e3a8dSMarcel Moolenaar 	while ((opt = getopt(argc, argv, "CSc:d:e:h:l:m:")) != -1) {
731c487da1eSNeel Natu 		switch (opt) {
7326380102cSPeter Grehan 		case 'c':
7336380102cSPeter Grehan 			error = altcons_open(optarg);
7346380102cSPeter Grehan 			if (error != 0)
7356380102cSPeter Grehan 				errx(EX_USAGE, "Could not open '%s'", optarg);
7366380102cSPeter Grehan 			break;
737cf087c12SPeter Grehan 
738c487da1eSNeel Natu 		case 'd':
739cf087c12SPeter Grehan 			error = disk_open(optarg);
740cf087c12SPeter Grehan 			if (error != 0)
741cf087c12SPeter Grehan 				errx(EX_USAGE, "Could not open '%s'", optarg);
742c487da1eSNeel Natu 			break;
743c487da1eSNeel Natu 
744b6afa84bSNeel Natu 		case 'e':
745b6afa84bSNeel Natu 			addenv(optarg);
746b6afa84bSNeel Natu 			break;
747b6afa84bSNeel Natu 
748c487da1eSNeel Natu 		case 'h':
749c487da1eSNeel Natu 			host_base = optarg;
750c487da1eSNeel Natu 			break;
751c487da1eSNeel Natu 
7528c96dcc1SMarcel Moolenaar 		case 'l':
7538c96dcc1SMarcel Moolenaar 			if (loader != NULL)
7548c96dcc1SMarcel Moolenaar 				errx(EX_USAGE, "-l can only be given once");
7558c96dcc1SMarcel Moolenaar 			loader = strdup(optarg);
7568c96dcc1SMarcel Moolenaar 			if (loader == NULL)
7578c96dcc1SMarcel Moolenaar 				err(EX_OSERR, "malloc");
758d3d381b2SKyle Evans 			explicit_loader = 1;
7598c96dcc1SMarcel Moolenaar 			break;
7608c96dcc1SMarcel Moolenaar 
761c487da1eSNeel Natu 		case 'm':
762200758f1SNeel Natu 			error = vm_parse_memsize(optarg, &mem_size);
763200758f1SNeel Natu 			if (error != 0)
764200758f1SNeel Natu 				errx(EX_USAGE, "Invalid memsize '%s'", optarg);
765c487da1eSNeel Natu 			break;
766568e3a8dSMarcel Moolenaar 		case 'C':
767568e3a8dSMarcel Moolenaar 			memflags |= VM_MEM_F_INCORE;
768568e3a8dSMarcel Moolenaar 			break;
7699b1aa8d6SNeel Natu 		case 'S':
7709b1aa8d6SNeel Natu 			memflags |= VM_MEM_F_WIRED;
7719b1aa8d6SNeel Natu 			break;
772c487da1eSNeel Natu 		case '?':
773c487da1eSNeel Natu 			usage();
774c487da1eSNeel Natu 		}
775c487da1eSNeel Natu 	}
776c487da1eSNeel Natu 
777c487da1eSNeel Natu 	argc -= optind;
778c487da1eSNeel Natu 	argv += optind;
779c487da1eSNeel Natu 
780c487da1eSNeel Natu 	if (argc != 1)
781c487da1eSNeel Natu 		usage();
782c487da1eSNeel Natu 
783c487da1eSNeel Natu 	vmname = argv[0];
784c487da1eSNeel Natu 
7855fcf252fSNeel Natu 	need_reinit = 0;
786c487da1eSNeel Natu 	error = vm_create(vmname);
7875fcf252fSNeel Natu 	if (error) {
7885fcf252fSNeel Natu 		if (errno != EEXIST) {
789c487da1eSNeel Natu 			perror("vm_create");
790c487da1eSNeel Natu 			exit(1);
7915fcf252fSNeel Natu 		}
7925fcf252fSNeel Natu 		need_reinit = 1;
793c487da1eSNeel Natu 	}
794c487da1eSNeel Natu 
795c487da1eSNeel Natu 	ctx = vm_open(vmname);
796c487da1eSNeel Natu 	if (ctx == NULL) {
797c487da1eSNeel Natu 		perror("vm_open");
798c487da1eSNeel Natu 		exit(1);
799c487da1eSNeel Natu 	}
800c487da1eSNeel Natu 
801d3d381b2SKyle Evans 	/*
802d3d381b2SKyle Evans 	 * setjmp in the case the guest wants to swap out interpreter,
803d3d381b2SKyle Evans 	 * cb_swap_interpreter will swap out loader as appropriate and set
804d3d381b2SKyle Evans 	 * need_reinit so that we end up in a clean state once again.
805d3d381b2SKyle Evans 	 */
806d3d381b2SKyle Evans 	setjmp(jb);
807d3d381b2SKyle Evans 
8085fcf252fSNeel Natu 	if (need_reinit) {
8095fcf252fSNeel Natu 		error = vm_reinit(ctx);
8105fcf252fSNeel Natu 		if (error) {
8115fcf252fSNeel Natu 			perror("vm_reinit");
8125fcf252fSNeel Natu 			exit(1);
8135fcf252fSNeel Natu 		}
8145fcf252fSNeel Natu 	}
8155fcf252fSNeel Natu 
8169b1aa8d6SNeel Natu 	vm_set_memflags(ctx, memflags);
817b060ba50SNeel Natu 	error = vm_setup_memory(ctx, mem_size, VM_MMAP_ALL);
818c487da1eSNeel Natu 	if (error) {
819b060ba50SNeel Natu 		perror("vm_setup_memory");
820c487da1eSNeel Natu 		exit(1);
821c487da1eSNeel Natu 	}
822c487da1eSNeel Natu 
8238c96dcc1SMarcel Moolenaar 	if (loader == NULL) {
8248c96dcc1SMarcel Moolenaar 		loader = strdup("/boot/userboot.so");
8258c96dcc1SMarcel Moolenaar 		if (loader == NULL)
8268c96dcc1SMarcel Moolenaar 			err(EX_OSERR, "malloc");
8278c96dcc1SMarcel Moolenaar 	}
828d3d381b2SKyle Evans 	if (loader_hdl != NULL)
829d3d381b2SKyle Evans 		dlclose(loader_hdl);
830d3d381b2SKyle Evans 	loader_hdl = dlopen(loader, RTLD_LOCAL);
831d3d381b2SKyle Evans 	if (!loader_hdl) {
8328c96dcc1SMarcel Moolenaar 		printf("%s\n", dlerror());
8338c96dcc1SMarcel Moolenaar 		free(loader);
8348c96dcc1SMarcel Moolenaar 		return (1);
8358c96dcc1SMarcel Moolenaar 	}
836d3d381b2SKyle Evans 	func = dlsym(loader_hdl, "loader_main");
8378c96dcc1SMarcel Moolenaar 	if (!func) {
8388c96dcc1SMarcel Moolenaar 		printf("%s\n", dlerror());
8398c96dcc1SMarcel Moolenaar 		free(loader);
8408c96dcc1SMarcel Moolenaar 		return (1);
8418c96dcc1SMarcel Moolenaar 	}
8428c96dcc1SMarcel Moolenaar 
8436380102cSPeter Grehan 	tcgetattr(consout_fd, &term);
844c487da1eSNeel Natu 	oldterm = term;
8456380102cSPeter Grehan 	cfmakeraw(&term);
8466380102cSPeter Grehan 	term.c_cflag |= CLOCAL;
8476380102cSPeter Grehan 
8486380102cSPeter Grehan 	tcsetattr(consout_fd, TCSAFLUSH, &term);
8496380102cSPeter Grehan 
850b6afa84bSNeel Natu 	addenv("smbios.bios.vendor=BHYVE");
851b6afa84bSNeel Natu 	addenv("boot_serial=1");
852b6afa84bSNeel Natu 
853d3d381b2SKyle Evans 	func(&cb, NULL, USERBOOT_VERSION_5, ndisks);
8548c96dcc1SMarcel Moolenaar 
8558c96dcc1SMarcel Moolenaar 	free(loader);
8568c96dcc1SMarcel Moolenaar 	return (0);
857c487da1eSNeel Natu }
858