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