Skip to content

Commit 22447bf

Browse files
committed
improve apic timer
1 parent 3725f10 commit 22447bf

File tree

2 files changed

+98
-49
lines changed

2 files changed

+98
-49
lines changed

src/rust/cpu/apic.rs

Lines changed: 98 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ pub struct Apic {
5353
timer_divider_shift: u32,
5454
timer_initial_count: u32,
5555
timer_current_count: u32,
56-
next_tick: f64,
56+
timer_last_tick: f64,
5757
lvt_timer: u32,
5858
lvt_perf_counter: u32,
5959
lvt_int0: u32,
@@ -79,7 +79,7 @@ static APIC: Mutex<Apic> = Mutex::new(Apic {
7979
timer_divider_shift: 1,
8080
timer_initial_count: 0,
8181
timer_current_count: 0,
82-
next_tick: 0.0,
82+
timer_last_tick: 0.0,
8383
lvt_timer: IOAPIC_CONFIG_MASKED,
8484
lvt_thermal_sensor: IOAPIC_CONFIG_MASKED,
8585
lvt_perf_counter: IOAPIC_CONFIG_MASKED,
@@ -133,7 +133,9 @@ fn read32_internal(apic: &mut Apic, addr: u32) -> u32 {
133133

134134
0xB0 => {
135135
// write-only (written by DSL)
136-
dbg_log!("APIC read eio register");
136+
if APIC_LOG_VERBOSE {
137+
dbg_log!("APIC read eoi register");
138+
}
137139
0
138140
},
139141

@@ -226,8 +228,37 @@ fn read32_internal(apic: &mut Apic, addr: u32) -> u32 {
226228
},
227229

228230
0x390 => {
229-
dbg_log!("read timer current count: {:08x}", apic.timer_current_count);
230-
apic.timer_current_count
231+
let now = unsafe { js::microtick() };
232+
if apic.timer_last_tick > now {
233+
// should only happen after restore_state
234+
dbg_log!("warning: APIC last_tick is in the future, resetting");
235+
apic.timer_last_tick = now;
236+
}
237+
let diff = now - apic.timer_last_tick;
238+
let diff_in_ticks = diff * APIC_TIMER_FREQ / (1 << apic.timer_divider_shift) as f64;
239+
dbg_assert!(diff_in_ticks >= 0.0);
240+
let diff_in_ticks = diff_in_ticks as u64;
241+
let result =
242+
if diff_in_ticks < apic.timer_initial_count as u64 {
243+
apic.timer_initial_count - diff_in_ticks as u32
244+
}
245+
else {
246+
let mode = apic.lvt_timer & APIC_TIMER_MODE_MASK;
247+
if mode == APIC_TIMER_MODE_PERIODIC {
248+
apic.timer_initial_count - (diff_in_ticks % (apic.timer_initial_count as u64 + 1)) as u32
249+
}
250+
else if mode == APIC_TIMER_MODE_ONE_SHOT {
251+
0
252+
}
253+
else {
254+
dbg_assert!(false, "apic unimplemented timer mode: {:x}", mode);
255+
0
256+
}
257+
};
258+
if APIC_LOG_VERBOSE {
259+
dbg_log!("read timer current count: {}", result);
260+
}
261+
result
231262
},
232263

233264
_ => {
@@ -357,6 +388,7 @@ fn write32_internal(apic: &mut Apic, addr: u32, value: u32) {
357388

358389
0x320 => {
359390
dbg_log!("timer lvt: {:08x}", value);
391+
// TODO: check if unmasking and if this should trigger an interrupt immediately
360392
apic.lvt_timer = value;
361393
},
362394

@@ -386,25 +418,33 @@ fn write32_internal(apic: &mut Apic, addr: u32, value: u32) {
386418
},
387419

388420
0x3E0 => {
389-
dbg_log!("timer divider: {:08x}", value);
390421
apic.timer_divider = value;
391422

392423
let divide_shift = (value & 0b11) | ((value & 0b1000) >> 1);
393424
apic.timer_divider_shift = if divide_shift == 0b111 { 0 } else { divide_shift + 1 };
425+
dbg_log!(
426+
"APIC timer divider: {:08x} shift={} tick={:.6}ms",
427+
apic.timer_divider,
428+
apic.timer_divider_shift,
429+
(1 << apic.timer_divider_shift) as f64 / APIC_TIMER_FREQ
430+
);
394431
},
395432

396433
0x380 => {
397434
if APIC_LOG_VERBOSE {
398-
dbg_log!("timer initial: {:08x}", value);
435+
dbg_log!(
436+
"APIC timer initial: {} next_interrupt={:.2}ms",
437+
value,
438+
value as f64 * (1 << apic.timer_divider_shift) as f64 / APIC_TIMER_FREQ,
439+
);
399440
}
400441
apic.timer_initial_count = value;
401442
apic.timer_current_count = value;
402-
403-
apic.next_tick = unsafe { js::microtick() };
443+
apic.timer_last_tick = unsafe { js::microtick() };
404444
},
405445

406446
0x390 => {
407-
dbg_log!("timer current: {:08x}", value);
447+
dbg_log!("write timer current: {:08x}", value);
408448
dbg_assert!(false, "read-only register");
409449
},
410450

@@ -419,58 +459,68 @@ fn write32_internal(apic: &mut Apic, addr: u32, value: u32) {
419459
pub fn apic_timer(now: f64) -> f64 { timer(&mut get_apic(), now) }
420460

421461
fn timer(apic: &mut Apic, now: f64) -> f64 {
422-
if apic.timer_current_count == 0 {
462+
if apic.timer_initial_count == 0 || apic.timer_current_count == 0 {
423463
return 100.0;
424464
}
425465

426-
let freq = APIC_TIMER_FREQ / (1 << apic.timer_divider_shift) as f64;
427-
let steps = ((now - apic.next_tick) * freq).trunc() as u32;
428-
apic.next_tick += steps as f64 / freq;
466+
if apic.timer_last_tick > now {
467+
// should only happen after restore_state
468+
dbg_log!("warning: APIC last_tick is in the future, resetting");
469+
apic.timer_last_tick = now;
470+
}
429471

430-
match apic.timer_current_count.checked_sub(steps) {
431-
Some(t) => apic.timer_current_count = if t == 0 { 1 } else { t },
432-
None => {
433-
let mode = apic.lvt_timer & APIC_TIMER_MODE_MASK;
472+
let diff = now - apic.timer_last_tick;
473+
let diff_in_ticks = diff * APIC_TIMER_FREQ / (1 << apic.timer_divider_shift) as f64;
474+
dbg_assert!(diff_in_ticks >= 0.0);
475+
let diff_in_ticks = diff_in_ticks as u64;
434476

435-
if mode == APIC_TIMER_MODE_PERIODIC {
436-
apic.timer_current_count = apic.timer_initial_count; // XXX
477+
let time_per_interrupt = apic.timer_initial_count as f64 * (1 << apic.timer_divider_shift) as f64 / APIC_TIMER_FREQ;
437478

438-
if apic.timer_current_count == 0 {
439-
apic.timer_current_count += apic.timer_initial_count;
440-
}
441-
dbg_assert!(apic.timer_current_count != 0);
442-
443-
if apic.lvt_timer & IOAPIC_CONFIG_MASKED == 0 {
444-
deliver(
445-
apic,
446-
(apic.lvt_timer & 0xFF) as u8,
447-
IOAPIC_DELIVERY_FIXED,
448-
false,
449-
);
450-
}
479+
if diff_in_ticks >= apic.timer_initial_count as u64 {
480+
let mode = apic.lvt_timer & APIC_TIMER_MODE_MASK;
481+
if mode == APIC_TIMER_MODE_PERIODIC {
482+
if APIC_LOG_VERBOSE {
483+
dbg_log!("APIC timer periodic interrupt");
451484
}
452-
else if mode == APIC_TIMER_MODE_ONE_SHOT {
453-
apic.timer_current_count = 0;
454-
if APIC_LOG_VERBOSE {
455-
dbg_log!("APIC timer one shot end");
456-
}
457485

458-
if apic.lvt_timer & IOAPIC_CONFIG_MASKED == 0 {
459-
deliver(
460-
apic,
461-
(apic.lvt_timer & 0xFF) as u8,
462-
IOAPIC_DELIVERY_FIXED,
463-
false,
464-
);
465-
}
486+
if diff_in_ticks >= 2 * apic.timer_initial_count as u64 {
487+
dbg_log!(
488+
"warning: APIC skipping {} interrupts initial={} ticks={} last_tick={:.1}ms now={:.1}ms d={:.1}ms",
489+
diff_in_ticks / apic.timer_initial_count as u64 - 1,
490+
apic.timer_initial_count,
491+
diff_in_ticks,
492+
apic.timer_last_tick,
493+
now,
494+
diff,
495+
);
496+
apic.timer_last_tick = now;
466497
}
467498
else {
468-
dbg_assert!(false, "apic unimplemented timer mode: {:x}", mode);
499+
apic.timer_last_tick += time_per_interrupt;
500+
dbg_assert!(apic.timer_last_tick <= now);
501+
}
502+
}
503+
else if mode == APIC_TIMER_MODE_ONE_SHOT {
504+
if APIC_LOG_VERBOSE {
505+
dbg_log!("APIC timer one shot end");
469506
}
507+
apic.timer_current_count = 0;
508+
}
509+
else {
510+
dbg_assert!(false, "apic unimplemented timer mode: {:x}", mode);
511+
}
512+
513+
if apic.lvt_timer & IOAPIC_CONFIG_MASKED == 0 {
514+
deliver(
515+
apic,
516+
(apic.lvt_timer & 0xFF) as u8,
517+
IOAPIC_DELIVERY_FIXED,
518+
false,
519+
);
470520
}
471521
}
472522

473-
(apic.timer_current_count as f64 / freq).max(0.0)
523+
apic.timer_last_tick + time_per_interrupt - now
474524
}
475525

476526
pub fn route(

tests/full/run.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1137,7 +1137,6 @@ if(cluster.isPrimary)
11371137
expect_graphical_mode: true,
11381138
expect_graphical_size: [1024, 768],
11391139
expect_mouse_registered: true,
1140-
acpi: false, // XXX: fails with acpi on (needs more precise apic timer impl)
11411140
},
11421141
];
11431142

0 commit comments

Comments
 (0)