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:
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user