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