Skip to main content

chipset/pit/
mod.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4pub 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)] // Ignore `select` since it's not part of the persistent state.
35    #[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    // Static configuration
76    enabled_at_reset: bool,
77
78    // Runtime glue
79    interrupt: Option<LineInterrupt>,
80
81    // Volatile state
82    #[inspect(flatten)]
83    state: TimerState,
84}
85
86#[derive(Copy, Clone, Debug, Inspect, PartialEq, Eq)]
87struct TimerState {
88    ce: u16,         // "counting element", i.e. the counter
89    cr: u16,         // count register, the new value
90    ol: Option<u16>, // the output latch
91    sl: Option<u8>,  // the status latch
92    state: CountState,
93    control: ControlWord,
94    out: bool,       // timer output
95    gate: bool,      // timer input
96    null: bool,      // cr has been set but not copied to ce yet
97    read_high: bool, // read the high counter byte next
98    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    /// Returns true for modes where counting stops when gate is low.
136    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
164/// Subtracts `n` from `ce` with wrap.
165///
166/// If `bcd`, then `ce` is in BCD format. The return value is always in binary
167/// format.
168fn 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
178/// Nanoseconds per PIT tick.
179const 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    /// Sets CE to the given value, wrapped. Stores CE in BCD
214    /// format if the PIT is in BCD mode.
215    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                            // A count-one rate generator reloads every tick without
249                            // lowering OUT.
250                            break;
251                        }
252                        let period = if reload == 0 {
253                            if bcd { 10_000 } else { 0x1_0000 }
254                        } else if reload == 1 {
255                            // Mode 3 strips the odd bit before wrapping through the
256                            // counter range, taking one extra tick to return here.
257                            if bcd { 10_001 } else { 0x1_0001 }
258                        } else {
259                            reload
260                        };
261                        if ticks >= period {
262                            // Skip whole cycles, preserving phase and emitting one
263                            // representative pulse instead of replaying every transition.
264                            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                    // Counts per tick.
274                    let per = match mode {
275                        Mode::TerminalCount
276                        | Mode::HardwareOneShot
277                        | Mode::RateGenerator
278                        | Mode::SoftwareStrobe
279                        | Mode::HardwareStrobe => 1,
280                        Mode::SquareWave => {
281                            // Strip the low bit. This takes an extra tick when
282                            // out is high.
283                            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                        // Terminal state.
295                        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                            // Decrement down to one tick before the terminal state.
318                            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                    // Wait for high to be set before taking any actions.
374                    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    // Returns the number of ticks until the next interrupt will occur.
477    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                // Add an extra count for the reload cycle.
488                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                            // Changing output in ce ticks.
500                            counter_sub(ce, 1, bcd) + 1
501                        }
502                        Mode::RateGenerator => {
503                            if ce == 1 {
504                                // Going high in 1 tick.
505                                1
506                            } else {
507                                // Going low in ce - 1 ticks.
508                                counter_sub(ce, 1, bcd)
509                            }
510                        }
511                        Mode::SquareWave => {
512                            // Inverts in ce / 2 ticks, rounding up if out is high.
513                            (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    // Runtime glue
550    vmtime: VmTimeAccess,
551
552    // Sub-emulators
553    #[inspect(iter_by_index)]
554    timers: [Timer; { PIT_TIMER_RANGE_END - PIT_TIMER_RANGE_START + 1 } as usize],
555
556    // Runtime book-keeping
557    dram_refresh: bool, // just jitters back and forth
558
559    // Volatile state
560    last: VmTime,
561}
562
563impl PitDevice {
564    pub fn new(interrupt: LineInterrupt, vmtime: VmTimeAccess) -> Self {
565        PitDevice {
566            // Timers 1 and 2 are enabled by default. Timer 1's output is hooked
567            // up to the interrupt line.
568            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        // Accumulate an integer number of ticks.
581        //
582        // N.B. if self.last were set to now, then each call to evaluate
583        // would leak a portion of a tick, causing timers to expire late.
584        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        // Request another tick if needed. This is only needed for timer 0 since
596        // that's the only one wired up to an interrupt.
597        if let Some(next) = self.timers[0].state.next_wakeup() {
598            // Delay waking up if the next wakeup is too soon to avoid spinning.
599            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            // Re-register the poll before arming the next wakeup rather than
636            // after so that a very short wakeup will still allow this function
637            // to return, hopefully avoiding livelock.
638            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                // Cycle the DRAM refresh bit every read. PCAT uses this to
665                // validate that DRAM is working, but it's not practical or
666                // useful to make the timing accurate.
667                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), // no unused bits
887                    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}