Skip to content

Commit c3cc11e

Browse files
xtask: Add support for running QEMU with an emulated TPM device
QEMU doesn't support attaching multiple TPM devices at the same time, so instead add a `--tpm` arg that accepts either `v1` or `v2`. This allows QEMU to be run with either an emulated TPM 1.2 device or an emulated TPM 2.0 device.
1 parent 1567d2c commit c3cc11e

File tree

4 files changed

+91
-1
lines changed

4 files changed

+91
-1
lines changed

xtask/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ mod opt;
77
mod pipe;
88
mod platform;
99
mod qemu;
10+
mod tpm;
1011
mod util;
1112

1213
use crate::opt::TestOpt;

xtask/src/opt.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
use crate::arch::UefiArch;
2-
use clap::{Parser, Subcommand};
2+
use clap::{Parser, Subcommand, ValueEnum};
33
use std::ops::Deref;
44
use std::path::PathBuf;
55

6+
#[derive(Clone, Copy, Debug, PartialEq, ValueEnum)]
7+
pub enum TpmVersion {
8+
V1,
9+
V2,
10+
}
11+
612
// Define some common options so that the doc strings don't have to be
713
// copy-pasted.
814

@@ -135,6 +141,10 @@ pub struct QemuOpt {
135141
#[clap(long, action)]
136142
pub headless: bool,
137143

144+
/// Attach a software TPM to QEMU.
145+
#[clap(long, action)]
146+
pub tpm: Option<TpmVersion>,
147+
138148
/// Path of an OVMF code file.
139149
#[clap(long, action)]
140150
pub ovmf_code: Option<PathBuf>,

xtask/src/qemu.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use crate::arch::UefiArch;
22
use crate::disk::{check_mbr_test_disk, create_mbr_test_disk};
33
use crate::opt::QemuOpt;
44
use crate::pipe::Pipe;
5+
use crate::tpm::Swtpm;
56
use crate::util::command_to_string;
67
use crate::{net, platform};
78
use anyhow::{bail, Context, Result};
@@ -539,6 +540,15 @@ pub fn run_qemu(arch: UefiArch, opt: &QemuOpt) -> Result<()> {
539540
None
540541
};
541542

543+
// Set up a software TPM if requested.
544+
let _tpm = if let Some(tpm_version) = opt.tpm {
545+
let tpm = Swtpm::spawn(tpm_version)?;
546+
cmd.args(tpm.qemu_args());
547+
Some(tpm)
548+
} else {
549+
None
550+
};
551+
542552
println!("{}", command_to_string(&cmd));
543553

544554
cmd.stdin(Stdio::piped());

xtask/src/tpm.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
use crate::opt::TpmVersion;
2+
use crate::util::command_to_string;
3+
use anyhow::Result;
4+
use std::process::{Child, Command};
5+
use tempfile::TempDir;
6+
7+
// Adapted from https://qemu.readthedocs.io/en/latest/specs/tpm.html
8+
9+
/// Wrapper for running `swtpm`, a software TPM emulator.
10+
///
11+
/// <https://github.com/stefanberger/swtpm>
12+
///
13+
/// The process is killed on drop.
14+
pub struct Swtpm {
15+
tmp_dir: TempDir,
16+
child: Child,
17+
}
18+
19+
impl Swtpm {
20+
/// Run `swtpm` in a new process.
21+
pub fn spawn(version: TpmVersion) -> Result<Self> {
22+
let tmp_dir = TempDir::new()?;
23+
let tmp_path = tmp_dir.path().to_str().unwrap();
24+
25+
let mut cmd = Command::new("swtpm");
26+
cmd.args(&[
27+
"socket",
28+
"--tpmstate",
29+
&format!("dir={tmp_path}"),
30+
"--ctrl",
31+
&format!("type=unixio,path={tmp_path}/swtpm-sock"),
32+
// Terminate when the connection drops. If for any reason
33+
// this fails, the process will be killed on drop.
34+
"--terminate",
35+
// Hide some log spam.
36+
"--log",
37+
"file=-",
38+
]);
39+
40+
if version == TpmVersion::V2 {
41+
cmd.arg("--tpm2");
42+
}
43+
44+
println!("{}", command_to_string(&cmd));
45+
let child = cmd.spawn()?;
46+
47+
Ok(Self { tmp_dir, child })
48+
}
49+
50+
/// Get the QEMU args needed to connect to the TPM emulator.
51+
pub fn qemu_args(&self) -> Vec<String> {
52+
let socket_path = self.tmp_dir.path().join("swtpm-sock");
53+
vec![
54+
"-chardev".into(),
55+
format!("socket,id=chrtpm0,path={}", socket_path.to_str().unwrap()),
56+
"-tpmdev".into(),
57+
"emulator,id=tpm0,chardev=chrtpm0".into(),
58+
"-device".into(),
59+
"tpm-tis,tpmdev=tpm0".into(),
60+
]
61+
}
62+
}
63+
64+
impl Drop for Swtpm {
65+
fn drop(&mut self) {
66+
let _ = self.child.kill();
67+
let _ = self.child.wait();
68+
}
69+
}

0 commit comments

Comments
 (0)