Memory Mapping: Improve various aspects

pull/99/head
Andre Richter 3 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],
}
/// 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<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
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 <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.
+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<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,
+ };
+
+ // 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 <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.
+///
+/// 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;

@ -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<const NUM_TABLES: usize> {
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<NUM_LVL2_TABLES>;
@ -194,13 +199,16 @@ impl convert::From<AttributeFields>
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),
);
}

@ -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;

@ -78,8 +78,13 @@ fn mmio_range_inclusive() -> RangeInclusive<usize> {
//--------------------------------------------------------------------------------------------------
/// 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.

@ -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) };

@ -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<const NUM_TABLES: usize> {
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<NUM_LVL2_TABLES>;
@ -194,13 +199,16 @@ impl convert::From<AttributeFields>
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),
);
}

@ -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;

@ -63,8 +63,13 @@ fn mmio_range_inclusive() -> RangeInclusive<usize> {
//--------------------------------------------------------------------------------------------------
/// 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.

@ -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) };

@ -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) };
-

@ -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<const NUM_TABLES: usize> {
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<NUM_LVL2_TABLES>;
@ -194,13 +199,16 @@ impl convert::From<AttributeFields>
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),
);
}

@ -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;

@ -63,8 +63,13 @@ fn mmio_range_inclusive() -> RangeInclusive<usize> {
//--------------------------------------------------------------------------------------------------
/// 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.

@ -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;

@ -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<const NUM_TABLES: usize> {
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<NUM_LVL2_TABLES>;
@ -194,13 +199,16 @@ impl convert::From<AttributeFields>
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),
);
}

@ -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;

@ -63,8 +63,13 @@ fn mmio_range_inclusive() -> RangeInclusive<usize> {
//--------------------------------------------------------------------------------------------------
/// 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.

@ -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<NUM_LVL2_TABLES>;
+ /// 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::<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(
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<Physical> {
+ 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<u64>;
static __bss_end_inclusive: UnsafeCell<u64>;
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)]

@ -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<AttributeFields>
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

Loading…
Cancel
Save