[PATCH 2/4] tpm: Add AMD Versal Gen 2 TPM2 driver

Ilias Apalodimas ilias.apalodimas at linaro.org
Fri Mar 27 14:21:24 CET 2026


Hi Padmarao


On Fri, 27 Mar 2026 at 14:53, Padmarao Begari <padmarao.begari at amd.com> wrote:
>
> Add tpm2_versal2_smc, a TPM2 driver for AMD Versal Gen 2 that
> exposes the PLM XilOCP HW PCR service as a standard TPM2 interface.
>
> PCR operations are routed through TF-A:
>  U-Boot -> xilinx_pm_request() -> SMC PASS_THROUGH -> TF-A -> IPI -> PLM
>
> PCR mapping: TPM PCR 0..7 -> PLM HW PCR 0..7
>
> Supported TPM2 commands:
>   - TPM2_CC_STARTUP        (no-op; PLM PCR module is always ready)
>   - TPM2_CC_SELF_TEST      (no-op)
>   - TPM2_CC_GET_CAPABILITY (CAP_PCRS and CAP_TPM_PROPERTIES)
>   - TPM2_CC_PCR_EXTEND     (SHA-256 and SHA-384)
>   - TPM2_CC_PCR_READ       (SHA-256 and SHA-384)
>
> Two PCR banks (SHA-256 and SHA-384) share the same 8 PLM HW PCRs.
> SHA-256 digests are zero-padded to 48 bytes (PLM native width) before
> being passed to PLM.
>
> Signed-off-by: Padmarao Begari <padmarao.begari at amd.com>
> ---
>  MAINTAINERS                    |   5 +
>  drivers/tpm/Kconfig            |  21 ++
>  drivers/tpm/Makefile           |   1 +
>  drivers/tpm/tpm2_versal2_smc.c | 666 +++++++++++++++++++++++++++++++++
>  4 files changed, 693 insertions(+)
>  create mode 100644 drivers/tpm/tpm2_versal2_smc.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index d4b527560aa..14b57772d71 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1841,6 +1841,11 @@ F:       drivers/tpm/
>  F:     include/tpm*
>  F:     lib/tpm*
>
> +TPM2 AMD VERSAL GEN 2 DRIVER
> +M:     Padmarao Begari <padmarao.begari at amd.com>
> +S:     Maintained
> +F:     drivers/tpm/tpm2_versal2_smc.c
> +
>  TQ GROUP
>  #M:    Martin Krause <martin.krause at tq-systems.de>
>  S:     Orphaned (Since 2016-02)
> diff --git a/drivers/tpm/Kconfig b/drivers/tpm/Kconfig
> index 219ea606b50..6f54ae5609e 100644
> --- a/drivers/tpm/Kconfig
> +++ b/drivers/tpm/Kconfig
> @@ -200,6 +200,27 @@ config TPM2_EVENT_LOG_SIZE
>           allocated twice. One for the eventlog it self and one for the
>           configuration table that is required from the TCG2 spec
>
> +config TPM2_VERSAL2_SMC
> +       bool "AMD Versal Gen 2 TPM2 driver (via TF-A SMC)"
> +       depends on TPM_V2 && ZYNQMP_FIRMWARE && ARCH_VERSAL2
> +       select SHA384
> +       help
> +         Enable the AMD Versal Gen 2 TPM2 driver.  Exposes the PLM XilOCP
> +         PCR service as a standard TPM2 interface.
> +
> +         All PCR operations are routed through xilinx_pm_request(), which
> +         issues an ARM SMC using the Versal Gen 2 extended calling convention.
> +         TF-A passes the call through to the PLM XilOCP module (module ID 13)
> +         over IPI.
> +
> +         PCR mapping:
> +           TPM PCR 0..7 -> HW PCR 0..7 (XOCP_API_EXTEND_HWPCR)
> +
> +         Supported TPM2 commands: STARTUP, SELF_TEST, GET_CAPABILITY,
> +         PCR_EXTEND, PCR_READ.
> +
> +         If unsure, say N.
> +
>  endif # TPM_V2
>
>  endmenu
> diff --git a/drivers/tpm/Makefile b/drivers/tpm/Makefile
> index b83ce703ec0..7ef5bcf6be1 100644
> --- a/drivers/tpm/Makefile
> +++ b/drivers/tpm/Makefile
> @@ -14,3 +14,4 @@ obj-$(CONFIG_TPM2_TIS_SPI) += tpm2_tis_core.o tpm2_tis_spi.o
>  obj-$(CONFIG_TPM2_TIS_I2C) += tpm2_tis_core.o tpm2_tis_i2c.o
>  obj-$(CONFIG_TPM2_FTPM_TEE) += tpm2_ftpm_tee.o
>  obj-$(CONFIG_TPM2_MMIO) += tpm2_tis_core.o tpm2_tis_mmio.o
> +obj-$(CONFIG_TPM2_VERSAL2_SMC) += tpm2_versal2_smc.o
> diff --git a/drivers/tpm/tpm2_versal2_smc.c b/drivers/tpm/tpm2_versal2_smc.c
> new file mode 100644
> index 00000000000..266d3bde45a
> --- /dev/null
> +++ b/drivers/tpm/tpm2_versal2_smc.c
> @@ -0,0 +1,666 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2026, Advanced Micro Devices, Inc.
> + *
> + * Author:
> + * Padmarao Begari <padmarao.begari at amd.com>
> + *
> + * Description:
> + * AMD Versal Gen 2 TPM2 driver via TF-A SMC.
> + *
> + * Exposes PLM XilOCP PCR services as a U-Boot DM TPM (UCLASS_TPM) device.
> + * All calls go through zynqmp_pm_xilocp_extend_hwpcr() /
> + * zynqmp_pm_xilocp_get_hwpcr() (firmware-zynqmp.c) which call
> + * xilinx_pm_request() -> smc_call_enhanced() -> ARM SMC (PASS_THROUGH) ->
> + * TF-A -> IPI -> PLM XilOCP (module ID 13).
> + *
> + * Communication path:
> + *   U-Boot -> zynqmp_pm_xilocp_*() -> xilinx_pm_request()
> + *          -> SMC (PASS_THROUGH) -> TF-A -> IPI -> PLM
> + *
> + * PCR mapping (U-Boot TPM PCR index -> XilOCP PLM PCR):
> + *   0..7 -> HW PCR 0..7 (extend: XOCP_API_EXTEND_HWPCR,
> + *                           read: XOCP_API_GET_HWPCR)
> + *
> + * Both SHA-256 and SHA-384 banks map to the same 8 HW PCRs.  SHA-256 digests
> + * are zero-padded to 48 bytes before passing to PLM (SHA3-384 native width).
> + *
> + * Supported TPM2 commands:
> + *   TPM2_CC_STARTUP         (0x0144) - no-op, always succeeds
> + *   TPM2_CC_SELF_TEST       (0x0143) - no-op, always succeeds
> + *   TPM2_CC_GET_CAPABILITY  (0x017a) - reports PCR banks and TPM properties
> + *   TPM2_CC_PCR_EXTEND      (0x0182) - extend HW PCR via PLM
> + *   TPM2_CC_PCR_READ        (0x017e) - read HW PCR via PLM

You have a lot of duplication in the driver. These are already defined in
include/tpm-v2.h.

Is there anything special with this driver that doesn't adhere to the TPM specs?
Is there any reason you can't use drivers/tpm2_tis_core.c and just
define the bus operations?

Thanks
/Ilias

> + *
> + * No device tree node is required.  The driver is automatically bound as
> + * a child of the zynqmp_firmware device by zynqmp_firmware_bind() after
> + * confirming that PLM supports XOCP_API_EXTEND_HWPCR and XOCP_API_GET_HWPCR.
> + */
> +
> +#define LOG_CATEGORY UCLASS_TPM
> +
> +#include <cpu_func.h>
> +#include <dm.h>
> +#include <log.h>
> +#include <tpm-common.h>
> +#include <tpm-v2.h>
> +#include <zynqmp_firmware.h>
> +
> +#include <asm/cache.h>
> +#include <dm/device_compat.h>
> +#include <linux/bitops.h>
> +#include <linux/string.h>
> +#include <linux/unaligned/be_byteshift.h>
> +
> +/* PCR count and hash size (SHA3-384) */
> +#define HW_PCR_COUNT   8U
> +#define PCR_HASH_SIZE  TPM2_SHA384_DIGEST_SIZE
> +
> +/* Common TPM2 response header: tag(2) + size(4) + rc(4) = 10 bytes */
> +struct tpm2_resp_hdr {
> +       __be16 tag;
> +       __be32 size;
> +       __be32 rc;
> +} __packed;
> +
> +/* One PCR bank descriptor in a GetCapability(CAP_PCRS) response */
> +struct tpm2_pcr_bank_desc {
> +       __be16 hash_alg;
> +       u8 size_of_select;
> +       u8 pcr_select;
> +} __packed;
> +
> +/* Full GetCapability(CAP_PCRS) response */
> +struct tpm2_resp_cap_pcrs {
> +       struct tpm2_resp_hdr hdr;
> +       u8 more_data;
> +       __be32 capability;
> +       __be32 count;
> +       struct tpm2_pcr_bank_desc banks[2];
> +} __packed;
> +
> +/* PCR_Read response (digest payload follows the fixed header) */
> +struct tpm2_resp_pcr_read {
> +       struct tpm2_resp_hdr hdr;
> +       __be32 update_count;
> +       u8 digest[];
> +} __packed;
> +
> +/* One property entry in a GetCapability(CAP_TPM_PROPERTIES) response */
> +struct tpm2_tagged_prop {
> +       __be32 property;
> +       __be32 value;
> +} __packed;
> +
> +/* Fixed header of a GetCapability(CAP_TPM_PROPERTIES) response */
> +struct tpm2_resp_cap_props {
> +       struct tpm2_resp_hdr hdr;
> +       u8 more_data;
> +       __be32 capability;
> +       __be32 count;
> +       struct tpm2_tagged_prop props[];
> +} __packed;
> +
> +/* Common TPM2 command header: tag(2) + size(4) + cc(4) = 10 bytes */
> +struct tpm2_req_hdr {
> +       __be16 tag;
> +       __be32 size;
> +       __be32 cc;
> +} __packed;
> +
> +/* GET_CAPABILITY command */
> +struct tpm2_cmd_get_capability {
> +       struct tpm2_req_hdr hdr;
> +       __be32 capability;
> +       __be32 property;
> +       __be32 property_count;
> +} __packed;
> +
> +/*
> + * PCR_EXTEND command fixed prefix.
> + * A variable-length auth area of auth_size bytes follows at offset
> + * sizeof(*c), then __be32 digest_count, __be16 hash_alg, u8 digest[].
> + */
> +struct tpm2_cmd_pcr_extend {
> +       struct tpm2_req_hdr hdr;
> +       __be32 pcr_handle;
> +       __be32 auth_size;
> +} __packed;
> +
> +/* PCR_READ command */
> +struct tpm2_cmd_pcr_read {
> +       struct tpm2_req_hdr hdr;
> +       __be32 sel_count;
> +       __be16 hash_alg;
> +       u8 size_of_select;
> +       u8 pcr_select[];
> +} __packed;
> +
> +struct tpm2_versal2_priv {
> +       u8 *pcr_buf;
> +};
> +
> +/**
> + * hw_pcr_extend() - Extend a Hardware PCR with a 48-byte hash.
> + * @pcr_buf: DMA buffer holding the digest (cache-flushed before PLM DMA read)
> + * @pcr_num: HW PCR index (0..HW_PCR_COUNT-1)
> + *
> + * Return: 0 on success, PLM error code on failure.
> + */
> +static int hw_pcr_extend(u8 *pcr_buf, u32 pcr_num)
> +{
> +       ulong hash_addr = (ulong)pcr_buf;
> +
> +       flush_dcache_range(hash_addr, hash_addr + PCR_HASH_SIZE);
> +
> +       return zynqmp_pm_xilocp_extend_hwpcr(pcr_num, hash_addr, PCR_HASH_SIZE);
> +}
> +
> +/**
> + * hw_pcr_get() - Read a Hardware PCR value into pcr_buf.
> + * @pcr_buf:  DMA buffer for PLM to write the result into
> + * @pcr_mask: Bitmask of PCRs to read; bit N selects HW PCR N
> + *
> + * Cache handling (DMA receive pattern):
> + *   1. Invalidate BEFORE: discard dirty CPU cache lines so they cannot be
> + *      written back after PLM's DMA write, corrupting PLM's data.
> + *   2. PLM DMA writes PCR value(s) to physical memory.
> + *   3. Invalidate AFTER: discard stale CPU cache so the CPU reads PLM's
> + *      freshly written data from physical memory.
> + *
> + * Return: 0 on success, PLM error code on failure.
> + */
> +static int hw_pcr_get(u8 *pcr_buf, u32 pcr_mask)
> +{
> +       ulong buf_addr = (ulong)pcr_buf;
> +       int ret;
> +
> +       invalidate_dcache_range(buf_addr, buf_addr + PCR_HASH_SIZE);
> +
> +       ret = zynqmp_pm_xilocp_get_hwpcr(pcr_mask, buf_addr, PCR_HASH_SIZE);
> +       if (!ret)
> +               invalidate_dcache_range(buf_addr, buf_addr + PCR_HASH_SIZE);
> +
> +       return ret;
> +}
> +
> +/**
> + * build_simple_response() - Build a minimal 10-byte TPM2 response.
> + * @resp: Output buffer (must be >= TPM_HEADER_SIZE bytes)
> + * @rc:   TPM2 response code (e.g. TPM2_RC_SUCCESS)
> + *
> + * Return: the response size (TPM_HEADER_SIZE).
> + */
> +static size_t build_simple_response(u8 *resp, u32 rc)
> +{
> +       struct tpm2_resp_hdr *r = (struct tpm2_resp_hdr *)resp;
> +
> +       r->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
> +       r->size = cpu_to_be32(sizeof(*r));
> +       r->rc = cpu_to_be32(rc);
> +
> +       return sizeof(*r);
> +}
> +
> +/**
> + * build_pcr_read_response() - Build a TPM2 PCR_Read response.
> + * @resp:       Output buffer (must be >= 14 + digest_len bytes)
> + * @pcr_data:   48-byte PCR value from PLM
> + * @digest_len: Number of bytes to include from pcr_data (32 for SHA256,
> + *              48 for SHA384)
> + *
> + * PLM stores SHA3-384 (48 bytes) internally.  We return the first
> + * digest_len bytes of that value to match the algorithm the caller
> + * requested:
> + *   SHA256 -> digest_len = 32 -> response is 46 bytes
> + *   SHA384 -> digest_len = 48 -> response is 62 bytes
> + *
> + * The caller (tpm2_pcr_read) extracts the digest as:
> + *   digest = response + (response_len - digest_len) = response + 14
> + *
> + * Return: the response size (14 + digest_len).
> + */
> +static size_t build_pcr_read_response(u8 *resp, const u8 *pcr_data,
> +                                     u32 digest_len)
> +{
> +       struct tpm2_resp_pcr_read *r = (struct tpm2_resp_pcr_read *)resp;
> +       size_t total = sizeof(*r) + digest_len;
> +
> +       r->hdr.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
> +       r->hdr.size = cpu_to_be32((u32)total);
> +       r->hdr.rc = cpu_to_be32(TPM2_RC_SUCCESS);
> +       r->update_count = cpu_to_be32(0);
> +       memcpy(r->digest, pcr_data, digest_len);
> +
> +       return total;
> +}
> +
> +/**
> + * build_cap_pcrs_response() - Build a TPM2 GetCapability(CAP_PCRS) response.
> + * @resp: Output buffer (must be >= 27 bytes)
> + *
> + * Reports two PCR banks - SHA256 and SHA384 - each with 8 PCRs
> + * (indices 0-7, 1-byte pcrSelect bitmap, 0xff).
> + *
> + * Both banks are backed by PLM HW PCR 0-7.  They share the same
> + * underlying 48-byte PCR value; the algorithm only controls how many
> + * bytes are returned on a read (32 for SHA256, 48 for SHA384) and how
> + * the incoming digest is formatted on extend (zero-padded to 48 for
> + * SHA256, passed directly for SHA384).
> + *
> + * CONFIG_SHA384=y is required so that tpm2_algorithm_supported(SHA384)
> + * returns true and tpm2_check_active_banks() passes for pcr_extend.
> + *
> + * Response layout (27 bytes):
> + *   [0..1]   tag          = TPM2_ST_NO_SESSIONS
> + *   [2..5]   size         = 27
> + *   [6..9]   rc           = TPM2_RC_SUCCESS
> + *   [10]     moreData     = 0
> + *   [11..14] capability   = TPM2_CAP_PCRS
> + *   [15..18] count        = 2
> + *   [19..20] hash[0]      = TPM2_ALG_SHA256 (0x000b)
> + *   [21]     sizeofSelect = 1  (1 byte covers PCRs 0-7)
> + *   [22]     pcrSelect[0] = 0xff
> + *   [23..24] hash[1]      = TPM2_ALG_SHA384 (0x000c)
> + *   [25]     sizeofSelect = 1
> + *   [26]     pcrSelect[1] = 0xff
> + *
> + * Return: the response size (27).
> + */
> +static size_t build_cap_pcrs_response(u8 *resp)
> +{
> +       struct tpm2_resp_cap_pcrs *r = (struct tpm2_resp_cap_pcrs *)resp;
> +
> +       r->hdr.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
> +       r->hdr.size = cpu_to_be32(sizeof(*r));
> +       r->hdr.rc = cpu_to_be32(TPM2_RC_SUCCESS);
> +       r->more_data = 0;
> +       r->capability = cpu_to_be32(TPM2_CAP_PCRS);
> +       r->count = cpu_to_be32(ARRAY_SIZE(r->banks));
> +       /* bank 0: SHA256 */
> +       r->banks[0].hash_alg = cpu_to_be16(TPM2_ALG_SHA256);
> +       r->banks[0].size_of_select = 1; /* 1 byte covers PCRs 0-7 */
> +       r->banks[0].pcr_select = 0xff; /* PCRs 0-7 active */
> +       /* bank 1: SHA384 */
> +       r->banks[1].hash_alg = cpu_to_be16(TPM2_ALG_SHA384);
> +       r->banks[1].size_of_select = 1;
> +       r->banks[1].pcr_select = 0xff;
> +
> +       return sizeof(*r);
> +}
> +
> +/**
> + * tpm_props - Minimal TPM2 fixed-property table (TPM2_CAP_TPM_PROPERTIES).
> + *
> + * Covers the properties most commonly queried by U-Boot and host software.
> + * The table is searched linearly; entries need not be sorted.
> + */
> +static const struct {
> +       u32 prop;
> +       u32 val;
> +} tpm_props[] = {
> +       { TPM2_PT_MANUFACTURER, ('A' << 24) | ('M' << 16) | ('D' << 8) | ' ' },
> +       { TPM2_PT_PCR_COUNT,         HW_PCR_COUNT },
> +       { TPM2_PT_MAX_COMMAND_SIZE,  TPM_MAX_BUF_SIZE },
> +       { TPM2_PT_MAX_RESPONSE_SIZE, TPM_MAX_BUF_SIZE },
> +};
> +
> +/**
> + * build_cap_tpm_props_response() - Build a GetCapability(CAP_TPM_PROPERTIES)
> + * response for any property range.
> + * @resp:        Output buffer of @buf_size bytes
> + * @buf_size:    Size of @resp in bytes; prop_count is clamped to fit
> + * @prop_start:  First property ID requested (inclusive)
> + * @prop_count:  Number of consecutive property IDs requested
> + *
> + * Always returns exactly prop_count entries (after clamping to buf_size) so
> + * the U-Boot tpm2_get_capability() caller reads the correct number of
> + * properties from the buffer.  Properties not found in tpm_props[] are
> + * returned with value 0.
> + *
> + * Return: the response size.
> + */
> +static size_t build_cap_tpm_props_response(u8 *resp, size_t buf_size,
> +                                          u32 prop_start, u32 prop_count)
> +{
> +       struct tpm2_resp_cap_props *r = (struct tpm2_resp_cap_props *)resp;
> +       u32 max_count;
> +       size_t total;
> +       size_t j;
> +       u32 n;
> +
> +       if (buf_size < sizeof(*r))
> +               return build_simple_response(resp, TPM2_RC_SIZE);
> +
> +       max_count = (u32)((buf_size - sizeof(*r)) / sizeof(r->props[0]));
> +       if (prop_count > max_count)
> +               prop_count = max_count;
> +
> +       total = sizeof(*r) + prop_count * sizeof(r->props[0]);
> +
> +       r->hdr.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
> +       r->hdr.size = cpu_to_be32((u32)total);
> +       r->hdr.rc = cpu_to_be32(TPM2_RC_SUCCESS);
> +       r->more_data = 0;
> +       r->capability = cpu_to_be32(TPM2_CAP_TPM_PROPERTIES);
> +       r->count = cpu_to_be32(prop_count);
> +
> +       /*
> +        * Always return exactly prop_count entries so the U-Boot
> +        * tpm2_get_capability() caller reads the correct number of entries
> +        * from the buffer. Properties not in our table are returned as 0.
> +        */
> +       for (n = 0; n < prop_count; n++) {
> +               u32 pid = prop_start + n;
> +               u32 val = 0;
> +
> +               for (j = 0; j < ARRAY_SIZE(tpm_props); j++) {
> +                       if (tpm_props[j].prop == pid) {
> +                               val = tpm_props[j].val;
> +                               break;
> +                       }
> +               }
> +               r->props[n].property = cpu_to_be32(pid);
> +               r->props[n].value = cpu_to_be32(val);
> +       }
> +
> +       return total;
> +}
> +
> +/**
> + * tpm2_versal2_smc_xfer() - Translate TPM2 command bytes to PLM SMC calls.
> + * @dev:       TPM device
> + * @sendbuf:   Raw TPM2 command buffer
> + * @send_size: Length of @sendbuf in bytes
> + * @recvbuf:   Output buffer for the TPM2 response
> + * @recv_len:  On return, the number of bytes written to @recvbuf
> + *
> + * Implements struct tpm_ops.xfer.  Receives a raw TPM2 command buffer,
> + * dispatches to the appropriate PLM function, and writes a TPM2-formatted
> + * response.
> + *
> + * Return: 0 on success, negative error code on failure.
> + */
> +static int tpm2_versal2_smc_xfer(struct udevice *dev, const u8 *sendbuf,
> +                                size_t send_size, u8 *recvbuf,
> +                                size_t *recv_len)
> +{
> +       const struct tpm2_req_hdr *hdr = (const struct tpm2_req_hdr *)sendbuf;
> +       size_t resp_len = build_simple_response(recvbuf, TPM2_RC_FAILURE);
> +       struct tpm2_versal2_priv *priv = dev_get_priv(dev);
> +       u8 *pcr_buf = priv->pcr_buf;
> +       u32 cc;
> +
> +       if (send_size < sizeof(*hdr)) {
> +               log_debug("command too short (%zu bytes)\n", send_size);
> +               *recv_len = build_simple_response(recvbuf, TPM2_RC_SIZE);
> +               return -EINVAL;
> +       }
> +
> +       cc = be32_to_cpu(hdr->cc);
> +
> +       switch (cc) {
> +       case TPM2_CC_STARTUP:
> +       case TPM2_CC_SELF_TEST:
> +               /* PLM PCR module is always ready; no initialization needed. */
> +               resp_len = build_simple_response(recvbuf, TPM2_RC_SUCCESS);
> +               break;
> +
> +       case TPM2_CC_GET_CAPABILITY: {
> +               const struct tpm2_cmd_get_capability *c =
> +                       (const struct tpm2_cmd_get_capability *)sendbuf;
> +               u32 cap, prop, prop_count;
> +
> +               if (send_size < sizeof(*c)) {
> +                       resp_len = build_simple_response(recvbuf, TPM2_RC_SIZE);
> +                       break;
> +               }
> +
> +               cap = be32_to_cpu(c->capability);
> +               prop = be32_to_cpu(c->property);
> +               prop_count = be32_to_cpu(c->property_count);
> +
> +               if (cap == TPM2_CAP_PCRS) {
> +                       resp_len = build_cap_pcrs_response(recvbuf);
> +               } else if (cap == TPM2_CAP_TPM_PROPERTIES) {
> +                       resp_len = build_cap_tpm_props_response(recvbuf,
> +                                                               TPM_MAX_BUF_SIZE,
> +                                                               prop,
> +                                                               prop_count);
> +               } else {
> +                       log_debug("unsupported cap=0x%08x\n", cap);
> +                       resp_len = build_simple_response(recvbuf,
> +                                                        TPM2_RC_VALUE);
> +               }
> +               break;
> +       }
> +
> +       case TPM2_CC_PCR_EXTEND: {
> +               const struct tpm2_cmd_pcr_extend *c =
> +                       (const struct tpm2_cmd_pcr_extend *)sendbuf;
> +               u32 pcr_handle, auth_size, digest_len = 0;
> +               const u8 *digest;
> +               u16 hash_alg;
> +
> +               if (send_size < sizeof(*c)) {
> +                       resp_len = build_simple_response(recvbuf,
> +                                                        TPM2_RC_SIZE);
> +                       break;
> +               }
> +
> +               pcr_handle = be32_to_cpu(c->pcr_handle);
> +               auth_size = be32_to_cpu(c->auth_size);
> +
> +               /* Need auth area + digest_count(4) + hash_alg(2) */
> +               if (send_size < sizeof(*c) + auth_size +
> +                               sizeof(__be32) + sizeof(__be16)) {
> +                       resp_len = build_simple_response(recvbuf,
> +                                                        TPM2_RC_SIZE);
> +                       break;
> +               }
> +
> +               hash_alg = get_unaligned_be16(sendbuf + sizeof(*c) +
> +                                             auth_size + sizeof(__be32));
> +
> +               switch (hash_alg) {
> +               case TPM2_ALG_SHA384:
> +                       digest_len = PCR_HASH_SIZE;
> +                       break;
> +               case TPM2_ALG_SHA256:
> +                       digest_len = TPM2_SHA256_DIGEST_SIZE;
> +                       break;
> +               default:
> +                       resp_len = build_simple_response(recvbuf,
> +                                                        TPM2_RC_VALUE);
> +                       break;
> +               }
> +
> +               if (!digest_len)
> +                       break;
> +
> +               if (send_size < sizeof(*c) + auth_size +
> +                               sizeof(__be32) + sizeof(__be16) + digest_len) {
> +                       resp_len = build_simple_response(recvbuf,
> +                                                        TPM2_RC_SIZE);
> +                       break;
> +               }
> +
> +               if (pcr_handle >= HW_PCR_COUNT) {
> +                       log_debug("PCR index %u out of range\n", pcr_handle);
> +                       resp_len = build_simple_response(recvbuf,
> +                                                        TPM2_RC_VALUE);
> +                       break;
> +               }
> +
> +               digest = sendbuf + sizeof(*c) + auth_size +
> +                        sizeof(__be32) + sizeof(__be16);
> +
> +               /*
> +                * Copy digest into priv buffer, zero-padding to 48 bytes.
> +                * Both SHA256 and SHA384 extend the same HW PCR; SHA256
> +                * digests are zero-padded to 48 bytes, SHA384 used as-is.
> +                */
> +               memset(pcr_buf, 0, PCR_HASH_SIZE);
> +               memcpy(pcr_buf, digest, digest_len);
> +
> +               resp_len = build_simple_response(recvbuf,
> +                                                hw_pcr_extend(pcr_buf, pcr_handle)
> +                                                ? TPM2_RC_FAILURE
> +                                                : TPM2_RC_SUCCESS);
> +               break;
> +       }
> +
> +       case TPM2_CC_PCR_READ: {
> +               const struct tpm2_cmd_pcr_read *c =
> +                       (const struct tpm2_cmd_pcr_read *)sendbuf;
> +               u32 pcr_handle, pcr_mask, digest_len = 0;
> +               bool found = false;
> +               const u8 *sel;
> +               u8 sel_sz;
> +               int i;
> +
> +               if (send_size < sizeof(*c)) {
> +                       resp_len = build_simple_response(recvbuf,
> +                                                        TPM2_RC_SIZE);
> +                       break;
> +               }
> +
> +               /* Only one PCR selection block is supported */
> +               if (be32_to_cpu(c->sel_count) != 1) {
> +                       resp_len = build_simple_response(recvbuf,
> +                                                        TPM2_RC_VALUE);
> +                       break;
> +               }
> +
> +               sel_sz = c->size_of_select;
> +
> +               if (send_size < sizeof(*c) + sel_sz) {
> +                       resp_len = build_simple_response(recvbuf,
> +                                                        TPM2_RC_SIZE);
> +                       break;
> +               }
> +
> +               sel = c->pcr_select;
> +
> +               switch (be16_to_cpu(c->hash_alg)) {
> +               case TPM2_ALG_SHA384:
> +                       digest_len = PCR_HASH_SIZE;
> +                       break;
> +               case TPM2_ALG_SHA256:
> +                       digest_len = TPM2_SHA256_DIGEST_SIZE;
> +                       break;
> +               default:
> +                       resp_len = build_simple_response(recvbuf,
> +                                                        TPM2_RC_VALUE);
> +                       break;
> +               }
> +
> +               if (!digest_len)
> +                       break;
> +
> +               /* Find the first set PCR bit in the selection bitmap */
> +               for (i = 0; i < (int)sel_sz * 8; i++) {
> +                       if (sel[i / 8] & BIT(i % 8)) {
> +                               pcr_handle = (u32)i;
> +                               found = true;
> +                               break;
> +                       }
> +               }
> +
> +               if (!found) {
> +                       log_debug("PCR_READ: no PCR selected\n");
> +                       resp_len = build_simple_response(recvbuf,
> +                                                        TPM2_RC_VALUE);
> +                       break;
> +               }
> +
> +               if (pcr_handle >= HW_PCR_COUNT) {
> +                       log_debug("PCR index %u out of range\n", pcr_handle);
> +                       resp_len = build_simple_response(recvbuf,
> +                                                        TPM2_RC_VALUE);
> +                       break;
> +               }
> +
> +               pcr_mask = BIT(pcr_handle);
> +
> +               /*
> +                * Both SHA256 and SHA384 read the same HW PCR.
> +                * digest_len controls how many bytes of the 48-byte PLM
> +                * value are returned (32 for SHA256, 48 for SHA384).
> +                */
> +               if (hw_pcr_get(pcr_buf, pcr_mask))
> +                       resp_len = build_simple_response(recvbuf,
> +                                                        TPM2_RC_FAILURE);
> +               else
> +                       resp_len = build_pcr_read_response(recvbuf, pcr_buf,
> +                                                          digest_len);
> +               break;
> +       }
> +
> +       default:
> +               log_debug("unsupported command code 0x%08x\n", cc);
> +               resp_len = build_simple_response(recvbuf,
> +                                                TPM2_RC_COMMAND_CODE);
> +               break;
> +       }
> +
> +       *recv_len = resp_len;
> +
> +       return 0;
> +}
> +
> +static int tpm2_versal2_smc_open(struct udevice *dev)
> +{
> +       return 0;
> +}
> +
> +static int tpm2_versal2_smc_close(struct udevice *dev)
> +{
> +       return 0;
> +}
> +
> +static int tpm2_versal2_smc_get_desc(struct udevice *dev, char *buf, int size)
> +{
> +       return snprintf(buf, size, "AMD Versal Gen 2 TPM2 (via TF-A SMC)");
> +}
> +
> +static int tpm2_versal2_smc_probe(struct udevice *dev)
> +{
> +       struct tpm_chip_priv *tpm_priv = dev_get_uclass_priv(dev);
> +       struct tpm2_versal2_priv *priv = dev_get_priv(dev);
> +
> +       priv->pcr_buf = memalign(ARCH_DMA_MINALIGN, PCR_HASH_SIZE);
> +       if (!priv->pcr_buf)
> +               return -ENOMEM;
> +
> +       tpm_priv->version = TPM_V2;
> +       tpm_priv->pcr_count = HW_PCR_COUNT;
> +       tpm_priv->pcr_select_min = 1;
> +
> +       return 0;
> +}
> +
> +static int tpm2_versal2_smc_remove(struct udevice *dev)
> +{
> +       struct tpm2_versal2_priv *priv = dev_get_priv(dev);
> +
> +       free(priv->pcr_buf);
> +
> +       return 0;
> +}
> +
> +static const struct tpm_ops tpm2_versal2_smc_ops = {
> +       .open = tpm2_versal2_smc_open,
> +       .close = tpm2_versal2_smc_close,
> +       .get_desc = tpm2_versal2_smc_get_desc,
> +       .xfer = tpm2_versal2_smc_xfer,
> +};
> +
> +U_BOOT_DRIVER(tpm2_versal2_smc) = {
> +       .name = "tpm2_versal2_smc",
> +       .id = UCLASS_TPM,
> +       .ops = &tpm2_versal2_smc_ops,
> +       .probe = tpm2_versal2_smc_probe,
> +       .remove = tpm2_versal2_smc_remove,
> +       .priv_auto = sizeof(struct tpm2_versal2_priv),
> +};
> --
> 2.34.1
>


More information about the U-Boot mailing list