blob: d9a9f8d2f2f7a0d434fabb98e4f5712c56f7fc65 [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 Browndc9cba12017-11-06 13:31:42 -0700226 let 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 Browndc9cba12017-11-06 13:31:42 -0700233 let primary = install_image(&mut bad_flash, run.slots[0].base_off, 32784, false);
234 let upgrade = install_image(&mut bad_flash, run.slots[1].base_off, 41928, true);
David Brown2639e072017-10-11 11:18:44 -0600235 let bad_slot1_image = Images {
David Browndc9cba12017-11-06 13:31:42 -0700236 flash: bad_flash,
David Brown3f687dc2017-11-06 13:41:18 -0700237 areadesc: run.areadesc.clone(),
David Browndb9a3952017-11-06 13:16:15 -0700238 slot0: run.slots[0].clone(),
239 slot1: run.slots[1].clone(),
David Browndc9cba12017-11-06 13:31:42 -0700240 primary: primary,
241 upgrade: upgrade,
David Brown541860c2017-11-06 11:25:42 -0700242 align: align,
David Brown2639e072017-10-11 11:18:44 -0600243 };
244
David Brown3f687dc2017-11-06 13:41:18 -0700245 failed |= run_signfail_upgrade(&bad_slot1_image);
David Brown2639e072017-10-11 11:18:44 -0600246
David Browndc9cba12017-11-06 13:31:42 -0700247 let mut flash = run.flash.clone();
248 let primary = install_image(&mut flash, run.slots[0].base_off, 32784, false);
249 let upgrade = install_image(&mut flash, run.slots[1].base_off, 41928, false);
250 let mut images = Images {
251 flash: flash,
David Brown3f687dc2017-11-06 13:41:18 -0700252 areadesc: run.areadesc.clone(),
David Browndb9a3952017-11-06 13:16:15 -0700253 slot0: run.slots[0].clone(),
254 slot1: run.slots[1].clone(),
David Browndc9cba12017-11-06 13:31:42 -0700255 primary: primary,
256 upgrade: upgrade,
David Brown541860c2017-11-06 11:25:42 -0700257 align: align,
David Brown2639e072017-10-11 11:18:44 -0600258 };
259
David Brown3f687dc2017-11-06 13:41:18 -0700260 failed |= run_norevert_newimage(&images);
David Brown2639e072017-10-11 11:18:44 -0600261
David Browndc9cba12017-11-06 13:31:42 -0700262 mark_upgrade(&mut images.flash, &images.slot1);
David Brown2639e072017-10-11 11:18:44 -0600263
264 // upgrades without fails, counts number of flash operations
David Brown3f687dc2017-11-06 13:41:18 -0700265 let total_count = match run_basic_upgrade(&images) {
David Brown2639e072017-10-11 11:18:44 -0600266 Ok(v) => v,
267 Err(_) => {
268 self.failures += 1;
269 return;
270 },
271 };
272
David Brown3f687dc2017-11-06 13:41:18 -0700273 failed |= run_basic_revert(&images);
274 failed |= run_revert_with_fails(&images, total_count);
275 failed |= run_perm_with_fails(&images, total_count);
276 failed |= run_perm_with_random_fails(&images, total_count, 5);
277 failed |= run_norevert(&images);
David Brown2639e072017-10-11 11:18:44 -0600278
279 //show_flash(&flash);
280
281 if failed {
282 self.failures += 1;
283 } else {
284 self.passes += 1;
285 }
286 }
David Browndd2b1182017-11-02 15:39:21 -0600287
288 pub fn failures(&self) -> usize {
289 self.failures
290 }
David Brown2639e072017-10-11 11:18:44 -0600291}
292
David Browndecbd042017-10-19 10:43:17 -0600293/// Build the Flash and area descriptor for a given device.
294pub fn make_device(device: DeviceName, align: u8) -> (SimFlash, AreaDesc) {
295 match device {
296 DeviceName::Stm32f4 => {
297 // STM style flash. Large sectors, with a large scratch area.
298 let flash = SimFlash::new(vec![16 * 1024, 16 * 1024, 16 * 1024, 16 * 1024,
299 64 * 1024,
300 128 * 1024, 128 * 1024, 128 * 1024],
301 align as usize);
302 let mut areadesc = AreaDesc::new(&flash);
303 areadesc.add_image(0x020000, 0x020000, FlashId::Image0);
304 areadesc.add_image(0x040000, 0x020000, FlashId::Image1);
305 areadesc.add_image(0x060000, 0x020000, FlashId::ImageScratch);
306 (flash, areadesc)
307 }
308 DeviceName::K64f => {
309 // NXP style flash. Small sectors, one small sector for scratch.
310 let flash = SimFlash::new(vec![4096; 128], align as usize);
311
312 let mut areadesc = AreaDesc::new(&flash);
313 areadesc.add_image(0x020000, 0x020000, FlashId::Image0);
314 areadesc.add_image(0x040000, 0x020000, FlashId::Image1);
315 areadesc.add_image(0x060000, 0x001000, FlashId::ImageScratch);
316 (flash, areadesc)
317 }
318 DeviceName::K64fBig => {
319 // Simulating an STM style flash on top of an NXP style flash. Underlying flash device
320 // uses small sectors, but we tell the bootloader they are large.
321 let flash = SimFlash::new(vec![4096; 128], align as usize);
322
323 let mut areadesc = AreaDesc::new(&flash);
324 areadesc.add_simple_image(0x020000, 0x020000, FlashId::Image0);
325 areadesc.add_simple_image(0x040000, 0x020000, FlashId::Image1);
326 areadesc.add_simple_image(0x060000, 0x020000, FlashId::ImageScratch);
327 (flash, areadesc)
328 }
329 DeviceName::Nrf52840 => {
330 // Simulating the flash on the nrf52840 with partitions set up so that the scratch size
331 // does not divide into the image size.
332 let flash = SimFlash::new(vec![4096; 128], align as usize);
333
334 let mut areadesc = AreaDesc::new(&flash);
335 areadesc.add_image(0x008000, 0x034000, FlashId::Image0);
336 areadesc.add_image(0x03c000, 0x034000, FlashId::Image1);
337 areadesc.add_image(0x070000, 0x00d000, FlashId::ImageScratch);
338 (flash, areadesc)
339 }
340 }
341}
342
David Brown2639e072017-10-11 11:18:44 -0600343/// A simple upgrade without forced failures.
344///
345/// Returns the number of flash operations which can later be used to
346/// inject failures at chosen steps.
David Brown3f687dc2017-11-06 13:41:18 -0700347fn run_basic_upgrade(images: &Images) -> Result<i32, ()> {
348 let (fl, total_count) = try_upgrade(&images.flash, &images, None);
David Brown2639e072017-10-11 11:18:44 -0600349 info!("Total flash operation count={}", total_count);
350
351 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
352 warn!("Image mismatch after first boot");
353 Err(())
354 } else {
355 Ok(total_count)
356 }
357}
358
359#[cfg(feature = "overwrite-only")]
360#[allow(unused_variables)]
David Brown3f687dc2017-11-06 13:41:18 -0700361fn run_basic_revert(images: &Images) -> bool {
David Brown2639e072017-10-11 11:18:44 -0600362 false
363}
364
365#[cfg(not(feature = "overwrite-only"))]
David Brown3f687dc2017-11-06 13:41:18 -0700366fn run_basic_revert(images: &Images) -> bool {
David Brown2639e072017-10-11 11:18:44 -0600367 let mut fails = 0;
368
369 // FIXME: this test would also pass if no swap is ever performed???
370 if Caps::SwapUpgrade.present() {
371 for count in 2 .. 5 {
372 info!("Try revert: {}", count);
David Brown3f687dc2017-11-06 13:41:18 -0700373 let fl = try_revert(&images.flash, &images.areadesc, count, images.align);
David Brown2639e072017-10-11 11:18:44 -0600374 if !verify_image(&fl, images.slot0.base_off, &images.primary) {
375 error!("Revert failure on count {}", count);
376 fails += 1;
377 }
378 }
379 }
380
381 fails > 0
382}
383
David Brown3f687dc2017-11-06 13:41:18 -0700384fn run_perm_with_fails(images: &Images, total_flash_ops: i32) -> bool {
David Brown2639e072017-10-11 11:18:44 -0600385 let mut fails = 0;
386
387 // Let's try an image halfway through.
388 for i in 1 .. total_flash_ops {
389 info!("Try interruption at {}", i);
David Brown3f687dc2017-11-06 13:41:18 -0700390 let (fl, count) = try_upgrade(&images.flash, &images, Some(i));
David Brown2639e072017-10-11 11:18:44 -0600391 info!("Second boot, count={}", count);
392 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
393 warn!("FAIL at step {} of {}", i, total_flash_ops);
394 fails += 1;
395 }
396
397 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
398 COPY_DONE) {
399 warn!("Mismatched trailer for Slot 0");
400 fails += 1;
401 }
402
403 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
404 UNSET) {
405 warn!("Mismatched trailer for Slot 1");
406 fails += 1;
407 }
408
409 if Caps::SwapUpgrade.present() {
410 if !verify_image(&fl, images.slot1.base_off, &images.primary) {
411 warn!("Slot 1 FAIL at step {} of {}", i, total_flash_ops);
412 fails += 1;
413 }
414 }
415 }
416
417 if fails > 0 {
418 error!("{} out of {} failed {:.2}%", fails, total_flash_ops,
419 fails as f32 * 100.0 / total_flash_ops as f32);
420 }
421
422 fails > 0
423}
424
David Brown3f687dc2017-11-06 13:41:18 -0700425fn run_perm_with_random_fails(images: &Images, total_flash_ops: i32,
David Brown2639e072017-10-11 11:18:44 -0600426 total_fails: usize) -> bool {
427 let mut fails = 0;
David Brown3f687dc2017-11-06 13:41:18 -0700428 let (fl, total_counts) = try_random_fails(&images.flash, &images,
David Brown2639e072017-10-11 11:18:44 -0600429 total_flash_ops, total_fails);
430 info!("Random interruptions at reset points={:?}", total_counts);
431
432 let slot0_ok = verify_image(&fl, images.slot0.base_off, &images.upgrade);
433 let slot1_ok = if Caps::SwapUpgrade.present() {
434 verify_image(&fl, images.slot1.base_off, &images.primary)
435 } else {
436 true
437 };
438 if !slot0_ok || !slot1_ok {
439 error!("Image mismatch after random interrupts: slot0={} slot1={}",
440 if slot0_ok { "ok" } else { "fail" },
441 if slot1_ok { "ok" } else { "fail" });
442 fails += 1;
443 }
444 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
445 COPY_DONE) {
446 error!("Mismatched trailer for Slot 0");
447 fails += 1;
448 }
449 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
450 UNSET) {
451 error!("Mismatched trailer for Slot 1");
452 fails += 1;
453 }
454
455 if fails > 0 {
456 error!("Error testing perm upgrade with {} fails", total_fails);
457 }
458
459 fails > 0
460}
461
462#[cfg(feature = "overwrite-only")]
463#[allow(unused_variables)]
David Browndc9cba12017-11-06 13:31:42 -0700464fn run_revert_with_fails(areadesc: &AreaDesc, images: &Images,
David Brown2639e072017-10-11 11:18:44 -0600465 total_count: i32) -> bool {
466 false
467}
468
469#[cfg(not(feature = "overwrite-only"))]
David Brown3f687dc2017-11-06 13:41:18 -0700470fn run_revert_with_fails(images: &Images, total_count: i32) -> bool {
David Brown2639e072017-10-11 11:18:44 -0600471 let mut fails = 0;
472
473 if Caps::SwapUpgrade.present() {
474 for i in 1 .. (total_count - 1) {
475 info!("Try interruption at {}", i);
David Brown3f687dc2017-11-06 13:41:18 -0700476 if try_revert_with_fail_at(&images.flash, &images, i) {
David Brown2639e072017-10-11 11:18:44 -0600477 error!("Revert failed at interruption {}", i);
478 fails += 1;
479 }
480 }
481 }
482
483 fails > 0
484}
485
486#[cfg(feature = "overwrite-only")]
487#[allow(unused_variables)]
David Brown3f687dc2017-11-06 13:41:18 -0700488fn run_norevert(images: &Images) -> bool {
David Brown2639e072017-10-11 11:18:44 -0600489 false
490}
491
492#[cfg(not(feature = "overwrite-only"))]
David Brown3f687dc2017-11-06 13:41:18 -0700493fn run_norevert(images: &Images) -> bool {
David Browndc9cba12017-11-06 13:31:42 -0700494 let mut fl = images.flash.clone();
David Brown2639e072017-10-11 11:18:44 -0600495 let mut fails = 0;
496
497 info!("Try norevert");
David Brown2639e072017-10-11 11:18:44 -0600498
499 // First do a normal upgrade...
David Brown3f687dc2017-11-06 13:41:18 -0700500 if c::boot_go(&mut fl, &images.areadesc, None, images.align) != 0 {
David Brown2639e072017-10-11 11:18:44 -0600501 warn!("Failed first boot");
502 fails += 1;
503 }
504
505 //FIXME: copy_done is written by boot_go, is it ok if no copy
506 // was ever done?
507
508 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
509 warn!("Slot 0 image verification FAIL");
510 fails += 1;
511 }
512 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, UNSET,
513 COPY_DONE) {
514 warn!("Mismatched trailer for Slot 0");
515 fails += 1;
516 }
517 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
518 UNSET) {
519 warn!("Mismatched trailer for Slot 1");
520 fails += 1;
521 }
522
523 // Marks image in slot0 as permanent, no revert should happen...
David Brown541860c2017-11-06 11:25:42 -0700524 mark_permanent_upgrade(&mut fl, &images.slot0, images.align);
David Brown2639e072017-10-11 11:18:44 -0600525
526 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
527 COPY_DONE) {
528 warn!("Mismatched trailer for Slot 0");
529 fails += 1;
530 }
531
David Brown3f687dc2017-11-06 13:41:18 -0700532 if c::boot_go(&mut fl, &images.areadesc, None, images.align) != 0 {
David Brown2639e072017-10-11 11:18:44 -0600533 warn!("Failed second boot");
534 fails += 1;
535 }
536
537 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
538 COPY_DONE) {
539 warn!("Mismatched trailer for Slot 0");
540 fails += 1;
541 }
542 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
543 warn!("Failed image verification");
544 fails += 1;
545 }
546
547 if fails > 0 {
548 error!("Error running upgrade without revert");
549 }
550
551 fails > 0
552}
553
554// Tests a new image written to slot0 that already has magic and image_ok set
555// while there is no image on slot1, so no revert should ever happen...
David Brown3f687dc2017-11-06 13:41:18 -0700556fn run_norevert_newimage(images: &Images) -> bool {
David Browndc9cba12017-11-06 13:31:42 -0700557 let mut fl = images.flash.clone();
David Brown2639e072017-10-11 11:18:44 -0600558 let mut fails = 0;
559
560 info!("Try non-revert on imgtool generated image");
David Brown2639e072017-10-11 11:18:44 -0600561
562 mark_upgrade(&mut fl, &images.slot0);
563
564 // This simulates writing an image created by imgtool to Slot 0
565 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, UNSET, UNSET) {
566 warn!("Mismatched trailer for Slot 0");
567 fails += 1;
568 }
569
570 // Run the bootloader...
David Brown3f687dc2017-11-06 13:41:18 -0700571 if c::boot_go(&mut fl, &images.areadesc, None, images.align) != 0 {
David Brown2639e072017-10-11 11:18:44 -0600572 warn!("Failed first boot");
573 fails += 1;
574 }
575
576 // State should not have changed
577 if !verify_image(&fl, images.slot0.base_off, &images.primary) {
578 warn!("Failed image verification");
579 fails += 1;
580 }
581 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, UNSET,
582 UNSET) {
583 warn!("Mismatched trailer for Slot 0");
584 fails += 1;
585 }
586 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
587 UNSET) {
588 warn!("Mismatched trailer for Slot 1");
589 fails += 1;
590 }
591
592 if fails > 0 {
593 error!("Expected a non revert with new image");
594 }
595
596 fails > 0
597}
598
599// Tests a new image written to slot0 that already has magic and image_ok set
600// while there is no image on slot1, so no revert should ever happen...
David Brown3f687dc2017-11-06 13:41:18 -0700601fn run_signfail_upgrade(images: &Images) -> bool {
David Browndc9cba12017-11-06 13:31:42 -0700602 let mut fl = images.flash.clone();
David Brown2639e072017-10-11 11:18:44 -0600603 let mut fails = 0;
604
605 info!("Try upgrade image with bad signature");
David Brown2639e072017-10-11 11:18:44 -0600606
607 mark_upgrade(&mut fl, &images.slot0);
David Brown541860c2017-11-06 11:25:42 -0700608 mark_permanent_upgrade(&mut fl, &images.slot0, images.align);
David Brown2639e072017-10-11 11:18:44 -0600609 mark_upgrade(&mut fl, &images.slot1);
610
611 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
612 UNSET) {
613 warn!("Mismatched trailer for Slot 0");
614 fails += 1;
615 }
616
617 // Run the bootloader...
David Brown3f687dc2017-11-06 13:41:18 -0700618 if c::boot_go(&mut fl, &images.areadesc, None, images.align) != 0 {
David Brown2639e072017-10-11 11:18:44 -0600619 warn!("Failed first boot");
620 fails += 1;
621 }
622
623 // State should not have changed
624 if !verify_image(&fl, images.slot0.base_off, &images.primary) {
625 warn!("Failed image verification");
626 fails += 1;
627 }
628 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
629 UNSET) {
630 warn!("Mismatched trailer for Slot 0");
631 fails += 1;
632 }
633
634 if fails > 0 {
635 error!("Expected an upgrade failure when image has bad signature");
636 }
637
638 fails > 0
639}
640
641/// Test a boot, optionally stopping after 'n' flash options. Returns a count
642/// of the number of flash operations done total.
David Brown3f687dc2017-11-06 13:41:18 -0700643fn try_upgrade(flash: &SimFlash, images: &Images,
David Brown2639e072017-10-11 11:18:44 -0600644 stop: Option<i32>) -> (SimFlash, i32) {
645 // Clone the flash to have a new copy.
646 let mut fl = flash.clone();
647
David Brown541860c2017-11-06 11:25:42 -0700648 mark_permanent_upgrade(&mut fl, &images.slot1, images.align);
David Brown2639e072017-10-11 11:18:44 -0600649
David Brownee61c832017-11-06 11:13:25 -0700650 let mut counter = stop.unwrap_or(0);
651
David Brown3f687dc2017-11-06 13:41:18 -0700652 let (first_interrupted, count) = match c::boot_go(&mut fl, &images.areadesc, Some(&mut counter), images.align) {
David Brown2639e072017-10-11 11:18:44 -0600653 -0x13579 => (true, stop.unwrap()),
David Brownee61c832017-11-06 11:13:25 -0700654 0 => (false, -counter),
David Brown2639e072017-10-11 11:18:44 -0600655 x => panic!("Unknown return: {}", x),
656 };
David Brown2639e072017-10-11 11:18:44 -0600657
David Brownee61c832017-11-06 11:13:25 -0700658 counter = 0;
David Brown2639e072017-10-11 11:18:44 -0600659 if first_interrupted {
660 // fl.dump();
David Brown3f687dc2017-11-06 13:41:18 -0700661 match c::boot_go(&mut fl, &images.areadesc, Some(&mut counter), images.align) {
David Brown2639e072017-10-11 11:18:44 -0600662 -0x13579 => panic!("Shouldn't stop again"),
663 0 => (),
664 x => panic!("Unknown return: {}", x),
665 }
666 }
667
David Brownee61c832017-11-06 11:13:25 -0700668 (fl, count - counter)
David Brown2639e072017-10-11 11:18:44 -0600669}
670
671#[cfg(not(feature = "overwrite-only"))]
David Brown541860c2017-11-06 11:25:42 -0700672fn try_revert(flash: &SimFlash, areadesc: &AreaDesc, count: usize, align: u8) -> SimFlash {
David Brown2639e072017-10-11 11:18:44 -0600673 let mut fl = flash.clone();
David Brown2639e072017-10-11 11:18:44 -0600674
675 // fl.write_file("image0.bin").unwrap();
676 for i in 0 .. count {
677 info!("Running boot pass {}", i + 1);
David Brown541860c2017-11-06 11:25:42 -0700678 assert_eq!(c::boot_go(&mut fl, &areadesc, None, align), 0);
David Brown2639e072017-10-11 11:18:44 -0600679 }
680 fl
681}
682
683#[cfg(not(feature = "overwrite-only"))]
David Brown3f687dc2017-11-06 13:41:18 -0700684fn try_revert_with_fail_at(flash: &SimFlash, images: &Images,
David Brown2639e072017-10-11 11:18:44 -0600685 stop: i32) -> bool {
686 let mut fl = flash.clone();
687 let mut x: i32;
688 let mut fails = 0;
689
David Brownee61c832017-11-06 11:13:25 -0700690 let mut counter = stop;
David Brown3f687dc2017-11-06 13:41:18 -0700691 x = c::boot_go(&mut fl, &images.areadesc, Some(&mut counter), images.align);
David Brown2639e072017-10-11 11:18:44 -0600692 if x != -0x13579 {
693 warn!("Should have stopped at interruption point");
694 fails += 1;
695 }
696
697 if !verify_trailer(&fl, images.slot0.trailer_off, None, None, UNSET) {
698 warn!("copy_done should be unset");
699 fails += 1;
700 }
701
David Brown3f687dc2017-11-06 13:41:18 -0700702 x = c::boot_go(&mut fl, &images.areadesc, None, images.align);
David Brown2639e072017-10-11 11:18:44 -0600703 if x != 0 {
704 warn!("Should have finished upgrade");
705 fails += 1;
706 }
707
708 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
709 warn!("Image in slot 0 before revert is invalid at stop={}", stop);
710 fails += 1;
711 }
712 if !verify_image(&fl, images.slot1.base_off, &images.primary) {
713 warn!("Image in slot 1 before revert is invalid at stop={}", stop);
714 fails += 1;
715 }
716 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, UNSET,
717 COPY_DONE) {
718 warn!("Mismatched trailer for Slot 0 before revert");
719 fails += 1;
720 }
721 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
722 UNSET) {
723 warn!("Mismatched trailer for Slot 1 before revert");
724 fails += 1;
725 }
726
727 // Do Revert
David Brown3f687dc2017-11-06 13:41:18 -0700728 x = c::boot_go(&mut fl, &images.areadesc, None, images.align);
David Brown2639e072017-10-11 11:18:44 -0600729 if x != 0 {
730 warn!("Should have finished a revert");
731 fails += 1;
732 }
733
734 if !verify_image(&fl, images.slot0.base_off, &images.primary) {
735 warn!("Image in slot 0 after revert is invalid at stop={}", stop);
736 fails += 1;
737 }
738 if !verify_image(&fl, images.slot1.base_off, &images.upgrade) {
739 warn!("Image in slot 1 after revert is invalid at stop={}", stop);
740 fails += 1;
741 }
742 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
743 COPY_DONE) {
744 warn!("Mismatched trailer for Slot 1 after revert");
745 fails += 1;
746 }
747 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
748 UNSET) {
749 warn!("Mismatched trailer for Slot 1 after revert");
750 fails += 1;
751 }
752
753 fails > 0
754}
755
David Brown3f687dc2017-11-06 13:41:18 -0700756fn try_random_fails(flash: &SimFlash, images: &Images,
David Brown2639e072017-10-11 11:18:44 -0600757 total_ops: i32, count: usize) -> (SimFlash, Vec<i32>) {
758 let mut fl = flash.clone();
759
David Brown541860c2017-11-06 11:25:42 -0700760 mark_permanent_upgrade(&mut fl, &images.slot1, images.align);
David Brown2639e072017-10-11 11:18:44 -0600761
762 let mut rng = rand::thread_rng();
763 let mut resets = vec![0i32; count];
764 let mut remaining_ops = total_ops;
765 for i in 0 .. count {
766 let ops = Range::new(1, remaining_ops / 2);
767 let reset_counter = ops.ind_sample(&mut rng);
David Brownee61c832017-11-06 11:13:25 -0700768 let mut counter = reset_counter;
David Brown3f687dc2017-11-06 13:41:18 -0700769 match c::boot_go(&mut fl, &images.areadesc, Some(&mut counter), images.align) {
David Brown2639e072017-10-11 11:18:44 -0600770 0 | -0x13579 => (),
771 x => panic!("Unknown return: {}", x),
772 }
773 remaining_ops -= reset_counter;
774 resets[i] = reset_counter;
775 }
776
David Brown3f687dc2017-11-06 13:41:18 -0700777 match c::boot_go(&mut fl, &images.areadesc, None, images.align) {
David Brown2639e072017-10-11 11:18:44 -0600778 -0x13579 => panic!("Should not be have been interrupted!"),
779 0 => (),
780 x => panic!("Unknown return: {}", x),
781 }
782
783 (fl, resets)
784}
785
786/// Show the flash layout.
787#[allow(dead_code)]
788fn show_flash(flash: &Flash) {
789 println!("---- Flash configuration ----");
790 for sector in flash.sector_iter() {
791 println!(" {:3}: 0x{:08x}, 0x{:08x}",
792 sector.num, sector.base, sector.size);
793 }
794 println!("");
795}
796
797/// Install a "program" into the given image. This fakes the image header, or at least all of the
798/// fields used by the given code. Returns a copy of the image that was written.
799fn install_image(flash: &mut Flash, offset: usize, len: usize,
800 bad_sig: bool) -> Vec<u8> {
801 let offset0 = offset;
802
803 let mut tlv = make_tlv();
804
805 // Generate a boot header. Note that the size doesn't include the header.
806 let header = ImageHeader {
807 magic: 0x96f3b83d,
808 tlv_size: tlv.get_size(),
809 _pad1: 0,
810 hdr_size: 32,
811 key_id: 0,
812 _pad2: 0,
813 img_size: len as u32,
814 flags: tlv.get_flags(),
815 ver: ImageVersion {
816 major: (offset / (128 * 1024)) as u8,
817 minor: 0,
818 revision: 1,
819 build_num: offset as u32,
820 },
821 _pad3: 0,
822 };
823
824 let b_header = header.as_raw();
825 tlv.add_bytes(&b_header);
826 /*
827 let b_header = unsafe { slice::from_raw_parts(&header as *const _ as *const u8,
828 mem::size_of::<ImageHeader>()) };
829 */
830 assert_eq!(b_header.len(), 32);
831 flash.write(offset, &b_header).unwrap();
832 let offset = offset + b_header.len();
833
834 // The core of the image itself is just pseudorandom data.
835 let mut buf = vec![0; len];
836 splat(&mut buf, offset);
837 tlv.add_bytes(&buf);
838
839 // Get and append the TLV itself.
840 if bad_sig {
841 let good_sig = &mut tlv.make_tlv();
842 buf.append(&mut vec![0; good_sig.len()]);
843 } else {
844 buf.append(&mut tlv.make_tlv());
845 }
846
847 // Pad the block to a flash alignment (8 bytes).
848 while buf.len() % 8 != 0 {
849 buf.push(0xFF);
850 }
851
852 flash.write(offset, &buf).unwrap();
853 let offset = offset + buf.len();
854
855 // Copy out the image so that we can verify that the image was installed correctly later.
856 let mut copy = vec![0u8; offset - offset0];
857 flash.read(offset0, &mut copy).unwrap();
858
859 copy
860}
861
862// The TLV in use depends on what kind of signature we are verifying.
863#[cfg(feature = "sig-rsa")]
864fn make_tlv() -> TlvGen {
865 TlvGen::new_rsa_pss()
866}
867
868#[cfg(not(feature = "sig-rsa"))]
869fn make_tlv() -> TlvGen {
870 TlvGen::new_hash_only()
871}
872
873/// Verify that given image is present in the flash at the given offset.
874fn verify_image(flash: &Flash, offset: usize, buf: &[u8]) -> bool {
875 let mut copy = vec![0u8; buf.len()];
876 flash.read(offset, &mut copy).unwrap();
877
878 if buf != &copy[..] {
879 for i in 0 .. buf.len() {
880 if buf[i] != copy[i] {
881 info!("First failure at {:#x}", offset + i);
882 break;
883 }
884 }
885 false
886 } else {
887 true
888 }
889}
890
891#[cfg(feature = "overwrite-only")]
892#[allow(unused_variables)]
893// overwrite-only doesn't employ trailer management
894fn verify_trailer(flash: &Flash, offset: usize,
895 magic: Option<&[u8]>, image_ok: Option<u8>,
896 copy_done: Option<u8>) -> bool {
897 true
898}
899
900#[cfg(not(feature = "overwrite-only"))]
901fn verify_trailer(flash: &Flash, offset: usize,
902 magic: Option<&[u8]>, image_ok: Option<u8>,
903 copy_done: Option<u8>) -> bool {
904 let mut copy = vec![0u8; c::boot_magic_sz() + c::boot_max_align() * 2];
905 let mut failed = false;
906
907 flash.read(offset, &mut copy).unwrap();
908
909 failed |= match magic {
910 Some(v) => {
911 if &copy[16..] != v {
912 warn!("\"magic\" mismatch at {:#x}", offset);
913 true
914 } else {
915 false
916 }
917 },
918 None => false,
919 };
920
921 failed |= match image_ok {
922 Some(v) => {
923 if copy[8] != v {
924 warn!("\"image_ok\" mismatch at {:#x}", offset);
925 true
926 } else {
927 false
928 }
929 },
930 None => false,
931 };
932
933 failed |= match copy_done {
934 Some(v) => {
935 if copy[0] != v {
936 warn!("\"copy_done\" mismatch at {:#x}", offset);
937 true
938 } else {
939 false
940 }
941 },
942 None => false,
943 };
944
945 !failed
946}
947
948/// The image header
949#[repr(C)]
950pub struct ImageHeader {
951 magic: u32,
952 tlv_size: u16,
953 key_id: u8,
954 _pad1: u8,
955 hdr_size: u16,
956 _pad2: u16,
957 img_size: u32,
958 flags: u32,
959 ver: ImageVersion,
960 _pad3: u32,
961}
962
963impl AsRaw for ImageHeader {}
964
965#[repr(C)]
966pub struct ImageVersion {
967 major: u8,
968 minor: u8,
969 revision: u16,
970 build_num: u32,
971}
972
David Brownd5e632c2017-10-19 10:49:46 -0600973#[derive(Clone)]
David Brown2639e072017-10-11 11:18:44 -0600974struct SlotInfo {
975 base_off: usize,
976 trailer_off: usize,
977}
978
David Brownd5e632c2017-10-19 10:49:46 -0600979struct Images {
David Browndc9cba12017-11-06 13:31:42 -0700980 flash: SimFlash,
David Brown3f687dc2017-11-06 13:41:18 -0700981 areadesc: AreaDesc,
David Brownd5e632c2017-10-19 10:49:46 -0600982 slot0: SlotInfo,
983 slot1: SlotInfo,
David Brown2639e072017-10-11 11:18:44 -0600984 primary: Vec<u8>,
985 upgrade: Vec<u8>,
David Brown541860c2017-11-06 11:25:42 -0700986 align: u8,
David Brown2639e072017-10-11 11:18:44 -0600987}
988
989const MAGIC_VALID: Option<&[u8]> = Some(&[0x77, 0xc2, 0x95, 0xf3,
990 0x60, 0xd2, 0xef, 0x7f,
991 0x35, 0x52, 0x50, 0x0f,
992 0x2c, 0xb6, 0x79, 0x80]);
993const MAGIC_UNSET: Option<&[u8]> = Some(&[0xff; 16]);
994
995const COPY_DONE: Option<u8> = Some(1);
996const IMAGE_OK: Option<u8> = Some(1);
997const UNSET: Option<u8> = Some(0xff);
998
999/// Write out the magic so that the loader tries doing an upgrade.
1000fn mark_upgrade(flash: &mut Flash, slot: &SlotInfo) {
1001 let offset = slot.trailer_off + c::boot_max_align() * 2;
1002 flash.write(offset, MAGIC_VALID.unwrap()).unwrap();
1003}
1004
1005/// Writes the image_ok flag which, guess what, tells the bootloader
1006/// the this image is ok (not a test, and no revert is to be performed).
David Brown541860c2017-11-06 11:25:42 -07001007fn mark_permanent_upgrade(flash: &mut Flash, slot: &SlotInfo, align: u8) {
David Brown2639e072017-10-11 11:18:44 -06001008 let ok = [1u8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
David Brown2639e072017-10-11 11:18:44 -06001009 let off = slot.trailer_off + c::boot_max_align();
David Brown541860c2017-11-06 11:25:42 -07001010 flash.write(off, &ok[..align as usize]).unwrap();
David Brown2639e072017-10-11 11:18:44 -06001011}
1012
1013// Drop some pseudo-random gibberish onto the data.
1014fn splat(data: &mut [u8], seed: usize) {
1015 let seed_block = [0x135782ea, 0x92184728, data.len() as u32, seed as u32];
1016 let mut rng: XorShiftRng = SeedableRng::from_seed(seed_block);
1017 rng.fill_bytes(data);
1018}
1019
1020/// Return a read-only view into the raw bytes of this object
1021trait AsRaw : Sized {
1022 fn as_raw<'a>(&'a self) -> &'a [u8] {
1023 unsafe { slice::from_raw_parts(self as *const _ as *const u8,
1024 mem::size_of::<Self>()) }
1025 }
1026}
1027
1028fn show_sizes() {
1029 // This isn't panic safe.
David Brown2639e072017-10-11 11:18:44 -06001030 for min in &[1, 2, 4, 8] {
David Brown541860c2017-11-06 11:25:42 -07001031 let msize = c::boot_trailer_sz(*min);
David Brown2639e072017-10-11 11:18:44 -06001032 println!("{:2}: {} (0x{:x})", min, msize, msize);
1033 }
David Brown2639e072017-10-11 11:18:44 -06001034}