blob: 1282b6ade7ea4b5fb75ff1c4f42b32b31bb18acc [file] [log] [blame]
// Copyright 2024 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use crate::operation::Operation;
use core::sync::atomic::{AtomicU32, Ordering};
use uefi::data_types::PhysicalAddress;
use uefi::proto::tcg::v1;
use uefi::table::boot::BootServices;
use uefi::{Identify, Status};
/// Install a TPM v1 protocol. This isn't a real TPM, just enough to
/// test some error cases (e.g. the behavior of crdyshim if the TPM is
/// deactivated).
pub fn create_tpm1(boot_services: &BootServices) {
static TPM_V1: TpmV1 = TpmV1 {
status_check,
hash_log_extend_event,
// These aren't called, so don't bother implementing.
hash_all: not_implemented,
log_event: not_implemented,
pass_through_to_tpm: not_implemented,
};
let interface: *const _ = &TPM_V1;
// SAFETY: The layout of `TPM_V1` matches the spec, and the GUID is
// correct.
unsafe { boot_services.install_protocol_interface(None, &v1::Tcg::GUID, interface.cast()) }
.unwrap();
}
/// Get TPM status. Depending on the operation, the TPM may be deactivated.
extern "efiapi" fn status_check(
_this: *mut TpmV1,
protocol_capability: *mut TpmV1Capability,
_feature_flags: *mut u32,
_event_log_location: *mut PhysicalAddress,
_event_log_last_entry: *mut PhysicalAddress,
) -> Status {
let caps = TpmV1Capability {
tpm_present_flag: 1,
tpm_deactivated_flag: if Operation::get() == Operation::Tpm1Deactivated {
1
} else {
0
},
..TpmV1Capability::default()
};
unsafe {
*protocol_capability = caps;
}
Status::SUCCESS
}
/// Stub implementation for extending a PCR. Just returns a status code
/// that depends on the operation.
unsafe extern "efiapi" fn hash_log_extend_event(
_this: *mut TpmV1,
_hash_data: PhysicalAddress,
_hash_data_len: u64,
_algorithm_id: u32,
_event: *mut v1::FfiPcrEvent,
_event_number: *mut u32,
_event_log_last_entry: *mut PhysicalAddress,
) -> Status {
static CALL_COUNT: AtomicU32 = AtomicU32::new(0);
// Get the current call count, and increment the static.
let call_count = CALL_COUNT.fetch_add(1, Ordering::Acquire);
// The initial calls to this function happen implicitly in the
// firmware when loading crdyshim. Always return success for those,
// otherwise the load will fail.
//
// There's no guarantee that the behavior of OVMF won't change in
// the future, but if it does, the test will fail rather than
// silently passing. If `call_count` is too low, crdyshim won't be
// loaded. If too high, the test won't see the expected
// `DEVICE_ERROR` log.
if call_count <= 2 {
return Status::SUCCESS;
}
if Operation::get() == Operation::Tpm1ExtendFail {
Status::DEVICE_ERROR
} else {
Status::SUCCESS
}
}
extern "efiapi" fn not_implemented() -> Status {
unimplemented!()
}
#[derive(Default)]
#[repr(C)]
pub struct TpmV1Capability {
size: u8,
structure_version: [u8; 4],
protocol_spec_version: [u8; 4],
hash_algorithm_bitmap: u8,
tpm_present_flag: u8,
tpm_deactivated_flag: u8,
}
#[repr(C)]
pub struct TpmV1 {
status_check: unsafe extern "efiapi" fn(
this: *mut Self,
protocol_capability: *mut TpmV1Capability,
feature_flags: *mut u32,
event_log_location: *mut PhysicalAddress,
event_log_last_entry: *mut PhysicalAddress,
) -> Status,
hash_all: unsafe extern "efiapi" fn() -> Status,
log_event: unsafe extern "efiapi" fn() -> Status,
pass_through_to_tpm: unsafe extern "efiapi" fn() -> Status,
hash_log_extend_event: unsafe extern "efiapi" fn(
this: *mut Self,
hash_data: PhysicalAddress,
hash_data_len: u64,
algorithm_id: u32,
event: *mut v1::FfiPcrEvent,
event_number: *mut u32,
event_log_last_entry: *mut PhysicalAddress,
) -> Status,
}