blob: 3404dddf2fa00730686f51e52d1c2e391e755527 [file] [log] [blame]
David Brown5c9e0f12019-01-09 16:34:33 -07001use log::{info, warn, error};
2use rand::{
3 distributions::{IndependentSample, Range},
4 Rng, SeedableRng, XorShiftRng,
5};
6use std::{
7 mem,
8 slice,
9};
10use aes_ctr::{
11 Aes128Ctr,
12 stream_cipher::{
13 generic_array::GenericArray,
14 NewFixStreamCipher,
15 StreamCipherCore,
16 },
17};
18
19use simflash::{Flash, SimFlashMap};
20use mcuboot_sys::{c, AreaDesc};
21use crate::caps::Caps;
David Brown43643dd2019-01-11 15:43:28 -070022use crate::tlv::{ManifestGen, TlvGen, TlvFlags, AES_SEC_KEY};
David Brown5c9e0f12019-01-09 16:34:33 -070023
24impl Images {
25 /// A simple upgrade without forced failures.
26 ///
27 /// Returns the number of flash operations which can later be used to
28 /// inject failures at chosen steps.
29 pub fn run_basic_upgrade(&self) -> Result<i32, ()> {
30 let (flashmap, total_count) = try_upgrade(&self.flashmap, &self, None);
31 info!("Total flash operation count={}", total_count);
32
33 if !verify_image(&flashmap, &self.slots, 0, &self.upgrades) {
34 warn!("Image mismatch after first boot");
35 Err(())
36 } else {
37 Ok(total_count)
38 }
39 }
40
David Brown5c9e0f12019-01-09 16:34:33 -070041 pub fn run_basic_revert(&self) -> bool {
David Brown3910ab12019-01-11 12:02:26 -070042 if Caps::OverwriteUpgrade.present() {
43 return false;
44 }
David Brown5c9e0f12019-01-09 16:34:33 -070045
David Brown5c9e0f12019-01-09 16:34:33 -070046 let mut fails = 0;
47
48 // FIXME: this test would also pass if no swap is ever performed???
49 if Caps::SwapUpgrade.present() {
50 for count in 2 .. 5 {
51 info!("Try revert: {}", count);
52 let flashmap = try_revert(&self.flashmap, &self.areadesc, count);
53 if !verify_image(&flashmap, &self.slots, 0, &self.primaries) {
54 error!("Revert failure on count {}", count);
55 fails += 1;
56 }
57 }
58 }
59
60 fails > 0
61 }
62
63 pub fn run_perm_with_fails(&self) -> bool {
64 let mut fails = 0;
65 let total_flash_ops = self.total_count.unwrap();
66
67 // Let's try an image halfway through.
68 for i in 1 .. total_flash_ops {
69 info!("Try interruption at {}", i);
70 let (flashmap, count) = try_upgrade(&self.flashmap, &self, Some(i));
71 info!("Second boot, count={}", count);
72 if !verify_image(&flashmap, &self.slots, 0, &self.upgrades) {
73 warn!("FAIL at step {} of {}", i, total_flash_ops);
74 fails += 1;
75 }
76
77 if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
78 BOOT_FLAG_SET, BOOT_FLAG_SET) {
David Vincze2d736ad2019-02-18 11:50:22 +010079 warn!("Mismatched trailer for the primary slot");
David Brown5c9e0f12019-01-09 16:34:33 -070080 fails += 1;
81 }
82
83 if !verify_trailer(&flashmap, &self.slots, 1, BOOT_MAGIC_UNSET,
84 BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
David Vincze2d736ad2019-02-18 11:50:22 +010085 warn!("Mismatched trailer for the secondary slot");
David Brown5c9e0f12019-01-09 16:34:33 -070086 fails += 1;
87 }
88
89 if Caps::SwapUpgrade.present() {
90 if !verify_image(&flashmap, &self.slots, 1, &self.primaries) {
David Vincze2d736ad2019-02-18 11:50:22 +010091 warn!("Secondary slot FAIL at step {} of {}",
92 i, total_flash_ops);
David Brown5c9e0f12019-01-09 16:34:33 -070093 fails += 1;
94 }
95 }
96 }
97
98 if fails > 0 {
99 error!("{} out of {} failed {:.2}%", fails, total_flash_ops,
100 fails as f32 * 100.0 / total_flash_ops as f32);
101 }
102
103 fails > 0
104 }
105
106 pub fn run_perm_with_random_fails_5(&self) -> bool {
107 self.run_perm_with_random_fails(5)
108 }
109
110 pub fn run_perm_with_random_fails(&self, total_fails: usize) -> bool {
111 let mut fails = 0;
112 let total_flash_ops = self.total_count.unwrap();
113 let (flashmap, total_counts) = try_random_fails(&self.flashmap, &self,
114 total_flash_ops, total_fails);
115 info!("Random interruptions at reset points={:?}", total_counts);
116
David Vincze2d736ad2019-02-18 11:50:22 +0100117 let primary_slot_ok = verify_image(&flashmap, &self.slots,
118 0, &self.upgrades);
119 let secondary_slot_ok = if Caps::SwapUpgrade.present() {
David Brown5c9e0f12019-01-09 16:34:33 -0700120 verify_image(&flashmap, &self.slots, 1, &self.primaries)
121 } else {
122 true
123 };
David Vincze2d736ad2019-02-18 11:50:22 +0100124 if !primary_slot_ok || !secondary_slot_ok {
125 error!("Image mismatch after random interrupts: primary slot={} \
126 secondary slot={}",
127 if primary_slot_ok { "ok" } else { "fail" },
128 if secondary_slot_ok { "ok" } else { "fail" });
David Brown5c9e0f12019-01-09 16:34:33 -0700129 fails += 1;
130 }
131 if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
132 BOOT_FLAG_SET, BOOT_FLAG_SET) {
David Vincze2d736ad2019-02-18 11:50:22 +0100133 error!("Mismatched trailer for the primary slot");
David Brown5c9e0f12019-01-09 16:34:33 -0700134 fails += 1;
135 }
136 if !verify_trailer(&flashmap, &self.slots, 1, BOOT_MAGIC_UNSET,
137 BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
David Vincze2d736ad2019-02-18 11:50:22 +0100138 error!("Mismatched trailer for the secondary slot");
David Brown5c9e0f12019-01-09 16:34:33 -0700139 fails += 1;
140 }
141
142 if fails > 0 {
143 error!("Error testing perm upgrade with {} fails", total_fails);
144 }
145
146 fails > 0
147 }
148
David Brown5c9e0f12019-01-09 16:34:33 -0700149 pub fn run_revert_with_fails(&self) -> bool {
David Brown3910ab12019-01-11 12:02:26 -0700150 if Caps::OverwriteUpgrade.present() {
151 return false;
152 }
David Brown5c9e0f12019-01-09 16:34:33 -0700153
David Brown5c9e0f12019-01-09 16:34:33 -0700154 let mut fails = 0;
155
156 if Caps::SwapUpgrade.present() {
157 for i in 1 .. (self.total_count.unwrap() - 1) {
158 info!("Try interruption at {}", i);
159 if try_revert_with_fail_at(&self.flashmap, &self, i) {
160 error!("Revert failed at interruption {}", i);
161 fails += 1;
162 }
163 }
164 }
165
166 fails > 0
167 }
168
David Brown5c9e0f12019-01-09 16:34:33 -0700169 pub fn run_norevert(&self) -> bool {
David Brown3910ab12019-01-11 12:02:26 -0700170 if Caps::OverwriteUpgrade.present() {
171 return false;
172 }
David Brown5c9e0f12019-01-09 16:34:33 -0700173
David Brown5c9e0f12019-01-09 16:34:33 -0700174 let mut flashmap = self.flashmap.clone();
175 let mut fails = 0;
176
177 info!("Try norevert");
178
179 // First do a normal upgrade...
180 let (result, _) = c::boot_go(&mut flashmap, &self.areadesc, None, false);
181 if result != 0 {
182 warn!("Failed first boot");
183 fails += 1;
184 }
185
186 //FIXME: copy_done is written by boot_go, is it ok if no copy
187 // was ever done?
188
189 if !verify_image(&flashmap, &self.slots, 0, &self.upgrades) {
David Vincze2d736ad2019-02-18 11:50:22 +0100190 warn!("Primary slot image verification FAIL");
David Brown5c9e0f12019-01-09 16:34:33 -0700191 fails += 1;
192 }
193 if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
194 BOOT_FLAG_UNSET, BOOT_FLAG_SET) {
David Vincze2d736ad2019-02-18 11:50:22 +0100195 warn!("Mismatched trailer for the primary slot");
David Brown5c9e0f12019-01-09 16:34:33 -0700196 fails += 1;
197 }
198 if !verify_trailer(&flashmap, &self.slots, 1, BOOT_MAGIC_UNSET,
199 BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
David Vincze2d736ad2019-02-18 11:50:22 +0100200 warn!("Mismatched trailer for the secondary slot");
David Brown5c9e0f12019-01-09 16:34:33 -0700201 fails += 1;
202 }
203
David Vincze2d736ad2019-02-18 11:50:22 +0100204 // Marks image in the primary slot as permanent,
205 // no revert should happen...
David Brown5c9e0f12019-01-09 16:34:33 -0700206 mark_permanent_upgrade(&mut flashmap, &self.slots[0]);
207
208 if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
209 BOOT_FLAG_SET, BOOT_FLAG_SET) {
David Vincze2d736ad2019-02-18 11:50:22 +0100210 warn!("Mismatched trailer for the primary slot");
David Brown5c9e0f12019-01-09 16:34:33 -0700211 fails += 1;
212 }
213
214 let (result, _) = c::boot_go(&mut flashmap, &self.areadesc, None, false);
215 if result != 0 {
216 warn!("Failed second boot");
217 fails += 1;
218 }
219
220 if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
221 BOOT_FLAG_SET, BOOT_FLAG_SET) {
David Vincze2d736ad2019-02-18 11:50:22 +0100222 warn!("Mismatched trailer for the primary slot");
David Brown5c9e0f12019-01-09 16:34:33 -0700223 fails += 1;
224 }
225 if !verify_image(&flashmap, &self.slots, 0, &self.upgrades) {
226 warn!("Failed image verification");
227 fails += 1;
228 }
229
230 if fails > 0 {
231 error!("Error running upgrade without revert");
232 }
233
234 fails > 0
235 }
236
David Vincze2d736ad2019-02-18 11:50:22 +0100237 // Tests a new image written to the primary slot that already has magic and
238 // image_ok set while there is no image on the secondary slot, so no revert
239 // should ever happen...
David Brown5c9e0f12019-01-09 16:34:33 -0700240 pub fn run_norevert_newimage(&self) -> bool {
241 let mut flashmap = self.flashmap.clone();
242 let mut fails = 0;
243
244 info!("Try non-revert on imgtool generated image");
245
246 mark_upgrade(&mut flashmap, &self.slots[0]);
247
David Vincze2d736ad2019-02-18 11:50:22 +0100248 // This simulates writing an image created by imgtool to
249 // the primary slot
David Brown5c9e0f12019-01-09 16:34:33 -0700250 if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
251 BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
David Vincze2d736ad2019-02-18 11:50:22 +0100252 warn!("Mismatched trailer for the primary slot");
David Brown5c9e0f12019-01-09 16:34:33 -0700253 fails += 1;
254 }
255
256 // Run the bootloader...
257 let (result, _) = c::boot_go(&mut flashmap, &self.areadesc, None, false);
258 if result != 0 {
259 warn!("Failed first boot");
260 fails += 1;
261 }
262
263 // State should not have changed
264 if !verify_image(&flashmap, &self.slots, 0, &self.primaries) {
265 warn!("Failed image verification");
266 fails += 1;
267 }
268 if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
269 BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
David Vincze2d736ad2019-02-18 11:50:22 +0100270 warn!("Mismatched trailer for the primary slot");
David Brown5c9e0f12019-01-09 16:34:33 -0700271 fails += 1;
272 }
273 if !verify_trailer(&flashmap, &self.slots, 1, BOOT_MAGIC_UNSET,
274 BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
David Vincze2d736ad2019-02-18 11:50:22 +0100275 warn!("Mismatched trailer for the secondary slot");
David Brown5c9e0f12019-01-09 16:34:33 -0700276 fails += 1;
277 }
278
279 if fails > 0 {
280 error!("Expected a non revert with new image");
281 }
282
283 fails > 0
284 }
285
David Vincze2d736ad2019-02-18 11:50:22 +0100286 // Tests a new image written to the primary slot that already has magic and
287 // image_ok set while there is no image on the secondary slot, so no revert
288 // should ever happen...
David Brown5c9e0f12019-01-09 16:34:33 -0700289 pub fn run_signfail_upgrade(&self) -> bool {
290 let mut flashmap = self.flashmap.clone();
291 let mut fails = 0;
292
293 info!("Try upgrade image with bad signature");
294
295 mark_upgrade(&mut flashmap, &self.slots[0]);
296 mark_permanent_upgrade(&mut flashmap, &self.slots[0]);
297 mark_upgrade(&mut flashmap, &self.slots[1]);
298
299 if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
300 BOOT_FLAG_SET, BOOT_FLAG_UNSET) {
David Vincze2d736ad2019-02-18 11:50:22 +0100301 warn!("Mismatched trailer for the primary slot");
David Brown5c9e0f12019-01-09 16:34:33 -0700302 fails += 1;
303 }
304
305 // Run the bootloader...
306 let (result, _) = c::boot_go(&mut flashmap, &self.areadesc, None, false);
307 if result != 0 {
308 warn!("Failed first boot");
309 fails += 1;
310 }
311
312 // State should not have changed
313 if !verify_image(&flashmap, &self.slots, 0, &self.primaries) {
314 warn!("Failed image verification");
315 fails += 1;
316 }
317 if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
318 BOOT_FLAG_SET, BOOT_FLAG_UNSET) {
David Vincze2d736ad2019-02-18 11:50:22 +0100319 warn!("Mismatched trailer for the primary slot");
David Brown5c9e0f12019-01-09 16:34:33 -0700320 fails += 1;
321 }
322
323 if fails > 0 {
324 error!("Expected an upgrade failure when image has bad signature");
325 }
326
327 fails > 0
328 }
329
David Brown5c9e0f12019-01-09 16:34:33 -0700330 fn trailer_sz(&self, align: usize) -> usize {
331 c::boot_trailer_sz(align as u8) as usize
332 }
333
334 // FIXME: could get status sz from bootloader
David Brown5c9e0f12019-01-09 16:34:33 -0700335 fn status_sz(&self, align: usize) -> usize {
David Brown9930a3e2019-01-11 12:28:26 -0700336 let bias = if Caps::EncRsa.present() || Caps::EncKw.present() {
337 32
338 } else {
339 0
340 };
David Brown5c9e0f12019-01-09 16:34:33 -0700341
David Brown9930a3e2019-01-11 12:28:26 -0700342 self.trailer_sz(align) - (16 + 24 + bias)
David Brown5c9e0f12019-01-09 16:34:33 -0700343 }
344
345 /// This test runs a simple upgrade with no fails in the images, but
346 /// allowing for fails in the status area. This should run to the end
347 /// and warn that write fails were detected...
David Brown5c9e0f12019-01-09 16:34:33 -0700348 pub fn run_with_status_fails_complete(&self) -> bool {
David Vincze2d736ad2019-02-18 11:50:22 +0100349 if !Caps::ValidatePrimarySlot.present() {
David Brown85904a82019-01-11 13:45:12 -0700350 return false;
351 }
352
David Brown5c9e0f12019-01-09 16:34:33 -0700353 let mut flashmap = self.flashmap.clone();
354 let mut fails = 0;
355
356 info!("Try swap with status fails");
357
358 mark_permanent_upgrade(&mut flashmap, &self.slots[1]);
359 self.mark_bad_status_with_rate(&mut flashmap, 0, 1.0);
360
361 let (result, asserts) = c::boot_go(&mut flashmap, &self.areadesc, None, true);
362 if result != 0 {
363 warn!("Failed!");
364 fails += 1;
365 }
366
367 // Failed writes to the marked "bad" region don't assert anymore.
368 // Any detected assert() is happening in another part of the code.
369 if asserts != 0 {
370 warn!("At least one assert() was called");
371 fails += 1;
372 }
373
374 if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
375 BOOT_FLAG_SET, BOOT_FLAG_SET) {
David Vincze2d736ad2019-02-18 11:50:22 +0100376 warn!("Mismatched trailer for the primary slot");
David Brown5c9e0f12019-01-09 16:34:33 -0700377 fails += 1;
378 }
379
380 if !verify_image(&flashmap, &self.slots, 0, &self.upgrades) {
381 warn!("Failed image verification");
382 fails += 1;
383 }
384
David Vincze2d736ad2019-02-18 11:50:22 +0100385 info!("validate primary slot enabled; \
386 re-run of boot_go should just work");
David Brown5c9e0f12019-01-09 16:34:33 -0700387 let (result, _) = c::boot_go(&mut flashmap, &self.areadesc, None, false);
388 if result != 0 {
389 warn!("Failed!");
390 fails += 1;
391 }
392
393 if fails > 0 {
394 error!("Error running upgrade with status write fails");
395 }
396
397 fails > 0
398 }
399
400 /// This test runs a simple upgrade with no fails in the images, but
401 /// allowing for fails in the status area. This should run to the end
402 /// and warn that write fails were detected...
David Brown5c9e0f12019-01-09 16:34:33 -0700403 pub fn run_with_status_fails_with_reset(&self) -> bool {
David Brown85904a82019-01-11 13:45:12 -0700404 if Caps::OverwriteUpgrade.present() {
405 false
David Vincze2d736ad2019-02-18 11:50:22 +0100406 } else if Caps::ValidatePrimarySlot.present() {
David Brown5c9e0f12019-01-09 16:34:33 -0700407
David Brown85904a82019-01-11 13:45:12 -0700408 let mut flashmap = self.flashmap.clone();
409 let mut fails = 0;
410 let mut count = self.total_count.unwrap() / 2;
David Brown5c9e0f12019-01-09 16:34:33 -0700411
David Brown85904a82019-01-11 13:45:12 -0700412 //info!("count={}\n", count);
David Brown5c9e0f12019-01-09 16:34:33 -0700413
David Brown85904a82019-01-11 13:45:12 -0700414 info!("Try interrupted swap with status fails");
David Brown5c9e0f12019-01-09 16:34:33 -0700415
David Brown85904a82019-01-11 13:45:12 -0700416 mark_permanent_upgrade(&mut flashmap, &self.slots[1]);
417 self.mark_bad_status_with_rate(&mut flashmap, 0, 0.5);
418
419 // Should not fail, writing to bad regions does not assert
420 let (_, asserts) = c::boot_go(&mut flashmap, &self.areadesc, Some(&mut count), true);
421 if asserts != 0 {
422 warn!("At least one assert() was called");
423 fails += 1;
424 }
425
426 self.reset_bad_status(&mut flashmap, 0);
427
428 info!("Resuming an interrupted swap operation");
429 let (_, asserts) = c::boot_go(&mut flashmap, &self.areadesc, None, true);
430
431 // This might throw no asserts, for large sector devices, where
432 // a single failure writing is indistinguishable from no failure,
433 // or throw a single assert for small sector devices that fail
434 // multiple times...
435 if asserts > 1 {
David Vincze2d736ad2019-02-18 11:50:22 +0100436 warn!("Expected single assert validating the primary slot, \
437 more detected {}", asserts);
David Brown85904a82019-01-11 13:45:12 -0700438 fails += 1;
439 }
440
441 if fails > 0 {
442 error!("Error running upgrade with status write fails");
443 }
444
445 fails > 0
446 } else {
447 let mut flashmap = self.flashmap.clone();
448 let mut fails = 0;
449
450 info!("Try interrupted swap with status fails");
451
452 mark_permanent_upgrade(&mut flashmap, &self.slots[1]);
453 self.mark_bad_status_with_rate(&mut flashmap, 0, 1.0);
454
455 // This is expected to fail while writing to bad regions...
456 let (_, asserts) = c::boot_go(&mut flashmap, &self.areadesc, None, true);
457 if asserts == 0 {
458 warn!("No assert() detected");
459 fails += 1;
460 }
461
462 fails > 0
David Brown5c9e0f12019-01-09 16:34:33 -0700463 }
David Brown5c9e0f12019-01-09 16:34:33 -0700464 }
465
466 /// Adds a new flash area that fails statistically
David Brown5c9e0f12019-01-09 16:34:33 -0700467 fn mark_bad_status_with_rate(&self, flashmap: &mut SimFlashMap, slot: usize,
468 rate: f32) {
David Brown85904a82019-01-11 13:45:12 -0700469 if Caps::OverwriteUpgrade.present() {
470 return;
471 }
472
David Brown5c9e0f12019-01-09 16:34:33 -0700473 let dev_id = &self.slots[slot].dev_id;
474 let flash = flashmap.get_mut(&dev_id).unwrap();
475 let align = flash.align();
476 let off = &self.slots[0].base_off;
477 let len = &self.slots[0].len;
478 let status_off = off + len - self.trailer_sz(align);
479
480 // Mark the status area as a bad area
481 let _ = flash.add_bad_region(status_off, self.status_sz(align), rate);
482 }
483
David Brown5c9e0f12019-01-09 16:34:33 -0700484 fn reset_bad_status(&self, flashmap: &mut SimFlashMap, slot: usize) {
David Vincze2d736ad2019-02-18 11:50:22 +0100485 if !Caps::ValidatePrimarySlot.present() {
David Brown85904a82019-01-11 13:45:12 -0700486 return;
487 }
488
David Brown5c9e0f12019-01-09 16:34:33 -0700489 let dev_id = &self.slots[slot].dev_id;
490 let flash = flashmap.get_mut(&dev_id).unwrap();
491 flash.reset_bad_regions();
492
493 // Disabling write verification the only assert triggered by
494 // boot_go should be checking for integrity of status bytes.
495 flash.set_verify_writes(false);
496 }
497
David Brown5c9e0f12019-01-09 16:34:33 -0700498}
499
500/// Test a boot, optionally stopping after 'n' flash options. Returns a count
501/// of the number of flash operations done total.
502fn try_upgrade(flashmap: &SimFlashMap, images: &Images,
503 stop: Option<i32>) -> (SimFlashMap, i32) {
504 // Clone the flash to have a new copy.
505 let mut flashmap = flashmap.clone();
506
507 mark_permanent_upgrade(&mut flashmap, &images.slots[1]);
508
509 let mut counter = stop.unwrap_or(0);
510
511 let (first_interrupted, count) = match c::boot_go(&mut flashmap, &images.areadesc, Some(&mut counter), false) {
512 (-0x13579, _) => (true, stop.unwrap()),
513 (0, _) => (false, -counter),
514 (x, _) => panic!("Unknown return: {}", x),
515 };
516
517 counter = 0;
518 if first_interrupted {
519 // fl.dump();
520 match c::boot_go(&mut flashmap, &images.areadesc, Some(&mut counter), false) {
521 (-0x13579, _) => panic!("Shouldn't stop again"),
522 (0, _) => (),
523 (x, _) => panic!("Unknown return: {}", x),
524 }
525 }
526
527 (flashmap, count - counter)
528}
529
David Brown5c9e0f12019-01-09 16:34:33 -0700530fn try_revert(flashmap: &SimFlashMap, areadesc: &AreaDesc, count: usize) -> SimFlashMap {
531 let mut flashmap = flashmap.clone();
532
533 // fl.write_file("image0.bin").unwrap();
534 for i in 0 .. count {
535 info!("Running boot pass {}", i + 1);
536 assert_eq!(c::boot_go(&mut flashmap, &areadesc, None, false), (0, 0));
537 }
538 flashmap
539}
540
David Brown5c9e0f12019-01-09 16:34:33 -0700541fn try_revert_with_fail_at(flashmap: &SimFlashMap, images: &Images,
542 stop: i32) -> bool {
543 let mut flashmap = flashmap.clone();
544 let mut fails = 0;
545
546 let mut counter = stop;
547 let (x, _) = c::boot_go(&mut flashmap, &images.areadesc, Some(&mut counter), false);
548 if x != -0x13579 {
549 warn!("Should have stopped at interruption point");
550 fails += 1;
551 }
552
553 if !verify_trailer(&flashmap, &images.slots, 0, None, None, BOOT_FLAG_UNSET) {
554 warn!("copy_done should be unset");
555 fails += 1;
556 }
557
558 let (x, _) = c::boot_go(&mut flashmap, &images.areadesc, None, false);
559 if x != 0 {
560 warn!("Should have finished upgrade");
561 fails += 1;
562 }
563
564 if !verify_image(&flashmap, &images.slots, 0, &images.upgrades) {
David Vincze2d736ad2019-02-18 11:50:22 +0100565 warn!("Image in the primary slot before revert is invalid at stop={}",
566 stop);
David Brown5c9e0f12019-01-09 16:34:33 -0700567 fails += 1;
568 }
569 if !verify_image(&flashmap, &images.slots, 1, &images.primaries) {
David Vincze2d736ad2019-02-18 11:50:22 +0100570 warn!("Image in the secondary slot before revert is invalid at stop={}",
571 stop);
David Brown5c9e0f12019-01-09 16:34:33 -0700572 fails += 1;
573 }
574 if !verify_trailer(&flashmap, &images.slots, 0, BOOT_MAGIC_GOOD,
575 BOOT_FLAG_UNSET, BOOT_FLAG_SET) {
David Vincze2d736ad2019-02-18 11:50:22 +0100576 warn!("Mismatched trailer for the primary slot before revert");
David Brown5c9e0f12019-01-09 16:34:33 -0700577 fails += 1;
578 }
579 if !verify_trailer(&flashmap, &images.slots, 1, BOOT_MAGIC_UNSET,
580 BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
David Vincze2d736ad2019-02-18 11:50:22 +0100581 warn!("Mismatched trailer for the secondary slot before revert");
David Brown5c9e0f12019-01-09 16:34:33 -0700582 fails += 1;
583 }
584
585 // Do Revert
586 let (x, _) = c::boot_go(&mut flashmap, &images.areadesc, None, false);
587 if x != 0 {
588 warn!("Should have finished a revert");
589 fails += 1;
590 }
591
592 if !verify_image(&flashmap, &images.slots, 0, &images.primaries) {
David Vincze2d736ad2019-02-18 11:50:22 +0100593 warn!("Image in the primary slot after revert is invalid at stop={}",
594 stop);
David Brown5c9e0f12019-01-09 16:34:33 -0700595 fails += 1;
596 }
597 if !verify_image(&flashmap, &images.slots, 1, &images.upgrades) {
David Vincze2d736ad2019-02-18 11:50:22 +0100598 warn!("Image in the secondary slot after revert is invalid at stop={}",
599 stop);
David Brown5c9e0f12019-01-09 16:34:33 -0700600 fails += 1;
601 }
602 if !verify_trailer(&flashmap, &images.slots, 0, BOOT_MAGIC_GOOD,
603 BOOT_FLAG_SET, BOOT_FLAG_SET) {
David Vincze2d736ad2019-02-18 11:50:22 +0100604 warn!("Mismatched trailer for the secondary slot after revert");
David Brown5c9e0f12019-01-09 16:34:33 -0700605 fails += 1;
606 }
607 if !verify_trailer(&flashmap, &images.slots, 1, BOOT_MAGIC_UNSET,
608 BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
David Vincze2d736ad2019-02-18 11:50:22 +0100609 warn!("Mismatched trailer for the secondary slot after revert");
David Brown5c9e0f12019-01-09 16:34:33 -0700610 fails += 1;
611 }
612
613 fails > 0
614}
615
616fn try_random_fails(flashmap: &SimFlashMap, images: &Images,
617 total_ops: i32, count: usize) -> (SimFlashMap, Vec<i32>) {
618 let mut flashmap = flashmap.clone();
619
620 mark_permanent_upgrade(&mut flashmap, &images.slots[1]);
621
622 let mut rng = rand::thread_rng();
623 let mut resets = vec![0i32; count];
624 let mut remaining_ops = total_ops;
625 for i in 0 .. count {
626 let ops = Range::new(1, remaining_ops / 2);
627 let reset_counter = ops.ind_sample(&mut rng);
628 let mut counter = reset_counter;
629 match c::boot_go(&mut flashmap, &images.areadesc, Some(&mut counter), false) {
630 (0, _) | (-0x13579, _) => (),
631 (x, _) => panic!("Unknown return: {}", x),
632 }
633 remaining_ops -= reset_counter;
634 resets[i] = reset_counter;
635 }
636
637 match c::boot_go(&mut flashmap, &images.areadesc, None, false) {
638 (-0x13579, _) => panic!("Should not be have been interrupted!"),
639 (0, _) => (),
640 (x, _) => panic!("Unknown return: {}", x),
641 }
642
643 (flashmap, resets)
644}
645
646/// Show the flash layout.
647#[allow(dead_code)]
648fn show_flash(flash: &dyn Flash) {
649 println!("---- Flash configuration ----");
650 for sector in flash.sector_iter() {
651 println!(" {:3}: 0x{:08x}, 0x{:08x}",
652 sector.num, sector.base, sector.size);
653 }
654 println!("");
655}
656
657/// Install a "program" into the given image. This fakes the image header, or at least all of the
658/// fields used by the given code. Returns a copy of the image that was written.
659pub fn install_image(flashmap: &mut SimFlashMap, slots: &[SlotInfo], slot: usize, len: usize,
660 bad_sig: bool) -> [Option<Vec<u8>>; 2] {
661 let offset = slots[slot].base_off;
662 let slot_len = slots[slot].len;
663 let dev_id = slots[slot].dev_id;
664
David Brown43643dd2019-01-11 15:43:28 -0700665 let mut tlv: Box<dyn ManifestGen> = Box::new(make_tlv());
David Brown5c9e0f12019-01-09 16:34:33 -0700666
667 const HDR_SIZE: usize = 32;
668
669 // Generate a boot header. Note that the size doesn't include the header.
670 let header = ImageHeader {
671 magic: 0x96f3b83d,
672 load_addr: 0,
673 hdr_size: HDR_SIZE as u16,
674 _pad1: 0,
675 img_size: len as u32,
676 flags: tlv.get_flags(),
677 ver: ImageVersion {
678 major: (offset / (128 * 1024)) as u8,
679 minor: 0,
680 revision: 1,
681 build_num: offset as u32,
682 },
683 _pad2: 0,
684 };
685
686 let mut b_header = [0; HDR_SIZE];
687 b_header[..32].clone_from_slice(header.as_raw());
688 assert_eq!(b_header.len(), HDR_SIZE);
689
690 tlv.add_bytes(&b_header);
691
692 // The core of the image itself is just pseudorandom data.
693 let mut b_img = vec![0; len];
694 splat(&mut b_img, offset);
695
696 // TLV signatures work over plain image
697 tlv.add_bytes(&b_img);
698
699 // Generate encrypted images
700 let flag = TlvFlags::ENCRYPTED as u32;
701 let is_encrypted = (tlv.get_flags() & flag) == flag;
702 let mut b_encimg = vec![];
703 if is_encrypted {
704 let key = GenericArray::from_slice(AES_SEC_KEY);
705 let nonce = GenericArray::from_slice(&[0; 16]);
706 let mut cipher = Aes128Ctr::new(&key, &nonce);
707 b_encimg = b_img.clone();
708 cipher.apply_keystream(&mut b_encimg);
709 }
710
711 // Build the TLV itself.
712 let mut b_tlv = if bad_sig {
713 let good_sig = &mut tlv.make_tlv();
714 vec![0; good_sig.len()]
715 } else {
716 tlv.make_tlv()
717 };
718
719 // Pad the block to a flash alignment (8 bytes).
720 while b_tlv.len() % 8 != 0 {
721 //FIXME: should be erase_val?
722 b_tlv.push(0xFF);
723 }
724
725 let mut buf = vec![];
726 buf.append(&mut b_header.to_vec());
727 buf.append(&mut b_img);
728 buf.append(&mut b_tlv.clone());
729
730 let mut encbuf = vec![];
731 if is_encrypted {
732 encbuf.append(&mut b_header.to_vec());
733 encbuf.append(&mut b_encimg);
734 encbuf.append(&mut b_tlv);
735 }
736
737 let result: [Option<Vec<u8>>; 2];
738
David Vincze2d736ad2019-02-18 11:50:22 +0100739 // Since images are always non-encrypted in the primary slot, we first write
740 // an encrypted image, re-read to use for verification, erase + flash
741 // un-encrypted. In the secondary slot the image is written un-encrypted,
742 // and if encryption is requested, it follows an erase + flash encrypted.
David Brown5c9e0f12019-01-09 16:34:33 -0700743
744 let flash = flashmap.get_mut(&dev_id).unwrap();
745
746 if slot == 0 {
747 let enc_copy: Option<Vec<u8>>;
748
749 if is_encrypted {
750 flash.write(offset, &encbuf).unwrap();
751
752 let mut enc = vec![0u8; encbuf.len()];
753 flash.read(offset, &mut enc).unwrap();
754
755 enc_copy = Some(enc);
756
757 flash.erase(offset, slot_len).unwrap();
758 } else {
759 enc_copy = None;
760 }
761
762 flash.write(offset, &buf).unwrap();
763
764 let mut copy = vec![0u8; buf.len()];
765 flash.read(offset, &mut copy).unwrap();
766
767 result = [Some(copy), enc_copy];
768 } else {
769
770 flash.write(offset, &buf).unwrap();
771
772 let mut copy = vec![0u8; buf.len()];
773 flash.read(offset, &mut copy).unwrap();
774
775 let enc_copy: Option<Vec<u8>>;
776
777 if is_encrypted {
778 flash.erase(offset, slot_len).unwrap();
779
780 flash.write(offset, &encbuf).unwrap();
781
782 let mut enc = vec![0u8; encbuf.len()];
783 flash.read(offset, &mut enc).unwrap();
784
785 enc_copy = Some(enc);
786 } else {
787 enc_copy = None;
788 }
789
790 result = [Some(copy), enc_copy];
791 }
792
793 result
794}
795
David Brown5c9e0f12019-01-09 16:34:33 -0700796fn make_tlv() -> TlvGen {
David Brownb8882112019-01-11 14:04:11 -0700797 if Caps::EcdsaP224.present() {
798 panic!("Ecdsa P224 not supported in Simulator");
799 }
David Brown5c9e0f12019-01-09 16:34:33 -0700800
David Brownb8882112019-01-11 14:04:11 -0700801 if Caps::EncKw.present() {
802 if Caps::RSA2048.present() {
803 TlvGen::new_rsa_kw()
804 } else if Caps::EcdsaP256.present() {
805 TlvGen::new_ecdsa_kw()
806 } else {
807 TlvGen::new_enc_kw()
808 }
809 } else if Caps::EncRsa.present() {
810 if Caps::RSA2048.present() {
811 TlvGen::new_sig_enc_rsa()
812 } else {
813 TlvGen::new_enc_rsa()
814 }
815 } else {
816 // The non-encrypted configuration.
817 if Caps::RSA2048.present() {
818 TlvGen::new_rsa_pss()
819 } else if Caps::EcdsaP256.present() {
820 TlvGen::new_ecdsa()
821 } else {
822 TlvGen::new_hash_only()
823 }
824 }
David Brown5c9e0f12019-01-09 16:34:33 -0700825}
826
David Brown5c9e0f12019-01-09 16:34:33 -0700827fn find_image(images: &[Option<Vec<u8>>; 2], slot: usize) -> &Vec<u8> {
David Brownf38bc342019-01-11 14:07:58 -0700828 let slot = if Caps::EncRsa.present() || Caps::EncKw.present() {
829 slot
830 } else {
831 0
832 };
David Brown5c9e0f12019-01-09 16:34:33 -0700833
David Brown5c9e0f12019-01-09 16:34:33 -0700834 match &images[slot] {
835 Some(image) => return image,
836 None => panic!("Invalid image"),
837 }
838}
839
David Brown5c9e0f12019-01-09 16:34:33 -0700840/// Verify that given image is present in the flash at the given offset.
841fn verify_image(flashmap: &SimFlashMap, slots: &[SlotInfo], slot: usize,
842 images: &[Option<Vec<u8>>; 2]) -> bool {
843 let image = find_image(images, slot);
844 let buf = image.as_slice();
845 let dev_id = slots[slot].dev_id;
846
847 let mut copy = vec![0u8; buf.len()];
848 let offset = slots[slot].base_off;
849 let flash = flashmap.get(&dev_id).unwrap();
850 flash.read(offset, &mut copy).unwrap();
851
852 if buf != &copy[..] {
853 for i in 0 .. buf.len() {
854 if buf[i] != copy[i] {
855 info!("First failure for slot{} at {:#x} {:#x}!={:#x}",
856 slot, offset + i, buf[i], copy[i]);
857 break;
858 }
859 }
860 false
861 } else {
862 true
863 }
864}
865
David Brown5c9e0f12019-01-09 16:34:33 -0700866fn verify_trailer(flashmap: &SimFlashMap, slots: &[SlotInfo], slot: usize,
867 magic: Option<u8>, image_ok: Option<u8>,
868 copy_done: Option<u8>) -> bool {
David Brown61a540d2019-01-11 14:29:14 -0700869 if Caps::OverwriteUpgrade.present() {
870 return true;
871 }
David Brown5c9e0f12019-01-09 16:34:33 -0700872
David Brown5c9e0f12019-01-09 16:34:33 -0700873 let offset = slots[slot].trailer_off;
874 let dev_id = slots[slot].dev_id;
875 let mut copy = vec![0u8; c::boot_magic_sz() + c::boot_max_align() * 2];
876 let mut failed = false;
877
878 let flash = flashmap.get(&dev_id).unwrap();
879 let erased_val = flash.erased_val();
880 flash.read(offset, &mut copy).unwrap();
881
882 failed |= match magic {
883 Some(v) => {
884 if v == 1 && &copy[16..] != MAGIC.unwrap() {
885 warn!("\"magic\" mismatch at {:#x}", offset);
886 true
887 } else if v == 3 {
888 let expected = [erased_val; 16];
889 if &copy[16..] != expected {
890 warn!("\"magic\" mismatch at {:#x}", offset);
891 true
892 } else {
893 false
894 }
895 } else {
896 false
897 }
898 },
899 None => false,
900 };
901
902 failed |= match image_ok {
903 Some(v) => {
904 if (v == 1 && copy[8] != v) || (v == 3 && copy[8] != erased_val) {
905 warn!("\"image_ok\" mismatch at {:#x} v={} val={:#x}", offset, v, copy[8]);
906 true
907 } else {
908 false
909 }
910 },
911 None => false,
912 };
913
914 failed |= match copy_done {
915 Some(v) => {
916 if (v == 1 && copy[0] != v) || (v == 3 && copy[0] != erased_val) {
917 warn!("\"copy_done\" mismatch at {:#x} v={} val={:#x}", offset, v, copy[0]);
918 true
919 } else {
920 false
921 }
922 },
923 None => false,
924 };
925
926 !failed
927}
928
929/// The image header
930#[repr(C)]
931pub struct ImageHeader {
932 magic: u32,
933 load_addr: u32,
934 hdr_size: u16,
935 _pad1: u16,
936 img_size: u32,
937 flags: u32,
938 ver: ImageVersion,
939 _pad2: u32,
940}
941
942impl AsRaw for ImageHeader {}
943
944#[repr(C)]
945pub struct ImageVersion {
946 major: u8,
947 minor: u8,
948 revision: u16,
949 build_num: u32,
950}
951
952#[derive(Clone)]
953pub struct SlotInfo {
954 pub base_off: usize,
955 pub trailer_off: usize,
956 pub len: usize,
957 pub dev_id: u8,
958}
959
960pub struct Images {
961 pub flashmap: SimFlashMap,
962 pub areadesc: AreaDesc,
963 pub slots: [SlotInfo; 2],
964 pub primaries: [Option<Vec<u8>>; 2],
965 pub upgrades: [Option<Vec<u8>>; 2],
966 pub total_count: Option<i32>,
967}
968
969const MAGIC: Option<&[u8]> = Some(&[0x77, 0xc2, 0x95, 0xf3,
970 0x60, 0xd2, 0xef, 0x7f,
971 0x35, 0x52, 0x50, 0x0f,
972 0x2c, 0xb6, 0x79, 0x80]);
973
974// Replicates defines found in bootutil.h
975const BOOT_MAGIC_GOOD: Option<u8> = Some(1);
976const BOOT_MAGIC_UNSET: Option<u8> = Some(3);
977
978const BOOT_FLAG_SET: Option<u8> = Some(1);
979const BOOT_FLAG_UNSET: Option<u8> = Some(3);
980
981/// Write out the magic so that the loader tries doing an upgrade.
982pub fn mark_upgrade(flashmap: &mut SimFlashMap, slot: &SlotInfo) {
983 let flash = flashmap.get_mut(&slot.dev_id).unwrap();
984 let offset = slot.trailer_off + c::boot_max_align() * 2;
985 flash.write(offset, MAGIC.unwrap()).unwrap();
986}
987
988/// Writes the image_ok flag which, guess what, tells the bootloader
989/// the this image is ok (not a test, and no revert is to be performed).
990fn mark_permanent_upgrade(flashmap: &mut SimFlashMap, slot: &SlotInfo) {
991 let flash = flashmap.get_mut(&slot.dev_id).unwrap();
992 let mut ok = [flash.erased_val(); 8];
993 ok[0] = 1u8;
994 let off = slot.trailer_off + c::boot_max_align();
995 let align = flash.align();
996 flash.write(off, &ok[..align]).unwrap();
997}
998
999// Drop some pseudo-random gibberish onto the data.
1000fn splat(data: &mut [u8], seed: usize) {
1001 let seed_block = [0x135782ea, 0x92184728, data.len() as u32, seed as u32];
1002 let mut rng: XorShiftRng = SeedableRng::from_seed(seed_block);
1003 rng.fill_bytes(data);
1004}
1005
1006/// Return a read-only view into the raw bytes of this object
1007trait AsRaw : Sized {
1008 fn as_raw<'a>(&'a self) -> &'a [u8] {
1009 unsafe { slice::from_raw_parts(self as *const _ as *const u8,
1010 mem::size_of::<Self>()) }
1011 }
1012}
1013
1014pub fn show_sizes() {
1015 // This isn't panic safe.
1016 for min in &[1, 2, 4, 8] {
1017 let msize = c::boot_trailer_sz(*min);
1018 println!("{:2}: {} (0x{:x})", min, msize, msize);
1019 }
1020}