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