Skip to main content

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}