The Linux Kernel Mailing List
 help / color / mirror / Atom feed
From: Jarkko Sakkinen <jarkko@kernel.org>
To: Surendran Kanagaraj <surenkj@amazon.com>
Cc: Peter Huewe <peterhuewe@gmx.de>, Jason Gunthorpe <jgg@ziepe.ca>,
	linux-integrity@vger.kernel.org, linux-kernel@vger.kernel.org,
	Jonathan Corbet <corbet@lwn.net>,
	linux-doc@vger.kernel.org, Jonathan McDowell <noodles@earth.li>,
	Breno Leitao <leitao@debian.org>,
	nh-open-source@amazon.com, Alexander Graf <graf@amazon.de>,
	Gunnar Kudrjavets <gunnarku@amazon.com>,
	Josh Levinson <joshlev@amazon.com>
Subject: Re: [RFC PATCH v3 1/8] tpm: Add a single table for all TPM wait limits
Date: Fri, 13 Nov 2026 21:45:13 +0200	[thread overview]
Message-ID: <avdoybHEoRd0Fhie@kernel.org> (raw)
In-Reply-To: <20261112163846.97654-2-surenkj@amazon.com>

On Thu, Nov 12, 2026 at 04:38:39PM +0000, Surendran Kanagaraj wrote:
> Every TPM wait takes its limit from a different place. The interface
> timeouts live in four jiffies fields, the TPM 1.x durations in an
> array, the TPM 2.0 durations in a per-ordinal map and the retry bound
> in a constant. A driver cannot see or adjust these values in one
> place. Apart from few TPM1.x fixups, nothing bounds checks these values.
> Also a driver can send a command before any timeout is set,
> xen-tpmfront calls tpm_get_timeouts() from probe before anything has
> set a timeout, so its TPM 1.x timeout query waits on the vTPM with a
> TIMEOUT_C of zero.
> 
> Add struct tpm_limits with one entry per limit: the interface timeouts
> TIMEOUT_A to TIMEOUT_D and the duration classes SHORT, MEDIUM, LONG,
> LONG_LONG and DEFAULT. Each entry holds the value in microseconds, its
> source (the defaults, the TPM or the driver) and whether validation
> clamped it.
> 
> tpm_chip_alloc() fills the table with the larger of the TPM 1.x and
> TPM 2.0 defaults, so a wait has a usable limit before the TPM is
> queried. Once the family is known, tpm_limits_update() rebuilds the
> table (next patch calls it from timing query) in a fixed order:
> the family defaults, the values the TPM reported, the driver's new
> ->adjust_limits() hook, then validation. Validation applies a floor
> and a ceiling, keeps SHORT to LONG_LONG in increasing order and
> keeps DEFAULT no shorter than LONG.
> 
> The hook sets values with tpm_limits_driver_set() or from a quirk
> table through tpm_limits_apply_quirks(). tpm_poll_timeout() waits on

OK, but ...

I.e. I'm not fully following what is the diffence between these call
paths.

> a condition for a limit from the table. No limits are read from the
> table yet. The following patches move them to it.
> 
> Signed-off-by: Surendran Kanagaraj <surenkj@amazon.com>
> ---
>  drivers/char/tpm/Makefile     |   1 +
>  drivers/char/tpm/tpm-chip.c   |   1 +
>  drivers/char/tpm/tpm-limits.c | 368 ++++++++++++++++++++++++++++++++++
>  drivers/char/tpm/tpm.h        |  13 ++
>  include/linux/tpm.h           |  54 +++++
>  5 files changed, 437 insertions(+)
>  create mode 100644 drivers/char/tpm/tpm-limits.c
> 
> diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile
> index 5b5cdc0d32e4..92f469218e15 100644
> --- a/drivers/char/tpm/Makefile
> +++ b/drivers/char/tpm/Makefile
> @@ -12,6 +12,7 @@ tpm-y += tpm2-cmd.o
>  tpm-y += tpmrm-dev.o
>  tpm-y += tpm2-space.o
>  tpm-y += tpm-sysfs.o
> +tpm-y += tpm-limits.o
>  tpm-y += eventlog/common.o
>  tpm-y += eventlog/tpm1.o
>  tpm-y += eventlog/tpm2.o
> diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c
> index 0be5dbfaa72e..11a050c43e10 100644
> --- a/drivers/char/tpm/tpm-chip.c
> +++ b/drivers/char/tpm/tpm-chip.c
> @@ -319,6 +319,7 @@ struct tpm_chip *tpm_chip_alloc(struct device *pdev,
>  	}
>  
>  	chip->locality = -1;
> +	tpm_limits_init(chip);
>  	return chip;
>  
>  out:
> diff --git a/drivers/char/tpm/tpm-limits.c b/drivers/char/tpm/tpm-limits.c
> new file mode 100644
> index 000000000000..3fe5affa1522
> --- /dev/null
> +++ b/drivers/char/tpm/tpm-limits.c
> @@ -0,0 +1,368 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Handles time limits for a TPM chip.

I'd extend this to just have brief description how this works. Just a
high-level overview.

> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/ktime.h>
> +#include <linux/string.h>
> +#include <linux/tpm_ptp.h>
> +#include "tpm.h"
> +
> +/* Bounds for the limits */
> +#define TPM_LIMIT_FLOOR_US	        (2 * TPM_POLL_DURATION_MAX_US)
> +#define TPM_LIMIT_INTF_CEILING_US	(60 * USEC_PER_SEC)
> +#define TPM_LIMIT_DURATION_CEILING_US	(30 * 60 * USEC_PER_SEC)
> +
> +#define TPM_POLL_START_US		100
> +#define TPM_POLL_INTF_MAX_US		100
> +#define TPM_POLL_DURATION_MAX_US	1000
> +
> +static const u32 tpm1_defaults[TPM_NR_LIMITS] = {
> +	[TPM_LIMIT_A]		= TIS_SHORT_TIMEOUT * USEC_PER_MSEC,
> +	[TPM_LIMIT_B]		= TIS_LONG_TIMEOUT * USEC_PER_MSEC,
> +	[TPM_LIMIT_C]		= TIS_SHORT_TIMEOUT * USEC_PER_MSEC,
> +	[TPM_LIMIT_D]		= TIS_SHORT_TIMEOUT * USEC_PER_MSEC,
> +
> +	[TPM_LIMIT_SHORT]	= 120 * USEC_PER_SEC,
> +	[TPM_LIMIT_MEDIUM]	= 120 * USEC_PER_SEC,
> +	[TPM_LIMIT_LONG]	= 120 * USEC_PER_SEC,
> +	[TPM_LIMIT_LONG_LONG]	= 120 * USEC_PER_SEC,
> +	[TPM_LIMIT_DEFAULT]	= 120 * USEC_PER_SEC,
> +};
> +
> +static const u32 tpm2_defaults[TPM_NR_LIMITS] = {
> +	[TPM_LIMIT_A]		= TPM2_TIMEOUT_A * USEC_PER_MSEC,
> +	[TPM_LIMIT_B]		= TPM2_TIMEOUT_B * USEC_PER_MSEC,
> +	[TPM_LIMIT_C]		= TPM2_TIMEOUT_C * USEC_PER_MSEC,
> +	[TPM_LIMIT_D]		= TPM2_TIMEOUT_D * USEC_PER_MSEC,
> +
> +	[TPM_LIMIT_SHORT]	= 750 * USEC_PER_MSEC,
> +	[TPM_LIMIT_MEDIUM]	= 2000 * USEC_PER_MSEC,
> +	[TPM_LIMIT_LONG]	= 3000 * USEC_PER_MSEC,
> +	[TPM_LIMIT_LONG_LONG]	= TPM2_DURATION_LONG_LONG * USEC_PER_MSEC,
> +	[TPM_LIMIT_DEFAULT]	= TPM2_DURATION_DEFAULT * USEC_PER_MSEC,
> +};
> +
> +static const char * const tpm_limit_names[TPM_NR_LIMITS] = {
> +	[TPM_LIMIT_A]		= "timeout_a",
> +	[TPM_LIMIT_B]		= "timeout_b",
> +	[TPM_LIMIT_C]		= "timeout_c",
> +	[TPM_LIMIT_D]		= "timeout_d",
> +	[TPM_LIMIT_SHORT]	= "short",
> +	[TPM_LIMIT_MEDIUM]	= "medium",
> +	[TPM_LIMIT_LONG]	= "long",
> +	[TPM_LIMIT_LONG_LONG]	= "long_long",
> +	[TPM_LIMIT_DEFAULT]	= "default",
> +};
> +
> +static const char * const tpm_limit_labels[TPM_NR_LIMITS] = {
> +	[TPM_LIMIT_A]		= "TIMEOUT_A",
> +	[TPM_LIMIT_B]		= "TIMEOUT_B",
> +	[TPM_LIMIT_C]		= "TIMEOUT_C",
> +	[TPM_LIMIT_D]		= "TIMEOUT_D",
> +	[TPM_LIMIT_SHORT]	= "SHORT",
> +	[TPM_LIMIT_MEDIUM]	= "MEDIUM",
> +	[TPM_LIMIT_LONG]	= "LONG",
> +	[TPM_LIMIT_LONG_LONG]	= "LONG_LONG",
> +	[TPM_LIMIT_DEFAULT]	= "DEFAULT",
> +};
> +
> +static const char * const tpm_limit_src_names[] = {
> +	[TPM_LSRC_DEFAULT]	= "DEFAULT",
> +	[TPM_LSRC_TPM]		= "TPM",
> +	[TPM_LSRC_DRIVER]	= "DRIVER",
> +};
> +
> +static u32 tpm_limits_default_us(enum tpm_family family, enum tpm_limit limit)
> +{
> +	switch (family) {
> +	case TPM_FAMILY_1:
> +		return tpm1_defaults[limit];
> +	case TPM_FAMILY_2:
> +		return tpm2_defaults[limit];
> +	default:
> +		return max(tpm1_defaults[limit], tpm2_defaults[limit]);
> +	}
> +}
> +
> +static u32 tpm_limits_ceiling_us(enum tpm_limit limit)
> +{
> +	if (TPM_LIMIT_INTF_MASK & BIT(limit))
> +		return TPM_LIMIT_INTF_CEILING_US;
> +
> +	return TPM_LIMIT_DURATION_CEILING_US;
> +}
> +
> +static void tpm_limits_store(struct tpm_limits *p, enum tpm_limit limit,
> +			     u32 us, enum tpm_limit_src src, u32 flags)
> +{
> +	p->entry[limit].time_us = us;
> +	p->entry[limit].src = src;
> +	p->entry[limit].flags = flags;
> +}
> +
> +static void tpm_limits_raise(struct tpm_limits *p, enum tpm_limit limit,
> +			     u32 us)
> +{
> +	if (p->entry[limit].time_us < us) {
> +		p->entry[limit].time_us = us;
> +		p->entry[limit].flags |= TPM_LF_CLAMPED;
> +	}
> +}
> +
> +static void tpm_limits_stage_defaults(struct tpm_limits *p,
> +				      enum tpm_family family)
> +{
> +	int limit;
> +
> +	memset(p, 0, sizeof(*p));
> +	p->family = family;
> +	for (limit = 0; limit < TPM_NR_LIMITS; limit++)
> +		tpm_limits_store(p, limit,
> +				 tpm_limits_default_us(family, limit),
> +				 TPM_LSRC_DEFAULT, 0);
> +}
> +
> +/* The values the TPM reported, in usecs; zero where it reported nothing. */
> +static void tpm_limits_stage_reported(struct tpm_limits *p,
> +				      const u32 *reported)
> +{
> +	int limit;
> +
> +	for (limit = 0; limit < TPM_NR_LIMITS; limit++) {
> +		if (reported[limit])
> +			tpm_limits_store(p, limit, reported[limit],
> +					 TPM_LSRC_TPM, 0);
> +	}
> +}
> +
> +static void tpm_limits_stage_validate(struct tpm_limits *p)
> +{
> +	int limit;
> +
> +	for (limit = 0; limit < TPM_NR_LIMITS; limit++) {
> +		struct tpm_limit_entry *e = &p->entry[limit];
> +		u32 lo = TPM_LIMIT_FLOOR_US;
> +		u32 hi = tpm_limits_ceiling_us(limit);
> +
> +		if (e->time_us < lo) {
> +			e->time_us = lo;
> +			e->flags |= TPM_LF_CLAMPED;
> +		} else if (e->time_us > hi) {
> +			e->time_us = hi;
> +			e->flags |= TPM_LF_CLAMPED;
> +		}
> +	}
> +
> +	/*
> +	 * Keep SHORT to LONG_LONG in increasing order, and DEFAULT no shorter
> +	 * than LONG.
> +	 */
> +	for (limit = TPM_LIMIT_MEDIUM; limit <= TPM_LIMIT_LONG_LONG; limit++)
> +		tpm_limits_raise(p, limit, p->entry[limit - 1].time_us);
> +
> +	tpm_limits_raise(p, TPM_LIMIT_DEFAULT,
> +			 p->entry[TPM_LIMIT_LONG].time_us);
> +}
> +
> +static void tpm_limits_commit(struct tpm_chip *chip,
> +			      const struct tpm_limits *p)
> +{
> +	const struct tpm_limit_entry *e = p->entry;
> +	int limit;
> +
> +	chip->limits = *p;
> +
> +	for (limit = 0; limit < TPM_NR_LIMITS; limit++)
> +		dev_dbg(&chip->dev, "limit %s = %u us (%s%s)\n",
> +			tpm_limit_names[limit], e[limit].time_us,
> +			tpm_limit_src_names[e[limit].src],
> +			e[limit].flags & TPM_LF_CLAMPED ? ", clamped" : "");
> +}
> +
> +/**
> + * tpm_limits_init() - set the boot limits of a new chip
> + * @chip: the chip tpm_chip_alloc() is setting up
> + *
> + * The family is not known yet, so each limit gets the larger of the two
> + * families' defaults before validation applies.
> + * The driver's data is not attached yet, so its ->adjust_limits() hook first
> + * runs from tpm_limits_update().
> + */
> +void tpm_limits_init(struct tpm_chip *chip)
> +{
> +	struct tpm_limits p;
> +
> +	tpm_limits_stage_defaults(&p, TPM_FAMILY_UNKNOWN);
> +	tpm_limits_stage_validate(&p);
> +	tpm_limits_commit(chip, &p);
> +}
> +
> +/**
> + * tpm_limits_update() - build the limits once the TPM family is known
> + * @chip:	the TPM chip, started, with TPM_CHIP_FLAG_TPM2 set on TPM 2.0
> + * @reported:	TPM_NR_LIMITS values in usecs, indexed by enum tpm_limit,
> + *		zero where the TPM reported nothing; or NULL
> + *
> + * Runs the stages in order: the family defaults, @reported, the driver's
> + * ->adjust_limits() hook, then validation. The hook may send commands, so
> + * the chip must be started. Call it before the chip is registered or with
> + * tpm_mutex held, so that no command sees half-built limits.
> + */
> +void tpm_limits_update(struct tpm_chip *chip, const u32 *reported)
> +{
> +	enum tpm_family family = chip->flags & TPM_CHIP_FLAG_TPM2 ?
> +				 TPM_FAMILY_2 : TPM_FAMILY_1;
> +	struct tpm_limits p;
> +
> +	tpm_limits_stage_defaults(&p, family);
> +	if (reported)
> +		tpm_limits_stage_reported(&p, reported);
> +	if (chip->ops->adjust_limits)
> +		chip->ops->adjust_limits(chip, &p);
> +	tpm_limits_stage_validate(&p);
> +	tpm_limits_commit(chip, &p);
> +}
> +
> +/**
> + * tpm_limits_driver_set() - set a limit from a driver's ->adjust_limits() hook
> + * @p:		the limits the hook received
> + * @limit:	the limit to set
> + * @us:		the new value in microseconds
> + *
> + * Marks the driver as the source of @limit. Validation runs after the hook,
> + * so the value can still be clamped.
> + */
> +void tpm_limits_driver_set(struct tpm_limits *p, enum tpm_limit limit, u32 us)
> +{
> +	if (WARN_ON_ONCE(limit >= TPM_NR_LIMITS))
> +		return;
> +
> +	tpm_limits_store(p, limit, us, TPM_LSRC_DRIVER, 0);
> +}
> +EXPORT_SYMBOL_GPL(tpm_limits_driver_set);
> +
> +/**
> + * tpm_limits_apply_quirks() - apply the first matching quirk entry
> + * @p:		the limits the ->adjust_limits() hook received
> + * @quirks:	the driver's quirk table
> + * @n:		the number of entries in @quirks
> + * @id:		the device identity to match, as the driver defines it
> + *
> + * Applies the first entry whose id is @id. Each non-zero value in the entry
> + * is set as tpm_limits_driver_set() does.
> + */
> +void tpm_limits_apply_quirks(struct tpm_limits *p,
> +			     const struct tpm_limits_quirk *quirks, size_t n,
> +			     u32 id)
> +{
> +	size_t i;
> +	int limit;
> +
> +	for (i = 0; i < n; i++) {
> +		if (quirks[i].id != id)
> +			continue;
> +
> +		for (limit = 0; limit < TPM_NR_LIMITS; limit++) {
> +			if (quirks[i].time_us[limit])
> +				tpm_limits_driver_set(p, limit,
> +						      quirks[i].time_us[limit]);
> +		}
> +		return;
> +	}
> +}
> +EXPORT_SYMBOL_GPL(tpm_limits_apply_quirks);
> +
> +static u32 tpm_limits_us(struct tpm_chip *chip, enum tpm_limit limit)
> +{
> +	if (!chip)
> +		return tpm_limits_default_us(TPM_FAMILY_UNKNOWN, limit);
> +
> +	return chip->limits.entry[limit].time_us;
> +}
> +
> +static int tpm_poll(struct tpm_chip *chip, u32 timeout_us,
> +		    enum tpm_limit limit,
> +		    bool (*cond)(struct tpm_chip *chip, void *arg), void *arg)
> +{
> +	ktime_t timeout = ktime_add_us(ktime_get(), timeout_us);
> +	unsigned int max_us = TPM_POLL_INTF_MAX_US;
> +	unsigned int delay = TPM_POLL_START_US;
> +	const char *label = "fixed timeout";
> +	const char *src = "CALLER";
> +
> +	might_sleep();
> +
> +	if (limit < TPM_NR_LIMITS && !(TPM_LIMIT_INTF_MASK & BIT(limit)))
> +		max_us = TPM_POLL_DURATION_MAX_US;
> +
> +	for (;;) {
> +		if (cond(chip, arg))
> +			return 0;
> +		if (ktime_after(ktime_get(), timeout))
> +			break;
> +
> +		usleep_range(delay / 2, delay);
> +		delay = min(delay * 2, max_us);
> +	}
> +
> +	if (cond(chip, arg))
> +		return 0;
> +	if (!chip)
> +		return -ETIME;
> +
> +	if (limit < TPM_NR_LIMITS) {
> +		label = tpm_limit_labels[limit];
> +		src = tpm_limit_src_names[chip->limits.entry[limit].src];
> +	}
> +	dev_warn_ratelimited(&chip->dev, "%s expired after %u us (source %s)\n",
> +			     label, timeout_us, src);
> +	return -ETIME;
> +}
> +
> +/**
> + * tpm_poll_timeout() - poll a condition until it holds or a limit passes
> + * @chip:	the TPM chip, or NULL before tpm_chip_alloc()
> + * @limit:	the limit that bounds the wait
> + * @cond:	returns true once the wait is over
> + * @arg:	passed to @cond
> + *
> + * Drivers pass an interface timeout, TPM_LIMIT_A to TPM_LIMIT_D, and the core
> + * passes a command's execution limit. Without a chip, the larger of the two
> + * families' defaults applies, @cond gets a NULL @chip and nothing is printed
> + * on expiry. @cond is checked once more after the deadline, so a TPM that
> + * finished while the caller was descheduled does not time out.
> + *
> + * Return: 0 once @cond holds, -ETIME if @limit passes first, or -EINVAL for
> + * a limit out of range.
> + */
> +int tpm_poll_timeout(struct tpm_chip *chip, enum tpm_limit limit,
> +		     bool (*cond)(struct tpm_chip *chip, void *arg), void *arg)
> +{
> +	if (WARN_ON_ONCE(limit >= TPM_NR_LIMITS))
> +		return -EINVAL;
> +
> +	return tpm_poll(chip, tpm_limits_us(chip, limit), limit, cond, arg);
> +}
> +EXPORT_SYMBOL_GPL(tpm_poll_timeout);
> +
> +/**
> + * tpm_poll_timeout_us() - poll a condition with a driver-chosen timeout
> + * @chip:	the TPM chip, or NULL before tpm_chip_alloc()
> + * @timeout_us:	the timeout in microseconds
> + * @cond:	returns true once the wait is over
> + * @arg:	passed to @cond
> + *
> + * For a wait no TCG spec names, such as a mailbox doorbell. Otherwise as
> + * tpm_poll_timeout().
> + *
> + * Return: 0 once @cond holds, or -ETIME if @timeout_us passes first.
> + */
> +int tpm_poll_timeout_us(struct tpm_chip *chip, u32 timeout_us,
> +			bool (*cond)(struct tpm_chip *chip, void *arg),
> +			void *arg)
> +{
> +	return tpm_poll(chip, timeout_us, TPM_NR_LIMITS, cond, arg);
> +}

I did not check all the details but could this be reorganized into:

1. __tpm_poll(): the same as tpm_poll() but exported and defined in
   drivers/char/tpm.h.
2. tpm_poll(): a single static inline function declared fully at
   drivers/char/tpm.h

__tpm_poll() would e.g., make a nicer trace point for a kprobe or
kretprobe.


> +EXPORT_SYMBOL_GPL(tpm_poll_timeout_us);
> diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
> index fa554c5ad80b..be88011f9618 100644
> --- a/drivers/char/tpm/tpm.h
> +++ b/drivers/char/tpm/tpm.h
> @@ -75,6 +75,19 @@ ssize_t tpm1_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap,
>  int tpm1_get_random(struct tpm_chip *chip, u8 *out, size_t max);
>  int tpm1_get_pcr_allocation(struct tpm_chip *chip);
>  unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal);
> +
> +void tpm_limits_init(struct tpm_chip *chip);
> +void tpm_limits_update(struct tpm_chip *chip, const u32 *reported);
> +void tpm_limits_driver_set(struct tpm_limits *p, enum tpm_limit limit, u32 us);
> +void tpm_limits_apply_quirks(struct tpm_limits *p,
> +			     const struct tpm_limits_quirk *quirks, size_t n,
> +			     u32 id);
> +int tpm_poll_timeout(struct tpm_chip *chip, enum tpm_limit limit,
> +		     bool (*cond)(struct tpm_chip *chip, void *arg), void *arg);
> +int tpm_poll_timeout_us(struct tpm_chip *chip, u32 timeout_us,
> +			bool (*cond)(struct tpm_chip *chip, void *arg),
> +			void *arg);
> +
>  int tpm_pm_suspend(struct device *dev);
>  int tpm_pm_resume(struct device *dev);
>  int tpm_class_shutdown(struct device *dev);
> diff --git a/include/linux/tpm.h b/include/linux/tpm.h
> index 0db277af45c3..5454d87f43ba 100644
> --- a/include/linux/tpm.h
> +++ b/include/linux/tpm.h
> @@ -29,6 +29,7 @@
>  #include <linux/tpm_buf.h>
>  
>  struct tpm_chip;
> +struct tpm_limits;
>  struct trusted_key_payload;
>  struct trusted_key_options;
>  /* opaque structure, holds auth session parameters like the session key */
> @@ -52,6 +53,7 @@ struct tpm_class_ops {
>  				unsigned long *timeout_cap);
>  	void (*update_durations)(struct tpm_chip *chip,
>  				 unsigned long *duration_cap);
> +	void (*adjust_limits)(struct tpm_chip *chip, struct tpm_limits *p);
>  	int (*go_idle)(struct tpm_chip *chip);
>  	int (*cmd_ready)(struct tpm_chip *chip);
>  	int (*request_locality)(struct tpm_chip *chip, int loc);
> @@ -71,6 +73,56 @@ enum tpm_duration {
>  	TPM_NUM_DURATIONS = TPM_UNDEFINED,
>  };
>  
> +enum tpm_limit {
> +	/* Interface handshake timeouts */
> +	TPM_LIMIT_A,
> +	TPM_LIMIT_B,
> +	TPM_LIMIT_C,
> +	TPM_LIMIT_D,
> +
> +	/* Command duration classes */
> +	TPM_LIMIT_SHORT,
> +	TPM_LIMIT_MEDIUM,
> +	TPM_LIMIT_LONG,
> +	TPM_LIMIT_LONG_LONG,
> +	TPM_LIMIT_DEFAULT,
> +
> +	TPM_NR_LIMITS,
> +};
> +
> +#define TPM_LIMIT_INTF_MASK		GENMASK(TPM_LIMIT_D, TPM_LIMIT_A)
> +
> +enum tpm_family {
> +	TPM_FAMILY_1,		/* TPM 1.1 and 1.2 */
> +	TPM_FAMILY_2,
> +	TPM_FAMILY_UNKNOWN,
> +};
> +
> +enum tpm_limit_src {
> +	TPM_LSRC_DEFAULT,	/* default values */
> +	TPM_LSRC_TPM,		/* reported by the TPM */
> +	TPM_LSRC_DRIVER,	/* set by the driver */
> +};
> +
> +#define TPM_LF_CLAMPED		BIT(0)	/* limit validation replaced the value */
> +
> +struct tpm_limit_entry {
> +	u32 time_us;
> +	enum tpm_limit_src src;
> +	u32 flags;		/* TPM_LF_* */
> +};
> +
> +struct tpm_limits {
> +	enum tpm_family family;
> +	struct tpm_limit_entry entry[TPM_NR_LIMITS];
> +};
> +
> +/* per-device overrides based on id the driver defines e.g. DID/VID */
> +struct tpm_limits_quirk {
> +	u32 id;				/* driver-defined device identity */
> +	u32 time_us[TPM_NR_LIMITS];	/* 0 leaves the limit alone */
> +};

I think it would make more sense to fully initialize the database per
device, if that is possible.

It is a non-concern if two TPM drivers do some redundant work, as it
is not a real-world production use case :-)

I'm just wondering if this quirk/entry division.

I.e. in my world view chip owns its limits (not e.g., tpm2_space).


> +
>  #define TPM_PPI_VERSION_LEN		3
>  
>  struct tpm_space {
> @@ -140,6 +192,8 @@ struct tpm_chip {
>  	unsigned long duration[TPM_NUM_DURATIONS]; /* jiffies */
>  	bool duration_adjusted;
>  
> +	struct tpm_limits limits;
> +
>  	struct dentry *bios_dir;
>  
>  	const struct attribute_group *groups[3 + TPM_MAX_HASHES];
> -- 
> 2.47.3
> 

This is a great starting point.

Br, Jarkko.

  reply	other threads:[~2026-11-13 19:45 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-12 16:38 [RFC PATCH v3 0/8] tpm: Rework timeout and duration handling Surendran Kanagaraj
2026-11-12 16:38 ` [RFC PATCH v3 1/8] tpm: Add a single table for all TPM wait limits Surendran Kanagaraj
2026-11-13 19:45   ` Jarkko Sakkinen [this message]
2026-11-12 16:38 ` [RFC PATCH v3 2/8] tpm: Wire the timing setup to the limits table Surendran Kanagaraj
2026-11-12 16:38 ` [RFC PATCH v3 3/8] tpm: Take command durations from " Surendran Kanagaraj
2026-11-12 16:38 ` [RFC PATCH v3 4/8] tpm: Bound core and CRB waits by " Surendran Kanagaraj
2026-11-12 16:42 ` [RFC PATCH v3 5/8] tpm: Drop the jiffies timing fields from struct tpm_chip Surendran Kanagaraj
2026-11-12 16:43 ` [RFC PATCH v3 6/8] tpm: Add the tpm.limits= parameter Surendran Kanagaraj
2026-11-12 16:45 ` [RFC PATCH v3 7/8] tpm: Show timeouts and durations for TPM 2.0 chips Surendran Kanagaraj
2026-11-12 16:46 ` [RFC PATCH v3 8/8] tpm_crb: Raise the limits for Amazon NitroTPM Surendran Kanagaraj
2026-11-12 18:39 ` [RFC PATCH v3 0/8] tpm: Rework timeout and duration handling Jarkko Sakkinen

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=avdoybHEoRd0Fhie@kernel.org \
    --to=jarkko@kernel.org \
    --cc=corbet@lwn.net \
    --cc=graf@amazon.de \
    --cc=gunnarku@amazon.com \
    --cc=jgg@ziepe.ca \
    --cc=joshlev@amazon.com \
    --cc=leitao@debian.org \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-integrity@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=nh-open-source@amazon.com \
    --cc=noodles@earth.li \
    --cc=peterhuewe@gmx.de \
    --cc=surenkj@amazon.com \
    /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