virt/
state.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Infrastructure to define VM state.
5
6pub(crate) use macros::state_trait;
7
8use hvdef::HvRegisterValue;
9use inspect::Inspect;
10use std::fmt::Debug;
11use thiserror::Error;
12
13pub trait StateElement<C, V>: Sized + Inspect {
14    /// Returns whether this state is present for the partition, based on the
15    /// partition capabilities.
16    fn is_present(caps: &C) -> bool;
17
18    /// Returns the value at VM reset.
19    fn at_reset(caps: &C, vp: &V) -> Self;
20
21    /// Returns whether it's possible to read this value and compare it to an
22    /// expected value.
23    ///
24    /// This will be false when the value may change as soon as its set (e.g., a
25    /// timestamp counter for hypervisors that cannot freeze time).
26    fn can_compare(_caps: &C) -> bool {
27        true
28    }
29}
30
31pub trait HvRegisterState<T, const COUNT: usize>: Default {
32    fn names(&self) -> &'static [T; COUNT];
33    fn get_values<'a>(&self, it: impl Iterator<Item = &'a mut HvRegisterValue>);
34    fn set_values(&mut self, it: impl Iterator<Item = HvRegisterValue>);
35}
36
37#[derive(Debug, Error)]
38#[error("state access error, phase {phase}")]
39pub struct StateError<T: 'static + Debug + std::error::Error> {
40    pub(crate) phase: &'static str,
41    #[source]
42    pub(crate) err: T,
43}
44
45mod macros {
46    /// Generates a trait for getting and setting some aspect of partition state
47    /// (e.g. per-partition state, per-VP state, per-VTL state).
48    ///
49    /// The trait is made up of methods for getting and setting individual
50    /// pieces of state. Each piece can be individually present or missing for a
51    /// partition, e.g. because certain processor features are enabled or not.
52    ///
53    /// A function is generated that will reset each individual piece of state.
54    ///
55    /// Another generated function will validate that each piece of state is
56    /// equal to its reset state (which is useful to validate that a partition's
57    /// initial state is equal to its reset state).
58    ///
59    /// The intent is that the partition types implement these traits to get and
60    /// set individual pieces of state.
61    macro_rules! state_trait {
62        ($doc:tt, $trait:ident, $caps:ty, $vp:ty, $save_state:ident, $package:expr,
63            $(($field_num:literal, $id:expr, $get:ident, $set:ident, $ty:ty $(,)?)),* $(,)?
64        ) => {
65            #[doc = $doc]
66            pub trait $trait {
67                type Error: 'static + std::error::Error + Send + Sync;
68
69                /// Gets the partition's capabilities.
70                fn caps(&self) -> &$caps;
71
72                /// Commits any state changes made with the `set_*` methods.
73                fn commit(&mut self) -> Result<(), Self::Error>;
74
75                $(
76                /// Gets the specified state.
77                fn $get(&mut self) -> Result<$ty, Self::Error>;
78                /// Sets the specified state.
79                fn $set(&mut self, value: &$ty) -> Result<(), Self::Error>;
80                )*
81
82                /// Save all state that can be restored by a call to restore.
83                #[allow(unused_mut)]
84                fn save_all(&mut self) -> Result<$save_state, $crate::state::StateError<Self::Error>> {
85                    let mut save_state = $save_state::default();
86                    $(
87                        if <$ty as $crate::state::StateElement<$caps, $vp>>::is_present(self.caps()) {
88                            save_state.$get = Some(self.$get().map_err(|err| $crate::state::StateError{phase: concat!("save ", stringify!($id)), err})?);
89                        }
90                    )*
91
92                    Ok(save_state)
93                }
94
95                /// Restore state elements saved in save state.
96                #[allow(unused_variables)]
97                fn restore_all(&mut self, state: &$save_state) -> Result<(), $crate::state::StateError<Self::Error>> {
98                    $(
99                        if let Some(value) = state.$get.as_ref() {
100                            // TODO: assert good or not?
101                            assert!(<$ty as $crate::state::StateElement<$caps, $vp>>::is_present(self.caps()));
102                            self.$set(value).map_err(|err| $crate::state::StateError{phase: concat!("restore ", stringify!($id)), err})?;
103                            if cfg!(debug_assertions) {
104                                if <$ty as $crate::state::StateElement<$caps, $vp>>::can_compare(self.caps()) {
105                                    assert_eq!(&self.$get().expect($id), value, "restore state mismatch (actual/expected)");
106                                }
107                            }
108                        }
109                    )*
110
111                    self.commit().map_err(|err| $crate::state::StateError{phase: "commit restore", err})
112                }
113
114                /// Resets all the state elements to their initial state (after machine reset).
115                fn reset_all(&mut self, vp_info: &$vp) -> Result<(), $crate::state::StateError<Self::Error>> {
116                    $(
117                        if <$ty as $crate::state::StateElement<$caps, $vp>>::is_present(self.caps()) {
118                            self.$set(&<$ty as $crate::state::StateElement<$caps, $vp>>::at_reset(self.caps(), vp_info)).map_err(|err| $crate::state::StateError{phase: concat!("reset ", stringify!($id)), err})?;
119                        }
120                    )*
121
122                    if cfg!(debug_assertions) {
123                        self.check_reset_all(vp_info);
124                    }
125                    Ok(())
126                }
127
128                /// Validates that all state elements are in their initial state (after machine reset).
129                #[allow(unused_variables)]
130                fn check_reset_all(&mut self, vp_info: &$vp) {
131                    $(
132                        if <$ty as $crate::state::StateElement<$caps, $vp>>::can_compare(self.caps()) && <$ty as $crate::state::StateElement<$caps, $vp>>::is_present(self.caps()) {
133                            assert_eq!(self.$get().expect($id), <$ty as $crate::state::StateElement<$caps, $vp>>::at_reset(self.caps(), vp_info), "reset state mismatch (actual/expected)");
134                        }
135                    )*
136                }
137
138                #[allow(unused_variables, unused_mut)]
139                fn inspect_all(&mut self, req: ::inspect::Request<'_>) {
140                    let mut resp = req.respond();
141                    $(
142                        if <$ty as $crate::state::StateElement<$caps, $vp>>::is_present(self.caps()) {
143                            resp.field_with($id, || self.$get().ok());
144                        }
145                    )*
146                }
147            }
148
149            /// Saved state type that can be used to save or restore.
150            #[derive(Debug, Default, PartialEq, Eq, mesh_protobuf::Protobuf, vmcore::save_restore::SavedStateRoot)]
151            #[mesh(package = $package)]
152            pub struct $save_state {
153                $(
154                    #[mesh($field_num)]
155                    $get: Option<$ty>,
156                )*
157            }
158        };
159    }
160
161    pub(crate) use state_trait;
162}