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:
- Mask Address:
addr.as_usize() & (APIC_MMIO_SIZE - 1)
extracts the offset within the 4KB APIC region - Normalize to 16-byte boundaries:
result >> 4
converts byte offset to register offset (APIC registers are 16-byte aligned) - Convert to enum:
ApicRegOffset::from(offset)
maps the numeric offset to the appropriate enum variant
Address Mapping Examples
Guest Physical Address | Masked Offset | Register Offset | ApicRegOffset Enum |
---|---|---|---|
0xFEE0_0020 | 0x020 | 0x2 | ApicRegOffset::ID |
0xFEE0_0030 | 0x030 | 0x3 | ApicRegOffset::Version |
0xFEE0_0100 | 0x100 | 0x10 | ApicRegOffset::ISR(ISRIndex0) |
0xFEE0_0320 | 0x320 | 0x32 | ApicRegOffset::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:
- Calculate relative offset:
addr.addr() - X2APIC_MSE_REG_BASE
gives the register offset - Convert to enum:
ApicRegOffset::from(offset)
maps the offset to the enum variant
MSR Address Mapping Examples
MSR Address | Relative Offset | ApicRegOffset Enum |
---|---|---|
0x802 | 0x2 | ApicRegOffset::ID |
0x803 | 0x3 | ApicRegOffset::Version |
0x810 | 0x10 | ApicRegOffset::ISR(ISRIndex0) |
0x832 | 0x32 | ApicRegOffset::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:
- Direct mappings: Single offsets like
0x2 => ApicRegOffset::ID
- Range mappings: Consecutive ranges like
0x10..=0x17 => ApicRegOffset::ISR(index)
- 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)