iommu: msm: Handle unmapping of PTE properly

The unmap api is currently not handling unmapping of page table
entries (PTE) properly. The generic function that calls the msm
unmap API expects the unmap call to unmap as much as possible
and then return the amount that was unmapped.
In addition the unmap function does not support an arbitrary input
length. However, the function that calls the msm unmap function
assumes that this is supported.

Both these issues can cause mappings to not be unmapped which will
cause subsequent mappings to fail because the mapping already exists.

Change-Id: I638d5c38673abe297a701de9b7209c962564e1f1
Signed-off-by: Olav Haugan <ohaugan@codeaurora.org>
This commit is contained in:
Olav Haugan 2013-06-19 11:17:41 -07:00 committed by Artem Borisov
parent 9dac5e5da3
commit 43040691a3
1 changed files with 2 additions and 73 deletions

View File

@ -282,79 +282,8 @@ fail:
size_t msm_iommu_pagetable_unmap(struct iommu_pt *pt, unsigned long va,
size_t len)
{
unsigned long *fl_pte;
unsigned long fl_offset;
unsigned long *sl_table;
unsigned long *sl_pte;
unsigned long sl_offset;
int i, ret = 0;
if (len != SZ_16M && len != SZ_1M &&
len != SZ_64K && len != SZ_4K) {
pr_debug("Bad length: %d\n", len);
ret = -EINVAL;
goto fail;
}
if (!pt->fl_table) {
pr_debug("Null page table\n");
ret = -EINVAL;
goto fail;
}
fl_offset = FL_OFFSET(va); /* Upper 12 bits */
fl_pte = pt->fl_table + fl_offset; /* int pointers, 4 bytes */
if (*fl_pte == 0) {
pr_debug("First level PTE is 0\n");
ret = -ENODEV;
goto fail;
}
/* Unmap supersection */
if (len == SZ_16M) {
for (i = 0; i < 16; i++)
*(fl_pte+i) = 0;
clean_pte(fl_pte, fl_pte + 16, pt->redirect);
}
if (len == SZ_1M) {
*fl_pte = 0;
clean_pte(fl_pte, fl_pte + 1, pt->redirect);
}
sl_table = (unsigned long *) __va(((*fl_pte) & FL_BASE_MASK));
sl_offset = SL_OFFSET(va);
sl_pte = sl_table + sl_offset;
if (len == SZ_64K) {
for (i = 0; i < 16; i++)
*(sl_pte+i) = 0;
clean_pte(sl_pte, sl_pte + 16, pt->redirect);
}
if (len == SZ_4K) {
*sl_pte = 0;
clean_pte(sl_pte, sl_pte + 1, pt->redirect);
}
if (len == SZ_4K || len == SZ_64K) {
int used = 0;
for (i = 0; i < NUM_SL_PTE; i++)
if (sl_table[i])
used = 1;
if (!used) {
free_page((unsigned long)sl_table);
*fl_pte = 0;
clean_pte(fl_pte, fl_pte + 1, pt->redirect);
}
}
fail:
return ret;
msm_iommu_pagetable_unmap_range(pt, va, len);
return len;
}
static phys_addr_t get_phys_addr(struct scatterlist *sg)