blob: 68226059b9fbce8ee8d11c3c16645612d09faf5f [file] [log] [blame]
David Brownde7729e2017-01-09 10:41:35 -07001extern crate docopt;
2extern crate libc;
3extern crate rand;
4extern crate rustc_serialize;
5
6#[macro_use]
7extern crate error_chain;
8
9use docopt::Docopt;
10use rand::{Rng, SeedableRng, XorShiftRng};
11use rustc_serialize::{Decodable, Decoder};
12use std::mem;
13use std::slice;
14
15mod area;
16mod c;
17mod flash;
18pub mod api;
19mod pdump;
20
21use flash::Flash;
22use area::{AreaDesc, FlashId};
23
24const USAGE: &'static str = "
25Mcuboot simulator
26
27Usage:
28 bootsim sizes
29 bootsim run --device TYPE [--align SIZE]
30 bootsim (--help | --version)
31
32Options:
33 -h, --help Show this message
34 --version Version
35 --device TYPE MCU to simulate
36 Valid values: stm32f4, k64f
37 --align SIZE Flash write alignment
38";
39
40#[derive(Debug, RustcDecodable)]
41struct Args {
42 flag_help: bool,
43 flag_version: bool,
44 flag_device: Option<DeviceName>,
45 flag_align: Option<AlignArg>,
46 cmd_sizes: bool,
47 cmd_run: bool,
48}
49
50#[derive(Debug, RustcDecodable)]
51enum DeviceName { Stm32f4, K64f }
52
53#[derive(Debug)]
54struct AlignArg(u8);
55
56impl Decodable for AlignArg {
57 // Decode the alignment ourselves, to restrict it to the valid possible alignments.
58 fn decode<D: Decoder>(d: &mut D) -> Result<AlignArg, D::Error> {
59 let m = d.read_u8()?;
60 match m {
61 1 | 2 | 4 | 8 => Ok(AlignArg(m)),
62 _ => Err(d.error("Invalid alignment")),
63 }
64 }
65}
66
67fn main() {
68 let args: Args = Docopt::new(USAGE)
69 .and_then(|d| d.decode())
70 .unwrap_or_else(|e| e.exit());
71 // println!("args: {:#?}", args);
72
73 if args.cmd_sizes {
74 show_sizes();
75 return;
76 }
77
78 let (mut flash, areadesc) = match args.flag_device {
79 None => panic!("Missing mandatory argument"),
80 Some(DeviceName::Stm32f4) => {
81 // STM style flash. Large sectors, with a large scratch area.
82 let flash = Flash::new(vec![16 * 1024, 16 * 1024, 16 * 1024, 16 * 1024,
83 64 * 1024,
84 128 * 1024, 128 * 1024, 128 * 1024]);
85 let mut areadesc = AreaDesc::new(&flash);
86 areadesc.add_image(0x020000, 0x020000, FlashId::Image0);
87 areadesc.add_image(0x040000, 0x020000, FlashId::Image1);
88 areadesc.add_image(0x060000, 0x020000, FlashId::ImageScratch);
89 (flash, areadesc)
90 }
91 Some(DeviceName::K64f) => {
92 // NXP style flash. Small sectors, one small sector for scratch.
93 let flash = Flash::new(vec![4096; 128]);
94
95 let mut areadesc = AreaDesc::new(&flash);
96 areadesc.add_image(0x020000, 0x020000, FlashId::Image0);
97 areadesc.add_image(0x040000, 0x020000, FlashId::Image1);
98 areadesc.add_image(0x060000, 0x001000, FlashId::ImageScratch);
99 (flash, areadesc)
100 }
101 };
102
103 // println!("Areas: {:#?}", areadesc.get_c());
104
105 // Install the boot trailer signature, so that the code will start an upgrade.
106 let primary = install_image(&mut flash, 0x020000, 32779);
107
108 // Install an upgrade image.
109 let upgrade = install_image(&mut flash, 0x040000, 41922);
110
111 // Set an alignment, and position the magic value.
112 c::set_sim_flash_align(args.flag_align.map(|x| x.0).unwrap_or(1));
113 let trailer_size = c::boot_trailer_sz();
114
115 // Mark the upgrade as ready to install. (This looks like it might be a bug in the code,
116 // however.)
117 mark_upgrade(&mut flash, 0x060000 - trailer_size as usize);
118
119 let (fl2, total_count) = try_upgrade(&flash, &areadesc, None);
120 println!("First boot, count={}", total_count);
121 assert!(verify_image(&fl2, 0x020000, &upgrade));
122
123 let mut bad = 0;
124 // Let's try an image halfway through.
125 for i in 1 .. total_count {
126 println!("Try interruption at {}", i);
127 let (fl3, total_count) = try_upgrade(&flash, &areadesc, Some(i));
128 println!("Second boot, count={}", total_count);
129 if !verify_image(&fl3, 0x020000, &upgrade) {
130 println!("FAIL");
131 bad += 1;
132 }
133 if !verify_image(&fl3, 0x040000, &primary) {
134 println!("Slot 1 FAIL");
135 bad += 1;
136 }
137 }
138 println!("{} out of {} failed {:.2}%",
139 bad, total_count,
140 bad as f32 * 100.0 / total_count as f32);
141
142 println!("Try revert");
143 let fl2 = try_revert(&flash, &areadesc);
144 assert!(verify_image(&fl2, 0x020000, &primary));
145
146 println!("Try norevert");
147 let fl2 = try_norevert(&flash, &areadesc);
148 assert!(verify_image(&fl2, 0x020000, &upgrade));
149
150 /*
151 // show_flash(&flash);
152
153 println!("First boot for upgrade");
154 // c::set_flash_counter(570);
155 c::boot_go(&mut flash, &areadesc);
156 // println!("{} flash ops", c::get_flash_counter());
157
158 verify_image(&flash, 0x020000, &upgrade);
159
160 println!("\n------------------\nSecond boot");
161 c::boot_go(&mut flash, &areadesc);
162 */
163}
164
165/// Test a boot, optionally stopping after 'n' flash options. Returns a count of the number of
166/// flash operations done total.
167fn try_upgrade(flash: &Flash, areadesc: &AreaDesc, stop: Option<i32>) -> (Flash, i32) {
168 // Clone the flash to have a new copy.
169 let mut fl = flash.clone();
170
171 c::set_flash_counter(stop.unwrap_or(0));
172 let (first_interrupted, cnt1) = match c::boot_go(&mut fl, &areadesc) {
173 -0x13579 => (true, stop.unwrap()),
174 0 => (false, -c::get_flash_counter()),
175 x => panic!("Unknown return: {}", x),
176 };
177 c::set_flash_counter(0);
178
179 if first_interrupted {
180 // fl.dump();
181 match c::boot_go(&mut fl, &areadesc) {
182 -0x13579 => panic!("Shouldn't stop again"),
183 0 => (),
184 x => panic!("Unknown return: {}", x),
185 }
186 }
187
188 let cnt2 = cnt1 - c::get_flash_counter();
189
190 (fl, cnt2)
191}
192
193fn try_revert(flash: &Flash, areadesc: &AreaDesc) -> Flash {
194 let mut fl = flash.clone();
195 c::set_flash_counter(0);
196
197 assert_eq!(c::boot_go(&mut fl, &areadesc), 0);
198 assert_eq!(c::boot_go(&mut fl, &areadesc), 0);
199 fl
200}
201
202fn try_norevert(flash: &Flash, areadesc: &AreaDesc) -> Flash {
203 let mut fl = flash.clone();
204 c::set_flash_counter(0);
205 let align = c::get_sim_flash_align() as usize;
206
207 assert_eq!(c::boot_go(&mut fl, &areadesc), 0);
208 // Write boot_ok
209 fl.write(0x040000 - align, &[1]).unwrap();
210 assert_eq!(c::boot_go(&mut fl, &areadesc), 0);
211 fl
212}
213
214/// Show the flash layout.
215#[allow(dead_code)]
216fn show_flash(flash: &Flash) {
217 println!("---- Flash configuration ----");
218 for sector in flash.sector_iter() {
219 println!(" {:2}: 0x{:08x}, 0x{:08x}",
220 sector.num, sector.base, sector.size);
221 }
222 println!("");
223}
224
225/// Install a "program" into the given image. This fakes the image header, or at least all of the
226/// fields used by the given code. Returns a copy of the image that was written.
227fn install_image(flash: &mut Flash, offset: usize, len: usize) -> Vec<u8> {
228 let offset0 = offset;
229
230 // Generate a boot header. Note that the size doesn't include the header.
231 let header = ImageHeader {
232 magic: 0x96f3b83c,
233 tlv_size: 0,
234 _pad1: 0,
235 hdr_size: 32,
236 key_id: 0,
237 _pad2: 0,
238 img_size: len as u32,
239 flags: 0,
240 ver: ImageVersion {
241 major: 1,
242 minor: 0,
243 revision: 1,
244 build_num: 1,
245 },
246 _pad3: 0,
247 };
248
249 let b_header = header.as_raw();
250 /*
251 let b_header = unsafe { slice::from_raw_parts(&header as *const _ as *const u8,
252 mem::size_of::<ImageHeader>()) };
253 */
254 assert_eq!(b_header.len(), 32);
255 flash.write(offset, &b_header).unwrap();
256 let offset = offset + b_header.len();
257
258 // The core of the image itself is just pseudorandom data.
259 let mut buf = vec![0; len];
260 splat(&mut buf, offset);
261 flash.write(offset, &buf).unwrap();
262 let offset = offset + buf.len();
263
264 // Copy out the image so that we can verify that the image was installed correctly later.
265 let mut copy = vec![0u8; offset - offset0];
266 flash.read(offset0, &mut copy).unwrap();
267
268 copy
269}
270
271/// Verify that given image is present in the flash at the given offset.
272fn verify_image(flash: &Flash, offset: usize, buf: &[u8]) -> bool {
273 let mut copy = vec![0u8; buf.len()];
274 flash.read(offset, &mut copy).unwrap();
275
276 if buf != &copy[..] {
277 for i in 0 .. buf.len() {
278 if buf[i] != copy[i] {
279 println!("First failure at {:#x}", offset + i);
280 break;
281 }
282 }
283 false
284 } else {
285 true
286 }
287}
288
289/// The image header
290#[repr(C)]
291pub struct ImageHeader {
292 magic: u32,
293 tlv_size: u16,
294 key_id: u8,
295 _pad1: u8,
296 hdr_size: u16,
297 _pad2: u16,
298 img_size: u32,
299 flags: u32,
300 ver: ImageVersion,
301 _pad3: u32,
302}
303
304impl AsRaw for ImageHeader {}
305
306#[repr(C)]
307pub struct ImageVersion {
308 major: u8,
309 minor: u8,
310 revision: u16,
311 build_num: u32,
312}
313
314/// Write out the magic so that the loader tries doing an upgrade.
315fn mark_upgrade(flash: &mut Flash, offset: usize) {
316 let magic = vec![0x77, 0xc2, 0x95, 0xf3,
317 0x60, 0xd2, 0xef, 0x7f,
318 0x35, 0x52, 0x50, 0x0f,
319 0x2c, 0xb6, 0x79, 0x80];
320 flash.write(offset, &magic).unwrap();
321}
322
323// Drop some pseudo-random gibberish onto the data.
324fn splat(data: &mut [u8], seed: usize) {
325 let seed_block = [0x135782ea, 0x92184728, data.len() as u32, seed as u32];
326 let mut rng: XorShiftRng = SeedableRng::from_seed(seed_block);
327 rng.fill_bytes(data);
328}
329
330/// Return a read-only view into the raw bytes of this object
331trait AsRaw : Sized {
332 fn as_raw<'a>(&'a self) -> &'a [u8] {
333 unsafe { slice::from_raw_parts(self as *const _ as *const u8,
334 mem::size_of::<Self>()) }
335 }
336}
337
338fn show_sizes() {
339 // This isn't panic safe.
340 let old_align = c::get_sim_flash_align();
341 for min in &[1, 2, 4, 8] {
342 c::set_sim_flash_align(*min);
343 let msize = c::boot_trailer_sz();
344 println!("{:2}: {} (0x{:x})", min, msize, msize);
345 }
346 c::set_sim_flash_align(old_align);
347}