|
<< Click to Display Table of Contents >> Navigation: ASA-EMulatR Reference Guide > Introduction > Architecture Overview > Chapter 16 – Device Model & DMA > 16.2 MMIO Routing Implementation |
When a guest load or store instruction targets a device register, the access follows a multi-stage routing path:
Guest Instruction (LDQ/STQ at VA)
│
▼
MBox: VA → PA Translation (Ev6SiliconTLB)
│
▼
GuestMemory::findRoute(PA) → RouteTarget::MMIOManager
│
▼
MMIOManager::handleRead/handleWrite(PA, width)
│
▼
Region lookup → RegionDescriptor match
│
▼
PA → BAR-relative offset conversion
│
▼
Device handler: ReadFn(ctx, offset, width) / WriteFn(ctx, offset, value, width)
│
▼
MMIOStatus returned to memory system
The critical routing decision occurs at GuestMemory::findRoute(). When the PA falls within the MMIO aperture (default 0x10_0000_0000 – 0x20_0000_0000, 64 GB), GuestMemory dispatches to MMIOManager rather than SafeMemory. This routing is established at initialization via the PA routing table (see 15.3) and is immutable at runtime.
MMIOManager (mmio_Manager.h, 114 lines) maintains a flat table of registered MMIO regions. Each region is described by a RegionDescriptor:
struct RegionDescriptor {
quint64 basePA; // Start physical address
quint64 sizeBytes; // Region size
quint32 flags; // RegionFlags (access policy)
quint32 deviceUid; // Owning device UID
quint32 hoseId; // PCI hose (interrupt domain)
};
RegionFlags encode per-region access policy: WIDTH_8, WIDTH_16, WIDTH_32, WIDTH_64 (allowed access widths), REQUIRE_NATURAL_ALIGNMENT or ALLOW_UNALIGNED, and HAS_SIDE_EFFECTS. When an access arrives, MMIOManager performs a linear scan of the region table, matches the PA to a region, validates the access width against the region's allowed widths, converts the PA to a BAR-relative offset via offset = PA - region.basePA, and dispatches to the device handler via function pointer.
Handler registration uses raw function pointers for maximum dispatch performance:
typedef quint64 (*ReadFn)(void* ctx, quint64 offset, quint8 width) noexcept;
typedef void (*WriteFn)(void* ctx, quint64 offset, quint64 value, quint8 width) noexcept;
After all regions are registered during initialization, finalize() locks the region table for runtime. No regions can be added or removed after finalization. This guarantees that the dispatch path is read-only during execution, eliminating synchronization overhead on the hot path.
MMIO writes bypass the WriteBufferManager entirely. When MBox detects that the target PA routes to RouteTarget::MMIOManager, it commits the write synchronously through GuestMemory → MMIOManager → device handler. The write is never queued in the CPU's write buffer. This ensures the strong ordering guarantee: every MMIO write is visible to the device at the point of execution, with no deferred commit.
MMIO reads similarly bypass speculation — the pipeline may stall for the duration of the MMIO access, and the result is available immediately upon return from the device handler.
See Also: mmioLib/mmio_Manager.h (~114 lines) – MMIOManager class; 15.3 GuestMemory Region Support and PA Routing ; 15.7 MMIO Access Semantics.