1223828Sneel/*-
2223828Sneel * Copyright (c) 2011 NetApp, Inc.
3223828Sneel * All rights reserved.
4223828Sneel *
5223828Sneel * Redistribution and use in source and binary forms, with or without
6223828Sneel * modification, are permitted provided that the following conditions
7223828Sneel * are met:
8223828Sneel * 1. Redistributions of source code must retain the above copyright
9223828Sneel *    notice, this list of conditions and the following disclaimer.
10223828Sneel * 2. Redistributions in binary form must reproduce the above copyright
11223828Sneel *    notice, this list of conditions and the following disclaimer in the
12223828Sneel *    documentation and/or other materials provided with the distribution.
13223828Sneel *
14223828Sneel * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
15223828Sneel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16223828Sneel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17223828Sneel * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
18223828Sneel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19223828Sneel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20223828Sneel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21223828Sneel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22223828Sneel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23223828Sneel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24223828Sneel * SUCH DAMAGE.
25223828Sneel *
26223828Sneel * $FreeBSD: stable/10/usr.sbin/bhyveload/bhyveload.c 323739 2017-09-19 08:19:20Z avg $
27223828Sneel */
28223828Sneel
29223828Sneel/*-
30223828Sneel * Copyright (c) 2011 Google, Inc.
31223828Sneel * All rights reserved.
32223828Sneel *
33223828Sneel * Redistribution and use in source and binary forms, with or without
34223828Sneel * modification, are permitted provided that the following conditions
35223828Sneel * are met:
36223828Sneel * 1. Redistributions of source code must retain the above copyright
37223828Sneel *    notice, this list of conditions and the following disclaimer.
38223828Sneel * 2. Redistributions in binary form must reproduce the above copyright
39223828Sneel *    notice, this list of conditions and the following disclaimer in the
40223828Sneel *    documentation and/or other materials provided with the distribution.
41223828Sneel *
42223828Sneel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
43223828Sneel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
44223828Sneel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
45223828Sneel * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
46223828Sneel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
47223828Sneel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
48223828Sneel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
49223828Sneel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
50223828Sneel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
51223828Sneel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
52223828Sneel * SUCH DAMAGE.
53223828Sneel *
54223828Sneel * $FreeBSD: stable/10/usr.sbin/bhyveload/bhyveload.c 323739 2017-09-19 08:19:20Z avg $
55223828Sneel */
56223828Sneel
57223828Sneel#include <sys/cdefs.h>
58223828Sneel__FBSDID("$FreeBSD: stable/10/usr.sbin/bhyveload/bhyveload.c 323739 2017-09-19 08:19:20Z avg $");
59223828Sneel
60223828Sneel#include <sys/ioctl.h>
61223828Sneel#include <sys/stat.h>
62242882Sneel#include <sys/disk.h>
63259301Sgrehan#include <sys/queue.h>
64223828Sneel
65223828Sneel#include <machine/specialreg.h>
66223828Sneel#include <machine/vmm.h>
67223828Sneel
68223828Sneel#include <dirent.h>
69223828Sneel#include <dlfcn.h>
70223828Sneel#include <errno.h>
71256176Sneel#include <err.h>
72223828Sneel#include <fcntl.h>
73223828Sneel#include <getopt.h>
74259301Sgrehan#include <libgen.h>
75223828Sneel#include <limits.h>
76223828Sneel#include <stdio.h>
77223828Sneel#include <stdlib.h>
78223828Sneel#include <string.h>
79256176Sneel#include <sysexits.h>
80223828Sneel#include <termios.h>
81223828Sneel#include <unistd.h>
82223828Sneel
83223828Sneel#include <vmmapi.h>
84223828Sneel
85223828Sneel#include "userboot.h"
86223828Sneel
87223828Sneel#define	MB	(1024 * 1024UL)
88223828Sneel#define	GB	(1024 * 1024 * 1024UL)
89223828Sneel#define	BSP	0
90223828Sneel
91268932Sjhb#define	NDISKS	32
92268932Sjhb
93259301Sgrehanstatic char *host_base;
94223828Sneelstatic struct termios term, oldterm;
95268932Sjhbstatic int disk_fd[NDISKS];
96268932Sjhbstatic int ndisks;
97259301Sgrehanstatic int consin_fd, consout_fd;
98223828Sneel
99248477Sneelstatic char *vmname, *progname;
100223828Sneelstatic struct vmctx *ctx;
101223828Sneel
102223828Sneelstatic uint64_t gdtbase, cr3, rsp;
103223828Sneel
104223828Sneelstatic void cb_exit(void *arg, int v);
105223828Sneel
106223828Sneel/*
107223828Sneel * Console i/o callbacks
108223828Sneel */
109223828Sneel
110223828Sneelstatic void
111223828Sneelcb_putc(void *arg, int ch)
112223828Sneel{
113223828Sneel	char c = ch;
114223828Sneel
115259301Sgrehan	(void) write(consout_fd, &c, 1);
116223828Sneel}
117223828Sneel
118223828Sneelstatic int
119223828Sneelcb_getc(void *arg)
120223828Sneel{
121223828Sneel	char c;
122223828Sneel
123259301Sgrehan	if (read(consin_fd, &c, 1) == 1)
124223828Sneel		return (c);
125223828Sneel	return (-1);
126223828Sneel}
127223828Sneel
128223828Sneelstatic int
129223828Sneelcb_poll(void *arg)
130223828Sneel{
131223828Sneel	int n;
132223828Sneel
133259301Sgrehan	if (ioctl(consin_fd, FIONREAD, &n) >= 0)
134223828Sneel		return (n > 0);
135223828Sneel	return (0);
136223828Sneel}
137223828Sneel
138223828Sneel/*
139223828Sneel * Host filesystem i/o callbacks
140223828Sneel */
141223828Sneel
142223828Sneelstruct cb_file {
143223828Sneel	int cf_isdir;
144223828Sneel	size_t cf_size;
145223828Sneel	struct stat cf_stat;
146223828Sneel	union {
147223828Sneel		int fd;
148223828Sneel		DIR *dir;
149223828Sneel	} cf_u;
150223828Sneel};
151223828Sneel
152223828Sneelstatic int
153223828Sneelcb_open(void *arg, const char *filename, void **hp)
154223828Sneel{
155223828Sneel	struct stat st;
156223828Sneel	struct cb_file *cf;
157223828Sneel	char path[PATH_MAX];
158223828Sneel
159223828Sneel	if (!host_base)
160223828Sneel		return (ENOENT);
161223828Sneel
162223828Sneel	strlcpy(path, host_base, PATH_MAX);
163223828Sneel	if (path[strlen(path) - 1] == '/')
164223828Sneel		path[strlen(path) - 1] = 0;
165223828Sneel	strlcat(path, filename, PATH_MAX);
166223828Sneel	cf = malloc(sizeof(struct cb_file));
167223828Sneel	if (stat(path, &cf->cf_stat) < 0) {
168223828Sneel		free(cf);
169223828Sneel		return (errno);
170223828Sneel	}
171223828Sneel
172223828Sneel	cf->cf_size = st.st_size;
173223828Sneel	if (S_ISDIR(cf->cf_stat.st_mode)) {
174223828Sneel		cf->cf_isdir = 1;
175223828Sneel		cf->cf_u.dir = opendir(path);
176223828Sneel		if (!cf->cf_u.dir)
177223828Sneel			goto out;
178223828Sneel		*hp = cf;
179223828Sneel		return (0);
180223828Sneel	}
181223828Sneel	if (S_ISREG(cf->cf_stat.st_mode)) {
182223828Sneel		cf->cf_isdir = 0;
183223828Sneel		cf->cf_u.fd = open(path, O_RDONLY);
184223828Sneel		if (cf->cf_u.fd < 0)
185223828Sneel			goto out;
186223828Sneel		*hp = cf;
187223828Sneel		return (0);
188223828Sneel	}
189223828Sneel
190223828Sneelout:
191223828Sneel	free(cf);
192223828Sneel	return (EINVAL);
193223828Sneel}
194223828Sneel
195223828Sneelstatic int
196223828Sneelcb_close(void *arg, void *h)
197223828Sneel{
198223828Sneel	struct cb_file *cf = h;
199223828Sneel
200223828Sneel	if (cf->cf_isdir)
201223828Sneel		closedir(cf->cf_u.dir);
202223828Sneel	else
203223828Sneel		close(cf->cf_u.fd);
204223828Sneel	free(cf);
205223828Sneel
206223828Sneel	return (0);
207223828Sneel}
208223828Sneel
209223828Sneelstatic int
210223828Sneelcb_isdir(void *arg, void *h)
211223828Sneel{
212223828Sneel	struct cb_file *cf = h;
213223828Sneel
214223828Sneel	return (cf->cf_isdir);
215223828Sneel}
216223828Sneel
217223828Sneelstatic int
218223828Sneelcb_read(void *arg, void *h, void *buf, size_t size, size_t *resid)
219223828Sneel{
220223828Sneel	struct cb_file *cf = h;
221223828Sneel	ssize_t sz;
222223828Sneel
223223828Sneel	if (cf->cf_isdir)
224223828Sneel		return (EINVAL);
225223828Sneel	sz = read(cf->cf_u.fd, buf, size);
226223828Sneel	if (sz < 0)
227223828Sneel		return (EINVAL);
228223828Sneel	*resid = size - sz;
229223828Sneel	return (0);
230223828Sneel}
231223828Sneel
232223828Sneelstatic int
233223828Sneelcb_readdir(void *arg, void *h, uint32_t *fileno_return, uint8_t *type_return,
234223828Sneel	   size_t *namelen_return, char *name)
235223828Sneel{
236223828Sneel	struct cb_file *cf = h;
237223828Sneel	struct dirent *dp;
238223828Sneel
239223828Sneel	if (!cf->cf_isdir)
240223828Sneel		return (EINVAL);
241223828Sneel
242223828Sneel	dp = readdir(cf->cf_u.dir);
243223828Sneel	if (!dp)
244223828Sneel		return (ENOENT);
245223828Sneel
246223828Sneel	/*
247223828Sneel	 * Note: d_namlen is in the range 0..255 and therefore less
248223828Sneel	 * than PATH_MAX so we don't need to test before copying.
249223828Sneel	 */
250223828Sneel	*fileno_return = dp->d_fileno;
251223828Sneel	*type_return = dp->d_type;
252223828Sneel	*namelen_return = dp->d_namlen;
253223828Sneel	memcpy(name, dp->d_name, dp->d_namlen);
254223828Sneel	name[dp->d_namlen] = 0;
255223828Sneel
256223828Sneel	return (0);
257223828Sneel}
258223828Sneel
259223828Sneelstatic int
260223828Sneelcb_seek(void *arg, void *h, uint64_t offset, int whence)
261223828Sneel{
262223828Sneel	struct cb_file *cf = h;
263223828Sneel
264223828Sneel	if (cf->cf_isdir)
265223828Sneel		return (EINVAL);
266223828Sneel	if (lseek(cf->cf_u.fd, offset, whence) < 0)
267223828Sneel		return (errno);
268223828Sneel	return (0);
269223828Sneel}
270223828Sneel
271223828Sneelstatic int
272223828Sneelcb_stat(void *arg, void *h, int *mode, int *uid, int *gid, uint64_t *size)
273223828Sneel{
274223828Sneel	struct cb_file *cf = h;
275223828Sneel
276223828Sneel	*mode = cf->cf_stat.st_mode;
277223828Sneel	*uid = cf->cf_stat.st_uid;
278223828Sneel	*gid = cf->cf_stat.st_gid;
279223828Sneel	*size = cf->cf_stat.st_size;
280223828Sneel	return (0);
281223828Sneel}
282223828Sneel
283223828Sneel/*
284223828Sneel * Disk image i/o callbacks
285223828Sneel */
286223828Sneel
287223828Sneelstatic int
288223828Sneelcb_diskread(void *arg, int unit, uint64_t from, void *to, size_t size,
289223828Sneel	    size_t *resid)
290223828Sneel{
291223828Sneel	ssize_t n;
292223828Sneel
293268932Sjhb	if (unit < 0 || unit >= ndisks )
294223828Sneel		return (EIO);
295268932Sjhb	n = pread(disk_fd[unit], to, size, from);
296223828Sneel	if (n < 0)
297223828Sneel		return (errno);
298223828Sneel	*resid = size - n;
299223828Sneel	return (0);
300223828Sneel}
301223828Sneel
302242882Sneelstatic int
303242882Sneelcb_diskioctl(void *arg, int unit, u_long cmd, void *data)
304242882Sneel{
305242882Sneel	struct stat sb;
306242882Sneel
307268932Sjhb	if (unit < 0 || unit >= ndisks)
308242882Sneel		return (EBADF);
309242882Sneel
310242882Sneel	switch (cmd) {
311242882Sneel	case DIOCGSECTORSIZE:
312242882Sneel		*(u_int *)data = 512;
313242882Sneel		break;
314242882Sneel	case DIOCGMEDIASIZE:
315323739Savg		if (fstat(disk_fd[unit], &sb) != 0)
316242882Sneel			return (ENOTTY);
317323739Savg		if (S_ISCHR(sb.st_mode) &&
318323739Savg		    ioctl(disk_fd[unit], DIOCGMEDIASIZE, &sb.st_size) != 0)
319323739Savg				return (ENOTTY);
320323739Savg		*(off_t *)data = sb.st_size;
321242882Sneel		break;
322242882Sneel	default:
323242882Sneel		return (ENOTTY);
324242882Sneel	}
325242882Sneel
326242882Sneel	return (0);
327242882Sneel}
328242882Sneel
329223828Sneel/*
330223828Sneel * Guest virtual machine i/o callbacks
331223828Sneel */
332223828Sneelstatic int
333223828Sneelcb_copyin(void *arg, const void *from, uint64_t to, size_t size)
334223828Sneel{
335248477Sneel	char *ptr;
336223828Sneel
337223828Sneel	to &= 0x7fffffff;
338248477Sneel
339248477Sneel	ptr = vm_map_gpa(ctx, to, size);
340248477Sneel	if (ptr == NULL)
341223828Sneel		return (EFAULT);
342223828Sneel
343248477Sneel	memcpy(ptr, from, size);
344223828Sneel	return (0);
345223828Sneel}
346223828Sneel
347223828Sneelstatic int
348223828Sneelcb_copyout(void *arg, uint64_t from, void *to, size_t size)
349223828Sneel{
350248477Sneel	char *ptr;
351223828Sneel
352223828Sneel	from &= 0x7fffffff;
353248477Sneel
354248477Sneel	ptr = vm_map_gpa(ctx, from, size);
355248477Sneel	if (ptr == NULL)
356223828Sneel		return (EFAULT);
357223828Sneel
358248477Sneel	memcpy(to, ptr, size);
359223828Sneel	return (0);
360223828Sneel}
361223828Sneel
362223828Sneelstatic void
363223828Sneelcb_setreg(void *arg, int r, uint64_t v)
364223828Sneel{
365223828Sneel	int error;
366223828Sneel	enum vm_reg_name vmreg;
367223828Sneel
368223828Sneel	vmreg = VM_REG_LAST;
369223828Sneel
370223828Sneel	switch (r) {
371223828Sneel	case 4:
372223828Sneel		vmreg = VM_REG_GUEST_RSP;
373223828Sneel		rsp = v;
374223828Sneel		break;
375223828Sneel	default:
376223828Sneel		break;
377223828Sneel	}
378223828Sneel
379223828Sneel	if (vmreg == VM_REG_LAST) {
380223828Sneel		printf("test_setreg(%d): not implemented\n", r);
381223828Sneel		cb_exit(NULL, USERBOOT_EXIT_QUIT);
382223828Sneel	}
383223828Sneel
384223828Sneel	error = vm_set_register(ctx, BSP, vmreg, v);
385223828Sneel	if (error) {
386223828Sneel		perror("vm_set_register");
387223828Sneel		cb_exit(NULL, USERBOOT_EXIT_QUIT);
388223828Sneel	}
389223828Sneel}
390223828Sneel
391223828Sneelstatic void
392223828Sneelcb_setmsr(void *arg, int r, uint64_t v)
393223828Sneel{
394223828Sneel	int error;
395223828Sneel	enum vm_reg_name vmreg;
396223828Sneel
397223828Sneel	vmreg = VM_REG_LAST;
398223828Sneel
399223828Sneel	switch (r) {
400223828Sneel	case MSR_EFER:
401223828Sneel		vmreg = VM_REG_GUEST_EFER;
402223828Sneel		break;
403223828Sneel	default:
404223828Sneel		break;
405223828Sneel	}
406223828Sneel
407223828Sneel	if (vmreg == VM_REG_LAST) {
408223828Sneel		printf("test_setmsr(%d): not implemented\n", r);
409223828Sneel		cb_exit(NULL, USERBOOT_EXIT_QUIT);
410223828Sneel	}
411223828Sneel
412223828Sneel	error = vm_set_register(ctx, BSP, vmreg, v);
413223828Sneel	if (error) {
414223828Sneel		perror("vm_set_msr");
415223828Sneel		cb_exit(NULL, USERBOOT_EXIT_QUIT);
416223828Sneel	}
417223828Sneel}
418223828Sneel
419223828Sneelstatic void
420223828Sneelcb_setcr(void *arg, int r, uint64_t v)
421223828Sneel{
422223828Sneel	int error;
423223828Sneel	enum vm_reg_name vmreg;
424223828Sneel
425223828Sneel	vmreg = VM_REG_LAST;
426223828Sneel
427223828Sneel	switch (r) {
428223828Sneel	case 0:
429223828Sneel		vmreg = VM_REG_GUEST_CR0;
430223828Sneel		break;
431223828Sneel	case 3:
432223828Sneel		vmreg = VM_REG_GUEST_CR3;
433223828Sneel		cr3 = v;
434223828Sneel		break;
435223828Sneel	case 4:
436223828Sneel		vmreg = VM_REG_GUEST_CR4;
437223828Sneel		break;
438223828Sneel	default:
439223828Sneel		break;
440223828Sneel	}
441223828Sneel
442223828Sneel	if (vmreg == VM_REG_LAST) {
443223828Sneel		printf("test_setcr(%d): not implemented\n", r);
444223828Sneel		cb_exit(NULL, USERBOOT_EXIT_QUIT);
445223828Sneel	}
446223828Sneel
447223828Sneel	error = vm_set_register(ctx, BSP, vmreg, v);
448223828Sneel	if (error) {
449223828Sneel		perror("vm_set_cr");
450223828Sneel		cb_exit(NULL, USERBOOT_EXIT_QUIT);
451223828Sneel	}
452223828Sneel}
453223828Sneel
454223828Sneelstatic void
455223828Sneelcb_setgdt(void *arg, uint64_t base, size_t size)
456223828Sneel{
457223828Sneel	int error;
458223828Sneel
459223828Sneel	error = vm_set_desc(ctx, BSP, VM_REG_GUEST_GDTR, base, size - 1, 0);
460223828Sneel	if (error != 0) {
461223828Sneel		perror("vm_set_desc(gdt)");
462223828Sneel		cb_exit(NULL, USERBOOT_EXIT_QUIT);
463223828Sneel	}
464223828Sneel
465223828Sneel	gdtbase = base;
466223828Sneel}
467223828Sneel
468223828Sneelstatic void
469223828Sneelcb_exec(void *arg, uint64_t rip)
470223828Sneel{
471223828Sneel	int error;
472223828Sneel
473267399Sjhb	if (cr3 == 0)
474267399Sjhb		error = vm_setup_freebsd_registers_i386(ctx, BSP, rip, gdtbase,
475267399Sjhb		    rsp);
476267399Sjhb	else
477267399Sjhb		error = vm_setup_freebsd_registers(ctx, BSP, rip, cr3, gdtbase,
478267399Sjhb		    rsp);
479223828Sneel	if (error) {
480223828Sneel		perror("vm_setup_freebsd_registers");
481223828Sneel		cb_exit(NULL, USERBOOT_EXIT_QUIT);
482223828Sneel	}
483223828Sneel
484223828Sneel	cb_exit(NULL, 0);
485223828Sneel}
486223828Sneel
487223828Sneel/*
488223828Sneel * Misc
489223828Sneel */
490223828Sneel
491223828Sneelstatic void
492223828Sneelcb_delay(void *arg, int usec)
493223828Sneel{
494223828Sneel
495223828Sneel	usleep(usec);
496223828Sneel}
497223828Sneel
498223828Sneelstatic void
499223828Sneelcb_exit(void *arg, int v)
500223828Sneel{
501223828Sneel
502259301Sgrehan	tcsetattr(consout_fd, TCSAFLUSH, &oldterm);
503223828Sneel	exit(v);
504223828Sneel}
505223828Sneel
506223828Sneelstatic void
507223828Sneelcb_getmem(void *arg, uint64_t *ret_lowmem, uint64_t *ret_highmem)
508223828Sneel{
509223828Sneel
510270074Sgrehan	*ret_lowmem = vm_get_lowmem_size(ctx);
511270074Sgrehan	*ret_highmem = vm_get_highmem_size(ctx);
512223828Sneel}
513223828Sneel
514259301Sgrehanstruct env {
515259301Sgrehan	const char *str;	/* name=value */
516259301Sgrehan	SLIST_ENTRY(env) next;
517259301Sgrehan};
518259301Sgrehan
519259301Sgrehanstatic SLIST_HEAD(envhead, env) envhead;
520259301Sgrehan
521259301Sgrehanstatic void
522259301Sgrehanaddenv(const char *str)
523259301Sgrehan{
524259301Sgrehan	struct env *env;
525259301Sgrehan
526259301Sgrehan	env = malloc(sizeof(struct env));
527259301Sgrehan	env->str = str;
528259301Sgrehan	SLIST_INSERT_HEAD(&envhead, env, next);
529259301Sgrehan}
530259301Sgrehan
531242676Sneelstatic const char *
532242676Sneelcb_getenv(void *arg, int num)
533242676Sneel{
534259301Sgrehan	int i;
535259301Sgrehan	struct env *env;
536242676Sneel
537259301Sgrehan	i = 0;
538259301Sgrehan	SLIST_FOREACH(env, &envhead, next) {
539259301Sgrehan		if (i == num)
540259301Sgrehan			return (env->str);
541259301Sgrehan		i++;
542259301Sgrehan	}
543242676Sneel
544259301Sgrehan	return (NULL);
545242676Sneel}
546242676Sneel
547242882Sneelstatic struct loader_callbacks cb = {
548223828Sneel	.getc = cb_getc,
549223828Sneel	.putc = cb_putc,
550223828Sneel	.poll = cb_poll,
551223828Sneel
552223828Sneel	.open = cb_open,
553223828Sneel	.close = cb_close,
554223828Sneel	.isdir = cb_isdir,
555223828Sneel	.read = cb_read,
556223828Sneel	.readdir = cb_readdir,
557223828Sneel	.seek = cb_seek,
558223828Sneel	.stat = cb_stat,
559223828Sneel
560223828Sneel	.diskread = cb_diskread,
561242882Sneel	.diskioctl = cb_diskioctl,
562223828Sneel
563223828Sneel	.copyin = cb_copyin,
564223828Sneel	.copyout = cb_copyout,
565223828Sneel	.setreg = cb_setreg,
566223828Sneel	.setmsr = cb_setmsr,
567223828Sneel	.setcr = cb_setcr,
568223828Sneel	.setgdt = cb_setgdt,
569223828Sneel	.exec = cb_exec,
570223828Sneel
571223828Sneel	.delay = cb_delay,
572223828Sneel	.exit = cb_exit,
573223828Sneel	.getmem = cb_getmem,
574242676Sneel
575242676Sneel	.getenv = cb_getenv,
576223828Sneel};
577223828Sneel
578259301Sgrehanstatic int
579259301Sgrehanaltcons_open(char *path)
580259301Sgrehan{
581259301Sgrehan	struct stat sb;
582259301Sgrehan	int err;
583259301Sgrehan	int fd;
584259301Sgrehan
585259301Sgrehan	/*
586259301Sgrehan	 * Allow stdio to be passed in so that the same string
587259301Sgrehan	 * can be used for the bhyveload console and bhyve com-port
588259301Sgrehan	 * parameters
589259301Sgrehan	 */
590259301Sgrehan	if (!strcmp(path, "stdio"))
591259301Sgrehan		return (0);
592259301Sgrehan
593259301Sgrehan	err = stat(path, &sb);
594259301Sgrehan	if (err == 0) {
595259301Sgrehan		if (!S_ISCHR(sb.st_mode))
596259301Sgrehan			err = ENOTSUP;
597259301Sgrehan		else {
598259301Sgrehan			fd = open(path, O_RDWR | O_NONBLOCK);
599259301Sgrehan			if (fd < 0)
600259301Sgrehan				err = errno;
601259301Sgrehan			else
602259301Sgrehan				consin_fd = consout_fd = fd;
603259301Sgrehan		}
604259301Sgrehan	}
605259301Sgrehan
606259301Sgrehan	return (err);
607259301Sgrehan}
608259301Sgrehan
609268932Sjhbstatic int
610268932Sjhbdisk_open(char *path)
611268932Sjhb{
612268932Sjhb	int err, fd;
613268932Sjhb
614284900Sneel	if (ndisks >= NDISKS)
615268932Sjhb		return (ERANGE);
616268932Sjhb
617268932Sjhb	err = 0;
618268932Sjhb	fd = open(path, O_RDONLY);
619268932Sjhb
620268932Sjhb	if (fd > 0) {
621268932Sjhb		disk_fd[ndisks] = fd;
622268932Sjhb		ndisks++;
623268932Sjhb	} else
624268932Sjhb		err = errno;
625268932Sjhb
626268932Sjhb	return (err);
627268932Sjhb}
628268932Sjhb
629223828Sneelstatic void
630223828Sneelusage(void)
631223828Sneel{
632223828Sneel
633248477Sneel	fprintf(stderr,
634295124Sgrehan	    "usage: %s [-S][-c <console-device>] [-d <disk-path>] [-e <name=value>]\n"
635270159Sgrehan	    "       %*s [-h <host-path>] [-m mem-size] <vmname>\n",
636259301Sgrehan	    progname,
637259301Sgrehan	    (int)strlen(progname), "");
638223828Sneel	exit(1);
639223828Sneel}
640223828Sneel
641223828Sneelint
642223828Sneelmain(int argc, char** argv)
643223828Sneel{
644295124Sgrehan	char *loader;
645223828Sneel	void *h;
646242882Sneel	void (*func)(struct loader_callbacks *, void *, int, int);
647248477Sneel	uint64_t mem_size;
648295124Sgrehan	int opt, error, need_reinit, memflags;
649223828Sneel
650259301Sgrehan	progname = basename(argv[0]);
651223828Sneel
652295124Sgrehan	loader = NULL;
653295124Sgrehan
654295124Sgrehan	memflags = 0;
655248477Sneel	mem_size = 256 * MB;
656223828Sneel
657259301Sgrehan	consin_fd = STDIN_FILENO;
658259301Sgrehan	consout_fd = STDOUT_FILENO;
659259301Sgrehan
660295124Sgrehan	while ((opt = getopt(argc, argv, "Sc:d:e:h:l:m:")) != -1) {
661223828Sneel		switch (opt) {
662259301Sgrehan		case 'c':
663259301Sgrehan			error = altcons_open(optarg);
664259301Sgrehan			if (error != 0)
665259301Sgrehan				errx(EX_USAGE, "Could not open '%s'", optarg);
666259301Sgrehan			break;
667268932Sjhb
668223828Sneel		case 'd':
669268932Sjhb			error = disk_open(optarg);
670268932Sjhb			if (error != 0)
671268932Sjhb				errx(EX_USAGE, "Could not open '%s'", optarg);
672223828Sneel			break;
673223828Sneel
674259301Sgrehan		case 'e':
675259301Sgrehan			addenv(optarg);
676259301Sgrehan			break;
677259301Sgrehan
678223828Sneel		case 'h':
679223828Sneel			host_base = optarg;
680223828Sneel			break;
681223828Sneel
682295124Sgrehan		case 'l':
683295124Sgrehan			if (loader != NULL)
684295124Sgrehan				errx(EX_USAGE, "-l can only be given once");
685295124Sgrehan			loader = strdup(optarg);
686295124Sgrehan			if (loader == NULL)
687295124Sgrehan				err(EX_OSERR, "malloc");
688295124Sgrehan			break;
689295124Sgrehan
690223828Sneel		case 'm':
691256176Sneel			error = vm_parse_memsize(optarg, &mem_size);
692256176Sneel			if (error != 0)
693256176Sneel				errx(EX_USAGE, "Invalid memsize '%s'", optarg);
694223828Sneel			break;
695295124Sgrehan		case 'S':
696295124Sgrehan			memflags |= VM_MEM_F_WIRED;
697295124Sgrehan			break;
698223828Sneel		case '?':
699223828Sneel			usage();
700223828Sneel		}
701223828Sneel	}
702223828Sneel
703223828Sneel	argc -= optind;
704223828Sneel	argv += optind;
705223828Sneel
706223828Sneel	if (argc != 1)
707223828Sneel		usage();
708223828Sneel
709223828Sneel	vmname = argv[0];
710223828Sneel
711270071Sgrehan	need_reinit = 0;
712223828Sneel	error = vm_create(vmname);
713270071Sgrehan	if (error) {
714270071Sgrehan		if (errno != EEXIST) {
715270071Sgrehan			perror("vm_create");
716270071Sgrehan			exit(1);
717270071Sgrehan		}
718270071Sgrehan		need_reinit = 1;
719223828Sneel	}
720223828Sneel
721223828Sneel	ctx = vm_open(vmname);
722223828Sneel	if (ctx == NULL) {
723223828Sneel		perror("vm_open");
724223828Sneel		exit(1);
725223828Sneel	}
726223828Sneel
727270071Sgrehan	if (need_reinit) {
728270071Sgrehan		error = vm_reinit(ctx);
729270071Sgrehan		if (error) {
730270071Sgrehan			perror("vm_reinit");
731270071Sgrehan			exit(1);
732270071Sgrehan		}
733270071Sgrehan	}
734270071Sgrehan
735295124Sgrehan	vm_set_memflags(ctx, memflags);
736248477Sneel	error = vm_setup_memory(ctx, mem_size, VM_MMAP_ALL);
737223828Sneel	if (error) {
738248477Sneel		perror("vm_setup_memory");
739223828Sneel		exit(1);
740223828Sneel	}
741223828Sneel
742295124Sgrehan	if (loader == NULL) {
743295124Sgrehan		loader = strdup("/boot/userboot.so");
744295124Sgrehan		if (loader == NULL)
745295124Sgrehan			err(EX_OSERR, "malloc");
746295124Sgrehan	}
747295124Sgrehan	h = dlopen(loader, RTLD_LOCAL);
748223828Sneel	if (!h) {
749223828Sneel		printf("%s\n", dlerror());
750295124Sgrehan		free(loader);
751223828Sneel		return (1);
752223828Sneel	}
753223828Sneel	func = dlsym(h, "loader_main");
754223828Sneel	if (!func) {
755223828Sneel		printf("%s\n", dlerror());
756295124Sgrehan		free(loader);
757223828Sneel		return (1);
758223828Sneel	}
759223828Sneel
760295124Sgrehan	tcgetattr(consout_fd, &term);
761295124Sgrehan	oldterm = term;
762295124Sgrehan	cfmakeraw(&term);
763295124Sgrehan	term.c_cflag |= CLOCAL;
764295124Sgrehan
765295124Sgrehan	tcsetattr(consout_fd, TCSAFLUSH, &term);
766295124Sgrehan
767259301Sgrehan	addenv("smbios.bios.vendor=BHYVE");
768259301Sgrehan	addenv("boot_serial=1");
769259301Sgrehan
770268932Sjhb	func(&cb, NULL, USERBOOT_VERSION_3, ndisks);
771295124Sgrehan
772295124Sgrehan	free(loader);
773295124Sgrehan	return (0);
774223828Sneel}
775