blob: bbb25a0c906a0b01a78669b3d7c78948c0baf658 [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
David Brown998aa8d2019-02-28 10:54:50 -070024/// Images represents the state of a simulation for a given set of images.
25/// The flashmap holds the state of the simulated flash, whereas primaries
26/// and upgrades hold the expected contents of these images.
27pub struct Images {
28 pub flashmap: SimFlashMap,
29 pub areadesc: AreaDesc,
30 pub slots: [SlotInfo; 2],
31 pub primaries: [Option<Vec<u8>>; 2],
32 pub upgrades: [Option<Vec<u8>>; 2],
33 pub total_count: Option<i32>,
34}
35
David Brown5c9e0f12019-01-09 16:34:33 -070036impl Images {
37 /// A simple upgrade without forced failures.
38 ///
39 /// Returns the number of flash operations which can later be used to
40 /// inject failures at chosen steps.
41 pub fn run_basic_upgrade(&self) -> Result<i32, ()> {
42 let (flashmap, total_count) = try_upgrade(&self.flashmap, &self, None);
43 info!("Total flash operation count={}", total_count);
44
45 if !verify_image(&flashmap, &self.slots, 0, &self.upgrades) {
46 warn!("Image mismatch after first boot");
47 Err(())
48 } else {
49 Ok(total_count)
50 }
51 }
52
David Brown5c9e0f12019-01-09 16:34:33 -070053 pub fn run_basic_revert(&self) -> bool {
David Brown3910ab12019-01-11 12:02:26 -070054 if Caps::OverwriteUpgrade.present() {
55 return false;
56 }
David Brown5c9e0f12019-01-09 16:34:33 -070057
David Brown5c9e0f12019-01-09 16:34:33 -070058 let mut fails = 0;
59
60 // FIXME: this test would also pass if no swap is ever performed???
61 if Caps::SwapUpgrade.present() {
62 for count in 2 .. 5 {
63 info!("Try revert: {}", count);
64 let flashmap = try_revert(&self.flashmap, &self.areadesc, count);
65 if !verify_image(&flashmap, &self.slots, 0, &self.primaries) {
66 error!("Revert failure on count {}", count);
67 fails += 1;
68 }
69 }
70 }
71
72 fails > 0
73 }
74
75 pub fn run_perm_with_fails(&self) -> bool {
76 let mut fails = 0;
77 let total_flash_ops = self.total_count.unwrap();
78
79 // Let's try an image halfway through.
80 for i in 1 .. total_flash_ops {
81 info!("Try interruption at {}", i);
82 let (flashmap, count) = try_upgrade(&self.flashmap, &self, Some(i));
83 info!("Second boot, count={}", count);
84 if !verify_image(&flashmap, &self.slots, 0, &self.upgrades) {
85 warn!("FAIL at step {} of {}", i, total_flash_ops);
86 fails += 1;
87 }
88
89 if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
90 BOOT_FLAG_SET, BOOT_FLAG_SET) {
David Vincze2d736ad2019-02-18 11:50:22 +010091 warn!("Mismatched trailer for the primary slot");
David Brown5c9e0f12019-01-09 16:34:33 -070092 fails += 1;
93 }
94
95 if !verify_trailer(&flashmap, &self.slots, 1, BOOT_MAGIC_UNSET,
96 BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
David Vincze2d736ad2019-02-18 11:50:22 +010097 warn!("Mismatched trailer for the secondary slot");
David Brown5c9e0f12019-01-09 16:34:33 -070098 fails += 1;
99 }
100
101 if Caps::SwapUpgrade.present() {
102 if !verify_image(&flashmap, &self.slots, 1, &self.primaries) {
David Vincze2d736ad2019-02-18 11:50:22 +0100103 warn!("Secondary slot FAIL at step {} of {}",
104 i, total_flash_ops);
David Brown5c9e0f12019-01-09 16:34:33 -0700105 fails += 1;
106 }
107 }
108 }
109
110 if fails > 0 {
111 error!("{} out of {} failed {:.2}%", fails, total_flash_ops,
112 fails as f32 * 100.0 / total_flash_ops as f32);
113 }
114
115 fails > 0
116 }
117
118 pub fn run_perm_with_random_fails_5(&self) -> bool {
119 self.run_perm_with_random_fails(5)
120 }
121
122 pub fn run_perm_with_random_fails(&self, total_fails: usize) -> bool {
123 let mut fails = 0;
124 let total_flash_ops = self.total_count.unwrap();
125 let (flashmap, total_counts) = try_random_fails(&self.flashmap, &self,
126 total_flash_ops, total_fails);
127 info!("Random interruptions at reset points={:?}", total_counts);
128
David Vincze2d736ad2019-02-18 11:50:22 +0100129 let primary_slot_ok = verify_image(&flashmap, &self.slots,
130 0, &self.upgrades);
131 let secondary_slot_ok = if Caps::SwapUpgrade.present() {
David Brown5c9e0f12019-01-09 16:34:33 -0700132 verify_image(&flashmap, &self.slots, 1, &self.primaries)
133 } else {
134 true
135 };
David Vincze2d736ad2019-02-18 11:50:22 +0100136 if !primary_slot_ok || !secondary_slot_ok {
137 error!("Image mismatch after random interrupts: primary slot={} \
138 secondary slot={}",
139 if primary_slot_ok { "ok" } else { "fail" },
140 if secondary_slot_ok { "ok" } else { "fail" });
David Brown5c9e0f12019-01-09 16:34:33 -0700141 fails += 1;
142 }
143 if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
144 BOOT_FLAG_SET, BOOT_FLAG_SET) {
David Vincze2d736ad2019-02-18 11:50:22 +0100145 error!("Mismatched trailer for the primary slot");
David Brown5c9e0f12019-01-09 16:34:33 -0700146 fails += 1;
147 }
148 if !verify_trailer(&flashmap, &self.slots, 1, BOOT_MAGIC_UNSET,
149 BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
David Vincze2d736ad2019-02-18 11:50:22 +0100150 error!("Mismatched trailer for the secondary slot");
David Brown5c9e0f12019-01-09 16:34:33 -0700151 fails += 1;
152 }
153
154 if fails > 0 {
155 error!("Error testing perm upgrade with {} fails", total_fails);
156 }
157
158 fails > 0
159 }
160
David Brown5c9e0f12019-01-09 16:34:33 -0700161 pub fn run_revert_with_fails(&self) -> bool {
David Brown3910ab12019-01-11 12:02:26 -0700162 if Caps::OverwriteUpgrade.present() {
163 return false;
164 }
David Brown5c9e0f12019-01-09 16:34:33 -0700165
David Brown5c9e0f12019-01-09 16:34:33 -0700166 let mut fails = 0;
167
168 if Caps::SwapUpgrade.present() {
169 for i in 1 .. (self.total_count.unwrap() - 1) {
170 info!("Try interruption at {}", i);
171 if try_revert_with_fail_at(&self.flashmap, &self, i) {
172 error!("Revert failed at interruption {}", i);
173 fails += 1;
174 }
175 }
176 }
177
178 fails > 0
179 }
180
David Brown5c9e0f12019-01-09 16:34:33 -0700181 pub fn run_norevert(&self) -> bool {
David Brown3910ab12019-01-11 12:02:26 -0700182 if Caps::OverwriteUpgrade.present() {
183 return false;
184 }
David Brown5c9e0f12019-01-09 16:34:33 -0700185
David Brown5c9e0f12019-01-09 16:34:33 -0700186 let mut flashmap = self.flashmap.clone();
187 let mut fails = 0;
188
189 info!("Try norevert");
190
191 // First do a normal upgrade...
192 let (result, _) = c::boot_go(&mut flashmap, &self.areadesc, None, false);
193 if result != 0 {
194 warn!("Failed first boot");
195 fails += 1;
196 }
197
198 //FIXME: copy_done is written by boot_go, is it ok if no copy
199 // was ever done?
200
201 if !verify_image(&flashmap, &self.slots, 0, &self.upgrades) {
David Vincze2d736ad2019-02-18 11:50:22 +0100202 warn!("Primary slot image verification FAIL");
David Brown5c9e0f12019-01-09 16:34:33 -0700203 fails += 1;
204 }
205 if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
206 BOOT_FLAG_UNSET, BOOT_FLAG_SET) {
David Vincze2d736ad2019-02-18 11:50:22 +0100207 warn!("Mismatched trailer for the primary slot");
David Brown5c9e0f12019-01-09 16:34:33 -0700208 fails += 1;
209 }
210 if !verify_trailer(&flashmap, &self.slots, 1, BOOT_MAGIC_UNSET,
211 BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
David Vincze2d736ad2019-02-18 11:50:22 +0100212 warn!("Mismatched trailer for the secondary slot");
David Brown5c9e0f12019-01-09 16:34:33 -0700213 fails += 1;
214 }
215
David Vincze2d736ad2019-02-18 11:50:22 +0100216 // Marks image in the primary slot as permanent,
217 // no revert should happen...
David Brown5c9e0f12019-01-09 16:34:33 -0700218 mark_permanent_upgrade(&mut flashmap, &self.slots[0]);
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
226 let (result, _) = c::boot_go(&mut flashmap, &self.areadesc, None, false);
227 if result != 0 {
228 warn!("Failed second boot");
229 fails += 1;
230 }
231
232 if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
233 BOOT_FLAG_SET, BOOT_FLAG_SET) {
David Vincze2d736ad2019-02-18 11:50:22 +0100234 warn!("Mismatched trailer for the primary slot");
David Brown5c9e0f12019-01-09 16:34:33 -0700235 fails += 1;
236 }
237 if !verify_image(&flashmap, &self.slots, 0, &self.upgrades) {
238 warn!("Failed image verification");
239 fails += 1;
240 }
241
242 if fails > 0 {
243 error!("Error running upgrade without revert");
244 }
245
246 fails > 0
247 }
248
David Vincze2d736ad2019-02-18 11:50:22 +0100249 // Tests a new image written to the primary slot that already has magic and
250 // image_ok set while there is no image on the secondary slot, so no revert
251 // should ever happen...
David Brown5c9e0f12019-01-09 16:34:33 -0700252 pub fn run_norevert_newimage(&self) -> bool {
253 let mut flashmap = self.flashmap.clone();
254 let mut fails = 0;
255
256 info!("Try non-revert on imgtool generated image");
257
258 mark_upgrade(&mut flashmap, &self.slots[0]);
259
David Vincze2d736ad2019-02-18 11:50:22 +0100260 // This simulates writing an image created by imgtool to
261 // the primary slot
David Brown5c9e0f12019-01-09 16:34:33 -0700262 if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
263 BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
David Vincze2d736ad2019-02-18 11:50:22 +0100264 warn!("Mismatched trailer for the primary slot");
David Brown5c9e0f12019-01-09 16:34:33 -0700265 fails += 1;
266 }
267
268 // Run the bootloader...
269 let (result, _) = c::boot_go(&mut flashmap, &self.areadesc, None, false);
270 if result != 0 {
271 warn!("Failed first boot");
272 fails += 1;
273 }
274
275 // State should not have changed
276 if !verify_image(&flashmap, &self.slots, 0, &self.primaries) {
277 warn!("Failed image verification");
278 fails += 1;
279 }
280 if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
281 BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
David Vincze2d736ad2019-02-18 11:50:22 +0100282 warn!("Mismatched trailer for the primary slot");
David Brown5c9e0f12019-01-09 16:34:33 -0700283 fails += 1;
284 }
285 if !verify_trailer(&flashmap, &self.slots, 1, BOOT_MAGIC_UNSET,
286 BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
David Vincze2d736ad2019-02-18 11:50:22 +0100287 warn!("Mismatched trailer for the secondary slot");
David Brown5c9e0f12019-01-09 16:34:33 -0700288 fails += 1;
289 }
290
291 if fails > 0 {
292 error!("Expected a non revert with new image");
293 }
294
295 fails > 0
296 }
297
David Vincze2d736ad2019-02-18 11:50:22 +0100298 // Tests a new image written to the primary slot that already has magic and
299 // image_ok set while there is no image on the secondary slot, so no revert
300 // should ever happen...
David Brown5c9e0f12019-01-09 16:34:33 -0700301 pub fn run_signfail_upgrade(&self) -> bool {
302 let mut flashmap = self.flashmap.clone();
303 let mut fails = 0;
304
305 info!("Try upgrade image with bad signature");
306
307 mark_upgrade(&mut flashmap, &self.slots[0]);
308 mark_permanent_upgrade(&mut flashmap, &self.slots[0]);
309 mark_upgrade(&mut flashmap, &self.slots[1]);
310
311 if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
312 BOOT_FLAG_SET, BOOT_FLAG_UNSET) {
David Vincze2d736ad2019-02-18 11:50:22 +0100313 warn!("Mismatched trailer for the primary slot");
David Brown5c9e0f12019-01-09 16:34:33 -0700314 fails += 1;
315 }
316
317 // Run the bootloader...
318 let (result, _) = c::boot_go(&mut flashmap, &self.areadesc, None, false);
319 if result != 0 {
320 warn!("Failed first boot");
321 fails += 1;
322 }
323
324 // State should not have changed
325 if !verify_image(&flashmap, &self.slots, 0, &self.primaries) {
326 warn!("Failed image verification");
327 fails += 1;
328 }
329 if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
330 BOOT_FLAG_SET, BOOT_FLAG_UNSET) {
David Vincze2d736ad2019-02-18 11:50:22 +0100331 warn!("Mismatched trailer for the primary slot");
David Brown5c9e0f12019-01-09 16:34:33 -0700332 fails += 1;
333 }
334
335 if fails > 0 {
336 error!("Expected an upgrade failure when image has bad signature");
337 }
338
339 fails > 0
340 }
341
David Brown5c9e0f12019-01-09 16:34:33 -0700342 fn trailer_sz(&self, align: usize) -> usize {
343 c::boot_trailer_sz(align as u8) as usize
344 }
345
346 // FIXME: could get status sz from bootloader
David Brown5c9e0f12019-01-09 16:34:33 -0700347 fn status_sz(&self, align: usize) -> usize {
David Brown9930a3e2019-01-11 12:28:26 -0700348 let bias = if Caps::EncRsa.present() || Caps::EncKw.present() {
349 32
350 } else {
351 0
352 };
David Brown5c9e0f12019-01-09 16:34:33 -0700353
David Brown9930a3e2019-01-11 12:28:26 -0700354 self.trailer_sz(align) - (16 + 24 + bias)
David Brown5c9e0f12019-01-09 16:34:33 -0700355 }
356
357 /// This test runs a simple upgrade with no fails in the images, but
358 /// allowing for fails in the status area. This should run to the end
359 /// and warn that write fails were detected...
David Brown5c9e0f12019-01-09 16:34:33 -0700360 pub fn run_with_status_fails_complete(&self) -> bool {
David Vincze2d736ad2019-02-18 11:50:22 +0100361 if !Caps::ValidatePrimarySlot.present() {
David Brown85904a82019-01-11 13:45:12 -0700362 return false;
363 }
364
David Brown5c9e0f12019-01-09 16:34:33 -0700365 let mut flashmap = self.flashmap.clone();
366 let mut fails = 0;
367
368 info!("Try swap with status fails");
369
370 mark_permanent_upgrade(&mut flashmap, &self.slots[1]);
371 self.mark_bad_status_with_rate(&mut flashmap, 0, 1.0);
372
373 let (result, asserts) = c::boot_go(&mut flashmap, &self.areadesc, None, true);
374 if result != 0 {
375 warn!("Failed!");
376 fails += 1;
377 }
378
379 // Failed writes to the marked "bad" region don't assert anymore.
380 // Any detected assert() is happening in another part of the code.
381 if asserts != 0 {
382 warn!("At least one assert() was called");
383 fails += 1;
384 }
385
386 if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
387 BOOT_FLAG_SET, BOOT_FLAG_SET) {
David Vincze2d736ad2019-02-18 11:50:22 +0100388 warn!("Mismatched trailer for the primary slot");
David Brown5c9e0f12019-01-09 16:34:33 -0700389 fails += 1;
390 }
391
392 if !verify_image(&flashmap, &self.slots, 0, &self.upgrades) {
393 warn!("Failed image verification");
394 fails += 1;
395 }
396
David Vincze2d736ad2019-02-18 11:50:22 +0100397 info!("validate primary slot enabled; \
398 re-run of boot_go should just work");
David Brown5c9e0f12019-01-09 16:34:33 -0700399 let (result, _) = c::boot_go(&mut flashmap, &self.areadesc, None, false);
400 if result != 0 {
401 warn!("Failed!");
402 fails += 1;
403 }
404
405 if fails > 0 {
406 error!("Error running upgrade with status write fails");
407 }
408
409 fails > 0
410 }
411
412 /// This test runs a simple upgrade with no fails in the images, but
413 /// allowing for fails in the status area. This should run to the end
414 /// and warn that write fails were detected...
David Brown5c9e0f12019-01-09 16:34:33 -0700415 pub fn run_with_status_fails_with_reset(&self) -> bool {
David Brown85904a82019-01-11 13:45:12 -0700416 if Caps::OverwriteUpgrade.present() {
417 false
David Vincze2d736ad2019-02-18 11:50:22 +0100418 } else if Caps::ValidatePrimarySlot.present() {
David Brown5c9e0f12019-01-09 16:34:33 -0700419
David Brown85904a82019-01-11 13:45:12 -0700420 let mut flashmap = self.flashmap.clone();
421 let mut fails = 0;
422 let mut count = self.total_count.unwrap() / 2;
David Brown5c9e0f12019-01-09 16:34:33 -0700423
David Brown85904a82019-01-11 13:45:12 -0700424 //info!("count={}\n", count);
David Brown5c9e0f12019-01-09 16:34:33 -0700425
David Brown85904a82019-01-11 13:45:12 -0700426 info!("Try interrupted swap with status fails");
David Brown5c9e0f12019-01-09 16:34:33 -0700427
David Brown85904a82019-01-11 13:45:12 -0700428 mark_permanent_upgrade(&mut flashmap, &self.slots[1]);
429 self.mark_bad_status_with_rate(&mut flashmap, 0, 0.5);
430
431 // Should not fail, writing to bad regions does not assert
432 let (_, asserts) = c::boot_go(&mut flashmap, &self.areadesc, Some(&mut count), true);
433 if asserts != 0 {
434 warn!("At least one assert() was called");
435 fails += 1;
436 }
437
438 self.reset_bad_status(&mut flashmap, 0);
439
440 info!("Resuming an interrupted swap operation");
441 let (_, asserts) = c::boot_go(&mut flashmap, &self.areadesc, None, true);
442
443 // This might throw no asserts, for large sector devices, where
444 // a single failure writing is indistinguishable from no failure,
445 // or throw a single assert for small sector devices that fail
446 // multiple times...
447 if asserts > 1 {
David Vincze2d736ad2019-02-18 11:50:22 +0100448 warn!("Expected single assert validating the primary slot, \
449 more detected {}", asserts);
David Brown85904a82019-01-11 13:45:12 -0700450 fails += 1;
451 }
452
453 if fails > 0 {
454 error!("Error running upgrade with status write fails");
455 }
456
457 fails > 0
458 } else {
459 let mut flashmap = self.flashmap.clone();
460 let mut fails = 0;
461
462 info!("Try interrupted swap with status fails");
463
464 mark_permanent_upgrade(&mut flashmap, &self.slots[1]);
465 self.mark_bad_status_with_rate(&mut flashmap, 0, 1.0);
466
467 // This is expected to fail while writing to bad regions...
468 let (_, asserts) = c::boot_go(&mut flashmap, &self.areadesc, None, true);
469 if asserts == 0 {
470 warn!("No assert() detected");
471 fails += 1;
472 }
473
474 fails > 0
David Brown5c9e0f12019-01-09 16:34:33 -0700475 }
David Brown5c9e0f12019-01-09 16:34:33 -0700476 }
477
478 /// Adds a new flash area that fails statistically
David Brown5c9e0f12019-01-09 16:34:33 -0700479 fn mark_bad_status_with_rate(&self, flashmap: &mut SimFlashMap, slot: usize,
480 rate: f32) {
David Brown85904a82019-01-11 13:45:12 -0700481 if Caps::OverwriteUpgrade.present() {
482 return;
483 }
484
David Brown5c9e0f12019-01-09 16:34:33 -0700485 let dev_id = &self.slots[slot].dev_id;
486 let flash = flashmap.get_mut(&dev_id).unwrap();
487 let align = flash.align();
488 let off = &self.slots[0].base_off;
489 let len = &self.slots[0].len;
490 let status_off = off + len - self.trailer_sz(align);
491
492 // Mark the status area as a bad area
493 let _ = flash.add_bad_region(status_off, self.status_sz(align), rate);
494 }
495
David Brown5c9e0f12019-01-09 16:34:33 -0700496 fn reset_bad_status(&self, flashmap: &mut SimFlashMap, slot: usize) {
David Vincze2d736ad2019-02-18 11:50:22 +0100497 if !Caps::ValidatePrimarySlot.present() {
David Brown85904a82019-01-11 13:45:12 -0700498 return;
499 }
500
David Brown5c9e0f12019-01-09 16:34:33 -0700501 let dev_id = &self.slots[slot].dev_id;
502 let flash = flashmap.get_mut(&dev_id).unwrap();
503 flash.reset_bad_regions();
504
505 // Disabling write verification the only assert triggered by
506 // boot_go should be checking for integrity of status bytes.
507 flash.set_verify_writes(false);
508 }
509
David Brown5c9e0f12019-01-09 16:34:33 -0700510}
511
512/// Test a boot, optionally stopping after 'n' flash options. Returns a count
513/// of the number of flash operations done total.
514fn try_upgrade(flashmap: &SimFlashMap, images: &Images,
515 stop: Option<i32>) -> (SimFlashMap, i32) {
516 // Clone the flash to have a new copy.
517 let mut flashmap = flashmap.clone();
518
519 mark_permanent_upgrade(&mut flashmap, &images.slots[1]);
520
521 let mut counter = stop.unwrap_or(0);
522
523 let (first_interrupted, count) = match c::boot_go(&mut flashmap, &images.areadesc, Some(&mut counter), false) {
524 (-0x13579, _) => (true, stop.unwrap()),
525 (0, _) => (false, -counter),
526 (x, _) => panic!("Unknown return: {}", x),
527 };
528
529 counter = 0;
530 if first_interrupted {
531 // fl.dump();
532 match c::boot_go(&mut flashmap, &images.areadesc, Some(&mut counter), false) {
533 (-0x13579, _) => panic!("Shouldn't stop again"),
534 (0, _) => (),
535 (x, _) => panic!("Unknown return: {}", x),
536 }
537 }
538
539 (flashmap, count - counter)
540}
541
David Brown5c9e0f12019-01-09 16:34:33 -0700542fn try_revert(flashmap: &SimFlashMap, areadesc: &AreaDesc, count: usize) -> SimFlashMap {
543 let mut flashmap = flashmap.clone();
544
545 // fl.write_file("image0.bin").unwrap();
546 for i in 0 .. count {
547 info!("Running boot pass {}", i + 1);
548 assert_eq!(c::boot_go(&mut flashmap, &areadesc, None, false), (0, 0));
549 }
550 flashmap
551}
552
David Brown5c9e0f12019-01-09 16:34:33 -0700553fn try_revert_with_fail_at(flashmap: &SimFlashMap, images: &Images,
554 stop: i32) -> bool {
555 let mut flashmap = flashmap.clone();
556 let mut fails = 0;
557
558 let mut counter = stop;
559 let (x, _) = c::boot_go(&mut flashmap, &images.areadesc, Some(&mut counter), false);
560 if x != -0x13579 {
561 warn!("Should have stopped at interruption point");
562 fails += 1;
563 }
564
565 if !verify_trailer(&flashmap, &images.slots, 0, None, None, BOOT_FLAG_UNSET) {
566 warn!("copy_done should be unset");
567 fails += 1;
568 }
569
570 let (x, _) = c::boot_go(&mut flashmap, &images.areadesc, None, false);
571 if x != 0 {
572 warn!("Should have finished upgrade");
573 fails += 1;
574 }
575
576 if !verify_image(&flashmap, &images.slots, 0, &images.upgrades) {
David Vincze2d736ad2019-02-18 11:50:22 +0100577 warn!("Image in the primary slot before revert is invalid at stop={}",
578 stop);
David Brown5c9e0f12019-01-09 16:34:33 -0700579 fails += 1;
580 }
581 if !verify_image(&flashmap, &images.slots, 1, &images.primaries) {
David Vincze2d736ad2019-02-18 11:50:22 +0100582 warn!("Image in the secondary slot before revert is invalid at stop={}",
583 stop);
David Brown5c9e0f12019-01-09 16:34:33 -0700584 fails += 1;
585 }
586 if !verify_trailer(&flashmap, &images.slots, 0, BOOT_MAGIC_GOOD,
587 BOOT_FLAG_UNSET, BOOT_FLAG_SET) {
David Vincze2d736ad2019-02-18 11:50:22 +0100588 warn!("Mismatched trailer for the primary slot before revert");
David Brown5c9e0f12019-01-09 16:34:33 -0700589 fails += 1;
590 }
591 if !verify_trailer(&flashmap, &images.slots, 1, BOOT_MAGIC_UNSET,
592 BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
David Vincze2d736ad2019-02-18 11:50:22 +0100593 warn!("Mismatched trailer for the secondary slot before revert");
David Brown5c9e0f12019-01-09 16:34:33 -0700594 fails += 1;
595 }
596
597 // Do Revert
598 let (x, _) = c::boot_go(&mut flashmap, &images.areadesc, None, false);
599 if x != 0 {
600 warn!("Should have finished a revert");
601 fails += 1;
602 }
603
604 if !verify_image(&flashmap, &images.slots, 0, &images.primaries) {
David Vincze2d736ad2019-02-18 11:50:22 +0100605 warn!("Image in the primary slot after revert is invalid at stop={}",
606 stop);
David Brown5c9e0f12019-01-09 16:34:33 -0700607 fails += 1;
608 }
609 if !verify_image(&flashmap, &images.slots, 1, &images.upgrades) {
David Vincze2d736ad2019-02-18 11:50:22 +0100610 warn!("Image in the secondary slot after revert is invalid at stop={}",
611 stop);
David Brown5c9e0f12019-01-09 16:34:33 -0700612 fails += 1;
613 }
614 if !verify_trailer(&flashmap, &images.slots, 0, BOOT_MAGIC_GOOD,
615 BOOT_FLAG_SET, BOOT_FLAG_SET) {
David Vincze2d736ad2019-02-18 11:50:22 +0100616 warn!("Mismatched trailer for the secondary slot after revert");
David Brown5c9e0f12019-01-09 16:34:33 -0700617 fails += 1;
618 }
619 if !verify_trailer(&flashmap, &images.slots, 1, BOOT_MAGIC_UNSET,
620 BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
David Vincze2d736ad2019-02-18 11:50:22 +0100621 warn!("Mismatched trailer for the secondary slot after revert");
David Brown5c9e0f12019-01-09 16:34:33 -0700622 fails += 1;
623 }
624
625 fails > 0
626}
627
628fn try_random_fails(flashmap: &SimFlashMap, images: &Images,
629 total_ops: i32, count: usize) -> (SimFlashMap, Vec<i32>) {
630 let mut flashmap = flashmap.clone();
631
632 mark_permanent_upgrade(&mut flashmap, &images.slots[1]);
633
634 let mut rng = rand::thread_rng();
635 let mut resets = vec![0i32; count];
636 let mut remaining_ops = total_ops;
637 for i in 0 .. count {
638 let ops = Range::new(1, remaining_ops / 2);
639 let reset_counter = ops.ind_sample(&mut rng);
640 let mut counter = reset_counter;
641 match c::boot_go(&mut flashmap, &images.areadesc, Some(&mut counter), false) {
642 (0, _) | (-0x13579, _) => (),
643 (x, _) => panic!("Unknown return: {}", x),
644 }
645 remaining_ops -= reset_counter;
646 resets[i] = reset_counter;
647 }
648
649 match c::boot_go(&mut flashmap, &images.areadesc, None, false) {
650 (-0x13579, _) => panic!("Should not be have been interrupted!"),
651 (0, _) => (),
652 (x, _) => panic!("Unknown return: {}", x),
653 }
654
655 (flashmap, resets)
656}
657
658/// Show the flash layout.
659#[allow(dead_code)]
660fn show_flash(flash: &dyn Flash) {
661 println!("---- Flash configuration ----");
662 for sector in flash.sector_iter() {
663 println!(" {:3}: 0x{:08x}, 0x{:08x}",
664 sector.num, sector.base, sector.size);
665 }
666 println!("");
667}
668
669/// Install a "program" into the given image. This fakes the image header, or at least all of the
670/// fields used by the given code. Returns a copy of the image that was written.
671pub fn install_image(flashmap: &mut SimFlashMap, slots: &[SlotInfo], slot: usize, len: usize,
672 bad_sig: bool) -> [Option<Vec<u8>>; 2] {
673 let offset = slots[slot].base_off;
674 let slot_len = slots[slot].len;
675 let dev_id = slots[slot].dev_id;
676
David Brown43643dd2019-01-11 15:43:28 -0700677 let mut tlv: Box<dyn ManifestGen> = Box::new(make_tlv());
David Brown5c9e0f12019-01-09 16:34:33 -0700678
679 const HDR_SIZE: usize = 32;
680
681 // Generate a boot header. Note that the size doesn't include the header.
682 let header = ImageHeader {
David Brownac46e262019-01-11 15:46:18 -0700683 magic: tlv.get_magic(),
David Brown5c9e0f12019-01-09 16:34:33 -0700684 load_addr: 0,
685 hdr_size: HDR_SIZE as u16,
686 _pad1: 0,
687 img_size: len as u32,
688 flags: tlv.get_flags(),
689 ver: ImageVersion {
690 major: (offset / (128 * 1024)) as u8,
691 minor: 0,
692 revision: 1,
693 build_num: offset as u32,
694 },
695 _pad2: 0,
696 };
697
698 let mut b_header = [0; HDR_SIZE];
699 b_header[..32].clone_from_slice(header.as_raw());
700 assert_eq!(b_header.len(), HDR_SIZE);
701
702 tlv.add_bytes(&b_header);
703
704 // The core of the image itself is just pseudorandom data.
705 let mut b_img = vec![0; len];
706 splat(&mut b_img, offset);
707
708 // TLV signatures work over plain image
709 tlv.add_bytes(&b_img);
710
711 // Generate encrypted images
712 let flag = TlvFlags::ENCRYPTED as u32;
713 let is_encrypted = (tlv.get_flags() & flag) == flag;
714 let mut b_encimg = vec![];
715 if is_encrypted {
716 let key = GenericArray::from_slice(AES_SEC_KEY);
717 let nonce = GenericArray::from_slice(&[0; 16]);
718 let mut cipher = Aes128Ctr::new(&key, &nonce);
719 b_encimg = b_img.clone();
720 cipher.apply_keystream(&mut b_encimg);
721 }
722
723 // Build the TLV itself.
724 let mut b_tlv = if bad_sig {
725 let good_sig = &mut tlv.make_tlv();
726 vec![0; good_sig.len()]
727 } else {
728 tlv.make_tlv()
729 };
730
731 // Pad the block to a flash alignment (8 bytes).
732 while b_tlv.len() % 8 != 0 {
733 //FIXME: should be erase_val?
734 b_tlv.push(0xFF);
735 }
736
737 let mut buf = vec![];
738 buf.append(&mut b_header.to_vec());
739 buf.append(&mut b_img);
740 buf.append(&mut b_tlv.clone());
741
742 let mut encbuf = vec![];
743 if is_encrypted {
744 encbuf.append(&mut b_header.to_vec());
745 encbuf.append(&mut b_encimg);
746 encbuf.append(&mut b_tlv);
747 }
748
749 let result: [Option<Vec<u8>>; 2];
750
David Vincze2d736ad2019-02-18 11:50:22 +0100751 // Since images are always non-encrypted in the primary slot, we first write
752 // an encrypted image, re-read to use for verification, erase + flash
753 // un-encrypted. In the secondary slot the image is written un-encrypted,
754 // and if encryption is requested, it follows an erase + flash encrypted.
David Brown5c9e0f12019-01-09 16:34:33 -0700755
756 let flash = flashmap.get_mut(&dev_id).unwrap();
757
758 if slot == 0 {
759 let enc_copy: Option<Vec<u8>>;
760
761 if is_encrypted {
762 flash.write(offset, &encbuf).unwrap();
763
764 let mut enc = vec![0u8; encbuf.len()];
765 flash.read(offset, &mut enc).unwrap();
766
767 enc_copy = Some(enc);
768
769 flash.erase(offset, slot_len).unwrap();
770 } else {
771 enc_copy = None;
772 }
773
774 flash.write(offset, &buf).unwrap();
775
776 let mut copy = vec![0u8; buf.len()];
777 flash.read(offset, &mut copy).unwrap();
778
779 result = [Some(copy), enc_copy];
780 } else {
781
782 flash.write(offset, &buf).unwrap();
783
784 let mut copy = vec![0u8; buf.len()];
785 flash.read(offset, &mut copy).unwrap();
786
787 let enc_copy: Option<Vec<u8>>;
788
789 if is_encrypted {
790 flash.erase(offset, slot_len).unwrap();
791
792 flash.write(offset, &encbuf).unwrap();
793
794 let mut enc = vec![0u8; encbuf.len()];
795 flash.read(offset, &mut enc).unwrap();
796
797 enc_copy = Some(enc);
798 } else {
799 enc_copy = None;
800 }
801
802 result = [Some(copy), enc_copy];
803 }
804
805 result
806}
807
David Brown5c9e0f12019-01-09 16:34:33 -0700808fn make_tlv() -> TlvGen {
David Brownb8882112019-01-11 14:04:11 -0700809 if Caps::EcdsaP224.present() {
810 panic!("Ecdsa P224 not supported in Simulator");
811 }
David Brown5c9e0f12019-01-09 16:34:33 -0700812
David Brownb8882112019-01-11 14:04:11 -0700813 if Caps::EncKw.present() {
814 if Caps::RSA2048.present() {
815 TlvGen::new_rsa_kw()
816 } else if Caps::EcdsaP256.present() {
817 TlvGen::new_ecdsa_kw()
818 } else {
819 TlvGen::new_enc_kw()
820 }
821 } else if Caps::EncRsa.present() {
822 if Caps::RSA2048.present() {
823 TlvGen::new_sig_enc_rsa()
824 } else {
825 TlvGen::new_enc_rsa()
826 }
827 } else {
828 // The non-encrypted configuration.
829 if Caps::RSA2048.present() {
830 TlvGen::new_rsa_pss()
831 } else if Caps::EcdsaP256.present() {
832 TlvGen::new_ecdsa()
833 } else {
834 TlvGen::new_hash_only()
835 }
836 }
David Brown5c9e0f12019-01-09 16:34:33 -0700837}
838
David Brown5c9e0f12019-01-09 16:34:33 -0700839fn find_image(images: &[Option<Vec<u8>>; 2], slot: usize) -> &Vec<u8> {
David Brownf38bc342019-01-11 14:07:58 -0700840 let slot = if Caps::EncRsa.present() || Caps::EncKw.present() {
841 slot
842 } else {
843 0
844 };
David Brown5c9e0f12019-01-09 16:34:33 -0700845
David Brown5c9e0f12019-01-09 16:34:33 -0700846 match &images[slot] {
847 Some(image) => return image,
848 None => panic!("Invalid image"),
849 }
850}
851
David Brown5c9e0f12019-01-09 16:34:33 -0700852/// Verify that given image is present in the flash at the given offset.
853fn verify_image(flashmap: &SimFlashMap, slots: &[SlotInfo], slot: usize,
854 images: &[Option<Vec<u8>>; 2]) -> bool {
855 let image = find_image(images, slot);
856 let buf = image.as_slice();
857 let dev_id = slots[slot].dev_id;
858
859 let mut copy = vec![0u8; buf.len()];
860 let offset = slots[slot].base_off;
861 let flash = flashmap.get(&dev_id).unwrap();
862 flash.read(offset, &mut copy).unwrap();
863
864 if buf != &copy[..] {
865 for i in 0 .. buf.len() {
866 if buf[i] != copy[i] {
867 info!("First failure for slot{} at {:#x} {:#x}!={:#x}",
868 slot, offset + i, buf[i], copy[i]);
869 break;
870 }
871 }
872 false
873 } else {
874 true
875 }
876}
877
David Brown5c9e0f12019-01-09 16:34:33 -0700878fn verify_trailer(flashmap: &SimFlashMap, slots: &[SlotInfo], slot: usize,
879 magic: Option<u8>, image_ok: Option<u8>,
880 copy_done: Option<u8>) -> bool {
David Brown61a540d2019-01-11 14:29:14 -0700881 if Caps::OverwriteUpgrade.present() {
882 return true;
883 }
David Brown5c9e0f12019-01-09 16:34:33 -0700884
David Brown5c9e0f12019-01-09 16:34:33 -0700885 let offset = slots[slot].trailer_off;
886 let dev_id = slots[slot].dev_id;
887 let mut copy = vec![0u8; c::boot_magic_sz() + c::boot_max_align() * 2];
888 let mut failed = false;
889
890 let flash = flashmap.get(&dev_id).unwrap();
891 let erased_val = flash.erased_val();
892 flash.read(offset, &mut copy).unwrap();
893
894 failed |= match magic {
895 Some(v) => {
896 if v == 1 && &copy[16..] != MAGIC.unwrap() {
897 warn!("\"magic\" mismatch at {:#x}", offset);
898 true
899 } else if v == 3 {
900 let expected = [erased_val; 16];
901 if &copy[16..] != expected {
902 warn!("\"magic\" mismatch at {:#x}", offset);
903 true
904 } else {
905 false
906 }
907 } else {
908 false
909 }
910 },
911 None => false,
912 };
913
914 failed |= match image_ok {
915 Some(v) => {
916 if (v == 1 && copy[8] != v) || (v == 3 && copy[8] != erased_val) {
917 warn!("\"image_ok\" mismatch at {:#x} v={} val={:#x}", offset, v, copy[8]);
918 true
919 } else {
920 false
921 }
922 },
923 None => false,
924 };
925
926 failed |= match copy_done {
927 Some(v) => {
928 if (v == 1 && copy[0] != v) || (v == 3 && copy[0] != erased_val) {
929 warn!("\"copy_done\" mismatch at {:#x} v={} val={:#x}", offset, v, copy[0]);
930 true
931 } else {
932 false
933 }
934 },
935 None => false,
936 };
937
938 !failed
939}
940
941/// The image header
942#[repr(C)]
943pub struct ImageHeader {
944 magic: u32,
945 load_addr: u32,
946 hdr_size: u16,
947 _pad1: u16,
948 img_size: u32,
949 flags: u32,
950 ver: ImageVersion,
951 _pad2: u32,
952}
953
954impl AsRaw for ImageHeader {}
955
956#[repr(C)]
957pub struct ImageVersion {
958 major: u8,
959 minor: u8,
960 revision: u16,
961 build_num: u32,
962}
963
964#[derive(Clone)]
965pub struct SlotInfo {
966 pub base_off: usize,
967 pub trailer_off: usize,
968 pub len: usize,
969 pub dev_id: u8,
970}
971
David Brown5c9e0f12019-01-09 16:34:33 -0700972const MAGIC: Option<&[u8]> = Some(&[0x77, 0xc2, 0x95, 0xf3,
973 0x60, 0xd2, 0xef, 0x7f,
974 0x35, 0x52, 0x50, 0x0f,
975 0x2c, 0xb6, 0x79, 0x80]);
976
977// Replicates defines found in bootutil.h
978const BOOT_MAGIC_GOOD: Option<u8> = Some(1);
979const BOOT_MAGIC_UNSET: Option<u8> = Some(3);
980
981const BOOT_FLAG_SET: Option<u8> = Some(1);
982const BOOT_FLAG_UNSET: Option<u8> = Some(3);
983
984/// Write out the magic so that the loader tries doing an upgrade.
985pub fn mark_upgrade(flashmap: &mut SimFlashMap, slot: &SlotInfo) {
986 let flash = flashmap.get_mut(&slot.dev_id).unwrap();
987 let offset = slot.trailer_off + c::boot_max_align() * 2;
988 flash.write(offset, MAGIC.unwrap()).unwrap();
989}
990
991/// Writes the image_ok flag which, guess what, tells the bootloader
992/// the this image is ok (not a test, and no revert is to be performed).
993fn mark_permanent_upgrade(flashmap: &mut SimFlashMap, slot: &SlotInfo) {
994 let flash = flashmap.get_mut(&slot.dev_id).unwrap();
995 let mut ok = [flash.erased_val(); 8];
996 ok[0] = 1u8;
997 let off = slot.trailer_off + c::boot_max_align();
998 let align = flash.align();
999 flash.write(off, &ok[..align]).unwrap();
1000}
1001
1002// Drop some pseudo-random gibberish onto the data.
1003fn splat(data: &mut [u8], seed: usize) {
1004 let seed_block = [0x135782ea, 0x92184728, data.len() as u32, seed as u32];
1005 let mut rng: XorShiftRng = SeedableRng::from_seed(seed_block);
1006 rng.fill_bytes(data);
1007}
1008
1009/// Return a read-only view into the raw bytes of this object
1010trait AsRaw : Sized {
1011 fn as_raw<'a>(&'a self) -> &'a [u8] {
1012 unsafe { slice::from_raw_parts(self as *const _ as *const u8,
1013 mem::size_of::<Self>()) }
1014 }
1015}
1016
1017pub fn show_sizes() {
1018 // This isn't panic safe.
1019 for min in &[1, 2, 4, 8] {
1020 let msize = c::boot_trailer_sz(*min);
1021 println!("{:2}: {} (0x{:x})", min, msize, msize);
1022 }
1023}