blob: 8131a701b055df373906a071b6b3d67db6b4a79a [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
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200165 let (slot0_base, slot0_len, slot0_dev_id) = areadesc.find(FlashId::Image0);
166 let (slot1_base, slot1_len, slot1_dev_id) = areadesc.find(FlashId::Image1);
David Browndb9a3952017-11-06 13:16:15 -0700167
Fabio Utzigb841f0a2017-11-24 08:11:05 -0200168 // NOTE: not accounting "swap_size" because it is not used by sim...
David Browndb9a3952017-11-06 13:16:15 -0700169 let offset_from_end = c::boot_magic_sz() + c::boot_max_align() * 2;
170
171 // Construct a primary image.
172 let slot0 = SlotInfo {
173 base_off: slot0_base as usize,
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200174 trailer_off: slot0_base + slot0_len - offset_from_end,
Fabio Utzig1e48b912018-09-18 09:04:18 -0300175 len: slot0_len as usize,
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200176 dev_id: slot0_dev_id,
David Browndb9a3952017-11-06 13:16:15 -0700177 };
178
179 // And an upgrade image.
180 let slot1 = SlotInfo {
181 base_off: slot1_base as usize,
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200182 trailer_off: slot1_base + slot1_len - offset_from_end,
Fabio Utzig1e48b912018-09-18 09:04:18 -0300183 len: slot1_len as usize,
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200184 dev_id: slot1_dev_id,
David Browndb9a3952017-11-06 13:16:15 -0700185 };
186
187 Run {
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200188 flashmap: flashmap,
David Browndb9a3952017-11-06 13:16:15 -0700189 areadesc: areadesc,
190 slots: [slot0, slot1],
David Browndb9a3952017-11-06 13:16:15 -0700191 }
192 }
193
194 pub fn each_device<F>(f: F)
195 where F: Fn(&mut Run)
196 {
197 for &dev in ALL_DEVICES {
198 for &align in &[1, 2, 4, 8] {
Fabio Utzigea0290b2018-08-09 14:23:01 -0300199 for &erased_val in &[0, 0xff] {
200 let mut run = Run::new(dev, align, erased_val);
201 f(&mut run);
202 }
David Browndb9a3952017-11-06 13:16:15 -0700203 }
204 }
205 }
David Brownf48b9502017-11-06 14:00:26 -0700206
207 /// Construct an `Images` that doesn't expect an upgrade to happen.
208 pub fn make_no_upgrade_image(&self) -> Images {
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200209 let mut flashmap = self.flashmap.clone();
210 let primaries = install_image(&mut flashmap, &self.slots, 0, 32784, false);
211 let upgrades = install_image(&mut flashmap, &self.slots, 1, 41928, false);
David Brownf48b9502017-11-06 14:00:26 -0700212 Images {
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200213 flashmap: flashmap,
David Brownf48b9502017-11-06 14:00:26 -0700214 areadesc: self.areadesc.clone(),
Fabio Utzig1e48b912018-09-18 09:04:18 -0300215 slots: [self.slots[0].clone(), self.slots[1].clone()],
216 primaries: primaries,
217 upgrades: upgrades,
David Brownc49811e2017-11-06 14:20:45 -0700218 total_count: None,
David Brownf48b9502017-11-06 14:00:26 -0700219 }
220 }
221
222 /// Construct an `Images` for normal testing.
223 pub fn make_image(&self) -> Images {
224 let mut images = self.make_no_upgrade_image();
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200225 mark_upgrade(&mut images.flashmap, &images.slots[1]);
David Brownc49811e2017-11-06 14:20:45 -0700226
227 // upgrades without fails, counts number of flash operations
228 let total_count = match images.run_basic_upgrade() {
229 Ok(v) => v,
230 Err(_) => {
231 panic!("Unable to perform basic upgrade");
232 },
233 };
234
235 images.total_count = Some(total_count);
David Brownf48b9502017-11-06 14:00:26 -0700236 images
237 }
238
239 pub fn make_bad_slot1_image(&self) -> Images {
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200240 let mut bad_flashmap = self.flashmap.clone();
241 let primaries = install_image(&mut bad_flashmap, &self.slots, 0, 32784, false);
242 let upgrades = install_image(&mut bad_flashmap, &self.slots, 1, 41928, true);
David Brownf48b9502017-11-06 14:00:26 -0700243 Images {
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200244 flashmap: bad_flashmap,
David Brownf48b9502017-11-06 14:00:26 -0700245 areadesc: self.areadesc.clone(),
Fabio Utzig1e48b912018-09-18 09:04:18 -0300246 slots: [self.slots[0].clone(), self.slots[1].clone()],
247 primaries: primaries,
248 upgrades: upgrades,
David Brownc49811e2017-11-06 14:20:45 -0700249 total_count: None,
David Brownf48b9502017-11-06 14:00:26 -0700250 }
251 }
David Brownc49811e2017-11-06 14:20:45 -0700252
David Browndb9a3952017-11-06 13:16:15 -0700253}
254
David Browndd2b1182017-11-02 15:39:21 -0600255pub struct RunStatus {
David Brown2639e072017-10-11 11:18:44 -0600256 failures: usize,
257 passes: usize,
258}
259
260impl RunStatus {
David Browndd2b1182017-11-02 15:39:21 -0600261 pub fn new() -> RunStatus {
David Brown2639e072017-10-11 11:18:44 -0600262 RunStatus {
263 failures: 0,
264 passes: 0,
265 }
266 }
267
Fabio Utzigea0290b2018-08-09 14:23:01 -0300268 pub fn run_single(&mut self, device: DeviceName, align: u8, erased_val: u8) {
David Brown2639e072017-10-11 11:18:44 -0600269 warn!("Running on device {} with alignment {}", device, align);
270
Fabio Utzigea0290b2018-08-09 14:23:01 -0300271 let run = Run::new(device, align, erased_val);
David Brown2639e072017-10-11 11:18:44 -0600272
David Brown2639e072017-10-11 11:18:44 -0600273 let mut failed = false;
274
275 // Creates a badly signed image in slot1 to check that it is not
276 // upgraded to
David Brownf48b9502017-11-06 14:00:26 -0700277 let bad_slot1_image = run.make_bad_slot1_image();
David Brown2639e072017-10-11 11:18:44 -0600278
David Brown5f7ec2b2017-11-06 13:54:02 -0700279 failed |= bad_slot1_image.run_signfail_upgrade();
David Brown2639e072017-10-11 11:18:44 -0600280
David Brownf48b9502017-11-06 14:00:26 -0700281 let images = run.make_no_upgrade_image();
David Brown5f7ec2b2017-11-06 13:54:02 -0700282 failed |= images.run_norevert_newimage();
David Brown2639e072017-10-11 11:18:44 -0600283
David Brownf48b9502017-11-06 14:00:26 -0700284 let images = run.make_image();
David Brown2639e072017-10-11 11:18:44 -0600285
David Brown5f7ec2b2017-11-06 13:54:02 -0700286 failed |= images.run_basic_revert();
David Brownc49811e2017-11-06 14:20:45 -0700287 failed |= images.run_revert_with_fails();
288 failed |= images.run_perm_with_fails();
289 failed |= images.run_perm_with_random_fails(5);
David Brown5f7ec2b2017-11-06 13:54:02 -0700290 failed |= images.run_norevert();
David Brown2639e072017-10-11 11:18:44 -0600291
Fabio Utzigb841f0a2017-11-24 08:11:05 -0200292 failed |= images.run_with_status_fails_complete();
Fabio Utzigeedcc452017-11-24 10:48:52 -0200293 failed |= images.run_with_status_fails_with_reset();
Fabio Utzigb841f0a2017-11-24 08:11:05 -0200294
David Brown2639e072017-10-11 11:18:44 -0600295 //show_flash(&flash);
296
297 if failed {
298 self.failures += 1;
299 } else {
300 self.passes += 1;
301 }
302 }
David Browndd2b1182017-11-02 15:39:21 -0600303
304 pub fn failures(&self) -> usize {
305 self.failures
306 }
David Brown2639e072017-10-11 11:18:44 -0600307}
308
David Browndecbd042017-10-19 10:43:17 -0600309/// Build the Flash and area descriptor for a given device.
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200310pub fn make_device(device: DeviceName, align: u8, erased_val: u8) -> (SimFlashMap, AreaDesc) {
David Browndecbd042017-10-19 10:43:17 -0600311 match device {
312 DeviceName::Stm32f4 => {
313 // STM style flash. Large sectors, with a large scratch area.
314 let flash = SimFlash::new(vec![16 * 1024, 16 * 1024, 16 * 1024, 16 * 1024,
315 64 * 1024,
316 128 * 1024, 128 * 1024, 128 * 1024],
Fabio Utzigea0290b2018-08-09 14:23:01 -0300317 align as usize, erased_val);
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200318 let dev_id = 0;
Fabio Utzig1caef132018-10-26 15:40:53 -0300319 let mut areadesc = AreaDesc::new();
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200320 areadesc.add_flash_sectors(dev_id, &flash);
321 areadesc.add_image(0x020000, 0x020000, FlashId::Image0, dev_id);
322 areadesc.add_image(0x040000, 0x020000, FlashId::Image1, dev_id);
323 areadesc.add_image(0x060000, 0x020000, FlashId::ImageScratch, dev_id);
324
325 let mut flashmap = SimFlashMap::new();
326 flashmap.insert(dev_id, flash);
327 (flashmap, areadesc)
David Browndecbd042017-10-19 10:43:17 -0600328 }
329 DeviceName::K64f => {
330 // NXP style flash. Small sectors, one small sector for scratch.
Fabio Utzigea0290b2018-08-09 14:23:01 -0300331 let flash = SimFlash::new(vec![4096; 128], align as usize, erased_val);
David Browndecbd042017-10-19 10:43:17 -0600332
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200333 let dev_id = 0;
Fabio Utzig1caef132018-10-26 15:40:53 -0300334 let mut areadesc = AreaDesc::new();
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200335 areadesc.add_flash_sectors(dev_id, &flash);
336 areadesc.add_image(0x020000, 0x020000, FlashId::Image0, dev_id);
337 areadesc.add_image(0x040000, 0x020000, FlashId::Image1, dev_id);
338 areadesc.add_image(0x060000, 0x001000, FlashId::ImageScratch, dev_id);
339
340 let mut flashmap = SimFlashMap::new();
341 flashmap.insert(dev_id, flash);
342 (flashmap, areadesc)
David Browndecbd042017-10-19 10:43:17 -0600343 }
344 DeviceName::K64fBig => {
345 // Simulating an STM style flash on top of an NXP style flash. Underlying flash device
346 // uses small sectors, but we tell the bootloader they are large.
Fabio Utzigea0290b2018-08-09 14:23:01 -0300347 let flash = SimFlash::new(vec![4096; 128], align as usize, erased_val);
David Browndecbd042017-10-19 10:43:17 -0600348
Fabio Utzig1caef132018-10-26 15:40:53 -0300349 let dev_id = 0;
350 let mut areadesc = AreaDesc::new();
351 areadesc.add_flash_sectors(dev_id, &flash);
352 areadesc.add_simple_image(0x020000, 0x020000, FlashId::Image0, dev_id);
353 areadesc.add_simple_image(0x040000, 0x020000, FlashId::Image1, dev_id);
354 areadesc.add_simple_image(0x060000, 0x020000, FlashId::ImageScratch, dev_id);
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200355
356 let mut flashmap = SimFlashMap::new();
357 flashmap.insert(dev_id, flash);
358 (flashmap, areadesc)
David Browndecbd042017-10-19 10:43:17 -0600359 }
360 DeviceName::Nrf52840 => {
361 // Simulating the flash on the nrf52840 with partitions set up so that the scratch size
362 // does not divide into the image size.
Fabio Utzigea0290b2018-08-09 14:23:01 -0300363 let flash = SimFlash::new(vec![4096; 128], align as usize, erased_val);
David Browndecbd042017-10-19 10:43:17 -0600364
Fabio Utzig1caef132018-10-26 15:40:53 -0300365 let dev_id = 0;
366 let mut areadesc = AreaDesc::new();
367 areadesc.add_flash_sectors(dev_id, &flash);
368 areadesc.add_image(0x008000, 0x034000, FlashId::Image0, dev_id);
369 areadesc.add_image(0x03c000, 0x034000, FlashId::Image1, dev_id);
370 areadesc.add_image(0x070000, 0x00d000, FlashId::ImageScratch, dev_id);
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200371
372 let mut flashmap = SimFlashMap::new();
373 flashmap.insert(dev_id, flash);
374 (flashmap, areadesc)
375 }
376 DeviceName::Nrf52840SpiFlash => {
377 // Simulate nrf52840 with external SPI flash. The external SPI flash
378 // has a larger sector size so for now store scratch on that flash.
379 let flash0 = SimFlash::new(vec![4096; 128], align as usize, erased_val);
Fabio Utzig64650772018-11-19 16:51:13 -0200380 let flash1 = SimFlash::new(vec![8192; 64], align as usize, erased_val);
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200381
382 let mut areadesc = AreaDesc::new();
383 areadesc.add_flash_sectors(0, &flash0);
384 areadesc.add_flash_sectors(1, &flash1);
385
386 areadesc.add_image(0x008000, 0x068000, FlashId::Image0, 0);
387 areadesc.add_image(0x000000, 0x068000, FlashId::Image1, 1);
388 areadesc.add_image(0x068000, 0x018000, FlashId::ImageScratch, 1);
389
390 let mut flashmap = SimFlashMap::new();
391 flashmap.insert(0, flash0);
392 flashmap.insert(1, flash1);
393 (flashmap, areadesc)
David Browndecbd042017-10-19 10:43:17 -0600394 }
395 }
396}