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, UartRx};
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    /// If set, keep the bootstrap strapping applied and do not perform the post-bootstrap reset
89    /// sequence.
90    #[arg(long)]
91    pub leave_in_bootstrap: bool,
92    /// If set, leave the reset signal asserted after completed bootstrapping.
93    #[arg(long)]
94    pub leave_in_reset: bool,
95    /// Duration of the inter-frame delay.
96    #[arg(long, value_parser = parse_duration)]
97    pub inter_frame_delay: Option<Duration>,
98    /// Duration of the flash-erase delay.
99    #[arg(long, value_parser = parse_duration)]
100    pub flash_erase_delay: Option<Duration>,
101}
102
103/// Bootstrap wraps and drives the various bootstrap protocols.
104pub struct Bootstrap<'a> {
105    pub protocol: BootstrapProtocol,
106    pub clear_uart_rx: bool,
107    pub uart_params: &'a UartParams,
108    pub spi_params: &'a SpiParams,
109    reset_pin: Rc<dyn GpioPin>,
110    leave_in_reset: bool,
111    leave_in_bootstrap: bool,
112}
113
114impl<'a> Bootstrap<'a> {
115    /// Perform the update, sending the firmware `payload` to a SPI or UART target depending on
116    /// given `options`, which specifies protocol and port to use.
117    pub fn update(
118        transport: &TransportWrapper,
119        options: &BootstrapOptions,
120        payload: &[u8],
121    ) -> Result<()> {
122        Self::update_with_progress(transport, options, payload, &NoProgressBar)
123    }
124
125    /// Perform the update, sending the firmware `payload` to a SPI or UART target depending on
126    /// given `options`, which specifies protocol and port to use.  The `progress` callback will
127    /// be called with the flash address and length of each chunk sent to the target device.
128    pub fn update_with_progress(
129        transport: &TransportWrapper,
130        options: &BootstrapOptions,
131        payload: &[u8],
132        progress: &dyn ProgressIndicator,
133    ) -> Result<()> {
134        if transport
135            .capabilities()?
136            .request(Capability::PROXY)
137            .ok()
138            .is_ok()
139        {
140            // The transport happens to be connection to a remove opentitan session.  Pass
141            // payload along with all relevant command line arguments to the remote session, and
142            // it will run the actual bootstrapping logic.
143            transport.proxy_ops()?.bootstrap(options, payload)?;
144            return Ok(());
145        }
146        let updater: Box<dyn UpdateProtocol> = match options.protocol {
147            BootstrapProtocol::Primitive => Box::new(primitive::Primitive::new(options)),
148            BootstrapProtocol::Legacy => Box::new(legacy::Legacy::new(options)),
149            BootstrapProtocol::LegacyRescue => Box::new(legacy_rescue::LegacyRescue::new(options)),
150            BootstrapProtocol::Eeprom => Box::new(eeprom::Eeprom::new()),
151            BootstrapProtocol::Emulator => {
152                // Not intended to be implemented by this struct.
153                unimplemented!();
154            }
155        };
156        Bootstrap {
157            protocol: options.protocol,
158            clear_uart_rx: options.clear_uart.unwrap_or(false),
159            uart_params: &options.uart_params,
160            spi_params: &options.spi_params,
161            reset_pin: transport.gpio_pin("RESET")?,
162            leave_in_reset: options.leave_in_reset,
163            leave_in_bootstrap: options.leave_in_bootstrap,
164        }
165        .do_update(updater, transport, payload, progress)
166    }
167
168    fn do_update(
169        &self,
170        updater: Box<dyn UpdateProtocol>,
171        transport: &TransportWrapper,
172        payload: &[u8],
173        progress: &dyn ProgressIndicator,
174    ) -> Result<()> {
175        updater.verify_capabilities(self, transport)?;
176        let perform_bootstrap_reset = updater.uses_common_bootstrap_reset();
177        let rom_boot_strapping = transport.pin_strapping("ROM_BOOTSTRAP")?;
178
179        if perform_bootstrap_reset {
180            log::info!("Asserting bootstrap pins...");
181            rom_boot_strapping.apply()?;
182            let uart_rx = match self.clear_uart_rx {
183                true => UartRx::Clear,
184                false => UartRx::Keep,
185            };
186            transport.reset(uart_rx)?;
187            log::info!("Performing bootstrap...");
188        }
189        let result = updater.update(self, transport, payload, progress);
190
191        if !self.leave_in_bootstrap && perform_bootstrap_reset {
192            if self.leave_in_reset {
193                log::info!("Releasing bootstrap pins, leaving device in reset...");
194                transport.pin_strapping("RESET")?.apply()?;
195                // For the case the ROM continuously monitors the bootstrapping pin, and boots the
196                // newly flashed image as soon as it is de-asserted, we only de-assert after
197                // having put the device under reset, in order to ensure that the caller can
198                // control when the newly flashed image gets to boot the first time.
199                rom_boot_strapping.remove()?;
200            } else {
201                log::info!("Releasing bootstrap pins, resetting device...");
202                rom_boot_strapping.remove()?;
203                // Don't clear the UART RX buffer after bootstrap to preserve the bootstrap
204                // output.
205                transport.reset(UartRx::Keep)?;
206            }
207        }
208        result
209    }
210}