1#![warn(missing_docs)]
8
9use self::spec::CmosReg;
10use self::spec::ENABLE_OSCILLATOR_CONTROL;
11use self::spec::StatusRegA;
12use self::spec::StatusRegB;
13use self::spec::StatusRegC;
14use self::spec::StatusRegD;
15use chipset_device::ChipsetDevice;
16use chipset_device::io::IoError;
17use chipset_device::io::IoResult;
18use chipset_device::pio::PortIoIntercept;
19use chipset_device::poll_device::PollDevice;
20use inspect::Inspect;
21use inspect::InspectMut;
22use local_clock::InspectableLocalClock;
23use local_clock::LocalClockTime;
24use std::ops::RangeInclusive;
25use std::task::Poll;
26use std::time::Duration;
27use vmcore::device_state::ChangeDeviceState;
28use vmcore::line_interrupt::LineInterrupt;
29use vmcore::vmtime::VmTime;
30use vmcore::vmtime::VmTimeAccess;
31use vmcore::vmtime::VmTimeSource;
32use vmcore::vmtime::VmTimerPeriodic;
33
34mod spec {
35 use bitfield_struct::bitfield;
38 use inspect::Inspect;
39
40 open_enum::open_enum! {
41 #[derive(Inspect)]
43 #[inspect(debug)]
44 pub enum CmosReg: u8 {
45 SECOND = 0x00,
46 SECOND_ALARM = 0x01,
47 MINUTE = 0x02,
48 MINUTE_ALARM = 0x03,
49 HOUR = 0x04,
50 HOUR_ALARM = 0x05,
51 DAY_OF_WEEK = 0x06,
52 DAY_OF_MONTH = 0x07,
53 MONTH = 0x08,
54 YEAR = 0x09,
55 STATUS_A = 0x0A,
56 STATUS_B = 0x0B,
57 STATUS_C = 0x0C,
58 STATUS_D = 0x0D,
59 }
60 }
61
62 impl CmosReg {
63 pub fn depends_on_rtc(&self, century: CmosReg) -> bool {
65 matches!(
66 *self,
67 CmosReg::SECOND
68 | CmosReg::MINUTE
69 | CmosReg::HOUR
70 | CmosReg::DAY_OF_WEEK
71 | CmosReg::DAY_OF_MONTH
72 | CmosReg::MONTH
73 | CmosReg::YEAR
74 ) || *self == century
75 }
76
77 pub fn depends_on_alarm(&self) -> bool {
79 matches!(
80 *self,
81 CmosReg::SECOND_ALARM | CmosReg::MINUTE_ALARM | CmosReg::HOUR_ALARM
82 )
83 }
84 }
85
86 pub const ENABLE_OSCILLATOR_CONTROL: u8 = 0b010;
87
88 pub const PERIODIC_TIMER_RATE_HZ: [usize; 15] = [
91 256, 128, 8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2,
92 ];
93
94 #[rustfmt::skip]
95 #[bitfield(u8)]
96 pub struct StatusRegA {
97 #[bits(4)] pub periodic_timer_rate: u8,
98 #[bits(3)] pub oscillator_control: u8,
99 #[bits(1)] pub update: bool,
100 }
101
102 #[rustfmt::skip]
103 #[bitfield(u8)]
104 pub struct StatusRegB {
105 pub dst: bool, pub h24_mode: bool,
107 pub disable_bcd: bool,
108 pub square_wave_enable: bool, pub irq_enable_update: bool,
110 pub irq_enable_alarm: bool,
111 pub irq_enable_periodic: bool,
112 pub set: bool,
113 }
114
115 #[rustfmt::skip]
116 #[bitfield(u8)]
117 pub struct StatusRegC {
118 #[bits(4)] _unused: u8,
119 pub irq_update: bool,
120 pub irq_alarm: bool,
121 pub irq_periodic: bool,
122 pub irq_combined: bool,
123 }
124
125 #[rustfmt::skip]
126 #[bitfield(u8)]
127 pub struct StatusRegD {
128 #[bits(7)] _unused: u8,
129 pub vrt: bool,
132 }
133
134 pub const ALARM_WILDCARD: u8 = 0xFF;
135}
136
137open_enum::open_enum! {
138 enum RtcIoPort: u16 {
140 ADDR = 0x70,
141 DATA = 0x71,
142 }
143}
144
145#[derive(Debug, Clone, Inspect)]
147#[inspect(transparent)]
148struct CmosData(#[inspect(hex, iter_by_index)] [u8; 256]);
149
150impl CmosData {
151 fn empty() -> CmosData {
152 CmosData([0; 256])
153 }
154}
155
156impl std::ops::Index<CmosReg> for CmosData {
157 type Output = u8;
158
159 fn index(&self, index: CmosReg) -> &Self::Output {
160 &self.0[index.0 as usize]
161 }
162}
163
164impl std::ops::IndexMut<CmosReg> for CmosData {
165 fn index_mut(&mut self, index: CmosReg) -> &mut Self::Output {
166 &mut self.0[index.0 as usize]
167 }
168}
169
170#[derive(InspectMut)]
172pub struct Rtc {
173 century_reg: CmosReg,
175 initial_cmos: Option<[u8; 256]>,
176 enlightened_interrupts: bool,
177
178 real_time_source: Box<dyn InspectableLocalClock>,
180 interrupt: LineInterrupt,
181 vmtime_alarm: VmTimeAccess,
182 vmtimer_periodic: VmTimerPeriodic,
183 vmtimer_update: VmTimerPeriodic,
184
185 #[inspect(debug)]
187 last_update_bit_blip: LocalClockTime,
188
189 state: RtcState,
191}
192
193#[derive(Debug, Inspect)]
194struct RtcState {
195 addr: u8,
196 cmos: CmosData,
197}
198
199impl RtcState {
200 fn new(initial_cmos: Option<[u8; 256]>) -> Self {
201 let mut cmos = initial_cmos.map(CmosData).unwrap_or_else(CmosData::empty);
202
203 cmos[CmosReg::STATUS_A] = {
204 StatusRegA::new()
205 .with_periodic_timer_rate(0b0110)
206 .with_oscillator_control(ENABLE_OSCILLATOR_CONTROL)
207 .into()
208 };
209 cmos[CmosReg::STATUS_B] = {
210 StatusRegB::new()
211 .with_disable_bcd(false)
212 .with_h24_mode(true)
213 .into()
214 };
215 cmos[CmosReg::STATUS_C] = StatusRegC::new().into();
216 cmos[CmosReg::STATUS_D] = StatusRegD::new().with_vrt(true).into();
217
218 Self {
219 addr: 0x80,
222 cmos,
223 }
224 }
225}
226
227impl ChangeDeviceState for Rtc {
228 fn start(&mut self) {}
229
230 async fn stop(&mut self) {}
231
232 async fn reset(&mut self) {
233 self.state = RtcState::new(self.initial_cmos);
234
235 self.update_timers();
236 self.sync_interrupt_line_level();
237 }
238}
239
240impl ChipsetDevice for Rtc {
241 fn supports_pio(&mut self) -> Option<&mut dyn PortIoIntercept> {
242 Some(self)
243 }
244
245 fn supports_poll_device(&mut self) -> Option<&mut dyn PollDevice> {
246 Some(self)
247 }
248}
249
250fn to_bcd(n: u8) -> u8 {
251 ((n / 10) << 4) | (n % 10)
252}
253
254fn from_bcd(n: u8) -> u8 {
255 (n >> 4) * 10 + (n & 0xf)
256}
257
258fn canonical_hms(status_b: StatusRegB, mut hour: u8, mut min: u8, mut sec: u8) -> (u8, u8, u8) {
260 if !status_b.disable_bcd() {
261 sec = from_bcd(sec);
262 min = from_bcd(min);
263
264 if status_b.h24_mode() {
265 hour = from_bcd(hour);
266 } else {
267 let mut hour_ampm = from_bcd(hour & 0x7F);
268 if hour_ampm == 12 {
269 hour_ampm = 0;
270 }
271 if hour & 0x80 != 0 {
272 hour = hour_ampm + 12;
273 } else {
274 hour = hour_ampm
275 }
276 }
277 } else {
278 if !status_b.h24_mode() {
279 let mut hour_ampm = hour & 0x7F;
280 if hour_ampm == 12 {
281 hour_ampm = 0;
282 }
283 hour = hour_ampm + if hour & 0x80 != 0 { 12 } else { 0 };
284 }
285 }
286
287 (hour, min, sec)
288}
289
290impl Rtc {
291 pub fn new(
308 real_time_source: Box<dyn InspectableLocalClock>,
309 interrupt: LineInterrupt,
310 vmtime_source: &VmTimeSource,
311 century_reg_idx: u8,
312 initial_cmos: Option<[u8; 256]>,
313 enlightened_interrupts: bool,
314 ) -> Self {
315 Rtc {
316 century_reg: CmosReg(century_reg_idx),
317 initial_cmos,
318 enlightened_interrupts,
319
320 real_time_source,
321 interrupt,
322 vmtime_alarm: vmtime_source.access("rtc-alarm"),
323 vmtimer_periodic: VmTimerPeriodic::new(vmtime_source.access("rtc-periodic")),
324 vmtimer_update: VmTimerPeriodic::new(vmtime_source.access("rtc-update")),
325
326 last_update_bit_blip: LocalClockTime::from_millis_since_unix_epoch(0),
327
328 state: RtcState::new(initial_cmos),
329 }
330 }
331
332 pub fn raw_cmos(&mut self) -> &mut [u8; 256] {
334 &mut self.state.cmos.0
335 }
336
337 fn calculate_alarm_duration(&self) -> Duration {
341 use self::spec::ALARM_WILDCARD;
342
343 const SECONDS_PER_MINUTE: u32 = 60;
344 const MINUTES_PER_HOUR: u32 = 60;
345 const HOURS_PER_DAY: u32 = 24;
346 const SECONDS_PER_HOUR: u32 = SECONDS_PER_MINUTE * MINUTES_PER_HOUR;
347 const SECONDS_PER_DAY: u32 = SECONDS_PER_HOUR * HOURS_PER_DAY;
348
349 let status_b = StatusRegB::from(self.state.cmos[CmosReg::STATUS_B]);
350 let (now_hour, now_min, now_sec) = canonical_hms(
351 status_b,
352 self.state.cmos[CmosReg::HOUR],
353 self.state.cmos[CmosReg::MINUTE],
354 self.state.cmos[CmosReg::SECOND],
355 );
356 let [
357 (hour_wildcard, hour),
358 (min_wildcard, min),
359 (sec_wildcard, sec),
360 ] = [
361 self.state.cmos[CmosReg::HOUR_ALARM],
362 self.state.cmos[CmosReg::MINUTE_ALARM],
363 self.state.cmos[CmosReg::SECOND_ALARM],
364 ]
365 .map(|value| {
366 if value & 0xC0 == 0xC0 {
367 (true, 0)
368 } else {
369 (false, value)
370 }
371 });
372 let (hour, min, sec) = canonical_hms(status_b, hour, min, sec);
373 let (alarm_hour, alarm_min, alarm_sec) = (
374 if hour_wildcard { ALARM_WILDCARD } else { hour },
375 if min_wildcard { ALARM_WILDCARD } else { min },
376 if sec_wildcard { ALARM_WILDCARD } else { sec },
377 );
378
379 let fields = [
380 (alarm_sec, 1, SECONDS_PER_MINUTE),
381 (alarm_min, SECONDS_PER_MINUTE, MINUTES_PER_HOUR),
382 (alarm_hour, SECONDS_PER_HOUR, HOURS_PER_DAY),
383 ];
384 if fields
385 .iter()
386 .any(|&(value, _, radix)| value != ALARM_WILDCARD && u32::from(value) >= radix)
387 {
388 return Duration::from_secs(u64::from(SECONDS_PER_DAY));
389 }
390
391 let now_seconds = (u32::from(now_hour) * SECONDS_PER_HOUR
392 + u32::from(now_min) * SECONDS_PER_MINUTE
393 + u32::from(now_sec))
394 % SECONDS_PER_DAY;
395 let mut next_seconds = now_seconds + 1;
396 let mut lower_fields = 0;
397 for (value, unit, radix) in fields {
398 if value == ALARM_WILDCARD {
399 continue;
400 }
401 let value = u32::from(value);
402 let current = (next_seconds / unit) % radix;
403 let advance = (value + radix - current) % radix;
404 if advance != 0 {
405 next_seconds = (next_seconds / unit + advance) * unit + lower_fields;
406 }
407 lower_fields += value * unit;
408 }
409 let alarm_duration = Duration::from_secs(u64::from(next_seconds - now_seconds));
410
411 tracing::debug!(
412 now = ?(now_hour, now_min, now_sec),
413 alarm = ?(alarm_hour, alarm_min, alarm_sec),
414 ?alarm_duration,
415 "setting alarm"
416 );
417
418 alarm_duration
419 }
420
421 fn set_alarm_timer(&mut self, now: VmTime) {
422 self.sync_clock_to_cmos();
423 let alarm_duration = self.calculate_alarm_duration();
424
425 self.vmtime_alarm
426 .set_timeout(now.wrapping_add(alarm_duration));
427 }
428
429 fn on_alarm_timer(&mut self, now: VmTime) {
430 let status_c = StatusRegC::from(self.state.cmos[CmosReg::STATUS_C]);
431 self.state.cmos[CmosReg::STATUS_C] = status_c.with_irq_alarm(true).into();
432
433 self.signal_interrupt();
434
435 self.set_alarm_timer(now)
437 }
438
439 fn set_periodic_timer(&mut self) {
440 use self::spec::PERIODIC_TIMER_RATE_HZ;
441
442 let status_a = StatusRegA::from(self.state.cmos[CmosReg::STATUS_A]);
443
444 if status_a.periodic_timer_rate() == 0 {
446 return;
447 }
448
449 let tick_hz = PERIODIC_TIMER_RATE_HZ[status_a.periodic_timer_rate() as usize - 1];
450 let tick_period = Duration::from_secs_f32(1. / tick_hz as f32);
451
452 tracing::debug!(
453 periodic_timer_rate = ?status_a.periodic_timer_rate(),
454 ?tick_hz,
455 ?tick_period,
456 "setting periodic timer"
457 );
458
459 self.vmtimer_periodic.start(tick_period);
460 }
461
462 fn on_periodic_timer(&mut self) {
463 let status_c = StatusRegC::from(self.state.cmos[CmosReg::STATUS_C]);
464 self.state.cmos[CmosReg::STATUS_C] = status_c.with_irq_periodic(true).into();
465
466 self.signal_interrupt();
467 }
468
469 fn set_update_timer(&mut self) {
470 self.vmtimer_update.start(Duration::from_secs(1));
471 }
472
473 fn on_update_timer(&mut self) {
474 let status_c = StatusRegC::from(self.state.cmos[CmosReg::STATUS_C]);
475 self.state.cmos[CmosReg::STATUS_C] = status_c.with_irq_update(true).into();
476
477 self.signal_interrupt();
478 }
479
480 fn signal_interrupt(&mut self) {
482 let irq_combined = self.update_irqf();
483 if self.enlightened_interrupts && irq_combined {
484 self.interrupt.set_level(false);
485 }
486 self.interrupt.set_level(irq_combined);
487 }
488
489 fn update_irqf(&mut self) -> bool {
490 let status_b = StatusRegB::from(self.state.cmos[CmosReg::STATUS_B]);
491 let status_c = StatusRegC::from(self.state.cmos[CmosReg::STATUS_C]);
492 let irq_combined = (status_c.irq_update() && status_b.irq_enable_update())
493 || (status_c.irq_periodic() && status_b.irq_enable_periodic())
494 || (status_c.irq_alarm() && status_b.irq_enable_alarm());
495 self.state.cmos[CmosReg::STATUS_C] = status_c.with_irq_combined(irq_combined).into();
496 irq_combined
497 }
498
499 fn sync_interrupt_line_level(&mut self) {
501 let irq_combined = self.update_irqf();
502 self.interrupt.set_level(irq_combined);
503 }
504
505 fn update_timers(&mut self) {
508 let status_b = StatusRegB::from(self.state.cmos[CmosReg::STATUS_B]);
509
510 if status_b.irq_enable_alarm() {
511 if self.vmtime_alarm.get_timeout().is_none() {
512 self.set_alarm_timer(self.vmtime_alarm.now())
513 }
514 } else {
515 self.vmtime_alarm.cancel_timeout();
516 }
517
518 if status_b.irq_enable_periodic() {
519 if !self.vmtimer_periodic.is_running() {
520 self.set_periodic_timer()
521 }
522 } else {
523 self.vmtimer_periodic.cancel();
524 }
525
526 if status_b.irq_enable_update() {
527 if !self.vmtimer_update.is_running() {
528 self.set_update_timer()
529 }
530 } else {
531 self.vmtimer_update.cancel();
532 }
533 }
534
535 pub fn set_cmos_byte(&mut self, addr: u8, data: u8) {
541 let addr = CmosReg(addr);
542
543 tracing::trace!(?addr, ?data, "set_cmos_byte");
544
545 if (CmosReg::STATUS_A..=CmosReg::STATUS_D).contains(&addr) {
546 self.set_status_byte(addr, data);
547 } else {
548 let old_data = self.state.cmos[addr];
549
550 if data != old_data {
551 if addr.depends_on_rtc(self.century_reg) {
553 self.sync_clock_to_cmos();
554 }
555
556 self.state.cmos[addr] = data;
557
558 if addr.depends_on_rtc(self.century_reg) {
560 self.sync_cmos_to_clock();
561 }
562
563 if addr.depends_on_alarm() || addr.depends_on_rtc(self.century_reg) {
564 if self.vmtime_alarm.get_timeout().is_some() {
565 self.set_alarm_timer(self.vmtime_alarm.now());
566 }
567 }
568 }
569 }
570 }
571
572 pub fn get_cmos_byte(&mut self, addr: u8) -> u8 {
578 let addr = CmosReg(addr);
579
580 let data = if (CmosReg::STATUS_A..=CmosReg::STATUS_D).contains(&addr) {
581 self.get_status_byte(addr)
582 } else {
583 if addr.depends_on_rtc(self.century_reg) {
584 self.sync_clock_to_cmos();
585 }
586
587 self.state.cmos[addr]
588 };
589
590 tracing::trace!(?addr, ?data, "get_cmos_byte");
591
592 data
593 }
594
595 fn set_status_byte(&mut self, addr: CmosReg, data: u8) {
596 match addr {
597 CmosReg::STATUS_A => {
598 let new_reg = StatusRegA::from(data);
599 let old_reg = StatusRegA::from(self.state.cmos[CmosReg::STATUS_A]);
600
601 if new_reg.oscillator_control() != old_reg.oscillator_control() {
603 self.vmtime_alarm.cancel_timeout();
605 }
606
607 if new_reg.periodic_timer_rate() != old_reg.periodic_timer_rate() {
608 self.vmtimer_periodic.cancel();
610 }
611
612 self.state.cmos[CmosReg::STATUS_A] = data & 0x7F;
614
615 self.update_timers();
616 }
617 CmosReg::STATUS_B => {
618 let mut new_reg = StatusRegB::from(data);
619
620 if new_reg.set() {
622 tracing::debug!("disable timer update and interrupt");
623 new_reg.set_irq_enable_update(false)
624 }
625
626 self.state.cmos[CmosReg::STATUS_B] = new_reg.into();
627
628 self.update_timers();
629 self.sync_interrupt_line_level();
630 }
631 CmosReg::STATUS_C | CmosReg::STATUS_D => {}
634 _ => unreachable!("passed invalid status reg"),
635 }
636 }
637
638 fn get_status_byte(&mut self, addr: CmosReg) -> u8 {
639 match addr {
640 CmosReg::STATUS_A => {
641 let mut data = StatusRegA::from(self.state.cmos[CmosReg::STATUS_A]);
642
643 if !StatusRegB::from(self.state.cmos[CmosReg::STATUS_B]).set() {
650 let now = self.real_time_source.get_time();
651 let elapsed = now - self.last_update_bit_blip;
652
653 if elapsed.as_millis().is_negative() {
655 tracing::debug!("clock jumped backwards between update bit blips");
656 self.last_update_bit_blip = LocalClockTime::from_millis_since_unix_epoch(0);
657 }
658
659 tracing::trace!(
660 ?self.last_update_bit_blip,
661 ?now,
662 ?elapsed,
663 "get_status_byte"
664 );
665
666 let elapsed_millis = elapsed.as_millis();
667 if elapsed_millis > 1000 {
668 data.set_update(true);
670 self.sync_clock_to_cmos();
671 self.last_update_bit_blip = now;
672 tracing::trace!(
673 ?data,
674 ?elapsed,
675 cmos_date_time = ?self.read_cmos_date_time(),
676 "blip'd status a update bit"
677 );
678 }
679 }
680
681 data.into()
682 }
683 CmosReg::STATUS_B => self.state.cmos[CmosReg::STATUS_B],
684 CmosReg::STATUS_C => {
685 let data = StatusRegC::from(self.state.cmos[CmosReg::STATUS_C]);
686
687 tracing::debug!("clearing rtc interrupts");
689 self.state.cmos[CmosReg::STATUS_C] = StatusRegC::new().into();
690 self.sync_interrupt_line_level();
691
692 data.into()
693 }
694 CmosReg::STATUS_D => {
695 StatusRegD::new().with_vrt(true).into()
697 }
698 _ => unreachable!("passed invalid status reg"),
699 }
700 }
701
702 fn read_cmos_date_time(&self) -> Result<jiff::civil::DateTime, jiff::Error> {
703 let mut sec = self.state.cmos[CmosReg::SECOND];
704 let mut min = self.state.cmos[CmosReg::MINUTE];
705 let mut hour = self.state.cmos[CmosReg::HOUR];
706 let mut day = self.state.cmos[CmosReg::DAY_OF_MONTH];
707 let mut month = self.state.cmos[CmosReg::MONTH];
708 let mut year = self.state.cmos[CmosReg::YEAR];
709 let mut century = self.state.cmos[self.century_reg];
710
711 let status_b = StatusRegB::from(self.state.cmos[CmosReg::STATUS_B]);
712
713 (hour, min, sec) = canonical_hms(status_b, hour, min, sec);
715 if !status_b.disable_bcd() {
716 (day, month, year, century) = (
717 from_bcd(day),
718 from_bcd(month),
719 from_bcd(year),
720 from_bcd(century),
721 );
722 }
723
724 jiff::civil::DateTime::new(
725 year as i16 + century as i16 * 100,
726 month as i8,
727 day as i8,
728 hour as i8,
729 min as i8,
730 sec as i8,
731 0,
732 )
733 }
734
735 fn sync_clock_to_cmos(&mut self) {
738 if StatusRegA::from(self.state.cmos[CmosReg::STATUS_A]).oscillator_control()
739 != ENABLE_OSCILLATOR_CONTROL
740 {
741 tracing::trace!(
743 cmos_reg_status_a = self.state.cmos[CmosReg::STATUS_A],
744 "sync_clock_to_cmos: Oscillator is disabled."
745 );
746
747 return;
748 }
749
750 let real_time = self.real_time_source.get_time();
751 let Ok(clock_time): Result<jiff::Timestamp, _> = real_time.try_into() else {
752 tracelimit::warn_ratelimited!(
753 ?real_time,
754 "invalid date/time in real_time_source, skipping sync"
755 );
756 return;
757 };
758
759 let clock_time = clock_time.to_zoned(jiff::tz::TimeZone::UTC).datetime();
760
761 let status_b = StatusRegB::from(self.state.cmos[CmosReg::STATUS_B]);
762
763 self.state.cmos[CmosReg::SECOND] = clock_time.second() as u8;
764 self.state.cmos[CmosReg::MINUTE] = clock_time.minute() as u8;
765 self.state.cmos[CmosReg::HOUR] = {
766 let hour = clock_time.hour() as u8;
767 if status_b.h24_mode() {
768 hour
769 } else {
770 let hour_ampm = hour % 12;
771 if hour_ampm == 0 { 12 } else { hour_ampm }
772 }
773 };
774 self.state.cmos[CmosReg::DAY_OF_WEEK] =
775 clock_time.date().weekday().to_sunday_one_offset() as u8;
776 self.state.cmos[CmosReg::DAY_OF_MONTH] = clock_time.day() as u8;
777 self.state.cmos[CmosReg::MONTH] = clock_time.month() as u8;
778 self.state.cmos[CmosReg::YEAR] = (clock_time.year() % 100) as u8;
779 self.state.cmos[self.century_reg] = (clock_time.year() / 100) as u8;
780
781 if !status_b.disable_bcd() {
782 let regs = [
783 CmosReg::SECOND,
784 CmosReg::MINUTE,
785 CmosReg::HOUR,
786 CmosReg::DAY_OF_WEEK,
787 CmosReg::DAY_OF_MONTH,
788 CmosReg::MONTH,
789 CmosReg::YEAR,
790 self.century_reg,
791 ];
792
793 for reg in regs {
794 self.state.cmos[reg] = to_bcd(self.state.cmos[reg])
795 }
796 }
797
798 if !status_b.h24_mode() {
799 if clock_time.hour() >= 12 {
800 self.state.cmos[CmosReg::HOUR] |= 0x80;
801 }
802 }
803
804 tracing::trace!(
805 cmos_reg_status_b = self.state.cmos[CmosReg::STATUS_B],
806 use_bcd_encoding = ?!status_b.disable_bcd(),
807 use_24h_time = ?status_b.h24_mode(),
808 cmos_date_time = ?self.read_cmos_date_time(),
809 "sync_clock_to_cmos"
810 );
811 }
812
813 fn sync_cmos_to_clock(&mut self) {
816 let cmos_time: jiff::Timestamp = match self
817 .read_cmos_date_time()
818 .and_then(|cmos_time| cmos_time.to_zoned(jiff::tz::TimeZone::UTC))
819 {
820 Ok(zoned) => zoned.timestamp(),
821 Err(e) => {
822 tracelimit::warn_ratelimited!(?e, "invalid date/time in RTC registers!");
823 return;
824 }
825 };
826 self.real_time_source.set_time(cmos_time.into());
827 }
828
829 #[cfg(test)]
830 fn get_cmos_date_time(&mut self) -> Result<jiff::civil::DateTime, jiff::Error> {
831 self.sync_clock_to_cmos();
832 self.read_cmos_date_time()
833 }
834}
835
836impl PortIoIntercept for Rtc {
837 fn io_read(&mut self, io_port: u16, data: &mut [u8]) -> IoResult {
838 data[0] = match RtcIoPort(io_port) {
842 RtcIoPort::ADDR => self.state.addr,
843 RtcIoPort::DATA => self.get_cmos_byte(self.state.addr),
844 _ => return IoResult::Err(IoError::InvalidRegister),
845 };
846
847 IoResult::Ok
848 }
849
850 fn io_write(&mut self, io_port: u16, data: &[u8]) -> IoResult {
851 match RtcIoPort(io_port) {
855 RtcIoPort::ADDR => {
856 if data[0] & 0x7F != data[0] {
857 tracing::debug!("guest tried to set high-bit in CMOS addr register")
858 }
859
860 self.state.addr = data[0] & 0x7F
861 }
862 RtcIoPort::DATA => self.set_cmos_byte(self.state.addr, data[0]),
863 _ => return IoResult::Err(IoError::InvalidRegister),
864 }
865
866 IoResult::Ok
867 }
868
869 fn get_static_regions(&mut self) -> &[(&str, RangeInclusive<u16>)] {
870 &[("io", (RtcIoPort::ADDR.0)..=(RtcIoPort::DATA.0))]
871 }
872}
873
874impl PollDevice for Rtc {
875 fn poll_device(&mut self, cx: &mut std::task::Context<'_>) {
876 while let Poll::Ready(now) = self.vmtime_alarm.poll_timeout(cx) {
877 self.on_alarm_timer(now)
878 }
879
880 if let Poll::Ready(_now) = self.vmtimer_periodic.poll_timeout(cx) {
881 self.on_periodic_timer()
882 }
883
884 if let Poll::Ready(_now) = self.vmtimer_update.poll_timeout(cx) {
885 self.on_update_timer()
886 }
887 }
888}
889
890mod save_restore {
891 use super::*;
892 use vmcore::save_restore::RestoreError;
893 use vmcore::save_restore::SaveError;
894 use vmcore::save_restore::SaveRestore;
895
896 mod state {
897 use mesh::payload::Protobuf;
898 use vmcore::save_restore::SavedStateRoot;
899
900 #[derive(Protobuf, SavedStateRoot)]
902 #[mesh(package = "chipset.cmos_rtc")]
903 pub struct SavedState {
904 #[mesh(1)]
905 pub addr: u8,
906 #[mesh(2)]
907 pub cmos: [u8; 256],
908 }
909 }
910
911 impl SaveRestore for Rtc {
912 type SavedState = state::SavedState;
913
914 fn save(&mut self) -> Result<Self::SavedState, SaveError> {
915 let RtcState { addr, ref cmos } = self.state;
916
917 let saved_state = state::SavedState { addr, cmos: cmos.0 };
918
919 Ok(saved_state)
920 }
921
922 fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
923 let state::SavedState { addr, cmos } = state;
924
925 self.state = RtcState {
926 addr,
927 cmos: CmosData(cmos),
928 };
929
930 self.update_timers();
931 self.sync_interrupt_line_level();
932
933 Ok(())
934 }
935 }
936}
937
938#[cfg(test)]
939mod tests {
940 use super::*;
941 use local_clock::MockLocalClock;
942 use local_clock::MockLocalClockAccessor;
943 use parking_lot::Mutex;
944 use std::sync::Arc;
945 use test_with_tracing::test;
946 use vmcore::line_interrupt::LineSetTarget;
947
948 #[derive(Default)]
949 struct RecordingTarget(Mutex<Vec<bool>>);
950
951 impl LineSetTarget for RecordingTarget {
952 fn set_irq(&self, _: u32, high: bool) {
953 self.0.lock().push(high);
954 }
955 }
956
957 #[test]
958 fn test_interrupt_masking() {
959 for enlightened in [false, true] {
960 for (source, other_source) in [(0x10, 0x20), (0x20, 0x40), (0x40, 0x10)] {
961 let (_, _, _, mut rtc) = new_test_rtc();
962 rtc.enlightened_interrupts = enlightened;
963 set_cmos_data(&mut rtc, CmosReg::STATUS_B, 0x06 | source);
964 let target = Arc::new(RecordingTarget::default());
965 rtc.interrupt = LineInterrupt::new_with_target("rtc", target.clone(), 0);
966 target.0.lock().clear();
967
968 let fire = |rtc: &mut Rtc| match source {
969 0x10 => rtc.on_update_timer(),
970 0x20 => rtc.on_alarm_timer(VmTime::from_100ns(0)),
971 0x40 => rtc.on_periodic_timer(),
972 _ => unreachable!(),
973 };
974 fire(&mut rtc);
975 assert_eq!(rtc.state.cmos[CmosReg::STATUS_C], source | 0x80);
976 assert_eq!(target.0.lock().last(), Some(&true));
977
978 target.0.lock().clear();
979 fire(&mut rtc);
980 if enlightened {
981 assert_eq!(*target.0.lock(), [false, true]);
982 } else {
983 assert!(!target.0.lock().contains(&false));
984 }
985
986 target.0.lock().clear();
987 set_cmos_data(&mut rtc, CmosReg::STATUS_B, 0x06);
988 assert_eq!(rtc.state.cmos[CmosReg::STATUS_C], source);
989 assert_eq!(*target.0.lock(), [false]);
990
991 target.0.lock().clear();
992 set_cmos_data(&mut rtc, CmosReg::STATUS_B, 0x06 | other_source);
993 assert_eq!(rtc.state.cmos[CmosReg::STATUS_C], source);
994 assert!(!target.0.lock().contains(&true));
995
996 set_cmos_data(&mut rtc, CmosReg::STATUS_B, 0x06 | source);
997 assert_eq!(rtc.state.cmos[CmosReg::STATUS_C], source | 0x80);
998 assert_eq!(target.0.lock().last(), Some(&true));
999
1000 target.0.lock().clear();
1001 set_cmos_data(&mut rtc, CmosReg::STATUS_B, 0x06 | source);
1002 assert!(!target.0.lock().contains(&false));
1003 assert_eq!(get_cmos_data(&mut rtc, CmosReg::STATUS_C), source | 0x80);
1004 assert_eq!(target.0.lock().last(), Some(&false));
1005 assert_eq!(get_cmos_data(&mut rtc, CmosReg::STATUS_C), 0);
1006
1007 target.0.lock().clear();
1008 set_cmos_data(&mut rtc, CmosReg::STATUS_B, 0x06 | other_source);
1009 fire(&mut rtc);
1010 assert_eq!(rtc.state.cmos[CmosReg::STATUS_C], source);
1011 assert!(!target.0.lock().contains(&true));
1012 assert_eq!(get_cmos_data(&mut rtc, CmosReg::STATUS_C), source);
1013 assert_eq!(get_cmos_data(&mut rtc, CmosReg::STATUS_C), 0);
1014 }
1015 }
1016 }
1017
1018 #[test]
1019 fn test_interrupt_mixed_sources() {
1020 for enlightened in [false, true] {
1021 let (_, _, _, mut rtc) = new_test_rtc();
1022 rtc.enlightened_interrupts = enlightened;
1023 set_cmos_data(&mut rtc, CmosReg::STATUS_B, 0x76);
1024 let target = Arc::new(RecordingTarget::default());
1025 rtc.interrupt = LineInterrupt::new_with_target("rtc", target.clone(), 0);
1026 rtc.on_periodic_timer();
1027 rtc.on_update_timer();
1028 assert_eq!(rtc.state.cmos[CmosReg::STATUS_C], 0xD0);
1029
1030 target.0.lock().clear();
1031 set_cmos_data(&mut rtc, CmosReg::STATUS_B, 0x26);
1032 assert_eq!(rtc.state.cmos[CmosReg::STATUS_C], 0x50);
1033 assert_eq!(*target.0.lock(), [false]);
1034
1035 target.0.lock().clear();
1036 set_cmos_data(&mut rtc, CmosReg::STATUS_B, 0x16);
1037 assert_eq!(rtc.state.cmos[CmosReg::STATUS_C], 0xD0);
1038 assert_eq!(*target.0.lock(), [true]);
1039
1040 target.0.lock().clear();
1041 set_cmos_data(&mut rtc, CmosReg::STATUS_B, 0x46);
1042 assert_eq!(rtc.state.cmos[CmosReg::STATUS_C], 0xD0);
1043 assert!(!target.0.lock().contains(&false));
1044 assert_eq!(get_cmos_data(&mut rtc, CmosReg::STATUS_C), 0xD0);
1045 assert_eq!(target.0.lock().last(), Some(&false));
1046 assert_eq!(get_cmos_data(&mut rtc, CmosReg::STATUS_C), 0);
1047 }
1048 }
1049
1050 #[test]
1051 fn restore_masked_pending_flag_clears_stale_irqf() {
1052 use vmcore::save_restore::SaveRestore;
1053
1054 for enlightened in [false, true] {
1055 let (mut pool, _, _, mut rtc) = new_test_rtc();
1056 rtc.enlightened_interrupts = enlightened;
1057 set_cmos_data(&mut rtc, CmosReg::STATUS_B, 0x46);
1058 rtc.on_periodic_timer();
1059 set_cmos_data(&mut rtc, CmosReg::STATUS_B, 0x06);
1060 let mut state = SaveRestore::save(&mut rtc).unwrap();
1061 state.cmos[usize::from(CmosReg::STATUS_C.0)] = 0xC0;
1062 pool.run_until(rtc.reset());
1063
1064 let target = Arc::new(RecordingTarget::default());
1065 rtc.interrupt = LineInterrupt::new_with_target("rtc", target.clone(), 0);
1066 rtc.interrupt.set_level(true);
1067 target.0.lock().clear();
1068 SaveRestore::restore(&mut rtc, state).unwrap();
1069 assert_eq!(rtc.state.cmos[CmosReg::STATUS_C], 0x40);
1070 assert_eq!(*target.0.lock(), [false]);
1071
1072 target.0.lock().clear();
1073 set_cmos_data(&mut rtc, CmosReg::STATUS_B, 0x46);
1074 assert_eq!(*target.0.lock(), [true]);
1075 assert_eq!(get_cmos_data(&mut rtc, CmosReg::STATUS_C), 0xC0);
1076 assert_eq!(get_cmos_data(&mut rtc, CmosReg::STATUS_C), 0);
1077 }
1078 }
1079
1080 #[test]
1081 fn restore_enlightened_rtc_publishes_level_without_a_pulse() {
1082 use vmcore::save_restore::SaveRestore;
1083
1084 let (mut pool, _, _, mut rtc) = new_test_rtc();
1085 rtc.enlightened_interrupts = true;
1086 set_cmos_data(&mut rtc, CmosReg::STATUS_B, 0x46);
1087 rtc.on_periodic_timer();
1088 let state = SaveRestore::save(&mut rtc).unwrap();
1089 pool.run_until(rtc.reset());
1090 let target = Arc::new(RecordingTarget::default());
1091 rtc.interrupt = LineInterrupt::new_with_target("rtc", target.clone(), 0);
1092 target.0.lock().clear();
1093 SaveRestore::restore(&mut rtc, state).unwrap();
1094 assert_eq!(*target.0.lock(), [true]);
1095
1096 target.0.lock().clear();
1097 rtc.on_periodic_timer();
1098 assert_eq!(*target.0.lock(), [false, true]);
1099 }
1100
1101 fn new_test_rtc() -> (
1102 pal_async::DefaultPool,
1103 vmcore::vmtime::VmTimeKeeper,
1104 MockLocalClockAccessor,
1105 Rtc,
1106 ) {
1107 let mut pool = pal_async::DefaultPool::new();
1108 let driver = pool.driver();
1109 let vm_time_keeper =
1110 vmcore::vmtime::VmTimeKeeper::new(&pool.driver(), VmTime::from_100ns(0));
1111 let vm_time_source = pool
1112 .run_until(vm_time_keeper.builder().build(&driver))
1113 .unwrap();
1114
1115 let time = MockLocalClock::new();
1116 let time_access = time.accessor();
1117
1118 let rtc = Rtc::new(
1119 Box::new(time),
1120 LineInterrupt::detached(),
1121 &vm_time_source,
1122 0x32,
1123 None,
1124 false,
1125 );
1126
1127 (pool, vm_time_keeper, time_access, rtc)
1128 }
1129
1130 fn get_cmos_data(rtc: &mut Rtc, addr: CmosReg) -> u8 {
1131 let mut temp = [addr.0];
1132 rtc.io_write(RtcIoPort::ADDR.0, &temp).unwrap();
1133 rtc.io_read(RtcIoPort::DATA.0, &mut temp).unwrap();
1134 temp[0]
1135 }
1136
1137 fn set_cmos_data(rtc: &mut Rtc, addr: CmosReg, data: u8) {
1138 let mut temp = [addr.0];
1139 rtc.io_write(RtcIoPort::ADDR.0, &temp).unwrap();
1140 temp[0] = data;
1141 rtc.io_write(RtcIoPort::DATA.0, &temp).unwrap();
1142 }
1143
1144 fn get_rtc_data(rtc: &mut Rtc, addr: CmosReg, bcd: bool, hour24: bool) -> u8 {
1145 let mut temp = [addr.0];
1146 rtc.io_write(RtcIoPort::ADDR.0, &temp).unwrap();
1147 rtc.io_read(RtcIoPort::DATA.0, &mut temp).unwrap();
1148 let mut data = temp[0];
1149 if addr == CmosReg::HOUR {
1150 let pm = if hour24 { false } else { (data & 0x80) != 0 };
1151 if pm {
1152 data &= 0x7f
1153 };
1154 if bcd {
1155 data = from_bcd(data)
1156 };
1157 if !hour24 && data == 12 {
1158 data = 0;
1159 }
1160 if pm {
1161 data += 12;
1162 }
1163 } else {
1164 if bcd {
1165 data = from_bcd(data)
1166 };
1167 }
1168
1169 println!("get {0}({0:#x}) convert to {1}", temp[0], data);
1170 data
1171 }
1172
1173 fn set_rtc_data(rtc: &mut Rtc, addr: CmosReg, data: u8, bcd: bool, hour24: bool) {
1174 let mut temp = [addr.0];
1175 rtc.io_write(RtcIoPort::ADDR.0, &temp).unwrap();
1176 let mut new_data = data;
1177 if addr == CmosReg::HOUR {
1178 let pm = !hour24 && new_data >= 12;
1179 if !hour24 {
1180 new_data %= 12;
1181 if new_data == 0 {
1182 new_data = 12;
1183 }
1184 }
1185 if bcd {
1186 new_data = to_bcd(new_data);
1187 }
1188 if pm {
1189 new_data |= 0x80;
1190 }
1191 } else {
1192 if bcd {
1193 new_data = to_bcd(new_data)
1194 };
1195 }
1196
1197 println!("set {0} convert to {1}({1:#x})", data, new_data);
1198 temp[0] = new_data;
1199 rtc.io_write(RtcIoPort::DATA.0, &temp).unwrap();
1200 }
1201
1202 fn wait_for_edge(rtc: &mut Rtc, high: bool, time: MockLocalClockAccessor) -> bool {
1205 let limit = 5; let stall_ms = 10; for _i in 0..(limit * 1000 / stall_ms) {
1209 let value = get_cmos_data(rtc, CmosReg::STATUS_A);
1210 if high {
1211 if value & 0x80 != 0 {
1212 return true;
1213 }
1214 } else {
1215 if value & 0x80 == 0 {
1216 return true;
1217 }
1218 }
1219
1220 time.tick(Duration::from_millis(stall_ms));
1221 }
1222
1223 false
1224 }
1225
1226 fn set_bcd(rtc: &mut Rtc) {
1227 let mut value = get_cmos_data(rtc, CmosReg::STATUS_B);
1228 value &= 0xFB;
1229 set_cmos_data(rtc, CmosReg::STATUS_B, value);
1230 }
1231
1232 fn set_binary(rtc: &mut Rtc) {
1233 let mut value = get_cmos_data(rtc, CmosReg::STATUS_B);
1234 value |= 0x4;
1235 set_cmos_data(rtc, CmosReg::STATUS_B, value);
1236 }
1237
1238 fn set_24hour(rtc: &mut Rtc) {
1239 let mut value = get_cmos_data(rtc, CmosReg::STATUS_B);
1240 value |= 0x2;
1241 set_cmos_data(rtc, CmosReg::STATUS_B, value);
1242 }
1243
1244 fn set_12hour(rtc: &mut Rtc) {
1245 let mut value = get_cmos_data(rtc, CmosReg::STATUS_B);
1246 value &= 0xFD;
1247 set_cmos_data(rtc, CmosReg::STATUS_B, value);
1248 }
1249
1250 #[test]
1251 fn test_setup() {
1252 let (_, _, _, _rtc) = new_test_rtc();
1253 }
1254
1255 #[test]
1256 fn test_default() {
1257 let default_state = RtcState::new(None);
1258
1259 let (_, _, _, mut rtc) = new_test_rtc();
1260
1261 let mut data = [0];
1262 rtc.io_read(RtcIoPort::ADDR.0, &mut data).unwrap();
1263 assert_eq!(data[0], 0x80);
1264 assert_eq!(
1265 get_cmos_data(&mut rtc, CmosReg::STATUS_A),
1266 default_state.cmos[CmosReg::STATUS_A] | 0x80
1267 );
1268 assert_eq!(
1269 get_cmos_data(&mut rtc, CmosReg::STATUS_B),
1270 default_state.cmos[CmosReg::STATUS_B]
1271 );
1272 assert_eq!(
1273 get_cmos_data(&mut rtc, CmosReg::STATUS_C),
1274 default_state.cmos[CmosReg::STATUS_C]
1275 );
1276 assert_eq!(
1277 get_cmos_data(&mut rtc, CmosReg::STATUS_D),
1278 default_state.cmos[CmosReg::STATUS_D]
1279 );
1280 }
1281
1282 fn test_time_move(rtc: &mut Rtc, is_move: bool, time: MockLocalClockAccessor) {
1283 if let Ok(before) = rtc.get_cmos_date_time() {
1284 time.tick(Duration::from_secs(2));
1285 if let Ok(after) = rtc.get_cmos_date_time() {
1286 if is_move {
1287 assert_ne!(before, after);
1288 } else {
1289 assert_eq!(before, after);
1290 }
1291 } else {
1292 panic!("get_cmos_date_time failed");
1293 }
1294 } else {
1295 panic!("get_cmos_date_time failed");
1296 }
1297 }
1298
1299 #[test]
1300 fn test_oscillator() {
1301 let default_state = RtcState::new(None);
1302
1303 let (_, _, time, mut rtc) = new_test_rtc();
1304
1305 assert_eq!(
1306 get_cmos_data(&mut rtc, CmosReg::STATUS_A),
1307 default_state.cmos[CmosReg::STATUS_A] | 0x80
1308 );
1309
1310 println!("RTC should move forward when oscillator is enabled (default control mask 010)");
1311 test_time_move(&mut rtc, true, time.clone());
1312
1313 set_cmos_data(&mut rtc, CmosReg::STATUS_A, 0x66);
1315 println!("RTC should not move forward when oscillator is disabled (control mask 110)");
1316 test_time_move(&mut rtc, false, time.clone());
1317
1318 set_cmos_data(&mut rtc, CmosReg::STATUS_A, 0x26);
1320 println!("RTC should move forward when oscillator is re-enabled (control mask 010)");
1321 test_time_move(&mut rtc, true, time);
1322 }
1323
1324 #[test]
1325 fn test_uip() {
1326 let (_, _, time, mut rtc) = new_test_rtc();
1327
1328 assert!(
1329 wait_for_edge(&mut rtc, false, time.clone())
1330 && wait_for_edge(&mut rtc, true, time.clone())
1331 );
1332 if let Ok(start) = rtc.get_cmos_date_time() {
1333 let seconds_to_wait: i64 = 65;
1334 for _i in 0..seconds_to_wait {
1335 assert!(
1336 wait_for_edge(&mut rtc, false, time.clone())
1337 && wait_for_edge(&mut rtc, true, time.clone())
1338 );
1339 }
1340
1341 if let Ok(end) = rtc.get_cmos_date_time() {
1342 let elapsed_secs =
1343 end.since(start).unwrap().total(jiff::Unit::Second).unwrap() as i64;
1344 let expected_secs = seconds_to_wait;
1345 let allowance_secs: i64 = 1;
1346 println!(
1347 "Expected: {}s Start: {:?} End: {:?} Elapsed: {}s Allowance: {}s, RTC generates update strobe at expected rate.",
1348 expected_secs, start, end, elapsed_secs, allowance_secs
1349 );
1350 assert!(
1351 elapsed_secs <= (expected_secs + allowance_secs)
1352 && elapsed_secs >= (expected_secs - allowance_secs)
1353 );
1354 } else {
1355 panic!("get_cmos_date_time failed");
1356 }
1357 } else {
1358 panic!("get_cmos_date_time failed");
1359 }
1360 }
1361
1362 #[test]
1363 fn test_readonly() {
1364 let default_state = RtcState::new(None);
1365
1366 let (_, _, _, mut rtc) = new_test_rtc();
1367
1368 assert_eq!(
1369 get_cmos_data(&mut rtc, CmosReg::STATUS_D),
1370 default_state.cmos[CmosReg::STATUS_D]
1371 );
1372
1373 for i in 0..=0xFF {
1375 set_cmos_data(&mut rtc, CmosReg::STATUS_D, i);
1376 assert_eq!(
1377 get_cmos_data(&mut rtc, CmosReg::STATUS_D),
1378 default_state.cmos[CmosReg::STATUS_D]
1379 );
1380 }
1381 }
1382
1383 #[test]
1384 fn test_writeable() {
1385 let (_, _, _, mut rtc) = new_test_rtc();
1386
1387 for i in (0x0F..=0x7F).map(CmosReg) {
1389 if i == rtc.century_reg {
1390 continue;
1391 }
1392
1393 set_cmos_data(&mut rtc, i, 0xFF);
1394 assert_eq!(get_cmos_data(&mut rtc, i), 0xFF);
1395 set_cmos_data(&mut rtc, i, 0);
1396 assert_eq!(get_cmos_data(&mut rtc, i), 0);
1397 }
1398 }
1399
1400 fn test_count(bcd: bool, hour24: bool) {
1401 let (_, _, time, mut rtc) = new_test_rtc();
1402
1403 if bcd {
1404 set_bcd(&mut rtc);
1405 } else {
1406 set_binary(&mut rtc);
1407 }
1408
1409 if hour24 {
1410 set_24hour(&mut rtc);
1411 } else {
1412 set_12hour(&mut rtc);
1413 }
1414
1415 let init_ts: jiff::Timestamp = time.get_time().try_into().unwrap();
1416 let init = init_ts.to_zoned(jiff::tz::TimeZone::UTC).datetime();
1417 println!("init: {:?}", init);
1418 set_rtc_data(&mut rtc, CmosReg::HOUR, 11, bcd, hour24);
1419 set_rtc_data(&mut rtc, CmosReg::MINUTE, 59, bcd, hour24);
1420 set_rtc_data(&mut rtc, CmosReg::SECOND, 59, bcd, hour24);
1421 time.tick(Duration::from_secs(2));
1422 assert_eq!(get_rtc_data(&mut rtc, CmosReg::SECOND, bcd, hour24), 1);
1423 assert_eq!(get_rtc_data(&mut rtc, CmosReg::MINUTE, bcd, hour24), 0);
1424 assert_eq!(get_rtc_data(&mut rtc, CmosReg::HOUR, bcd, hour24), 12);
1425 assert_eq!(
1426 get_rtc_data(&mut rtc, CmosReg::DAY_OF_MONTH, bcd, hour24),
1427 init.day() as u8
1428 );
1429 assert_eq!(
1430 get_rtc_data(&mut rtc, CmosReg::DAY_OF_WEEK, bcd, hour24),
1431 init.date().weekday().to_sunday_one_offset() as u8
1432 );
1433
1434 set_rtc_data(&mut rtc, CmosReg::HOUR, 23, bcd, hour24);
1435 set_rtc_data(&mut rtc, CmosReg::MINUTE, 59, bcd, hour24);
1436 set_rtc_data(&mut rtc, CmosReg::SECOND, 59, bcd, hour24);
1437 time.tick(Duration::from_secs(2));
1438 assert_eq!(get_rtc_data(&mut rtc, CmosReg::SECOND, bcd, hour24), 1);
1439 assert_eq!(get_rtc_data(&mut rtc, CmosReg::MINUTE, bcd, hour24), 0);
1440 assert_eq!(get_rtc_data(&mut rtc, CmosReg::HOUR, bcd, hour24), 0);
1441 let temp = init_ts
1442 .checked_add(jiff::SignedDuration::from_hours(24))
1443 .unwrap()
1444 .to_zoned(jiff::tz::TimeZone::UTC)
1445 .datetime();
1446 assert_eq!(
1447 get_rtc_data(&mut rtc, CmosReg::DAY_OF_MONTH, bcd, hour24),
1448 temp.day() as u8
1449 );
1450 assert_eq!(
1451 get_rtc_data(&mut rtc, CmosReg::DAY_OF_WEEK, bcd, hour24),
1452 temp.date().weekday().to_sunday_one_offset() as u8
1453 );
1454
1455 set_rtc_data(&mut rtc, CmosReg::MINUTE, 59, bcd, hour24);
1456 set_rtc_data(&mut rtc, CmosReg::SECOND, 59, bcd, hour24);
1457 time.tick(Duration::from_secs(2));
1458 assert_eq!(get_rtc_data(&mut rtc, CmosReg::SECOND, bcd, hour24), 1);
1459 assert_eq!(get_rtc_data(&mut rtc, CmosReg::MINUTE, bcd, hour24), 0);
1460 assert_eq!(get_rtc_data(&mut rtc, CmosReg::HOUR, bcd, hour24), 1);
1461 }
1462
1463 #[test]
1464 fn test_bcd_binary() {
1465 println!("----Testing BCD mode...");
1466 test_count(true, true);
1467 println!("----Testing Binary mode...");
1468 test_count(false, true);
1469 }
1470
1471 #[test]
1472 fn test_hour_encodings() {
1473 let (_, _, _, mut rtc) = new_test_rtc();
1474 let hours = [
1475 (0, 0x12, 0x0C),
1476 (1, 0x01, 0x01),
1477 (2, 0x02, 0x02),
1478 (3, 0x03, 0x03),
1479 (4, 0x04, 0x04),
1480 (5, 0x05, 0x05),
1481 (6, 0x06, 0x06),
1482 (7, 0x07, 0x07),
1483 (8, 0x08, 0x08),
1484 (9, 0x09, 0x09),
1485 (10, 0x10, 0x0A),
1486 (11, 0x11, 0x0B),
1487 (12, 0x92, 0x8C),
1488 (13, 0x81, 0x81),
1489 (14, 0x82, 0x82),
1490 (15, 0x83, 0x83),
1491 (16, 0x84, 0x84),
1492 (17, 0x85, 0x85),
1493 (18, 0x86, 0x86),
1494 (19, 0x87, 0x87),
1495 (20, 0x88, 0x88),
1496 (21, 0x89, 0x89),
1497 (22, 0x90, 0x8A),
1498 (23, 0x91, 0x8B),
1499 ];
1500
1501 for status in [0x00, 0x02, 0x04, 0x06] {
1502 set_cmos_data(&mut rtc, CmosReg::STATUS_B, status);
1503 for (hour, bcd_hour, binary_hour) in hours {
1504 let timestamp: jiff::Timestamp =
1505 format!("2026-01-01T{hour:02}:00:00Z").parse().unwrap();
1506 rtc.real_time_source.set_time(timestamp.into());
1507 let expected = match status {
1508 0x00 => bcd_hour,
1509 0x02 => to_bcd(hour),
1510 0x04 => binary_hour,
1511 0x06 => hour,
1512 _ => unreachable!(),
1513 };
1514 assert_eq!(
1515 get_cmos_data(&mut rtc, CmosReg::HOUR),
1516 expected,
1517 "hour {hour}, status B {status:#04x}"
1518 );
1519 }
1520 }
1521 }
1522
1523 #[test]
1524 fn test_hour_encoding_transitions() {
1525 let (_, _, time, mut rtc) = new_test_rtc();
1526
1527 for (mode, status) in [0x00, 0x02, 0x04, 0x06].into_iter().enumerate() {
1528 set_cmos_data(&mut rtc, CmosReg::STATUS_B, status);
1529 for (start_hour, expected_hours, expected_day) in [
1530 (11, [0x92, 0x12, 0x8C, 0x0C], 1),
1531 (23, [0x12, 0x00, 0x0C, 0x00], 2),
1532 ] {
1533 let timestamp: jiff::Timestamp = format!("2026-01-01T{start_hour:02}:59:59Z")
1534 .parse()
1535 .unwrap();
1536 rtc.real_time_source.set_time(timestamp.into());
1537 time.tick(Duration::from_secs(2));
1538
1539 assert_eq!(
1540 get_cmos_data(&mut rtc, CmosReg::HOUR),
1541 expected_hours[mode],
1542 "start hour {start_hour}, status B {status:#04x}"
1543 );
1544 assert_eq!(get_cmos_data(&mut rtc, CmosReg::DAY_OF_MONTH), expected_day);
1545 assert_eq!(get_cmos_data(&mut rtc, CmosReg::MINUTE), 0);
1546 assert_eq!(get_cmos_data(&mut rtc, CmosReg::SECOND), 1);
1547 }
1548 }
1549 }
1550
1551 #[test]
1552 fn test_hour_inputs() {
1553 let (_, _, time, mut rtc) = new_test_rtc();
1554
1555 for (status, midnight_hour) in [(0x00, 0x12), (0x04, 0x0C)] {
1556 for (pm_bit, expected_hour) in [(0x00, 0), (0x80, 12)] {
1557 set_cmos_data(&mut rtc, CmosReg::STATUS_B, status);
1558 let timestamp: jiff::Timestamp = "2026-01-01T06:00:00Z".parse().unwrap();
1559 rtc.real_time_source.set_time(timestamp.into());
1560 rtc.sync_clock_to_cmos();
1561 set_cmos_data(&mut rtc, CmosReg::STATUS_A, 0x06);
1562 let encoded_hour = midnight_hour | pm_bit;
1563 set_cmos_data(&mut rtc, CmosReg::HOUR, encoded_hour);
1564
1565 let timestamp: jiff::Timestamp = time.get_time().try_into().unwrap();
1566 assert_eq!(
1567 timestamp.to_zoned(jiff::tz::TimeZone::UTC).hour(),
1568 expected_hour,
1569 "hour {encoded_hour:#04x}, status B {status:#04x}"
1570 );
1571
1572 set_cmos_data(&mut rtc, CmosReg::STATUS_A, 0x26);
1573 time.tick(Duration::from_secs(2));
1574 assert_eq!(get_cmos_data(&mut rtc, CmosReg::HOUR), encoded_hour);
1575 assert_eq!(get_cmos_data(&mut rtc, CmosReg::SECOND), 2);
1576 time.tick(Duration::from_secs(3600));
1577 assert_eq!(get_cmos_data(&mut rtc, CmosReg::HOUR), 0x01 | pm_bit);
1578 assert_eq!(get_cmos_data(&mut rtc, CmosReg::DAY_OF_MONTH), 1);
1579 }
1580 }
1581 }
1582
1583 #[test]
1584 fn test_hour_alarms() {
1585 let (_, _, _, mut rtc) = new_test_rtc();
1586
1587 for (mode, status) in [0x00, 0x02, 0x04, 0x06].into_iter().enumerate() {
1588 set_cmos_data(&mut rtc, CmosReg::STATUS_B, status);
1589 for (start_hour, alarm_hours) in [
1590 (11, [0x92, 0x12, 0x8C, 0x0C]),
1591 (23, [0x12, 0x00, 0x0C, 0x00]),
1592 (12, [0x81, 0x13, 0x81, 0x0D]),
1593 (0, [0x01, 0x01, 0x01, 0x01]),
1594 ] {
1595 let timestamp: jiff::Timestamp = format!("2026-01-01T{start_hour:02}:59:59Z")
1596 .parse()
1597 .unwrap();
1598 rtc.real_time_source.set_time(timestamp.into());
1599 rtc.sync_clock_to_cmos();
1600 set_cmos_data(&mut rtc, CmosReg::HOUR_ALARM, alarm_hours[mode]);
1601 set_cmos_data(&mut rtc, CmosReg::MINUTE_ALARM, 0);
1602 set_cmos_data(&mut rtc, CmosReg::SECOND_ALARM, 0);
1603
1604 assert_eq!(
1605 rtc.calculate_alarm_duration(),
1606 Duration::from_secs(1),
1607 "start hour {start_hour}, status B {status:#04x}"
1608 );
1609 }
1610 }
1611 }
1612
1613 #[test]
1614 fn test_hour_mode() {
1615 println!("----Testing Binary + 12 hour mode...");
1616 test_count(false, false);
1617 println!("----Testing Binary + 24 hour mode...");
1618 test_count(false, true);
1619 println!("----Testing BCD + 12 hour mode...");
1620 test_count(true, false);
1621 println!("----Testing BCD + 24 hour mode...");
1622 test_count(true, true);
1623 }
1624
1625 fn test_day_of_month(bcd: bool, hour24: bool) {
1626 let century_reg_idx = 0x32;
1627 let century_reg = CmosReg(century_reg_idx);
1628
1629 let (_, _, time, mut rtc) = new_test_rtc();
1630
1631 if bcd {
1632 set_bcd(&mut rtc);
1633 } else {
1634 set_binary(&mut rtc);
1635 }
1636
1637 if hour24 {
1638 set_24hour(&mut rtc);
1639 } else {
1640 set_12hour(&mut rtc);
1641 }
1642
1643 for month in 1..=12 {
1644 let mut day;
1645 let mut year_check_count = 1;
1646 if month == 4 || month == 6 || month == 9 || month == 11 {
1647 day = 30;
1648 } else if month != 2 {
1649 day = 31;
1650 } else {
1651 day = 0;
1652 year_check_count = 4;
1653 }
1654
1655 while year_check_count > 0 {
1656 let century = 30;
1657 let year = 4 + year_check_count;
1658 if month == 2 {
1659 day = if (year & 3) > 0 { 28 } else { 29 };
1660 }
1661
1662 println!(
1663 "----Testing {:02}{:02}-{:02}-{:02}",
1664 century, year, month, day
1665 );
1666 set_rtc_data(&mut rtc, century_reg, century, bcd, hour24);
1667 set_rtc_data(&mut rtc, CmosReg::YEAR, year, bcd, hour24);
1668 set_rtc_data(&mut rtc, CmosReg::MONTH, month, bcd, hour24);
1669 set_rtc_data(&mut rtc, CmosReg::DAY_OF_MONTH, day, bcd, hour24);
1670 set_rtc_data(&mut rtc, CmosReg::HOUR, 23, bcd, hour24);
1671 set_rtc_data(&mut rtc, CmosReg::MINUTE, 59, bcd, hour24);
1672 set_rtc_data(&mut rtc, CmosReg::SECOND, 59, bcd, hour24);
1673 time.tick(Duration::from_secs(2));
1674 assert_eq!(get_rtc_data(&mut rtc, CmosReg::SECOND, bcd, hour24), 1);
1675 assert_eq!(get_rtc_data(&mut rtc, CmosReg::MINUTE, bcd, hour24), 0);
1676 assert_eq!(get_rtc_data(&mut rtc, CmosReg::HOUR, bcd, hour24), 0);
1677 assert_eq!(
1678 get_rtc_data(&mut rtc, CmosReg::DAY_OF_MONTH, bcd, hour24),
1679 1
1680 );
1681 assert_eq!(
1682 get_rtc_data(&mut rtc, CmosReg::MONTH, bcd, hour24),
1683 if month == 12 { 1 } else { month + 1 }
1684 );
1685 assert_eq!(
1686 get_rtc_data(&mut rtc, CmosReg::YEAR, bcd, hour24),
1687 if month < 12 { year } else { year + 1 }
1688 );
1689 assert_eq!(get_rtc_data(&mut rtc, century_reg, bcd, hour24), century);
1690 year_check_count -= 1;
1691 }
1692 }
1693 }
1694
1695 #[test]
1696 fn test_month() {
1697 println!("----Testing BCD mode...");
1698 test_day_of_month(true, true);
1699 println!("----Testing Binary mode...");
1700 test_day_of_month(false, true);
1701 }
1702
1703 fn test_day_of_week(bcd: bool, hour24: bool) {
1704 let century_reg_idx = 0x32;
1705 let century_reg = CmosReg(century_reg_idx);
1706
1707 let (_, _, time, mut rtc) = new_test_rtc();
1708
1709 if bcd {
1710 set_bcd(&mut rtc);
1711 } else {
1712 set_binary(&mut rtc);
1713 }
1714
1715 if hour24 {
1716 set_24hour(&mut rtc);
1717 } else {
1718 set_12hour(&mut rtc);
1719 }
1720
1721 let century = 30;
1722 let year = 5;
1723 let month = 1;
1724 let day = 5;
1725
1726 println!(
1727 "----Testing {:02}{:02}-{:02}-{:02}",
1728 century, year, month, day
1729 );
1730 set_rtc_data(&mut rtc, century_reg, century, bcd, hour24);
1731 set_rtc_data(&mut rtc, CmosReg::YEAR, year, bcd, hour24);
1732 set_rtc_data(&mut rtc, CmosReg::MONTH, month, bcd, hour24);
1733 set_rtc_data(&mut rtc, CmosReg::DAY_OF_MONTH, day, bcd, hour24);
1734 set_rtc_data(&mut rtc, CmosReg::DAY_OF_WEEK, 7, bcd, hour24);
1735 set_rtc_data(&mut rtc, CmosReg::HOUR, 23, bcd, hour24);
1736 set_rtc_data(&mut rtc, CmosReg::MINUTE, 59, bcd, hour24);
1737 set_rtc_data(&mut rtc, CmosReg::SECOND, 59, bcd, hour24);
1738 time.tick(Duration::from_secs(2));
1739 assert_eq!(get_rtc_data(&mut rtc, CmosReg::SECOND, bcd, hour24), 1);
1740 assert_eq!(get_rtc_data(&mut rtc, CmosReg::MINUTE, bcd, hour24), 0);
1741 assert_eq!(get_rtc_data(&mut rtc, CmosReg::HOUR, bcd, hour24), 0);
1742 assert_eq!(get_rtc_data(&mut rtc, CmosReg::DAY_OF_WEEK, bcd, hour24), 1);
1743
1744 set_rtc_data(&mut rtc, CmosReg::DAY_OF_MONTH, day - 1, bcd, hour24);
1745 set_rtc_data(&mut rtc, CmosReg::DAY_OF_WEEK, 6, bcd, hour24);
1746 set_rtc_data(&mut rtc, CmosReg::HOUR, 23, bcd, hour24);
1747 set_rtc_data(&mut rtc, CmosReg::MINUTE, 59, bcd, hour24);
1748 set_rtc_data(&mut rtc, CmosReg::SECOND, 59, bcd, hour24);
1749 time.tick(Duration::from_secs(2));
1750 assert_eq!(get_rtc_data(&mut rtc, CmosReg::SECOND, bcd, hour24), 1);
1751 assert_eq!(get_rtc_data(&mut rtc, CmosReg::MINUTE, bcd, hour24), 0);
1752 assert_eq!(get_rtc_data(&mut rtc, CmosReg::HOUR, bcd, hour24), 0);
1753 assert_eq!(get_rtc_data(&mut rtc, CmosReg::DAY_OF_WEEK, bcd, hour24), 7);
1754 }
1755
1756 #[test]
1757 fn test_week() {
1758 println!("----Testing Binary mode...");
1759 test_day_of_week(false, true);
1760 println!("----Testing BCD mode...");
1761 test_day_of_week(true, true);
1762 }
1763
1764 #[test]
1765 fn test_alarm_next_match() {
1766 let (_, _, _, mut rtc) = new_test_rtc();
1767
1768 for status in [0x00, 0x02, 0x04, 0x06] {
1769 set_cmos_data(&mut rtc, CmosReg::STATUS_B, status);
1770 for (time, alarm, delays) in [
1771 ("12:30:59", [12, 30, 0], [86341, 86400]),
1772 ("12:30:58", [12, 30, 0xFF], [1, 86341]),
1773 ("12:30:59", [12, 30, 0xFF], [86341, 1]),
1774 ("12:31:00", [12, 30, 0xFF], [86340, 1]),
1775 ("12:59:00", [12, 0xFF, 0], [82860, 60]),
1776 ("12:30:59", [0xFF, 30, 0], [3541, 3600]),
1777 ("12:30:59", [0xFF, 30, 0xFF], [3541, 1]),
1778 ("12:30:59", [0xFF, 0xFF, 0], [1, 60]),
1779 ("12:30:59", [0xFF, 0xFF, 0xFF], [1, 1]),
1780 ("12:59:59", [12, 0xFF, 0xFF], [82801, 1]),
1781 ("12:30:00", [12, 30, 0], [86400, 86400]),
1782 ("23:59:59", [0, 0, 0], [1, 86400]),
1783 ("11:59:59", [12, 0, 0], [1, 86400]),
1784 ("12:59:59", [13, 0, 0], [1, 86400]),
1785 ("12:30:59", [0xFF, 31, 59], [60, 3600]),
1786 ("12:22:33", [14, 0xFF, 0xFF], [5847, 1]),
1787 ("12:22:33", [0xFF, 23, 0xFF], [27, 1]),
1788 ("23:59:59", [23, 59, 59], [86400, 86400]),
1789 ] {
1790 let encode = |value: u8| {
1791 if value == 0xFF || status & 4 != 0 {
1792 value
1793 } else {
1794 (value / 10) * 16 + value % 10
1795 }
1796 };
1797 let hour = if alarm[0] == 0xFF || status & 2 != 0 {
1798 encode(alarm[0])
1799 } else {
1800 let magnitude = match alarm[0] % 12 {
1801 0 => 12,
1802 magnitude => magnitude,
1803 };
1804 encode(magnitude) | if alarm[0] >= 12 { 0x80 } else { 0 }
1805 };
1806 set_cmos_data(&mut rtc, CmosReg::HOUR_ALARM, hour);
1807 set_cmos_data(&mut rtc, CmosReg::MINUTE_ALARM, encode(alarm[1]));
1808 set_cmos_data(&mut rtc, CmosReg::SECOND_ALARM, encode(alarm[2]));
1809
1810 let mut timestamp: jiff::Timestamp = format!("2026-01-01T{time}Z").parse().unwrap();
1811 for expected_seconds in delays {
1812 rtc.real_time_source.set_time(timestamp.into());
1813 rtc.sync_clock_to_cmos();
1814 assert_eq!(
1815 rtc.calculate_alarm_duration(),
1816 Duration::from_secs(expected_seconds),
1817 "status B {status:#04x}, now {timestamp}, alarm {alarm:?}"
1818 );
1819 timestamp = timestamp
1820 .checked_add(Duration::from_secs(expected_seconds))
1821 .unwrap();
1822 }
1823 }
1824 }
1825 }
1826
1827 #[test]
1828 fn test_alarm_invalid_fields() {
1829 let (_, _, _, mut rtc) = new_test_rtc();
1830 for status in [0x00, 0x02, 0x04, 0x06] {
1831 set_cmos_data(&mut rtc, CmosReg::STATUS_B, status);
1832 let timestamp: jiff::Timestamp = "2026-01-01T12:00:00Z".parse().unwrap();
1833 rtc.real_time_source.set_time(timestamp.into());
1834 rtc.sync_clock_to_cmos();
1835 for invalid_field in 0..3 {
1836 for (field, register) in [
1837 CmosReg::HOUR_ALARM,
1838 CmosReg::MINUTE_ALARM,
1839 CmosReg::SECOND_ALARM,
1840 ]
1841 .into_iter()
1842 .enumerate()
1843 {
1844 set_cmos_data(
1845 &mut rtc,
1846 register,
1847 if field == invalid_field { 0x7F } else { 0xFF },
1848 );
1849 }
1850 assert_eq!(rtc.calculate_alarm_duration(), Duration::from_hours(24));
1851 }
1852 }
1853 }
1854
1855 #[test]
1856 fn test_alarm_wildcards() {
1857 let (_, _, _, mut rtc) = new_test_rtc();
1858
1859 for (status, hour, minute, second) in [
1860 (0x00, 0x82, 0x01, 0x01),
1861 (0x02, 0x14, 0x01, 0x01),
1862 (0x04, 0x82, 1, 1),
1863 (0x06, 14, 1, 1),
1864 ] {
1865 set_cmos_data(&mut rtc, CmosReg::STATUS_B, status);
1866 let timestamp: jiff::Timestamp = "2026-01-01T13:00:00Z".parse().unwrap();
1867 rtc.real_time_source.set_time(timestamp.into());
1868 rtc.sync_clock_to_cmos();
1869
1870 for wildcard in 0xC0..=0xFF {
1871 for (mask, expected_seconds) in [
1872 (0, 3661),
1873 (1, 3660),
1874 (2, 3601),
1875 (3, 3600),
1876 (4, 61),
1877 (5, 60),
1878 (6, 1),
1879 (7, 1),
1880 ] {
1881 let alarm_hour = if mask & 4 != 0 { wildcard } else { hour };
1882 let alarm_minute = if mask & 2 != 0 { wildcard } else { minute };
1883 let alarm_second = if mask & 1 != 0 { wildcard } else { second };
1884 set_cmos_data(&mut rtc, CmosReg::HOUR_ALARM, alarm_hour);
1885 set_cmos_data(&mut rtc, CmosReg::MINUTE_ALARM, alarm_minute);
1886 set_cmos_data(&mut rtc, CmosReg::SECOND_ALARM, alarm_second);
1887
1888 assert_eq!(
1889 rtc.calculate_alarm_duration(),
1890 Duration::from_secs(expected_seconds),
1891 "status B {status:#04x}, wildcard {wildcard:#04x}, mask {mask:#05b}"
1892 );
1893 assert_eq!(get_cmos_data(&mut rtc, CmosReg::HOUR_ALARM), alarm_hour);
1894 assert_eq!(get_cmos_data(&mut rtc, CmosReg::MINUTE_ALARM), alarm_minute);
1895 assert_eq!(get_cmos_data(&mut rtc, CmosReg::SECOND_ALARM), alarm_second);
1896 }
1897 }
1898 }
1899 }
1900
1901 #[test]
1902 fn test_alarm() {
1903 fn hms_to_duration(h: u8, m: u8, s: u8) -> Duration {
1904 Duration::from_secs(s as u64 + 60 * m as u64 + h as u64 * 60 * 60)
1905 }
1906
1907 let (_, _, _time, mut rtc) = new_test_rtc();
1908
1909 set_binary(&mut rtc);
1910
1911 set_cmos_data(&mut rtc, CmosReg::HOUR, 2);
1912 set_cmos_data(&mut rtc, CmosReg::MINUTE, 2);
1913 set_cmos_data(&mut rtc, CmosReg::SECOND, 2);
1914
1915 set_cmos_data(&mut rtc, CmosReg::HOUR_ALARM, 3);
1916 set_cmos_data(&mut rtc, CmosReg::MINUTE_ALARM, 3);
1917 set_cmos_data(&mut rtc, CmosReg::SECOND_ALARM, 3);
1918
1919 assert_eq!(rtc.calculate_alarm_duration(), hms_to_duration(1, 1, 1));
1920 set_cmos_data(&mut rtc, CmosReg::HOUR_ALARM, 0xff);
1921 set_cmos_data(&mut rtc, CmosReg::MINUTE_ALARM, 0xff);
1922 set_cmos_data(&mut rtc, CmosReg::SECOND_ALARM, 0xff);
1923 assert_eq!(rtc.calculate_alarm_duration(), hms_to_duration(0, 0, 1));
1924
1925 }
1927}