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.
next prev parent reply other threads:[~2026-11-13 19:45 UTC|newest]
Thread overview: 12+ 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-13 20:05 ` Jarkko Sakkinen
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