sim: Test software rollback protection

Signed-off-by: Håkon Øye Amundsen <haakon.amundsen@nordicsemi.no>
Signed-off-by: David Brown <david.brown@linaro.org>
diff --git a/sim/src/caps.rs b/sim/src/caps.rs
index a63a343..f823b99 100644
--- a/sim/src/caps.rs
+++ b/sim/src/caps.rs
@@ -22,6 +22,7 @@
     Ed25519              = (1 << 9),
     EncEc256             = (1 << 10),
     SwapUsingMove        = (1 << 11),
+    DowngradePrevention  = (1 << 12),
 }
 
 impl Caps {
diff --git a/sim/src/depends.rs b/sim/src/depends.rs
index 57b4bad..f3bbc29 100644
--- a/sim/src/depends.rs
+++ b/sim/src/depends.rs
@@ -22,11 +22,28 @@
 /// A boring image is used when we aren't testing dependencies.  There will
 /// be meaningful version numbers.  The size field is the image number we
 /// are.
-pub struct BoringDep(pub usize);
+pub struct BoringDep {
+    number: usize,
+    test: DepTest,
+}
+
+impl BoringDep {
+    pub fn new(number: usize, test: &DepTest) -> BoringDep {
+        BoringDep {
+            number: number,
+            test: test.clone(),
+        }
+    }
+}
 
 impl Depender for BoringDep {
     fn my_version(&self, _offset: usize, slot: usize) -> ImageVersion {
-        ImageVersion::new_synthetic(self.0 as u8, slot as u8, 0)
+        let slot = if self.test.downgrade {
+            1 - slot
+        } else {
+            slot
+        };
+        ImageVersion::new_synthetic(self.number as u8, slot as u8, 0)
     }
 
     fn my_deps(&self, _offset: usize, _slot: usize) -> Vec<ImageVersion> {
@@ -48,6 +65,10 @@
 
     /// What is the expected outcome of the upgrade.
     pub upgrades: [UpgradeInfo; 2],
+
+    /// Should this be considered a downgrade (cause the version number to
+    /// decrease).
+    pub downgrade: bool,
 }
 
 /// Describes the various types of dependency information that can be
@@ -81,6 +102,15 @@
 pub static NO_DEPS: DepTest = DepTest {
     depends: [DepType::Nothing, DepType::Nothing],
     upgrades: [UpgradeInfo::Upgraded, UpgradeInfo::Upgraded],
+    downgrade: false,
+};
+
+/// A "test" with no dependency information, and the images marked as a
+/// downgrade.
+pub static REV_DEPS: DepTest = DepTest {
+    depends: [DepType::Nothing, DepType::Nothing],
+    upgrades: [UpgradeInfo::Held, UpgradeInfo::Held],
+    downgrade: true,
 };
 
 /// A PairDep describes the dependencies between two pairs.
@@ -106,6 +136,11 @@
 
 impl Depender for PairDep {
     fn my_version(&self, _offset: usize, slot: usize) -> ImageVersion {
+        let slot = if self.test.downgrade {
+            1 - slot
+        } else {
+            slot
+        };
         ImageVersion::new_synthetic(self.number as u8, slot as u8, 0)
     }
 
diff --git a/sim/src/image.rs b/sim/src/image.rs
index 9c24a3e..2c99694 100644
--- a/sim/src/image.rs
+++ b/sim/src/image.rs
@@ -45,6 +45,7 @@
     Depender,
     DepTest,
     DepType,
+    NO_DEPS,
     PairDep,
     UpgradeInfo,
 };
@@ -177,7 +178,7 @@
             let dep: Box<dyn Depender> = if num_images > 1 {
                 Box::new(PairDep::new(num_images, image_num, deps))
             } else {
-                Box::new(BoringDep(image_num))
+                Box::new(BoringDep::new(image_num, deps))
             };
             let primaries = install_image(&mut flash, &slots[0], 42784, &*dep, false);
             let upgrades = match deps.depends[image_num] {
@@ -222,7 +223,7 @@
     pub fn make_bad_secondary_slot_image(self) -> Images {
         let mut bad_flash = self.flash;
         let images = self.slots.into_iter().enumerate().map(|(image_num, slots)| {
-            let dep = BoringDep(image_num);
+            let dep = BoringDep::new(image_num, &NO_DEPS);
             let primaries = install_image(&mut bad_flash, &slots[0], 32784, &dep, false);
             let upgrades = install_image(&mut bad_flash, &slots[1], 41928, &dep, true);
             OneImage {
@@ -569,6 +570,37 @@
         fails > 0
     }
 
+    // Test that an upgrade is rejected.  Assumes that the image was build
+    // such that the upgrade is instead a downgrade.
+    pub fn run_nodowngrade(&self) -> bool {
+        if !Caps::DowngradePrevention.present() {
+            return false;
+        }
+
+        let mut flash = self.flash.clone();
+        let mut fails = 0;
+
+        info!("Try no downgrade");
+
+        // First, do a normal upgrade.
+        let (result, _) = c::boot_go(&mut flash, &self.areadesc, None, false);
+        if result != 0 {
+            warn!("Failed first boot");
+            fails += 1;
+        }
+
+        if !self.verify_images(&flash, 0, 0) {
+            warn!("Failed verification after downgrade rejection");
+            fails += 1;
+        }
+
+        if fails > 0 {
+            error!("Error testing downgrade rejection");
+        }
+
+        fails > 0
+    }
+
     // Tests a new image written to the primary slot that already has magic and
     // image_ok set while there is no image on the secondary slot, so no revert
     // should ever happen...
@@ -1450,6 +1482,7 @@
 
 /// The image header
 #[repr(C)]
+#[derive(Debug)]
 pub struct ImageHeader {
     magic: u32,
     load_addr: u32,
diff --git a/sim/src/lib.rs b/sim/src/lib.rs
index 4e2f6c3..ec8e5ed 100644
--- a/sim/src/lib.rs
+++ b/sim/src/lib.rs
@@ -23,7 +23,9 @@
         DepTest,
         DepType,
         UpgradeInfo,
-        NO_DEPS,},
+        NO_DEPS,
+        REV_DEPS,
+    },
     image::{
         ImagesBuilder,
         Images,