static int pte_type(u64 *pte)
{
- return *pte & PTE_TYPE_MASK;
+ return *pte & PTE_TYPE_VALID ? *pte & PTE_TYPE_MASK : PTE_TYPE_FAULT;
}
/* Returns the LSB number for a PTE on level <level> */
return NULL;
}
+/* Checks if the current PTE is an aligned subset of the region */
static bool is_aligned(u64 addr, u64 size, u64 align)
{
- return !(addr & (align - 1)) && !(size & (align - 1));
+ return !(addr & (align - 1)) && size >= align;
}
/* Use flag to indicate if attrs has more than d-cache attributes */
u64 levelsize = 1ULL << levelshift;
u64 *pte = find_pte(start, level);
- /* Can we can just modify the current level block PTE? */
+ /* Can we can just modify the current level block/page? */
if (is_aligned(start, size, levelsize)) {
- if (flag) {
+ if (attrs == PTE_TYPE_FAULT) {
+ if (pte_type(pte) == PTE_TYPE_TABLE && level < 3)
+ *pte = 0;
+ else
+ *pte &= ~(PTE_TYPE_MASK);
+ } else if (flag) {
*pte &= ~PMD_ATTRMASK;
*pte |= attrs & PMD_ATTRMASK;
} else {
gd->arch.tlb_addr + gd->arch.tlb_size);
__asm_invalidate_tlb_all();
+ /* If we were unmapping a region then we have nothing to make and can return. */
+ if (attrs == PTE_TYPE_FAULT)
+ return;
+
mmu_change_region_attr_nobreak(addr, size, attrs);
}