cmos.rs92.16%
1
// Copyright 2026 Google LLC2
//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 at6
//7
// https://www.apache.org/licenses/LICENSE-2.08
//9
// Unless required by applicable law or agreed to in writing, software10
// 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 and13
// limitations under the License.14
15
use std::sync::atomic::{AtomicU8, Ordering};16
17
use bitfield::bitfield;18
use chrono::{DateTime, Datelike, Timelike};19
20
use crate::device::clock::Clock;21
use crate::device::{MmioDev, Pause, Result};22
use crate::mem::emulated::{Action, Mmio};23
use crate::{bitflags, consts, mem};24
25
bitfield! {26
pub struct CmosReg(u8);27
pub disable_nmi, set_disable_nmi: 7;28
pub reg, set_reg: 6, 0;29
}30
31
consts! {32
pub struct CmosIntrFreq(u8) {33
HZ_1024 = 0b0110;34
}35
}36
37
consts! {38
pub struct CmosTimeBase(u8) {39
HZ_32768 = 0b010;40
}41
}42
43
bitfield! {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
51
impl Default for CmosRegA {52
fn default() -> Self {6x53
CmosRegA::new(CmosIntrFreq::HZ_1024, CmosTimeBase::HZ_32768, false)6x54
}6x55
}56
57
bitflags! {58
pub struct CmosRegB(u8) {59
HOUR_24 = 1 << 1;60
BINARY_FORMAT = 1 << 2;61
}62
}63
64
bitflags! {65
pub struct CmosRegD(u8) {66
POWER = 1 << 7;67
}68
}69
70
/// CMOS RTC device.71
///72
/// https://stanislavs.org/helppc/cmos_ram.html73
/// https://wiki.osdev.org/CMOS74
#[derive(Debug)]75
pub struct Cmos<C> {76
reg: AtomicU8,77
clock: C,78
}79
80
impl<C> Cmos<C> {81
pub fn new(clock: C) -> Self {6x82
Self {6x83
reg: AtomicU8::new(0),6x84
clock,6x85
}6x86
}6x87
}88
89
impl<C: Clock> Mmio for Cmos<C> {90
fn size(&self) -> u64 {3x91
23x92
}3x93
94
fn read(&self, offset: u64, _size: u8) -> mem::Result<u64> {42x95
let reg = self.reg.load(Ordering::Relaxed);42x96
if offset == 0 {42x97
return Ok(reg as u64);3x98
}39x99
let nanos = self.clock.now().as_nanos();39x100
let now = DateTime::from_timestamp_nanos(nanos as i64);39x101
let ret = match CmosReg(reg).reg() {39x102
0x00 => now.second(),3x103
0x02 => now.minute(),3x104
0x04 => now.hour(),3x105
0x06 => now.weekday().number_from_sunday(),3x106
0x07 => now.day(),3x107
0x08 => now.month(),3x108
0x09 => now.year() as u32 % 100,3x109
0x32 => now.year() as u32 / 100 + 1,3x110
0x0a => {111
// Assuming the hardware takes 8 crystal cycles to update112
// the 8 registers above.113
// 1 / 32768 Hz * 8 = 244140 ns114
let mut r = CmosRegA::default();6x115
if now.nanosecond() < 244140 {6x116
r.set_update_in_progress(true);3x117
}3x118
r.0 as u326x119
}120
0x0b => (CmosRegB::HOUR_24 | CmosRegB::BINARY_FORMAT).bits() as u32,3x121
0x0d => CmosRegD::POWER.bits() as u32,3x122
_ => {123
log::debug!("cmos: read from reg {reg:#x}: ignored");3x124
03x125
}126
};127
Ok(ret as u64)39x128
}42x129
130
fn write(&self, offset: u64, _size: u8, val: u64) -> mem::Result<Action> {42x131
if offset == 0 {42x132
self.reg.store(val as u8, Ordering::Relaxed);39x133
} else {39x134
log::debug!(3x135
"cmos: write {val:#x} to reg {:#x}: ignored",136
self.reg.load(Ordering::Relaxed)3x137
);138
}139
Ok(Action::None)42x140
}42x141
}142
143
impl<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
153
impl<C: Clock> MmioDev for Cmos<C> {}154
155
#[cfg(test)]156
#[path = "cmos_test.rs"]157
mod tests;158