Add UEFI TPM2 driver

Add support for driving a TPM via UEFI firmware provided drivers, and
bind those devices from the UEFI app.

Signed-off-by: Matthew Garrett <mgarrett@aurora.tech>
Reviewed-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
Matthew Garrett
2024-11-23 11:55:05 -08:00
committed by Simon Glass
parent 4bb984a205
commit f07b9497ba
6 changed files with 186 additions and 0 deletions

View File

@@ -209,6 +209,13 @@ config TPM2_MMIO
to the device using the standard TPM Interface Specification (TIS)
protocol.
config TPM2_EFI
bool "UEFI firmware based TPM2 Interface"
depends on TPM_V2 && EFI_APP
help
This driver supports the use of UEFI firmware-provided drivers for
interfacing with a TPM 2.
endif # TPM_V2
endmenu

View File

@@ -16,3 +16,4 @@ obj-$(CONFIG_TPM2_TIS_SPI) += tpm2_tis_core.o tpm2_tis_spi.o
obj-$(CONFIG_TPM2_TIS_I2C) += tpm2_tis_core.o tpm2_tis_i2c.o
obj-$(CONFIG_TPM2_FTPM_TEE) += tpm2_ftpm_tee.o
obj-$(CONFIG_TPM2_MMIO) += tpm2_tis_core.o tpm2_tis_mmio.o
obj-$(CONFIG_TPM2_EFI) += tpm2_efi.o

97
drivers/tpm/tpm2_efi.c Normal file
View File

@@ -0,0 +1,97 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Aurora Innovation, Inc. Copyright 2022.
*
*/
#include <config.h>
#include <dm.h>
#include <efi.h>
#include <efi_api.h>
#include <efi_tcg2.h>
#include <malloc.h>
#include <tpm-v2.h>
static int efi_tpm_bind(struct udevice *dev)
{
struct tpm_chip_priv *priv = dev_get_uclass_priv(dev);
struct efi_tpm_plat *plat = dev_get_plat(dev);
struct efi_tcg2_boot_service_capability caps;
efi_status_t status;
caps.size = sizeof(caps);
status = plat->proto->get_capability(plat->proto, &caps);
if (status != EFI_SUCCESS)
return -EINVAL;
if (!caps.tpm_present_flag)
return -ENODEV;
priv->pcr_count = 24;
priv->pcr_select_min = 3;
priv->version = TPM_V2;
return 0;
}
static int efi_tpm_open(struct udevice *dev)
{
return 0;
}
static int efi_tpm_close(struct udevice *dev)
{
return 0;
}
static int efi_tpm_xfer(struct udevice *dev, const u8 *sendbuf,
size_t send_size, u8 *recvbuf,
size_t *recv_len)
{
struct efi_tpm_plat *plat = dev_get_plat(dev);
efi_status_t status;
status = plat->proto->submit_command(plat->proto, send_size,
(u8 *)sendbuf, *recv_len,
recvbuf);
switch (status) {
case EFI_BUFFER_TOO_SMALL:
debug("%s:response length is bigger than receive buffer\n",
__func__);
return -EIO;
case EFI_DEVICE_ERROR:
debug("%s:received error from device on write\n", __func__);
return -EIO;
case EFI_INVALID_PARAMETER:
debug("%s:invalid parameter\n", __func__);
return -EINVAL;
case EFI_SUCCESS:
return 0;
default:
debug("%s:received unknown error 0x%lx\n", __func__, status);
return -EIO;
}
}
static int efi_tpm_desc(struct udevice *dev, char *buf, int size)
{
if (size < 9)
return -ENOSPC;
return snprintf(buf, size, "UEFI TPM");
}
static const struct tpm_ops efi_tpm_ops = {
.open = efi_tpm_open,
.close = efi_tpm_close,
.get_desc = efi_tpm_desc,
.xfer = efi_tpm_xfer,
};
U_BOOT_DRIVER(efi_tpm) = {
.name = "efi_tpm",
.id = UCLASS_TPM,
.bind = efi_tpm_bind,
.ops = &efi_tpm_ops,
.plat_auto = sizeof(struct efi_tpm_plat),
};

View File

@@ -510,6 +510,17 @@ struct efi_net_plat {
};
#endif
/*
* EFI attributes of the udevice handled by efi_tpm driver
*
* @handle: handle of the controller on which this driver is installed
* @proto: pointer to the TCG2 EFI protocol
*/
struct efi_tpm_plat {
efi_handle_t handle;
struct efi_tcg2_protocol *proto;
};
/* Base address of the EFI image */
extern char image_base[];

View File

@@ -17,6 +17,7 @@
#define _EFI_TCG2_PROTOCOL_H_
#include <efi_api.h>
#include <part_efi.h>
#include <tpm-v2.h>
#include <tpm_tcg2.h>

View File

@@ -9,6 +9,7 @@
#include <dm.h>
#include <efi.h>
#include <efi_api.h>
#include <efi_tcg2.h>
#include <errno.h>
#include <malloc.h>
#include <asm/global_data.h>
@@ -247,6 +248,71 @@ static int setup_net(void)
return 0;
}
/**
* setup_tpm() - Find all TPMs and setup EFI devices for them
*
* Return: 0 if found, -ENOSYS if there is no boot-services table, -ENOTSUPP
* if a required protocol is not supported
*/
static int setup_tpm(void)
{
efi_guid_t efi_tcg2_guid = EFI_TCG2_PROTOCOL_GUID;
struct efi_boot_services *boot = efi_get_boot();
efi_uintn_t num_handles;
efi_handle_t *handle;
int ret, i;
if (!boot)
return log_msg_ret("sys", -ENOSYS);
/* Find all devices which support the TCG2 protocol */
ret = boot->locate_handle_buffer(BY_PROTOCOL, &efi_tcg2_guid, NULL,
&num_handles, &handle);
if (ret)
return 0;
log_debug("Found %d TPM handles:\n", (int)num_handles);
for (i = 0; i < num_handles; i++) {
struct efi_tcg2_protocol *proto;
struct efi_tpm_plat *plat;
struct udevice *dev;
char name[18];
ret = boot->handle_protocol(handle[i], &efi_tcg2_guid,
(void **)&proto);
if (ret != EFI_SUCCESS) {
log_warning("- TPM %d failed (ret=0x%x)\n", i, ret);
continue;
}
plat = malloc(sizeof(*plat));
if (!plat) {
log_warning("- TPM %d failed to alloc platform data", i);
continue;
}
plat->handle = handle[i];
plat->proto = proto;
ret = device_bind(dm_root(), DM_DRIVER_GET(efi_net), "efi_tpm",
plat, ofnode_null(), &dev);
if (ret) {
log_warning("- bind TPM %d failed (ret=0x%x)\n", i,
ret);
continue;
}
snprintf(name, sizeof(name), "efi_tpm_%x", dev_seq(dev));
device_set_name(dev, name);
printf("%2d: %-12s\n", i, dev->name);
}
boot->free_pool(handle);
return 0;
}
/**
* board_early_init_r() - Scan for UEFI devices that should be available
*
@@ -266,6 +332,9 @@ int board_early_init_r(void)
ret = setup_net();
if (ret)
return ret;
ret = setup_tpm();
if (ret)
return ret;
}
return 0;