devices/
cmos.rs

1// Copyright 2017 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::cmp::min;
6use std::sync::Arc;
7use std::time::Duration;
8use std::time::Instant;
9
10use anyhow::anyhow;
11use anyhow::Context;
12use base::custom_serde::deserialize_seq_to_arr;
13use base::custom_serde::serialize_arr;
14use base::error;
15use base::info;
16use base::Event;
17use base::EventToken;
18use base::Timer;
19use base::TimerTrait;
20use base::Tube;
21use base::WaitContext;
22use base::WorkerThread;
23use chrono::DateTime;
24use chrono::Datelike;
25use chrono::TimeZone;
26use chrono::Timelike;
27use chrono::Utc;
28use metrics::log_metric;
29use metrics::MetricEventType;
30use serde::Deserialize;
31use serde::Serialize;
32use snapshot::AnySnapshot;
33use sync::Mutex;
34use vm_control::VmResponse;
35
36use crate::pci::CrosvmDeviceId;
37use crate::BusAccessInfo;
38use crate::BusDevice;
39use crate::DeviceId;
40use crate::IrqEdgeEvent;
41use crate::Suspendable;
42
43pub const RTC_IRQ: u8 = 8;
44
45const INDEX_MASK: u8 = 0x7f;
46const INDEX_OFFSET: u64 = 0x0;
47const DATA_OFFSET: u64 = 0x1;
48const DATA_LEN: usize = 128;
49
50const RTC_REG_SEC: u8 = 0x0;
51const RTC_REG_ALARM_SEC: u8 = 0x1;
52const RTC_REG_MIN: u8 = 0x2;
53const RTC_REG_ALARM_MIN: u8 = 0x3;
54const RTC_REG_HOUR: u8 = 0x4;
55const RTC_REG_ALARM_HOUR: u8 = 0x5;
56const RTC_REG_WEEK_DAY: u8 = 0x6;
57const RTC_REG_DAY: u8 = 0x7;
58const RTC_REG_MONTH: u8 = 0x8;
59const RTC_REG_YEAR: u8 = 0x9;
60pub const RTC_REG_CENTURY: u8 = 0x32;
61pub const RTC_REG_ALARM_DAY: u8 = 0x33;
62pub const RTC_REG_ALARM_MONTH: u8 = 0x34;
63
64const RTC_REG_B: u8 = 0x0b;
65const RTC_REG_B_UNSUPPORTED: u8 = 0xdd;
66const RTC_REG_B_24_HOUR_MODE: u8 = 0x02;
67const RTC_REG_B_ALARM_ENABLE: u8 = 0x20;
68
69const RTC_REG_C: u8 = 0x0c;
70const RTC_REG_C_IRQF: u8 = 0x80;
71const RTC_REG_C_AF: u8 = 0x20;
72
73const RTC_REG_D: u8 = 0x0d;
74const RTC_REG_D_VRT: u8 = 0x80; // RAM and time valid
75
76pub type CmosNowFn = fn() -> DateTime<Utc>;
77
78// Alarm state shared between Cmos and the alarm worker thread.
79struct AlarmState {
80    alarm: Timer,
81    vm_control: Tube,
82    irq: IrqEdgeEvent,
83    armed_time: Instant,
84    clear_evt: Option<Event>,
85}
86
87impl AlarmState {
88    fn trigger_rtc_interrupt(&self) -> anyhow::Result<Event> {
89        self.irq.trigger().context("failed to trigger irq")?;
90
91        let elapsed = self.armed_time.elapsed().as_millis();
92        log_metric(
93            MetricEventType::RtcWakeup,
94            elapsed.try_into().unwrap_or(i64::MAX),
95        );
96
97        let msg = vm_control::VmRequest::Rtc {
98            clear_evt: Event::new().context("failed to create clear event")?,
99        };
100
101        // The Linux kernel expects wakeups to come via ACPI when ACPI is enabled. There's
102        // no real way to determine that here, so just send this unconditionally.
103        self.vm_control.send(&msg).context("send failed")?;
104
105        let vm_control::VmRequest::Rtc { clear_evt } = msg else {
106            unreachable!("message type failure");
107        };
108
109        match self.vm_control.recv().context("recv failed")? {
110            VmResponse::Ok => Ok(clear_evt),
111            resp => Err(anyhow!("unexpected rtc response: {:?}", resp)),
112        }
113    }
114}
115
116/// A CMOS/RTC device commonly seen on x86 I/O port 0x70/0x71.
117#[derive(Serialize)]
118pub struct Cmos {
119    index: u8,
120    #[serde(serialize_with = "serialize_arr")]
121    data: [u8; DATA_LEN],
122    #[serde(skip_serializing)] // skip serializing time function.
123    now_fn: CmosNowFn,
124    // alarm_time is re-loaded from data on deserialization, so there's
125    // no need to explicitly serialize it.
126    #[serde(skip_serializing)]
127    alarm_time: Option<DateTime<Utc>>,
128    // alarm_state fields are either constant across snapshotting or
129    // reloaded from |data| on restore, so no need to serialize.
130    #[serde(skip_serializing)]
131    alarm_state: Arc<Mutex<AlarmState>>,
132    #[serde(skip_serializing)] // skip serializing the worker thread
133    worker: Option<WorkerThread<()>>,
134}
135
136impl Cmos {
137    /// Constructs a CMOS/RTC device with initial data.
138    /// `mem_below_4g` is the size of memory in bytes below the 32-bit gap.
139    /// `mem_above_4g` is the size of memory in bytes above the 32-bit gap.
140    /// `now_fn` is a function that returns the current date and time.
141    pub fn new(
142        mem_below_4g: u64,
143        mem_above_4g: u64,
144        now_fn: CmosNowFn,
145        vm_control: Tube,
146        irq: IrqEdgeEvent,
147    ) -> anyhow::Result<Cmos> {
148        let mut data = [0u8; DATA_LEN];
149
150        data[0x0B] = RTC_REG_B_24_HOUR_MODE; // Status Register B: 24-hour mode
151
152        // Extended memory from 16 MB to 4 GB in units of 64 KB
153        let ext_mem = min(
154            0xFFFF,
155            mem_below_4g.saturating_sub(16 * 1024 * 1024) / (64 * 1024),
156        );
157        data[0x34] = ext_mem as u8;
158        data[0x35] = (ext_mem >> 8) as u8;
159
160        // High memory (> 4GB) in units of 64 KB
161        let high_mem = min(0xFFFFFF, mem_above_4g / (64 * 1024));
162        data[0x5b] = high_mem as u8;
163        data[0x5c] = (high_mem >> 8) as u8;
164        data[0x5d] = (high_mem >> 16) as u8;
165
166        Ok(Cmos {
167            index: 0,
168            data,
169            now_fn,
170            alarm_time: None,
171            alarm_state: Arc::new(Mutex::new(AlarmState {
172                alarm: Timer::new().context("cmos timer")?,
173                irq,
174                vm_control,
175                // Not actually armed, but simpler than wrapping with an Option.
176                armed_time: Instant::now(),
177                clear_evt: None,
178            })),
179            worker: None,
180        })
181    }
182
183    fn spawn_worker(&mut self, alarm_state: Arc<Mutex<AlarmState>>) {
184        self.worker = Some(WorkerThread::start("CMOS_alarm", move |kill_evt| {
185            if let Err(e) = run_cmos_worker(alarm_state, kill_evt) {
186                error!("Failed to spawn worker {:?}", e);
187            }
188        }));
189    }
190
191    fn set_alarm(&mut self) {
192        let mut state = self.alarm_state.lock();
193        if self.data[RTC_REG_B as usize] & RTC_REG_B_ALARM_ENABLE != 0 {
194            let now = (self.now_fn)();
195            let target = alarm_from_registers(now.year(), &self.data).and_then(|this_year| {
196                // There is no year register for the alarm. If the alarm target has
197                // already passed this year, then the next time it will occur is next
198                // year.
199                //
200                // Note that there is something of a race condition here. If |now|
201                // advances while the driver is configuring the alarm, then an alarm that
202                // should only be one second in the future could become one year in the
203                // future. Unfortunately there isn't anything in the rtc-cmos hardware
204                // specification that lets us handle this race condition in the device, so
205                // we just have to rely on the driver to deal with it.
206                if this_year < now {
207                    alarm_from_registers(now.year() + 1, &self.data)
208                } else {
209                    Some(this_year)
210                }
211            });
212            if let Some(target) = target {
213                if Some(target) != self.alarm_time {
214                    self.alarm_time = Some(target);
215                    state.armed_time = Instant::now();
216
217                    let duration = target
218                        .signed_duration_since(now)
219                        .to_std()
220                        .unwrap_or(Duration::new(0, 0));
221                    if let Err(e) = state.alarm.reset_oneshot(duration) {
222                        error!("Failed to set alarm {:?}", e);
223                    }
224                }
225            }
226        } else if self.alarm_time.take().is_some() {
227            if let Err(e) = state.alarm.clear() {
228                error!("Failed to clear alarm {:?}", e);
229            }
230            if let Some(clear_evt) = state.clear_evt.take() {
231                if let Err(e) = clear_evt.signal() {
232                    error!("failed to clear rtc pm signal {:?}", e);
233                }
234            }
235        }
236
237        let needs_worker = self.alarm_time.is_some();
238        drop(state);
239
240        if needs_worker && self.worker.is_none() {
241            self.spawn_worker(self.alarm_state.clone());
242        }
243    }
244}
245
246fn run_cmos_worker(alarm_state: Arc<Mutex<AlarmState>>, kill_evt: Event) -> anyhow::Result<()> {
247    #[derive(EventToken)]
248    enum Token {
249        Alarm,
250        Kill,
251    }
252
253    let wait_ctx: WaitContext<Token> = WaitContext::build_with(&[
254        (&alarm_state.lock().alarm, Token::Alarm),
255        (&kill_evt, Token::Kill),
256    ])
257    .context("worker context failed")?;
258
259    loop {
260        let events = wait_ctx.wait().context("wait failed")?;
261        let mut state = alarm_state.lock();
262        for event in events.iter().filter(|e| e.is_readable) {
263            match event.token {
264                Token::Alarm => {
265                    if state.alarm.mark_waited().context("timer ack failed")? {
266                        continue;
267                    }
268
269                    match state.trigger_rtc_interrupt() {
270                        Ok(clear_evt) => state.clear_evt = Some(clear_evt),
271                        Err(e) => error!("Failed to send rtc {:?}", e),
272                    }
273                }
274                Token::Kill => return Ok(()),
275            }
276        }
277    }
278}
279
280fn from_bcd(v: u8) -> Option<u32> {
281    let ones = (v & 0xf) as u32;
282    let tens = (v >> 4) as u32;
283    if ones < 10 && tens < 10 {
284        Some(10 * tens + ones)
285    } else {
286        None
287    }
288}
289
290fn alarm_from_registers(year: i32, data: &[u8; DATA_LEN]) -> Option<DateTime<Utc>> {
291    Utc.with_ymd_and_hms(
292        year,
293        from_bcd(data[RTC_REG_ALARM_MONTH as usize])?,
294        from_bcd(data[RTC_REG_ALARM_DAY as usize])?,
295        from_bcd(data[RTC_REG_ALARM_HOUR as usize])?,
296        from_bcd(data[RTC_REG_ALARM_MIN as usize])?,
297        from_bcd(data[RTC_REG_ALARM_SEC as usize])?,
298    )
299    .single()
300}
301
302impl BusDevice for Cmos {
303    fn device_id(&self) -> DeviceId {
304        CrosvmDeviceId::Cmos.into()
305    }
306
307    fn debug_label(&self) -> String {
308        "cmos".to_owned()
309    }
310
311    fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
312        if data.len() != 1 {
313            return;
314        }
315
316        match info.offset {
317            INDEX_OFFSET => self.index = data[0] & INDEX_MASK,
318            DATA_OFFSET => {
319                let mut data = data[0];
320                if self.index == RTC_REG_B {
321                    // The features which we don't support are:
322                    //   0x80 (SET)  - disable clock updates (i.e. let guest configure the clock)
323                    //   0x40 (PIE)  - enable periodic interrupts
324                    //   0x10 (IUE)  - enable interrupts after clock updates
325                    //   0x08 (SQWE) - enable square wave generation
326                    //   0x04 (DM)   - use binary data format (instead of BCD)
327                    //   0x01 (DSE)  - control daylight savings (we just do what the host does)
328                    if data & RTC_REG_B_UNSUPPORTED != 0 {
329                        info!(
330                            "Ignoring unsupported bits: {:x}",
331                            data & RTC_REG_B_UNSUPPORTED
332                        );
333                        data &= !RTC_REG_B_UNSUPPORTED;
334                    }
335                    if data & RTC_REG_B_24_HOUR_MODE == 0 {
336                        info!("12-hour mode unsupported");
337                        data |= RTC_REG_B_24_HOUR_MODE;
338                    }
339                }
340
341                self.data[self.index as usize] = data;
342
343                if self.index == RTC_REG_B {
344                    self.set_alarm();
345                }
346            }
347            o => panic!("bad write offset on CMOS device: {o}"),
348        }
349    }
350
351    fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
352        fn to_bcd(v: u8) -> u8 {
353            assert!(v < 100);
354            ((v / 10) << 4) | (v % 10)
355        }
356
357        if data.len() != 1 {
358            return;
359        }
360
361        data[0] = match info.offset {
362            INDEX_OFFSET => self.index,
363            DATA_OFFSET => {
364                let now = (self.now_fn)();
365                let seconds = now.second(); // 0..=59
366                let minutes = now.minute(); // 0..=59
367                let hours = now.hour(); // 0..=23 (24-hour mode only)
368                let week_day = now.weekday().number_from_sunday(); // 1 (Sun) ..= 7 (Sat)
369                let day = now.day(); // 1..=31
370                let month = now.month(); // 1..=12
371                let year = now.year();
372                match self.index {
373                    RTC_REG_SEC => to_bcd(seconds as u8),
374                    RTC_REG_MIN => to_bcd(minutes as u8),
375                    RTC_REG_HOUR => to_bcd(hours as u8),
376                    RTC_REG_WEEK_DAY => to_bcd(week_day as u8),
377                    RTC_REG_DAY => to_bcd(day as u8),
378                    RTC_REG_MONTH => to_bcd(month as u8),
379                    RTC_REG_YEAR => to_bcd((year % 100) as u8),
380                    RTC_REG_CENTURY => to_bcd((year / 100) as u8),
381                    RTC_REG_C => {
382                        if self.alarm_time.is_some_and(|alarm_time| alarm_time <= now) {
383                            // Reading from RTC_REG_C resets interrupts, so clear the
384                            // status bits. The IrqEdgeEvent is reset automatically.
385                            self.alarm_time.take();
386                            RTC_REG_C_IRQF | RTC_REG_C_AF
387                        } else {
388                            0
389                        }
390                    }
391                    RTC_REG_D => RTC_REG_D_VRT,
392                    _ => {
393                        // self.index is always guaranteed to be in range via INDEX_MASK.
394                        self.data[(self.index & INDEX_MASK) as usize]
395                    }
396                }
397            }
398            o => panic!("bad read offset on CMOS device: {o}"),
399        }
400    }
401}
402
403impl Suspendable for Cmos {
404    fn snapshot(&mut self) -> anyhow::Result<AnySnapshot> {
405        AnySnapshot::to_any(self).context("failed to serialize Cmos")
406    }
407
408    fn restore(&mut self, data: AnySnapshot) -> anyhow::Result<()> {
409        #[derive(Deserialize)]
410        struct CmosIndex {
411            index: u8,
412            #[serde(deserialize_with = "deserialize_seq_to_arr")]
413            data: [u8; DATA_LEN],
414        }
415
416        let deser: CmosIndex = AnySnapshot::from_any(data).context("failed to deserialize Cmos")?;
417        self.index = deser.index;
418        self.data = deser.data;
419        self.set_alarm();
420
421        Ok(())
422    }
423
424    fn sleep(&mut self) -> anyhow::Result<()> {
425        if let Some(worker) = self.worker.take() {
426            worker.stop();
427        }
428        Ok(())
429    }
430
431    fn wake(&mut self) -> anyhow::Result<()> {
432        self.spawn_worker(self.alarm_state.clone());
433        Ok(())
434    }
435}
436
437#[cfg(test)]
438mod tests {
439    use super::*;
440    use crate::suspendable_tests;
441
442    fn read_reg(cmos: &mut Cmos, reg: u8) -> u8 {
443        // Write register number to INDEX_OFFSET (0).
444        cmos.write(
445            BusAccessInfo {
446                offset: 0,
447                address: 0x70,
448                id: 0,
449            },
450            &[reg],
451        );
452
453        // Read register value back from DATA_OFFSET (1).
454
455        let mut data = [0u8];
456        cmos.read(
457            BusAccessInfo {
458                offset: 1,
459                address: 0x71,
460                id: 0,
461            },
462            &mut data,
463        );
464        data[0]
465    }
466
467    fn write_reg(cmos: &mut Cmos, reg: u8, val: u8) {
468        // Write register number to INDEX_OFFSET (0).
469        cmos.write(
470            BusAccessInfo {
471                offset: 0,
472                address: 0x70,
473                id: 0,
474            },
475            &[reg],
476        );
477
478        // Write register value to DATA_OFFSET (1).
479
480        let data = [val];
481        cmos.write(
482            BusAccessInfo {
483                offset: 1,
484                address: 0x71,
485                id: 0,
486            },
487            &data,
488        );
489    }
490
491    fn timestamp_to_datetime(timestamp: i64) -> DateTime<Utc> {
492        DateTime::from_timestamp(timestamp, 0).unwrap()
493    }
494
495    fn test_now_party_like_its_1999() -> DateTime<Utc> {
496        // 1999-12-31T23:59:59+00:00
497        timestamp_to_datetime(946684799)
498    }
499
500    fn test_now_y2k_compliant() -> DateTime<Utc> {
501        // 2000-01-01T00:00:00+00:00
502        timestamp_to_datetime(946684800)
503    }
504
505    fn test_now_2016_before_leap_second() -> DateTime<Utc> {
506        // 2016-12-31T23:59:59+00:00
507        timestamp_to_datetime(1483228799)
508    }
509
510    fn test_now_2017_after_leap_second() -> DateTime<Utc> {
511        // 2017-01-01T00:00:00+00:00
512        timestamp_to_datetime(1483228800)
513    }
514
515    fn new_cmos_for_test(now_fn: CmosNowFn) -> Cmos {
516        let irq = IrqEdgeEvent::new().unwrap();
517        Cmos::new(1024, 0, now_fn, Tube::pair().unwrap().0, irq).unwrap()
518    }
519
520    #[test]
521    fn cmos_write_index() {
522        let mut cmos = new_cmos_for_test(test_now_party_like_its_1999);
523        // Write index.
524        cmos.write(
525            BusAccessInfo {
526                offset: 0,
527                address: 0x71,
528                id: 0,
529            },
530            &[0x41],
531        );
532        assert_eq!(cmos.index, 0x41);
533    }
534
535    #[test]
536    fn cmos_write_data() {
537        let mut cmos = new_cmos_for_test(test_now_party_like_its_1999);
538        // Write data 0x01 at index 0x41.
539        cmos.write(
540            BusAccessInfo {
541                offset: 0,
542                address: 0x71,
543                id: 0,
544            },
545            &[0x41],
546        );
547        cmos.write(
548            BusAccessInfo {
549                offset: 1,
550                address: 0x71,
551                id: 0,
552            },
553            &[0x01],
554        );
555        assert_eq!(cmos.data[0x41], 0x01);
556    }
557
558    fn modify_device(cmos: &mut Cmos) {
559        let info_index = BusAccessInfo {
560            offset: 0,
561            address: 0x71,
562            id: 0,
563        };
564
565        let info_data = BusAccessInfo {
566            offset: 1,
567            address: 0x71,
568            id: 0,
569        };
570        // change index to 0x42.
571        cmos.write(info_index, &[0x42]);
572        cmos.write(info_data, &[0x01]);
573    }
574
575    #[test]
576    fn cmos_date_time_1999() {
577        let mut cmos = new_cmos_for_test(test_now_party_like_its_1999);
578        assert_eq!(read_reg(&mut cmos, 0x00), 0x59); // seconds
579        assert_eq!(read_reg(&mut cmos, 0x02), 0x59); // minutes
580        assert_eq!(read_reg(&mut cmos, 0x04), 0x23); // hours
581        assert_eq!(read_reg(&mut cmos, 0x06), 0x06); // day of week
582        assert_eq!(read_reg(&mut cmos, 0x07), 0x31); // day of month
583        assert_eq!(read_reg(&mut cmos, 0x08), 0x12); // month
584        assert_eq!(read_reg(&mut cmos, 0x09), 0x99); // year
585        assert_eq!(read_reg(&mut cmos, 0x32), 0x19); // century
586    }
587
588    #[test]
589    fn cmos_date_time_2000() {
590        let mut cmos = new_cmos_for_test(test_now_y2k_compliant);
591        assert_eq!(read_reg(&mut cmos, 0x00), 0x00); // seconds
592        assert_eq!(read_reg(&mut cmos, 0x02), 0x00); // minutes
593        assert_eq!(read_reg(&mut cmos, 0x04), 0x00); // hours
594        assert_eq!(read_reg(&mut cmos, 0x06), 0x07); // day of week
595        assert_eq!(read_reg(&mut cmos, 0x07), 0x01); // day of month
596        assert_eq!(read_reg(&mut cmos, 0x08), 0x01); // month
597        assert_eq!(read_reg(&mut cmos, 0x09), 0x00); // year
598        assert_eq!(read_reg(&mut cmos, 0x32), 0x20); // century
599    }
600
601    #[test]
602    fn cmos_date_time_before_leap_second() {
603        let mut cmos = new_cmos_for_test(test_now_2016_before_leap_second);
604        assert_eq!(read_reg(&mut cmos, 0x00), 0x59); // seconds
605        assert_eq!(read_reg(&mut cmos, 0x02), 0x59); // minutes
606        assert_eq!(read_reg(&mut cmos, 0x04), 0x23); // hours
607        assert_eq!(read_reg(&mut cmos, 0x06), 0x07); // day of week
608        assert_eq!(read_reg(&mut cmos, 0x07), 0x31); // day of month
609        assert_eq!(read_reg(&mut cmos, 0x08), 0x12); // month
610        assert_eq!(read_reg(&mut cmos, 0x09), 0x16); // year
611        assert_eq!(read_reg(&mut cmos, 0x32), 0x20); // century
612    }
613
614    #[test]
615    fn cmos_date_time_after_leap_second() {
616        let mut cmos = new_cmos_for_test(test_now_2017_after_leap_second);
617        assert_eq!(read_reg(&mut cmos, 0x00), 0x00); // seconds
618        assert_eq!(read_reg(&mut cmos, 0x02), 0x00); // minutes
619        assert_eq!(read_reg(&mut cmos, 0x04), 0x00); // hours
620        assert_eq!(read_reg(&mut cmos, 0x06), 0x01); // day of week
621        assert_eq!(read_reg(&mut cmos, 0x07), 0x01); // day of month
622        assert_eq!(read_reg(&mut cmos, 0x08), 0x01); // month
623        assert_eq!(read_reg(&mut cmos, 0x09), 0x17); // year
624        assert_eq!(read_reg(&mut cmos, 0x32), 0x20); // century
625    }
626
627    #[test]
628    fn cmos_alarm() {
629        // 2000-01-02T03:04:05+00:00
630        let now_fn = || timestamp_to_datetime(946782245);
631        let mut cmos = new_cmos_for_test(now_fn);
632
633        // A date later this year
634        write_reg(&mut cmos, 0x01, 0x06); // seconds
635        write_reg(&mut cmos, 0x03, 0x05); // minutes
636        write_reg(&mut cmos, 0x05, 0x04); // hours
637        write_reg(&mut cmos, 0x33, 0x03); // day of month
638        write_reg(&mut cmos, 0x34, 0x02); // month
639        write_reg(&mut cmos, 0x0b, 0x20); // RTC_REG_B_ALARM_ENABLE
640                                          // 2000-02-03T04:05:06+00:00
641        assert_eq!(cmos.alarm_time, Some(timestamp_to_datetime(949550706)));
642
643        // A date (one year - one second) in the future
644        write_reg(&mut cmos, 0x01, 0x04); // seconds
645        write_reg(&mut cmos, 0x03, 0x04); // minutes
646        write_reg(&mut cmos, 0x05, 0x03); // hours
647        write_reg(&mut cmos, 0x33, 0x02); // day of month
648        write_reg(&mut cmos, 0x34, 0x01); // month
649        write_reg(&mut cmos, 0x0b, 0x20); // RTC_REG_B_ALARM_ENABLE
650                                          // 2001-01-02T03:04:04+00:00
651        assert_eq!(cmos.alarm_time, Some(timestamp_to_datetime(978404644)));
652
653        // The current time
654        write_reg(&mut cmos, 0x01, 0x05); // seconds
655        write_reg(&mut cmos, 0x03, 0x04); // minutes
656        write_reg(&mut cmos, 0x05, 0x03); // hours
657        write_reg(&mut cmos, 0x33, 0x02); // day of month
658        write_reg(&mut cmos, 0x34, 0x01); // month
659        write_reg(&mut cmos, 0x0b, 0x20); // RTC_REG_B_ALARM_ENABLE
660        assert_eq!(cmos.alarm_time, Some(timestamp_to_datetime(946782245)));
661        assert_eq!(read_reg(&mut cmos, 0x0c), 0xa0); // RTC_REG_C_IRQF | RTC_REG_C_AF
662        assert_eq!(cmos.alarm_time, None);
663        assert_eq!(read_reg(&mut cmos, 0x0c), 0);
664
665        // Invalid BCD
666        write_reg(&mut cmos, 0x01, 0xa0); // seconds
667        write_reg(&mut cmos, 0x0b, 0x20); // RTC_REG_B_ALARM_ENABLE
668        assert_eq!(cmos.alarm_time, None);
669    }
670
671    #[test]
672    fn cmos_reg_d() {
673        let mut cmos = new_cmos_for_test(test_now_party_like_its_1999);
674        assert_eq!(read_reg(&mut cmos, 0x0d), 0x80) // RAM and time are valid
675    }
676
677    #[test]
678    fn cmos_snapshot_restore() -> anyhow::Result<()> {
679        // time function doesn't matter in this case.
680        let mut cmos = new_cmos_for_test(test_now_party_like_its_1999);
681
682        let info_index = BusAccessInfo {
683            offset: 0,
684            address: 0x71,
685            id: 0,
686        };
687
688        let info_data = BusAccessInfo {
689            offset: 1,
690            address: 0x71,
691            id: 0,
692        };
693
694        // change index to 0x41.
695        cmos.write(info_index, &[0x41]);
696        cmos.write(info_data, &[0x01]);
697
698        let snap = cmos.snapshot().context("failed to snapshot Cmos")?;
699
700        // change index to 0x42.
701        cmos.write(info_index, &[0x42]);
702        cmos.write(info_data, &[0x01]);
703
704        // Restore Cmos.
705        cmos.restore(snap).context("failed to restore Cmos")?;
706
707        // after restore, the index should be 0x41, which was the index before snapshot was taken.
708        assert_eq!(cmos.index, 0x41);
709        assert_eq!(cmos.data[0x41], 0x01);
710        assert_ne!(cmos.data[0x42], 0x01);
711        Ok(())
712    }
713
714    #[test]
715    fn cmos_sleep_wake() {
716        // 2000-01-02T03:04:05+00:00
717        let irq = IrqEdgeEvent::new().unwrap();
718        let now_fn = || timestamp_to_datetime(946782245);
719        let mut cmos = Cmos::new(1024, 0, now_fn, Tube::pair().unwrap().0, irq).unwrap();
720
721        // A date later this year
722        write_reg(&mut cmos, 0x01, 0x06); // seconds
723        write_reg(&mut cmos, 0x03, 0x05); // minutes
724        write_reg(&mut cmos, 0x05, 0x04); // hours
725        write_reg(&mut cmos, 0x33, 0x03); // day of month
726        write_reg(&mut cmos, 0x34, 0x02); // month
727        write_reg(&mut cmos, 0x0b, 0x20); // RTC_REG_B_ALARM_ENABLE
728                                          // 2000-02-03T04:05:06+00:00
729        assert_eq!(cmos.alarm_time, Some(timestamp_to_datetime(949550706)));
730        assert!(cmos.worker.is_some());
731
732        cmos.sleep().unwrap();
733        assert!(cmos.worker.is_none());
734
735        cmos.wake().unwrap();
736        assert!(cmos.worker.is_some());
737    }
738
739    suspendable_tests!(
740        cmos1999,
741        new_cmos_for_test(test_now_party_like_its_1999),
742        modify_device
743    );
744    suspendable_tests!(
745        cmos2k,
746        new_cmos_for_test(test_now_y2k_compliant),
747        modify_device
748    );
749    suspendable_tests!(
750        cmos2016,
751        new_cmos_for_test(test_now_2016_before_leap_second),
752        modify_device
753    );
754    suspendable_tests!(
755        cmos2017,
756        new_cmos_for_test(test_now_2017_after_leap_second),
757        modify_device
758    );
759}