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, Nrf52840UnequalSlotsLargerSlot1,
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 DeviceName::Nrf52840UnequalSlotsLargerSlot1,
79 ];
80
81 impl fmt::Display for DeviceName {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result82 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83 let name = match *self {
84 DeviceName::Stm32f4 => "stm32f4",
85 DeviceName::K64f => "k64f",
86 DeviceName::K64fBig => "k64fbig",
87 DeviceName::K64fMulti => "k64fmulti",
88 DeviceName::Nrf52840 => "nrf52840",
89 DeviceName::Nrf52840SpiFlash => "Nrf52840SpiFlash",
90 DeviceName::Nrf52840UnequalSlots => "Nrf52840UnequalSlots",
91 DeviceName::Nrf52840UnequalSlotsLargerSlot1 => "Nrf52840UnequalSlotsLargerSlot1",
92 };
93 f.write_str(name)
94 }
95 }
96
97 #[derive(Debug)]
98 struct AlignArg(usize);
99
100 struct AlignArgVisitor;
101
102 impl<'de> serde::de::Visitor<'de> for AlignArgVisitor {
103 type Value = AlignArg;
104
expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result105 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
106 formatter.write_str("1, 2, 4 or 8")
107 }
108
visit_u32<E>(self, n: u32) -> Result<Self::Value, E> where E: serde::de::Error109 fn visit_u32<E>(self, n: u32) -> Result<Self::Value, E>
110 where E: serde::de::Error
111 {
112 Ok(match n {
113 1 | 2 | 4 | 8 => AlignArg(n as usize),
114 n => {
115 let err = format!("Could not deserialize '{}' as alignment", n);
116 return Err(E::custom(err));
117 }
118 })
119 }
120 }
121
122 impl<'de> serde::de::Deserialize<'de> for AlignArg {
deserialize<D>(d: D) -> Result<AlignArg, D::Error> where D: serde::de::Deserializer<'de>123 fn deserialize<D>(d: D) -> Result<AlignArg, D::Error>
124 where D: serde::de::Deserializer<'de>
125 {
126 d.deserialize_u8(AlignArgVisitor)
127 }
128 }
129
main()130 pub fn main() {
131 let args: Args = Docopt::new(USAGE)
132 .and_then(|d| d.deserialize())
133 .unwrap_or_else(|e| e.exit());
134 // println!("args: {:#?}", args);
135
136 if args.cmd_sizes {
137 show_sizes();
138 return;
139 }
140
141 let mut status = RunStatus::new();
142 if args.cmd_run {
143
144 let align = args.flag_align.map(|x| x.0).unwrap_or(1);
145
146
147 let device = match args.flag_device {
148 None => panic!("Missing mandatory device argument"),
149 Some(dev) => dev,
150 };
151
152 status.run_single(device, align, 0xff);
153 }
154
155 if args.cmd_runall {
156 for &dev in ALL_DEVICES {
157 for &align in &[1, 2, 4, 8] {
158 for &erased_val in &[0, 0xff] {
159 status.run_single(dev, align, erased_val);
160 }
161 }
162 }
163 }
164
165 if status.failures > 0 {
166 error!("{} Tests ran with {} failures", status.failures + status.passes, status.failures);
167 process::exit(1);
168 } else {
169 error!("{} Tests ran successfully", status.passes);
170 process::exit(0);
171 }
172 }
173
174 #[derive(Default)]
175 pub struct RunStatus {
176 failures: usize,
177 passes: usize,
178 }
179
180 impl RunStatus {
new() -> RunStatus181 pub fn new() -> RunStatus {
182 RunStatus {
183 failures: 0,
184 passes: 0,
185 }
186 }
187
run_single(&mut self, device: DeviceName, align: usize, erased_val: u8)188 pub fn run_single(&mut self, device: DeviceName, align: usize, erased_val: u8) {
189 warn!("Running on device {} with alignment {}", device, align);
190
191 let run = match ImagesBuilder::new(device, align, erased_val) {
192 Ok(builder) => builder,
193 Err(msg) => {
194 warn!("Skipping {}: {}", device, msg);
195 return;
196 }
197 };
198
199 let mut failed = false;
200
201 // Creates a badly signed image in the secondary slot to check that
202 // it is not upgraded to
203 let bad_secondary_slot_image = run.clone().make_bad_secondary_slot_image();
204
205 failed |= bad_secondary_slot_image.run_signfail_upgrade();
206
207 let images = run.clone().make_no_upgrade_image(&NO_DEPS, ImageManipulation::None);
208 failed |= images.run_norevert_newimage();
209
210 let images = run.make_image(&NO_DEPS, true);
211
212 failed |= images.run_basic_revert();
213 failed |= images.run_revert_with_fails();
214 failed |= images.run_perm_with_fails();
215 failed |= images.run_perm_with_random_fails(5);
216 failed |= images.run_norevert();
217
218 failed |= images.run_with_status_fails_complete();
219 failed |= images.run_with_status_fails_with_reset();
220
221 //show_flash(&flash);
222
223 if failed {
224 self.failures += 1;
225 } else {
226 self.passes += 1;
227 }
228 }
229
failures(&self) -> usize230 pub fn failures(&self) -> usize {
231 self.failures
232 }
233 }
234