blob: effd76ee97942f7e98a694df1bafb9305fbee958 [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 Brown01617af2019-02-28 10:45:12 -0700154/// A Run describes a single run of the simulator. It captures the
155/// configuration of a particular device configuration, including the flash
156/// devices and the information about the slots. This can be thought of as
157/// a builder for `Images`.
158#[derive(Clone)]
David Browndb9a3952017-11-06 13:16:15 -0700159pub struct Run {
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200160 flashmap: SimFlashMap,
David Browndb9a3952017-11-06 13:16:15 -0700161 areadesc: AreaDesc,
162 slots: [SlotInfo; 2],
David Browndb9a3952017-11-06 13:16:15 -0700163}
164
165impl Run {
Fabio Utzigea0290b2018-08-09 14:23:01 -0300166 pub fn new(device: DeviceName, align: u8, erased_val: u8) -> Run {
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200167 let (flashmap, areadesc) = make_device(device, align, erased_val);
David Browndb9a3952017-11-06 13:16:15 -0700168
David Vincze2d736ad2019-02-18 11:50:22 +0100169 let (primary_slot_base, primary_slot_len, primary_slot_dev_id) =
170 areadesc.find(FlashId::Image0);
171 let (secondary_slot_base, secondary_slot_len, secondary_slot_dev_id) =
172 areadesc.find(FlashId::Image1);
David Browndb9a3952017-11-06 13:16:15 -0700173
Fabio Utzigb841f0a2017-11-24 08:11:05 -0200174 // NOTE: not accounting "swap_size" because it is not used by sim...
David Browndb9a3952017-11-06 13:16:15 -0700175 let offset_from_end = c::boot_magic_sz() + c::boot_max_align() * 2;
176
177 // Construct a primary image.
David Vincze2d736ad2019-02-18 11:50:22 +0100178 let primary_slot = SlotInfo {
179 base_off: primary_slot_base as usize,
180 trailer_off: primary_slot_base + primary_slot_len - offset_from_end,
181 len: primary_slot_len as usize,
182 dev_id: primary_slot_dev_id,
David Browndb9a3952017-11-06 13:16:15 -0700183 };
184
185 // And an upgrade image.
David Vincze2d736ad2019-02-18 11:50:22 +0100186 let secondary_slot = SlotInfo {
187 base_off: secondary_slot_base as usize,
188 trailer_off: secondary_slot_base + secondary_slot_len - offset_from_end,
189 len: secondary_slot_len as usize,
190 dev_id: secondary_slot_dev_id,
David Browndb9a3952017-11-06 13:16:15 -0700191 };
192
193 Run {
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200194 flashmap: flashmap,
David Browndb9a3952017-11-06 13:16:15 -0700195 areadesc: areadesc,
David Vincze2d736ad2019-02-18 11:50:22 +0100196 slots: [primary_slot, secondary_slot],
David Browndb9a3952017-11-06 13:16:15 -0700197 }
198 }
199
200 pub fn each_device<F>(f: F)
David Brown01617af2019-02-28 10:45:12 -0700201 where F: Fn(Run)
David Browndb9a3952017-11-06 13:16:15 -0700202 {
203 for &dev in ALL_DEVICES {
204 for &align in &[1, 2, 4, 8] {
Fabio Utzigea0290b2018-08-09 14:23:01 -0300205 for &erased_val in &[0, 0xff] {
David Brown01617af2019-02-28 10:45:12 -0700206 let run = Run::new(dev, align, erased_val);
207 f(run);
Fabio Utzigea0290b2018-08-09 14:23:01 -0300208 }
David Browndb9a3952017-11-06 13:16:15 -0700209 }
210 }
211 }
David Brownf48b9502017-11-06 14:00:26 -0700212
213 /// Construct an `Images` that doesn't expect an upgrade to happen.
David Brown01617af2019-02-28 10:45:12 -0700214 pub fn make_no_upgrade_image(self) -> Images {
215 let mut flashmap = self.flashmap;
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200216 let primaries = install_image(&mut flashmap, &self.slots, 0, 32784, false);
217 let upgrades = install_image(&mut flashmap, &self.slots, 1, 41928, false);
David Brownf48b9502017-11-06 14:00:26 -0700218 Images {
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200219 flashmap: flashmap,
David Brown01617af2019-02-28 10:45:12 -0700220 areadesc: self.areadesc,
221 slots: self.slots,
Fabio Utzig1e48b912018-09-18 09:04:18 -0300222 primaries: primaries,
223 upgrades: upgrades,
David Brownc49811e2017-11-06 14:20:45 -0700224 total_count: None,
David Brownf48b9502017-11-06 14:00:26 -0700225 }
226 }
227
228 /// Construct an `Images` for normal testing.
David Brown01617af2019-02-28 10:45:12 -0700229 pub fn make_image(self) -> Images {
David Brownf48b9502017-11-06 14:00:26 -0700230 let mut images = self.make_no_upgrade_image();
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200231 mark_upgrade(&mut images.flashmap, &images.slots[1]);
David Brownc49811e2017-11-06 14:20:45 -0700232
233 // upgrades without fails, counts number of flash operations
234 let total_count = match images.run_basic_upgrade() {
235 Ok(v) => v,
236 Err(_) => {
237 panic!("Unable to perform basic upgrade");
238 },
239 };
240
241 images.total_count = Some(total_count);
David Brownf48b9502017-11-06 14:00:26 -0700242 images
243 }
244
David Brown01617af2019-02-28 10:45:12 -0700245 pub fn make_bad_secondary_slot_image(self) -> Images {
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200246 let mut bad_flashmap = self.flashmap.clone();
247 let primaries = install_image(&mut bad_flashmap, &self.slots, 0, 32784, false);
248 let upgrades = install_image(&mut bad_flashmap, &self.slots, 1, 41928, true);
David Brownf48b9502017-11-06 14:00:26 -0700249 Images {
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200250 flashmap: bad_flashmap,
David Brown01617af2019-02-28 10:45:12 -0700251 areadesc: self.areadesc,
252 slots: self.slots,
Fabio Utzig1e48b912018-09-18 09:04:18 -0300253 primaries: primaries,
254 upgrades: upgrades,
David Brownc49811e2017-11-06 14:20:45 -0700255 total_count: None,
David Brownf48b9502017-11-06 14:00:26 -0700256 }
257 }
David Brownc49811e2017-11-06 14:20:45 -0700258
David Browndb9a3952017-11-06 13:16:15 -0700259}
260
David Browndd2b1182017-11-02 15:39:21 -0600261pub struct RunStatus {
David Brown2639e072017-10-11 11:18:44 -0600262 failures: usize,
263 passes: usize,
264}
265
266impl RunStatus {
David Browndd2b1182017-11-02 15:39:21 -0600267 pub fn new() -> RunStatus {
David Brown2639e072017-10-11 11:18:44 -0600268 RunStatus {
269 failures: 0,
270 passes: 0,
271 }
272 }
273
Fabio Utzigea0290b2018-08-09 14:23:01 -0300274 pub fn run_single(&mut self, device: DeviceName, align: u8, erased_val: u8) {
David Brown2639e072017-10-11 11:18:44 -0600275 warn!("Running on device {} with alignment {}", device, align);
276
Fabio Utzigea0290b2018-08-09 14:23:01 -0300277 let run = Run::new(device, align, erased_val);
David Brown2639e072017-10-11 11:18:44 -0600278
David Brown2639e072017-10-11 11:18:44 -0600279 let mut failed = false;
280
David Vincze2d736ad2019-02-18 11:50:22 +0100281 // Creates a badly signed image in the secondary slot to check that
282 // it is not upgraded to
David Brown01617af2019-02-28 10:45:12 -0700283 let bad_secondary_slot_image = run.clone().make_bad_secondary_slot_image();
David Brown2639e072017-10-11 11:18:44 -0600284
David Vincze2d736ad2019-02-18 11:50:22 +0100285 failed |= bad_secondary_slot_image.run_signfail_upgrade();
David Brown2639e072017-10-11 11:18:44 -0600286
David Brown01617af2019-02-28 10:45:12 -0700287 let images = run.clone().make_no_upgrade_image();
David Brown5f7ec2b2017-11-06 13:54:02 -0700288 failed |= images.run_norevert_newimage();
David Brown2639e072017-10-11 11:18:44 -0600289
David Brownf48b9502017-11-06 14:00:26 -0700290 let images = run.make_image();
David Brown2639e072017-10-11 11:18:44 -0600291
David Brown5f7ec2b2017-11-06 13:54:02 -0700292 failed |= images.run_basic_revert();
David Brownc49811e2017-11-06 14:20:45 -0700293 failed |= images.run_revert_with_fails();
294 failed |= images.run_perm_with_fails();
295 failed |= images.run_perm_with_random_fails(5);
David Brown5f7ec2b2017-11-06 13:54:02 -0700296 failed |= images.run_norevert();
David Brown2639e072017-10-11 11:18:44 -0600297
Fabio Utzigb841f0a2017-11-24 08:11:05 -0200298 failed |= images.run_with_status_fails_complete();
Fabio Utzigeedcc452017-11-24 10:48:52 -0200299 failed |= images.run_with_status_fails_with_reset();
Fabio Utzigb841f0a2017-11-24 08:11:05 -0200300
David Brown2639e072017-10-11 11:18:44 -0600301 //show_flash(&flash);
302
303 if failed {
304 self.failures += 1;
305 } else {
306 self.passes += 1;
307 }
308 }
David Browndd2b1182017-11-02 15:39:21 -0600309
310 pub fn failures(&self) -> usize {
311 self.failures
312 }
David Brown2639e072017-10-11 11:18:44 -0600313}
314
David Browndecbd042017-10-19 10:43:17 -0600315/// Build the Flash and area descriptor for a given device.
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200316pub fn make_device(device: DeviceName, align: u8, erased_val: u8) -> (SimFlashMap, AreaDesc) {
David Browndecbd042017-10-19 10:43:17 -0600317 match device {
318 DeviceName::Stm32f4 => {
319 // STM style flash. Large sectors, with a large scratch area.
320 let flash = SimFlash::new(vec![16 * 1024, 16 * 1024, 16 * 1024, 16 * 1024,
321 64 * 1024,
322 128 * 1024, 128 * 1024, 128 * 1024],
Fabio Utzigea0290b2018-08-09 14:23:01 -0300323 align as usize, erased_val);
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200324 let dev_id = 0;
Fabio Utzig1caef132018-10-26 15:40:53 -0300325 let mut areadesc = AreaDesc::new();
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200326 areadesc.add_flash_sectors(dev_id, &flash);
327 areadesc.add_image(0x020000, 0x020000, FlashId::Image0, dev_id);
328 areadesc.add_image(0x040000, 0x020000, FlashId::Image1, dev_id);
329 areadesc.add_image(0x060000, 0x020000, FlashId::ImageScratch, dev_id);
330
331 let mut flashmap = SimFlashMap::new();
332 flashmap.insert(dev_id, flash);
333 (flashmap, areadesc)
David Browndecbd042017-10-19 10:43:17 -0600334 }
335 DeviceName::K64f => {
336 // NXP style flash. Small sectors, one small sector for scratch.
Fabio Utzigea0290b2018-08-09 14:23:01 -0300337 let flash = SimFlash::new(vec![4096; 128], align as usize, erased_val);
David Browndecbd042017-10-19 10:43:17 -0600338
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200339 let dev_id = 0;
Fabio Utzig1caef132018-10-26 15:40:53 -0300340 let mut areadesc = AreaDesc::new();
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200341 areadesc.add_flash_sectors(dev_id, &flash);
342 areadesc.add_image(0x020000, 0x020000, FlashId::Image0, dev_id);
343 areadesc.add_image(0x040000, 0x020000, FlashId::Image1, dev_id);
344 areadesc.add_image(0x060000, 0x001000, FlashId::ImageScratch, dev_id);
345
346 let mut flashmap = SimFlashMap::new();
347 flashmap.insert(dev_id, flash);
348 (flashmap, areadesc)
David Browndecbd042017-10-19 10:43:17 -0600349 }
350 DeviceName::K64fBig => {
351 // Simulating an STM style flash on top of an NXP style flash. Underlying flash device
352 // uses small sectors, but we tell the bootloader they are large.
Fabio Utzigea0290b2018-08-09 14:23:01 -0300353 let flash = SimFlash::new(vec![4096; 128], align as usize, erased_val);
David Browndecbd042017-10-19 10:43:17 -0600354
Fabio Utzig1caef132018-10-26 15:40:53 -0300355 let dev_id = 0;
356 let mut areadesc = AreaDesc::new();
357 areadesc.add_flash_sectors(dev_id, &flash);
358 areadesc.add_simple_image(0x020000, 0x020000, FlashId::Image0, dev_id);
359 areadesc.add_simple_image(0x040000, 0x020000, FlashId::Image1, dev_id);
360 areadesc.add_simple_image(0x060000, 0x020000, FlashId::ImageScratch, dev_id);
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200361
362 let mut flashmap = SimFlashMap::new();
363 flashmap.insert(dev_id, flash);
364 (flashmap, areadesc)
David Browndecbd042017-10-19 10:43:17 -0600365 }
366 DeviceName::Nrf52840 => {
367 // Simulating the flash on the nrf52840 with partitions set up so that the scratch size
368 // does not divide into the image size.
Fabio Utzigea0290b2018-08-09 14:23:01 -0300369 let flash = SimFlash::new(vec![4096; 128], align as usize, erased_val);
David Browndecbd042017-10-19 10:43:17 -0600370
Fabio Utzig1caef132018-10-26 15:40:53 -0300371 let dev_id = 0;
372 let mut areadesc = AreaDesc::new();
373 areadesc.add_flash_sectors(dev_id, &flash);
374 areadesc.add_image(0x008000, 0x034000, FlashId::Image0, dev_id);
375 areadesc.add_image(0x03c000, 0x034000, FlashId::Image1, dev_id);
376 areadesc.add_image(0x070000, 0x00d000, FlashId::ImageScratch, dev_id);
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200377
378 let mut flashmap = SimFlashMap::new();
379 flashmap.insert(dev_id, flash);
380 (flashmap, areadesc)
381 }
382 DeviceName::Nrf52840SpiFlash => {
383 // Simulate nrf52840 with external SPI flash. The external SPI flash
384 // has a larger sector size so for now store scratch on that flash.
385 let flash0 = SimFlash::new(vec![4096; 128], align as usize, erased_val);
Fabio Utzig64650772018-11-19 16:51:13 -0200386 let flash1 = SimFlash::new(vec![8192; 64], align as usize, erased_val);
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200387
388 let mut areadesc = AreaDesc::new();
389 areadesc.add_flash_sectors(0, &flash0);
390 areadesc.add_flash_sectors(1, &flash1);
391
392 areadesc.add_image(0x008000, 0x068000, FlashId::Image0, 0);
393 areadesc.add_image(0x000000, 0x068000, FlashId::Image1, 1);
394 areadesc.add_image(0x068000, 0x018000, FlashId::ImageScratch, 1);
395
396 let mut flashmap = SimFlashMap::new();
397 flashmap.insert(0, flash0);
398 flashmap.insert(1, flash1);
399 (flashmap, areadesc)
David Browndecbd042017-10-19 10:43:17 -0600400 }
401 }
402}