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