This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

TTBR0_EL1 translation fault level 3 on 4KiB blocks where 2MiB blocks work.

I'm building kernel that is running ARMv8. I'm currently facing an issue with mapping of user processes.

TTBR1_EL1 flat-maps (identity mapping) 8GiB of memory via "PGD → PUD → 8x PMD → 512x 2MiB each" tables.
TTBR0_EL1 identity mapping works, but switching it to user process mapping fails with "translation fault, level 3". I managed to narrow it down to the size of the blocks I'm using: the user mapping works, if I use block descriptors in PMD, i.e. do a 2MiB mapping. Unfortunately, using it this way would require turning the whole allocator upside down, so I would really like to be able to use 4KiB.

The descriptors in question have the following form (modulo the PA which changes):

0x000000010000e701

My current SPSR, TCR, and MAIR setup looks as follows:

/// MAIR:
pub static MAIR_EL1: i64 = 0x0000_0000_0000_44_00ff

/// - [34:32] Set physical address range to 44 bits - this is the same as the
///           value reported by the ID_AA64MMFR0_EL1 register.
/// - [29:28] Set memory shareablitity to inner-sharable for TTBR1_EL1
/// - [31:30] Use 4KB granule size for high memory.
/// - [27:26] Outer cacheability set to W-B, R-A, W-A for TTBR1_EL1.
/// - [25:24] Inner cacheability set to W-B, R-A, W-A for TTBR1_EL1.
/// - [21:16] Size offset for high memory in bits - use 48 bits.
/// - [13:12] Set memory shareablitity to inner-sharable for TTBR0_EL1
/// - [15:14] Use 4KB granule size for low memory.
/// - [11:10] Outer cacheability set to W-B, R-A, W-A for TTBR0_EL1.
/// - [9:8]   Inner cacheability set to W-B, R-A, W-A for TTBR0_EL1.
/// - [5:0]   Size offset for low memory in bits - use 48 bits.
///
/// Used by boot.S
#[no_mangle]
pub static TCR_EL1_CONF: u64 = (0b100 << 32)
    | (0b10 << 30)
    | (0b11 << 28)
    | (0b01 << 26)
    | (0b01 << 24)
    | ((64 - 48) << 16)
    | (0b00 << 14)  // XXX: TG0 uses different values than TG1.
    | (0b11 << 12)
    | (0b01 << 10)
    | (0b01 << 8)
    | (64 - 48);

/// - [25]  Set exception endiannes at EL1 to little-endian (0)
/// - [24]  Set explicit data access at EL0 to little-endian (0)
/// - [19]  Do not mark writable regions as eXecute Never (0)
/// - [12]  Enable Instruction cache (0)
/// - [2]   Enable data cache (0)
/// - [0]   Enable MMU
/// - [...] RES1 - reserved, set to 1
#[no_mangle]
pub static SCTLR_EL1_CONF: i64 = (0 << 25)
    | (0 << 24)
    | (0 << 19)
    | (1 << 12)
    | (1 << 2)
    | (1 << 0)
    // Reserved-1 values
    | (1 << 23)
    | (1 << 22)
    | (1 << 20)
    | (1 << 11)
    | (1 << 8)
    | (1 << 7);


Why is the MMU not permitting me to use 4KiB here? How to fix it? Could it be related to the TTBR1 mapping still using 2MiB blocks?

Parents Reply Children
  • Regarding the 2MiB alignment, the MMU seems to work with that on BCM2711. Not sure if it's UB or implementation defined, but it just ignores the rest of the unaligned address

    It likely works because the MMU ignores the sub-2MB address bits on a level-2 block descriptor. The manual declares the bit#12 (among a few other bits) in a level-2 block descriptor as RES0.