]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bhyveload/bhyveload.c
Import the skein hashing algorithm, based on the threefish block cipher
[FreeBSD/FreeBSD.git] / usr.sbin / bhyveload / bhyveload.c
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 #include <sys/queue.h>
64
65 #include <machine/specialreg.h>
66 #include <machine/vmm.h>
67
68 #include <dirent.h>
69 #include <dlfcn.h>
70 #include <errno.h>
71 #include <err.h>
72 #include <fcntl.h>
73 #include <getopt.h>
74 #include <libgen.h>
75 #include <limits.h>
76 #include <stdio.h>
77 #include <stdlib.h>
78 #include <string.h>
79 #include <sysexits.h>
80 #include <termios.h>
81 #include <unistd.h>
82
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 static char *host_base;
94 static struct termios term, oldterm;
95 static int disk_fd[NDISKS];
96 static int ndisks;
97 static int consin_fd, consout_fd;
98
99 static char *vmname, *progname;
100 static struct vmctx *ctx;
101
102 static uint64_t gdtbase, cr3, rsp;
103
104 static void cb_exit(void *arg, int v);
105
106 /*
107  * Console i/o callbacks
108  */
109
110 static void
111 cb_putc(void *arg, int ch)
112 {
113         char c = ch;
114
115         (void) write(consout_fd, &c, 1);
116 }
117
118 static int
119 cb_getc(void *arg)
120 {
121         char c;
122
123         if (read(consin_fd, &c, 1) == 1)
124                 return (c);
125         return (-1);
126 }
127
128 static int
129 cb_poll(void *arg)
130 {
131         int n;
132
133         if (ioctl(consin_fd, FIONREAD, &n) >= 0)
134                 return (n > 0);
135         return (0);
136 }
137
138 /*
139  * Host filesystem i/o callbacks
140  */
141
142 struct cb_file {
143         int cf_isdir;
144         size_t cf_size;
145         struct stat cf_stat;
146         union {
147                 int fd;
148                 DIR *dir;
149         } cf_u;
150 };
151
152 static int
153 cb_open(void *arg, const char *filename, void **hp)
154 {
155         struct cb_file *cf;
156         char path[PATH_MAX];
157
158         if (!host_base)
159                 return (ENOENT);
160
161         strlcpy(path, host_base, PATH_MAX);
162         if (path[strlen(path) - 1] == '/')
163                 path[strlen(path) - 1] = 0;
164         strlcat(path, filename, PATH_MAX);
165         cf = malloc(sizeof(struct cb_file));
166         if (stat(path, &cf->cf_stat) < 0) {
167                 free(cf);
168                 return (errno);
169         }
170
171         cf->cf_size = cf->cf_stat.st_size;
172         if (S_ISDIR(cf->cf_stat.st_mode)) {
173                 cf->cf_isdir = 1;
174                 cf->cf_u.dir = opendir(path);
175                 if (!cf->cf_u.dir)
176                         goto out;
177                 *hp = cf;
178                 return (0);
179         }
180         if (S_ISREG(cf->cf_stat.st_mode)) {
181                 cf->cf_isdir = 0;
182                 cf->cf_u.fd = open(path, O_RDONLY);
183                 if (cf->cf_u.fd < 0)
184                         goto out;
185                 *hp = cf;
186                 return (0);
187         }
188
189 out:
190         free(cf);
191         return (EINVAL);
192 }
193
194 static int
195 cb_close(void *arg, void *h)
196 {
197         struct cb_file *cf = h;
198
199         if (cf->cf_isdir)
200                 closedir(cf->cf_u.dir);
201         else
202                 close(cf->cf_u.fd);
203         free(cf);
204
205         return (0);
206 }
207
208 static int
209 cb_isdir(void *arg, void *h)
210 {
211         struct cb_file *cf = h;
212
213         return (cf->cf_isdir);
214 }
215
216 static int
217 cb_read(void *arg, void *h, void *buf, size_t size, size_t *resid)
218 {
219         struct cb_file *cf = h;
220         ssize_t sz;
221
222         if (cf->cf_isdir)
223                 return (EINVAL);
224         sz = read(cf->cf_u.fd, buf, size);
225         if (sz < 0)
226                 return (EINVAL);
227         *resid = size - sz;
228         return (0);
229 }
230
231 static int
232 cb_readdir(void *arg, void *h, uint32_t *fileno_return, uint8_t *type_return,
233            size_t *namelen_return, char *name)
234 {
235         struct cb_file *cf = h;
236         struct dirent *dp;
237
238         if (!cf->cf_isdir)
239                 return (EINVAL);
240
241         dp = readdir(cf->cf_u.dir);
242         if (!dp)
243                 return (ENOENT);
244
245         /*
246          * Note: d_namlen is in the range 0..255 and therefore less
247          * than PATH_MAX so we don't need to test before copying.
248          */
249         *fileno_return = dp->d_fileno;
250         *type_return = dp->d_type;
251         *namelen_return = dp->d_namlen;
252         memcpy(name, dp->d_name, dp->d_namlen);
253         name[dp->d_namlen] = 0;
254
255         return (0);
256 }
257
258 static int
259 cb_seek(void *arg, void *h, uint64_t offset, int whence)
260 {
261         struct cb_file *cf = h;
262
263         if (cf->cf_isdir)
264                 return (EINVAL);
265         if (lseek(cf->cf_u.fd, offset, whence) < 0)
266                 return (errno);
267         return (0);
268 }
269
270 static int
271 cb_stat(void *arg, void *h, int *mode, int *uid, int *gid, uint64_t *size)
272 {
273         struct cb_file *cf = h;
274
275         *mode = cf->cf_stat.st_mode;
276         *uid = cf->cf_stat.st_uid;
277         *gid = cf->cf_stat.st_gid;
278         *size = cf->cf_stat.st_size;
279         return (0);
280 }
281
282 /*
283  * Disk image i/o callbacks
284  */
285
286 static int
287 cb_diskread(void *arg, int unit, uint64_t from, void *to, size_t size,
288             size_t *resid)
289 {
290         ssize_t n;
291
292         if (unit < 0 || unit >= ndisks )
293                 return (EIO);
294         n = pread(disk_fd[unit], to, size, from);
295         if (n < 0)
296                 return (errno);
297         *resid = size - n;
298         return (0);
299 }
300
301 static int
302 cb_diskioctl(void *arg, int unit, u_long cmd, void *data)
303 {
304         struct stat sb;
305
306         if (unit < 0 || unit >= ndisks)
307                 return (EBADF);
308
309         switch (cmd) {
310         case DIOCGSECTORSIZE:
311                 *(u_int *)data = 512;
312                 break;
313         case DIOCGMEDIASIZE:
314                 if (fstat(disk_fd[unit], &sb) == 0)
315                         *(off_t *)data = sb.st_size;
316                 else
317                         return (ENOTTY);
318                 break;
319         default:
320                 return (ENOTTY);
321         }
322
323         return (0);
324 }
325
326 /*
327  * Guest virtual machine i/o callbacks
328  */
329 static int
330 cb_copyin(void *arg, const void *from, uint64_t to, size_t size)
331 {
332         char *ptr;
333
334         to &= 0x7fffffff;
335
336         ptr = vm_map_gpa(ctx, to, size);
337         if (ptr == NULL)
338                 return (EFAULT);
339
340         memcpy(ptr, from, size);
341         return (0);
342 }
343
344 static int
345 cb_copyout(void *arg, uint64_t from, void *to, size_t size)
346 {
347         char *ptr;
348
349         from &= 0x7fffffff;
350
351         ptr = vm_map_gpa(ctx, from, size);
352         if (ptr == NULL)
353                 return (EFAULT);
354
355         memcpy(to, ptr, size);
356         return (0);
357 }
358
359 static void
360 cb_setreg(void *arg, int r, uint64_t v)
361 {
362         int error;
363         enum vm_reg_name vmreg;
364         
365         vmreg = VM_REG_LAST;
366
367         switch (r) {
368         case 4:
369                 vmreg = VM_REG_GUEST_RSP;
370                 rsp = v;
371                 break;
372         default:
373                 break;
374         }
375
376         if (vmreg == VM_REG_LAST) {
377                 printf("test_setreg(%d): not implemented\n", r);
378                 cb_exit(NULL, USERBOOT_EXIT_QUIT);
379         }
380
381         error = vm_set_register(ctx, BSP, vmreg, v);
382         if (error) {
383                 perror("vm_set_register");
384                 cb_exit(NULL, USERBOOT_EXIT_QUIT);
385         }
386 }
387
388 static void
389 cb_setmsr(void *arg, int r, uint64_t v)
390 {
391         int error;
392         enum vm_reg_name vmreg;
393         
394         vmreg = VM_REG_LAST;
395
396         switch (r) {
397         case MSR_EFER:
398                 vmreg = VM_REG_GUEST_EFER;
399                 break;
400         default:
401                 break;
402         }
403
404         if (vmreg == VM_REG_LAST) {
405                 printf("test_setmsr(%d): not implemented\n", r);
406                 cb_exit(NULL, USERBOOT_EXIT_QUIT);
407         }
408
409         error = vm_set_register(ctx, BSP, vmreg, v);
410         if (error) {
411                 perror("vm_set_msr");
412                 cb_exit(NULL, USERBOOT_EXIT_QUIT);
413         }
414 }
415
416 static void
417 cb_setcr(void *arg, int r, uint64_t v)
418 {
419         int error;
420         enum vm_reg_name vmreg;
421         
422         vmreg = VM_REG_LAST;
423
424         switch (r) {
425         case 0:
426                 vmreg = VM_REG_GUEST_CR0;
427                 break;
428         case 3:
429                 vmreg = VM_REG_GUEST_CR3;
430                 cr3 = v;
431                 break;
432         case 4:
433                 vmreg = VM_REG_GUEST_CR4;
434                 break;
435         default:
436                 break;
437         }
438
439         if (vmreg == VM_REG_LAST) {
440                 printf("test_setcr(%d): not implemented\n", r);
441                 cb_exit(NULL, USERBOOT_EXIT_QUIT);
442         }
443
444         error = vm_set_register(ctx, BSP, vmreg, v);
445         if (error) {
446                 perror("vm_set_cr");
447                 cb_exit(NULL, USERBOOT_EXIT_QUIT);
448         }
449 }
450
451 static void
452 cb_setgdt(void *arg, uint64_t base, size_t size)
453 {
454         int error;
455
456         error = vm_set_desc(ctx, BSP, VM_REG_GUEST_GDTR, base, size - 1, 0);
457         if (error != 0) {
458                 perror("vm_set_desc(gdt)");
459                 cb_exit(NULL, USERBOOT_EXIT_QUIT);
460         }
461
462         gdtbase = base;
463 }
464
465 static void
466 cb_exec(void *arg, uint64_t rip)
467 {
468         int error;
469
470         if (cr3 == 0)
471                 error = vm_setup_freebsd_registers_i386(ctx, BSP, rip, gdtbase,
472                     rsp);
473         else
474                 error = vm_setup_freebsd_registers(ctx, BSP, rip, cr3, gdtbase,
475                     rsp);
476         if (error) {
477                 perror("vm_setup_freebsd_registers");
478                 cb_exit(NULL, USERBOOT_EXIT_QUIT);
479         }
480
481         cb_exit(NULL, 0);
482 }
483
484 /*
485  * Misc
486  */
487
488 static void
489 cb_delay(void *arg, int usec)
490 {
491
492         usleep(usec);
493 }
494
495 static void
496 cb_exit(void *arg, int v)
497 {
498
499         tcsetattr(consout_fd, TCSAFLUSH, &oldterm);
500         exit(v);
501 }
502
503 static void
504 cb_getmem(void *arg, uint64_t *ret_lowmem, uint64_t *ret_highmem)
505 {
506
507         *ret_lowmem = vm_get_lowmem_size(ctx);
508         *ret_highmem = vm_get_highmem_size(ctx);
509 }
510
511 struct env {
512         const char *str;        /* name=value */
513         SLIST_ENTRY(env) next;
514 };
515
516 static SLIST_HEAD(envhead, env) envhead;
517
518 static void
519 addenv(const char *str)
520 {
521         struct env *env;
522
523         env = malloc(sizeof(struct env));
524         env->str = str;
525         SLIST_INSERT_HEAD(&envhead, env, next);
526 }
527
528 static const char *
529 cb_getenv(void *arg, int num)
530 {
531         int i;
532         struct env *env;
533
534         i = 0;
535         SLIST_FOREACH(env, &envhead, next) {
536                 if (i == num)
537                         return (env->str);
538                 i++;
539         }
540
541         return (NULL);
542 }
543
544 static int
545 cb_vm_set_register(void *arg, int vcpu, int reg, uint64_t val)
546 {
547
548         return (vm_set_register(ctx, vcpu, reg, val));
549 }
550
551 static int
552 cb_vm_set_desc(void *arg, int vcpu, int reg, uint64_t base, u_int limit,
553     u_int access)
554 {
555
556         return (vm_set_desc(ctx, vcpu, reg, base, limit, access));
557 }
558
559 static struct loader_callbacks cb = {
560         .getc = cb_getc,
561         .putc = cb_putc,
562         .poll = cb_poll,
563
564         .open = cb_open,
565         .close = cb_close,
566         .isdir = cb_isdir,
567         .read = cb_read,
568         .readdir = cb_readdir,
569         .seek = cb_seek,
570         .stat = cb_stat,
571
572         .diskread = cb_diskread,
573         .diskioctl = cb_diskioctl,
574
575         .copyin = cb_copyin,
576         .copyout = cb_copyout,
577         .setreg = cb_setreg,
578         .setmsr = cb_setmsr,
579         .setcr = cb_setcr,
580         .setgdt = cb_setgdt,
581         .exec = cb_exec,
582
583         .delay = cb_delay,
584         .exit = cb_exit,
585         .getmem = cb_getmem,
586
587         .getenv = cb_getenv,
588
589         /* Version 4 additions */
590         .vm_set_register = cb_vm_set_register,
591         .vm_set_desc = cb_vm_set_desc,
592 };
593
594 static int
595 altcons_open(char *path)
596 {
597         struct stat sb;
598         int err;
599         int fd;
600
601         /*
602          * Allow stdio to be passed in so that the same string
603          * can be used for the bhyveload console and bhyve com-port
604          * parameters
605          */
606         if (!strcmp(path, "stdio"))
607                 return (0);
608
609         err = stat(path, &sb);
610         if (err == 0) {
611                 if (!S_ISCHR(sb.st_mode))
612                         err = ENOTSUP;
613                 else {
614                         fd = open(path, O_RDWR | O_NONBLOCK);
615                         if (fd < 0)
616                                 err = errno;
617                         else
618                                 consin_fd = consout_fd = fd;
619                 }
620         }
621
622         return (err);
623 }
624
625 static int
626 disk_open(char *path)
627 {
628         int err, fd;
629
630         if (ndisks >= NDISKS)
631                 return (ERANGE);
632
633         err = 0;
634         fd = open(path, O_RDONLY);
635
636         if (fd > 0) {
637                 disk_fd[ndisks] = fd;
638                 ndisks++;
639         } else 
640                 err = errno;
641
642         return (err);
643 }
644
645 static void
646 usage(void)
647 {
648
649         fprintf(stderr,
650             "usage: %s [-S][-c <console-device>] [-d <disk-path>] [-e <name=value>]\n"
651             "       %*s [-h <host-path>] [-m mem-size] <vmname>\n",
652             progname,
653             (int)strlen(progname), "");
654         exit(1);
655 }
656
657 int
658 main(int argc, char** argv)
659 {
660         char *loader;
661         void *h;
662         void (*func)(struct loader_callbacks *, void *, int, int);
663         uint64_t mem_size;
664         int opt, error, need_reinit, memflags;
665
666         progname = basename(argv[0]);
667
668         loader = NULL;
669
670         memflags = 0;
671         mem_size = 256 * MB;
672
673         consin_fd = STDIN_FILENO;
674         consout_fd = STDOUT_FILENO;
675
676         while ((opt = getopt(argc, argv, "CSc:d:e:h:l:m:")) != -1) {
677                 switch (opt) {
678                 case 'c':
679                         error = altcons_open(optarg);
680                         if (error != 0)
681                                 errx(EX_USAGE, "Could not open '%s'", optarg);
682                         break;
683
684                 case 'd':
685                         error = disk_open(optarg);
686                         if (error != 0)
687                                 errx(EX_USAGE, "Could not open '%s'", optarg);
688                         break;
689
690                 case 'e':
691                         addenv(optarg);
692                         break;
693
694                 case 'h':
695                         host_base = optarg;
696                         break;
697
698                 case 'l':
699                         if (loader != NULL)
700                                 errx(EX_USAGE, "-l can only be given once");
701                         loader = strdup(optarg);
702                         if (loader == NULL)
703                                 err(EX_OSERR, "malloc");
704                         break;
705
706                 case 'm':
707                         error = vm_parse_memsize(optarg, &mem_size);
708                         if (error != 0)
709                                 errx(EX_USAGE, "Invalid memsize '%s'", optarg);
710                         break;
711                 case 'C':
712                         memflags |= VM_MEM_F_INCORE;
713                         break;
714                 case 'S':
715                         memflags |= VM_MEM_F_WIRED;
716                         break;
717                 case '?':
718                         usage();
719                 }
720         }
721
722         argc -= optind;
723         argv += optind;
724
725         if (argc != 1)
726                 usage();
727
728         vmname = argv[0];
729
730         need_reinit = 0;
731         error = vm_create(vmname);
732         if (error) {
733                 if (errno != EEXIST) {
734                         perror("vm_create");
735                         exit(1);
736                 }
737                 need_reinit = 1;
738         }
739
740         ctx = vm_open(vmname);
741         if (ctx == NULL) {
742                 perror("vm_open");
743                 exit(1);
744         }
745
746         if (need_reinit) {
747                 error = vm_reinit(ctx);
748                 if (error) {
749                         perror("vm_reinit");
750                         exit(1);
751                 }
752         }
753
754         vm_set_memflags(ctx, memflags);
755         error = vm_setup_memory(ctx, mem_size, VM_MMAP_ALL);
756         if (error) {
757                 perror("vm_setup_memory");
758                 exit(1);
759         }
760
761         if (loader == NULL) {
762                 loader = strdup("/boot/userboot.so");
763                 if (loader == NULL)
764                         err(EX_OSERR, "malloc");
765         }
766         h = dlopen(loader, RTLD_LOCAL);
767         if (!h) {
768                 printf("%s\n", dlerror());
769                 free(loader);
770                 return (1);
771         }
772         func = dlsym(h, "loader_main");
773         if (!func) {
774                 printf("%s\n", dlerror());
775                 free(loader);
776                 return (1);
777         }
778
779         tcgetattr(consout_fd, &term);
780         oldterm = term;
781         cfmakeraw(&term);
782         term.c_cflag |= CLOCAL;
783
784         tcsetattr(consout_fd, TCSAFLUSH, &term);
785
786         addenv("smbios.bios.vendor=BHYVE");
787         addenv("boot_serial=1");
788
789         func(&cb, NULL, USERBOOT_VERSION_4, ndisks);
790
791         free(loader);
792         return (0);
793 }