Register Address Translation

Relevant source files

This document describes the address translation mechanism that converts guest memory accesses into internal register identifiers within the x86_vlapic crate. The translation layer serves as a bridge between two different APIC access methods (xAPIC MMIO and x2APIC MSR) and the unified internal register representation using the ApicRegOffset enum.

For information about the overall virtual register management system, see Virtual Register Management. For details about specific register layouts and functionality, see Register System.

Translation Architecture Overview

The address translation system provides a unified interface for accessing APIC registers regardless of whether the guest uses xAPIC MMIO-based access or x2APIC MSR-based access. Both access methods are translated into the same internal ApicRegOffset enum representation.

flowchart TD
subgraph subGraph4["Register Access Handlers"]
    VLAPIC_REGS["VirtualApicRegshandle_read/write methods"]
end
subgraph subGraph3["Unified Register Space"]
    APIC_REG_OFFSET["ApicRegOffset enumsrc/consts.rs:61-114"]
    subgraph subGraph2["Register Categories"]
        CONTROL_REGS["Control RegistersID, Version, TPR, etc."]
        ARRAY_REGS["Array RegistersISR, TMR, IRR"]
        LVT_REGS["LVT RegistersTimer, Thermal, PMC, etc."]
        TIMER_REGS["Timer RegistersInitCount, CurCount, DivConf"]
    end
end
subgraph subGraph1["Translation Functions"]
    XAPIC_FUNC["xapic_mmio_access_reg_offset()src/consts.rs:200-202"]
    X2APIC_FUNC["x2apic_msr_access_reg()src/consts.rs:213-215"]
end
subgraph subGraph0["Guest Access Methods"]
    GUEST_MMIO["Guest xAPIC MMIO AccessGuestPhysAddr0xFEE0_0000-0xFEE0_0FFF"]
    GUEST_MSR["Guest x2APIC MSR AccessSysRegAddr0x800-0x8FF"]
end

APIC_REG_OFFSET --> ARRAY_REGS
APIC_REG_OFFSET --> CONTROL_REGS
APIC_REG_OFFSET --> LVT_REGS
APIC_REG_OFFSET --> TIMER_REGS
ARRAY_REGS --> VLAPIC_REGS
CONTROL_REGS --> VLAPIC_REGS
GUEST_MMIO --> XAPIC_FUNC
GUEST_MSR --> X2APIC_FUNC
LVT_REGS --> VLAPIC_REGS
TIMER_REGS --> VLAPIC_REGS
X2APIC_FUNC --> APIC_REG_OFFSET
XAPIC_FUNC --> APIC_REG_OFFSET

Sources: src/consts.rs(L200 - L202)  src/consts.rs(L213 - L215)  src/consts.rs(L61 - L114)  src/lib.rs(L90)  src/lib.rs(L105)  src/lib.rs(L137)  src/lib.rs(L152) 

xAPIC MMIO Address Translation

The xAPIC mode uses Memory-Mapped I/O (MMIO) for register access. Guest physical addresses in the range 0xFEE0_0000 to 0xFEE0_0FFF are translated to register offsets using the xapic_mmio_access_reg_offset() function.

Translation Process

flowchart TD
subgraph Constants["Constants"]
    APIC_BASE["DEFAULT_APIC_BASE0xFEE0_0000"]
    APIC_SIZE["APIC_MMIO_SIZE0x1000 (4KB)"]
end
START["Guest Physical AddressGuestPhysAddr"]
MASK["Apply MMIO Size Maskaddr & (APIC_MMIO_SIZE - 1)Extract lower 12 bits"]
SHIFT["Right Shift by 4result >> 4Convert to 16-byte aligned offset"]
CONVERT["ApicRegOffset::from()Map offset to enum variant"]
END["ApicRegOffset enum"]

CONVERT --> END
MASK --> SHIFT
SHIFT --> CONVERT
START --> MASK

The translation algorithm:

  1. Mask Address: addr.as_usize() & (APIC_MMIO_SIZE - 1) extracts the offset within the 4KB APIC region
  2. Normalize to 16-byte boundaries: result >> 4 converts byte offset to register offset (APIC registers are 16-byte aligned)
  3. Convert to enum: ApicRegOffset::from(offset) maps the numeric offset to the appropriate enum variant

Address Mapping Examples

Guest Physical AddressMasked OffsetRegister OffsetApicRegOffset Enum
0xFEE0_00200x0200x2ApicRegOffset::ID
0xFEE0_00300x0300x3ApicRegOffset::Version
0xFEE0_01000x1000x10ApicRegOffset::ISR(ISRIndex0)
0xFEE0_03200x3200x32ApicRegOffset::LvtTimer

Sources: src/consts.rs(L192 - L203)  src/consts.rs(L197 - L198)  src/lib.rs(L90)  src/lib.rs(L105) 

x2APIC MSR Address Translation

The x2APIC mode uses Model-Specific Registers (MSRs) for register access. MSR addresses in the range 0x800 to 0x8FF are translated using the x2apic_msr_access_reg() function.

Translation Process

flowchart TD
subgraph Constants["Constants"]
    MSR_BASE["X2APIC_MSE_REG_BASE0x800"]
    MSR_SIZE["X2APIC_MSE_REG_SIZE0x100 (256 registers)"]
end
START["System Register AddressSysRegAddr"]
SUBTRACT["Subtract Base Addressaddr.addr() - X2APIC_MSE_REG_BASEConvert to relative offset"]
CONVERT["ApicRegOffset::from()Map offset to enum variant"]
END["ApicRegOffset enum"]

CONVERT --> END
START --> SUBTRACT
SUBTRACT --> CONVERT

The translation is simpler than xAPIC because x2APIC MSRs directly correspond to register offsets:

  1. Calculate relative offset: addr.addr() - X2APIC_MSE_REG_BASE gives the register offset
  2. Convert to enum: ApicRegOffset::from(offset) maps the offset to the enum variant

MSR Address Mapping Examples

MSR AddressRelative OffsetApicRegOffset Enum
0x8020x2ApicRegOffset::ID
0x8030x3ApicRegOffset::Version
0x8100x10ApicRegOffset::ISR(ISRIndex0)
0x8320x32ApicRegOffset::LvtTimer

Sources: src/consts.rs(L205 - L216)  src/consts.rs(L210 - L211)  src/lib.rs(L137)  src/lib.rs(L152) 

ApicRegOffset Enum Structure

The ApicRegOffset enum serves as the unified internal representation for all APIC registers. It categorizes registers into distinct types and handles special cases like register arrays.

flowchart TD
subgraph subGraph5["Index Enums"]
    ISR_INDEX["ISRIndexISRIndex0-ISRIndex7"]
    TMR_INDEX["TMRIndexTMRIndex0-TMRIndex7"]
    IRR_INDEX["IRRIndexIRRIndex0-IRRIndex7"]
end
subgraph subGraph4["ApicRegOffset Enum Variants"]
    LVT_LINT0["LvtLint0 (0x35)"]
    SIVR_REG["SIVR (0xF)"]
    subgraph subGraph1["Interrupt State Arrays"]
        ISR_ARRAY["ISR(ISRIndex)0x10-0x178 registers"]
        TMR_ARRAY["TMR(TMRIndex)0x18-0x1F8 registers"]
        IRR_ARRAY["IRR(IRRIndex)0x20-0x278 registers"]
    end
    subgraph subGraph3["Timer Control"]
        TIMER_INIT["TimerInitCount (0x38)"]
        TIMER_CUR["TimerCurCount (0x39)"]
        TIMER_DIV["TimerDivConf (0x3E)"]
        LVT_TIMER["LvtTimer (0x32)"]
        LVT_THERMAL["LvtThermal (0x33)"]
        LVT_PMC["LvtPmc (0x34)"]
        ID_REG["ID (0x2)"]
        VER_REG["Version (0x3)"]
        TPR_REG["TPR (0x8)"]
    end
    subgraph subGraph2["Local Vector Table"]
        LVT_LINT1["LvtLint1 (0x36)"]
        LVT_ERR["LvtErr (0x37)"]
        LVT_CMCI["LvtCMCI (0x2F)"]
        subgraph subGraph0["Basic Control Registers"]
            TIMER_INIT["TimerInitCount (0x38)"]
            TIMER_CUR["TimerCurCount (0x39)"]
            TIMER_DIV["TimerDivConf (0x3E)"]
            LVT_TIMER["LvtTimer (0x32)"]
            LVT_THERMAL["LvtThermal (0x33)"]
            LVT_PMC["LvtPmc (0x34)"]
            LVT_LINT0["LvtLint0 (0x35)"]
            ID_REG["ID (0x2)"]
            VER_REG["Version (0x3)"]
            TPR_REG["TPR (0x8)"]
            SIVR_REG["SIVR (0xF)"]
        end
    end
end

IRR_ARRAY --> IRR_INDEX
ISR_ARRAY --> ISR_INDEX
TMR_ARRAY --> TMR_INDEX

Register Array Handling

The enum includes special handling for register arrays (ISR, TMR, IRR) that span multiple consecutive offsets. Each array uses a dedicated index enum generated by the define_index_enum! macro:

  • ISR (In-Service Register): 8 registers at offsets 0x10-0x17
  • TMR (Trigger Mode Register): 8 registers at offsets 0x18-0x1F
  • IRR (Interrupt Request Register): 8 registers at offsets 0x20-0x27

The index calculation uses: IndexType::from(offset - base_offset) where base_offset is the starting offset for each array.

Sources: src/consts.rs(L61 - L114)  src/consts.rs(L129 - L131)  src/consts.rs(L3 - L54)  src/consts.rs(L56 - L58) 

Translation Implementation Details

The core translation logic is implemented in the ApicRegOffset::from() method, which uses pattern matching to map numeric offsets to enum variants.

Pattern Matching Strategy

flowchart TD
START["Numeric Offset (usize)"]
CAST["Cast to u32"]
MATCH["Pattern Match"]
SINGLE["Single Register0x2, 0x3, 0x8, etc."]
RANGE["Range Patterns0x10..=0x170x18..=0x1F0x20..=0x27"]
INVALID["Invalid Offsetpanic!"]
SINGLE_VARIANT["Direct Enum VariantApicRegOffset::ID"]
ARRAY_VARIANT["Array Enum VariantApicRegOffset::ISR(index)"]

CAST --> MATCH
MATCH --> INVALID
MATCH --> RANGE
MATCH --> SINGLE
RANGE --> ARRAY_VARIANT
SINGLE --> SINGLE_VARIANT
START --> CAST

The implementation handles three categories of offsets:

  1. Direct mappings: Single offsets like 0x2 => ApicRegOffset::ID
  2. Range mappings: Consecutive ranges like 0x10..=0x17 => ApicRegOffset::ISR(index)
  3. Invalid offsets: Any unmapped offset triggers a panic

Index Calculation for Arrays

For register arrays, the index is calculated by subtracting the base offset:

// Example for ISR array (offset 0x10-0x17)
0x10..=0x17 => ApicRegOffset::ISR(ISRIndex::from(value - 0x10))

This ensures that offset 0x10 maps to ISRIndex0, offset 0x11 maps to ISRIndex1, and so on.

Sources: src/consts.rs(L116 - L148)  src/consts.rs(L129 - L131)  src/consts.rs(L19 - L31)