Model below:

  • ordinary RAM
  • memory-mapped I/O(MMIO)
  • memory controllers that can dynamically reroute physical memory regions to different destinations

Memory is modeled as an acyclic graph (tree) of MemoryRegion objects. Sinks(leaves) are RAM and MMIO regions, while other nodes represent buses, memory controllers, and memory regions that have been rerouted.

In addition to Memory Regions objects, the memory API provides AddressSpace objects for every root and possibly for intermediate MemoryRegions too. These represent memoyr as seen from the CPU or a device’s viewpoint.

Type of regions

  • RAM: simply a range of host memory that can be made available to the guest.
  • MMIO: a range of guest memory that is implemented by host callbacks; each read or write causes a callback to be called on the host.
  • ROM: work like a RAM for read and forbid write.
  • ROM device: work like a RAM for read, but like MMIO for write (invoking a callback)
  • IOMMU region: translate addresses of accesses make to it and forwards them to some other target memory regions
  • container: a container simply includes other memory regions, each at a different offset. Containers are useful for grouping several regions into one unit. For example, a PCI BAR may be composed of a RAM region and an MMIO region.
  • alias: a subsection of another region. Aliases allow a region to be split aprt into discontiguous regions.
  • reservation region: for debugging
struct MemoryRegion {
	// MMIO read/write callbacks
	const MemoryRegionsOps *ops;
	
	// parent region
	MemoryRegion *container;
	
	// Occupied address range
	hwaddr addr;
	Int128 size;
	
	// Children
	QTAILQ_HEAD(, MemoryRegion) subregions;
}
 
// Types
// Container, no backing memory
memory_region_init();
// backed by host memory
memory_region_init_ram();
// I/O registers calls ops->read/write
memory_region_init_io();
// View of another region
memory_region_init_alias();

Overlapping regions and priority

priority values are local to a container.

Visibility

The memory core uses the following rules to select a memory region when the guest accesses an address:

  • all direct subregions of the root region are matched against the address, in descending priority order
    • if the address lies outside the region offset/size, the subregion is discarded
    • if the subregion is a leaf (RAM or MMIO), the search terminates, returning this leaf region
    • if the subregion is a container, the same algorithm is used within the subregion (after the address is adjusted by the subregion offset) (Rerun)
    • if the subregion is an alias, the search is continued at the alias target (after the address is adjusted by the subregion offset and alias offset)
    • if a recursive search within a container or alias subregion does not find a match (because of a “hole” in the container’s coverage of its address range), then if this is a container with its own MMIO or RAM backing the search terminates, returning the container itself. Otherwise we continue with the next subregion in priority order
  • if none of the subregions match the address then the search terminates with no match found

总体来说是个遍历算法,MemoryRegion 是嵌套结构,为了确定地址在哪个 MemoryRegion,查询时间为 O(N),所以 MemoryRegion 是建模,运行时会生成 FlatView ,可以直接进行二分搜索,时间效率变为 O(log(N))

FlatView

FlatView contains FlatView that is generated by generate_memory_topology:

address_space_init: use MemoryRegion to init AddressSpace

  • address_space_update_topology
    • memory_region_get_flatview_root
    • generate_memory_topology

FlatView is an array, to speed up access, it’s needed construct tree-like struct: AddressSpaceDispatch.

// include/system/memory.h
struct FlatView {
	// Array of ranges
	FlatRange *ranges;
	unsigned nr;
	
	// Radix tree for quick lookups (page tables)
	struct AddressSpaceDispatch *dispatch;
	// Tree
	MemoryRegion *root;
};
 
// system/memory.c
struct FlatRange {
	// Backing region
	MemoryRegion *mr;
	hwaddr offset_in_region;
	// Corresponding range in flattened space
	AddrRange addr;
};


  • Memory regions can have different priorities
  • Whenever memory regions change (PCI device dynamic found)
    • rebuild flat views
    • rebuild radix tree
    • notify listeners
      • e.g. flush TLBs

AddressSpace

struct AddressSpace {
	MemoryRegion *root;
	struct FlatView *current_map;
	QTAILQ_HEAD(, MemoryListener) listeners;
};

QEMU have two global AddressSpace, and all others are subsets of these;

// system/physmem.c
static MemoryRegion* system_memory;
static MemoryRegion* system_io;
 
AddressSpace address_space_io;  
AddressSpace address_space_memory;
static void memory_map_init(void) {
	// RAM
	memory_region_init(system_memory, nULL, "system", UNIT64_MAX);
	address_space_init(&address_space_memory, system_memory, "memory");
	
	// I/O memory space
	memory_region_init_io(system_io, NULL, &unassigned_io_ops, NULL, "io",  
                      65536);  
	address_space_init(&address_space_io, system_io, "I/O");
}