Linux Integrity Measurement development
 help / color / mirror / Atom feed
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


  parent reply	other threads:[~2026-11-12 16:44 UTC|newest]

Thread overview: 9+ 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

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