MPU Memory Protection Unit
- The Memory Protection Unit (MPU) is a feature that allows for programmable memory access permissions and attributes for different memory regions.
- It can prevent tasks from corrupting memory or accessing critical peripherals, and can also prevent code injection attacks.
- If a memory access violates the access permissions, a fault exception will be triggered.
The Cortex-M Processor Modes
- The Cortex-M processor supports two operating modes:
Thread mode
Used to execute application software. The processor enters Thread mode when it comes out of reset.
Handler mode
Used to handle exceptions. The processor returns to Thread mode when it has finished all exception processing.
The Cortex-M Privilege Levels
Unprivileged:
Unprivileged code execution limits or excludes access to some resources like certain instructions and specific memory locations
Privileged:
The software can use all the instructions and has access to all resources.
Privileges and Modes (cont.):
The MPU allows us to change the region of memory that can be accessed based on the operating mode and the privilege level.

Privileges and Modes
Thread Mode:
In Thread mode, the CONTROL register controls whether software execution is privileged or unprivileged
Handler Mode:
In Handler mode, software execution is always privileged

- Regions (cont.)
- The regions can overlap, and can be nested
- The region 15 has the highest priority and the region 0 has the lowest.
- The priorities are fixed, and cannot be changed.

-
The MPU in Cortex-M3/M4
- The MPU configuration is a useful feature that enables various functions, including:
- Protecting important data from accidental corruption by setting a read-only access for a specific RAM/SRAM region.
- Detecting stack overflow by restricting access to a portion of RAM/SRAM at the bottom of the stack.
- Preventing code injection attacks by defining a RAM/SRAM region as execute never (XN).
- The MPU configuration is a useful feature that enables various functions, including:
-
The MPU can be fully configured using 5 registers
-
Some Important MPU Registers (cont.)
- Defines size and attributes of a MPU region
- MPU Base Attribute and Size Register: MPU->RASR
- Defines base address of a MPU region
- MPU Region Base Address Register: MPU->RBAR
- Provides information about the MPU
- MPU Type Register: MPU->TYPE
- Enable/disable and background region control
- MPU Control Register: MPU->CTRL
- Select which MPU region to be configured
- MPU Region Number Register: MPU->RNR
- Defines size and attributes of a MPU region

#define MPU_TYPE_REG (*(volatile uint32_t *)0xE000ED90)
#define START_OF_DREGION 8
uint8_t num_of_regions;
int main()
{
/*Initialize debug UART*/
debug_uart_init();
num_of_regions = MPU_TYPE_REG >> START_OF_DREGION;
printf("Number of regions : %d\n\r", num_of_regions);
}





#define MPU_RBASE_ADDR_REG (*(volatile uint32_t *)0xE000ED9C)
uint32_t min_region_sz;
int main()
{
/*Set bits 4 to 32 to 1 in the MPU_RBASE_ADDR_REG
* and then read the MPU_RBASE_ADDR_REG. If the bits remain set then the
* minimum possible region size of 32bytes is supported */
MPU_RBASE_ADDR_REG = 0xFFFFFFE0; // 0b 1111 1111......1110 0000
min_region_sz = MPU_RBASE_ADDR_REG;
while (1)
{
}
}

Memory Attributes and Access Permissions









_A1 , _A2 and _A3
the MemManageFault Registers

- MemManage Address Register (MMFAR)
- When the MMARVALID bit of the MMFSR is set to 1, register contains the address of the access which triggered the fault recorded in MMFSR.

Catching Stack Overflow using the MPU


we changed the end of stack in the linker file
#include <stdint.h>
/* System control registers */
#define SHCSR_REG (*(volatile uint32_t *)0xE000ED24)
#define MPU_CTRL_REG (*(volatile uint32_t *)0xE000ED94)
#define MPU_RAS_REG (*(volatile uint32_t *)0xE000EDA0)
#define MPU_RBASE_ADDR_REG (*(volatile uint32_t *)0xE000ED9C)
#define MPU_TYPE_REG (*(volatile uint32_t *)0xE000ED90)
#define START_OF_DREGION 8
#define MEMFAULTENA (1U<<16)
uint8_t num_of_regions;
uint32_t min_region_sz;
#define SIZE_TO_CONFIG 32
/* The maximum stack we have defined in the linker script */
#define MAX_STACK 128
uint32_t factorial(uint32_t n)
{
if(n == 0 || n == 1)
{
return 1;
}
else{
return n * factorial(n - 1);
}
}
int main()
{
/*Enable FPU*/
fpu_enable();
/*Initialize debug UART*/
debug_uart_init();
/*Enable MemFault Handler*/
SHCSR_REG |= MEMFAULTENA;
/*Check number of regions*/
num_of_regions = MPU_TYPE_REG >> START_OF_DREGION;
/*Check minimum region size*/
/*Set bits 4 to 32 to 1 in the MPU_RBASE_ADDR_REG
* and then read the MPU_RBASE_ADDR_REG. If the bits remain set then the
* minimum possible region size of 32bytes is supported */
MPU_RBASE_ADDR_REG = 0xFFFFFFF0; // 0B 1111 1111.....1110 0000
min_region_sz = MPU_RBASE_ADDR_REG;
/*Compute final base addr of region to config*/
const uint32_t base_addr = ((uint32_t)0x20000000 + MAX_STACK );
/*Config MPU_RBASE_ADDR_REG*/
/*Set Address, VALID bit, and Region number to 0*/
MPU_RBASE_ADDR_REG = (base_addr | 1 << 4 | 0);
/*Config MPU_RAS_REG*/
MPU_RAS_REG = (0b000 << 24) | (0b000110 << 16) | (4 << 1) | 0x1;
/*Config MPU_CTRL_REG*/
MPU_CTRL_REG = (1U<<2) | (1U<<0);
uint32_t fact_result;
while(1)
{
fact_result = factorial(5);
}
}
void HardFault_Handler(void)
{
printf("HARDFAULT \n\r");
}
void MemManage_Handler(void)
{
printf("MEMFAULT \n\r");
}