Begin work on true virtio mmio Kill tests/vmrunkernel.c Our makefiles, plus emacs...
[akaros.git] / kern / arch / x86 / vmm / intel / vmx.c
index 72647a8..e4198fc 100644 (file)
@@ -93,8 +93,8 @@
  * We divide this into two things: vmm_proc_init and vm_run.
  * Currently, on Intel, vmm_proc_init does nothing.
  *
- * vm_run is really complicated. It is called with a coreid, rip, rsp,
- * cr3, and flags.  On intel, it calls vmx_launch. vmx_launch is set
+ * vm_run is really complicated. It is called with a coreid, and
+ * vmctl struct. On intel, it calls vmx_launch. vmx_launch is set
  * up for a few test cases. If rip is 1, it sets the guest rip to
  * a function which will deref 0 and should exit with failure 2. If rip is 0,
  * it calls an infinite loop in the guest.
@@ -1284,175 +1284,6 @@ msrio(struct vmx_vcpu *vcpu, uint32_t opcode, uint32_t qual) {
        return SHUTDOWN_UNHANDLED_EXIT_REASON;
 }
 
-/* crude PCI bus. Just enough to get virtio working. I would rather not add to this. */
-struct pciconfig {
-       uint32_t registers[256];
-};
-
-/* just index by devfn, i.e. 8 bits */
-struct pciconfig pcibus[] = {
-       /* linux requires that devfn 0 be a bridge. 
-        * 00:00.0 Host bridge: Intel Corporation 440BX/ZX/DX - 82443BX/ZX/DX Host bridge (rev 01)
-        */
-       {
-               {0x71908086, 0x02000006, 0x06000001},
-       },
-};
-/* cf8 is a single-threaded resource. */
-static uint32_t cf8;
-static uint32_t allones = (uint32_t)-1;
-
-/* Return a pointer to the 32-bit "register" in the "pcibus" give an address. Use cf8.
- * only for readonly access.
- * this will fail if we ever want to do writes, but we don't.
- */
-void regp(uint32_t **reg)
-{
-       *reg = &allones;
-       int devfn = (cf8>>8) & 0xff;
-       printk("devfn %d\n", devfn);
-       if (devfn < ARRAY_SIZE(pcibus))
-               *reg = &pcibus[devfn].registers[(cf8>>2)&0x3f];
-       printk("-->regp *reg 0x%lx\n", **reg);
-}
-
-static uint32_t configaddr(uint32_t val)
-{
-       printk("%s 0x%lx\n", __func__, val);
-       cf8 = val;
-       return 0;
-}
-
-static uint32_t configread32(uint32_t edx, uint64_t *reg)
-{
-       uint32_t *r = &cf8;
-       regp(&r);
-       *reg = set_low32(*reg, *r);
-       printk("%s: 0x%lx 0x%lx, 0x%lx 0x%lx\n", __func__, cf8, edx, r, *reg);
-       return 0;
-}
-
-static uint32_t configread16(uint32_t edx, uint64_t *reg)
-{
-       uint64_t val;
-       int which = ((edx&2)>>1) * 16;
-       configread32(edx, &val);
-       val >>= which;
-       *reg = set_low16(*reg, val);
-       printk("%s: 0x%lx, 0x%lx 0x%lx\n", __func__, edx, val, *reg);
-       return 0;
-}
-
-static uint32_t configread8(uint32_t edx, uint64_t *reg)
-{
-       uint64_t val;
-       int which = (edx&3) * 8;
-       configread32(edx, &val);
-       val >>= which;
-       *reg = set_low16(*reg, val);
-       printk("%s: 0x%lx, 0x%lx 0x%lx\n", __func__, edx, val, *reg);
-       return 0;
-}
-
-static int configwrite32(uint32_t addr, uint32_t val)
-{
-       printk("%s 0x%lx 0x%lx\n", __func__, addr, val);
-       return 0;
-}
-
-static int configwrite16(uint32_t addr, uint16_t val)
-{
-       printk("%s 0x%lx 0x%lx\n", __func__, addr, val);
-       return 0;
-}
-
-static int configwrite8(uint32_t addr, uint8_t val)
-{
-       printk("%s 0x%lx 0x%lx\n", __func__, addr, val);
-       return 0;
-}
-
-/* this is very minimal. It needs to move to vmm/io.c but we don't
- * know if this minimal approach will even be workable. It only (for
- * now) handles pci config space. We'd like to hope that's all we will
- * need.
- * 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 vmx_vcpu *vcpu, int *advance)
-{
-
-       /* Get a pointer to the memory at %rip. This is quite messy and part of the
-        * reason we don't want to do this at all. It sucks. Would have been nice
-        * had linux had an option to ONLY do mmio config space access, but no such
-        * luck.
-        */
-       uint8_t *ip8 = NULL;
-       uint16_t *ip16;
-       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
-        */
-       ip = vcpu->regs.tf_rip & 0x3fffffff;
-       edx = vcpu->regs.tf_rdx;
-       ip8 = (void *)ip;
-       ip16 = (void *)ip;
-       printk("io: ip16 %p\n", *ip16, edx);
-
-       if (*ip8 == 0xef) {
-               *advance = 1;
-               /* out at %edx */
-               if (edx == 0xcf8) {
-                       printk("Set cf8 ");
-                       return configaddr(vcpu->regs.tf_rax);
-               }
-               if (edx == 0xcf8) {
-                       printk("IGNORE Set cf8 ");
-                       return configaddr(vcpu->regs.tf_rax);
-               }
-               printk("unhandled IO address dx @%p is 0x%x\n", ip8, edx);
-               return SHUTDOWN_UNHANDLED_EXIT_REASON;
-       }
-       // out %al, %dx
-       if (*ip8 == 0xee) {
-               *advance = 1;
-               /* out al %edx */
-               if (edx == 0xcfb) { // special!
-                       printk("Just ignore the damned cfb write\n");
-                       return 0;
-               }
-               if ((edx&~3) == 0xcfc) {
-                       printk("ignoring write to cfc ");
-                       return 0;
-               }
-               printk("unhandled IO address dx @%p is 0x%x\n", ip8, edx);
-               return SHUTDOWN_UNHANDLED_EXIT_REASON;
-       }
-       if (*ip8 == 0xec) {
-               *advance = 1;
-               printk("configread8 ");
-               return configread8(edx, &vcpu->regs.tf_rax);
-       }
-       if (*ip8 == 0xed) {
-               *advance = 1;
-               if (edx == 0xcf8) {
-                       printk("read cf8 0x%lx\n", vcpu->regs.tf_rax);
-                       vcpu->regs.tf_rax = cf8;
-                       return 0;
-               }
-               printk("configread32 ");
-               return configread32(edx, &vcpu->regs.tf_rax);
-       }
-       if (*ip16 == 0xed66) {
-               *advance = 2;
-               printk("configread16 ");
-               return configread16(edx, &vcpu->regs.tf_rax);
-       }
-       printk("unknown IO %p %x %x\n", ip8, *ip8, *ip16);
-       return SHUTDOWN_UNHANDLED_EXIT_REASON;
-}
-
 /* Notes on autoloading.  We can't autoload FS_BASE or GS_BASE, according to the
  * manual, but that's because they are automatically saved and restored when all
  * of the other architectural registers are saved and restored, such as cs, ds,
@@ -1740,7 +1571,7 @@ static void vmx_step_instruction(void) {
                    vmcs_read32(VM_EXIT_INSTRUCTION_LEN));
 }
 
-static int vmx_handle_ept_violation(struct vmx_vcpu *vcpu) {
+static int vmx_handle_ept_violation(struct vmx_vcpu *vcpu, struct vmctl *v) {
        unsigned long gva, gpa;
        int exit_qual, ret = -1;
        page_t *page;
@@ -1749,7 +1580,9 @@ static int vmx_handle_ept_violation(struct vmx_vcpu *vcpu) {
        exit_qual = vmcs_read32(EXIT_QUALIFICATION);
        gva = vmcs_readl(GUEST_LINEAR_ADDRESS);
        gpa = vmcs_read64(GUEST_PHYSICAL_ADDRESS);
-
+       v->gpa = gpa;
+       v->gva = gva;
+       v->exit_qual = exit_qual;
        vmx_put_cpu(vcpu);
 
        int prot = 0;
@@ -1758,13 +1591,17 @@ static int vmx_handle_ept_violation(struct vmx_vcpu *vcpu) {
        prot |= exit_qual & VMX_EPT_FAULT_INS ? PROT_EXEC : 0;
        ret = handle_page_fault(current, gpa, prot);
 
-       if (ret) {
+       // Some of these get fixed in the vmm; be less chatty now.
+       if (0 && ret) {
                printk("EPT page fault failure %d, GPA: %p, GVA: %p\n", ret, gpa,
                       gva);
                vmx_dump_cpu(vcpu);
        }
 
-       return ret;
+       /* we let the vmm handle the failure cases. So return
+        * the VMX exit violation, not what handle_page_fault returned.
+        */
+       return EXIT_REASON_EPT_VIOLATION;
 }
 
 static void vmx_handle_cpuid(struct vmx_vcpu *vcpu) {
@@ -1800,7 +1637,7 @@ static int vmx_handle_nmi_exception(struct vmx_vcpu *vcpu) {
  * vmx_launch - the main loop for a VMX Dune process
  * @conf: the launch configuration
  */
-int vmx_launch(uint64_t rip, uint64_t rsp, uint64_t cr3) {
+int vmx_launch(struct vmctl *v) {
        int ret;
        struct vmx_vcpu *vcpu;
        int errors = 0;
@@ -1819,14 +1656,32 @@ int vmx_launch(uint64_t rip, uint64_t rsp, uint64_t cr3) {
         * core is the KERN_GS_BASE). */
        rdmsrl(MSR_KERNEL_GS_BASE, vcpu->msr_autoload.host[0].value);
        /* if cr3 is set, means 'set everything', else means 'start where you left off' */
-       if (cr3) {
-               vmx_get_cpu(vcpu);
-               vmcs_writel(GUEST_RIP, rip);
-               vmcs_writel(GUEST_RSP, rsp);
-               vmcs_writel(GUEST_CR3, cr3);
-               vmx_put_cpu(vcpu);
+       vmx_get_cpu(vcpu);
+       switch(v->command) {
+       case REG_ALL:
+               printk("REG_ALL\n");
+               // fallthrough
+               vcpu->regs = v->regs;
+               vmcs_writel(GUEST_RSP, v->regs.tf_rsp);
+               vmcs_writel(GUEST_RIP, v->regs.tf_rip);
+               break;
+       case REG_RSP_RIP_CR3:
+               printk("REG_RSP_RIP_CR3\n");
+               vmcs_writel(GUEST_RSP, v->regs.tf_rsp);
+               vmcs_writel(GUEST_CR3, v->cr3);
+               // fallthrough
+       case REG_RIP:
+               printk("REG_RIP %p\n", v->regs.tf_rip);
+               vmcs_writel(GUEST_RIP, v->regs.tf_rip);
+               break;
+       case RESUME:
+               printk("RESUME\n");
+               break;
+       default: 
+               error(EINVAL, "Bad command in vmx_launch");
        }
-
+       vcpu->shutdown = 0;
+       vmx_put_cpu(vcpu);
        vcpu->ret_code = -1;
 
        while (1) {
@@ -1870,10 +1725,10 @@ int vmx_launch(uint64_t rip, uint64_t rsp, uint64_t cr3) {
                        vmcs_writel(GUEST_RIP, vcpu->regs.tf_rip + 2);
                        vmx_put_cpu(vcpu);
                } else if (ret == EXIT_REASON_EPT_VIOLATION) {
-                       if (vmx_handle_ept_violation(vcpu))
+                       if (vmx_handle_ept_violation(vcpu, v))
                                vcpu->shutdown = SHUTDOWN_EPT_VIOLATION;
                } else if (ret == EXIT_REASON_EXCEPTION_NMI) {
-                       if (vmx_handle_nmi_exception(vcpu))
+                       if (vmx_handle_nmi_exception(vcpu)) 
                                vcpu->shutdown = SHUTDOWN_NMI_EXCEPTION;
                } else if (ret == EXIT_REASON_EXTERNAL_INTERRUPT) {
                        printd("External interrupt\n");
@@ -1891,13 +1746,12 @@ int vmx_launch(uint64_t rip, uint64_t rsp, uint64_t cr3) {
                                msrio(vcpu, ret, vmcs_read32(EXIT_QUALIFICATION));
                        advance = 2;
                } else if (ret == EXIT_REASON_IO_INSTRUCTION) {
-                       /* we never wanted to do this. But virtio
-                        * requires pci config space emulation. */
-                       vcpu->shutdown = io(vcpu, &advance);
+                       /* the VMM does this now. */
+                       vcpu->shutdown = ret; 
                } else {
                        printk("unhandled exit: reason 0x%x, exit qualification 0x%x\n",
                               ret, vmcs_read32(EXIT_QUALIFICATION));
-                       vmx_dump_cpu(vcpu);
+                       //vmx_dump_cpu(vcpu);
                        vcpu->shutdown = SHUTDOWN_UNHANDLED_EXIT_REASON;
                }
 
@@ -1914,8 +1768,11 @@ int vmx_launch(uint64_t rip, uint64_t rsp, uint64_t cr3) {
                }
        }
 
-       printd("RETURN. ip %016lx sp %016lx\n",
-              vcpu->regs.tf_rip, vcpu->regs.tf_rsp);
+       printk("RETURN. ip %016lx sp %016lx, shutdown 0x%lx ret 0x%lx\n",
+              vcpu->regs.tf_rip, vcpu->regs.tf_rsp, vcpu->shutdown, vcpu->shutdown);
+       v->regs = vcpu->regs;
+       v->shutdown = vcpu->shutdown;
+       v->ret_code = ret;
 //  hexdump((void *)vcpu->regs.tf_rsp, 128 * 8);
        /*
         * Return both the reason for the shutdown and a status value.