wip: Add sim support for loaded images

For developing new functionality, allow the simulator to test using a
loaded image.  For this particular case, for example, we could test
ecdsa booting with the following:

    $ dd if=/dev/zero bs=128 of=header.bin
    $ dd if=/dev/random bs=1024 count=2 of=payload.bin
    $ cat header.bin payload.bin loaded.bin
    $ ../scripts/imgtool.py sign \
        -k ../root-ec-p256.pem \
        --align 8 \
        --version 1.2.3 \
        --header-size 128 \
        -S 16384 \
        loaded.bin loaded.suit
    $ cargo test \
        --features "overwrite-only validate-primary-slot sig-ecdsa" \
        -- --nocapture --test-threads 1 \
        external_load

Signed-off-by: David Brown <david.brown@linaro.org>
diff --git a/sim/src/image.rs b/sim/src/image.rs
index ae0c26f..2704a19 100644
--- a/sim/src/image.rs
+++ b/sim/src/image.rs
@@ -20,8 +20,10 @@
 };
 use std::{
     collections::{BTreeMap, HashSet},
-    io::{Cursor, Write},
+    fs::File,
+    io::{Cursor, Read, Write},
     mem,
+    path::Path,
     slice,
 };
 use aes_ctr::{
@@ -282,6 +284,35 @@
         }
     }
 
+    pub fn make_loaded_image<P>(self, path: P) -> Images
+        where P: AsRef<Path>,
+    {
+        // let num_images = self.num_images();
+        let mut flash = self.flash;
+        // let ram = self.ram.clone();  // TODO: Avoid this clone.
+        let images = self.slots.into_iter().enumerate().map(|(_image_num, slots)| {
+            let primaries = load_image(&mut flash, &slots[0], path.as_ref());
+            /*
+            let upgrades = match deps.depends[image_num] {
+                DepType::NoUpgrade => install_no_image(),
+                _ => todo!(), // install_image(&mut flash, &slots[1], size, &ram, &*dep, false)
+            };
+            */
+            let upgrades = install_no_image();
+            OneImage {
+                slots,
+                primaries,
+                upgrades,
+            }}).collect();
+        Images {
+            flash,
+            areadesc: self.areadesc,
+            images,
+            total_count: None,
+            ram: self.ram,
+        }
+    }
+
     pub fn make_erased_secondary_image(self) -> Images {
         let mut flash = self.flash;
         let ram = self.ram.clone(); // TODO: Avoid this clone.
@@ -1081,6 +1112,18 @@
         return false;
     }
 
+    /// Verify a normal boot works on the first image.
+    pub fn test_normal_boot(&self) -> bool {
+        let mut flash = self.flash.clone();
+
+        if !c::boot_go(&mut flash, &self.areadesc, None, false).success() {
+            warn!("Failed to boot image");
+            return true;
+        }
+
+        false
+    }
+
     /// Adds a new flash area that fails statistically
     fn mark_bad_status_with_rate(&self, flash: &mut SimMultiFlash, slot: usize,
                                  rate: f32) {
@@ -1579,6 +1622,37 @@
     }
 }
 
+/// Install an image coming from an external file.  Returns a copy of the image that was written.
+fn load_image(flash: &mut SimMultiFlash, slot: &SlotInfo, path: &Path) -> ImageData {
+    let mut buf = vec![];
+    // This is not in a Result context, so just expect these.
+    File::open(path).expect("open image file").read_to_end(&mut buf).expect("read data from image file");
+
+    // Corrupt the image so that it doesn't validate.
+    // buf[1025] ^= 1;
+
+    // Corrupt the image.
+    buf[1025] ^= 1;
+
+    // Pad the image to the largest alignment.
+    while buf.len() & 15 != 0 {
+        buf.push(0xFF);
+    }
+
+    let offset = slot.base_off;
+    let dev_id = slot.dev_id;
+    let dev = flash.get_mut(&dev_id).unwrap();
+
+    dev.write(offset, &buf).unwrap();
+    let mut copy = vec![0u8; buf.len()];
+    dev.read(offset, &mut copy).unwrap();
+
+    ImageData {
+        plain: copy,
+        cipher: None,
+    }
+}
+
 /// Install no image.  This is used when no upgrade happens.
 fn install_no_image() -> ImageData {
     ImageData {
diff --git a/sim/tests/core.rs b/sim/tests/core.rs
index ca91bbb..b394ff5 100644
--- a/sim/tests/core.rs
+++ b/sim/tests/core.rs
@@ -62,6 +62,8 @@
 sim_test!(direct_xip_first, make_no_upgrade_image(&NO_DEPS), run_direct_xip());
 sim_test!(ram_load_first, make_no_upgrade_image(&NO_DEPS), run_ram_load());
 
+sim_test!(external_load, make_loaded_image("loaded.suit"), test_normal_boot());
+
 // Test various combinations of incorrect dependencies.
 test_shell!(dependency_combos, r, {
     // Only test setups with two images.