1use 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#[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
57trait UpdateProtocol {
59 fn verify_capabilities(
62 &self,
63 container: &Bootstrap,
64 transport: &TransportWrapper,
65 ) -> Result<()>;
66 fn uses_common_bootstrap_reset(&self) -> bool;
69 fn update(
71 &self,
72 container: &Bootstrap,
73 transport: &TransportWrapper,
74 payload: &[u8],
75 progress: &dyn ProgressIndicator,
76 ) -> Result<()>;
77}
78
79#[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 #[arg(short, long, value_enum, ignore_case = true, default_value = "eeprom")]
91 pub protocol: BootstrapProtocol,
92 #[arg(long)]
94 pub clear_uart: Option<bool>,
95 #[arg(long, value_parser=maybe_hex::<u32>)]
97 pub target_addr: Option<u32>,
98 #[arg(long)]
101 pub leave_in_bootstrap: bool,
102 #[arg(long)]
104 pub leave_in_reset: bool,
105 #[arg(long, value_parser = parse_duration)]
107 pub inter_frame_delay: Option<Duration>,
108 #[arg(long, value_parser = parse_duration)]
110 pub flash_erase_delay: Option<Duration>,
111}
112
113pub 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 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 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 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 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 rom_boot_strapping.remove()?;
215 } else {
216 log::info!("Releasing bootstrap pins, resetting device...");
217 rom_boot_strapping.remove()?;
218 transport.reset(UartRx::Keep)?;
221 }
222 }
223 result
224 }
225}