#![cfg(target_os = "linux")]
mod common;
use common::{compare_exe_outputs, LSPCI_MUSL_PATH};
const PCI_IDS_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/data/pci.ids");
macro_rules! user_dump_multiple_args {
($($fname:ident: $x:expr, $args:expr,)*) => {
$(
#[test]
fn $fname() {
let dump = format!("{}/tests/data/machine/362f18e/out.{}.txt", env!("CARGO_MANIFEST_DIR"), $x);
compare_exe_outputs(LSPCI_MUSL_PATH, &format!("-i {} -F {} {}", PCI_IDS_PATH, dump, $args), true);
}
)*
}
}
user_dump_multiple_args! {
args_x: "x", "",
args_x_n: "x", "-n",
args_x_nn: "x", "-nn",
args_x_v: "x", "-v",
args_x_vv: "x", "-vv",
args_x_vvv: "x", "-vvv",
args_x_nv: "x", "-nv",
args_x_nvv: "x", "-nvv",
args_x_nvvv: "x", "-nvvv",
args_x_nnv: "x", "-nnv",
args_x_nnvv: "x", "-nnvv",
args_x_nnvvv: "x", "-nnvvv",
args_xxx: "xxx", "",
args_xxx_n: "xxx", "-n",
args_xxx_nn: "xxx", "-nn",
args_xxx_v: "xxx", "-v",
args_xxx_vv: "xxx", "-vv",
args_xxx_vvv: "xxx", "-vvv",
args_xxx_nv: "xxx", "-nv",
args_xxx_nvv: "xxx", "-nvv",
args_xxx_nvvv: "xxx", "-nvvv",
args_xxx_nnv: "xxx", "-nnv",
args_xxx_nnvv: "xxx", "-nnvv",
args_xxx_nnvvv: "xxx", "-nnvvv",
args_xxxx: "xxxx", "",
args_xxxx_n: "xxxx", "-n",
args_xxxx_nn: "xxxx", "-nn",
args_xxxx_v: "xxxx", "-v",
args_xxxx_vv: "xxxx", "-vv",
args_xxxx_vvv: "xxxx", "-vvv",
args_xxxx_nv: "xxxx", "-nv",
args_xxxx_nvv: "xxxx", "-nvv",
args_xxxx_nvvv: "xxxx", "-nvvv",
args_xxxx_nnv: "xxxx", "-nnv",
args_xxxx_nnvv: "xxxx", "-nnvv",
args_xxxx_nnvvv: "xxxx", "-nnvvv",
}
macro_rules! machines {
($($fname:ident: $machine:expr,)*) => {
$(
#[test]
fn $fname() {
compare_exe_outputs(LSPCI_MUSL_PATH, &format!(
"-F {}/tests/data/machine/{}/out.xxxx.txt -nnvvv -i {}",
env!("CARGO_MANIFEST_DIR"),
$machine,
PCI_IDS_PATH,
), true);
}
)*
}
}
machines! {
machine_ec8a5fc: "ec8a5fc",
machine_02daadc: "02daadc",
machine_23c7a39: "23c7a39",
}
#[cfg(test)]
mod fuzzing {
use super::*;
use seq_macro::seq;
use std::{io::Write, iter};
use tempfile::NamedTempFile;
const RANDOM_DATA: &[u8; 4096 * 64] = include_bytes!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/data/fuzzing/random"
));
const TEST_COUNT: usize = if cfg!(feature = "expensive_tests") {
64
} else {
1
};
const CAP_EA_FIXES: [(usize, u8, u8); 12 * 3] = [
(0x44, 0b111, 0x02),
(0x48, 0b10, 0x00),
(0x4c, 0b10, 0x00),
(0x50, 0b111, 0x03),
(0x54, 0b10, 0x00),
(0x58, 0b10, 0x10),
(0x60, 0b111, 0x03),
(0x64, 0b10, 0x10),
(0x68, 0b10, 0x00),
(0x70, 0b111, 0x04),
(0x74, 0b10, 0x10),
(0x78, 0b10, 0x10),
(0x84, 0b111, 0x02),
(0x88, 0b10, 0x00),
(0x8c, 0b10, 0x00),
(0x90, 0b111, 0x03),
(0x94, 0b10, 0x00),
(0x98, 0b10, 0x10),
(0xa0, 0b111, 0x03),
(0xa4, 0b10, 0x10),
(0xa8, 0b10, 0x00),
(0xb0, 0b111, 0x04),
(0xb4, 0b10, 0x10),
(0xb8, 0b10, 0x10),
(0xc4, 0b111, 0x02),
(0xc8, 0b10, 0x00),
(0xcc, 0b10, 0x00),
(0xd0, 0b111, 0x03),
(0xd4, 0b10, 0x10),
(0xd8, 0b10, 0x00),
(0xe0, 0b111, 0x03),
(0xe4, 0b10, 0x00),
(0xe8, 0b10, 0x10),
(0xf0, 0b111, 0x03),
(0xf4, 0b10, 0x10),
(0xf8, 0b10, 0x00),
];
#[derive(Clone, Copy)]
enum Param {
Header { htype: u8 },
Caps { id: u8, htype: u8 },
Ecaps { id: u16, htype: u8, aux: usize },
}
#[test]
fn header_type_00() {
run_test(Param::Header { htype: 0x00 });
}
#[test]
fn header_type_00_multifunction() {
run_test(Param::Header { htype: 0x08 });
}
#[test]
fn header_type_01() {
run_test(Param::Header { htype: 0x01 });
}
#[test]
fn header_type_01_multifunction() {
run_test(Param::Header { htype: 0x01 | 0x80 });
}
#[test]
fn header_type_02() {
run_test(Param::Header { htype: 0x02 });
}
#[test]
fn header_type_02_multifunction() {
run_test(Param::Header { htype: 0x02 | 0x80 });
}
seq!(N in 0x00..=0x15 {
#[test]
fn capability_~N() {
run_test(Param::Caps { id: N, htype: 0 });
}
});
#[test]
fn capability_10_bridge() {
run_test(Param::Caps { id: 10, htype: 1 });
}
#[test]
fn capability_14_bridge() {
run_test(Param::Caps { id: 14, htype: 1 });
}
seq!(N in 0x00..=0x34 {
#[test]
fn extended_capability_~N() {
run_test(Param::Ecaps { id: N, htype: 0, aux: 0 });
}
});
#[test]
fn extended_capability_23_cxl() {
run_test(Param::Ecaps {
id: 0x23,
htype: 0,
aux: 0x1e980000,
});
}
fn run_test(param: Param) {
let dump: String = RANDOM_DATA
.chunks_exact(4096)
.take(TEST_COUNT)
.chain(iter::once([u8::MIN; 4096].as_slice()))
.chain(iter::once([u8::MAX; 4096].as_slice()))
.enumerate()
.map(|(dn, cs)| {
let conf_space = &mut [0u8; 4096];
conf_space.copy_from_slice(cs);
let len = add_fixtures(conf_space, param);
let (bus, dev, fun) = ((dn & 0xff00) >> 8, (dn & 0b11111000) >> 3, dn & 0b111);
let body = conf_space[..len]
.chunks_exact(16)
.enumerate()
.map(|(ln, hbytes)| {
let hbytes: String = hbytes.iter().map(|b| format!(" {:02x}", b)).collect();
format!("{:x}0:{}\n", ln, hbytes)
});
Some(format!("{:02x}:{:02x}.{:x} _\n", bus, dev, fun))
.into_iter()
.chain(body)
.collect::<String>()
})
.collect();
let mut file = NamedTempFile::new().unwrap();
writeln!(file, "{}", dump).unwrap();
let args = format!(
"-nnvvv -F {} -i {}",
file.path().to_string_lossy(),
PCI_IDS_PATH
);
dbg!(&args);
file.keep().unwrap();
compare_exe_outputs(LSPCI_MUSL_PATH, &args, true);
}
fn add_fixtures(slice: &mut [u8], param: Param) -> usize {
let mut fill_common_caps = |id, htype| {
slice[..64].fill(0);
slice[0x06] = 0x10; slice[0x0e] = htype; slice[0x34] = 0x40; slice[0x40] = id; slice[0x41] = 0; };
match param {
Param::Header { htype } => {
slice[0x0e] = htype;
slice[0x34] = slice[0x34].max(0x40); slice[0x3d] &= 0xf; 64
}
Param::Caps { id: 0x14, htype } => {
fill_common_caps(0x14, htype);
slice[0x42] &= 0xf; for (mut offset, mask, val) in CAP_EA_FIXES {
if htype == 1 {
offset += 4;
}
slice[offset] = slice[offset] & !mask | val;
}
256
}
Param::Caps { id, htype } => {
fill_common_caps(id, htype);
256
}
Param::Ecaps { id, htype, aux } => {
let _ = htype;
slice[..256].fill(0);
slice[0x06] = 0x10; slice[0x34] = 0x40; slice[0x40] = 0x10; let [lo, hi] = id.to_le_bytes();
(slice[0x100], slice[0x101]) = (lo, hi); (slice[0x102], slice[0x103]) = (0, 0); if aux == 0x1e980000 {
slice[0x104..0x10A].copy_from_slice(&[0x98, 0x1E, 0x81, 0x03, 0x00, 0x00]);
}
4096
}
}
}
}