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