Skip to main content

embassy_boot/
lib.rs

1#![no_std]
2#![allow(async_fn_in_trait)]
3#![allow(unsafe_op_in_unsafe_fn)]
4#![warn(missing_docs)]
5#![doc = include_str!("../README.md")]
6
7//! ## Feature flags
8#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
9
10mod fmt;
11
12mod boot_loader;
13mod digest_adapters;
14mod firmware_updater;
15#[cfg(test)]
16mod mem_flash;
17#[cfg(test)]
18mod test_flash;
19
20// The expected value of the flash after an erase
21// TODO: Use the value provided by NorFlash when available
22#[cfg(not(feature = "flash-erase-zero"))]
23pub(crate) const STATE_ERASE_VALUE: u8 = 0xFF;
24#[cfg(feature = "flash-erase-zero")]
25pub(crate) const STATE_ERASE_VALUE: u8 = 0x00;
26
27pub use boot_loader::{BootError, BootLoader, BootLoaderConfig};
28pub use firmware_updater::{
29    BlockingFirmwareState, BlockingFirmwareUpdater, FirmwareState, FirmwareUpdater, FirmwareUpdaterConfig,
30    FirmwareUpdaterError,
31};
32
33pub(crate) const REVERT_MAGIC: u8 = 0xC0;
34pub(crate) const BOOT_MAGIC: u8 = 0xD0;
35pub(crate) const SWAP_MAGIC: u8 = 0xF0;
36pub(crate) const DFU_DETACH_MAGIC: u8 = 0xE0;
37
38/// The state of the bootloader after running prepare.
39#[derive(PartialEq, Eq, Debug)]
40#[cfg_attr(feature = "defmt", derive(defmt::Format))]
41pub enum State {
42    /// Bootloader is ready to boot the active partition.
43    Boot,
44    /// Bootloader has swapped the active partition with the dfu partition and will attempt boot.
45    Swap,
46    /// Bootloader has reverted the active partition with the dfu partition and will attempt boot.
47    Revert,
48    /// Application has received a request to reboot into DFU mode to apply an update.
49    DfuDetach,
50}
51
52impl<T> From<T> for State
53where
54    T: AsRef<[u8]>,
55{
56    fn from(magic: T) -> State {
57        let magic = magic.as_ref();
58        if !magic.iter().any(|&b| b != SWAP_MAGIC) {
59            State::Swap
60        } else if !magic.iter().any(|&b| b != REVERT_MAGIC) {
61            State::Revert
62        } else if !magic.iter().any(|&b| b != DFU_DETACH_MAGIC) {
63            State::DfuDetach
64        } else {
65            State::Boot
66        }
67    }
68}
69
70/// Buffer aligned to 32 byte boundary, largest known alignment requirement for embassy-boot.
71#[repr(align(32))]
72pub struct AlignedBuffer<const N: usize>(pub [u8; N]);
73
74impl<const N: usize> AsRef<[u8]> for AlignedBuffer<N> {
75    fn as_ref(&self) -> &[u8] {
76        &self.0
77    }
78}
79
80impl<const N: usize> AsMut<[u8]> for AlignedBuffer<N> {
81    fn as_mut(&mut self) -> &mut [u8] {
82        &mut self.0
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    #![allow(unused_imports)]
89
90    use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
91    use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash;
92    use futures::executor::block_on;
93
94    use super::*;
95    use crate::boot_loader::BootLoaderConfig;
96    use crate::firmware_updater::FirmwareUpdaterConfig;
97    use crate::mem_flash::MemFlash;
98    use crate::test_flash::{AsyncTestFlash, BlockingTestFlash};
99
100    /*
101    #[test]
102    fn test_bad_magic() {
103        let mut flash = MemFlash([0xff; 131072]);
104        let mut flash = SingleFlashConfig::new(&mut flash);
105
106        let mut bootloader = BootLoader::<4096>::new(ACTIVE, DFU, STATE);
107
108        assert_eq!(
109            bootloader.prepare_boot(&mut flash),
110            Err(BootError::BadMagic)
111        );
112    }
113    */
114
115    #[test]
116    fn test_boot_state() {
117        let flash = BlockingTestFlash::new(BootLoaderConfig {
118            active: MemFlash::<57344, 4096, 4>::default(),
119            dfu: MemFlash::<61440, 4096, 4>::default(),
120            state: MemFlash::<4096, 4096, 4>::default(),
121        });
122
123        flash.state().write(0, &[BOOT_MAGIC; 4]).unwrap();
124
125        let mut bootloader = BootLoader::new(BootLoaderConfig {
126            active: flash.active(),
127            dfu: flash.dfu(),
128            state: flash.state(),
129        });
130
131        let mut page = [0; 4096];
132        assert_eq!(State::Boot, bootloader.prepare_boot(&mut page).unwrap());
133    }
134
135    #[test]
136    #[cfg(not(feature = "_verify"))]
137    fn test_swap_state() {
138        const FIRMWARE_SIZE: usize = 57344;
139        let flash = AsyncTestFlash::new(BootLoaderConfig {
140            active: MemFlash::<FIRMWARE_SIZE, 4096, 4>::default(),
141            dfu: MemFlash::<61440, 4096, 4>::default(),
142            state: MemFlash::<4096, 4096, 4>::default(),
143        });
144
145        const ORIGINAL: [u8; FIRMWARE_SIZE] = [0x55; FIRMWARE_SIZE];
146        const UPDATE: [u8; FIRMWARE_SIZE] = [0xAA; FIRMWARE_SIZE];
147        let mut aligned = [0; 4];
148
149        block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap();
150        block_on(flash.active().write(0, &ORIGINAL)).unwrap();
151
152        let mut updater = FirmwareUpdater::new(
153            FirmwareUpdaterConfig {
154                dfu: flash.dfu(),
155                state: flash.state(),
156            },
157            &mut aligned,
158        );
159        block_on(updater.write_firmware(0, &UPDATE)).unwrap();
160        block_on(updater.mark_updated()).unwrap();
161
162        // Writing after marking updated is not allowed until marked as booted.
163        let res: Result<(), FirmwareUpdaterError> = block_on(updater.write_firmware(0, &UPDATE));
164        assert!(matches!(res, Err::<(), _>(FirmwareUpdaterError::BadState)));
165
166        let flash = flash.into_blocking();
167        let mut bootloader = BootLoader::new(BootLoaderConfig {
168            active: flash.active(),
169            dfu: flash.dfu(),
170            state: flash.state(),
171        });
172
173        let mut page = [0; 1024];
174        assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap());
175
176        let mut read_buf = [0; FIRMWARE_SIZE];
177        flash.active().read(0, &mut read_buf).unwrap();
178        assert_eq!(UPDATE, read_buf);
179        // First DFU page is untouched
180        flash.dfu().read(4096, &mut read_buf).unwrap();
181        assert_eq!(ORIGINAL, read_buf);
182
183        // Running again should cause a revert
184        assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap());
185
186        // Next time we know it was reverted
187        assert_eq!(State::Revert, bootloader.prepare_boot(&mut page).unwrap());
188
189        let mut read_buf = [0; FIRMWARE_SIZE];
190        flash.active().read(0, &mut read_buf).unwrap();
191        assert_eq!(ORIGINAL, read_buf);
192        // Last DFU page is untouched
193        flash.dfu().read(0, &mut read_buf).unwrap();
194        assert_eq!(UPDATE, read_buf);
195
196        // Mark as booted
197        let flash = flash.into_async();
198        let mut updater = FirmwareUpdater::new(
199            FirmwareUpdaterConfig {
200                dfu: flash.dfu(),
201                state: flash.state(),
202            },
203            &mut aligned,
204        );
205        block_on(updater.mark_booted()).unwrap();
206
207        let flash = flash.into_blocking();
208        let mut bootloader = BootLoader::new(BootLoaderConfig {
209            active: flash.active(),
210            dfu: flash.dfu(),
211            state: flash.state(),
212        });
213        assert_eq!(State::Boot, bootloader.prepare_boot(&mut page).unwrap());
214    }
215
216    #[test]
217    #[cfg(not(feature = "_verify"))]
218    fn test_swap_state_active_page_biggest() {
219        const FIRMWARE_SIZE: usize = 12288;
220        let flash = AsyncTestFlash::new(BootLoaderConfig {
221            active: MemFlash::<12288, 4096, 8>::random(),
222            dfu: MemFlash::<16384, 2048, 8>::random(),
223            state: MemFlash::<2048, 128, 4>::random(),
224        });
225
226        const ORIGINAL: [u8; FIRMWARE_SIZE] = [0x55; FIRMWARE_SIZE];
227        const UPDATE: [u8; FIRMWARE_SIZE] = [0xAA; FIRMWARE_SIZE];
228        let mut aligned = [0; 4];
229
230        block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap();
231        block_on(flash.active().write(0, &ORIGINAL)).unwrap();
232
233        let mut updater = FirmwareUpdater::new(
234            FirmwareUpdaterConfig {
235                dfu: flash.dfu(),
236                state: flash.state(),
237            },
238            &mut aligned,
239        );
240        block_on(updater.write_firmware(0, &UPDATE)).unwrap();
241        block_on(updater.mark_updated()).unwrap();
242
243        let flash = flash.into_blocking();
244        let mut bootloader = BootLoader::new(BootLoaderConfig {
245            active: flash.active(),
246            dfu: flash.dfu(),
247            state: flash.state(),
248        });
249
250        let mut page = [0; 4096];
251        assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap());
252
253        let mut read_buf = [0; FIRMWARE_SIZE];
254        flash.active().read(0, &mut read_buf).unwrap();
255        assert_eq!(UPDATE, read_buf);
256        // First DFU page is untouched
257        flash.dfu().read(4096, &mut read_buf).unwrap();
258        assert_eq!(ORIGINAL, read_buf);
259    }
260
261    #[test]
262    #[cfg(not(feature = "_verify"))]
263    fn test_swap_state_dfu_page_biggest() {
264        const FIRMWARE_SIZE: usize = 12288;
265        let flash = AsyncTestFlash::new(BootLoaderConfig {
266            active: MemFlash::<FIRMWARE_SIZE, 2048, 4>::random(),
267            dfu: MemFlash::<16384, 4096, 8>::random(),
268            state: MemFlash::<2048, 128, 4>::random(),
269        });
270
271        const ORIGINAL: [u8; FIRMWARE_SIZE] = [0x55; FIRMWARE_SIZE];
272        const UPDATE: [u8; FIRMWARE_SIZE] = [0xAA; FIRMWARE_SIZE];
273        let mut aligned = [0; 4];
274
275        block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap();
276        block_on(flash.active().write(0, &ORIGINAL)).unwrap();
277
278        let mut updater = FirmwareUpdater::new(
279            FirmwareUpdaterConfig {
280                dfu: flash.dfu(),
281                state: flash.state(),
282            },
283            &mut aligned,
284        );
285        block_on(updater.write_firmware(0, &UPDATE)).unwrap();
286        block_on(updater.mark_updated()).unwrap();
287
288        let flash = flash.into_blocking();
289        let mut bootloader = BootLoader::new(BootLoaderConfig {
290            active: flash.active(),
291            dfu: flash.dfu(),
292            state: flash.state(),
293        });
294        let mut page = [0; 4096];
295        assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap());
296
297        let mut read_buf = [0; FIRMWARE_SIZE];
298        flash.active().read(0, &mut read_buf).unwrap();
299        assert_eq!(UPDATE, read_buf);
300        // First DFU page is untouched
301        flash.dfu().read(4096, &mut read_buf).unwrap();
302        assert_eq!(ORIGINAL, read_buf);
303    }
304
305    #[test]
306    #[cfg(feature = "_verify")]
307    fn test_verify() {
308        // The following key setup is based on:
309        // https://docs.rs/ed25519-dalek/latest/ed25519_dalek/#example
310
311        use ed25519_dalek::{Digest, Sha512, Signature, Signer, SigningKey, VerifyingKey};
312        use rand::rngs::OsRng;
313
314        let mut csprng = OsRng {};
315        let keypair = SigningKey::generate(&mut csprng);
316
317        let firmware: &[u8] = b"This are bytes that would otherwise be firmware bytes for DFU.";
318        let mut digest = Sha512::new();
319        digest.update(&firmware);
320        let message = digest.finalize();
321        let signature: Signature = keypair.sign(&message);
322
323        let public_key = keypair.verifying_key();
324
325        // Setup flash
326        let flash = BlockingTestFlash::new(BootLoaderConfig {
327            active: MemFlash::<0, 0, 0>::default(),
328            dfu: MemFlash::<4096, 4096, 4>::default(),
329            state: MemFlash::<4096, 4096, 4>::default(),
330        });
331
332        let firmware_len = firmware.len();
333
334        let mut write_buf = [0; 4096];
335        write_buf[0..firmware_len].copy_from_slice(firmware);
336        flash.dfu().write(0, &write_buf).unwrap();
337
338        // On with the test
339        let flash = flash.into_async();
340        let mut aligned = [0; 4];
341        let mut updater = FirmwareUpdater::new(
342            FirmwareUpdaterConfig {
343                dfu: flash.dfu(),
344                state: flash.state(),
345            },
346            &mut aligned,
347        );
348
349        assert!(
350            block_on(updater.verify_and_mark_updated(
351                &public_key.to_bytes(),
352                &signature.to_bytes(),
353                firmware_len as u32,
354            ))
355            .is_ok()
356        );
357    }
358}