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
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    // Runtime glue
41    #[inspect(skip)]
42    rt: GuestwatchdogRt,
43
44    // Sub-emulators
45    watchdog: WatchdogServices,
46
47    // Volatile state
48    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}