Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
15 KiB
Red Bear OS Hardware Quirks System
Overview
Red Bear OS implements a data-driven hardware quirks system inspired by Linux's PCI/USB/DMI quirk infrastructure, adapted for Redox's microkernel/userspace-driver architecture.
Quirks handle known hardware defects that cannot be fixed by correct driver code alone. They override default driver behavior for specific devices, revisions, or entire system models.
For the current follow-up cleanup and integration roadmap, see
local/docs/QUIRKS-IMPROVEMENT-PLAN.md.
Architecture
Driver probes device
└─ PciDeviceInfo::quirks()
├─ Layer 1: Compiled-in table (pci_table.rs, usb_table.rs)
├─ Layer 2: TOML files from /etc/quirks.d/*.toml
└─ Layer 3: DMI-based system rules
└─ Returns: PciQuirkFlags (bitwise OR of all matching entries)
All matching entries accumulate via bitwise OR, so broad rules (e.g., "all AMD GPUs need firmware") and narrow rules (e.g., "this specific revision has broken MSI-X") compose naturally.
Quirk Sources
1. Compiled-in Tables
Location: local/recipes/drivers/redox-driver-sys/source/src/quirks/
Critical quirks that must be available before the root filesystem is mounted.
Defined as const arrays in Rust:
pci_table.rs—PCI_QUIRK_TABLE: &[PciQuirkEntry]usb_table.rs—USB_QUIRK_TABLE: &[UsbQuirkEntry]
Each entry specifies:
- Vendor/device/subsystem match fields (0xFFFF = wildcard)
- Revision range (lo..hi inclusive)
- Class code mask and match value
PciQuirkFlagsbitmask
2. TOML Quirk Files
Location: /etc/quirks.d/*.toml (shipped by the redbear-quirks package)
Extensible at runtime without recompiling drivers. Format:
[[pci_quirk]]
vendor = 0x1002
device = 0x73BF
flags = ["need_firmware", "no_d3cold"]
[[pci_quirk]]
vendor = 0x10EC
device = 0x8125
flags = ["no_aspm"]
[[usb_quirk]]
vendor = 0x0A12
flags = ["bad_descriptor", "no_set_config"]
Files are loaded alphabetically from /etc/quirks.d/. Recommended naming:
00-core.toml, 10-gpu.toml, 20-usb.toml, 30-net.toml, 40-storage.toml,
50-system.toml.
Runtime TOML loading now also supports [[dmi_system_quirk]] entries. Those
entries are applied when acpid is running and serving live DMI data from
/scheme/acpi/dmi.
3. DMI-Based System Quirks
Match by SMBIOS fields (sys_vendor, board_name, product_name) to apply
system-wide quirk overrides. Eight compiled-in rules exist for known systems,
and /etc/quirks.d/*.toml can now add [[dmi_system_quirk]] rules with
match.* keys plus optional pci_vendor / pci_device selectors. Runtime use
now reads live SMBIOS strings from acpid via /scheme/acpi/dmi.
Available Quirk Flags
PCI Quirks (PciQuirkFlags)
| Flag | Meaning |
|---|---|
NO_MSI |
MSI capability broken; use MSI-X or legacy |
NO_MSIX |
MSI-X capability broken; use MSI or legacy |
FORCE_LEGACY_IRQ |
Must use INTx interrupts |
NO_PM |
Disable all power management |
NO_D3COLD |
Cannot recover from D3cold power state |
NO_ASPM |
Active State Power Management broken |
NEED_IOMMU |
Requires IOMMU isolation |
NO_IOMMU |
Must NOT be behind IOMMU |
DMA_32BIT_ONLY |
Only supports 32-bit DMA |
RESIZE_BAR |
BAR sizing reports incorrectly |
DISABLE_BAR_SIZING |
Use firmware BAR values as-is |
NEED_FIRMWARE |
Requires firmware files to initialize |
DISABLE_ACCEL |
Disable hardware acceleration |
FORCE_VRAM_ONLY |
No GTT/system memory fallback |
NO_USB3 |
Force USB 2.0 mode |
RESET_DELAY_MS |
Needs extra post-reset delay |
NO_STRING_FETCH |
Do not fetch string descriptors |
BAD_EEPROM |
EEPROM unreliable; use hardcoded values |
BUS_MASTER_DELAY |
Needs delay after bus-master enable |
WRONG_CLASS |
Reports incorrect class code |
BROKEN_BRIDGE |
PCI bridge forwarding bug |
NO_RESOURCE_RELOC |
Do not relocate PCI resources |
USB Quirks (UsbQuirkFlags)
| Flag | Meaning |
|---|---|
NO_STRING_FETCH |
Do not fetch string descriptors |
RESET_DELAY |
Needs extra reset delay |
NO_USB3 |
Disable USB 3.x |
NO_SET_CONFIG |
Cannot handle SetConfiguration |
NO_SUSPEND |
Broken suspend/resume |
NEED_RESET |
Needs reset after probe |
BAD_DESCRIPTOR |
Wrong descriptor sizes |
NO_LPM |
Disable Link Power Management |
NO_U1U2 |
Disable U1/U2 link transitions |
Driver Integration
For Rust Drivers (using redox-driver-sys)
use redox_driver_sys::quirks::PciQuirkFlags;
fn probe(info: &PciDeviceInfo) {
let quirks = info.quirks();
if quirks.contains(PciQuirkFlags::NO_MSIX) {
// Skip MSI-X, try MSI or legacy
}
if quirks.contains(PciQuirkFlags::NEED_FIRMWARE) {
// Load firmware before initializing device
}
if quirks.contains(PciQuirkFlags::DISABLE_ACCEL) {
// Skip hardware probe, let software renderer take over
return Err(DriverError::QuirkDisabled);
}
}
For C Drivers (using linux-kpi)
The linux-kpi crate exposes two FFI functions for C drivers to query quirks:
#include <linux/pci.h>
// After pci_enable_device() in your probe callback:
static int my_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
u64 quirks = pci_get_quirk_flags(dev);
if (quirks & PCI_QUIRK_NO_MSIX) {
// Skip MSI-X, fall back to MSI or legacy IRQ
}
if (pci_has_quirk(dev, PCI_QUIRK_NEED_FIRMWARE)) {
// Load firmware before initializing hardware
}
}
The amdgpu Redox glue/runtime path is now the first in-tree production C consumer
of this interface: it queries pci_get_quirk_flags() during AMD DC init, logs the
resulting IRQ expectations, and treats PCI_QUIRK_NEED_FIRMWARE as a hard failure
instead of a warn-and-continue path when that quirk is active.
For USB Storage Drivers (usbscsid)
The USB SCSI driver (drivers/storage/usbscsid) carries its own self-contained quirk
module (src/quirks.rs) that reads [[usb_storage_quirk]] entries from /etc/quirks.d/*.toml.
It does not depend on redox-driver-sys — the quirk lookup is inline.
At daemon startup, usbscsid extracts vendor/product IDs from the USB device descriptor
and looks up matching quirks from both the compiled-in table and TOML files. The resulting
UsbStorageQuirkFlags propagate to both the BOT transport and the SCSI command layer.
Active behavioral flags and their injection points:
| Flag | Layer | Effect |
|---|---|---|
IGNORE_RESIDUE |
BOT (bot.rs) |
Suppresses CSW data_residue; avoids false short-transfer errors |
FIX_CAPACITY |
SCSI (scsi/mod.rs) |
Subtracts 1 from READ CAPACITY(10) block count |
SINGLE_LUN |
BOT (bot.rs) |
Enforces LUN=0 in CBW |
MAX_SECTORS_64 |
SCSI (scsi/mod.rs) |
Clamps transfer_len to 64 sectors per command |
INITIAL_READ10 |
SCSI (scsi/mod.rs) |
Uses READ(10)/WRITE(10) instead of READ(16)/WRITE(16) |
TOML format for storage quirks:
[[usb_storage_quirk]]
vendor = 0x03EB
product = 0x2002
revision = "0100-0100"
manufacturer = "ATMEL"
description = "SND1 Storage"
flags = ["ignore_residue"]
The full 214-entry table lives in quirks.d/30-storage.toml, mined from Linux 7.0's
drivers/usb/storage/unusual_devs.h.
Available C quirk flag macros (defined in linux/pci.h):
| Macro | Bit | Meaning |
|---|---|---|
PCI_QUIRK_NO_MSI |
0 | MSI interrupts broken |
PCI_QUIRK_NO_MSIX |
1 | MSI-X interrupts broken |
PCI_QUIRK_FORCE_LEGACY |
2 | Must use legacy INTx |
PCI_QUIRK_NO_PM |
3 | Power management broken |
PCI_QUIRK_NO_D3COLD |
4 | D3cold state broken |
PCI_QUIRK_NO_ASPM |
5 | ASPM broken |
PCI_QUIRK_NEED_IOMMU |
6 | Requires IOMMU |
PCI_QUIRK_DMA_32BIT_ONLY |
8 | DMA limited to 32-bit |
PCI_QUIRK_NEED_FIRMWARE |
11 | Requires firmware load |
PCI_QUIRK_DISABLE_ACCEL |
12 | Disable hardware acceleration |
Adding New Quirks
To the compiled-in table
Edit local/recipes/drivers/redox-driver-sys/source/src/quirks/pci_table.rs:
const F_MY_FLAGS: PciQuirkFlags = PciQuirkFlags::from_bits_truncate(
PciQuirkFlags::NEED_FIRMWARE.bits() | PciQuirkFlags::NO_ASPM.bits(),
);
PciQuirkEntry {
vendor: 0xVENDOR,
device: 0xDEVICE,
flags: F_MY_FLAGS,
..PciQuirkEntry::WILDCARD
},
To a TOML file
Create or edit a file in local/recipes/system/redbear-quirks/source/quirks.d/:
[[pci_quirk]]
vendor = 0xVENDOR
device = 0xDEVICE
flags = ["need_firmware", "no_aspm"]
[[usb_quirk]]
vendor = 0xVENDOR
product = 0xPRODUCT
flags = ["no_lpm", "need_reset"]
[[usb_storage_quirk]]
vendor = 0xVENDOR
product = 0xPRODUCT
flags = ["ignore_residue", "fix_capacity"]
[[dmi_system_quirk]]
pci_vendor = 0xVENDOR
flags = ["disable_accel"]
match.sys_vendor = "Example Vendor"
match.product_name = "Example Model"
[[acpi_table_quirk]]
signature = "DMAR"
match.sys_vendor = "Example Vendor"
match.product_name = "Example Model"
Choosing where to add
- Compiled-in: Boot-critical quirks, anything needed before root mount
- TOML: Everything else — easier to update, no recompilation needed
- DMI rule: System-specific workarounds that apply to specific laptop models
File Layout
local/recipes/drivers/redox-driver-sys/source/src/quirks/
├── mod.rs # Public API: lookup_pci_quirks(), PciQuirkFlags, PciQuirkEntry
├── pci_table.rs # Compiled-in PCI quirk table
├── usb_table.rs # Compiled-in USB quirk table
├── dmi.rs # DMI/SMBIOS matching and system-level quirk rules
└── toml_loader.rs # /etc/quirks.d/*.toml parser
local/recipes/system/redbear-quirks/
├── recipe.toml # Custom build: copies TOML files to /etc/quirks.d/
└── source/quirks.d/
├── 00-core.toml
├── 10-gpu.toml
├── 20-usb.toml
├── 30-net.toml
├── 40-storage.toml
└── 50-system.toml
Relationship to Linux Quirks
| Linux Pattern | Red Bear Equivalent |
|---|---|
DECLARE_PCI_FIXUP_HEADER(v, d, fn) |
PciQuirkEntry { vendor: v, device: d, flags: ... } |
pci_dev->dev_flags |= PCI_DEV_FLAGS_NO_BUS_RESET |
No direct equivalent — future flag candidate |
USB_QUIRK_STRING_FETCH |
UsbQuirkFlags::NO_STRING_FETCH |
DMI_MATCH(DMI_SYS_VENDOR, "Lenovo") |
DmiMatchRule { sys_vendor: Some("Lenovo") } |
acpi_black_listed() |
[[acpi_table_quirk]] signature = "...." with skip semantics in acpid |
Testing
Run quirks unit tests:
cd local/recipes/drivers/redox-driver-sys/source
cargo test
Implementation Status
| Phase | Component | Status |
|---|---|---|
| Q1 | Core types (PciQuirkFlags, PciQuirkEntry, UsbQuirkFlags) | ✅ Done |
| Q1 | Compiled-in PCI/USB quirk tables | ✅ Done |
| Q1 | Lookup API (quirks(), has_quirk()) | ✅ Done |
| Q1 | Subsystem (subvendor/subdevice) fields | ✅ Done — compiled and TOML PCI matching both apply subsystem selectors |
| Q2 | TOML loader for /etc/quirks.d/ | ✅ Done |
| Q2 | redbear-quirks data package | ✅ Done |
| Q3 | redox-drm integration (MSI-X/MSI/legacy + DISABLE_ACCEL) | ✅ Done |
| Q3 | xhcid PCI controller quirks (interrupt + reset delay) | ✅ Done |
| Q3 | xhcid USB device quirks (descriptor/configuration/BOS handling) | ✅ Done |
| Q3 | pcid-spawner quirk passthrough | ✅ Done |
| Q3 | linux-kpi quirk flag bridge | ✅ Done |
| Q3 | amdgpu linux-kpi quirk consumption | ✅ Done |
| Q3 | redbear-info --quirks display | ✅ Done |
| Q4 | DMI/SMBIOS compiled-in rules | ✅ Done — 8 system rules (const table) |
| Q4 | DMI/SMBIOS TOML runtime loading | ✅ Done — dmi_system_quirk uses live /scheme/acpi/dmi data from acpid |
| Q4 | ACPI table blacklist/override | ✅ Done — acpid applies [[acpi_table_quirk]] skip rules during table load |
| Q5 | lspci quirk display | ✅ Done — shows active quirks per device |
| Q5 | lsusb quirk display | ✅ Done — shows active quirks per device |
| Q5 | Linux quirk extraction tool | ✅ Script exists — PCI mode uses heuristic name matching, USB mode works for table entries |
Quirk flags span data definition, infrastructure wiring, and driver consumption. Most flags are defined but not yet consumed at runtime — the tables below show the honest breakdown.
Flags consumed by drivers (runtime checks in production code):
- redox-drm:
NO_MSIX,NO_MSI,FORCE_LEGACY_IRQ,DISABLE_ACCEL(interrupt setup + driver probe) - xhcid:
RESET_DELAY_MS,NO_MSI,NO_MSIX,FORCE_LEGACY_IRQ(interrupt selection + port reset delay) - xhcid (USB device path):
NO_STRING_FETCH,BAD_DESCRIPTOR,RESET_DELAY,HUB_SLOW_RESET,NO_BOS,SHORT_SET_ADDR_TIMEOUT,FORCE_ONE_CONFIG,HONOR_BNUMINTERFACES,DELAY_CTRL_MSG,NO_SET_CONFIG,NO_SET_INTF,NEED_RESET,NO_SUSPEND(enumeration/configuration/BOS/runtime recovery plus suspend gating) - amdgpu: startup firmware requirement enforced at the Rust DRM boundary, with real quirk-aware runtime logging for
NO_ASPM,NEED_IOMMU,NO_MSI,NO_MSIX
Infrastructure (data flows, reporting, and partial integration):
- pcid-spawner: computes
PCI_QUIRK_FLAGSby calling the canonicalredox-driver-syslookup on synthesizedPciDeviceInfo, then passes the env var onward - linux-kpi:
pci_get_quirk_flags()/pci_has_quirk()C FFI is available for C drivers and is now consumed by the Red Bear amdgpu path - redbear-info:
--quirksreads/etc/quirks.d/*.tomland reports configured PCI/USB/DMI entries - lspci: shows active quirk flags per PCI device (via redox-driver-sys lookup)
- lsusb: shows active quirk flags per USB device (via redox-driver-sys lookup)
- DMI compiled-in rules: 8 entries match systems by vendor/product/board (served through
acpidat/scheme/acpi/dmi)
Observed/logged but not yet strongly enforced in runtime policy:
- xhcid
NO_SUSPENDis now enforced andusbhubdmirrors USB 2 hub-port suspend state into child xhcid devices, but suspend policy origination and USB 3 link-state coordination are still pending in the broader hub/power-management layer NO_ASPM,NEED_IOMMU,NO_MSI,NO_MSIXin the amdgpu path are surfaced in quirk-aware logs before broader driver policy exists.
Defined but not yet consumed by any real driver path:
NO_PM,NO_D3COLD,DMA_32BIT_ONLY,BUS_MASTER_DELAY,NO_IOMMU, etc.
firmware-loader itself does not interpret NEED_FIRMWARE; that policy is now enforced at the Rust-side DRM startup boundary instead.
Active Red Bear images that include redbear-device-services already ship the upstream
redbear-firmware bundle into /lib/firmware. The bounded Intel DMC work therefore selects and
requires the right startup blobs from that shipped firmware set, rather than depending on the user
to fetch firmware at runtime.
For early xhcid timing quirks, [[usb_quirk]] entries may also carry port = "1.2.3" selectors.
Those selectors are used only for pre-descriptor timing flags (RESET_DELAY, HUB_SLOW_RESET,
SHORT_SET_ADDR_TIMEOUT) where vendor/product IDs are not yet available.
Remaining infrastructure work:
- none in the current quirks scope
pcid-spawner now brokers quirks through the canonical redox-driver-sys lookup instead of carrying a separate in-tree PCI quirk engine.