In this lesson, you will learn how to configure the Memory Protection Unit (MPU) to create hardware-enforced boundaries for specific memory regions, preventing unauthorized access or execution.
The Role of the MPU
While a Memory Management Unit (MMU) handles virtual-to-physical address translation and paging, an MPU is a simpler, deterministic hardware block often found in embedded systems (like ARM Cortex-M). It allows you to define a set of "Regions" in your memory map and assign permissions to them, such as Read-Only (RO), Read-Write (RW), or Execute-Never (XN).
If your software attempts to write to a region marked as RO or execute code from a RAM area marked XN, the MPU triggers a hardware fault. This is your primary line of defense against buffer overflows and stack smashing in no_std environments.
Configuring an MPU Region
Configuring the MPU involves interacting directly with the peripheral registers. Because this requires precise bit manipulation, you typically perform these operations within an unsafe block.
For example, to configure a region for a static buffer that should never be executed, you define the base address, size, and access attributes:
unsafe {
// Select the MPU region (e.g., region 0)
core::ptr::write_volatile(MPU_RNR as *mut u32, 0);
// Set the base address (must be aligned to region size)
core::ptr::write_volatile(MPU_RBAR as *mut u32, 0x2000_0000);
// Set attributes: Size 256 bytes, Read-Write, Execute Never (XN)
// The bitmask calculation depends on your specific architecture
core::ptr::write_volatile(MPU_RASR as *mut u32, 0x1003_001B);
// Enable the region
let ctrl = core::ptr::read_volatile(MPU_CTRL as *const u32);
core::ptr::write_volatile(MPU_CTRL as *mut u32, ctrl | 0x1);
}
Safety Implications
By mapping your FLASH as Read-Only and your stack as Execute-Never, you harden your system against code-injection attacks. However, you must be careful: if your MPU configuration is incorrect, your application will trigger a memory management fault immediately upon accessing the restricted area. In Rust, this fault is usually caught by your HardFault handler, which you should configure to log the offending program counter and stack frame to aid in debugging.
Key Takeaway: The MPU provides hardware-level enforcement of memory access policies, allowing you to catch memory safety violations at the instruction level before they compromise system integrity.