⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

veridian_kernel/virt/hypervisor/
lapic.rs

1//! Virtual LAPIC Emulation
2//!
3//! Full LAPIC register emulation with timer modes for guest VMs.
4
5use super::{
6    smp::{IpiDeliveryMode, IpiMessage},
7    LAPIC_BASE_ADDR, LAPIC_REGION_SIZE,
8};
9
10// ---------------------------------------------------------------------------
11// 5. Virtual LAPIC Emulation
12// ---------------------------------------------------------------------------
13
14/// LAPIC timer mode
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
16pub enum LapicTimerMode {
17    /// One-shot: fires once, then stops
18    #[default]
19    OneShot,
20    /// Periodic: fires repeatedly at interval
21    Periodic,
22    /// TSC-deadline: fires when TSC >= deadline
23    TscDeadline,
24}
25
26/// Local Vector Table (LVT) entry
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub struct LvtEntry {
29    /// Raw 32-bit register value
30    pub raw: u32,
31}
32
33impl Default for LvtEntry {
34    fn default() -> Self {
35        Self { raw: 0x0001_0000 } // Masked by default
36    }
37}
38
39impl LvtEntry {
40    pub fn vector(&self) -> u8 {
41        (self.raw & 0xFF) as u8
42    }
43
44    pub fn delivery_mode(&self) -> u8 {
45        ((self.raw >> 8) & 0x7) as u8
46    }
47
48    pub fn is_masked(&self) -> bool {
49        self.raw & (1 << 16) != 0
50    }
51
52    pub fn trigger_mode(&self) -> bool {
53        self.raw & (1 << 15) != 0
54    }
55
56    pub fn timer_mode(&self) -> LapicTimerMode {
57        match (self.raw >> 17) & 0x3 {
58            0 => LapicTimerMode::OneShot,
59            1 => LapicTimerMode::Periodic,
60            2 => LapicTimerMode::TscDeadline,
61            _ => LapicTimerMode::OneShot,
62        }
63    }
64}
65
66/// Virtual LAPIC register offsets
67#[allow(unused)]
68pub struct LapicRegs;
69
70#[allow(unused)]
71impl LapicRegs {
72    pub const ID: u32 = 0x020;
73    pub const VERSION: u32 = 0x030;
74    pub const TPR: u32 = 0x080;
75    pub const APR: u32 = 0x090;
76    pub const PPR: u32 = 0x0A0;
77    pub const EOI: u32 = 0x0B0;
78    pub const RRD: u32 = 0x0C0;
79    pub const LDR: u32 = 0x0D0;
80    pub const DFR: u32 = 0x0E0;
81    pub const SVR: u32 = 0x0F0;
82    pub const ISR_BASE: u32 = 0x100;
83    pub const TMR_BASE: u32 = 0x180;
84    pub const IRR_BASE: u32 = 0x200;
85    pub const ESR: u32 = 0x280;
86    pub const ICR_LOW: u32 = 0x300;
87    pub const ICR_HIGH: u32 = 0x310;
88    pub const LVT_TIMER: u32 = 0x320;
89    pub const LVT_THERMAL: u32 = 0x330;
90    pub const LVT_PERFMON: u32 = 0x340;
91    pub const LVT_LINT0: u32 = 0x350;
92    pub const LVT_LINT1: u32 = 0x360;
93    pub const LVT_ERROR: u32 = 0x370;
94    pub const TIMER_INITIAL_COUNT: u32 = 0x380;
95    pub const TIMER_CURRENT_COUNT: u32 = 0x390;
96    pub const TIMER_DIVIDE_CONFIG: u32 = 0x3E0;
97}
98
99/// Virtual LAPIC state
100pub struct VirtualLapic {
101    /// LAPIC ID
102    pub id: u32,
103    /// Task Priority Register
104    pub tpr: u32,
105    /// Spurious Interrupt Vector Register
106    pub svr: u32,
107    /// In-Service Register (256 bits = 8 x u32)
108    pub isr: [u32; 8],
109    /// Interrupt Request Register (256 bits = 8 x u32)
110    pub irr: [u32; 8],
111    /// Trigger Mode Register (256 bits = 8 x u32)
112    pub tmr: [u32; 8],
113    /// LVT Timer entry
114    pub lvt_timer: LvtEntry,
115    /// LVT Thermal entry
116    pub lvt_thermal: LvtEntry,
117    /// LVT Performance Monitor entry
118    pub lvt_perfmon: LvtEntry,
119    /// LVT LINT0 entry
120    pub lvt_lint0: LvtEntry,
121    /// LVT LINT1 entry
122    pub lvt_lint1: LvtEntry,
123    /// LVT Error entry
124    pub lvt_error: LvtEntry,
125    /// Timer initial count
126    pub timer_initial_count: u32,
127    /// Timer current count (decrements)
128    pub timer_current_count: u32,
129    /// Timer divide configuration
130    pub timer_divide_config: u32,
131    /// TSC deadline value
132    pub tsc_deadline: u64,
133    /// Error status register
134    pub esr: u32,
135    /// Interrupt Command Register (low 32 bits)
136    pub icr_low: u32,
137    /// Interrupt Command Register (high 32 bits)
138    pub icr_high: u32,
139    /// Logical Destination Register
140    pub ldr: u32,
141    /// Destination Format Register
142    pub dfr: u32,
143    /// Whether the LAPIC is enabled (via SVR bit 8)
144    pub enabled: bool,
145}
146
147impl VirtualLapic {
148    pub fn new(id: u32) -> Self {
149        Self {
150            id,
151            tpr: 0,
152            svr: 0xFF, // Disabled by default (bit 8 = 0)
153            isr: [0; 8],
154            irr: [0; 8],
155            tmr: [0; 8],
156            lvt_timer: LvtEntry::default(),
157            lvt_thermal: LvtEntry::default(),
158            lvt_perfmon: LvtEntry::default(),
159            lvt_lint0: LvtEntry::default(),
160            lvt_lint1: LvtEntry::default(),
161            lvt_error: LvtEntry::default(),
162            timer_initial_count: 0,
163            timer_current_count: 0,
164            timer_divide_config: 0,
165            tsc_deadline: 0,
166            esr: 0,
167            icr_low: 0,
168            icr_high: 0,
169            ldr: 0,
170            dfr: 0xFFFF_FFFF,
171            enabled: false,
172        }
173    }
174
175    /// Handle MMIO read from LAPIC register space
176    pub fn read_register(&self, offset: u32) -> u32 {
177        match offset {
178            LapicRegs::ID => self.id << 24,
179            LapicRegs::VERSION => 0x0005_0014, // version 0x14, max LVT 5
180            LapicRegs::TPR => self.tpr,
181            LapicRegs::PPR => self.compute_ppr(),
182            LapicRegs::LDR => self.ldr,
183            LapicRegs::DFR => self.dfr,
184            LapicRegs::SVR => self.svr,
185            LapicRegs::ESR => self.esr,
186            LapicRegs::ICR_LOW => self.icr_low,
187            LapicRegs::ICR_HIGH => self.icr_high,
188            LapicRegs::LVT_TIMER => self.lvt_timer.raw,
189            LapicRegs::LVT_THERMAL => self.lvt_thermal.raw,
190            LapicRegs::LVT_PERFMON => self.lvt_perfmon.raw,
191            LapicRegs::LVT_LINT0 => self.lvt_lint0.raw,
192            LapicRegs::LVT_LINT1 => self.lvt_lint1.raw,
193            LapicRegs::LVT_ERROR => self.lvt_error.raw,
194            LapicRegs::TIMER_INITIAL_COUNT => self.timer_initial_count,
195            LapicRegs::TIMER_CURRENT_COUNT => self.timer_current_count,
196            LapicRegs::TIMER_DIVIDE_CONFIG => self.timer_divide_config,
197            // ISR/IRR/TMR: 8 registers each at 0x10 intervals
198            off if (LapicRegs::ISR_BASE..LapicRegs::ISR_BASE + 0x80).contains(&off) => {
199                let idx = ((off - LapicRegs::ISR_BASE) / 0x10) as usize;
200                if idx < 8 {
201                    self.isr[idx]
202                } else {
203                    0
204                }
205            }
206            off if (LapicRegs::TMR_BASE..LapicRegs::TMR_BASE + 0x80).contains(&off) => {
207                let idx = ((off - LapicRegs::TMR_BASE) / 0x10) as usize;
208                if idx < 8 {
209                    self.tmr[idx]
210                } else {
211                    0
212                }
213            }
214            off if (LapicRegs::IRR_BASE..LapicRegs::IRR_BASE + 0x80).contains(&off) => {
215                let idx = ((off - LapicRegs::IRR_BASE) / 0x10) as usize;
216                if idx < 8 {
217                    self.irr[idx]
218                } else {
219                    0
220                }
221            }
222            _ => 0,
223        }
224    }
225
226    /// Handle MMIO write to LAPIC register space
227    pub fn write_register(&mut self, offset: u32, value: u32) {
228        match offset {
229            LapicRegs::ID => self.id = value >> 24,
230            LapicRegs::TPR => self.tpr = value & 0xFF,
231            LapicRegs::LDR => self.ldr = value & 0xFF00_0000,
232            LapicRegs::DFR => self.dfr = value | 0x0FFF_FFFF,
233            LapicRegs::SVR => {
234                self.svr = value;
235                self.enabled = value & (1 << 8) != 0;
236            }
237            LapicRegs::EOI => {
238                // End of interrupt: clear highest-priority bit in ISR
239                self.handle_eoi();
240            }
241            LapicRegs::ESR => {
242                // Write clears ESR
243                self.esr = 0;
244            }
245            LapicRegs::ICR_LOW => {
246                self.icr_low = value;
247                // Writing ICR low triggers IPI send
248            }
249            LapicRegs::ICR_HIGH => {
250                self.icr_high = value;
251            }
252            LapicRegs::LVT_TIMER => {
253                self.lvt_timer = LvtEntry { raw: value };
254            }
255            LapicRegs::LVT_THERMAL => {
256                self.lvt_thermal = LvtEntry { raw: value };
257            }
258            LapicRegs::LVT_PERFMON => {
259                self.lvt_perfmon = LvtEntry { raw: value };
260            }
261            LapicRegs::LVT_LINT0 => {
262                self.lvt_lint0 = LvtEntry { raw: value };
263            }
264            LapicRegs::LVT_LINT1 => {
265                self.lvt_lint1 = LvtEntry { raw: value };
266            }
267            LapicRegs::LVT_ERROR => {
268                self.lvt_error = LvtEntry { raw: value };
269            }
270            LapicRegs::TIMER_INITIAL_COUNT => {
271                self.timer_initial_count = value;
272                self.timer_current_count = value;
273            }
274            LapicRegs::TIMER_DIVIDE_CONFIG => {
275                self.timer_divide_config = value & 0xB;
276            }
277            _ => {}
278        }
279    }
280
281    /// Compute the processor priority register
282    fn compute_ppr(&self) -> u32 {
283        let isrv = self.highest_isr_priority();
284        let tpr_class = self.tpr >> 4;
285        let isr_class = isrv >> 4;
286        if tpr_class >= isr_class {
287            self.tpr
288        } else {
289            isrv
290        }
291    }
292
293    /// Find highest-priority bit set in ISR
294    fn highest_isr_priority(&self) -> u32 {
295        for i in (0..8).rev() {
296            if self.isr[i] != 0 {
297                let bit = 31 - self.isr[i].leading_zeros();
298                return (i as u32) * 32 + bit;
299            }
300        }
301        0
302    }
303
304    /// Find highest-priority bit set in IRR
305    fn highest_irr_priority(&self) -> Option<u32> {
306        for i in (0..8).rev() {
307            if self.irr[i] != 0 {
308                let bit = 31 - self.irr[i].leading_zeros();
309                return Some((i as u32) * 32 + bit);
310            }
311        }
312        None
313    }
314
315    /// Handle End-Of-Interrupt: clear highest ISR bit
316    fn handle_eoi(&mut self) {
317        for i in (0..8).rev() {
318            if self.isr[i] != 0 {
319                let bit = 31 - self.isr[i].leading_zeros();
320                self.isr[i] &= !(1 << bit);
321                return;
322            }
323        }
324    }
325
326    /// Accept an interrupt: set IRR bit
327    pub fn accept_interrupt(&mut self, vector: u8) {
328        let idx = (vector / 32) as usize;
329        let bit = (vector % 32) as u32;
330        if idx < 8 {
331            self.irr[idx] |= 1 << bit;
332        }
333    }
334
335    /// Try to deliver next pending interrupt (IRR -> ISR)
336    pub fn deliver_pending_interrupt(&mut self) -> Option<u8> {
337        if !self.enabled {
338            return None;
339        }
340
341        let ppr = self.compute_ppr();
342        let ppr_class = ppr >> 4;
343
344        if let Some(vector) = self.highest_irr_priority() {
345            let vector_class = vector >> 4;
346            if vector_class > ppr_class {
347                // Move from IRR to ISR
348                let idx = (vector / 32) as usize;
349                let bit = vector % 32;
350                self.irr[idx] &= !(1 << bit);
351                self.isr[idx] |= 1 << bit;
352                return Some(vector as u8);
353            }
354        }
355        None
356    }
357
358    /// Tick the LAPIC timer (called periodically by hypervisor)
359    /// Returns true if timer interrupt should fire
360    pub fn tick_timer(&mut self, ticks: u32) -> bool {
361        if self.timer_initial_count == 0 || self.lvt_timer.is_masked() {
362            return false;
363        }
364
365        let mode = self.lvt_timer.timer_mode();
366        match mode {
367            LapicTimerMode::OneShot => {
368                if self.timer_current_count > 0 {
369                    if self.timer_current_count <= ticks {
370                        self.timer_current_count = 0;
371                        return true;
372                    }
373                    self.timer_current_count -= ticks;
374                }
375                false
376            }
377            LapicTimerMode::Periodic => {
378                if self.timer_current_count <= ticks {
379                    self.timer_current_count = self.timer_initial_count;
380                    true
381                } else {
382                    self.timer_current_count -= ticks;
383                    false
384                }
385            }
386            LapicTimerMode::TscDeadline => {
387                // TSC deadline mode handled separately
388                false
389            }
390        }
391    }
392
393    /// Get the timer divide value from config register
394    pub fn timer_divide_value(&self) -> u32 {
395        let bits = ((self.timer_divide_config & 0x8) >> 1) | (self.timer_divide_config & 0x3);
396        match bits {
397            0b000 => 2,
398            0b001 => 4,
399            0b010 => 8,
400            0b011 => 16,
401            0b100 => 32,
402            0b101 => 64,
403            0b110 => 128,
404            0b111 => 1,
405            _ => 1,
406        }
407    }
408
409    /// Extract IPI delivery info from ICR
410    pub fn extract_ipi(&self) -> IpiMessage {
411        let vector = (self.icr_low & 0xFF) as u8;
412        let delivery = match (self.icr_low >> 8) & 0x7 {
413            0 => IpiDeliveryMode::Fixed,
414            1 => IpiDeliveryMode::LowestPriority,
415            4 => IpiDeliveryMode::Nmi,
416            5 => IpiDeliveryMode::Init,
417            6 => IpiDeliveryMode::Sipi,
418            7 => IpiDeliveryMode::ExtInt,
419            _ => IpiDeliveryMode::Fixed,
420        };
421        let level = self.icr_low & (1 << 14) != 0;
422        let trigger = self.icr_low & (1 << 15) != 0;
423        let dest = (self.icr_high >> 24) as u8;
424
425        IpiMessage {
426            source: self.id as u8,
427            destination: dest,
428            delivery_mode: delivery,
429            vector,
430            level,
431            trigger_level: trigger,
432        }
433    }
434
435    /// Check if the LAPIC is software-enabled
436    pub fn is_enabled(&self) -> bool {
437        self.enabled
438    }
439
440    /// Get the LAPIC base address
441    pub fn base_address() -> u64 {
442        LAPIC_BASE_ADDR
443    }
444
445    /// Get the LAPIC region size
446    pub fn region_size() -> u64 {
447        LAPIC_REGION_SIZE
448    }
449}