xref: /aosp_15_r20/external/crosvm/devices/src/fw_cfg.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2023 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 //! fw_cfg device implementing QEMU's Firmware Configuration interface
6 //! <https://www.qemu.org/docs/master/specs/fw_cfg.html>
7 
8 use std::collections::HashSet;
9 use std::fs;
10 use std::iter::repeat;
11 use std::path::PathBuf;
12 
13 #[cfg(windows)]
14 use base::error;
15 use serde::Deserialize;
16 use serde::Serialize;
17 use serde_keyvalue::FromKeyValues;
18 use thiserror::Error as ThisError;
19 
20 use crate::BusAccessInfo;
21 use crate::BusDevice;
22 use crate::DeviceId;
23 use crate::Suspendable;
24 
25 pub const FW_CFG_BASE_PORT: u64 = 0x510;
26 pub const FW_CFG_WIDTH: u64 = 0x4;
27 // For the 16-bit selector, the 2nd highest-order bit represents whether the data port will be read
28 // or written to. Because this has been deprecrated by Qemu, this bit is useless. The highest order
29 // bit represents whether the selected configuration item is arch-specific. Therefore, only the
30 // lower 14 bits are used for indexing and we mask the two highest bits off with
31 // FW_CFG_SELECTOR_SELECT_MASK. 16384 = 2^14.
32 pub const FW_CFG_MAX_FILE_SLOTS: usize = 16384 - FW_CFG_FILE_FIRST;
33 const FW_CFG_FILE_FIRST: usize = 0x0020;
34 const FW_CFG_SELECTOR_PORT_OFFSET: u64 = 0x0;
35 const FW_CFG_DATA_PORT_OFFSET: u64 = 0x1;
36 const FW_CFG_SELECTOR_RW_MASK: u16 = 0x2000;
37 const FW_CFG_SELECTOR_ARCH_MASK: u16 = 0x4000;
38 const FW_CFG_SELECTOR_SELECT_MASK: u16 = 0xbfff;
39 const FW_CFG_SIGNATURE: [u8; 4] = [b'Q', b'E', b'M', b'U'];
40 const FW_CFG_REVISION: [u8; 4] = [0, 0, 0, 1];
41 const FW_CFG_SIGNATURE_SELECTOR: u16 = 0x0000;
42 const FW_CFG_REVISION_SELECTOR: u16 = 0x0001;
43 const FW_CFG_FILE_DIR_SELECTOR: u16 = 0x0019;
44 // Code that uses fw_cfg expects to read a char[56] for filenames
45 const FW_CFG_FILENAME_SIZE: usize = 56;
46 
47 #[derive(ThisError, Debug)]
48 pub enum Error {
49     #[error("Ran out of file slots")]
50     InsufficientFileSlots,
51 
52     #[error("File already exists")]
53     FileAlreadyExists,
54 
55     #[error("Data blob's size too large: overflowed u32")]
56     SizeOverflow,
57 
58     #[error("too many entries: oveflows u16 selector")]
59     IndexOverflow,
60 
61     #[error("Filename must be less than 55 characters long")]
62     FileNameTooLong,
63 
64     #[error("Unable to open file {0} for fw_cfg: {1}")]
65     FileOpen(PathBuf, std::io::Error),
66 
67     #[error("fw_cfg parameters must have exactly one of string or path")]
68     StringOrPathRequired,
69 }
70 
71 pub type Result<T> = std::result::Result<T, Error>;
72 
73 #[derive(Clone, Debug, Deserialize, Serialize, FromKeyValues, PartialEq, Eq)]
74 #[serde(deny_unknown_fields, rename_all = "kebab-case")]
75 pub struct FwCfgParameters {
76     pub name: String,
77     pub string: Option<String>,
78     pub path: Option<PathBuf>,
79 }
80 
81 #[derive(PartialEq)]
82 pub enum FwCfgItemType {
83     GenericItem,
84     ArchSpecificItem,
85     FileDir,
86     Signature,
87     RevisionVector,
88 }
89 
90 impl FwCfgItemType {
value(&self) -> usize91     fn value(&self) -> usize {
92         match self {
93             FwCfgItemType::ArchSpecificItem => 1,
94             _ => 0,
95         }
96     }
97 }
98 
99 // Contains metadata about the entries stored in fw_cfg.
100 // FwCfgFile is exposed to the the guest
101 // so that the guest may search for the entry
102 // with the desired filename and obtain its 16-bit-wide select
103 // key to write to the control register
104 struct FwCfgFile {
105     pub size: u32,
106     pub select: u16,
107     pub name: String,
108 }
109 
110 // Contains the actual data. The data is represented as an
111 // array of u8 to conviently pass
112 // a data item byte-by-byte when read() is called
113 // on that item
114 #[derive(Clone)]
115 struct FwCfgEntry {
116     pub allow_write: bool,
117     pub data: Vec<u8>,
118 }
119 
120 // Device exposed to the rest of crosvm. Contains state information in addition to arrays of
121 // FwCfgEntry and FwCfgFile. cur_entry keeps the index of the currently selected entry. cur_offset
122 // keeps the byte offset within cur_entry. Storing cur_offset is neccessary because the data IO port
123 // is only 8 bits wide, so a call to read() will only retrieve one 8 bit chunk of data at a time.
124 // cur_offset allows for a data item larger than 8 bits to be read through multiple calls to read(),
125 // maintaining the position of the current read and incrementing across calls to read().
126 pub struct FwCfgDevice {
127     file_slots: usize,
128     // entries[0] holds generic fw_cfg items in addition to special items (file dir, signature, and
129     // revision vector). entries[1] holds arch-specific items.
130     entries: [Vec<FwCfgEntry>; 2],
131     files: Vec<FwCfgFile>,
132     cur_item_type: FwCfgItemType,
133     cur_entry: u16,
134     cur_offset: usize,
135     file_names: HashSet<String>,
136 }
137 
138 impl FwCfgDevice {
new(file_slots: usize, fw_cfg_parameters: Vec<FwCfgParameters>) -> Result<FwCfgDevice>139     pub fn new(file_slots: usize, fw_cfg_parameters: Vec<FwCfgParameters>) -> Result<FwCfgDevice> {
140         let mut device = FwCfgDevice {
141             file_slots,
142             entries: [
143                 vec![
144                     FwCfgEntry {
145                         allow_write: false,
146                         data: vec![]
147                     };
148                     FW_CFG_FILE_FIRST
149                 ],
150                 Vec::new(),
151             ],
152             files: Vec::new(),
153             cur_item_type: FwCfgItemType::GenericItem,
154             cur_entry: 0,
155             cur_offset: 0,
156             file_names: HashSet::new(),
157         };
158 
159         for param in fw_cfg_parameters {
160             let data = match (&param.string, &param.path) {
161                 (Some(string), None) => string.as_bytes().to_vec(),
162                 (None, Some(path)) => {
163                     fs::read(path).map_err(|e| Error::FileOpen(path.clone(), e))?
164                 }
165                 _ => return Err(Error::StringOrPathRequired),
166             };
167 
168             // The file added from the command line will be a generic item. QEMU does not
169             // give users the option to specify whether the user-specified blob is
170             // arch-specific, so we won't either.
171             device.add_file(&param.name, data, FwCfgItemType::GenericItem)?
172         }
173 
174         device.add_bytes(FW_CFG_SIGNATURE.to_vec(), FwCfgItemType::Signature);
175         device.add_bytes(FW_CFG_REVISION.to_vec(), FwCfgItemType::RevisionVector);
176 
177         Ok(device)
178     }
179 
180     /// Adds a file to the device.
181     ///
182     /// # Arguments
183     ///
184     /// - `filename`: Name of file. Must be valid a Unix-style filename
185     /// - `data`: File data as bytes
add_file( &mut self, filename: &str, data: Vec<u8>, item_type: FwCfgItemType, ) -> Result<()>186     pub fn add_file(
187         &mut self,
188         filename: &str,
189         data: Vec<u8>,
190         item_type: FwCfgItemType,
191     ) -> Result<()> {
192         // Adds a data blob to the device under the name filename. This entails creating an
193         // FwCfgEntry and its associated FwCfgFile and adding them to FwCfgDevice.
194 
195         if self.files.len() >= FW_CFG_MAX_FILE_SLOTS || self.files.len() >= self.file_slots {
196             return Err(Error::InsufficientFileSlots);
197         }
198 
199         if filename.len() > FW_CFG_FILENAME_SIZE - 1 {
200             return Err(Error::FileNameTooLong);
201         }
202 
203         // No need to worry about endianess in this function. We will deal with this in read(). We
204         // are only using FwCfgFile internally.
205         let index = self.entries[item_type.value()].len();
206 
207         if self.file_names.contains(filename) {
208             return Err(Error::FileAlreadyExists);
209         }
210 
211         // Since the size field of an entry is stored as a u32, the largest file that can be stored
212         // in the device is 2^32 - 1 ~ 4GB
213         let size: u32 = data.len().try_into().map_err(|_| Error::SizeOverflow)?;
214 
215         let mut select: u16 = (index).try_into().map_err(|_| Error::IndexOverflow)?;
216 
217         if item_type == FwCfgItemType::ArchSpecificItem {
218             select |= FW_CFG_SELECTOR_ARCH_MASK;
219         }
220 
221         let new_file = FwCfgFile {
222             size,
223             select,
224             name: filename.to_owned(),
225         };
226 
227         self.add_bytes(data, item_type);
228         self.files.push(new_file);
229         self.file_names.insert(filename.to_string());
230         // We need to update the file_dir entry every time we insert a new file.
231         self.update_file_dir_entry();
232 
233         Ok(())
234     }
235 
add_bytes(&mut self, data: Vec<u8>, item_type: FwCfgItemType)236     fn add_bytes(&mut self, data: Vec<u8>, item_type: FwCfgItemType) {
237         // Add a FwCfgEntry to FwCfgDevice's entries array
238 
239         let new_entry = FwCfgEntry {
240             allow_write: false,
241             data,
242         };
243 
244         match item_type {
245             FwCfgItemType::GenericItem | FwCfgItemType::ArchSpecificItem => {
246                 self.entries[item_type.value()].push(new_entry)
247             }
248             FwCfgItemType::FileDir => {
249                 self.entries[item_type.value()][FW_CFG_FILE_DIR_SELECTOR as usize] = new_entry
250             }
251             FwCfgItemType::Signature => {
252                 self.entries[item_type.value()][FW_CFG_SIGNATURE_SELECTOR as usize] = new_entry
253             }
254             FwCfgItemType::RevisionVector => {
255                 self.entries[item_type.value()][FW_CFG_REVISION_SELECTOR as usize] = new_entry
256             }
257         }
258     }
259 
update_file_dir_entry(&mut self)260     fn update_file_dir_entry(&mut self) {
261         let mut raw_file_dir: Vec<u8> = Vec::new();
262         // casting to u32 should not be problematic. insert_file() assures that there can be no
263         // more than 2^14 items in the device.
264         let files_dir_count = self.files.len() as u32;
265         raw_file_dir.extend_from_slice(&files_dir_count.to_be_bytes());
266 
267         for file in &self.files {
268             raw_file_dir.extend_from_slice(&file.size.to_be_bytes());
269             raw_file_dir.extend_from_slice(&file.select.to_be_bytes());
270             // The caller expects a "reserved" field to be present on each FwCfgFile. Since
271             // we always set the field to zero, we don't bother to store it on FwCfgDevice and
272             // return zero unconditionally.
273             raw_file_dir.extend_from_slice(&[0, 0]);
274             raw_file_dir.extend_from_slice(file.name.as_bytes());
275             // Padding for c-style char[]
276             raw_file_dir.extend(repeat(0).take(FW_CFG_FILENAME_SIZE - file.name.as_bytes().len()));
277         }
278 
279         self.add_bytes(raw_file_dir, FwCfgItemType::FileDir);
280     }
281 }
282 
283 // We implement two 8-bit registers: a Selector(Control) Register and a Data Register
284 impl BusDevice for FwCfgDevice {
device_id(&self) -> DeviceId285     fn device_id(&self) -> DeviceId {
286         super::CrosvmDeviceId::FwCfg.into()
287     }
288 
debug_label(&self) -> String289     fn debug_label(&self) -> String {
290         "FwCfg".to_owned()
291     }
292 
293     // Read a byte from the FwCfgDevice. The byte read is based on the current state of the device.
read(&mut self, info: BusAccessInfo, data: &mut [u8])294     fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
295         if data.len() != 1 {
296             return;
297         }
298 
299         // Attemping to read anything other than the data port is a NOP
300         if info.offset == FW_CFG_DATA_PORT_OFFSET {
301             let entries_index = self.cur_entry as usize;
302             // If the caller attempts to read bytes past the current entry, read returns
303             // zero
304             if self.cur_offset
305                 >= self.entries[self.cur_item_type.value()][entries_index]
306                     .data
307                     .len()
308             {
309                 data[0] = 0x00;
310                 return;
311             }
312             data[0] = self.entries[self.cur_item_type.value()][entries_index].data[self.cur_offset];
313             self.cur_offset += 1;
314         }
315     }
316 
317     // Write to the FwCfgDevice. Used to set the select register.
write(&mut self, info: BusAccessInfo, data: &[u8])318     fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
319         // Attempting to write to any port other than the data port is a NOP
320         if info.offset == FW_CFG_SELECTOR_PORT_OFFSET {
321             if data.len() != 2 {
322                 return;
323             }
324 
325             let Ok(selector) = data.try_into().map(u16::from_le_bytes) else {
326                 return;
327             };
328 
329             self.cur_offset = 0;
330 
331             match selector {
332                 FW_CFG_FILE_DIR_SELECTOR => {
333                     self.cur_entry = FW_CFG_FILE_DIR_SELECTOR;
334                 }
335                 FW_CFG_REVISION_SELECTOR => {
336                     self.cur_entry = FW_CFG_REVISION_SELECTOR;
337                 }
338                 FW_CFG_SIGNATURE_SELECTOR => {
339                     self.cur_entry = FW_CFG_SIGNATURE_SELECTOR;
340                 }
341                 _ => {
342                     let entries_index = selector as usize;
343 
344                     // Checks if the 15th bit is set. The bit indicates whether the fw_cfg item
345                     // selected is archetecture specific.
346                     if (FW_CFG_SELECTOR_ARCH_MASK & selector) > 0 {
347                         self.cur_item_type = FwCfgItemType::ArchSpecificItem;
348                     } else {
349                         self.cur_item_type = FwCfgItemType::GenericItem;
350                     }
351 
352                     // Check if the selector key is valid.
353                     if self.entries[self.cur_item_type.value()].len() <= entries_index {
354                         return;
355                     }
356 
357                     // Checks if the 14th bit is set. The bit indicates whether the fw_cfg item
358                     // selected is going to be written to or only read via the data port. Since
359                     // writes to the data port have been deprecated as of Qemu v2.4, we don't
360                     // support them either. This code is only included for clarity.
361                     self.entries[self.cur_item_type.value()][entries_index].allow_write =
362                         (FW_CFG_SELECTOR_RW_MASK & selector) > 0;
363 
364                     // Checks if the 15th bit is set. The bit indicates whether the fw_cfg item
365                     // selected is archetecture specific.
366                     if (FW_CFG_SELECTOR_ARCH_MASK & selector) > 0 {
367                         self.cur_item_type = FwCfgItemType::ArchSpecificItem;
368                     } else {
369                         self.cur_item_type = FwCfgItemType::GenericItem;
370                     }
371 
372                     // Only the lower 14 bits are used for actual indexing. The 14th bit
373                     // determines whether the data item will be written to or only read
374                     // from the data port. The 15th bit determines whether the selected
375                     // configuration item is architecture specific. Therefore, we mask the 14th
376                     // and 15th bit off.
377                     self.cur_entry = selector & FW_CFG_SELECTOR_SELECT_MASK;
378                 }
379             }
380         }
381     }
382 }
383 
384 impl Suspendable for FwCfgDevice {
sleep(&mut self) -> anyhow::Result<()>385     fn sleep(&mut self) -> anyhow::Result<()> {
386         Ok(())
387     }
388 
wake(&mut self) -> anyhow::Result<()>389     fn wake(&mut self) -> anyhow::Result<()> {
390         Ok(())
391     }
392 }
393 
394 #[cfg(test)]
395 mod tests {
396     use serde_keyvalue::*;
397 
398     use super::*;
399     use crate::FW_CFG_BASE_PORT;
400 
401     const MAGIC_BYTE: u8 = 111;
402     const MAGIC_BYTE_ALT: u8 = 222;
403     const FILENAME: &str = "/test/device/crosvmval";
404     const FILENAMES: [&str; 6] = [
405         "test/hello.txt",
406         "user/data/mydata.txt",
407         "bruschetta/user/foo",
408         "valid_unix/me/home/dir/back.txt",
409         "/dev/null",
410         "google/unix/sys/maple.txt",
411     ];
412 
default_params() -> Vec<FwCfgParameters>413     fn default_params() -> Vec<FwCfgParameters> {
414         Vec::new()
415     }
416 
get_contents() -> [Vec<u8>; 6]417     fn get_contents() -> [Vec<u8>; 6] {
418         [
419             b"CROSVM".to_vec(),
420             b"GOOGLE".to_vec(),
421             b"FWCONFIG".to_vec(),
422             b"PIZZA".to_vec(),
423             b"CHROMEOS".to_vec(),
424             b"42".to_vec(),
425         ]
426     }
427 
make_device( filenames: &[&str], contents: &[Vec<u8>], params: &[FwCfgParameters], file_slots: &usize, ) -> Result<FwCfgDevice>428     fn make_device(
429         filenames: &[&str],
430         contents: &[Vec<u8>],
431         params: &[FwCfgParameters],
432         file_slots: &usize,
433     ) -> Result<FwCfgDevice> {
434         let mut device = FwCfgDevice::new(*file_slots, params.to_owned())?;
435         let count = filenames.len();
436 
437         for i in 0..count {
438             device.add_file(
439                 filenames[i],
440                 contents[i].clone(),
441                 FwCfgItemType::GenericItem,
442             )?;
443         }
444 
445         Ok(device)
446     }
447 
from_fw_cfg_arg(options: &str) -> std::result::Result<FwCfgParameters, ParseError>448     fn from_fw_cfg_arg(options: &str) -> std::result::Result<FwCfgParameters, ParseError> {
449         from_key_values(options)
450     }
451 
read_u32(device: &mut FwCfgDevice, bai: BusAccessInfo, data: &mut [u8]) -> u32452     fn read_u32(device: &mut FwCfgDevice, bai: BusAccessInfo, data: &mut [u8]) -> u32 {
453         let mut bytes: [u8; 4] = [0, 0, 0, 0];
454         device.read(bai, data);
455         bytes[0] = data[0];
456         device.read(bai, data);
457         bytes[1] = data[0];
458         device.read(bai, data);
459         bytes[2] = data[0];
460         device.read(bai, data);
461         bytes[3] = data[0];
462 
463         u32::from_be_bytes(bytes)
464     }
465 
read_u16(device: &mut FwCfgDevice, bai: BusAccessInfo, data: &mut [u8]) -> u16466     fn read_u16(device: &mut FwCfgDevice, bai: BusAccessInfo, data: &mut [u8]) -> u16 {
467         let mut bytes: [u8; 2] = [0, 0];
468         device.read(bai, data);
469         bytes[0] = data[0];
470         device.read(bai, data);
471         bytes[1] = data[0];
472 
473         u16::from_be_bytes(bytes)
474     }
475 
read_u8(device: &mut FwCfgDevice, bai: BusAccessInfo, data: &mut [u8]) -> u8476     fn read_u8(device: &mut FwCfgDevice, bai: BusAccessInfo, data: &mut [u8]) -> u8 {
477         let mut bytes: [u8; 1] = [0];
478         device.read(bai, data);
479         bytes[0] = data[0];
480         u8::from_be_bytes(bytes)
481     }
482 
read_char_56(device: &mut FwCfgDevice, bai: BusAccessInfo, data: &mut [u8]) -> String483     fn read_char_56(device: &mut FwCfgDevice, bai: BusAccessInfo, data: &mut [u8]) -> String {
484         let mut c: char = read_u8(device, bai, data) as char;
485         let mut count = 1; //start at 1 b/c called read_u8 above
486         let mut name: String = String::new();
487         while c != '\0' {
488             name.push(c);
489             c = read_u8(device, bai, data) as char;
490             count += 1;
491         }
492         while count < FW_CFG_FILENAME_SIZE {
493             read_u8(device, bai, data);
494             count += 1;
495         }
496         name
497     }
498 
get_entry( device: &mut FwCfgDevice, mut bai: BusAccessInfo, size: usize, selector: u16, ) -> Vec<u8>499     fn get_entry(
500         device: &mut FwCfgDevice,
501         mut bai: BusAccessInfo,
502         size: usize,
503         selector: u16,
504     ) -> Vec<u8> {
505         let mut data: Vec<u8> = vec![0];
506         let mut blob: Vec<u8> = Vec::new();
507 
508         bai.address = FW_CFG_BASE_PORT;
509         bai.offset = FW_CFG_SELECTOR_PORT_OFFSET;
510         let selector: [u8; 2] = selector.to_le_bytes();
511         device.write(bai, &selector);
512 
513         bai.offset = FW_CFG_DATA_PORT_OFFSET;
514 
515         for _i in 0..size {
516             read_u8(device, bai, &mut data[..]);
517             blob.push(data[0]);
518         }
519 
520         blob
521     }
522 
assert_read_entries(filenames: &[&str], device: &mut FwCfgDevice, bai: BusAccessInfo)523     fn assert_read_entries(filenames: &[&str], device: &mut FwCfgDevice, bai: BusAccessInfo) {
524         let data_len = device.entries[0][0 + FW_CFG_FILE_FIRST].data.len();
525         assert_eq!(
526             get_entry(device, bai, data_len, (0 + FW_CFG_FILE_FIRST) as u16),
527             device.entries[0][0 + FW_CFG_FILE_FIRST].data
528         );
529 
530         for i in (FW_CFG_FILE_FIRST + 1)..filenames.len() {
531             let data_len = device.entries[0][i].data.len();
532             assert_eq!(
533                 get_entry(device, bai, data_len, (i + FW_CFG_FILE_FIRST) as u16),
534                 device.entries[0][i].data
535             );
536         }
537     }
538 
assert_read_file_dir( filenames: &[&str], contents: &[Vec<u8>], device: &mut FwCfgDevice, bai: BusAccessInfo, )539     fn assert_read_file_dir(
540         filenames: &[&str],
541         contents: &[Vec<u8>],
542         device: &mut FwCfgDevice,
543         bai: BusAccessInfo,
544     ) {
545         let mut data: Vec<u8> = vec![0];
546         let file_count = read_u32(device, bai, &mut data[..]);
547         assert_eq!(file_count, filenames.len() as u32);
548 
549         for i in 0..filenames.len() {
550             let file_size = read_u32(device, bai, &mut data[..]);
551             assert_eq!(file_size, contents[i].len() as u32);
552 
553             let file_select = read_u16(device, bai, &mut data[..]);
554             assert_eq!(file_select - (FW_CFG_FILE_FIRST as u16), i as u16);
555 
556             let file_reserved = read_u16(device, bai, &mut data[..]);
557             assert_eq!(file_reserved, 0);
558 
559             let file_name = read_char_56(device, bai, &mut data[..]);
560             assert_eq!(file_name, FILENAMES[i]);
561         }
562     }
setup_read( filenames: &[&str], contents: &[Vec<u8>], selector: u16, ) -> (FwCfgDevice, BusAccessInfo)563     fn setup_read(
564         filenames: &[&str],
565         contents: &[Vec<u8>],
566         selector: u16,
567     ) -> (FwCfgDevice, BusAccessInfo) {
568         let mut device = make_device(
569             filenames,
570             contents,
571             &default_params(),
572             &(filenames.len() + 5),
573         )
574         .unwrap();
575         let mut bai = BusAccessInfo {
576             offset: FW_CFG_SELECTOR_PORT_OFFSET,
577             address: FW_CFG_BASE_PORT,
578             id: 0,
579         };
580         let selector: [u8; 2] = selector.to_le_bytes();
581         device.write(bai, &selector);
582         bai.offset = FW_CFG_DATA_PORT_OFFSET;
583 
584         (device, bai)
585     }
586 
587     #[test]
588     // Attempt to build FwCfgParams from key value pairs
params_from_key_values()589     fn params_from_key_values() {
590         from_fw_cfg_arg("").expect_err("parsing empty string should fail");
591 
592         let params = from_fw_cfg_arg("name=foo,path=/path/to/input").unwrap();
593         assert_eq!(
594             params,
595             FwCfgParameters {
596                 name: "foo".into(),
597                 path: Some("/path/to/input".into()),
598                 string: None,
599             }
600         );
601 
602         let params = from_fw_cfg_arg("name=bar,string=testdata").unwrap();
603         assert_eq!(
604             params,
605             FwCfgParameters {
606                 name: "bar".into(),
607                 string: Some("testdata".into()),
608                 path: None,
609             }
610         );
611     }
612 
613     #[test]
614     // Try to cause underflow by using a selector less than FW_CFG_FILE_FIRST but not one of the
615     // special selectors
attempt_underflow_read()616     fn attempt_underflow_read() {
617         let (_device, _bai) = setup_read(
618             &FILENAMES,
619             &get_contents(),
620             (FW_CFG_FILE_FIRST - 0x05) as u16,
621         );
622     }
623 
624     #[test]
625     // Write a simple one byte file and confirm that an entry is properly created
write_one_byte_file()626     fn write_one_byte_file() {
627         let mut fw_cfg = FwCfgDevice::new(100, default_params()).unwrap();
628         let data = vec![MAGIC_BYTE];
629         fw_cfg
630             .add_file(FILENAME, data, FwCfgItemType::GenericItem)
631             .expect("File insert failed");
632         let ind = fw_cfg.entries[0].len();
633         assert_eq!(
634             ind,
635             FW_CFG_FILE_FIRST + 1,
636             "Insertion into fw_cfg failed: Index is wrong. expected {}, got {},
637                  ",
638             FW_CFG_FILE_FIRST + 1,
639             ind
640         );
641         assert_eq!(
642             fw_cfg.entries[0][ind - 1].data,
643             vec![MAGIC_BYTE],
644             "Insertion failed: unexpected fw_cfg entry values"
645         );
646     }
647 
648     #[test]
649     // Write a simple four byte file and confirm that an entry is properly created
write_four_byte_file()650     fn write_four_byte_file() {
651         let mut fw_cfg = FwCfgDevice::new(100, default_params()).unwrap();
652         let data = vec![MAGIC_BYTE, MAGIC_BYTE_ALT, MAGIC_BYTE, MAGIC_BYTE_ALT];
653         fw_cfg
654             .add_file(FILENAME, data, FwCfgItemType::GenericItem)
655             .expect("File insert failed");
656         let ind = fw_cfg.entries[0].len();
657         assert_eq!(
658             ind,
659             FW_CFG_FILE_FIRST + 1,
660             "Insertion into fw_cfg failed: Index is wrong. expected {}, got {}",
661             FW_CFG_FILE_FIRST + 1,
662             ind
663         );
664         assert_eq!(
665             fw_cfg.entries[0][ind - 1].data,
666             vec![MAGIC_BYTE, MAGIC_BYTE_ALT, MAGIC_BYTE, MAGIC_BYTE_ALT],
667             "Insertion failed: unexpected fw_cfg entry values"
668         );
669     }
670 
671     #[test]
672     #[should_panic]
673     // Attempt to add a file to an fw_cfg device w/ no fileslots and assert that nothing gets
674     // inserted
write_file_one_slot_expect_nop()675     fn write_file_one_slot_expect_nop() {
676         let mut fw_cfg = FwCfgDevice::new(0, default_params()).unwrap();
677         let data = vec![MAGIC_BYTE];
678         fw_cfg
679             .add_file(FILENAME, data, FwCfgItemType::GenericItem)
680             .expect("File insert failed");
681     }
682 
683     #[test]
684     #[should_panic]
685     // Attempt to add two files to an fw_cfg w/ only one fileslot and assert only first insert
686     // succeeds.
write_two_files_no_slots_expect_nop_on_second()687     fn write_two_files_no_slots_expect_nop_on_second() {
688         let mut fw_cfg = FwCfgDevice::new(1, default_params()).unwrap();
689         let data = vec![MAGIC_BYTE];
690         let data2 = vec![MAGIC_BYTE_ALT];
691         fw_cfg
692             .add_file(FILENAME, data, FwCfgItemType::GenericItem)
693             .expect("File insert failed");
694         assert_eq!(
695             fw_cfg.entries[0].len(),
696             1,
697             "Insertion into fw_cfg failed: Expected {} elements, got {}",
698             1,
699             fw_cfg.entries[0].len()
700         );
701         fw_cfg
702             .add_file(FILENAME, data2, FwCfgItemType::GenericItem)
703             .expect("File insert failed");
704     }
705 
706     #[test]
707     // Attempt to read a FwCfgDevice's signature
read_fw_cfg_signature()708     fn read_fw_cfg_signature() {
709         let mut data: Vec<u8> = vec![0];
710         let (mut device, bai) = setup_read(&FILENAMES, &get_contents(), FW_CFG_SIGNATURE_SELECTOR);
711         // To logically compare the revison vector to FW_CFG_REVISION byte-by-byte, we must use
712         // to_be_bytes() since we are comparing byte arrays, not integers.
713         let signature = read_u32(&mut device, bai, &mut data[..]).to_be_bytes();
714         assert_eq!(signature, FW_CFG_SIGNATURE);
715     }
716 
717     #[test]
718     // Attempt to read a FwCfgDevice's revision bit vector
read_fw_cfg_revision()719     fn read_fw_cfg_revision() {
720         let mut data: Vec<u8> = vec![0];
721         let (mut device, bai) = setup_read(&FILENAMES, &get_contents(), FW_CFG_REVISION_SELECTOR);
722         // To logically compare the revison vector to FW_CFG_REVISION byte-by-byte, we must use
723         // to_be_bytes() since we are comparing byte arrays, not integers.
724         let revision = read_u32(&mut device, bai, &mut data[..]).to_be_bytes();
725         assert_eq!(revision, FW_CFG_REVISION);
726     }
727 
728     #[test]
729     // Attempt to read a FwCfgDevice's file directory
read_file_dir()730     fn read_file_dir() {
731         let contents = get_contents();
732         let (mut device, bai) = setup_read(&FILENAMES, &contents, FW_CFG_FILE_DIR_SELECTOR);
733         assert_read_file_dir(&FILENAMES, &contents, &mut device, bai);
734     }
735 
736     #[test]
737     // Attempt to read all of a FwCfgDevice's entries
read_fw_cfg_entries()738     fn read_fw_cfg_entries() {
739         let contents = get_contents();
740         let (mut device, bai) = setup_read(&FILENAMES, &contents, (0 + FW_CFG_FILE_FIRST) as u16);
741 
742         assert_read_entries(&FILENAMES, &mut device, bai);
743     }
744 
745     #[test]
746     // Attempt to read revision, file dir, and entries in random order to make
747     // sure that proper state is maintained.
read_whole_device()748     fn read_whole_device() {
749         let contents = get_contents();
750         let mut data: Vec<u8> = vec![0];
751 
752         let (mut device, mut bai) = setup_read(&FILENAMES, &contents, FW_CFG_REVISION_SELECTOR);
753 
754         let revision = read_u32(&mut device, bai, &mut data[..]).to_be_bytes();
755         assert_eq!(revision, FW_CFG_REVISION);
756 
757         let i = FILENAMES.len() - 1;
758         let data_len = device.entries[0][i].data.len();
759         assert_eq!(
760             get_entry(&mut device, bai, data_len, (i + FW_CFG_FILE_FIRST) as u16),
761             device.entries[0][i].data
762         );
763 
764         bai.address = FW_CFG_BASE_PORT;
765         bai.offset = FW_CFG_SELECTOR_PORT_OFFSET;
766         device.write(bai, &FW_CFG_FILE_DIR_SELECTOR.to_le_bytes());
767         bai.offset = FW_CFG_DATA_PORT_OFFSET;
768 
769         assert_read_file_dir(&FILENAMES, &contents, &mut device, bai);
770 
771         let data_len = device.entries[0][FW_CFG_FILE_FIRST + 0].data.len();
772         assert_eq!(
773             get_entry(&mut device, bai, data_len, (0 + FW_CFG_FILE_FIRST) as u16),
774             device.entries[0][FW_CFG_FILE_FIRST + 0].data
775         );
776     }
777 
778     #[test]
779     // Assert that the device maintains proper state after reads of random length.
read_incorrect_bytes()780     fn read_incorrect_bytes() {
781         let contents = get_contents();
782         let mut data: Vec<u8> = vec![0];
783         let (mut device, mut bai) =
784             setup_read(&FILENAMES, &contents, (0 + FW_CFG_FILE_FIRST) as u16);
785 
786         for _i in 1..1000 {
787             let _random_bytes = read_u32(&mut device, bai, &mut data[..]);
788         }
789 
790         bai.address = FW_CFG_BASE_PORT;
791         device.write(bai, &FW_CFG_FILE_DIR_SELECTOR.to_le_bytes());
792         bai.offset = FW_CFG_DATA_PORT_OFFSET;
793 
794         for _i in 1..10000 {
795             let mut data: Vec<u8> = vec![0];
796             let _random_bytes = read_u32(&mut device, bai, &mut data[..]);
797         }
798 
799         bai.address = FW_CFG_BASE_PORT;
800         bai.offset = FW_CFG_SELECTOR_PORT_OFFSET;
801         device.write(bai, &FW_CFG_FILE_DIR_SELECTOR.to_le_bytes());
802         bai.offset = FW_CFG_DATA_PORT_OFFSET;
803 
804         assert_read_file_dir(&FILENAMES, &contents, &mut device, bai);
805 
806         let i = FILENAMES.len() - 1;
807 
808         bai.address = FW_CFG_BASE_PORT;
809         bai.offset = FW_CFG_SELECTOR_PORT_OFFSET;
810         device.write(bai, &(FW_CFG_FILE_FIRST + i).to_le_bytes());
811         bai.offset = FW_CFG_DATA_PORT_OFFSET;
812 
813         for _i in 1..1000 {
814             let _random_bytes = read_u32(&mut device, bai, &mut data[..]);
815         }
816 
817         assert_read_entries(&FILENAMES, &mut device, bai);
818     }
819 }
820