|
@@ -263,6 +263,10 @@ static void __arm_lpae_set_pte(arm_lpae_iopte *ptep, arm_lpae_iopte pte,
|
|
sizeof(pte), DMA_TO_DEVICE);
|
|
sizeof(pte), DMA_TO_DEVICE);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+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,
|
|
static int arm_lpae_init_pte(struct arm_lpae_io_pgtable *data,
|
|
unsigned long iova, phys_addr_t paddr,
|
|
unsigned long iova, phys_addr_t paddr,
|
|
arm_lpae_iopte prot, int lvl,
|
|
arm_lpae_iopte prot, int lvl,
|
|
@@ -271,10 +275,21 @@ static int arm_lpae_init_pte(struct arm_lpae_io_pgtable *data,
|
|
arm_lpae_iopte pte = prot;
|
|
arm_lpae_iopte pte = prot;
|
|
struct io_pgtable_cfg *cfg = &data->iop.cfg;
|
|
struct io_pgtable_cfg *cfg = &data->iop.cfg;
|
|
|
|
|
|
- /* We require an unmap first */
|
|
|
|
if (iopte_leaf(*ptep, lvl)) {
|
|
if (iopte_leaf(*ptep, lvl)) {
|
|
|
|
+ /* We require an unmap first */
|
|
WARN_ON(!selftest_running);
|
|
WARN_ON(!selftest_running);
|
|
return -EEXIST;
|
|
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 (cfg->quirks & IO_PGTABLE_QUIRK_ARM_NS)
|
|
if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_NS)
|