HomeAbout UsContact Us

Zephyr MPU Setup for Memory Protection in Embedded Systems

By Jithin Tom
Published in Embedded Concepts
August 29, 2026
4 min read
Zephyr MPU Setup for Memory Protection in Embedded Systems

Table Of Contents

01
Understanding the Zephyr MPU Architecture
02
Problem: Unprotected Memory Access Leading to System Faults
03
Solution: Step-by-Step MPU Configuration in Zephyr
04
Complete Code Example: MPU Configuration for STM32F4
05
Verification: Testing MPU Protection
06
ASCII Art: MPU Region Layout
07
Advanced MPU Configuration Techniques
08
Advanced MPU Configuration Techniques
09
Summary
10
Related Reading
11
References
12
Frequently Asked Questions

Zephyr RTOS provides a Memory Protection Unit (MPU) to enforce memory access controls, enhancing system reliability by preventing tasks from accessing unauthorized memory regions. Proper MPU configuration is essential for safety-critical embedded systems where memory corruption can lead to catastrophic failures. This article explains how to configure the Zephyr MPU, defines memory regions with appropriate permissions, and provides working code examples to protect critical system memory.

Understanding the Zephyr MPU Architecture

The Zephyr MPU abstraction layer provides a unified interface to configure hardware MPUs across different architectures (ARM Cortex-M, RISC-V, x86). The MPU divides the memory address space into regions, each with configurable base address, size, and access permissions (read, write, execute for privileged and unprivileged modes).

MPU Region Configuration

Each MPU region requires:

  • Base address: Start address of the memory region (aligned to region size)
  • Region size: Power-of-two size (typically 32 bytes to 4GB)
  • Access permissions: Separate settings for privileged and unprivileged access
  • Memory attributes: Cacheability, bufferability (architecture-dependent)

Zephyr supports up to 8 MPU regions by default, configurable via CONFIG_MPU_REGION_NUM. Regions are configured in priority order (region 0 highest priority), with overlapping regions requiring careful priority management.

Problem: Unprotected Memory Access Leading to System Faults

Consider a scenario where multiple threads share memory resources without protection. A buggy thread could accidentally overwrite critical kernel data structures, stack of another thread, or read-only code sections, leading to unpredictable behavior, security vulnerabilities, or system crashes.

Without MPU protection:

  • Threads can corrupt each other’s stacks
  • Unprivileged code can modify privileged kernel data
  • Buffer overflows can execute arbitrary code from data regions
  • No hardware enforcement of memory isolation

Solution: Step-by-Step MPU Configuration in Zephyr

Step 1: Enable MPU Support

First, enable MPU support in your Zephyr configuration:

CONFIG_MPU=y
CONFIG_MPU_REQUIRES_NONOVERLAPPING_REGIONS=y

Step 2: Define Memory Regions

Define your memory regions based on your application’s memory layout. Typical regions include:

  • Flash/code region: Read-only, execute-only for privileged mode
  • RAM/data region: Read-write for privileged and unprivileged modes (as needed)
  • Peripheral regions: Configure based on memory-mapped I/O access requirements
  • Stack regions: Optional per-thread stack protection
  • Background region: Default memory access for undefined addresses

Step 3: Configure MPU Regions

Use the z_arm_mpu_configure_region() function (ARM Cortex-M) or architecture-specific equivalent to configure each region. The function takes region index, base address, size, and attribute flags.

Step 4: Set Up Privileged/Unprivileged Threads

Configure threads to run in privileged or unprivileged mode via thread creation options:

k_thread_create(&thread_data, thread_stack, K_THREAD_STACK_SIZEOF(thread_stack),
thread_func, NULL, NULL, NULL,
K_PRIO_COOP(7), 0, K_NO_WAIT);

To create an unprivileged thread, add K_USER to the priority:

K_PRIO_COOP(7) | K_USER

Step 5: Enable MPU

Finally, enable the MPU with z_arm_mpu_enable().

Complete Code Example: MPU Configuration for STM32F4

Here’s a complete example configuring the MPU for an STM32F407Zephyr application:

#include <zephyr.h>
#include <arm/mpu.h>
/* MPU configuration stack */
static struct arm_mpu_region mpu_regions[] = {
/* Region 0: Flash (code) - read only, privileged only */
MPU_REGION_ENTRY("FLASH_0",
0x08000000,
REGION_FLASH_SIZE,
REGION_RO_EXECUTE_PRIV),
/* Region 1: SRAM - read/write, privileged and unprivileged */
MPU_REGION_ENTRY("SRAM_0",
0x20000000,
REGION_SRAM_SIZE,
REGION_RW_DATA_PRIV_U),
/* Region 2: Peripherals - read/write, privileged only */
MPU_REGION_ENTRY("PERIPHERALS",
0x40000000,
REGION_PERIPH_SIZE,
REGION_RW_DEVICE_PRIV),
/* Region 3: Background region - no access */
MPU_REGION_ENTRY_BACKGROUND(
REGION_NO_ACCESS)
};
void configure_mpu(void)
{
/* Disable MPU during configuration */
z_arm_mpu_disable();
/* Configure MPU regions */
for (int i = 0; i < ARRAY_SIZE(mpu_regions); i++) {
z_arm_mpu_configure_region(&mpu_regions[i], i);
}
/* Enable MPU */
z_arm_mpu_enable();
}
void main(void)
{
configure_mpu();
/* Create unprivileged thread */
k_thread_create(&unpriv_thread_data, unpriv_thread_stack,
K_THREAD_STACK_SIZEOF(unpriv_thread_stack),
unpriv_thread_func, NULL, NULL, NULL,
K_PRIO_COOP(7) | K_USER, 0, K_NO_WAIT);
/* Start scheduler */
k_start();
}

Explanation of MPU Attributes

The MPU_REGION_ENTRY macros use predefined attribute constants:

  • REGION_RO_EXECUTE_PRIV: Read-only, execute-only, privileged access only
  • REGION_RW_DATA_PRIV_U: Read-write data, accessible by both privileged and unprivileged
  • REGION_RW_DEVICE_PRIV: Read-write device memory, privileged only (strongly ordered)
  • REGION_NO_ACCESS: No access (triggers fault on any access)

Verification: Testing MPU Protection

To verify your MPU configuration works correctly:

  1. Create a test thread that attempts to access protected memory
  2. Trigger a memory access fault by writing to a read-only region or executing from data
  3. Verify the fault handler catches the exception and logs appropriate diagnostic information

Example Fault Test

void unpriv_thread_func(void *p1, void *p2, void *p3)
{
ARG_UNUSED(p1);
ARG_UNUSED(p2);
ARG_UNUSED(p3);
/* Attempt to write to flash (should trigger MPU fault) */
volatile uint32_t *flash_ptr = (uint32_t *)0x08000000;
*flash_ptr = 0xDEADBEEF; /* This should cause a fault */
/* If we reach here, MPU is not working correctly */
printk("MPU test failed: write to flash succeeded\n");
}

Configure a memory management fault handler to catch and log the fault:

void MemManage_Handler(void)
{
printk("Memory management fault occurred!\n");
/* Extract fault status registers for diagnosis */
/* ... */
while (1) {
/* Halt or perform recovery */
}
}

ASCII Art: MPU Region Layout

The following diagram illustrates typical MPU region configuration for a Zephyr application:

+--------------------------------------------------------------+
| 0x08000000 - 0x080FFFFF |
| FLASH (Code Region) |
| Attributes: RO, Execute, Privileged Only |
+--------------------------------------------------------------+
| 0x20000000 - 0x2001FFFF |
| SRAM (Data Region) |
| Attributes: RW, Privileged & Unprivileged |
+--------------------------------------------------------------+
| 0x40000000 - 0x400FFFFF |
| PERIPHERALS (Memory-Mapped I/O) |
| Attributes: RW, Privileged Only, Strongly Ordered |
+--------------------------------------------------------------+
| 0x60000000 - 0x9FFFFFFF |
| BACKGROUND REGION (Default) |
| Attributes: NO ACCESS (Triggers Fault on Any Access) |
+--------------------------------------------------------------+

Advanced MPU Configuration Techniques

For complex systems, basic MPU configuration may not be sufficient. Consider these advanced techniques:

Dynamic MPU Region Reconfiguration

In some applications, you may need to change MPU settings at runtime, for example, to temporarily grant access to a memory region for a specific operation. Zephyr allows dynamic reconfiguration of MPU regions, but care must be taken to ensure system stability.

To reconfigure an MPU region at runtime:

Note: Disabling the MPU leaves the system unprotected during the reconfiguration window. Ensure that no memory access violations can occur during this critical section.

Combining MPU with Memory Domains

Zephyr’s memory domain API allows grouping memory partitions and assigning them to threads. When used with MPU, memory domains can provide an additional layer of flexibility.

However, note that memory domains are primarily for user mode threads and do not replace the need for proper MPU configuration for system memory.

Example of defining a memory domain and assigning a partition:

MPU and Cache Considerations

When configuring MPU regions, it’s important to consider the cache attributes. Incorrect cache settings can lead to stale data or unexpected behavior.

For example, marking a region as cacheable when it should be device memory (non-cacheable) can cause issues with memory-mapped peripherals.

Always refer to the processor’s documentation for the correct cache and buffer settings for each memory region.

Advanced MPU Configuration Techniques

For complex systems, basic MPU configuration may not be sufficient. Consider these advanced techniques:

Dynamic MPU Region Reconfiguration

In some applications, you may need to change MPU settings at runtime, for example, to temporarily grant access to a memory region for a specific operation. Zephyr allows dynamic reconfiguration of MPU regions, but care must be taken to ensure system stability.

To reconfigure an MPU region at runtime:

void reconfigure_mpu_region(int region_index, uint32_t base_addr, uint32_t size, uint32_t attr)
{
/* Disable MPU during reconfiguration */
z_arm_mpu_disable();
/* Update the region configuration */
struct arm_mpu_region region;
region.base = base_addr;
region.size = size;
region.attr = attr;
z_arm_mpu_configure_region(&region, region_index);
/* Re-enable MPU */
z_arm_mpu_enable();
}

Note: Disabling the MPU leaves the system unprotected during the reconfiguration window. Ensure that no memory access violations can occur during this critical section.

Combining MPU with Memory Domains

Zephyr’s memory domain API allows grouping memory partitions and assigning them to threads. When used with MPU, memory domains can provide an additional layer of flexibility.

However, note that memory domains are primarily for user mode threads and do not replace the need for proper MPU configuration for system memory.

Example of defining a memory domain and assigning a partition:

/* Define a memory partition for a custom buffer */
K_APP_DMEM(custom_buffer) = {0};
/* Initialize the memory domain */
k_mem_domain_init(&my_domain, 1, &custom_buffer_partition);
/* Assign the domain to a thread */
k_thread_mem_domain_add(&my_thread_data, &my_domain);

MPU and Cache Considerations

When configuring MPU regions, it’s important to consider the cache attributes. Incorrect cache settings can lead to stale data or unexpected behavior.

For example, marking a region as cacheable when it should be device memory (non-cacheable) can cause issues with memory-mapped peripherals.

Always refer to the processor’s documentation for the correct cache and buffer settings for each memory region.

Summary

Configuring the Zephyr Memory Protection Unit (MPU) is a critical step in building robust and secure embedded systems. By defining memory regions with appropriate access permissions, you can:

  • Prevent accidental memory corruption between tasks
  • Protect critical kernel data structures from unauthorized access
  • Defend against common exploits like buffer overflow attacks
  • Achieve hardware-enforced memory isolation as required by safety standards

The Zephyr MPU abstraction simplifies configuration across different architectures while providing fine-grained control over memory access permissions. Follow the step-by-step guide and code examples presented here to integrate MPU protection into your Zephyr-based applications, significantly enhancing system reliability and security.

  • Zephyr Thread Management and Synchronization
  • Configuring Zephyr Stacks for Thread Safety
  • Hardware Abstraction Layer Design for Microcontrollers

References

  1. Zephyr Project Documentation, “Memory Protection Unit (MPU) Sample”, https://docs.zephyrproject.org/latest/samples/arch/mpu/mpu_test/README.html
  2. ARMv7-M Architecture Reference Manual, “Memory Protection Unit”, ARM DDI 0403E.d
  3. STM32F4 Reference Manual, “Memory Protection Unit (MPU)”, STMicroelectronics RM0090
  4. “Designing Embedded Systems with 32-Bit PIC Microcontrollers and MikroC”, Lucio Di Jasio, Newnes, 2008
  5. “Embedded Systems Architecture: Prepare for the Growth of IoT in Embedded Systems”, Tammy Noergaard, Newnes, 2012
  6. “Secure Embedded Systems: Principles and Practices”, Jon Rogers et al., Springer, 2020

Frequently Asked Questions

What is the Memory Protection Unit (MPU) in Zephyr?

The Memory Protection Unit (MPU) in Zephyr is a hardware feature that allows defining memory regions with specific access permissions, preventing unauthorized access and enhancing system robustness by isolating tasks and protecting critical memory.

How many MPU regions does Zephyr support by default?

Zephyr supports up to 8 MPU regions by default, configurable via CONFIG_MPU_REQUIRES_NONOVERLAPPING_REGIONS and CONFIG_MPU_REGION_NUM, allowing flexible memory protection schemes for complex embedded applications.

Why configure the MPU background region as privileged access only?

Configuring the MPU background region as privileged access only ensures that unprivileged threads cannot access memory outside defined regions, triggering a memory management fault on violations and preventing accidental or malicious corruption of system memory.

Tags

zephyrmpumemory-protectionembedded-systems

Share


Previous Article
Fixing Cortex-M Hard Fault Handler Stack Corruption
Jithin Tom

Jithin Tom

A Closer Look at C/C++, RTOS, and Embedded Systems

Related Posts

Fixing Zephyr Devicetree Overlays That Silently Fail
Fixing Zephyr Devicetree Overlays That Silently Fail
August 25, 2026
6 min
© 2026, All Rights Reserved.
Powered By Netlyft

Quick Links

Advertise with usAbout UsContact Us

Social Media