abortive try at setting up tables. Failed miserably.
authorRonald G. Minnich <rminnich@gmail.com>
Fri, 28 Aug 2015 01:55:40 +0000 (18:55 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Mon, 2 Nov 2015 23:53:49 +0000 (18:53 -0500)
We're going to have to create our own acpi tables. Dammit.

Signed-off-by: Ronald G. Minnich <rminnich@gmail.com>
Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
kern/arch/x86/devarch.c
kern/arch/x86/vmm/intel/vmx.c
kern/drivers/dev/acpi.c
kern/include/acpi.h
tests/vmm/vmrunkernel.c
user/vmm/decode.c
user/vmm/io.c

index 8f0f541..90721c6 100644 (file)
@@ -54,9 +54,9 @@ enum {
        Qiow,
        Qiol,
        Qgdb,
-       Qbase,
        Qmapram,
        Qrealmem,
+       Qbase,
 
        Qmax = 16,
 };
@@ -74,7 +74,7 @@ static struct dirtab archdir[Qmax] = {
        {"iol", {Qiol, 0}, 0, 0666},
        {"gdb", {Qgdb, 0}, 0, 0660},
        {"mapram", {Qmapram, 0}, 0, 0444},
-       {"realmodemem", {Qrealmem, 0}, 0, 0660},
+       {"realmodemem", {Qrealmem, 0}, 0, 0664},
 };
 
 spinlock_t archwlock;                  /* the lock is only for changing archdir */
@@ -391,6 +391,11 @@ static long archread(struct chan *c, void *a, long n, int64_t offset)
                case Qioalloc:
                        break;
 
+               case Qrealmem:
+                       printk("readmem %p %p %p %p %p\n",offset, a, n, KADDR(0), 1048576);
+                       return readmem(offset, a, n, KADDR(0), 1048576);
+                       break;
+
                default:
                        if (c->qid.path < narchdir && (fn = readfn[c->qid.path]))
                                return fn(c, a, n, offset);
index 43c179d..d87596d 100644 (file)
@@ -892,6 +892,7 @@ vmx_dump_cpu(struct vmx_vcpu *vcpu)
        vmx_get_cpu(vcpu);
        printk("GUEST_INTERRUPTIBILITY_INFO: 0x%08x\n",  vmcs_readl(GUEST_INTERRUPTIBILITY_INFO));
        printk("VM_ENTRY_INTR_INFO_FIELD 0x%08x\n", vmcs_readl(VM_ENTRY_INTR_INFO_FIELD));
+       printk("EXIT_QUALIFICATION 0x%08x\n", vmcs_read32(EXIT_QUALIFICATION));
        vcpu->regs.tf_rip = vmcs_readl(GUEST_RIP);
        vcpu->regs.tf_rsp = vmcs_readl(GUEST_RSP);
        flags = vmcs_readl(GUEST_RFLAGS);
index 6de78ad..9ff3eb8 100644 (file)
@@ -64,6 +64,7 @@ static struct dirtab acpidir[] = {
        {"acpipretty", {Qpretty}, 0, 0444},
        {"ioapic", {Qioapic}, 0, 0444},
        {"apic", {Qapic}, 0, 0444},
+       {"raw", {Qraw}, 0, 0444},
 };
 
 /*
@@ -884,7 +885,7 @@ static char *dumpmadt(char *start, char *end, struct Madt *apics)
        struct Apicst *st;
 
        start =
-               seprintf(start, end, "acpi: madt lapic paddr %llux pcat %d:\n",
+               seprintf(start, end, "acpi: madt lapic paddr %p pcat %d:\n",
                                 apics->lapicpa, apics->pcat);
        for (st = apics->st; st != NULL; st = st->next)
 
@@ -1178,10 +1179,16 @@ static void acpirsdptr(void)
        uintptr_t sdtpa;
 
        if ((rsd = rsdsearch("RSD PTR ")) == NULL) {
+               printk("NO RSDP\n");
                return;
        }
 
+
        assert(sizeof(struct Sdthdr) == 36);
+       printd("/* RSDP */ struct Rsdp = {%08c, %x, %06c, %x, %p, %d, %p, %x}\n",
+              rsd->signature, rsd->rchecksum, rsd->oemid, rsd->revision,
+              *(uint32_t *)rsd->raddr, *(uint32_t *)rsd->length,
+              *(uint32_t *)rsd->xaddr, rsd->xchecksum);
 
        printd("acpi: RSD PTR@ %#p, physaddr $%p length %ud %#llux rev %d\n",
                   rsd, l32get(rsd->raddr), l32get(rsd->length),
@@ -1567,6 +1574,9 @@ static long acpiread(struct chan *c, void *a, long n, int64_t off)
        switch (q) {
                case Qdir:
                        return devdirread(c, a, n, acpidir, ARRAY_SIZE(acpidir), acpigen);
+               case Qraw:
+                       return readmem(off, a, n, ttext, tlen);
+                       break;
                case Qtbl:
                        s = ttext;
                        e = ttext + tlen;
index 0c46678..284ed4c 100644 (file)
@@ -80,6 +80,7 @@ enum {
        Qpretty,
        Qioapic,
        Qapic,
+       Qraw,
 };
 
 /*
index 019941c..a43295f 100644 (file)
 #include <vmm/virtio_ids.h>
 #include <vmm/virtio_config.h>
 
+/* Kind of sad what a total clusterf the pc world is. By 1999, you could just scan the hardware 
+ * and work it out. But 2005, that was no longer possible. How sad. 
+ * so we have to fake acpi to make it all work. !@#$!@#$#.
+ * This will be copied to memory at 0xe0000, so the kernel can find it.
+ */
+/* assume they're all 256 bytes long just to make it easy. Just have pointers that point to aligned things. */
+
+struct Rsdp rsdp = {
+       .signature = "RSDP PTR ",
+       .rchecksum = 0,
+       .oemid = "AKAROS",
+       .raddr = [0x00, 0x01, 0xe0, 0x00], // 0x00e00100
+       .revision = 2,
+       .length = 36,
+};
+
+/* This has to be dropped into memory, then the other crap just follows it.
+ * this starts at 0xe00100
+ */
+struct Sdthdr fmadt = {
+       .sig = "MADT",
+       .length = 0,
+       .rev = 0,
+       .csum = 0,
+       .oemid = "AKAROS",
+       .oemtblid = "GOOGGOOD",
+       .oemrev = "WORK",
+       .creatorid = "NAN ",
+       .creatorrev = "WAN "
+};
+
+struct Madt madt = {
+       .lapicpa = 0xfee00000ULL,
+       .pcat = 0,
+       // Intel screwed this up. They put a pointer here, but it seems to imply an array? Who knows? 
+       .st = 0x00e00200;
+};
+
+struct Apicst Apic0 = {.type = 0, .next = 0x00e00300, .pid = 0, .id = 0};
+struct Apicst Apic1 = {.type = 1, .next = 0x00000000, .id = 1, .ibase = 0xfec00000, .ibase 0};
+       
+/* the array of things. These get copied to consecutive 256-byte boundary areas starting at 0xe0000 */
+void *apicarray[] = { &rsdp, &fmadt, &madt, &Apic0, &Apic1};
 /* this test will run the "kernel" in the negative address space. We hope. */
-int *mmap_blob;
+void *low1m;
+uint8_t low4k[4096];
 unsigned long long stack[1024];
 volatile int shared = 0;
 volatile int quit = 0;
@@ -43,6 +87,7 @@ unsigned long long *p512, *p1, *p2m;
 void **my_retvals;
 int nr_threads = 3;
 int debug = 0;
+int resumeprompt = 0;
 /* unlike Linux, this shared struct is for both host and guest. */
 //     struct virtqueue *constoguest = 
 //             vring_new_virtqueue(0, 512, 8192, 0, inpages, NULL, NULL, "test");
@@ -164,10 +209,16 @@ vqs: {
        }
 };
 
+void lowmem() {
+       __asm__ __volatile__ (".section .lowmem, \"aw\"\n\tlow: \n\t.=0x1000\n\t.align 0x100000\n\t.previous\n");
+}
+
 int main(int argc, char **argv)
 {
        uint64_t virtiobase = 0x100000000ULL;
-       void *rsdp = (void *)0;
+       // lowmem is a bump allocated pointer to 2M at the "physbase" of memory 
+       void *lowmem = (void *) 0x1000000;
+       void *rsdp = (void *) 0x1000000;
        struct vmctl vmctl;
        int amt;
        int vmmflags = 0; // Disabled probably forever. VMM_VMCALL_PRINTF;
@@ -178,6 +229,7 @@ int main(int argc, char **argv)
        int kfd = -1;
        static char cmd[512];
        void *coreboot_tables = (void *) 0x1165000;
+printf("%p %p %p %p\n", PGSIZE, PGSHIFT, PML1_SHIFT, PML1_PTE_REACH);
 
        // mmap is not working for us at present.
        if ((uint64_t)_kernel > GKERNBASE) {
@@ -185,6 +237,7 @@ int main(int argc, char **argv)
                exit(1);
        }
        memset(_kernel, 0, sizeof(_kernel));
+       memset(lowmem, 0xff, 2*1048576);
 
        if (fd < 0) {
                perror("#cons/sysctl");
@@ -242,22 +295,46 @@ int main(int argc, char **argv)
                x += amt;
        }
        fprintf(stderr, "Read in %d bytes\n", x-kerneladdress);
-
-       fprintf(stderr, "Run with %d cores and vmmflags 0x%x\n", nr_gpcs, vmmflags);
-       if (ros_syscall(SYS_setup_vmm, nr_gpcs, vmmflags, 0, 0, 0, 0) != nr_gpcs) {
-               perror("Guest pcore setup failed");
-               exit(1);
-       }
+       close(kfd);
        /* blob that is faulted in from the EPT first.  we need this to be in low
         * memory (not above the normal mmap_break), so the EPT can look it up.
         * Note that we won't get 4096.  The min is 1MB now, and ld is there. */
-       mmap_blob = mmap((int*)4096, PGSIZE, PROT_READ | PROT_WRITE,
+       low1m = mmap((int*)4096, MiB-4096, PROT_READ | PROT_WRITE,
                         MAP_ANONYMOUS, -1, 0);
-       if (mmap_blob == MAP_FAILED) {
-               perror("Unable to mmap");
+       if (low1m != (void *)4096) {
+               perror("Unable to mmap low 1m");
                exit(1);
        }
+       memset(low1m, 0xff, MiB-4096);
 
+       /* read in acpi. */
+       kfd = open("#P/realmodemem", 0);
+       if (kfd < 0) {
+               perror("#P/realmodemem");
+       }
+       amt = read(kfd, low4k, sizeof(low4k));
+       if (amt < sizeof(low4k)) {
+               fprintf(stderr, "read 4k: %d\n", amt);
+               perror("read");
+               exit(1);
+       }
+       memset(low4k, 0xff, 4096);
+       amt = read(kfd, low1m, MiB-4096);
+       if (amt < MiB-4096) {
+               fprintf(stderr, "read mib-4k: %d\n", amt);
+               perror("read");
+               exit(1);
+       }
+       close(kfd);
+       printf("Read in %d bytes for RSDP\n", MiB-4096);
+       hexdump(stdout, low4k, 4096);
+
+       if (ros_syscall(SYS_setup_vmm, nr_gpcs, vmmflags, 0, 0, 0, 0) != nr_gpcs) {
+               perror("Guest pcore setup failed");
+               exit(1);
+       }
+
+       fprintf(stderr, "Run with %d cores and vmmflags 0x%x\n", nr_gpcs, vmmflags);
        mcp = 1;
        if (mcp) {
                my_retvals = malloc(sizeof(void*) * nr_threads);
@@ -328,35 +405,50 @@ int main(int argc, char **argv)
                int c;
                uint8_t byte;
                vmctl.command = REG_RIP;
-               if (maxresume-- == 0)
+               if (maxresume-- == 0) {
                        debug = 1;
+                       resumeprompt = 1;
+               }
                if (debug) {
                        printf("RIP %p, shutdown 0x%x\n", vmctl.regs.tf_rip, vmctl.shutdown);
                        showstatus(stdout, &vmctl);
+               }
+               if (resumeprompt) {
                        printf("RESUME?\n");
                        c = getchar();
                        if (c == 'q')
                                break;
                }
                if (vmctl.shutdown == SHUTDOWN_EPT_VIOLATION) {
-                       uint64_t gpa;
-                       uint64_t *regp;
+                       uint64_t gpa, *regp, val;
                        uint8_t regx;
-                       int store;
-                       if (decode(&vmctl, &gpa, &regx, &regp, &store)) {
+                       int store, size;
+                       int advance;
+                       if (decode(&vmctl, &gpa, &regx, &regp, &store, &size, &advance)) {
+                               printf("RIP %p, shutdown 0x%x\n", vmctl.regs.tf_rip, vmctl.shutdown);
+                               showstatus(stdout, &vmctl);
                                quit = 1;
                                break;
                        }
+                       if (debug) printf("%p %p %p %p %p %p\n", gpa, regx, regp, store, size, advance);
                        if ((gpa & ~0xfffULL) == virtiobase) {
                                if (debug) printf("DO SOME VIRTIO\n");
+                               // Lucky for us the various virtio ops are well-defined.
                                virtio_mmio(&vmctl, gpa, regx, regp, store);
-                       } else if (gpa == 0x40e) {
-                               *regp = (uint64_t) rsdp;
+                       } else if (gpa < 4096) {
+                               uint64_t val = 0;
+                               memmove(&val, &low4k[gpa], size);
+                               hexdump(stdout, &low4k[gpa], size);
+                               printf("Low 1m, code %p read @ %p, size %d, val %p\n", vmctl.regs.tf_rip, gpa, size, val);
+                               memmove(regp, &low4k[gpa], size);
+                               hexdump(stdout, regp, size);
                        } else {
                                printf("EPT violation: can't handle %p\n", gpa);
                                quit = 1;
                                break;
                        }
+                       vmctl.regs.tf_rip += advance;
+                       if (debug) printf("Advance rip by %d bytes to %p\n", advance, vmctl.regs.tf_rip);
                        vmctl.shutdown = 0;
                        vmctl.gpa = 0;
                        vmctl.command = REG_ALL;
@@ -369,14 +461,27 @@ int main(int argc, char **argv)
                                vmctl.regs.tf_rip += 3;
                                break;
                        case EXIT_REASON_EXTERNAL_INTERRUPT:
-                               fprintf(stderr, "XINT\n");
-                               // Just inject a GPF for now. See what shakes.
+                               //debug = 1;
+                               fprintf(stderr, "XINT 0x%x 0x%x\n", vmctl.intrinfo1, vmctl.intrinfo2);
                                vmctl.interrupt = 0x80000302; // b0d;
+                               // That sent an NMI and we got it.
+
+                               vmctl.interrupt = 0x80000320; // b0d;
+                               // This fails on entry
+                               
+                               vmctl.interrupt = 0x80000306; // b0d;
+                               // This succeedd in sending a UD.
+
+                               vmctl.interrupt = 0x8000030f; // b0d;
+                               
                                vmctl.command = RESUME;
                                break;
                        case EXIT_REASON_IO_INSTRUCTION:
                                printf("IO @ %p\n", vmctl.regs.tf_rip);
                                io(&vmctl);
+                               vmctl.shutdown = 0;
+                               vmctl.gpa = 0;
+                               vmctl.command = REG_ALL;
                                break;
                        case EXIT_REASON_HLT:
                                printf("\n================== Guest halted. RIP. =======================\n");
index 11f9f39..d717878 100644 (file)
@@ -43,23 +43,127 @@ int debug_decode = 0;
 
 static char *modrmreg[] = {"rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi"};
 
+// Since we at most have to decode less than half of each instruction, I'm trying to be dumb here.
+// Fortunately, for me, what's not hard.
+
+// Target size -- 1, 2, 4, or 8 bytes. We have yet to see 64 bytes. 
+// TODO: if we ever see it, test the prefix. Since this only supports the low 1M,
+// that's not likely.
+static int target(void *insn, int *store) 
+{
+       *store = 0;
+       int s = -1;
+       uint8_t *byte = insn;
+       uint16_t *word = insn;
+
+       if (*byte == 0x66) {
+               s = target(insn+1,store);
+               // flip the sense of s.
+               s = s == 4 ? 2 : 4;
+               return s;
+       }
+       switch(*byte) {
+       case 0x3a:
+       case 0x8a:
+       case 0x88:
+               s = 1;
+               break;
+       case 0x89:
+       case 0x8b:
+               s = 2;
+               break;
+       case 0x81:
+               s = 4;  
+               break;
+       case 0x0f:
+       switch(*word) {
+               case 0xb70f:
+                       s = 4;
+                       break;
+               default:
+                       fprintf(stderr, "can't get size of %02x/%04x @ %p\n", *byte, *word, byte);
+                       return -1;
+                       break;
+               }
+               break;
+       default:
+               fprintf(stderr, "can't get size of %02x @ %p\n", *byte, byte);
+               return -1;
+               break;
+       }
+
+       switch(*byte) {
+       case 0x3a:
+       case 0x8a:
+       case 0x88:
+       case 0x89:
+       case 0x8b:
+       case 0x81:
+               *store = !(*byte & 2);
+       }
+       return s;
+}
+
 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
+static int insize(void *rip)
+{
+       uint8_t *kva = rip;
+       int advance = 3;
+       /* the dreaded mod/rm byte. */
+       int mod = kva[1]>>6;
+       int rm = kva[1] & 7;
+
+       switch(kva[0]) {
+       default: 
+               fprintf(stderr, "BUG! %s got 0x%x\n", __func__, kva[0]);
+       case 0x0f: 
+               break;
+       case 0x81:
+               advance = 6;
+               break;
+       case 0x3a:
+       case 0x8a:
+       case 0x88:
+       case 0x89:
+       case 0x8b:
+               switch (mod) {
+               case 0: 
+                       advance = 2 + (rm == 4);
+                       break;
+               case 1:
+                       advance = 3 + (rm == 4);
+                       break;
+               case 2: 
+                       advance = 6 + (rm == 4);
+                       break;
+               case 3:
+                       advance = 2;
+                       break;
+               }
+               break;
+       }
+       return advance;
+}
+
+// This is a very limited function. It's only here to manage virtio-mmio and low memory
 // 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.
+// enough info to let you emulate the operation if you want. Because we have the failing physical
+// address (gpa) the decode is far simpler because we only need to find the register, how many bytes
+// to move, and how big the instruction is. I thought about bringing in emulate.c from kvm from xen,
+// but it has way more stuff than we need.
 // 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 decode(struct vmctl *v, uint64_t *gpa, uint8_t *destreg, uint64_t **regp, int *store, int *size, int *advance)
 {
-       int advance = 3; /* how much to move the IP forward at the end. 3 is a good default. */
-       //DPRINTF("v is %p\n", v);
+
+       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!
@@ -69,7 +173,7 @@ int decode(struct vmctl *v, uint64_t *gpa, uint8_t *destreg, uint64_t **regp, in
        // 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);
+       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
@@ -78,36 +182,17 @@ int decode(struct vmctl *v, uint64_t *gpa, uint8_t *destreg, uint64_t **regp, in
        // 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);
+       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]);
+       // fail fast. If we can't get the size we're done.
+       *size = target(kva, store);
+       if (*size < 0)
                return -1;
-       }
 
        uint16_t ins = *(uint16_t *)kva;
-       //DPRINTF("ins is %04x\n", ins);
+       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;
-               }
-       }
+       *advance = insize(kva);
 
        *destreg = (ins>>11) & 7;
        // Our primitive approach wins big here.
@@ -140,8 +225,21 @@ int decode(struct vmctl *v, uint64_t *gpa, uint8_t *destreg, uint64_t **regp, in
                *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;
 }
 
+#if 0
+// stupid emulator since what we need is so limited.
+int emu(struct vmctl *v, uint64_t gpa, uint8_t destreg, uint64_t *regp, int store, int size, int advance)
+{
+       uint8_t *kva = f->regs.tf_rip;
+
+       if (
+       switch(kva[0]) {
+
+                               val = *(uint64_t*) (lowmem + gpa); 
+                               printf("val %p ", val);
+                               memcpy(regp, &val, size);
+
+}
+#endif
index 027184e..600e968 100644 (file)
@@ -153,7 +153,7 @@ int io(struct vmctl *v)
                        //printf("Set cfc ");
                        return configwrite32(edx, v->regs.tf_rax);
                }
-               printf("unhandled IO address dx @%p is 0x%x\n", ip8, edx);
+               printf("(out rax, edx): unhandled IO address dx @%p is 0x%x\n", ip8, edx);
                return -1;
        }
        // out %al, %dx
@@ -168,7 +168,7 @@ int io(struct vmctl *v)
                        //printf("ignoring write to cfc ");
                        return 0;
                }
-               printf("unhandled IO address dx @%p is 0x%x\n", ip8, edx);
+               printf("out al, dx: unhandled IO address dx @%p is 0x%x\n", ip8, edx);
                return -1;
        }
        if (*ip8 == 0xec) {