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