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};
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, value_parser = parse_duration, default_value = "100ms")]
90 pub reset_delay: Duration,
91 #[arg(long)]
94 pub leave_in_bootstrap: bool,
95 #[arg(long)]
97 pub leave_in_reset: bool,
98 #[arg(long, value_parser = parse_duration)]
100 pub inter_frame_delay: Option<Duration>,
101 #[arg(long, value_parser = parse_duration)]
103 pub flash_erase_delay: Option<Duration>,
104}
105
106pub 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 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 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 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 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 rom_boot_strapping.remove()?;
201 } else {
202 log::info!("Releasing bootstrap pins, resetting device...");
203 rom_boot_strapping.remove()?;
204 transport.reset_target(self.reset_delay, false)?;
207 }
208 }
209 result
210 }
211}