Linux Integrity Measurement development
 help / color / mirror / Atom feed
* [PATCH v2 0/2] tpm: Handle transient NitroTPM unavailability
@ 2026-10-16  4:37 Surendran Kanagaraj
  2026-10-16  4:37 ` [PATCH v2 1/2] tpm: Add support for updating TPM2 durations Surendran Kanagaraj
  2026-10-16  4:37 ` [PATCH v2 2/2] tpm_crb: Raise durations for Amazon NitroTPM Surendran Kanagaraj
  0 siblings, 2 replies; 5+ messages in thread
From: Surendran Kanagaraj @ 2026-10-16  4:37 UTC (permalink / raw)
  To: Jarkko Sakkinen, Peter Huewe, Jason Gunthorpe
  Cc: linux-integrity, linux-kernel, Breno Leitao, nh-open-source,
	Alexander Graf, Gunnar Kudrjavets, Josh Levinson

TPM commands need to complete within the duration defined in the
TPM2 spec or keep answering TPM2_RC_RETRY for at most TPM2_DURATION_LONG
(2s).

Devices that have different timeout requirements than the TPM2 spec
can either exhaust the retry budget or delay the command completion.
NitroTPM can very rarely enter a mode where it either sends
TPM2_RC_RETRY for more than 2s or completes the command late.

When this happens in tpm2_load_null() during auth session start, the
driver sets TPM_CHIP_FLAG_DISABLE and fails all subsequent TPM requests.

    tpm tpm0: in retry loop
    tpm tpm0: tpm2_load_context: failed with a TPM error 0x0922

Add support to override command durations and TPM2_RC_RETRY budget
based on chip->duration. tpm2_get_timeouts() fills the array with TPM2
defaults and lets the driver adjust the durations (patch 1) as TPM 1.x
does. Raise the TPM_SHORT, TPM_MEDIUM and TPM_LONG durations to 30s for
NitroTPM (patch 2).

V1 -> V2:
- Drop chip->busy_timeout_ms. TPM2 durations and the TPM2_RC_RETRY
  budget now come from chip->duration.
- Map TPM2_CC_SELF_TEST and TPM2_CC_VERIFY_SIGNATURE to TPM_LONG with
  floors of 3s and 30s since their durations do not fit SHORT, MEDIUM,
  LONG durations.
- Keep the TPM2 defaults for commands sent before tpm2_get_timeouts()
  has run such as the one in tpm2_probe() and keep the 2s retry
  budget for TPM 1.x chips.
- Keep the first TPM2_RC_RETRY wait at TPM2_DURATION_SHORT instead of
  chip->duration[TPM_SHORT]. No TPM2 command maps to TPM_SHORT today
  and a driver that raises TPM_SHORT for its commands should not also
  lengthen the first retry wait.
- Return early from tpm_tis_update_durations() for TPM2 chips, since
  its overrides are for TPM 1.x.
- Raise TPM_SHORT, TPM_MEDIUM and TPM_LONG to 30s for NitroTPM.
- Replace the vendor quirk table in tpm_crb with a plain vendor ID
  check.
- Use the TPM_CRB_ prefix for the new tpm_crb macros.
- Rename the patches from "tpm: Add per-chip timeout for transient
  unavailability" and "tpm_crb: Raise timeouts for Amazon NitroTPM".

Tested on an EC2 instance with NitroTPM by inducing unavailability
windows under HMAC session workloads and in QEMU with swtpm on
tpm_crb, tpm_tis, tpm_tis_i2c, tpm_ibmvtpm and tpm_vtpm_proxy.

v1: https://lore.kernel.org/linux-integrity/20260923142834.16786-1-surenkj@amazon.com/

Surendran Kanagaraj (2):
  tpm: Add support for updating TPM2 durations
  tpm_crb: Raise durations for Amazon NitroTPM

 drivers/char/tpm/tpm-interface.c | 11 ++--
 drivers/char/tpm/tpm.h           |  2 +-
 drivers/char/tpm/tpm2-cmd.c      | 90 ++++++++++++++++++++++++--------
 drivers/char/tpm/tpm_crb.c       | 44 ++++++++++++++++
 drivers/char/tpm/tpm_tis_core.c  |  4 ++
 5 files changed, 125 insertions(+), 26 deletions(-)


base-commit: 9708d29f225bd326165bfec1962d13f0a00b91ba
-- 
2.47.3


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH v2 1/2] tpm: Add support for updating TPM2 durations
  2026-10-16  4:37 [PATCH v2 0/2] tpm: Handle transient NitroTPM unavailability Surendran Kanagaraj
@ 2026-10-16  4:37 ` Surendran Kanagaraj
  2026-10-16  4:37 ` [PATCH v2 2/2] tpm_crb: Raise durations for Amazon NitroTPM Surendran Kanagaraj
  1 sibling, 0 replies; 5+ messages in thread
From: Surendran Kanagaraj @ 2026-10-16  4:37 UTC (permalink / raw)
  To: Jarkko Sakkinen, Peter Huewe, Jason Gunthorpe
  Cc: linux-integrity, linux-kernel, Breno Leitao, nh-open-source,
	Alexander Graf, Gunnar Kudrjavets, Josh Levinson

TPM commands need to complete within the command duration defined
in the TPM2 spec or keep answering TPM2_RC_RETRY for at most
TPM2_DURATION_LONG (2s).

Devices that have different timeout requirements than the TPM2 spec
could exhaust the retry budget or exceed the command duration. These
failures disable the device during an auth session, failing all
subsequent TPM requests. Worse, when a TPM2_CC_FLUSH_CONTEXT command
fails, it leaks the TPM's transient memory:

    tpm tpm0: in retry loop
    tpm tpm0: tpm2_load_context: failed with a TPM error 0x0922
    ...
    tpm tpm0: A TPM error (2338) occurred flushing context

Fix this by overriding the command durations and retry budget
based on chip->duration and letting the driver modify the durations
during tpm2_get_timeouts() as TPM 1.x does. Chips without
the override keep the current durations.

Tested in QEMU with swtpm on tpm_crb, tpm_tis, tpm_tis_i2c, tpm_ibmvtpm
and tpm_vtpm_proxy.

Assisted-by: LLM
Signed-off-by: Surendran Kanagaraj <surenkj@amazon.com>
---
V1 -> V2:
- Drop chip->busy_timeout_ms. TPM2 durations and the TPM2_RC_RETRY
  budget now come from chip->duration, which drivers adjust through
  ->update_durations() as TPM 1.x does.
- Map TPM2_CC_SELF_TEST and TPM2_CC_VERIFY_SIGNATURE to TPM_LONG with
  floors of 3s and 30s since their durations do not fit SHORT, MEDIUM,
  LONG durations.
- Keep the TPM2 defaults for commands sent before tpm2_get_timeouts()
  has run such as the one in tpm2_probe() and keep the 2s retry
  budget for TPM 1.x chips.
- Keep the first TPM2_RC_RETRY wait at TPM2_DURATION_SHORT instead of
  chip->duration[TPM_SHORT].
- Return early from tpm_tis_update_durations() for TPM2 chips, since
  its overrides are for TPM 1.x.
- Rename from "tpm: Add per-chip timeout for transient unavailability".

v1: https://lore.kernel.org/linux-integrity/20260923142834.16786-2-surenkj@amazon.com/
---
 drivers/char/tpm/tpm-interface.c | 11 ++--
 drivers/char/tpm/tpm.h           |  2 +-
 drivers/char/tpm/tpm2-cmd.c      | 90 ++++++++++++++++++++++++--------
 drivers/char/tpm/tpm_tis_core.c  |  4 ++
 4 files changed, 81 insertions(+), 26 deletions(-)

diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index e0385b16616b..747e20ab421b 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -53,7 +53,7 @@ MODULE_PARM_DESC(suspend_pcr,
 unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal)
 {
 	if (chip->flags & TPM_CHIP_FLAG_TPM2)
-		return tpm2_calc_ordinal_duration(ordinal);
+		return tpm2_calc_ordinal_duration(chip, ordinal);
 	else
 		return tpm1_calc_ordinal_duration(chip, ordinal);
 }
@@ -214,7 +214,7 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, void *buf, size_t bufsiz)
  *
  * A wrapper around tpm_try_transmit() that handles TPM2_RC_RETRY returns from
  * the TPM and retransmits the command after a delay up to a maximum wait of
- * TPM2_DURATION_LONG.
+ * chip->duration[TPM_LONG] (TPM2_DURATION_LONG until the durations are set).
  *
  * Note that TPM 1.x never returns TPM2_RC_RETRY so the retry logic is TPM 2.0
  * only.
@@ -229,12 +229,17 @@ ssize_t tpm_transmit(struct tpm_chip *chip, u8 *buf, size_t bufsiz)
 	/* space for header and handles */
 	u8 save[TPM_HEADER_SIZE + 3*sizeof(u32)];
 	unsigned int delay_msec = TPM2_DURATION_SHORT;
+	unsigned int max_delay_msec = TPM2_DURATION_LONG;
 	u32 rc = 0;
 	ssize_t ret;
 	const size_t save_size = min(sizeof(save), bufsiz);
 	/* the command code is where the return code will be */
 	u32 cc = be32_to_cpu(header->return_code);
 
+	if ((chip->flags & TPM_CHIP_FLAG_TPM2) &&
+	    (chip->flags & TPM_CHIP_FLAG_HAVE_TIMEOUTS))
+		max_delay_msec = jiffies_to_msecs(chip->duration[TPM_LONG]);
+
 	/*
 	 * Subtlety here: if we have a space, the handles will be
 	 * transformed, so when we restore the header we also have to
@@ -256,7 +261,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, u8 *buf, size_t bufsiz)
 		if (rc == TPM2_RC_TESTING && cc == TPM2_CC_SELF_TEST)
 			break;
 
-		if (delay_msec > TPM2_DURATION_LONG) {
+		if (delay_msec > max_delay_msec) {
 			if (rc == TPM2_RC_RETRY)
 				dev_err(&chip->dev, "in retry loop\n");
 			else
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index fa554c5ad80b..457eba8d03dd 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -119,7 +119,7 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id,
 ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip);
 int tpm2_auto_startup(struct tpm_chip *chip);
 void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type);
-unsigned long tpm2_calc_ordinal_duration(u32 ordinal);
+unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal);
 int tpm2_probe(struct tpm_chip *chip);
 int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip);
 int tpm2_find_cc(struct tpm_chip *chip, u32 cc);
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 4b2bb2ddc321..83a9e78ac843 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -41,55 +41,101 @@ int tpm2_find_hash_alg(unsigned int crypto_id)
 }
 EXPORT_SYMBOL_GPL(tpm2_find_hash_alg);
 
+/*
+ * The TPM2 defaults of chip->duration[] in milliseconds, indexed by
+ * enum tpm_duration.
+ */
+static const unsigned int tpm2_durations_ms[TPM_NUM_DURATIONS] = {
+	[TPM_SHORT] = TPM2_DURATION_SHORT,
+	[TPM_MEDIUM] = TPM2_DURATION_MEDIUM,
+	[TPM_LONG] = TPM2_DURATION_LONG,
+	[TPM_LONG_LONG] = TPM2_DURATION_LONG_LONG,
+};
+
 int tpm2_get_timeouts(struct tpm_chip *chip)
 {
+	unsigned long durations[3];
+	int i;
+
 	chip->timeout_a = msecs_to_jiffies(TPM2_TIMEOUT_A);
 	chip->timeout_b = msecs_to_jiffies(TPM2_TIMEOUT_B);
 	chip->timeout_c = msecs_to_jiffies(TPM2_TIMEOUT_C);
 	chip->timeout_d = msecs_to_jiffies(TPM2_TIMEOUT_D);
+
+	for (i = 0; i < TPM_NUM_DURATIONS; i++)
+		chip->duration[i] = msecs_to_jiffies(tpm2_durations_ms[i]);
+
+	/* Provide the ability for vendor overrides of duration values. */
+	if (chip->ops->update_durations)
+		chip->ops->update_durations(chip, durations);
+
+	if (chip->duration_adjusted) {
+		chip->duration[TPM_SHORT] = durations[0];
+		chip->duration[TPM_MEDIUM] = durations[1];
+		chip->duration[TPM_LONG] = durations[2];
+	}
+
 	chip->flags |= TPM_CHIP_FLAG_HAVE_TIMEOUTS;
 	return 0;
 }
 
 /*
- * Contains the maximum durations in milliseconds for TPM2 commands.
+ * Maps each TPM2 command to the chip->duration[] entry that holds its
+ * maximum duration. min_ms, when set, is a lower bound in milliseconds.
  */
 static const struct {
 	unsigned long ordinal;
-	unsigned long duration;
+	enum tpm_duration index;
+	unsigned int min_ms;
 } tpm2_ordinal_duration_map[] = {
-	{TPM2_CC_STARTUP, 750},
-	{TPM2_CC_SELF_TEST, 3000},
-	{TPM2_CC_GET_RANDOM, 2000},
-	{TPM2_CC_SEQUENCE_UPDATE, 750},
-	{TPM2_CC_SEQUENCE_COMPLETE, 750},
-	{TPM2_CC_EVENT_SEQUENCE_COMPLETE, 750},
-	{TPM2_CC_HASH_SEQUENCE_START, 750},
-	{TPM2_CC_VERIFY_SIGNATURE, 30000},
-	{TPM2_CC_PCR_EXTEND, 750},
-	{TPM2_CC_HIERARCHY_CONTROL, 2000},
-	{TPM2_CC_HIERARCHY_CHANGE_AUTH, 2000},
-	{TPM2_CC_GET_CAPABILITY, 750},
-	{TPM2_CC_NV_READ, 2000},
-	{TPM2_CC_CREATE_PRIMARY, 300000},
-	{TPM2_CC_CREATE, 300000},
-	{TPM2_CC_CREATE_LOADED, 300000},
+	{TPM2_CC_STARTUP, TPM_MEDIUM},
+	{TPM2_CC_SELF_TEST, TPM_LONG, 3000},
+	{TPM2_CC_GET_RANDOM, TPM_LONG},
+	{TPM2_CC_SEQUENCE_UPDATE, TPM_MEDIUM},
+	{TPM2_CC_SEQUENCE_COMPLETE, TPM_MEDIUM},
+	{TPM2_CC_EVENT_SEQUENCE_COMPLETE, TPM_MEDIUM},
+	{TPM2_CC_HASH_SEQUENCE_START, TPM_MEDIUM},
+	{TPM2_CC_VERIFY_SIGNATURE, TPM_LONG, 30000},
+	{TPM2_CC_PCR_EXTEND, TPM_MEDIUM},
+	{TPM2_CC_HIERARCHY_CONTROL, TPM_LONG},
+	{TPM2_CC_HIERARCHY_CHANGE_AUTH, TPM_LONG},
+	{TPM2_CC_GET_CAPABILITY, TPM_MEDIUM},
+	{TPM2_CC_NV_READ, TPM_LONG},
+	{TPM2_CC_CREATE_PRIMARY, TPM_LONG_LONG},
+	{TPM2_CC_CREATE, TPM_LONG_LONG},
+	{TPM2_CC_CREATE_LOADED, TPM_LONG_LONG},
 };
 
 /**
  * tpm2_calc_ordinal_duration() - Calculate the maximum command duration
+ * @chip: TPM chip to use.
  * @ordinal: TPM command ordinal.
  *
  * Returns the maximum amount of time the chip is expected by kernel to
  * take in jiffies.
  */
-unsigned long tpm2_calc_ordinal_duration(u32 ordinal)
+unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal)
 {
+	enum tpm_duration index;
+	unsigned long duration;
+	unsigned int min_ms;
 	int i;
 
-	for (i = 0; i < ARRAY_SIZE(tpm2_ordinal_duration_map); i++)
-		if (ordinal == tpm2_ordinal_duration_map[i].ordinal)
-			return msecs_to_jiffies(tpm2_ordinal_duration_map[i].duration);
+	for (i = 0; i < ARRAY_SIZE(tpm2_ordinal_duration_map); i++) {
+		if (ordinal != tpm2_ordinal_duration_map[i].ordinal)
+			continue;
+
+		index = tpm2_ordinal_duration_map[i].index;
+		min_ms = tpm2_ordinal_duration_map[i].min_ms;
+
+		/* chip->duration[] is unset until tpm2_get_timeouts() runs. */
+		if (chip->flags & TPM_CHIP_FLAG_HAVE_TIMEOUTS)
+			duration = chip->duration[index];
+		else
+			duration = msecs_to_jiffies(tpm2_durations_ms[index]);
+
+		return max(duration, msecs_to_jiffies(min_ms));
+	}
 
 	return msecs_to_jiffies(TPM2_DURATION_DEFAULT);
 }
diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c
index 143ef160820e..3738a5dd33e9 100644
--- a/drivers/char/tpm/tpm_tis_core.c
+++ b/drivers/char/tpm/tpm_tis_core.c
@@ -645,6 +645,10 @@ static void tpm_tis_update_durations(struct tpm_chip *chip,
 	int i, rc;
 	cap_t cap;
 
+	/* The vendor overrides below are for TPM 1.x chips only. */
+	if (chip->flags & TPM_CHIP_FLAG_TPM2)
+		return;
+
 	chip->duration_adjusted = false;
 
 	if (chip->ops->clk_enable != NULL)
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH v2 2/2] tpm_crb: Raise durations for Amazon NitroTPM
  2026-10-16  4:37 [PATCH v2 0/2] tpm: Handle transient NitroTPM unavailability Surendran Kanagaraj
  2026-10-16  4:37 ` [PATCH v2 1/2] tpm: Add support for updating TPM2 durations Surendran Kanagaraj
@ 2026-10-16  4:37 ` Surendran Kanagaraj
  2026-10-16 16:27   ` Jonathan McDowell
  1 sibling, 1 reply; 5+ messages in thread
From: Surendran Kanagaraj @ 2026-10-16  4:37 UTC (permalink / raw)
  To: Jarkko Sakkinen, Peter Huewe, Jason Gunthorpe
  Cc: linux-integrity, linux-kernel, Breno Leitao, nh-open-source,
	Alexander Graf, Gunnar Kudrjavets, Josh Levinson

TPM commands need to complete within the command duration defined in
the TPM2 spec or keep answering TPM2_RC_RETRY for at most
TPM2_DURATION_LONG (2s).

In rare scenarios NitroTPM can enter a mode where it either sends
TPM2_RC_RETRY for more than 2s or delays command completion. 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 support for vendor duration overrides to the CRB driver, based on
the vendor ID read from the interface ID register. For NitroTPM, raise
TPM_SHORT, TPM_MEDIUM and TPM_LONG to 30s which covers the longest
unavailability time along with some headroom.

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.

Assisted-by: LLM
Signed-off-by: Surendran Kanagaraj <surenkj@amazon.com>
---
V1 -> V2:
- Raise the durations through ->update_durations() instead of setting
  chip->busy_timeout_ms.
- Raise TPM_SHORT, TPM_MEDIUM and TPM_LONG to 30s.
- Replace the vendor quirk table with a plain vendor ID check.
- Use the TPM_CRB_ prefix for the new macros.
- Rename from "tpm_crb: Raise timeouts for Amazon NitroTPM".

v1: https://lore.kernel.org/linux-integrity/20260923142834.16786-3-surenkj@amazon.com/
---
 drivers/char/tpm/tpm_crb.c | 44 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)

diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c
index ceb4100ba400..6ba9016d6463 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,15 @@
 #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)
+
+/* Amazon NitroTPM */
+#define TPM_CRB_INTF_VID_AMZN		0x0ec2
+
+/* Longest NitroTPM unavailability, with some headroom */
+#define TPM_CRB_AMZN_DURATION_MS	30000
+
 static const guid_t crb_acpi_start_guid =
 	GUID_INIT(0x6BBF6CAB, 0x5463, 0x4714,
 		  0xB7, 0xCD, 0xF0, 0x20, 0x3C, 0x03, 0x68, 0xD4);
@@ -521,6 +531,39 @@ 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);
+}
+
+static void crb_update_durations(struct tpm_chip *chip,
+				 unsigned long *duration_cap)
+{
+	struct crb_priv *priv = dev_get_drvdata(&chip->dev);
+	unsigned long duration;
+
+	chip->duration_adjusted = false;
+
+	if (crb_vendor_id(priv) == TPM_CRB_INTF_VID_AMZN) {
+		duration = msecs_to_jiffies(TPM_CRB_AMZN_DURATION_MS);
+		duration_cap[TPM_SHORT] = duration;
+		duration_cap[TPM_MEDIUM] = duration;
+		duration_cap[TPM_LONG] = duration;
+		chip->duration_adjusted = true;
+	}
+}
+
 static const struct tpm_class_ops tpm_crb = {
 	.flags = TPM_OPS_AUTO_STARTUP,
 	.status = crb_status,
@@ -532,6 +575,7 @@ static const struct tpm_class_ops tpm_crb = {
 	.cmd_ready = crb_cmd_ready,
 	.request_locality = crb_request_locality,
 	.relinquish_locality = crb_relinquish_locality,
+	.update_durations = crb_update_durations,
 	.req_complete_mask = CRB_DRV_STS_COMPLETE,
 	.req_complete_val = CRB_DRV_STS_COMPLETE,
 };
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH v2 2/2] tpm_crb: Raise durations for Amazon NitroTPM
  2026-10-16  4:37 ` [PATCH v2 2/2] tpm_crb: Raise durations for Amazon NitroTPM Surendran Kanagaraj
@ 2026-10-16 16:27   ` Jonathan McDowell
  2026-10-16 16:38     ` Graf (AWS), Alexander
  0 siblings, 1 reply; 5+ messages in thread
From: Jonathan McDowell @ 2026-10-16 16:27 UTC (permalink / raw)
  To: Surendran Kanagaraj
  Cc: Jarkko Sakkinen, Peter Huewe, Jason Gunthorpe, linux-integrity,
	linux-kernel, Breno Leitao, nh-open-source, Alexander Graf,
	Gunnar Kudrjavets, Josh Levinson

On Fri, Oct 16, 2026 at 04:37:33AM +0000, Surendran Kanagaraj wrote:
> TPM commands need to complete within the command duration defined in
> the TPM2 spec or keep answering TPM2_RC_RETRY for at most
> TPM2_DURATION_LONG (2s).
> 
> In rare scenarios NitroTPM can enter a mode where it either sends
> TPM2_RC_RETRY for more than 2s or delays command completion. 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 support for vendor duration overrides to the CRB driver, based on
> the vendor ID read from the interface ID register. For NitroTPM, raise
> TPM_SHORT, TPM_MEDIUM and TPM_LONG to 30s which covers the longest
> unavailability time along with some headroom.

Bumping all of these to 30s seems excessive. Is it really the case that 
all commands can potentially take this long? Is that because of 
non-TPM-related operations Nitro might be doing behind the scenes?

> 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.
> 
> Assisted-by: LLM
> Signed-off-by: Surendran Kanagaraj <surenkj@amazon.com>
> ---
> V1 -> V2:
> - Raise the durations through ->update_durations() instead of setting
>   chip->busy_timeout_ms.
> - Raise TPM_SHORT, TPM_MEDIUM and TPM_LONG to 30s.
> - Replace the vendor quirk table with a plain vendor ID check.
> - Use the TPM_CRB_ prefix for the new macros.
> - Rename from "tpm_crb: Raise timeouts for Amazon NitroTPM".
> 
> v1: https://lore.kernel.org/linux-integrity/20260923142834.16786-3-surenkj@amazon.com/
> ---
>  drivers/char/tpm/tpm_crb.c | 44 ++++++++++++++++++++++++++++++++++++++
>  1 file changed, 44 insertions(+)
> 
> diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c
> index ceb4100ba400..6ba9016d6463 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,15 @@
>  #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)
> +
> +/* Amazon NitroTPM */
> +#define TPM_CRB_INTF_VID_AMZN		0x0ec2

This should really live with the other VID definitions in 
linux/include/tpm.h

> +/* Longest NitroTPM unavailability, with some headroom */
> +#define TPM_CRB_AMZN_DURATION_MS	30000
> +
>  static const guid_t crb_acpi_start_guid =
>  	GUID_INIT(0x6BBF6CAB, 0x5463, 0x4714,
>  		  0xB7, 0xCD, 0xF0, 0x20, 0x3C, 0x03, 0x68, 0xD4);
> @@ -521,6 +531,39 @@ 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);
> +}
> +
> +static void crb_update_durations(struct tpm_chip *chip,
> +				 unsigned long *duration_cap)
> +{
> +	struct crb_priv *priv = dev_get_drvdata(&chip->dev);
> +	unsigned long duration;
> +
> +	chip->duration_adjusted = false;
> +
> +	if (crb_vendor_id(priv) == TPM_CRB_INTF_VID_AMZN) {
> +		duration = msecs_to_jiffies(TPM_CRB_AMZN_DURATION_MS);
> +		duration_cap[TPM_SHORT] = duration;
> +		duration_cap[TPM_MEDIUM] = duration;
> +		duration_cap[TPM_LONG] = duration;
> +		chip->duration_adjusted = true;
> +	}
> +}
> +
>  static const struct tpm_class_ops tpm_crb = {
>  	.flags = TPM_OPS_AUTO_STARTUP,
>  	.status = crb_status,
> @@ -532,6 +575,7 @@ static const struct tpm_class_ops tpm_crb = {
>  	.cmd_ready = crb_cmd_ready,
>  	.request_locality = crb_request_locality,
>  	.relinquish_locality = crb_relinquish_locality,
> +	.update_durations = crb_update_durations,
>  	.req_complete_mask = CRB_DRV_STS_COMPLETE,
>  	.req_complete_val = CRB_DRV_STS_COMPLETE,
>  };
> -- 
> 2.47.3
> 
> 

J.

-- 
... She's the one for me. She's all I really need, oh yeah.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH v2 2/2] tpm_crb: Raise durations for Amazon NitroTPM
  2026-10-16 16:27   ` Jonathan McDowell
@ 2026-10-16 16:38     ` Graf (AWS), Alexander
  0 siblings, 0 replies; 5+ messages in thread
From: Graf (AWS), Alexander @ 2026-10-16 16:38 UTC (permalink / raw)
  To: Jonathan McDowell, Kanagaraj, Surendran
  Cc: Jarkko Sakkinen, Peter Huewe, Jason Gunthorpe,
	linux-integrity@vger.kernel.org, linux-kernel@vger.kernel.org,
	Breno Leitao, nh-open-source@amazon.com, Kudrjavets, Gunnar,
	Levinson, Josh


On 16.10.26 18:27, Jonathan McDowell wrote:
> On Fri, Oct 16, 2026 at 04:37:33AM +0000, Surendran Kanagaraj wrote:
>> TPM commands need to complete within the command duration defined in
>> the TPM2 spec or keep answering TPM2_RC_RETRY for at most
>> TPM2_DURATION_LONG (2s).
>>
>> In rare scenarios NitroTPM can enter a mode where it either sends
>> TPM2_RC_RETRY for more than 2s or delays command completion. 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 support for vendor duration overrides to the CRB driver, based on
>> the vendor ID read from the interface ID register. For NitroTPM, raise
>> TPM_SHORT, TPM_MEDIUM and TPM_LONG to 30s which covers the longest
>> unavailability time along with some headroom.
> Bumping all of these to 30s seems excessive. Is it really the case that
> all commands can potentially take this long? Is that because of
> non-TPM-related operations Nitro might be doing behind the scenes?


We won't reach the 30s, which is exactly why it's a safe default for the 
safety net that these timeouts are :).


Alex

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-10-16 16:55 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-16  4:37 [PATCH v2 0/2] tpm: Handle transient NitroTPM unavailability Surendran Kanagaraj
2026-10-16  4:37 ` [PATCH v2 1/2] tpm: Add support for updating TPM2 durations Surendran Kanagaraj
2026-10-16  4:37 ` [PATCH v2 2/2] tpm_crb: Raise durations for Amazon NitroTPM Surendran Kanagaraj
2026-10-16 16:27   ` Jonathan McDowell
2026-10-16 16:38     ` Graf (AWS), Alexander

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox