On Wed, Sep 23, 2026 at 02:28:33PM +0000, Surendran Kanagaraj wrote: > 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 <[email protected]> > --- > 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;
So I think this is a wrong solution. Instead this should be tpm_crb internal and you should simply patch chip->duration[]. See tpm_tis_core for example. > + > struct dentry *bios_dir; > > const struct attribute_group *groups[3 + TPM_MAX_HASHES]; > -- > 2.47.3 > Br, Jarkko

