blob: e7ad671fa388862af340fdfdb8ca7de304abc512 [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")]
698 fn status_sz(&self) -> usize {
699 self.trailer_sz() - (16 + 24 + 32)
700 }
701
702 #[cfg(feature = "enc-kw")]
703 fn status_sz(&self) -> usize {
704 self.trailer_sz() - (16 + 24 + 32)
705 }
706
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200707 /// This test runs a simple upgrade with no fails in the images, but
708 /// allowing for fails in the status area. This should run to the end
709 /// and warn that write fails were detected...
710 #[cfg(not(feature = "validate-slot0"))]
711 pub fn run_with_status_fails_complete(&self) -> bool { false }
712
713 #[cfg(feature = "validate-slot0")]
714 pub fn run_with_status_fails_complete(&self) -> bool {
715 let mut fl = self.flash.clone();
716 let mut fails = 0;
717
718 info!("Try swap with status fails");
719
Fabio Utzig1e48b912018-09-18 09:04:18 -0300720 mark_permanent_upgrade(&mut fl, &self.slots[1], self.align, self.erased_val);
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200721
Fabio Utzig1e48b912018-09-18 09:04:18 -0300722 let status_off = self.slots[1].base_off - self.trailer_sz();
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200723
724 // Always fail writes to status area...
725 let _ = fl.add_bad_region(status_off, self.status_sz(), 1.0);
726
727 let (result, asserts) = c::boot_go(&mut fl, &self.areadesc, None, self.align, true);
728 if result != 0 {
729 warn!("Failed!");
730 fails += 1;
731 }
732
733 // Failed writes to the marked "bad" region don't assert anymore.
734 // Any detected assert() is happening in another part of the code.
735 if asserts != 0 {
736 warn!("At least one assert() was called");
737 fails += 1;
738 }
739
Fabio Utzig1e48b912018-09-18 09:04:18 -0300740 if !verify_trailer(&fl, self.slots[0].trailer_off, BOOT_MAGIC_GOOD,
Fabio Utzigea0290b2018-08-09 14:23:01 -0300741 BOOT_FLAG_SET, BOOT_FLAG_SET) {
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200742 warn!("Mismatched trailer for Slot 0");
743 fails += 1;
744 }
745
Fabio Utzig1e48b912018-09-18 09:04:18 -0300746 if !verify_image(&fl, &self.slots, 0, &self.upgrades) {
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200747 warn!("Failed image verification");
748 fails += 1;
749 }
750
751 info!("validate slot0 enabled; re-run of boot_go should just work");
752 let (result, _) = c::boot_go(&mut fl, &self.areadesc, None, self.align, false);
753 if result != 0 {
754 warn!("Failed!");
755 fails += 1;
756 }
757
758 if fails > 0 {
759 error!("Error running upgrade with status write fails");
760 }
761
762 fails > 0
763 }
764
765 /// This test runs a simple upgrade with no fails in the images, but
766 /// allowing for fails in the status area. This should run to the end
767 /// and warn that write fails were detected...
768 #[cfg(feature = "validate-slot0")]
769 pub fn run_with_status_fails_with_reset(&self) -> bool {
770 let mut fl = self.flash.clone();
771 let mut fails = 0;
772 let mut count = self.total_count.unwrap() / 2;
773
774 //info!("count={}\n", count);
775
776 info!("Try interrupted swap with status fails");
777
Fabio Utzig1e48b912018-09-18 09:04:18 -0300778 mark_permanent_upgrade(&mut fl, &self.slots[1], self.align, self.erased_val);
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200779
Fabio Utzig1e48b912018-09-18 09:04:18 -0300780 let status_off = self.slots[1].base_off - self.trailer_sz();
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200781
782 // Mark the status area as a bad area
783 let _ = fl.add_bad_region(status_off, self.status_sz(), 0.5);
784
785 // Should not fail, writing to bad regions does not assert
786 let (_, asserts) = c::boot_go(&mut fl, &self.areadesc, Some(&mut count), self.align, true);
787 if asserts != 0 {
788 warn!("At least one assert() was called");
789 fails += 1;
790 }
791
792 fl.reset_bad_regions();
793
794 // Disabling write verification the only assert triggered by
795 // boot_go should be checking for integrity of status bytes.
796 fl.set_verify_writes(false);
797
798 info!("Resuming an interrupted swap operation");
799 let (_, asserts) = c::boot_go(&mut fl, &self.areadesc, None, self.align, true);
800
801 // This might throw no asserts, for large sector devices, where
802 // a single failure writing is indistinguishable from no failure,
803 // or throw a single assert for small sector devices that fail
804 // multiple times...
805 if asserts > 1 {
806 warn!("Expected single assert validating slot0, more detected {}", asserts);
807 fails += 1;
808 }
809
810 if fails > 0 {
811 error!("Error running upgrade with status write fails");
812 }
813
814 fails > 0
815 }
816
817 #[cfg(not(feature = "validate-slot0"))]
818 #[cfg(not(feature = "overwrite-only"))]
819 pub fn run_with_status_fails_with_reset(&self) -> bool {
820 let mut fl = self.flash.clone();
821 let mut fails = 0;
822
823 info!("Try interrupted swap with status fails");
824
Fabio Utzig1e48b912018-09-18 09:04:18 -0300825 mark_permanent_upgrade(&mut fl, &self.slots[1], self.align, self.erased_val);
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200826
Fabio Utzig1e48b912018-09-18 09:04:18 -0300827 let status_off = self.slots[1].base_off - self.trailer_sz();
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200828
829 // Mark the status area as a bad area
830 let _ = fl.add_bad_region(status_off, self.status_sz(), 1.0);
831
832 // This is expected to fail while writing to bad regions...
833 let (_, asserts) = c::boot_go(&mut fl, &self.areadesc, None, self.align, true);
834 if asserts == 0 {
835 warn!("No assert() detected");
836 fails += 1;
837 }
838
839 fails > 0
840 }
841
842 #[cfg(feature = "overwrite-only")]
843 pub fn run_with_status_fails_with_reset(&self) -> bool {
844 false
845 }
David Brown2639e072017-10-11 11:18:44 -0600846}
847
848/// Test a boot, optionally stopping after 'n' flash options. Returns a count
849/// of the number of flash operations done total.
David Brown3f687dc2017-11-06 13:41:18 -0700850fn try_upgrade(flash: &SimFlash, images: &Images,
David Brown2639e072017-10-11 11:18:44 -0600851 stop: Option<i32>) -> (SimFlash, i32) {
852 // Clone the flash to have a new copy.
853 let mut fl = flash.clone();
854
Fabio Utzig1e48b912018-09-18 09:04:18 -0300855 mark_permanent_upgrade(&mut fl, &images.slots[1], images.align, images.erased_val);
David Brown2639e072017-10-11 11:18:44 -0600856
David Brownee61c832017-11-06 11:13:25 -0700857 let mut counter = stop.unwrap_or(0);
858
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200859 let (first_interrupted, count) = match c::boot_go(&mut fl, &images.areadesc, Some(&mut counter), images.align, false) {
860 (-0x13579, _) => (true, stop.unwrap()),
861 (0, _) => (false, -counter),
862 (x, _) => panic!("Unknown return: {}", x),
David Brown2639e072017-10-11 11:18:44 -0600863 };
David Brown2639e072017-10-11 11:18:44 -0600864
David Brownee61c832017-11-06 11:13:25 -0700865 counter = 0;
David Brown2639e072017-10-11 11:18:44 -0600866 if first_interrupted {
867 // fl.dump();
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200868 match c::boot_go(&mut fl, &images.areadesc, Some(&mut counter), images.align, false) {
869 (-0x13579, _) => panic!("Shouldn't stop again"),
870 (0, _) => (),
871 (x, _) => panic!("Unknown return: {}", x),
David Brown2639e072017-10-11 11:18:44 -0600872 }
873 }
874
David Brownee61c832017-11-06 11:13:25 -0700875 (fl, count - counter)
David Brown2639e072017-10-11 11:18:44 -0600876}
877
878#[cfg(not(feature = "overwrite-only"))]
David Brown541860c2017-11-06 11:25:42 -0700879fn try_revert(flash: &SimFlash, areadesc: &AreaDesc, count: usize, align: u8) -> SimFlash {
David Brown2639e072017-10-11 11:18:44 -0600880 let mut fl = flash.clone();
David Brown2639e072017-10-11 11:18:44 -0600881
882 // fl.write_file("image0.bin").unwrap();
883 for i in 0 .. count {
884 info!("Running boot pass {}", i + 1);
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200885 assert_eq!(c::boot_go(&mut fl, &areadesc, None, align, false), (0, 0));
David Brown2639e072017-10-11 11:18:44 -0600886 }
887 fl
888}
889
890#[cfg(not(feature = "overwrite-only"))]
David Brown3f687dc2017-11-06 13:41:18 -0700891fn try_revert_with_fail_at(flash: &SimFlash, images: &Images,
David Brown2639e072017-10-11 11:18:44 -0600892 stop: i32) -> bool {
893 let mut fl = flash.clone();
David Brown2639e072017-10-11 11:18:44 -0600894 let mut fails = 0;
895
David Brownee61c832017-11-06 11:13:25 -0700896 let mut counter = stop;
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200897 let (x, _) = c::boot_go(&mut fl, &images.areadesc, Some(&mut counter), images.align, false);
David Brown2639e072017-10-11 11:18:44 -0600898 if x != -0x13579 {
899 warn!("Should have stopped at interruption point");
900 fails += 1;
901 }
902
Fabio Utzig1e48b912018-09-18 09:04:18 -0300903 if !verify_trailer(&fl, images.slots[0].trailer_off, None, None, BOOT_FLAG_UNSET) {
David Brown2639e072017-10-11 11:18:44 -0600904 warn!("copy_done should be unset");
905 fails += 1;
906 }
907
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200908 let (x, _) = c::boot_go(&mut fl, &images.areadesc, None, images.align, false);
David Brown2639e072017-10-11 11:18:44 -0600909 if x != 0 {
910 warn!("Should have finished upgrade");
911 fails += 1;
912 }
913
Fabio Utzig1e48b912018-09-18 09:04:18 -0300914 if !verify_image(&fl, &images.slots, 0, &images.upgrades) {
David Brown2639e072017-10-11 11:18:44 -0600915 warn!("Image in slot 0 before revert is invalid at stop={}", stop);
916 fails += 1;
917 }
Fabio Utzig1e48b912018-09-18 09:04:18 -0300918 if !verify_image(&fl, &images.slots, 1, &images.primaries) {
David Brown2639e072017-10-11 11:18:44 -0600919 warn!("Image in slot 1 before revert is invalid at stop={}", stop);
920 fails += 1;
921 }
Fabio Utzig1e48b912018-09-18 09:04:18 -0300922 if !verify_trailer(&fl, images.slots[0].trailer_off, BOOT_MAGIC_GOOD,
Fabio Utzigea0290b2018-08-09 14:23:01 -0300923 BOOT_FLAG_UNSET, BOOT_FLAG_SET) {
David Brown2639e072017-10-11 11:18:44 -0600924 warn!("Mismatched trailer for Slot 0 before revert");
925 fails += 1;
926 }
Fabio Utzig1e48b912018-09-18 09:04:18 -0300927 if !verify_trailer(&fl, images.slots[1].trailer_off, BOOT_MAGIC_UNSET,
Fabio Utzigea0290b2018-08-09 14:23:01 -0300928 BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
David Brown2639e072017-10-11 11:18:44 -0600929 warn!("Mismatched trailer for Slot 1 before revert");
930 fails += 1;
931 }
932
933 // Do Revert
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200934 let (x, _) = c::boot_go(&mut fl, &images.areadesc, None, images.align, false);
David Brown2639e072017-10-11 11:18:44 -0600935 if x != 0 {
936 warn!("Should have finished a revert");
937 fails += 1;
938 }
939
Fabio Utzig1e48b912018-09-18 09:04:18 -0300940 if !verify_image(&fl, &images.slots, 0, &images.primaries) {
David Brown2639e072017-10-11 11:18:44 -0600941 warn!("Image in slot 0 after revert is invalid at stop={}", stop);
942 fails += 1;
943 }
Fabio Utzig1e48b912018-09-18 09:04:18 -0300944 if !verify_image(&fl, &images.slots, 1, &images.upgrades) {
David Brown2639e072017-10-11 11:18:44 -0600945 warn!("Image in slot 1 after revert is invalid at stop={}", stop);
946 fails += 1;
947 }
Fabio Utzig1e48b912018-09-18 09:04:18 -0300948 if !verify_trailer(&fl, images.slots[0].trailer_off, BOOT_MAGIC_GOOD,
Fabio Utzigea0290b2018-08-09 14:23:01 -0300949 BOOT_FLAG_SET, BOOT_FLAG_SET) {
David Brown2639e072017-10-11 11:18:44 -0600950 warn!("Mismatched trailer for Slot 1 after revert");
951 fails += 1;
952 }
Fabio Utzig1e48b912018-09-18 09:04:18 -0300953 if !verify_trailer(&fl, images.slots[1].trailer_off, BOOT_MAGIC_UNSET,
Fabio Utzigea0290b2018-08-09 14:23:01 -0300954 BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
David Brown2639e072017-10-11 11:18:44 -0600955 warn!("Mismatched trailer for Slot 1 after revert");
956 fails += 1;
957 }
958
959 fails > 0
960}
961
David Brown3f687dc2017-11-06 13:41:18 -0700962fn try_random_fails(flash: &SimFlash, images: &Images,
David Brown2639e072017-10-11 11:18:44 -0600963 total_ops: i32, count: usize) -> (SimFlash, Vec<i32>) {
964 let mut fl = flash.clone();
965
Fabio Utzig1e48b912018-09-18 09:04:18 -0300966 mark_permanent_upgrade(&mut fl, &images.slots[1], images.align, images.erased_val);
David Brown2639e072017-10-11 11:18:44 -0600967
968 let mut rng = rand::thread_rng();
969 let mut resets = vec![0i32; count];
970 let mut remaining_ops = total_ops;
971 for i in 0 .. count {
972 let ops = Range::new(1, remaining_ops / 2);
973 let reset_counter = ops.ind_sample(&mut rng);
David Brownee61c832017-11-06 11:13:25 -0700974 let mut counter = reset_counter;
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200975 match c::boot_go(&mut fl, &images.areadesc, Some(&mut counter), images.align, false) {
976 (0, _) | (-0x13579, _) => (),
977 (x, _) => panic!("Unknown return: {}", x),
David Brown2639e072017-10-11 11:18:44 -0600978 }
979 remaining_ops -= reset_counter;
980 resets[i] = reset_counter;
981 }
982
Fabio Utzig9b0ee902017-11-23 19:49:00 -0200983 match c::boot_go(&mut fl, &images.areadesc, None, images.align, false) {
984 (-0x13579, _) => panic!("Should not be have been interrupted!"),
985 (0, _) => (),
986 (x, _) => panic!("Unknown return: {}", x),
David Brown2639e072017-10-11 11:18:44 -0600987 }
988
989 (fl, resets)
990}
991
992/// Show the flash layout.
993#[allow(dead_code)]
994fn show_flash(flash: &Flash) {
995 println!("---- Flash configuration ----");
996 for sector in flash.sector_iter() {
997 println!(" {:3}: 0x{:08x}, 0x{:08x}",
998 sector.num, sector.base, sector.size);
999 }
1000 println!("");
1001}
1002
1003/// Install a "program" into the given image. This fakes the image header, or at least all of the
1004/// fields used by the given code. Returns a copy of the image that was written.
Fabio Utzig1e48b912018-09-18 09:04:18 -03001005fn install_image(flash: &mut Flash, slots: &[SlotInfo], slot: usize, len: usize,
1006 bad_sig: bool) -> [Option<Vec<u8>>; 2] {
1007 let offset = slots[slot].base_off;
1008 let slot_len = slots[slot].len;
David Brown2639e072017-10-11 11:18:44 -06001009
1010 let mut tlv = make_tlv();
1011
1012 // Generate a boot header. Note that the size doesn't include the header.
1013 let header = ImageHeader {
1014 magic: 0x96f3b83d,
Fabio Utzig1e48b912018-09-18 09:04:18 -03001015 load_addr: 0,
David Brown2639e072017-10-11 11:18:44 -06001016 hdr_size: 32,
Fabio Utzig1e48b912018-09-18 09:04:18 -03001017 _pad1: 0,
David Brown2639e072017-10-11 11:18:44 -06001018 img_size: len as u32,
1019 flags: tlv.get_flags(),
1020 ver: ImageVersion {
1021 major: (offset / (128 * 1024)) as u8,
1022 minor: 0,
1023 revision: 1,
1024 build_num: offset as u32,
1025 },
Fabio Utzig1e48b912018-09-18 09:04:18 -03001026 _pad2: 0,
David Brown2639e072017-10-11 11:18:44 -06001027 };
1028
1029 let b_header = header.as_raw();
1030 tlv.add_bytes(&b_header);
1031 /*
1032 let b_header = unsafe { slice::from_raw_parts(&header as *const _ as *const u8,
1033 mem::size_of::<ImageHeader>()) };
1034 */
1035 assert_eq!(b_header.len(), 32);
David Brown2639e072017-10-11 11:18:44 -06001036
1037 // The core of the image itself is just pseudorandom data.
Fabio Utzig1e48b912018-09-18 09:04:18 -03001038 let mut b_img = vec![0; len];
1039 splat(&mut b_img, offset);
David Brown2639e072017-10-11 11:18:44 -06001040
Fabio Utzig1e48b912018-09-18 09:04:18 -03001041 // TLV signatures work over plain image
1042 tlv.add_bytes(&b_img);
1043
1044 // Generate encrypted images
1045 let flag = TlvFlags::ENCRYPTED as u32;
1046 let is_encrypted = (tlv.get_flags() & flag) == flag;
1047 let mut b_encimg = vec![];
1048 if is_encrypted {
1049 let key = GenericArray::from_slice(AES_SEC_KEY);
1050 let nonce = GenericArray::from_slice(&[0; 16]);
1051 let mut cipher = Aes128Ctr::new(&key, &nonce);
1052 b_encimg = b_img.clone();
1053 cipher.apply_keystream(&mut b_encimg);
David Brown2639e072017-10-11 11:18:44 -06001054 }
1055
Fabio Utzig1e48b912018-09-18 09:04:18 -03001056 // Build the TLV itself.
1057 let mut b_tlv = if bad_sig {
1058 let good_sig = &mut tlv.make_tlv();
1059 vec![0; good_sig.len()]
1060 } else {
1061 tlv.make_tlv()
1062 };
1063
David Brown2639e072017-10-11 11:18:44 -06001064 // Pad the block to a flash alignment (8 bytes).
Fabio Utzig1e48b912018-09-18 09:04:18 -03001065 while b_tlv.len() % 8 != 0 {
1066 //FIXME: should be erase_val?
1067 b_tlv.push(0xFF);
David Brown2639e072017-10-11 11:18:44 -06001068 }
1069
Fabio Utzig1e48b912018-09-18 09:04:18 -03001070 let mut buf = vec![];
1071 buf.append(&mut b_header.to_vec());
1072 buf.append(&mut b_img);
1073 buf.append(&mut b_tlv.clone());
David Brown2639e072017-10-11 11:18:44 -06001074
Fabio Utzig1e48b912018-09-18 09:04:18 -03001075 let mut encbuf = vec![];
1076 if is_encrypted {
1077 encbuf.append(&mut b_header.to_vec());
1078 encbuf.append(&mut b_encimg);
1079 encbuf.append(&mut b_tlv);
1080 }
David Brown2639e072017-10-11 11:18:44 -06001081
Fabio Utzig1e48b912018-09-18 09:04:18 -03001082 let result: [Option<Vec<u8>>; 2];
1083
1084 // Since images are always non-encrypted in slot0, we first write an
1085 // encrypted image, re-read to use for verification, erase + flash
1086 // un-encrypted. In slot1 the image is written un-encrypted, and if
1087 // encryption is requested, it follows an erase + flash encrypted.
1088
1089 if slot == 0 {
1090 let enc_copy: Option<Vec<u8>>;
1091
1092 if is_encrypted {
1093 flash.write(offset, &encbuf).unwrap();
1094
1095 let mut enc = vec![0u8; encbuf.len()];
1096 flash.read(offset, &mut enc).unwrap();
1097
1098 enc_copy = Some(enc);
1099
1100 flash.erase(offset, slot_len).unwrap();
1101 } else {
1102 enc_copy = None;
1103 }
1104
1105 flash.write(offset, &buf).unwrap();
1106
1107 let mut copy = vec![0u8; buf.len()];
1108 flash.read(offset, &mut copy).unwrap();
1109
1110 result = [Some(copy), enc_copy];
1111 } else {
1112 flash.write(offset, &buf).unwrap();
1113
1114 let mut copy = vec![0u8; buf.len()];
1115 flash.read(offset, &mut copy).unwrap();
1116
1117 let enc_copy: Option<Vec<u8>>;
1118
1119 if is_encrypted {
1120 flash.erase(offset, slot_len).unwrap();
1121
1122 flash.write(offset, &encbuf).unwrap();
1123
1124 let mut enc = vec![0u8; encbuf.len()];
1125 flash.read(offset, &mut enc).unwrap();
1126
1127 enc_copy = Some(enc);
1128 } else {
1129 enc_copy = None;
1130 }
1131
1132 result = [Some(copy), enc_copy];
1133 }
1134
1135 result
David Brown2639e072017-10-11 11:18:44 -06001136}
1137
1138// The TLV in use depends on what kind of signature we are verifying.
1139#[cfg(feature = "sig-rsa")]
1140fn make_tlv() -> TlvGen {
1141 TlvGen::new_rsa_pss()
1142}
1143
Fabio Utzig80fde2f2017-12-05 09:25:31 -02001144#[cfg(feature = "sig-ecdsa")]
1145fn make_tlv() -> TlvGen {
1146 TlvGen::new_ecdsa()
1147}
1148
Fabio Utzig1e48b912018-09-18 09:04:18 -03001149#[cfg(feature = "enc-rsa")]
1150fn make_tlv() -> TlvGen {
1151 TlvGen::new_enc_rsa()
1152}
1153
1154#[cfg(feature = "enc-kw")]
1155fn make_tlv() -> TlvGen {
1156 TlvGen::new_enc_kw()
1157}
1158
David Brown2639e072017-10-11 11:18:44 -06001159#[cfg(not(feature = "sig-rsa"))]
Fabio Utzig80fde2f2017-12-05 09:25:31 -02001160#[cfg(not(feature = "sig-ecdsa"))]
Fabio Utzig1e48b912018-09-18 09:04:18 -03001161#[cfg(not(feature = "enc-rsa"))]
1162#[cfg(not(feature = "enc-kw"))]
David Brown2639e072017-10-11 11:18:44 -06001163fn make_tlv() -> TlvGen {
1164 TlvGen::new_hash_only()
1165}
1166
Fabio Utzig1e48b912018-09-18 09:04:18 -03001167#[cfg(feature = "enc-rsa")]
1168fn find_image(images: &[Option<Vec<u8>>; 2], slot: usize) -> &Vec<u8> {
1169 match &images[slot] {
1170 Some(image) => return image,
1171 None => panic!("Invalid image"),
1172 }
1173}
1174
1175#[cfg(feature = "enc-kw")]
1176fn find_image(images: &[Option<Vec<u8>>; 2], slot: usize) -> &Vec<u8> {
1177 match &images[slot] {
1178 Some(image) => return image,
1179 None => panic!("Invalid image"),
1180 }
1181}
1182
1183#[cfg(not(feature = "enc-rsa"))]
1184#[cfg(not(feature = "enc-kw"))]
1185fn find_image(images: &[Option<Vec<u8>>; 2], _slot: usize) -> &Vec<u8> {
1186 match &images[0] {
1187 Some(image) => return image,
1188 None => panic!("Invalid image"),
1189 }
1190}
1191
David Brown2639e072017-10-11 11:18:44 -06001192/// Verify that given image is present in the flash at the given offset.
Fabio Utzig1e48b912018-09-18 09:04:18 -03001193fn verify_image(flash: &Flash, slots: &[SlotInfo], slot: usize,
1194 images: &[Option<Vec<u8>>; 2]) -> bool {
1195 let image = find_image(images, slot);
1196 let buf = image.as_slice();
1197
David Brown2639e072017-10-11 11:18:44 -06001198 let mut copy = vec![0u8; buf.len()];
Fabio Utzig1e48b912018-09-18 09:04:18 -03001199 let offset = slots[slot].base_off;
David Brown2639e072017-10-11 11:18:44 -06001200 flash.read(offset, &mut copy).unwrap();
1201
1202 if buf != &copy[..] {
1203 for i in 0 .. buf.len() {
1204 if buf[i] != copy[i] {
Fabio Utzig1e48b912018-09-18 09:04:18 -03001205 info!("First failure for slot{} at {:#x} {:#x}!={:#x}",
1206 slot, offset + i, buf[i], copy[i]);
David Brown2639e072017-10-11 11:18:44 -06001207 break;
1208 }
1209 }
1210 false
1211 } else {
1212 true
1213 }
1214}
1215
1216#[cfg(feature = "overwrite-only")]
1217#[allow(unused_variables)]
1218// overwrite-only doesn't employ trailer management
1219fn verify_trailer(flash: &Flash, offset: usize,
Fabio Utzigea0290b2018-08-09 14:23:01 -03001220 magic: Option<u8>, image_ok: Option<u8>,
David Brown2639e072017-10-11 11:18:44 -06001221 copy_done: Option<u8>) -> bool {
1222 true
1223}
1224
1225#[cfg(not(feature = "overwrite-only"))]
1226fn verify_trailer(flash: &Flash, offset: usize,
Fabio Utzigea0290b2018-08-09 14:23:01 -03001227 magic: Option<u8>, image_ok: Option<u8>,
David Brown2639e072017-10-11 11:18:44 -06001228 copy_done: Option<u8>) -> bool {
1229 let mut copy = vec![0u8; c::boot_magic_sz() + c::boot_max_align() * 2];
1230 let mut failed = false;
Fabio Utzigea0290b2018-08-09 14:23:01 -03001231 let erased_val = flash.erased_val();
David Brown2639e072017-10-11 11:18:44 -06001232
1233 flash.read(offset, &mut copy).unwrap();
1234
1235 failed |= match magic {
1236 Some(v) => {
Fabio Utzigea0290b2018-08-09 14:23:01 -03001237 if v == 1 && &copy[16..] != MAGIC.unwrap() {
David Brown2639e072017-10-11 11:18:44 -06001238 warn!("\"magic\" mismatch at {:#x}", offset);
1239 true
Fabio Utzigea0290b2018-08-09 14:23:01 -03001240 } else if v == 3 {
1241 let expected = [erased_val; 16];
1242 if &copy[16..] != expected {
1243 warn!("\"magic\" mismatch at {:#x}", offset);
1244 true
1245 } else {
1246 false
1247 }
David Brown2639e072017-10-11 11:18:44 -06001248 } else {
1249 false
1250 }
1251 },
1252 None => false,
1253 };
1254
1255 failed |= match image_ok {
1256 Some(v) => {
Fabio Utzigea0290b2018-08-09 14:23:01 -03001257 if (v == 1 && copy[8] != v) || (v == 3 && copy[8] != erased_val) {
1258 warn!("\"image_ok\" mismatch at {:#x} v={} val={:#x}", offset, v, copy[8]);
David Brown2639e072017-10-11 11:18:44 -06001259 true
1260 } else {
1261 false
1262 }
1263 },
1264 None => false,
1265 };
1266
1267 failed |= match copy_done {
1268 Some(v) => {
Fabio Utzigea0290b2018-08-09 14:23:01 -03001269 if (v == 1 && copy[0] != v) || (v == 3 && copy[0] != erased_val) {
1270 warn!("\"copy_done\" mismatch at {:#x} v={} val={:#x}", offset, v, copy[0]);
David Brown2639e072017-10-11 11:18:44 -06001271 true
1272 } else {
1273 false
1274 }
1275 },
1276 None => false,
1277 };
1278
1279 !failed
1280}
1281
1282/// The image header
1283#[repr(C)]
1284pub struct ImageHeader {
1285 magic: u32,
Fabio Utzig1e48b912018-09-18 09:04:18 -03001286 load_addr: u32,
David Brown2639e072017-10-11 11:18:44 -06001287 hdr_size: u16,
Fabio Utzig1e48b912018-09-18 09:04:18 -03001288 _pad1: u16,
David Brown2639e072017-10-11 11:18:44 -06001289 img_size: u32,
1290 flags: u32,
1291 ver: ImageVersion,
Fabio Utzig1e48b912018-09-18 09:04:18 -03001292 _pad2: u32,
David Brown2639e072017-10-11 11:18:44 -06001293}
1294
1295impl AsRaw for ImageHeader {}
1296
1297#[repr(C)]
1298pub struct ImageVersion {
1299 major: u8,
1300 minor: u8,
1301 revision: u16,
1302 build_num: u32,
1303}
1304
David Brownd5e632c2017-10-19 10:49:46 -06001305#[derive(Clone)]
David Brown2639e072017-10-11 11:18:44 -06001306struct SlotInfo {
1307 base_off: usize,
1308 trailer_off: usize,
Fabio Utzig1e48b912018-09-18 09:04:18 -03001309 len: usize,
David Brown2639e072017-10-11 11:18:44 -06001310}
1311
David Brownf48b9502017-11-06 14:00:26 -07001312pub struct Images {
David Browndc9cba12017-11-06 13:31:42 -07001313 flash: SimFlash,
David Brown3f687dc2017-11-06 13:41:18 -07001314 areadesc: AreaDesc,
Fabio Utzig1e48b912018-09-18 09:04:18 -03001315 slots: [SlotInfo; 2],
1316 primaries: [Option<Vec<u8>>; 2],
1317 upgrades: [Option<Vec<u8>>; 2],
David Brownc49811e2017-11-06 14:20:45 -07001318 total_count: Option<i32>,
David Brown541860c2017-11-06 11:25:42 -07001319 align: u8,
Fabio Utzigea0290b2018-08-09 14:23:01 -03001320 erased_val: u8,
David Brown2639e072017-10-11 11:18:44 -06001321}
1322
Fabio Utzigea0290b2018-08-09 14:23:01 -03001323const MAGIC: Option<&[u8]> = Some(&[0x77, 0xc2, 0x95, 0xf3,
1324 0x60, 0xd2, 0xef, 0x7f,
1325 0x35, 0x52, 0x50, 0x0f,
1326 0x2c, 0xb6, 0x79, 0x80]);
David Brown2639e072017-10-11 11:18:44 -06001327
Fabio Utzigea0290b2018-08-09 14:23:01 -03001328// Replicates defines found in bootutil.h
1329const BOOT_MAGIC_GOOD: Option<u8> = Some(1);
1330const BOOT_MAGIC_UNSET: Option<u8> = Some(3);
1331
1332const BOOT_FLAG_SET: Option<u8> = Some(1);
1333const BOOT_FLAG_UNSET: Option<u8> = Some(3);
David Brown2639e072017-10-11 11:18:44 -06001334
1335/// Write out the magic so that the loader tries doing an upgrade.
1336fn mark_upgrade(flash: &mut Flash, slot: &SlotInfo) {
1337 let offset = slot.trailer_off + c::boot_max_align() * 2;
Fabio Utzigea0290b2018-08-09 14:23:01 -03001338 flash.write(offset, MAGIC.unwrap()).unwrap();
David Brown2639e072017-10-11 11:18:44 -06001339}
1340
1341/// Writes the image_ok flag which, guess what, tells the bootloader
1342/// the this image is ok (not a test, and no revert is to be performed).
Fabio Utzigea0290b2018-08-09 14:23:01 -03001343fn mark_permanent_upgrade(flash: &mut Flash, slot: &SlotInfo, align: u8, erased_val: u8) {
1344 let mut ok = [erased_val; 8];
1345 ok[0] = 1u8;
David Brown2639e072017-10-11 11:18:44 -06001346 let off = slot.trailer_off + c::boot_max_align();
David Brown541860c2017-11-06 11:25:42 -07001347 flash.write(off, &ok[..align as usize]).unwrap();
David Brown2639e072017-10-11 11:18:44 -06001348}
1349
1350// Drop some pseudo-random gibberish onto the data.
1351fn splat(data: &mut [u8], seed: usize) {
1352 let seed_block = [0x135782ea, 0x92184728, data.len() as u32, seed as u32];
1353 let mut rng: XorShiftRng = SeedableRng::from_seed(seed_block);
1354 rng.fill_bytes(data);
1355}
1356
1357/// Return a read-only view into the raw bytes of this object
1358trait AsRaw : Sized {
1359 fn as_raw<'a>(&'a self) -> &'a [u8] {
1360 unsafe { slice::from_raw_parts(self as *const _ as *const u8,
1361 mem::size_of::<Self>()) }
1362 }
1363}
1364
1365fn show_sizes() {
1366 // This isn't panic safe.
David Brown2639e072017-10-11 11:18:44 -06001367 for min in &[1, 2, 4, 8] {
David Brown541860c2017-11-06 11:25:42 -07001368 let msize = c::boot_trailer_sz(*min);
David Brown2639e072017-10-11 11:18:44 -06001369 println!("{:2}: {} (0x{:x})", min, msize, msize);
1370 }
David Brown2639e072017-10-11 11:18:44 -06001371}