diff mbox series

[2/2,v4] efi: Add basic EFI_TCG2_PROTOCOL support

Message ID 20201111091812.400969-2-ilias.apalodimas@linaro.org
State Superseded, archived
Delegated to: Heinrich Schuchardt
Headers show
Series [1/2,v4] tpm: Add some headers from the spec | expand

Commit Message

Ilias Apalodimas Nov. 11, 2020, 9:18 a.m. UTC
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>
---
* changes since v3:
- added check for maximum number of PCRs allowed
- replaced multiple return Xl with goto out tags
* changes since v2:
- added description about include/efi_tcg2.h
- switch bool to u8 for tpm_present_flag
- removed superfluous 'default n' from Kconfig
- use 'goto 'tag' when possible

* changes since v1: 
- change return variable of platform_get_tpm2_device() when used
- since more headers were included in patch #2 use them in offset 
  calculations for all tpm commands
- change the size of the response buffer regardless of what 
  tpm2_get_capability() is doing
 include/efi_loader.h       |   2 +
 include/efi_tcg2.h         |  94 +++++++
 lib/efi_loader/Kconfig     |   7 +
 lib/efi_loader/Makefile    |   1 +
 lib/efi_loader/efi_setup.c |   7 +
 lib/efi_loader/efi_tcg2.c  | 539 +++++++++++++++++++++++++++++++++++++
 6 files changed, 650 insertions(+)
 create mode 100644 include/efi_tcg2.h
 create mode 100644 lib/efi_loader/efi_tcg2.c

Comments

Simon Glass Nov. 11, 2020, 2:42 p.m. UTC | #1
Hi Ilias,

On Wed, 11 Nov 2020 at 02:18, 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>
> ---
> * changes since v3:
> - added check for maximum number of PCRs allowed
> - replaced multiple return Xl with goto out tags
> * changes since v2:
> - added description about include/efi_tcg2.h
> - switch bool to u8 for tpm_present_flag
> - removed superfluous 'default n' from Kconfig
> - use 'goto 'tag' when possible
>
> * changes since v1:
> - change return variable of platform_get_tpm2_device() when used
> - since more headers were included in patch #2 use them in offset
>   calculations for all tpm commands
> - change the size of the response buffer regardless of what
>   tpm2_get_capability() is doing
>  include/efi_loader.h       |   2 +
>  include/efi_tcg2.h         |  94 +++++++
>  lib/efi_loader/Kconfig     |   7 +
>  lib/efi_loader/Makefile    |   1 +
>  lib/efi_loader/efi_setup.c |   7 +
>  lib/efi_loader/efi_tcg2.c  | 539 +++++++++++++++++++++++++++++++++++++
>  6 files changed, 650 insertions(+)
>  create mode 100644 include/efi_tcg2.h
>  create mode 100644 lib/efi_loader/efi_tcg2.c

I will let Heinrich review this one. I do feel that the overly long
identifiers make the code hard to read.

Regards,
Simon
Ilias Apalodimas Nov. 11, 2020, 2:46 p.m. UTC | #2
Hi Simon, 

On Wed, Nov 11, 2020 at 07:42:31AM -0700, Simon Glass wrote:
> Hi Ilias,
> 
> On Wed, 11 Nov 2020 at 02:18, 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>
> > ---
> > * changes since v3:
> > - added check for maximum number of PCRs allowed
> > - replaced multiple return Xl with goto out tags
> > * changes since v2:
> > - added description about include/efi_tcg2.h
> > - switch bool to u8 for tpm_present_flag
> > - removed superfluous 'default n' from Kconfig
> > - use 'goto 'tag' when possible
> >
> > * changes since v1:
> > - change return variable of platform_get_tpm2_device() when used
> > - since more headers were included in patch #2 use them in offset
> >   calculations for all tpm commands
> > - change the size of the response buffer regardless of what
> >   tpm2_get_capability() is doing
> >  include/efi_loader.h       |   2 +
> >  include/efi_tcg2.h         |  94 +++++++
> >  lib/efi_loader/Kconfig     |   7 +
> >  lib/efi_loader/Makefile    |   1 +
> >  lib/efi_loader/efi_setup.c |   7 +
> >  lib/efi_loader/efi_tcg2.c  | 539 +++++++++++++++++++++++++++++++++++++
> >  6 files changed, 650 insertions(+)
> >  create mode 100644 include/efi_tcg2.h
> >  create mode 100644 lib/efi_loader/efi_tcg2.c
> 
> I will let Heinrich review this one. I do feel that the overly long
> identifiers make the code hard to read.

I completely agree. The reason I kept them that long, is that the TCG specs
are quite confusing to follow, so I tried to adhere to the naming as much as 
possible.

Regards
/Ilias
> 
> Regards,
> Simon
Heinrich Schuchardt Nov. 12, 2020, 6:49 p.m. UTC | #3
On 11/11/20 10:18 AM, 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>
> ---
> * changes since v3:
> - added check for maximum number of PCRs allowed
> - replaced multiple return Xl with goto out tags
> * changes since v2:
> - added description about include/efi_tcg2.h
> - switch bool to u8 for tpm_present_flag
> - removed superfluous 'default n' from Kconfig
> - use 'goto 'tag' when possible
>
> * changes since v1:
> - change return variable of platform_get_tpm2_device() when used
> - since more headers were included in patch #2 use them in offset
>   calculations for all tpm commands
> - change the size of the response buffer regardless of what
>   tpm2_get_capability() is doing
>  include/efi_loader.h       |   2 +
>  include/efi_tcg2.h         |  94 +++++++
>  lib/efi_loader/Kconfig     |   7 +
>  lib/efi_loader/Makefile    |   1 +
>  lib/efi_loader/efi_setup.c |   7 +
>  lib/efi_loader/efi_tcg2.c  | 539 +++++++++++++++++++++++++++++++++++++
>  6 files changed, 650 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..4214f767eaba
> --- /dev/null
> +++ b/include/efi_tcg2.h
> @@ -0,0 +1,94 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Defines data structures and APIs that allow an OS to interact with UEFI
> + * firmware to query information about the device
> + *
> + * 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;
> +	u8 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/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> index 075481428cdf..29ea14b2ee2a 100644
> --- a/lib/efi_loader/Kconfig
> +++ b/lib/efi_loader/Kconfig
> @@ -184,6 +184,13 @@ 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
> +	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..d600cff66af1
> --- /dev/null
> +++ b/lib/efi_loader/efi_tcg2.c
> @@ -0,0 +1,539 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Defines APIs that allow an OS to interact with UEFI firmware to query
> + * information about the device.
> + * https://trustedcomputinggroup.org/resource/tcg-efi-protocol-specification/
> + *
> + * 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;
> +
> +/*
> + * When requesting TPM2_CAP_TPM_PROPERTIES the value is on a standard offset.
> + * Since the current tpm2_get_capability() response buffers starts at
> + * 'union tpmu_capabilities data' of 'struct tpms_capability_data', calculate
> + * the response size and offset once for all consumers
> + */
> +#define TPM2_RESPONSE_BUFFER_SIZE (sizeof(struct tpms_capability_data) - \
> +				   offsetof(struct tpms_capability_data, data))
> +#define properties_offset (offsetof(struct tpml_tagged_tpm_property, tpm_property) + \
> +			   offsetof(struct tpms_tagged_property, value))
> +
> +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);

'dm tree' on the sandbox shows:

tpm           0  [   ]   google_sandbox_tpm    |-- tpm
tpm           1  [   ]   sandbox_tpm2          |-- tpm2

You have to loop over all TPM devices and look for one supporting TPMv2
using tpm_get_version(dev).

> +	if (ret)
> +		return EFI_DEVICE_ERROR;

If none supports TPMv2, you should return EFI_NOT_FOUND.

> +
> +	*dev = devp;
> +
> +	return EFI_SUCCESS;
> +}
> +
> +/**
> + * tpm2_get_max_command_size() - 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[TPM2_RESPONSE_BUFFER_SIZE];
> +	u32 ret;
> +
> +	memset(response, 0, sizeof(response));
> +	ret = tpm2_get_capability(dev, TPM2_CAP_TPM_PROPERTIES,
> +				  TPM2_PT_MAX_COMMAND_SIZE, response, 1);
> +	if (ret)
> +		return -1;
> +
> +	*max_command_size = (uint16_t)get_unaligned_be32(response +
> +							 properties_offset);
> +
> +	return 0;
> +}
> +
> +/**
> + * tpm2_get_max_response_size() - 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[TPM2_RESPONSE_BUFFER_SIZE];
> +	u32 ret;
> +
> +	memset(response, 0, sizeof(response));
> +	ret = tpm2_get_capability(dev, TPM2_CAP_TPM_PROPERTIES,
> +				  TPM2_PT_MAX_RESPONSE_SIZE, response, 1);
> +	if (ret)
> +		return -1;
> +
> +	*max_response_size = (uint16_t)get_unaligned_be32(response +
> +							  properties_offset);
> +
> +	return 0;
> +}
> +
> +/**
> + * tpm2_get_manufacturer_id() - 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[TPM2_RESPONSE_BUFFER_SIZE];
> +	u32 ret;
> +
> +	memset(response, 0, sizeof(response));
> +	ret = tpm2_get_capability(dev, TPM2_CAP_TPM_PROPERTIES,
> +				  TPM2_PT_MANUFACTURER, response, 1);
> +	if (ret)
> +		return -1;
> +
> +	*manufacturer_id = get_unaligned_be32(response + properties_offset);
> +
> +	return 0;
> +}
> +
> +/**
> + * tpm2_get_num_pcr() - get the number of PCRs
> + *
> + * @dev:		TPM device
> + * @manufacturer_id:	output buffer for the number
> + *
> + * Return: 0 on success, -1 on error
> + */
> +static int tpm2_get_num_pcr(struct udevice *dev, u32 *num_pcr)
> +{
> +	u8 response[TPM2_RESPONSE_BUFFER_SIZE];
> +	u32 ret;
> +
> +	memset(response, 0, sizeof(response));
> +	ret = tpm2_get_capability(dev, TPM2_CAP_TPM_PROPERTIES,
> +				  TPM2_PT_PCR_COUNT, response, 1);
> +	if (ret)
> +		return -1;
> +
> +	*num_pcr = get_unaligned_be32(response + properties_offset);
> +	if (*num_pcr > TPM2_MAX_PCRS)
> +		return -1;
> +
> +	return 0;
> +}
> +
> +/**
> + * is_active_pcr() - Check if a 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() - 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[TPM2_RESPONSE_BUFFER_SIZE];
> +	struct tpml_pcr_selection pcrs;
> +	u32 ret, num_pcr;
> +	int i, tpm_ret;
> +
> +	memset(response, 0, sizeof(response));
> +	ret = tpm2_get_capability(dev, TPM2_CAP_PCRS, 0, response, 1);
> +	if (ret)
> +		goto out;
> +
> +	pcrs.count = get_unaligned_be32(response);
> +	/*
> +	 * We only support 5 algorithms for now so check against that
> +	 * instead of TPM2_NUM_PCR_BANKS
> +	 */
> +	if (pcrs.count > MAX_HASH_COUNT || pcrs.count < 1)
> +		goto out;
> +
> +	tpm_ret = tpm2_get_num_pcr(dev, &num_pcr);
> +	if (tpm_ret)
> +		goto out;
> +
> +	for (i = 0; i < pcrs.count; i++) {
> +		/*
> +		 * Definition of TPMS_PCR_SELECTION Structure
> +		 * hash: u16
> +		 * size_of_select: u8
> +		 * pcr_select: u8 array
> +		 *
> +		 * The offsets depend on the number of the device PCRs
> +		 * so we have to calculate them based on that
> +		 */
> +		u32 hash_offset = offsetof(struct tpml_pcr_selection, selection) +
> +			i * offsetof(struct tpms_pcr_selection, pcr_select) +
> +			i * ((num_pcr + 7) / 8);
> +		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);
> +		if (pcrs.selection[i].size_of_select > TPM2_PCR_SELECT_MAX)
> +			goto out;
> +		/* 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:
> +			EFI_PRINT("Unknown algorithm %x\n",
> +				  pcrs.selection[i].hash);
> +			break;
> +		}
> +	}
> +
> +	*pcr_banks = pcrs.count;
> +
> +	return 0;
> +out:
> +	return -1;
> +}
> +
> +/**
> + * get_capability() - 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;
> +	efi_status_t efi_ret;
> +	int ret;
> +
> +	EFI_ENTRY("%p, %p", this, capability);
> +
> +	if (!this || !capability) {
> +		efi_ret = EFI_INVALID_PARAMETER;
> +		goto out;
> +	}
> +
> +	if (capability->size < boot_service_capability_min) {
> +		capability->size = boot_service_capability_min;
> +		efi_ret = EFI_BUFFER_TOO_SMALL;
> +		goto out;
> +	}
> +
> +	if (capability->size < sizeof(*capability)) {
> +		capability->size = sizeof(*capability);
> +		efi_ret = EFI_BUFFER_TOO_SMALL;
> +		goto out;
> +	}
> +
> +	capability->structure_version.major = 1;
> +	capability->structure_version.minor = 1;
> +	capability->protocol_version.major = 1;
> +	capability->protocol_version.minor = 1;
> +
> +	efi_ret = platform_get_tpm2_device(&dev);
> +	if (efi_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;
> +
> +		efi_ret = EFI_SUCCESS;
> +		goto out;
> +	}
> +
> +	/* 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) {
> +		efi_ret = EFI_DEVICE_ERROR;
> +		goto out;
> +	}
> +
> +	/* Max command size */
> +	ret = tpm2_get_max_command_size(dev, &capability->max_command_size);
> +	if (ret) {
> +		efi_ret = EFI_DEVICE_ERROR;
> +		goto out;
> +	}
> +
> +	/* Max response size */
> +	ret = tpm2_get_max_response_size(dev, &capability->max_response_size);
> +	if (ret) {
> +		efi_ret = EFI_DEVICE_ERROR;
> +		goto out;
> +	}
> +
> +	/* Manufacturer ID */
> +	ret = tpm2_get_manufacturer_id(dev, &capability->manufacturer_id);
> +	if (ret) {
> +		efi_ret = EFI_DEVICE_ERROR;
> +		goto out;
> +	}
> +
> +	return EFI_EXIT(EFI_SUCCESS);
> +out:
> +	return EFI_EXIT(efi_ret);
> +}
> +
> +/**
> + * get_eventlog() - retrieve the the address of an 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 i
> + *				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)
> +{
> +	efi_status_t ret;
> +	struct udevice *dev;
> +	enum tpm_version tpm_ver;
> +
> +	ret = platform_get_tpm2_device(&dev);
> +	if (ret != EFI_SUCCESS)

Write a warning indicating that the device was not found.

> +		return EFI_SUCCESS;
> +
> +	tpm_ver = tpm_get_version(dev);
> +	if (tpm_ver != TPM_V2) {
> +		log_warning("Only TPMv2 supported for EFI_TCG2_PROTOCOL");

The message should end with \n.

This message becomes superfluous if you correct platform_get_tpm2_device().

> +		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");

The message should end with \n.

Best regards

Heinrich

> +
> +	return ret;
> +}
>
Ilias Apalodimas Nov. 12, 2020, 7:10 p.m. UTC | #4
Hi Heinrich, 


[...]
> > +		return EFI_SUCCESS;
> > +
> > +	tpm_ver = tpm_get_version(dev);
> > +	if (tpm_ver != TPM_V2) {
> > +		log_warning("Only TPMv2 supported for EFI_TCG2_PROTOCOL");
> 
> The message should end with \n.
> 
> This message becomes superfluous if you correct platform_get_tpm2_device().
> 

platform_get_tpm2_device() is used in EFI calls, won't that break printing from 
EFI apps?

> > +		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");
> 
> The message should end with \n.
> 
> Best regards
> 
> Heinrich
> 
> > +
> > +	return ret;
> > +}
> >
> 

Cheers
/Ilias
Heinrich Schuchardt Nov. 12, 2020, 7:21 p.m. UTC | #5
On 11/12/20 7:49 PM, Heinrich Schuchardt wrote:
> On 11/11/20 10:18 AM, 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>
>> ---
>> * changes since v3:
>> - added check for maximum number of PCRs allowed
>> - replaced multiple return Xl with goto out tags
>> * changes since v2:
>> - added description about include/efi_tcg2.h
>> - switch bool to u8 for tpm_present_flag
>> - removed superfluous 'default n' from Kconfig
>> - use 'goto 'tag' when possible
>>
>> * changes since v1:
>> - change return variable of platform_get_tpm2_device() when used
>> - since more headers were included in patch #2 use them in offset
>>   calculations for all tpm commands
>> - change the size of the response buffer regardless of what
>>   tpm2_get_capability() is doing
>>  include/efi_loader.h       |   2 +
>>  include/efi_tcg2.h         |  94 +++++++
>>  lib/efi_loader/Kconfig     |   7 +
>>  lib/efi_loader/Makefile    |   1 +
>>  lib/efi_loader/efi_setup.c |   7 +
>>  lib/efi_loader/efi_tcg2.c  | 539 +++++++++++++++++++++++++++++++++++++
>>  6 files changed, 650 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..4214f767eaba
>> --- /dev/null
>> +++ b/include/efi_tcg2.h
>> @@ -0,0 +1,94 @@
>> +/* SPDX-License-Identifier: GPL-2.0+ */
>> +/*
>> + * Defines data structures and APIs that allow an OS to interact with UEFI
>> + * firmware to query information about the device
>> + *
>> + * 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;
>> +	u8 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/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
>> index 075481428cdf..29ea14b2ee2a 100644
>> --- a/lib/efi_loader/Kconfig
>> +++ b/lib/efi_loader/Kconfig
>> @@ -184,6 +184,13 @@ 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
>> +	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..d600cff66af1
>> --- /dev/null
>> +++ b/lib/efi_loader/efi_tcg2.c
>> @@ -0,0 +1,539 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * Defines APIs that allow an OS to interact with UEFI firmware to query
>> + * information about the device.
>> + * https://trustedcomputinggroup.org/resource/tcg-efi-protocol-specification/
>> + *
>> + * 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;
>> +
>> +/*
>> + * When requesting TPM2_CAP_TPM_PROPERTIES the value is on a standard offset.
>> + * Since the current tpm2_get_capability() response buffers starts at
>> + * 'union tpmu_capabilities data' of 'struct tpms_capability_data', calculate
>> + * the response size and offset once for all consumers
>> + */
>> +#define TPM2_RESPONSE_BUFFER_SIZE (sizeof(struct tpms_capability_data) - \
>> +				   offsetof(struct tpms_capability_data, data))
>> +#define properties_offset (offsetof(struct tpml_tagged_tpm_property, tpm_property) + \
>> +			   offsetof(struct tpms_tagged_property, value))
>> +
>> +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);
>
> 'dm tree' on the sandbox shows:
>
> tpm           0  [   ]   google_sandbox_tpm    |-- tpm
> tpm           1  [   ]   sandbox_tpm2          |-- tpm2
>
> You have to loop over all TPM devices and look for one supporting TPMv2
> using tpm_get_version(dev).

You must also ensure that the device is probed. for_each_tpm_device()
does the probing for you:

__weak efi_status_t platform_get_tpm2_device(struct udevice **dev)
{
        for_each_tpm_device((*dev)) {
                if (tpm_get_version(*dev) == TPM_V2)
                        return EFI_SUCCESS;
        }
        return EFI_NOT_FOUND;
}

Best regards

Heinrich
Heinrich Schuchardt Nov. 12, 2020, 7:22 p.m. UTC | #6
On 11/12/20 8:10 PM, Ilias Apalodimas wrote:
> Hi Heinrich,
>
>
> [...]
>>> +		return EFI_SUCCESS;
>>> +
>>> +	tpm_ver = tpm_get_version(dev);
>>> +	if (tpm_ver != TPM_V2) {
>>> +		log_warning("Only TPMv2 supported for EFI_TCG2_PROTOCOL");
>>
>> The message should end with \n.
>>
>> This message becomes superfluous if you correct platform_get_tpm2_device().
>>
>
> platform_get_tpm2_device() is used in EFI calls, won't that break printing from
> EFI apps?

If platform_get_tpm2_device() only return TPM2v2 devices, you don't need
to check the type and hence you won't need the log_warning() above.

Regards

Heinrich

>
>>> +		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");
>>
>> The message should end with \n.
>>
>> Best regards
>>
>> Heinrich
>>
>>> +
>>> +	return ret;
>>> +}
>>>
>>
>
> Cheers
> /Ilias
>
Heinrich Schuchardt Nov. 12, 2020, 7:26 p.m. UTC | #7
On 11/11/20 10:18 AM, 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>
> ---
> * changes since v3:
> - added check for maximum number of PCRs allowed
> - replaced multiple return Xl with goto out tags
> * changes since v2:
> - added description about include/efi_tcg2.h
> - switch bool to u8 for tpm_present_flag
> - removed superfluous 'default n' from Kconfig
> - use 'goto 'tag' when possible
>
> * changes since v1:
> - change return variable of platform_get_tpm2_device() when used
> - since more headers were included in patch #2 use them in offset
>   calculations for all tpm commands
> - change the size of the response buffer regardless of what
>   tpm2_get_capability() is doing
>  include/efi_loader.h       |   2 +
>  include/efi_tcg2.h         |  94 +++++++
>  lib/efi_loader/Kconfig     |   7 +
>  lib/efi_loader/Makefile    |   1 +
>  lib/efi_loader/efi_setup.c |   7 +
>  lib/efi_loader/efi_tcg2.c  | 539 +++++++++++++++++++++++++++++++++++++
>  6 files changed, 650 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..4214f767eaba
> --- /dev/null
> +++ b/include/efi_tcg2.h
> @@ -0,0 +1,94 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Defines data structures and APIs that allow an OS to interact with UEFI
> + * firmware to query information about the device
> + *
> + * 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;
> +	u8 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/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> index 075481428cdf..29ea14b2ee2a 100644
> --- a/lib/efi_loader/Kconfig
> +++ b/lib/efi_loader/Kconfig
> @@ -184,6 +184,13 @@ 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
> +	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..d600cff66af1
> --- /dev/null
> +++ b/lib/efi_loader/efi_tcg2.c
> @@ -0,0 +1,539 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Defines APIs that allow an OS to interact with UEFI firmware to query
> + * information about the device.
> + * https://trustedcomputinggroup.org/resource/tcg-efi-protocol-specification/
> + *
> + * 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;
> +
> +/*
> + * When requesting TPM2_CAP_TPM_PROPERTIES the value is on a standard offset.
> + * Since the current tpm2_get_capability() response buffers starts at
> + * 'union tpmu_capabilities data' of 'struct tpms_capability_data', calculate
> + * the response size and offset once for all consumers
> + */
> +#define TPM2_RESPONSE_BUFFER_SIZE (sizeof(struct tpms_capability_data) - \
> +				   offsetof(struct tpms_capability_data, data))
> +#define properties_offset (offsetof(struct tpml_tagged_tpm_property, tpm_property) + \
> +			   offsetof(struct tpms_tagged_property, value))
> +
> +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)
> +		return EFI_DEVICE_ERROR;
> +
> +	*dev = devp;
> +
> +	return EFI_SUCCESS;
> +}
> +
> +/**
> + * tpm2_get_max_command_size() - 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[TPM2_RESPONSE_BUFFER_SIZE];
> +	u32 ret;
> +
> +	memset(response, 0, sizeof(response));
> +	ret = tpm2_get_capability(dev, TPM2_CAP_TPM_PROPERTIES,
> +				  TPM2_PT_MAX_COMMAND_SIZE, response, 1);
> +	if (ret)
> +		return -1;
> +
> +	*max_command_size = (uint16_t)get_unaligned_be32(response +
> +							 properties_offset);
> +
> +	return 0;
> +}
> +
> +/**
> + * tpm2_get_max_response_size() - 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[TPM2_RESPONSE_BUFFER_SIZE];
> +	u32 ret;
> +
> +	memset(response, 0, sizeof(response));
> +	ret = tpm2_get_capability(dev, TPM2_CAP_TPM_PROPERTIES,
> +				  TPM2_PT_MAX_RESPONSE_SIZE, response, 1);
> +	if (ret)
> +		return -1;
> +
> +	*max_response_size = (uint16_t)get_unaligned_be32(response +
> +							  properties_offset);
> +
> +	return 0;
> +}
> +
> +/**
> + * tpm2_get_manufacturer_id() - 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[TPM2_RESPONSE_BUFFER_SIZE];
> +	u32 ret;
> +
> +	memset(response, 0, sizeof(response));
> +	ret = tpm2_get_capability(dev, TPM2_CAP_TPM_PROPERTIES,
> +				  TPM2_PT_MANUFACTURER, response, 1);
> +	if (ret)
> +		return -1;
> +
> +	*manufacturer_id = get_unaligned_be32(response + properties_offset);
> +
> +	return 0;
> +}
> +
> +/**
> + * tpm2_get_num_pcr() - get the number of PCRs
> + *
> + * @dev:		TPM device
> + * @manufacturer_id:	output buffer for the number
> + *
> + * Return: 0 on success, -1 on error
> + */
> +static int tpm2_get_num_pcr(struct udevice *dev, u32 *num_pcr)
> +{
> +	u8 response[TPM2_RESPONSE_BUFFER_SIZE];
> +	u32 ret;
> +
> +	memset(response, 0, sizeof(response));
> +	ret = tpm2_get_capability(dev, TPM2_CAP_TPM_PROPERTIES,
> +				  TPM2_PT_PCR_COUNT, response, 1);
> +	if (ret)
> +		return -1;
> +
> +	*num_pcr = get_unaligned_be32(response + properties_offset);
> +	if (*num_pcr > TPM2_MAX_PCRS)
> +		return -1;
> +
> +	return 0;
> +}
> +
> +/**
> + * is_active_pcr() - Check if a 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() - 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[TPM2_RESPONSE_BUFFER_SIZE];
> +	struct tpml_pcr_selection pcrs;
> +	u32 ret, num_pcr;
> +	int i, tpm_ret;
> +
> +	memset(response, 0, sizeof(response));
> +	ret = tpm2_get_capability(dev, TPM2_CAP_PCRS, 0, response, 1);
> +	if (ret)
> +		goto out;
> +
> +	pcrs.count = get_unaligned_be32(response);
> +	/*
> +	 * We only support 5 algorithms for now so check against that
> +	 * instead of TPM2_NUM_PCR_BANKS
> +	 */
> +	if (pcrs.count > MAX_HASH_COUNT || pcrs.count < 1)
> +		goto out;
> +
> +	tpm_ret = tpm2_get_num_pcr(dev, &num_pcr);
> +	if (tpm_ret)
> +		goto out;
> +
> +	for (i = 0; i < pcrs.count; i++) {
> +		/*
> +		 * Definition of TPMS_PCR_SELECTION Structure
> +		 * hash: u16
> +		 * size_of_select: u8
> +		 * pcr_select: u8 array
> +		 *
> +		 * The offsets depend on the number of the device PCRs
> +		 * so we have to calculate them based on that
> +		 */
> +		u32 hash_offset = offsetof(struct tpml_pcr_selection, selection) +
> +			i * offsetof(struct tpms_pcr_selection, pcr_select) +
> +			i * ((num_pcr + 7) / 8);
> +		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);
> +		if (pcrs.selection[i].size_of_select > TPM2_PCR_SELECT_MAX)
> +			goto out;
> +		/* 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:
> +			EFI_PRINT("Unknown algorithm %x\n",
> +				  pcrs.selection[i].hash);
> +			break;
> +		}
> +	}
> +
> +	*pcr_banks = pcrs.count;
> +
> +	return 0;
> +out:
> +	return -1;
> +}
> +
> +/**
> + * get_capability() - 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,

Please, use unique function names. That makes setting breakpoints in gdb
much easier. How about: efi_tcg2_get_capability()?

Same for all other functions.

Regards

Heinrich

> +	       struct efi_tcg2_boot_service_capability *capability)
> +{
> +	struct udevice *dev;
> +	efi_status_t efi_ret;
> +	int ret;
> +
> +	EFI_ENTRY("%p, %p", this, capability);
> +
> +	if (!this || !capability) {
> +		efi_ret = EFI_INVALID_PARAMETER;
> +		goto out;
> +	}
> +
> +	if (capability->size < boot_service_capability_min) {
> +		capability->size = boot_service_capability_min;
> +		efi_ret = EFI_BUFFER_TOO_SMALL;
> +		goto out;
> +	}
> +
> +	if (capability->size < sizeof(*capability)) {
> +		capability->size = sizeof(*capability);
> +		efi_ret = EFI_BUFFER_TOO_SMALL;
> +		goto out;
> +	}
> +
> +	capability->structure_version.major = 1;
> +	capability->structure_version.minor = 1;
> +	capability->protocol_version.major = 1;
> +	capability->protocol_version.minor = 1;
> +
> +	efi_ret = platform_get_tpm2_device(&dev);
> +	if (efi_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;
> +
> +		efi_ret = EFI_SUCCESS;
> +		goto out;
> +	}
> +
> +	/* 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) {
> +		efi_ret = EFI_DEVICE_ERROR;
> +		goto out;
> +	}
> +
> +	/* Max command size */
> +	ret = tpm2_get_max_command_size(dev, &capability->max_command_size);
> +	if (ret) {
> +		efi_ret = EFI_DEVICE_ERROR;
> +		goto out;
> +	}
> +
> +	/* Max response size */
> +	ret = tpm2_get_max_response_size(dev, &capability->max_response_size);
> +	if (ret) {
> +		efi_ret = EFI_DEVICE_ERROR;
> +		goto out;
> +	}
> +
> +	/* Manufacturer ID */
> +	ret = tpm2_get_manufacturer_id(dev, &capability->manufacturer_id);
> +	if (ret) {
> +		efi_ret = EFI_DEVICE_ERROR;
> +		goto out;
> +	}
> +
> +	return EFI_EXIT(EFI_SUCCESS);
> +out:
> +	return EFI_EXIT(efi_ret);
> +}
> +
> +/**
> + * get_eventlog() - retrieve the the address of an 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 i
> + *				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)
> +{
> +	efi_status_t 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;
> +}
>
Ilias Apalodimas Nov. 12, 2020, 7:27 p.m. UTC | #8
On Thu, 12 Nov 2020 at 21:22, Heinrich Schuchardt <xypron.glpk@gmx.de>
wrote:

> On 11/12/20 8:10 PM, Ilias Apalodimas wrote:
> > Hi Heinrich,
> >
> >
> > [...]
> >>> +           return EFI_SUCCESS;
> >>> +
> >>> +   tpm_ver = tpm_get_version(dev);
> >>> +   if (tpm_ver != TPM_V2) {
> >>> +           log_warning("Only TPMv2 supported for EFI_TCG2_PROTOCOL");
> >>
> >> The message should end with \n.
> >>
> >> This message becomes superfluous if you correct
> platform_get_tpm2_device().
> >>
> >
> > platform_get_tpm2_device() is used in EFI calls, won't that break
> printing from
> > EFI apps?
>
> If platform_get_tpm2_device() only return TPM2v2 devices, you don't need
> to check the type and hence you won't need the log_warning() above.
>

Well you still need to warn the user that only TPMv2 devices are supported
no?
Hence my question on what's the best way to do that.


> Regards
>
> Heinrich
>
> >
> >>> +           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");
> >>
> >> The message should end with \n.
> >>
> >> Best regards
> >>
> >> Heinrich
> >>
> >>> +
> >>> +   return ret;
> >>> +}
> >>>
> >>
> >
> > Cheers
> > /Ilias
> >
>
>
diff mbox series

Patch

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..4214f767eaba
--- /dev/null
+++ b/include/efi_tcg2.h
@@ -0,0 +1,94 @@ 
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Defines data structures and APIs that allow an OS to interact with UEFI
+ * firmware to query information about the device
+ *
+ * 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;
+	u8 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/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
index 075481428cdf..29ea14b2ee2a 100644
--- a/lib/efi_loader/Kconfig
+++ b/lib/efi_loader/Kconfig
@@ -184,6 +184,13 @@  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
+	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..d600cff66af1
--- /dev/null
+++ b/lib/efi_loader/efi_tcg2.c
@@ -0,0 +1,539 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Defines APIs that allow an OS to interact with UEFI firmware to query
+ * information about the device.
+ * https://trustedcomputinggroup.org/resource/tcg-efi-protocol-specification/
+ *
+ * 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;
+
+/*
+ * When requesting TPM2_CAP_TPM_PROPERTIES the value is on a standard offset.
+ * Since the current tpm2_get_capability() response buffers starts at
+ * 'union tpmu_capabilities data' of 'struct tpms_capability_data', calculate
+ * the response size and offset once for all consumers
+ */
+#define TPM2_RESPONSE_BUFFER_SIZE (sizeof(struct tpms_capability_data) - \
+				   offsetof(struct tpms_capability_data, data))
+#define properties_offset (offsetof(struct tpml_tagged_tpm_property, tpm_property) + \
+			   offsetof(struct tpms_tagged_property, value))
+
+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)
+		return EFI_DEVICE_ERROR;
+
+	*dev = devp;
+
+	return EFI_SUCCESS;
+}
+
+/**
+ * tpm2_get_max_command_size() - 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[TPM2_RESPONSE_BUFFER_SIZE];
+	u32 ret;
+
+	memset(response, 0, sizeof(response));
+	ret = tpm2_get_capability(dev, TPM2_CAP_TPM_PROPERTIES,
+				  TPM2_PT_MAX_COMMAND_SIZE, response, 1);
+	if (ret)
+		return -1;
+
+	*max_command_size = (uint16_t)get_unaligned_be32(response +
+							 properties_offset);
+
+	return 0;
+}
+
+/**
+ * tpm2_get_max_response_size() - 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[TPM2_RESPONSE_BUFFER_SIZE];
+	u32 ret;
+
+	memset(response, 0, sizeof(response));
+	ret = tpm2_get_capability(dev, TPM2_CAP_TPM_PROPERTIES,
+				  TPM2_PT_MAX_RESPONSE_SIZE, response, 1);
+	if (ret)
+		return -1;
+
+	*max_response_size = (uint16_t)get_unaligned_be32(response +
+							  properties_offset);
+
+	return 0;
+}
+
+/**
+ * tpm2_get_manufacturer_id() - 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[TPM2_RESPONSE_BUFFER_SIZE];
+	u32 ret;
+
+	memset(response, 0, sizeof(response));
+	ret = tpm2_get_capability(dev, TPM2_CAP_TPM_PROPERTIES,
+				  TPM2_PT_MANUFACTURER, response, 1);
+	if (ret)
+		return -1;
+
+	*manufacturer_id = get_unaligned_be32(response + properties_offset);
+
+	return 0;
+}
+
+/**
+ * tpm2_get_num_pcr() - get the number of PCRs
+ *
+ * @dev:		TPM device
+ * @manufacturer_id:	output buffer for the number
+ *
+ * Return: 0 on success, -1 on error
+ */
+static int tpm2_get_num_pcr(struct udevice *dev, u32 *num_pcr)
+{
+	u8 response[TPM2_RESPONSE_BUFFER_SIZE];
+	u32 ret;
+
+	memset(response, 0, sizeof(response));
+	ret = tpm2_get_capability(dev, TPM2_CAP_TPM_PROPERTIES,
+				  TPM2_PT_PCR_COUNT, response, 1);
+	if (ret)
+		return -1;
+
+	*num_pcr = get_unaligned_be32(response + properties_offset);
+	if (*num_pcr > TPM2_MAX_PCRS)
+		return -1;
+
+	return 0;
+}
+
+/**
+ * is_active_pcr() - Check if a 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() - 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[TPM2_RESPONSE_BUFFER_SIZE];
+	struct tpml_pcr_selection pcrs;
+	u32 ret, num_pcr;
+	int i, tpm_ret;
+
+	memset(response, 0, sizeof(response));
+	ret = tpm2_get_capability(dev, TPM2_CAP_PCRS, 0, response, 1);
+	if (ret)
+		goto out;
+
+	pcrs.count = get_unaligned_be32(response);
+	/*
+	 * We only support 5 algorithms for now so check against that
+	 * instead of TPM2_NUM_PCR_BANKS
+	 */
+	if (pcrs.count > MAX_HASH_COUNT || pcrs.count < 1)
+		goto out;
+
+	tpm_ret = tpm2_get_num_pcr(dev, &num_pcr);
+	if (tpm_ret)
+		goto out;
+
+	for (i = 0; i < pcrs.count; i++) {
+		/*
+		 * Definition of TPMS_PCR_SELECTION Structure
+		 * hash: u16
+		 * size_of_select: u8
+		 * pcr_select: u8 array
+		 *
+		 * The offsets depend on the number of the device PCRs
+		 * so we have to calculate them based on that
+		 */
+		u32 hash_offset = offsetof(struct tpml_pcr_selection, selection) +
+			i * offsetof(struct tpms_pcr_selection, pcr_select) +
+			i * ((num_pcr + 7) / 8);
+		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);
+		if (pcrs.selection[i].size_of_select > TPM2_PCR_SELECT_MAX)
+			goto out;
+		/* 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:
+			EFI_PRINT("Unknown algorithm %x\n",
+				  pcrs.selection[i].hash);
+			break;
+		}
+	}
+
+	*pcr_banks = pcrs.count;
+
+	return 0;
+out:
+	return -1;
+}
+
+/**
+ * get_capability() - 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;
+	efi_status_t efi_ret;
+	int ret;
+
+	EFI_ENTRY("%p, %p", this, capability);
+
+	if (!this || !capability) {
+		efi_ret = EFI_INVALID_PARAMETER;
+		goto out;
+	}
+
+	if (capability->size < boot_service_capability_min) {
+		capability->size = boot_service_capability_min;
+		efi_ret = EFI_BUFFER_TOO_SMALL;
+		goto out;
+	}
+
+	if (capability->size < sizeof(*capability)) {
+		capability->size = sizeof(*capability);
+		efi_ret = EFI_BUFFER_TOO_SMALL;
+		goto out;
+	}
+
+	capability->structure_version.major = 1;
+	capability->structure_version.minor = 1;
+	capability->protocol_version.major = 1;
+	capability->protocol_version.minor = 1;
+
+	efi_ret = platform_get_tpm2_device(&dev);
+	if (efi_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;
+
+		efi_ret = EFI_SUCCESS;
+		goto out;
+	}
+
+	/* 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) {
+		efi_ret = EFI_DEVICE_ERROR;
+		goto out;
+	}
+
+	/* Max command size */
+	ret = tpm2_get_max_command_size(dev, &capability->max_command_size);
+	if (ret) {
+		efi_ret = EFI_DEVICE_ERROR;
+		goto out;
+	}
+
+	/* Max response size */
+	ret = tpm2_get_max_response_size(dev, &capability->max_response_size);
+	if (ret) {
+		efi_ret = EFI_DEVICE_ERROR;
+		goto out;
+	}
+
+	/* Manufacturer ID */
+	ret = tpm2_get_manufacturer_id(dev, &capability->manufacturer_id);
+	if (ret) {
+		efi_ret = EFI_DEVICE_ERROR;
+		goto out;
+	}
+
+	return EFI_EXIT(EFI_SUCCESS);
+out:
+	return EFI_EXIT(efi_ret);
+}
+
+/**
+ * get_eventlog() - retrieve the the address of an 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 i
+ *				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)
+{
+	efi_status_t 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;
+}