iommu: signal kernel /scheme/irq/remapping after IR init
Completes the daemon side of the Gap 11 fix from LOWLEVEL plan v1.1. The kernel exposes /scheme/irq/remapping for the iommu daemon to control the MSI validation gate (set_iommu_remapping_active in kernel/src/scheme/irq.rs). This change: - Adds IommuScheme::has_interrupt_remapping() that returns true once at least one AMD-Vi or Intel VT-d unit has been initialized. - Adds set_kernel_remapping(active: bool) helper in main.rs that opens /scheme/irq/remapping and writes "1" (activate) or "0" (deactivate). - Calls set_kernel_remapping(true) in the main loop after the first request that leaves a unit initialized. Idempotent: subsequent writes are harmless. - Calls set_kernel_remapping(false) unconditionally on exit so the kernel does not retain stale state if the daemon terminates. The write failures are non-fatal: in environments where the scheme is not available (QEMU without PCI passthrough, very early boot), the daemon continues and MSI delivery works without remapping, which the kernel handles correctly via the existing fallback in iommu_validate_msi_irq().
This commit is contained in:
@@ -191,6 +191,14 @@ impl IommuScheme {
|
||||
self.amd_units.len() + self.intel_units.len()
|
||||
}
|
||||
|
||||
/// Returns true if at least one AMD-Vi or Intel VT-d unit has been
|
||||
/// successfully initialized. Used by the daemon to decide when to
|
||||
/// signal the kernel that IOMMU interrupt remapping is active.
|
||||
pub fn has_interrupt_remapping(&self) -> bool {
|
||||
self.amd_units.iter().any(|unit| unit.initialized())
|
||||
|| self.intel_units.iter().any(|unit| unit.initialized())
|
||||
}
|
||||
|
||||
fn insert_handle(&mut self, kind: HandleKind) -> usize {
|
||||
let id = self.next_id;
|
||||
self.next_id = self.next_id.saturating_add(1);
|
||||
|
||||
@@ -377,6 +377,7 @@ fn run() -> Result<(), String> {
|
||||
info!("iommu: registered scheme:iommu");
|
||||
|
||||
let mut scheme = IommuScheme::with_units(amd_units, discovery.intel_units);
|
||||
let mut remapping_signalled = false;
|
||||
|
||||
loop {
|
||||
let request = match socket.next_request(SignalBehavior::Restart) {
|
||||
@@ -403,6 +404,16 @@ fn run() -> Result<(), String> {
|
||||
}
|
||||
};
|
||||
|
||||
// After the daemon has initialized at least one interrupt-remapping
|
||||
// capable unit, signal the kernel once. We poll the scheme's status
|
||||
// (cheap, no IPC) instead of introspecting the request. Idempotent
|
||||
// and harmless. The unconditional deactivation on exit (in main())
|
||||
// clears the kernel state if the daemon terminates unexpectedly.
|
||||
if !remapping_signalled && scheme.has_interrupt_remapping() {
|
||||
set_kernel_remapping(true);
|
||||
remapping_signalled = true;
|
||||
}
|
||||
|
||||
if let Err(e) = socket.write_response(response, SignalBehavior::Restart) {
|
||||
error!("iommu: failed to write response: {e}");
|
||||
}
|
||||
@@ -524,12 +535,54 @@ fn main() {
|
||||
run()
|
||||
};
|
||||
|
||||
// Always deactivate remapping on exit so the kernel does not retain
|
||||
// stale "remapping active" state if the daemon dies.
|
||||
set_kernel_remapping(false);
|
||||
|
||||
if let Err(e) = result {
|
||||
error!("iommu: fatal error: {e}");
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/// Inform the kernel whether IOMMU interrupt remapping is currently active.
|
||||
///
|
||||
/// The kernel exposes `/scheme/irq/remapping` for this purpose. Writing `b"1"`
|
||||
/// activates the gate; writing `b"0"` deactivates it. When active, the kernel
|
||||
/// trusts the IOMMU hardware to validate remapped MSI vectors instead of
|
||||
/// attempting software-side validation.
|
||||
///
|
||||
/// Failures are logged but not fatal: if the kernel scheme is not reachable
|
||||
/// (e.g. early boot, QEMU without PCI passthrough), the daemon continues and
|
||||
/// MSI delivery works without remapping — the kernel handles that path.
|
||||
fn set_kernel_remapping(active: bool) {
|
||||
let path = "/scheme/irq/remapping";
|
||||
let value: &[u8] = if active { b"1" } else { b"0" };
|
||||
match fs::OpenOptions::new().write(true).open(path) {
|
||||
Ok(mut fd) => {
|
||||
use std::io::Write;
|
||||
match fd.write_all(value) {
|
||||
Ok(_) => info!(
|
||||
"iommu: kernel remapping {}",
|
||||
if active { "ACTIVATED" } else { "DEACTIVATED" }
|
||||
),
|
||||
Err(e) => warn!(
|
||||
"iommu: failed to write {} to {}: {}",
|
||||
String::from_utf8_lossy(value),
|
||||
path,
|
||||
e
|
||||
),
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
// Not fatal — the scheme may not be available in all environments.
|
||||
// The kernel default (remapping=false) is safe for QEMU and bare metal
|
||||
// without an IOMMU.
|
||||
warn!("iommu: cannot open {}: {} (continuing)", path, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{
|
||||
|
||||
Reference in New Issue
Block a user