blob: ce3abfc4d3af9de4fa4830e7edff5b305817d403 [file] [log] [blame]
David Brown2639e072017-10-11 11:18:44 -06001use docopt::Docopt;
David Brown5c9e0f12019-01-09 16:34:33 -07002use log::{warn, error};
David Brown10b5de12019-01-02 16:10:01 -07003use std::{
4 fmt,
David Brown10b5de12019-01-02 16:10:01 -07005 process,
David Brown10b5de12019-01-02 16:10:01 -07006};
7use serde_derive::Deserialize;
David Brown2639e072017-10-11 11:18:44 -06008
9mod caps;
David Brown5c9e0f12019-01-09 16:34:33 -070010mod image;
David Brown2639e072017-10-11 11:18:44 -060011mod tlv;
David Brownca7b5d32017-11-03 08:37:38 -060012pub mod testlog;
David Brown2639e072017-10-11 11:18:44 -060013
David Brown5c9e0f12019-01-09 16:34:33 -070014use simflash::{SimFlash, SimFlashMap};
David Brown2639e072017-10-11 11:18:44 -060015use mcuboot_sys::{c, AreaDesc, FlashId};
David Brown5c9e0f12019-01-09 16:34:33 -070016
17use crate::image::{
18 Images,
19 install_image,
20 mark_upgrade,
21 SlotInfo,
22 show_sizes,
23};
David Brown2639e072017-10-11 11:18:44 -060024
25const USAGE: &'static str = "
26Mcuboot simulator
27
28Usage:
29 bootsim sizes
30 bootsim run --device TYPE [--align SIZE]
31 bootsim runall
32 bootsim (--help | --version)
33
34Options:
35 -h, --help Show this message
36 --version Version
37 --device TYPE MCU to simulate
38 Valid values: stm32f4, k64f
39 --align SIZE Flash write alignment
40";
41
42#[derive(Debug, Deserialize)]
43struct Args {
44 flag_help: bool,
45 flag_version: bool,
46 flag_device: Option<DeviceName>,
47 flag_align: Option<AlignArg>,
48 cmd_sizes: bool,
49 cmd_run: bool,
50 cmd_runall: bool,
51}
52
53#[derive(Copy, Clone, Debug, Deserialize)]
Fabio Utzigafb2bc92018-11-19 16:11:52 -020054pub enum DeviceName { Stm32f4, K64f, K64fBig, Nrf52840, Nrf52840SpiFlash, }
David Brown2639e072017-10-11 11:18:44 -060055
David Browndd2b1182017-11-02 15:39:21 -060056pub static ALL_DEVICES: &'static [DeviceName] = &[
David Brown2639e072017-10-11 11:18:44 -060057 DeviceName::Stm32f4,
58 DeviceName::K64f,
59 DeviceName::K64fBig,
60 DeviceName::Nrf52840,
Fabio Utzigafb2bc92018-11-19 16:11:52 -020061 DeviceName::Nrf52840SpiFlash,
David Brown2639e072017-10-11 11:18:44 -060062];
63
64impl fmt::Display for DeviceName {
David Brown10b5de12019-01-02 16:10:01 -070065 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
David Brown2639e072017-10-11 11:18:44 -060066 let name = match *self {
67 DeviceName::Stm32f4 => "stm32f4",
68 DeviceName::K64f => "k64f",
69 DeviceName::K64fBig => "k64fbig",
70 DeviceName::Nrf52840 => "nrf52840",
Fabio Utzigafb2bc92018-11-19 16:11:52 -020071 DeviceName::Nrf52840SpiFlash => "Nrf52840SpiFlash",
David Brown2639e072017-10-11 11:18:44 -060072 };
73 f.write_str(name)
74 }
75}
76
77#[derive(Debug)]
78struct AlignArg(u8);
79
80struct AlignArgVisitor;
81
82impl<'de> serde::de::Visitor<'de> for AlignArgVisitor {
83 type Value = AlignArg;
84
David Brown10b5de12019-01-02 16:10:01 -070085 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
David Brown2639e072017-10-11 11:18:44 -060086 formatter.write_str("1, 2, 4 or 8")
87 }
88
89 fn visit_u8<E>(self, n: u8) -> Result<Self::Value, E>
90 where E: serde::de::Error
91 {
92 Ok(match n {
93 1 | 2 | 4 | 8 => AlignArg(n),
94 n => {
95 let err = format!("Could not deserialize '{}' as alignment", n);
96 return Err(E::custom(err));
97 }
98 })
99 }
100}
101
102impl<'de> serde::de::Deserialize<'de> for AlignArg {
103 fn deserialize<D>(d: D) -> Result<AlignArg, D::Error>
104 where D: serde::de::Deserializer<'de>
105 {
106 d.deserialize_u8(AlignArgVisitor)
107 }
108}
109
110pub fn main() {
111 let args: Args = Docopt::new(USAGE)
112 .and_then(|d| d.deserialize())
113 .unwrap_or_else(|e| e.exit());
114 // println!("args: {:#?}", args);
115
116 if args.cmd_sizes {
117 show_sizes();
118 return;
119 }
120
121 let mut status = RunStatus::new();
122 if args.cmd_run {
123
124 let align = args.flag_align.map(|x| x.0).unwrap_or(1);
125
126
127 let device = match args.flag_device {
128 None => panic!("Missing mandatory device argument"),
129 Some(dev) => dev,
130 };
131
Fabio Utzigea0290b2018-08-09 14:23:01 -0300132 status.run_single(device, align, 0xff);
David Brown2639e072017-10-11 11:18:44 -0600133 }
134
135 if args.cmd_runall {
136 for &dev in ALL_DEVICES {
137 for &align in &[1, 2, 4, 8] {
Fabio Utzigea0290b2018-08-09 14:23:01 -0300138 for &erased_val in &[0, 0xff] {
139 status.run_single(dev, align, erased_val);
140 }
David Brown2639e072017-10-11 11:18:44 -0600141 }
142 }
143 }
144
145 if status.failures > 0 {
146 error!("{} Tests ran with {} failures", status.failures + status.passes, status.failures);
147 process::exit(1);
148 } else {
149 error!("{} Tests ran successfully", status.passes);
150 process::exit(0);
151 }
152}
153
David Browndb9a3952017-11-06 13:16:15 -0700154/// A test run, intended to be run from "cargo test", so panics on failure.
155pub struct Run {
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200156 flashmap: SimFlashMap,
David Browndb9a3952017-11-06 13:16:15 -0700157 areadesc: AreaDesc,
158 slots: [SlotInfo; 2],
David Browndb9a3952017-11-06 13:16:15 -0700159}
160
161impl Run {
Fabio Utzigea0290b2018-08-09 14:23:01 -0300162 pub fn new(device: DeviceName, align: u8, erased_val: u8) -> Run {
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200163 let (flashmap, areadesc) = make_device(device, align, erased_val);
David Browndb9a3952017-11-06 13:16:15 -0700164
David Vincze2d736ad2019-02-18 11:50:22 +0100165 let (primary_slot_base, primary_slot_len, primary_slot_dev_id) =
166 areadesc.find(FlashId::Image0);
167 let (secondary_slot_base, secondary_slot_len, secondary_slot_dev_id) =
168 areadesc.find(FlashId::Image1);
David Browndb9a3952017-11-06 13:16:15 -0700169
Fabio Utzigb841f0a2017-11-24 08:11:05 -0200170 // NOTE: not accounting "swap_size" because it is not used by sim...
David Browndb9a3952017-11-06 13:16:15 -0700171 let offset_from_end = c::boot_magic_sz() + c::boot_max_align() * 2;
172
173 // Construct a primary image.
David Vincze2d736ad2019-02-18 11:50:22 +0100174 let primary_slot = SlotInfo {
175 base_off: primary_slot_base as usize,
176 trailer_off: primary_slot_base + primary_slot_len - offset_from_end,
177 len: primary_slot_len as usize,
178 dev_id: primary_slot_dev_id,
David Browndb9a3952017-11-06 13:16:15 -0700179 };
180
181 // And an upgrade image.
David Vincze2d736ad2019-02-18 11:50:22 +0100182 let secondary_slot = SlotInfo {
183 base_off: secondary_slot_base as usize,
184 trailer_off: secondary_slot_base + secondary_slot_len - offset_from_end,
185 len: secondary_slot_len as usize,
186 dev_id: secondary_slot_dev_id,
David Browndb9a3952017-11-06 13:16:15 -0700187 };
188
189 Run {
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200190 flashmap: flashmap,
David Browndb9a3952017-11-06 13:16:15 -0700191 areadesc: areadesc,
David Vincze2d736ad2019-02-18 11:50:22 +0100192 slots: [primary_slot, secondary_slot],
David Browndb9a3952017-11-06 13:16:15 -0700193 }
194 }
195
196 pub fn each_device<F>(f: F)
197 where F: Fn(&mut Run)
198 {
199 for &dev in ALL_DEVICES {
200 for &align in &[1, 2, 4, 8] {
Fabio Utzigea0290b2018-08-09 14:23:01 -0300201 for &erased_val in &[0, 0xff] {
202 let mut run = Run::new(dev, align, erased_val);
203 f(&mut run);
204 }
David Browndb9a3952017-11-06 13:16:15 -0700205 }
206 }
207 }
David Brownf48b9502017-11-06 14:00:26 -0700208
209 /// Construct an `Images` that doesn't expect an upgrade to happen.
210 pub fn make_no_upgrade_image(&self) -> Images {
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200211 let mut flashmap = self.flashmap.clone();
212 let primaries = install_image(&mut flashmap, &self.slots, 0, 32784, false);
213 let upgrades = install_image(&mut flashmap, &self.slots, 1, 41928, false);
David Brownf48b9502017-11-06 14:00:26 -0700214 Images {
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200215 flashmap: flashmap,
David Brownf48b9502017-11-06 14:00:26 -0700216 areadesc: self.areadesc.clone(),
Fabio Utzig1e48b912018-09-18 09:04:18 -0300217 slots: [self.slots[0].clone(), self.slots[1].clone()],
218 primaries: primaries,
219 upgrades: upgrades,
David Brownc49811e2017-11-06 14:20:45 -0700220 total_count: None,
David Brownf48b9502017-11-06 14:00:26 -0700221 }
222 }
223
224 /// Construct an `Images` for normal testing.
225 pub fn make_image(&self) -> Images {
226 let mut images = self.make_no_upgrade_image();
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200227 mark_upgrade(&mut images.flashmap, &images.slots[1]);
David Brownc49811e2017-11-06 14:20:45 -0700228
229 // upgrades without fails, counts number of flash operations
230 let total_count = match images.run_basic_upgrade() {
231 Ok(v) => v,
232 Err(_) => {
233 panic!("Unable to perform basic upgrade");
234 },
235 };
236
237 images.total_count = Some(total_count);
David Brownf48b9502017-11-06 14:00:26 -0700238 images
239 }
240
David Vincze2d736ad2019-02-18 11:50:22 +0100241 pub fn make_bad_secondary_slot_image(&self) -> Images {
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200242 let mut bad_flashmap = self.flashmap.clone();
243 let primaries = install_image(&mut bad_flashmap, &self.slots, 0, 32784, false);
244 let upgrades = install_image(&mut bad_flashmap, &self.slots, 1, 41928, true);
David Brownf48b9502017-11-06 14:00:26 -0700245 Images {
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200246 flashmap: bad_flashmap,
David Brownf48b9502017-11-06 14:00:26 -0700247 areadesc: self.areadesc.clone(),
Fabio Utzig1e48b912018-09-18 09:04:18 -0300248 slots: [self.slots[0].clone(), self.slots[1].clone()],
249 primaries: primaries,
250 upgrades: upgrades,
David Brownc49811e2017-11-06 14:20:45 -0700251 total_count: None,
David Brownf48b9502017-11-06 14:00:26 -0700252 }
253 }
David Brownc49811e2017-11-06 14:20:45 -0700254
David Browndb9a3952017-11-06 13:16:15 -0700255}
256
David Browndd2b1182017-11-02 15:39:21 -0600257pub struct RunStatus {
David Brown2639e072017-10-11 11:18:44 -0600258 failures: usize,
259 passes: usize,
260}
261
262impl RunStatus {
David Browndd2b1182017-11-02 15:39:21 -0600263 pub fn new() -> RunStatus {
David Brown2639e072017-10-11 11:18:44 -0600264 RunStatus {
265 failures: 0,
266 passes: 0,
267 }
268 }
269
Fabio Utzigea0290b2018-08-09 14:23:01 -0300270 pub fn run_single(&mut self, device: DeviceName, align: u8, erased_val: u8) {
David Brown2639e072017-10-11 11:18:44 -0600271 warn!("Running on device {} with alignment {}", device, align);
272
Fabio Utzigea0290b2018-08-09 14:23:01 -0300273 let run = Run::new(device, align, erased_val);
David Brown2639e072017-10-11 11:18:44 -0600274
David Brown2639e072017-10-11 11:18:44 -0600275 let mut failed = false;
276
David Vincze2d736ad2019-02-18 11:50:22 +0100277 // Creates a badly signed image in the secondary slot to check that
278 // it is not upgraded to
279 let bad_secondary_slot_image = run.make_bad_secondary_slot_image();
David Brown2639e072017-10-11 11:18:44 -0600280
David Vincze2d736ad2019-02-18 11:50:22 +0100281 failed |= bad_secondary_slot_image.run_signfail_upgrade();
David Brown2639e072017-10-11 11:18:44 -0600282
David Brownf48b9502017-11-06 14:00:26 -0700283 let images = run.make_no_upgrade_image();
David Brown5f7ec2b2017-11-06 13:54:02 -0700284 failed |= images.run_norevert_newimage();
David Brown2639e072017-10-11 11:18:44 -0600285
David Brownf48b9502017-11-06 14:00:26 -0700286 let images = run.make_image();
David Brown2639e072017-10-11 11:18:44 -0600287
David Brown5f7ec2b2017-11-06 13:54:02 -0700288 failed |= images.run_basic_revert();
David Brownc49811e2017-11-06 14:20:45 -0700289 failed |= images.run_revert_with_fails();
290 failed |= images.run_perm_with_fails();
291 failed |= images.run_perm_with_random_fails(5);
David Brown5f7ec2b2017-11-06 13:54:02 -0700292 failed |= images.run_norevert();
David Brown2639e072017-10-11 11:18:44 -0600293
Fabio Utzigb841f0a2017-11-24 08:11:05 -0200294 failed |= images.run_with_status_fails_complete();
Fabio Utzigeedcc452017-11-24 10:48:52 -0200295 failed |= images.run_with_status_fails_with_reset();
Fabio Utzigb841f0a2017-11-24 08:11:05 -0200296
David Brown2639e072017-10-11 11:18:44 -0600297 //show_flash(&flash);
298
299 if failed {
300 self.failures += 1;
301 } else {
302 self.passes += 1;
303 }
304 }
David Browndd2b1182017-11-02 15:39:21 -0600305
306 pub fn failures(&self) -> usize {
307 self.failures
308 }
David Brown2639e072017-10-11 11:18:44 -0600309}
310
David Browndecbd042017-10-19 10:43:17 -0600311/// Build the Flash and area descriptor for a given device.
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200312pub fn make_device(device: DeviceName, align: u8, erased_val: u8) -> (SimFlashMap, AreaDesc) {
David Browndecbd042017-10-19 10:43:17 -0600313 match device {
314 DeviceName::Stm32f4 => {
315 // STM style flash. Large sectors, with a large scratch area.
316 let flash = SimFlash::new(vec![16 * 1024, 16 * 1024, 16 * 1024, 16 * 1024,
317 64 * 1024,
318 128 * 1024, 128 * 1024, 128 * 1024],
Fabio Utzigea0290b2018-08-09 14:23:01 -0300319 align as usize, erased_val);
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200320 let dev_id = 0;
Fabio Utzig1caef132018-10-26 15:40:53 -0300321 let mut areadesc = AreaDesc::new();
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200322 areadesc.add_flash_sectors(dev_id, &flash);
323 areadesc.add_image(0x020000, 0x020000, FlashId::Image0, dev_id);
324 areadesc.add_image(0x040000, 0x020000, FlashId::Image1, dev_id);
325 areadesc.add_image(0x060000, 0x020000, FlashId::ImageScratch, dev_id);
326
327 let mut flashmap = SimFlashMap::new();
328 flashmap.insert(dev_id, flash);
329 (flashmap, areadesc)
David Browndecbd042017-10-19 10:43:17 -0600330 }
331 DeviceName::K64f => {
332 // NXP style flash. Small sectors, one small sector for scratch.
Fabio Utzigea0290b2018-08-09 14:23:01 -0300333 let flash = SimFlash::new(vec![4096; 128], align as usize, erased_val);
David Browndecbd042017-10-19 10:43:17 -0600334
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200335 let dev_id = 0;
Fabio Utzig1caef132018-10-26 15:40:53 -0300336 let mut areadesc = AreaDesc::new();
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200337 areadesc.add_flash_sectors(dev_id, &flash);
338 areadesc.add_image(0x020000, 0x020000, FlashId::Image0, dev_id);
339 areadesc.add_image(0x040000, 0x020000, FlashId::Image1, dev_id);
340 areadesc.add_image(0x060000, 0x001000, FlashId::ImageScratch, dev_id);
341
342 let mut flashmap = SimFlashMap::new();
343 flashmap.insert(dev_id, flash);
344 (flashmap, areadesc)
David Browndecbd042017-10-19 10:43:17 -0600345 }
346 DeviceName::K64fBig => {
347 // Simulating an STM style flash on top of an NXP style flash. Underlying flash device
348 // uses small sectors, but we tell the bootloader they are large.
Fabio Utzigea0290b2018-08-09 14:23:01 -0300349 let flash = SimFlash::new(vec![4096; 128], align as usize, erased_val);
David Browndecbd042017-10-19 10:43:17 -0600350
Fabio Utzig1caef132018-10-26 15:40:53 -0300351 let dev_id = 0;
352 let mut areadesc = AreaDesc::new();
353 areadesc.add_flash_sectors(dev_id, &flash);
354 areadesc.add_simple_image(0x020000, 0x020000, FlashId::Image0, dev_id);
355 areadesc.add_simple_image(0x040000, 0x020000, FlashId::Image1, dev_id);
356 areadesc.add_simple_image(0x060000, 0x020000, FlashId::ImageScratch, dev_id);
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200357
358 let mut flashmap = SimFlashMap::new();
359 flashmap.insert(dev_id, flash);
360 (flashmap, areadesc)
David Browndecbd042017-10-19 10:43:17 -0600361 }
362 DeviceName::Nrf52840 => {
363 // Simulating the flash on the nrf52840 with partitions set up so that the scratch size
364 // does not divide into the image size.
Fabio Utzigea0290b2018-08-09 14:23:01 -0300365 let flash = SimFlash::new(vec![4096; 128], align as usize, erased_val);
David Browndecbd042017-10-19 10:43:17 -0600366
Fabio Utzig1caef132018-10-26 15:40:53 -0300367 let dev_id = 0;
368 let mut areadesc = AreaDesc::new();
369 areadesc.add_flash_sectors(dev_id, &flash);
370 areadesc.add_image(0x008000, 0x034000, FlashId::Image0, dev_id);
371 areadesc.add_image(0x03c000, 0x034000, FlashId::Image1, dev_id);
372 areadesc.add_image(0x070000, 0x00d000, FlashId::ImageScratch, dev_id);
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200373
374 let mut flashmap = SimFlashMap::new();
375 flashmap.insert(dev_id, flash);
376 (flashmap, areadesc)
377 }
378 DeviceName::Nrf52840SpiFlash => {
379 // Simulate nrf52840 with external SPI flash. The external SPI flash
380 // has a larger sector size so for now store scratch on that flash.
381 let flash0 = SimFlash::new(vec![4096; 128], align as usize, erased_val);
Fabio Utzig64650772018-11-19 16:51:13 -0200382 let flash1 = SimFlash::new(vec![8192; 64], align as usize, erased_val);
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200383
384 let mut areadesc = AreaDesc::new();
385 areadesc.add_flash_sectors(0, &flash0);
386 areadesc.add_flash_sectors(1, &flash1);
387
388 areadesc.add_image(0x008000, 0x068000, FlashId::Image0, 0);
389 areadesc.add_image(0x000000, 0x068000, FlashId::Image1, 1);
390 areadesc.add_image(0x068000, 0x018000, FlashId::ImageScratch, 1);
391
392 let mut flashmap = SimFlashMap::new();
393 flashmap.insert(0, flash0);
394 flashmap.insert(1, flash1);
395 (flashmap, areadesc)
David Browndecbd042017-10-19 10:43:17 -0600396 }
397 }
398}