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:
Julius Werner 2016-01-26 19:17:53 -08:00 committed by Patrick Georgi
parent 89c61b5630
commit 372d0ff1d1
2 changed files with 27 additions and 0 deletions

View File

@ -199,6 +199,26 @@ static void sanity_check(uint64_t addr, uint64_t size)
size >= GRANULE_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 /* Func : mmu_config_range
* Desc : This function repeatedly calls init_xlat_table with the base * Desc : This function repeatedly calls init_xlat_table with the base
* address. Based on size returned from init_xlat_table, base_addr is updated * 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) 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(); uint32_t sctlr = raw_read_sctlr_el3();
sctlr |= SCTLR_C | SCTLR_M | SCTLR_I; sctlr |= SCTLR_C | SCTLR_M | SCTLR_I;
raw_write_sctlr_el3(sctlr); raw_write_sctlr_el3(sctlr);

View File

@ -102,6 +102,7 @@
#define BLOCK_INDEX_MEM_NORMAL_NC 3 #define BLOCK_INDEX_MEM_NORMAL_NC 3
#define BLOCK_INDEX_MEM_NORMAL 4 #define BLOCK_INDEX_MEM_NORMAL 4
#define BLOCK_INDEX_MASK 0x7
#define BLOCK_INDEX_SHIFT 2 #define BLOCK_INDEX_SHIFT 2
/* MAIR attributes */ /* MAIR attributes */