From 1d2b5ad0221f64f2e3de0dba793ff46713d1426c Mon Sep 17 00:00:00 2001 From: Andre Richter Date: Fri, 1 Jan 2021 21:03:18 +0100 Subject: [PATCH] Memory Mapping: Improve various aspects --- .../README.md | 49 ++++++++--- .../src/_arch/aarch64/memory/mmu.rs | 16 +++- .../src/bsp/raspberrypi/memory.rs | 13 +++ .../src/bsp/raspberrypi/memory/mmu.rs | 7 +- 12_exceptions_part1_groundwork/README.md | 80 ++++++++--------- .../src/_arch/aarch64/memory/mmu.rs | 16 +++- .../src/bsp/raspberrypi/memory.rs | 13 +++ .../src/bsp/raspberrypi/memory/mmu.rs | 7 +- 12_exceptions_part1_groundwork/src/main.rs | 4 +- 13_integrated_testing/README.md | 8 +- .../src/_arch/aarch64/memory/mmu.rs | 16 +++- .../src/bsp/raspberrypi/memory.rs | 13 +++ .../src/bsp/raspberrypi/memory/mmu.rs | 7 +- 14_exceptions_part2_peripheral_IRQs/README.md | 4 +- .../src/_arch/aarch64/memory/mmu.rs | 16 +++- .../src/bsp/raspberrypi/memory.rs | 13 +++ .../src/bsp/raspberrypi/memory/mmu.rs | 7 +- 15_virtual_mem_part2_mmio_remap/README.md | 86 ++++++++++++------- .../src/_arch/aarch64/memory/mmu.rs | 12 ++- 19 files changed, 278 insertions(+), 109 deletions(-) diff --git a/11_virtual_mem_part1_identity_mapping/README.md b/11_virtual_mem_part1_identity_mapping/README.md index c8c35972..df84c966 100644 --- a/11_virtual_mem_part1_identity_mapping/README.md +++ b/11_virtual_mem_part1_identity_mapping/README.md @@ -140,7 +140,6 @@ struct FixedSizeTranslationTable { lvl2: [TableDescriptor; NUM_TABLES], } -/// Usually evaluates to 1 GiB for RPi3 and 4 GiB for RPi 4. const NUM_LVL2_TABLES: usize = bsp::memory::mmu::addr_space_size() >> FIVETWELVE_MIB_SHIFT; type ArchTranslationTable = FixedSizeTranslationTable; @@ -205,7 +204,7 @@ virtual addresses: - Since we identity map the whole `Device MMIO` region, it is accessible by asserting its physical base address (`0x3F20_1000` or `0xFA20_1000` depending on which RPi you use) after the `MMU` is turned on. -- Additionally, it is also mapped into the last `64 KiB` entry of the `lvl3` table, making it +- Additionally, it is also mapped into the last `64 KiB` slot in the first `512 MiB`, making it accessible through base address `0x1FFF_1000`. The following block diagram visualizes the underlying translation for the second mapping. @@ -303,7 +302,7 @@ Minipush 1.0 diff -uNr 10_privilege_level/src/_arch/aarch64/memory/mmu.rs 11_virtual_mem_part1_identity_mapping/src/_arch/aarch64/memory/mmu.rs --- 10_privilege_level/src/_arch/aarch64/memory/mmu.rs +++ 11_virtual_mem_part1_identity_mapping/src/_arch/aarch64/memory/mmu.rs -@@ -0,0 +1,333 @@ +@@ -0,0 +1,343 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2021 Andre Richter @@ -343,6 +342,12 @@ diff -uNr 10_privilege_level/src/_arch/aarch64/memory/mmu.rs 11_virtual_mem_part +// A level 3 page descriptor, as per ARMv8-A Architecture Reference Manual Figure D5-17. +register_bitfields! {u64, + STAGE1_PAGE_DESCRIPTOR [ ++ /// Unprivileged execute-never. ++ UXN OFFSET(54) NUMBITS(1) [ ++ False = 0, ++ True = 1 ++ ], ++ + /// Privileged execute-never. + PXN OFFSET(53) NUMBITS(1) [ + False = 0, @@ -416,7 +421,6 @@ diff -uNr 10_privilege_level/src/_arch/aarch64/memory/mmu.rs 11_virtual_mem_part + lvl2: [TableDescriptor; NUM_TABLES], +} + -+/// Usually evaluates to 1 GiB for RPi3 and 4 GiB for RPi 4. +const NUM_LVL2_TABLES: usize = bsp::memory::mmu::addr_space_size() >> FIVETWELVE_MIB_SHIFT; +type ArchTranslationTable = FixedSizeTranslationTable; + @@ -500,13 +504,16 @@ diff -uNr 10_privilege_level/src/_arch/aarch64/memory/mmu.rs 11_virtual_mem_part + AccessPermissions::ReadWrite => STAGE1_PAGE_DESCRIPTOR::AP::RW_EL1, + }; + -+ // Execute Never. ++ // The execute-never attribute is mapped to PXN in AArch64. + desc += if attribute_fields.execute_never { + STAGE1_PAGE_DESCRIPTOR::PXN::True + } else { + STAGE1_PAGE_DESCRIPTOR::PXN::False + }; + ++ // Always set unprivileged exectue-never as long as userspace is not implemented yet. ++ desc += STAGE1_PAGE_DESCRIPTOR::UXN::True; ++ + desc + } +} @@ -579,16 +586,18 @@ diff -uNr 10_privilege_level/src/_arch/aarch64/memory/mmu.rs 11_virtual_mem_part +/// Configure various settings of stage 1 of the EL1 translation regime. +fn configure_translation_control() { + let ips = ID_AA64MMFR0_EL1.read(ID_AA64MMFR0_EL1::PARange); ++ let t0sz: u64 = bsp::memory::mmu::addr_space_size().trailing_zeros().into(); + + TCR_EL1.write( + TCR_EL1::TBI0::Ignored + + TCR_EL1::IPS.val(ips) ++ + TCR_EL1::EPD1::DisableTTBR1Walks + + TCR_EL1::TG0::KiB_64 + + TCR_EL1::SH0::Inner + + TCR_EL1::ORGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + + TCR_EL1::IRGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + + TCR_EL1::EPD0::EnableTTBR0Walks -+ + TCR_EL1::T0SZ.val(32), // TTBR0 spans 4 GiB total. ++ + TCR_EL1::T0SZ.val(t0sz), + ); +} + @@ -662,7 +671,7 @@ diff -uNr 10_privilege_level/src/bsp/raspberrypi/link.ld 11_virtual_mem_part1_id diff -uNr 10_privilege_level/src/bsp/raspberrypi/memory/mmu.rs 11_virtual_mem_part1_identity_mapping/src/bsp/raspberrypi/memory/mmu.rs --- 10_privilege_level/src/bsp/raspberrypi/memory/mmu.rs +++ 11_virtual_mem_part1_identity_mapping/src/bsp/raspberrypi/memory/mmu.rs -@@ -0,0 +1,88 @@ +@@ -0,0 +1,93 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2021 Andre Richter @@ -743,8 +752,13 @@ diff -uNr 10_privilege_level/src/bsp/raspberrypi/memory/mmu.rs 11_virtual_mem_pa +//-------------------------------------------------------------------------------------------------- + +/// Return the address space size in bytes. ++/// ++/// Guarantees size to be a power of two. +pub const fn addr_space_size() -> usize { -+ memory_map::END_INCLUSIVE + 1 ++ let size = memory_map::END_INCLUSIVE + 1; ++ assert!(size.is_power_of_two()); ++ ++ size +} + +/// Return a reference to the virtual memory layout. @@ -773,16 +787,29 @@ diff -uNr 10_privilege_level/src/bsp/raspberrypi/memory.rs 11_virtual_mem_part1_ } //-------------------------------------------------------------------------------------------------- -@@ -23,6 +27,8 @@ +@@ -23,6 +27,21 @@ /// The board's memory map. #[rustfmt::skip] pub(super) mod map { ++ /// The inclusive end address of the memory map. ++ /// ++ /// End address + 1 must be power of two. ++ /// ++ /// # Note ++ /// ++ /// RPi3 and RPi4 boards can have different amounts of RAM. To make our code lean for ++ /// educational purposes, we set the max size of the address space to 4 GiB regardless of board. ++ /// This way, we can map the entire range that we need (end of MMIO for RPi4) in one take. ++ /// ++ /// However, making this trade-off has the downside of making it possible for the CPU to assert a ++ /// physical address that is not backed by any DRAM (e.g. accessing an address close to 4 GiB on ++ /// an RPi3 that comes with 1 GiB of RAM). This would result in a crash or other kind of error. + pub const END_INCLUSIVE: usize = 0xFFFF_FFFF; + pub const BOOT_CORE_STACK_END: usize = 0x8_0000; pub const GPIO_OFFSET: usize = 0x0020_0000; -@@ -36,6 +42,7 @@ +@@ -36,6 +55,7 @@ pub const START: usize = 0x3F00_0000; pub const GPIO_START: usize = START + GPIO_OFFSET; pub const PL011_UART_START: usize = START + UART_OFFSET; @@ -790,7 +817,7 @@ diff -uNr 10_privilege_level/src/bsp/raspberrypi/memory.rs 11_virtual_mem_part1_ } /// Physical devices. -@@ -46,10 +53,35 @@ +@@ -46,10 +66,35 @@ pub const START: usize = 0xFE00_0000; pub const GPIO_START: usize = START + GPIO_OFFSET; pub const PL011_UART_START: usize = START + UART_OFFSET; diff --git a/11_virtual_mem_part1_identity_mapping/src/_arch/aarch64/memory/mmu.rs b/11_virtual_mem_part1_identity_mapping/src/_arch/aarch64/memory/mmu.rs index aeacbe8e..69023cef 100644 --- a/11_virtual_mem_part1_identity_mapping/src/_arch/aarch64/memory/mmu.rs +++ b/11_virtual_mem_part1_identity_mapping/src/_arch/aarch64/memory/mmu.rs @@ -37,6 +37,12 @@ register_bitfields! {u64, // A level 3 page descriptor, as per ARMv8-A Architecture Reference Manual Figure D5-17. register_bitfields! {u64, STAGE1_PAGE_DESCRIPTOR [ + /// Unprivileged execute-never. + UXN OFFSET(54) NUMBITS(1) [ + False = 0, + True = 1 + ], + /// Privileged execute-never. PXN OFFSET(53) NUMBITS(1) [ False = 0, @@ -110,7 +116,6 @@ struct FixedSizeTranslationTable { lvl2: [TableDescriptor; NUM_TABLES], } -/// Usually evaluates to 1 GiB for RPi3 and 4 GiB for RPi 4. const NUM_LVL2_TABLES: usize = bsp::memory::mmu::addr_space_size() >> FIVETWELVE_MIB_SHIFT; type ArchTranslationTable = FixedSizeTranslationTable; @@ -194,13 +199,16 @@ impl convert::From AccessPermissions::ReadWrite => STAGE1_PAGE_DESCRIPTOR::AP::RW_EL1, }; - // Execute Never. + // The execute-never attribute is mapped to PXN in AArch64. desc += if attribute_fields.execute_never { STAGE1_PAGE_DESCRIPTOR::PXN::True } else { STAGE1_PAGE_DESCRIPTOR::PXN::False }; + // Always set unprivileged exectue-never as long as userspace is not implemented yet. + desc += STAGE1_PAGE_DESCRIPTOR::UXN::True; + desc } } @@ -273,16 +281,18 @@ unsafe fn populate_tt_entries() -> Result<(), &'static str> { /// Configure various settings of stage 1 of the EL1 translation regime. fn configure_translation_control() { let ips = ID_AA64MMFR0_EL1.read(ID_AA64MMFR0_EL1::PARange); + let t0sz: u64 = bsp::memory::mmu::addr_space_size().trailing_zeros().into(); TCR_EL1.write( TCR_EL1::TBI0::Ignored + TCR_EL1::IPS.val(ips) + + TCR_EL1::EPD1::DisableTTBR1Walks + TCR_EL1::TG0::KiB_64 + TCR_EL1::SH0::Inner + TCR_EL1::ORGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + TCR_EL1::IRGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + TCR_EL1::EPD0::EnableTTBR0Walks - + TCR_EL1::T0SZ.val(32), // TTBR0 spans 4 GiB total. + + TCR_EL1::T0SZ.val(t0sz), ); } diff --git a/11_virtual_mem_part1_identity_mapping/src/bsp/raspberrypi/memory.rs b/11_virtual_mem_part1_identity_mapping/src/bsp/raspberrypi/memory.rs index 9ab30232..d4bcf0a3 100644 --- a/11_virtual_mem_part1_identity_mapping/src/bsp/raspberrypi/memory.rs +++ b/11_virtual_mem_part1_identity_mapping/src/bsp/raspberrypi/memory.rs @@ -27,6 +27,19 @@ extern "Rust" { /// The board's memory map. #[rustfmt::skip] pub(super) mod map { + /// The inclusive end address of the memory map. + /// + /// End address + 1 must be power of two. + /// + /// # Note + /// + /// RPi3 and RPi4 boards can have different amounts of RAM. To make our code lean for + /// educational purposes, we set the max size of the address space to 4 GiB regardless of board. + /// This way, we can map the entire range that we need (end of MMIO for RPi4) in one take. + /// + /// However, making this trade-off has the downside of making it possible for the CPU to assert a + /// physical address that is not backed by any DRAM (e.g. accessing an address close to 4 GiB on + /// an RPi3 that comes with 1 GiB of RAM). This would result in a crash or other kind of error. pub const END_INCLUSIVE: usize = 0xFFFF_FFFF; pub const BOOT_CORE_STACK_END: usize = 0x8_0000; diff --git a/11_virtual_mem_part1_identity_mapping/src/bsp/raspberrypi/memory/mmu.rs b/11_virtual_mem_part1_identity_mapping/src/bsp/raspberrypi/memory/mmu.rs index 6930b675..982fc065 100644 --- a/11_virtual_mem_part1_identity_mapping/src/bsp/raspberrypi/memory/mmu.rs +++ b/11_virtual_mem_part1_identity_mapping/src/bsp/raspberrypi/memory/mmu.rs @@ -78,8 +78,13 @@ fn mmio_range_inclusive() -> RangeInclusive { //-------------------------------------------------------------------------------------------------- /// Return the address space size in bytes. +/// +/// Guarantees size to be a power of two. pub const fn addr_space_size() -> usize { - memory_map::END_INCLUSIVE + 1 + let size = memory_map::END_INCLUSIVE + 1; + assert!(size.is_power_of_two()); + + size } /// Return a reference to the virtual memory layout. diff --git a/12_exceptions_part1_groundwork/README.md b/12_exceptions_part1_groundwork/README.md index f70a33c5..97963686 100644 --- a/12_exceptions_part1_groundwork/README.md +++ b/12_exceptions_part1_groundwork/README.md @@ -350,14 +350,14 @@ we cause a data abort exception by reading from memory address `8 GiB`: // For demo purposes, the exception handler will catch the faulting 8 GiB address and allow // execution to continue. info!(""); -info!("Trying to write to address 8 GiB..."); +info!("Trying to read from address 8 GiB..."); let mut big_addr: u64 = 8 * 1024 * 1024 * 1024; unsafe { core::ptr::read_volatile(big_addr as *mut u64) }; ``` This triggers our exception code, because we try to read from a virtual address for which no mapping -has been installed. Remember, we only installed identity-mapped page tables for the first `1 GiB` -(RPi3) or `4 GiB` (RPi4) of address space in the previous tutorial. +has been installed. Remember, we only installed up to `4 GiB` of address space in the previous +tutorial. To survive this exception, the respective handler has a special demo case: @@ -412,29 +412,29 @@ Minipush 1.0 [MP] ⏩ Pushing 64 KiB ========================================🦀 100% 32 KiB/s Time: 00:00:02 [ML] Loaded! Executing the payload now -[ 3.090618] Booting on: Raspberry Pi 3 -[ 3.091701] MMU online. Special regions: -[ 3.093610] 0x00080000 - 0x0008ffff | 64 KiB | C RO PX | Kernel code and RO data -[ 3.097688] 0x3f000000 - 0x4000ffff | 16 MiB | Dev RW PXN | Device MMIO -[ 3.101246] Current privilege level: EL1 -[ 3.103155] Exception handling state: -[ 3.104934] Debug: Masked -[ 3.106496] SError: Masked -[ 3.108058] IRQ: Masked -[ 3.109619] FIQ: Masked -[ 3.111181] Architectural timer resolution: 52 ns -[ 3.113481] Drivers loaded: -[ 3.114826] 1. BCM GPIO -[ 3.116257] 2. BCM PL011 UART -[ 3.117950] Timer test, spinning for 1 second -[ 4.120076] -[ 4.120079] Trying to write to address 8 GiB... -[ 4.122242] ************************************************ -[ 4.125018] Whoa! We recovered from a synchronous exception! -[ 4.127795] ************************************************ -[ 4.130571] -[ 4.131266] Let's try again -[ 4.132611] Trying to write to address 9 GiB... +[ 3.091032] Booting on: Raspberry Pi 3 +[ 3.092116] MMU online. Special regions: +[ 3.094025] 0x00080000 - 0x0008ffff | 64 KiB | C RO PX | Kernel code and RO data +[ 3.098103] 0x3f000000 - 0x4000ffff | 16 MiB | Dev RW PXN | Device MMIO +[ 3.101661] Current privilege level: EL1 +[ 3.103570] Exception handling state: +[ 3.105348] Debug: Masked +[ 3.106910] SError: Masked +[ 3.108472] IRQ: Masked +[ 3.110034] FIQ: Masked +[ 3.111596] Architectural timer resolution: 52 ns +[ 3.113895] Drivers loaded: +[ 3.115240] 1. BCM GPIO +[ 3.116672] 2. BCM PL011 UART +[ 3.118364] Timer test, spinning for 1 second +[ 4.120490] +[ 4.120494] Trying to read from address 8 GiB... +[ 4.122700] ************************************************ +[ 4.125476] Whoa! We recovered from a synchronous exception! +[ 4.128253] ************************************************ +[ 4.131030] +[ 4.131724] Let's try again +[ 4.133069] Trying to read from address 9 GiB... Kernel panic: @@ -443,7 +443,7 @@ FAR_EL1: 0x0000000240000000 ESR_EL1: 0x96000004 Exception Class (EC) : 0x25 - Data Abort, current EL Instr Specific Syndrome (ISS): 0x4 -ELR_EL1: 0x0000000000081454 +ELR_EL1: 0x0000000000081458 SPSR_EL1: 0x600003c5 Flags: Negative (N): Not set @@ -458,22 +458,22 @@ SPSR_EL1: 0x600003c5 Illegal Execution State (IL): Not set General purpose register: - x0 : 0x0000000000000000 x1 : 0x0000000000085726 - x2 : 0x0000000000000026 x3 : 0x0000000000083bf0 - x4 : 0x0000000000000003 x5 : 0xfb4f101900000000 - x6 : 0x0000000000000000 x7 : 0x7e9198052b2b0200 + x0 : 0x0000000000000000 x1 : 0x0000000000085727 + x2 : 0x0000000000000027 x3 : 0x0000000000000000 + x4 : 0x0000000000000002 x5 : 0x3f27329c00000000 + x6 : 0x0000000000000000 x7 : 0xdbd1b90800000000 x8 : 0x0000000240000000 x9 : 0x000000003f201000 - x10: 0x0000000000000019 x11: 0x0000000000000000 - x12: 0x0000000000000006 x13: 0x0000000000000031 + x10: 0x0000000000000019 x11: 0x00000000000819d0 + x12: 0x0000000000000000 x13: 0x0000000000000033 x14: 0x000000000007fc2d x15: 0x0000000000000000 - x16: 0x0000000000000040 x17: 0xb557f006f276cfb6 + x16: 0x0000000000000040 x17: 0xfd7f702255f847d0 x18: 0x0000000000000003 x19: 0x0000000000090008 x20: 0x0000000000085510 x21: 0x000000003b9aca00 - x22: 0x00000000000003e8 x23: 0x000000000008160c - x24: 0x0000000000082264 x25: 0x00000000000f4240 + x22: 0x00000000000003e8 x23: 0x0000000000081610 + x24: 0x0000000000082268 x25: 0x00000000000f4240 x26: 0xffffffffc4653600 x27: 0x00000000000855f0 - x28: 0x0000000000083f84 x29: 0x0000000000086810 - lr : 0x0000000000081448 + x28: 0x0000000000083f80 x29: 0x0000000000086810 + lr : 0x000000000008144c ``` ## Diff to previous @@ -989,7 +989,7 @@ diff -uNr 11_virtual_mem_part1_identity_mapping/src/main.rs 12_exceptions_part1_ + // For demo purposes, the exception handler will catch the faulting 8 GiB address and allow + // execution to continue. + info!(""); -+ info!("Trying to write to address 8 GiB..."); ++ info!("Trying to read from address 8 GiB..."); + let mut big_addr: u64 = 8 * 1024 * 1024 * 1024; + unsafe { core::ptr::read_volatile(big_addr as *mut u64) }; + @@ -1000,7 +1000,7 @@ diff -uNr 11_virtual_mem_part1_identity_mapping/src/main.rs 12_exceptions_part1_ + info!("Let's try again"); + + // Now use address 9 GiB. The exception handler won't forgive us this time. -+ info!("Trying to write to address 9 GiB..."); ++ info!("Trying to read from address 9 GiB..."); + big_addr = 9 * 1024 * 1024 * 1024; + unsafe { core::ptr::read_volatile(big_addr as *mut u64) }; diff --git a/12_exceptions_part1_groundwork/src/_arch/aarch64/memory/mmu.rs b/12_exceptions_part1_groundwork/src/_arch/aarch64/memory/mmu.rs index aeacbe8e..69023cef 100644 --- a/12_exceptions_part1_groundwork/src/_arch/aarch64/memory/mmu.rs +++ b/12_exceptions_part1_groundwork/src/_arch/aarch64/memory/mmu.rs @@ -37,6 +37,12 @@ register_bitfields! {u64, // A level 3 page descriptor, as per ARMv8-A Architecture Reference Manual Figure D5-17. register_bitfields! {u64, STAGE1_PAGE_DESCRIPTOR [ + /// Unprivileged execute-never. + UXN OFFSET(54) NUMBITS(1) [ + False = 0, + True = 1 + ], + /// Privileged execute-never. PXN OFFSET(53) NUMBITS(1) [ False = 0, @@ -110,7 +116,6 @@ struct FixedSizeTranslationTable { lvl2: [TableDescriptor; NUM_TABLES], } -/// Usually evaluates to 1 GiB for RPi3 and 4 GiB for RPi 4. const NUM_LVL2_TABLES: usize = bsp::memory::mmu::addr_space_size() >> FIVETWELVE_MIB_SHIFT; type ArchTranslationTable = FixedSizeTranslationTable; @@ -194,13 +199,16 @@ impl convert::From AccessPermissions::ReadWrite => STAGE1_PAGE_DESCRIPTOR::AP::RW_EL1, }; - // Execute Never. + // The execute-never attribute is mapped to PXN in AArch64. desc += if attribute_fields.execute_never { STAGE1_PAGE_DESCRIPTOR::PXN::True } else { STAGE1_PAGE_DESCRIPTOR::PXN::False }; + // Always set unprivileged exectue-never as long as userspace is not implemented yet. + desc += STAGE1_PAGE_DESCRIPTOR::UXN::True; + desc } } @@ -273,16 +281,18 @@ unsafe fn populate_tt_entries() -> Result<(), &'static str> { /// Configure various settings of stage 1 of the EL1 translation regime. fn configure_translation_control() { let ips = ID_AA64MMFR0_EL1.read(ID_AA64MMFR0_EL1::PARange); + let t0sz: u64 = bsp::memory::mmu::addr_space_size().trailing_zeros().into(); TCR_EL1.write( TCR_EL1::TBI0::Ignored + TCR_EL1::IPS.val(ips) + + TCR_EL1::EPD1::DisableTTBR1Walks + TCR_EL1::TG0::KiB_64 + TCR_EL1::SH0::Inner + TCR_EL1::ORGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + TCR_EL1::IRGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + TCR_EL1::EPD0::EnableTTBR0Walks - + TCR_EL1::T0SZ.val(32), // TTBR0 spans 4 GiB total. + + TCR_EL1::T0SZ.val(t0sz), ); } diff --git a/12_exceptions_part1_groundwork/src/bsp/raspberrypi/memory.rs b/12_exceptions_part1_groundwork/src/bsp/raspberrypi/memory.rs index 9ab30232..d4bcf0a3 100644 --- a/12_exceptions_part1_groundwork/src/bsp/raspberrypi/memory.rs +++ b/12_exceptions_part1_groundwork/src/bsp/raspberrypi/memory.rs @@ -27,6 +27,19 @@ extern "Rust" { /// The board's memory map. #[rustfmt::skip] pub(super) mod map { + /// The inclusive end address of the memory map. + /// + /// End address + 1 must be power of two. + /// + /// # Note + /// + /// RPi3 and RPi4 boards can have different amounts of RAM. To make our code lean for + /// educational purposes, we set the max size of the address space to 4 GiB regardless of board. + /// This way, we can map the entire range that we need (end of MMIO for RPi4) in one take. + /// + /// However, making this trade-off has the downside of making it possible for the CPU to assert a + /// physical address that is not backed by any DRAM (e.g. accessing an address close to 4 GiB on + /// an RPi3 that comes with 1 GiB of RAM). This would result in a crash or other kind of error. pub const END_INCLUSIVE: usize = 0xFFFF_FFFF; pub const BOOT_CORE_STACK_END: usize = 0x8_0000; diff --git a/12_exceptions_part1_groundwork/src/bsp/raspberrypi/memory/mmu.rs b/12_exceptions_part1_groundwork/src/bsp/raspberrypi/memory/mmu.rs index 1114bcce..59d736a7 100644 --- a/12_exceptions_part1_groundwork/src/bsp/raspberrypi/memory/mmu.rs +++ b/12_exceptions_part1_groundwork/src/bsp/raspberrypi/memory/mmu.rs @@ -63,8 +63,13 @@ fn mmio_range_inclusive() -> RangeInclusive { //-------------------------------------------------------------------------------------------------- /// Return the address space size in bytes. +/// +/// Guarantees size to be a power of two. pub const fn addr_space_size() -> usize { - memory_map::END_INCLUSIVE + 1 + let size = memory_map::END_INCLUSIVE + 1; + assert!(size.is_power_of_two()); + + size } /// Return a reference to the virtual memory layout. diff --git a/12_exceptions_part1_groundwork/src/main.rs b/12_exceptions_part1_groundwork/src/main.rs index 66a32d08..4d8f5d7c 100644 --- a/12_exceptions_part1_groundwork/src/main.rs +++ b/12_exceptions_part1_groundwork/src/main.rs @@ -204,7 +204,7 @@ fn kernel_main() -> ! { // For demo purposes, the exception handler will catch the faulting 8 GiB address and allow // execution to continue. info!(""); - info!("Trying to write to address 8 GiB..."); + info!("Trying to read from address 8 GiB..."); let mut big_addr: u64 = 8 * 1024 * 1024 * 1024; unsafe { core::ptr::read_volatile(big_addr as *mut u64) }; @@ -215,7 +215,7 @@ fn kernel_main() -> ! { info!("Let's try again"); // Now use address 9 GiB. The exception handler won't forgive us this time. - info!("Trying to write to address 9 GiB..."); + info!("Trying to read from address 9 GiB..."); big_addr = 9 * 1024 * 1024 * 1024; unsafe { core::ptr::read_volatile(big_addr as *mut u64) }; diff --git a/13_integrated_testing/README.md b/13_integrated_testing/README.md index 3ecdc039..5f8baf23 100644 --- a/13_integrated_testing/README.md +++ b/13_integrated_testing/README.md @@ -990,7 +990,7 @@ diff -uNr 12_exceptions_part1_groundwork/src/_arch/aarch64/exception.rs 13_integ diff -uNr 12_exceptions_part1_groundwork/src/_arch/aarch64/memory/mmu.rs 13_integrated_testing/src/_arch/aarch64/memory/mmu.rs --- 12_exceptions_part1_groundwork/src/_arch/aarch64/memory/mmu.rs +++ 13_integrated_testing/src/_arch/aarch64/memory/mmu.rs -@@ -331,3 +331,40 @@ +@@ -341,3 +341,40 @@ Ok(()) } } @@ -1053,7 +1053,7 @@ diff -uNr 12_exceptions_part1_groundwork/src/bsp/raspberrypi/console.rs 13_integ diff -uNr 12_exceptions_part1_groundwork/src/bsp/raspberrypi/memory/mmu.rs 13_integrated_testing/src/bsp/raspberrypi/memory/mmu.rs --- 12_exceptions_part1_groundwork/src/bsp/raspberrypi/memory/mmu.rs +++ 13_integrated_testing/src/bsp/raspberrypi/memory/mmu.rs -@@ -71,3 +71,46 @@ +@@ -76,3 +76,46 @@ pub fn virt_mem_layout() -> &'static KernelVirtualLayout<{ NUM_MEM_RANGES }> { &LAYOUT } @@ -1469,7 +1469,7 @@ diff -uNr 12_exceptions_part1_groundwork/src/main.rs 13_integrated_testing/src/m - // For demo purposes, the exception handler will catch the faulting 8 GiB address and allow - // execution to continue. - info!(""); -- info!("Trying to write to address 8 GiB..."); +- info!("Trying to read from address 8 GiB..."); - let mut big_addr: u64 = 8 * 1024 * 1024 * 1024; - unsafe { core::ptr::read_volatile(big_addr as *mut u64) }; - @@ -1480,7 +1480,7 @@ diff -uNr 12_exceptions_part1_groundwork/src/main.rs 13_integrated_testing/src/m - info!("Let's try again"); - - // Now use address 9 GiB. The exception handler won't forgive us this time. -- info!("Trying to write to address 9 GiB..."); +- info!("Trying to read from address 9 GiB..."); - big_addr = 9 * 1024 * 1024 * 1024; - unsafe { core::ptr::read_volatile(big_addr as *mut u64) }; - diff --git a/13_integrated_testing/src/_arch/aarch64/memory/mmu.rs b/13_integrated_testing/src/_arch/aarch64/memory/mmu.rs index 5061f600..9b658e86 100644 --- a/13_integrated_testing/src/_arch/aarch64/memory/mmu.rs +++ b/13_integrated_testing/src/_arch/aarch64/memory/mmu.rs @@ -37,6 +37,12 @@ register_bitfields! {u64, // A level 3 page descriptor, as per ARMv8-A Architecture Reference Manual Figure D5-17. register_bitfields! {u64, STAGE1_PAGE_DESCRIPTOR [ + /// Unprivileged execute-never. + UXN OFFSET(54) NUMBITS(1) [ + False = 0, + True = 1 + ], + /// Privileged execute-never. PXN OFFSET(53) NUMBITS(1) [ False = 0, @@ -110,7 +116,6 @@ struct FixedSizeTranslationTable { lvl2: [TableDescriptor; NUM_TABLES], } -/// Usually evaluates to 1 GiB for RPi3 and 4 GiB for RPi 4. const NUM_LVL2_TABLES: usize = bsp::memory::mmu::addr_space_size() >> FIVETWELVE_MIB_SHIFT; type ArchTranslationTable = FixedSizeTranslationTable; @@ -194,13 +199,16 @@ impl convert::From AccessPermissions::ReadWrite => STAGE1_PAGE_DESCRIPTOR::AP::RW_EL1, }; - // Execute Never. + // The execute-never attribute is mapped to PXN in AArch64. desc += if attribute_fields.execute_never { STAGE1_PAGE_DESCRIPTOR::PXN::True } else { STAGE1_PAGE_DESCRIPTOR::PXN::False }; + // Always set unprivileged exectue-never as long as userspace is not implemented yet. + desc += STAGE1_PAGE_DESCRIPTOR::UXN::True; + desc } } @@ -273,16 +281,18 @@ unsafe fn populate_tt_entries() -> Result<(), &'static str> { /// Configure various settings of stage 1 of the EL1 translation regime. fn configure_translation_control() { let ips = ID_AA64MMFR0_EL1.read(ID_AA64MMFR0_EL1::PARange); + let t0sz: u64 = bsp::memory::mmu::addr_space_size().trailing_zeros().into(); TCR_EL1.write( TCR_EL1::TBI0::Ignored + TCR_EL1::IPS.val(ips) + + TCR_EL1::EPD1::DisableTTBR1Walks + TCR_EL1::TG0::KiB_64 + TCR_EL1::SH0::Inner + TCR_EL1::ORGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + TCR_EL1::IRGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + TCR_EL1::EPD0::EnableTTBR0Walks - + TCR_EL1::T0SZ.val(32), // TTBR0 spans 4 GiB total. + + TCR_EL1::T0SZ.val(t0sz), ); } diff --git a/13_integrated_testing/src/bsp/raspberrypi/memory.rs b/13_integrated_testing/src/bsp/raspberrypi/memory.rs index 9ab30232..d4bcf0a3 100644 --- a/13_integrated_testing/src/bsp/raspberrypi/memory.rs +++ b/13_integrated_testing/src/bsp/raspberrypi/memory.rs @@ -27,6 +27,19 @@ extern "Rust" { /// The board's memory map. #[rustfmt::skip] pub(super) mod map { + /// The inclusive end address of the memory map. + /// + /// End address + 1 must be power of two. + /// + /// # Note + /// + /// RPi3 and RPi4 boards can have different amounts of RAM. To make our code lean for + /// educational purposes, we set the max size of the address space to 4 GiB regardless of board. + /// This way, we can map the entire range that we need (end of MMIO for RPi4) in one take. + /// + /// However, making this trade-off has the downside of making it possible for the CPU to assert a + /// physical address that is not backed by any DRAM (e.g. accessing an address close to 4 GiB on + /// an RPi3 that comes with 1 GiB of RAM). This would result in a crash or other kind of error. pub const END_INCLUSIVE: usize = 0xFFFF_FFFF; pub const BOOT_CORE_STACK_END: usize = 0x8_0000; diff --git a/13_integrated_testing/src/bsp/raspberrypi/memory/mmu.rs b/13_integrated_testing/src/bsp/raspberrypi/memory/mmu.rs index d41de813..485b8a46 100644 --- a/13_integrated_testing/src/bsp/raspberrypi/memory/mmu.rs +++ b/13_integrated_testing/src/bsp/raspberrypi/memory/mmu.rs @@ -63,8 +63,13 @@ fn mmio_range_inclusive() -> RangeInclusive { //-------------------------------------------------------------------------------------------------- /// Return the address space size in bytes. +/// +/// Guarantees size to be a power of two. pub const fn addr_space_size() -> usize { - memory_map::END_INCLUSIVE + 1 + let size = memory_map::END_INCLUSIVE + 1; + assert!(size.is_power_of_two()); + + size } /// Return a reference to the virtual memory layout. diff --git a/14_exceptions_part2_peripheral_IRQs/README.md b/14_exceptions_part2_peripheral_IRQs/README.md index 58e1bf22..042c3f60 100644 --- a/14_exceptions_part2_peripheral_IRQs/README.md +++ b/14_exceptions_part2_peripheral_IRQs/README.md @@ -2145,7 +2145,7 @@ diff -uNr 13_integrated_testing/src/bsp/raspberrypi/exception.rs 14_exceptions_p diff -uNr 13_integrated_testing/src/bsp/raspberrypi/memory.rs 14_exceptions_part2_peripheral_IRQs/src/bsp/raspberrypi/memory.rs --- 13_integrated_testing/src/bsp/raspberrypi/memory.rs +++ 14_exceptions_part2_peripheral_IRQs/src/bsp/raspberrypi/memory.rs -@@ -39,10 +39,12 @@ +@@ -52,10 +52,12 @@ pub mod mmio { use super::*; @@ -2162,7 +2162,7 @@ diff -uNr 13_integrated_testing/src/bsp/raspberrypi/memory.rs 14_exceptions_part } /// Physical devices. -@@ -53,6 +55,8 @@ +@@ -66,6 +68,8 @@ pub const START: usize = 0xFE00_0000; pub const GPIO_START: usize = START + GPIO_OFFSET; pub const PL011_UART_START: usize = START + UART_OFFSET; diff --git a/14_exceptions_part2_peripheral_IRQs/src/_arch/aarch64/memory/mmu.rs b/14_exceptions_part2_peripheral_IRQs/src/_arch/aarch64/memory/mmu.rs index 5061f600..9b658e86 100644 --- a/14_exceptions_part2_peripheral_IRQs/src/_arch/aarch64/memory/mmu.rs +++ b/14_exceptions_part2_peripheral_IRQs/src/_arch/aarch64/memory/mmu.rs @@ -37,6 +37,12 @@ register_bitfields! {u64, // A level 3 page descriptor, as per ARMv8-A Architecture Reference Manual Figure D5-17. register_bitfields! {u64, STAGE1_PAGE_DESCRIPTOR [ + /// Unprivileged execute-never. + UXN OFFSET(54) NUMBITS(1) [ + False = 0, + True = 1 + ], + /// Privileged execute-never. PXN OFFSET(53) NUMBITS(1) [ False = 0, @@ -110,7 +116,6 @@ struct FixedSizeTranslationTable { lvl2: [TableDescriptor; NUM_TABLES], } -/// Usually evaluates to 1 GiB for RPi3 and 4 GiB for RPi 4. const NUM_LVL2_TABLES: usize = bsp::memory::mmu::addr_space_size() >> FIVETWELVE_MIB_SHIFT; type ArchTranslationTable = FixedSizeTranslationTable; @@ -194,13 +199,16 @@ impl convert::From AccessPermissions::ReadWrite => STAGE1_PAGE_DESCRIPTOR::AP::RW_EL1, }; - // Execute Never. + // The execute-never attribute is mapped to PXN in AArch64. desc += if attribute_fields.execute_never { STAGE1_PAGE_DESCRIPTOR::PXN::True } else { STAGE1_PAGE_DESCRIPTOR::PXN::False }; + // Always set unprivileged exectue-never as long as userspace is not implemented yet. + desc += STAGE1_PAGE_DESCRIPTOR::UXN::True; + desc } } @@ -273,16 +281,18 @@ unsafe fn populate_tt_entries() -> Result<(), &'static str> { /// Configure various settings of stage 1 of the EL1 translation regime. fn configure_translation_control() { let ips = ID_AA64MMFR0_EL1.read(ID_AA64MMFR0_EL1::PARange); + let t0sz: u64 = bsp::memory::mmu::addr_space_size().trailing_zeros().into(); TCR_EL1.write( TCR_EL1::TBI0::Ignored + TCR_EL1::IPS.val(ips) + + TCR_EL1::EPD1::DisableTTBR1Walks + TCR_EL1::TG0::KiB_64 + TCR_EL1::SH0::Inner + TCR_EL1::ORGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + TCR_EL1::IRGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + TCR_EL1::EPD0::EnableTTBR0Walks - + TCR_EL1::T0SZ.val(32), // TTBR0 spans 4 GiB total. + + TCR_EL1::T0SZ.val(t0sz), ); } diff --git a/14_exceptions_part2_peripheral_IRQs/src/bsp/raspberrypi/memory.rs b/14_exceptions_part2_peripheral_IRQs/src/bsp/raspberrypi/memory.rs index 44c20e2c..4ac3b57b 100644 --- a/14_exceptions_part2_peripheral_IRQs/src/bsp/raspberrypi/memory.rs +++ b/14_exceptions_part2_peripheral_IRQs/src/bsp/raspberrypi/memory.rs @@ -27,6 +27,19 @@ extern "Rust" { /// The board's memory map. #[rustfmt::skip] pub(super) mod map { + /// The inclusive end address of the memory map. + /// + /// End address + 1 must be power of two. + /// + /// # Note + /// + /// RPi3 and RPi4 boards can have different amounts of RAM. To make our code lean for + /// educational purposes, we set the max size of the address space to 4 GiB regardless of board. + /// This way, we can map the entire range that we need (end of MMIO for RPi4) in one take. + /// + /// However, making this trade-off has the downside of making it possible for the CPU to assert a + /// physical address that is not backed by any DRAM (e.g. accessing an address close to 4 GiB on + /// an RPi3 that comes with 1 GiB of RAM). This would result in a crash or other kind of error. pub const END_INCLUSIVE: usize = 0xFFFF_FFFF; pub const BOOT_CORE_STACK_END: usize = 0x8_0000; diff --git a/14_exceptions_part2_peripheral_IRQs/src/bsp/raspberrypi/memory/mmu.rs b/14_exceptions_part2_peripheral_IRQs/src/bsp/raspberrypi/memory/mmu.rs index d41de813..485b8a46 100644 --- a/14_exceptions_part2_peripheral_IRQs/src/bsp/raspberrypi/memory/mmu.rs +++ b/14_exceptions_part2_peripheral_IRQs/src/bsp/raspberrypi/memory/mmu.rs @@ -63,8 +63,13 @@ fn mmio_range_inclusive() -> RangeInclusive { //-------------------------------------------------------------------------------------------------- /// Return the address space size in bytes. +/// +/// Guarantees size to be a power of two. pub const fn addr_space_size() -> usize { - memory_map::END_INCLUSIVE + 1 + let size = memory_map::END_INCLUSIVE + 1; + assert!(size.is_power_of_two()); + + size } /// Return a reference to the virtual memory layout. diff --git a/15_virtual_mem_part2_mmio_remap/README.md b/15_virtual_mem_part2_mmio_remap/README.md index f7a9b192..ab11fbfa 100644 --- a/15_virtual_mem_part2_mmio_remap/README.md +++ b/15_virtual_mem_part2_mmio_remap/README.md @@ -357,7 +357,7 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/_arch/aarch64/memory/mmu.rs 15 // A table descriptor, as per ARMv8-A Architecture Reference Manual Figure D5-15. register_bitfields! {u64, -@@ -81,9 +91,6 @@ +@@ -87,9 +97,6 @@ ] } @@ -367,7 +367,7 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/_arch/aarch64/memory/mmu.rs 15 /// A table descriptor for 64 KiB aperture. /// /// The output points to the next table. -@@ -98,36 +105,65 @@ +@@ -104,35 +111,65 @@ #[repr(transparent)] struct PageDescriptor(u64); @@ -417,7 +417,6 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/_arch/aarch64/memory/mmu.rs 15 lvl2: [TableDescriptor; NUM_TABLES], -} --/// Usually evaluates to 1 GiB for RPi3 and 4 GiB for RPi 4. -const NUM_LVL2_TABLES: usize = bsp::memory::mmu::addr_space_size() >> FIVETWELVE_MIB_SHIFT; -type ArchTranslationTable = FixedSizeTranslationTable; + /// Index of the next free MMIO page. @@ -449,7 +448,7 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/_arch/aarch64/memory/mmu.rs 15 //-------------------------------------------------------------------------------------------------- // Global instances -@@ -138,7 +174,8 @@ +@@ -143,7 +180,8 @@ /// # Safety /// /// - Supposed to land in `.bss`. Therefore, ensure that all initial member values boil down to "0". @@ -459,7 +458,7 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/_arch/aarch64/memory/mmu.rs 15 static MMU: MemoryManagementUnit = MemoryManagementUnit; -@@ -146,13 +183,15 @@ +@@ -151,13 +189,15 @@ // Private Code //-------------------------------------------------------------------------------------------------- @@ -481,7 +480,7 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/_arch/aarch64/memory/mmu.rs 15 } } -@@ -160,7 +199,7 @@ +@@ -165,7 +205,7 @@ fn from(next_lvl_table_addr: usize) -> Self { let val = InMemoryRegister::::new(0); @@ -490,7 +489,7 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/_arch/aarch64/memory/mmu.rs 15 val.write( STAGE1_TABLE_DESCRIPTOR::VALID::True + STAGE1_TABLE_DESCRIPTOR::TYPE::Table -@@ -207,23 +246,33 @@ +@@ -215,23 +255,33 @@ impl PageDescriptor { /// Create an instance. @@ -527,7 +526,7 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/_arch/aarch64/memory/mmu.rs 15 /// Create an instance. pub const fn new() -> Self { assert!(NUM_TABLES > 0); -@@ -231,7 +280,55 @@ +@@ -239,7 +289,55 @@ Self { lvl3: [[PageDescriptor(0); 8192]; NUM_TABLES], lvl2: [TableDescriptor(0); NUM_TABLES], @@ -583,7 +582,7 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/_arch/aarch64/memory/mmu.rs 15 } } -@@ -248,28 +345,6 @@ +@@ -256,32 +354,9 @@ ); } @@ -612,16 +611,20 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/_arch/aarch64/memory/mmu.rs 15 /// Configure various settings of stage 1 of the EL1 translation regime. fn configure_translation_control() { let ips = ID_AA64MMFR0_EL1.read(ID_AA64MMFR0_EL1::PARange); -@@ -282,7 +357,7 @@ +- let t0sz: u64 = bsp::memory::mmu::addr_space_size().trailing_zeros().into(); + + TCR_EL1.write( + TCR_EL1::TBI0::Ignored +@@ -292,7 +367,7 @@ + TCR_EL1::ORGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + TCR_EL1::IRGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + TCR_EL1::EPD0::EnableTTBR0Walks -- + TCR_EL1::T0SZ.val(32), // TTBR0 spans 4 GiB total. +- + TCR_EL1::T0SZ.val(t0sz), + + TCR_EL1::T0SZ.val(T0SZ), ); } -@@ -290,17 +365,126 @@ +@@ -300,17 +375,126 @@ // Public Code //-------------------------------------------------------------------------------------------------- @@ -751,7 +754,7 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/_arch/aarch64/memory/mmu.rs 15 // Fail early if translation granule is not supported. Both RPis support it, though. if !ID_AA64MMFR0_EL1.matches_all(ID_AA64MMFR0_EL1::TGran64::Supported) { return Err("Translation granule not supported in HW"); -@@ -309,11 +493,8 @@ +@@ -319,11 +503,8 @@ // Prepare the memory attribute indirection register. set_up_mair(); @@ -764,7 +767,7 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/_arch/aarch64/memory/mmu.rs 15 configure_translation_control(); -@@ -337,6 +518,9 @@ +@@ -347,6 +528,9 @@ //-------------------------------------------------------------------------------------------------- #[cfg(test)] @@ -774,7 +777,7 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/_arch/aarch64/memory/mmu.rs 15 mod tests { use super::*; use test_macros::kernel_test; -@@ -363,7 +547,7 @@ +@@ -373,7 +557,7 @@ #[kernel_test] fn kernel_tables_in_bss() { let bss_range = bsp::memory::bss_range_inclusive(); @@ -1465,7 +1468,7 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/bsp/raspberrypi/link.ld 15_vir diff -uNr 14_exceptions_part2_peripheral_IRQs/src/bsp/raspberrypi/memory/mmu.rs 15_virtual_mem_part2_mmio_remap/src/bsp/raspberrypi/memory/mmu.rs --- 14_exceptions_part2_peripheral_IRQs/src/bsp/raspberrypi/memory/mmu.rs +++ 15_virtual_mem_part2_mmio_remap/src/bsp/raspberrypi/memory/mmu.rs -@@ -4,72 +4,128 @@ +@@ -4,77 +4,128 @@ //! BSP Memory Management Unit. @@ -1584,23 +1587,22 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/bsp/raspberrypi/memory/mmu.rs //-------------------------------------------------------------------------------------------------- -/// Return the address space size in bytes. --pub const fn addr_space_size() -> usize { -- memory_map::END_INCLUSIVE + 1 +/// Pointer to the last page of the physical address space. +pub fn phys_addr_space_end_page() -> *const Page { + common::align_down( + super::phys_addr_space_end().into_usize(), + KernelGranule::SIZE, + ) as *const Page<_> - } - --/// Return a reference to the virtual memory layout. --pub fn virt_mem_layout() -> &'static KernelVirtualLayout<{ NUM_MEM_RANGES }> { -- &LAYOUT ++} ++ +/// Map the kernel binary. +/// +/// # Safety -+/// + /// +-/// Guarantees size to be a power of two. +-pub const fn addr_space_size() -> usize { +- let size = memory_map::END_INCLUSIVE + 1; +- assert!(size.is_power_of_two()); +/// - Any miscalculation or attribute error will likely be fatal. Needs careful manual checking. +pub unsafe fn kernel_map_binary() -> Result<(), &'static str> { + kernel_mmu::kernel_map_pages_at( @@ -1613,7 +1615,9 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/bsp/raspberrypi/memory/mmu.rs + execute_never: true, + }, + )?; -+ + +- size +-} + kernel_mmu::kernel_map_pages_at( + "Kernel code and RO data", + &phys_ro_page_desc(), @@ -1624,7 +1628,10 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/bsp/raspberrypi/memory/mmu.rs + execute_never: false, + }, + )?; -+ + +-/// Return a reference to the virtual memory layout. +-pub fn virt_mem_layout() -> &'static KernelVirtualLayout<{ NUM_MEM_RANGES }> { +- &LAYOUT + kernel_mmu::kernel_map_pages_at( + "Kernel data and bss", + &phys_data_page_desc(), @@ -1640,7 +1647,7 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/bsp/raspberrypi/memory/mmu.rs } //-------------------------------------------------------------------------------------------------- -@@ -84,14 +140,12 @@ +@@ -89,14 +140,12 @@ /// Check alignment of the kernel's virtual memory layout sections. #[kernel_test] fn virt_mem_layout_sections_are_64KiB_aligned() { @@ -1660,7 +1667,7 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/bsp/raspberrypi/memory/mmu.rs assert!(end >= start); } } -@@ -99,17 +153,18 @@ +@@ -104,17 +153,18 @@ /// Ensure the kernel's virtual memory layout is free of overlaps. #[kernel_test] fn virt_mem_layout_has_no_overlaps() { @@ -1736,7 +1743,7 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/bsp/raspberrypi/memory.rs 15_v use core::{cell::UnsafeCell, ops::RangeInclusive}; //-------------------------------------------------------------------------------------------------- -@@ -17,34 +49,39 @@ +@@ -17,47 +49,39 @@ static __bss_start: UnsafeCell; static __bss_end_inclusive: UnsafeCell; static __ro_start: UnsafeCell<()>; @@ -1753,6 +1760,19 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/bsp/raspberrypi/memory.rs 15_v +/// The board's physical memory map. #[rustfmt::skip] pub(super) mod map { +- /// The inclusive end address of the memory map. +- /// +- /// End address + 1 must be power of two. +- /// +- /// # Note +- /// +- /// RPi3 and RPi4 boards can have different amounts of RAM. To make our code lean for +- /// educational purposes, we set the max size of the address space to 4 GiB regardless of board. +- /// This way, we can map the entire range that we need (end of MMIO for RPi4) in one take. +- /// +- /// However, making this trade-off has the downside of making it possible for the CPU to assert a +- /// physical address that is not backed by any DRAM (e.g. accessing an address close to 4 GiB on +- /// an RPi3 that comes with 1 GiB of RAM). This would result in a crash or other kind of error. - pub const END_INCLUSIVE: usize = 0xFFFF_FFFF; + use super::*; @@ -1789,7 +1809,7 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/bsp/raspberrypi/memory.rs 15_v } /// Physical devices. -@@ -52,13 +89,22 @@ +@@ -65,13 +89,22 @@ pub mod mmio { use super::*; @@ -1818,7 +1838,7 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/bsp/raspberrypi/memory.rs 15_v } //-------------------------------------------------------------------------------------------------- -@@ -71,8 +117,8 @@ +@@ -84,8 +117,8 @@ /// /// - Value is provided by the linker script and must be trusted as-is. #[inline(always)] @@ -1829,7 +1849,7 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/bsp/raspberrypi/memory.rs 15_v } /// Size of the Read-Only (RO) range of the kernel binary. -@@ -81,8 +127,42 @@ +@@ -94,8 +127,42 @@ /// /// - Value is provided by the linker script and must be trusted as-is. #[inline(always)] @@ -1874,7 +1894,7 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/bsp/raspberrypi/memory.rs 15_v } //-------------------------------------------------------------------------------------------------- -@@ -91,8 +171,10 @@ +@@ -104,8 +171,10 @@ /// Exclusive end address of the boot core's stack. #[inline(always)] diff --git a/15_virtual_mem_part2_mmio_remap/src/_arch/aarch64/memory/mmu.rs b/15_virtual_mem_part2_mmio_remap/src/_arch/aarch64/memory/mmu.rs index 0e45185e..8abb997e 100644 --- a/15_virtual_mem_part2_mmio_remap/src/_arch/aarch64/memory/mmu.rs +++ b/15_virtual_mem_part2_mmio_remap/src/_arch/aarch64/memory/mmu.rs @@ -47,6 +47,12 @@ register_bitfields! {u64, // A level 3 page descriptor, as per ARMv8-A Architecture Reference Manual Figure D5-17. register_bitfields! {u64, STAGE1_PAGE_DESCRIPTOR [ + /// Unprivileged execute-never. + UXN OFFSET(54) NUMBITS(1) [ + False = 0, + True = 1 + ], + /// Privileged execute-never. PXN OFFSET(53) NUMBITS(1) [ False = 0, @@ -233,13 +239,16 @@ impl convert::From AccessPermissions::ReadWrite => STAGE1_PAGE_DESCRIPTOR::AP::RW_EL1, }; - // Execute Never. + // The execute-never attribute is mapped to PXN in AArch64. desc += if attribute_fields.execute_never { STAGE1_PAGE_DESCRIPTOR::PXN::True } else { STAGE1_PAGE_DESCRIPTOR::PXN::False }; + // Always set unprivileged exectue-never as long as userspace is not implemented yet. + desc += STAGE1_PAGE_DESCRIPTOR::UXN::True; + desc } } @@ -352,6 +361,7 @@ fn configure_translation_control() { TCR_EL1.write( TCR_EL1::TBI0::Ignored + TCR_EL1::IPS.val(ips) + + TCR_EL1::EPD1::DisableTTBR1Walks + TCR_EL1::TG0::KiB_64 + TCR_EL1::SH0::Inner + TCR_EL1::ORGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable