devices/
fw_cfg.rs

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
8use std::collections::HashSet;
9use std::fs;
10use std::path::PathBuf;
11
12#[cfg(windows)]
13use base::error;
14use serde::Deserialize;
15use serde::Serialize;
16use serde_keyvalue::FromKeyValues;
17use thiserror::Error as ThisError;
18use vm_control::DeviceId;
19use vm_control::PlatformDeviceId;
20
21use crate::BusAccessInfo;
22use crate::BusDevice;
23use crate::Suspendable;
24
25pub const FW_CFG_BASE_PORT: u64 = 0x510;
26pub 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.
32pub const FW_CFG_MAX_FILE_SLOTS: usize = 16384 - FW_CFG_FILE_FIRST;
33const FW_CFG_FILE_FIRST: usize = 0x0020;
34const FW_CFG_SELECTOR_PORT_OFFSET: u64 = 0x0;
35const FW_CFG_DATA_PORT_OFFSET: u64 = 0x1;
36const FW_CFG_SELECTOR_RW_MASK: u16 = 0x2000;
37const FW_CFG_SELECTOR_ARCH_MASK: u16 = 0x4000;
38const FW_CFG_SELECTOR_SELECT_MASK: u16 = 0xbfff;
39const FW_CFG_SIGNATURE: [u8; 4] = [b'Q', b'E', b'M', b'U'];
40const FW_CFG_REVISION: [u8; 4] = [0, 0, 0, 1];
41const FW_CFG_SIGNATURE_SELECTOR: u16 = 0x0000;
42const FW_CFG_REVISION_SELECTOR: u16 = 0x0001;
43const FW_CFG_FILE_DIR_SELECTOR: u16 = 0x0019;
44// Code that uses fw_cfg expects to read a char[56] for filenames
45const FW_CFG_FILENAME_SIZE: usize = 56;
46
47#[derive(ThisError, Debug)]
48pub 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
71pub 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")]
75pub struct FwCfgParameters {
76    pub name: String,
77    pub string: Option<String>,
78    pub path: Option<PathBuf>,
79}
80
81#[derive(PartialEq)]
82pub enum FwCfgItemType {
83    GenericItem,
84    ArchSpecificItem,
85    FileDir,
86    Signature,
87    RevisionVector,
88}
89
90impl FwCfgItemType {
91    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
104struct 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)]
115struct 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().
126pub 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
138impl 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
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
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
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(std::iter::repeat_n(
277                0,
278                FW_CFG_FILENAME_SIZE - file.name.len(),
279            ));
280        }
281
282        self.add_bytes(raw_file_dir, FwCfgItemType::FileDir);
283    }
284}
285
286// We implement two 8-bit registers: a Selector(Control) Register and a Data Register
287impl BusDevice for FwCfgDevice {
288    fn device_id(&self) -> DeviceId {
289        PlatformDeviceId::FwCfg.into()
290    }
291
292    fn debug_label(&self) -> String {
293        "FwCfg".to_owned()
294    }
295
296    // Read a byte from the FwCfgDevice. The byte read is based on the current state of the device.
297    fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
298        if data.len() != 1 {
299            return;
300        }
301
302        // Attemping to read anything other than the data port is a NOP
303        if info.offset == FW_CFG_DATA_PORT_OFFSET {
304            let entries_index = self.cur_entry as usize;
305            // If the caller attempts to read bytes past the current entry, read returns
306            // zero
307            if self.cur_offset
308                >= self.entries[self.cur_item_type.value()][entries_index]
309                    .data
310                    .len()
311            {
312                data[0] = 0x00;
313                return;
314            }
315            data[0] = self.entries[self.cur_item_type.value()][entries_index].data[self.cur_offset];
316            self.cur_offset += 1;
317        }
318    }
319
320    // Write to the FwCfgDevice. Used to set the select register.
321    fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
322        // Attempting to write to any port other than the data port is a NOP
323        if info.offset == FW_CFG_SELECTOR_PORT_OFFSET {
324            if data.len() != 2 {
325                return;
326            }
327
328            let Ok(selector) = data.try_into().map(u16::from_le_bytes) else {
329                return;
330            };
331
332            self.cur_offset = 0;
333
334            match selector {
335                FW_CFG_FILE_DIR_SELECTOR => {
336                    self.cur_entry = FW_CFG_FILE_DIR_SELECTOR;
337                }
338                FW_CFG_REVISION_SELECTOR => {
339                    self.cur_entry = FW_CFG_REVISION_SELECTOR;
340                }
341                FW_CFG_SIGNATURE_SELECTOR => {
342                    self.cur_entry = FW_CFG_SIGNATURE_SELECTOR;
343                }
344                _ => {
345                    let entries_index = selector as usize;
346
347                    // Checks if the 15th bit is set. The bit indicates whether the fw_cfg item
348                    // selected is archetecture specific.
349                    if (FW_CFG_SELECTOR_ARCH_MASK & selector) > 0 {
350                        self.cur_item_type = FwCfgItemType::ArchSpecificItem;
351                    } else {
352                        self.cur_item_type = FwCfgItemType::GenericItem;
353                    }
354
355                    // Check if the selector key is valid.
356                    if self.entries[self.cur_item_type.value()].len() <= entries_index {
357                        return;
358                    }
359
360                    // Checks if the 14th bit is set. The bit indicates whether the fw_cfg item
361                    // selected is going to be written to or only read via the data port. Since
362                    // writes to the data port have been deprecated as of Qemu v2.4, we don't
363                    // support them either. This code is only included for clarity.
364                    self.entries[self.cur_item_type.value()][entries_index].allow_write =
365                        (FW_CFG_SELECTOR_RW_MASK & selector) > 0;
366
367                    // Checks if the 15th bit is set. The bit indicates whether the fw_cfg item
368                    // selected is archetecture specific.
369                    if (FW_CFG_SELECTOR_ARCH_MASK & selector) > 0 {
370                        self.cur_item_type = FwCfgItemType::ArchSpecificItem;
371                    } else {
372                        self.cur_item_type = FwCfgItemType::GenericItem;
373                    }
374
375                    // Only the lower 14 bits are used for actual indexing. The 14th bit
376                    // determines whether the data item will be written to or only read
377                    // from the data port. The 15th bit determines whether the selected
378                    // configuration item is architecture specific. Therefore, we mask the 14th
379                    // and 15th bit off.
380                    self.cur_entry = selector & FW_CFG_SELECTOR_SELECT_MASK;
381                }
382            }
383        }
384    }
385}
386
387impl Suspendable for FwCfgDevice {
388    fn sleep(&mut self) -> anyhow::Result<()> {
389        Ok(())
390    }
391
392    fn wake(&mut self) -> anyhow::Result<()> {
393        Ok(())
394    }
395}
396
397#[cfg(test)]
398mod tests {
399    use serde_keyvalue::*;
400
401    use super::*;
402    use crate::FW_CFG_BASE_PORT;
403
404    const MAGIC_BYTE: u8 = 111;
405    const MAGIC_BYTE_ALT: u8 = 222;
406    const FILENAME: &str = "/test/device/crosvmval";
407    const FILENAMES: [&str; 6] = [
408        "test/hello.txt",
409        "user/data/mydata.txt",
410        "bruschetta/user/foo",
411        "valid_unix/me/home/dir/back.txt",
412        "/dev/null",
413        "google/unix/sys/maple.txt",
414    ];
415
416    fn default_params() -> Vec<FwCfgParameters> {
417        Vec::new()
418    }
419
420    fn get_contents() -> [Vec<u8>; 6] {
421        [
422            b"CROSVM".to_vec(),
423            b"GOOGLE".to_vec(),
424            b"FWCONFIG".to_vec(),
425            b"PIZZA".to_vec(),
426            b"CHROMEOS".to_vec(),
427            b"42".to_vec(),
428        ]
429    }
430
431    fn make_device(
432        filenames: &[&str],
433        contents: &[Vec<u8>],
434        params: &[FwCfgParameters],
435        file_slots: &usize,
436    ) -> Result<FwCfgDevice> {
437        let mut device = FwCfgDevice::new(*file_slots, params.to_owned())?;
438        let count = filenames.len();
439
440        for i in 0..count {
441            device.add_file(
442                filenames[i],
443                contents[i].clone(),
444                FwCfgItemType::GenericItem,
445            )?;
446        }
447
448        Ok(device)
449    }
450
451    fn from_fw_cfg_arg(options: &str) -> std::result::Result<FwCfgParameters, ParseError> {
452        from_key_values(options)
453    }
454
455    fn read_u32(device: &mut FwCfgDevice, bai: BusAccessInfo, data: &mut [u8]) -> u32 {
456        let mut bytes: [u8; 4] = [0, 0, 0, 0];
457        device.read(bai, data);
458        bytes[0] = data[0];
459        device.read(bai, data);
460        bytes[1] = data[0];
461        device.read(bai, data);
462        bytes[2] = data[0];
463        device.read(bai, data);
464        bytes[3] = data[0];
465
466        u32::from_be_bytes(bytes)
467    }
468
469    fn read_u16(device: &mut FwCfgDevice, bai: BusAccessInfo, data: &mut [u8]) -> u16 {
470        let mut bytes: [u8; 2] = [0, 0];
471        device.read(bai, data);
472        bytes[0] = data[0];
473        device.read(bai, data);
474        bytes[1] = data[0];
475
476        u16::from_be_bytes(bytes)
477    }
478
479    fn read_u8(device: &mut FwCfgDevice, bai: BusAccessInfo, data: &mut [u8]) -> u8 {
480        let mut bytes: [u8; 1] = [0];
481        device.read(bai, data);
482        bytes[0] = data[0];
483        u8::from_be_bytes(bytes)
484    }
485
486    fn read_char_56(device: &mut FwCfgDevice, bai: BusAccessInfo, data: &mut [u8]) -> String {
487        let mut c: char = read_u8(device, bai, data) as char;
488        let mut count = 1; //start at 1 b/c called read_u8 above
489        let mut name: String = String::new();
490        while c != '\0' {
491            name.push(c);
492            c = read_u8(device, bai, data) as char;
493            count += 1;
494        }
495        while count < FW_CFG_FILENAME_SIZE {
496            read_u8(device, bai, data);
497            count += 1;
498        }
499        name
500    }
501
502    fn get_entry(
503        device: &mut FwCfgDevice,
504        mut bai: BusAccessInfo,
505        size: usize,
506        selector: u16,
507    ) -> Vec<u8> {
508        let mut data: Vec<u8> = vec![0];
509        let mut blob: Vec<u8> = Vec::new();
510
511        bai.address = FW_CFG_BASE_PORT;
512        bai.offset = FW_CFG_SELECTOR_PORT_OFFSET;
513        let selector: [u8; 2] = selector.to_le_bytes();
514        device.write(bai, &selector);
515
516        bai.offset = FW_CFG_DATA_PORT_OFFSET;
517
518        for _i in 0..size {
519            read_u8(device, bai, &mut data[..]);
520            blob.push(data[0]);
521        }
522
523        blob
524    }
525
526    fn assert_read_entries(filenames: &[&str], device: &mut FwCfgDevice, bai: BusAccessInfo) {
527        let data_len = device.entries[0][0 + FW_CFG_FILE_FIRST].data.len();
528        assert_eq!(
529            get_entry(device, bai, data_len, (0 + FW_CFG_FILE_FIRST) as u16),
530            device.entries[0][0 + FW_CFG_FILE_FIRST].data
531        );
532
533        for i in (FW_CFG_FILE_FIRST + 1)..filenames.len() {
534            let data_len = device.entries[0][i].data.len();
535            assert_eq!(
536                get_entry(device, bai, data_len, (i + FW_CFG_FILE_FIRST) as u16),
537                device.entries[0][i].data
538            );
539        }
540    }
541
542    fn assert_read_file_dir(
543        filenames: &[&str],
544        contents: &[Vec<u8>],
545        device: &mut FwCfgDevice,
546        bai: BusAccessInfo,
547    ) {
548        let mut data: Vec<u8> = vec![0];
549        let file_count = read_u32(device, bai, &mut data[..]);
550        assert_eq!(file_count, filenames.len() as u32);
551
552        for i in 0..filenames.len() {
553            let file_size = read_u32(device, bai, &mut data[..]);
554            assert_eq!(file_size, contents[i].len() as u32);
555
556            let file_select = read_u16(device, bai, &mut data[..]);
557            assert_eq!(file_select - (FW_CFG_FILE_FIRST as u16), i as u16);
558
559            let file_reserved = read_u16(device, bai, &mut data[..]);
560            assert_eq!(file_reserved, 0);
561
562            let file_name = read_char_56(device, bai, &mut data[..]);
563            assert_eq!(file_name, FILENAMES[i]);
564        }
565    }
566    fn setup_read(
567        filenames: &[&str],
568        contents: &[Vec<u8>],
569        selector: u16,
570    ) -> (FwCfgDevice, BusAccessInfo) {
571        let mut device = make_device(
572            filenames,
573            contents,
574            &default_params(),
575            &(filenames.len() + 5),
576        )
577        .unwrap();
578        let mut bai = BusAccessInfo {
579            offset: FW_CFG_SELECTOR_PORT_OFFSET,
580            address: FW_CFG_BASE_PORT,
581            id: 0,
582        };
583        let selector: [u8; 2] = selector.to_le_bytes();
584        device.write(bai, &selector);
585        bai.offset = FW_CFG_DATA_PORT_OFFSET;
586
587        (device, bai)
588    }
589
590    #[test]
591    // Attempt to build FwCfgParams from key value pairs
592    fn params_from_key_values() {
593        from_fw_cfg_arg("").expect_err("parsing empty string should fail");
594
595        let params = from_fw_cfg_arg("name=foo,path=/path/to/input").unwrap();
596        assert_eq!(
597            params,
598            FwCfgParameters {
599                name: "foo".into(),
600                path: Some("/path/to/input".into()),
601                string: None,
602            }
603        );
604
605        let params = from_fw_cfg_arg("name=bar,string=testdata").unwrap();
606        assert_eq!(
607            params,
608            FwCfgParameters {
609                name: "bar".into(),
610                string: Some("testdata".into()),
611                path: None,
612            }
613        );
614    }
615
616    #[test]
617    // Try to cause underflow by using a selector less than FW_CFG_FILE_FIRST but not one of the
618    // special selectors
619    fn attempt_underflow_read() {
620        let (_device, _bai) = setup_read(
621            &FILENAMES,
622            &get_contents(),
623            (FW_CFG_FILE_FIRST - 0x05) as u16,
624        );
625    }
626
627    #[test]
628    // Write a simple one byte file and confirm that an entry is properly created
629    fn write_one_byte_file() {
630        let mut fw_cfg = FwCfgDevice::new(100, default_params()).unwrap();
631        let data = vec![MAGIC_BYTE];
632        fw_cfg
633            .add_file(FILENAME, data, FwCfgItemType::GenericItem)
634            .expect("File insert failed");
635        let ind = fw_cfg.entries[0].len();
636        assert_eq!(
637            ind,
638            FW_CFG_FILE_FIRST + 1,
639            "Insertion into fw_cfg failed: Index is wrong. expected {}, got {},
640                 ",
641            FW_CFG_FILE_FIRST + 1,
642            ind
643        );
644        assert_eq!(
645            fw_cfg.entries[0][ind - 1].data,
646            vec![MAGIC_BYTE],
647            "Insertion failed: unexpected fw_cfg entry values"
648        );
649    }
650
651    #[test]
652    // Write a simple four byte file and confirm that an entry is properly created
653    fn write_four_byte_file() {
654        let mut fw_cfg = FwCfgDevice::new(100, default_params()).unwrap();
655        let data = vec![MAGIC_BYTE, MAGIC_BYTE_ALT, MAGIC_BYTE, MAGIC_BYTE_ALT];
656        fw_cfg
657            .add_file(FILENAME, data, FwCfgItemType::GenericItem)
658            .expect("File insert failed");
659        let ind = fw_cfg.entries[0].len();
660        assert_eq!(
661            ind,
662            FW_CFG_FILE_FIRST + 1,
663            "Insertion into fw_cfg failed: Index is wrong. expected {}, got {}",
664            FW_CFG_FILE_FIRST + 1,
665            ind
666        );
667        assert_eq!(
668            fw_cfg.entries[0][ind - 1].data,
669            vec![MAGIC_BYTE, MAGIC_BYTE_ALT, MAGIC_BYTE, MAGIC_BYTE_ALT],
670            "Insertion failed: unexpected fw_cfg entry values"
671        );
672    }
673
674    #[test]
675    #[should_panic]
676    // Attempt to add a file to an fw_cfg device w/ no fileslots and assert that nothing gets
677    // inserted
678    fn write_file_one_slot_expect_nop() {
679        let mut fw_cfg = FwCfgDevice::new(0, default_params()).unwrap();
680        let data = vec![MAGIC_BYTE];
681        fw_cfg
682            .add_file(FILENAME, data, FwCfgItemType::GenericItem)
683            .expect("File insert failed");
684    }
685
686    #[test]
687    #[should_panic]
688    // Attempt to add two files to an fw_cfg w/ only one fileslot and assert only first insert
689    // succeeds.
690    fn write_two_files_no_slots_expect_nop_on_second() {
691        let mut fw_cfg = FwCfgDevice::new(1, default_params()).unwrap();
692        let data = vec![MAGIC_BYTE];
693        let data2 = vec![MAGIC_BYTE_ALT];
694        fw_cfg
695            .add_file(FILENAME, data, FwCfgItemType::GenericItem)
696            .expect("File insert failed");
697        assert_eq!(
698            fw_cfg.entries[0].len(),
699            1,
700            "Insertion into fw_cfg failed: Expected {} elements, got {}",
701            1,
702            fw_cfg.entries[0].len()
703        );
704        fw_cfg
705            .add_file(FILENAME, data2, FwCfgItemType::GenericItem)
706            .expect("File insert failed");
707    }
708
709    #[test]
710    // Attempt to read a FwCfgDevice's signature
711    fn read_fw_cfg_signature() {
712        let mut data: Vec<u8> = vec![0];
713        let (mut device, bai) = setup_read(&FILENAMES, &get_contents(), FW_CFG_SIGNATURE_SELECTOR);
714        // To logically compare the revison vector to FW_CFG_REVISION byte-by-byte, we must use
715        // to_be_bytes() since we are comparing byte arrays, not integers.
716        let signature = read_u32(&mut device, bai, &mut data[..]).to_be_bytes();
717        assert_eq!(signature, FW_CFG_SIGNATURE);
718    }
719
720    #[test]
721    // Attempt to read a FwCfgDevice's revision bit vector
722    fn read_fw_cfg_revision() {
723        let mut data: Vec<u8> = vec![0];
724        let (mut device, bai) = setup_read(&FILENAMES, &get_contents(), FW_CFG_REVISION_SELECTOR);
725        // To logically compare the revison vector to FW_CFG_REVISION byte-by-byte, we must use
726        // to_be_bytes() since we are comparing byte arrays, not integers.
727        let revision = read_u32(&mut device, bai, &mut data[..]).to_be_bytes();
728        assert_eq!(revision, FW_CFG_REVISION);
729    }
730
731    #[test]
732    // Attempt to read a FwCfgDevice's file directory
733    fn read_file_dir() {
734        let contents = get_contents();
735        let (mut device, bai) = setup_read(&FILENAMES, &contents, FW_CFG_FILE_DIR_SELECTOR);
736        assert_read_file_dir(&FILENAMES, &contents, &mut device, bai);
737    }
738
739    #[test]
740    // Attempt to read all of a FwCfgDevice's entries
741    fn read_fw_cfg_entries() {
742        let contents = get_contents();
743        let (mut device, bai) = setup_read(&FILENAMES, &contents, (0 + FW_CFG_FILE_FIRST) as u16);
744
745        assert_read_entries(&FILENAMES, &mut device, bai);
746    }
747
748    #[test]
749    // Attempt to read revision, file dir, and entries in random order to make
750    // sure that proper state is maintained.
751    fn read_whole_device() {
752        let contents = get_contents();
753        let mut data: Vec<u8> = vec![0];
754
755        let (mut device, mut bai) = setup_read(&FILENAMES, &contents, FW_CFG_REVISION_SELECTOR);
756
757        let revision = read_u32(&mut device, bai, &mut data[..]).to_be_bytes();
758        assert_eq!(revision, FW_CFG_REVISION);
759
760        let i = FILENAMES.len() - 1;
761        let data_len = device.entries[0][i].data.len();
762        assert_eq!(
763            get_entry(&mut device, bai, data_len, (i + FW_CFG_FILE_FIRST) as u16),
764            device.entries[0][i].data
765        );
766
767        bai.address = FW_CFG_BASE_PORT;
768        bai.offset = FW_CFG_SELECTOR_PORT_OFFSET;
769        device.write(bai, &FW_CFG_FILE_DIR_SELECTOR.to_le_bytes());
770        bai.offset = FW_CFG_DATA_PORT_OFFSET;
771
772        assert_read_file_dir(&FILENAMES, &contents, &mut device, bai);
773
774        let data_len = device.entries[0][FW_CFG_FILE_FIRST + 0].data.len();
775        assert_eq!(
776            get_entry(&mut device, bai, data_len, (0 + FW_CFG_FILE_FIRST) as u16),
777            device.entries[0][FW_CFG_FILE_FIRST + 0].data
778        );
779    }
780
781    #[test]
782    // Assert that the device maintains proper state after reads of random length.
783    fn read_incorrect_bytes() {
784        let contents = get_contents();
785        let mut data: Vec<u8> = vec![0];
786        let (mut device, mut bai) =
787            setup_read(&FILENAMES, &contents, (0 + FW_CFG_FILE_FIRST) as u16);
788
789        for _i in 1..1000 {
790            let _random_bytes = read_u32(&mut device, bai, &mut data[..]);
791        }
792
793        bai.address = FW_CFG_BASE_PORT;
794        device.write(bai, &FW_CFG_FILE_DIR_SELECTOR.to_le_bytes());
795        bai.offset = FW_CFG_DATA_PORT_OFFSET;
796
797        for _i in 1..10000 {
798            let mut data: Vec<u8> = vec![0];
799            let _random_bytes = read_u32(&mut device, bai, &mut data[..]);
800        }
801
802        bai.address = FW_CFG_BASE_PORT;
803        bai.offset = FW_CFG_SELECTOR_PORT_OFFSET;
804        device.write(bai, &FW_CFG_FILE_DIR_SELECTOR.to_le_bytes());
805        bai.offset = FW_CFG_DATA_PORT_OFFSET;
806
807        assert_read_file_dir(&FILENAMES, &contents, &mut device, bai);
808
809        let i = FILENAMES.len() - 1;
810
811        bai.address = FW_CFG_BASE_PORT;
812        bai.offset = FW_CFG_SELECTOR_PORT_OFFSET;
813        device.write(bai, &(FW_CFG_FILE_FIRST + i).to_le_bytes());
814        bai.offset = FW_CFG_DATA_PORT_OFFSET;
815
816        for _i in 1..1000 {
817            let _random_bytes = read_u32(&mut device, bai, &mut data[..]);
818        }
819
820        assert_read_entries(&FILENAMES, &mut device, bai);
821    }
822}