bhyveload.c revision 267399
1156230Smux/*-
2156230Smux * Copyright (c) 2011 NetApp, Inc.
3156230Smux * All rights reserved.
4156230Smux *
5156230Smux * Redistribution and use in source and binary forms, with or without
6156230Smux * modification, are permitted provided that the following conditions
7156230Smux * are met:
8156230Smux * 1. Redistributions of source code must retain the above copyright
9156230Smux *    notice, this list of conditions and the following disclaimer.
10156230Smux * 2. Redistributions in binary form must reproduce the above copyright
11156230Smux *    notice, this list of conditions and the following disclaimer in the
12156230Smux *    documentation and/or other materials provided with the distribution.
13156230Smux *
14156230Smux * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
15156230Smux * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16156230Smux * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17156230Smux * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
18156230Smux * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19156230Smux * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20156230Smux * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21156230Smux * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22156230Smux * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23156230Smux * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24156230Smux * SUCH DAMAGE.
25156230Smux *
26156230Smux * $FreeBSD: stable/10/usr.sbin/bhyveload/bhyveload.c 267399 2014-06-12 15:20:59Z jhb $
27156230Smux */
28156230Smux
29156230Smux/*-
30156230Smux * Copyright (c) 2011 Google, Inc.
31156230Smux * All rights reserved.
32156230Smux *
33156230Smux * Redistribution and use in source and binary forms, with or without
34156230Smux * modification, are permitted provided that the following conditions
35156230Smux * are met:
36156701Smux * 1. Redistributions of source code must retain the above copyright
37156230Smux *    notice, this list of conditions and the following disclaimer.
38156230Smux * 2. Redistributions in binary form must reproduce the above copyright
39156230Smux *    notice, this list of conditions and the following disclaimer in the
40156230Smux *    documentation and/or other materials provided with the distribution.
41156230Smux *
42156230Smux * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
43156230Smux * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
44156230Smux * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
45156230Smux * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
46156230Smux * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
47156230Smux * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
48156230Smux * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
49156230Smux * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
50156230Smux * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
51156230Smux * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
52156230Smux * SUCH DAMAGE.
53156230Smux *
54156230Smux * $FreeBSD: stable/10/usr.sbin/bhyveload/bhyveload.c 267399 2014-06-12 15:20:59Z jhb $
55156230Smux */
56156230Smux
57156230Smux#include <sys/cdefs.h>
58156230Smux__FBSDID("$FreeBSD: stable/10/usr.sbin/bhyveload/bhyveload.c 267399 2014-06-12 15:20:59Z jhb $");
59156230Smux
60156230Smux#include <sys/ioctl.h>
61156230Smux#include <sys/stat.h>
62156230Smux#include <sys/disk.h>
63156230Smux#include <sys/queue.h>
64156230Smux
65156230Smux#include <machine/specialreg.h>
66156230Smux#include <machine/vmm.h>
67156701Smux
68156701Smux#include <dirent.h>
69156701Smux#include <dlfcn.h>
70156701Smux#include <errno.h>
71156701Smux#include <err.h>
72156701Smux#include <fcntl.h>
73156701Smux#include <getopt.h>
74156701Smux#include <libgen.h>
75156701Smux#include <limits.h>
76156701Smux#include <stdio.h>
77156701Smux#include <stdlib.h>
78156701Smux#include <string.h>
79156701Smux#include <sysexits.h>
80156701Smux#include <termios.h>
81156701Smux#include <unistd.h>
82156701Smux
83156701Smux#include <vmmapi.h>
84156701Smux
85156230Smux#include "userboot.h"
86156230Smux
87156230Smux#define	MB	(1024 * 1024UL)
88156230Smux#define	GB	(1024 * 1024 * 1024UL)
89156230Smux#define	BSP	0
90156230Smux
91156230Smuxstatic char *host_base;
92156230Smuxstatic struct termios term, oldterm;
93156230Smuxstatic int disk_fd = -1;
94156230Smuxstatic int consin_fd, consout_fd;
95156230Smux
96156230Smuxstatic char *vmname, *progname;
97156230Smuxstatic struct vmctx *ctx;
98156230Smux
99156230Smuxstatic uint64_t gdtbase, cr3, rsp;
100156230Smux
101156230Smuxstatic void cb_exit(void *arg, int v);
102156230Smux
103156230Smux/*
104156230Smux * Console i/o callbacks
105156230Smux */
106156230Smux
107156230Smuxstatic void
108156230Smuxcb_putc(void *arg, int ch)
109156230Smux{
110156230Smux	char c = ch;
111156230Smux
112156230Smux	(void) write(consout_fd, &c, 1);
113156230Smux}
114156230Smux
115156230Smuxstatic int
116156230Smuxcb_getc(void *arg)
117156230Smux{
118156230Smux	char c;
119156230Smux
120156230Smux	if (read(consin_fd, &c, 1) == 1)
121156230Smux		return (c);
122156230Smux	return (-1);
123156230Smux}
124156230Smux
125156230Smuxstatic int
126156230Smuxcb_poll(void *arg)
127156230Smux{
128156230Smux	int n;
129156230Smux
130156230Smux	if (ioctl(consin_fd, FIONREAD, &n) >= 0)
131156230Smux		return (n > 0);
132156230Smux	return (0);
133156230Smux}
134156230Smux
135156230Smux/*
136156230Smux * Host filesystem i/o callbacks
137156230Smux */
138156230Smux
139156230Smuxstruct cb_file {
140156230Smux	int cf_isdir;
141156230Smux	size_t cf_size;
142156230Smux	struct stat cf_stat;
143156230Smux	union {
144156230Smux		int fd;
145156230Smux		DIR *dir;
146156230Smux	} cf_u;
147156230Smux};
148156230Smux
149156230Smuxstatic int
150156230Smuxcb_open(void *arg, const char *filename, void **hp)
151156230Smux{
152156230Smux	struct stat st;
153156230Smux	struct cb_file *cf;
154156230Smux	char path[PATH_MAX];
155156230Smux
156156230Smux	if (!host_base)
157156230Smux		return (ENOENT);
158156230Smux
159156230Smux	strlcpy(path, host_base, PATH_MAX);
160156230Smux	if (path[strlen(path) - 1] == '/')
161156230Smux		path[strlen(path) - 1] = 0;
162156230Smux	strlcat(path, filename, PATH_MAX);
163156230Smux	cf = malloc(sizeof(struct cb_file));
164156230Smux	if (stat(path, &cf->cf_stat) < 0) {
165156230Smux		free(cf);
166156230Smux		return (errno);
167156230Smux	}
168156230Smux
169156230Smux	cf->cf_size = st.st_size;
170156230Smux	if (S_ISDIR(cf->cf_stat.st_mode)) {
171156230Smux		cf->cf_isdir = 1;
172156230Smux		cf->cf_u.dir = opendir(path);
173156230Smux		if (!cf->cf_u.dir)
174156230Smux			goto out;
175156230Smux		*hp = cf;
176156230Smux		return (0);
177156230Smux	}
178156230Smux	if (S_ISREG(cf->cf_stat.st_mode)) {
179156230Smux		cf->cf_isdir = 0;
180156230Smux		cf->cf_u.fd = open(path, O_RDONLY);
181156230Smux		if (cf->cf_u.fd < 0)
182156230Smux			goto out;
183156230Smux		*hp = cf;
184156230Smux		return (0);
185156230Smux	}
186156230Smux
187156230Smuxout:
188156230Smux	free(cf);
189156230Smux	return (EINVAL);
190156230Smux}
191156230Smux
192156230Smuxstatic int
193156230Smuxcb_close(void *arg, void *h)
194156230Smux{
195156230Smux	struct cb_file *cf = h;
196156230Smux
197156230Smux	if (cf->cf_isdir)
198156230Smux		closedir(cf->cf_u.dir);
199156230Smux	else
200156230Smux		close(cf->cf_u.fd);
201156230Smux	free(cf);
202156230Smux
203156230Smux	return (0);
204186781Slulf}
205186781Slulf
206156230Smuxstatic int
207186781Slulfcb_isdir(void *arg, void *h)
208156230Smux{
209156230Smux	struct cb_file *cf = h;
210156230Smux
211156230Smux	return (cf->cf_isdir);
212156230Smux}
213156230Smux
214156230Smuxstatic int
215156230Smuxcb_read(void *arg, void *h, void *buf, size_t size, size_t *resid)
216156230Smux{
217156230Smux	struct cb_file *cf = h;
218156230Smux	ssize_t sz;
219156230Smux
220156230Smux	if (cf->cf_isdir)
221156230Smux		return (EINVAL);
222156230Smux	sz = read(cf->cf_u.fd, buf, size);
223156230Smux	if (sz < 0)
224156230Smux		return (EINVAL);
225156251Smux	*resid = size - sz;
226156230Smux	return (0);
227156230Smux}
228156230Smux
229156230Smuxstatic int
230156230Smuxcb_readdir(void *arg, void *h, uint32_t *fileno_return, uint8_t *type_return,
231156230Smux	   size_t *namelen_return, char *name)
232156230Smux{
233156230Smux	struct cb_file *cf = h;
234156230Smux	struct dirent *dp;
235156230Smux
236156230Smux	if (!cf->cf_isdir)
237156230Smux		return (EINVAL);
238156230Smux
239156230Smux	dp = readdir(cf->cf_u.dir);
240156230Smux	if (!dp)
241156230Smux		return (ENOENT);
242156230Smux
243156230Smux	/*
244156230Smux	 * Note: d_namlen is in the range 0..255 and therefore less
245156230Smux	 * than PATH_MAX so we don't need to test before copying.
246156230Smux	 */
247156230Smux	*fileno_return = dp->d_fileno;
248156230Smux	*type_return = dp->d_type;
249156230Smux	*namelen_return = dp->d_namlen;
250156230Smux	memcpy(name, dp->d_name, dp->d_namlen);
251186781Slulf	name[dp->d_namlen] = 0;
252186781Slulf
253186781Slulf	return (0);
254186781Slulf}
255186781Slulf
256186781Slulfstatic int
257186781Slulfcb_seek(void *arg, void *h, uint64_t offset, int whence)
258186781Slulf{
259186781Slulf	struct cb_file *cf = h;
260186781Slulf
261186781Slulf	if (cf->cf_isdir)
262186781Slulf		return (EINVAL);
263186781Slulf	if (lseek(cf->cf_u.fd, offset, whence) < 0)
264186781Slulf		return (errno);
265186781Slulf	return (0);
266186781Slulf}
267186781Slulf
268186781Slulfstatic int
269186781Slulfcb_stat(void *arg, void *h, int *mode, int *uid, int *gid, uint64_t *size)
270186781Slulf{
271186781Slulf	struct cb_file *cf = h;
272186781Slulf
273186781Slulf	*mode = cf->cf_stat.st_mode;
274186781Slulf	*uid = cf->cf_stat.st_uid;
275186781Slulf	*gid = cf->cf_stat.st_gid;
276156230Smux	*size = cf->cf_stat.st_size;
277156230Smux	return (0);
278156230Smux}
279156230Smux
280156230Smux/*
281156230Smux * Disk image i/o callbacks
282156230Smux */
283156230Smux
284156230Smuxstatic int
285156230Smuxcb_diskread(void *arg, int unit, uint64_t from, void *to, size_t size,
286156230Smux	    size_t *resid)
287186781Slulf{
288156230Smux	ssize_t n;
289156230Smux
290156230Smux	if (unit != 0 || disk_fd == -1)
291156230Smux		return (EIO);
292156230Smux	n = pread(disk_fd, to, size, from);
293186781Slulf	if (n < 0)
294186781Slulf		return (errno);
295186781Slulf	*resid = size - n;
296186781Slulf	return (0);
297186781Slulf}
298186781Slulf
299186781Slulfstatic int
300186781Slulfcb_diskioctl(void *arg, int unit, u_long cmd, void *data)
301186781Slulf{
302186781Slulf	struct stat sb;
303186781Slulf
304186781Slulf	if (unit != 0 || disk_fd == -1)
305186781Slulf		return (EBADF);
306186781Slulf
307186781Slulf	switch (cmd) {
308186781Slulf	case DIOCGSECTORSIZE:
309186781Slulf		*(u_int *)data = 512;
310186781Slulf		break;
311186781Slulf	case DIOCGMEDIASIZE:
312186781Slulf		if (fstat(disk_fd, &sb) == 0)
313186781Slulf			*(off_t *)data = sb.st_size;
314186781Slulf		else
315186781Slulf			return (ENOTTY);
316186781Slulf		break;
317186781Slulf	default:
318186781Slulf		return (ENOTTY);
319186781Slulf	}
320186781Slulf
321186781Slulf	return (0);
322186781Slulf}
323186781Slulf
324186781Slulf/*
325186781Slulf * Guest virtual machine i/o callbacks
326186781Slulf */
327186781Slulfstatic int
328186781Slulfcb_copyin(void *arg, const void *from, uint64_t to, size_t size)
329186781Slulf{
330186781Slulf	char *ptr;
331186781Slulf
332156230Smux	to &= 0x7fffffff;
333156230Smux
334156230Smux	ptr = vm_map_gpa(ctx, to, size);
335156230Smux	if (ptr == NULL)
336156230Smux		return (EFAULT);
337156230Smux
338156230Smux	memcpy(ptr, from, size);
339156230Smux	return (0);
340156230Smux}
341156230Smux
342156230Smuxstatic int
343156230Smuxcb_copyout(void *arg, uint64_t from, void *to, size_t size)
344156230Smux{
345156230Smux	char *ptr;
346156230Smux
347156230Smux	from &= 0x7fffffff;
348156230Smux
349156230Smux	ptr = vm_map_gpa(ctx, from, size);
350156230Smux	if (ptr == NULL)
351156230Smux		return (EFAULT);
352156230Smux
353156230Smux	memcpy(to, ptr, size);
354156230Smux	return (0);
355156230Smux}
356156230Smux
357156230Smuxstatic void
358156230Smuxcb_setreg(void *arg, int r, uint64_t v)
359156230Smux{
360156230Smux	int error;
361156230Smux	enum vm_reg_name vmreg;
362156230Smux
363156230Smux	vmreg = VM_REG_LAST;
364156230Smux
365156230Smux	switch (r) {
366156230Smux	case 4:
367156230Smux		vmreg = VM_REG_GUEST_RSP;
368156230Smux		rsp = v;
369156230Smux		break;
370156230Smux	default:
371156230Smux		break;
372156230Smux	}
373156230Smux
374156230Smux	if (vmreg == VM_REG_LAST) {
375156230Smux		printf("test_setreg(%d): not implemented\n", r);
376156230Smux		cb_exit(NULL, USERBOOT_EXIT_QUIT);
377156230Smux	}
378156230Smux
379156230Smux	error = vm_set_register(ctx, BSP, vmreg, v);
380156230Smux	if (error) {
381156230Smux		perror("vm_set_register");
382156230Smux		cb_exit(NULL, USERBOOT_EXIT_QUIT);
383156230Smux	}
384156230Smux}
385156230Smux
386156230Smuxstatic void
387156230Smuxcb_setmsr(void *arg, int r, uint64_t v)
388156230Smux{
389156230Smux	int error;
390156230Smux	enum vm_reg_name vmreg;
391156230Smux
392156230Smux	vmreg = VM_REG_LAST;
393156230Smux
394156230Smux	switch (r) {
395156230Smux	case MSR_EFER:
396156230Smux		vmreg = VM_REG_GUEST_EFER;
397156230Smux		break;
398156230Smux	default:
399156230Smux		break;
400156230Smux	}
401156230Smux
402156230Smux	if (vmreg == VM_REG_LAST) {
403156230Smux		printf("test_setmsr(%d): not implemented\n", r);
404156230Smux		cb_exit(NULL, USERBOOT_EXIT_QUIT);
405156230Smux	}
406156230Smux
407156230Smux	error = vm_set_register(ctx, BSP, vmreg, v);
408156230Smux	if (error) {
409156230Smux		perror("vm_set_msr");
410156230Smux		cb_exit(NULL, USERBOOT_EXIT_QUIT);
411156230Smux	}
412156230Smux}
413156230Smux
414156230Smuxstatic void
415156230Smuxcb_setcr(void *arg, int r, uint64_t v)
416156230Smux{
417156230Smux	int error;
418156230Smux	enum vm_reg_name vmreg;
419156230Smux
420156230Smux	vmreg = VM_REG_LAST;
421156230Smux
422156230Smux	switch (r) {
423156230Smux	case 0:
424156230Smux		vmreg = VM_REG_GUEST_CR0;
425156230Smux		break;
426156230Smux	case 3:
427156230Smux		vmreg = VM_REG_GUEST_CR3;
428156230Smux		cr3 = v;
429156230Smux		break;
430156230Smux	case 4:
431156230Smux		vmreg = VM_REG_GUEST_CR4;
432156230Smux		break;
433156230Smux	default:
434156230Smux		break;
435156230Smux	}
436156230Smux
437156230Smux	if (vmreg == VM_REG_LAST) {
438156230Smux		printf("test_setcr(%d): not implemented\n", r);
439156230Smux		cb_exit(NULL, USERBOOT_EXIT_QUIT);
440156230Smux	}
441156230Smux
442156230Smux	error = vm_set_register(ctx, BSP, vmreg, v);
443156230Smux	if (error) {
444156230Smux		perror("vm_set_cr");
445156230Smux		cb_exit(NULL, USERBOOT_EXIT_QUIT);
446156230Smux	}
447156230Smux}
448156230Smux
449156230Smuxstatic void
450156230Smuxcb_setgdt(void *arg, uint64_t base, size_t size)
451156230Smux{
452156230Smux	int error;
453156230Smux
454156230Smux	error = vm_set_desc(ctx, BSP, VM_REG_GUEST_GDTR, base, size - 1, 0);
455156230Smux	if (error != 0) {
456156230Smux		perror("vm_set_desc(gdt)");
457156230Smux		cb_exit(NULL, USERBOOT_EXIT_QUIT);
458156230Smux	}
459156230Smux
460156230Smux	gdtbase = base;
461156230Smux}
462156230Smux
463156230Smuxstatic void
464156230Smuxcb_exec(void *arg, uint64_t rip)
465156230Smux{
466156230Smux	int error;
467156230Smux
468156230Smux	if (cr3 == 0)
469156230Smux		error = vm_setup_freebsd_registers_i386(ctx, BSP, rip, gdtbase,
470156230Smux		    rsp);
471156230Smux	else
472156230Smux		error = vm_setup_freebsd_registers(ctx, BSP, rip, cr3, gdtbase,
473156230Smux		    rsp);
474156230Smux	if (error) {
475156230Smux		perror("vm_setup_freebsd_registers");
476156230Smux		cb_exit(NULL, USERBOOT_EXIT_QUIT);
477156230Smux	}
478156230Smux
479156230Smux	cb_exit(NULL, 0);
480156230Smux}
481156230Smux
482156230Smux/*
483156230Smux * Misc
484156230Smux */
485156230Smux
486156230Smuxstatic void
487156230Smuxcb_delay(void *arg, int usec)
488156230Smux{
489156230Smux
490156230Smux	usleep(usec);
491156230Smux}
492156230Smux
493156230Smuxstatic void
494156230Smuxcb_exit(void *arg, int v)
495156230Smux{
496156230Smux
497156230Smux	tcsetattr(consout_fd, TCSAFLUSH, &oldterm);
498156230Smux	exit(v);
499156230Smux}
500156230Smux
501156230Smuxstatic void
502156230Smuxcb_getmem(void *arg, uint64_t *ret_lowmem, uint64_t *ret_highmem)
503156230Smux{
504156230Smux
505156230Smux	vm_get_memory_seg(ctx, 0, ret_lowmem, NULL);
506156230Smux	vm_get_memory_seg(ctx, 4 * GB, ret_highmem, NULL);
507156230Smux}
508156230Smux
509156230Smuxstruct env {
510156230Smux	const char *str;	/* name=value */
511156230Smux	SLIST_ENTRY(env) next;
512156230Smux};
513156230Smux
514156230Smuxstatic SLIST_HEAD(envhead, env) envhead;
515156230Smux
516156230Smuxstatic void
517156230Smuxaddenv(const char *str)
518156230Smux{
519156230Smux	struct env *env;
520156230Smux
521156230Smux	env = malloc(sizeof(struct env));
522156230Smux	env->str = str;
523156230Smux	SLIST_INSERT_HEAD(&envhead, env, next);
524156230Smux}
525156230Smux
526156230Smuxstatic const char *
527156230Smuxcb_getenv(void *arg, int num)
528156230Smux{
529156230Smux	int i;
530156230Smux	struct env *env;
531156230Smux
532156230Smux	i = 0;
533156230Smux	SLIST_FOREACH(env, &envhead, next) {
534156230Smux		if (i == num)
535156230Smux			return (env->str);
536156230Smux		i++;
537156230Smux	}
538156230Smux
539156230Smux	return (NULL);
540156230Smux}
541156230Smux
542156230Smuxstatic struct loader_callbacks cb = {
543156230Smux	.getc = cb_getc,
544156230Smux	.putc = cb_putc,
545156230Smux	.poll = cb_poll,
546156230Smux
547156230Smux	.open = cb_open,
548156230Smux	.close = cb_close,
549156230Smux	.isdir = cb_isdir,
550156230Smux	.read = cb_read,
551156230Smux	.readdir = cb_readdir,
552156230Smux	.seek = cb_seek,
553156230Smux	.stat = cb_stat,
554156230Smux
555156230Smux	.diskread = cb_diskread,
556156230Smux	.diskioctl = cb_diskioctl,
557156230Smux
558156230Smux	.copyin = cb_copyin,
559156230Smux	.copyout = cb_copyout,
560156230Smux	.setreg = cb_setreg,
561156230Smux	.setmsr = cb_setmsr,
562156230Smux	.setcr = cb_setcr,
563156230Smux	.setgdt = cb_setgdt,
564156230Smux	.exec = cb_exec,
565156230Smux
566156230Smux	.delay = cb_delay,
567156230Smux	.exit = cb_exit,
568156230Smux	.getmem = cb_getmem,
569156230Smux
570156230Smux	.getenv = cb_getenv,
571156230Smux};
572156230Smux
573156230Smuxstatic int
574156230Smuxaltcons_open(char *path)
575186781Slulf{
576186781Slulf	struct stat sb;
577186781Slulf	int err;
578186781Slulf	int fd;
579186781Slulf
580186781Slulf	/*
581186781Slulf	 * Allow stdio to be passed in so that the same string
582186781Slulf	 * can be used for the bhyveload console and bhyve com-port
583186781Slulf	 * parameters
584186781Slulf	 */
585186781Slulf	if (!strcmp(path, "stdio"))
586186781Slulf		return (0);
587186781Slulf
588186781Slulf	err = stat(path, &sb);
589186781Slulf	if (err == 0) {
590186781Slulf		if (!S_ISCHR(sb.st_mode))
591186781Slulf			err = ENOTSUP;
592186781Slulf		else {
593186781Slulf			fd = open(path, O_RDWR | O_NONBLOCK);
594186781Slulf			if (fd < 0)
595186781Slulf				err = errno;
596186781Slulf			else
597186781Slulf				consin_fd = consout_fd = fd;
598186781Slulf		}
599186781Slulf	}
600186781Slulf
601186781Slulf	return (err);
602186781Slulf}
603186781Slulf
604186781Slulfstatic void
605186781Slulfusage(void)
606186781Slulf{
607186781Slulf
608186781Slulf	fprintf(stderr,
609186781Slulf	    "usage: %s [-m mem-size] [-d <disk-path>] [-h <host-path>]\n"
610186781Slulf	    "       %*s [-e <name=value>] [-c <console-device>] <vmname>\n",
611186781Slulf	    progname,
612186781Slulf	    (int)strlen(progname), "");
613186781Slulf	exit(1);
614186781Slulf}
615186781Slulf
616186781Slulfint
617186781Slulfmain(int argc, char** argv)
618186781Slulf{
619186781Slulf	void *h;
620186781Slulf	void (*func)(struct loader_callbacks *, void *, int, int);
621186781Slulf	uint64_t mem_size;
622186781Slulf	int opt, error;
623186781Slulf	char *disk_image;
624186781Slulf
625186781Slulf	progname = basename(argv[0]);
626186781Slulf
627186781Slulf	mem_size = 256 * MB;
628186781Slulf	disk_image = NULL;
629186781Slulf
630186781Slulf	consin_fd = STDIN_FILENO;
631186781Slulf	consout_fd = STDOUT_FILENO;
632186781Slulf
633186781Slulf	while ((opt = getopt(argc, argv, "c:d:e:h:m:")) != -1) {
634186781Slulf		switch (opt) {
635186781Slulf		case 'c':
636186781Slulf			error = altcons_open(optarg);
637186781Slulf			if (error != 0)
638186781Slulf				errx(EX_USAGE, "Could not open '%s'", optarg);
639186781Slulf			break;
640186781Slulf		case 'd':
641186781Slulf			disk_image = optarg;
642186781Slulf			break;
643186781Slulf
644186781Slulf		case 'e':
645			addenv(optarg);
646			break;
647
648		case 'h':
649			host_base = optarg;
650			break;
651
652		case 'm':
653			error = vm_parse_memsize(optarg, &mem_size);
654			if (error != 0)
655				errx(EX_USAGE, "Invalid memsize '%s'", optarg);
656			break;
657		case '?':
658			usage();
659		}
660	}
661
662	argc -= optind;
663	argv += optind;
664
665	if (argc != 1)
666		usage();
667
668	vmname = argv[0];
669
670	error = vm_create(vmname);
671	if (error != 0 && errno != EEXIST) {
672		perror("vm_create");
673		exit(1);
674
675	}
676
677	ctx = vm_open(vmname);
678	if (ctx == NULL) {
679		perror("vm_open");
680		exit(1);
681	}
682
683	error = vm_setup_memory(ctx, mem_size, VM_MMAP_ALL);
684	if (error) {
685		perror("vm_setup_memory");
686		exit(1);
687	}
688
689	tcgetattr(consout_fd, &term);
690	oldterm = term;
691	cfmakeraw(&term);
692	term.c_cflag |= CLOCAL;
693
694	tcsetattr(consout_fd, TCSAFLUSH, &term);
695
696	h = dlopen("/boot/userboot.so", RTLD_LOCAL);
697	if (!h) {
698		printf("%s\n", dlerror());
699		return (1);
700	}
701	func = dlsym(h, "loader_main");
702	if (!func) {
703		printf("%s\n", dlerror());
704		return (1);
705	}
706
707	if (disk_image) {
708		disk_fd = open(disk_image, O_RDONLY);
709	}
710
711	addenv("smbios.bios.vendor=BHYVE");
712	addenv("boot_serial=1");
713
714	func(&cb, NULL, USERBOOT_VERSION_3, disk_fd >= 0);
715}
716