[RFC PATCH v3 8/8] tpm_crb: Raise the limits for Amazon NitroTPM
From: Surendran Kanagaraj
Date: Thu Nov 12 2026 - 12:09:53 EST
In rare scenarios NitroTPM can enter a mode where it either sends
TPM2_RC_RETRY for longer than LONG duration or delays
command completion for longer than the TPM 2.0 durations allow.
When this happens during auth session start, the driver disables
the chip and every subsequent request fails:
tpm tpm0: in retry loop
tpm tpm0: tpm2_load_context: failed with a TPM error 0x0922
Add a quirk table to the CRB driver keyed by the vendor ID from the
interface ID register, and apply it from ->adjust_limits(). For
NitroTPM, raise the SHORT and MEDIUM durations to 30 s, which covers
the longest unavailability with some headroom, and LONG to 45 s. With
the back-off doubling from 20 ms, a 45 s LONG duration lets the driver
resubmit a command for about 40.9 s, so the retry loop covers the
entire unavailability window.
These are upper bounds, not expected waits. A command that completes
sooner ends the wait as soon as the poll sees it. Add TPM_VID_AMZN
next to the vendor ID list in tpm.h.
Tested on an EC2 instance with NitroTPM by inducing multiple
unavailability windows with no TPM errors, and in QEMU with swtpm by
holding the TPM in TPM2_RC_RETRY and stalling command completion for
longer than 2s.
Signed-off-by: Surendran Kanagaraj <surenkj@xxxxxxxxxx>
---
drivers/char/tpm/tpm_crb.c | 50 ++++++++++++++++++++++++++++++++++++++
include/linux/tpm.h | 1 +
2 files changed, 51 insertions(+)
diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c
index 7c1011eb855a..0e18f61cfddf 100644
--- a/drivers/char/tpm/tpm_crb.c
+++ b/drivers/char/tpm/tpm_crb.c
@@ -13,6 +13,7 @@
#include <linux/acpi.h>
#include <linux/highmem.h>
+#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/rculist.h>
#include <linux/module.h>
#include <linux/platform_device.h>
@@ -26,6 +27,14 @@
#define ACPI_SIG_TPM2 "TPM2"
#define TPM_CRB_MAX_RESOURCES 3
+/* TPM_CRB_INTF_ID_x vendor ID field, bits 47:32 of the 64-bit register */
+#define TPM_CRB_INTF_ID_VID(intf_id) (((intf_id) >> 32) & 0xffff)
+
+/* Longest NitroTPM unavailability, with some headroom */
+#define TPM_CRB_AMZN_LIMIT_US (30 * USEC_PER_SEC)
+/* LONG also bounds the retry loop: 45 s lets it resubmit for 40.9 s */
+#define TPM_CRB_AMZN_LONG_US (45 * USEC_PER_SEC)
+
static const guid_t crb_acpi_start_guid =
GUID_INIT(0x6BBF6CAB, 0x5463, 0x4714,
0xB7, 0xCD, 0xF0, 0x20, 0x3C, 0x03, 0x68, 0xD4);
@@ -663,6 +672,46 @@ static bool crb_req_canceled(struct tpm_chip *chip, u8 status)
return (cancel & CRB_CANCEL_INVOKE) == CRB_CANCEL_INVOKE;
}
+/*
+ * Read the vendor ID from the interface ID register. Returns -ENODEV when
+ * the head registers are not mapped for this start method.
+ */
+static int crb_vendor_id(struct crb_priv *priv)
+{
+ u64 intf_id;
+
+ if (!priv->regs_h)
+ return -ENODEV;
+
+ intf_id = lo_hi_readq(&priv->regs_h->intf_id);
+
+ return TPM_CRB_INTF_ID_VID(intf_id);
+}
+
+/* Limits keyed by the vendor ID of the interface ID register */
+static const struct tpm_limits_quirk crb_limits_quirks[] = {
+ {
+ .id = TPM_VID_AMZN,
+ .time_us = {
+ [TPM_LIMIT_SHORT] = TPM_CRB_AMZN_LIMIT_US,
+ [TPM_LIMIT_MEDIUM] = TPM_CRB_AMZN_LIMIT_US,
+ [TPM_LIMIT_LONG] = TPM_CRB_AMZN_LONG_US,
+ },
+ },
+};
+
+static void crb_adjust_limits(struct tpm_chip *chip, struct tpm_limits *p)
+{
+ struct crb_priv *priv = dev_get_drvdata(&chip->dev);
+ int vid = crb_vendor_id(priv);
+
+ if (vid < 0)
+ return;
+
+ tpm_limits_apply_quirks(p, crb_limits_quirks,
+ ARRAY_SIZE(crb_limits_quirks), vid);
+}
+
static const struct tpm_class_ops tpm_crb = {
.flags = TPM_OPS_AUTO_STARTUP,
.status = crb_status,
@@ -674,6 +723,7 @@ static const struct tpm_class_ops tpm_crb = {
.cmd_ready = crb_cmd_ready,
.request_locality = crb_request_locality,
.relinquish_locality = crb_relinquish_locality,
+ .adjust_limits = crb_adjust_limits,
.req_complete_mask = CRB_DRV_STS_COMPLETE,
.req_complete_val = CRB_DRV_STS_COMPLETE,
};
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index a4b7282d88b5..f557c1a32388 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -220,6 +220,7 @@ static inline enum tpm2_mso_type tpm2_handle_mso(u32 handle)
#define TPM_VID_STM 0x104A
#define TPM_VID_ATML 0x1114
#define TPM_VID_IFX 0x15D1
+#define TPM_VID_AMZN 0x0EC2
enum tpm_chip_flags {
TPM_CHIP_FLAG_BOOTSTRAPPED = BIT(0),
--
2.47.3