Skip to main content

guest_watchdog/
lib.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4#![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    // Runtime glue
42    #[inspect(skip)]
43    rt: GuestwatchdogRt,
44
45    // Sub-emulators
46    watchdog: WatchdogServices,
47
48    // Volatile state
49    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}