quirks: resolve Blocker 5 — add bios_vendor + bios_date to DMI structs

Phase R10 audit (2026-06-07) identified that DmiInfo and DmiMatchRule
were missing the bios_vendor and bios_date fields that Linux SMBIOS
Type 0 (BIOS Information) provides. Many DMI-based quirk rules in
Linux drivers/acpi/osi.c, drivers/acpi/ec.c, and drivers/platform/x86/
match on BIOS vendor or BIOS release date for firmware-version
workarounds.

Changes:
  - dmi.rs: add bios_vendor: Option<String> and bios_date: Option<String>
    to both DmiInfo and DmiMatchRule structs
  - dmi.rs: extend is_empty() and matches() to consider the new fields
  - dmi.rs: extend parse_dmi_data() to handle bios_vendor and bios_date
    keys in /scheme/acpi/dmi text format
  - dmi.rs: extend all 8 compiled-in DmiPciQuirkRule literals and 3
    DmiInfo test fixtures with the new fields
  - toml_loader.rs: extend parse_dmi_match_rule() to parse bios_vendor
    and bios_date from [[dmi_system_quirk]] match tables
  - toml_loader.rs: extend all 4 DmiInfo test fixtures
  - dmi.rs: 5 new unit tests (bios_vendor match, bios_date match,
    combined match, parse_dmi_data, is_empty with bios fields)
  - toml_loader.rs: 1 new integration test (TOML bios_vendor+date
    parse and match, miss, and absent cases)
  - QUIRKS-SYSTEM.md: mark Blocker 5 as RESOLVED

Tests: 120/120 pass (was 114, +6 new).
Clippy: +2 warnings (same map_or pattern as existing 7 DMI fields,
follows existing convention; not a new defect).

Source-of-truth: drivers/firmware/dmi_scan.c (dmi_decode_table) and
include/linux/mod_devicetable.h (dmi_system_id).

Depends on Blocker 2 (acpid DMI producer at /scheme/acpi/dmi) for
runtime data; the fields are now in place and will activate when
acpid is updated to populate the new keys.
This commit is contained in:
2026-06-07 14:14:45 +03:00
parent 60b2006011
commit 9a28b68ef8
3 changed files with 295 additions and 0 deletions
+9
View File
@@ -2908,6 +2908,15 @@ extend `parse_dmi_match_rule()` in
add tests; update the DMI producer in acpid (Blocker 2) to emit the new
keys.
**Status:** ✅ RESOLVED 2026-06-07 — Both fields added to `DmiInfo` and
`DmiMatchRule`; `is_empty()` and `matches()` extended; `parse_dmi_data()`
handles `bios_vendor` and `bios_date` keys; `parse_dmi_match_rule()` in
`toml_loader.rs` handles new TOML fields. 5 new dmi.rs unit tests +
1 new toml_loader.rs test added. 120/120 tests pass. Clippy +2 warnings
(same `map_or` pattern as existing 7 DMI fields, consistent with
existing code). Awaiting Blocker 2 (acpid DMI producer) for the runtime
data source.
### Gaps 620 (Medium and Low Priority)
| # | Gap | File:line | Impact |
@@ -3,6 +3,14 @@ use crate::pci::PciDeviceInfo;
use std::borrow::Cow;
/// DMI/SMBIOS field identifiers for system matching.
///
/// Phase R10 audit (2026-06-07) — added `bios_vendor` and `bios_date`
/// fields. Linux SMBIOS provides both (Type 0 BIOS info), and many
/// DMI-based quirk rules in `drivers/acpi/osi.c`, `drivers/acpi/ec.c`,
/// and `drivers/platform/x86/` match on BIOS vendor or BIOS release
/// date for firmware-version workarounds. Linux source-of-truth:
/// `drivers/firmware/dmi_scan.c` (dmi_decode_table) and
/// `include/linux/mod_devicetable.h` (`dmi_system_id`).
#[derive(Clone, Debug, Default)]
pub struct DmiMatchRule {
pub sys_vendor: Option<Cow<'static, str>>,
@@ -12,6 +20,8 @@ pub struct DmiMatchRule {
pub product_name: Option<Cow<'static, str>>,
pub product_version: Option<Cow<'static, str>>,
pub bios_version: Option<Cow<'static, str>>,
pub bios_vendor: Option<Cow<'static, str>>,
pub bios_date: Option<Cow<'static, str>>,
}
impl DmiMatchRule {
@@ -23,6 +33,8 @@ impl DmiMatchRule {
&& self.product_name.is_none()
&& self.product_version.is_none()
&& self.bios_version.is_none()
&& self.bios_vendor.is_none()
&& self.bios_date.is_none()
}
pub fn matches(&self, info: &DmiInfo) -> bool {
@@ -89,10 +101,36 @@ impl DmiMatchRule {
return false;
}
}
if let Some(ref val) = self.bios_vendor {
if info
.bios_vendor
.as_deref()
.map_or(true, |v| v != val.as_ref())
{
return false;
}
}
if let Some(ref val) = self.bios_date {
if info
.bios_date
.as_deref()
.map_or(true, |v| v != val.as_ref())
{
return false;
}
}
true
}
}
/// DMI/SMBIOS system information as parsed from acpid's `/scheme/acpi/dmi`
/// text-format producer (or, in tests, from a synthetic fixture).
///
/// Phase R10 audit (2026-06-07) — added `bios_vendor` and `bios_date`
/// fields. These come from SMBIOS Type 0 (BIOS Information) and are
/// exposed by acpid along with the Type 1 (System Information) fields
/// (sys_vendor, product_name, board_*, etc.) that the original
/// implementation already covered.
#[derive(Clone, Debug, Default)]
pub struct DmiInfo {
pub sys_vendor: Option<String>,
@@ -102,6 +140,8 @@ pub struct DmiInfo {
pub product_name: Option<String>,
pub product_version: Option<String>,
pub bios_version: Option<String>,
pub bios_vendor: Option<String>,
pub bios_date: Option<String>,
}
/// A DMI-based quirk rule: if the system matches the DMI rule, apply PCI
@@ -157,6 +197,8 @@ fn parse_dmi_data(data: &str) -> Result<DmiInfo, ()> {
"product_name" => info.product_name = Some(value),
"product_version" => info.product_version = Some(value),
"bios_version" => info.bios_version = Some(value),
"bios_vendor" => info.bios_vendor = Some(value),
"bios_date" => info.bios_date = Some(value),
_ => {}
}
}
@@ -184,6 +226,8 @@ pub const DMI_PCI_QUIRK_RULES: &[DmiPciQuirkRule] = &[
board_version: None,
product_version: None,
bios_version: None,
bios_vendor: None,
bios_date: None,
},
vendor: 0x8086,
device: PCI_QUIRK_ANY_ID,
@@ -198,6 +242,8 @@ pub const DMI_PCI_QUIRK_RULES: &[DmiPciQuirkRule] = &[
board_version: None,
product_version: None,
bios_version: None,
bios_vendor: None,
bios_date: None,
},
vendor: PCI_QUIRK_ANY_ID,
device: PCI_QUIRK_ANY_ID,
@@ -212,6 +258,8 @@ pub const DMI_PCI_QUIRK_RULES: &[DmiPciQuirkRule] = &[
board_version: None,
product_version: None,
bios_version: None,
bios_vendor: None,
bios_date: None,
},
vendor: 0x14E4,
device: PCI_QUIRK_ANY_ID,
@@ -226,6 +274,8 @@ pub const DMI_PCI_QUIRK_RULES: &[DmiPciQuirkRule] = &[
board_version: None,
product_version: None,
bios_version: None,
bios_vendor: None,
bios_date: None,
},
vendor: 0x8086,
device: PCI_QUIRK_ANY_ID,
@@ -240,6 +290,8 @@ pub const DMI_PCI_QUIRK_RULES: &[DmiPciQuirkRule] = &[
product_name: None,
product_version: None,
bios_version: None,
bios_vendor: None,
bios_date: None,
},
vendor: 0x1002,
device: PCI_QUIRK_ANY_ID,
@@ -254,6 +306,8 @@ pub const DMI_PCI_QUIRK_RULES: &[DmiPciQuirkRule] = &[
board_version: None,
product_version: None,
bios_version: None,
bios_vendor: None,
bios_date: None,
},
vendor: 0x1002,
device: PCI_QUIRK_ANY_ID,
@@ -268,6 +322,8 @@ pub const DMI_PCI_QUIRK_RULES: &[DmiPciQuirkRule] = &[
board_version: None,
product_version: None,
bios_version: None,
bios_vendor: None,
bios_date: None,
},
vendor: PCI_QUIRK_ANY_ID,
device: PCI_QUIRK_ANY_ID,
@@ -282,6 +338,8 @@ pub const DMI_PCI_QUIRK_RULES: &[DmiPciQuirkRule] = &[
product_name: None,
product_version: None,
bios_version: None,
bios_vendor: None,
bios_date: None,
},
vendor: 0x1022,
device: PCI_QUIRK_ANY_ID,
@@ -407,6 +465,8 @@ mod tests {
board_version: None,
product_version: None,
bios_version: None,
bios_vendor: None,
bios_date: None,
};
let info = DmiInfo {
sys_vendor: Some("Framework".to_string()),
@@ -416,6 +476,8 @@ mod tests {
board_version: None,
product_version: None,
bios_version: None,
bios_vendor: None,
bios_date: None,
};
assert!(rule.matches(&info));
}
@@ -430,6 +492,8 @@ mod tests {
product_name: None,
product_version: None,
bios_version: None,
bios_vendor: None,
bios_date: None,
};
let info = DmiInfo {
sys_vendor: Some("Lenovo".to_string()),
@@ -439,6 +503,8 @@ mod tests {
product_name: None,
product_version: None,
bios_version: None,
bios_vendor: None,
bios_date: None,
};
assert!(!rule.matches(&info));
}
@@ -453,6 +519,8 @@ mod tests {
product_name: None,
product_version: None,
bios_version: None,
bios_vendor: None,
bios_date: None,
};
let info = DmiInfo {
sys_vendor: None,
@@ -462,6 +530,8 @@ mod tests {
product_name: None,
product_version: None,
bios_version: None,
bios_vendor: None,
bios_date: None,
};
assert!(!rule.matches(&info));
}
@@ -497,6 +567,8 @@ mod tests {
board_version: None,
product_version: None,
bios_version: None,
bios_vendor: None,
bios_date: None,
},
vendor: 0x1002,
device: PCI_QUIRK_ANY_ID,
@@ -506,4 +578,131 @@ mod tests {
let flags = apply_dmi_pci_quirk_rules(&info, None, &rules);
assert!(flags.is_empty());
}
#[test]
fn dmi_match_bios_vendor() {
let rule = DmiMatchRule {
sys_vendor: None,
board_vendor: None,
board_name: None,
board_version: None,
product_name: None,
product_version: None,
bios_version: None,
bios_vendor: Some(Cow::Borrowed("LENOVO")),
bios_date: None,
};
let info_match = DmiInfo {
sys_vendor: None,
board_vendor: None,
board_name: None,
board_version: None,
product_name: None,
product_version: None,
bios_version: None,
bios_vendor: Some("LENOVO".to_string()),
bios_date: None,
};
assert!(rule.matches(&info_match));
let info_miss = DmiInfo {
bios_vendor: Some("AMI".to_string()),
..info_match.clone()
};
assert!(!rule.matches(&info_miss));
let info_absent = DmiInfo::default();
assert!(!rule.matches(&info_absent));
}
#[test]
fn dmi_match_bios_date() {
let rule = DmiMatchRule {
sys_vendor: None,
board_vendor: None,
board_name: None,
board_version: None,
product_name: None,
product_version: None,
bios_version: None,
bios_vendor: None,
bios_date: Some(Cow::Borrowed("12/01/2020")),
};
let info_match = DmiInfo {
sys_vendor: None,
board_vendor: None,
board_name: None,
board_version: None,
product_name: None,
product_version: None,
bios_version: None,
bios_vendor: None,
bios_date: Some("12/01/2020".to_string()),
};
assert!(rule.matches(&info_match));
let info_miss = DmiInfo {
bios_date: Some("06/15/2021".to_string()),
..info_match.clone()
};
assert!(!rule.matches(&info_miss));
}
#[test]
fn dmi_match_combined_bios_vendor_and_date() {
let rule = DmiMatchRule {
sys_vendor: None,
board_vendor: None,
board_name: None,
board_version: None,
product_name: None,
product_version: None,
bios_version: None,
bios_vendor: Some(Cow::Borrowed("Dell Inc.")),
bios_date: Some(Cow::Borrowed("01/15/2022")),
};
let info_both = DmiInfo {
sys_vendor: None,
board_vendor: None,
board_name: None,
board_version: None,
product_name: None,
product_version: None,
bios_version: None,
bios_vendor: Some("Dell Inc.".to_string()),
bios_date: Some("01/15/2022".to_string()),
};
assert!(rule.matches(&info_both));
let info_vendor_only = DmiInfo {
bios_date: None,
..info_both.clone()
};
assert!(!rule.matches(&info_vendor_only));
}
#[test]
fn parse_dmi_data_bios_vendor_and_date() {
let data = "sys_vendor=LENOVO\n\
bios_vendor=LENOVO\n\
bios_date=12/01/2020\n\
product_name=ThinkPad X1 Carbon\n";
let info = parse_dmi_data(data).unwrap();
assert_eq!(info.sys_vendor.as_deref(), Some("LENOVO"));
assert_eq!(info.bios_vendor.as_deref(), Some("LENOVO"));
assert_eq!(info.bios_date.as_deref(), Some("12/01/2020"));
assert_eq!(info.product_name.as_deref(), Some("ThinkPad X1 Carbon"));
}
#[test]
fn dmi_match_rule_is_empty_with_bios_fields() {
let rule = DmiMatchRule::default();
assert!(rule.is_empty());
let rule = DmiMatchRule {
bios_vendor: Some(Cow::Borrowed("LENOVO")),
..DmiMatchRule::default()
};
assert!(!rule.is_empty());
let rule = DmiMatchRule {
bios_date: Some(Cow::Borrowed("12/01/2020")),
..DmiMatchRule::default()
};
assert!(!rule.is_empty());
}
}
@@ -480,6 +480,19 @@ fn parse_dmi_match_rule(table: &toml::Table, path: &str) -> Option<DmiMatchRule>
Ok(value) => value,
Err(()) => return None,
};
// Phase R10 audit (2026-06-07) — bios_vendor and bios_date. Linux SMBIOS
// Type 0 fields; many DMI-based rules in `drivers/acpi/osi.c`,
// `drivers/acpi/ec.c`, and `drivers/platform/x86/` match on BIOS vendor
// or BIOS release date. Source-of-truth:
// `drivers/firmware/dmi_scan.c` (dmi_decode_table).
let bios_vendor = match parse_string_field(table, "bios_vendor", path, "match") {
Ok(value) => value,
Err(()) => return None,
};
let bios_date = match parse_string_field(table, "bios_date", path, "match") {
Ok(value) => value,
Err(()) => return None,
};
let rule = DmiMatchRule {
sys_vendor,
@@ -489,6 +502,8 @@ fn parse_dmi_match_rule(table: &toml::Table, path: &str) -> Option<DmiMatchRule>
product_name,
product_version,
bios_version,
bios_vendor,
bios_date,
};
if rule.is_empty() {
@@ -1082,6 +1097,8 @@ mod tests {
board_version: None,
product_version: None,
bios_version: None,
bios_vendor: None,
bios_date: None,
};
let flags =
@@ -1115,6 +1132,8 @@ mod tests {
board_version: None,
product_version: None,
bios_version: None,
bios_vendor: None,
bios_date: None,
};
let flags =
@@ -1159,6 +1178,70 @@ mod tests {
assert!(rules.is_empty());
}
// -------------------------------------------------------------------------
// Phase R10 audit (2026-06-07) — bios_vendor and bios_date fields
// -------------------------------------------------------------------------
// Linux SMBIOS Type 0 (BIOS Information) provides both vendor and
// release date. Many DMI-based rules in `drivers/acpi/osi.c`,
// `drivers/acpi/ec.c`, and `drivers/platform/x86/` match on these
// fields for firmware-version workarounds. Source-of-truth:
// `drivers/firmware/dmi_scan.c` (dmi_decode_table).
#[test]
fn dmi_toml_bios_vendor_and_date_parse_and_match() {
let doc = r#"
[[dmi_system_quirk]]
pci_vendor = 0x8086
pci_device = 0x9D31
flags = ["no_msi"]
match.bios_vendor = "LENOVO"
match.bios_date = "12/01/2020"
"#
.parse::<toml::Value>()
.unwrap();
let mut rules = Vec::new();
parse_dmi_toml(&doc, &mut rules, "test.toml");
assert_eq!(rules.len(), 1);
let dmi_info_match = DmiInfo {
sys_vendor: None,
board_vendor: None,
board_name: None,
board_version: None,
product_name: None,
product_version: None,
bios_version: None,
bios_vendor: Some("LENOVO".to_string()),
bios_date: Some("12/01/2020".to_string()),
};
let flags_match = dmi::apply_dmi_pci_quirk_rules(
&make_info(0x8086, 0x9D31),
Some(&dmi_info_match),
&rules,
);
assert!(flags_match.contains(PciQuirkFlags::NO_MSI));
let dmi_info_wrong_date = DmiInfo {
bios_date: Some("06/15/2021".to_string()),
..dmi_info_match.clone()
};
let flags_miss = dmi::apply_dmi_pci_quirk_rules(
&make_info(0x8086, 0x9D31),
Some(&dmi_info_wrong_date),
&rules,
);
assert!(flags_miss.is_empty());
let dmi_info_absent = DmiInfo::default();
let flags_absent = dmi::apply_dmi_pci_quirk_rules(
&make_info(0x8086, 0x9D31),
Some(&dmi_info_absent),
&rules,
);
assert!(flags_absent.is_empty());
}
// -------------------------------------------------------------------------
// Phase R4 (2026-06-07) — TOML `action` field parsing
// -------------------------------------------------------------------------
@@ -1603,6 +1686,8 @@ mod tests {
board_version: None,
product_version: None,
bios_version: None,
bios_vendor: None,
bios_date: None,
};
let rules = vec![
DmiXhciQuirkRule {
@@ -1641,6 +1726,8 @@ mod tests {
board_version: None,
product_version: None,
bios_version: None,
bios_vendor: None,
bios_date: None,
};
let rules = vec![DmiXhciQuirkRule {
dmi_match: DmiMatchRule {