probe_rs/flashing/download.rs
1use probe_rs_target::InstructionSet;
2#[cfg(feature = "builtin-formats")]
3use serde::{Deserialize, Serialize};
4
5use std::{fs::File, path::Path};
6
7use super::*;
8use crate::session::Session;
9
10/// Extended options for flashing a binary file.
11#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Default)]
12#[cfg(feature = "builtin-formats")]
13pub struct BinOptions {
14 /// The address in memory where the binary will be put at.
15 pub base_address: Option<u64>,
16 /// The number of bytes to skip at the start of the binary file.
17 pub skip: u32,
18}
19
20/// Extended options for flashing an ELF file.
21#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Default)]
22#[cfg(feature = "builtin-formats")]
23pub struct ElfOptions {
24 /// Sections to skip flashing
25 pub skip_sections: Vec<String>,
26}
27
28/// A finite list of all the errors that can occur when flashing a given file.
29///
30/// This includes corrupt file issues,
31/// OS permission issues as well as chip connectivity and memory boundary issues.
32#[derive(Debug, thiserror::Error, docsplay::Display)]
33pub enum FileDownloadError {
34 /// An error with the flashing procedure has occurred.
35 #[ignore_extra_doc_attributes]
36 ///
37 /// This is mostly an error in the communication with the target inflicted by a bad hardware connection or a probe-rs bug.
38 Flash(#[from] FlashError),
39
40 /// Failed to read or decode the IHEX file.
41 #[cfg(feature = "builtin-formats")]
42 IhexRead(#[from] ihex::ReaderError),
43
44 /// An IO error has occurred while reading the firmware file.
45 IO(#[from] std::io::Error),
46
47 /// Error while reading the object file: {0}.
48 Object(&'static str),
49
50 /// Failed to read or decode the ELF file.
51 #[cfg(feature = "builtin-formats")]
52 Elf(#[from] object::read::Error),
53
54 /// An error specific to the {format} image format has occurred.
55 ImageFormatSpecific {
56 /// The image format.
57 format: String,
58
59 /// The specific error.
60 source: Box<dyn std::error::Error + Send + Sync>,
61 },
62
63 /// No loadable segments were found in the ELF file.
64 #[ignore_extra_doc_attributes]
65 ///
66 /// This is most likely because of a bad linker script.
67 NoLoadableSegments,
68
69 /// The image ({image:?}) is not compatible with the target ({print_instr_sets(target)}).
70 IncompatibleImage {
71 /// The target's instruction set.
72 target: Vec<InstructionSet>,
73 /// The image's instruction set.
74 image: InstructionSet,
75 },
76
77 /// The target chip {target} is not compatible with the image. The image is compatible with: {image_chips.join(", ")}
78 IncompatibleImageChip {
79 /// The target chip.
80 target: String,
81 /// The chips compatible with the image.
82 image_chips: Vec<String>,
83 },
84
85 /// An error occurred during download.
86 Other(#[source] crate::Error),
87}
88
89fn print_instr_sets(instr_sets: &[InstructionSet]) -> String {
90 instr_sets
91 .iter()
92 .map(|instr_set| format!("{instr_set:?}"))
93 .collect::<Vec<_>>()
94 .join(", ")
95}
96
97/// Options for downloading a file onto a target chip.
98///
99/// This struct should be created using the [`DownloadOptions::default()`] function, and can be configured by setting
100/// the fields directly:
101///
102/// ```
103/// use probe_rs::flashing::DownloadOptions;
104///
105/// let mut options = DownloadOptions::default();
106///
107/// options.verify = true;
108/// ```
109#[derive(Default)]
110#[non_exhaustive]
111pub struct DownloadOptions<'p> {
112 /// An optional progress reporter which is used if this argument is set to `Some(...)`.
113 pub progress: FlashProgress<'p>,
114 /// If `keep_unwritten_bytes` is `true`, erased portions of the flash that are not overwritten by the ELF data
115 /// are restored afterwards, such that the old contents are untouched.
116 ///
117 /// This is necessary because the flash can only be erased in sectors. If only parts of the erased sector are written thereafter,
118 /// instead of the full sector, the excessively erased bytes wont match the contents before the erase which might not be intuitive
119 /// to the user or even worse, result in unexpected behavior if those contents contain important data.
120 pub keep_unwritten_bytes: bool,
121 /// Perform a dry run. This prepares everything for flashing, but does not write anything to flash.
122 pub dry_run: bool,
123 /// If this flag is set to true, probe-rs will try to use the chips built in method to do a full chip erase if one is available.
124 /// This is often faster than erasing a lot of single sectors.
125 /// So if you do not need the old contents of the flash, this is a good option.
126 pub do_chip_erase: bool,
127 /// If the chip was pre-erased with external erasers, this flag can set to true to skip erasing
128 /// It may be useful for mass production.
129 pub skip_erase: bool,
130 /// Before flashing, read back the flash contents to skip up-to-date regions.
131 pub preverify: bool,
132 /// After flashing, read back all the flashed data to verify it has been written correctly.
133 pub verify: bool,
134 /// Disable double buffering when loading flash.
135 pub disable_double_buffering: bool,
136 /// If there are multiple valid flash algorithms for a memory region, this list allows
137 /// overriding the default selection.
138 pub preferred_algos: Vec<String>,
139}
140
141impl DownloadOptions<'_> {
142 /// DownloadOptions with default values.
143 pub fn new() -> Self {
144 Self::default()
145 }
146}
147
148/// Builds a new flash loader for the given target and path. This
149/// will check the path for validity and check what pages have to be
150/// flashed etc.
151pub fn build_loader(
152 session: &mut Session,
153 path: impl AsRef<Path>,
154 format: impl ImageLoader,
155 image_instruction_set: Option<InstructionSet>,
156) -> Result<FlashLoader, FileDownloadError> {
157 // Create the flash loader
158 let mut loader = session.target().flash_loader();
159
160 // Add data from the BIN.
161 let mut file = File::open(path).map_err(FileDownloadError::IO)?;
162
163 loader.load_image(session, &mut file, format, image_instruction_set)?;
164
165 Ok(loader)
166}
167
168/// Downloads a file of given `format` at `path` to the flash of the target given in `session`.
169///
170/// This will ensure that memory boundaries are honored and does unlocking, erasing and programming of the flash for you.
171///
172/// If you are looking for more options, have a look at [download_file_with_options].
173pub fn download_file(
174 session: &mut Session,
175 path: impl AsRef<Path>,
176 format: impl ImageLoader,
177) -> Result<(), FileDownloadError> {
178 download_file_with_options(session, path, format, DownloadOptions::default())
179}
180
181/// Downloads a file of given `format` at `path` to the flash of the target given in `session`.
182///
183/// This will ensure that memory boundaries are honored and does unlocking, erasing and programming of the flash for you.
184///
185/// If you are looking for a simple version without many options, have a look at [download_file].
186pub fn download_file_with_options(
187 session: &mut Session,
188 path: impl AsRef<Path>,
189 format: impl ImageLoader,
190 options: DownloadOptions,
191) -> Result<(), FileDownloadError> {
192 let loader = build_loader(session, path, format, None)?;
193
194 loader
195 .commit(session, options)
196 .map_err(FileDownloadError::Flash)
197}