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