blob: dfebea774674485843fd90ca0e55bd396cd0d116 [file] [log] [blame]
David Brown4440af82017-01-09 12:15:05 -07001#[macro_use] extern crate log;
2extern crate env_logger;
David Brownde7729e2017-01-09 10:41:35 -07003extern crate docopt;
4extern crate libc;
5extern crate rand;
6extern crate rustc_serialize;
David Brown2cbc4702017-07-06 14:18:58 -06007extern crate simflash;
David Brownde7729e2017-01-09 10:41:35 -07008
9use docopt::Docopt;
David Brown4cb26232017-04-11 08:15:18 -060010use rand::{Rng, SeedableRng, XorShiftRng};
Fabio Utzigbb5635e2017-04-10 09:07:02 -030011use rand::distributions::{IndependentSample, Range};
David Brownde7729e2017-01-09 10:41:35 -070012use rustc_serialize::{Decodable, Decoder};
David Browna3b93cf2017-03-29 12:41:26 -060013use std::fmt;
David Brownde7729e2017-01-09 10:41:35 -070014use std::mem;
David Brown361be7a2017-03-29 12:28:47 -060015use std::process;
David Brownde7729e2017-01-09 10:41:35 -070016use std::slice;
17
18mod area;
19mod c;
David Brownde7729e2017-01-09 10:41:35 -070020pub mod api;
David Brown902d6172017-05-05 09:37:41 -060021mod caps;
David Brownde7729e2017-01-09 10:41:35 -070022
David Brown2cbc4702017-07-06 14:18:58 -060023use simflash::{Flash, SimFlash};
David Brownde7729e2017-01-09 10:41:35 -070024use area::{AreaDesc, FlashId};
David Brown902d6172017-05-05 09:37:41 -060025use caps::Caps;
David Brownde7729e2017-01-09 10:41:35 -070026
27const USAGE: &'static str = "
28Mcuboot simulator
29
30Usage:
31 bootsim sizes
32 bootsim run --device TYPE [--align SIZE]
David Browna3b93cf2017-03-29 12:41:26 -060033 bootsim runall
David Brownde7729e2017-01-09 10:41:35 -070034 bootsim (--help | --version)
35
36Options:
37 -h, --help Show this message
38 --version Version
39 --device TYPE MCU to simulate
40 Valid values: stm32f4, k64f
41 --align SIZE Flash write alignment
42";
43
44#[derive(Debug, RustcDecodable)]
45struct Args {
46 flag_help: bool,
47 flag_version: bool,
48 flag_device: Option<DeviceName>,
49 flag_align: Option<AlignArg>,
50 cmd_sizes: bool,
51 cmd_run: bool,
David Browna3b93cf2017-03-29 12:41:26 -060052 cmd_runall: bool,
David Brownde7729e2017-01-09 10:41:35 -070053}
54
David Browna3b93cf2017-03-29 12:41:26 -060055#[derive(Copy, Clone, Debug, RustcDecodable)]
David Brown07fb8fa2017-03-20 12:40:57 -060056enum DeviceName { Stm32f4, K64f, K64fBig, Nrf52840 }
David Brownde7729e2017-01-09 10:41:35 -070057
David Browna3b93cf2017-03-29 12:41:26 -060058static ALL_DEVICES: &'static [DeviceName] = &[
59 DeviceName::Stm32f4,
60 DeviceName::K64f,
61 DeviceName::K64fBig,
62 DeviceName::Nrf52840,
63];
64
65impl fmt::Display for DeviceName {
66 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
67 let name = match *self {
68 DeviceName::Stm32f4 => "stm32f4",
69 DeviceName::K64f => "k64f",
70 DeviceName::K64fBig => "k64fbig",
71 DeviceName::Nrf52840 => "nrf52840",
72 };
73 f.write_str(name)
74 }
75}
76
David Brownde7729e2017-01-09 10:41:35 -070077#[derive(Debug)]
78struct AlignArg(u8);
79
80impl Decodable for AlignArg {
81 // Decode the alignment ourselves, to restrict it to the valid possible alignments.
82 fn decode<D: Decoder>(d: &mut D) -> Result<AlignArg, D::Error> {
83 let m = d.read_u8()?;
84 match m {
85 1 | 2 | 4 | 8 => Ok(AlignArg(m)),
86 _ => Err(d.error("Invalid alignment")),
87 }
88 }
89}
90
91fn main() {
David Brown4440af82017-01-09 12:15:05 -070092 env_logger::init().unwrap();
93
David Brownde7729e2017-01-09 10:41:35 -070094 let args: Args = Docopt::new(USAGE)
95 .and_then(|d| d.decode())
96 .unwrap_or_else(|e| e.exit());
97 // println!("args: {:#?}", args);
98
99 if args.cmd_sizes {
100 show_sizes();
101 return;
102 }
103
David Brown361be7a2017-03-29 12:28:47 -0600104 let mut status = RunStatus::new();
David Browna3b93cf2017-03-29 12:41:26 -0600105 if args.cmd_run {
David Brown361be7a2017-03-29 12:28:47 -0600106
David Browna3b93cf2017-03-29 12:41:26 -0600107 let align = args.flag_align.map(|x| x.0).unwrap_or(1);
David Brown562a7a02017-01-23 11:19:03 -0700108
Fabio Utzigebeecef2017-07-06 10:36:42 -0300109
David Browna3b93cf2017-03-29 12:41:26 -0600110 let device = match args.flag_device {
111 None => panic!("Missing mandatory device argument"),
112 Some(dev) => dev,
113 };
David Brownde7729e2017-01-09 10:41:35 -0700114
David Browna3b93cf2017-03-29 12:41:26 -0600115 status.run_single(device, align);
116 }
117
118 if args.cmd_runall {
119 for &dev in ALL_DEVICES {
120 for &align in &[1, 2, 4, 8] {
121 status.run_single(dev, align);
122 }
123 }
124 }
David Brown5c6b6792017-03-20 12:51:28 -0600125
David Brown361be7a2017-03-29 12:28:47 -0600126 if status.failures > 0 {
127 warn!("{} Tests ran with {} failures", status.failures + status.passes, status.failures);
128 process::exit(1);
129 } else {
130 warn!("{} Tests ran successfully", status.passes);
131 process::exit(0);
132 }
133}
David Brown5c6b6792017-03-20 12:51:28 -0600134
David Brown361be7a2017-03-29 12:28:47 -0600135struct RunStatus {
136 failures: usize,
137 passes: usize,
138}
David Brownde7729e2017-01-09 10:41:35 -0700139
David Brown361be7a2017-03-29 12:28:47 -0600140impl RunStatus {
141 fn new() -> RunStatus {
142 RunStatus {
143 failures: 0,
144 passes: 0,
David Brownde7729e2017-01-09 10:41:35 -0700145 }
146 }
David Brownde7729e2017-01-09 10:41:35 -0700147
David Brown361be7a2017-03-29 12:28:47 -0600148 fn run_single(&mut self, device: DeviceName, align: u8) {
David Browna3b93cf2017-03-29 12:41:26 -0600149 warn!("Running on device {} with alignment {}", device, align);
150
David Brown361be7a2017-03-29 12:28:47 -0600151 let (mut flash, areadesc) = match device {
152 DeviceName::Stm32f4 => {
153 // STM style flash. Large sectors, with a large scratch area.
David Brown7ddec0b2017-07-06 10:47:35 -0600154 let flash = SimFlash::new(vec![16 * 1024, 16 * 1024, 16 * 1024, 16 * 1024,
155 64 * 1024,
156 128 * 1024, 128 * 1024, 128 * 1024],
157 align as usize);
David Brown361be7a2017-03-29 12:28:47 -0600158 let mut areadesc = AreaDesc::new(&flash);
159 areadesc.add_image(0x020000, 0x020000, FlashId::Image0);
160 areadesc.add_image(0x040000, 0x020000, FlashId::Image1);
161 areadesc.add_image(0x060000, 0x020000, FlashId::ImageScratch);
162 (flash, areadesc)
163 }
164 DeviceName::K64f => {
165 // NXP style flash. Small sectors, one small sector for scratch.
David Brown7ddec0b2017-07-06 10:47:35 -0600166 let flash = SimFlash::new(vec![4096; 128], align as usize);
David Brown361be7a2017-03-29 12:28:47 -0600167
168 let mut areadesc = AreaDesc::new(&flash);
169 areadesc.add_image(0x020000, 0x020000, FlashId::Image0);
170 areadesc.add_image(0x040000, 0x020000, FlashId::Image1);
171 areadesc.add_image(0x060000, 0x001000, FlashId::ImageScratch);
172 (flash, areadesc)
173 }
174 DeviceName::K64fBig => {
175 // Simulating an STM style flash on top of an NXP style flash. Underlying flash device
176 // uses small sectors, but we tell the bootloader they are large.
David Brown7ddec0b2017-07-06 10:47:35 -0600177 let flash = SimFlash::new(vec![4096; 128], align as usize);
David Brown361be7a2017-03-29 12:28:47 -0600178
179 let mut areadesc = AreaDesc::new(&flash);
180 areadesc.add_simple_image(0x020000, 0x020000, FlashId::Image0);
181 areadesc.add_simple_image(0x040000, 0x020000, FlashId::Image1);
182 areadesc.add_simple_image(0x060000, 0x020000, FlashId::ImageScratch);
183 (flash, areadesc)
184 }
185 DeviceName::Nrf52840 => {
186 // Simulating the flash on the nrf52840 with partitions set up so that the scratch size
187 // does not divide into the image size.
David Brown7ddec0b2017-07-06 10:47:35 -0600188 let flash = SimFlash::new(vec![4096; 128], align as usize);
David Brown361be7a2017-03-29 12:28:47 -0600189
190 let mut areadesc = AreaDesc::new(&flash);
191 areadesc.add_image(0x008000, 0x034000, FlashId::Image0);
192 areadesc.add_image(0x03c000, 0x034000, FlashId::Image1);
193 areadesc.add_image(0x070000, 0x00d000, FlashId::ImageScratch);
194 (flash, areadesc)
195 }
196 };
197
198 let (slot0_base, slot0_len) = areadesc.find(FlashId::Image0);
199 let (slot1_base, slot1_len) = areadesc.find(FlashId::Image1);
200 let (scratch_base, _) = areadesc.find(FlashId::ImageScratch);
201
202 // Code below assumes that the slots are consecutive.
203 assert_eq!(slot1_base, slot0_base + slot0_len);
204 assert_eq!(scratch_base, slot1_base + slot1_len);
205
Fabio Utzigebeecef2017-07-06 10:36:42 -0300206 let offset_from_end = c::boot_magic_sz() + c::boot_max_align() * 2;
207
David Brown361be7a2017-03-29 12:28:47 -0600208 // println!("Areas: {:#?}", areadesc.get_c());
209
210 // Install the boot trailer signature, so that the code will start an upgrade.
211 // TODO: This must be a multiple of flash alignment, add support for an image that is smaller,
212 // and just gets padded.
David Brown361be7a2017-03-29 12:28:47 -0600213
Fabio Utzigebeecef2017-07-06 10:36:42 -0300214 // Create original and upgrade images
215 let slot0 = SlotInfo {
216 base_off: slot0_base as usize,
217 trailer_off: slot1_base - offset_from_end,
218 };
219
220 let slot1 = SlotInfo {
221 base_off: slot1_base as usize,
222 trailer_off: scratch_base - offset_from_end,
223 };
224
225 let images = Images {
226 slot0: slot0,
227 slot1: slot1,
228 primary: install_image(&mut flash, slot0_base, 32784),
229 upgrade: install_image(&mut flash, slot1_base, 41928),
230 };
231
232 let mut failed = false;
David Brown361be7a2017-03-29 12:28:47 -0600233
234 // Set an alignment, and position the magic value.
235 c::set_sim_flash_align(align);
David Brown361be7a2017-03-29 12:28:47 -0600236
Fabio Utzigebeecef2017-07-06 10:36:42 -0300237 mark_upgrade(&mut flash, &images.slot1);
David Brown361be7a2017-03-29 12:28:47 -0600238
Fabio Utzigebeecef2017-07-06 10:36:42 -0300239 // upgrades without fails, counts number of flash operations
240 let total_count = match run_basic_upgrade(&flash, &areadesc, &images) {
241 Ok(v) => v,
242 Err(_) => {
243 self.failures += 1;
244 return;
245 },
David Brown902d6172017-05-05 09:37:41 -0600246 };
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300247
Fabio Utzigebeecef2017-07-06 10:36:42 -0300248 failed |= run_basic_revert(&flash, &areadesc, &images);
249 failed |= run_revert_with_fails(&flash, &areadesc, &images, total_count);
250 failed |= run_perm_with_fails(&flash, &areadesc, &images, total_count);
251 failed |= run_perm_with_random_fails(&flash, &areadesc, &images,
252 total_count, 5);
253 failed |= run_norevert(&flash, &areadesc, &images);
David Brown361be7a2017-03-29 12:28:47 -0600254
Fabio Utzigebeecef2017-07-06 10:36:42 -0300255 //show_flash(&flash);
David Brown361be7a2017-03-29 12:28:47 -0600256
David Brown361be7a2017-03-29 12:28:47 -0600257 if failed {
258 self.failures += 1;
259 } else {
260 self.passes += 1;
261 }
David Brownc638f792017-01-10 12:34:33 -0700262 }
David Brownde7729e2017-01-09 10:41:35 -0700263}
264
Fabio Utzigebeecef2017-07-06 10:36:42 -0300265/// A simple upgrade without forced failures.
266///
267/// Returns the number of flash operations which can later be used to
268/// inject failures at chosen steps.
David Brown7ddec0b2017-07-06 10:47:35 -0600269fn run_basic_upgrade(flash: &SimFlash, areadesc: &AreaDesc, images: &Images)
Fabio Utzigebeecef2017-07-06 10:36:42 -0300270 -> Result<i32, ()> {
271 let (fl, total_count) = try_upgrade(&flash, &areadesc, &images, None);
272 info!("Total flash operation count={}", total_count);
273
274 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
275 warn!("Image mismatch after first boot");
276 Err(())
277 } else {
278 Ok(total_count)
279 }
280}
281
David Brown7ddec0b2017-07-06 10:47:35 -0600282fn run_basic_revert(flash: &SimFlash, areadesc: &AreaDesc, images: &Images) -> bool {
Fabio Utzigebeecef2017-07-06 10:36:42 -0300283 let mut fails = 0;
284
285 if Caps::SwapUpgrade.present() {
286 for count in 2 .. 5 {
287 info!("Try revert: {}", count);
288 let fl = try_revert(&flash, &areadesc, count);
289 if !verify_image(&fl, images.slot0.base_off, &images.primary) {
290 warn!("Revert failure on count {}", count);
291 fails += 1;
292 }
293 }
294 }
295
296 fails > 0
297}
298
David Brown7ddec0b2017-07-06 10:47:35 -0600299fn run_perm_with_fails(flash: &SimFlash, areadesc: &AreaDesc, images: &Images,
Fabio Utzigebeecef2017-07-06 10:36:42 -0300300 total_flash_ops: i32) -> bool {
301 let mut fails = 0;
302
303 // Let's try an image halfway through.
304 for i in 1 .. total_flash_ops {
305 info!("Try interruption at {}", i);
306 let (fl, count) = try_upgrade(&flash, &areadesc, &images, Some(i));
307 info!("Second boot, count={}", count);
308 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
309 warn!("FAIL at step {} of {}", i, total_flash_ops);
310 fails += 1;
311 }
312
313 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
314 COPY_DONE) {
315 warn!("Mismatched trailer for Slot 0");
316 fails += 1;
317 }
318
319 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
320 UNSET) {
321 warn!("Mismatched trailer for Slot 1");
322 fails += 1;
323 }
324
325 if Caps::SwapUpgrade.present() {
326 if !verify_image(&fl, images.slot1.base_off, &images.primary) {
327 warn!("Slot 1 FAIL at step {} of {}", i, total_flash_ops);
328 fails += 1;
329 }
330 }
331 }
332
333 info!("{} out of {} failed {:.2}%", fails, total_flash_ops,
334 fails as f32 * 100.0 / total_flash_ops as f32);
335
336 fails > 0
337}
338
David Brown7ddec0b2017-07-06 10:47:35 -0600339fn run_perm_with_random_fails(flash: &SimFlash, areadesc: &AreaDesc,
Fabio Utzigebeecef2017-07-06 10:36:42 -0300340 images: &Images, total_flash_ops: i32,
341 total_fails: usize) -> bool {
342 let mut fails = 0;
343 let (fl, total_counts) = try_random_fails(&flash, &areadesc, &images,
344 total_flash_ops, total_fails);
345 info!("Random interruptions at reset points={:?}", total_counts);
346
347 let slot0_ok = verify_image(&fl, images.slot0.base_off, &images.upgrade);
348 let slot1_ok = if Caps::SwapUpgrade.present() {
349 verify_image(&fl, images.slot1.base_off, &images.primary)
350 } else {
351 true
352 };
353 if !slot0_ok || !slot1_ok {
354 error!("Image mismatch after random interrupts: slot0={} slot1={}",
355 if slot0_ok { "ok" } else { "fail" },
356 if slot1_ok { "ok" } else { "fail" });
357 fails += 1;
358 }
359 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
360 COPY_DONE) {
361 error!("Mismatched trailer for Slot 0");
362 fails += 1;
363 }
364 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
365 UNSET) {
366 error!("Mismatched trailer for Slot 1");
367 fails += 1;
368 }
369
370 fails > 0
371}
372
David Brown7ddec0b2017-07-06 10:47:35 -0600373fn run_revert_with_fails(flash: &SimFlash, areadesc: &AreaDesc, images: &Images,
Fabio Utzigebeecef2017-07-06 10:36:42 -0300374 total_count: i32) -> bool {
375 let mut fails = 0;
376
377 if Caps::SwapUpgrade.present() {
378 for i in 1 .. (total_count - 1) {
379 info!("Try interruption at {}", i);
380 if try_revert_with_fail_at(&flash, &areadesc, &images, i) {
381 fails += 1;
382 }
383 }
384 }
385
386 fails > 0
387}
388
David Brown7ddec0b2017-07-06 10:47:35 -0600389fn run_norevert(flash: &SimFlash, areadesc: &AreaDesc, images: &Images) -> bool {
Fabio Utzigebeecef2017-07-06 10:36:42 -0300390 let mut fl = flash.clone();
391 let mut fails = 0;
392
393 info!("Try norevert");
394 c::set_flash_counter(0);
395
396 // First do a normal upgrade...
397 if c::boot_go(&mut fl, &areadesc) != 0 {
398 warn!("Failed first boot");
399 fails += 1;
400 }
401
402 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
403 warn!("Slot 0 image verification FAIL");
404 fails += 1;
405 }
406 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, UNSET,
407 COPY_DONE) {
408 warn!("Mismatched trailer for Slot 0");
409 fails += 1;
410 }
411 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
412 UNSET) {
413 warn!("Mismatched trailer for Slot 1");
414 fails += 1;
415 }
416
417 // Marks image in slot0 as permanent, no revert should happen...
418 mark_permanent_upgrade(&mut fl, &images.slot0);
419
420 if c::boot_go(&mut fl, &areadesc) != 0 {
421 warn!("Failed second boot");
422 fails += 1;
423 }
424
425 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
426 COPY_DONE) {
427 warn!("Mismatched trailer for Slot 0");
428 fails += 1;
429 }
430 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
431 warn!("Failed image verification");
432 fails += 1;
433 }
434
435 fails > 0
436}
437
438/// Test a boot, optionally stopping after 'n' flash options. Returns a count
439/// of the number of flash operations done total.
David Brown7ddec0b2017-07-06 10:47:35 -0600440fn try_upgrade(flash: &SimFlash, areadesc: &AreaDesc, images: &Images,
441 stop: Option<i32>) -> (SimFlash, i32) {
David Brownde7729e2017-01-09 10:41:35 -0700442 // Clone the flash to have a new copy.
443 let mut fl = flash.clone();
444
Fabio Utzigebeecef2017-07-06 10:36:42 -0300445 mark_permanent_upgrade(&mut fl, &images.slot1);
Fabio Utzig57652312017-04-25 19:54:26 -0300446
David Brownde7729e2017-01-09 10:41:35 -0700447 c::set_flash_counter(stop.unwrap_or(0));
Fabio Utzigebeecef2017-07-06 10:36:42 -0300448 let (first_interrupted, count) = match c::boot_go(&mut fl, &areadesc) {
David Brownde7729e2017-01-09 10:41:35 -0700449 -0x13579 => (true, stop.unwrap()),
450 0 => (false, -c::get_flash_counter()),
451 x => panic!("Unknown return: {}", x),
452 };
453 c::set_flash_counter(0);
454
455 if first_interrupted {
456 // fl.dump();
457 match c::boot_go(&mut fl, &areadesc) {
458 -0x13579 => panic!("Shouldn't stop again"),
459 0 => (),
460 x => panic!("Unknown return: {}", x),
461 }
462 }
463
Fabio Utzigebeecef2017-07-06 10:36:42 -0300464 (fl, count - c::get_flash_counter())
David Brownde7729e2017-01-09 10:41:35 -0700465}
466
David Brown7ddec0b2017-07-06 10:47:35 -0600467fn try_revert(flash: &SimFlash, areadesc: &AreaDesc, count: usize) -> SimFlash {
David Brownde7729e2017-01-09 10:41:35 -0700468 let mut fl = flash.clone();
469 c::set_flash_counter(0);
470
David Brown163ab232017-01-23 15:48:35 -0700471 // fl.write_file("image0.bin").unwrap();
472 for i in 0 .. count {
473 info!("Running boot pass {}", i + 1);
David Brownc638f792017-01-10 12:34:33 -0700474 assert_eq!(c::boot_go(&mut fl, &areadesc), 0);
475 }
David Brownde7729e2017-01-09 10:41:35 -0700476 fl
477}
478
David Brown7ddec0b2017-07-06 10:47:35 -0600479fn try_revert_with_fail_at(flash: &SimFlash, areadesc: &AreaDesc, images: &Images,
Fabio Utzigebeecef2017-07-06 10:36:42 -0300480 stop: i32) -> bool {
David Brownde7729e2017-01-09 10:41:35 -0700481 let mut fl = flash.clone();
Fabio Utzigebeecef2017-07-06 10:36:42 -0300482 let mut x: i32;
483 let mut fails = 0;
David Brownde7729e2017-01-09 10:41:35 -0700484
Fabio Utzigebeecef2017-07-06 10:36:42 -0300485 c::set_flash_counter(stop);
486 x = c::boot_go(&mut fl, &areadesc);
487 if x != -0x13579 {
488 warn!("Should have stopped at interruption point");
489 fails += 1;
490 }
491
492 if !verify_trailer(&fl, images.slot0.trailer_off, None, None, UNSET) {
493 warn!("copy_done should be unset");
494 fails += 1;
495 }
496
497 c::set_flash_counter(0);
498 x = c::boot_go(&mut fl, &areadesc);
499 if x != 0 {
500 warn!("Should have finished upgrade");
501 fails += 1;
502 }
503
504 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
505 warn!("Image in slot 0 before revert is invalid at stop={}", stop);
506 fails += 1;
507 }
508 if !verify_image(&fl, images.slot1.base_off, &images.primary) {
509 warn!("Image in slot 1 before revert is invalid at stop={}", stop);
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 before revert");
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 before revert");
520 fails += 1;
521 }
522
523 // Do Revert
524 c::set_flash_counter(0);
525 x = c::boot_go(&mut fl, &areadesc);
526 if x != 0 {
527 warn!("Should have finished a revert");
528 fails += 1;
529 }
530
531 if !verify_image(&fl, images.slot0.base_off, &images.primary) {
532 warn!("Image in slot 0 after revert is invalid at stop={}", stop);
533 fails += 1;
534 }
535 if !verify_image(&fl, images.slot1.base_off, &images.upgrade) {
536 warn!("Image in slot 1 after revert is invalid at stop={}", stop);
537 fails += 1;
538 }
539 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
540 COPY_DONE) {
541 warn!("Mismatched trailer for Slot 1 after revert");
542 fails += 1;
543 }
544 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
545 UNSET) {
546 warn!("Mismatched trailer for Slot 1 after revert");
547 fails += 1;
548 }
549
550 fails > 0
David Brownde7729e2017-01-09 10:41:35 -0700551}
552
David Brown7ddec0b2017-07-06 10:47:35 -0600553fn try_random_fails(flash: &SimFlash, areadesc: &AreaDesc, images: &Images,
554 total_ops: i32, count: usize) -> (SimFlash, Vec<i32>) {
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300555 let mut fl = flash.clone();
556
Fabio Utzigebeecef2017-07-06 10:36:42 -0300557 mark_permanent_upgrade(&mut fl, &images.slot1);
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300558
559 let mut rng = rand::thread_rng();
Fabio Utzig57652312017-04-25 19:54:26 -0300560 let mut resets = vec![0i32; count];
561 let mut remaining_ops = total_ops;
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300562 for i in 0 .. count {
Fabio Utzig57652312017-04-25 19:54:26 -0300563 let ops = Range::new(1, remaining_ops / 2);
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300564 let reset_counter = ops.ind_sample(&mut rng);
565 c::set_flash_counter(reset_counter);
566 match c::boot_go(&mut fl, &areadesc) {
567 0 | -0x13579 => (),
568 x => panic!("Unknown return: {}", x),
569 }
Fabio Utzig57652312017-04-25 19:54:26 -0300570 remaining_ops -= reset_counter;
571 resets[i] = reset_counter;
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300572 }
573
574 c::set_flash_counter(0);
575 match c::boot_go(&mut fl, &areadesc) {
576 -0x13579 => panic!("Should not be have been interrupted!"),
577 0 => (),
578 x => panic!("Unknown return: {}", x),
579 }
580
Fabio Utzig57652312017-04-25 19:54:26 -0300581 (fl, resets)
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300582}
583
David Brownde7729e2017-01-09 10:41:35 -0700584/// Show the flash layout.
585#[allow(dead_code)]
586fn show_flash(flash: &Flash) {
587 println!("---- Flash configuration ----");
588 for sector in flash.sector_iter() {
Fabio Utzigebeecef2017-07-06 10:36:42 -0300589 println!(" {:3}: 0x{:08x}, 0x{:08x}",
David Brownde7729e2017-01-09 10:41:35 -0700590 sector.num, sector.base, sector.size);
591 }
592 println!("");
593}
594
595/// Install a "program" into the given image. This fakes the image header, or at least all of the
596/// fields used by the given code. Returns a copy of the image that was written.
597fn install_image(flash: &mut Flash, offset: usize, len: usize) -> Vec<u8> {
598 let offset0 = offset;
599
600 // Generate a boot header. Note that the size doesn't include the header.
601 let header = ImageHeader {
602 magic: 0x96f3b83c,
603 tlv_size: 0,
604 _pad1: 0,
605 hdr_size: 32,
606 key_id: 0,
607 _pad2: 0,
608 img_size: len as u32,
609 flags: 0,
610 ver: ImageVersion {
David Browne380fa62017-01-23 15:49:09 -0700611 major: (offset / (128 * 1024)) as u8,
David Brownde7729e2017-01-09 10:41:35 -0700612 minor: 0,
613 revision: 1,
David Browne380fa62017-01-23 15:49:09 -0700614 build_num: offset as u32,
David Brownde7729e2017-01-09 10:41:35 -0700615 },
616 _pad3: 0,
617 };
618
619 let b_header = header.as_raw();
620 /*
621 let b_header = unsafe { slice::from_raw_parts(&header as *const _ as *const u8,
622 mem::size_of::<ImageHeader>()) };
623 */
624 assert_eq!(b_header.len(), 32);
625 flash.write(offset, &b_header).unwrap();
626 let offset = offset + b_header.len();
627
628 // The core of the image itself is just pseudorandom data.
629 let mut buf = vec![0; len];
630 splat(&mut buf, offset);
631 flash.write(offset, &buf).unwrap();
632 let offset = offset + buf.len();
633
634 // Copy out the image so that we can verify that the image was installed correctly later.
635 let mut copy = vec![0u8; offset - offset0];
636 flash.read(offset0, &mut copy).unwrap();
637
638 copy
639}
640
641/// Verify that given image is present in the flash at the given offset.
642fn verify_image(flash: &Flash, offset: usize, buf: &[u8]) -> bool {
643 let mut copy = vec![0u8; buf.len()];
644 flash.read(offset, &mut copy).unwrap();
645
646 if buf != &copy[..] {
647 for i in 0 .. buf.len() {
648 if buf[i] != copy[i] {
David Brown4440af82017-01-09 12:15:05 -0700649 info!("First failure at {:#x}", offset + i);
David Brownde7729e2017-01-09 10:41:35 -0700650 break;
651 }
652 }
653 false
654 } else {
655 true
656 }
657}
658
Fabio Utzigebeecef2017-07-06 10:36:42 -0300659fn verify_trailer(flash: &Flash, offset: usize,
660 magic: Option<&[u8]>, image_ok: Option<u8>,
661 copy_done: Option<u8>) -> bool {
662 let mut copy = vec![0u8; c::boot_magic_sz() + c::boot_max_align() * 2];
663 let mut failed = false;
664
665 flash.read(offset, &mut copy).unwrap();
666
667 failed |= match magic {
668 Some(v) => {
669 if &copy[16..] != v {
670 warn!("\"magic\" mismatch at {:#x}", offset);
671 true
672 } else {
673 false
674 }
675 },
676 None => false,
677 };
678
679 failed |= match image_ok {
680 Some(v) => {
681 if copy[8] != v {
682 warn!("\"image_ok\" mismatch at {:#x}", offset);
683 true
684 } else {
685 false
686 }
687 },
688 None => false,
689 };
690
691 failed |= match copy_done {
692 Some(v) => {
693 if copy[0] != v {
694 warn!("\"copy_done\" mismatch at {:#x}", offset);
695 true
696 } else {
697 false
698 }
699 },
700 None => false,
701 };
702
703 !failed
704}
705
David Brownde7729e2017-01-09 10:41:35 -0700706/// The image header
707#[repr(C)]
708pub struct ImageHeader {
709 magic: u32,
710 tlv_size: u16,
711 key_id: u8,
712 _pad1: u8,
713 hdr_size: u16,
714 _pad2: u16,
715 img_size: u32,
716 flags: u32,
717 ver: ImageVersion,
718 _pad3: u32,
719}
720
721impl AsRaw for ImageHeader {}
722
723#[repr(C)]
724pub struct ImageVersion {
725 major: u8,
726 minor: u8,
727 revision: u16,
728 build_num: u32,
729}
730
Fabio Utzigebeecef2017-07-06 10:36:42 -0300731struct SlotInfo {
732 base_off: usize,
733 trailer_off: usize,
734}
735
736struct Images {
737 slot0: SlotInfo,
738 slot1: SlotInfo,
739 primary: Vec<u8>,
740 upgrade: Vec<u8>,
741}
742
743const MAGIC_VALID: Option<&[u8]> = Some(&[0x77, 0xc2, 0x95, 0xf3,
744 0x60, 0xd2, 0xef, 0x7f,
745 0x35, 0x52, 0x50, 0x0f,
746 0x2c, 0xb6, 0x79, 0x80]);
747const MAGIC_UNSET: Option<&[u8]> = Some(&[0xff; 16]);
748
749const COPY_DONE: Option<u8> = Some(1);
750const IMAGE_OK: Option<u8> = Some(1);
751const UNSET: Option<u8> = Some(0xff);
752
David Brownde7729e2017-01-09 10:41:35 -0700753/// Write out the magic so that the loader tries doing an upgrade.
Fabio Utzigebeecef2017-07-06 10:36:42 -0300754fn mark_upgrade(flash: &mut Flash, slot: &SlotInfo) {
755 let offset = slot.trailer_off + c::boot_max_align() * 2;
756 flash.write(offset, MAGIC_VALID.unwrap()).unwrap();
757}
758
759/// Writes the image_ok flag which, guess what, tells the bootloader
760/// the this image is ok (not a test, and no revert is to be performed).
761fn mark_permanent_upgrade(flash: &mut Flash, slot: &SlotInfo) {
762 let ok = [1u8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
763 let align = c::get_sim_flash_align() as usize;
764 let off = slot.trailer_off + c::boot_max_align();
765 flash.write(off, &ok[..align]).unwrap();
David Brownde7729e2017-01-09 10:41:35 -0700766}
767
768// Drop some pseudo-random gibberish onto the data.
769fn splat(data: &mut [u8], seed: usize) {
770 let seed_block = [0x135782ea, 0x92184728, data.len() as u32, seed as u32];
771 let mut rng: XorShiftRng = SeedableRng::from_seed(seed_block);
772 rng.fill_bytes(data);
773}
774
775/// Return a read-only view into the raw bytes of this object
776trait AsRaw : Sized {
777 fn as_raw<'a>(&'a self) -> &'a [u8] {
778 unsafe { slice::from_raw_parts(self as *const _ as *const u8,
779 mem::size_of::<Self>()) }
780 }
781}
782
783fn show_sizes() {
784 // This isn't panic safe.
785 let old_align = c::get_sim_flash_align();
786 for min in &[1, 2, 4, 8] {
787 c::set_sim_flash_align(*min);
788 let msize = c::boot_trailer_sz();
789 println!("{:2}: {} (0x{:x})", min, msize, msize);
790 }
791 c::set_sim_flash_align(old_align);
792}