blob: 797a044ded87b385199a86bc0e8cb79b72213cdf [file] [log] [blame]
David Brownde7729e2017-01-09 10:41:35 -07001//! A flash simulator
2//!
3//! This module is capable of simulating the type of NOR flash commonly used in microcontrollers.
4//! These generally can be written as individual bytes, but must be erased in larger units.
5
David Brown163ab232017-01-23 15:48:35 -07006use std::fs::File;
7use std::io::Write;
David Brownde7729e2017-01-09 10:41:35 -07008use std::iter::Enumerate;
David Brown163ab232017-01-23 15:48:35 -07009use std::path::Path;
David Brownde7729e2017-01-09 10:41:35 -070010use std::slice;
11use pdump::HexDump;
12
13error_chain! {
14 errors {
15 OutOfBounds(t: String) {
16 description("Offset is out of bounds")
17 display("Offset out of bounds: {}", t)
18 }
19 Write(t: String) {
20 description("Invalid write")
21 display("Invalid write: {}", t)
22 }
23 }
24}
25
26fn ebounds<T: AsRef<str>>(message: T) -> ErrorKind {
27 ErrorKind::OutOfBounds(message.as_ref().to_owned())
28}
29
30fn ewrite<T: AsRef<str>>(message: T) -> ErrorKind {
31 ErrorKind::Write(message.as_ref().to_owned())
32}
33
34/// An emulated flash device. It is represented as a block of bytes, and a list of the sector
35/// mapings.
36#[derive(Clone)]
37pub struct Flash {
38 data: Vec<u8>,
39 sectors: Vec<usize>,
David Brown562a7a02017-01-23 11:19:03 -070040 // Alignment required for writes.
41 align: usize,
David Brownde7729e2017-01-09 10:41:35 -070042}
43
44impl Flash {
45 /// Given a sector size map, construct a flash device for that.
David Brown562a7a02017-01-23 11:19:03 -070046 pub fn new(sectors: Vec<usize>, align: usize) -> Flash {
47 // Verify that the alignment is a positive power of two.
48 assert!(align > 0);
49 assert!(align & (align - 1) == 0);
50
David Brownde7729e2017-01-09 10:41:35 -070051 let total = sectors.iter().sum();
52 Flash {
53 data: vec![0xffu8; total],
54 sectors: sectors,
David Brown562a7a02017-01-23 11:19:03 -070055 align: align,
David Brownde7729e2017-01-09 10:41:35 -070056 }
57 }
58
59 /// The flash drivers tend to erase beyond the bounds of the given range. Instead, we'll be
60 /// strict, and make sure that the passed arguments are exactly at a sector boundary, otherwise
61 /// return an error.
62 pub fn erase(&mut self, offset: usize, len: usize) -> Result<()> {
63 let (_start, slen) = self.get_sector(offset).ok_or_else(|| ebounds("start"))?;
64 let (end, elen) = self.get_sector(offset + len - 1).ok_or_else(|| ebounds("end"))?;
65
66 if slen != 0 {
67 bail!(ebounds("offset not at start of sector"));
68 }
69 if elen != self.sectors[end] - 1 {
70 bail!(ebounds("end not at start of sector"));
71 }
72
73 for x in &mut self.data[offset .. offset + len] {
74 *x = 0xff;
75 }
76
77 Ok(())
78 }
79
80 /// Writes are fairly unconstrained, but we restrict to only allowing writes of values that
81 /// are entirely written as 0xFF.
82 pub fn write(&mut self, offset: usize, payload: &[u8]) -> Result<()> {
83 if offset + payload.len() > self.data.len() {
David Brownf253fa82017-01-23 15:43:47 -070084 panic!("Write outside of device");
David Brownde7729e2017-01-09 10:41:35 -070085 }
86
David Brown562a7a02017-01-23 11:19:03 -070087 // Verify the alignment (which must be a power of two).
88 if offset & (self.align - 1) != 0 {
David Brownf253fa82017-01-23 15:43:47 -070089 panic!("Misaligned write address");
David Brown562a7a02017-01-23 11:19:03 -070090 }
91
92 if payload.len() & (self.align - 1) != 0 {
David Brownf253fa82017-01-23 15:43:47 -070093 panic!("Write length not multiple of alignment");
David Brown562a7a02017-01-23 11:19:03 -070094 }
95
David Brownde7729e2017-01-09 10:41:35 -070096 let mut sub = &mut self.data[offset .. offset + payload.len()];
97 if sub.iter().any(|x| *x != 0xFF) {
David Brown274f7872017-03-21 16:12:53 -060098 bail!(ewrite(format!("Write to non-FF location: offset: {:x}", offset)));
David Brownde7729e2017-01-09 10:41:35 -070099 }
100
101 sub.copy_from_slice(payload);
102 Ok(())
103 }
104
105 /// Read is simple.
106 pub fn read(&self, offset: usize, data: &mut [u8]) -> Result<()> {
107 if offset + data.len() > self.data.len() {
108 bail!(ebounds("Read outside of device"));
109 }
110
111 let sub = &self.data[offset .. offset + data.len()];
112 data.copy_from_slice(sub);
113 Ok(())
114 }
115
116 // Scan the sector map, and return the base and offset within a sector for this given byte.
117 // Returns None if the value is outside of the device.
118 fn get_sector(&self, offset: usize) -> Option<(usize, usize)> {
119 let mut offset = offset;
120 for (sector, &size) in self.sectors.iter().enumerate() {
121 if offset < size {
122 return Some((sector, offset));
123 }
124 offset -= size;
125 }
126 return None;
127 }
128
129 /// An iterator over each sector in the device.
130 pub fn sector_iter(&self) -> SectorIter {
131 SectorIter {
132 iter: self.sectors.iter().enumerate(),
133 base: 0,
134 }
135 }
136
137 pub fn device_size(&self) -> usize {
138 self.data.len()
139 }
140
141 pub fn dump(&self) {
142 self.data.dump();
143 }
David Brown163ab232017-01-23 15:48:35 -0700144
145 /// Dump this image to the given file.
146 pub fn write_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
147 let mut fd = File::create(path).chain_err(|| "Unable to write image file")?;
148 fd.write_all(&self.data).chain_err(|| "Unable to write to image file")?;
149 Ok(())
150 }
David Brownde7729e2017-01-09 10:41:35 -0700151}
152
153/// It is possible to iterate over the sectors in the device, each element returning this.
154#[derive(Debug)]
155pub struct Sector {
156 /// Which sector is this, starting from 0.
157 pub num: usize,
158 /// The offset, in bytes, of the start of this sector.
159 pub base: usize,
160 /// The length, in bytes, of this sector.
161 pub size: usize,
162}
163
164pub struct SectorIter<'a> {
165 iter: Enumerate<slice::Iter<'a, usize>>,
166 base: usize,
167}
168
169impl<'a> Iterator for SectorIter<'a> {
170 type Item = Sector;
171
172 fn next(&mut self) -> Option<Sector> {
173 match self.iter.next() {
174 None => None,
175 Some((num, &size)) => {
176 let base = self.base;
177 self.base += size;
178 Some(Sector {
179 num: num,
180 base: base,
181 size: size,
182 })
183 }
184 }
185 }
186}
187
188#[cfg(test)]
189mod test {
190 use super::{Flash, Error, ErrorKind, Result, Sector};
191
192 #[test]
193 fn test_flash() {
194 // NXP-style, uniform sectors.
195 let mut f1 = Flash::new(vec![4096usize; 256]);
196 test_device(&mut f1);
197
198 // STM style, non-uniform sectors
199 let mut f2 = Flash::new(vec![16 * 1024, 16 * 1024, 16 * 1024, 64 * 1024,
200 128 * 1024, 128 * 1024, 128 * 1024]);
201 test_device(&mut f2);
202 }
203
204 fn test_device(flash: &mut Flash) {
205 let sectors: Vec<Sector> = flash.sector_iter().collect();
206
207 flash.erase(0, sectors[0].size).unwrap();
208 let flash_size = flash.device_size();
209 flash.erase(0, flash_size).unwrap();
210 assert!(flash.erase(0, sectors[0].size - 1).is_bounds());
211
212 // Verify that write and erase do something.
213 flash.write(0, &[0]).unwrap();
214 let mut buf = [0; 4];
215 flash.read(0, &mut buf).unwrap();
216 assert_eq!(buf, [0, 0xff, 0xff, 0xff]);
217
218 flash.erase(0, sectors[0].size).unwrap();
219 flash.read(0, &mut buf).unwrap();
220 assert_eq!(buf, [0xff; 4]);
221
222 // Program the first and last byte of each sector, verify that has been done, and then
223 // erase to verify the erase boundaries.
224 for sector in &sectors {
225 let byte = [(sector.num & 127) as u8];
226 flash.write(sector.base, &byte).unwrap();
227 flash.write(sector.base + sector.size - 1, &byte).unwrap();
228 }
229
230 // Verify the above
231 let mut buf = Vec::new();
232 for sector in &sectors {
233 let byte = (sector.num & 127) as u8;
234 buf.resize(sector.size, 0);
235 flash.read(sector.base, &mut buf).unwrap();
236 assert_eq!(buf.first(), Some(&byte));
237 assert_eq!(buf.last(), Some(&byte));
238 assert!(buf[1..buf.len()-1].iter().all(|&x| x == 0xff));
239 }
240 }
241
242 // Helper checks for the result type.
243 trait EChecker {
244 fn is_bounds(&self) -> bool;
245 }
246
247 impl<T> EChecker for Result<T> {
248
249 fn is_bounds(&self) -> bool {
250 match *self {
251 Err(Error(ErrorKind::OutOfBounds(_), _)) => true,
252 _ => false,
253 }
254 }
255 }
256}