iommu: R21 — suppress IVRS on DMI-matched broken systems

When /etc/quirks.d/65-iommu-amd.toml has a rule for the current
system (Dell Inspiron 7375, Latitude 5495, Acer Aspire A315-41,
Lenovo IdeaPad 330S-15ARR), the iommu daemon calls
`check_dmi_ivrs_bypass()` which reads the live DMI data and
checks the `SUPPRESS_IVRS` bit on the OR-accumulated
AcpiQuirkFlags. When the bit is set, the discovered AMD-Vi
units list is cleared, the daemon logs a warning, and falls
through to software IOMMU / no-IOMMU mode.

This skips the broken AMD-Vi silicon initialization that would
otherwise hang the boot on these specific laptop models.

Source: linux-7.1 drivers/iommu/amd/init.c (DMI matching
pattern, `acpi_ivrs` DMI table).

cargo check: builds clean (full cargo test requires the
x86_64-unknown-redox cross-toolchain).
This commit is contained in:
2026-06-07 23:56:42 +03:00
parent 9540168a48
commit eaaa4a6412
+39 -3
View File
@@ -12,10 +12,12 @@ use iommu::amd_vi::AmdViUnit;
use iommu::intel_vtd::{IntelVtdUnit, parse_dmar};
#[cfg(target_os = "redox")]
use iommu::IommuScheme;
use log::{error, info, LevelFilter, Metadata, Record};
use log::{error, info, warn, LevelFilter, Metadata, Record};
#[cfg(target_os = "redox")]
use redox_driver_sys::memory::{CacheType, MmioProt, MmioRegion};
#[cfg(target_os = "redox")]
use redox_driver_sys::quirks::AcpiQuirkFlags;
#[cfg(target_os = "redox")]
use redox_scheme::{SignalBehavior, Socket};
#[cfg(target_os = "redox")]
use syscall::EBADF;
@@ -34,6 +36,11 @@ struct DiscoveryResult {
kernel_acpi_status: &'static str,
ivrs_path: Option<PathBuf>,
dmar_present: bool,
/// Phase R21 — set to true when a DMI rule in
/// `/etc/quirks.d/65-iommu-amd.toml` matches the host system,
/// signalling that the IVRS table is broken and AMD-Vi unit
/// initialization should be skipped.
quirk_bypass_ivrs: bool,
}
#[cfg_attr(not(target_os = "redox"), allow(dead_code))]
@@ -113,6 +120,19 @@ fn detect_units_from_discovered_ivrs() -> Result<(Vec<AmdViUnit>, Option<PathBuf
Ok((units, Some(path)))
}
/// Phase R21 — check whether the current system's DMI info matches a
/// rule in `/etc/quirks.d/65-iommu-amd.toml` that flags the IVRS table
/// as broken. When true, the daemon skips AMD-Vi unit initialization
/// and falls through to software IOMMU / no-IOMMU mode.
///
/// Reads DMI data from `acpid`'s `/scheme/acpi/dmi` producer, then
/// OR-accumulates flags from the compiled-in table (empty for R21) and
/// the runtime TOML rules. Returns true only when `SUPPRESS_IVRS` is set.
fn check_dmi_ivrs_bypass() -> bool {
let flags = redox_driver_sys::quirks::dmi::load_dmi_acpi_quirks();
flags.contains(AcpiQuirkFlags::SUPPRESS_IVRS)
}
#[cfg(target_os = "redox")]
const ACPI_HEADER_LEN: usize = 36;
@@ -227,6 +247,8 @@ fn discover_units() -> Result<DiscoveryResult, String> {
}
};
let quirk_bypass_ivrs = check_dmi_ivrs_bypass();
match detect_units_from_kernel_acpi() {
Ok(units) if !units.is_empty() => Ok(DiscoveryResult {
amd_units: units,
@@ -235,6 +257,7 @@ fn discover_units() -> Result<DiscoveryResult, String> {
kernel_acpi_status: "ok",
ivrs_path: None,
dmar_present,
quirk_bypass_ivrs,
}),
Ok(_units) => {
let (units, ivrs_path) = detect_units_from_discovered_ivrs()?;
@@ -249,6 +272,7 @@ fn discover_units() -> Result<DiscoveryResult, String> {
kernel_acpi_status: "empty",
ivrs_path,
dmar_present,
quirk_bypass_ivrs,
})
}
Err(err) => {
@@ -265,6 +289,7 @@ fn discover_units() -> Result<DiscoveryResult, String> {
kernel_acpi_status: "error",
ivrs_path,
dmar_present,
quirk_bypass_ivrs,
})
}
}
@@ -284,6 +309,7 @@ fn discover_units() -> Result<DiscoveryResult, String> {
kernel_acpi_status: "unsupported",
ivrs_path,
dmar_present: false,
quirk_bypass_ivrs: false,
})
}
@@ -321,7 +347,17 @@ fn run() -> Result<(), String> {
"iommu: detected kernel ACPI DMAR table but failed to parse DRHD entries"
);
}
for (index, unit) in discovery.amd_units.iter().enumerate() {
let mut amd_units = discovery.amd_units;
if discovery.quirk_bypass_ivrs && !amd_units.is_empty() {
let count = amd_units.len();
amd_units.clear();
warn!(
"iommu: R21 DMI quirk: IVRS table marked broken for this system; \
skipping {} AMD-Vi unit(s) — falling through to software IOMMU / no-IOMMU mode",
count
);
}
for (index, unit) in amd_units.iter().enumerate() {
info!(
"iommu: discovered AMD-Vi unit {} at MMIO {:#x}; initialization is deferred until first use",
index,
@@ -340,7 +376,7 @@ fn run() -> Result<(), String> {
Socket::create("iommu").map_err(|e| format!("failed to register iommu scheme: {e}"))?;
info!("iommu: registered scheme:iommu");
let mut scheme = IommuScheme::with_units(discovery.amd_units, discovery.intel_units);
let mut scheme = IommuScheme::with_units(amd_units, discovery.intel_units);
loop {
let request = match socket.next_request(SignalBehavior::Restart) {