ACPI changes for DMAR and new directory hierarchy.
authorDan Cross <crossd@gmail.com>
Fri, 5 Feb 2016 20:36:13 +0000 (15:36 -0500)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 10 Feb 2016 15:49:19 +0000 (10:49 -0500)
Add the DMAR parser, and rationalize the ACPI directory
hierarchy to make it traversable from the shell. There
is additional work to do here on the latter, but that is
not critical path and this gets Gan the DMAR code he
needs for virtualization.

Signed-off-by: Dan Cross <crossd@gmail.com>
[ Fixed 16 -> KMALLOC_ALIGNMENT ]
Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
kern/arch/x86/devarch.c
kern/arch/x86/ioapic.c
kern/arch/x86/mpacpi.c
kern/arch/x86/topology.c
kern/drivers/dev/acpi.c
kern/drivers/timers/hpet.c
kern/drivers/timers/hpet.h
kern/include/acpi.h

index bbdec43..ca742d6 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
  * This file is part of the UCB release of Plan 9. It is subject to the license
  * terms in the LICENSE file found in the top-level directory of this
  * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
index a4f8f58..b0ccff9 100644 (file)
@@ -216,14 +216,20 @@ static int acpi_irq2ioapic(int irq)
  *             use MSI instead). */
 static int acpi_make_rdt(int tbdf, int irq, int busno, int devno)
 {
-       struct Apicst *st;
+       struct Atable *at;
+       struct Apicst *st, *lst;
        uint32_t lo;
        int pol, edge_level, ioapic_nr, gsi_irq;
 
-       for (st = apics->st; st != NULL; st = st->next) {
-               if (st->type == ASintovr) {
-                       if (st->intovr.irq == irq)
+       at = apics;
+       st = NULL;
+       for (int i = 0; i < at->nchildren; i++) {
+               lst = at->children[i]->tbl;
+               if (lst->type == ASintovr) {
+                       if (lst->intovr.irq == irq) {
+                               st = lst;
                                break;
+                       }
                }
        }
        if (st) {
index acccc43..03a2ce8 100644 (file)
 #include <arch/ioapic.h>
 #include <arch/topology.h>
 
-extern struct Madt *apics;
-
 int mpacpi(int ncleft)
 {
        char *already;
        int np, bp;
        struct apic *apic;
        struct Apicst *st;
+       struct Madt *mt;
 
        /* If we don't have an mpisabusno yet, it's because the MP tables failed to
         * parse.  So we'll just take the last one available.  I think we're
@@ -41,12 +40,16 @@ int mpacpi(int ncleft)
 
        if (apics == NULL)
                return ncleft;
+       mt = apics->tbl;
+       if (mt == NULL)
+               return ncleft;
 
        printd("APIC lapic paddr %#.8llux, flags %#.8ux\n",
-                  apics->lapicpa, apics->pcat);
+                  mt->lapicpa, mt->pcat);
        np = 0;
        printd("apics->st %p\n", apics->st);
-       for (st = apics->st; st != NULL; st = st->next) {
+       for (int i = 0; i < apics->nchildren; i++) {
+               st = apics->children[i]->tbl;
                already = "";
                switch (st->type) {
                        case ASlapic:
@@ -60,7 +63,7 @@ int mpacpi(int ncleft)
                                        already = "(mp)";
                                } else if (ncleft != 0) {
                                        ncleft--;
-                                       apicinit(st->lapic.id, apics->lapicpa, bp);
+                                       apicinit(st->lapic.id, mt->lapicpa, bp);
                                } else
                                        already = "(off)";
 
index 69a8155..4f220a5 100644 (file)
@@ -7,6 +7,7 @@
 #include <stddef.h>
 #include <kmalloc.h>
 #include <string.h>
+#include <ns.h>
 #include <acpi.h>
 #include <arch/arch.h>
 #include <arch/apic.h>
@@ -78,13 +79,16 @@ static void set_socket_ids(void)
  * apid_id. */
 static int find_numa_domain(int apic_id)
 {
-       struct Srat *temp = srat;
-       while (temp) {
-               if (temp->type == SRlapic) {
+       if (srat == NULL)
+               return -1;
+
+       for (int i = 0; i < srat->nchildren; i++) {
+               struct Srat *temp = srat->children[i]->tbl;
+
+               if (temp != NULL && temp->type == SRlapic) {
                        if (temp->lapic.apic == apic_id)
                                return temp->lapic.dom;
                }
-               temp = temp->next;
        }
        return -1;
 }
@@ -93,24 +97,28 @@ static int find_numa_domain(int apic_id)
  * cpu_topology_info struct. */
 static void set_num_cores(void)
 {
-       struct Apicst *temp = apics->st;
-       while (temp) {
-               if (temp->type == ASlapic)
+       if (apics == NULL)
+               return;
+
+       for (int i = 0; i < apics->nchildren; i++) {
+               struct Apicst *temp = apics->children[i]->tbl;
+
+               if (temp != NULL && temp->type == ASlapic)
                        num_cores++;
-               temp = temp->next;
        }
 }
 
 /* Determine if srat has a unique numa domain compared to to all of the srat
  * records in list_head that are of type SRlapic. */
-static bool is_unique_numa(struct Srat *srat, struct Srat *list_head)
+static bool is_unique_numa(struct Srat *srat, struct Atable **tail,
+                           size_t begin, size_t end)
 {
-       while (list_head) {
-               if (list_head->type == SRlapic) {
-                       if (srat->lapic.dom == list_head->lapic.dom)
+       for (int i = begin; i < end; i++) {
+               struct Srat *st = tail[i]->tbl;
+
+               if (st->type == SRlapic)
+                       if (srat->lapic.dom == st->lapic.dom)
                                return FALSE;
-               }
-               list_head = list_head->next;
        }
        return TRUE;
 }
@@ -120,13 +128,18 @@ static bool is_unique_numa(struct Srat *srat, struct Srat *list_head)
 static int get_num_numa(void)
 {
        int numa = 0;
-       struct Srat *temp = srat;
-       while (temp) {
-               if (temp->type == SRlapic)
-                       if (is_unique_numa(temp, temp->next))
+
+       if (srat == NULL)
+               return 0;
+
+       for (int i = 0; i < srat->nchildren; i++) {
+               struct Srat *temp = srat->children[i]->tbl;
+
+               if (temp != NULL && temp->type == SRlapic)
+                       if (is_unique_numa(temp, srat->children, i, srat->nchildren))
                                numa++;
-               temp = temp->next;
        }
+
        return numa;
 }
 
@@ -140,13 +153,13 @@ static void set_num_numa(void)
  * cpu_topology_info struct. */
 static void set_max_apic_id(void)
 {
-       struct Apicst *temp = apics->st;
-       while (temp) {
+       for (int i = 0; i < apics->nchildren; i++) {
+               struct Apicst *temp = apics->children[i]->tbl;
+
                if (temp->type == ASlapic) {
                        if (temp->lapic.id > max_apic_id)
                                max_apic_id = temp->lapic.id;
                }
-               temp = temp->next;
        }
 }
 
@@ -166,11 +179,11 @@ static void init_os_coreid_lookup(void)
         * necessary because there is no ordering to the linked list we are
         * pulling these ids from. After this, loop back through and set the
         * mapping appropriately. */
-       struct Apicst *temp = apics->st;
-       while (temp) {
+       for (int i = 0; i < apics->nchildren; i++) {
+               struct Apicst *temp = apics->children[i]->tbl;
+
                if (temp->type == ASlapic)
                        os_coreid_lookup[temp->lapic.id] = 0;
-               temp = temp->next;
        }
        int os_coreid = 0;
        for (int i = 0; i <= max_apic_id; i++)
index 9a255eb..8ba9eb4 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
  * This file is part of the UCB release of Plan 9. It is subject to the license
  * terms in the LICENSE file found in the top-level directory of this
  * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
@@ -20,7 +20,9 @@
 #include <pmap.h>
 #include <smp.h>
 #include <ip.h>
+#include <ns.h>
 #include <acpi.h>
+#include <slice.h>
 
 #include "../timers/hpet.h"
 
 #include <arch/pci.h>
 #endif
 
+/* -----------------------------------------------------------------------------
+ * Basic ACPI device.
+ *
+ * The qid.Path will be made unique by incrementing lastpath. lastpath starts
+ * at Qroot.
+ *
+ * Qtbl will return a pointer to the Atable, which includes the signature, OEM
+ * data, and so on.
+ *
+ * Raw, at any level, dumps the raw table at that level, which by the ACPI
+ * flattened tree layout will include all descendents.
+ *
+ * Qpretty, at any level, will print the pretty form for that level and all
+ * descendants.
+ */
+enum {
+       Qroot = 0,
+
+       // The type is the qid.path mod NQtypes.
+       Qdir = 0,
+       Qpretty,
+       Qraw,
+       Qtbl,
+       NQtypes,
+
+       QIndexShift = 8,
+       QIndexMask = (1 << QIndexShift) - 1,
+};
+
+#define ATABLEBUFSZ    ROUNDUP(sizeof(struct Atable), KMALLOC_ALIGNMENT)
+
+static uint64_t lastpath;
+static struct slice emptyslice;
+static struct Atable **atableindex;
 struct dev acpidevtab;
 
 static char *devname(void)
@@ -39,97 +75,143 @@ static char *devname(void)
  * ACPI 4.0 Support.
  * Still WIP.
  *
- * This driver locates tables and parses only the FADT
- * and the XSDT. All other tables are mapped and kept there
- * for the user-level interpreter.
+ * This driver locates tables and parses only a small subset
+ * of tables. All other tables are mapped and kept for the user-level
+ * interpreter.
  */
-
-static struct Atable *acpifadt(uint8_t *, int);
-static struct Atable *acpitable(uint8_t *, int);
-static struct Atable *acpimadt(uint8_t *, int);
-static struct Atable *acpimsct(uint8_t *, int);
-static struct Atable *acpisrat(uint8_t *, int);
-static struct Atable *acpislit(uint8_t *, int);
-
 static struct cmdtab ctls[] = {
        {CMregion, "region", 6},
        {CMgpe, "gpe", 3},
 };
 
-static struct dirtab acpidir[] = {
-       {".", {Qdir, 0, QTDIR}, 0, DMDIR | 0555},
-       {"acpictl", {Qctl}, 0, 0666},
-       {"acpitbl", {Qtbl}, 0, 0444},
-       {"acpiregio", {Qio}, 0, 0666},
-       {"acpipretty", {Qpretty}, 0, 0444},
-       {"ioapic", {Qioapic}, 0, 0444},
-       {"apic", {Qapic}, 0, 0444},
-       {"raw", {Qraw}, 0, 0444},
-};
-
-/*
- * The DSDT is always given to the user interpreter.
- * Tables listed here are also loaded from the XSDT:
- * MSCT, MADT, and FADT are processed by us, because they are
- * required to do early initialization before we have user processes.
- * Other tables are given to the user level interpreter for
- * execution.
- *
- * These historically returned a value to tell acpi whether or not it was okay
- * to unmap the table.  (return 0 means there was no table, meaning it was okay
- * to unmap).  We just use the kernbase mapping, so it's irrelevant. */
-static struct Parse ptables[] = {
-       {"FACP", acpifadt},
-       {"APIC", acpimadt,},
-       {"SRAT", acpisrat,},
-       {"SLIT", acpislit,},
-       {"MSCT", acpimsct,},
-       {"SSDT", acpitable,},
-       {"HPET", acpihpet,},
-};
-
 static struct Facs *facs;              /* Firmware ACPI control structure */
-static struct Fadt fadt;               /* Fixed ACPI description. To reach ACPI registers */
+static struct Fadt *fadt;              /* Fixed ACPI description to reach ACPI regs */
+static struct Atable *root;
 static struct Xsdt *xsdt;              /* XSDT table */
 static struct Atable *tfirst;  /* loaded DSDT/SSDT/... tables */
 static struct Atable *tlast;   /* pointer to last table */
-struct Madt *apics;                            /* APIC info */
-struct Srat *srat;                             /* System resource affinity, used by physalloc */
-static struct Slit *slit;              /* System locality information table used by the scheduler */
-static struct Msct *msct;              /* Maximum system characteristics table */
+struct Atable *apics;                  /* APIC info */
+struct Atable *srat;                   /* System resource affinity used by physalloc */
+struct Atable *dmar;
+static struct Slit *slit;              /* Sys locality info table used by scheduler */
+static struct Atable *mscttbl;         /* Maximum system characteristics table */
 static struct Reg *reg;                        /* region used for I/O */
 static struct Gpe *gpes;               /* General purpose events */
 static int ngpes;
 
 static char *regnames[] = {
        "mem", "io", "pcicfg", "embed",
-       "smb", "cmos", "pcibar",
+       "smb", "cmos", "pcibar", "ipmi",
 };
 
+/*
+ * Lists to store RAM that we copy ACPI tables into. When we map a new
+ * ACPI list into the kernel, we copy it into a specifically RAM buffer
+ * (to make sure it's not coming from e.g. slow device memory). We store
+ * pointers to those buffers on these lists.
+ */
+struct Acpilist {
+       struct Acpilist *next;
+       size_t size;
+       int8_t raw[];
+};
+
+static struct Acpilist *acpilists;
+
+/*
+ * Produces an Atable at some level in the tree. Note that Atables are
+ * isomorphic to directories in the file system namespace; this code
+ * ensures that invariant.
+ */
+struct Atable *mkatable(struct Atable *parent,
+                        int type, char *name, uint8_t *raw,
+                        size_t rawsize, size_t addsize)
+{
+       void *m;
+       struct Atable *t;
+
+       m = kzmalloc(ATABLEBUFSZ + addsize, KMALLOC_WAIT);
+       if (m == NULL)
+               panic("no memory for more aml tables");
+       t = m;
+       t->parent = parent;
+       t->tbl = NULL;
+       if (addsize != 0)
+               t->tbl = m + ATABLEBUFSZ;
+       t->rawsize = rawsize;
+       t->raw = raw;
+       strlcpy(t->name, name, sizeof(t->name));
+       mkqid(&t->qid,  (lastpath << QIndexShift) + Qdir, 0, QTDIR);
+       mkqid(&t->rqid, (lastpath << QIndexShift) + Qraw, 0, 0);
+       mkqid(&t->pqid, (lastpath << QIndexShift) + Qpretty, 0, 0);
+       mkqid(&t->tqid, (lastpath << QIndexShift) + Qtbl, 0, 0);
+       lastpath++;
+
+       return t;
+}
+
+struct Atable *finatable(struct Atable *t, struct slice *slice)
+{
+       size_t n;
+       struct Atable *tail;
+       struct dirtab *dirs;
+
+       n = slice_len(slice);
+       t->nchildren = n;
+       t->children = (struct Atable **)slice_finalize(slice);
+       dirs = kreallocarray(NULL, n + NQtypes, sizeof(struct dirtab),
+                            KMALLOC_WAIT);
+       assert(dirs != NULL);
+       dirs[0] = (struct dirtab){ ".",      t->qid,   0, 0555 };
+       dirs[1] = (struct dirtab){ "pretty", t->pqid,  0, 0444 };
+       dirs[2] = (struct dirtab){ "raw",    t->rqid,  0, 0444 };
+       dirs[3] = (struct dirtab){ "table",  t->tqid,  0, 0444 };
+       for (size_t i = 0; i < n; i++) {
+               strlcpy(dirs[i + NQtypes].name, t->children[i]->name, KNAMELEN);
+               dirs[i + NQtypes].qid = t->children[i]->qid;
+               dirs[i + NQtypes].length = 0;
+               dirs[i + NQtypes].perm = DMDIR | 0555;
+       }
+       t->cdirs = dirs;
+       tail = NULL;
+       while (n-- > 0) {
+               t->children[n]->next = tail;
+               tail = t->children[n];
+       }
+
+       return t;
+}
+
+struct Atable *finatable_nochildren(struct Atable *t)
+{
+       return finatable(t, &emptyslice);
+}
+
 static char *dumpGas(char *start, char *end, char *prefix, struct Gas *g);
+static void dumpxsdt(void);
 
 static char *acpiregstr(int id)
 {
        static char buf[20];            /* BUG */
 
-       if (id >= 0 && id < ARRAY_SIZE(regnames)) {
+       if (id >= 0 && id < ARRAY_SIZE(regnames))
                return regnames[id];
-       }
        seprintf(buf, buf + sizeof(buf), "spc:%#x", id);
        return buf;
 }
 
 static int acpiregid(char *s)
 {
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(regnames); i++)
-               if (strcmp(regnames[i], s) == 0) {
+       for (int i = 0; i < ARRAY_SIZE(regnames); i++)
+               if (strcmp(regnames[i], s) == 0)
                        return i;
-               }
        return -1;
 }
 
+/*
+ * TODO(rminnich): Fix these if we're ever on a different-endian machine.
+ * They are specific to little-endian processors and are not portable.
+ */
 static uint8_t mget8(uintptr_t p, void *unused)
 {
        uint8_t *cp = (uint8_t *) p;
@@ -208,8 +290,10 @@ static void ioset32(uintptr_t p, uint32_t v, void *unused)
        outl(p, v);
 }
 
-/* TODO: these cfgs are hacky. maybe all the struct Reg should have struct
- * pci_device or something? */
+/*
+ * TODO(rminnich): these cfgs are hacky. Maybe all the struct Reg should have
+ * struct pci_device or something?
+ */
 static uint8_t cfgget8(uintptr_t p, void *r)
 {
        struct Reg *ro = r;
@@ -377,51 +461,24 @@ static long regio(struct Reg *r, void *p, uint32_t len, uintptr_t off, int iswr)
        return len;
 }
 
-struct Atable *new_acpi_table(uint8_t * p)
-{
-       struct Atable *t;
-       struct Sdthdr *h;
-
-       t = kzmalloc(sizeof(struct Atable), 0);
-       if (t == NULL)
-               panic("no memory for more aml tables");
-       t->tbl = p;
-       h = (struct Sdthdr *)t->tbl;
-       t->is64 = h->rev >= 2;
-       t->dlen = l32get(h->length) - Sdthdrsz;
-       memmove(t->sig, h->sig, sizeof(h->sig));
-       t->sig[sizeof(t->sig) - 1] = 0;
-       memmove(t->oemid, h->oemid, sizeof(h->oemid));
-       t->oemtblid[sizeof(t->oemtblid) - 1] = 0;
-       memmove(t->oemtblid, h->oemtblid, sizeof(h->oemtblid));
-       t->oemtblid[sizeof(t->oemtblid) - 1] = 0;
-       t->next = NULL;
-       if (tfirst == NULL)
-               tfirst = tlast = t;
-       else {
-               tlast->next = t;
-               tlast = t;
-       }
-       return t;
-}
-
-static void *sdtchecksum(void *addr, int len)
+/*
+ * Compute and return SDT checksum: '0' is a correct sum.
+ */
+static uint8_t sdtchecksum(void *addr, int len)
 {
        uint8_t *p, sum;
 
        sum = 0;
        for (p = addr; len-- > 0; p++)
                sum += *p;
-       if (sum == 0) {
-               return addr;
-       }
 
-       return NULL;
+       return sum;
 }
 
-static void *sdtmap(uintptr_t pa, int *n, int cksum)
+static void *sdtmap(uintptr_t pa, size_t *n, int cksum)
 {
        struct Sdthdr *sdt;
+       struct Acpilist *p;
 
        if (!pa) {
                printk("sdtmap: NULL pa\n");
@@ -429,39 +486,42 @@ static void *sdtmap(uintptr_t pa, int *n, int cksum)
        }
        sdt = KADDR_NOCHECK(pa);
        if (sdt == NULL) {
-               printk("acpi: vmap1: NULL\n");
+               printk("acpi: vmap: NULL\n");
                return NULL;
        }
        *n = l32get(sdt->length);
        if (!*n) {
-               printk("sdt has zero length!\n");
-               return NULL;
-       }
-       if ((sdt = KADDR_NOCHECK(pa)) == NULL) {
-               printk("acpi: NULL vmap\n");
+               printk("sdt has zero length: pa = %p, sig = %.4s\n", pa, sdt->sig);
                return NULL;
        }
-       if (cksum != 0 && sdtchecksum(sdt, *n) == NULL) {
-               printk("acpi: SDT: bad checksum\n");
+       if (cksum != 0 && sdtchecksum(sdt, *n) != 0) {
+               printk("acpi: SDT: bad checksum. pa = %p, len = %lu\n", pa, *n);
                return NULL;
        }
-       return sdt;
+       p = kzmalloc(sizeof(struct Acpilist) + *n, KMALLOC_WAIT);
+       if (p == NULL)
+               panic("sdtmap: memory allocation failed for %lu bytes", *n);
+       memmove(p->raw, (void *)sdt, *n);
+       p->size = *n;
+       p->next = acpilists;
+       acpilists = p;
+
+       return p->raw;
 }
 
 static int loadfacs(uintptr_t pa)
 {
-       int n;
+       size_t n;
 
        facs = sdtmap(pa, &n, 0);
-       if (facs == NULL) {
+       if (facs == NULL)
                return -1;
-       }
-       if (memcmp(facs, "FACS", 4) != 0) {
+       if (memcmp(facs->sig, "FACS", 4) != 0) {
                facs = NULL;
                return -1;
        }
-       /* no unmap */
 
+       /* no unmap */
        printd("acpi: facs: hwsig: %#p\n", facs->hwsig);
        printd("acpi: facs: wakingv: %#p\n", facs->wakingv);
        printd("acpi: facs: flags: %#p\n", facs->flags);
@@ -469,21 +529,23 @@ static int loadfacs(uintptr_t pa)
        printd("acpi: facs: xwakingv: %#p\n", facs->xwakingv);
        printd("acpi: facs: vers: %#p\n", facs->vers);
        printd("acpi: facs: ospmflags: %#p\n", facs->ospmflags);
+
        return 0;
 }
 
 static void loaddsdt(uintptr_t pa)
 {
-       int n;
+       size_t n;
        uint8_t *dsdtp;
 
        dsdtp = sdtmap(pa, &n, 1);
        if (dsdtp == NULL) {
+               printk("acpi: Failed to map dsdtp.\n");
                return;
        }
 }
 
-static void gasget(struct Gas *gas, uint8_t * p)
+static void gasget(struct Gas *gas, uint8_t *p)
 {
        gas->spc = p[0];
        gas->len = p[1];
@@ -494,9 +556,8 @@ static void gasget(struct Gas *gas, uint8_t * p)
 
 static char *dumpfadt(char *start, char *end, struct Fadt *fp)
 {
-       if (2 == 0) {
-               return NULL;
-       }
+       if (fp == NULL)
+               return start;
 
        start = seprintf(start, end, "acpi: FADT@%p\n", fp);
        start = seprintf(start, end, "acpi: fadt: facs: $%p\n", fp->facs);
@@ -561,15 +622,21 @@ static char *dumpfadt(char *start, char *end, struct Fadt *fp)
        return start;
 }
 
-static struct Atable *acpifadt(uint8_t * p, int len)
+static struct Atable *parsefadt(struct Atable *parent,
+                                                               char *name, uint8_t *p, size_t rawsize)
 {
+       struct Atable *t;
        struct Fadt *fp;
 
-       if (len < 116) {
+       t = mkatable(parent, FADT, name, p, rawsize, sizeof(struct Fadt));
+
+       if (rawsize < 116) {
                printk("ACPI: unusually short FADT, aborting!\n");
-               return 0;
+               return t;
        }
-       fp = &fadt;
+       /* for now, keep the globals. We'll get rid of them later. */
+       fp = t->tbl;
+       fadt = fp;
        fp->facs = l32get(p + 36);
        fp->dsdt = l32get(p + 40);
        fp->pmprofile = p[45];
@@ -607,9 +674,12 @@ static struct Atable *acpifadt(uint8_t * p, int len)
        fp->iapcbootarch = l16get(p + 109);
        fp->flags = l32get(p + 112);
 
-       /* qemu gives us a 116 byte fadt, though i haven't seen any HW do that. */
-       if (len < 244)
-               return 0;
+       /*
+        * qemu gives us a 116 byte fadt, though i haven't seen any HW do that.
+        * The right way to do this is to realloc the table and fake it out.
+        */
+       if (rawsize < 244)
+               return finatable_nochildren(t);
 
        gasget(&fp->resetreg, p + 116);
        fp->resetval = p[128];
@@ -629,26 +699,36 @@ static struct Atable *acpifadt(uint8_t * p, int len)
        else
                loadfacs(fp->facs);
 
-       if (fp->xdsdt == ((uint64_t) fp->dsdt)) /* acpica */
+       if (fp->xdsdt == (uint64_t)fp->dsdt)    /* acpica */
                loaddsdt(fp->xdsdt);
        else
                loaddsdt(fp->dsdt);
 
-       return NULL;    /* can be unmapped once parsed */
+       return finatable_nochildren(t);
 }
 
-static char *dumpmsct(char *start, char *end, struct Msct *msct)
+static char *dumpmsct(char *start, char *end, struct Atable *table)
 {
-       struct Mdom *st;
+       struct Msct *msct;
+
+       if (!table)
+               return start;
 
+       msct = table->tbl;
        if (!msct)
                return start;
+
        start = seprintf(start, end, "acpi: msct: %d doms %d clkdoms %#p maxpa\n",
                                         msct->ndoms, msct->nclkdoms, msct->maxpa);
-       for (st = msct->dom; st != NULL; st = st->next)
+       for (int i = 0; i < table->nchildren; i++) {
+               struct Atable *domtbl = table->children[i]->tbl;
+               struct Mdom *st = domtbl->tbl;
+
                start = seprintf(start, end, "\t[%d:%d] %d maxproc %#p maxmmem\n",
                                                 st->start, st->end, st->maxproc, st->maxmem);
+       }
        start = seprintf(start, end, "\n");
+
        return start;
 }
 
@@ -656,37 +736,77 @@ static char *dumpmsct(char *start, char *end, struct Msct *msct)
  * XXX: should perhaps update our idea of available memory.
  * Else we should remove this code.
  */
-static struct Atable *acpimsct(uint8_t * p, int len)
+static struct Atable *parsemsct(struct Atable *parent,
+                                char *name, uint8_t *raw, size_t rawsize)
 {
-       uint8_t *pe;
+       struct Atable *t;
+       uint8_t *r, *re;
+       struct Msct *msct;
        struct Mdom **stl, *st;
-       int off;
+       size_t off, nmdom;
+       int i;
 
-       msct = kzmalloc(sizeof(struct Msct), 0);
-       msct->ndoms = l32get(p + 40) + 1;
-       msct->nclkdoms = l32get(p + 44) + 1;
-       msct->maxpa = l64get(p + 48);
+       re = raw + rawsize;
+       off = l32get(raw + 36);
+       nmdom = 0;
+       for (r = raw + off, re = raw + rawsize; r < re; r += 22)
+               nmdom++;
+       t = mkatable(parent, MSCT, name, raw, rawsize,
+                    sizeof(struct Msct) + nmdom * sizeof(struct Mdom));
+       msct = t->tbl;
+       msct->ndoms = l32get(raw + 40) + 1;
+       msct->nclkdoms = l32get(raw + 44) + 1;
+       msct->maxpa = l64get(raw + 48);
+       msct->nmdom = nmdom;
        msct->dom = NULL;
-       stl = &msct->dom;
-       pe = p + len;
-       off = l32get(p + 36);
-       for (p += off; p < pe; p += 22) {
-               st = kzmalloc(sizeof(struct Mdom), 0);
-               st->next = NULL;
-               st->start = l32get(p + 2);
-               st->end = l32get(p + 6);
-               st->maxproc = l32get(p + 10);
-               st->maxmem = l64get(p + 14);
-               *stl = st;
-               stl = &st->next;
+       if (nmdom != 0)
+               msct->dom = (void *)msct + sizeof(struct Msct);
+       for (i = 0, r = raw; i < nmdom; i++, r += 22) {
+               msct->dom[i].start = l32get(r + 2);
+               msct->dom[i].end = l32get(r + 6);
+               msct->dom[i].maxproc = l32get(r + 10);
+               msct->dom[i].maxmem = l64get(r + 14);
        }
-       return NULL;    /* can be unmapped once parsed */
+       mscttbl = finatable_nochildren(t);
+
+       return mscttbl;
 }
 
-static char *dumpsrat(char *start, char *end, struct Srat *st)
+/* TODO(rminnich): only handles on IOMMU for now. */
+static char *dumpdmar(char *start, char *end, struct Atable *dmar)
 {
-       start = seprintf(start, end, "acpi: START@%p:\n", st);
-       for (; st != NULL; st = st->next)
+       struct Dmar *dt;
+
+       if (dmar == NULL)
+               return start;
+
+       dt = dmar->tbl;
+       start = seprintf(start, end, "acpi: DMAR addr %p:\n", dt);
+       start = seprintf(start, end, "\tdmar: intr_remap %d haw %d\n",
+                        dt->intr_remap, dt->haw);
+       for (int i = 0; i < dmar->nchildren; i++) {
+               struct Atable *at = dmar->children[i];
+               struct Drhd *drhd = at->tbl;
+
+               start = seprintf(start, end, "\tDRHD: ");
+               start = seprintf(start, end, "%s 0x%02x 0x%016x\n",
+                                drhd->all & 1 ? "INCLUDE_PCI_ALL" : "Scoped",
+                                drhd->segment, drhd->rba);
+       }
+
+       return start;
+}
+
+static char *dumpsrat(char *start, char *end, struct Atable *table)
+{
+       if (table == NULL)
+               return seprintf(start, end, "NO SRAT\n");
+       start = seprintf(start, end, "acpi: SRAT@%p:\n", table->tbl);
+       for (; table != NULL; table = table->next) {
+               struct Srat *st = table->tbl;
+
+               if (st == NULL)
+                       continue;
                switch (st->type) {
                        case SRlapic:
                                start =
@@ -710,29 +830,35 @@ static char *dumpsrat(char *start, char *end, struct Srat *st)
                        default:
                                start = seprintf(start, end, "\t<unknown srat entry>\n");
                }
+       }
        start = seprintf(start, end, "\n");
        return start;
 }
 
-static struct Atable *acpisrat(uint8_t * p, int len)
+static struct Atable *parsesrat(struct Atable *parent,
+                                char *name, uint8_t *p, size_t rawsize)
 {
 
-       struct Srat **stl, *st;
+       struct Atable *t, *tt, *tail;
        uint8_t *pe;
        int stlen, flags;
+       struct slice slice;
+       char buf[16];
+       int i;
+       struct Srat *st;
 
-       if (srat != NULL) {
-               printd("acpi: two SRATs?\n");
-               return NULL;
-       }
+       if (srat != NULL)
+               panic("acpi: two SRATs?\n");
 
-       stl = &srat;
-       pe = p + len;
-       for (p += 48; p < pe; p += stlen) {
-               st = kzmalloc(sizeof(struct Srat), 1);
-               st->type = p[0];
-               st->next = NULL;
+       t = mkatable(parent, SRAT, name, p, rawsize, 0);
+       slice_init(&slice);
+       pe = p + rawsize;
+       for (p += 48, i = 0; p < pe; p += stlen, i++) {
+               snprintf(buf, sizeof(buf), "%d", i);
                stlen = p[1];
+               tt = mkatable(t, SRAT, buf, p, stlen, sizeof(struct Srat));
+               st = tt->tbl;
+               st->type = p[0];
                switch (st->type) {
                        case SRlapic:
                                st->lapic.dom = p[2] | p[9] << 24 | p[10] << 16 | p[11] << 8;
@@ -740,8 +866,8 @@ static struct Atable *acpisrat(uint8_t * p, int len)
                                st->lapic.sapic = p[8];
                                st->lapic.clkdom = l32get(p + 12);
                                if (l32get(p + 4) == 0) {
-                                       kfree(st);
-                                       st = NULL;
+                                       kfree(tt);
+                                       tt = NULL;
                                }
                                break;
                        case SRmem:
@@ -750,8 +876,8 @@ static struct Atable *acpisrat(uint8_t * p, int len)
                                st->mem.len = l64get(p + 16);
                                flags = l32get(p + 28);
                                if ((flags & 1) == 0) { /* not enabled */
-                                       kfree(st);
-                                       st = NULL;
+                                       kfree(tt);
+                                       tt = NULL;
                                } else {
                                        st->mem.hplug = flags & 2;
                                        st->mem.nvram = flags & 4;
@@ -762,28 +888,31 @@ static struct Atable *acpisrat(uint8_t * p, int len)
                                st->lx2apic.apic = l32get(p + 8);
                                st->lx2apic.clkdom = l32get(p + 16);
                                if (l32get(p + 12) == 0) {
-                                       kfree(st);
-                                       st = NULL;
+                                       kfree(tt);
+                                       tt = NULL;
                                }
                                break;
                        default:
                                printd("unknown SRAT structure\n");
-                               kfree(st);
-                               st = NULL;
+                               kfree(tt);
+                               tt = NULL;
+                               break;
                }
-               if (st != NULL) {
-                       *stl = st;
-                       stl = &st->next;
+               if (tt != NULL) {
+                       finatable_nochildren(tt);
+                       slice_append(&slice, tt);
                }
        }
-       return NULL;    /* can be unmapped once parsed */
+       srat = finatable(t, &slice);
+
+       return srat;
 }
 
 static char *dumpslit(char *start, char *end, struct Slit *sl)
 {
        int i;
 
-       if (!sl)
+       if (sl == NULL)
                return start;
        start = seprintf(start, end, "acpi slit:\n");
        for (i = 0; i < sl->rowlen * sl->rowlen; i++) {
@@ -804,47 +933,47 @@ static int cmpslitent(void *v1, void *v2)
        return se1->dist - se2->dist;
 }
 
-static struct Atable *acpislit(uint8_t * p, int len)
+static struct Atable *parseslit(struct Atable *parent,
+                                char *name, uint8_t *raw, size_t rawsize)
 {
-
-       uint8_t *pe;
+       struct Atable *t;
+       uint8_t *r, *re;
        int i, j, k;
        struct SlEntry *se;
+       size_t addsize, rowlen;
+       void *p;
+
+       addsize = sizeof(*slit);
+       rowlen = l64get(raw + 36);
+       addsize += rowlen * sizeof(struct SlEntry *);
+       addsize += sizeof(struct SlEntry) * rowlen * rowlen;
+
+       t = mkatable(parent, SLIT, name, raw, rawsize, addsize);
+       slit = t->tbl;
+       slit->rowlen = rowlen;
+       p = (void *)slit + sizeof(*slit);
+       slit->e = p;
+       p += rowlen * sizeof(struct SlEntry *);
+       for (i = 0; i < rowlen; i++) {
+               slit->e[i] = p;
+               p += sizeof(struct SlEntry) * rowlen;
+       }
+       for (i = 0, r = raw + 44, re = raw + rawsize; r < re; r++, i++) {
+               int j = i / rowlen;
+               int k = i % rowlen;
 
-       pe = p + len;
-       slit = kzmalloc(sizeof(*slit), 0);
-       slit->rowlen = l64get(p + 36);
-       slit->e = kzmalloc(slit->rowlen * sizeof(struct SlEntry *), 0);
-       for (i = 0; i < slit->rowlen; i++)
-               slit->e[i] = kzmalloc(sizeof(struct SlEntry) * slit->rowlen, 0);
-
-       i = 0;
-       for (p += 44; p < pe; p++, i++) {
-               j = i / slit->rowlen;
-               k = i % slit->rowlen;
                se = &slit->e[j][k];
                se->dom = k;
-               se->dist = *p;
+               se->dist = *r;
        }
+
 #if 0
        /* TODO: might need to sort this shit */
        for (i = 0; i < slit->rowlen; i++)
                qsort(slit->e[i], slit->rowlen, sizeof(slit->e[0][0]), cmpslitent);
 #endif
-       return NULL;    /* can be unmapped once parsed */
-}
-
-uintptr_t acpimblocksize(uintptr_t addr, int *dom)
-{
-       struct Srat *sl;
 
-       for (sl = srat; sl != NULL; sl = sl->next)
-               if (sl->type == SRmem)
-                       if (sl->mem.addr <= addr && sl->mem.addr + sl->mem.len > addr) {
-                               *dom = sl->mem.dom;
-                               return sl->mem.len - (addr - sl->mem.addr);
-                       }
-       return 0;
+       return finatable_nochildren(t);
 }
 
 int pickcore(int mycolor, int index)
@@ -852,9 +981,8 @@ int pickcore(int mycolor, int index)
        int color;
        int ncorepercol;
 
-       if (slit == NULL) {
+       if (slit == NULL)
                return 0;
-       }
        ncorepercol = num_cores / slit->rowlen;
        color = slit->e[mycolor][index / ncorepercol].dom;
        return color * ncorepercol + index % ncorepercol;
@@ -881,14 +1009,21 @@ static char *printiflags(char *start, char *end, int flags)
                                        polarity[flags & AFpmask], trigger[(flags & AFtmask) >> 2]);
 }
 
-static char *dumpmadt(char *start, char *end, struct Madt *apics)
+static char *dumpmadt(char *start, char *end, struct Atable *apics)
 {
-       struct Apicst *st;
+       struct Madt *mt;
 
-       start =
-               seprintf(start, end, "acpi: MADT@%p: lapic paddr %p pcat %d:\n",
-                               apics, apics->lapicpa, apics->pcat);
-       for (st = apics->st; st != NULL; st = st->next)
+       if (apics == NULL)
+               return start;
+
+       mt = apics->tbl;
+       if (mt == NULL)
+               return seprintf(start, end, "acpi: no MADT");
+       start = seprintf(start, end, "acpi: MADT@%p: lapic paddr %p pcat %d:\n",
+                        mt, mt->lapicpa, mt->pcat);
+       for (int i = 0; i < apics->nchildren; i++) {
+               struct Atable *apic = apics->children[i];
+               struct Apicst *st = apic->tbl;
 
                switch (st->type) {
                        case ASlapic:
@@ -950,35 +1085,43 @@ static char *dumpmadt(char *start, char *end, struct Madt *apics)
                        default:
                                start = seprintf(start, end, "\t<unknown madt entry>\n");
                }
+       }
        start = seprintf(start, end, "\n");
        return start;
 }
 
-static struct Atable *acpimadt(uint8_t * p, int len)
+static struct Atable *parsemadt(struct Atable *parent,
+                                char *name, uint8_t *p, size_t size)
 {
-
+       struct Atable *t, *tt, *tail;
        uint8_t *pe;
-       struct Apicst *st, *l, **stl;
-       int stlen, id;
-
-       apics = kzmalloc(sizeof(struct Madt), 1);
-       apics->lapicpa = l32get(p + 36);
-       apics->pcat = l32get(p + 40);
-       apics->st = NULL;
-       stl = &apics->st;
-       pe = p + len;
-       for (p += 44; p < pe; p += stlen) {
-               st = kzmalloc(sizeof(struct Apicst), 1);
-               st->type = p[0];
-               st->next = NULL;
+       struct Madt *mt;
+       struct Apicst *st, *l;
+       int id;
+       size_t stlen;
+       char buf[16];
+       int i;
+       struct slice slice;
+
+       slice_init(&slice);
+       t = mkatable(parent, MADT, name, p, size, sizeof(struct Madt));
+       mt = t->tbl;
+       mt->lapicpa = l32get(p + 36);
+       mt->pcat = l32get(p + 40);
+       pe = p + size;
+       for (p += 44, i = 0; p < pe; p += stlen, i++) {
+               snprintf(buf, sizeof(buf), "%d", i);
                stlen = p[1];
+               tt = mkatable(t, APIC, buf, p, stlen, sizeof(struct Apicst));
+               st = tt->tbl;
+               st->type = p[0];
                switch (st->type) {
                        case ASlapic:
                                st->lapic.pid = p[2];
                                st->lapic.id = p[3];
                                if (l32get(p + 4) == 0) {
-                                       kfree(st);
-                                       st = NULL;
+                                       kfree(tt);
+                                       tt = NULL;
                                }
                                break;
                        case ASioapic:
@@ -986,12 +1129,14 @@ static struct Atable *acpimadt(uint8_t * p, int len)
                                st->ioapic.addr = l32get(p + 4);
                                st->ioapic.ibase = l32get(p + 8);
                                /* ioapic overrides any ioapic entry for the same id */
-                               for (l = apics->st; l != NULL; l = l->next)
+                               for (int i = 0; i < slice_len(&slice); i++) {
+                                       l = ((struct Atable *)slice_get(&slice, i))->tbl;
                                        if (l->type == ASiosapic && l->iosapic.id == id) {
                                                st->ioapic = l->iosapic;
                                                /* we leave it linked; could be removed */
                                                break;
                                        }
+                               }
                                break;
                        case ASintovr:
                                st->intovr.irq = p[3];
@@ -1011,20 +1156,22 @@ static struct Atable *acpimadt(uint8_t * p, int len)
                                /* This is for 64 bits, perhaps we should not
                                 * honor it on 32 bits.
                                 */
-                               apics->lapicpa = l64get(p + 8);
+                               mt->lapicpa = l64get(p + 8);
                                break;
                        case ASiosapic:
                                id = st->iosapic.id = p[2];
                                st->iosapic.ibase = l32get(p + 4);
                                st->iosapic.addr = l64get(p + 8);
                                /* iosapic overrides any ioapic entry for the same id */
-                               for (l = apics->st; l != NULL; l = l->next)
+                               for (int i = 0; i < slice_len(&slice); i++) {
+                                       l = ((struct Atable*)slice_get(&slice, i))->tbl;
                                        if (l->type == ASioapic && l->ioapic.id == id) {
                                                l->ioapic = st->iosapic;
-                                               kfree(st);
-                                               st = NULL;
+                                               kfree(tt);
+                                               tt = NULL;
                                                break;
                                        }
+                               }
                                break;
                        case ASlsapic:
                                st->lsapic.pid = p[2];
@@ -1032,8 +1179,8 @@ static struct Atable *acpimadt(uint8_t * p, int len)
                                st->lsapic.eid = p[4];
                                st->lsapic.puid = l32get(p + 12);
                                if (l32get(p + 8) == 0) {
-                                       kfree(st);
-                                       st = NULL;
+                                       kfree(tt);
+                                       tt = NULL;
                                } else
                                        kstrdup(&st->lsapic.puids, (char *)p + 16);
                                break;
@@ -1050,8 +1197,8 @@ static struct Atable *acpimadt(uint8_t * p, int len)
                                st->lx2apic.id = l32get(p + 4);
                                st->lx2apic.puid = l32get(p + 12);
                                if (l32get(p + 8) == 0) {
-                                       kfree(st);
-                                       st = NULL;
+                                       kfree(tt);
+                                       tt = NULL;
                                }
                                break;
                        case ASlx2nmi:
@@ -1061,29 +1208,130 @@ static struct Atable *acpimadt(uint8_t * p, int len)
                                break;
                        default:
                                printd("unknown APIC structure\n");
-                               kfree(st);
-                               st = NULL;
+                               kfree(tt);
+                               tt = NULL;
                }
-               if (st != NULL) {
-                       *stl = st;
-                       stl = &st->next;
+               if (tt != NULL) {
+                       finatable_nochildren(tt);
+                       slice_append(&slice, tt);
                }
        }
-       return NULL;    /* can be unmapped once parsed */
+       apics = finatable(t, &slice);
+
+       return apics;
+}
+
+static struct Atable *parsedmar(struct Atable *parent,
+                                char *name, uint8_t *raw, size_t rawsize)
+{
+       struct Atable *t, *tt;
+       int i;
+       int baselen = MIN(rawsize, 38);
+       int nentry, nscope, npath, off, dslen, dhlen, len, type, flags;
+       void *pathp;
+       char buf[16];
+       struct slice drhds;
+       struct Drhd *drhd;
+       struct Dmar *dt;
+
+       /* count the entries */
+       for (nentry = 0, off = 48; off < rawsize; nentry++) {
+               dslen = l16get(raw + off + 2);
+               printk("acpi DMAR: entry %d is addr %p (0x%x/0x%x)\n",
+                      nentry, raw + off, l16get(raw + off), dslen);
+               off = off + dslen;
+       }
+       printk("DMAR: %d entries\n", nentry);
+
+       t = mkatable(parent, DMAR, name, raw, rawsize, sizeof(*dmar));
+       dt = t->tbl;
+       /* The table can be only partly filled. */
+       if (baselen >= 38 && raw[37] & 1)
+               dt->intr_remap = 1;
+       if (baselen >= 37)
+               dt->haw = raw[36] + 1;
+
+       /* Now we walk all the DMAR entries. */
+       slice_init(&drhds);
+       for (off = 48, i = 0; i < nentry; i++, off += dslen) {
+               snprintf(buf, sizeof(buf), "%d", i);
+               dslen = l16get(raw + off + 2);
+               type = l16get(raw + off);
+               // TODO(dcross): Introduce sensible symbolic constants
+               // for DMAR entry types. For right now, type 0 => DRHD.
+               // We skip everything else.
+               if (type != 0)
+                       continue;
+               npath = 0;
+               nscope = 0;
+               for (int o = off + 16; o < (off + dslen); o += dhlen) {
+                       nscope++;
+                       dhlen = *(raw + o + 1); // Single byte length.
+                       npath += ((dhlen - 6) / 2);
+               }
+               tt = mkatable(t, DRHD, buf, raw + off, dslen,
+                             sizeof(struct Drhd) + 2 * npath +
+                             nscope * sizeof(struct DevScope));
+               flags = *(raw + off + 4);
+               drhd = tt->tbl;
+               drhd->all = flags & 1;
+               drhd->segment = l16get(raw + off + 6);
+               drhd->rba = l64get(raw + off + 8);
+               drhd->nscope = nscope;
+               drhd->scopes = (void *)drhd + sizeof(struct Drhd);
+               pathp = (void *)drhd +
+                   sizeof(struct Drhd) + nscope * sizeof(struct DevScope);
+               for (int i = 0, o = off + 16; i < nscope; i++) {
+                       struct DevScope *ds = &drhd->scopes[i];
+
+                       dhlen = *(raw + o + 1);
+                       ds->enumeration_id = *(raw + o + 4);
+                       ds->start_bus_number = *(raw + o + 5);
+                       ds->npath = (dhlen - 6) / 2;
+                       ds->paths = pathp;
+                       for (int j = 0; j < ds->npath; j++)
+                               ds->paths[j] = l16get(raw + o + 6 + 2*j);
+                       pathp += 2*ds->npath;
+                       o += dhlen;
+               }
+               /*
+                * NOTE: if all is set, there should be no scopes of type
+                * This being ACPI, where vendors randomly copy tables
+                * from one system to another, and creating breakage,
+                * anything is possible. But we'll warn them.
+                */
+               finatable_nochildren(tt);
+               slice_append(&drhds, tt);
+       }
+       dmar = finatable(t, &drhds);
+
+       return dmar;
 }
 
 /*
  * Map the table and keep it there.
  */
-static struct Atable *acpitable(uint8_t * p, int len)
+static struct Atable *parsessdt(struct Atable *parent,
+                                char *name, uint8_t *raw, size_t size)
 {
-       if (len < Sdthdrsz) {
+       struct Atable *t;
+       struct Sdthdr *h;
+
+       /*
+        * We found it and it is too small.
+        * Simply return with no side effect.
+        */
+       if (size < Sdthdrsz)
                return NULL;
-       }
-       return new_acpi_table(p);
+       t = mkatable(parent, SSDT, name, raw, size, 0);
+       h = (struct Sdthdr *)raw;
+       memmove(t->name, h->sig, sizeof(h->sig));
+       t->name[sizeof(h->sig)] = '\0';
+
+       return finatable_nochildren(t);
 }
 
-static char *dumptable(char *start, char *end, char *sig, uint8_t * p, int l)
+static char *dumptable(char *start, char *end, char *sig, uint8_t *p, int l)
 {
        int n, i;
 
@@ -1111,9 +1359,9 @@ static char *seprinttable(char *s, char *e, struct Atable *t)
        uint8_t *p;
        int i, n;
 
-       p = (uint8_t *) t->tbl; /* include header */
-       n = Sdthdrsz + t->dlen;
-       s = seprintf(s, e, "%s @ %#p\n", t->sig, p);
+       p = (uint8_t *)t->tbl;  /* include header */
+       n = t->rawsize;
+       s = seprintf(s, e, "%s @ %#p\n", t->name, p);
        for (i = 0; i < n; i++) {
                if ((i % 16) == 0)
                        s = seprintf(s, e, "%x: ", i);
@@ -1124,87 +1372,151 @@ static char *seprinttable(char *s, char *e, struct Atable *t)
        return seprintf(s, e, "\n\n");
 }
 
+static void *rsdsearch(char *signature)
+{
+       uintptr_t p;
+       uint8_t *bda;
+       void *rsd;
+
+       /*
+        * Search for the data structure signature:
+        * 1) in the BIOS ROM between 0xE0000 and 0xFFFFF.
+        */
+       return sigscan(KADDR(0xE0000), 0x20000, signature);
+}
+
+/*
+ * Note: some of this comment is from the unfinished user interpreter.
+ *
+ * The DSDT is always given to the user interpreter.
+ * Tables listed here are also loaded from the XSDT:
+ * MSCT, MADT, and FADT are processed by us, because they are
+ * required to do early initialization before we have user processes.
+ * Other tables are given to the user level interpreter for
+ * execution.
+ *
+ * These historically returned a value to tell acpi whether or not it was okay
+ * to unmap the table.  (return 0 means there was no table, meaning it was okay
+ * to unmap).  We just use the kernbase mapping, so it's irrelevant.
+ *
+ * N.B. The intel source code defines the constants for ACPI in a
+ * non-endian-independent manner. Rather than bring in the huge wad o' code
+ * that represents, we just the names.
+ */
+struct Parser {
+       char *sig;
+       struct Atable *(*parse)(struct Atable *parent,
+                               char *name, uint8_t *raw, size_t rawsize);
+};
+
+
+static struct Parser ptable[] = {
+       {"FACP", parsefadt},
+       {"APIC", parsemadt},
+       {"DMAR", parsedmar},
+       {"SRAT", parsesrat},
+       {"SLIT", parseslit},
+       {"MSCT", parsemsct},
+       {"SSDT", parsessdt},
+       {"HPET", parsehpet},
+};
+
 /*
  * process xsdt table and load tables with sig, or all if NULL.
  * (XXX: should be able to search for sig, oemid, oemtblid)
  */
-static int acpixsdtload(char *sig)
+static void parsexsdt(struct Atable *root)
 {
-       int i, l, t, found;
+       ERRSTACK(1);
+       struct Sdthdr *sdt;
+       struct Atable *table;
+       struct slice slice;
+       size_t l, end;
        uintptr_t dhpa;
-       uint8_t *sdt;
-       char tsig[5];
-       char table[128];
-
-       found = 0;
-       for (i = 0; i < xsdt->len; i += xsdt->asize) {
-               if (xsdt->asize == 8)
-                       dhpa = l64get(xsdt->p + i);
-               else
-                       dhpa = l32get(xsdt->p + i);
-               if ((sdt = sdtmap(dhpa, &l, 1)) == NULL)
+       struct Atable *n;
+       uint8_t *tbl;
+
+       slice_init(&slice);
+       if (waserror()) {
+               slice_destroy(&slice);
+               return;
+       }
+
+       tbl = xsdt->p + sizeof(struct Sdthdr);
+       end = xsdt->len - sizeof(struct Sdthdr);
+       for (int i = 0; i < end; i += xsdt->asize) {
+               dhpa = (xsdt->asize == 8) ? l64get(tbl + i) : l32get(tbl + i);
+               sdt = sdtmap(dhpa, &l, 1);
+               if (sdt == NULL)
                        continue;
-               memmove(tsig, sdt, 4);
-               tsig[4] = 0;
-               if (sig == NULL || strcmp(sig, tsig) == 0) {
-                       printd("acpi: %s addr %#p\n", tsig, sdt);
-                       for (t = 0; t < ARRAY_SIZE(ptables); t++)
-                               if (strcmp(tsig, ptables[t].sig) == 0) {
-                                       //dumptable(table, &table[127], tsig, sdt, l);
-                                       ptables[t].f(sdt, l);
-                                       found = 1;
-                                       break;
-                               }
+               printd("acpi: %s addr %#p\n", tsig, sdt);
+               for (int j = 0; j < ARRAY_SIZE(ptable); j++) {
+                       if (memcmp(sdt->sig, ptable[j].sig, sizeof(sdt->sig)) == 0) {
+                               table = ptable[j].parse(root, ptable[j].sig, (void *)sdt, l);
+                               if (table != NULL)
+                                       slice_append(&slice, table);
+                               break;
+                       }
                }
        }
-       return found;
+       finatable(root, &slice);
 }
 
-static void *rsdsearch(char *signature)
+void makeindex(struct Atable *root)
 {
-       uintptr_t p;
-       uint8_t *bda;
-       void *rsd;
+       uint64_t index;
 
-       /*
-        * Search for the data structure signature:
-        * 1) in the BIOS ROM between 0xE0000 and 0xFFFFF.
-        */
-       return sigscan(KADDR(0xE0000), 0x20000, signature);
+       if (root == NULL)
+               return;
+       index = root->qid.path >> QIndexShift;
+       atableindex[index] = root;
+       for (int k = 0; k < root->nchildren; k++)
+               makeindex(root->children[k]);
 }
 
-static void acpirsdptr(void)
+static void parsersdptr(void)
 {
        struct Rsdp *rsd;
-       int asize;
+       int asize, cksum;
        uintptr_t sdtpa;
 
-       if ((rsd = rsdsearch("RSD PTR ")) == NULL) {
+       static_assert(sizeof(struct Sdthdr) == 36);
+
+       /* Find the root pointer. */
+       rsd = rsdsearch("RSD PTR ");
+       if (rsd == NULL) {
                printk("NO RSDP\n");
                return;
        }
 
+       /*
+        * Initialize the root of ACPI parse tree.
+        */
+       lastpath = Qroot;
+       root = mkatable(NULL, XSDT, devname(), NULL, 0, sizeof(struct Xsdt));
+       root->parent = root;
 
-       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);
+                  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),
                   l64get(rsd->xaddr), rsd->revision);
 
        if (rsd->revision >= 2) {
-               if (sdtchecksum(rsd, 36) == NULL) {
-                       printk("acpi: RSD: bad checksum\n");
+               cksum = sdtchecksum(rsd, 36);
+               if (cksum != 0) {
+                       printk("acpi: bad RSD checksum %d, 64 bit parser aborted\n", cksum);
                        return;
                }
                sdtpa = l64get(rsd->xaddr);
                asize = 8;
        } else {
-               if (sdtchecksum(rsd, 20) == NULL) {
-                       printk("acpi: RSD: bad checksum\n");
+               cksum = sdtchecksum(rsd, 20);
+               if (cksum != 0) {
+                       printk("acpi: bad RSD checksum %d, 32 bit parser aborted\n", cksum);
                        return;
                }
                sdtpa = l32get(rsd->raddr);
@@ -1214,61 +1526,70 @@ static void acpirsdptr(void)
        /*
         * process the RSDT or XSDT table.
         */
-       xsdt = kzmalloc(sizeof(struct Xsdt), 0);
-       if (xsdt == NULL) {
-               printk("acpi: malloc failed\n");
-               return;
-       }
-       if ((xsdt->p = sdtmap(sdtpa, &xsdt->len, 1)) == NULL) {
+       xsdt = root->tbl;
+       xsdt->p = sdtmap(sdtpa, &xsdt->len, 1);
+       if (xsdt->p == NULL) {
                printk("acpi: sdtmap failed\n");
                return;
        }
        if ((xsdt->p[0] != 'R' && xsdt->p[0] != 'X')
                || memcmp(xsdt->p + 1, "SDT", 3) != 0) {
-               printd("acpi: xsdt sig: %c%c%c%c\n", xsdt->p[0], xsdt->p[1], xsdt->p[2],
-                          xsdt->p[3]);
-               kfree(xsdt);
+               printd("acpi: xsdt sig: %c%c%c%c\n",
+                      xsdt->p[0], xsdt->p[1], xsdt->p[2], xsdt->p[3]);
                xsdt = NULL;
                return;
        }
-       xsdt->p += sizeof(struct Sdthdr);
-       xsdt->len -= sizeof(struct Sdthdr);
        xsdt->asize = asize;
        printd("acpi: XSDT %#p\n", xsdt);
-       acpixsdtload(NULL);
-       /* xsdt is kept and not unmapped */
+       parsexsdt(root);
+       atableindex = kreallocarray(NULL, lastpath, sizeof(struct Atable *),
+                                   KMALLOC_WAIT);
+       assert(atableindex != NULL);
+       makeindex(root);
+}
 
+/*
+ * The invariant that each level in the tree has an associated
+ * Atable implies that each chan can be mapped to an Atable.
+ * The assertions here enforce that invariant.
+ */
+static struct Atable *genatable(struct chan *c)
+{
+       struct Atable *a;
+       uint64_t ai;
+
+       ai = c->qid.path >> QIndexShift;
+       assert(ai < lastpath);
+       a = atableindex[ai];
+       assert(a != NULL);
+
+       return a;
 }
 
-static int
-acpigen(struct chan *c, char *unused_char_p_t, struct dirtab *tab, int ntab,
-               int i, struct dir *dp)
+static int acpigen(struct chan *c, char *name, struct dirtab *tab, int ntab,
+                                  int i, struct dir *dp)
 {
-       struct qid qid;
+       struct Atable *a = genatable(c);
 
        if (i == DEVDOTDOT) {
-               mkqid(&qid, Qdir, 0, QTDIR);
-               devdir(c, qid, devname(), 0, eve, 0555, dp);
+               assert((c->qid.path & QIndexMask) == Qdir);
+               devdir(c, a->parent->qid, a->parent->name, 0, eve, DMDIR|0555, dp);
                return 1;
        }
-       i++;    /* skip first element for . itself */
-       if (tab == 0 || i >= ntab) {
-               return -1;
-       }
-       tab += i;
-       qid = tab->qid;
-       qid.path &= ~Qdir;
-       qid.vers = 0;
-       devdir(c, qid, tab->name, tab->length, eve, tab->perm, dp);
-       return 1;
+       return devgen(c, name, a->cdirs, a->nchildren + NQtypes, i, dp);
+}
+
+/*
+ * Print the contents of the XSDT.
+ */
+static void dumpxsdt(void)
+{
+       printk("xsdt: len = %lu, asize = %lu, p = %p\n",
+              xsdt->len, xsdt->asize, xsdt->p);
 }
 
 static char *dumpGas(char *start, char *end, char *prefix, struct Gas *g)
 {
-       static char *rnames[] = {
-               "mem", "io", "pcicfg", "embed",
-               "smb", "cmos", "pcibar", "ipmi"
-       };
        start = seprintf(start, end, "%s", prefix);
 
        switch (g->spc) {
@@ -1279,18 +1600,18 @@ static char *dumpGas(char *start, char *end, char *prefix, struct Gas *g)
                case Rcmos:
                case Rpcibar:
                case Ripmi:
-                       start = seprintf(start, end, "[%s ", rnames[g->spc]);
+                       start = seprintf(start, end, "[%s ", regnames[g->spc]);
                        break;
                case Rpcicfg:
                        start = seprintf(start, end, "[pci ");
                        start =
                                seprintf(start, end, "dev %#p ",
-                                                (uint32_t) (g->addr >> 32) & 0xFFFF);
+                                                (uint32_t)(g->addr >> 32) & 0xFFFF);
                        start =
                                seprintf(start, end, "fn %#p ",
-                                                (uint32_t) (g->addr & 0xFFFF0000) >> 16);
+                                                (uint32_t)(g->addr & 0xFFFF0000) >> 16);
                        start =
-                               seprintf(start, end, "adr %#p ", (uint32_t) (g->addr & 0xFFFF));
+                               seprintf(start, end, "adr %#p ", (uint32_t)(g->addr & 0xFFFF));
                        break;
                case Rfixedhw:
                        start = seprintf(start, end, "[hw ");
@@ -1366,25 +1687,29 @@ static unsigned int setbanked(uintptr_t ra, uintptr_t rb, int sz, int v)
 
 static unsigned int getpm1ctl(void)
 {
-       return getbanked(fadt.pm1acntblk, fadt.pm1bcntblk, fadt.pm1cntlen);
+       assert(fadt != NULL);
+       return getbanked(fadt->pm1acntblk, fadt->pm1bcntblk, fadt->pm1cntlen);
 }
 
 static void setpm1sts(unsigned int v)
 {
-       setbanked(fadt.pm1aevtblk, fadt.pm1bevtblk, fadt.pm1evtlen / 2, v);
+       assert(fadt != NULL);
+       setbanked(fadt->pm1aevtblk, fadt->pm1bevtblk, fadt->pm1evtlen / 2, v);
 }
 
 static unsigned int getpm1sts(void)
 {
-       return getbanked(fadt.pm1aevtblk, fadt.pm1bevtblk, fadt.pm1evtlen / 2);
+       assert(fadt != NULL);
+       return getbanked(fadt->pm1aevtblk, fadt->pm1bevtblk, fadt->pm1evtlen / 2);
 }
 
 static unsigned int getpm1en(void)
 {
        int sz;
 
-       sz = fadt.pm1evtlen / 2;
-       return getbanked(fadt.pm1aevtblk + sz, fadt.pm1bevtblk + sz, sz);
+       assert(fadt != NULL);
+       sz = fadt->pm1evtlen / 2;
+       return getbanked(fadt->pm1aevtblk + sz, fadt->pm1bevtblk + sz, sz);
 }
 
 static int getgpeen(int n)
@@ -1444,27 +1769,29 @@ static void acpiintr(Ureg *, void *)
        setpm1sts(sts);
 }
 #endif
+
 static void initgpes(void)
 {
        int i, n0, n1;
 
-       n0 = fadt.gpe0blklen / 2;
-       n1 = fadt.gpe1blklen / 2;
+       assert(fadt != NULL);
+       n0 = fadt->gpe0blklen / 2;
+       n1 = fadt->gpe1blklen / 2;
        ngpes = n0 + n1;
        gpes = kzmalloc(sizeof(struct Gpe) * ngpes, 1);
        for (i = 0; i < n0; i++) {
                gpes[i].nb = i;
                gpes[i].stsbit = i & 7;
-               gpes[i].stsio = fadt.gpe0blk + (i >> 3);
+               gpes[i].stsio = fadt->gpe0blk + (i >> 3);
                gpes[i].enbit = (n0 + i) & 7;
-               gpes[i].enio = fadt.gpe0blk + ((n0 + i) >> 3);
+               gpes[i].enio = fadt->gpe0blk + ((n0 + i) >> 3);
        }
        for (i = 0; i + n0 < ngpes; i++) {
-               gpes[i + n0].nb = fadt.gp1base + i;
+               gpes[i + n0].nb = fadt->gp1base + i;
                gpes[i + n0].stsbit = i & 7;
-               gpes[i + n0].stsio = fadt.gpe1blk + (i >> 3);
+               gpes[i + n0].stsio = fadt->gpe1blk + (i >> 3);
                gpes[i + n0].enbit = (n1 + i) & 7;
-               gpes[i + n0].enio = fadt.gpe1blk + ((n1 + i) >> 3);
+               gpes[i + n0].enio = fadt->gpe1blk + ((n1 + i) >> 3);
        }
        for (i = 0; i < ngpes; i++) {
                setgpeen(i, 0);
@@ -1474,30 +1801,27 @@ static void initgpes(void)
 
 static void acpiioalloc(unsigned int addr, int len)
 {
-       if (addr != 0) {
-               printk("Just TAKING port %016lx to %016lx\n", addr, addr + len);
-               //ioalloc(addr, len, 0, "acpi");
-       }
+       if (addr != 0)
+               printd("Just TAKING port %016lx to %016lx\n", addr, addr + len);
+}
+
+static void acpiinitonce(void)
+{
+       parsersdptr();
+       if (root != NULL)
+               printk("ACPI initialized\n");
 }
 
 int acpiinit(void)
 {
-       /* this smicmd test implements 'run once' for now. */
-       if (fadt.smicmd == 0) {
-               //fmtinstall('G', Gfmt);
-               acpirsdptr();
-               if (fadt.smicmd == 0) {
-                       return -1;
-               }
-       }
-       printk("ACPI initialized\n");
-       return 0;
+       run_once(acpiinitonce());
+       return (root == NULL) ? -1 : 0;
 }
 
 static struct chan *acpiattach(char *spec)
 {
        int i;
-
+       struct chan *c;
        /*
         * This was written for the stock kernel.
         * This code must use 64 registers to be acpi ready in nix.
@@ -1509,47 +1833,64 @@ static struct chan *acpiattach(char *spec)
         * should use fadt->xpm* and fadt->xgpe* registers for 64 bits.
         * We are not ready in this kernel for that.
         */
-       acpiioalloc(fadt.smicmd, 1);
-       acpiioalloc(fadt.pm1aevtblk, fadt.pm1evtlen);
-       acpiioalloc(fadt.pm1bevtblk, fadt.pm1evtlen);
-       acpiioalloc(fadt.pm1acntblk, fadt.pm1cntlen);
-       acpiioalloc(fadt.pm1bcntblk, fadt.pm1cntlen);
-       acpiioalloc(fadt.pm2cntblk, fadt.pm2cntlen);
-       acpiioalloc(fadt.pmtmrblk, fadt.pmtmrlen);
-       acpiioalloc(fadt.gpe0blk, fadt.gpe0blklen);
-       acpiioalloc(fadt.gpe1blk, fadt.gpe1blklen);
+       assert(fadt != NULL);
+       acpiioalloc(fadt->smicmd, 1);
+       acpiioalloc(fadt->pm1aevtblk, fadt->pm1evtlen);
+       acpiioalloc(fadt->pm1bevtblk, fadt->pm1evtlen);
+       acpiioalloc(fadt->pm1acntblk, fadt->pm1cntlen);
+       acpiioalloc(fadt->pm1bcntblk, fadt->pm1cntlen);
+       acpiioalloc(fadt->pm2cntblk, fadt->pm2cntlen);
+       acpiioalloc(fadt->pmtmrblk, fadt->pmtmrlen);
+       acpiioalloc(fadt->gpe0blk, fadt->gpe0blklen);
+       acpiioalloc(fadt->gpe1blk, fadt->gpe1blklen);
 
        initgpes();
-
-       /*
+#ifdef RON_SAYS_CONFIG_WE_ARE_NOT_WORTHY
+       /* this is frightening. SMI: just say no. Although we will almost
+        * certainly find that we have no choice.
+        *
         * This starts ACPI, which may require we handle
         * power mgmt events ourselves. Use with care.
         */
-       outb(fadt.smicmd, fadt.acpienable);
+       outb(fadt->smicmd, fadt->acpienable);
        for (i = 0; i < 10; i++)
                if (getpm1ctl() & Pm1SciEn)
                        break;
        if (i == 10)
                error(EFAIL, "acpi: failed to enable\n");
-//  if(fadt.sciint != 0)
-//      intrenable(fadt.sciint, acpiintr, 0, BUSUNKNOWN, "acpi");
-       return devattach(devname(), spec);
+       if (fadt->sciint != 0)
+               intrenable(fadt->sciint, acpiintr, 0, BUSUNKNOWN, "acpi");
+#endif
+       c = devattach(devname(), spec);
+
+       return c;
 }
 
 static struct walkqid *acpiwalk(struct chan *c, struct chan *nc, char **name,
                                                                int nname)
 {
-       return devwalk(c, nc, name, nname, acpidir, ARRAY_SIZE(acpidir), acpigen);
+       /*
+        * Note that devwalk hard-codes a test against the location of 'devgen',
+        * so we pretty much have to not pass it here.
+        */
+       return devwalk(c, nc, name, nname, NULL, 0, acpigen);
 }
 
-static int acpistat(struct chan *c, uint8_t * dp, int n)
+static int acpistat(struct chan *c, uint8_t *dp, int n)
 {
-       return devstat(c, dp, n, acpidir, ARRAY_SIZE(acpidir), acpigen);
+       struct Atable *a = genatable(c);
+
+       if (c->qid.type == QTDIR)
+               a = a->parent;
+       assert(a != NULL);
+
+       /* TODO(dcross): make acpigen work here. */
+       return devstat(c, dp, n, a->cdirs, a->nchildren + NQtypes, devgen);
 }
 
 static struct chan *acpiopen(struct chan *c, int omode)
 {
-       return devopen(c, omode, acpidir, ARRAY_SIZE(acpidir), acpigen);
+       return devopen(c, omode, NULL, 0, acpigen);
 }
 
 static void acpiclose(struct chan *unused)
@@ -1559,6 +1900,8 @@ static void acpiclose(struct chan *unused)
 static char *ttext;
 static int tlen;
 
+// Get the table from the qid.
+// Read that one table using the pointers.
 static long acpiread(struct chan *c, void *a, long n, int64_t off)
 {
        long q;
@@ -1571,58 +1914,53 @@ static long acpiread(struct chan *c, void *a, long n, int64_t off)
        }
        if (ttext == NULL)
                error(ENOMEM, "acpiread: no memory");
-       q = c->qid.path;
+       q = c->qid.path & QIndexMask;
        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;
-                       strlcpy(s, "no tables\n", tlen);
-                       for (t = tfirst; t != NULL; t = t->next) {
+       case Qdir:
+               return devdirread(c, a, n, NULL, 0, acpigen);
+       case Qraw:
+               return readmem(off, a, n, ttext, tlen);
+       case Qtbl:
+               s = ttext;
+               e = ttext + tlen;
+               strlcpy(s, "no tables\n", tlen);
+               for (t = tfirst; t != NULL; t = t->next) {
+                       ns = seprinttable(s, e, t);
+                       while (ns == e - 1) {
+                               ntext = krealloc(ttext, tlen * 2, 0);
+                               if (ntext == NULL)
+                                       panic("acpi: no memory\n");
+                               s = ntext + (ttext - s);
+                               ttext = ntext;
+                               tlen *= 2;
+                               e = ttext + tlen;
                                ns = seprinttable(s, e, t);
-                               while (ns == e - 1) {
-                                       ntext = krealloc(ttext, tlen * 2, 0);
-                                       if (ntext == NULL)
-                                               panic("acpi: no memory\n");
-                                       s = ntext + (ttext - s);
-                                       ttext = ntext;
-                                       tlen *= 2;
-                                       e = ttext + tlen;
-                                       ns = seprinttable(s, e, t);
-                               }
-                               s = ns;
                        }
-                       return readstr(off, a, n, ttext);
-               case Qpretty:
-                       s = ttext;
-                       e = ttext + tlen;
-                       s = dumpfadt(s, e, &fadt);
-                       s = dumpmadt(s, e, apics);
-                       s = dumpslit(s, e, slit);
-                       s = dumpsrat(s, e, srat);
-                       dumpmsct(s, e, msct);
-                       return readstr(off, a, n, ttext);
-               case Qioapic:
-                       s = ioapicdump(ttext, ttext + tlen);
-                       return readstr(off, a, n, ttext);
-               case Qapic:
-                       s = apicdump(ttext, ttext + tlen);
-                       return readstr(off, a, n, ttext);
-               case Qio:
-                       if (reg == NULL)
-                               error(EFAIL, "region not configured");
-                       return regio(reg, a, n, off, 0);
+                       s = ns;
+               }
+               return readstr(off, a, n, ttext);
+       case Qpretty:
+               s = ttext;
+               e = ttext + tlen;
+               s = dumpfadt(s, e, fadt);
+               s = dumpmadt(s, e, apics);
+               s = dumpslit(s, e, slit);
+               s = dumpsrat(s, e, srat);
+               s = dumpdmar(s, e, dmar);
+               dumpmsct(s, e, mscttbl);
+               return readstr(off, a, n, ttext);
+       default:
+               error(EINVAL, "acpiread: bad path %d\n", q);
        }
        error(EPERM, ERROR_FIXME);
+
        return -1;
 }
 
 static long acpiwrite(struct chan *c, void *a, long n, int64_t off)
 {
+       error(EFAIL, "acpiwrite: not until we can figure out what it's for");
+#if 0
        ERRSTACK(2);
        struct cmdtab *ct;
        struct cmdbuf *cb;
@@ -1663,11 +2001,11 @@ static long acpiwrite(struct chan *c, void *a, long n, int64_t off)
                                fun = r->base >> Rpcifunshift & Rpcifunmask;
                                dev = r->base >> Rpcidevshift & Rpcidevmask;
                                bus = r->base >> Rpcibusshift & Rpcibusmask;
-                               #ifdef CONFIG_X86
+#ifdef CONFIG_X86
                                r->tbdf = MKBUS(BusPCI, bus, dev, fun);
-                               #else
+#else
                                r->tbdf = 0
-                               #endif
+#endif
                                r->base = rno;  /* register ~ our base addr */
                        }
                        r->base = strtoul(cb->f[3], NULL, 0);
@@ -1695,6 +2033,33 @@ static long acpiwrite(struct chan *c, void *a, long n, int64_t off)
        poperror();
        kfree(cb);
        return n;
+#endif
+}
+
+struct {
+       char *(*pretty)(struct Atable *atbl, char *start, char *end, void *arg);
+} acpisw[NACPITBLS] = {
+};
+
+static char *pretty(struct Atable *atbl, char *start, char *end, void *arg)
+{
+       int type;
+
+       type = atbl->type;
+       if (type < 0 || NACPITBLS < type)
+               return start;
+       if (acpisw[type].pretty == NULL)
+               return seprintf(start, end, "\"\"\n");
+       return acpisw[type].pretty(atbl, start, end, arg);
+}
+
+static char *raw(struct Atable *atbl, char *start, char *end, void *unused_arg)
+{
+       size_t len = MIN(end - start, atbl->rawsize);
+
+       memmove(start, atbl->raw, len);
+
+       return start + len;
 }
 
 struct dev acpidevtab __devtab = {
index 386a014..7ccfd8e 100644 (file)
@@ -20,24 +20,25 @@ static inline uint64_t hpet_r64(uintptr_t reg)
        return *((volatile uint64_t*)reg);
 }
 
-struct Atable *acpihpet(uint8_t * p, int len)
+struct Atable *parsehpet(struct Atable *parent,
+                         char *name, uint8_t *raw, size_t rawsize)
 {
        /* Do we want to keep this table around?  if so, we can use newtable, which
         * allocs an Atable and puts it on a global stailq.  then we return that
         * pointer, not as an addr, but as a signal to parse code about whether or
         * not it is safe to unmap (which we don't do anymore). */
-       struct Atable *hpet = new_acpi_table(p);
+       struct Atable *hpet = mkatable(parent, HPET, "HPET", raw, rawsize, 0);
        unsigned long hp_addr;
        uint32_t evt_blk_id;
        int nr_timers;
 
        assert(hpet);
-       printk("HPET table detected at %p, for %d bytes\n", p, len);
+       printk("HPET table detected at %p, for %d bytes\n", raw, rawsize);
 
-       evt_blk_id = l32get(p + 36);
+       evt_blk_id = l32get(raw + 36);
        printd("EV BID 0x%08x\n", evt_blk_id);
 
-       hp_addr = (unsigned long)KADDR_NOCHECK(l64get(p + 44));
+       hp_addr = (unsigned long)KADDR_NOCHECK(l64get(raw + 44));
 
        printd("cap/ip %p\n", hpet_r64(hp_addr + 0x00));
        printd("config %p\n", hpet_r64(hp_addr + 0x10));
@@ -52,7 +53,8 @@ struct Atable *acpihpet(uint8_t * p, int len)
         * HPET registers with reserved fields.*/
        hpet_w64(hp_addr + 0x10, hpet_r64(hp_addr + 0x10) & ~0x3);
        printk("Disabled the HPET timer\n");
-       return hpet;
+
+       return finatable_nochildren(hpet);
 }
 
 void cmos_dumping_ground()
index 3c2f4c5..70dd7f5 100644 (file)
@@ -2,4 +2,5 @@
 
 #include <acpi.h>
 
-struct Atable *acpihpet(uint8_t *p, int len);
+struct Atable *parsehpet(struct Atable *parent,
+                         char *name, uint8_t *p, size_t rawsize);
index 7a6d0ba..c9a340c 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
  * This file is part of the UCB release of Plan 9. It is subject to the license
  * terms in the LICENSE file found in the top-level directory of this
  * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
@@ -7,11 +7,57 @@
  * in the LICENSE file.
  */
 
+/* -----------------------------------------------------------------------------
+ * ACPI is a table of tables. The tables define a hierarchy.
+ *
+ * From the hardware's perspective:
+ * Each table that we care about has a header, and the header has a
+ * length that includes the the length of all its subtables. So, even
+ * if you can't completely parse a table, you can find the next table.
+ *
+ * The process of parsing is to find the RSDP, and then for each subtable
+ * see what type it is and parse it. The process is recursive except for
+ * a few issues: The RSDP signature and header differs from the header of
+ * its subtables; their headers differ from the signatures of the tables
+ * they contain. As you walk down the tree, you need different parsers.
+ *
+ * The parser is recursive descent. Each parsing function takes a pointer
+ * to the parent of the node it is parsing and will attach itself to the parent
+ * via that pointer. Parsing functions are responsible for building the data
+ * structures that represent their node and recursive invocations of the parser
+ * for subtables.
+ *
+ * So, in this case, it's something like this:
+ *
+ * RSDP is the root. It has a standard header and size. You map that
+ * memory.  You find the first header, get its type and size, and
+ * parse as much of it as you can. Parsing will involve either a
+ * function or case statement for each element type. DMARs are complex
+ * and need functions; APICs are simple and we can get by with case
+ * statements.
+ *
+ * Each node in the tree is represented as a 'struct Atable'. This has a
+ * pointer to the actual node data, a type tag, a name, pointers to this
+ * node's children (if any) and a parent pointer. It also has a QID so that
+ * the entire structure can be exposed as a filesystem. The Atable doesn't
+ * contain any table data per se; it's metadata. The table pointer contains
+ * the table data as well as a pointer back to it's corresponding Atable.
+ *
+ * In the end we present a directory tree for #apic that looks, in this example:
+ * #acpi/DMAR/DRHD/0/{pretty,raw}
+ *
+ * 'cat pretty' will return JSON-encoded data described the element.
+ * 'cat raw' gets you the raw bytes.
+ */
+
 #pragma once
 
+#include <ns.h>
+#include <slice.h>
+
 enum {
 
-       Sdthdrsz = 36,                          /* size of SDT header */
+       Sdthdrsz = 36,                  /* size of SDT header */
 
        /* ACPI regions. Gas ids */
        Rsysmem = 0,
@@ -25,7 +71,7 @@ enum {
        Rfixedhw = 0x7f,
 
        /* ACPI PM1 control */
-       Pm1SciEn = 0x1, /* Generate SCI and not SMI */
+       Pm1SciEn = 0x1,                 /* Generate SCI and not SMI */
 
        /* ACPI tbdf as encoded in acpi region base addresses */
        Rpciregshift = 0,
@@ -38,61 +84,110 @@ enum {
        Rpcibusmask = 0xFFFF,
 
        /* Apic structure types */
-       ASlapic = 0,    /* processor local apic */
-       ASioapic,       /* I/O apic */
-       ASintovr,       /* Interrupt source override */
-       ASnmi,  /* NMI source */
-       ASlnmi, /* local apic nmi */
-       ASladdr,        /* local apic address override */
-       ASiosapic,      /* I/O sapic */
-       ASlsapic,       /* local sapic */
-       ASintsrc,       /* platform interrupt sources */
-       ASlx2apic,      /* local x2 apic */
-       ASlx2nmi,       /* local x2 apic NMI */
+       ASlapic = 0,            /* processor local apic */
+       ASioapic,                       /* I/O apic */
+       ASintovr,                       /* Interrupt source override */
+       ASnmi,                          /* NMI source */
+       ASlnmi,                         /* local apic nmi */
+       ASladdr,                        /* local apic address override */
+       ASiosapic,                      /* I/O sapic */
+       ASlsapic,                       /* local sapic */
+       ASintsrc,                       /* platform interrupt sources */
+       ASlx2apic,                      /* local x2 apic */
+       ASlx2nmi,                       /* local x2 apic NMI */
 
        /* Apic flags */
-       AFbus = 0,      /* polarity/trigger like in ISA */
-       AFhigh = 1,     /* active high */
-       AFlow = 3,      /* active low */
-       AFpmask = 3,    /* polarity bits */
+       AFbus = 0,                      /* polarity/trigger like in ISA */
+       AFhigh = 1,                     /* active high */
+       AFlow = 3,                      /* active low */
+       AFpmask = 3,            /* polarity bits */
        AFedge = 1 << 2,        /* edge triggered */
        AFlevel = 3 << 2,       /* level triggered */
        AFtmask = 3 << 2,       /* trigger bits */
 
+       /* Table types. */
+       RSDP = 0,
+       SDTH,
+       RSDT,
+       FADT,
+       FACS,
+       DSDT,
+       SSDT,
+       MADT,
+       SBST,
+       XSDT,
+       ECDT,
+       SLIT,
+       SRAT,
+       CPEP,
+       MSCT,
+       RASF,
+       MPST,
+       PMTT,
+       BGRT,
+       FPDT,
+       GTDT,
+       HPET,
+       APIC,
+       DMAR,
+       /* DMAR types */
+       DRHD,
+       RMRR,
+       ATSR,
+       RHSA,
+       ANDD,
+       NACPITBLS,                      /* Number of ACPI tables */
+
        /* SRAT types */
-       SRlapic = 0,    /* Local apic/sapic affinity */
-       SRmem,  /* Memory affinity */
-       SRlx2apic,      /* x2 apic affinity */
+       SRlapic = 0,            /* Local apic/sapic affinity */
+       SRmem,                          /* Memory affinity */
+       SRlx2apic,                      /* x2 apic affinity */
+
+       /* Atable constants */
+       SIGSZ           = 4+1,  /* Size of the signature (including NUL) */
+       OEMIDSZ         = 6+1,  /* Size of the OEM ID (including NUL) */
+       OEMTBLIDSZ      = 8+1,  /* Size of the OEM Table ID (including NUL) */
 
        /* Arg for _PIC */
-       Ppic = 0,       /* PIC interrupt model */
-       Papic,  /* APIC interrupt model */
-       Psapic, /* SAPIC interrupt model */
-
-       CMregion = 0,   /* regio name spc base len accsz */
-       CMgpe,  /* gpe name id */
-
-       Qdir = 0,
-       Qctl,
-       Qtbl,
-       Qio,
-       Qpretty,
-       Qioapic,
-       Qapic,
-       Qraw,
+       Ppic = 0,                       /* PIC interrupt model */
+       Papic,                          /* APIC interrupt model */
+       Psapic,                         /* SAPIC interrupt model */
+
+       CMregion = 0,           /* regio name spc base len accsz */
+       CMgpe,                          /* gpe name id */
 };
 
 /*
  * ACPI table (sw)
+ *
+ * This Atable struct corresponds to an interpretation of the standard header
+ * for all table types we support. It has a pointer to the converted data, i.e.
+ * the structs created by functions like acpimadt and so on. Note: althouh the
+ * various things in this are a superset of many ACPI table names (DRHD, DRHD
+ * scopes, etc). The raw data follows this header.
+ *
+ * Child entries in the table are kept in an array of pointers. Each entry has
+ * a pointer to it's logically "next" sibling, thus forming a linked list. But
+ * these lists are purely for convenience and all point to nodes within the
+ * same array.
  */
 struct Atable {
-       struct Atable *next;            /* next table in list */
-       int is64;                                       /* uses 64bits */
-       char sig[5];                            /* signature */
-       char oemid[7];                          /* oem id str. */
-       char oemtblid[9];                       /* oem tbl. id str. */
-       uint8_t *tbl;                           /* pointer to table in memory */
-       long dlen;                                      /* size of data in table, after Stdhdr */
+       struct qid qid;             /* QID corresponding to this table. */
+       struct qid rqid;                        /* This table's 'raw' QID. */
+       struct qid pqid;                        /* This table's 'pretty' QID. */
+       struct qid tqid;                        /* This table's 'table' QID. */
+       int type;                                       /* This table's type */
+       void *tbl;                                      /* pointer to the converted table, e.g. madt. */
+       char name[16];                          /* name of this table */
+
+       struct Atable *parent;          /* Parent pointer */
+       struct Atable **children;       /* children of this node (an array). */
+       struct dirtab *cdirs;           /* child directory entries of this node. */
+       size_t nchildren;                       /* count of this node's children */
+       struct Atable *next;            /* Pointer to the next sibling. */
+
+       size_t rawsize;                         /* Total size of raw table */
+       uint8_t *raw;                           /* Raw data. */
 };
 
 struct Gpe {
@@ -105,21 +200,16 @@ struct Gpe {
        int id;                                         /* id as supplied by user */
 };
 
-struct Parse {
-       char *sig;
-       struct Atable *(*f) (uint8_t * unused_uint8_p_t, int);  /* return NULL to keep vmap */
-};
-
 struct Regio {
        void *arg;
-        uint8_t(*get8) (uintptr_t, void *);
-       void (*set8) (uintptr_t, uint8_t unused_int, void *);
-        uint16_t(*get16) (uintptr_t, void *);
-       void (*set16) (uintptr_t, uint16_t unused_int, void *);
-        uint32_t(*get32) (uintptr_t, void *);
-       void (*set32) (uintptr_t, uint32_t, void *);
-        uint64_t(*get64) (uintptr_t, void *);
-       void (*set64) (uintptr_t, uint64_t unused_int, void *);
+       uint8_t (*get8)(uintptr_t, void *);
+       void (*set8)(uintptr_t, uint8_t unused_int, void *);
+       uint16_t (*get16)(uintptr_t, void *);
+       void (*set16)(uintptr_t, uint16_t unused_int, void *);
+       uint32_t (*get32)(uintptr_t, void *);
+       void (*set32)(uintptr_t, uint32_t, void *);
+       uint64_t (*get64)(uintptr_t, void *);
+       void (*set64)(uintptr_t, uint64_t unused_int, void *);
 };
 
 struct Reg {
@@ -132,7 +222,7 @@ struct Reg {
        int accsz;                                      /* access size */
 };
 
-/* Generic address structure. 
+/* Generic address structure.
  */
 struct Gas {
        uint8_t spc;                            /* address space id */
@@ -151,7 +241,6 @@ struct Gas {
  *     - SSDTs tables with AML code to add to the acpi namespace.
  *     - pointers to other tables for apics, etc.
  */
-
 struct Rsdp {
        uint8_t signature[8];           /* "RSD PTR " */
        uint8_t rchecksum;
@@ -161,7 +250,7 @@ struct Rsdp {
        uint8_t length[4];
        uint8_t xaddr[8];                       /* XSDT */
        uint8_t xchecksum;                      /* XSDT */
-       uint8_t _33_[3];                        /* reserved */
+       uint8_t _reserved[3];                   /* reserved */
 };
 
 /* Header for ACPI description tables
@@ -181,6 +270,8 @@ struct Sdthdr {
 /* Firmware control structure
  */
 struct Facs {
+       uint8_t sig[4];
+       uint8_t len[4];
        uint32_t hwsig;
        uint32_t wakingv;
        uint32_t glock;
@@ -196,12 +287,11 @@ struct Msct {
        int ndoms;                                      /* number of domains */
        int nclkdoms;                           /* number of clock domains */
        uint64_t maxpa;                         /* max physical address */
-
-       struct Mdom *dom;                       /* domain information list */
+       size_t nmdom;                           /* number of discovered domains */
+       struct Mdom *dom;                       /* array of domains */
 };
 
 struct Mdom {
-       struct Mdom *next;
        int start;                                      /* start dom id */
        int end;                                        /* end dom id */
        int maxproc;                            /* max processor capacity */
@@ -217,12 +307,10 @@ struct Mdom {
 struct Madt {
        uint64_t lapicpa;                       /* local APIC addr */
        int pcat;                                       /* the machine has PC/AT 8259s */
-       struct Apicst *st;                      /* list of Apic related structures */
 };
 
 struct Apicst {
        int type;
-       struct Apicst *next;
        union {
                struct {
                        int pid;                        /* processor id */
@@ -279,7 +367,6 @@ struct Apicst {
  */
 struct Srat {
        int type;
-       struct Srat *next;
        union {
                struct {
                        int dom;                        /* proximity domain */
@@ -378,15 +465,51 @@ struct Fadt {
 /* XSDT/RSDT. 4/8 byte addresses starting at p.
  */
 struct Xsdt {
-       int len;
-       int asize;
+       size_t len;
+       size_t asize;
        uint8_t *p;
 };
 
+/* DMAR.
+ */
+/*
+ * Device scope.
+ */
+struct DevScope {
+       int enumeration_id;
+       int start_bus_number;
+       int npath;
+       int *paths;
+};
+/*
+ * The device scope is basic tbdf as uint32_t. There is a special value
+ * that means "everything" and if we see that we set "all" in the Drhd.
+ */
+struct Drhd {
+       int flags;
+       int segment;
+       uintptr_t rba;
+       uintptr_t all;  // This drhd scope is for everything.
+       size_t nscope;
+       struct DevScope *scopes;
+};
 
-extern uintptr_t acpimblocksize(uintptr_t, int *);
+struct Dmar {
+       int haw;
+       /*
+        * If your firmware disables x2apic mode, you should not be here.
+        * We ignore that bit.
+        */
+       int intr_remap;
+};
 
 int acpiinit(void);
-struct Atable *new_acpi_table(uint8_t *p);
-extern struct Madt *apics;
-extern struct Srat *srat;
+struct Atable *mkatable(struct Atable *parent,
+                        int type, char *name, uint8_t *raw,
+                        size_t rawsize, size_t addsize);
+struct Atable *finatable(struct Atable *t, struct slice *slice);
+struct Atable *finatable_nochildren(struct Atable *t);
+
+extern struct Atable *apics;
+extern struct Atable *dmar;
+extern struct Atable *srat;