1 // Copyright (c) 2017-2019 Linaro LTD
2 // Copyright (c) 2017-2019 JUUL Labs
3 // Copyright (c) 2019-2023 Arm Limited
4 //
5 // SPDX-License-Identifier: Apache-2.0
6
7 use docopt::Docopt;
8 use log::{warn, error};
9 use std::{
10 fmt,
11 process,
12 };
13 use serde_derive::Deserialize;
14
15 mod caps;
16 mod depends;
17 mod image;
18 mod tlv;
19 mod utils;
20 pub mod testlog;
21
22 pub use crate::{
23 depends::{
24 DepTest,
25 DepType,
26 UpgradeInfo,
27 NO_DEPS,
28 REV_DEPS,
29 },
30 image::{
31 ImagesBuilder,
32 Images,
33 ImageManipulation,
34 show_sizes,
35 },
36 };
37
38 const USAGE: &str = "
39 Mcuboot simulator
40
41 Usage:
42 bootsim sizes
43 bootsim run --device TYPE [--align SIZE]
44 bootsim runall
45 bootsim (--help | --version)
46
47 Options:
48 -h, --help Show this message
49 --version Version
50 --device TYPE MCU to simulate
51 Valid values: stm32f4, k64f
52 --align SIZE Flash write alignment
53 ";
54
55 #[derive(Debug, Deserialize)]
56 struct Args {
57 flag_device: Option<DeviceName>,
58 flag_align: Option<AlignArg>,
59 cmd_sizes: bool,
60 cmd_run: bool,
61 cmd_runall: bool,
62 }
63
64 #[derive(Copy, Clone, Debug, Deserialize)]
65 pub enum DeviceName {
66 Stm32f4, K64f, K64fBig, K64fMulti, Nrf52840, Nrf52840SpiFlash,
67 Nrf52840UnequalSlots,
68 }
69
70 pub static ALL_DEVICES: &[DeviceName] = &[
71 DeviceName::Stm32f4,
72 DeviceName::K64f,
73 DeviceName::K64fBig,
74 DeviceName::K64fMulti,
75 DeviceName::Nrf52840,
76 DeviceName::Nrf52840SpiFlash,
77 DeviceName::Nrf52840UnequalSlots,
78 ];
79
80 impl fmt::Display for DeviceName {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result81 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82 let name = match *self {
83 DeviceName::Stm32f4 => "stm32f4",
84 DeviceName::K64f => "k64f",
85 DeviceName::K64fBig => "k64fbig",
86 DeviceName::K64fMulti => "k64fmulti",
87 DeviceName::Nrf52840 => "nrf52840",
88 DeviceName::Nrf52840SpiFlash => "Nrf52840SpiFlash",
89 DeviceName::Nrf52840UnequalSlots => "Nrf52840UnequalSlots",
90 };
91 f.write_str(name)
92 }
93 }
94
95 #[derive(Debug)]
96 struct AlignArg(usize);
97
98 struct AlignArgVisitor;
99
100 impl<'de> serde::de::Visitor<'de> for AlignArgVisitor {
101 type Value = AlignArg;
102
expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result103 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
104 formatter.write_str("1, 2, 4 or 8")
105 }
106
visit_u32<E>(self, n: u32) -> Result<Self::Value, E> where E: serde::de::Error107 fn visit_u32<E>(self, n: u32) -> Result<Self::Value, E>
108 where E: serde::de::Error
109 {
110 Ok(match n {
111 1 | 2 | 4 | 8 => AlignArg(n as usize),
112 n => {
113 let err = format!("Could not deserialize '{}' as alignment", n);
114 return Err(E::custom(err));
115 }
116 })
117 }
118 }
119
120 impl<'de> serde::de::Deserialize<'de> for AlignArg {
deserialize<D>(d: D) -> Result<AlignArg, D::Error> where D: serde::de::Deserializer<'de>121 fn deserialize<D>(d: D) -> Result<AlignArg, D::Error>
122 where D: serde::de::Deserializer<'de>
123 {
124 d.deserialize_u8(AlignArgVisitor)
125 }
126 }
127
main()128 pub fn main() {
129 let args: Args = Docopt::new(USAGE)
130 .and_then(|d| d.deserialize())
131 .unwrap_or_else(|e| e.exit());
132 // println!("args: {:#?}", args);
133
134 if args.cmd_sizes {
135 show_sizes();
136 return;
137 }
138
139 let mut status = RunStatus::new();
140 if args.cmd_run {
141
142 let align = args.flag_align.map(|x| x.0).unwrap_or(1);
143
144
145 let device = match args.flag_device {
146 None => panic!("Missing mandatory device argument"),
147 Some(dev) => dev,
148 };
149
150 status.run_single(device, align, 0xff);
151 }
152
153 if args.cmd_runall {
154 for &dev in ALL_DEVICES {
155 for &align in &[1, 2, 4, 8] {
156 for &erased_val in &[0, 0xff] {
157 status.run_single(dev, align, erased_val);
158 }
159 }
160 }
161 }
162
163 if status.failures > 0 {
164 error!("{} Tests ran with {} failures", status.failures + status.passes, status.failures);
165 process::exit(1);
166 } else {
167 error!("{} Tests ran successfully", status.passes);
168 process::exit(0);
169 }
170 }
171
172 #[derive(Default)]
173 pub struct RunStatus {
174 failures: usize,
175 passes: usize,
176 }
177
178 impl RunStatus {
new() -> RunStatus179 pub fn new() -> RunStatus {
180 RunStatus {
181 failures: 0,
182 passes: 0,
183 }
184 }
185
run_single(&mut self, device: DeviceName, align: usize, erased_val: u8)186 pub fn run_single(&mut self, device: DeviceName, align: usize, erased_val: u8) {
187 warn!("Running on device {} with alignment {}", device, align);
188
189 let run = match ImagesBuilder::new(device, align, erased_val) {
190 Ok(builder) => builder,
191 Err(msg) => {
192 warn!("Skipping {}: {}", device, msg);
193 return;
194 }
195 };
196
197 let mut failed = false;
198
199 // Creates a badly signed image in the secondary slot to check that
200 // it is not upgraded to
201 let bad_secondary_slot_image = run.clone().make_bad_secondary_slot_image();
202
203 failed |= bad_secondary_slot_image.run_signfail_upgrade();
204
205 let images = run.clone().make_no_upgrade_image(&NO_DEPS, ImageManipulation::None);
206 failed |= images.run_norevert_newimage();
207
208 let images = run.make_image(&NO_DEPS, true);
209
210 failed |= images.run_basic_revert();
211 failed |= images.run_revert_with_fails();
212 failed |= images.run_perm_with_fails();
213 failed |= images.run_perm_with_random_fails(5);
214 failed |= images.run_norevert();
215
216 failed |= images.run_with_status_fails_complete();
217 failed |= images.run_with_status_fails_with_reset();
218
219 //show_flash(&flash);
220
221 if failed {
222 self.failures += 1;
223 } else {
224 self.passes += 1;
225 }
226 }
227
failures(&self) -> usize228 pub fn failures(&self) -> usize {
229 self.failures
230 }
231 }
232