> Date: Thu, 8 Oct 2020 00:34:28 -0500
> From: Jordan Hargrave <jordan_hargr...@hotmail.com>
> 
> Ok updated the new changes.

I think this is good enough for further cleanup in the tree now.
Builds all thr amd64 kernels, doesn't break i386 and arm64 GENERIC.MP.

However, I think acpidmar(4) shouldn't be enabled yet until have done
a bit more testing.  Theo do we want to compile in the driver such
that people can easily flip it on in UKC?

I also noticed that re(4) does a bad DMA transfer as soon as the
interface is brought up. With your diff the kernel doesn't panic if
that happensl it just spits out some debugging information.  We may
want to change that in the future.

ok kettenis@ once the question about enabling acpidmar(4) is resolved.

> 
> On Mon, Oct 05, 2020 at 09:54:02PM +0200, Mark Kettenis wrote:
> > > Date: Thu, 17 Sep 2020 20:54:51 -0500
> > > From: Jordan Hargrave <jordan_hargr...@hotmail.com>
> > > Cc: ma...@peereboom.org, kette...@openbsd.org, tech@openbsd.org,
> > >         d...@openbsd.org, j...@openbsd.org
> > > Content-Type: text/plain; charset=us-ascii
> > > Content-Disposition: inline
> > > 
> > > Ok made more changes....
> > > 
> > > > 
> > > > Should be handled by that activate function as well.
> > > >
> > 
> > So there are quite a few style issues.  I can point them out to you,
> > or I could fix them after this is committed, which is probably more
> > efficient.
> > 
> > Also, there seems to be lot of debug code left in here that should be
> > removed or at least hidden before this gets committed.
> > 
> > > 
> > > diff --git a/sys/arch/amd64/conf/GENERIC b/sys/arch/amd64/conf/GENERIC
> > > index 1d6397391..a69c72c26 100644
> > > --- a/sys/arch/amd64/conf/GENERIC
> > > +++ b/sys/arch/amd64/conf/GENERIC
> > > @@ -45,6 +45,7 @@ acpibtn*        at acpi?
> > >  acpicpu* at acpi?
> > >  acpicmos*        at acpi?
> > >  acpidock*        at acpi?
> > > +acpidmar0        at acpi?
> > >  acpiec*          at acpi?
> > >  acpipci* at acpi?
> > >  acpiprt* at acpi?
> > > diff --git a/sys/arch/amd64/include/pci_machdep.h 
> > > b/sys/arch/amd64/include/pci_machdep.h
> > > index bc295cc22..ea09f1abc 100644
> > > --- a/sys/arch/amd64/include/pci_machdep.h
> > > +++ b/sys/arch/amd64/include/pci_machdep.h
> > > @@ -91,7 +91,12 @@ void           
> > > *pci_intr_establish_cpu(pci_chipset_tag_t, pci_intr_handle_t,
> > >               int, struct cpu_info *,
> > >               int (*)(void *), void *, const char *);
> > >  void             pci_intr_disestablish(pci_chipset_tag_t, void *);
> > > +#if NACPIDMAR > 0
> > > +int              pci_probe_device_hook(pci_chipset_tag_t,
> > > +             struct pci_attach_args *);
> > > +#else
> > >  #define  pci_probe_device_hook(c, a)     (0)
> > > +#endif
> > 
> > This is probably a bad idea.  You don't include "acpidmar.h" in this
> > file, and doing so is probaly undesireable.  But that means the
> > definition of the hook function depends on whether the file that
> > includes this does that or not.
> > 
> > Better just unconditionally provide the prototype and use a #if
> > NACPIDMAR > 0 in the implementation.
> >
> 
> Ok changed to that method
> 
> > > +#include "acpidmar.h"
> > > +#include "amd_iommu.h"
> > > +
> > > +//#define IOMMU_DEBUG
> > 
> > No C++-style comments please.  Make this an #undef or use /* */.
> > 
> > > +
> > > +#ifdef IOMMU_DEBUG
> > > +#define dprintf(x...) printf(x)
> > > +#else
> > > +#define dprintf(x...)
> > > +#endif
> > > +
> > > +#ifdef DDB
> > > +int      acpidmar_ddb = 0;
> > > +#endif
> > > +
> > > +int      intel_iommu_gfx_mapped = 0;
> > 
> > Unused variable.
> > 
> > > +int      force_cm = 1;
> > 
> > Rename to "acpidmar_force_cm"?
> > 
> > > +
> > > +void showahci(void *);
> > 
> > Unused prototype.
> > 
> > > +
> > > +/* 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;
> > > +}
> > > +
> > > +/* Alias mapping */
> > > +#define SID_INVALID 0x80000000L
> > > +static uint32_t sid_flag[65536];
> > > +
> > > +struct domain_dev {
> > > + int                     sid;
> > > + int                     sec;
> > > + int                     sub;
> > > + 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;
> > > +
> > > + struct mutex            exlck;
> > > + char                    exname[32];
> > > + struct extent           *iovamap;
> > > + TAILQ_HEAD(,domain_dev) devices;
> > > + TAILQ_ENTRY(domain)     link;
> > > +};
> > > +
> > > +#define DOM_DEBUG 0x1
> > > +#define DOM_NOMAP 0x2
> > > +
> > > +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 ivhd_devlist {
> > > + int                             start_id;
> > > + int                             end_id;
> > > + int                             cfg;
> > > + TAILQ_ENTRY(ivhd_devlist)       link;
> > > +};
> > > +
> > > +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
> > 
> > C++ comment.
> > 
> > > + int                     qi_head;
> > > + int                     qi_tail;
> > > + paddr_t                 qip;
> > > + struct qi_entry         *qi;
> > > +
> > > + struct domain           *unity;
> > > + TAILQ_HEAD(,domain)     domains;
> > > +}
> > > +
> > > +/* Intel: Initialize IOMMU */
> > > +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);
> > > + }
> > 
> > Why are you using _bus_space_map() here?  Do you expect this address
> > space to be mapped by some other driver?
> >
> 
> It shouldn't be used anywhere else.
> 
> > > +
> > > + TAILQ_INIT(&iommu->domains);
> > > + iommu->id = ++niommu;
> > > + iommu->flags = dh->flags;
> > > + iommu->segment = dh->segment;
> > > + iommu->iot = sc->sc_memt;
> > > +
> > > + iommu->cap = iommu_read_8(iommu, DMAR_CAP_REG);
> > > + iommu->ecap = iommu_read_8(iommu, DMAR_ECAP_REG);
> > > + iommu->ndoms = cap_nd(iommu->cap);
> > > +
> > > + printf("  caps: %s%s%s%s%s%s%s%s%s%s%s\n",
> > > +     iommu->cap & CAP_AFL ? "afl " : "",         // adv fault
> > > +     iommu->cap & CAP_RWBF ? "rwbf " : "",       // write-buffer flush
> > > +     iommu->cap & CAP_PLMR ? "plmr " : "",       // protected lo region
> > > +     iommu->cap & CAP_PHMR ? "phmr " : "",       // protected hi region
> > > +     iommu->cap & CAP_CM ? "cm " : "",           // caching mode
> > > +     iommu->cap & CAP_ZLR ? "zlr " : "",         // zero-length read
> > > +     iommu->cap & CAP_PSI ? "psi " : "",         // page invalidate
> > > +     iommu->cap & CAP_DWD ? "dwd " : "",         // write drain
> > > +     iommu->cap & CAP_DRD ? "drd " : "",         // read drain
> > > +     iommu->cap & CAP_FL1GP ? "Gb " : "",        // 1Gb pages
> > > +     iommu->cap & CAP_PI ? "pi " : "");          // posted interrupts
> > > + printf("  ecap: %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
> > > +     iommu->ecap & ECAP_C ? "c " : "",           // coherent
> > > +     iommu->ecap & ECAP_QI ? "qi " : "",         // queued invalidate
> > > +     iommu->ecap & ECAP_DT ? "dt " : "",         // device iotlb
> > > +     iommu->ecap & ECAP_IR ? "ir " : "",         // intr remap
> > > +     iommu->ecap & ECAP_EIM ? "eim " : "",       // x2apic
> > > +     iommu->ecap & ECAP_PT ? "pt " : "",         // passthrough
> > > +     iommu->ecap & ECAP_SC ? "sc " : "",         // snoop control
> > > +     iommu->ecap & ECAP_ECS ? "ecs " : "",       // extended context
> > > +     iommu->ecap & ECAP_MTS ? "mts " : "",       // memory type
> > > +     iommu->ecap & ECAP_NEST ? "nest " : "",     // nested translations
> > > +     iommu->ecap & ECAP_DIS ? "dis " : "",       // deferred invalidation
> > > +     iommu->ecap & ECAP_PASID ? "pas " : "",     // pasid
> > > +     iommu->ecap & ECAP_PRS ? "prs " : "",       // page request
> > > +     iommu->ecap & ECAP_ERS ? "ers " : "",       // execute request
> > > +     iommu->ecap & ECAP_SRS ? "srs " : "",       // supervisor request
> > > +     iommu->ecap & ECAP_NWFS ? "nwfs " : "",     // no write flag
> > > +     iommu->ecap & ECAP_EAFS ? "eafs " : "");    // extended accessed 
> > > flag
> > 
> > Are these debug printfs?  Or are you planning to keep these?
> >
> 
> Made them all debugs
> 
> > > +
> > > + mtx_init(&iommu->reg_lock, IPL_HIGH);
> > > +
> > > + /* Clear Interrupt Masking */
> > > + iommu_write_4(iommu, DMAR_FSTS_REG, FSTS_PFO | FSTS_PPF);
> > > +
> > > + iommu->intr = acpidmar_intr_establish(iommu, IPL_HIGH,
> > > +     acpidmar_intr, iommu, "dmarintr");
> > > +
> > > + /* Enable interrupts */
> > > + sts = iommu_read_4(iommu, DMAR_FECTL_REG);
> > > + iommu_write_4(iommu, DMAR_FECTL_REG, sts & ~FECTL_IM);
> > > +
> > > + /* 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
> > 
> > Probably should make this #ifdef IOMMU_DEBUG
> > 
> > > + iommu->rtaddr = paddr;
> > > + iommu_flush_write_buffer(iommu);
> > > + iommu_set_rtaddr(iommu, paddr);
> > > +
> > > +#if 0
> > > + if (iommu->ecap & ECAP_QI) {
> > > +         /* Queued Invalidation support */
> > > +         iommu->qi = iommu_alloc_page(iommu, &iommu->qip);
> > > +         iommu_write_8(iommu, DMAR_IQT_REG, 0);
> > > +         iommu_write_8(iommu, DMAR_IQA_REG, iommu->qip | IQA_QS_256);
> > > + }
> > > + if (iommu->ecap & ECAP_IR) {
> > > +         /* Interrupt remapping support */
> > > +         iommu_write_8(iommu, DMAR_IRTA_REG, 0);
> > > + }
> > > +#endif
> > 
> > At some point you'll need to decide whether this code should be kept
> > or not...
> > 
> > > +
> > > + /* Calculate guest address width and supported guest widths */
> > > + gaw = -1;
> > > + iommu->mgaw = cap_mgaw(iommu->cap);
> > > + printf("gaw: %d { ", iommu->mgaw);
> > > + for (i = 0; i < 5; i++) {
> > > +         if (cap_sagaw(iommu->cap) & (1L << i)) {
> > > +                 gaw = VTD_LEVELTOAW(i);
> > > +                 printf("%d ", gaw);
> > > +                 iommu->agaw = gaw;
> > > +         }
> > > + }
> > > + printf("}\n");
> > 
> > More debug printfs?
> > 
> > > +
> > > + /* Cache current status register bits */
> > > + sts = iommu_read_4(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;
> > > + if (iommu->gcmd) {
> > > +         printf("gcmd: %x preset\n", iommu->gcmd);
> > > + }
> > > + acpidmar_intr(iommu);
> > > + return (0);
> > > +}
> > > +
> > > +const char *dmar_rn(int reg);
> > > +
> > > +const char *
> > > +dmar_rn(int reg)
> > > +{
> > > + switch (reg) {
> > > + case EVT_HEAD_REG: return "evthead";
> > > + case EVT_TAIL_REG: return "evttail";
> > > + case CMD_HEAD_REG: return "cmdhead";
> > > + case CMD_TAIL_REG: return "cmdtail";
> > > + case CMD_BASE_REG: return "cmdbase";
> > > + case EVT_BASE_REG: return "evtbase";
> > > + case DEV_TAB_BASE_REG: return "devtblbase";
> > > + case IOMMUCTL_REG: return "iommuctl";
> > > +#if 0
> > > + 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";
> > > +#endif
> > > + }
> > > + return "unknown";
> > > +}
> > > +
> > > +/* Read/Write IOMMU register */
> > > +uint32_t
> > > +iommu_read_4(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 %.8lx [%s]\n",
> > > +             iommu->id, reg, (unsigned long)v, dmar_rn(reg));
> > > + }
> > > +
> > > + return (v);
> > > +}
> > > +
> > > +
> > > +#define dbprintf(x...)
> > 
> > What is the point of this?  Shoulkd this be a IOMMU_DEBUG feature?
> > 
> > > +
> > > +void
> > > +iommu_write_4(struct iommu_softc *iommu, int reg, uint32_t v)
> > > +{
> > > + dbprintf("iommu%d: write %.8x %.16lx [%s]\n",
> > > +     iommu->id, reg, (unsigned long)v, dmar_rn(reg));
> > > + bus_space_write_4(iommu->iot, iommu->ioh, reg, (uint32_t)v);
> > > +}
> > > +
> > > +uint64_t
> > > +iommu_read_8(struct iommu_softc *iommu, int reg)
> > > +{
> > > + uint64_t        v;
> > > +
> > > + v = bus_space_read_8(iommu->iot, iommu->ioh, reg);
> > > + if (reg < 00) {
> > 
> > double-nul?  In any case, checking for reg < 0 doesn't make sense to me...
> > 
> > > +         printf("iommu%d: read %x %.8lx [%s]\n",
> > > +             iommu->id, reg, (unsigned long)v, dmar_rn(reg));
> > > + }
> > > +
> > > + return (v);
> > > +}
> > > +
> > > +void
> > > +iommu_write_8(struct iommu_softc *iommu, int reg, uint64_t v)
> > > +{
> > > + dbprintf("iommu%d: write %.8x %.16lx [%s]\n",
> > > +             iommu->id, reg, (unsigned long)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;
> > > + int gaw;
> > > +
> > > + dprintf("iommu%d: create domain: %.4x\n", iommu->id, did);
> > > + 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;       // nop
> > > + dom->dmat._dmamap_destroy   = dmar_dmamap_destroy;      // nop
> > > + dom->dmat._dmamap_load      = dmar_dmamap_load;         // lm
> > > + dom->dmat._dmamap_load_mbuf = dmar_dmamap_load_mbuf;    // lm
> > > + dom->dmat._dmamap_load_uio  = dmar_dmamap_load_uio;     // lm
> > > + dom->dmat._dmamap_load_raw  = dmar_dmamap_load_raw;     // lm
> > > + dom->dmat._dmamap_unload    = dmar_dmamap_unload;       // um
> > > + dom->dmat._dmamap_sync      = dmar_dmamap_sync;         // lm
> > > + dom->dmat._dmamem_alloc     = dmar_dmamem_alloc;        // nop
> > > + dom->dmat._dmamem_free      = dmar_dmamem_free;         // nop
> > > + dom->dmat._dmamem_map       = dmar_dmamem_map;          // nop
> > > + dom->dmat._dmamem_unmap     = dmar_dmamem_unmap;        // nop
> > > + dom->dmat._dmamem_mmap      = dmar_dmamem_mmap;
> > > +
> > > + snprintf(dom->exname, sizeof(dom->exname), "did:%x.%.4x",
> > > +     iommu->id, dom->did);
> > > +
> > > + /* Setup IOMMU address map */
> > > + gaw = min(iommu->agaw, iommu->mgaw);
> > > + dom->iovamap = extent_create(dom->exname, 1024*1024*16,
> > > +     (1LL << gaw)-1,
> > > +     M_DEVBUF, NULL, 0,
> > > +     EX_WAITOK|EX_NOCOALESCE);
> > > +
> > > + /* Zero out Interrupt region */
> > > + extent_alloc_region(dom->iovamap, 0xFEE00000L, 0x100000,
> > > +     EX_WAITOK);
> > 
> > Magic hardcoded address?
> >
> 
> It's the MSI interrupt address.  Linux reserves those areas so I marked them 
> invalid as well.
> 
> > > + mtx_init(&dom->exlck, IPL_HIGH);
> > > +
> > > + TAILQ_INSERT_TAIL(&iommu->domains, dom, link);
> > > +
> > > + return dom;
> > > +}
> > > +
> > > +void
> > > +domain_add_device(struct domain *dom, int sid)
> > > +{        
> > > + struct domain_dev *ddev;
> > > + 
> > > + dprintf("add %s to iommu%d.%.4x\n", dmar_bdf(sid), dom->iommu->id, 
> > > dom->did);
> > > + ddev = malloc(sizeof(*ddev), M_DEVBUF, M_ZERO | M_WAITOK);
> > > + ddev->sid = sid;
> > > + TAILQ_INSERT_TAIL(&dom->devices, ddev, link);
> > > +
> > > + /* Should set context entry here?? */
> > > +}
> > > +
> > > +void
> > > +domain_remove_device(struct domain *dom, int sid)
> > > +{
> > > + struct domain_dev *ddev, *tmp;
> > > +
> > > + TAILQ_FOREACH_SAFE(ddev, &dom->devices, link, tmp) {
> > > +         if (ddev->sid == sid) {
> > > +                 TAILQ_REMOVE(&dom->devices, ddev, link);
> > > +                 free(ddev, sizeof(*ddev), M_DEVBUF);
> > > +         }
> > > + }
> > > +}
> > > +
> > > +/* 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_dev       *ddev;
> > > + struct domain           *dom;
> > > + 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;
> > > + }
> > > +
> > > + //acpidmar_intr(iommu);
> > 
> > ?
> > 
> > > +
> > > + /* 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 */
> > > + domain_add_device(dom, sid);
> > > +
> > > + return dom;
> > > +}
> > > +
> > > +/* Map Guest Pages into IOMMU */
> > > +void
> > > +_iommu_map(void *dom, vaddr_t va, bus_addr_t gpa, bus_size_t len)
> > > +{
> > > + bus_size_t i;
> > > + paddr_t hpa;
> > > +
> > > + if (dom == NULL) {
> > > +         return;
> > > + }
> > > + dprintf("Mapping dma: %lx = %lx/%lx\n", va, gpa, len);
> > > + for (i = 0; i < len; i += PAGE_SIZE) {
> > > +         hpa = 0;
> > > +         pmap_extract(curproc->p_vmspace->vm_map.pmap, va, &hpa);
> > > +         domain_map_page(dom, gpa, hpa, PTE_P | PTE_R | PTE_W);
> > > +         gpa += PAGE_SIZE;
> > > +         va  += PAGE_SIZE;
> > > + }
> > > +}
> > > +
> > > +/* Find IOMMU for a given PCI device */
> > > +void
> > > +*_iommu_domain(int segment, int bus, int dev, int func, int *id)
> > > +{
> > > + struct domain *dom;
> > > +
> > > + dom = domain_lookup(acpidmar_sc, segment, mksid(bus, dev, func));
> > > + if (dom) {
> > > +         *id = dom->did;
> > > + }
> > > + return dom;
> > > +}
> > > +
> > > +void
> > > +domain_map_device(struct domain *dom, int sid);
> > > +
> > > +void
> > > +domain_map_device(struct domain *dom, int sid)
> > > +{
> > > + struct iommu_softc      *iommu;
> > > + struct context_entry    *ctx;
> > > + paddr_t                 paddr;
> > > + int                     bus, devfn;
> > > + int                     tt, lvl;
> > > +
> > > + iommu = dom->iommu;
> > > +
> > > + bus = sid_bus(sid);
> > > + devfn = sid_devfn(sid);
> > > + /* AMD attach device */
> > > + if (iommu->dte) {
> > > +         struct ivhd_dte *dte = &iommu->dte[sid];
> > > +         if (!dte->dw0) {
> > > +                 /* Setup Device Table Entry: bus.devfn */
> > > +                 dprintf("@@@ PCI Attach: %.4x[%s] %.4x\n", sid, 
> > > dmar_bdf(sid), dom->did);
> > > +                 dte_set_host_page_table_root_ptr(dte, dom->ptep);
> > > +                 dte_set_domain(dte, dom->did);
> > > +                 dte_set_mode(dte, 3);  // Set 4 level PTE
> > > +                 dte_set_tv(dte);
> > > +                 dte_set_valid(dte);
> > > +                 ivhd_flush_devtab(iommu, dom->did);
> > > +#ifdef IOMMU_DEBUG
> > > +                 //ivhd_showreg(iommu);
> > > +                 ivhd_showdte(iommu);
> > > +#endif
> > > +         }
> > > +         //ivhd_poll_events(iommu);
> > > +         return;
> > > + }
> > > +
> > > + /* 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 = VTD_AWTOLEVEL(iommu->agaw);
> > > +
> > > +         /* 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));
> > > +         if ((iommu->cap & CAP_CM) || force_cm) {
> > > +                 iommu_flush_ctx(iommu, CTX_DEVICE, dom->did, sid, 0);
> > > +                 iommu_flush_tlb(iommu, IOTLB_GLOBAL, 0);
> > > +         } else {
> > > +                 iommu_flush_write_buffer(iommu);
> > > +         }
> > > +         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);
> > > + }
> > > +}
> > > +
> > > +struct domain *
> > > +acpidmar_pci_attach(struct acpidmar_softc *sc, int segment, int sid, int 
> > > mapctx)
> > > +{
> > > + static struct domain    *dom;
> > > +
> > > + dom = domain_lookup(sc, segment, sid);
> > > + if (!dom) {
> > > +         printf("no domain: %s\n", dmar_bdf(sid));
> > > +         return NULL;
> > > + }
> > > +
> > > + if (mapctx) {
> > > +         domain_map_device(dom, sid);
> > > + }
> > > +
> > > + return dom;
> > > +}
> > > +
> > > +int ismap(int bus, int dev, int fun) {
> > > + return 1;
> > > +}
> > 
> > Unused function.
> > 
> > > +
> > > +void
> > > +acpidmar_pci_hook(pci_chipset_tag_t pc, struct pci_attach_args *pa)
> > > +{
> > > + int             bus, dev, fun, sid;
> > > + struct domain   *dom;
> > > + pcireg_t        reg;
> > > +
> > > + if (!acpidmar_sc) {
> > > +         /* No DMAR, ignore */
> > > +         return;
> > > + }
> > > +
> > > + /* Add device to our list */
> > > + pci_decompose_tag(pc, pa->pa_tag, &bus, &dev, &fun);
> > > + sid = mksid(bus, dev, fun);
> > > + if (sid_flag[sid] & SID_INVALID)
> > > +         return;
> > > +
> > > + reg = pci_conf_read(pc, pa->pa_tag, PCI_CLASS_REG);
> > > +#if 0
> > > + 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;
> > > + }
> > > +#endif
> > 
> > Remove?  You seem to be handling this case below.
> > 
> > > + /* Add device to domain */
> > > + dom = acpidmar_pci_attach(acpidmar_sc, pa->pa_domain, sid, 0);
> > > + if (dom == NULL)
> > > +         return;
> > > +
> > > + if (PCI_CLASS(reg) == PCI_CLASS_DISPLAY &&
> > > +     PCI_SUBCLASS(reg) == PCI_SUBCLASS_DISPLAY_VGA) {
> > > +         dom->flag = DOM_NOMAP;
> > > + }
> > > + if (PCI_CLASS(reg) == PCI_CLASS_BRIDGE &&
> > > +     PCI_SUBCLASS(reg) == PCI_SUBCLASS_BRIDGE_ISA) {
> > > +         /* For ISA Bridges, map 0-16Mb as 1:1 */
> > > +         printf("dmar: %.4x:%.2x:%.2x.%x mapping ISA\n",
> > > +             pa->pa_domain, bus, dev, fun);
> > > +         domain_map_pthru(dom, 0x00, 16*1024*1024);
> > > + }
> > > +
> > > + /* 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 - sizeof(*ds);
> > > +         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("  %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");
> > 
> > More debug printf stuff?
> > 
> > > + }
> > > +}
> > > +
> > > +/* DMA Remapping Hardware Unit */
> 
> 
> diff --git a/sys/arch/amd64/conf/GENERIC b/sys/arch/amd64/conf/GENERIC
> index ea43a2158..8ea728311 100644
> --- a/sys/arch/amd64/conf/GENERIC
> +++ b/sys/arch/amd64/conf/GENERIC
> @@ -45,6 +45,7 @@ acpibtn*    at acpi?
>  acpicpu*     at acpi?
>  acpicmos*    at acpi?
>  acpidock*    at acpi?
> +acpidmar0    at acpi?
>  acpiec*              at acpi?
>  acpipci*     at acpi?
>  acpiprt*     at acpi?
> diff --git a/sys/arch/amd64/include/pci_machdep.h 
> b/sys/arch/amd64/include/pci_machdep.h
> index bc295cc22..c725bdc73 100644
> --- a/sys/arch/amd64/include/pci_machdep.h
> +++ b/sys/arch/amd64/include/pci_machdep.h
> @@ -91,7 +91,8 @@ void                
> *pci_intr_establish_cpu(pci_chipset_tag_t, pci_intr_handle_t,
>                   int, struct cpu_info *,
>                   int (*)(void *), void *, const char *);
>  void         pci_intr_disestablish(pci_chipset_tag_t, void *);
> -#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 *);
>  
> diff --git a/sys/arch/amd64/pci/pci_machdep.c 
> b/sys/arch/amd64/pci/pci_machdep.c
> index cf4e835de..80d4f4d86 100644
> --- a/sys/arch/amd64/pci/pci_machdep.c
> +++ b/sys/arch/amd64/pci/pci_machdep.c
> @@ -89,6 +89,13 @@
>  #include <machine/mpbiosvar.h>
>  #endif
>  
> +#include "acpi.h"
> +
> +#include "acpidmar.h"
> +#if NACPIDMAR > 0
> +#include <dev/acpi/acpidmar.h>
> +#endif
> +
>  /*
>   * Memory Mapped Configuration space access.
>   *
> @@ -797,7 +804,15 @@ pci_init_extents(void)
>       }
>  }
>  
> -#include "acpi.h"
> +int
> +pci_probe_device_hook(pci_chipset_tag_t pc, struct pci_attach_args *pa)
> +{
> +#if NACPIDMAR > 0
> +     acpidmar_pci_hook(pc, pa);
> +#endif
> +     return 0;
> +}
> +
>  #if NACPI > 0
>  void acpi_pci_match(struct device *, struct pci_attach_args *);
>  pcireg_t acpi_pci_min_powerstate(pci_chipset_tag_t, pcitag_t);
> diff --git a/sys/dev/acpi/acpidmar.c b/sys/dev/acpi/acpidmar.c
> new file mode 100644
> index 000000000..0c21a92a2
> --- /dev/null
> +++ b/sys/dev/acpi/acpidmar.c
> @@ -0,0 +1,2913 @@
> +/*
> + * 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/biosvar.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"
> +#include "amd_iommu.h"
> +
> +/* We don't want IOMMU to remap MSI */
> +#define MSI_BASE_ADDRESS 0xFEE00000L
> +#define MSI_BASE_SIZE    0x00100000L
> +#define MAX_DEVFN 65536
> +
> +#ifdef IOMMU_DEBUG
> +int acpidmar_dbg_lvl = 0;
> +#define DPRINTF(lvl,x...) if (acpidmar_dbg_lvl >= lvl) { printf(x) }
> +#else
> +#define DPRINTF(lvl,x...)
> +#endif
> +
> +#ifdef DDB
> +int  acpidmar_ddb = 0;
> +#endif
> +
> +int  acpidmar_force_cm = 1;
> +
> +/* 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;
> +}
> +
> +/* Alias mapping */
> +#define SID_INVALID 0x80000000L
> +static uint32_t sid_flag[MAX_DEVFN];
> +
> +struct domain_dev {
> +     int                     sid;
> +     int                     sec;
> +     int                     sub;
> +     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;
> +
> +     struct mutex            exlck;
> +     char                    exname[32];
> +     struct extent           *iovamap;
> +     TAILQ_HEAD(,domain_dev) devices;
> +     TAILQ_ENTRY(domain)     link;
> +};
> +
> +#define DOM_DEBUG 0x1
> +#define DOM_NOMAP 0x2
> +
> +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 ivhd_devlist {
> +     int                             start_id;
> +     int                             end_id;
> +     int                             cfg;
> +     TAILQ_ENTRY(ivhd_devlist)       link;
> +};
> +
> +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;
> +
> +     /* AMD iommu */
> +     struct ivhd_dte         *dte;
> +     void                    *cmd_tbl;
> +     void                    *evt_tbl;
> +     paddr_t                 cmd_tblp;
> +     paddr_t                 evt_tblp;
> +};
> +
> +static inline int iommu_bad(struct iommu_softc *sc)
> +{
> +     return (sc->flags & IOMMU_FLAGS_BAD);
> +}
> +
> +static inline int iommu_enabled(struct iommu_softc *sc)
> +{
> +     if (sc->dte) {
> +             return 1;
> +     }
> +     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;
> +     struct bus_dma_tag      sc_dmat;
> +
> +     struct ivhd_dte         *sc_hwdte;
> +     paddr_t                 sc_hwdtep;
> +
> +     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, 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 *);
> +int          acpiivhd_intr(void *);
> +
> +#define DID_UNITY 0x1
> +
> +void _dumppte(struct pte_entry *, int, vaddr_t);
> +
> +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, int, const char *);
> +
> +void (*domain_map_page)(struct domain *, vaddr_t, paddr_t, uint64_t);
> +void domain_map_page_amd(struct domain *, vaddr_t, paddr_t, uint64_t);
> +void domain_map_page_intel(struct domain *, vaddr_t, paddr_t, uint64_t);
> +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 acpiivrs_init(struct acpidmar_softc *, struct acpi_ivrs *);
> +
> +void *acpidmar_intr_establish(void *, int, int (*)(void *), void *,
> +    const char *);
> +
> +void iommu_write_4(struct iommu_softc *, int, uint32_t);
> +uint32_t iommu_read_4(struct iommu_softc *, int);
> +void iommu_write_8(struct iommu_softc *, int, uint64_t);
> +uint64_t iommu_read_8(struct iommu_softc *, int);
> +void iommu_showfault(struct iommu_softc *, int,
> +    struct fault_entry *);
> +void iommu_showcfg(struct iommu_softc *, int);
> +
> +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_write_buffer(struct iommu_softc *);
> +void iommu_issue_qi(struct iommu_softc *, struct qi_entry *);
> +
> +void iommu_flush_ctx(struct iommu_softc *, int, int, int, int);
> +void iommu_flush_ctx_qi(struct iommu_softc *, int, int, int, int);
> +void iommu_flush_tlb(struct iommu_softc *, int, int);
> +void iommu_flush_tlb_qi(struct iommu_softc *, int, int);
> +
> +void iommu_set_rtaddr(struct iommu_softc *, paddr_t);
> +
> +void *iommu_alloc_hwdte(struct acpidmar_softc *, size_t, paddr_t *);
> +
> +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);
> +
> +static void dmar_dumpseg(bus_dma_tag_t, int, bus_dma_segment_t *, const char 
> *);
> +const char *dom_bdf(struct domain *);
> +void domain_map_check(struct domain *);
> +
> +struct pte_entry *pte_lvl(struct iommu_softc *, struct pte_entry *, vaddr_t, 
> int, uint64_t);
> +int  ivhd_poll_events(struct iommu_softc *);
> +void ivhd_showreg(struct iommu_softc *);
> +void ivhd_showdte(struct iommu_softc *);
> +void ivhd_showcmd(struct iommu_softc *);
> +
> +static inline int
> +debugme(struct domain *dom)
> +{
> +     return 0;
> +     return (dom->flag & DOM_DEBUG);
> +}
> +
> +void
> +domain_map_check(struct domain *dom)
> +{
> +     struct iommu_softc *iommu;
> +     struct domain_dev *dd;
> +     struct context_entry *ctx;
> +     int v;
> +
> +     iommu = dom->iommu;
> +     TAILQ_FOREACH(dd, &dom->devices, link) {
> +             acpidmar_pci_attach(acpidmar_sc, iommu->segment, dd->sid, 1);
> +
> +             if (iommu->dte)
> +                     continue;
> +
> +             /* Check if this is the first time we are mapped */
> +             ctx = &iommu->ctx[sid_bus(dd->sid)][sid_devfn(dd->sid)];
> +             v = context_user(ctx);
> +             if (v != 0xA) {
> +                     printf("  map: %.4x:%.2x:%.2x.%x iommu:%d did:%.4x\n",
> +                         iommu->segment,
> +                         sid_bus(dd->sid),
> +                         sid_dev(dd->sid),
> +                         sid_fun(dd->sid),
> +                         iommu->id,
> +                         dom->did);
> +                     context_set_user(ctx, 0xA);
> +             }
> +     }
> +}
> +
> +/* Map a single page as passthrough - used for DRM */
> +void
> +dmar_ptmap(bus_dma_tag_t tag, bus_addr_t addr)
> +{
> +     struct domain *dom = tag->_cookie;
> +
> +     if (!acpidmar_sc)
> +             return;
> +     domain_map_check(dom);
> +     domain_map_page(dom, addr, addr, PTE_P | PTE_R | PTE_W);
> +}
> +
> +/* Map a range of pages 1:1 */
> +void
> +domain_map_pthru(struct domain *dom, paddr_t start, paddr_t end)
> +{
> +     domain_map_check(dom);
> +     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_intel(struct domain *dom, vaddr_t va, paddr_t pa, uint64_t 
> flags)
> +{
> +     paddr_t paddr;
> +     struct pte_entry *pte, *npte;
> +     int lvl, idx;
> +     struct iommu_softc *iommu;
> +
> +     iommu = dom->iommu;
> +     /* Insert physical address into virtual address map
> +      * XXX: could we use private pmap here?
> +      * essentially doing a pmap_enter(map, va, pa, prot);
> +      */
> +
> +     /* Only handle 4k pages for now */
> +     npte = dom->pte;
> +     for (lvl = iommu->agaw - VTD_STRIDE_SIZE; lvl>= VTD_LEVEL0;
> +         lvl -= VTD_STRIDE_SIZE) {
> +             idx = (va >> lvl) & VTD_STRIDE_MASK;
> +             pte = &npte[idx];
> +             if (lvl == VTD_LEVEL0) {
> +                     /* Level 1: Page Table - add physical address */
> +                     pte->val = pa | flags;
> +                     iommu_flush_cache(iommu, pte, sizeof(*pte));
> +                     break;
> +             } else if (!(pte->val & PTE_P)) {
> +                     /* Level N: Point to lower level table */
> +                     iommu_alloc_page(iommu, &paddr);
> +                     pte->val = paddr | PTE_P | PTE_R | PTE_W;
> +                     iommu_flush_cache(iommu, pte, sizeof(*pte));
> +             }
> +             npte = (void *)PMAP_DIRECT_MAP((pte->val & VTD_PTE_MASK));
> +     }
> +}
> +
> +/* Map a single paddr to IOMMU paddr: AMD
> + * physical address breakdown into levels:
> + * xxxxxxxx.xxxxxxxx.xxxxxxxx.xxxxxxxx.xxxxxxxx.xxxxxxxx.xxxxxxxx.xxxxxxxx
> + *        5.55555555.44444444.43333333,33222222.22211111.1111----.-------- 
> + * mode:
> + *  000 = none   shift
> + *  001 = 1 [21].12
> + *  010 = 2 [30].21
> + *  011 = 3 [39].30
> + *  100 = 4 [48].39
> + *  101 = 5 [57]
> + *  110 = 6
> + *  111 = reserved
> + */
> +struct pte_entry *
> +pte_lvl(struct iommu_softc *iommu, struct pte_entry *pte, vaddr_t va, 
> +     int shift, uint64_t flags)
> +{
> +     paddr_t paddr;
> +     int idx;
> +     
> +     idx = (va >> shift) & VTD_STRIDE_MASK;
> +     if (!(pte[idx].val & PTE_P)) {
> +             /* Page Table entry is not present... create a new page entry */
> +             iommu_alloc_page(iommu, &paddr);
> +             pte[idx].val = paddr | flags;
> +             iommu_flush_cache(iommu, &pte[idx], sizeof(pte[idx]));
> +     }
> +     return (void *)PMAP_DIRECT_MAP((pte[idx].val & PTE_PADDR_MASK));
> +}
> + 
> +void
> +domain_map_page_amd(struct domain *dom, vaddr_t va, paddr_t pa, uint64_t 
> flags)
> +{
> +     struct pte_entry *pte;
> +     struct iommu_softc *iommu;
> +     int idx;
> +
> +     iommu = dom->iommu;
> +     /* Insert physical address into virtual address map
> +      * XXX: could we use private pmap here?
> +      * essentially doing a pmap_enter(map, va, pa, prot);
> +      */
> +
> +     /* Always assume AMD levels=4                           */
> +     /*        39        30        21        12              */
> +     /* ---------|---------|---------|---------|------------ */
> +     pte = dom->pte;
> +     pte = pte_lvl(iommu, pte, va, 30, PTE_NXTLVL(2) | PTE_IR | PTE_IW | 
> PTE_P);
> +     pte = pte_lvl(iommu, pte, va, 21, PTE_NXTLVL(1) | PTE_IR | PTE_IW | 
> PTE_P);
> +     if (flags)
> +             flags = PTE_P | PTE_R | PTE_W | PTE_IW | PTE_IR | PTE_NXTLVL(0);
> +
> +     /* Level 1: Page Table - add physical address */
> +     idx = (va >> 12) & 0x1FF;
> +     pte[idx].val = pa | flags;
> +
> +     iommu_flush_cache(iommu, pte, sizeof(*pte));
> +}
> +
> +static void
> +dmar_dumpseg(bus_dma_tag_t tag, int nseg, bus_dma_segment_t *segs,
> +    const char *lbl)
> +{
> +     struct domain *dom = tag->_cookie;
> +     int i;
> +
> +     return;
> +     if (!debugme(dom))
> +             return;
> +     printf("%s: %s\n", lbl, dom_bdf(dom));
> +     for (i = 0; i < nseg; i++) {
> +             printf("  %.16llx %.8x\n",
> +                 (uint64_t)segs[i].ds_addr,
> +                 (uint32_t)segs[i].ds_len);
> +     }
> +}
> +
> +/* 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;
> +
> +     if (iommu_bad(dom->iommu)) {
> +             printf("unload map no iommu\n");
> +             return;
> +     }
> +
> +     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);
> +             }
> +
> +             /* Clear PTE */
> +             for (idx = 0; idx < alen; idx += VTD_PAGE_SIZE)
> +                     domain_map_page(dom, base + idx, 0, 0);
> +
> +             if (dom->flag & DOM_NOMAP) {
> +                     printf("%s: nomap %.16llx\n", dom_bdf(dom), 
> (uint64_t)base);
> +                     continue;
> +             }               
> +
> +             mtx_enter(&dom->exlck);
> +             if (extent_free(dom->iovamap, base, alen, EX_NOWAIT)) {
> +                     panic("domain_unload_map: extent_free");
> +             }
> +             mtx_leave(&dom->exlck);
> +     }
> +}
> +
> +/* map.segs[x].ds_addr is modified to IOMMU virtual PA */
> +void
> +domain_load_map(struct domain *dom, bus_dmamap_t map, int flags, int 
> pteflag, 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;
> +
> +     iommu = dom->iommu;
> +     if (!iommu_enabled(iommu)) {
> +             /* Lazy enable translation when required */
> +             if (iommu_enable_translation(iommu, 1)) {
> +                     return;
> +             }
> +     }
> +     domain_map_check(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;
> +             res  = base;
> +
> +             if (dom->flag & DOM_NOMAP) {
> +                     goto nomap;
> +             }
> +
> +             /* Allocate DMA Virtual Address */
> +             mtx_enter(&dom->exlck);
> +             if (extent_alloc(dom->iovamap, alen, VTD_PAGE_SIZE, 0,
> +                 map->_dm_boundary, EX_NOWAIT, &res)) {
> +                     panic("domain_load_map: extent_alloc");
> +             }
> +             if (res == -1) {
> +                     panic("got -1 address\n");
> +             }
> +             mtx_leave(&dom->exlck);
> +
> +             /* Reassign DMA address */
> +             seg->ds_addr = res | (seg->ds_addr & VTD_PAGE_MASK);
> +nomap:
> +             if (debugme(dom)) {
> +                     printf("  LOADMAP: %.16llx %x => %.16llx\n",
> +                         (uint64_t)seg->ds_addr, (uint32_t)seg->ds_len,
> +                         (uint64_t)res);
> +             }
> +             for (idx = 0; idx < alen; idx += VTD_PAGE_SIZE) {
> +                     domain_map_page(dom, res + idx, base + idx,
> +                         PTE_P | pteflag);
> +             }
> +     }
> +     if ((iommu->cap & CAP_CM) || acpidmar_force_cm) {
> +             iommu_flush_tlb(iommu, IOTLB_DOMAIN, dom->did);
> +     } else {
> +             iommu_flush_write_buffer(iommu);
> +     }
> +}
> +
> +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);
> +}
> +
> +/* Bus DMA Map functions */
> +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)
> +{
> +     int rc;
> +
> +     rc = _bus_dmamap_create(tag, size, nsegments, maxsegsz, boundary,
> +         flags, dmamp);
> +     if (!rc) {
> +             dmar_dumpseg(tag, (*dmamp)->dm_nsegs, (*dmamp)->dm_segs,
> +                 __FUNCTION__);
> +     }
> +     return (rc);
> +}
> +
> +static void
> +dmar_dmamap_destroy(bus_dma_tag_t tag, bus_dmamap_t dmam)
> +{
> +     dmar_dumpseg(tag, dmam->dm_nsegs, dmam->dm_segs, __FUNCTION__);
> +     _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) {
> +             dmar_dumpseg(tag, dmam->dm_nsegs, dmam->dm_segs,
> +                 __FUNCTION__);
> +             domain_load_map(dom, dmam, flags, PTE_R|PTE_W, __FUNCTION__);
> +             dmar_dumpseg(tag, dmam->dm_nsegs, dmam->dm_segs,
> +                 __FUNCTION__);
> +     }
> +     return (rc);
> +}
> +
> +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) {
> +             dmar_dumpseg(tag, dmam->dm_nsegs, dmam->dm_segs,
> +                 __FUNCTION__);
> +             domain_load_map(dom, dmam, flags, PTE_R|PTE_W,__FUNCTION__);
> +             dmar_dumpseg(tag, dmam->dm_nsegs, dmam->dm_segs,
> +                 __FUNCTION__);
> +     }
> +     return (rc);
> +}
> +
> +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) {
> +             dmar_dumpseg(tag, dmam->dm_nsegs, dmam->dm_segs,
> +                 __FUNCTION__);
> +             domain_load_map(dom, dmam, flags, PTE_R|PTE_W, __FUNCTION__);
> +             dmar_dumpseg(tag, dmam->dm_nsegs, dmam->dm_segs,
> +                 __FUNCTION__);
> +     }
> +     return (rc);
> +}
> +
> +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) {
> +             dmar_dumpseg(tag, dmam->dm_nsegs, dmam->dm_segs,
> +                 __FUNCTION__);
> +             domain_load_map(dom, dmam, flags, PTE_R|PTE_W, __FUNCTION__);
> +             dmar_dumpseg(tag, dmam->dm_nsegs, dmam->dm_segs,
> +                 __FUNCTION__);
> +     }
> +     return (rc);
> +}
> +
> +static void
> +dmar_dmamap_unload(bus_dma_tag_t tag, bus_dmamap_t dmam)
> +{
> +     struct domain *dom = tag->_cookie;
> +
> +     dmar_dumpseg(tag, dmam->dm_nsegs, dmam->dm_segs, __FUNCTION__);
> +     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)
> +{
> +#if 0
> +     struct domain *dom = tag->_cookie;
> +     int             flag;
> +
> +     flag = PTE_P;
> +     if (ops == BUS_DMASYNC_PREREAD) {
> +             /* make readable */
> +             flag |= PTE_R;
> +     }
> +     else if (ops == BUS_DMASYNC_PREWRITE) {
> +             /* make writeable */
> +             flag |= PTE_W;
> +     }
> +     dmar_dumpseg(tag, dmam->dm_nsegs, dmam->dm_segs, __FUNCTION__);
> +#endif
> +     _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)
> +{
> +     int rc;
> +
> +     rc = _bus_dmamem_alloc(tag, size, alignment, boundary, segs, nsegs,
> +         rsegs, flags);
> +     if (!rc) {
> +             dmar_dumpseg(tag, *rsegs, segs, __FUNCTION__);
> +     }
> +     return (rc);
> +}
> +
> +static void
> +dmar_dmamem_free(bus_dma_tag_t tag, bus_dma_segment_t *segs, int nsegs)
> +{
> +     dmar_dumpseg(tag, nsegs, segs, __FUNCTION__);
> +     _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)
> +{
> +     dmar_dumpseg(tag, nsegs, segs, __FUNCTION__);
> +     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)
> +{
> +     dmar_dumpseg(tag, nsegs, segs, __FUNCTION__);
> +     return (_bus_dmamem_mmap(tag, segs, nsegs, off, prot, flags));
> +}
> +
> +/*===================================
> + * IOMMU code
> + *===================================*/
> +
> +/* Intel: Set Context Root Address */
> +void
> +iommu_set_rtaddr(struct iommu_softc *iommu, paddr_t paddr)
> +{
> +     int i, sts;
> +
> +     mtx_enter(&iommu->reg_lock);
> +     iommu_write_8(iommu, DMAR_RTADDR_REG, paddr);
> +     iommu_write_4(iommu, DMAR_GCMD_REG, iommu->gcmd | GCMD_SRTP);
> +     for (i = 0; i < 5; i++) {
> +             sts = iommu_read_4(iommu, DMAR_GSTS_REG);
> +             if (sts & GSTS_RTPS)
> +                     break;
> +     }
> +     mtx_leave(&iommu->reg_lock);
> +
> +     if (i == 5) {
> +             printf("set_rtaddr fails\n");
> +     }
> +}
> +
> +/* Allocate contiguous memory (1Mb) for the Device Table Entries */
> +void *
> +iommu_alloc_hwdte(struct acpidmar_softc *sc, size_t size, paddr_t *paddr)
> +{
> +     caddr_t vaddr;
> +     bus_dmamap_t map;
> +     bus_dma_segment_t seg;
> +     bus_dma_tag_t dmat;
> +     int rc, nsegs;
> +
> +     rc = _bus_dmamap_create(dmat, size, 1, size, 0, 
> +             BUS_DMA_NOWAIT, &map);
> +     if (rc != 0) {
> +             printf("hwdte_create fails\n");
> +             return NULL;
> +     }
> +     rc = _bus_dmamem_alloc(dmat, size, 4, 0, &seg, 1,
> +             &nsegs, BUS_DMA_NOWAIT | BUS_DMA_ZERO);
> +     if (rc != 0) {
> +             printf("hwdte alloc fails\n");
> +             return NULL;
> +     }
> +     rc = _bus_dmamem_map(dmat, &seg, 1, size, &vaddr,
> +             BUS_DMA_NOWAIT | BUS_DMA_COHERENT);
> +     if (rc != 0) {
> +             printf("hwdte map fails\n");
> +             return NULL;
> +     }
> +     rc = _bus_dmamap_load_raw(dmat, map, &seg, 1, size, BUS_DMA_NOWAIT);
> +     if (rc != 0) {
> +             printf("hwdte load raw fails\n");
> +             return NULL;
> +     }
> +     *paddr = map->dm_segs[0].ds_addr;
> +     return vaddr;
> +}
> +
> +/* COMMON: Allocate a new memory page */
> +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);
> +}
> +
> +
> +/* Intel: Issue command via queued invalidation */
> +void
> +iommu_issue_qi(struct iommu_softc *iommu, struct qi_entry *qi)
> +{
> +#if 0
> +     struct qi_entry *pi, *pw;
> +
> +     idx = iommu->qi_head;
> +     pi = &iommu->qi[idx];
> +     pw = &iommu->qi[(idx+1) % MAXQ];
> +     iommu->qi_head = (idx+2) % MAXQ;
> +
> +     memcpy(pw, &qi, sizeof(qi));
> +     issue command;
> +     while (pw->xxx)
> +             ;
> +#endif
> +}
> +
> +/* Intel: Flush TLB entries, Queued Invalidation mode */
> +void
> +iommu_flush_tlb_qi(struct iommu_softc *iommu, int mode, int did)
> +{
> +     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;
> +     iommu_issue_qi(iommu, &qi);
> +}
> +
> +/* Intel: Flush Context entries, Queued Invalidation mode */
> +void
> +iommu_flush_ctx_qi(struct iommu_softc *iommu, int mode, int did,
> +    int sid, int fm)
> +{
> +     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;
> +     }
> +     iommu_issue_qi(iommu, &qi);
> +}
> +
> +/* Intel: Flush write buffers */
> +void
> +iommu_flush_write_buffer(struct iommu_softc *iommu)
> +{
> +     int i, sts;
> +
> +     if (iommu->dte)
> +             return;
> +     if (!(iommu->cap & CAP_RWBF))
> +             return;
> +     DPRINTF(1,"writebuf\n");
> +     iommu_write_4(iommu, DMAR_GCMD_REG, iommu->gcmd | GCMD_WBF);
> +     for (i = 0; i < 5; i++) {
> +             sts = iommu_read_4(iommu, DMAR_GSTS_REG);
> +             if (sts & GSTS_WBFS)
> +                     break;
> +             delay(10000);
> +     }
> +     if (i == 5) {
> +             printf("write buffer flush fails\n");
> +     }
> +}
> +
> +void
> +iommu_flush_cache(struct iommu_softc *iommu, void *addr, size_t size)
> +{
> +     if (iommu->dte) {
> +             pmap_flush_cache((vaddr_t)addr, size);
> +             return;
> +     }
> +     if (!(iommu->ecap & ECAP_C))
> +             pmap_flush_cache((vaddr_t)addr, size);
> +}
> +
> +/*
> + * Intel: Flush IOMMU TLB Entries
> + * Flushing can occur globally, per domain or per page 
> + */
> +void
> +iommu_flush_tlb(struct iommu_softc *iommu, int mode, int did)
> +{
> +     int             n;
> +     uint64_t        val;
> +
> +     /* Call AMD */
> +     if (iommu->dte) {
> +             ivhd_invalidate_domain(iommu, did);
> +             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;
> +     }
> +
> +     /* Check for Read/Write Drain */
> +     if (iommu->cap & CAP_DRD)
> +             val |= IOTLB_DR;
> +     if (iommu->cap & CAP_DWD)
> +             val |= IOTLB_DW;
> +
> +     mtx_enter(&iommu->reg_lock);
> +
> +     iommu_write_8(iommu, DMAR_IOTLB_REG(iommu), val);
> +     n = 0;
> +     do {
> +             val = iommu_read_8(iommu, DMAR_IOTLB_REG(iommu));
> +     } while (n++ < 5 && val & IOTLB_IVT);
> +
> +     mtx_leave(&iommu->reg_lock);
> +}
> +
> +/* Intel: Flush IOMMU settings
> + * Flushes can occur globally, per domain, or per device
> + */
> +void
> +iommu_flush_ctx(struct iommu_softc *iommu, int mode, int did, int sid, int 
> fm)
> +{
> +     uint64_t        val;
> +     int             n;
> +
> +     if (iommu->dte)
> +             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_write_8(iommu, DMAR_CCMD_REG, val);
> +     do {
> +             val = iommu_read_8(iommu, DMAR_CCMD_REG);
> +     } while (n++ < 5 && val & CCMD_ICC);
> +
> +     mtx_leave(&iommu->reg_lock);
> +}
> +
> +/* Intel: Enable Queued Invalidation */
> +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_write_4(iommu, DMAR_GCMD_REG, iommu->gcmd);
> +             do {
> +                     sts = iommu_read_4(iommu, DMAR_GSTS_REG);
> +             } while (n++ < 5 && !(sts & GSTS_QIES));
> +
> +             mtx_leave(&iommu->reg_lock);
> +
> +             DPRINTF(1,"set.qie: %d\n", n);
> +     } else {
> +             iommu->gcmd &= ~GCMD_QIE;
> +
> +             mtx_enter(&iommu->reg_lock);
> +
> +             iommu_write_4(iommu, DMAR_GCMD_REG, iommu->gcmd);
> +             do {
> +                     sts = iommu_read_4(iommu, DMAR_GSTS_REG);
> +             } while (n++ < 5 && sts & GSTS_QIES);
> +
> +             mtx_leave(&iommu->reg_lock);
> +
> +             DPRINTF(1,"clr.qie: %d\n", n);
> +     }
> +}
> +
> +/* Intel: Enable IOMMU translation */
> +int
> +iommu_enable_translation(struct iommu_softc *iommu, int enable)
> +{
> +     uint32_t        sts;
> +     uint64_t        reg;
> +     int             n = 0;
> +
> +     if (iommu->dte)
> +             return (0);
> +     reg = 0;
> +     if (enable) {
> +             DPRINTF(0,"enable iommu %d\n", iommu->id);
> +             iommu_showcfg(iommu, -1);
> +
> +             iommu->gcmd |= GCMD_TE;
> +
> +             /* Enable translation */
> +             printf(" pre tes: ");
> +
> +             mtx_enter(&iommu->reg_lock);
> +             iommu_write_4(iommu, DMAR_GCMD_REG, iommu->gcmd);
> +             printf("xxx");
> +             do {
> +                     printf("yyy");
> +                     sts = iommu_read_4(iommu, DMAR_GSTS_REG);
> +                     delay(n * 10000);
> +             } 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_write_4(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_write_4(iommu, DMAR_GCMD_REG, iommu->gcmd);
> +             do {
> +                     sts = iommu_read_4(iommu, DMAR_GSTS_REG);
> +             } while (n++ < 5 && sts & GSTS_TES);
> +             mtx_leave(&iommu->reg_lock);
> +
> +             printf(" clr.tes: %d\n", n);
> +     }
> +
> +     return (0);
> +}
> +
> +/* Intel: Initialize IOMMU */
> +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_read_8(iommu, DMAR_CAP_REG);
> +     iommu->ecap = iommu_read_8(iommu, DMAR_ECAP_REG);
> +     iommu->ndoms = cap_nd(iommu->cap);
> +
> +     /* Print Capabilities & Extended Capabilities */
> +     DPRINTF(0, "  caps: %s%s%s%s%s%s%s%s%s%s%s\n",
> +         iommu->cap & CAP_AFL ? "afl " : "",         /* adv fault */
> +         iommu->cap & CAP_RWBF ? "rwbf " : "",       /* write-buffer flush */
> +         iommu->cap & CAP_PLMR ? "plmr " : "",       /* protected lo region 
> */
> +         iommu->cap & CAP_PHMR ? "phmr " : "",       /* protected hi region 
> */
> +         iommu->cap & CAP_CM ? "cm " : "",           /* caching mode */
> +         iommu->cap & CAP_ZLR ? "zlr " : "",         /* zero-length read */
> +         iommu->cap & CAP_PSI ? "psi " : "",         /* page invalidate */
> +         iommu->cap & CAP_DWD ? "dwd " : "",         /* write drain */
> +         iommu->cap & CAP_DRD ? "drd " : "",         /* read drain */
> +         iommu->cap & CAP_FL1GP ? "Gb " : "",        /* 1Gb pages */
> +         iommu->cap & CAP_PI ? "pi " : "");          /* posted interrupts */
> +     DPRINTF(0, "  ecap: %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
> +         iommu->ecap & ECAP_C ? "c " : "",           /* coherent */
> +         iommu->ecap & ECAP_QI ? "qi " : "",         /* queued invalidate */
> +         iommu->ecap & ECAP_DT ? "dt " : "",         /* device iotlb */
> +         iommu->ecap & ECAP_IR ? "ir " : "",         /* intr remap */
> +         iommu->ecap & ECAP_EIM ? "eim " : "",       /* x2apic */
> +         iommu->ecap & ECAP_PT ? "pt " : "",         /* passthrough */
> +         iommu->ecap & ECAP_SC ? "sc " : "",         /* snoop control */
> +         iommu->ecap & ECAP_ECS ? "ecs " : "",       /* extended context */
> +         iommu->ecap & ECAP_MTS ? "mts " : "",       /* memory type */
> +         iommu->ecap & ECAP_NEST ? "nest " : "",     /* nested translations 
> */
> +         iommu->ecap & ECAP_DIS ? "dis " : "",       /* deferred 
> invalidation */
> +         iommu->ecap & ECAP_PASID ? "pas " : "",     /* pasid */
> +         iommu->ecap & ECAP_PRS ? "prs " : "",       /* page request */
> +         iommu->ecap & ECAP_ERS ? "ers " : "",       /* execute request */
> +         iommu->ecap & ECAP_SRS ? "srs " : "",       /* supervisor request */
> +         iommu->ecap & ECAP_NWFS ? "nwfs " : "",     /* no write flag */
> +         iommu->ecap & ECAP_EAFS ? "eafs " : "");    /* extended accessed 
> flag */
> +
> +     mtx_init(&iommu->reg_lock, IPL_HIGH);
> +
> +     /* Clear Interrupt Masking */
> +     iommu_write_4(iommu, DMAR_FSTS_REG, FSTS_PFO | FSTS_PPF);
> +
> +     iommu->intr = acpidmar_intr_establish(iommu, IPL_HIGH,
> +         acpidmar_intr, iommu, "dmarintr");
> +
> +     /* Enable interrupts */
> +     sts = iommu_read_4(iommu, DMAR_FECTL_REG);
> +     iommu_write_4(iommu, DMAR_FECTL_REG, sts & ~FECTL_IM);
> +
> +     /* Allocate root pointer */
> +     iommu->root = iommu_alloc_page(iommu, &paddr);
> +     DPRINTF(0, "Allocated root pointer: pa:%.16llx va:%p\n",
> +         (uint64_t)paddr, iommu->root);
> +     iommu->rtaddr = paddr;
> +     iommu_flush_write_buffer(iommu);
> +     iommu_set_rtaddr(iommu, paddr);
> +
> +#if 0
> +     if (iommu->ecap & ECAP_QI) {
> +             /* Queued Invalidation support */
> +             iommu->qi = iommu_alloc_page(iommu, &iommu->qip);
> +             iommu_write_8(iommu, DMAR_IQT_REG, 0);
> +             iommu_write_8(iommu, DMAR_IQA_REG, iommu->qip | IQA_QS_256);
> +     }
> +     if (iommu->ecap & ECAP_IR) {
> +             /* Interrupt remapping support */
> +             iommu_write_8(iommu, DMAR_IRTA_REG, 0);
> +     }
> +#endif
> +
> +     /* Calculate guest address width and supported guest widths */
> +     gaw = -1;
> +     iommu->mgaw = cap_mgaw(iommu->cap);
> +     DPRINTF(0, "gaw: %d { ", iommu->mgaw);
> +     for (i = 0; i < 5; i++) {
> +             if (cap_sagaw(iommu->cap) & (1L << i)) {
> +                     gaw = VTD_LEVELTOAW(i);
> +                     DPRINTF(0, "%d ", gaw);
> +                     iommu->agaw = gaw;
> +             }
> +     }
> +     DPRINTF(0, "}\n");
> +
> +     /* Cache current status register bits */
> +     sts = iommu_read_4(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;
> +     DPRINTF(0, "gcmd: %x preset\n", iommu->gcmd);
> +     acpidmar_intr(iommu);
> +     return (0);
> +}
> +
> +/* Read/Write IOMMU register */
> +uint32_t
> +iommu_read_4(struct iommu_softc *iommu, int reg)
> +{
> +     uint32_t        v;
> +
> +     v = bus_space_read_4(iommu->iot, iommu->ioh, reg);
> +     return (v);
> +}
> +
> +
> +void
> +iommu_write_4(struct iommu_softc *iommu, int reg, uint32_t v)
> +{
> +     bus_space_write_4(iommu->iot, iommu->ioh, reg, (uint32_t)v);
> +}
> +
> +uint64_t
> +iommu_read_8(struct iommu_softc *iommu, int reg)
> +{
> +     uint64_t        v;
> +
> +     v = bus_space_read_8(iommu->iot, iommu->ioh, reg);
> +     return (v);
> +}
> +
> +void
> +iommu_write_8(struct iommu_softc *iommu, int reg, uint64_t v)
> +{
> +     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;
> +     int gaw;
> +
> +     DPRINTF(0, "iommu%d: create domain: %.4x\n", iommu->id, did);
> +     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;       /* nop */
> +     dom->dmat._dmamap_destroy   = dmar_dmamap_destroy;      /* nop */
> +     dom->dmat._dmamap_load      = dmar_dmamap_load;         /* lm */
> +     dom->dmat._dmamap_load_mbuf = dmar_dmamap_load_mbuf;    /* lm */
> +     dom->dmat._dmamap_load_uio  = dmar_dmamap_load_uio;     /* lm */
> +     dom->dmat._dmamap_load_raw  = dmar_dmamap_load_raw;     /* lm */
> +     dom->dmat._dmamap_unload    = dmar_dmamap_unload;       /* um */
> +     dom->dmat._dmamap_sync      = dmar_dmamap_sync;         /* lm */
> +     dom->dmat._dmamem_alloc     = dmar_dmamem_alloc;        /* nop */
> +     dom->dmat._dmamem_free      = dmar_dmamem_free;         /* nop */
> +     dom->dmat._dmamem_map       = dmar_dmamem_map;          /* nop */
> +     dom->dmat._dmamem_unmap     = dmar_dmamem_unmap;        /* nop */
> +     dom->dmat._dmamem_mmap      = dmar_dmamem_mmap;
> +
> +     snprintf(dom->exname, sizeof(dom->exname), "did:%x.%.4x",
> +         iommu->id, dom->did);
> +
> +     /* Setup IOMMU address map */
> +     gaw = min(iommu->agaw, iommu->mgaw);
> +     dom->iovamap = extent_create(dom->exname, 1024*1024*16,
> +         (1LL << gaw)-1,
> +         M_DEVBUF, NULL, 0,
> +         EX_WAITOK|EX_NOCOALESCE);
> +
> +     /* Zero out MSI Interrupt region */
> +     extent_alloc_region(dom->iovamap, MSI_BASE_ADDRESS, MSI_BASE_SIZE,
> +         EX_WAITOK);
> +     mtx_init(&dom->exlck, IPL_HIGH);
> +
> +     TAILQ_INSERT_TAIL(&iommu->domains, dom, link);
> +
> +     return dom;
> +}
> +
> +void
> +domain_add_device(struct domain *dom, int sid)
> +{    
> +     struct domain_dev *ddev;
> +     
> +     DPRINTF(0, "add %s to iommu%d.%.4x\n", dmar_bdf(sid), dom->iommu->id, 
> dom->did);
> +     ddev = malloc(sizeof(*ddev), M_DEVBUF, M_ZERO | M_WAITOK);
> +     ddev->sid = sid;
> +     TAILQ_INSERT_TAIL(&dom->devices, ddev, link);
> +
> +     /* Should set context entry here?? */
> +}
> +
> +void
> +domain_remove_device(struct domain *dom, int sid)
> +{
> +     struct domain_dev *ddev, *tmp;
> +
> +     TAILQ_FOREACH_SAFE(ddev, &dom->devices, link, tmp) {
> +             if (ddev->sid == sid) {
> +                     TAILQ_REMOVE(&dom->devices, ddev, link);
> +                     free(ddev, sizeof(*ddev), M_DEVBUF);
> +             }
> +     }
> +}
> +
> +/* 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_dev       *ddev;
> +     struct domain           *dom;
> +     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 */
> +     domain_add_device(dom, sid);
> +
> +     return dom;
> +}
> +
> +/* Map Guest Pages into IOMMU */
> +void
> +_iommu_map(void *dom, vaddr_t va, bus_addr_t gpa, bus_size_t len)
> +{
> +     bus_size_t i;
> +     paddr_t hpa;
> +
> +     if (dom == NULL) {
> +             return;
> +     }
> +     DPRINTF(1, "Mapping dma: %lx = %lx/%lx\n", va, gpa, len);
> +     for (i = 0; i < len; i += PAGE_SIZE) {
> +             hpa = 0;
> +             pmap_extract(curproc->p_vmspace->vm_map.pmap, va, &hpa);
> +             domain_map_page(dom, gpa, hpa, PTE_P | PTE_R | PTE_W);
> +             gpa += PAGE_SIZE;
> +             va  += PAGE_SIZE;
> +     }
> +}
> +
> +/* Find IOMMU for a given PCI device */
> +void
> +*_iommu_domain(int segment, int bus, int dev, int func, int *id)
> +{
> +     struct domain *dom;
> +
> +     dom = domain_lookup(acpidmar_sc, segment, mksid(bus, dev, func));
> +     if (dom) {
> +             *id = dom->did;
> +     }
> +     return dom;
> +}
> +
> +void
> +domain_map_device(struct domain *dom, int sid);
> +
> +void
> +domain_map_device(struct domain *dom, int sid)
> +{
> +     struct iommu_softc      *iommu;
> +     struct context_entry    *ctx;
> +     paddr_t                 paddr;
> +     int                     bus, devfn;
> +     int                     tt, lvl;
> +
> +     iommu = dom->iommu;
> +
> +     bus = sid_bus(sid);
> +     devfn = sid_devfn(sid);
> +     /* AMD attach device */
> +     if (iommu->dte) {
> +             struct ivhd_dte *dte = &iommu->dte[sid];
> +             if (!dte->dw0) {
> +                     /* Setup Device Table Entry: bus.devfn */
> +                     DPRINTF(1, "@@@ PCI Attach: %.4x[%s] %.4x\n", sid, 
> dmar_bdf(sid), dom->did);
> +                     dte_set_host_page_table_root_ptr(dte, dom->ptep);
> +                     dte_set_domain(dte, dom->did);
> +                     dte_set_mode(dte, 3);  /* Set 3 level PTE */
> +                     dte_set_tv(dte);
> +                     dte_set_valid(dte);
> +                     ivhd_flush_devtab(iommu, dom->did);
> +#ifdef IOMMU_DEBUG
> +                     //ivhd_showreg(iommu);
> +                     ivhd_showdte(iommu);
> +#endif
> +             }
> +             return;
> +     }
> +
> +     /* 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(0, "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 = VTD_AWTOLEVEL(iommu->agaw);
> +
> +             /* 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));
> +             if ((iommu->cap & CAP_CM) || acpidmar_force_cm) {
> +                     iommu_flush_ctx(iommu, CTX_DEVICE, dom->did, sid, 0);
> +                     iommu_flush_tlb(iommu, IOTLB_GLOBAL, 0);
> +             } else {
> +                     iommu_flush_write_buffer(iommu);
> +             }
> +             DPRINTF(0, "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);
> +     }
> +}
> +
> +struct domain *
> +acpidmar_pci_attach(struct acpidmar_softc *sc, int segment, int sid, int 
> mapctx)
> +{
> +     static struct domain    *dom;
> +
> +     dom = domain_lookup(sc, segment, sid);
> +     if (!dom) {
> +             printf("no domain: %s\n", dmar_bdf(sid));
> +             return NULL;
> +     }
> +
> +     if (mapctx) {
> +             domain_map_device(dom, sid);
> +     }
> +
> +     return dom;
> +}
> +
> +void
> +acpidmar_pci_hook(pci_chipset_tag_t pc, struct pci_attach_args *pa)
> +{
> +     int             bus, dev, fun, sid;
> +     struct domain   *dom;
> +     pcireg_t        reg;
> +
> +     if (!acpidmar_sc) {
> +             /* No DMAR, ignore */
> +             return;
> +     }
> +
> +     /* Add device to our list if valid */
> +     pci_decompose_tag(pc, pa->pa_tag, &bus, &dev, &fun);
> +     sid = mksid(bus, dev, fun);
> +     if (sid_flag[sid] & SID_INVALID)
> +             return;
> +
> +     reg = pci_conf_read(pc, pa->pa_tag, PCI_CLASS_REG);
> +
> +     /* Add device to domain */
> +     dom = acpidmar_pci_attach(acpidmar_sc, pa->pa_domain, sid, 0);
> +     if (dom == NULL)
> +             return;
> +
> +     if (PCI_CLASS(reg) == PCI_CLASS_DISPLAY &&
> +         PCI_SUBCLASS(reg) == PCI_SUBCLASS_DISPLAY_VGA) {
> +             dom->flag = DOM_NOMAP;
> +     }
> +     if (PCI_CLASS(reg) == PCI_CLASS_BRIDGE &&
> +         PCI_SUBCLASS(reg) == PCI_SUBCLASS_BRIDGE_ISA) {
> +             /* For ISA Bridges, map 0-16Mb as 1:1 */
> +             printf("dmar: %.4x:%.2x:%.2x.%x mapping ISA\n",
> +                 pa->pa_domain, bus, dev, fun);
> +             domain_map_pthru(dom, 0x00, 16*1024*1024);
> +     }
> +
> +     /* 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 - sizeof(*ds);
> +             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);
> +
> +             DPRINTF(1, "  %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++) {
> +                     DPRINTF(1, " %2x.%x ",
> +                         d->dp[i].device,
> +                         d->dp[i].function);
> +             }
> +             DPRINTF(1, "}\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, *jm;
> +     uint64_t                start, end;
> +
> +     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;
> +             /* Search for adjacent reserved regions */
> +             start = im->addr;
> +             end   = im->addr+im->size;
> +             for (jm = im+1; jm->type == BIOS_MAP_RES && end == jm->addr;
> +                 jm++) {
> +                     end = jm->addr+jm->size;
> +             }
> +             printf("e820: %.16llx - %.16llx\n", start, end);
> +             if (start <= rmrr->start && rmrr->end <= end) {
> +                     /* Bah.. some buggy BIOS stomp outside RMRR */
> +                     printf("  ** inside E820 Reserved %.16llx %.16llx\n",
> +                         start, end);
> +                     rmrr->start = trunc_page(start);
> +                     rmrr->end   = round_page(end);
> +                     break;
> +             }
> +     }
> +     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;
> +
> +     domain_map_page = domain_map_page_intel;
> +     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;
> +     }
> +
> +     /* Pre-create domains for iommu devices */
> +     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);
> +                     dom = acpidmar_pci_attach(sc, iommu->segment, sid, 0);
> +                     if (dom != NULL) {
> +                             printf("%.4x:%.2x:%.2x.%x iommu:%d did:%.4x\n",
> +                                 iommu->segment, dl->bus, dl->dp[0].device, 
> dl->dp[0].function,
> +                                 iommu->id, dom->did);
> +                     }
> +             }
> +     }
> +     /* 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, 0);
> +                     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);
> +                     }
> +             }
> +     }
> +}
> +
> +
> +/*=====================================================
> + * AMD Vi
> + *=====================================================*/
> +void         acpiivrs_ivhd(struct acpidmar_softc *, struct acpi_ivhd *);
> +int  ivhd_iommu_init(struct acpidmar_softc *, struct iommu_softc *,
> +             struct acpi_ivhd *);
> +int  _ivhd_issue_command(struct iommu_softc *, const struct ivhd_command *);
> +void         ivhd_show_event(struct iommu_softc *, struct ivhd_event *evt, 
> int);
> +int  ivhd_issue_command(struct iommu_softc *, const struct ivhd_command *, 
> int);
> +int  ivhd_invalidate_domain(struct iommu_softc *, int);
> +void         ivhd_intr_map(struct iommu_softc *, int);
> +void         ivhd_checkerr(struct iommu_softc *iommu);
> +int  acpiivhd_intr(void *);
> +
> +int
> +acpiivhd_intr(void *ctx)
> +{
> +     struct iommu_softc *iommu = ctx;
> +
> +     if (!iommu->dte)
> +             return (0);
> +     ivhd_poll_events(iommu);
> +     return (1);
> +}
> +
> +/* Setup interrupt for AMD */
> +void
> +ivhd_intr_map(struct iommu_softc *iommu, int devid) {
> +     pci_intr_handle_t ih;
> +
> +     if (iommu->intr)
> +             return;
> +     ih.tag = pci_make_tag(NULL, sid_bus(devid), sid_dev(devid), 
> sid_fun(devid));
> +     ih.line = APIC_INT_VIA_MSG;
> +     ih.pin = 0;
> +     iommu->intr = pci_intr_establish(NULL, ih, IPL_NET | IPL_MPSAFE,
> +                             acpiivhd_intr, iommu, "amd_iommu");
> +     printf("amd iommu intr: %p\n", iommu->intr);
> +}
> +
> +void
> +_dumppte(struct pte_entry *pte, int lvl, vaddr_t va)
> +{
> +     char *pfx[] = { "    ", "   ", "  ", " ", "" };
> +     uint64_t i, sh;
> +     struct pte_entry *npte;
> +  
> +     for (i = 0; i < 512; i++) {
> +             sh = (i << (((lvl-1) * 9) + 12));
> +             if (pte[i].val & PTE_P) {
> +                     if (lvl > 1) {
> +                             npte = (void *)PMAP_DIRECT_MAP((pte[i].val & 
> PTE_PADDR_MASK));
> +                             printf("%slvl%d: %.16llx nxt:%llu\n", pfx[lvl], 
> lvl, 
> +                                 pte[i].val, (pte[i].val >> 9) & 7);
> +                             _dumppte(npte, lvl-1, va | sh);
> +                     }
> +                     else { 
> +                             printf("%slvl%d: %.16llx <- %.16llx \n", 
> pfx[lvl], lvl,
> +                                 pte[i].val, va | sh);
> +                     }
> +             }
> +     }
> +}
> +
> +void
> +ivhd_showpage(struct iommu_softc *iommu, int sid, paddr_t paddr)
> +{
> +     struct domain *dom;
> +     static int show = 0;
> +
> +     if (show > 10)
> +             return;
> +     show++;
> +     dom = acpidmar_pci_attach(acpidmar_sc, 0, sid, 0);
> +     if (!dom)
> +             return;
> +     printf("DTE: %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n",
> +             iommu->dte[sid].dw0,
> +             iommu->dte[sid].dw1,
> +             iommu->dte[sid].dw2,
> +             iommu->dte[sid].dw3,
> +             iommu->dte[sid].dw4,
> +             iommu->dte[sid].dw5,
> +             iommu->dte[sid].dw6,
> +             iommu->dte[sid].dw7);
> +     _dumppte(dom->pte, 3, 0);
> +}
> +
> +/* Display AMD IOMMU Error */
> +void
> +ivhd_show_event(struct iommu_softc *iommu, struct ivhd_event *evt, int head)
> +{
> +     int type, sid, did, flag;
> +     uint64_t address;
> +
> +     /* Get Device, Domain, Address and Type of event */     
> +     sid  = __EXTRACT(evt->dw0, EVT_SID);
> +     type = __EXTRACT(evt->dw1, EVT_TYPE);
> +     did  = __EXTRACT(evt->dw1, EVT_DID);
> +     flag = __EXTRACT(evt->dw1, EVT_FLAG);
> +     address = _get64(&evt->dw2);
> +
> +     printf("=== IOMMU Error[%.4x]: ", head);
> +     switch (type) {
> +     case ILLEGAL_DEV_TABLE_ENTRY:
> +             printf("illegal dev table entry dev=%s addr=0x%.16llx %s, %s, 
> %s, %s\n",
> +                dmar_bdf(sid), address,
> +                evt->dw1 & EVT_TR ? "translation" : "transaction",
> +                evt->dw1 & EVT_RZ ? "reserved bit" : "invalid level",
> +                evt->dw1 & EVT_RW ? "write" : "read",
> +                evt->dw1 & EVT_I  ? "interrupt" : "memory");
> +             ivhd_showdte(iommu);
> +             break;
> +     case IO_PAGE_FAULT:
> +             printf("io page fault dev=%s did=0x%.4x addr=0x%.16llx\n%s, %s, 
> %s, %s, %s, %s\n",
> +                dmar_bdf(sid), did, address,
> +                evt->dw1 & EVT_TR ? "translation" : "transaction",
> +                evt->dw1 & EVT_RZ ? "reserved bit" : "invalid level",
> +                evt->dw1 & EVT_PE ? "no perm" : "perm",
> +                evt->dw1 & EVT_RW ? "write" : "read",
> +                evt->dw1 & EVT_PR ? "present" : "not present",
> +                evt->dw1 & EVT_I  ? "interrupt" : "memory");
> +             ivhd_showdte(iommu);
> +             ivhd_showpage(iommu, sid, address);
> +             break;
> +     case DEV_TAB_HARDWARE_ERROR:
> +             printf("device table hardware error dev=%s addr=0x%.16llx %s, 
> %s, %s\n",
> +                 dmar_bdf(sid), address,
> +                evt->dw1 & EVT_TR ? "translation" : "transaction",
> +                evt->dw1 & EVT_RW ? "write" : "read",
> +                evt->dw1 & EVT_I  ? "interrupt" : "memory");
> +             ivhd_showdte(iommu);
> +             break;
> +     case PAGE_TAB_HARDWARE_ERROR:
> +             printf("page table hardware error dev=%s addr=0x%.16llx %s, %s, 
> %s\n",
> +                dmar_bdf(sid), address,
> +                evt->dw1 & EVT_TR ? "translation" : "transaction",
> +                evt->dw1 & EVT_RW ? "write" : "read",
> +                evt->dw1 & EVT_I  ? "interrupt" : "memory");
> +             ivhd_showdte(iommu);
> +             break;
> +     case ILLEGAL_COMMAND_ERROR:
> +             printf("illegal command addr=0x%.16llx\n", address);
> +             ivhd_showcmd(iommu);
> +             break;
> +     case COMMAND_HARDWARE_ERROR:
> +             printf("command hardware error addr=0x%.16llx flag=0x%.4x\n",
> +                address, flag);
> +             ivhd_showcmd(iommu);
> +             break;
> +     case IOTLB_INV_TIMEOUT:
> +             printf("iotlb invalidation timeout dev=%s address=0x%.16llx\n",
> +                dmar_bdf(sid), address);
> +             break;
> +     case INVALID_DEVICE_REQUEST:
> +             printf("invalid device request dev=%s addr=0x%.16llx 
> flag=0x%.4x\n",
> +                dmar_bdf(sid), address, flag);
> +             break;
> +     default:
> +             printf("unknown type=0x%.2x\n", type);
> +             break;
> +     }
> +     /* Clear old event */
> +     evt->dw0 = 0;
> +     evt->dw1 = 0;
> +     evt->dw2 = 0;
> +     evt->dw3 = 0;
> +}
> +
> +/* AMD: Process IOMMU error from hardware */
> +int
> +ivhd_poll_events(struct iommu_softc *iommu)
> +{
> +     uint32_t head, tail;
> +     int sz;
> +
> +     sz = sizeof(struct ivhd_event);
> +     head = iommu_read_4(iommu, EVT_HEAD_REG);
> +     tail = iommu_read_4(iommu, EVT_TAIL_REG);
> +     if (head == tail) {
> +             /* No pending events */
> +             return (0);
> +     }
> +     while (head != tail) {
> +             ivhd_show_event(iommu, iommu->evt_tbl + head, head);
> +             head = (head + sz) % EVT_TBL_SIZE;
> +     }
> +     iommu_write_4(iommu, EVT_HEAD_REG, head);
> +     return (0);
> +}
> +
> +/* AMD: Issue command to IOMMU queue */
> +int
> +_ivhd_issue_command(struct iommu_softc *iommu, const struct ivhd_command 
> *cmd)
> +{
> +     u_long rf;
> +     uint32_t head, tail, next;
> +     int sz;
> +     
> +     head = iommu_read_4(iommu, CMD_HEAD_REG);
> +     sz = sizeof(*cmd);
> +     rf = intr_disable();
> +     tail = iommu_read_4(iommu, CMD_TAIL_REG);
> +     next = (tail + sz) % CMD_TBL_SIZE;
> +     if (next == head) {
> +             printf("FULL\n");
> +             /* Queue is full */
> +             intr_restore(rf);
> +             return -EBUSY;
> +     }
> +     memcpy(iommu->cmd_tbl + tail, cmd, sz);
> +     iommu_write_4(iommu, CMD_TAIL_REG, next);
> +     intr_restore(rf);
> +     return (tail / sz);
> +}
> +
> +#define IVHD_MAXDELAY 8
> +
> +int
> +ivhd_issue_command(struct iommu_softc *iommu, const struct ivhd_command 
> *cmd, int wait)
> +{
> +     struct ivhd_command wq = { 0 };
> +     volatile uint64_t wv __aligned(16) = 0LL;
> +     paddr_t paddr;
> +     int rc, i;
> +
> +     rc = _ivhd_issue_command(iommu, cmd);
> +     if (rc >= 0 && wait) {
> +             /* Wait for previous commands to complete.
> +              * Store address of completion variable to command */
> +             pmap_extract(pmap_kernel(), (vaddr_t)&wv, &paddr);
> +             wq.dw0 = (paddr & ~0xF) | 0x1;
> +             wq.dw1 = (COMPLETION_WAIT << CMD_SHIFT) | ((paddr >> 32) & 
> 0xFFFFF);
> +             wq.dw2 = 0xDEADBEEF;
> +             wq.dw3 = 0xFEEDC0DE;
> +             
> +             rc = _ivhd_issue_command(iommu, &wq);
> +             /* wv will change to value in dw2/dw3 when command is complete 
> */
> +             for (i = 0; i < IVHD_MAXDELAY && !wv; i++) {
> +                     DELAY(10 << i);
> +             }
> +             if (i == IVHD_MAXDELAY) {
> +                     printf("ivhd command timeout: %.8x %.8x %.8x %.8x 
> wv:%llx idx:%x\n", 
> +                             cmd->dw0, cmd->dw1, cmd->dw2, cmd->dw3, wv, rc);
> +             }
> +     }
> +     return rc;
> +
> +}
> +
> +/* AMD: Flush changes to Device Table Entry for a specific domain */
> +int
> +ivhd_flush_devtab(struct iommu_softc *iommu, int did)
> +{
> +     struct ivhd_command cmd = { .dw0 = did, .dw1 = INVALIDATE_DEVTAB_ENTRY 
> << CMD_SHIFT };
> +     return ivhd_issue_command(iommu, &cmd, 1); 
> +}
> +
> +/* AMD: Invalidate all IOMMU device and page tables */
> +int
> +ivhd_invalidate_iommu_all(struct iommu_softc *iommu)
> +{
> +     struct ivhd_command cmd = { .dw1 = INVALIDATE_IOMMU_ALL << CMD_SHIFT };
> +     return ivhd_issue_command(iommu, &cmd, 0); 
> +}
> +
> +/* AMD: Invalidate interrupt remapping */
> +int
> +ivhd_invalidate_interrupt_table(struct iommu_softc *iommu, int did)
> +{
> +     struct ivhd_command cmd = { .dw0 = did, .dw1 = 
> INVALIDATE_INTERRUPT_TABLE << CMD_SHIFT };
> +     return ivhd_issue_command(iommu, &cmd, 0); 
> +}
> +
> +/* AMD: Invalidate all page tables in a domain */
> +int
> +ivhd_invalidate_domain(struct iommu_softc *iommu, int did)
> +{
> +     struct ivhd_command cmd = { .dw1 = did | (INVALIDATE_IOMMU_PAGES << 
> CMD_SHIFT) };
> +
> +     cmd.dw2 = 0xFFFFF000 | 0x3;
> +     cmd.dw3 = 0x7FFFFFFF;
> +     return ivhd_issue_command(iommu, &cmd, 1);
> +}
> +
> +/* AMD: Display Registers */
> +void
> +ivhd_showreg(struct iommu_softc *iommu)
> +{
> +     printf("---- dt:%.16llx cmd:%.16llx evt:%.16llx ctl:%.16llx 
> sts:%.16llx\n",
> +             iommu_read_8(iommu, DEV_TAB_BASE_REG),
> +             iommu_read_8(iommu, CMD_BASE_REG),
> +             iommu_read_8(iommu, EVT_BASE_REG),
> +             iommu_read_8(iommu, IOMMUCTL_REG),
> +             iommu_read_8(iommu, IOMMUSTS_REG));
> +     printf("---- cmd queue:%.16llx %.16llx evt queue:%.16llx %.16llx\n",
> +             iommu_read_8(iommu, CMD_HEAD_REG),
> +             iommu_read_8(iommu, CMD_TAIL_REG),
> +             iommu_read_8(iommu, EVT_HEAD_REG),
> +             iommu_read_8(iommu, EVT_TAIL_REG));
> +}
> +
> +/* AMD: Generate Errors to test event handler */
> +void
> +ivhd_checkerr(struct iommu_softc *iommu)
> +{
> +     struct ivhd_command cmd = { -1, -1, -1, -1 };
> +
> +     /* Generate ILLEGAL DEV TAB entry? */
> +     iommu->dte[0x2303].dw0 = -1;      /* invalid */
> +     iommu->dte[0x2303].dw2 = 0x1234;  /* domain */
> +     iommu->dte[0x2303].dw7 = -1;      /* reserved */
> +     ivhd_flush_devtab(iommu, 0x1234);
> +     ivhd_poll_events(iommu);
> +
> +     /* Generate ILLEGAL_COMMAND_ERROR : ok */
> +     ivhd_issue_command(iommu, &cmd, 0);
> +     ivhd_poll_events(iommu);
> +
> +     /* Generate page hardware error */
> +}
> +
> +/* AMD: Show Device Table Entry */
> +void
> +ivhd_showdte(struct iommu_softc *iommu)
> +{
> +     int i;
> +     
> +
> +     for (i = 0; i < 65536; i++) {
> +             if (iommu->dte[i].dw0) {
> +                     printf("%.2x:%.2x.%x: %.8x %.8x %.8x %.8x %.8x %.8x 
> %.8x %.8x\n",
> +                             i >> 8, (i >> 3) & 0x1F, i & 0x7,
> +                             iommu->dte[i].dw0, iommu->dte[i].dw1,
> +                             iommu->dte[i].dw2, iommu->dte[i].dw3,
> +                             iommu->dte[i].dw4, iommu->dte[i].dw5,
> +                             iommu->dte[i].dw6, iommu->dte[i].dw7);
> +             }
> +     }
> +}
> +
> +/* AMD: Show command entries */
> +void
> +ivhd_showcmd(struct iommu_softc *iommu)
> +{
> +     struct ivhd_command *ihd;
> +     paddr_t phd;
> +     int i;
> +
> +     ihd = iommu->cmd_tbl;
> +     phd = iommu_read_8(iommu, CMD_BASE_REG) & CMD_BASE_MASK;
> +     for (i = 0; i < 4096 / 128; i++) {
> +             printf("%.2x: %.16llx %.8x %.8x %.8x %.8x\n", i, 
> +                     (uint64_t)phd + i * sizeof(*ihd),
> +                     ihd[i].dw0,ihd[i].dw1,ihd[i].dw2,ihd[i].dw3);
> +     }
> +}
> +
> +#define _c(x) (int)((iommu->ecap >> x ##_SHIFT) & x ## _MASK)
> +
> +/* AMD: Initialize IOMMU */
> +int
> +ivhd_iommu_init(struct acpidmar_softc *sc, struct iommu_softc *iommu,
> +     struct acpi_ivhd *ivhd)
> +{
> +     static int niommu;
> +     paddr_t paddr;
> +     uint64_t ov;
> +
> +     if (sc == NULL || iommu == NULL || ivhd == NULL) {
> +             printf("Bad pointer to iommu_init!\n");
> +             return -1;
> +     }
> +     if (_bus_space_map(sc->sc_memt, ivhd->address, 0x80000, 0, &iommu->ioh) 
> != 0) {
> +             printf("Bus Space Map fails\n");
> +             return -1;
> +     }
> +     TAILQ_INIT(&iommu->domains);
> +     TAILQ_INIT(&iommu->devices);
> +
> +     /* Setup address width and number of domains */
> +     iommu->id = ++niommu;
> +     iommu->iot = sc->sc_memt;
> +     iommu->mgaw = 48;
> +     iommu->agaw = 48;
> +     iommu->flags = 1;
> +     iommu->segment = 0;
> +     iommu->ndoms = 256;
> +
> +     printf(": AMD iommu%d at 0x%.8llx\n", iommu->id, ivhd->address);
> +
> +     iommu->ecap = iommu_read_8(iommu, EXTFEAT_REG);
> +     DPRINTF("iommu%d: ecap:%.16llx ", iommu->id, iommu->ecap);
> +     DPRINTF("%s%s%s%s%s%s%s%s\n",
> +             iommu->ecap & EFR_PREFSUP ? "pref " : "",
> +             iommu->ecap & EFR_PPRSUP  ? "ppr " : "",
> +             iommu->ecap & EFR_NXSUP   ? "nx " : "",
> +             iommu->ecap & EFR_GTSUP   ? "gt " : "",
> +             iommu->ecap & EFR_IASUP   ? "ia " : "",
> +             iommu->ecap & EFR_GASUP   ? "ga " : "",
> +             iommu->ecap & EFR_HESUP   ? "he " : "",
> +             iommu->ecap & EFR_PCSUP   ? "pc " : "");
> +     DPRINTF(0,"hats:%x gats:%x glxsup:%x smif:%x smifrc:%x gam:%x\n",
> +             _c(EFR_HATS), _c(EFR_GATS), _c(EFR_GLXSUP), _c(EFR_SMIFSUP),
> +             _c(EFR_SMIFRC), _c(EFR_GAMSUP));
> +
> +     /* Turn off iommu */
> +     ov = iommu_read_8(iommu, IOMMUCTL_REG);
> +     iommu_write_8(iommu, IOMMUCTL_REG, ov & ~(CTL_IOMMUEN | CTL_COHERENT | 
> +             CTL_HTTUNEN | CTL_RESPASSPW | CTL_PASSPW | CTL_ISOC));
> +
> +     /* Enable intr, mark IOMMU device as invalid for remap */
> +     sid_flag[ivhd->devid] |= SID_INVALID;
> +     ivhd_intr_map(iommu, ivhd->devid);
> +
> +     /* Setup command buffer with 4k buffer (128 entries) */
> +     iommu->cmd_tbl = iommu_alloc_page(iommu, &paddr);
> +     iommu_write_8(iommu, CMD_BASE_REG, (paddr & CMD_BASE_MASK) | 
> CMD_TBL_LEN_4K);
> +     iommu_write_4(iommu, CMD_HEAD_REG, 0x00);
> +     iommu_write_4(iommu, CMD_TAIL_REG, 0x00);
> +     iommu->cmd_tblp = paddr;
> +
> +     /* Setup event log with 4k buffer (128 entries) */
> +     iommu->evt_tbl = iommu_alloc_page(iommu, &paddr);
> +     iommu_write_8(iommu, EVT_BASE_REG, (paddr & EVT_BASE_MASK) | 
> EVT_TBL_LEN_4K);
> +     iommu_write_4(iommu, EVT_HEAD_REG, 0x00);
> +     iommu_write_4(iommu, EVT_TAIL_REG, 0x00);
> +     iommu->evt_tblp = paddr;
> +
> +     /* Setup device table
> +      * 1 entry per source ID (bus:device:function - 64k entries)
> +      */
> +     iommu->dte = sc->sc_hwdte;
> +     iommu_write_8(iommu, DEV_TAB_BASE_REG, (sc->sc_hwdtep & DEV_TAB_MASK) | 
> DEV_TAB_LEN);
> +
> +     /* Enable IOMMU */
> +     ov |= (CTL_IOMMUEN | CTL_EVENTLOGEN | CTL_CMDBUFEN | CTL_EVENTINTEN);
> +     if (ivhd->flags & IVHD_COHERENT)
> +             ov |= CTL_COHERENT;
> +     if (ivhd->flags & IVHD_HTTUNEN)
> +             ov |= CTL_HTTUNEN;
> +     if (ivhd->flags & IVHD_RESPASSPW)
> +             ov |= CTL_RESPASSPW;
> +     if (ivhd->flags & IVHD_PASSPW)
> +             ov |= CTL_PASSPW;
> +     if (ivhd->flags & IVHD_ISOC)
> +             ov |= CTL_ISOC;
> +     ov &= ~(CTL_INVTIMEOUT_MASK << CTL_INVTIMEOUT_SHIFT);
> +     ov |=  (CTL_INVTIMEOUT_10MS << CTL_INVTIMEOUT_SHIFT);
> +     iommu_write_8(iommu, IOMMUCTL_REG, ov);
> +
> +     ivhd_invalidate_iommu_all(iommu);
> +
> +     TAILQ_INSERT_TAIL(&sc->sc_drhds, iommu, link);
> +     return 0;
> +}
> +
> +void
> +acpiivrs_ivhd(struct acpidmar_softc *sc, struct acpi_ivhd *ivhd)
> +{
> +     struct iommu_softc *iommu;
> +     struct acpi_ivhd_ext *ext;
> +     union acpi_ivhd_entry *ie;
> +     int start, off, dte, all_dte = 0;
> +
> +     if (ivhd->type == IVRS_IVHD_EXT) {
> +             ext = (struct acpi_ivhd_ext *)ivhd;
> +             DPRINTF(0,"ivhd: %.2x %.2x %.4x %.4x:%s %.4x %.16llx %.4x %.8x 
> %.16llx\n",
> +                    ext->type, ext->flags, ext->length,
> +                    ext->segment, dmar_bdf(ext->devid), ext->cap,
> +                    ext->address, ext->info,
> +                    ext->attrib, ext->efr);
> +             if (ext->flags & IVHD_PPRSUP)
> +                     DPRINTF(0," PPRSup");
> +             if (ext->flags & IVHD_PREFSUP)
> +                     DPRINTF(0," PreFSup");
> +             if (ext->flags & IVHD_COHERENT)
> +                     DPRINTF(0," Coherent");
> +             if (ext->flags & IVHD_IOTLB)
> +                     DPRINTF(0," Iotlb");
> +             if (ext->flags & IVHD_ISOC)
> +                     DPRINTF(0," ISoc");
> +             if (ext->flags & IVHD_RESPASSPW)
> +                     DPRINTF(0," ResPassPW");
> +             if (ext->flags & IVHD_PASSPW)
> +                     DPRINTF(0," PassPW");
> +             if (ext->flags & IVHD_HTTUNEN)
> +                     DPRINTF(0, " HtTunEn");
> +             if (ext->flags)
> +                     DPRINTF(0,"\n");
> +             off = sizeof(*ext);
> +             iommu = malloc(sizeof(*iommu), M_DEVBUF, M_ZERO|M_WAITOK);
> +             ivhd_iommu_init(sc, iommu, ivhd);
> +     } else {
> +             DPRINTF(0,"ivhd: %.2x %.2x %.4x %.4x:%s %.4x %.16llx %.4x 
> %.8x\n",
> +                    ivhd->type, ivhd->flags, ivhd->length,
> +                    ivhd->segment, dmar_bdf(ivhd->devid), ivhd->cap,
> +                    ivhd->address, ivhd->info,
> +                    ivhd->feature);
> +             if (ivhd->flags & IVHD_PPRSUP)
> +                     DPRINTF(0," PPRSup");
> +             if (ivhd->flags & IVHD_PREFSUP)
> +                     DPRINTF(0," PreFSup");
> +             if (ivhd->flags & IVHD_COHERENT)
> +                     DPRINTF(0," Coherent");
> +             if (ivhd->flags & IVHD_IOTLB)
> +                     DPRINTF(0," Iotlb");
> +             if (ivhd->flags & IVHD_ISOC)
> +                     DPRINTF(0," ISoc");
> +             if (ivhd->flags & IVHD_RESPASSPW)
> +                     DPRINTF(0," ResPassPW");
> +             if (ivhd->flags & IVHD_PASSPW)
> +                     DPRINTF(0," PassPW");
> +             if (ivhd->flags & IVHD_HTTUNEN)
> +                     DPRINTF(0, " HtTunEn");
> +             if (ivhd->flags)
> +                     DPRINTF(0,"\n");
> +             off = sizeof(*ivhd);
> +     }
> +     while (off < ivhd->length) {
> +             ie = (void *)ivhd + off;
> +             switch (ie->type) {
> +             case IVHD_ALL:
> +                     all_dte = ie->all.data;
> +                     DPRINTF(0," ALL %.4x\n", dte);
> +                     off += sizeof(ie->all);
> +                     break;
> +             case IVHD_SEL:
> +                     dte = ie->sel.data;
> +                     DPRINTF(0," SELECT: %s %.4x\n", 
> dmar_bdf(ie->sel.devid), dte);
> +                     off += sizeof(ie->sel);
> +                     break;
> +             case IVHD_SOR:
> +                     dte = ie->sor.data;
> +                     start = ie->sor.devid;
> +                     DPRINTF(0," SOR: %s %.4x\n", dmar_bdf(start), dte);
> +                     off += sizeof(ie->sor);
> +                     break;
> +             case IVHD_EOR:
> +                     DPRINTF(0," EOR: %s\n", dmar_bdf(ie->eor.devid));
> +                     off += sizeof(ie->eor);
> +                     break;
> +             case IVHD_ALIAS_SEL:
> +                     dte = ie->alias.data;
> +                     DPRINTF(0," ALIAS: src=%s: ", 
> dmar_bdf(ie->alias.srcid));
> +                     DPRINTF(0," %s %.4x\n", dmar_bdf(ie->alias.devid), dte);
> +                     off += sizeof(ie->alias);
> +                     break;
> +             case IVHD_ALIAS_SOR:
> +                     dte = ie->alias.data;
> +                     DPRINTF(0," ALIAS_SOR: %s %.4x ", 
> dmar_bdf(ie->alias.devid), dte);
> +                     DPRINTF(0," src=%s\n", dmar_bdf(ie->alias.srcid));
> +                     off += sizeof(ie->alias);
> +                     break;
> +             case IVHD_EXT_SEL:
> +                     dte = ie->ext.data;
> +                     DPRINTF(0," EXT SEL: %s %.4x %.8x\n", 
> dmar_bdf(ie->ext.devid),
> +                             dte, ie->ext.extdata);
> +                     off += sizeof(ie->ext);
> +                     break;
> +             case IVHD_EXT_SOR:
> +                     dte = ie->ext.data;
> +                     DPRINTF(0," EXT SOR: %s %.4x %.8x\n", 
> dmar_bdf(ie->ext.devid),
> +                            dte, ie->ext.extdata);
> +                     off += sizeof(ie->ext);
> +                     break;
> +             case IVHD_SPECIAL:
> +                     DPRINTF(0," SPECIAL\n");
> +                     off += sizeof(ie->special);
> +                     break;
> +             default:
> +                     DPRINTF(0," 2:unknown %x\n", ie->type);
> +                     off = ivhd->length;
> +                     break;
> +             }
> +     }
> +}
> +
> +void
> +acpiivrs_init(struct acpidmar_softc *sc, struct acpi_ivrs *ivrs)
> +{
> +     union acpi_ivrs_entry *ie;
> +     int off;
> +     
> +     if (!sc->sc_hwdte) {
> +             sc->sc_hwdte = iommu_alloc_hwdte(sc, HWDTE_SIZE, 
> &sc->sc_hwdtep);
> +             if (sc->sc_hwdte == NULL)
> +                     panic("Can't allocate HWDTE!\n");
> +     }
> +
> +     domain_map_page = domain_map_page_amd;
> +     DPRINTF(0,"IVRS Version: %d\n", ivrs->hdr.revision);
> +     DPRINTF(0," VA Size: %d\n", (ivrs->ivinfo >> IVRS_VASIZE_SHIFT) & 
> IVRS_VASIZE_MASK);
> +     DPRINTF(0," PA Size: %d\n", (ivrs->ivinfo >> IVRS_PASIZE_SHIFT) & 
> IVRS_PASIZE_MASK);
> +
> +     TAILQ_INIT(&sc->sc_drhds);
> +     TAILQ_INIT(&sc->sc_rmrrs);
> +     TAILQ_INIT(&sc->sc_atsrs);
> +
> +     DPRINTF(0,"======== IVRS\n");
> +     off = sizeof(*ivrs);
> +     while (off < ivrs->hdr.length) {
> +             ie = (void *)ivrs + off;
> +             switch (ie->type) {
> +             case IVRS_IVHD:
> +             case IVRS_IVHD_EXT:
> +                     acpiivrs_ivhd(sc, &ie->ivhd);
> +                     break;
> +             case IVRS_IVMD_ALL:
> +             case IVRS_IVMD_SPECIFIED:
> +             case IVRS_IVMD_RANGE:
> +                     DPRINTF(0,"ivmd\n");
> +                     break;
> +             default:
> +                     DPRINTF(0,"1:unknown: %x\n", ie->type);
> +                     break;
> +             }
> +             off += ie->length;
> +     }
> +     DPRINTF(0,"======== End IVRS\n");
> +}
> +
> +static int
> +acpiivhd_activate(struct iommu_softc *iommu, int act)
> +{
> +     switch (act) {
> +     case DVACT_SUSPEND:     
> +             iommu->flags |= IOMMU_FLAGS_SUSPEND;
> +             break;
> +     case DVACT_RESUME:
> +             iommu->flags &= ~IOMMU_FLAGS_SUSPEND;
> +             break;
> +     }
> +     return (0);
> +}
> +
> +int
> +acpidmar_activate(struct device *self, int act)
> +{
> +     struct acpidmar_softc *sc = (struct acpidmar_softc *)self;
> +     struct iommu_softc *iommu;
> +
> +     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) {
> +                     printf("iommu%d resume\n", iommu->id);
> +                     if (iommu->dte) {
> +                             acpiivhd_activate(iommu, act);
> +                             continue;
> +                     }
> +                     iommu_flush_write_buffer(iommu);
> +                     iommu_set_rtaddr(iommu, iommu->rtaddr);
> +                     iommu_write_4(iommu, DMAR_FEDATA_REG, iommu->fedata);
> +                     iommu_write_4(iommu, DMAR_FEADDR_REG, iommu->feaddr);
> +                     iommu_write_4(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, -1);
> +             }
> +             break;
> +     case DVACT_SUSPEND:
> +             TAILQ_FOREACH(iommu, &sc->sc_drhds, link) {
> +                     printf("iommu%d suspend\n", iommu->id);
> +                     if (iommu->flags & IOMMU_FLAGS_BAD)
> +                             continue;
> +                     if (iommu->dte) {
> +                             acpiivhd_activate(iommu, act);
> +                             continue;
> +                     }
> +                     iommu->flags |= IOMMU_FLAGS_SUSPEND;
> +                     iommu_enable_translation(iommu, 0);
> +                     iommu_showcfg(iommu, -1);
> +             }
> +             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 (1);
> +     if (memcmp(hdr->signature, IVRS_SIG, sizeof(IVRS_SIG) - 1) == 0)
> +             return (1);
> +
> +     return (0);
> +}
> +
> +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;
> +     struct acpi_ivrs        *ivrs = (struct acpi_ivrs *)aaa->aaa_table;
> +     struct acpi_table_header *hdr;
> +
> +     hdr = (struct acpi_table_header *)aaa->aaa_table;
> +     sc->sc_memt = aaa->aaa_memt;
> +     if (memcmp(hdr->signature, DMAR_SIG, sizeof(DMAR_SIG) - 1) == 0) {
> +             acpidmar_sc = sc;
> +             acpidmar_init(sc, dmar);
> +     }
> +     if (memcmp(hdr->signature, IVRS_SIG, sizeof(IVRS_SIG) - 1) == 0) {
> +             acpidmar_sc = sc;
> +             acpiivrs_init(sc, ivrs);
> +     }
> +}
> +
> +/* 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_write_4(iommu, DMAR_FECTL_REG, FECTL_IM);
> +     iommu_read_4(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_write_4(iommu, DMAR_FECTL_REG, 0);
> +     iommu_read_4(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;
> +
> +     mtx_enter(&iommu->reg_lock);
> +
> +     iommu->fedata = vec;
> +     iommu->feaddr = 0xfee00000L | (ci->ci_apicid << 12);
> +     iommu_write_4(iommu, DMAR_FEDATA_REG, vec);
> +     iommu_write_4(iommu, DMAR_FEADDR_REG, iommu->feaddr);
> +     iommu_write_4(iommu, DMAR_FEUADDR_REG, iommu->feaddr >> 32);
> +
> +     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, level);
> +#endif
> +
> +     return intr_establish(-1, pic, 0, IST_PULSE, level, NULL, func, arg, 
> what);
> +}
> +
> +/* Intel: Handle DMAR Interrupt */
> +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;
> +
> +     /*splassert(IPL_HIGH);*/
> +
> +     if (!(iommu->gcmd & GCMD_TE)) {
> +             return (1);
> +     }
> +     mtx_enter(&iommu->reg_lock);
> +     sts = iommu_read_4(iommu, DMAR_FECTL_REG);
> +     sts = iommu_read_4(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_read_8(iommu, fro + (fri*16) + 8);
> +             if (!(fe.hi & FRCD_HI_F))
> +                     break;
> +
> +             fe.lo = iommu_read_8(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_write_4(iommu, DMAR_FSTS_REG, FSTS_PFO | FSTS_PPF);
> +
> +     mtx_leave(&iommu->reg_lock);
> +
> +     return (1);
> +}
> +
> +const char *vtd_faults[] = {
> +     "Software",
> +     "Root Entry Not Present",       /* ok (rtaddr + 4096) */
> +     "Context Entry Not Present",    /* ok (no CTX_P) */
> +     "Context Entry Invalid",        /* ok (tt = 3) */
> +     "Address Beyond MGAW",
> +     "Write",                        /* ok */
> +     "Read",                         /* ok */
> +     "Paging Entry Invalid",         /* ok */
> +     "Root Table Invalid",
> +     "Context Table Invalid",
> +     "Root Entry Reserved",          /* ok (root.lo |= 0x4) */
> +     "Context Entry Reserved",
> +     "Paging Entry Reserved",
> +     "Context Entry TT",
> +     "Reserved",
> +};
> +
> +void iommu_showpte(uint64_t, int, uint64_t);
> +
> +/* Intel: Show IOMMU page table entry */
> +void
> +iommu_showpte(uint64_t ptep, int lvl, uint64_t base)
> +{
> +     uint64_t nb, pb, i;
> +     struct pte_entry *pte;
> +
> +     pte = (void *)PMAP_DIRECT_MAP(ptep);
> +     for (i = 0; i < 512; i++) {
> +             if (!(pte[i].val & PTE_P))
> +                     continue;
> +             nb = base + (i << lvl);
> +             pb = pte[i].val & ~VTD_PAGE_MASK;
> +             if(lvl == VTD_LEVEL0) {
> +                     printf("   %3llx %.16llx = %.16llx %c%c %s\n",
> +                         i, nb, pb,
> +                         pte[i].val == PTE_R ? 'r' : ' ',
> +                         pte[i].val & PTE_W ? 'w' : ' ',
> +                         (nb == pb) ? " ident" : "");
> +                     if (nb == pb)
> +                             return;
> +             } else {
> +                     iommu_showpte(pb, lvl - VTD_STRIDE_SIZE, nb);
> +             }
> +     }
> +}
> +
> +/* Intel: Show IOMMU configuration */
> +void
> +iommu_showcfg(struct iommu_softc *iommu, int sid)
> +{
> +     int i, j, sts, cmd;
> +     struct context_entry *ctx;
> +     pcitag_t tag;
> +     pcireg_t clc;
> +
> +     cmd = iommu_read_4(iommu, DMAR_GCMD_REG);
> +     sts = iommu_read_4(iommu, DMAR_GSTS_REG);
> +     printf("iommu%d: flags:%d root pa:%.16llx %s %s %s %.8x %.8x\n",
> +         iommu->id, iommu->flags, iommu_read_8(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;
> +             }
> +             for (j = 0; j < 256; j++) {
> +                     ctx = iommu->ctx[i] + j;
> +                     if (!context_entry_is_valid(ctx)) {
> +                             continue;
> +                     }
> +                     tag = pci_make_tag(NULL, i, (j >> 3), j & 0x7);
> +                     clc = pci_conf_read(NULL, tag, 0x08) >> 8;
> +                     printf("  %.2x:%.2x.%x lvl:%d did:%.4x tt:%d 
> ptep:%.16llx flag:%x cc:%.6x\n",
> +                         i, (j >> 3), j & 7,
> +                         context_address_width(ctx),
> +                         context_domain_id(ctx),
> +                         context_translation_type(ctx),
> +                         context_pte(ctx),
> +                         context_user(ctx),
> +                         clc);
> +#if 0
> +                     /* dump pagetables */
> +                     iommu_showpte(ctx->lo & ~VTD_PAGE_MASK, iommu->agaw -
> +                         VTD_STRIDE_SIZE, 0);
> +#endif
> +             }
> +     }
> +}
> +
> +/* Intel: Show IOMMU fault */
> +void
> +iommu_showfault(struct iommu_softc *iommu, int fri, struct fault_entry *fe)
> +{
> +     int bus, dev, fun, type, fr, df;
> +     bios_memmap_t   *im;
> +     const char *mapped;
> +
> +     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;
> +     df  = (fe->hi >> FRCD_HI_FUN_SHIFT) & 0xFF;
> +     iommu_showcfg(iommu, mksid(bus,dev,fun));
> +     if (!iommu->ctx[bus]) {
> +             /* Bus is not initialized */
> +             mapped = "nobus";
> +     } else if (!context_entry_is_valid(&iommu->ctx[bus][df])) {
> +             /* DevFn not initialized */
> +             mapped = "nodevfn";
> +     } else if (context_user(&iommu->ctx[bus][df]) != 0xA) {
> +             /* no bus_space_map */
> +             mapped = "nomap";
> +     } else {
> +             /* bus_space_map */
> +             mapped = "mapped";
> +     }
> +     printf("fri%d: dmar: %.2x:%.2x.%x %s error at %llx fr:%d [%s] iommu:%d 
> [%s]\n",
> +         fri, bus, dev, fun,
> +         type == 'r' ? "read" : "write",
> +         fe->lo,
> +         fr, fr <= 13 ? vtd_faults[fr] : "unknown",
> +         iommu->id,
> +         mapped);
> +     for (im = bios_memmap; im->type != BIOS_MAP_END; im++) {
> +             if ((im->type == BIOS_MAP_RES) &&
> +                 (im->addr <= fe->lo) &&
> +                 (fe->lo <= im->addr+im->size)) {
> +                     printf("mem in e820.reserved\n");
> +             }
> +     }
> +#ifdef DDB
> +     if (acpidmar_ddb)
> +             db_enter();
> +#endif
> +}
> +
> diff --git a/sys/dev/acpi/acpidmar.h b/sys/dev/acpi/acpidmar.h
> new file mode 100644
> index 000000000..7938f5381
> --- /dev/null
> +++ b/sys/dev/acpi/acpidmar.h
> @@ -0,0 +1,536 @@
> +/*
> + * 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 IOMMU_DEBUG*/
> +
> +#define VTD_STRIDE_MASK 0x1FF
> +#define VTD_STRIDE_SIZE 9
> +#define VTD_PAGE_SIZE   4096
> +#define VTD_PAGE_MASK   0xFFF
> +#define VTD_PTE_MASK    0x0000FFFFFFFFF000LL
> +
> +#define VTD_LEVEL0   12
> +#define VTD_LEVEL1   21
> +#define VTD_LEVEL2   30 /* Minimum level supported */
> +#define VTD_LEVEL3   39 /* Also supported */
> +#define VTD_LEVEL4   48
> +#define VTD_LEVEL5   57
> +
> +#define _xbit(x,y) (((x)>> (y)) & 1)
> +#define _xfld(x,y) (uint32_t)(((x)>> y##_SHIFT) & y##_MASK)
> +
> +#define VTD_AWTOLEVEL(x)    (((x) - 30) / VTD_STRIDE_SIZE)
> +#define VTD_LEVELTOAW(x)    (((x) * VTD_STRIDE_SIZE) + 30)
> +
> +#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 */
> +
> +/* Read-Modify-Write helpers */
> +static inline void iommu_rmw32(void *ov, uint32_t mask, uint32_t shift, 
> uint32_t nv)
> +{
> +     *(uint32_t *)ov &= ~(mask << shift);
> +     *(uint32_t *)ov |= (nv & mask) << shift;
> +}
> +static inline void iommu_rmw64(void *ov, uint32_t mask, uint32_t shift, 
> uint64_t nv)
> +{
> +     *(uint64_t *)ov &= ~(mask << shift);
> +     *(uint64_t *)ov |= (nv & mask) << shift;
> +}
> +
> +/*
> + * 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_USER_MASK 0xF
> +#define CTX_H_USER_SHIFT 3
> +#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 &= VTD_PAGE_MASK;
> +     ce->lo |= (slpte & ~VTD_PAGE_MASK);
> +}
> +
> +/* 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 & ~VTD_PAGE_MASK);
> +}
> +
> +/* 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 VTD_LEVELTOAW((ce->hi >> CTX_H_AW_SHIFT) & CTX_H_AW_MASK);
> +}
> +
> +/* Check if context entry is valid */
> +static inline bool
> +context_entry_is_valid(struct context_entry *ce)
> +{
> +     return (ce->lo & CTX_P);
> +}
> +
> +/* User-available bits in context entry */
> +static inline int
> +context_user(struct context_entry *ce)
> +{
> +     return (ce->hi >> CTX_H_USER_SHIFT) & CTX_H_USER_MASK;
> +}
> +
> +static inline void
> +context_set_user(struct context_entry *ce, int v)
> +{
> +     ce->hi &= ~(CTX_H_USER_MASK << CTX_H_USER_SHIFT);
> +     ce->hi |=  ((v & CTX_H_USER_MASK) << CTX_H_USER_SHIFT);
> +}
> +
> +/*
> + * 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)
> +#define PTE_US  (1L << 2)
> +#define PTE_PWT (1L << 3)
> +#define PTE_PCD (1L << 4)
> +#define PTE_A   (1L << 5)
> +#define PTE_D   (1L << 6)
> +#define PTE_PAT (1L << 7)
> +#define PTE_G   (1L << 8)
> +#define PTE_EA  (1L << 10)
> +#define PTE_XD  (1LL << 63)
> +
> +/* PDE Level entry */
> +#define PTE_PS  (1L << 7)
> +
> +/* PDPE Level entry */
> +
> +/* ----------------------------------------------------------------
> + * 5555555444444444333333333222222222111111111000000000------------ 
> + * [PML4 ->] PDPE.1GB
> + * [PML4 ->] PDPE.PDE -> PDE.2MB
> + * [PML4 ->] PDPE.PDE -> PDE -> PTE
> + * GAW0 = (12.20) (PTE)
> + * GAW1 = (21.29) (PDE)
> + * GAW2 = (30.38) (PDPE)
> + * GAW3 = (39.47) (PML4)
> + * GAW4 = (48.57) (n/a)
> + * GAW5 = (58.63) (n/a)
> + */
> +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,
> +};
> +
> +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 */
> +};
> +
> +#endif
> +
> +void acpidmar_pci_hook(pci_chipset_tag_t, struct pci_attach_args *);
> +void dmar_ptmap(bus_dma_tag_t, bus_addr_t);
> +void acpidmar_sw(int);
> +
> +#define __EXTRACT(v,m) (((v) >> m##_SHIFT) & m##_MASK)
> diff --git a/sys/dev/acpi/amd_iommu.h b/sys/dev/acpi/amd_iommu.h
> new file mode 100644
> index 000000000..30de2593e
> --- /dev/null
> +++ b/sys/dev/acpi/amd_iommu.h
> @@ -0,0 +1,360 @@
> +/*
> + * Copyright (c) 2019 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 __amd_iommu_h__
> +#define __amd_iommu_h__
> +
> +#define DEV_TAB_BASE_REG     0x0000
> +#define CMD_BASE_REG         0x0008
> +#define EVT_BASE_REG         0x0010
> +
> +#define EXCL_BASE_REG                0x0020
> +#define EXCL_LIMIT_REG               0x0028
> +
> +/* Extended Feature Register */
> +#define EXTFEAT_REG          0x0030
> +#define  EFR_PREFSUP         (1L << 0)
> +#define  EFR_PPRSUP          (1L << 1)
> +#define  EFR_NXSUP           (1L << 3)
> +#define  EFR_GTSUP           (1L << 4)
> +#define  EFR_IASUP           (1L << 6)
> +#define  EFR_GASUP           (1L << 7)
> +#define  EFR_HESUP           (1L << 8)
> +#define  EFR_PCSUP           (1L << 9)
> +#define  EFR_HATS_SHIFT              10
> +#define  EFR_HATS_MASK               0x3
> +#define  EFR_GATS_SHIFT              12
> +#define  EFR_GATS_MASK               0x3
> +#define  EFR_GLXSUP_SHIFT    14
> +#define  EFR_GLXSUP_MASK     0x3
> +#define  EFR_SMIFSUP_SHIFT   16
> +#define  EFR_SMIFSUP_MASK    0x3        
> +#define  EFR_SMIFRC_SHIFT    18
> +#define  EFR_SMIFRC_MASK     0x7
> +#define  EFR_GAMSUP_SHIFT    21
> +#define  EFR_GAMSUP_MASK     0x7
> +
> +#define CMD_HEAD_REG         0x2000
> +#define CMD_TAIL_REG         0x2008
> +#define EVT_HEAD_REG         0x2010
> +#define EVT_TAIL_REG         0x2018
> +
> +#define IOMMUSTS_REG         0x2020
> +
> +#define DEV_TAB_MASK         0x000FFFFFFFFFF000LL
> +#define DEV_TAB_LEN          0x1FF
> +
> +/* IOMMU Control */
> +#define IOMMUCTL_REG         0x0018
> +#define  CTL_IOMMUEN         (1L << 0)
> +#define  CTL_HTTUNEN         (1L << 1)
> +#define  CTL_EVENTLOGEN              (1L << 2)
> +#define  CTL_EVENTINTEN              (1L << 3)
> +#define  CTL_COMWAITINTEN    (1L << 4)
> +#define  CTL_INVTIMEOUT_SHIFT        5
> +#define  CTL_INVTIMEOUT_MASK         0x7
> +#define  CTL_INVTIMEOUT_NONE 0
> +#define  CTL_INVTIMEOUT_1MS     1
> +#define  CTL_INVTIMEOUT_10MS    2
> +#define  CTL_INVTIMEOUT_100MS   3
> +#define  CTL_INVTIMEOUT_1S      4
> +#define  CTL_INVTIMEOUT_10S     5
> +#define  CTL_INVTIMEOUT_100S    6
> +#define  CTL_PASSPW          (1L << 8)
> +#define  CTL_RESPASSPW               (1L << 9)
> +#define  CTL_COHERENT                (1L << 10)
> +#define  CTL_ISOC            (1L << 11)
> +#define  CTL_CMDBUFEN                (1L << 12)
> +#define  CTL_PPRLOGEN                (1L << 13)
> +#define  CTL_PPRINTEN                (1L << 14)
> +#define  CTL_PPREN           (1L << 15)
> +#define  CTL_GTEN            (1L << 16)
> +#define  CTL_GAEN            (1L << 17)
> +#define  CTL_CRW_SHIFT               18
> +#define  CTL_CRW_MASK                0xF
> +#define  CTL_SMIFEN          (1L << 22)
> +#define  CTL_SLFWBDIS                (1L << 23)
> +#define  CTL_SMIFLOGEN               (1L << 24)
> +#define  CTL_GAMEN_SHIFT     25
> +#define  CTL_GAMEN_MASK              0x7
> +#define  CTL_GALOGEN         (1L << 28)
> +#define  CTL_GAINTEN         (1L << 29)
> +#define  CTL_DUALPPRLOGEN_SHIFT      30
> +#define  CTL_DUALPPRLOGEN_MASK       0x3
> +#define  CTL_DUALEVTLOGEN_SHIFT      32
> +#define  CTL_DUALEVTLOGEN_MASK       0x3
> +#define  CTL_DEVTBLSEGEN_SHIFT       34
> +#define  CTL_DEVTBLSEGEN_MASK        0x7
> +#define  CTL_PRIVABRTEN_SHIFT        37
> +#define  CTL_PRIVABRTEN_MASK 0x3
> +#define  CTL_PPRAUTORSPEN    (1LL << 39)
> +#define  CTL_MARCEN          (1LL << 40)
> +#define  CTL_BLKSTOPMRKEN    (1LL << 41)
> +#define  CTL_PPRAUTOSPAON    (1LL << 42)
> +#define  CTL_DOMAINIDPNE     (1LL << 43)
> +
> +#define CMD_BASE_MASK                0x000FFFFFFFFFF000LL
> +#define CMD_TBL_SIZE         4096
> +#define CMD_TBL_LEN_4K               (8LL << 56)
> +#define CMD_TBL_LEN_8K               (9lL << 56)
> +
> +#define EVT_BASE_MASK                0x000FFFFFFFFFF000LL
> +#define EVT_TBL_SIZE         4096
> +#define EVT_TBL_LEN_4K               (8LL << 56)
> +#define EVT_TBL_LEN_8K               (9LL << 56)
> +
> +/*========================
> + * DEVICE TABLE ENTRY
> + * Contains mapping of bus-device-function 
> + *
> + *  0       Valid (V)
> + *  1       Translation Valid (TV)
> + *  7:8     Host Address Dirty (HAD)
> + *  9:11    Page Table Depth (usually 4)
> + *  12:51   Page Table Physical Address
> + *  52      PPR Enable
> + *  53      GPRP
> + *  54      Guest I/O Protection Valid (GIoV)
> + *  55      Guest Translation Valid (GV)
> + *  56:57   Guest Levels translated (GLX)
> + *  58:60   Guest CR3 bits 12:14 (GCR3TRP)
> + *  61      I/O Read Permission (IR)
> + *  62      I/O Write Permission (IW)
> + *  64:79   Domain ID
> + *  80:95   Guest CR3 bits 15:30 (GCR3TRP)
> + *  96      IOTLB Enable (I)
> + *  97      Suppress multiple I/O page faults (I)
> + *  98      Supress all I/O page faults (SA)
> + *  99:100  Port I/O Control (IoCTL)
> + *  101     Cache IOTLB Hint
> + *  102     Snoop Disable (SD)
> + *  103     Allow Exclusion (EX)
> + *  104:105 System Management Message (SysMgt)
> + *  107:127 Guest CR3 bits 31:51 (GCR3TRP)
> + *  128     Interrupt Map Valid (IV)
> + *  129:132 Interrupt Table Length (IntTabLen)
> + *========================*/
> +struct ivhd_dte {
> +     uint32_t dw0;
> +     uint32_t dw1;
> +     uint32_t dw2;
> +     uint32_t dw3;
> +     uint32_t dw4;
> +     uint32_t dw5;
> +     uint32_t dw6;
> +     uint32_t dw7;
> +} __packed;
> +
> +#define HWDTE_SIZE (65536 * sizeof(struct ivhd_dte))
> +
> +#define DTE_V                        (1L << 0)                       /* dw0 
> */
> +#define DTE_TV                       (1L << 1)                       /* dw0 
> */
> +#define DTE_LEVEL_SHIFT              9                               /* dw0 
> */
> +#define DTE_LEVEL_MASK               0x7                             /* dw0 
> */
> +#define DTE_HPTRP_MASK               0x000FFFFFFFFFF000LL            /* 
> dw0,1 */
> +
> +#define DTE_PPR                      (1L << 20)                      /* dw1 
> */
> +#define DTE_GPRP             (1L << 21)                      /* dw1 */
> +#define DTE_GIOV             (1L << 22)                      /* dw1 */
> +#define DTE_GV                       (1L << 23)                      /* dw1 
> */
> +#define DTE_IR                       (1L << 29)                      /* dw1 
> */
> +#define DTE_IW                       (1L << 30)                      /* dw1 
> */
> +
> +#define DTE_DID_MASK         0xFFFF                          /* dw2 */
> +
> +#define DTE_IV                       (1L << 0)                       /* dw3 
> */
> +#define DTE_SE                       (1L << 1)
> +#define DTE_SA                       (1L << 2)
> +#define DTE_INTTABLEN_SHIFT  1
> +#define DTE_INTTABLEN_MASK   0xF
> +#define DTE_IRTP_MASK                0x000FFFFFFFFFFFC0LL
> +
> +#define PTE_LVL5                48
> +#define PTE_LVL4                39
> +#define PTE_LVL3                30
> +#define PTE_LVL2                21
> +#define PTE_LVL1                12
> +
> +#define PTE_NXTLVL(x)           (((x) & 0x7) << 9)
> +#define PTE_PADDR_MASK               0x000FFFFFFFFFF000LL
> +#define PTE_IR                  (1LL << 61)
> +#define PTE_IW                  (1LL << 62)
> +
> +#define DTE_GCR312_MASK              0x3
> +#define DTE_GCR312_SHIFT     24
> +
> +#define DTE_GCR315_MASK              0xFFFF
> +#define DTE_GCR315_SHIFT     16
> +
> +#define DTE_GCR331_MASK              0xFFFFF
> +#define DTE_GCR331_SHIFT     12
> +
> +#define _get64(x)   *(uint64_t *)(x)
> +#define _put64(x,v) *(uint64_t *)(x) = (v)
> +
> +/* Set Guest CR3 address */
> +static inline void
> +dte_set_guest_cr3(struct ivhd_dte *dte, paddr_t paddr)
> +{
> +     iommu_rmw32(&dte->dw1, DTE_GCR312_MASK, DTE_GCR312_SHIFT, paddr >> 12);
> +     iommu_rmw32(&dte->dw2, DTE_GCR315_MASK, DTE_GCR315_SHIFT, paddr >> 15);
> +     iommu_rmw32(&dte->dw3, DTE_GCR331_MASK, DTE_GCR331_SHIFT, paddr >> 31);
> +}
> +
> +/* Set Interrupt Remapping Root Pointer */
> +static inline void
> +dte_set_interrupt_table_root_ptr(struct ivhd_dte *dte, paddr_t paddr)
> +{
> +     uint64_t ov = _get64(&dte->dw4);
> +     _put64(&dte->dw4, (ov & ~DTE_IRTP_MASK) | (paddr & DTE_IRTP_MASK));
> +}
> +
> +/* Set Interrupt Remapping Table length */
> +static inline void
> +dte_set_interrupt_table_length(struct ivhd_dte *dte, int nEnt)
> +{
> +     iommu_rmw32(&dte->dw4, DTE_INTTABLEN_MASK, DTE_INTTABLEN_SHIFT, nEnt);
> +}
> +
> +/* Set Interrupt Remapping Valid */
> +static inline void
> +dte_set_interrupt_valid(struct ivhd_dte *dte)
> +{
> +     dte->dw4 |= DTE_IV;
> +}
> +
> +/* Set Domain ID in Device Table Entry */
> +static inline void
> +dte_set_domain(struct ivhd_dte *dte, uint16_t did)
> +{
> +     dte->dw2 = (dte->dw2 & ~DTE_DID_MASK) | (did & DTE_DID_MASK);
> +}
> +
> +/* Set Page Table Pointer for device */
> +static inline void
> +dte_set_host_page_table_root_ptr(struct ivhd_dte *dte, paddr_t paddr)
> +{
> +     uint64_t ov;
> +
> +     ov = _get64(&dte->dw0) & ~DTE_HPTRP_MASK;
> +     ov |= (paddr & DTE_HPTRP_MASK) | PTE_IW | PTE_IR;
> +
> +     _put64(&dte->dw0, ov);
> +}
> +
> +/* Set Page Table Levels Mask */
> +static inline void
> +dte_set_mode(struct ivhd_dte *dte, int mode)
> +{
> +     iommu_rmw32(&dte->dw0, DTE_LEVEL_MASK, DTE_LEVEL_SHIFT, mode);
> +}
> +
> +static inline void
> +dte_set_tv(struct ivhd_dte *dte)
> +{
> +     dte->dw0 |= DTE_TV;
> +}
> +
> +/* Set Device Table Entry valid.
> + * Domain/Level/Mode/PageTable should already be set
> + */
> +static inline void
> +dte_set_valid(struct ivhd_dte *dte)
> +{
> +     dte->dw0 |= DTE_V;
> +}
> +
> +/* Check if Device Table Entry is valid */
> +static inline int
> +dte_is_valid(struct ivhd_dte *dte)
> +{
> +     return (dte->dw0 & DTE_V);
> +}
> +
> +/*=========================================
> + * COMMAND
> + *=========================================*/
> +struct ivhd_command {
> +     uint32_t dw0;
> +     uint32_t dw1;
> +     uint32_t dw2;
> +     uint32_t dw3;
> +} __packed;
> +
> +#define CMD_SHIFT 28
> +
> +enum {
> +     COMPLETION_WAIT                 = 0x01,
> +     INVALIDATE_DEVTAB_ENTRY         = 0x02,
> +     INVALIDATE_IOMMU_PAGES          = 0x03,
> +     INVALIDATE_IOTLB_PAGES          = 0x04,
> +     INVALIDATE_INTERRUPT_TABLE      = 0x05,
> +     PREFETCH_IOMMU_PAGES            = 0x06,
> +     COMPLETE_PPR_REQUEST            = 0x07,
> +     INVALIDATE_IOMMU_ALL            = 0x08,
> +};
> +
> +/*=========================================
> + * EVENT
> + *=========================================*/
> +struct ivhd_event {
> +     uint32_t dw0;
> +     uint32_t dw1;
> +     uint32_t dw2;
> +     uint32_t dw3;
> +} __packed;
> +
> +#define EVT_TYPE_SHIFT               28
> +#define EVT_TYPE_MASK                0xF
> +#define EVT_SID_SHIFT                0
> +#define EVT_SID_MASK         0xFFFF
> +#define EVT_DID_SHIFT                0
> +#define EVT_DID_MASK         0xFFFF
> +#define EVT_FLAG_SHIFT       16
> +#define EVT_FLAG_MASK        0xFFF
> +
> +/* IOMMU Fault reasons */
> +enum {
> +     ILLEGAL_DEV_TABLE_ENTRY         = 0x1,
> +     IO_PAGE_FAULT                   = 0x2,
> +     DEV_TAB_HARDWARE_ERROR          = 0x3,
> +     PAGE_TAB_HARDWARE_ERROR         = 0x4,
> +     ILLEGAL_COMMAND_ERROR           = 0x5,
> +     COMMAND_HARDWARE_ERROR          = 0x6,
> +     IOTLB_INV_TIMEOUT               = 0x7,
> +     INVALID_DEVICE_REQUEST          = 0x8,
> +};
> +
> +#define EVT_GN                       (1L << 16)
> +#define EVT_NX                       (1L << 17)
> +#define EVT_US                       (1L << 18)
> +#define EVT_I                        (1L << 19)
> +#define EVT_PR                       (1L << 20)
> +#define EVT_RW                       (1L << 21)
> +#define EVT_PE                       (1L << 22)
> +#define EVT_RZ                       (1L << 23)
> +#define EVT_TR                       (1L << 24)
> +
> +struct iommu_softc;
> +
> +int  ivhd_flush_devtab(struct iommu_softc *, int);
> +int  ivhd_invalidate_iommu_all(struct iommu_softc *);
> +int  ivhd_invalidate_interrupt_table(struct iommu_softc *, int);
> +int  ivhd_issue_command(struct iommu_softc *, const struct ivhd_command *, 
> int);
> +int  ivhd_invalidate_domain(struct iommu_softc *, int);
> +
> +void _dumppte(struct pte_entry *, int, vaddr_t);
> +
> +#endif
> diff --git a/sys/dev/acpi/files.acpi b/sys/dev/acpi/files.acpi
> index 252cc5bcf..13bcce896 100644
> --- a/sys/dev/acpi/files.acpi
> +++ b/sys/dev/acpi/files.acpi
> @@ -70,6 +70,11 @@ device     acpiprt
>  attach       acpiprt at acpi
>  file dev/acpi/acpiprt.c              acpiprt needs-flag
>  
> +# DMAR device
> +device       acpidmar
> +attach       acpidmar at acpi
> +file dev/acpi/acpidmar.c             acpidmar needs-flag
> +
>  # Docking station
>  device       acpidock
>  attach       acpidock at acpi
> 

Reply via email to