blob: 4c6f0695ec616d042225ff308ddfe912dc2c95c5 [file] [log] [blame]
David Brown2639e072017-10-11 11:18:44 -06001#[macro_use] extern crate log;
2extern crate ring;
3extern crate env_logger;
4extern crate docopt;
5extern crate libc;
6extern crate pem;
7extern crate rand;
8#[macro_use] extern crate serde_derive;
9extern crate serde;
10extern crate simflash;
11extern crate untrusted;
12extern crate mcuboot_sys;
13
14use docopt::Docopt;
15use rand::{Rng, SeedableRng, XorShiftRng};
16use rand::distributions::{IndependentSample, Range};
17use std::fmt;
18use std::mem;
19use std::process;
20use std::slice;
21
22mod caps;
23mod tlv;
David Brownca7b5d32017-11-03 08:37:38 -060024pub mod testlog;
David Brown2639e072017-10-11 11:18:44 -060025
26use simflash::{Flash, SimFlash};
27use mcuboot_sys::{c, AreaDesc, FlashId};
28use caps::Caps;
29use tlv::TlvGen;
30
31const USAGE: &'static str = "
32Mcuboot simulator
33
34Usage:
35 bootsim sizes
36 bootsim run --device TYPE [--align SIZE]
37 bootsim runall
38 bootsim (--help | --version)
39
40Options:
41 -h, --help Show this message
42 --version Version
43 --device TYPE MCU to simulate
44 Valid values: stm32f4, k64f
45 --align SIZE Flash write alignment
46";
47
48#[derive(Debug, Deserialize)]
49struct Args {
50 flag_help: bool,
51 flag_version: bool,
52 flag_device: Option<DeviceName>,
53 flag_align: Option<AlignArg>,
54 cmd_sizes: bool,
55 cmd_run: bool,
56 cmd_runall: bool,
57}
58
59#[derive(Copy, Clone, Debug, Deserialize)]
David Browndecbd042017-10-19 10:43:17 -060060pub enum DeviceName { Stm32f4, K64f, K64fBig, Nrf52840 }
David Brown2639e072017-10-11 11:18:44 -060061
David Browndd2b1182017-11-02 15:39:21 -060062pub static ALL_DEVICES: &'static [DeviceName] = &[
David Brown2639e072017-10-11 11:18:44 -060063 DeviceName::Stm32f4,
64 DeviceName::K64f,
65 DeviceName::K64fBig,
66 DeviceName::Nrf52840,
67];
68
69impl fmt::Display for DeviceName {
70 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
71 let name = match *self {
72 DeviceName::Stm32f4 => "stm32f4",
73 DeviceName::K64f => "k64f",
74 DeviceName::K64fBig => "k64fbig",
75 DeviceName::Nrf52840 => "nrf52840",
76 };
77 f.write_str(name)
78 }
79}
80
81#[derive(Debug)]
82struct AlignArg(u8);
83
84struct AlignArgVisitor;
85
86impl<'de> serde::de::Visitor<'de> for AlignArgVisitor {
87 type Value = AlignArg;
88
89 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
90 formatter.write_str("1, 2, 4 or 8")
91 }
92
93 fn visit_u8<E>(self, n: u8) -> Result<Self::Value, E>
94 where E: serde::de::Error
95 {
96 Ok(match n {
97 1 | 2 | 4 | 8 => AlignArg(n),
98 n => {
99 let err = format!("Could not deserialize '{}' as alignment", n);
100 return Err(E::custom(err));
101 }
102 })
103 }
104}
105
106impl<'de> serde::de::Deserialize<'de> for AlignArg {
107 fn deserialize<D>(d: D) -> Result<AlignArg, D::Error>
108 where D: serde::de::Deserializer<'de>
109 {
110 d.deserialize_u8(AlignArgVisitor)
111 }
112}
113
114pub fn main() {
115 let args: Args = Docopt::new(USAGE)
116 .and_then(|d| d.deserialize())
117 .unwrap_or_else(|e| e.exit());
118 // println!("args: {:#?}", args);
119
120 if args.cmd_sizes {
121 show_sizes();
122 return;
123 }
124
125 let mut status = RunStatus::new();
126 if args.cmd_run {
127
128 let align = args.flag_align.map(|x| x.0).unwrap_or(1);
129
130
131 let device = match args.flag_device {
132 None => panic!("Missing mandatory device argument"),
133 Some(dev) => dev,
134 };
135
136 status.run_single(device, align);
137 }
138
139 if args.cmd_runall {
140 for &dev in ALL_DEVICES {
141 for &align in &[1, 2, 4, 8] {
142 status.run_single(dev, align);
143 }
144 }
145 }
146
147 if status.failures > 0 {
148 error!("{} Tests ran with {} failures", status.failures + status.passes, status.failures);
149 process::exit(1);
150 } else {
151 error!("{} Tests ran successfully", status.passes);
152 process::exit(0);
153 }
154}
155
David Browndb9a3952017-11-06 13:16:15 -0700156/// A test run, intended to be run from "cargo test", so panics on failure.
157pub struct Run {
158 flash: SimFlash,
159 areadesc: AreaDesc,
160 slots: [SlotInfo; 2],
161 align: u8,
162}
163
164impl Run {
165 pub fn new(device: DeviceName, align: u8) -> Run {
166 let (flash, areadesc) = make_device(device, align);
167
168 let (slot0_base, slot0_len) = areadesc.find(FlashId::Image0);
169 let (slot1_base, slot1_len) = areadesc.find(FlashId::Image1);
170 let (scratch_base, _) = areadesc.find(FlashId::ImageScratch);
171
172 // The code assumes that the slots are consecutive.
173 assert_eq!(slot1_base, slot0_base + slot0_len);
174 assert_eq!(scratch_base, slot1_base + slot1_len);
175
176 let offset_from_end = c::boot_magic_sz() + c::boot_max_align() * 2;
177
178 // Construct a primary image.
179 let slot0 = SlotInfo {
180 base_off: slot0_base as usize,
181 trailer_off: slot1_base - offset_from_end,
182 };
183
184 // And an upgrade image.
185 let slot1 = SlotInfo {
186 base_off: slot1_base as usize,
187 trailer_off: scratch_base - offset_from_end,
188 };
189
190 Run {
191 flash: flash,
192 areadesc: areadesc,
193 slots: [slot0, slot1],
194 align: align,
195 }
196 }
197
198 pub fn each_device<F>(f: F)
199 where F: Fn(&mut Run)
200 {
201 for &dev in ALL_DEVICES {
202 for &align in &[1, 2, 4, 8] {
203 let mut run = Run::new(dev, align);
204 f(&mut run);
205 }
206 }
207 }
208}
209
David Browndd2b1182017-11-02 15:39:21 -0600210pub struct RunStatus {
David Brown2639e072017-10-11 11:18:44 -0600211 failures: usize,
212 passes: usize,
213}
214
215impl RunStatus {
David Browndd2b1182017-11-02 15:39:21 -0600216 pub fn new() -> RunStatus {
David Brown2639e072017-10-11 11:18:44 -0600217 RunStatus {
218 failures: 0,
219 passes: 0,
220 }
221 }
222
David Browndd2b1182017-11-02 15:39:21 -0600223 pub fn run_single(&mut self, device: DeviceName, align: u8) {
David Brown2639e072017-10-11 11:18:44 -0600224 warn!("Running on device {} with alignment {}", device, align);
225
David Browndb9a3952017-11-06 13:16:15 -0700226 let mut run = Run::new(device, align);
David Brown2639e072017-10-11 11:18:44 -0600227
David Brown2639e072017-10-11 11:18:44 -0600228 let mut failed = false;
229
230 // Creates a badly signed image in slot1 to check that it is not
231 // upgraded to
David Browndb9a3952017-11-06 13:16:15 -0700232 let mut bad_flash = run.flash.clone();
David Brown2639e072017-10-11 11:18:44 -0600233 let bad_slot1_image = Images {
David Browndb9a3952017-11-06 13:16:15 -0700234 slot0: run.slots[0].clone(),
235 slot1: run.slots[1].clone(),
236 primary: install_image(&mut bad_flash, run.slots[0].base_off, 32784, false),
237 upgrade: install_image(&mut bad_flash, run.slots[1].base_off, 41928, true),
David Brown541860c2017-11-06 11:25:42 -0700238 align: align,
David Brown2639e072017-10-11 11:18:44 -0600239 };
240
David Browndb9a3952017-11-06 13:16:15 -0700241 failed |= run_signfail_upgrade(&bad_flash, &run.areadesc, &bad_slot1_image);
David Brown2639e072017-10-11 11:18:44 -0600242
243 let images = Images {
David Browndb9a3952017-11-06 13:16:15 -0700244 slot0: run.slots[0].clone(),
245 slot1: run.slots[1].clone(),
246 primary: install_image(&mut run.flash, run.slots[0].base_off, 32784, false),
247 upgrade: install_image(&mut run.flash, run.slots[1].base_off, 41928, false),
David Brown541860c2017-11-06 11:25:42 -0700248 align: align,
David Brown2639e072017-10-11 11:18:44 -0600249 };
250
David Browndb9a3952017-11-06 13:16:15 -0700251 failed |= run_norevert_newimage(&run.flash, &run.areadesc, &images);
David Brown2639e072017-10-11 11:18:44 -0600252
David Browndb9a3952017-11-06 13:16:15 -0700253 mark_upgrade(&mut run.flash, &images.slot1);
David Brown2639e072017-10-11 11:18:44 -0600254
255 // upgrades without fails, counts number of flash operations
David Browndb9a3952017-11-06 13:16:15 -0700256 let total_count = match run_basic_upgrade(&run.flash, &run.areadesc, &images) {
David Brown2639e072017-10-11 11:18:44 -0600257 Ok(v) => v,
258 Err(_) => {
259 self.failures += 1;
260 return;
261 },
262 };
263
David Browndb9a3952017-11-06 13:16:15 -0700264 failed |= run_basic_revert(&run.flash, &run.areadesc, &images);
265 failed |= run_revert_with_fails(&run.flash, &run.areadesc, &images, total_count);
266 failed |= run_perm_with_fails(&run.flash, &run.areadesc, &images, total_count);
267 failed |= run_perm_with_random_fails(&run.flash, &run.areadesc, &images,
David Brown2639e072017-10-11 11:18:44 -0600268 total_count, 5);
David Browndb9a3952017-11-06 13:16:15 -0700269 failed |= run_norevert(&run.flash, &run.areadesc, &images);
David Brown2639e072017-10-11 11:18:44 -0600270
271 //show_flash(&flash);
272
273 if failed {
274 self.failures += 1;
275 } else {
276 self.passes += 1;
277 }
278 }
David Browndd2b1182017-11-02 15:39:21 -0600279
280 pub fn failures(&self) -> usize {
281 self.failures
282 }
David Brown2639e072017-10-11 11:18:44 -0600283}
284
David Browndecbd042017-10-19 10:43:17 -0600285/// Build the Flash and area descriptor for a given device.
286pub fn make_device(device: DeviceName, align: u8) -> (SimFlash, AreaDesc) {
287 match device {
288 DeviceName::Stm32f4 => {
289 // STM style flash. Large sectors, with a large scratch area.
290 let flash = SimFlash::new(vec![16 * 1024, 16 * 1024, 16 * 1024, 16 * 1024,
291 64 * 1024,
292 128 * 1024, 128 * 1024, 128 * 1024],
293 align as usize);
294 let mut areadesc = AreaDesc::new(&flash);
295 areadesc.add_image(0x020000, 0x020000, FlashId::Image0);
296 areadesc.add_image(0x040000, 0x020000, FlashId::Image1);
297 areadesc.add_image(0x060000, 0x020000, FlashId::ImageScratch);
298 (flash, areadesc)
299 }
300 DeviceName::K64f => {
301 // NXP style flash. Small sectors, one small sector for scratch.
302 let flash = SimFlash::new(vec![4096; 128], align as usize);
303
304 let mut areadesc = AreaDesc::new(&flash);
305 areadesc.add_image(0x020000, 0x020000, FlashId::Image0);
306 areadesc.add_image(0x040000, 0x020000, FlashId::Image1);
307 areadesc.add_image(0x060000, 0x001000, FlashId::ImageScratch);
308 (flash, areadesc)
309 }
310 DeviceName::K64fBig => {
311 // Simulating an STM style flash on top of an NXP style flash. Underlying flash device
312 // uses small sectors, but we tell the bootloader they are large.
313 let flash = SimFlash::new(vec![4096; 128], align as usize);
314
315 let mut areadesc = AreaDesc::new(&flash);
316 areadesc.add_simple_image(0x020000, 0x020000, FlashId::Image0);
317 areadesc.add_simple_image(0x040000, 0x020000, FlashId::Image1);
318 areadesc.add_simple_image(0x060000, 0x020000, FlashId::ImageScratch);
319 (flash, areadesc)
320 }
321 DeviceName::Nrf52840 => {
322 // Simulating the flash on the nrf52840 with partitions set up so that the scratch size
323 // does not divide into the image size.
324 let flash = SimFlash::new(vec![4096; 128], align as usize);
325
326 let mut areadesc = AreaDesc::new(&flash);
327 areadesc.add_image(0x008000, 0x034000, FlashId::Image0);
328 areadesc.add_image(0x03c000, 0x034000, FlashId::Image1);
329 areadesc.add_image(0x070000, 0x00d000, FlashId::ImageScratch);
330 (flash, areadesc)
331 }
332 }
333}
334
David Brown2639e072017-10-11 11:18:44 -0600335/// A simple upgrade without forced failures.
336///
337/// Returns the number of flash operations which can later be used to
338/// inject failures at chosen steps.
339fn run_basic_upgrade(flash: &SimFlash, areadesc: &AreaDesc, images: &Images)
340 -> Result<i32, ()> {
341 let (fl, total_count) = try_upgrade(&flash, &areadesc, &images, None);
342 info!("Total flash operation count={}", total_count);
343
344 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
345 warn!("Image mismatch after first boot");
346 Err(())
347 } else {
348 Ok(total_count)
349 }
350}
351
352#[cfg(feature = "overwrite-only")]
353#[allow(unused_variables)]
354fn run_basic_revert(flash: &SimFlash, areadesc: &AreaDesc, images: &Images) -> bool {
355 false
356}
357
358#[cfg(not(feature = "overwrite-only"))]
359fn run_basic_revert(flash: &SimFlash, areadesc: &AreaDesc, images: &Images) -> bool {
360 let mut fails = 0;
361
362 // FIXME: this test would also pass if no swap is ever performed???
363 if Caps::SwapUpgrade.present() {
364 for count in 2 .. 5 {
365 info!("Try revert: {}", count);
David Brown541860c2017-11-06 11:25:42 -0700366 let fl = try_revert(&flash, &areadesc, count, images.align);
David Brown2639e072017-10-11 11:18:44 -0600367 if !verify_image(&fl, images.slot0.base_off, &images.primary) {
368 error!("Revert failure on count {}", count);
369 fails += 1;
370 }
371 }
372 }
373
374 fails > 0
375}
376
377fn run_perm_with_fails(flash: &SimFlash, areadesc: &AreaDesc, images: &Images,
378 total_flash_ops: i32) -> bool {
379 let mut fails = 0;
380
381 // Let's try an image halfway through.
382 for i in 1 .. total_flash_ops {
383 info!("Try interruption at {}", i);
384 let (fl, count) = try_upgrade(&flash, &areadesc, &images, Some(i));
385 info!("Second boot, count={}", count);
386 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
387 warn!("FAIL at step {} of {}", i, total_flash_ops);
388 fails += 1;
389 }
390
391 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
392 COPY_DONE) {
393 warn!("Mismatched trailer for Slot 0");
394 fails += 1;
395 }
396
397 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
398 UNSET) {
399 warn!("Mismatched trailer for Slot 1");
400 fails += 1;
401 }
402
403 if Caps::SwapUpgrade.present() {
404 if !verify_image(&fl, images.slot1.base_off, &images.primary) {
405 warn!("Slot 1 FAIL at step {} of {}", i, total_flash_ops);
406 fails += 1;
407 }
408 }
409 }
410
411 if fails > 0 {
412 error!("{} out of {} failed {:.2}%", fails, total_flash_ops,
413 fails as f32 * 100.0 / total_flash_ops as f32);
414 }
415
416 fails > 0
417}
418
419fn run_perm_with_random_fails(flash: &SimFlash, areadesc: &AreaDesc,
420 images: &Images, total_flash_ops: i32,
421 total_fails: usize) -> bool {
422 let mut fails = 0;
423 let (fl, total_counts) = try_random_fails(&flash, &areadesc, &images,
424 total_flash_ops, total_fails);
425 info!("Random interruptions at reset points={:?}", total_counts);
426
427 let slot0_ok = verify_image(&fl, images.slot0.base_off, &images.upgrade);
428 let slot1_ok = if Caps::SwapUpgrade.present() {
429 verify_image(&fl, images.slot1.base_off, &images.primary)
430 } else {
431 true
432 };
433 if !slot0_ok || !slot1_ok {
434 error!("Image mismatch after random interrupts: slot0={} slot1={}",
435 if slot0_ok { "ok" } else { "fail" },
436 if slot1_ok { "ok" } else { "fail" });
437 fails += 1;
438 }
439 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
440 COPY_DONE) {
441 error!("Mismatched trailer for Slot 0");
442 fails += 1;
443 }
444 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
445 UNSET) {
446 error!("Mismatched trailer for Slot 1");
447 fails += 1;
448 }
449
450 if fails > 0 {
451 error!("Error testing perm upgrade with {} fails", total_fails);
452 }
453
454 fails > 0
455}
456
457#[cfg(feature = "overwrite-only")]
458#[allow(unused_variables)]
459fn run_revert_with_fails(flash: &SimFlash, areadesc: &AreaDesc, images: &Images,
460 total_count: i32) -> bool {
461 false
462}
463
464#[cfg(not(feature = "overwrite-only"))]
465fn run_revert_with_fails(flash: &SimFlash, areadesc: &AreaDesc, images: &Images,
466 total_count: i32) -> bool {
467 let mut fails = 0;
468
469 if Caps::SwapUpgrade.present() {
470 for i in 1 .. (total_count - 1) {
471 info!("Try interruption at {}", i);
472 if try_revert_with_fail_at(&flash, &areadesc, &images, i) {
473 error!("Revert failed at interruption {}", i);
474 fails += 1;
475 }
476 }
477 }
478
479 fails > 0
480}
481
482#[cfg(feature = "overwrite-only")]
483#[allow(unused_variables)]
484fn run_norevert(flash: &SimFlash, areadesc: &AreaDesc, images: &Images) -> bool {
485 false
486}
487
488#[cfg(not(feature = "overwrite-only"))]
489fn run_norevert(flash: &SimFlash, areadesc: &AreaDesc, images: &Images) -> bool {
490 let mut fl = flash.clone();
491 let mut fails = 0;
492
493 info!("Try norevert");
David Brown2639e072017-10-11 11:18:44 -0600494
495 // First do a normal upgrade...
David Brown541860c2017-11-06 11:25:42 -0700496 if c::boot_go(&mut fl, &areadesc, None, images.align) != 0 {
David Brown2639e072017-10-11 11:18:44 -0600497 warn!("Failed first boot");
498 fails += 1;
499 }
500
501 //FIXME: copy_done is written by boot_go, is it ok if no copy
502 // was ever done?
503
504 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
505 warn!("Slot 0 image verification FAIL");
506 fails += 1;
507 }
508 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, UNSET,
509 COPY_DONE) {
510 warn!("Mismatched trailer for Slot 0");
511 fails += 1;
512 }
513 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
514 UNSET) {
515 warn!("Mismatched trailer for Slot 1");
516 fails += 1;
517 }
518
519 // Marks image in slot0 as permanent, no revert should happen...
David Brown541860c2017-11-06 11:25:42 -0700520 mark_permanent_upgrade(&mut fl, &images.slot0, images.align);
David Brown2639e072017-10-11 11:18:44 -0600521
522 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
523 COPY_DONE) {
524 warn!("Mismatched trailer for Slot 0");
525 fails += 1;
526 }
527
David Brown541860c2017-11-06 11:25:42 -0700528 if c::boot_go(&mut fl, &areadesc, None, images.align) != 0 {
David Brown2639e072017-10-11 11:18:44 -0600529 warn!("Failed second boot");
530 fails += 1;
531 }
532
533 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
534 COPY_DONE) {
535 warn!("Mismatched trailer for Slot 0");
536 fails += 1;
537 }
538 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
539 warn!("Failed image verification");
540 fails += 1;
541 }
542
543 if fails > 0 {
544 error!("Error running upgrade without revert");
545 }
546
547 fails > 0
548}
549
550// Tests a new image written to slot0 that already has magic and image_ok set
551// while there is no image on slot1, so no revert should ever happen...
552fn run_norevert_newimage(flash: &SimFlash, areadesc: &AreaDesc,
553 images: &Images) -> bool {
554 let mut fl = flash.clone();
555 let mut fails = 0;
556
557 info!("Try non-revert on imgtool generated image");
David Brown2639e072017-10-11 11:18:44 -0600558
559 mark_upgrade(&mut fl, &images.slot0);
560
561 // This simulates writing an image created by imgtool to Slot 0
562 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, UNSET, UNSET) {
563 warn!("Mismatched trailer for Slot 0");
564 fails += 1;
565 }
566
567 // Run the bootloader...
David Brown541860c2017-11-06 11:25:42 -0700568 if c::boot_go(&mut fl, &areadesc, None, images.align) != 0 {
David Brown2639e072017-10-11 11:18:44 -0600569 warn!("Failed first boot");
570 fails += 1;
571 }
572
573 // State should not have changed
574 if !verify_image(&fl, images.slot0.base_off, &images.primary) {
575 warn!("Failed image verification");
576 fails += 1;
577 }
578 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, UNSET,
579 UNSET) {
580 warn!("Mismatched trailer for Slot 0");
581 fails += 1;
582 }
583 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
584 UNSET) {
585 warn!("Mismatched trailer for Slot 1");
586 fails += 1;
587 }
588
589 if fails > 0 {
590 error!("Expected a non revert with new image");
591 }
592
593 fails > 0
594}
595
596// Tests a new image written to slot0 that already has magic and image_ok set
597// while there is no image on slot1, so no revert should ever happen...
598fn run_signfail_upgrade(flash: &SimFlash, areadesc: &AreaDesc,
599 images: &Images) -> bool {
600 let mut fl = flash.clone();
601 let mut fails = 0;
602
603 info!("Try upgrade image with bad signature");
David Brown2639e072017-10-11 11:18:44 -0600604
605 mark_upgrade(&mut fl, &images.slot0);
David Brown541860c2017-11-06 11:25:42 -0700606 mark_permanent_upgrade(&mut fl, &images.slot0, images.align);
David Brown2639e072017-10-11 11:18:44 -0600607 mark_upgrade(&mut fl, &images.slot1);
608
609 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
610 UNSET) {
611 warn!("Mismatched trailer for Slot 0");
612 fails += 1;
613 }
614
615 // Run the bootloader...
David Brown541860c2017-11-06 11:25:42 -0700616 if c::boot_go(&mut fl, &areadesc, None, images.align) != 0 {
David Brown2639e072017-10-11 11:18:44 -0600617 warn!("Failed first boot");
618 fails += 1;
619 }
620
621 // State should not have changed
622 if !verify_image(&fl, images.slot0.base_off, &images.primary) {
623 warn!("Failed image verification");
624 fails += 1;
625 }
626 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
627 UNSET) {
628 warn!("Mismatched trailer for Slot 0");
629 fails += 1;
630 }
631
632 if fails > 0 {
633 error!("Expected an upgrade failure when image has bad signature");
634 }
635
636 fails > 0
637}
638
639/// Test a boot, optionally stopping after 'n' flash options. Returns a count
640/// of the number of flash operations done total.
641fn try_upgrade(flash: &SimFlash, areadesc: &AreaDesc, images: &Images,
642 stop: Option<i32>) -> (SimFlash, i32) {
643 // Clone the flash to have a new copy.
644 let mut fl = flash.clone();
645
David Brown541860c2017-11-06 11:25:42 -0700646 mark_permanent_upgrade(&mut fl, &images.slot1, images.align);
David Brown2639e072017-10-11 11:18:44 -0600647
David Brownee61c832017-11-06 11:13:25 -0700648 let mut counter = stop.unwrap_or(0);
649
David Brown541860c2017-11-06 11:25:42 -0700650 let (first_interrupted, count) = match c::boot_go(&mut fl, &areadesc, Some(&mut counter), images.align) {
David Brown2639e072017-10-11 11:18:44 -0600651 -0x13579 => (true, stop.unwrap()),
David Brownee61c832017-11-06 11:13:25 -0700652 0 => (false, -counter),
David Brown2639e072017-10-11 11:18:44 -0600653 x => panic!("Unknown return: {}", x),
654 };
David Brown2639e072017-10-11 11:18:44 -0600655
David Brownee61c832017-11-06 11:13:25 -0700656 counter = 0;
David Brown2639e072017-10-11 11:18:44 -0600657 if first_interrupted {
658 // fl.dump();
David Brown541860c2017-11-06 11:25:42 -0700659 match c::boot_go(&mut fl, &areadesc, Some(&mut counter), images.align) {
David Brown2639e072017-10-11 11:18:44 -0600660 -0x13579 => panic!("Shouldn't stop again"),
661 0 => (),
662 x => panic!("Unknown return: {}", x),
663 }
664 }
665
David Brownee61c832017-11-06 11:13:25 -0700666 (fl, count - counter)
David Brown2639e072017-10-11 11:18:44 -0600667}
668
669#[cfg(not(feature = "overwrite-only"))]
David Brown541860c2017-11-06 11:25:42 -0700670fn try_revert(flash: &SimFlash, areadesc: &AreaDesc, count: usize, align: u8) -> SimFlash {
David Brown2639e072017-10-11 11:18:44 -0600671 let mut fl = flash.clone();
David Brown2639e072017-10-11 11:18:44 -0600672
673 // fl.write_file("image0.bin").unwrap();
674 for i in 0 .. count {
675 info!("Running boot pass {}", i + 1);
David Brown541860c2017-11-06 11:25:42 -0700676 assert_eq!(c::boot_go(&mut fl, &areadesc, None, align), 0);
David Brown2639e072017-10-11 11:18:44 -0600677 }
678 fl
679}
680
681#[cfg(not(feature = "overwrite-only"))]
682fn try_revert_with_fail_at(flash: &SimFlash, areadesc: &AreaDesc, images: &Images,
683 stop: i32) -> bool {
684 let mut fl = flash.clone();
685 let mut x: i32;
686 let mut fails = 0;
687
David Brownee61c832017-11-06 11:13:25 -0700688 let mut counter = stop;
David Brown541860c2017-11-06 11:25:42 -0700689 x = c::boot_go(&mut fl, &areadesc, Some(&mut counter), images.align);
David Brown2639e072017-10-11 11:18:44 -0600690 if x != -0x13579 {
691 warn!("Should have stopped at interruption point");
692 fails += 1;
693 }
694
695 if !verify_trailer(&fl, images.slot0.trailer_off, None, None, UNSET) {
696 warn!("copy_done should be unset");
697 fails += 1;
698 }
699
David Brown541860c2017-11-06 11:25:42 -0700700 x = c::boot_go(&mut fl, &areadesc, None, images.align);
David Brown2639e072017-10-11 11:18:44 -0600701 if x != 0 {
702 warn!("Should have finished upgrade");
703 fails += 1;
704 }
705
706 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
707 warn!("Image in slot 0 before revert is invalid at stop={}", stop);
708 fails += 1;
709 }
710 if !verify_image(&fl, images.slot1.base_off, &images.primary) {
711 warn!("Image in slot 1 before revert is invalid at stop={}", stop);
712 fails += 1;
713 }
714 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, UNSET,
715 COPY_DONE) {
716 warn!("Mismatched trailer for Slot 0 before revert");
717 fails += 1;
718 }
719 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
720 UNSET) {
721 warn!("Mismatched trailer for Slot 1 before revert");
722 fails += 1;
723 }
724
725 // Do Revert
David Brown541860c2017-11-06 11:25:42 -0700726 x = c::boot_go(&mut fl, &areadesc, None, images.align);
David Brown2639e072017-10-11 11:18:44 -0600727 if x != 0 {
728 warn!("Should have finished a revert");
729 fails += 1;
730 }
731
732 if !verify_image(&fl, images.slot0.base_off, &images.primary) {
733 warn!("Image in slot 0 after revert is invalid at stop={}", stop);
734 fails += 1;
735 }
736 if !verify_image(&fl, images.slot1.base_off, &images.upgrade) {
737 warn!("Image in slot 1 after revert is invalid at stop={}", stop);
738 fails += 1;
739 }
740 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
741 COPY_DONE) {
742 warn!("Mismatched trailer for Slot 1 after revert");
743 fails += 1;
744 }
745 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
746 UNSET) {
747 warn!("Mismatched trailer for Slot 1 after revert");
748 fails += 1;
749 }
750
751 fails > 0
752}
753
754fn try_random_fails(flash: &SimFlash, areadesc: &AreaDesc, images: &Images,
755 total_ops: i32, count: usize) -> (SimFlash, Vec<i32>) {
756 let mut fl = flash.clone();
757
David Brown541860c2017-11-06 11:25:42 -0700758 mark_permanent_upgrade(&mut fl, &images.slot1, images.align);
David Brown2639e072017-10-11 11:18:44 -0600759
760 let mut rng = rand::thread_rng();
761 let mut resets = vec![0i32; count];
762 let mut remaining_ops = total_ops;
763 for i in 0 .. count {
764 let ops = Range::new(1, remaining_ops / 2);
765 let reset_counter = ops.ind_sample(&mut rng);
David Brownee61c832017-11-06 11:13:25 -0700766 let mut counter = reset_counter;
David Brown541860c2017-11-06 11:25:42 -0700767 match c::boot_go(&mut fl, &areadesc, Some(&mut counter), images.align) {
David Brown2639e072017-10-11 11:18:44 -0600768 0 | -0x13579 => (),
769 x => panic!("Unknown return: {}", x),
770 }
771 remaining_ops -= reset_counter;
772 resets[i] = reset_counter;
773 }
774
David Brown541860c2017-11-06 11:25:42 -0700775 match c::boot_go(&mut fl, &areadesc, None, images.align) {
David Brown2639e072017-10-11 11:18:44 -0600776 -0x13579 => panic!("Should not be have been interrupted!"),
777 0 => (),
778 x => panic!("Unknown return: {}", x),
779 }
780
781 (fl, resets)
782}
783
784/// Show the flash layout.
785#[allow(dead_code)]
786fn show_flash(flash: &Flash) {
787 println!("---- Flash configuration ----");
788 for sector in flash.sector_iter() {
789 println!(" {:3}: 0x{:08x}, 0x{:08x}",
790 sector.num, sector.base, sector.size);
791 }
792 println!("");
793}
794
795/// Install a "program" into the given image. This fakes the image header, or at least all of the
796/// fields used by the given code. Returns a copy of the image that was written.
797fn install_image(flash: &mut Flash, offset: usize, len: usize,
798 bad_sig: bool) -> Vec<u8> {
799 let offset0 = offset;
800
801 let mut tlv = make_tlv();
802
803 // Generate a boot header. Note that the size doesn't include the header.
804 let header = ImageHeader {
805 magic: 0x96f3b83d,
806 tlv_size: tlv.get_size(),
807 _pad1: 0,
808 hdr_size: 32,
809 key_id: 0,
810 _pad2: 0,
811 img_size: len as u32,
812 flags: tlv.get_flags(),
813 ver: ImageVersion {
814 major: (offset / (128 * 1024)) as u8,
815 minor: 0,
816 revision: 1,
817 build_num: offset as u32,
818 },
819 _pad3: 0,
820 };
821
822 let b_header = header.as_raw();
823 tlv.add_bytes(&b_header);
824 /*
825 let b_header = unsafe { slice::from_raw_parts(&header as *const _ as *const u8,
826 mem::size_of::<ImageHeader>()) };
827 */
828 assert_eq!(b_header.len(), 32);
829 flash.write(offset, &b_header).unwrap();
830 let offset = offset + b_header.len();
831
832 // The core of the image itself is just pseudorandom data.
833 let mut buf = vec![0; len];
834 splat(&mut buf, offset);
835 tlv.add_bytes(&buf);
836
837 // Get and append the TLV itself.
838 if bad_sig {
839 let good_sig = &mut tlv.make_tlv();
840 buf.append(&mut vec![0; good_sig.len()]);
841 } else {
842 buf.append(&mut tlv.make_tlv());
843 }
844
845 // Pad the block to a flash alignment (8 bytes).
846 while buf.len() % 8 != 0 {
847 buf.push(0xFF);
848 }
849
850 flash.write(offset, &buf).unwrap();
851 let offset = offset + buf.len();
852
853 // Copy out the image so that we can verify that the image was installed correctly later.
854 let mut copy = vec![0u8; offset - offset0];
855 flash.read(offset0, &mut copy).unwrap();
856
857 copy
858}
859
860// The TLV in use depends on what kind of signature we are verifying.
861#[cfg(feature = "sig-rsa")]
862fn make_tlv() -> TlvGen {
863 TlvGen::new_rsa_pss()
864}
865
866#[cfg(not(feature = "sig-rsa"))]
867fn make_tlv() -> TlvGen {
868 TlvGen::new_hash_only()
869}
870
871/// Verify that given image is present in the flash at the given offset.
872fn verify_image(flash: &Flash, offset: usize, buf: &[u8]) -> bool {
873 let mut copy = vec![0u8; buf.len()];
874 flash.read(offset, &mut copy).unwrap();
875
876 if buf != &copy[..] {
877 for i in 0 .. buf.len() {
878 if buf[i] != copy[i] {
879 info!("First failure at {:#x}", offset + i);
880 break;
881 }
882 }
883 false
884 } else {
885 true
886 }
887}
888
889#[cfg(feature = "overwrite-only")]
890#[allow(unused_variables)]
891// overwrite-only doesn't employ trailer management
892fn verify_trailer(flash: &Flash, offset: usize,
893 magic: Option<&[u8]>, image_ok: Option<u8>,
894 copy_done: Option<u8>) -> bool {
895 true
896}
897
898#[cfg(not(feature = "overwrite-only"))]
899fn verify_trailer(flash: &Flash, offset: usize,
900 magic: Option<&[u8]>, image_ok: Option<u8>,
901 copy_done: Option<u8>) -> bool {
902 let mut copy = vec![0u8; c::boot_magic_sz() + c::boot_max_align() * 2];
903 let mut failed = false;
904
905 flash.read(offset, &mut copy).unwrap();
906
907 failed |= match magic {
908 Some(v) => {
909 if &copy[16..] != v {
910 warn!("\"magic\" mismatch at {:#x}", offset);
911 true
912 } else {
913 false
914 }
915 },
916 None => false,
917 };
918
919 failed |= match image_ok {
920 Some(v) => {
921 if copy[8] != v {
922 warn!("\"image_ok\" mismatch at {:#x}", offset);
923 true
924 } else {
925 false
926 }
927 },
928 None => false,
929 };
930
931 failed |= match copy_done {
932 Some(v) => {
933 if copy[0] != v {
934 warn!("\"copy_done\" mismatch at {:#x}", offset);
935 true
936 } else {
937 false
938 }
939 },
940 None => false,
941 };
942
943 !failed
944}
945
946/// The image header
947#[repr(C)]
948pub struct ImageHeader {
949 magic: u32,
950 tlv_size: u16,
951 key_id: u8,
952 _pad1: u8,
953 hdr_size: u16,
954 _pad2: u16,
955 img_size: u32,
956 flags: u32,
957 ver: ImageVersion,
958 _pad3: u32,
959}
960
961impl AsRaw for ImageHeader {}
962
963#[repr(C)]
964pub struct ImageVersion {
965 major: u8,
966 minor: u8,
967 revision: u16,
968 build_num: u32,
969}
970
David Brownd5e632c2017-10-19 10:49:46 -0600971#[derive(Clone)]
David Brown2639e072017-10-11 11:18:44 -0600972struct SlotInfo {
973 base_off: usize,
974 trailer_off: usize,
975}
976
David Brownd5e632c2017-10-19 10:49:46 -0600977struct Images {
978 slot0: SlotInfo,
979 slot1: SlotInfo,
David Brown2639e072017-10-11 11:18:44 -0600980 primary: Vec<u8>,
981 upgrade: Vec<u8>,
David Brown541860c2017-11-06 11:25:42 -0700982 align: u8,
David Brown2639e072017-10-11 11:18:44 -0600983}
984
985const MAGIC_VALID: Option<&[u8]> = Some(&[0x77, 0xc2, 0x95, 0xf3,
986 0x60, 0xd2, 0xef, 0x7f,
987 0x35, 0x52, 0x50, 0x0f,
988 0x2c, 0xb6, 0x79, 0x80]);
989const MAGIC_UNSET: Option<&[u8]> = Some(&[0xff; 16]);
990
991const COPY_DONE: Option<u8> = Some(1);
992const IMAGE_OK: Option<u8> = Some(1);
993const UNSET: Option<u8> = Some(0xff);
994
995/// Write out the magic so that the loader tries doing an upgrade.
996fn mark_upgrade(flash: &mut Flash, slot: &SlotInfo) {
997 let offset = slot.trailer_off + c::boot_max_align() * 2;
998 flash.write(offset, MAGIC_VALID.unwrap()).unwrap();
999}
1000
1001/// Writes the image_ok flag which, guess what, tells the bootloader
1002/// the this image is ok (not a test, and no revert is to be performed).
David Brown541860c2017-11-06 11:25:42 -07001003fn mark_permanent_upgrade(flash: &mut Flash, slot: &SlotInfo, align: u8) {
David Brown2639e072017-10-11 11:18:44 -06001004 let ok = [1u8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
David Brown2639e072017-10-11 11:18:44 -06001005 let off = slot.trailer_off + c::boot_max_align();
David Brown541860c2017-11-06 11:25:42 -07001006 flash.write(off, &ok[..align as usize]).unwrap();
David Brown2639e072017-10-11 11:18:44 -06001007}
1008
1009// Drop some pseudo-random gibberish onto the data.
1010fn splat(data: &mut [u8], seed: usize) {
1011 let seed_block = [0x135782ea, 0x92184728, data.len() as u32, seed as u32];
1012 let mut rng: XorShiftRng = SeedableRng::from_seed(seed_block);
1013 rng.fill_bytes(data);
1014}
1015
1016/// Return a read-only view into the raw bytes of this object
1017trait AsRaw : Sized {
1018 fn as_raw<'a>(&'a self) -> &'a [u8] {
1019 unsafe { slice::from_raw_parts(self as *const _ as *const u8,
1020 mem::size_of::<Self>()) }
1021 }
1022}
1023
1024fn show_sizes() {
1025 // This isn't panic safe.
David Brown2639e072017-10-11 11:18:44 -06001026 for min in &[1, 2, 4, 8] {
David Brown541860c2017-11-06 11:25:42 -07001027 let msize = c::boot_trailer_sz(*min);
David Brown2639e072017-10-11 11:18:44 -06001028 println!("{:2}: {} (0x{:x})", min, msize, msize);
1029 }
David Brown2639e072017-10-11 11:18:44 -06001030}