blob: 1a0e413c1a336fa2db4fdbabcb7ad6f529db9b19 [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 }
David Brownf48b9502017-11-06 14:00:26 -0700208
209 /// Construct an `Images` that doesn't expect an upgrade to happen.
210 pub fn make_no_upgrade_image(&self) -> Images {
211 let mut flash = self.flash.clone();
212 let primary = install_image(&mut flash, self.slots[0].base_off, 32784, false);
213 let upgrade = install_image(&mut flash, self.slots[1].base_off, 41928, false);
214 Images {
215 flash: flash,
216 areadesc: self.areadesc.clone(),
217 slot0: self.slots[0].clone(),
218 slot1: self.slots[1].clone(),
219 primary: primary,
220 upgrade: upgrade,
221 align: self.align,
222 }
223 }
224
225 /// Construct an `Images` for normal testing.
226 pub fn make_image(&self) -> Images {
227 let mut images = self.make_no_upgrade_image();
228 mark_upgrade(&mut images.flash, &images.slot1);
229 images
230 }
231
232 pub fn make_bad_slot1_image(&self) -> Images {
233 let mut bad_flash = self.flash.clone();
234 let primary = install_image(&mut bad_flash, self.slots[0].base_off, 32784, false);
235 let upgrade = install_image(&mut bad_flash, self.slots[1].base_off, 41928, true);
236 Images {
237 flash: bad_flash,
238 areadesc: self.areadesc.clone(),
239 slot0: self.slots[0].clone(),
240 slot1: self.slots[1].clone(),
241 primary: primary,
242 upgrade: upgrade,
243 align: self.align,
244 }
245 }
David Browndb9a3952017-11-06 13:16:15 -0700246}
247
David Browndd2b1182017-11-02 15:39:21 -0600248pub struct RunStatus {
David Brown2639e072017-10-11 11:18:44 -0600249 failures: usize,
250 passes: usize,
251}
252
253impl RunStatus {
David Browndd2b1182017-11-02 15:39:21 -0600254 pub fn new() -> RunStatus {
David Brown2639e072017-10-11 11:18:44 -0600255 RunStatus {
256 failures: 0,
257 passes: 0,
258 }
259 }
260
David Browndd2b1182017-11-02 15:39:21 -0600261 pub fn run_single(&mut self, device: DeviceName, align: u8) {
David Brown2639e072017-10-11 11:18:44 -0600262 warn!("Running on device {} with alignment {}", device, align);
263
David Browndc9cba12017-11-06 13:31:42 -0700264 let run = Run::new(device, align);
David Brown2639e072017-10-11 11:18:44 -0600265
David Brown2639e072017-10-11 11:18:44 -0600266 let mut failed = false;
267
268 // Creates a badly signed image in slot1 to check that it is not
269 // upgraded to
David Brownf48b9502017-11-06 14:00:26 -0700270 let bad_slot1_image = run.make_bad_slot1_image();
David Brown2639e072017-10-11 11:18:44 -0600271
David Brown5f7ec2b2017-11-06 13:54:02 -0700272 failed |= bad_slot1_image.run_signfail_upgrade();
David Brown2639e072017-10-11 11:18:44 -0600273
David Brownf48b9502017-11-06 14:00:26 -0700274 let images = run.make_no_upgrade_image();
David Brown5f7ec2b2017-11-06 13:54:02 -0700275 failed |= images.run_norevert_newimage();
David Brown2639e072017-10-11 11:18:44 -0600276
David Brownf48b9502017-11-06 14:00:26 -0700277 let images = run.make_image();
David Brown2639e072017-10-11 11:18:44 -0600278
279 // upgrades without fails, counts number of flash operations
David Brown5f7ec2b2017-11-06 13:54:02 -0700280 let total_count = match images.run_basic_upgrade() {
David Brown2639e072017-10-11 11:18:44 -0600281 Ok(v) => v,
282 Err(_) => {
283 self.failures += 1;
284 return;
285 },
286 };
287
David Brown5f7ec2b2017-11-06 13:54:02 -0700288 failed |= images.run_basic_revert();
289 failed |= images.run_revert_with_fails(total_count);
290 failed |= images.run_perm_with_fails(total_count);
291 failed |= images.run_perm_with_random_fails(total_count, 5);
292 failed |= images.run_norevert();
David Brown2639e072017-10-11 11:18:44 -0600293
294 //show_flash(&flash);
295
296 if failed {
297 self.failures += 1;
298 } else {
299 self.passes += 1;
300 }
301 }
David Browndd2b1182017-11-02 15:39:21 -0600302
303 pub fn failures(&self) -> usize {
304 self.failures
305 }
David Brown2639e072017-10-11 11:18:44 -0600306}
307
David Browndecbd042017-10-19 10:43:17 -0600308/// Build the Flash and area descriptor for a given device.
309pub fn make_device(device: DeviceName, align: u8) -> (SimFlash, AreaDesc) {
310 match device {
311 DeviceName::Stm32f4 => {
312 // STM style flash. Large sectors, with a large scratch area.
313 let flash = SimFlash::new(vec![16 * 1024, 16 * 1024, 16 * 1024, 16 * 1024,
314 64 * 1024,
315 128 * 1024, 128 * 1024, 128 * 1024],
316 align as usize);
317 let mut areadesc = AreaDesc::new(&flash);
318 areadesc.add_image(0x020000, 0x020000, FlashId::Image0);
319 areadesc.add_image(0x040000, 0x020000, FlashId::Image1);
320 areadesc.add_image(0x060000, 0x020000, FlashId::ImageScratch);
321 (flash, areadesc)
322 }
323 DeviceName::K64f => {
324 // NXP style flash. Small sectors, one small sector for scratch.
325 let flash = SimFlash::new(vec![4096; 128], align as usize);
326
327 let mut areadesc = AreaDesc::new(&flash);
328 areadesc.add_image(0x020000, 0x020000, FlashId::Image0);
329 areadesc.add_image(0x040000, 0x020000, FlashId::Image1);
330 areadesc.add_image(0x060000, 0x001000, FlashId::ImageScratch);
331 (flash, areadesc)
332 }
333 DeviceName::K64fBig => {
334 // Simulating an STM style flash on top of an NXP style flash. Underlying flash device
335 // uses small sectors, but we tell the bootloader they are large.
336 let flash = SimFlash::new(vec![4096; 128], align as usize);
337
338 let mut areadesc = AreaDesc::new(&flash);
339 areadesc.add_simple_image(0x020000, 0x020000, FlashId::Image0);
340 areadesc.add_simple_image(0x040000, 0x020000, FlashId::Image1);
341 areadesc.add_simple_image(0x060000, 0x020000, FlashId::ImageScratch);
342 (flash, areadesc)
343 }
344 DeviceName::Nrf52840 => {
345 // Simulating the flash on the nrf52840 with partitions set up so that the scratch size
346 // does not divide into the image size.
347 let flash = SimFlash::new(vec![4096; 128], align as usize);
348
349 let mut areadesc = AreaDesc::new(&flash);
350 areadesc.add_image(0x008000, 0x034000, FlashId::Image0);
351 areadesc.add_image(0x03c000, 0x034000, FlashId::Image1);
352 areadesc.add_image(0x070000, 0x00d000, FlashId::ImageScratch);
353 (flash, areadesc)
354 }
355 }
356}
357
David Brown5f7ec2b2017-11-06 13:54:02 -0700358impl Images {
359 /// A simple upgrade without forced failures.
360 ///
361 /// Returns the number of flash operations which can later be used to
362 /// inject failures at chosen steps.
363 fn run_basic_upgrade(&self) -> Result<i32, ()> {
364 let (fl, total_count) = try_upgrade(&self.flash, &self, None);
365 info!("Total flash operation count={}", total_count);
David Brown2639e072017-10-11 11:18:44 -0600366
David Brown5f7ec2b2017-11-06 13:54:02 -0700367 if !verify_image(&fl, self.slot0.base_off, &self.upgrade) {
368 warn!("Image mismatch after first boot");
369 Err(())
370 } else {
371 Ok(total_count)
David Brown2639e072017-10-11 11:18:44 -0600372 }
373 }
374
David Brown5f7ec2b2017-11-06 13:54:02 -0700375 #[cfg(feature = "overwrite-only")]
376 fn run_basic_revert(&self) -> bool {
377 false
378 }
David Brown2639e072017-10-11 11:18:44 -0600379
David Brown5f7ec2b2017-11-06 13:54:02 -0700380 #[cfg(not(feature = "overwrite-only"))]
381 fn run_basic_revert(&self) -> bool {
382 let mut fails = 0;
David Brown2639e072017-10-11 11:18:44 -0600383
David Brown5f7ec2b2017-11-06 13:54:02 -0700384 // FIXME: this test would also pass if no swap is ever performed???
385 if Caps::SwapUpgrade.present() {
386 for count in 2 .. 5 {
387 info!("Try revert: {}", count);
388 let fl = try_revert(&self.flash, &self.areadesc, count, self.align);
389 if !verify_image(&fl, self.slot0.base_off, &self.primary) {
390 error!("Revert failure on count {}", count);
391 fails += 1;
392 }
393 }
394 }
395
396 fails > 0
397 }
398
399 fn run_perm_with_fails(&self, total_flash_ops: i32) -> bool {
400 let mut fails = 0;
401
402 // Let's try an image halfway through.
403 for i in 1 .. total_flash_ops {
404 info!("Try interruption at {}", i);
405 let (fl, count) = try_upgrade(&self.flash, &self, Some(i));
406 info!("Second boot, count={}", count);
407 if !verify_image(&fl, self.slot0.base_off, &self.upgrade) {
408 warn!("FAIL at step {} of {}", i, total_flash_ops);
409 fails += 1;
410 }
411
412 if !verify_trailer(&fl, self.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
413 COPY_DONE) {
414 warn!("Mismatched trailer for Slot 0");
415 fails += 1;
416 }
417
418 if !verify_trailer(&fl, self.slot1.trailer_off, MAGIC_UNSET, UNSET,
419 UNSET) {
420 warn!("Mismatched trailer for Slot 1");
421 fails += 1;
422 }
423
424 if Caps::SwapUpgrade.present() {
425 if !verify_image(&fl, self.slot1.base_off, &self.primary) {
426 warn!("Slot 1 FAIL at step {} of {}", i, total_flash_ops);
427 fails += 1;
428 }
429 }
430 }
431
432 if fails > 0 {
433 error!("{} out of {} failed {:.2}%", fails, total_flash_ops,
434 fails as f32 * 100.0 / total_flash_ops as f32);
435 }
436
437 fails > 0
438 }
439
440 fn run_perm_with_random_fails(&self, total_flash_ops: i32,
441 total_fails: usize) -> bool {
442 let mut fails = 0;
443 let (fl, total_counts) = try_random_fails(&self.flash, &self,
444 total_flash_ops, total_fails);
445 info!("Random interruptions at reset points={:?}", total_counts);
446
447 let slot0_ok = verify_image(&fl, self.slot0.base_off, &self.upgrade);
448 let slot1_ok = if Caps::SwapUpgrade.present() {
449 verify_image(&fl, self.slot1.base_off, &self.primary)
450 } else {
451 true
452 };
453 if !slot0_ok || !slot1_ok {
454 error!("Image mismatch after random interrupts: slot0={} slot1={}",
455 if slot0_ok { "ok" } else { "fail" },
456 if slot1_ok { "ok" } else { "fail" });
457 fails += 1;
458 }
459 if !verify_trailer(&fl, self.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
460 COPY_DONE) {
461 error!("Mismatched trailer for Slot 0");
462 fails += 1;
463 }
464 if !verify_trailer(&fl, self.slot1.trailer_off, MAGIC_UNSET, UNSET,
465 UNSET) {
466 error!("Mismatched trailer for Slot 1");
David Brown2639e072017-10-11 11:18:44 -0600467 fails += 1;
468 }
469
David Brown5f7ec2b2017-11-06 13:54:02 -0700470 if fails > 0 {
471 error!("Error testing perm upgrade with {} fails", total_fails);
472 }
473
474 fails > 0
475 }
476
477 #[cfg(feature = "overwrite-only")]
478 #[allow(unused_variables)]
479 fn run_revert_with_fails(&self, total_count: i32) -> bool {
480 false
481 }
482
483 #[cfg(not(feature = "overwrite-only"))]
484 fn run_revert_with_fails(&self, total_count: i32) -> bool {
485 let mut fails = 0;
486
487 if Caps::SwapUpgrade.present() {
488 for i in 1 .. (total_count - 1) {
489 info!("Try interruption at {}", i);
490 if try_revert_with_fail_at(&self.flash, &self, i) {
491 error!("Revert failed at interruption {}", i);
492 fails += 1;
493 }
494 }
495 }
496
497 fails > 0
498 }
499
500 #[cfg(feature = "overwrite-only")]
501 fn run_norevert(&self) -> bool {
502 false
503 }
504
505 #[cfg(not(feature = "overwrite-only"))]
506 fn run_norevert(&self) -> bool {
507 let mut fl = self.flash.clone();
508 let mut fails = 0;
509
510 info!("Try norevert");
511
512 // First do a normal upgrade...
513 if c::boot_go(&mut fl, &self.areadesc, None, self.align) != 0 {
514 warn!("Failed first boot");
515 fails += 1;
516 }
517
518 //FIXME: copy_done is written by boot_go, is it ok if no copy
519 // was ever done?
520
521 if !verify_image(&fl, self.slot0.base_off, &self.upgrade) {
522 warn!("Slot 0 image verification FAIL");
523 fails += 1;
524 }
525 if !verify_trailer(&fl, self.slot0.trailer_off, MAGIC_VALID, UNSET,
David Brown2639e072017-10-11 11:18:44 -0600526 COPY_DONE) {
527 warn!("Mismatched trailer for Slot 0");
528 fails += 1;
529 }
David Brown5f7ec2b2017-11-06 13:54:02 -0700530 if !verify_trailer(&fl, self.slot1.trailer_off, MAGIC_UNSET, UNSET,
David Brown2639e072017-10-11 11:18:44 -0600531 UNSET) {
532 warn!("Mismatched trailer for Slot 1");
533 fails += 1;
534 }
535
David Brown5f7ec2b2017-11-06 13:54:02 -0700536 // Marks image in slot0 as permanent, no revert should happen...
537 mark_permanent_upgrade(&mut fl, &self.slot0, self.align);
538
539 if !verify_trailer(&fl, self.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
540 COPY_DONE) {
541 warn!("Mismatched trailer for Slot 0");
542 fails += 1;
David Brown2639e072017-10-11 11:18:44 -0600543 }
David Brown2639e072017-10-11 11:18:44 -0600544
David Brown5f7ec2b2017-11-06 13:54:02 -0700545 if c::boot_go(&mut fl, &self.areadesc, None, self.align) != 0 {
546 warn!("Failed second boot");
547 fails += 1;
David Brown2639e072017-10-11 11:18:44 -0600548 }
David Brown5f7ec2b2017-11-06 13:54:02 -0700549
550 if !verify_trailer(&fl, self.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
551 COPY_DONE) {
552 warn!("Mismatched trailer for Slot 0");
553 fails += 1;
554 }
555 if !verify_image(&fl, self.slot0.base_off, &self.upgrade) {
556 warn!("Failed image verification");
557 fails += 1;
558 }
559
560 if fails > 0 {
561 error!("Error running upgrade without revert");
562 }
563
564 fails > 0
David Brown2639e072017-10-11 11:18:44 -0600565 }
566
David Brown5f7ec2b2017-11-06 13:54:02 -0700567 // Tests a new image written to slot0 that already has magic and image_ok set
568 // while there is no image on slot1, so no revert should ever happen...
569 fn run_norevert_newimage(&self) -> bool {
570 let mut fl = self.flash.clone();
571 let mut fails = 0;
David Brown2639e072017-10-11 11:18:44 -0600572
David Brown5f7ec2b2017-11-06 13:54:02 -0700573 info!("Try non-revert on imgtool generated image");
David Brown2639e072017-10-11 11:18:44 -0600574
David Brown5f7ec2b2017-11-06 13:54:02 -0700575 mark_upgrade(&mut fl, &self.slot0);
David Brown2639e072017-10-11 11:18:44 -0600576
David Brown5f7ec2b2017-11-06 13:54:02 -0700577 // This simulates writing an image created by imgtool to Slot 0
578 if !verify_trailer(&fl, self.slot0.trailer_off, MAGIC_VALID, UNSET, UNSET) {
579 warn!("Mismatched trailer for Slot 0");
580 fails += 1;
581 }
David Brown2639e072017-10-11 11:18:44 -0600582
David Brown5f7ec2b2017-11-06 13:54:02 -0700583 // Run the bootloader...
584 if c::boot_go(&mut fl, &self.areadesc, None, self.align) != 0 {
585 warn!("Failed first boot");
586 fails += 1;
587 }
588
589 // State should not have changed
590 if !verify_image(&fl, self.slot0.base_off, &self.primary) {
591 warn!("Failed image verification");
592 fails += 1;
593 }
594 if !verify_trailer(&fl, self.slot0.trailer_off, MAGIC_VALID, UNSET,
595 UNSET) {
596 warn!("Mismatched trailer for Slot 0");
597 fails += 1;
598 }
599 if !verify_trailer(&fl, self.slot1.trailer_off, MAGIC_UNSET, UNSET,
600 UNSET) {
601 warn!("Mismatched trailer for Slot 1");
602 fails += 1;
603 }
604
605 if fails > 0 {
606 error!("Expected a non revert with new image");
607 }
608
609 fails > 0
David Brown2639e072017-10-11 11:18:44 -0600610 }
611
David Brown5f7ec2b2017-11-06 13:54:02 -0700612 // Tests a new image written to slot0 that already has magic and image_ok set
613 // while there is no image on slot1, so no revert should ever happen...
614 fn run_signfail_upgrade(&self) -> bool {
615 let mut fl = self.flash.clone();
616 let mut fails = 0;
David Brown2639e072017-10-11 11:18:44 -0600617
David Brown5f7ec2b2017-11-06 13:54:02 -0700618 info!("Try upgrade image with bad signature");
619
620 mark_upgrade(&mut fl, &self.slot0);
621 mark_permanent_upgrade(&mut fl, &self.slot0, self.align);
622 mark_upgrade(&mut fl, &self.slot1);
623
624 if !verify_trailer(&fl, self.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
625 UNSET) {
626 warn!("Mismatched trailer for Slot 0");
627 fails += 1;
628 }
629
630 // Run the bootloader...
631 if c::boot_go(&mut fl, &self.areadesc, None, self.align) != 0 {
632 warn!("Failed first boot");
633 fails += 1;
634 }
635
636 // State should not have changed
637 if !verify_image(&fl, self.slot0.base_off, &self.primary) {
638 warn!("Failed image verification");
639 fails += 1;
640 }
641 if !verify_trailer(&fl, self.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
642 UNSET) {
643 warn!("Mismatched trailer for Slot 0");
644 fails += 1;
645 }
646
647 if fails > 0 {
648 error!("Expected an upgrade failure when image has bad signature");
649 }
650
651 fails > 0
David Brown2639e072017-10-11 11:18:44 -0600652 }
David Brown2639e072017-10-11 11:18:44 -0600653}
654
655/// Test a boot, optionally stopping after 'n' flash options. Returns a count
656/// of the number of flash operations done total.
David Brown3f687dc2017-11-06 13:41:18 -0700657fn try_upgrade(flash: &SimFlash, images: &Images,
David Brown2639e072017-10-11 11:18:44 -0600658 stop: Option<i32>) -> (SimFlash, i32) {
659 // Clone the flash to have a new copy.
660 let mut fl = flash.clone();
661
David Brown541860c2017-11-06 11:25:42 -0700662 mark_permanent_upgrade(&mut fl, &images.slot1, images.align);
David Brown2639e072017-10-11 11:18:44 -0600663
David Brownee61c832017-11-06 11:13:25 -0700664 let mut counter = stop.unwrap_or(0);
665
David Brown3f687dc2017-11-06 13:41:18 -0700666 let (first_interrupted, count) = match c::boot_go(&mut fl, &images.areadesc, Some(&mut counter), images.align) {
David Brown2639e072017-10-11 11:18:44 -0600667 -0x13579 => (true, stop.unwrap()),
David Brownee61c832017-11-06 11:13:25 -0700668 0 => (false, -counter),
David Brown2639e072017-10-11 11:18:44 -0600669 x => panic!("Unknown return: {}", x),
670 };
David Brown2639e072017-10-11 11:18:44 -0600671
David Brownee61c832017-11-06 11:13:25 -0700672 counter = 0;
David Brown2639e072017-10-11 11:18:44 -0600673 if first_interrupted {
674 // fl.dump();
David Brown3f687dc2017-11-06 13:41:18 -0700675 match c::boot_go(&mut fl, &images.areadesc, Some(&mut counter), images.align) {
David Brown2639e072017-10-11 11:18:44 -0600676 -0x13579 => panic!("Shouldn't stop again"),
677 0 => (),
678 x => panic!("Unknown return: {}", x),
679 }
680 }
681
David Brownee61c832017-11-06 11:13:25 -0700682 (fl, count - counter)
David Brown2639e072017-10-11 11:18:44 -0600683}
684
685#[cfg(not(feature = "overwrite-only"))]
David Brown541860c2017-11-06 11:25:42 -0700686fn try_revert(flash: &SimFlash, areadesc: &AreaDesc, count: usize, align: u8) -> SimFlash {
David Brown2639e072017-10-11 11:18:44 -0600687 let mut fl = flash.clone();
David Brown2639e072017-10-11 11:18:44 -0600688
689 // fl.write_file("image0.bin").unwrap();
690 for i in 0 .. count {
691 info!("Running boot pass {}", i + 1);
David Brown541860c2017-11-06 11:25:42 -0700692 assert_eq!(c::boot_go(&mut fl, &areadesc, None, align), 0);
David Brown2639e072017-10-11 11:18:44 -0600693 }
694 fl
695}
696
697#[cfg(not(feature = "overwrite-only"))]
David Brown3f687dc2017-11-06 13:41:18 -0700698fn try_revert_with_fail_at(flash: &SimFlash, images: &Images,
David Brown2639e072017-10-11 11:18:44 -0600699 stop: i32) -> bool {
700 let mut fl = flash.clone();
701 let mut x: i32;
702 let mut fails = 0;
703
David Brownee61c832017-11-06 11:13:25 -0700704 let mut counter = stop;
David Brown3f687dc2017-11-06 13:41:18 -0700705 x = c::boot_go(&mut fl, &images.areadesc, Some(&mut counter), images.align);
David Brown2639e072017-10-11 11:18:44 -0600706 if x != -0x13579 {
707 warn!("Should have stopped at interruption point");
708 fails += 1;
709 }
710
711 if !verify_trailer(&fl, images.slot0.trailer_off, None, None, UNSET) {
712 warn!("copy_done should be unset");
713 fails += 1;
714 }
715
David Brown3f687dc2017-11-06 13:41:18 -0700716 x = c::boot_go(&mut fl, &images.areadesc, None, images.align);
David Brown2639e072017-10-11 11:18:44 -0600717 if x != 0 {
718 warn!("Should have finished upgrade");
719 fails += 1;
720 }
721
722 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
723 warn!("Image in slot 0 before revert is invalid at stop={}", stop);
724 fails += 1;
725 }
726 if !verify_image(&fl, images.slot1.base_off, &images.primary) {
727 warn!("Image in slot 1 before revert is invalid at stop={}", stop);
728 fails += 1;
729 }
730 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, UNSET,
731 COPY_DONE) {
732 warn!("Mismatched trailer for Slot 0 before revert");
733 fails += 1;
734 }
735 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
736 UNSET) {
737 warn!("Mismatched trailer for Slot 1 before revert");
738 fails += 1;
739 }
740
741 // Do Revert
David Brown3f687dc2017-11-06 13:41:18 -0700742 x = c::boot_go(&mut fl, &images.areadesc, None, images.align);
David Brown2639e072017-10-11 11:18:44 -0600743 if x != 0 {
744 warn!("Should have finished a revert");
745 fails += 1;
746 }
747
748 if !verify_image(&fl, images.slot0.base_off, &images.primary) {
749 warn!("Image in slot 0 after revert is invalid at stop={}", stop);
750 fails += 1;
751 }
752 if !verify_image(&fl, images.slot1.base_off, &images.upgrade) {
753 warn!("Image in slot 1 after revert is invalid at stop={}", stop);
754 fails += 1;
755 }
756 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
757 COPY_DONE) {
758 warn!("Mismatched trailer for Slot 1 after revert");
759 fails += 1;
760 }
761 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
762 UNSET) {
763 warn!("Mismatched trailer for Slot 1 after revert");
764 fails += 1;
765 }
766
767 fails > 0
768}
769
David Brown3f687dc2017-11-06 13:41:18 -0700770fn try_random_fails(flash: &SimFlash, images: &Images,
David Brown2639e072017-10-11 11:18:44 -0600771 total_ops: i32, count: usize) -> (SimFlash, Vec<i32>) {
772 let mut fl = flash.clone();
773
David Brown541860c2017-11-06 11:25:42 -0700774 mark_permanent_upgrade(&mut fl, &images.slot1, images.align);
David Brown2639e072017-10-11 11:18:44 -0600775
776 let mut rng = rand::thread_rng();
777 let mut resets = vec![0i32; count];
778 let mut remaining_ops = total_ops;
779 for i in 0 .. count {
780 let ops = Range::new(1, remaining_ops / 2);
781 let reset_counter = ops.ind_sample(&mut rng);
David Brownee61c832017-11-06 11:13:25 -0700782 let mut counter = reset_counter;
David Brown3f687dc2017-11-06 13:41:18 -0700783 match c::boot_go(&mut fl, &images.areadesc, Some(&mut counter), images.align) {
David Brown2639e072017-10-11 11:18:44 -0600784 0 | -0x13579 => (),
785 x => panic!("Unknown return: {}", x),
786 }
787 remaining_ops -= reset_counter;
788 resets[i] = reset_counter;
789 }
790
David Brown3f687dc2017-11-06 13:41:18 -0700791 match c::boot_go(&mut fl, &images.areadesc, None, images.align) {
David Brown2639e072017-10-11 11:18:44 -0600792 -0x13579 => panic!("Should not be have been interrupted!"),
793 0 => (),
794 x => panic!("Unknown return: {}", x),
795 }
796
797 (fl, resets)
798}
799
800/// Show the flash layout.
801#[allow(dead_code)]
802fn show_flash(flash: &Flash) {
803 println!("---- Flash configuration ----");
804 for sector in flash.sector_iter() {
805 println!(" {:3}: 0x{:08x}, 0x{:08x}",
806 sector.num, sector.base, sector.size);
807 }
808 println!("");
809}
810
811/// Install a "program" into the given image. This fakes the image header, or at least all of the
812/// fields used by the given code. Returns a copy of the image that was written.
813fn install_image(flash: &mut Flash, offset: usize, len: usize,
814 bad_sig: bool) -> Vec<u8> {
815 let offset0 = offset;
816
817 let mut tlv = make_tlv();
818
819 // Generate a boot header. Note that the size doesn't include the header.
820 let header = ImageHeader {
821 magic: 0x96f3b83d,
822 tlv_size: tlv.get_size(),
823 _pad1: 0,
824 hdr_size: 32,
825 key_id: 0,
826 _pad2: 0,
827 img_size: len as u32,
828 flags: tlv.get_flags(),
829 ver: ImageVersion {
830 major: (offset / (128 * 1024)) as u8,
831 minor: 0,
832 revision: 1,
833 build_num: offset as u32,
834 },
835 _pad3: 0,
836 };
837
838 let b_header = header.as_raw();
839 tlv.add_bytes(&b_header);
840 /*
841 let b_header = unsafe { slice::from_raw_parts(&header as *const _ as *const u8,
842 mem::size_of::<ImageHeader>()) };
843 */
844 assert_eq!(b_header.len(), 32);
845 flash.write(offset, &b_header).unwrap();
846 let offset = offset + b_header.len();
847
848 // The core of the image itself is just pseudorandom data.
849 let mut buf = vec![0; len];
850 splat(&mut buf, offset);
851 tlv.add_bytes(&buf);
852
853 // Get and append the TLV itself.
854 if bad_sig {
855 let good_sig = &mut tlv.make_tlv();
856 buf.append(&mut vec![0; good_sig.len()]);
857 } else {
858 buf.append(&mut tlv.make_tlv());
859 }
860
861 // Pad the block to a flash alignment (8 bytes).
862 while buf.len() % 8 != 0 {
863 buf.push(0xFF);
864 }
865
866 flash.write(offset, &buf).unwrap();
867 let offset = offset + buf.len();
868
869 // Copy out the image so that we can verify that the image was installed correctly later.
870 let mut copy = vec![0u8; offset - offset0];
871 flash.read(offset0, &mut copy).unwrap();
872
873 copy
874}
875
876// The TLV in use depends on what kind of signature we are verifying.
877#[cfg(feature = "sig-rsa")]
878fn make_tlv() -> TlvGen {
879 TlvGen::new_rsa_pss()
880}
881
882#[cfg(not(feature = "sig-rsa"))]
883fn make_tlv() -> TlvGen {
884 TlvGen::new_hash_only()
885}
886
887/// Verify that given image is present in the flash at the given offset.
888fn verify_image(flash: &Flash, offset: usize, buf: &[u8]) -> bool {
889 let mut copy = vec![0u8; buf.len()];
890 flash.read(offset, &mut copy).unwrap();
891
892 if buf != &copy[..] {
893 for i in 0 .. buf.len() {
894 if buf[i] != copy[i] {
895 info!("First failure at {:#x}", offset + i);
896 break;
897 }
898 }
899 false
900 } else {
901 true
902 }
903}
904
905#[cfg(feature = "overwrite-only")]
906#[allow(unused_variables)]
907// overwrite-only doesn't employ trailer management
908fn verify_trailer(flash: &Flash, offset: usize,
909 magic: Option<&[u8]>, image_ok: Option<u8>,
910 copy_done: Option<u8>) -> bool {
911 true
912}
913
914#[cfg(not(feature = "overwrite-only"))]
915fn verify_trailer(flash: &Flash, offset: usize,
916 magic: Option<&[u8]>, image_ok: Option<u8>,
917 copy_done: Option<u8>) -> bool {
918 let mut copy = vec![0u8; c::boot_magic_sz() + c::boot_max_align() * 2];
919 let mut failed = false;
920
921 flash.read(offset, &mut copy).unwrap();
922
923 failed |= match magic {
924 Some(v) => {
925 if &copy[16..] != v {
926 warn!("\"magic\" mismatch at {:#x}", offset);
927 true
928 } else {
929 false
930 }
931 },
932 None => false,
933 };
934
935 failed |= match image_ok {
936 Some(v) => {
937 if copy[8] != v {
938 warn!("\"image_ok\" mismatch at {:#x}", offset);
939 true
940 } else {
941 false
942 }
943 },
944 None => false,
945 };
946
947 failed |= match copy_done {
948 Some(v) => {
949 if copy[0] != v {
950 warn!("\"copy_done\" mismatch at {:#x}", offset);
951 true
952 } else {
953 false
954 }
955 },
956 None => false,
957 };
958
959 !failed
960}
961
962/// The image header
963#[repr(C)]
964pub struct ImageHeader {
965 magic: u32,
966 tlv_size: u16,
967 key_id: u8,
968 _pad1: u8,
969 hdr_size: u16,
970 _pad2: u16,
971 img_size: u32,
972 flags: u32,
973 ver: ImageVersion,
974 _pad3: u32,
975}
976
977impl AsRaw for ImageHeader {}
978
979#[repr(C)]
980pub struct ImageVersion {
981 major: u8,
982 minor: u8,
983 revision: u16,
984 build_num: u32,
985}
986
David Brownd5e632c2017-10-19 10:49:46 -0600987#[derive(Clone)]
David Brown2639e072017-10-11 11:18:44 -0600988struct SlotInfo {
989 base_off: usize,
990 trailer_off: usize,
991}
992
David Brownf48b9502017-11-06 14:00:26 -0700993pub struct Images {
David Browndc9cba12017-11-06 13:31:42 -0700994 flash: SimFlash,
David Brown3f687dc2017-11-06 13:41:18 -0700995 areadesc: AreaDesc,
David Brownd5e632c2017-10-19 10:49:46 -0600996 slot0: SlotInfo,
997 slot1: SlotInfo,
David Brown2639e072017-10-11 11:18:44 -0600998 primary: Vec<u8>,
999 upgrade: Vec<u8>,
David Brown541860c2017-11-06 11:25:42 -07001000 align: u8,
David Brown2639e072017-10-11 11:18:44 -06001001}
1002
1003const MAGIC_VALID: Option<&[u8]> = Some(&[0x77, 0xc2, 0x95, 0xf3,
1004 0x60, 0xd2, 0xef, 0x7f,
1005 0x35, 0x52, 0x50, 0x0f,
1006 0x2c, 0xb6, 0x79, 0x80]);
1007const MAGIC_UNSET: Option<&[u8]> = Some(&[0xff; 16]);
1008
1009const COPY_DONE: Option<u8> = Some(1);
1010const IMAGE_OK: Option<u8> = Some(1);
1011const UNSET: Option<u8> = Some(0xff);
1012
1013/// Write out the magic so that the loader tries doing an upgrade.
1014fn mark_upgrade(flash: &mut Flash, slot: &SlotInfo) {
1015 let offset = slot.trailer_off + c::boot_max_align() * 2;
1016 flash.write(offset, MAGIC_VALID.unwrap()).unwrap();
1017}
1018
1019/// Writes the image_ok flag which, guess what, tells the bootloader
1020/// the this image is ok (not a test, and no revert is to be performed).
David Brown541860c2017-11-06 11:25:42 -07001021fn mark_permanent_upgrade(flash: &mut Flash, slot: &SlotInfo, align: u8) {
David Brown2639e072017-10-11 11:18:44 -06001022 let ok = [1u8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
David Brown2639e072017-10-11 11:18:44 -06001023 let off = slot.trailer_off + c::boot_max_align();
David Brown541860c2017-11-06 11:25:42 -07001024 flash.write(off, &ok[..align as usize]).unwrap();
David Brown2639e072017-10-11 11:18:44 -06001025}
1026
1027// Drop some pseudo-random gibberish onto the data.
1028fn splat(data: &mut [u8], seed: usize) {
1029 let seed_block = [0x135782ea, 0x92184728, data.len() as u32, seed as u32];
1030 let mut rng: XorShiftRng = SeedableRng::from_seed(seed_block);
1031 rng.fill_bytes(data);
1032}
1033
1034/// Return a read-only view into the raw bytes of this object
1035trait AsRaw : Sized {
1036 fn as_raw<'a>(&'a self) -> &'a [u8] {
1037 unsafe { slice::from_raw_parts(self as *const _ as *const u8,
1038 mem::size_of::<Self>()) }
1039 }
1040}
1041
1042fn show_sizes() {
1043 // This isn't panic safe.
David Brown2639e072017-10-11 11:18:44 -06001044 for min in &[1, 2, 4, 8] {
David Brown541860c2017-11-06 11:25:42 -07001045 let msize = c::boot_trailer_sz(*min);
David Brown2639e072017-10-11 11:18:44 -06001046 println!("{:2}: {} (0x{:x})", min, msize, msize);
1047 }
David Brown2639e072017-10-11 11:18:44 -06001048}