veridian_kernel/virt/hypervisor/
lapic.rs1use super::{
6 smp::{IpiDeliveryMode, IpiMessage},
7 LAPIC_BASE_ADDR, LAPIC_REGION_SIZE,
8};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
16pub enum LapicTimerMode {
17 #[default]
19 OneShot,
20 Periodic,
22 TscDeadline,
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub struct LvtEntry {
29 pub raw: u32,
31}
32
33impl Default for LvtEntry {
34 fn default() -> Self {
35 Self { raw: 0x0001_0000 } }
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#[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
99pub struct VirtualLapic {
101 pub id: u32,
103 pub tpr: u32,
105 pub svr: u32,
107 pub isr: [u32; 8],
109 pub irr: [u32; 8],
111 pub tmr: [u32; 8],
113 pub lvt_timer: LvtEntry,
115 pub lvt_thermal: LvtEntry,
117 pub lvt_perfmon: LvtEntry,
119 pub lvt_lint0: LvtEntry,
121 pub lvt_lint1: LvtEntry,
123 pub lvt_error: LvtEntry,
125 pub timer_initial_count: u32,
127 pub timer_current_count: u32,
129 pub timer_divide_config: u32,
131 pub tsc_deadline: u64,
133 pub esr: u32,
135 pub icr_low: u32,
137 pub icr_high: u32,
139 pub ldr: u32,
141 pub dfr: u32,
143 pub enabled: bool,
145}
146
147impl VirtualLapic {
148 pub fn new(id: u32) -> Self {
149 Self {
150 id,
151 tpr: 0,
152 svr: 0xFF, 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 pub fn read_register(&self, offset: u32) -> u32 {
177 match offset {
178 LapicRegs::ID => self.id << 24,
179 LapicRegs::VERSION => 0x0005_0014, 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 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 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 self.handle_eoi();
240 }
241 LapicRegs::ESR => {
242 self.esr = 0;
244 }
245 LapicRegs::ICR_LOW => {
246 self.icr_low = value;
247 }
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 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 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 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 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 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 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 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 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 false
389 }
390 }
391 }
392
393 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 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 pub fn is_enabled(&self) -> bool {
437 self.enabled
438 }
439
440 pub fn base_address() -> u64 {
442 LAPIC_BASE_ADDR
443 }
444
445 pub fn region_size() -> u64 {
447 LAPIC_REGION_SIZE
448 }
449}