Major cleanups.
authorRonald G. Minnich <rminnich@gmail.com>
Mon, 24 Aug 2015 23:56:52 +0000 (16:56 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Mon, 2 Nov 2015 23:24:25 +0000 (18:24 -0500)
Put IO into user mode. PCI will be moving too.

Break out decoding.

Add -d (debug) and -m (max iterations before turning debug on) into vmrunkernel

Drop out of vmx if we an IO exit.

The goal here is to get more stuff into user mode and maybe someday even get interrupts.

Signed-off-by: Ronald G. Minnich <rminnich@gmail.com>
Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
kern/arch/x86/vmm/intel/vmx.c
scripts/VMPXE
tests/vmm/vmrunkernel.c
user/vmm/decode.c [new file with mode: 0644]
user/vmm/include/virtio_mmio.h
user/vmm/include/vmm.h [new file with mode: 0644]
user/vmm/io.c
user/vmm/virtio-mmio.c
user/vmm/vmx.c

index f7d293d..43c179d 100644 (file)
@@ -1647,7 +1647,6 @@ int vmx_launch(struct vmctl *v) {
        int errors = 0;
        int advance;
 
-       printd("RUNNING: %s: rip %p rsp %p cr3 %p \n", __func__, rip, rsp, cr3);
        /* TODO: dirty hack til we have VMM contexts */
        vcpu = current->vmm.guest_pcores[0];
        if (!vcpu) {
@@ -1767,8 +1766,7 @@ int vmx_launch(struct vmctl *v) {
                                msrio(vcpu, ret, vmcs_read32(EXIT_QUALIFICATION));
                        advance = 2;
                } else if (ret == EXIT_REASON_IO_INSTRUCTION) {
-                       /* the VMM does this now. */
-                       vcpu->shutdown = ret; 
+                       vcpu->shutdown = SHUTDOWN_UNHANDLED_EXIT_REASON;
                } else {
                        printk("unhandled exit: reason 0x%x, exit qualification 0x%x\n",
                               ret, vmcs_read32(EXIT_QUALIFICATION));
index cc6524a..47e4281 100644 (file)
@@ -1,6 +1,6 @@
 #!/bin/bash
 export ARCH=x86
-rm -f obj/tests/vmm/vmrunkernel kern/kfs/bin/vmrunkernel && make tests && make fill-kfs &&make  && sudo cp obj/kern/akaros-kernel /var/lib/tftpboot/akaros \
+rm -f obj/tests/vmm/vmrunkernel kern/kfs/bin/vmrunkernel && make tests && make fill-kfs &&make  && cp obj/kern/akaros-kernel /var/lib/tftpboot/akaros \
        && echo "OK" && exit
 
 
index 2bbd283..fe4bd82 100644 (file)
@@ -13,7 +13,7 @@
 #include <ros/syscall.h>
 #include <sys/mman.h>
 #include <vmm/coreboot_tables.h>
-#include <ros/vmm.h>
+#include <vmm/vmm.h>
 #include <ros/arch/mmu.h>
 #include <ros/vmx.h>
 #include <parlib/uthread.h>
@@ -29,6 +29,9 @@ volatile int shared = 0;
 volatile int quit = 0;
 int mcp = 1;
 
+/* total hack. If the vm runs away we want to get control again. */
+unsigned int maxresume = (unsigned int) -1;
+
 #define MiB 0x100000u
 #define GiB (1u<<30)
 #define GKERNBASE (16*MiB)
@@ -164,6 +167,7 @@ vqs: {
 int main(int argc, char **argv)
 {
        uint64_t virtiobase = 0x100000000ULL;
+       void *rsdp = (void *)0;
        struct vmctl vmctl;
        int amt;
        int vmmflags = 0; // Disabled probably forever. VMM_VMCALL_PRINTF;
@@ -193,9 +197,16 @@ int main(int argc, char **argv)
                if (*argv[0] != '-')
                        break;
                switch(argv[0][1]) {
+               case 'd':
+                       debug++;
+                       break;
                case 'v':
                        vmmflags |= VMM_VMCALL_PRINTF;
                        break;
+               case 'm':
+                       argc--,argv++;
+                       maxresume = strtoull(argv[0], 0, 0);
+                       break;
                default:
                        printf("BMAFR\n");
                        break;
@@ -317,17 +328,35 @@ int main(int argc, char **argv)
                int c;
                uint8_t byte;
                vmctl.command = REG_RIP;
-               if (debug) printf("RESUME?\n");
-               //c = getchar();
-               //if (c == 'q')
-                       //break;
-               if (debug) printf("RIP %p, shutdown 0x%x\n", vmctl.regs.tf_rip, vmctl.shutdown);
-               //showstatus(stdout, &vmctl);
-               // this will be in a function, someday.
-               // A rough check: is the GPA 
-               if ((vmctl.shutdown == SHUTDOWN_EPT_VIOLATION) && ((vmctl.gpa & ~0xfffULL) == virtiobase)) {
-                       if (debug) printf("DO SOME VIRTIO\n");
-                       virtio_mmio(&vmctl);
+               if (maxresume-- == 0)
+                       debug = 1;
+               if (debug) {
+                       printf("RIP %p, shutdown 0x%x\n", vmctl.regs.tf_rip, vmctl.shutdown);
+                       showstatus(stdout, &vmctl);
+                       printf("RESUME?\n");
+                       c = getchar();
+                       if (c == 'q')
+                               break;
+               }
+               if (vmctl.shutdown == SHUTDOWN_EPT_VIOLATION) {
+                       uint64_t gpa;
+                       uint64_t *regp;
+                       uint8_t regx;
+                       int store;
+                       if (decode(&vmctl, &gpa, &regx, &regp, &store)) {
+                               quit = 1;
+                               break;
+                       }
+                       if ((gpa & ~0xfffULL) == virtiobase) {
+                               if (debug) printf("DO SOME VIRTIO\n");
+                               virtio_mmio(&vmctl, gpa, regx, regp, store);
+                       } else if (gpa == 0x40e) {
+                               *regp = (uint64_t) rsdp;
+                       } else {
+                               printf("EPT violation: can't handle %p\n", gpa);
+                               quit = 1;
+                               break;
+                       }
                        vmctl.shutdown = 0;
                        vmctl.gpa = 0;
                        vmctl.command = REG_ALL;
@@ -345,6 +374,10 @@ int main(int argc, char **argv)
                                vmctl.interrupt = 0;//x8000030e; // b0d;
                                vmctl.command = RESUME;
                                break;
+                       case EXIT_REASON_IO_INSTRUCTION:
+                               printf("IO @ %p\n", vmctl.regs.tf_rip);
+                               io(&vmctl);
+                               break;
                        case EXIT_REASON_HLT:
                                printf("\n================== Guest halted. RIP. =======================\n");
                                quit = 1;
@@ -355,6 +388,7 @@ int main(int argc, char **argv)
                                break;
                        }
                }
+               if (debug) printf("at bottom of switch, quit is %d\n", quit);
                if (quit)
                        break;
                if (debug) printf("NOW DO A RESUME\n");
@@ -364,9 +398,6 @@ int main(int argc, char **argv)
                }
        }
 
-       printf("shared is %d, blob is %d\n", shared, *mmap_blob);
-
-       quit = 1;
        /* later. 
        for (int i = 0; i < nr_threads-1; i++) {
                int ret;
diff --git a/user/vmm/decode.c b/user/vmm/decode.c
new file mode 100644 (file)
index 0000000..11f9f39
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * This file is part of Akaros.
+ *
+ * Akarosn is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ * 
+ * Akaros is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * Lesser GNU General Public License for more details.
+ * 
+ * See COPYING.LESSER for details on the GNU Lesser General Public License.
+ * See COPYING for details on the GNU General Public License.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <pthread.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <parlib/arch/arch.h>
+#include <parlib/ros_debug.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/uio.h>
+#include <stdint.h>
+#include <err.h>
+#include <sys/mman.h>
+#include <vmm/vmm.h>
+#include <vmm/virtio.h>
+#include <vmm/virtio_mmio.h>
+#include <vmm/virtio_ids.h>
+#include <vmm/virtio_config.h>
+
+int debug_decode = 0;
+#define DPRINTF(fmt, ...) \
+       if (debug_decode) { printf("decode: " fmt , ## __VA_ARGS__); }
+
+static char *modrmreg[] = {"rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi"};
+
+char *regname(uint8_t reg)
+{
+       return modrmreg[reg];
+}
+
+// This is a very limited function. It's only here to manage virtio-mmio and acpi root
+// pointer loads. I am hoping it won't grow with time. The intent is that we enter it with
+// and EPT fault from a region that is deliberately left unbacked by any memory. We return
+// enough info to let you emulate the operation if you want.
+// gpa is a pointer to the gpa. 
+// int is the reg index which we can use for printing info.
+// regp points to the register in hw_trapframe from which
+// to load or store a result.
+int decode(struct vmctl *v, uint64_t *gpa, uint8_t *destreg, uint64_t **regp, int *store)
+{
+       int advance = 3; /* how much to move the IP forward at the end. 3 is a good default. */
+       //DPRINTF("v is %p\n", v);
+
+       // Duh, which way did he go George? Which way did he go? 
+       // First hit on Google gets you there!
+       // This is the guest physical address of the access.
+       // This is nice, because if we ever go with more complete
+       // instruction decode, knowing this gpa reduces our work:
+       // we don't have to find the source address in registers,
+       // only the register holding or receiving the value.
+       *gpa = v->gpa;
+       //DPRINTF("gpa is %p\n", *gpa);
+
+       // To find out what to do, we have to look at
+       // RIP. Technically, we should read RIP, walk the page tables
+       // to find the PA, and read that. But we're in the kernel, so
+       // we take a shortcut for now: read the low 30 bits and use
+       // that as the kernel PA, or our VA, and see what's
+       // there. Hokey. Works.
+       uint8_t *kva = (void *)(v->regs.tf_rip & 0x3fffffff);
+       //DPRINTF("kva is %p\n", kva);
+
+       // If this gets any longer we'll have to make a smarter loop. I'm betting it
+       // won't
+       if ((kva[0] != 0x8b) && (kva[0] != 0x89) && (kva[0] != 0x0f || kva[1] != 0xb7)) {
+               fprintf(stderr, "%s: can't handle instruction 0x%x\n", kva[0]);
+               return -1;
+       }
+
+       uint16_t ins = *(uint16_t *)kva;
+       //DPRINTF("ins is %04x\n", ins);
+       
+       *store = (kva[0] == 0x8b) ? 0 : 1;
+
+       if (kva[0] != 0x0f) {
+               /* the dreaded mod/rm byte. */
+               int mod = kva[1]>>6;
+               switch (mod) {
+               case 0: 
+               case 3:
+                       advance = 2;
+                       break;
+               case 1:
+                       advance = 3;
+                       break;
+               case 2: 
+                       advance = 6;
+                       break;
+               }
+       }
+
+       *destreg = (ins>>11) & 7;
+       // Our primitive approach wins big here.
+       // We don't have to decode the register or the offset used
+       // in the computation; that was done by the CPU and is the gpa.
+       // All we need to know is which destination or source register it is.
+       switch (*destreg) {
+       case 0:
+               *regp = &v->regs.tf_rax;
+               break;
+       case 1:
+               *regp = &v->regs.tf_rcx;
+               break;
+       case 2:
+               *regp = &v->regs.tf_rdx;
+               break;
+       case 3:
+               *regp = &v->regs.tf_rbx;
+               break;
+       case 4:
+               *regp = &v->regs.tf_rsp; // uh, right.
+               break;
+       case 5:
+               *regp = &v->regs.tf_rbp;
+               break;
+       case 6:
+               *regp = &v->regs.tf_rsi;
+               break;
+       case 7:
+               *regp = &v->regs.tf_rdi;
+               break;
+       }
+       v->regs.tf_rip += advance;
+       DPRINTF("Advance rip by %d bytes to %p\n", advance, v->regs.tf_rip);
+       return 0;
+}
+
index 5671c22..4c28a40 100644 (file)
@@ -180,6 +180,6 @@ struct virtio_threadarg {
 
 void dumpvirtio_mmio(FILE *f, uint64_t gpa);
 void register_virtio_mmio(struct vqdev *v, uint64_t virtio_base);
-void virtio_mmio(struct vmctl *v);
+int virtio_mmio(struct vmctl *v, uint64_t gpa, int destreg, uint64_t *regp, int store);
 
 #endif
diff --git a/user/vmm/include/vmm.h b/user/vmm/include/vmm.h
new file mode 100644 (file)
index 0000000..7ff8580
--- /dev/null
@@ -0,0 +1,14 @@
+/* Copyright (c) 2015 Google Inc.
+ * Ron Minnich <rminnich@google.com>
+ * See LICENSE for details.
+ *
+ * VMM.h */
+
+#pragma once
+
+#include <ros/vmm.h>
+
+char *regname(uint8_t reg);
+int decode(struct vmctl *v, uint64_t *gpa, uint8_t *destreg, uint64_t **regp,
+           int *store, int *size, int *advance);
+int io(struct vmctl *v);
index cc3c257..027184e 100644 (file)
@@ -14,7 +14,7 @@
 #include <sys/mman.h>
 #include <vmm/coreboot_tables.h>
 #include <ros/common.h>
-#include <ros/vmm.h>
+#include <vmm/vmm.h>
 #include <vmm/virtio.h>
 #include <vmm/virtio_mmio.h>
 #include <vmm/virtio_ids.h>
@@ -120,7 +120,7 @@ static int configwrite8(uint32_t addr, uint8_t val)
  * It would have been nice had intel encoded the IO exit info as nicely as they
  * encoded, some of the other exits.
  */
-static int io(struct vmctl *v)
+int io(struct vmctl *v)
 {
 
        /* Get a pointer to the memory at %rip. This is quite messy and part of the
@@ -133,7 +133,8 @@ static int io(struct vmctl *v)
        uintptr_t ip;
        uint32_t edx;
        /* for now, we're going to be a bit crude. In kernel, p is about v, so we just blow away
-        * the upper 34 bits and take the rest as our address
+        * the upper 34 bits and take the rest + 1M as our address
+        * TODO: put this in vmctl somewhere?
         */
        ip = v->regs.tf_rip & 0x3fffffff;
        edx = v->regs.tf_rdx;
index 2d29ace..8a20902 100644 (file)
@@ -34,7 +34,7 @@
 #include <stdint.h>
 #include <err.h>
 #include <sys/mman.h>
-#include <ros/vmm.h>
+#include <vmm/vmm.h>
 #include <vmm/virtio.h>
 #include <vmm/virtio_mmio.h>
 #include <vmm/virtio_ids.h>
@@ -402,104 +402,14 @@ static void virtio_mmio_write(uint64_t gpa, uint32_t value)
 
 }
 
-static char *modrmreg[] = {"rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi"};
-
-void virtio_mmio(struct vmctl *v)
+int virtio_mmio(struct vmctl *v, uint64_t gpa, int destreg, uint64_t *regp, int store)
 {
-       int advance = 3; /* how much to move the IP forward at the end. 3 is a good default. */
-       // All virtio accesses seem to be 32 bits.
-       // valp points to a place to get or put the value. 
-       uint32_t *valp;
-       //DPRINTF("v is %p\n", v);
-       // regp points to the register in hw_trapframe from which
-       // to load or store a result.
-       uint64_t *regp;
-
-       // Duh, which way did he go George? Which way did he go? 
-       // First hit on Google gets you there!
-       // This is the guest physical address of the access.
-       // This is nice, because if we ever go with more complete
-       // instruction decode, knowing this gpa reduces our work:
-       // we don't have to find the source address in registers,
-       // only the register holding or receiving the value.
-       uint64_t gpa = v->gpa;
-       //DPRINTF("gpa is %p\n", gpa);
-
-       // To find out what to do, we have to look at
-       // RIP. Technically, we should read RIP, walk the page tables
-       // to find the PA, and read that. But we're in the kernel, so
-       // we take a shortcut for now: read the low 30 bits and use
-       // that as the kernel PA, or our VA, and see what's
-       // there. Hokey. Works.
-       uint8_t *kva = (void *)(v->regs.tf_rip & 0x3fffffff);
-       //DPRINTF("kva is %p\n", kva);
-
-       if ((kva[0] != 0x8b) && (kva[0] != 0x89)) {
-               fprintf(stderr, "%s: can't handle instruction 0x%x\n", kva[0]);
-               return;
-       }
-
-       uint16_t ins = *(uint16_t *)kva;
-       //DPRINTF("ins is %04x\n", ins);
-       
-       int write = (kva[0] == 0x8b) ? 0 : 1;
-       if (write)
-               valp = (uint32_t *)gpa;
-
-       int mod = kva[1]>>6;
-       switch (mod) {
-               case 0: 
-               case 3:
-                       advance = 2;
-                       break;
-               case 1:
-                       advance = 3;
-                       break;
-               case 2: 
-                       advance = 6;
-                       break;
-       }
-       /* the dreaded mod/rm byte. */
-       int destreg = (ins>>11) & 7;
-       // Our primitive approach wins big here.
-       // We don't have to decode the register or the offset used
-       // in the computation; that was done by the CPU and is the gpa.
-       // All we need to know is which destination or source register it is.
-       switch (destreg) {
-       case 0:
-               regp = &v->regs.tf_rax;
-               break;
-       case 1:
-               regp = &v->regs.tf_rcx;
-               break;
-       case 2:
-               regp = &v->regs.tf_rdx;
-               break;
-       case 3:
-               regp = &v->regs.tf_rbx;
-               break;
-       case 4:
-               regp = &v->regs.tf_rsp; // uh, right.
-               break;
-       case 5:
-               regp = &v->regs.tf_rbp;
-               break;
-       case 6:
-               regp = &v->regs.tf_rsi;
-               break;
-       case 7:
-               regp = &v->regs.tf_rdi;
-               break;
-       }
-
-       if (write) {
+       if (store) {
                virtio_mmio_write(gpa, *regp);
-               DPRINTF("Write: mov %s to %s @%p val %p\n", modrmreg[destreg], virtio_names[(uint8_t)gpa], gpa, *regp);
+               DPRINTF("Write: mov %s to %s @%p val %p\n", regname(destreg), virtio_names[(uint8_t)gpa], gpa, *regp);
        } else {
                *regp = virtio_mmio_read(gpa);
-               DPRINTF("Read: Set %s from %s @%p to %p\n", modrmreg[destreg], virtio_names[(uint8_t)gpa], gpa, *regp);
+               DPRINTF("Read: Set %s from %s @%p to %p\n", regname(destreg), virtio_names[(uint8_t)gpa], gpa, *regp);
        }
 
-       DPRINTF("Advance rip by %d bytes to %p\n", advance, v->regs.tf_rip);
-       v->regs.tf_rip += advance;
 }
index fd6d9d2..b9a3cad 100644 (file)
@@ -14,7 +14,7 @@
 #include <sys/mman.h>
 #include <vmm/coreboot_tables.h>
 #include <ros/common.h>
-#include <ros/vmm.h>
+#include <vmm/vmm.h>
 #include <vmm/virtio.h>
 #include <vmm/virtio_mmio.h>
 #include <vmm/virtio_ids.h>