blob: 614def39ab9248bf67c082521a5a37d78d8124a1 [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 Brown187dd882017-07-11 11:15:23 -06004extern crate enumflags;
5#[macro_use] extern crate enumflags_derive;
David Brownde7729e2017-01-09 10:41:35 -07006extern crate docopt;
7extern crate libc;
8extern crate rand;
9extern crate rustc_serialize;
David Brown2cbc4702017-07-06 14:18:58 -060010extern crate simflash;
David Brownde7729e2017-01-09 10:41:35 -070011
12use docopt::Docopt;
David Brown4cb26232017-04-11 08:15:18 -060013use rand::{Rng, SeedableRng, XorShiftRng};
Fabio Utzigbb5635e2017-04-10 09:07:02 -030014use rand::distributions::{IndependentSample, Range};
David Brownde7729e2017-01-09 10:41:35 -070015use rustc_serialize::{Decodable, Decoder};
David Browna3b93cf2017-03-29 12:41:26 -060016use std::fmt;
David Brownde7729e2017-01-09 10:41:35 -070017use std::mem;
David Brown361be7a2017-03-29 12:28:47 -060018use std::process;
David Brownde7729e2017-01-09 10:41:35 -070019use std::slice;
20
21mod area;
22mod c;
David Brownde7729e2017-01-09 10:41:35 -070023pub mod api;
David Brown902d6172017-05-05 09:37:41 -060024mod caps;
David Brown187dd882017-07-11 11:15:23 -060025mod tlv;
David Brownde7729e2017-01-09 10:41:35 -070026
David Brown2cbc4702017-07-06 14:18:58 -060027use simflash::{Flash, SimFlash};
David Brownde7729e2017-01-09 10:41:35 -070028use area::{AreaDesc, FlashId};
David Brown902d6172017-05-05 09:37:41 -060029use caps::Caps;
David Brown187dd882017-07-11 11:15:23 -060030use tlv::TlvGen;
David Brownde7729e2017-01-09 10:41:35 -070031
32const USAGE: &'static str = "
33Mcuboot simulator
34
35Usage:
36 bootsim sizes
37 bootsim run --device TYPE [--align SIZE]
David Browna3b93cf2017-03-29 12:41:26 -060038 bootsim runall
David Brownde7729e2017-01-09 10:41:35 -070039 bootsim (--help | --version)
40
41Options:
42 -h, --help Show this message
43 --version Version
44 --device TYPE MCU to simulate
45 Valid values: stm32f4, k64f
46 --align SIZE Flash write alignment
47";
48
49#[derive(Debug, RustcDecodable)]
50struct Args {
51 flag_help: bool,
52 flag_version: bool,
53 flag_device: Option<DeviceName>,
54 flag_align: Option<AlignArg>,
55 cmd_sizes: bool,
56 cmd_run: bool,
David Browna3b93cf2017-03-29 12:41:26 -060057 cmd_runall: bool,
David Brownde7729e2017-01-09 10:41:35 -070058}
59
David Browna3b93cf2017-03-29 12:41:26 -060060#[derive(Copy, Clone, Debug, RustcDecodable)]
David Brown07fb8fa2017-03-20 12:40:57 -060061enum DeviceName { Stm32f4, K64f, K64fBig, Nrf52840 }
David Brownde7729e2017-01-09 10:41:35 -070062
David Browna3b93cf2017-03-29 12:41:26 -060063static ALL_DEVICES: &'static [DeviceName] = &[
64 DeviceName::Stm32f4,
65 DeviceName::K64f,
66 DeviceName::K64fBig,
67 DeviceName::Nrf52840,
68];
69
70impl fmt::Display for DeviceName {
71 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
72 let name = match *self {
73 DeviceName::Stm32f4 => "stm32f4",
74 DeviceName::K64f => "k64f",
75 DeviceName::K64fBig => "k64fbig",
76 DeviceName::Nrf52840 => "nrf52840",
77 };
78 f.write_str(name)
79 }
80}
81
David Brownde7729e2017-01-09 10:41:35 -070082#[derive(Debug)]
83struct AlignArg(u8);
84
85impl Decodable for AlignArg {
86 // Decode the alignment ourselves, to restrict it to the valid possible alignments.
87 fn decode<D: Decoder>(d: &mut D) -> Result<AlignArg, D::Error> {
88 let m = d.read_u8()?;
89 match m {
90 1 | 2 | 4 | 8 => Ok(AlignArg(m)),
91 _ => Err(d.error("Invalid alignment")),
92 }
93 }
94}
95
96fn main() {
David Brown4440af82017-01-09 12:15:05 -070097 env_logger::init().unwrap();
98
David Brownde7729e2017-01-09 10:41:35 -070099 let args: Args = Docopt::new(USAGE)
100 .and_then(|d| d.decode())
101 .unwrap_or_else(|e| e.exit());
102 // println!("args: {:#?}", args);
103
104 if args.cmd_sizes {
105 show_sizes();
106 return;
107 }
108
David Brown361be7a2017-03-29 12:28:47 -0600109 let mut status = RunStatus::new();
David Browna3b93cf2017-03-29 12:41:26 -0600110 if args.cmd_run {
David Brown361be7a2017-03-29 12:28:47 -0600111
David Browna3b93cf2017-03-29 12:41:26 -0600112 let align = args.flag_align.map(|x| x.0).unwrap_or(1);
David Brown562a7a02017-01-23 11:19:03 -0700113
Fabio Utzigebeecef2017-07-06 10:36:42 -0300114
David Browna3b93cf2017-03-29 12:41:26 -0600115 let device = match args.flag_device {
116 None => panic!("Missing mandatory device argument"),
117 Some(dev) => dev,
118 };
David Brownde7729e2017-01-09 10:41:35 -0700119
David Browna3b93cf2017-03-29 12:41:26 -0600120 status.run_single(device, align);
121 }
122
123 if args.cmd_runall {
124 for &dev in ALL_DEVICES {
125 for &align in &[1, 2, 4, 8] {
126 status.run_single(dev, align);
127 }
128 }
129 }
David Brown5c6b6792017-03-20 12:51:28 -0600130
David Brown361be7a2017-03-29 12:28:47 -0600131 if status.failures > 0 {
David Brown187dd882017-07-11 11:15:23 -0600132 error!("{} Tests ran with {} failures", status.failures + status.passes, status.failures);
David Brown361be7a2017-03-29 12:28:47 -0600133 process::exit(1);
134 } else {
135 warn!("{} Tests ran successfully", status.passes);
136 process::exit(0);
137 }
138}
David Brown5c6b6792017-03-20 12:51:28 -0600139
David Brown361be7a2017-03-29 12:28:47 -0600140struct RunStatus {
141 failures: usize,
142 passes: usize,
143}
David Brownde7729e2017-01-09 10:41:35 -0700144
David Brown361be7a2017-03-29 12:28:47 -0600145impl RunStatus {
146 fn new() -> RunStatus {
147 RunStatus {
148 failures: 0,
149 passes: 0,
David Brownde7729e2017-01-09 10:41:35 -0700150 }
151 }
David Brownde7729e2017-01-09 10:41:35 -0700152
David Brown361be7a2017-03-29 12:28:47 -0600153 fn run_single(&mut self, device: DeviceName, align: u8) {
David Browna3b93cf2017-03-29 12:41:26 -0600154 warn!("Running on device {} with alignment {}", device, align);
155
David Brown361be7a2017-03-29 12:28:47 -0600156 let (mut flash, areadesc) = match device {
157 DeviceName::Stm32f4 => {
158 // STM style flash. Large sectors, with a large scratch area.
David Brown7ddec0b2017-07-06 10:47:35 -0600159 let flash = SimFlash::new(vec![16 * 1024, 16 * 1024, 16 * 1024, 16 * 1024,
160 64 * 1024,
161 128 * 1024, 128 * 1024, 128 * 1024],
162 align as usize);
David Brown361be7a2017-03-29 12:28:47 -0600163 let mut areadesc = AreaDesc::new(&flash);
164 areadesc.add_image(0x020000, 0x020000, FlashId::Image0);
165 areadesc.add_image(0x040000, 0x020000, FlashId::Image1);
166 areadesc.add_image(0x060000, 0x020000, FlashId::ImageScratch);
167 (flash, areadesc)
168 }
169 DeviceName::K64f => {
170 // NXP style flash. Small sectors, one small sector for scratch.
David Brown7ddec0b2017-07-06 10:47:35 -0600171 let flash = SimFlash::new(vec![4096; 128], align as usize);
David Brown361be7a2017-03-29 12:28:47 -0600172
173 let mut areadesc = AreaDesc::new(&flash);
174 areadesc.add_image(0x020000, 0x020000, FlashId::Image0);
175 areadesc.add_image(0x040000, 0x020000, FlashId::Image1);
176 areadesc.add_image(0x060000, 0x001000, FlashId::ImageScratch);
177 (flash, areadesc)
178 }
179 DeviceName::K64fBig => {
180 // Simulating an STM style flash on top of an NXP style flash. Underlying flash device
181 // uses small sectors, but we tell the bootloader they are large.
David Brown7ddec0b2017-07-06 10:47:35 -0600182 let flash = SimFlash::new(vec![4096; 128], align as usize);
David Brown361be7a2017-03-29 12:28:47 -0600183
184 let mut areadesc = AreaDesc::new(&flash);
185 areadesc.add_simple_image(0x020000, 0x020000, FlashId::Image0);
186 areadesc.add_simple_image(0x040000, 0x020000, FlashId::Image1);
187 areadesc.add_simple_image(0x060000, 0x020000, FlashId::ImageScratch);
188 (flash, areadesc)
189 }
190 DeviceName::Nrf52840 => {
191 // Simulating the flash on the nrf52840 with partitions set up so that the scratch size
192 // does not divide into the image size.
David Brown7ddec0b2017-07-06 10:47:35 -0600193 let flash = SimFlash::new(vec![4096; 128], align as usize);
David Brown361be7a2017-03-29 12:28:47 -0600194
195 let mut areadesc = AreaDesc::new(&flash);
196 areadesc.add_image(0x008000, 0x034000, FlashId::Image0);
197 areadesc.add_image(0x03c000, 0x034000, FlashId::Image1);
198 areadesc.add_image(0x070000, 0x00d000, FlashId::ImageScratch);
199 (flash, areadesc)
200 }
201 };
202
203 let (slot0_base, slot0_len) = areadesc.find(FlashId::Image0);
204 let (slot1_base, slot1_len) = areadesc.find(FlashId::Image1);
205 let (scratch_base, _) = areadesc.find(FlashId::ImageScratch);
206
207 // Code below assumes that the slots are consecutive.
208 assert_eq!(slot1_base, slot0_base + slot0_len);
209 assert_eq!(scratch_base, slot1_base + slot1_len);
210
Fabio Utzigebeecef2017-07-06 10:36:42 -0300211 let offset_from_end = c::boot_magic_sz() + c::boot_max_align() * 2;
212
David Brown361be7a2017-03-29 12:28:47 -0600213 // println!("Areas: {:#?}", areadesc.get_c());
214
215 // Install the boot trailer signature, so that the code will start an upgrade.
216 // TODO: This must be a multiple of flash alignment, add support for an image that is smaller,
217 // and just gets padded.
David Brown361be7a2017-03-29 12:28:47 -0600218
Fabio Utzigebeecef2017-07-06 10:36:42 -0300219 // Create original and upgrade images
220 let slot0 = SlotInfo {
221 base_off: slot0_base as usize,
222 trailer_off: slot1_base - offset_from_end,
223 };
224
225 let slot1 = SlotInfo {
226 base_off: slot1_base as usize,
227 trailer_off: scratch_base - offset_from_end,
228 };
229
230 let images = Images {
231 slot0: slot0,
232 slot1: slot1,
233 primary: install_image(&mut flash, slot0_base, 32784),
234 upgrade: install_image(&mut flash, slot1_base, 41928),
235 };
236
237 let mut failed = false;
David Brown361be7a2017-03-29 12:28:47 -0600238
239 // Set an alignment, and position the magic value.
240 c::set_sim_flash_align(align);
David Brown361be7a2017-03-29 12:28:47 -0600241
Fabio Utzigebeecef2017-07-06 10:36:42 -0300242 mark_upgrade(&mut flash, &images.slot1);
David Brown361be7a2017-03-29 12:28:47 -0600243
Fabio Utzigebeecef2017-07-06 10:36:42 -0300244 // upgrades without fails, counts number of flash operations
245 let total_count = match run_basic_upgrade(&flash, &areadesc, &images) {
246 Ok(v) => v,
247 Err(_) => {
248 self.failures += 1;
249 return;
250 },
David Brown902d6172017-05-05 09:37:41 -0600251 };
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300252
Fabio Utzigebeecef2017-07-06 10:36:42 -0300253 failed |= run_basic_revert(&flash, &areadesc, &images);
254 failed |= run_revert_with_fails(&flash, &areadesc, &images, total_count);
255 failed |= run_perm_with_fails(&flash, &areadesc, &images, total_count);
256 failed |= run_perm_with_random_fails(&flash, &areadesc, &images,
257 total_count, 5);
258 failed |= run_norevert(&flash, &areadesc, &images);
David Brown361be7a2017-03-29 12:28:47 -0600259
Fabio Utzigebeecef2017-07-06 10:36:42 -0300260 //show_flash(&flash);
David Brown361be7a2017-03-29 12:28:47 -0600261
David Brown361be7a2017-03-29 12:28:47 -0600262 if failed {
263 self.failures += 1;
264 } else {
265 self.passes += 1;
266 }
David Brownc638f792017-01-10 12:34:33 -0700267 }
David Brownde7729e2017-01-09 10:41:35 -0700268}
269
Fabio Utzigebeecef2017-07-06 10:36:42 -0300270/// A simple upgrade without forced failures.
271///
272/// Returns the number of flash operations which can later be used to
273/// inject failures at chosen steps.
David Brown7ddec0b2017-07-06 10:47:35 -0600274fn run_basic_upgrade(flash: &SimFlash, areadesc: &AreaDesc, images: &Images)
Fabio Utzigebeecef2017-07-06 10:36:42 -0300275 -> Result<i32, ()> {
276 let (fl, total_count) = try_upgrade(&flash, &areadesc, &images, None);
277 info!("Total flash operation count={}", total_count);
278
279 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
280 warn!("Image mismatch after first boot");
281 Err(())
282 } else {
283 Ok(total_count)
284 }
285}
286
David Brown7ddec0b2017-07-06 10:47:35 -0600287fn run_basic_revert(flash: &SimFlash, areadesc: &AreaDesc, images: &Images) -> bool {
Fabio Utzigebeecef2017-07-06 10:36:42 -0300288 let mut fails = 0;
289
290 if Caps::SwapUpgrade.present() {
291 for count in 2 .. 5 {
292 info!("Try revert: {}", count);
293 let fl = try_revert(&flash, &areadesc, count);
294 if !verify_image(&fl, images.slot0.base_off, &images.primary) {
295 warn!("Revert failure on count {}", count);
296 fails += 1;
297 }
298 }
299 }
300
301 fails > 0
302}
303
David Brown7ddec0b2017-07-06 10:47:35 -0600304fn run_perm_with_fails(flash: &SimFlash, areadesc: &AreaDesc, images: &Images,
Fabio Utzigebeecef2017-07-06 10:36:42 -0300305 total_flash_ops: i32) -> bool {
306 let mut fails = 0;
307
308 // Let's try an image halfway through.
309 for i in 1 .. total_flash_ops {
310 info!("Try interruption at {}", i);
311 let (fl, count) = try_upgrade(&flash, &areadesc, &images, Some(i));
312 info!("Second boot, count={}", count);
313 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
314 warn!("FAIL at step {} of {}", i, total_flash_ops);
315 fails += 1;
316 }
317
318 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
319 COPY_DONE) {
320 warn!("Mismatched trailer for Slot 0");
321 fails += 1;
322 }
323
324 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
325 UNSET) {
326 warn!("Mismatched trailer for Slot 1");
327 fails += 1;
328 }
329
330 if Caps::SwapUpgrade.present() {
331 if !verify_image(&fl, images.slot1.base_off, &images.primary) {
332 warn!("Slot 1 FAIL at step {} of {}", i, total_flash_ops);
333 fails += 1;
334 }
335 }
336 }
337
338 info!("{} out of {} failed {:.2}%", fails, total_flash_ops,
339 fails as f32 * 100.0 / total_flash_ops as f32);
340
341 fails > 0
342}
343
David Brown7ddec0b2017-07-06 10:47:35 -0600344fn run_perm_with_random_fails(flash: &SimFlash, areadesc: &AreaDesc,
Fabio Utzigebeecef2017-07-06 10:36:42 -0300345 images: &Images, total_flash_ops: i32,
346 total_fails: usize) -> bool {
347 let mut fails = 0;
348 let (fl, total_counts) = try_random_fails(&flash, &areadesc, &images,
349 total_flash_ops, total_fails);
350 info!("Random interruptions at reset points={:?}", total_counts);
351
352 let slot0_ok = verify_image(&fl, images.slot0.base_off, &images.upgrade);
353 let slot1_ok = if Caps::SwapUpgrade.present() {
354 verify_image(&fl, images.slot1.base_off, &images.primary)
355 } else {
356 true
357 };
358 if !slot0_ok || !slot1_ok {
359 error!("Image mismatch after random interrupts: slot0={} slot1={}",
360 if slot0_ok { "ok" } else { "fail" },
361 if slot1_ok { "ok" } else { "fail" });
362 fails += 1;
363 }
364 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
365 COPY_DONE) {
366 error!("Mismatched trailer for Slot 0");
367 fails += 1;
368 }
369 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
370 UNSET) {
371 error!("Mismatched trailer for Slot 1");
372 fails += 1;
373 }
374
375 fails > 0
376}
377
David Brown7ddec0b2017-07-06 10:47:35 -0600378fn run_revert_with_fails(flash: &SimFlash, areadesc: &AreaDesc, images: &Images,
Fabio Utzigebeecef2017-07-06 10:36:42 -0300379 total_count: i32) -> bool {
380 let mut fails = 0;
381
382 if Caps::SwapUpgrade.present() {
383 for i in 1 .. (total_count - 1) {
384 info!("Try interruption at {}", i);
385 if try_revert_with_fail_at(&flash, &areadesc, &images, i) {
386 fails += 1;
387 }
388 }
389 }
390
391 fails > 0
392}
393
David Brown7ddec0b2017-07-06 10:47:35 -0600394fn run_norevert(flash: &SimFlash, areadesc: &AreaDesc, images: &Images) -> bool {
Fabio Utzigebeecef2017-07-06 10:36:42 -0300395 let mut fl = flash.clone();
396 let mut fails = 0;
397
398 info!("Try norevert");
399 c::set_flash_counter(0);
400
401 // First do a normal upgrade...
402 if c::boot_go(&mut fl, &areadesc) != 0 {
403 warn!("Failed first boot");
404 fails += 1;
405 }
406
407 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
408 warn!("Slot 0 image verification FAIL");
409 fails += 1;
410 }
411 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, UNSET,
412 COPY_DONE) {
413 warn!("Mismatched trailer for Slot 0");
414 fails += 1;
415 }
416 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
417 UNSET) {
418 warn!("Mismatched trailer for Slot 1");
419 fails += 1;
420 }
421
422 // Marks image in slot0 as permanent, no revert should happen...
423 mark_permanent_upgrade(&mut fl, &images.slot0);
424
425 if c::boot_go(&mut fl, &areadesc) != 0 {
426 warn!("Failed second boot");
427 fails += 1;
428 }
429
430 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
431 COPY_DONE) {
432 warn!("Mismatched trailer for Slot 0");
433 fails += 1;
434 }
435 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
436 warn!("Failed image verification");
437 fails += 1;
438 }
439
440 fails > 0
441}
442
443/// Test a boot, optionally stopping after 'n' flash options. Returns a count
444/// of the number of flash operations done total.
David Brown7ddec0b2017-07-06 10:47:35 -0600445fn try_upgrade(flash: &SimFlash, areadesc: &AreaDesc, images: &Images,
446 stop: Option<i32>) -> (SimFlash, i32) {
David Brownde7729e2017-01-09 10:41:35 -0700447 // Clone the flash to have a new copy.
448 let mut fl = flash.clone();
449
Fabio Utzigebeecef2017-07-06 10:36:42 -0300450 mark_permanent_upgrade(&mut fl, &images.slot1);
Fabio Utzig57652312017-04-25 19:54:26 -0300451
David Brownde7729e2017-01-09 10:41:35 -0700452 c::set_flash_counter(stop.unwrap_or(0));
Fabio Utzigebeecef2017-07-06 10:36:42 -0300453 let (first_interrupted, count) = match c::boot_go(&mut fl, &areadesc) {
David Brownde7729e2017-01-09 10:41:35 -0700454 -0x13579 => (true, stop.unwrap()),
455 0 => (false, -c::get_flash_counter()),
456 x => panic!("Unknown return: {}", x),
457 };
458 c::set_flash_counter(0);
459
460 if first_interrupted {
461 // fl.dump();
462 match c::boot_go(&mut fl, &areadesc) {
463 -0x13579 => panic!("Shouldn't stop again"),
464 0 => (),
465 x => panic!("Unknown return: {}", x),
466 }
467 }
468
Fabio Utzigebeecef2017-07-06 10:36:42 -0300469 (fl, count - c::get_flash_counter())
David Brownde7729e2017-01-09 10:41:35 -0700470}
471
David Brown7ddec0b2017-07-06 10:47:35 -0600472fn try_revert(flash: &SimFlash, areadesc: &AreaDesc, count: usize) -> SimFlash {
David Brownde7729e2017-01-09 10:41:35 -0700473 let mut fl = flash.clone();
474 c::set_flash_counter(0);
475
David Brown163ab232017-01-23 15:48:35 -0700476 // fl.write_file("image0.bin").unwrap();
477 for i in 0 .. count {
478 info!("Running boot pass {}", i + 1);
David Brownc638f792017-01-10 12:34:33 -0700479 assert_eq!(c::boot_go(&mut fl, &areadesc), 0);
480 }
David Brownde7729e2017-01-09 10:41:35 -0700481 fl
482}
483
David Brown7ddec0b2017-07-06 10:47:35 -0600484fn try_revert_with_fail_at(flash: &SimFlash, areadesc: &AreaDesc, images: &Images,
Fabio Utzigebeecef2017-07-06 10:36:42 -0300485 stop: i32) -> bool {
David Brownde7729e2017-01-09 10:41:35 -0700486 let mut fl = flash.clone();
Fabio Utzigebeecef2017-07-06 10:36:42 -0300487 let mut x: i32;
488 let mut fails = 0;
David Brownde7729e2017-01-09 10:41:35 -0700489
Fabio Utzigebeecef2017-07-06 10:36:42 -0300490 c::set_flash_counter(stop);
491 x = c::boot_go(&mut fl, &areadesc);
492 if x != -0x13579 {
493 warn!("Should have stopped at interruption point");
494 fails += 1;
495 }
496
497 if !verify_trailer(&fl, images.slot0.trailer_off, None, None, UNSET) {
498 warn!("copy_done should be unset");
499 fails += 1;
500 }
501
502 c::set_flash_counter(0);
503 x = c::boot_go(&mut fl, &areadesc);
504 if x != 0 {
505 warn!("Should have finished upgrade");
506 fails += 1;
507 }
508
509 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
510 warn!("Image in slot 0 before revert is invalid at stop={}", stop);
511 fails += 1;
512 }
513 if !verify_image(&fl, images.slot1.base_off, &images.primary) {
514 warn!("Image in slot 1 before revert is invalid at stop={}", stop);
515 fails += 1;
516 }
517 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, UNSET,
518 COPY_DONE) {
519 warn!("Mismatched trailer for Slot 0 before revert");
520 fails += 1;
521 }
522 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
523 UNSET) {
524 warn!("Mismatched trailer for Slot 1 before revert");
525 fails += 1;
526 }
527
528 // Do Revert
529 c::set_flash_counter(0);
530 x = c::boot_go(&mut fl, &areadesc);
531 if x != 0 {
532 warn!("Should have finished a revert");
533 fails += 1;
534 }
535
536 if !verify_image(&fl, images.slot0.base_off, &images.primary) {
537 warn!("Image in slot 0 after revert is invalid at stop={}", stop);
538 fails += 1;
539 }
540 if !verify_image(&fl, images.slot1.base_off, &images.upgrade) {
541 warn!("Image in slot 1 after revert is invalid at stop={}", stop);
542 fails += 1;
543 }
544 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
545 COPY_DONE) {
546 warn!("Mismatched trailer for Slot 1 after revert");
547 fails += 1;
548 }
549 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
550 UNSET) {
551 warn!("Mismatched trailer for Slot 1 after revert");
552 fails += 1;
553 }
554
555 fails > 0
David Brownde7729e2017-01-09 10:41:35 -0700556}
557
David Brown7ddec0b2017-07-06 10:47:35 -0600558fn try_random_fails(flash: &SimFlash, areadesc: &AreaDesc, images: &Images,
559 total_ops: i32, count: usize) -> (SimFlash, Vec<i32>) {
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300560 let mut fl = flash.clone();
561
Fabio Utzigebeecef2017-07-06 10:36:42 -0300562 mark_permanent_upgrade(&mut fl, &images.slot1);
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300563
564 let mut rng = rand::thread_rng();
Fabio Utzig57652312017-04-25 19:54:26 -0300565 let mut resets = vec![0i32; count];
566 let mut remaining_ops = total_ops;
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300567 for i in 0 .. count {
Fabio Utzig57652312017-04-25 19:54:26 -0300568 let ops = Range::new(1, remaining_ops / 2);
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300569 let reset_counter = ops.ind_sample(&mut rng);
570 c::set_flash_counter(reset_counter);
571 match c::boot_go(&mut fl, &areadesc) {
572 0 | -0x13579 => (),
573 x => panic!("Unknown return: {}", x),
574 }
Fabio Utzig57652312017-04-25 19:54:26 -0300575 remaining_ops -= reset_counter;
576 resets[i] = reset_counter;
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300577 }
578
579 c::set_flash_counter(0);
580 match c::boot_go(&mut fl, &areadesc) {
581 -0x13579 => panic!("Should not be have been interrupted!"),
582 0 => (),
583 x => panic!("Unknown return: {}", x),
584 }
585
Fabio Utzig57652312017-04-25 19:54:26 -0300586 (fl, resets)
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300587}
588
David Brownde7729e2017-01-09 10:41:35 -0700589/// Show the flash layout.
590#[allow(dead_code)]
591fn show_flash(flash: &Flash) {
592 println!("---- Flash configuration ----");
593 for sector in flash.sector_iter() {
Fabio Utzigebeecef2017-07-06 10:36:42 -0300594 println!(" {:3}: 0x{:08x}, 0x{:08x}",
David Brownde7729e2017-01-09 10:41:35 -0700595 sector.num, sector.base, sector.size);
596 }
597 println!("");
598}
599
600/// Install a "program" into the given image. This fakes the image header, or at least all of the
601/// fields used by the given code. Returns a copy of the image that was written.
602fn install_image(flash: &mut Flash, offset: usize, len: usize) -> Vec<u8> {
603 let offset0 = offset;
604
David Brown187dd882017-07-11 11:15:23 -0600605 let mut tlv = TlvGen::new_hash_only();
606
David Brownde7729e2017-01-09 10:41:35 -0700607 // Generate a boot header. Note that the size doesn't include the header.
608 let header = ImageHeader {
609 magic: 0x96f3b83c,
David Brown187dd882017-07-11 11:15:23 -0600610 tlv_size: tlv.get_size(),
David Brownde7729e2017-01-09 10:41:35 -0700611 _pad1: 0,
612 hdr_size: 32,
613 key_id: 0,
614 _pad2: 0,
615 img_size: len as u32,
David Brown187dd882017-07-11 11:15:23 -0600616 flags: tlv.get_flags(),
David Brownde7729e2017-01-09 10:41:35 -0700617 ver: ImageVersion {
David Browne380fa62017-01-23 15:49:09 -0700618 major: (offset / (128 * 1024)) as u8,
David Brownde7729e2017-01-09 10:41:35 -0700619 minor: 0,
620 revision: 1,
David Browne380fa62017-01-23 15:49:09 -0700621 build_num: offset as u32,
David Brownde7729e2017-01-09 10:41:35 -0700622 },
623 _pad3: 0,
624 };
625
626 let b_header = header.as_raw();
David Brown187dd882017-07-11 11:15:23 -0600627 tlv.add_bytes(&b_header);
David Brownde7729e2017-01-09 10:41:35 -0700628 /*
629 let b_header = unsafe { slice::from_raw_parts(&header as *const _ as *const u8,
630 mem::size_of::<ImageHeader>()) };
631 */
632 assert_eq!(b_header.len(), 32);
633 flash.write(offset, &b_header).unwrap();
634 let offset = offset + b_header.len();
635
636 // The core of the image itself is just pseudorandom data.
637 let mut buf = vec![0; len];
638 splat(&mut buf, offset);
David Brown187dd882017-07-11 11:15:23 -0600639 tlv.add_bytes(&buf);
640
641 // Get and append the TLV itself.
642 buf.append(&mut tlv.make_tlv());
643
644 // Pad the block to a flash alignment (8 bytes).
645 while buf.len() % 8 != 0 {
646 buf.push(0xFF);
647 }
648
David Brownde7729e2017-01-09 10:41:35 -0700649 flash.write(offset, &buf).unwrap();
650 let offset = offset + buf.len();
651
652 // Copy out the image so that we can verify that the image was installed correctly later.
653 let mut copy = vec![0u8; offset - offset0];
654 flash.read(offset0, &mut copy).unwrap();
655
656 copy
657}
658
659/// Verify that given image is present in the flash at the given offset.
660fn verify_image(flash: &Flash, offset: usize, buf: &[u8]) -> bool {
661 let mut copy = vec![0u8; buf.len()];
662 flash.read(offset, &mut copy).unwrap();
663
664 if buf != &copy[..] {
665 for i in 0 .. buf.len() {
666 if buf[i] != copy[i] {
David Brown4440af82017-01-09 12:15:05 -0700667 info!("First failure at {:#x}", offset + i);
David Brownde7729e2017-01-09 10:41:35 -0700668 break;
669 }
670 }
671 false
672 } else {
673 true
674 }
675}
676
Fabio Utzigebeecef2017-07-06 10:36:42 -0300677fn verify_trailer(flash: &Flash, offset: usize,
678 magic: Option<&[u8]>, image_ok: Option<u8>,
679 copy_done: Option<u8>) -> bool {
680 let mut copy = vec![0u8; c::boot_magic_sz() + c::boot_max_align() * 2];
681 let mut failed = false;
682
683 flash.read(offset, &mut copy).unwrap();
684
685 failed |= match magic {
686 Some(v) => {
687 if &copy[16..] != v {
688 warn!("\"magic\" mismatch at {:#x}", offset);
689 true
690 } else {
691 false
692 }
693 },
694 None => false,
695 };
696
697 failed |= match image_ok {
698 Some(v) => {
699 if copy[8] != v {
700 warn!("\"image_ok\" mismatch at {:#x}", offset);
701 true
702 } else {
703 false
704 }
705 },
706 None => false,
707 };
708
709 failed |= match copy_done {
710 Some(v) => {
711 if copy[0] != v {
712 warn!("\"copy_done\" mismatch at {:#x}", offset);
713 true
714 } else {
715 false
716 }
717 },
718 None => false,
719 };
720
721 !failed
722}
723
David Brownde7729e2017-01-09 10:41:35 -0700724/// The image header
725#[repr(C)]
726pub struct ImageHeader {
727 magic: u32,
728 tlv_size: u16,
729 key_id: u8,
730 _pad1: u8,
731 hdr_size: u16,
732 _pad2: u16,
733 img_size: u32,
734 flags: u32,
735 ver: ImageVersion,
736 _pad3: u32,
737}
738
739impl AsRaw for ImageHeader {}
740
741#[repr(C)]
742pub struct ImageVersion {
743 major: u8,
744 minor: u8,
745 revision: u16,
746 build_num: u32,
747}
748
Fabio Utzigebeecef2017-07-06 10:36:42 -0300749struct SlotInfo {
750 base_off: usize,
751 trailer_off: usize,
752}
753
754struct Images {
755 slot0: SlotInfo,
756 slot1: SlotInfo,
757 primary: Vec<u8>,
758 upgrade: Vec<u8>,
759}
760
761const MAGIC_VALID: Option<&[u8]> = Some(&[0x77, 0xc2, 0x95, 0xf3,
762 0x60, 0xd2, 0xef, 0x7f,
763 0x35, 0x52, 0x50, 0x0f,
764 0x2c, 0xb6, 0x79, 0x80]);
765const MAGIC_UNSET: Option<&[u8]> = Some(&[0xff; 16]);
766
767const COPY_DONE: Option<u8> = Some(1);
768const IMAGE_OK: Option<u8> = Some(1);
769const UNSET: Option<u8> = Some(0xff);
770
David Brownde7729e2017-01-09 10:41:35 -0700771/// Write out the magic so that the loader tries doing an upgrade.
Fabio Utzigebeecef2017-07-06 10:36:42 -0300772fn mark_upgrade(flash: &mut Flash, slot: &SlotInfo) {
773 let offset = slot.trailer_off + c::boot_max_align() * 2;
774 flash.write(offset, MAGIC_VALID.unwrap()).unwrap();
775}
776
777/// Writes the image_ok flag which, guess what, tells the bootloader
778/// the this image is ok (not a test, and no revert is to be performed).
779fn mark_permanent_upgrade(flash: &mut Flash, slot: &SlotInfo) {
780 let ok = [1u8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
781 let align = c::get_sim_flash_align() as usize;
782 let off = slot.trailer_off + c::boot_max_align();
783 flash.write(off, &ok[..align]).unwrap();
David Brownde7729e2017-01-09 10:41:35 -0700784}
785
786// Drop some pseudo-random gibberish onto the data.
787fn splat(data: &mut [u8], seed: usize) {
788 let seed_block = [0x135782ea, 0x92184728, data.len() as u32, seed as u32];
789 let mut rng: XorShiftRng = SeedableRng::from_seed(seed_block);
790 rng.fill_bytes(data);
791}
792
793/// Return a read-only view into the raw bytes of this object
794trait AsRaw : Sized {
795 fn as_raw<'a>(&'a self) -> &'a [u8] {
796 unsafe { slice::from_raw_parts(self as *const _ as *const u8,
797 mem::size_of::<Self>()) }
798 }
799}
800
801fn show_sizes() {
802 // This isn't panic safe.
803 let old_align = c::get_sim_flash_align();
804 for min in &[1, 2, 4, 8] {
805 c::set_sim_flash_align(*min);
806 let msize = c::boot_trailer_sz();
807 println!("{:2}: {} (0x{:x})", min, msize, msize);
808 }
809 c::set_sim_flash_align(old_align);
810}