6f93b6ed43
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
388 lines
15 KiB
Markdown
388 lines
15 KiB
Markdown
# 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
|
|
- `PciQuirkFlags` bitmask
|
|
|
|
### 2. TOML Quirk Files
|
|
|
|
Location: `/etc/quirks.d/*.toml` (shipped by the `redbear-quirks` package)
|
|
|
|
Extensible at runtime without recompiling drivers. Format:
|
|
|
|
```toml
|
|
[[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)
|
|
|
|
```rust
|
|
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:
|
|
|
|
```c
|
|
#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:
|
|
|
|
```toml
|
|
[[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`:
|
|
|
|
```rust
|
|
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/`:
|
|
|
|
```toml
|
|
[[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:
|
|
|
|
```bash
|
|
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: `NEED_FIRMWARE` (hard firmware gate), with real quirk-aware logging for `NO_ASPM`, `NEED_IOMMU`, `NO_MSI`, `NO_MSIX`
|
|
|
|
**Infrastructure (data flows, reporting, and partial integration):**
|
|
- pcid-spawner: computes `PCI_QUIRK_FLAGS` by calling the canonical `redox-driver-sys` lookup on synthesized `PciDeviceInfo`, 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: `--quirks` reads `/etc/quirks.d/*.toml` and 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 `acpid` at `/scheme/acpi/dmi`)
|
|
|
|
**Observed/logged but not yet strongly enforced in runtime policy:**
|
|
- xhcid `NO_SUSPEND` is now enforced and `usbhubd` mirrors 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_MSIX` in 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 in the amdgpu driver path instead.
|
|
|
|
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.
|