[PATCH 2/4] tpm: Add AMD Versal Gen 2 TPM2 driver
Padmarao Begari
padmarao.begari at amd.com
Fri Mar 27 13:52:56 CET 2026
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
+ *
+ * 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