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