blob: 2b0874c990fe86f622b10ebe5e02cfcfe75a5746 [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::{ensure, Result};
use erased_serde::Serialize;
use humantime::parse_duration;
use std::any::Any;
use std::path::PathBuf;
use std::time::Duration;
use structopt::StructOpt;
use opentitanlib::app::command::CommandDispatch;
use opentitanlib::app::TransportWrapper;
use opentitanlib::bootstrap::{Bootstrap, BootstrapOptions, BootstrapProtocol};
use opentitanlib::io::spi::SpiParams;
use opentitanlib::transport;
use opentitanlib::transport::Capability;
use opentitanlib::util::image::ImageAssembler;
use opentitanlib::util::parse_int::ParseInt;
/// Bootstrap the target device.
#[derive(Debug, StructOpt)]
pub struct BootstrapCommand {
#[structopt(flatten)]
params: SpiParams,
#[structopt(
short,
long,
possible_values = &BootstrapProtocol::variants(),
case_insensitive = true,
default_value = "primitive",
help = "Bootstrap protocol to use"
)]
protocol: BootstrapProtocol,
#[structopt(long, parse(try_from_str=parse_duration), help = "Duration of the reset delay")]
reset_delay: Option<Duration>,
#[structopt(long, parse(try_from_str=parse_duration), help = "Duration of the inter-frame delay")]
inter_frame_delay: Option<Duration>,
#[structopt(long, parse(try_from_str=parse_duration), help = "Duration of the flash-erase delay")]
flash_erase_delay: Option<Duration>,
#[structopt(
long,
parse(try_from_str=usize::from_str),
default_value="1048576",
help="The size of the image to assemble (only valid with mutliple FILE arguments)"
)]
size: usize,
#[structopt(
long,
parse(try_from_str),
default_value = "true",
help = "Whether or not the assembled image is mirrored (only valid with mutliple FILE arguments)"
)]
mirror: bool,
#[structopt(
name = "FILE",
min_values(1),
help = "An image to bootstrap or multiple filename@offset specifiers to assemble into a bootstrap image."
)]
filename: Vec<String>,
}
impl BootstrapCommand {
fn bootstrap_using_direct_emulator_integration(
&self,
transport: &TransportWrapper,
) -> Result<Option<Box<dyn Serialize>>> {
ensure!(
!(self.filename.len() > 1 || self.filename[0].contains('@')),
"The `emulator` protocol does not support image assembly"
);
transport.dispatch(&transport::Bootstrap {
image_path: PathBuf::from(&self.filename[0]),
})
}
fn payload(&self) -> Result<Vec<u8>> {
if self.filename.len() > 1 || self.filename[0].contains('@') {
let mut image = ImageAssembler::with_params(self.size, self.mirror);
image.parse(&self.filename)?;
image.assemble()
} else {
Ok(std::fs::read(&self.filename[0])?)
}
}
}
impl CommandDispatch for BootstrapCommand {
fn run(
&self,
_context: &dyn Any,
transport: &TransportWrapper,
) -> Result<Option<Box<dyn Serialize>>> {
// The `min_values` structopt attribute should take care of this, but it doesn't.
ensure!(
!self.filename.is_empty(),
"You must supply at least one filename"
);
if self.protocol == BootstrapProtocol::Emulator {
return self.bootstrap_using_direct_emulator_integration(transport);
}
transport
.capabilities()
.request(Capability::GPIO | Capability::SPI)
.ok()?;
let options = BootstrapOptions {
reset_delay: self.reset_delay,
inter_frame_delay: self.inter_frame_delay,
flash_erase_delay: self.flash_erase_delay,
};
let bootstrap = Bootstrap::new(self.protocol, options)?;
let spi = self.params.create(transport)?;
let reset_pin = transport.gpio_pin("RESET")?;
let bootstrap_pin = transport.gpio_pin("BOOTSTRAP")?;
let payload = self.payload()?;
bootstrap.update(&*spi, &*reset_pin, &*bootstrap_pin, &payload)?;
Ok(None)
}
}