arch/arm64: mmu: Spot check TTB memory attributes
On ARM64, the memory type for accessing page table descriptors during address translation is governed by the Translation Control Register (TCR). When the MMU code accesses the same descriptors to change page mappings, it uses the standard memory type rules (defined by the page table descriptor for the page that contains that table, or 'device' if the MMU is off). Accessing the same memory with different memory types can lead to all kinds of fun and hard to debug effects. In particular, if the TCR says "cacheable" and the page tables say "uncacheable", page table walks will pull stale entries into the cache and later mmu_config_range() calls will write directly to memory, bypassing those cache lines. This means the translations will not get updated even after a TLB flush, and later cache flushes/evictions may write the stale entries back to memory. Since page table configuration is currently always done from SoC code, we can't generally ensure that the TTB is always mapped as cacheable. We can however save developers of future SoCs a lot of headaches and time by spot checking the attributes when the MMU gets enabled, as this patch does. BRANCH=None BUG=None TEST=Booted Oak. Manually tested get_pte() with a few addresses. Change-Id: I3afd29dece848c4b5f759ce2f00ca2b7433374da Signed-off-by: Patrick Georgi <pgeorgi@chromium.org> Original-Commit-Id: f3947f4bb0abf4466006d5e3a962bbcb8919b12d Original-Change-Id: I1008883e5ed4cc37d30cae5777a60287d3d01af0 Original-Signed-off-by: Julius Werner <jwerner@chromium.org> Original-Reviewed-on: https://chromium-review.googlesource.com/323862 Original-Reviewed-by: Aaron Durbin <adurbin@chromium.org> Reviewed-on: https://review.coreboot.org/13595 Tested-by: build bot (Jenkins) Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
This commit is contained in:
parent
89c61b5630
commit
372d0ff1d1
|
@ -199,6 +199,26 @@ static void sanity_check(uint64_t addr, uint64_t size)
|
|||
size >= GRANULE_SIZE);
|
||||
}
|
||||
|
||||
/* Func : get_pte
|
||||
* Desc : Returns the page table entry governing a specific address. */
|
||||
static uint64_t get_pte(void *addr)
|
||||
{
|
||||
int shift = BITS_PER_VA > L1_ADDR_SHIFT ? L1_ADDR_SHIFT : L2_ADDR_SHIFT;
|
||||
uint64_t *pte = (uint64_t *)_ttb;
|
||||
|
||||
while (1) {
|
||||
int index = ((uintptr_t)addr >> shift) &
|
||||
((1UL << BITS_RESOLVED_PER_LVL) - 1);
|
||||
|
||||
if ((pte[index] & DESC_MASK) != TABLE_DESC ||
|
||||
shift <= GRANULE_SIZE_SHIFT)
|
||||
return pte[index];
|
||||
|
||||
pte = (uint64_t *)(pte[index] & XLAT_ADDR_MASK);
|
||||
shift -= BITS_RESOLVED_PER_LVL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Func : mmu_config_range
|
||||
* Desc : This function repeatedly calls init_xlat_table with the base
|
||||
* address. Based on size returned from init_xlat_table, base_addr is updated
|
||||
|
@ -256,6 +276,12 @@ void mmu_init(void)
|
|||
|
||||
void mmu_enable(void)
|
||||
{
|
||||
if (((get_pte(_ttb) >> BLOCK_INDEX_SHIFT) & BLOCK_INDEX_MASK)
|
||||
!= BLOCK_INDEX_MEM_NORMAL ||
|
||||
((get_pte(_ettb - 1) >> BLOCK_INDEX_SHIFT) & BLOCK_INDEX_MASK)
|
||||
!= BLOCK_INDEX_MEM_NORMAL)
|
||||
die("TTB memory type must match TCR (normal, cacheable)!");
|
||||
|
||||
uint32_t sctlr = raw_read_sctlr_el3();
|
||||
sctlr |= SCTLR_C | SCTLR_M | SCTLR_I;
|
||||
raw_write_sctlr_el3(sctlr);
|
||||
|
|
|
@ -102,6 +102,7 @@
|
|||
#define BLOCK_INDEX_MEM_NORMAL_NC 3
|
||||
#define BLOCK_INDEX_MEM_NORMAL 4
|
||||
|
||||
#define BLOCK_INDEX_MASK 0x7
|
||||
#define BLOCK_INDEX_SHIFT 2
|
||||
|
||||
/* MAIR attributes */
|
||||
|
|
Loading…
Reference in New Issue