xref: /freebsd/usr.sbin/bhyveload/bhyveload.c (revision 55d0e022fbcd83ec5a29fe5751af85e2c3ebf19b)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2011 NetApp, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
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 
55 #include <sys/cdefs.h>
56 #include <sys/ioctl.h>
57 #include <sys/stat.h>
58 #include <sys/disk.h>
59 #include <sys/queue.h>
60 
61 #include <machine/specialreg.h>
62 #include <machine/vmm.h>
63 
64 #include <assert.h>
65 #include <dirent.h>
66 #include <dlfcn.h>
67 #include <errno.h>
68 #include <err.h>
69 #include <fcntl.h>
70 #include <getopt.h>
71 #include <libgen.h>
72 #include <limits.h>
73 #include <setjmp.h>
74 #include <stdbool.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 <capsicum_helpers.h>
83 #include <vmmapi.h>
84 
85 #include "userboot.h"
86 
87 #define	MB	(1024 * 1024UL)
88 #define	GB	(1024 * 1024 * 1024UL)
89 #define	BSP	0
90 
91 #define	NDISKS	32
92 
93 /*
94  * Reason for our loader reload and reentry, though these aren't really used
95  * at the moment.
96  */
97 enum {
98 	/* 0 cannot be allocated; setjmp(3) return. */
99 	JMP_SWAPLOADER = 0x01,
100 	JMP_REBOOT,
101 };
102 
103 struct env {
104 	char *str;	/* name=value */
105 	SLIST_ENTRY(env) next;
106 };
107 
108 struct bhyveload_softc {
109 	struct vmctx *ctx;
110 	struct vcpu *vcpu;
111 
112 	SLIST_HEAD(, env) envhead;
113 
114 	struct termios oldterm;
115 
116 	char *loader;
117 
118 	int boot_dfd;			/* directory fd for host /boot */
119 	int consin_fd, consout_fd;	/* console I/O fds */
120 	int explicit_loader_fd;		/* handle for alternate userboot.so */
121 	int hostbase_fd;		/* host root directory fd */
122 
123 	int ndisks;
124 	int disk_fd[NDISKS];
125 
126 	uint64_t gdtbase;
127 	uint64_t cr3;
128 	uint64_t rsp;
129 
130 	jmp_buf jb;
131 };
132 
133 static void cb_exit(void *arg, int v);
134 
135 /*
136  * Console i/o callbacks
137  */
138 
139 static void
cb_putc(void * arg,int ch)140 cb_putc(void *arg, int ch)
141 {
142 	struct bhyveload_softc *sc = arg;
143 	char c = ch;
144 
145 	(void)write(sc->consout_fd, &c, 1);
146 }
147 
148 static int
cb_getc(void * arg)149 cb_getc(void *arg)
150 {
151 	struct bhyveload_softc *sc = arg;
152 	char c;
153 	ssize_t nread;
154 
155 	nread = read(sc->consin_fd, &c, 1);
156 	if (nread == 0)
157 		cb_exit(sc, USERBOOT_EXIT_QUIT);
158 	if (nread == 1)
159 		return (c);
160 	return (-1);
161 }
162 
163 static int
cb_poll(void * arg)164 cb_poll(void *arg)
165 {
166 	struct bhyveload_softc *sc = arg;
167 	int n;
168 
169 	if (ioctl(sc->consin_fd, FIONREAD, &n) >= 0)
170 		return (n > 0);
171 	return (0);
172 }
173 
174 /*
175  * Host filesystem i/o callbacks
176  */
177 
178 struct cb_file {
179 	int cf_isdir;
180 	size_t cf_size;
181 	struct stat cf_stat;
182 	union {
183 		int fd;
184 		DIR *dir;
185 	} cf_u;
186 };
187 
188 static int
cb_open(void * arg,const char * filename,void ** hp)189 cb_open(void *arg, const char *filename, void **hp)
190 {
191 	struct bhyveload_softc *sc = arg;
192 	struct cb_file *cf;
193 	struct stat sb;
194 	int fd;
195 
196 	cf = NULL;
197 	if (sc->hostbase_fd == -1)
198 		return (ENOENT);
199 
200 	/* Absolute paths are relative to our hostbase, chop off leading /. */
201 	if (filename[0] == '/')
202 		filename++;
203 
204 	/* Lookup of /, use . instead. */
205 	if (filename[0] == '\0')
206 		filename = ".";
207 
208 	/* May be opening the root dir */
209 	fd = openat(sc->hostbase_fd, filename, O_RDONLY | O_RESOLVE_BENEATH);
210 	if (fd < 0)
211 		return (errno);
212 
213 	if (fstat(fd, &sb) < 0) {
214 		int serrno = errno;
215 
216 		close(fd);
217 		return (serrno);
218 	} else if (!S_ISDIR(sb.st_mode) && !S_ISREG(sb.st_mode)) {
219 		close(fd);
220 		return (EINVAL);
221 	}
222 
223 	cf = malloc(sizeof(struct cb_file));
224 	if (cf == NULL) {
225 		close(fd);
226 		return (ENOMEM);
227 	}
228 
229 	cf->cf_stat = sb;
230 	cf->cf_size = cf->cf_stat.st_size;
231 
232 	if (S_ISDIR(cf->cf_stat.st_mode)) {
233 		cf->cf_isdir = 1;
234 		cf->cf_u.dir = fdopendir(fd);
235 		if (cf->cf_u.dir == NULL) {
236 			close(fd);
237 			free(cf);
238 			return (ENOMEM);
239 		}
240 	} else {
241 		cf->cf_isdir = 0;
242 		cf->cf_u.fd = fd;
243 	}
244 	*hp = cf;
245 	return (0);
246 }
247 
248 static int
cb_close(void * arg __unused,void * h)249 cb_close(void *arg __unused, void *h)
250 {
251 	struct cb_file *cf = h;
252 
253 	if (cf->cf_isdir)
254 		closedir(cf->cf_u.dir);
255 	else
256 		close(cf->cf_u.fd);
257 	free(cf);
258 
259 	return (0);
260 }
261 
262 static int
cb_isdir(void * arg __unused,void * h)263 cb_isdir(void *arg __unused, void *h)
264 {
265 	struct cb_file *cf = h;
266 
267 	return (cf->cf_isdir);
268 }
269 
270 static int
cb_read(void * arg __unused,void * h,void * buf,size_t size,size_t * resid)271 cb_read(void *arg __unused, void *h, void *buf, size_t size, size_t *resid)
272 {
273 	struct cb_file *cf = h;
274 	ssize_t sz;
275 
276 	if (cf->cf_isdir)
277 		return (EINVAL);
278 	sz = read(cf->cf_u.fd, buf, size);
279 	if (sz < 0)
280 		return (EINVAL);
281 	*resid = size - sz;
282 	return (0);
283 }
284 
285 static int
cb_readdir(void * arg __unused,void * h,uint32_t * fileno_return,uint8_t * type_return,size_t * namelen_return,char * name)286 cb_readdir(void *arg __unused, void *h, uint32_t *fileno_return,
287     uint8_t *type_return, size_t *namelen_return, char *name)
288 {
289 	struct cb_file *cf = h;
290 	struct dirent *dp;
291 
292 	if (!cf->cf_isdir)
293 		return (EINVAL);
294 
295 	dp = readdir(cf->cf_u.dir);
296 	if (!dp)
297 		return (ENOENT);
298 
299 	/*
300 	 * Note: d_namlen is in the range 0..255 and therefore less
301 	 * than PATH_MAX so we don't need to test before copying.
302 	 */
303 	*fileno_return = dp->d_fileno;
304 	*type_return = dp->d_type;
305 	*namelen_return = dp->d_namlen;
306 	memcpy(name, dp->d_name, dp->d_namlen);
307 	name[dp->d_namlen] = 0;
308 
309 	return (0);
310 }
311 
312 static int
cb_seek(void * arg __unused,void * h,uint64_t offset,int whence)313 cb_seek(void *arg __unused, void *h, uint64_t offset, int whence)
314 {
315 	struct cb_file *cf = h;
316 
317 	if (cf->cf_isdir)
318 		return (EINVAL);
319 	if (lseek(cf->cf_u.fd, offset, whence) < 0)
320 		return (errno);
321 	return (0);
322 }
323 
324 static int
cb_stat(void * arg __unused,void * h,struct stat * sbp)325 cb_stat(void *arg __unused, void *h, struct stat *sbp)
326 {
327 	struct cb_file *cf = h;
328 
329 	memset(sbp, 0, sizeof(struct stat));
330 	sbp->st_mode = cf->cf_stat.st_mode;
331 	sbp->st_uid = cf->cf_stat.st_uid;
332 	sbp->st_gid = cf->cf_stat.st_gid;
333 	sbp->st_size = cf->cf_stat.st_size;
334 	sbp->st_mtime = cf->cf_stat.st_mtime;
335 	sbp->st_dev = cf->cf_stat.st_dev;
336 	sbp->st_ino = cf->cf_stat.st_ino;
337 
338 	return (0);
339 }
340 
341 /*
342  * Disk image i/o callbacks
343  */
344 
345 static int
cb_diskread(void * arg,int unit,uint64_t from,void * to,size_t size,size_t * resid)346 cb_diskread(void *arg, int unit, uint64_t from, void *to, size_t size,
347     size_t *resid)
348 {
349 	struct bhyveload_softc *sc = arg;
350 	ssize_t n;
351 
352 	if (unit < 0 || unit >= sc->ndisks)
353 		return (EIO);
354 	n = pread(sc->disk_fd[unit], to, size, from);
355 	if (n < 0)
356 		return (errno);
357 	*resid = size - n;
358 	return (0);
359 }
360 
361 static int
cb_diskwrite(void * arg,int unit,uint64_t offset,void * src,size_t size,size_t * resid)362 cb_diskwrite(void *arg, int unit, uint64_t offset, void *src, size_t size,
363     size_t *resid)
364 {
365 	struct bhyveload_softc *sc = arg;
366 	ssize_t n;
367 
368 	if (unit < 0 || unit >= sc->ndisks)
369 		return (EIO);
370 	n = pwrite(sc->disk_fd[unit], src, size, offset);
371 	if (n < 0)
372 		return (errno);
373 	*resid = size - n;
374 	return (0);
375 }
376 
377 static int
cb_diskioctl(void * arg,int unit,u_long cmd,void * data)378 cb_diskioctl(void *arg, int unit, u_long cmd, void *data)
379 {
380 	struct bhyveload_softc *sc = arg;
381 	struct stat sb;
382 
383 	if (unit < 0 || unit >= sc->ndisks)
384 		return (EBADF);
385 
386 	switch (cmd) {
387 	case DIOCGSECTORSIZE:
388 		if (fstat(sc->disk_fd[unit], &sb) != 0)
389 			return (ENOTTY);
390 		if (S_ISCHR(sb.st_mode)) {
391 			if (ioctl(sc->disk_fd[unit], DIOCGSECTORSIZE, data) !=
392 			    0)
393 				return (ENOTTY);
394 		} else {
395 			*(u_int *)data = 512;
396 		}
397 		break;
398 	case DIOCGMEDIASIZE:
399 		if (fstat(sc->disk_fd[unit], &sb) != 0)
400 			return (ENOTTY);
401 		if (S_ISCHR(sb.st_mode) &&
402 		    ioctl(sc->disk_fd[unit], DIOCGMEDIASIZE, &sb.st_size) != 0)
403 			return (ENOTTY);
404 		*(off_t *)data = sb.st_size;
405 		break;
406 	default:
407 		return (ENOTTY);
408 	}
409 
410 	return (0);
411 }
412 
413 /*
414  * Guest virtual machine i/o callbacks
415  */
416 static int
cb_copyin(void * arg,const void * from,uint64_t to,size_t size)417 cb_copyin(void *arg, const void *from, uint64_t to, size_t size)
418 {
419 	struct bhyveload_softc *sc = arg;
420 	char *ptr;
421 
422 	to &= 0x7fffffff;
423 
424 	ptr = vm_map_gpa(sc->ctx, to, size);
425 	if (ptr == NULL)
426 		return (EFAULT);
427 
428 	memcpy(ptr, from, size);
429 	return (0);
430 }
431 
432 static int
cb_copyout(void * arg,uint64_t from,void * to,size_t size)433 cb_copyout(void *arg, uint64_t from, void *to, size_t size)
434 {
435 	struct bhyveload_softc *sc = arg;
436 	char *ptr;
437 
438 	from &= 0x7fffffff;
439 
440 	ptr = vm_map_gpa(sc->ctx, from, size);
441 	if (ptr == NULL)
442 		return (EFAULT);
443 
444 	memcpy(to, ptr, size);
445 	return (0);
446 }
447 
448 static void
cb_setreg(void * arg,int r,uint64_t v)449 cb_setreg(void *arg, int r, uint64_t v)
450 {
451 	struct bhyveload_softc *sc = arg;
452 	int error;
453 	enum vm_reg_name vmreg;
454 
455 	vmreg = VM_REG_LAST;
456 
457 	switch (r) {
458 	case 4:
459 		vmreg = VM_REG_GUEST_RSP;
460 		sc->rsp = v;
461 		break;
462 	default:
463 		break;
464 	}
465 
466 	if (vmreg == VM_REG_LAST) {
467 		printf("test_setreg(%d): not implemented\n", r);
468 		cb_exit(sc, USERBOOT_EXIT_QUIT);
469 	}
470 
471 	error = vm_set_register(sc->vcpu, vmreg, v);
472 	if (error) {
473 		perror("vm_set_register");
474 		cb_exit(sc, USERBOOT_EXIT_QUIT);
475 	}
476 }
477 
478 static void
cb_setmsr(void * arg,int r,uint64_t v)479 cb_setmsr(void *arg, int r, uint64_t v)
480 {
481 	struct bhyveload_softc *sc = arg;
482 	int error;
483 	enum vm_reg_name vmreg;
484 
485 	vmreg = VM_REG_LAST;
486 
487 	switch (r) {
488 	case MSR_EFER:
489 		vmreg = VM_REG_GUEST_EFER;
490 		break;
491 	default:
492 		break;
493 	}
494 
495 	if (vmreg == VM_REG_LAST) {
496 		printf("test_setmsr(%d): not implemented\n", r);
497 		cb_exit(sc, USERBOOT_EXIT_QUIT);
498 	}
499 
500 	error = vm_set_register(sc->vcpu, vmreg, v);
501 	if (error) {
502 		perror("vm_set_msr");
503 		cb_exit(sc, USERBOOT_EXIT_QUIT);
504 	}
505 }
506 
507 static void
cb_setcr(void * arg,int r,uint64_t v)508 cb_setcr(void *arg, int r, uint64_t v)
509 {
510 	struct bhyveload_softc *sc = arg;
511 	int error;
512 	enum vm_reg_name vmreg;
513 
514 	vmreg = VM_REG_LAST;
515 
516 	switch (r) {
517 	case 0:
518 		vmreg = VM_REG_GUEST_CR0;
519 		break;
520 	case 3:
521 		vmreg = VM_REG_GUEST_CR3;
522 		sc->cr3 = v;
523 		break;
524 	case 4:
525 		vmreg = VM_REG_GUEST_CR4;
526 		break;
527 	default:
528 		break;
529 	}
530 
531 	if (vmreg == VM_REG_LAST) {
532 		printf("test_setcr(%d): not implemented\n", r);
533 		cb_exit(sc, USERBOOT_EXIT_QUIT);
534 	}
535 
536 	error = vm_set_register(sc->vcpu, vmreg, v);
537 	if (error) {
538 		perror("vm_set_cr");
539 		cb_exit(sc, USERBOOT_EXIT_QUIT);
540 	}
541 }
542 
543 static void
cb_setgdt(void * arg,uint64_t base,size_t size)544 cb_setgdt(void *arg, uint64_t base, size_t size)
545 {
546 	struct bhyveload_softc *sc = arg;
547 	int error;
548 
549 	error = vm_set_desc(sc->vcpu, VM_REG_GUEST_GDTR, base, size - 1, 0);
550 	if (error != 0) {
551 		perror("vm_set_desc(gdt)");
552 		cb_exit(sc, USERBOOT_EXIT_QUIT);
553 	}
554 
555 	sc->gdtbase = base;
556 }
557 
558 static void
cb_exec(void * arg,uint64_t rip)559 cb_exec(void *arg, uint64_t rip)
560 {
561 	struct bhyveload_softc *sc = arg;
562 	int error;
563 
564 	if (sc->cr3 == 0)
565 		error = vm_setup_freebsd_registers_i386(sc->vcpu, rip,
566 		    sc->gdtbase, sc->rsp);
567 	else
568 		error = vm_setup_freebsd_registers(sc->vcpu, rip,
569 		    sc->cr3, sc->gdtbase, sc->rsp);
570 	if (error) {
571 		perror("vm_setup_freebsd_registers");
572 		cb_exit(sc, USERBOOT_EXIT_QUIT);
573 	}
574 
575 	cb_exit(sc, 0);
576 }
577 
578 /*
579  * Misc
580  */
581 
582 static void
cb_delay(void * arg __unused,int usec)583 cb_delay(void *arg __unused, int usec)
584 {
585 
586 	usleep(usec);
587 }
588 
589 static void
cb_exit(void * arg,int v)590 cb_exit(void *arg, int v)
591 {
592 	struct bhyveload_softc *sc = arg;
593 
594 	tcsetattr(sc->consout_fd, TCSAFLUSH, &sc->oldterm);
595 	if (v == USERBOOT_EXIT_REBOOT)
596 		longjmp(sc->jb, JMP_REBOOT);
597 	exit(v);
598 }
599 
600 static void
cb_getmem(void * arg,uint64_t * ret_lowmem,uint64_t * ret_highmem)601 cb_getmem(void *arg, uint64_t *ret_lowmem, uint64_t *ret_highmem)
602 {
603 	struct bhyveload_softc *sc = arg;
604 
605 	*ret_lowmem = vm_get_lowmem_size(sc->ctx);
606 	*ret_highmem = vm_get_highmem_size(sc->ctx);
607 }
608 
609 static void
addenv(struct bhyveload_softc * sc,const char * str)610 addenv(struct bhyveload_softc *sc, const char *str)
611 {
612 	struct env *env;
613 
614 	env = malloc(sizeof(struct env));
615 	if (env == NULL)
616 		err(EX_OSERR, "malloc");
617 	env->str = strdup(str);
618 	if (env->str == NULL)
619 		err(EX_OSERR, "strdup");
620 	SLIST_INSERT_HEAD(&sc->envhead, env, next);
621 }
622 
623 static char *
cb_getenv(void * arg,int num)624 cb_getenv(void *arg, int num)
625 {
626 	struct bhyveload_softc *sc = arg;
627 	struct env *env;
628 	int i;
629 
630 	i = 0;
631 	SLIST_FOREACH(env, &sc->envhead, next) {
632 		if (i == num)
633 			return (env->str);
634 		i++;
635 	}
636 
637 	return (NULL);
638 }
639 
640 static int
cb_vm_set_register(void * arg,int vcpuid,int reg,uint64_t val)641 cb_vm_set_register(void *arg, int vcpuid, int reg, uint64_t val)
642 {
643 	struct bhyveload_softc *sc = arg;
644 
645 	assert(vcpuid == BSP);
646 	return (vm_set_register(sc->vcpu, reg, val));
647 }
648 
649 static int
cb_vm_set_desc(void * arg,int vcpuid,int reg,uint64_t base,u_int limit,u_int access)650 cb_vm_set_desc(void *arg, int vcpuid, int reg, uint64_t base, u_int limit,
651     u_int access)
652 {
653 	struct bhyveload_softc *sc = arg;
654 
655 	assert(vcpuid == BSP);
656 	return (vm_set_desc(sc->vcpu, reg, base, limit, access));
657 }
658 
659 static void
cb_swap_interpreter(void * arg,const char * interp_req)660 cb_swap_interpreter(void *arg, const char *interp_req)
661 {
662 	struct bhyveload_softc *sc = arg;
663 
664 	/*
665 	 * If the user specified a loader but we detected a mismatch, we should
666 	 * not try to pivot to a different loader on them.
667 	 */
668 	free(sc->loader);
669 	if (sc->explicit_loader_fd != -1) {
670 		perror("requested loader interpreter does not match guest userboot");
671 		cb_exit(sc, 1);
672 	}
673 	if (interp_req == NULL || *interp_req == '\0') {
674 		perror("guest failed to request an interpreter");
675 		cb_exit(sc, 1);
676 	}
677 	if (asprintf(&sc->loader, "userboot_%s.so", interp_req) == -1)
678 		err(EX_OSERR, "malloc");
679 	longjmp(sc->jb, JMP_SWAPLOADER);
680 }
681 
682 static void
cb_accept_interpreter(void * arg)683 cb_accept_interpreter(void *arg)
684 {
685 	struct bhyveload_softc *sc = arg;
686 
687 	if (sc->boot_dfd == -1)
688 		return;
689 
690 	close(sc->boot_dfd);
691 	sc->boot_dfd = -1;
692 }
693 
694 static struct loader_callbacks cb = {
695 	.getc = cb_getc,
696 	.putc = cb_putc,
697 	.poll = cb_poll,
698 
699 	.open = cb_open,
700 	.close = cb_close,
701 	.isdir = cb_isdir,
702 	.read = cb_read,
703 	.readdir = cb_readdir,
704 	.seek = cb_seek,
705 	.stat = cb_stat,
706 
707 	.diskread = cb_diskread,
708 	.diskwrite = cb_diskwrite,
709 	.diskioctl = cb_diskioctl,
710 
711 	.copyin = cb_copyin,
712 	.copyout = cb_copyout,
713 	.setreg = cb_setreg,
714 	.setmsr = cb_setmsr,
715 	.setcr = cb_setcr,
716 	.setgdt = cb_setgdt,
717 	.exec = cb_exec,
718 
719 	.delay = cb_delay,
720 	.exit = cb_exit,
721 	.getmem = cb_getmem,
722 
723 	.getenv = cb_getenv,
724 
725 	/* Version 4 additions */
726 	.vm_set_register = cb_vm_set_register,
727 	.vm_set_desc = cb_vm_set_desc,
728 
729 	/* Version 5 additions */
730 	.swap_interpreter = cb_swap_interpreter,
731 
732 	/* Version 6 additions */
733 	.accept_interpreter = cb_accept_interpreter,
734 };
735 
736 static int
altcons_open(struct bhyveload_softc * sc,const char * path)737 altcons_open(struct bhyveload_softc *sc, const char *path)
738 {
739 	struct stat sb;
740 	int err;
741 	int fd;
742 
743 	/*
744 	 * Allow stdio to be passed in so that the same string
745 	 * can be used for the bhyveload console and bhyve com-port
746 	 * parameters
747 	 */
748 	if (!strcmp(path, "stdio"))
749 		return (0);
750 
751 	err = stat(path, &sb);
752 	if (err == 0) {
753 		if (!S_ISCHR(sb.st_mode))
754 			err = ENOTSUP;
755 		else {
756 			fd = open(path, O_RDWR | O_NONBLOCK);
757 			if (fd < 0)
758 				err = errno;
759 			else
760 				sc->consin_fd = sc->consout_fd = fd;
761 		}
762 	}
763 
764 	return (err);
765 }
766 
767 static int
disk_open(struct bhyveload_softc * sc,const char * path)768 disk_open(struct bhyveload_softc *sc, const char *path)
769 {
770 	struct stat sbuf;
771 	off_t size;
772 	int fd, ret, sectsz;
773 
774 	if (sc->ndisks >= NDISKS)
775 		return (ERANGE);
776 
777 	fd = open(path, O_RDWR);
778 	if (fd < 0)
779 		fd = open(path, O_RDONLY);
780 	if (fd < 0)
781 		return (errno);
782 	if (fstat(fd, &sbuf) < 0) {
783 		ret = errno;
784 		goto err;
785 	}
786 
787 	size = sbuf.st_size;
788 	sectsz = DEV_BSIZE;
789 	if (S_ISCHR(sbuf.st_mode)) {
790 		if (ioctl(fd, DIOCGMEDIASIZE, &size) < 0 ||
791 		    ioctl(fd, DIOCGSECTORSIZE, &sectsz) < 0) {
792 			ret = errno;
793 			goto err;
794 		}
795 		assert(size != 0);
796 		assert(sectsz != 0);
797 	}
798 
799 	sc->disk_fd[sc->ndisks++] = fd;
800 
801 	return (0);
802 err:
803 	close(fd);
804 	return (ret);
805 }
806 
807 static void
usage(const char * progname)808 usage(const char *progname)
809 {
810 
811 	fprintf(stderr,
812 	    "usage: %s [-S][-c <console-device>] [-d <disk-path>] [-e <name=value>]\n"
813 	    "       %*s [-h <host-path>] [-m memsize[K|k|M|m|G|g|T|t]] <vmname>\n",
814 	    progname,
815 	    (int)strlen(progname), "");
816 	exit(1);
817 }
818 
819 static int
hostbase_open(const char * base)820 hostbase_open(const char *base)
821 {
822 	cap_rights_t rights;
823 	int fd;
824 
825 	fd = open(base, O_DIRECTORY | O_PATH);
826 	if (fd == -1)
827 		err(EX_OSERR, "open");
828 
829 	if (caph_rights_limit(fd,
830 	    cap_rights_init(&rights, CAP_FSTATAT, CAP_LOOKUP, CAP_PREAD)) < 0)
831 		err(EX_OSERR, "caph_rights_limit");
832 
833 	return (fd);
834 }
835 
836 static void *
loader_open(struct bhyveload_softc * sc)837 loader_open(struct bhyveload_softc *sc)
838 {
839 	void *handle;
840 	int fd;
841 
842 	if (sc->loader == NULL) {
843 		sc->loader = strdup("userboot.so");
844 		if (sc->loader == NULL)
845 			err(EX_OSERR, "malloc");
846 	}
847 
848 	assert(sc->boot_dfd >= 0 || sc->explicit_loader_fd >= 0);
849 	if (sc->explicit_loader_fd >= 0)
850 		fd = sc->explicit_loader_fd;
851 	else
852 		fd = openat(sc->boot_dfd, sc->loader,
853 		    O_RDONLY | O_RESOLVE_BENEATH);
854 	if (fd == -1)
855 		err(EX_OSERR, "openat");
856 
857 	handle = fdlopen(fd, RTLD_LOCAL);
858 	if (handle == NULL)
859 		errx(EX_OSERR, "dlopen: %s", dlerror());
860 	if (fd != sc->explicit_loader_fd)
861 		close(fd);
862 
863 	return (handle);
864 }
865 
866 int
main(int argc,char ** argv)867 main(int argc, char** argv)
868 {
869 	struct bhyveload_softc sc;
870 	struct termios term;
871 	void (*func)(struct loader_callbacks *, void *, int, int);
872 	void * volatile loader_hdl;
873 	char *vmname, *progname;
874 	uint64_t mem_size;
875 	int opt, error, memflags;
876 	bool need_reinit;
877 
878 	progname = basename(argv[0]);
879 
880 	memset(&sc, 0, sizeof(sc));
881 	SLIST_INIT(&sc.envhead);
882 	sc.boot_dfd = sc.explicit_loader_fd = sc.hostbase_fd = -1;
883 	sc.consin_fd = STDIN_FILENO;
884 	sc.consout_fd = STDOUT_FILENO;
885 
886 	memflags = 0;
887 	mem_size = 256 * MB;
888 
889 	while ((opt = getopt(argc, argv, "CSc:d:e:h:l:m:")) != -1) {
890 		switch (opt) {
891 		case 'c':
892 			error = altcons_open(&sc, optarg);
893 			if (error != 0)
894 				errx(EX_USAGE, "Could not open '%s'", optarg);
895 			break;
896 
897 		case 'd':
898 			error = disk_open(&sc, optarg);
899 			if (error != 0)
900 				errc(EX_USAGE, error, "Could not open '%s'", optarg);
901 			break;
902 
903 		case 'e':
904 			addenv(&sc, optarg);
905 			break;
906 
907 		case 'h':
908 			if (sc.hostbase_fd != -1)
909 				close(sc.hostbase_fd);
910 			sc.hostbase_fd = hostbase_open(optarg);
911 			break;
912 
913 		case 'l':
914 			if (sc.loader != NULL)
915 				errx(EX_USAGE, "-l can only be given once");
916 			sc.loader = strdup(optarg);
917 			if (sc.loader == NULL)
918 				err(EX_OSERR, "malloc");
919 			sc.explicit_loader_fd = open(sc.loader, O_RDONLY);
920 			if (sc.explicit_loader_fd == -1)
921 				err(EX_OSERR, "%s", sc.loader);
922 			break;
923 
924 		case 'm':
925 			error = vm_parse_memsize(optarg, &mem_size);
926 			if (error != 0)
927 				errx(EX_USAGE, "Invalid memsize '%s'", optarg);
928 			break;
929 		case 'C':
930 			memflags |= VM_MEM_F_INCORE;
931 			break;
932 		case 'S':
933 			memflags |= VM_MEM_F_WIRED;
934 			break;
935 		case '?':
936 			usage(progname);
937 		}
938 	}
939 
940 	argc -= optind;
941 	argv += optind;
942 
943 	if (argc != 1)
944 		usage(progname);
945 
946 	vmname = argv[0];
947 
948 	/*
949 	 * If we weren't given an explicit loader to use, we need to support the
950 	 * guest requesting a different one.
951 	 */
952 	if (sc.explicit_loader_fd == -1) {
953 		cap_rights_t rights;
954 		int fd;
955 
956 		fd = open("/boot", O_DIRECTORY | O_PATH);
957 		if (fd == -1)
958 			err(1, "open");
959 
960 		/*
961 		 * boot_dfd will be used to do a lookup of our loader and do an
962 		 * fdlopen(3) on the loader; thus, we need mmap(2) in addition
963 		 * to the more usual lookup rights.
964 		 */
965 		if (caph_rights_limit(fd, cap_rights_init(&rights,
966 		    CAP_FSTATAT, CAP_LOOKUP, CAP_MMAP_RX, CAP_PREAD)) < 0)
967 			err(1, "caph_rights_limit");
968 		sc.boot_dfd = fd;
969 	}
970 
971 	need_reinit = false;
972 	error = vm_create(vmname);
973 	if (error) {
974 		if (errno != EEXIST)
975 			err(1, "vm_create");
976 		need_reinit = true;
977 	}
978 
979 	sc.ctx = vm_open(vmname);
980 	if (sc.ctx == NULL)
981 		err(1, "vm_open");
982 
983 	sc.vcpu = vm_vcpu_open(sc.ctx, BSP);
984 	if (sc.vcpu == NULL)
985 		err(1, "vm_vcpu_open");
986 
987 	caph_cache_catpages();
988 	if (caph_enter() < 0)
989 		err(1, "caph_enter");
990 
991 	/*
992 	 * setjmp in the case the guest wants to swap out interpreter,
993 	 * cb_swap_interpreter will swap out loader as appropriate and set
994 	 * need_reinit so that we end up in a clean state once again.
995 	 */
996 	loader_hdl = NULL;
997 	if (setjmp(sc.jb) != 0) {
998 		dlclose(__DEVOLATILE(void *, loader_hdl));
999 		loader_hdl = NULL;
1000 
1001 		need_reinit = true;
1002 	}
1003 
1004 	if (need_reinit) {
1005 		error = vm_reinit(sc.ctx);
1006 		if (error)
1007 			err(1, "vm_reinit");
1008 	}
1009 
1010 	vm_set_memflags(sc.ctx, memflags);
1011 	error = vm_setup_memory(sc.ctx, mem_size, VM_MMAP_ALL);
1012 	if (error)
1013 		err(1, "vm_setup_memory");
1014 
1015 	loader_hdl = loader_open(&sc);
1016 	func = dlsym(__DEVOLATILE(void *, loader_hdl), "loader_main");
1017 	if (!func)
1018 		errx(1, "dlsym: %s", dlerror());
1019 
1020 	tcgetattr(sc.consout_fd, &sc.oldterm);
1021 	term = sc.oldterm;
1022 	cfmakeraw(&term);
1023 	term.c_cflag |= CLOCAL;
1024 	tcsetattr(sc.consout_fd, TCSAFLUSH, &term);
1025 
1026 	addenv(&sc, "smbios.bios.vendor=BHYVE");
1027 	addenv(&sc, "boot_serial=1");
1028 
1029 	func(&cb, &sc, USERBOOT_VERSION_6, sc.ndisks);
1030 
1031 	return (0);
1032 }
1033