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
5 use std::cmp::min;
6 use std::sync::Arc;
7 use std::time::Duration;
8 use std::time::Instant;
9
10 use anyhow::anyhow;
11 use anyhow::Context;
12 use base::custom_serde::deserialize_seq_to_arr;
13 use base::custom_serde::serialize_arr;
14 use base::error;
15 use base::info;
16 use base::Event;
17 use base::EventToken;
18 use base::Timer;
19 use base::TimerTrait;
20 use base::Tube;
21 use base::WaitContext;
22 use base::WorkerThread;
23 use chrono::DateTime;
24 use chrono::Datelike;
25 use chrono::TimeZone;
26 use chrono::Timelike;
27 use chrono::Utc;
28 use metrics::log_metric;
29 use metrics::MetricEventType;
30 use serde::Deserialize;
31 use serde::Serialize;
32 use snapshot::AnySnapshot;
33 use sync::Mutex;
34 use vm_control::VmResponse;
35
36 use crate::pci::CrosvmDeviceId;
37 use crate::BusAccessInfo;
38 use crate::BusDevice;
39 use crate::DeviceId;
40 use crate::IrqEdgeEvent;
41 use crate::Suspendable;
42
43 pub const RTC_IRQ: u8 = 8;
44
45 const INDEX_MASK: u8 = 0x7f;
46 const INDEX_OFFSET: u64 = 0x0;
47 const DATA_OFFSET: u64 = 0x1;
48 const DATA_LEN: usize = 128;
49
50 const RTC_REG_SEC: u8 = 0x0;
51 const RTC_REG_ALARM_SEC: u8 = 0x1;
52 const RTC_REG_MIN: u8 = 0x2;
53 const RTC_REG_ALARM_MIN: u8 = 0x3;
54 const RTC_REG_HOUR: u8 = 0x4;
55 const RTC_REG_ALARM_HOUR: u8 = 0x5;
56 const RTC_REG_WEEK_DAY: u8 = 0x6;
57 const RTC_REG_DAY: u8 = 0x7;
58 const RTC_REG_MONTH: u8 = 0x8;
59 const RTC_REG_YEAR: u8 = 0x9;
60 pub const RTC_REG_CENTURY: u8 = 0x32;
61 pub const RTC_REG_ALARM_DAY: u8 = 0x33;
62 pub const RTC_REG_ALARM_MONTH: u8 = 0x34;
63
64 const RTC_REG_B: u8 = 0x0b;
65 const RTC_REG_B_UNSUPPORTED: u8 = 0xdd;
66 const RTC_REG_B_24_HOUR_MODE: u8 = 0x02;
67 const RTC_REG_B_ALARM_ENABLE: u8 = 0x20;
68
69 const RTC_REG_C: u8 = 0x0c;
70 const RTC_REG_C_IRQF: u8 = 0x80;
71 const RTC_REG_C_AF: u8 = 0x20;
72
73 const RTC_REG_D: u8 = 0x0d;
74 const RTC_REG_D_VRT: u8 = 0x80; // RAM and time valid
75
76 pub type CmosNowFn = fn() -> DateTime<Utc>;
77
78 // Alarm state shared between Cmos and the alarm worker thread.
79 struct AlarmState {
80 alarm: Timer,
81 vm_control: Tube,
82 irq: IrqEdgeEvent,
83 armed_time: Instant,
84 clear_evt: Option<Event>,
85 }
86
87 impl AlarmState {
trigger_rtc_interrupt(&self) -> anyhow::Result<Event>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)]
118 pub 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
136 impl 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.
new( mem_below_4g: u64, mem_above_4g: u64, now_fn: CmosNowFn, vm_control: Tube, irq: IrqEdgeEvent, ) -> anyhow::Result<Cmos>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
spawn_worker(&mut self, alarm_state: Arc<Mutex<AlarmState>>)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
set_alarm(&mut self)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
run_cmos_worker(alarm_state: Arc<Mutex<AlarmState>>, kill_evt: Event) -> anyhow::Result<()>246 fn 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
from_bcd(v: u8) -> Option<u32>280 fn 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
alarm_from_registers(year: i32, data: &[u8; DATA_LEN]) -> Option<DateTime<Utc>>290 fn 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
302 impl BusDevice for Cmos {
device_id(&self) -> DeviceId303 fn device_id(&self) -> DeviceId {
304 CrosvmDeviceId::Cmos.into()
305 }
306
debug_label(&self) -> String307 fn debug_label(&self) -> String {
308 "cmos".to_owned()
309 }
310
write(&mut self, info: BusAccessInfo, data: &[u8])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
read(&mut self, info: BusAccessInfo, data: &mut [u8])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
403 impl Suspendable for Cmos {
snapshot(&mut self) -> anyhow::Result<AnySnapshot>404 fn snapshot(&mut self) -> anyhow::Result<AnySnapshot> {
405 AnySnapshot::to_any(self).context("failed to serialize Cmos")
406 }
407
restore(&mut self, data: AnySnapshot) -> anyhow::Result<()>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
sleep(&mut self) -> anyhow::Result<()>424 fn sleep(&mut self) -> anyhow::Result<()> {
425 if let Some(worker) = self.worker.take() {
426 worker.stop();
427 }
428 Ok(())
429 }
430
wake(&mut self) -> anyhow::Result<()>431 fn wake(&mut self) -> anyhow::Result<()> {
432 self.spawn_worker(self.alarm_state.clone());
433 Ok(())
434 }
435 }
436
437 #[cfg(test)]
438 mod tests {
439 use super::*;
440 use crate::suspendable_tests;
441
read_reg(cmos: &mut Cmos, reg: u8) -> u8442 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
write_reg(cmos: &mut Cmos, reg: u8, val: u8)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
timestamp_to_datetime(timestamp: i64) -> DateTime<Utc>491 fn timestamp_to_datetime(timestamp: i64) -> DateTime<Utc> {
492 DateTime::from_timestamp(timestamp, 0).unwrap()
493 }
494
test_now_party_like_its_1999() -> DateTime<Utc>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
test_now_y2k_compliant() -> DateTime<Utc>500 fn test_now_y2k_compliant() -> DateTime<Utc> {
501 // 2000-01-01T00:00:00+00:00
502 timestamp_to_datetime(946684800)
503 }
504
test_now_2016_before_leap_second() -> DateTime<Utc>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
test_now_2017_after_leap_second() -> DateTime<Utc>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
new_cmos_for_test(now_fn: CmosNowFn) -> Cmos515 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]
cmos_write_index()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]
cmos_write_data()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
modify_device(cmos: &mut Cmos)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]
cmos_date_time_1999()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]
cmos_date_time_2000()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]
cmos_date_time_before_leap_second()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]
cmos_date_time_after_leap_second()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]
cmos_alarm()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]
cmos_reg_d()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]
cmos_snapshot_restore() -> anyhow::Result<()>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]
cmos_sleep_wake()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 }
760