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 TypeSize (bytes)Bit RangeUse Case
Byte10..8Character data, byte operations
Word20..1616-bit integers, x86 word operations
Dword40..3232-bit integers, most common size
Qword80..6464-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:

ArchitectureRegister TypeAddress Format
x86_64MSRDirect MSR number
RISC-VCSRDirect CSR address
ARM64System RegisterESR_EL2.ISS format:000000

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)