blob: 734b3c11514b26a034be357f904964ed42ee4774 [file] [log] [blame]
David Browne2acfae2020-01-21 16:45:01 -07001// Copyright (c) 2017-2019 Linaro LTD
2// Copyright (c) 2017-2018 JUUL Labs
3//
4// SPDX-License-Identifier: Apache-2.0
5
David Brownde7729e2017-01-09 10:41:35 -07006//! A flash simulator
7//!
8//! This module is capable of simulating the type of NOR flash commonly used in microcontrollers.
9//! These generally can be written as individual bytes, but must be erased in larger units.
10
David Brown2cbc4702017-07-06 14:18:58 -060011mod pdump;
12
David Brownafabfcf2019-01-02 11:30:27 -070013use crate::pdump::HexDump;
David Brown96eb0de2019-02-22 16:23:59 -070014use failure::Fail;
David Brownea25c412019-01-02 11:33:55 -070015use log::info;
16use rand::{
17 self,
David Brownfd8b05e2020-07-09 15:33:49 -060018 distributions::Standard,
19 Rng,
David Brownea25c412019-01-02 11:33:55 -070020};
21use std::{
22 collections::HashMap,
23 fs::File,
David Brown96eb0de2019-02-22 16:23:59 -070024 io::{self, Write},
David Brownea25c412019-01-02 11:33:55 -070025 iter::Enumerate,
26 path::Path,
27 slice,
28};
David Brownde7729e2017-01-09 10:41:35 -070029
David Brown96eb0de2019-02-22 16:23:59 -070030pub type Result<T> = std::result::Result<T, FlashError>;
31
32#[derive(Fail, Debug)]
33pub enum FlashError {
34 #[fail(display = "Offset out of bounds: {}", _0)]
35 OutOfBounds(String),
36 #[fail(display = "Invalid write: {}", _0)]
37 Write(String),
38 #[fail(display = "Write failed by chance: {}", _0)]
39 SimulatedFail(String),
40 #[fail(display = "{}", _0)]
41 Io(#[cause] io::Error),
42}
43
44impl From<io::Error> for FlashError {
45 fn from(error: io::Error) -> Self {
46 FlashError::Io(error)
David Brownde7729e2017-01-09 10:41:35 -070047 }
48}
49
David Brown96eb0de2019-02-22 16:23:59 -070050// Transition from error-chain.
51macro_rules! bail {
52 ($item:expr) => (return Err($item.into());)
53}
54
Fabio Utzig1c9aea52018-11-15 10:36:07 -020055pub struct FlashPtr {
David Brownea25c412019-01-02 11:33:55 -070056 pub ptr: *mut dyn Flash,
Fabio Utzig1c9aea52018-11-15 10:36:07 -020057}
58unsafe impl Send for FlashPtr {}
59
David Brown7ddec0b2017-07-06 10:47:35 -060060pub trait Flash {
61 fn erase(&mut self, offset: usize, len: usize) -> Result<()>;
62 fn write(&mut self, offset: usize, payload: &[u8]) -> Result<()>;
63 fn read(&self, offset: usize, data: &mut [u8]) -> Result<()>;
64
Fabio Utzigf5c895e2017-11-23 19:57:17 -020065 fn add_bad_region(&mut self, offset: usize, len: usize, rate: f32) -> Result<()>;
66 fn reset_bad_regions(&mut self);
67
Fabio Utzigfa137fc2017-11-23 20:01:02 -020068 fn set_verify_writes(&mut self, enable: bool);
69
David Brownea25c412019-01-02 11:33:55 -070070 fn sector_iter(&self) -> SectorIter<'_>;
David Brown7ddec0b2017-07-06 10:47:35 -060071 fn device_size(&self) -> usize;
Fabio Utzigea0290b2018-08-09 14:23:01 -030072
Fabio Utzig269d2862018-10-24 17:45:38 -030073 fn align(&self) -> usize;
Fabio Utzigea0290b2018-08-09 14:23:01 -030074 fn erased_val(&self) -> u8;
David Brown7ddec0b2017-07-06 10:47:35 -060075}
76
David Brown96eb0de2019-02-22 16:23:59 -070077fn ebounds<T: AsRef<str>>(message: T) -> FlashError {
78 FlashError::OutOfBounds(message.as_ref().to_owned())
David Brownde7729e2017-01-09 10:41:35 -070079}
80
Fabio Utzig65935d72017-07-17 15:34:36 -030081#[allow(dead_code)]
David Brown96eb0de2019-02-22 16:23:59 -070082fn ewrite<T: AsRef<str>>(message: T) -> FlashError {
83 FlashError::Write(message.as_ref().to_owned())
David Brownde7729e2017-01-09 10:41:35 -070084}
85
Fabio Utzigf5c895e2017-11-23 19:57:17 -020086#[allow(dead_code)]
David Brown96eb0de2019-02-22 16:23:59 -070087fn esimulatedwrite<T: AsRef<str>>(message: T) -> FlashError {
88 FlashError::SimulatedFail(message.as_ref().to_owned())
Fabio Utzigf5c895e2017-11-23 19:57:17 -020089}
90
David Brownde7729e2017-01-09 10:41:35 -070091/// An emulated flash device. It is represented as a block of bytes, and a list of the sector
Sam Bristowd0ca0ff2019-10-30 20:51:35 +130092/// mappings.
David Brownde7729e2017-01-09 10:41:35 -070093#[derive(Clone)]
David Brown7ddec0b2017-07-06 10:47:35 -060094pub struct SimFlash {
David Brownde7729e2017-01-09 10:41:35 -070095 data: Vec<u8>,
Marti Bolivar51d36dd2017-05-17 17:39:46 -040096 write_safe: Vec<bool>,
David Brownde7729e2017-01-09 10:41:35 -070097 sectors: Vec<usize>,
Fabio Utzigf5c895e2017-11-23 19:57:17 -020098 bad_region: Vec<(usize, usize, f32)>,
David Brown562a7a02017-01-23 11:19:03 -070099 // Alignment required for writes.
100 align: usize,
Fabio Utzigfa137fc2017-11-23 20:01:02 -0200101 verify_writes: bool,
Fabio Utzigea0290b2018-08-09 14:23:01 -0300102 erased_val: u8,
David Brownde7729e2017-01-09 10:41:35 -0700103}
104
David Brown7ddec0b2017-07-06 10:47:35 -0600105impl SimFlash {
David Brownde7729e2017-01-09 10:41:35 -0700106 /// Given a sector size map, construct a flash device for that.
Fabio Utzigea0290b2018-08-09 14:23:01 -0300107 pub fn new(sectors: Vec<usize>, align: usize, erased_val: u8) -> SimFlash {
David Brown562a7a02017-01-23 11:19:03 -0700108 // Verify that the alignment is a positive power of two.
109 assert!(align > 0);
110 assert!(align & (align - 1) == 0);
111
David Brownde7729e2017-01-09 10:41:35 -0700112 let total = sectors.iter().sum();
David Brown7ddec0b2017-07-06 10:47:35 -0600113 SimFlash {
Fabio Utzigea0290b2018-08-09 14:23:01 -0300114 data: vec![erased_val; total],
Marti Bolivar51d36dd2017-05-17 17:39:46 -0400115 write_safe: vec![true; total],
David Brownde7729e2017-01-09 10:41:35 -0700116 sectors: sectors,
Fabio Utzigf5c895e2017-11-23 19:57:17 -0200117 bad_region: Vec::new(),
David Brown562a7a02017-01-23 11:19:03 -0700118 align: align,
Fabio Utzigfa137fc2017-11-23 20:01:02 -0200119 verify_writes: true,
Fabio Utzigea0290b2018-08-09 14:23:01 -0300120 erased_val: erased_val,
David Brownde7729e2017-01-09 10:41:35 -0700121 }
122 }
123
David Brown7ddec0b2017-07-06 10:47:35 -0600124 #[allow(dead_code)]
125 pub fn dump(&self) {
126 self.data.dump();
127 }
128
129 /// Dump this image to the given file.
130 #[allow(dead_code)]
131 pub fn write_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
David Brown96eb0de2019-02-22 16:23:59 -0700132 let mut fd = File::create(path)?;
133 fd.write_all(&self.data)?;
David Brown7ddec0b2017-07-06 10:47:35 -0600134 Ok(())
135 }
136
137 // Scan the sector map, and return the base and offset within a sector for this given byte.
138 // Returns None if the value is outside of the device.
139 fn get_sector(&self, offset: usize) -> Option<(usize, usize)> {
140 let mut offset = offset;
141 for (sector, &size) in self.sectors.iter().enumerate() {
142 if offset < size {
143 return Some((sector, offset));
144 }
145 offset -= size;
146 }
147 return None;
148 }
149
150}
151
David Brown76101572019-02-28 11:29:03 -0700152pub type SimMultiFlash = HashMap<u8, SimFlash>;
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200153
David Brown7ddec0b2017-07-06 10:47:35 -0600154impl Flash for SimFlash {
David Brownde7729e2017-01-09 10:41:35 -0700155 /// The flash drivers tend to erase beyond the bounds of the given range. Instead, we'll be
156 /// strict, and make sure that the passed arguments are exactly at a sector boundary, otherwise
157 /// return an error.
David Brown7ddec0b2017-07-06 10:47:35 -0600158 fn erase(&mut self, offset: usize, len: usize) -> Result<()> {
David Brownde7729e2017-01-09 10:41:35 -0700159 let (_start, slen) = self.get_sector(offset).ok_or_else(|| ebounds("start"))?;
160 let (end, elen) = self.get_sector(offset + len - 1).ok_or_else(|| ebounds("end"))?;
161
162 if slen != 0 {
163 bail!(ebounds("offset not at start of sector"));
164 }
165 if elen != self.sectors[end] - 1 {
166 bail!(ebounds("end not at start of sector"));
167 }
168
169 for x in &mut self.data[offset .. offset + len] {
Fabio Utzigea0290b2018-08-09 14:23:01 -0300170 *x = self.erased_val;
David Brownde7729e2017-01-09 10:41:35 -0700171 }
172
Marti Bolivar51d36dd2017-05-17 17:39:46 -0400173 for x in &mut self.write_safe[offset .. offset + len] {
174 *x = true;
175 }
176
David Brownde7729e2017-01-09 10:41:35 -0700177 Ok(())
178 }
179
Marti Bolivar51d36dd2017-05-17 17:39:46 -0400180 /// We restrict to only allowing writes of values that are:
181 ///
182 /// 1. being written to for the first time
183 /// 2. being written to after being erased
184 ///
185 /// This emulates a flash device which starts out erased, with the
186 /// added restriction that repeated writes to the same location
187 /// are disallowed, even if they would be safe to do.
David Brown7ddec0b2017-07-06 10:47:35 -0600188 fn write(&mut self, offset: usize, payload: &[u8]) -> Result<()> {
Fabio Utzigf5c895e2017-11-23 19:57:17 -0200189 for &(off, len, rate) in &self.bad_region {
190 if offset >= off && (offset + payload.len()) <= (off + len) {
191 let mut rng = rand::thread_rng();
David Brownfd8b05e2020-07-09 15:33:49 -0600192 let samp: f32 = rng.sample(Standard);
193 if samp < rate {
Fabio Utzigf5c895e2017-11-23 19:57:17 -0200194 bail!(esimulatedwrite(
195 format!("Ignoring write to {:#x}-{:#x}", off, off + len)));
196 }
197 }
198 }
199
David Brownde7729e2017-01-09 10:41:35 -0700200 if offset + payload.len() > self.data.len() {
David Brownf253fa82017-01-23 15:43:47 -0700201 panic!("Write outside of device");
David Brownde7729e2017-01-09 10:41:35 -0700202 }
203
David Brown562a7a02017-01-23 11:19:03 -0700204 // Verify the alignment (which must be a power of two).
205 if offset & (self.align - 1) != 0 {
David Brownf253fa82017-01-23 15:43:47 -0700206 panic!("Misaligned write address");
David Brown562a7a02017-01-23 11:19:03 -0700207 }
208
209 if payload.len() & (self.align - 1) != 0 {
David Brownf253fa82017-01-23 15:43:47 -0700210 panic!("Write length not multiple of alignment");
David Brown562a7a02017-01-23 11:19:03 -0700211 }
212
Marti Bolivar51d36dd2017-05-17 17:39:46 -0400213 for (i, x) in &mut self.write_safe[offset .. offset + payload.len()].iter_mut().enumerate() {
Fabio Utzigfa137fc2017-11-23 20:01:02 -0200214 if self.verify_writes && !(*x) {
Fabio Utzig65935d72017-07-17 15:34:36 -0300215 panic!("Write to unerased location at 0x{:x}", offset + i);
Fabio Utzig19b2c1a2017-04-20 07:32:44 -0300216 }
Marti Bolivar51d36dd2017-05-17 17:39:46 -0400217 *x = false;
David Brownde7729e2017-01-09 10:41:35 -0700218 }
219
David Brown59ae5222017-12-06 11:45:15 -0700220 let sub = &mut self.data[offset .. offset + payload.len()];
David Brownde7729e2017-01-09 10:41:35 -0700221 sub.copy_from_slice(payload);
222 Ok(())
223 }
224
225 /// Read is simple.
David Brown7ddec0b2017-07-06 10:47:35 -0600226 fn read(&self, offset: usize, data: &mut [u8]) -> Result<()> {
David Brownde7729e2017-01-09 10:41:35 -0700227 if offset + data.len() > self.data.len() {
228 bail!(ebounds("Read outside of device"));
229 }
230
231 let sub = &self.data[offset .. offset + data.len()];
232 data.copy_from_slice(sub);
233 Ok(())
234 }
235
Fabio Utzigf5c895e2017-11-23 19:57:17 -0200236 /// Adds a new flash bad region. Writes to this area fail with a chance
237 /// given by `rate`.
238 fn add_bad_region(&mut self, offset: usize, len: usize, rate: f32) -> Result<()> {
239 if rate < 0.0 || rate > 1.0 {
240 bail!(ebounds("Invalid rate"));
241 }
242
243 info!("Adding new bad region {:#x}-{:#x}", offset, offset + len);
244 self.bad_region.push((offset, len, rate));
245
246 Ok(())
247 }
248
249 fn reset_bad_regions(&mut self) {
250 self.bad_region.clear();
251 }
252
Fabio Utzigfa137fc2017-11-23 20:01:02 -0200253 fn set_verify_writes(&mut self, enable: bool) {
254 self.verify_writes = enable;
255 }
256
David Brownde7729e2017-01-09 10:41:35 -0700257 /// An iterator over each sector in the device.
David Brownea25c412019-01-02 11:33:55 -0700258 fn sector_iter(&self) -> SectorIter<'_> {
David Brownde7729e2017-01-09 10:41:35 -0700259 SectorIter {
260 iter: self.sectors.iter().enumerate(),
261 base: 0,
262 }
263 }
264
David Brown7ddec0b2017-07-06 10:47:35 -0600265 fn device_size(&self) -> usize {
David Brownde7729e2017-01-09 10:41:35 -0700266 self.data.len()
267 }
Fabio Utzigea0290b2018-08-09 14:23:01 -0300268
Fabio Utzig269d2862018-10-24 17:45:38 -0300269 fn align(&self) -> usize {
270 self.align
271 }
272
Fabio Utzigea0290b2018-08-09 14:23:01 -0300273 fn erased_val(&self) -> u8 {
274 self.erased_val
275 }
David Brownde7729e2017-01-09 10:41:35 -0700276}
277
278/// It is possible to iterate over the sectors in the device, each element returning this.
David Brown3f687dc2017-11-06 13:41:18 -0700279#[derive(Debug, Clone)]
David Brownde7729e2017-01-09 10:41:35 -0700280pub struct Sector {
281 /// Which sector is this, starting from 0.
282 pub num: usize,
283 /// The offset, in bytes, of the start of this sector.
284 pub base: usize,
285 /// The length, in bytes, of this sector.
286 pub size: usize,
287}
288
289pub struct SectorIter<'a> {
290 iter: Enumerate<slice::Iter<'a, usize>>,
291 base: usize,
292}
293
294impl<'a> Iterator for SectorIter<'a> {
295 type Item = Sector;
296
297 fn next(&mut self) -> Option<Sector> {
298 match self.iter.next() {
299 None => None,
300 Some((num, &size)) => {
301 let base = self.base;
302 self.base += size;
303 Some(Sector {
304 num: num,
305 base: base,
306 size: size,
307 })
308 }
309 }
310 }
311}
312
313#[cfg(test)]
314mod test {
David Brown96eb0de2019-02-22 16:23:59 -0700315 use super::{Flash, FlashError, SimFlash, Result, Sector};
David Brownde7729e2017-01-09 10:41:35 -0700316
317 #[test]
318 fn test_flash() {
Fabio Utzigea0290b2018-08-09 14:23:01 -0300319 for &erased_val in &[0, 0xff] {
320 // NXP-style, uniform sectors.
321 let mut f1 = SimFlash::new(vec![4096usize; 256], 1, erased_val);
322 test_device(&mut f1, erased_val);
David Brownde7729e2017-01-09 10:41:35 -0700323
Fabio Utzigea0290b2018-08-09 14:23:01 -0300324 // STM style, non-uniform sectors.
325 let mut f2 = SimFlash::new(vec![16 * 1024, 16 * 1024, 16 * 1024, 64 * 1024,
326 128 * 1024, 128 * 1024, 128 * 1024], 1, erased_val);
327 test_device(&mut f2, erased_val);
328 }
David Brownde7729e2017-01-09 10:41:35 -0700329 }
330
David Brownea25c412019-01-02 11:33:55 -0700331 fn test_device(flash: &mut dyn Flash, erased_val: u8) {
David Brownde7729e2017-01-09 10:41:35 -0700332 let sectors: Vec<Sector> = flash.sector_iter().collect();
333
334 flash.erase(0, sectors[0].size).unwrap();
335 let flash_size = flash.device_size();
336 flash.erase(0, flash_size).unwrap();
337 assert!(flash.erase(0, sectors[0].size - 1).is_bounds());
338
339 // Verify that write and erase do something.
Fabio Utzigea0290b2018-08-09 14:23:01 -0300340 flash.write(0, &[0x55]).unwrap();
341 let mut buf = [0xAA; 4];
David Brownde7729e2017-01-09 10:41:35 -0700342 flash.read(0, &mut buf).unwrap();
Fabio Utzigea0290b2018-08-09 14:23:01 -0300343 assert_eq!(buf, [0x55, erased_val, erased_val, erased_val]);
David Brownde7729e2017-01-09 10:41:35 -0700344
345 flash.erase(0, sectors[0].size).unwrap();
346 flash.read(0, &mut buf).unwrap();
Fabio Utzigea0290b2018-08-09 14:23:01 -0300347 assert_eq!(buf, [erased_val; 4]);
David Brownde7729e2017-01-09 10:41:35 -0700348
349 // Program the first and last byte of each sector, verify that has been done, and then
350 // erase to verify the erase boundaries.
351 for sector in &sectors {
352 let byte = [(sector.num & 127) as u8];
353 flash.write(sector.base, &byte).unwrap();
354 flash.write(sector.base + sector.size - 1, &byte).unwrap();
355 }
356
357 // Verify the above
358 let mut buf = Vec::new();
359 for sector in &sectors {
360 let byte = (sector.num & 127) as u8;
361 buf.resize(sector.size, 0);
362 flash.read(sector.base, &mut buf).unwrap();
363 assert_eq!(buf.first(), Some(&byte));
364 assert_eq!(buf.last(), Some(&byte));
Fabio Utzigea0290b2018-08-09 14:23:01 -0300365 assert!(buf[1..buf.len()-1].iter().all(|&x| x == erased_val));
David Brownde7729e2017-01-09 10:41:35 -0700366 }
367 }
368
369 // Helper checks for the result type.
370 trait EChecker {
371 fn is_bounds(&self) -> bool;
372 }
373
374 impl<T> EChecker for Result<T> {
375
376 fn is_bounds(&self) -> bool {
377 match *self {
David Brown96eb0de2019-02-22 16:23:59 -0700378 Err(FlashError::OutOfBounds(_)) => true,
David Brownde7729e2017-01-09 10:41:35 -0700379 _ => false,
380 }
381 }
382 }
383}