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.update_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 hour_ampm = from_bcd(hour & 0x7F);
268 if hour & 0x80 != 0 {
269 hour = hour_ampm + 12;
270 } else {
271 hour = hour_ampm
272 }
273 }
274 } else {
275 if !status_b.h24_mode() {
276 if hour & 0x80 != 0 {
277 hour = (hour & 0x7F) + 12;
278 }
279 }
280 }
281
282 (hour, min, sec)
283}
284
285impl Rtc {
286 pub fn new(
303 real_time_source: Box<dyn InspectableLocalClock>,
304 interrupt: LineInterrupt,
305 vmtime_source: &VmTimeSource,
306 century_reg_idx: u8,
307 initial_cmos: Option<[u8; 256]>,
308 enlightened_interrupts: bool,
309 ) -> Self {
310 Rtc {
311 century_reg: CmosReg(century_reg_idx),
312 initial_cmos,
313 enlightened_interrupts,
314
315 real_time_source,
316 interrupt,
317 vmtime_alarm: vmtime_source.access("rtc-alarm"),
318 vmtimer_periodic: VmTimerPeriodic::new(vmtime_source.access("rtc-periodic")),
319 vmtimer_update: VmTimerPeriodic::new(vmtime_source.access("rtc-update")),
320
321 last_update_bit_blip: LocalClockTime::from_millis_since_unix_epoch(0),
322
323 state: RtcState::new(initial_cmos),
324 }
325 }
326
327 pub fn raw_cmos(&mut self) -> &mut [u8; 256] {
329 &mut self.state.cmos.0
330 }
331
332 fn calculate_alarm_duration(&self) -> Duration {
366 use self::spec::ALARM_WILDCARD;
367
368 let status_b = StatusRegB::from(self.state.cmos[CmosReg::STATUS_B]);
369 let (now_hour, now_min, now_sec) = canonical_hms(
370 status_b,
371 self.state.cmos[CmosReg::HOUR],
372 self.state.cmos[CmosReg::MINUTE],
373 self.state.cmos[CmosReg::SECOND],
374 );
375 let (alarm_hour, alarm_min, alarm_sec) = canonical_hms(
376 status_b,
377 self.state.cmos[CmosReg::HOUR_ALARM],
378 self.state.cmos[CmosReg::MINUTE_ALARM],
379 self.state.cmos[CmosReg::SECOND_ALARM],
380 );
381
382 let mut delta_hour: u8 = 0;
383 let mut delta_min: u8 = 0;
384 let mut delta_sec: u8 = 0;
385
386 if alarm_sec == ALARM_WILDCARD {
387 delta_sec = 0;
388 } else {
389 delta_sec = delta_sec.wrapping_add(alarm_sec.wrapping_sub(now_sec));
390 if alarm_sec < now_sec {
391 delta_sec = delta_sec.wrapping_add(60);
392 delta_min = delta_min.wrapping_sub(1);
393 }
394 }
395
396 if alarm_min == ALARM_WILDCARD {
397 delta_min = 0;
398 } else {
399 delta_min = delta_min.wrapping_add(alarm_min.wrapping_sub(now_min));
400 if alarm_min < now_min {
401 delta_min = delta_min.wrapping_add(60);
402 delta_hour = delta_hour.wrapping_sub(1);
403 }
404 }
405
406 if alarm_hour == ALARM_WILDCARD {
407 delta_hour = 0;
408 } else {
409 delta_hour = delta_hour.wrapping_add(alarm_hour.wrapping_sub(now_hour));
410 if alarm_hour < now_hour {
411 delta_hour = delta_hour.wrapping_add(24);
412 }
413 }
414
415 const DURATION_SEC: Duration = Duration::from_secs(1);
416 const DURATION_MIN: Duration = Duration::from_secs(60);
417 const DURATION_HOUR: Duration = Duration::from_secs(60 * 60);
418 const DURATION_DAY: Duration = Duration::from_secs(60 * 60 * 24);
419
420 let go_around_again = match (alarm_sec, alarm_min, alarm_hour) {
421 (ALARM_WILDCARD, _, _) => DURATION_SEC,
422 (_, ALARM_WILDCARD, _) => DURATION_MIN,
423 (_, _, ALARM_WILDCARD) => DURATION_HOUR,
424 _ => DURATION_DAY,
426 };
427
428 let alarm_duration = {
429 DURATION_HOUR * delta_hour as u32
430 + DURATION_MIN * delta_min as u32
431 + DURATION_SEC * delta_sec as u32
432 };
433
434 tracing::debug!(
435 now = ?(now_hour, now_min, now_sec),
436 alarm = ?(alarm_hour, alarm_min, alarm_sec),
437 delta = ?(delta_hour, delta_min, delta_sec),
438 ?go_around_again,
439 ?alarm_duration,
440 "setting alarm"
441 );
442
443 if alarm_duration.is_zero() {
444 go_around_again
445 } else {
446 alarm_duration
447 }
448 }
449
450 fn set_alarm_timer(&mut self, now: VmTime) {
451 self.sync_clock_to_cmos();
452 let alarm_duration = self.calculate_alarm_duration();
453
454 self.vmtime_alarm
455 .set_timeout(now.wrapping_add(alarm_duration));
456 }
457
458 fn on_alarm_timer(&mut self, now: VmTime) {
459 let status_c = StatusRegC::from(self.state.cmos[CmosReg::STATUS_C]);
460 self.state.cmos[CmosReg::STATUS_C] =
461 status_c.with_irq_alarm(true).with_irq_combined(true).into();
462
463 self.update_interrupt_line_level();
464
465 self.set_alarm_timer(now)
467 }
468
469 fn set_periodic_timer(&mut self) {
470 use self::spec::PERIODIC_TIMER_RATE_HZ;
471
472 let status_a = StatusRegA::from(self.state.cmos[CmosReg::STATUS_A]);
473
474 if status_a.periodic_timer_rate() == 0 {
476 return;
477 }
478
479 let tick_hz = PERIODIC_TIMER_RATE_HZ[status_a.periodic_timer_rate() as usize - 1];
480 let tick_period = Duration::from_secs_f32(1. / tick_hz as f32);
481
482 tracing::debug!(
483 periodic_timer_rate = ?status_a.periodic_timer_rate(),
484 ?tick_hz,
485 ?tick_period,
486 "setting periodic timer"
487 );
488
489 self.vmtimer_periodic.start(tick_period);
490 }
491
492 fn on_periodic_timer(&mut self) {
493 let status_c = StatusRegC::from(self.state.cmos[CmosReg::STATUS_C]);
494 self.state.cmos[CmosReg::STATUS_C] = status_c
495 .with_irq_periodic(true)
496 .with_irq_combined(true)
497 .into();
498
499 self.update_interrupt_line_level();
500 }
501
502 fn set_update_timer(&mut self) {
503 self.vmtimer_update.start(Duration::from_secs(1));
504 }
505
506 fn on_update_timer(&mut self) {
507 let status_c = StatusRegC::from(self.state.cmos[CmosReg::STATUS_C]);
508 self.state.cmos[CmosReg::STATUS_C] = status_c
509 .with_irq_update(true)
510 .with_irq_combined(true)
511 .into();
512
513 self.update_interrupt_line_level();
514 }
515
516 fn update_interrupt_line_level(&self) {
519 let status_c = StatusRegC::from(self.state.cmos[CmosReg::STATUS_C]);
520
521 if status_c.irq_update() || status_c.irq_periodic() || status_c.irq_alarm() {
522 assert!(status_c.irq_combined());
523 if self.enlightened_interrupts {
524 self.interrupt.set_level(false);
525 }
526 self.interrupt.set_level(true);
527 } else {
528 assert!(!status_c.irq_combined());
529 self.interrupt.set_level(false);
530 }
531 }
532
533 fn update_timers(&mut self) {
536 let status_b = StatusRegB::from(self.state.cmos[CmosReg::STATUS_B]);
537
538 if status_b.irq_enable_alarm() {
539 if self.vmtime_alarm.get_timeout().is_none() {
540 self.set_alarm_timer(self.vmtime_alarm.now())
541 }
542 } else {
543 self.vmtime_alarm.cancel_timeout();
544 }
545
546 if status_b.irq_enable_periodic() {
547 if !self.vmtimer_periodic.is_running() {
548 self.set_periodic_timer()
549 }
550 } else {
551 self.vmtimer_periodic.cancel();
552 }
553
554 if status_b.irq_enable_update() {
555 if !self.vmtimer_update.is_running() {
556 self.set_update_timer()
557 }
558 } else {
559 self.vmtimer_update.cancel();
560 }
561 }
562
563 pub fn set_cmos_byte(&mut self, addr: u8, data: u8) {
569 let addr = CmosReg(addr);
570
571 tracing::trace!(?addr, ?data, "set_cmos_byte");
572
573 if (CmosReg::STATUS_A..=CmosReg::STATUS_D).contains(&addr) {
574 self.set_status_byte(addr, data);
575 } else {
576 let old_data = self.state.cmos[addr];
577
578 if data != old_data {
579 if addr.depends_on_rtc(self.century_reg) {
581 self.sync_clock_to_cmos();
582 }
583
584 self.state.cmos[addr] = data;
585
586 if addr.depends_on_rtc(self.century_reg) {
588 self.sync_cmos_to_clock();
589 }
590
591 if addr.depends_on_alarm() || addr.depends_on_rtc(self.century_reg) {
592 if self.vmtime_alarm.get_timeout().is_some() {
593 self.set_alarm_timer(self.vmtime_alarm.now());
594 }
595 }
596 }
597 }
598 }
599
600 pub fn get_cmos_byte(&mut self, addr: u8) -> u8 {
606 let addr = CmosReg(addr);
607
608 let data = if (CmosReg::STATUS_A..=CmosReg::STATUS_D).contains(&addr) {
609 self.get_status_byte(addr)
610 } else {
611 if addr.depends_on_rtc(self.century_reg) {
612 self.sync_clock_to_cmos();
613 }
614
615 self.state.cmos[addr]
616 };
617
618 tracing::trace!(?addr, ?data, "get_cmos_byte");
619
620 data
621 }
622
623 fn set_status_byte(&mut self, addr: CmosReg, data: u8) {
624 match addr {
625 CmosReg::STATUS_A => {
626 let new_reg = StatusRegA::from(data);
627 let old_reg = StatusRegA::from(self.state.cmos[CmosReg::STATUS_A]);
628
629 if new_reg.oscillator_control() != old_reg.oscillator_control() {
631 self.vmtime_alarm.cancel_timeout();
633 }
634
635 if new_reg.periodic_timer_rate() != old_reg.periodic_timer_rate() {
636 self.vmtimer_periodic.cancel();
638 }
639
640 self.state.cmos[CmosReg::STATUS_A] = data & 0x7F;
642
643 self.update_timers();
644 }
645 CmosReg::STATUS_B => {
646 let mut new_reg = StatusRegB::from(data);
647
648 if new_reg.set() {
650 tracing::debug!("disable timer update and interrupt");
651 new_reg.set_irq_enable_update(false)
652 }
653
654 self.state.cmos[CmosReg::STATUS_B] = new_reg.into();
655
656 self.update_timers();
657 }
658 CmosReg::STATUS_C | CmosReg::STATUS_D => {}
661 _ => unreachable!("passed invalid status reg"),
662 }
663 }
664
665 fn get_status_byte(&mut self, addr: CmosReg) -> u8 {
666 match addr {
667 CmosReg::STATUS_A => {
668 let mut data = StatusRegA::from(self.state.cmos[CmosReg::STATUS_A]);
669
670 if !StatusRegB::from(self.state.cmos[CmosReg::STATUS_B]).set() {
677 let now = self.real_time_source.get_time();
678 let elapsed = now - self.last_update_bit_blip;
679
680 if elapsed.as_millis().is_negative() {
682 tracing::debug!("clock jumped backwards between update bit blips");
683 self.last_update_bit_blip = LocalClockTime::from_millis_since_unix_epoch(0);
684 }
685
686 tracing::trace!(
687 ?self.last_update_bit_blip,
688 ?now,
689 ?elapsed,
690 "get_status_byte"
691 );
692
693 let elapsed_millis = elapsed.as_millis();
694 if elapsed_millis > 1000 {
695 data.set_update(true);
697 self.sync_clock_to_cmos();
698 self.last_update_bit_blip = now;
699 tracing::trace!(
700 ?data,
701 ?elapsed,
702 cmos_date_time = ?self.read_cmos_date_time(),
703 "blip'd status a update bit"
704 );
705 }
706 }
707
708 data.into()
709 }
710 CmosReg::STATUS_B => self.state.cmos[CmosReg::STATUS_B],
711 CmosReg::STATUS_C => {
712 let data = StatusRegC::from(self.state.cmos[CmosReg::STATUS_C]);
713
714 tracing::debug!("clearing rtc interrupts");
716 self.state.cmos[CmosReg::STATUS_C] = StatusRegC::new().into();
717 self.update_interrupt_line_level();
718
719 data.into()
720 }
721 CmosReg::STATUS_D => {
722 StatusRegD::new().with_vrt(true).into()
724 }
725 _ => unreachable!("passed invalid status reg"),
726 }
727 }
728
729 fn read_cmos_date_time(&self) -> Result<jiff::civil::DateTime, jiff::Error> {
730 let mut sec = self.state.cmos[CmosReg::SECOND];
731 let mut min = self.state.cmos[CmosReg::MINUTE];
732 let mut hour = self.state.cmos[CmosReg::HOUR];
733 let mut day = self.state.cmos[CmosReg::DAY_OF_MONTH];
734 let mut month = self.state.cmos[CmosReg::MONTH];
735 let mut year = self.state.cmos[CmosReg::YEAR];
736 let mut century = self.state.cmos[self.century_reg];
737
738 let status_b = StatusRegB::from(self.state.cmos[CmosReg::STATUS_B]);
739
740 (hour, min, sec) = canonical_hms(status_b, hour, min, sec);
742 if !status_b.disable_bcd() {
743 (day, month, year, century) = (
744 from_bcd(day),
745 from_bcd(month),
746 from_bcd(year),
747 from_bcd(century),
748 );
749 }
750
751 jiff::civil::DateTime::new(
752 year as i16 + century as i16 * 100,
753 month as i8,
754 day as i8,
755 hour as i8,
756 min as i8,
757 sec as i8,
758 0,
759 )
760 }
761
762 fn sync_clock_to_cmos(&mut self) {
765 if StatusRegA::from(self.state.cmos[CmosReg::STATUS_A]).oscillator_control()
766 != ENABLE_OSCILLATOR_CONTROL
767 {
768 tracing::trace!(
770 cmos_reg_status_a = self.state.cmos[CmosReg::STATUS_A],
771 "sync_clock_to_cmos: Oscillator is disabled."
772 );
773
774 return;
775 }
776
777 let real_time = self.real_time_source.get_time();
778 let Ok(clock_time): Result<jiff::Timestamp, _> = real_time.try_into() else {
779 tracelimit::warn_ratelimited!(
780 ?real_time,
781 "invalid date/time in real_time_source, skipping sync"
782 );
783 return;
784 };
785
786 let clock_time = clock_time.to_zoned(jiff::tz::TimeZone::UTC).datetime();
787
788 let status_b = StatusRegB::from(self.state.cmos[CmosReg::STATUS_B]);
789
790 self.state.cmos[CmosReg::SECOND] = clock_time.second() as u8;
791 self.state.cmos[CmosReg::MINUTE] = clock_time.minute() as u8;
792 self.state.cmos[CmosReg::HOUR] = {
793 let hour = clock_time.hour() as u8;
794 if status_b.h24_mode() {
795 hour
796 } else {
797 if clock_time.hour() > 12 {
798 hour - 12
799 } else {
800 hour
801 }
802 }
803 };
804 self.state.cmos[CmosReg::DAY_OF_WEEK] =
805 clock_time.date().weekday().to_sunday_one_offset() as u8;
806 self.state.cmos[CmosReg::DAY_OF_MONTH] = clock_time.day() as u8;
807 self.state.cmos[CmosReg::MONTH] = clock_time.month() as u8;
808 self.state.cmos[CmosReg::YEAR] = (clock_time.year() % 100) as u8;
809 self.state.cmos[self.century_reg] = (clock_time.year() / 100) as u8;
810
811 if !status_b.disable_bcd() {
812 let regs = [
813 CmosReg::SECOND,
814 CmosReg::MINUTE,
815 CmosReg::HOUR,
816 CmosReg::DAY_OF_WEEK,
817 CmosReg::DAY_OF_MONTH,
818 CmosReg::MONTH,
819 CmosReg::YEAR,
820 self.century_reg,
821 ];
822
823 for reg in regs {
824 self.state.cmos[reg] = to_bcd(self.state.cmos[reg])
825 }
826 }
827
828 if !status_b.h24_mode() {
829 if clock_time.hour() > 12 {
830 self.state.cmos[CmosReg::HOUR] |= 0x80;
831 }
832 }
833
834 tracing::trace!(
835 cmos_reg_status_b = self.state.cmos[CmosReg::STATUS_B],
836 use_bcd_encoding = ?!status_b.disable_bcd(),
837 use_24h_time = ?status_b.h24_mode(),
838 cmos_date_time = ?self.read_cmos_date_time(),
839 "sync_clock_to_cmos"
840 );
841 }
842
843 fn sync_cmos_to_clock(&mut self) {
846 let cmos_time: jiff::Timestamp = match self
847 .read_cmos_date_time()
848 .and_then(|cmos_time| cmos_time.to_zoned(jiff::tz::TimeZone::UTC))
849 {
850 Ok(zoned) => zoned.timestamp(),
851 Err(e) => {
852 tracelimit::warn_ratelimited!(?e, "invalid date/time in RTC registers!");
853 return;
854 }
855 };
856 self.real_time_source.set_time(cmos_time.into());
857 }
858
859 #[cfg(test)]
860 fn get_cmos_date_time(&mut self) -> Result<jiff::civil::DateTime, jiff::Error> {
861 self.sync_clock_to_cmos();
862 self.read_cmos_date_time()
863 }
864}
865
866impl PortIoIntercept for Rtc {
867 fn io_read(&mut self, io_port: u16, data: &mut [u8]) -> IoResult {
868 data[0] = match RtcIoPort(io_port) {
872 RtcIoPort::ADDR => self.state.addr,
873 RtcIoPort::DATA => self.get_cmos_byte(self.state.addr),
874 _ => return IoResult::Err(IoError::InvalidRegister),
875 };
876
877 IoResult::Ok
878 }
879
880 fn io_write(&mut self, io_port: u16, data: &[u8]) -> IoResult {
881 match RtcIoPort(io_port) {
885 RtcIoPort::ADDR => {
886 if data[0] & 0x7F != data[0] {
887 tracing::debug!("guest tried to set high-bit in CMOS addr register")
888 }
889
890 self.state.addr = data[0] & 0x7F
891 }
892 RtcIoPort::DATA => self.set_cmos_byte(self.state.addr, data[0]),
893 _ => return IoResult::Err(IoError::InvalidRegister),
894 }
895
896 IoResult::Ok
897 }
898
899 fn get_static_regions(&mut self) -> &[(&str, RangeInclusive<u16>)] {
900 &[("io", (RtcIoPort::ADDR.0)..=(RtcIoPort::DATA.0))]
901 }
902}
903
904impl PollDevice for Rtc {
905 fn poll_device(&mut self, cx: &mut std::task::Context<'_>) {
906 while let Poll::Ready(now) = self.vmtime_alarm.poll_timeout(cx) {
907 self.on_alarm_timer(now)
908 }
909
910 if let Poll::Ready(_now) = self.vmtimer_periodic.poll_timeout(cx) {
911 self.on_periodic_timer()
912 }
913
914 if let Poll::Ready(_now) = self.vmtimer_update.poll_timeout(cx) {
915 self.on_update_timer()
916 }
917 }
918}
919
920mod save_restore {
921 use super::*;
922 use vmcore::save_restore::RestoreError;
923 use vmcore::save_restore::SaveError;
924 use vmcore::save_restore::SaveRestore;
925
926 mod state {
927 use mesh::payload::Protobuf;
928 use vmcore::save_restore::SavedStateRoot;
929
930 #[derive(Protobuf, SavedStateRoot)]
932 #[mesh(package = "chipset.cmos_rtc")]
933 pub struct SavedState {
934 #[mesh(1)]
935 pub addr: u8,
936 #[mesh(2)]
937 pub cmos: [u8; 256],
938 }
939 }
940
941 impl SaveRestore for Rtc {
942 type SavedState = state::SavedState;
943
944 fn save(&mut self) -> Result<Self::SavedState, SaveError> {
945 let RtcState { addr, ref cmos } = self.state;
946
947 let saved_state = state::SavedState { addr, cmos: cmos.0 };
948
949 Ok(saved_state)
950 }
951
952 fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
953 let state::SavedState { addr, cmos } = state;
954
955 self.state = RtcState {
956 addr,
957 cmos: CmosData(cmos),
958 };
959
960 self.update_timers();
961 self.update_interrupt_line_level();
962
963 Ok(())
964 }
965 }
966}
967
968#[cfg(test)]
969mod tests {
970 use super::*;
971 use local_clock::MockLocalClock;
972 use local_clock::MockLocalClockAccessor;
973 use test_with_tracing::test;
974
975 fn new_test_rtc() -> (
976 pal_async::DefaultPool,
977 vmcore::vmtime::VmTimeKeeper,
978 MockLocalClockAccessor,
979 Rtc,
980 ) {
981 let mut pool = pal_async::DefaultPool::new();
982 let driver = pool.driver();
983 let vm_time_keeper =
984 vmcore::vmtime::VmTimeKeeper::new(&pool.driver(), VmTime::from_100ns(0));
985 let vm_time_source = pool
986 .run_until(vm_time_keeper.builder().build(&driver))
987 .unwrap();
988
989 let time = MockLocalClock::new();
990 let time_access = time.accessor();
991
992 let rtc = Rtc::new(
993 Box::new(time),
994 LineInterrupt::detached(),
995 &vm_time_source,
996 0x32,
997 None,
998 false,
999 );
1000
1001 (pool, vm_time_keeper, time_access, rtc)
1002 }
1003
1004 fn get_cmos_data(rtc: &mut Rtc, addr: CmosReg) -> u8 {
1005 let mut temp = [addr.0];
1006 rtc.io_write(RtcIoPort::ADDR.0, &temp).unwrap();
1007 rtc.io_read(RtcIoPort::DATA.0, &mut temp).unwrap();
1008 temp[0]
1009 }
1010
1011 fn set_cmos_data(rtc: &mut Rtc, addr: CmosReg, data: u8) {
1012 let mut temp = [addr.0];
1013 rtc.io_write(RtcIoPort::ADDR.0, &temp).unwrap();
1014 temp[0] = data;
1015 rtc.io_write(RtcIoPort::DATA.0, &temp).unwrap();
1016 }
1017
1018 fn get_rtc_data(rtc: &mut Rtc, addr: CmosReg, bcd: bool, hour24: bool) -> u8 {
1019 let mut temp = [addr.0];
1020 rtc.io_write(RtcIoPort::ADDR.0, &temp).unwrap();
1021 rtc.io_read(RtcIoPort::DATA.0, &mut temp).unwrap();
1022 let mut data = temp[0];
1023 if addr == CmosReg::HOUR {
1024 let pm = if hour24 { false } else { (data & 0x80) != 0 };
1025 if pm {
1026 data &= 0x7f
1027 };
1028 if bcd {
1029 data = from_bcd(data)
1030 };
1031 if pm {
1032 data += 12;
1033 }
1034 } else {
1035 if bcd {
1036 data = from_bcd(data)
1037 };
1038 }
1039
1040 println!("get {0}({0:#x}) convert to {1}", temp[0], data);
1041 data
1042 }
1043
1044 fn set_rtc_data(rtc: &mut Rtc, addr: CmosReg, data: u8, bcd: bool, hour24: bool) {
1045 let mut temp = [addr.0];
1046 rtc.io_write(RtcIoPort::ADDR.0, &temp).unwrap();
1047 let mut new_data = data;
1048 if addr == CmosReg::HOUR {
1049 let pm = if hour24 { false } else { new_data > 12 };
1050 if bcd {
1051 new_data =
1052 to_bcd(if pm { new_data - 12 } else { new_data }) | if pm { 0x80 } else { 0 };
1053 } else {
1054 if pm {
1055 new_data = (new_data - 12) | 0x80;
1056 }
1057 }
1058 } else {
1059 if bcd {
1060 new_data = to_bcd(new_data)
1061 };
1062 }
1063
1064 println!("set {0} convert to {1}({1:#x})", data, new_data);
1065 temp[0] = new_data;
1066 rtc.io_write(RtcIoPort::DATA.0, &temp).unwrap();
1067 }
1068
1069 fn wait_for_edge(rtc: &mut Rtc, high: bool, time: MockLocalClockAccessor) -> bool {
1072 let limit = 5; let stall_ms = 10; for _i in 0..(limit * 1000 / stall_ms) {
1076 let value = get_cmos_data(rtc, CmosReg::STATUS_A);
1077 if high {
1078 if value & 0x80 != 0 {
1079 return true;
1080 }
1081 } else {
1082 if value & 0x80 == 0 {
1083 return true;
1084 }
1085 }
1086
1087 time.tick(Duration::from_millis(stall_ms));
1088 }
1089
1090 false
1091 }
1092
1093 fn set_bcd(rtc: &mut Rtc) {
1094 let mut value = get_cmos_data(rtc, CmosReg::STATUS_B);
1095 value &= 0xFB;
1096 set_cmos_data(rtc, CmosReg::STATUS_B, value);
1097 }
1098
1099 fn set_binary(rtc: &mut Rtc) {
1100 let mut value = get_cmos_data(rtc, CmosReg::STATUS_B);
1101 value |= 0x4;
1102 set_cmos_data(rtc, CmosReg::STATUS_B, value);
1103 }
1104
1105 fn set_24hour(rtc: &mut Rtc) {
1106 let mut value = get_cmos_data(rtc, CmosReg::STATUS_B);
1107 value |= 0x2;
1108 set_cmos_data(rtc, CmosReg::STATUS_B, value);
1109 }
1110
1111 fn set_12hour(rtc: &mut Rtc) {
1112 let mut value = get_cmos_data(rtc, CmosReg::STATUS_B);
1113 value &= 0xFD;
1114 set_cmos_data(rtc, CmosReg::STATUS_B, value);
1115 }
1116
1117 #[test]
1118 fn test_setup() {
1119 let (_, _, _, _rtc) = new_test_rtc();
1120 }
1121
1122 #[test]
1123 fn test_default() {
1124 let default_state = RtcState::new(None);
1125
1126 let (_, _, _, mut rtc) = new_test_rtc();
1127
1128 let mut data = [0];
1129 rtc.io_read(RtcIoPort::ADDR.0, &mut data).unwrap();
1130 assert_eq!(data[0], 0x80);
1131 assert_eq!(
1132 get_cmos_data(&mut rtc, CmosReg::STATUS_A),
1133 default_state.cmos[CmosReg::STATUS_A] | 0x80
1134 );
1135 assert_eq!(
1136 get_cmos_data(&mut rtc, CmosReg::STATUS_B),
1137 default_state.cmos[CmosReg::STATUS_B]
1138 );
1139 assert_eq!(
1140 get_cmos_data(&mut rtc, CmosReg::STATUS_C),
1141 default_state.cmos[CmosReg::STATUS_C]
1142 );
1143 assert_eq!(
1144 get_cmos_data(&mut rtc, CmosReg::STATUS_D),
1145 default_state.cmos[CmosReg::STATUS_D]
1146 );
1147 }
1148
1149 fn test_time_move(rtc: &mut Rtc, is_move: bool, time: MockLocalClockAccessor) {
1150 if let Ok(before) = rtc.get_cmos_date_time() {
1151 time.tick(Duration::from_secs(2));
1152 if let Ok(after) = rtc.get_cmos_date_time() {
1153 if is_move {
1154 assert_ne!(before, after);
1155 } else {
1156 assert_eq!(before, after);
1157 }
1158 } else {
1159 panic!("get_cmos_date_time failed");
1160 }
1161 } else {
1162 panic!("get_cmos_date_time failed");
1163 }
1164 }
1165
1166 #[test]
1167 fn test_oscillator() {
1168 let default_state = RtcState::new(None);
1169
1170 let (_, _, time, mut rtc) = new_test_rtc();
1171
1172 assert_eq!(
1173 get_cmos_data(&mut rtc, CmosReg::STATUS_A),
1174 default_state.cmos[CmosReg::STATUS_A] | 0x80
1175 );
1176
1177 println!("RTC should move forward when oscillator is enabled (default control mask 010)");
1178 test_time_move(&mut rtc, true, time.clone());
1179
1180 set_cmos_data(&mut rtc, CmosReg::STATUS_A, 0x66);
1182 println!("RTC should not move forward when oscillator is disabled (control mask 110)");
1183 test_time_move(&mut rtc, false, time.clone());
1184
1185 set_cmos_data(&mut rtc, CmosReg::STATUS_A, 0x26);
1187 println!("RTC should move forward when oscillator is re-enabled (control mask 010)");
1188 test_time_move(&mut rtc, true, time);
1189 }
1190
1191 #[test]
1192 fn test_uip() {
1193 let (_, _, time, mut rtc) = new_test_rtc();
1194
1195 assert!(
1196 wait_for_edge(&mut rtc, false, time.clone())
1197 && wait_for_edge(&mut rtc, true, time.clone())
1198 );
1199 if let Ok(start) = rtc.get_cmos_date_time() {
1200 let seconds_to_wait: i64 = 65;
1201 for _i in 0..seconds_to_wait {
1202 assert!(
1203 wait_for_edge(&mut rtc, false, time.clone())
1204 && wait_for_edge(&mut rtc, true, time.clone())
1205 );
1206 }
1207
1208 if let Ok(end) = rtc.get_cmos_date_time() {
1209 let elapsed_secs =
1210 end.since(start).unwrap().total(jiff::Unit::Second).unwrap() as i64;
1211 let expected_secs = seconds_to_wait;
1212 let allowance_secs: i64 = 1;
1213 println!(
1214 "Expected: {}s Start: {:?} End: {:?} Elapsed: {}s Allowance: {}s, RTC generates update strobe at expected rate.",
1215 expected_secs, start, end, elapsed_secs, allowance_secs
1216 );
1217 assert!(
1218 elapsed_secs <= (expected_secs + allowance_secs)
1219 && elapsed_secs >= (expected_secs - allowance_secs)
1220 );
1221 } else {
1222 panic!("get_cmos_date_time failed");
1223 }
1224 } else {
1225 panic!("get_cmos_date_time failed");
1226 }
1227 }
1228
1229 #[test]
1230 fn test_readonly() {
1231 let default_state = RtcState::new(None);
1232
1233 let (_, _, _, mut rtc) = new_test_rtc();
1234
1235 assert_eq!(
1236 get_cmos_data(&mut rtc, CmosReg::STATUS_D),
1237 default_state.cmos[CmosReg::STATUS_D]
1238 );
1239
1240 for i in 0..=0xFF {
1242 set_cmos_data(&mut rtc, CmosReg::STATUS_D, i);
1243 assert_eq!(
1244 get_cmos_data(&mut rtc, CmosReg::STATUS_D),
1245 default_state.cmos[CmosReg::STATUS_D]
1246 );
1247 }
1248 }
1249
1250 #[test]
1251 fn test_writeable() {
1252 let (_, _, _, mut rtc) = new_test_rtc();
1253
1254 for i in (0x0F..=0x7F).map(CmosReg) {
1256 if i == rtc.century_reg {
1257 continue;
1258 }
1259
1260 set_cmos_data(&mut rtc, i, 0xFF);
1261 assert_eq!(get_cmos_data(&mut rtc, i), 0xFF);
1262 set_cmos_data(&mut rtc, i, 0);
1263 assert_eq!(get_cmos_data(&mut rtc, i), 0);
1264 }
1265 }
1266
1267 fn test_count(bcd: bool, hour24: bool) {
1268 let (_, _, time, mut rtc) = new_test_rtc();
1269
1270 if bcd {
1271 set_bcd(&mut rtc);
1272 } else {
1273 set_binary(&mut rtc);
1274 }
1275
1276 if hour24 {
1277 set_24hour(&mut rtc);
1278 } else {
1279 set_12hour(&mut rtc);
1280 }
1281
1282 let init_ts: jiff::Timestamp = time.get_time().try_into().unwrap();
1283 let init = init_ts.to_zoned(jiff::tz::TimeZone::UTC).datetime();
1284 println!("init: {:?}", init);
1285 set_rtc_data(&mut rtc, CmosReg::HOUR, 11, bcd, hour24);
1286 set_rtc_data(&mut rtc, CmosReg::MINUTE, 59, bcd, hour24);
1287 set_rtc_data(&mut rtc, CmosReg::SECOND, 59, bcd, hour24);
1288 time.tick(Duration::from_secs(2));
1289 assert_eq!(get_rtc_data(&mut rtc, CmosReg::SECOND, bcd, hour24), 1);
1290 assert_eq!(get_rtc_data(&mut rtc, CmosReg::MINUTE, bcd, hour24), 0);
1291 assert_eq!(get_rtc_data(&mut rtc, CmosReg::HOUR, bcd, hour24), 12);
1292 assert_eq!(
1293 get_rtc_data(&mut rtc, CmosReg::DAY_OF_MONTH, bcd, hour24),
1294 init.day() as u8
1295 );
1296 assert_eq!(
1297 get_rtc_data(&mut rtc, CmosReg::DAY_OF_WEEK, bcd, hour24),
1298 init.date().weekday().to_sunday_one_offset() as u8
1299 );
1300
1301 set_rtc_data(&mut rtc, CmosReg::HOUR, 23, bcd, hour24);
1302 set_rtc_data(&mut rtc, CmosReg::MINUTE, 59, bcd, hour24);
1303 set_rtc_data(&mut rtc, CmosReg::SECOND, 59, bcd, hour24);
1304 time.tick(Duration::from_secs(2));
1305 assert_eq!(get_rtc_data(&mut rtc, CmosReg::SECOND, bcd, hour24), 1);
1306 assert_eq!(get_rtc_data(&mut rtc, CmosReg::MINUTE, bcd, hour24), 0);
1307 assert_eq!(get_rtc_data(&mut rtc, CmosReg::HOUR, bcd, hour24), 0);
1308 let temp = init_ts
1309 .checked_add(jiff::SignedDuration::from_hours(24))
1310 .unwrap()
1311 .to_zoned(jiff::tz::TimeZone::UTC)
1312 .datetime();
1313 assert_eq!(
1314 get_rtc_data(&mut rtc, CmosReg::DAY_OF_MONTH, bcd, hour24),
1315 temp.day() as u8
1316 );
1317 assert_eq!(
1318 get_rtc_data(&mut rtc, CmosReg::DAY_OF_WEEK, bcd, hour24),
1319 temp.date().weekday().to_sunday_one_offset() as u8
1320 );
1321
1322 set_rtc_data(&mut rtc, CmosReg::MINUTE, 59, bcd, hour24);
1323 set_rtc_data(&mut rtc, CmosReg::SECOND, 59, bcd, hour24);
1324 time.tick(Duration::from_secs(2));
1325 assert_eq!(get_rtc_data(&mut rtc, CmosReg::SECOND, bcd, hour24), 1);
1326 assert_eq!(get_rtc_data(&mut rtc, CmosReg::MINUTE, bcd, hour24), 0);
1327 assert_eq!(get_rtc_data(&mut rtc, CmosReg::HOUR, bcd, hour24), 1);
1328 }
1329
1330 #[test]
1331 fn test_bcd_binary() {
1332 println!("----Testing BCD mode...");
1333 test_count(true, true);
1334 println!("----Testing Binary mode...");
1335 test_count(false, true);
1336 }
1337
1338 #[test]
1339 fn test_hour_mode() {
1340 println!("----Testing Binary + 12 hour mode...");
1341 test_count(false, false);
1342 println!("----Testing Binary + 24 hour mode...");
1343 test_count(false, true);
1344 println!("----Testing BCD + 12 hour mode...");
1345 test_count(true, false);
1346 println!("----Testing BCD + 24 hour mode...");
1347 test_count(true, true);
1348 }
1349
1350 fn test_day_of_month(bcd: bool, hour24: bool) {
1351 let century_reg_idx = 0x32;
1352 let century_reg = CmosReg(century_reg_idx);
1353
1354 let (_, _, time, mut rtc) = new_test_rtc();
1355
1356 if bcd {
1357 set_bcd(&mut rtc);
1358 } else {
1359 set_binary(&mut rtc);
1360 }
1361
1362 if hour24 {
1363 set_24hour(&mut rtc);
1364 } else {
1365 set_12hour(&mut rtc);
1366 }
1367
1368 for month in 1..=12 {
1369 let mut day;
1370 let mut year_check_count = 1;
1371 if month == 4 || month == 6 || month == 9 || month == 11 {
1372 day = 30;
1373 } else if month != 2 {
1374 day = 31;
1375 } else {
1376 day = 0;
1377 year_check_count = 4;
1378 }
1379
1380 while year_check_count > 0 {
1381 let century = 30;
1382 let year = 4 + year_check_count;
1383 if month == 2 {
1384 day = if (year & 3) > 0 { 28 } else { 29 };
1385 }
1386
1387 println!(
1388 "----Testing {:02}{:02}-{:02}-{:02}",
1389 century, year, month, day
1390 );
1391 set_rtc_data(&mut rtc, century_reg, century, bcd, hour24);
1392 set_rtc_data(&mut rtc, CmosReg::YEAR, year, bcd, hour24);
1393 set_rtc_data(&mut rtc, CmosReg::MONTH, month, bcd, hour24);
1394 set_rtc_data(&mut rtc, CmosReg::DAY_OF_MONTH, day, bcd, hour24);
1395 set_rtc_data(&mut rtc, CmosReg::HOUR, 23, bcd, hour24);
1396 set_rtc_data(&mut rtc, CmosReg::MINUTE, 59, bcd, hour24);
1397 set_rtc_data(&mut rtc, CmosReg::SECOND, 59, bcd, hour24);
1398 time.tick(Duration::from_secs(2));
1399 assert_eq!(get_rtc_data(&mut rtc, CmosReg::SECOND, bcd, hour24), 1);
1400 assert_eq!(get_rtc_data(&mut rtc, CmosReg::MINUTE, bcd, hour24), 0);
1401 assert_eq!(get_rtc_data(&mut rtc, CmosReg::HOUR, bcd, hour24), 0);
1402 assert_eq!(
1403 get_rtc_data(&mut rtc, CmosReg::DAY_OF_MONTH, bcd, hour24),
1404 1
1405 );
1406 assert_eq!(
1407 get_rtc_data(&mut rtc, CmosReg::MONTH, bcd, hour24),
1408 if month == 12 { 1 } else { month + 1 }
1409 );
1410 assert_eq!(
1411 get_rtc_data(&mut rtc, CmosReg::YEAR, bcd, hour24),
1412 if month < 12 { year } else { year + 1 }
1413 );
1414 assert_eq!(get_rtc_data(&mut rtc, century_reg, bcd, hour24), century);
1415 year_check_count -= 1;
1416 }
1417 }
1418 }
1419
1420 #[test]
1421 fn test_month() {
1422 println!("----Testing BCD mode...");
1423 test_day_of_month(true, true);
1424 println!("----Testing Binary mode...");
1425 test_day_of_month(false, true);
1426 }
1427
1428 fn test_day_of_week(bcd: bool, hour24: bool) {
1429 let century_reg_idx = 0x32;
1430 let century_reg = CmosReg(century_reg_idx);
1431
1432 let (_, _, time, mut rtc) = new_test_rtc();
1433
1434 if bcd {
1435 set_bcd(&mut rtc);
1436 } else {
1437 set_binary(&mut rtc);
1438 }
1439
1440 if hour24 {
1441 set_24hour(&mut rtc);
1442 } else {
1443 set_12hour(&mut rtc);
1444 }
1445
1446 let century = 30;
1447 let year = 5;
1448 let month = 1;
1449 let day = 5;
1450
1451 println!(
1452 "----Testing {:02}{:02}-{:02}-{:02}",
1453 century, year, month, day
1454 );
1455 set_rtc_data(&mut rtc, century_reg, century, bcd, hour24);
1456 set_rtc_data(&mut rtc, CmosReg::YEAR, year, bcd, hour24);
1457 set_rtc_data(&mut rtc, CmosReg::MONTH, month, bcd, hour24);
1458 set_rtc_data(&mut rtc, CmosReg::DAY_OF_MONTH, day, bcd, hour24);
1459 set_rtc_data(&mut rtc, CmosReg::DAY_OF_WEEK, 7, bcd, hour24);
1460 set_rtc_data(&mut rtc, CmosReg::HOUR, 23, bcd, hour24);
1461 set_rtc_data(&mut rtc, CmosReg::MINUTE, 59, bcd, hour24);
1462 set_rtc_data(&mut rtc, CmosReg::SECOND, 59, bcd, hour24);
1463 time.tick(Duration::from_secs(2));
1464 assert_eq!(get_rtc_data(&mut rtc, CmosReg::SECOND, bcd, hour24), 1);
1465 assert_eq!(get_rtc_data(&mut rtc, CmosReg::MINUTE, bcd, hour24), 0);
1466 assert_eq!(get_rtc_data(&mut rtc, CmosReg::HOUR, bcd, hour24), 0);
1467 assert_eq!(get_rtc_data(&mut rtc, CmosReg::DAY_OF_WEEK, bcd, hour24), 1);
1468
1469 set_rtc_data(&mut rtc, CmosReg::DAY_OF_MONTH, day - 1, bcd, hour24);
1470 set_rtc_data(&mut rtc, CmosReg::DAY_OF_WEEK, 6, bcd, hour24);
1471 set_rtc_data(&mut rtc, CmosReg::HOUR, 23, bcd, hour24);
1472 set_rtc_data(&mut rtc, CmosReg::MINUTE, 59, bcd, hour24);
1473 set_rtc_data(&mut rtc, CmosReg::SECOND, 59, bcd, hour24);
1474 time.tick(Duration::from_secs(2));
1475 assert_eq!(get_rtc_data(&mut rtc, CmosReg::SECOND, bcd, hour24), 1);
1476 assert_eq!(get_rtc_data(&mut rtc, CmosReg::MINUTE, bcd, hour24), 0);
1477 assert_eq!(get_rtc_data(&mut rtc, CmosReg::HOUR, bcd, hour24), 0);
1478 assert_eq!(get_rtc_data(&mut rtc, CmosReg::DAY_OF_WEEK, bcd, hour24), 7);
1479 }
1480
1481 #[test]
1482 fn test_week() {
1483 println!("----Testing Binary mode...");
1484 test_day_of_week(false, true);
1485 println!("----Testing BCD mode...");
1486 test_day_of_week(true, true);
1487 }
1488
1489 #[test]
1490 fn test_alarm() {
1491 fn hms_to_duration(h: u8, m: u8, s: u8) -> Duration {
1492 Duration::from_secs(s as u64 + 60 * m as u64 + h as u64 * 60 * 60)
1493 }
1494
1495 let (_, _, _time, mut rtc) = new_test_rtc();
1496
1497 set_binary(&mut rtc);
1498
1499 set_cmos_data(&mut rtc, CmosReg::HOUR, 2);
1500 set_cmos_data(&mut rtc, CmosReg::MINUTE, 2);
1501 set_cmos_data(&mut rtc, CmosReg::SECOND, 2);
1502
1503 set_cmos_data(&mut rtc, CmosReg::HOUR_ALARM, 3);
1504 set_cmos_data(&mut rtc, CmosReg::MINUTE_ALARM, 3);
1505 set_cmos_data(&mut rtc, CmosReg::SECOND_ALARM, 3);
1506
1507 assert_eq!(rtc.calculate_alarm_duration(), hms_to_duration(1, 1, 1));
1508 set_cmos_data(&mut rtc, CmosReg::HOUR_ALARM, 0xff);
1509 set_cmos_data(&mut rtc, CmosReg::MINUTE_ALARM, 0xff);
1510 set_cmos_data(&mut rtc, CmosReg::SECOND_ALARM, 0xff);
1511 assert_eq!(rtc.calculate_alarm_duration(), hms_to_duration(0, 0, 1));
1512
1513 }
1515}