From: Surendran Kanagaraj <surenkj@amazon.com>
To: Jarkko Sakkinen <jarkko@kernel.org>,
Peter Huewe <peterhuewe@gmx.de>, Jason Gunthorpe <jgg@ziepe.ca>
Cc: <linux-integrity@vger.kernel.org>, <linux-kernel@vger.kernel.org>,
<nh-open-source@amazon.com>, Alexander Graf <graf@amazon.de>,
"Gunnar Kudrjavets" <gunnarku@amazon.com>,
Josh Levinson <joshlev@amazon.com>
Subject: [PATCH 1/2] tpm: Add per-chip timeout for transient unavailability
Date: Wed, 23 Sep 2026 14:28:33 +0000 [thread overview]
Message-ID: <20260923142834.16786-2-surenkj@amazon.com> (raw)
In-Reply-To: <20260923142834.16786-1-surenkj@amazon.com>
TPM commands need to complete within the command duration defined
in the TPM2 spec or to 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 adding chip->busy_timeout_ms to support devices that know
the expected delay window. This value raises the TPM2_RC_RETRY retry
budget and per-command durations to at least busy_timeout_ms. Chips
that leave it at 0 keep the current timeouts. The driver sets it for
NitroTPM in the following patch.
Tested with CONFIG_TCG_TPM2_HMAC=y and the following patch with the
NitroTPM quirk applied, in QEMU with swtpm by stalling commands and
holding the TPM in TPM2_RC_RETRY.
Assisted-by: LLM
Signed-off-by: Surendran Kanagaraj <surenkj@amazon.com>
---
drivers/char/tpm/tpm-interface.c | 11 ++++++++---
drivers/char/tpm/tpm.h | 2 +-
drivers/char/tpm/tpm2-cmd.c | 17 ++++++++++++-----
include/linux/tpm.h | 7 +++++++
4 files changed, 28 insertions(+), 9 deletions(-)
diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index 0bab78c8767c..a423395b201a 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);
}
@@ -213,7 +213,8 @@ 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.
+ * TPM2_DURATION_LONG, or chip->busy_timeout_ms when the driver declared a
+ * longer transient unavailability window.
*
* Note that TPM 1.x never returns TPM2_RC_RETRY so the retry logic is TPM 2.0
* only.
@@ -228,6 +229,7 @@ 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);
@@ -241,6 +243,9 @@ ssize_t tpm_transmit(struct tpm_chip *chip, u8 *buf, size_t bufsiz)
*/
memcpy(save, buf, save_size);
+ if (chip->busy_timeout_ms > max_delay_msec)
+ max_delay_msec = chip->busy_timeout_ms;
+
for (;;) {
ret = tpm_try_transmit(chip, buf, bufsiz);
if (ret < 0)
@@ -255,7 +260,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 ae22295df798..dc5ed57fefe1 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -78,20 +78,27 @@ static const struct {
/**
* 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.
+ * take in jiffies. The duration is never lower than chip->busy_timeout_ms.
*/
-unsigned long tpm2_calc_ordinal_duration(u32 ordinal)
+unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal)
{
+ unsigned long duration = TPM2_DURATION_DEFAULT;
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);
+ if (ordinal == tpm2_ordinal_duration_map[i].ordinal) {
+ duration = tpm2_ordinal_duration_map[i].duration;
+ break;
+ }
+
+ if (duration < chip->busy_timeout_ms)
+ duration = chip->busy_timeout_ms;
- return msecs_to_jiffies(TPM2_DURATION_DEFAULT);
+ return msecs_to_jiffies(duration);
}
/**
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 0db277af45c3..7089067412d3 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -140,6 +140,13 @@ struct tpm_chip {
unsigned long duration[TPM_NUM_DURATIONS]; /* jiffies */
bool duration_adjusted;
+ /*
+ * Longest unavailability expected from the chip in ms. Raises the
+ * TPM2_RC_RETRY retry budget and the per-command durations to at
+ * least this value; 0 keeps the defaults.
+ */
+ unsigned int busy_timeout_ms;
+
struct dentry *bios_dir;
const struct attribute_group *groups[3 + TPM_MAX_HASHES];
--
2.47.3
next prev parent reply other threads:[~2026-09-23 14:28 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-23 14:28 [PATCH 0/2] tpm: Handle transient NitroTPM unavailability Surendran Kanagaraj
2026-09-23 14:28 ` Surendran Kanagaraj [this message]
2026-09-25 9:04 ` [PATCH 1/2] tpm: Add per-chip timeout for transient unavailability Breno Leitao
2026-09-23 14:28 ` [PATCH 2/2] tpm_crb: Raise timeouts for Amazon NitroTPM Surendran Kanagaraj
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260923142834.16786-2-surenkj@amazon.com \
--to=surenkj@amazon.com \
--cc=graf@amazon.de \
--cc=gunnarku@amazon.com \
--cc=jarkko@kernel.org \
--cc=jgg@ziepe.ca \
--cc=joshlev@amazon.com \
--cc=linux-integrity@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=nh-open-source@amazon.com \
--cc=peterhuewe@gmx.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®