1use std::path::PathBuf;
25
26use anyhow::bail;
27use base::error;
28use base::VolatileSlice;
29use disk::DiskFile;
30use serde::Deserialize;
31use serde::Serialize;
32use snapshot::AnySnapshot;
33
34use crate::pci::CrosvmDeviceId;
35use crate::BusAccessInfo;
36use crate::BusDevice;
37use crate::DeviceId;
38use crate::Suspendable;
39
40const COMMAND_WRITE_BYTE: u8 = 0x10;
41const COMMAND_BLOCK_ERASE: u8 = 0x20;
42const COMMAND_CLEAR_STATUS: u8 = 0x50;
43const COMMAND_READ_STATUS: u8 = 0x70;
44const COMMAND_BLOCK_ERASE_CONFIRM: u8 = 0xd0;
45const COMMAND_READ_ARRAY: u8 = 0xff;
46
47const STATUS_READY: u8 = 0x80;
48
49fn pflash_parameters_default_block_size() -> u32 {
50 4 * (1 << 10)
52}
53
54#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
55pub struct PflashParameters {
56 pub path: PathBuf,
57 #[serde(default = "pflash_parameters_default_block_size")]
58 pub block_size: u32,
59}
60
61#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
62enum State {
63 ReadArray,
64 ReadStatus,
65 BlockErase(u64),
66 Write(u64),
67}
68
69pub struct Pflash {
70 image: Box<dyn DiskFile>,
71 image_size: u64,
72 block_size: u32,
73
74 state: State,
75 status: u8,
76}
77
78impl Pflash {
79 pub fn new(image: Box<dyn DiskFile>, block_size: u32) -> anyhow::Result<Pflash> {
80 if !block_size.is_power_of_two() {
81 bail!("Block size {} is not a power of 2", block_size);
82 }
83 let image_size = image.get_len()?;
84 if image_size % block_size as u64 != 0 {
85 bail!(
86 "Disk size {} is not a multiple of block size {}",
87 image_size,
88 block_size
89 );
90 }
91
92 Ok(Pflash {
93 image,
94 image_size,
95 block_size,
96 state: State::ReadArray,
97 status: STATUS_READY,
98 })
99 }
100}
101
102impl BusDevice for Pflash {
103 fn device_id(&self) -> DeviceId {
104 CrosvmDeviceId::Pflash.into()
105 }
106
107 fn debug_label(&self) -> String {
108 "pflash".to_owned()
109 }
110
111 fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
112 let offset = info.offset;
113 match &self.state {
114 State::ReadArray => {
115 if offset + data.len() as u64 >= self.image_size {
116 error!("pflash read request beyond disk");
117 return;
118 }
119 if let Err(e) = self
120 .image
121 .read_exact_at_volatile(VolatileSlice::new(data), offset)
122 {
123 error!("pflash failed to read: {}", e);
124 }
125 }
126 State::ReadStatus => {
127 self.state = State::ReadArray;
128 for d in data {
129 *d = self.status;
130 }
131 }
132 _ => {
133 error!(
134 "pflash received unexpected read in state {:?}, recovering to ReadArray mode",
135 self.state
136 );
137 self.state = State::ReadArray;
138 }
139 }
140 }
141
142 fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
143 if data.len() > 1 {
144 error!("pflash write request for >1 byte, ignoring");
145 return;
146 }
147 let data = data[0];
148 let offset = info.offset;
149
150 match self.state {
151 State::Write(expected_offset) => {
152 self.state = State::ReadArray;
153 self.status = STATUS_READY;
154
155 if offset != expected_offset {
156 error!("pflash received write for offset {} that doesn't match offset from WRITE_BYTE command {}", offset, expected_offset);
157 return;
158 }
159 if offset >= self.image_size {
160 error!(
161 "pflash offset {} greater than image size {}",
162 offset, self.image_size
163 );
164 return;
165 }
166
167 if let Err(e) = self
168 .image
169 .write_all_at_volatile(VolatileSlice::new(&mut [data]), offset)
170 {
171 error!("failed to write to pflash: {}", e);
172 }
173 }
174 State::BlockErase(expected_offset) => {
175 self.state = State::ReadArray;
176 self.status = STATUS_READY;
177
178 if data != COMMAND_BLOCK_ERASE_CONFIRM {
179 error!("pflash write data {} after BLOCK_ERASE command, wanted COMMAND_BLOCK_ERASE_CONFIRM", data);
180 return;
181 }
182 if offset != expected_offset {
183 error!("pflash offset {} for BLOCK_ERASE_CONFIRM command does not match the one for BLOCK_ERASE {}", offset, expected_offset);
184 return;
185 }
186 if offset >= self.image_size {
187 error!(
188 "pflash block erase attempt offset {} beyond image size {}",
189 offset, self.image_size
190 );
191 return;
192 }
193 if offset % self.block_size as u64 != 0 {
194 error!(
195 "pflash block erase offset {} not on block boundary with block size {}",
196 offset, self.block_size
197 );
198 return;
199 }
200
201 if let Err(e) = self.image.write_all_at_volatile(
202 VolatileSlice::new(&mut [0xff].repeat(self.block_size.try_into().unwrap())),
203 offset,
204 ) {
205 error!("pflash failed to erase block: {}", e);
206 }
207 }
208 _ => {
209 let command = data;
212
213 match command {
214 COMMAND_READ_ARRAY => {
215 self.state = State::ReadArray;
216 self.status = STATUS_READY;
217 }
218 COMMAND_READ_STATUS => self.state = State::ReadStatus,
219 COMMAND_CLEAR_STATUS => {
220 self.state = State::ReadArray;
221 self.status = 0;
222 }
223 COMMAND_WRITE_BYTE => self.state = State::Write(offset),
224 COMMAND_BLOCK_ERASE => self.state = State::BlockErase(offset),
225 _ => {
226 error!("received unexpected/unsupported pflash command {}, ignoring and returning to read mode", command);
227 self.state = State::ReadArray
228 }
229 }
230 }
231 }
232 }
233}
234
235impl Suspendable for Pflash {
236 fn snapshot(&mut self) -> anyhow::Result<AnySnapshot> {
237 AnySnapshot::to_any((self.status, self.state))
238 }
239
240 fn restore(&mut self, data: AnySnapshot) -> anyhow::Result<()> {
241 let (status, state) = AnySnapshot::from_any(data)?;
242 self.status = status;
243 self.state = state;
244 Ok(())
245 }
246
247 fn sleep(&mut self) -> anyhow::Result<()> {
248 Ok(())
250 }
251
252 fn wake(&mut self) -> anyhow::Result<()> {
253 Ok(())
254 }
255}
256
257#[cfg(test)]
258mod tests {
259 use base::FileReadWriteAtVolatile;
260 use tempfile::tempfile;
261
262 use super::*;
263
264 const IMAGE_SIZE: usize = 4 * (1 << 20); const BLOCK_SIZE: u32 = 4 * (1 << 10); fn empty_image() -> Box<dyn DiskFile> {
268 let f = Box::new(tempfile().unwrap());
269 f.write_all_at_volatile(VolatileSlice::new(&mut [0xff].repeat(IMAGE_SIZE)), 0)
270 .unwrap();
271 f
272 }
273
274 fn new(f: Box<dyn DiskFile>) -> Pflash {
275 Pflash::new(f, BLOCK_SIZE).unwrap()
276 }
277
278 fn off(offset: u64) -> BusAccessInfo {
279 BusAccessInfo {
280 offset,
281 address: 0,
282 id: 0,
283 }
284 }
285
286 #[test]
287 fn read() {
288 let f = empty_image();
289 let mut want = [0xde, 0xad, 0xbe, 0xef];
290 let offset = 0x1000;
291 f.write_all_at_volatile(VolatileSlice::new(&mut want), offset)
292 .unwrap();
293
294 let mut pflash = new(f);
295 let mut got = [0u8; 4];
296 pflash.read(off(offset), &mut got[..]);
297 assert_eq!(want, got);
298 }
299
300 #[test]
301 fn write() {
302 let f = empty_image();
303 let want = [0xdeu8];
304 let offset = 0x1000;
305
306 let mut pflash = new(f);
307 pflash.write(off(offset), &[COMMAND_WRITE_BYTE]);
308 pflash.write(off(offset), &want);
309
310 pflash.write(off(0), &[COMMAND_READ_ARRAY]);
312 let mut got = [0u8; 1];
313 pflash.read(off(offset), &mut got);
314 assert_eq!(want, got);
315
316 pflash
318 .image
319 .read_exact_at_volatile(VolatileSlice::new(&mut got), offset)
320 .unwrap();
321 assert_eq!(want, got);
322
323 let mut pflash = new(pflash.image);
325 pflash.read(off(offset), &mut got);
326 assert_eq!(want, got);
327
328 let mut got = [0u8; 4];
330 pflash.write(off(offset), &[COMMAND_READ_STATUS]);
331 pflash.read(off(offset), &mut got);
332 let want = [STATUS_READY; 4];
333 assert_eq!(want, got);
334 }
335
336 #[test]
337 fn erase() {
338 let f = empty_image();
339 let mut data = [0xde, 0xad, 0xbe, 0xef];
340 let offset = 0x1000;
341 f.write_all_at_volatile(VolatileSlice::new(&mut data), offset)
342 .unwrap();
343 f.write_all_at_volatile(VolatileSlice::new(&mut data), offset * 2)
344 .unwrap();
345
346 let mut pflash = new(f);
347 pflash.write(off(offset), &[COMMAND_BLOCK_ERASE]);
348 pflash.write(off(offset), &[COMMAND_BLOCK_ERASE_CONFIRM]);
349
350 pflash.write(off(0), &[COMMAND_READ_ARRAY]);
351 let mut got = [0u8; 4];
352 pflash.read(off(offset), &mut got);
353 let want = [0xffu8; 4];
354 assert_eq!(want, got);
355
356 let want = data;
357 pflash.read(off(offset * 2), &mut got);
358 assert_eq!(want, got);
359
360 pflash.write(off(offset), &[COMMAND_READ_STATUS]);
362 pflash.read(off(offset), &mut got);
363 let want = [STATUS_READY; 4];
364 assert_eq!(want, got);
365 }
366
367 #[test]
368 fn status() {
369 let f = empty_image();
370 let mut data = [0xde, 0xad, 0xbe, 0xff];
371 let offset = 0x0;
372 f.write_all_at_volatile(VolatileSlice::new(&mut data), offset)
373 .unwrap();
374
375 let mut pflash = new(f);
376
377 pflash.write(off(offset), &[COMMAND_READ_STATUS]);
379 let mut got = [0u8; 4];
380 pflash.read(off(offset), &mut got);
381 let want = [STATUS_READY; 4];
382 assert_eq!(want, got);
383
384 pflash.write(off(offset), &[COMMAND_CLEAR_STATUS]);
386 pflash.write(off(offset), &[COMMAND_READ_STATUS]);
387 pflash.read(off(offset), &mut got);
388 let want = [0; 4];
389 assert_eq!(want, got);
390
391 pflash.read(off(offset), &mut got);
395 pflash.write(off(offset), &[COMMAND_READ_STATUS]);
396 pflash.read(off(offset), &mut got);
397 let want = [0; 4];
398 assert_eq!(want, got);
399 }
400
401 #[test]
402 fn overwrite() {
403 let f = empty_image();
404 let data = [0];
405 let offset = off((16 * IMAGE_SIZE).try_into().unwrap());
406
407 let mut pflash = new(f);
409 let old_size = pflash.image.get_len().unwrap();
410 assert_eq!(old_size, IMAGE_SIZE as u64);
411
412 pflash.write(offset, &[COMMAND_WRITE_BYTE]);
413 pflash.write(offset, &data);
414
415 let new_size = pflash.image.get_len().unwrap();
416 assert_eq!(new_size, IMAGE_SIZE as u64);
417 }
418}