blob: acd15a97465d15e1946d1a5ead515ea326e641f0 [file] [log] [blame]
David Brown4440af82017-01-09 12:15:05 -07001#[macro_use] extern crate log;
David Brown8054ce22017-07-11 12:12:09 -06002extern crate ring;
David Brown4440af82017-01-09 12:15:05 -07003extern crate env_logger;
David Brown1e158592017-07-11 12:29:25 -06004#[macro_use] extern crate bitflags;
David Brownde7729e2017-01-09 10:41:35 -07005extern crate docopt;
6extern crate libc;
David Brown7e701d82017-07-11 13:24:25 -06007extern crate pem;
David Brownde7729e2017-01-09 10:41:35 -07008extern crate rand;
9extern crate rustc_serialize;
David Brown2cbc4702017-07-06 14:18:58 -060010extern crate simflash;
David Brown7e701d82017-07-11 13:24:25 -060011extern crate untrusted;
David Brownde7729e2017-01-09 10:41:35 -070012
13use docopt::Docopt;
David Brown4cb26232017-04-11 08:15:18 -060014use rand::{Rng, SeedableRng, XorShiftRng};
Fabio Utzigbb5635e2017-04-10 09:07:02 -030015use rand::distributions::{IndependentSample, Range};
David Brownde7729e2017-01-09 10:41:35 -070016use rustc_serialize::{Decodable, Decoder};
David Browna3b93cf2017-03-29 12:41:26 -060017use std::fmt;
David Brownde7729e2017-01-09 10:41:35 -070018use std::mem;
David Brown361be7a2017-03-29 12:28:47 -060019use std::process;
David Brownde7729e2017-01-09 10:41:35 -070020use std::slice;
21
22mod area;
23mod c;
David Brownde7729e2017-01-09 10:41:35 -070024pub mod api;
David Brown902d6172017-05-05 09:37:41 -060025mod caps;
David Brown187dd882017-07-11 11:15:23 -060026mod tlv;
David Brownde7729e2017-01-09 10:41:35 -070027
David Brown2cbc4702017-07-06 14:18:58 -060028use simflash::{Flash, SimFlash};
David Brownde7729e2017-01-09 10:41:35 -070029use area::{AreaDesc, FlashId};
David Brown902d6172017-05-05 09:37:41 -060030use caps::Caps;
David Brown187dd882017-07-11 11:15:23 -060031use tlv::TlvGen;
David Brownde7729e2017-01-09 10:41:35 -070032
33const USAGE: &'static str = "
34Mcuboot simulator
35
36Usage:
37 bootsim sizes
38 bootsim run --device TYPE [--align SIZE]
David Browna3b93cf2017-03-29 12:41:26 -060039 bootsim runall
David Brownde7729e2017-01-09 10:41:35 -070040 bootsim (--help | --version)
41
42Options:
43 -h, --help Show this message
44 --version Version
45 --device TYPE MCU to simulate
46 Valid values: stm32f4, k64f
47 --align SIZE Flash write alignment
48";
49
50#[derive(Debug, RustcDecodable)]
51struct Args {
52 flag_help: bool,
53 flag_version: bool,
54 flag_device: Option<DeviceName>,
55 flag_align: Option<AlignArg>,
56 cmd_sizes: bool,
57 cmd_run: bool,
David Browna3b93cf2017-03-29 12:41:26 -060058 cmd_runall: bool,
David Brownde7729e2017-01-09 10:41:35 -070059}
60
David Browna3b93cf2017-03-29 12:41:26 -060061#[derive(Copy, Clone, Debug, RustcDecodable)]
David Brown07fb8fa2017-03-20 12:40:57 -060062enum DeviceName { Stm32f4, K64f, K64fBig, Nrf52840 }
David Brownde7729e2017-01-09 10:41:35 -070063
David Browna3b93cf2017-03-29 12:41:26 -060064static ALL_DEVICES: &'static [DeviceName] = &[
65 DeviceName::Stm32f4,
66 DeviceName::K64f,
67 DeviceName::K64fBig,
68 DeviceName::Nrf52840,
69];
70
71impl fmt::Display for DeviceName {
72 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
73 let name = match *self {
74 DeviceName::Stm32f4 => "stm32f4",
75 DeviceName::K64f => "k64f",
76 DeviceName::K64fBig => "k64fbig",
77 DeviceName::Nrf52840 => "nrf52840",
78 };
79 f.write_str(name)
80 }
81}
82
David Brownde7729e2017-01-09 10:41:35 -070083#[derive(Debug)]
84struct AlignArg(u8);
85
86impl Decodable for AlignArg {
87 // Decode the alignment ourselves, to restrict it to the valid possible alignments.
88 fn decode<D: Decoder>(d: &mut D) -> Result<AlignArg, D::Error> {
89 let m = d.read_u8()?;
90 match m {
91 1 | 2 | 4 | 8 => Ok(AlignArg(m)),
92 _ => Err(d.error("Invalid alignment")),
93 }
94 }
95}
96
97fn main() {
David Brown4440af82017-01-09 12:15:05 -070098 env_logger::init().unwrap();
99
David Brownde7729e2017-01-09 10:41:35 -0700100 let args: Args = Docopt::new(USAGE)
101 .and_then(|d| d.decode())
102 .unwrap_or_else(|e| e.exit());
103 // println!("args: {:#?}", args);
104
105 if args.cmd_sizes {
106 show_sizes();
107 return;
108 }
109
David Brown361be7a2017-03-29 12:28:47 -0600110 let mut status = RunStatus::new();
David Browna3b93cf2017-03-29 12:41:26 -0600111 if args.cmd_run {
David Brown361be7a2017-03-29 12:28:47 -0600112
David Browna3b93cf2017-03-29 12:41:26 -0600113 let align = args.flag_align.map(|x| x.0).unwrap_or(1);
David Brown562a7a02017-01-23 11:19:03 -0700114
Fabio Utzigebeecef2017-07-06 10:36:42 -0300115
David Browna3b93cf2017-03-29 12:41:26 -0600116 let device = match args.flag_device {
117 None => panic!("Missing mandatory device argument"),
118 Some(dev) => dev,
119 };
David Brownde7729e2017-01-09 10:41:35 -0700120
David Browna3b93cf2017-03-29 12:41:26 -0600121 status.run_single(device, align);
122 }
123
124 if args.cmd_runall {
125 for &dev in ALL_DEVICES {
126 for &align in &[1, 2, 4, 8] {
127 status.run_single(dev, align);
128 }
129 }
130 }
David Brown5c6b6792017-03-20 12:51:28 -0600131
David Brown361be7a2017-03-29 12:28:47 -0600132 if status.failures > 0 {
David Brown187dd882017-07-11 11:15:23 -0600133 error!("{} Tests ran with {} failures", status.failures + status.passes, status.failures);
David Brown361be7a2017-03-29 12:28:47 -0600134 process::exit(1);
135 } else {
136 warn!("{} Tests ran successfully", status.passes);
137 process::exit(0);
138 }
139}
David Brown5c6b6792017-03-20 12:51:28 -0600140
David Brown361be7a2017-03-29 12:28:47 -0600141struct RunStatus {
142 failures: usize,
143 passes: usize,
144}
David Brownde7729e2017-01-09 10:41:35 -0700145
David Brown361be7a2017-03-29 12:28:47 -0600146impl RunStatus {
147 fn new() -> RunStatus {
148 RunStatus {
149 failures: 0,
150 passes: 0,
David Brownde7729e2017-01-09 10:41:35 -0700151 }
152 }
David Brownde7729e2017-01-09 10:41:35 -0700153
David Brown361be7a2017-03-29 12:28:47 -0600154 fn run_single(&mut self, device: DeviceName, align: u8) {
David Browna3b93cf2017-03-29 12:41:26 -0600155 warn!("Running on device {} with alignment {}", device, align);
156
David Brown361be7a2017-03-29 12:28:47 -0600157 let (mut flash, areadesc) = match device {
158 DeviceName::Stm32f4 => {
159 // STM style flash. Large sectors, with a large scratch area.
David Brown7ddec0b2017-07-06 10:47:35 -0600160 let flash = SimFlash::new(vec![16 * 1024, 16 * 1024, 16 * 1024, 16 * 1024,
161 64 * 1024,
162 128 * 1024, 128 * 1024, 128 * 1024],
163 align as usize);
David Brown361be7a2017-03-29 12:28:47 -0600164 let mut areadesc = AreaDesc::new(&flash);
165 areadesc.add_image(0x020000, 0x020000, FlashId::Image0);
166 areadesc.add_image(0x040000, 0x020000, FlashId::Image1);
167 areadesc.add_image(0x060000, 0x020000, FlashId::ImageScratch);
168 (flash, areadesc)
169 }
170 DeviceName::K64f => {
171 // NXP style flash. Small sectors, one small sector for scratch.
David Brown7ddec0b2017-07-06 10:47:35 -0600172 let flash = SimFlash::new(vec![4096; 128], align as usize);
David Brown361be7a2017-03-29 12:28:47 -0600173
174 let mut areadesc = AreaDesc::new(&flash);
175 areadesc.add_image(0x020000, 0x020000, FlashId::Image0);
176 areadesc.add_image(0x040000, 0x020000, FlashId::Image1);
177 areadesc.add_image(0x060000, 0x001000, FlashId::ImageScratch);
178 (flash, areadesc)
179 }
180 DeviceName::K64fBig => {
181 // Simulating an STM style flash on top of an NXP style flash. Underlying flash device
182 // uses small sectors, but we tell the bootloader they are large.
David Brown7ddec0b2017-07-06 10:47:35 -0600183 let flash = SimFlash::new(vec![4096; 128], align as usize);
David Brown361be7a2017-03-29 12:28:47 -0600184
185 let mut areadesc = AreaDesc::new(&flash);
186 areadesc.add_simple_image(0x020000, 0x020000, FlashId::Image0);
187 areadesc.add_simple_image(0x040000, 0x020000, FlashId::Image1);
188 areadesc.add_simple_image(0x060000, 0x020000, FlashId::ImageScratch);
189 (flash, areadesc)
190 }
191 DeviceName::Nrf52840 => {
192 // Simulating the flash on the nrf52840 with partitions set up so that the scratch size
193 // does not divide into the image size.
David Brown7ddec0b2017-07-06 10:47:35 -0600194 let flash = SimFlash::new(vec![4096; 128], align as usize);
David Brown361be7a2017-03-29 12:28:47 -0600195
196 let mut areadesc = AreaDesc::new(&flash);
197 areadesc.add_image(0x008000, 0x034000, FlashId::Image0);
198 areadesc.add_image(0x03c000, 0x034000, FlashId::Image1);
199 areadesc.add_image(0x070000, 0x00d000, FlashId::ImageScratch);
200 (flash, areadesc)
201 }
202 };
203
204 let (slot0_base, slot0_len) = areadesc.find(FlashId::Image0);
205 let (slot1_base, slot1_len) = areadesc.find(FlashId::Image1);
206 let (scratch_base, _) = areadesc.find(FlashId::ImageScratch);
207
208 // Code below assumes that the slots are consecutive.
209 assert_eq!(slot1_base, slot0_base + slot0_len);
210 assert_eq!(scratch_base, slot1_base + slot1_len);
211
Fabio Utzigebeecef2017-07-06 10:36:42 -0300212 let offset_from_end = c::boot_magic_sz() + c::boot_max_align() * 2;
213
David Brown361be7a2017-03-29 12:28:47 -0600214 // println!("Areas: {:#?}", areadesc.get_c());
215
216 // Install the boot trailer signature, so that the code will start an upgrade.
217 // TODO: This must be a multiple of flash alignment, add support for an image that is smaller,
218 // and just gets padded.
David Brown361be7a2017-03-29 12:28:47 -0600219
Fabio Utzigebeecef2017-07-06 10:36:42 -0300220 // Create original and upgrade images
221 let slot0 = SlotInfo {
222 base_off: slot0_base as usize,
223 trailer_off: slot1_base - offset_from_end,
224 };
225
226 let slot1 = SlotInfo {
227 base_off: slot1_base as usize,
228 trailer_off: scratch_base - offset_from_end,
229 };
230
231 let images = Images {
232 slot0: slot0,
233 slot1: slot1,
234 primary: install_image(&mut flash, slot0_base, 32784),
235 upgrade: install_image(&mut flash, slot1_base, 41928),
236 };
237
238 let mut failed = false;
David Brown361be7a2017-03-29 12:28:47 -0600239
240 // Set an alignment, and position the magic value.
241 c::set_sim_flash_align(align);
David Brown361be7a2017-03-29 12:28:47 -0600242
Fabio Utzigebeecef2017-07-06 10:36:42 -0300243 mark_upgrade(&mut flash, &images.slot1);
David Brown361be7a2017-03-29 12:28:47 -0600244
Fabio Utzigebeecef2017-07-06 10:36:42 -0300245 // upgrades without fails, counts number of flash operations
246 let total_count = match run_basic_upgrade(&flash, &areadesc, &images) {
247 Ok(v) => v,
248 Err(_) => {
249 self.failures += 1;
250 return;
251 },
David Brown902d6172017-05-05 09:37:41 -0600252 };
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300253
Fabio Utzigebeecef2017-07-06 10:36:42 -0300254 failed |= run_basic_revert(&flash, &areadesc, &images);
255 failed |= run_revert_with_fails(&flash, &areadesc, &images, total_count);
256 failed |= run_perm_with_fails(&flash, &areadesc, &images, total_count);
257 failed |= run_perm_with_random_fails(&flash, &areadesc, &images,
258 total_count, 5);
259 failed |= run_norevert(&flash, &areadesc, &images);
David Brown361be7a2017-03-29 12:28:47 -0600260
Fabio Utzigebeecef2017-07-06 10:36:42 -0300261 //show_flash(&flash);
David Brown361be7a2017-03-29 12:28:47 -0600262
David Brown361be7a2017-03-29 12:28:47 -0600263 if failed {
264 self.failures += 1;
265 } else {
266 self.passes += 1;
267 }
David Brownc638f792017-01-10 12:34:33 -0700268 }
David Brownde7729e2017-01-09 10:41:35 -0700269}
270
Fabio Utzigebeecef2017-07-06 10:36:42 -0300271/// A simple upgrade without forced failures.
272///
273/// Returns the number of flash operations which can later be used to
274/// inject failures at chosen steps.
David Brown7ddec0b2017-07-06 10:47:35 -0600275fn run_basic_upgrade(flash: &SimFlash, areadesc: &AreaDesc, images: &Images)
Fabio Utzigebeecef2017-07-06 10:36:42 -0300276 -> Result<i32, ()> {
277 let (fl, total_count) = try_upgrade(&flash, &areadesc, &images, None);
278 info!("Total flash operation count={}", total_count);
279
280 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
281 warn!("Image mismatch after first boot");
282 Err(())
283 } else {
284 Ok(total_count)
285 }
286}
287
David Brown7ddec0b2017-07-06 10:47:35 -0600288fn run_basic_revert(flash: &SimFlash, areadesc: &AreaDesc, images: &Images) -> bool {
Fabio Utzigebeecef2017-07-06 10:36:42 -0300289 let mut fails = 0;
290
291 if Caps::SwapUpgrade.present() {
292 for count in 2 .. 5 {
293 info!("Try revert: {}", count);
294 let fl = try_revert(&flash, &areadesc, count);
295 if !verify_image(&fl, images.slot0.base_off, &images.primary) {
296 warn!("Revert failure on count {}", count);
297 fails += 1;
298 }
299 }
300 }
301
302 fails > 0
303}
304
David Brown7ddec0b2017-07-06 10:47:35 -0600305fn run_perm_with_fails(flash: &SimFlash, areadesc: &AreaDesc, images: &Images,
Fabio Utzigebeecef2017-07-06 10:36:42 -0300306 total_flash_ops: i32) -> bool {
307 let mut fails = 0;
308
309 // Let's try an image halfway through.
310 for i in 1 .. total_flash_ops {
311 info!("Try interruption at {}", i);
312 let (fl, count) = try_upgrade(&flash, &areadesc, &images, Some(i));
313 info!("Second boot, count={}", count);
314 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
315 warn!("FAIL at step {} of {}", i, total_flash_ops);
316 fails += 1;
317 }
318
319 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
320 COPY_DONE) {
321 warn!("Mismatched trailer for Slot 0");
322 fails += 1;
323 }
324
325 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
326 UNSET) {
327 warn!("Mismatched trailer for Slot 1");
328 fails += 1;
329 }
330
331 if Caps::SwapUpgrade.present() {
332 if !verify_image(&fl, images.slot1.base_off, &images.primary) {
333 warn!("Slot 1 FAIL at step {} of {}", i, total_flash_ops);
334 fails += 1;
335 }
336 }
337 }
338
339 info!("{} out of {} failed {:.2}%", fails, total_flash_ops,
340 fails as f32 * 100.0 / total_flash_ops as f32);
341
342 fails > 0
343}
344
David Brown7ddec0b2017-07-06 10:47:35 -0600345fn run_perm_with_random_fails(flash: &SimFlash, areadesc: &AreaDesc,
Fabio Utzigebeecef2017-07-06 10:36:42 -0300346 images: &Images, total_flash_ops: i32,
347 total_fails: usize) -> bool {
348 let mut fails = 0;
349 let (fl, total_counts) = try_random_fails(&flash, &areadesc, &images,
350 total_flash_ops, total_fails);
351 info!("Random interruptions at reset points={:?}", total_counts);
352
353 let slot0_ok = verify_image(&fl, images.slot0.base_off, &images.upgrade);
354 let slot1_ok = if Caps::SwapUpgrade.present() {
355 verify_image(&fl, images.slot1.base_off, &images.primary)
356 } else {
357 true
358 };
359 if !slot0_ok || !slot1_ok {
360 error!("Image mismatch after random interrupts: slot0={} slot1={}",
361 if slot0_ok { "ok" } else { "fail" },
362 if slot1_ok { "ok" } else { "fail" });
363 fails += 1;
364 }
365 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
366 COPY_DONE) {
367 error!("Mismatched trailer for Slot 0");
368 fails += 1;
369 }
370 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
371 UNSET) {
372 error!("Mismatched trailer for Slot 1");
373 fails += 1;
374 }
375
376 fails > 0
377}
378
David Brown7ddec0b2017-07-06 10:47:35 -0600379fn run_revert_with_fails(flash: &SimFlash, areadesc: &AreaDesc, images: &Images,
Fabio Utzigebeecef2017-07-06 10:36:42 -0300380 total_count: i32) -> bool {
381 let mut fails = 0;
382
383 if Caps::SwapUpgrade.present() {
384 for i in 1 .. (total_count - 1) {
385 info!("Try interruption at {}", i);
386 if try_revert_with_fail_at(&flash, &areadesc, &images, i) {
387 fails += 1;
388 }
389 }
390 }
391
392 fails > 0
393}
394
David Brown7ddec0b2017-07-06 10:47:35 -0600395fn run_norevert(flash: &SimFlash, areadesc: &AreaDesc, images: &Images) -> bool {
Fabio Utzigebeecef2017-07-06 10:36:42 -0300396 let mut fl = flash.clone();
397 let mut fails = 0;
398
399 info!("Try norevert");
400 c::set_flash_counter(0);
401
402 // First do a normal upgrade...
403 if c::boot_go(&mut fl, &areadesc) != 0 {
404 warn!("Failed first boot");
405 fails += 1;
406 }
407
408 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
409 warn!("Slot 0 image verification FAIL");
410 fails += 1;
411 }
412 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, UNSET,
413 COPY_DONE) {
414 warn!("Mismatched trailer for Slot 0");
415 fails += 1;
416 }
417 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
418 UNSET) {
419 warn!("Mismatched trailer for Slot 1");
420 fails += 1;
421 }
422
423 // Marks image in slot0 as permanent, no revert should happen...
424 mark_permanent_upgrade(&mut fl, &images.slot0);
425
426 if c::boot_go(&mut fl, &areadesc) != 0 {
427 warn!("Failed second boot");
428 fails += 1;
429 }
430
431 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
432 COPY_DONE) {
433 warn!("Mismatched trailer for Slot 0");
434 fails += 1;
435 }
436 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
437 warn!("Failed image verification");
438 fails += 1;
439 }
440
441 fails > 0
442}
443
444/// Test a boot, optionally stopping after 'n' flash options. Returns a count
445/// of the number of flash operations done total.
David Brown7ddec0b2017-07-06 10:47:35 -0600446fn try_upgrade(flash: &SimFlash, areadesc: &AreaDesc, images: &Images,
447 stop: Option<i32>) -> (SimFlash, i32) {
David Brownde7729e2017-01-09 10:41:35 -0700448 // Clone the flash to have a new copy.
449 let mut fl = flash.clone();
450
Fabio Utzigebeecef2017-07-06 10:36:42 -0300451 mark_permanent_upgrade(&mut fl, &images.slot1);
Fabio Utzig57652312017-04-25 19:54:26 -0300452
David Brownde7729e2017-01-09 10:41:35 -0700453 c::set_flash_counter(stop.unwrap_or(0));
Fabio Utzigebeecef2017-07-06 10:36:42 -0300454 let (first_interrupted, count) = match c::boot_go(&mut fl, &areadesc) {
David Brownde7729e2017-01-09 10:41:35 -0700455 -0x13579 => (true, stop.unwrap()),
456 0 => (false, -c::get_flash_counter()),
457 x => panic!("Unknown return: {}", x),
458 };
459 c::set_flash_counter(0);
460
461 if first_interrupted {
462 // fl.dump();
463 match c::boot_go(&mut fl, &areadesc) {
464 -0x13579 => panic!("Shouldn't stop again"),
465 0 => (),
466 x => panic!("Unknown return: {}", x),
467 }
468 }
469
Fabio Utzigebeecef2017-07-06 10:36:42 -0300470 (fl, count - c::get_flash_counter())
David Brownde7729e2017-01-09 10:41:35 -0700471}
472
David Brown7ddec0b2017-07-06 10:47:35 -0600473fn try_revert(flash: &SimFlash, areadesc: &AreaDesc, count: usize) -> SimFlash {
David Brownde7729e2017-01-09 10:41:35 -0700474 let mut fl = flash.clone();
475 c::set_flash_counter(0);
476
David Brown163ab232017-01-23 15:48:35 -0700477 // fl.write_file("image0.bin").unwrap();
478 for i in 0 .. count {
479 info!("Running boot pass {}", i + 1);
David Brownc638f792017-01-10 12:34:33 -0700480 assert_eq!(c::boot_go(&mut fl, &areadesc), 0);
481 }
David Brownde7729e2017-01-09 10:41:35 -0700482 fl
483}
484
David Brown7ddec0b2017-07-06 10:47:35 -0600485fn try_revert_with_fail_at(flash: &SimFlash, areadesc: &AreaDesc, images: &Images,
Fabio Utzigebeecef2017-07-06 10:36:42 -0300486 stop: i32) -> bool {
David Brownde7729e2017-01-09 10:41:35 -0700487 let mut fl = flash.clone();
Fabio Utzigebeecef2017-07-06 10:36:42 -0300488 let mut x: i32;
489 let mut fails = 0;
David Brownde7729e2017-01-09 10:41:35 -0700490
Fabio Utzigebeecef2017-07-06 10:36:42 -0300491 c::set_flash_counter(stop);
492 x = c::boot_go(&mut fl, &areadesc);
493 if x != -0x13579 {
494 warn!("Should have stopped at interruption point");
495 fails += 1;
496 }
497
498 if !verify_trailer(&fl, images.slot0.trailer_off, None, None, UNSET) {
499 warn!("copy_done should be unset");
500 fails += 1;
501 }
502
503 c::set_flash_counter(0);
504 x = c::boot_go(&mut fl, &areadesc);
505 if x != 0 {
506 warn!("Should have finished upgrade");
507 fails += 1;
508 }
509
510 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
511 warn!("Image in slot 0 before revert is invalid at stop={}", stop);
512 fails += 1;
513 }
514 if !verify_image(&fl, images.slot1.base_off, &images.primary) {
515 warn!("Image in slot 1 before revert is invalid at stop={}", stop);
516 fails += 1;
517 }
518 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, UNSET,
519 COPY_DONE) {
520 warn!("Mismatched trailer for Slot 0 before revert");
521 fails += 1;
522 }
523 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
524 UNSET) {
525 warn!("Mismatched trailer for Slot 1 before revert");
526 fails += 1;
527 }
528
529 // Do Revert
530 c::set_flash_counter(0);
531 x = c::boot_go(&mut fl, &areadesc);
532 if x != 0 {
533 warn!("Should have finished a revert");
534 fails += 1;
535 }
536
537 if !verify_image(&fl, images.slot0.base_off, &images.primary) {
538 warn!("Image in slot 0 after revert is invalid at stop={}", stop);
539 fails += 1;
540 }
541 if !verify_image(&fl, images.slot1.base_off, &images.upgrade) {
542 warn!("Image in slot 1 after revert is invalid at stop={}", stop);
543 fails += 1;
544 }
545 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
546 COPY_DONE) {
547 warn!("Mismatched trailer for Slot 1 after revert");
548 fails += 1;
549 }
550 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
551 UNSET) {
552 warn!("Mismatched trailer for Slot 1 after revert");
553 fails += 1;
554 }
555
556 fails > 0
David Brownde7729e2017-01-09 10:41:35 -0700557}
558
David Brown7ddec0b2017-07-06 10:47:35 -0600559fn try_random_fails(flash: &SimFlash, areadesc: &AreaDesc, images: &Images,
560 total_ops: i32, count: usize) -> (SimFlash, Vec<i32>) {
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300561 let mut fl = flash.clone();
562
Fabio Utzigebeecef2017-07-06 10:36:42 -0300563 mark_permanent_upgrade(&mut fl, &images.slot1);
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300564
565 let mut rng = rand::thread_rng();
Fabio Utzig57652312017-04-25 19:54:26 -0300566 let mut resets = vec![0i32; count];
567 let mut remaining_ops = total_ops;
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300568 for i in 0 .. count {
Fabio Utzig57652312017-04-25 19:54:26 -0300569 let ops = Range::new(1, remaining_ops / 2);
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300570 let reset_counter = ops.ind_sample(&mut rng);
571 c::set_flash_counter(reset_counter);
572 match c::boot_go(&mut fl, &areadesc) {
573 0 | -0x13579 => (),
574 x => panic!("Unknown return: {}", x),
575 }
Fabio Utzig57652312017-04-25 19:54:26 -0300576 remaining_ops -= reset_counter;
577 resets[i] = reset_counter;
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300578 }
579
580 c::set_flash_counter(0);
581 match c::boot_go(&mut fl, &areadesc) {
582 -0x13579 => panic!("Should not be have been interrupted!"),
583 0 => (),
584 x => panic!("Unknown return: {}", x),
585 }
586
Fabio Utzig57652312017-04-25 19:54:26 -0300587 (fl, resets)
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300588}
589
David Brownde7729e2017-01-09 10:41:35 -0700590/// Show the flash layout.
591#[allow(dead_code)]
592fn show_flash(flash: &Flash) {
593 println!("---- Flash configuration ----");
594 for sector in flash.sector_iter() {
Fabio Utzigebeecef2017-07-06 10:36:42 -0300595 println!(" {:3}: 0x{:08x}, 0x{:08x}",
David Brownde7729e2017-01-09 10:41:35 -0700596 sector.num, sector.base, sector.size);
597 }
598 println!("");
599}
600
601/// Install a "program" into the given image. This fakes the image header, or at least all of the
602/// fields used by the given code. Returns a copy of the image that was written.
603fn install_image(flash: &mut Flash, offset: usize, len: usize) -> Vec<u8> {
604 let offset0 = offset;
605
David Brown7e701d82017-07-11 13:24:25 -0600606 let mut tlv = TlvGen::new_rsa_pss();
David Brown187dd882017-07-11 11:15:23 -0600607
David Brownde7729e2017-01-09 10:41:35 -0700608 // Generate a boot header. Note that the size doesn't include the header.
609 let header = ImageHeader {
610 magic: 0x96f3b83c,
David Brown187dd882017-07-11 11:15:23 -0600611 tlv_size: tlv.get_size(),
David Brownde7729e2017-01-09 10:41:35 -0700612 _pad1: 0,
613 hdr_size: 32,
614 key_id: 0,
615 _pad2: 0,
616 img_size: len as u32,
David Brown187dd882017-07-11 11:15:23 -0600617 flags: tlv.get_flags(),
David Brownde7729e2017-01-09 10:41:35 -0700618 ver: ImageVersion {
David Browne380fa62017-01-23 15:49:09 -0700619 major: (offset / (128 * 1024)) as u8,
David Brownde7729e2017-01-09 10:41:35 -0700620 minor: 0,
621 revision: 1,
David Browne380fa62017-01-23 15:49:09 -0700622 build_num: offset as u32,
David Brownde7729e2017-01-09 10:41:35 -0700623 },
624 _pad3: 0,
625 };
626
627 let b_header = header.as_raw();
David Brown187dd882017-07-11 11:15:23 -0600628 tlv.add_bytes(&b_header);
David Brownde7729e2017-01-09 10:41:35 -0700629 /*
630 let b_header = unsafe { slice::from_raw_parts(&header as *const _ as *const u8,
631 mem::size_of::<ImageHeader>()) };
632 */
633 assert_eq!(b_header.len(), 32);
634 flash.write(offset, &b_header).unwrap();
635 let offset = offset + b_header.len();
636
637 // The core of the image itself is just pseudorandom data.
638 let mut buf = vec![0; len];
639 splat(&mut buf, offset);
David Brown187dd882017-07-11 11:15:23 -0600640 tlv.add_bytes(&buf);
641
642 // Get and append the TLV itself.
643 buf.append(&mut tlv.make_tlv());
644
645 // Pad the block to a flash alignment (8 bytes).
646 while buf.len() % 8 != 0 {
647 buf.push(0xFF);
648 }
649
David Brownde7729e2017-01-09 10:41:35 -0700650 flash.write(offset, &buf).unwrap();
651 let offset = offset + buf.len();
652
653 // Copy out the image so that we can verify that the image was installed correctly later.
654 let mut copy = vec![0u8; offset - offset0];
655 flash.read(offset0, &mut copy).unwrap();
656
657 copy
658}
659
660/// Verify that given image is present in the flash at the given offset.
661fn verify_image(flash: &Flash, offset: usize, buf: &[u8]) -> bool {
662 let mut copy = vec![0u8; buf.len()];
663 flash.read(offset, &mut copy).unwrap();
664
665 if buf != &copy[..] {
666 for i in 0 .. buf.len() {
667 if buf[i] != copy[i] {
David Brown4440af82017-01-09 12:15:05 -0700668 info!("First failure at {:#x}", offset + i);
David Brownde7729e2017-01-09 10:41:35 -0700669 break;
670 }
671 }
672 false
673 } else {
674 true
675 }
676}
677
Fabio Utzigebeecef2017-07-06 10:36:42 -0300678fn verify_trailer(flash: &Flash, offset: usize,
679 magic: Option<&[u8]>, image_ok: Option<u8>,
680 copy_done: Option<u8>) -> bool {
681 let mut copy = vec![0u8; c::boot_magic_sz() + c::boot_max_align() * 2];
682 let mut failed = false;
683
684 flash.read(offset, &mut copy).unwrap();
685
686 failed |= match magic {
687 Some(v) => {
688 if &copy[16..] != v {
689 warn!("\"magic\" mismatch at {:#x}", offset);
690 true
691 } else {
692 false
693 }
694 },
695 None => false,
696 };
697
698 failed |= match image_ok {
699 Some(v) => {
700 if copy[8] != v {
701 warn!("\"image_ok\" mismatch at {:#x}", offset);
702 true
703 } else {
704 false
705 }
706 },
707 None => false,
708 };
709
710 failed |= match copy_done {
711 Some(v) => {
712 if copy[0] != v {
713 warn!("\"copy_done\" mismatch at {:#x}", offset);
714 true
715 } else {
716 false
717 }
718 },
719 None => false,
720 };
721
722 !failed
723}
724
David Brownde7729e2017-01-09 10:41:35 -0700725/// The image header
726#[repr(C)]
727pub struct ImageHeader {
728 magic: u32,
729 tlv_size: u16,
730 key_id: u8,
731 _pad1: u8,
732 hdr_size: u16,
733 _pad2: u16,
734 img_size: u32,
735 flags: u32,
736 ver: ImageVersion,
737 _pad3: u32,
738}
739
740impl AsRaw for ImageHeader {}
741
742#[repr(C)]
743pub struct ImageVersion {
744 major: u8,
745 minor: u8,
746 revision: u16,
747 build_num: u32,
748}
749
Fabio Utzigebeecef2017-07-06 10:36:42 -0300750struct SlotInfo {
751 base_off: usize,
752 trailer_off: usize,
753}
754
755struct Images {
756 slot0: SlotInfo,
757 slot1: SlotInfo,
758 primary: Vec<u8>,
759 upgrade: Vec<u8>,
760}
761
762const MAGIC_VALID: Option<&[u8]> = Some(&[0x77, 0xc2, 0x95, 0xf3,
763 0x60, 0xd2, 0xef, 0x7f,
764 0x35, 0x52, 0x50, 0x0f,
765 0x2c, 0xb6, 0x79, 0x80]);
766const MAGIC_UNSET: Option<&[u8]> = Some(&[0xff; 16]);
767
768const COPY_DONE: Option<u8> = Some(1);
769const IMAGE_OK: Option<u8> = Some(1);
770const UNSET: Option<u8> = Some(0xff);
771
David Brownde7729e2017-01-09 10:41:35 -0700772/// Write out the magic so that the loader tries doing an upgrade.
Fabio Utzigebeecef2017-07-06 10:36:42 -0300773fn mark_upgrade(flash: &mut Flash, slot: &SlotInfo) {
774 let offset = slot.trailer_off + c::boot_max_align() * 2;
775 flash.write(offset, MAGIC_VALID.unwrap()).unwrap();
776}
777
778/// Writes the image_ok flag which, guess what, tells the bootloader
779/// the this image is ok (not a test, and no revert is to be performed).
780fn mark_permanent_upgrade(flash: &mut Flash, slot: &SlotInfo) {
781 let ok = [1u8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
782 let align = c::get_sim_flash_align() as usize;
783 let off = slot.trailer_off + c::boot_max_align();
784 flash.write(off, &ok[..align]).unwrap();
David Brownde7729e2017-01-09 10:41:35 -0700785}
786
787// Drop some pseudo-random gibberish onto the data.
788fn splat(data: &mut [u8], seed: usize) {
789 let seed_block = [0x135782ea, 0x92184728, data.len() as u32, seed as u32];
790 let mut rng: XorShiftRng = SeedableRng::from_seed(seed_block);
791 rng.fill_bytes(data);
792}
793
794/// Return a read-only view into the raw bytes of this object
795trait AsRaw : Sized {
796 fn as_raw<'a>(&'a self) -> &'a [u8] {
797 unsafe { slice::from_raw_parts(self as *const _ as *const u8,
798 mem::size_of::<Self>()) }
799 }
800}
801
802fn show_sizes() {
803 // This isn't panic safe.
804 let old_align = c::get_sim_flash_align();
805 for min in &[1, 2, 4, 8] {
806 c::set_sim_flash_align(*min);
807 let msize = c::boot_trailer_sz();
808 println!("{:2}: {} (0x{:x})", min, msize, msize);
809 }
810 c::set_sim_flash_align(old_align);
811}