vmcore/
non_volatile_store.rs

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