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, §sz) < 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