xref: /freebsd-src/usr.sbin/bhyveload/bhyveload.c (revision 6589ee29dfcc027d6a82428f8022d4db71289c9d)
1c487da1eSNeel Natu /*-
2c487da1eSNeel Natu  * Copyright (c) 2011 NetApp, Inc.
3c487da1eSNeel Natu  * All rights reserved.
4c487da1eSNeel Natu  *
5c487da1eSNeel Natu  * Redistribution and use in source and binary forms, with or without
6c487da1eSNeel Natu  * modification, are permitted provided that the following conditions
7c487da1eSNeel Natu  * are met:
8c487da1eSNeel Natu  * 1. Redistributions of source code must retain the above copyright
9c487da1eSNeel Natu  *    notice, this list of conditions and the following disclaimer.
10c487da1eSNeel Natu  * 2. Redistributions in binary form must reproduce the above copyright
11c487da1eSNeel Natu  *    notice, this list of conditions and the following disclaimer in the
12c487da1eSNeel Natu  *    documentation and/or other materials provided with the distribution.
13c487da1eSNeel Natu  *
14c487da1eSNeel Natu  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
15c487da1eSNeel Natu  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16c487da1eSNeel Natu  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17c487da1eSNeel Natu  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
18c487da1eSNeel Natu  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19c487da1eSNeel Natu  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20c487da1eSNeel Natu  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21c487da1eSNeel Natu  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22c487da1eSNeel Natu  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23c487da1eSNeel Natu  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24c487da1eSNeel Natu  * SUCH DAMAGE.
25c487da1eSNeel Natu  *
26c487da1eSNeel Natu  * $FreeBSD$
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  * $FreeBSD$
55c487da1eSNeel Natu  */
56c487da1eSNeel Natu 
57c487da1eSNeel Natu #include <sys/cdefs.h>
58c487da1eSNeel Natu __FBSDID("$FreeBSD$");
59c487da1eSNeel Natu 
60c487da1eSNeel Natu #include <sys/ioctl.h>
61c487da1eSNeel Natu #include <sys/stat.h>
62a10c6f55SNeel Natu #include <sys/disk.h>
63b6afa84bSNeel Natu #include <sys/queue.h>
64c487da1eSNeel Natu 
65c487da1eSNeel Natu #include <machine/specialreg.h>
66c487da1eSNeel Natu #include <machine/vmm.h>
67c487da1eSNeel Natu 
68c487da1eSNeel Natu #include <dirent.h>
69c487da1eSNeel Natu #include <dlfcn.h>
70c487da1eSNeel Natu #include <errno.h>
71200758f1SNeel Natu #include <err.h>
72c487da1eSNeel Natu #include <fcntl.h>
73c487da1eSNeel Natu #include <getopt.h>
74b5331f4dSNeel Natu #include <libgen.h>
75c487da1eSNeel Natu #include <limits.h>
76c487da1eSNeel Natu #include <stdio.h>
77c487da1eSNeel Natu #include <stdlib.h>
78c487da1eSNeel Natu #include <string.h>
79200758f1SNeel Natu #include <sysexits.h>
80c487da1eSNeel Natu #include <termios.h>
81c487da1eSNeel Natu #include <unistd.h>
82c487da1eSNeel Natu 
83c487da1eSNeel Natu #include <vmmapi.h>
84c487da1eSNeel Natu 
85c487da1eSNeel Natu #include "userboot.h"
86c487da1eSNeel Natu 
87c487da1eSNeel Natu #define	MB	(1024 * 1024UL)
88c487da1eSNeel Natu #define	GB	(1024 * 1024 * 1024UL)
89c487da1eSNeel Natu #define	BSP	0
90c487da1eSNeel Natu 
91cf087c12SPeter Grehan #define	NDISKS	32
92cf087c12SPeter Grehan 
93384305ffSPeter Grehan static char *host_base;
94c487da1eSNeel Natu static struct termios term, oldterm;
95cf087c12SPeter Grehan static int disk_fd[NDISKS];
96cf087c12SPeter Grehan static int ndisks;
976380102cSPeter Grehan static int consin_fd, consout_fd;
98c487da1eSNeel Natu 
99b060ba50SNeel Natu static char *vmname, *progname;
100c487da1eSNeel Natu static struct vmctx *ctx;
101c487da1eSNeel Natu 
102c487da1eSNeel Natu static uint64_t gdtbase, cr3, rsp;
103c487da1eSNeel Natu 
104c487da1eSNeel Natu static void cb_exit(void *arg, int v);
105c487da1eSNeel Natu 
106c487da1eSNeel Natu /*
107c487da1eSNeel Natu  * Console i/o callbacks
108c487da1eSNeel Natu  */
109c487da1eSNeel Natu 
110c487da1eSNeel Natu static void
111c487da1eSNeel Natu cb_putc(void *arg, int ch)
112c487da1eSNeel Natu {
113c487da1eSNeel Natu 	char c = ch;
114c487da1eSNeel Natu 
1156380102cSPeter Grehan 	(void) write(consout_fd, &c, 1);
116c487da1eSNeel Natu }
117c487da1eSNeel Natu 
118c487da1eSNeel Natu static int
119c487da1eSNeel Natu cb_getc(void *arg)
120c487da1eSNeel Natu {
121c487da1eSNeel Natu 	char c;
122c487da1eSNeel Natu 
1236380102cSPeter Grehan 	if (read(consin_fd, &c, 1) == 1)
124c487da1eSNeel Natu 		return (c);
125c487da1eSNeel Natu 	return (-1);
126c487da1eSNeel Natu }
127c487da1eSNeel Natu 
128c487da1eSNeel Natu static int
129c487da1eSNeel Natu cb_poll(void *arg)
130c487da1eSNeel Natu {
131c487da1eSNeel Natu 	int n;
132c487da1eSNeel Natu 
1336380102cSPeter Grehan 	if (ioctl(consin_fd, FIONREAD, &n) >= 0)
134c487da1eSNeel Natu 		return (n > 0);
135c487da1eSNeel Natu 	return (0);
136c487da1eSNeel Natu }
137c487da1eSNeel Natu 
138c487da1eSNeel Natu /*
139c487da1eSNeel Natu  * Host filesystem i/o callbacks
140c487da1eSNeel Natu  */
141c487da1eSNeel Natu 
142c487da1eSNeel Natu struct cb_file {
143c487da1eSNeel Natu 	int cf_isdir;
144c487da1eSNeel Natu 	size_t cf_size;
145c487da1eSNeel Natu 	struct stat cf_stat;
146c487da1eSNeel Natu 	union {
147c487da1eSNeel Natu 		int fd;
148c487da1eSNeel Natu 		DIR *dir;
149c487da1eSNeel Natu 	} cf_u;
150c487da1eSNeel Natu };
151c487da1eSNeel Natu 
152c487da1eSNeel Natu static int
153c487da1eSNeel Natu cb_open(void *arg, const char *filename, void **hp)
154c487da1eSNeel Natu {
155c487da1eSNeel Natu 	struct cb_file *cf;
156c487da1eSNeel Natu 	char path[PATH_MAX];
157c487da1eSNeel Natu 
158c487da1eSNeel Natu 	if (!host_base)
159c487da1eSNeel Natu 		return (ENOENT);
160c487da1eSNeel Natu 
161c487da1eSNeel Natu 	strlcpy(path, host_base, PATH_MAX);
162c487da1eSNeel Natu 	if (path[strlen(path) - 1] == '/')
163c487da1eSNeel Natu 		path[strlen(path) - 1] = 0;
164c487da1eSNeel Natu 	strlcat(path, filename, PATH_MAX);
165c487da1eSNeel Natu 	cf = malloc(sizeof(struct cb_file));
166c487da1eSNeel Natu 	if (stat(path, &cf->cf_stat) < 0) {
167c487da1eSNeel Natu 		free(cf);
168c487da1eSNeel Natu 		return (errno);
169c487da1eSNeel Natu 	}
170c487da1eSNeel Natu 
1718f61276dSPedro F. Giffuni 	cf->cf_size = cf->cf_stat.st_size;
172c487da1eSNeel Natu 	if (S_ISDIR(cf->cf_stat.st_mode)) {
173c487da1eSNeel Natu 		cf->cf_isdir = 1;
174c487da1eSNeel Natu 		cf->cf_u.dir = opendir(path);
175c487da1eSNeel Natu 		if (!cf->cf_u.dir)
176c487da1eSNeel Natu 			goto out;
177c487da1eSNeel Natu 		*hp = cf;
178c487da1eSNeel Natu 		return (0);
179c487da1eSNeel Natu 	}
180c487da1eSNeel Natu 	if (S_ISREG(cf->cf_stat.st_mode)) {
181c487da1eSNeel Natu 		cf->cf_isdir = 0;
182c487da1eSNeel Natu 		cf->cf_u.fd = open(path, O_RDONLY);
183c487da1eSNeel Natu 		if (cf->cf_u.fd < 0)
184c487da1eSNeel Natu 			goto out;
185c487da1eSNeel Natu 		*hp = cf;
186c487da1eSNeel Natu 		return (0);
187c487da1eSNeel Natu 	}
188c487da1eSNeel Natu 
189c487da1eSNeel Natu out:
190c487da1eSNeel Natu 	free(cf);
191c487da1eSNeel Natu 	return (EINVAL);
192c487da1eSNeel Natu }
193c487da1eSNeel Natu 
194c487da1eSNeel Natu static int
195c487da1eSNeel Natu cb_close(void *arg, void *h)
196c487da1eSNeel Natu {
197c487da1eSNeel Natu 	struct cb_file *cf = h;
198c487da1eSNeel Natu 
199c487da1eSNeel Natu 	if (cf->cf_isdir)
200c487da1eSNeel Natu 		closedir(cf->cf_u.dir);
201c487da1eSNeel Natu 	else
202c487da1eSNeel Natu 		close(cf->cf_u.fd);
203c487da1eSNeel Natu 	free(cf);
204c487da1eSNeel Natu 
205c487da1eSNeel Natu 	return (0);
206c487da1eSNeel Natu }
207c487da1eSNeel Natu 
208c487da1eSNeel Natu static int
209c487da1eSNeel Natu cb_isdir(void *arg, void *h)
210c487da1eSNeel Natu {
211c487da1eSNeel Natu 	struct cb_file *cf = h;
212c487da1eSNeel Natu 
213c487da1eSNeel Natu 	return (cf->cf_isdir);
214c487da1eSNeel Natu }
215c487da1eSNeel Natu 
216c487da1eSNeel Natu static int
217c487da1eSNeel Natu cb_read(void *arg, void *h, void *buf, size_t size, size_t *resid)
218c487da1eSNeel Natu {
219c487da1eSNeel Natu 	struct cb_file *cf = h;
220c487da1eSNeel Natu 	ssize_t sz;
221c487da1eSNeel Natu 
222c487da1eSNeel Natu 	if (cf->cf_isdir)
223c487da1eSNeel Natu 		return (EINVAL);
224c487da1eSNeel Natu 	sz = read(cf->cf_u.fd, buf, size);
225c487da1eSNeel Natu 	if (sz < 0)
226c487da1eSNeel Natu 		return (EINVAL);
227c487da1eSNeel Natu 	*resid = size - sz;
228c487da1eSNeel Natu 	return (0);
229c487da1eSNeel Natu }
230c487da1eSNeel Natu 
231c487da1eSNeel Natu static int
232c487da1eSNeel Natu cb_readdir(void *arg, void *h, uint32_t *fileno_return, uint8_t *type_return,
233c487da1eSNeel Natu 	   size_t *namelen_return, char *name)
234c487da1eSNeel Natu {
235c487da1eSNeel Natu 	struct cb_file *cf = h;
236c487da1eSNeel Natu 	struct dirent *dp;
237c487da1eSNeel Natu 
238c487da1eSNeel Natu 	if (!cf->cf_isdir)
239c487da1eSNeel Natu 		return (EINVAL);
240c487da1eSNeel Natu 
241c487da1eSNeel Natu 	dp = readdir(cf->cf_u.dir);
242c487da1eSNeel Natu 	if (!dp)
243c487da1eSNeel Natu 		return (ENOENT);
244c487da1eSNeel Natu 
245c487da1eSNeel Natu 	/*
246c487da1eSNeel Natu 	 * Note: d_namlen is in the range 0..255 and therefore less
247c487da1eSNeel Natu 	 * than PATH_MAX so we don't need to test before copying.
248c487da1eSNeel Natu 	 */
249c487da1eSNeel Natu 	*fileno_return = dp->d_fileno;
250c487da1eSNeel Natu 	*type_return = dp->d_type;
251c487da1eSNeel Natu 	*namelen_return = dp->d_namlen;
252c487da1eSNeel Natu 	memcpy(name, dp->d_name, dp->d_namlen);
253c487da1eSNeel Natu 	name[dp->d_namlen] = 0;
254c487da1eSNeel Natu 
255c487da1eSNeel Natu 	return (0);
256c487da1eSNeel Natu }
257c487da1eSNeel Natu 
258c487da1eSNeel Natu static int
259c487da1eSNeel Natu cb_seek(void *arg, void *h, uint64_t offset, int whence)
260c487da1eSNeel Natu {
261c487da1eSNeel Natu 	struct cb_file *cf = h;
262c487da1eSNeel Natu 
263c487da1eSNeel Natu 	if (cf->cf_isdir)
264c487da1eSNeel Natu 		return (EINVAL);
265c487da1eSNeel Natu 	if (lseek(cf->cf_u.fd, offset, whence) < 0)
266c487da1eSNeel Natu 		return (errno);
267c487da1eSNeel Natu 	return (0);
268c487da1eSNeel Natu }
269c487da1eSNeel Natu 
270c487da1eSNeel Natu static int
271c487da1eSNeel Natu cb_stat(void *arg, void *h, int *mode, int *uid, int *gid, uint64_t *size)
272c487da1eSNeel Natu {
273c487da1eSNeel Natu 	struct cb_file *cf = h;
274c487da1eSNeel Natu 
275c487da1eSNeel Natu 	*mode = cf->cf_stat.st_mode;
276c487da1eSNeel Natu 	*uid = cf->cf_stat.st_uid;
277c487da1eSNeel Natu 	*gid = cf->cf_stat.st_gid;
278c487da1eSNeel Natu 	*size = cf->cf_stat.st_size;
279c487da1eSNeel Natu 	return (0);
280c487da1eSNeel Natu }
281c487da1eSNeel Natu 
282c487da1eSNeel Natu /*
283c487da1eSNeel Natu  * Disk image i/o callbacks
284c487da1eSNeel Natu  */
285c487da1eSNeel Natu 
286c487da1eSNeel Natu static int
287c487da1eSNeel Natu cb_diskread(void *arg, int unit, uint64_t from, void *to, size_t size,
288c487da1eSNeel Natu 	    size_t *resid)
289c487da1eSNeel Natu {
290c487da1eSNeel Natu 	ssize_t n;
291c487da1eSNeel Natu 
292cf087c12SPeter Grehan 	if (unit < 0 || unit >= ndisks )
293c487da1eSNeel Natu 		return (EIO);
294cf087c12SPeter Grehan 	n = pread(disk_fd[unit], to, size, from);
295c487da1eSNeel Natu 	if (n < 0)
296c487da1eSNeel Natu 		return (errno);
297c487da1eSNeel Natu 	*resid = size - n;
298c487da1eSNeel Natu 	return (0);
299c487da1eSNeel Natu }
300c487da1eSNeel Natu 
301a10c6f55SNeel Natu static int
302a10c6f55SNeel Natu cb_diskioctl(void *arg, int unit, u_long cmd, void *data)
303a10c6f55SNeel Natu {
304a10c6f55SNeel Natu 	struct stat sb;
305a10c6f55SNeel Natu 
306cf087c12SPeter Grehan 	if (unit < 0 || unit >= ndisks)
307a10c6f55SNeel Natu 		return (EBADF);
308a10c6f55SNeel Natu 
309a10c6f55SNeel Natu 	switch (cmd) {
310a10c6f55SNeel Natu 	case DIOCGSECTORSIZE:
311a10c6f55SNeel Natu 		*(u_int *)data = 512;
312a10c6f55SNeel Natu 		break;
313a10c6f55SNeel Natu 	case DIOCGMEDIASIZE:
314*6589ee29SAndriy Gapon 		if (fstat(disk_fd[unit], &sb) != 0)
315a10c6f55SNeel Natu 			return (ENOTTY);
316*6589ee29SAndriy Gapon 		if (S_ISCHR(sb.st_mode) &&
317*6589ee29SAndriy Gapon 		    ioctl(disk_fd[unit], DIOCGMEDIASIZE, &sb.st_size) != 0)
318*6589ee29SAndriy Gapon 				return (ENOTTY);
319*6589ee29SAndriy Gapon 		*(off_t *)data = sb.st_size;
320a10c6f55SNeel Natu 		break;
321a10c6f55SNeel Natu 	default:
322a10c6f55SNeel Natu 		return (ENOTTY);
323a10c6f55SNeel Natu 	}
324a10c6f55SNeel Natu 
325a10c6f55SNeel Natu 	return (0);
326a10c6f55SNeel Natu }
327a10c6f55SNeel Natu 
328c487da1eSNeel Natu /*
329c487da1eSNeel Natu  * Guest virtual machine i/o callbacks
330c487da1eSNeel Natu  */
331c487da1eSNeel Natu static int
332c487da1eSNeel Natu cb_copyin(void *arg, const void *from, uint64_t to, size_t size)
333c487da1eSNeel Natu {
334b060ba50SNeel Natu 	char *ptr;
335c487da1eSNeel Natu 
336c487da1eSNeel Natu 	to &= 0x7fffffff;
337b060ba50SNeel Natu 
338b060ba50SNeel Natu 	ptr = vm_map_gpa(ctx, to, size);
339b060ba50SNeel Natu 	if (ptr == NULL)
340c487da1eSNeel Natu 		return (EFAULT);
341c487da1eSNeel Natu 
342b060ba50SNeel Natu 	memcpy(ptr, from, size);
343c487da1eSNeel Natu 	return (0);
344c487da1eSNeel Natu }
345c487da1eSNeel Natu 
346c487da1eSNeel Natu static int
347c487da1eSNeel Natu cb_copyout(void *arg, uint64_t from, void *to, size_t size)
348c487da1eSNeel Natu {
349b060ba50SNeel Natu 	char *ptr;
350c487da1eSNeel Natu 
351c487da1eSNeel Natu 	from &= 0x7fffffff;
352b060ba50SNeel Natu 
353b060ba50SNeel Natu 	ptr = vm_map_gpa(ctx, from, size);
354b060ba50SNeel Natu 	if (ptr == NULL)
355c487da1eSNeel Natu 		return (EFAULT);
356c487da1eSNeel Natu 
357b060ba50SNeel Natu 	memcpy(to, ptr, size);
358c487da1eSNeel Natu 	return (0);
359c487da1eSNeel Natu }
360c487da1eSNeel Natu 
361c487da1eSNeel Natu static void
362c487da1eSNeel Natu cb_setreg(void *arg, int r, uint64_t v)
363c487da1eSNeel Natu {
364c487da1eSNeel Natu 	int error;
365c487da1eSNeel Natu 	enum vm_reg_name vmreg;
366c487da1eSNeel Natu 
367c487da1eSNeel Natu 	vmreg = VM_REG_LAST;
368c487da1eSNeel Natu 
369c487da1eSNeel Natu 	switch (r) {
370c487da1eSNeel Natu 	case 4:
371c487da1eSNeel Natu 		vmreg = VM_REG_GUEST_RSP;
372c487da1eSNeel Natu 		rsp = v;
373c487da1eSNeel Natu 		break;
374c487da1eSNeel Natu 	default:
375c487da1eSNeel Natu 		break;
376c487da1eSNeel Natu 	}
377c487da1eSNeel Natu 
378c487da1eSNeel Natu 	if (vmreg == VM_REG_LAST) {
379c487da1eSNeel Natu 		printf("test_setreg(%d): not implemented\n", r);
380c487da1eSNeel Natu 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
381c487da1eSNeel Natu 	}
382c487da1eSNeel Natu 
383c487da1eSNeel Natu 	error = vm_set_register(ctx, BSP, vmreg, v);
384c487da1eSNeel Natu 	if (error) {
385c487da1eSNeel Natu 		perror("vm_set_register");
386c487da1eSNeel Natu 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
387c487da1eSNeel Natu 	}
388c487da1eSNeel Natu }
389c487da1eSNeel Natu 
390c487da1eSNeel Natu static void
391c487da1eSNeel Natu cb_setmsr(void *arg, int r, uint64_t v)
392c487da1eSNeel Natu {
393c487da1eSNeel Natu 	int error;
394c487da1eSNeel Natu 	enum vm_reg_name vmreg;
395c487da1eSNeel Natu 
396c487da1eSNeel Natu 	vmreg = VM_REG_LAST;
397c487da1eSNeel Natu 
398c487da1eSNeel Natu 	switch (r) {
399c487da1eSNeel Natu 	case MSR_EFER:
400c487da1eSNeel Natu 		vmreg = VM_REG_GUEST_EFER;
401c487da1eSNeel Natu 		break;
402c487da1eSNeel Natu 	default:
403c487da1eSNeel Natu 		break;
404c487da1eSNeel Natu 	}
405c487da1eSNeel Natu 
406c487da1eSNeel Natu 	if (vmreg == VM_REG_LAST) {
407c487da1eSNeel Natu 		printf("test_setmsr(%d): not implemented\n", r);
408c487da1eSNeel Natu 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
409c487da1eSNeel Natu 	}
410c487da1eSNeel Natu 
411c487da1eSNeel Natu 	error = vm_set_register(ctx, BSP, vmreg, v);
412c487da1eSNeel Natu 	if (error) {
413c487da1eSNeel Natu 		perror("vm_set_msr");
414c487da1eSNeel Natu 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
415c487da1eSNeel Natu 	}
416c487da1eSNeel Natu }
417c487da1eSNeel Natu 
418c487da1eSNeel Natu static void
419c487da1eSNeel Natu cb_setcr(void *arg, int r, uint64_t v)
420c487da1eSNeel Natu {
421c487da1eSNeel Natu 	int error;
422c487da1eSNeel Natu 	enum vm_reg_name vmreg;
423c487da1eSNeel Natu 
424c487da1eSNeel Natu 	vmreg = VM_REG_LAST;
425c487da1eSNeel Natu 
426c487da1eSNeel Natu 	switch (r) {
427c487da1eSNeel Natu 	case 0:
428c487da1eSNeel Natu 		vmreg = VM_REG_GUEST_CR0;
429c487da1eSNeel Natu 		break;
430c487da1eSNeel Natu 	case 3:
431c487da1eSNeel Natu 		vmreg = VM_REG_GUEST_CR3;
432c487da1eSNeel Natu 		cr3 = v;
433c487da1eSNeel Natu 		break;
434c487da1eSNeel Natu 	case 4:
435c487da1eSNeel Natu 		vmreg = VM_REG_GUEST_CR4;
436c487da1eSNeel Natu 		break;
437c487da1eSNeel Natu 	default:
438c487da1eSNeel Natu 		break;
439c487da1eSNeel Natu 	}
440c487da1eSNeel Natu 
441c487da1eSNeel Natu 	if (vmreg == VM_REG_LAST) {
442c487da1eSNeel Natu 		printf("test_setcr(%d): not implemented\n", r);
443c487da1eSNeel Natu 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
444c487da1eSNeel Natu 	}
445c487da1eSNeel Natu 
446c487da1eSNeel Natu 	error = vm_set_register(ctx, BSP, vmreg, v);
447c487da1eSNeel Natu 	if (error) {
448c487da1eSNeel Natu 		perror("vm_set_cr");
449c487da1eSNeel Natu 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
450c487da1eSNeel Natu 	}
451c487da1eSNeel Natu }
452c487da1eSNeel Natu 
453c487da1eSNeel Natu static void
454c487da1eSNeel Natu cb_setgdt(void *arg, uint64_t base, size_t size)
455c487da1eSNeel Natu {
456c487da1eSNeel Natu 	int error;
457c487da1eSNeel Natu 
458c487da1eSNeel Natu 	error = vm_set_desc(ctx, BSP, VM_REG_GUEST_GDTR, base, size - 1, 0);
459c487da1eSNeel Natu 	if (error != 0) {
460c487da1eSNeel Natu 		perror("vm_set_desc(gdt)");
461c487da1eSNeel Natu 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
462c487da1eSNeel Natu 	}
463c487da1eSNeel Natu 
464c487da1eSNeel Natu 	gdtbase = base;
465c487da1eSNeel Natu }
466c487da1eSNeel Natu 
467c487da1eSNeel Natu static void
468c487da1eSNeel Natu cb_exec(void *arg, uint64_t rip)
469c487da1eSNeel Natu {
470c487da1eSNeel Natu 	int error;
471c487da1eSNeel Natu 
47200f3efe1SJohn Baldwin 	if (cr3 == 0)
47300f3efe1SJohn Baldwin 		error = vm_setup_freebsd_registers_i386(ctx, BSP, rip, gdtbase,
47400f3efe1SJohn Baldwin 		    rsp);
47500f3efe1SJohn Baldwin 	else
47600f3efe1SJohn Baldwin 		error = vm_setup_freebsd_registers(ctx, BSP, rip, cr3, gdtbase,
47700f3efe1SJohn Baldwin 		    rsp);
478c487da1eSNeel Natu 	if (error) {
479c487da1eSNeel Natu 		perror("vm_setup_freebsd_registers");
480c487da1eSNeel Natu 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
481c487da1eSNeel Natu 	}
482c487da1eSNeel Natu 
483c487da1eSNeel Natu 	cb_exit(NULL, 0);
484c487da1eSNeel Natu }
485c487da1eSNeel Natu 
486c487da1eSNeel Natu /*
487c487da1eSNeel Natu  * Misc
488c487da1eSNeel Natu  */
489c487da1eSNeel Natu 
490c487da1eSNeel Natu static void
491c487da1eSNeel Natu cb_delay(void *arg, int usec)
492c487da1eSNeel Natu {
493c487da1eSNeel Natu 
494c487da1eSNeel Natu 	usleep(usec);
495c487da1eSNeel Natu }
496c487da1eSNeel Natu 
497c487da1eSNeel Natu static void
498c487da1eSNeel Natu cb_exit(void *arg, int v)
499c487da1eSNeel Natu {
500c487da1eSNeel Natu 
5016380102cSPeter Grehan 	tcsetattr(consout_fd, TCSAFLUSH, &oldterm);
502c487da1eSNeel Natu 	exit(v);
503c487da1eSNeel Natu }
504c487da1eSNeel Natu 
505c487da1eSNeel Natu static void
506c487da1eSNeel Natu cb_getmem(void *arg, uint64_t *ret_lowmem, uint64_t *ret_highmem)
507c487da1eSNeel Natu {
508c487da1eSNeel Natu 
509be679db4SNeel Natu 	*ret_lowmem = vm_get_lowmem_size(ctx);
510be679db4SNeel Natu 	*ret_highmem = vm_get_highmem_size(ctx);
511c487da1eSNeel Natu }
512c487da1eSNeel Natu 
513b6afa84bSNeel Natu struct env {
514b6afa84bSNeel Natu 	const char *str;	/* name=value */
515b6afa84bSNeel Natu 	SLIST_ENTRY(env) next;
516b6afa84bSNeel Natu };
517b6afa84bSNeel Natu 
518b6afa84bSNeel Natu static SLIST_HEAD(envhead, env) envhead;
519b6afa84bSNeel Natu 
520b6afa84bSNeel Natu static void
521b6afa84bSNeel Natu addenv(const char *str)
522b6afa84bSNeel Natu {
523b6afa84bSNeel Natu 	struct env *env;
524b6afa84bSNeel Natu 
525b6afa84bSNeel Natu 	env = malloc(sizeof(struct env));
526b6afa84bSNeel Natu 	env->str = str;
527b6afa84bSNeel Natu 	SLIST_INSERT_HEAD(&envhead, env, next);
528b6afa84bSNeel Natu }
529b6afa84bSNeel Natu 
530c3e9ce33SNeel Natu static const char *
531c3e9ce33SNeel Natu cb_getenv(void *arg, int num)
532c3e9ce33SNeel Natu {
533b6afa84bSNeel Natu 	int i;
534b6afa84bSNeel Natu 	struct env *env;
535c3e9ce33SNeel Natu 
536b6afa84bSNeel Natu 	i = 0;
537b6afa84bSNeel Natu 	SLIST_FOREACH(env, &envhead, next) {
538b6afa84bSNeel Natu 		if (i == num)
539b6afa84bSNeel Natu 			return (env->str);
540b6afa84bSNeel Natu 		i++;
541b6afa84bSNeel Natu 	}
542c3e9ce33SNeel Natu 
543c3e9ce33SNeel Natu 	return (NULL);
544c3e9ce33SNeel Natu }
545c3e9ce33SNeel Natu 
54648f337b8SMarcel Moolenaar static int
54748f337b8SMarcel Moolenaar cb_vm_set_register(void *arg, int vcpu, int reg, uint64_t val)
54848f337b8SMarcel Moolenaar {
54948f337b8SMarcel Moolenaar 
55048f337b8SMarcel Moolenaar 	return (vm_set_register(ctx, vcpu, reg, val));
55148f337b8SMarcel Moolenaar }
55248f337b8SMarcel Moolenaar 
55348f337b8SMarcel Moolenaar static int
55448f337b8SMarcel Moolenaar cb_vm_set_desc(void *arg, int vcpu, int reg, uint64_t base, u_int limit,
55548f337b8SMarcel Moolenaar     u_int access)
55648f337b8SMarcel Moolenaar {
55748f337b8SMarcel Moolenaar 
55848f337b8SMarcel Moolenaar 	return (vm_set_desc(ctx, vcpu, reg, base, limit, access));
55948f337b8SMarcel Moolenaar }
56048f337b8SMarcel Moolenaar 
561a10c6f55SNeel Natu static struct loader_callbacks cb = {
562c487da1eSNeel Natu 	.getc = cb_getc,
563c487da1eSNeel Natu 	.putc = cb_putc,
564c487da1eSNeel Natu 	.poll = cb_poll,
565c487da1eSNeel Natu 
566c487da1eSNeel Natu 	.open = cb_open,
567c487da1eSNeel Natu 	.close = cb_close,
568c487da1eSNeel Natu 	.isdir = cb_isdir,
569c487da1eSNeel Natu 	.read = cb_read,
570c487da1eSNeel Natu 	.readdir = cb_readdir,
571c487da1eSNeel Natu 	.seek = cb_seek,
572c487da1eSNeel Natu 	.stat = cb_stat,
573c487da1eSNeel Natu 
574c487da1eSNeel Natu 	.diskread = cb_diskread,
575a10c6f55SNeel Natu 	.diskioctl = cb_diskioctl,
576c487da1eSNeel Natu 
577c487da1eSNeel Natu 	.copyin = cb_copyin,
578c487da1eSNeel Natu 	.copyout = cb_copyout,
579c487da1eSNeel Natu 	.setreg = cb_setreg,
580c487da1eSNeel Natu 	.setmsr = cb_setmsr,
581c487da1eSNeel Natu 	.setcr = cb_setcr,
582c487da1eSNeel Natu 	.setgdt = cb_setgdt,
583c487da1eSNeel Natu 	.exec = cb_exec,
584c487da1eSNeel Natu 
585c487da1eSNeel Natu 	.delay = cb_delay,
586c487da1eSNeel Natu 	.exit = cb_exit,
587c487da1eSNeel Natu 	.getmem = cb_getmem,
588c3e9ce33SNeel Natu 
589c3e9ce33SNeel Natu 	.getenv = cb_getenv,
59048f337b8SMarcel Moolenaar 
59148f337b8SMarcel Moolenaar 	/* Version 4 additions */
59248f337b8SMarcel Moolenaar 	.vm_set_register = cb_vm_set_register,
59348f337b8SMarcel Moolenaar 	.vm_set_desc = cb_vm_set_desc,
594c487da1eSNeel Natu };
595c487da1eSNeel Natu 
5966380102cSPeter Grehan static int
5976380102cSPeter Grehan altcons_open(char *path)
5986380102cSPeter Grehan {
5996380102cSPeter Grehan 	struct stat sb;
6006380102cSPeter Grehan 	int err;
6016380102cSPeter Grehan 	int fd;
6026380102cSPeter Grehan 
6036380102cSPeter Grehan 	/*
6046380102cSPeter Grehan 	 * Allow stdio to be passed in so that the same string
6056380102cSPeter Grehan 	 * can be used for the bhyveload console and bhyve com-port
6066380102cSPeter Grehan 	 * parameters
6076380102cSPeter Grehan 	 */
6086380102cSPeter Grehan 	if (!strcmp(path, "stdio"))
6096380102cSPeter Grehan 		return (0);
6106380102cSPeter Grehan 
6116380102cSPeter Grehan 	err = stat(path, &sb);
6126380102cSPeter Grehan 	if (err == 0) {
6136380102cSPeter Grehan 		if (!S_ISCHR(sb.st_mode))
6146380102cSPeter Grehan 			err = ENOTSUP;
6156380102cSPeter Grehan 		else {
6166380102cSPeter Grehan 			fd = open(path, O_RDWR | O_NONBLOCK);
6176380102cSPeter Grehan 			if (fd < 0)
6186380102cSPeter Grehan 				err = errno;
6196380102cSPeter Grehan 			else
6206380102cSPeter Grehan 				consin_fd = consout_fd = fd;
6216380102cSPeter Grehan 		}
6226380102cSPeter Grehan 	}
6236380102cSPeter Grehan 
6246380102cSPeter Grehan 	return (err);
6256380102cSPeter Grehan }
6266380102cSPeter Grehan 
627cf087c12SPeter Grehan static int
628cf087c12SPeter Grehan disk_open(char *path)
629cf087c12SPeter Grehan {
630cf087c12SPeter Grehan 	int err, fd;
631cf087c12SPeter Grehan 
6320db293c1SAllan Jude 	if (ndisks >= NDISKS)
633cf087c12SPeter Grehan 		return (ERANGE);
634cf087c12SPeter Grehan 
635cf087c12SPeter Grehan 	err = 0;
636cf087c12SPeter Grehan 	fd = open(path, O_RDONLY);
637cf087c12SPeter Grehan 
638cf087c12SPeter Grehan 	if (fd > 0) {
639cf087c12SPeter Grehan 		disk_fd[ndisks] = fd;
640cf087c12SPeter Grehan 		ndisks++;
641cf087c12SPeter Grehan 	} else
642cf087c12SPeter Grehan 		err = errno;
643cf087c12SPeter Grehan 
644cf087c12SPeter Grehan 	return (err);
645cf087c12SPeter Grehan }
646cf087c12SPeter Grehan 
647c487da1eSNeel Natu static void
648c487da1eSNeel Natu usage(void)
649c487da1eSNeel Natu {
650c487da1eSNeel Natu 
651b060ba50SNeel Natu 	fprintf(stderr,
6529b1aa8d6SNeel Natu 	    "usage: %s [-S][-c <console-device>] [-d <disk-path>] [-e <name=value>]\n"
6536ee52c65SRoman Bogorodskiy 	    "       %*s [-h <host-path>] [-m memsize[K|k|M|m|G|g|T|t]] <vmname>\n",
6546380102cSPeter Grehan 	    progname,
655b5331f4dSNeel Natu 	    (int)strlen(progname), "");
656c487da1eSNeel Natu 	exit(1);
657c487da1eSNeel Natu }
658c487da1eSNeel Natu 
659c487da1eSNeel Natu int
660c487da1eSNeel Natu main(int argc, char** argv)
661c487da1eSNeel Natu {
6628c96dcc1SMarcel Moolenaar 	char *loader;
663c487da1eSNeel Natu 	void *h;
664a10c6f55SNeel Natu 	void (*func)(struct loader_callbacks *, void *, int, int);
665b060ba50SNeel Natu 	uint64_t mem_size;
6669b1aa8d6SNeel Natu 	int opt, error, need_reinit, memflags;
667c487da1eSNeel Natu 
668b5331f4dSNeel Natu 	progname = basename(argv[0]);
669c487da1eSNeel Natu 
6708c96dcc1SMarcel Moolenaar 	loader = NULL;
6718c96dcc1SMarcel Moolenaar 
6729b1aa8d6SNeel Natu 	memflags = 0;
673b060ba50SNeel Natu 	mem_size = 256 * MB;
674c487da1eSNeel Natu 
6756380102cSPeter Grehan 	consin_fd = STDIN_FILENO;
6766380102cSPeter Grehan 	consout_fd = STDOUT_FILENO;
6776380102cSPeter Grehan 
678568e3a8dSMarcel Moolenaar 	while ((opt = getopt(argc, argv, "CSc:d:e:h:l:m:")) != -1) {
679c487da1eSNeel Natu 		switch (opt) {
6806380102cSPeter Grehan 		case 'c':
6816380102cSPeter Grehan 			error = altcons_open(optarg);
6826380102cSPeter Grehan 			if (error != 0)
6836380102cSPeter Grehan 				errx(EX_USAGE, "Could not open '%s'", optarg);
6846380102cSPeter Grehan 			break;
685cf087c12SPeter Grehan 
686c487da1eSNeel Natu 		case 'd':
687cf087c12SPeter Grehan 			error = disk_open(optarg);
688cf087c12SPeter Grehan 			if (error != 0)
689cf087c12SPeter Grehan 				errx(EX_USAGE, "Could not open '%s'", optarg);
690c487da1eSNeel Natu 			break;
691c487da1eSNeel Natu 
692b6afa84bSNeel Natu 		case 'e':
693b6afa84bSNeel Natu 			addenv(optarg);
694b6afa84bSNeel Natu 			break;
695b6afa84bSNeel Natu 
696c487da1eSNeel Natu 		case 'h':
697c487da1eSNeel Natu 			host_base = optarg;
698c487da1eSNeel Natu 			break;
699c487da1eSNeel Natu 
7008c96dcc1SMarcel Moolenaar 		case 'l':
7018c96dcc1SMarcel Moolenaar 			if (loader != NULL)
7028c96dcc1SMarcel Moolenaar 				errx(EX_USAGE, "-l can only be given once");
7038c96dcc1SMarcel Moolenaar 			loader = strdup(optarg);
7048c96dcc1SMarcel Moolenaar 			if (loader == NULL)
7058c96dcc1SMarcel Moolenaar 				err(EX_OSERR, "malloc");
7068c96dcc1SMarcel Moolenaar 			break;
7078c96dcc1SMarcel Moolenaar 
708c487da1eSNeel Natu 		case 'm':
709200758f1SNeel Natu 			error = vm_parse_memsize(optarg, &mem_size);
710200758f1SNeel Natu 			if (error != 0)
711200758f1SNeel Natu 				errx(EX_USAGE, "Invalid memsize '%s'", optarg);
712c487da1eSNeel Natu 			break;
713568e3a8dSMarcel Moolenaar 		case 'C':
714568e3a8dSMarcel Moolenaar 			memflags |= VM_MEM_F_INCORE;
715568e3a8dSMarcel Moolenaar 			break;
7169b1aa8d6SNeel Natu 		case 'S':
7179b1aa8d6SNeel Natu 			memflags |= VM_MEM_F_WIRED;
7189b1aa8d6SNeel Natu 			break;
719c487da1eSNeel Natu 		case '?':
720c487da1eSNeel Natu 			usage();
721c487da1eSNeel Natu 		}
722c487da1eSNeel Natu 	}
723c487da1eSNeel Natu 
724c487da1eSNeel Natu 	argc -= optind;
725c487da1eSNeel Natu 	argv += optind;
726c487da1eSNeel Natu 
727c487da1eSNeel Natu 	if (argc != 1)
728c487da1eSNeel Natu 		usage();
729c487da1eSNeel Natu 
730c487da1eSNeel Natu 	vmname = argv[0];
731c487da1eSNeel Natu 
7325fcf252fSNeel Natu 	need_reinit = 0;
733c487da1eSNeel Natu 	error = vm_create(vmname);
7345fcf252fSNeel Natu 	if (error) {
7355fcf252fSNeel Natu 		if (errno != EEXIST) {
736c487da1eSNeel Natu 			perror("vm_create");
737c487da1eSNeel Natu 			exit(1);
7385fcf252fSNeel Natu 		}
7395fcf252fSNeel Natu 		need_reinit = 1;
740c487da1eSNeel Natu 	}
741c487da1eSNeel Natu 
742c487da1eSNeel Natu 	ctx = vm_open(vmname);
743c487da1eSNeel Natu 	if (ctx == NULL) {
744c487da1eSNeel Natu 		perror("vm_open");
745c487da1eSNeel Natu 		exit(1);
746c487da1eSNeel Natu 	}
747c487da1eSNeel Natu 
7485fcf252fSNeel Natu 	if (need_reinit) {
7495fcf252fSNeel Natu 		error = vm_reinit(ctx);
7505fcf252fSNeel Natu 		if (error) {
7515fcf252fSNeel Natu 			perror("vm_reinit");
7525fcf252fSNeel Natu 			exit(1);
7535fcf252fSNeel Natu 		}
7545fcf252fSNeel Natu 	}
7555fcf252fSNeel Natu 
7569b1aa8d6SNeel Natu 	vm_set_memflags(ctx, memflags);
757b060ba50SNeel Natu 	error = vm_setup_memory(ctx, mem_size, VM_MMAP_ALL);
758c487da1eSNeel Natu 	if (error) {
759b060ba50SNeel Natu 		perror("vm_setup_memory");
760c487da1eSNeel Natu 		exit(1);
761c487da1eSNeel Natu 	}
762c487da1eSNeel Natu 
7638c96dcc1SMarcel Moolenaar 	if (loader == NULL) {
7648c96dcc1SMarcel Moolenaar 		loader = strdup("/boot/userboot.so");
7658c96dcc1SMarcel Moolenaar 		if (loader == NULL)
7668c96dcc1SMarcel Moolenaar 			err(EX_OSERR, "malloc");
7678c96dcc1SMarcel Moolenaar 	}
7688c96dcc1SMarcel Moolenaar 	h = dlopen(loader, RTLD_LOCAL);
7698c96dcc1SMarcel Moolenaar 	if (!h) {
7708c96dcc1SMarcel Moolenaar 		printf("%s\n", dlerror());
7718c96dcc1SMarcel Moolenaar 		free(loader);
7728c96dcc1SMarcel Moolenaar 		return (1);
7738c96dcc1SMarcel Moolenaar 	}
7748c96dcc1SMarcel Moolenaar 	func = dlsym(h, "loader_main");
7758c96dcc1SMarcel Moolenaar 	if (!func) {
7768c96dcc1SMarcel Moolenaar 		printf("%s\n", dlerror());
7778c96dcc1SMarcel Moolenaar 		free(loader);
7788c96dcc1SMarcel Moolenaar 		return (1);
7798c96dcc1SMarcel Moolenaar 	}
7808c96dcc1SMarcel Moolenaar 
7816380102cSPeter Grehan 	tcgetattr(consout_fd, &term);
782c487da1eSNeel Natu 	oldterm = term;
7836380102cSPeter Grehan 	cfmakeraw(&term);
7846380102cSPeter Grehan 	term.c_cflag |= CLOCAL;
7856380102cSPeter Grehan 
7866380102cSPeter Grehan 	tcsetattr(consout_fd, TCSAFLUSH, &term);
7876380102cSPeter Grehan 
788b6afa84bSNeel Natu 	addenv("smbios.bios.vendor=BHYVE");
789b6afa84bSNeel Natu 	addenv("boot_serial=1");
790b6afa84bSNeel Natu 
79148f337b8SMarcel Moolenaar 	func(&cb, NULL, USERBOOT_VERSION_4, ndisks);
7928c96dcc1SMarcel Moolenaar 
7938c96dcc1SMarcel Moolenaar 	free(loader);
7948c96dcc1SMarcel Moolenaar 	return (0);
795c487da1eSNeel Natu }
796