chipset_legacy/
piix4_cmos_rtc.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! PIIX4 - CMOS RTC
5//!
6//! Extends basic x86 CMOS RTC with a few additional ports + more RAM.
7
8use chipset_device::ChipsetDevice;
9use chipset_device::io::IoError;
10use chipset_device::io::IoResult;
11use chipset_device::pio::PortIoIntercept;
12use chipset_device::poll_device::PollDevice;
13use inspect::Inspect;
14use inspect::InspectMut;
15use local_clock::InspectableLocalClock;
16use std::ops::RangeInclusive;
17use vmcore::device_state::ChangeDeviceState;
18use vmcore::line_interrupt::LineInterrupt;
19use vmcore::vmtime::VmTimeSource;
20
21open_enum::open_enum! {
22    enum Piix4CmosRtcIoPort: u16 {
23        ADDRESS          = 0x70,
24        DATA             = 0x71,
25        EXTENDED_ADDRESS = 0x72,
26        EXTENDED_DATA    = 0x73,
27        ADDRESS_SHADOW_2 = 0x74,
28        DATA_SHADOW_2    = 0x75,
29        ADDRESS_SHADOW_3 = 0x76,
30        DATA_SHADOW_3    = 0x77,
31    }
32}
33
34#[derive(Debug, Inspect)]
35struct Piix4CmosRtcState {
36    ext_addr: u8,
37}
38
39#[derive(InspectMut)]
40pub struct Piix4CmosRtc {
41    // Sub-emulators
42    #[inspect(mut)]
43    inner: chipset::cmos_rtc::Rtc,
44
45    // Volatile state
46    state: Piix4CmosRtcState,
47}
48
49impl Piix4CmosRtc {
50    pub fn new(
51        real_time_source: Box<dyn InspectableLocalClock>,
52        interrupt: LineInterrupt,
53        vmtime_source: &VmTimeSource,
54        initial_cmos: Option<[u8; 256]>,
55        enlightened_interrupts: bool,
56    ) -> Piix4CmosRtc {
57        Piix4CmosRtc {
58            state: Piix4CmosRtcState { ext_addr: 0 },
59            inner: chipset::cmos_rtc::Rtc::new(
60                real_time_source,
61                interrupt,
62                vmtime_source,
63                0x32,
64                initial_cmos,
65                enlightened_interrupts,
66            ),
67        }
68    }
69}
70
71impl ChangeDeviceState for Piix4CmosRtc {
72    fn start(&mut self) {}
73
74    async fn stop(&mut self) {}
75
76    async fn reset(&mut self) {
77        self.inner.reset().await;
78    }
79}
80
81impl ChipsetDevice for Piix4CmosRtc {
82    fn supports_pio(&mut self) -> Option<&mut dyn PortIoIntercept> {
83        Some(self)
84    }
85
86    fn supports_poll_device(&mut self) -> Option<&mut dyn PollDevice> {
87        Some(self)
88    }
89}
90
91impl PortIoIntercept for Piix4CmosRtc {
92    fn io_read(&mut self, io_port: u16, data: &mut [u8]) -> IoResult {
93        // We assume all accesses are one byte in size. Attempts to
94        // access larger sizes will return a single byte of information
95        // (zero-extended to the size of the access).
96        data[0] = match Piix4CmosRtcIoPort(io_port) {
97            Piix4CmosRtcIoPort::ADDRESS | Piix4CmosRtcIoPort::DATA => {
98                return self.inner.io_read(io_port, data);
99            }
100            Piix4CmosRtcIoPort::ADDRESS_SHADOW_2 | Piix4CmosRtcIoPort::DATA_SHADOW_2 => {
101                return self.inner.io_read(io_port - 4, data);
102            }
103            Piix4CmosRtcIoPort::EXTENDED_ADDRESS | Piix4CmosRtcIoPort::ADDRESS_SHADOW_3 => {
104                self.state.ext_addr
105            }
106            Piix4CmosRtcIoPort::EXTENDED_DATA | Piix4CmosRtcIoPort::DATA_SHADOW_3 => {
107                self.inner.get_cmos_byte(self.state.ext_addr + 128)
108            }
109            _ => return IoResult::Err(IoError::InvalidRegister),
110        };
111
112        IoResult::Ok
113    }
114
115    fn io_write(&mut self, io_port: u16, data: &[u8]) -> IoResult {
116        // We assume all accesses are one byte in size. Attempts to
117        // access larger sizes will return a single byte of information
118        // (zero-extended to the size of the access).
119        match Piix4CmosRtcIoPort(io_port) {
120            Piix4CmosRtcIoPort::ADDRESS | Piix4CmosRtcIoPort::DATA => {
121                return self.inner.io_write(io_port, data);
122            }
123            Piix4CmosRtcIoPort::ADDRESS_SHADOW_2 | Piix4CmosRtcIoPort::DATA_SHADOW_2 => {
124                return self.inner.io_write(io_port - 4, data);
125            }
126            Piix4CmosRtcIoPort::EXTENDED_ADDRESS | Piix4CmosRtcIoPort::ADDRESS_SHADOW_3 => {
127                self.state.ext_addr = data[0] & 0x7F;
128            }
129            Piix4CmosRtcIoPort::EXTENDED_DATA | Piix4CmosRtcIoPort::DATA_SHADOW_3 => {
130                self.inner.set_cmos_byte(self.state.ext_addr + 128, data[0]);
131            }
132            _ => return IoResult::Err(IoError::InvalidRegister),
133        }
134
135        IoResult::Ok
136    }
137
138    fn get_static_regions(&mut self) -> &[(&str, RangeInclusive<u16>)] {
139        &[
140            (
141                "io",
142                (Piix4CmosRtcIoPort::ADDRESS.0)..=(Piix4CmosRtcIoPort::DATA.0),
143            ),
144            (
145                "io-ext",
146                (Piix4CmosRtcIoPort::EXTENDED_ADDRESS.0)..=(Piix4CmosRtcIoPort::DATA_SHADOW_3.0),
147            ),
148        ]
149    }
150}
151
152impl PollDevice for Piix4CmosRtc {
153    fn poll_device(&mut self, cx: &mut std::task::Context<'_>) {
154        self.inner.poll_device(cx)
155    }
156}
157
158mod save_restore {
159    use super::*;
160    use vmcore::save_restore::RestoreError;
161    use vmcore::save_restore::SaveError;
162    use vmcore::save_restore::SaveRestore;
163
164    mod state {
165        use mesh::payload::Protobuf;
166        use vmcore::save_restore::SaveRestore;
167        use vmcore::save_restore::SavedStateRoot;
168
169        #[derive(Protobuf, SavedStateRoot)]
170        #[mesh(package = "chipset.piix4.cmos_rtc")]
171        pub struct SavedState {
172            #[mesh(1)]
173            pub ext_addr: u8,
174            #[mesh(2)]
175            pub inner: <chipset::cmos_rtc::Rtc as SaveRestore>::SavedState,
176        }
177    }
178
179    impl SaveRestore for Piix4CmosRtc {
180        type SavedState = state::SavedState;
181
182        fn save(&mut self) -> Result<Self::SavedState, SaveError> {
183            let Piix4CmosRtcState { ext_addr } = self.state;
184
185            let saved_state = state::SavedState {
186                ext_addr,
187                inner: self.inner.save()?,
188            };
189
190            Ok(saved_state)
191        }
192
193        fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
194            let state::SavedState { ext_addr, inner } = state;
195            self.state = Piix4CmosRtcState { ext_addr };
196            self.inner.restore(inner)?;
197            Ok(())
198        }
199    }
200}
201
202#[cfg(test)]
203mod tests {
204    use super::*;
205    use local_clock::MockLocalClock;
206
207    fn new_test_rtc() -> (
208        pal_async::DefaultPool,
209        vmcore::vmtime::VmTimeKeeper,
210        Piix4CmosRtc,
211    ) {
212        let mut pool = pal_async::DefaultPool::new();
213        let driver = pool.driver();
214        let vm_time_keeper =
215            vmcore::vmtime::VmTimeKeeper::new(&driver, vmcore::vmtime::VmTime::from_100ns(0));
216        let vm_time_source = pool
217            .run_until(vm_time_keeper.builder().build(&driver))
218            .unwrap();
219
220        let rtc = Piix4CmosRtc::new(
221            Box::new(MockLocalClock::new()),
222            LineInterrupt::detached(),
223            &vm_time_source,
224            None,
225            false,
226        );
227
228        (pool, vm_time_keeper, rtc)
229    }
230
231    fn get_cmos_data(rtc: &mut Piix4CmosRtc, addr: u8) -> u8 {
232        let mut temp = [addr];
233        rtc.io_write(Piix4CmosRtcIoPort::ADDRESS.0, &temp).unwrap();
234        rtc.io_read(Piix4CmosRtcIoPort::DATA.0, &mut temp).unwrap();
235        temp[0]
236    }
237
238    fn set_cmos_data(rtc: &mut Piix4CmosRtc, addr: u8, data: u8) {
239        let mut temp = [addr];
240        rtc.io_write(Piix4CmosRtcIoPort::ADDRESS.0, &temp).unwrap();
241        temp[0] = data;
242        rtc.io_write(Piix4CmosRtcIoPort::DATA.0, &temp).unwrap();
243    }
244
245    fn get_ext_cmos_data(rtc: &mut Piix4CmosRtc, addr: u8) -> u8 {
246        let mut temp = [addr];
247        rtc.io_write(Piix4CmosRtcIoPort::EXTENDED_ADDRESS.0, &temp)
248            .unwrap();
249        rtc.io_read(Piix4CmosRtcIoPort::EXTENDED_DATA.0, &mut temp)
250            .unwrap();
251        temp[0]
252    }
253
254    fn set_ext_cmos_data(rtc: &mut Piix4CmosRtc, addr: u8, data: u8) {
255        let mut temp = [addr];
256        rtc.io_write(Piix4CmosRtcIoPort::EXTENDED_ADDRESS.0, &temp)
257            .unwrap();
258        temp[0] = data;
259        rtc.io_write(Piix4CmosRtcIoPort::EXTENDED_DATA.0, &temp)
260            .unwrap();
261    }
262
263    fn get_cmos_data_shadow(rtc: &mut Piix4CmosRtc, addr: u8) -> u8 {
264        let mut temp = [addr];
265        rtc.io_write(Piix4CmosRtcIoPort::ADDRESS_SHADOW_2.0, &temp)
266            .unwrap();
267        rtc.io_read(Piix4CmosRtcIoPort::DATA_SHADOW_2.0, &mut temp)
268            .unwrap();
269        temp[0]
270    }
271
272    fn set_cmos_data_shadow(rtc: &mut Piix4CmosRtc, addr: u8, data: u8) {
273        let mut temp = [addr];
274        rtc.io_write(Piix4CmosRtcIoPort::ADDRESS_SHADOW_2.0, &temp)
275            .unwrap();
276        temp[0] = data;
277        rtc.io_write(Piix4CmosRtcIoPort::DATA_SHADOW_2.0, &temp)
278            .unwrap();
279    }
280
281    fn get_ext_cmos_data_shadow(rtc: &mut Piix4CmosRtc, addr: u8) -> u8 {
282        let mut temp = [addr];
283        rtc.io_write(Piix4CmosRtcIoPort::ADDRESS_SHADOW_3.0, &temp)
284            .unwrap();
285        rtc.io_read(Piix4CmosRtcIoPort::DATA_SHADOW_3.0, &mut temp)
286            .unwrap();
287        temp[0]
288    }
289
290    fn set_ext_cmos_data_shadow(rtc: &mut Piix4CmosRtc, addr: u8, data: u8) {
291        let mut temp = [addr];
292        rtc.io_write(Piix4CmosRtcIoPort::ADDRESS_SHADOW_3.0, &temp)
293            .unwrap();
294        temp[0] = data;
295        rtc.io_write(Piix4CmosRtcIoPort::DATA_SHADOW_3.0, &temp)
296            .unwrap();
297    }
298
299    #[test]
300    fn test_writeable() {
301        let (_, _, mut rtc) = new_test_rtc();
302
303        //Rigisters 0x0f..0x7f should be writable, skip 0x32 which is century field of RTC
304        for i in 0x0F..=0x7F {
305            if i == 0x32 {
306                continue;
307            }
308
309            set_cmos_data(&mut rtc, i, 0xFF);
310            assert_eq!(get_cmos_data(&mut rtc, i), 0xFF);
311            assert_eq!(get_cmos_data_shadow(&mut rtc, i), 0xFF);
312            set_cmos_data(&mut rtc, i, 0);
313            assert_eq!(get_cmos_data(&mut rtc, i), 0);
314            assert_eq!(get_cmos_data_shadow(&mut rtc, i), 0);
315            set_cmos_data_shadow(&mut rtc, i, 0xFF);
316            assert_eq!(get_cmos_data(&mut rtc, i), 0xFF);
317            assert_eq!(get_cmos_data_shadow(&mut rtc, i), 0xFF);
318            set_cmos_data_shadow(&mut rtc, i, 0);
319            assert_eq!(get_cmos_data(&mut rtc, i), 0);
320            assert_eq!(get_cmos_data_shadow(&mut rtc, i), 0);
321        }
322    }
323
324    #[test]
325    fn test_writeable_ext() {
326        let (_, _, mut rtc) = new_test_rtc();
327
328        //Rigisters 0x80..0xff should be writable through extended gate 0x72/0x73
329        for i in 0..=0x7F {
330            set_ext_cmos_data(&mut rtc, i, 0xFF);
331            assert_eq!(get_ext_cmos_data(&mut rtc, i), 0xFF);
332            assert_eq!(get_ext_cmos_data_shadow(&mut rtc, i), 0xFF);
333            set_ext_cmos_data(&mut rtc, i, 0);
334            assert_eq!(get_ext_cmos_data(&mut rtc, i), 0);
335            assert_eq!(get_ext_cmos_data_shadow(&mut rtc, i), 0);
336            set_ext_cmos_data_shadow(&mut rtc, i, 0xFF);
337            assert_eq!(get_ext_cmos_data(&mut rtc, i), 0xFF);
338            assert_eq!(get_ext_cmos_data_shadow(&mut rtc, i), 0xFF);
339            set_ext_cmos_data_shadow(&mut rtc, i, 0);
340            assert_eq!(get_ext_cmos_data(&mut rtc, i), 0);
341            assert_eq!(get_ext_cmos_data_shadow(&mut rtc, i), 0);
342        }
343    }
344}