feat(tpm): initial tpm support code, we just tell systemd about the pcr banks right now

This commit is contained in:
2025-10-31 01:30:07 -04:00
parent 6602e1d69e
commit 81cf331158
4 changed files with 118 additions and 0 deletions

View File

@@ -148,4 +148,16 @@ impl BootloaderInterface {
VariableClass::BootAndRuntimeTemporary, VariableClass::BootAndRuntimeTemporary,
) )
} }
/// Tell the system what the number of active PCR banks is.
/// If this is zero, that is okay.
pub fn set_tpm2_active_pcr_banks(value: u32) -> Result<()> {
// Format the value into the specification format.
let value = format!("0x{:08x}", value);
Self::VENDOR.set_cstr16(
"LoaderTpm2ActivePcrBanks",
&value,
VariableClass::BootAndRuntimeTemporary,
)
}
} }

View File

@@ -13,6 +13,7 @@ use crate::options::SproutOptions;
use crate::options::parser::OptionsRepresentable; use crate::options::parser::OptionsRepresentable;
use crate::phases::phase; use crate::phases::phase;
use crate::platform::timer::PlatformTimer; use crate::platform::timer::PlatformTimer;
use crate::platform::tpm::PlatformTpm;
use crate::secure::SecureBoot; use crate::secure::SecureBoot;
use crate::utils::PartitionGuidForm; use crate::utils::PartitionGuidForm;
use anyhow::{Context, Result, bail}; use anyhow::{Context, Result, bail};
@@ -92,6 +93,13 @@ fn run() -> Result<()> {
BootloaderInterface::set_loader_info() BootloaderInterface::set_loader_info()
.context("unable to set loader info in bootloader interface")?; .context("unable to set loader info in bootloader interface")?;
// Acquire the number of active PCR banks on the TPM.
// If no TPM is available, this will return zero.
let active_pcr_banks = PlatformTpm::active_pcr_banks()?;
// Tell the bootloader interface what the number of active PCR banks is.
BootloaderInterface::set_tpm2_active_pcr_banks(active_pcr_banks)
.context("unable to set tpm2 active PCR banks in bootloader interface")?;
// Parse the options to the sprout executable. // Parse the options to the sprout executable.
let options = SproutOptions::parse().context("unable to parse options")?; let options = SproutOptions::parse().context("unable to parse options")?;

View File

@@ -1,2 +1,4 @@
/// timer: Platform timer support. /// timer: Platform timer support.
pub mod timer; pub mod timer;
/// tpm: Platform TPM support.
pub mod tpm;

96
src/platform/tpm.rs Normal file
View File

@@ -0,0 +1,96 @@
use crate::utils;
use anyhow::{Context, Result};
use uefi::boot::ScopedProtocol;
use uefi::proto::tcg::v2::Tcg;
use uefi_raw::protocol::tcg::v2::{Tcg2Protocol, Tcg2Version};
/// Represents the platform TPM.
pub struct PlatformTpm;
/// Represents an open TPM handle.
pub struct TpmProtocolHandle {
/// The version of the TPM protocol.
version: Tcg2Version,
/// The protocol itself.
protocol: ScopedProtocol<Tcg>,
}
impl TpmProtocolHandle {
/// Construct a new [TpmProtocolHandle] from the `version` and `protocol`.
pub fn new(version: Tcg2Version, protocol: ScopedProtocol<Tcg>) -> Self {
Self { version, protocol }
}
/// Access the version provided by the tcg2 protocol.
pub fn version(&self) -> Tcg2Version {
self.version
}
/// Access the protocol interface for tcg2.
pub fn protocol(&mut self) -> &mut ScopedProtocol<Tcg> {
&mut self.protocol
}
}
impl PlatformTpm {
/// Acquire access to the TPM protocol handle, if possible.
/// Returns None if TPM is not available.
fn protocol() -> Result<Option<TpmProtocolHandle>> {
// Attempt to acquire the TCG2 protocol handle. If it's not available, return None.
let Some(handle) =
utils::find_handle(&Tcg2Protocol::GUID).context("unable to determine tpm presence")?
else {
return Ok(None);
};
// If we reach here, we've already validated that the handle
// implements the TCG2 protocol.
let mut protocol = uefi::boot::open_protocol_exclusive::<Tcg>(handle)
.context("unable to open tcg2 protocol")?;
// Acquire the capabilities of the TPM.
let capability = protocol
.get_capability()
.context("unable to get tcg2 boot service capability")?;
// If the TPM is not present, return None.
if !capability.tpm_present() {
return Ok(None);
}
// If the TPM is present, we need to determine the version of the TPM.
let version = capability.protocol_version;
// We have a TPM, so return the protocol version and the protocol handle.
Ok(Some(TpmProtocolHandle::new(version, protocol)))
}
/// Determines whether the platform TPM is present.
pub fn present() -> Result<bool> {
Ok(PlatformTpm::protocol()?.is_some())
}
/// Determine the number of active PCR banks on the TPM.
/// If no TPM is available, this will return zero.
pub fn active_pcr_banks() -> Result<u32> {
// Acquire access to the TPM protocol handle.
let Some(mut handle) = PlatformTpm::protocol()? else {
return Ok(0);
};
// Check if the TPM supports `GetActivePcrBanks`, and if it doesn't return zero.
if handle.version().major < 1 || handle.version().major == 1 && handle.version().minor < 1 {
return Ok(0);
}
// The safe wrapper for this function will decode the bitmap.
// Strictly speaking, it's not future-proof to re-encode that, but in practice it will work.
let banks = handle
.protocol()
.get_active_pcr_banks()
.context("unable to get active pcr banks")?;
// Return the number of active PCR banks.
Ok(banks.bits())
}
}