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:
@@ -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 6–20 (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 {
|
||||
|
||||
Reference in New Issue
Block a user