Improve IOMMU self-test diagnostics
This commit is contained in:
@@ -22,6 +22,32 @@ struct StderrLogger {
|
|||||||
level: LevelFilter,
|
level: LevelFilter,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(not(target_os = "redox"), allow(dead_code))]
|
||||||
|
struct DiscoveryResult {
|
||||||
|
units: Vec<AmdViUnit>,
|
||||||
|
source: DiscoverySource,
|
||||||
|
kernel_acpi_status: &'static str,
|
||||||
|
ivrs_path: Option<PathBuf>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(not(target_os = "redox"), allow(dead_code))]
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
enum DiscoverySource {
|
||||||
|
KernelAcpi,
|
||||||
|
Filesystem,
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DiscoverySource {
|
||||||
|
fn as_str(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::KernelAcpi => "kernel_acpi",
|
||||||
|
Self::Filesystem => "filesystem",
|
||||||
|
Self::None => "none",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl log::Log for StderrLogger {
|
impl log::Log for StderrLogger {
|
||||||
fn enabled(&self, metadata: &Metadata) -> bool {
|
fn enabled(&self, metadata: &Metadata) -> bool {
|
||||||
metadata.level() <= self.level
|
metadata.level() <= self.level
|
||||||
@@ -65,17 +91,22 @@ fn discover_ivrs_path() -> Option<PathBuf> {
|
|||||||
discover_ivrs_path_from_candidates(&candidate_ivrs_paths())
|
discover_ivrs_path_from_candidates(&candidate_ivrs_paths())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn detect_units() -> Result<Vec<AmdViUnit>, String> {
|
fn detect_units_from_ivrs_path(path: &PathBuf) -> Result<Vec<AmdViUnit>, String> {
|
||||||
let Some(path) = discover_ivrs_path() else {
|
let bytes = fs::read(path)
|
||||||
return Ok(Vec::new());
|
|
||||||
};
|
|
||||||
|
|
||||||
let bytes = fs::read(&path)
|
|
||||||
.map_err(|err| format!("failed to read IVRS table from {}: {err}", path.display()))?;
|
.map_err(|err| format!("failed to read IVRS table from {}: {err}", path.display()))?;
|
||||||
let units = AmdViUnit::detect(&bytes).map_err(|err| format!("failed to parse IVRS: {err}"))?;
|
let units = AmdViUnit::detect(&bytes).map_err(|err| format!("failed to parse IVRS: {err}"))?;
|
||||||
Ok(units)
|
Ok(units)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn detect_units_from_discovered_ivrs() -> Result<(Vec<AmdViUnit>, Option<PathBuf>), String> {
|
||||||
|
let Some(path) = discover_ivrs_path() else {
|
||||||
|
return Ok((Vec::new(), None));
|
||||||
|
};
|
||||||
|
|
||||||
|
let units = detect_units_from_ivrs_path(&path)?;
|
||||||
|
Ok((units, Some(path)))
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "redox")]
|
#[cfg(target_os = "redox")]
|
||||||
const ACPI_HEADER_LEN: usize = 36;
|
const ACPI_HEADER_LEN: usize = 36;
|
||||||
|
|
||||||
@@ -149,13 +180,81 @@ fn detect_units_from_kernel_acpi() -> Result<Vec<AmdViUnit>, String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "redox")]
|
#[cfg(target_os = "redox")]
|
||||||
fn run() -> Result<(), String> {
|
fn discover_units() -> Result<DiscoveryResult, String> {
|
||||||
let units = detect_units_from_kernel_acpi().or_else(|err| {
|
match detect_units_from_kernel_acpi() {
|
||||||
|
Ok(units) if !units.is_empty() => Ok(DiscoveryResult {
|
||||||
|
units,
|
||||||
|
source: DiscoverySource::KernelAcpi,
|
||||||
|
kernel_acpi_status: "ok",
|
||||||
|
ivrs_path: None,
|
||||||
|
}),
|
||||||
|
Ok(_units) => {
|
||||||
|
let (units, ivrs_path) = detect_units_from_discovered_ivrs()?;
|
||||||
|
Ok(DiscoveryResult {
|
||||||
|
source: if ivrs_path.is_some() {
|
||||||
|
DiscoverySource::Filesystem
|
||||||
|
} else {
|
||||||
|
DiscoverySource::None
|
||||||
|
},
|
||||||
|
units,
|
||||||
|
kernel_acpi_status: "empty",
|
||||||
|
ivrs_path,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
info!("iommu: kernel ACPI discovery unavailable: {err}");
|
info!("iommu: kernel ACPI discovery unavailable: {err}");
|
||||||
detect_units()
|
let (units, ivrs_path) = detect_units_from_discovered_ivrs()?;
|
||||||
})?;
|
Ok(DiscoveryResult {
|
||||||
info!("iommu: detected {} AMD-Vi unit(s)", units.len());
|
source: if ivrs_path.is_some() {
|
||||||
for (index, unit) in units.iter().enumerate() {
|
DiscoverySource::Filesystem
|
||||||
|
} else {
|
||||||
|
DiscoverySource::None
|
||||||
|
},
|
||||||
|
units,
|
||||||
|
kernel_acpi_status: "error",
|
||||||
|
ivrs_path,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "redox"))]
|
||||||
|
fn discover_units() -> Result<DiscoveryResult, String> {
|
||||||
|
let (units, ivrs_path) = detect_units_from_discovered_ivrs()?;
|
||||||
|
Ok(DiscoveryResult {
|
||||||
|
source: if ivrs_path.is_some() {
|
||||||
|
DiscoverySource::Filesystem
|
||||||
|
} else {
|
||||||
|
DiscoverySource::None
|
||||||
|
},
|
||||||
|
units,
|
||||||
|
kernel_acpi_status: "unsupported",
|
||||||
|
ivrs_path,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "redox")]
|
||||||
|
fn run() -> Result<(), String> {
|
||||||
|
let discovery = discover_units()?;
|
||||||
|
if discovery.units.is_empty() {
|
||||||
|
info!(
|
||||||
|
"iommu: no AMD-Vi units found (source={}, kernel_acpi_status={}, ivrs_path={})",
|
||||||
|
discovery.source.as_str(),
|
||||||
|
discovery.kernel_acpi_status,
|
||||||
|
discovery
|
||||||
|
.ivrs_path
|
||||||
|
.as_ref()
|
||||||
|
.map(|path| path.display().to_string())
|
||||||
|
.unwrap_or_else(|| "none".to_string())
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
info!(
|
||||||
|
"iommu: detected {} AMD-Vi unit(s) via {}",
|
||||||
|
discovery.units.len(),
|
||||||
|
discovery.source.as_str()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for (index, unit) in discovery.units.iter().enumerate() {
|
||||||
info!(
|
info!(
|
||||||
"iommu: discovered unit {} at MMIO {:#x}; initialization is deferred until first use",
|
"iommu: discovered unit {} at MMIO {:#x}; initialization is deferred until first use",
|
||||||
index,
|
index,
|
||||||
@@ -167,7 +266,7 @@ fn run() -> Result<(), String> {
|
|||||||
Socket::create("iommu").map_err(|e| format!("failed to register iommu scheme: {e}"))?;
|
Socket::create("iommu").map_err(|e| format!("failed to register iommu scheme: {e}"))?;
|
||||||
info!("iommu: registered scheme:iommu");
|
info!("iommu: registered scheme:iommu");
|
||||||
|
|
||||||
let mut scheme = IommuScheme::with_units(units);
|
let mut scheme = IommuScheme::with_units(discovery.units);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let request = match socket.next_request(SignalBehavior::Restart) {
|
let request = match socket.next_request(SignalBehavior::Restart) {
|
||||||
@@ -204,11 +303,19 @@ fn run() -> Result<(), String> {
|
|||||||
|
|
||||||
#[cfg(target_os = "redox")]
|
#[cfg(target_os = "redox")]
|
||||||
fn run_self_test() -> Result<(), String> {
|
fn run_self_test() -> Result<(), String> {
|
||||||
let mut units = detect_units_from_kernel_acpi().or_else(|err| {
|
let discovery = discover_units()?;
|
||||||
info!("iommu: kernel ACPI discovery unavailable: {err}");
|
let mut units = discovery.units;
|
||||||
detect_units()
|
|
||||||
})?;
|
|
||||||
|
|
||||||
|
println!("discovery_source={}", discovery.source.as_str());
|
||||||
|
println!("kernel_acpi_status={}", discovery.kernel_acpi_status);
|
||||||
|
println!(
|
||||||
|
"ivrs_path={}",
|
||||||
|
discovery
|
||||||
|
.ivrs_path
|
||||||
|
.as_ref()
|
||||||
|
.map(|path| path.display().to_string())
|
||||||
|
.unwrap_or_else(|| "none".to_string())
|
||||||
|
);
|
||||||
println!("units_detected={}", units.len());
|
println!("units_detected={}", units.len());
|
||||||
if units.is_empty() {
|
if units.is_empty() {
|
||||||
return Err("iommu self-test detected zero AMD-Vi unit(s)".to_string());
|
return Err("iommu self-test detected zero AMD-Vi unit(s)".to_string());
|
||||||
@@ -254,10 +361,11 @@ fn run_self_test() -> Result<(), String> {
|
|||||||
|
|
||||||
#[cfg(not(target_os = "redox"))]
|
#[cfg(not(target_os = "redox"))]
|
||||||
fn run() -> Result<(), String> {
|
fn run() -> Result<(), String> {
|
||||||
let units = detect_units()?;
|
let discovery = discover_units()?;
|
||||||
info!(
|
info!(
|
||||||
"iommu: host build stub active; parsed {} AMD-Vi unit(s) from discovered IVRS source",
|
"iommu: host build stub active; parsed {} AMD-Vi unit(s) via {}",
|
||||||
units.len()
|
discovery.units.len(),
|
||||||
|
discovery.source.as_str()
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -292,7 +400,9 @@ fn main() {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{candidate_ivrs_paths, discover_ivrs_path_from_candidates};
|
use super::{
|
||||||
|
candidate_ivrs_paths, discover_ivrs_path_from_candidates, DiscoverySource,
|
||||||
|
};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -313,4 +423,11 @@ mod tests {
|
|||||||
let discovered = discover_ivrs_path_from_candidates(&candidates);
|
let discovered = discover_ivrs_path_from_candidates(&candidates);
|
||||||
assert_eq!(discovered, Some(PathBuf::from("/tmp")));
|
assert_eq!(discovered, Some(PathBuf::from("/tmp")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn discovery_source_strings_are_stable() {
|
||||||
|
assert_eq!(DiscoverySource::KernelAcpi.as_str(), "kernel_acpi");
|
||||||
|
assert_eq!(DiscoverySource::Filesystem.as_str(), "filesystem");
|
||||||
|
assert_eq!(DiscoverySource::None.as_str(), "none");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,9 @@ fn run() -> Result<(), String> {
|
|||||||
if !stdout.contains("units_detected=") {
|
if !stdout.contains("units_detected=") {
|
||||||
return Err("iommu self-test did not report detected unit count".to_string());
|
return Err("iommu self-test did not report detected unit count".to_string());
|
||||||
}
|
}
|
||||||
|
if !stdout.contains("discovery_source=") {
|
||||||
|
return Err("iommu self-test did not report discovery source".to_string());
|
||||||
|
}
|
||||||
if !stdout.contains("units_initialized_now=") {
|
if !stdout.contains("units_initialized_now=") {
|
||||||
return Err("iommu self-test did not report initialized unit count".to_string());
|
return Err("iommu self-test did not report initialized unit count".to_string());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user