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