Virtual Register Management

Relevant source files

The Virtual Register Management system implements the core virtualization layer for APIC registers through the VirtualApicRegs struct. This system allocates and manages a 4KB memory page that contains a complete set of virtualized Local APIC registers, providing the foundation for both xAPIC MMIO and x2APIC MSR access patterns. For information about the device interface that uses this register management, see EmulatedLocalApic Device Interface. For details about address translation that routes to these registers, see Register Address Translation.

Memory Management Architecture

The VirtualApicRegs<H: AxMmHal> struct serves as the primary memory manager for virtualized APIC registers. It allocates a single 4KB physical memory page and maps the LocalAPICRegs structure directly onto this page, providing a complete virtualized APIC register set.

flowchart TD
subgraph subGraph2["Register Layout in Memory"]
    ID["ID (0x20)"]
    VER["VERSION (0x30)"]
    TPR["TPR (0x80)"]
    SVR["SVR (0xF0)"]
    ISR["ISR Arrays (0x100-0x170)"]
    TMR["TMR Arrays (0x180-0x1F0)"]
    IRR["IRR Arrays (0x200-0x270)"]
    LVT["LVT Registers (0x2F0-0x370)"]
    TIMER["Timer Registers (0x380-0x3E0)"]
end
subgraph subGraph1["Physical Memory"]
    PM["4KB PhysFrame"]
    LAR["LocalAPICRegs Structure"]
end
subgraph subGraph0["VirtualApicRegs Structure"]
    VR["VirtualApicRegs<H>"]
    VLP["virtual_lapic: NonNull<LocalAPICRegs>"]
    AP["apic_page: PhysFrame<H>"]
    SL["svr_last: SpuriousInterruptVectorRegisterLocal"]
    LL["lvt_last: LocalVectorTable"]
end

AP --> PM
LAR --> ID
LAR --> IRR
LAR --> ISR
LAR --> LVT
LAR --> SVR
LAR --> TIMER
LAR --> TMR
LAR --> TPR
LAR --> VER
PM --> LAR
VLP --> LAR
VR --> AP
VR --> LL
VR --> SL
VR --> VLP

The allocation process uses PhysFrame::alloc_zero() to obtain a zeroed 4KB page, ensuring clean initial register state. The NonNull<LocalAPICRegs> pointer provides direct access to the register structure while maintaining memory safety.

Sources: src/vlapic.rs(L15 - L40) 

Register Structure and Layout

The LocalAPICRegs structure defines the exact memory layout of the 4KB virtual APIC page, mapping all register offsets according to the Intel APIC specification. This structure uses the tock_registers framework to provide type-safe register access with proper read/write permissions.

flowchart TD
subgraph subGraph4["LocalAPICRegs Memory Map (4KB Page)"]
    subgraph subGraph2["Local Vector Table"]
        ICR_TIMER["ICR_TIMER: ReadWrite<u32> (0x380)"]
        CCR_TIMER["CCR_TIMER: ReadOnly<u32> (0x390)"]
        DCR_TIMER["DCR_TIMER: ReadWrite<u32> (0x3E0)"]
        LVT_CMCI["LVT_CMCI: LvtCmciRegisterMmio (0x2F0)"]
        LVT_TIMER["LVT_TIMER: LvtTimerRegisterMmio (0x320)"]
        LVT_THERMAL["LVT_THERMAL: LvtThermalMonitorRegisterMmio (0x330)"]
        LVT_PMI["LVT_PMI: LvtPerformanceCounterRegisterMmio (0x340)"]
        LVT_LINT0["LVT_LINT0: LvtLint0RegisterMmio (0x350)"]
        LVT_LINT1["LVT_LINT1: LvtLint1RegisterMmio (0x360)"]
        LVT_ERROR["LVT_ERROR: LvtErrorRegisterMmio (0x370)"]
        ISR_REG["ISR: [ReadOnly<u128>; 8] (0x100-0x170)"]
        TMR_REG["TMR: [ReadOnly<u128>; 8] (0x180-0x1F0)"]
        IRR_REG["IRR: [ReadOnly<u128>; 8] (0x200-0x270)"]
        ID_REG["ID: ReadWrite<u32> (0x20)"]
        VERSION_REG["VERSION: ReadOnly<u32> (0x30)"]
        TPR_REG["TPR: ReadWrite<u32> (0x80)"]
        SVR_REG["SVR: SpuriousInterruptVectorRegisterMmio (0xF0)"]
    end
    subgraph subGraph0["Control Registers"]
        ICR_TIMER["ICR_TIMER: ReadWrite<u32> (0x380)"]
        CCR_TIMER["CCR_TIMER: ReadOnly<u32> (0x390)"]
        DCR_TIMER["DCR_TIMER: ReadWrite<u32> (0x3E0)"]
        LVT_CMCI["LVT_CMCI: LvtCmciRegisterMmio (0x2F0)"]
        LVT_TIMER["LVT_TIMER: LvtTimerRegisterMmio (0x320)"]
        LVT_THERMAL["LVT_THERMAL: LvtThermalMonitorRegisterMmio (0x330)"]
        LVT_PMI["LVT_PMI: LvtPerformanceCounterRegisterMmio (0x340)"]
        ISR_REG["ISR: [ReadOnly<u128>; 8] (0x100-0x170)"]
        TMR_REG["TMR: [ReadOnly<u128>; 8] (0x180-0x1F0)"]
        IRR_REG["IRR: [ReadOnly<u128>; 8] (0x200-0x270)"]
        ID_REG["ID: ReadWrite<u32> (0x20)"]
        VERSION_REG["VERSION: ReadOnly<u32> (0x30)"]
        TPR_REG["TPR: ReadWrite<u32> (0x80)"]
        SVR_REG["SVR: SpuriousInterruptVectorRegisterMmio (0xF0)"]
    end
end

The register arrays (ISR, TMR, IRR) each consist of 8 x 128-bit fields that collectively represent 256-bit interrupt state vectors, with each bit corresponding to a specific interrupt vector number.

Sources: src/regs/mod.rs(L13 - L104) 

Register Access Patterns

The VirtualApicRegs implements register access through handle_read() and handle_write() methods that translate ApicRegOffset enum values to direct memory operations on the LocalAPICRegs structure. This provides a unified interface for both xAPIC MMIO and x2APIC MSR access patterns.

sequenceDiagram
    participant AccessClient as "Access Client"
    participant VirtualApicRegs as "VirtualApicRegs"
    participant LocalAPICRegs as "LocalAPICRegs"
    participant LocalCopies as "Local Copies"

    Note over AccessClient,LocalCopies: Register Read Operation
    AccessClient ->> VirtualApicRegs: "handle_read(offset, width, context)"
    VirtualApicRegs ->> VirtualApicRegs: "match offset"
    alt Standard Register
        VirtualApicRegs ->> LocalAPICRegs: "self.regs().REGISTER.get()"
        LocalAPICRegs -->> VirtualApicRegs: "register_value"
    else LVT Register
        VirtualApicRegs ->> LocalCopies: "self.lvt_last.register.get()"
        LocalCopies -->> VirtualApicRegs: "cached_value"
    else Array Register (ISR/TMR/IRR)
        VirtualApicRegs ->> LocalAPICRegs: "self.regs().ARRAY[index].get()"
        LocalAPICRegs -->> VirtualApicRegs: "array_element_value"
    end
    VirtualApicRegs -->> AccessClient: "Ok(value)"
    Note over AccessClient,LocalCopies: Register Write Operation
    AccessClient ->> VirtualApicRegs: "handle_write(offset, width, context)"
    VirtualApicRegs ->> VirtualApicRegs: "match offset and validate"
    VirtualApicRegs -->> AccessClient: "Ok(())"

The read operations access different register types through specific patterns:

  • Standard registers: Direct access via self.regs().REGISTER.get()
  • LVT registers: Cached access via self.lvt_last.register.get()
  • Array registers: Indexed access via self.regs().ARRAY[index].get()

Sources: src/vlapic.rs(L62 - L186) 

Caching Strategy

The VirtualApicRegs maintains local copies of certain registers to support change detection and coherent snapshots. This caching strategy ensures consistent behavior during register modifications and enables tracking of register state changes.

Cached Register TypeField NamePurpose
Spurious Interrupt Vectorsvr_last: SpuriousInterruptVectorRegisterLocalChange detection for SVR modifications
Local Vector Tablelvt_last: LocalVectorTableCoherent snapshot of all LVT registers
flowchart TD
subgraph subGraph3["VirtualApicRegs Caching Architecture"]
    subgraph subGraph2["Access Patterns"]
        READ_SVR["SVR Read Operations"]
        READ_LVT["LVT Read Operations"]
    end
    subgraph subGraph1["Local Copies"]
        CACHE_SVR["svr_last: SpuriousInterruptVectorRegisterLocal"]
        CACHE_LVT["lvt_last: LocalVectorTable"]
    end
    subgraph subGraph0["Hardware Registers"]
        HW_SVR["SVR in LocalAPICRegs"]
        HW_LVT["LVT Registers in LocalAPICRegs"]
    end
end

HW_LVT --> CACHE_LVT
HW_SVR --> CACHE_SVR
READ_LVT --> CACHE_LVT
READ_SVR --> HW_SVR

The cached LVT registers are accessed during read operations instead of the hardware registers, providing stable values during register modifications. The SVR cache enables detection of configuration changes that affect APIC behavior.

Sources: src/vlapic.rs(L21 - L26)  src/vlapic.rs(L37 - L38)  src/vlapic.rs(L131 - L157) 

Memory Management Lifecycle

The VirtualApicRegs implements proper resource management through RAII patterns, automatically deallocating the 4KB page when the instance is dropped.

flowchart TD
subgraph subGraph0["Lifecycle Management"]
    CREATE["VirtualApicRegs::new()"]
    ALLOC["PhysFrame::alloc_zero()"]
    INIT["Initialize cached registers"]
    USE["Runtime register access"]
    DROP["Drop trait implementation"]
    DEALLOC["H::dealloc_frame()"]
end

ALLOC --> INIT
CREATE --> ALLOC
DROP --> DEALLOC
INIT --> USE
USE --> DROP

The initialization process sets default values for cached registers using RESET_SPURIOUS_INTERRUPT_VECTOR for the SVR and LocalVectorTable::default() for the LVT state, ensuring proper initial configuration.

Sources: src/vlapic.rs(L30 - L40)  src/vlapic.rs(L55 - L59)