blob: 404ccb000339f55bac0157eba381b36672106704 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
use anyhow::Result;
use std::time::Duration;
use structopt::clap::arg_enum;
use thiserror::Error;
use crate::io::gpio::GpioPin;
use crate::io::spi::Target;
mod primitive;
mod legacy;
#[derive(Debug, Error)]
pub enum BootstrapError {
#[error("Invalid hash length: {0}")]
InvalidHashLength(usize),
}
arg_enum! {
/// `BootstrapProtocol` describes the supported types of bootstrap.
/// The `Primitive` protocol is used by OpenTitan during development.
/// The `Legacy` protocol is used by previous generations of Google Titan-class chips.
/// The `Eeprom` protocol is planned to be implemented for OpenTitan.
/// The 'Emulator' value indicates that this tool has a direct way
/// of communicating with the OpenTitan emulator, to replace the
/// contents of the emulated flash storage.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BootstrapProtocol {
Primitive,
Legacy,
Eeprom,
Emulator,
}
}
// Implementations of bootstrap need to implement the `UpdateProtocol` trait.
trait UpdateProtocol {
fn update(&self, spi: &dyn Target, payload: &[u8]) -> Result<()>;
}
/// Options which control bootstrap behavior.
/// The meaning of each of these values depends on the specific bootstrap protocol being used.
#[derive(Debug, Default)]
pub struct BootstrapOptions {
/// How long to hold the reset pin during the reset sequence.
pub reset_delay: Option<Duration>,
/// How long to delay between sending bootstrap frames.
pub inter_frame_delay: Option<Duration>,
/// How long to delay during a flash erase operation.
pub flash_erase_delay: Option<Duration>,
}
/// Bootstrap wraps and drives the various bootstrap protocols.
pub struct Bootstrap {
pub protocol: BootstrapProtocol,
reset_delay: Duration,
updater: Box<dyn UpdateProtocol>,
}
impl Bootstrap {
const RESET_DELAY: Duration = Duration::from_millis(200);
/// Consrtuct a `Bootstrap` struct configured to use `protocol` and `options`.
pub fn new(protocol: BootstrapProtocol, options: BootstrapOptions) -> Result<Self> {
let updater: Box<dyn UpdateProtocol> = match protocol {
BootstrapProtocol::Primitive => Box::new(primitive::Primitive::new(&options)),
BootstrapProtocol::Legacy => Box::new(legacy::Legacy::new(&options)),
BootstrapProtocol::Eeprom => {
unimplemented!();
}
BootstrapProtocol::Emulator => {
// Not intended to be implemented by this struct.
unimplemented!();
}
};
Ok(Bootstrap {
protocol,
reset_delay: options.reset_delay.unwrap_or(Self::RESET_DELAY),
updater: updater,
})
}
/// Perform the update, sending the firmware `payload` to the `spi` target,
/// using `gpio` to sequence the reset and bootstrap pins.
pub fn update(
&self,
spi: &dyn Target,
reset: &dyn GpioPin,
bootstrap: &dyn GpioPin,
payload: &[u8],
) -> Result<()> {
log::info!("Asserting bootstrap pins...");
bootstrap.write(true)?;
log::info!("Restting the target...");
reset.write(false)?; // Low active
std::thread::sleep(self.reset_delay);
reset.write(true)?; // Release reset
std::thread::sleep(self.reset_delay);
log::info!("Performing bootstrap...");
self.updater.update(spi, payload)?;
log::info!("Releasing bootstrap pins...");
bootstrap.write(false)?;
Ok(())
}
}