Index: /trunk/src/VBox/Devices/Bus/DevIommuAmd.cpp
===================================================================
--- /trunk/src/VBox/Devices/Bus/DevIommuAmd.cpp	(revision 87325)
+++ /trunk/src/VBox/Devices/Bus/DevIommuAmd.cpp	(revision 87326)
@@ -42,9 +42,11 @@
 /** The IOMMU device instance magic. */
 #define IOMMU_MAGIC                                 0x10acce55
-/** The maximum number of IOTLB entries in our cache implementation. */
-#define IOMMU_IOTLBE_MAX                            64
 /** Enable the IOTLBE cache. */
 #define IOMMU_WITH_IOTLBE_CACHE
+
 #ifdef IOMMU_WITH_IOTLBE_CACHE
+# define IOMMU_IOTLBE_PAGE_SIZE_MAX                 _4M
+/** The maximum number of IOTLB entries in our cache implementation. */
+# define IOMMU_IOTLBE_MAX                           (IOMMU_IOTLBE_PAGE_SIZE_MAX / _4K)
 /** The mask of bits for the domain ID of the IOTLBE key. */
 # define IOMMU_IOTLB_DOMAIN_ID_MASK                 UINT64_C(0xffffff0000000000)
@@ -133,5 +135,5 @@
     uint8_t         fIoPerm;
     /** Padding. */
-    uint8_t         abPadding[2];
+    uint16_t        abPadding[2];
 } IOWALKRESULT;
 /** Pointer to an I/O walk result struct. */
@@ -148,5 +150,5 @@
     /** The domain ID assigned by software. */
     uint16_t        uDomainId;
-    /** Whether the domain ID is valid (since all bits of domain ID are usable). */
+    /** Whether the device ID is valid (if not, lookups must re-read DTE). */
     bool            fValid;
     bool            afAlignment[1];
@@ -544,4 +546,15 @@
 #ifdef IOMMU_WITH_IOTLBE_CACHE
 /**
+ * @callback_method_impl{AVLU64CALLBACK}
+ */
+static DECLCALLBACK(int) iommuAmdR3DestroyIotlbe(PAVLU64NODECORE pCore, void *pvUser)
+{
+    RT_NOREF2(pCore, pvUser);
+    /* Nothing to do as we will destroy IOTLB entries wholesale later. */
+    return VINF_SUCCESS;
+}
+
+
+/**
  * Constructs the key for an IOTLB entry suitable for using as part of the IOTLB
  * cache.
@@ -607,4 +620,6 @@
 static void iommuAmdIotlbAdd(PIOMMU pThis, PCIOWALKRESULT pWalkResult)
 {
+    /** @todo Make sure somewhere we don't cache translations whose permissions are
+     *        IOMMU_IO_PERM_NONE. */
     /*
      * If the cache is full, evict the last recently used entry.
@@ -618,6 +633,4 @@
         RTAvlU64Remove(&pThis->TreeIotlbe, pIotlbe->Core.Key);
         --pThis->cCachedIotlbes;
-        /* Zero out IOTLB entry before reuse. */
-        RT_BZERO(pIotlbe, sizeof(IOTLBE));
     }
     else
@@ -628,6 +641,9 @@
     }
 
+    /* Zero out IOTLB entry before reuse. */
+    Assert(pIotlbe);
+    RT_ZERO(*pIotlbe);
+
     /* Update the entry with the results of the page walk. */
-    Assert(pIotlbe);
     pIotlbe->WalkResult = *pWalkResult;
 
@@ -640,4 +656,86 @@
      */
     RTListAppend(&pThis->LstLruIotlbe, &pIotlbe->NdLru);
+}
+
+
+/**
+ * Removes the IOTLB entry corresponding to the given domain ID and I/O virtual
+ * address.
+ *
+ * @param   pThis           The IOMMU device state.
+ * @param   uDomainId       The domain ID.
+ * @param   uIova           The I/O virtual address.
+ */
+static void iommuAmdIotlbRemove(PIOMMU pThis, uint16_t uDomainId, uint64_t uIova)
+{
+    uint64_t const uKey = iommuAmdIotlbConstructKey(uDomainId, uIova);
+    PIOTLBE pIotlbe = (PIOTLBE)RTAvlU64Remove(&pThis->TreeIotlbe, uKey);
+    if (pIotlbe)
+    {
+        RTListNodeRemove(&pIotlbe->NdLru);
+        --pThis->cCachedIotlbes;
+        RT_BZERO(pIotlbe, sizeof(IOTLBE));
+    }
+}
+
+
+/**
+ * Removes all IOTLB entries from the cache.
+ *
+ * @param   pThis   The IOMMU device state.
+ */
+static void iommuAmdIotlbRemoveAll(PIOMMU pThis)
+{
+    RTAvlU64Destroy(&pThis->TreeIotlbe, iommuAmdR3DestroyIotlbe, NULL /* pvParam */);
+    RTListInit(&pThis->LstLruIotlbe);
+    pThis->cCachedIotlbes = 0;
+}
+
+
+/**
+ * Removes a set of IOTLB entries from the cache given the domain ID, I/O virtual
+ * address and size.
+ *
+ * @param   pThis       The IOMMU device state.
+ * @param   uDomainId   The domain ID.
+ * @param   uIova       The I/O virtual address.
+ * @param   cShift      The number of bits to shift to get the size of the range
+ *                      being removed.
+ */
+static void iommuAmdIotlbRemoveRange(PIOMMU pThis, uint16_t uDomainId, uint64_t uIova, uint8_t cShift)
+{
+    /*
+     * Our cache is currently based on 4K pages. Pages larger than this are split into 4K pages
+     * before storing in the cache. So an 8K page will have 2 IOTLB entries, 16K will have 4 etc.
+     * However, our cache capacity is limited (see IOMMU_IOTLBE_MAX). Thus, if the guest uses pages
+     * larger than what our cache can split and hold, we will simply flush the entire cache when
+     * requested to flush a partial range since it exceeds the capacity anyway.
+     */
+    bool fFitsInCache;
+    /** @todo Find a more efficient way of checking split sizes? */
+    if (   cShift == 12 /* 4K  */
+        || cShift == 13 /* 8K  */
+        || cShift == 14 /* 16K */
+        || cShift == 20 /* 1M */)
+        fFitsInCache = true;
+    else
+        fFitsInCache = false;
+
+    if (fFitsInCache)
+    {
+        /* Mask off 4K page offset from the address. */
+        uIova &= X86_PAGE_4K_OFFSET_MASK;
+
+        /* Remove pages in the given range. */
+        uint16_t const cPages = (1 << cShift) / X86_PAGE_4K_SHIFT;
+        Assert(cPages <= IOMMU_IOTLBE_MAX);
+        for (uint32_t i = 0; i < cPages; i++)
+        {
+            iommuAmdIotlbRemove(pThis, uDomainId, uIova);
+            uIova += _4K;
+        }
+    }
+    else
+        iommuAmdIotlbRemoveAll(pThis);
 }
 #endif  /* IOMMU_WITH_IOTLBE_CACHE */
@@ -4570,17 +4668,4 @@
     return VERR_NOT_IMPLEMENTED;
 }
-
-
-#ifdef IOMMU_WITH_IOTLBE_CACHE
-/**
- * @callback_method_impl{AVLU64CALLBACK}
- */
-static DECLCALLBACK(int) iommuAmdR3DestroyIotlbe(PAVLU64NODECORE pCore, void *pvUser)
-{
-    RT_NOREF2(pCore, pvUser);
-    /* Nothing to do as we will destroy IOTLB entries wholesale later. */
-    return VINF_SUCCESS;
-}
-#endif
 
 
