Merge pull request #28 from d3zd3z/sim

Sim updates
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..9cd03d1
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,6 @@
+# Travis configuration.  Build the simulator and run its tests.
+
+language: rust
+script:
+    - cd sim; cargo build --release
+    - cd sim; cargo run --release -- runall
diff --git a/sim/src/area.rs b/sim/src/area.rs
index c74f664..4b85912 100644
--- a/sim/src/area.rs
+++ b/sim/src/area.rs
@@ -1,7 +1,6 @@
 //! Describe flash areas.
 
 use flash::{Flash, Sector};
-use std::marker::PhantomData;
 use std::ptr;
 
 /// Structure to build up the boot area table.
@@ -102,6 +101,17 @@
         });
     }
 
+    // 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) {
+        for area in &self.whole {
+            if area.flash_id == id {
+                return (area.off as usize, area.size as usize);
+            }
+        }
+        panic!("Requesting area that is not present in flash");
+    }
+
     pub fn get_c(&self) -> CAreaDesc {
         let mut areas: CAreaDesc = Default::default();
 
@@ -125,29 +135,27 @@
 /// The area descriptor, C format.
 #[repr(C)]
 #[derive(Debug, Default)]
-pub struct CAreaDesc<'a> {
-    slots: [CArea<'a>; 16],
+pub struct CAreaDesc {
+    slots: [CArea; 16],
     num_slots: u32,
 }
 
 #[repr(C)]
 #[derive(Debug)]
-pub struct CArea<'a> {
+pub struct CArea {
     whole: FlashArea,
     areas: *const FlashArea,
     num_areas: u32,
     id: FlashId,
-    phantom: PhantomData<&'a AreaDesc>,
 }
 
-impl<'a> Default for CArea<'a> {
-    fn default() -> CArea<'a> {
+impl Default for CArea {
+    fn default() -> CArea {
         CArea {
             areas: ptr::null(),
             whole: Default::default(),
             id: FlashId::BootLoader,
             num_areas: 0,
-            phantom: PhantomData,
         }
     }
 }
diff --git a/sim/src/flash.rs b/sim/src/flash.rs
index 83ef771..797a044 100644
--- a/sim/src/flash.rs
+++ b/sim/src/flash.rs
@@ -95,7 +95,7 @@
 
         let mut sub = &mut self.data[offset .. offset + payload.len()];
         if sub.iter().any(|x| *x != 0xFF) {
-            bail!(ewrite("Write to non-FF location"));
+            bail!(ewrite(format!("Write to non-FF location: offset: {:x}", offset)));
         }
 
         sub.copy_from_slice(payload);
diff --git a/sim/src/main.rs b/sim/src/main.rs
index f077668..0889d5b 100644
--- a/sim/src/main.rs
+++ b/sim/src/main.rs
@@ -11,7 +11,9 @@
 use docopt::Docopt;
 use rand::{Rng, SeedableRng, XorShiftRng};
 use rustc_serialize::{Decodable, Decoder};
+use std::fmt;
 use std::mem;
+use std::process;
 use std::slice;
 
 mod area;
@@ -29,6 +31,7 @@
 Usage:
   bootsim sizes
   bootsim run --device TYPE [--align SIZE]
+  bootsim runall
   bootsim (--help | --version)
 
 Options:
@@ -47,10 +50,30 @@
     flag_align: Option<AlignArg>,
     cmd_sizes: bool,
     cmd_run: bool,
+    cmd_runall: bool,
 }
 
-#[derive(Debug, RustcDecodable)]
-enum DeviceName { Stm32f4, K64f, K64fBig }
+#[derive(Copy, Clone, Debug, RustcDecodable)]
+enum DeviceName { Stm32f4, K64f, K64fBig, Nrf52840 }
+
+static ALL_DEVICES: &'static [DeviceName] = &[
+    DeviceName::Stm32f4,
+    DeviceName::K64f,
+    DeviceName::K64fBig,
+    DeviceName::Nrf52840,
+];
+
+impl fmt::Display for DeviceName {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let name = match *self {
+            DeviceName::Stm32f4 => "stm32f4",
+            DeviceName::K64f => "k64f",
+            DeviceName::K64fBig => "k64fbig",
+            DeviceName::Nrf52840 => "nrf52840",
+        };
+        f.write_str(name)
+    }
+}
 
 #[derive(Debug)]
 struct AlignArg(u8);
@@ -79,109 +102,193 @@
         return;
     }
 
-    let align = args.flag_align.map(|x| x.0).unwrap_or(1);
+    let mut status = RunStatus::new();
+    if args.cmd_run {
 
-    let (mut flash, areadesc) = match args.flag_device {
-        None => panic!("Missing mandatory argument"),
-        Some(DeviceName::Stm32f4) => {
-            // STM style flash.  Large sectors, with a large scratch area.
-            let flash = Flash::new(vec![16 * 1024, 16 * 1024, 16 * 1024, 16 * 1024,
-                                   64 * 1024,
-                                   128 * 1024, 128 * 1024, 128 * 1024],
-                                   align as usize);
-            let mut areadesc = AreaDesc::new(&flash);
-            areadesc.add_image(0x020000, 0x020000, FlashId::Image0);
-            areadesc.add_image(0x040000, 0x020000, FlashId::Image1);
-            areadesc.add_image(0x060000, 0x020000, FlashId::ImageScratch);
-            (flash, areadesc)
-        }
-        Some(DeviceName::K64f) => {
-            // NXP style flash.  Small sectors, one small sector for scratch.
-            let flash = Flash::new(vec![4096; 128], align as usize);
+        let align = args.flag_align.map(|x| x.0).unwrap_or(1);
 
-            let mut areadesc = AreaDesc::new(&flash);
-            areadesc.add_image(0x020000, 0x020000, FlashId::Image0);
-            areadesc.add_image(0x040000, 0x020000, FlashId::Image1);
-            areadesc.add_image(0x060000, 0x001000, FlashId::ImageScratch);
-            (flash, areadesc)
-        }
-        Some(DeviceName::K64fBig) => {
-            // Simulating an STM style flash on top of an NXP style flash.  Underlying flash device
-            // uses small sectors, but we tell the bootloader they are large.
-            let flash = Flash::new(vec![4096; 128], align as usize);
+        let device = match args.flag_device {
+            None => panic!("Missing mandatory device argument"),
+            Some(dev) => dev,
+        };
 
-            let mut areadesc = AreaDesc::new(&flash);
-            areadesc.add_simple_image(0x020000, 0x020000, FlashId::Image0);
-            areadesc.add_simple_image(0x040000, 0x020000, FlashId::Image1);
-            areadesc.add_simple_image(0x060000, 0x020000, FlashId::ImageScratch);
-            (flash, areadesc)
-        }
-    };
-
-    // println!("Areas: {:#?}", areadesc.get_c());
-
-    // Install the boot trailer signature, so that the code will start an upgrade.
-    // TODO: This must be a multiple of flash alignment, add support for an image that is smaller,
-    // and just gets padded.
-    let primary = install_image(&mut flash, 0x020000, 32784);
-
-    // Install an upgrade image.
-    let upgrade = install_image(&mut flash, 0x040000, 41928);
-
-    // Set an alignment, and position the magic value.
-    c::set_sim_flash_align(align);
-    let trailer_size = c::boot_trailer_sz();
-
-    // Mark the upgrade as ready to install.  (This looks like it might be a bug in the code,
-    // however.)
-    mark_upgrade(&mut flash, 0x060000 - trailer_size as usize);
-
-    let (fl2, total_count) = try_upgrade(&flash, &areadesc, None);
-    info!("First boot, count={}", total_count);
-    assert!(verify_image(&fl2, 0x020000, &upgrade));
-
-    let mut bad = 0;
-    // Let's try an image halfway through.
-    for i in 1 .. total_count {
-        info!("Try interruption at {}", i);
-        let (fl3, total_count) = try_upgrade(&flash, &areadesc, Some(i));
-        info!("Second boot, count={}", total_count);
-        if !verify_image(&fl3, 0x020000, &upgrade) {
-            warn!("FAIL at step {} of {}", i, total_count);
-            bad += 1;
-        }
-        if !verify_image(&fl3, 0x040000, &primary) {
-            warn!("Slot 1 FAIL at step {} of {}", i, total_count);
-            bad += 1;
-        }
-    }
-    error!("{} out of {} failed {:.2}%",
-           bad, total_count,
-           bad as f32 * 100.0 / total_count as f32);
-
-    for count in 2 .. 5 {
-        info!("Try revert: {}", count);
-        let fl2 = try_revert(&flash, &areadesc, count);
-        assert!(verify_image(&fl2, 0x020000, &primary));
+        status.run_single(device, align);
     }
 
-    info!("Try norevert");
-    let fl2 = try_norevert(&flash, &areadesc);
-    assert!(verify_image(&fl2, 0x020000, &upgrade));
+    if args.cmd_runall {
+        for &dev in ALL_DEVICES {
+            for &align in &[1, 2, 4, 8] {
+                status.run_single(dev, align);
+            }
+        }
+    }
 
-    /*
-    // show_flash(&flash);
+    if status.failures > 0 {
+        warn!("{} Tests ran with {} failures", status.failures + status.passes, status.failures);
+        process::exit(1);
+    } else {
+        warn!("{} Tests ran successfully", status.passes);
+        process::exit(0);
+    }
+}
 
-    println!("First boot for upgrade");
-    // c::set_flash_counter(570);
-    c::boot_go(&mut flash, &areadesc);
-    // println!("{} flash ops", c::get_flash_counter());
+struct RunStatus {
+    failures: usize,
+    passes: usize,
+}
 
-    verify_image(&flash, 0x020000, &upgrade);
+impl RunStatus {
+    fn new() -> RunStatus {
+        RunStatus {
+            failures: 0,
+            passes: 0,
+        }
+    }
 
-    println!("\n------------------\nSecond boot");
-    c::boot_go(&mut flash, &areadesc);
-    */
+    fn run_single(&mut self, device: DeviceName, align: u8) {
+        let mut failed = false;
+
+        warn!("Running on device {} with alignment {}", device, align);
+
+        let (mut flash, areadesc) = match device {
+            DeviceName::Stm32f4 => {
+                // STM style flash.  Large sectors, with a large scratch area.
+                let flash = Flash::new(vec![16 * 1024, 16 * 1024, 16 * 1024, 16 * 1024,
+                                       64 * 1024,
+                                       128 * 1024, 128 * 1024, 128 * 1024],
+                                       align as usize);
+                let mut areadesc = AreaDesc::new(&flash);
+                areadesc.add_image(0x020000, 0x020000, FlashId::Image0);
+                areadesc.add_image(0x040000, 0x020000, FlashId::Image1);
+                areadesc.add_image(0x060000, 0x020000, FlashId::ImageScratch);
+                (flash, areadesc)
+            }
+            DeviceName::K64f => {
+                // NXP style flash.  Small sectors, one small sector for scratch.
+                let flash = Flash::new(vec![4096; 128], align as usize);
+
+                let mut areadesc = AreaDesc::new(&flash);
+                areadesc.add_image(0x020000, 0x020000, FlashId::Image0);
+                areadesc.add_image(0x040000, 0x020000, FlashId::Image1);
+                areadesc.add_image(0x060000, 0x001000, FlashId::ImageScratch);
+                (flash, areadesc)
+            }
+            DeviceName::K64fBig => {
+                // Simulating an STM style flash on top of an NXP style flash.  Underlying flash device
+                // uses small sectors, but we tell the bootloader they are large.
+                let flash = Flash::new(vec![4096; 128], align as usize);
+
+                let mut areadesc = AreaDesc::new(&flash);
+                areadesc.add_simple_image(0x020000, 0x020000, FlashId::Image0);
+                areadesc.add_simple_image(0x040000, 0x020000, FlashId::Image1);
+                areadesc.add_simple_image(0x060000, 0x020000, FlashId::ImageScratch);
+                (flash, areadesc)
+            }
+            DeviceName::Nrf52840 => {
+                // Simulating the flash on the nrf52840 with partitions set up so that the scratch size
+                // does not divide into the image size.
+                let flash = Flash::new(vec![4096; 128], align as usize);
+
+                let mut areadesc = AreaDesc::new(&flash);
+                areadesc.add_image(0x008000, 0x034000, FlashId::Image0);
+                areadesc.add_image(0x03c000, 0x034000, FlashId::Image1);
+                areadesc.add_image(0x070000, 0x00d000, FlashId::ImageScratch);
+                (flash, areadesc)
+            }
+        };
+
+        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);
+
+        // Code below assumes that the slots are consecutive.
+        assert_eq!(slot1_base, slot0_base + slot0_len);
+        assert_eq!(scratch_base, slot1_base + slot1_len);
+
+        // println!("Areas: {:#?}", areadesc.get_c());
+
+        // Install the boot trailer signature, so that the code will start an upgrade.
+        // TODO: This must be a multiple of flash alignment, add support for an image that is smaller,
+        // and just gets padded.
+        let primary = install_image(&mut flash, slot0_base, 32784);
+
+        // Install an upgrade image.
+        let upgrade = install_image(&mut flash, slot1_base, 41928);
+
+        // Set an alignment, and position the magic value.
+        c::set_sim_flash_align(align);
+        let trailer_size = c::boot_trailer_sz();
+
+        // Mark the upgrade as ready to install.  (This looks like it might be a bug in the code,
+        // however.)
+        mark_upgrade(&mut flash, scratch_base - trailer_size as usize);
+
+        let (fl2, total_count) = try_upgrade(&flash, &areadesc, None);
+        info!("First boot, count={}", total_count);
+        if !verify_image(&fl2, slot0_base, &upgrade) {
+            error!("Image mismatch after first boot");
+            // This isn't really recoverable, and more tests aren't likely to reveal much.
+            self.failures += 1;
+            return;
+        }
+
+        let mut bad = 0;
+        // Let's try an image halfway through.
+        for i in 1 .. total_count {
+            info!("Try interruption at {}", i);
+            let (fl3, total_count) = try_upgrade(&flash, &areadesc, Some(i));
+            info!("Second boot, count={}", total_count);
+            if !verify_image(&fl3, slot0_base, &upgrade) {
+                warn!("FAIL at step {} of {}", i, total_count);
+                bad += 1;
+            }
+            if !verify_image(&fl3, slot1_base, &primary) {
+                warn!("Slot 1 FAIL at step {} of {}", i, total_count);
+                bad += 1;
+            }
+        }
+        error!("{} out of {} failed {:.2}%",
+               bad, total_count,
+               bad as f32 * 100.0 / total_count as f32);
+        if bad > 0 {
+            failed = true;
+        }
+
+        for count in 2 .. 5 {
+            info!("Try revert: {}", count);
+            let fl2 = try_revert(&flash, &areadesc, count);
+            if !verify_image(&fl2, slot0_base, &primary) {
+                warn!("Revert failure on count {}", count);
+                failed = true;
+            }
+        }
+
+        info!("Try norevert");
+        let fl2 = try_norevert(&flash, &areadesc);
+        if !verify_image(&fl2, slot0_base, &upgrade) {
+            warn!("No revert failed");
+            failed = true;
+        }
+
+        /*
+        // show_flash(&flash);
+
+        println!("First boot for upgrade");
+        // c::set_flash_counter(570);
+        c::boot_go(&mut flash, &areadesc);
+        // println!("{} flash ops", c::get_flash_counter());
+
+        verify_image(&flash, slot0_base, &upgrade);
+
+        println!("\n------------------\nSecond boot");
+        c::boot_go(&mut flash, &areadesc);
+        */
+        if failed {
+            self.failures += 1;
+        } else {
+            self.passes += 1;
+        }
+    }
 }
 
 /// Test a boot, optionally stopping after 'n' flash options.  Returns a count of the number of
@@ -232,7 +339,8 @@
     assert_eq!(c::boot_go(&mut fl, &areadesc), 0);
     // Write boot_ok
     let ok = [1u8, 0, 0, 0, 0, 0, 0, 0];
-    fl.write(0x040000 - align, &ok[..align]).unwrap();
+    let (slot0_base, slot0_len) = areadesc.find(FlashId::Image0);
+    fl.write(slot0_base + slot0_len - align, &ok[..align]).unwrap();
     assert_eq!(c::boot_go(&mut fl, &areadesc), 0);
     fl
 }