[PATCH 2/4] tpm: Add AMD Versal Gen 2 TPM2 driver
Begari, Padmarao
Padmarao.Begari at amd.com
Sun Mar 29 20:55:32 CEST 2026
[AMD Official Use Only - AMD Internal Distribution Only]
Hi Ilias,
> From: Ilias Apalodimas <ilias.apalodimas at linaro.org>
> Sent: Friday, March 27, 2026 6:51 PM
> To: Begari, Padmarao <Padmarao.Begari at amd.com>
> Cc: u-boot at lists.denx.de; Simek, Michal <michal.simek at amd.com>; git (AMD-
> Xilinx) <git at amd.com>; Tom Rini <trini at konsulko.com>
> Subject: Re: [PATCH 2/4] tpm: Add AMD Versal Gen 2 TPM2 driver
>
> Caution: This message originated from an External Source. Use proper caution
> when opening attachments, clicking links, or responding.
>
>
> 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.
Agree. I will remove the hex values from the comment since the command codes are
already defined in tpm-v2.h. I will check and replace the local struct definitions from
tpm-v2.h / tpm-common.h.
>
> Is there anything special with this driver that doesn't adhere to the TPM specs?
Yes. There are differences from standard TPM2 behavior, caused by the PLM XilOCP hardware:
1. Only 8 PCRs (0–7) are supported instead of the 24 in the standard TPM2.
2. SHA-256 and SHA-384 banks share the same underlying 48-byte hardware
register-extending with either algorithm updates the same PLM HW PCR,
unlike a standard TPM2 where each algorithm maintains a fully independent bank.
3. SHA-256 digests (32 bytes) are zero-padded to 48 bytes before being passed
to PLM, because PLM only accepts 48-byte (SHA-384-sized) inputs. The resulting
PCR value will differ from what a standard TPM2 SHA-256 bank would store.
4. TPM2_CC_STARTUP and TPM2_CC_SELF_TEST are no-ops: Standard TPM2 requires a
proper startup and self-test sequence. Here both always return success
immediately because the PLM PCR module is always ready.
> Is there any reason you can't use drivers/tpm2_tis_core.c and just define the bus
> operations?
tpm2_tis_core.c is designed for TIS hardware that exposes memory-mapped TPM
registers - locality, status, FIFO, burst count, and timeouts. Its bus
abstraction (phy_ops->read_bytes / write_bytes) maps directly to register
reads and writes at fixed TIS offsets:
phy_ops->read_bytes(dev, TPM_ACCESS(loc), 1, &locality);
phy_ops->read_bytes(dev, TPM_STS(chip->locality), 1, status);
PLM XilOCP has none of these registers. It is accessed as an IPC service
through TF-A SMC calls (xilinx_pm_request -> smc_call_enhanced). Even if the
bus operations were wrapped to make SMC calls, the TIS state machine -
locality negotiation, FIFO management, status polling, burst-count throttling
- does not exist in PLM and cannot be mapped to SMC calls.
The closest existing driver structurally is tpm2_ftpm_tee.c, which also uses a
simple transceive()/xfer() interface to forward raw TPM2 command bytes to
firmware. However, ftpm_tee_transceive() is a pure pass-through: it sends the
raw command buffer to the fTPM OP-TEE TA without inspecting it because there
is a complete TPM2 engine on the other side that handles all command parsing,
authorization sessions, key management, and response construction.
PLM XilOCP is not a full TPM2 engine - the SMC interface exposes no session
handling, key hierarchy, NV storage, or general TPM2 command dispatcher. Of
its 17 APIs, only XOCP_API_EXTEND_HWPCR and XOCP_API_GET_HWPCR are used by
this driver. Because of this, the driver cannot simply forward raw TPM2 bytes
and let firmware handle them. Instead, it must parse each command code itself,
call the appropriate PLM API, and construct a well-formed TPM2 response - which
is exactly what tpm2_versal2_smc_xfer() does.
The xfer()-based approach is therefore not a workaround but the correct
minimal design: it exposes only the TPM2 interface that PLM backs,
without emulating commands that have no implementation behind them.
Regards
Padmarao
>
> 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