blob: a25e738c5c8ac6da7f9174e8e1b07c7c6ca15a8e [file] [log] [blame]
David Brown5c9e0f12019-01-09 16:34:33 -07001use log::{info, warn, error};
2use rand::{
3 distributions::{IndependentSample, Range},
4 Rng, SeedableRng, XorShiftRng,
5};
6use std::{
7 mem,
8 slice,
9};
10use aes_ctr::{
11 Aes128Ctr,
12 stream_cipher::{
13 generic_array::GenericArray,
14 NewFixStreamCipher,
15 StreamCipherCore,
16 },
17};
18
19use simflash::{Flash, SimFlashMap};
20use mcuboot_sys::{c, AreaDesc};
21use crate::caps::Caps;
22use crate::tlv::{TlvGen, TlvFlags, AES_SEC_KEY};
23
24impl Images {
25 /// A simple upgrade without forced failures.
26 ///
27 /// Returns the number of flash operations which can later be used to
28 /// inject failures at chosen steps.
29 pub fn run_basic_upgrade(&self) -> Result<i32, ()> {
30 let (flashmap, total_count) = try_upgrade(&self.flashmap, &self, None);
31 info!("Total flash operation count={}", total_count);
32
33 if !verify_image(&flashmap, &self.slots, 0, &self.upgrades) {
34 warn!("Image mismatch after first boot");
35 Err(())
36 } else {
37 Ok(total_count)
38 }
39 }
40
David Brown5c9e0f12019-01-09 16:34:33 -070041 pub fn run_basic_revert(&self) -> bool {
David Brown3910ab12019-01-11 12:02:26 -070042 if Caps::OverwriteUpgrade.present() {
43 return false;
44 }
David Brown5c9e0f12019-01-09 16:34:33 -070045
David Brown5c9e0f12019-01-09 16:34:33 -070046 let mut fails = 0;
47
48 // FIXME: this test would also pass if no swap is ever performed???
49 if Caps::SwapUpgrade.present() {
50 for count in 2 .. 5 {
51 info!("Try revert: {}", count);
52 let flashmap = try_revert(&self.flashmap, &self.areadesc, count);
53 if !verify_image(&flashmap, &self.slots, 0, &self.primaries) {
54 error!("Revert failure on count {}", count);
55 fails += 1;
56 }
57 }
58 }
59
60 fails > 0
61 }
62
63 pub fn run_perm_with_fails(&self) -> bool {
64 let mut fails = 0;
65 let total_flash_ops = self.total_count.unwrap();
66
67 // Let's try an image halfway through.
68 for i in 1 .. total_flash_ops {
69 info!("Try interruption at {}", i);
70 let (flashmap, count) = try_upgrade(&self.flashmap, &self, Some(i));
71 info!("Second boot, count={}", count);
72 if !verify_image(&flashmap, &self.slots, 0, &self.upgrades) {
73 warn!("FAIL at step {} of {}", i, total_flash_ops);
74 fails += 1;
75 }
76
77 if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
78 BOOT_FLAG_SET, BOOT_FLAG_SET) {
79 warn!("Mismatched trailer for Slot 0");
80 fails += 1;
81 }
82
83 if !verify_trailer(&flashmap, &self.slots, 1, BOOT_MAGIC_UNSET,
84 BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
85 warn!("Mismatched trailer for Slot 1");
86 fails += 1;
87 }
88
89 if Caps::SwapUpgrade.present() {
90 if !verify_image(&flashmap, &self.slots, 1, &self.primaries) {
91 warn!("Slot 1 FAIL at step {} of {}", i, total_flash_ops);
92 fails += 1;
93 }
94 }
95 }
96
97 if fails > 0 {
98 error!("{} out of {} failed {:.2}%", fails, total_flash_ops,
99 fails as f32 * 100.0 / total_flash_ops as f32);
100 }
101
102 fails > 0
103 }
104
105 pub fn run_perm_with_random_fails_5(&self) -> bool {
106 self.run_perm_with_random_fails(5)
107 }
108
109 pub fn run_perm_with_random_fails(&self, total_fails: usize) -> bool {
110 let mut fails = 0;
111 let total_flash_ops = self.total_count.unwrap();
112 let (flashmap, total_counts) = try_random_fails(&self.flashmap, &self,
113 total_flash_ops, total_fails);
114 info!("Random interruptions at reset points={:?}", total_counts);
115
116 let slot0_ok = verify_image(&flashmap, &self.slots, 0, &self.upgrades);
117 let slot1_ok = if Caps::SwapUpgrade.present() {
118 verify_image(&flashmap, &self.slots, 1, &self.primaries)
119 } else {
120 true
121 };
122 if !slot0_ok || !slot1_ok {
123 error!("Image mismatch after random interrupts: slot0={} slot1={}",
124 if slot0_ok { "ok" } else { "fail" },
125 if slot1_ok { "ok" } else { "fail" });
126 fails += 1;
127 }
128 if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
129 BOOT_FLAG_SET, BOOT_FLAG_SET) {
130 error!("Mismatched trailer for Slot 0");
131 fails += 1;
132 }
133 if !verify_trailer(&flashmap, &self.slots, 1, BOOT_MAGIC_UNSET,
134 BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
135 error!("Mismatched trailer for Slot 1");
136 fails += 1;
137 }
138
139 if fails > 0 {
140 error!("Error testing perm upgrade with {} fails", total_fails);
141 }
142
143 fails > 0
144 }
145
David Brown5c9e0f12019-01-09 16:34:33 -0700146 pub fn run_revert_with_fails(&self) -> bool {
David Brown3910ab12019-01-11 12:02:26 -0700147 if Caps::OverwriteUpgrade.present() {
148 return false;
149 }
David Brown5c9e0f12019-01-09 16:34:33 -0700150
David Brown5c9e0f12019-01-09 16:34:33 -0700151 let mut fails = 0;
152
153 if Caps::SwapUpgrade.present() {
154 for i in 1 .. (self.total_count.unwrap() - 1) {
155 info!("Try interruption at {}", i);
156 if try_revert_with_fail_at(&self.flashmap, &self, i) {
157 error!("Revert failed at interruption {}", i);
158 fails += 1;
159 }
160 }
161 }
162
163 fails > 0
164 }
165
David Brown5c9e0f12019-01-09 16:34:33 -0700166 pub fn run_norevert(&self) -> bool {
David Brown3910ab12019-01-11 12:02:26 -0700167 if Caps::OverwriteUpgrade.present() {
168 return false;
169 }
David Brown5c9e0f12019-01-09 16:34:33 -0700170
David Brown5c9e0f12019-01-09 16:34:33 -0700171 let mut flashmap = self.flashmap.clone();
172 let mut fails = 0;
173
174 info!("Try norevert");
175
176 // First do a normal upgrade...
177 let (result, _) = c::boot_go(&mut flashmap, &self.areadesc, None, false);
178 if result != 0 {
179 warn!("Failed first boot");
180 fails += 1;
181 }
182
183 //FIXME: copy_done is written by boot_go, is it ok if no copy
184 // was ever done?
185
186 if !verify_image(&flashmap, &self.slots, 0, &self.upgrades) {
187 warn!("Slot 0 image verification FAIL");
188 fails += 1;
189 }
190 if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
191 BOOT_FLAG_UNSET, BOOT_FLAG_SET) {
192 warn!("Mismatched trailer for Slot 0");
193 fails += 1;
194 }
195 if !verify_trailer(&flashmap, &self.slots, 1, BOOT_MAGIC_UNSET,
196 BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
197 warn!("Mismatched trailer for Slot 1");
198 fails += 1;
199 }
200
201 // Marks image in slot0 as permanent, no revert should happen...
202 mark_permanent_upgrade(&mut flashmap, &self.slots[0]);
203
204 if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
205 BOOT_FLAG_SET, BOOT_FLAG_SET) {
206 warn!("Mismatched trailer for Slot 0");
207 fails += 1;
208 }
209
210 let (result, _) = c::boot_go(&mut flashmap, &self.areadesc, None, false);
211 if result != 0 {
212 warn!("Failed second boot");
213 fails += 1;
214 }
215
216 if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
217 BOOT_FLAG_SET, BOOT_FLAG_SET) {
218 warn!("Mismatched trailer for Slot 0");
219 fails += 1;
220 }
221 if !verify_image(&flashmap, &self.slots, 0, &self.upgrades) {
222 warn!("Failed image verification");
223 fails += 1;
224 }
225
226 if fails > 0 {
227 error!("Error running upgrade without revert");
228 }
229
230 fails > 0
231 }
232
233 // Tests a new image written to slot0 that already has magic and image_ok set
234 // while there is no image on slot1, so no revert should ever happen...
235 pub fn run_norevert_newimage(&self) -> bool {
236 let mut flashmap = self.flashmap.clone();
237 let mut fails = 0;
238
239 info!("Try non-revert on imgtool generated image");
240
241 mark_upgrade(&mut flashmap, &self.slots[0]);
242
243 // This simulates writing an image created by imgtool to Slot 0
244 if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
245 BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
246 warn!("Mismatched trailer for Slot 0");
247 fails += 1;
248 }
249
250 // Run the bootloader...
251 let (result, _) = c::boot_go(&mut flashmap, &self.areadesc, None, false);
252 if result != 0 {
253 warn!("Failed first boot");
254 fails += 1;
255 }
256
257 // State should not have changed
258 if !verify_image(&flashmap, &self.slots, 0, &self.primaries) {
259 warn!("Failed image verification");
260 fails += 1;
261 }
262 if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
263 BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
264 warn!("Mismatched trailer for Slot 0");
265 fails += 1;
266 }
267 if !verify_trailer(&flashmap, &self.slots, 1, BOOT_MAGIC_UNSET,
268 BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
269 warn!("Mismatched trailer for Slot 1");
270 fails += 1;
271 }
272
273 if fails > 0 {
274 error!("Expected a non revert with new image");
275 }
276
277 fails > 0
278 }
279
280 // Tests a new image written to slot0 that already has magic and image_ok set
281 // while there is no image on slot1, so no revert should ever happen...
282 pub fn run_signfail_upgrade(&self) -> bool {
283 let mut flashmap = self.flashmap.clone();
284 let mut fails = 0;
285
286 info!("Try upgrade image with bad signature");
287
288 mark_upgrade(&mut flashmap, &self.slots[0]);
289 mark_permanent_upgrade(&mut flashmap, &self.slots[0]);
290 mark_upgrade(&mut flashmap, &self.slots[1]);
291
292 if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
293 BOOT_FLAG_SET, BOOT_FLAG_UNSET) {
294 warn!("Mismatched trailer for Slot 0");
295 fails += 1;
296 }
297
298 // Run the bootloader...
299 let (result, _) = c::boot_go(&mut flashmap, &self.areadesc, None, false);
300 if result != 0 {
301 warn!("Failed first boot");
302 fails += 1;
303 }
304
305 // State should not have changed
306 if !verify_image(&flashmap, &self.slots, 0, &self.primaries) {
307 warn!("Failed image verification");
308 fails += 1;
309 }
310 if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
311 BOOT_FLAG_SET, BOOT_FLAG_UNSET) {
312 warn!("Mismatched trailer for Slot 0");
313 fails += 1;
314 }
315
316 if fails > 0 {
317 error!("Expected an upgrade failure when image has bad signature");
318 }
319
320 fails > 0
321 }
322
323 #[cfg(not(feature = "overwrite-only"))]
324 fn trailer_sz(&self, align: usize) -> usize {
325 c::boot_trailer_sz(align as u8) as usize
326 }
327
328 // FIXME: could get status sz from bootloader
329 #[cfg(not(feature = "overwrite-only"))]
330 #[cfg(not(feature = "enc-rsa"))]
331 #[cfg(not(feature = "enc-kw"))]
332 fn status_sz(&self, align: usize) -> usize {
333 self.trailer_sz(align) - (16 + 24)
334 }
335
336 #[cfg(feature = "enc-rsa")]
337 #[cfg(not(feature = "overwrite-only"))]
338 fn status_sz(&self, align: usize) -> usize {
339 self.trailer_sz(align) - (16 + 24 + 32)
340 }
341
342 #[cfg(feature = "enc-kw")]
343 #[cfg(not(feature = "overwrite-only"))]
344 fn status_sz(&self, align: usize) -> usize {
345 self.trailer_sz(align) - (16 + 24 + 32)
346 }
347
348 /// This test runs a simple upgrade with no fails in the images, but
349 /// allowing for fails in the status area. This should run to the end
350 /// and warn that write fails were detected...
351 #[cfg(not(feature = "validate-slot0"))]
352 pub fn run_with_status_fails_complete(&self) -> bool { false }
353
354 #[cfg(feature = "validate-slot0")]
355 pub fn run_with_status_fails_complete(&self) -> bool {
356 let mut flashmap = self.flashmap.clone();
357 let mut fails = 0;
358
359 info!("Try swap with status fails");
360
361 mark_permanent_upgrade(&mut flashmap, &self.slots[1]);
362 self.mark_bad_status_with_rate(&mut flashmap, 0, 1.0);
363
364 let (result, asserts) = c::boot_go(&mut flashmap, &self.areadesc, None, true);
365 if result != 0 {
366 warn!("Failed!");
367 fails += 1;
368 }
369
370 // Failed writes to the marked "bad" region don't assert anymore.
371 // Any detected assert() is happening in another part of the code.
372 if asserts != 0 {
373 warn!("At least one assert() was called");
374 fails += 1;
375 }
376
377 if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
378 BOOT_FLAG_SET, BOOT_FLAG_SET) {
379 warn!("Mismatched trailer for Slot 0");
380 fails += 1;
381 }
382
383 if !verify_image(&flashmap, &self.slots, 0, &self.upgrades) {
384 warn!("Failed image verification");
385 fails += 1;
386 }
387
388 info!("validate slot0 enabled; re-run of boot_go should just work");
389 let (result, _) = c::boot_go(&mut flashmap, &self.areadesc, None, false);
390 if result != 0 {
391 warn!("Failed!");
392 fails += 1;
393 }
394
395 if fails > 0 {
396 error!("Error running upgrade with status write fails");
397 }
398
399 fails > 0
400 }
401
402 /// This test runs a simple upgrade with no fails in the images, but
403 /// allowing for fails in the status area. This should run to the end
404 /// and warn that write fails were detected...
405 #[cfg(feature = "validate-slot0")]
406 pub fn run_with_status_fails_with_reset(&self) -> bool {
407 let mut flashmap = self.flashmap.clone();
408 let mut fails = 0;
409 let mut count = self.total_count.unwrap() / 2;
410
411 //info!("count={}\n", count);
412
413 info!("Try interrupted swap with status fails");
414
415 mark_permanent_upgrade(&mut flashmap, &self.slots[1]);
416 self.mark_bad_status_with_rate(&mut flashmap, 0, 0.5);
417
418 // Should not fail, writing to bad regions does not assert
419 let (_, asserts) = c::boot_go(&mut flashmap, &self.areadesc, Some(&mut count), true);
420 if asserts != 0 {
421 warn!("At least one assert() was called");
422 fails += 1;
423 }
424
425 self.reset_bad_status(&mut flashmap, 0);
426
427 info!("Resuming an interrupted swap operation");
428 let (_, asserts) = c::boot_go(&mut flashmap, &self.areadesc, None, true);
429
430 // This might throw no asserts, for large sector devices, where
431 // a single failure writing is indistinguishable from no failure,
432 // or throw a single assert for small sector devices that fail
433 // multiple times...
434 if asserts > 1 {
435 warn!("Expected single assert validating slot0, more detected {}", asserts);
436 fails += 1;
437 }
438
439 if fails > 0 {
440 error!("Error running upgrade with status write fails");
441 }
442
443 fails > 0
444 }
445
446 /// Adds a new flash area that fails statistically
447 #[cfg(not(feature = "overwrite-only"))]
448 fn mark_bad_status_with_rate(&self, flashmap: &mut SimFlashMap, slot: usize,
449 rate: f32) {
450 let dev_id = &self.slots[slot].dev_id;
451 let flash = flashmap.get_mut(&dev_id).unwrap();
452 let align = flash.align();
453 let off = &self.slots[0].base_off;
454 let len = &self.slots[0].len;
455 let status_off = off + len - self.trailer_sz(align);
456
457 // Mark the status area as a bad area
458 let _ = flash.add_bad_region(status_off, self.status_sz(align), rate);
459 }
460
461 #[cfg(feature = "validate-slot0")]
462 fn reset_bad_status(&self, flashmap: &mut SimFlashMap, slot: usize) {
463 let dev_id = &self.slots[slot].dev_id;
464 let flash = flashmap.get_mut(&dev_id).unwrap();
465 flash.reset_bad_regions();
466
467 // Disabling write verification the only assert triggered by
468 // boot_go should be checking for integrity of status bytes.
469 flash.set_verify_writes(false);
470 }
471
472 #[cfg(not(feature = "validate-slot0"))]
473 #[cfg(not(feature = "overwrite-only"))]
474 pub fn run_with_status_fails_with_reset(&self) -> bool {
475 let mut flashmap = self.flashmap.clone();
476 let mut fails = 0;
477
478 info!("Try interrupted swap with status fails");
479
480 mark_permanent_upgrade(&mut flashmap, &self.slots[1]);
481 self.mark_bad_status_with_rate(&mut flashmap, 0, 1.0);
482
483 // This is expected to fail while writing to bad regions...
484 let (_, asserts) = c::boot_go(&mut flashmap, &self.areadesc, None, true);
485 if asserts == 0 {
486 warn!("No assert() detected");
487 fails += 1;
488 }
489
490 fails > 0
491 }
492
493 #[cfg(feature = "overwrite-only")]
494 pub fn run_with_status_fails_with_reset(&self) -> bool {
495 false
496 }
497}
498
499/// Test a boot, optionally stopping after 'n' flash options. Returns a count
500/// of the number of flash operations done total.
501fn try_upgrade(flashmap: &SimFlashMap, images: &Images,
502 stop: Option<i32>) -> (SimFlashMap, i32) {
503 // Clone the flash to have a new copy.
504 let mut flashmap = flashmap.clone();
505
506 mark_permanent_upgrade(&mut flashmap, &images.slots[1]);
507
508 let mut counter = stop.unwrap_or(0);
509
510 let (first_interrupted, count) = match c::boot_go(&mut flashmap, &images.areadesc, Some(&mut counter), false) {
511 (-0x13579, _) => (true, stop.unwrap()),
512 (0, _) => (false, -counter),
513 (x, _) => panic!("Unknown return: {}", x),
514 };
515
516 counter = 0;
517 if first_interrupted {
518 // fl.dump();
519 match c::boot_go(&mut flashmap, &images.areadesc, Some(&mut counter), false) {
520 (-0x13579, _) => panic!("Shouldn't stop again"),
521 (0, _) => (),
522 (x, _) => panic!("Unknown return: {}", x),
523 }
524 }
525
526 (flashmap, count - counter)
527}
528
David Brown5c9e0f12019-01-09 16:34:33 -0700529fn try_revert(flashmap: &SimFlashMap, areadesc: &AreaDesc, count: usize) -> SimFlashMap {
530 let mut flashmap = flashmap.clone();
531
532 // fl.write_file("image0.bin").unwrap();
533 for i in 0 .. count {
534 info!("Running boot pass {}", i + 1);
535 assert_eq!(c::boot_go(&mut flashmap, &areadesc, None, false), (0, 0));
536 }
537 flashmap
538}
539
David Brown5c9e0f12019-01-09 16:34:33 -0700540fn try_revert_with_fail_at(flashmap: &SimFlashMap, images: &Images,
541 stop: i32) -> bool {
542 let mut flashmap = flashmap.clone();
543 let mut fails = 0;
544
545 let mut counter = stop;
546 let (x, _) = c::boot_go(&mut flashmap, &images.areadesc, Some(&mut counter), false);
547 if x != -0x13579 {
548 warn!("Should have stopped at interruption point");
549 fails += 1;
550 }
551
552 if !verify_trailer(&flashmap, &images.slots, 0, None, None, BOOT_FLAG_UNSET) {
553 warn!("copy_done should be unset");
554 fails += 1;
555 }
556
557 let (x, _) = c::boot_go(&mut flashmap, &images.areadesc, None, false);
558 if x != 0 {
559 warn!("Should have finished upgrade");
560 fails += 1;
561 }
562
563 if !verify_image(&flashmap, &images.slots, 0, &images.upgrades) {
564 warn!("Image in slot 0 before revert is invalid at stop={}", stop);
565 fails += 1;
566 }
567 if !verify_image(&flashmap, &images.slots, 1, &images.primaries) {
568 warn!("Image in slot 1 before revert is invalid at stop={}", stop);
569 fails += 1;
570 }
571 if !verify_trailer(&flashmap, &images.slots, 0, BOOT_MAGIC_GOOD,
572 BOOT_FLAG_UNSET, BOOT_FLAG_SET) {
573 warn!("Mismatched trailer for Slot 0 before revert");
574 fails += 1;
575 }
576 if !verify_trailer(&flashmap, &images.slots, 1, BOOT_MAGIC_UNSET,
577 BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
578 warn!("Mismatched trailer for Slot 1 before revert");
579 fails += 1;
580 }
581
582 // Do Revert
583 let (x, _) = c::boot_go(&mut flashmap, &images.areadesc, None, false);
584 if x != 0 {
585 warn!("Should have finished a revert");
586 fails += 1;
587 }
588
589 if !verify_image(&flashmap, &images.slots, 0, &images.primaries) {
590 warn!("Image in slot 0 after revert is invalid at stop={}", stop);
591 fails += 1;
592 }
593 if !verify_image(&flashmap, &images.slots, 1, &images.upgrades) {
594 warn!("Image in slot 1 after revert is invalid at stop={}", stop);
595 fails += 1;
596 }
597 if !verify_trailer(&flashmap, &images.slots, 0, BOOT_MAGIC_GOOD,
598 BOOT_FLAG_SET, BOOT_FLAG_SET) {
599 warn!("Mismatched trailer for Slot 1 after revert");
600 fails += 1;
601 }
602 if !verify_trailer(&flashmap, &images.slots, 1, BOOT_MAGIC_UNSET,
603 BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
604 warn!("Mismatched trailer for Slot 1 after revert");
605 fails += 1;
606 }
607
608 fails > 0
609}
610
611fn try_random_fails(flashmap: &SimFlashMap, images: &Images,
612 total_ops: i32, count: usize) -> (SimFlashMap, Vec<i32>) {
613 let mut flashmap = flashmap.clone();
614
615 mark_permanent_upgrade(&mut flashmap, &images.slots[1]);
616
617 let mut rng = rand::thread_rng();
618 let mut resets = vec![0i32; count];
619 let mut remaining_ops = total_ops;
620 for i in 0 .. count {
621 let ops = Range::new(1, remaining_ops / 2);
622 let reset_counter = ops.ind_sample(&mut rng);
623 let mut counter = reset_counter;
624 match c::boot_go(&mut flashmap, &images.areadesc, Some(&mut counter), false) {
625 (0, _) | (-0x13579, _) => (),
626 (x, _) => panic!("Unknown return: {}", x),
627 }
628 remaining_ops -= reset_counter;
629 resets[i] = reset_counter;
630 }
631
632 match c::boot_go(&mut flashmap, &images.areadesc, None, false) {
633 (-0x13579, _) => panic!("Should not be have been interrupted!"),
634 (0, _) => (),
635 (x, _) => panic!("Unknown return: {}", x),
636 }
637
638 (flashmap, resets)
639}
640
641/// Show the flash layout.
642#[allow(dead_code)]
643fn show_flash(flash: &dyn Flash) {
644 println!("---- Flash configuration ----");
645 for sector in flash.sector_iter() {
646 println!(" {:3}: 0x{:08x}, 0x{:08x}",
647 sector.num, sector.base, sector.size);
648 }
649 println!("");
650}
651
652/// Install a "program" into the given image. This fakes the image header, or at least all of the
653/// fields used by the given code. Returns a copy of the image that was written.
654pub fn install_image(flashmap: &mut SimFlashMap, slots: &[SlotInfo], slot: usize, len: usize,
655 bad_sig: bool) -> [Option<Vec<u8>>; 2] {
656 let offset = slots[slot].base_off;
657 let slot_len = slots[slot].len;
658 let dev_id = slots[slot].dev_id;
659
660 let mut tlv = make_tlv();
661
662 const HDR_SIZE: usize = 32;
663
664 // Generate a boot header. Note that the size doesn't include the header.
665 let header = ImageHeader {
666 magic: 0x96f3b83d,
667 load_addr: 0,
668 hdr_size: HDR_SIZE as u16,
669 _pad1: 0,
670 img_size: len as u32,
671 flags: tlv.get_flags(),
672 ver: ImageVersion {
673 major: (offset / (128 * 1024)) as u8,
674 minor: 0,
675 revision: 1,
676 build_num: offset as u32,
677 },
678 _pad2: 0,
679 };
680
681 let mut b_header = [0; HDR_SIZE];
682 b_header[..32].clone_from_slice(header.as_raw());
683 assert_eq!(b_header.len(), HDR_SIZE);
684
685 tlv.add_bytes(&b_header);
686
687 // The core of the image itself is just pseudorandom data.
688 let mut b_img = vec![0; len];
689 splat(&mut b_img, offset);
690
691 // TLV signatures work over plain image
692 tlv.add_bytes(&b_img);
693
694 // Generate encrypted images
695 let flag = TlvFlags::ENCRYPTED as u32;
696 let is_encrypted = (tlv.get_flags() & flag) == flag;
697 let mut b_encimg = vec![];
698 if is_encrypted {
699 let key = GenericArray::from_slice(AES_SEC_KEY);
700 let nonce = GenericArray::from_slice(&[0; 16]);
701 let mut cipher = Aes128Ctr::new(&key, &nonce);
702 b_encimg = b_img.clone();
703 cipher.apply_keystream(&mut b_encimg);
704 }
705
706 // Build the TLV itself.
707 let mut b_tlv = if bad_sig {
708 let good_sig = &mut tlv.make_tlv();
709 vec![0; good_sig.len()]
710 } else {
711 tlv.make_tlv()
712 };
713
714 // Pad the block to a flash alignment (8 bytes).
715 while b_tlv.len() % 8 != 0 {
716 //FIXME: should be erase_val?
717 b_tlv.push(0xFF);
718 }
719
720 let mut buf = vec![];
721 buf.append(&mut b_header.to_vec());
722 buf.append(&mut b_img);
723 buf.append(&mut b_tlv.clone());
724
725 let mut encbuf = vec![];
726 if is_encrypted {
727 encbuf.append(&mut b_header.to_vec());
728 encbuf.append(&mut b_encimg);
729 encbuf.append(&mut b_tlv);
730 }
731
732 let result: [Option<Vec<u8>>; 2];
733
734 // Since images are always non-encrypted in slot0, we first write an
735 // encrypted image, re-read to use for verification, erase + flash
736 // un-encrypted. In slot1 the image is written un-encrypted, and if
737 // encryption is requested, it follows an erase + flash encrypted.
738
739 let flash = flashmap.get_mut(&dev_id).unwrap();
740
741 if slot == 0 {
742 let enc_copy: Option<Vec<u8>>;
743
744 if is_encrypted {
745 flash.write(offset, &encbuf).unwrap();
746
747 let mut enc = vec![0u8; encbuf.len()];
748 flash.read(offset, &mut enc).unwrap();
749
750 enc_copy = Some(enc);
751
752 flash.erase(offset, slot_len).unwrap();
753 } else {
754 enc_copy = None;
755 }
756
757 flash.write(offset, &buf).unwrap();
758
759 let mut copy = vec![0u8; buf.len()];
760 flash.read(offset, &mut copy).unwrap();
761
762 result = [Some(copy), enc_copy];
763 } else {
764
765 flash.write(offset, &buf).unwrap();
766
767 let mut copy = vec![0u8; buf.len()];
768 flash.read(offset, &mut copy).unwrap();
769
770 let enc_copy: Option<Vec<u8>>;
771
772 if is_encrypted {
773 flash.erase(offset, slot_len).unwrap();
774
775 flash.write(offset, &encbuf).unwrap();
776
777 let mut enc = vec![0u8; encbuf.len()];
778 flash.read(offset, &mut enc).unwrap();
779
780 enc_copy = Some(enc);
781 } else {
782 enc_copy = None;
783 }
784
785 result = [Some(copy), enc_copy];
786 }
787
788 result
789}
790
791#[cfg(feature = "sig-rsa")]
792#[cfg(feature = "enc-kw")]
793fn make_tlv() -> TlvGen {
794 TlvGen::new_rsa_kw()
795}
796
797#[cfg(feature = "sig-rsa")]
798#[cfg(not(feature = "enc-rsa"))]
799#[cfg(not(feature = "enc-kw"))]
800fn make_tlv() -> TlvGen {
801 TlvGen::new_rsa_pss()
802}
803
804#[cfg(feature = "sig-ecdsa")]
805#[cfg(feature = "enc-kw")]
806fn make_tlv() -> TlvGen {
807 TlvGen::new_ecdsa_kw()
808}
809
810#[cfg(feature = "sig-ecdsa")]
811#[cfg(not(feature = "enc-kw"))]
812fn make_tlv() -> TlvGen {
813 TlvGen::new_ecdsa()
814}
815
816#[cfg(not(feature = "sig-rsa"))]
817#[cfg(feature = "enc-rsa")]
818fn make_tlv() -> TlvGen {
819 TlvGen::new_enc_rsa()
820}
821
822#[cfg(feature = "sig-rsa")]
823#[cfg(feature = "enc-rsa")]
824fn make_tlv() -> TlvGen {
825 TlvGen::new_sig_enc_rsa()
826}
827
828#[cfg(not(feature = "sig-rsa"))]
829#[cfg(not(feature = "sig-ecdsa"))]
830#[cfg(feature = "enc-kw")]
831fn make_tlv() -> TlvGen {
832 TlvGen::new_enc_kw()
833}
834
835#[cfg(not(feature = "sig-rsa"))]
836#[cfg(not(feature = "sig-ecdsa"))]
837#[cfg(not(feature = "enc-rsa"))]
838#[cfg(not(feature = "enc-kw"))]
839fn make_tlv() -> TlvGen {
840 TlvGen::new_hash_only()
841}
842
843#[cfg(feature = "enc-rsa")]
844fn find_image(images: &[Option<Vec<u8>>; 2], slot: usize) -> &Vec<u8> {
845 match &images[slot] {
846 Some(image) => return image,
847 None => panic!("Invalid image"),
848 }
849}
850
851#[cfg(feature = "enc-kw")]
852fn find_image(images: &[Option<Vec<u8>>; 2], slot: usize) -> &Vec<u8> {
853 match &images[slot] {
854 Some(image) => return image,
855 None => panic!("Invalid image"),
856 }
857}
858
859#[cfg(not(feature = "enc-rsa"))]
860#[cfg(not(feature = "enc-kw"))]
861fn find_image(images: &[Option<Vec<u8>>; 2], _slot: usize) -> &Vec<u8> {
862 match &images[0] {
863 Some(image) => return image,
864 None => panic!("Invalid image"),
865 }
866}
867
868/// Verify that given image is present in the flash at the given offset.
869fn verify_image(flashmap: &SimFlashMap, slots: &[SlotInfo], slot: usize,
870 images: &[Option<Vec<u8>>; 2]) -> bool {
871 let image = find_image(images, slot);
872 let buf = image.as_slice();
873 let dev_id = slots[slot].dev_id;
874
875 let mut copy = vec![0u8; buf.len()];
876 let offset = slots[slot].base_off;
877 let flash = flashmap.get(&dev_id).unwrap();
878 flash.read(offset, &mut copy).unwrap();
879
880 if buf != &copy[..] {
881 for i in 0 .. buf.len() {
882 if buf[i] != copy[i] {
883 info!("First failure for slot{} at {:#x} {:#x}!={:#x}",
884 slot, offset + i, buf[i], copy[i]);
885 break;
886 }
887 }
888 false
889 } else {
890 true
891 }
892}
893
894#[cfg(feature = "overwrite-only")]
895#[allow(unused_variables)]
896// overwrite-only doesn't employ trailer management
897fn verify_trailer(flashmap: &SimFlashMap, slots: &[SlotInfo], slot: usize,
898 magic: Option<u8>, image_ok: Option<u8>,
899 copy_done: Option<u8>) -> bool {
900 true
901}
902
903#[cfg(not(feature = "overwrite-only"))]
904fn verify_trailer(flashmap: &SimFlashMap, slots: &[SlotInfo], slot: usize,
905 magic: Option<u8>, image_ok: Option<u8>,
906 copy_done: Option<u8>) -> bool {
907 let offset = slots[slot].trailer_off;
908 let dev_id = slots[slot].dev_id;
909 let mut copy = vec![0u8; c::boot_magic_sz() + c::boot_max_align() * 2];
910 let mut failed = false;
911
912 let flash = flashmap.get(&dev_id).unwrap();
913 let erased_val = flash.erased_val();
914 flash.read(offset, &mut copy).unwrap();
915
916 failed |= match magic {
917 Some(v) => {
918 if v == 1 && &copy[16..] != MAGIC.unwrap() {
919 warn!("\"magic\" mismatch at {:#x}", offset);
920 true
921 } else if v == 3 {
922 let expected = [erased_val; 16];
923 if &copy[16..] != expected {
924 warn!("\"magic\" mismatch at {:#x}", offset);
925 true
926 } else {
927 false
928 }
929 } else {
930 false
931 }
932 },
933 None => false,
934 };
935
936 failed |= match image_ok {
937 Some(v) => {
938 if (v == 1 && copy[8] != v) || (v == 3 && copy[8] != erased_val) {
939 warn!("\"image_ok\" mismatch at {:#x} v={} val={:#x}", offset, v, copy[8]);
940 true
941 } else {
942 false
943 }
944 },
945 None => false,
946 };
947
948 failed |= match copy_done {
949 Some(v) => {
950 if (v == 1 && copy[0] != v) || (v == 3 && copy[0] != erased_val) {
951 warn!("\"copy_done\" mismatch at {:#x} v={} val={:#x}", offset, v, copy[0]);
952 true
953 } else {
954 false
955 }
956 },
957 None => false,
958 };
959
960 !failed
961}
962
963/// The image header
964#[repr(C)]
965pub struct ImageHeader {
966 magic: u32,
967 load_addr: u32,
968 hdr_size: u16,
969 _pad1: u16,
970 img_size: u32,
971 flags: u32,
972 ver: ImageVersion,
973 _pad2: u32,
974}
975
976impl AsRaw for ImageHeader {}
977
978#[repr(C)]
979pub struct ImageVersion {
980 major: u8,
981 minor: u8,
982 revision: u16,
983 build_num: u32,
984}
985
986#[derive(Clone)]
987pub struct SlotInfo {
988 pub base_off: usize,
989 pub trailer_off: usize,
990 pub len: usize,
991 pub dev_id: u8,
992}
993
994pub struct Images {
995 pub flashmap: SimFlashMap,
996 pub areadesc: AreaDesc,
997 pub slots: [SlotInfo; 2],
998 pub primaries: [Option<Vec<u8>>; 2],
999 pub upgrades: [Option<Vec<u8>>; 2],
1000 pub total_count: Option<i32>,
1001}
1002
1003const MAGIC: Option<&[u8]> = Some(&[0x77, 0xc2, 0x95, 0xf3,
1004 0x60, 0xd2, 0xef, 0x7f,
1005 0x35, 0x52, 0x50, 0x0f,
1006 0x2c, 0xb6, 0x79, 0x80]);
1007
1008// Replicates defines found in bootutil.h
1009const BOOT_MAGIC_GOOD: Option<u8> = Some(1);
1010const BOOT_MAGIC_UNSET: Option<u8> = Some(3);
1011
1012const BOOT_FLAG_SET: Option<u8> = Some(1);
1013const BOOT_FLAG_UNSET: Option<u8> = Some(3);
1014
1015/// Write out the magic so that the loader tries doing an upgrade.
1016pub fn mark_upgrade(flashmap: &mut SimFlashMap, slot: &SlotInfo) {
1017 let flash = flashmap.get_mut(&slot.dev_id).unwrap();
1018 let offset = slot.trailer_off + c::boot_max_align() * 2;
1019 flash.write(offset, MAGIC.unwrap()).unwrap();
1020}
1021
1022/// Writes the image_ok flag which, guess what, tells the bootloader
1023/// the this image is ok (not a test, and no revert is to be performed).
1024fn mark_permanent_upgrade(flashmap: &mut SimFlashMap, slot: &SlotInfo) {
1025 let flash = flashmap.get_mut(&slot.dev_id).unwrap();
1026 let mut ok = [flash.erased_val(); 8];
1027 ok[0] = 1u8;
1028 let off = slot.trailer_off + c::boot_max_align();
1029 let align = flash.align();
1030 flash.write(off, &ok[..align]).unwrap();
1031}
1032
1033// Drop some pseudo-random gibberish onto the data.
1034fn splat(data: &mut [u8], seed: usize) {
1035 let seed_block = [0x135782ea, 0x92184728, data.len() as u32, seed as u32];
1036 let mut rng: XorShiftRng = SeedableRng::from_seed(seed_block);
1037 rng.fill_bytes(data);
1038}
1039
1040/// Return a read-only view into the raw bytes of this object
1041trait AsRaw : Sized {
1042 fn as_raw<'a>(&'a self) -> &'a [u8] {
1043 unsafe { slice::from_raw_parts(self as *const _ as *const u8,
1044 mem::size_of::<Self>()) }
1045 }
1046}
1047
1048pub fn show_sizes() {
1049 // This isn't panic safe.
1050 for min in &[1, 2, 4, 8] {
1051 let msize = c::boot_trailer_sz(*min);
1052 println!("{:2}: {} (0x{:x})", min, msize, msize);
1053 }
1054}