1. Today’s topic
Today we examine hardware memory protection:
MPU — Memory Protection Unit
privileged and unprivileged code
read-only and execute-never regions
stack guard
configuration protection
DMA buffer isolation
FreeRTOS MPU tasks
ESP32 stack watchpoints and memory protectionThe central idea: MPU does not fix a bug, but turns hidden memory corruption into an immediate, diagnosable fault near the responsible instruction.
2. Why this matters in your projects
For EC25 and the AT parser, MPU helps catch a buffer overrun immediately rather than through a later crash in malloc() or MQTT. For a phase detector, protect read-only tables:
GPIO mapping
channel active level
threshold values
phase transition policy
permitted phase combinationsFor DMA, MPU does not always restrict DMA itself, but helps define a separate DMA pool, make it non-cacheable and XN, and place a guard/canary between DMA and critical structures.
3. Theory
5.3.1 3.1. What MPU is
MPU describes memory regions and assigns:
address and size
privileged/unprivileged access
read-only or read-write
execute-never
cacheable/bufferable/shareable
memory typeMPU does not create virtual memory or replace MMU. It determines whether the current code may read/write/execute a physical address.
5.3.2 3.2. A typical protection map
| Region | Access | Execution |
|---|---|---|
| Flash containing code | read-only | permitted |
| .rodata | read-only | usually no separate requirement |
| SRAM | read-write | forbidden |
| Task stacks | read-write | forbidden |
| Heap | read-write | forbidden |
| DMA pool | read-write, non-cacheable | forbidden |
| Peripheral registers | privileged read-write | forbidden |
| Config snapshot | read-only after init | forbidden |
| Unused memory | no-access | forbidden |
The main practical protection:
RAM = XN, execute-never5.3.3 3.3. Stack overflow: protection levels
1. Correct stack size.
2. Software canary for FreeRTOS.
3. Hardware watchpoint at the stack end on ESP32.
4. MPU guard region next to the stack.An MPU guard catches a write into the forbidden region immediately, before a neighboring task is corrupted.
5.3.4 3.4. FreeRTOS MPU port
FreeRTOS MPU variants can create restricted tasks:
xTaskCreateRestricted();
vTaskAllocateMPURegions();The idea:
unprivileged modem logic
→ command queue
→ privileged UART driverThe AT parser should not directly access power GPIOs, Flash configuration, or another task’s stack.
5.3.5 3.5. MPU and DMA
The core’s MPU does not necessarily restrict DMA, because DMA is a separate bus master. Therefore you need:
exact transfer length
static assertions for sizes
DMA descriptors
guard/canary after the buffer
sequence numbers
DMA error flags checkingOn STM32H7/F7, a non-cacheable DMA pool is useful.
5.3.6 3.6. ESP32 without a classic MPU
For ESP32-WROOM-32U, a practical protection set is:
FreeRTOS end-of-stack watchpoint
FreeRTOS stack overflow checks
GCC stack protector
heap poisoning
heap integrity checks
hardware watchpoints
core dump
owner-task architecture
bounds-checked parsers4. Common mistakes
1. Enabling MPU without a complete memory map.
2. Allowing privileged default map and assuming everything is protected.
3. Making all SRAM executable.
4. Protecting the wrong side of the stack.
5. Guard lies in the same permissive MPU region.
6. Copying MPU settings from another Cortex-M.
7. Incorrect DMA-region cacheability.
8. Expecting MPU to stop DMA.
9. A guard that is too small.
10. A HardFault handler prints through a corrupt stack.
11. Making MPU the first step instead of fixing ownership.5. Practical assignment for 30-60 minutes
Create MEMORY_PROTECTION_POLICY.md:
# Memory protection policy
1. Flash code is read-only and executable.
2. General SRAM is read-write and execute-never.
3. Peripheral memory is privileged and execute-never.
4. DMA pool is execute-never and has explicit cache policy.
5. Validated runtime configuration becomes read-only.
6. Critical task stacks have guard protection where possible.
7. MemManage faults save PC, LR, CFSR and MMFAR.
8. MPU configuration is generated for the exact MCU architecture.For STM32: create a protected configuration region, switch it to read-only, and attempt a write in a debug build. For ESP32: enable the stack watchpoint/stack protector and run a controlled stack-overflow test in a separate HIL/debug firmware. CLI command:
memprot statusExample:
memory protection:
MPU=enabled
regions=6
RAM_XN=yes
config_RO=yes
DMA_region=non-cacheable
stack_guards=3
last_fault=none6. Further reading
- CMSIS-Core MPU API.
- STM32CubeMX/CubeMX2 MPU configuration for the selected STM32.
- FreeRTOS MPU support.
- ESP-IDF Fatal Errors and Heap Memory Debugging.
Brief recap
owner-task architecture
→ bounded buffers
→ stack sizing
→ canaries/watchpoints
→ XN for RAM
→ read-only config
→ DMA memory policy
→ stack guards
→ restricted RTOS tasks
→ HIL fault injectionExercise
A CPU write into a protected configuration snapshot faults correctly, but DMA still corrupts adjacent memory. Explain why the MPU result does not prove DMA safety.
Self-check criteria: Separate CPU access control from DMA bounds. A successful CPU protection test cannot replace DMA ownership/length validation.
Show the supplied answer
The CPU MPU may not constrain the separate DMA bus master. Verify transfer lengths, destination capacity, descriptors, buffer ownership, and guard/canary evidence; use the target’s DMA-access and cache policy.
Exercise
Design a controlled stack-guard test for a debug/HIL build. What target-specific properties must be checked before deliberately reaching the guard?
Self-check criteria: Show that the guard lies outside permitted stack access and that diagnostics do not depend on the already corrupted stack.
Show the supplied answer
Check stack growth direction, region alignment/size, overlapping MPU permissions, the target’s watchpoint/MPU capability, and the fault handler’s usable stack. Trigger the test only in the isolated debug setup and retain fault evidence.