These changes are a raw update to a vanilla kernel 4.1.10, with the
[kvmfornfv.git] / kernel / drivers / iommu / io-pgtable-arm.c
index 4e46021..e29d5d7 100644 (file)
@@ -200,6 +200,10 @@ typedef u64 arm_lpae_iopte;
 
 static bool selftest_running = false;
 
+static int __arm_lpae_unmap(struct arm_lpae_io_pgtable *data,
+                           unsigned long iova, size_t size, int lvl,
+                           arm_lpae_iopte *ptep);
+
 static int arm_lpae_init_pte(struct arm_lpae_io_pgtable *data,
                             unsigned long iova, phys_addr_t paddr,
                             arm_lpae_iopte prot, int lvl,
@@ -207,10 +211,21 @@ static int arm_lpae_init_pte(struct arm_lpae_io_pgtable *data,
 {
        arm_lpae_iopte pte = prot;
 
-       /* We require an unmap first */
        if (iopte_leaf(*ptep, lvl)) {
+               /* We require an unmap first */
                WARN_ON(!selftest_running);
                return -EEXIST;
+       } else if (iopte_type(*ptep, lvl) == ARM_LPAE_PTE_TYPE_TABLE) {
+               /*
+                * We need to unmap and free the old table before
+                * overwriting it with a block entry.
+                */
+               arm_lpae_iopte *tblp;
+               size_t sz = ARM_LPAE_BLOCK_SIZE(lvl, data);
+
+               tblp = ptep - ARM_LPAE_LVL_IDX(iova, lvl, data);
+               if (WARN_ON(__arm_lpae_unmap(data, iova, sz, lvl, tblp) != sz))
+                       return -EINVAL;
        }
 
        if (data->iop.cfg.quirks & IO_PGTABLE_QUIRK_ARM_NS)