Memory Mapping: Improve various aspects

pull/99/head
Andre Richter 4 years ago
parent 9b89f297d7
commit 1d2b5ad022
No known key found for this signature in database
GPG Key ID: 2116C1AB102F615E

@ -140,7 +140,6 @@ struct FixedSizeTranslationTable<const NUM_TABLES: usize> {
lvl2: [TableDescriptor; NUM_TABLES], 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; const NUM_LVL2_TABLES: usize = bsp::memory::mmu::addr_space_size() >> FIVETWELVE_MIB_SHIFT;
type ArchTranslationTable = FixedSizeTranslationTable<NUM_LVL2_TABLES>; type ArchTranslationTable = FixedSizeTranslationTable<NUM_LVL2_TABLES>;
@ -205,7 +204,7 @@ virtual addresses:
- Since we identity map the whole `Device MMIO` region, it is accessible by asserting its physical - 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 base address (`0x3F20_1000` or `0xFA20_1000` depending on which RPi you use) after the `MMU` is
turned on. 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`. accessible through base address `0x1FFF_1000`.
The following block diagram visualizes the underlying translation for the second mapping. 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 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 --- 10_privilege_level/src/_arch/aarch64/memory/mmu.rs
+++ 11_virtual_mem_part1_identity_mapping/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 +// SPDX-License-Identifier: MIT OR Apache-2.0
+// +//
+// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com> +// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
@ -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. +// A level 3 page descriptor, as per ARMv8-A Architecture Reference Manual Figure D5-17.
+register_bitfields! {u64, +register_bitfields! {u64,
+ STAGE1_PAGE_DESCRIPTOR [ + STAGE1_PAGE_DESCRIPTOR [
+ /// Unprivileged execute-never.
+ UXN OFFSET(54) NUMBITS(1) [
+ False = 0,
+ True = 1
+ ],
+
+ /// Privileged execute-never. + /// Privileged execute-never.
+ PXN OFFSET(53) NUMBITS(1) [ + PXN OFFSET(53) NUMBITS(1) [
+ False = 0, + 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], + 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; +const NUM_LVL2_TABLES: usize = bsp::memory::mmu::addr_space_size() >> FIVETWELVE_MIB_SHIFT;
+type ArchTranslationTable = FixedSizeTranslationTable<NUM_LVL2_TABLES>; +type ArchTranslationTable = FixedSizeTranslationTable<NUM_LVL2_TABLES>;
+ +
@ -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, + 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 { + desc += if attribute_fields.execute_never {
+ STAGE1_PAGE_DESCRIPTOR::PXN::True + STAGE1_PAGE_DESCRIPTOR::PXN::True
+ } else { + } else {
+ STAGE1_PAGE_DESCRIPTOR::PXN::False + 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 + 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. +/// Configure various settings of stage 1 of the EL1 translation regime.
+fn configure_translation_control() { +fn configure_translation_control() {
+ let ips = ID_AA64MMFR0_EL1.read(ID_AA64MMFR0_EL1::PARange); + 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.write(
+ TCR_EL1::TBI0::Ignored + TCR_EL1::TBI0::Ignored
+ + TCR_EL1::IPS.val(ips) + + TCR_EL1::IPS.val(ips)
+ + TCR_EL1::EPD1::DisableTTBR1Walks
+ + TCR_EL1::TG0::KiB_64 + + TCR_EL1::TG0::KiB_64
+ + TCR_EL1::SH0::Inner + + TCR_EL1::SH0::Inner
+ + TCR_EL1::ORGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + + TCR_EL1::ORGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable
+ + TCR_EL1::IRGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + + TCR_EL1::IRGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable
+ + TCR_EL1::EPD0::EnableTTBR0Walks + + 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 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 --- 10_privilege_level/src/bsp/raspberrypi/memory/mmu.rs
+++ 11_virtual_mem_part1_identity_mapping/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 +// SPDX-License-Identifier: MIT OR Apache-2.0
+// +//
+// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com> +// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
@ -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. +/// Return the address space size in bytes.
+///
+/// Guarantees size to be a power of two.
+pub const fn addr_space_size() -> usize { +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. +/// 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. /// The board's memory map.
#[rustfmt::skip] #[rustfmt::skip]
pub(super) mod map { 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 END_INCLUSIVE: usize = 0xFFFF_FFFF;
+ +
pub const BOOT_CORE_STACK_END: usize = 0x8_0000; pub const BOOT_CORE_STACK_END: usize = 0x8_0000;
pub const GPIO_OFFSET: usize = 0x0020_0000; pub const GPIO_OFFSET: usize = 0x0020_0000;
@@ -36,6 +42,7 @@ @@ -36,6 +55,7 @@
pub const START: usize = 0x3F00_0000; pub const START: usize = 0x3F00_0000;
pub const GPIO_START: usize = START + GPIO_OFFSET; pub const GPIO_START: usize = START + GPIO_OFFSET;
pub const PL011_UART_START: usize = START + UART_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. /// Physical devices.
@@ -46,10 +53,35 @@ @@ -46,10 +66,35 @@
pub const START: usize = 0xFE00_0000; pub const START: usize = 0xFE00_0000;
pub const GPIO_START: usize = START + GPIO_OFFSET; pub const GPIO_START: usize = START + GPIO_OFFSET;
pub const PL011_UART_START: usize = START + UART_OFFSET; pub const PL011_UART_START: usize = START + UART_OFFSET;

@ -37,6 +37,12 @@ register_bitfields! {u64,
// A level 3 page descriptor, as per ARMv8-A Architecture Reference Manual Figure D5-17. // A level 3 page descriptor, as per ARMv8-A Architecture Reference Manual Figure D5-17.
register_bitfields! {u64, register_bitfields! {u64,
STAGE1_PAGE_DESCRIPTOR [ STAGE1_PAGE_DESCRIPTOR [
/// Unprivileged execute-never.
UXN OFFSET(54) NUMBITS(1) [
False = 0,
True = 1
],
/// Privileged execute-never. /// Privileged execute-never.
PXN OFFSET(53) NUMBITS(1) [ PXN OFFSET(53) NUMBITS(1) [
False = 0, False = 0,
@ -110,7 +116,6 @@ struct FixedSizeTranslationTable<const NUM_TABLES: usize> {
lvl2: [TableDescriptor; NUM_TABLES], 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; const NUM_LVL2_TABLES: usize = bsp::memory::mmu::addr_space_size() >> FIVETWELVE_MIB_SHIFT;
type ArchTranslationTable = FixedSizeTranslationTable<NUM_LVL2_TABLES>; type ArchTranslationTable = FixedSizeTranslationTable<NUM_LVL2_TABLES>;
@ -194,13 +199,16 @@ impl convert::From<AttributeFields>
AccessPermissions::ReadWrite => STAGE1_PAGE_DESCRIPTOR::AP::RW_EL1, 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 { desc += if attribute_fields.execute_never {
STAGE1_PAGE_DESCRIPTOR::PXN::True STAGE1_PAGE_DESCRIPTOR::PXN::True
} else { } else {
STAGE1_PAGE_DESCRIPTOR::PXN::False 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 desc
} }
} }
@ -273,16 +281,18 @@ unsafe fn populate_tt_entries() -> Result<(), &'static str> {
/// Configure various settings of stage 1 of the EL1 translation regime. /// Configure various settings of stage 1 of the EL1 translation regime.
fn configure_translation_control() { fn configure_translation_control() {
let ips = ID_AA64MMFR0_EL1.read(ID_AA64MMFR0_EL1::PARange); 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.write(
TCR_EL1::TBI0::Ignored TCR_EL1::TBI0::Ignored
+ TCR_EL1::IPS.val(ips) + TCR_EL1::IPS.val(ips)
+ TCR_EL1::EPD1::DisableTTBR1Walks
+ TCR_EL1::TG0::KiB_64 + TCR_EL1::TG0::KiB_64
+ TCR_EL1::SH0::Inner + TCR_EL1::SH0::Inner
+ TCR_EL1::ORGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + TCR_EL1::ORGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable
+ TCR_EL1::IRGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + TCR_EL1::IRGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable
+ TCR_EL1::EPD0::EnableTTBR0Walks + TCR_EL1::EPD0::EnableTTBR0Walks
+ TCR_EL1::T0SZ.val(32), // TTBR0 spans 4 GiB total. + TCR_EL1::T0SZ.val(t0sz),
); );
} }

@ -27,6 +27,19 @@ extern "Rust" {
/// The board's memory map. /// The board's memory map.
#[rustfmt::skip] #[rustfmt::skip]
pub(super) mod map { 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 END_INCLUSIVE: usize = 0xFFFF_FFFF;
pub const BOOT_CORE_STACK_END: usize = 0x8_0000; pub const BOOT_CORE_STACK_END: usize = 0x8_0000;

@ -78,8 +78,13 @@ fn mmio_range_inclusive() -> RangeInclusive<usize> {
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
/// Return the address space size in bytes. /// Return the address space size in bytes.
///
/// Guarantees size to be a power of two.
pub const fn addr_space_size() -> usize { 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. /// Return a reference to the virtual memory layout.

@ -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 // For demo purposes, the exception handler will catch the faulting 8 GiB address and allow
// execution to continue. // execution to continue.
info!(""); 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; let mut big_addr: u64 = 8 * 1024 * 1024 * 1024;
unsafe { core::ptr::read_volatile(big_addr as *mut u64) }; 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 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` has been installed. Remember, we only installed up to `4 GiB` of address space in the previous
(RPi3) or `4 GiB` (RPi4) of address space in the previous tutorial. tutorial.
To survive this exception, the respective handler has a special demo case: 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 [MP] ⏩ Pushing 64 KiB ========================================🦀 100% 32 KiB/s Time: 00:00:02
[ML] Loaded! Executing the payload now [ML] Loaded! Executing the payload now
[ 3.090618] Booting on: Raspberry Pi 3 [ 3.091032] Booting on: Raspberry Pi 3
[ 3.091701] MMU online. Special regions: [ 3.092116] MMU online. Special regions:
[ 3.093610] 0x00080000 - 0x0008ffff | 64 KiB | C RO PX | Kernel code and RO data [ 3.094025] 0x00080000 - 0x0008ffff | 64 KiB | C RO PX | Kernel code and RO data
[ 3.097688] 0x3f000000 - 0x4000ffff | 16 MiB | Dev RW PXN | Device MMIO [ 3.098103] 0x3f000000 - 0x4000ffff | 16 MiB | Dev RW PXN | Device MMIO
[ 3.101246] Current privilege level: EL1 [ 3.101661] Current privilege level: EL1
[ 3.103155] Exception handling state: [ 3.103570] Exception handling state:
[ 3.104934] Debug: Masked [ 3.105348] Debug: Masked
[ 3.106496] SError: Masked [ 3.106910] SError: Masked
[ 3.108058] IRQ: Masked [ 3.108472] IRQ: Masked
[ 3.109619] FIQ: Masked [ 3.110034] FIQ: Masked
[ 3.111181] Architectural timer resolution: 52 ns [ 3.111596] Architectural timer resolution: 52 ns
[ 3.113481] Drivers loaded: [ 3.113895] Drivers loaded:
[ 3.114826] 1. BCM GPIO [ 3.115240] 1. BCM GPIO
[ 3.116257] 2. BCM PL011 UART [ 3.116672] 2. BCM PL011 UART
[ 3.117950] Timer test, spinning for 1 second [ 3.118364] Timer test, spinning for 1 second
[ 4.120076] [ 4.120490]
[ 4.120079] Trying to write to address 8 GiB... [ 4.120494] Trying to read from address 8 GiB...
[ 4.122242] ************************************************ [ 4.122700] ************************************************
[ 4.125018] Whoa! We recovered from a synchronous exception! [ 4.125476] Whoa! We recovered from a synchronous exception!
[ 4.127795] ************************************************ [ 4.128253] ************************************************
[ 4.130571] [ 4.131030]
[ 4.131266] Let's try again [ 4.131724] Let's try again
[ 4.132611] Trying to write to address 9 GiB... [ 4.133069] Trying to read from address 9 GiB...
Kernel panic: Kernel panic:
@ -443,7 +443,7 @@ FAR_EL1: 0x0000000240000000
ESR_EL1: 0x96000004 ESR_EL1: 0x96000004
Exception Class (EC) : 0x25 - Data Abort, current EL Exception Class (EC) : 0x25 - Data Abort, current EL
Instr Specific Syndrome (ISS): 0x4 Instr Specific Syndrome (ISS): 0x4
ELR_EL1: 0x0000000000081454 ELR_EL1: 0x0000000000081458
SPSR_EL1: 0x600003c5 SPSR_EL1: 0x600003c5
Flags: Flags:
Negative (N): Not set Negative (N): Not set
@ -458,22 +458,22 @@ SPSR_EL1: 0x600003c5
Illegal Execution State (IL): Not set Illegal Execution State (IL): Not set
General purpose register: General purpose register:
x0 : 0x0000000000000000 x1 : 0x0000000000085726 x0 : 0x0000000000000000 x1 : 0x0000000000085727
x2 : 0x0000000000000026 x3 : 0x0000000000083bf0 x2 : 0x0000000000000027 x3 : 0x0000000000000000
x4 : 0x0000000000000003 x5 : 0xfb4f101900000000 x4 : 0x0000000000000002 x5 : 0x3f27329c00000000
x6 : 0x0000000000000000 x7 : 0x7e9198052b2b0200 x6 : 0x0000000000000000 x7 : 0xdbd1b90800000000
x8 : 0x0000000240000000 x9 : 0x000000003f201000 x8 : 0x0000000240000000 x9 : 0x000000003f201000
x10: 0x0000000000000019 x11: 0x0000000000000000 x10: 0x0000000000000019 x11: 0x00000000000819d0
x12: 0x0000000000000006 x13: 0x0000000000000031 x12: 0x0000000000000000 x13: 0x0000000000000033
x14: 0x000000000007fc2d x15: 0x0000000000000000 x14: 0x000000000007fc2d x15: 0x0000000000000000
x16: 0x0000000000000040 x17: 0xb557f006f276cfb6 x16: 0x0000000000000040 x17: 0xfd7f702255f847d0
x18: 0x0000000000000003 x19: 0x0000000000090008 x18: 0x0000000000000003 x19: 0x0000000000090008
x20: 0x0000000000085510 x21: 0x000000003b9aca00 x20: 0x0000000000085510 x21: 0x000000003b9aca00
x22: 0x00000000000003e8 x23: 0x000000000008160c x22: 0x00000000000003e8 x23: 0x0000000000081610
x24: 0x0000000000082264 x25: 0x00000000000f4240 x24: 0x0000000000082268 x25: 0x00000000000f4240
x26: 0xffffffffc4653600 x27: 0x00000000000855f0 x26: 0xffffffffc4653600 x27: 0x00000000000855f0
x28: 0x0000000000083f84 x29: 0x0000000000086810 x28: 0x0000000000083f80 x29: 0x0000000000086810
lr : 0x0000000000081448 lr : 0x000000000008144c
``` ```
## Diff to previous ## 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 + // For demo purposes, the exception handler will catch the faulting 8 GiB address and allow
+ // execution to continue. + // execution to continue.
+ info!(""); + 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; + let mut big_addr: u64 = 8 * 1024 * 1024 * 1024;
+ unsafe { core::ptr::read_volatile(big_addr as *mut u64) }; + 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"); + info!("Let's try again");
+ +
+ // Now use address 9 GiB. The exception handler won't forgive us this time. + // 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; + big_addr = 9 * 1024 * 1024 * 1024;
+ unsafe { core::ptr::read_volatile(big_addr as *mut u64) }; + unsafe { core::ptr::read_volatile(big_addr as *mut u64) };

@ -37,6 +37,12 @@ register_bitfields! {u64,
// A level 3 page descriptor, as per ARMv8-A Architecture Reference Manual Figure D5-17. // A level 3 page descriptor, as per ARMv8-A Architecture Reference Manual Figure D5-17.
register_bitfields! {u64, register_bitfields! {u64,
STAGE1_PAGE_DESCRIPTOR [ STAGE1_PAGE_DESCRIPTOR [
/// Unprivileged execute-never.
UXN OFFSET(54) NUMBITS(1) [
False = 0,
True = 1
],
/// Privileged execute-never. /// Privileged execute-never.
PXN OFFSET(53) NUMBITS(1) [ PXN OFFSET(53) NUMBITS(1) [
False = 0, False = 0,
@ -110,7 +116,6 @@ struct FixedSizeTranslationTable<const NUM_TABLES: usize> {
lvl2: [TableDescriptor; NUM_TABLES], 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; const NUM_LVL2_TABLES: usize = bsp::memory::mmu::addr_space_size() >> FIVETWELVE_MIB_SHIFT;
type ArchTranslationTable = FixedSizeTranslationTable<NUM_LVL2_TABLES>; type ArchTranslationTable = FixedSizeTranslationTable<NUM_LVL2_TABLES>;
@ -194,13 +199,16 @@ impl convert::From<AttributeFields>
AccessPermissions::ReadWrite => STAGE1_PAGE_DESCRIPTOR::AP::RW_EL1, 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 { desc += if attribute_fields.execute_never {
STAGE1_PAGE_DESCRIPTOR::PXN::True STAGE1_PAGE_DESCRIPTOR::PXN::True
} else { } else {
STAGE1_PAGE_DESCRIPTOR::PXN::False 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 desc
} }
} }
@ -273,16 +281,18 @@ unsafe fn populate_tt_entries() -> Result<(), &'static str> {
/// Configure various settings of stage 1 of the EL1 translation regime. /// Configure various settings of stage 1 of the EL1 translation regime.
fn configure_translation_control() { fn configure_translation_control() {
let ips = ID_AA64MMFR0_EL1.read(ID_AA64MMFR0_EL1::PARange); 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.write(
TCR_EL1::TBI0::Ignored TCR_EL1::TBI0::Ignored
+ TCR_EL1::IPS.val(ips) + TCR_EL1::IPS.val(ips)
+ TCR_EL1::EPD1::DisableTTBR1Walks
+ TCR_EL1::TG0::KiB_64 + TCR_EL1::TG0::KiB_64
+ TCR_EL1::SH0::Inner + TCR_EL1::SH0::Inner
+ TCR_EL1::ORGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + TCR_EL1::ORGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable
+ TCR_EL1::IRGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + TCR_EL1::IRGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable
+ TCR_EL1::EPD0::EnableTTBR0Walks + TCR_EL1::EPD0::EnableTTBR0Walks
+ TCR_EL1::T0SZ.val(32), // TTBR0 spans 4 GiB total. + TCR_EL1::T0SZ.val(t0sz),
); );
} }

@ -27,6 +27,19 @@ extern "Rust" {
/// The board's memory map. /// The board's memory map.
#[rustfmt::skip] #[rustfmt::skip]
pub(super) mod map { 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 END_INCLUSIVE: usize = 0xFFFF_FFFF;
pub const BOOT_CORE_STACK_END: usize = 0x8_0000; pub const BOOT_CORE_STACK_END: usize = 0x8_0000;

@ -63,8 +63,13 @@ fn mmio_range_inclusive() -> RangeInclusive<usize> {
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
/// Return the address space size in bytes. /// Return the address space size in bytes.
///
/// Guarantees size to be a power of two.
pub const fn addr_space_size() -> usize { 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. /// Return a reference to the virtual memory layout.

@ -204,7 +204,7 @@ fn kernel_main() -> ! {
// For demo purposes, the exception handler will catch the faulting 8 GiB address and allow // For demo purposes, the exception handler will catch the faulting 8 GiB address and allow
// execution to continue. // execution to continue.
info!(""); 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; let mut big_addr: u64 = 8 * 1024 * 1024 * 1024;
unsafe { core::ptr::read_volatile(big_addr as *mut u64) }; unsafe { core::ptr::read_volatile(big_addr as *mut u64) };
@ -215,7 +215,7 @@ fn kernel_main() -> ! {
info!("Let's try again"); info!("Let's try again");
// Now use address 9 GiB. The exception handler won't forgive us this time. // 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; big_addr = 9 * 1024 * 1024 * 1024;
unsafe { core::ptr::read_volatile(big_addr as *mut u64) }; unsafe { core::ptr::read_volatile(big_addr as *mut u64) };

@ -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 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 --- 12_exceptions_part1_groundwork/src/_arch/aarch64/memory/mmu.rs
+++ 13_integrated_testing/src/_arch/aarch64/memory/mmu.rs +++ 13_integrated_testing/src/_arch/aarch64/memory/mmu.rs
@@ -331,3 +331,40 @@ @@ -341,3 +341,40 @@
Ok(()) 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 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 --- 12_exceptions_part1_groundwork/src/bsp/raspberrypi/memory/mmu.rs
+++ 13_integrated_testing/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 }> { pub fn virt_mem_layout() -> &'static KernelVirtualLayout<{ NUM_MEM_RANGES }> {
&LAYOUT &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 - // For demo purposes, the exception handler will catch the faulting 8 GiB address and allow
- // execution to continue. - // execution to continue.
- info!(""); - 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; - let mut big_addr: u64 = 8 * 1024 * 1024 * 1024;
- unsafe { core::ptr::read_volatile(big_addr as *mut u64) }; - 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"); - info!("Let's try again");
- -
- // Now use address 9 GiB. The exception handler won't forgive us this time. - // 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; - big_addr = 9 * 1024 * 1024 * 1024;
- unsafe { core::ptr::read_volatile(big_addr as *mut u64) }; - unsafe { core::ptr::read_volatile(big_addr as *mut u64) };
- -

@ -37,6 +37,12 @@ register_bitfields! {u64,
// A level 3 page descriptor, as per ARMv8-A Architecture Reference Manual Figure D5-17. // A level 3 page descriptor, as per ARMv8-A Architecture Reference Manual Figure D5-17.
register_bitfields! {u64, register_bitfields! {u64,
STAGE1_PAGE_DESCRIPTOR [ STAGE1_PAGE_DESCRIPTOR [
/// Unprivileged execute-never.
UXN OFFSET(54) NUMBITS(1) [
False = 0,
True = 1
],
/// Privileged execute-never. /// Privileged execute-never.
PXN OFFSET(53) NUMBITS(1) [ PXN OFFSET(53) NUMBITS(1) [
False = 0, False = 0,
@ -110,7 +116,6 @@ struct FixedSizeTranslationTable<const NUM_TABLES: usize> {
lvl2: [TableDescriptor; NUM_TABLES], 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; const NUM_LVL2_TABLES: usize = bsp::memory::mmu::addr_space_size() >> FIVETWELVE_MIB_SHIFT;
type ArchTranslationTable = FixedSizeTranslationTable<NUM_LVL2_TABLES>; type ArchTranslationTable = FixedSizeTranslationTable<NUM_LVL2_TABLES>;
@ -194,13 +199,16 @@ impl convert::From<AttributeFields>
AccessPermissions::ReadWrite => STAGE1_PAGE_DESCRIPTOR::AP::RW_EL1, 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 { desc += if attribute_fields.execute_never {
STAGE1_PAGE_DESCRIPTOR::PXN::True STAGE1_PAGE_DESCRIPTOR::PXN::True
} else { } else {
STAGE1_PAGE_DESCRIPTOR::PXN::False 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 desc
} }
} }
@ -273,16 +281,18 @@ unsafe fn populate_tt_entries() -> Result<(), &'static str> {
/// Configure various settings of stage 1 of the EL1 translation regime. /// Configure various settings of stage 1 of the EL1 translation regime.
fn configure_translation_control() { fn configure_translation_control() {
let ips = ID_AA64MMFR0_EL1.read(ID_AA64MMFR0_EL1::PARange); 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.write(
TCR_EL1::TBI0::Ignored TCR_EL1::TBI0::Ignored
+ TCR_EL1::IPS.val(ips) + TCR_EL1::IPS.val(ips)
+ TCR_EL1::EPD1::DisableTTBR1Walks
+ TCR_EL1::TG0::KiB_64 + TCR_EL1::TG0::KiB_64
+ TCR_EL1::SH0::Inner + TCR_EL1::SH0::Inner
+ TCR_EL1::ORGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + TCR_EL1::ORGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable
+ TCR_EL1::IRGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + TCR_EL1::IRGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable
+ TCR_EL1::EPD0::EnableTTBR0Walks + TCR_EL1::EPD0::EnableTTBR0Walks
+ TCR_EL1::T0SZ.val(32), // TTBR0 spans 4 GiB total. + TCR_EL1::T0SZ.val(t0sz),
); );
} }

@ -27,6 +27,19 @@ extern "Rust" {
/// The board's memory map. /// The board's memory map.
#[rustfmt::skip] #[rustfmt::skip]
pub(super) mod map { 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 END_INCLUSIVE: usize = 0xFFFF_FFFF;
pub const BOOT_CORE_STACK_END: usize = 0x8_0000; pub const BOOT_CORE_STACK_END: usize = 0x8_0000;

@ -63,8 +63,13 @@ fn mmio_range_inclusive() -> RangeInclusive<usize> {
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
/// Return the address space size in bytes. /// Return the address space size in bytes.
///
/// Guarantees size to be a power of two.
pub const fn addr_space_size() -> usize { 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. /// Return a reference to the virtual memory layout.

@ -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 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 --- 13_integrated_testing/src/bsp/raspberrypi/memory.rs
+++ 14_exceptions_part2_peripheral_IRQs/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 { pub mod mmio {
use super::*; use super::*;
@ -2162,7 +2162,7 @@ diff -uNr 13_integrated_testing/src/bsp/raspberrypi/memory.rs 14_exceptions_part
} }
/// Physical devices. /// Physical devices.
@@ -53,6 +55,8 @@ @@ -66,6 +68,8 @@
pub const START: usize = 0xFE00_0000; pub const START: usize = 0xFE00_0000;
pub const GPIO_START: usize = START + GPIO_OFFSET; pub const GPIO_START: usize = START + GPIO_OFFSET;
pub const PL011_UART_START: usize = START + UART_OFFSET; pub const PL011_UART_START: usize = START + UART_OFFSET;

@ -37,6 +37,12 @@ register_bitfields! {u64,
// A level 3 page descriptor, as per ARMv8-A Architecture Reference Manual Figure D5-17. // A level 3 page descriptor, as per ARMv8-A Architecture Reference Manual Figure D5-17.
register_bitfields! {u64, register_bitfields! {u64,
STAGE1_PAGE_DESCRIPTOR [ STAGE1_PAGE_DESCRIPTOR [
/// Unprivileged execute-never.
UXN OFFSET(54) NUMBITS(1) [
False = 0,
True = 1
],
/// Privileged execute-never. /// Privileged execute-never.
PXN OFFSET(53) NUMBITS(1) [ PXN OFFSET(53) NUMBITS(1) [
False = 0, False = 0,
@ -110,7 +116,6 @@ struct FixedSizeTranslationTable<const NUM_TABLES: usize> {
lvl2: [TableDescriptor; NUM_TABLES], 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; const NUM_LVL2_TABLES: usize = bsp::memory::mmu::addr_space_size() >> FIVETWELVE_MIB_SHIFT;
type ArchTranslationTable = FixedSizeTranslationTable<NUM_LVL2_TABLES>; type ArchTranslationTable = FixedSizeTranslationTable<NUM_LVL2_TABLES>;
@ -194,13 +199,16 @@ impl convert::From<AttributeFields>
AccessPermissions::ReadWrite => STAGE1_PAGE_DESCRIPTOR::AP::RW_EL1, 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 { desc += if attribute_fields.execute_never {
STAGE1_PAGE_DESCRIPTOR::PXN::True STAGE1_PAGE_DESCRIPTOR::PXN::True
} else { } else {
STAGE1_PAGE_DESCRIPTOR::PXN::False 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 desc
} }
} }
@ -273,16 +281,18 @@ unsafe fn populate_tt_entries() -> Result<(), &'static str> {
/// Configure various settings of stage 1 of the EL1 translation regime. /// Configure various settings of stage 1 of the EL1 translation regime.
fn configure_translation_control() { fn configure_translation_control() {
let ips = ID_AA64MMFR0_EL1.read(ID_AA64MMFR0_EL1::PARange); 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.write(
TCR_EL1::TBI0::Ignored TCR_EL1::TBI0::Ignored
+ TCR_EL1::IPS.val(ips) + TCR_EL1::IPS.val(ips)
+ TCR_EL1::EPD1::DisableTTBR1Walks
+ TCR_EL1::TG0::KiB_64 + TCR_EL1::TG0::KiB_64
+ TCR_EL1::SH0::Inner + TCR_EL1::SH0::Inner
+ TCR_EL1::ORGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + TCR_EL1::ORGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable
+ TCR_EL1::IRGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + TCR_EL1::IRGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable
+ TCR_EL1::EPD0::EnableTTBR0Walks + TCR_EL1::EPD0::EnableTTBR0Walks
+ TCR_EL1::T0SZ.val(32), // TTBR0 spans 4 GiB total. + TCR_EL1::T0SZ.val(t0sz),
); );
} }

@ -27,6 +27,19 @@ extern "Rust" {
/// The board's memory map. /// The board's memory map.
#[rustfmt::skip] #[rustfmt::skip]
pub(super) mod map { 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 END_INCLUSIVE: usize = 0xFFFF_FFFF;
pub const BOOT_CORE_STACK_END: usize = 0x8_0000; pub const BOOT_CORE_STACK_END: usize = 0x8_0000;

@ -63,8 +63,13 @@ fn mmio_range_inclusive() -> RangeInclusive<usize> {
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
/// Return the address space size in bytes. /// Return the address space size in bytes.
///
/// Guarantees size to be a power of two.
pub const fn addr_space_size() -> usize { 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. /// Return a reference to the virtual memory layout.

@ -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. // A table descriptor, as per ARMv8-A Architecture Reference Manual Figure D5-15.
register_bitfields! {u64, 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. /// A table descriptor for 64 KiB aperture.
/// ///
/// The output points to the next table. /// The output points to the next table.
@@ -98,36 +105,65 @@ @@ -104,35 +111,65 @@
#[repr(transparent)] #[repr(transparent)]
struct PageDescriptor(u64); 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], 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; -const NUM_LVL2_TABLES: usize = bsp::memory::mmu::addr_space_size() >> FIVETWELVE_MIB_SHIFT;
-type ArchTranslationTable = FixedSizeTranslationTable<NUM_LVL2_TABLES>; -type ArchTranslationTable = FixedSizeTranslationTable<NUM_LVL2_TABLES>;
+ /// Index of the next free MMIO page. + /// 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 // Global instances
@@ -138,7 +174,8 @@ @@ -143,7 +180,8 @@
/// # Safety /// # Safety
/// ///
/// - Supposed to land in `.bss`. Therefore, ensure that all initial member values boil down to "0". /// - 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; static MMU: MemoryManagementUnit = MemoryManagementUnit;
@@ -146,13 +183,15 @@ @@ -151,13 +189,15 @@
// Private Code // 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 { fn from(next_lvl_table_addr: usize) -> Self {
let val = InMemoryRegister::<u64, STAGE1_TABLE_DESCRIPTOR::Register>::new(0); let val = InMemoryRegister::<u64, STAGE1_TABLE_DESCRIPTOR::Register>::new(0);
@ -490,7 +489,7 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/_arch/aarch64/memory/mmu.rs 15
val.write( val.write(
STAGE1_TABLE_DESCRIPTOR::VALID::True STAGE1_TABLE_DESCRIPTOR::VALID::True
+ STAGE1_TABLE_DESCRIPTOR::TYPE::Table + STAGE1_TABLE_DESCRIPTOR::TYPE::Table
@@ -207,23 +246,33 @@ @@ -215,23 +255,33 @@
impl PageDescriptor { impl PageDescriptor {
/// Create an instance. /// Create an instance.
@ -527,7 +526,7 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/_arch/aarch64/memory/mmu.rs 15
/// Create an instance. /// Create an instance.
pub const fn new() -> Self { pub const fn new() -> Self {
assert!(NUM_TABLES > 0); assert!(NUM_TABLES > 0);
@@ -231,7 +280,55 @@ @@ -239,7 +289,55 @@
Self { Self {
lvl3: [[PageDescriptor(0); 8192]; NUM_TABLES], lvl3: [[PageDescriptor(0); 8192]; NUM_TABLES],
lvl2: [TableDescriptor(0); 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. /// Configure various settings of stage 1 of the EL1 translation regime.
fn configure_translation_control() { fn configure_translation_control() {
let ips = ID_AA64MMFR0_EL1.read(ID_AA64MMFR0_EL1::PARange); 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::ORGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable
+ TCR_EL1::IRGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + TCR_EL1::IRGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable
+ TCR_EL1::EPD0::EnableTTBR0Walks + TCR_EL1::EPD0::EnableTTBR0Walks
- + TCR_EL1::T0SZ.val(32), // TTBR0 spans 4 GiB total. - + TCR_EL1::T0SZ.val(t0sz),
+ + TCR_EL1::T0SZ.val(T0SZ), + + TCR_EL1::T0SZ.val(T0SZ),
); );
} }
@@ -290,17 +365,126 @@ @@ -300,17 +375,126 @@
// Public Code // 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. // Fail early if translation granule is not supported. Both RPis support it, though.
if !ID_AA64MMFR0_EL1.matches_all(ID_AA64MMFR0_EL1::TGran64::Supported) { if !ID_AA64MMFR0_EL1.matches_all(ID_AA64MMFR0_EL1::TGran64::Supported) {
return Err("Translation granule not supported in HW"); return Err("Translation granule not supported in HW");
@@ -309,11 +493,8 @@ @@ -319,11 +503,8 @@
// Prepare the memory attribute indirection register. // Prepare the memory attribute indirection register.
set_up_mair(); set_up_mair();
@ -764,7 +767,7 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/_arch/aarch64/memory/mmu.rs 15
configure_translation_control(); configure_translation_control();
@@ -337,6 +518,9 @@ @@ -347,6 +528,9 @@
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
#[cfg(test)] #[cfg(test)]
@ -774,7 +777,7 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/_arch/aarch64/memory/mmu.rs 15
mod tests { mod tests {
use super::*; use super::*;
use test_macros::kernel_test; use test_macros::kernel_test;
@@ -363,7 +547,7 @@ @@ -373,7 +557,7 @@
#[kernel_test] #[kernel_test]
fn kernel_tables_in_bss() { fn kernel_tables_in_bss() {
let bss_range = bsp::memory::bss_range_inclusive(); 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 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 --- 14_exceptions_part2_peripheral_IRQs/src/bsp/raspberrypi/memory/mmu.rs
+++ 15_virtual_mem_part2_mmio_remap/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. //! 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. -/// 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. +/// Pointer to the last page of the physical address space.
+pub fn phys_addr_space_end_page() -> *const Page<Physical> { +pub fn phys_addr_space_end_page() -> *const Page<Physical> {
+ common::align_down( + common::align_down(
+ super::phys_addr_space_end().into_usize(), + super::phys_addr_space_end().into_usize(),
+ KernelGranule::SIZE, + KernelGranule::SIZE,
+ ) as *const Page<_> + ) 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. +/// Map the kernel binary.
+/// +///
+/// # Safety +/// # 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. +/// - Any miscalculation or attribute error will likely be fatal. Needs careful manual checking.
+pub unsafe fn kernel_map_binary() -> Result<(), &'static str> { +pub unsafe fn kernel_map_binary() -> Result<(), &'static str> {
+ kernel_mmu::kernel_map_pages_at( + 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, + execute_never: true,
+ }, + },
+ )?; + )?;
+
- size
-}
+ kernel_mmu::kernel_map_pages_at( + kernel_mmu::kernel_map_pages_at(
+ "Kernel code and RO data", + "Kernel code and RO data",
+ &phys_ro_page_desc(), + &phys_ro_page_desc(),
@ -1624,7 +1628,10 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/bsp/raspberrypi/memory/mmu.rs
+ execute_never: false, + 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_mmu::kernel_map_pages_at(
+ "Kernel data and bss", + "Kernel data and bss",
+ &phys_data_page_desc(), + &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. /// Check alignment of the kernel's virtual memory layout sections.
#[kernel_test] #[kernel_test]
fn virt_mem_layout_sections_are_64KiB_aligned() { 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); assert!(end >= start);
} }
} }
@@ -99,17 +153,18 @@ @@ -104,17 +153,18 @@
/// Ensure the kernel's virtual memory layout is free of overlaps. /// Ensure the kernel's virtual memory layout is free of overlaps.
#[kernel_test] #[kernel_test]
fn virt_mem_layout_has_no_overlaps() { 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}; use core::{cell::UnsafeCell, ops::RangeInclusive};
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@@ -17,34 +49,39 @@ @@ -17,47 +49,39 @@
static __bss_start: UnsafeCell<u64>; static __bss_start: UnsafeCell<u64>;
static __bss_end_inclusive: UnsafeCell<u64>; static __bss_end_inclusive: UnsafeCell<u64>;
static __ro_start: 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. +/// The board's physical memory map.
#[rustfmt::skip] #[rustfmt::skip]
pub(super) mod map { 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 END_INCLUSIVE: usize = 0xFFFF_FFFF;
+ use super::*; + use super::*;
@ -1789,7 +1809,7 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/src/bsp/raspberrypi/memory.rs 15_v
} }
/// Physical devices. /// Physical devices.
@@ -52,13 +89,22 @@ @@ -65,13 +89,22 @@
pub mod mmio { pub mod mmio {
use super::*; 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. /// - Value is provided by the linker script and must be trusted as-is.
#[inline(always)] #[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. /// 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. /// - Value is provided by the linker script and must be trusted as-is.
#[inline(always)] #[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. /// Exclusive end address of the boot core's stack.
#[inline(always)] #[inline(always)]

@ -47,6 +47,12 @@ register_bitfields! {u64,
// A level 3 page descriptor, as per ARMv8-A Architecture Reference Manual Figure D5-17. // A level 3 page descriptor, as per ARMv8-A Architecture Reference Manual Figure D5-17.
register_bitfields! {u64, register_bitfields! {u64,
STAGE1_PAGE_DESCRIPTOR [ STAGE1_PAGE_DESCRIPTOR [
/// Unprivileged execute-never.
UXN OFFSET(54) NUMBITS(1) [
False = 0,
True = 1
],
/// Privileged execute-never. /// Privileged execute-never.
PXN OFFSET(53) NUMBITS(1) [ PXN OFFSET(53) NUMBITS(1) [
False = 0, False = 0,
@ -233,13 +239,16 @@ impl convert::From<AttributeFields>
AccessPermissions::ReadWrite => STAGE1_PAGE_DESCRIPTOR::AP::RW_EL1, 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 { desc += if attribute_fields.execute_never {
STAGE1_PAGE_DESCRIPTOR::PXN::True STAGE1_PAGE_DESCRIPTOR::PXN::True
} else { } else {
STAGE1_PAGE_DESCRIPTOR::PXN::False 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 desc
} }
} }
@ -352,6 +361,7 @@ fn configure_translation_control() {
TCR_EL1.write( TCR_EL1.write(
TCR_EL1::TBI0::Ignored TCR_EL1::TBI0::Ignored
+ TCR_EL1::IPS.val(ips) + TCR_EL1::IPS.val(ips)
+ TCR_EL1::EPD1::DisableTTBR1Walks
+ TCR_EL1::TG0::KiB_64 + TCR_EL1::TG0::KiB_64
+ TCR_EL1::SH0::Inner + TCR_EL1::SH0::Inner
+ TCR_EL1::ORGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + TCR_EL1::ORGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable

Loading…
Cancel
Save