generation_id/
lib.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Implementation of Generation ID services (shared across both PCAT and UEFI)
5
6use guestmem::GuestMemory;
7use inspect::InspectMut;
8use mesh::RecvError;
9use std::task::Context;
10use std::task::Poll;
11use vmcore::line_interrupt::LineInterrupt;
12
13/// Various runtime objects used by the GenerationId device.
14#[expect(missing_docs)] // self-explanatory fields
15pub struct GenerationIdRuntimeDeps {
16    pub gm: GuestMemory,
17    pub generation_id_recv: mesh::Receiver<[u8; 16]>,
18    pub notify_interrupt: LineInterrupt,
19}
20
21/// Device providing initial and dynamic Generation ID update capabilities.
22#[derive(InspectMut)]
23#[inspect(extra = "GenerationId::inspect_extra")]
24pub struct GenerationId {
25    // Runtime glue
26    #[inspect(skip)]
27    rt: GenerationIdRuntimeDeps,
28
29    // Volatile state
30    id: [u8; 16],
31    #[inspect(with = "ptr_to_opt_u64")]
32    ptr: [Option<u32>; 2],
33}
34
35impl GenerationId {
36    fn inspect_extra(&mut self, resp: &mut inspect::Response<'_>) {
37        resp.field_mut_with("force_update", |update| {
38            let v = if let Some(update) = update {
39                let update = update.parse()?;
40                if update {
41                    let mut id = [0; 16];
42                    getrandom::fill(&mut id).unwrap();
43                    self.set_and_update_generation_id(id);
44                    tracing::info!("Force updated genid...");
45                }
46                update
47            } else {
48                false
49            };
50            Result::<_, std::str::ParseBoolError>::Ok(v.to_string())
51        });
52    }
53}
54
55impl GenerationId {
56    /// Construct a new GenerationId device.
57    pub fn new(initial_generation_id: [u8; 16], platform: GenerationIdRuntimeDeps) -> Self {
58        Self {
59            id: initial_generation_id,
60            ptr: [None, None],
61            rt: platform,
62        }
63    }
64
65    /// Reset the GenerationId state back to what it was when first constructed.
66    pub fn reset(&mut self) {
67        // Just reset the pointer, not the ID. Since this is not a "time travel"
68        // event, there is no need to change the ID.
69        self.ptr = [None; 2];
70    }
71
72    /// Update the low bits of the generation id pointer.
73    pub fn write_generation_id_low(&mut self, data: u32) {
74        self.set_ptr(0, data)
75    }
76
77    /// Update the high bits of the generation id pointer.
78    pub fn write_generation_id_high(&mut self, data: u32) {
79        self.set_ptr(1, data)
80    }
81
82    /// Polls for new generation IDs.
83    pub fn poll(&mut self, cx: &mut Context<'_>) {
84        while let Poll::Ready(val) = self.rt.generation_id_recv.poll_recv(cx) {
85            match val {
86                Ok(val) => self.set_and_update_generation_id(val),
87                Err(RecvError::Closed) => break,
88                Err(err) => {
89                    tracing::error!(
90                        error = &err as &dyn std::error::Error,
91                        "Error receiving generation ID"
92                    );
93                    break;
94                }
95            }
96        }
97    }
98
99    /// Attempts to write the current generation ID into guest memory at
100    /// the specified location by the BIOS (PCAT/UEFI).
101    fn update_generation_id(&self) {
102        if let Some(ptr) = ptr_to_opt_u64(&self.ptr) {
103            if let Err(e) = self.rt.gm.write_at(ptr, &self.id) {
104                tracelimit::error_ratelimited!(
105                    error = &e as &dyn std::error::Error,
106                    "failed to write generation ID"
107                )
108            }
109        }
110    }
111
112    /// Sets and updates generation id
113    fn set_and_update_generation_id(&mut self, val: [u8; 16]) {
114        self.id = val;
115        self.update_generation_id();
116
117        // Pulse the interrupt line.
118        //
119        // Ideally there would be some notification from the firmware to clear
120        // the interrupt line, but the current firmware DSDT implementations do
121        // not do this.
122        self.rt.notify_interrupt.set_level(true);
123        self.rt.notify_interrupt.set_level(false);
124    }
125
126    fn set_ptr(&mut self, index: usize, data: u32) {
127        self.ptr[index] = Some(data);
128        self.update_generation_id();
129    }
130}
131
132/// Convert the lo/hi pair of genid prt registers to a single u64, returning
133/// `None` if either ho/lo register has yet to be set.
134fn ptr_to_opt_u64(ptr: &[Option<u32>; 2]) -> Option<u64> {
135    Some(((ptr[1]? as u64) << 32) | ptr[0]? as u64)
136}
137
138mod save_restore {
139    use super::*;
140    use vmcore::save_restore::RestoreError;
141    use vmcore::save_restore::SaveError;
142    use vmcore::save_restore::SaveRestore;
143
144    mod state {
145        use mesh::payload::Protobuf;
146
147        #[derive(Protobuf)]
148        #[mesh(package = "firmware.generation_id")]
149        pub struct SavedState {
150            #[mesh(1)]
151            pub id: [u8; 16],
152            #[mesh(2)]
153            pub ptr: [Option<u32>; 2],
154        }
155    }
156
157    impl SaveRestore for GenerationId {
158        type SavedState = state::SavedState;
159
160        fn save(&mut self) -> Result<Self::SavedState, SaveError> {
161            let Self { rt: _, id, ptr } = *self;
162
163            let saved_state = state::SavedState { id, ptr };
164            Ok(saved_state)
165        }
166
167        fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
168            let state::SavedState { id, ptr } = state;
169
170            self.id = id;
171            self.ptr = ptr;
172            Ok(())
173        }
174    }
175}