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