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$
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$
55223828Sneel */
56223828Sneel
57223828Sneel#include <sys/cdefs.h>
58223828Sneel__FBSDID("$FreeBSD$");
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:
315268932Sjhb		if (fstat(disk_fd[unit], &sb) == 0)
316242882Sneel			*(off_t *)data = sb.st_size;
317242882Sneel		else
318242882Sneel			return (ENOTTY);
319242882Sneel		break;
320242882Sneel	default:
321242882Sneel		return (ENOTTY);
322242882Sneel	}
323242882Sneel
324242882Sneel	return (0);
325242882Sneel}
326242882Sneel
327223828Sneel/*
328223828Sneel * Guest virtual machine i/o callbacks
329223828Sneel */
330223828Sneelstatic int
331223828Sneelcb_copyin(void *arg, const void *from, uint64_t to, size_t size)
332223828Sneel{
333248477Sneel	char *ptr;
334223828Sneel
335223828Sneel	to &= 0x7fffffff;
336248477Sneel
337248477Sneel	ptr = vm_map_gpa(ctx, to, size);
338248477Sneel	if (ptr == NULL)
339223828Sneel		return (EFAULT);
340223828Sneel
341248477Sneel	memcpy(ptr, from, size);
342223828Sneel	return (0);
343223828Sneel}
344223828Sneel
345223828Sneelstatic int
346223828Sneelcb_copyout(void *arg, uint64_t from, void *to, size_t size)
347223828Sneel{
348248477Sneel	char *ptr;
349223828Sneel
350223828Sneel	from &= 0x7fffffff;
351248477Sneel
352248477Sneel	ptr = vm_map_gpa(ctx, from, size);
353248477Sneel	if (ptr == NULL)
354223828Sneel		return (EFAULT);
355223828Sneel
356248477Sneel	memcpy(to, ptr, size);
357223828Sneel	return (0);
358223828Sneel}
359223828Sneel
360223828Sneelstatic void
361223828Sneelcb_setreg(void *arg, int r, uint64_t v)
362223828Sneel{
363223828Sneel	int error;
364223828Sneel	enum vm_reg_name vmreg;
365223828Sneel
366223828Sneel	vmreg = VM_REG_LAST;
367223828Sneel
368223828Sneel	switch (r) {
369223828Sneel	case 4:
370223828Sneel		vmreg = VM_REG_GUEST_RSP;
371223828Sneel		rsp = v;
372223828Sneel		break;
373223828Sneel	default:
374223828Sneel		break;
375223828Sneel	}
376223828Sneel
377223828Sneel	if (vmreg == VM_REG_LAST) {
378223828Sneel		printf("test_setreg(%d): not implemented\n", r);
379223828Sneel		cb_exit(NULL, USERBOOT_EXIT_QUIT);
380223828Sneel	}
381223828Sneel
382223828Sneel	error = vm_set_register(ctx, BSP, vmreg, v);
383223828Sneel	if (error) {
384223828Sneel		perror("vm_set_register");
385223828Sneel		cb_exit(NULL, USERBOOT_EXIT_QUIT);
386223828Sneel	}
387223828Sneel}
388223828Sneel
389223828Sneelstatic void
390223828Sneelcb_setmsr(void *arg, int r, uint64_t v)
391223828Sneel{
392223828Sneel	int error;
393223828Sneel	enum vm_reg_name vmreg;
394223828Sneel
395223828Sneel	vmreg = VM_REG_LAST;
396223828Sneel
397223828Sneel	switch (r) {
398223828Sneel	case MSR_EFER:
399223828Sneel		vmreg = VM_REG_GUEST_EFER;
400223828Sneel		break;
401223828Sneel	default:
402223828Sneel		break;
403223828Sneel	}
404223828Sneel
405223828Sneel	if (vmreg == VM_REG_LAST) {
406223828Sneel		printf("test_setmsr(%d): not implemented\n", r);
407223828Sneel		cb_exit(NULL, USERBOOT_EXIT_QUIT);
408223828Sneel	}
409223828Sneel
410223828Sneel	error = vm_set_register(ctx, BSP, vmreg, v);
411223828Sneel	if (error) {
412223828Sneel		perror("vm_set_msr");
413223828Sneel		cb_exit(NULL, USERBOOT_EXIT_QUIT);
414223828Sneel	}
415223828Sneel}
416223828Sneel
417223828Sneelstatic void
418223828Sneelcb_setcr(void *arg, int r, uint64_t v)
419223828Sneel{
420223828Sneel	int error;
421223828Sneel	enum vm_reg_name vmreg;
422223828Sneel
423223828Sneel	vmreg = VM_REG_LAST;
424223828Sneel
425223828Sneel	switch (r) {
426223828Sneel	case 0:
427223828Sneel		vmreg = VM_REG_GUEST_CR0;
428223828Sneel		break;
429223828Sneel	case 3:
430223828Sneel		vmreg = VM_REG_GUEST_CR3;
431223828Sneel		cr3 = v;
432223828Sneel		break;
433223828Sneel	case 4:
434223828Sneel		vmreg = VM_REG_GUEST_CR4;
435223828Sneel		break;
436223828Sneel	default:
437223828Sneel		break;
438223828Sneel	}
439223828Sneel
440223828Sneel	if (vmreg == VM_REG_LAST) {
441223828Sneel		printf("test_setcr(%d): not implemented\n", r);
442223828Sneel		cb_exit(NULL, USERBOOT_EXIT_QUIT);
443223828Sneel	}
444223828Sneel
445223828Sneel	error = vm_set_register(ctx, BSP, vmreg, v);
446223828Sneel	if (error) {
447223828Sneel		perror("vm_set_cr");
448223828Sneel		cb_exit(NULL, USERBOOT_EXIT_QUIT);
449223828Sneel	}
450223828Sneel}
451223828Sneel
452223828Sneelstatic void
453223828Sneelcb_setgdt(void *arg, uint64_t base, size_t size)
454223828Sneel{
455223828Sneel	int error;
456223828Sneel
457223828Sneel	error = vm_set_desc(ctx, BSP, VM_REG_GUEST_GDTR, base, size - 1, 0);
458223828Sneel	if (error != 0) {
459223828Sneel		perror("vm_set_desc(gdt)");
460223828Sneel		cb_exit(NULL, USERBOOT_EXIT_QUIT);
461223828Sneel	}
462223828Sneel
463223828Sneel	gdtbase = base;
464223828Sneel}
465223828Sneel
466223828Sneelstatic void
467223828Sneelcb_exec(void *arg, uint64_t rip)
468223828Sneel{
469223828Sneel	int error;
470223828Sneel
471267399Sjhb	if (cr3 == 0)
472267399Sjhb		error = vm_setup_freebsd_registers_i386(ctx, BSP, rip, gdtbase,
473267399Sjhb		    rsp);
474267399Sjhb	else
475267399Sjhb		error = vm_setup_freebsd_registers(ctx, BSP, rip, cr3, gdtbase,
476267399Sjhb		    rsp);
477223828Sneel	if (error) {
478223828Sneel		perror("vm_setup_freebsd_registers");
479223828Sneel		cb_exit(NULL, USERBOOT_EXIT_QUIT);
480223828Sneel	}
481223828Sneel
482223828Sneel	cb_exit(NULL, 0);
483223828Sneel}
484223828Sneel
485223828Sneel/*
486223828Sneel * Misc
487223828Sneel */
488223828Sneel
489223828Sneelstatic void
490223828Sneelcb_delay(void *arg, int usec)
491223828Sneel{
492223828Sneel
493223828Sneel	usleep(usec);
494223828Sneel}
495223828Sneel
496223828Sneelstatic void
497223828Sneelcb_exit(void *arg, int v)
498223828Sneel{
499223828Sneel
500259301Sgrehan	tcsetattr(consout_fd, TCSAFLUSH, &oldterm);
501223828Sneel	exit(v);
502223828Sneel}
503223828Sneel
504223828Sneelstatic void
505223828Sneelcb_getmem(void *arg, uint64_t *ret_lowmem, uint64_t *ret_highmem)
506223828Sneel{
507223828Sneel
508270074Sgrehan	*ret_lowmem = vm_get_lowmem_size(ctx);
509270074Sgrehan	*ret_highmem = vm_get_highmem_size(ctx);
510223828Sneel}
511223828Sneel
512259301Sgrehanstruct env {
513259301Sgrehan	const char *str;	/* name=value */
514259301Sgrehan	SLIST_ENTRY(env) next;
515259301Sgrehan};
516259301Sgrehan
517259301Sgrehanstatic SLIST_HEAD(envhead, env) envhead;
518259301Sgrehan
519259301Sgrehanstatic void
520259301Sgrehanaddenv(const char *str)
521259301Sgrehan{
522259301Sgrehan	struct env *env;
523259301Sgrehan
524259301Sgrehan	env = malloc(sizeof(struct env));
525259301Sgrehan	env->str = str;
526259301Sgrehan	SLIST_INSERT_HEAD(&envhead, env, next);
527259301Sgrehan}
528259301Sgrehan
529242676Sneelstatic const char *
530242676Sneelcb_getenv(void *arg, int num)
531242676Sneel{
532259301Sgrehan	int i;
533259301Sgrehan	struct env *env;
534242676Sneel
535259301Sgrehan	i = 0;
536259301Sgrehan	SLIST_FOREACH(env, &envhead, next) {
537259301Sgrehan		if (i == num)
538259301Sgrehan			return (env->str);
539259301Sgrehan		i++;
540259301Sgrehan	}
541242676Sneel
542259301Sgrehan	return (NULL);
543242676Sneel}
544242676Sneel
545242882Sneelstatic struct loader_callbacks cb = {
546223828Sneel	.getc = cb_getc,
547223828Sneel	.putc = cb_putc,
548223828Sneel	.poll = cb_poll,
549223828Sneel
550223828Sneel	.open = cb_open,
551223828Sneel	.close = cb_close,
552223828Sneel	.isdir = cb_isdir,
553223828Sneel	.read = cb_read,
554223828Sneel	.readdir = cb_readdir,
555223828Sneel	.seek = cb_seek,
556223828Sneel	.stat = cb_stat,
557223828Sneel
558223828Sneel	.diskread = cb_diskread,
559242882Sneel	.diskioctl = cb_diskioctl,
560223828Sneel
561223828Sneel	.copyin = cb_copyin,
562223828Sneel	.copyout = cb_copyout,
563223828Sneel	.setreg = cb_setreg,
564223828Sneel	.setmsr = cb_setmsr,
565223828Sneel	.setcr = cb_setcr,
566223828Sneel	.setgdt = cb_setgdt,
567223828Sneel	.exec = cb_exec,
568223828Sneel
569223828Sneel	.delay = cb_delay,
570223828Sneel	.exit = cb_exit,
571223828Sneel	.getmem = cb_getmem,
572242676Sneel
573242676Sneel	.getenv = cb_getenv,
574223828Sneel};
575223828Sneel
576259301Sgrehanstatic int
577259301Sgrehanaltcons_open(char *path)
578259301Sgrehan{
579259301Sgrehan	struct stat sb;
580259301Sgrehan	int err;
581259301Sgrehan	int fd;
582259301Sgrehan
583259301Sgrehan	/*
584259301Sgrehan	 * Allow stdio to be passed in so that the same string
585259301Sgrehan	 * can be used for the bhyveload console and bhyve com-port
586259301Sgrehan	 * parameters
587259301Sgrehan	 */
588259301Sgrehan	if (!strcmp(path, "stdio"))
589259301Sgrehan		return (0);
590259301Sgrehan
591259301Sgrehan	err = stat(path, &sb);
592259301Sgrehan	if (err == 0) {
593259301Sgrehan		if (!S_ISCHR(sb.st_mode))
594259301Sgrehan			err = ENOTSUP;
595259301Sgrehan		else {
596259301Sgrehan			fd = open(path, O_RDWR | O_NONBLOCK);
597259301Sgrehan			if (fd < 0)
598259301Sgrehan				err = errno;
599259301Sgrehan			else
600259301Sgrehan				consin_fd = consout_fd = fd;
601259301Sgrehan		}
602259301Sgrehan	}
603259301Sgrehan
604259301Sgrehan	return (err);
605259301Sgrehan}
606259301Sgrehan
607268932Sjhbstatic int
608268932Sjhbdisk_open(char *path)
609268932Sjhb{
610268932Sjhb	int err, fd;
611268932Sjhb
612268932Sjhb	if (ndisks > NDISKS)
613268932Sjhb		return (ERANGE);
614268932Sjhb
615268932Sjhb	err = 0;
616268932Sjhb	fd = open(path, O_RDONLY);
617268932Sjhb
618268932Sjhb	if (fd > 0) {
619268932Sjhb		disk_fd[ndisks] = fd;
620268932Sjhb		ndisks++;
621268932Sjhb	} else
622268932Sjhb		err = errno;
623268932Sjhb
624268932Sjhb	return (err);
625268932Sjhb}
626268932Sjhb
627223828Sneelstatic void
628223828Sneelusage(void)
629223828Sneel{
630223828Sneel
631248477Sneel	fprintf(stderr,
632270159Sgrehan	    "usage: %s [-c <console-device>] [-d <disk-path>] [-e <name=value>]\n"
633270159Sgrehan	    "       %*s [-h <host-path>] [-m mem-size] <vmname>\n",
634259301Sgrehan	    progname,
635259301Sgrehan	    (int)strlen(progname), "");
636223828Sneel	exit(1);
637223828Sneel}
638223828Sneel
639223828Sneelint
640223828Sneelmain(int argc, char** argv)
641223828Sneel{
642223828Sneel	void *h;
643242882Sneel	void (*func)(struct loader_callbacks *, void *, int, int);
644248477Sneel	uint64_t mem_size;
645270071Sgrehan	int opt, error, need_reinit;
646223828Sneel
647259301Sgrehan	progname = basename(argv[0]);
648223828Sneel
649248477Sneel	mem_size = 256 * MB;
650223828Sneel
651259301Sgrehan	consin_fd = STDIN_FILENO;
652259301Sgrehan	consout_fd = STDOUT_FILENO;
653259301Sgrehan
654259301Sgrehan	while ((opt = getopt(argc, argv, "c:d:e:h:m:")) != -1) {
655223828Sneel		switch (opt) {
656259301Sgrehan		case 'c':
657259301Sgrehan			error = altcons_open(optarg);
658259301Sgrehan			if (error != 0)
659259301Sgrehan				errx(EX_USAGE, "Could not open '%s'", optarg);
660259301Sgrehan			break;
661268932Sjhb
662223828Sneel		case 'd':
663268932Sjhb			error = disk_open(optarg);
664268932Sjhb			if (error != 0)
665268932Sjhb				errx(EX_USAGE, "Could not open '%s'", optarg);
666223828Sneel			break;
667223828Sneel
668259301Sgrehan		case 'e':
669259301Sgrehan			addenv(optarg);
670259301Sgrehan			break;
671259301Sgrehan
672223828Sneel		case 'h':
673223828Sneel			host_base = optarg;
674223828Sneel			break;
675223828Sneel
676223828Sneel		case 'm':
677256176Sneel			error = vm_parse_memsize(optarg, &mem_size);
678256176Sneel			if (error != 0)
679256176Sneel				errx(EX_USAGE, "Invalid memsize '%s'", optarg);
680223828Sneel			break;
681223828Sneel		case '?':
682223828Sneel			usage();
683223828Sneel		}
684223828Sneel	}
685223828Sneel
686223828Sneel	argc -= optind;
687223828Sneel	argv += optind;
688223828Sneel
689223828Sneel	if (argc != 1)
690223828Sneel		usage();
691223828Sneel
692223828Sneel	vmname = argv[0];
693223828Sneel
694270071Sgrehan	need_reinit = 0;
695223828Sneel	error = vm_create(vmname);
696270071Sgrehan	if (error) {
697270071Sgrehan		if (errno != EEXIST) {
698270071Sgrehan			perror("vm_create");
699270071Sgrehan			exit(1);
700270071Sgrehan		}
701270071Sgrehan		need_reinit = 1;
702223828Sneel	}
703223828Sneel
704223828Sneel	ctx = vm_open(vmname);
705223828Sneel	if (ctx == NULL) {
706223828Sneel		perror("vm_open");
707223828Sneel		exit(1);
708223828Sneel	}
709223828Sneel
710270071Sgrehan	if (need_reinit) {
711270071Sgrehan		error = vm_reinit(ctx);
712270071Sgrehan		if (error) {
713270071Sgrehan			perror("vm_reinit");
714270071Sgrehan			exit(1);
715270071Sgrehan		}
716270071Sgrehan	}
717270071Sgrehan
718248477Sneel	error = vm_setup_memory(ctx, mem_size, VM_MMAP_ALL);
719223828Sneel	if (error) {
720248477Sneel		perror("vm_setup_memory");
721223828Sneel		exit(1);
722223828Sneel	}
723223828Sneel
724259301Sgrehan	tcgetattr(consout_fd, &term);
725223828Sneel	oldterm = term;
726259301Sgrehan	cfmakeraw(&term);
727259301Sgrehan	term.c_cflag |= CLOCAL;
728259301Sgrehan
729259301Sgrehan	tcsetattr(consout_fd, TCSAFLUSH, &term);
730259301Sgrehan
731234695Sgrehan	h = dlopen("/boot/userboot.so", RTLD_LOCAL);
732223828Sneel	if (!h) {
733223828Sneel		printf("%s\n", dlerror());
734223828Sneel		return (1);
735223828Sneel	}
736223828Sneel	func = dlsym(h, "loader_main");
737223828Sneel	if (!func) {
738223828Sneel		printf("%s\n", dlerror());
739223828Sneel		return (1);
740223828Sneel	}
741223828Sneel
742259301Sgrehan	addenv("smbios.bios.vendor=BHYVE");
743259301Sgrehan	addenv("boot_serial=1");
744259301Sgrehan
745268932Sjhb	func(&cb, NULL, USERBOOT_VERSION_3, ndisks);
746223828Sneel}
747