From: Surendran Kanagaraj <surenkj@amazon.com>
To: Jarkko Sakkinen <jarkko@kernel.org>, Peter Huewe <peterhuewe@gmx.de>
Cc: 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: [RFC PATCH v3 6/8] tpm: Add the tpm.limits= parameter
Date: Thu, 12 Nov 2026 16:43:50 +0000 [thread overview]
Message-ID: <20261112164350.707-1-surenkj@amazon.com> (raw)
In-Reply-To: <20261112163846.97654-1-surenkj@amazon.com>
When a TPM needs more time than its limits allow, the only way to give
it more is to change its driver and rebuild the kernel.
Add tpm.limits=, which takes name:usecs pairs such as
tpm.limits=timeout_c:2000000,long:600000000. The values apply when the
chip is allocated and again after the driver's ->adjust_limits() hook,
both times before validation, so they carry the new PARAM source and
still get the floor and the ceiling. Document the parameter in
kernel-parameters.txt.
Signed-off-by: Surendran Kanagaraj <surenkj@amazon.com>
---
.../admin-guide/kernel-parameters.txt | 13 +++
drivers/char/tpm/tpm-limits.c | 102 +++++++++++++++++-
include/linux/tpm.h | 1 +
3 files changed, 112 insertions(+), 4 deletions(-)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 0b3c140babd9..3c4d6f3101d4 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -7667,6 +7667,19 @@ Kernel parameters
causing a major performance hit, and the space where
machines are deployed is by other means guarded.
+ tpm.limits= [HW,TPM]
+ Format: <name>:<usecs>[,<name>:<usecs>...]
+ Override how long the kernel waits for the TPM.
+ The names are timeout_a, timeout_b, timeout_c and
+ timeout_d for the interface timeouts, and short,
+ medium, long, long_long and default for command
+ execution. long also bounds the total sleep between
+ resubmissions of a command a TPM 2.0 chip asked to
+ retry. Values are in microseconds, and the TPM core
+ still applies its floor and ceiling. A value of 0
+ leaves that limit unchanged.
+ Example: tpm.limits=timeout_c:2000000,long:600000000
+
tpm_crb_ffa.busy_timeout_ms= [ARM64,TPM]
Maximum time in milliseconds to retry sending a message
to the TPM service before giving up. This parameter controls
diff --git a/drivers/char/tpm/tpm-limits.c b/drivers/char/tpm/tpm-limits.c
index 2cd51a9f27ba..251772b286a1 100644
--- a/drivers/char/tpm/tpm-limits.c
+++ b/drivers/char/tpm/tpm-limits.c
@@ -5,6 +5,7 @@
#include <linux/kernel.h>
#include <linux/ktime.h>
+#include <linux/moduleparam.h>
#include <linux/string.h>
#include <linux/tpm_ptp.h>
#include "tpm.h"
@@ -72,8 +73,87 @@ static const char * const tpm_limit_src_names[] = {
[TPM_LSRC_DEFAULT] = "DEFAULT",
[TPM_LSRC_TPM] = "TPM",
[TPM_LSRC_DRIVER] = "DRIVER",
+ [TPM_LSRC_PARAM] = "PARAM",
};
+/* tpm.limits=, in usecs per limit; 0 leaves the limit alone. */
+static u32 tpm_param_us[TPM_NR_LIMITS];
+
+/*
+ * Parse "name:usecs[,name:usecs...]" into @us. Built-in params are parsed
+ * before slab is up, so parse a stack copy; over 255 characters is -E2BIG.
+ */
+static int tpm_limits_parse_param(const char *val, u32 *us)
+{
+ char buf[256], *cur = buf, *tok;
+ int limit, rc;
+
+ memset(us, 0, TPM_NR_LIMITS * sizeof(*us));
+ if (strscpy(buf, val, sizeof(buf)) < 0)
+ return -E2BIG;
+
+ while ((tok = strsep(&cur, ",")) != NULL) {
+ char *num;
+
+ tok = strim(tok);
+ if (!*tok)
+ continue;
+
+ num = strchr(tok, ':');
+ if (!num)
+ return -EINVAL;
+ *num++ = '\0';
+
+ limit = match_string(tpm_limit_names, TPM_NR_LIMITS, tok);
+ if (limit < 0)
+ return -EINVAL;
+
+ rc = kstrtou32(num, 0, &us[limit]);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+/* A rejected string applies none of its values. */
+static int tpm_limits_param_set(const char *val,
+ const struct kernel_param *kp)
+{
+ u32 us[TPM_NR_LIMITS];
+ int rc;
+
+ rc = tpm_limits_parse_param(val, us);
+ if (!rc)
+ memcpy(tpm_param_us, us, sizeof(us));
+
+ return rc;
+}
+
+static int tpm_limits_param_get(char *buf, const struct kernel_param *kp)
+{
+ int len = 0;
+ int limit;
+
+ for (limit = 0; limit < TPM_NR_LIMITS; limit++) {
+ if (!tpm_param_us[limit])
+ continue;
+ len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s:%u",
+ len ? "," : "", tpm_limit_names[limit],
+ tpm_param_us[limit]);
+ }
+
+ return len + scnprintf(buf + len, PAGE_SIZE - len, "\n");
+}
+
+static const struct kernel_param_ops tpm_limits_param_ops = {
+ .set = tpm_limits_param_set,
+ .get = tpm_limits_param_get,
+};
+module_param_cb(limits, &tpm_limits_param_ops, NULL, 0444);
+MODULE_PARM_DESC(limits,
+ "TPM wait limits in usecs, e.g. timeout_c:2000000,long:600000000");
+
static u32 tpm_limits_default_us(enum tpm_family family, enum tpm_limit limit)
{
switch (family) {
@@ -137,6 +217,17 @@ static void tpm_limits_stage_reported(struct tpm_limits *p,
}
}
+static void tpm_limits_stage_param(struct tpm_limits *p)
+{
+ int limit;
+
+ for (limit = 0; limit < TPM_NR_LIMITS; limit++) {
+ if (tpm_param_us[limit])
+ tpm_limits_store(p, limit, tpm_param_us[limit],
+ TPM_LSRC_PARAM, 0);
+ }
+}
+
static void tpm_limits_stage_validate(struct tpm_limits *p)
{
int limit;
@@ -186,7 +277,7 @@ static void tpm_limits_commit(struct tpm_chip *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.
+ * families' defaults before the tpm.limits= parameter and validation apply.
* The driver's data is not attached yet, so its ->adjust_limits() hook first
* runs from tpm_limits_update().
*/
@@ -195,6 +286,7 @@ void tpm_limits_init(struct tpm_chip *chip)
struct tpm_limits p;
tpm_limits_stage_defaults(&p, TPM_FAMILY_UNKNOWN);
+ tpm_limits_stage_param(&p);
tpm_limits_stage_validate(&p);
tpm_limits_commit(chip, &p);
}
@@ -206,9 +298,10 @@ void tpm_limits_init(struct tpm_chip *chip)
* 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.
+ * ->adjust_limits() hook, the tpm.limits= parameter, 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)
{
@@ -221,6 +314,7 @@ void tpm_limits_update(struct tpm_chip *chip, const u32 *reported)
tpm_limits_stage_reported(&p, reported);
if (chip->ops->adjust_limits)
chip->ops->adjust_limits(chip, &p);
+ tpm_limits_stage_param(&p);
tpm_limits_stage_validate(&p);
tpm_limits_commit(chip, &p);
}
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 8ff1683f21ea..a4b7282d88b5 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -89,6 +89,7 @@ enum tpm_limit_src {
TPM_LSRC_DEFAULT, /* default values */
TPM_LSRC_TPM, /* reported by the TPM */
TPM_LSRC_DRIVER, /* set by the driver */
+ TPM_LSRC_PARAM, /* tpm.limits= */
};
#define TPM_LF_CLAMPED BIT(0) /* limit validation replaced the value */
--
2.47.3
next prev parent reply other threads:[~2026-11-12 16:44 UTC|newest]
Thread overview: 10+ 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-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 ` Surendran Kanagaraj [this message]
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=20261112164350.707-1-surenkj@amazon.com \
--to=surenkj@amazon.com \
--cc=corbet@lwn.net \
--cc=graf@amazon.de \
--cc=gunnarku@amazon.com \
--cc=jarkko@kernel.org \
--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 \
/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