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