Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/cmos.rs
5392 views
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::DeviceId;
35
use vm_control::PlatformDeviceId;
36
use vm_control::VmResponse;
37
38
use crate::BusAccessInfo;
39
use crate::BusDevice;
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 {
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.
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
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
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
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 {
303
fn device_id(&self) -> DeviceId {
304
PlatformDeviceId::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
403
impl 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)]
438
mod 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
}
760
761