xref: /aosp_15_r20/external/crosvm/devices/src/pflash.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2022 The ChromiumOS Authors
2*bb4ee6a4SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*bb4ee6a4SAndroid Build Coastguard Worker // found in the LICENSE file.
4*bb4ee6a4SAndroid Build Coastguard Worker 
5*bb4ee6a4SAndroid Build Coastguard Worker //! Programmable flash device that supports the minimum interface that OVMF
6*bb4ee6a4SAndroid Build Coastguard Worker //! requires. This is purpose-built to allow OVMF to store UEFI variables in
7*bb4ee6a4SAndroid Build Coastguard Worker //! the same way that it stores them on QEMU.
8*bb4ee6a4SAndroid Build Coastguard Worker //!
9*bb4ee6a4SAndroid Build Coastguard Worker //! For that reason it's heavily based on [QEMU's pflash implementation], while
10*bb4ee6a4SAndroid Build Coastguard Worker //! taking even more shortcuts, chief among them being the complete lack of CFI
11*bb4ee6a4SAndroid Build Coastguard Worker //! tables, which systems would normally use to learn how to use the device.
12*bb4ee6a4SAndroid Build Coastguard Worker //!
13*bb4ee6a4SAndroid Build Coastguard Worker //! In addition to full-width reads, we only support single byte writes,
14*bb4ee6a4SAndroid Build Coastguard Worker //! block erases, and status requests, which OVMF uses to probe the device to
15*bb4ee6a4SAndroid Build Coastguard Worker //! determine if it is pflash.
16*bb4ee6a4SAndroid Build Coastguard Worker //!
17*bb4ee6a4SAndroid Build Coastguard Worker //! Note that without SMM support in crosvm (which it doesn't yet have) this
18*bb4ee6a4SAndroid Build Coastguard Worker //! device is directly accessible to potentially malicious kernels. With SMM
19*bb4ee6a4SAndroid Build Coastguard Worker //! and the appropriate changes to this device this could be made more secure
20*bb4ee6a4SAndroid Build Coastguard Worker //! by ensuring only the BIOS is able to touch the pflash.
21*bb4ee6a4SAndroid Build Coastguard Worker //!
22*bb4ee6a4SAndroid Build Coastguard Worker //! [QEMU's pflash implementation]: https://github.com/qemu/qemu/blob/master/hw/block/pflash_cfi01.c
23*bb4ee6a4SAndroid Build Coastguard Worker 
24*bb4ee6a4SAndroid Build Coastguard Worker use std::path::PathBuf;
25*bb4ee6a4SAndroid Build Coastguard Worker 
26*bb4ee6a4SAndroid Build Coastguard Worker use anyhow::bail;
27*bb4ee6a4SAndroid Build Coastguard Worker use base::error;
28*bb4ee6a4SAndroid Build Coastguard Worker use base::VolatileSlice;
29*bb4ee6a4SAndroid Build Coastguard Worker use disk::DiskFile;
30*bb4ee6a4SAndroid Build Coastguard Worker use serde::Deserialize;
31*bb4ee6a4SAndroid Build Coastguard Worker use serde::Serialize;
32*bb4ee6a4SAndroid Build Coastguard Worker 
33*bb4ee6a4SAndroid Build Coastguard Worker use crate::pci::CrosvmDeviceId;
34*bb4ee6a4SAndroid Build Coastguard Worker use crate::BusAccessInfo;
35*bb4ee6a4SAndroid Build Coastguard Worker use crate::BusDevice;
36*bb4ee6a4SAndroid Build Coastguard Worker use crate::DeviceId;
37*bb4ee6a4SAndroid Build Coastguard Worker use crate::Suspendable;
38*bb4ee6a4SAndroid Build Coastguard Worker 
39*bb4ee6a4SAndroid Build Coastguard Worker const COMMAND_WRITE_BYTE: u8 = 0x10;
40*bb4ee6a4SAndroid Build Coastguard Worker const COMMAND_BLOCK_ERASE: u8 = 0x20;
41*bb4ee6a4SAndroid Build Coastguard Worker const COMMAND_CLEAR_STATUS: u8 = 0x50;
42*bb4ee6a4SAndroid Build Coastguard Worker const COMMAND_READ_STATUS: u8 = 0x70;
43*bb4ee6a4SAndroid Build Coastguard Worker const COMMAND_BLOCK_ERASE_CONFIRM: u8 = 0xd0;
44*bb4ee6a4SAndroid Build Coastguard Worker const COMMAND_READ_ARRAY: u8 = 0xff;
45*bb4ee6a4SAndroid Build Coastguard Worker 
46*bb4ee6a4SAndroid Build Coastguard Worker const STATUS_READY: u8 = 0x80;
47*bb4ee6a4SAndroid Build Coastguard Worker 
pflash_parameters_default_block_size() -> u3248*bb4ee6a4SAndroid Build Coastguard Worker fn pflash_parameters_default_block_size() -> u32 {
49*bb4ee6a4SAndroid Build Coastguard Worker     // 4K
50*bb4ee6a4SAndroid Build Coastguard Worker     4 * (1 << 10)
51*bb4ee6a4SAndroid Build Coastguard Worker }
52*bb4ee6a4SAndroid Build Coastguard Worker 
53*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
54*bb4ee6a4SAndroid Build Coastguard Worker pub struct PflashParameters {
55*bb4ee6a4SAndroid Build Coastguard Worker     pub path: PathBuf,
56*bb4ee6a4SAndroid Build Coastguard Worker     #[serde(default = "pflash_parameters_default_block_size")]
57*bb4ee6a4SAndroid Build Coastguard Worker     pub block_size: u32,
58*bb4ee6a4SAndroid Build Coastguard Worker }
59*bb4ee6a4SAndroid Build Coastguard Worker 
60*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Clone, Copy, Debug, Deserialize, Serialize)]
61*bb4ee6a4SAndroid Build Coastguard Worker enum State {
62*bb4ee6a4SAndroid Build Coastguard Worker     ReadArray,
63*bb4ee6a4SAndroid Build Coastguard Worker     ReadStatus,
64*bb4ee6a4SAndroid Build Coastguard Worker     BlockErase(u64),
65*bb4ee6a4SAndroid Build Coastguard Worker     Write(u64),
66*bb4ee6a4SAndroid Build Coastguard Worker }
67*bb4ee6a4SAndroid Build Coastguard Worker 
68*bb4ee6a4SAndroid Build Coastguard Worker pub struct Pflash {
69*bb4ee6a4SAndroid Build Coastguard Worker     image: Box<dyn DiskFile>,
70*bb4ee6a4SAndroid Build Coastguard Worker     image_size: u64,
71*bb4ee6a4SAndroid Build Coastguard Worker     block_size: u32,
72*bb4ee6a4SAndroid Build Coastguard Worker 
73*bb4ee6a4SAndroid Build Coastguard Worker     state: State,
74*bb4ee6a4SAndroid Build Coastguard Worker     status: u8,
75*bb4ee6a4SAndroid Build Coastguard Worker }
76*bb4ee6a4SAndroid Build Coastguard Worker 
77*bb4ee6a4SAndroid Build Coastguard Worker impl Pflash {
new(image: Box<dyn DiskFile>, block_size: u32) -> anyhow::Result<Pflash>78*bb4ee6a4SAndroid Build Coastguard Worker     pub fn new(image: Box<dyn DiskFile>, block_size: u32) -> anyhow::Result<Pflash> {
79*bb4ee6a4SAndroid Build Coastguard Worker         if !block_size.is_power_of_two() {
80*bb4ee6a4SAndroid Build Coastguard Worker             bail!("Block size {} is not a power of 2", block_size);
81*bb4ee6a4SAndroid Build Coastguard Worker         }
82*bb4ee6a4SAndroid Build Coastguard Worker         let image_size = image.get_len()?;
83*bb4ee6a4SAndroid Build Coastguard Worker         if image_size % block_size as u64 != 0 {
84*bb4ee6a4SAndroid Build Coastguard Worker             bail!(
85*bb4ee6a4SAndroid Build Coastguard Worker                 "Disk size {} is not a multiple of block size {}",
86*bb4ee6a4SAndroid Build Coastguard Worker                 image_size,
87*bb4ee6a4SAndroid Build Coastguard Worker                 block_size
88*bb4ee6a4SAndroid Build Coastguard Worker             );
89*bb4ee6a4SAndroid Build Coastguard Worker         }
90*bb4ee6a4SAndroid Build Coastguard Worker 
91*bb4ee6a4SAndroid Build Coastguard Worker         Ok(Pflash {
92*bb4ee6a4SAndroid Build Coastguard Worker             image,
93*bb4ee6a4SAndroid Build Coastguard Worker             image_size,
94*bb4ee6a4SAndroid Build Coastguard Worker             block_size,
95*bb4ee6a4SAndroid Build Coastguard Worker             state: State::ReadArray,
96*bb4ee6a4SAndroid Build Coastguard Worker             status: STATUS_READY,
97*bb4ee6a4SAndroid Build Coastguard Worker         })
98*bb4ee6a4SAndroid Build Coastguard Worker     }
99*bb4ee6a4SAndroid Build Coastguard Worker }
100*bb4ee6a4SAndroid Build Coastguard Worker 
101*bb4ee6a4SAndroid Build Coastguard Worker impl BusDevice for Pflash {
device_id(&self) -> DeviceId102*bb4ee6a4SAndroid Build Coastguard Worker     fn device_id(&self) -> DeviceId {
103*bb4ee6a4SAndroid Build Coastguard Worker         CrosvmDeviceId::Pflash.into()
104*bb4ee6a4SAndroid Build Coastguard Worker     }
105*bb4ee6a4SAndroid Build Coastguard Worker 
debug_label(&self) -> String106*bb4ee6a4SAndroid Build Coastguard Worker     fn debug_label(&self) -> String {
107*bb4ee6a4SAndroid Build Coastguard Worker         "pflash".to_owned()
108*bb4ee6a4SAndroid Build Coastguard Worker     }
109*bb4ee6a4SAndroid Build Coastguard Worker 
read(&mut self, info: BusAccessInfo, data: &mut [u8])110*bb4ee6a4SAndroid Build Coastguard Worker     fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
111*bb4ee6a4SAndroid Build Coastguard Worker         let offset = info.offset;
112*bb4ee6a4SAndroid Build Coastguard Worker         match &self.state {
113*bb4ee6a4SAndroid Build Coastguard Worker             State::ReadArray => {
114*bb4ee6a4SAndroid Build Coastguard Worker                 if offset + data.len() as u64 >= self.image_size {
115*bb4ee6a4SAndroid Build Coastguard Worker                     error!("pflash read request beyond disk");
116*bb4ee6a4SAndroid Build Coastguard Worker                     return;
117*bb4ee6a4SAndroid Build Coastguard Worker                 }
118*bb4ee6a4SAndroid Build Coastguard Worker                 if let Err(e) = self
119*bb4ee6a4SAndroid Build Coastguard Worker                     .image
120*bb4ee6a4SAndroid Build Coastguard Worker                     .read_exact_at_volatile(VolatileSlice::new(data), offset)
121*bb4ee6a4SAndroid Build Coastguard Worker                 {
122*bb4ee6a4SAndroid Build Coastguard Worker                     error!("pflash failed to read: {}", e);
123*bb4ee6a4SAndroid Build Coastguard Worker                 }
124*bb4ee6a4SAndroid Build Coastguard Worker             }
125*bb4ee6a4SAndroid Build Coastguard Worker             State::ReadStatus => {
126*bb4ee6a4SAndroid Build Coastguard Worker                 self.state = State::ReadArray;
127*bb4ee6a4SAndroid Build Coastguard Worker                 for d in data {
128*bb4ee6a4SAndroid Build Coastguard Worker                     *d = self.status;
129*bb4ee6a4SAndroid Build Coastguard Worker                 }
130*bb4ee6a4SAndroid Build Coastguard Worker             }
131*bb4ee6a4SAndroid Build Coastguard Worker             _ => {
132*bb4ee6a4SAndroid Build Coastguard Worker                 error!(
133*bb4ee6a4SAndroid Build Coastguard Worker                     "pflash received unexpected read in state {:?}, recovering to ReadArray mode",
134*bb4ee6a4SAndroid Build Coastguard Worker                     self.state
135*bb4ee6a4SAndroid Build Coastguard Worker                 );
136*bb4ee6a4SAndroid Build Coastguard Worker                 self.state = State::ReadArray;
137*bb4ee6a4SAndroid Build Coastguard Worker             }
138*bb4ee6a4SAndroid Build Coastguard Worker         }
139*bb4ee6a4SAndroid Build Coastguard Worker     }
140*bb4ee6a4SAndroid Build Coastguard Worker 
write(&mut self, info: BusAccessInfo, data: &[u8])141*bb4ee6a4SAndroid Build Coastguard Worker     fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
142*bb4ee6a4SAndroid Build Coastguard Worker         if data.len() > 1 {
143*bb4ee6a4SAndroid Build Coastguard Worker             error!("pflash write request for >1 byte, ignoring");
144*bb4ee6a4SAndroid Build Coastguard Worker             return;
145*bb4ee6a4SAndroid Build Coastguard Worker         }
146*bb4ee6a4SAndroid Build Coastguard Worker         let data = data[0];
147*bb4ee6a4SAndroid Build Coastguard Worker         let offset = info.offset;
148*bb4ee6a4SAndroid Build Coastguard Worker 
149*bb4ee6a4SAndroid Build Coastguard Worker         match self.state {
150*bb4ee6a4SAndroid Build Coastguard Worker             State::Write(expected_offset) => {
151*bb4ee6a4SAndroid Build Coastguard Worker                 self.state = State::ReadArray;
152*bb4ee6a4SAndroid Build Coastguard Worker                 self.status = STATUS_READY;
153*bb4ee6a4SAndroid Build Coastguard Worker 
154*bb4ee6a4SAndroid Build Coastguard Worker                 if offset != expected_offset {
155*bb4ee6a4SAndroid Build Coastguard Worker                     error!("pflash received write for offset {} that doesn't match offset from WRITE_BYTE command {}", offset, expected_offset);
156*bb4ee6a4SAndroid Build Coastguard Worker                     return;
157*bb4ee6a4SAndroid Build Coastguard Worker                 }
158*bb4ee6a4SAndroid Build Coastguard Worker                 if offset >= self.image_size {
159*bb4ee6a4SAndroid Build Coastguard Worker                     error!(
160*bb4ee6a4SAndroid Build Coastguard Worker                         "pflash offset {} greater than image size {}",
161*bb4ee6a4SAndroid Build Coastguard Worker                         offset, self.image_size
162*bb4ee6a4SAndroid Build Coastguard Worker                     );
163*bb4ee6a4SAndroid Build Coastguard Worker                     return;
164*bb4ee6a4SAndroid Build Coastguard Worker                 }
165*bb4ee6a4SAndroid Build Coastguard Worker 
166*bb4ee6a4SAndroid Build Coastguard Worker                 if let Err(e) = self
167*bb4ee6a4SAndroid Build Coastguard Worker                     .image
168*bb4ee6a4SAndroid Build Coastguard Worker                     .write_all_at_volatile(VolatileSlice::new(&mut [data]), offset)
169*bb4ee6a4SAndroid Build Coastguard Worker                 {
170*bb4ee6a4SAndroid Build Coastguard Worker                     error!("failed to write to pflash: {}", e);
171*bb4ee6a4SAndroid Build Coastguard Worker                 }
172*bb4ee6a4SAndroid Build Coastguard Worker             }
173*bb4ee6a4SAndroid Build Coastguard Worker             State::BlockErase(expected_offset) => {
174*bb4ee6a4SAndroid Build Coastguard Worker                 self.state = State::ReadArray;
175*bb4ee6a4SAndroid Build Coastguard Worker                 self.status = STATUS_READY;
176*bb4ee6a4SAndroid Build Coastguard Worker 
177*bb4ee6a4SAndroid Build Coastguard Worker                 if data != COMMAND_BLOCK_ERASE_CONFIRM {
178*bb4ee6a4SAndroid Build Coastguard Worker                     error!("pflash write data {} after BLOCK_ERASE command, wanted COMMAND_BLOCK_ERASE_CONFIRM", data);
179*bb4ee6a4SAndroid Build Coastguard Worker                     return;
180*bb4ee6a4SAndroid Build Coastguard Worker                 }
181*bb4ee6a4SAndroid Build Coastguard Worker                 if offset != expected_offset {
182*bb4ee6a4SAndroid Build Coastguard Worker                     error!("pflash offset {} for BLOCK_ERASE_CONFIRM command does not match the one for BLOCK_ERASE {}", offset, expected_offset);
183*bb4ee6a4SAndroid Build Coastguard Worker                     return;
184*bb4ee6a4SAndroid Build Coastguard Worker                 }
185*bb4ee6a4SAndroid Build Coastguard Worker                 if offset >= self.image_size {
186*bb4ee6a4SAndroid Build Coastguard Worker                     error!(
187*bb4ee6a4SAndroid Build Coastguard Worker                         "pflash block erase attempt offset {} beyond image size {}",
188*bb4ee6a4SAndroid Build Coastguard Worker                         offset, self.image_size
189*bb4ee6a4SAndroid Build Coastguard Worker                     );
190*bb4ee6a4SAndroid Build Coastguard Worker                     return;
191*bb4ee6a4SAndroid Build Coastguard Worker                 }
192*bb4ee6a4SAndroid Build Coastguard Worker                 if offset % self.block_size as u64 != 0 {
193*bb4ee6a4SAndroid Build Coastguard Worker                     error!(
194*bb4ee6a4SAndroid Build Coastguard Worker                         "pflash block erase offset {} not on block boundary with block size {}",
195*bb4ee6a4SAndroid Build Coastguard Worker                         offset, self.block_size
196*bb4ee6a4SAndroid Build Coastguard Worker                     );
197*bb4ee6a4SAndroid Build Coastguard Worker                     return;
198*bb4ee6a4SAndroid Build Coastguard Worker                 }
199*bb4ee6a4SAndroid Build Coastguard Worker 
200*bb4ee6a4SAndroid Build Coastguard Worker                 if let Err(e) = self.image.write_all_at_volatile(
201*bb4ee6a4SAndroid Build Coastguard Worker                     VolatileSlice::new(&mut [0xff].repeat(self.block_size.try_into().unwrap())),
202*bb4ee6a4SAndroid Build Coastguard Worker                     offset,
203*bb4ee6a4SAndroid Build Coastguard Worker                 ) {
204*bb4ee6a4SAndroid Build Coastguard Worker                     error!("pflash failed to erase block: {}", e);
205*bb4ee6a4SAndroid Build Coastguard Worker                 }
206*bb4ee6a4SAndroid Build Coastguard Worker             }
207*bb4ee6a4SAndroid Build Coastguard Worker             _ => {
208*bb4ee6a4SAndroid Build Coastguard Worker                 // If we're not expecting anything else then assume this is a
209*bb4ee6a4SAndroid Build Coastguard Worker                 // command to transition states.
210*bb4ee6a4SAndroid Build Coastguard Worker                 let command = data;
211*bb4ee6a4SAndroid Build Coastguard Worker 
212*bb4ee6a4SAndroid Build Coastguard Worker                 match command {
213*bb4ee6a4SAndroid Build Coastguard Worker                     COMMAND_READ_ARRAY => {
214*bb4ee6a4SAndroid Build Coastguard Worker                         self.state = State::ReadArray;
215*bb4ee6a4SAndroid Build Coastguard Worker                         self.status = STATUS_READY;
216*bb4ee6a4SAndroid Build Coastguard Worker                     }
217*bb4ee6a4SAndroid Build Coastguard Worker                     COMMAND_READ_STATUS => self.state = State::ReadStatus,
218*bb4ee6a4SAndroid Build Coastguard Worker                     COMMAND_CLEAR_STATUS => {
219*bb4ee6a4SAndroid Build Coastguard Worker                         self.state = State::ReadArray;
220*bb4ee6a4SAndroid Build Coastguard Worker                         self.status = 0;
221*bb4ee6a4SAndroid Build Coastguard Worker                     }
222*bb4ee6a4SAndroid Build Coastguard Worker                     COMMAND_WRITE_BYTE => self.state = State::Write(offset),
223*bb4ee6a4SAndroid Build Coastguard Worker                     COMMAND_BLOCK_ERASE => self.state = State::BlockErase(offset),
224*bb4ee6a4SAndroid Build Coastguard Worker                     _ => {
225*bb4ee6a4SAndroid Build Coastguard Worker                         error!("received unexpected/unsupported pflash command {}, ignoring and returning to read mode", command);
226*bb4ee6a4SAndroid Build Coastguard Worker                         self.state = State::ReadArray
227*bb4ee6a4SAndroid Build Coastguard Worker                     }
228*bb4ee6a4SAndroid Build Coastguard Worker                 }
229*bb4ee6a4SAndroid Build Coastguard Worker             }
230*bb4ee6a4SAndroid Build Coastguard Worker         }
231*bb4ee6a4SAndroid Build Coastguard Worker     }
232*bb4ee6a4SAndroid Build Coastguard Worker }
233*bb4ee6a4SAndroid Build Coastguard Worker 
234*bb4ee6a4SAndroid Build Coastguard Worker impl Suspendable for Pflash {
snapshot(&mut self) -> anyhow::Result<serde_json::Value>235*bb4ee6a4SAndroid Build Coastguard Worker     fn snapshot(&mut self) -> anyhow::Result<serde_json::Value> {
236*bb4ee6a4SAndroid Build Coastguard Worker         Ok(serde_json::to_value((self.status, self.state))?)
237*bb4ee6a4SAndroid Build Coastguard Worker     }
238*bb4ee6a4SAndroid Build Coastguard Worker 
restore(&mut self, data: serde_json::Value) -> anyhow::Result<()>239*bb4ee6a4SAndroid Build Coastguard Worker     fn restore(&mut self, data: serde_json::Value) -> anyhow::Result<()> {
240*bb4ee6a4SAndroid Build Coastguard Worker         let (status, state) = serde_json::from_value(data)?;
241*bb4ee6a4SAndroid Build Coastguard Worker         self.status = status;
242*bb4ee6a4SAndroid Build Coastguard Worker         self.state = state;
243*bb4ee6a4SAndroid Build Coastguard Worker         Ok(())
244*bb4ee6a4SAndroid Build Coastguard Worker     }
245*bb4ee6a4SAndroid Build Coastguard Worker 
sleep(&mut self) -> anyhow::Result<()>246*bb4ee6a4SAndroid Build Coastguard Worker     fn sleep(&mut self) -> anyhow::Result<()> {
247*bb4ee6a4SAndroid Build Coastguard Worker         // TODO(schuffelen): Flush the disk after lifting flush() from AsyncDisk to DiskFile
248*bb4ee6a4SAndroid Build Coastguard Worker         Ok(())
249*bb4ee6a4SAndroid Build Coastguard Worker     }
250*bb4ee6a4SAndroid Build Coastguard Worker 
wake(&mut self) -> anyhow::Result<()>251*bb4ee6a4SAndroid Build Coastguard Worker     fn wake(&mut self) -> anyhow::Result<()> {
252*bb4ee6a4SAndroid Build Coastguard Worker         Ok(())
253*bb4ee6a4SAndroid Build Coastguard Worker     }
254*bb4ee6a4SAndroid Build Coastguard Worker }
255*bb4ee6a4SAndroid Build Coastguard Worker 
256*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(test)]
257*bb4ee6a4SAndroid Build Coastguard Worker mod tests {
258*bb4ee6a4SAndroid Build Coastguard Worker     use base::FileReadWriteAtVolatile;
259*bb4ee6a4SAndroid Build Coastguard Worker     use tempfile::tempfile;
260*bb4ee6a4SAndroid Build Coastguard Worker 
261*bb4ee6a4SAndroid Build Coastguard Worker     use super::*;
262*bb4ee6a4SAndroid Build Coastguard Worker 
263*bb4ee6a4SAndroid Build Coastguard Worker     const IMAGE_SIZE: usize = 4 * (1 << 20); // 4M
264*bb4ee6a4SAndroid Build Coastguard Worker     const BLOCK_SIZE: u32 = 4 * (1 << 10); // 4K
265*bb4ee6a4SAndroid Build Coastguard Worker 
empty_image() -> Box<dyn DiskFile>266*bb4ee6a4SAndroid Build Coastguard Worker     fn empty_image() -> Box<dyn DiskFile> {
267*bb4ee6a4SAndroid Build Coastguard Worker         let f = Box::new(tempfile().unwrap());
268*bb4ee6a4SAndroid Build Coastguard Worker         f.write_all_at_volatile(VolatileSlice::new(&mut [0xff].repeat(IMAGE_SIZE)), 0)
269*bb4ee6a4SAndroid Build Coastguard Worker             .unwrap();
270*bb4ee6a4SAndroid Build Coastguard Worker         f
271*bb4ee6a4SAndroid Build Coastguard Worker     }
272*bb4ee6a4SAndroid Build Coastguard Worker 
new(f: Box<dyn DiskFile>) -> Pflash273*bb4ee6a4SAndroid Build Coastguard Worker     fn new(f: Box<dyn DiskFile>) -> Pflash {
274*bb4ee6a4SAndroid Build Coastguard Worker         Pflash::new(f, BLOCK_SIZE).unwrap()
275*bb4ee6a4SAndroid Build Coastguard Worker     }
276*bb4ee6a4SAndroid Build Coastguard Worker 
off(offset: u64) -> BusAccessInfo277*bb4ee6a4SAndroid Build Coastguard Worker     fn off(offset: u64) -> BusAccessInfo {
278*bb4ee6a4SAndroid Build Coastguard Worker         BusAccessInfo {
279*bb4ee6a4SAndroid Build Coastguard Worker             offset,
280*bb4ee6a4SAndroid Build Coastguard Worker             address: 0,
281*bb4ee6a4SAndroid Build Coastguard Worker             id: 0,
282*bb4ee6a4SAndroid Build Coastguard Worker         }
283*bb4ee6a4SAndroid Build Coastguard Worker     }
284*bb4ee6a4SAndroid Build Coastguard Worker 
285*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
read()286*bb4ee6a4SAndroid Build Coastguard Worker     fn read() {
287*bb4ee6a4SAndroid Build Coastguard Worker         let f = empty_image();
288*bb4ee6a4SAndroid Build Coastguard Worker         let mut want = [0xde, 0xad, 0xbe, 0xef];
289*bb4ee6a4SAndroid Build Coastguard Worker         let offset = 0x1000;
290*bb4ee6a4SAndroid Build Coastguard Worker         f.write_all_at_volatile(VolatileSlice::new(&mut want), offset)
291*bb4ee6a4SAndroid Build Coastguard Worker             .unwrap();
292*bb4ee6a4SAndroid Build Coastguard Worker 
293*bb4ee6a4SAndroid Build Coastguard Worker         let mut pflash = new(f);
294*bb4ee6a4SAndroid Build Coastguard Worker         let mut got = [0u8; 4];
295*bb4ee6a4SAndroid Build Coastguard Worker         pflash.read(off(offset), &mut got[..]);
296*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(want, got);
297*bb4ee6a4SAndroid Build Coastguard Worker     }
298*bb4ee6a4SAndroid Build Coastguard Worker 
299*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
write()300*bb4ee6a4SAndroid Build Coastguard Worker     fn write() {
301*bb4ee6a4SAndroid Build Coastguard Worker         let f = empty_image();
302*bb4ee6a4SAndroid Build Coastguard Worker         let want = [0xdeu8];
303*bb4ee6a4SAndroid Build Coastguard Worker         let offset = 0x1000;
304*bb4ee6a4SAndroid Build Coastguard Worker 
305*bb4ee6a4SAndroid Build Coastguard Worker         let mut pflash = new(f);
306*bb4ee6a4SAndroid Build Coastguard Worker         pflash.write(off(offset), &[COMMAND_WRITE_BYTE]);
307*bb4ee6a4SAndroid Build Coastguard Worker         pflash.write(off(offset), &want);
308*bb4ee6a4SAndroid Build Coastguard Worker 
309*bb4ee6a4SAndroid Build Coastguard Worker         // Make sure the data reads back correctly over the bus...
310*bb4ee6a4SAndroid Build Coastguard Worker         pflash.write(off(0), &[COMMAND_READ_ARRAY]);
311*bb4ee6a4SAndroid Build Coastguard Worker         let mut got = [0u8; 1];
312*bb4ee6a4SAndroid Build Coastguard Worker         pflash.read(off(offset), &mut got);
313*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(want, got);
314*bb4ee6a4SAndroid Build Coastguard Worker 
315*bb4ee6a4SAndroid Build Coastguard Worker         // And from the backing file itself...
316*bb4ee6a4SAndroid Build Coastguard Worker         pflash
317*bb4ee6a4SAndroid Build Coastguard Worker             .image
318*bb4ee6a4SAndroid Build Coastguard Worker             .read_exact_at_volatile(VolatileSlice::new(&mut got), offset)
319*bb4ee6a4SAndroid Build Coastguard Worker             .unwrap();
320*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(want, got);
321*bb4ee6a4SAndroid Build Coastguard Worker 
322*bb4ee6a4SAndroid Build Coastguard Worker         // And when we recreate the device.
323*bb4ee6a4SAndroid Build Coastguard Worker         let mut pflash = new(pflash.image);
324*bb4ee6a4SAndroid Build Coastguard Worker         pflash.read(off(offset), &mut got);
325*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(want, got);
326*bb4ee6a4SAndroid Build Coastguard Worker 
327*bb4ee6a4SAndroid Build Coastguard Worker         // Finally make sure our status is ready.
328*bb4ee6a4SAndroid Build Coastguard Worker         let mut got = [0u8; 4];
329*bb4ee6a4SAndroid Build Coastguard Worker         pflash.write(off(offset), &[COMMAND_READ_STATUS]);
330*bb4ee6a4SAndroid Build Coastguard Worker         pflash.read(off(offset), &mut got);
331*bb4ee6a4SAndroid Build Coastguard Worker         let want = [STATUS_READY; 4];
332*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(want, got);
333*bb4ee6a4SAndroid Build Coastguard Worker     }
334*bb4ee6a4SAndroid Build Coastguard Worker 
335*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
erase()336*bb4ee6a4SAndroid Build Coastguard Worker     fn erase() {
337*bb4ee6a4SAndroid Build Coastguard Worker         let f = empty_image();
338*bb4ee6a4SAndroid Build Coastguard Worker         let mut data = [0xde, 0xad, 0xbe, 0xef];
339*bb4ee6a4SAndroid Build Coastguard Worker         let offset = 0x1000;
340*bb4ee6a4SAndroid Build Coastguard Worker         f.write_all_at_volatile(VolatileSlice::new(&mut data), offset)
341*bb4ee6a4SAndroid Build Coastguard Worker             .unwrap();
342*bb4ee6a4SAndroid Build Coastguard Worker         f.write_all_at_volatile(VolatileSlice::new(&mut data), offset * 2)
343*bb4ee6a4SAndroid Build Coastguard Worker             .unwrap();
344*bb4ee6a4SAndroid Build Coastguard Worker 
345*bb4ee6a4SAndroid Build Coastguard Worker         let mut pflash = new(f);
346*bb4ee6a4SAndroid Build Coastguard Worker         pflash.write(off(offset), &[COMMAND_BLOCK_ERASE]);
347*bb4ee6a4SAndroid Build Coastguard Worker         pflash.write(off(offset), &[COMMAND_BLOCK_ERASE_CONFIRM]);
348*bb4ee6a4SAndroid Build Coastguard Worker 
349*bb4ee6a4SAndroid Build Coastguard Worker         pflash.write(off(0), &[COMMAND_READ_ARRAY]);
350*bb4ee6a4SAndroid Build Coastguard Worker         let mut got = [0u8; 4];
351*bb4ee6a4SAndroid Build Coastguard Worker         pflash.read(off(offset), &mut got);
352*bb4ee6a4SAndroid Build Coastguard Worker         let want = [0xffu8; 4];
353*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(want, got);
354*bb4ee6a4SAndroid Build Coastguard Worker 
355*bb4ee6a4SAndroid Build Coastguard Worker         let want = data;
356*bb4ee6a4SAndroid Build Coastguard Worker         pflash.read(off(offset * 2), &mut got);
357*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(want, got);
358*bb4ee6a4SAndroid Build Coastguard Worker 
359*bb4ee6a4SAndroid Build Coastguard Worker         // Make sure our status is ready.
360*bb4ee6a4SAndroid Build Coastguard Worker         pflash.write(off(offset), &[COMMAND_READ_STATUS]);
361*bb4ee6a4SAndroid Build Coastguard Worker         pflash.read(off(offset), &mut got);
362*bb4ee6a4SAndroid Build Coastguard Worker         let want = [STATUS_READY; 4];
363*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(want, got);
364*bb4ee6a4SAndroid Build Coastguard Worker     }
365*bb4ee6a4SAndroid Build Coastguard Worker 
366*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
status()367*bb4ee6a4SAndroid Build Coastguard Worker     fn status() {
368*bb4ee6a4SAndroid Build Coastguard Worker         let f = empty_image();
369*bb4ee6a4SAndroid Build Coastguard Worker         let mut data = [0xde, 0xad, 0xbe, 0xff];
370*bb4ee6a4SAndroid Build Coastguard Worker         let offset = 0x0;
371*bb4ee6a4SAndroid Build Coastguard Worker         f.write_all_at_volatile(VolatileSlice::new(&mut data), offset)
372*bb4ee6a4SAndroid Build Coastguard Worker             .unwrap();
373*bb4ee6a4SAndroid Build Coastguard Worker 
374*bb4ee6a4SAndroid Build Coastguard Worker         let mut pflash = new(f);
375*bb4ee6a4SAndroid Build Coastguard Worker 
376*bb4ee6a4SAndroid Build Coastguard Worker         // Make sure we start off in the "ready" status.
377*bb4ee6a4SAndroid Build Coastguard Worker         pflash.write(off(offset), &[COMMAND_READ_STATUS]);
378*bb4ee6a4SAndroid Build Coastguard Worker         let mut got = [0u8; 4];
379*bb4ee6a4SAndroid Build Coastguard Worker         pflash.read(off(offset), &mut got);
380*bb4ee6a4SAndroid Build Coastguard Worker         let want = [STATUS_READY; 4];
381*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(want, got);
382*bb4ee6a4SAndroid Build Coastguard Worker 
383*bb4ee6a4SAndroid Build Coastguard Worker         // Make sure we can clear the status properly.
384*bb4ee6a4SAndroid Build Coastguard Worker         pflash.write(off(offset), &[COMMAND_CLEAR_STATUS]);
385*bb4ee6a4SAndroid Build Coastguard Worker         pflash.write(off(offset), &[COMMAND_READ_STATUS]);
386*bb4ee6a4SAndroid Build Coastguard Worker         pflash.read(off(offset), &mut got);
387*bb4ee6a4SAndroid Build Coastguard Worker         let want = [0; 4];
388*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(want, got);
389*bb4ee6a4SAndroid Build Coastguard Worker 
390*bb4ee6a4SAndroid Build Coastguard Worker         // We implicitly jump back into READ_ARRAY mode after reading the,
391*bb4ee6a4SAndroid Build Coastguard Worker         // status but for OVMF's probe we require that this doesn't actually
392*bb4ee6a4SAndroid Build Coastguard Worker         // affect the cleared status.
393*bb4ee6a4SAndroid Build Coastguard Worker         pflash.read(off(offset), &mut got);
394*bb4ee6a4SAndroid Build Coastguard Worker         pflash.write(off(offset), &[COMMAND_READ_STATUS]);
395*bb4ee6a4SAndroid Build Coastguard Worker         pflash.read(off(offset), &mut got);
396*bb4ee6a4SAndroid Build Coastguard Worker         let want = [0; 4];
397*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(want, got);
398*bb4ee6a4SAndroid Build Coastguard Worker     }
399*bb4ee6a4SAndroid Build Coastguard Worker 
400*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
overwrite()401*bb4ee6a4SAndroid Build Coastguard Worker     fn overwrite() {
402*bb4ee6a4SAndroid Build Coastguard Worker         let f = empty_image();
403*bb4ee6a4SAndroid Build Coastguard Worker         let data = [0];
404*bb4ee6a4SAndroid Build Coastguard Worker         let offset = off((16 * IMAGE_SIZE).try_into().unwrap());
405*bb4ee6a4SAndroid Build Coastguard Worker 
406*bb4ee6a4SAndroid Build Coastguard Worker         // Ensure a write past the pflash device doesn't grow the backing file.
407*bb4ee6a4SAndroid Build Coastguard Worker         let mut pflash = new(f);
408*bb4ee6a4SAndroid Build Coastguard Worker         let old_size = pflash.image.get_len().unwrap();
409*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(old_size, IMAGE_SIZE as u64);
410*bb4ee6a4SAndroid Build Coastguard Worker 
411*bb4ee6a4SAndroid Build Coastguard Worker         pflash.write(offset, &[COMMAND_WRITE_BYTE]);
412*bb4ee6a4SAndroid Build Coastguard Worker         pflash.write(offset, &data);
413*bb4ee6a4SAndroid Build Coastguard Worker 
414*bb4ee6a4SAndroid Build Coastguard Worker         let new_size = pflash.image.get_len().unwrap();
415*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(new_size, IMAGE_SIZE as u64);
416*bb4ee6a4SAndroid Build Coastguard Worker     }
417*bb4ee6a4SAndroid Build Coastguard Worker }
418