Skip to content

Commit 81cf331

Browse files
committed
feat(tpm): initial tpm support code, we just tell systemd about the pcr banks right now
1 parent 6602e1d commit 81cf331

File tree

4 files changed

+118
-0
lines changed

4 files changed

+118
-0
lines changed

src/integrations/bootloader_interface.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,4 +148,16 @@ impl BootloaderInterface {
148148
VariableClass::BootAndRuntimeTemporary,
149149
)
150150
}
151+
152+
/// Tell the system what the number of active PCR banks is.
153+
/// If this is zero, that is okay.
154+
pub fn set_tpm2_active_pcr_banks(value: u32) -> Result<()> {
155+
// Format the value into the specification format.
156+
let value = format!("0x{:08x}", value);
157+
Self::VENDOR.set_cstr16(
158+
"LoaderTpm2ActivePcrBanks",
159+
&value,
160+
VariableClass::BootAndRuntimeTemporary,
161+
)
162+
}
151163
}

src/main.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use crate::options::SproutOptions;
1313
use crate::options::parser::OptionsRepresentable;
1414
use crate::phases::phase;
1515
use crate::platform::timer::PlatformTimer;
16+
use crate::platform::tpm::PlatformTpm;
1617
use crate::secure::SecureBoot;
1718
use crate::utils::PartitionGuidForm;
1819
use anyhow::{Context, Result, bail};
@@ -92,6 +93,13 @@ fn run() -> Result<()> {
9293
BootloaderInterface::set_loader_info()
9394
.context("unable to set loader info in bootloader interface")?;
9495

96+
// Acquire the number of active PCR banks on the TPM.
97+
// If no TPM is available, this will return zero.
98+
let active_pcr_banks = PlatformTpm::active_pcr_banks()?;
99+
// Tell the bootloader interface what the number of active PCR banks is.
100+
BootloaderInterface::set_tpm2_active_pcr_banks(active_pcr_banks)
101+
.context("unable to set tpm2 active PCR banks in bootloader interface")?;
102+
95103
// Parse the options to the sprout executable.
96104
let options = SproutOptions::parse().context("unable to parse options")?;
97105

src/platform.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
/// timer: Platform timer support.
22
pub mod timer;
3+
/// tpm: Platform TPM support.
4+
pub mod tpm;

src/platform/tpm.rs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
use crate::utils;
2+
use anyhow::{Context, Result};
3+
use uefi::boot::ScopedProtocol;
4+
use uefi::proto::tcg::v2::Tcg;
5+
use uefi_raw::protocol::tcg::v2::{Tcg2Protocol, Tcg2Version};
6+
7+
/// Represents the platform TPM.
8+
pub struct PlatformTpm;
9+
10+
/// Represents an open TPM handle.
11+
pub struct TpmProtocolHandle {
12+
/// The version of the TPM protocol.
13+
version: Tcg2Version,
14+
/// The protocol itself.
15+
protocol: ScopedProtocol<Tcg>,
16+
}
17+
18+
impl TpmProtocolHandle {
19+
/// Construct a new [TpmProtocolHandle] from the `version` and `protocol`.
20+
pub fn new(version: Tcg2Version, protocol: ScopedProtocol<Tcg>) -> Self {
21+
Self { version, protocol }
22+
}
23+
24+
/// Access the version provided by the tcg2 protocol.
25+
pub fn version(&self) -> Tcg2Version {
26+
self.version
27+
}
28+
29+
/// Access the protocol interface for tcg2.
30+
pub fn protocol(&mut self) -> &mut ScopedProtocol<Tcg> {
31+
&mut self.protocol
32+
}
33+
}
34+
35+
impl PlatformTpm {
36+
/// Acquire access to the TPM protocol handle, if possible.
37+
/// Returns None if TPM is not available.
38+
fn protocol() -> Result<Option<TpmProtocolHandle>> {
39+
// Attempt to acquire the TCG2 protocol handle. If it's not available, return None.
40+
let Some(handle) =
41+
utils::find_handle(&Tcg2Protocol::GUID).context("unable to determine tpm presence")?
42+
else {
43+
return Ok(None);
44+
};
45+
46+
// If we reach here, we've already validated that the handle
47+
// implements the TCG2 protocol.
48+
let mut protocol = uefi::boot::open_protocol_exclusive::<Tcg>(handle)
49+
.context("unable to open tcg2 protocol")?;
50+
51+
// Acquire the capabilities of the TPM.
52+
let capability = protocol
53+
.get_capability()
54+
.context("unable to get tcg2 boot service capability")?;
55+
56+
// If the TPM is not present, return None.
57+
if !capability.tpm_present() {
58+
return Ok(None);
59+
}
60+
61+
// If the TPM is present, we need to determine the version of the TPM.
62+
let version = capability.protocol_version;
63+
64+
// We have a TPM, so return the protocol version and the protocol handle.
65+
Ok(Some(TpmProtocolHandle::new(version, protocol)))
66+
}
67+
68+
/// Determines whether the platform TPM is present.
69+
pub fn present() -> Result<bool> {
70+
Ok(PlatformTpm::protocol()?.is_some())
71+
}
72+
73+
/// Determine the number of active PCR banks on the TPM.
74+
/// If no TPM is available, this will return zero.
75+
pub fn active_pcr_banks() -> Result<u32> {
76+
// Acquire access to the TPM protocol handle.
77+
let Some(mut handle) = PlatformTpm::protocol()? else {
78+
return Ok(0);
79+
};
80+
81+
// Check if the TPM supports `GetActivePcrBanks`, and if it doesn't return zero.
82+
if handle.version().major < 1 || handle.version().major == 1 && handle.version().minor < 1 {
83+
return Ok(0);
84+
}
85+
86+
// The safe wrapper for this function will decode the bitmap.
87+
// Strictly speaking, it's not future-proof to re-encode that, but in practice it will work.
88+
let banks = handle
89+
.protocol()
90+
.get_active_pcr_banks()
91+
.context("unable to get active pcr banks")?;
92+
93+
// Return the number of active PCR banks.
94+
Ok(banks.bits())
95+
}
96+
}

0 commit comments

Comments
 (0)