Skip to main content

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            #[expect(clippy::allow_attributes)]
67            pub trait $trait {
68                type Error: 'static + std::error::Error + Send + Sync;
69
70                /// Gets the partition's capabilities.
71                fn caps(&self) -> &$caps;
72
73                /// Commits any state changes made with the `set_*` methods.
74                fn commit(&mut self) -> Result<(), Self::Error>;
75
76                $(
77                /// Gets the specified state.
78                fn $get(&mut self) -> Result<$ty, Self::Error>;
79                /// Sets the specified state.
80                fn $set(&mut self, value: &$ty) -> Result<(), Self::Error>;
81                )*
82
83                /// Save all state that can be restored by a call to restore.
84                #[allow(unused_mut)]
85                fn save_all(&mut self) -> Result<$save_state, $crate::state::StateError<Self::Error>> {
86                    let mut save_state = $save_state::default();
87                    $(
88                        if <$ty as $crate::state::StateElement<$caps, $vp>>::is_present(self.caps()) {
89                            save_state.$get = Some(self.$get().map_err(|err| $crate::state::StateError{phase: concat!("save ", stringify!($id)), err})?);
90                        }
91                    )*
92
93                    Ok(save_state)
94                }
95
96                /// Restore state elements saved in save state.
97                #[allow(unused_variables)]
98                fn restore_all(&mut self, state: &$save_state) -> Result<(), $crate::state::StateError<Self::Error>> {
99                    $(
100                        if let Some(value) = state.$get.as_ref() {
101                            // TODO: assert good or not?
102                            assert!(<$ty as $crate::state::StateElement<$caps, $vp>>::is_present(self.caps()));
103                            self.$set(value).map_err(|err| $crate::state::StateError{phase: concat!("restore ", stringify!($id)), err})?;
104                            if cfg!(debug_assertions) {
105                                if <$ty as $crate::state::StateElement<$caps, $vp>>::can_compare(self.caps()) {
106                                    assert_eq!(&self.$get().expect($id), value, "restore state mismatch (actual/expected)");
107                                }
108                            }
109                        }
110                    )*
111
112                    self.commit().map_err(|err| $crate::state::StateError{phase: "commit restore", err})
113                }
114
115                /// Resets all the state elements to their initial state (after machine reset).
116                fn reset_all(&mut self, vp_info: &$vp) -> Result<(), $crate::state::StateError<Self::Error>> {
117                    $(
118                        if <$ty as $crate::state::StateElement<$caps, $vp>>::is_present(self.caps()) {
119                            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})?;
120                        }
121                    )*
122
123                    if cfg!(debug_assertions) {
124                        self.check_reset_all(vp_info);
125                    }
126                    Ok(())
127                }
128
129                /// Validates that all state elements are in their initial state (after machine reset).
130                #[allow(unused_variables)]
131                fn check_reset_all(&mut self, vp_info: &$vp) {
132                    $(
133                        if <$ty as $crate::state::StateElement<$caps, $vp>>::can_compare(self.caps()) && <$ty as $crate::state::StateElement<$caps, $vp>>::is_present(self.caps()) {
134                            assert_eq!(self.$get().expect($id), <$ty as $crate::state::StateElement<$caps, $vp>>::at_reset(self.caps(), vp_info), "reset state mismatch (actual/expected)");
135                        }
136                    )*
137                }
138
139                #[allow(unused_variables, unused_mut)]
140                fn inspect_all(&mut self) -> impl ::inspect::InspectMut {
141                    inspect::adhoc_mut(|req| {
142                        let mut resp = req.respond();
143                        $(
144                            if <$ty as $crate::state::StateElement<$caps, $vp>>::is_present(self.caps()) {
145                                resp.field_with($id, || self.$get().ok());
146                            }
147                        )*
148                    })
149                }
150            }
151
152            /// Saved state type that can be used to save or restore.
153            #[derive(Debug, Default, PartialEq, Eq, mesh_protobuf::Protobuf, vmcore::save_restore::SavedStateRoot)]
154            #[mesh(package = $package)]
155            pub struct $save_state {
156                $(
157                    #[mesh($field_num)]
158                    $get: Option<$ty>,
159                )*
160            }
161        };
162    }
163
164    pub(crate) use state_trait;
165}