[PATCH 1/2] tpm: Make response length of tpm2_get_capability() configurable

A following patch introduces EFI_TCG2_PROTOCOL. One of the functions of that protocol is GetCapability(). In order to parse device capabilities we need to access a u32 before the properties which the current implementation ignores while reading device properties.
So let's make the response length configurable and prepare the functions for EFI_TCG2_PROTOCOL.
Signed-off-by: Ilias Apalodimas ilias.apalodimas@linaro.org --- cmd/tpm-v2.c | 2 +- include/tpm-v2.h | 12 +++++++----- lib/tpm-v2.c | 10 +++++++--- 3 files changed, 15 insertions(+), 9 deletions(-)
diff --git a/cmd/tpm-v2.c b/cmd/tpm-v2.c index e6742656f578..c2df1c34043a 100644 --- a/cmd/tpm-v2.c +++ b/cmd/tpm-v2.c @@ -183,7 +183,7 @@ static int do_tpm_get_capability(struct cmd_tbl *cmdtp, int flag, int argc, data = map_sysmem(simple_strtoul(argv[3], NULL, 0), 0); count = simple_strtoul(argv[4], NULL, 0);
- rc = tpm2_get_capability(dev, capability, property, data, count); + rc = tpm2_get_capability(dev, capability, property, data, count, false); if (rc) goto unmap_data;
diff --git a/include/tpm-v2.h b/include/tpm-v2.h index f6c045d35480..ee74028ca83b 100644 --- a/include/tpm-v2.h +++ b/include/tpm-v2.h @@ -257,15 +257,17 @@ u32 tpm2_pcr_read(struct udevice *dev, u32 idx, unsigned int idx_min_sz, * to query property index that is 4-byte wide. * * @dev TPM device - * @capability Partition of capabilities - * @property Further definition of capability, limited to be 4 bytes wide - * @buf Output buffer for capability information - * @prop_count Size of output buffer + * @capability Partition of capabilities + * @property Further definition of capability, limited to be 4 bytes + * wide + * @buf Output buffer for capability information + * @prop_count Size of output buffer + * @get_count Include tpmu property count * * @return code of the operation */ u32 tpm2_get_capability(struct udevice *dev, u32 capability, u32 property, - void *buf, size_t prop_count); + void *buf, size_t prop_count, bool get_count);
/** * Issue a TPM2_DictionaryAttackLockReset command. diff --git a/lib/tpm-v2.c b/lib/tpm-v2.c index a4c352e3ef75..b58c1057995b 100644 --- a/lib/tpm-v2.c +++ b/lib/tpm-v2.c @@ -161,7 +161,7 @@ u32 tpm2_pcr_read(struct udevice *dev, u32 idx, unsigned int idx_min_sz, }
u32 tpm2_get_capability(struct udevice *dev, u32 capability, u32 property, - void *buf, size_t prop_count) + void *buf, size_t prop_count, bool get_count) { u8 command_v2[COMMAND_BUFFER_SIZE] = { tpm_u16(TPM2_ST_NO_SESSIONS), /* TAG */ @@ -181,13 +181,17 @@ u32 tpm2_get_capability(struct udevice *dev, u32 capability, u32 property, if (ret) return ret;
+ /* When reading PCR properties we need the count */ + properties_off = sizeof(u16) + sizeof(u32) + sizeof(u32) + + sizeof(u8) + sizeof(u32); /* * In the response buffer, the properties are located after the: * tag (u16), response size (u32), response code (u32), * YES/NO flag (u8), TPM_CAP (u32) and TPMU_CAPABILITIES (u32). */ - properties_off = sizeof(u16) + sizeof(u32) + sizeof(u32) + - sizeof(u8) + sizeof(u32) + sizeof(u32); + if (!get_count) + properties_off += sizeof(u32); + memcpy(buf, &response[properties_off], response_len - properties_off);
return 0;

Since U-boot EFI implementation is getting richer it makes sense to add support for EFI_TCG2_PROTOCOL taking advantage of any hardware TPM available on the device.
This is the initial implementation of the protocol which only adds support for GetCapability(). It's limited in the newer and safer TPMv2 devices.
Signed-off-by: Ilias Apalodimas ilias.apalodimas@linaro.org --- The protocol requires mode that GetCapability to be usable. I intend to add support for GetEventLog() and HashLogExtendEvent() once this gets reviewed/merged include/efi_loader.h | 2 + include/efi_tcg2.h | 91 ++++++++ include/tpm-v2.h | 48 ++++ lib/efi_loader/Kconfig | 8 + lib/efi_loader/Makefile | 1 + lib/efi_loader/efi_setup.c | 7 + lib/efi_loader/efi_tcg2.c | 460 +++++++++++++++++++++++++++++++++++++ 7 files changed, 617 insertions(+) create mode 100644 include/efi_tcg2.h create mode 100644 lib/efi_loader/efi_tcg2.c
diff --git a/include/efi_loader.h b/include/efi_loader.h index f550ced56876..e5015d865ec9 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -405,6 +405,8 @@ efi_status_t efi_console_register(void); efi_status_t efi_disk_register(void); /* Called by efi_init_obj_list() to install EFI_RNG_PROTOCOL */ efi_status_t efi_rng_register(void); +/* Called by efi_init_obj_list() to install EFI_TCG2_PROTOCOL */ +efi_status_t efi_tcg2_register(void); /* Create handles and protocols for the partitions of a block device */ int efi_disk_create_partitions(efi_handle_t parent, struct blk_desc *desc, const char *if_typename, int diskid, diff --git a/include/efi_tcg2.h b/include/efi_tcg2.h new file mode 100644 index 000000000000..9e7b85db058d --- /dev/null +++ b/include/efi_tcg2.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2020, Linaro Limited + */ + +#if !defined _EFI_TCG2_PROTOCOL_H_ +#define _EFI_TCG2_PROTOCOL_H_ + +#include <tpm-v2.h> + +#define EFI_TCG2_PROTOCOL_GUID \ + EFI_GUID(0x607f766c, 0x7455, 0x42be, 0x93, \ + 0x0b, 0xe4, 0xd7, 0x6d, 0xb2, 0x72, 0x0f) + +/* TPMV2 only */ +#define TCG2_EVENT_LOG_FORMAT_TCG_2 0x00000002 + +/* SHA1, SHA256, SHA384, SHA512, TPM_ALG_SM3_256 */ +#define MAX_HASH_COUNT 5 +/* Algorithm Registry */ +#define EFI_TCG2_BOOT_HASH_ALG_SHA1 0x00000001 +#define EFI_TCG2_BOOT_HASH_ALG_SHA256 0x00000002 +#define EFI_TCG2_BOOT_HASH_ALG_SHA384 0x00000004 +#define EFI_TCG2_BOOT_HASH_ALG_SHA512 0x00000008 +#define EFI_TCG2_BOOT_HASH_ALG_SM3_256 0x00000010 + +typedef u32 efi_tcg_event_log_bitmap; +typedef u32 efi_tcg_event_log_format; +typedef u32 efi_tcg_event_algorithm_bitmap; + +struct efi_tcg2_version { + u8 major; + u8 minor; +}; + +struct efi_tcg2_event_header { + u32 header_size; + u16 header_version; + u32 pcr_index; + u32 event_type; +} __packed; + +struct efi_tcg2_event { + u32 size; + struct efi_tcg2_event_header header; + u8 event[]; +} __packed; + +struct efi_tcg2_boot_service_capability { + u8 size; + struct efi_tcg2_version structure_version; + struct efi_tcg2_version protocol_version; + efi_tcg_event_algorithm_bitmap hash_algorithm_bitmap; + efi_tcg_event_log_bitmap supported_event_logs; + bool tpm_present_flag; + u16 max_command_size; + u16 max_response_size; + u32 manufacturer_id; + u32 number_of_pcr_banks; + efi_tcg_event_algorithm_bitmap active_pcr_banks; +}; + +#define boot_service_capability_min \ + sizeof(struct efi_tcg2_boot_service_capability) - \ + offsetof(struct efi_tcg2_boot_service_capability, number_of_pcr_banks) + +struct efi_tcg2_protocol { + efi_status_t (EFIAPI * get_capability)(struct efi_tcg2_protocol *this, + struct efi_tcg2_boot_service_capability *capability); + efi_status_t (EFIAPI * get_eventlog)(struct efi_tcg2_protocol *this, + efi_tcg_event_log_format log_format, + u64 *event_log_location, u64 *event_log_last_entry, + bool *event_log_truncated); + efi_status_t (EFIAPI * hash_log_extend_event)(struct efi_tcg2_protocol *this, + u64 flags, u64 data_to_hash, + u64 data_to_hash_len, + struct efi_tcg2_event *efi_tcg_event); + efi_status_t (EFIAPI * submit_command)(struct efi_tcg2_protocol *this, + u32 input_parameter_block_size, + u8 *input_parameter_block, + u32 output_parameter_block_size, + u8 *output_parameter_block); + efi_status_t (EFIAPI * get_active_pcr_banks)(struct efi_tcg2_protocol *this, + u32 *active_pcr_banks); + efi_status_t (EFIAPI * set_active_pcr_banks)(struct efi_tcg2_protocol *this, + u32 active_pcr_banks); + efi_status_t (EFIAPI * get_result_of_set_active_pcr_banks)(struct efi_tcg2_protocol *this, + u32 *operation_present, + u32 *response); +}; +#endif diff --git a/include/tpm-v2.h b/include/tpm-v2.h index ee74028ca83b..505b8c96eb92 100644 --- a/include/tpm-v2.h +++ b/include/tpm-v2.h @@ -11,6 +11,52 @@
#define TPM2_DIGEST_LEN 32
+#define IMPLEMENTATION_PCR 24 + +/* + * We deviate from this draft of the specification by increasing the value of TPM2_NUM_PCR_BANKS + * from 3 to 16 to ensure compatibility with TPM2 implementations that have enabled a larger than + * typical number of PCR banks. This larger value for TPM2_NUM_PCR_BANKS is expected to be included + * in a future revision of the specification. + */ +#define TPM2_NUM_PCR_BANKS 16 + +/* Table 22 — Definition of (UINT32) TPM_CAP Constants */ +#define TPM_CAP_FIRST 0x00000000U +#define TPM_CAP_ALGS 0x00000000U +#define TPM_CAP_HANDLES 0x00000001U +#define TPM_CAP_COMMANDS 0x00000002U +#define TPM_CAP_PP_COMMANDS 0x00000003U +#define TPM_CAP_AUDIT_COMMANDS 0x00000004U +#define TPM_CAP_PCRS 0x00000005U +#define TPM_CAP_TPM_PROPERTIES 0x00000006U +#define TPM_CAP_PCR_PROPERTIES 0x00000007U +#define TPM_CAP_ECC_CURVES 0x00000008U +#define TPM_CAP_LAST 0x00000008U +#define TPM_CAP_VENDOR_PROPERTY 0x00000100U + +/* Table 23 — Definition of (UINT32) TPM_PT Constants */ +#define TPM_PT_NONE (u32)(0x00000000) +#define PT_GROUP (u32)(0x00000100) +#define PT_FIXED (u32)(PT_GROUP * 1) +#define TPM_PT_MANUFACTURER (u32)(PT_FIXED + 5) +#define TPM_PT_PCR_COUNT (u32)(PT_FIXED + 18) +#define TPM_PT_MAX_COMMAND_SIZE (u32)(PT_FIXED + 30) +#define TPM_PT_MAX_RESPONSE_SIZE (u32)(PT_FIXED + 31) + +/* Definition of TPMS_PCR_SELECTION Structure */ +struct tpms_pcr_selection { + u16 hash; + u8 size_of_select; + u8 pcr_select[(IMPLEMENTATION_PCR + 7) / 8]; +} __packed; + +/* Definition of TPML_PCR_SELECTION Structure */ +struct tpml_pcr_selection { + u32 count; + struct tpms_pcr_selection selection[TPM2_NUM_PCR_BANKS]; +} __packed; + /** * TPM2 Structure Tags for command/response buffers. * @@ -123,11 +169,13 @@ enum tpm2_return_codes { * TPM2 algorithms. */ enum tpm2_algorithms { + TPM2_ALG_SHA1 = 0x04, TPM2_ALG_XOR = 0x0A, TPM2_ALG_SHA256 = 0x0B, TPM2_ALG_SHA384 = 0x0C, TPM2_ALG_SHA512 = 0x0D, TPM2_ALG_NULL = 0x10, + TPM2_ALG_SM3_256 = 0x12, };
/* NV index attributes */ diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index 075481428cdf..5d5074a4bc41 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -184,6 +184,14 @@ config EFI_RNG_PROTOCOL Provide a EFI_RNG_PROTOCOL implementation using the hardware random number generator of the platform.
+config EFI_TCG2_PROTOCOL + bool "EFI_TCG2_PROTOCOL support" + depends on TPM_V2 + default n + help + Provide a EFI_TCG2_PROTOCOL implementation using the TPM hardware + of the platform. + config EFI_LOAD_FILE2_INITRD bool "EFI_FILE_LOAD2_PROTOCOL for Linux initial ramdisk" default n diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index 8892fb01e125..cd4b252a417c 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -53,6 +53,7 @@ obj-$(CONFIG_NET) += efi_net.o obj-$(CONFIG_GENERATE_ACPI_TABLE) += efi_acpi.o obj-$(CONFIG_GENERATE_SMBIOS_TABLE) += efi_smbios.o obj-$(CONFIG_EFI_RNG_PROTOCOL) += efi_rng.o +obj-$(CONFIG_EFI_TCG2_PROTOCOL) += efi_tcg2.o obj-$(CONFIG_EFI_LOAD_FILE2_INITRD) += efi_load_initrd.o obj-y += efi_signature.o
diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c index 45226c5c1a53..e206b60bb82c 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -156,6 +156,13 @@ efi_status_t efi_init_obj_list(void) if (ret != EFI_SUCCESS) goto out; } + + if (IS_ENABLED(CONFIG_EFI_TCG2_PROTOCOL)) { + ret = efi_tcg2_register(); + if (ret != EFI_SUCCESS) + goto out; + } + /* Initialize variable services */ ret = efi_init_variables(); if (ret != EFI_SUCCESS) diff --git a/lib/efi_loader/efi_tcg2.c b/lib/efi_loader/efi_tcg2.c new file mode 100644 index 000000000000..1e12f95b2e41 --- /dev/null +++ b/lib/efi_loader/efi_tcg2.c @@ -0,0 +1,460 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2020, Linaro Limited + */ + +#define LOG_CATEGORY LOGC_EFI +#include <common.h> +#include <dm.h> +#include <efi_loader.h> +#include <efi_tcg2.h> +#include <log.h> +#include <tpm-v2.h> +#include <linux/unaligned/access_ok.h> +#include <linux/unaligned/generic.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define COMMAND_BUFFER_SIZE 256 + +const efi_guid_t efi_guid_tcg2_protocol = EFI_TCG2_PROTOCOL_GUID; + +/** + * platform_get_tpm_device() - retrieve TPM device + * + * This function retrieves the udevice implementing a TPM + * + * This function may be overridden if special initialization is needed. + * + * @dev: udevice + * Return: status code + */ +__weak efi_status_t platform_get_tpm2_device(struct udevice **dev) +{ + int ret; + struct udevice *devp; + + ret = uclass_get_device(UCLASS_TPM, 0, &devp); + if (ret) { + log_warning("Unable to get tpm device\n"); + return EFI_DEVICE_ERROR; + } + + *dev = devp; + + return EFI_SUCCESS; +} + +/** + * tpm2_get_max_command_size() - TPM2 command to get the supported max command size + * + * @dev: TPM device + * @max_command_size: output buffer for the size + * + * Return: 0 on success -1 on error + */ +static int tpm2_get_max_command_size(struct udevice *dev, u16 *max_command_size) +{ + u8 response[COMMAND_BUFFER_SIZE]; + u32 ret; + + ret = tpm2_get_capability(dev, TPM_CAP_TPM_PROPERTIES, + TPM_PT_MAX_COMMAND_SIZE, response, 1, false); + if (ret) + return -1; + /* + * On the definition of TPMS_TAGGED_PROPERTY Structure + * property: u32 + * value: u32 + * So advance the response for TPM_CAP_TPM_PROPERTIES by sizeof(u32) + * to get the value + */ + *max_command_size = (uint16_t)get_unaligned_be32(response + sizeof(u32)); + + return 0; +} + +/** + * tpm2_get_max_response_size() - TPM2 command to get the supported max response size + * + * @dev: TPM device + * @max_response_size: output buffer for the size + * + * Return: 0 on success -1 on error + */ +static int tpm2_get_max_response_size(struct udevice *dev, u16 *max_response_size) +{ + u8 response[COMMAND_BUFFER_SIZE]; + u32 ret; + + ret = tpm2_get_capability(dev, TPM_CAP_TPM_PROPERTIES, + TPM_PT_MAX_RESPONSE_SIZE, response, 1, false); + if (ret) + return -1; + /* + * On the definition of TPMS_TAGGED_PROPERTY Structure + * property: u32 + * value: u32 + * So advance the response for TPM_CAP_TPM_PROPERTIES by sizeof(u32) + * to get the value + */ + *max_response_size = (uint16_t)get_unaligned_be32(response + sizeof(u32)); + + return 0; +} + +/** + * tpm2_get_manufacturer_id() - Issue a TPM2 command to get the manufacturer ID + * + * @dev: TPM device + * @manufacturer_id: output buffer for the id + * + * Return: 0 on success -1 on error + */ +static int tpm2_get_manufacturer_id(struct udevice *dev, u32 *manufacturer_id) +{ + u8 response[COMMAND_BUFFER_SIZE]; + u32 ret; + + ret = tpm2_get_capability(dev, TPM_CAP_TPM_PROPERTIES, + TPM_PT_MANUFACTURER, response, 1, false); + if (ret) + return -1; + *manufacturer_id = get_unaligned_be32(response + sizeof(u32)); + + return 0; +} + +/** + * is_active_pcr() - Check if an supported algorithm is active + * + * @dev: TPM device + * @selection: struct of PCR information + * + * Return: true if PCR is active + */ +bool is_active_pcr(struct tpms_pcr_selection *selection) +{ + int i; + /* + * check the pcr_select. If at least one of the PCRs supports the algorithm + * add it on the active ones + */ + for (i = 0; i < selection->size_of_select; i++) { + if (selection->pcr_select[i]) + return true; + } + + return false; +} + +/** + * tpm2_get_pcr_info() - Issue a TPM2 command to get the supported, active PCRs and number of banks + * + * @dev: TPM device + * @supported_pcr: bitmask with the algorithms supported + * @active_pcr: bitmask with the active algorithms + * @pcr_banks: number of PCR banks + * + * Return: 0 on success -1 on error + */ +static int tpm2_get_pcr_info(struct udevice *dev, u32 *supported_pcr, u32 *active_pcr, + u32 *pcr_banks) +{ + u8 response[COMMAND_BUFFER_SIZE]; + struct tpml_pcr_selection pcrs; + u32 ret; + int i; + + ret = tpm2_get_capability(dev, TPM_CAP_PCRS, 0, response, 1, true); + if (ret) + return -1; + + pcrs.count = get_unaligned_be32(response); + if (pcrs.count > MAX_HASH_COUNT || pcrs.count < 1) + return -1; + + for (i = 0; i < pcrs.count; i++) { + /* + * Definition of TPMS_PCR_SELECTION Structure + * hash: u16 + * size_of_select: u8 + * pcr_select: u8 array + */ + u32 hash_offset = sizeof(pcrs.count) + i * sizeof(pcrs.selection[0]); + u32 size_select_offset = + hash_offset + offsetof(struct tpms_pcr_selection, size_of_select); + u32 pcr_select_offset = + hash_offset + offsetof(struct tpms_pcr_selection, pcr_select); + + pcrs.selection[i].hash = get_unaligned_be16(response + hash_offset); + pcrs.selection[i].size_of_select = + __get_unaligned_be(response + size_select_offset); + /* copy the array of pcr_select */ + memcpy(pcrs.selection[i].pcr_select, response + pcr_select_offset, + pcrs.selection[i].size_of_select); + } + + for (i = 0; i < pcrs.count; i++) { + switch (pcrs.selection[i].hash) { + case TPM2_ALG_SHA1: + *supported_pcr |= EFI_TCG2_BOOT_HASH_ALG_SHA1; + if (is_active_pcr(&pcrs.selection[i])) + *active_pcr |= EFI_TCG2_BOOT_HASH_ALG_SHA1; + break; + case TPM2_ALG_SHA256: + *supported_pcr |= EFI_TCG2_BOOT_HASH_ALG_SHA256; + if (is_active_pcr(&pcrs.selection[i])) + *active_pcr |= EFI_TCG2_BOOT_HASH_ALG_SHA256; + break; + case TPM2_ALG_SHA384: + *supported_pcr |= EFI_TCG2_BOOT_HASH_ALG_SHA384; + if (is_active_pcr(&pcrs.selection[i])) + *active_pcr |= EFI_TCG2_BOOT_HASH_ALG_SHA384; + break; + case TPM2_ALG_SHA512: + *supported_pcr |= EFI_TCG2_BOOT_HASH_ALG_SHA512; + if (is_active_pcr(&pcrs.selection[i])) + *active_pcr |= EFI_TCG2_BOOT_HASH_ALG_SHA512; + break; + case TPM2_ALG_SM3_256: + *supported_pcr |= EFI_TCG2_BOOT_HASH_ALG_SM3_256; + if (is_active_pcr(&pcrs.selection[i])) + *active_pcr |= EFI_TCG2_BOOT_HASH_ALG_SM3_256; + break; + default: + log_err("Unknown algorithm %x\n", pcrs.selection[i].hash); + break; + } + } + + *pcr_banks = pcrs.count; + + return 0; +} + +/** + * get_capability() - provide protocol capability information and state information + * + * @this: TCG2 protocol instance + * @capability: caller allocated memory with size field to the size of the + * structure allocated + + * Return: status code + */ +static efi_status_t EFIAPI +get_capability(struct efi_tcg2_protocol *this, + struct efi_tcg2_boot_service_capability *capability) +{ + struct udevice *dev; + int ret; + + EFI_ENTRY("%p, %p", this, capability); + + if (!this || !capability) + return EFI_EXIT(EFI_INVALID_PARAMETER); + + if (capability->size < boot_service_capability_min) { + capability->size = boot_service_capability_min; + return EFI_EXIT(EFI_BUFFER_TOO_SMALL); + } + + if (capability->size < sizeof(*capability)) { + capability->size = sizeof(*capability); + return EFI_EXIT(EFI_BUFFER_TOO_SMALL); + } + + capability->structure_version.major = 1; + capability->structure_version.minor = 1; + capability->protocol_version.major = 1; + capability->protocol_version.minor = 1; + + ret = platform_get_tpm2_device(&dev); + if (ret != EFI_SUCCESS) { + capability->supported_event_logs = 0; + capability->hash_algorithm_bitmap = 0; + capability->tpm_present_flag = false; + capability->max_command_size = 0; + capability->max_response_size = 0; + capability->manufacturer_id = 0; + capability->number_of_pcr_banks = 0; + capability->active_pcr_banks = 0; + + return EFI_EXIT(EFI_SUCCESS); + } + + /* We only allow a TPMv2 device to register the EFI protocol */ + capability->supported_event_logs = TCG2_EVENT_LOG_FORMAT_TCG_2; + + capability->tpm_present_flag = true; + + /* Supported and active PCRs */ + capability->hash_algorithm_bitmap = 0; + capability->active_pcr_banks = 0; + ret = tpm2_get_pcr_info(dev, &capability->hash_algorithm_bitmap, + &capability->active_pcr_banks, + &capability->number_of_pcr_banks); + if (ret) + return EFI_EXIT(EFI_DEVICE_ERROR); + + /* Max command size */ + ret = tpm2_get_max_command_size(dev, &capability->max_command_size); + if (ret) + return EFI_EXIT(EFI_DEVICE_ERROR); + + /* Max response size */ + ret = tpm2_get_max_response_size(dev, &capability->max_response_size); + if (ret) + return EFI_EXIT(EFI_DEVICE_ERROR); + + /* Manufacturer ID */ + ret = tpm2_get_manufacturer_id(dev, &capability->manufacturer_id); + if (ret) + return EFI_EXIT(EFI_DEVICE_ERROR); + + return EFI_EXIT(EFI_SUCCESS); +} + +/** + * get_eventlog() - retrieve the the address of a given event log and its last entry. + * + * @this: TCG2 protocol instance + * @log_format: type of event log format + * @event_log_location: pointer to the memory address of the event log + * @event_log_last_entry: pointer to the address of the start of the last entry in the + * event log in memory, if log contains more than 1 entry + * @event_log_truncated: set to true, if the Event Log is missing at least one entry + * + * Return: status code + */ +static efi_status_t EFIAPI +get_eventlog(struct efi_tcg2_protocol *this, + efi_tcg_event_log_format log_format, u64 *event_log_location, + u64 *event_log_last_entry, bool *event_log_truncated) +{ + return EFI_UNSUPPORTED; +} + +/** + * hash_log_extend_event()- extend and optionally log events + * + * @this: TCG2 protocol instance + * @flags: bitmap providing additional information on the operation + * @data_to_hash: physical address of the start of the data buffer to be hashed + * @data_to_hash_len: the length in bytes of the buffer referenced by data_to_hash + * @efi_tcg_event: pointer to data buffer containing information about the event + * + * Return: status code + */ +static efi_status_t EFIAPI +hash_log_extend_event(struct efi_tcg2_protocol *this, u64 flags, + u64 data_to_hash, u64 data_to_hash_len, + struct efi_tcg2_event *efi_tcg_event) +{ + return EFI_UNSUPPORTED; +} + +/** + * submit_command() - Send command to the TPM + * + * @this: TCG2 protocol instance + * @input_param_block_size: size of the TPM input parameter block + * @input_param_block: pointer to the TPM input parameter block + * @output_param_block_size: size of the TPM output parameter block + * @output_param_block: pointer to the TPM output parameter block + * + * Return: status code + */ +efi_status_t EFIAPI +submit_command(struct efi_tcg2_protocol *this, u32 input_param_block_size, + u8 *input_param_block, u32 output_param_block_size, + u8 *output_param_block) +{ + return EFI_UNSUPPORTED; +} + +/** + * get_active_pcr_banks() - returns the currently active PCR banks + * + * @this: TCG2 protocol instance + * @active_pcr_banks: pointer for receiving the bitmap of currently active PCR banks + * + * Return: status code + */ +efi_status_t EFIAPI +get_active_pcr_banks(struct efi_tcg2_protocol *this, u32 *active_pcr_banks) +{ + return EFI_UNSUPPORTED; +} + +/** + * set_active_pcr_banks() - sets the currently active PCR banks + * + * @this: TCG2 protocol instance + * @active_pcr_banks: bitmap of the requested active PCR banks + * + * Return: status code + */ +efi_status_t EFIAPI +set_active_pcr_banks(struct efi_tcg2_protocol *this, u32 active_pcr_banks) +{ + return EFI_UNSUPPORTED; +} + +/** + * get_result_of_set_active_pcr_banks() - retrieves the result of a previous set_active_pcr_banks() + * + * @this: TCG2 protocol instance + * @operation_present: non-zero value to indicate a set_active_pcr_banks operation was + * invoked during last boot + * @response: result value could be returned + * + * Return: status code + */ +efi_status_t EFIAPI +get_result_of_set_active_pcr_banks(struct efi_tcg2_protocol *this, + u32 *operation_present, u32 *response) +{ + return EFI_UNSUPPORTED; +} + +static const struct efi_tcg2_protocol efi_tcg2_protocol = { + .get_capability = get_capability, + .get_eventlog = get_eventlog, + .hash_log_extend_event = hash_log_extend_event, + .submit_command = submit_command, + .get_active_pcr_banks = get_active_pcr_banks, + .set_active_pcr_banks = set_active_pcr_banks, + .get_result_of_set_active_pcr_banks = get_result_of_set_active_pcr_banks, +}; + +/** + * efi_tcg2_register() - register EFI_TCG2_PROTOCOL + * + * If a TPM2 device is available, the TPM TCG2 Protocol is registered + * + * Return: An error status is only returned if adding the protocol fails. + */ +efi_status_t efi_tcg2_register(void) +{ + int ret; + struct udevice *dev; + enum tpm_version tpm_ver; + + ret = platform_get_tpm2_device(&dev); + if (ret != EFI_SUCCESS) + return EFI_SUCCESS; + + tpm_ver = tpm_get_version(dev); + if (tpm_ver != TPM_V2) { + log_warning("Only TPMv2 supported for EFI_TCG2_PROTOCOL"); + return EFI_SUCCESS; + } + + ret = efi_add_protocol(efi_root, &efi_guid_tcg2_protocol, + (void *)&efi_tcg2_protocol); + if (ret != EFI_SUCCESS) + log_err("Cannot install EFI_TCG2_PROTOCOL"); + + return ret; +}

On 04.11.20 14:47, Ilias Apalodimas wrote:
Since U-boot EFI implementation is getting richer it makes sense to add support for EFI_TCG2_PROTOCOL taking advantage of any hardware TPM available on the device.
This is the initial implementation of the protocol which only adds support for GetCapability(). It's limited in the newer and safer TPMv2 devices.
Signed-off-by: Ilias Apalodimas ilias.apalodimas@linaro.org
The protocol requires mode that GetCapability to be usable. I intend to add support for GetEventLog() and HashLogExtendEvent() once this gets reviewed/merged include/efi_loader.h | 2 + include/efi_tcg2.h | 91 ++++++++ include/tpm-v2.h | 48 ++++ lib/efi_loader/Kconfig | 8 + lib/efi_loader/Makefile | 1 + lib/efi_loader/efi_setup.c | 7 + lib/efi_loader/efi_tcg2.c | 460 +++++++++++++++++++++++++++++++++++++ 7 files changed, 617 insertions(+) create mode 100644 include/efi_tcg2.h create mode 100644 lib/efi_loader/efi_tcg2.c
diff --git a/include/efi_loader.h b/include/efi_loader.h index f550ced56876..e5015d865ec9 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -405,6 +405,8 @@ efi_status_t efi_console_register(void); efi_status_t efi_disk_register(void); /* Called by efi_init_obj_list() to install EFI_RNG_PROTOCOL */ efi_status_t efi_rng_register(void); +/* Called by efi_init_obj_list() to install EFI_TCG2_PROTOCOL */ +efi_status_t efi_tcg2_register(void); /* Create handles and protocols for the partitions of a block device */ int efi_disk_create_partitions(efi_handle_t parent, struct blk_desc *desc, const char *if_typename, int diskid, diff --git a/include/efi_tcg2.h b/include/efi_tcg2.h new file mode 100644 index 000000000000..9e7b85db058d --- /dev/null +++ b/include/efi_tcg2.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- Copyright (c) 2020, Linaro Limited
- */
+#if !defined _EFI_TCG2_PROTOCOL_H_ +#define _EFI_TCG2_PROTOCOL_H_
+#include <tpm-v2.h>
+#define EFI_TCG2_PROTOCOL_GUID \
- EFI_GUID(0x607f766c, 0x7455, 0x42be, 0x93, \
0x0b, 0xe4, 0xd7, 0x6d, 0xb2, 0x72, 0x0f)
+/* TPMV2 only */ +#define TCG2_EVENT_LOG_FORMAT_TCG_2 0x00000002
+/* SHA1, SHA256, SHA384, SHA512, TPM_ALG_SM3_256 */ +#define MAX_HASH_COUNT 5 +/* Algorithm Registry */ +#define EFI_TCG2_BOOT_HASH_ALG_SHA1 0x00000001 +#define EFI_TCG2_BOOT_HASH_ALG_SHA256 0x00000002 +#define EFI_TCG2_BOOT_HASH_ALG_SHA384 0x00000004 +#define EFI_TCG2_BOOT_HASH_ALG_SHA512 0x00000008 +#define EFI_TCG2_BOOT_HASH_ALG_SM3_256 0x00000010
+typedef u32 efi_tcg_event_log_bitmap; +typedef u32 efi_tcg_event_log_format; +typedef u32 efi_tcg_event_algorithm_bitmap;
+struct efi_tcg2_version {
- u8 major;
- u8 minor;
+};
+struct efi_tcg2_event_header {
- u32 header_size;
- u16 header_version;
- u32 pcr_index;
- u32 event_type;
+} __packed;
+struct efi_tcg2_event {
- u32 size;
- struct efi_tcg2_event_header header;
- u8 event[];
+} __packed;
+struct efi_tcg2_boot_service_capability {
- u8 size;
- struct efi_tcg2_version structure_version;
- struct efi_tcg2_version protocol_version;
- efi_tcg_event_algorithm_bitmap hash_algorithm_bitmap;
- efi_tcg_event_log_bitmap supported_event_logs;
- bool tpm_present_flag;
- u16 max_command_size;
- u16 max_response_size;
- u32 manufacturer_id;
- u32 number_of_pcr_banks;
- efi_tcg_event_algorithm_bitmap active_pcr_banks;
+};
+#define boot_service_capability_min \
- sizeof(struct efi_tcg2_boot_service_capability) - \
- offsetof(struct efi_tcg2_boot_service_capability, number_of_pcr_banks)
+struct efi_tcg2_protocol {
- efi_status_t (EFIAPI * get_capability)(struct efi_tcg2_protocol *this,
struct efi_tcg2_boot_service_capability *capability);
- efi_status_t (EFIAPI * get_eventlog)(struct efi_tcg2_protocol *this,
efi_tcg_event_log_format log_format,
u64 *event_log_location, u64 *event_log_last_entry,
bool *event_log_truncated);
- efi_status_t (EFIAPI * hash_log_extend_event)(struct efi_tcg2_protocol *this,
u64 flags, u64 data_to_hash,
u64 data_to_hash_len,
struct efi_tcg2_event *efi_tcg_event);
- efi_status_t (EFIAPI * submit_command)(struct efi_tcg2_protocol *this,
u32 input_parameter_block_size,
u8 *input_parameter_block,
u32 output_parameter_block_size,
u8 *output_parameter_block);
- efi_status_t (EFIAPI * get_active_pcr_banks)(struct efi_tcg2_protocol *this,
u32 *active_pcr_banks);
- efi_status_t (EFIAPI * set_active_pcr_banks)(struct efi_tcg2_protocol *this,
u32 active_pcr_banks);
- efi_status_t (EFIAPI * get_result_of_set_active_pcr_banks)(struct efi_tcg2_protocol *this,
u32 *operation_present,
u32 *response);
+}; +#endif diff --git a/include/tpm-v2.h b/include/tpm-v2.h index ee74028ca83b..505b8c96eb92 100644 --- a/include/tpm-v2.h +++ b/include/tpm-v2.h @@ -11,6 +11,52 @@
#define TPM2_DIGEST_LEN 32
+#define IMPLEMENTATION_PCR 24
+/*
- We deviate from this draft of the specification by increasing the value of TPM2_NUM_PCR_BANKS
- from 3 to 16 to ensure compatibility with TPM2 implementations that have enabled a larger than
- typical number of PCR banks. This larger value for TPM2_NUM_PCR_BANKS is expected to be included
- in a future revision of the specification.
- */
+#define TPM2_NUM_PCR_BANKS 16
+/* Table 22 — Definition of (UINT32) TPM_CAP Constants */ +#define TPM_CAP_FIRST 0x00000000U +#define TPM_CAP_ALGS 0x00000000U +#define TPM_CAP_HANDLES 0x00000001U +#define TPM_CAP_COMMANDS 0x00000002U +#define TPM_CAP_PP_COMMANDS 0x00000003U +#define TPM_CAP_AUDIT_COMMANDS 0x00000004U +#define TPM_CAP_PCRS 0x00000005U +#define TPM_CAP_TPM_PROPERTIES 0x00000006U +#define TPM_CAP_PCR_PROPERTIES 0x00000007U +#define TPM_CAP_ECC_CURVES 0x00000008U +#define TPM_CAP_LAST 0x00000008U +#define TPM_CAP_VENDOR_PROPERTY 0x00000100U
Thanks a lot for implementing this protocol which may be consumed by GRUB for example.
The changes to tpm-v2.h should be in a separate patch as they are not tied to UEFI.
+/* Table 23 — Definition of (UINT32) TPM_PT Constants */ +#define TPM_PT_NONE (u32)(0x00000000) +#define PT_GROUP (u32)(0x00000100) +#define PT_FIXED (u32)(PT_GROUP * 1) +#define TPM_PT_MANUFACTURER (u32)(PT_FIXED + 5) +#define TPM_PT_PCR_COUNT (u32)(PT_FIXED + 18) +#define TPM_PT_MAX_COMMAND_SIZE (u32)(PT_FIXED + 30) +#define TPM_PT_MAX_RESPONSE_SIZE (u32)(PT_FIXED + 31)
+/* Definition of TPMS_PCR_SELECTION Structure */ +struct tpms_pcr_selection {
- u16 hash;
- u8 size_of_select;
- u8 pcr_select[(IMPLEMENTATION_PCR + 7) / 8];
+} __packed;
+/* Definition of TPML_PCR_SELECTION Structure */ +struct tpml_pcr_selection {
- u32 count;
- struct tpms_pcr_selection selection[TPM2_NUM_PCR_BANKS];
+} __packed;
/**
- TPM2 Structure Tags for command/response buffers.
@@ -123,11 +169,13 @@ enum tpm2_return_codes {
- TPM2 algorithms.
*/ enum tpm2_algorithms {
- TPM2_ALG_SHA1 = 0x04, TPM2_ALG_XOR = 0x0A, TPM2_ALG_SHA256 = 0x0B, TPM2_ALG_SHA384 = 0x0C, TPM2_ALG_SHA512 = 0x0D, TPM2_ALG_NULL = 0x10,
- TPM2_ALG_SM3_256 = 0x12,
};
/* NV index attributes */ diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index 075481428cdf..5d5074a4bc41 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -184,6 +184,14 @@ config EFI_RNG_PROTOCOL Provide a EFI_RNG_PROTOCOL implementation using the hardware random number generator of the platform.
+config EFI_TCG2_PROTOCOL
- bool "EFI_TCG2_PROTOCOL support"
- depends on TPM_V2
- default n
- help
Provide a EFI_TCG2_PROTOCOL implementation using the TPM hardware
of the platform.
config EFI_LOAD_FILE2_INITRD bool "EFI_FILE_LOAD2_PROTOCOL for Linux initial ramdisk" default n diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index 8892fb01e125..cd4b252a417c 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -53,6 +53,7 @@ obj-$(CONFIG_NET) += efi_net.o obj-$(CONFIG_GENERATE_ACPI_TABLE) += efi_acpi.o obj-$(CONFIG_GENERATE_SMBIOS_TABLE) += efi_smbios.o obj-$(CONFIG_EFI_RNG_PROTOCOL) += efi_rng.o +obj-$(CONFIG_EFI_TCG2_PROTOCOL) += efi_tcg2.o obj-$(CONFIG_EFI_LOAD_FILE2_INITRD) += efi_load_initrd.o obj-y += efi_signature.o
diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c index 45226c5c1a53..e206b60bb82c 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -156,6 +156,13 @@ efi_status_t efi_init_obj_list(void) if (ret != EFI_SUCCESS) goto out; }
- if (IS_ENABLED(CONFIG_EFI_TCG2_PROTOCOL)) {
ret = efi_tcg2_register();
if (ret != EFI_SUCCESS)
goto out;
- }
- /* Initialize variable services */ ret = efi_init_variables(); if (ret != EFI_SUCCESS)
diff --git a/lib/efi_loader/efi_tcg2.c b/lib/efi_loader/efi_tcg2.c new file mode 100644 index 000000000000..1e12f95b2e41 --- /dev/null +++ b/lib/efi_loader/efi_tcg2.c @@ -0,0 +1,460 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright (c) 2020, Linaro Limited
- */
+#define LOG_CATEGORY LOGC_EFI +#include <common.h> +#include <dm.h> +#include <efi_loader.h> +#include <efi_tcg2.h> +#include <log.h> +#include <tpm-v2.h> +#include <linux/unaligned/access_ok.h> +#include <linux/unaligned/generic.h>
+DECLARE_GLOBAL_DATA_PTR;
+#define COMMAND_BUFFER_SIZE 256
+const efi_guid_t efi_guid_tcg2_protocol = EFI_TCG2_PROTOCOL_GUID;
+/**
- platform_get_tpm_device() - retrieve TPM device
- This function retrieves the udevice implementing a TPM
- This function may be overridden if special initialization is needed.
- @dev: udevice
- Return: status code
- */
+__weak efi_status_t platform_get_tpm2_device(struct udevice **dev) +{
- int ret;
- struct udevice *devp;
- ret = uclass_get_device(UCLASS_TPM, 0, &devp);
- if (ret) {
log_warning("Unable to get tpm device\n");
return EFI_DEVICE_ERROR;
- }
- *dev = devp;
- return EFI_SUCCESS;
+}
We are talking to the DM driver. That DM driver should take care of any initialization needed to make the TPM usable. I do not understand why we need a weak function here.
+/**
- tpm2_get_max_command_size() - TPM2 command to get the supported max command size
- @dev: TPM device
- @max_command_size: output buffer for the size
- Return: 0 on success -1 on error
- */
+static int tpm2_get_max_command_size(struct udevice *dev, u16 *max_command_size) +{
- u8 response[COMMAND_BUFFER_SIZE];
- u32 ret;
- ret = tpm2_get_capability(dev, TPM_CAP_TPM_PROPERTIES,
TPM_PT_MAX_COMMAND_SIZE, response, 1, false);
- if (ret)
return -1;
- /*
* On the definition of TPMS_TAGGED_PROPERTY Structure
* property: u32
* value: u32
* So advance the response for TPM_CAP_TPM_PROPERTIES by sizeof(u32)
* to get the value
*/
- *max_command_size = (uint16_t)get_unaligned_be32(response + sizeof(u32));
Why are we using COMMAND_BUFFER_SIZE throughout the TPM code if the required buffer size for commands and responses can be read from the TPM device?
- return 0;
+}
+/**
- tpm2_get_max_response_size() - TPM2 command to get the supported max response size
- @dev: TPM device
- @max_response_size: output buffer for the size
- Return: 0 on success -1 on error
- */
+static int tpm2_get_max_response_size(struct udevice *dev, u16 *max_response_size) +{
- u8 response[COMMAND_BUFFER_SIZE];
- u32 ret;
- ret = tpm2_get_capability(dev, TPM_CAP_TPM_PROPERTIES,
TPM_PT_MAX_RESPONSE_SIZE, response, 1, false);
- if (ret)
return -1;
- /*
* On the definition of TPMS_TAGGED_PROPERTY Structure
* property: u32
* value: u32
* So advance the response for TPM_CAP_TPM_PROPERTIES by sizeof(u32)
* to get the value
*/
- *max_response_size = (uint16_t)get_unaligned_be32(response + sizeof(u32));
- return 0;
+}
+/**
- tpm2_get_manufacturer_id() - Issue a TPM2 command to get the manufacturer ID
- @dev: TPM device
- @manufacturer_id: output buffer for the id
- Return: 0 on success -1 on error
- */
+static int tpm2_get_manufacturer_id(struct udevice *dev, u32 *manufacturer_id) +{
- u8 response[COMMAND_BUFFER_SIZE];
- u32 ret;
- ret = tpm2_get_capability(dev, TPM_CAP_TPM_PROPERTIES,
TPM_PT_MANUFACTURER, response, 1, false);
- if (ret)
return -1;
- *manufacturer_id = get_unaligned_be32(response + sizeof(u32));
- return 0;
+}
+/**
- is_active_pcr() - Check if an supported algorithm is active
%s/an supported/a supported/
- @dev: TPM device
- @selection: struct of PCR information
- Return: true if PCR is active
- */
+bool is_active_pcr(struct tpms_pcr_selection *selection) +{
- int i;
- /*
* check the pcr_select. If at least one of the PCRs supports the algorithm
* add it on the active ones
*/
- for (i = 0; i < selection->size_of_select; i++) {
if (selection->pcr_select[i])
return true;
- }
- return false;
+}
+/**
- tpm2_get_pcr_info() - Issue a TPM2 command to get the supported, active PCRs and number of banks
- @dev: TPM device
- @supported_pcr: bitmask with the algorithms supported
- @active_pcr: bitmask with the active algorithms
- @pcr_banks: number of PCR banks
- Return: 0 on success -1 on error
- */
+static int tpm2_get_pcr_info(struct udevice *dev, u32 *supported_pcr, u32 *active_pcr,
u32 *pcr_banks)
+{
- u8 response[COMMAND_BUFFER_SIZE];
- struct tpml_pcr_selection pcrs;
- u32 ret;
- int i;
- ret = tpm2_get_capability(dev, TPM_CAP_PCRS, 0, response, 1, true);
- if (ret)
return -1;
- pcrs.count = get_unaligned_be32(response);
- if (pcrs.count > MAX_HASH_COUNT || pcrs.count < 1)
return -1;
- for (i = 0; i < pcrs.count; i++) {
/*
* Definition of TPMS_PCR_SELECTION Structure
* hash: u16
* size_of_select: u8
* pcr_select: u8 array
*/
u32 hash_offset = sizeof(pcrs.count) + i * sizeof(pcrs.selection[0]);
u32 size_select_offset =
hash_offset + offsetof(struct tpms_pcr_selection, size_of_select);
u32 pcr_select_offset =
hash_offset + offsetof(struct tpms_pcr_selection, pcr_select);
pcrs.selection[i].hash = get_unaligned_be16(response + hash_offset);
pcrs.selection[i].size_of_select =
__get_unaligned_be(response + size_select_offset);
/* copy the array of pcr_select */
memcpy(pcrs.selection[i].pcr_select, response + pcr_select_offset,
pcrs.selection[i].size_of_select);
- }
- for (i = 0; i < pcrs.count; i++) {
switch (pcrs.selection[i].hash) {
case TPM2_ALG_SHA1:
*supported_pcr |= EFI_TCG2_BOOT_HASH_ALG_SHA1;
if (is_active_pcr(&pcrs.selection[i]))
*active_pcr |= EFI_TCG2_BOOT_HASH_ALG_SHA1;
break;
case TPM2_ALG_SHA256:
*supported_pcr |= EFI_TCG2_BOOT_HASH_ALG_SHA256;
if (is_active_pcr(&pcrs.selection[i]))
*active_pcr |= EFI_TCG2_BOOT_HASH_ALG_SHA256;
break;
case TPM2_ALG_SHA384:
*supported_pcr |= EFI_TCG2_BOOT_HASH_ALG_SHA384;
if (is_active_pcr(&pcrs.selection[i]))
*active_pcr |= EFI_TCG2_BOOT_HASH_ALG_SHA384;
break;
case TPM2_ALG_SHA512:
*supported_pcr |= EFI_TCG2_BOOT_HASH_ALG_SHA512;
if (is_active_pcr(&pcrs.selection[i]))
*active_pcr |= EFI_TCG2_BOOT_HASH_ALG_SHA512;
break;
case TPM2_ALG_SM3_256:
*supported_pcr |= EFI_TCG2_BOOT_HASH_ALG_SM3_256;
if (is_active_pcr(&pcrs.selection[i]))
*active_pcr |= EFI_TCG2_BOOT_HASH_ALG_SM3_256;
break;
default:
log_err("Unknown algorithm %x\n", pcrs.selection[i].hash);
You do not want to mess up the screen output of an EFI application.
Please, use EFI_PRINT().
break;
}
- }
- *pcr_banks = pcrs.count;
- return 0;
+}
+/**
- get_capability() - provide protocol capability information and state information
- @this: TCG2 protocol instance
- @capability: caller allocated memory with size field to the size of the
structure allocated
- Return: status code
- */
+static efi_status_t EFIAPI +get_capability(struct efi_tcg2_protocol *this,
struct efi_tcg2_boot_service_capability *capability)
+{
- struct udevice *dev;
- int ret;
- EFI_ENTRY("%p, %p", this, capability);
- if (!this || !capability)
return EFI_EXIT(EFI_INVALID_PARAMETER);
- if (capability->size < boot_service_capability_min) {
capability->size = boot_service_capability_min;
return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
- }
- if (capability->size < sizeof(*capability)) {
capability->size = sizeof(*capability);
return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
- }
- capability->structure_version.major = 1;
- capability->structure_version.minor = 1;
- capability->protocol_version.major = 1;
- capability->protocol_version.minor = 1;
- ret = platform_get_tpm2_device(&dev);
The return value is of type efi_uintn_t and not int.
- if (ret != EFI_SUCCESS) {
capability->supported_event_logs = 0;
capability->hash_algorithm_bitmap = 0;
capability->tpm_present_flag = false;
capability->max_command_size = 0;
capability->max_response_size = 0;
capability->manufacturer_id = 0;
capability->number_of_pcr_banks = 0;
capability->active_pcr_banks = 0;
Why don't you simply return EFI_DEVICE_ERROR if there is no device?
So actually I would write:
if (ret != EFI_SUCCESS) goto out;
Where out: is the central exit point.
out: EFI_EXIT(ret);
return EFI_EXIT(EFI_SUCCESS);
- }
- /* We only allow a TPMv2 device to register the EFI protocol */
- capability->supported_event_logs = TCG2_EVENT_LOG_FORMAT_TCG_2;
- capability->tpm_present_flag = true;
- /* Supported and active PCRs */
- capability->hash_algorithm_bitmap = 0;
- capability->active_pcr_banks = 0;
- ret = tpm2_get_pcr_info(dev, &capability->hash_algorithm_bitmap,
&capability->active_pcr_banks,
&capability->number_of_pcr_banks);
- if (ret)
return EFI_EXIT(EFI_DEVICE_ERROR);
- /* Max command size */
- ret = tpm2_get_max_command_size(dev, &capability->max_command_size);
- if (ret)
return EFI_EXIT(EFI_DEVICE_ERROR);
- /* Max response size */
- ret = tpm2_get_max_response_size(dev, &capability->max_response_size);
- if (ret)
return EFI_EXIT(EFI_DEVICE_ERROR);
- /* Manufacturer ID */
- ret = tpm2_get_manufacturer_id(dev, &capability->manufacturer_id);
- if (ret)
return EFI_EXIT(EFI_DEVICE_ERROR);
- return EFI_EXIT(EFI_SUCCESS);
+}
+/**
- get_eventlog() - retrieve the the address of a given event log and its last entry.
- @this: TCG2 protocol instance
- @log_format: type of event log format
- @event_log_location: pointer to the memory address of the event log
- @event_log_last_entry: pointer to the address of the start of the last entry in the
event log in memory, if log contains more than 1 entry
- @event_log_truncated: set to true, if the Event Log is missing at least one entry
- Return: status code
- */
+static efi_status_t EFIAPI +get_eventlog(struct efi_tcg2_protocol *this,
efi_tcg_event_log_format log_format, u64 *event_log_location,
u64 *event_log_last_entry, bool *event_log_truncated)
+{
- return EFI_UNSUPPORTED;
+}
+/**
- hash_log_extend_event()- extend and optionally log events
- @this: TCG2 protocol instance
- @flags: bitmap providing additional information on the operation
- @data_to_hash: physical address of the start of the data buffer to be hashed
- @data_to_hash_len: the length in bytes of the buffer referenced by data_to_hash
- @efi_tcg_event: pointer to data buffer containing information about the event
- Return: status code
- */
+static efi_status_t EFIAPI +hash_log_extend_event(struct efi_tcg2_protocol *this, u64 flags,
u64 data_to_hash, u64 data_to_hash_len,
struct efi_tcg2_event *efi_tcg_event)
+{
- return EFI_UNSUPPORTED;
+}
+/**
- submit_command() - Send command to the TPM
- @this: TCG2 protocol instance
- @input_param_block_size: size of the TPM input parameter block
- @input_param_block: pointer to the TPM input parameter block
- @output_param_block_size: size of the TPM output parameter block
- @output_param_block: pointer to the TPM output parameter block
- Return: status code
- */
+efi_status_t EFIAPI +submit_command(struct efi_tcg2_protocol *this, u32 input_param_block_size,
u8 *input_param_block, u32 output_param_block_size,
u8 *output_param_block)
+{
- return EFI_UNSUPPORTED;
+}
+/**
- get_active_pcr_banks() - returns the currently active PCR banks
- @this: TCG2 protocol instance
- @active_pcr_banks: pointer for receiving the bitmap of currently active PCR banks
- Return: status code
- */
+efi_status_t EFIAPI +get_active_pcr_banks(struct efi_tcg2_protocol *this, u32 *active_pcr_banks) +{
- return EFI_UNSUPPORTED;
+}
+/**
- set_active_pcr_banks() - sets the currently active PCR banks
- @this: TCG2 protocol instance
- @active_pcr_banks: bitmap of the requested active PCR banks
- Return: status code
- */
+efi_status_t EFIAPI +set_active_pcr_banks(struct efi_tcg2_protocol *this, u32 active_pcr_banks) +{
- return EFI_UNSUPPORTED;
+}
+/**
- get_result_of_set_active_pcr_banks() - retrieves the result of a previous set_active_pcr_banks()
- @this: TCG2 protocol instance
- @operation_present: non-zero value to indicate a set_active_pcr_banks operation was
invoked during last boot
- @response: result value could be returned
- Return: status code
- */
+efi_status_t EFIAPI +get_result_of_set_active_pcr_banks(struct efi_tcg2_protocol *this,
u32 *operation_present, u32 *response)
+{
- return EFI_UNSUPPORTED;
+}
+static const struct efi_tcg2_protocol efi_tcg2_protocol = {
- .get_capability = get_capability,
- .get_eventlog = get_eventlog,
- .hash_log_extend_event = hash_log_extend_event,
- .submit_command = submit_command,
- .get_active_pcr_banks = get_active_pcr_banks,
- .set_active_pcr_banks = set_active_pcr_banks,
- .get_result_of_set_active_pcr_banks = get_result_of_set_active_pcr_banks,
+};
+/**
- efi_tcg2_register() - register EFI_TCG2_PROTOCOL
- If a TPM2 device is available, the TPM TCG2 Protocol is registered
- Return: An error status is only returned if adding the protocol fails.
- */
+efi_status_t efi_tcg2_register(void) +{
- int ret;
efi_unintn_t ret;
Best regards
Heinrich
- struct udevice *dev;
- enum tpm_version tpm_ver;
- ret = platform_get_tpm2_device(&dev);
The return value is of type efi_uintn_t (aka size_t).
- if (ret != EFI_SUCCESS)
return EFI_SUCCESS;
- tpm_ver = tpm_get_version(dev);
- if (tpm_ver != TPM_V2) {
log_warning("Only TPMv2 supported for EFI_TCG2_PROTOCOL");
return EFI_SUCCESS;
- }
- ret = efi_add_protocol(efi_root, &efi_guid_tcg2_protocol,
(void *)&efi_tcg2_protocol);
- if (ret != EFI_SUCCESS)
log_err("Cannot install EFI_TCG2_PROTOCOL");
- return ret;
+}

Hi Heinrich
[...]
+#define TPM2_NUM_PCR_BANKS 16
+/* Table 22 — Definition of (UINT32) TPM_CAP Constants */ +#define TPM_CAP_FIRST 0x00000000U +#define TPM_CAP_ALGS 0x00000000U +#define TPM_CAP_HANDLES 0x00000001U +#define TPM_CAP_COMMANDS 0x00000002U +#define TPM_CAP_PP_COMMANDS 0x00000003U +#define TPM_CAP_AUDIT_COMMANDS 0x00000004U +#define TPM_CAP_PCRS 0x00000005U +#define TPM_CAP_TPM_PROPERTIES 0x00000006U +#define TPM_CAP_PCR_PROPERTIES 0x00000007U +#define TPM_CAP_ECC_CURVES 0x00000008U +#define TPM_CAP_LAST 0x00000008U +#define TPM_CAP_VENDOR_PROPERTY 0x00000100U
Thanks a lot for implementing this protocol which may be consumed by GRUB for example.
There's still some way ahead for that, but at least this implements the basics
The changes to tpm-v2.h should be in a separate patch as they are not tied to UEFI.
Ok I'll split them off in v2
[...]
+const efi_guid_t efi_guid_tcg2_protocol = EFI_TCG2_PROTOCOL_GUID;
+/**
- platform_get_tpm_device() - retrieve TPM device
- This function retrieves the udevice implementing a TPM
- This function may be overridden if special initialization is needed.
- @dev: udevice
- Return: status code
- */
+__weak efi_status_t platform_get_tpm2_device(struct udevice **dev) +{
- int ret;
- struct udevice *devp;
- ret = uclass_get_device(UCLASS_TPM, 0, &devp);
- if (ret) {
log_warning("Unable to get tpm device\n");
return EFI_DEVICE_ERROR;
- }
- *dev = devp;
- return EFI_SUCCESS;
+}
We are talking to the DM driver. That DM driver should take care of any initialization needed to make the TPM usable. I do not understand why we need a weak function here.
I was defined as a __weak function in efi_rng.c, so I assumed that was the common practice. Shall I remove it?
+/**
- tpm2_get_max_command_size() - TPM2 command to get the supported max command size
- @dev: TPM device
- @max_command_size: output buffer for the size
- Return: 0 on success -1 on error
- */
+static int tpm2_get_max_command_size(struct udevice *dev, u16 *max_command_size) +{
- u8 response[COMMAND_BUFFER_SIZE];
- u32 ret;
- ret = tpm2_get_capability(dev, TPM_CAP_TPM_PROPERTIES,
TPM_PT_MAX_COMMAND_SIZE, response, 1, false);
- if (ret)
return -1;
- /*
* On the definition of TPMS_TAGGED_PROPERTY Structure
* property: u32
* value: u32
* So advance the response for TPM_CAP_TPM_PROPERTIES by sizeof(u32)
* to get the value
*/
- *max_command_size = (uint16_t)get_unaligned_be32(response + sizeof(u32));
Why are we using COMMAND_BUFFER_SIZE throughout the TPM code if the required buffer size for commands and responses can be read from the TPM device?
I think the logic is that 256b is enough for the basic commands we needed. I can change that here. Get the TPM response during efi_tcg2_register() and use that for the rest of the code?
- return 0;
+}
+/**
- tpm2_get_max_response_size() - TPM2 command to get the supported max response size
- @dev: TPM device
- @max_response_size: output buffer for the size
- Return: 0 on success -1 on error
- */
+static int tpm2_get_max_response_size(struct udevice *dev, u16 *max_response_size) +{
- u8 response[COMMAND_BUFFER_SIZE];
- u32 ret;
- ret = tpm2_get_capability(dev, TPM_CAP_TPM_PROPERTIES,
TPM_PT_MAX_RESPONSE_SIZE, response, 1, false);
- if (ret)
return -1;
- /*
* On the definition of TPMS_TAGGED_PROPERTY Structure
* property: u32
* value: u32
* So advance the response for TPM_CAP_TPM_PROPERTIES by sizeof(u32)
* to get the value
*/
- *max_response_size = (uint16_t)get_unaligned_be32(response + sizeof(u32));
- return 0;
+}
+/**
- tpm2_get_manufacturer_id() - Issue a TPM2 command to get the manufacturer ID
- @dev: TPM device
- @manufacturer_id: output buffer for the id
- Return: 0 on success -1 on error
- */
+static int tpm2_get_manufacturer_id(struct udevice *dev, u32 *manufacturer_id) +{
- u8 response[COMMAND_BUFFER_SIZE];
- u32 ret;
- ret = tpm2_get_capability(dev, TPM_CAP_TPM_PROPERTIES,
TPM_PT_MANUFACTURER, response, 1, false);
- if (ret)
return -1;
- *manufacturer_id = get_unaligned_be32(response + sizeof(u32));
- return 0;
+}
+/**
- is_active_pcr() - Check if an supported algorithm is active
%s/an supported/a supported/
Ok
- @dev: TPM device
- @selection: struct of PCR information
- Return: true if PCR is active
- */
+bool is_active_pcr(struct tpms_pcr_selection *selection) +{
- int i;
- /*
* check the pcr_select. If at least one of the PCRs supports the algorithm
* add it on the active ones
*/
- for (i = 0; i < selection->size_of_select; i++) {
if (selection->pcr_select[i])
return true;
- }
- return false;
+}
+/**
- tpm2_get_pcr_info() - Issue a TPM2 command to get the supported, active PCRs and number of banks
- @dev: TPM device
- @supported_pcr: bitmask with the algorithms supported
- @active_pcr: bitmask with the active algorithms
- @pcr_banks: number of PCR banks
- Return: 0 on success -1 on error
- */
+static int tpm2_get_pcr_info(struct udevice *dev, u32 *supported_pcr, u32 *active_pcr,
u32 *pcr_banks)
+{
- u8 response[COMMAND_BUFFER_SIZE];
- struct tpml_pcr_selection pcrs;
- u32 ret;
- int i;
- ret = tpm2_get_capability(dev, TPM_CAP_PCRS, 0, response, 1, true);
- if (ret)
return -1;
- pcrs.count = get_unaligned_be32(response);
- if (pcrs.count > MAX_HASH_COUNT || pcrs.count < 1)
return -1;
- for (i = 0; i < pcrs.count; i++) {
/*
* Definition of TPMS_PCR_SELECTION Structure
* hash: u16
* size_of_select: u8
* pcr_select: u8 array
*/
u32 hash_offset = sizeof(pcrs.count) + i * sizeof(pcrs.selection[0]);
u32 size_select_offset =
hash_offset + offsetof(struct tpms_pcr_selection, size_of_select);
u32 pcr_select_offset =
hash_offset + offsetof(struct tpms_pcr_selection, pcr_select);
pcrs.selection[i].hash = get_unaligned_be16(response + hash_offset);
pcrs.selection[i].size_of_select =
__get_unaligned_be(response + size_select_offset);
/* copy the array of pcr_select */
memcpy(pcrs.selection[i].pcr_select, response + pcr_select_offset,
pcrs.selection[i].size_of_select);
- }
- for (i = 0; i < pcrs.count; i++) {
switch (pcrs.selection[i].hash) {
case TPM2_ALG_SHA1:
*supported_pcr |= EFI_TCG2_BOOT_HASH_ALG_SHA1;
if (is_active_pcr(&pcrs.selection[i]))
*active_pcr |= EFI_TCG2_BOOT_HASH_ALG_SHA1;
break;
case TPM2_ALG_SHA256:
*supported_pcr |= EFI_TCG2_BOOT_HASH_ALG_SHA256;
if (is_active_pcr(&pcrs.selection[i]))
*active_pcr |= EFI_TCG2_BOOT_HASH_ALG_SHA256;
break;
case TPM2_ALG_SHA384:
*supported_pcr |= EFI_TCG2_BOOT_HASH_ALG_SHA384;
if (is_active_pcr(&pcrs.selection[i]))
*active_pcr |= EFI_TCG2_BOOT_HASH_ALG_SHA384;
break;
case TPM2_ALG_SHA512:
*supported_pcr |= EFI_TCG2_BOOT_HASH_ALG_SHA512;
if (is_active_pcr(&pcrs.selection[i]))
*active_pcr |= EFI_TCG2_BOOT_HASH_ALG_SHA512;
break;
case TPM2_ALG_SM3_256:
*supported_pcr |= EFI_TCG2_BOOT_HASH_ALG_SM3_256;
if (is_active_pcr(&pcrs.selection[i]))
*active_pcr |= EFI_TCG2_BOOT_HASH_ALG_SM3_256;
break;
default:
log_err("Unknown algorithm %x\n", pcrs.selection[i].hash);
You do not want to mess up the screen output of an EFI application.
Please, use EFI_PRINT().
Will do
break;
}
- }
- *pcr_banks = pcrs.count;
- return 0;
+}
+/**
- get_capability() - provide protocol capability information and state information
- @this: TCG2 protocol instance
- @capability: caller allocated memory with size field to the size of the
structure allocated
- Return: status code
- */
+static efi_status_t EFIAPI +get_capability(struct efi_tcg2_protocol *this,
struct efi_tcg2_boot_service_capability *capability)
+{
- struct udevice *dev;
- int ret;
- EFI_ENTRY("%p, %p", this, capability);
- if (!this || !capability)
return EFI_EXIT(EFI_INVALID_PARAMETER);
- if (capability->size < boot_service_capability_min) {
capability->size = boot_service_capability_min;
return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
- }
- if (capability->size < sizeof(*capability)) {
capability->size = sizeof(*capability);
return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
- }
- capability->structure_version.major = 1;
- capability->structure_version.minor = 1;
- capability->protocol_version.major = 1;
- capability->protocol_version.minor = 1;
- ret = platform_get_tpm2_device(&dev);
The return value is of type efi_uintn_t and not int.
Yep, missed that, thanks!
- if (ret != EFI_SUCCESS) {
capability->supported_event_logs = 0;
capability->hash_algorithm_bitmap = 0;
capability->tpm_present_flag = false;
capability->max_command_size = 0;
capability->max_response_size = 0;
capability->manufacturer_id = 0;
capability->number_of_pcr_banks = 0;
capability->active_pcr_banks = 0;
Why don't you simply return EFI_DEVICE_ERROR if there is no device?
So actually I would write:
if (ret != EFI_SUCCESS) goto out;
Where out: is the central exit point.
Because that's what the TPM TCG2 spec says I should do, if a device is not present [1]
out: EFI_EXIT(ret);
- .get_result_of_set_active_pcr_banks = get_result_of_set_active_pcr_banks,
[...]
+};
+/**
- efi_tcg2_register() - register EFI_TCG2_PROTOCOL
- If a TPM2 device is available, the TPM TCG2 Protocol is registered
- Return: An error status is only returned if adding the protocol fails.
- */
+efi_status_t efi_tcg2_register(void) +{
- int ret;
efi_unintn_t ret;
Yep, I'll fix it for v2
Best regards
Heinrich
- struct udevice *dev;
- enum tpm_version tpm_ver;
- ret = platform_get_tpm2_device(&dev);
The return value is of type efi_uintn_t (aka size_t).
- if (ret != EFI_SUCCESS)
return EFI_SUCCESS;
- tpm_ver = tpm_get_version(dev);
- if (tpm_ver != TPM_V2) {
log_warning("Only TPMv2 supported for EFI_TCG2_PROTOCOL");
return EFI_SUCCESS;
- }
- ret = efi_add_protocol(efi_root, &efi_guid_tcg2_protocol,
(void *)&efi_tcg2_protocol);
- if (ret != EFI_SUCCESS)
log_err("Cannot install EFI_TCG2_PROTOCOL");
- return ret;
+}
[1] https://trustedcomputinggroup.org/wp-content/uploads/EFI-Protocol-Specificat... Section 6.4.4
Regards /Ilias

Hi Heinrich
[...]
Why are we using COMMAND_BUFFER_SIZE throughout the TPM code if the required buffer size for commands and responses can be read from the TPM device?
I think the logic is that 256b is enough for the basic commands we needed. I can change that here. Get the TPM response during efi_tcg2_register() and use that for the rest of the code?
A clarification is needed here, which I forgot on my initial response. The tpm library in U-boot is using the same buffer and length. That's the reason I used the same response buffer size. The reply is copied from the internal buffer defined in tpm2_get_capability() to our response buffer. So unless we change the TPM internals changing the EFI part will make no difference. That being said I don't mind changing the EFI code since it will be future-proof, against the tpm code changes. Thoughts?
Regards /Ilias

On 11/4/20 10:06 PM, Ilias Apalodimas wrote:
Hi Heinrich
[...]
Why are we using COMMAND_BUFFER_SIZE throughout the TPM code if the required buffer size for commands and responses can be read from the TPM device?
I think the logic is that 256b is enough for the basic commands we needed. I can change that here. Get the TPM response during efi_tcg2_register() and use that for the rest of the code?
A clarification is needed here, which I forgot on my initial response. The tpm library in U-boot is using the same buffer and length. That's the reason I used the same response buffer size. The reply is copied from the internal buffer defined in tpm2_get_capability() to our response buffer. So unless we change the TPM internals changing the EFI part will make no difference. That being said I don't mind changing the EFI code since it will be future-proof, against the tpm code changes. Thoughts?
My question was not about EFI, it was about the lib/tpm-v2.c's usage of COMMAND_BUFFER_SIZE which would only be adequate if the specification explicitly forbids to implement a TPM with larger responses.
Types like TPML_PCR_SELECTION contain arrays. Why should we assume that these arrays fit into 256 bytes?
Should the logic in lib/tpm-v2.c be wrong, this needs to be fixed before building an EFI protocol upon it.
Best regards
Heinrich

On Wed, Nov 04, 2020 at 11:09:22PM +0100, Heinrich Schuchardt wrote:
On 11/4/20 10:06 PM, Ilias Apalodimas wrote:
Hi Heinrich
[...]
Why are we using COMMAND_BUFFER_SIZE throughout the TPM code if the required buffer size for commands and responses can be read from the TPM device?
I think the logic is that 256b is enough for the basic commands we needed. I can change that here. Get the TPM response during efi_tcg2_register() and use that for the rest of the code?
A clarification is needed here, which I forgot on my initial response. The tpm library in U-boot is using the same buffer and length. That's the reason I used the same response buffer size. The reply is copied from the internal buffer defined in tpm2_get_capability() to our response buffer. So unless we change the TPM internals changing the EFI part will make no difference. That being said I don't mind changing the EFI code since it will be future-proof, against the tpm code changes. Thoughts?
My question was not about EFI, it was about the lib/tpm-v2.c's usage of COMMAND_BUFFER_SIZE which would only be adequate if the specification explicitly forbids to implement a TPM with larger responses.
Right, that goes back to my initial response then. People that contributed the TPM code are cc'ed so they might have a better answer, I'll just repeat myself here. I think the 256b was choosen because it was enough for the basic operations that were implemented.
Types like TPML_PCR_SELECTION contain arrays. Why should we assume that these arrays fit into 256 bytes?
Well in practice they did. The spec used to say TPM2_NUM_PCR_BANKS = 3, so those specific responses were meant to fit in there. The struct is: typedef struct { UINT32 count; TPMS_PCR_SELECTION pcrSelections[TPM2_NUM_PCR_BANKS]; } TPML_PCR_SELECTION;
As far as I understand this is going to change with a new revision and TPM2_NUM_PCR_BANKS will be 16 since newer hardware supports more PCR banks.
Should the logic in lib/tpm-v2.c be wrong, this needs to be fixed before building an EFI protocol upon it.
Why? The functionality it offsers is working *today*. As long as we make sure the added EFI protocol can use a configurable response buffer, acquired by the hardware we can go back and fix lib/tpm-v2.c when we need to. No?
Regards /Ilias
Best regards
Heinrich

Hi Ilias,
On Wed, 4 Nov 2020 at 06:48, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Since U-boot EFI implementation is getting richer it makes sense to add support for EFI_TCG2_PROTOCOL taking advantage of any hardware TPM available on the device.
This is the initial implementation of the protocol which only adds support for GetCapability(). It's limited in the newer and safer TPMv2 devices.
Signed-off-by: Ilias Apalodimas ilias.apalodimas@linaro.org
The protocol requires mode that GetCapability to be usable. I intend to add support for GetEventLog() and HashLogExtendEvent() once this gets reviewed/merged include/efi_loader.h | 2 + include/efi_tcg2.h | 91 ++++++++ include/tpm-v2.h | 48 ++++ lib/efi_loader/Kconfig | 8 + lib/efi_loader/Makefile | 1 + lib/efi_loader/efi_setup.c | 7 + lib/efi_loader/efi_tcg2.c | 460 +++++++++++++++++++++++++++++++++++++ 7 files changed, 617 insertions(+) create mode 100644 include/efi_tcg2.h create mode 100644 lib/efi_loader/efi_tcg2.c
How can we add tests for this? We have a basic TPM emulator available so perhaps it could be used to create a sandbox test?
Regards, Simon

Hi Simon,
On Wed, Nov 04, 2020 at 11:08:42AM -0700, Simon Glass wrote:
Hi Ilias,
On Wed, 4 Nov 2020 at 06:48, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Since U-boot EFI implementation is getting richer it makes sense to add support for EFI_TCG2_PROTOCOL taking advantage of any hardware TPM available on the device.
This is the initial implementation of the protocol which only adds support for GetCapability(). It's limited in the newer and safer TPMv2 devices.
Signed-off-by: Ilias Apalodimas ilias.apalodimas@linaro.org
The protocol requires mode that GetCapability to be usable. I intend to add support for GetEventLog() and HashLogExtendEvent() once this gets reviewed/merged include/efi_loader.h | 2 + include/efi_tcg2.h | 91 ++++++++ include/tpm-v2.h | 48 ++++ lib/efi_loader/Kconfig | 8 + lib/efi_loader/Makefile | 1 + lib/efi_loader/efi_setup.c | 7 + lib/efi_loader/efi_tcg2.c | 460 +++++++++++++++++++++++++++++++++++++ 7 files changed, 617 insertions(+) create mode 100644 include/efi_tcg2.h create mode 100644 lib/efi_loader/efi_tcg2.c
How can we add tests for this? We have a basic TPM emulator available so perhaps it could be used to create a sandbox test?
I assume you refer to drivers/tpm/tpm2_tis_sandbox.c right? I did check this before posting but it only supports TPM_CAP_TPM_PROPERTIES(0x6). The GetCapability() also uses TPM_CAP_PCRS(0x5). I don't really know if it's worth extending that, since the patches that will follow implementing GetEventLog() and HashLogExtendEvent() are a lot more demanding on the TPM.
Maybe look into some software TPM? On my side I tested this on an armv8 with fTPM and and EFI application [1]
[1] https://github.com/apalos/efi-tpm2-utils
Regards /Ilias
Regards, Simon

Hi Ilias,
On Wed, 4 Nov 2020 at 11:52, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Simon,
On Wed, Nov 04, 2020 at 11:08:42AM -0700, Simon Glass wrote:
Hi Ilias,
On Wed, 4 Nov 2020 at 06:48, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Since U-boot EFI implementation is getting richer it makes sense to add support for EFI_TCG2_PROTOCOL taking advantage of any hardware TPM available on the device.
This is the initial implementation of the protocol which only adds support for GetCapability(). It's limited in the newer and safer TPMv2 devices.
Signed-off-by: Ilias Apalodimas ilias.apalodimas@linaro.org
The protocol requires mode that GetCapability to be usable. I intend to add support for GetEventLog() and HashLogExtendEvent() once this gets reviewed/merged include/efi_loader.h | 2 + include/efi_tcg2.h | 91 ++++++++ include/tpm-v2.h | 48 ++++ lib/efi_loader/Kconfig | 8 + lib/efi_loader/Makefile | 1 + lib/efi_loader/efi_setup.c | 7 + lib/efi_loader/efi_tcg2.c | 460 +++++++++++++++++++++++++++++++++++++ 7 files changed, 617 insertions(+) create mode 100644 include/efi_tcg2.h create mode 100644 lib/efi_loader/efi_tcg2.c
How can we add tests for this? We have a basic TPM emulator available so perhaps it could be used to create a sandbox test?
I assume you refer to drivers/tpm/tpm2_tis_sandbox.c right? I did check this before posting but it only supports TPM_CAP_TPM_PROPERTIES(0x6). The GetCapability() also uses TPM_CAP_PCRS(0x5). I don't really know if it's worth extending that, since the patches that will follow implementing GetEventLog() and HashLogExtendEvent() are a lot more demanding on the TPM.
The benefit is that we get fast unit tests for the code in U-Boot.
Maybe look into some software TPM?
The things we use are not that complicated. I think bringing in something simple would be OK, but it needs to just cover what we need.
On my side I tested this on an armv8 with fTPM and and EFI application [1]
We can probably put some of that code in U-Boot if you are amenable. Heinrich has added tests for most/all of the U-Boot EFI functionality.
Regards, Simon

On Wed, Nov 04, 2020 at 03:02:06PM -0700, Simon Glass wrote:
Hi Ilias,
On Wed, 4 Nov 2020 at 11:52, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Simon,
On Wed, Nov 04, 2020 at 11:08:42AM -0700, Simon Glass wrote:
Hi Ilias,
On Wed, 4 Nov 2020 at 06:48, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Since U-boot EFI implementation is getting richer it makes sense to add support for EFI_TCG2_PROTOCOL taking advantage of any hardware TPM available on the device.
This is the initial implementation of the protocol which only adds support for GetCapability(). It's limited in the newer and safer TPMv2 devices.
Signed-off-by: Ilias Apalodimas ilias.apalodimas@linaro.org
The protocol requires mode that GetCapability to be usable. I intend to add support for GetEventLog() and HashLogExtendEvent() once this gets reviewed/merged include/efi_loader.h | 2 + include/efi_tcg2.h | 91 ++++++++ include/tpm-v2.h | 48 ++++ lib/efi_loader/Kconfig | 8 + lib/efi_loader/Makefile | 1 + lib/efi_loader/efi_setup.c | 7 + lib/efi_loader/efi_tcg2.c | 460 +++++++++++++++++++++++++++++++++++++ 7 files changed, 617 insertions(+) create mode 100644 include/efi_tcg2.h create mode 100644 lib/efi_loader/efi_tcg2.c
How can we add tests for this? We have a basic TPM emulator available so perhaps it could be used to create a sandbox test?
I assume you refer to drivers/tpm/tpm2_tis_sandbox.c right? I did check this before posting but it only supports TPM_CAP_TPM_PROPERTIES(0x6). The GetCapability() also uses TPM_CAP_PCRS(0x5). I don't really know if it's worth extending that, since the patches that will follow implementing GetEventLog() and HashLogExtendEvent() are a lot more demanding on the TPM.
The benefit is that we get fast unit tests for the code in U-Boot.
Maybe look into some software TPM?
The things we use are not that complicated. I think bringing in something simple would be OK, but it needs to just cover what we need.
Sure. Let me check tpm2_tis_sandbox.c a bit more before we go ahead exploring other posibilities and see how far we can get.
An alternative over here would be to use QEMU + OP-TEE + fTPM once and if QEMU gets an RPMB emulation available (needed for fTPM) or QEMU with softwareTPM. I think the latter is easier and not strictly bound to Arm architecture.
On my side I tested this on an armv8 with fTPM and and EFI application [1]
We can probably put some of that code in U-Boot if you are amenable. Heinrich has added tests for most/all of the U-Boot EFI functionality.
That repo is not my code. I just fixed the arm64 compilation and used it during my development. If the licence permits it, we can indeed use some of the code in our selftests.
Regards /Ilias

On 04.11.20 14:47, Ilias Apalodimas wrote:
A following patch introduces EFI_TCG2_PROTOCOL. One of the functions of that protocol is GetCapability(). In order to parse device capabilities we need to access a u32 before the properties which the current implementation ignores while reading device properties.
So let's make the response length configurable and prepare the functions for EFI_TCG2_PROTOCOL.
Signed-off-by: Ilias Apalodimas ilias.apalodimas@linaro.org
cmd/tpm-v2.c | 2 +- include/tpm-v2.h | 12 +++++++----- lib/tpm-v2.c | 10 +++++++--- 3 files changed, 15 insertions(+), 9 deletions(-)
diff --git a/cmd/tpm-v2.c b/cmd/tpm-v2.c index e6742656f578..c2df1c34043a 100644 --- a/cmd/tpm-v2.c +++ b/cmd/tpm-v2.c @@ -183,7 +183,7 @@ static int do_tpm_get_capability(struct cmd_tbl *cmdtp, int flag, int argc, data = map_sysmem(simple_strtoul(argv[3], NULL, 0), 0); count = simple_strtoul(argv[4], NULL, 0);
- rc = tpm2_get_capability(dev, capability, property, data, count);
- rc = tpm2_get_capability(dev, capability, property, data, count, false); if (rc) goto unmap_data;
diff --git a/include/tpm-v2.h b/include/tpm-v2.h index f6c045d35480..ee74028ca83b 100644 --- a/include/tpm-v2.h +++ b/include/tpm-v2.h @@ -257,15 +257,17 @@ u32 tpm2_pcr_read(struct udevice *dev, u32 idx, unsigned int idx_min_sz,
- to query property index that is 4-byte wide.
- @dev TPM device
- @capability Partition of capabilities
- @property Further definition of capability, limited to be 4 bytes wide
- @buf Output buffer for capability information
- @prop_count Size of output buffer
- @capability Partition of capabilities
- @property Further definition of capability, limited to be 4 bytes
wide
- @buf Output buffer for capability information
- @prop_count Size of output buffer
*/
- @get_count Include tpmu property count
- @return code of the operation
u32 tpm2_get_capability(struct udevice *dev, u32 capability, u32 property,
void *buf, size_t prop_count);
void *buf, size_t prop_count, bool get_count);
/**
- Issue a TPM2_DictionaryAttackLockReset command.
diff --git a/lib/tpm-v2.c b/lib/tpm-v2.c index a4c352e3ef75..b58c1057995b 100644 --- a/lib/tpm-v2.c +++ b/lib/tpm-v2.c @@ -161,7 +161,7 @@ u32 tpm2_pcr_read(struct udevice *dev, u32 idx, unsigned int idx_min_sz, }
u32 tpm2_get_capability(struct udevice *dev, u32 capability, u32 property,
void *buf, size_t prop_count)
void *buf, size_t prop_count, bool get_count)
The implementation would be more stable if we would derive the offset from field property instead of adding get_count.
{ u8 command_v2[COMMAND_BUFFER_SIZE] = {
Shouldn't COMMAND_BUFFER_SIZE be changed to something with TPM in the name, e.g TPM_COMMAND_BUFFER_SIZE?
tpm_u16(TPM2_ST_NO_SESSIONS), /* TAG */
@@ -181,13 +181,17 @@ u32 tpm2_get_capability(struct udevice *dev, u32 capability, u32 property, if (ret) return ret;
- /* When reading PCR properties we need the count */
- properties_off = sizeof(u16) + sizeof(u32) + sizeof(u32) +
/*sizeof(u8) + sizeof(u32);
*/
- In the response buffer, the properties are located after the:
- tag (u16), response size (u32), response code (u32),
- YES/NO flag (u8), TPM_CAP (u32) and TPMU_CAPABILITIES (u32).
This comment should be above 'properties_off ='. 'get_count' related field should be mentioned.
Best regards
Heinrich
- properties_off = sizeof(u16) + sizeof(u32) + sizeof(u32) +
sizeof(u8) + sizeof(u32) + sizeof(u32);
if (!get_count)
properties_off += sizeof(u32);
memcpy(buf, &response[properties_off], response_len - properties_off);
return 0;

Hi Heinrich,
[...]
+++ b/lib/tpm-v2.c @@ -161,7 +161,7 @@ u32 tpm2_pcr_read(struct udevice *dev, u32 idx, unsigned int idx_min_sz, }
u32 tpm2_get_capability(struct udevice *dev, u32 capability, u32 property,
void *buf, size_t prop_count)
void *buf, size_t prop_count, bool get_count)
The implementation would be more stable if we would derive the offset from field property instead of adding get_count.
We are not defining the tpmv2 internal structures anywhere in U-Boot. That's why the code is doing static sizeof(uX) instead of using offsetof. In the EFI part of the patchset, I've done exaclty that. Working with offsets on well defined struct is better, but out of scope for this patchset imho. We can look into refactoring the generic tpmv2 code once I add the rest of the EFI protocol parts?
{ u8 command_v2[COMMAND_BUFFER_SIZE] = {
Shouldn't COMMAND_BUFFER_SIZE be changed to something with TPM in the name, e.g TPM_COMMAND_BUFFER_SIZE?
tpm_u16(TPM2_ST_NO_SESSIONS), /* TAG */
@@ -181,13 +181,17 @@ u32 tpm2_get_capability(struct udevice *dev, u32 capability, u32 property, if (ret) return ret;
- /* When reading PCR properties we need the count */
- properties_off = sizeof(u16) + sizeof(u32) + sizeof(u32) +
/*sizeof(u8) + sizeof(u32);
*/
- In the response buffer, the properties are located after the:
- tag (u16), response size (u32), response code (u32),
- YES/NO flag (u8), TPM_CAP (u32) and TPMU_CAPABILITIES (u32).
This comment should be above 'properties_off ='. 'get_count' related field should be mentioned.
Sure, I'll fix this
Regards /Ilias
Best regards
Heinrich
- properties_off = sizeof(u16) + sizeof(u32) + sizeof(u32) +
sizeof(u8) + sizeof(u32) + sizeof(u32);
if (!get_count)
properties_off += sizeof(u32);
memcpy(buf, &response[properties_off], response_len - properties_off);
return 0;

On 04.11.20 16:56, Ilias Apalodimas wrote:
Hi Heinrich,
[...]
+++ b/lib/tpm-v2.c @@ -161,7 +161,7 @@ u32 tpm2_pcr_read(struct udevice *dev, u32 idx, unsigned int idx_min_sz, }
u32 tpm2_get_capability(struct udevice *dev, u32 capability, u32 property,
void *buf, size_t prop_count)
void *buf, size_t prop_count, bool get_count)
The implementation would be more stable if we would derive the offset from field property instead of adding get_count.
We are not defining the tpmv2 internal structures anywhere in U-Boot. That's why the code is doing static sizeof(uX) instead of using offsetof. In the EFI part of the patchset, I've done exaclty that. Working with offsets on well defined struct is better, but out of scope for this patchset imho. We can look into refactoring the generic tpmv2 code once I add the rest of the EFI protocol parts?
Can't we add the structures that we need to tpm-v2.h and use their size here?
Best regards
Heinrich
{ u8 command_v2[COMMAND_BUFFER_SIZE] = {
Shouldn't COMMAND_BUFFER_SIZE be changed to something with TPM in the name, e.g TPM_COMMAND_BUFFER_SIZE?
tpm_u16(TPM2_ST_NO_SESSIONS), /* TAG */
@@ -181,13 +181,17 @@ u32 tpm2_get_capability(struct udevice *dev, u32 capability, u32 property, if (ret) return ret;
- /* When reading PCR properties we need the count */
- properties_off = sizeof(u16) + sizeof(u32) + sizeof(u32) +
/*sizeof(u8) + sizeof(u32);
*/
- In the response buffer, the properties are located after the:
- tag (u16), response size (u32), response code (u32),
- YES/NO flag (u8), TPM_CAP (u32) and TPMU_CAPABILITIES (u32).
This comment should be above 'properties_off ='. 'get_count' related field should be mentioned.
Sure, I'll fix this
Regards /Ilias
Best regards
Heinrich
- properties_off = sizeof(u16) + sizeof(u32) + sizeof(u32) +
sizeof(u8) + sizeof(u32) + sizeof(u32);
if (!get_count)
properties_off += sizeof(u32);
memcpy(buf, &response[properties_off], response_len - properties_off);
return 0;

Hi Heinrich,
On Wed, Nov 04, 2020 at 05:50:16PM +0100, Heinrich Schuchardt wrote:
On 04.11.20 16:56, Ilias Apalodimas wrote:
u32 tpm2_get_capability(struct udevice *dev, u32 capability, u32 property,
void *buf, size_t prop_count)
void *buf, size_t prop_count, bool get_count)
The implementation would be more stable if we would derive the offset from field property instead of adding get_count.
We are not defining the tpmv2 internal structures anywhere in U-Boot. That's why the code is doing static sizeof(uX) instead of using offsetof. In the EFI part of the patchset, I've done exaclty that. Working with offsets on well defined struct is better, but out of scope for this patchset imho. We can look into refactoring the generic tpmv2 code once I add the rest of the EFI protocol parts?
Can't we add the structures that we need to tpm-v2.h and use their size here?
Yes we can, but the scope of the patch is adding EFI_TCG2_PROTOCOL, not re-factor the tpmv2 code along the way. Since the only change on the existing code to support the functionality needed for the EFI_TCG2_PROTOCOL protocol is just 4 bytes on an existing buffer, can we stick to that and think of refactoring the tpm stuff afterwards?
Regards /Ilias
Best regards
Heinrich
{ u8 command_v2[COMMAND_BUFFER_SIZE] = {
Shouldn't COMMAND_BUFFER_SIZE be changed to something with TPM in the name, e.g TPM_COMMAND_BUFFER_SIZE?
tpm_u16(TPM2_ST_NO_SESSIONS), /* TAG */
@@ -181,13 +181,17 @@ u32 tpm2_get_capability(struct udevice *dev, u32 capability, u32 property, if (ret) return ret;
- /* When reading PCR properties we need the count */
- properties_off = sizeof(u16) + sizeof(u32) + sizeof(u32) +
/*sizeof(u8) + sizeof(u32);
*/
- In the response buffer, the properties are located after the:
- tag (u16), response size (u32), response code (u32),
- YES/NO flag (u8), TPM_CAP (u32) and TPMU_CAPABILITIES (u32).
This comment should be above 'properties_off ='. 'get_count' related field should be mentioned.
Sure, I'll fix this
Regards /Ilias
Best regards
Heinrich
- properties_off = sizeof(u16) + sizeof(u32) + sizeof(u32) +
sizeof(u8) + sizeof(u32) + sizeof(u32);
if (!get_count)
properties_off += sizeof(u32);
memcpy(buf, &response[properties_off], response_len - properties_off);
return 0;

On 11/4/20 6:26 PM, Ilias Apalodimas wrote:
Hi Heinrich,
On Wed, Nov 04, 2020 at 05:50:16PM +0100, Heinrich Schuchardt wrote:
On 04.11.20 16:56, Ilias Apalodimas wrote:
u32 tpm2_get_capability(struct udevice *dev, u32 capability, u32 property,
void *buf, size_t prop_count)
void *buf, size_t prop_count, bool get_count)
The implementation would be more stable if we would derive the offset from field property instead of adding get_count.
We are not defining the tpmv2 internal structures anywhere in U-Boot. That's why the code is doing static sizeof(uX) instead of using offsetof. In the EFI part of the patchset, I've done exaclty that. Working with offsets on well defined struct is better, but out of scope for this patchset imho. We can look into refactoring the generic tpmv2 code once I add the rest of the EFI protocol parts?
Can't we add the structures that we need to tpm-v2.h and use their size here?
Yes we can, but the scope of the patch is adding EFI_TCG2_PROTOCOL, not re-factor the tpmv2 code along the way. Since the only change on the existing code to support the functionality needed for the EFI_TCG2_PROTOCOL protocol is just 4 bytes on an existing buffer, can we stick to that and think of refactoring the tpm stuff afterwards?
You expect a TPML_PCR_SELECTION when capabilities is TPM_CAP_PCRS.
typedef struct { UINT32 count; TPMS_PCR_SELECTION pcrSelections[TPM2_NUM_PCR_BANKS]; } TPML_PCR_SELECTION;
Why do you have to skip over the UINT32 here?
You just have to define this one structure in preparation of patch 2 and then you can correctly parse the response in your implementation of the EFI protocol.
I suggest not to change tpm2_get_capability() at all.
Best regards
Heinrich
Regards /Ilias
Best regards
Heinrich
{ u8 command_v2[COMMAND_BUFFER_SIZE] = {
Shouldn't COMMAND_BUFFER_SIZE be changed to something with TPM in the name, e.g TPM_COMMAND_BUFFER_SIZE?
tpm_u16(TPM2_ST_NO_SESSIONS), /* TAG */
@@ -181,13 +181,17 @@ u32 tpm2_get_capability(struct udevice *dev, u32 capability, u32 property, if (ret) return ret;
- /* When reading PCR properties we need the count */
- properties_off = sizeof(u16) + sizeof(u32) + sizeof(u32) +
/*sizeof(u8) + sizeof(u32);
*/
- In the response buffer, the properties are located after the:
- tag (u16), response size (u32), response code (u32),
- YES/NO flag (u8), TPM_CAP (u32) and TPMU_CAPABILITIES (u32).
This comment should be above 'properties_off ='. 'get_count' related field should be mentioned.
Sure, I'll fix this
Regards /Ilias
Best regards
Heinrich
- properties_off = sizeof(u16) + sizeof(u32) + sizeof(u32) +
sizeof(u8) + sizeof(u32) + sizeof(u32);
if (!get_count)
properties_off += sizeof(u32);
memcpy(buf, &response[properties_off], response_len - properties_off);
return 0;

On Wed, Nov 04, 2020 at 10:59:03PM +0100, Heinrich Schuchardt wrote:
On 11/4/20 6:26 PM, Ilias Apalodimas wrote:
Hi Heinrich,
On Wed, Nov 04, 2020 at 05:50:16PM +0100, Heinrich Schuchardt wrote:
On 04.11.20 16:56, Ilias Apalodimas wrote:
u32 tpm2_get_capability(struct udevice *dev, u32 capability, u32 property,
void *buf, size_t prop_count)
void *buf, size_t prop_count, bool get_count)
The implementation would be more stable if we would derive the offset from field property instead of adding get_count.
We are not defining the tpmv2 internal structures anywhere in U-Boot. That's why the code is doing static sizeof(uX) instead of using offsetof. In the EFI part of the patchset, I've done exaclty that. Working with offsets on well defined struct is better, but out of scope for this patchset imho. We can look into refactoring the generic tpmv2 code once I add the rest of the EFI protocol parts?
Can't we add the structures that we need to tpm-v2.h and use their size here?
Yes we can, but the scope of the patch is adding EFI_TCG2_PROTOCOL, not re-factor the tpmv2 code along the way. Since the only change on the existing code to support the functionality needed for the EFI_TCG2_PROTOCOL protocol is just 4 bytes on an existing buffer, can we stick to that and think of refactoring the tpm stuff afterwards?
You expect a TPML_PCR_SELECTION when capabilities is TPM_CAP_PCRS.
typedef struct { UINT32 count; TPMS_PCR_SELECTION pcrSelections[TPM2_NUM_PCR_BANKS]; } TPML_PCR_SELECTION;
Why do you have to skip over the UINT32 here?
You *don't* have to skip it since you need to implement GetCapabilty() and specifically the HashAlgorithmBitmap and ActivePcrBanks. That's what this patch is actually doing, preserve the u32 (corresponding to count).
You just have to define this one structure in preparation of patch 2 and then you can correctly parse the response in your implementation of the EFI protocol.
The struct is defined in this patchset, it's part of patch 2 and used to implement the EFI protocol.
I suggest not to change tpm2_get_capability() at all.
You can't.
The current code has this comment: " * In the response buffer, the properties are located after the: * tag (u16), response size (u32), response code (u32), * YES/NO flag (u8), TPM_CAP (u32) and TPMU_CAPABILITIES (u32)."
So unless I am missing something it's referring to: typedef struct { UINT32 count; TPMS_TAGGED_PROPERTY tpmProperty[MAX_TPM_PROPERTIES]; } TPML_TAGGED_TPM_PROPERTY;
So the last u32 that's removed from the buffer is the 'count', which it removes to access the tpmProperty directly.
Regards /Ilias
Best regards
Heinrich
Regards /Ilias
Best regards
Heinrich
{ u8 command_v2[COMMAND_BUFFER_SIZE] = {
Shouldn't COMMAND_BUFFER_SIZE be changed to something with TPM in the name, e.g TPM_COMMAND_BUFFER_SIZE?
tpm_u16(TPM2_ST_NO_SESSIONS), /* TAG */
@@ -181,13 +181,17 @@ u32 tpm2_get_capability(struct udevice *dev, u32 capability, u32 property, if (ret) return ret;
- /* When reading PCR properties we need the count */
- properties_off = sizeof(u16) + sizeof(u32) + sizeof(u32) +
/*sizeof(u8) + sizeof(u32);
*/
- In the response buffer, the properties are located after the:
- tag (u16), response size (u32), response code (u32),
- YES/NO flag (u8), TPM_CAP (u32) and TPMU_CAPABILITIES (u32).
This comment should be above 'properties_off ='. 'get_count' related field should be mentioned.
Sure, I'll fix this
Regards /Ilias
Best regards
Heinrich
- properties_off = sizeof(u16) + sizeof(u32) + sizeof(u32) +
sizeof(u8) + sizeof(u32) + sizeof(u32);
if (!get_count)
properties_off += sizeof(u32);
memcpy(buf, &response[properties_off], response_len - properties_off);
return 0;

On 11/5/20 1:22 AM, Ilias Apalodimas wrote:
On Wed, Nov 04, 2020 at 10:59:03PM +0100, Heinrich Schuchardt wrote:
On 11/4/20 6:26 PM, Ilias Apalodimas wrote:
Hi Heinrich,
On Wed, Nov 04, 2020 at 05:50:16PM +0100, Heinrich Schuchardt wrote:
On 04.11.20 16:56, Ilias Apalodimas wrote:
> > u32 tpm2_get_capability(struct udevice *dev, u32 capability, u32 property, > - void *buf, size_t prop_count) > + void *buf, size_t prop_count, bool get_count)
The implementation would be more stable if we would derive the offset from field property instead of adding get_count.
We are not defining the tpmv2 internal structures anywhere in U-Boot. That's why the code is doing static sizeof(uX) instead of using offsetof. In the EFI part of the patchset, I've done exaclty that. Working with offsets on well defined struct is better, but out of scope for this patchset imho. We can look into refactoring the generic tpmv2 code once I add the rest of the EFI protocol parts?
Can't we add the structures that we need to tpm-v2.h and use their size here?
Yes we can, but the scope of the patch is adding EFI_TCG2_PROTOCOL, not re-factor the tpmv2 code along the way. Since the only change on the existing code to support the functionality needed for the EFI_TCG2_PROTOCOL protocol is just 4 bytes on an existing buffer, can we stick to that and think of refactoring the tpm stuff afterwards?
You expect a TPML_PCR_SELECTION when capabilities is TPM_CAP_PCRS.
typedef struct { UINT32 count; TPMS_PCR_SELECTION pcrSelections[TPM2_NUM_PCR_BANKS]; } TPML_PCR_SELECTION;
Why do you have to skip over the UINT32 here?
You *don't* have to skip it since you need to implement GetCapabilty() and specifically the HashAlgorithmBitmap and ActivePcrBanks. That's what this patch is actually doing, preserve the u32 (corresponding to count).
You just have to define this one structure in preparation of patch 2 and then you can correctly parse the response in your implementation of the EFI protocol.
The struct is defined in this patchset, it's part of patch 2 and used to implement the EFI protocol.
I suggest not to change tpm2_get_capability() at all.
You can't.
The current code has this comment: " * In the response buffer, the properties are located after the:
- tag (u16), response size (u32), response code (u32),
- YES/NO flag (u8), TPM_CAP (u32) and TPMU_CAPABILITIES (u32)."
So unless I am missing something it's referring to: typedef struct { UINT32 count; TPMS_TAGGED_PROPERTY tpmProperty[MAX_TPM_PROPERTIES]; } TPML_TAGGED_TPM_PROPERTY;
So the last u32 that's removed from the buffer is the 'count', which it removes to access the tpmProperty directly.
do_tpm_get_capability() can request any combination of capability and property.
For most capabilities only a single value of property is allowable according to "Trusted Platform Module LibraryPart 3: Commands".
The return type depends on the capability. All relevant return types start with a u32 count field followed by an array of structures. The type of the structure depends on the capability.
The description of the 'tpm2 get_capabilities' command seems to be incorrect in this light:
"get_capability <capability> <property> <addr> <count>\n" " Read and display <count> entries indexed by <capability>/<property>.\n" " Values are 4 bytes long and are written at <addr>.\n" " <capability>: capability\n" " <property>: property\n
There are no 4 byte return values to be shown. Without the count field the hexdump cannot be interpreted as you will not know where random garbage in memory starts.
So can't we simply remove 4 bytes from the offset. Then do_tpm_get_capability() will show the count as the first 4 bytes which is necessary anyway to interpret the output. And you get the output you need for the EFI protocol.
do_tpm_get_capability() should better use print_hex_dump() for output instead of inventing its own formatting routine. In the long run the 'tpm2 get_capability' sub-command should be completely reworked to show structured output according to the capability being read.
In the spec there is a constant #define TPM2_MAX_CAP_BUFFER 1024 which is used to define TPM2_MAX_TPM_PROPERTIES and other maximum array sizes. So using COMMAND_BUFFER_SIZE=256 for the length of the response buffer seems to be wrong in patch 2/2.
Cf. TCG TSS2.0 Overview and Common Structures Specification, Version 0.90, Revision 03, January 4, 2018
Best regards
Heinrich
Regards /Ilias
Best regards
Heinrich
Regards /Ilias
Best regards
Heinrich
> { > u8 command_v2[COMMAND_BUFFER_SIZE] = {
Shouldn't COMMAND_BUFFER_SIZE be changed to something with TPM in the name, e.g TPM_COMMAND_BUFFER_SIZE?
> tpm_u16(TPM2_ST_NO_SESSIONS), /* TAG */ > @@ -181,13 +181,17 @@ u32 tpm2_get_capability(struct udevice *dev, u32 capability, u32 property, > if (ret) > return ret; > > + /* When reading PCR properties we need the count */ > + properties_off = sizeof(u16) + sizeof(u32) + sizeof(u32) + > + sizeof(u8) + sizeof(u32); > /* > * In the response buffer, the properties are located after the: > * tag (u16), response size (u32), response code (u32), > * YES/NO flag (u8), TPM_CAP (u32) and TPMU_CAPABILITIES (u32). > */
This comment should be above 'properties_off ='. 'get_count' related field should be mentioned.
Sure, I'll fix this
Regards /Ilias
Best regards
Heinrich
> - properties_off = sizeof(u16) + sizeof(u32) + sizeof(u32) + > - sizeof(u8) + sizeof(u32) + sizeof(u32); > + if (!get_count) > + properties_off += sizeof(u32); > + > memcpy(buf, &response[properties_off], response_len - properties_off); > > return 0; >

On Thu, Nov 05, 2020 at 03:27:15AM +0100, Heinrich Schuchardt wrote:
On 11/5/20 1:22 AM, Ilias Apalodimas wrote:
On Wed, Nov 04, 2020 at 10:59:03PM +0100, Heinrich Schuchardt wrote:
On 11/4/20 6:26 PM, Ilias Apalodimas wrote:
Hi Heinrich,
On Wed, Nov 04, 2020 at 05:50:16PM +0100, Heinrich Schuchardt wrote:
On 04.11.20 16:56, Ilias Apalodimas wrote:
>> >> u32 tpm2_get_capability(struct udevice *dev, u32 capability, u32 property, >> - void *buf, size_t prop_count) >> + void *buf, size_t prop_count, bool get_count) > > The implementation would be more stable if we would derive the offset > from field property instead of adding get_count. >
We are not defining the tpmv2 internal structures anywhere in U-Boot. That's why the code is doing static sizeof(uX) instead of using offsetof. In the EFI part of the patchset, I've done exaclty that. Working with offsets on well defined struct is better, but out of scope for this patchset imho. We can look into refactoring the generic tpmv2 code once I add the rest of the EFI protocol parts?
Can't we add the structures that we need to tpm-v2.h and use their size here?
Yes we can, but the scope of the patch is adding EFI_TCG2_PROTOCOL, not re-factor the tpmv2 code along the way. Since the only change on the existing code to support the functionality needed for the EFI_TCG2_PROTOCOL protocol is just 4 bytes on an existing buffer, can we stick to that and think of refactoring the tpm stuff afterwards?
You expect a TPML_PCR_SELECTION when capabilities is TPM_CAP_PCRS.
typedef struct { UINT32 count; TPMS_PCR_SELECTION pcrSelections[TPM2_NUM_PCR_BANKS]; } TPML_PCR_SELECTION;
Why do you have to skip over the UINT32 here?
You *don't* have to skip it since you need to implement GetCapabilty() and specifically the HashAlgorithmBitmap and ActivePcrBanks. That's what this patch is actually doing, preserve the u32 (corresponding to count).
You just have to define this one structure in preparation of patch 2 and then you can correctly parse the response in your implementation of the EFI protocol.
The struct is defined in this patchset, it's part of patch 2 and used to implement the EFI protocol.
I suggest not to change tpm2_get_capability() at all.
You can't.
The current code has this comment: " * In the response buffer, the properties are located after the:
- tag (u16), response size (u32), response code (u32),
- YES/NO flag (u8), TPM_CAP (u32) and TPMU_CAPABILITIES (u32)."
So unless I am missing something it's referring to: typedef struct { UINT32 count; TPMS_TAGGED_PROPERTY tpmProperty[MAX_TPM_PROPERTIES]; } TPML_TAGGED_TPM_PROPERTY;
So the last u32 that's removed from the buffer is the 'count', which it removes to access the tpmProperty directly.
do_tpm_get_capability() can request any combination of capability and property.
For most capabilities only a single value of property is allowable according to "Trusted Platform Module LibraryPart 3: Commands".
The return type depends on the capability. All relevant return types start with a u32 count field followed by an array of structures. The type of the structure depends on the capability.
Exactly
The description of the 'tpm2 get_capabilities' command seems to be incorrect in this light:
"get_capability <capability> <property> <addr> <count>\n" " Read and display <count> entries indexed by <capability>/<property>.\n" " Values are 4 bytes long and are written at <addr>.\n" " <capability>: capability\n" " <property>: property\n
There are no 4 byte return values to be shown. Without the count field the hexdump cannot be interpreted as you will not know where random garbage in memory starts.
So can't we simply remove 4 bytes from the offset. Then do_tpm_get_capability() will show the count as the first 4 bytes which is necessary anyway to interpret the output. And you get the output you need for the EFI protocol.
We can remove it. But since this is a patch for for an EFI protocol, I wanted to keep the non EFI changes to a minimum and that's what this patch does. Get the extra 4 bytes of the count when required, without having to rewrite anything else on the TPM libraries.
do_tpm_get_capability() should better use print_hex_dump() for output instead of inventing its own formatting routine. In the long run the 'tpm2 get_capability' sub-command should be completely reworked to show structured output according to the capability being read.
In the spec there is a constant #define TPM2_MAX_CAP_BUFFER 1024 which is used to define TPM2_MAX_TPM_PROPERTIES and other maximum array sizes. So using COMMAND_BUFFER_SIZE=256 for the length of the response buffer seems to be wrong in patch 2/2.
patch 2/2 uses it because the tpm2_get_capability() uses it. So since the latter is using to talk to the tpm and then copy that into the provided buffer, you'll never get more than 256 whatever you end up defining this in patch 2/2.
Regards /Ilias
Cf. TCG TSS2.0 Overview and Common Structures Specification, Version 0.90, Revision 03, January 4, 2018
Best regards
Heinrich
Regards /Ilias
Best regards
Heinrich
Regards /Ilias
Best regards
Heinrich
>> { >> u8 command_v2[COMMAND_BUFFER_SIZE] = { > > Shouldn't COMMAND_BUFFER_SIZE be changed to something with TPM in the > name, e.g TPM_COMMAND_BUFFER_SIZE? > >> tpm_u16(TPM2_ST_NO_SESSIONS), /* TAG */ >> @@ -181,13 +181,17 @@ u32 tpm2_get_capability(struct udevice *dev, u32 capability, u32 property, >> if (ret) >> return ret; >> >> + /* When reading PCR properties we need the count */ >> + properties_off = sizeof(u16) + sizeof(u32) + sizeof(u32) + >> + sizeof(u8) + sizeof(u32); >> /* >> * In the response buffer, the properties are located after the: >> * tag (u16), response size (u32), response code (u32), >> * YES/NO flag (u8), TPM_CAP (u32) and TPMU_CAPABILITIES (u32). >> */ > > This comment should be above 'properties_off ='. 'get_count' related > field should be mentioned.
Sure, I'll fix this
Regards /Ilias > > Best regards > > Heinrich > >> - properties_off = sizeof(u16) + sizeof(u32) + sizeof(u32) + >> - sizeof(u8) + sizeof(u32) + sizeof(u32); >> + if (!get_count) >> + properties_off += sizeof(u32); >> + >> memcpy(buf, &response[properties_off], response_len - properties_off); >> >> return 0; >> >
participants (3)
-
Heinrich Schuchardt
-
Ilias Apalodimas
-
Simon Glass