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 (¶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 // 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(¶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 /// 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