vmcore/
non_volatile_store.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Defines the [`NonVolatileStore`] trait.
5
6#![forbid(unsafe_code)]
7
8use thiserror::Error;
9
10/// Error when accessing a [`NonVolatileStore`]
11#[derive(Error, Debug)]
12#[error("error accessing non-volatile store")]
13pub struct NonVolatileStoreError(#[from] anyhow::Error);
14
15impl NonVolatileStoreError {
16    /// Create a new [`NonVolatileStoreError`]
17    pub fn new(e: impl Into<anyhow::Error>) -> NonVolatileStoreError {
18        Self(e.into())
19    }
20}
21
22/// Save and restore hunks of data to a non-volatile storage medium.
23///
24/// E.g: certain devices contain onboard non-volatile storage (e.g: UEFI's nvram
25/// variables, the TPM's internal state, etc...) that must be persisted across
26/// reboots.
27///
28/// This trait provides a generic mechanism for persisting / restoring this kind
29/// of non-volatile data, while leaving the details of how it gets stored to
30/// supporting infrastructure.
31#[async_trait::async_trait]
32pub trait NonVolatileStore: Send + Sync {
33    /// Write `data` to a non-volatile storage medium.
34    async fn persist(&mut self, data: Vec<u8>) -> Result<(), NonVolatileStoreError>;
35
36    /// Read any previously written `data`. Returns `None` if no data exists.
37    async fn restore(&mut self) -> Result<Option<Vec<u8>>, NonVolatileStoreError>;
38}
39
40// Boilerplate: forward `NonVolatileStore` methods for `Box<dyn NonVolatileStore>`
41#[async_trait::async_trait]
42impl NonVolatileStore for Box<dyn NonVolatileStore> {
43    async fn persist(&mut self, data: Vec<u8>) -> Result<(), NonVolatileStoreError> {
44        (**self).persist(data).await
45    }
46
47    async fn restore(&mut self) -> Result<Option<Vec<u8>>, NonVolatileStoreError> {
48        (**self).restore().await
49    }
50}
51
52// Boilerplate: forward `NonVolatileStore` methods for `&mut NonVolatileStore`
53#[async_trait::async_trait]
54impl<T> NonVolatileStore for &mut T
55where
56    T: NonVolatileStore,
57{
58    async fn persist(&mut self, data: Vec<u8>) -> Result<(), NonVolatileStoreError> {
59        (**self).persist(data).await
60    }
61
62    async fn restore(&mut self) -> Result<Option<Vec<u8>>, NonVolatileStoreError> {
63        (**self).restore().await
64    }
65}
66
67/// An ephemeral implementation of [`NonVolatileStore`] backed by an in-memory
68/// buffer. Useful for tests, stateless VM scenarios.
69#[derive(Default)]
70pub struct EphemeralNonVolatileStore(Option<Vec<u8>>);
71
72impl EphemeralNonVolatileStore {
73    /// Shortcut to create a [`Box<dyn NonVolatileStore>`] backed by an
74    /// [`EphemeralNonVolatileStore`].
75    pub fn new_boxed() -> Box<dyn NonVolatileStore> {
76        Box::new(Self::default())
77    }
78}
79
80#[async_trait::async_trait]
81impl NonVolatileStore for EphemeralNonVolatileStore {
82    async fn persist(&mut self, data: Vec<u8>) -> Result<(), NonVolatileStoreError> {
83        self.0 = Some(data);
84        Ok(())
85    }
86
87    async fn restore(&mut self) -> Result<Option<Vec<u8>>, NonVolatileStoreError> {
88        Ok(self.0.clone())
89    }
90}
91
92/// Resource-related definitions.
93///
94/// TODO: split resolvers and resources, move resources to another crate.
95pub mod resources {
96    use super::EphemeralNonVolatileStore;
97    use super::NonVolatileStore;
98    use mesh::MeshPayload;
99    use std::convert::Infallible;
100    use vm_resource::CanResolveTo;
101    use vm_resource::ResolveResource;
102    use vm_resource::ResourceId;
103    use vm_resource::declare_static_resolver;
104    use vm_resource::kind::NonVolatileStoreKind;
105
106    impl CanResolveTo<ResolvedNonVolatileStore> for NonVolatileStoreKind {
107        type Input<'a> = ();
108    }
109
110    /// The output from resolving a [`NonVolatileStoreKind`].
111    pub struct ResolvedNonVolatileStore(pub Box<dyn NonVolatileStore>);
112
113    impl<T: 'static + NonVolatileStore> From<T> for ResolvedNonVolatileStore {
114        fn from(store: T) -> Self {
115            Self(Box::new(store))
116        }
117    }
118
119    /// A resolver for [`EphemeralNonVolatileStore`].
120    pub struct EphemeralNonVolatileStoreResolver;
121
122    /// A resource handle for [`EphemeralNonVolatileStore`].
123    #[derive(MeshPayload)]
124    pub struct EphemeralNonVolatileStoreHandle;
125
126    impl ResourceId<NonVolatileStoreKind> for EphemeralNonVolatileStoreHandle {
127        const ID: &'static str = "ephemeral";
128    }
129
130    declare_static_resolver! {
131        EphemeralNonVolatileStoreResolver,
132        (NonVolatileStoreKind, EphemeralNonVolatileStoreHandle),
133    }
134
135    impl ResolveResource<NonVolatileStoreKind, EphemeralNonVolatileStoreHandle>
136        for EphemeralNonVolatileStoreResolver
137    {
138        type Error = Infallible;
139        type Output = ResolvedNonVolatileStore;
140
141        fn resolve(
142            &self,
143            EphemeralNonVolatileStoreHandle: EphemeralNonVolatileStoreHandle,
144            _input: (),
145        ) -> Result<Self::Output, Infallible> {
146            Ok(EphemeralNonVolatileStore::default().into())
147        }
148    }
149}