blob: a2b1a934b40265286cfd748e08f848e1d04aa251 [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;
David Brown046a0a62017-07-12 16:08:22 -06009#[macro_use] extern crate serde_derive;
10extern crate serde;
David Brown2cbc4702017-07-06 14:18:58 -060011extern crate simflash;
David Brown7e701d82017-07-11 13:24:25 -060012extern crate untrusted;
David Brown63902772017-07-12 09:47:49 -060013extern crate mcuboot_sys;
David Brownde7729e2017-01-09 10:41:35 -070014
15use docopt::Docopt;
David Brown4cb26232017-04-11 08:15:18 -060016use rand::{Rng, SeedableRng, XorShiftRng};
Fabio Utzigbb5635e2017-04-10 09:07:02 -030017use rand::distributions::{IndependentSample, Range};
David Browna3b93cf2017-03-29 12:41:26 -060018use std::fmt;
David Brownde7729e2017-01-09 10:41:35 -070019use std::mem;
David Brown361be7a2017-03-29 12:28:47 -060020use std::process;
David Brownde7729e2017-01-09 10:41:35 -070021use std::slice;
22
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 Brownf52272c2017-07-12 09:56:16 -060027use mcuboot_sys::{c, 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
David Brown046a0a62017-07-12 16:08:22 -060048#[derive(Debug, Deserialize)]
David Brownde7729e2017-01-09 10:41:35 -070049struct 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 Brown046a0a62017-07-12 16:08:22 -060059#[derive(Copy, Clone, Debug, Deserialize)]
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
David Brown046a0a62017-07-12 16:08:22 -060084struct AlignArgVisitor;
85
86impl<'de> serde::de::Visitor<'de> for AlignArgVisitor {
87 type Value = AlignArg;
88
89 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
90 formatter.write_str("1, 2, 4 or 8")
91 }
92
93 fn visit_u8<E>(self, n: u8) -> Result<Self::Value, E>
94 where E: serde::de::Error
95 {
96 Ok(match n {
97 1 | 2 | 4 | 8 => AlignArg(n),
98 n => {
99 let err = format!("Could not deserialize '{}' as alignment", n);
100 return Err(E::custom(err));
101 }
102 })
103 }
104}
105
106impl<'de> serde::de::Deserialize<'de> for AlignArg {
107 fn deserialize<D>(d: D) -> Result<AlignArg, D::Error>
108 where D: serde::de::Deserializer<'de>
109 {
110 d.deserialize_u8(AlignArgVisitor)
David Brownde7729e2017-01-09 10:41:35 -0700111 }
112}
113
114fn main() {
David Brown4440af82017-01-09 12:15:05 -0700115 env_logger::init().unwrap();
116
David Brownde7729e2017-01-09 10:41:35 -0700117 let args: Args = Docopt::new(USAGE)
David Brown046a0a62017-07-12 16:08:22 -0600118 .and_then(|d| d.deserialize())
David Brownde7729e2017-01-09 10:41:35 -0700119 .unwrap_or_else(|e| e.exit());
120 // println!("args: {:#?}", args);
121
122 if args.cmd_sizes {
123 show_sizes();
124 return;
125 }
126
David Brown361be7a2017-03-29 12:28:47 -0600127 let mut status = RunStatus::new();
David Browna3b93cf2017-03-29 12:41:26 -0600128 if args.cmd_run {
David Brown361be7a2017-03-29 12:28:47 -0600129
David Browna3b93cf2017-03-29 12:41:26 -0600130 let align = args.flag_align.map(|x| x.0).unwrap_or(1);
David Brown562a7a02017-01-23 11:19:03 -0700131
Fabio Utzigebeecef2017-07-06 10:36:42 -0300132
David Browna3b93cf2017-03-29 12:41:26 -0600133 let device = match args.flag_device {
134 None => panic!("Missing mandatory device argument"),
135 Some(dev) => dev,
136 };
David Brownde7729e2017-01-09 10:41:35 -0700137
David Browna3b93cf2017-03-29 12:41:26 -0600138 status.run_single(device, align);
139 }
140
141 if args.cmd_runall {
142 for &dev in ALL_DEVICES {
143 for &align in &[1, 2, 4, 8] {
144 status.run_single(dev, align);
145 }
146 }
147 }
David Brown5c6b6792017-03-20 12:51:28 -0600148
David Brown361be7a2017-03-29 12:28:47 -0600149 if status.failures > 0 {
David Brown187dd882017-07-11 11:15:23 -0600150 error!("{} Tests ran with {} failures", status.failures + status.passes, status.failures);
David Brown361be7a2017-03-29 12:28:47 -0600151 process::exit(1);
152 } else {
Fabio Utzig7b47ef72017-07-13 09:34:33 -0300153 error!("{} Tests ran successfully", status.passes);
David Brown361be7a2017-03-29 12:28:47 -0600154 process::exit(0);
155 }
156}
David Brown5c6b6792017-03-20 12:51:28 -0600157
David Brown361be7a2017-03-29 12:28:47 -0600158struct RunStatus {
159 failures: usize,
160 passes: usize,
161}
David Brownde7729e2017-01-09 10:41:35 -0700162
David Brown361be7a2017-03-29 12:28:47 -0600163impl RunStatus {
164 fn new() -> RunStatus {
165 RunStatus {
166 failures: 0,
167 passes: 0,
David Brownde7729e2017-01-09 10:41:35 -0700168 }
169 }
David Brownde7729e2017-01-09 10:41:35 -0700170
David Brown361be7a2017-03-29 12:28:47 -0600171 fn run_single(&mut self, device: DeviceName, align: u8) {
David Browna3b93cf2017-03-29 12:41:26 -0600172 warn!("Running on device {} with alignment {}", device, align);
173
David Brown361be7a2017-03-29 12:28:47 -0600174 let (mut flash, areadesc) = match device {
175 DeviceName::Stm32f4 => {
176 // STM style flash. Large sectors, with a large scratch area.
David Brown7ddec0b2017-07-06 10:47:35 -0600177 let flash = SimFlash::new(vec![16 * 1024, 16 * 1024, 16 * 1024, 16 * 1024,
178 64 * 1024,
179 128 * 1024, 128 * 1024, 128 * 1024],
180 align as usize);
David Brown361be7a2017-03-29 12:28:47 -0600181 let mut areadesc = AreaDesc::new(&flash);
182 areadesc.add_image(0x020000, 0x020000, FlashId::Image0);
183 areadesc.add_image(0x040000, 0x020000, FlashId::Image1);
184 areadesc.add_image(0x060000, 0x020000, FlashId::ImageScratch);
185 (flash, areadesc)
186 }
187 DeviceName::K64f => {
188 // NXP style flash. Small sectors, one small sector for scratch.
David Brown7ddec0b2017-07-06 10:47:35 -0600189 let flash = SimFlash::new(vec![4096; 128], align as usize);
David Brown361be7a2017-03-29 12:28:47 -0600190
191 let mut areadesc = AreaDesc::new(&flash);
192 areadesc.add_image(0x020000, 0x020000, FlashId::Image0);
193 areadesc.add_image(0x040000, 0x020000, FlashId::Image1);
194 areadesc.add_image(0x060000, 0x001000, FlashId::ImageScratch);
195 (flash, areadesc)
196 }
197 DeviceName::K64fBig => {
198 // Simulating an STM style flash on top of an NXP style flash. Underlying flash device
199 // uses small sectors, but we tell the bootloader they are large.
David Brown7ddec0b2017-07-06 10:47:35 -0600200 let flash = SimFlash::new(vec![4096; 128], align as usize);
David Brown361be7a2017-03-29 12:28:47 -0600201
202 let mut areadesc = AreaDesc::new(&flash);
203 areadesc.add_simple_image(0x020000, 0x020000, FlashId::Image0);
204 areadesc.add_simple_image(0x040000, 0x020000, FlashId::Image1);
205 areadesc.add_simple_image(0x060000, 0x020000, FlashId::ImageScratch);
206 (flash, areadesc)
207 }
208 DeviceName::Nrf52840 => {
209 // Simulating the flash on the nrf52840 with partitions set up so that the scratch size
210 // does not divide into the image size.
David Brown7ddec0b2017-07-06 10:47:35 -0600211 let flash = SimFlash::new(vec![4096; 128], align as usize);
David Brown361be7a2017-03-29 12:28:47 -0600212
213 let mut areadesc = AreaDesc::new(&flash);
214 areadesc.add_image(0x008000, 0x034000, FlashId::Image0);
215 areadesc.add_image(0x03c000, 0x034000, FlashId::Image1);
216 areadesc.add_image(0x070000, 0x00d000, FlashId::ImageScratch);
217 (flash, areadesc)
218 }
219 };
220
221 let (slot0_base, slot0_len) = areadesc.find(FlashId::Image0);
222 let (slot1_base, slot1_len) = areadesc.find(FlashId::Image1);
223 let (scratch_base, _) = areadesc.find(FlashId::ImageScratch);
224
225 // Code below assumes that the slots are consecutive.
226 assert_eq!(slot1_base, slot0_base + slot0_len);
227 assert_eq!(scratch_base, slot1_base + slot1_len);
228
Fabio Utzigebeecef2017-07-06 10:36:42 -0300229 let offset_from_end = c::boot_magic_sz() + c::boot_max_align() * 2;
230
David Brown361be7a2017-03-29 12:28:47 -0600231 // println!("Areas: {:#?}", areadesc.get_c());
232
233 // Install the boot trailer signature, so that the code will start an upgrade.
234 // TODO: This must be a multiple of flash alignment, add support for an image that is smaller,
235 // and just gets padded.
David Brown361be7a2017-03-29 12:28:47 -0600236
Fabio Utzigebeecef2017-07-06 10:36:42 -0300237 // Create original and upgrade images
238 let slot0 = SlotInfo {
239 base_off: slot0_base as usize,
240 trailer_off: slot1_base - offset_from_end,
241 };
242
243 let slot1 = SlotInfo {
244 base_off: slot1_base as usize,
245 trailer_off: scratch_base - offset_from_end,
246 };
247
Fabio Utzig645e5142017-07-17 15:36:13 -0300248 // Set an alignment, and position the magic value.
249 c::set_sim_flash_align(align);
Fabio Utzigebeecef2017-07-06 10:36:42 -0300250
251 let mut failed = false;
David Brown361be7a2017-03-29 12:28:47 -0600252
Fabio Utzig645e5142017-07-17 15:36:13 -0300253 // Creates a badly signed image in slot1 to check that it is not
254 // upgraded to
255 let mut bad_flash = flash.clone();
256 let bad_slot1_image = Images {
257 slot0: &slot0,
258 slot1: &slot1,
259 primary: install_image(&mut bad_flash, slot0_base, 32784, false),
260 upgrade: install_image(&mut bad_flash, slot1_base, 41928, true),
261 };
262
263 failed |= run_signfail_upgrade(&bad_flash, &areadesc, &bad_slot1_image);
264
265 let images = Images {
266 slot0: &slot0,
267 slot1: &slot1,
268 primary: install_image(&mut flash, slot0_base, 32784, false),
269 upgrade: install_image(&mut flash, slot1_base, 41928, false),
270 };
David Brown361be7a2017-03-29 12:28:47 -0600271
Fabio Utzig7b47ef72017-07-13 09:34:33 -0300272 failed |= run_norevert_newimage(&flash, &areadesc, &images);
273
Fabio Utzigebeecef2017-07-06 10:36:42 -0300274 mark_upgrade(&mut flash, &images.slot1);
David Brown361be7a2017-03-29 12:28:47 -0600275
Fabio Utzigebeecef2017-07-06 10:36:42 -0300276 // upgrades without fails, counts number of flash operations
277 let total_count = match run_basic_upgrade(&flash, &areadesc, &images) {
278 Ok(v) => v,
279 Err(_) => {
280 self.failures += 1;
281 return;
282 },
David Brown902d6172017-05-05 09:37:41 -0600283 };
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300284
Fabio Utzigebeecef2017-07-06 10:36:42 -0300285 failed |= run_basic_revert(&flash, &areadesc, &images);
286 failed |= run_revert_with_fails(&flash, &areadesc, &images, total_count);
287 failed |= run_perm_with_fails(&flash, &areadesc, &images, total_count);
288 failed |= run_perm_with_random_fails(&flash, &areadesc, &images,
Fabio Utzig7b47ef72017-07-13 09:34:33 -0300289 total_count, 5);
Fabio Utzigebeecef2017-07-06 10:36:42 -0300290 failed |= run_norevert(&flash, &areadesc, &images);
David Brown361be7a2017-03-29 12:28:47 -0600291
Fabio Utzigebeecef2017-07-06 10:36:42 -0300292 //show_flash(&flash);
David Brown361be7a2017-03-29 12:28:47 -0600293
David Brown361be7a2017-03-29 12:28:47 -0600294 if failed {
295 self.failures += 1;
296 } else {
297 self.passes += 1;
298 }
David Brownc638f792017-01-10 12:34:33 -0700299 }
David Brownde7729e2017-01-09 10:41:35 -0700300}
301
Fabio Utzigebeecef2017-07-06 10:36:42 -0300302/// A simple upgrade without forced failures.
303///
304/// Returns the number of flash operations which can later be used to
305/// inject failures at chosen steps.
David Brown7ddec0b2017-07-06 10:47:35 -0600306fn run_basic_upgrade(flash: &SimFlash, areadesc: &AreaDesc, images: &Images)
Fabio Utzigebeecef2017-07-06 10:36:42 -0300307 -> Result<i32, ()> {
308 let (fl, total_count) = try_upgrade(&flash, &areadesc, &images, None);
309 info!("Total flash operation count={}", total_count);
310
311 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
312 warn!("Image mismatch after first boot");
313 Err(())
314 } else {
315 Ok(total_count)
316 }
317}
318
David Brown7ddec0b2017-07-06 10:47:35 -0600319fn run_basic_revert(flash: &SimFlash, areadesc: &AreaDesc, images: &Images) -> bool {
Fabio Utzigebeecef2017-07-06 10:36:42 -0300320 let mut fails = 0;
321
Fabio Utzig7b47ef72017-07-13 09:34:33 -0300322 // FIXME: this test would also pass if no swap is ever performed???
Fabio Utzigebeecef2017-07-06 10:36:42 -0300323 if Caps::SwapUpgrade.present() {
324 for count in 2 .. 5 {
325 info!("Try revert: {}", count);
326 let fl = try_revert(&flash, &areadesc, count);
327 if !verify_image(&fl, images.slot0.base_off, &images.primary) {
Fabio Utzig7b47ef72017-07-13 09:34:33 -0300328 error!("Revert failure on count {}", count);
Fabio Utzigebeecef2017-07-06 10:36:42 -0300329 fails += 1;
330 }
331 }
332 }
333
334 fails > 0
335}
336
David Brown7ddec0b2017-07-06 10:47:35 -0600337fn run_perm_with_fails(flash: &SimFlash, areadesc: &AreaDesc, images: &Images,
Fabio Utzigebeecef2017-07-06 10:36:42 -0300338 total_flash_ops: i32) -> bool {
339 let mut fails = 0;
340
341 // Let's try an image halfway through.
342 for i in 1 .. total_flash_ops {
343 info!("Try interruption at {}", i);
344 let (fl, count) = try_upgrade(&flash, &areadesc, &images, Some(i));
345 info!("Second boot, count={}", count);
346 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
347 warn!("FAIL at step {} of {}", i, total_flash_ops);
348 fails += 1;
349 }
350
351 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
352 COPY_DONE) {
353 warn!("Mismatched trailer for Slot 0");
354 fails += 1;
355 }
356
357 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
358 UNSET) {
359 warn!("Mismatched trailer for Slot 1");
360 fails += 1;
361 }
362
363 if Caps::SwapUpgrade.present() {
364 if !verify_image(&fl, images.slot1.base_off, &images.primary) {
365 warn!("Slot 1 FAIL at step {} of {}", i, total_flash_ops);
366 fails += 1;
367 }
368 }
369 }
370
Fabio Utzig7b47ef72017-07-13 09:34:33 -0300371 if fails > 0 {
372 error!("{} out of {} failed {:.2}%", fails, total_flash_ops,
373 fails as f32 * 100.0 / total_flash_ops as f32);
374 }
Fabio Utzigebeecef2017-07-06 10:36:42 -0300375
376 fails > 0
377}
378
David Brown7ddec0b2017-07-06 10:47:35 -0600379fn run_perm_with_random_fails(flash: &SimFlash, areadesc: &AreaDesc,
Fabio Utzigebeecef2017-07-06 10:36:42 -0300380 images: &Images, total_flash_ops: i32,
381 total_fails: usize) -> bool {
382 let mut fails = 0;
383 let (fl, total_counts) = try_random_fails(&flash, &areadesc, &images,
384 total_flash_ops, total_fails);
385 info!("Random interruptions at reset points={:?}", total_counts);
386
387 let slot0_ok = verify_image(&fl, images.slot0.base_off, &images.upgrade);
388 let slot1_ok = if Caps::SwapUpgrade.present() {
389 verify_image(&fl, images.slot1.base_off, &images.primary)
390 } else {
391 true
392 };
393 if !slot0_ok || !slot1_ok {
394 error!("Image mismatch after random interrupts: slot0={} slot1={}",
395 if slot0_ok { "ok" } else { "fail" },
396 if slot1_ok { "ok" } else { "fail" });
397 fails += 1;
398 }
399 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
400 COPY_DONE) {
401 error!("Mismatched trailer for Slot 0");
402 fails += 1;
403 }
404 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
405 UNSET) {
406 error!("Mismatched trailer for Slot 1");
407 fails += 1;
408 }
409
Fabio Utzig7b47ef72017-07-13 09:34:33 -0300410 if fails > 0 {
411 error!("Error testing perm upgrade with {} fails", total_fails);
412 }
413
Fabio Utzigebeecef2017-07-06 10:36:42 -0300414 fails > 0
415}
416
David Brown7ddec0b2017-07-06 10:47:35 -0600417fn run_revert_with_fails(flash: &SimFlash, areadesc: &AreaDesc, images: &Images,
Fabio Utzigebeecef2017-07-06 10:36:42 -0300418 total_count: i32) -> bool {
419 let mut fails = 0;
420
421 if Caps::SwapUpgrade.present() {
422 for i in 1 .. (total_count - 1) {
423 info!("Try interruption at {}", i);
424 if try_revert_with_fail_at(&flash, &areadesc, &images, i) {
Fabio Utzig7b47ef72017-07-13 09:34:33 -0300425 error!("Revert failed at interruption {}", i);
Fabio Utzigebeecef2017-07-06 10:36:42 -0300426 fails += 1;
427 }
428 }
429 }
430
431 fails > 0
432}
433
David Brown7ddec0b2017-07-06 10:47:35 -0600434fn run_norevert(flash: &SimFlash, areadesc: &AreaDesc, images: &Images) -> bool {
Fabio Utzigebeecef2017-07-06 10:36:42 -0300435 let mut fl = flash.clone();
436 let mut fails = 0;
437
438 info!("Try norevert");
439 c::set_flash_counter(0);
440
441 // First do a normal upgrade...
442 if c::boot_go(&mut fl, &areadesc) != 0 {
443 warn!("Failed first boot");
444 fails += 1;
445 }
446
Fabio Utzig7b47ef72017-07-13 09:34:33 -0300447 //FIXME: copy_done is written by boot_go, is it ok if no copy
448 // was ever done?
449
Fabio Utzigebeecef2017-07-06 10:36:42 -0300450 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
451 warn!("Slot 0 image verification FAIL");
452 fails += 1;
453 }
454 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, UNSET,
455 COPY_DONE) {
456 warn!("Mismatched trailer for Slot 0");
457 fails += 1;
458 }
459 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
460 UNSET) {
461 warn!("Mismatched trailer for Slot 1");
462 fails += 1;
463 }
464
465 // Marks image in slot0 as permanent, no revert should happen...
466 mark_permanent_upgrade(&mut fl, &images.slot0);
467
Fabio Utzig7b47ef72017-07-13 09:34:33 -0300468 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
469 COPY_DONE) {
470 warn!("Mismatched trailer for Slot 0");
471 fails += 1;
472 }
473
Fabio Utzigebeecef2017-07-06 10:36:42 -0300474 if c::boot_go(&mut fl, &areadesc) != 0 {
475 warn!("Failed second boot");
476 fails += 1;
477 }
478
479 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
480 COPY_DONE) {
481 warn!("Mismatched trailer for Slot 0");
482 fails += 1;
483 }
484 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
485 warn!("Failed image verification");
486 fails += 1;
487 }
488
Fabio Utzig7b47ef72017-07-13 09:34:33 -0300489 if fails > 0 {
490 error!("Error running upgrade without revert");
491 }
492
493 fails > 0
494}
495
496// Tests a new image written to slot0 that already has magic and image_ok set
497// while there is no image on slot1, so no revert should ever happen...
498fn run_norevert_newimage(flash: &SimFlash, areadesc: &AreaDesc,
499 images: &Images) -> bool {
500 let mut fl = flash.clone();
501 let mut fails = 0;
502
503 info!("Try non-revert on imgtool generated image");
504 c::set_flash_counter(0);
505
506 mark_upgrade(&mut fl, &images.slot0);
507
508 // This simulates writing an image created by imgtool to Slot 0
509 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, UNSET, UNSET) {
510 warn!("Mismatched trailer for Slot 0");
511 fails += 1;
512 }
513
514 // Run the bootloader...
515 if c::boot_go(&mut fl, &areadesc) != 0 {
516 warn!("Failed first boot");
517 fails += 1;
518 }
519
520 // State should not have changed
521 if !verify_image(&fl, images.slot0.base_off, &images.primary) {
522 warn!("Failed image verification");
523 fails += 1;
524 }
525 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, UNSET,
526 UNSET) {
527 warn!("Mismatched trailer for Slot 0");
528 fails += 1;
529 }
530 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
531 UNSET) {
532 warn!("Mismatched trailer for Slot 1");
533 fails += 1;
534 }
535
536 if fails > 0 {
537 error!("Expected a non revert with new image");
538 }
539
Fabio Utzigebeecef2017-07-06 10:36:42 -0300540 fails > 0
541}
542
Fabio Utzig645e5142017-07-17 15:36:13 -0300543// Tests a new image written to slot0 that already has magic and image_ok set
544// while there is no image on slot1, so no revert should ever happen...
545fn run_signfail_upgrade(flash: &SimFlash, areadesc: &AreaDesc,
546 images: &Images) -> bool {
547 let mut fl = flash.clone();
548 let mut fails = 0;
549
550 info!("Try upgrade image with bad signature");
551 c::set_flash_counter(0);
552
553 mark_upgrade(&mut fl, &images.slot0);
554 mark_permanent_upgrade(&mut fl, &images.slot0);
555 mark_upgrade(&mut fl, &images.slot1);
556
557 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
558 UNSET) {
559 warn!("Mismatched trailer for Slot 0");
560 fails += 1;
561 }
562
563 // Run the bootloader...
564 if c::boot_go(&mut fl, &areadesc) != 0 {
565 warn!("Failed first boot");
566 fails += 1;
567 }
568
569 // State should not have changed
570 if !verify_image(&fl, images.slot0.base_off, &images.primary) {
571 warn!("Failed image verification");
572 fails += 1;
573 }
574 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
575 UNSET) {
576 warn!("Mismatched trailer for Slot 0");
577 fails += 1;
578 }
579
580 if fails > 0 {
581 error!("Expected an upgrade failure when image has bad signature");
582 }
583
584 fails > 0
585}
586
Fabio Utzigebeecef2017-07-06 10:36:42 -0300587/// Test a boot, optionally stopping after 'n' flash options. Returns a count
588/// of the number of flash operations done total.
David Brown7ddec0b2017-07-06 10:47:35 -0600589fn try_upgrade(flash: &SimFlash, areadesc: &AreaDesc, images: &Images,
590 stop: Option<i32>) -> (SimFlash, i32) {
David Brownde7729e2017-01-09 10:41:35 -0700591 // Clone the flash to have a new copy.
592 let mut fl = flash.clone();
593
Fabio Utzigebeecef2017-07-06 10:36:42 -0300594 mark_permanent_upgrade(&mut fl, &images.slot1);
Fabio Utzig57652312017-04-25 19:54:26 -0300595
David Brownde7729e2017-01-09 10:41:35 -0700596 c::set_flash_counter(stop.unwrap_or(0));
Fabio Utzigebeecef2017-07-06 10:36:42 -0300597 let (first_interrupted, count) = match c::boot_go(&mut fl, &areadesc) {
David Brownde7729e2017-01-09 10:41:35 -0700598 -0x13579 => (true, stop.unwrap()),
599 0 => (false, -c::get_flash_counter()),
600 x => panic!("Unknown return: {}", x),
601 };
602 c::set_flash_counter(0);
603
604 if first_interrupted {
605 // fl.dump();
606 match c::boot_go(&mut fl, &areadesc) {
607 -0x13579 => panic!("Shouldn't stop again"),
608 0 => (),
609 x => panic!("Unknown return: {}", x),
610 }
611 }
612
Fabio Utzigebeecef2017-07-06 10:36:42 -0300613 (fl, count - c::get_flash_counter())
David Brownde7729e2017-01-09 10:41:35 -0700614}
615
David Brown7ddec0b2017-07-06 10:47:35 -0600616fn try_revert(flash: &SimFlash, areadesc: &AreaDesc, count: usize) -> SimFlash {
David Brownde7729e2017-01-09 10:41:35 -0700617 let mut fl = flash.clone();
618 c::set_flash_counter(0);
619
David Brown163ab232017-01-23 15:48:35 -0700620 // fl.write_file("image0.bin").unwrap();
621 for i in 0 .. count {
622 info!("Running boot pass {}", i + 1);
David Brownc638f792017-01-10 12:34:33 -0700623 assert_eq!(c::boot_go(&mut fl, &areadesc), 0);
624 }
David Brownde7729e2017-01-09 10:41:35 -0700625 fl
626}
627
David Brown7ddec0b2017-07-06 10:47:35 -0600628fn try_revert_with_fail_at(flash: &SimFlash, areadesc: &AreaDesc, images: &Images,
Fabio Utzigebeecef2017-07-06 10:36:42 -0300629 stop: i32) -> bool {
David Brownde7729e2017-01-09 10:41:35 -0700630 let mut fl = flash.clone();
Fabio Utzigebeecef2017-07-06 10:36:42 -0300631 let mut x: i32;
632 let mut fails = 0;
David Brownde7729e2017-01-09 10:41:35 -0700633
Fabio Utzigebeecef2017-07-06 10:36:42 -0300634 c::set_flash_counter(stop);
635 x = c::boot_go(&mut fl, &areadesc);
636 if x != -0x13579 {
637 warn!("Should have stopped at interruption point");
638 fails += 1;
639 }
640
641 if !verify_trailer(&fl, images.slot0.trailer_off, None, None, UNSET) {
642 warn!("copy_done should be unset");
643 fails += 1;
644 }
645
646 c::set_flash_counter(0);
647 x = c::boot_go(&mut fl, &areadesc);
648 if x != 0 {
649 warn!("Should have finished upgrade");
650 fails += 1;
651 }
652
653 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
654 warn!("Image in slot 0 before revert is invalid at stop={}", stop);
655 fails += 1;
656 }
657 if !verify_image(&fl, images.slot1.base_off, &images.primary) {
658 warn!("Image in slot 1 before revert is invalid at stop={}", stop);
659 fails += 1;
660 }
661 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, UNSET,
662 COPY_DONE) {
663 warn!("Mismatched trailer for Slot 0 before revert");
664 fails += 1;
665 }
666 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
667 UNSET) {
668 warn!("Mismatched trailer for Slot 1 before revert");
669 fails += 1;
670 }
671
672 // Do Revert
673 c::set_flash_counter(0);
674 x = c::boot_go(&mut fl, &areadesc);
675 if x != 0 {
676 warn!("Should have finished a revert");
677 fails += 1;
678 }
679
680 if !verify_image(&fl, images.slot0.base_off, &images.primary) {
681 warn!("Image in slot 0 after revert is invalid at stop={}", stop);
682 fails += 1;
683 }
684 if !verify_image(&fl, images.slot1.base_off, &images.upgrade) {
685 warn!("Image in slot 1 after revert is invalid at stop={}", stop);
686 fails += 1;
687 }
688 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
689 COPY_DONE) {
690 warn!("Mismatched trailer for Slot 1 after revert");
691 fails += 1;
692 }
693 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
694 UNSET) {
695 warn!("Mismatched trailer for Slot 1 after revert");
696 fails += 1;
697 }
698
699 fails > 0
David Brownde7729e2017-01-09 10:41:35 -0700700}
701
David Brown7ddec0b2017-07-06 10:47:35 -0600702fn try_random_fails(flash: &SimFlash, areadesc: &AreaDesc, images: &Images,
703 total_ops: i32, count: usize) -> (SimFlash, Vec<i32>) {
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300704 let mut fl = flash.clone();
705
Fabio Utzigebeecef2017-07-06 10:36:42 -0300706 mark_permanent_upgrade(&mut fl, &images.slot1);
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300707
708 let mut rng = rand::thread_rng();
Fabio Utzig57652312017-04-25 19:54:26 -0300709 let mut resets = vec![0i32; count];
710 let mut remaining_ops = total_ops;
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300711 for i in 0 .. count {
Fabio Utzig57652312017-04-25 19:54:26 -0300712 let ops = Range::new(1, remaining_ops / 2);
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300713 let reset_counter = ops.ind_sample(&mut rng);
714 c::set_flash_counter(reset_counter);
715 match c::boot_go(&mut fl, &areadesc) {
716 0 | -0x13579 => (),
717 x => panic!("Unknown return: {}", x),
718 }
Fabio Utzig57652312017-04-25 19:54:26 -0300719 remaining_ops -= reset_counter;
720 resets[i] = reset_counter;
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300721 }
722
723 c::set_flash_counter(0);
724 match c::boot_go(&mut fl, &areadesc) {
725 -0x13579 => panic!("Should not be have been interrupted!"),
726 0 => (),
727 x => panic!("Unknown return: {}", x),
728 }
729
Fabio Utzig57652312017-04-25 19:54:26 -0300730 (fl, resets)
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300731}
732
David Brownde7729e2017-01-09 10:41:35 -0700733/// Show the flash layout.
734#[allow(dead_code)]
735fn show_flash(flash: &Flash) {
736 println!("---- Flash configuration ----");
737 for sector in flash.sector_iter() {
Fabio Utzigebeecef2017-07-06 10:36:42 -0300738 println!(" {:3}: 0x{:08x}, 0x{:08x}",
David Brownde7729e2017-01-09 10:41:35 -0700739 sector.num, sector.base, sector.size);
740 }
741 println!("");
742}
743
744/// Install a "program" into the given image. This fakes the image header, or at least all of the
745/// fields used by the given code. Returns a copy of the image that was written.
Fabio Utzig645e5142017-07-17 15:36:13 -0300746fn install_image(flash: &mut Flash, offset: usize, len: usize,
747 bad_sig: bool) -> Vec<u8> {
David Brownde7729e2017-01-09 10:41:35 -0700748 let offset0 = offset;
749
David Brown704ac6f2017-07-12 10:14:47 -0600750 let mut tlv = make_tlv();
David Brown187dd882017-07-11 11:15:23 -0600751
David Brownde7729e2017-01-09 10:41:35 -0700752 // Generate a boot header. Note that the size doesn't include the header.
753 let header = ImageHeader {
754 magic: 0x96f3b83c,
David Brown187dd882017-07-11 11:15:23 -0600755 tlv_size: tlv.get_size(),
David Brownde7729e2017-01-09 10:41:35 -0700756 _pad1: 0,
757 hdr_size: 32,
758 key_id: 0,
759 _pad2: 0,
760 img_size: len as u32,
David Brown187dd882017-07-11 11:15:23 -0600761 flags: tlv.get_flags(),
David Brownde7729e2017-01-09 10:41:35 -0700762 ver: ImageVersion {
David Browne380fa62017-01-23 15:49:09 -0700763 major: (offset / (128 * 1024)) as u8,
David Brownde7729e2017-01-09 10:41:35 -0700764 minor: 0,
765 revision: 1,
David Browne380fa62017-01-23 15:49:09 -0700766 build_num: offset as u32,
David Brownde7729e2017-01-09 10:41:35 -0700767 },
768 _pad3: 0,
769 };
770
771 let b_header = header.as_raw();
David Brown187dd882017-07-11 11:15:23 -0600772 tlv.add_bytes(&b_header);
David Brownde7729e2017-01-09 10:41:35 -0700773 /*
774 let b_header = unsafe { slice::from_raw_parts(&header as *const _ as *const u8,
775 mem::size_of::<ImageHeader>()) };
776 */
777 assert_eq!(b_header.len(), 32);
778 flash.write(offset, &b_header).unwrap();
779 let offset = offset + b_header.len();
780
781 // The core of the image itself is just pseudorandom data.
782 let mut buf = vec![0; len];
783 splat(&mut buf, offset);
David Brown187dd882017-07-11 11:15:23 -0600784 tlv.add_bytes(&buf);
785
786 // Get and append the TLV itself.
Fabio Utzig645e5142017-07-17 15:36:13 -0300787 if bad_sig {
788 let good_sig = &mut tlv.make_tlv();
789 buf.append(&mut vec![0; good_sig.len()]);
790 } else {
791 buf.append(&mut tlv.make_tlv());
792 }
David Brown187dd882017-07-11 11:15:23 -0600793
794 // Pad the block to a flash alignment (8 bytes).
795 while buf.len() % 8 != 0 {
796 buf.push(0xFF);
797 }
798
David Brownde7729e2017-01-09 10:41:35 -0700799 flash.write(offset, &buf).unwrap();
800 let offset = offset + buf.len();
801
802 // Copy out the image so that we can verify that the image was installed correctly later.
803 let mut copy = vec![0u8; offset - offset0];
804 flash.read(offset0, &mut copy).unwrap();
805
806 copy
807}
808
David Brown704ac6f2017-07-12 10:14:47 -0600809// The TLV in use depends on what kind of signature we are verifying.
810#[cfg(feature = "sig-rsa")]
811fn make_tlv() -> TlvGen {
812 TlvGen::new_rsa_pss()
813}
814
815#[cfg(not(feature = "sig-rsa"))]
816fn make_tlv() -> TlvGen {
817 TlvGen::new_hash_only()
818}
819
David Brownde7729e2017-01-09 10:41:35 -0700820/// Verify that given image is present in the flash at the given offset.
821fn verify_image(flash: &Flash, offset: usize, buf: &[u8]) -> bool {
822 let mut copy = vec![0u8; buf.len()];
823 flash.read(offset, &mut copy).unwrap();
824
825 if buf != &copy[..] {
826 for i in 0 .. buf.len() {
827 if buf[i] != copy[i] {
David Brown4440af82017-01-09 12:15:05 -0700828 info!("First failure at {:#x}", offset + i);
David Brownde7729e2017-01-09 10:41:35 -0700829 break;
830 }
831 }
832 false
833 } else {
834 true
835 }
836}
837
Fabio Utzigebeecef2017-07-06 10:36:42 -0300838fn verify_trailer(flash: &Flash, offset: usize,
839 magic: Option<&[u8]>, image_ok: Option<u8>,
840 copy_done: Option<u8>) -> bool {
841 let mut copy = vec![0u8; c::boot_magic_sz() + c::boot_max_align() * 2];
842 let mut failed = false;
843
844 flash.read(offset, &mut copy).unwrap();
845
846 failed |= match magic {
847 Some(v) => {
848 if &copy[16..] != v {
849 warn!("\"magic\" mismatch at {:#x}", offset);
850 true
851 } else {
852 false
853 }
854 },
855 None => false,
856 };
857
858 failed |= match image_ok {
859 Some(v) => {
860 if copy[8] != v {
861 warn!("\"image_ok\" mismatch at {:#x}", offset);
862 true
863 } else {
864 false
865 }
866 },
867 None => false,
868 };
869
870 failed |= match copy_done {
871 Some(v) => {
872 if copy[0] != v {
873 warn!("\"copy_done\" mismatch at {:#x}", offset);
874 true
875 } else {
876 false
877 }
878 },
879 None => false,
880 };
881
882 !failed
883}
884
David Brownde7729e2017-01-09 10:41:35 -0700885/// The image header
886#[repr(C)]
887pub struct ImageHeader {
888 magic: u32,
889 tlv_size: u16,
890 key_id: u8,
891 _pad1: u8,
892 hdr_size: u16,
893 _pad2: u16,
894 img_size: u32,
895 flags: u32,
896 ver: ImageVersion,
897 _pad3: u32,
898}
899
900impl AsRaw for ImageHeader {}
901
902#[repr(C)]
903pub struct ImageVersion {
904 major: u8,
905 minor: u8,
906 revision: u16,
907 build_num: u32,
908}
909
Fabio Utzigebeecef2017-07-06 10:36:42 -0300910struct SlotInfo {
911 base_off: usize,
912 trailer_off: usize,
913}
914
Fabio Utzig645e5142017-07-17 15:36:13 -0300915struct Images<'a> {
916 slot0: &'a SlotInfo,
917 slot1: &'a SlotInfo,
Fabio Utzigebeecef2017-07-06 10:36:42 -0300918 primary: Vec<u8>,
919 upgrade: Vec<u8>,
920}
921
922const MAGIC_VALID: Option<&[u8]> = Some(&[0x77, 0xc2, 0x95, 0xf3,
923 0x60, 0xd2, 0xef, 0x7f,
924 0x35, 0x52, 0x50, 0x0f,
925 0x2c, 0xb6, 0x79, 0x80]);
926const MAGIC_UNSET: Option<&[u8]> = Some(&[0xff; 16]);
927
928const COPY_DONE: Option<u8> = Some(1);
929const IMAGE_OK: Option<u8> = Some(1);
930const UNSET: Option<u8> = Some(0xff);
931
David Brownde7729e2017-01-09 10:41:35 -0700932/// Write out the magic so that the loader tries doing an upgrade.
Fabio Utzigebeecef2017-07-06 10:36:42 -0300933fn mark_upgrade(flash: &mut Flash, slot: &SlotInfo) {
934 let offset = slot.trailer_off + c::boot_max_align() * 2;
935 flash.write(offset, MAGIC_VALID.unwrap()).unwrap();
936}
937
938/// Writes the image_ok flag which, guess what, tells the bootloader
939/// the this image is ok (not a test, and no revert is to be performed).
940fn mark_permanent_upgrade(flash: &mut Flash, slot: &SlotInfo) {
941 let ok = [1u8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
942 let align = c::get_sim_flash_align() as usize;
943 let off = slot.trailer_off + c::boot_max_align();
944 flash.write(off, &ok[..align]).unwrap();
David Brownde7729e2017-01-09 10:41:35 -0700945}
946
947// Drop some pseudo-random gibberish onto the data.
948fn splat(data: &mut [u8], seed: usize) {
949 let seed_block = [0x135782ea, 0x92184728, data.len() as u32, seed as u32];
950 let mut rng: XorShiftRng = SeedableRng::from_seed(seed_block);
951 rng.fill_bytes(data);
952}
953
954/// Return a read-only view into the raw bytes of this object
955trait AsRaw : Sized {
956 fn as_raw<'a>(&'a self) -> &'a [u8] {
957 unsafe { slice::from_raw_parts(self as *const _ as *const u8,
958 mem::size_of::<Self>()) }
959 }
960}
961
962fn show_sizes() {
963 // This isn't panic safe.
964 let old_align = c::get_sim_flash_align();
965 for min in &[1, 2, 4, 8] {
966 c::set_sim_flash_align(*min);
967 let msize = c::boot_trailer_sz();
968 println!("{:2}: {} (0x{:x})", min, msize, msize);
969 }
970 c::set_sim_flash_align(old_align);
971}