opentitanlib/bootstrap/
mod.rs

1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5use anyhow::Result;
6use clap::{Args, ValueEnum};
7use humantime::parse_duration;
8use serde::{Deserialize, Serialize};
9use std::rc::Rc;
10use std::time::Duration;
11use thiserror::Error;
12
13use crate::app::{NoProgressBar, TransportWrapper};
14use crate::impl_serializable_error;
15use crate::io::gpio::GpioPin;
16use crate::io::spi::SpiParams;
17use crate::io::uart::UartParams;
18use crate::transport::{Capability, ProgressIndicator};
19
20mod eeprom;
21mod legacy;
22mod legacy_rescue;
23mod primitive;
24
25pub use legacy::LegacyBootstrapError;
26pub use legacy_rescue::LegacyRescueError;
27
28#[derive(Debug, Error, Serialize, Deserialize)]
29pub enum BootstrapError {
30    #[error("Invalid hash length: {0}")]
31    InvalidHashLength(usize),
32}
33impl_serializable_error!(BootstrapError);
34
35/// `BootstrapProtocol` describes the supported types of bootstrap.
36/// The `Primitive` SPI protocol is used by OpenTitan during development.
37/// The `Legacy` SPI protocol is used by previous generations of Google Titan-class chips.
38/// The `LegacyRescue` UART protocol is used by previous generations of Google Titan-class chips.
39/// The `Eeprom` SPI protocol is planned to be implemented for OpenTitan.
40/// The 'Emulator' value indicates that this tool has a direct way
41/// of communicating with the OpenTitan emulator, to replace the
42/// contents of the emulated flash storage.
43#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, ValueEnum)]
44pub enum BootstrapProtocol {
45    Primitive,
46    Legacy,
47    LegacyRescue,
48    Eeprom,
49    Emulator,
50}
51
52// Implementations of bootstrap need to implement the `UpdateProtocol` trait.
53trait UpdateProtocol {
54    /// Called before any action is taken, to allow the protocol to verify that the transport
55    /// supports SPI/UART or whatever it needs.
56    fn verify_capabilities(
57        &self,
58        container: &Bootstrap,
59        transport: &TransportWrapper,
60    ) -> Result<()>;
61    /// Indicates whether the caller should assert the bootstrap pin and reset the chip, before
62    /// invoking update().
63    fn uses_common_bootstrap_reset(&self) -> bool;
64    /// Invoked to perform the actual transfer of an executable image to the OpenTitan chip.
65    fn update(
66        &self,
67        container: &Bootstrap,
68        transport: &TransportWrapper,
69        payload: &[u8],
70        progress: &dyn ProgressIndicator,
71    ) -> Result<()>;
72}
73
74/// Options which control bootstrap behavior.
75/// The meaning of each of these values depends on the specific bootstrap protocol being used.
76#[derive(Clone, Debug, Args, Serialize, Deserialize)]
77pub struct BootstrapOptions {
78    #[command(flatten)]
79    pub uart_params: UartParams,
80    #[command(flatten)]
81    pub spi_params: SpiParams,
82    /// Bootstrap protocol to use.
83    #[arg(short, long, value_enum, ignore_case = true, default_value = "eeprom")]
84    pub protocol: BootstrapProtocol,
85    /// Whether to reset target and clear UART RX buffer after bootstrap. For Chip Whisperer board only.
86    #[arg(long)]
87    pub clear_uart: Option<bool>,
88    /// Duration of the reset pulse.
89    #[arg(long, value_parser = parse_duration, default_value = "100ms")]
90    pub reset_delay: Duration,
91    /// If set, keep the bootstrap strapping applied and do not perform the post-bootstrap reset
92    /// sequence.
93    #[arg(long)]
94    pub leave_in_bootstrap: bool,
95    /// If set, leave the reset signal asserted after completed bootstrapping.
96    #[arg(long)]
97    pub leave_in_reset: bool,
98    /// Duration of the inter-frame delay.
99    #[arg(long, value_parser = parse_duration)]
100    pub inter_frame_delay: Option<Duration>,
101    /// Duration of the flash-erase delay.
102    #[arg(long, value_parser = parse_duration)]
103    pub flash_erase_delay: Option<Duration>,
104}
105
106/// Bootstrap wraps and drives the various bootstrap protocols.
107pub struct Bootstrap<'a> {
108    pub protocol: BootstrapProtocol,
109    pub clear_uart_rx: bool,
110    pub uart_params: &'a UartParams,
111    pub spi_params: &'a SpiParams,
112    reset_pin: Rc<dyn GpioPin>,
113    reset_delay: Duration,
114    leave_in_reset: bool,
115    leave_in_bootstrap: bool,
116}
117
118impl<'a> Bootstrap<'a> {
119    /// Perform the update, sending the firmware `payload` to a SPI or UART target depending on
120    /// given `options`, which specifies protocol and port to use.
121    pub fn update(
122        transport: &TransportWrapper,
123        options: &BootstrapOptions,
124        payload: &[u8],
125    ) -> Result<()> {
126        Self::update_with_progress(transport, options, payload, &NoProgressBar)
127    }
128
129    /// Perform the update, sending the firmware `payload` to a SPI or UART target depending on
130    /// given `options`, which specifies protocol and port to use.  The `progress` callback will
131    /// be called with the flash address and length of each chunk sent to the target device.
132    pub fn update_with_progress(
133        transport: &TransportWrapper,
134        options: &BootstrapOptions,
135        payload: &[u8],
136        progress: &dyn ProgressIndicator,
137    ) -> Result<()> {
138        if transport
139            .capabilities()?
140            .request(Capability::PROXY)
141            .ok()
142            .is_ok()
143        {
144            // The transport happens to be connection to a remove opentitan session.  Pass
145            // payload along with all relevant command line arguments to the remote session, and
146            // it will run the actual bootstrapping logic.
147            transport.proxy_ops()?.bootstrap(options, payload)?;
148            return Ok(());
149        }
150        let updater: Box<dyn UpdateProtocol> = match options.protocol {
151            BootstrapProtocol::Primitive => Box::new(primitive::Primitive::new(options)),
152            BootstrapProtocol::Legacy => Box::new(legacy::Legacy::new(options)),
153            BootstrapProtocol::LegacyRescue => Box::new(legacy_rescue::LegacyRescue::new(options)),
154            BootstrapProtocol::Eeprom => Box::new(eeprom::Eeprom::new()),
155            BootstrapProtocol::Emulator => {
156                // Not intended to be implemented by this struct.
157                unimplemented!();
158            }
159        };
160        Bootstrap {
161            protocol: options.protocol,
162            clear_uart_rx: options.clear_uart.unwrap_or(false),
163            uart_params: &options.uart_params,
164            spi_params: &options.spi_params,
165            reset_pin: transport.gpio_pin("RESET")?,
166            reset_delay: options.reset_delay,
167            leave_in_reset: options.leave_in_reset,
168            leave_in_bootstrap: options.leave_in_bootstrap,
169        }
170        .do_update(updater, transport, payload, progress)
171    }
172
173    fn do_update(
174        &self,
175        updater: Box<dyn UpdateProtocol>,
176        transport: &TransportWrapper,
177        payload: &[u8],
178        progress: &dyn ProgressIndicator,
179    ) -> Result<()> {
180        updater.verify_capabilities(self, transport)?;
181        let perform_bootstrap_reset = updater.uses_common_bootstrap_reset();
182        let rom_boot_strapping = transport.pin_strapping("ROM_BOOTSTRAP")?;
183
184        if perform_bootstrap_reset {
185            log::info!("Asserting bootstrap pins...");
186            rom_boot_strapping.apply()?;
187            transport.reset_target(self.reset_delay, self.clear_uart_rx)?;
188            log::info!("Performing bootstrap...");
189        }
190        let result = updater.update(self, transport, payload, progress);
191
192        if !self.leave_in_bootstrap && perform_bootstrap_reset {
193            if self.leave_in_reset {
194                log::info!("Releasing bootstrap pins, leaving device in reset...");
195                transport.pin_strapping("RESET")?.apply()?;
196                // For the case the ROM continuously monitors the bootstrapping pin, and boots the
197                // newly flashed image as soon as it is de-asserted, we only de-assert after
198                // having put the device under reset, in order to ensure that the caller can
199                // control when the newly flashed image gets to boot the first time.
200                rom_boot_strapping.remove()?;
201            } else {
202                log::info!("Releasing bootstrap pins, resetting device...");
203                rom_boot_strapping.remove()?;
204                // Don't clear the UART RX buffer after bootstrap to preserve the bootstrap
205                // output.
206                transport.reset_target(self.reset_delay, false)?;
207            }
208        }
209        result
210    }
211}