MPU Configuration and Memory Protection Failure

The issue at hand revolves around the Memory Protection Unit (MPU) on an ARM Cortex-M processor failing to trigger a MemManage fault or HardFault when a protected memory region is accessed. The MPU is a critical component in embedded systems for enforcing memory access rules, ensuring that unauthorized access to specific memory regions is prevented. In this case, the MPU is configured to protect a 64-byte memory region starting at address 0x20000000. Despite the configuration, writing to this protected region does not result in the expected fault, raising concerns about the correctness of the MPU setup or the underlying hardware-software interaction.

The provided code snippet demonstrates the problem. The MPU is configured using the following registers:

  • MPU_RNR (MPU Region Number Register) to select the region.
  • MPU_RBAR (MPU Region Base Address Register) to set the base address of the protected region.
  • MPU_RASR (MPU Region Attribute and Size Register) to define the size, access permissions, and other attributes of the region.
  • MPU_CTRL (MPU Control Register) to enable the MPU.
  • SCB_SHCSR (System Handler Control and State Register) to enable the MemManage fault handler.

The code attempts to write to the protected memory region using inline assembly in the main function. However, the write operation succeeds without triggering any fault, indicating that the MPU is not enforcing the configured memory protection rules.

Incorrect Register Configuration and Assembly Code Typo

The failure of the MPU to trigger a fault can be attributed to two primary issues: an incorrect configuration of the MPU registers and a typo in the assembly code used to perform the memory write operation.

MPU Register Configuration Issues

The MPU configuration in the provided code is incomplete. Specifically, the MPU_RBAR register is missing the VALID bit, which is crucial for enabling the region. The VALID bit (bit 4 of MPU_RBAR) must be set to indicate that the base address and region number are valid. Without this bit set, the MPU region is effectively disabled, and the memory protection rules are not enforced.

The MPU_RASR register is configured with the value 0x1608FF0B, which corresponds to the following attributes:

  • Size: 64 bytes (encoded as 0x08 in the size field).
  • Access permissions: Read-only for both privileged and unprivileged access (encoded as 0x03 in the AP field).
  • Execute Never (XN) bit: Set to 0, allowing execution from this region.
  • Sub-region disable bits: All sub-regions are enabled (0xFF).
  • Memory attributes: b=s=c=0, indicating that the region is not cacheable, not shareable, and not bufferable.

While the MPU_RASR configuration appears correct, the absence of the VALID bit in MPU_RBAR renders the entire MPU configuration ineffective.

Assembly Code Typo

The assembly code in the main function contains a critical typo that prevents the intended memory access from being properly tested. The code is as follows:

LDR R0, =0x20000000
MOV R1, 0x77777777
STR R1, [R5,#0]

The issue lies in the last instruction, STR R1, [R5,#0]. The intention is to store the value in R1 (which is 0x77777777) into the memory location pointed to by R0 (which is 0x20000000). However, the code mistakenly uses R5 instead of R0 as the base register for the store operation. Since R5 is not initialized in this context, its value is undefined, leading to an unpredictable memory access. This typo not only prevents the intended test of the MPU protection but also introduces undefined behavior that could result in a HardFault for reasons unrelated to the MPU configuration.

Correcting MPU Configuration and Assembly Code

To resolve the issue and ensure that the MPU correctly triggers a MemManage fault when the protected memory region is accessed, the following steps must be taken:

Setting the VALID Bit in MPU_RBAR

The MPU_RBAR register must be configured with the VALID bit set to indicate that the region is active. The correct configuration for MPU_RBAR is as follows:

MPU_RBAR = 0x20000000 | (0x00 << 4) | 0x10;

Here, 0x20000000 is the base address, 0x00 is the region number (region 0), and 0x10 sets the VALID bit. This ensures that the MPU region is properly enabled and that the base address and region number are recognized as valid.

Fixing the Assembly Code Typo

The assembly code in the main function must be corrected to use R0 as the base register for the store operation. The corrected code is as follows:

LDR R0, =0x20000000
MOV R1, 0x77777777
STR R1, [R0,#0]

This ensures that the value in R1 is stored at the memory location 0x20000000, which is the intended protected region. With this correction, the MPU should trigger a MemManage fault when the store operation is attempted.

Verifying the MPU Configuration

After making the above corrections, it is essential to verify that the MPU is correctly configured and that the fault handlers are properly set up. The following steps should be taken:

  1. Enable the MemManage Fault Handler: Ensure that the MemManage fault handler is enabled in the SCB_SHCSR register. This is already done in the provided code with the line SCB_SHCSR |= 0x00010000;.

  2. Enable the MPU: Ensure that the MPU is enabled by setting the ENABLE bit in the MPU_CTRL register. This is also correctly done in the provided code with the line MPU_CTRL = 0x00000005;.

  3. Check the Fault Status Registers: If a fault occurs, the fault status registers (SCB_CFSR, SCB_HFSR, etc.) should be checked to determine the cause of the fault. This can be done within the MemManage_Handler or HardFault_Handler functions.

  4. Debugging with Breakpoints: Set breakpoints in the MemManage_Handler and HardFault_Handler functions to confirm that the fault is being triggered as expected. This can help verify that the MPU is correctly enforcing the memory protection rules.

Example of Corrected Code

The following is the corrected version of the provided code, incorporating the fixes discussed above:

#define MPU_CTRL (*((volatile unsigned long*) 0xE000ED94))
#define MPU_RNR (*((volatile unsigned long*) 0xE000ED98))
#define MPU_RBAR (*((volatile unsigned long*) 0xE000ED9C))
#define MPU_RASR (*((volatile unsigned long*) 0xE000EDA0))
#define SCB_SHCSR (*((volatile unsigned long*) 0xE000ED24)) // System handler control and state register

void Registers_Init(void) {
    // MPU Configuring
    MPU_RNR = 0x00000000; // region 0
    MPU_RBAR = 0x20000000 | (0x00 << 4) | 0x10; // base address is 0x20000000, region 0, VALID bit set
    MPU_RASR = 0x1608FF0B; // 64 bytes, ro/ro, b=s=c=0
    SCB_SHCSR |= 0x00010000; // enable MemManage Fault
    MPU_CTRL = 0x00000005; // enable memory protection unit, guaranteeing default privileged access
}

void MemManage_Handler(void) {
    __asm(
        "MOV R4, 0x77777777\n\t"
        "MOV R5, 0x77777777\n\t"
    );
}

void HardFault_Handler(void) {
    __asm(
        "MOV R6, 0x77777777\n\t"
        "MOV R7, 0x77777777\n\t"
    );
}

int main(void) {
    Registers_Init();

    while(1) {
        __asm(
            "LDR R0, =0x20000000\n\t"
            "MOV R1, 0x77777777\n\t"
            "STR R1, [R0,#0]\n\t"
        );
    }
    return (1);
}

void SystemInit(void) {}

Conclusion

The failure of the MPU to trigger a MemManage fault when accessing a protected memory region is primarily due to two issues: the absence of the VALID bit in the MPU_RBAR register and a typo in the assembly code used to perform the memory write operation. By setting the VALID bit and correcting the assembly code, the MPU should now correctly enforce the memory protection rules and trigger the appropriate fault when an unauthorized access is attempted. Additionally, verifying the MPU configuration and fault handlers ensures that the system behaves as expected and provides the necessary debugging information to diagnose any further issues.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *