xref: /freebsd/usr.sbin/bhyveload/bhyveload.c (revision 0698344859516a56ccc2d1577416f36b429b05be)
1 /*-
2  * Copyright (c) 2011 NetApp, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 /*-
30  * Copyright (c) 2011 Google, Inc.
31  * All rights reserved.
32  *
33  * Redistribution and use in source and binary forms, with or without
34  * modification, are permitted provided that the following conditions
35  * are met:
36  * 1. Redistributions of source code must retain the above copyright
37  *    notice, this list of conditions and the following disclaimer.
38  * 2. Redistributions in binary form must reproduce the above copyright
39  *    notice, this list of conditions and the following disclaimer in the
40  *    documentation and/or other materials provided with the distribution.
41  *
42  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
43  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
44  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
45  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
46  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
47  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
48  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
49  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
50  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
51  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
52  * SUCH DAMAGE.
53  *
54  * $FreeBSD$
55  */
56 
57 #include <sys/cdefs.h>
58 __FBSDID("$FreeBSD$");
59 
60 #include <sys/ioctl.h>
61 #include <sys/stat.h>
62 #include <sys/disk.h>
63 #include <sys/queue.h>
64 
65 #include <machine/specialreg.h>
66 #include <machine/vmm.h>
67 
68 #include <dirent.h>
69 #include <dlfcn.h>
70 #include <errno.h>
71 #include <err.h>
72 #include <fcntl.h>
73 #include <getopt.h>
74 #include <limits.h>
75 #include <stdio.h>
76 #include <stdlib.h>
77 #include <string.h>
78 #include <sysexits.h>
79 #include <termios.h>
80 #include <unistd.h>
81 
82 #include <vmmapi.h>
83 
84 #include "userboot.h"
85 
86 #define	MB	(1024 * 1024UL)
87 #define	GB	(1024 * 1024 * 1024UL)
88 #define	BSP	0
89 
90 static char *host_base = "/";
91 static struct termios term, oldterm;
92 static int disk_fd = -1;
93 
94 static char *vmname, *progname;
95 static struct vmctx *ctx;
96 
97 static uint64_t gdtbase, cr3, rsp;
98 
99 static void cb_exit(void *arg, int v);
100 
101 /*
102  * Console i/o callbacks
103  */
104 
105 static void
106 cb_putc(void *arg, int ch)
107 {
108 	char c = ch;
109 
110 	write(1, &c, 1);
111 }
112 
113 static int
114 cb_getc(void *arg)
115 {
116 	char c;
117 
118 	if (read(0, &c, 1) == 1)
119 		return (c);
120 	return (-1);
121 }
122 
123 static int
124 cb_poll(void *arg)
125 {
126 	int n;
127 
128 	if (ioctl(0, FIONREAD, &n) >= 0)
129 		return (n > 0);
130 	return (0);
131 }
132 
133 /*
134  * Host filesystem i/o callbacks
135  */
136 
137 struct cb_file {
138 	int cf_isdir;
139 	size_t cf_size;
140 	struct stat cf_stat;
141 	union {
142 		int fd;
143 		DIR *dir;
144 	} cf_u;
145 };
146 
147 static int
148 cb_open(void *arg, const char *filename, void **hp)
149 {
150 	struct stat st;
151 	struct cb_file *cf;
152 	char path[PATH_MAX];
153 
154 	if (!host_base)
155 		return (ENOENT);
156 
157 	strlcpy(path, host_base, PATH_MAX);
158 	if (path[strlen(path) - 1] == '/')
159 		path[strlen(path) - 1] = 0;
160 	strlcat(path, filename, PATH_MAX);
161 	cf = malloc(sizeof(struct cb_file));
162 	if (stat(path, &cf->cf_stat) < 0) {
163 		free(cf);
164 		return (errno);
165 	}
166 
167 	cf->cf_size = st.st_size;
168 	if (S_ISDIR(cf->cf_stat.st_mode)) {
169 		cf->cf_isdir = 1;
170 		cf->cf_u.dir = opendir(path);
171 		if (!cf->cf_u.dir)
172 			goto out;
173 		*hp = cf;
174 		return (0);
175 	}
176 	if (S_ISREG(cf->cf_stat.st_mode)) {
177 		cf->cf_isdir = 0;
178 		cf->cf_u.fd = open(path, O_RDONLY);
179 		if (cf->cf_u.fd < 0)
180 			goto out;
181 		*hp = cf;
182 		return (0);
183 	}
184 
185 out:
186 	free(cf);
187 	return (EINVAL);
188 }
189 
190 static int
191 cb_close(void *arg, void *h)
192 {
193 	struct cb_file *cf = h;
194 
195 	if (cf->cf_isdir)
196 		closedir(cf->cf_u.dir);
197 	else
198 		close(cf->cf_u.fd);
199 	free(cf);
200 
201 	return (0);
202 }
203 
204 static int
205 cb_isdir(void *arg, void *h)
206 {
207 	struct cb_file *cf = h;
208 
209 	return (cf->cf_isdir);
210 }
211 
212 static int
213 cb_read(void *arg, void *h, void *buf, size_t size, size_t *resid)
214 {
215 	struct cb_file *cf = h;
216 	ssize_t sz;
217 
218 	if (cf->cf_isdir)
219 		return (EINVAL);
220 	sz = read(cf->cf_u.fd, buf, size);
221 	if (sz < 0)
222 		return (EINVAL);
223 	*resid = size - sz;
224 	return (0);
225 }
226 
227 static int
228 cb_readdir(void *arg, void *h, uint32_t *fileno_return, uint8_t *type_return,
229 	   size_t *namelen_return, char *name)
230 {
231 	struct cb_file *cf = h;
232 	struct dirent *dp;
233 
234 	if (!cf->cf_isdir)
235 		return (EINVAL);
236 
237 	dp = readdir(cf->cf_u.dir);
238 	if (!dp)
239 		return (ENOENT);
240 
241 	/*
242 	 * Note: d_namlen is in the range 0..255 and therefore less
243 	 * than PATH_MAX so we don't need to test before copying.
244 	 */
245 	*fileno_return = dp->d_fileno;
246 	*type_return = dp->d_type;
247 	*namelen_return = dp->d_namlen;
248 	memcpy(name, dp->d_name, dp->d_namlen);
249 	name[dp->d_namlen] = 0;
250 
251 	return (0);
252 }
253 
254 static int
255 cb_seek(void *arg, void *h, uint64_t offset, int whence)
256 {
257 	struct cb_file *cf = h;
258 
259 	if (cf->cf_isdir)
260 		return (EINVAL);
261 	if (lseek(cf->cf_u.fd, offset, whence) < 0)
262 		return (errno);
263 	return (0);
264 }
265 
266 static int
267 cb_stat(void *arg, void *h, int *mode, int *uid, int *gid, uint64_t *size)
268 {
269 	struct cb_file *cf = h;
270 
271 	*mode = cf->cf_stat.st_mode;
272 	*uid = cf->cf_stat.st_uid;
273 	*gid = cf->cf_stat.st_gid;
274 	*size = cf->cf_stat.st_size;
275 	return (0);
276 }
277 
278 /*
279  * Disk image i/o callbacks
280  */
281 
282 static int
283 cb_diskread(void *arg, int unit, uint64_t from, void *to, size_t size,
284 	    size_t *resid)
285 {
286 	ssize_t n;
287 
288 	if (unit != 0 || disk_fd == -1)
289 		return (EIO);
290 	n = pread(disk_fd, to, size, from);
291 	if (n < 0)
292 		return (errno);
293 	*resid = size - n;
294 	return (0);
295 }
296 
297 static int
298 cb_diskioctl(void *arg, int unit, u_long cmd, void *data)
299 {
300 	struct stat sb;
301 
302 	if (unit != 0 || disk_fd == -1)
303 		return (EBADF);
304 
305 	switch (cmd) {
306 	case DIOCGSECTORSIZE:
307 		*(u_int *)data = 512;
308 		break;
309 	case DIOCGMEDIASIZE:
310 		if (fstat(disk_fd, &sb) == 0)
311 			*(off_t *)data = sb.st_size;
312 		else
313 			return (ENOTTY);
314 		break;
315 	default:
316 		return (ENOTTY);
317 	}
318 
319 	return (0);
320 }
321 
322 /*
323  * Guest virtual machine i/o callbacks
324  */
325 static int
326 cb_copyin(void *arg, const void *from, uint64_t to, size_t size)
327 {
328 	char *ptr;
329 
330 	to &= 0x7fffffff;
331 
332 	ptr = vm_map_gpa(ctx, to, size);
333 	if (ptr == NULL)
334 		return (EFAULT);
335 
336 	memcpy(ptr, from, size);
337 	return (0);
338 }
339 
340 static int
341 cb_copyout(void *arg, uint64_t from, void *to, size_t size)
342 {
343 	char *ptr;
344 
345 	from &= 0x7fffffff;
346 
347 	ptr = vm_map_gpa(ctx, from, size);
348 	if (ptr == NULL)
349 		return (EFAULT);
350 
351 	memcpy(to, ptr, size);
352 	return (0);
353 }
354 
355 static void
356 cb_setreg(void *arg, int r, uint64_t v)
357 {
358 	int error;
359 	enum vm_reg_name vmreg;
360 
361 	vmreg = VM_REG_LAST;
362 
363 	switch (r) {
364 	case 4:
365 		vmreg = VM_REG_GUEST_RSP;
366 		rsp = v;
367 		break;
368 	default:
369 		break;
370 	}
371 
372 	if (vmreg == VM_REG_LAST) {
373 		printf("test_setreg(%d): not implemented\n", r);
374 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
375 	}
376 
377 	error = vm_set_register(ctx, BSP, vmreg, v);
378 	if (error) {
379 		perror("vm_set_register");
380 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
381 	}
382 }
383 
384 static void
385 cb_setmsr(void *arg, int r, uint64_t v)
386 {
387 	int error;
388 	enum vm_reg_name vmreg;
389 
390 	vmreg = VM_REG_LAST;
391 
392 	switch (r) {
393 	case MSR_EFER:
394 		vmreg = VM_REG_GUEST_EFER;
395 		break;
396 	default:
397 		break;
398 	}
399 
400 	if (vmreg == VM_REG_LAST) {
401 		printf("test_setmsr(%d): not implemented\n", r);
402 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
403 	}
404 
405 	error = vm_set_register(ctx, BSP, vmreg, v);
406 	if (error) {
407 		perror("vm_set_msr");
408 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
409 	}
410 }
411 
412 static void
413 cb_setcr(void *arg, int r, uint64_t v)
414 {
415 	int error;
416 	enum vm_reg_name vmreg;
417 
418 	vmreg = VM_REG_LAST;
419 
420 	switch (r) {
421 	case 0:
422 		vmreg = VM_REG_GUEST_CR0;
423 		break;
424 	case 3:
425 		vmreg = VM_REG_GUEST_CR3;
426 		cr3 = v;
427 		break;
428 	case 4:
429 		vmreg = VM_REG_GUEST_CR4;
430 		break;
431 	default:
432 		break;
433 	}
434 
435 	if (vmreg == VM_REG_LAST) {
436 		printf("test_setcr(%d): not implemented\n", r);
437 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
438 	}
439 
440 	error = vm_set_register(ctx, BSP, vmreg, v);
441 	if (error) {
442 		perror("vm_set_cr");
443 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
444 	}
445 }
446 
447 static void
448 cb_setgdt(void *arg, uint64_t base, size_t size)
449 {
450 	int error;
451 
452 	error = vm_set_desc(ctx, BSP, VM_REG_GUEST_GDTR, base, size - 1, 0);
453 	if (error != 0) {
454 		perror("vm_set_desc(gdt)");
455 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
456 	}
457 
458 	gdtbase = base;
459 }
460 
461 static void
462 cb_exec(void *arg, uint64_t rip)
463 {
464 	int error;
465 
466 	error = vm_setup_freebsd_registers(ctx, BSP, rip, cr3, gdtbase, rsp);
467 	if (error) {
468 		perror("vm_setup_freebsd_registers");
469 		cb_exit(NULL, USERBOOT_EXIT_QUIT);
470 	}
471 
472 	cb_exit(NULL, 0);
473 }
474 
475 /*
476  * Misc
477  */
478 
479 static void
480 cb_delay(void *arg, int usec)
481 {
482 
483 	usleep(usec);
484 }
485 
486 static void
487 cb_exit(void *arg, int v)
488 {
489 
490 	tcsetattr(0, TCSAFLUSH, &oldterm);
491 	exit(v);
492 }
493 
494 static void
495 cb_getmem(void *arg, uint64_t *ret_lowmem, uint64_t *ret_highmem)
496 {
497 
498 	vm_get_memory_seg(ctx, 0, ret_lowmem, NULL);
499 	vm_get_memory_seg(ctx, 4 * GB, ret_highmem, NULL);
500 }
501 
502 struct env {
503 	const char *str;	/* name=value */
504 	SLIST_ENTRY(env) next;
505 };
506 
507 static SLIST_HEAD(envhead, env) envhead;
508 
509 static void
510 addenv(const char *str)
511 {
512 	struct env *env;
513 
514 	env = malloc(sizeof(struct env));
515 	env->str = str;
516 	SLIST_INSERT_HEAD(&envhead, env, next);
517 }
518 
519 static const char *
520 cb_getenv(void *arg, int num)
521 {
522 	int i;
523 	struct env *env;
524 
525 	i = 0;
526 	SLIST_FOREACH(env, &envhead, next) {
527 		if (i == num)
528 			return (env->str);
529 		i++;
530 	}
531 
532 	return (NULL);
533 }
534 
535 static struct loader_callbacks cb = {
536 	.getc = cb_getc,
537 	.putc = cb_putc,
538 	.poll = cb_poll,
539 
540 	.open = cb_open,
541 	.close = cb_close,
542 	.isdir = cb_isdir,
543 	.read = cb_read,
544 	.readdir = cb_readdir,
545 	.seek = cb_seek,
546 	.stat = cb_stat,
547 
548 	.diskread = cb_diskread,
549 	.diskioctl = cb_diskioctl,
550 
551 	.copyin = cb_copyin,
552 	.copyout = cb_copyout,
553 	.setreg = cb_setreg,
554 	.setmsr = cb_setmsr,
555 	.setcr = cb_setcr,
556 	.setgdt = cb_setgdt,
557 	.exec = cb_exec,
558 
559 	.delay = cb_delay,
560 	.exit = cb_exit,
561 	.getmem = cb_getmem,
562 
563 	.getenv = cb_getenv,
564 };
565 
566 static void
567 usage(void)
568 {
569 
570 	fprintf(stderr,
571 	    "usage: %s [-m mem-size][-d <disk-path>] [-h <host-path>] "
572 	    "[-e <name=value>] <vmname>\n", progname);
573 	exit(1);
574 }
575 
576 int
577 main(int argc, char** argv)
578 {
579 	void *h;
580 	void (*func)(struct loader_callbacks *, void *, int, int);
581 	uint64_t mem_size;
582 	int opt, error;
583 	char *disk_image;
584 
585 	progname = argv[0];
586 
587 	mem_size = 256 * MB;
588 	disk_image = NULL;
589 
590 	while ((opt = getopt(argc, argv, "d:e:h:m:")) != -1) {
591 		switch (opt) {
592 		case 'd':
593 			disk_image = optarg;
594 			break;
595 
596 		case 'e':
597 			addenv(optarg);
598 			break;
599 
600 		case 'h':
601 			host_base = optarg;
602 			break;
603 
604 		case 'm':
605 			error = vm_parse_memsize(optarg, &mem_size);
606 			if (error != 0)
607 				errx(EX_USAGE, "Invalid memsize '%s'", optarg);
608 			break;
609 		case '?':
610 			usage();
611 		}
612 	}
613 
614 	argc -= optind;
615 	argv += optind;
616 
617 	if (argc != 1)
618 		usage();
619 
620 	vmname = argv[0];
621 
622 	error = vm_create(vmname);
623 	if (error != 0 && errno != EEXIST) {
624 		perror("vm_create");
625 		exit(1);
626 
627 	}
628 
629 	ctx = vm_open(vmname);
630 	if (ctx == NULL) {
631 		perror("vm_open");
632 		exit(1);
633 	}
634 
635 	error = vm_setup_memory(ctx, mem_size, VM_MMAP_ALL);
636 	if (error) {
637 		perror("vm_setup_memory");
638 		exit(1);
639 	}
640 
641 	tcgetattr(0, &term);
642 	oldterm = term;
643 	term.c_lflag &= ~(ICANON|ECHO);
644 	term.c_iflag &= ~ICRNL;
645 	tcsetattr(0, TCSAFLUSH, &term);
646 	h = dlopen("/boot/userboot.so", RTLD_LOCAL);
647 	if (!h) {
648 		printf("%s\n", dlerror());
649 		return (1);
650 	}
651 	func = dlsym(h, "loader_main");
652 	if (!func) {
653 		printf("%s\n", dlerror());
654 		return (1);
655 	}
656 
657 	if (disk_image) {
658 		disk_fd = open(disk_image, O_RDONLY);
659 	}
660 
661 	addenv("smbios.bios.vendor=BHYVE");
662 	addenv("boot_serial=1");
663 
664 	func(&cb, NULL, USERBOOT_VERSION_3, disk_fd >= 0);
665 }
666