Alioth Code Coverage

cmos.rs92.00%

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::{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 now = self.clock.now();39x
100 let ret = match CmosReg(reg).reg() {39x
101 0x00 => now.second(),3x
102 0x02 => now.minute(),3x
103 0x04 => now.hour(),3x
104 0x06 => now.weekday().number_from_sunday(),3x
105 0x07 => now.day(),3x
106 0x08 => now.month(),3x
107 0x09 => now.year() as u32 % 100,3x
108 0x32 => now.year() as u32 / 100 + 1,3x
109 0x0a => {
110 // Assuming the hardware takes 8 crystal cycles to update
111 // the 8 registers above.
112 // 1 / 32768 Hz * 8 = 244140 ns
113 let mut r = CmosRegA::default();6x
114 if now.nanosecond() < 244140 {6x
115 r.set_update_in_progress(true);3x
116 }3x
117 r.0 as u326x
118 }
119 0x0b => (CmosRegB::HOUR_24 | CmosRegB::BINARY_FORMAT).bits() as u32,3x
120 0x0d => CmosRegD::POWER.bits() as u32,3x
121 _ => {
122 log::debug!("cmos: read from reg {reg:#x}: ignored");3x
123 03x
124 }
125 };
126 Ok(ret as u64)39x
127 }42x
128
129 fn write(&self, offset: u64, _size: u8, val: u64) -> mem::Result<Action> {42x
130 if offset == 0 {42x
131 self.reg.store(val as u8, Ordering::Relaxed);39x
132 } else {39x
133 log::debug!(3x
134 "cmos: write {val:#x} to reg {:#x}: ignored",
135 self.reg.load(Ordering::Relaxed)3x
136 );
137 }
138 Ok(Action::None)42x
139 }42x
140}
141
142impl<C: Clock> Pause for Cmos<C> {
143 fn pause(&self) -> Result<()> {
144 todo!()
145 }
146
147 fn resume(&self) -> Result<()> {
148 todo!()
149 }
150}
151
152impl<C: Clock> MmioDev for Cmos<C> {}
153
154#[cfg(test)]
155#[path = "cmos_test.rs"]
156mod tests;
157