Timeline
Timeline
2025-11-21
- init
This article introduces the address space abstraction mechanism in QEMU, discussing in detail the CPU memory access flow and the two core concepts, address-space and memory-region, provided by QEMU to simulate memory and peripheral behaviors. The article further summarizes the address space layout, the address overlap and alias mechanisms of memory-region, the structural definitions and initialization flows of AddressSpace and MemoryRegion, and elucidates how QEMU uses these mechanisms to achieve discrete mapping and remapping of addresses, thereby constructing the physical address space view of the virtual machine.
Environment
Source code
1 | wget https://download.qemu.org/qemu-10.1.2.tar.xz |
Create .clangd
1 | CompileFlags: |
gdb
1 | gdb -args ./build/qemu-system-riscv64 -M virt -device edu,id=edu1 -nographic |
Basic Introduction
From the CPU’s perspective, all memory access operations are performed on addresses (load/store). The CPU does not care what device corresponds to the address, as long as it can read and write the correct results.
CPU Memory Access Flow
- After calculating the target address (via the Arithmetic Logic Unit ALU), the CPU sends it to the address bus, while also providing read/write control signals;
- **The device corresponding to the address could be a regular memory block, or an I/O device (**specifically referring to peripherals here), which will respond to the signals on the address bus;
- If it is a read operation, the data corresponding to that address is transmitted back via the bus according to the bit width specified by the CPU, generally stored in the register specified by the CPU’s memory access instruction;
- If it is a write operation, the data transmitted from the bus is written into the specified address according to the bit width specified by the CPU. If it is an I/O device, it generally updates the register corresponding to that address and may produce side effects.
QEMU Simulating Memory and Peripherals
To simulate the behavior of memory/peripherals, QEMU must at least implement the following mechanisms:
- Basic address space management,The ability to distinguish what device it is based on the address delivered by the CPU;
- Implementing discrete address mapping, as the addresses of some peripherals are not necessarily contiguous;
- Implementing address remapping, for example, the RAM and XRAM of the MCS-51 both start from address 0;
To this end, QEMU provides two concepts,address-space and memory-region(referred to as mr below), the former is used to describe the mapping relationship of the entire address space (different components may see different address spaces), and the latter is used to describe the mapping rules for a certain address range within the address space.
Address Space Layout
Execution:
1 | $ ./build/qemu-system-riscv64 -M virt -monitor stdio -s -S -display none |
Then we inputinfo mtreethe command to see the layout of the address space:
1 | (qemu) info mtree |
It can be seen that:
| Address Range | Type | Corresponding Device |
|---|---|---|
| 0x00001000~0xFFFF | ROM | Onboard Firmware |
| 0x00100000~0x00101023 | I/O | Test device + RTC |
| 0x02000000~0x020BFFFF | I/O | CLINT (Software/Timer Interrupt) |
| 0x03000000~0x030FFFFF | I/O | PCI I/O window |
| 0x04000000~0x05FFFFFF | I/O | Platform Bus device |
| 0x0C000000~0x0C5FFFFF | I/O | PLIC |
| 0x10000000~0x100081FF | I/O | UART + Virtio-MMIO |
| 0x20000000~0x23FFFFFF | ROMD | flash0 / flash1 |
| 0x80000000~0x87FFFFFF | RAM | Guest memory |
| 0x300000000~0x7FFFFFFFF | I/O | PCI MMIO alias |
- A Guest (representing the emulated object, here referring to the virt machine) can have multiple address-spaces,The address mapping relationships described by each address-space are not necessarily the same, typically I/O and memory。
- Each address-space corresponds to an mr tree, for example, the root node of the mr corresponding to address-space: memory is system, and the child nodes are arranged in order of address size.
Since mr describes the mapping rules within a specific address range, thus making it very convenient to implement discrete mapping of devices.
Example:
1 | address-space: cpu-memory-0 |
cpu-memory-0is address-spacesystemis the top-level memory-region (the container of the entire virtual system)riscv_virt_board.mrom、sifive.testetc. are all sub memory-region
Each memory-region is attached under the address-space and provides an access handler
memory_region address overlap
mr supports address range overlap at the same level, the overlapping parts are presented according to priority, with the high-priority overlapping part serving as the access target. In (prio 0, type), the prio followed by the priority, there is no address overlap between virt peripherals, so the priority is all 0.
Here is an example:
1 | 0x8000 0x70000 0x60000 0x50000 0x40000 0x30000 0x20000 0x10000 0 |
For mr A, its address range can be viewed as:
1 | 0x8000 0x70000 0x60000 0x50000 0x40000 0x30000 0x20000 0x10000 0 |
To implement the above mechanism,QEMU uses alias to describe the overlapping parts in mr. Using alias can place a part of one mr onto another mr, thereby simplifying the complexity of memory simulation (can be analogized to mmap)。
alias example
1 | 0000000030000000-000000003fffffff (prio 0, i/o): alias pcie-ecam @pcie-mmcfg-mmio 0000000000000000-000000000fffffff |
- alias memory-region maps a region to another address range of the address-space
- Facilitates different buses accessing the same physical device
- An alias is also a memory-region, but it internally references another region
AddressSpace
- Represents The complete address space seen by the CPU or bus
- Includes:
- All mapped memory-region
- Priority (prio) of each region
- Type (RAM / ROM / I/O / alias)
- Can be understood as The “physical address space view” of the virtual machine
A CPU can have multiple address-spaces (for example, a risc-v CPU has
cpu-memory-0, and also other I/O spaces or PCI bus spaces).
include/system/memory.h
1 | /** |
MemoryRegion
- Memory or I/O Specific block
- Includes:
- Starting address range (offset relative to the address-space)
- Size
- Type (RAM / ROM / I/O / alias / container)
- Sub memory-region (supports nesting)
- Corresponding read/write handler or object pointer
Can be understood as A “single block” in the address-space, which can be physical memory, device registers, PCI BARs, etc.
include/system/memory.h
1 | /** MemoryRegion: |
Initialization process
Let’s understand the relationship between mr and address-space from the QEMU initialization process:
1 | main() // system/main.c |
memory_map_init
system/physmem.c
1 | static void memory_map_init(void) |
Formemory_region_init(), eventually callingmemory_region_do_init():
system/memory.c
1 | void memory_region_init(MemoryRegion *mr, |
This piece of code will complete the initialization of some key fields of the mr.
1 | /** MemoryRegion: |
- ops points to the actual interface for mr memory access;
- subregions points to other mrs, through subregions, all associated mrs can be linked together.
Some of this initialization code consists of registered function callbacks, making it inconvenient to clarify the intermediate logic through static code review; gdb can be used to assist.
system_memory is a global variable pointer,pointing to the root node of the mr, we can set watchpoints on system_memory->ops and system_memory->subregions to see in which function they are initialized.
First, observe system_memory->ops, the commands and process are as follows:

The first and second hits on the watchpoint are reset operations on ops, and the third hit is where the actual initialization takes place; we can observe the call stack:

It can be seen thatmemory_region_initfn() is in theobject_init_with_type() is called, this is QEMU’s QOM module, which canbe simply understood as the initialization of the mr object, thisThe initialization method is a registered function pointer。
By analogy, we can find out where system_memory->subregions is initialized:

Node relationships
Further watchsystem_memory->subregions

memory_region_update_container_subregions()the process is very simple, the final execution result is as follows:
1 | struct MemoryRegion |
Doesn’t it look like a tree structure? In fact, this isRed-black tree。
There is a root field inside the address-space, pointing to the root node of the memory-region, thus implementing one address-space corresponding to one memory-region tree, as follows:
1 | AddressSpace |
Each mr corresponds to a specific memory block RAMBlock,This memory block is allocated from the Host, serving as storage for Guest peripheral devices.
mr provides some types for describing storage devices, common ones include RAM, ROM, IOMMU, container.
Back in the QEMU interactive terminal, using the following command, we can print the memory-region distribution of virt and the corresponding peripherals:

memory-region (mr) all represent an accessible address block, such as:
- RAM/ROM → stored content
- I/O → device registers
- alias → address mapping
- container → ‘container’, managing sub-regions
Each mr has starting offset(relative to the parent region) and Size。
For memory-region container type, which contains other mrs and records the offset of each mr.
container is a special type of memory-region
Itself does not store data, nor does it have direct read/write handlers
Function:
- manages sub-memory-regions
- provides address offset mapping
- establishes a hierarchical structure
In practical application scenarios, wecan use mr containers to create different address hierarchical relationships, which can clearly describe the relationships between different subsystems at the address space level, which is very helpful for achieving modularity.
Reference:
