use chipset_device::io::IoError;
use chipset_device::io::IoResult;
use chipset_device::pio::PortIoIntercept;
use chipset_device::poll_device::PollDevice;
use chipset_device::ChipsetDevice;
use inspect::Inspect;
use inspect::InspectMut;
use local_clock::InspectableLocalClock;
use std::ops::RangeInclusive;
use vmcore::device_state::ChangeDeviceState;
use vmcore::line_interrupt::LineInterrupt;
use vmcore::vmtime::VmTimeSource;
open_enum::open_enum! {
enum Piix4CmosRtcIoPort: u16 {
ADDRESS = 0x70,
DATA = 0x71,
EXTENDED_ADDRESS = 0x72,
EXTENDED_DATA = 0x73,
ADDRESS_SHADOW_2 = 0x74,
DATA_SHADOW_2 = 0x75,
ADDRESS_SHADOW_3 = 0x76,
DATA_SHADOW_3 = 0x77,
}
}
#[derive(Debug, Inspect)]
struct Piix4CmosRtcState {
ext_addr: u8,
}
#[derive(InspectMut)]
pub struct Piix4CmosRtc {
#[inspect(mut)]
inner: chipset::cmos_rtc::Rtc,
state: Piix4CmosRtcState,
}
impl Piix4CmosRtc {
pub fn new(
real_time_source: Box<dyn InspectableLocalClock>,
interrupt: LineInterrupt,
vmtime_source: &VmTimeSource,
initial_cmos: Option<[u8; 256]>,
enlightened_interrupts: bool,
) -> Piix4CmosRtc {
Piix4CmosRtc {
state: Piix4CmosRtcState { ext_addr: 0 },
inner: chipset::cmos_rtc::Rtc::new(
real_time_source,
interrupt,
vmtime_source,
0x32,
initial_cmos,
enlightened_interrupts,
),
}
}
}
impl ChangeDeviceState for Piix4CmosRtc {
fn start(&mut self) {}
async fn stop(&mut self) {}
async fn reset(&mut self) {
self.inner.reset().await;
}
}
impl ChipsetDevice for Piix4CmosRtc {
fn supports_pio(&mut self) -> Option<&mut dyn PortIoIntercept> {
Some(self)
}
fn supports_poll_device(&mut self) -> Option<&mut dyn PollDevice> {
Some(self)
}
}
impl PortIoIntercept for Piix4CmosRtc {
fn io_read(&mut self, io_port: u16, data: &mut [u8]) -> IoResult {
data[0] = match Piix4CmosRtcIoPort(io_port) {
Piix4CmosRtcIoPort::ADDRESS | Piix4CmosRtcIoPort::DATA => {
return self.inner.io_read(io_port, data)
}
Piix4CmosRtcIoPort::ADDRESS_SHADOW_2 | Piix4CmosRtcIoPort::DATA_SHADOW_2 => {
return self.inner.io_read(io_port - 4, data)
}
Piix4CmosRtcIoPort::EXTENDED_ADDRESS | Piix4CmosRtcIoPort::ADDRESS_SHADOW_3 => {
self.state.ext_addr
}
Piix4CmosRtcIoPort::EXTENDED_DATA | Piix4CmosRtcIoPort::DATA_SHADOW_3 => {
self.inner.get_cmos_byte(self.state.ext_addr + 128)
}
_ => return IoResult::Err(IoError::InvalidRegister),
};
IoResult::Ok
}
fn io_write(&mut self, io_port: u16, data: &[u8]) -> IoResult {
match Piix4CmosRtcIoPort(io_port) {
Piix4CmosRtcIoPort::ADDRESS | Piix4CmosRtcIoPort::DATA => {
return self.inner.io_write(io_port, data)
}
Piix4CmosRtcIoPort::ADDRESS_SHADOW_2 | Piix4CmosRtcIoPort::DATA_SHADOW_2 => {
return self.inner.io_write(io_port - 4, data)
}
Piix4CmosRtcIoPort::EXTENDED_ADDRESS | Piix4CmosRtcIoPort::ADDRESS_SHADOW_3 => {
self.state.ext_addr = data[0] & 0x7F;
}
Piix4CmosRtcIoPort::EXTENDED_DATA | Piix4CmosRtcIoPort::DATA_SHADOW_3 => {
self.inner.set_cmos_byte(self.state.ext_addr + 128, data[0]);
}
_ => return IoResult::Err(IoError::InvalidRegister),
}
IoResult::Ok
}
fn get_static_regions(&mut self) -> &[(&str, RangeInclusive<u16>)] {
&[
(
"io",
(Piix4CmosRtcIoPort::ADDRESS.0)..=(Piix4CmosRtcIoPort::DATA.0),
),
(
"io-ext",
(Piix4CmosRtcIoPort::EXTENDED_ADDRESS.0)..=(Piix4CmosRtcIoPort::DATA_SHADOW_3.0),
),
]
}
}
impl PollDevice for Piix4CmosRtc {
fn poll_device(&mut self, cx: &mut std::task::Context<'_>) {
self.inner.poll_device(cx)
}
}
mod save_restore {
use super::*;
use vmcore::save_restore::RestoreError;
use vmcore::save_restore::SaveError;
use vmcore::save_restore::SaveRestore;
mod state {
use mesh::payload::Protobuf;
use vmcore::save_restore::SaveRestore;
use vmcore::save_restore::SavedStateRoot;
#[derive(Protobuf, SavedStateRoot)]
#[mesh(package = "chipset.piix4.cmos_rtc")]
pub struct SavedState {
#[mesh(1)]
pub ext_addr: u8,
#[mesh(2)]
pub inner: <chipset::cmos_rtc::Rtc as SaveRestore>::SavedState,
}
}
impl SaveRestore for Piix4CmosRtc {
type SavedState = state::SavedState;
fn save(&mut self) -> Result<Self::SavedState, SaveError> {
let Piix4CmosRtcState { ext_addr } = self.state;
let saved_state = state::SavedState {
ext_addr,
inner: self.inner.save()?,
};
Ok(saved_state)
}
fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
let state::SavedState { ext_addr, inner } = state;
self.state = Piix4CmosRtcState { ext_addr };
self.inner.restore(inner)?;
Ok(())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use local_clock::MockLocalClock;
fn new_test_rtc() -> (
pal_async::DefaultPool,
vmcore::vmtime::VmTimeKeeper,
Piix4CmosRtc,
) {
let mut pool = pal_async::DefaultPool::new();
let driver = pool.driver();
let vm_time_keeper =
vmcore::vmtime::VmTimeKeeper::new(&driver, vmcore::vmtime::VmTime::from_100ns(0));
let vm_time_source = pool
.run_until(vm_time_keeper.builder().build(&driver))
.unwrap();
let rtc = Piix4CmosRtc::new(
Box::new(MockLocalClock::new()),
LineInterrupt::detached(),
&vm_time_source,
None,
false,
);
(pool, vm_time_keeper, rtc)
}
fn get_cmos_data(rtc: &mut Piix4CmosRtc, addr: u8) -> u8 {
let mut temp = [addr];
rtc.io_write(Piix4CmosRtcIoPort::ADDRESS.0, &temp).unwrap();
rtc.io_read(Piix4CmosRtcIoPort::DATA.0, &mut temp).unwrap();
temp[0]
}
fn set_cmos_data(rtc: &mut Piix4CmosRtc, addr: u8, data: u8) {
let mut temp = [addr];
rtc.io_write(Piix4CmosRtcIoPort::ADDRESS.0, &temp).unwrap();
temp[0] = data;
rtc.io_write(Piix4CmosRtcIoPort::DATA.0, &temp).unwrap();
}
fn get_ext_cmos_data(rtc: &mut Piix4CmosRtc, addr: u8) -> u8 {
let mut temp = [addr];
rtc.io_write(Piix4CmosRtcIoPort::EXTENDED_ADDRESS.0, &temp)
.unwrap();
rtc.io_read(Piix4CmosRtcIoPort::EXTENDED_DATA.0, &mut temp)
.unwrap();
temp[0]
}
fn set_ext_cmos_data(rtc: &mut Piix4CmosRtc, addr: u8, data: u8) {
let mut temp = [addr];
rtc.io_write(Piix4CmosRtcIoPort::EXTENDED_ADDRESS.0, &temp)
.unwrap();
temp[0] = data;
rtc.io_write(Piix4CmosRtcIoPort::EXTENDED_DATA.0, &temp)
.unwrap();
}
fn get_cmos_data_shadow(rtc: &mut Piix4CmosRtc, addr: u8) -> u8 {
let mut temp = [addr];
rtc.io_write(Piix4CmosRtcIoPort::ADDRESS_SHADOW_2.0, &temp)
.unwrap();
rtc.io_read(Piix4CmosRtcIoPort::DATA_SHADOW_2.0, &mut temp)
.unwrap();
temp[0]
}
fn set_cmos_data_shadow(rtc: &mut Piix4CmosRtc, addr: u8, data: u8) {
let mut temp = [addr];
rtc.io_write(Piix4CmosRtcIoPort::ADDRESS_SHADOW_2.0, &temp)
.unwrap();
temp[0] = data;
rtc.io_write(Piix4CmosRtcIoPort::DATA_SHADOW_2.0, &temp)
.unwrap();
}
fn get_ext_cmos_data_shadow(rtc: &mut Piix4CmosRtc, addr: u8) -> u8 {
let mut temp = [addr];
rtc.io_write(Piix4CmosRtcIoPort::ADDRESS_SHADOW_3.0, &temp)
.unwrap();
rtc.io_read(Piix4CmosRtcIoPort::DATA_SHADOW_3.0, &mut temp)
.unwrap();
temp[0]
}
fn set_ext_cmos_data_shadow(rtc: &mut Piix4CmosRtc, addr: u8, data: u8) {
let mut temp = [addr];
rtc.io_write(Piix4CmosRtcIoPort::ADDRESS_SHADOW_3.0, &temp)
.unwrap();
temp[0] = data;
rtc.io_write(Piix4CmosRtcIoPort::DATA_SHADOW_3.0, &temp)
.unwrap();
}
#[test]
fn test_writeable() {
let (_, _, mut rtc) = new_test_rtc();
for i in 0x0F..=0x7F {
if i == 0x32 {
continue;
}
set_cmos_data(&mut rtc, i, 0xFF);
assert_eq!(get_cmos_data(&mut rtc, i), 0xFF);
assert_eq!(get_cmos_data_shadow(&mut rtc, i), 0xFF);
set_cmos_data(&mut rtc, i, 0);
assert_eq!(get_cmos_data(&mut rtc, i), 0);
assert_eq!(get_cmos_data_shadow(&mut rtc, i), 0);
set_cmos_data_shadow(&mut rtc, i, 0xFF);
assert_eq!(get_cmos_data(&mut rtc, i), 0xFF);
assert_eq!(get_cmos_data_shadow(&mut rtc, i), 0xFF);
set_cmos_data_shadow(&mut rtc, i, 0);
assert_eq!(get_cmos_data(&mut rtc, i), 0);
assert_eq!(get_cmos_data_shadow(&mut rtc, i), 0);
}
}
#[test]
fn test_writeable_ext() {
let (_, _, mut rtc) = new_test_rtc();
for i in 0..=0x7F {
set_ext_cmos_data(&mut rtc, i, 0xFF);
assert_eq!(get_ext_cmos_data(&mut rtc, i), 0xFF);
assert_eq!(get_ext_cmos_data_shadow(&mut rtc, i), 0xFF);
set_ext_cmos_data(&mut rtc, i, 0);
assert_eq!(get_ext_cmos_data(&mut rtc, i), 0);
assert_eq!(get_ext_cmos_data_shadow(&mut rtc, i), 0);
set_ext_cmos_data_shadow(&mut rtc, i, 0xFF);
assert_eq!(get_ext_cmos_data(&mut rtc, i), 0xFF);
assert_eq!(get_ext_cmos_data_shadow(&mut rtc, i), 0xFF);
set_ext_cmos_data_shadow(&mut rtc, i, 0);
assert_eq!(get_ext_cmos_data(&mut rtc, i), 0);
assert_eq!(get_ext_cmos_data_shadow(&mut rtc, i), 0);
}
}
}