The Memory Protection Unit (MPU) is a hardware component present in many ARM Cortex-M processors, including the Cortex-M4. It's designed to enhance the reliability and security of embedded systems by providing memory access control. An MPU allows you to divide the memory map into distinct regions and assign specific access permissions to each region. This capability is crucial for preventing unintended memory corruption and enforcing privilege separation.
The MPU operates by defining memory regions with associated attributes. These attributes dictate what type of access is permitted (read, write, execute) and the privilege level required to perform the access (privileged or unprivileged). When the CPU attempts to access a memory location, the MPU checks if the access is allowed based on the region's attributes. If the access violates the defined permissions, the MPU triggers a fault, such as a Memory Management Fault (MMF).
The MPU is configured through a set of registers that define the memory regions and their attributes. Key registers include:
Yes, it is possible to configure the MPU on a Cortex-M4 to prevent instructions executed from a specific region of ROM from writing to a specific region of RAM. This involves setting up MPU regions with appropriate access permissions.
Let's say you have a section of ROM at address 0x08000000 and you want to prevent any instructions executed from this ROM from writing to a section of RAM at address 0x20000000.
With this configuration, any attempt by code executing from the ROM region (0x08000000) to write to the RAM region (0x20000000) will trigger an MPU fault, preventing the write operation and potentially halting the system or triggering an error handler.
Configuring the MPU correctly is crucial for ensuring the desired level of memory protection without hindering legitimate memory accesses. Here are some important considerations:
If memory regions overlap, the MPU uses a priority scheme to determine which region's attributes apply. Typically, the region with the highest number takes precedence. This allows you to create exceptions to broader memory protection rules.
If the same memory range is covered in multiple MPU regions, the configuration in the highest REGION number will be applied. For example, if REGION 0 turns off access to the first 32kB of RAM for Unprivileged mode but REGION 1 makes the same RAM region read-only, REGION 1 settings will take precedence.
The Cortex-M4 supports privileged and unprivileged execution modes. Privileged code (e.g., the operating system kernel) has unrestricted access to all memory regions, while unprivileged code (e.g., user applications) is subject to the MPU's access control rules. You can configure MPU regions to allow or deny access based on the current privilege level.
Some MPU implementations allow you to divide a region into subregions, each with its own access permissions. This provides finer-grained control over memory access. The number of definable regions varies based on implementation. The MPU in Arm® Cortex®-M4/M7 and M0+ CPU in TRAVEOTM T2G supports up to 8 programmable regions and each region defined can further be divided into 8 equal subregions. This increases the granularity of the implemented protection.
When the MPU detects a memory access violation, it triggers a fault. You should implement a fault handler to catch these exceptions and take appropriate action, such as logging the error, terminating the offending task, or resetting the system. Examples of MPU faults include:
Implementing an MPU provides several benefits for embedded systems:
The MPU helps protect against malicious attacks by preventing unauthorized code from accessing critical memory regions. By preventing instructions executed from ROM from writing to specific RAM areas, the MPU adds a layer of defense against code injection attacks or unintended data corruption.
By preventing accidental memory overwrites, the MPU can improve the overall reliability of the system. This is particularly important in safety-critical applications where memory corruption can have serious consequences.
MPU faults can help identify memory access errors during development, making it easier to debug and fix software bugs. An MPU exception can be a great hint when you are debugging memory access problems.
The MPU is useful in a variety of applications:
This table demonstrates how to configure the MPU to prevent ROM instructions from modifying RAM. It provides a simplified example and should be adapted to fit specific system requirements.
| Region Number | Base Address | Size | Access Permissions | Description |
|---|---|---|---|---|
| 0 | 0x08000000 (ROM Start) | 32KB | Execute Only, No Write | ROM Region - Instructions are executed from here. |
| 1 | 0x20000000 (RAM Start) | 64KB | Read/Write, Privileged Access Only | RAM Region - Data is stored here, write access restricted. |
| 2 | 0x40000000 (Peripheral Memory) | 16KB | Read/Write | Peripheral Memory - Accessible for read and write. |
Examining development boards that utilize the Cortex-M4 architecture can provide a clearer understanding of how MPUs are implemented in hardware. These boards often come with pre-configured memory regions and example code that demonstrate MPU functionality.
The images above showcase various STM32F407 development boards, which are popular choices for embedded systems development using the ARM Cortex-M4 processor. These boards typically include features like on-board memory, peripherals, and debugging interfaces, making them ideal for experimenting with MPU configurations. By studying the memory maps and example code provided with these boards, developers can gain practical insights into how to effectively use the MPU to protect memory regions and enhance system security.
The video "STM32 MPU Config || #1. Need for the Memory Protection Unit" provides a comprehensive guide on configuring the MPU on STM32 microcontrollers, which utilize the ARM Cortex-M architecture. This video is particularly relevant as it delves into the practical aspects of setting up the MPU, including defining memory regions and configuring access permissions. The presenter effectively illustrates the importance of using the MPU to protect memory and enhance the security of embedded systems. By watching this video, developers can gain a deeper understanding of the MPU configuration process and learn how to implement it effectively in their own projects.
This example demonstrates how to configure MPU registers in C for a Cortex-M4 processor. Note that the specific register addresses and bit definitions may vary depending on the microcontroller you are using. Always refer to the manufacturer's documentation for accurate details.
// Define MPU register addresses
#define MPU_CTRL (*((volatile unsigned long*) 0xE000ED94)) // MPU Control Register
#define MPU_RNR (*((volatile unsigned long*) 0xE000ED98)) // MPU Region Number Register
#define MPU_RBAR (*((volatile unsigned long*) 0xE000ED9C)) // MPU Region Base Address Register
#define MPU_RASR (*((volatile unsigned long*) 0xE000EDA0)) // MPU Region Attribute and Size Register
// Define bit masks for MPU_RASR
#define MPU_RASR_ENABLE (1 << 0) // Region enable bit
#define MPU_RASR_SIZE_8KB (5 << 1) // 8KB region size
#define MPU_RASR_AP_PRIV_RW (3 << 24) // Privileged read/write access
#define MPU_RASR_XN (1 << 28) // Execute Never
void configure_mpu() {
// Disable MPU
MPU_CTRL = 0;
// Configure Region 0 (ROM)
MPU_RNR = 0;
MPU_RBAR = 0x08000000; // ROM base address
MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_SIZE_8KB | MPU_RASR_XN; // Enable, 8KB, Execute Never
// Configure Region 1 (RAM)
MPU_RNR = 1;
MPU_RBAR = 0x20000000; // RAM base address
MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_SIZE_8KB | MPU_RASR_AP_PRIV_RW; // Enable, 8KB, Privileged read/write
// Enable MPU
MPU_CTRL = 1;
}
int main() {
configure_mpu();
// Your code here
return 0;
}