@@ -53,7 +53,7 @@ pub struct Apic {
53
53
timer_divider_shift : u32 ,
54
54
timer_initial_count : u32 ,
55
55
timer_current_count : u32 ,
56
- next_tick : f64 ,
56
+ timer_last_tick : f64 ,
57
57
lvt_timer : u32 ,
58
58
lvt_perf_counter : u32 ,
59
59
lvt_int0 : u32 ,
@@ -79,7 +79,7 @@ static APIC: Mutex<Apic> = Mutex::new(Apic {
79
79
timer_divider_shift : 1 ,
80
80
timer_initial_count : 0 ,
81
81
timer_current_count : 0 ,
82
- next_tick : 0.0 ,
82
+ timer_last_tick : 0.0 ,
83
83
lvt_timer : IOAPIC_CONFIG_MASKED ,
84
84
lvt_thermal_sensor : IOAPIC_CONFIG_MASKED ,
85
85
lvt_perf_counter : IOAPIC_CONFIG_MASKED ,
@@ -133,7 +133,9 @@ fn read32_internal(apic: &mut Apic, addr: u32) -> u32 {
133
133
134
134
0xB0 => {
135
135
// 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
+ }
137
139
0
138
140
} ,
139
141
@@ -226,8 +228,37 @@ fn read32_internal(apic: &mut Apic, addr: u32) -> u32 {
226
228
} ,
227
229
228
230
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
231
262
} ,
232
263
233
264
_ => {
@@ -357,6 +388,7 @@ fn write32_internal(apic: &mut Apic, addr: u32, value: u32) {
357
388
358
389
0x320 => {
359
390
dbg_log ! ( "timer lvt: {:08x}" , value) ;
391
+ // TODO: check if unmasking and if this should trigger an interrupt immediately
360
392
apic. lvt_timer = value;
361
393
} ,
362
394
@@ -386,25 +418,33 @@ fn write32_internal(apic: &mut Apic, addr: u32, value: u32) {
386
418
} ,
387
419
388
420
0x3E0 => {
389
- dbg_log ! ( "timer divider: {:08x}" , value) ;
390
421
apic. timer_divider = value;
391
422
392
423
let divide_shift = ( value & 0b11 ) | ( ( value & 0b1000 ) >> 1 ) ;
393
424
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
+ ) ;
394
431
} ,
395
432
396
433
0x380 => {
397
434
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
+ ) ;
399
440
}
400
441
apic. timer_initial_count = value;
401
442
apic. timer_current_count = value;
402
-
403
- apic. next_tick = unsafe { js:: microtick ( ) } ;
443
+ apic. timer_last_tick = unsafe { js:: microtick ( ) } ;
404
444
} ,
405
445
406
446
0x390 => {
407
- dbg_log ! ( "timer current: {:08x}" , value) ;
447
+ dbg_log ! ( "write timer current: {:08x}" , value) ;
408
448
dbg_assert ! ( false , "read-only register" ) ;
409
449
} ,
410
450
@@ -419,58 +459,68 @@ fn write32_internal(apic: &mut Apic, addr: u32, value: u32) {
419
459
pub fn apic_timer ( now : f64 ) -> f64 { timer ( & mut get_apic ( ) , now) }
420
460
421
461
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 {
423
463
return 100.0 ;
424
464
}
425
465
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
+ }
429
471
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 ;
434
476
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 ;
437
478
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" ) ;
451
484
}
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
- }
457
485
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;
466
497
}
467
498
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" ) ;
469
506
}
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
+ ) ;
470
520
}
471
521
}
472
522
473
- ( apic. timer_current_count as f64 / freq ) . max ( 0.0 )
523
+ apic. timer_last_tick + time_per_interrupt - now
474
524
}
475
525
476
526
pub fn route (
0 commit comments