1#![expect(missing_docs)]
5#![forbid(unsafe_code)]
6
7use chipset_device::ChipsetDevice;
8use chipset_device::io::IoError;
9use chipset_device::io::IoResult;
10use chipset_device::pio::ControlPortIoIntercept;
11use chipset_device::pio::PortIoIntercept;
12use chipset_device::pio::RegisterPortIoIntercept;
13use chipset_device::poll_device::PollDevice;
14use inspect::Inspect;
15use inspect::InspectMut;
16use std::task::Context;
17use vmcore::device_state::ChangeDeviceState;
18use vmcore::vmtime::VmTimeAccess;
19use watchdog_core::WatchdogServices;
20use watchdog_core::platform::WatchdogPlatform;
21
22open_enum::open_enum! {
23 enum WatchdogPort: u16 {
24 ADDRESS = 0x0,
25 DATA = 0x4,
26 }
27}
28
29pub struct GuestwatchdogRt {
30 pio_static_wdat_port: Box<dyn ControlPortIoIntercept>,
31}
32
33#[derive(Debug, Inspect)]
34pub struct GuestWatchdogState {
35 watchdog_reg: u32,
36}
37
38#[derive(InspectMut)]
39pub struct GuestWatchdogServices {
40 #[inspect(skip)]
42 rt: GuestwatchdogRt,
43
44 watchdog: WatchdogServices,
46
47 state: GuestWatchdogState,
49}
50
51impl GuestWatchdogServices {
52 pub async fn new(
53 vmtime: VmTimeAccess,
54 watchdog_platform: Box<dyn WatchdogPlatform>,
55 register_pio: &mut dyn RegisterPortIoIntercept,
56 pio_wdat_port: u16,
57 is_restoring: bool,
58 ) -> GuestWatchdogServices {
59 let mut pio_static_wdat_port = register_pio.new_io_region("wdat_port", 8);
60
61 pio_static_wdat_port.map(pio_wdat_port);
62
63 GuestWatchdogServices {
64 watchdog: WatchdogServices::new(
65 "guest-watchdog",
66 vmtime,
67 watchdog_platform,
68 is_restoring,
69 )
70 .await,
71 state: GuestWatchdogState { watchdog_reg: 0 },
72 rt: GuestwatchdogRt {
73 pio_static_wdat_port,
74 },
75 }
76 }
77
78 fn read_data(&mut self, addr: u32) -> u32 {
79 match WatchdogRegister(addr) {
80 WatchdogRegister::WATCHDOG_RESOLUTION
81 | WatchdogRegister::WATCHDOG_CONFIG
82 | WatchdogRegister::WATCHDOG_COUNT => {
83 let reg = bios_cmd_to_watchdog_register(WatchdogRegister(addr)).unwrap();
84 match self.watchdog.read(reg) {
85 Ok(val) => val,
86 Err(err) => {
87 tracelimit::warn_ratelimited!(
88 error = &err as &dyn std::error::Error,
89 "Error while reading from watchdog device"
90 );
91 !0
92 }
93 }
94 }
95 _ => {
96 tracelimit::warn_ratelimited!(?addr, "unknown bios read");
97 !0
98 }
99 }
100 }
101
102 fn write_data(&mut self, addr: u32, data: u32) {
103 match WatchdogRegister(addr) {
104 WatchdogRegister::WATCHDOG_RESOLUTION
105 | WatchdogRegister::WATCHDOG_CONFIG
106 | WatchdogRegister::WATCHDOG_COUNT => {
107 let reg = bios_cmd_to_watchdog_register(WatchdogRegister(addr)).unwrap();
108 match self.watchdog.write(reg, data) {
109 Ok(()) => (),
110 Err(err) => {
111 tracelimit::warn_ratelimited!(
112 error = &err as &dyn std::error::Error,
113 "Error while writing to watchdog device"
114 );
115 }
116 }
117 }
118 _ => tracelimit::warn_ratelimited!(addr, data, "unknown bios write"),
119 }
120 }
121}
122
123fn bios_cmd_to_watchdog_register(cmd: WatchdogRegister) -> Option<watchdog_core::Register> {
124 let res = match cmd {
125 WatchdogRegister::WATCHDOG_RESOLUTION => watchdog_core::Register::Resolution,
126 WatchdogRegister::WATCHDOG_CONFIG => watchdog_core::Register::Config,
127 WatchdogRegister::WATCHDOG_COUNT => watchdog_core::Register::Count,
128 _ => return None,
129 };
130 Some(res)
131}
132
133impl PollDevice for GuestWatchdogServices {
134 fn poll_device(&mut self, cx: &mut Context<'_>) {
135 self.watchdog.poll(cx);
136 }
137}
138
139impl ChangeDeviceState for GuestWatchdogServices {
140 fn start(&mut self) {}
141
142 async fn stop(&mut self) {}
143
144 async fn reset(&mut self) {
145 self.watchdog.reset();
146 self.state.watchdog_reg = 0;
147 }
148}
149
150impl ChipsetDevice for GuestWatchdogServices {
151 fn supports_pio(&mut self) -> Option<&mut dyn PortIoIntercept> {
152 Some(self)
153 }
154
155 fn supports_poll_device(&mut self) -> Option<&mut dyn PollDevice> {
156 Some(self)
157 }
158}
159
160impl PortIoIntercept for GuestWatchdogServices {
161 fn io_read(&mut self, io_port: u16, data: &mut [u8]) -> IoResult {
162 if data.len() != 4 {
163 return IoResult::Err(IoError::InvalidAccessSize);
164 }
165
166 if let Some(offset) = self.rt.pio_static_wdat_port.offset_of(io_port) {
167 let v = match WatchdogPort(offset) {
168 WatchdogPort::ADDRESS => self.state.watchdog_reg,
169 WatchdogPort::DATA => self.read_data(self.state.watchdog_reg),
170 _ => return IoResult::Err(IoError::InvalidRegister),
171 };
172
173 data.copy_from_slice(&v.to_ne_bytes());
174 return IoResult::Ok;
175 }
176 IoResult::Err(IoError::InvalidRegister)
177 }
178
179 fn io_write(&mut self, io_port: u16, data: &[u8]) -> IoResult {
180 if data.len() != 4 {
181 return IoResult::Err(IoError::InvalidAccessSize);
182 }
183
184 if let Some(offset) = self.rt.pio_static_wdat_port.offset_of(io_port) {
185 let v = u32::from_ne_bytes(data.try_into().unwrap());
186 match WatchdogPort(offset) {
187 WatchdogPort::ADDRESS => self.state.watchdog_reg = v,
188 WatchdogPort::DATA => self.write_data(self.state.watchdog_reg, v),
189 _ => return IoResult::Err(IoError::InvalidRegister),
190 };
191 return IoResult::Ok;
192 }
193 IoResult::Err(IoError::InvalidRegister)
194 }
195}
196
197open_enum::open_enum! {
198 pub enum WatchdogRegister: u32 {
199 WATCHDOG_CONFIG = 0x27,
200 WATCHDOG_RESOLUTION = 0x28,
201 WATCHDOG_COUNT = 0x29,
202 }
203}
204
205mod save_restore {
206 use super::*;
207 use vmcore::save_restore::RestoreError;
208 use vmcore::save_restore::SaveError;
209 use vmcore::save_restore::SaveRestore;
210
211 mod state {
212 use mesh::payload::Protobuf;
213 use vmcore::save_restore::SaveRestore;
214 use vmcore::save_restore::SavedStateRoot;
215
216 #[derive(Protobuf, SavedStateRoot)]
217 #[mesh(package = "chipset.watchdog.guest")]
218 pub struct SavedState {
219 #[mesh(1)]
220 pub watchdog_reg: u32,
221 #[mesh(2)]
222 pub inner: <watchdog_core::WatchdogServices as SaveRestore>::SavedState,
223 }
224 }
225
226 impl SaveRestore for GuestWatchdogServices {
227 type SavedState = state::SavedState;
228
229 fn save(&mut self) -> Result<Self::SavedState, SaveError> {
230 let GuestWatchdogState { watchdog_reg } = self.state;
231 let saved_state = state::SavedState {
232 watchdog_reg,
233 inner: self.watchdog.save()?,
234 };
235
236 Ok(saved_state)
237 }
238
239 fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
240 let state::SavedState {
241 watchdog_reg,
242 inner,
243 } = state;
244 self.state = GuestWatchdogState { watchdog_reg };
245 self.watchdog.restore(inner)?;
246 Ok(())
247 }
248 }
249}