Memory Access and I/O Operations
Relevant source files
This document covers how memory access and I/O operations are handled within the axvcpu exit system. It details the mechanisms for memory-mapped I/O (MMIO), system register access, I/O port operations, and memory access violations that trigger VM exits. The focus is on the data structures and workflows that enable the hypervisor to intercept and emulate these operations.
For information about the broader exit handling categories and system, see Exit Reasons and Categories. For details on VCPU lifecycle management, see Core VCPU Management.
Access Width Specification
The AccessWidth
enum provides a standardized way to specify the size of memory and I/O operations across different architectures. It supports 8-bit through 64-bit accesses with explicit bit range calculations.
Access Type | Size (bytes) | Bit Range | Use Case |
---|---|---|---|
Byte | 1 | 0..8 | Character data, byte operations |
Word | 2 | 0..16 | 16-bit integers, x86 word operations |
Dword | 4 | 0..32 | 32-bit integers, most common size |
Qword | 8 | 0..64 | 64-bit integers, pointer operations |
flowchart TD AccessWidth["AccessWidth Enum"] Byte["Byte (1 byte)0..8 bits"] Word["Word (2 bytes)0..16 bits"] Dword["Dword (4 bytes)0..32 bits"] Qword["Qword (8 bytes)0..64 bits"] ByteOps["Character I/OByte registers"] WordOps["x86 16-bit operationsLegacy I/O"] DwordOps["32-bit registersMost MMIO operations"] QwordOps["64-bit pointersLarge data transfers"] AccessWidth --> Byte AccessWidth --> Dword AccessWidth --> Qword AccessWidth --> Word Byte --> ByteOps Dword --> DwordOps Qword --> QwordOps Word --> WordOps
The AccessWidth
implementation provides conversion methods between enum values and byte sizes, enabling precise operation handling across architectures.
Sources: src/exit.rs(L6 - L61)
Memory-Mapped I/O (MMIO) Operations
MMIO operations represent the most common form of device interaction in virtualized environments. When a guest attempts to access memory-mapped device registers, the hardware triggers a VM exit that the hypervisor must handle through device emulation.
MMIO Read Operations
MMIO read exits occur when the guest CPU attempts to read from a memory-mapped device register. The exit provides the physical address, access width, and register information needed for emulation.
sequenceDiagram participant GuestOS as "Guest OS" participant GuestCPU as "Guest CPU" participant Hypervisor as "Hypervisor" participant EmulatedDevice as "Emulated Device" GuestOS ->> GuestCPU: "ldr x0, [device_base + offset]" GuestCPU ->> Hypervisor: "MmioRead exit" Note over Hypervisor: "addr: GuestPhysAddr<br>width: AccessWidth<br>reg: usize<br>reg_width: AccessWidth" Hypervisor ->> EmulatedDevice: "Read device register" EmulatedDevice ->> Hypervisor: "Register value" Hypervisor ->> GuestCPU: "Set guest register" GuestCPU ->> GuestOS: "Continue execution"
The MmioRead
variant includes both the memory access width and the target register width, allowing for proper handling of sub-register operations and sign extension.
MMIO Write Operations
MMIO write exits are triggered when the guest writes to memory-mapped device registers. The exit provides the target address, data value, and access width for the hypervisor to emulate the write operation.
flowchart TD MmioWrite["MmioWrite Exit"] addr["addr: GuestPhysAddrDevice register address"] width["width: AccessWidthOperation size"] data["data: u64Value to write"] DeviceEmu["Device Emulation"] RegUpdate["Update Device State"] SideEffects["Trigger Side Effects"] Interrupts["Generate Interrupts"] DeviceEmu --> Interrupts DeviceEmu --> RegUpdate DeviceEmu --> SideEffects MmioWrite --> addr MmioWrite --> data MmioWrite --> width addr --> DeviceEmu data --> DeviceEmu width --> DeviceEmu
Sources: src/exit.rs(L78 - L97)
System Register Access
System register operations provide a unified interface for accessing architecture-specific control and status registers. The implementation handles MSRs (x86), CSRs (RISC-V), and system registers (ARM64) through a common exit mechanism.
Register Address Encoding
Different architectures use distinct addressing schemes for system registers:
Architecture | Register Type | Address Format |
---|---|---|
x86_64 | MSR | Direct MSR number |
RISC-V | CSR | Direct CSR address |
ARM64 | System Register | ESR_EL2.ISS format: |
System Register Read/Write Flow
flowchart TD SysReg["System Register Access"] SysRegRead["SysRegRead"] SysRegWrite["SysRegWrite"] ReadAddr["addr: usizeRegister address"] ReadReg["reg: usizeTarget GPR index"] WriteAddr["addr: usizeRegister address"] WriteValue["value: u64Data to write"] ArchHandler["Architecture-Specific Handler"] x86MSR["x86: MSR Access"] RISCVCSR["RISC-V: CSR Access"] ARMSysReg["ARM64: System Register"] ArchHandler --> ARMSysReg ArchHandler --> RISCVCSR ArchHandler --> x86MSR ReadAddr --> ArchHandler ReadReg --> ArchHandler SysReg --> SysRegRead SysReg --> SysRegWrite SysRegRead --> ReadAddr SysRegRead --> ReadReg SysRegWrite --> WriteAddr SysRegWrite --> WriteValue WriteAddr --> ArchHandler WriteValue --> ArchHandler
Sources: src/exit.rs(L98 - L125)
I/O Port Operations
I/O port operations are specific to x86 architectures and handle legacy port-based device communication. These operations use 16-bit port addresses and support the standard x86 I/O instruction set.
Port I/O Characteristics
- Port Range: 16-bit port numbers (0-65535)
- Register Usage: Fixed to
al
/ax
/eax
registers - Architecture: x86-specific feature
- Access Widths: Byte, Word, Dword operations
flowchart TD IoOps["I/O Port Operations"] IoRead["IoRead"] IoWrite["IoWrite"] ReadPort["port: u16Port number"] ReadWidth["width: AccessWidthData size"] WritePort["port: u16Port number"] WriteWidth["width: AccessWidthData size"] WriteData["data: u64Output value"] PortEmulation["Port Device Emulation"] LegacyDevices["Legacy PC Devices"] DebugPorts["Debug Interfaces"] ControlPorts["System Control"] IoOps --> IoRead IoOps --> IoWrite IoRead --> ReadPort IoRead --> ReadWidth IoWrite --> WriteData IoWrite --> WritePort IoWrite --> WriteWidth PortEmulation --> ControlPorts PortEmulation --> DebugPorts PortEmulation --> LegacyDevices ReadPort --> PortEmulation ReadWidth --> PortEmulation WriteData --> PortEmulation WritePort --> PortEmulation WriteWidth --> PortEmulation
Sources: src/exit.rs(L126 - L145)
Memory Access Violations
Nested page faults occur when guest memory accesses violate the host's memory protection policies. These exits enable the hypervisor to implement memory management features like demand paging, copy-on-write, and access control.
Nested Page Fault Handling
flowchart TD GuestAccess["Guest Memory Access"] PageTableWalk["Hardware Page Table Walk"] ViolationCheck["Access Violation?"] DirectAccess["Direct Memory Access"] NestedPageFault["NestedPageFault Exit"] FaultAddr["addr: GuestPhysAddrFaulting address"] AccessFlags["access_flags: MappingFlagsAttempted operation"] HypervisorHandler["Hypervisor Memory Manager"] DemandPaging["Demand Paging"] COW["Copy-on-Write"] AccessControl["Access Control"] SwapHandling["Swap Management"] AccessFlags --> HypervisorHandler FaultAddr --> HypervisorHandler GuestAccess --> PageTableWalk HypervisorHandler --> AccessControl HypervisorHandler --> COW HypervisorHandler --> DemandPaging HypervisorHandler --> SwapHandling NestedPageFault --> AccessFlags NestedPageFault --> FaultAddr PageTableWalk --> ViolationCheck ViolationCheck --> DirectAccess ViolationCheck --> NestedPageFault
The NestedPageFault
exit provides the faulting guest physical address and the access flags that describe the type of operation that caused the fault (read, write, execute).
Sources: src/exit.rs(L153 - L161)
Integration with Exit Handling System
All memory access and I/O operations are unified under the AxVCpuExitReason
enum, providing a consistent interface for the hypervisor to handle different types of virtualization exits.
Exit Type Classification
flowchart TD AxVCpuExitReason["AxVCpuExitReason"] MemoryOps["Memory Operations"] IOOps["I/O Operations"] SystemOps["System Operations"] MmioRead["MmioRead"] MmioWrite["MmioWrite"] NestedPageFault["NestedPageFault"] IoRead["IoRead (x86)"] IoWrite["IoWrite (x86)"] SysRegRead["SysRegRead"] SysRegWrite["SysRegWrite"] AccessWidth["AccessWidth Support"] ArchSpecific["Architecture-Specific Handling"] AxVCpuExitReason --> IOOps AxVCpuExitReason --> MemoryOps AxVCpuExitReason --> SystemOps IOOps --> IoRead IOOps --> IoWrite IoRead --> AccessWidth IoWrite --> AccessWidth MemoryOps --> MmioRead MemoryOps --> MmioWrite MemoryOps --> NestedPageFault MmioRead --> AccessWidth MmioWrite --> AccessWidth SysRegRead --> ArchSpecific SysRegWrite --> ArchSpecific SystemOps --> SysRegRead SystemOps --> SysRegWrite
This unified approach enables the hypervisor to implement comprehensive device emulation, memory management, and system control through a single exit handling interface.
Sources: src/exit.rs(L68 - L210)