From 5aa5ef6cc47ca7101dceeebda23e418c681952ee Mon Sep 17 00:00:00 2001 From: Christian Bruel Date: Sun, 29 Mar 2026 14:20:34 +0200 Subject: [PATCH 1/3] aarch64: Restrict address mappings in loader.rs Limit the 1:1 address mapping in the loader to cover only the necessary blocks using level-2 translation tables and with 2MB blocks. Don't map device memory at all, therefore, printf must not be used in the elfloader after the MMU is enabled. This prevents speculative accesses to device or secure memory which otherwise would cause faults or unwanted side-effects. Signed-off-by: Christian Bruel --- loader/aarch64.ld | 4 +++ loader/src/aarch64/mmu.c | 1 + loader/src/uart.c | 4 +++ tool/microkit/src/loader.rs | 53 ++++++++++++++++++++++++++++++++++--- 4 files changed, 58 insertions(+), 4 deletions(-) diff --git a/loader/aarch64.ld b/loader/aarch64.ld index 6a583113d..977ccc574 100644 --- a/loader/aarch64.ld +++ b/loader/aarch64.ld @@ -12,6 +12,8 @@ SECTIONS { . = LINK_ADDRESS; + _loader_start = .; + .text : { _text = .; @@ -36,4 +38,6 @@ SECTIONS . = ALIGN(4); _bss_end = .; } :all + + _loader_end = .; } diff --git a/loader/src/aarch64/mmu.c b/loader/src/aarch64/mmu.c index 37a9030c4..8ef6427ce 100644 --- a/loader/src/aarch64/mmu.c +++ b/loader/src/aarch64/mmu.c @@ -23,6 +23,7 @@ uint64_t boot_lvl2_upper[1 << 9] ALIGN(1 << 12); /* Paging structures for identity mapping */ uint64_t boot_lvl0_lower[1 << 9] ALIGN(1 << 12); uint64_t boot_lvl1_lower[1 << 9] ALIGN(1 << 12); +uint64_t boot_lvl2_lower[1 << 9] ALIGN(1 << 12); int arch_mmu_enable(int logical_cpu) { diff --git a/loader/src/uart.c b/loader/src/uart.c index 54b182100..ad04f23ee 100644 --- a/loader/src/uart.c +++ b/loader/src/uart.c @@ -205,6 +205,10 @@ void putc(uint8_t ch) #error Board not defined #endif +#ifdef UART_BASE +uint32_t *uart_addr = (uint32_t *)UART_BASE; +#endif + void puts(const char *s) { while (*s) { diff --git a/tool/microkit/src/loader.rs b/tool/microkit/src/loader.rs index 7b8d9f773..24ea080be 100644 --- a/tool/microkit/src/loader.rs +++ b/tool/microkit/src/loader.rs @@ -487,20 +487,64 @@ impl<'a> Loader<'a> { let (boot_lvl0_upper_addr, boot_lvl0_upper_size) = elf .find_symbol("boot_lvl0_upper") .expect("Could not find 'boot_lvl0_upper' symbol"); + let (boot_lvl2_lower_addr, boot_lvl2_lower_size) = elf + .find_symbol("boot_lvl2_lower") + .expect("Could not find 'boot_lvl2_lower' symbol"); + let (start_addr, _) = elf + .find_symbol("_loader_start") + .expect("Could not find 'loader_start' symbol"); + let (end_addr, _) = elf + .find_symbol("_loader_end") + .expect("Could not find 'loader_end' symbol"); + + if Aarch64::lvl1_index(start_addr) != Aarch64::lvl1_index(end_addr) { + panic!("We only map 1GiB, but elfloader paddr range covers multiple GiB"); + } let mut boot_lvl0_lower: [u8; PAGE_TABLE_SIZE] = [0; PAGE_TABLE_SIZE]; boot_lvl0_lower[..8].copy_from_slice(&(boot_lvl1_lower_addr | 3).to_le_bytes()); let mut boot_lvl1_lower: [u8; PAGE_TABLE_SIZE] = [0; PAGE_TABLE_SIZE]; - for i in 0..512 { + + // map optional UART MMIO in l1 1GB page, only available if CONFIG_PRINTING + if let Ok((uart_addr, _)) = elf.find_symbol("uart_addr") { + let data = elf + .get_data(uart_addr, 8) + .expect("uart_addr not initialized"); + let uart_base = u64::from_le_bytes(data[0..8].try_into().unwrap()); + + let lvl1_idx = Aarch64::lvl1_index(uart_base); #[allow(clippy::identity_op)] // keep the (0 << 2) for clarity - let pt_entry: u64 = ((i as u64) << AARCH64_1GB_BLOCK_BITS) | + let pt_entry: u64 = ((lvl1_idx as u64) << AARCH64_1GB_BLOCK_BITS) | (1 << 10) | // access flag (0 << 2) | // strongly ordered memory - (1); // 1G block + (1 << 0); // 1G block + let start = 8 * lvl1_idx; + let end = 8 * (lvl1_idx + 1); + boot_lvl1_lower[start..end].copy_from_slice(&pt_entry.to_le_bytes()); + } + + let mut boot_lvl2_lower: [u8; PAGE_TABLE_SIZE] = [0; PAGE_TABLE_SIZE]; + + // 1GB lvl1 Table entry + let pt_entry = (boot_lvl2_lower_addr | 3).to_le_bytes(); + let lvl1_idx = Aarch64::lvl1_index(start_addr); + let start = 8 * lvl1_idx; + let end = 8 * (lvl1_idx + 1); + boot_lvl1_lower[start..end].copy_from_slice(&pt_entry); + + // map the loader 1:1 access into 2MB lvl2 Block entries for a 4KB granule + let lvl2_idx = Aarch64::lvl2_index(start_addr); + for i in lvl2_idx..=Aarch64::lvl2_index(end_addr) { + let entry_idx = (i - Aarch64::lvl2_index(start_addr)) << AARCH64_2MB_BLOCK_BITS; + let pt_entry: u64 = (entry_idx as u64 + start_addr) | + (1 << 10) | // Access flag + (3 << 8) | // Sharable + (0 << 2) | + (1 << 0); // 2M block let start = 8 * i; let end = 8 * (i + 1); - boot_lvl1_lower[start..end].copy_from_slice(&pt_entry.to_le_bytes()); + boot_lvl2_lower[start..end].copy_from_slice(&pt_entry.to_le_bytes()); } let boot_lvl0_upper: [u8; PAGE_TABLE_SIZE] = [0; PAGE_TABLE_SIZE]; @@ -538,6 +582,7 @@ impl<'a> Loader<'a> { (boot_lvl0_upper_addr, boot_lvl0_upper_size, boot_lvl0_upper), (boot_lvl1_upper_addr, boot_lvl1_upper_size, boot_lvl1_upper), (boot_lvl2_upper_addr, boot_lvl2_upper_size, boot_lvl2_upper), + (boot_lvl2_lower_addr, boot_lvl2_lower_size, boot_lvl2_lower), ] } } From 2ca916e0b816e7e5846514dbfdeb77d3665021d5 Mon Sep 17 00:00:00 2001 From: Ivan Velickovic Date: Fri, 24 Apr 2026 13:39:31 +1000 Subject: [PATCH 2/3] tool: loader map spin table page for BCM2711 The previous commit restricts the loader to only mapping itself rather than the first 1GB of memory. This is fine for most platforms as the only other thing they need to access is the UART which is now also separately mapped in. The one exception is the BCM2711/Raspberry Pi 4B where the a spin table is used for SMP booting which is located in the first page of memory, so we need to explicitly map that in as well. This code is a bit of hack since it assumes the CPU release addrs are always in the first 2MB of memory but the only platform we have right now that uses spin tables is the Raspberry Pi 4B. So we can merge the previous patch, we add this so that we don't have to regress the Raspberry Pi 4B. Signed-off-by: Ivan Velickovic --- tool/microkit/src/loader.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tool/microkit/src/loader.rs b/tool/microkit/src/loader.rs index 24ea080be..7640e49cc 100644 --- a/tool/microkit/src/loader.rs +++ b/tool/microkit/src/loader.rs @@ -547,6 +547,23 @@ impl<'a> Loader<'a> { boot_lvl2_lower[start..end].copy_from_slice(&pt_entry.to_le_bytes()); } + // TODO: this is a complete hack specific to BCM2711/Raspberry Pi 4B and + // will be removed with patches that re-do this loader mapping code. + if elf.find_symbol("cpus_release_addr").is_ok() { + let lvl2_idx = Aarch64::lvl2_index(0); + // Make sure we don't override the loader mappings done above. + assert!(Aarch64::lvl2_index(start_addr) != lvl2_idx); + assert!(Aarch64::lvl1_index(start_addr) == Aarch64::lvl1_index(0)); + #[allow(clippy::identity_op)] // keep the (0 << 2) for clarity + let pt_entry: u64 = ((lvl2_idx as u64) << AARCH64_2MB_BLOCK_BITS) | + (1 << 10) | // access flag + (0 << 2) | // strongly ordered memory + (1 << 0); // 2M block + let start = 8 * lvl2_idx; + let end = 8 * (lvl2_idx + 1); + boot_lvl2_lower[start..end].copy_from_slice(&pt_entry.to_le_bytes()); + } + let boot_lvl0_upper: [u8; PAGE_TABLE_SIZE] = [0; PAGE_TABLE_SIZE]; { let pt_entry = (boot_lvl1_upper_addr | 3).to_le_bytes(); From b9ccceb17b25a028d69dedb5bc898ec1a20fda01 Mon Sep 17 00:00:00 2001 From: Ivan Velickovic Date: Fri, 24 Apr 2026 13:16:45 +1000 Subject: [PATCH 3/3] Loader name consistency Signed-off-by: Ivan Velickovic --- loader/src/loader.c | 2 +- tool/microkit/src/loader.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/loader/src/loader.c b/loader/src/loader.c index 0b919cbed..67c09b149 100644 --- a/loader/src/loader.c +++ b/loader/src/loader.c @@ -157,7 +157,7 @@ int main(void) arch_init(); - puts("LDR|INFO: altloader for seL4 starting\n"); + puts("LDR|INFO: loader for seL4 starting\n"); /* Check that the loader magic number is set correctly */ if (loader_data->magic != MAGIC) { puts("LDR|ERROR: mismatch on loader data structure magic number\n"); diff --git a/tool/microkit/src/loader.rs b/tool/microkit/src/loader.rs index 7640e49cc..ba8d59a80 100644 --- a/tool/microkit/src/loader.rs +++ b/tool/microkit/src/loader.rs @@ -498,7 +498,7 @@ impl<'a> Loader<'a> { .expect("Could not find 'loader_end' symbol"); if Aarch64::lvl1_index(start_addr) != Aarch64::lvl1_index(end_addr) { - panic!("We only map 1GiB, but elfloader paddr range covers multiple GiB"); + panic!("We only map 1GiB, but loader paddr range covers multiple GiB"); } let mut boot_lvl0_lower: [u8; PAGE_TABLE_SIZE] = [0; PAGE_TABLE_SIZE];