[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