watchdog_vmgs_format/
lib.rs1#![forbid(unsafe_code)]
10
11use thiserror::Error;
12use vmcore::non_volatile_store::NonVolatileStore;
13
14#[derive(Debug, Error)]
16#[error("expected single byte (0 or 1), got {0} bytes, starting with {1:?}")]
17pub struct InvalidFormatError(usize, Option<u8>);
18
19struct WatchdogVmgsFormat {
21 boot_failure: bool,
22}
23
24impl WatchdogVmgsFormat {
25 fn new() -> Self {
27 Self {
28 boot_failure: false,
29 }
30 }
31
32 fn update_from_slice(&mut self, buf: &[u8]) -> Result<(), InvalidFormatError> {
35 let boot_status = match buf {
36 [] => return Err(InvalidFormatError(0, None)),
37 [0] => false,
38 [1] => true,
39 [other, ..] => return Err(InvalidFormatError(buf.len(), Some(*other))),
40 };
41
42 self.boot_failure = boot_status;
43
44 Ok(())
45 }
46
47 fn as_slice(&self) -> &[u8] {
49 if self.boot_failure { &[1] } else { &[0] }
50 }
51}
52
53#[derive(Debug, Error)]
56#[expect(missing_docs)] pub enum WatchdogVmgsFormatStoreError {
58 #[error("could not access non-volatile store")]
59 NonVolatileStoreAccessError(#[source] vmcore::non_volatile_store::NonVolatileStoreError),
60 #[error("invalid data pull from non-volatile store")]
61 InvalidFormat(#[source] InvalidFormatError),
62}
63
64pub struct WatchdogVmgsFormatStore {
67 store: Box<dyn NonVolatileStore>,
68 state: WatchdogVmgsFormat,
69}
70
71impl WatchdogVmgsFormatStore {
72 pub async fn new(
75 mut store: Box<dyn NonVolatileStore>,
76 ) -> Result<Self, WatchdogVmgsFormatStoreError> {
77 use WatchdogVmgsFormatStoreError as Error;
78
79 let buf = store
80 .restore()
81 .await
82 .map_err(Error::NonVolatileStoreAccessError)?;
83
84 let mut state = WatchdogVmgsFormat::new();
85
86 if let Some(buf) = buf {
87 state
88 .update_from_slice(&buf)
89 .map_err(Error::InvalidFormat)?;
90 }
91
92 Ok(Self { store, state })
93 }
94
95 async fn flush(&mut self) -> Result<(), WatchdogVmgsFormatStoreError> {
96 use WatchdogVmgsFormatStoreError as Error;
97
98 self.store
99 .persist(self.state.as_slice().to_vec())
100 .await
101 .map_err(Error::NonVolatileStoreAccessError)?;
102
103 Ok(())
104 }
105
106 pub async fn set_boot_failure(&mut self) -> Result<(), WatchdogVmgsFormatStoreError> {
108 let prev = self.state.boot_failure;
109 self.state.boot_failure = true;
110
111 if !prev {
113 self.flush().await?;
114 }
115
116 Ok(())
117 }
118
119 pub async fn read_and_clear_boot_status(
122 &mut self,
123 ) -> Result<bool, WatchdogVmgsFormatStoreError> {
124 let prev = self.state.boot_failure;
125 self.state.boot_failure = false;
126
127 if prev {
129 self.flush().await?;
130 }
131
132 Ok(prev)
133 }
134}