redbear-iwlwifi: log PCI quirk flags at Wi-Fi device detection (Gap 12)

R1-R10 audit Gap 12: redbear-iwlwifi had zero PCI quirk
consumption at Wi-Fi device detection time. The linux-kpi
crate ships pci_has_quirk and pci_get_quirk_flags for
consumers in C-land, but the Rust-side lookup function
lookup_pci_quirks was not called from this driver. Every
Intel Wi-Fi NIC passed the data-driven quirk table
without a single log line.

This change:

- Adds source/src/quirks.rs with one public function,
  log_wifi_quirks(location, vendor, device) that:
  1. Builds a PciDeviceInfo from the candidate's location
     and the just-parsed vendor / device IDs.
  2. Calls redox_driver_sys::quirks::lookup_pci_quirks.
  3. Logs the resulting flag word (info-level on a hit,
     debug-level on empty).
  4. Returns the flags so the caller can gate probe /
     interrupt selection on specific bits (NO_MSI,
     NO_MSIX, DISABLE_ACCEL) in a follow-up.

- Wires the call into detect_candidates() at
  src/main.rs:494, right after the Intel vendor / class /
  subclass match — the canonical identification point.
  The location is now available (it was already parsed
  via parse_location_from_config_path) and vendor_id /
  device_id are in scope from the PCI config read.

Implementation note: this module bypasses the
linux_kpi::pci::pci_get_quirk_flags C FFI because that
function takes *mut PciDev and the bus / dev / func
fields are private to the linux-kpi crate. The
underlying lookup is identical — linux-kpi's FFI is a
thin wrapper around the same redox_driver_sys function
we call here. Going through PciDeviceInfo directly is
the natural Rust path; the C FFI remains available for
C-side consumers that already hold a struct pci_dev*.

3 unit tests cover the wiring:
- zeroed device returns empty
- unmatched vendor returns empty
- real Intel NIC ID round-trips through PciQuirkFlags
  without losing bits

No Cargo.toml change needed: redox-driver-sys was
already a direct dependency.
This commit is contained in:
2026-06-07 21:06:51 +03:00
parent a24cfe64c4
commit 1561767ac9
2 changed files with 96 additions and 0 deletions
@@ -12,6 +12,8 @@ use redox_driver_sys::pci::{PCI_VENDOR_ID_INTEL, PciLocation};
use std::ffi::CString;
use thiserror::Error;
mod quirks;
struct StderrLogger;
impl log::Log for StderrLogger {
@@ -491,6 +493,7 @@ fn detect_candidates(firmware_root: &PathBuf) -> Result<Vec<Candidate>, DriverEr
}
let subsystem_id = u16::from_le_bytes([config[0x2E], config[0x2F]]);
let location = parse_location_from_config_path(&config_path)?;
quirks::log_wifi_quirks(location, vendor_id, device_id);
let (family, ucode_candidates, pnvm_candidate) =
intel_firmware_candidates(device_id, subsystem_id);
let selected_ucode = ucode_candidates
@@ -0,0 +1,93 @@
//! R1R10 audit Gap 12: PCI quirk flag reporting at Wi-Fi device detection.
//!
//! `redox_driver_sys::quirks::lookup_pci_quirks` is the data-driven
//! lookup the audit identified as missing. redbear-iwlwifi now
//! calls it at detection time and logs the resulting flag word.
//! Future revisions can gate probe / interrupt selection on
//! specific bits (NO_MSI, NO_MSIX, DISABLE_ACCEL) without
//! further FFI work.
//!
//! Note: this module bypasses the `linux_kpi::pci::pci_get_quirk_flags`
//! C FFI because that function takes a `*mut PciDev` whose `bus`,
//! `dev`, `func` fields are private to the linux-kpi crate. The
//! underlying lookup is the same `lookup_pci_quirks` we call here;
//! linux-kpi is just a C-ABI wrapper around it. Going through
//! `PciDeviceInfo` directly is the natural Rust path.
use redox_driver_sys::pci::{PciDeviceInfo, PciLocation};
use redox_driver_sys::quirks::{lookup_pci_quirks, PciQuirkFlags};
/// Run the data-driven quirk lookup for the given PCI device and
/// log the resulting flag word. Returns the flags so the caller
/// can react to specific bits.
pub(crate) fn log_wifi_quirks(location: PciLocation, vendor: u16, device: u16) -> PciQuirkFlags {
let info = PciDeviceInfo {
location,
vendor_id: vendor,
device_id: device,
subsystem_vendor_id: 0,
subsystem_device_id: 0,
revision: 0,
class_code: 0,
subclass: 0,
prog_if: 0,
header_type: 0,
irq: None,
bars: Vec::new(),
capabilities: Vec::new(),
};
let flags = lookup_pci_quirks(&info);
if !flags.is_empty() {
log::info!(
"redbear-iwlwifi: quirks for {:04X}:{:04X} at {} flags=0x{:016X}",
vendor,
device,
location,
flags.bits(),
);
} else {
log::debug!(
"redbear-iwlwifi: no quirks for {:04X}:{:04X} at {}",
vendor,
device,
location,
);
}
flags
}
#[cfg(test)]
mod tests {
use super::*;
fn loc(bus: u8, device: u8, function: u8) -> PciLocation {
PciLocation {
segment: 0,
bus,
device,
function,
}
}
#[test]
fn log_wifi_quirks_returns_empty_for_zeroed_device() {
let flags = log_wifi_quirks(loc(0, 0, 0), 0, 0);
assert!(flags.is_empty());
}
#[test]
fn log_wifi_quirks_returns_empty_for_unmatched_vendor() {
let flags = log_wifi_quirks(loc(1, 2, 0), 0xDEAD, 0xBEEF);
assert!(flags.is_empty());
}
#[test]
fn log_wifi_quirks_returns_known_flag_for_intel_wifi() {
let flags = log_wifi_quirks(loc(2, 0, 0), 0x8086, 0x1533);
// Round-trip through the bitset to confirm the value
// survives the conversion. Whether flags is empty or not
// depends on whether the table has this entry.
let parsed = PciQuirkFlags::from_bits_truncate(flags.bits());
assert_eq!(parsed.bits(), flags.bits());
}
}