blob: c52f53ebff9cf47f7d249669b8f8208c1249741f [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;
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +020069
70 fn set_erase_by_sector(&mut self, enable: bool);
David Brown7ddec0b2017-07-06 10:47:35 -060071}
72
David Brown96eb0de2019-02-22 16:23:59 -070073fn ebounds<T: AsRef<str>>(message: T) -> FlashError {
74 FlashError::OutOfBounds(message.as_ref().to_owned())
David Brownde7729e2017-01-09 10:41:35 -070075}
76
Fabio Utzig65935d72017-07-17 15:34:36 -030077#[allow(dead_code)]
David Brown96eb0de2019-02-22 16:23:59 -070078fn ewrite<T: AsRef<str>>(message: T) -> FlashError {
79 FlashError::Write(message.as_ref().to_owned())
David Brownde7729e2017-01-09 10:41:35 -070080}
81
Fabio Utzigf5c895e2017-11-23 19:57:17 -020082#[allow(dead_code)]
David Brown96eb0de2019-02-22 16:23:59 -070083fn esimulatedwrite<T: AsRef<str>>(message: T) -> FlashError {
84 FlashError::SimulatedFail(message.as_ref().to_owned())
Fabio Utzigf5c895e2017-11-23 19:57:17 -020085}
86
David Brownde7729e2017-01-09 10:41:35 -070087/// 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 +130088/// mappings.
David Brownde7729e2017-01-09 10:41:35 -070089#[derive(Clone)]
David Brown7ddec0b2017-07-06 10:47:35 -060090pub struct SimFlash {
David Brownde7729e2017-01-09 10:41:35 -070091 data: Vec<u8>,
Marti Bolivar51d36dd2017-05-17 17:39:46 -040092 write_safe: Vec<bool>,
David Brownde7729e2017-01-09 10:41:35 -070093 sectors: Vec<usize>,
Fabio Utzigf5c895e2017-11-23 19:57:17 -020094 bad_region: Vec<(usize, usize, f32)>,
David Brown562a7a02017-01-23 11:19:03 -070095 // Alignment required for writes.
96 align: usize,
Fabio Utzigfa137fc2017-11-23 20:01:02 -020097 verify_writes: bool,
Fabio Utzigea0290b2018-08-09 14:23:01 -030098 erased_val: u8,
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +020099 erase_by_sector: bool,
David Brownde7729e2017-01-09 10:41:35 -0700100}
101
David Brown7ddec0b2017-07-06 10:47:35 -0600102impl SimFlash {
David Brownde7729e2017-01-09 10:41:35 -0700103 /// Given a sector size map, construct a flash device for that.
Fabio Utzigea0290b2018-08-09 14:23:01 -0300104 pub fn new(sectors: Vec<usize>, align: usize, erased_val: u8) -> SimFlash {
David Brown562a7a02017-01-23 11:19:03 -0700105 // Verify that the alignment is a positive power of two.
106 assert!(align > 0);
107 assert!(align & (align - 1) == 0);
108
David Brownde7729e2017-01-09 10:41:35 -0700109 let total = sectors.iter().sum();
David Brown7ddec0b2017-07-06 10:47:35 -0600110 SimFlash {
Fabio Utzigea0290b2018-08-09 14:23:01 -0300111 data: vec![erased_val; total],
Marti Bolivar51d36dd2017-05-17 17:39:46 -0400112 write_safe: vec![true; total],
David Brown4dfb33c2021-03-10 05:15:45 -0700113 sectors,
Fabio Utzigf5c895e2017-11-23 19:57:17 -0200114 bad_region: Vec::new(),
David Brown4dfb33c2021-03-10 05:15:45 -0700115 align,
Fabio Utzigfa137fc2017-11-23 20:01:02 -0200116 verify_writes: true,
David Brown4dfb33c2021-03-10 05:15:45 -0700117 erased_val,
David Brownde7729e2017-01-09 10:41:35 -0700118 }
119 }
120
David Brown7ddec0b2017-07-06 10:47:35 -0600121 #[allow(dead_code)]
122 pub fn dump(&self) {
123 self.data.dump();
124 }
125
126 /// Dump this image to the given file.
127 #[allow(dead_code)]
128 pub fn write_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
David Brown96eb0de2019-02-22 16:23:59 -0700129 let mut fd = File::create(path)?;
130 fd.write_all(&self.data)?;
David Brown7ddec0b2017-07-06 10:47:35 -0600131 Ok(())
132 }
133
134 // Scan the sector map, and return the base and offset within a sector for this given byte.
135 // Returns None if the value is outside of the device.
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200136 fn get_sector(&self, offset: usize) -> Option<(usize, usize, usize)> {
David Brown7ddec0b2017-07-06 10:47:35 -0600137 let mut offset = offset;
138 for (sector, &size) in self.sectors.iter().enumerate() {
139 if offset < size {
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200140 return Some((sector, offset, size));
David Brown7ddec0b2017-07-06 10:47:35 -0600141 }
142 offset -= size;
143 }
David Brownc20bbb22021-03-10 05:19:14 -0700144 None
David Brown7ddec0b2017-07-06 10:47:35 -0600145 }
146
147}
148
David Brown76101572019-02-28 11:29:03 -0700149pub type SimMultiFlash = HashMap<u8, SimFlash>;
Fabio Utzigafb2bc92018-11-19 16:11:52 -0200150
David Brown7ddec0b2017-07-06 10:47:35 -0600151impl Flash for SimFlash {
David Brownde7729e2017-01-09 10:41:35 -0700152 /// The flash drivers tend to erase beyond the bounds of the given range. Instead, we'll be
153 /// strict, and make sure that the passed arguments are exactly at a sector boundary, otherwise
154 /// return an error.
David Brown7ddec0b2017-07-06 10:47:35 -0600155 fn erase(&mut self, offset: usize, len: usize) -> Result<()> {
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200156 let (_start, mut slen, ssize) = self.get_sector(offset).ok_or_else(|| ebounds("start"))?;
157 let (end, mut elen, _) = self.get_sector(offset + len - 1).ok_or_else(|| ebounds("end"))?;
158
159 let mut offset = offset;
160 let mut len = len;
161
162 if self.erase_by_sector {
163 // info!("erase_by_sector: {:#X}/{:#X} -> {:#X}/{:#X}", offset, len, offset - slen, ssize);
164
165 offset = offset - slen;
166 len = ssize;
167
168 slen = 0;
169 elen = self.sectors[end] - 1;
170 }
David Brownde7729e2017-01-09 10:41:35 -0700171
172 if slen != 0 {
173 bail!(ebounds("offset not at start of sector"));
174 }
175 if elen != self.sectors[end] - 1 {
176 bail!(ebounds("end not at start of sector"));
177 }
178
179 for x in &mut self.data[offset .. offset + len] {
Fabio Utzigea0290b2018-08-09 14:23:01 -0300180 *x = self.erased_val;
David Brownde7729e2017-01-09 10:41:35 -0700181 }
182
Marti Bolivar51d36dd2017-05-17 17:39:46 -0400183 for x in &mut self.write_safe[offset .. offset + len] {
184 *x = true;
185 }
186
David Brownde7729e2017-01-09 10:41:35 -0700187 Ok(())
188 }
189
Marti Bolivar51d36dd2017-05-17 17:39:46 -0400190 /// We restrict to only allowing writes of values that are:
191 ///
192 /// 1. being written to for the first time
193 /// 2. being written to after being erased
194 ///
195 /// This emulates a flash device which starts out erased, with the
196 /// added restriction that repeated writes to the same location
197 /// are disallowed, even if they would be safe to do.
David Brown7ddec0b2017-07-06 10:47:35 -0600198 fn write(&mut self, offset: usize, payload: &[u8]) -> Result<()> {
Fabio Utzigf5c895e2017-11-23 19:57:17 -0200199 for &(off, len, rate) in &self.bad_region {
200 if offset >= off && (offset + payload.len()) <= (off + len) {
201 let mut rng = rand::thread_rng();
David Brownfd8b05e2020-07-09 15:33:49 -0600202 let samp: f32 = rng.sample(Standard);
203 if samp < rate {
Fabio Utzigf5c895e2017-11-23 19:57:17 -0200204 bail!(esimulatedwrite(
205 format!("Ignoring write to {:#x}-{:#x}", off, off + len)));
206 }
207 }
208 }
209
David Brownde7729e2017-01-09 10:41:35 -0700210 if offset + payload.len() > self.data.len() {
David Brownf253fa82017-01-23 15:43:47 -0700211 panic!("Write outside of device");
David Brownde7729e2017-01-09 10:41:35 -0700212 }
213
David Brown562a7a02017-01-23 11:19:03 -0700214 // Verify the alignment (which must be a power of two).
215 if offset & (self.align - 1) != 0 {
David Brownf253fa82017-01-23 15:43:47 -0700216 panic!("Misaligned write address");
David Brown562a7a02017-01-23 11:19:03 -0700217 }
218
219 if payload.len() & (self.align - 1) != 0 {
David Brownf253fa82017-01-23 15:43:47 -0700220 panic!("Write length not multiple of alignment");
David Brown562a7a02017-01-23 11:19:03 -0700221 }
222
Marti Bolivar51d36dd2017-05-17 17:39:46 -0400223 for (i, x) in &mut self.write_safe[offset .. offset + payload.len()].iter_mut().enumerate() {
Fabio Utzigfa137fc2017-11-23 20:01:02 -0200224 if self.verify_writes && !(*x) {
Fabio Utzig65935d72017-07-17 15:34:36 -0300225 panic!("Write to unerased location at 0x{:x}", offset + i);
Fabio Utzig19b2c1a2017-04-20 07:32:44 -0300226 }
Marti Bolivar51d36dd2017-05-17 17:39:46 -0400227 *x = false;
David Brownde7729e2017-01-09 10:41:35 -0700228 }
229
David Brown59ae5222017-12-06 11:45:15 -0700230 let sub = &mut self.data[offset .. offset + payload.len()];
David Brownde7729e2017-01-09 10:41:35 -0700231 sub.copy_from_slice(payload);
232 Ok(())
233 }
234
235 /// Read is simple.
David Brown7ddec0b2017-07-06 10:47:35 -0600236 fn read(&self, offset: usize, data: &mut [u8]) -> Result<()> {
David Brownde7729e2017-01-09 10:41:35 -0700237 if offset + data.len() > self.data.len() {
238 bail!(ebounds("Read outside of device"));
239 }
240
241 let sub = &self.data[offset .. offset + data.len()];
242 data.copy_from_slice(sub);
243 Ok(())
244 }
245
Fabio Utzigf5c895e2017-11-23 19:57:17 -0200246 /// Adds a new flash bad region. Writes to this area fail with a chance
247 /// given by `rate`.
248 fn add_bad_region(&mut self, offset: usize, len: usize, rate: f32) -> Result<()> {
David Brown2547c002021-03-10 05:20:17 -0700249 if !(0.0..=1.0).contains(&rate) {
Fabio Utzigf5c895e2017-11-23 19:57:17 -0200250 bail!(ebounds("Invalid rate"));
251 }
252
253 info!("Adding new bad region {:#x}-{:#x}", offset, offset + len);
254 self.bad_region.push((offset, len, rate));
255
256 Ok(())
257 }
258
259 fn reset_bad_regions(&mut self) {
260 self.bad_region.clear();
261 }
262
Fabio Utzigfa137fc2017-11-23 20:01:02 -0200263 fn set_verify_writes(&mut self, enable: bool) {
264 self.verify_writes = enable;
265 }
266
David Brownde7729e2017-01-09 10:41:35 -0700267 /// An iterator over each sector in the device.
David Brownea25c412019-01-02 11:33:55 -0700268 fn sector_iter(&self) -> SectorIter<'_> {
David Brownde7729e2017-01-09 10:41:35 -0700269 SectorIter {
270 iter: self.sectors.iter().enumerate(),
271 base: 0,
272 }
273 }
274
David Brown7ddec0b2017-07-06 10:47:35 -0600275 fn device_size(&self) -> usize {
David Brownde7729e2017-01-09 10:41:35 -0700276 self.data.len()
277 }
Fabio Utzigea0290b2018-08-09 14:23:01 -0300278
Fabio Utzig269d2862018-10-24 17:45:38 -0300279 fn align(&self) -> usize {
280 self.align
281 }
282
Fabio Utzigea0290b2018-08-09 14:23:01 -0300283 fn erased_val(&self) -> u8 {
284 self.erased_val
285 }
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200286
287 fn set_erase_by_sector(&mut self, enable: bool) {
288 self.erase_by_sector = enable;
289 }
David Brownde7729e2017-01-09 10:41:35 -0700290}
291
292/// It is possible to iterate over the sectors in the device, each element returning this.
David Brown3f687dc2017-11-06 13:41:18 -0700293#[derive(Debug, Clone)]
David Brownde7729e2017-01-09 10:41:35 -0700294pub struct Sector {
295 /// Which sector is this, starting from 0.
296 pub num: usize,
297 /// The offset, in bytes, of the start of this sector.
298 pub base: usize,
299 /// The length, in bytes, of this sector.
300 pub size: usize,
301}
302
303pub struct SectorIter<'a> {
304 iter: Enumerate<slice::Iter<'a, usize>>,
305 base: usize,
306}
307
308impl<'a> Iterator for SectorIter<'a> {
309 type Item = Sector;
310
311 fn next(&mut self) -> Option<Sector> {
312 match self.iter.next() {
313 None => None,
314 Some((num, &size)) => {
315 let base = self.base;
316 self.base += size;
317 Some(Sector {
David Brown4dfb33c2021-03-10 05:15:45 -0700318 num,
319 base,
320 size,
David Brownde7729e2017-01-09 10:41:35 -0700321 })
322 }
323 }
324 }
325}
326
327#[cfg(test)]
328mod test {
David Brown96eb0de2019-02-22 16:23:59 -0700329 use super::{Flash, FlashError, SimFlash, Result, Sector};
David Brownde7729e2017-01-09 10:41:35 -0700330
331 #[test]
332 fn test_flash() {
Fabio Utzigea0290b2018-08-09 14:23:01 -0300333 for &erased_val in &[0, 0xff] {
334 // NXP-style, uniform sectors.
335 let mut f1 = SimFlash::new(vec![4096usize; 256], 1, erased_val);
336 test_device(&mut f1, erased_val);
David Brownde7729e2017-01-09 10:41:35 -0700337
Fabio Utzigea0290b2018-08-09 14:23:01 -0300338 // STM style, non-uniform sectors.
339 let mut f2 = SimFlash::new(vec![16 * 1024, 16 * 1024, 16 * 1024, 64 * 1024,
340 128 * 1024, 128 * 1024, 128 * 1024], 1, erased_val);
341 test_device(&mut f2, erased_val);
342 }
David Brownde7729e2017-01-09 10:41:35 -0700343 }
344
David Brownea25c412019-01-02 11:33:55 -0700345 fn test_device(flash: &mut dyn Flash, erased_val: u8) {
David Brownde7729e2017-01-09 10:41:35 -0700346 let sectors: Vec<Sector> = flash.sector_iter().collect();
347
348 flash.erase(0, sectors[0].size).unwrap();
349 let flash_size = flash.device_size();
350 flash.erase(0, flash_size).unwrap();
351 assert!(flash.erase(0, sectors[0].size - 1).is_bounds());
352
353 // Verify that write and erase do something.
Fabio Utzigea0290b2018-08-09 14:23:01 -0300354 flash.write(0, &[0x55]).unwrap();
355 let mut buf = [0xAA; 4];
David Brownde7729e2017-01-09 10:41:35 -0700356 flash.read(0, &mut buf).unwrap();
Fabio Utzigea0290b2018-08-09 14:23:01 -0300357 assert_eq!(buf, [0x55, erased_val, erased_val, erased_val]);
David Brownde7729e2017-01-09 10:41:35 -0700358
359 flash.erase(0, sectors[0].size).unwrap();
360 flash.read(0, &mut buf).unwrap();
Fabio Utzigea0290b2018-08-09 14:23:01 -0300361 assert_eq!(buf, [erased_val; 4]);
David Brownde7729e2017-01-09 10:41:35 -0700362
363 // Program the first and last byte of each sector, verify that has been done, and then
364 // erase to verify the erase boundaries.
365 for sector in &sectors {
366 let byte = [(sector.num & 127) as u8];
367 flash.write(sector.base, &byte).unwrap();
368 flash.write(sector.base + sector.size - 1, &byte).unwrap();
369 }
370
371 // Verify the above
372 let mut buf = Vec::new();
373 for sector in &sectors {
374 let byte = (sector.num & 127) as u8;
375 buf.resize(sector.size, 0);
376 flash.read(sector.base, &mut buf).unwrap();
377 assert_eq!(buf.first(), Some(&byte));
378 assert_eq!(buf.last(), Some(&byte));
Fabio Utzigea0290b2018-08-09 14:23:01 -0300379 assert!(buf[1..buf.len()-1].iter().all(|&x| x == erased_val));
David Brownde7729e2017-01-09 10:41:35 -0700380 }
381 }
382
383 // Helper checks for the result type.
384 trait EChecker {
385 fn is_bounds(&self) -> bool;
386 }
387
388 impl<T> EChecker for Result<T> {
389
390 fn is_bounds(&self) -> bool {
391 match *self {
David Brown96eb0de2019-02-22 16:23:59 -0700392 Err(FlashError::OutOfBounds(_)) => true,
David Brownde7729e2017-01-09 10:41:35 -0700393 _ => false,
394 }
395 }
396 }
397}