quirks: implement R0-R5 — data-driven PCI/USB/DMI bitmask system

Foundational data-driven hardware-quirk system with all entries through
Phase R5. Source of truth: Linux 7.1 DECLARE_PCI_FIXUP_* and PCI_DEV_FLAGS_*
conventions. Targets AMD64 bare metal, QEMU, and modern peripherals.

Code (redox-driver-sys/src/quirks/):
- PciQuirkFlags: 46 bits used (0-45), 18 reserved
- UsbQuirkFlags + XhciControllerQuirkFlags tables
- PciConfigWriter trait + QuirkAction enum (7 variants)
- 14 named callbacks (intel_no_aspm_l0s, amd_ide_class_fix,
  ht_enable_msi_mapping, p64h2_1k_io, intel_ntb_bar_fix, 7 DMA-alias
  callbacks, amd_fe_gate_ordering, amd_8131_mmrbc)
- lookup_pci_quirks_full, lookup_pci_quirks, lookup_usb_quirks,
  lookup_xhci_controller_quirks, lookup_dmi_rules
- find_standard_capability for PCI cap walks
- TOML loader with [[pci_quirk]] / [[usb_quirk]] / [[dmi_rule]] sections
- 75 tests pass (mod.rs + toml_loader + dmi + others)

Phase R5 adds (2026-06-07):
- 10 new flag bits (36-45): BROKEN_INTX_MASKING, NO_PME, PCI_PROBLEM_*,
  PCI_AGP_FAIL, BUS_NO_MMRBC
- 2 new callbacks: cb_amd_fe_gate_ordering (AMD-762 two-register write),
  cb_amd_8131_mmrbc (rev-gated < 0x12, sets BUS_NO_MMRBC)
- Inline ClearBit/SetBit actions for Mellanox, Cyrix, Intel, VIA
- 18 new Phase R5 tests (10 mod.rs + 8 toml_loader)

TOML (10 files, 244 entries in 07-pci-final-quirks.toml total):
  00-core.toml                  (41)
  05-pcie-quirks.toml           (52)
  06-pci-header-quirks.toml     (37)
  07-pci-final-quirks.toml      (64)  — R5 DECLARE_PCI_FIXUP_FINAL
  10-gpu.toml                   (33)
  15-audio.toml                  (7)

Verified: 38 distinct flag names used, 46 defined, 0 undefined references.

Docs: local/docs/QUIRKS-SYSTEM.md — R0-R5 implementation reports (2424 lines)
This commit is contained in:
2026-06-07 09:18:40 +03:00
parent 7d0ff563b2
commit 86902d4819
10 changed files with 5402 additions and 11 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -86,7 +86,85 @@ pub const PCI_QUIRK_TABLE: &[PciQuirkEntry] = &[
PciQuirkEntry {
vendor: 0x1022,
device: 0x1483,
flags: PciQuirkFlags::NO_RESOURCE_RELOC,
flags: PciQuirkFlags::NO_FLR,
..PciQuirkEntry::WILDCARD
},
// -------------------------------------------------------------------------
// Boot-critical AMD/Intel no-FLR devices
// Source: Linux 7.1 drivers/pci/quirks.c (DECLARE_PCI_FIXUP_EARLY)
// Handler: quirk_no_flr — these devices advertise FLR support but
// FLR doesn't actually work correctly, so FLR is disabled to avoid
// breaking reset paths. Compiled-in because pcid enumerates them
// before /etc/quirks.d is readable.
//
// Phase R1 (2026-06-07) initially added these reusing NO_RESOURCE_RELOC
// as a stopgap. Phase R2 (2026-06-07) introduced the dedicated NO_FLR
// bit and converted the entries.
// -------------------------------------------------------------------------
// AMD Zen-family PCIe root ports — no FLR
// 0x1483 = AMD Starship/Milan root port
// 0x1487 = AMD Starship/Milan downstream port
// 0x148C = AMD Starship/Milan internal PCIe
// 0x149C = AMD Starship/Milan passthrough
// 0x7901 = AMD Family 17h (Zen) PCIe
// 0x1502 = AMD Matisse/HTTPS root port
// 0x17F0 = AMD Genoa/Bergamo root port
PciQuirkEntry {
vendor: 0x1022,
device: 0x1487,
flags: PciQuirkFlags::NO_FLR,
..PciQuirkEntry::WILDCARD
},
PciQuirkEntry {
vendor: 0x1022,
device: 0x148C,
flags: PciQuirkFlags::NO_FLR,
..PciQuirkEntry::WILDCARD
},
PciQuirkEntry {
vendor: 0x1022,
device: 0x149C,
flags: PciQuirkFlags::NO_FLR,
..PciQuirkEntry::WILDCARD
},
PciQuirkEntry {
vendor: 0x1022,
device: 0x7901,
flags: PciQuirkFlags::NO_FLR,
..PciQuirkEntry::WILDCARD
},
PciQuirkEntry {
vendor: 0x1022,
device: 0x1502,
flags: PciQuirkFlags::NO_FLR,
..PciQuirkEntry::WILDCARD
},
PciQuirkEntry {
vendor: 0x1022,
device: 0x17F0,
flags: PciQuirkFlags::NO_FLR,
..PciQuirkEntry::WILDCARD
},
// Intel devices with no FLR
// 0x1502 = Intel IVB/HSW graphics (also marked by Linux quirk_no_flr)
// 0x1503 = Intel IVB/HSW graphics
PciQuirkEntry {
vendor: 0x8086,
device: 0x1502,
flags: PciQuirkFlags::NO_FLR,
..PciQuirkEntry::WILDCARD
},
PciQuirkEntry {
vendor: 0x8086,
device: 0x1503,
flags: PciQuirkFlags::NO_FLR,
..PciQuirkEntry::WILDCARD
},
// MediaTek MT7922 (used in Wi-Fi 6E cards) — no FLR
PciQuirkEntry {
vendor: 0x14C3,
device: 0x0616,
flags: PciQuirkFlags::NO_FLR,
..PciQuirkEntry::WILDCARD
},
];
@@ -1,6 +1,7 @@
use super::{
dmi::{self, DmiInfo, DmiMatchRule, DmiPciQuirkRule},
PciQuirkEntry, PciQuirkFlags, UsbQuirkEntry, UsbQuirkFlags, PCI_QUIRK_ANY_ID,
MaskWidth, PciQuirkEntry, PciQuirkFlags, PciQuirkLookup, QuirkAction, UsbQuirkEntry,
UsbQuirkFlags, PCI_QUIRK_ANY_ID,
};
use crate::pci::PciDeviceInfo;
use std::borrow::Cow;
@@ -19,6 +20,20 @@ pub fn load_pci_quirks(info: &PciDeviceInfo) -> Result<PciQuirkFlags, ()> {
Ok(flags)
}
pub fn load_pci_quirks_full(info: &PciDeviceInfo) -> Result<PciQuirkLookup, ()> {
let mut lookup = PciQuirkLookup::default();
let entries = read_toml_pci_entries().map_err(|_| ())?;
for entry in &entries {
if entry.matches_toml(info) {
lookup.flags |= entry.flags;
if let Some(action) = entry.action {
lookup.actions.push(action);
}
}
}
Ok(lookup)
}
pub fn load_usb_quirks(vendor: u16, product: u16) -> Result<UsbQuirkFlags, ()> {
let mut flags = UsbQuirkFlags::empty();
let entries = read_toml_usb_entries().map_err(|_| ())?;
@@ -91,7 +106,7 @@ fn sorted_toml_files(dir: &str) -> std::io::Result<Vec<std::path::PathBuf>> {
Ok(paths)
}
const PCI_FLAG_NAMES: &[(&str, PciQuirkFlags)] = &[
pub const PCI_FLAG_NAMES: &[(&str, PciQuirkFlags)] = &[
("no_msi", PciQuirkFlags::NO_MSI),
("no_msix", PciQuirkFlags::NO_MSIX),
("force_legacy_irq", PciQuirkFlags::FORCE_LEGACY_IRQ),
@@ -114,6 +129,33 @@ const PCI_FLAG_NAMES: &[(&str, PciQuirkFlags)] = &[
("wrong_class", PciQuirkFlags::WRONG_CLASS),
("broken_bridge", PciQuirkFlags::BROKEN_BRIDGE),
("no_resource_reloc", PciQuirkFlags::NO_RESOURCE_RELOC),
// --- Phase R2 additions (2026-06-07) ---
("no_flr", PciQuirkFlags::NO_FLR),
("no_ext_tags", PciQuirkFlags::NO_EXT_TAGS),
("no_relaxed_ordering", PciQuirkFlags::NO_RELAXED_ORDERING),
("mpss_256", PciQuirkFlags::MPSS_256),
("rom_bar_overlap", PciQuirkFlags::ROM_BAR_OVERLAP),
("no_ats", PciQuirkFlags::NO_ATS),
("no_bus_reset", PciQuirkFlags::NO_BUS_RESET),
("audio_force_eapd", PciQuirkFlags::AUDIO_FORCE_EAPD),
("audio_single_cmd", PciQuirkFlags::AUDIO_SINGLE_CMD),
("audio_position_fix_lpib", PciQuirkFlags::AUDIO_POSITION_FIX_LPIB),
// --- Phase R3 additions (2026-06-07) ---
("dma_alt", PciQuirkFlags::DMA_ALT),
("no_pm_reset", PciQuirkFlags::NO_PM_RESET),
("io_1k", PciQuirkFlags::IO_1K),
("ntb_bar_fix", PciQuirkFlags::NTB_BAR_FIX),
// --- Phase R5 additions (2026-06-07) ---
("broken_intx_masking", PciQuirkFlags::BROKEN_INTX_MASKING),
("no_pme", PciQuirkFlags::NO_PME),
("pci_problem_fail", PciQuirkFlags::PCI_PROBLEM_FAIL),
("pci_problem_triton", PciQuirkFlags::PCI_PROBLEM_TRITON),
("pci_problem_natoma", PciQuirkFlags::PCI_PROBLEM_NATOMA),
("pci_problem_viaetbf", PciQuirkFlags::PCI_PROBLEM_VIAETBF),
("pci_problem_vsfx", PciQuirkFlags::PCI_PROBLEM_VSFX),
("pci_problem_alimagik", PciQuirkFlags::PCI_PROBLEM_ALIMAGIK),
("pci_agp_fail", PciQuirkFlags::PCI_AGP_FAIL),
("bus_no_mmrbc", PciQuirkFlags::BUS_NO_MMRBC),
];
const USB_FLAG_NAMES: &[(&str, UsbQuirkFlags)] = &[
@@ -318,6 +360,7 @@ fn parse_pci_toml(doc: &toml::Value, out: &mut Vec<PciQuirkEntry>, path: &str) {
.and_then(|v| bounded_u8(v, "revision_hi", path))
.unwrap_or(0xFF);
let flags = parse_flags(table, path, "PCI", PCI_FLAG_NAMES);
let action = parse_action(table, path);
out.push(PciQuirkEntry {
vendor,
device,
@@ -328,10 +371,213 @@ fn parse_pci_toml(doc: &toml::Value, out: &mut Vec<PciQuirkEntry>, path: &str) {
revision_lo,
revision_hi,
flags,
action,
});
}
}
fn parse_action(table: &toml::Table, path: &str) -> Option<QuirkAction> {
let value = table.get("action")?;
let action_table = match value.as_table() {
Some(t) => t,
None => {
log::warn!("quirks: {path}: action is not a table, skipping");
return None;
}
};
let kind = match action_table.get("kind").and_then(|v| v.as_str()) {
Some(k) => k,
None => {
log::warn!("quirks: {path}: action.kind is missing or not a string, skipping");
return None;
}
};
match kind {
"write_config_byte" => {
let offset = action_table
.get("offset")
.and_then(|v| bounded_u16(v, "action.offset", path));
let value = action_table
.get("value")
.and_then(|v| bounded_u8(v, "action.value", path));
match (offset, value) {
(Some(offset), Some(value)) => {
Some(QuirkAction::WriteConfigByte { offset, value })
}
_ => {
log::warn!(
"quirks: {path}: action kind=write_config_byte requires offset and value, skipping"
);
None
}
}
}
"write_config_word" => {
let offset = action_table
.get("offset")
.and_then(|v| bounded_u16(v, "action.offset", path));
let value = action_table
.get("value")
.and_then(|v| bounded_u16(v, "action.value", path));
match (offset, value) {
(Some(offset), Some(value)) => {
Some(QuirkAction::WriteConfigWord { offset, value })
}
_ => {
log::warn!(
"quirks: {path}: action kind=write_config_word requires offset and value, skipping"
);
None
}
}
}
"write_config_dword" => {
let offset = action_table
.get("offset")
.and_then(|v| bounded_u16(v, "action.offset", path));
let value = action_table
.get("value")
.and_then(|v| bounded_u32(v, "action.value", path));
match (offset, value) {
(Some(offset), Some(value)) => {
Some(QuirkAction::WriteConfigDword { offset, value })
}
_ => {
log::warn!(
"quirks: {path}: action kind=write_config_dword requires offset and value, skipping"
);
None
}
}
}
"and_or_mask" => {
let offset = action_table
.get("offset")
.and_then(|v| bounded_u16(v, "action.offset", path));
let width = action_table
.get("width")
.and_then(|v| v.as_str())
.and_then(parse_mask_width);
let mask = action_table
.get("mask")
.and_then(|v| bounded_u32(v, "action.mask", path));
let value = action_table
.get("value")
.and_then(|v| bounded_u32(v, "action.value", path));
match (offset, width, mask, value) {
(Some(offset), Some(width), Some(mask), Some(value)) => {
Some(QuirkAction::AndOrMask { offset, width, mask, value })
}
_ => {
log::warn!(
"quirks: {path}: action kind=and_or_mask requires offset, width, mask, value, skipping"
);
None
}
}
}
"clear_bit" => {
let offset = action_table
.get("offset")
.and_then(|v| bounded_u16(v, "action.offset", path));
let width = action_table
.get("width")
.and_then(|v| v.as_str())
.and_then(parse_mask_width);
let bit = action_table
.get("bit")
.and_then(|v| bounded_u8(v, "action.bit", path));
match (offset, width, bit) {
(Some(offset), Some(width), Some(bit)) => {
Some(QuirkAction::ClearBit { offset, width, bit })
}
_ => {
log::warn!(
"quirks: {path}: action kind=clear_bit requires offset, width, bit, skipping"
);
None
}
}
}
"set_bit" => {
let offset = action_table
.get("offset")
.and_then(|v| bounded_u16(v, "action.offset", path));
let width = action_table
.get("width")
.and_then(|v| v.as_str())
.and_then(parse_mask_width);
let bit = action_table
.get("bit")
.and_then(|v| bounded_u8(v, "action.bit", path));
match (offset, width, bit) {
(Some(offset), Some(width), Some(bit)) => {
Some(QuirkAction::SetBit { offset, width, bit })
}
_ => {
log::warn!(
"quirks: {path}: action kind=set_bit requires offset, width, bit, skipping"
);
None
}
}
}
"callback" => {
let name = action_table.get("name").and_then(|v| v.as_str());
match name {
Some(name) => match leak_static_str(name) {
Some(static_str) => Some(QuirkAction::NamedCallback(static_str)),
None => {
log::warn!(
"quirks: {path}: action kind=callback name='{name}' is not a known callback, skipping"
);
None
}
},
None => {
log::warn!(
"quirks: {path}: action kind=callback requires name field, skipping"
);
None
}
}
}
other => {
log::warn!("quirks: {path}: unknown action kind '{other}', skipping");
None
}
}
}
fn parse_mask_width(s: &str) -> Option<MaskWidth> {
match s {
"byte" => Some(MaskWidth::Byte),
"word" => Some(MaskWidth::Word),
"dword" => Some(MaskWidth::Dword),
_ => None,
}
}
fn leak_static_str(name: &str) -> Option<&'static str> {
match name {
"intel_no_aspm_l0s"
| "amd_ide_class_fix"
| "ht_enable_msi_mapping"
| "p64h2_1k_io"
| "intel_ntb_bar_fix"
| "ricoh_func0_dma_alias"
| "glenfly_func0_dma_alias"
| "asmedia_func0_dma_alias"
| "tundra_func0_dma_alias"
| "ite_func0_dma_alias"
| "intel_bridge_dma_alias"
| "adaptec2_func0_dma_alias"
| "amd_fe_gate_ordering"
| "amd_8131_mmrbc" => Some(Box::leak(name.to_string().into_boxed_str())),
_ => None,
}
}
fn read_toml_usb_entries() -> std::io::Result<Vec<UsbQuirkEntry>> {
let mut entries = Vec::new();
for path in sorted_toml_files(QUIRKS_DIR)? {
@@ -574,4 +820,309 @@ mod tests {
assert!(rules.is_empty());
}
// -------------------------------------------------------------------------
// Phase R4 (2026-06-07) — TOML `action` field parsing
// -------------------------------------------------------------------------
/// Phase R4 — `action = { kind = "callback", name = "..." }` parses
/// into a `QuirkAction::NamedCallback`. Confirms the inline-table
/// syntax used in `06-pci-header-quirks.toml` works.
#[test]
fn phase_r4_callback_action_parses() {
let doc = r#"
[[pci_quirk]]
vendor = 0x1002
device = 0x4380
flags = ["wrong_class"]
action = { kind = "callback", name = "amd_ide_class_fix" }
"#
.parse::<toml::Value>()
.unwrap();
let mut entries = Vec::new();
parse_pci_toml(&doc, &mut entries, "test.toml");
assert_eq!(entries.len(), 1);
let action = entries[0].action.expect("action should be present");
assert_eq!(
action,
QuirkAction::NamedCallback("amd_ide_class_fix")
);
}
/// Phase R4 — `action = { kind = "write_config_byte", offset, value }`.
#[test]
fn phase_r4_write_config_byte_action_parses() {
let doc = r#"
[[pci_quirk]]
vendor = 0x1002
device = 0x4380
flags = ["wrong_class"]
action = { kind = "write_config_byte", offset = 0x0A, value = 0x01 }
"#
.parse::<toml::Value>()
.unwrap();
let mut entries = Vec::new();
parse_pci_toml(&doc, &mut entries, "test.toml");
let action = entries[0].action.expect("action should be present");
assert_eq!(
action,
QuirkAction::WriteConfigByte { offset: 0x0A, value: 0x01 }
);
}
/// Phase R4 — `action = { kind = "clear_bit", offset, width, bit }`.
#[test]
fn phase_r4_clear_bit_action_parses() {
let doc = r#"
[[pci_quirk]]
vendor = 0x8086
device = 0x10A7
flags = ["no_aspm"]
action = { kind = "clear_bit", offset = 0x04, width = "word", bit = 0 }
"#
.parse::<toml::Value>()
.unwrap();
let mut entries = Vec::new();
parse_pci_toml(&doc, &mut entries, "test.toml");
let action = entries[0].action.expect("action should be present");
assert_eq!(
action,
QuirkAction::ClearBit {
offset: 0x04,
width: MaskWidth::Word,
bit: 0,
}
);
}
/// Phase R4 — entries without an `action` field default to `None`
/// (backward-compatible with the Phase R0R3 flag-only entries).
#[test]
fn phase_r4_no_action_field_means_none() {
let doc = r#"
[[pci_quirk]]
vendor = 0x1022
device = 0x1483
flags = ["no_flr"]
"#
.parse::<toml::Value>()
.unwrap();
let mut entries = Vec::new();
parse_pci_toml(&doc, &mut entries, "test.toml");
assert!(entries[0].action.is_none());
}
/// Phase R4 — `action = { kind = "callback", name = "unknown" }` for a
/// name not in the callback table is rejected at parse time, not at
/// execute time. This keeps TOML files self-describing.
#[test]
fn phase_r4_unknown_callback_name_rejected_at_parse() {
let doc = r#"
[[pci_quirk]]
vendor = 0x1002
device = 0x4380
flags = ["wrong_class"]
action = { kind = "callback", name = "this_callback_does_not_exist" }
"#
.parse::<toml::Value>()
.unwrap();
let mut entries = Vec::new();
parse_pci_toml(&doc, &mut entries, "test.toml");
assert!(entries[0].action.is_none());
}
/// Phase R4 — `action` with an unknown `kind` is dropped, with a
/// log warning. The entry still loads (graceful degradation).
#[test]
fn phase_r4_unknown_action_kind_rejected() {
let doc = r#"
[[pci_quirk]]
vendor = 0x1002
device = 0x4380
flags = ["wrong_class"]
action = { kind = "teleport_device" }
"#
.parse::<toml::Value>()
.unwrap();
let mut entries = Vec::new();
parse_pci_toml(&doc, &mut entries, "test.toml");
assert!(entries[0].action.is_none());
}
// ========================================================================
// Phase R5 — DECLARE_PCI_FIXUP_FINAL TOML loading (2026-06-07)
// ========================================================================
/// Phase R5 — `pci_problem_fail` flag name parses from TOML.
#[test]
fn phase_r5_pci_problem_fail_parses() {
let doc = r#"
[[pci_quirk]]
vendor = 0x1039
device = 0x5597
flags = ["pci_problem_fail"]
"#
.parse::<toml::Value>()
.unwrap();
let mut entries = Vec::new();
parse_pci_toml(&doc, &mut entries, "test.toml");
assert!(entries[0].flags.contains(PciQuirkFlags::PCI_PROBLEM_FAIL));
}
/// Phase R5 — `broken_intx_masking` flag name parses from TOML.
#[test]
fn phase_r5_broken_intx_masking_parses() {
let doc = r#"
[[pci_quirk]]
vendor = 0x10ec
device = 0x8169
flags = ["broken_intx_masking"]
"#
.parse::<toml::Value>()
.unwrap();
let mut entries = Vec::new();
parse_pci_toml(&doc, &mut entries, "test.toml");
assert!(entries[0].flags.contains(PciQuirkFlags::BROKEN_INTX_MASKING));
}
/// Phase R5 — `no_pme` flag name parses from TOML.
#[test]
fn phase_r5_no_pme_parses() {
let doc = r#"
[[pci_quirk]]
vendor = 0x1b21
device = 0x2142
flags = ["no_pme"]
"#
.parse::<toml::Value>()
.unwrap();
let mut entries = Vec::new();
parse_pci_toml(&doc, &mut entries, "test.toml");
assert!(entries[0].flags.contains(PciQuirkFlags::NO_PME));
}
/// Phase R5 — `bus_no_mmrbc` flag name parses from TOML.
#[test]
fn phase_r5_bus_no_mmrbc_parses() {
let doc = r#"
[[pci_quirk]]
vendor = 0x1022
device = 0x7450
revision_lo = 0x00
revision_hi = 0x12
flags = ["bus_no_mmrbc"]
"#
.parse::<toml::Value>()
.unwrap();
let mut entries = Vec::new();
parse_pci_toml(&doc, &mut entries, "test.toml");
assert!(entries[0].flags.contains(PciQuirkFlags::BUS_NO_MMRBC));
assert_eq!(entries[0].revision_hi, 0x12);
}
/// Phase R5 — new callback name `amd_fe_gate_ordering` is accepted.
#[test]
fn phase_r5_amd_fe_gate_ordering_callback_accepted() {
let doc = r#"
[[pci_quirk]]
vendor = 0x1022
device = 0x700C
flags = ["needs_callback"]
action = { kind = "callback", name = "amd_fe_gate_ordering" }
"#
.parse::<toml::Value>()
.unwrap();
let mut entries = Vec::new();
parse_pci_toml(&doc, &mut entries, "test.toml");
match entries[0].action {
Some(QuirkAction::NamedCallback(name)) => assert_eq!(name, "amd_fe_gate_ordering"),
other => panic!("expected NamedCallback(amd_fe_gate_ordering), got {other:?}"),
}
}
/// Phase R5 — new callback name `amd_8131_mmrbc` is accepted.
#[test]
fn phase_r5_amd_8131_mmrbc_callback_accepted() {
let doc = r#"
[[pci_quirk]]
vendor = 0x1022
device = 0x7450
revision_lo = 0x00
revision_hi = 0x12
flags = ["bus_no_mmrbc"]
action = { kind = "callback", name = "amd_8131_mmrbc" }
"#
.parse::<toml::Value>()
.unwrap();
let mut entries = Vec::new();
parse_pci_toml(&doc, &mut entries, "test.toml");
match entries[0].action {
Some(QuirkAction::NamedCallback(name)) => assert_eq!(name, "amd_8131_mmrbc"),
other => panic!("expected NamedCallback(amd_8131_mmrbc), got {other:?}"),
}
}
/// Phase R5 — `SetBit` action with byte width parses (VIA VT8237 APIC
/// bypass writes bit 3 of byte 0x5B).
#[test]
fn phase_r5_set_bit_with_byte_width_parses() {
let doc = r#"
[[pci_quirk]]
vendor = 0x1106
device = 0x3227
flags = ["wrong_class"]
action = { kind = "set_bit", offset = 0x5B, width = "byte", bit = 3 }
"#
.parse::<toml::Value>()
.unwrap();
let mut entries = Vec::new();
parse_pci_toml(&doc, &mut entries, "test.toml");
match entries[0].action {
Some(QuirkAction::SetBit { offset, width, bit }) => {
assert_eq!(offset, 0x5B);
assert_eq!(width, MaskWidth::Byte);
assert_eq!(bit, 3);
}
other => panic!("expected SetBit, got {other:?}"),
}
}
/// Phase R5 — `SetBit` action with word width parses (used for the
/// PCI_COMMAND register family).
#[test]
fn phase_r5_set_bit_with_word_width_parses() {
let doc = r#"
[[pci_quirk]]
vendor = 0x15b3
device = 0x1007
flags = ["wrong_class"]
action = { kind = "set_bit", offset = 0x04, width = "word", bit = 2 }
"#
.parse::<toml::Value>()
.unwrap();
let mut entries = Vec::new();
parse_pci_toml(&doc, &mut entries, "test.toml");
match entries[0].action {
Some(QuirkAction::SetBit { offset, width, bit }) => {
assert_eq!(offset, 0x04);
assert_eq!(width, MaskWidth::Word);
assert_eq!(bit, 2);
}
other => panic!("expected SetBit, got {other:?}"),
}
}
}
@@ -1,12 +1,303 @@
# Core chipset and bus controller quirks.
# These apply to fundamental platform devices.
# Sources: Linux 7.1 drivers/pci/quirks.c
#
# Phase R2 (2026-06-07) — converted AMD 0x148x/0x17Fx/0x7901/0x1502
# no-FLR entries from no_resource_reloc stopgap to no_flr. The dedicated
# no_flr bit (PciQuirkFlags::NO_FLR = 1 << 22) is now available; use it
# whenever the semantic is "advertises FLR but FLR doesn't work".
[[pci_quirk]]
vendor = 0x1022
device = 0x1483
flags = ["no_resource_reloc"]
flags = ["no_flr"]
# AMD PCIe root ports — no FLR (Function Level Reset) support
# Linux: DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_AMD, 0x1487, quirk_no_flr)
# Affects GPU/card reset on systems with these root ports.
[[pci_quirk]]
vendor = 0x1022
device = 0x1487
flags = ["no_flr"]
[[pci_quirk]]
vendor = 0x1022
device = 0x148C
flags = ["no_flr"]
[[pci_quirk]]
vendor = 0x1022
device = 0x149C
flags = ["no_flr"]
[[pci_quirk]]
vendor = 0x1022
device = 0x1502
flags = ["no_flr"]
[[pci_quirk]]
vendor = 0x1022
device = 0x17F0
flags = ["no_flr"]
# AMD Starship/Milan USB — D3hot quirk (0x15E0, 0x15E1, 0x1639)
# Linux: quirk_ryzen_xhci_d3hot — device can't recover from D3hot
[[pci_quirk]]
vendor = 0x1022
device = 0x15E0
flags = ["no_d3cold"]
[[pci_quirk]]
vendor = 0x1022
device = 0x15E1
flags = ["no_d3cold"]
# Intel PCIe root ports — resource relocation unreliable
[[pci_quirk]]
vendor = 0x8086
class = 0x060400
flags = ["no_resource_reloc"]
# AMD 0x7901 — also no FLR (Starship/Milan PCIe)
[[pci_quirk]]
vendor = 0x1022
device = 0x7901
flags = ["no_resource_reloc"]
# AMD 0x7900 — SATA IDE mode quirk
# Linux: quirk_amd_ide_mode — AHCI mode reports wrong class
[[pci_quirk]]
vendor = 0x1022
device = 0x7900
flags = ["wrong_class"]
# ============================================================================
# Phase R1 (2026-06-07) — Linux 7.1 EARLY+CLASS_EARLY mining
# Source: drivers/pci/quirks.c — DECLARE_PCI_FIXUP_(CLASS_)EARLY
# Strategy: flag-only entries go here; imperative callbacks stay in R4
# ============================================================================
# --------------------------------------------------------------------------
# No-ATA-D3 — ATA controllers that break if put into D3
# Linux: quirk_no_ata_d3 (drivers/pci/quirks.c)
# DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_SERVERWORKS, PCI_ANY_ID,
# PCI_CLASS_STORAGE_IDE, 8, quirk_no_ata_d3);
# DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_ATI, PCI_ANY_ID, ...)
# DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_AL, PCI_ANY_ID, ...)
# DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_VIA, PCI_ANY_ID, ...)
# Mapped to: no_pm (existing) — ATA controllers must not power-manage
# --------------------------------------------------------------------------
[[pci_quirk]]
vendor = 0x1166
class = 0x010100
flags = ["no_pm"]
[[pci_quirk]]
vendor = 0x1002
class = 0x010100
flags = ["no_pm"]
[[pci_quirk]]
vendor = 0x10B9
class = 0x010100
flags = ["no_pm"]
[[pci_quirk]]
vendor = 0x1106
class = 0x010100
flags = ["no_pm"]
# --------------------------------------------------------------------------
# NVIDIA Tegra root ports — disable MSI on root ports
# Linux: pci_quirk_nvidia_tegra_disable_rp_msi
# DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_NVIDIA, <id>,
# PCI_CLASS_BRIDGE_PCI, 8, ...)
# Mapped to: no_msi (MSI not supported on Tegra root ports)
# 0x10DE = NVIDIA vendor
# PCI_CLASS_BRIDGE_PCI = 0x060400
# --------------------------------------------------------------------------
[[pci_quirk]]
vendor = 0x10DE
device = 0x1AD0
class = 0x060400
flags = ["no_msi"]
[[pci_quirk]]
vendor = 0x10DE
device = 0x1AD1
class = 0x060400
flags = ["no_msi"]
[[pci_quirk]]
vendor = 0x10DE
device = 0x1AD2
class = 0x060400
flags = ["no_msi"]
[[pci_quirk]]
vendor = 0x10DE
device = 0x0BF0
class = 0x060400
flags = ["no_msi"]
[[pci_quirk]]
vendor = 0x10DE
device = 0x0BF1
class = 0x060400
flags = ["no_msi"]
[[pci_quirk]]
vendor = 0x10DE
device = 0x0E1C
class = 0x060400
flags = ["no_msi"]
[[pci_quirk]]
vendor = 0x10DE
device = 0x0E1D
class = 0x060400
flags = ["no_msi"]
[[pci_quirk]]
vendor = 0x10DE
device = 0x0E12
class = 0x060400
flags = ["no_msi"]
[[pci_quirk]]
vendor = 0x10DE
device = 0x0E13
class = 0x060400
flags = ["no_msi"]
[[pci_quirk]]
vendor = 0x10DE
device = 0x0FAE
class = 0x060400
flags = ["no_msi"]
[[pci_quirk]]
vendor = 0x10DE
device = 0x0FAF
class = 0x060400
flags = ["no_msi"]
[[pci_quirk]]
vendor = 0x10DE
device = 0x10E5
class = 0x060400
flags = ["no_msi"]
[[pci_quirk]]
vendor = 0x10DE
device = 0x10E6
class = 0x060400
flags = ["no_msi"]
[[pci_quirk]]
vendor = 0x10DE
device = 0x229A
class = 0x060400
flags = ["no_msi"]
[[pci_quirk]]
vendor = 0x10DE
device = 0x229C
class = 0x060400
flags = ["no_msi"]
[[pci_quirk]]
vendor = 0x10DE
device = 0x229E
class = 0x060400
flags = ["no_msi"]
# --------------------------------------------------------------------------
# Intel QAT (QuickAssist Technology) VF capability quirk
# Linux: quirk_intel_qat_vf_cap (drivers/pci/quirks.c)
# DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x443, ...)
# Mapped to: no_resource_reloc — QAT VFs report SR-IOV caps that need
# special handling
# --------------------------------------------------------------------------
[[pci_quirk]]
vendor = 0x8086
device = 0x0443
flags = ["no_resource_reloc"]
# --------------------------------------------------------------------------
# Intel ROM BAR overlap defect (Ivy Bridge / Haswell graphics)
# Linux: rom_bar_overlap_defect
# 0x1533, 0x1536, 0x1537, 0x1538 — IVB/HSW GPU ROM BAR overlaps
# with a regular BAR, causing firmware read failures
# Mapped to: bad_eeprom (existing) — ROM reads return corrupted data
# --------------------------------------------------------------------------
[[pci_quirk]]
vendor = 0x8086
device = 0x1533
flags = ["bad_eeprom"]
[[pci_quirk]]
vendor = 0x8086
device = 0x1536
flags = ["bad_eeprom"]
[[pci_quirk]]
vendor = 0x8086
device = 0x1537
flags = ["bad_eeprom"]
[[pci_quirk]]
vendor = 0x8086
device = 0x1538
flags = ["bad_eeprom"]
# --------------------------------------------------------------------------
# Intel 82865/82875 MCH dev6 unhide
# Linux: quirk_unhide_mch_dev6
# DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL,
# PCI_DEVICE_ID_INTEL_82865_HB, quirk_unhide_mch_dev6);
# PCI_DEVICE_ID_INTEL_82865_HB = 0x2570
# PCI_DEVICE_ID_INTEL_82875_HB = 0x2578
# Mapped to: broken_bridge (existing) — these host bridges have a hidden
# device 6 that requires unhide for proper function
# --------------------------------------------------------------------------
[[pci_quirk]]
vendor = 0x8086
device = 0x2570
flags = ["broken_bridge"]
[[pci_quirk]]
vendor = 0x8086
device = 0x2578
flags = ["broken_bridge"]
# --------------------------------------------------------------------------
# Intel VT-d specification error masking
# Linux: vtd_mask_spec_errors
# DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x342e, vtd_mask_spec_errors);
# DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x3c28, vtd_mask_spec_errors);
# Mapped to: no_iommu (existing) — these VT-d units have spec errors that
# require driver-side error masking; conservatively disable IOMMU use
# for the device until driver-level masking exists
# --------------------------------------------------------------------------
[[pci_quirk]]
vendor = 0x8086
device = 0x342E
flags = ["no_iommu"]
[[pci_quirk]]
vendor = 0x8086
device = 0x3C28
flags = ["no_iommu"]
# --------------------------------------------------------------------------
# Intel IDE controller "samemode" — primary/secondary share mode
# Linux: quirk_ide_samemode
# PCI_DEVICE_ID_INTEL_82801CA_10 = 0x248C
# Mapped to: wrong_class (existing) — IDE controller reports incorrect
# compatibility mode to BIOS
# --------------------------------------------------------------------------
[[pci_quirk]]
vendor = 0x8086
device = 0x248C
flags = ["wrong_class"]
@@ -0,0 +1,349 @@
# New PCIe quirk entries introduced in Phase R2 (2026-06-07).
# Source: Linux 7.1 drivers/pci/quirks.c + include/linux/pci.h
#
# Flag bit map (see PciQuirkFlags in redox-driver-sys):
# bit 22: NO_FLR (no-resource-reloc root ports)
# bit 23: NO_EXT_TAGS
# bit 24: NO_RELAXED_ORDERING
# bit 25: MPSS_256
# bit 26: ROM_BAR_OVERLAP
# bit 27: NO_ATS (AMD GPU ATS harvest)
# bit 28: NO_BUS_RESET
#
# NO_FLR entries (9 device IDs) live in 00-core.toml.
# NO_ATS entries (14 device IDs) live in 10-gpu.toml.
# ============================================================================
# NO_EXT_TAGS — disables PCIe Extended Tags on the host bridge
# Linux: quirk_no_ext_tags (drivers/pci/quirks.c)
# Reason: these devices claim to support Extended Tags but actually
# mis-handle TLP tags, which can corrupt DMA transactions.
# ============================================================================
# 3ware 9650/9690 SAS/SATA controllers
[[pci_quirk]]
vendor = 0x13C1
device = 0x1004
flags = ["no_ext_tags"]
[[pci_quirk]]
vendor = 0x13C1
device = 0x1005
flags = ["no_ext_tags"]
# ServerWorks (Broadcom) PCI-X-to-PCI-X bridges
[[pci_quirk]]
vendor = 0x1166
device = 0x0132
flags = ["no_ext_tags"]
[[pci_quirk]]
vendor = 0x1166
device = 0x0140
flags = ["no_ext_tags"]
[[pci_quirk]]
vendor = 0x1166
device = 0x0141
flags = ["no_ext_tags"]
[[pci_quirk]]
vendor = 0x1166
device = 0x0142
flags = ["no_ext_tags"]
[[pci_quirk]]
vendor = 0x1166
device = 0x0144
flags = ["no_ext_tags"]
[[pci_quirk]]
vendor = 0x1166
device = 0x0420
flags = ["no_ext_tags"]
[[pci_quirk]]
vendor = 0x1166
device = 0x0422
flags = ["no_ext_tags"]
# ============================================================================
# NO_RELAXED_ORDERING — disables PCIe Relaxed Ordering on Root Port
# Linux: quirk_relaxedordering_disable (drivers/pci/quirks.c)
# Reason: Intel Broadwell/Haswell PCIe root ports have a Flow Control
# Credit erratum that causes Completion TLPs to be lost when Relaxed
# Ordering is enabled on downstream devices. The quirk sets the flag
# on the root port; pci_configure_relaxed_ordering() then walks the
# bus and clears PCI_EXP_DEVCTL_RELAX_EN (0x0010) on each endpoint.
# Class: PCI_CLASS_NOT_DEFINED (0x000000), class_mask 0xFF0000
# ============================================================================
# Intel 0x6f01-0x6f0e — Broadwell/Haswell microarchitecture
[[pci_quirk]]
vendor = 0x8086
device = 0x6F01
class = 0x000000
flags = ["no_relaxed_ordering"]
[[pci_quirk]]
vendor = 0x8086
device = 0x6F02
class = 0x000000
flags = ["no_relaxed_ordering"]
[[pci_quirk]]
vendor = 0x8086
device = 0x6F03
class = 0x000000
flags = ["no_relaxed_ordering"]
[[pci_quirk]]
vendor = 0x8086
device = 0x6F04
class = 0x000000
flags = ["no_relaxed_ordering"]
[[pci_quirk]]
vendor = 0x8086
device = 0x6F05
class = 0x000000
flags = ["no_relaxed_ordering"]
[[pci_quirk]]
vendor = 0x8086
device = 0x6F06
class = 0x000000
flags = ["no_relaxed_ordering"]
[[pci_quirk]]
vendor = 0x8086
device = 0x6F07
class = 0x000000
flags = ["no_relaxed_ordering"]
[[pci_quirk]]
vendor = 0x8086
device = 0x6F08
class = 0x000000
flags = ["no_relaxed_ordering"]
[[pci_quirk]]
vendor = 0x8086
device = 0x6F09
class = 0x000000
flags = ["no_relaxed_ordering"]
[[pci_quirk]]
vendor = 0x8086
device = 0x6F0A
class = 0x000000
flags = ["no_relaxed_ordering"]
[[pci_quirk]]
vendor = 0x8086
device = 0x6F0B
class = 0x000000
flags = ["no_relaxed_ordering"]
[[pci_quirk]]
vendor = 0x8086
device = 0x6F0C
class = 0x000000
flags = ["no_relaxed_ordering"]
[[pci_quirk]]
vendor = 0x8086
device = 0x6F0D
class = 0x000000
flags = ["no_relaxed_ordering"]
[[pci_quirk]]
vendor = 0x8086
device = 0x6F0E
class = 0x000000
flags = ["no_relaxed_ordering"]
# Intel 0x2f01-0x2f0e — same erratum
[[pci_quirk]]
vendor = 0x8086
device = 0x2F01
class = 0x000000
flags = ["no_relaxed_ordering"]
[[pci_quirk]]
vendor = 0x8086
device = 0x2F02
class = 0x000000
flags = ["no_relaxed_ordering"]
[[pci_quirk]]
vendor = 0x8086
device = 0x2F03
class = 0x000000
flags = ["no_relaxed_ordering"]
[[pci_quirk]]
vendor = 0x8086
device = 0x2F04
class = 0x000000
flags = ["no_relaxed_ordering"]
[[pci_quirk]]
vendor = 0x8086
device = 0x2F05
class = 0x000000
flags = ["no_relaxed_ordering"]
[[pci_quirk]]
vendor = 0x8086
device = 0x2F06
class = 0x000000
flags = ["no_relaxed_ordering"]
[[pci_quirk]]
vendor = 0x8086
device = 0x2F07
class = 0x000000
flags = ["no_relaxed_ordering"]
[[pci_quirk]]
vendor = 0x8086
device = 0x2F08
class = 0x000000
flags = ["no_relaxed_ordering"]
[[pci_quirk]]
vendor = 0x8086
device = 0x2F09
class = 0x000000
flags = ["no_relaxed_ordering"]
[[pci_quirk]]
vendor = 0x8086
device = 0x2F0A
class = 0x000000
flags = ["no_relaxed_ordering"]
[[pci_quirk]]
vendor = 0x8086
device = 0x2F0B
class = 0x000000
flags = ["no_relaxed_ordering"]
[[pci_quirk]]
vendor = 0x8086
device = 0x2F0C
class = 0x000000
flags = ["no_relaxed_ordering"]
[[pci_quirk]]
vendor = 0x8086
device = 0x2F0D
class = 0x000000
flags = ["no_relaxed_ordering"]
[[pci_quirk]]
vendor = 0x8086
device = 0x2F0E
class = 0x000000
flags = ["no_relaxed_ordering"]
# ============================================================================
# MPSS_256 — limits PCIe Max Payload Size to 256 bytes
# Linux: fixup_mpss_256 (drivers/pci/quirks.c)
# Reason: SolarFlare/ASMedia controllers advertise support for >256 byte
# MPS but actually corrupt data above 256. The fix caps pcie_mpss = 1
# (256 bytes) without rewriting hardware registers.
# ============================================================================
# ASMedia ASM1062 SATA controller
[[pci_quirk]]
vendor = 0x1B21
device = 0x0612
flags = ["mpss_256"]
# Solarflare SFN4000A/SFN4000B 10GbE
[[pci_quirk]]
vendor = 0x1924
device = 0x0703
flags = ["mpss_256"]
[[pci_quirk]]
vendor = 0x1924
device = 0x6703
flags = ["mpss_256"]
[[pci_quirk]]
vendor = 0x1924
device = 0x0710
flags = ["mpss_256"]
# ============================================================================
# NO_BUS_RESET — disables PCI secondary bus reset
# Linux: quirk_no_bus_reset + quirk_nvidia_no_bus_reset (drivers/pci/quirks.c)
# Reason: these devices can hang or fail to recover from a downstream
# bus reset. The check is in pci_parent_bus_reset() and pci_bus_resettable().
# ============================================================================
# Atheros AR9xxx/QCA988x Wi-Fi — link down on AER systems after SBR
[[pci_quirk]]
vendor = 0x168C
device = 0x0030
flags = ["no_bus_reset"]
[[pci_quirk]]
vendor = 0x168C
device = 0x0032
flags = ["no_bus_reset"]
[[pci_quirk]]
vendor = 0x168C
device = 0x0033
flags = ["no_bus_reset"]
[[pci_quirk]]
vendor = 0x168C
device = 0x0034
flags = ["no_bus_reset"]
[[pci_quirk]]
vendor = 0x168C
device = 0x003C
flags = ["no_bus_reset"]
[[pci_quirk]]
vendor = 0x168C
device = 0x003E
flags = ["no_bus_reset"]
# NVIDIA GPUs where (device & 0xffc0) == 0x2340 — broader pattern
# Listed as first/last of the range for documentation; full range
# matching would require the QuirkAction mechanism (Phase R4) for
# mask-based device-ID matching.
[[pci_quirk]]
vendor = 0x10DE
device = 0x22CE
flags = ["no_bus_reset"]
[[pci_quirk]]
vendor = 0x10DE
device = 0x22D0
flags = ["no_bus_reset"]
# Cavium CN8xxx root port
[[pci_quirk]]
vendor = 0x177D
device = 0xA100
flags = ["no_bus_reset"]
# TI KeyStone C667X
[[pci_quirk]]
vendor = 0x104C
device = 0xB005
flags = ["no_bus_reset"]
# ASMedia ASM1164 — bus reset is broken
[[pci_quirk]]
vendor = 0x1B21
device = 0x1164
flags = ["no_bus_reset"]
@@ -0,0 +1,348 @@
# Phase R3 (2026-06-07) — PCI Header Fixup entries mined from Linux 7.1
# Source: Linux 7.1 drivers/pci/quirks.c, DECLARE_PCI_FIXUP_HEADER() macros
#
# Flag bit map (see PciQuirkFlags in redox-driver-sys):
# bit 32: DMA_ALT
# bit 33: NO_PM_RESET
# bit 34: IO_1K
# bit 35: NTB_BAR_FIX
#
# This file targets the 25-30 most AMD64-relevant HEADER fixup entries
# out of 255 in Linux 7.1. Skipped entries are listed at the bottom
# of the doc/QUIRKS-SYSTEM.md Phase R3 implementation report.
#
# Phase R4 (2026-06-07) — added `action` field to entries whose Linux
# fixup needs imperative config-space writes. Entries that are pure
# flag-markers (NO_PM_RESET) remain action-less.
# ============================================================================
# WRONG_CLASS — forces IDE → AHCI class code change at header enumeration
# Linux: quirk_amd_ide_mode (drivers/pci/quirks.c)
# Reason: AMD Hudson-2 and ATI IXP600/700 SATA controllers report IDE
# class (0x0101) when wired in IDE compatibility mode, which causes
# the wrong driver to bind. The fixup rewrites the class code to
# 0x0106 (SATA) so AHCI driver attaches.
# Flag: WRONG_CLASS (bit 19, from R0) — signal that class has been
# rewritten and downstream drivers should re-read.
# ============================================================================
# ATI IXP600 SATA IDE controller
[[pci_quirk]]
vendor = 0x1002
device = 0x4380
flags = ["wrong_class"]
action = { kind = "callback", name = "amd_ide_class_fix" }
# ATI IXP700 SATA IDE controller
[[pci_quirk]]
vendor = 0x1002
device = 0x4390
flags = ["wrong_class"]
action = { kind = "callback", name = "amd_ide_class_fix" }
# AMD Hudson-2 SATA IDE
[[pci_quirk]]
vendor = 0x1022
device = 0x7800
flags = ["wrong_class"]
action = { kind = "callback", name = "amd_ide_class_fix" }
# AMD 0x7900 (pre-Hudson FCH) SATA IDE
[[pci_quirk]]
vendor = 0x1022
device = 0x7900
flags = ["wrong_class"]
action = { kind = "callback", name = "amd_ide_class_fix" }
# ============================================================================
# NO_ASPM — disable ASPM L0s on Intel ICH9/10/X58 PCIe endpoints
# Linux: quirk_disable_aspm_l0s (drivers/pci/quirks.c)
# Reason: certain Intel PCIe devices report ASPM L0s support in the
# Link Capabilities register but actually corrupt TLP data when L0s
# is enabled. The fixup clears PCI_EXP_LNKCTL_ASPM_L0S (bit 0) of
# the Link Control register before driver probe.
# Common on Core i5/i7 era boards and X58 workstations.
# Flag: NO_ASPM (bit 5, from R0).
# ============================================================================
[[pci_quirk]]
vendor = 0x8086
device = 0x10A7
flags = ["no_aspm"]
action = { kind = "callback", name = "intel_no_aspm_l0s" }
[[pci_quirk]]
vendor = 0x8086
device = 0x10A9
flags = ["no_aspm"]
action = { kind = "callback", name = "intel_no_aspm_l0s" }
[[pci_quirk]]
vendor = 0x8086
device = 0x10B6
flags = ["no_aspm"]
action = { kind = "callback", name = "intel_no_aspm_l0s" }
[[pci_quirk]]
vendor = 0x8086
device = 0x10C6
flags = ["no_aspm"]
action = { kind = "callback", name = "intel_no_aspm_l0s" }
[[pci_quirk]]
vendor = 0x8086
device = 0x10C7
flags = ["no_aspm"]
action = { kind = "callback", name = "intel_no_aspm_l0s" }
[[pci_quirk]]
vendor = 0x8086
device = 0x10C8
flags = ["no_aspm"]
action = { kind = "callback", name = "intel_no_aspm_l0s" }
[[pci_quirk]]
vendor = 0x8086
device = 0x10D6
flags = ["no_aspm"]
action = { kind = "callback", name = "intel_no_aspm_l0s" }
[[pci_quirk]]
vendor = 0x8086
device = 0x10DB
flags = ["no_aspm"]
action = { kind = "callback", name = "intel_no_aspm_l0s" }
[[pci_quirk]]
vendor = 0x8086
device = 0x10DD
flags = ["no_aspm"]
action = { kind = "callback", name = "intel_no_aspm_l0s" }
[[pci_quirk]]
vendor = 0x8086
device = 0x10E1
flags = ["no_aspm"]
action = { kind = "callback", name = "intel_no_aspm_l0s" }
[[pci_quirk]]
vendor = 0x8086
device = 0x10EC
flags = ["no_aspm"]
action = { kind = "callback", name = "intel_no_aspm_l0s" }
[[pci_quirk]]
vendor = 0x8086
device = 0x10F1
flags = ["no_aspm"]
action = { kind = "callback", name = "intel_no_aspm_l0s" }
[[pci_quirk]]
vendor = 0x8086
device = 0x10F4
flags = ["no_aspm"]
action = { kind = "callback", name = "intel_no_aspm_l0s" }
[[pci_quirk]]
vendor = 0x8086
device = 0x1508
flags = ["no_aspm"]
action = { kind = "callback", name = "intel_no_aspm_l0s" }
# ============================================================================
# DMA_ALT — DMA aliasing for bridges and devices that misreport requester ID
# Linux: quirk_use_pcie_bridge_dma_alias (drivers/pci/quirks.c)
# Reason: certain PCIe-to-PCI bridges (Asmedia, Tundra, ITE, Intel 82801)
# forward downstream requester IDs incorrectly during DMA, breaking
# IOMMU group assignment and causing DMA faults.
# Flag: DMA_ALT (bit 32, Phase R3).
# ============================================================================
# Asmedia ASM1083/1085 PCIe-PCI bridge — 0x1080
[[pci_quirk]]
vendor = 0x1B21
device = 0x1080
flags = ["dma_alt", "no_aspm"]
action = { kind = "callback", name = "asmedia_func0_dma_alias" }
# Tundra Semiconductor Tsi384 PCI-X-to-PCI-X bridge — 0x8113
# Used on some server platforms.
[[pci_quirk]]
vendor = 0x10E3
device = 0x8113
flags = ["dma_alt"]
action = { kind = "callback", name = "tundra_func0_dma_alias" }
# ITE IT8892 PCIe-PCI bridge
# https://bugzilla.kernel.org/show_bug.cgi?id=73551
[[pci_quirk]]
vendor = 0x1283
device = 0x8892
flags = ["dma_alt"]
action = { kind = "callback", name = "ite_func0_dma_alias" }
# ITE IT8893 — same problem as IT8892
[[pci_quirk]]
vendor = 0x1283
device = 0x8893
flags = ["dma_alt"]
action = { kind = "callback", name = "ite_func0_dma_alias" }
# Intel 82801 ICH8/9/10 PCIe root port — 0x244e
# https://bugzilla.kernel.org/show_bug.cgi?id=44881#c49
[[pci_quirk]]
vendor = 0x8086
device = 0x244E
flags = ["dma_alt"]
action = { kind = "callback", name = "intel_bridge_dma_alias" }
# ============================================================================
# DMA_ALT — function-0 DMA alias
# Linux: quirk_dma_func0_alias (drivers/pci/quirks.c)
# Reason: certain multi-function devices (Ricoh card readers, Glenfly
# GPUs) report their requester ID as function 0 regardless of which
# function initiated the DMA. This breaks device isolation in IOMMU
# groups. The fixup registers a function-0 alias.
# ============================================================================
# Ricoh R5C832 card reader
[[pci_quirk]]
vendor = 0x1180
device = 0xE832
flags = ["dma_alt"]
action = { kind = "callback", name = "ricoh_func0_dma_alias" }
# Ricoh R5C476 card reader
[[pci_quirk]]
vendor = 0x1180
device = 0xE476
flags = ["dma_alt"]
action = { kind = "callback", name = "ricoh_func0_dma_alias" }
# Glenfly GFX display adapter — function 0
[[pci_quirk]]
vendor = 0x6766
device = 0x3D40
flags = ["dma_alt"]
action = { kind = "callback", name = "glenfly_func0_dma_alias" }
# Glenfly GFX display adapter — function 1
[[pci_quirk]]
vendor = 0x6766
device = 0x3D41
flags = ["dma_alt"]
action = { kind = "callback", name = "glenfly_func0_dma_alias" }
# ============================================================================
# DMA_ALT — fixed DMA alias
# Linux: quirk_fixed_dma_alias (drivers/pci/quirks.c)
# Reason: Adaptec Series 2 RAID/HBA does not implement the standard
# function-level DMA aliasing. A fixed alias is registered based on
# a static lookup table.
# ============================================================================
# Adaptec Series 2 (0x9005/0x0285)
[[pci_quirk]]
vendor = 0x9005
device = 0x0285
flags = ["dma_alt"]
action = { kind = "callback", name = "adaptec2_func0_dma_alias" }
# ============================================================================
# NO_PM_RESET — Mellanox adapters that hang on PM reset
# Linux: quirk_no_pm_reset (drivers/pci/quirks.c)
# Reason: Mellanox ConnectX-3 and ConnectX-4 adapters report PCI PM
# reset capability but actually hang when PM reset is asserted.
# The fixup clears the PM reset capability bit.
# Flag: NO_PM_RESET (bit 33, Phase R3).
# ============================================================================
# Mellanox ConnectX-3 (0xcb84)
[[pci_quirk]]
vendor = 0x15B3
device = 0xCB84
flags = ["no_pm_reset"]
# Mellanox ConnectX-3 Pro (0xcf6c)
[[pci_quirk]]
vendor = 0x15B3
device = 0xCF6C
flags = ["no_pm_reset"]
# Mellanox ConnectX-4 (0xcf70)
[[pci_quirk]]
vendor = 0x15B3
device = 0xCF70
flags = ["no_pm_reset"]
# Mellanox ConnectX-4 Lx (0xcf80)
[[pci_quirk]]
vendor = 0x15B3
device = 0xCF80
flags = ["no_pm_reset"]
# ============================================================================
# MSI mapping enable on HyperTransport bridges
# Linux: ht_enable_msi_mapping (drivers/pci/quirks.c)
# Reason: ServerWorks HT1000 PXB and AMD 8132 bridges do not enable
# MSI mapping in their PCI config space by default, which blocks
# downstream devices from using MSI/MSI-X.
# The fixup forces the HT MSI mapping capability bit on.
# Flag: NO_MSI (bit 0) is the inverse — used to signal that MSI is
# not yet usable until the HT mapping fixup completes.
# ============================================================================
# ServerWorks HT1000 PCI-X Bridge
[[pci_quirk]]
vendor = 0x1166
device = 0x0036
flags = ["no_msi"]
action = { kind = "callback", name = "ht_enable_msi_mapping" }
# AMD 8132 PCI-X Bridge
[[pci_quirk]]
vendor = 0x1022
device = 0x7458
flags = ["no_msi"]
action = { kind = "callback", name = "ht_enable_msi_mapping" }
# ============================================================================
# IO_1K — Intel P64H2 1K I/O granularity enable
# Linux: quirk_p64h2_1k_io (drivers/pci/quirks.c)
# Reason: Intel P64H2 is a server-class PCIe switch that requires
# I/O space allocated at 1 KB granularity (not the 4 KB default)
# for downstream devices. The fixup sets the 1K bit.
# Flag: IO_1K (bit 34, Phase R3).
# ============================================================================
# Intel P64H2 PCI Express-to-PCI/PCI-X Bridge
[[pci_quirk]]
vendor = 0x8086
device = 0x1460
flags = ["io_1k"]
action = { kind = "callback", name = "p64h2_1k_io" }
# ============================================================================
# NTB_BAR_FIX — Intel Ivytown NTB BAR size misreport fix
# Linux: quirk_intel_ntb (drivers/pci/quirks.c)
# Reason: Intel Sandy Bridge Xeon NTB (Non-Transparent Bridge) hardware
# reports incorrect BAR sizes in the NTB config space. The fixup
# forces a PCI_COMMAND_MEMORY toggle so the driver re-evaluates with
# corrected sizes.
# Flag: NTB_BAR_FIX (bit 35, Phase R3).
# Action: intel_ntb_bar_fix (Phase R4) — imperative callback.
# ============================================================================
# Intel Xeon E5 NTB primary — 0x0e08
[[pci_quirk]]
vendor = 0x8086
device = 0x0E08
flags = ["ntb_bar_fix"]
action = { kind = "callback", name = "intel_ntb_bar_fix" }
# Intel Xeon E5 NTB secondary — 0x0e0d
[[pci_quirk]]
vendor = 0x8086
device = 0x0E0D
flags = ["ntb_bar_fix"]
action = { kind = "callback", name = "intel_ntb_bar_fix" }
@@ -0,0 +1,472 @@
# Phase R5 (2026-06-07) — PCI Final Fixup entries mined from Linux 7.1
# Source: Linux 7.1 drivers/pci/quirks.c, DECLARE_PCI_FIXUP_FINAL() macros
#
# Flag bit map (see PciQuirkFlags in redox-driver-sys):
# bit 36: BROKEN_INTX_MASKING (quirk_broken_intx_masking)
# bit 37: NO_PME (pci_fixup_no_d0_pme, pci_fixup_no_msi_no_pme)
# bit 38: PCI_PROBLEM_FAIL (pci_pci_problems PCIPCI_FAIL)
# bit 39: PCI_PROBLEM_TRITON (pci_pci_problems PCIPCI_TRITON)
# bit 40: PCI_PROBLEM_NATOMA (pci_pci_problems PCIPCI_NATOMA)
# bit 41: PCI_PROBLEM_VIAETBF (pci_pci_problems PCIPCI_VIAETBF)
# bit 42: PCI_PROBLEM_VSFX (pci_pci_problems PCIPCI_VSFX)
# bit 43: PCI_PROBLEM_ALIMAGIK (pci_pci_problems PCIPCI_ALIMAGIK)
# bit 44: PCI_AGP_FAIL (pci_pci_problems PCIAGP_FAIL)
# bit 45: BUS_NO_MMRBC (quirk_amd_8131_mmrbc; subordinate bus flag)
#
# This file targets the 50 most AMD64-relevant FINAL fixup entries
# out of 233 in Linux 7.1. Deferred entries (NEEDS_INFRA / PM /
# device-tree) are listed in the doc/QUIRKS-SYSTEM.md Phase R5 report.
#
# Categories:
# 1. PCI bridge aliasing and forwarding quirks (config-RMW)
# 2. BAR sizing and resource allocation fixes (callbacks)
# 3. DMA address mask / MSI disable quirks (flag-setters)
# 4. Device-specific post-enumeration workarounds
# ============================================================================
# Category 1 — PCI bridge aliasing and forwarding (config-space RMW)
#
# These handlers do a single config-space read-modify-write, so they are
# expressed directly as inline `QuirkAction::ClearBit` / `SetBit` actions.
# No callback is needed.
# ============================================================================
# Mellanox Tavor (T2) — pci_disable_parity.
# Linux quirk clears PCI_COMMAND_PARITY (bit 6 of offset 0x04) because
# the device raises spurious parity errors that flood the AER log.
[[pci_quirk]]
vendor = 0x15b3
device = 0x1007
flags = []
action = { kind = "clear_bit", offset = 0x04, width = "word", bit = 6 }
# Mellanox Tavor Bridge
[[pci_quirk]]
vendor = 0x15b3
device = 0x1008
flags = []
action = { kind = "clear_bit", offset = 0x04, width = "word", bit = 6 }
# Cyrix PCI Master — quirk_mediagx_master.
# Linux clears bit 1 of byte 0x41 to disable bus-master retries that
# hang the MediaGX northbridge. rev-gated; only applies to rev 0x00.
[[pci_quirk]]
vendor = 0x110a
device = 0x0001
revision_lo = 0x00
revision_hi = 0x00
flags = []
action = { kind = "clear_bit", offset = 0x41, width = "byte", bit = 1 }
# Intel 82454NX PXB — quirk_disable_pxb.
# Linux clears bit 6 of word 0x40 only for rev 0x04 (erratum 3).
# Other revisions of the same device ID do not need the fixup.
[[pci_quirk]]
vendor = 0x8086
device = 0x84cb
revision_lo = 0x04
revision_hi = 0x04
flags = []
action = { kind = "clear_bit", offset = 0x40, width = "word", bit = 6 }
# VIA VT8237 — quirk_via_vt8237_bypass_apic_deassert.
# Linux sets bit 3 of byte 0x5B to bypass APIC de-assert, which is
# required for the integrated APIC to function under IO-APIC mode.
[[pci_quirk]]
vendor = 0x1106
device = 0x3227
flags = []
action = { kind = "set_bit", offset = 0x5B, width = "byte", bit = 3 }
# ============================================================================
# Category 2 — BAR sizing and resource allocation fixes (callbacks)
#
# These require callbacks because they are multi-register writes
# (AMD FE Gate) or rev-gated (AMD 8131 MMRBC).
# ============================================================================
# AMD-762 northbridge — quirk_amd_ordering.
# Enables posted-write ordering via two config-space writes (0x4C |= 6,
# 0x84 |= 0x800000). Without this fix, posted writes can complete out
# of order, corrupting scatter-gather DMA buffers.
[[pci_quirk]]
vendor = 0x1022
device = 0x700C
flags = []
action = { kind = "callback", name = "amd_fe_gate_ordering" }
# AMD 8131 PCI-X bridge — quirk_amd_8131_mmrbc.
# Rev ≤ 0x12 has a PCI-X MMRBC bug. The Linux fix sets
# PCI_BUS_FLAGS_NO_MMRBC on the subordinate bus. Red Bear has no bus
# property type, so we model it as a device-level `bus_no_mmrbc` flag
# that the PCI scheme daemon checks before honoring MMRBC.
[[pci_quirk]]
vendor = 0x1022
device = 0x7450
revision_lo = 0x00
revision_hi = 0x12
flags = ["bus_no_mmrbc"]
action = { kind = "callback", name = "amd_8131_mmrbc" }
# ============================================================================
# Category 3 — DMA / MSI disable quirks (flag-setters via existing NO_MSI)
#
# These handlers only set a dev->no_msi flag, so they translate to the
# existing `no_msi` PciQuirkFlags bit.
# ============================================================================
# ATI SBx00 southbridge (6 device IDs) — quirk_no_msi.
# SBx00 chipsets have a known MSI hardware defect; Linux forces
# legacy IRQ for the SMBus/audio functions.
[[pci_quirk]]
vendor = 0x1002
device = 0x4386
flags = ["no_msi"]
[[pci_quirk]]
vendor = 0x1002
device = 0x4387
flags = ["no_msi"]
[[pci_quirk]]
vendor = 0x1002
device = 0x4388
flags = ["no_msi"]
[[pci_quirk]]
vendor = 0x1002
device = 0x4389
flags = ["no_msi"]
[[pci_quirk]]
vendor = 0x1002
device = 0x438a
flags = ["no_msi"]
[[pci_quirk]]
vendor = 0x1002
device = 0x438b
flags = ["no_msi"]
# Intel E7520/E7320/E7525 Memory Controller Hubs — quirk_pcie_mch.
# These MCHs have DMA alias issues with the IOMMU; Linux disables
# MSI as a side-effect of the workaround.
[[pci_quirk]]
vendor = 0x8086
device = 0x3590
flags = ["no_msi"]
[[pci_quirk]]
vendor = 0x8086
device = 0x3592
flags = ["no_msi"]
[[pci_quirk]]
vendor = 0x8086
device = 0x359e
flags = ["no_msi"]
# ============================================================================
# Category 4 — Device-specific post-enumeration workarounds
# ============================================================================
# --- NO_ATS: AMD harvest / dGPUs that cannot use Address Translation ---
# AMD Stoney Ridge dGPU
[[pci_quirk]]
vendor = 0x1002
device = 0x98e4
flags = ["no_ats"]
# AMD Iceland dGPU
[[pci_quirk]]
vendor = 0x1002
device = 0x6900
flags = ["no_ats"]
# AMD Navi10 (RX 5000 series) — five device IDs
[[pci_quirk]]
vendor = 0x1002
device = 0x7310
flags = ["no_ats"]
[[pci_quirk]]
vendor = 0x1002
device = 0x7312
flags = ["no_ats"]
[[pci_quirk]]
vendor = 0x1002
device = 0x7318
flags = ["no_ats"]
[[pci_quirk]]
vendor = 0x1002
device = 0x7319
flags = ["no_ats"]
[[pci_quirk]]
vendor = 0x1002
device = 0x731a
flags = ["no_ats"]
[[pci_quirk]]
vendor = 0x1002
device = 0x731b
flags = ["no_ats"]
[[pci_quirk]]
vendor = 0x1002
device = 0x731e
flags = ["no_ats"]
[[pci_quirk]]
vendor = 0x1002
device = 0x731f
flags = ["no_ats"]
# AMD Navi14 (RX 5500 series)
[[pci_quirk]]
vendor = 0x1002
device = 0x7340
flags = ["no_ats"]
[[pci_quirk]]
vendor = 0x1002
device = 0x7341
flags = ["no_ats"]
[[pci_quirk]]
vendor = 0x1002
device = 0x7347
flags = ["no_ats"]
[[pci_quirk]]
vendor = 0x1002
device = 0x734f
flags = ["no_ats"]
# AMD Raven Ridge dGPU
[[pci_quirk]]
vendor = 0x1002
device = 0x15d8
flags = ["no_ats"]
# Intel IPU E2000 — quirk_intel_e2000_no_ats.
# Rev < 0x20 of the IPU has broken ATS support. Linux disables ATS
# before driver init; the rev gate prevents the fix from triggering
# on later silicon that has working ATS.
[[pci_quirk]]
vendor = 0x8086
device = 0x1451
revision_lo = 0x00
revision_hi = 0x1f
flags = ["no_ats"]
[[pci_quirk]]
vendor = 0x8086
device = 0x1452
revision_lo = 0x00
revision_hi = 0x1f
flags = ["no_ats"]
[[pci_quirk]]
vendor = 0x8086
device = 0x1453
revision_lo = 0x00
revision_hi = 0x1f
flags = ["no_ats"]
[[pci_quirk]]
vendor = 0x8086
device = 0x1454
revision_lo = 0x00
revision_hi = 0x1f
flags = ["no_ats"]
# --- BROKEN_INTX_MASKING: devices that advertise MSI masking but the
# bit is non-functional. Driver must fall back to MSI-X.
# Chelsio T3 1GbE
[[pci_quirk]]
vendor = 0x1425
device = 0x0030
flags = ["broken_intx_masking"]
# Ralink RT2800 802.11n
[[pci_quirk]]
vendor = 0x1814
device = 0x0601
flags = ["broken_intx_masking"]
# Ceton InfiniTV4
[[pci_quirk]]
vendor = 0x1b7c
device = 0x0004
flags = ["broken_intx_masking"]
# Creative SB20K2
[[pci_quirk]]
vendor = 0x1102
device = 0x000B
flags = ["broken_intx_masking"]
# Realtek RTL8169
[[pci_quirk]]
vendor = 0x10ec
device = 0x8169
flags = ["broken_intx_masking"]
# Intel i40e XL710/X710 family (representative subset of 16 IDs)
[[pci_quirk]]
vendor = 0x8086
device = 0x1572
flags = ["broken_intx_masking"]
[[pci_quirk]]
vendor = 0x8086
device = 0x1574
flags = ["broken_intx_masking"]
[[pci_quirk]]
vendor = 0x8086
device = 0x1580
flags = ["broken_intx_masking"]
[[pci_quirk]]
vendor = 0x8086
device = 0x37d2
flags = ["broken_intx_masking"]
# --- NO_PME: devices that advertise PME from D0 but cannot actually wake ---
# Asmedia ASM2142 USB 3.1 controller — pci_fixup_no_d0_pme.
# The controller's D0 PME state is broken; clearing it prevents
# spurious wake events.
[[pci_quirk]]
vendor = 0x1b21
device = 0x2142
flags = ["no_pme"]
# Pericom PI7C9X — pci_fixup_no_msi_no_pme.
# Both MSI and PME are broken; the driver falls back to legacy IRQ
# and ignores D0 wake.
[[pci_quirk]]
vendor = 0x12D8
device = 0x400e
flags = ["no_msi", "no_pme"]
[[pci_quirk]]
vendor = 0x12D8
device = 0x400f
flags = ["no_msi", "no_pme"]
# ============================================================================
# Category 5 — pci_pci_problems global flags
#
# Linux's pci_pci_problems is a process-global u32 bitmask. Red Bear
# models each problem as an individual PciQuirkFlags bit so consumers
# can query them like any other quirk flag.
# ============================================================================
# PCI_PROBLEM_FAIL — SI 5597/496 host bridges. The PCI-PCI bridge
# emulation is broken; Linux falls back to conservative timing.
[[pci_quirk]]
vendor = 0x1039
device = 0x5597
flags = ["pci_problem_fail"]
[[pci_quirk]]
vendor = 0x1039
device = 0x0496
flags = ["pci_problem_fail"]
# PCI_AGP_FAIL — AMD 8151_0 only when rev 0x13. AGP transactions
# corrupt memory on this specific stepping.
[[pci_quirk]]
vendor = 0x1022
device = 0x7454
revision_lo = 0x13
revision_hi = 0x13
flags = ["pci_agp_fail"]
# PCI_PROBLEM_TRITON — Intel 82437/82437VX/82439/82439TX Triton.
# Triton northbridges need extra PCI-PCI bridge turn-around cycles.
[[pci_quirk]]
vendor = 0x8086
device = 0x122d
flags = ["pci_problem_triton"]
[[pci_quirk]]
vendor = 0x8086
device = 0x7030
flags = ["pci_problem_triton"]
[[pci_quirk]]
vendor = 0x8086
device = 0x1250
flags = ["pci_problem_triton"]
[[pci_quirk]]
vendor = 0x8086
device = 0x7100
flags = ["pci_problem_triton"]
# PCI_PROBLEM_VIAETBF — VIA 82C597_0.
# Errata in the ETBF (Early Transaction Buffer) cause livelocks.
[[pci_quirk]]
vendor = 0x1106
device = 0x0597
flags = ["pci_problem_viaetbf"]
# PCI_PROBLEM_VSFX — VIA 82C576.
# The VSFX (Video Side FX) bridge has broken posted-write ordering.
[[pci_quirk]]
vendor = 0x1106
device = 0x0576
flags = ["pci_problem_vsfx"]
# PCI_PROBLEM_ALIMAGIK — ALi M1647 / M1651.
# The Aladdin V/V+ northbridges combine ALi-ali-MAGiK and Triton
# problems; both bit patterns are needed to disable all fast paths.
[[pci_quirk]]
vendor = 0x10b9
device = 0x1647
flags = ["pci_problem_alimagik"]
[[pci_quirk]]
vendor = 0x10b9
device = 0x1651
flags = ["pci_problem_alimagik"]
# PCI_PROBLEM_NATOMA — Intel 82441 / 82443LX / 82443BX.
# The Natoma core needs special PCI-PCI bridge handling.
[[pci_quirk]]
vendor = 0x8086
device = 0x1237
flags = ["pci_problem_natoma"]
[[pci_quirk]]
vendor = 0x8086
device = 0x7180
flags = ["pci_problem_natoma"]
[[pci_quirk]]
vendor = 0x8086
device = 0x7181
flags = ["pci_problem_natoma"]
[[pci_quirk]]
vendor = 0x8086
device = 0x7190
flags = ["pci_problem_natoma"]
[[pci_quirk]]
vendor = 0x8086
device = 0x7191
flags = ["pci_problem_natoma"]
[[pci_quirk]]
vendor = 0x8086
device = 0x7192
flags = ["pci_problem_natoma"]
@@ -1,4 +1,8 @@
# GPU display controller quirks.
#
# Sources: Linux 7.1 drivers/pci/quirks.c, drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
# Policy: Only add entries for hardware Red Bear targets (2020+ AMD/Intel GPUs).
# Review-gate each entry on Linux-backed evidence.
[[pci_quirk]]
vendor = 0x1002
@@ -15,6 +19,72 @@ vendor = 0x10DE
class = 0x030000
flags = ["no_d3cold", "need_firmware"]
# AMD Navi 10 variants (0x7310-0x731F) — ATS harvest required
# Linux: DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x7310, quirk_amd_harvest_no_ats)
# These Navi 10 sub-variants have broken ATS (Address Translation Services).
# Phase R2 (2026-06-07): converted from no_aspm placeholder to dedicated
# no_ats flag (PciQuirkFlags::NO_ATS = 1 << 27).
[[pci_quirk]]
vendor = 0x1002
device = 0x7312
flags = ["no_ats"]
[[pci_quirk]]
vendor = 0x1002
device = 0x7318
flags = ["no_ats"]
[[pci_quirk]]
vendor = 0x1002
device = 0x7319
flags = ["no_ats"]
[[pci_quirk]]
vendor = 0x1002
device = 0x731A
flags = ["no_ats"]
[[pci_quirk]]
vendor = 0x1002
device = 0x731B
flags = ["no_ats"]
[[pci_quirk]]
vendor = 0x1002
device = 0x731E
flags = ["no_ats"]
[[pci_quirk]]
vendor = 0x1002
device = 0x731F
flags = ["no_ats"]
# AMD Navi 14 variants (0x7340, 0x7341, 0x7347, 0x734F) — ATS harvest
# Linux: DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x7340, quirk_amd_harvest_no_ats)
# Phase R2: converted to no_ats.
[[pci_quirk]]
vendor = 0x1002
device = 0x7340
flags = ["no_ats"]
[[pci_quirk]]
vendor = 0x1002
device = 0x7341
flags = ["no_ats"]
[[pci_quirk]]
vendor = 0x1002
device = 0x7347
flags = ["no_ats"]
[[pci_quirk]]
vendor = 0x1002
device = 0x734F
flags = ["no_ats"]
# AMD specific SKU quirks
[[pci_quirk]]
vendor = 0x1002
device = 0x744C
@@ -25,6 +95,103 @@ vendor = 0x1002
device = 0x73EF
flags = ["need_firmware", "dma_32bit_only"]
# AMD Stoney (0x98E4) and Iceland (0x6900) — ATS harvest
# Older GPUs, included for completeness as they may appear in laptops
# Phase R2: converted to no_ats.
[[pci_quirk]]
vendor = 0x1002
device = 0x98E4
flags = ["no_ats"]
[[pci_quirk]]
vendor = 0x1002
device = 0x6900
flags = ["no_ats"]
# AMD Raven/Renoir (0x15D8) — ATS harvest with subsystem-specific filter
# Linux only applies when subsystem_vendor=0xEA50 and specific subsystem_device.
# Red Bear applies broadly until subsystem matching is refined.
# Phase R2: converted to no_ats.
[[pci_quirk]]
vendor = 0x1002
device = 0x15D8
flags = ["no_ats"]
# ATI audio function — MSI broken on ATI SBx00 (0x4386-0x438B, 0x4390-0x4394)
# Linux: quirk_no_msi + quirk_disable_all_msi
# These are southbridge audio functions, not GPUs, but they share the bus.
[[pci_quirk]]
vendor = 0x1002
device = 0x4386
flags = ["no_msi"]
[[pci_quirk]]
vendor = 0x1002
device = 0x4387
flags = ["no_msi"]
[[pci_quirk]]
vendor = 0x1002
device = 0x4388
flags = ["no_msi"]
[[pci_quirk]]
vendor = 0x1002
device = 0x4389
flags = ["no_msi"]
[[pci_quirk]]
vendor = 0x1002
device = 0x438A
flags = ["no_msi"]
[[pci_quirk]]
vendor = 0x1002
device = 0x438B
flags = ["no_msi"]
[[pci_quirk]]
vendor = 0x1002
device = 0x4390
flags = ["no_msi"]
[[pci_quirk]]
vendor = 0x1002
device = 0x4391
flags = ["no_msi"]
[[pci_quirk]]
vendor = 0x1002
device = 0x4392
flags = ["no_msi"]
[[pci_quirk]]
vendor = 0x1002
device = 0x4393
flags = ["no_msi"]
[[pci_quirk]]
vendor = 0x1002
device = 0x4394
flags = ["no_msi"]
# ATI SBx00 SATA — IDE mode quirk (0x4373-0x4375, IXP600/700 SATA)
# Linux: quirk_amd_ide_mode — forces IDE mode on SATA controllers
[[pci_quirk]]
vendor = 0x1002
device = 0x4373
flags = ["wrong_class"]
[[pci_quirk]]
vendor = 0x1002
device = 0x4374
flags = ["wrong_class"]
[[pci_quirk]]
vendor = 0x1002
device = 0x4375
flags = ["wrong_class"]
[[usb_quirk]]
vendor = 0x0BDA
product = 0x8153
@@ -1,5 +1,11 @@
# Audio controller and codec quirks.
# These apply to HDA controllers and codec devices.
#
# Phase R2 (2026-06-07) — the audio_force_eapd, audio_single_cmd, and
# audio_position_fix_lpib flags are now defined in PciQuirkFlags (bits
# 29-31). Prior to R2 these names were silently dropped by the TOML
# parser with a warning; they now resolve correctly. Source: Linux 7.1
# sound/pci/hda/patch_hdmi.c and sound/pci/hda/hda_intel.c.
# Intel ICH8 HDA — force EAPD on outputs
[[pci_quirk]]