1use 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;
18
19use crate::BusAccessInfo;
20use crate::BusDevice;
21use crate::DeviceId;
22use crate::Suspendable;
23
24pub const FW_CFG_BASE_PORT: u64 = 0x510;
25pub const FW_CFG_WIDTH: u64 = 0x4;
26pub const FW_CFG_MAX_FILE_SLOTS: usize = 16384 - FW_CFG_FILE_FIRST;
32const FW_CFG_FILE_FIRST: usize = 0x0020;
33const FW_CFG_SELECTOR_PORT_OFFSET: u64 = 0x0;
34const FW_CFG_DATA_PORT_OFFSET: u64 = 0x1;
35const FW_CFG_SELECTOR_RW_MASK: u16 = 0x2000;
36const FW_CFG_SELECTOR_ARCH_MASK: u16 = 0x4000;
37const FW_CFG_SELECTOR_SELECT_MASK: u16 = 0xbfff;
38const FW_CFG_SIGNATURE: [u8; 4] = [b'Q', b'E', b'M', b'U'];
39const FW_CFG_REVISION: [u8; 4] = [0, 0, 0, 1];
40const FW_CFG_SIGNATURE_SELECTOR: u16 = 0x0000;
41const FW_CFG_REVISION_SELECTOR: u16 = 0x0001;
42const FW_CFG_FILE_DIR_SELECTOR: u16 = 0x0019;
43const FW_CFG_FILENAME_SIZE: usize = 56;
45
46#[derive(ThisError, Debug)]
47pub enum Error {
48 #[error("Ran out of file slots")]
49 InsufficientFileSlots,
50
51 #[error("File already exists")]
52 FileAlreadyExists,
53
54 #[error("Data blob's size too large: overflowed u32")]
55 SizeOverflow,
56
57 #[error("too many entries: oveflows u16 selector")]
58 IndexOverflow,
59
60 #[error("Filename must be less than 55 characters long")]
61 FileNameTooLong,
62
63 #[error("Unable to open file {0} for fw_cfg: {1}")]
64 FileOpen(PathBuf, std::io::Error),
65
66 #[error("fw_cfg parameters must have exactly one of string or path")]
67 StringOrPathRequired,
68}
69
70pub type Result<T> = std::result::Result<T, Error>;
71
72#[derive(Clone, Debug, Deserialize, Serialize, FromKeyValues, PartialEq, Eq)]
73#[serde(deny_unknown_fields, rename_all = "kebab-case")]
74pub struct FwCfgParameters {
75 pub name: String,
76 pub string: Option<String>,
77 pub path: Option<PathBuf>,
78}
79
80#[derive(PartialEq)]
81pub enum FwCfgItemType {
82 GenericItem,
83 ArchSpecificItem,
84 FileDir,
85 Signature,
86 RevisionVector,
87}
88
89impl FwCfgItemType {
90 fn value(&self) -> usize {
91 match self {
92 FwCfgItemType::ArchSpecificItem => 1,
93 _ => 0,
94 }
95 }
96}
97
98struct FwCfgFile {
104 pub size: u32,
105 pub select: u16,
106 pub name: String,
107}
108
109#[derive(Clone)]
114struct FwCfgEntry {
115 pub allow_write: bool,
116 pub data: Vec<u8>,
117}
118
119pub struct FwCfgDevice {
126 file_slots: usize,
127 entries: [Vec<FwCfgEntry>; 2],
130 files: Vec<FwCfgFile>,
131 cur_item_type: FwCfgItemType,
132 cur_entry: u16,
133 cur_offset: usize,
134 file_names: HashSet<String>,
135}
136
137impl FwCfgDevice {
138 pub fn new(file_slots: usize, fw_cfg_parameters: Vec<FwCfgParameters>) -> Result<FwCfgDevice> {
139 let mut device = FwCfgDevice {
140 file_slots,
141 entries: [
142 vec![
143 FwCfgEntry {
144 allow_write: false,
145 data: vec![]
146 };
147 FW_CFG_FILE_FIRST
148 ],
149 Vec::new(),
150 ],
151 files: Vec::new(),
152 cur_item_type: FwCfgItemType::GenericItem,
153 cur_entry: 0,
154 cur_offset: 0,
155 file_names: HashSet::new(),
156 };
157
158 for param in fw_cfg_parameters {
159 let data = match (¶m.string, ¶m.path) {
160 (Some(string), None) => string.as_bytes().to_vec(),
161 (None, Some(path)) => {
162 fs::read(path).map_err(|e| Error::FileOpen(path.clone(), e))?
163 }
164 _ => return Err(Error::StringOrPathRequired),
165 };
166
167 device.add_file(¶m.name, data, FwCfgItemType::GenericItem)?
171 }
172
173 device.add_bytes(FW_CFG_SIGNATURE.to_vec(), FwCfgItemType::Signature);
174 device.add_bytes(FW_CFG_REVISION.to_vec(), FwCfgItemType::RevisionVector);
175
176 Ok(device)
177 }
178
179 pub fn add_file(
186 &mut self,
187 filename: &str,
188 data: Vec<u8>,
189 item_type: FwCfgItemType,
190 ) -> Result<()> {
191 if self.files.len() >= FW_CFG_MAX_FILE_SLOTS || self.files.len() >= self.file_slots {
195 return Err(Error::InsufficientFileSlots);
196 }
197
198 if filename.len() > FW_CFG_FILENAME_SIZE - 1 {
199 return Err(Error::FileNameTooLong);
200 }
201
202 let index = self.entries[item_type.value()].len();
205
206 if self.file_names.contains(filename) {
207 return Err(Error::FileAlreadyExists);
208 }
209
210 let size: u32 = data.len().try_into().map_err(|_| Error::SizeOverflow)?;
213
214 let mut select: u16 = (index).try_into().map_err(|_| Error::IndexOverflow)?;
215
216 if item_type == FwCfgItemType::ArchSpecificItem {
217 select |= FW_CFG_SELECTOR_ARCH_MASK;
218 }
219
220 let new_file = FwCfgFile {
221 size,
222 select,
223 name: filename.to_owned(),
224 };
225
226 self.add_bytes(data, item_type);
227 self.files.push(new_file);
228 self.file_names.insert(filename.to_string());
229 self.update_file_dir_entry();
231
232 Ok(())
233 }
234
235 fn add_bytes(&mut self, data: Vec<u8>, item_type: FwCfgItemType) {
236 let new_entry = FwCfgEntry {
239 allow_write: false,
240 data,
241 };
242
243 match item_type {
244 FwCfgItemType::GenericItem | FwCfgItemType::ArchSpecificItem => {
245 self.entries[item_type.value()].push(new_entry)
246 }
247 FwCfgItemType::FileDir => {
248 self.entries[item_type.value()][FW_CFG_FILE_DIR_SELECTOR as usize] = new_entry
249 }
250 FwCfgItemType::Signature => {
251 self.entries[item_type.value()][FW_CFG_SIGNATURE_SELECTOR as usize] = new_entry
252 }
253 FwCfgItemType::RevisionVector => {
254 self.entries[item_type.value()][FW_CFG_REVISION_SELECTOR as usize] = new_entry
255 }
256 }
257 }
258
259 fn update_file_dir_entry(&mut self) {
260 let mut raw_file_dir: Vec<u8> = Vec::new();
261 let files_dir_count = self.files.len() as u32;
264 raw_file_dir.extend_from_slice(&files_dir_count.to_be_bytes());
265
266 for file in &self.files {
267 raw_file_dir.extend_from_slice(&file.size.to_be_bytes());
268 raw_file_dir.extend_from_slice(&file.select.to_be_bytes());
269 raw_file_dir.extend_from_slice(&[0, 0]);
273 raw_file_dir.extend_from_slice(file.name.as_bytes());
274 raw_file_dir.extend(std::iter::repeat_n(
276 0,
277 FW_CFG_FILENAME_SIZE - file.name.len(),
278 ));
279 }
280
281 self.add_bytes(raw_file_dir, FwCfgItemType::FileDir);
282 }
283}
284
285impl BusDevice for FwCfgDevice {
287 fn device_id(&self) -> DeviceId {
288 super::CrosvmDeviceId::FwCfg.into()
289 }
290
291 fn debug_label(&self) -> String {
292 "FwCfg".to_owned()
293 }
294
295 fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
297 if data.len() != 1 {
298 return;
299 }
300
301 if info.offset == FW_CFG_DATA_PORT_OFFSET {
303 let entries_index = self.cur_entry as usize;
304 if self.cur_offset
307 >= self.entries[self.cur_item_type.value()][entries_index]
308 .data
309 .len()
310 {
311 data[0] = 0x00;
312 return;
313 }
314 data[0] = self.entries[self.cur_item_type.value()][entries_index].data[self.cur_offset];
315 self.cur_offset += 1;
316 }
317 }
318
319 fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
321 if info.offset == FW_CFG_SELECTOR_PORT_OFFSET {
323 if data.len() != 2 {
324 return;
325 }
326
327 let Ok(selector) = data.try_into().map(u16::from_le_bytes) else {
328 return;
329 };
330
331 self.cur_offset = 0;
332
333 match selector {
334 FW_CFG_FILE_DIR_SELECTOR => {
335 self.cur_entry = FW_CFG_FILE_DIR_SELECTOR;
336 }
337 FW_CFG_REVISION_SELECTOR => {
338 self.cur_entry = FW_CFG_REVISION_SELECTOR;
339 }
340 FW_CFG_SIGNATURE_SELECTOR => {
341 self.cur_entry = FW_CFG_SIGNATURE_SELECTOR;
342 }
343 _ => {
344 let entries_index = selector as usize;
345
346 if (FW_CFG_SELECTOR_ARCH_MASK & selector) > 0 {
349 self.cur_item_type = FwCfgItemType::ArchSpecificItem;
350 } else {
351 self.cur_item_type = FwCfgItemType::GenericItem;
352 }
353
354 if self.entries[self.cur_item_type.value()].len() <= entries_index {
356 return;
357 }
358
359 self.entries[self.cur_item_type.value()][entries_index].allow_write =
364 (FW_CFG_SELECTOR_RW_MASK & selector) > 0;
365
366 if (FW_CFG_SELECTOR_ARCH_MASK & selector) > 0 {
369 self.cur_item_type = FwCfgItemType::ArchSpecificItem;
370 } else {
371 self.cur_item_type = FwCfgItemType::GenericItem;
372 }
373
374 self.cur_entry = selector & FW_CFG_SELECTOR_SELECT_MASK;
380 }
381 }
382 }
383 }
384}
385
386impl Suspendable for FwCfgDevice {
387 fn sleep(&mut self) -> anyhow::Result<()> {
388 Ok(())
389 }
390
391 fn wake(&mut self) -> anyhow::Result<()> {
392 Ok(())
393 }
394}
395
396#[cfg(test)]
397mod tests {
398 use serde_keyvalue::*;
399
400 use super::*;
401 use crate::FW_CFG_BASE_PORT;
402
403 const MAGIC_BYTE: u8 = 111;
404 const MAGIC_BYTE_ALT: u8 = 222;
405 const FILENAME: &str = "/test/device/crosvmval";
406 const FILENAMES: [&str; 6] = [
407 "test/hello.txt",
408 "user/data/mydata.txt",
409 "bruschetta/user/foo",
410 "valid_unix/me/home/dir/back.txt",
411 "/dev/null",
412 "google/unix/sys/maple.txt",
413 ];
414
415 fn default_params() -> Vec<FwCfgParameters> {
416 Vec::new()
417 }
418
419 fn get_contents() -> [Vec<u8>; 6] {
420 [
421 b"CROSVM".to_vec(),
422 b"GOOGLE".to_vec(),
423 b"FWCONFIG".to_vec(),
424 b"PIZZA".to_vec(),
425 b"CHROMEOS".to_vec(),
426 b"42".to_vec(),
427 ]
428 }
429
430 fn make_device(
431 filenames: &[&str],
432 contents: &[Vec<u8>],
433 params: &[FwCfgParameters],
434 file_slots: &usize,
435 ) -> Result<FwCfgDevice> {
436 let mut device = FwCfgDevice::new(*file_slots, params.to_owned())?;
437 let count = filenames.len();
438
439 for i in 0..count {
440 device.add_file(
441 filenames[i],
442 contents[i].clone(),
443 FwCfgItemType::GenericItem,
444 )?;
445 }
446
447 Ok(device)
448 }
449
450 fn from_fw_cfg_arg(options: &str) -> std::result::Result<FwCfgParameters, ParseError> {
451 from_key_values(options)
452 }
453
454 fn read_u32(device: &mut FwCfgDevice, bai: BusAccessInfo, data: &mut [u8]) -> u32 {
455 let mut bytes: [u8; 4] = [0, 0, 0, 0];
456 device.read(bai, data);
457 bytes[0] = data[0];
458 device.read(bai, data);
459 bytes[1] = data[0];
460 device.read(bai, data);
461 bytes[2] = data[0];
462 device.read(bai, data);
463 bytes[3] = data[0];
464
465 u32::from_be_bytes(bytes)
466 }
467
468 fn read_u16(device: &mut FwCfgDevice, bai: BusAccessInfo, data: &mut [u8]) -> u16 {
469 let mut bytes: [u8; 2] = [0, 0];
470 device.read(bai, data);
471 bytes[0] = data[0];
472 device.read(bai, data);
473 bytes[1] = data[0];
474
475 u16::from_be_bytes(bytes)
476 }
477
478 fn read_u8(device: &mut FwCfgDevice, bai: BusAccessInfo, data: &mut [u8]) -> u8 {
479 let mut bytes: [u8; 1] = [0];
480 device.read(bai, data);
481 bytes[0] = data[0];
482 u8::from_be_bytes(bytes)
483 }
484
485 fn read_char_56(device: &mut FwCfgDevice, bai: BusAccessInfo, data: &mut [u8]) -> String {
486 let mut c: char = read_u8(device, bai, data) as char;
487 let mut count = 1; let mut name: String = String::new();
489 while c != '\0' {
490 name.push(c);
491 c = read_u8(device, bai, data) as char;
492 count += 1;
493 }
494 while count < FW_CFG_FILENAME_SIZE {
495 read_u8(device, bai, data);
496 count += 1;
497 }
498 name
499 }
500
501 fn get_entry(
502 device: &mut FwCfgDevice,
503 mut bai: BusAccessInfo,
504 size: usize,
505 selector: u16,
506 ) -> Vec<u8> {
507 let mut data: Vec<u8> = vec![0];
508 let mut blob: Vec<u8> = Vec::new();
509
510 bai.address = FW_CFG_BASE_PORT;
511 bai.offset = FW_CFG_SELECTOR_PORT_OFFSET;
512 let selector: [u8; 2] = selector.to_le_bytes();
513 device.write(bai, &selector);
514
515 bai.offset = FW_CFG_DATA_PORT_OFFSET;
516
517 for _i in 0..size {
518 read_u8(device, bai, &mut data[..]);
519 blob.push(data[0]);
520 }
521
522 blob
523 }
524
525 fn assert_read_entries(filenames: &[&str], device: &mut FwCfgDevice, bai: BusAccessInfo) {
526 let data_len = device.entries[0][0 + FW_CFG_FILE_FIRST].data.len();
527 assert_eq!(
528 get_entry(device, bai, data_len, (0 + FW_CFG_FILE_FIRST) as u16),
529 device.entries[0][0 + FW_CFG_FILE_FIRST].data
530 );
531
532 for i in (FW_CFG_FILE_FIRST + 1)..filenames.len() {
533 let data_len = device.entries[0][i].data.len();
534 assert_eq!(
535 get_entry(device, bai, data_len, (i + FW_CFG_FILE_FIRST) as u16),
536 device.entries[0][i].data
537 );
538 }
539 }
540
541 fn assert_read_file_dir(
542 filenames: &[&str],
543 contents: &[Vec<u8>],
544 device: &mut FwCfgDevice,
545 bai: BusAccessInfo,
546 ) {
547 let mut data: Vec<u8> = vec![0];
548 let file_count = read_u32(device, bai, &mut data[..]);
549 assert_eq!(file_count, filenames.len() as u32);
550
551 for i in 0..filenames.len() {
552 let file_size = read_u32(device, bai, &mut data[..]);
553 assert_eq!(file_size, contents[i].len() as u32);
554
555 let file_select = read_u16(device, bai, &mut data[..]);
556 assert_eq!(file_select - (FW_CFG_FILE_FIRST as u16), i as u16);
557
558 let file_reserved = read_u16(device, bai, &mut data[..]);
559 assert_eq!(file_reserved, 0);
560
561 let file_name = read_char_56(device, bai, &mut data[..]);
562 assert_eq!(file_name, FILENAMES[i]);
563 }
564 }
565 fn setup_read(
566 filenames: &[&str],
567 contents: &[Vec<u8>],
568 selector: u16,
569 ) -> (FwCfgDevice, BusAccessInfo) {
570 let mut device = make_device(
571 filenames,
572 contents,
573 &default_params(),
574 &(filenames.len() + 5),
575 )
576 .unwrap();
577 let mut bai = BusAccessInfo {
578 offset: FW_CFG_SELECTOR_PORT_OFFSET,
579 address: FW_CFG_BASE_PORT,
580 id: 0,
581 };
582 let selector: [u8; 2] = selector.to_le_bytes();
583 device.write(bai, &selector);
584 bai.offset = FW_CFG_DATA_PORT_OFFSET;
585
586 (device, bai)
587 }
588
589 #[test]
590 fn params_from_key_values() {
592 from_fw_cfg_arg("").expect_err("parsing empty string should fail");
593
594 let params = from_fw_cfg_arg("name=foo,path=/path/to/input").unwrap();
595 assert_eq!(
596 params,
597 FwCfgParameters {
598 name: "foo".into(),
599 path: Some("/path/to/input".into()),
600 string: None,
601 }
602 );
603
604 let params = from_fw_cfg_arg("name=bar,string=testdata").unwrap();
605 assert_eq!(
606 params,
607 FwCfgParameters {
608 name: "bar".into(),
609 string: Some("testdata".into()),
610 path: None,
611 }
612 );
613 }
614
615 #[test]
616 fn attempt_underflow_read() {
619 let (_device, _bai) = setup_read(
620 &FILENAMES,
621 &get_contents(),
622 (FW_CFG_FILE_FIRST - 0x05) as u16,
623 );
624 }
625
626 #[test]
627 fn write_one_byte_file() {
629 let mut fw_cfg = FwCfgDevice::new(100, default_params()).unwrap();
630 let data = vec![MAGIC_BYTE];
631 fw_cfg
632 .add_file(FILENAME, data, FwCfgItemType::GenericItem)
633 .expect("File insert failed");
634 let ind = fw_cfg.entries[0].len();
635 assert_eq!(
636 ind,
637 FW_CFG_FILE_FIRST + 1,
638 "Insertion into fw_cfg failed: Index is wrong. expected {}, got {},
639 ",
640 FW_CFG_FILE_FIRST + 1,
641 ind
642 );
643 assert_eq!(
644 fw_cfg.entries[0][ind - 1].data,
645 vec![MAGIC_BYTE],
646 "Insertion failed: unexpected fw_cfg entry values"
647 );
648 }
649
650 #[test]
651 fn write_four_byte_file() {
653 let mut fw_cfg = FwCfgDevice::new(100, default_params()).unwrap();
654 let data = vec![MAGIC_BYTE, MAGIC_BYTE_ALT, MAGIC_BYTE, MAGIC_BYTE_ALT];
655 fw_cfg
656 .add_file(FILENAME, data, FwCfgItemType::GenericItem)
657 .expect("File insert failed");
658 let ind = fw_cfg.entries[0].len();
659 assert_eq!(
660 ind,
661 FW_CFG_FILE_FIRST + 1,
662 "Insertion into fw_cfg failed: Index is wrong. expected {}, got {}",
663 FW_CFG_FILE_FIRST + 1,
664 ind
665 );
666 assert_eq!(
667 fw_cfg.entries[0][ind - 1].data,
668 vec![MAGIC_BYTE, MAGIC_BYTE_ALT, MAGIC_BYTE, MAGIC_BYTE_ALT],
669 "Insertion failed: unexpected fw_cfg entry values"
670 );
671 }
672
673 #[test]
674 #[should_panic]
675 fn write_file_one_slot_expect_nop() {
678 let mut fw_cfg = FwCfgDevice::new(0, default_params()).unwrap();
679 let data = vec![MAGIC_BYTE];
680 fw_cfg
681 .add_file(FILENAME, data, FwCfgItemType::GenericItem)
682 .expect("File insert failed");
683 }
684
685 #[test]
686 #[should_panic]
687 fn write_two_files_no_slots_expect_nop_on_second() {
690 let mut fw_cfg = FwCfgDevice::new(1, default_params()).unwrap();
691 let data = vec![MAGIC_BYTE];
692 let data2 = vec![MAGIC_BYTE_ALT];
693 fw_cfg
694 .add_file(FILENAME, data, FwCfgItemType::GenericItem)
695 .expect("File insert failed");
696 assert_eq!(
697 fw_cfg.entries[0].len(),
698 1,
699 "Insertion into fw_cfg failed: Expected {} elements, got {}",
700 1,
701 fw_cfg.entries[0].len()
702 );
703 fw_cfg
704 .add_file(FILENAME, data2, FwCfgItemType::GenericItem)
705 .expect("File insert failed");
706 }
707
708 #[test]
709 fn read_fw_cfg_signature() {
711 let mut data: Vec<u8> = vec![0];
712 let (mut device, bai) = setup_read(&FILENAMES, &get_contents(), FW_CFG_SIGNATURE_SELECTOR);
713 let signature = read_u32(&mut device, bai, &mut data[..]).to_be_bytes();
716 assert_eq!(signature, FW_CFG_SIGNATURE);
717 }
718
719 #[test]
720 fn read_fw_cfg_revision() {
722 let mut data: Vec<u8> = vec![0];
723 let (mut device, bai) = setup_read(&FILENAMES, &get_contents(), FW_CFG_REVISION_SELECTOR);
724 let revision = read_u32(&mut device, bai, &mut data[..]).to_be_bytes();
727 assert_eq!(revision, FW_CFG_REVISION);
728 }
729
730 #[test]
731 fn read_file_dir() {
733 let contents = get_contents();
734 let (mut device, bai) = setup_read(&FILENAMES, &contents, FW_CFG_FILE_DIR_SELECTOR);
735 assert_read_file_dir(&FILENAMES, &contents, &mut device, bai);
736 }
737
738 #[test]
739 fn read_fw_cfg_entries() {
741 let contents = get_contents();
742 let (mut device, bai) = setup_read(&FILENAMES, &contents, (0 + FW_CFG_FILE_FIRST) as u16);
743
744 assert_read_entries(&FILENAMES, &mut device, bai);
745 }
746
747 #[test]
748 fn read_whole_device() {
751 let contents = get_contents();
752 let mut data: Vec<u8> = vec![0];
753
754 let (mut device, mut bai) = setup_read(&FILENAMES, &contents, FW_CFG_REVISION_SELECTOR);
755
756 let revision = read_u32(&mut device, bai, &mut data[..]).to_be_bytes();
757 assert_eq!(revision, FW_CFG_REVISION);
758
759 let i = FILENAMES.len() - 1;
760 let data_len = device.entries[0][i].data.len();
761 assert_eq!(
762 get_entry(&mut device, bai, data_len, (i + FW_CFG_FILE_FIRST) as u16),
763 device.entries[0][i].data
764 );
765
766 bai.address = FW_CFG_BASE_PORT;
767 bai.offset = FW_CFG_SELECTOR_PORT_OFFSET;
768 device.write(bai, &FW_CFG_FILE_DIR_SELECTOR.to_le_bytes());
769 bai.offset = FW_CFG_DATA_PORT_OFFSET;
770
771 assert_read_file_dir(&FILENAMES, &contents, &mut device, bai);
772
773 let data_len = device.entries[0][FW_CFG_FILE_FIRST + 0].data.len();
774 assert_eq!(
775 get_entry(&mut device, bai, data_len, (0 + FW_CFG_FILE_FIRST) as u16),
776 device.entries[0][FW_CFG_FILE_FIRST + 0].data
777 );
778 }
779
780 #[test]
781 fn read_incorrect_bytes() {
783 let contents = get_contents();
784 let mut data: Vec<u8> = vec![0];
785 let (mut device, mut bai) =
786 setup_read(&FILENAMES, &contents, (0 + FW_CFG_FILE_FIRST) as u16);
787
788 for _i in 1..1000 {
789 let _random_bytes = read_u32(&mut device, bai, &mut data[..]);
790 }
791
792 bai.address = FW_CFG_BASE_PORT;
793 device.write(bai, &FW_CFG_FILE_DIR_SELECTOR.to_le_bytes());
794 bai.offset = FW_CFG_DATA_PORT_OFFSET;
795
796 for _i in 1..10000 {
797 let mut data: Vec<u8> = vec![0];
798 let _random_bytes = read_u32(&mut device, bai, &mut data[..]);
799 }
800
801 bai.address = FW_CFG_BASE_PORT;
802 bai.offset = FW_CFG_SELECTOR_PORT_OFFSET;
803 device.write(bai, &FW_CFG_FILE_DIR_SELECTOR.to_le_bytes());
804 bai.offset = FW_CFG_DATA_PORT_OFFSET;
805
806 assert_read_file_dir(&FILENAMES, &contents, &mut device, bai);
807
808 let i = FILENAMES.len() - 1;
809
810 bai.address = FW_CFG_BASE_PORT;
811 bai.offset = FW_CFG_SELECTOR_PORT_OFFSET;
812 device.write(bai, &(FW_CFG_FILE_FIRST + i).to_le_bytes());
813 bai.offset = FW_CFG_DATA_PORT_OFFSET;
814
815 for _i in 1..1000 {
816 let _random_bytes = read_u32(&mut device, bai, &mut data[..]);
817 }
818
819 assert_read_entries(&FILENAMES, &mut device, bai);
820 }
821}