Skip to main content How MMU, MPU, Page Tables, and Address Spaces Differ Across MCU, RTOS, and Linux Systems | IoT Worker

How MMU, MPU, Page Tables, and Address Spaces Differ Across MCU, RTOS, and Linux Systems

In embedded systems, the word “address” often sounds as if everyone means the same thing.

In bare-metal code, a pointer may be close to a real SRAM address. RTOS tasks may share one address space. An MCU with an MPU can catch some out-of-bounds accesses, but usually does not provide full address translation. A Linux user process sees virtual addresses, while a DMA device may see a different bus address.

Mixing these layers causes wrong debugging: handing a user pointer directly to DMA, an RTOS task corrupting another task’s stack, the same Linux virtual address mapping to different physical pages in different processes, an MPU fault being treated as bad memory, or a physical address being mistaken for a device DMA address.

The safer first model is this: without protection, an address is close to the hardware view; an MPU adds permission boundaries to address regions; an MMU translates virtual addresses into physical addresses through page tables; and the operating system uses these mechanisms to build task or process address spaces.

bare metal: address -> memory / register
MPU: address -> region permission check -> memory / register
MMU: virtual address -> page table -> physical address + permission
Linux: process virtual address space -> kernel-managed mappings

So the first debugging question is not “what is this address?” It is “what does this address mean in this system model?”

Without MPU or MMU, Addresses Are Close to Hardware

Many small bare-metal MCU systems have no MMU, and may have no MPU. Accessing an address usually means accessing a location in the chip’s memory map.

Common address regions include:

  • flash
  • SRAM
  • peripheral registers
  • boot ROM
  • special alias or bit-band regions
  • external RAM or memory-mapped storage windows

This model is simple, direct, and low overhead. Startup code, drivers, and application logic usually share one address view.

The cost is weak isolation:

  • a null or wild pointer may corrupt global data
  • stack overflow may overwrite another task stack or control block
  • application code can directly write peripheral registers
  • driver/application boundaries rely mostly on code discipline
  • errors may show up much later than the original write

So in bare metal or small RTOS systems, a wrong address often does not cause a segmentation fault. It silently corrupts system state.

An RTOS Does Not Automatically Mean Address Isolation

An RTOS provides tasks, scheduling, synchronization, timers, and ISR-to-task paths. It does not automatically provide process-level address isolation.

Many RTOS tasks share one system address space:

task A stack
task B stack
heap
global variables
peripheral registers
RTOS kernel objects

Task switching mainly switches registers, stack pointer, and scheduling state. It does not necessarily switch page tables or address spaces. A bad pointer in one task may directly write another task’s data.

That is why stack sizing, stack-watermark checking, memory pools, queue lifetimes, and shared-data synchronization matter so much in RTOS systems. A task is a scheduling unit, not necessarily a protection boundary.

Some RTOSes can use an MPU for task isolation or kernel/user separation, but that is an additional design choice, not something every RTOS has by default.

An MPU Mainly Protects Regions

MPU means Memory Protection Unit. It usually does not translate virtual addresses into another physical address space. Instead, it configures access permissions and attributes for address regions.

A typical MPU can do things such as:

  • make flash read-only or executable
  • allow only one task to access a RAM region
  • allow only privileged code to access peripheral registers
  • place a guard region at the end of a stack
  • configure cache, buffer, or execute attributes for regions

The value of an MPU is earlier error detection and some permission boundaries.

But it differs clearly from a full MMU:

  • region count is usually limited
  • granularity is usually coarser than page tables
  • it does not provide independent virtual address spaces per process
  • it does not naturally support demand paging, file mappings, or swapping
  • address values usually remain close to the system physical address view

So an MPU is more like guardrails around a real address view. It improves reliability and isolation, but it is not the same as Linux-style virtual memory.

An MMU Translates Addresses and Checks Permissions

MMU means Memory Management Unit. It uses page tables to translate virtual addresses into physical addresses while checking permissions and attributes.

A typical access path is:

CPU accesses virtual address
-> MMU / TLB looks up mapping
-> physical address, permissions, cache attributes are found
-> access is allowed or an exception is raised

Page tables commonly record:

  • whether a virtual page has a mapping
  • which physical page it maps to
  • whether it is readable, writable, executable
  • whether user mode may access it
  • cache and memory-type attributes
  • dirty, accessed, shared, or global state

The key change with an MMU is that the address seen by a program is not the physical address. The OS can let different processes see similar virtual layouts while mapping them to different physical pages. It can also map one physical page into multiple address spaces for sharing.

This is the hardware foundation for process isolation, demand allocation, mmap, shared memory, and user/kernel separation.

Linux Process Address Spaces Are Built by the OS Using the MMU

In Linux-like systems, a user process sees its own virtual address space.

The same virtual address value may map to different physical pages in different processes:

process A: VA 0x400000 -> physical page X
process B: VA 0x400000 -> physical page Y

This gives each process its own memory-layout view. Code, heap, stack, shared libraries, mmap regions, and inaccessible gaps all belong to that process’s address-space view.

The kernel maintains page tables and physical pages:

  • when physical pages are allocated
  • which pages are readable, writable, executable
  • which pages are shared
  • whether a page fault can create a valid mapping
  • which pages are file-backed
  • which address space is active during a process switch

So a Linux user-space pointer cannot be understood outside its process address space. Storing a user pointer long-term in a driver, or handing it directly to DMA hardware, is a dangerous model.

Kernel, Physical, and DMA Addresses Are Also Different

In MMU-based systems, the kernel itself runs through virtual mappings too.

Drivers often encounter several address types:

  • user virtual address: a pointer inside a user process
  • kernel virtual address: an address kernel code can access
  • physical address: a physical page address seen by the CPU memory system
  • DMA address: an address a device can use through a bus or IOMMU
  • I/O virtual address: an address mapped for a device through an IOMMU

These values may be identical on some simple platforms, but they must not be treated as the same concept.

DMA is especially error-prone. A device usually cannot understand a normal user virtual address. The driver must use kernel APIs to establish mappings, pin or manage pages, handle cache coherency, and obtain a DMA address the device can use.

So “I have a pointer, write it into the DMA register” is not a reliable model.

Page Faults and Faults Depend on the System Model

The same bad access can look different across systems.

Without MPU or MMU, an invalid write may not fault immediately. It may silently corrupt memory. On an MCU with MPU, crossing into a protected region may trigger a MemManage fault or similar exception. On Linux with MMU, user access to an unmapped page, read-only page, or non-executable page may trigger a page fault, which the kernel may fix or convert into a signal.

A page fault is not always an error. Linux can allocate pages on demand, or read data only when an mmaped file page is first accessed. It becomes a visible error only when the access cannot be legally satisfied.

So after a fault, ask:

  • does this system have an MPU or MMU?
  • is this a permission error, missing mapping, unaligned access, or bus error?
  • did it happen in user mode, kernel mode, ISR, or early boot?
  • which region does the accessed address belong to?
  • does the OS have a way to repair this access?

The same “bad address” has very different meanings in different system models.

On ARM and RISC-V, Separate Architectural Capability From Chip Use

ARM and RISC-V both appear in systems with no MMU, with an MPU or PMP-like protection, and with full MMU-based Linux.

For example, Cortex-M commonly has an MPU or no MPU. Cortex-A commonly has an MMU and complex caches. A RISC-V MCU may have only PMP or simple protection, while a Linux-capable RISC-V SoC needs suitable privileged architecture, page-table mechanism, timer, interrupts, and kernel support.

Do not infer virtual memory from the words ARM or RISC-V alone. Check:

  • concrete core or profile
  • whether MMU, MPU, PMP, or IOMMU is implemented
  • whether firmware and OS enable it
  • page-table format and address width
  • cache and memory-attribute support
  • whether the kernel, RTOS, or bare-metal software actually uses the capability

Hardware support does not mean the system has enabled it. Even when enabled, not every address follows the same rules.

Classify the Address First

When debugging memory access, overflow, DMA, or sharing problems, classify the address first:

  • is this a bare-metal unified address or a process virtual address?
  • does the current CPU have an MMU, and are page tables enabled?
  • which address space is active for this task or process?
  • is this flash, SRAM, DRAM, peripheral register space, or an mmap region?
  • is the access read, write, execute, or user-mode access?
  • can MPU, PMP, page tables, or IOMMU block this address?
  • is the driver using kernel virtual, physical, or DMA address?
  • are cache attributes and DMA synchronization direction correct?

Once the address is placed into the right layer, many problems become clearer. Otherwise one pointer value mixes permissions, translation, lifetime, cache, and device-bus issues together.

What to Remember

MPU and MMU both relate to memory protection, but they solve different problems.

An MPU mainly divides a close-to-real address view into protected regions, adds permissions, and catches out-of-bounds access earlier. An MMU uses page tables for address translation and permission checks, allowing an OS to build independent address spaces, shared mappings, demand allocation, and user/kernel isolation.

Bare metal, RTOS, MPU systems, and Linux differ not only by “one more address layer.” They give pointers, task boundaries, faults, DMA, and shared memory different meanings.

Start debugging by asking: which address space does this address belong to, which protection or translation mechanisms does it pass through, and who finally observes it? With that answered, memory and driver bugs have a correct starting point.