sim: Basic ram-load test
Test the basic configuration for ram loading. Instead of a fixed
address for RAM, the values come dynamically from a thread-local
variable (allowing the tests to run in parallel). The size of the ram
along with the address of the buffer in the test address space are
passed in this way.
This tests the single-image configurations of ram loading. Testing
multi-image will take additional work, as the RAM will need to be large
enough for both images, and the second image will need a meaningful
offset address in RAM.
Signed-off-by: David Brown <david.brown@linaro.org>
diff --git a/boot/bootutil/src/bootutil_priv.h b/boot/bootutil/src/bootutil_priv.h
index 3d48225..c730052 100644
--- a/boot/bootutil/src/bootutil_priv.h
+++ b/boot/bootutil/src/bootutil_priv.h
@@ -403,7 +403,26 @@
#endif /* !defined(MCUBOOT_USE_FLASH_AREA_GET_SECTORS) */
#ifdef MCUBOOT_RAM_LOAD
-#define IMAGE_RAM_BASE ((uintptr_t)0)
+# ifdef __BOOTSIM__
+
+/* Query for the layout of a RAM buffer appropriate for holding the
+ * image. This will be per-test-thread, and therefore must be queried
+ * through this call. */
+struct bootsim_ram_info {
+ uint32_t start;
+ uint32_t size;
+ uintptr_t base;
+};
+struct bootsim_ram_info *bootsim_get_ram_info(void);
+
+#define IMAGE_GET_FIELD(field) (bootsim_get_ram_info()->field)
+#define IMAGE_RAM_BASE IMAGE_GET_FIELD(base)
+#define IMAGE_EXECUTABLE_RAM_START IMAGE_GET_FIELD(start)
+#define IMAGE_EXECUTABLE_RAM_SIZE IMAGE_GET_FIELD(size)
+
+# else
+# define IMAGE_RAM_BASE ((uintptr_t)0)
+# endif
#define LOAD_IMAGE_DATA(hdr, fap, start, output, size) \
(memcpy((output),(void*)(IMAGE_RAM_BASE + (hdr)->ih_load_addr + (start)), \
diff --git a/sim/mcuboot-sys/build.rs b/sim/mcuboot-sys/build.rs
index f2649d3..1a3f8b5 100644
--- a/sim/mcuboot-sys/build.rs
+++ b/sim/mcuboot-sys/build.rs
@@ -60,9 +60,6 @@
if ram_load {
conf.define("MCUBOOT_RAM_LOAD", None);
-
- conf.define("IMAGE_EXECUTABLE_RAM_START", "0x10000");
- conf.define("IMAGE_EXECUTABLE_RAM_SIZE", "0x10000");
}
if direct_xip {
diff --git a/sim/mcuboot-sys/src/api.rs b/sim/mcuboot-sys/src/api.rs
index a99c1c8..8d1140d 100644
--- a/sim/mcuboot-sys/src/api.rs
+++ b/sim/mcuboot-sys/src/api.rs
@@ -121,9 +121,20 @@
}
}
+/// This struct describes the RAM layout of the current device. It will be stashed, per test
+/// thread, and queried by the C code.
+#[repr(C)]
+#[derive(Debug, Default)]
+pub struct BootsimRamInfo {
+ pub start: u32,
+ pub size: u32,
+ pub base: usize,
+}
+
thread_local! {
pub static THREAD_CTX: RefCell<FlashContext> = RefCell::new(FlashContext::new());
pub static SIM_CTX: RefCell<CSimContextPtr> = RefCell::new(CSimContextPtr::new());
+ pub static RAM_CTX: RefCell<BootsimRamInfo> = RefCell::new(BootsimRamInfo::default());
}
/// Set the flash device to be used by the simulation. The pointer is unsafely stashed away.
@@ -197,6 +208,32 @@
}
#[no_mangle]
+pub extern "C" fn bootsim_get_ram_info() -> *const BootsimRamInfo {
+ RAM_CTX.with(|ctx| {
+ if ctx.borrow().base == 0 {
+ // Option is messier to get a pointer out of, so just check if the base has been set to
+ // anything.
+ panic!("ram info not set, but being used");
+ }
+ ctx.as_ptr()
+ })
+}
+
+/// Store a copy of this RAM info.
+pub fn set_ram_info(info: BootsimRamInfo) {
+ RAM_CTX.with(|ctx| {
+ ctx.replace(info);
+ });
+}
+
+/// Clear out the ram info.
+pub fn clear_ram_info() {
+ RAM_CTX.with(|ctx| {
+ ctx.borrow_mut().base = 0;
+ });
+}
+
+#[no_mangle]
pub extern fn sim_flash_erase(dev_id: u8, offset: u32, size: u32) -> libc::c_int {
let mut rc: libc::c_int = -19;
THREAD_CTX.with(|ctx| {
diff --git a/sim/mcuboot-sys/src/lib.rs b/sim/mcuboot-sys/src/lib.rs
index 8acb246..9f4cf47 100644
--- a/sim/mcuboot-sys/src/lib.rs
+++ b/sim/mcuboot-sys/src/lib.rs
@@ -10,3 +10,38 @@
pub mod api;
pub use crate::area::{AreaDesc, FlashId};
+
+/// For testing the ram load feature, we need to emulate a block of RAM and be able to pass that
+/// down to the C code. The call down to boot_go should go through this object so that the buffer
+/// itself is managed properly.
+pub struct RamBlock {
+ ram: Vec<u8>,
+ offset: u32, // 32-bit offset.
+}
+
+impl RamBlock {
+ pub fn new(size: u32, offset: u32) -> RamBlock {
+ RamBlock {
+ ram: vec![0; size as usize],
+ offset: offset,
+ }
+ }
+
+ /// Borrow the RAM buffer, with 'offset' being the beginning of the buffer.
+ pub fn borrow(&self) -> &[u8] {
+ &self.ram
+ }
+
+ pub fn invoke<F, R>(&self, act: F) -> R
+ where F: FnOnce() -> R
+ {
+ api::set_ram_info(api::BootsimRamInfo {
+ start: self.offset,
+ size: self.ram.len() as u32,
+ base: &self.ram[0] as *const u8 as usize - self.offset as usize,
+ });
+ let result = act();
+ api::clear_ram_info();
+ result
+ }
+}
diff --git a/sim/src/image.rs b/sim/src/image.rs
index c3f8452..0e826dd 100644
--- a/sim/src/image.rs
+++ b/sim/src/image.rs
@@ -35,7 +35,7 @@
};
use simflash::{Flash, SimFlash, SimMultiFlash};
-use mcuboot_sys::{c, AreaDesc, FlashId};
+use mcuboot_sys::{c, AreaDesc, FlashId, RamBlock};
use crate::{
ALL_DEVICES,
DeviceName,
@@ -53,6 +53,18 @@
use crate::tlv::{ManifestGen, TlvGen, TlvFlags};
use typenum::{U32, U16};
+/// For testing, use a non-zero offset for the ram-load, to make sure the offset is getting used
+/// properly, but the value is not really that important.
+const RAM_LOAD_ADDR: u32 = 1024;
+
+fn ram_load_addr() -> u32 {
+ if Caps::RamLoad.present() {
+ RAM_LOAD_ADDR
+ } else {
+ 0
+ }
+}
+
/// A builder for Images. This describes a single run of the simulator,
/// capturing the configuration of a particular set of devices, including
/// the flash simulator(s) and the information about the slots.
@@ -998,6 +1010,50 @@
false
}
+ /// Test the ram-loading.
+ pub fn run_ram_load(&self) -> bool {
+ if !Caps::RamLoad.present() {
+ return false;
+ }
+
+ // Clone the flash so we can tell if unchanged.
+ let mut flash = self.flash.clone();
+
+ let image = &self.images[0].primaries;
+
+ // Test with the minimal size.
+
+ // First verify that if the RAM is too small, we reject it.
+ let ram = RamBlock::new(image.plain.len() as u32 - 1, RAM_LOAD_ADDR);
+ let result = ram.invoke(|| c::boot_go(&mut flash, &self.areadesc, None, true));
+ if result.success() {
+ error!("Failed to detect RAM too small");
+ return true;
+ }
+ drop(ram);
+
+ // TODO: The code will erase the flash if it doesn't fit. Either verify this, or change
+ // this behavior.
+
+ let mut flash = self.flash.clone();
+
+ let ram = RamBlock::new(image.plain.len() as u32, RAM_LOAD_ADDR);
+ let result = ram.invoke(|| c::boot_go(&mut flash, &self.areadesc, None, true));
+ if !result.success() {
+ error!("Failed to ram-load image");
+ return true;
+ }
+ println!("Result: {:?}", result);
+
+ // Verify the image was loaded correctly.
+ if ram.borrow() != &image.plain {
+ error!("Image not loaded correctly");
+ 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) {
@@ -1312,7 +1368,7 @@
// Generate a boot header. Note that the size doesn't include the header.
let header = ImageHeader {
magic: tlv.get_magic(),
- load_addr: 0,
+ load_addr: if Caps::RamLoad.present() { ram_load_addr() } else { 0 },
hdr_size: HDR_SIZE as u16,
protect_tlv_size: tlv.protect_size(),
img_size: len as u32,
diff --git a/sim/src/tlv.rs b/sim/src/tlv.rs
index 8ccdb0a..a642446 100644
--- a/sim/src/tlv.rs
+++ b/sim/src/tlv.rs
@@ -17,6 +17,7 @@
use byteorder::{
LittleEndian, WriteBytesExt,
};
+use crate::caps::Caps;
use crate::image::ImageVersion;
use log::info;
use ring::{digest, rand, agreement, hkdf, hmac};
@@ -295,7 +296,12 @@
/// Retrieve the header flags for this configuration. This can be called at any time.
fn get_flags(&self) -> u32 {
- self.flags
+ // For the RamLoad case, add in the flag for this feature.
+ if Caps::RamLoad.present() {
+ self.flags | (TlvFlags::RAM_LOAD as u32)
+ } else {
+ self.flags
+ }
}
/// Add bytes to the covered hash.
diff --git a/sim/tests/core.rs b/sim/tests/core.rs
index 104fe79..ca91bbb 100644
--- a/sim/tests/core.rs
+++ b/sim/tests/core.rs
@@ -60,6 +60,7 @@
sim_test!(downgrade_prevention, make_image(&REV_DEPS, true), run_nodowngrade());
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());
// Test various combinations of incorrect dependencies.
test_shell!(dependency_combos, r, {