1use guestmem::GuestMemory;
7use inspect::InspectMut;
8use mesh::RecvError;
9use std::task::Context;
10use std::task::Poll;
11use vmcore::line_interrupt::LineInterrupt;
12
13#[expect(missing_docs)] pub struct GenerationIdRuntimeDeps {
16 pub gm: GuestMemory,
17 pub generation_id_recv: mesh::Receiver<[u8; 16]>,
18 pub notify_interrupt: LineInterrupt,
19}
20
21#[derive(InspectMut)]
23#[inspect(extra = "GenerationId::inspect_extra")]
24pub struct GenerationId {
25 #[inspect(skip)]
27 rt: GenerationIdRuntimeDeps,
28
29 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 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 pub fn reset(&mut self) {
67 self.ptr = [None; 2];
70 }
71
72 pub fn write_generation_id_low(&mut self, data: u32) {
74 self.set_ptr(0, data)
75 }
76
77 pub fn write_generation_id_high(&mut self, data: u32) {
79 self.set_ptr(1, data)
80 }
81
82 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 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 fn set_and_update_generation_id(&mut self, val: [u8; 16]) {
114 self.id = val;
115 self.update_generation_id();
116
117 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
132fn 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}