Index: trunk/src/VBox/Devices/Bus/DevIommuIntel.cpp
===================================================================
--- trunk/src/VBox/Devices/Bus/DevIommuIntel.cpp	(revision 88568)
+++ trunk/src/VBox/Devices/Bus/DevIommuIntel.cpp	(revision 88574)
@@ -54,6 +54,26 @@
                                                      || (a_off) - DMAR_MMIO_GROUP_1_OFF_FIRST < DMAR_MMIO_GROUP_1_SIZE)
 
-/** @name DMAR implementation specifics.
- * @{ */
+/** Acquires the DMAR lock but returns with the given error code on failure. */
+#define DMAR_LOCK_RET(a_pDevIns, a_pThisCC, a_rcBusy) \
+    do { \
+        if ((a_pThisCC)->CTX_SUFF(pIommuHlp)->pfnLock((a_pDevIns), (a_rcBusy)) == VINF_SUCCESS) \
+        { /* likely */ } \
+        else \
+            return (a_rcBusy); \
+    } while (0)
+
+/** Release the DMAR lock. */
+#define DMAR_UNLOCK(a_pDevIns, a_pThisCC)           (a_pThisCC)->CTX_SUFF(pIommuHlp)->pfnUnlock(a_pDevIns)
+
+/** Checks whether the calling thread is the owner of the DMAR lock. */
+#define DMAR_LOCK_IS_OWNER(a_pDevIns, a_pThisCC)    (a_pThisCC)->CTX_SUFF(pIommuHlp)->pfnLockIsOwner(a_pDevIns)
+
+/** Asserts that the calling thread owns the DMAR lock. */
+#define DMAR_ASSERT_LOCK_IS_OWNER(a_pDevIns, a_pThisCC) \
+    do { \
+        Assert((a_pThisCC)->CTX_SUFF(pIommuHlp)->pfnLockIsOwner(a_pDevIns)); \
+        RT_NOREF1(a_pThisCC); \
+    } while (0)
+
 /** The number of fault recording registers our implementation supports.
  *  Normal guest operation shouldn't trigger faults anyway, so we only support the
@@ -98,5 +118,4 @@
 /** DMAR implementation's minor version number (exposed to software). */
 #define DMAR_VER_MINOR                              0
-/** @} */
 
 /** Release log prefix string. */
@@ -243,4 +262,6 @@
 /** Pointer to the DMAR device state for the current context. */
 typedef CTX_SUFF(PDMAR) PDMARCC;
+/** Pointer to the const DMAR device state for the current context. */
+typedef const CTX_SUFF(PDMAR) PCDMARCC;
 
 
@@ -606,4 +627,42 @@
 
 /**
+ * Modifies a 32-bit register.
+ *
+ * @param   pThis       The shared DMAR device state.
+ * @param   offReg      The MMIO offset of the register.
+ * @param   fAndMask    The AND mask (applied first).
+ * @param   fOrMask     The OR mask.
+ */
+static void dmarRegChange32(PDMAR pThis, uint16_t offReg, uint32_t fAndMask, uint32_t fOrMask)
+{
+    uint8_t idxGroup;
+    uint8_t *pabRegs = dmarRegGetGroup(pThis, offReg, sizeof(uint32_t), &idxGroup);
+    NOREF(idxGroup);
+    uint32_t uReg = *(uint32_t *)(pabRegs + offReg);
+    uReg = (uReg & fAndMask) | fOrMask;
+    *(uint32_t *)(pabRegs + offReg) = uReg;
+}
+
+
+/**
+ * Modifies a 64-bit register.
+ *
+ * @param   pThis       The shared DMAR device state.
+ * @param   offReg      The MMIO offset of the register.
+ * @param   fAndMask    The AND mask (applied first).
+ * @param   fOrMask     The OR mask.
+ */
+static void dmarRegChange64(PDMAR pThis, uint16_t offReg, uint64_t fAndMask, uint64_t fOrMask)
+{
+    uint8_t idxGroup;
+    uint8_t *pabRegs = dmarRegGetGroup(pThis, offReg, sizeof(uint64_t), &idxGroup);
+    NOREF(idxGroup);
+    uint64_t uReg = *(uint64_t *)(pabRegs + offReg);
+    uReg = (uReg & fAndMask) | fOrMask;
+    *(uint64_t *)(pabRegs + offReg) = uReg;
+}
+
+
+/**
  * Reads a 64-bit register with exactly the value it contains.
  *
@@ -751,4 +810,33 @@
     uint64_t const uRtAddrReg = dmarRegRead64(pThis, VTD_MMIO_OFF_RTADDR_REG);
     return RT_BF_GET(uRtAddrReg, VTD_BF_RTADDR_REG_TTM);
+}
+
+
+/**
+ * Raises an IQE (invalidation queue error) fault.
+ *
+ * @param   pDevIns     The IOMMU device instance.
+ * @param   pThis       The shared DMAR device state.
+ * @param   enmIqei     The IQE information.
+ * @param   enmDiag     The diagnostic reason.
+ */
+static void dmarFaultRaiseIqe(PPDMDEVINS pDevIns, DMARDIAG enmDiag, VTD_IQERCD_IQEI_T enmIqei)
+{
+    PDMAR    pThis   = PDMDEVINS_2_DATA(pDevIns, PDMAR);
+    PCDMARCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PCDMARCC);
+    DMAR_ASSERT_LOCK_IS_OWNER(pDevIns, pThisCC);
+
+    /* Set the error bit. */
+    uint32_t const fIqe = RT_BF_MAKE(VTD_BF_FSTS_REG_IQE, 1);
+    dmarRegChange32(pThis, VTD_MMIO_OFF_FSTS_REG, UINT32_MAX, fIqe);
+
+    /* Set the error information. */
+    uint64_t const fIqei = RT_BF_MAKE(VTD_BF_IQERCD_REG_IQEI, enmIqei);
+    dmarRegChange64(pThis, VTD_MMIO_OFF_IQERCD_REG, UINT64_MAX, fIqei);
+
+    /* Update diagnostic reason. */
+    pThis->enmDiag = enmDiag;
+
+    /** @todo Raise interrupt based on FECTL_REG. */
 }
 
@@ -820,8 +908,5 @@
         }
         else
-        {
-            /* Raise invalidation queue error as queue tail not aligned to 256-bits. */
-            /** @todo Raise error. */
-        }
+            dmarFaultRaiseIqe(pDevIns, kDmarDiag_IqtReg_Qt_NotAligned, kQueueTailNotAligned);
     }
     return VINF_SUCCESS;
@@ -969,4 +1054,24 @@
 
 #ifdef IN_RING3
+/**
+ * @callback_method_impl{FNDBGFHANDLERDEV}
+ */
+static DECLCALLBACK(void) dmarR3DbgInfo(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
+{
+    PCDMAR      pThis   = PDMDEVINS_2_DATA(pDevIns, PDMAR);
+    PCPDMPCIDEV pPciDev = pDevIns->apPciDevs[0];
+    PDMPCIDEV_ASSERT_VALID(pDevIns, pPciDev);
+
+    bool const fVerbose = RTStrCmp(pszArgs, "verbose") == 0;
+
+    DMARDIAG const enmDiag = pThis->enmDiag;
+    const char *pszDiag    = enmDiag < RT_ELEMENTS(g_apszDmarDiagDesc) ? g_apszDmarDiagDesc[enmDiag] : "(Unknown)";
+
+    pHlp->pfnPrintf(pHlp, "Intel-IOMMU:\n");
+    pHlp->pfnPrintf(pHlp, " Diag = %u (%s)\n", enmDiag, pszDiag);
+    pHlp->pfnPrintf(pHlp, "\n");
+}
+
+
 /**
  * Initializes all registers in the DMAR unit.
@@ -1214,4 +1319,9 @@
     AssertRCReturn(rc, rc);
 
+    /*
+     * Register debugger info items.
+     */
+    PDMDevHlpDBGFInfoRegister(pDevIns, "iommu", "Display IOMMU state.", dmarR3DbgInfo);
+
 #ifdef VBOX_WITH_STATISTICS
     /*
@@ -1294,6 +1404,7 @@
     AssertReturn(pThisCC->CTX_SUFF(pIommuHlp)->u32Version == CTX_SUFF(PDM_IOMMUHLP)_VERSION, VERR_VERSION_MISMATCH);
     AssertReturn(pThisCC->CTX_SUFF(pIommuHlp)->u32TheEnd  == CTX_SUFF(PDM_IOMMUHLP)_VERSION, VERR_VERSION_MISMATCH);
-    AssertPtrReturn(pThisCC->CTX_SUFF(pIommuHlp)->pfnLock,   VERR_INVALID_POINTER);
-    AssertPtrReturn(pThisCC->CTX_SUFF(pIommuHlp)->pfnUnlock, VERR_INVALID_POINTER);
+    AssertPtrReturn(pThisCC->CTX_SUFF(pIommuHlp)->pfnLock,        VERR_INVALID_POINTER);
+    AssertPtrReturn(pThisCC->CTX_SUFF(pIommuHlp)->pfnUnlock,      VERR_INVALID_POINTER);
+    AssertPtrReturn(pThisCC->CTX_SUFF(pIommuHlp)->pfnLockIsOwner, VERR_INVALID_POINTER);
 
     return VINF_SUCCESS;
