blob: 3c9f8835d14c006f3af2cf46adcdddccb34d5846 [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 Brownde7729e2017-01-09 10:41:35 -07004extern crate docopt;
5extern crate libc;
David Brown7e701d82017-07-11 13:24:25 -06006extern crate pem;
David Brownde7729e2017-01-09 10:41:35 -07007extern crate rand;
David Brown046a0a62017-07-12 16:08:22 -06008#[macro_use] extern crate serde_derive;
9extern crate serde;
David Brown2cbc4702017-07-06 14:18:58 -060010extern crate simflash;
David Brown7e701d82017-07-11 13:24:25 -060011extern crate untrusted;
David Brown63902772017-07-12 09:47:49 -060012extern crate mcuboot_sys;
David Brownde7729e2017-01-09 10:41:35 -070013
14use docopt::Docopt;
David Brown4cb26232017-04-11 08:15:18 -060015use rand::{Rng, SeedableRng, XorShiftRng};
Fabio Utzigbb5635e2017-04-10 09:07:02 -030016use rand::distributions::{IndependentSample, Range};
David Browna3b93cf2017-03-29 12:41:26 -060017use std::fmt;
David Brownde7729e2017-01-09 10:41:35 -070018use std::mem;
David Brown361be7a2017-03-29 12:28:47 -060019use std::process;
David Brownde7729e2017-01-09 10:41:35 -070020use std::slice;
21
David Brown902d6172017-05-05 09:37:41 -060022mod caps;
David Brown187dd882017-07-11 11:15:23 -060023mod tlv;
David Brownde7729e2017-01-09 10:41:35 -070024
David Brown2cbc4702017-07-06 14:18:58 -060025use simflash::{Flash, SimFlash};
David Brownf52272c2017-07-12 09:56:16 -060026use mcuboot_sys::{c, AreaDesc, FlashId};
David Brown902d6172017-05-05 09:37:41 -060027use caps::Caps;
David Brown187dd882017-07-11 11:15:23 -060028use tlv::TlvGen;
David Brownde7729e2017-01-09 10:41:35 -070029
30const USAGE: &'static str = "
31Mcuboot simulator
32
33Usage:
34 bootsim sizes
35 bootsim run --device TYPE [--align SIZE]
David Browna3b93cf2017-03-29 12:41:26 -060036 bootsim runall
David Brownde7729e2017-01-09 10:41:35 -070037 bootsim (--help | --version)
38
39Options:
40 -h, --help Show this message
41 --version Version
42 --device TYPE MCU to simulate
43 Valid values: stm32f4, k64f
44 --align SIZE Flash write alignment
45";
46
David Brown046a0a62017-07-12 16:08:22 -060047#[derive(Debug, Deserialize)]
David Brownde7729e2017-01-09 10:41:35 -070048struct Args {
49 flag_help: bool,
50 flag_version: bool,
51 flag_device: Option<DeviceName>,
52 flag_align: Option<AlignArg>,
53 cmd_sizes: bool,
54 cmd_run: bool,
David Browna3b93cf2017-03-29 12:41:26 -060055 cmd_runall: bool,
David Brownde7729e2017-01-09 10:41:35 -070056}
57
David Brown046a0a62017-07-12 16:08:22 -060058#[derive(Copy, Clone, Debug, Deserialize)]
David Brown07fb8fa2017-03-20 12:40:57 -060059enum DeviceName { Stm32f4, K64f, K64fBig, Nrf52840 }
David Brownde7729e2017-01-09 10:41:35 -070060
David Browna3b93cf2017-03-29 12:41:26 -060061static ALL_DEVICES: &'static [DeviceName] = &[
62 DeviceName::Stm32f4,
63 DeviceName::K64f,
64 DeviceName::K64fBig,
65 DeviceName::Nrf52840,
66];
67
68impl fmt::Display for DeviceName {
69 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
70 let name = match *self {
71 DeviceName::Stm32f4 => "stm32f4",
72 DeviceName::K64f => "k64f",
73 DeviceName::K64fBig => "k64fbig",
74 DeviceName::Nrf52840 => "nrf52840",
75 };
76 f.write_str(name)
77 }
78}
79
David Brownde7729e2017-01-09 10:41:35 -070080#[derive(Debug)]
81struct AlignArg(u8);
82
David Brown046a0a62017-07-12 16:08:22 -060083struct AlignArgVisitor;
84
85impl<'de> serde::de::Visitor<'de> for AlignArgVisitor {
86 type Value = AlignArg;
87
88 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
89 formatter.write_str("1, 2, 4 or 8")
90 }
91
92 fn visit_u8<E>(self, n: u8) -> Result<Self::Value, E>
93 where E: serde::de::Error
94 {
95 Ok(match n {
96 1 | 2 | 4 | 8 => AlignArg(n),
97 n => {
98 let err = format!("Could not deserialize '{}' as alignment", n);
99 return Err(E::custom(err));
100 }
101 })
102 }
103}
104
105impl<'de> serde::de::Deserialize<'de> for AlignArg {
106 fn deserialize<D>(d: D) -> Result<AlignArg, D::Error>
107 where D: serde::de::Deserializer<'de>
108 {
109 d.deserialize_u8(AlignArgVisitor)
David Brownde7729e2017-01-09 10:41:35 -0700110 }
111}
112
113fn main() {
David Brown4440af82017-01-09 12:15:05 -0700114 env_logger::init().unwrap();
115
David Brownde7729e2017-01-09 10:41:35 -0700116 let args: Args = Docopt::new(USAGE)
David Brown046a0a62017-07-12 16:08:22 -0600117 .and_then(|d| d.deserialize())
David Brownde7729e2017-01-09 10:41:35 -0700118 .unwrap_or_else(|e| e.exit());
119 // println!("args: {:#?}", args);
120
121 if args.cmd_sizes {
122 show_sizes();
123 return;
124 }
125
David Brown361be7a2017-03-29 12:28:47 -0600126 let mut status = RunStatus::new();
David Browna3b93cf2017-03-29 12:41:26 -0600127 if args.cmd_run {
David Brown361be7a2017-03-29 12:28:47 -0600128
David Browna3b93cf2017-03-29 12:41:26 -0600129 let align = args.flag_align.map(|x| x.0).unwrap_or(1);
David Brown562a7a02017-01-23 11:19:03 -0700130
Fabio Utzigebeecef2017-07-06 10:36:42 -0300131
David Browna3b93cf2017-03-29 12:41:26 -0600132 let device = match args.flag_device {
133 None => panic!("Missing mandatory device argument"),
134 Some(dev) => dev,
135 };
David Brownde7729e2017-01-09 10:41:35 -0700136
David Browna3b93cf2017-03-29 12:41:26 -0600137 status.run_single(device, align);
138 }
139
140 if args.cmd_runall {
141 for &dev in ALL_DEVICES {
142 for &align in &[1, 2, 4, 8] {
143 status.run_single(dev, align);
144 }
145 }
146 }
David Brown5c6b6792017-03-20 12:51:28 -0600147
David Brown361be7a2017-03-29 12:28:47 -0600148 if status.failures > 0 {
David Brown187dd882017-07-11 11:15:23 -0600149 error!("{} Tests ran with {} failures", status.failures + status.passes, status.failures);
David Brown361be7a2017-03-29 12:28:47 -0600150 process::exit(1);
151 } else {
Fabio Utzig7b47ef72017-07-13 09:34:33 -0300152 error!("{} Tests ran successfully", status.passes);
David Brown361be7a2017-03-29 12:28:47 -0600153 process::exit(0);
154 }
155}
David Brown5c6b6792017-03-20 12:51:28 -0600156
David Brown361be7a2017-03-29 12:28:47 -0600157struct RunStatus {
158 failures: usize,
159 passes: usize,
160}
David Brownde7729e2017-01-09 10:41:35 -0700161
David Brown361be7a2017-03-29 12:28:47 -0600162impl RunStatus {
163 fn new() -> RunStatus {
164 RunStatus {
165 failures: 0,
166 passes: 0,
David Brownde7729e2017-01-09 10:41:35 -0700167 }
168 }
David Brownde7729e2017-01-09 10:41:35 -0700169
David Brown361be7a2017-03-29 12:28:47 -0600170 fn run_single(&mut self, device: DeviceName, align: u8) {
David Browna3b93cf2017-03-29 12:41:26 -0600171 warn!("Running on device {} with alignment {}", device, align);
172
David Brown361be7a2017-03-29 12:28:47 -0600173 let (mut flash, areadesc) = match device {
174 DeviceName::Stm32f4 => {
175 // STM style flash. Large sectors, with a large scratch area.
David Brown7ddec0b2017-07-06 10:47:35 -0600176 let flash = SimFlash::new(vec![16 * 1024, 16 * 1024, 16 * 1024, 16 * 1024,
177 64 * 1024,
178 128 * 1024, 128 * 1024, 128 * 1024],
179 align as usize);
David Brown361be7a2017-03-29 12:28:47 -0600180 let mut areadesc = AreaDesc::new(&flash);
181 areadesc.add_image(0x020000, 0x020000, FlashId::Image0);
182 areadesc.add_image(0x040000, 0x020000, FlashId::Image1);
183 areadesc.add_image(0x060000, 0x020000, FlashId::ImageScratch);
184 (flash, areadesc)
185 }
186 DeviceName::K64f => {
187 // NXP style flash. Small sectors, one small sector for scratch.
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(0x020000, 0x020000, FlashId::Image0);
192 areadesc.add_image(0x040000, 0x020000, FlashId::Image1);
193 areadesc.add_image(0x060000, 0x001000, FlashId::ImageScratch);
194 (flash, areadesc)
195 }
196 DeviceName::K64fBig => {
197 // Simulating an STM style flash on top of an NXP style flash. Underlying flash device
198 // uses small sectors, but we tell the bootloader they are large.
David Brown7ddec0b2017-07-06 10:47:35 -0600199 let flash = SimFlash::new(vec![4096; 128], align as usize);
David Brown361be7a2017-03-29 12:28:47 -0600200
201 let mut areadesc = AreaDesc::new(&flash);
202 areadesc.add_simple_image(0x020000, 0x020000, FlashId::Image0);
203 areadesc.add_simple_image(0x040000, 0x020000, FlashId::Image1);
204 areadesc.add_simple_image(0x060000, 0x020000, FlashId::ImageScratch);
205 (flash, areadesc)
206 }
207 DeviceName::Nrf52840 => {
208 // Simulating the flash on the nrf52840 with partitions set up so that the scratch size
209 // does not divide into the image size.
David Brown7ddec0b2017-07-06 10:47:35 -0600210 let flash = SimFlash::new(vec![4096; 128], align as usize);
David Brown361be7a2017-03-29 12:28:47 -0600211
212 let mut areadesc = AreaDesc::new(&flash);
213 areadesc.add_image(0x008000, 0x034000, FlashId::Image0);
214 areadesc.add_image(0x03c000, 0x034000, FlashId::Image1);
215 areadesc.add_image(0x070000, 0x00d000, FlashId::ImageScratch);
216 (flash, areadesc)
217 }
218 };
219
220 let (slot0_base, slot0_len) = areadesc.find(FlashId::Image0);
221 let (slot1_base, slot1_len) = areadesc.find(FlashId::Image1);
222 let (scratch_base, _) = areadesc.find(FlashId::ImageScratch);
223
224 // Code below assumes that the slots are consecutive.
225 assert_eq!(slot1_base, slot0_base + slot0_len);
226 assert_eq!(scratch_base, slot1_base + slot1_len);
227
Fabio Utzigebeecef2017-07-06 10:36:42 -0300228 let offset_from_end = c::boot_magic_sz() + c::boot_max_align() * 2;
229
David Brown361be7a2017-03-29 12:28:47 -0600230 // println!("Areas: {:#?}", areadesc.get_c());
231
232 // Install the boot trailer signature, so that the code will start an upgrade.
233 // TODO: This must be a multiple of flash alignment, add support for an image that is smaller,
234 // and just gets padded.
David Brown361be7a2017-03-29 12:28:47 -0600235
Fabio Utzigebeecef2017-07-06 10:36:42 -0300236 // Create original and upgrade images
237 let slot0 = SlotInfo {
238 base_off: slot0_base as usize,
239 trailer_off: slot1_base - offset_from_end,
240 };
241
242 let slot1 = SlotInfo {
243 base_off: slot1_base as usize,
244 trailer_off: scratch_base - offset_from_end,
245 };
246
Fabio Utzig645e5142017-07-17 15:36:13 -0300247 // Set an alignment, and position the magic value.
248 c::set_sim_flash_align(align);
Fabio Utzigebeecef2017-07-06 10:36:42 -0300249
250 let mut failed = false;
David Brown361be7a2017-03-29 12:28:47 -0600251
Fabio Utzig645e5142017-07-17 15:36:13 -0300252 // Creates a badly signed image in slot1 to check that it is not
253 // upgraded to
254 let mut bad_flash = flash.clone();
255 let bad_slot1_image = Images {
256 slot0: &slot0,
257 slot1: &slot1,
258 primary: install_image(&mut bad_flash, slot0_base, 32784, false),
259 upgrade: install_image(&mut bad_flash, slot1_base, 41928, true),
260 };
261
262 failed |= run_signfail_upgrade(&bad_flash, &areadesc, &bad_slot1_image);
263
264 let images = Images {
265 slot0: &slot0,
266 slot1: &slot1,
267 primary: install_image(&mut flash, slot0_base, 32784, false),
268 upgrade: install_image(&mut flash, slot1_base, 41928, false),
269 };
David Brown361be7a2017-03-29 12:28:47 -0600270
Fabio Utzig7b47ef72017-07-13 09:34:33 -0300271 failed |= run_norevert_newimage(&flash, &areadesc, &images);
272
Fabio Utzigebeecef2017-07-06 10:36:42 -0300273 mark_upgrade(&mut flash, &images.slot1);
David Brown361be7a2017-03-29 12:28:47 -0600274
Fabio Utzigebeecef2017-07-06 10:36:42 -0300275 // upgrades without fails, counts number of flash operations
276 let total_count = match run_basic_upgrade(&flash, &areadesc, &images) {
277 Ok(v) => v,
278 Err(_) => {
279 self.failures += 1;
280 return;
281 },
David Brown902d6172017-05-05 09:37:41 -0600282 };
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300283
Fabio Utzigebeecef2017-07-06 10:36:42 -0300284 failed |= run_basic_revert(&flash, &areadesc, &images);
285 failed |= run_revert_with_fails(&flash, &areadesc, &images, total_count);
286 failed |= run_perm_with_fails(&flash, &areadesc, &images, total_count);
287 failed |= run_perm_with_random_fails(&flash, &areadesc, &images,
Fabio Utzig7b47ef72017-07-13 09:34:33 -0300288 total_count, 5);
Fabio Utzigebeecef2017-07-06 10:36:42 -0300289 failed |= run_norevert(&flash, &areadesc, &images);
David Brown361be7a2017-03-29 12:28:47 -0600290
Fabio Utzigebeecef2017-07-06 10:36:42 -0300291 //show_flash(&flash);
David Brown361be7a2017-03-29 12:28:47 -0600292
David Brown361be7a2017-03-29 12:28:47 -0600293 if failed {
294 self.failures += 1;
295 } else {
296 self.passes += 1;
297 }
David Brownc638f792017-01-10 12:34:33 -0700298 }
David Brownde7729e2017-01-09 10:41:35 -0700299}
300
Fabio Utzigebeecef2017-07-06 10:36:42 -0300301/// A simple upgrade without forced failures.
302///
303/// Returns the number of flash operations which can later be used to
304/// inject failures at chosen steps.
David Brown7ddec0b2017-07-06 10:47:35 -0600305fn run_basic_upgrade(flash: &SimFlash, areadesc: &AreaDesc, images: &Images)
Fabio Utzigebeecef2017-07-06 10:36:42 -0300306 -> Result<i32, ()> {
307 let (fl, total_count) = try_upgrade(&flash, &areadesc, &images, None);
308 info!("Total flash operation count={}", total_count);
309
310 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
311 warn!("Image mismatch after first boot");
312 Err(())
313 } else {
314 Ok(total_count)
315 }
316}
317
Fabio Utzig100bb742017-09-13 17:18:36 -0300318#[cfg(feature = "overwrite-only")]
319#[allow(unused_variables)]
320fn run_basic_revert(flash: &SimFlash, areadesc: &AreaDesc, images: &Images) -> bool {
321 false
322}
323
324#[cfg(not(feature = "overwrite-only"))]
David Brown7ddec0b2017-07-06 10:47:35 -0600325fn run_basic_revert(flash: &SimFlash, areadesc: &AreaDesc, images: &Images) -> bool {
Fabio Utzigebeecef2017-07-06 10:36:42 -0300326 let mut fails = 0;
327
Fabio Utzig7b47ef72017-07-13 09:34:33 -0300328 // FIXME: this test would also pass if no swap is ever performed???
Fabio Utzigebeecef2017-07-06 10:36:42 -0300329 if Caps::SwapUpgrade.present() {
330 for count in 2 .. 5 {
331 info!("Try revert: {}", count);
332 let fl = try_revert(&flash, &areadesc, count);
333 if !verify_image(&fl, images.slot0.base_off, &images.primary) {
Fabio Utzig7b47ef72017-07-13 09:34:33 -0300334 error!("Revert failure on count {}", count);
Fabio Utzigebeecef2017-07-06 10:36:42 -0300335 fails += 1;
336 }
337 }
338 }
339
340 fails > 0
341}
342
David Brown7ddec0b2017-07-06 10:47:35 -0600343fn run_perm_with_fails(flash: &SimFlash, areadesc: &AreaDesc, images: &Images,
Fabio Utzigebeecef2017-07-06 10:36:42 -0300344 total_flash_ops: i32) -> bool {
345 let mut fails = 0;
346
347 // Let's try an image halfway through.
348 for i in 1 .. total_flash_ops {
349 info!("Try interruption at {}", i);
350 let (fl, count) = try_upgrade(&flash, &areadesc, &images, Some(i));
351 info!("Second boot, count={}", count);
352 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
353 warn!("FAIL at step {} of {}", i, total_flash_ops);
354 fails += 1;
355 }
356
357 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
358 COPY_DONE) {
359 warn!("Mismatched trailer for Slot 0");
360 fails += 1;
361 }
362
363 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
364 UNSET) {
365 warn!("Mismatched trailer for Slot 1");
366 fails += 1;
367 }
368
369 if Caps::SwapUpgrade.present() {
370 if !verify_image(&fl, images.slot1.base_off, &images.primary) {
371 warn!("Slot 1 FAIL at step {} of {}", i, total_flash_ops);
372 fails += 1;
373 }
374 }
375 }
376
Fabio Utzig7b47ef72017-07-13 09:34:33 -0300377 if fails > 0 {
378 error!("{} out of {} failed {:.2}%", fails, total_flash_ops,
379 fails as f32 * 100.0 / total_flash_ops as f32);
380 }
Fabio Utzigebeecef2017-07-06 10:36:42 -0300381
382 fails > 0
383}
384
David Brown7ddec0b2017-07-06 10:47:35 -0600385fn run_perm_with_random_fails(flash: &SimFlash, areadesc: &AreaDesc,
Fabio Utzigebeecef2017-07-06 10:36:42 -0300386 images: &Images, total_flash_ops: i32,
387 total_fails: usize) -> bool {
388 let mut fails = 0;
389 let (fl, total_counts) = try_random_fails(&flash, &areadesc, &images,
390 total_flash_ops, total_fails);
391 info!("Random interruptions at reset points={:?}", total_counts);
392
393 let slot0_ok = verify_image(&fl, images.slot0.base_off, &images.upgrade);
394 let slot1_ok = if Caps::SwapUpgrade.present() {
395 verify_image(&fl, images.slot1.base_off, &images.primary)
396 } else {
397 true
398 };
399 if !slot0_ok || !slot1_ok {
400 error!("Image mismatch after random interrupts: slot0={} slot1={}",
401 if slot0_ok { "ok" } else { "fail" },
402 if slot1_ok { "ok" } else { "fail" });
403 fails += 1;
404 }
405 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
406 COPY_DONE) {
407 error!("Mismatched trailer for Slot 0");
408 fails += 1;
409 }
410 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
411 UNSET) {
412 error!("Mismatched trailer for Slot 1");
413 fails += 1;
414 }
415
Fabio Utzig7b47ef72017-07-13 09:34:33 -0300416 if fails > 0 {
417 error!("Error testing perm upgrade with {} fails", total_fails);
418 }
419
Fabio Utzigebeecef2017-07-06 10:36:42 -0300420 fails > 0
421}
422
Fabio Utzig100bb742017-09-13 17:18:36 -0300423#[cfg(feature = "overwrite-only")]
424#[allow(unused_variables)]
425fn run_revert_with_fails(flash: &SimFlash, areadesc: &AreaDesc, images: &Images,
426 total_count: i32) -> bool {
427 false
428}
429
430#[cfg(not(feature = "overwrite-only"))]
David Brown7ddec0b2017-07-06 10:47:35 -0600431fn run_revert_with_fails(flash: &SimFlash, areadesc: &AreaDesc, images: &Images,
Fabio Utzigebeecef2017-07-06 10:36:42 -0300432 total_count: i32) -> bool {
433 let mut fails = 0;
434
435 if Caps::SwapUpgrade.present() {
436 for i in 1 .. (total_count - 1) {
437 info!("Try interruption at {}", i);
438 if try_revert_with_fail_at(&flash, &areadesc, &images, i) {
Fabio Utzig7b47ef72017-07-13 09:34:33 -0300439 error!("Revert failed at interruption {}", i);
Fabio Utzigebeecef2017-07-06 10:36:42 -0300440 fails += 1;
441 }
442 }
443 }
444
445 fails > 0
446}
447
Fabio Utzig100bb742017-09-13 17:18:36 -0300448#[cfg(feature = "overwrite-only")]
449#[allow(unused_variables)]
450fn run_norevert(flash: &SimFlash, areadesc: &AreaDesc, images: &Images) -> bool {
451 false
452}
453
454#[cfg(not(feature = "overwrite-only"))]
David Brown7ddec0b2017-07-06 10:47:35 -0600455fn run_norevert(flash: &SimFlash, areadesc: &AreaDesc, images: &Images) -> bool {
Fabio Utzigebeecef2017-07-06 10:36:42 -0300456 let mut fl = flash.clone();
457 let mut fails = 0;
458
459 info!("Try norevert");
460 c::set_flash_counter(0);
461
462 // First do a normal upgrade...
463 if c::boot_go(&mut fl, &areadesc) != 0 {
464 warn!("Failed first boot");
465 fails += 1;
466 }
467
Fabio Utzig7b47ef72017-07-13 09:34:33 -0300468 //FIXME: copy_done is written by boot_go, is it ok if no copy
469 // was ever done?
470
Fabio Utzigebeecef2017-07-06 10:36:42 -0300471 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
472 warn!("Slot 0 image verification FAIL");
473 fails += 1;
474 }
475 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, UNSET,
476 COPY_DONE) {
477 warn!("Mismatched trailer for Slot 0");
478 fails += 1;
479 }
480 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
481 UNSET) {
482 warn!("Mismatched trailer for Slot 1");
483 fails += 1;
484 }
485
486 // Marks image in slot0 as permanent, no revert should happen...
487 mark_permanent_upgrade(&mut fl, &images.slot0);
488
Fabio Utzig7b47ef72017-07-13 09:34:33 -0300489 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
490 COPY_DONE) {
491 warn!("Mismatched trailer for Slot 0");
492 fails += 1;
493 }
494
Fabio Utzigebeecef2017-07-06 10:36:42 -0300495 if c::boot_go(&mut fl, &areadesc) != 0 {
496 warn!("Failed second boot");
497 fails += 1;
498 }
499
500 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
501 COPY_DONE) {
502 warn!("Mismatched trailer for Slot 0");
503 fails += 1;
504 }
505 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
506 warn!("Failed image verification");
507 fails += 1;
508 }
509
Fabio Utzig7b47ef72017-07-13 09:34:33 -0300510 if fails > 0 {
511 error!("Error running upgrade without revert");
512 }
513
514 fails > 0
515}
516
517// Tests a new image written to slot0 that already has magic and image_ok set
518// while there is no image on slot1, so no revert should ever happen...
519fn run_norevert_newimage(flash: &SimFlash, areadesc: &AreaDesc,
520 images: &Images) -> bool {
521 let mut fl = flash.clone();
522 let mut fails = 0;
523
524 info!("Try non-revert on imgtool generated image");
525 c::set_flash_counter(0);
526
527 mark_upgrade(&mut fl, &images.slot0);
528
529 // This simulates writing an image created by imgtool to Slot 0
530 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, UNSET, UNSET) {
531 warn!("Mismatched trailer for Slot 0");
532 fails += 1;
533 }
534
535 // Run the bootloader...
536 if c::boot_go(&mut fl, &areadesc) != 0 {
537 warn!("Failed first boot");
538 fails += 1;
539 }
540
541 // State should not have changed
542 if !verify_image(&fl, images.slot0.base_off, &images.primary) {
543 warn!("Failed image verification");
544 fails += 1;
545 }
546 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, UNSET,
547 UNSET) {
548 warn!("Mismatched trailer for Slot 0");
549 fails += 1;
550 }
551 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
552 UNSET) {
553 warn!("Mismatched trailer for Slot 1");
554 fails += 1;
555 }
556
557 if fails > 0 {
558 error!("Expected a non revert with new image");
559 }
560
Fabio Utzigebeecef2017-07-06 10:36:42 -0300561 fails > 0
562}
563
Fabio Utzig645e5142017-07-17 15:36:13 -0300564// Tests a new image written to slot0 that already has magic and image_ok set
565// while there is no image on slot1, so no revert should ever happen...
566fn run_signfail_upgrade(flash: &SimFlash, areadesc: &AreaDesc,
567 images: &Images) -> bool {
568 let mut fl = flash.clone();
569 let mut fails = 0;
570
571 info!("Try upgrade image with bad signature");
572 c::set_flash_counter(0);
573
574 mark_upgrade(&mut fl, &images.slot0);
575 mark_permanent_upgrade(&mut fl, &images.slot0);
576 mark_upgrade(&mut fl, &images.slot1);
577
578 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
579 UNSET) {
580 warn!("Mismatched trailer for Slot 0");
581 fails += 1;
582 }
583
584 // Run the bootloader...
585 if c::boot_go(&mut fl, &areadesc) != 0 {
586 warn!("Failed first boot");
587 fails += 1;
588 }
589
590 // State should not have changed
591 if !verify_image(&fl, images.slot0.base_off, &images.primary) {
592 warn!("Failed image verification");
593 fails += 1;
594 }
595 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
596 UNSET) {
597 warn!("Mismatched trailer for Slot 0");
598 fails += 1;
599 }
600
601 if fails > 0 {
602 error!("Expected an upgrade failure when image has bad signature");
603 }
604
605 fails > 0
606}
607
Fabio Utzigebeecef2017-07-06 10:36:42 -0300608/// Test a boot, optionally stopping after 'n' flash options. Returns a count
609/// of the number of flash operations done total.
David Brown7ddec0b2017-07-06 10:47:35 -0600610fn try_upgrade(flash: &SimFlash, areadesc: &AreaDesc, images: &Images,
611 stop: Option<i32>) -> (SimFlash, i32) {
David Brownde7729e2017-01-09 10:41:35 -0700612 // Clone the flash to have a new copy.
613 let mut fl = flash.clone();
614
Fabio Utzigebeecef2017-07-06 10:36:42 -0300615 mark_permanent_upgrade(&mut fl, &images.slot1);
Fabio Utzig57652312017-04-25 19:54:26 -0300616
David Brownde7729e2017-01-09 10:41:35 -0700617 c::set_flash_counter(stop.unwrap_or(0));
Fabio Utzigebeecef2017-07-06 10:36:42 -0300618 let (first_interrupted, count) = match c::boot_go(&mut fl, &areadesc) {
David Brownde7729e2017-01-09 10:41:35 -0700619 -0x13579 => (true, stop.unwrap()),
620 0 => (false, -c::get_flash_counter()),
621 x => panic!("Unknown return: {}", x),
622 };
623 c::set_flash_counter(0);
624
625 if first_interrupted {
626 // fl.dump();
627 match c::boot_go(&mut fl, &areadesc) {
628 -0x13579 => panic!("Shouldn't stop again"),
629 0 => (),
630 x => panic!("Unknown return: {}", x),
631 }
632 }
633
Fabio Utzigebeecef2017-07-06 10:36:42 -0300634 (fl, count - c::get_flash_counter())
David Brownde7729e2017-01-09 10:41:35 -0700635}
636
Fabio Utzig100bb742017-09-13 17:18:36 -0300637#[cfg(not(feature = "overwrite-only"))]
David Brown7ddec0b2017-07-06 10:47:35 -0600638fn try_revert(flash: &SimFlash, areadesc: &AreaDesc, count: usize) -> SimFlash {
David Brownde7729e2017-01-09 10:41:35 -0700639 let mut fl = flash.clone();
640 c::set_flash_counter(0);
641
David Brown163ab232017-01-23 15:48:35 -0700642 // fl.write_file("image0.bin").unwrap();
643 for i in 0 .. count {
644 info!("Running boot pass {}", i + 1);
David Brownc638f792017-01-10 12:34:33 -0700645 assert_eq!(c::boot_go(&mut fl, &areadesc), 0);
646 }
David Brownde7729e2017-01-09 10:41:35 -0700647 fl
648}
649
Fabio Utzig100bb742017-09-13 17:18:36 -0300650#[cfg(not(feature = "overwrite-only"))]
David Brown7ddec0b2017-07-06 10:47:35 -0600651fn try_revert_with_fail_at(flash: &SimFlash, areadesc: &AreaDesc, images: &Images,
Fabio Utzigebeecef2017-07-06 10:36:42 -0300652 stop: i32) -> bool {
David Brownde7729e2017-01-09 10:41:35 -0700653 let mut fl = flash.clone();
Fabio Utzigebeecef2017-07-06 10:36:42 -0300654 let mut x: i32;
655 let mut fails = 0;
David Brownde7729e2017-01-09 10:41:35 -0700656
Fabio Utzigebeecef2017-07-06 10:36:42 -0300657 c::set_flash_counter(stop);
658 x = c::boot_go(&mut fl, &areadesc);
659 if x != -0x13579 {
660 warn!("Should have stopped at interruption point");
661 fails += 1;
662 }
663
664 if !verify_trailer(&fl, images.slot0.trailer_off, None, None, UNSET) {
665 warn!("copy_done should be unset");
666 fails += 1;
667 }
668
669 c::set_flash_counter(0);
670 x = c::boot_go(&mut fl, &areadesc);
671 if x != 0 {
672 warn!("Should have finished upgrade");
673 fails += 1;
674 }
675
676 if !verify_image(&fl, images.slot0.base_off, &images.upgrade) {
677 warn!("Image in slot 0 before revert is invalid at stop={}", stop);
678 fails += 1;
679 }
680 if !verify_image(&fl, images.slot1.base_off, &images.primary) {
681 warn!("Image in slot 1 before revert is invalid at stop={}", stop);
682 fails += 1;
683 }
684 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, UNSET,
685 COPY_DONE) {
686 warn!("Mismatched trailer for Slot 0 before revert");
687 fails += 1;
688 }
689 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
690 UNSET) {
691 warn!("Mismatched trailer for Slot 1 before revert");
692 fails += 1;
693 }
694
695 // Do Revert
696 c::set_flash_counter(0);
697 x = c::boot_go(&mut fl, &areadesc);
698 if x != 0 {
699 warn!("Should have finished a revert");
700 fails += 1;
701 }
702
703 if !verify_image(&fl, images.slot0.base_off, &images.primary) {
704 warn!("Image in slot 0 after revert is invalid at stop={}", stop);
705 fails += 1;
706 }
707 if !verify_image(&fl, images.slot1.base_off, &images.upgrade) {
708 warn!("Image in slot 1 after revert is invalid at stop={}", stop);
709 fails += 1;
710 }
711 if !verify_trailer(&fl, images.slot0.trailer_off, MAGIC_VALID, IMAGE_OK,
712 COPY_DONE) {
713 warn!("Mismatched trailer for Slot 1 after revert");
714 fails += 1;
715 }
716 if !verify_trailer(&fl, images.slot1.trailer_off, MAGIC_UNSET, UNSET,
717 UNSET) {
718 warn!("Mismatched trailer for Slot 1 after revert");
719 fails += 1;
720 }
721
722 fails > 0
David Brownde7729e2017-01-09 10:41:35 -0700723}
724
David Brown7ddec0b2017-07-06 10:47:35 -0600725fn try_random_fails(flash: &SimFlash, areadesc: &AreaDesc, images: &Images,
726 total_ops: i32, count: usize) -> (SimFlash, Vec<i32>) {
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300727 let mut fl = flash.clone();
728
Fabio Utzigebeecef2017-07-06 10:36:42 -0300729 mark_permanent_upgrade(&mut fl, &images.slot1);
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300730
731 let mut rng = rand::thread_rng();
Fabio Utzig57652312017-04-25 19:54:26 -0300732 let mut resets = vec![0i32; count];
733 let mut remaining_ops = total_ops;
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300734 for i in 0 .. count {
Fabio Utzig57652312017-04-25 19:54:26 -0300735 let ops = Range::new(1, remaining_ops / 2);
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300736 let reset_counter = ops.ind_sample(&mut rng);
737 c::set_flash_counter(reset_counter);
738 match c::boot_go(&mut fl, &areadesc) {
739 0 | -0x13579 => (),
740 x => panic!("Unknown return: {}", x),
741 }
Fabio Utzig57652312017-04-25 19:54:26 -0300742 remaining_ops -= reset_counter;
743 resets[i] = reset_counter;
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300744 }
745
746 c::set_flash_counter(0);
747 match c::boot_go(&mut fl, &areadesc) {
748 -0x13579 => panic!("Should not be have been interrupted!"),
749 0 => (),
750 x => panic!("Unknown return: {}", x),
751 }
752
Fabio Utzig57652312017-04-25 19:54:26 -0300753 (fl, resets)
Fabio Utzigbb5635e2017-04-10 09:07:02 -0300754}
755
David Brownde7729e2017-01-09 10:41:35 -0700756/// Show the flash layout.
757#[allow(dead_code)]
758fn show_flash(flash: &Flash) {
759 println!("---- Flash configuration ----");
760 for sector in flash.sector_iter() {
Fabio Utzigebeecef2017-07-06 10:36:42 -0300761 println!(" {:3}: 0x{:08x}, 0x{:08x}",
David Brownde7729e2017-01-09 10:41:35 -0700762 sector.num, sector.base, sector.size);
763 }
764 println!("");
765}
766
767/// Install a "program" into the given image. This fakes the image header, or at least all of the
768/// fields used by the given code. Returns a copy of the image that was written.
Fabio Utzig645e5142017-07-17 15:36:13 -0300769fn install_image(flash: &mut Flash, offset: usize, len: usize,
770 bad_sig: bool) -> Vec<u8> {
David Brownde7729e2017-01-09 10:41:35 -0700771 let offset0 = offset;
772
David Brown704ac6f2017-07-12 10:14:47 -0600773 let mut tlv = make_tlv();
David Brown187dd882017-07-11 11:15:23 -0600774
David Brownde7729e2017-01-09 10:41:35 -0700775 // Generate a boot header. Note that the size doesn't include the header.
776 let header = ImageHeader {
David Brown72e7a512017-09-01 11:08:23 -0600777 magic: 0x96f3b83d,
David Brown187dd882017-07-11 11:15:23 -0600778 tlv_size: tlv.get_size(),
David Brownde7729e2017-01-09 10:41:35 -0700779 _pad1: 0,
780 hdr_size: 32,
781 key_id: 0,
782 _pad2: 0,
783 img_size: len as u32,
David Brown187dd882017-07-11 11:15:23 -0600784 flags: tlv.get_flags(),
David Brownde7729e2017-01-09 10:41:35 -0700785 ver: ImageVersion {
David Browne380fa62017-01-23 15:49:09 -0700786 major: (offset / (128 * 1024)) as u8,
David Brownde7729e2017-01-09 10:41:35 -0700787 minor: 0,
788 revision: 1,
David Browne380fa62017-01-23 15:49:09 -0700789 build_num: offset as u32,
David Brownde7729e2017-01-09 10:41:35 -0700790 },
791 _pad3: 0,
792 };
793
794 let b_header = header.as_raw();
David Brown187dd882017-07-11 11:15:23 -0600795 tlv.add_bytes(&b_header);
David Brownde7729e2017-01-09 10:41:35 -0700796 /*
797 let b_header = unsafe { slice::from_raw_parts(&header as *const _ as *const u8,
798 mem::size_of::<ImageHeader>()) };
799 */
800 assert_eq!(b_header.len(), 32);
801 flash.write(offset, &b_header).unwrap();
802 let offset = offset + b_header.len();
803
804 // The core of the image itself is just pseudorandom data.
805 let mut buf = vec![0; len];
806 splat(&mut buf, offset);
David Brown187dd882017-07-11 11:15:23 -0600807 tlv.add_bytes(&buf);
808
809 // Get and append the TLV itself.
Fabio Utzig645e5142017-07-17 15:36:13 -0300810 if bad_sig {
811 let good_sig = &mut tlv.make_tlv();
812 buf.append(&mut vec![0; good_sig.len()]);
813 } else {
814 buf.append(&mut tlv.make_tlv());
815 }
David Brown187dd882017-07-11 11:15:23 -0600816
817 // Pad the block to a flash alignment (8 bytes).
818 while buf.len() % 8 != 0 {
819 buf.push(0xFF);
820 }
821
David Brownde7729e2017-01-09 10:41:35 -0700822 flash.write(offset, &buf).unwrap();
823 let offset = offset + buf.len();
824
825 // Copy out the image so that we can verify that the image was installed correctly later.
826 let mut copy = vec![0u8; offset - offset0];
827 flash.read(offset0, &mut copy).unwrap();
828
829 copy
830}
831
David Brown704ac6f2017-07-12 10:14:47 -0600832// The TLV in use depends on what kind of signature we are verifying.
833#[cfg(feature = "sig-rsa")]
834fn make_tlv() -> TlvGen {
835 TlvGen::new_rsa_pss()
836}
837
838#[cfg(not(feature = "sig-rsa"))]
839fn make_tlv() -> TlvGen {
840 TlvGen::new_hash_only()
841}
842
David Brownde7729e2017-01-09 10:41:35 -0700843/// Verify that given image is present in the flash at the given offset.
844fn verify_image(flash: &Flash, offset: usize, buf: &[u8]) -> bool {
845 let mut copy = vec![0u8; buf.len()];
846 flash.read(offset, &mut copy).unwrap();
847
848 if buf != &copy[..] {
849 for i in 0 .. buf.len() {
850 if buf[i] != copy[i] {
David Brown4440af82017-01-09 12:15:05 -0700851 info!("First failure at {:#x}", offset + i);
David Brownde7729e2017-01-09 10:41:35 -0700852 break;
853 }
854 }
855 false
856 } else {
857 true
858 }
859}
860
Fabio Utzig100bb742017-09-13 17:18:36 -0300861#[cfg(feature = "overwrite-only")]
862#[allow(unused_variables)]
863// overwrite-only doesn't employ trailer management
864fn verify_trailer(flash: &Flash, offset: usize,
865 magic: Option<&[u8]>, image_ok: Option<u8>,
866 copy_done: Option<u8>) -> bool {
867 true
868}
869
870#[cfg(not(feature = "overwrite-only"))]
Fabio Utzigebeecef2017-07-06 10:36:42 -0300871fn verify_trailer(flash: &Flash, offset: usize,
872 magic: Option<&[u8]>, image_ok: Option<u8>,
873 copy_done: Option<u8>) -> bool {
874 let mut copy = vec![0u8; c::boot_magic_sz() + c::boot_max_align() * 2];
875 let mut failed = false;
876
877 flash.read(offset, &mut copy).unwrap();
878
879 failed |= match magic {
880 Some(v) => {
881 if &copy[16..] != v {
882 warn!("\"magic\" mismatch at {:#x}", offset);
883 true
884 } else {
885 false
886 }
887 },
888 None => false,
889 };
890
891 failed |= match image_ok {
892 Some(v) => {
893 if copy[8] != v {
894 warn!("\"image_ok\" mismatch at {:#x}", offset);
895 true
896 } else {
897 false
898 }
899 },
900 None => false,
901 };
902
903 failed |= match copy_done {
904 Some(v) => {
905 if copy[0] != v {
906 warn!("\"copy_done\" mismatch at {:#x}", offset);
907 true
908 } else {
909 false
910 }
911 },
912 None => false,
913 };
914
915 !failed
916}
917
David Brownde7729e2017-01-09 10:41:35 -0700918/// The image header
919#[repr(C)]
920pub struct ImageHeader {
921 magic: u32,
922 tlv_size: u16,
923 key_id: u8,
924 _pad1: u8,
925 hdr_size: u16,
926 _pad2: u16,
927 img_size: u32,
928 flags: u32,
929 ver: ImageVersion,
930 _pad3: u32,
931}
932
933impl AsRaw for ImageHeader {}
934
935#[repr(C)]
936pub struct ImageVersion {
937 major: u8,
938 minor: u8,
939 revision: u16,
940 build_num: u32,
941}
942
Fabio Utzigebeecef2017-07-06 10:36:42 -0300943struct SlotInfo {
944 base_off: usize,
945 trailer_off: usize,
946}
947
Fabio Utzig645e5142017-07-17 15:36:13 -0300948struct Images<'a> {
949 slot0: &'a SlotInfo,
950 slot1: &'a SlotInfo,
Fabio Utzigebeecef2017-07-06 10:36:42 -0300951 primary: Vec<u8>,
952 upgrade: Vec<u8>,
953}
954
955const MAGIC_VALID: Option<&[u8]> = Some(&[0x77, 0xc2, 0x95, 0xf3,
956 0x60, 0xd2, 0xef, 0x7f,
957 0x35, 0x52, 0x50, 0x0f,
958 0x2c, 0xb6, 0x79, 0x80]);
959const MAGIC_UNSET: Option<&[u8]> = Some(&[0xff; 16]);
960
961const COPY_DONE: Option<u8> = Some(1);
962const IMAGE_OK: Option<u8> = Some(1);
963const UNSET: Option<u8> = Some(0xff);
964
David Brownde7729e2017-01-09 10:41:35 -0700965/// Write out the magic so that the loader tries doing an upgrade.
Fabio Utzigebeecef2017-07-06 10:36:42 -0300966fn mark_upgrade(flash: &mut Flash, slot: &SlotInfo) {
967 let offset = slot.trailer_off + c::boot_max_align() * 2;
968 flash.write(offset, MAGIC_VALID.unwrap()).unwrap();
969}
970
971/// Writes the image_ok flag which, guess what, tells the bootloader
972/// the this image is ok (not a test, and no revert is to be performed).
973fn mark_permanent_upgrade(flash: &mut Flash, slot: &SlotInfo) {
974 let ok = [1u8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
975 let align = c::get_sim_flash_align() as usize;
976 let off = slot.trailer_off + c::boot_max_align();
977 flash.write(off, &ok[..align]).unwrap();
David Brownde7729e2017-01-09 10:41:35 -0700978}
979
980// Drop some pseudo-random gibberish onto the data.
981fn splat(data: &mut [u8], seed: usize) {
982 let seed_block = [0x135782ea, 0x92184728, data.len() as u32, seed as u32];
983 let mut rng: XorShiftRng = SeedableRng::from_seed(seed_block);
984 rng.fill_bytes(data);
985}
986
987/// Return a read-only view into the raw bytes of this object
988trait AsRaw : Sized {
989 fn as_raw<'a>(&'a self) -> &'a [u8] {
990 unsafe { slice::from_raw_parts(self as *const _ as *const u8,
991 mem::size_of::<Self>()) }
992 }
993}
994
995fn show_sizes() {
996 // This isn't panic safe.
997 let old_align = c::get_sim_flash_align();
998 for min in &[1, 2, 4, 8] {
999 c::set_sim_flash_align(*min);
1000 let msize = c::boot_trailer_sz();
1001 println!("{:2}: {} (0x{:x})", min, msize, msize);
1002 }
1003 c::set_sim_flash_align(old_align);
1004}