Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/fw_cfg.rs
5392 views
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::path::PathBuf;
11
12
#[cfg(windows)]
13
use base::error;
14
use serde::Deserialize;
15
use serde::Serialize;
16
use serde_keyvalue::FromKeyValues;
17
use thiserror::Error as ThisError;
18
use vm_control::DeviceId;
19
use vm_control::PlatformDeviceId;
20
21
use crate::BusAccessInfo;
22
use crate::BusDevice;
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 {
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
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 {
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
287
impl 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
387
impl 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)]
398
mod 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
}
823
824