1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

//! Implementation of Generation ID services (shared across both PCAT and UEFI)

#![warn(missing_docs)]

use guestmem::GuestMemory;
use inspect::InspectMut;
use mesh::RecvError;
use std::task::Context;
use std::task::Poll;
use vmcore::line_interrupt::LineInterrupt;

/// Various runtime objects used by the GenerationId device.
#[allow(missing_docs)] // self-explanatory fields
pub struct GenerationIdRuntimeDeps {
    pub gm: GuestMemory,
    pub generation_id_recv: mesh::Receiver<[u8; 16]>,
    pub notify_interrupt: LineInterrupt,
}

/// Device providing initial and dynamic Generation ID update capabilities.
#[derive(InspectMut)]
#[inspect(extra = "GenerationId::inspect_extra")]
pub struct GenerationId {
    // Runtime glue
    #[inspect(skip)]
    rt: GenerationIdRuntimeDeps,

    // Volatile state
    id: [u8; 16],
    #[inspect(with = "ptr_to_opt_u64")]
    ptr: [Option<u32>; 2],
}

impl GenerationId {
    fn inspect_extra(&mut self, resp: &mut inspect::Response<'_>) {
        resp.field_mut_with("force_update", |update| {
            let v = if let Some(update) = update {
                let update = update.parse()?;
                if update {
                    let mut id = [0; 16];
                    getrandom::getrandom(&mut id).unwrap();
                    self.set_and_update_generation_id(id);
                    tracing::info!("Force updated genid...");
                }
                update
            } else {
                false
            };
            Result::<_, std::str::ParseBoolError>::Ok(v.to_string())
        });
    }
}

impl GenerationId {
    /// Construct a new GenerationId device.
    pub fn new(initial_generation_id: [u8; 16], platform: GenerationIdRuntimeDeps) -> Self {
        Self {
            id: initial_generation_id,
            ptr: [None, None],
            rt: platform,
        }
    }

    /// Reset the GenerationId state back to what it was when first constructed.
    pub fn reset(&mut self) {
        // Just reset the pointer, not the ID. Since this is not a "time travel"
        // event, there is no need to change the ID.
        self.ptr = [None; 2];
    }

    /// Update the low bits of the generation id pointer.
    pub fn write_generation_id_low(&mut self, data: u32) {
        self.set_ptr(0, data)
    }

    /// Update the high bits of the generation id pointer.
    pub fn write_generation_id_high(&mut self, data: u32) {
        self.set_ptr(1, data)
    }

    /// Polls for new generation IDs.
    pub fn poll(&mut self, cx: &mut Context<'_>) {
        while let Poll::Ready(val) = self.rt.generation_id_recv.poll_recv(cx) {
            match val {
                Ok(val) => self.set_and_update_generation_id(val),
                Err(RecvError::Closed) => break,
                Err(err) => {
                    tracing::error!(
                        error = &err as &dyn std::error::Error,
                        "Error receiving generation ID"
                    );
                    break;
                }
            }
        }
    }

    /// Attempts to write the current generation ID into guest memory at
    /// the specified location by the BIOS (PCAT/UEFI).
    fn update_generation_id(&self) {
        if let Some(ptr) = ptr_to_opt_u64(&self.ptr) {
            if let Err(e) = self.rt.gm.write_at(ptr, &self.id) {
                tracelimit::error_ratelimited!(
                    error = &e as &dyn std::error::Error,
                    "failed to write generation ID"
                )
            }
        }
    }

    /// Sets and updates generation id
    fn set_and_update_generation_id(&mut self, val: [u8; 16]) {
        self.id = val;
        self.update_generation_id();

        // Pulse the interrupt line.
        //
        // Ideally there would be some notification from the firmware to clear
        // the interrupt line, but the current firmware DSDT implementations do
        // not do this.
        self.rt.notify_interrupt.set_level(true);
        self.rt.notify_interrupt.set_level(false);
    }

    fn set_ptr(&mut self, index: usize, data: u32) {
        self.ptr[index] = Some(data);
        self.update_generation_id();
    }
}

/// Convert the lo/hi pair of genid prt registers to a single u64, returning
/// `None` if either ho/lo register has yet to be set.
fn ptr_to_opt_u64(ptr: &[Option<u32>; 2]) -> Option<u64> {
    Some(((ptr[1]? as u64) << 32) | ptr[0]? as u64)
}

mod save_restore {
    use super::*;
    use vmcore::save_restore::RestoreError;
    use vmcore::save_restore::SaveError;
    use vmcore::save_restore::SaveRestore;

    mod state {
        use mesh::payload::Protobuf;

        #[derive(Protobuf)]
        #[mesh(package = "firmware.generation_id")]
        pub struct SavedState {
            #[mesh(1)]
            pub id: [u8; 16],
            #[mesh(2)]
            pub ptr: [Option<u32>; 2],
        }
    }

    impl SaveRestore for GenerationId {
        type SavedState = state::SavedState;

        fn save(&mut self) -> Result<Self::SavedState, SaveError> {
            let Self { rt: _, id, ptr } = *self;

            let saved_state = state::SavedState { id, ptr };
            Ok(saved_state)
        }

        fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
            let state::SavedState { id, ptr } = state;

            self.id = id;
            self.ptr = ptr;
            Ok(())
        }
    }
}