blob: ffc7b94f3d570cb854c43b058624d13addd974a0 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// The file implements the TPM Command Transmission Interface (TCTI) layer in the EFI environment
// for using TCG Software Stack
#include "tss2_uefi.h"
#include <stdio.h>
#include "tss2/tss2_rc.h"
#include "utils.h"
namespace gigaboot {
namespace {
// Up-cast a TSS2_TCTI_CONTEXT into our implementation specific `Tss2UefiTctiContext`
Tss2UefiTctiContext *CheckAndGetTctiUefiContext(TSS2_TCTI_CONTEXT *context) {
// Make sure magic and version matches before casting.
if (context && TSS2_TCTI_MAGIC(context) == kTctiUefiMagic &&
TSS2_TCTI_VERSION(context) == kTctiUefiVersion) {
return reinterpret_cast<Tss2UefiTctiContext *>(context);
}
return nullptr;
}
TSS2_RC TctiUefiTransmit(TSS2_TCTI_CONTEXT *context, size_t size, const uint8_t *command) {
// UEFI TPM 2 interface is synchronous/blocking. Thus, for transmit, we will just copy the command
// to an internal buffer. Actual transmission and execution of the command will be done in the
// receive callback.
Tss2UefiTctiContext *uefi_context = CheckAndGetTctiUefiContext(context);
if (!uefi_context) {
return TSS2_TCTI_RC_BAD_CONTEXT;
}
if (size > uefi_context->command_buffer.size()) {
return TSS2_TCTI_RC_BAD_VALUE;
}
memcpy(uefi_context->command_buffer.data(), command, size);
uefi_context->current_command_size = size;
return TSS2_RC_SUCCESS;
}
TSS2_RC TcTiUefiReceive(TSS2_TCTI_CONTEXT *context, size_t *size, uint8_t *response,
int32_t timeout) {
Tss2UefiTctiContext *uefi_context = CheckAndGetTctiUefiContext(context);
if (!uefi_context) {
return TSS2_TCTI_RC_BAD_CONTEXT;
}
if (uefi_context->current_command_size == 0) {
// No command. Transmit has not been called yet.
return TSS2_TCTI_RC_BAD_SEQUENCE;
}
// UEFI TPM2 protocol is blocking. Timeout is not supported.
if (timeout != TSS2_TCTI_TIMEOUT_BLOCK) {
return TSS2_TCTI_RC_NOT_IMPLEMENTED;
}
// When response buffer is nullptr, callback shall return the expected response size.
// If multi-chunk read is not supported, the maximum possible size is returned.
if (response == nullptr) {
// Another approach is to use the maximum response size obtained from GetCapability() TCG EFI
// service, which is typically smaller.
*size = TPM2_MAX_RESPONSE_SIZE;
return TSS2_RC_SUCCESS;
}
auto tpm2_protocol = gigaboot::EfiLocateProtocol<efi_tcg2_protocol>();
if (tpm2_protocol.is_error()) {
return TSS2_TCTI_RC_GENERAL_FAILURE;
}
ZX_ASSERT(uefi_context->current_command_size <= TPM2_MAX_COMMAND_SIZE);
efi_status status = tpm2_protocol->SubmitCommand(
tpm2_protocol.value().get(), static_cast<uint32_t>(uefi_context->current_command_size),
uefi_context->command_buffer.data(), static_cast<uint32_t>(*size), response);
if (status != EFI_SUCCESS) {
printf("Failed to submit command: %s\n", EfiStatusToString(status));
return TSS2_TCTI_RC_GENERAL_FAILURE;
}
uefi_context->current_command_size = 0;
return TSS2_RC_SUCCESS;
}
void TcTiUefiFinalize(TSS2_TCTI_CONTEXT *context) {
// Nothing need to be done.
}
} // namespace
std::unique_ptr<Tss2UefiTctiContext> CreateTss2UefiTctiContext() {
auto context = std::make_unique<Tss2UefiTctiContext>();
TSS2_TCTI_MAGIC(context.get()) = kTctiUefiMagic;
TSS2_TCTI_VERSION(context.get()) = kTctiUefiVersion;
// Another approach is to use the maximum command size obtained from GetCapability() TCG EFI
// service, which is typically smaller.
context->command_buffer.resize(TPM2_MAX_COMMAND_SIZE);
TSS2_TCTI_TRANSMIT(context.get()) = TctiUefiTransmit;
TSS2_TCTI_RECEIVE(context.get()) = TcTiUefiReceive;
TSS2_TCTI_FINALIZE(context.get()) = TcTiUefiFinalize;
// The following are optional callbacks and not applicable to UEFI environment.
TSS2_TCTI_CANCEL(context.get()) = nullptr;
TSS2_TCTI_GET_POLL_HANDLES(context.get()) = nullptr;
TSS2_TCTI_SET_LOCALITY(context.get()) = nullptr;
TSS2_TCTI_MAKE_STICKY(context.get()) = nullptr;
return context;
}
std::unique_ptr<Tss2UefiSysContext> Tss2UefiSysContext::Create() {
auto context = std::make_unique<Tss2UefiSysContext>();
// Passing 0 to `Tss2_Sys_GetContextSize` will compute the sys context size assuming
// TPM2_MAX_COMMAND_SIZE
size_t size = Tss2_Sys_GetContextSize(0);
context->sys_context_.resize(size);
// Use the matching version from the current TSS library version.
TSS2_ABI_VERSION abi_version = TSS2_ABI_VERSION_CURRENT;
context->tcti_context_ = CreateTss2UefiTctiContext();
if (!context->tcti_context_) {
return nullptr;
}
// Initialize the TSS sys context given the TCTI context.
TSS2_RC ret = Tss2_Sys_Initialize(
context->sys_context(), size,
reinterpret_cast<TSS2_TCTI_CONTEXT *>(context->tcti_context_.get()), &abi_version);
if (ret != TSS2_RC_SUCCESS) {
const char *err_msg = Tss2_RC_Decode(ret);
printf("Failed to initialize SYS context: 0x%x, %s\n", ret, err_msg);
return nullptr;
}
return context;
}
} // namespace gigaboot