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;
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;
27pub 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;
44const 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
99struct FwCfgFile {
105 pub size: u32,
106 pub select: u16,
107 pub name: String,
108}
109
110#[derive(Clone)]
115struct FwCfgEntry {
116 pub allow_write: bool,
117 pub data: Vec<u8>,
118}
119
120pub struct FwCfgDevice {
127 file_slots: usize,
128 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 (¶m.string, ¶m.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 device.add_file(¶m.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 pub fn add_file(
187 &mut self,
188 filename: &str,
189 data: Vec<u8>,
190 item_type: FwCfgItemType,
191 ) -> Result<()> {
192 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 let index = self.entries[item_type.value()].len();
206
207 if self.file_names.contains(filename) {
208 return Err(Error::FileAlreadyExists);
209 }
210
211 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 self.update_file_dir_entry();
232
233 Ok(())
234 }
235
236 fn add_bytes(&mut self, data: Vec<u8>, item_type: FwCfgItemType) {
237 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 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 raw_file_dir.extend_from_slice(&[0, 0]);
274 raw_file_dir.extend_from_slice(file.name.as_bytes());
275 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
286impl 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 fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
298 if data.len() != 1 {
299 return;
300 }
301
302 if info.offset == FW_CFG_DATA_PORT_OFFSET {
304 let entries_index = self.cur_entry as usize;
305 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 fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
322 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 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 if self.entries[self.cur_item_type.value()].len() <= entries_index {
357 return;
358 }
359
360 self.entries[self.cur_item_type.value()][entries_index].allow_write =
365 (FW_CFG_SELECTOR_RW_MASK & selector) > 0;
366
367 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 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; 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 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 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 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 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 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 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 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 let signature = read_u32(&mut device, bai, &mut data[..]).to_be_bytes();
717 assert_eq!(signature, FW_CFG_SIGNATURE);
718 }
719
720 #[test]
721 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 let revision = read_u32(&mut device, bai, &mut data[..]).to_be_bytes();
728 assert_eq!(revision, FW_CFG_REVISION);
729 }
730
731 #[test]
732 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 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 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 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}