blob: 47d20eba7f5312a2ccca86b8c794638dbaf3f6f6 [file] [log] [blame]
David Brown2639e072017-10-11 11:18:44 -06001#[macro_use] extern crate log;
2extern crate ring;
Fabio Utzig1e48b912018-09-18 09:04:18 -03003extern crate aes_ctr;
4extern crate base64;
David Brown2639e072017-10-11 11:18:44 -06005extern crate env_logger;
6extern crate docopt;
7extern crate libc;
8extern crate pem;
9extern crate rand;
10#[macro_use] extern crate serde_derive;
11extern crate serde;
12extern crate simflash;
13extern crate untrusted;
14extern crate mcuboot_sys;
15
16use docopt::Docopt;
17use rand::{Rng, SeedableRng, XorShiftRng};
18use rand::distributions::{IndependentSample, Range};
19use std::fmt;
20use std::mem;
21use std::process;
22use std::slice;
Fabio Utzig1e48b912018-09-18 09:04:18 -030023use aes_ctr::Aes128Ctr;
24use aes_ctr::stream_cipher::generic_array::GenericArray;
25use aes_ctr::stream_cipher::{NewFixStreamCipher, StreamCipherCore};
David Brown2639e072017-10-11 11:18:44 -060026
27mod caps;
28mod tlv;
David Brownca7b5d32017-11-03 08:37:38 -060029pub mod testlog;
David Brown2639e072017-10-11 11:18:44 -060030
31use simflash::{Flash, SimFlash};
32use mcuboot_sys::{c, AreaDesc, FlashId};
33use caps::Caps;
Fabio Utzig1e48b912018-09-18 09:04:18 -030034use tlv::{TlvGen, TlvFlags, AES_SEC_KEY};
David Brown2639e072017-10-11 11:18:44 -060035
36const USAGE: &'static str = "
37Mcuboot simulator
38
39Usage:
40 bootsim sizes
41 bootsim run --device TYPE [--align SIZE]
42 bootsim runall
43 bootsim (--help | --version)
44
45Options:
46 -h, --help Show this message
47 --version Version
48 --device TYPE MCU to simulate
49 Valid values: stm32f4, k64f
50 --align SIZE Flash write alignment
51";
52
53#[derive(Debug, Deserialize)]
54struct Args {
55 flag_help: bool,
56 flag_version: bool,
57 flag_device: Option<DeviceName>,
58 flag_align: Option<AlignArg>,
59 cmd_sizes: bool,
60 cmd_run: bool,
61 cmd_runall: bool,
62}
63
64#[derive(Copy, Clone, Debug, Deserialize)]
David Browndecbd042017-10-19 10:43:17 -060065pub enum DeviceName { Stm32f4, K64f, K64fBig, Nrf52840 }
David Brown2639e072017-10-11 11:18:44 -060066
David Browndd2b1182017-11-02 15:39:21 -060067pub static ALL_DEVICES: &'static [DeviceName] = &[
David Brown2639e072017-10-11 11:18:44 -060068 DeviceName::Stm32f4,
69 DeviceName::K64f,
70 DeviceName::K64fBig,
71 DeviceName::Nrf52840,
72];
73
74impl fmt::Display for DeviceName {
75 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
76 let name = match *self {
77 DeviceName::Stm32f4 => "stm32f4",
78 DeviceName::K64f => "k64f",
79 DeviceName::K64fBig => "k64fbig",
80 DeviceName::Nrf52840 => "nrf52840",
81 };
82 f.write_str(name)
83 }
84}
85
86#[derive(Debug)]
87struct AlignArg(u8);
88
89struct AlignArgVisitor;
90
91impl<'de> serde::de::Visitor<'de> for AlignArgVisitor {
92 type Value = AlignArg;
93
94 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
95 formatter.write_str("1, 2, 4 or 8")
96 }
97
98 fn visit_u8<E>(self, n: u8) -> Result<Self::Value, E>
99 where E: serde::de::Error
100 {
101 Ok(match n {
102 1 | 2 | 4 | 8 => AlignArg(n),
103 n => {
104 let err = format!("Could not deserialize '{}' as alignment", n);
105 return Err(E::custom(err));
106 }
107 })
108 }
109}
110
111impl<'de> serde::de::Deserialize<'de> for AlignArg {
112 fn deserialize<D>(d: D) -> Result<AlignArg, D::Error>
113 where D: serde::de::Deserializer<'de>
114 {
115 d.deserialize_u8(AlignArgVisitor)
116 }
117}
118
119pub fn main() {
120 let args: Args = Docopt::new(USAGE)
121 .and_then(|d| d.deserialize())
122 .unwrap_or_else(|e| e.exit());
123 // println!("args: {:#?}", args);
124
125 if args.cmd_sizes {
126 show_sizes();
127 return;
128 }
129
130 let mut status = RunStatus::new();
131 if args.cmd_run {
132
133 let align = args.flag_align.map(|x| x.0).unwrap_or(1);
134
135
136 let device = match args.flag_device {
137 None => panic!("Missing mandatory device argument"),
138 Some(dev) => dev,
139 };
140
Fabio Utzigea0290b2018-08-09 14:23:01 -0300141 status.run_single(device, align, 0xff);
David Brown2639e072017-10-11 11:18:44 -0600142 }
143
144 if args.cmd_runall {
145 for &dev in ALL_DEVICES {
146 for &align in &[1, 2, 4, 8] {
Fabio Utzigea0290b2018-08-09 14:23:01 -0300147 for &erased_val in &[0, 0xff] {
148 status.run_single(dev, align, erased_val);
149 }
David Brown2639e072017-10-11 11:18:44 -0600150 }
151 }
152 }
153
154 if status.failures > 0 {
155 error!("{} Tests ran with {} failures", status.failures + status.passes, status.failures);
156 process::exit(1);
157 } else {
158 error!("{} Tests ran successfully", status.passes);
159 process::exit(0);
160 }
161}
162
David Browndb9a3952017-11-06 13:16:15 -0700163/// A test run, intended to be run from "cargo test", so panics on failure.
164pub struct Run {
165 flash: SimFlash,
166 areadesc: AreaDesc,
167 slots: [SlotInfo; 2],
168 align: u8,
Fabio Utzigea0290b2018-08-09 14:23:01 -0300169 erased_val: u8,
David Browndb9a3952017-11-06 13:16:15 -0700170}
171
172impl Run {
Fabio Utzigea0290b2018-08-09 14:23:01 -0300173 pub fn new(device: DeviceName, align: u8, erased_val: u8) -> Run {
174 let (flash, areadesc) = make_device(device, align, erased_val);
David Browndb9a3952017-11-06 13:16:15 -0700175
176 let (slot0_base, slot0_len) = areadesc.find(FlashId::Image0);
177 let (slot1_base, slot1_len) = areadesc.find(FlashId::Image1);
178 let (scratch_base, _) = areadesc.find(FlashId::ImageScratch);
179
180 // The code assumes that the slots are consecutive.
181 assert_eq!(slot1_base, slot0_base + slot0_len);
182 assert_eq!(scratch_base, slot1_base + slot1_len);
183
Fabio Utzigb841f0a2017-11-24 08:11:05 -0200184 // NOTE: not accounting "swap_size" because it is not used by sim...
David Browndb9a3952017-11-06 13:16:15 -0700185 let offset_from_end = c::boot_magic_sz() + c::boot_max_align() * 2;
186
187 // Construct a primary image.
188 let slot0 = SlotInfo {
189 base_off: slot0_base as usize,
190 trailer_off: slot1_base - offset_from_end,
Fabio Utzig1e48b912018-09-18 09:04:18 -0300191 len: slot0_len as usize,
David Browndb9a3952017-11-06 13:16:15 -0700192 };
193
194 // And an upgrade image.
195 let slot1 = SlotInfo {
196 base_off: slot1_base as usize,
197 trailer_off: scratch_base - offset_from_end,
Fabio Utzig1e48b912018-09-18 09:04:18 -0300198 len: slot1_len as usize,
David Browndb9a3952017-11-06 13:16:15 -0700199 };
200
201 Run {
202 flash: flash,
203 areadesc: areadesc,
204 slots: [slot0, slot1],
205 align: align,
Fabio Utzigea0290b2018-08-09 14:23:01 -0300206 erased_val: erased_val,
David Browndb9a3952017-11-06 13:16:15 -0700207 }
208 }
209
210 pub fn each_device<F>(f: F)
211 where F: Fn(&mut Run)
212 {
213 for &dev in ALL_DEVICES {
214 for &align in &[1, 2, 4, 8] {
Fabio Utzigea0290b2018-08-09 14:23:01 -0300215 for &erased_val in &[0, 0xff] {
216 let mut run = Run::new(dev, align, erased_val);
217 f(&mut run);
218 }
David Browndb9a3952017-11-06 13:16:15 -0700219 }
220 }
221 }
David Brownf48b9502017-11-06 14:00:26 -0700222
223 /// Construct an `Images` that doesn't expect an upgrade to happen.
224 pub fn make_no_upgrade_image(&self) -> Images {
225 let mut flash = self.flash.clone();
Fabio Utzig1e48b912018-09-18 09:04:18 -0300226 let primaries = install_image(&mut flash, &self.slots, 0, 32784, false);
227 let upgrades = install_image(&mut flash, &self.slots, 1, 41928, false);
David Brownf48b9502017-11-06 14:00:26 -0700228 Images {
229 flash: flash,
230 areadesc: self.areadesc.clone(),
Fabio Utzig1e48b912018-09-18 09:04:18 -0300231 slots: [self.slots[0].clone(), self.slots[1].clone()],
232 primaries: primaries,
233 upgrades: upgrades,
David Brownc49811e2017-11-06 14:20:45 -0700234 total_count: None,
David Brownf48b9502017-11-06 14:00:26 -0700235 align: self.align,
Fabio Utzigea0290b2018-08-09 14:23:01 -0300236 erased_val: self.erased_val,
David Brownf48b9502017-11-06 14:00:26 -0700237 }
238 }
239
240 /// Construct an `Images` for normal testing.
241 pub fn make_image(&self) -> Images {
242 let mut images = self.make_no_upgrade_image();
Fabio Utzig1e48b912018-09-18 09:04:18 -0300243 mark_upgrade(&mut images.flash, &images.slots[1]);
David Brownc49811e2017-11-06 14:20:45 -0700244
245 // upgrades without fails, counts number of flash operations
246 let total_count = match images.run_basic_upgrade() {
247 Ok(v) => v,
248 Err(_) => {
249 panic!("Unable to perform basic upgrade");
250 },
251 };
252
253 images.total_count = Some(total_count);
David Brownf48b9502017-11-06 14:00:26 -0700254 images
255 }
256
257 pub fn make_bad_slot1_image(&self) -> Images {
258 let mut bad_flash = self.flash.clone();
Fabio Utzig1e48b912018-09-18 09:04:18 -0300259 let primaries = install_image(&mut bad_flash, &self.slots, 0, 32784, false);
260 let upgrades = install_image(&mut bad_flash, &self.slots, 1, 41928, true);
David Brownf48b9502017-11-06 14:00:26 -0700261 Images {
262 flash: bad_flash,
263 areadesc: self.areadesc.clone(),
Fabio Utzig1e48b912018-09-18 09:04:18 -0300264 slots: [self.slots[0].clone(), self.slots[1].clone()],
265 primaries: primaries,
266 upgrades: upgrades,
David Brownc49811e2017-11-06 14:20:45 -0700267 total_count: None,
David Brownf48b9502017-11-06 14:00:26 -0700268 align: self.align,
Fabio Utzigea0290b2018-08-09 14:23:01 -0300269 erased_val: self.erased_val,
David Brownf48b9502017-11-06 14:00:26 -0700270 }
271 }
David Brownc49811e2017-11-06 14:20:45 -0700272
David Browndb9a3952017-11-06 13:16:15 -0700273}
274
David Browndd2b1182017-11-02 15:39:21 -0600275pub struct RunStatus {
David Brown2639e072017-10-11 11:18:44 -0600276 failures: usize,
277 passes: usize,
278}
279
280impl RunStatus {
David Browndd2b1182017-11-02 15:39:21 -0600281 pub fn new() -> RunStatus {
David Brown2639e072017-10-11 11:18:44 -0600282 RunStatus {
283 failures: 0,
284 passes: 0,
285 }
286 }
287
Fabio Utzigea0290b2018-08-09 14:23:01 -0300288 pub fn run_single(&mut self, device: DeviceName, align: u8, erased_val: u8) {
David Brown2639e072017-10-11 11:18:44 -0600289 warn!("Running on device {} with alignment {}", device, align);
290
Fabio Utzigea0290b2018-08-09 14:23:01 -0300291 let run = Run::new(device, align, erased_val);
David Brown2639e072017-10-11 11:18:44 -0600292
David Brown2639e072017-10-11 11:18:44 -0600293 let mut failed = false;
294
295 // Creates a badly signed image in slot1 to check that it is not
296 // upgraded to
David Brownf48b9502017-11-06 14:00:26 -0700297 let bad_slot1_image = run.make_bad_slot1_image();
David Brown2639e072017-10-11 11:18:44 -0600298
David Brown5f7ec2b2017-11-06 13:54:02 -0700299 failed |= bad_slot1_image.run_signfail_upgrade();
David Brown2639e072017-10-11 11:18:44 -0600300
David Brownf48b9502017-11-06 14:00:26 -0700301 let images = run.make_no_upgrade_image();
David Brown5f7ec2b2017-11-06 13:54:02 -0700302 failed |= images.run_norevert_newimage();
David Brown2639e072017-10-11 11:18:44 -0600303
David Brownf48b9502017-11-06 14:00:26 -0700304 let images = run.make_image();
David Brown2639e072017-10-11 11:18:44 -0600305
David Brown5f7ec2b2017-11-06 13:54:02 -0700306 failed |= images.run_basic_revert();
David Brownc49811e2017-11-06 14:20:45 -0700307 failed |= images.run_revert_with_fails();
308 failed |= images.run_perm_with_fails();
309 failed |= images.run_perm_with_random_fails(5);
David Brown5f7ec2b2017-11-06 13:54:02 -0700310 failed |= images.run_norevert();
David Brown2639e072017-10-11 11:18:44 -0600311
Fabio Utzigb841f0a2017-11-24 08:11:05 -0200312 failed |= images.run_with_status_fails_complete();
Fabio Utzigeedcc452017-11-24 10:48:52 -0200313 failed |= images.run_with_status_fails_with_reset();
Fabio Utzigb841f0a2017-11-24 08:11:05 -0200314
David Brown2639e072017-10-11 11:18:44 -0600315 //show_flash(&flash);
316
317 if failed {
318 self.failures += 1;
319 } else {
320 self.passes += 1;
321 }
322 }
David Browndd2b1182017-11-02 15:39:21 -0600323
324 pub fn failures(&self) -> usize {
325 self.failures
326 }
David Brown2639e072017-10-11 11:18:44 -0600327}
328
David Browndecbd042017-10-19 10:43:17 -0600329/// Build the Flash and area descriptor for a given device.
Fabio Utzigea0290b2018-08-09 14:23:01 -0300330pub fn make_device(device: DeviceName, align: u8, erased_val: u8) -> (SimFlash, AreaDesc) {
David Browndecbd042017-10-19 10:43:17 -0600331 match device {
332 DeviceName::Stm32f4 => {
333 // STM style flash. Large sectors, with a large scratch area.
334 let flash = SimFlash::new(vec![16 * 1024, 16 * 1024, 16 * 1024, 16 * 1024,
335 64 * 1024,
336 128 * 1024, 128 * 1024, 128 * 1024],
Fabio Utzigea0290b2018-08-09 14:23:01 -0300337 align as usize, erased_val);
David Browndecbd042017-10-19 10:43:17 -0600338 let mut areadesc = AreaDesc::new(&flash);
339 areadesc.add_image(0x020000, 0x020000, FlashId::Image0);
340 areadesc.add_image(0x040000, 0x020000, FlashId::Image1);
341 areadesc.add_image(0x060000, 0x020000, FlashId::ImageScratch);
342 (flash, areadesc)
343 }
344 DeviceName::K64f => {
345 // NXP style flash. Small sectors, one small sector for scratch.
Fabio Utzigea0290b2018-08-09 14:23:01 -0300346 let flash = SimFlash::new(vec![4096; 128], align as usize, erased_val);
David Browndecbd042017-10-19 10:43:17 -0600347
348 let mut areadesc = AreaDesc::new(&flash);
349 areadesc.add_image(0x020000, 0x020000, FlashId::Image0);
350 areadesc.add_image(0x040000, 0x020000, FlashId::Image1);
351 areadesc.add_image(0x060000, 0x001000, FlashId::ImageScratch);
352 (flash, areadesc)
353 }
354 DeviceName::K64fBig => {
355 // Simulating an STM style flash on top of an NXP style flash. Underlying flash device
356 // uses small sectors, but we tell the bootloader they are large.
Fabio Utzigea0290b2018-08-09 14:23:01 -0300357 let flash = SimFlash::new(vec![4096; 128], align as usize, erased_val);
David Browndecbd042017-10-19 10:43:17 -0600358
359 let mut areadesc = AreaDesc::new(&flash);
360 areadesc.add_simple_image(0x020000, 0x020000, FlashId::Image0);
361 areadesc.add_simple_image(0x040000, 0x020000, FlashId::Image1);
362 areadesc.add_simple_image(0x060000, 0x020000, FlashId::ImageScratch);
363 (flash, areadesc)
364 }
365 DeviceName::Nrf52840 => {
366 // Simulating the flash on the nrf52840 with partitions set up so that the scratch size
367 // does not divide into the image size.
Fabio Utzigea0290b2018-08-09 14:23:01 -0300368 let flash = SimFlash::new(vec![4096; 128], align as usize, erased_val);
David Browndecbd042017-10-19 10:43:17 -0600369
370 let mut areadesc = AreaDesc::new(&flash);
371 areadesc.add_image(0x008000, 0x034000, FlashId::Image0);
372 areadesc.add_image(0x03c000, 0x034000, FlashId::Image1);
373 areadesc.add_image(0x070000, 0x00d000, FlashId::ImageScratch);
374 (flash, areadesc)
375 }
376 }
377}
378
David Brown5f7ec2b2017-11-06 13:54:02 -0700379impl Images {
380 /// A simple upgrade without forced failures.
381 ///
382 /// Returns the number of flash operations which can later be used to
383 /// inject failures at chosen steps.
David Brownc49811e2017-11-06 14:20:45 -0700384 pub fn run_basic_upgrade(&self) -> Result<i32, ()> {
David Brown5f7ec2b2017-11-06 13:54:02 -0700385 let (fl, total_count) = try_upgrade(&self.flash, &self, None);
386 info!("Total flash operation count={}", total_count);
David Brown2639e072017-10-11 11:18:44 -0600387
Fabio Utzig1e48b912018-09-18 09:04:18 -0300388 if !verify_image(&fl, &self.slots, 0, &self.upgrades) {
David Brown5f7ec2b2017-11-06 13:54:02 -0700389 warn!("Image mismatch after first boot");
390 Err(())
391 } else {
392 Ok(total_count)
David Brown2639e072017-10-11 11:18:44 -0600393 }
394 }
395
David Brown5f7ec2b2017-11-06 13:54:02 -0700396 #[cfg(feature = "overwrite-only")]
David Browna4167ef2017-11-06 14:30:05 -0700397 pub fn run_basic_revert(&self) -> bool {
David Brown5f7ec2b2017-11-06 13:54:02 -0700398 false
399 }
David Brown2639e072017-10-11 11:18:44 -0600400
David Brown5f7ec2b2017-11-06 13:54:02 -0700401 #[cfg(not(feature = "overwrite-only"))]
David Browna4167ef2017-11-06 14:30:05 -0700402 pub fn run_basic_revert(&self) -> bool {
David Brown5f7ec2b2017-11-06 13:54:02 -0700403 let mut fails = 0;
David Brown2639e072017-10-11 11:18:44 -0600404
David Brown5f7ec2b2017-11-06 13:54:02 -0700405 // FIXME: this test would also pass if no swap is ever performed???
406 if Caps::SwapUpgrade.present() {
407 for count in 2 .. 5 {
408 info!("Try revert: {}", count);
409 let fl = try_revert(&self.flash, &self.areadesc, count, self.align);
Fabio Utzig1e48b912018-09-18 09:04:18 -0300410 if !verify_image(&fl, &self.slots, 0, &self.primaries) {
David Brown5f7ec2b2017-11-06 13:54:02 -0700411 error!("Revert failure on count {}", count);
412 fails += 1;
413 }
414 }
415 }
416
417 fails > 0
418 }
419
David Browna4167ef2017-11-06 14:30:05 -0700420 pub fn run_perm_with_fails(&self) -> bool {
David Brown5f7ec2b2017-11-06 13:54:02 -0700421 let mut fails = 0;
David Brownc49811e2017-11-06 14:20:45 -0700422 let total_flash_ops = self.total_count.unwrap();
David Brown5f7ec2b2017-11-06 13:54:02 -0700423
424 // Let's try an image halfway through.
425 for i in 1 .. total_flash_ops {
426 info!("Try interruption at {}", i);
427 let (fl, count) = try_upgrade(&self.flash, &self, Some(i));
428 info!("Second boot, count={}", count);
Fabio Utzig1e48b912018-09-18 09:04:18 -0300429 if !verify_image(&fl, &self.slots, 0, &self.upgrades) {
David Brown5f7ec2b2017-11-06 13:54:02 -0700430 warn!("FAIL at step {} of {}", i, total_flash_ops);
431 fails += 1;
432 }
433
Fabio Utzig1e48b912018-09-18 09:04:18 -0300434 if !verify_trailer(&fl, self.slots[0].trailer_off, BOOT_MAGIC_GOOD,
Fabio Utzigea0290b2018-08-09 14:23:01 -0300435 BOOT_FLAG_SET, BOOT_FLAG_SET) {
David Brown5f7ec2b2017-11-06 13:54:02 -0700436 warn!("Mismatched trailer for Slot 0");
437 fails += 1;
438 }
439
Fabio Utzig1e48b912018-09-18 09:04:18 -0300440 if !verify_trailer(&fl, self.slots[1].trailer_off, BOOT_MAGIC_UNSET,
Fabio Utzigea0290b2018-08-09 14:23:01 -0300441 BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
David Brown5f7ec2b2017-11-06 13:54:02 -0700442 warn!("Mismatched trailer for Slot 1");
443 fails += 1;
444 }
445
446 if Caps::SwapUpgrade.present() {
Fabio Utzig1e48b912018-09-18 09:04:18 -0300447 if !verify_image(&fl, &self.slots, 1, &self.primaries) {
David Brown5f7ec2b2017-11-06 13:54:02 -0700448 warn!("Slot 1 FAIL at step {} of {}", i, total_flash_ops);
449 fails += 1;
450 }
451 }
452 }
453
454 if fails > 0 {
455 error!("{} out of {} failed {:.2}%", fails, total_flash_ops,
456 fails as f32 * 100.0 / total_flash_ops as f32);
457 }
458
459 fails > 0
460 }
461
David Browna4167ef2017-11-06 14:30:05 -0700462 pub fn run_perm_with_random_fails_5(&self) -> bool {
463 self.run_perm_with_random_fails(5)
464 }
465
David Brownc49811e2017-11-06 14:20:45 -0700466 fn run_perm_with_random_fails(&self, total_fails: usize) -> bool {
David Brown5f7ec2b2017-11-06 13:54:02 -0700467 let mut fails = 0;
David Brownc49811e2017-11-06 14:20:45 -0700468 let total_flash_ops = self.total_count.unwrap();
David Brown5f7ec2b2017-11-06 13:54:02 -0700469 let (fl, total_counts) = try_random_fails(&self.flash, &self,
470 total_flash_ops, total_fails);
471 info!("Random interruptions at reset points={:?}", total_counts);
472
Fabio Utzig1e48b912018-09-18 09:04:18 -0300473 let slot0_ok = verify_image(&fl, &self.slots, 0, &self.upgrades);
David Brown5f7ec2b2017-11-06 13:54:02 -0700474 let slot1_ok = if Caps::SwapUpgrade.present() {
Fabio Utzig1e48b912018-09-18 09:04:18 -0300475 verify_image(&fl, &self.slots, 1, &self.primaries)
David Brown5f7ec2b2017-11-06 13:54:02 -0700476 } else {
477 true
478 };
479 if !slot0_ok || !slot1_ok {
480 error!("Image mismatch after random interrupts: slot0={} slot1={}",
481 if slot0_ok { "ok" } else { "fail" },
482 if slot1_ok { "ok" } else { "fail" });
483 fails += 1;
484 }
Fabio Utzig1e48b912018-09-18 09:04:18 -0300485 if !verify_trailer(&fl, self.slots[0].trailer_off, BOOT_MAGIC_GOOD,
Fabio Utzigea0290b2018-08-09 14:23:01 -0300486 BOOT_FLAG_SET, BOOT_FLAG_SET) {
David Brown5f7ec2b2017-11-06 13:54:02 -0700487 error!("Mismatched trailer for Slot 0");
488 fails += 1;
489 }
Fabio Utzig1e48b912018-09-18 09:04:18 -0300490 if !verify_trailer(&fl, self.slots[1].trailer_off, BOOT_MAGIC_UNSET,
Fabio Utzigea0290b2018-08-09 14:23:01 -0300491 BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
David Brown5f7ec2b2017-11-06 13:54:02 -0700492 error!("Mismatched trailer for Slot 1");
David Brown2639e072017-10-11 11:18:44 -0600493 fails += 1;
494 }
495
David Brown5f7ec2b2017-11-06 13:54:02 -0700496 if fails > 0 {
497 error!("Error testing perm upgrade with {} fails", total_fails);
498 }
499
500 fails > 0
501 }
502
503 #[cfg(feature = "overwrite-only")]
David Browna4167ef2017-11-06 14:30:05 -0700504 pub fn run_revert_with_fails(&self) -> bool {
David Brown5f7ec2b2017-11-06 13:54:02 -0700505 false
506 }
507
508 #[cfg(not(feature = "overwrite-only"))]
David Browna4167ef2017-11-06 14:30:05 -0700509 pub fn run_revert_with_fails(&self) -> bool {
David Brown5f7ec2b2017-11-06 13:54:02 -0700510 let mut fails = 0;
511
512 if Caps::SwapUpgrade.present() {
David Brownc49811e2017-11-06 14:20:45 -0700513 for i in 1 .. (self.total_count.unwrap() - 1) {
David Brown5f7ec2b2017-11-06 13:54:02 -0700514 info!("Try interruption at {}", i);
515 if try_revert_with_fail_at(&self.flash, &self, i) {
516 error!("Revert failed at interruption {}", i);
517 fails += 1;
518 }
519 }
520 }
521
522 fails > 0
523 }
524
525 #[cfg(feature = "overwrite-only")]
David Browna4167ef2017-11-06 14:30:05 -0700526 pub fn run_norevert(&self) -> bool {
David Brown5f7ec2b2017-11-06 13:54:02 -0700527 false
528 }
529
530 #[cfg(not(feature = "overwrite-only"))]
David Browna4167ef2017-11-06 14:30:05 -0700531 pub fn run_norevert(&self) -> bool {
David Brown5f7ec2b2017-11-06 13:54:02 -0700532 let mut fl = self.flash.clone();
533 let mut fails = 0;
534
535 info!("Try norevert");
536
537 // First do a normal upgrade...
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200538 let (result, _) = c::boot_go(&mut fl, &self.areadesc, None, self.align, false);
539 if result != 0 {
David Brown5f7ec2b2017-11-06 13:54:02 -0700540 warn!("Failed first boot");
541 fails += 1;
542 }
543
544 //FIXME: copy_done is written by boot_go, is it ok if no copy
545 // was ever done?
546
Fabio Utzig1e48b912018-09-18 09:04:18 -0300547 if !verify_image(&fl, &self.slots, 0, &self.upgrades) {
David Brown5f7ec2b2017-11-06 13:54:02 -0700548 warn!("Slot 0 image verification FAIL");
549 fails += 1;
550 }
Fabio Utzig1e48b912018-09-18 09:04:18 -0300551 if !verify_trailer(&fl, self.slots[0].trailer_off, BOOT_MAGIC_GOOD,
Fabio Utzigea0290b2018-08-09 14:23:01 -0300552 BOOT_FLAG_UNSET, BOOT_FLAG_SET) {
David Brown2639e072017-10-11 11:18:44 -0600553 warn!("Mismatched trailer for Slot 0");
554 fails += 1;
555 }
Fabio Utzig1e48b912018-09-18 09:04:18 -0300556 if !verify_trailer(&fl, self.slots[1].trailer_off, BOOT_MAGIC_UNSET,
Fabio Utzigea0290b2018-08-09 14:23:01 -0300557 BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
David Brown2639e072017-10-11 11:18:44 -0600558 warn!("Mismatched trailer for Slot 1");
559 fails += 1;
560 }
561
David Brown5f7ec2b2017-11-06 13:54:02 -0700562 // Marks image in slot0 as permanent, no revert should happen...
Fabio Utzig1e48b912018-09-18 09:04:18 -0300563 mark_permanent_upgrade(&mut fl, &self.slots[0], self.align, self.erased_val);
David Brown5f7ec2b2017-11-06 13:54:02 -0700564
Fabio Utzig1e48b912018-09-18 09:04:18 -0300565 if !verify_trailer(&fl, self.slots[0].trailer_off, BOOT_MAGIC_GOOD,
Fabio Utzigea0290b2018-08-09 14:23:01 -0300566 BOOT_FLAG_SET, BOOT_FLAG_SET) {
David Brown5f7ec2b2017-11-06 13:54:02 -0700567 warn!("Mismatched trailer for Slot 0");
568 fails += 1;
David Brown2639e072017-10-11 11:18:44 -0600569 }
David Brown2639e072017-10-11 11:18:44 -0600570
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200571 let (result, _) = c::boot_go(&mut fl, &self.areadesc, None, self.align, false);
572 if result != 0 {
David Brown5f7ec2b2017-11-06 13:54:02 -0700573 warn!("Failed second boot");
574 fails += 1;
David Brown2639e072017-10-11 11:18:44 -0600575 }
David Brown5f7ec2b2017-11-06 13:54:02 -0700576
Fabio Utzig1e48b912018-09-18 09:04:18 -0300577 if !verify_trailer(&fl, self.slots[0].trailer_off, BOOT_MAGIC_GOOD,
Fabio Utzigea0290b2018-08-09 14:23:01 -0300578 BOOT_FLAG_SET, BOOT_FLAG_SET) {
David Brown5f7ec2b2017-11-06 13:54:02 -0700579 warn!("Mismatched trailer for Slot 0");
580 fails += 1;
581 }
Fabio Utzig1e48b912018-09-18 09:04:18 -0300582 if !verify_image(&fl, &self.slots, 0, &self.upgrades) {
David Brown5f7ec2b2017-11-06 13:54:02 -0700583 warn!("Failed image verification");
584 fails += 1;
585 }
586
587 if fails > 0 {
588 error!("Error running upgrade without revert");
589 }
590
591 fails > 0
David Brown2639e072017-10-11 11:18:44 -0600592 }
593
David Brown5f7ec2b2017-11-06 13:54:02 -0700594 // Tests a new image written to slot0 that already has magic and image_ok set
595 // while there is no image on slot1, so no revert should ever happen...
David Brownc49811e2017-11-06 14:20:45 -0700596 pub fn run_norevert_newimage(&self) -> bool {
David Brown5f7ec2b2017-11-06 13:54:02 -0700597 let mut fl = self.flash.clone();
598 let mut fails = 0;
David Brown2639e072017-10-11 11:18:44 -0600599
David Brown5f7ec2b2017-11-06 13:54:02 -0700600 info!("Try non-revert on imgtool generated image");
David Brown2639e072017-10-11 11:18:44 -0600601
Fabio Utzig1e48b912018-09-18 09:04:18 -0300602 mark_upgrade(&mut fl, &self.slots[0]);
David Brown2639e072017-10-11 11:18:44 -0600603
David Brown5f7ec2b2017-11-06 13:54:02 -0700604 // This simulates writing an image created by imgtool to Slot 0
Fabio Utzig1e48b912018-09-18 09:04:18 -0300605 if !verify_trailer(&fl, self.slots[0].trailer_off, BOOT_MAGIC_GOOD,
Fabio Utzigea0290b2018-08-09 14:23:01 -0300606 BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
David Brown5f7ec2b2017-11-06 13:54:02 -0700607 warn!("Mismatched trailer for Slot 0");
608 fails += 1;
609 }
David Brown2639e072017-10-11 11:18:44 -0600610
David Brown5f7ec2b2017-11-06 13:54:02 -0700611 // Run the bootloader...
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200612 let (result, _) = c::boot_go(&mut fl, &self.areadesc, None, self.align, false);
613 if result != 0 {
David Brown5f7ec2b2017-11-06 13:54:02 -0700614 warn!("Failed first boot");
615 fails += 1;
616 }
617
618 // State should not have changed
Fabio Utzig1e48b912018-09-18 09:04:18 -0300619 if !verify_image(&fl, &self.slots, 0, &self.primaries) {
David Brown5f7ec2b2017-11-06 13:54:02 -0700620 warn!("Failed image verification");
621 fails += 1;
622 }
Fabio Utzig1e48b912018-09-18 09:04:18 -0300623 if !verify_trailer(&fl, self.slots[0].trailer_off, BOOT_MAGIC_GOOD,
Fabio Utzigea0290b2018-08-09 14:23:01 -0300624 BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
David Brown5f7ec2b2017-11-06 13:54:02 -0700625 warn!("Mismatched trailer for Slot 0");
626 fails += 1;
627 }
Fabio Utzig1e48b912018-09-18 09:04:18 -0300628 if !verify_trailer(&fl, self.slots[1].trailer_off, BOOT_MAGIC_UNSET,
Fabio Utzigea0290b2018-08-09 14:23:01 -0300629 BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
David Brown5f7ec2b2017-11-06 13:54:02 -0700630 warn!("Mismatched trailer for Slot 1");
631 fails += 1;
632 }
633
634 if fails > 0 {
635 error!("Expected a non revert with new image");
636 }
637
638 fails > 0
David Brown2639e072017-10-11 11:18:44 -0600639 }
640
David Brown5f7ec2b2017-11-06 13:54:02 -0700641 // Tests a new image written to slot0 that already has magic and image_ok set
642 // while there is no image on slot1, so no revert should ever happen...
David Brownc49811e2017-11-06 14:20:45 -0700643 pub fn run_signfail_upgrade(&self) -> bool {
David Brown5f7ec2b2017-11-06 13:54:02 -0700644 let mut fl = self.flash.clone();
645 let mut fails = 0;
David Brown2639e072017-10-11 11:18:44 -0600646
David Brown5f7ec2b2017-11-06 13:54:02 -0700647 info!("Try upgrade image with bad signature");
648
Fabio Utzig1e48b912018-09-18 09:04:18 -0300649 mark_upgrade(&mut fl, &self.slots[0]);
650 mark_permanent_upgrade(&mut fl, &self.slots[0], self.align, self.erased_val);
651 mark_upgrade(&mut fl, &self.slots[1]);
David Brown5f7ec2b2017-11-06 13:54:02 -0700652
Fabio Utzig1e48b912018-09-18 09:04:18 -0300653 if !verify_trailer(&fl, self.slots[0].trailer_off, BOOT_MAGIC_GOOD,
Fabio Utzigea0290b2018-08-09 14:23:01 -0300654 BOOT_FLAG_SET, BOOT_FLAG_UNSET) {
David Brown5f7ec2b2017-11-06 13:54:02 -0700655 warn!("Mismatched trailer for Slot 0");
656 fails += 1;
657 }
658
659 // Run the bootloader...
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200660 let (result, _) = c::boot_go(&mut fl, &self.areadesc, None, self.align, false);
661 if result != 0 {
David Brown5f7ec2b2017-11-06 13:54:02 -0700662 warn!("Failed first boot");
663 fails += 1;
664 }
665
666 // State should not have changed
Fabio Utzig1e48b912018-09-18 09:04:18 -0300667 if !verify_image(&fl, &self.slots, 0, &self.primaries) {
David Brown5f7ec2b2017-11-06 13:54:02 -0700668 warn!("Failed image verification");
669 fails += 1;
670 }
Fabio Utzig1e48b912018-09-18 09:04:18 -0300671 if !verify_trailer(&fl, self.slots[0].trailer_off, BOOT_MAGIC_GOOD,
Fabio Utzigea0290b2018-08-09 14:23:01 -0300672 BOOT_FLAG_SET, BOOT_FLAG_UNSET) {
David Brown5f7ec2b2017-11-06 13:54:02 -0700673 warn!("Mismatched trailer for Slot 0");
674 fails += 1;
675 }
676
677 if fails > 0 {
678 error!("Expected an upgrade failure when image has bad signature");
679 }
680
681 fails > 0
David Brown2639e072017-10-11 11:18:44 -0600682 }
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200683
Fabio Utziga91c6262017-12-06 09:01:12 -0200684 #[cfg(not(feature = "overwrite-only"))]
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200685 fn trailer_sz(&self) -> usize {
686 c::boot_trailer_sz(self.align) as usize
687 }
688
689 // FIXME: could get status sz from bootloader
Fabio Utziga91c6262017-12-06 09:01:12 -0200690 #[cfg(not(feature = "overwrite-only"))]
Fabio Utzig1e48b912018-09-18 09:04:18 -0300691 #[cfg(not(feature = "enc-rsa"))]
692 #[cfg(not(feature = "enc-kw"))]
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200693 fn status_sz(&self) -> usize {
694 self.trailer_sz() - (16 + 24)
695 }
696
Fabio Utzig1e48b912018-09-18 09:04:18 -0300697 #[cfg(feature = "enc-rsa")]
Fabio Utzig9b7a2582018-12-03 10:40:05 -0200698 #[cfg(not(feature = "overwrite-only"))]
Fabio Utzig1e48b912018-09-18 09:04:18 -0300699 fn status_sz(&self) -> usize {
700 self.trailer_sz() - (16 + 24 + 32)
701 }
702
703 #[cfg(feature = "enc-kw")]
Fabio Utzig9b7a2582018-12-03 10:40:05 -0200704 #[cfg(not(feature = "overwrite-only"))]
Fabio Utzig1e48b912018-09-18 09:04:18 -0300705 fn status_sz(&self) -> usize {
706 self.trailer_sz() - (16 + 24 + 32)
707 }
708
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200709 /// This test runs a simple upgrade with no fails in the images, but
710 /// allowing for fails in the status area. This should run to the end
711 /// and warn that write fails were detected...
712 #[cfg(not(feature = "validate-slot0"))]
713 pub fn run_with_status_fails_complete(&self) -> bool { false }
714
715 #[cfg(feature = "validate-slot0")]
716 pub fn run_with_status_fails_complete(&self) -> bool {
717 let mut fl = self.flash.clone();
718 let mut fails = 0;
719
720 info!("Try swap with status fails");
721
Fabio Utzig1e48b912018-09-18 09:04:18 -0300722 mark_permanent_upgrade(&mut fl, &self.slots[1], self.align, self.erased_val);
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200723
Fabio Utzig1e48b912018-09-18 09:04:18 -0300724 let status_off = self.slots[1].base_off - self.trailer_sz();
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200725
726 // Always fail writes to status area...
727 let _ = fl.add_bad_region(status_off, self.status_sz(), 1.0);
728
729 let (result, asserts) = c::boot_go(&mut fl, &self.areadesc, None, self.align, true);
730 if result != 0 {
731 warn!("Failed!");
732 fails += 1;
733 }
734
735 // Failed writes to the marked "bad" region don't assert anymore.
736 // Any detected assert() is happening in another part of the code.
737 if asserts != 0 {
738 warn!("At least one assert() was called");
739 fails += 1;
740 }
741
Fabio Utzig1e48b912018-09-18 09:04:18 -0300742 if !verify_trailer(&fl, self.slots[0].trailer_off, BOOT_MAGIC_GOOD,
Fabio Utzigea0290b2018-08-09 14:23:01 -0300743 BOOT_FLAG_SET, BOOT_FLAG_SET) {
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200744 warn!("Mismatched trailer for Slot 0");
745 fails += 1;
746 }
747
Fabio Utzig1e48b912018-09-18 09:04:18 -0300748 if !verify_image(&fl, &self.slots, 0, &self.upgrades) {
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200749 warn!("Failed image verification");
750 fails += 1;
751 }
752
753 info!("validate slot0 enabled; re-run of boot_go should just work");
754 let (result, _) = c::boot_go(&mut fl, &self.areadesc, None, self.align, false);
755 if result != 0 {
756 warn!("Failed!");
757 fails += 1;
758 }
759
760 if fails > 0 {
761 error!("Error running upgrade with status write fails");
762 }
763
764 fails > 0
765 }
766
767 /// This test runs a simple upgrade with no fails in the images, but
768 /// allowing for fails in the status area. This should run to the end
769 /// and warn that write fails were detected...
770 #[cfg(feature = "validate-slot0")]
771 pub fn run_with_status_fails_with_reset(&self) -> bool {
772 let mut fl = self.flash.clone();
773 let mut fails = 0;
774 let mut count = self.total_count.unwrap() / 2;
775
776 //info!("count={}\n", count);
777
778 info!("Try interrupted swap with status fails");
779
Fabio Utzig1e48b912018-09-18 09:04:18 -0300780 mark_permanent_upgrade(&mut fl, &self.slots[1], self.align, self.erased_val);
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200781
Fabio Utzig1e48b912018-09-18 09:04:18 -0300782 let status_off = self.slots[1].base_off - self.trailer_sz();
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200783
784 // Mark the status area as a bad area
785 let _ = fl.add_bad_region(status_off, self.status_sz(), 0.5);
786
787 // Should not fail, writing to bad regions does not assert
788 let (_, asserts) = c::boot_go(&mut fl, &self.areadesc, Some(&mut count), self.align, true);
789 if asserts != 0 {
790 warn!("At least one assert() was called");
791 fails += 1;
792 }
793
794 fl.reset_bad_regions();
795
796 // Disabling write verification the only assert triggered by
797 // boot_go should be checking for integrity of status bytes.
798 fl.set_verify_writes(false);
799
800 info!("Resuming an interrupted swap operation");
801 let (_, asserts) = c::boot_go(&mut fl, &self.areadesc, None, self.align, true);
802
803 // This might throw no asserts, for large sector devices, where
804 // a single failure writing is indistinguishable from no failure,
805 // or throw a single assert for small sector devices that fail
806 // multiple times...
807 if asserts > 1 {
808 warn!("Expected single assert validating slot0, more detected {}", asserts);
809 fails += 1;
810 }
811
812 if fails > 0 {
813 error!("Error running upgrade with status write fails");
814 }
815
816 fails > 0
817 }
818
819 #[cfg(not(feature = "validate-slot0"))]
820 #[cfg(not(feature = "overwrite-only"))]
821 pub fn run_with_status_fails_with_reset(&self) -> bool {
822 let mut fl = self.flash.clone();
823 let mut fails = 0;
824
825 info!("Try interrupted swap with status fails");
826
Fabio Utzig1e48b912018-09-18 09:04:18 -0300827 mark_permanent_upgrade(&mut fl, &self.slots[1], self.align, self.erased_val);
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200828
Fabio Utzig1e48b912018-09-18 09:04:18 -0300829 let status_off = self.slots[1].base_off - self.trailer_sz();
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200830
831 // Mark the status area as a bad area
832 let _ = fl.add_bad_region(status_off, self.status_sz(), 1.0);
833
834 // This is expected to fail while writing to bad regions...
835 let (_, asserts) = c::boot_go(&mut fl, &self.areadesc, None, self.align, true);
836 if asserts == 0 {
837 warn!("No assert() detected");
838 fails += 1;
839 }
840
841 fails > 0
842 }
843
844 #[cfg(feature = "overwrite-only")]
845 pub fn run_with_status_fails_with_reset(&self) -> bool {
846 false
847 }
David Brown2639e072017-10-11 11:18:44 -0600848}
849
850/// Test a boot, optionally stopping after 'n' flash options. Returns a count
851/// of the number of flash operations done total.
David Brown3f687dc2017-11-06 13:41:18 -0700852fn try_upgrade(flash: &SimFlash, images: &Images,
David Brown2639e072017-10-11 11:18:44 -0600853 stop: Option<i32>) -> (SimFlash, i32) {
854 // Clone the flash to have a new copy.
855 let mut fl = flash.clone();
856
Fabio Utzig1e48b912018-09-18 09:04:18 -0300857 mark_permanent_upgrade(&mut fl, &images.slots[1], images.align, images.erased_val);
David Brown2639e072017-10-11 11:18:44 -0600858
David Brownee61c832017-11-06 11:13:25 -0700859 let mut counter = stop.unwrap_or(0);
860
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200861 let (first_interrupted, count) = match c::boot_go(&mut fl, &images.areadesc, Some(&mut counter), images.align, false) {
862 (-0x13579, _) => (true, stop.unwrap()),
863 (0, _) => (false, -counter),
864 (x, _) => panic!("Unknown return: {}", x),
David Brown2639e072017-10-11 11:18:44 -0600865 };
David Brown2639e072017-10-11 11:18:44 -0600866
David Brownee61c832017-11-06 11:13:25 -0700867 counter = 0;
David Brown2639e072017-10-11 11:18:44 -0600868 if first_interrupted {
869 // fl.dump();
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200870 match c::boot_go(&mut fl, &images.areadesc, Some(&mut counter), images.align, false) {
871 (-0x13579, _) => panic!("Shouldn't stop again"),
872 (0, _) => (),
873 (x, _) => panic!("Unknown return: {}", x),
David Brown2639e072017-10-11 11:18:44 -0600874 }
875 }
876
David Brownee61c832017-11-06 11:13:25 -0700877 (fl, count - counter)
David Brown2639e072017-10-11 11:18:44 -0600878}
879
880#[cfg(not(feature = "overwrite-only"))]
David Brown541860c2017-11-06 11:25:42 -0700881fn try_revert(flash: &SimFlash, areadesc: &AreaDesc, count: usize, align: u8) -> SimFlash {
David Brown2639e072017-10-11 11:18:44 -0600882 let mut fl = flash.clone();
David Brown2639e072017-10-11 11:18:44 -0600883
884 // fl.write_file("image0.bin").unwrap();
885 for i in 0 .. count {
886 info!("Running boot pass {}", i + 1);
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200887 assert_eq!(c::boot_go(&mut fl, &areadesc, None, align, false), (0, 0));
David Brown2639e072017-10-11 11:18:44 -0600888 }
889 fl
890}
891
892#[cfg(not(feature = "overwrite-only"))]
David Brown3f687dc2017-11-06 13:41:18 -0700893fn try_revert_with_fail_at(flash: &SimFlash, images: &Images,
David Brown2639e072017-10-11 11:18:44 -0600894 stop: i32) -> bool {
895 let mut fl = flash.clone();
David Brown2639e072017-10-11 11:18:44 -0600896 let mut fails = 0;
897
David Brownee61c832017-11-06 11:13:25 -0700898 let mut counter = stop;
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200899 let (x, _) = c::boot_go(&mut fl, &images.areadesc, Some(&mut counter), images.align, false);
David Brown2639e072017-10-11 11:18:44 -0600900 if x != -0x13579 {
901 warn!("Should have stopped at interruption point");
902 fails += 1;
903 }
904
Fabio Utzig1e48b912018-09-18 09:04:18 -0300905 if !verify_trailer(&fl, images.slots[0].trailer_off, None, None, BOOT_FLAG_UNSET) {
David Brown2639e072017-10-11 11:18:44 -0600906 warn!("copy_done should be unset");
907 fails += 1;
908 }
909
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200910 let (x, _) = c::boot_go(&mut fl, &images.areadesc, None, images.align, false);
David Brown2639e072017-10-11 11:18:44 -0600911 if x != 0 {
912 warn!("Should have finished upgrade");
913 fails += 1;
914 }
915
Fabio Utzig1e48b912018-09-18 09:04:18 -0300916 if !verify_image(&fl, &images.slots, 0, &images.upgrades) {
David Brown2639e072017-10-11 11:18:44 -0600917 warn!("Image in slot 0 before revert is invalid at stop={}", stop);
918 fails += 1;
919 }
Fabio Utzig1e48b912018-09-18 09:04:18 -0300920 if !verify_image(&fl, &images.slots, 1, &images.primaries) {
David Brown2639e072017-10-11 11:18:44 -0600921 warn!("Image in slot 1 before revert is invalid at stop={}", stop);
922 fails += 1;
923 }
Fabio Utzig1e48b912018-09-18 09:04:18 -0300924 if !verify_trailer(&fl, images.slots[0].trailer_off, BOOT_MAGIC_GOOD,
Fabio Utzigea0290b2018-08-09 14:23:01 -0300925 BOOT_FLAG_UNSET, BOOT_FLAG_SET) {
David Brown2639e072017-10-11 11:18:44 -0600926 warn!("Mismatched trailer for Slot 0 before revert");
927 fails += 1;
928 }
Fabio Utzig1e48b912018-09-18 09:04:18 -0300929 if !verify_trailer(&fl, images.slots[1].trailer_off, BOOT_MAGIC_UNSET,
Fabio Utzigea0290b2018-08-09 14:23:01 -0300930 BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
David Brown2639e072017-10-11 11:18:44 -0600931 warn!("Mismatched trailer for Slot 1 before revert");
932 fails += 1;
933 }
934
935 // Do Revert
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200936 let (x, _) = c::boot_go(&mut fl, &images.areadesc, None, images.align, false);
David Brown2639e072017-10-11 11:18:44 -0600937 if x != 0 {
938 warn!("Should have finished a revert");
939 fails += 1;
940 }
941
Fabio Utzig1e48b912018-09-18 09:04:18 -0300942 if !verify_image(&fl, &images.slots, 0, &images.primaries) {
David Brown2639e072017-10-11 11:18:44 -0600943 warn!("Image in slot 0 after revert is invalid at stop={}", stop);
944 fails += 1;
945 }
Fabio Utzig1e48b912018-09-18 09:04:18 -0300946 if !verify_image(&fl, &images.slots, 1, &images.upgrades) {
David Brown2639e072017-10-11 11:18:44 -0600947 warn!("Image in slot 1 after revert is invalid at stop={}", stop);
948 fails += 1;
949 }
Fabio Utzig1e48b912018-09-18 09:04:18 -0300950 if !verify_trailer(&fl, images.slots[0].trailer_off, BOOT_MAGIC_GOOD,
Fabio Utzigea0290b2018-08-09 14:23:01 -0300951 BOOT_FLAG_SET, BOOT_FLAG_SET) {
David Brown2639e072017-10-11 11:18:44 -0600952 warn!("Mismatched trailer for Slot 1 after revert");
953 fails += 1;
954 }
Fabio Utzig1e48b912018-09-18 09:04:18 -0300955 if !verify_trailer(&fl, images.slots[1].trailer_off, BOOT_MAGIC_UNSET,
Fabio Utzigea0290b2018-08-09 14:23:01 -0300956 BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
David Brown2639e072017-10-11 11:18:44 -0600957 warn!("Mismatched trailer for Slot 1 after revert");
958 fails += 1;
959 }
960
961 fails > 0
962}
963
David Brown3f687dc2017-11-06 13:41:18 -0700964fn try_random_fails(flash: &SimFlash, images: &Images,
David Brown2639e072017-10-11 11:18:44 -0600965 total_ops: i32, count: usize) -> (SimFlash, Vec<i32>) {
966 let mut fl = flash.clone();
967
Fabio Utzig1e48b912018-09-18 09:04:18 -0300968 mark_permanent_upgrade(&mut fl, &images.slots[1], images.align, images.erased_val);
David Brown2639e072017-10-11 11:18:44 -0600969
970 let mut rng = rand::thread_rng();
971 let mut resets = vec![0i32; count];
972 let mut remaining_ops = total_ops;
973 for i in 0 .. count {
974 let ops = Range::new(1, remaining_ops / 2);
975 let reset_counter = ops.ind_sample(&mut rng);
David Brownee61c832017-11-06 11:13:25 -0700976 let mut counter = reset_counter;
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200977 match c::boot_go(&mut fl, &images.areadesc, Some(&mut counter), images.align, false) {
978 (0, _) | (-0x13579, _) => (),
979 (x, _) => panic!("Unknown return: {}", x),
David Brown2639e072017-10-11 11:18:44 -0600980 }
981 remaining_ops -= reset_counter;
982 resets[i] = reset_counter;
983 }
984
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200985 match c::boot_go(&mut fl, &images.areadesc, None, images.align, false) {
986 (-0x13579, _) => panic!("Should not be have been interrupted!"),
987 (0, _) => (),
988 (x, _) => panic!("Unknown return: {}", x),
David Brown2639e072017-10-11 11:18:44 -0600989 }
990
991 (fl, resets)
992}
993
994/// Show the flash layout.
995#[allow(dead_code)]
996fn show_flash(flash: &Flash) {
997 println!("---- Flash configuration ----");
998 for sector in flash.sector_iter() {
999 println!(" {:3}: 0x{:08x}, 0x{:08x}",
1000 sector.num, sector.base, sector.size);
1001 }
1002 println!("");
1003}
1004
1005/// Install a "program" into the given image. This fakes the image header, or at least all of the
1006/// fields used by the given code. Returns a copy of the image that was written.
Fabio Utzig1e48b912018-09-18 09:04:18 -03001007fn install_image(flash: &mut Flash, slots: &[SlotInfo], slot: usize, len: usize,
1008 bad_sig: bool) -> [Option<Vec<u8>>; 2] {
1009 let offset = slots[slot].base_off;
1010 let slot_len = slots[slot].len;
David Brown2639e072017-10-11 11:18:44 -06001011
1012 let mut tlv = make_tlv();
1013
1014 // Generate a boot header. Note that the size doesn't include the header.
1015 let header = ImageHeader {
1016 magic: 0x96f3b83d,
Fabio Utzig1e48b912018-09-18 09:04:18 -03001017 load_addr: 0,
David Brown2639e072017-10-11 11:18:44 -06001018 hdr_size: 32,
Fabio Utzig1e48b912018-09-18 09:04:18 -03001019 _pad1: 0,
David Brown2639e072017-10-11 11:18:44 -06001020 img_size: len as u32,
1021 flags: tlv.get_flags(),
1022 ver: ImageVersion {
1023 major: (offset / (128 * 1024)) as u8,
1024 minor: 0,
1025 revision: 1,
1026 build_num: offset as u32,
1027 },
Fabio Utzig1e48b912018-09-18 09:04:18 -03001028 _pad2: 0,
David Brown2639e072017-10-11 11:18:44 -06001029 };
1030
1031 let b_header = header.as_raw();
1032 tlv.add_bytes(&b_header);
1033 /*
1034 let b_header = unsafe { slice::from_raw_parts(&header as *const _ as *const u8,
1035 mem::size_of::<ImageHeader>()) };
1036 */
1037 assert_eq!(b_header.len(), 32);
David Brown2639e072017-10-11 11:18:44 -06001038
1039 // The core of the image itself is just pseudorandom data.
Fabio Utzig1e48b912018-09-18 09:04:18 -03001040 let mut b_img = vec![0; len];
1041 splat(&mut b_img, offset);
David Brown2639e072017-10-11 11:18:44 -06001042
Fabio Utzig1e48b912018-09-18 09:04:18 -03001043 // TLV signatures work over plain image
1044 tlv.add_bytes(&b_img);
1045
1046 // Generate encrypted images
1047 let flag = TlvFlags::ENCRYPTED as u32;
1048 let is_encrypted = (tlv.get_flags() & flag) == flag;
1049 let mut b_encimg = vec![];
1050 if is_encrypted {
1051 let key = GenericArray::from_slice(AES_SEC_KEY);
1052 let nonce = GenericArray::from_slice(&[0; 16]);
1053 let mut cipher = Aes128Ctr::new(&key, &nonce);
1054 b_encimg = b_img.clone();
1055 cipher.apply_keystream(&mut b_encimg);
David Brown2639e072017-10-11 11:18:44 -06001056 }
1057
Fabio Utzig1e48b912018-09-18 09:04:18 -03001058 // Build the TLV itself.
1059 let mut b_tlv = if bad_sig {
1060 let good_sig = &mut tlv.make_tlv();
1061 vec![0; good_sig.len()]
1062 } else {
1063 tlv.make_tlv()
1064 };
1065
David Brown2639e072017-10-11 11:18:44 -06001066 // Pad the block to a flash alignment (8 bytes).
Fabio Utzig1e48b912018-09-18 09:04:18 -03001067 while b_tlv.len() % 8 != 0 {
1068 //FIXME: should be erase_val?
1069 b_tlv.push(0xFF);
David Brown2639e072017-10-11 11:18:44 -06001070 }
1071
Fabio Utzig1e48b912018-09-18 09:04:18 -03001072 let mut buf = vec![];
1073 buf.append(&mut b_header.to_vec());
1074 buf.append(&mut b_img);
1075 buf.append(&mut b_tlv.clone());
David Brown2639e072017-10-11 11:18:44 -06001076
Fabio Utzig1e48b912018-09-18 09:04:18 -03001077 let mut encbuf = vec![];
1078 if is_encrypted {
1079 encbuf.append(&mut b_header.to_vec());
1080 encbuf.append(&mut b_encimg);
1081 encbuf.append(&mut b_tlv);
1082 }
David Brown2639e072017-10-11 11:18:44 -06001083
Fabio Utzig1e48b912018-09-18 09:04:18 -03001084 let result: [Option<Vec<u8>>; 2];
1085
1086 // Since images are always non-encrypted in slot0, we first write an
1087 // encrypted image, re-read to use for verification, erase + flash
1088 // un-encrypted. In slot1 the image is written un-encrypted, and if
1089 // encryption is requested, it follows an erase + flash encrypted.
1090
1091 if slot == 0 {
1092 let enc_copy: Option<Vec<u8>>;
1093
1094 if is_encrypted {
1095 flash.write(offset, &encbuf).unwrap();
1096
1097 let mut enc = vec![0u8; encbuf.len()];
1098 flash.read(offset, &mut enc).unwrap();
1099
1100 enc_copy = Some(enc);
1101
1102 flash.erase(offset, slot_len).unwrap();
1103 } else {
1104 enc_copy = None;
1105 }
1106
1107 flash.write(offset, &buf).unwrap();
1108
1109 let mut copy = vec![0u8; buf.len()];
1110 flash.read(offset, &mut copy).unwrap();
1111
1112 result = [Some(copy), enc_copy];
1113 } else {
1114 flash.write(offset, &buf).unwrap();
1115
1116 let mut copy = vec![0u8; buf.len()];
1117 flash.read(offset, &mut copy).unwrap();
1118
1119 let enc_copy: Option<Vec<u8>>;
1120
1121 if is_encrypted {
1122 flash.erase(offset, slot_len).unwrap();
1123
1124 flash.write(offset, &encbuf).unwrap();
1125
1126 let mut enc = vec![0u8; encbuf.len()];
1127 flash.read(offset, &mut enc).unwrap();
1128
1129 enc_copy = Some(enc);
1130 } else {
1131 enc_copy = None;
1132 }
1133
1134 result = [Some(copy), enc_copy];
1135 }
1136
1137 result
David Brown2639e072017-10-11 11:18:44 -06001138}
1139
1140// The TLV in use depends on what kind of signature we are verifying.
1141#[cfg(feature = "sig-rsa")]
1142fn make_tlv() -> TlvGen {
1143 TlvGen::new_rsa_pss()
1144}
1145
Fabio Utzig80fde2f2017-12-05 09:25:31 -02001146#[cfg(feature = "sig-ecdsa")]
1147fn make_tlv() -> TlvGen {
1148 TlvGen::new_ecdsa()
1149}
1150
Fabio Utzig1e48b912018-09-18 09:04:18 -03001151#[cfg(feature = "enc-rsa")]
1152fn make_tlv() -> TlvGen {
1153 TlvGen::new_enc_rsa()
1154}
1155
1156#[cfg(feature = "enc-kw")]
1157fn make_tlv() -> TlvGen {
1158 TlvGen::new_enc_kw()
1159}
1160
David Brown2639e072017-10-11 11:18:44 -06001161#[cfg(not(feature = "sig-rsa"))]
Fabio Utzig80fde2f2017-12-05 09:25:31 -02001162#[cfg(not(feature = "sig-ecdsa"))]
Fabio Utzig1e48b912018-09-18 09:04:18 -03001163#[cfg(not(feature = "enc-rsa"))]
1164#[cfg(not(feature = "enc-kw"))]
David Brown2639e072017-10-11 11:18:44 -06001165fn make_tlv() -> TlvGen {
1166 TlvGen::new_hash_only()
1167}
1168
Fabio Utzig1e48b912018-09-18 09:04:18 -03001169#[cfg(feature = "enc-rsa")]
1170fn find_image(images: &[Option<Vec<u8>>; 2], slot: usize) -> &Vec<u8> {
1171 match &images[slot] {
1172 Some(image) => return image,
1173 None => panic!("Invalid image"),
1174 }
1175}
1176
1177#[cfg(feature = "enc-kw")]
1178fn find_image(images: &[Option<Vec<u8>>; 2], slot: usize) -> &Vec<u8> {
1179 match &images[slot] {
1180 Some(image) => return image,
1181 None => panic!("Invalid image"),
1182 }
1183}
1184
1185#[cfg(not(feature = "enc-rsa"))]
1186#[cfg(not(feature = "enc-kw"))]
1187fn find_image(images: &[Option<Vec<u8>>; 2], _slot: usize) -> &Vec<u8> {
1188 match &images[0] {
1189 Some(image) => return image,
1190 None => panic!("Invalid image"),
1191 }
1192}
1193
David Brown2639e072017-10-11 11:18:44 -06001194/// Verify that given image is present in the flash at the given offset.
Fabio Utzig1e48b912018-09-18 09:04:18 -03001195fn verify_image(flash: &Flash, slots: &[SlotInfo], slot: usize,
1196 images: &[Option<Vec<u8>>; 2]) -> bool {
1197 let image = find_image(images, slot);
1198 let buf = image.as_slice();
1199
David Brown2639e072017-10-11 11:18:44 -06001200 let mut copy = vec![0u8; buf.len()];
Fabio Utzig1e48b912018-09-18 09:04:18 -03001201 let offset = slots[slot].base_off;
David Brown2639e072017-10-11 11:18:44 -06001202 flash.read(offset, &mut copy).unwrap();
1203
1204 if buf != &copy[..] {
1205 for i in 0 .. buf.len() {
1206 if buf[i] != copy[i] {
Fabio Utzig1e48b912018-09-18 09:04:18 -03001207 info!("First failure for slot{} at {:#x} {:#x}!={:#x}",
1208 slot, offset + i, buf[i], copy[i]);
David Brown2639e072017-10-11 11:18:44 -06001209 break;
1210 }
1211 }
1212 false
1213 } else {
1214 true
1215 }
1216}
1217
1218#[cfg(feature = "overwrite-only")]
1219#[allow(unused_variables)]
1220// overwrite-only doesn't employ trailer management
1221fn verify_trailer(flash: &Flash, offset: usize,
Fabio Utzigea0290b2018-08-09 14:23:01 -03001222 magic: Option<u8>, image_ok: Option<u8>,
David Brown2639e072017-10-11 11:18:44 -06001223 copy_done: Option<u8>) -> bool {
1224 true
1225}
1226
1227#[cfg(not(feature = "overwrite-only"))]
1228fn verify_trailer(flash: &Flash, offset: usize,
Fabio Utzigea0290b2018-08-09 14:23:01 -03001229 magic: Option<u8>, image_ok: Option<u8>,
David Brown2639e072017-10-11 11:18:44 -06001230 copy_done: Option<u8>) -> bool {
1231 let mut copy = vec![0u8; c::boot_magic_sz() + c::boot_max_align() * 2];
1232 let mut failed = false;
Fabio Utzigea0290b2018-08-09 14:23:01 -03001233 let erased_val = flash.erased_val();
David Brown2639e072017-10-11 11:18:44 -06001234
1235 flash.read(offset, &mut copy).unwrap();
1236
1237 failed |= match magic {
1238 Some(v) => {
Fabio Utzigea0290b2018-08-09 14:23:01 -03001239 if v == 1 && &copy[16..] != MAGIC.unwrap() {
David Brown2639e072017-10-11 11:18:44 -06001240 warn!("\"magic\" mismatch at {:#x}", offset);
1241 true
Fabio Utzigea0290b2018-08-09 14:23:01 -03001242 } else if v == 3 {
1243 let expected = [erased_val; 16];
1244 if &copy[16..] != expected {
1245 warn!("\"magic\" mismatch at {:#x}", offset);
1246 true
1247 } else {
1248 false
1249 }
David Brown2639e072017-10-11 11:18:44 -06001250 } else {
1251 false
1252 }
1253 },
1254 None => false,
1255 };
1256
1257 failed |= match image_ok {
1258 Some(v) => {
Fabio Utzigea0290b2018-08-09 14:23:01 -03001259 if (v == 1 && copy[8] != v) || (v == 3 && copy[8] != erased_val) {
1260 warn!("\"image_ok\" mismatch at {:#x} v={} val={:#x}", offset, v, copy[8]);
David Brown2639e072017-10-11 11:18:44 -06001261 true
1262 } else {
1263 false
1264 }
1265 },
1266 None => false,
1267 };
1268
1269 failed |= match copy_done {
1270 Some(v) => {
Fabio Utzigea0290b2018-08-09 14:23:01 -03001271 if (v == 1 && copy[0] != v) || (v == 3 && copy[0] != erased_val) {
1272 warn!("\"copy_done\" mismatch at {:#x} v={} val={:#x}", offset, v, copy[0]);
David Brown2639e072017-10-11 11:18:44 -06001273 true
1274 } else {
1275 false
1276 }
1277 },
1278 None => false,
1279 };
1280
1281 !failed
1282}
1283
1284/// The image header
1285#[repr(C)]
1286pub struct ImageHeader {
1287 magic: u32,
Fabio Utzig1e48b912018-09-18 09:04:18 -03001288 load_addr: u32,
David Brown2639e072017-10-11 11:18:44 -06001289 hdr_size: u16,
Fabio Utzig1e48b912018-09-18 09:04:18 -03001290 _pad1: u16,
David Brown2639e072017-10-11 11:18:44 -06001291 img_size: u32,
1292 flags: u32,
1293 ver: ImageVersion,
Fabio Utzig1e48b912018-09-18 09:04:18 -03001294 _pad2: u32,
David Brown2639e072017-10-11 11:18:44 -06001295}
1296
1297impl AsRaw for ImageHeader {}
1298
1299#[repr(C)]
1300pub struct ImageVersion {
1301 major: u8,
1302 minor: u8,
1303 revision: u16,
1304 build_num: u32,
1305}
1306
David Brownd5e632c2017-10-19 10:49:46 -06001307#[derive(Clone)]
David Brown2639e072017-10-11 11:18:44 -06001308struct SlotInfo {
1309 base_off: usize,
1310 trailer_off: usize,
Fabio Utzig1e48b912018-09-18 09:04:18 -03001311 len: usize,
David Brown2639e072017-10-11 11:18:44 -06001312}
1313
David Brownf48b9502017-11-06 14:00:26 -07001314pub struct Images {
David Browndc9cba12017-11-06 13:31:42 -07001315 flash: SimFlash,
David Brown3f687dc2017-11-06 13:41:18 -07001316 areadesc: AreaDesc,
Fabio Utzig1e48b912018-09-18 09:04:18 -03001317 slots: [SlotInfo; 2],
1318 primaries: [Option<Vec<u8>>; 2],
1319 upgrades: [Option<Vec<u8>>; 2],
David Brownc49811e2017-11-06 14:20:45 -07001320 total_count: Option<i32>,
David Brown541860c2017-11-06 11:25:42 -07001321 align: u8,
Fabio Utzigea0290b2018-08-09 14:23:01 -03001322 erased_val: u8,
David Brown2639e072017-10-11 11:18:44 -06001323}
1324
Fabio Utzigea0290b2018-08-09 14:23:01 -03001325const MAGIC: Option<&[u8]> = Some(&[0x77, 0xc2, 0x95, 0xf3,
1326 0x60, 0xd2, 0xef, 0x7f,
1327 0x35, 0x52, 0x50, 0x0f,
1328 0x2c, 0xb6, 0x79, 0x80]);
David Brown2639e072017-10-11 11:18:44 -06001329
Fabio Utzigea0290b2018-08-09 14:23:01 -03001330// Replicates defines found in bootutil.h
1331const BOOT_MAGIC_GOOD: Option<u8> = Some(1);
1332const BOOT_MAGIC_UNSET: Option<u8> = Some(3);
1333
1334const BOOT_FLAG_SET: Option<u8> = Some(1);
1335const BOOT_FLAG_UNSET: Option<u8> = Some(3);
David Brown2639e072017-10-11 11:18:44 -06001336
1337/// Write out the magic so that the loader tries doing an upgrade.
1338fn mark_upgrade(flash: &mut Flash, slot: &SlotInfo) {
1339 let offset = slot.trailer_off + c::boot_max_align() * 2;
Fabio Utzigea0290b2018-08-09 14:23:01 -03001340 flash.write(offset, MAGIC.unwrap()).unwrap();
David Brown2639e072017-10-11 11:18:44 -06001341}
1342
1343/// Writes the image_ok flag which, guess what, tells the bootloader
1344/// the this image is ok (not a test, and no revert is to be performed).
Fabio Utzigea0290b2018-08-09 14:23:01 -03001345fn mark_permanent_upgrade(flash: &mut Flash, slot: &SlotInfo, align: u8, erased_val: u8) {
1346 let mut ok = [erased_val; 8];
1347 ok[0] = 1u8;
David Brown2639e072017-10-11 11:18:44 -06001348 let off = slot.trailer_off + c::boot_max_align();
David Brown541860c2017-11-06 11:25:42 -07001349 flash.write(off, &ok[..align as usize]).unwrap();
David Brown2639e072017-10-11 11:18:44 -06001350}
1351
1352// Drop some pseudo-random gibberish onto the data.
1353fn splat(data: &mut [u8], seed: usize) {
1354 let seed_block = [0x135782ea, 0x92184728, data.len() as u32, seed as u32];
1355 let mut rng: XorShiftRng = SeedableRng::from_seed(seed_block);
1356 rng.fill_bytes(data);
1357}
1358
1359/// Return a read-only view into the raw bytes of this object
1360trait AsRaw : Sized {
1361 fn as_raw<'a>(&'a self) -> &'a [u8] {
1362 unsafe { slice::from_raw_parts(self as *const _ as *const u8,
1363 mem::size_of::<Self>()) }
1364 }
1365}
1366
1367fn show_sizes() {
1368 // This isn't panic safe.
David Brown2639e072017-10-11 11:18:44 -06001369 for min in &[1, 2, 4, 8] {
David Brown541860c2017-11-06 11:25:42 -07001370 let msize = c::boot_trailer_sz(*min);
David Brown2639e072017-10-11 11:18:44 -06001371 println!("{:2}: {} (0x{:x})", min, msize, msize);
1372 }
David Brown2639e072017-10-11 11:18:44 -06001373}