1#![forbid(unsafe_code)]
7
8use guestmem::GuestMemory;
9use inspect::InspectMut;
10use mesh::RecvError;
11use std::task::Context;
12use std::task::Poll;
13use vmcore::line_interrupt::LineInterrupt;
14
15#[expect(missing_docs)] pub struct GenerationIdRuntimeDeps {
18 pub gm: GuestMemory,
19 pub generation_id_recv: mesh::Receiver<[u8; 16]>,
20 pub notify_interrupt: LineInterrupt,
21}
22
23#[derive(InspectMut)]
25#[inspect(extra = "GenerationId::inspect_extra")]
26pub struct GenerationId {
27 #[inspect(skip)]
29 rt: GenerationIdRuntimeDeps,
30
31 id: [u8; 16],
33 #[inspect(with = "ptr_to_opt_u64")]
34 ptr: [Option<u32>; 2],
35}
36
37impl GenerationId {
38 fn inspect_extra(&mut self, resp: &mut inspect::Response<'_>) {
39 resp.field_mut_with("force_update", |update| {
40 let v = if let Some(update) = update {
41 let update = update.parse()?;
42 if update {
43 let mut id = [0; 16];
44 getrandom::fill(&mut id).unwrap();
45 self.set_and_update_generation_id(id);
46 tracing::info!("Force updated genid...");
47 }
48 update
49 } else {
50 false
51 };
52 Result::<_, std::str::ParseBoolError>::Ok(v.to_string())
53 });
54 }
55}
56
57impl GenerationId {
58 pub fn new(initial_generation_id: [u8; 16], platform: GenerationIdRuntimeDeps) -> Self {
60 Self {
61 id: initial_generation_id,
62 ptr: [None, None],
63 rt: platform,
64 }
65 }
66
67 pub fn reset(&mut self) {
69 self.ptr = [None; 2];
72 }
73
74 pub fn write_generation_id_low(&mut self, data: u32) {
76 self.set_ptr(0, data)
77 }
78
79 pub fn write_generation_id_high(&mut self, data: u32) {
81 self.set_ptr(1, data)
82 }
83
84 pub fn poll(&mut self, cx: &mut Context<'_>) {
86 while let Poll::Ready(val) = self.rt.generation_id_recv.poll_recv(cx) {
87 match val {
88 Ok(val) => self.set_and_update_generation_id(val),
89 Err(RecvError::Closed) => break,
90 Err(err) => {
91 tracing::error!(
92 error = &err as &dyn std::error::Error,
93 "Error receiving generation ID"
94 );
95 break;
96 }
97 }
98 }
99 }
100
101 fn update_generation_id(&self) {
104 if let Some(ptr) = ptr_to_opt_u64(&self.ptr) {
105 if let Err(e) = self.rt.gm.write_at(ptr, &self.id) {
106 tracelimit::error_ratelimited!(
107 error = &e as &dyn std::error::Error,
108 "failed to write generation ID"
109 )
110 }
111 }
112 }
113
114 fn set_and_update_generation_id(&mut self, val: [u8; 16]) {
116 self.id = val;
117 self.update_generation_id();
118
119 self.rt.notify_interrupt.set_level(true);
125 self.rt.notify_interrupt.set_level(false);
126 }
127
128 fn set_ptr(&mut self, index: usize, data: u32) {
129 self.ptr[index] = Some(data);
130 self.update_generation_id();
131 }
132}
133
134fn ptr_to_opt_u64(ptr: &[Option<u32>; 2]) -> Option<u64> {
137 Some(((ptr[1]? as u64) << 32) | ptr[0]? as u64)
138}
139
140mod save_restore {
141 use super::*;
142 use vmcore::save_restore::RestoreError;
143 use vmcore::save_restore::SaveError;
144 use vmcore::save_restore::SaveRestore;
145
146 mod state {
147 use mesh::payload::Protobuf;
148
149 #[derive(Protobuf)]
150 #[mesh(package = "firmware.generation_id")]
151 pub struct SavedState {
152 #[mesh(1)]
153 pub id: [u8; 16],
154 #[mesh(2)]
155 pub ptr: [Option<u32>; 2],
156 }
157 }
158
159 impl SaveRestore for GenerationId {
160 type SavedState = state::SavedState;
161
162 fn save(&mut self) -> Result<Self::SavedState, SaveError> {
163 let Self { rt: _, id, ptr } = *self;
164
165 let saved_state = state::SavedState { id, ptr };
166 Ok(saved_state)
167 }
168
169 fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
170 let state::SavedState { id, ptr } = state;
171
172 self.id = id;
173 self.ptr = ptr;
174 Ok(())
175 }
176 }
177}