Index: trunk/src/VBox/Devices/Bus/DevIommuIntel.cpp
===================================================================
--- trunk/src/VBox/Devices/Bus/DevIommuIntel.cpp	(revision 89417)
+++ trunk/src/VBox/Devices/Bus/DevIommuIntel.cpp	(revision 89418)
@@ -151,7 +151,7 @@
 /** DMA request permission: Write. */
 #define DMAR_PERM_WRITE                             RT_BIT(1)
-/** DMA request permission: Execute. */
+/** DMA request permission: Execute (ER). */
 #define DMAR_PERM_EXE                               RT_BIT(2)
-/** DMA request permission: Supervisor privilege. */
+/** DMA request permission: Supervisor privilege (PR). */
 #define DMAR_PERM_PRIV                              RT_BIT(3)
 /** DMA request permissions: All. */
@@ -189,4 +189,7 @@
     kDmarDiag_Atf_Lct_4_3,
     kDmarDiag_Atf_Lct_5,
+    kDmarDiag_Atf_Lgn_1_1,
+    kDmarDiag_Atf_Lgn_1_2,
+    kDmarDiag_Atf_Lgn_1_3,
     kDmarDiag_Atf_Lrt_1,
     kDmarDiag_Atf_Lrt_2,
@@ -198,4 +201,5 @@
     kDmarDiag_Atf_Rta_1_2,
     kDmarDiag_Atf_Rta_1_3,
+    kDmarDiag_Atf_Sgn_5,
     kDmarDiag_Atf_Ssl_1,
     kDmarDiag_Atf_Ssl_2,
@@ -262,4 +266,7 @@
     DMARDIAG_DESC(Atf_Lct_4_3               ),
     DMARDIAG_DESC(Atf_Lct_5                 ),
+    DMARDIAG_DESC(Atf_Lgn_1_1               ),
+    DMARDIAG_DESC(Atf_Lgn_1_2               ),
+    DMARDIAG_DESC(Atf_Lgn_1_3               ),
     DMARDIAG_DESC(Atf_Lrt_1                 ),
     DMARDIAG_DESC(Atf_Lrt_2                 ),
@@ -271,4 +278,5 @@
     DMARDIAG_DESC(Atf_Rta_1_2               ),
     DMARDIAG_DESC(Atf_Rta_1_3               ),
+    DMARDIAG_DESC(Atf_Sgn_5                 ),
     DMARDIAG_DESC(Atf_Ssl_1                 ),
     DMARDIAG_DESC(Atf_Ssl_2                 ),
@@ -344,8 +352,8 @@
     /** Host-address width (HAW) base address mask. */
     uint64_t                    fHawBaseMask;
-    /** Maximum guest-address width (MGAW) base address mask. */
-    uint64_t                    fMgawBaseMask;
+    /** Maximum guest-address width (MGAW) invalid address mask. */
+    uint64_t                    fInvMgawMask;
     /** Maximum supported paging level (3, 4 or 5). */
-    uint8_t                     uMaxPagingLevel;
+    uint8_t                     cMaxPagingLevel;
     /** DMA request valid permissions mask. */
     uint8_t                     fPermValidMask;
@@ -1884,7 +1892,7 @@
  * @param   pThis               The shared DMAR device state.
  * @param   pCtxEntry           The context entry.
- * @param   puPagingLevel       Where to store the paging level.
- */
-static bool dmarDrLegacyModeIsAwValid(PCDMAR pThis, PCVTD_CONTEXT_ENTRY_T pCtxEntry, uint8_t *puPagingLevel)
+ * @param   pcPagingLevel       Where to store the paging level.
+ */
+static bool dmarDrLegacyModeIsAwValid(PCDMAR pThis, PCVTD_CONTEXT_ENTRY_T pCtxEntry, uint8_t *pcPagingLevel)
 {
     uint8_t const fTt     = RT_BF_GET(pCtxEntry->au64[0], VTD_BF_0_CONTEXT_ENTRY_TT);
@@ -1894,11 +1902,11 @@
     Assert(!(fSagaw & ~(RT_BIT(1) | RT_BIT(2) | RT_BIT(3))));
 
-    *puPagingLevel = fAw + 2;
+    *pcPagingLevel = fAw + 2;
 
     /* With pass-through, the address width must be the largest AGAW supported by hardware. */
     if (fTt == VTD_TT_UNTRANSLATED_PT)
     {
-        Assert(pThis->uMaxPagingLevel >= 3 && pThis->uMaxPagingLevel <= 5); /* Paranoia. */
-        return *puPagingLevel == pThis->uMaxPagingLevel;
+        Assert(pThis->cMaxPagingLevel >= 3 && pThis->cMaxPagingLevel <= 5); /* Paranoia. */
+        return *pcPagingLevel == pThis->cMaxPagingLevel;
     }
 
@@ -1967,9 +1975,9 @@
  * @param   pDevIns         The IOMMU device instance.
  * @param   SlpEntry        The second-level paging entry.
- * @param   uPagingLevel    The paging level.
+ * @param   cPagingLevel    The paging level.
  * @param   idDomain        The domain ID for the translation.
  * @param   pAddrRemap      The DMA address remap info.
  */
-static int dmarDrSecondLevelTranslate(PPDMDEVINS pDevIns, VTD_SLP_ENTRY_T SlpEntry, uint8_t uPagingLevel, uint16_t idDomain,
+static int dmarDrSecondLevelTranslate(PPDMDEVINS pDevIns, VTD_SLP_ENTRY_T SlpEntry, uint8_t cPagingLevel, uint16_t idDomain,
                                       PDMARADDRMAP pAddrRemap)
 {
@@ -1983,5 +1991,5 @@
                                                  VTD_SL_PML5E_VALID_MASK };
 
-    /* Mask of valid large-page paging entry bits. */
+    /* Mask of valid large-page (1GB, 2MB) paging entries. */
     static uint64_t const s_auLargePageRsvd[] = { 0,
                                                   VTD_SL_PDE2M_VALID_MASK,
@@ -1991,7 +1999,20 @@
 
     /* Paranoia. */
-    Assert(uPagingLevel >= 3 && uPagingLevel <= 5);
+    Assert(cPagingLevel >= 3 && cPagingLevel <= 5);
     AssertCompile(RT_ELEMENTS(s_auPtEntityRsvd) == RT_ELEMENTS(s_auLargePageRsvd));
     AssertCompile(RT_ELEMENTS(s_auPtEntityRsvd) == 5);
+
+    /* Second-level translations restricts input address to an implementation-specific MGAW. */
+    uint64_t const uDmaAddr = pAddrRemap->uDmaAddr;
+    if (!(uDmaAddr & pThis->fInvMgawMask))
+    { /* likely */ }
+    else
+    {
+        if (pAddrRemap->fTtm == VTD_TTM_LEGACY_MODE)
+            dmarAtFaultRecord(pDevIns, kDmarDiag_Atf_Lgn_1_1, VTDATFAULT_LGN_1_1, pAddrRemap);
+        else
+            dmarAtFaultRecord(pDevIns, kDmarDiag_Atf_Sgn_5, VTDATFAULT_SGN_5, pAddrRemap);
+        return VERR_IOMMU_ADDR_TRANSLATION_FAILED;
+    }
 
     /*
@@ -1999,8 +2020,6 @@
      * Unlike AMD IOMMU paging, here there is no feature for "skipping" levels.
      */
-    uint64_t       uPtEntity    = SlpEntry;
-    uint64_t const uDmaAddr     = pAddrRemap->uDmaAddr & pThis->fMgawBaseMask;
-    uint64_t const fHawBaseMask = pThis->fHawBaseMask;
-    for (int8_t iLevel = uPagingLevel - 1; iLevel >= 0; iLevel--)
+    uint64_t uPtEntity = SlpEntry;
+    for (int8_t iLevel = cPagingLevel - 1; iLevel >= 0; iLevel--)
     {
         /*
@@ -2011,6 +2030,5 @@
             uint16_t const idxPte         = (uDmaAddr >> cLevelShift) & UINT64_C(0x1ff);
             uint64_t const offPte         = idxPte << 3;
-            /** @todo if we validate 63:HAW (since hardware treats it as reserved and we should fault if that's the case, we might not need to & fHawbaseMask here. */
-            RTGCPHYS const GCPhysPtEntity = (uPtEntity & fHawBaseMask) | offPte;
+            RTGCPHYS const GCPhysPtEntity = uPtEntity | offPte;
             int const rc = PDMDevHlpPhysReadMeta(pDevIns, GCPhysPtEntity, &uPtEntity, sizeof(uPtEntity));
             if (RT_SUCCESS(rc))
@@ -2168,6 +2186,6 @@
                                     {
                                         /* Validate the address width and get the paging level. */
-                                        uint8_t uPagingLevel;
-                                        if (dmarDrLegacyModeIsAwValid(pThis, &CtxEntry, &uPagingLevel))
+                                        uint8_t cPagingLevel;
+                                        if (dmarDrLegacyModeIsAwValid(pThis, &CtxEntry, &cPagingLevel))
                                         {
                                             /* Read the SLPTPTR from guest memory. */
@@ -2178,5 +2196,5 @@
                                             {
                                                 /* Finally... perform second-level translation. */
-                                                return dmarDrSecondLevelTranslate(pDevIns, SlpEntry, uPagingLevel, idDomain,
+                                                return dmarDrSecondLevelTranslate(pDevIns, SlpEntry, cPagingLevel, idDomain,
                                                                                   pAddrRemap);
                                             }
@@ -3461,7 +3479,7 @@
         dmarRegWriteRaw64(pThis, VTD_MMIO_OFF_CAP_REG, pThis->fCapReg);
 
-        pThis->fHawBaseMask    = ~(UINT64_MAX << cGstPhysAddrBits) & X86_PAGE_4K_BASE_MASK;
-        pThis->fMgawBaseMask   = pThis->fHawBaseMask;
-        pThis->uMaxPagingLevel = vtdCapRegGetMaxPagingLevel(fSagaw);
+        pThis->fHawBaseMask = ~(UINT64_MAX << cGstPhysAddrBits) & X86_PAGE_4K_BASE_MASK;
+        pThis->fInvMgawMask = UINT64_MAX << cGstPhysAddrBits;
+        pThis->cMaxPagingLevel = vtdCapRegGetMaxPagingLevel(fSagaw);
     }
 
@@ -3730,7 +3748,7 @@
     uint16_t const offFrcd   = RT_BF_GET(pThis->fCapReg, VTD_BF_CAP_REG_FRO);
     uint16_t const offIva    = RT_BF_GET(pThis->fExtCapReg, VTD_BF_ECAP_REG_IRO);
-    LogRel(("%s: VER=%u.%u CAP=%#RX64 ECAP=%#RX64 (MGAW=%u bits, SAGAW=%#x HAW=%#RX64 MGAW=%#RX64 FRO=%#x IRO=%#x) mapped at %#RGp\n",
+    LogRel(("%s: VER=%u.%u CAP=%#RX64 ECAP=%#RX64 (MGAW=%u bits, SAGAW=%#x HAW_Base=%#RX64 MGAW_Inv=%#RX64 FRO=%#x IRO=%#x) mapped at %#RGp\n",
             DMAR_LOG_PFX, RT_BF_GET(uVerReg, VTD_BF_VER_REG_MAX), RT_BF_GET(uVerReg, VTD_BF_VER_REG_MIN),
-            pThis->fCapReg, pThis->fExtCapReg, cMgawBits, fSagaw, pThis->fHawBaseMask, pThis->fMgawBaseMask, offFrcd, offIva,
+            pThis->fCapReg, pThis->fExtCapReg, cMgawBits, fSagaw, pThis->fHawBaseMask, pThis->fInvMgawMask, offFrcd, offIva,
             DMAR_MMIO_BASE_PHYSADDR));
 
