1use 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#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, ValueEnum)]
44pub enum BootstrapProtocol {
45 Primitive,
46 Legacy,
47 LegacyRescue,
48 Eeprom,
49 Emulator,
50}
51
52trait UpdateProtocol {
54 fn verify_capabilities(
57 &self,
58 container: &Bootstrap,
59 transport: &TransportWrapper,
60 ) -> Result<()>;
61 fn uses_common_bootstrap_reset(&self) -> bool;
64 fn update(
66 &self,
67 container: &Bootstrap,
68 transport: &TransportWrapper,
69 payload: &[u8],
70 progress: &dyn ProgressIndicator,
71 ) -> Result<()>;
72}
73
74#[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 #[arg(short, long, value_enum, ignore_case = true, default_value = "eeprom")]
84 pub protocol: BootstrapProtocol,
85 #[arg(long)]
87 pub clear_uart: Option<bool>,
88 #[arg(long)]
91 pub leave_in_bootstrap: bool,
92 #[arg(long)]
94 pub leave_in_reset: bool,
95 #[arg(long, value_parser = parse_duration)]
97 pub inter_frame_delay: Option<Duration>,
98 #[arg(long, value_parser = parse_duration)]
100 pub flash_erase_delay: Option<Duration>,
101}
102
103pub 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 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 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 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 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 rom_boot_strapping.remove()?;
200 } else {
201 log::info!("Releasing bootstrap pins, resetting device...");
202 rom_boot_strapping.remove()?;
203 transport.reset(UartRx::Keep)?;
206 }
207 }
208 result
209 }
210}