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, } impl TpmProtocolHandle { /// Construct a new [TpmProtocolHandle] from the `version` and `protocol`. pub fn new(version: Tcg2Version, protocol: ScopedProtocol) -> 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 { &mut self.protocol } } impl PlatformTpm { /// Acquire access to the TPM protocol handle, if possible. /// Returns None if TPM is not available. fn protocol() -> Result> { // 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::(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 { 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 { // 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()) } }