underhill_core/emuplat/
watchdog.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use cvm_tracing::CVM_ALLOWED;
5use vmcore::non_volatile_store::NonVolatileStore;
6use watchdog_core::platform::WatchdogPlatform;
7use watchdog_vmgs_format::WatchdogVmgsFormatStore;
8use watchdog_vmgs_format::WatchdogVmgsFormatStoreError;
9
10/// An implementation of [`WatchdogPlatform`] for use with both the UEFI
11/// watchdog and the Guest Watchdog in Underhill.
12pub struct UnderhillWatchdog {
13    store: WatchdogVmgsFormatStore,
14    get: guest_emulation_transport::GuestEmulationTransportClient,
15    hook: Box<dyn WatchdogTimeout>,
16}
17
18#[async_trait::async_trait]
19pub trait WatchdogTimeout: Send + Sync {
20    async fn on_timeout(&self);
21}
22
23impl UnderhillWatchdog {
24    pub async fn new(
25        store: Box<dyn NonVolatileStore>,
26        get: guest_emulation_transport::GuestEmulationTransportClient,
27        hook: Box<dyn WatchdogTimeout>,
28    ) -> Result<Self, WatchdogVmgsFormatStoreError> {
29        Ok(UnderhillWatchdog {
30            store: WatchdogVmgsFormatStore::new(store).await?,
31            get,
32            hook,
33        })
34    }
35}
36
37#[async_trait::async_trait]
38impl WatchdogPlatform for UnderhillWatchdog {
39    async fn on_timeout(&mut self) {
40        let res = self.store.set_boot_failure().await;
41        if let Err(e) = res {
42            tracing::error!(
43                CVM_ALLOWED,
44                error = &e as &dyn std::error::Error,
45                "error persisting watchdog status"
46            );
47        }
48
49        // Call the hook before reporting this to the GET, as the hook may
50        // want to do something before the host tears us down.
51        self.hook.on_timeout().await;
52
53        // FUTURE: consider emitting different events for the UEFI watchdog vs.
54        // the guest watchdog
55        self.get
56            .event_log_fatal(get_protocol::EventLogId::WATCHDOG_TIMEOUT_RESET)
57            .await;
58    }
59
60    async fn read_and_clear_boot_status(&mut self) -> bool {
61        let res = self.store.read_and_clear_boot_status().await;
62        match res {
63            Ok(status) => status,
64            Err(e) => {
65                tracing::error!(
66                    CVM_ALLOWED,
67                    error = &e as &dyn std::error::Error,
68                    "error reading watchdog status"
69                );
70                // assume no failure
71                false
72            }
73        }
74    }
75}