Alioth Code Coverage

cmos.rs92.16%

1// Copyright 2026 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::sync::atomic::{AtomicU8, Ordering};
16
17use bitfield::bitfield;
18use chrono::{DateTime, Datelike, Timelike};
19
20use crate::device::clock::Clock;
21use crate::device::{MmioDev, Pause, Result};
22use crate::mem::emulated::{Action, Mmio};
23use crate::{bitflags, consts, mem};
24
25bitfield! {
26 pub struct CmosReg(u8);
27 pub disable_nmi, set_disable_nmi: 7;
28 pub reg, set_reg: 6, 0;
29}
30
31consts! {
32 pub struct CmosIntrFreq(u8) {
33 HZ_1024 = 0b0110;
34 }
35}
36
37consts! {
38 pub struct CmosTimeBase(u8) {
39 HZ_32768 = 0b010;
40 }
41}
42
43bitfield! {
44 pub struct CmosRegA(u8);
45 impl new;
46 pub u8, from into CmosIntrFreq, intr_freq, set_intr_freq: 3, 0;
47 pub u8, from into CmosTimeBase, time_base, set_time_base: 6, 4;
48 pub update_in_progress, set_update_in_progress: 7;
49}
50
51impl Default for CmosRegA {
52 fn default() -> Self {6x
53 CmosRegA::new(CmosIntrFreq::HZ_1024, CmosTimeBase::HZ_32768, false)6x
54 }6x
55}
56
57bitflags! {
58 pub struct CmosRegB(u8) {
59 HOUR_24 = 1 << 1;
60 BINARY_FORMAT = 1 << 2;
61 }
62}
63
64bitflags! {
65 pub struct CmosRegD(u8) {
66 POWER = 1 << 7;
67 }
68}
69
70/// CMOS RTC device.
71///
72/// https://stanislavs.org/helppc/cmos_ram.html
73/// https://wiki.osdev.org/CMOS
74#[derive(Debug)]
75pub struct Cmos<C> {
76 reg: AtomicU8,
77 clock: C,
78}
79
80impl<C> Cmos<C> {
81 pub fn new(clock: C) -> Self {6x
82 Self {6x
83 reg: AtomicU8::new(0),6x
84 clock,6x
85 }6x
86 }6x
87}
88
89impl<C: Clock> Mmio for Cmos<C> {
90 fn size(&self) -> u64 {3x
91 23x
92 }3x
93
94 fn read(&self, offset: u64, _size: u8) -> mem::Result<u64> {42x
95 let reg = self.reg.load(Ordering::Relaxed);42x
96 if offset == 0 {42x
97 return Ok(reg as u64);3x
98 }39x
99 let nanos = self.clock.now().as_nanos();39x
100 let now = DateTime::from_timestamp_nanos(nanos as i64);39x
101 let ret = match CmosReg(reg).reg() {39x
102 0x00 => now.second(),3x
103 0x02 => now.minute(),3x
104 0x04 => now.hour(),3x
105 0x06 => now.weekday().number_from_sunday(),3x
106 0x07 => now.day(),3x
107 0x08 => now.month(),3x
108 0x09 => now.year() as u32 % 100,3x
109 0x32 => now.year() as u32 / 100 + 1,3x
110 0x0a => {
111 // Assuming the hardware takes 8 crystal cycles to update
112 // the 8 registers above.
113 // 1 / 32768 Hz * 8 = 244140 ns
114 let mut r = CmosRegA::default();6x
115 if now.nanosecond() < 244140 {6x
116 r.set_update_in_progress(true);3x
117 }3x
118 r.0 as u326x
119 }
120 0x0b => (CmosRegB::HOUR_24 | CmosRegB::BINARY_FORMAT).bits() as u32,3x
121 0x0d => CmosRegD::POWER.bits() as u32,3x
122 _ => {
123 log::debug!("cmos: read from reg {reg:#x}: ignored");3x
124 03x
125 }
126 };
127 Ok(ret as u64)39x
128 }42x
129
130 fn write(&self, offset: u64, _size: u8, val: u64) -> mem::Result<Action> {42x
131 if offset == 0 {42x
132 self.reg.store(val as u8, Ordering::Relaxed);39x
133 } else {39x
134 log::debug!(3x
135 "cmos: write {val:#x} to reg {:#x}: ignored",
136 self.reg.load(Ordering::Relaxed)3x
137 );
138 }
139 Ok(Action::None)42x
140 }42x
141}
142
143impl<C: Clock> Pause for Cmos<C> {
144 fn pause(&self) -> Result<()> {
145 todo!()
146 }
147
148 fn resume(&self) -> Result<()> {
149 todo!()
150 }
151}
152
153impl<C: Clock> MmioDev for Cmos<C> {}
154
155#[cfg(test)]
156#[path = "cmos_test.rs"]
157mod tests;
158