blob: 532fb0efd9ef53f4369b8ecd60af84b7e101ab1b [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 Brownea25c412019-01-02 11:33:55 -070014use log::info;
15use rand::{
16 self,
David Brownfd8b05e2020-07-09 15:33:49 -060017 distributions::Standard,
18 Rng,
David Brownea25c412019-01-02 11:33:55 -070019};
20use std::{
21 collections::HashMap,
22 fs::File,
David Brown96eb0de2019-02-22 16:23:59 -070023 io::{self, Write},
David Brownea25c412019-01-02 11:33:55 -070024 iter::Enumerate,
25 path::Path,
26 slice,
27};
David Brownc51949d2021-02-25 16:29:14 -070028use thiserror::Error;
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
David Brownc51949d2021-02-25 16:29:14 -070032#[derive(Error, Debug)]
David Brown96eb0de2019-02-22 16:23:59 -070033pub enum FlashError {
David Brownc51949d2021-02-25 16:29:14 -070034 #[error("Offset out of bounds: {0}")]
David Brown96eb0de2019-02-22 16:23:59 -070035 OutOfBounds(String),
David Brownc51949d2021-02-25 16:29:14 -070036 #[error("Invalid write: {0}")]
David Brown96eb0de2019-02-22 16:23:59 -070037 Write(String),
David Brownc51949d2021-02-25 16:29:14 -070038 #[error("Write failed by chance: {0}")]
David Brown96eb0de2019-02-22 16:23:59 -070039 SimulatedFail(String),
David Brownc51949d2021-02-25 16:29:14 -070040 #[error("{0}")]
41 Io(#[from] io::Error),
David Brownde7729e2017-01-09 10:41:35 -070042}
43
David Brown96eb0de2019-02-22 16:23:59 -070044// Transition from error-chain.
45macro_rules! bail {
46 ($item:expr) => (return Err($item.into());)
47}
48
Fabio Utzig1c9aea52018-11-15 10:36:07 -020049pub struct FlashPtr {
David Brownea25c412019-01-02 11:33:55 -070050 pub ptr: *mut dyn Flash,
Fabio Utzig1c9aea52018-11-15 10:36:07 -020051}
52unsafe impl Send for FlashPtr {}
53
David Brown7ddec0b2017-07-06 10:47:35 -060054pub trait Flash {
55 fn erase(&mut self, offset: usize, len: usize) -> Result<()>;
56 fn write(&mut self, offset: usize, payload: &[u8]) -> Result<()>;
57 fn read(&self, offset: usize, data: &mut [u8]) -> Result<()>;
58
Fabio Utzigf5c895e2017-11-23 19:57:17 -020059 fn add_bad_region(&mut self, offset: usize, len: usize, rate: f32) -> Result<()>;
60 fn reset_bad_regions(&mut self);
61
Fabio Utzigfa137fc2017-11-23 20:01:02 -020062 fn set_verify_writes(&mut self, enable: bool);
63
David Brownea25c412019-01-02 11:33:55 -070064 fn sector_iter(&self) -> SectorIter<'_>;
David Brown7ddec0b2017-07-06 10:47:35 -060065 fn device_size(&self) -> usize;
Fabio Utzigea0290b2018-08-09 14:23:01 -030066
Fabio Utzig269d2862018-10-24 17:45:38 -030067 fn align(&self) -> usize;
Fabio Utzigea0290b2018-08-09 14:23:01 -030068 fn erased_val(&self) -> u8;
David Brown7ddec0b2017-07-06 10:47:35 -060069}
70
David Brown96eb0de2019-02-22 16:23:59 -070071fn ebounds<T: AsRef<str>>(message: T) -> FlashError {
72 FlashError::OutOfBounds(message.as_ref().to_owned())
David Brownde7729e2017-01-09 10:41:35 -070073}
74
Fabio Utzig65935d72017-07-17 15:34:36 -030075#[allow(dead_code)]
David Brown96eb0de2019-02-22 16:23:59 -070076fn ewrite<T: AsRef<str>>(message: T) -> FlashError {
77 FlashError::Write(message.as_ref().to_owned())
David Brownde7729e2017-01-09 10:41:35 -070078}
79
Fabio Utzigf5c895e2017-11-23 19:57:17 -020080#[allow(dead_code)]
David Brown96eb0de2019-02-22 16:23:59 -070081fn esimulatedwrite<T: AsRef<str>>(message: T) -> FlashError {
82 FlashError::SimulatedFail(message.as_ref().to_owned())
Fabio Utzigf5c895e2017-11-23 19:57:17 -020083}
84
David Brownde7729e2017-01-09 10:41:35 -070085/// 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 +130086/// mappings.
David Brownde7729e2017-01-09 10:41:35 -070087#[derive(Clone)]
David Brown7ddec0b2017-07-06 10:47:35 -060088pub struct SimFlash {
David Brownde7729e2017-01-09 10:41:35 -070089 data: Vec<u8>,
Marti Bolivar51d36dd2017-05-17 17:39:46 -040090 write_safe: Vec<bool>,
David Brownde7729e2017-01-09 10:41:35 -070091 sectors: Vec<usize>,
Fabio Utzigf5c895e2017-11-23 19:57:17 -020092 bad_region: Vec<(usize, usize, f32)>,
David Brown562a7a02017-01-23 11:19:03 -070093 // Alignment required for writes.
94 align: usize,
Fabio Utzigfa137fc2017-11-23 20:01:02 -020095 verify_writes: bool,
Fabio Utzigea0290b2018-08-09 14:23:01 -030096 erased_val: u8,
David Brownde7729e2017-01-09 10:41:35 -070097}
98
David Brown7ddec0b2017-07-06 10:47:35 -060099impl SimFlash {
David Brownde7729e2017-01-09 10:41:35 -0700100 /// Given a sector size map, construct a flash device for that.
Fabio Utzigea0290b2018-08-09 14:23:01 -0300101 pub fn new(sectors: Vec<usize>, align: usize, erased_val: u8) -> SimFlash {
David Brown562a7a02017-01-23 11:19:03 -0700102 // Verify that the alignment is a positive power of two.
103 assert!(align > 0);
104 assert!(align & (align - 1) == 0);
105
David Brownde7729e2017-01-09 10:41:35 -0700106 let total = sectors.iter().sum();
David Brown7ddec0b2017-07-06 10:47:35 -0600107 SimFlash {
Fabio Utzigea0290b2018-08-09 14:23:01 -0300108 data: vec![erased_val; total],
Marti Bolivar51d36dd2017-05-17 17:39:46 -0400109 write_safe: vec![true; total],
David Brown4dfb33c2021-03-10 05:15:45 -0700110 sectors,
Fabio Utzigf5c895e2017-11-23 19:57:17 -0200111 bad_region: Vec::new(),
David Brown4dfb33c2021-03-10 05:15:45 -0700112 align,
Fabio Utzigfa137fc2017-11-23 20:01:02 -0200113 verify_writes: true,
David Brown4dfb33c2021-03-10 05:15:45 -0700114 erased_val,
David Brownde7729e2017-01-09 10:41:35 -0700115 }
116 }
117
David Brown7ddec0b2017-07-06 10:47:35 -0600118 #[allow(dead_code)]
119 pub fn dump(&self) {
120 self.data.dump();
121 }
122
123 /// Dump this image to the given file.
124 #[allow(dead_code)]
125 pub fn write_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
David Brown96eb0de2019-02-22 16:23:59 -0700126 let mut fd = File::create(path)?;
127 fd.write_all(&self.data)?;
David Brown7ddec0b2017-07-06 10:47:35 -0600128 Ok(())
129 }
130
131 // Scan the sector map, and return the base and offset within a sector for this given byte.
132 // Returns None if the value is outside of the device.
133 fn get_sector(&self, offset: usize) -> Option<(usize, usize)> {
134 let mut offset = offset;
135 for (sector, &size) in self.sectors.iter().enumerate() {
136 if offset < size {
137 return Some((sector, offset));
138 }
139 offset -= size;
140 }
David Brownc20bbb22021-03-10 05:19:14 -0700141 None
David Brown7ddec0b2017-07-06 10:47:35 -0600142 }
143
144}
145
David Brown76101572019-02-28 11:29:03 -0700146pub type SimMultiFlash = HashMap<u8, SimFlash>;
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200147
David Brown7ddec0b2017-07-06 10:47:35 -0600148impl Flash for SimFlash {
David Brownde7729e2017-01-09 10:41:35 -0700149 /// The flash drivers tend to erase beyond the bounds of the given range. Instead, we'll be
150 /// strict, and make sure that the passed arguments are exactly at a sector boundary, otherwise
151 /// return an error.
David Brown7ddec0b2017-07-06 10:47:35 -0600152 fn erase(&mut self, offset: usize, len: usize) -> Result<()> {
David Brownde7729e2017-01-09 10:41:35 -0700153 let (_start, slen) = self.get_sector(offset).ok_or_else(|| ebounds("start"))?;
154 let (end, elen) = self.get_sector(offset + len - 1).ok_or_else(|| ebounds("end"))?;
155
156 if slen != 0 {
157 bail!(ebounds("offset not at start of sector"));
158 }
159 if elen != self.sectors[end] - 1 {
160 bail!(ebounds("end not at start of sector"));
161 }
162
163 for x in &mut self.data[offset .. offset + len] {
Fabio Utzigea0290b2018-08-09 14:23:01 -0300164 *x = self.erased_val;
David Brownde7729e2017-01-09 10:41:35 -0700165 }
166
Marti Bolivar51d36dd2017-05-17 17:39:46 -0400167 for x in &mut self.write_safe[offset .. offset + len] {
168 *x = true;
169 }
170
David Brownde7729e2017-01-09 10:41:35 -0700171 Ok(())
172 }
173
Marti Bolivar51d36dd2017-05-17 17:39:46 -0400174 /// We restrict to only allowing writes of values that are:
175 ///
176 /// 1. being written to for the first time
177 /// 2. being written to after being erased
178 ///
179 /// This emulates a flash device which starts out erased, with the
180 /// added restriction that repeated writes to the same location
181 /// are disallowed, even if they would be safe to do.
David Brown7ddec0b2017-07-06 10:47:35 -0600182 fn write(&mut self, offset: usize, payload: &[u8]) -> Result<()> {
Fabio Utzigf5c895e2017-11-23 19:57:17 -0200183 for &(off, len, rate) in &self.bad_region {
184 if offset >= off && (offset + payload.len()) <= (off + len) {
185 let mut rng = rand::thread_rng();
David Brownfd8b05e2020-07-09 15:33:49 -0600186 let samp: f32 = rng.sample(Standard);
187 if samp < rate {
Fabio Utzigf5c895e2017-11-23 19:57:17 -0200188 bail!(esimulatedwrite(
189 format!("Ignoring write to {:#x}-{:#x}", off, off + len)));
190 }
191 }
192 }
193
David Brownde7729e2017-01-09 10:41:35 -0700194 if offset + payload.len() > self.data.len() {
David Brownf253fa82017-01-23 15:43:47 -0700195 panic!("Write outside of device");
David Brownde7729e2017-01-09 10:41:35 -0700196 }
197
David Brown562a7a02017-01-23 11:19:03 -0700198 // Verify the alignment (which must be a power of two).
199 if offset & (self.align - 1) != 0 {
David Brownf253fa82017-01-23 15:43:47 -0700200 panic!("Misaligned write address");
David Brown562a7a02017-01-23 11:19:03 -0700201 }
202
203 if payload.len() & (self.align - 1) != 0 {
David Brownf253fa82017-01-23 15:43:47 -0700204 panic!("Write length not multiple of alignment");
David Brown562a7a02017-01-23 11:19:03 -0700205 }
206
Marti Bolivar51d36dd2017-05-17 17:39:46 -0400207 for (i, x) in &mut self.write_safe[offset .. offset + payload.len()].iter_mut().enumerate() {
Fabio Utzigfa137fc2017-11-23 20:01:02 -0200208 if self.verify_writes && !(*x) {
Fabio Utzig65935d72017-07-17 15:34:36 -0300209 panic!("Write to unerased location at 0x{:x}", offset + i);
Fabio Utzig19b2c1a2017-04-20 07:32:44 -0300210 }
Marti Bolivar51d36dd2017-05-17 17:39:46 -0400211 *x = false;
David Brownde7729e2017-01-09 10:41:35 -0700212 }
213
David Brown59ae5222017-12-06 11:45:15 -0700214 let sub = &mut self.data[offset .. offset + payload.len()];
David Brownde7729e2017-01-09 10:41:35 -0700215 sub.copy_from_slice(payload);
216 Ok(())
217 }
218
219 /// Read is simple.
David Brown7ddec0b2017-07-06 10:47:35 -0600220 fn read(&self, offset: usize, data: &mut [u8]) -> Result<()> {
David Brownde7729e2017-01-09 10:41:35 -0700221 if offset + data.len() > self.data.len() {
222 bail!(ebounds("Read outside of device"));
223 }
224
225 let sub = &self.data[offset .. offset + data.len()];
226 data.copy_from_slice(sub);
227 Ok(())
228 }
229
Fabio Utzigf5c895e2017-11-23 19:57:17 -0200230 /// Adds a new flash bad region. Writes to this area fail with a chance
231 /// given by `rate`.
232 fn add_bad_region(&mut self, offset: usize, len: usize, rate: f32) -> Result<()> {
David Brown2547c002021-03-10 05:20:17 -0700233 if !(0.0..=1.0).contains(&rate) {
Fabio Utzigf5c895e2017-11-23 19:57:17 -0200234 bail!(ebounds("Invalid rate"));
235 }
236
237 info!("Adding new bad region {:#x}-{:#x}", offset, offset + len);
238 self.bad_region.push((offset, len, rate));
239
240 Ok(())
241 }
242
243 fn reset_bad_regions(&mut self) {
244 self.bad_region.clear();
245 }
246
Fabio Utzigfa137fc2017-11-23 20:01:02 -0200247 fn set_verify_writes(&mut self, enable: bool) {
248 self.verify_writes = enable;
249 }
250
David Brownde7729e2017-01-09 10:41:35 -0700251 /// An iterator over each sector in the device.
David Brownea25c412019-01-02 11:33:55 -0700252 fn sector_iter(&self) -> SectorIter<'_> {
David Brownde7729e2017-01-09 10:41:35 -0700253 SectorIter {
254 iter: self.sectors.iter().enumerate(),
255 base: 0,
256 }
257 }
258
David Brown7ddec0b2017-07-06 10:47:35 -0600259 fn device_size(&self) -> usize {
David Brownde7729e2017-01-09 10:41:35 -0700260 self.data.len()
261 }
Fabio Utzigea0290b2018-08-09 14:23:01 -0300262
Fabio Utzig269d2862018-10-24 17:45:38 -0300263 fn align(&self) -> usize {
264 self.align
265 }
266
Fabio Utzigea0290b2018-08-09 14:23:01 -0300267 fn erased_val(&self) -> u8 {
268 self.erased_val
269 }
David Brownde7729e2017-01-09 10:41:35 -0700270}
271
272/// It is possible to iterate over the sectors in the device, each element returning this.
David Brown3f687dc2017-11-06 13:41:18 -0700273#[derive(Debug, Clone)]
David Brownde7729e2017-01-09 10:41:35 -0700274pub struct Sector {
275 /// Which sector is this, starting from 0.
276 pub num: usize,
277 /// The offset, in bytes, of the start of this sector.
278 pub base: usize,
279 /// The length, in bytes, of this sector.
280 pub size: usize,
281}
282
283pub struct SectorIter<'a> {
284 iter: Enumerate<slice::Iter<'a, usize>>,
285 base: usize,
286}
287
288impl<'a> Iterator for SectorIter<'a> {
289 type Item = Sector;
290
291 fn next(&mut self) -> Option<Sector> {
292 match self.iter.next() {
293 None => None,
294 Some((num, &size)) => {
295 let base = self.base;
296 self.base += size;
297 Some(Sector {
David Brown4dfb33c2021-03-10 05:15:45 -0700298 num,
299 base,
300 size,
David Brownde7729e2017-01-09 10:41:35 -0700301 })
302 }
303 }
304 }
305}
306
307#[cfg(test)]
308mod test {
David Brown96eb0de2019-02-22 16:23:59 -0700309 use super::{Flash, FlashError, SimFlash, Result, Sector};
David Brownde7729e2017-01-09 10:41:35 -0700310
311 #[test]
312 fn test_flash() {
Fabio Utzigea0290b2018-08-09 14:23:01 -0300313 for &erased_val in &[0, 0xff] {
314 // NXP-style, uniform sectors.
315 let mut f1 = SimFlash::new(vec![4096usize; 256], 1, erased_val);
316 test_device(&mut f1, erased_val);
David Brownde7729e2017-01-09 10:41:35 -0700317
Fabio Utzigea0290b2018-08-09 14:23:01 -0300318 // STM style, non-uniform sectors.
319 let mut f2 = SimFlash::new(vec![16 * 1024, 16 * 1024, 16 * 1024, 64 * 1024,
320 128 * 1024, 128 * 1024, 128 * 1024], 1, erased_val);
321 test_device(&mut f2, erased_val);
322 }
David Brownde7729e2017-01-09 10:41:35 -0700323 }
324
David Brownea25c412019-01-02 11:33:55 -0700325 fn test_device(flash: &mut dyn Flash, erased_val: u8) {
David Brownde7729e2017-01-09 10:41:35 -0700326 let sectors: Vec<Sector> = flash.sector_iter().collect();
327
328 flash.erase(0, sectors[0].size).unwrap();
329 let flash_size = flash.device_size();
330 flash.erase(0, flash_size).unwrap();
331 assert!(flash.erase(0, sectors[0].size - 1).is_bounds());
332
333 // Verify that write and erase do something.
Fabio Utzigea0290b2018-08-09 14:23:01 -0300334 flash.write(0, &[0x55]).unwrap();
335 let mut buf = [0xAA; 4];
David Brownde7729e2017-01-09 10:41:35 -0700336 flash.read(0, &mut buf).unwrap();
Fabio Utzigea0290b2018-08-09 14:23:01 -0300337 assert_eq!(buf, [0x55, erased_val, erased_val, erased_val]);
David Brownde7729e2017-01-09 10:41:35 -0700338
339 flash.erase(0, sectors[0].size).unwrap();
340 flash.read(0, &mut buf).unwrap();
Fabio Utzigea0290b2018-08-09 14:23:01 -0300341 assert_eq!(buf, [erased_val; 4]);
David Brownde7729e2017-01-09 10:41:35 -0700342
343 // Program the first and last byte of each sector, verify that has been done, and then
344 // erase to verify the erase boundaries.
345 for sector in &sectors {
346 let byte = [(sector.num & 127) as u8];
347 flash.write(sector.base, &byte).unwrap();
348 flash.write(sector.base + sector.size - 1, &byte).unwrap();
349 }
350
351 // Verify the above
352 let mut buf = Vec::new();
353 for sector in &sectors {
354 let byte = (sector.num & 127) as u8;
355 buf.resize(sector.size, 0);
356 flash.read(sector.base, &mut buf).unwrap();
357 assert_eq!(buf.first(), Some(&byte));
358 assert_eq!(buf.last(), Some(&byte));
Fabio Utzigea0290b2018-08-09 14:23:01 -0300359 assert!(buf[1..buf.len()-1].iter().all(|&x| x == erased_val));
David Brownde7729e2017-01-09 10:41:35 -0700360 }
361 }
362
363 // Helper checks for the result type.
364 trait EChecker {
365 fn is_bounds(&self) -> bool;
366 }
367
368 impl<T> EChecker for Result<T> {
369
370 fn is_bounds(&self) -> bool {
371 match *self {
David Brown96eb0de2019-02-22 16:23:59 -0700372 Err(FlashError::OutOfBounds(_)) => true,
David Brownde7729e2017-01-09 10:41:35 -0700373 _ => false,
374 }
375 }
376 }
377}