sim: Add dependency tests to simulator
Create a trait `Depender` which is passed down to the image create and
verification to check and test for various types of dependency
resolution. Add a test that uses this to test the simple case of unmet
dependencies preventing an upgrade.
The actual test is disabled (with an `if false ...`) because the code
under test loops forever in this configuration.
Signed-off-by: David Brown <david.brown@linaro.org>
diff --git a/sim/src/depends.rs b/sim/src/depends.rs
new file mode 100644
index 0000000..9036d8d
--- /dev/null
+++ b/sim/src/depends.rs
@@ -0,0 +1,133 @@
+//! Support and tests related to the dependency management for multi-image
+//! support.
+
+use crate::image::ImageVersion;
+
+pub trait Depender {
+ /// Generate a version for this particular image. The slot indicates
+ /// which slot this is being put in.
+ fn my_version(&self, offset: usize, slot: usize) -> ImageVersion;
+
+ /// Return dependencies for this image/slot combination.
+ fn my_deps(&self, offset: usize, slot: usize) -> Vec<ImageVersion>;
+
+ /// Return the image ID of the other version.
+ fn other_id(&self) -> u8;
+}
+
+/// 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);
+
+impl Depender for BoringDep {
+ fn my_version(&self, _offset: usize, slot: usize) -> ImageVersion {
+ ImageVersion::new_synthetic(self.0 as u8, slot as u8, 0)
+ }
+
+ fn my_deps(&self, _offset: usize, _slot: usize) -> Vec<ImageVersion> {
+ vec![]
+ }
+
+ fn other_id(&self) -> u8 {
+ 0
+ }
+}
+
+/// An individual test of the dependency mechanism describes one of the
+/// possibilities for the dependency information for each image, and what
+/// the expected outcome is.
+#[derive(Clone, Debug)]
+pub struct DepTest {
+ /// What kinds of dependency should be installed in the image.
+ pub depends: [DepType; 2],
+
+ /// What is the expected outcome of the upgrade.
+ pub upgrades: [UpgradeInfo; 2],
+}
+
+/// Describes the various types of dependency information that can be
+/// provided.
+#[derive(Clone, Debug)]
+pub enum DepType {
+ /// Do not include dependency information
+ Nothing,
+ /// Provide dependency information that matches the other image.
+ Correct,
+ /// Provide dependency information describing something newer than the
+ /// other image.
+ Newer,
+}
+
+/// Describes what our expectation is for an upgrade.
+#[derive(Clone, Debug)]
+pub enum UpgradeInfo {
+ /// The current version should be held.
+ Held,
+ /// The image should be upgraded
+ Upgraded,
+}
+
+/// A "test" that gives no dependency information.
+pub static NO_DEPS: DepTest = DepTest {
+ depends: [DepType::Nothing, DepType::Nothing],
+ upgrades: [UpgradeInfo::Upgraded, UpgradeInfo::Upgraded],
+};
+
+/// A PairDep describes the dependencies between two pairs.
+pub struct PairDep {
+ /// The image number of this image.
+ number: usize,
+
+ test: DepTest,
+}
+
+impl PairDep {
+ pub fn new(total_image: usize, my_image: usize, deps: &DepTest) -> PairDep {
+ if total_image != 2 {
+ panic!("PairDep only works when there are two images");
+ }
+
+ PairDep {
+ number: my_image,
+ test: deps.clone(),
+ }
+ }
+}
+
+impl Depender for PairDep {
+ fn my_version(&self, _offset: usize, slot: usize) -> ImageVersion {
+ ImageVersion::new_synthetic(self.number as u8, slot as u8, 0)
+ }
+
+ fn my_deps(&self, _offset: usize, slot: usize) -> Vec<ImageVersion> {
+ match self.test.depends[slot] {
+ DepType::Nothing => vec![],
+ DepType::Correct => vec![
+ ImageVersion::new_synthetic(self.other_id(), slot as u8, 0)
+ ],
+ DepType::Newer => vec![
+ ImageVersion::new_synthetic(self.other_id(), slot as u8, 1)
+ ],
+ }
+ }
+
+ fn other_id(&self) -> u8 {
+ (1 - self.number) as u8
+ }
+}
+
+impl ImageVersion {
+ /// Generate an image version based on some key information. The image
+ /// number influences the major version number (by an arbitrary factor),
+ /// and the slot affects the major number on the build_number. The minor
+ /// number can also be given to force numbers to be different.
+ fn new_synthetic(image_id: u8, slot: u8, minor: u8) -> ImageVersion {
+ ImageVersion {
+ major: image_id * 20 + slot,
+ minor: minor,
+ revision: 1,
+ build_num: slot as u32,
+ }
+ }
+}
diff --git a/sim/src/image.rs b/sim/src/image.rs
index b0d996d..6b351f8 100644
--- a/sim/src/image.rs
+++ b/sim/src/image.rs
@@ -23,6 +23,13 @@
DeviceName,
};
use crate::caps::Caps;
+use crate::depends::{
+ BoringDep,
+ Depender,
+ DepTest,
+ PairDep,
+ UpgradeInfo,
+};
use crate::tlv::{ManifestGen, TlvGen, TlvFlags, AES_SEC_KEY};
/// A builder for Images. This describes a single run of the simulator,
@@ -139,11 +146,17 @@
}
/// Construct an `Images` that doesn't expect an upgrade to happen.
- pub fn make_no_upgrade_image(self) -> Images {
+ pub fn make_no_upgrade_image(self, deps: &DepTest) -> Images {
+ let num_images = self.num_images();
let mut flash = self.flash;
- let images = self.slots.into_iter().map(|slots| {
- let primaries = install_image(&mut flash, &slots[0], 42784, false);
- let upgrades = install_image(&mut flash, &slots[1], 46928, false);
+ let images = self.slots.into_iter().enumerate().map(|(image_num, slots)| {
+ let dep: Box<dyn Depender> = if num_images > 1 {
+ Box::new(PairDep::new(num_images, image_num, deps))
+ } else {
+ Box::new(BoringDep(image_num))
+ };
+ let primaries = install_image(&mut flash, &slots[0], 42784, &*dep, false);
+ let upgrades = install_image(&mut flash, &slots[1], 46928, &*dep, false);
OneImage {
slots: slots,
primaries: primaries,
@@ -157,8 +170,8 @@
}
}
- pub fn make_image(self, permanent: bool) -> Images {
- let mut images = self.make_no_upgrade_image();
+ pub fn make_image(self, deps: &DepTest, permanent: bool) -> Images {
+ let mut images = self.make_no_upgrade_image(deps);
for image in &images.images {
mark_upgrade(&mut images.flash, &image.slots[1]);
}
@@ -177,9 +190,10 @@
pub fn make_bad_secondary_slot_image(self) -> Images {
let mut bad_flash = self.flash;
- let images = self.slots.into_iter().map(|slots| {
- let primaries = install_image(&mut bad_flash, &slots[0], 32784, false);
- let upgrades = install_image(&mut bad_flash, &slots[1], 41928, true);
+ let images = self.slots.into_iter().enumerate().map(|(image_num, slots)| {
+ let dep = BoringDep(image_num);
+ 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 {
slots: slots,
primaries: primaries,
@@ -298,6 +312,10 @@
}
}
}
+
+ pub fn num_images(&self) -> usize {
+ self.slots.len()
+ }
}
impl Images {
@@ -317,6 +335,14 @@
}
}
+ /// Test a simple upgrade, with dependencies given, and verify that the
+ /// image does as is described in the test.
+ pub fn run_check_deps(&self, deps: &DepTest) -> bool {
+ let (flash, _) = self.try_upgrade(None, true);
+
+ self.verify_dep_images(&flash, deps)
+ }
+
pub fn run_basic_revert(&self) -> bool {
if Caps::OverwriteUpgrade.present() {
return false;
@@ -961,6 +987,23 @@
true
}
+ /// Verify the images, according to the dependency test.
+ fn verify_dep_images(&self, flash: &SimMultiFlash, deps: &DepTest) -> bool {
+ for (image_num, (image, upgrade)) in self.images.iter().zip(deps.upgrades.iter()).enumerate() {
+ info!("Upgrade: slot:{}, {:?}", image_num, upgrade);
+ if !verify_image(flash, &image.slots[0],
+ match upgrade {
+ UpgradeInfo::Upgraded => &image.upgrades,
+ UpgradeInfo::Held => &image.primaries,
+ }) {
+ error!("Failed to upgrade properly: image: {}, upgrade: {:?}", image_num, upgrade);
+ return true;
+ }
+ }
+
+ false
+ }
+
/// Verify that at least one of the trailers of the images have the
/// specified values.
fn verify_trailers_loose(&self, flash: &SimMultiFlash, slot: usize,
@@ -1018,13 +1061,18 @@
/// 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 SimMultiFlash, slot: &SlotInfo, len: usize,
- bad_sig: bool) -> ImageData {
+ deps: &dyn Depender, bad_sig: bool) -> ImageData {
let offset = slot.base_off;
let slot_len = slot.len;
let dev_id = slot.dev_id;
let mut tlv: Box<dyn ManifestGen> = Box::new(make_tlv());
+ // Add the dependencies early to the tlv.
+ for dep in deps.my_deps(offset, slot.index) {
+ tlv.add_dependency(deps.other_id(), &dep);
+ }
+
const HDR_SIZE: usize = 32;
// Generate a boot header. Note that the size doesn't include the header.
@@ -1035,12 +1083,7 @@
protect_tlv_size: tlv.protect_size(),
img_size: len as u32,
flags: tlv.get_flags(),
- ver: ImageVersion {
- major: (offset / (128 * 1024)) as u8,
- minor: 0,
- revision: 1,
- build_num: offset as u32,
- },
+ ver: deps.my_version(offset, slot.index),
_pad2: 0,
};
@@ -1219,8 +1262,8 @@
if buf != ©[..] {
for i in 0 .. buf.len() {
if buf[i] != copy[i] {
- info!("First failure for slot{} at {:#x} {:#x}!={:#x}",
- slot.index, offset + i, buf[i], copy[i]);
+ info!("First failure for slot{} at {:#x} ({:#x} within) {:#x}!={:#x}",
+ slot.index, offset + i, i, buf[i], copy[i]);
break;
}
}
@@ -1309,7 +1352,7 @@
impl AsRaw for ImageHeader {}
#[repr(C)]
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub struct ImageVersion {
pub major: u8,
pub minor: u8,
@@ -1317,7 +1360,7 @@
pub build_num: u32,
}
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub struct SlotInfo {
pub base_off: usize,
pub trailer_off: usize,
diff --git a/sim/src/lib.rs b/sim/src/lib.rs
index 1ed75dd..c8e8963 100644
--- a/sim/src/lib.rs
+++ b/sim/src/lib.rs
@@ -7,13 +7,21 @@
use serde_derive::Deserialize;
mod caps;
+mod depends;
mod image;
mod tlv;
pub mod testlog;
-pub use crate::image::{
- ImagesBuilder,
- show_sizes,
+pub use crate::{
+ depends::{
+ DepTest,
+ DepType,
+ UpgradeInfo,
+ NO_DEPS,},
+ image::{
+ ImagesBuilder,
+ show_sizes,
+ },
};
const USAGE: &'static str = "
@@ -179,10 +187,10 @@
failed |= bad_secondary_slot_image.run_signfail_upgrade();
- let images = run.clone().make_no_upgrade_image();
+ let images = run.clone().make_no_upgrade_image(&NO_DEPS);
failed |= images.run_norevert_newimage();
- let images = run.make_image(true);
+ let images = run.make_image(&NO_DEPS, true);
failed |= images.run_basic_revert();
failed |= images.run_revert_with_fails();
diff --git a/sim/src/tlv.rs b/sim/src/tlv.rs
index 43dd2e2..c6f30dc 100644
--- a/sim/src/tlv.rs
+++ b/sim/src/tlv.rs
@@ -26,7 +26,7 @@
use mcuboot_sys::c;
#[repr(u8)]
-#[derive(Copy, Clone, PartialEq, Eq)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[allow(dead_code)] // TODO: For now
pub enum TlvKinds {
KEYHASH = 0x01,
@@ -74,6 +74,7 @@
fn make_tlv(self: Box<Self>) -> Vec<u8>;
}
+#[derive(Debug)]
pub struct TlvGen {
flags: u32,
kinds: Vec<TlvKinds>,
@@ -85,6 +86,7 @@
dependencies: Vec<Dependency>,
}
+#[derive(Debug)]
struct Dependency {
id: u8,
version: ImageVersion,