blob: adbb69a3fb5f316c7c98f8fd39171e9cd0939d45 [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()];
Fabio Utzig19b2c1a2017-04-20 07:32:44 -030097 for (i, x) in sub.iter().enumerate() {
98 if *x != 0xFF {
99 bail!(ewrite(format!("Write to non-FF location at 0x{:x}", offset + i)));
100 }
David Brownde7729e2017-01-09 10:41:35 -0700101 }
102
103 sub.copy_from_slice(payload);
104 Ok(())
105 }
106
107 /// Read is simple.
108 pub fn read(&self, offset: usize, data: &mut [u8]) -> Result<()> {
109 if offset + data.len() > self.data.len() {
110 bail!(ebounds("Read outside of device"));
111 }
112
113 let sub = &self.data[offset .. offset + data.len()];
114 data.copy_from_slice(sub);
115 Ok(())
116 }
117
118 // Scan the sector map, and return the base and offset within a sector for this given byte.
119 // Returns None if the value is outside of the device.
120 fn get_sector(&self, offset: usize) -> Option<(usize, usize)> {
121 let mut offset = offset;
122 for (sector, &size) in self.sectors.iter().enumerate() {
123 if offset < size {
124 return Some((sector, offset));
125 }
126 offset -= size;
127 }
128 return None;
129 }
130
131 /// An iterator over each sector in the device.
132 pub fn sector_iter(&self) -> SectorIter {
133 SectorIter {
134 iter: self.sectors.iter().enumerate(),
135 base: 0,
136 }
137 }
138
139 pub fn device_size(&self) -> usize {
140 self.data.len()
141 }
142
143 pub fn dump(&self) {
144 self.data.dump();
145 }
David Brown163ab232017-01-23 15:48:35 -0700146
147 /// Dump this image to the given file.
148 pub fn write_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
149 let mut fd = File::create(path).chain_err(|| "Unable to write image file")?;
150 fd.write_all(&self.data).chain_err(|| "Unable to write to image file")?;
151 Ok(())
152 }
David Brownde7729e2017-01-09 10:41:35 -0700153}
154
155/// It is possible to iterate over the sectors in the device, each element returning this.
156#[derive(Debug)]
157pub struct Sector {
158 /// Which sector is this, starting from 0.
159 pub num: usize,
160 /// The offset, in bytes, of the start of this sector.
161 pub base: usize,
162 /// The length, in bytes, of this sector.
163 pub size: usize,
164}
165
166pub struct SectorIter<'a> {
167 iter: Enumerate<slice::Iter<'a, usize>>,
168 base: usize,
169}
170
171impl<'a> Iterator for SectorIter<'a> {
172 type Item = Sector;
173
174 fn next(&mut self) -> Option<Sector> {
175 match self.iter.next() {
176 None => None,
177 Some((num, &size)) => {
178 let base = self.base;
179 self.base += size;
180 Some(Sector {
181 num: num,
182 base: base,
183 size: size,
184 })
185 }
186 }
187 }
188}
189
190#[cfg(test)]
191mod test {
192 use super::{Flash, Error, ErrorKind, Result, Sector};
193
194 #[test]
195 fn test_flash() {
196 // NXP-style, uniform sectors.
197 let mut f1 = Flash::new(vec![4096usize; 256]);
198 test_device(&mut f1);
199
200 // STM style, non-uniform sectors
201 let mut f2 = Flash::new(vec![16 * 1024, 16 * 1024, 16 * 1024, 64 * 1024,
202 128 * 1024, 128 * 1024, 128 * 1024]);
203 test_device(&mut f2);
204 }
205
206 fn test_device(flash: &mut Flash) {
207 let sectors: Vec<Sector> = flash.sector_iter().collect();
208
209 flash.erase(0, sectors[0].size).unwrap();
210 let flash_size = flash.device_size();
211 flash.erase(0, flash_size).unwrap();
212 assert!(flash.erase(0, sectors[0].size - 1).is_bounds());
213
214 // Verify that write and erase do something.
215 flash.write(0, &[0]).unwrap();
216 let mut buf = [0; 4];
217 flash.read(0, &mut buf).unwrap();
218 assert_eq!(buf, [0, 0xff, 0xff, 0xff]);
219
220 flash.erase(0, sectors[0].size).unwrap();
221 flash.read(0, &mut buf).unwrap();
222 assert_eq!(buf, [0xff; 4]);
223
224 // Program the first and last byte of each sector, verify that has been done, and then
225 // erase to verify the erase boundaries.
226 for sector in &sectors {
227 let byte = [(sector.num & 127) as u8];
228 flash.write(sector.base, &byte).unwrap();
229 flash.write(sector.base + sector.size - 1, &byte).unwrap();
230 }
231
232 // Verify the above
233 let mut buf = Vec::new();
234 for sector in &sectors {
235 let byte = (sector.num & 127) as u8;
236 buf.resize(sector.size, 0);
237 flash.read(sector.base, &mut buf).unwrap();
238 assert_eq!(buf.first(), Some(&byte));
239 assert_eq!(buf.last(), Some(&byte));
240 assert!(buf[1..buf.len()-1].iter().all(|&x| x == 0xff));
241 }
242 }
243
244 // Helper checks for the result type.
245 trait EChecker {
246 fn is_bounds(&self) -> bool;
247 }
248
249 impl<T> EChecker for Result<T> {
250
251 fn is_bounds(&self) -> bool {
252 match *self {
253 Err(Error(ErrorKind::OutOfBounds(_), _)) => true,
254 _ => false,
255 }
256 }
257 }
258}