1pub mod resolver;
5
6use bitfield_struct::bitfield;
7use chipset_device::ChipsetDevice;
8use chipset_device::io::IoError;
9use chipset_device::io::IoResult;
10use chipset_device::pio::PortIoIntercept;
11use chipset_device::poll_device::PollDevice;
12use inspect::Inspect;
13use inspect::InspectMut;
14use open_enum::open_enum;
15use std::ops::RangeInclusive;
16use std::task::Context;
17use std::task::Poll;
18use std::time::Duration;
19use thiserror::Error;
20use vmcore::device_state::ChangeDeviceState;
21use vmcore::line_interrupt::LineInterrupt;
22use vmcore::vmtime::VmTime;
23use vmcore::vmtime::VmTimeAccess;
24
25#[rustfmt::skip]
26#[derive(Inspect)]
27#[bitfield(u8)]
28struct ControlWord {
29 #[bits(1)] bcd: bool,
30 #[inspect(with = "|x| Mode::from(*x)")]
31 #[bits(3)] mode: u8,
32 #[inspect(with = "|x| RwMode(*x)")]
33 #[bits(2)] rw: u8,
34 #[inspect(skip)] #[bits(2)] select: u8,
36}
37
38impl PartialEq for ControlWord {
39 fn eq(&self, other: &Self) -> bool {
40 self.0 == other.0
41 }
42}
43
44impl Eq for ControlWord {}
45
46#[rustfmt::skip]
47#[bitfield(u8)]
48struct StatusWord {
49 #[bits(1)] bcd: bool,
50 #[bits(3)] mode: u8,
51 #[bits(2)] rw: u8,
52 #[bits(1)] null: bool,
53 #[bits(1)] out: bool,
54}
55
56#[bitfield(u8)]
57struct ReadBackCommand {
58 reserved: bool,
59 counter0: bool,
60 counter1: bool,
61 counter2: bool,
62 status: bool,
63 count: bool,
64 #[bits(2)]
65 one: u8,
66}
67
68const PIT_TIMER_RANGE_START: u16 = 0x40;
69const PIT_TIMER_RANGE_END: u16 = 0x42;
70const PIT_CONTROL_REGISTER: u16 = 0x43;
71const PIT_PORT61_REGISTER: u16 = 0x61;
72
73#[derive(Debug, Inspect)]
74struct Timer {
75 enabled_at_reset: bool,
77
78 interrupt: Option<LineInterrupt>,
80
81 #[inspect(flatten)]
83 state: TimerState,
84}
85
86#[derive(Copy, Clone, Debug, Inspect, PartialEq, Eq)]
87struct TimerState {
88 ce: u16, cr: u16, ol: Option<u16>, sl: Option<u8>, state: CountState,
93 control: ControlWord,
94 out: bool, gate: bool, null: bool, read_high: bool, cr_low: Option<u8>,
99}
100
101#[derive(Copy, Clone, Debug, Inspect, PartialEq, Eq)]
102enum CountState {
103 Inactive,
104 WaitingForGate,
105 Reloading,
106 Active,
107 Counting,
108}
109
110#[derive(Debug, Copy, Clone, Inspect, PartialEq, Eq)]
111enum Mode {
112 TerminalCount = 0,
113 HardwareOneShot = 1,
114 RateGenerator = 2,
115 SquareWave = 3,
116 SoftwareStrobe = 4,
117 HardwareStrobe = 5,
118}
119
120impl From<u8> for Mode {
121 fn from(v: u8) -> Self {
122 match v {
123 0 => Mode::TerminalCount,
124 1 => Mode::HardwareOneShot,
125 2 | 6 => Mode::RateGenerator,
126 3 | 7 => Mode::SquareWave,
127 4 => Mode::SoftwareStrobe,
128 5 => Mode::HardwareStrobe,
129 _ => unreachable!(),
130 }
131 }
132}
133
134impl Mode {
135 fn gate_stops_count(&self) -> bool {
137 match self {
138 Mode::TerminalCount | Mode::RateGenerator | Mode::SquareWave | Mode::SoftwareStrobe => {
139 true
140 }
141 Mode::HardwareOneShot | Mode::HardwareStrobe => false,
142 }
143 }
144}
145
146open_enum! {
147 #[derive(Inspect)]
148 #[inspect(debug)]
149 enum RwMode: u8 {
150 LOW = 1,
151 HIGH = 2,
152 LOW_HIGH = 3,
153 }
154}
155
156const fn from_bcd(n: u16) -> u16 {
157 (n & 0xf) + ((n & 0xf0) >> 4) * 10 + ((n & 0xf00) >> 8) * 100 + ((n & 0xf000) >> 12) * 1000
158}
159
160const fn to_bcd(n: u16) -> u16 {
161 (n % 10) + (((n / 10) % 10) << 4) + (((n / 100) % 10) << 8) + (((n / 1000) % 10) << 12)
162}
163
164fn counter_sub(ce: u16, n: u64, bcd: bool) -> u64 {
169 if bcd {
170 let ce = from_bcd(ce);
171 let n = (n % 10000) as u16;
172 (if ce >= n { ce - n } else { 10000 - (n - ce) }) as u64
173 } else {
174 ce.wrapping_sub(n as u16) as u64
175 }
176}
177
178const NANOS_PER_TICK: u64 = 838;
180
181impl Timer {
182 fn new(enabled_at_reset: bool, interrupt: Option<LineInterrupt>) -> Self {
183 Self {
184 enabled_at_reset,
185 interrupt,
186 state: TimerState::new(enabled_at_reset),
187 }
188 }
189
190 fn reset(&mut self) {
191 self.state = TimerState::new(self.enabled_at_reset);
192 self.sync_interrupt();
193 }
194
195 fn sync_interrupt(&mut self) {
196 if let Some(interrupt) = &self.interrupt {
197 interrupt.set_level(self.state.out);
198 }
199 }
200
201 fn set_out(&mut self, state: bool) {
202 if self.state.out != state {
203 self.state.out = state;
204 self.sync_interrupt();
205 }
206 }
207
208 fn load_ce(&mut self) {
209 self.state.ce = self.state.cr;
210 self.state.null = false;
211 }
212
213 fn set_ce(&mut self, ce: u64) {
216 if self.state.control.bcd() {
217 self.state.ce = to_bcd((ce % 10000) as u16);
218 } else {
219 self.state.ce = ce as u16;
220 }
221 }
222
223 fn evaluate(&mut self, mut ticks: u64) {
224 let mode = self.state.op_mode();
225 let bcd = self.state.control.bcd();
226 while ticks > 0 {
227 match self.state.state {
228 CountState::Inactive | CountState::WaitingForGate => break,
229 CountState::Reloading => {
230 ticks -= 1;
231 self.load_ce();
232 self.state.state = CountState::Active;
233 }
234 CountState::Active => {
235 if !self.state.gate && mode.gate_stops_count() {
236 break;
237 }
238 if self.state.ce == self.state.cr
239 && !self.state.null
240 && matches!(mode, Mode::RateGenerator | Mode::SquareWave)
241 {
242 let reload = if bcd {
243 u64::from(from_bcd(self.state.cr))
244 } else {
245 u64::from(self.state.cr)
246 };
247 if reload == 1 && mode == Mode::RateGenerator {
248 break;
251 }
252 let period = if reload == 0 {
253 if bcd { 10_000 } else { 0x1_0000 }
254 } else if reload == 1 {
255 if bcd { 10_001 } else { 0x1_0001 }
258 } else {
259 reload
260 };
261 if ticks >= period {
262 let out = self.state.out;
265 self.set_out(!out);
266 self.set_out(out);
267 ticks %= period;
268 if ticks == 0 {
269 break;
270 }
271 }
272 }
273 let per = match mode {
275 Mode::TerminalCount
276 | Mode::HardwareOneShot
277 | Mode::RateGenerator
278 | Mode::SoftwareStrobe
279 | Mode::HardwareStrobe => 1,
280 Mode::SquareWave => {
281 if self.state.ce & 1 != 0 {
284 self.state.ce &= !1;
285 if self.state.out {
286 ticks -= 1;
287 continue;
288 }
289 }
290 2
291 }
292 };
293 if self.state.ce as u64 == per {
294 self.state.ce = 0;
296 ticks -= 1;
297 match mode {
298 Mode::TerminalCount | Mode::HardwareOneShot => {
299 self.set_out(true);
300 self.state.state = CountState::Counting;
301 }
302 Mode::RateGenerator => {
303 self.set_out(true);
304 self.load_ce();
305 }
306 Mode::SquareWave => {
307 self.set_out(!self.state.out);
308 self.load_ce();
309 }
310 Mode::SoftwareStrobe | Mode::HardwareStrobe => {
311 self.set_out(false);
312 self.state.state = CountState::Counting;
313 }
314 }
315 } else {
316 if ticks >= counter_sub(self.state.ce, per, bcd) / per {
317 ticks -= counter_sub(self.state.ce, per, bcd) / per;
319 self.state.ce = per as u16;
320 if mode == Mode::RateGenerator {
321 self.set_out(false);
322 }
323 } else {
324 self.set_ce(counter_sub(self.state.ce, ticks * per, bcd));
325 ticks = 0;
326 }
327 }
328 }
329 CountState::Counting => {
330 if !self.state.gate && mode.gate_stops_count() {
331 break;
332 }
333 self.set_ce(counter_sub(self.state.ce, ticks, bcd));
334 ticks = 0;
335 self.set_out(true);
336 }
337 }
338 }
339 }
340
341 fn set_control(&mut self, control: ControlWord) {
342 if control.rw() == 0 {
343 self.state.latch_counter();
344 return;
345 }
346
347 self.state.control = control.with_select(0);
348 self.state.ce = 0;
349 self.state.cr = 0;
350 self.state.cr_low = None;
351 self.state.read_high = false;
352 self.state.state = CountState::Inactive;
353 self.state.null = true;
354 self.set_out(match self.state.op_mode() {
355 Mode::TerminalCount => false,
356 Mode::HardwareOneShot => true,
357 Mode::RateGenerator | Mode::SquareWave => true,
358 Mode::SoftwareStrobe => true,
359 Mode::HardwareStrobe => true,
360 });
361 }
362
363 fn write(&mut self, n: u8) {
364 let n = n as u16;
365 match RwMode(self.state.control.rw()) {
366 RwMode::LOW => self.state.cr = n,
367 RwMode::HIGH => self.state.cr = n << 8,
368 RwMode::LOW_HIGH => {
369 if let Some(low) = self.state.cr_low {
370 self.state.cr = (n << 8) | (low as u16);
371 } else {
372 self.state.cr_low = Some(n as u8);
373 return;
375 }
376 }
377 _ => unreachable!(),
378 }
379 self.state.null = true;
380 match self.state.op_mode() {
381 Mode::TerminalCount => {
382 self.state.state = CountState::Reloading;
383 self.set_out(false);
384 }
385 Mode::HardwareOneShot => {
386 self.state.state = CountState::WaitingForGate;
387 }
388 Mode::RateGenerator | Mode::SquareWave => {
389 if self.state.state != CountState::Active {
390 self.state.state = CountState::Reloading;
391 }
392 }
393 Mode::SoftwareStrobe => {
394 self.state.state = CountState::Reloading;
395 }
396 Mode::HardwareStrobe => {
397 self.state.state = CountState::WaitingForGate;
398 }
399 }
400 }
401
402 fn read(&mut self) -> u8 {
403 if let Some(sl) = self.state.sl.take() {
404 return sl;
405 }
406 let value = self.state.ol.unwrap_or(self.state.ce);
407 let value = match RwMode(self.state.control.rw()) {
408 RwMode::LOW => value as u8,
409 RwMode::HIGH => (value >> 8) as u8,
410 RwMode::LOW_HIGH => {
411 self.state.read_high = !self.state.read_high;
412 if self.state.read_high {
413 value as u8
414 } else {
415 (value >> 8) as u8
416 }
417 }
418 _ => unreachable!(),
419 };
420 if !self.state.read_high {
421 self.state.ol = None;
422 }
423 value
424 }
425
426 fn set_gate(&mut self, gate: bool) {
427 match self.state.op_mode() {
428 Mode::TerminalCount => {}
429 Mode::HardwareOneShot => {
430 if !self.state.gate && gate && self.state.state == CountState::WaitingForGate {
431 self.state.state = CountState::Reloading;
432 self.set_out(false);
433 }
434 }
435 Mode::RateGenerator | Mode::SquareWave => {
436 if gate && !self.state.gate {
437 if self.state.state == CountState::Active {
438 self.state.state = CountState::Reloading;
439 }
440 } else if !gate {
441 self.set_out(true);
442 }
443 }
444 Mode::SoftwareStrobe => {}
445 Mode::HardwareStrobe => {
446 if !self.state.gate && gate && self.state.state == CountState::WaitingForGate {
447 self.state.state = CountState::Reloading;
448 }
449 }
450 }
451 self.state.gate = gate;
452 }
453}
454
455impl TimerState {
456 fn new(enabled: bool) -> Self {
457 Self {
458 ce: 0,
459 cr: 0,
460 ol: None,
461 sl: None,
462 control: ControlWord::new().with_rw(1),
463 state: CountState::Inactive,
464 out: false,
465 null: true,
466 gate: enabled,
467 read_high: false,
468 cr_low: None,
469 }
470 }
471
472 fn op_mode(&self) -> Mode {
473 self.control.mode().into()
474 }
475
476 fn next_wakeup(&self) -> Option<u64> {
478 let mode = self.op_mode();
479 let bcd = self.control.bcd();
480 match self.state {
481 CountState::Inactive => None,
482 CountState::WaitingForGate => None,
483 CountState::Reloading | CountState::Active => {
484 if !self.gate && mode.gate_stops_count() {
485 return None;
486 }
487 let (ce, extra) = if self.state == CountState::Reloading {
489 (self.cr, 1)
490 } else {
491 (self.ce, 0)
492 };
493 let v = {
494 match mode {
495 Mode::TerminalCount
496 | Mode::HardwareOneShot
497 | Mode::SoftwareStrobe
498 | Mode::HardwareStrobe => {
499 counter_sub(ce, 1, bcd) + 1
501 }
502 Mode::RateGenerator => {
503 if ce == 1 {
504 1
506 } else {
507 counter_sub(ce, 1, bcd)
509 }
510 }
511 Mode::SquareWave => {
512 (counter_sub(ce, 2, bcd) + 2) / 2 + (self.out && ce & 1 != 0) as u64
514 }
515 }
516 };
517 Some(v + extra)
518 }
519 CountState::Counting => {
520 if self.out || (!self.gate && mode.gate_stops_count()) {
521 None
522 } else {
523 Some(1)
524 }
525 }
526 }
527 }
528
529 fn latch_status(&mut self) {
530 if self.sl.is_none() {
531 self.sl = Some(
532 StatusWord(self.control.0)
533 .with_null(self.null)
534 .with_out(self.out)
535 .into(),
536 );
537 }
538 }
539
540 fn latch_counter(&mut self) {
541 if self.ol.is_none() {
542 self.ol = Some(self.ce);
543 }
544 }
545}
546
547#[derive(InspectMut)]
548pub struct PitDevice {
549 vmtime: VmTimeAccess,
551
552 #[inspect(iter_by_index)]
554 timers: [Timer; { PIT_TIMER_RANGE_END - PIT_TIMER_RANGE_START + 1 } as usize],
555
556 dram_refresh: bool, last: VmTime,
561}
562
563impl PitDevice {
564 pub fn new(interrupt: LineInterrupt, vmtime: VmTimeAccess) -> Self {
565 PitDevice {
566 timers: [
569 Timer::new(true, Some(interrupt)),
570 Timer::new(true, None),
571 Timer::new(false, None),
572 ],
573 last: vmtime.now(),
574 vmtime,
575 dram_refresh: false,
576 }
577 }
578
579 fn evaluate(&mut self, now: VmTime) {
580 let delta = now.checked_sub(self.last).unwrap_or(Duration::ZERO);
585 let ticks = delta.as_nanos() as u64 / NANOS_PER_TICK;
586 self.last = self
587 .last
588 .wrapping_add(Duration::from_nanos(ticks * NANOS_PER_TICK));
589 self.timers[0].evaluate(ticks);
590 self.timers[1].evaluate(ticks);
591 self.timers[2].evaluate(ticks);
592 }
593
594 fn arm_wakeup(&mut self) {
595 if let Some(next) = self.timers[0].state.next_wakeup() {
598 let next = next.max(20);
600 self.vmtime.set_timeout_if_before(
601 self.last
602 .wrapping_add(Duration::from_nanos(next * NANOS_PER_TICK)),
603 );
604 }
605 }
606}
607
608impl ChangeDeviceState for PitDevice {
609 fn start(&mut self) {}
610
611 async fn stop(&mut self) {}
612
613 async fn reset(&mut self) {
614 for timer in &mut self.timers {
615 timer.reset();
616 }
617 self.last = self.vmtime.now();
618 }
619}
620
621impl ChipsetDevice for PitDevice {
622 fn supports_poll_device(&mut self) -> Option<&mut dyn PollDevice> {
623 Some(self)
624 }
625
626 fn supports_pio(&mut self) -> Option<&mut dyn PortIoIntercept> {
627 Some(self)
628 }
629}
630
631impl PollDevice for PitDevice {
632 fn poll_device(&mut self, cx: &mut Context<'_>) {
633 if let Poll::Ready(now) = self.vmtime.poll_timeout(cx) {
634 self.evaluate(now);
635 assert!(self.vmtime.poll_timeout(cx).is_pending());
639 self.arm_wakeup();
640 }
641 }
642}
643
644impl PortIoIntercept for PitDevice {
645 fn io_read(&mut self, io_port: u16, data: &mut [u8]) -> IoResult {
646 if data.len() != 1 {
647 return IoResult::Err(IoError::InvalidAccessSize);
648 }
649
650 self.evaluate(self.vmtime.now());
651 match io_port {
652 PIT_TIMER_RANGE_START..=PIT_TIMER_RANGE_END => {
653 let offset = io_port - PIT_TIMER_RANGE_START;
654 data[0] = self.timers[offset as usize].read();
655 }
656 PIT_CONTROL_REGISTER => {
657 tracelimit::warn_ratelimited!("reading from write-only command register!");
658 data[0] = !0;
659 }
660 PIT_PORT61_REGISTER => {
661 data[0] = ((self.timers[2].state.out as u8) << 5)
662 | ((self.dram_refresh as u8) << 4)
663 | self.timers[2].state.gate as u8;
664 self.dram_refresh = !self.dram_refresh;
668 }
669 _ => return IoResult::Err(IoError::InvalidRegister),
670 }
671
672 self.arm_wakeup();
673 IoResult::Ok
674 }
675
676 fn io_write(&mut self, io_port: u16, data: &[u8]) -> IoResult {
677 let &[b] = data else {
678 return IoResult::Err(IoError::InvalidAccessSize);
679 };
680
681 self.evaluate(self.vmtime.now());
682
683 match io_port {
684 PIT_TIMER_RANGE_START..=PIT_TIMER_RANGE_END => {
685 let offset = io_port - PIT_TIMER_RANGE_START;
686 self.timers[offset as usize].write(b);
687 }
688 PIT_CONTROL_REGISTER => {
689 let control = ControlWord(b);
690 match control.select() {
691 i @ 0..=2 => {
692 tracing::trace!(timer = i, ?control, "control write");
693 self.timers[i as usize].set_control(control);
694 }
695 3 => {
696 let command = ReadBackCommand(b);
697 tracing::trace!(?command, "read back");
698 for (i, select) in
699 [command.counter0(), command.counter1(), command.counter2()]
700 .into_iter()
701 .enumerate()
702 {
703 if select {
704 if !command.status() {
705 self.timers[i].state.latch_status();
706 }
707 if !command.count() {
708 self.timers[i].state.latch_counter();
709 }
710 }
711 }
712 }
713 _ => unreachable!(),
714 }
715 }
716 PIT_PORT61_REGISTER => {
717 self.timers[2].set_gate((b & 1) != 0);
718 }
719 _ => return IoResult::Err(IoError::InvalidRegister),
720 }
721
722 self.arm_wakeup();
723 IoResult::Ok
724 }
725
726 fn get_static_regions(&mut self) -> &[(&str, RangeInclusive<u16>)] {
727 &[
728 ("main", PIT_TIMER_RANGE_START..=PIT_CONTROL_REGISTER),
729 ("port61", PIT_PORT61_REGISTER..=PIT_PORT61_REGISTER),
730 ]
731 }
732}
733
734mod save_restore {
735 use super::*;
736 use vmcore::save_restore::RestoreError;
737 use vmcore::save_restore::SaveError;
738 use vmcore::save_restore::SaveRestore;
739
740 mod state {
741 use mesh::payload::Protobuf;
742 use vmcore::save_restore::SavedStateRoot;
743 use vmcore::vmtime::VmTime;
744
745 #[derive(Protobuf)]
746 #[mesh(package = "chipset.pit")]
747 pub enum SavedCountState {
748 #[mesh(1)]
749 Inactive,
750 #[mesh(2)]
751 WaitingForGate,
752 #[mesh(3)]
753 Reloading,
754 #[mesh(4)]
755 Active,
756 #[mesh(5)]
757 Counting,
758 }
759
760 #[derive(Protobuf)]
761 #[mesh(package = "chipset.pit")]
762 pub struct SavedTimerState {
763 #[mesh(1)]
764 pub ce: u16,
765 #[mesh(2)]
766 pub cr: u16,
767 #[mesh(3)]
768 pub ol: Option<u16>,
769 #[mesh(4)]
770 pub sl: Option<u8>,
771 #[mesh(5)]
772 pub state: SavedCountState,
773 #[mesh(6)]
774 pub control: u8,
775 #[mesh(7)]
776 pub out: bool,
777 #[mesh(8)]
778 pub gate: bool,
779 #[mesh(9)]
780 pub null: bool,
781 #[mesh(10)]
782 pub read_high: bool,
783 #[mesh(11)]
784 pub cr_low: Option<u8>,
785 }
786
787 #[derive(Protobuf, SavedStateRoot)]
788 #[mesh(package = "chipset.pit")]
789 pub struct SavedState {
790 #[mesh(1)]
791 pub timers: [SavedTimerState; 3],
792 #[mesh(2)]
793 pub last: VmTime,
794 }
795 }
796
797 #[derive(Debug, Error)]
798 enum PitDeviceRestoreError {
799 #[error("last tick time is after current time")]
800 InvalidLastTick,
801 }
802
803 impl SaveRestore for PitDevice {
804 type SavedState = state::SavedState;
805
806 fn save(&mut self) -> Result<Self::SavedState, SaveError> {
807 let Self {
808 vmtime: _,
809 timers,
810 dram_refresh: _,
811 last,
812 } = self;
813
814 Ok(state::SavedState {
815 timers: [&timers[0].state, &timers[1].state, &timers[2].state].map(|timer| {
816 let &TimerState {
817 ce,
818 cr,
819 ol,
820 sl,
821 state,
822 control,
823 out,
824 gate,
825 null,
826 read_high,
827 cr_low,
828 } = timer;
829
830 state::SavedTimerState {
831 ce,
832 cr,
833 ol,
834 sl,
835 state: match state {
836 CountState::Inactive => state::SavedCountState::Inactive,
837 CountState::WaitingForGate => state::SavedCountState::WaitingForGate,
838 CountState::Reloading => state::SavedCountState::Reloading,
839 CountState::Active => state::SavedCountState::Active,
840 CountState::Counting => state::SavedCountState::Counting,
841 },
842 control: control.into(),
843 out,
844 gate,
845 null,
846 read_high,
847 cr_low,
848 }
849 }),
850
851 last: *last,
852 })
853 }
854
855 fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
856 let state::SavedState { timers, last } = state;
857
858 for (timer, state) in self.timers.iter_mut().zip(timers) {
859 let state::SavedTimerState {
860 ce,
861 cr,
862 ol,
863 sl,
864 state,
865 control,
866 out,
867 gate,
868 null,
869 read_high,
870 cr_low,
871 } = state;
872
873 timer.state = TimerState {
874 ce,
875 cr,
876 ol,
877 sl,
878 state: match state {
879 state::SavedCountState::Inactive => CountState::Inactive,
880 state::SavedCountState::WaitingForGate => CountState::WaitingForGate,
881 state::SavedCountState::Reloading => CountState::Reloading,
882 state::SavedCountState::Active => CountState::Active,
883 state::SavedCountState::Counting => CountState::Counting,
884 },
885 out,
886 control: ControlWord::from(control), gate,
888 null,
889 read_high,
890 cr_low,
891 };
892
893 timer.sync_interrupt();
894 }
895
896 self.last = last;
897 if last.is_after(self.vmtime.now()) {
898 return Err(RestoreError::InvalidSavedState(
899 PitDeviceRestoreError::InvalidLastTick.into(),
900 ));
901 }
902
903 Ok(())
904 }
905 }
906}
907
908#[cfg(test)]
909mod tests {
910 use super::ControlWord;
911 use super::Mode;
912 use super::RwMode;
913 use super::Timer;
914 use super::to_bcd;
915 use crate::pit::from_bcd;
916 use parking_lot::Mutex;
917 use std::sync::Arc;
918 use test_with_tracing::test;
919 use vmcore::line_interrupt::LineInterrupt;
920 use vmcore::line_interrupt::LineSetTarget;
921
922 #[derive(Default)]
923 struct RecordingTarget(Mutex<Vec<bool>>);
924
925 impl LineSetTarget for RecordingTarget {
926 fn set_irq(&self, _vector: u32, high: bool) {
927 self.0.lock().push(high);
928 }
929 }
930
931 #[test]
932 fn test_bcd_comp() {
933 for i in 0..=9999 {
934 assert_eq!(from_bcd(to_bcd(i)), i, "{i} {}", to_bcd(i));
935 }
936 }
937
938 fn set_timer(timer: &mut Timer, mode: Mode, mut cr: u16, bcd: bool) {
939 timer.set_control(
940 ControlWord::new()
941 .with_mode(mode as u8)
942 .with_rw(RwMode::LOW_HIGH.0)
943 .with_bcd(bcd),
944 );
945 if bcd {
946 cr = to_bcd(cr);
947 }
948 timer.write(cr as u8);
949 timer.write((cr >> 8) as u8);
950 }
951
952 fn set_low_byte_timer(timer: &mut Timer, mode: Mode, cr: u8) {
953 timer.set_control(
954 ControlWord::new()
955 .with_mode(mode as u8)
956 .with_rw(RwMode::LOW.0),
957 );
958 timer.write(cr);
959 }
960
961 fn check_invert(timer: &mut Timer, initial_out: bool, expected_next: u64) {
962 let mode = Mode::from(timer.state.control.mode());
963 assert_eq!(timer.state.out, initial_out, "{mode:?}");
964 let n = timer.state.next_wakeup().unwrap();
965 assert_eq!(n, expected_next, "{mode:?}");
966 for i in 0..n - 1 {
967 timer.evaluate(1);
968 assert_eq!(
969 i + timer.state.next_wakeup().unwrap() + 1,
970 n,
971 "{mode:?}, {i}"
972 );
973 assert_eq!(timer.state.out, initial_out, "{mode:?}, {i}");
974 }
975 timer.evaluate(1);
976 assert_eq!(timer.state.out, !initial_out, "{mode:?}, {n}");
977 }
978
979 fn check_done(timer: &mut Timer) {
980 assert!(timer.state.next_wakeup().is_none());
981 let out = timer.state.out;
982 for _ in 0..65536 {
983 timer.evaluate(1);
984 assert_eq!(timer.state.out, out);
985 }
986 }
987
988 fn test_output(bcd: bool) {
989 let mut timer = Timer::new(true, None);
990 let max = if bcd { 10000 } else { 0x10000 };
991
992 set_timer(&mut timer, Mode::TerminalCount, 0, bcd);
993 check_invert(&mut timer, false, max + 1);
994 check_done(&mut timer);
995
996 set_timer(&mut timer, Mode::HardwareOneShot, 0, bcd);
997 check_done(&mut timer);
998 timer.set_gate(false);
999 timer.set_gate(true);
1000 check_invert(&mut timer, false, max + 1);
1001 check_done(&mut timer);
1002
1003 set_timer(&mut timer, Mode::RateGenerator, 0, bcd);
1004 check_invert(&mut timer, true, max);
1005 check_invert(&mut timer, false, 1);
1006 check_invert(&mut timer, true, max - 1);
1007
1008 set_timer(&mut timer, Mode::SquareWave, 0, bcd);
1009 check_invert(&mut timer, true, max / 2 + 1);
1010 check_invert(&mut timer, false, max / 2);
1011 check_invert(&mut timer, true, max / 2);
1012
1013 set_timer(&mut timer, Mode::SquareWave, 1001, bcd);
1014 check_invert(&mut timer, true, 502);
1015 check_invert(&mut timer, false, 500);
1016 check_invert(&mut timer, true, 501);
1017
1018 set_timer(&mut timer, Mode::SoftwareStrobe, 0, bcd);
1019 check_invert(&mut timer, true, max + 1);
1020 check_invert(&mut timer, false, 1);
1021 check_done(&mut timer);
1022
1023 set_timer(&mut timer, Mode::HardwareStrobe, 0, bcd);
1024 check_done(&mut timer);
1025 timer.set_gate(false);
1026 timer.set_gate(true);
1027 check_invert(&mut timer, true, max + 1);
1028 check_invert(&mut timer, false, 1);
1029 check_done(&mut timer);
1030 }
1031
1032 #[test]
1033 fn test_binary() {
1034 test_output(false);
1035 }
1036
1037 #[test]
1038 fn test_bcd() {
1039 test_output(true);
1040 }
1041
1042 #[test]
1043 fn elapsed_interval_matches_incremental_pit_evaluation() {
1044 for mode in [
1045 Mode::TerminalCount,
1046 Mode::RateGenerator,
1047 Mode::SquareWave,
1048 Mode::SoftwareStrobe,
1049 ] {
1050 let mut elapsed = Timer::new(true, None);
1051 let mut incremental = Timer::new(true, None);
1052 set_timer(&mut elapsed, mode, 100, false);
1053 set_timer(&mut incremental, mode, 100, false);
1054
1055 elapsed.evaluate(350);
1056 for _ in 0..350 {
1057 incremental.evaluate(1);
1058 }
1059
1060 assert_eq!(elapsed.state, incremental.state, "{mode:?}");
1061 }
1062 }
1063
1064 #[test]
1065 fn elapsed_interval_preserves_periodic_phase() {
1066 let mut timer = Timer::new(true, None);
1067 set_timer(&mut timer, Mode::RateGenerator, 100, false);
1068
1069 timer.evaluate(350);
1070
1071 assert_eq!(timer.state.ce, 51);
1072 assert!(timer.state.out);
1073 assert_eq!(timer.state.next_wakeup(), Some(50));
1074 }
1075
1076 #[test]
1077 fn long_elapsed_interval_fast_forwards_periodic_pit() {
1078 for mode in [Mode::RateGenerator, Mode::SquareWave] {
1079 for (count, bcd) in [
1080 (100, false),
1081 (1234, true),
1082 (3, false),
1083 (3, true),
1084 (1001, false),
1085 (1001, true),
1086 (0, false),
1087 (0, true),
1088 (1, false),
1089 (1, true),
1090 ] {
1091 let counter_range = if bcd { 10_000 } else { 0x1_0000 };
1092 let period = if count == 0 {
1093 counter_range
1094 } else if count == 1 && mode == Mode::SquareWave {
1095 counter_range + 1
1096 } else {
1097 u64::from(count)
1098 };
1099
1100 let mut elapsed = Timer::new(true, None);
1101 let mut incremental = Timer::new(true, None);
1102 set_timer(&mut elapsed, mode, count, bcd);
1103 set_timer(&mut incremental, mode, count, bcd);
1104 elapsed.evaluate(1);
1105 incremental.evaluate(1);
1106
1107 let phased_ticks = period * 2 + period / 3;
1108 elapsed.evaluate(phased_ticks);
1109 for _ in 0..phased_ticks {
1110 incremental.evaluate(1);
1111 }
1112 assert_eq!(
1113 elapsed.state, incremental.state,
1114 "{mode:?}, count={count}, bcd={bcd}"
1115 );
1116
1117 let mut elapsed = Timer::new(true, None);
1118 let mut remainder = Timer::new(true, None);
1119 set_timer(&mut elapsed, mode, count, bcd);
1120 set_timer(&mut remainder, mode, count, bcd);
1121 elapsed.evaluate(1);
1122 remainder.evaluate(1);
1123
1124 elapsed.evaluate(u64::MAX);
1125 remainder.evaluate(u64::MAX % period);
1126 assert_eq!(
1127 elapsed.state, remainder.state,
1128 "{mode:?}, count={count}, bcd={bcd}"
1129 );
1130 }
1131 }
1132 }
1133
1134 #[test]
1135 fn periodic_fast_forward_matches_incremental_from_each_phase() {
1136 for mode in [Mode::RateGenerator, Mode::SquareWave] {
1137 for bcd in [false, true] {
1138 for count in [2, 3, 100, 1001] {
1139 let period = u64::from(count);
1140 for phase in [0, 1, period / 2, period / 2 + 1, period - 1] {
1141 let mut initial = Timer::new(true, None);
1142 set_timer(&mut initial, mode, count, bcd);
1143 initial.evaluate(1);
1144 for _ in 0..phase {
1145 initial.evaluate(1);
1146 }
1147
1148 for ticks in [
1149 period - 1,
1150 period,
1151 period + 1,
1152 period * 3 - 1,
1153 period * 3,
1154 period * 3 + 1,
1155 ] {
1156 let mut elapsed = Timer::new(true, None);
1157 let mut incremental = Timer::new(true, None);
1158 elapsed.state = initial.state;
1159 incremental.state = initial.state;
1160
1161 elapsed.evaluate(ticks);
1162 for _ in 0..ticks {
1163 incremental.evaluate(1);
1164 }
1165
1166 assert_eq!(
1167 elapsed.state, incremental.state,
1168 "{mode:?}, count={count}, bcd={bcd}, phase={phase}, ticks={ticks}"
1169 );
1170 }
1171 }
1172 }
1173 }
1174 }
1175 }
1176
1177 #[test]
1178 fn count_one_rate_generator_does_not_emit_irq0_edges() {
1179 for bcd in [false, true] {
1180 let target = Arc::new(RecordingTarget::default());
1181 let interrupt = LineInterrupt::new_with_target("pit", target.clone(), 0);
1182 let mut timer = Timer::new(true, Some(interrupt));
1183 set_timer(&mut timer, Mode::RateGenerator, 1, bcd);
1184 timer.evaluate(1);
1185 target.0.lock().clear();
1186 let active_state = timer.state;
1187
1188 timer.evaluate(u64::MAX);
1189
1190 assert_eq!(timer.state, active_state, "bcd={bcd}");
1191 assert!(target.0.lock().is_empty(), "bcd={bcd}");
1192 }
1193 }
1194
1195 #[test]
1196 fn reprogrammed_periodic_count_loads_before_fast_forward() {
1197 for mode in [Mode::RateGenerator, Mode::SquareWave] {
1198 let mut elapsed = Timer::new(true, None);
1199 let mut incremental = Timer::new(true, None);
1200 set_low_byte_timer(&mut elapsed, mode, 100);
1201 set_low_byte_timer(&mut incremental, mode, 100);
1202 elapsed.evaluate(1);
1203 incremental.evaluate(1);
1204 elapsed.write(100);
1205 incremental.write(100);
1206
1207 elapsed.evaluate(100);
1208 for _ in 0..100 {
1209 incremental.evaluate(1);
1210 }
1211
1212 assert_eq!(elapsed.state, incremental.state, "{mode:?}");
1213 assert!(!elapsed.state.null, "{mode:?}");
1214 }
1215
1216 let target = Arc::new(RecordingTarget::default());
1217 let interrupt = LineInterrupt::new_with_target("pit", target.clone(), 0);
1218 let mut timer = Timer::new(true, Some(interrupt));
1219 set_low_byte_timer(&mut timer, Mode::RateGenerator, 4);
1220 timer.evaluate(4);
1221 timer.write(1);
1222 target.0.lock().clear();
1223 assert!(timer.state.null);
1224 assert!(!timer.state.out);
1225
1226 timer.evaluate(u64::MAX);
1227
1228 assert!(!timer.state.null);
1229 assert!(timer.state.out);
1230 assert_eq!(*target.0.lock(), [true]);
1231 }
1232
1233 #[test]
1234 fn low_gate_pauses_periodic_fast_forward() {
1235 for mode in [Mode::RateGenerator, Mode::SquareWave] {
1236 let target = Arc::new(RecordingTarget::default());
1237 let interrupt = LineInterrupt::new_with_target("pit", target.clone(), 0);
1238 let mut timer = Timer::new(true, Some(interrupt));
1239 set_timer(&mut timer, mode, 100, false);
1240 timer.evaluate(1);
1241 timer.set_gate(false);
1242 target.0.lock().clear();
1243 let paused_state = timer.state;
1244
1245 timer.evaluate(u64::MAX);
1246
1247 assert_eq!(timer.state, paused_state, "{mode:?}");
1248 assert!(target.0.lock().is_empty(), "{mode:?}");
1249 }
1250 }
1251
1252 #[test]
1253 fn long_elapsed_interval_coalesces_periodic_irq0_edges() {
1254 let target = Arc::new(RecordingTarget::default());
1255 let interrupt = LineInterrupt::new_with_target("pit", target.clone(), 0);
1256 let mut timer = Timer::new(true, Some(interrupt));
1257 set_timer(&mut timer, Mode::RateGenerator, 2, false);
1258 timer.evaluate(1);
1259 target.0.lock().clear();
1260
1261 timer.evaluate(u64::MAX - 1);
1262
1263 assert_eq!(*target.0.lock(), [false, true]);
1264 assert!(timer.state.out);
1265 }
1266}