From 9e7020bc50ecc2355cc03107d223d54bc330ff7a Mon Sep 17 00:00:00 2001 From: Admin Pupkin Date: Sun, 7 Jun 2026 22:04:27 +0300 Subject: [PATCH] quirks: ChipsetQuirkFlags + 11-entry early_qrk data file (R17) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase R17 (2026-06-07) — Early-boot chipset quirks. The data side lands now; the kernel-side consumer walks the table at boot and dispatches to the imperative handlers (nvidia_bugs, via_bugs, fix_hypertransport_config, ati_bugs, intel_remapping_check, intel_graphics_quirks, force_disable_hpet, apple_airport_reset). Changes: 1. ChipsetQuirkFlags (mod.rs:483) with 10 bits, one per Linux 7.1 early_qrk[] callback: QFLAG_APPLY_ONCE, NVIDIA_BUGS, VIA_BUGS, AMD_K8_NB_FIXUP, ATI_BUGS, ATI_BUGS_CONTD, INTEL_REMAPPING_CHECK, INTEL_GRAPHICS_QUIRKS, FORCE_DISABLE_HPET, APPLE_AIRPORT_RESET. 2. ChipsetQuirkEntry (mod.rs:509) — vendor (0xFFFF any) + device (0xFFFF any) + class + class_mask. matches() honours the class-mask semantics from Linux's early-quirks.c (the (class ^ target) & mask test). 3. CHIPSET FLAG_NAMES + parse_chipset_toml + load_chipset_flags (toml_loader.rs) — new [[chipset_quirk]] TOML table type with vendor + device + class + class_mask + flags. 4. 1 new unit test: phase_r17_chipset_quirk_entry_matches exercises NVIDIA + AMD K8 class-mask semantics + 5 match / mismatch combinations. 127/127 tests pass. 5. quirks.d/55-chipset-early.toml (110 lines) — 11 entries sourced from Linux 7.1 arch/x86/kernel/early-quirks.c: - NVIDIA any bridge → nvidia_bugs (QFLAG_APPLY_ONCE) - VIA any bridge → via_bugs (QFLAG_APPLY_ONCE) - AMD K8 northbridge 0x1100 → fix_hypertransport_config - ATI IXP400 SMBus 0x4372 → ati_bugs - ATI SBX00 SMBus 0x4385 → ati_bugs_contd - Intel 0x3403/0x3405/0x3406 host bridges → intel_remapping_check - Intel any VGA → intel_graphics_quirks - Intel 0x0F00 (Baytrail) → force_disable_hpet - Broadcom 0x4331 → apple_airport_reset cargo test: 127/127 (was 126, +1 for the new test). cargo check: clean. The kernel early-pci-scan path will call load_chipset_flags() for each PCI device it walks and invoke the named handler before any Rust user code. Compiled-in chipset_table is empty (handler bodies are imperative and don't fit a data-driven table). --- .../redox-driver-sys/source/src/quirks/dmi.rs | 34 ++++++ .../redox-driver-sys/source/src/quirks/mod.rs | 48 ++++++++ .../source/src/quirks/toml_loader.rs | 113 ++++++++++++++++- .../source/quirks.d/55-chipset-early.toml | 114 ++++++++++++++++++ 4 files changed, 304 insertions(+), 5 deletions(-) create mode 100644 local/recipes/system/redbear-quirks/source/quirks.d/55-chipset-early.toml diff --git a/local/recipes/drivers/redox-driver-sys/source/src/quirks/dmi.rs b/local/recipes/drivers/redox-driver-sys/source/src/quirks/dmi.rs index 30123e2dc8..c7340c2389 100644 --- a/local/recipes/drivers/redox-driver-sys/source/src/quirks/dmi.rs +++ b/local/recipes/drivers/redox-driver-sys/source/src/quirks/dmi.rs @@ -993,4 +993,38 @@ mod tests { assert!(wildcard.matches(0x8086, 0x24C0, 0)); assert!(wildcard.matches(0x8086, 0x24C0, 200)); } + + /// Phase R17 — `ChipsetQuirkEntry::matches` honours + /// vendor / device wildcards and the class-mask semantics. + #[test] + fn phase_r17_chipset_quirk_entry_matches() { + use super::super::ChipsetQuirkEntry; + // NVIDIA bridges of any model and any bridge class. + let nvidia = ChipsetQuirkEntry { + vendor: 0x10DE, + device: 0xFFFF, + class: 0x0604, // PCI_CLASS_BRIDGE_PCI + class_mask: 0xFFFF, + flags: super::super::ChipsetQuirkFlags::NVIDIA_BUGS, + }; + // Match: NVIDIA bridge at any device id + assert!(nvidia.matches(0x10DE, 0x0001, 0x0604)); + // Wrong vendor + assert!(!nvidia.matches(0x8086, 0x0001, 0x0604)); + // Wrong class (with mask 0xFFFF any bit mismatches) + assert!(!nvidia.matches(0x10DE, 0x0001, 0x0300)); + // AMD K8 northbridge, any host-bridge variant + let amd_k8 = ChipsetQuirkEntry { + vendor: 0x1022, + device: 0x1100, + class: 0x0600, // PCI_CLASS_BRIDGE_HOST + class_mask: 0xFF00, // match high byte (class base) + flags: super::super::ChipsetQuirkFlags::AMD_K8_NB_FIXUP, + }; + // Class base 0x06 (bridge) with any subclass + assert!(amd_k8.matches(0x1022, 0x1100, 0x0600)); + assert!(amd_k8.matches(0x1022, 0x1100, 0x0601)); + // Different class base + assert!(!amd_k8.matches(0x1022, 0x1100, 0x0300)); + } } diff --git a/local/recipes/drivers/redox-driver-sys/source/src/quirks/mod.rs b/local/recipes/drivers/redox-driver-sys/source/src/quirks/mod.rs index 47572ff2b3..1e65e879ac 100644 --- a/local/recipes/drivers/redox-driver-sys/source/src/quirks/mod.rs +++ b/local/recipes/drivers/redox-driver-sys/source/src/quirks/mod.rs @@ -509,6 +509,54 @@ impl ClocksourceQuirkEntry { } } +bitflags::bitflags! { + /// Early-boot chipset quirk flags. Phase R17 (2026-06-07) + /// — ported from Linux 7.1 + /// `arch/x86/kernel/early-quirks.c` `early_qrk[]` (11 + /// entries). Each bit names a chipset init routine that + /// must run before any Rust user code. The kernel-side + /// consumer (R17 follow-up) walks the chipset table at + /// boot and dispatches to the named handler. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + pub struct ChipsetQuirkFlags: u64 { + const QFLAG_APPLY_ONCE = 1 << 0; + const NVIDIA_BUGS = 1 << 1; + const VIA_BUGS = 1 << 2; + const AMD_K8_NB_FIXUP = 1 << 3; // fix_hypertransport_config + const ATI_BUGS = 1 << 4; // ati_bugs + const ATI_BUGS_CONTD = 1 << 5; // ati_bugs_contd + const INTEL_REMAPPING_CHECK = 1 << 6; + const INTEL_GRAPHICS_QUIRKS = 1 << 7; // stolen memory + const FORCE_DISABLE_HPET = 1 << 8; // Baytrail HPET bad + const APPLE_AIRPORT_RESET = 1 << 9; // Broadcom 4331 + } +} + +/// One early-boot chipset quirk rule. Phase R17 (2026-06-07). +#[derive(Debug, Clone)] +pub struct ChipsetQuirkEntry { + pub vendor: u16, // 0xFFFF = any + pub device: u16, // 0xFFFF = any + pub class: u16, + pub class_mask: u16, // 0xFFFF = match whole class + pub flags: ChipsetQuirkFlags, +} + +impl ChipsetQuirkEntry { + /// Match the (vendor, device, class) tuple. class_mask + /// must be non-zero for a class comparison; 0xFFFF means + /// match-all. + pub fn matches(&self, vendor: u16, device: u16, class: u16) -> bool { + if self.vendor != 0xFFFF && self.vendor != vendor { + return false; + } + if self.device != 0xFFFF && self.device != device { + return false; + } + (self.class ^ class) & self.class_mask == 0 + } +} + /// Wildcard value for PCI ID matching. pub const PCI_QUIRK_ANY_ID: u16 = 0xFFFF; diff --git a/local/recipes/drivers/redox-driver-sys/source/src/quirks/toml_loader.rs b/local/recipes/drivers/redox-driver-sys/source/src/quirks/toml_loader.rs index 4defe9076f..da5020eb56 100644 --- a/local/recipes/drivers/redox-driver-sys/source/src/quirks/toml_loader.rs +++ b/local/recipes/drivers/redox-driver-sys/source/src/quirks/toml_loader.rs @@ -1,10 +1,10 @@ use super::{ dmi::{self, DmiAcpiQuirkRule, DmiDrmPanelQuirkRule, DmiInfo, DmiMatchRule, DmiPciQuirkRule, DmiXhciQuirkRule, PlatformDmiQuirkRule}, - AcpiQuirkFlags, ClocksourceQuirkEntry, ClocksourceQuirkFlags, CpuBugFlags, - CpuBugQuirkEntry, CpuId, DrmPanelOrientation, HidQuirkEntry, HidQuirkFlags, - MaskWidth, PciQuirkEntry, PciQuirkFlags, PciQuirkLookup, PciQuirkPhase, - PlatformSubsystem, QuirkAction, UsbQuirkEntry, UsbQuirkFlags, - XhciControllerQuirk, XhciControllerQuirkFlags, PCI_QUIRK_ANY_ID, + AcpiQuirkFlags, ChipsetQuirkEntry, ChipsetQuirkFlags, ClocksourceQuirkEntry, + ClocksourceQuirkFlags, CpuBugFlags, CpuBugQuirkEntry, CpuId, DrmPanelOrientation, + HidQuirkEntry, HidQuirkFlags, MaskWidth, PciQuirkEntry, PciQuirkFlags, + PciQuirkLookup, PciQuirkPhase, PlatformSubsystem, QuirkAction, UsbQuirkEntry, + UsbQuirkFlags, XhciControllerQuirk, XhciControllerQuirkFlags, PCI_QUIRK_ANY_ID, }; use crate::pci::PciDeviceInfo; use std::borrow::Cow; @@ -613,6 +613,109 @@ pub(crate) fn load_clocksource_flags( flags } +pub const CHIPSET_FLAG_NAMES: &[(&str, ChipsetQuirkFlags)] = &[ + ("qflag_apply_once", ChipsetQuirkFlags::QFLAG_APPLY_ONCE), + ("nvidia_bugs", ChipsetQuirkFlags::NVIDIA_BUGS), + ("via_bugs", ChipsetQuirkFlags::VIA_BUGS), + ("amd_k8_nb_fixup", ChipsetQuirkFlags::AMD_K8_NB_FIXUP), + ("ati_bugs", ChipsetQuirkFlags::ATI_BUGS), + ("ati_bugs_contd", ChipsetQuirkFlags::ATI_BUGS_CONTD), + ("intel_remapping_check", ChipsetQuirkFlags::INTEL_REMAPPING_CHECK), + ("intel_graphics_quirks", ChipsetQuirkFlags::INTEL_GRAPHICS_QUIRKS), + ("force_disable_hpet", ChipsetQuirkFlags::FORCE_DISABLE_HPET), + ("apple_airport_reset", ChipsetQuirkFlags::APPLE_AIRPORT_RESET), +]; + +pub(crate) fn read_toml_chipset_entries() -> std::io::Result> { + let mut entries = Vec::new(); + for path in sorted_toml_files(QUIRKS_DIR)? { + let path_str = path.display().to_string(); + let content = match std::fs::read_to_string(&path) { + Ok(c) => c, + Err(e) => { + log::warn!("quirks: failed to read {path_str}: {e}"); + continue; + } + }; + let doc = match content.parse::() { + Ok(d) => d, + Err(e) => { + log::warn!("quirks: failed to parse {path_str}: {e}"); + continue; + } + }; + parse_chipset_toml(&doc, &mut entries, &path_str); + } + Ok(entries) +} + +fn parse_chipset_toml( + doc: &toml::Value, + out: &mut Vec, + path: &str, +) { + let Some(arr) = doc.get("chipset_quirk").and_then(|v| v.as_array()) else { + return; + }; + for item in arr { + let Some(table) = item.as_table() else { + log::warn!("quirks: {path}: chipset_quirk entry is not a table, skipping"); + continue; + }; + let vendor = match table.get("vendor") { + Some(value) => match bounded_u16(value, "vendor", path) { + Some(value) => value, + None => continue, + }, + None => 0xFFFF, + }; + let device = match table.get("device") { + Some(value) => match bounded_u16(value, "device", path) { + Some(value) => value, + None => continue, + }, + None => 0xFFFF, + }; + let class = match table.get("class") { + Some(value) => match bounded_u16(value, "class", path) { + Some(value) => value, + None => continue, + }, + None => 0, + }; + let class_mask = match table.get("class_mask") { + Some(value) => match bounded_u16(value, "class_mask", path) { + Some(value) => value, + None => continue, + }, + None => 0xFFFF, + }; + let flags = parse_flags(table, path, "Chipset", CHIPSET_FLAG_NAMES); + out.push(ChipsetQuirkEntry { + vendor, + device, + class, + class_mask, + flags, + }); + } +} + +/// Look up the chipset flags for the given (vendor, device, +/// class) tuple across all runtime TOML entries. Returns +/// the OR-accumulated flags. +pub(crate) fn load_chipset_flags(vendor: u16, device: u16, class: u16) -> ChipsetQuirkFlags { + let mut flags = ChipsetQuirkFlags::empty(); + if let Ok(entries) = read_toml_chipset_entries() { + for entry in entries { + if entry.matches(vendor, device, class) { + flags |= entry.flags; + } + } + } + flags +} + fn bounded_u16(val: &toml::Value, field: &str, path: &str) -> Option { match val.as_integer() { Some(v) => u16::try_from(v).ok().or_else(|| { diff --git a/local/recipes/system/redbear-quirks/source/quirks.d/55-chipset-early.toml b/local/recipes/system/redbear-quirks/source/quirks.d/55-chipset-early.toml new file mode 100644 index 0000000000..262c673d0d --- /dev/null +++ b/local/recipes/system/redbear-quirks/source/quirks.d/55-chipset-early.toml @@ -0,0 +1,114 @@ +# Early-boot chipset quirks — PCI vendor / device / class-based. +# Mined from Linux 7.1 +# `arch/x86/kernel/early-quirks.c` `early_qrk[]` (11 entries). +# Each `[[chipset_quirk]]` entry matches on (vendor, device, +# class, class_mask) and produces a ChipsetQuirkFlags bit +# set that names the imperative handler to invoke before +# any Rust user code runs. +# +# Phase R17 (2026-06-07). The kernel-side consumer walks +# the table at boot (in the early-pci-scan path) and +# dispatches to the named handlers. The compiled-in +# table is empty; runtime TOML is the data surface. +# +# Class codes used: +# PCI_CLASS_BRIDGE_PCI = 0x0604 (any PCI bridge) +# PCI_CLASS_BRIDGE_HOST = 0x0600 (host bridge) +# PCI_BASE_CLASS_BRIDGE = 0x06 (any bridge class) +# PCI_CLASS_SERIAL_SMBUS = 0x0C05 +# PCI_CLASS_DISPLAY_VGA = 0x0300 + +# NVIDIA — any PCI bridge. nvidia_bugs() covers the +# MCP61 / MCP65 / MCP67 / MCP73 / MCP79S chipset bugs. +[[chipset_quirk]] +vendor = 0x10DE +device = 0xFFFF +class = 0x0604 +class_mask = 0xFFFF +flags = ["nvidia_bugs", "qflag_apply_once"] + +# VIA — any PCI bridge. via_bugs() covers VT8235 / VT8237 +# / VT8363 / VT8366 / VT8377 / VT8251 issues. +[[chipset_quirk]] +vendor = 0x1106 +device = 0xFFFF +class = 0x0604 +class_mask = 0xFFFF +flags = ["via_bugs", "qflag_apply_once"] + +# AMD K8 northbridge — fix_hypertransport_config for +# PCI_CLASS_BRIDGE_HOST 0x0600. Subclass mask is 0xFF00 so +# any host-bridge subclass matches. +[[chipset_quirk]] +vendor = 0x1022 +device = 0x1100 +class = 0x0600 +class_mask = 0xFF00 +flags = ["amd_k8_nb_fixup"] + +# ATI IXP400 SMBus (0x4372) — ati_bugs(). +[[chipset_quirk]] +vendor = 0x1002 +device = 0x4372 +class = 0x0C05 +class_mask = 0xFFFF +flags = ["ati_bugs"] + +# ATI SBX00 SMBus (0x4385) — ati_bugs_contd(). +[[chipset_quirk]] +vendor = 0x1002 +device = 0x4385 +class = 0x0C05 +class_mask = 0xFFFF +flags = ["ati_bugs_contd"] + +# Intel 0x3403 / 0x3405 / 0x3406 — host bridges, +# intel_remapping_check() for VT-d interrupt remapping. +# (any bridge class with class_mask = 0xFF00) +[[chipset_quirk]] +vendor = 0x8086 +device = 0x3403 +class = 0x0600 +class_mask = 0xFF00 +flags = ["intel_remapping_check"] + +[[chipset_quirk]] +vendor = 0x8086 +device = 0x3405 +class = 0x0600 +class_mask = 0xFF00 +flags = ["intel_remapping_check"] + +[[chipset_quirk]] +vendor = 0x8086 +device = 0x3406 +class = 0x0600 +class_mask = 0xFF00 +flags = ["intel_remapping_check"] + +# Intel — any VGA display controller. intel_graphics_quirks() +# reserves stolen memory at boot. +[[chipset_quirk]] +vendor = 0x8086 +device = 0xFFFF +class = 0x0300 +class_mask = 0xFFFF +flags = ["intel_graphics_quirks"] + +# Intel 0x0F00 (Baytrail SoC) — force_disable_hpet. +# HPET on this chipset halts in deep idle. +[[chipset_quirk]] +vendor = 0x8086 +device = 0x0F00 +class = 0x0600 +class_mask = 0xFF00 +flags = ["force_disable_hpet"] + +# Broadcom 0x4331 — apple_airport_reset. The Apple wireless +# card needs a reset before the driver can attach. +[[chipset_quirk]] +vendor = 0x14E4 +device = 0x4331 +class = 0x0280 +class_mask = 0xFFFF +flags = ["apple_airport_reset"]