Convert run_once() to parlib_run_once() (XCC)
[akaros.git] / user / vmm / io.c
index 9e83057..b6917b4 100644 (file)
@@ -1,4 +1,4 @@
-#include <stdio.h> 
+#include <stdio.h>
 #include <pthread.h>
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -28,7 +28,7 @@ struct pciconfig {
 
 /* just index by devfn, i.e. 8 bits */
 struct pciconfig pcibus[] = {
-       /* linux requires that devfn 0 be a bridge. 
+       /* 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)
         */
        {
@@ -53,63 +53,56 @@ void regp(uint32_t **reg)
        //printf("-->regp *reg 0x%lx\n", **reg);
 }
 
-static uint32_t configaddr(uint32_t val)
+static void configaddr(uint32_t val)
 {
-       printf("%s 0x%lx\n", __func__, val);
+       printd("%s 0x%lx\n", __func__, val);
        cf8 = val;
-       return 0;
 }
 
-static uint32_t configread32(uint32_t edx, uint64_t *reg)
+static void configread32(uint32_t edx, uint64_t *reg)
 {
        uint32_t *r = &cf8;
        regp(&r);
        *reg = *r;
-       printf("%s: 0x%lx 0x%lx, 0x%lx 0x%lx\n", __func__, cf8, edx, r, *reg);
-       return 0;
+       printd("%s: 0x%lx 0x%lx, 0x%lx 0x%lx\n", __func__, cf8, edx, r, *reg);
 }
 
-static uint32_t configread16(uint32_t edx, uint64_t *reg)
+static void configread16(uint32_t edx, uint64_t *reg)
 {
        uint64_t val;
        int which = ((edx&2)>>1) * 16;
        configread32(edx, &val);
        val >>= which;
        *reg = val;
-       printf("%s: 0x%lx, 0x%lx 0x%lx\n", __func__, edx, val, *reg);
-       return 0;
+       printd("%s: 0x%lx, 0x%lx 0x%lx\n", __func__, edx, val, *reg);
 }
 
-static uint32_t configread8(uint32_t edx, uint64_t *reg)
+static void configread8(uint32_t edx, uint64_t *reg)
 {
        uint64_t val;
        int which = (edx&3) * 8;
        configread32(edx, &val);
        val >>= which;
        *reg = val;
-       printf("%s: 0x%lx, 0x%lx 0x%lx\n", __func__, edx, val, *reg);
-       return 0;
+       printd("%s: 0x%lx, 0x%lx 0x%lx\n", __func__, edx, val, *reg);
 }
 
-static int configwrite32(uint32_t addr, uint32_t val)
+static void configwrite32(uint32_t addr, uint32_t val)
 {
        uint32_t *r = &cf8;
        regp(&r);
        *r = val;
-       printf("%s 0x%lx 0x%lx\n", __func__, addr, val);
-       return 0;
+       printd("%s 0x%lx 0x%lx\n", __func__, addr, val);
 }
 
-static int configwrite16(uint32_t addr, uint16_t val)
+static void configwrite16(uint32_t addr, uint16_t val)
 {
-       printf("%s 0x%lx 0x%lx\n", __func__, addr, val);
-       return 0;
+       printd("%s 0x%lx 0x%lx\n", __func__, addr, val);
 }
 
-static int configwrite8(uint32_t addr, uint8_t val)
+static void configwrite8(uint32_t addr, uint8_t val)
 {
-       printf("%s 0x%lx 0x%lx\n", __func__, addr, val);
-       return 0;
+       printd("%s 0x%lx 0x%lx\n", __func__, addr, val);
 }
 
 /* this is very minimal. It needs to move to vmm/io.c but we don't
@@ -132,11 +125,10 @@ int io(struct guest_thread *vm_thread)
        uintptr_t ip;
        uint32_t edx;
        struct vm_trapframe *vm_tf = &(vm_thread->uthread.u_ctx.tf.vm_tf);
-       /* 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 + 1M as our address
-        * TODO: put this in vmctl somewhere?
-        */
-       ip = vm_tf->tf_rip & 0x3fffffff;
+
+       /* Get the RIP of the io access. */
+       if (rippa(vm_thread, (uint64_t *)&ip))
+               return VM_PAGE_FAULT;
        edx = vm_tf->tf_rdx;
        ip8 = (void *)ip;
        ip16 = (void *)ip;
@@ -147,14 +139,31 @@ int io(struct guest_thread *vm_thread)
                /* out at %edx */
                if (edx == 0xcf8) {
                        //printf("Set cf8 ");
-                       return configaddr(vm_tf->tf_rax);
+                       configaddr(vm_tf->tf_rax);
+                       return 0;
                }
                if (edx == 0xcfc) {
                        //printf("Set cfc ");
-                       return configwrite32(edx, vm_tf->tf_rax);
+                       configwrite32(edx, vm_tf->tf_rax);
+                       return 0;
                }
+               /* While it is perfectly legal to do IO operations to
+                * nonexistant places, we print a warning here as it
+                * might also indicate a problem.  In practice these
+                * types of IOs happens less frequently, and whether
+                * they are bad or not is not always easy to decide.
+                * Simple example: for about the first 10 years Linux
+                * used to outb 0x98 to port 0x80 while idle. We
+                * wouldn't want to call that an error, but that kind
+                * of thing is a bad practice we ought to know about,
+                * because it can cause chipset errors and result in
+                * other non-obvious failures (in one case, breaking
+                * BIOS reflash operations).  Plus, true story, it
+                * confused people into thinking we were running
+                * Windows 98, not Linux.
+                */
                printf("(out rax, edx): unhandled IO address dx @%p is 0x%x\n", ip8, edx);
-               return -1;
+               return 0;
        }
        // out %al, %dx
        if (*ip8 == 0xee) {
@@ -168,13 +177,15 @@ int io(struct guest_thread *vm_thread)
                        //printf("ignoring write to cfc ");
                        return 0;
                }
+               /* Another case where we print a message but it's not an error. */
                printf("out al, dx: unhandled IO address dx @%p is 0x%x\n", ip8, edx);
-               return -1;
+               return 0;
        }
        if (*ip8 == 0xec) {
                vm_tf->tf_rip += 1;
                //printf("configread8 ");
-               return configread8(edx, &vm_tf->tf_rax);
+               configread8(edx, &vm_tf->tf_rax);
+               return 0;
        }
        if (*ip8 == 0xed) {
                vm_tf->tf_rip += 1;
@@ -184,13 +195,37 @@ int io(struct guest_thread *vm_thread)
                        return 0;
                }
                //printf("configread32 ");
-               return configread32(edx, &vm_tf->tf_rax);
+               configread32(edx, &vm_tf->tf_rax);
+               return 0;
+       }
+       /* Detects when something is written to the PIC. */
+       if (*ip8 == 0xe6) {
+               vm_tf->tf_rip += 2;
+               return 0;
+       }
+       /* Detects when something is read from the PIC, so
+        * a value signifying there is no PIC is given.
+        */
+       if (*ip16 == 0x21e4) {
+               vm_tf->tf_rip += 2;
+               vm_tf->tf_rax |= 0x00000ff;
+               return 0;
        }
        if (*ip16 == 0xed66) {
                vm_tf->tf_rip += 2;
                //printf("configread16 ");
-               return configread16(edx, &vm_tf->tf_rax);
+               configread16(edx, &vm_tf->tf_rax);
+               return 0;
        }
+
+       /* This is, so far, the only case in which we indicate
+        * failure: we can't even decode the instruction. We've
+        * implemented the common cases above, and recently this
+        * failure has been seen only when the RIP is set to some
+        * bizarre value and we start fetching instructions from
+        * (e.g.) the middle of a page table. PTEs look like IO
+        * instructions to the CPU.
+        */
        printf("unknown IO %p %x %x\n", ip8, *ip8, *ip16);
        return -1;
 }