// Copyright (c) 2017-2019 Linaro LTD // Copyright (c) 2017-2019 JUUL Labs // Copyright (c) 2019 Arm Limited // // SPDX-License-Identifier: Apache-2.0 use docopt::Docopt; use log::{warn, error}; use std::{ fmt, process, }; use serde_derive::Deserialize; mod caps; mod depends; mod image; mod tlv; mod utils; pub mod testlog; pub use crate::{ depends::{ DepTest, DepType, UpgradeInfo, NO_DEPS, REV_DEPS, }, image::{ ImagesBuilder, Images, show_sizes, }, }; const USAGE: &str = " Mcuboot simulator Usage: bootsim sizes bootsim run --device TYPE [--align SIZE] bootsim runall bootsim (--help | --version) Options: -h, --help Show this message --version Version --device TYPE MCU to simulate Valid values: stm32f4, k64f --align SIZE Flash write alignment "; #[derive(Debug, Deserialize)] struct Args { flag_device: Option, flag_align: Option, cmd_sizes: bool, cmd_run: bool, cmd_runall: bool, } #[derive(Copy, Clone, Debug, Deserialize)] pub enum DeviceName { Stm32f4, K64f, K64fBig, K64fMulti, Nrf52840, Nrf52840SpiFlash, Nrf52840UnequalSlots, } pub static ALL_DEVICES: &[DeviceName] = &[ DeviceName::Stm32f4, DeviceName::K64f, DeviceName::K64fBig, DeviceName::K64fMulti, DeviceName::Nrf52840, DeviceName::Nrf52840SpiFlash, DeviceName::Nrf52840UnequalSlots, ]; 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::K64fMulti => "k64fmulti", DeviceName::Nrf52840 => "nrf52840", DeviceName::Nrf52840SpiFlash => "Nrf52840SpiFlash", DeviceName::Nrf52840UnequalSlots => "Nrf52840UnequalSlots", }; f.write_str(name) } } #[derive(Debug)] struct AlignArg(usize); struct AlignArgVisitor; impl<'de> serde::de::Visitor<'de> for AlignArgVisitor { type Value = AlignArg; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("1, 2, 4 or 8") } fn visit_u32(self, n: u32) -> Result where E: serde::de::Error { Ok(match n { 1 | 2 | 4 | 8 => AlignArg(n as usize), n => { let err = format!("Could not deserialize '{}' as alignment", n); return Err(E::custom(err)); } }) } } impl<'de> serde::de::Deserialize<'de> for AlignArg { fn deserialize(d: D) -> Result where D: serde::de::Deserializer<'de> { d.deserialize_u8(AlignArgVisitor) } } pub fn main() { let args: Args = Docopt::new(USAGE) .and_then(|d| d.deserialize()) .unwrap_or_else(|e| e.exit()); // println!("args: {:#?}", args); if args.cmd_sizes { show_sizes(); return; } let mut status = RunStatus::new(); if args.cmd_run { let align = args.flag_align.map(|x| x.0).unwrap_or(1); let device = match args.flag_device { None => panic!("Missing mandatory device argument"), Some(dev) => dev, }; status.run_single(device, align, 0xff); } if args.cmd_runall { for &dev in ALL_DEVICES { for &align in &[1, 2, 4, 8] { for &erased_val in &[0, 0xff] { status.run_single(dev, align, erased_val); } } } } if status.failures > 0 { error!("{} Tests ran with {} failures", status.failures + status.passes, status.failures); process::exit(1); } else { error!("{} Tests ran successfully", status.passes); process::exit(0); } } #[derive(Default)] pub struct RunStatus { failures: usize, passes: usize, } impl RunStatus { pub fn new() -> RunStatus { RunStatus { failures: 0, passes: 0, } } pub fn run_single(&mut self, device: DeviceName, align: usize, erased_val: u8) { warn!("Running on device {} with alignment {}", device, align); let run = match ImagesBuilder::new(device, align, erased_val) { Ok(builder) => builder, Err(msg) => { warn!("Skipping {}: {}", device, msg); return; } }; let mut failed = false; // Creates a badly signed image in the secondary slot to check that // it is not upgraded to let bad_secondary_slot_image = run.clone().make_bad_secondary_slot_image(); failed |= bad_secondary_slot_image.run_signfail_upgrade(); let images = run.clone().make_no_upgrade_image(&NO_DEPS); failed |= images.run_norevert_newimage(); let images = run.make_image(&NO_DEPS, true); failed |= images.run_basic_revert(); failed |= images.run_revert_with_fails(); failed |= images.run_perm_with_fails(); failed |= images.run_perm_with_random_fails(5); failed |= images.run_norevert(); failed |= images.run_with_status_fails_complete(); failed |= images.run_with_status_fails_with_reset(); //show_flash(&flash); if failed { self.failures += 1; } else { self.passes += 1; } } pub fn failures(&self) -> usize { self.failures } }