opentitanlib/bootstrap/
legacy.rs1use anyhow::Result;
6use sha2::{Digest, Sha256};
7use std::time::Duration;
8use thiserror::Error;
9use zerocopy::{Immutable, IntoBytes};
10
11use crate::app::TransportWrapper;
12use crate::bootstrap::{Bootstrap, BootstrapOptions, UpdateProtocol};
13use crate::impl_serializable_error;
14use crate::io::spi::Transfer;
15use crate::transport::{Capability, ProgressIndicator};
16
17#[derive(Immutable, IntoBytes, Debug, Default)]
18#[repr(C)]
19struct FrameHeader {
20 hash: [u8; Frame::HASH_LEN],
21 frame_num: u32,
22 flash_offset: u32,
23}
24
25#[derive(Immutable, IntoBytes, Debug)]
26#[repr(C)]
27struct Frame {
28 header: FrameHeader,
29 data: [u8; Frame::DATA_LEN],
30}
31
32impl Default for Frame {
33 fn default() -> Self {
34 Frame {
35 header: Default::default(),
36 data: [0xff; Frame::DATA_LEN],
37 }
38 }
39}
40
41impl Frame {
42 const EOF: u32 = 0x8000_0000;
43 const FLASH_SECTOR_SIZE: usize = 2048;
44 const FLASH_SECTOR_MASK: usize = Self::FLASH_SECTOR_SIZE - 1;
45 const FLASH_BUFFER_SIZE: usize = 128;
46 const FLASH_BUFFER_MASK: usize = Self::FLASH_BUFFER_SIZE - 1;
47 const DATA_LEN: usize = 2048 - std::mem::size_of::<FrameHeader>();
48 const HASH_LEN: usize = 32;
49 const FLASH_BASE_ADDRESS: usize = 65536 * 8;
50
51 fn header_hash(&self) -> [u8; Frame::HASH_LEN] {
53 let frame = self.as_bytes();
54 let sha = Sha256::digest(&frame[Frame::HASH_LEN..]);
55 sha.into()
56 }
57
58 fn frame_hash(&self) -> [u8; Frame::HASH_LEN] {
60 let mut digest = Sha256::digest(self.as_bytes());
61 for b in &mut digest {
63 if *b == 0 {
64 *b = 1;
65 }
66 }
67 digest.into()
68 }
69
70 fn from_payload(payload: &[u8]) -> Vec<Frame> {
72 let mut frames = Vec::new();
73
74 let max_addr = (payload.chunks(4).rposition(|c| c != [0xff; 4]).unwrap_or(0) + 1) * 4;
75
76 let mut frame_num = 0;
77 let mut addr = 0;
78 while addr < max_addr {
79 let nonempty_addr = addr
81 + payload[addr..]
82 .chunks(4)
83 .position(|c| c != [0xff; 4])
84 .unwrap()
85 * 4;
86 let skip_addr = nonempty_addr & !Self::FLASH_SECTOR_MASK;
87 if skip_addr > addr && (addr == 0 || addr & Self::FLASH_BUFFER_MASK != 0) {
88 addr = skip_addr;
91 }
92
93 let mut frame = Frame {
94 header: FrameHeader {
95 frame_num,
96 flash_offset: (addr + Self::FLASH_BASE_ADDRESS) as u32,
97 ..Default::default()
98 },
99 ..Default::default()
100 };
101 let slice_size = Self::DATA_LEN.min(payload.len() - addr);
102 frame.data[..slice_size].copy_from_slice(&payload[addr..addr + slice_size]);
103 frames.push(frame);
104
105 addr += Self::DATA_LEN;
106 frame_num += 1;
107 }
108 if let Some(f) = frames.last_mut() {
109 f.header.frame_num |= Self::EOF;
110 }
111 frames
112 .iter_mut()
113 .for_each(|f| f.header.hash = f.header_hash());
114 frames
115 }
116}
117
118pub struct Legacy {
120 pub inter_frame_delay: Duration,
126}
127
128impl Legacy {
129 const INTER_FRAME_DELAY: Duration = Duration::from_millis(1);
130 const MAX_CONSECUTIVE_ERRORS: u32 = 100;
131
132 pub fn new(options: &BootstrapOptions) -> Self {
134 Self {
135 inter_frame_delay: options.inter_frame_delay.unwrap_or(Self::INTER_FRAME_DELAY),
136 }
137 }
138}
139
140#[derive(Debug, Error, serde::Serialize, serde::Deserialize)]
141pub enum LegacyBootstrapError {
142 #[error("Boot rom not ready")]
143 NotReady,
144 #[error("Unknown boot rom error: {0}")]
145 Unknown(u8),
146 #[error("Boot rom error: NOREQUEST")]
147 NoRequest,
148 #[error("Boot rom error: NOMAGIC")]
149 NoMagic,
150 #[error("Boot rom error: TOOBIG")]
151 TooBig,
152 #[error("Boot rom error: TOOHIGH")]
153 TooHigh,
154 #[error("Boot rom error: NOALIGN")]
155 NoAlign,
156 #[error("Boot rom error: NOROUND")]
157 NoRound,
158 #[error("Boot rom error: BADKEY")]
159 BadKey,
160 #[error("Boot rom error: BADSTART")]
161 BadStart,
162 #[error("Boot rom error: NOWIPE")]
163 NoWipe,
164 #[error("Boot rom error: NOWIPE0")]
165 NoWipe0,
166 #[error("Boot rom error: NOWIPE1")]
167 NoWipe1,
168 #[error("Boot rom error: NOTEMPTY")]
169 NotEmpty,
170 #[error("Boot rom error: NOWRITE")]
171 NoWrite,
172 #[error("Boot rom error: BADADR")]
173 BadAdr,
174 #[error("Boot rom error: OVERFLOW")]
175 Overflow,
176 #[error("Repeated errors communicating with boot rom")]
177 RepeatedErrors,
178}
179impl_serializable_error!(LegacyBootstrapError);
180
181impl From<u8> for LegacyBootstrapError {
182 fn from(value: u8) -> LegacyBootstrapError {
183 match value {
184 0 | 255 => LegacyBootstrapError::NotReady,
186 1 => LegacyBootstrapError::NoRequest,
188 2 => LegacyBootstrapError::NoMagic,
189 3 => LegacyBootstrapError::TooBig,
190 4 => LegacyBootstrapError::TooHigh,
191 5 => LegacyBootstrapError::NoAlign,
192 6 => LegacyBootstrapError::NoRound,
193 7 => LegacyBootstrapError::BadKey,
194 8 => LegacyBootstrapError::BadStart,
195 10 => LegacyBootstrapError::NoWipe,
196 11 => LegacyBootstrapError::NoWipe0,
197 12 => LegacyBootstrapError::NoWipe1,
198 13 => LegacyBootstrapError::NotEmpty,
199 14 => LegacyBootstrapError::NoWrite,
200 15 => LegacyBootstrapError::BadAdr,
201 16 => LegacyBootstrapError::Overflow,
202 n => LegacyBootstrapError::Unknown(n),
203 }
204 }
205}
206
207impl UpdateProtocol for Legacy {
208 fn verify_capabilities(
209 &self,
210 _container: &Bootstrap,
211 transport: &TransportWrapper,
212 ) -> Result<()> {
213 transport
214 .capabilities()?
215 .request(Capability::GPIO | Capability::SPI)
216 .ok()?;
217 Ok(())
218 }
219
220 fn uses_common_bootstrap_reset(&self) -> bool {
221 true
222 }
223
224 fn update(
226 &self,
227 container: &Bootstrap,
228 transport: &TransportWrapper,
229 payload: &[u8],
230 progress: &dyn ProgressIndicator,
231 ) -> Result<()> {
232 let spi = container.spi_params.create(transport, "BOOTSTRAP")?;
233
234 let frames = Frame::from_payload(payload);
235
236 let mut first_unacked_index = 0;
239
240 let mut consecutive_errors = 0;
242
243 let mut optimistic = false;
247
248 let mut becoming_optimistic = false;
253
254 progress.new_stage("", payload.len());
255 loop {
256 if consecutive_errors > Self::MAX_CONSECUTIVE_ERRORS {
257 return Err(LegacyBootstrapError::RepeatedErrors.into());
258 }
259
260 let second_unacked_index = (first_unacked_index + 1).min(frames.len() - 1);
261 let transmit_index = if optimistic {
262 second_unacked_index
263 } else {
264 first_unacked_index
265 };
266 let frame = &frames[transmit_index];
267 eprint!("{}.", transmit_index);
268 std::thread::sleep(self.inter_frame_delay);
269
270 progress.progress(frame.header.flash_offset as usize - Frame::FLASH_BASE_ADDRESS);
272 let mut response = [0u8; std::mem::size_of::<Frame>()];
273 spi.run_transaction(&mut [Transfer::Both(frame.as_bytes(), &mut response)])?;
274
275 if becoming_optimistic {
276 optimistic = true;
277 becoming_optimistic = false;
278 continue;
279 }
280
281 if response[..Frame::HASH_LEN]
282 .iter()
283 .all(|&x| x == response[0])
284 {
285 match LegacyBootstrapError::from(response[0]) {
287 LegacyBootstrapError::NotReady => {
288 consecutive_errors += 1;
289 continue; }
291 error => return Err(error.into()),
292 }
293 }
294
295 if response[..Frame::HASH_LEN] == frames[first_unacked_index].frame_hash() {
296 first_unacked_index += 1;
297 } else if response[..Frame::HASH_LEN] == frames[second_unacked_index].frame_hash() {
298 first_unacked_index = second_unacked_index + 1;
299 } else {
300 consecutive_errors += 1;
301 optimistic = false;
302 continue;
303 }
304
305 consecutive_errors = 0;
306 if first_unacked_index == frames.len() {
307 break;
309 }
310 if !optimistic {
311 becoming_optimistic = first_unacked_index > 1;
313 }
314 }
315 progress.progress(payload.len());
316 eprintln!("success");
317 Ok(())
318 }
319}
320
321#[cfg(test)]
322mod tests {
323 use super::*;
324
325 const SIMPLE_BIN: &[u8; 2048] = include_bytes!("simple.bin");
326
327 #[test]
328 fn test_small_binary() -> Result<()> {
329 let frames = Frame::from_payload(SIMPLE_BIN);
330
331 assert_eq!(frames[0].header.frame_num, 0);
332 assert_eq!(frames[0].header.flash_offset, 0x80000);
333 assert_eq!(
334 hex::encode(frames[0].header.hash),
335 "4e31bfd8b3be32358f2235c0f241f3970de575fc6aca0564aa6bf30adaf33910"
336 );
337
338 assert_eq!(frames[1].header.frame_num, 0x8000_0001);
339 assert_eq!(frames[1].header.flash_offset, 0x807d8);
340 assert_eq!(
341 hex::encode(frames[1].header.hash),
342 "ff584c07bbb0a039934a660bd49b7812af8ee847d1e675d9aba71c11fab3cfcb"
343 );
344 Ok(())
345 }
346}