Skip to content

Commit aebd97b

Browse files
authored
Merge pull request #2 from edera-dev/bleggett/iommu-checks
Add explicit IOMMU checking
2 parents 1da8923 + 5737f6f commit aebd97b

5 files changed

Lines changed: 117 additions & 9 deletions

File tree

.github/workflows/release-artifacts.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ jobs:
9393
persist-credentials: false
9494

9595
- name: 'Build and sign protect-${{ matrix.component }} image'
96-
uses: edera-dev/actions/build-and-sign-image@v0.0.3
96+
uses: edera-dev/actions/build-and-sign-image@v0.0.6
9797
with:
9898
component: '${{ matrix.component }}'
9999
event: '${{ github.event_name }}'

src/checkers/iommu.rs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
use crate::helpers::{
2+
CheckGroup, CheckGroupResult, CheckResult,
3+
CheckResultValue::{Errored, Failed, Passed, Skipped},
4+
host_executor::HostNamespaceExecutor,
5+
};
6+
use async_trait::async_trait;
7+
use futures::{FutureExt, future::join_all};
8+
use log::debug;
9+
use std::path::Path;
10+
11+
const GROUP_IDENTIFIER: &str = "IOMMUChecks";
12+
const NAME: &str = "IOMMU Checks";
13+
14+
pub struct IOMMUChecks {
15+
host_executor: HostNamespaceExecutor,
16+
}
17+
18+
#[cfg(target_arch = "x86_64")]
19+
impl IOMMUChecks {
20+
pub fn new(host_executor: HostNamespaceExecutor) -> Self {
21+
IOMMUChecks { host_executor }
22+
}
23+
24+
/// Run all the checkers asynchronously, then
25+
/// join and collect the results.
26+
pub async fn run_all(&self) -> CheckGroupResult {
27+
let results = join_all([self.check_iommu().boxed()]).await;
28+
29+
let mut group_result = Skipped;
30+
for res in results.iter() {
31+
// Set group result to Failed if we failed and aren't already in an Errored state
32+
if !matches!(group_result, Errored(_)) && matches!(res.result, Failed(_)) {
33+
group_result = Failed(String::from("group failed"));
34+
}
35+
36+
if matches!(res.result, Errored(_)) {
37+
group_result = Errored(String::from("group errored"));
38+
}
39+
}
40+
41+
CheckGroupResult {
42+
name: NAME.to_string(),
43+
result: group_result,
44+
results,
45+
}
46+
}
47+
48+
async fn check_iommu(&self) -> CheckResult {
49+
let name = String::from("IOMMU Support");
50+
51+
match self
52+
.host_executor
53+
.spawn_in_host_ns(async {
54+
if Path::new("/sys/firmware/acpi/tables/DMAR").exists() {
55+
debug!("Found Intel IOMMU");
56+
true
57+
} else if Path::new("/sys/firmware/acpi/tables/IVRS").exists() {
58+
debug!("Found AMD IOMMU");
59+
true
60+
} else {
61+
false
62+
}
63+
})
64+
.await
65+
{
66+
Ok(true) => CheckResult::new(&name, Passed),
67+
Ok(false) => CheckResult::new(&name, Failed("no IOMMU detected".to_string())),
68+
Err(e) => {
69+
debug!("Error: {}", e);
70+
CheckResult::new(&name, Errored(e.to_string()))
71+
}
72+
}
73+
}
74+
}
75+
76+
// No-op for other archs
77+
// TODO(bml) arm64 IOMMU??
78+
#[cfg(not(target_arch = "x86_64"))]
79+
impl IOMMUChecks {
80+
pub fn new(host_executor: HostNamespaceExecutor) -> Self {
81+
IOMMUChecks { host_executor }
82+
}
83+
84+
pub async fn run_all(&self) -> CheckGroupResult {
85+
CheckGroupResult::new(NAME)
86+
}
87+
}
88+
89+
#[async_trait]
90+
impl CheckGroup for IOMMUChecks {
91+
fn id(&self) -> &str {
92+
GROUP_IDENTIFIER
93+
}
94+
95+
fn name(&self) -> &str {
96+
NAME
97+
}
98+
99+
fn description(&self) -> &str {
100+
"IOMMU capability checks"
101+
}
102+
103+
async fn run(&self) -> CheckGroupResult {
104+
self.run_all().await
105+
}
106+
}

src/checkers/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
pub mod iommu;
12
pub mod kernel;
23
pub mod pvh;
34
pub mod system;

src/checkers/pvh.rs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::helpers::{
66
use anyhow::{Result, bail};
77
use async_trait::async_trait;
88
use futures::{FutureExt, future::join_all};
9-
use log::{debug, warn};
9+
use log::{debug, error, warn};
1010
use std::{
1111
fs,
1212
fs::File,
@@ -87,7 +87,7 @@ impl PVHChecks {
8787
)
8888
}
8989
Err(e) => {
90-
eprintln!("Error: {}", e);
90+
error!("Error: {}", e);
9191
CheckResult::new(&name, Errored(e.to_string()))
9292
}
9393
}
@@ -106,7 +106,7 @@ impl PVHChecks {
106106
"GenuineIntel" => self.check_intel(&flags).await,
107107
"AuthenticAMD" => self.check_amd(&flags).await,
108108
_ => {
109-
eprintln!("Unknown CPU vendor: {}", cpu_vendor);
109+
error!("Unknown CPU vendor: {}", cpu_vendor);
110110
Ok(VirtStatus::Disabled)
111111
}
112112
}
@@ -195,14 +195,12 @@ impl PVHChecks {
195195
}
196196
}
197197
Err(e) => {
198-
eprintln!("WARN: Cannot read EFER: {}", e);
198+
debug!("Cannot read EFER: {}", e);
199199
if has_svm {
200-
eprintln!(
201-
" Hardware supports AMD-V, assuming it can be enabled"
202-
);
200+
debug!("Hardware supports AMD-V, assuming it can be enabled");
203201
Ok(VirtStatus::CanBeEnabled)
204202
} else {
205-
eprintln!(" Cannot determine if AMD-V can be used");
203+
debug!("Cannot determine if AMD-V can be used");
206204
Ok(VirtStatus::Disabled)
207205
}
208206
}

src/main.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ use std::{
2121
};
2222
use tokio::task::JoinHandle;
2323

24+
use crate::checkers::iommu::IOMMUChecks;
25+
2426
// Skip certain groups. List is separated by ;
2527
fn skip_groups() -> Vec<String> {
2628
let skips = env::var("EDERA_PREFLIGHT_SKIP_GROUPS").unwrap_or_default();
@@ -110,6 +112,7 @@ async fn main() -> Result<()> {
110112
let groups: Vec<Box<dyn CheckGroup>> = vec![
111113
Box::new(SystemChecks::new(host_executor.clone())),
112114
Box::new(PVHChecks::new(host_executor.clone())),
115+
Box::new(IOMMUChecks::new(host_executor.clone())),
113116
Box::new(KernelChecks::new(host_executor.clone())),
114117
Box::new(SystemRecorder::new(host_executor.clone())),
115118
];

0 commit comments

Comments
 (0)