blob: 89be7ff0fcfd7ed9bee3b5658847ddc8de64f060 [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;
7extern crate rand;
8extern crate rustc_serialize;
David Brown2cbc4702017-07-06 14:18:58 -06009extern crate simflash;
David Brownde7729e2017-01-09 10:41:35 -070010
11use docopt::Docopt;
David Brown4cb26232017-04-11 08:15:18 -060012use rand::{Rng, SeedableRng, XorShiftRng};
Fabio Utzigbb5635e2017-04-10 09:07:02 -030013use rand::distributions::{IndependentSample, Range};
David Brownde7729e2017-01-09 10:41:35 -070014use rustc_serialize::{Decodable, Decoder};
David Browna3b93cf2017-03-29 12:41:26 -060015use std::fmt;
David Brownde7729e2017-01-09 10:41:35 -070016use std::mem;
David Brown361be7a2017-03-29 12:28:47 -060017use std::process;
David Brownde7729e2017-01-09 10:41:35 -070018use std::slice;
19
20mod area;
21mod c;
David Brownde7729e2017-01-09 10:41:35 -070022pub mod api;
David Brown902d6172017-05-05 09:37:41 -060023mod caps;
David Brown187dd882017-07-11 11:15:23 -060024mod tlv;
David Brownde7729e2017-01-09 10:41:35 -070025
David Brown2cbc4702017-07-06 14:18:58 -060026use simflash::{Flash, SimFlash};
David Brownde7729e2017-01-09 10:41:35 -070027use area::{AreaDesc, FlashId};
David Brown902d6172017-05-05 09:37:41 -060028use caps::Caps;
David Brown187dd882017-07-11 11:15:23 -060029use tlv::TlvGen;
David Brownde7729e2017-01-09 10:41:35 -070030
31const USAGE: &'static str = "
32Mcuboot simulator
33
34Usage:
35 bootsim sizes
36 bootsim run --device TYPE [--align SIZE]
David Browna3b93cf2017-03-29 12:41:26 -060037 bootsim runall
David Brownde7729e2017-01-09 10:41:35 -070038 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, RustcDecodable)]
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,
David Browna3b93cf2017-03-29 12:41:26 -060056 cmd_runall: bool,
David Brownde7729e2017-01-09 10:41:35 -070057}
58
David Browna3b93cf2017-03-29 12:41:26 -060059#[derive(Copy, Clone, Debug, RustcDecodable)]
David Brown07fb8fa2017-03-20 12:40:57 -060060enum DeviceName { Stm32f4, K64f, K64fBig, Nrf52840 }
David Brownde7729e2017-01-09 10:41:35 -070061
David Browna3b93cf2017-03-29 12:41:26 -060062static ALL_DEVICES: &'static [DeviceName] = &[
63 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
David Brownde7729e2017-01-09 10:41:35 -070081#[derive(Debug)]
82struct AlignArg(u8);
83
84impl Decodable for AlignArg {
85 // Decode the alignment ourselves, to restrict it to the valid possible alignments.
86 fn decode<D: Decoder>(d: &mut D) -> Result<AlignArg, D::Error> {
87 let m = d.read_u8()?;
88 match m {
89 1 | 2 | 4 | 8 => Ok(AlignArg(m)),
90 _ => Err(d.error("Invalid alignment")),
91 }
92 }
93}
94
95fn main() {
David Brown4440af82017-01-09 12:15:05 -070096 env_logger::init().unwrap();
97
David Brownde7729e2017-01-09 10:41:35 -070098 let args: Args = Docopt::new(USAGE)
99 .and_then(|d| d.decode())
100 .unwrap_or_else(|e| e.exit());
101 // println!("args: {:#?}", args);
102
103 if args.cmd_sizes {
104 show_sizes();
105 return;
106 }
107
David Brown361be7a2017-03-29 12:28:47 -0600108 let mut status = RunStatus::new();
David Browna3b93cf2017-03-29 12:41:26 -0600109 if args.cmd_run {
David Brown361be7a2017-03-29 12:28:47 -0600110
David Browna3b93cf2017-03-29 12:41:26 -0600111 let align = args.flag_align.map(|x| x.0).unwrap_or(1);
David Brown562a7a02017-01-23 11:19:03 -0700112
Fabio Utzigebeecef2017-07-06 10:36:42 -0300113
David Browna3b93cf2017-03-29 12:41:26 -0600114 let device = match args.flag_device {
115 None => panic!("Missing mandatory device argument"),
116 Some(dev) => dev,
117 };
David Brownde7729e2017-01-09 10:41:35 -0700118
David Browna3b93cf2017-03-29 12:41:26 -0600119 status.run_single(device, align);
120 }
121
122 if args.cmd_runall {
123 for &dev in ALL_DEVICES {
124 for &align in &[1, 2, 4, 8] {
125 status.run_single(dev, align);
126 }
127 }
128 }
David Brown5c6b6792017-03-20 12:51:28 -0600129
David Brown361be7a2017-03-29 12:28:47 -0600130 if status.failures > 0 {
David Brown187dd882017-07-11 11:15:23 -0600131 error!("{} Tests ran with {} failures", status.failures + status.passes, status.failures);
David Brown361be7a2017-03-29 12:28:47 -0600132 process::exit(1);
133 } else {
134 warn!("{} Tests ran successfully", status.passes);
135 process::exit(0);
136 }
137}
David Brown5c6b6792017-03-20 12:51:28 -0600138
David Brown361be7a2017-03-29 12:28:47 -0600139struct RunStatus {
140 failures: usize,
141 passes: usize,
142}
David Brownde7729e2017-01-09 10:41:35 -0700143
David Brown361be7a2017-03-29 12:28:47 -0600144impl RunStatus {
145 fn new() -> RunStatus {
146 RunStatus {
147 failures: 0,
148 passes: 0,
David Brownde7729e2017-01-09 10:41:35 -0700149 }
150 }
David Brownde7729e2017-01-09 10:41:35 -0700151
David Brown361be7a2017-03-29 12:28:47 -0600152 fn run_single(&mut self, device: DeviceName, align: u8) {
David Browna3b93cf2017-03-29 12:41:26 -0600153 warn!("Running on device {} with alignment {}", device, align);
154
David Brown361be7a2017-03-29 12:28:47 -0600155 let (mut flash, areadesc) = match device {
156 DeviceName::Stm32f4 => {
157 // STM style flash. Large sectors, with a large scratch area.
David Brown7ddec0b2017-07-06 10:47:35 -0600158 let flash = SimFlash::new(vec![16 * 1024, 16 * 1024, 16 * 1024, 16 * 1024,
159 64 * 1024,
160 128 * 1024, 128 * 1024, 128 * 1024],
161 align as usize);
David Brown361be7a2017-03-29 12:28:47 -0600162 let mut areadesc = AreaDesc::new(&flash);
163 areadesc.add_image(0x020000, 0x020000, FlashId::Image0);
164 areadesc.add_image(0x040000, 0x020000, FlashId::Image1);
165 areadesc.add_image(0x060000, 0x020000, FlashId::ImageScratch);
166 (flash, areadesc)
167 }
168 DeviceName::K64f => {
169 // NXP style flash. Small sectors, one small sector for scratch.
David Brown7ddec0b2017-07-06 10:47:35 -0600170 let flash = SimFlash::new(vec![4096; 128], align as usize);
David Brown361be7a2017-03-29 12:28:47 -0600171
172 let mut areadesc = AreaDesc::new(&flash);
173 areadesc.add_image(0x020000, 0x020000, FlashId::Image0);
174 areadesc.add_image(0x040000, 0x020000, FlashId::Image1);
175 areadesc.add_image(0x060000, 0x001000, FlashId::ImageScratch);
176 (flash, areadesc)
177 }
178 DeviceName::K64fBig => {
179 // Simulating an STM style flash on top of an NXP style flash. Underlying flash device
180 // uses small sectors, but we tell the bootloader they are large.
David Brown7ddec0b2017-07-06 10:47:35 -0600181 let flash = SimFlash::new(vec![4096; 128], align as usize);
David Brown361be7a2017-03-29 12:28:47 -0600182
183 let mut areadesc = AreaDesc::new(&flash);
184 areadesc.add_simple_image(0x020000, 0x020000, FlashId::Image0);
185 areadesc.add_simple_image(0x040000, 0x020000, FlashId::Image1);
186 areadesc.add_simple_image(0x060000, 0x020000, FlashId::ImageScratch);
187 (flash, areadesc)
188 }
189 DeviceName::Nrf52840 => {
190 // Simulating the flash on the nrf52840 with partitions set up so that the scratch size
191 // does not divide into the image size.
David Brown7ddec0b2017-07-06 10:47:35 -0600192 let flash = SimFlash::new(vec![4096; 128], align as usize);
David Brown361be7a2017-03-29 12:28:47 -0600193
194 let mut areadesc = AreaDesc::new(&flash);
195 areadesc.add_image(0x008000, 0x034000, FlashId::Image0);
196 areadesc.add_image(0x03c000, 0x034000, FlashId::Image1);
197 areadesc.add_image(0x070000, 0x00d000, FlashId::ImageScratch);
198 (flash, areadesc)
199 }
200 };
201
202 let (slot0_base, slot0_len) = areadesc.find(FlashId::Image0);
203 let (slot1_base, slot1_len) = areadesc.find(FlashId::Image1);
204 let (scratch_base, _) = areadesc.find(FlashId::ImageScratch);
205
206 // Code below assumes that the slots are consecutive.
207 assert_eq!(slot1_base, slot0_base + slot0_len);
208 assert_eq!(scratch_base, slot1_base + slot1_len);
209
Fabio Utzigebeecef2017-07-06 10:36:42 -0300210 let offset_from_end = c::boot_magic_sz() + c::boot_max_align() * 2;
211
David Brown361be7a2017-03-29 12:28:47 -0600212 // println!("Areas: {:#?}", areadesc.get_c());
213
214 // Install the boot trailer signature, so that the code will start an upgrade.
215 // TODO: This must be a multiple of flash alignment, add support for an image that is smaller,
216 // and just gets padded.
David Brown361be7a2017-03-29 12:28:47 -0600217
Fabio Utzigebeecef2017-07-06 10:36:42 -0300218 // Create original and upgrade images
219 let slot0 = SlotInfo {
220 base_off: slot0_base as usize,
221 trailer_off: slot1_base - offset_from_end,
222 };
223
224 let slot1 = SlotInfo {
225 base_off: slot1_base as usize,
226 trailer_off: scratch_base - offset_from_end,
227 };
228
229 let images = Images {
230 slot0: slot0,
231 slot1: slot1,
232 primary: install_image(&mut flash, slot0_base, 32784),
233 upgrade: install_image(&mut flash, slot1_base, 41928),
234 };
235
236 let mut failed = false;
David Brown361be7a2017-03-29 12:28:47 -0600237
238 // Set an alignment, and position the magic value.
239 c::set_sim_flash_align(align);
David Brown361be7a2017-03-29 12:28:47 -0600240
Fabio Utzigebeecef2017-07-06 10:36:42 -0300241 mark_upgrade(&mut flash, &images.slot1);
David Brown361be7a2017-03-29 12:28:47 -0600242
Fabio Utzigebeecef2017-07-06 10:36:42 -0300243 // upgrades without fails, counts number of flash operations
244 let total_count = match run_basic_upgrade(&flash, &areadesc, &images) {
245 Ok(v) => v,
246 Err(_) => {
247 self.failures += 1;
248 return;
249 },
David Brown902d6172017-05-05 09:37:41 -0600250 };
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300251
Fabio Utzigebeecef2017-07-06 10:36:42 -0300252 failed |= run_basic_revert(&flash, &areadesc, &images);
253 failed |= run_revert_with_fails(&flash, &areadesc, &images, total_count);
254 failed |= run_perm_with_fails(&flash, &areadesc, &images, total_count);
255 failed |= run_perm_with_random_fails(&flash, &areadesc, &images,
256 total_count, 5);
257 failed |= run_norevert(&flash, &areadesc, &images);
David Brown361be7a2017-03-29 12:28:47 -0600258
Fabio Utzigebeecef2017-07-06 10:36:42 -0300259 //show_flash(&flash);
David Brown361be7a2017-03-29 12:28:47 -0600260
David Brown361be7a2017-03-29 12:28:47 -0600261 if failed {
262 self.failures += 1;
263 } else {
264 self.passes += 1;
265 }
David Brownc638f792017-01-10 12:34:33 -0700266 }
David Brownde7729e2017-01-09 10:41:35 -0700267}
268
Fabio Utzigebeecef2017-07-06 10:36:42 -0300269/// A simple upgrade without forced failures.
270///
271/// Returns the number of flash operations which can later be used to
272/// inject failures at chosen steps.
David Brown7ddec0b2017-07-06 10:47:35 -0600273fn run_basic_upgrade(flash: &SimFlash, areadesc: &AreaDesc, images: &Images)
Fabio Utzigebeecef2017-07-06 10:36:42 -0300274 -> Result<i32, ()> {
275 let (fl, total_count) = try_upgrade(&flash, &areadesc, &images, None);
276 info!("Total flash operation count={}", total_count);
277
278 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
279 warn!("Image mismatch after first boot");
280 Err(())
281 } else {
282 Ok(total_count)
283 }
284}
285
David Brown7ddec0b2017-07-06 10:47:35 -0600286fn run_basic_revert(flash: &SimFlash, areadesc: &AreaDesc, images: &Images) -> bool {
Fabio Utzigebeecef2017-07-06 10:36:42 -0300287 let mut fails = 0;
288
289 if Caps::SwapUpgrade.present() {
290 for count in 2 .. 5 {
291 info!("Try revert: {}", count);
292 let fl = try_revert(&flash, &areadesc, count);
293 if !verify_image(&fl, images.slot0.base_off, &images.primary) {
294 warn!("Revert failure on count {}", count);
295 fails += 1;
296 }
297 }
298 }
299
300 fails > 0
301}
302
David Brown7ddec0b2017-07-06 10:47:35 -0600303fn run_perm_with_fails(flash: &SimFlash, areadesc: &AreaDesc, images: &Images,
Fabio Utzigebeecef2017-07-06 10:36:42 -0300304 total_flash_ops: i32) -> bool {
305 let mut fails = 0;
306
307 // Let's try an image halfway through.
308 for i in 1 .. total_flash_ops {
309 info!("Try interruption at {}", i);
310 let (fl, count) = try_upgrade(&flash, &areadesc, &images, Some(i));
311 info!("Second boot, count={}", count);
312 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
313 warn!("FAIL at step {} of {}", i, total_flash_ops);
314 fails += 1;
315 }
316
317 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
318 COPY_DONE) {
319 warn!("Mismatched trailer for Slot 0");
320 fails += 1;
321 }
322
323 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
324 UNSET) {
325 warn!("Mismatched trailer for Slot 1");
326 fails += 1;
327 }
328
329 if Caps::SwapUpgrade.present() {
330 if !verify_image(&fl, images.slot1.base_off, &images.primary) {
331 warn!("Slot 1 FAIL at step {} of {}", i, total_flash_ops);
332 fails += 1;
333 }
334 }
335 }
336
337 info!("{} out of {} failed {:.2}%", fails, total_flash_ops,
338 fails as f32 * 100.0 / total_flash_ops as f32);
339
340 fails > 0
341}
342
David Brown7ddec0b2017-07-06 10:47:35 -0600343fn run_perm_with_random_fails(flash: &SimFlash, areadesc: &AreaDesc,
Fabio Utzigebeecef2017-07-06 10:36:42 -0300344 images: &Images, total_flash_ops: i32,
345 total_fails: usize) -> bool {
346 let mut fails = 0;
347 let (fl, total_counts) = try_random_fails(&flash, &areadesc, &images,
348 total_flash_ops, total_fails);
349 info!("Random interruptions at reset points={:?}", total_counts);
350
351 let slot0_ok = verify_image(&fl, images.slot0.base_off, &images.upgrade);
352 let slot1_ok = if Caps::SwapUpgrade.present() {
353 verify_image(&fl, images.slot1.base_off, &images.primary)
354 } else {
355 true
356 };
357 if !slot0_ok || !slot1_ok {
358 error!("Image mismatch after random interrupts: slot0={} slot1={}",
359 if slot0_ok { "ok" } else { "fail" },
360 if slot1_ok { "ok" } else { "fail" });
361 fails += 1;
362 }
363 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
364 COPY_DONE) {
365 error!("Mismatched trailer for Slot 0");
366 fails += 1;
367 }
368 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
369 UNSET) {
370 error!("Mismatched trailer for Slot 1");
371 fails += 1;
372 }
373
374 fails > 0
375}
376
David Brown7ddec0b2017-07-06 10:47:35 -0600377fn run_revert_with_fails(flash: &SimFlash, areadesc: &AreaDesc, images: &Images,
Fabio Utzigebeecef2017-07-06 10:36:42 -0300378 total_count: i32) -> bool {
379 let mut fails = 0;
380
381 if Caps::SwapUpgrade.present() {
382 for i in 1 .. (total_count - 1) {
383 info!("Try interruption at {}", i);
384 if try_revert_with_fail_at(&flash, &areadesc, &images, i) {
385 fails += 1;
386 }
387 }
388 }
389
390 fails > 0
391}
392
David Brown7ddec0b2017-07-06 10:47:35 -0600393fn run_norevert(flash: &SimFlash, areadesc: &AreaDesc, images: &Images) -> bool {
Fabio Utzigebeecef2017-07-06 10:36:42 -0300394 let mut fl = flash.clone();
395 let mut fails = 0;
396
397 info!("Try norevert");
398 c::set_flash_counter(0);
399
400 // First do a normal upgrade...
401 if c::boot_go(&mut fl, &areadesc) != 0 {
402 warn!("Failed first boot");
403 fails += 1;
404 }
405
406 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
407 warn!("Slot 0 image verification FAIL");
408 fails += 1;
409 }
410 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, UNSET,
411 COPY_DONE) {
412 warn!("Mismatched trailer for Slot 0");
413 fails += 1;
414 }
415 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
416 UNSET) {
417 warn!("Mismatched trailer for Slot 1");
418 fails += 1;
419 }
420
421 // Marks image in slot0 as permanent, no revert should happen...
422 mark_permanent_upgrade(&mut fl, &images.slot0);
423
424 if c::boot_go(&mut fl, &areadesc) != 0 {
425 warn!("Failed second boot");
426 fails += 1;
427 }
428
429 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
430 COPY_DONE) {
431 warn!("Mismatched trailer for Slot 0");
432 fails += 1;
433 }
434 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
435 warn!("Failed image verification");
436 fails += 1;
437 }
438
439 fails > 0
440}
441
442/// Test a boot, optionally stopping after 'n' flash options. Returns a count
443/// of the number of flash operations done total.
David Brown7ddec0b2017-07-06 10:47:35 -0600444fn try_upgrade(flash: &SimFlash, areadesc: &AreaDesc, images: &Images,
445 stop: Option<i32>) -> (SimFlash, i32) {
David Brownde7729e2017-01-09 10:41:35 -0700446 // Clone the flash to have a new copy.
447 let mut fl = flash.clone();
448
Fabio Utzigebeecef2017-07-06 10:36:42 -0300449 mark_permanent_upgrade(&mut fl, &images.slot1);
Fabio Utzig57652312017-04-25 19:54:26 -0300450
David Brownde7729e2017-01-09 10:41:35 -0700451 c::set_flash_counter(stop.unwrap_or(0));
Fabio Utzigebeecef2017-07-06 10:36:42 -0300452 let (first_interrupted, count) = match c::boot_go(&mut fl, &areadesc) {
David Brownde7729e2017-01-09 10:41:35 -0700453 -0x13579 => (true, stop.unwrap()),
454 0 => (false, -c::get_flash_counter()),
455 x => panic!("Unknown return: {}", x),
456 };
457 c::set_flash_counter(0);
458
459 if first_interrupted {
460 // fl.dump();
461 match c::boot_go(&mut fl, &areadesc) {
462 -0x13579 => panic!("Shouldn't stop again"),
463 0 => (),
464 x => panic!("Unknown return: {}", x),
465 }
466 }
467
Fabio Utzigebeecef2017-07-06 10:36:42 -0300468 (fl, count - c::get_flash_counter())
David Brownde7729e2017-01-09 10:41:35 -0700469}
470
David Brown7ddec0b2017-07-06 10:47:35 -0600471fn try_revert(flash: &SimFlash, areadesc: &AreaDesc, count: usize) -> SimFlash {
David Brownde7729e2017-01-09 10:41:35 -0700472 let mut fl = flash.clone();
473 c::set_flash_counter(0);
474
David Brown163ab232017-01-23 15:48:35 -0700475 // fl.write_file("image0.bin").unwrap();
476 for i in 0 .. count {
477 info!("Running boot pass {}", i + 1);
David Brownc638f792017-01-10 12:34:33 -0700478 assert_eq!(c::boot_go(&mut fl, &areadesc), 0);
479 }
David Brownde7729e2017-01-09 10:41:35 -0700480 fl
481}
482
David Brown7ddec0b2017-07-06 10:47:35 -0600483fn try_revert_with_fail_at(flash: &SimFlash, areadesc: &AreaDesc, images: &Images,
Fabio Utzigebeecef2017-07-06 10:36:42 -0300484 stop: i32) -> bool {
David Brownde7729e2017-01-09 10:41:35 -0700485 let mut fl = flash.clone();
Fabio Utzigebeecef2017-07-06 10:36:42 -0300486 let mut x: i32;
487 let mut fails = 0;
David Brownde7729e2017-01-09 10:41:35 -0700488
Fabio Utzigebeecef2017-07-06 10:36:42 -0300489 c::set_flash_counter(stop);
490 x = c::boot_go(&mut fl, &areadesc);
491 if x != -0x13579 {
492 warn!("Should have stopped at interruption point");
493 fails += 1;
494 }
495
496 if !verify_trailer(&fl, images.slot0.trailer_off, None, None, UNSET) {
497 warn!("copy_done should be unset");
498 fails += 1;
499 }
500
501 c::set_flash_counter(0);
502 x = c::boot_go(&mut fl, &areadesc);
503 if x != 0 {
504 warn!("Should have finished upgrade");
505 fails += 1;
506 }
507
508 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
509 warn!("Image in slot 0 before revert is invalid at stop={}", stop);
510 fails += 1;
511 }
512 if !verify_image(&fl, images.slot1.base_off, &images.primary) {
513 warn!("Image in slot 1 before revert is invalid at stop={}", stop);
514 fails += 1;
515 }
516 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, UNSET,
517 COPY_DONE) {
518 warn!("Mismatched trailer for Slot 0 before revert");
519 fails += 1;
520 }
521 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
522 UNSET) {
523 warn!("Mismatched trailer for Slot 1 before revert");
524 fails += 1;
525 }
526
527 // Do Revert
528 c::set_flash_counter(0);
529 x = c::boot_go(&mut fl, &areadesc);
530 if x != 0 {
531 warn!("Should have finished a revert");
532 fails += 1;
533 }
534
535 if !verify_image(&fl, images.slot0.base_off, &images.primary) {
536 warn!("Image in slot 0 after revert is invalid at stop={}", stop);
537 fails += 1;
538 }
539 if !verify_image(&fl, images.slot1.base_off, &images.upgrade) {
540 warn!("Image in slot 1 after revert is invalid at stop={}", stop);
541 fails += 1;
542 }
543 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
544 COPY_DONE) {
545 warn!("Mismatched trailer for Slot 1 after revert");
546 fails += 1;
547 }
548 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
549 UNSET) {
550 warn!("Mismatched trailer for Slot 1 after revert");
551 fails += 1;
552 }
553
554 fails > 0
David Brownde7729e2017-01-09 10:41:35 -0700555}
556
David Brown7ddec0b2017-07-06 10:47:35 -0600557fn try_random_fails(flash: &SimFlash, areadesc: &AreaDesc, images: &Images,
558 total_ops: i32, count: usize) -> (SimFlash, Vec<i32>) {
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300559 let mut fl = flash.clone();
560
Fabio Utzigebeecef2017-07-06 10:36:42 -0300561 mark_permanent_upgrade(&mut fl, &images.slot1);
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300562
563 let mut rng = rand::thread_rng();
Fabio Utzig57652312017-04-25 19:54:26 -0300564 let mut resets = vec![0i32; count];
565 let mut remaining_ops = total_ops;
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300566 for i in 0 .. count {
Fabio Utzig57652312017-04-25 19:54:26 -0300567 let ops = Range::new(1, remaining_ops / 2);
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300568 let reset_counter = ops.ind_sample(&mut rng);
569 c::set_flash_counter(reset_counter);
570 match c::boot_go(&mut fl, &areadesc) {
571 0 | -0x13579 => (),
572 x => panic!("Unknown return: {}", x),
573 }
Fabio Utzig57652312017-04-25 19:54:26 -0300574 remaining_ops -= reset_counter;
575 resets[i] = reset_counter;
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300576 }
577
578 c::set_flash_counter(0);
579 match c::boot_go(&mut fl, &areadesc) {
580 -0x13579 => panic!("Should not be have been interrupted!"),
581 0 => (),
582 x => panic!("Unknown return: {}", x),
583 }
584
Fabio Utzig57652312017-04-25 19:54:26 -0300585 (fl, resets)
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300586}
587
David Brownde7729e2017-01-09 10:41:35 -0700588/// Show the flash layout.
589#[allow(dead_code)]
590fn show_flash(flash: &Flash) {
591 println!("---- Flash configuration ----");
592 for sector in flash.sector_iter() {
Fabio Utzigebeecef2017-07-06 10:36:42 -0300593 println!(" {:3}: 0x{:08x}, 0x{:08x}",
David Brownde7729e2017-01-09 10:41:35 -0700594 sector.num, sector.base, sector.size);
595 }
596 println!("");
597}
598
599/// Install a "program" into the given image. This fakes the image header, or at least all of the
600/// fields used by the given code. Returns a copy of the image that was written.
601fn install_image(flash: &mut Flash, offset: usize, len: usize) -> Vec<u8> {
602 let offset0 = offset;
603
David Brown187dd882017-07-11 11:15:23 -0600604 let mut tlv = TlvGen::new_hash_only();
605
David Brownde7729e2017-01-09 10:41:35 -0700606 // Generate a boot header. Note that the size doesn't include the header.
607 let header = ImageHeader {
608 magic: 0x96f3b83c,
David Brown187dd882017-07-11 11:15:23 -0600609 tlv_size: tlv.get_size(),
David Brownde7729e2017-01-09 10:41:35 -0700610 _pad1: 0,
611 hdr_size: 32,
612 key_id: 0,
613 _pad2: 0,
614 img_size: len as u32,
David Brown187dd882017-07-11 11:15:23 -0600615 flags: tlv.get_flags(),
David Brownde7729e2017-01-09 10:41:35 -0700616 ver: ImageVersion {
David Browne380fa62017-01-23 15:49:09 -0700617 major: (offset / (128 * 1024)) as u8,
David Brownde7729e2017-01-09 10:41:35 -0700618 minor: 0,
619 revision: 1,
David Browne380fa62017-01-23 15:49:09 -0700620 build_num: offset as u32,
David Brownde7729e2017-01-09 10:41:35 -0700621 },
622 _pad3: 0,
623 };
624
625 let b_header = header.as_raw();
David Brown187dd882017-07-11 11:15:23 -0600626 tlv.add_bytes(&b_header);
David Brownde7729e2017-01-09 10:41:35 -0700627 /*
628 let b_header = unsafe { slice::from_raw_parts(&header as *const _ as *const u8,
629 mem::size_of::<ImageHeader>()) };
630 */
631 assert_eq!(b_header.len(), 32);
632 flash.write(offset, &b_header).unwrap();
633 let offset = offset + b_header.len();
634
635 // The core of the image itself is just pseudorandom data.
636 let mut buf = vec![0; len];
637 splat(&mut buf, offset);
David Brown187dd882017-07-11 11:15:23 -0600638 tlv.add_bytes(&buf);
639
640 // Get and append the TLV itself.
641 buf.append(&mut tlv.make_tlv());
642
643 // Pad the block to a flash alignment (8 bytes).
644 while buf.len() % 8 != 0 {
645 buf.push(0xFF);
646 }
647
David Brownde7729e2017-01-09 10:41:35 -0700648 flash.write(offset, &buf).unwrap();
649 let offset = offset + buf.len();
650
651 // Copy out the image so that we can verify that the image was installed correctly later.
652 let mut copy = vec![0u8; offset - offset0];
653 flash.read(offset0, &mut copy).unwrap();
654
655 copy
656}
657
658/// Verify that given image is present in the flash at the given offset.
659fn verify_image(flash: &Flash, offset: usize, buf: &[u8]) -> bool {
660 let mut copy = vec![0u8; buf.len()];
661 flash.read(offset, &mut copy).unwrap();
662
663 if buf != &copy[..] {
664 for i in 0 .. buf.len() {
665 if buf[i] != copy[i] {
David Brown4440af82017-01-09 12:15:05 -0700666 info!("First failure at {:#x}", offset + i);
David Brownde7729e2017-01-09 10:41:35 -0700667 break;
668 }
669 }
670 false
671 } else {
672 true
673 }
674}
675
Fabio Utzigebeecef2017-07-06 10:36:42 -0300676fn verify_trailer(flash: &Flash, offset: usize,
677 magic: Option<&[u8]>, image_ok: Option<u8>,
678 copy_done: Option<u8>) -> bool {
679 let mut copy = vec![0u8; c::boot_magic_sz() + c::boot_max_align() * 2];
680 let mut failed = false;
681
682 flash.read(offset, &mut copy).unwrap();
683
684 failed |= match magic {
685 Some(v) => {
686 if &copy[16..] != v {
687 warn!("\"magic\" mismatch at {:#x}", offset);
688 true
689 } else {
690 false
691 }
692 },
693 None => false,
694 };
695
696 failed |= match image_ok {
697 Some(v) => {
698 if copy[8] != v {
699 warn!("\"image_ok\" mismatch at {:#x}", offset);
700 true
701 } else {
702 false
703 }
704 },
705 None => false,
706 };
707
708 failed |= match copy_done {
709 Some(v) => {
710 if copy[0] != v {
711 warn!("\"copy_done\" mismatch at {:#x}", offset);
712 true
713 } else {
714 false
715 }
716 },
717 None => false,
718 };
719
720 !failed
721}
722
David Brownde7729e2017-01-09 10:41:35 -0700723/// The image header
724#[repr(C)]
725pub struct ImageHeader {
726 magic: u32,
727 tlv_size: u16,
728 key_id: u8,
729 _pad1: u8,
730 hdr_size: u16,
731 _pad2: u16,
732 img_size: u32,
733 flags: u32,
734 ver: ImageVersion,
735 _pad3: u32,
736}
737
738impl AsRaw for ImageHeader {}
739
740#[repr(C)]
741pub struct ImageVersion {
742 major: u8,
743 minor: u8,
744 revision: u16,
745 build_num: u32,
746}
747
Fabio Utzigebeecef2017-07-06 10:36:42 -0300748struct SlotInfo {
749 base_off: usize,
750 trailer_off: usize,
751}
752
753struct Images {
754 slot0: SlotInfo,
755 slot1: SlotInfo,
756 primary: Vec<u8>,
757 upgrade: Vec<u8>,
758}
759
760const MAGIC_VALID: Option<&[u8]> = Some(&[0x77, 0xc2, 0x95, 0xf3,
761 0x60, 0xd2, 0xef, 0x7f,
762 0x35, 0x52, 0x50, 0x0f,
763 0x2c, 0xb6, 0x79, 0x80]);
764const MAGIC_UNSET: Option<&[u8]> = Some(&[0xff; 16]);
765
766const COPY_DONE: Option<u8> = Some(1);
767const IMAGE_OK: Option<u8> = Some(1);
768const UNSET: Option<u8> = Some(0xff);
769
David Brownde7729e2017-01-09 10:41:35 -0700770/// Write out the magic so that the loader tries doing an upgrade.
Fabio Utzigebeecef2017-07-06 10:36:42 -0300771fn mark_upgrade(flash: &mut Flash, slot: &SlotInfo) {
772 let offset = slot.trailer_off + c::boot_max_align() * 2;
773 flash.write(offset, MAGIC_VALID.unwrap()).unwrap();
774}
775
776/// Writes the image_ok flag which, guess what, tells the bootloader
777/// the this image is ok (not a test, and no revert is to be performed).
778fn mark_permanent_upgrade(flash: &mut Flash, slot: &SlotInfo) {
779 let ok = [1u8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
780 let align = c::get_sim_flash_align() as usize;
781 let off = slot.trailer_off + c::boot_max_align();
782 flash.write(off, &ok[..align]).unwrap();
David Brownde7729e2017-01-09 10:41:35 -0700783}
784
785// Drop some pseudo-random gibberish onto the data.
786fn splat(data: &mut [u8], seed: usize) {
787 let seed_block = [0x135782ea, 0x92184728, data.len() as u32, seed as u32];
788 let mut rng: XorShiftRng = SeedableRng::from_seed(seed_block);
789 rng.fill_bytes(data);
790}
791
792/// Return a read-only view into the raw bytes of this object
793trait AsRaw : Sized {
794 fn as_raw<'a>(&'a self) -> &'a [u8] {
795 unsafe { slice::from_raw_parts(self as *const _ as *const u8,
796 mem::size_of::<Self>()) }
797 }
798}
799
800fn show_sizes() {
801 // This isn't panic safe.
802 let old_align = c::get_sim_flash_align();
803 for min in &[1, 2, 4, 8] {
804 c::set_sim_flash_align(*min);
805 let msize = c::boot_trailer_sz();
806 println!("{:2}: {} (0x{:x})", min, msize, msize);
807 }
808 c::set_sim_flash_align(old_align);
809}