1use 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 #[inspect(mut)]
43 inner: chipset::cmos_rtc::Rtc,
44
45 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 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 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 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 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}