|  | // Copyright lowRISC contributors. | 
|  | // Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
|  | // SPDX-License-Identifier: Apache-2.0 | 
|  |  | 
|  | use anyhow::{bail, ensure, Result}; | 
|  | use sha2::{Digest, Sha256}; | 
|  | use std::time::{Duration, Instant}; | 
|  | use thiserror::Error; | 
|  | use zerocopy::AsBytes; | 
|  |  | 
|  | use crate::app::TransportWrapper; | 
|  | use crate::bootstrap::{Bootstrap, BootstrapOptions, UpdateProtocol}; | 
|  | use crate::impl_serializable_error; | 
|  | use crate::io::uart::Uart; | 
|  | use crate::transport::Capability; | 
|  |  | 
|  | #[derive(AsBytes, Debug, Default)] | 
|  | #[repr(C)] | 
|  | struct FrameHeader { | 
|  | hash: [u8; Frame::HASH_LEN], | 
|  | frame_num: u32, | 
|  | flash_offset: u32, | 
|  | } | 
|  |  | 
|  | #[derive(AsBytes, Debug)] | 
|  | #[repr(C)] | 
|  | struct Frame { | 
|  | header: FrameHeader, | 
|  | data: [u8; Frame::DATA_LEN], | 
|  | } | 
|  |  | 
|  | impl Default for Frame { | 
|  | fn default() -> Self { | 
|  | Frame { | 
|  | header: Default::default(), | 
|  | data: [0xff; Frame::DATA_LEN], | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | impl Frame { | 
|  | const EOF: u32 = 0x8000_0000; | 
|  | const FLASH_SECTOR_SIZE: usize = 2048; | 
|  | const FLASH_SECTOR_MASK: usize = Self::FLASH_SECTOR_SIZE - 1; | 
|  | const FLASH_BUFFER_SIZE: usize = 128; | 
|  | const FLASH_BUFFER_MASK: usize = Self::FLASH_BUFFER_SIZE - 1; | 
|  | const DATA_LEN: usize = 1024 - std::mem::size_of::<FrameHeader>(); | 
|  | const HASH_LEN: usize = 32; | 
|  | const HEADER_ALIGNMENT: usize = 0x1000; | 
|  | const MAGIC_HEADER: [u8; 4] = [0xfd, 0xff, 0xff, 0xff]; | 
|  | const CRYPTOLIB_TELL: [u8; 4] = [0x53, 0x53, 0x53, 0x53]; | 
|  |  | 
|  | /// Computes the hash in the header. | 
|  | fn header_hash(&self) -> [u8; Frame::HASH_LEN] { | 
|  | let frame = self.as_bytes(); | 
|  | let sha = Sha256::digest(&frame[Frame::HASH_LEN..]); | 
|  | sha.into() | 
|  | } | 
|  |  | 
|  | /// Computes the hash over the entire frame. | 
|  | fn frame_hash(&self) -> [u8; Frame::HASH_LEN] { | 
|  | let mut digest = Sha256::digest(self.as_bytes()); | 
|  | // Touch up zeroes into ones, as that is what the old chips are doing. | 
|  | for b in &mut digest { | 
|  | if *b == 0 { | 
|  | *b = 1; | 
|  | } | 
|  | } | 
|  | digest.into() | 
|  | } | 
|  |  | 
|  | /// Creates a sequence of frames based on a `payload` binary. | 
|  | fn from_payload(payload: &[u8]) -> Result<Vec<Frame>> { | 
|  | // The given payload will contain up to four sections concatenated together: | 
|  | // RO_A, RW_A optionally follwed by RO_B, RW_B | 
|  | // Each section starts with a magic number on at least a 256 byte boundary. | 
|  |  | 
|  | // This rescue protocol uses the RW_A section only, which will start at the second | 
|  | // occurrance of the magic value, and end at the third occurrence or at the end of the | 
|  | // file. | 
|  |  | 
|  | ensure!( | 
|  | payload.starts_with(&Self::MAGIC_HEADER), | 
|  | RescueError::ImageFormatError | 
|  | ); | 
|  |  | 
|  | // Find second occurrence of magic value, not followed by signature of encrypted | 
|  | // cryptolib. | 
|  | let min_addr = match payload[Self::HEADER_ALIGNMENT..] | 
|  | .chunks(Self::HEADER_ALIGNMENT) | 
|  | .position(|c| &c[0..4] == &Self::MAGIC_HEADER && &c[4..8] != &Self::CRYPTOLIB_TELL) | 
|  | { | 
|  | Some(n) => (n + 1) * Self::HEADER_ALIGNMENT, | 
|  | None => bail!(RescueError::ImageFormatError), | 
|  | }; | 
|  |  | 
|  | // Find third occurrence of magic value. | 
|  | let max_addr = match payload[min_addr + Self::HEADER_ALIGNMENT..] | 
|  | .chunks(Self::HEADER_ALIGNMENT) | 
|  | .position(|c| &c[0..4] == &Self::MAGIC_HEADER) | 
|  | { | 
|  | Some(n) => (n + 1) * Self::HEADER_ALIGNMENT + min_addr, | 
|  | None => payload.len(), | 
|  | }; | 
|  |  | 
|  | // Trim trailing 0xff bytes. | 
|  | let max_addr = (payload[..max_addr] | 
|  | .chunks(4) | 
|  | .rposition(|c| c != &[0xff; 4]) | 
|  | .unwrap_or(0) | 
|  | + 1) | 
|  | * 4; | 
|  |  | 
|  | let mut frames = Vec::new(); | 
|  | let mut frame_num = 0; | 
|  | let mut addr = min_addr; | 
|  | while addr < max_addr { | 
|  | // Try skipping over 0xffffffff words. | 
|  | let nonempty_addr = addr | 
|  | + payload[addr..] | 
|  | .chunks(4) | 
|  | .position(|c| c != &[0xff; 4]) | 
|  | .unwrap() | 
|  | * 4; | 
|  | let skip_addr = nonempty_addr & !Self::FLASH_SECTOR_MASK; | 
|  | if skip_addr > addr && (addr == 0 || addr & Self::FLASH_BUFFER_MASK != 0) { | 
|  | // Can only skip from the start or if the last addr wasn't an exact multiple of | 
|  | // 128 (per H1D boot rom). | 
|  | addr = skip_addr; | 
|  | } | 
|  |  | 
|  | let mut frame = Frame { | 
|  | header: FrameHeader { | 
|  | frame_num, | 
|  | flash_offset: addr as u32, | 
|  | ..Default::default() | 
|  | }, | 
|  | ..Default::default() | 
|  | }; | 
|  | let slice_size = Self::DATA_LEN.min(payload.len() - addr); | 
|  | frame.data[..slice_size].copy_from_slice(&payload[addr..addr + slice_size]); | 
|  | frames.push(frame); | 
|  |  | 
|  | addr += Self::DATA_LEN; | 
|  | frame_num += 1; | 
|  | } | 
|  | frames.last_mut().map(|f| f.header.frame_num |= Self::EOF); | 
|  | frames | 
|  | .iter_mut() | 
|  | .for_each(|f| f.header.hash = f.header_hash()); | 
|  | Ok(frames) | 
|  | } | 
|  | } | 
|  |  | 
|  | #[derive(Debug, Error, serde::Serialize, serde::Deserialize)] | 
|  | pub enum RescueError { | 
|  | #[error("Unrecognized image file format")] | 
|  | ImageFormatError, | 
|  | #[error("Synchronization error communicating with boot rom")] | 
|  | SyncError, | 
|  | #[error("Repeated errors communicating with boot rom")] | 
|  | RepeatedErrors, | 
|  | } | 
|  | impl_serializable_error!(RescueError); | 
|  |  | 
|  | /// Implements the UART rescue protocol of Google Ti50 firmware. | 
|  | pub struct Rescue {} | 
|  |  | 
|  | impl Rescue { | 
|  | /// Abort if a block has not been accepted after this number of retries. | 
|  | const MAX_CONSECUTIVE_ERRORS: u32 = 50; | 
|  | /// Take some measure to regain protocol synchronization, in case of this number of retries | 
|  | /// of the same block. | 
|  | const RESYNC_AFTER_CONSECUTIVE_ERRORS: u32 = 3; | 
|  |  | 
|  | /// Creates a new `Rescue` protocol updater from `options`. | 
|  | pub fn new(_options: &BootstrapOptions) -> Self { | 
|  | Self {} | 
|  | } | 
|  |  | 
|  | /// Waits for some time for a character, returns None on timeout. | 
|  | fn read_char(&self, uart: &dyn Uart) -> Option<char> { | 
|  | let mut buf = [0u8; 1]; | 
|  | match uart.read_timeout(&mut buf, Duration::from_millis(100)) { | 
|  | Ok(1) => Some(buf[0] as char), | 
|  | Ok(_) => None, | 
|  | _ => None, | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Waits some time for data, returning true if the given string was seen in full, or false | 
|  | /// as soon as a non-matching character is received or on timeout. | 
|  | fn expect_string(&self, uart: &dyn Uart, s: &str) -> bool { | 
|  | for expected_ch in s.chars() { | 
|  | match self.read_char(uart) { | 
|  | Some(ch) if ch == expected_ch => (), | 
|  | _ => return false, | 
|  | } | 
|  | } | 
|  | true | 
|  | } | 
|  |  | 
|  | /// Reads and discards any characters in the receive buffer, waiting a little while for any | 
|  | /// more which will also be discarded. | 
|  | fn flush_rx(&self, uart: &dyn Uart) { | 
|  | let mut response = [0u8; Frame::HASH_LEN]; | 
|  | loop { | 
|  | match uart.read_timeout(&mut response, Duration::from_millis(500)) { | 
|  | Ok(0) | Err(_) => break, | 
|  | Ok(_) => continue, | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /// As the 1024 byte blocks sent to the chip have no discernible header, the sender and | 
|  | /// receiver could be "out of sync".  This is resolved by sending one byte at a time, and | 
|  | /// observing when the chip sends a response (which will be a rejection due to checksum). | 
|  | fn synchronize(&self, uart: &dyn Uart) -> Result<()> { | 
|  | // Most likely, only a few "extra" bytes have been sent during initial negotiation. | 
|  | // Send almost a complete block in one go, and then send each of the last 16 bytes one | 
|  | // at a time, slowly enough to detect a response before sending the next byte. | 
|  | uart.write(&[0u8; 1008])?; | 
|  | let mut response = [0u8; 1]; | 
|  | let limit = match uart.read_timeout(&mut response, Duration::from_millis(50)) { | 
|  | Ok(0) | Err(_) => 16, | 
|  | Ok(_) => { | 
|  | // A response at this point must mean that more than 16 bytes had already been | 
|  | // sent before entering this method.  This will be resolved by doing another | 
|  | // slower round of 1024 bytes with delay in between every one. | 
|  | self.flush_rx(uart); | 
|  | 1024 | 
|  | } | 
|  | }; | 
|  | for _ in 0..limit { | 
|  | uart.write(&[0u8; 1])?; | 
|  | match uart.read_timeout(&mut response, Duration::from_millis(50)) { | 
|  | Ok(0) | Err(_) => (), | 
|  | Ok(_) => { | 
|  | self.flush_rx(uart); | 
|  | return Ok(()); | 
|  | } | 
|  | } | 
|  | } | 
|  | Err(RescueError::SyncError.into()) | 
|  | } | 
|  |  | 
|  | /// Reset the chip and send the magic 'r' character at the opportune moment during boot in | 
|  | /// order to enter rescue more, repeat if necessary. | 
|  | fn enter_rescue_mode(&self, container: &Bootstrap, uart: &dyn Uart) -> Result<()> { | 
|  | // Attempt getting the attention of the bootloader. | 
|  | let timeout = Duration::from_millis(2000); | 
|  | for _ in 0..Self::MAX_CONSECUTIVE_ERRORS { | 
|  | eprint!("Resetting..."); | 
|  | container.reset_pin.write(false)?; // Low active | 
|  | std::thread::sleep(container.reset_delay); | 
|  | container.reset_pin.write(true)?; // Release reset | 
|  | let stopwatch = Instant::now(); | 
|  | while stopwatch.elapsed() < timeout { | 
|  | if !self.expect_string(uart, "Bldr |") { | 
|  | continue; | 
|  | } | 
|  | uart.write(&['r' as u8])?; | 
|  | eprint!("a."); | 
|  | while stopwatch.elapsed() < timeout { | 
|  | if !self.expect_string(uart, "oops?|") { | 
|  | continue; | 
|  | } | 
|  | uart.write(&['r' as u8])?; | 
|  | eprint!("b."); | 
|  | if self.expect_string(uart, "escue") { | 
|  | eprintln!("c: Entered rescue mode!"); | 
|  | self.synchronize(uart)?; | 
|  | return Ok(()); | 
|  | } | 
|  | } | 
|  | } | 
|  | eprintln!(" Failed to enter rescue mode."); | 
|  | } | 
|  | Err(RescueError::RepeatedErrors.into()) | 
|  | } | 
|  | } | 
|  |  | 
|  | impl UpdateProtocol for Rescue { | 
|  | fn verify_capabilities( | 
|  | &self, | 
|  | _container: &Bootstrap, | 
|  | transport: &TransportWrapper, | 
|  | ) -> Result<()> { | 
|  | transport | 
|  | .capabilities()? | 
|  | .request(Capability::GPIO | Capability::UART) | 
|  | .ok()?; | 
|  | Ok(()) | 
|  | } | 
|  |  | 
|  | /// Returns false, in order to as the containing Bootstrap struct to not perform standard | 
|  | /// BOOTSTRAP/RESET sequence. | 
|  | fn uses_common_bootstrap_reset(&self) -> bool { | 
|  | false | 
|  | } | 
|  |  | 
|  | /// Performs the update protocol using the `transport` with the firmware `payload`. | 
|  | fn update( | 
|  | &self, | 
|  | container: &Bootstrap, | 
|  | transport: &TransportWrapper, | 
|  | payload: &[u8], | 
|  | progress: &dyn Fn(u32, u32), | 
|  | ) -> Result<()> { | 
|  | let frames = Frame::from_payload(payload)?; | 
|  | let uart = container.uart_params.create(transport)?; | 
|  |  | 
|  | self.enter_rescue_mode(container, &*uart)?; | 
|  |  | 
|  | // Send frames one at a time. | 
|  | 'next_block: for (idx, frame) in frames.iter().enumerate() { | 
|  | for consecutive_errors in 0..Self::MAX_CONSECUTIVE_ERRORS { | 
|  | eprint!("{}.", idx); | 
|  | progress(frame.header.flash_offset, frame.data.len() as u32); | 
|  | uart.write(frame.as_bytes())?; | 
|  | let mut response = [0u8; Frame::HASH_LEN]; | 
|  | let mut index = 0; | 
|  | while index < Frame::HASH_LEN { | 
|  | let timeout = if index == 0 { | 
|  | Duration::from_millis(1000) | 
|  | } else { | 
|  | Duration::from_millis(10) | 
|  | }; | 
|  | match uart.read_timeout(&mut response[index..], timeout) { | 
|  | Ok(0) | Err(_) => break, | 
|  | Ok(n) => index += n, | 
|  | } | 
|  | } | 
|  | if index < Frame::HASH_LEN { | 
|  | eprint!("sync."); | 
|  | self.synchronize(&*uart)?; | 
|  | continue; | 
|  | } | 
|  | if response[4..].chunks(4).all(|x| x == &response[..4]) { | 
|  | eprint!("sync."); | 
|  | self.synchronize(&*uart)?; | 
|  | } else if response == frame.frame_hash() { | 
|  | continue 'next_block; | 
|  | } else { | 
|  | self.flush_rx(&*uart); | 
|  | if consecutive_errors >= Self::RESYNC_AFTER_CONSECUTIVE_ERRORS { | 
|  | eprint!("sync."); | 
|  | self.synchronize(&*uart)?; | 
|  | } | 
|  | } | 
|  | } | 
|  | bail!(RescueError::RepeatedErrors); | 
|  | } | 
|  |  | 
|  | // Reset, in order to leave rescue mode. | 
|  | container.reset_pin.write(false)?; // Low active | 
|  | std::thread::sleep(container.reset_delay); | 
|  | container.reset_pin.write(true)?; // Release reset | 
|  | eprintln!("Success!"); | 
|  | Ok(()) | 
|  | } | 
|  | } |