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