Skip to content

ioremap()

Overview

Memory-Mapped I/O (MMIO) allows hardware peripherals to expose registers through physical memory addresses.

Unlike normal RAM, these physical addresses correspond to hardware devices rather than system memory.

Linux drivers cannot directly access MMIO physical addresses. Instead, they must create a kernel virtual mapping using ioremap().

After mapping, drivers access device registers through the returned virtual address using APIs such as readl() and writel().


Why ioremap()?

Modern operating systems use virtual memory.

Kernel code normally operates on virtual addresses, while hardware peripherals expose fixed physical addresses.

For example:

Physical Address

0xFE200000
    │
    ▼
GPIO Controller Registers

Instead of directly accessing this physical address, Linux performs:

Physical Address
        │
        ▼
ioremap()
        │
        ▼
Kernel Virtual Address

The returned virtual address is then used throughout the driver.


Linux MMIO Architecture

In Linux, ioremap() establishes a page-table mapping between a virtual address and a physical MMIO region.

Whenever the driver accesses the mapped address, the CPU MMU performs address translation automatically.

Driver

Kernel Virtual Address
          │
          ▼
        MMU
          │
          ▼
Physical Address
          │
          ▼
Hardware Registers

The driver never performs this translation itself.


Simulator Architecture

The simulator cannot modify kernel page tables.

Instead, it implements a simplified MMIO mapping layer entirely in userspace.

            +----------------------+
            |     io_region        |
            +----------------------+
            | Physical Address     |
            | Size                 |
            | Backing Storage      |
            +----------+-----------+
                       |
                       |
            +----------v-----------+
            |    io_mapping        |
            +----------------------+
            | Virtual Address      |
            | Physical Address     |
            | Size                 |
            +----------+-----------+
                       |
                       ▼
           io_mapping_translate()
                       |
                       ▼
             Backing Storage Pointer

Unlike Linux, the simulator introduces backing storage.

This storage is allocated from heap memory and simulates hardware registers.


MMIO Region

A region represents a physical MMIO device.

io_region_register(0x10000000, 0x1000);

Each registered region owns:

  • Physical base address
  • Region size
  • Simulated backing storage

Multiple virtual mappings may reference the same region.


Virtual Mapping

Drivers create virtual mappings using ioremap().

base = ioremap(0x10000000, 0x100);

The simulator allocates virtual addresses independently of the backing storage.

For example:

Physical Address          Virtual Address

0x10000000      --->      0x20000000
0x10000800      --->      0x20001000

Mappings are allocated in page-sized increments, similar to Linux.


Address Translation

The simulated virtual address cannot be dereferenced directly.

Instead:

Virtual Address
        │
        ▼
Find Mapping
        │
        ▼
Calculate Mapping Offset
        │
        ▼
Calculate Region Offset
        │
        ▼
Backing Storage

The translation computes:

storage
+ region_offset
+ mapping_offset

This replaces the work normally performed by the MMU.


Mapping Lifecycle

The ownership model follows the Linux driver model.

io_region_register()
        │
        ▼
ioremap()
        │
        ▼
MMIO Access
        │
        ▼
iounmap()
        │
        ▼
io_region_unregister()

A region cannot be removed while active mappings still exist.


Linux vs Simulator

Linux Simulator
Hardware registers Backing storage
Physical address Physical address
Page table Mapping list
MMU io_mapping_translate()
Kernel virtual address Simulated virtual address
readl() directly accesses MMIO readl() translates to backing storage

Key Points

  • MMIO devices are accessed through memory addresses rather than I/O ports.
  • ioremap() converts a physical MMIO address into a kernel virtual address.
  • Linux relies on the MMU to perform address translation automatically.
  • The simulator replaces the MMU with io_mapping_translate().
  • Backing storage exists only in the simulator to emulate hardware registers.
  • Multiple virtual mappings may reference the same physical MMIO region.