]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - usr.sbin/bhyveload/bhyveload.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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
64 #include <machine/specialreg.h>
65 #include <machine/vmm.h>
66
67 #include <dirent.h>
68 #include <dlfcn.h>
69 #include <errno.h>
70 #include <err.h>
71 #include <fcntl.h>
72 #include <getopt.h>
73 #include <limits.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 <vmmapi.h>
82
83 #include "userboot.h"
84
85 #define MB      (1024 * 1024UL)
86 #define GB      (1024 * 1024 * 1024UL)
87 #define BSP     0
88
89 static char *host_base = "/";
90 static struct termios term, oldterm;
91 static int disk_fd = -1;
92
93 static char *vmname, *progname;
94 static struct vmctx *ctx;
95
96 static uint64_t gdtbase, cr3, rsp;
97
98 static void cb_exit(void *arg, int v);
99
100 /*
101  * Console i/o callbacks
102  */
103
104 static void
105 cb_putc(void *arg, int ch)
106 {
107         char c = ch;
108
109         write(1, &c, 1);
110 }
111
112 static int
113 cb_getc(void *arg)
114 {
115         char c;
116
117         if (read(0, &c, 1) == 1)
118                 return (c);
119         return (-1);
120 }
121
122 static int
123 cb_poll(void *arg)
124 {
125         int n;
126
127         if (ioctl(0, FIONREAD, &n) >= 0)
128                 return (n > 0);
129         return (0);
130 }
131
132 /*
133  * Host filesystem i/o callbacks
134  */
135
136 struct cb_file {
137         int cf_isdir;
138         size_t cf_size;
139         struct stat cf_stat;
140         union {
141                 int fd;
142                 DIR *dir;
143         } cf_u;
144 };
145
146 static int
147 cb_open(void *arg, const char *filename, void **hp)
148 {
149         struct stat st;
150         struct cb_file *cf;
151         char path[PATH_MAX];
152
153         if (!host_base)
154                 return (ENOENT);
155
156         strlcpy(path, host_base, PATH_MAX);
157         if (path[strlen(path) - 1] == '/')
158                 path[strlen(path) - 1] = 0;
159         strlcat(path, filename, PATH_MAX);
160         cf = malloc(sizeof(struct cb_file));
161         if (stat(path, &cf->cf_stat) < 0) {
162                 free(cf);
163                 return (errno);
164         }
165
166         cf->cf_size = st.st_size;
167         if (S_ISDIR(cf->cf_stat.st_mode)) {
168                 cf->cf_isdir = 1;
169                 cf->cf_u.dir = opendir(path);
170                 if (!cf->cf_u.dir)
171                         goto out;
172                 *hp = cf;
173                 return (0);
174         }
175         if (S_ISREG(cf->cf_stat.st_mode)) {
176                 cf->cf_isdir = 0;
177                 cf->cf_u.fd = open(path, O_RDONLY);
178                 if (cf->cf_u.fd < 0)
179                         goto out;
180                 *hp = cf;
181                 return (0);
182         }
183
184 out:
185         free(cf);
186         return (EINVAL);
187 }
188
189 static int
190 cb_close(void *arg, void *h)
191 {
192         struct cb_file *cf = h;
193
194         if (cf->cf_isdir)
195                 closedir(cf->cf_u.dir);
196         else
197                 close(cf->cf_u.fd);
198         free(cf);
199
200         return (0);
201 }
202
203 static int
204 cb_isdir(void *arg, void *h)
205 {
206         struct cb_file *cf = h;
207
208         return (cf->cf_isdir);
209 }
210
211 static int
212 cb_read(void *arg, void *h, void *buf, size_t size, size_t *resid)
213 {
214         struct cb_file *cf = h;
215         ssize_t sz;
216
217         if (cf->cf_isdir)
218                 return (EINVAL);
219         sz = read(cf->cf_u.fd, buf, size);
220         if (sz < 0)
221                 return (EINVAL);
222         *resid = size - sz;
223         return (0);
224 }
225
226 static int
227 cb_readdir(void *arg, void *h, uint32_t *fileno_return, uint8_t *type_return,
228            size_t *namelen_return, char *name)
229 {
230         struct cb_file *cf = h;
231         struct dirent *dp;
232
233         if (!cf->cf_isdir)
234                 return (EINVAL);
235
236         dp = readdir(cf->cf_u.dir);
237         if (!dp)
238                 return (ENOENT);
239
240         /*
241          * Note: d_namlen is in the range 0..255 and therefore less
242          * than PATH_MAX so we don't need to test before copying.
243          */
244         *fileno_return = dp->d_fileno;
245         *type_return = dp->d_type;
246         *namelen_return = dp->d_namlen;
247         memcpy(name, dp->d_name, dp->d_namlen);
248         name[dp->d_namlen] = 0;
249
250         return (0);
251 }
252
253 static int
254 cb_seek(void *arg, void *h, uint64_t offset, int whence)
255 {
256         struct cb_file *cf = h;
257
258         if (cf->cf_isdir)
259                 return (EINVAL);
260         if (lseek(cf->cf_u.fd, offset, whence) < 0)
261                 return (errno);
262         return (0);
263 }
264
265 static int
266 cb_stat(void *arg, void *h, int *mode, int *uid, int *gid, uint64_t *size)
267 {
268         struct cb_file *cf = h;
269
270         *mode = cf->cf_stat.st_mode;
271         *uid = cf->cf_stat.st_uid;
272         *gid = cf->cf_stat.st_gid;
273         *size = cf->cf_stat.st_size;
274         return (0);
275 }
276
277 /*
278  * Disk image i/o callbacks
279  */
280
281 static int
282 cb_diskread(void *arg, int unit, uint64_t from, void *to, size_t size,
283             size_t *resid)
284 {
285         ssize_t n;
286
287         if (unit != 0 || disk_fd == -1)
288                 return (EIO);
289         n = pread(disk_fd, to, size, from);
290         if (n < 0)
291                 return (errno);
292         *resid = size - n;
293         return (0);
294 }
295
296 static int
297 cb_diskioctl(void *arg, int unit, u_long cmd, void *data)
298 {
299         struct stat sb;
300
301         if (unit != 0 || disk_fd == -1)
302                 return (EBADF);
303
304         switch (cmd) {
305         case DIOCGSECTORSIZE:
306                 *(u_int *)data = 512;
307                 break;
308         case DIOCGMEDIASIZE:
309                 if (fstat(disk_fd, &sb) == 0)
310                         *(off_t *)data = sb.st_size;
311                 else
312                         return (ENOTTY);
313                 break;
314         default:
315                 return (ENOTTY);
316         }
317
318         return (0);
319 }
320
321 /*
322  * Guest virtual machine i/o callbacks
323  */
324 static int
325 cb_copyin(void *arg, const void *from, uint64_t to, size_t size)
326 {
327         char *ptr;
328
329         to &= 0x7fffffff;
330
331         ptr = vm_map_gpa(ctx, to, size);
332         if (ptr == NULL)
333                 return (EFAULT);
334
335         memcpy(ptr, from, size);
336         return (0);
337 }
338
339 static int
340 cb_copyout(void *arg, uint64_t from, void *to, size_t size)
341 {
342         char *ptr;
343
344         from &= 0x7fffffff;
345
346         ptr = vm_map_gpa(ctx, from, size);
347         if (ptr == NULL)
348                 return (EFAULT);
349
350         memcpy(to, ptr, size);
351         return (0);
352 }
353
354 static void
355 cb_setreg(void *arg, int r, uint64_t v)
356 {
357         int error;
358         enum vm_reg_name vmreg;
359         
360         vmreg = VM_REG_LAST;
361
362         switch (r) {
363         case 4:
364                 vmreg = VM_REG_GUEST_RSP;
365                 rsp = v;
366                 break;
367         default:
368                 break;
369         }
370
371         if (vmreg == VM_REG_LAST) {
372                 printf("test_setreg(%d): not implemented\n", r);
373                 cb_exit(NULL, USERBOOT_EXIT_QUIT);
374         }
375
376         error = vm_set_register(ctx, BSP, vmreg, v);
377         if (error) {
378                 perror("vm_set_register");
379                 cb_exit(NULL, USERBOOT_EXIT_QUIT);
380         }
381 }
382
383 static void
384 cb_setmsr(void *arg, int r, uint64_t v)
385 {
386         int error;
387         enum vm_reg_name vmreg;
388         
389         vmreg = VM_REG_LAST;
390
391         switch (r) {
392         case MSR_EFER:
393                 vmreg = VM_REG_GUEST_EFER;
394                 break;
395         default:
396                 break;
397         }
398
399         if (vmreg == VM_REG_LAST) {
400                 printf("test_setmsr(%d): not implemented\n", r);
401                 cb_exit(NULL, USERBOOT_EXIT_QUIT);
402         }
403
404         error = vm_set_register(ctx, BSP, vmreg, v);
405         if (error) {
406                 perror("vm_set_msr");
407                 cb_exit(NULL, USERBOOT_EXIT_QUIT);
408         }
409 }
410
411 static void
412 cb_setcr(void *arg, int r, uint64_t v)
413 {
414         int error;
415         enum vm_reg_name vmreg;
416         
417         vmreg = VM_REG_LAST;
418
419         switch (r) {
420         case 0:
421                 vmreg = VM_REG_GUEST_CR0;
422                 break;
423         case 3:
424                 vmreg = VM_REG_GUEST_CR3;
425                 cr3 = v;
426                 break;
427         case 4:
428                 vmreg = VM_REG_GUEST_CR4;
429                 break;
430         default:
431                 break;
432         }
433
434         if (vmreg == VM_REG_LAST) {
435                 printf("test_setcr(%d): not implemented\n", r);
436                 cb_exit(NULL, USERBOOT_EXIT_QUIT);
437         }
438
439         error = vm_set_register(ctx, BSP, vmreg, v);
440         if (error) {
441                 perror("vm_set_cr");
442                 cb_exit(NULL, USERBOOT_EXIT_QUIT);
443         }
444 }
445
446 static void
447 cb_setgdt(void *arg, uint64_t base, size_t size)
448 {
449         int error;
450
451         error = vm_set_desc(ctx, BSP, VM_REG_GUEST_GDTR, base, size - 1, 0);
452         if (error != 0) {
453                 perror("vm_set_desc(gdt)");
454                 cb_exit(NULL, USERBOOT_EXIT_QUIT);
455         }
456
457         gdtbase = base;
458 }
459
460 static void
461 cb_exec(void *arg, uint64_t rip)
462 {
463         int error;
464
465         error = vm_setup_freebsd_registers(ctx, BSP, rip, cr3, gdtbase, rsp);
466         if (error) {
467                 perror("vm_setup_freebsd_registers");
468                 cb_exit(NULL, USERBOOT_EXIT_QUIT);
469         }
470
471         cb_exit(NULL, 0);
472 }
473
474 /*
475  * Misc
476  */
477
478 static void
479 cb_delay(void *arg, int usec)
480 {
481
482         usleep(usec);
483 }
484
485 static void
486 cb_exit(void *arg, int v)
487 {
488
489         tcsetattr(0, TCSAFLUSH, &oldterm);
490         exit(v);
491 }
492
493 static void
494 cb_getmem(void *arg, uint64_t *ret_lowmem, uint64_t *ret_highmem)
495 {
496
497         vm_get_memory_seg(ctx, 0, ret_lowmem, NULL);
498         vm_get_memory_seg(ctx, 4 * GB, ret_highmem, NULL);
499 }
500
501 static const char *
502 cb_getenv(void *arg, int num)
503 {
504         int max;
505
506         static const char * var[] = {
507                 "smbios.bios.vendor=BHYVE",
508                 "boot_serial=1",
509                 NULL
510         };
511
512         max = sizeof(var) / sizeof(var[0]);
513
514         if (num < max)
515                 return (var[num]);
516         else
517                 return (NULL);
518 }
519
520 static struct loader_callbacks cb = {
521         .getc = cb_getc,
522         .putc = cb_putc,
523         .poll = cb_poll,
524
525         .open = cb_open,
526         .close = cb_close,
527         .isdir = cb_isdir,
528         .read = cb_read,
529         .readdir = cb_readdir,
530         .seek = cb_seek,
531         .stat = cb_stat,
532
533         .diskread = cb_diskread,
534         .diskioctl = cb_diskioctl,
535
536         .copyin = cb_copyin,
537         .copyout = cb_copyout,
538         .setreg = cb_setreg,
539         .setmsr = cb_setmsr,
540         .setcr = cb_setcr,
541         .setgdt = cb_setgdt,
542         .exec = cb_exec,
543
544         .delay = cb_delay,
545         .exit = cb_exit,
546         .getmem = cb_getmem,
547
548         .getenv = cb_getenv,
549 };
550
551 static void
552 usage(void)
553 {
554
555         fprintf(stderr,
556                 "usage: %s [-m mem-size][-d <disk-path>] [-h <host-path>] "
557                 "<vmname>\n", progname);
558         exit(1);
559 }
560
561 int
562 main(int argc, char** argv)
563 {
564         void *h;
565         void (*func)(struct loader_callbacks *, void *, int, int);
566         uint64_t mem_size;
567         int opt, error;
568         char *disk_image;
569
570         progname = argv[0];
571
572         mem_size = 256 * MB;
573         disk_image = NULL;
574
575         while ((opt = getopt(argc, argv, "d:h:m:")) != -1) {
576                 switch (opt) {
577                 case 'd':
578                         disk_image = optarg;
579                         break;
580
581                 case 'h':
582                         host_base = optarg;
583                         break;
584
585                 case 'm':
586                         error = vm_parse_memsize(optarg, &mem_size);
587                         if (error != 0)
588                                 errx(EX_USAGE, "Invalid memsize '%s'", optarg);
589                         break;
590                 case '?':
591                         usage();
592                 }
593         }
594
595         argc -= optind;
596         argv += optind;
597
598         if (argc != 1)
599                 usage();
600
601         vmname = argv[0];
602
603         error = vm_create(vmname);
604         if (error != 0 && errno != EEXIST) {
605                 perror("vm_create");
606                 exit(1);
607
608         }
609
610         ctx = vm_open(vmname);
611         if (ctx == NULL) {
612                 perror("vm_open");
613                 exit(1);
614         }
615
616         error = vm_setup_memory(ctx, mem_size, VM_MMAP_ALL);
617         if (error) {
618                 perror("vm_setup_memory");
619                 exit(1);
620         }
621
622         tcgetattr(0, &term);
623         oldterm = term;
624         term.c_lflag &= ~(ICANON|ECHO);
625         term.c_iflag &= ~ICRNL;
626         tcsetattr(0, TCSAFLUSH, &term);
627         h = dlopen("/boot/userboot.so", RTLD_LOCAL);
628         if (!h) {
629                 printf("%s\n", dlerror());
630                 return (1);
631         }
632         func = dlsym(h, "loader_main");
633         if (!func) {
634                 printf("%s\n", dlerror());
635                 return (1);
636         }
637
638         if (disk_image) {
639                 disk_fd = open(disk_image, O_RDONLY);
640         }
641         func(&cb, NULL, USERBOOT_VERSION_3, disk_fd >= 0);
642 }