Update main simulator routines for multi-flash

This adds an initial device with multiple flash (nrf52840 + SPI flash)
and updates all test routines to use a HashMap of flash devices (added
as type SimFlashMap).

Signed-off-by: Fabio Utzig <utzig@apache.org>
diff --git a/sim/mcuboot-sys/src/api.rs b/sim/mcuboot-sys/src/api.rs
index 1c34b5e..3914b22 100644
--- a/sim/mcuboot-sys/src/api.rs
+++ b/sim/mcuboot-sys/src/api.rs
@@ -10,7 +10,7 @@
 use std::ops::Deref;
 
 /// A FlashMap maintain a table of [device_id -> Flash trait]
-type FlashMap = HashMap<u8, FlashPtr>;
+pub type FlashMap = HashMap<u8, FlashPtr>;
 
 lazy_static! {
     static ref FLASH: Mutex<FlashMap> = {
diff --git a/sim/mcuboot-sys/src/area.rs b/sim/mcuboot-sys/src/area.rs
index 841bc78..8666aa1 100644
--- a/sim/mcuboot-sys/src/area.rs
+++ b/sim/mcuboot-sys/src/area.rs
@@ -106,13 +106,13 @@
         });
     }
 
-    // Look for the image with the given ID, and return its base and size.  Panics if the area is
-    // not present.
-    pub fn find(&self, id: FlashId) -> (usize, usize) {
+    // Look for the image with the given ID, and return its offset, size and
+    // device id. Panics if the area is not present.
+    pub fn find(&self, id: FlashId) -> (usize, usize, u8) {
         for area in &self.whole {
             // FIXME: should we ensure id is not duplicated over multiple devices?
             if area.flash_id == id {
-                return (area.off as usize, area.size as usize);
+                return (area.off as usize, area.size as usize, area.device_id);
             }
         }
         panic!("Requesting area that is not present in flash");
diff --git a/sim/mcuboot-sys/src/c.rs b/sim/mcuboot-sys/src/c.rs
index 65ff71f..d828607 100644
--- a/sim/mcuboot-sys/src/c.rs
+++ b/sim/mcuboot-sys/src/c.rs
@@ -1,7 +1,7 @@
 /// Interface wrappers to C API entering to the bootloader
 
 use area::AreaDesc;
-use simflash::Flash;
+use simflash::SimFlashMap;
 use libc;
 use api;
 use std::sync::Mutex;
@@ -13,12 +13,14 @@
 }
 
 /// Invoke the bootloader on this flash device.
-pub fn boot_go(flash: &mut Flash, areadesc: &AreaDesc, counter: Option<&mut i32>,
-               catch_asserts: bool) -> (i32, u8) {
+pub fn boot_go(flashmap: &mut SimFlashMap, areadesc: &AreaDesc,
+               counter: Option<&mut i32>, catch_asserts: bool) -> (i32, u8) {
     let _lock = BOOT_LOCK.lock().unwrap();
 
     unsafe {
-        api::set_flash(0, flash);
+        for (&dev_id, flash) in flashmap.iter_mut() {
+            api::set_flash(dev_id, flash);
+        }
         raw::c_catch_asserts = if catch_asserts { 1 } else { 0 };
         raw::c_asserts = 0u8;
         raw::flash_counter = match counter {
@@ -30,7 +32,9 @@
     let asserts = unsafe { raw::c_asserts };
     unsafe {
         counter.map(|c| *c = raw::flash_counter as i32);
-        api::clear_flash(0);
+        for (&dev_id, _) in flashmap {
+            api::clear_flash(dev_id);
+        }
     };
     (result, asserts)
 }
diff --git a/sim/simflash/src/lib.rs b/sim/simflash/src/lib.rs
index 1e9876c..df1020e 100644
--- a/sim/simflash/src/lib.rs
+++ b/sim/simflash/src/lib.rs
@@ -14,6 +14,7 @@
 use std::iter::Enumerate;
 use std::path::Path;
 use std::slice;
+use std::collections::HashMap;
 use pdump::HexDump;
 
 error_chain! {
@@ -130,6 +131,8 @@
 
 }
 
+pub type SimFlashMap = HashMap<u8, SimFlash>;
+
 impl Flash for SimFlash {
     /// The flash drivers tend to erase beyond the bounds of the given range.  Instead, we'll be
     /// strict, and make sure that the passed arguments are exactly at a sector boundary, otherwise
diff --git a/sim/src/lib.rs b/sim/src/lib.rs
index 992dcee..deac61e 100644
--- a/sim/src/lib.rs
+++ b/sim/src/lib.rs
@@ -28,7 +28,7 @@
 mod tlv;
 pub mod testlog;
 
-use simflash::{Flash, SimFlash};
+use simflash::{Flash, SimFlash, SimFlashMap};
 use mcuboot_sys::{c, AreaDesc, FlashId};
 use caps::Caps;
 use tlv::{TlvGen, TlvFlags, AES_SEC_KEY};
@@ -62,13 +62,14 @@
 }
 
 #[derive(Copy, Clone, Debug, Deserialize)]
-pub enum DeviceName { Stm32f4, K64f, K64fBig, Nrf52840 }
+pub enum DeviceName { Stm32f4, K64f, K64fBig, Nrf52840, Nrf52840SpiFlash, }
 
 pub static ALL_DEVICES: &'static [DeviceName] = &[
     DeviceName::Stm32f4,
     DeviceName::K64f,
     DeviceName::K64fBig,
     DeviceName::Nrf52840,
+    DeviceName::Nrf52840SpiFlash,
 ];
 
 impl fmt::Display for DeviceName {
@@ -78,6 +79,7 @@
             DeviceName::K64f => "k64f",
             DeviceName::K64fBig => "k64fbig",
             DeviceName::Nrf52840 => "nrf52840",
+            DeviceName::Nrf52840SpiFlash => "Nrf52840SpiFlash",
         };
         f.write_str(name)
     }
@@ -162,22 +164,17 @@
 
 /// A test run, intended to be run from "cargo test", so panics on failure.
 pub struct Run {
-    flash: SimFlash,
+    flashmap: SimFlashMap,
     areadesc: AreaDesc,
     slots: [SlotInfo; 2],
 }
 
 impl Run {
     pub fn new(device: DeviceName, align: u8, erased_val: u8) -> Run {
-        let (flash, areadesc) = make_device(device, align, erased_val);
+        let (flashmap, areadesc) = make_device(device, align, erased_val);
 
-        let (slot0_base, slot0_len) = areadesc.find(FlashId::Image0);
-        let (slot1_base, slot1_len) = areadesc.find(FlashId::Image1);
-        let (scratch_base, _) = areadesc.find(FlashId::ImageScratch);
-
-        // The code assumes that the slots are consecutive.
-        assert_eq!(slot1_base, slot0_base + slot0_len);
-        assert_eq!(scratch_base, slot1_base + slot1_len);
+        let (slot0_base, slot0_len, slot0_dev_id) = areadesc.find(FlashId::Image0);
+        let (slot1_base, slot1_len, slot1_dev_id) = areadesc.find(FlashId::Image1);
 
         // NOTE: not accounting "swap_size" because it is not used by sim...
         let offset_from_end = c::boot_magic_sz() + c::boot_max_align() * 2;
@@ -185,19 +182,21 @@
         // Construct a primary image.
         let slot0 = SlotInfo {
             base_off: slot0_base as usize,
-            trailer_off: slot1_base - offset_from_end,
+            trailer_off: slot0_base + slot0_len - offset_from_end,
             len: slot0_len as usize,
+            dev_id: slot0_dev_id,
         };
 
         // And an upgrade image.
         let slot1 = SlotInfo {
             base_off: slot1_base as usize,
-            trailer_off: scratch_base - offset_from_end,
+            trailer_off: slot1_base + slot1_len - offset_from_end,
             len: slot1_len as usize,
+            dev_id: slot1_dev_id,
         };
 
         Run {
-            flash: flash,
+            flashmap: flashmap,
             areadesc: areadesc,
             slots: [slot0, slot1],
         }
@@ -218,11 +217,11 @@
 
     /// Construct an `Images` that doesn't expect an upgrade to happen.
     pub fn make_no_upgrade_image(&self) -> Images {
-        let mut flash = self.flash.clone();
-        let primaries = install_image(&mut flash, &self.slots, 0, 32784, false);
-        let upgrades = install_image(&mut flash, &self.slots, 1, 41928, false);
+        let mut flashmap = self.flashmap.clone();
+        let primaries = install_image(&mut flashmap, &self.slots, 0, 32784, false);
+        let upgrades = install_image(&mut flashmap, &self.slots, 1, 41928, false);
         Images {
-            flash: flash,
+            flashmap: flashmap,
             areadesc: self.areadesc.clone(),
             slots: [self.slots[0].clone(), self.slots[1].clone()],
             primaries: primaries,
@@ -234,7 +233,7 @@
     /// Construct an `Images` for normal testing.
     pub fn make_image(&self) -> Images {
         let mut images = self.make_no_upgrade_image();
-        mark_upgrade(&mut images.flash, &images.slots[1]);
+        mark_upgrade(&mut images.flashmap, &images.slots[1]);
 
         // upgrades without fails, counts number of flash operations
         let total_count = match images.run_basic_upgrade() {
@@ -249,11 +248,11 @@
     }
 
     pub fn make_bad_slot1_image(&self) -> Images {
-        let mut bad_flash = self.flash.clone();
-        let primaries = install_image(&mut bad_flash, &self.slots, 0, 32784, false);
-        let upgrades = install_image(&mut bad_flash, &self.slots, 1, 41928, true);
+        let mut bad_flashmap = self.flashmap.clone();
+        let primaries = install_image(&mut bad_flashmap, &self.slots, 0, 32784, false);
+        let upgrades = install_image(&mut bad_flashmap, &self.slots, 1, 41928, true);
         Images {
-            flash: bad_flash,
+            flashmap: bad_flashmap,
             areadesc: self.areadesc.clone(),
             slots: [self.slots[0].clone(), self.slots[1].clone()],
             primaries: primaries,
@@ -319,7 +318,7 @@
 }
 
 /// Build the Flash and area descriptor for a given device.
-pub fn make_device(device: DeviceName, align: u8, erased_val: u8) -> (SimFlash, AreaDesc) {
+pub fn make_device(device: DeviceName, align: u8, erased_val: u8) -> (SimFlashMap, AreaDesc) {
     match device {
         DeviceName::Stm32f4 => {
             // STM style flash.  Large sectors, with a large scratch area.
@@ -327,25 +326,31 @@
                                       64 * 1024,
                                       128 * 1024, 128 * 1024, 128 * 1024],
                                       align as usize, erased_val);
-            let flash_id = 0;
+            let dev_id = 0;
             let mut areadesc = AreaDesc::new();
-            areadesc.add_flash_sectors(flash_id, &flash);
-            areadesc.add_image(0x020000, 0x020000, FlashId::Image0, flash_id);
-            areadesc.add_image(0x040000, 0x020000, FlashId::Image1, flash_id);
-            areadesc.add_image(0x060000, 0x020000, FlashId::ImageScratch, flash_id);
-            (flash, areadesc)
+            areadesc.add_flash_sectors(dev_id, &flash);
+            areadesc.add_image(0x020000, 0x020000, FlashId::Image0, dev_id);
+            areadesc.add_image(0x040000, 0x020000, FlashId::Image1, dev_id);
+            areadesc.add_image(0x060000, 0x020000, FlashId::ImageScratch, dev_id);
+
+            let mut flashmap = SimFlashMap::new();
+            flashmap.insert(dev_id, flash);
+            (flashmap, areadesc)
         }
         DeviceName::K64f => {
             // NXP style flash.  Small sectors, one small sector for scratch.
             let flash = SimFlash::new(vec![4096; 128], align as usize, erased_val);
 
-            let flash_id = 0;
+            let dev_id = 0;
             let mut areadesc = AreaDesc::new();
-            areadesc.add_flash_sectors(flash_id, &flash);
-            areadesc.add_image(0x020000, 0x020000, FlashId::Image0, flash_id);
-            areadesc.add_image(0x040000, 0x020000, FlashId::Image1, flash_id);
-            areadesc.add_image(0x060000, 0x001000, FlashId::ImageScratch, flash_id);
-            (flash, areadesc)
+            areadesc.add_flash_sectors(dev_id, &flash);
+            areadesc.add_image(0x020000, 0x020000, FlashId::Image0, dev_id);
+            areadesc.add_image(0x040000, 0x020000, FlashId::Image1, dev_id);
+            areadesc.add_image(0x060000, 0x001000, FlashId::ImageScratch, dev_id);
+
+            let mut flashmap = SimFlashMap::new();
+            flashmap.insert(dev_id, flash);
+            (flashmap, areadesc)
         }
         DeviceName::K64fBig => {
             // Simulating an STM style flash on top of an NXP style flash.  Underlying flash device
@@ -358,7 +363,10 @@
             areadesc.add_simple_image(0x020000, 0x020000, FlashId::Image0, dev_id);
             areadesc.add_simple_image(0x040000, 0x020000, FlashId::Image1, dev_id);
             areadesc.add_simple_image(0x060000, 0x020000, FlashId::ImageScratch, dev_id);
-            (flash, areadesc)
+
+            let mut flashmap = SimFlashMap::new();
+            flashmap.insert(dev_id, flash);
+            (flashmap, areadesc)
         }
         DeviceName::Nrf52840 => {
             // Simulating the flash on the nrf52840 with partitions set up so that the scratch size
@@ -371,7 +379,29 @@
             areadesc.add_image(0x008000, 0x034000, FlashId::Image0, dev_id);
             areadesc.add_image(0x03c000, 0x034000, FlashId::Image1, dev_id);
             areadesc.add_image(0x070000, 0x00d000, FlashId::ImageScratch, dev_id);
-            (flash, areadesc)
+
+            let mut flashmap = SimFlashMap::new();
+            flashmap.insert(dev_id, flash);
+            (flashmap, areadesc)
+        }
+        DeviceName::Nrf52840SpiFlash => {
+            // Simulate nrf52840 with external SPI flash. The external SPI flash
+            // has a larger sector size so for now store scratch on that flash.
+            let flash0 = SimFlash::new(vec![4096; 128], align as usize, erased_val);
+            let flash1 = SimFlash::new(vec![4096; 128], align as usize, erased_val);
+
+            let mut areadesc = AreaDesc::new();
+            areadesc.add_flash_sectors(0, &flash0);
+            areadesc.add_flash_sectors(1, &flash1);
+
+            areadesc.add_image(0x008000, 0x068000, FlashId::Image0, 0);
+            areadesc.add_image(0x000000, 0x068000, FlashId::Image1, 1);
+            areadesc.add_image(0x068000, 0x018000, FlashId::ImageScratch, 1);
+
+            let mut flashmap = SimFlashMap::new();
+            flashmap.insert(0, flash0);
+            flashmap.insert(1, flash1);
+            (flashmap, areadesc)
         }
     }
 }
@@ -382,10 +412,10 @@
     /// Returns the number of flash operations which can later be used to
     /// inject failures at chosen steps.
     pub fn run_basic_upgrade(&self) -> Result<i32, ()> {
-        let (fl, total_count) = try_upgrade(&self.flash, &self, None);
+        let (flashmap, total_count) = try_upgrade(&self.flashmap, &self, None);
         info!("Total flash operation count={}", total_count);
 
-        if !verify_image(&fl, &self.slots, 0, &self.upgrades) {
+        if !verify_image(&flashmap, &self.slots, 0, &self.upgrades) {
             warn!("Image mismatch after first boot");
             Err(())
         } else {
@@ -406,8 +436,8 @@
         if Caps::SwapUpgrade.present() {
             for count in 2 .. 5 {
                 info!("Try revert: {}", count);
-                let fl = try_revert(&self.flash, &self.areadesc, count);
-                if !verify_image(&fl, &self.slots, 0, &self.primaries) {
+                let flashmap = try_revert(&self.flashmap, &self.areadesc, count);
+                if !verify_image(&flashmap, &self.slots, 0, &self.primaries) {
                     error!("Revert failure on count {}", count);
                     fails += 1;
                 }
@@ -424,27 +454,27 @@
         // Let's try an image halfway through.
         for i in 1 .. total_flash_ops {
             info!("Try interruption at {}", i);
-            let (fl, count) = try_upgrade(&self.flash, &self, Some(i));
+            let (flashmap, count) = try_upgrade(&self.flashmap, &self, Some(i));
             info!("Second boot, count={}", count);
-            if !verify_image(&fl, &self.slots, 0, &self.upgrades) {
+            if !verify_image(&flashmap, &self.slots, 0, &self.upgrades) {
                 warn!("FAIL at step {} of {}", i, total_flash_ops);
                 fails += 1;
             }
 
-            if !verify_trailer(&fl, self.slots[0].trailer_off, BOOT_MAGIC_GOOD,
+            if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
                                BOOT_FLAG_SET, BOOT_FLAG_SET) {
                 warn!("Mismatched trailer for Slot 0");
                 fails += 1;
             }
 
-            if !verify_trailer(&fl, self.slots[1].trailer_off, BOOT_MAGIC_UNSET,
+            if !verify_trailer(&flashmap, &self.slots, 1, BOOT_MAGIC_UNSET,
                                BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
                 warn!("Mismatched trailer for Slot 1");
                 fails += 1;
             }
 
             if Caps::SwapUpgrade.present() {
-                if !verify_image(&fl, &self.slots, 1, &self.primaries) {
+                if !verify_image(&flashmap, &self.slots, 1, &self.primaries) {
                     warn!("Slot 1 FAIL at step {} of {}", i, total_flash_ops);
                     fails += 1;
                 }
@@ -466,13 +496,13 @@
     fn run_perm_with_random_fails(&self, total_fails: usize) -> bool {
         let mut fails = 0;
         let total_flash_ops = self.total_count.unwrap();
-        let (fl, total_counts) = try_random_fails(&self.flash, &self,
-                                                  total_flash_ops, total_fails);
+        let (flashmap, total_counts) = try_random_fails(&self.flashmap, &self,
+                                                        total_flash_ops, total_fails);
         info!("Random interruptions at reset points={:?}", total_counts);
 
-        let slot0_ok = verify_image(&fl, &self.slots, 0, &self.upgrades);
+        let slot0_ok = verify_image(&flashmap, &self.slots, 0, &self.upgrades);
         let slot1_ok = if Caps::SwapUpgrade.present() {
-            verify_image(&fl, &self.slots, 1, &self.primaries)
+            verify_image(&flashmap, &self.slots, 1, &self.primaries)
         } else {
             true
         };
@@ -482,12 +512,12 @@
                    if slot1_ok { "ok" } else { "fail" });
             fails += 1;
         }
-        if !verify_trailer(&fl, self.slots[0].trailer_off, BOOT_MAGIC_GOOD,
+        if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
                            BOOT_FLAG_SET, BOOT_FLAG_SET) {
             error!("Mismatched trailer for Slot 0");
             fails += 1;
         }
-        if !verify_trailer(&fl, self.slots[1].trailer_off, BOOT_MAGIC_UNSET,
+        if !verify_trailer(&flashmap, &self.slots, 1, BOOT_MAGIC_UNSET,
                            BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
             error!("Mismatched trailer for Slot 1");
             fails += 1;
@@ -512,7 +542,7 @@
         if Caps::SwapUpgrade.present() {
             for i in 1 .. (self.total_count.unwrap() - 1) {
                 info!("Try interruption at {}", i);
-                if try_revert_with_fail_at(&self.flash, &self, i) {
+                if try_revert_with_fail_at(&self.flashmap, &self, i) {
                     error!("Revert failed at interruption {}", i);
                     fails += 1;
                 }
@@ -529,13 +559,13 @@
 
     #[cfg(not(feature = "overwrite-only"))]
     pub fn run_norevert(&self) -> bool {
-        let mut fl = self.flash.clone();
+        let mut flashmap = self.flashmap.clone();
         let mut fails = 0;
 
         info!("Try norevert");
 
         // First do a normal upgrade...
-        let (result, _) = c::boot_go(&mut fl, &self.areadesc, None, false);
+        let (result, _) = c::boot_go(&mut flashmap, &self.areadesc, None, false);
         if result != 0 {
             warn!("Failed first boot");
             fails += 1;
@@ -544,42 +574,42 @@
         //FIXME: copy_done is written by boot_go, is it ok if no copy
         //       was ever done?
 
-        if !verify_image(&fl, &self.slots, 0, &self.upgrades) {
+        if !verify_image(&flashmap, &self.slots, 0, &self.upgrades) {
             warn!("Slot 0 image verification FAIL");
             fails += 1;
         }
-        if !verify_trailer(&fl, self.slots[0].trailer_off, BOOT_MAGIC_GOOD,
+        if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
                            BOOT_FLAG_UNSET, BOOT_FLAG_SET) {
             warn!("Mismatched trailer for Slot 0");
             fails += 1;
         }
-        if !verify_trailer(&fl, self.slots[1].trailer_off, BOOT_MAGIC_UNSET,
+        if !verify_trailer(&flashmap, &self.slots, 1, BOOT_MAGIC_UNSET,
                            BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
             warn!("Mismatched trailer for Slot 1");
             fails += 1;
         }
 
         // Marks image in slot0 as permanent, no revert should happen...
-        mark_permanent_upgrade(&mut fl, &self.slots[0]);
+        mark_permanent_upgrade(&mut flashmap, &self.slots[0]);
 
-        if !verify_trailer(&fl, self.slots[0].trailer_off, BOOT_MAGIC_GOOD,
+        if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
                            BOOT_FLAG_SET, BOOT_FLAG_SET) {
             warn!("Mismatched trailer for Slot 0");
             fails += 1;
         }
 
-        let (result, _) = c::boot_go(&mut fl, &self.areadesc, None, false);
+        let (result, _) = c::boot_go(&mut flashmap, &self.areadesc, None, false);
         if result != 0 {
             warn!("Failed second boot");
             fails += 1;
         }
 
-        if !verify_trailer(&fl, self.slots[0].trailer_off, BOOT_MAGIC_GOOD,
+        if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
                            BOOT_FLAG_SET, BOOT_FLAG_SET) {
             warn!("Mismatched trailer for Slot 0");
             fails += 1;
         }
-        if !verify_image(&fl, &self.slots, 0, &self.upgrades) {
+        if !verify_image(&flashmap, &self.slots, 0, &self.upgrades) {
             warn!("Failed image verification");
             fails += 1;
         }
@@ -594,38 +624,38 @@
     // Tests a new image written to slot0 that already has magic and image_ok set
     // while there is no image on slot1, so no revert should ever happen...
     pub fn run_norevert_newimage(&self) -> bool {
-        let mut fl = self.flash.clone();
+        let mut flashmap = self.flashmap.clone();
         let mut fails = 0;
 
         info!("Try non-revert on imgtool generated image");
 
-        mark_upgrade(&mut fl, &self.slots[0]);
+        mark_upgrade(&mut flashmap, &self.slots[0]);
 
         // This simulates writing an image created by imgtool to Slot 0
-        if !verify_trailer(&fl, self.slots[0].trailer_off, BOOT_MAGIC_GOOD,
+        if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
                            BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
             warn!("Mismatched trailer for Slot 0");
             fails += 1;
         }
 
         // Run the bootloader...
-        let (result, _) = c::boot_go(&mut fl, &self.areadesc, None, false);
+        let (result, _) = c::boot_go(&mut flashmap, &self.areadesc, None, false);
         if result != 0 {
             warn!("Failed first boot");
             fails += 1;
         }
 
         // State should not have changed
-        if !verify_image(&fl, &self.slots, 0, &self.primaries) {
+        if !verify_image(&flashmap, &self.slots, 0, &self.primaries) {
             warn!("Failed image verification");
             fails += 1;
         }
-        if !verify_trailer(&fl, self.slots[0].trailer_off, BOOT_MAGIC_GOOD,
+        if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
                            BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
             warn!("Mismatched trailer for Slot 0");
             fails += 1;
         }
-        if !verify_trailer(&fl, self.slots[1].trailer_off, BOOT_MAGIC_UNSET,
+        if !verify_trailer(&flashmap, &self.slots, 1, BOOT_MAGIC_UNSET,
                            BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
             warn!("Mismatched trailer for Slot 1");
             fails += 1;
@@ -641,34 +671,34 @@
     // Tests a new image written to slot0 that already has magic and image_ok set
     // while there is no image on slot1, so no revert should ever happen...
     pub fn run_signfail_upgrade(&self) -> bool {
-        let mut fl = self.flash.clone();
+        let mut flashmap = self.flashmap.clone();
         let mut fails = 0;
 
         info!("Try upgrade image with bad signature");
 
-        mark_upgrade(&mut fl, &self.slots[0]);
-        mark_permanent_upgrade(&mut fl, &self.slots[0]);
-        mark_upgrade(&mut fl, &self.slots[1]);
+        mark_upgrade(&mut flashmap, &self.slots[0]);
+        mark_permanent_upgrade(&mut flashmap, &self.slots[0]);
+        mark_upgrade(&mut flashmap, &self.slots[1]);
 
-        if !verify_trailer(&fl, self.slots[0].trailer_off, BOOT_MAGIC_GOOD,
+        if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
                            BOOT_FLAG_SET, BOOT_FLAG_UNSET) {
             warn!("Mismatched trailer for Slot 0");
             fails += 1;
         }
 
         // Run the bootloader...
-        let (result, _) = c::boot_go(&mut fl, &self.areadesc, None, false);
+        let (result, _) = c::boot_go(&mut flashmap, &self.areadesc, None, false);
         if result != 0 {
             warn!("Failed first boot");
             fails += 1;
         }
 
         // State should not have changed
-        if !verify_image(&fl, &self.slots, 0, &self.primaries) {
+        if !verify_image(&flashmap, &self.slots, 0, &self.primaries) {
             warn!("Failed image verification");
             fails += 1;
         }
-        if !verify_trailer(&fl, self.slots[0].trailer_off, BOOT_MAGIC_GOOD,
+        if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
                            BOOT_FLAG_SET, BOOT_FLAG_UNSET) {
             warn!("Mismatched trailer for Slot 0");
             fails += 1;
@@ -714,20 +744,15 @@
 
     #[cfg(feature = "validate-slot0")]
     pub fn run_with_status_fails_complete(&self) -> bool {
-        let mut fl = self.flash.clone();
+        let mut flashmap = self.flashmap.clone();
         let mut fails = 0;
 
         info!("Try swap with status fails");
 
-        mark_permanent_upgrade(&mut fl, &self.slots[1]);
+        mark_permanent_upgrade(&mut flashmap, &self.slots[1]);
+        self.mark_bad_status_with_rate(&mut flashmap, 0, 1.0);
 
-        let align = fl.align();
-        let status_off = self.slots[1].base_off - self.trailer_sz(align);
-
-        // Always fail writes to status area...
-        let _ = fl.add_bad_region(status_off, self.status_sz(align), 1.0);
-
-        let (result, asserts) = c::boot_go(&mut fl, &self.areadesc, None, true);
+        let (result, asserts) = c::boot_go(&mut flashmap, &self.areadesc, None, true);
         if result != 0 {
             warn!("Failed!");
             fails += 1;
@@ -740,19 +765,19 @@
             fails += 1;
         }
 
-        if !verify_trailer(&fl, self.slots[0].trailer_off, BOOT_MAGIC_GOOD,
+        if !verify_trailer(&flashmap, &self.slots, 0, BOOT_MAGIC_GOOD,
                            BOOT_FLAG_SET, BOOT_FLAG_SET) {
             warn!("Mismatched trailer for Slot 0");
             fails += 1;
         }
 
-        if !verify_image(&fl, &self.slots, 0, &self.upgrades) {
+        if !verify_image(&flashmap, &self.slots, 0, &self.upgrades) {
             warn!("Failed image verification");
             fails += 1;
         }
 
         info!("validate slot0 enabled; re-run of boot_go should just work");
-        let (result, _) = c::boot_go(&mut fl, &self.areadesc, None, false);
+        let (result, _) = c::boot_go(&mut flashmap, &self.areadesc, None, false);
         if result != 0 {
             warn!("Failed!");
             fails += 1;
@@ -770,7 +795,7 @@
     /// and warn that write fails were detected...
     #[cfg(feature = "validate-slot0")]
     pub fn run_with_status_fails_with_reset(&self) -> bool {
-        let mut fl = self.flash.clone();
+        let mut flashmap = self.flashmap.clone();
         let mut fails = 0;
         let mut count = self.total_count.unwrap() / 2;
 
@@ -778,29 +803,20 @@
 
         info!("Try interrupted swap with status fails");
 
-        mark_permanent_upgrade(&mut fl, &self.slots[1]);
-
-        let align = fl.align();
-        let status_off = self.slots[1].base_off - self.trailer_sz(align);
-
-        // Mark the status area as a bad area
-        let _ = fl.add_bad_region(status_off, self.status_sz(align), 0.5);
+        mark_permanent_upgrade(&mut flashmap, &self.slots[1]);
+        self.mark_bad_status_with_rate(&mut flashmap, 0, 0.5);
 
         // Should not fail, writing to bad regions does not assert
-        let (_, asserts) = c::boot_go(&mut fl, &self.areadesc, Some(&mut count), true);
+        let (_, asserts) = c::boot_go(&mut flashmap, &self.areadesc, Some(&mut count), true);
         if asserts != 0 {
             warn!("At least one assert() was called");
             fails += 1;
         }
 
-        fl.reset_bad_regions();
-
-        // Disabling write verification the only assert triggered by
-        // boot_go should be checking for integrity of status bytes.
-        fl.set_verify_writes(false);
+        self.reset_bad_status(&mut flashmap, 0);
 
         info!("Resuming an interrupted swap operation");
-        let (_, asserts) = c::boot_go(&mut fl, &self.areadesc, None, true);
+        let (_, asserts) = c::boot_go(&mut flashmap, &self.areadesc, None, true);
 
         // This might throw no asserts, for large sector devices, where
         // a single failure writing is indistinguishable from no failure,
@@ -818,24 +834,45 @@
         fails > 0
     }
 
+    /// Adds a new flash area that fails statistically
+    #[cfg(not(feature = "overwrite-only"))]
+    fn mark_bad_status_with_rate(&self, flashmap: &mut SimFlashMap, slot: usize,
+                                 rate: f32) {
+        let dev_id = &self.slots[slot].dev_id;
+        let flash = flashmap.get_mut(&dev_id).unwrap();
+        let align = flash.align();
+        let off = &self.slots[0].base_off;
+        let len = &self.slots[0].len;
+        let status_off = off + len - self.trailer_sz(align);
+
+        // Mark the status area as a bad area
+        let _ = flash.add_bad_region(status_off, self.status_sz(align), rate);
+    }
+
+    #[cfg(feature = "validate-slot0")]
+    fn reset_bad_status(&self, flashmap: &mut SimFlashMap, slot: usize) {
+        let dev_id = &self.slots[slot].dev_id;
+        let flash = flashmap.get_mut(&dev_id).unwrap();
+        flash.reset_bad_regions();
+
+        // Disabling write verification the only assert triggered by
+        // boot_go should be checking for integrity of status bytes.
+        flash.set_verify_writes(false);
+    }
+
     #[cfg(not(feature = "validate-slot0"))]
     #[cfg(not(feature = "overwrite-only"))]
     pub fn run_with_status_fails_with_reset(&self) -> bool {
-        let mut fl = self.flash.clone();
+        let mut flashmap = self.flashmap.clone();
         let mut fails = 0;
 
         info!("Try interrupted swap with status fails");
 
-        mark_permanent_upgrade(&mut fl, &self.slots[1]);
-
-        let status_off = self.slots[1].base_off - self.trailer_sz(fl.align());
-
-        // Mark the status area as a bad area
-        let align = fl.align();
-        let _ = fl.add_bad_region(status_off, self.status_sz(align), 1.0);
+        mark_permanent_upgrade(&mut flashmap, &self.slots[1]);
+        self.mark_bad_status_with_rate(&mut flashmap, 0, 1.0);
 
         // This is expected to fail while writing to bad regions...
-        let (_, asserts) = c::boot_go(&mut fl, &self.areadesc, None, true);
+        let (_, asserts) = c::boot_go(&mut flashmap, &self.areadesc, None, true);
         if asserts == 0 {
             warn!("No assert() detected");
             fails += 1;
@@ -852,16 +889,16 @@
 
 /// Test a boot, optionally stopping after 'n' flash options.  Returns a count
 /// of the number of flash operations done total.
-fn try_upgrade(flash: &SimFlash, images: &Images,
-               stop: Option<i32>) -> (SimFlash, i32) {
+fn try_upgrade(flashmap: &SimFlashMap, images: &Images,
+               stop: Option<i32>) -> (SimFlashMap, i32) {
     // Clone the flash to have a new copy.
-    let mut fl = flash.clone();
+    let mut flashmap = flashmap.clone();
 
-    mark_permanent_upgrade(&mut fl, &images.slots[1]);
+    mark_permanent_upgrade(&mut flashmap, &images.slots[1]);
 
     let mut counter = stop.unwrap_or(0);
 
-    let (first_interrupted, count) = match c::boot_go(&mut fl, &images.areadesc, Some(&mut counter), false) {
+    let (first_interrupted, count) = match c::boot_go(&mut flashmap, &images.areadesc, Some(&mut counter), false) {
         (-0x13579, _) => (true, stop.unwrap()),
         (0, _) => (false, -counter),
         (x, _) => panic!("Unknown return: {}", x),
@@ -870,92 +907,92 @@
     counter = 0;
     if first_interrupted {
         // fl.dump();
-        match c::boot_go(&mut fl, &images.areadesc, Some(&mut counter), false) {
+        match c::boot_go(&mut flashmap, &images.areadesc, Some(&mut counter), false) {
             (-0x13579, _) => panic!("Shouldn't stop again"),
             (0, _) => (),
             (x, _) => panic!("Unknown return: {}", x),
         }
     }
 
-    (fl, count - counter)
+    (flashmap, count - counter)
 }
 
 #[cfg(not(feature = "overwrite-only"))]
-fn try_revert(flash: &SimFlash, areadesc: &AreaDesc, count: usize) -> SimFlash {
-    let mut fl = flash.clone();
+fn try_revert(flashmap: &SimFlashMap, areadesc: &AreaDesc, count: usize) -> SimFlashMap {
+    let mut flashmap = flashmap.clone();
 
     // fl.write_file("image0.bin").unwrap();
     for i in 0 .. count {
         info!("Running boot pass {}", i + 1);
-        assert_eq!(c::boot_go(&mut fl, &areadesc, None, false), (0, 0));
+        assert_eq!(c::boot_go(&mut flashmap, &areadesc, None, false), (0, 0));
     }
-    fl
+    flashmap
 }
 
 #[cfg(not(feature = "overwrite-only"))]
-fn try_revert_with_fail_at(flash: &SimFlash, images: &Images,
+fn try_revert_with_fail_at(flashmap: &SimFlashMap, images: &Images,
                            stop: i32) -> bool {
-    let mut fl = flash.clone();
+    let mut flashmap = flashmap.clone();
     let mut fails = 0;
 
     let mut counter = stop;
-    let (x, _) = c::boot_go(&mut fl, &images.areadesc, Some(&mut counter), false);
+    let (x, _) = c::boot_go(&mut flashmap, &images.areadesc, Some(&mut counter), false);
     if x != -0x13579 {
         warn!("Should have stopped at interruption point");
         fails += 1;
     }
 
-    if !verify_trailer(&fl, images.slots[0].trailer_off, None, None, BOOT_FLAG_UNSET) {
+    if !verify_trailer(&flashmap, &images.slots, 0, None, None, BOOT_FLAG_UNSET) {
         warn!("copy_done should be unset");
         fails += 1;
     }
 
-    let (x, _) = c::boot_go(&mut fl, &images.areadesc, None, false);
+    let (x, _) = c::boot_go(&mut flashmap, &images.areadesc, None, false);
     if x != 0 {
         warn!("Should have finished upgrade");
         fails += 1;
     }
 
-    if !verify_image(&fl, &images.slots, 0, &images.upgrades) {
+    if !verify_image(&flashmap, &images.slots, 0, &images.upgrades) {
         warn!("Image in slot 0 before revert is invalid at stop={}", stop);
         fails += 1;
     }
-    if !verify_image(&fl, &images.slots, 1, &images.primaries) {
+    if !verify_image(&flashmap, &images.slots, 1, &images.primaries) {
         warn!("Image in slot 1 before revert is invalid at stop={}", stop);
         fails += 1;
     }
-    if !verify_trailer(&fl, images.slots[0].trailer_off, BOOT_MAGIC_GOOD,
+    if !verify_trailer(&flashmap, &images.slots, 0, BOOT_MAGIC_GOOD,
                        BOOT_FLAG_UNSET, BOOT_FLAG_SET) {
         warn!("Mismatched trailer for Slot 0 before revert");
         fails += 1;
     }
-    if !verify_trailer(&fl, images.slots[1].trailer_off, BOOT_MAGIC_UNSET,
+    if !verify_trailer(&flashmap, &images.slots, 1, BOOT_MAGIC_UNSET,
                        BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
         warn!("Mismatched trailer for Slot 1 before revert");
         fails += 1;
     }
 
     // Do Revert
-    let (x, _) = c::boot_go(&mut fl, &images.areadesc, None, false);
+    let (x, _) = c::boot_go(&mut flashmap, &images.areadesc, None, false);
     if x != 0 {
         warn!("Should have finished a revert");
         fails += 1;
     }
 
-    if !verify_image(&fl, &images.slots, 0, &images.primaries) {
+    if !verify_image(&flashmap, &images.slots, 0, &images.primaries) {
         warn!("Image in slot 0 after revert is invalid at stop={}", stop);
         fails += 1;
     }
-    if !verify_image(&fl, &images.slots, 1, &images.upgrades) {
+    if !verify_image(&flashmap, &images.slots, 1, &images.upgrades) {
         warn!("Image in slot 1 after revert is invalid at stop={}", stop);
         fails += 1;
     }
-    if !verify_trailer(&fl, images.slots[0].trailer_off, BOOT_MAGIC_GOOD,
+    if !verify_trailer(&flashmap, &images.slots, 0, BOOT_MAGIC_GOOD,
                        BOOT_FLAG_SET, BOOT_FLAG_SET) {
         warn!("Mismatched trailer for Slot 1 after revert");
         fails += 1;
     }
-    if !verify_trailer(&fl, images.slots[1].trailer_off, BOOT_MAGIC_UNSET,
+    if !verify_trailer(&flashmap, &images.slots, 1, BOOT_MAGIC_UNSET,
                        BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) {
         warn!("Mismatched trailer for Slot 1 after revert");
         fails += 1;
@@ -964,11 +1001,11 @@
     fails > 0
 }
 
-fn try_random_fails(flash: &SimFlash, images: &Images,
-                    total_ops: i32,  count: usize) -> (SimFlash, Vec<i32>) {
-    let mut fl = flash.clone();
+fn try_random_fails(flashmap: &SimFlashMap, images: &Images,
+                    total_ops: i32,  count: usize) -> (SimFlashMap, Vec<i32>) {
+    let mut flashmap = flashmap.clone();
 
-    mark_permanent_upgrade(&mut fl, &images.slots[1]);
+    mark_permanent_upgrade(&mut flashmap, &images.slots[1]);
 
     let mut rng = rand::thread_rng();
     let mut resets = vec![0i32; count];
@@ -977,7 +1014,7 @@
         let ops = Range::new(1, remaining_ops / 2);
         let reset_counter = ops.ind_sample(&mut rng);
         let mut counter = reset_counter;
-        match c::boot_go(&mut fl, &images.areadesc, Some(&mut counter), false) {
+        match c::boot_go(&mut flashmap, &images.areadesc, Some(&mut counter), false) {
             (0, _) | (-0x13579, _) => (),
             (x, _) => panic!("Unknown return: {}", x),
         }
@@ -985,13 +1022,13 @@
         resets[i] = reset_counter;
     }
 
-    match c::boot_go(&mut fl, &images.areadesc, None, false) {
+    match c::boot_go(&mut flashmap, &images.areadesc, None, false) {
         (-0x13579, _) => panic!("Should not be have been interrupted!"),
         (0, _) => (),
         (x, _) => panic!("Unknown return: {}", x),
     }
 
-    (fl, resets)
+    (flashmap, resets)
 }
 
 /// Show the flash layout.
@@ -1007,10 +1044,11 @@
 
 /// Install a "program" into the given image.  This fakes the image header, or at least all of the
 /// fields used by the given code.  Returns a copy of the image that was written.
-fn install_image(flash: &mut Flash, slots: &[SlotInfo], slot: usize, len: usize,
+fn install_image(flashmap: &mut SimFlashMap, slots: &[SlotInfo], slot: usize, len: usize,
                  bad_sig: bool) -> [Option<Vec<u8>>; 2] {
     let offset = slots[slot].base_off;
     let slot_len = slots[slot].len;
+    let dev_id = slots[slot].dev_id;
 
     let mut tlv = make_tlv();
 
@@ -1091,6 +1129,8 @@
     // un-encrypted. In slot1 the image is written un-encrypted, and if
     // encryption is requested, it follows an erase + flash encrypted.
 
+    let flash = flashmap.get_mut(&dev_id).unwrap();
+
     if slot == 0 {
         let enc_copy: Option<Vec<u8>>;
 
@@ -1114,6 +1154,7 @@
 
         result = [Some(copy), enc_copy];
     } else {
+
         flash.write(offset, &buf).unwrap();
 
         let mut copy = vec![0u8; buf.len()];
@@ -1203,13 +1244,15 @@
 }
 
 /// Verify that given image is present in the flash at the given offset.
-fn verify_image(flash: &Flash, slots: &[SlotInfo], slot: usize,
+fn verify_image(flashmap: &SimFlashMap, slots: &[SlotInfo], slot: usize,
                 images: &[Option<Vec<u8>>; 2]) -> bool {
     let image = find_image(images, slot);
     let buf = image.as_slice();
+    let dev_id = slots[slot].dev_id;
 
     let mut copy = vec![0u8; buf.len()];
     let offset = slots[slot].base_off;
+    let flash = flashmap.get(&dev_id).unwrap();
     flash.read(offset, &mut copy).unwrap();
 
     if buf != &copy[..] {
@@ -1229,20 +1272,23 @@
 #[cfg(feature = "overwrite-only")]
 #[allow(unused_variables)]
 // overwrite-only doesn't employ trailer management
-fn verify_trailer(flash: &Flash, offset: usize,
+fn verify_trailer(flashmap: &SimFlashMap, slots: &[SlotInfo], slot: usize,
                   magic: Option<u8>, image_ok: Option<u8>,
                   copy_done: Option<u8>) -> bool {
     true
 }
 
 #[cfg(not(feature = "overwrite-only"))]
-fn verify_trailer(flash: &Flash, offset: usize,
+fn verify_trailer(flashmap: &SimFlashMap, slots: &[SlotInfo], slot: usize,
                   magic: Option<u8>, image_ok: Option<u8>,
                   copy_done: Option<u8>) -> bool {
+    let offset = slots[slot].trailer_off;
+    let dev_id = slots[slot].dev_id;
     let mut copy = vec![0u8; c::boot_magic_sz() + c::boot_max_align() * 2];
     let mut failed = false;
-    let erased_val = flash.erased_val();
 
+    let flash = flashmap.get(&dev_id).unwrap();
+    let erased_val = flash.erased_val();
     flash.read(offset, &mut copy).unwrap();
 
     failed |= match magic {
@@ -1320,10 +1366,11 @@
     base_off: usize,
     trailer_off: usize,
     len: usize,
+    dev_id: u8,
 }
 
 pub struct Images {
-    flash: SimFlash,
+    flashmap: SimFlashMap,
     areadesc: AreaDesc,
     slots: [SlotInfo; 2],
     primaries: [Option<Vec<u8>>; 2],
@@ -1344,14 +1391,16 @@
 const BOOT_FLAG_UNSET: Option<u8> = Some(3);
 
 /// Write out the magic so that the loader tries doing an upgrade.
-fn mark_upgrade(flash: &mut Flash, slot: &SlotInfo) {
+fn mark_upgrade(flashmap: &mut SimFlashMap, slot: &SlotInfo) {
+    let flash = flashmap.get_mut(&slot.dev_id).unwrap();
     let offset = slot.trailer_off + c::boot_max_align() * 2;
     flash.write(offset, MAGIC.unwrap()).unwrap();
 }
 
 /// Writes the image_ok flag which, guess what, tells the bootloader
 /// the this image is ok (not a test, and no revert is to be performed).
-fn mark_permanent_upgrade(flash: &mut Flash, slot: &SlotInfo) {
+fn mark_permanent_upgrade(flashmap: &mut SimFlashMap, slot: &SlotInfo) {
+    let flash = flashmap.get_mut(&slot.dev_id).unwrap();
     let mut ok = [flash.erased_val(); 8];
     ok[0] = 1u8;
     let off = slot.trailer_off + c::boot_max_align();