Below you'll find a patch that adds vt-d support
(https://software.intel.com/en-us/blogs/2009/06/25/understanding-vt-d-intel-virtualization-technology-for-directed-io).
Being able to restrict where devices perform DMA is a pretty big security
benefit.  This code works on several machines but isn't quite completed yet.
For example it doesn't handle drm correctly yet (it bypasses it for now).  It
also needs a lot of testing on different hardware to determine its actual
state.  The time has however come to share this with the community to gauge
interest and getting some testing.  Suspend/resume is also only working on some
machines and needs more testing.  I think the acpidmar devices need to be one 
of the
last devices suspended/first resumed.
 
This code was developed under contract from Company 0 for the Bitrig project.
Code was developed on OpenBSD because that is where I have most experience. I
sincerely hope this code can be imported into both trees.

Index: arch/amd64/conf/GENERIC
===================================================================
RCS file: /cvs/src/sys/arch/amd64/conf/GENERIC,v
retrieving revision 1.391
diff -u -p -u -p -r1.391 GENERIC
--- arch/amd64/conf/GENERIC     21 Jul 2015 03:38:22 -0000      1.391
+++ arch/amd64/conf/GENERIC     22 Jul 2015 20:24:49 -0000
@@ -52,6 +52,7 @@ acpiec*               at acpi?
 acpiprt*       at acpi?
 acpitz*                at acpi?
 acpimadt0      at acpi?
+acpidmar0      at acpi?
 acpimcfg*      at acpi?
 acpiasus*      at acpi?
 acpisony*      at acpi?
Index: arch/amd64/include/pci_machdep.h
===================================================================
RCS file: /cvs/src/sys/arch/amd64/include/pci_machdep.h,v
retrieving revision 1.23
diff -u -p -u -p -r1.23 pci_machdep.h
--- arch/amd64/include/pci_machdep.h    17 Jul 2015 22:42:09 -0000      1.23
+++ arch/amd64/include/pci_machdep.h    22 Jul 2015 20:24:49 -0000
@@ -90,7 +90,8 @@ void          *pci_intr_establish(pci_chipset_ta
 void           pci_intr_disestablish(pci_chipset_tag_t, void *);
 void           pci_decompose_tag(pci_chipset_tag_t, pcitag_t,
                    int *, int *, int *);
-#define        pci_probe_device_hook(c, a)     (0)
+int            pci_probe_device_hook(pci_chipset_tag_t,
+                   struct pci_attach_args *);
 
 void           pci_dev_postattach(struct device *, struct pci_attach_args *);
 
Index: arch/amd64/pci/pci_machdep.c
===================================================================
RCS file: /cvs/src/sys/arch/amd64/pci/pci_machdep.c,v
retrieving revision 1.62
diff -u -p -u -p -r1.62 pci_machdep.c
--- arch/amd64/pci/pci_machdep.c        14 Mar 2015 03:38:46 -0000      1.62
+++ arch/amd64/pci/pci_machdep.c        22 Jul 2015 20:24:49 -0000
@@ -147,6 +147,15 @@ struct bus_dma_tag pci_bus_dma_tag = {
        _bus_dmamem_mmap,
 };
 
+extern void acpidmar_pci_hook(pci_chipset_tag_t, struct pci_attach_args *);
+
+int
+pci_probe_device_hook(pci_chipset_tag_t pc, struct pci_attach_args *pa)
+{
+       acpidmar_pci_hook(pc, pa);
+       return (0);
+}
+
 void
 pci_attach_hook(struct device *parent, struct device *self,
     struct pcibus_attach_args *pba)
Index: dev/acpi/acpidmar.c
===================================================================
RCS file: dev/acpi/acpidmar.c
diff -N dev/acpi/acpidmar.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ dev/acpi/acpidmar.c 22 Jul 2015 20:25:36 -0000
@@ -0,0 +1,1872 @@
+/*
+ * Copyright (c) 2015 Jordan Hargrave <jordan_hargr...@hotmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/device.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/mbuf.h>
+#include <sys/proc.h>
+
+#include <uvm/uvm_extern.h>
+
+#include <machine/apicvar.h>
+#include <machine/cpuvar.h>
+#include <machine/bus.h>
+
+#include <dev/acpi/acpireg.h>
+#include <dev/acpi/acpivar.h>
+#include <dev/acpi/acpidev.h>
+#include <dev/acpi/amltypes.h>
+#include <dev/acpi/dsdt.h>
+
+#include <uvm/uvm_extern.h>
+
+#include <machine/i8259.h>
+#include <machine/i82093reg.h>
+#include <machine/i82093var.h>
+#include <machine/i82489reg.h>
+#include <machine/i82489var.h>
+
+#include <machine/mpbiosvar.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcidevs.h>
+#include <dev/pci/ppbreg.h>
+
+#include "ioapic.h"
+
+#include "acpidmar.h"
+
+#define dprintf(x...)
+
+int intel_iommu_gfx_mapped = 0;
+
+/* Page Table Entry per domain */
+struct iommu_softc;
+
+static inline int mksid(int b, int d, int f)
+{
+       return (b << 8) + (d << 3) + f;
+}
+
+static inline int
+sid_devfn(int sid)
+{
+       return sid & 0xff;
+}
+
+static inline int sid_bus(int sid)
+{
+       return (sid>> 8) & 0xff;
+}
+
+static inline int sid_dev(int sid)
+{
+       return (sid>> 3) & 0x1f;
+}
+
+static inline int sid_fun(int sid)
+{
+       return (sid>> 0) & 0x7;
+}
+
+struct domain_dev {
+       int                     sid;
+       TAILQ_ENTRY(domain_dev) link;
+};
+
+struct domain {
+       struct iommu_softc      *iommu;
+       int                     did;
+       int                     gaw;
+       struct pte_entry        *pte;
+       paddr_t                 ptep;
+       struct bus_dma_tag      dmat;
+       int                     flag;
+
+       char                    exname[32];
+       struct extent           *iovamap;
+       TAILQ_HEAD(,domain_dev) devices;
+       TAILQ_ENTRY(domain)     link;
+};
+
+struct dmar_devlist {
+       int                             type;
+       int                             bus;
+       int                             ndp;
+       struct acpidmar_devpath         *dp;
+       TAILQ_ENTRY(dmar_devlist)       link;
+};
+
+TAILQ_HEAD(devlist_head, dmar_devlist);
+
+struct rmrr_softc {
+       TAILQ_ENTRY(rmrr_softc) link;
+       struct devlist_head     devices;
+       int                     segment;
+       uint64_t                start;
+       uint64_t                end;
+};
+
+struct atsr_softc {
+       TAILQ_ENTRY(atsr_softc) link;
+       struct devlist_head     devices;
+       int                     segment;
+       int                     flags;
+};
+
+struct iommu_pic {
+       struct pic              pic;
+       struct iommu_softc      *iommu;
+};
+
+#define IOMMU_FLAGS_CATCHALL           0x1
+#define IOMMU_FLAGS_BAD                        0x2
+#define IOMMU_FLAGS_SUSPEND            0x4
+
+struct iommu_softc {
+       TAILQ_ENTRY(iommu_softc)link;
+       struct devlist_head     devices;
+       int                     id;
+       int                     flags;
+       int                     segment;
+
+       struct mutex            reg_lock;
+
+       bus_space_tag_t         iot;
+       bus_space_handle_t      ioh;
+
+       uint64_t                cap;
+       uint64_t                ecap;
+       uint32_t                gcmd;
+
+       int                     mgaw;
+       int                     agaw;
+       int                     ndoms;
+
+       struct root_entry       *root;
+       struct context_entry    *ctx[256];
+
+       void                    *intr;
+       struct iommu_pic        pic;
+       int                     fedata;
+       uint64_t                feaddr;
+       uint64_t                rtaddr;
+
+       // Queued Invalidation
+       int                     qi_head;
+       int                     qi_tail;
+       paddr_t                 qip;
+       struct qi_entry         *qi;
+
+       struct domain           *unity;
+       TAILQ_HEAD(,domain)     domains;
+};
+
+static inline int iommu_bad(struct iommu_softc *sc)
+{
+       return (sc->flags & IOMMU_FLAGS_BAD);
+}
+
+static inline int iommu_enabled(struct iommu_softc *sc)
+{
+       return (sc->gcmd & GCMD_TE);
+}
+
+struct acpidmar_softc {
+       struct device           sc_dev;
+
+       pci_chipset_tag_t       sc_pc;
+       bus_space_tag_t         sc_memt;
+       int                     sc_haw;
+       int                     sc_flags;
+
+       TAILQ_HEAD(,iommu_softc)sc_drhds;
+       TAILQ_HEAD(,rmrr_softc) sc_rmrrs;
+       TAILQ_HEAD(,atsr_softc) sc_atsrs;
+};
+
+int            acpidmar_activate(struct device *, int);
+int            acpidmar_match(struct device *, void *, void *);
+void           acpidmar_attach(struct device *, struct device *, void *);
+struct domain  *acpidmar_pci_attach(struct acpidmar_softc *, int, int);
+
+struct cfattach acpidmar_ca = {
+       sizeof(struct acpidmar_softc), acpidmar_match, acpidmar_attach,
+       NULL, acpidmar_activate
+};
+
+struct cfdriver acpidmar_cd = {
+       NULL, "acpidmar", DV_DULL
+};
+
+struct         acpidmar_softc *acpidmar_sc;
+int            acpidmar_intr(void *);
+
+#define DID_UNITY 0x1
+
+struct domain *domain_create(struct iommu_softc *, int);
+struct domain *domain_lookup(struct acpidmar_softc *, int, int);
+
+void domain_unload_map(struct domain *, bus_dmamap_t);
+void domain_load_map(struct domain *, bus_dmamap_t, int, const char *);
+
+void domain_map_page(struct domain *, vaddr_t, paddr_t, int);
+void domain_map_pthru(struct domain *, paddr_t, paddr_t);
+
+void acpidmar_pci_hook(pci_chipset_tag_t, struct pci_attach_args *);
+void acpidmar_parse_devscope(union acpidmar_entry *, int, int,
+    struct devlist_head *);
+int  acpidmar_match_devscope(struct devlist_head *, pci_chipset_tag_t, int);
+
+void acpidmar_init(struct acpidmar_softc *, struct acpi_dmar *);
+void acpidmar_drhd(struct acpidmar_softc *, union acpidmar_entry *);
+void acpidmar_rmrr(struct acpidmar_softc *, union acpidmar_entry *);
+void acpidmar_atsr(struct acpidmar_softc *, union acpidmar_entry *);
+
+void *acpidmar_intr_establish(void *, int, int (*)(void *), void *,
+    const char *);
+
+void iommu_writel(struct iommu_softc *, int, uint32_t);
+void iommu_writeq(struct iommu_softc *, int, uint64_t);
+uint64_t iommu_readq(struct iommu_softc *, int);
+uint32_t iommu_readl(struct iommu_softc *, int);
+void iommu_showfault(struct iommu_softc *, int,
+    struct fault_entry *);
+void iommu_showcfg(struct iommu_softc *);
+
+int iommu_init(struct acpidmar_softc *, struct iommu_softc *,
+    struct acpidmar_drhd *);
+int iommu_enable_translation(struct iommu_softc *, int);
+void iommu_enable_qi(struct iommu_softc *, int);
+void iommu_flush_cache(struct iommu_softc *, void *, size_t);
+void *iommu_alloc_page(struct iommu_softc *, paddr_t *);
+
+void iommu_flush_ctx(struct iommu_softc *, int, int, int, int);
+void iommu_flush_tlb(struct iommu_softc *, int, int);
+
+const char *dmar_bdf(int);
+
+const char *
+dmar_bdf(int sid)
+{
+       static char     bdf[32];
+
+       snprintf(bdf, sizeof(bdf), "%.4x:%.2x:%.2x.%x", 0,
+           sid_bus(sid), sid_dev(sid), sid_fun(sid));
+
+       return (bdf);
+}
+
+/* busdma */
+static int dmar_dmamap_create(bus_dma_tag_t, bus_size_t, int, bus_size_t,
+    bus_size_t, int, bus_dmamap_t *);
+static void dmar_dmamap_destroy(bus_dma_tag_t, bus_dmamap_t);
+static int dmar_dmamap_load(bus_dma_tag_t, bus_dmamap_t, void *, bus_size_t,
+    struct proc *, int);
+static int dmar_dmamap_load_mbuf(bus_dma_tag_t, bus_dmamap_t, struct mbuf *,
+    int);
+static int dmar_dmamap_load_uio(bus_dma_tag_t, bus_dmamap_t, struct uio *, 
int);
+static int dmar_dmamap_load_raw(bus_dma_tag_t, bus_dmamap_t,
+    bus_dma_segment_t *, int, bus_size_t, int);
+static void dmar_dmamap_unload(bus_dma_tag_t, bus_dmamap_t);
+static void dmar_dmamap_sync(bus_dma_tag_t, bus_dmamap_t, bus_addr_t,
+    bus_size_t, int);
+static int dmar_dmamem_alloc(bus_dma_tag_t, bus_size_t, bus_size_t, bus_size_t,
+    bus_dma_segment_t *, int, int *, int);
+static void dmar_dmamem_free(bus_dma_tag_t, bus_dma_segment_t *, int);
+static int dmar_dmamem_map(bus_dma_tag_t, bus_dma_segment_t *, int, size_t,
+    caddr_t *, int);
+static void dmar_dmamem_unmap(bus_dma_tag_t, caddr_t, size_t);
+static paddr_t dmar_dmamem_mmap(bus_dma_tag_t, bus_dma_segment_t *, int, off_t,
+    int, int);
+
+int dmar_dbg = 0;
+
+static inline int
+debugme(struct domain *dom)
+{
+       return (0);
+}
+
+#if 0
+void
+dmar_printf(const char *fmt, ...)
+{
+       va_list ap;
+
+       return;
+       va_start(ap,fmt);
+       vprintf(fmt,ap);
+}
+#endif
+
+const char *dom_bdf(struct domain *dom);
+
+/* Map a range of pages 1:1 */
+void
+domain_map_pthru(struct domain *dom, paddr_t start, paddr_t end)
+{
+       while (start < end) {
+               domain_map_page(dom, start, start, PTE_P | PTE_R | PTE_W);
+               start += VTD_PAGE_SIZE;
+       }
+}
+
+/* Map a single paddr to IOMMU paddr */
+void
+domain_map_page(struct domain *dom, vaddr_t va, paddr_t pa, int flags)
+{
+       paddr_t paddr;
+       struct pte_entry *pte, *npte;
+       int lvl, idx;
+
+       /* Only handle 4k pages for now */
+       npte = dom->pte;
+       /* XXX why 12? */
+       for (lvl = dom->iommu->agaw - VTD_STRIDE_SIZE; lvl>= 12;
+           lvl -= VTD_STRIDE_SIZE) {
+               idx = (va>> lvl) & VTD_STRIDE_MASK;
+               pte = &npte[idx];
+               if (lvl == 12) {
+                       /* Level 1: Page Table - add physical address */
+                       pte->val = pa | flags;
+                       iommu_flush_cache(dom->iommu, pte, sizeof(*pte));
+                       break;
+               } else if (!pte->val & PTE_P) {
+                       /* Level N: Point to lower level table */
+                       iommu_alloc_page(dom->iommu, &paddr);
+                       pte->val = paddr | PTE_P | PTE_R | PTE_W;
+                       iommu_flush_cache(dom->iommu, pte, sizeof(*pte));
+               }
+               npte = (void *)PMAP_DIRECT_MAP((pte->val & ~0xFFF));
+       }
+}
+
+/* Unload mapping */
+void
+domain_unload_map(struct domain *dom, bus_dmamap_t dmam)
+{
+       bus_dma_segment_t       *seg;
+       paddr_t                 base, end, idx;
+       psize_t                 alen;
+       int                     i, s;
+
+       if (iommu_bad(dom->iommu)) {
+               printf("unload map no iommu\n");
+               return;
+       }
+
+       acpidmar_intr(dom->iommu);
+       for (i = 0; i < dmam->dm_nsegs; i++) {
+               seg  = &dmam->dm_segs[i];
+
+               base = trunc_page(seg->ds_addr);
+               end  = roundup(seg->ds_addr + seg->ds_len, VTD_PAGE_SIZE);
+               alen = end - base;
+
+               if (debugme(dom)) {
+                       printf("  va:%.16llx len:%x\n",
+                           (uint64_t)base, (uint32_t)alen);
+               }
+
+               s = splhigh();
+               extent_free(dom->iovamap, base, alen, EX_NOWAIT);
+               splx(s);
+
+               /* Clear PTE */
+               for (idx = 0; idx < alen; idx+=VTD_PAGE_SIZE)
+                       domain_map_page(dom, base + idx, 0, 0);
+       }
+}
+
+/* map.segs[x].ds_addr is modified to IOMMU virtual PA */
+void
+domain_load_map(struct domain *dom, bus_dmamap_t map, int flags, const char 
*fn)
+{
+       bus_dma_segment_t       *seg;
+       struct iommu_softc      *iommu;
+       paddr_t                 base, end, idx;
+       psize_t                 alen;
+       u_long                  res;
+       int                     i, s;
+
+       iommu = dom->iommu;
+       if (!iommu_enabled(iommu)) {
+               /* Lazy enable translation when required */
+               if (iommu_enable_translation(iommu, 1)) {
+                       return;
+               }
+       }
+
+       acpidmar_intr(dom->iommu);
+       if (debugme(dom)) {
+               printf("%s: %s\n", fn, dom_bdf(dom));
+       }
+
+       for (i = 0; i < map->dm_nsegs; i++) {
+               seg = &map->dm_segs[i];
+
+               base = trunc_page(seg->ds_addr);
+               end  = roundup(seg->ds_addr + seg->ds_len, VTD_PAGE_SIZE);
+               alen = end - base;
+
+               s = splhigh();
+               extent_alloc(dom->iovamap, alen, VTD_PAGE_SIZE, 0,
+                   map->_dm_boundary, EX_NOWAIT, &res);
+               splx(s);
+
+               if (debugme(dom)) {
+                       printf("  %.16llx %x => %.16llx\n",
+                           (uint64_t)seg->ds_addr, (uint32_t)seg->ds_len,
+                           (uint64_t)res);
+               }
+
+               seg->ds_addr = res | (seg->ds_addr & 0xFFF);
+               for (idx = 0; idx < alen; idx += VTD_PAGE_SIZE) {
+                       domain_map_page(dom, res + idx, base + idx,
+                           PTE_P | PTE_R | PTE_W);
+               }
+       }
+
+       iommu_flush_tlb(dom->iommu, IOTLB_DOMAIN, dom->did);
+}
+
+/* Bus DMA Map functions */
+const char *dom_bdf(struct domain *dom)
+{
+       struct domain_dev       *dd;
+       static char             mmm[48];
+
+       dd = TAILQ_FIRST(&dom->devices);
+       snprintf(mmm, sizeof(mmm), "%s iommu:%d did:%.4x%s",
+           dmar_bdf(dd->sid), dom->iommu->id, dom->did,
+           dom->did == DID_UNITY ? " [unity]" : "");
+
+       return (mmm);
+}
+
+static int
+dmar_dmamap_create(bus_dma_tag_t tag, bus_size_t size, int nsegments,
+    bus_size_t maxsegsz, bus_size_t boundary, int flags, bus_dmamap_t *dmamp)
+{
+       struct domain   *dom = tag->_cookie;
+       bus_dmamap_t    dmam;
+       int             rc, i;
+
+       rc=_bus_dmamap_create(tag,size,nsegments,maxsegsz,boundary,
+           flags,dmamp);
+       if (rc)
+               return (rc);
+
+       dmam = *dmamp;
+       if (debugme(dom)) {
+               printf("dmamap_create: %s\n", dom_bdf(dom));
+               for (i = 0; i<dmam->dm_nsegs; i++) {
+                       printf("  create: %.16llx %x\n",
+                           (uint64_t)dmam->dm_segs[i].ds_addr,
+                           (uint32_t)dmam->dm_segs[i].ds_len);
+               }
+       }
+
+       return (rc);
+}
+
+static void
+dmar_dmamap_destroy(bus_dma_tag_t tag, bus_dmamap_t dmam)
+{
+       struct domain   *dom = tag->_cookie;
+       int             i;
+
+       if (debugme(dom)) {
+               printf("dmamap_destroy: %s\n", dom_bdf(dom));
+               for (i = 0; i < dmam->dm_nsegs; i++) {
+                       printf("  destroy: %.16llx %x\n",
+                           (uint64_t)dmam->dm_segs[i].ds_addr,
+                           (uint32_t)dmam->dm_segs[i].ds_len);
+               }
+       }
+       _bus_dmamap_destroy(tag,dmam);
+}
+
+static int
+dmar_dmamap_load(bus_dma_tag_t tag, bus_dmamap_t dmam, void *buf,
+    bus_size_t buflen, struct proc *p, int flags)
+{
+       struct domain   *dom = tag->_cookie;
+       int             rc;
+
+       rc=_bus_dmamap_load(tag,dmam,buf,buflen,p,flags);
+       if (rc)
+               return (rc);
+
+       domain_load_map(dom, dmam, flags, __FUNCTION__);
+
+       return (0);
+}
+
+static int
+dmar_dmamap_load_mbuf(bus_dma_tag_t tag, bus_dmamap_t dmam, struct mbuf *chain,
+    int flags)
+{
+       struct domain   *dom = tag->_cookie;
+       int             rc;
+
+       rc=_bus_dmamap_load_mbuf(tag,dmam,chain,flags);
+       if (rc)
+               return (rc);
+
+       domain_load_map(dom, dmam, flags, __FUNCTION__);
+
+       return (0);
+}
+
+static int
+dmar_dmamap_load_uio(bus_dma_tag_t tag, bus_dmamap_t dmam, struct uio *uio,
+    int flags)
+{
+       struct domain   *dom = tag->_cookie;
+       int             rc;
+
+       rc=_bus_dmamap_load_uio(tag,dmam,uio,flags);
+       if (rc)
+               return (rc);
+
+       domain_load_map(dom, dmam, flags, __FUNCTION__);
+
+       return (0);
+}
+
+static int
+dmar_dmamap_load_raw(bus_dma_tag_t tag, bus_dmamap_t dmam,
+    bus_dma_segment_t *segs, int nsegs, bus_size_t size, int flags)
+{
+       struct domain *dom = tag->_cookie;
+       int rc;
+
+       rc=_bus_dmamap_load_raw(tag,dmam,segs,nsegs,size,flags);
+       if (rc)
+               return rc;
+
+       domain_load_map(dom, dmam, flags, __FUNCTION__);
+
+       return (0);
+}
+
+static void
+dmar_dmamap_unload(bus_dma_tag_t tag, bus_dmamap_t dmam)
+{
+       struct domain   *dom = tag->_cookie;
+
+       if (debugme(dom)) {
+               printf("dmamap_unload: %s\n", dom_bdf(dom));
+       }
+       domain_unload_map(dom, dmam);
+       _bus_dmamap_unload(tag,dmam);
+}
+
+static void
+dmar_dmamap_sync(bus_dma_tag_t tag, bus_dmamap_t dmam, bus_addr_t offset,
+    bus_size_t len, int ops)
+{
+       struct domain   *dom = tag->_cookie;
+       int             i, flag;
+
+       flag = 0;
+       acpidmar_intr(dom->iommu);
+       if (ops == BUS_DMASYNC_PREREAD) {
+               /* make readable */
+               flag = PTE_P | PTE_R;
+       } else if (ops == BUS_DMASYNC_PREWRITE) {
+               /* make writeable */
+               flag = PTE_P | PTE_W;
+       }
+       if (debugme(dom)) {
+               printf("dmamap_sync: %s\n", dom_bdf(dom));
+               for (i=0; i<dmam->dm_nsegs; i++) {
+                       printf("  sync: %.16llx %x\n",
+                           (uint64_t)dmam->dm_segs[i].ds_addr,
+                           (uint32_t)dmam->dm_segs[i].ds_len);
+               }
+       }
+       _bus_dmamap_sync(tag,dmam,offset,len,ops);
+}
+
+static int
+dmar_dmamem_alloc(bus_dma_tag_t tag, bus_size_t size, bus_size_t alignment,
+    bus_size_t boundary, bus_dma_segment_t *segs, int nsegs, int *rsegs,
+    int flags)
+{
+       struct domain   *dom = tag->_cookie;
+       int             rc, i;
+
+       rc = _bus_dmamem_alloc(tag,size,alignment,boundary,segs,nsegs,rsegs,
+           flags);
+       if (rc)
+               return rc;
+
+       if (debugme(dom)) {
+               printf("dmamap_alloc size=%llx nsegs=%d %s\n",
+                   (uint64_t)size, nsegs, dom_bdf(dom));
+               for (i = 0; i < *rsegs; i++) {
+                       printf("  alloc: %.16llx %x\n",
+                           (uint64_t)segs[i].ds_addr,
+                           (uint32_t)segs[i].ds_len);
+               }
+       }
+
+       return (rc);
+}
+
+static void
+dmar_dmamem_free(bus_dma_tag_t tag, bus_dma_segment_t *segs, int nsegs)
+{
+       struct domain   *dom = tag->_cookie;
+       int             i;
+
+       if (debugme(dom)) {
+               printf("dmamap_free: %s\n", dom_bdf(dom));
+               for (i = 0; i < nsegs; i++) {
+                       printf("  free: %.16llx %x\n",
+                           (uint64_t)segs[i].ds_addr,
+                           (uint32_t)segs[i].ds_len);
+               }
+       }
+       _bus_dmamem_free(tag,segs,nsegs);
+}
+
+static int
+dmar_dmamem_map(bus_dma_tag_t tag, bus_dma_segment_t *segs, int nsegs,
+    size_t size, caddr_t *kvap, int flags)
+{
+       struct domain   *dom = tag->_cookie;
+       int             i;
+
+       if (debugme(dom)) {
+               printf("dmamap_map: %s\n", dom_bdf(dom));
+               for (i = 0; i < nsegs; i++) {
+                       printf("  map: %.16llx %x\n",
+                           (uint64_t)segs[i].ds_addr,
+                           (uint32_t)segs[i].ds_len);
+               }
+       }
+
+       return (_bus_dmamem_map(tag,segs,nsegs,size,kvap,flags));
+}
+
+static void
+dmar_dmamem_unmap(bus_dma_tag_t tag, caddr_t kva, size_t size)
+{
+       struct domain   *dom = tag->_cookie;
+
+       if (debugme(dom)) {
+               printf("dmamap_unmap: %s\n", dom_bdf(dom));
+       }
+       _bus_dmamem_unmap(tag,kva,size);
+}
+
+static paddr_t
+dmar_dmamem_mmap(bus_dma_tag_t tag, bus_dma_segment_t *segs, int nsegs,
+    off_t off, int prot, int flags)
+{
+       struct domain   *dom = tag->_cookie;
+       int             i;
+
+       if (debugme(dom)) {
+               printf("dmamap_mmap: %s\n", dom_bdf(dom));
+               for (i = 0; i < nsegs; i++) {
+                       printf("  mmap: %.16llx %.8x\n",
+                           (uint64_t)segs[i].ds_addr,
+                           (uint32_t)segs[i].ds_len);
+               }
+       }
+
+       return (_bus_dmamem_mmap(tag,segs,nsegs,off,prot,flags));
+}
+
+/*===================================
+ * IOMMU code
+ *==================================*/
+void *
+iommu_alloc_page(struct iommu_softc *iommu, paddr_t *paddr)
+{
+       void    *va;
+
+       *paddr = 0;
+       va = km_alloc(VTD_PAGE_SIZE, &kv_page, &kp_zero, &kd_nowait);
+       if (va == NULL) {
+               panic("can't allocate page\n");
+       }
+       pmap_extract(pmap_kernel(), (vaddr_t)va, paddr);
+
+       return (va);
+}
+
+void
+iommu_flush_cache(struct iommu_softc *iommu, void *addr, size_t size)
+{
+       if (!(iommu->ecap & ECAP_C))
+               pmap_flush_cache((vaddr_t)addr, size);
+}
+
+void
+iommu_flush_tlb(struct iommu_softc *iommu, int mode, int did)
+{
+       int             n;
+       uint64_t        val;
+
+       if (iommu->gcmd & GCMD_QIE) {
+               struct qi_entry qi;
+
+               /* Use queued invalidation */
+               qi.hi = 0;
+               switch (mode) {
+               case IOTLB_GLOBAL:
+                       qi.lo = QI_IOTLB | QI_IOTLB_IG_GLOBAL;
+                       break;
+               case IOTLB_DOMAIN:
+                       qi.lo = QI_IOTLB | QI_IOTLB_IG_DOMAIN |
+                           QI_IOTLB_DID(did);
+                       break;
+               case IOTLB_PAGE:
+                       qi.lo = QI_IOTLB | QI_IOTLB_IG_PAGE | QI_IOTLB_DID(did);
+                       qi.hi = 0;
+                       break;
+               }
+               if (iommu->cap & CAP_DRD)
+                       qi.lo |= QI_IOTLB_DR;
+               if (iommu->cap & CAP_DWD)
+                       qi.lo |= QI_IOTLB_DW;
+               return;
+       }
+
+       val = IOTLB_IVT;
+       switch (mode) {
+       case IOTLB_GLOBAL:
+               val |= IIG_GLOBAL;
+               break;
+       case IOTLB_DOMAIN:
+               val |= IIG_DOMAIN | IOTLB_DID(did);
+               break;
+       case IOTLB_PAGE:
+               val |= IIG_PAGE | IOTLB_DID(did);
+               break;
+       }
+
+       if (iommu->cap & CAP_DRD)
+               val |= IOTLB_DR;
+       if (iommu->cap & CAP_DWD)
+               val |= IOTLB_DW;
+
+       mtx_enter(&iommu->reg_lock);
+
+       iommu_writeq(iommu, DMAR_IOTLB_REG(iommu), val);
+       n = 0;
+       do {
+               val = iommu_readq(iommu, DMAR_IOTLB_REG(iommu));
+       } while (n++ < 5 && val & IOTLB_IVT);
+
+       mtx_leave(&iommu->reg_lock);
+
+#ifdef DEBUG
+       {
+               static int rg;
+               int a, r;
+
+               if (!rg) {
+                       a = (val>> IOTLB_IAIG_SHIFT) & IOTLB_IAIG_MASK;
+                       r = (val>> IOTLB_IIRG_SHIFT) & IOTLB_IIRG_MASK;
+                       if (a != r) {
+                               printf("TLB Requested:%d Actual:%d\n", r, a);
+                               rg = 1;
+                       }
+               }
+       }
+#endif
+}
+
+void
+iommu_flush_ctx(struct iommu_softc *iommu, int mode, int did, int sid, int fm)
+{
+       uint64_t        val;
+       int             n;
+
+       if (iommu->gcmd & GCMD_QIE) {
+               struct qi_entry qi;
+
+               /* Use queued invalidation */
+               qi.hi = 0;
+               switch (mode) {
+               case CTX_GLOBAL:
+                       qi.lo = QI_CTX | QI_CTX_IG_GLOBAL;
+                       break;
+               case CTX_DOMAIN:
+                       qi.lo = QI_CTX | QI_CTX_IG_DOMAIN | QI_CTX_DID(did);
+                       break;
+               case CTX_DEVICE:
+                       qi.lo = QI_CTX | QI_CTX_IG_DEVICE | QI_CTX_DID(did) |
+                           QI_CTX_SID(sid) | QI_CTX_FM(fm);
+                       break;
+               }
+               return;
+       }
+
+       val = CCMD_ICC;
+       switch (mode) {
+       case CTX_GLOBAL:
+               val |= CIG_GLOBAL;
+               break;
+       case CTX_DOMAIN:
+               val |= CIG_DOMAIN | CCMD_DID(did);
+               break;
+       case CTX_DEVICE:
+               val |= CIG_DEVICE | CCMD_DID(did) |
+                   CCMD_SID(sid) | CCMD_FM(fm);
+               break;
+       }
+
+       mtx_enter(&iommu->reg_lock);
+
+       n = 0;
+       iommu_writeq(iommu, DMAR_CCMD_REG, val);
+       do {
+               val = iommu_readq(iommu, DMAR_CCMD_REG);
+       } while (n++ < 5 && val & CCMD_ICC);
+
+       mtx_leave(&iommu->reg_lock);
+
+#ifdef DEBUG
+       {
+               static int rg;
+               int a, r;
+
+               if (!rg) {
+                       a = (val>> CCMD_CAIG_SHIFT) & CCMD_CAIG_MASK;
+                       r = (val>> CCMD_CIRG_SHIFT) & CCMD_CIRG_MASK;
+                       if (a != r) {
+                               printf("CTX Requested:%d Actual:%d\n", r, a);
+                               rg = 1;
+                       }
+               }
+       }
+#endif
+}
+
+void
+iommu_enable_qi(struct iommu_softc *iommu, int enable)
+{
+       int     n = 0;
+       int     sts;
+
+       if (!(iommu->ecap & ECAP_QI))
+               return;
+
+       if (enable) {
+               iommu->gcmd |= GCMD_QIE;
+
+               mtx_enter(&iommu->reg_lock);
+
+               iommu_writel(iommu, DMAR_GCMD_REG, iommu->gcmd);
+               do {
+                       sts = iommu_readl(iommu, DMAR_GSTS_REG);
+               } while (n++ < 5 && !(sts & GSTS_QIES));
+
+               mtx_leave(&iommu->reg_lock);
+
+               printf("set.qie: %d\n", n);
+       } else {
+               iommu->gcmd &= ~GCMD_QIE;
+
+               mtx_enter(&iommu->reg_lock);
+
+               iommu_writel(iommu, DMAR_GCMD_REG, iommu->gcmd);
+               do {
+                       sts = iommu_readl(iommu, DMAR_GSTS_REG);
+               } while (n++ < 5 && sts & GSTS_QIES);
+
+               mtx_leave(&iommu->reg_lock);
+
+               printf("clr.qie: %d\n", n);
+       }
+}
+
+int
+iommu_enable_translation(struct iommu_softc *iommu, int enable)
+{
+       uint32_t        sts;
+       uint64_t        reg;
+       int             n = 0;
+
+       reg = 0;
+       if (enable) {
+               printf("enable iommu %d\n", iommu->id);
+               iommu_showcfg(iommu);
+
+               iommu->gcmd |= GCMD_TE;
+
+               /* Enable translation */
+               printf(" pre tes: ");
+
+               mtx_enter(&iommu->reg_lock);
+
+               iommu_writel(iommu, DMAR_GCMD_REG, iommu->gcmd);
+               printf("xxx");
+               do {
+                       printf("yyy");
+                       sts = iommu_readl(iommu, DMAR_GSTS_REG);
+               } while (n++ < 5 && !(sts & GSTS_TES));
+
+               mtx_leave(&iommu->reg_lock);
+
+               printf(" set.tes: %d\n", n);
+
+               if (n>= 5) {
+                       printf("error.. unable to initialize iommu %d\n",
+                           iommu->id);
+                       iommu->flags |= IOMMU_FLAGS_BAD;
+
+                       /* Disable IOMMU */
+                       iommu->gcmd &= ~GCMD_TE;
+                       mtx_enter(&iommu->reg_lock);
+                       iommu_writel(iommu, DMAR_GCMD_REG, iommu->gcmd);
+                       mtx_leave(&iommu->reg_lock);
+
+                       return (1);
+               }
+
+               iommu_flush_ctx(iommu, CTX_GLOBAL, 0, 0, 0);
+               iommu_flush_tlb(iommu, IOTLB_GLOBAL, 0);
+       } else {
+               iommu->gcmd &= ~GCMD_TE;
+
+               mtx_enter(&iommu->reg_lock);
+
+               iommu_writel(iommu, DMAR_GCMD_REG, iommu->gcmd);
+               do {
+                       sts = iommu_readl(iommu, DMAR_GSTS_REG);
+               } while (n++ < 5 && sts & GSTS_TES);
+
+               mtx_leave(&iommu->reg_lock);
+
+               printf(" clr.tes: %d\n", n);
+       }
+
+       return (0);
+}
+
+int
+iommu_init(struct acpidmar_softc *sc, struct iommu_softc *iommu,
+    struct acpidmar_drhd *dh)
+{
+       static int      niommu;
+       int             len = VTD_PAGE_SIZE;
+       int             i, gaw;
+       uint32_t        sts;
+       paddr_t         paddr;
+
+       if (_bus_space_map(sc->sc_memt, dh->address, len, 0, &iommu->ioh) != 0) 
{
+               return (-1);
+       }
+
+       TAILQ_INIT(&iommu->domains);
+       iommu->id = ++niommu;
+       iommu->flags = dh->flags;
+       iommu->segment = dh->segment;
+       iommu->iot = sc->sc_memt;
+
+       iommu->cap = iommu_readq(iommu, DMAR_CAP_REG);
+       iommu->ecap = iommu_readq(iommu, DMAR_ECAP_REG);
+       iommu->ndoms = cap_nd(iommu->cap);
+
+       mtx_init(&iommu->reg_lock, IPL_HIGH);
+
+       /* Clear Interrupt Masking */
+       iommu->intr = acpidmar_intr_establish(iommu, IPL_BIO, acpidmar_intr,
+           iommu, "dmarintr");
+
+       /* Allocate root pointer */
+       iommu->root = iommu_alloc_page(iommu, &paddr);
+#ifdef DEBUG
+       printf("Allocated root pointer: pa:%.16llx va:%p\n",
+           (uint64_t)paddr, iommu->root);
+#endif
+       iommu->rtaddr = paddr;
+       i = 0;
+       iommu_writeq(iommu, DMAR_RTADDR_REG, paddr);
+       iommu_writel(iommu, DMAR_GCMD_REG, iommu->gcmd | GCMD_SRTP);
+       do {
+               /* XXX shoule we sleep here? */
+               sts = iommu_readl(iommu, DMAR_GSTS_REG);
+       } while (++i < 5 && !(sts & GSTS_RTPS));
+       dprintf("set.rtp = %d\n", i);
+
+       /* Disable interrupts */
+       sts = iommu_readl(iommu, DMAR_FECTL_REG);
+       iommu_writel(iommu, DMAR_FECTL_REG, sts | FECTL_IM);
+#if 0
+       if (iommu->ecap & ECAP_QI) {
+               /* Queued Invalidation support */
+               iommu->qi = iommu_alloc_page(iommu, &iommu->qip);
+               iommu_writeq(iommu, DMAR_IQT_REG, 0);
+               iommu_writeq(iommu, DMAR_IQA_REG, iommu->qip | IQA_QS_256);
+       }
+       if (iommu->ecap & ECAP_IR) {
+               /* Interrupt remapping support */
+               iommu_writeq(iommu, DMAR_IRTA_REG, 0);
+       }
+#endif
+
+#if 0
+       printf("  pt:%d c:%d dt:%d zlr:%d dwd:%d drd:%d cm:%d domains:%d ",
+           !!(iommu->ecap & ECAP_PT),   // passthrough
+           !!(iommu->ecap & ECAP_C),    // coherent
+           !!(iommu->ecap & ECAP_DT),   // device iotlb
+           !!(iommu->cap & CAP_ZLR),    // zero-length read
+           !!(iommu->cap & CAP_DWD),    // write draining
+           !!(iommu->cap & CAP_DRD),    // read draining
+           !!(iommu->cap & CAP_CM),     // caching mode
+           iommu->ndoms);
+#endif
+       gaw = -1;
+       iommu->mgaw = cap_mgaw(iommu->cap);
+       for (i = 0; i < 5; i++) {
+               if (cap_sagaw(iommu->cap) & (1L << i)) {
+                       gaw = (i * VTD_STRIDE_SIZE) + 30;
+                       iommu->agaw = gaw;
+               }
+       }
+
+       sts = iommu_readl(iommu, DMAR_GSTS_REG);
+       if (sts & GSTS_TES)
+               iommu->gcmd |= GCMD_TE;
+       if (sts & GSTS_QIES)
+               iommu->gcmd |= GCMD_QIE;
+       if (sts & GSTS_IRES)
+               iommu->gcmd |= GCMD_IRE;
+
+       return (0);
+}
+
+const char *dmar_rn(int reg);
+
+const char *
+dmar_rn(int reg)
+{
+       switch (reg) {
+       case DMAR_VER_REG: return "ver";
+       case DMAR_CAP_REG: return "cap";
+       case DMAR_ECAP_REG: return "ecap";
+       case DMAR_GSTS_REG: return "gsts";
+       case DMAR_GCMD_REG: return "gcmd";
+       case DMAR_FSTS_REG: return "fsts";
+       case DMAR_FECTL_REG: return "fectl";
+       case DMAR_RTADDR_REG: return "rtaddr";
+       case DMAR_FEDATA_REG: return "fedata";
+       case DMAR_FEADDR_REG: return "feaddr";
+       case DMAR_FEUADDR_REG: return "feuaddr";
+       case DMAR_PMEN_REG: return "pmen";
+       case DMAR_IEDATA_REG: return "iedata";
+       case DMAR_IEADDR_REG: return "ieaddr";
+       case DMAR_IEUADDR_REG: return "ieuaddr";
+       case DMAR_IRTA_REG: return "irta";
+       case DMAR_CCMD_REG: return "ccmd";
+       case DMAR_IQH_REG: return "iqh";
+       case DMAR_IQT_REG: return "iqt";
+       case DMAR_IQA_REG: return "iqa";
+       }
+       return "unknown";
+}
+
+/* Read/Write IOMMU register */
+uint32_t
+iommu_readl(struct iommu_softc *iommu, int reg)
+{
+       uint32_t        v;
+
+       v = bus_space_read_4(iommu->iot, iommu->ioh, reg);
+       if (reg < 00) {
+               printf("iommu%d: read %x %.8x [%s]\n",
+                   iommu->id, reg, v, dmar_rn(reg));
+       }
+
+       return (v);
+}
+
+uint64_t
+iommu_readq(struct iommu_softc *iommu, int reg)
+{
+       uint64_t        v;
+
+       v = bus_space_read_8(iommu->iot, iommu->ioh, reg);
+       if (reg < 00) {
+               printf("iommu%d: read %x %.16llx [%s]\n",
+                   iommu->id, reg, v, dmar_rn(reg));
+       }
+
+       return (v);
+}
+
+void
+iommu_writel(struct iommu_softc *iommu, int reg, uint32_t v)
+{
+       if (reg < 00) {
+               printf("iommu%d: write %x %.8x [%s]\n",
+                   iommu->id, reg, v, dmar_rn(reg));
+       }
+       bus_space_write_4(iommu->iot, iommu->ioh, reg, v);
+}
+
+void
+iommu_writeq(struct iommu_softc *iommu, int reg, uint64_t v)
+{
+       if (reg < 00) {
+               printf("iommu%d: write %x %.16llx [%s]\n",
+                   iommu->id, reg, v, dmar_rn(reg));
+       }
+       bus_space_write_8(iommu->iot, iommu->ioh, reg, v);
+}
+
+/* Check if a device is within a device scope */
+int
+acpidmar_match_devscope(struct devlist_head *devlist, pci_chipset_tag_t pc,
+    int sid)
+{
+       struct dmar_devlist     *ds;
+       int                     sub, sec, i;
+       int                     bus, dev, fun, sbus;
+       pcireg_t                reg;
+       pcitag_t                tag;
+
+       sbus = sid_bus(sid);
+       TAILQ_FOREACH(ds, devlist, link) {
+               bus = ds->bus;
+               dev = ds->dp[0].device;
+               fun = ds->dp[0].function;
+               /* Walk PCI bridges in path */
+               for (i = 1; i < ds->ndp; i++) {
+                       tag = pci_make_tag(pc, bus, dev, fun);
+                       reg = pci_conf_read(pc, tag, PPB_REG_BUSINFO);
+                       bus = PPB_BUSINFO_SECONDARY(reg);
+                       dev = ds->dp[i].device;
+                       fun = ds->dp[i].function;
+               }
+
+               /* Check for device exact match */
+               if (sid == mksid(bus, dev, fun)) {
+                       return DMAR_ENDPOINT;
+               }
+
+               /* Check for device subtree match */
+               if (ds->type == DMAR_BRIDGE) {
+                       tag = pci_make_tag(pc, bus, dev, fun);
+                       reg = pci_conf_read(pc, tag, PPB_REG_BUSINFO);
+                       sec = PPB_BUSINFO_SECONDARY(reg);
+                       sub = PPB_BUSINFO_SUBORDINATE(reg);
+                       if (sec <= sbus && sbus <= sub) {
+                               return DMAR_BRIDGE;
+                       }
+               }
+       }
+
+       return (0);
+}
+
+struct domain *
+domain_create(struct iommu_softc *iommu, int did)
+{
+       struct domain   *dom;
+
+       dom = malloc(sizeof(*dom), M_DEVBUF, M_ZERO | M_WAITOK);
+       dom->did = did;
+       dom->iommu = iommu;
+       dom->pte = iommu_alloc_page(iommu, &dom->ptep);
+       TAILQ_INIT(&dom->devices);
+
+       /* Setup DMA */
+       dom->dmat._cookie = dom;
+       dom->dmat._dmamap_create    = dmar_dmamap_create;
+       dom->dmat._dmamap_destroy   = dmar_dmamap_destroy;
+       dom->dmat._dmamap_load      = dmar_dmamap_load;
+       dom->dmat._dmamap_load_mbuf = dmar_dmamap_load_mbuf;
+       dom->dmat._dmamap_load_uio  = dmar_dmamap_load_uio;
+       dom->dmat._dmamap_load_raw  = dmar_dmamap_load_raw;
+       dom->dmat._dmamap_unload    = dmar_dmamap_unload;
+       dom->dmat._dmamap_sync      = dmar_dmamap_sync;
+       dom->dmat._dmamem_alloc     = dmar_dmamem_alloc;
+       dom->dmat._dmamem_free      = dmar_dmamem_free;
+       dom->dmat._dmamem_map       = dmar_dmamem_map;
+       dom->dmat._dmamem_unmap     = dmar_dmamem_unmap;
+       dom->dmat._dmamem_mmap      = dmar_dmamem_mmap;
+
+       snprintf(dom->exname, sizeof(dom->exname), "did:%x.%.4x",
+           iommu->id, dom->did);
+
+       /* Setup IOMMU address map */
+       dom->iovamap = extent_create(dom->exname, 1024*1024*16,
+           (1LL << iommu->agaw)-1,
+           M_DEVBUF, NULL, 0,
+           EX_WAITOK|EX_NOCOALESCE);
+
+       /* Zero out Interrupt region */
+       extent_alloc_region(dom->iovamap, 0xFEE00000L, 0x100000,
+           EX_WAITOK);
+
+       TAILQ_INSERT_TAIL(&iommu->domains, dom, link);
+
+       return dom;
+}
+
+/* Lookup domain by segment & source id (bus.device.function) */
+struct domain *
+domain_lookup(struct acpidmar_softc *sc, int segment, int sid)
+{
+       struct iommu_softc      *iommu;
+       struct domain           *dom;
+       struct domain_dev       *ddev;
+       int                     rc;
+
+       if (sc == NULL) {
+               return NULL;
+       }
+
+       /* Lookup IOMMU for this device */
+       TAILQ_FOREACH(iommu, &sc->sc_drhds, link) {
+               if (iommu->segment != segment)
+                       continue;
+               /* Check for devscope match or catchall iommu */
+               rc=acpidmar_match_devscope(&iommu->devices, sc->sc_pc, sid);
+               if (rc != 0 || iommu->flags) {
+                       break;
+               }
+       }
+       if (!iommu) {
+               printf("%s: no iommu found\n", dmar_bdf(sid));
+               return NULL;
+       }
+       /* Search domain devices */
+       TAILQ_FOREACH(dom, &iommu->domains, link) {
+               TAILQ_FOREACH(ddev, &dom->devices, link) {
+                       /* XXX: match all functions? */
+                       if (ddev->sid == sid) {
+                               return dom;
+                       }
+               }
+       }
+       if (iommu->ndoms <= 2) {
+               /* Running out of domains.. create catchall domain */
+               if (!iommu->unity) {
+                       iommu->unity = domain_create(iommu, 1);
+               }
+               dom = iommu->unity;
+       } else {
+               dom = domain_create(iommu, --iommu->ndoms);
+       }
+       if (!dom) {
+               printf("no domain here\n");
+               return NULL;
+       }
+
+       /* Add device to domain */
+       ddev = malloc(sizeof(*ddev), M_DEVBUF, M_ZERO | M_WAITOK);
+       ddev->sid = sid;
+       TAILQ_INSERT_TAIL(&dom->devices, ddev, link);
+
+       printf("pci bus %d device %d function %d is iommu %d domain %.4x\n",
+           sid_bus(sid), sid_dev(sid), sid_fun(sid),
+           iommu->id, dom->did);
+
+       return dom;
+}
+
+struct domain *
+acpidmar_pci_attach(struct acpidmar_softc *sc, int segment, int sid)
+{
+       static struct domain    *dom;
+       struct iommu_softc      *iommu;
+       struct context_entry    *ctx;
+       paddr_t                 paddr;
+       int                     bus, devfn;
+       int                     tt, lvl;
+
+       bus   = sid_bus(sid);
+       devfn = sid_devfn(sid);
+
+       dom = domain_lookup(sc, segment, sid);
+       if (!dom) {
+               printf("no domain: %s\n", dmar_bdf(sid));
+               return NULL;
+       }
+       iommu = dom->iommu;
+
+       /* Create Bus mapping */
+       if (!root_entry_is_valid(&iommu->root[bus])) {
+               iommu->ctx[bus] = iommu_alloc_page(iommu, &paddr);
+               iommu->root[bus].lo = paddr | ROOT_P;
+               iommu_flush_cache(iommu, &iommu->root[bus],
+                   sizeof(struct root_entry));
+               dprintf("iommu%d: Allocate context for bus: %.2x pa:%.16llx 
va:%p\n", 
+                   iommu->id, bus, (uint64_t)paddr,
+                   iommu->ctx[bus]);
+       }
+
+       /* Create DevFn mapping */
+       ctx = iommu->ctx[bus] + devfn;
+       if (!context_entry_is_valid(ctx)) {
+               tt = CTX_T_MULTI;
+               lvl = (iommu->agaw - 30)/VTD_STRIDE_SIZE;
+
+               dprintf("pci: %s\n", dom_bdf(dom));
+
+               /* Initialize context */
+               context_set_slpte(ctx, dom->ptep);
+               context_set_translation_type(ctx, tt);
+               context_set_domain_id(ctx, dom->did);
+               context_set_address_width(ctx, lvl);
+               context_set_present(ctx);
+
+               /* Flush it */
+               iommu_flush_cache(iommu, ctx, sizeof(struct context_entry));
+               iommu_flush_ctx(iommu, CTX_DEVICE, dom->did, sid, 0);
+               iommu_flush_tlb(iommu, IOTLB_GLOBAL, 0);
+               dprintf("iommu%d: %s set context ptep:%.16llx lvl:%d did:%.4x 
tt:%d\n", 
+                   iommu->id, dmar_bdf(sid), (uint64_t)dom->ptep, lvl,
+                   dom->did, tt);
+       }
+       return dom;
+}
+
+void
+acpidmar_pci_hook(pci_chipset_tag_t pc, struct pci_attach_args *pa)
+{
+       int             bus, dev, fun;
+       struct domain   *dom;
+       pcireg_t        reg;
+
+       if (!acpidmar_sc) {
+               /* No DMAR, ignore */
+               return;
+       }
+       pci_decompose_tag(pc, pa->pa_tag, &bus, &dev, &fun);
+       reg = pci_conf_read(pc, pa->pa_tag, PCI_CLASS_REG);
+       if (PCI_CLASS(reg) == PCI_CLASS_DISPLAY &&
+           PCI_SUBCLASS(reg) == PCI_SUBCLASS_DISPLAY_VGA) {
+               printf("dmar: %.4x:%.2x:%.2x.%x is VGA, ignoring\n",
+                   pa->pa_domain, bus, dev, fun);
+               return;
+       }
+
+       /* Add device to domain */
+       dom = acpidmar_pci_attach(acpidmar_sc, pa->pa_domain,
+           mksid(bus, dev, fun));
+
+       if (dom != NULL && !iommu_bad(dom->iommu))   {
+               /* Change DMA tag */
+               pa->pa_dmat = &dom->dmat;
+       }
+}
+
+/* Create list of device scope entries from ACPI table */
+void
+acpidmar_parse_devscope(union acpidmar_entry *de, int off, int segment,
+    struct devlist_head *devlist)
+{
+       struct acpidmar_devscope        *ds;
+       struct dmar_devlist             *d;
+       int                             dplen, i;
+
+       TAILQ_INIT(devlist);
+       while (off < de->length) {
+               ds = (struct acpidmar_devscope *)((unsigned char *)de + off);
+               off += ds->length;
+
+               /* We only care about bridges and endpoints */
+               if (ds->type != DMAR_ENDPOINT && ds->type != DMAR_BRIDGE)
+                       continue;
+
+               dplen = ds->length - 6; /* XXX make this a #define */
+               d = malloc(sizeof(*d) + dplen, M_DEVBUF, M_ZERO | M_WAITOK);
+               d->bus  = ds->bus;
+               d->type = ds->type;
+               d->ndp  = dplen / 2;
+               d->dp   = (void *)&d[1];
+               memcpy(d->dp, &ds[1], dplen);
+               TAILQ_INSERT_TAIL(devlist, d, link);
+
+               printf("  devscope: %8s  %.4x:%.2x.%.2x.%x {",
+                   ds->type == DMAR_BRIDGE ? "bridge" : "endpoint",
+                   segment, ds->bus,
+                   d->dp[0].device,
+                   d->dp[0].function);
+
+               for (i = 1; i < d->ndp; i++) {
+                       printf(" %2x.%x ",
+                           d->dp[i].device,
+                           d->dp[i].function);
+               }
+               printf("}\n");
+       }
+}
+
+/* DMA Remapping Hardware Unit */
+void
+acpidmar_drhd(struct acpidmar_softc *sc, union acpidmar_entry *de)
+{
+       struct iommu_softc      *iommu;
+
+       printf("DRHD: segment:%.4x base:%.16llx flags:%.2x\n",
+           de->drhd.segment,
+           de->drhd.address,
+           de->drhd.flags);
+       iommu = malloc(sizeof(*iommu), M_DEVBUF, M_ZERO | M_WAITOK);
+       acpidmar_parse_devscope(de, sizeof(de->drhd), de->drhd.segment,
+           &iommu->devices);
+       iommu_init(sc, iommu, &de->drhd);
+
+       if (de->drhd.flags) {
+               /* Catchall IOMMU goes at end of list */
+               TAILQ_INSERT_TAIL(&sc->sc_drhds, iommu, link);
+       } else {
+               TAILQ_INSERT_HEAD(&sc->sc_drhds, iommu, link);
+       }
+}
+
+/* Reserved Memory Region Reporting */
+void
+acpidmar_rmrr(struct acpidmar_softc *sc, union acpidmar_entry *de)
+{
+       struct rmrr_softc       *rmrr;
+       bios_memmap_t           *im;
+
+       printf("RMRR: segment:%.4x range:%.16llx-%.16llx\n",
+           de->rmrr.segment, de->rmrr.base, de->rmrr.limit);
+       if (de->rmrr.limit <= de->rmrr.base) {
+               printf("  buggy BIOS\n");
+               return;
+       }
+
+       rmrr = malloc(sizeof(*rmrr), M_DEVBUF, M_ZERO | M_WAITOK);
+       rmrr->start = trunc_page(de->rmrr.base);
+       rmrr->end = round_page(de->rmrr.limit);
+       rmrr->segment = de->rmrr.segment;
+       acpidmar_parse_devscope(de, sizeof(de->rmrr), de->rmrr.segment,
+           &rmrr->devices);
+
+       for (im = bios_memmap; im->type != BIOS_MAP_END; im++) {
+               if (im->type != BIOS_MAP_RES)
+                       continue;
+               if (rmrr->start>= im->addr &&
+                   rmrr->end <= im->addr+im->size) {
+                       /* Bah.. some buggy BIOS stomp outside RMRR */
+                       printf("  ** inside E820 Reserved %.16llx %.16llx\n",
+                           im->addr, im->addr+im->size);
+                       rmrr->start = trunc_page(im->addr);
+                       rmrr->end   = round_page(im->addr + im->size);
+               }
+       }
+       TAILQ_INSERT_TAIL(&sc->sc_rmrrs, rmrr, link);
+}
+
+/* Root Port ATS Reporting */
+void
+acpidmar_atsr(struct acpidmar_softc *sc, union acpidmar_entry *de)
+{
+       struct atsr_softc *atsr;
+
+       printf("ATSR: segment:%.4x flags:%x\n",
+           de->atsr.segment,
+           de->atsr.flags);
+
+       atsr = malloc(sizeof(*atsr), M_DEVBUF, M_ZERO | M_WAITOK);
+       atsr->flags = de->atsr.flags;
+       atsr->segment = de->atsr.segment;
+       acpidmar_parse_devscope(de, sizeof(de->atsr), de->atsr.segment,
+           &atsr->devices);
+
+       TAILQ_INSERT_TAIL(&sc->sc_atsrs, atsr, link);
+}
+
+void
+acpidmar_init(struct acpidmar_softc *sc, struct acpi_dmar *dmar)
+{
+       struct rmrr_softc       *rmrr;
+       struct iommu_softc      *iommu;
+       struct domain           *dom;
+       struct dmar_devlist     *dl;
+       union acpidmar_entry    *de;
+       int                     off, sid, rc;
+
+       printf(": hardware width: %d, intr_remap:%d x2apic_opt_out:%d\n",
+           dmar->haw+1,
+           !!(dmar->flags & 0x1),
+           !!(dmar->flags & 0x2));
+       sc->sc_haw = dmar->haw+1;
+       sc->sc_flags = dmar->flags;
+
+       TAILQ_INIT(&sc->sc_drhds);
+       TAILQ_INIT(&sc->sc_rmrrs);
+       TAILQ_INIT(&sc->sc_atsrs);
+
+       off = sizeof(*dmar);
+       while (off < dmar->hdr.length) {
+               de = (union acpidmar_entry *)((unsigned char *)dmar + off);
+               switch (de->type) {
+               case DMAR_DRHD:
+                       acpidmar_drhd(sc, de);
+                       break;
+               case DMAR_RMRR:
+                       acpidmar_rmrr(sc, de);
+                       break;
+               case DMAR_ATSR:
+                       acpidmar_atsr(sc, de);
+                       break;
+               default:
+                       printf("DMAR: unknown %x\n", de->type);
+                       break;
+               }
+               off += de->length;
+       }
+
+       /* Map passthrough pages for RMRR */
+       TAILQ_FOREACH(rmrr, &sc->sc_rmrrs, link) {
+               TAILQ_FOREACH(dl, &rmrr->devices, link) {
+                       sid = mksid(dl->bus, dl->dp[0].device,
+                           dl->dp[0].function);
+                       dom = acpidmar_pci_attach(sc, rmrr->segment, sid);
+                       if (dom != NULL) {
+                               printf("%s map ident: %.16llx %.16llx\n",
+                                   dom_bdf(dom), rmrr->start, rmrr->end);
+                               domain_map_pthru(dom, rmrr->start, rmrr->end);
+                               rc = extent_alloc_region(dom->iovamap,
+                                   rmrr->start, rmrr->end, EX_WAITOK);
+                       }
+               }
+       }
+       TAILQ_FOREACH(iommu, &sc->sc_drhds, link) {
+               TAILQ_FOREACH(dl, &iommu->devices, link) {
+                       sid = mksid(dl->bus, dl->dp[0].device,
+                           dl->dp[0].function);
+                       acpidmar_pci_attach(sc, iommu->segment, sid);
+               }
+       }
+       printf("========\n");
+}
+
+int
+acpidmar_activate(struct device *self, int act)
+{
+       struct acpidmar_softc *sc = (struct acpidmar_softc *)self;
+       struct iommu_softc *iommu;
+       int i, sts;
+
+       printf("called acpidmar_activate %d %p\n", act, sc);
+
+       if (sc == NULL) {
+               return (0);
+       }
+
+       switch (act) {
+       case DVACT_RESUME:
+               TAILQ_FOREACH(iommu, &sc->sc_drhds, link) {
+                       i = 0;
+                       iommu_writeq(iommu, DMAR_RTADDR_REG, iommu->rtaddr);
+                       iommu_writel(iommu, DMAR_GCMD_REG, iommu->gcmd | 
GCMD_SRTP);
+                       do {
+                               /* XXX shoule we sleep here? */
+                               sts = iommu_readl(iommu, DMAR_GSTS_REG);
+                       } while (++i < 5 && !(sts & GSTS_RTPS));
+                       dprintf("set.rtp = %d\n", i);
+
+                       iommu_writel(iommu, DMAR_FEDATA_REG, iommu->fedata);
+                       iommu_writel(iommu, DMAR_FEADDR_REG, iommu->feaddr);
+                       iommu_writel(iommu, DMAR_FEUADDR_REG,
+                           iommu->feaddr>> 32);
+                       if ((iommu->flags & 
(IOMMU_FLAGS_BAD|IOMMU_FLAGS_SUSPEND)) ==
+                            IOMMU_FLAGS_SUSPEND) {
+                               printf("enable wakeup translation\n");
+                               iommu_enable_translation(iommu, 1);
+                       }
+                       iommu_showcfg(iommu);
+               }
+               break;
+       case DVACT_SUSPEND:
+               TAILQ_FOREACH(iommu, &sc->sc_drhds, link) {
+                       printf("iommu suspend\n");
+                       iommu->flags |= IOMMU_FLAGS_SUSPEND;
+                       iommu_enable_translation(iommu, 0);
+               }
+               break;
+       }
+       return (0);
+}
+
+int
+acpidmar_match(struct device *parent, void *match, void *aux)
+{
+       struct acpi_attach_args         *aaa = aux;
+       struct acpi_table_header        *hdr;
+
+       /* If we do not have a table, it is not us */
+       if (aaa->aaa_table == NULL)
+               return (0);
+
+       /* If it is an DMAR table, we can attach */
+       hdr = (struct acpi_table_header *)aaa->aaa_table;
+       if (memcmp(hdr->signature, DMAR_SIG, sizeof(DMAR_SIG) - 1) != 0)
+               return (0);
+
+       return (1);
+}
+
+void
+acpidmar_attach(struct device *parent, struct device *self, void *aux)
+{
+       struct acpidmar_softc   *sc = (void *)self;
+       struct acpi_attach_args *aaa = aux;
+       struct acpi_dmar        *dmar = (struct acpi_dmar *)aaa->aaa_table;
+
+       acpidmar_sc = sc;
+       sc->sc_memt = aaa->aaa_memt;
+       acpidmar_init(sc, dmar);
+}
+
+/* Interrupt shiz */
+void acpidmar_msi_hwmask(struct pic *, int);
+void acpidmar_msi_hwunmask(struct pic *, int);
+void acpidmar_msi_addroute(struct pic *, struct cpu_info *, int, int, int);
+void acpidmar_msi_delroute(struct pic *, struct cpu_info *, int, int, int);
+
+void
+acpidmar_msi_hwmask(struct pic *pic, int pin)
+{
+       struct iommu_pic        *ip = (void *)pic;
+       struct iommu_softc      *iommu = ip->iommu;
+
+       printf("msi_hwmask\n");
+
+       mtx_enter(&iommu->reg_lock);
+
+       iommu_writel(iommu, DMAR_FECTL_REG, FECTL_IM);
+       iommu_readl(iommu, DMAR_FECTL_REG);
+
+       mtx_leave(&iommu->reg_lock);
+}
+
+void
+acpidmar_msi_hwunmask(struct pic *pic, int pin)
+{
+       struct iommu_pic        *ip = (void *)pic;
+       struct iommu_softc      *iommu = ip->iommu;
+
+       printf("msi_hwunmask\n");
+
+       mtx_enter(&iommu->reg_lock);
+
+       iommu_writel(iommu, DMAR_FECTL_REG, 0);
+       iommu_readl(iommu, DMAR_FECTL_REG);
+
+       mtx_leave(&iommu->reg_lock);
+}
+
+void
+acpidmar_msi_addroute(struct pic *pic, struct cpu_info *ci, int pin, int vec,
+    int type)
+{
+       struct iommu_pic        *ip = (void *)pic;
+       struct iommu_softc      *iommu = ip->iommu;
+       uint32_t                sts;
+
+       mtx_enter(&iommu->reg_lock);
+
+       iommu->fedata = vec;
+       iommu->feaddr = 0xfee00000L | (ci->ci_apicid << 12);
+       iommu_writel(iommu,  DMAR_FEDATA_REG, vec);
+       iommu_writel(iommu, DMAR_FEADDR_REG, iommu->feaddr);
+       iommu_writel(iommu, DMAR_FEUADDR_REG, iommu->feaddr>> 32);
+       sts = iommu_readl(iommu, DMAR_FECTL_REG);
+       iommu_writel(iommu, DMAR_FECTL_REG, sts * ~FECTL_IM);
+
+       mtx_leave(&iommu->reg_lock);
+}
+
+void
+acpidmar_msi_delroute(struct pic *pic, struct cpu_info *ci, int pin, int vec,
+    int type)
+{
+       printf("msi_delroute\n");
+}
+
+void *
+acpidmar_intr_establish(void *ctx, int level, int (*func)(void *),
+    void *arg, const char *what)
+{
+       struct iommu_softc      *iommu = ctx;
+       struct pic              *pic;
+
+       pic = &iommu->pic.pic;
+       iommu->pic.iommu = iommu;
+
+       strlcpy(pic->pic_dev.dv_xname, "dmarpic",
+               sizeof(pic->pic_dev.dv_xname));
+       pic->pic_type = PIC_MSI;
+       pic->pic_hwmask = acpidmar_msi_hwmask;
+       pic->pic_hwunmask = acpidmar_msi_hwunmask;
+       pic->pic_addroute = acpidmar_msi_addroute;
+       pic->pic_delroute = acpidmar_msi_delroute;
+       pic->pic_edge_stubs = ioapic_edge_stubs;
+#ifdef MULTIPROCESSOR
+       mtx_init(&pic->pic_mutex, IPL_HIGH);
+#endif
+
+       return intr_establish(-1, pic, 0, IST_PULSE, level, func, arg, what);
+}
+
+int
+acpidmar_intr(void *ctx)
+{
+       struct iommu_softc              *iommu = ctx;
+       struct fault_entry              fe;
+       static struct fault_entry       ofe;
+       int                             fro, nfr, fri, i;
+       uint32_t                        sts;
+
+       mtx_enter(&iommu->reg_lock);
+       sts = iommu_readl(iommu, DMAR_FSTS_REG);
+
+       if (!(sts & FSTS_PPF)) {
+               mtx_leave(&iommu->reg_lock);
+               return (1);
+       }
+
+       nfr = cap_nfr(iommu->cap);
+       fro = cap_fro(iommu->cap);
+       fri = (sts>> FSTS_FRI_SHIFT) & FSTS_FRI_MASK;
+       for (i = 0; i < nfr; i++) {
+               fe.hi = iommu_readq(iommu, fro + (fri*16) + 8);
+               if (!(fe.hi & FRCD_HI_F))
+                       break;
+
+               fe.lo = iommu_readq(iommu, fro + (fri*16));
+               if (ofe.hi != fe.hi || ofe.lo != fe.lo) {
+                       iommu_showfault(iommu, fri, &fe);
+                       ofe.hi = fe.hi;
+                       ofe.lo = fe.lo;
+               }
+               fri = (fri + 1) % nfr;
+       }
+
+       iommu_writel(iommu, DMAR_FSTS_REG, FSTS_PFO | FSTS_PPF);
+       mtx_leave(&iommu->reg_lock);
+
+       return (1);
+}
+
+enum {
+       VTD_FAULT_ROOT_P = 0x1,         /* P field in root entry is 0 */
+       VTD_FAULT_CTX_P = 0x2,          /* P field in context entry is 0 */
+       VTD_FAULT_CTX_INVAL = 0x3,      /* context AW/TT/SLPPTR invalid */
+       VTD_FAULT_LIMIT = 0x4,          /* Address is outside of MGAW */
+       VTD_FAULT_WRITE = 0x5,          /* Address-translation fault, 
non-writable */
+       VTD_FAULT_READ = 0x6,           /* Address-translation fault, 
non-readable */
+       VTD_FAULT_PTE_INVAL = 0x7,      /* page table hw access error */
+       VTD_FAULT_ROOT_INVAL = 0x8,     /* root table hw access error */
+       VTD_FAULT_CTX_TBL_INVAL = 0x9,  /* context entry hw access error */
+       VTD_FAULT_ROOT_RESERVED = 0xa,  /* non-zero reserved field in root 
entry */ 
+       VTD_FAULT_CTX_RESERVED = 0xb,   /* non-zero reserved field in context 
entry */
+       VTD_FAULT_PTE_RESERVED = 0xc,   /* non-zero reserved field in paging 
entry */
+       VTD_FAULT_CTX_TT = 0xd,         /* invalid translation type */
+};
+
+const char *vtd_faults[] = {
+       "Software",
+       "Root Entry Not Present",
+       "Context Entry Not Present",
+       "Context Entry Invalid",
+       "Address Beyond MGAW",
+       "Write",
+       "Read",
+       "Paging Entry Invalid",
+       "Root Table Invalid",
+       "Context Table Invalid",
+       "Root Entry Reserved",
+       "Context Entry Reserved",
+       "Paging Entry Reserved",
+       "Context Entry TT",
+       "Reserved",
+};
+
+void
+iommu_showcfg(struct iommu_softc *iommu)
+{
+       int i, j, sts, cmd;
+
+       cmd = iommu_readl(iommu, DMAR_GCMD_REG);
+       sts = iommu_readl(iommu, DMAR_GSTS_REG);
+       printf("iommu%d: flags:%d root pa:%.16llx %s %s %s %.8x %.8x\n",
+           iommu->id, iommu->flags, iommu_readq(iommu, DMAR_RTADDR_REG),
+           sts & GSTS_TES ? "enabled" : "disabled",
+           sts & GSTS_QIES ? "qi" : "ccmd",
+           sts & GSTS_IRES ? "ir" : "",
+           cmd, sts);
+       for (i = 0; i < 256; i++) {
+               if (!root_entry_is_valid(&iommu->root[i])) {
+                       continue;
+               }
+#if 0
+               printf("iommu%d: bus %.2x %.16llx\n",
+                   iommu->id, i, iommu->root[i].lo & ~0xFFF);
+#endif
+               for (j = 0; j < 256; j++) {
+                       struct context_entry *ctx;
+
+                       ctx = iommu->ctx[i] + j;
+                       if (!context_entry_is_valid(ctx)) {
+                               continue;
+                       }
+                       printf("  %.2x:%.2x.%x lvl:%d did:%.4x tt:%d 
ptep:%.16llx\n",
+                           i, (j>> 3), j & 7,
+                           context_address_width(ctx),
+                           context_domain_id(ctx),
+                           context_translation_type(ctx),
+                           context_pte(ctx));
+               }
+       }
+}
+
+void
+iommu_showfault(struct iommu_softc *iommu, int fri, struct fault_entry *fe)
+{
+       int bus, dev, fun, type, fr;
+
+       if (!(fe->hi & FRCD_HI_F))
+               return;
+       type = (fe->hi & FRCD_HI_T) ? 'r' : 'w';
+       fr = (fe->hi>> FRCD_HI_FR_SHIFT) & FRCD_HI_FR_MASK;
+       bus = (fe->hi>> FRCD_HI_BUS_SHIFT) & FRCD_HI_BUS_MASK;
+       dev = (fe->hi>> FRCD_HI_DEV_SHIFT) & FRCD_HI_DEV_MASK;
+       fun = (fe->hi>> FRCD_HI_FUN_SHIFT) & FRCD_HI_FUN_MASK;
+       printf("fri%d: dmar: %.2x:%.2x.%x %s error at %llx fr:%d [%s] 
iommu:%d\n",
+           fri, bus, dev, fun, 
+           type == 'r' ? "read" : "write",
+           fe->lo,
+           fr, fr <= 13 ? vtd_faults[fr] : "unknown",
+           iommu->id);
+       panic("error");
+}
Index: dev/acpi/acpidmar.h
===================================================================
RCS file: dev/acpi/acpidmar.h
diff -N dev/acpi/acpidmar.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ dev/acpi/acpidmar.h 22 Jul 2015 20:25:36 -0000
@@ -0,0 +1,447 @@
+/*
+ * Copyright (c) 2015 Jordan Hargrave <jordan_hargr...@hotmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _DEV_ACPI_DMARREG_H_
+#define _DEV_ACPI_DMARREG_H_
+
+#define VTD_STRIDE_MASK 0x1FF
+#define VTD_STRIDE_SIZE 9
+#define VTD_PAGE_SIZE   4096
+
+#define _xbit(x,y) (((x)>> (y)) & 1)
+#define _xfld(x,y) (uint32_t)(((x)>> y##_SHIFT) & y##_MASK)
+
+#define DMAR_VER_REG           0x00    /* 32:Arch version supported by this 
IOMMU */
+#define DMAR_RTADDR_REG                0x20    /* 64:Root entry table */
+#define DMAR_FEDATA_REG                0x3c    /* 32:Fault event interrupt 
data register */
+#define DMAR_FEADDR_REG                0x40    /* 32:Fault event interrupt 
addr register */
+#define DMAR_FEUADDR_REG       0x44    /* 32:Upper address register */
+#define DMAR_AFLOG_REG         0x58    /* 64:Advanced Fault control */
+#define DMAR_PMEN_REG          0x64    /* 32:Enable Protected Memory Region */
+#define DMAR_PLMBASE_REG       0x68    /* 32:PMRR Low addr */
+#define DMAR_PLMLIMIT_REG      0x6c    /* 32:PMRR low limit */
+#define DMAR_PHMBASE_REG       0x70    /* 64:pmrr high base addr */
+#define DMAR_PHMLIMIT_REG      0x78    /* 64:pmrr high limit */
+#define DMAR_ICS_REG           0x9C    /* 32:Invalidation complete status 
register */
+#define DMAR_IECTL_REG         0xa0    /* 32:Invalidation event control 
register */
+#define DMAR_IEDATA_REG                0xa4    /* 32:Invalidation event data 
register */
+#define DMAR_IEADDR_REG                0xa8    /* 32:Invalidation event 
address register */
+#define DMAR_IEUADDR_REG       0xac    /* 32:Invalidation event upper address 
register */
+#define DMAR_IRTA_REG          0xb8    /* 64:Interrupt remapping table addr 
register */
+#define DMAR_CAP_REG           0x08    /* 64:Hardware supported capabilities */
+
+#define   CAP_PI               (1LL << 59)
+#define   CAP_FL1GP            (1LL << 56)
+#define   CAP_DRD              (1LL << 55)
+#define   CAP_DWD              (1LL << 54)
+#define   CAP_MAMV_MASK                0x3F
+#define   CAP_MAMV_SHIFT       48LL
+#define   cap_mamv(x)          _xfld(x,CAP_MAMV)
+#define   CAP_NFR_MASK         0xFF
+#define   CAP_NFR_SHIFT                40LL
+#define   cap_nfr(x)           (_xfld(x,CAP_NFR) + 1)
+#define   CAP_PSI              (1LL << 39)
+#define   CAP_SLLPS_MASK       0xF
+#define   CAP_SLLPS_SHIFT      34LL
+#define   cap_sllps(x)         _xfld(x,CAP_SLLPS)
+#define   CAP_FRO_MASK         0x3FF
+#define   CAP_FRO_SHIFT                24LL
+#define   cap_fro(x)           (_xfld(x,CAP_FRO) * 16)
+#define   CAP_ZLR              (1LL << 22)
+#define   CAP_MGAW_MASK                0x3F
+#define   CAP_MGAW_SHIFT       16LL
+#define   cap_mgaw(x)          (_xfld(x,CAP_MGAW) + 1)
+#define   CAP_SAGAW_MASK       0x1F
+#define   CAP_SAGAW_SHIFT      8LL
+#define   cap_sagaw(x)         _xfld(x,CAP_SAGAW) 
+#define   CAP_CM               (1LL << 7)
+#define   CAP_PHMR             (1LL << 6)
+#define   CAP_PLMR             (1LL << 5)
+#define   CAP_RWBF             (1LL << 4)
+#define   CAP_AFL              (1LL << 3)
+#define   CAP_ND_MASK          0x7
+#define   CAP_ND_SHIFT         0x00
+#define   cap_nd(x)            (16 << (((x) & CAP_ND_MASK) << 1))
+
+#define DMAR_ECAP_REG          0x10    /* 64:Extended capabilities supported */
+#define   ECAP_PSS_MASK                0x1F
+#define   ECAP_PSS_SHIFT       35
+#define   ECAP_EAFS            (1LL << 34)
+#define   ECAP_NWFS            (1LL << 33)
+#define   ECAP_SRS             (1LL << 31)
+#define   ECAP_ERS             (1LL << 30)
+#define   ECAP_PRS             (1LL << 29)
+#define   ECAP_PASID           (1LL << 28)
+#define   ECAP_DIS             (1LL << 27)
+#define   ECAP_NEST            (1LL << 26)
+#define   ECAP_MTS             (1LL << 25)
+#define   ECAP_ECS             (1LL << 24)
+#define   ECAP_MHMV_MASK       0xF
+#define   ECAP_MHMV_SHIFT      0x20
+#define   ecap_mhmv(x)         _xfld(x,ECAP_MHMV)
+#define   ECAP_IRO_MASK                0x3FF   /* IOTLB Register */
+#define   ECAP_IRO_SHIFT       0x8
+#define   ecap_iro(x)          (_xfld(x,ECAP_IRO) * 16)
+#define   ECAP_SC              (1LL << 7)      /* Snoop Control */
+#define   ECAP_PT              (1LL << 6)      /* HW Passthru */
+#define   ECAP_EIM             (1LL << 4)
+#define   ECAP_IR              (1LL << 3)      /* Interrupt remap */
+#define   ECAP_DT              (1LL << 2)      /* Device IOTLB */
+#define   ECAP_QI              (1LL << 1)      /* Queued Invalidation */
+#define   ECAP_C               (1LL << 0)      /* Coherent cache */
+
+#define DMAR_GCMD_REG          0x18            /* 32:Global command register */
+#define   GCMD_TE              (1LL << 31)
+#define   GCMD_SRTP            (1LL << 30)
+#define   GCMD_SFL             (1LL << 29)
+#define   GCMD_EAFL            (1LL << 28)
+#define   GCMD_WBF             (1LL << 27)
+#define   GCMD_QIE             (1LL << 26)
+#define   GCMD_IRE             (1LL << 25)
+#define   GCMD_SIRTP           (1LL << 24)
+#define   GCMD_CFI             (1LL << 23)
+
+#define DMAR_GSTS_REG          0x1c    /* 32:Global status register */
+#define   GSTS_TES             (1LL << 31)
+#define   GSTS_RTPS            (1LL << 30)
+#define   GSTS_FLS             (1LL << 29)
+#define   GSTS_AFLS            (1LL << 28)
+#define   GSTS_WBFS            (1LL << 27)
+#define   GSTS_QIES            (1LL << 26)
+#define   GSTS_IRES            (1LL << 25)
+#define   GSTS_IRTPS           (1LL << 24)
+#define   GSTS_CFIS            (1LL << 23)
+
+#define DMAR_CCMD_REG          0x28    /* 64:Context command reg */
+#define   CCMD_ICC             (1LL << 63)
+#define   CCMD_CIRG_MASK       0x3
+#define   CCMD_CIRG_SHIFT      61
+#define   CCMD_CIRG(x)         ((uint64_t)(x) << CCMD_CIRG_SHIFT)
+#define   CCMD_CAIG_MASK       0x3
+#define   CCMD_CAIG_SHIFT      59
+#define   CCMD_FM_MASK         0x3
+#define   CCMD_FM_SHIFT                32
+#define   CCMD_FM(x)           (((uint64_t)(x) << CCMD_FM_SHIFT))
+#define   CCMD_SID_MASK                0xFFFF
+#define   CCMD_SID_SHIFT       8
+#define   CCMD_SID(x)          (((x) << CCMD_SID_SHIFT))
+#define   CCMD_DID_MASK                0xFFFF
+#define   CCMD_DID_SHIFT       0
+#define   CCMD_DID(x)          (((x) << CCMD_DID_SHIFT))
+
+#define CIG_GLOBAL             CCMD_CIRG(CTX_GLOBAL)
+#define CIG_DOMAIN             CCMD_CIRG(CTX_DOMAIN)
+#define CIG_DEVICE             CCMD_CIRG(CTX_DEVICE)
+
+
+#define DMAR_FSTS_REG          0x34    /* 32:Fault Status register */
+#define   FSTS_FRI_MASK                0xFF
+#define   FSTS_FRI_SHIFT       8
+#define   FSTS_PRO             (1LL << 7)
+#define   FSTS_ITE             (1LL << 6)
+#define   FSTS_ICE             (1LL << 5)
+#define   FSTS_IQE             (1LL << 4)
+#define   FSTS_APF             (1LL << 3)
+#define   FSTS_APO             (1LL << 2)
+#define   FSTS_PPF             (1LL << 1)
+#define   FSTS_PFO             (1LL << 0)
+
+#define DMAR_FECTL_REG         0x38    /* 32:Fault control register */
+#define   FECTL_IM             (1LL << 31)
+#define   FECTL_IP             (1LL << 30)
+
+#define FRCD_HI_F              (1LL << (127-64))
+#define FRCD_HI_T              (1LL << (126-64))
+#define FRCD_HI_AT_MASK                0x3
+#define FRCD_HI_AT_SHIFT       (124-64)
+#define FRCD_HI_PV_MASK                0xFFFFF
+#define FRCD_HI_PV_SHIFT       (104-64)
+#define FRCD_HI_FR_MASK                0xFF
+#define FRCD_HI_FR_SHIFT       (96-64)
+#define FRCD_HI_PP             (1LL << (95-64))
+
+#define FRCD_HI_SID_MASK       0xFF
+#define FRCD_HI_SID_SHIFT      0
+#define FRCD_HI_BUS_SHIFT      8
+#define FRCD_HI_BUS_MASK       0xFF
+#define FRCD_HI_DEV_SHIFT      3
+#define FRCD_HI_DEV_MASK       0x1F
+#define FRCD_HI_FUN_SHIFT      0
+#define FRCD_HI_FUN_MASK       0x7
+
+#define DMAR_IOTLB_REG(x)      (ecap_iro((x)->ecap) + 8)
+#define DMAR_IVA_REG(x)                (ecap_iro((x)->ecap) + 0)
+
+#define DMAR_FRIH_REG(x,i)     (cap_fro((x)->cap) + 16*(i) + 8)
+#define DMAR_FRIL_REG(x,i)     (cap_fro((x)->cap) + 16*(i) + 0)
+
+#define IOTLB_IVT              (1LL << 63)
+#define IOTLB_IIRG_MASK                0x3
+#define IOTLB_IIRG_SHIFT       60
+#define IOTLB_IIRG(x)          ((uint64_t)(x) << IOTLB_IIRG_SHIFT)
+#define IOTLB_IAIG_MASK                0x3
+#define IOTLB_IAIG_SHIFT       57
+#define IOTLB_DR               (1LL << 49)
+#define IOTLB_DW               (1LL << 48)
+#define IOTLB_DID_MASK         0xFFFF
+#define IOTLB_DID_SHIFT                32
+#define IOTLB_DID(x)           ((uint64_t)(x) << IOTLB_DID_SHIFT)
+
+#define IIG_GLOBAL     IOTLB_IIRG(IOTLB_GLOBAL)
+#define IIG_DOMAIN     IOTLB_IIRG(IOTLB_DOMAIN)
+#define IIG_PAGE       IOTLB_IIRG(IOTLB_PAGE)
+
+#define DMAR_IQH_REG   0x80    /* 64:Invalidation queue head register */
+#define DMAR_IQT_REG   0x88    /* 64:Invalidation queue tail register */
+#define DMAR_IQA_REG   0x90    /* 64:Invalidation queue addr register */
+#define IQA_QS_256     0       /* 256 entries */
+#define IQA_QS_512     1       /* 512 */
+#define IQA_QS_1K      2       /* 1024 */
+#define IQA_QS_2K      3       /* 2048 */
+#define IQA_QS_4K      4       /* 4096 */
+#define IQA_QS_8K      5       /* 8192 */
+#define IQA_QS_16K     6       /* 16384 */
+#define IQA_QS_32K     7       /* 32768 */
+
+/*
+ * Root Entry: one per bus (256 x 128 bit = 4k)
+ *   0        = Present
+ *   1:11     = Reserved
+ *   12:HAW-1 = Context Table Pointer
+ *   HAW:63   = Reserved
+ *   64:127   = Reserved
+ */
+#define ROOT_P (1L << 0)
+struct root_entry {
+       uint64_t                lo;
+       uint64_t                hi;
+};
+
+/* Check if root entry is valid */
+static inline bool
+root_entry_is_valid(struct root_entry *re)
+{
+       return (re->lo & ROOT_P);
+}
+
+/*
+ * Context Entry: one per devfn (256 x 128 bit = 4k)
+ *   0      = Present
+ *   1      = Fault Processing Disable
+ *   2:3    = Translation Type
+ *   4:11   = Reserved
+ *   12:63  = Second Level Page Translation
+ *   64:66  = Address Width (# PTE levels)
+ *   67:70  = Ignore
+ *   71     = Reserved
+ *   72:87  = Domain ID
+ *   88:127 = Reserved
+ */
+#define CTX_P          (1L << 0)
+#define CTX_FPD                (1L << 1)
+#define CTX_T_MASK     0x3
+#define CTX_T_SHIFT    2
+enum {
+       CTX_T_MULTI,
+       CTX_T_IOTLB,
+       CTX_T_PASSTHRU
+};
+
+#define CTX_H_AW_MASK  0x7
+#define CTX_H_AW_SHIFT 0
+#define CTX_H_DID_MASK 0xFFFF
+#define CTX_H_DID_SHIFT        8
+
+struct context_entry {
+       uint64_t                lo;
+       uint64_t                hi;
+};
+
+/* Set fault processing enable/disable */
+static inline void
+context_set_fpd(struct context_entry *ce, int enable)
+{
+       ce->lo &= ~CTX_FPD;
+       if (enable)
+               ce->lo |= CTX_FPD;
+}
+
+/* Set context entry present */
+static inline void
+context_set_present(struct context_entry *ce)
+{
+       ce->lo |= CTX_P;
+}
+
+/* Set Second Level Page Table Entry PA */
+static inline void
+context_set_slpte(struct context_entry *ce, paddr_t slpte)
+{
+       ce->lo &= 0xFFF;
+       ce->lo |= (slpte & ~0xFFF);
+}
+
+/* Set translation type */
+static inline void
+context_set_translation_type(struct context_entry *ce, int tt)
+{
+       ce->lo &= ~(CTX_T_MASK << CTX_T_SHIFT);
+       ce->lo |= ((tt & CTX_T_MASK) << CTX_T_SHIFT);
+}
+
+/* Set Address Width (# of Page Table levels) */
+static inline void
+context_set_address_width(struct context_entry *ce, int lvl)
+{
+       ce->hi &= ~(CTX_H_AW_MASK << CTX_H_AW_SHIFT);
+       ce->hi |= ((lvl & CTX_H_AW_MASK) << CTX_H_AW_SHIFT);
+}
+
+/* Set domain ID */
+static inline void
+context_set_domain_id(struct context_entry *ce, int did)
+{
+       ce->hi &= ~(CTX_H_DID_MASK << CTX_H_DID_SHIFT);
+       ce->hi |= ((did & CTX_H_DID_MASK) << CTX_H_DID_SHIFT);
+}
+
+/* Get Second Level Page Table PA */
+static inline uint64_t
+context_pte(struct context_entry *ce)
+{
+       return (ce->lo & ~0xFFF);
+}
+
+/* Get translation type */
+static inline int
+context_translation_type(struct context_entry *ce)
+{
+       return (ce->lo>> CTX_T_SHIFT) & CTX_T_MASK;
+}
+
+/* Get domain ID */
+static inline int
+context_domain_id(struct context_entry *ce)
+{
+       return (ce->hi>> CTX_H_DID_SHIFT) & CTX_H_DID_MASK;
+}
+
+/* Get Address Width */
+static inline int
+context_address_width(struct context_entry *ce)
+{
+       return 30 + ((ce->hi>> CTX_H_AW_SHIFT) & CTX_H_AW_MASK) *
+           VTD_STRIDE_SIZE;
+}
+
+/* Check if context entry is valid */
+static inline bool
+context_entry_is_valid(struct context_entry *ce)
+{
+       return (ce->lo & CTX_P);
+}
+
+/*
+ * Fault entry
+ *   0..HAW-1 = Fault address
+ *   HAW:63   = Reserved
+ *   64:71    = Source ID
+ *   96:103   = Fault Reason
+ *   104:123  = PV
+ *   124:125  = Address Translation type
+ *   126      = Type (0 = Read, 1 = Write)
+ *   127      = Fault bit
+ */
+struct fault_entry
+{
+       uint64_t        lo;
+       uint64_t        hi;
+};
+
+/* PTE Entry: 512 x 64-bit = 4k */
+#define PTE_P  (1L << 0)
+#define PTE_R  0x00
+#define PTE_W  (1L << 1)
+struct pte_entry {
+       uint64_t        val;
+};
+
+/*
+ * Queued Invalidation entry
+ *  0:3   = 01h
+ *  4:5   = Granularity
+ *  6:15  = Reserved
+ *  16:31 = Domain ID
+ *  32:47 = Source ID
+ *  48:49 = FM
+ */
+
+/* Invalidate Context Entry */
+#define QI_CTX_DID_MASK                0xFFFF
+#define QI_CTX_DID_SHIFT       16
+#define QI_CTX_SID_MASK                0xFFFF
+#define QI_CTX_SID_SHIFT       32
+#define QI_CTX_FM_MASK         0x3
+#define QI_CTX_FM_SHIFT                48
+#define QI_CTX_IG_MASK         0x3
+#define QI_CTX_IG_SHIFT                4
+#define QI_CTX_DID(x)          (((uint64_t)(x) << QI_CTX_DID_SHIFT))
+#define QI_CTX_SID(x)          (((uint64_t)(x) << QI_CTX_SID_SHIFT))
+#define QI_CTX_FM(x)           (((uint64_t)(x) << QI_CTX_FM_SHIFT))
+
+#define QI_CTX_IG_GLOBAL       (CTX_GLOBAL << QI_CTX_IG_SHIFT)
+#define QI_CTX_IG_DOMAIN       (CTX_DOMAIN << QI_CTX_IG_SHIFT)
+#define QI_CTX_IG_DEVICE       (CTX_DEVICE << QI_CTX_IG_SHIFT)
+
+/* Invalidate IOTLB Entry */
+#define QI_IOTLB_DID_MASK      0xFFFF
+#define QI_IOTLB_DID_SHIFT     16
+#define QI_IOTLB_IG_MASK       0x3
+#define QI_IOTLB_IG_SHIFT      4
+#define QI_IOTLB_DR            (1LL << 6)
+#define QI_IOTLB_DW            (1LL << 5)
+#define QI_IOTLB_DID(x)                (((uint64_t)(x) << QI_IOTLB_DID_SHIFT))
+
+#define QI_IOTLB_IG_GLOBAL     (1 << QI_IOTLB_IG_SHIFT)
+#define QI_IOTLB_IG_DOMAIN     (2 << QI_IOTLB_IG_SHIFT)
+#define QI_IOTLB_IG_PAGE       (3 << QI_IOTLB_IG_SHIFT)
+
+/* QI Commands */
+#define QI_CTX         0x1
+#define QI_IOTLB       0x2
+#define QI_DEVTLB      0x3
+#define QI_INTR                0x4
+#define QI_WAIT                0x5
+#define QI_EXTTLB      0x6
+#define QI_PAS         0x7
+#define QI_EXTDEV      0x8
+
+struct qi_entry {
+       uint64_t        lo;
+       uint64_t        hi;
+};
+
+enum {
+       CTX_GLOBAL = 1,
+       CTX_DOMAIN,
+       CTX_DEVICE,
+
+       IOTLB_GLOBAL = 1,
+       IOTLB_DOMAIN,
+       IOTLB_PAGE,
+};
+
+#endif
Index: dev/acpi/files.acpi
===================================================================
RCS file: /cvs/src/sys/dev/acpi/files.acpi,v
retrieving revision 1.27
diff -u -p -u -p -r1.27 files.acpi
--- dev/acpi/files.acpi 6 Nov 2013 10:40:36 -0000       1.27
+++ dev/acpi/files.acpi 22 Jul 2015 20:25:36 -0000
@@ -56,6 +56,11 @@ device       acpimadt
 attach acpimadt at acpi
 file   dev/acpi/acpimadt.c             acpimadt
 
+# DMA Remapping Table
+device  acpidmar
+attach  acpidmar at acpi
+file    dev/acpi/acpidmar.c             acpidmar
+
 # Memory Mapped Configuration Space Address Description Table
 device acpimcfg
 attach acpimcfg at acpi
                                          

Reply via email to