Strengthen PCI and IRQ helper coverage
This commit is contained in:
@@ -92,6 +92,27 @@ struct AllocatedVectors {
|
||||
vectors: Vec<i32>,
|
||||
}
|
||||
|
||||
fn describe_irq_flags(flags: u32) -> String {
|
||||
let mut parts = Vec::new();
|
||||
if flags & PCI_IRQ_MSI != 0 {
|
||||
parts.push("msi");
|
||||
}
|
||||
if flags & PCI_IRQ_MSIX != 0 {
|
||||
parts.push("msix");
|
||||
}
|
||||
if flags & PCI_IRQ_LEGACY != 0 {
|
||||
parts.push("legacy");
|
||||
}
|
||||
if flags & PCI_IRQ_NOLEGACY != 0 {
|
||||
parts.push("nolegacy");
|
||||
}
|
||||
if parts.is_empty() {
|
||||
"none".to_string()
|
||||
} else {
|
||||
parts.join("|")
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref CURRENT_DEVICE: Mutex<Option<CurrentDevice>> = Mutex::new(None);
|
||||
static ref REGISTERED_PROBE: Mutex<Option<PciDriverProbe>> = Mutex::new(None);
|
||||
@@ -294,6 +315,13 @@ fn allocate_vectors(dev: *mut PciDev, min_vecs: i32, max_vecs: i32, flags: u32)
|
||||
}
|
||||
|
||||
let allocated = (0..count).map(|index| base_irq + index).collect::<Vec<_>>();
|
||||
log::info!(
|
||||
"pci_alloc_irq_vectors: base_irq={} count={} flags={} vectors={:?}",
|
||||
base_irq,
|
||||
count,
|
||||
describe_irq_flags(flags),
|
||||
allocated
|
||||
);
|
||||
vectors.insert(
|
||||
dev_key,
|
||||
AllocatedVectors {
|
||||
@@ -309,6 +337,25 @@ pub extern "C" fn pci_enable_device(dev: *mut PciDev) -> i32 {
|
||||
if dev.is_null() {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
{
|
||||
let mut pci = match open_current_device(dev) {
|
||||
Ok(pci) => pci,
|
||||
Err(error) => return error,
|
||||
};
|
||||
|
||||
if let Err(error) = pci.enable_device() {
|
||||
log::warn!(
|
||||
"pci_enable_device: failed to enable {:04x}:{:04x}: {}",
|
||||
unsafe { (*dev).vendor },
|
||||
unsafe { (*dev).device },
|
||||
error
|
||||
);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
log::info!(
|
||||
"pci_enable_device: vendor=0x{:04x} device=0x{:04x}",
|
||||
unsafe { (*dev).vendor },
|
||||
@@ -499,6 +546,15 @@ pub extern "C" fn pci_free_irq_vectors(dev: *mut PciDev) {
|
||||
if dev.is_null() {
|
||||
return;
|
||||
}
|
||||
if let Ok(vectors) = IRQ_VECTORS.lock() {
|
||||
if let Some(allocated) = vectors.get(&(dev as usize)) {
|
||||
log::info!(
|
||||
"pci_free_irq_vectors: releasing {} vectors {:?}",
|
||||
allocated.vectors.len(),
|
||||
allocated.vectors
|
||||
);
|
||||
}
|
||||
}
|
||||
clear_irq_vectors_for_ptr(dev as usize);
|
||||
}
|
||||
|
||||
@@ -707,4 +763,15 @@ mod tests {
|
||||
-22
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn describe_irq_flags_formats_requested_modes() {
|
||||
assert_eq!(describe_irq_flags(0), "none");
|
||||
assert_eq!(describe_irq_flags(PCI_IRQ_MSI), "msi");
|
||||
assert_eq!(describe_irq_flags(PCI_IRQ_MSIX | PCI_IRQ_NOLEGACY), "msix|nolegacy");
|
||||
assert_eq!(
|
||||
describe_irq_flags(PCI_IRQ_MSI | PCI_IRQ_MSIX | PCI_IRQ_LEGACY),
|
||||
"msi|msix|legacy"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,3 +317,91 @@ fn allocate_irq_vector(cpu_id: u8) -> Result<(u32, File)> {
|
||||
"no free IRQ vectors available in {dir}"
|
||||
)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::pci::{PciBarInfo, PciBarKind, PciLocation};
|
||||
|
||||
fn test_device_info() -> PciDeviceInfo {
|
||||
PciDeviceInfo {
|
||||
location: PciLocation {
|
||||
segment: 0,
|
||||
bus: 0,
|
||||
device: 0,
|
||||
function: 0,
|
||||
},
|
||||
vendor_id: 0x1002,
|
||||
device_id: 0x1234,
|
||||
subsystem_vendor_id: 0xffff,
|
||||
subsystem_device_id: 0xffff,
|
||||
revision: 0,
|
||||
class_code: 0,
|
||||
subclass: 0,
|
||||
prog_if: 0,
|
||||
header_type: 0,
|
||||
irq: None,
|
||||
bars: Vec::new(),
|
||||
capabilities: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checked_bar_window_accepts_in_range_region() {
|
||||
let start = checked_bar_window(0x1000, 0x400, 0x80, 0x40).expect("window in range");
|
||||
assert_eq!(start, 0x1080);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checked_bar_window_rejects_region_past_bar_end() {
|
||||
let error = checked_bar_window(0x1000, 0x100, 0xf0, 0x20).expect_err("window past end");
|
||||
assert!(matches!(error, DriverError::Irq(_)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checked_bar_window_rejects_address_overflow() {
|
||||
let error = checked_bar_window(u64::MAX - 0x10, 0x200, 0x40, 0x20)
|
||||
.expect_err("overflow should be rejected");
|
||||
assert!(matches!(error, DriverError::InvalidParam("MSI-X BAR address overflow")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lookup_msix_bar_requires_memory_bar() {
|
||||
let mut device = test_device_info();
|
||||
device.bars.push(PciBarInfo {
|
||||
index: 0,
|
||||
kind: PciBarKind::Io,
|
||||
addr: 0x3f8,
|
||||
size: 8,
|
||||
prefetchable: false,
|
||||
});
|
||||
|
||||
let error = lookup_msix_bar(&device, 0, "table").expect_err("I/O BAR should be rejected");
|
||||
assert!(matches!(error, DriverError::CapabilityNotFound(_)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lookup_msix_bar_returns_matching_memory_bar() {
|
||||
let mut device = test_device_info();
|
||||
device.bars.push(PciBarInfo {
|
||||
index: 2,
|
||||
kind: PciBarKind::Memory64,
|
||||
addr: 0x20_0000,
|
||||
size: 0x1000,
|
||||
prefetchable: true,
|
||||
});
|
||||
|
||||
let bar = lookup_msix_bar(&device, 2, "table").expect("memory BAR should be found");
|
||||
assert_eq!(bar.index, 2);
|
||||
assert_eq!(bar.addr, 0x20_0000);
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
#[test]
|
||||
fn irq_request_reports_non_redox_platform_limit() {
|
||||
match IrqHandle::request(5) {
|
||||
Ok(_) => panic!("host builds should reject IRQ requests"),
|
||||
Err(error) => assert!(matches!(error, DriverError::Irq(_))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ impl PciBarInfo {
|
||||
|
||||
pub fn io_port(&self) -> Option<u16> {
|
||||
if self.is_io() && self.addr != 0 {
|
||||
Some(self.addr as u16)
|
||||
u16::try_from(self.addr).ok()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -709,6 +709,9 @@ fn parse_scheme_entry(name: &str) -> Option<PciLocation> {
|
||||
}
|
||||
let device = u8::from_str_radix(dev_func[0], 16).ok()?;
|
||||
let function = u8::from_str_radix(dev_func[1], 16).ok()?;
|
||||
if device > 0x1F || function > 0x07 {
|
||||
return None;
|
||||
}
|
||||
Some(PciLocation {
|
||||
segment,
|
||||
bus,
|
||||
@@ -728,3 +731,65 @@ pub fn find_intel_gpus() -> Result<Vec<PciDeviceInfo>> {
|
||||
all.retain(|d| d.is_intel_gpu());
|
||||
Ok(all)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn pci_location_bdf_round_trip_preserves_bus_device_function() {
|
||||
let location = PciLocation {
|
||||
segment: 0,
|
||||
bus: 0x5a,
|
||||
device: 0x1c,
|
||||
function: 0x03,
|
||||
};
|
||||
|
||||
let round_trip = PciLocation::from_bdf(location.bdf());
|
||||
assert_eq!(round_trip.segment, 0);
|
||||
assert_eq!(round_trip.bus, location.bus);
|
||||
assert_eq!(round_trip.device, location.device);
|
||||
assert_eq!(round_trip.function, location.function);
|
||||
assert_eq!(location.scheme_path(), "/scheme/pci/0000--5a--1c.3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn io_bar_port_requires_nonzero_u16_address() {
|
||||
let io_bar = PciBarInfo {
|
||||
index: 0,
|
||||
kind: PciBarKind::Io,
|
||||
addr: 0x3f8,
|
||||
size: 8,
|
||||
prefetchable: false,
|
||||
};
|
||||
assert_eq!(io_bar.io_port(), Some(0x3f8));
|
||||
|
||||
let zero_io_bar = PciBarInfo {
|
||||
addr: 0,
|
||||
..io_bar
|
||||
};
|
||||
assert_eq!(zero_io_bar.io_port(), None);
|
||||
|
||||
let oversized_io_bar = PciBarInfo {
|
||||
addr: u64::from(u16::MAX) + 1,
|
||||
..io_bar
|
||||
};
|
||||
assert_eq!(oversized_io_bar.io_port(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_scheme_entry_rejects_invalid_bdf_components() {
|
||||
assert!(parse_scheme_entry("0000--00--20.0").is_none());
|
||||
assert!(parse_scheme_entry("0000--00--1f.8").is_none());
|
||||
assert!(parse_scheme_entry("not-a-device").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_scheme_entry_accepts_valid_pci_scheme_name() {
|
||||
let parsed = parse_scheme_entry("0000--80--1f.0").expect("valid PCI entry should parse");
|
||||
assert_eq!(parsed.segment, 0);
|
||||
assert_eq!(parsed.bus, 0x80);
|
||||
assert_eq!(parsed.device, 0x1f);
|
||||
assert_eq!(parsed.function, 0x00);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user