parlib: Implement join/detach() for all uthreads
[akaros.git] / user / vmm / io.c
index 027184e..a640c27 100644 (file)
@@ -1,5 +1,4 @@
-#include <stdio.h> 
-#include <pthread.h>
+#include <stdio.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
@@ -18,9 +17,8 @@
 #include <vmm/virtio.h>
 #include <vmm/virtio_mmio.h>
 #include <vmm/virtio_ids.h>
-
-/* nowhere on my linux system. */
-#define ARRAY_SIZE(x) (sizeof((x))/sizeof((x)[0]))
+#include <vmm/sched.h>
+#include <ros/arch/trapframe.h>
 
 /* crude PCI bus. Just enough to get virtio working. I would rather not add to this. */
 struct pciconfig {
@@ -29,7 +27,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)
         */
        {
@@ -49,68 +47,61 @@ void regp(uint32_t **reg)
        *reg = &allones;
        int devfn = (cf8>>8) & 0xff;
        //printf("devfn %d\n", devfn);
-       if (devfn < ARRAY_SIZE(pcibus))
+       if (devfn < COUNT_OF(pcibus))
                *reg = &pcibus[devfn].registers[(cf8>>2)&0x3f];
        //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
@@ -120,7 +111,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.
  */
-int io(struct vmctl *v)
+int io(struct guest_thread *vm_thread)
 {
 
        /* Get a pointer to the memory at %rip. This is quite messy and part of the
@@ -132,33 +123,50 @@ int io(struct vmctl *v)
        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 + 1M as our address
-        * TODO: put this in vmctl somewhere?
-        */
-       ip = v->regs.tf_rip & 0x3fffffff;
-       edx = v->regs.tf_rdx;
+       struct vm_trapframe *vm_tf = &(vm_thread->uthread.u_ctx.tf.vm_tf);
+
+       /* 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;
        //printf("io: ip16 %p\n", *ip16, edx);
 
        if (*ip8 == 0xef) {
-               v->regs.tf_rip += 1;
+               vm_tf->tf_rip += 1;
                /* out at %edx */
                if (edx == 0xcf8) {
                        //printf("Set cf8 ");
-                       return configaddr(v->regs.tf_rax);
+                       configaddr(vm_tf->tf_rax);
+                       return 0;
                }
                if (edx == 0xcfc) {
                        //printf("Set cfc ");
-                       return configwrite32(edx, v->regs.tf_rax);
+                       configwrite32(edx, vm_tf->tf_rax);
+                       return 0;
                }
-               printf("unhandled IO address dx @%p is 0x%x\n", ip8, edx);
-               return -1;
+               /* 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 0;
        }
        // out %al, %dx
        if (*ip8 == 0xee) {
-               v->regs.tf_rip += 1;
+               vm_tf->tf_rip += 1;
                /* out al %edx */
                if (edx == 0xcfb) { // special!
                        printf("Just ignore the damned cfb write\n");
@@ -168,29 +176,55 @@ int io(struct vmctl *v)
                        //printf("ignoring write to cfc ");
                        return 0;
                }
-               printf("unhandled IO address dx @%p is 0x%x\n", ip8, edx);
-               return -1;
+               /* 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 0;
        }
        if (*ip8 == 0xec) {
-               v->regs.tf_rip += 1;
+               vm_tf->tf_rip += 1;
                //printf("configread8 ");
-               return configread8(edx, &v->regs.tf_rax);
+               configread8(edx, &vm_tf->tf_rax);
+               return 0;
        }
        if (*ip8 == 0xed) {
-               v->regs.tf_rip += 1;
+               vm_tf->tf_rip += 1;
                if (edx == 0xcf8) {
                        //printf("read cf8 0x%lx\n", v->regs.tf_rax);
-                       v->regs.tf_rax = cf8;
+                       vm_tf->tf_rax = cf8;
                        return 0;
                }
                //printf("configread32 ");
-               return configread32(edx, &v->regs.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) {
-               v->regs.tf_rip += 2;
+               vm_tf->tf_rip += 2;
                //printf("configread16 ");
-               return configread16(edx, &v->regs.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;
 }