* [PATCH v7 10/12] tpm-buf: Merge TPM_BUF_BOUNDARY_ERROR and TPM_BUF_OVERFLOW
From: Jarkko Sakkinen @ 2025-12-16 7:44 UTC (permalink / raw)
To: linux-integrity
Cc: Jarkko Sakkinen, Jonathan McDowell, Peter Huewe, Jarkko Sakkinen,
Jason Gunthorpe, David Howells, Paul Moore, James Morris,
Serge E. Hallyn, James Bottomley, Mimi Zohar, open list,
open list:KEYS/KEYRINGS, open list:SECURITY SUBSYSTEM
In-Reply-To: <20251216074454.2192499-1-jarkko@kernel.org>
From: Jarkko Sakkinen <jarkko.sakkinen@opinsys.com>
Merge TPM_BUF_BOUNDARY_ERROR and TPM_BUF_OVERFLOW into TPM_BUF_INVALID,
given that they are identical. The only difference are the log messages.
In addition, add a missing TPM_BUF_INVALID check to tpm_buf_append_handle()
following the pattern from other functions in tpm-buf.c.
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@opinsys.com>
Reviewed-by: Jonathan McDowell <noodles@meta.com>
---
drivers/char/tpm/tpm-buf.c | 14 ++++++++------
include/linux/tpm.h | 8 +++-----
security/keys/trusted-keys/trusted_tpm2.c | 6 +++---
3 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/drivers/char/tpm/tpm-buf.c b/drivers/char/tpm/tpm-buf.c
index dc882fc9fa9e..69ee77400539 100644
--- a/drivers/char/tpm/tpm-buf.c
+++ b/drivers/char/tpm/tpm-buf.c
@@ -104,13 +104,12 @@ EXPORT_SYMBOL_GPL(tpm_buf_length);
*/
void tpm_buf_append(struct tpm_buf *buf, const u8 *new_data, u16 new_length)
{
- /* Return silently if overflow has already happened. */
- if (buf->flags & TPM_BUF_OVERFLOW)
+ if (buf->flags & TPM_BUF_INVALID)
return;
if ((buf->length + new_length) > PAGE_SIZE) {
WARN(1, "tpm_buf: write overflow\n");
- buf->flags |= TPM_BUF_OVERFLOW;
+ buf->flags |= TPM_BUF_INVALID;
return;
}
@@ -157,8 +156,12 @@ EXPORT_SYMBOL_GPL(tpm_buf_append_u32);
*/
void tpm_buf_append_handle(struct tpm_chip *chip, struct tpm_buf *buf, u32 handle)
{
+ if (buf->flags & TPM_BUF_INVALID)
+ return;
+
if (buf->flags & TPM_BUF_TPM2B) {
dev_err(&chip->dev, "Invalid buffer type (TPM2B)\n");
+ buf->flags |= TPM_BUF_INVALID;
return;
}
@@ -177,14 +180,13 @@ static void tpm_buf_read(struct tpm_buf *buf, off_t *offset, size_t count, void
{
off_t next_offset;
- /* Return silently if overflow has already happened. */
- if (buf->flags & TPM_BUF_BOUNDARY_ERROR)
+ if (buf->flags & TPM_BUF_INVALID)
return;
next_offset = *offset + count;
if (next_offset > buf->length) {
WARN(1, "tpm_buf: read out of boundary\n");
- buf->flags |= TPM_BUF_BOUNDARY_ERROR;
+ buf->flags |= TPM_BUF_INVALID;
return;
}
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 72610f1aa402..f8c135dd6e7b 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -382,12 +382,10 @@ struct tpm_header {
} __packed;
enum tpm_buf_flags {
- /* the capacity exceeded: */
- TPM_BUF_OVERFLOW = BIT(0),
/* TPM2B format: */
- TPM_BUF_TPM2B = BIT(1),
- /* read out of boundary: */
- TPM_BUF_BOUNDARY_ERROR = BIT(2),
+ TPM_BUF_TPM2B = BIT(0),
+ /* The buffer is in invalid and unusable state: */
+ TPM_BUF_INVALID = BIT(1),
};
/*
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 3de84b30b655..6fcff1066873 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -292,7 +292,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
/* creation PCR */
tpm_buf_append_u32(&buf, 0);
- if (buf.flags & TPM_BUF_OVERFLOW) {
+ if (buf.flags & TPM_BUF_INVALID) {
rc = -E2BIG;
tpm2_end_auth_session(chip);
goto out;
@@ -308,7 +308,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
goto out;
blob_len = tpm_buf_read_u32(&buf, &offset);
- if (blob_len > MAX_BLOB_SIZE || buf.flags & TPM_BUF_BOUNDARY_ERROR) {
+ if (blob_len > MAX_BLOB_SIZE || buf.flags & TPM_BUF_INVALID) {
rc = -E2BIG;
goto out;
}
@@ -412,7 +412,7 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
tpm_buf_append(&buf, blob, blob_len);
- if (buf.flags & TPM_BUF_OVERFLOW) {
+ if (buf.flags & TPM_BUF_INVALID) {
rc = -E2BIG;
tpm2_end_auth_session(chip);
goto out;
--
2.39.5
^ permalink raw reply related
* [PATCH v7 09/12] tpm: Change tpm_get_random() opportunistic
From: Jarkko Sakkinen @ 2025-12-16 7:44 UTC (permalink / raw)
To: linux-integrity
Cc: Jarkko Sakkinen, David S . Miller, Herbert Xu, Peter Huewe,
Jason Gunthorpe, David Howells, Paul Moore, James Morris,
Serge E. Hallyn, open list, open list:KEYS/KEYRINGS,
open list:SECURITY SUBSYSTEM
In-Reply-To: <20251216074454.2192499-1-jarkko@kernel.org>
hwrng framework does not have a requirement that the all bytes requested
need to be provided. By enforcing such a requirement internally, TPM driver
can cause unpredictability in latency, as a single tpm_get_random() call
can result multiple TPM commands.
Especially, when TCG_TPM2_HMAC is enabled, extra roundtrips could have
significant effect to the system latency.
Thus, send TPM command only once and return bytes received instead of
committing to the number of requested bytes.
Cc: David S. Miller <davem@davemloft.net>
Cc: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
v7:
- Given that hwrng is now only caller for tpm_get_random(), remove the
wait parameter.
v4:
- Fixed grammar mistakes.
---
drivers/char/tpm/tpm-interface.c | 28 +++++-----------------------
1 file changed, 5 insertions(+), 23 deletions(-)
diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index d157be738612..677dcef05dfb 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -626,10 +626,6 @@ static int tpm2_get_random(struct tpm_chip *chip, u8 *out, size_t max)
*/
int tpm_get_random(struct tpm_chip *chip, u8 *out, size_t max)
{
- u32 num_bytes = max;
- u8 *out_ptr = out;
- int retries = 5;
- int total = 0;
int rc;
if (!out || !max || max > TPM_MAX_RNG_DATA)
@@ -646,28 +642,14 @@ int tpm_get_random(struct tpm_chip *chip, u8 *out, size_t max)
rc = tpm2_start_auth_session(chip);
if (rc)
return rc;
- }
-
- do {
- if (chip->flags & TPM_CHIP_FLAG_TPM2)
- rc = tpm2_get_random(chip, out_ptr, num_bytes);
- else
- rc = tpm1_get_random(chip, out_ptr, num_bytes);
-
- if (rc < 0)
- goto err;
-
- out_ptr += rc;
- total += rc;
- num_bytes -= rc;
- } while (retries-- && total < max);
- tpm_put_ops(chip);
- return total ? total : -EIO;
+ rc = tpm2_get_random(chip, out, max);
+ } else {
+ rc = tpm1_get_random(chip, out, max);
+ }
-err:
tpm_put_ops(chip);
- return rc;
+ return rc != 0 ? rc : -EIO;
}
EXPORT_SYMBOL_GPL(tpm_get_random);
--
2.39.5
^ permalink raw reply related
* [PATCH v7 08/12] tpm: Orchestrate TPM commands in tpm_get_random()
From: Jarkko Sakkinen @ 2025-12-16 7:44 UTC (permalink / raw)
To: linux-integrity
Cc: Jarkko Sakkinen, Peter Huewe, Jason Gunthorpe, David Howells,
Paul Moore, James Morris, Serge E. Hallyn, open list,
open list:KEYS/KEYRINGS, open list:SECURITY SUBSYSTEM
In-Reply-To: <20251216074454.2192499-1-jarkko@kernel.org>
tpm1_get_random() and tpm2_get_random() contain duplicate orchestration
code. Consolidate orchestration to tpm_get_random().
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
drivers/char/tpm/tpm-interface.c | 175 +++++++++++++++++++++++++++++--
drivers/char/tpm/tpm.h | 2 -
drivers/char/tpm/tpm1-cmd.c | 69 ------------
drivers/char/tpm/tpm2-cmd.c | 104 ------------------
4 files changed, 164 insertions(+), 186 deletions(-)
diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index f745a098908b..d157be738612 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -26,7 +26,7 @@
#include <linux/suspend.h>
#include <linux/freezer.h>
#include <linux/tpm_eventlog.h>
-
+#include <linux/tpm_command.h>
#include "tpm.h"
/*
@@ -486,19 +486,153 @@ int tpm_pm_resume(struct device *dev)
}
EXPORT_SYMBOL_GPL(tpm_pm_resume);
+struct tpm1_get_random_out {
+ __be32 rng_data_len;
+ u8 rng_data[TPM_MAX_RNG_DATA];
+} __packed;
+
+static int tpm1_get_random(struct tpm_chip *chip, u8 *out, size_t max)
+{
+ struct tpm1_get_random_out *resp;
+ struct tpm_buf buf;
+ u32 recd;
+ int rc;
+
+ if (!out || !max || max > TPM_MAX_RNG_DATA)
+ return -EINVAL;
+
+ rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GETRANDOM);
+ if (rc)
+ return rc;
+
+ tpm_buf_append_u32(&buf, max);
+
+ rc = tpm_transmit_cmd(chip, &buf, sizeof(resp->rng_data_len), "TPM_GetRandom");
+ if (rc) {
+ if (rc > 0)
+ rc = -EIO;
+ goto err;
+ }
+
+ resp = (struct tpm1_get_random_out *)&buf.data[TPM_HEADER_SIZE];
+
+ recd = be32_to_cpu(resp->rng_data_len);
+ if (recd > max) {
+ rc = -EIO;
+ goto err;
+ }
+
+ if (buf.length < TPM_HEADER_SIZE + sizeof(resp->rng_data_len) + recd) {
+ rc = -EIO;
+ goto err;
+ }
+
+ memcpy(out, resp->rng_data, recd);
+ tpm_buf_destroy(&buf);
+ return recd;
+
+err:
+ tpm_buf_destroy(&buf);
+ return rc;
+}
+
+struct tpm2_get_random_out {
+ __be16 size;
+ u8 buffer[TPM_MAX_RNG_DATA];
+} __packed;
+
+static int tpm2_get_random(struct tpm_chip *chip, u8 *out, size_t max)
+{
+ struct tpm2_get_random_out *resp;
+ struct tpm_header *head;
+ struct tpm_buf buf;
+ off_t offset;
+ u32 recd;
+ int ret;
+
+ if (!out || !max || max > TPM_MAX_RNG_DATA)
+ return -EINVAL;
+
+ ret = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_GET_RANDOM);
+ if (ret)
+ return ret;
+
+ if (tpm2_chip_auth(chip)) {
+ tpm_buf_append_hmac_session(chip, &buf,
+ TPM2_SA_ENCRYPT | TPM2_SA_CONTINUE_SESSION,
+ NULL, 0);
+ } else {
+ head = (struct tpm_header *)buf.data;
+ head->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
+ }
+ tpm_buf_append_u16(&buf, max);
+
+ ret = tpm_buf_fill_hmac_session(chip, &buf);
+ if (ret) {
+ tpm_buf_destroy(&buf);
+ return ret;
+ }
+
+ ret = tpm_transmit_cmd(chip, &buf, offsetof(struct tpm2_get_random_out, buffer),
+ "TPM2_GetRandom");
+
+ ret = tpm_buf_check_hmac_response(chip, &buf, ret);
+ if (ret) {
+ if (ret > 0)
+ ret = -EIO;
+
+ goto out;
+ }
+
+ head = (struct tpm_header *)buf.data;
+ offset = TPM_HEADER_SIZE;
+
+ /* Skip the parameter size field: */
+ if (be16_to_cpu(head->tag) == TPM2_ST_SESSIONS)
+ offset += 4;
+
+ resp = (struct tpm2_get_random_out *)&buf.data[offset];
+ recd = min_t(u32, be16_to_cpu(resp->size), max);
+
+ if (tpm_buf_length(&buf) <
+ TPM_HEADER_SIZE + offsetof(struct tpm2_get_random_out, buffer) + recd) {
+ ret = -EIO;
+ goto out;
+ }
+
+ memcpy(out, resp->buffer, recd);
+ return recd;
+
+out:
+ tpm2_end_auth_session(chip);
+ tpm_buf_destroy(&buf);
+ return ret;
+}
+
/**
- * tpm_get_random() - get random bytes from the TPM's RNG
- * @chip: a &struct tpm_chip instance, %NULL for the default chip
- * @out: destination buffer for the random bytes
- * @max: the max number of bytes to write to @out
+ * tpm_get_random() - Get random bytes from the TPM's RNG
+ * @chip: A &tpm_chip instance. Whenset to %NULL, the default chip is used.
+ * @out: Destination buffer for the acquired random bytes.
+ * @max: The maximum number of bytes to write to @out.
+ *
+ * Iterates pulling more bytes from TPM up until all of the @max bytes have been
+ * received.
*
- * Return: number of random bytes read or a negative error value.
+ * Returns the number of random bytes read on success.
+ * Returns -EINVAL when @out is NULL, or @max is not between zero and
+ * %TPM_MAX_RNG_DATA.
+ * Returns tpm_transmit_cmd() error codes when the TPM command results an
+ * error.
*/
int tpm_get_random(struct tpm_chip *chip, u8 *out, size_t max)
{
+ u32 num_bytes = max;
+ u8 *out_ptr = out;
+ int retries = 5;
+ int total = 0;
int rc;
- if (!out || max > TPM_MAX_RNG_DATA)
+ if (!out || !max || max > TPM_MAX_RNG_DATA)
return -EINVAL;
if (!chip)
@@ -508,11 +642,30 @@ int tpm_get_random(struct tpm_chip *chip, u8 *out, size_t max)
if (rc)
return rc;
- if (chip->flags & TPM_CHIP_FLAG_TPM2)
- rc = tpm2_get_random(chip, out, max);
- else
- rc = tpm1_get_random(chip, out, max);
+ if (chip->flags & TPM_CHIP_FLAG_TPM2) {
+ rc = tpm2_start_auth_session(chip);
+ if (rc)
+ return rc;
+ }
+
+ do {
+ if (chip->flags & TPM_CHIP_FLAG_TPM2)
+ rc = tpm2_get_random(chip, out_ptr, num_bytes);
+ else
+ rc = tpm1_get_random(chip, out_ptr, num_bytes);
+
+ if (rc < 0)
+ goto err;
+
+ out_ptr += rc;
+ total += rc;
+ num_bytes -= rc;
+ } while (retries-- && total < max);
+
+ tpm_put_ops(chip);
+ return total ? total : -EIO;
+err:
tpm_put_ops(chip);
return rc;
}
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index 02c07fef41ba..f698d01401de 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -251,7 +251,6 @@ int tpm1_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash,
int tpm1_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf);
ssize_t tpm1_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap,
const char *desc, size_t min_cap_length);
-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);
int tpm_pm_suspend(struct device *dev);
@@ -291,7 +290,6 @@ int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
struct tpm_digest *digest, u16 *digest_size_ptr);
int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
struct tpm_digest *digests);
-int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max);
ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id,
u32 *value, const char *desc);
diff --git a/drivers/char/tpm/tpm1-cmd.c b/drivers/char/tpm/tpm1-cmd.c
index b49a790f1bd5..0604e11c9778 100644
--- a/drivers/char/tpm/tpm1-cmd.c
+++ b/drivers/char/tpm/tpm1-cmd.c
@@ -511,75 +511,6 @@ ssize_t tpm1_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap,
}
EXPORT_SYMBOL_GPL(tpm1_getcap);
-#define TPM_ORD_GET_RANDOM 70
-struct tpm1_get_random_out {
- __be32 rng_data_len;
- u8 rng_data[TPM_MAX_RNG_DATA];
-} __packed;
-
-/**
- * tpm1_get_random() - get random bytes from the TPM's RNG
- * @chip: a &struct tpm_chip instance
- * @dest: destination buffer for the random bytes
- * @max: the maximum number of bytes to write to @dest
- *
- * Return:
- * * number of bytes read
- * * -errno (positive TPM return codes are masked to -EIO)
- */
-int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
-{
- struct tpm1_get_random_out *out;
- u32 num_bytes = min_t(u32, max, TPM_MAX_RNG_DATA);
- struct tpm_buf buf;
- u32 total = 0;
- int retries = 5;
- u32 recd;
- int rc;
-
- rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_RANDOM);
- if (rc)
- return rc;
-
- do {
- tpm_buf_append_u32(&buf, num_bytes);
-
- rc = tpm_transmit_cmd(chip, &buf, sizeof(out->rng_data_len),
- "attempting get random");
- if (rc) {
- if (rc > 0)
- rc = -EIO;
- goto out;
- }
-
- out = (struct tpm1_get_random_out *)&buf.data[TPM_HEADER_SIZE];
-
- recd = be32_to_cpu(out->rng_data_len);
- if (recd > num_bytes) {
- rc = -EFAULT;
- goto out;
- }
-
- if (tpm_buf_length(&buf) < TPM_HEADER_SIZE +
- sizeof(out->rng_data_len) + recd) {
- rc = -EFAULT;
- goto out;
- }
- memcpy(dest, out->rng_data, recd);
-
- dest += recd;
- total += recd;
- num_bytes -= recd;
-
- tpm_buf_reset(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_RANDOM);
- } while (retries-- && total < max);
-
- rc = total ? (int)total : -EIO;
-out:
- tpm_buf_destroy(&buf);
- return rc;
-}
-
#define TPM_ORD_PCRREAD 21
int tpm1_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf)
{
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 1f561ad3bdcf..461e85c3abe5 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -239,110 +239,6 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
return rc;
}
-struct tpm2_get_random_out {
- __be16 size;
- u8 buffer[TPM_MAX_RNG_DATA];
-} __packed;
-
-/**
- * tpm2_get_random() - get random bytes from the TPM RNG
- *
- * @chip: a &tpm_chip instance
- * @dest: destination buffer
- * @max: the max number of random bytes to pull
- *
- * Return:
- * size of the buffer on success,
- * -errno otherwise (positive TPM return codes are masked to -EIO)
- */
-int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
-{
- struct tpm2_get_random_out *out;
- struct tpm_header *head;
- struct tpm_buf buf;
- u32 recd;
- u32 num_bytes = max;
- int err;
- int total = 0;
- int retries = 5;
- u8 *dest_ptr = dest;
- off_t offset;
-
- if (!num_bytes || max > TPM_MAX_RNG_DATA)
- return -EINVAL;
-
- err = tpm2_start_auth_session(chip);
- if (err)
- return err;
-
- err = tpm_buf_init(&buf, 0, 0);
- if (err) {
- tpm2_end_auth_session(chip);
- return err;
- }
-
- do {
- tpm_buf_reset(&buf, TPM2_ST_SESSIONS, TPM2_CC_GET_RANDOM);
- if (tpm2_chip_auth(chip)) {
- tpm_buf_append_hmac_session(chip, &buf,
- TPM2_SA_ENCRYPT |
- TPM2_SA_CONTINUE_SESSION,
- NULL, 0);
- } else {
- offset = buf.handles * 4 + TPM_HEADER_SIZE;
- head = (struct tpm_header *)buf.data;
- if (tpm_buf_length(&buf) == offset)
- head->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
- }
- tpm_buf_append_u16(&buf, num_bytes);
- err = tpm_buf_fill_hmac_session(chip, &buf);
- if (err) {
- tpm_buf_destroy(&buf);
- return err;
- }
-
- err = tpm_transmit_cmd(chip, &buf,
- offsetof(struct tpm2_get_random_out,
- buffer),
- "attempting get random");
- err = tpm_buf_check_hmac_response(chip, &buf, err);
- if (err) {
- if (err > 0)
- err = -EIO;
- goto out;
- }
-
- head = (struct tpm_header *)buf.data;
- offset = TPM_HEADER_SIZE;
- /* Skip the parameter size field: */
- if (be16_to_cpu(head->tag) == TPM2_ST_SESSIONS)
- offset += 4;
-
- out = (struct tpm2_get_random_out *)&buf.data[offset];
- recd = min_t(u32, be16_to_cpu(out->size), num_bytes);
- if (tpm_buf_length(&buf) <
- TPM_HEADER_SIZE +
- offsetof(struct tpm2_get_random_out, buffer) +
- recd) {
- err = -EFAULT;
- goto out;
- }
- memcpy(dest_ptr, out->buffer, recd);
-
- dest_ptr += recd;
- total += recd;
- num_bytes -= recd;
- } while (retries-- && total < max);
-
- tpm_buf_destroy(&buf);
-
- return total ? total : -EIO;
-out:
- tpm_buf_destroy(&buf);
- tpm2_end_auth_session(chip);
- return err;
-}
-
/**
* tpm2_flush_context() - execute a TPM2_FlushContext command
* @chip: TPM chip to use
--
2.39.5
^ permalink raw reply related
* [PATCH v7 07/12] tpm2-sessions: Remove AUTH_MAX_NAMES
From: Jarkko Sakkinen @ 2025-12-16 7:44 UTC (permalink / raw)
To: linux-integrity
Cc: Jarkko Sakkinen, Peter Huewe, Jason Gunthorpe, David Howells,
Paul Moore, James Morris, Serge E. Hallyn, open list,
open list:KEYS/KEYRINGS, open list:SECURITY SUBSYSTEM
In-Reply-To: <20251216074454.2192499-1-jarkko@kernel.org>
In all of the call sites only one session is ever append. Thus, reduce
AUTH_MAX_NAMES, which leads into removing constant completely.
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
drivers/char/tpm/tpm2-sessions.c | 31 +++++++++++--------------------
1 file changed, 11 insertions(+), 20 deletions(-)
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index 3bc3c31cf512..37570dc088cf 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -72,9 +72,6 @@
#include <crypto/sha2.h>
#include <crypto/utils.h>
-/* maximum number of names the TPM must remember for authorization */
-#define AUTH_MAX_NAMES 3
-
#define AES_KEY_BYTES AES_KEYSIZE_128
#define AES_KEY_BITS (AES_KEY_BYTES*8)
@@ -136,8 +133,8 @@ struct tpm2_auth {
* handle, but they are part of the session by name, which
* we must compute and remember
*/
- u8 name[AUTH_MAX_NAMES][TPM2_MAX_NAME_SIZE];
- u16 name_size_tbl[AUTH_MAX_NAMES];
+ u8 name[TPM2_MAX_NAME_SIZE];
+ u16 name_size;
};
#ifdef CONFIG_TCG_TPM2_HMAC
@@ -261,11 +258,14 @@ EXPORT_SYMBOL_GPL(tpm2_read_public);
int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
u32 handle, u8 *name, u16 name_size)
{
-#ifdef CONFIG_TCG_TPM2_HMAC
struct tpm2_auth *auth;
- int slot;
int ret;
-#endif
+
+ if (tpm_buf_length(buf) != TPM_HEADER_SIZE) {
+ dev_err(&chip->dev, "too many handles\n");
+ ret = -EIO;
+ goto err;
+ }
if (!tpm2_chip_auth(chip)) {
tpm_buf_append_handle(chip, buf, handle);
@@ -273,12 +273,6 @@ int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
}
#ifdef CONFIG_TCG_TPM2_HMAC
- slot = (tpm_buf_length(buf) - TPM_HEADER_SIZE) / 4;
- if (slot >= AUTH_MAX_NAMES) {
- dev_err(&chip->dev, "too many handles\n");
- ret = -EIO;
- goto err;
- }
auth = chip->auth;
if (auth->session != tpm_buf_length(buf)) {
dev_err(&chip->dev, "session state malformed");
@@ -287,16 +281,14 @@ int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
}
tpm_buf_append_u32(buf, handle);
auth->session += 4;
- memcpy(auth->name[slot], name, name_size);
- auth->name_size_tbl[slot] = name_size;
+ memcpy(auth->name, name, name_size);
+ auth->name_size = name_size;
#endif
return 0;
-#ifdef CONFIG_TCG_TPM2_HMAC
err:
tpm2_end_auth_session(chip);
return ret;
-#endif
}
EXPORT_SYMBOL_GPL(tpm_buf_append_name);
@@ -665,8 +657,7 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
/* ordinal is already BE */
sha256_update(&sctx, (u8 *)&head->ordinal, sizeof(head->ordinal));
/* add the handle names */
- for (i = 0; i < handles; i++)
- sha256_update(&sctx, auth->name[i], auth->name_size_tbl[i]);
+ sha256_update(&sctx, auth->name, auth->name_size);
if (offset_s != tpm_buf_length(buf))
sha256_update(&sctx, &buf->data[offset_s],
tpm_buf_length(buf) - offset_s);
--
2.39.5
^ permalink raw reply related
* [PATCH v7 06/12] KEYS: trusted: Re-orchestrate tpm2_read_public() calls
From: Jarkko Sakkinen @ 2025-12-16 7:44 UTC (permalink / raw)
To: linux-integrity
Cc: Jarkko Sakkinen, Peter Huewe, Jason Gunthorpe, David Howells,
Paul Moore, James Morris, Serge E. Hallyn, James Bottomley,
Mimi Zohar, open list, open list:KEYS/KEYRINGS,
open list:SECURITY SUBSYSTEM
In-Reply-To: <20251216074454.2192499-1-jarkko@kernel.org>
tpm2_load_cmd() and tpm2_unseal_cmd() use the same parent, and calls to
tpm_buf_append_name() cause the exact same TPM2_ReadPublic command to be
sent to the chip, causing unnecessary traffic.
1. Export tpm2_read_public in order to make it callable from
'trusted_tpm2'.
2. Re-orchestrate tpm2_seal_trusted() and tpm2_unseal_trusted() in order to
halve the name resolutions required:
2a. Move tpm2_read_public() calls into trusted_tpm2.
2b. Pass TPM name to tpm_buf_append_name().
2c. Rework tpm_buf_append_name() to use the pre-resolved name.
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
drivers/char/tpm/tpm2-cmd.c | 3 +-
drivers/char/tpm/tpm2-sessions.c | 95 +++++------------
include/linux/tpm.h | 10 +-
security/keys/trusted-keys/trusted_tpm2.c | 124 ++++++++++++++--------
4 files changed, 118 insertions(+), 114 deletions(-)
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 3a77be7ebf4a..1f561ad3bdcf 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -202,7 +202,8 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
}
if (!disable_pcr_integrity) {
- rc = tpm_buf_append_name(chip, &buf, pcr_idx, NULL);
+ rc = tpm_buf_append_name(chip, &buf, pcr_idx, (u8 *)&pcr_idx,
+ sizeof(u32));
if (rc) {
tpm_buf_destroy(&buf);
return rc;
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index 525b8622d1c3..3bc3c31cf512 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -136,8 +136,8 @@ struct tpm2_auth {
* handle, but they are part of the session by name, which
* we must compute and remember
*/
- u32 name_h[AUTH_MAX_NAMES];
u8 name[AUTH_MAX_NAMES][TPM2_MAX_NAME_SIZE];
+ u16 name_size_tbl[AUTH_MAX_NAMES];
};
#ifdef CONFIG_TCG_TPM2_HMAC
@@ -163,7 +163,17 @@ static int name_size(const u8 *name)
}
}
-static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
+/**
+ * tpm2_read_public: Resolve TPM name for a handle
+ * @chip: TPM chip to use.
+ * @handle: TPM handle.
+ * @name: A buffer for returning the name blob. Must have a
+ * capacity of 'SHA512_DIGET_SIZE + 2' bytes at minimum
+ *
+ * Returns size of TPM handle name of success.
+ * Returns tpm_transmit_cmd error codes when TPM2_ReadPublic fails.
+ */
+int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
{
u32 mso = tpm2_handle_mso(handle);
off_t offset = TPM_HEADER_SIZE;
@@ -219,14 +229,16 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
memcpy(name, &buf.data[offset], rc);
return name_size_alg;
}
+EXPORT_SYMBOL_GPL(tpm2_read_public);
#endif /* CONFIG_TCG_TPM2_HMAC */
/**
- * tpm_buf_append_name() - add a handle area to the buffer
- * @chip: the TPM chip structure
- * @buf: The buffer to be appended
- * @handle: The handle to be appended
- * @name: The name of the handle (may be NULL)
+ * tpm_buf_append_name() - Append a handle and store TPM name
+ * @chip: TPM chip to use.
+ * @buf: TPM buffer containing the TPM command in-transit.
+ * @handle: TPM handle to be appended.
+ * @name: TPM name of the handle
+ * @name_size: Size of the TPM name.
*
* In order to compute session HMACs, we need to know the names of the
* objects pointed to by the handles. For most objects, this is simply
@@ -243,15 +255,14 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
* will be caused by an incorrect programming model and indicated by a
* kernel message.
*
- * Ends the authorization session on failure.
+ * Returns zero on success.
+ * Returns -EIO when the authorization area state is malformed.
*/
int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
- u32 handle, u8 *name)
+ u32 handle, u8 *name, u16 name_size)
{
#ifdef CONFIG_TCG_TPM2_HMAC
- enum tpm2_mso_type mso = tpm2_handle_mso(handle);
struct tpm2_auth *auth;
- u16 name_size_alg;
int slot;
int ret;
#endif
@@ -276,36 +287,15 @@ int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
}
tpm_buf_append_u32(buf, handle);
auth->session += 4;
-
- if (mso == TPM2_MSO_PERSISTENT ||
- mso == TPM2_MSO_VOLATILE ||
- mso == TPM2_MSO_NVRAM) {
- if (!name) {
- ret = tpm2_read_public(chip, handle, auth->name[slot]);
- if (ret < 0)
- goto err;
-
- name_size_alg = ret;
- }
- } else {
- if (name) {
- dev_err(&chip->dev, "handle 0x%08x does not use a name\n",
- handle);
- ret = -EIO;
- goto err;
- }
- }
-
- auth->name_h[slot] = handle;
- if (name)
- memcpy(auth->name[slot], name, name_size_alg);
+ memcpy(auth->name[slot], name, name_size);
+ auth->name_size_tbl[slot] = name_size;
#endif
return 0;
#ifdef CONFIG_TCG_TPM2_HMAC
err:
tpm2_end_auth_session(chip);
- return tpm_ret_to_err(ret);
+ return ret;
#endif
}
EXPORT_SYMBOL_GPL(tpm_buf_append_name);
@@ -613,22 +603,8 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
attrs = chip->cc_attrs_tbl[i];
handles = (attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0);
+ offset_s += handles * sizeof(u32);
- /*
- * just check the names, it's easy to make mistakes. This
- * would happen if someone added a handle via
- * tpm_buf_append_u32() instead of tpm_buf_append_name()
- */
- for (i = 0; i < handles; i++) {
- u32 handle = tpm_buf_read_u32(buf, &offset_s);
-
- if (auth->name_h[i] != handle) {
- dev_err(&chip->dev, "invalid handle 0x%08x\n", handle);
- ret = -EIO;
- goto err;
- }
- }
- /* point offset_s to the start of the sessions */
val = tpm_buf_read_u32(buf, &offset_s);
/* point offset_p to the start of the parameters */
offset_p = offset_s + val;
@@ -689,23 +665,8 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
/* ordinal is already BE */
sha256_update(&sctx, (u8 *)&head->ordinal, sizeof(head->ordinal));
/* add the handle names */
- for (i = 0; i < handles; i++) {
- enum tpm2_mso_type mso = tpm2_handle_mso(auth->name_h[i]);
-
- if (mso == TPM2_MSO_PERSISTENT ||
- mso == TPM2_MSO_VOLATILE ||
- mso == TPM2_MSO_NVRAM) {
- ret = name_size(auth->name[i]);
- if (ret < 0)
- goto err;
-
- sha256_update(&sctx, auth->name[i], ret);
- } else {
- __be32 h = cpu_to_be32(auth->name_h[i]);
-
- sha256_update(&sctx, (u8 *)&h, 4);
- }
- }
+ for (i = 0; i < handles; i++)
+ sha256_update(&sctx, auth->name[i], auth->name_size_tbl[i]);
if (offset_s != tpm_buf_length(buf))
sha256_update(&sctx, &buf->data[offset_s],
tpm_buf_length(buf) - offset_s);
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index e10f2096eae7..72610f1aa402 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -543,7 +543,7 @@ static inline struct tpm2_auth *tpm2_chip_auth(struct tpm_chip *chip)
}
int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
- u32 handle, u8 *name);
+ u32 handle, u8 *name, u16 name_size);
void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf,
u8 attributes, u8 *passphrase,
int passphraselen);
@@ -557,6 +557,7 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf);
int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf,
int rc);
void tpm2_end_auth_session(struct tpm_chip *chip);
+int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name);
#else
#include <linux/unaligned.h>
@@ -580,6 +581,13 @@ static inline int tpm_buf_check_hmac_response(struct tpm_chip *chip,
{
return rc;
}
+
+static inline int tpm2_read_public(struct tpm_chip *chip, u32 handle,
+ void *name)
+{
+ memcpy(name, &handle, sizeof(u32));
+ return sizeof(u32);
+}
#endif /* CONFIG_TCG_TPM2_HMAC */
#endif
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 3666e3e48eab..3de84b30b655 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -203,8 +203,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
struct trusted_key_payload *payload,
struct trusted_key_options *options)
{
+ u8 parent_name[TPM2_MAX_NAME_SIZE];
off_t offset = TPM_HEADER_SIZE;
struct tpm_buf buf, sized;
+ u16 parent_name_size;
int blob_len = 0;
int hash;
u32 flags;
@@ -221,6 +223,12 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
if (rc)
return rc;
+ rc = tpm2_read_public(chip, options->keyhandle, parent_name);
+ if (rc < 0)
+ goto out_put;
+
+ parent_name_size = rc;
+
rc = tpm2_start_auth_session(chip);
if (rc)
goto out_put;
@@ -238,7 +246,8 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
goto out_put;
}
- rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+ rc = tpm_buf_append_name(chip, &buf, options->keyhandle, parent_name,
+ parent_name_size);
if (rc)
goto out;
@@ -325,21 +334,25 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
}
/**
- * tpm2_load_cmd() - execute a TPM2_Load command
- *
- * @chip: TPM chip to use
- * @payload: the key data in clear and encrypted form
- * @options: authentication values and other options
- * @blob_handle: returned blob handle
+ * tpm2_load_cmd() - Execute TPM2_Load
+ * @chip: TPM chip to use.
+ * @payload: Key data in clear text.
+ * @options: Trusted key options.
+ * @parent_name: A cryptographic name, i.e. a TPMT_HA blob, of the
+ * parent key.
+ * @blob: The decoded payload for the key.
+ * @blob_handle: On success, will contain handle to the loaded keyedhash
+ * blob.
*
- * Return: 0 on success.
- * -E2BIG on wrong payload size.
- * -EPERM on tpm error status.
- * < 0 error from tpm_send.
+ * Return -E2BIG when the blob size is too small for all the data.
+ * Returns tpm_transmit_cmd() error codes when either TPM2_Load fails.
*/
static int tpm2_load_cmd(struct tpm_chip *chip,
struct trusted_key_payload *payload,
struct trusted_key_options *options,
+ u8 *parent_name,
+ u16 parent_name_size,
+ const u8 *blob,
u32 *blob_handle)
{
u8 *blob_ref __free(kfree) = NULL;
@@ -347,27 +360,13 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
unsigned int private_len;
unsigned int public_len;
unsigned int blob_len;
- u8 *blob, *pub;
+ const u8 *pub;
int rc;
u32 attrs;
- rc = tpm2_key_decode(payload, options, &blob);
- if (rc) {
- /* old form */
- blob = payload->blob;
- payload->old_format = 1;
- } else {
- /* Bind for cleanup: */
- blob_ref = blob;
- }
-
- /* new format carries keyhandle but old format doesn't */
- if (!options->keyhandle)
- return -EINVAL;
-
/* must be big enough for at least the two be16 size counts */
if (payload->blob_len < 4)
- return -EINVAL;
+ return -E2BIG;
private_len = get_unaligned_be16(blob);
@@ -403,7 +402,8 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
return rc;
}
- rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+ rc = tpm_buf_append_name(chip, &buf, options->keyhandle, parent_name,
+ parent_name_size);
if (rc)
goto out;
@@ -435,20 +435,23 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
}
/**
- * tpm2_unseal_cmd() - execute a TPM2_Unload command
+ * tpm2_unseal_cmd() - Execute TPM2_Unload
*
- * @chip: TPM chip to use
- * @payload: the key data in clear and encrypted form
- * @options: authentication values and other options
- * @blob_handle: blob handle
+ * @chip: TPM chip to use
+ * @payload: Key data in clear text.
+ * @options: Trusted key options.
+ * @parent_name: A cryptographic name, i.e. a TPMT_HA blob, of the
+ * parent key.
+ * @blob_handle: Handle to the loaded keyedhash blob.
*
- * Return: 0 on success
- * -EPERM on tpm error status
- * < 0 error from tpm_send
+ * Return -E2BIG when the blob size is too small for all the data.
+ * Returns tpm_transmit_cmd() error codes when either TPM2_Load fails.
*/
static int tpm2_unseal_cmd(struct tpm_chip *chip,
struct trusted_key_payload *payload,
struct trusted_key_options *options,
+ u8 *parent_name,
+ u16 parent_name_size,
u32 blob_handle)
{
struct tpm_buf buf;
@@ -466,7 +469,8 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
return rc;
}
- rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+ rc = tpm_buf_append_name(chip, &buf, options->keyhandle, parent_name,
+ parent_name_size);
if (rc)
goto out;
@@ -539,30 +543,60 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
}
/**
- * tpm2_unseal_trusted() - unseal the payload of a trusted key
+ * tpm2_unseal_trusted() - Unseal a trusted key
+ * @chip: TPM chip to use.
+ * @payload: Key data in clear text.
+ * @options: Trusted key options.
*
- * @chip: TPM chip to use
- * @payload: the key data in clear and encrypted form
- * @options: authentication values and other options
- *
- * Return: Same as with tpm_send.
+ * Return -E2BIG when the blob size is too small for all the data.
+ * Return -EINVAL when parent's key handle has not been set.
+ * Returns tpm_transmit_cmd() error codes when either TPM2_Load or TPM2_Unseal
+ * fails.
*/
int tpm2_unseal_trusted(struct tpm_chip *chip,
struct trusted_key_payload *payload,
struct trusted_key_options *options)
{
+ u8 *blob_ref __free(kfree) = NULL;
+ u8 parent_name[TPM2_MAX_NAME_SIZE];
+ u16 parent_name_size;
u32 blob_handle;
+ u8 *blob;
int rc;
+ /*
+ * Try to decode the provided blob as an ASN.1 blob. Assume that the
+ * blob is in the legacy format if decoding does not end successfully.
+ */
+ rc = tpm2_key_decode(payload, options, &blob);
+ if (rc) {
+ blob = payload->blob;
+ payload->old_format = 1;
+ } else {
+ blob_ref = blob;
+ }
+
+ if (!options->keyhandle)
+ return -EINVAL;
+
rc = tpm_try_get_ops(chip);
if (rc)
return rc;
- rc = tpm2_load_cmd(chip, payload, options, &blob_handle);
+ rc = tpm2_read_public(chip, options->keyhandle, parent_name);
+ if (rc < 0)
+ goto out;
+
+ parent_name_size = rc;
+
+ rc = tpm2_load_cmd(chip, payload, options, parent_name,
+ parent_name_size, blob, &blob_handle);
if (rc)
goto out;
- rc = tpm2_unseal_cmd(chip, payload, options, blob_handle);
+ rc = tpm2_unseal_cmd(chip, payload, options, parent_name,
+ parent_name_size, blob_handle);
+
tpm2_flush_context(chip, blob_handle);
out:
--
2.39.5
^ permalink raw reply related
* [PATCH v7 05/12] KEYS: trusted: Remove dead branch from tpm2_unseal_cmd
From: Jarkko Sakkinen @ 2025-12-16 7:44 UTC (permalink / raw)
To: linux-integrity
Cc: Jarkko Sakkinen, David Howells, Paul Moore, James Morris,
Serge E. Hallyn, James Bottomley, Mimi Zohar,
open list:KEYS/KEYRINGS, open list:SECURITY SUBSYSTEM, open list
In-Reply-To: <20251216074454.2192499-1-jarkko@kernel.org>
TPM2_Unseal requires TPM2_ST_SESSIONS, and tpm2_unseal_cmd() always does
set up either password or HMAC session.
Remove the branch in tpm2_unseal_cmd() conditionally setting
TPM2_ST_NO_SESSIONS. It is faulty but luckily it is never exercised at
run-time, and thus does not cause regressions.
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
security/keys/trusted-keys/trusted_tpm2.c | 10 +---------
1 file changed, 1 insertion(+), 9 deletions(-)
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index d3a5c5f2b926..3666e3e48eab 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -451,10 +451,8 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
struct trusted_key_options *options,
u32 blob_handle)
{
- struct tpm_header *head;
struct tpm_buf buf;
u16 data_len;
- int offset;
u8 *data;
int rc;
@@ -495,14 +493,8 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
tpm_buf_append_u16(&buf, options->blobauth_len);
tpm_buf_append(&buf, options->blobauth, options->blobauth_len);
- if (tpm2_chip_auth(chip)) {
+ if (tpm2_chip_auth(chip))
tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_ENCRYPT, NULL, 0);
- } else {
- offset = buf.handles * 4 + TPM_HEADER_SIZE;
- head = (struct tpm_header *)buf.data;
- if (tpm_buf_length(&buf) == offset)
- head->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
- }
}
rc = tpm_buf_fill_hmac_session(chip, &buf);
--
2.39.5
^ permalink raw reply related
* [PATCH v7 04/12] KEYS: trusted: Open code tpm2_buf_append()
From: Jarkko Sakkinen @ 2025-12-16 7:44 UTC (permalink / raw)
To: linux-integrity
Cc: Jarkko Sakkinen, Jonathan McDowell, David Howells,
Jarkko Sakkinen, Paul Moore, James Morris, Serge E. Hallyn,
James Bottomley, Mimi Zohar, open list:KEYS/KEYRINGS,
open list:SECURITY SUBSYSTEM, open list
In-Reply-To: <20251216074454.2192499-1-jarkko@kernel.org>
From: Jarkko Sakkinen <jarkko.sakkinen@opinsys.com>
tpm2_buf_append_auth() has a single call site and most of its parameters
are redundant. Open code it to the call site so that less cross-referencing
is required while browsing the source code.
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@opinsys.com>
Reviewed-by: Jonathan McDowell <noodles@earth.li>
---
v6:
- Trimmed the patch by removing comment update as it is out of scope.
---
security/keys/trusted-keys/trusted_tpm2.c | 40 ++++-------------------
1 file changed, 7 insertions(+), 33 deletions(-)
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index a7ea4a1c3bed..d3a5c5f2b926 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -190,36 +190,6 @@ int tpm2_key_priv(void *context, size_t hdrlen,
return 0;
}
-/**
- * tpm2_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer.
- *
- * @buf: an allocated tpm_buf instance
- * @session_handle: session handle
- * @nonce: the session nonce, may be NULL if not used
- * @nonce_len: the session nonce length, may be 0 if not used
- * @attributes: the session attributes
- * @hmac: the session HMAC or password, may be NULL if not used
- * @hmac_len: the session HMAC or password length, maybe 0 if not used
- */
-static void tpm2_buf_append_auth(struct tpm_buf *buf, u32 session_handle,
- const u8 *nonce, u16 nonce_len,
- u8 attributes,
- const u8 *hmac, u16 hmac_len)
-{
- tpm_buf_append_u32(buf, 9 + nonce_len + hmac_len);
- tpm_buf_append_u32(buf, session_handle);
- tpm_buf_append_u16(buf, nonce_len);
-
- if (nonce && nonce_len)
- tpm_buf_append(buf, nonce, nonce_len);
-
- tpm_buf_append_u8(buf, attributes);
- tpm_buf_append_u16(buf, hmac_len);
-
- if (hmac && hmac_len)
- tpm_buf_append(buf, hmac, hmac_len);
-}
-
/**
* tpm2_seal_trusted() - seal the payload of a trusted key
*
@@ -518,9 +488,13 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
* could repeat our actions with the exfiltrated
* password.
*/
- tpm2_buf_append_auth(&buf, options->policyhandle,
- NULL /* nonce */, 0, 0,
- options->blobauth, options->blobauth_len);
+ tpm_buf_append_u32(&buf, 9 + options->blobauth_len);
+ tpm_buf_append_u32(&buf, options->policyhandle);
+ tpm_buf_append_u16(&buf, 0);
+ tpm_buf_append_u8(&buf, 0);
+ tpm_buf_append_u16(&buf, options->blobauth_len);
+ tpm_buf_append(&buf, options->blobauth, options->blobauth_len);
+
if (tpm2_chip_auth(chip)) {
tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_ENCRYPT, NULL, 0);
} else {
--
2.39.5
^ permalink raw reply related
* [PATCH v7 03/12] tpm2-sessions: Define TPM2_NAME_MAX_SIZE
From: Jarkko Sakkinen @ 2025-12-16 7:44 UTC (permalink / raw)
To: linux-integrity
Cc: Jarkko Sakkinen, Peter Huewe, Jason Gunthorpe, David Howells,
Paul Moore, James Morris, Serge E. Hallyn, open list,
open list:KEYS/KEYRINGS, open list:SECURITY SUBSYSTEM
In-Reply-To: <20251216074454.2192499-1-jarkko@kernel.org>
Define TPM2_NAME_MAX_SIZE, which describes the maximum size for hashes
encoded as TPMT_HA, which the prime identifier used for persistent and
transient keys in TPM2 protocol.
Set its value to 'SHA512_DIGEST_SIZE + 2', as SHA512 has the largest
digest size of the algorithms in TCG algorithm repository.
In additionl, rename TPM2_NAME_SIZE as TPM2_NULL_NAME_SIZE in order to
avoid any possible confusion.
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
v6:
- Rewrote the commit message.
v2:
- Rename TPM2_NAME_SIZE as TPM2_NULL_NAME_SIZE.
---
drivers/char/tpm/tpm-sysfs.c | 2 +-
drivers/char/tpm/tpm2-sessions.c | 2 +-
include/linux/tpm.h | 37 +++++++++++++++++++++-----------
3 files changed, 27 insertions(+), 14 deletions(-)
diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c
index 94231f052ea7..4a6a27ee295d 100644
--- a/drivers/char/tpm/tpm-sysfs.c
+++ b/drivers/char/tpm/tpm-sysfs.c
@@ -314,7 +314,7 @@ static ssize_t null_name_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct tpm_chip *chip = to_tpm_chip(dev);
- int size = TPM2_NAME_SIZE;
+ int size = TPM2_NULL_NAME_SIZE;
bin2hex(buf, chip->null_key_name, size);
size *= 2;
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index 4149379665c4..525b8622d1c3 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -137,7 +137,7 @@ struct tpm2_auth {
* we must compute and remember
*/
u32 name_h[AUTH_MAX_NAMES];
- u8 name[AUTH_MAX_NAMES][2 + SHA512_DIGEST_SIZE];
+ u8 name[AUTH_MAX_NAMES][TPM2_MAX_NAME_SIZE];
};
#ifdef CONFIG_TCG_TPM2_HMAC
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 202da079d500..e10f2096eae7 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -27,9 +27,33 @@
#define TPM_DIGEST_SIZE 20 /* Max TPM v1.2 PCR size */
+/*
+ * SHA-512 is, as of today, the largest digest in the TCG algorithm repository.
+ */
#define TPM2_MAX_DIGEST_SIZE SHA512_DIGEST_SIZE
+
+/*
+ * A TPM name digest i.e., TPMT_HA, is a concatenation of TPM_ALG_ID of the
+ * name algorithm and hash of TPMT_PUBLIC.
+ */
+#define TPM2_MAX_NAME_SIZE (TPM2_MAX_DIGEST_SIZE + 2)
+
+/*
+ * The maximum number of PCR banks.
+ */
#define TPM2_MAX_PCR_BANKS 8
+/*
+ * fixed define for the size of a name. This is actually HASHALG size
+ * plus 2, so 32 for SHA256
+ */
+#define TPM2_NULL_NAME_SIZE 34
+
+/*
+ * The maximum size for an object context
+ */
+#define TPM2_MAX_CONTEXT_SIZE 4096
+
struct tpm_chip;
struct trusted_key_payload;
struct trusted_key_options;
@@ -139,17 +163,6 @@ struct tpm_chip_seqops {
/* fixed define for the curve we use which is NIST_P256 */
#define EC_PT_SZ 32
-/*
- * fixed define for the size of a name. This is actually HASHALG size
- * plus 2, so 32 for SHA256
- */
-#define TPM2_NAME_SIZE 34
-
-/*
- * The maximum size for an object context
- */
-#define TPM2_MAX_CONTEXT_SIZE 4096
-
struct tpm_chip {
struct device dev;
struct device devs;
@@ -211,7 +224,7 @@ struct tpm_chip {
/* saved context for NULL seed */
u8 null_key_context[TPM2_MAX_CONTEXT_SIZE];
/* name of NULL seed */
- u8 null_key_name[TPM2_NAME_SIZE];
+ u8 null_key_name[TPM2_NULL_NAME_SIZE];
u8 null_ec_key_x[EC_PT_SZ];
u8 null_ec_key_y[EC_PT_SZ];
struct tpm2_auth *auth;
--
2.39.5
^ permalink raw reply related
* [PATCH v7 02/12] KEYS: trusted: Use get_random_bytes_wait() instead of tpm_get_random()
From: Jarkko Sakkinen @ 2025-12-16 7:44 UTC (permalink / raw)
To: linux-integrity
Cc: Jarkko Sakkinen, Eric Biggers, David Howells, Paul Moore,
James Morris, Serge E. Hallyn, James Bottomley, Mimi Zohar,
open list:KEYS/KEYRINGS, open list:SECURITY SUBSYSTEM, open list
In-Reply-To: <20251216074454.2192499-1-jarkko@kernel.org>
Substitute remaining tpm_get_random() calls in trusted_tpm1.c with
get_random_bytes_wait() thus aligning random number generation for TPM 1.2
with the removal of '.get_random' callback.
Cc: Eric Biggers <ebiggers@kernel.org>
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
security/keys/trusted-keys/trusted_tpm1.c | 18 +++---------------
1 file changed, 3 insertions(+), 15 deletions(-)
diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c
index 7ce7e31bcdfb..3d75bb6f9689 100644
--- a/security/keys/trusted-keys/trusted_tpm1.c
+++ b/security/keys/trusted-keys/trusted_tpm1.c
@@ -371,13 +371,10 @@ static int osap(struct tpm_buf *tb, struct osapsess *s,
unsigned char ononce[TPM_NONCE_SIZE];
int ret;
- ret = tpm_get_random(chip, ononce, TPM_NONCE_SIZE);
+ ret = get_random_bytes_wait(ononce, TPM_NONCE_SIZE);
if (ret < 0)
return ret;
- if (ret != TPM_NONCE_SIZE)
- return -EIO;
-
tpm_buf_reset(tb, TPM_TAG_RQU_COMMAND, TPM_ORD_OSAP);
tpm_buf_append_u16(tb, type);
tpm_buf_append_u32(tb, handle);
@@ -464,15 +461,10 @@ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype,
memcpy(td->xorwork + SHA1_DIGEST_SIZE, sess.enonce, SHA1_DIGEST_SIZE);
sha1(td->xorwork, SHA1_DIGEST_SIZE * 2, td->xorhash);
- ret = tpm_get_random(chip, td->nonceodd, TPM_NONCE_SIZE);
+ ret = get_random_bytes_wait(td->nonceodd, TPM_NONCE_SIZE);
if (ret < 0)
goto out;
- if (ret != TPM_NONCE_SIZE) {
- ret = -EIO;
- goto out;
- }
-
ordinal = htonl(TPM_ORD_SEAL);
datsize = htonl(datalen);
pcrsize = htonl(pcrinfosize);
@@ -575,14 +567,10 @@ static int tpm_unseal(struct tpm_buf *tb,
}
ordinal = htonl(TPM_ORD_UNSEAL);
- ret = tpm_get_random(chip, nonceodd, TPM_NONCE_SIZE);
+ ret = get_random_bytes_wait(nonceodd, TPM_NONCE_SIZE);
if (ret < 0)
return ret;
- if (ret != TPM_NONCE_SIZE) {
- pr_info("tpm_get_random failed (%d)\n", ret);
- return -EIO;
- }
ret = TSS_authhmac(authdata1, keyauth, TPM_NONCE_SIZE,
enonce1, nonceodd, cont, sizeof(uint32_t),
&ordinal, bloblen, blob, 0, 0);
--
2.39.5
^ permalink raw reply related
* [PATCH v7 01/12] KEYS: trusted: Use get_random-fallback for TPM
From: Jarkko Sakkinen @ 2025-12-16 7:44 UTC (permalink / raw)
To: linux-integrity
Cc: Jarkko Sakkinen, Eric Biggers, David Howells, Paul Moore,
James Morris, Serge E. Hallyn, James Bottomley, Mimi Zohar,
open list:KEYS/KEYRINGS, open list:SECURITY SUBSYSTEM, open list
In-Reply-To: <20251216074454.2192499-1-jarkko@kernel.org>
1. tpm2_get_random() is costly when TCG_TPM2_HMAC is enabled and thus its
use should be pooled rather than directly used. This both reduces
latency and improves its predictability.
2. Linux is better off overall if every subsystem uses the same source for
generating the random numbers required.
Thus, unset '.get_random', which causes fallback to kernel_get_random().
One might argue that TPM RNG should be used for the generated trusted keys,
so that they have matching entropy with the TPM internally generated
objects.
This argument does have some weight into it but as far cryptography goes,
FIPS certification sets the exact bar, not which exact FIPS certified RNG
will be used. Thus, the rational choice is obviously to pick the lowest
latency path, which is kernel RNG.
Finally, there is an actual defence in depth benefit when using kernel RNG
as it helps to mitigate TPM firmware bugs concerning RNG implementation,
given the obfuscation by the other entropy sources.
Reviewed-by: Eric Biggers <ebiggers@kernel.org>
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
v7:
- A new patch. Simplifies follow up patches.
---
security/keys/trusted-keys/trusted_tpm1.c | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c
index 636acb66a4f6..7ce7e31bcdfb 100644
--- a/security/keys/trusted-keys/trusted_tpm1.c
+++ b/security/keys/trusted-keys/trusted_tpm1.c
@@ -6,6 +6,16 @@
* See Documentation/security/keys/trusted-encrypted.rst
*/
+/**
+ * DOC: Random Number Generation
+ *
+ * tpm_get_random() was previously used here as the RNG in order to have equal
+ * entropy with the objects fully inside the TPM. However, as far as goes,
+ * kernel RNG is equally fine, as long as long as it is FIPS certified. Also,
+ * using kernel RNG has the benefit of mitigating bugs in the TPM firmware
+ * associated with the RNG.
+ */
+
#include <crypto/hash_info.h>
#include <crypto/sha1.h>
#include <crypto/utils.h>
@@ -936,11 +946,6 @@ static int trusted_tpm_unseal(struct trusted_key_payload *p, char *datablob)
return ret;
}
-static int trusted_tpm_get_random(unsigned char *key, size_t key_len)
-{
- return tpm_get_random(chip, key, key_len);
-}
-
static int __init init_digests(void)
{
int i;
@@ -992,6 +997,5 @@ struct trusted_key_ops trusted_key_tpm_ops = {
.init = trusted_tpm_init,
.seal = trusted_tpm_seal,
.unseal = trusted_tpm_unseal,
- .get_random = trusted_tpm_get_random,
.exit = trusted_tpm_exit,
};
--
2.39.5
^ permalink raw reply related
* [PATCH v7 00/12] Streamline TPM2 HMAC sessions
From: Jarkko Sakkinen @ 2025-12-16 7:44 UTC (permalink / raw)
To: linux-integrity
Cc: Jarkko Sakkinen, David Howells, Paul Moore, James Morris,
Serge E. Hallyn, open list:KEYS/KEYRINGS,
open list:SECURITY SUBSYSTEM, open list
This patch set contains accumulated patches, which gradually improve
TPM2 HMAC session management and TPM driver memory management.
RNG test
========
I run this test both TPM1 and TPM2 chips using QEMU and swtpm:
#!/bin/sh
ctrl_c() {
kill -15 $TEST_PID
echo 0 > tracing_on
echo nop > current_tracer
echo > kprobe_events
echo > set_ftrace_filter
echo BYE
exit
}
trap ctrl_c EXIT INT
mount -t tracefs none /sys/kernel/tracing
set -e
cd /sys/kernel/tracing
echo function > current_tracer
echo p:tpm_get_random tpm_get_random > kprobe_events
echo tpm_get_random > set_ftrace_filter
echo 1 > tracing_on
TEST_PID=$(cat /dev/hwrng > /dev/null &)
echo > trace
cat trace_pipe &
sleep 10
Change Log
==========
v7:
- Updated cover letter to match better the current state of the patch
set.
v6:
- OK, so I decided to send one more update with managed allocations
moved to the tail so that it does not block reviewing more trivial
patches.
- Trimmed some of the patches and improved commit messages.
v5:
- I decided to add the managed allocation patch to this and take it from
the master branch for the time being, as it needs more eyes despite
having already one reviewed-by tag (especially tested-by tags).
Jarkko Sakkinen (12):
KEYS: trusted: Use get_random-fallback for TPM
KEYS: trusted: Use get_random_bytes_wait() instead of tpm_get_random()
tpm2-sessions: Define TPM2_NAME_MAX_SIZE
KEYS: trusted: Open code tpm2_buf_append()
KEYS: trusted: Remove dead branch from tpm2_unseal_cmd
KEYS: trusted: Re-orchestrate tpm2_read_public() calls
tpm2-sessions: Remove AUTH_MAX_NAMES
tpm: Orchestrate TPM commands in tpm_get_random()
tpm: Change tpm_get_random() opportunistic
tpm-buf: Merge TPM_BUF_BOUNDARY_ERROR and TPM_BUF_OVERFLOW
tpm-buf: Implement managed allocations
tpm-buf: Remove tpm_buf_append_handle
drivers/char/tpm/tpm-buf.c | 154 ++++-----
drivers/char/tpm/tpm-interface.c | 145 ++++++++-
drivers/char/tpm/tpm-sysfs.c | 23 +-
drivers/char/tpm/tpm.h | 3 -
drivers/char/tpm/tpm1-cmd.c | 198 ++++--------
drivers/char/tpm/tpm2-cmd.c | 371 +++++++---------------
drivers/char/tpm/tpm2-sessions.c | 272 ++++++----------
drivers/char/tpm/tpm2-space.c | 44 ++-
drivers/char/tpm/tpm_vtpm_proxy.c | 30 +-
include/linux/tpm.h | 77 +++--
security/keys/trusted-keys/trusted_tpm1.c | 70 ++--
security/keys/trusted-keys/trusted_tpm2.c | 329 ++++++++++---------
12 files changed, 776 insertions(+), 940 deletions(-)
--
2.39.5
^ permalink raw reply
* Re: [PATCH v1 00/17] tee: Use bus callbacks instead of driver callbacks
From: Sumit Garg @ 2025-12-16 7:38 UTC (permalink / raw)
To: Uwe Kleine-König
Cc: Sumit Garg, Jens Wiklander, Olivia Mackall, Herbert Xu,
Clément Léger, Alexandre Belloni, Ard Biesheuvel,
Maxime Coquelin, Alexandre Torgue, Ilias Apalodimas, Jan Kiszka,
Sudeep Holla, Christophe JAILLET, Michael Chan, Pavan Chebbi,
Rafał Miłecki, James Bottomley, Jarkko Sakkinen,
Mimi Zohar, David Howells, Paul Moore, James Morris,
Serge E. Hallyn, Peter Huewe, op-tee, linux-kernel, linux-crypto,
linux-rtc, linux-efi, linux-stm32, linux-arm-kernel,
Cristian Marussi, arm-scmi, netdev, linux-mips, linux-integrity,
keyrings, linux-security-module, Jason Gunthorpe
In-Reply-To: <dhunzydod4d7vj73llpuqemxb5er2ja4emxusr66irwf77jhhb@es4yd2axzl25>
Hi Uwe,
On Mon, Dec 15, 2025 at 3:02 PM Uwe Kleine-König
<u.kleine-koenig@baylibre.com> wrote:
>
> Hello Sumit,
>
> On Mon, Dec 15, 2025 at 04:54:11PM +0900, Sumit Garg wrote:
> > On Thu, Dec 11, 2025 at 06:14:54PM +0100, Uwe Kleine-König wrote:
> > > Hello,
> > >
> > > the objective of this series is to make tee driver stop using callbacks
> > > in struct device_driver. These were superseded by bus methods in 2006
> > > (commit 594c8281f905 ("[PATCH] Add bus_type probe, remove, shutdown
> > > methods.")) but nobody cared to convert all subsystems accordingly.
> > >
> > > Here the tee drivers are converted. The first commit is somewhat
> > > unrelated, but simplifies the conversion (and the drivers). It
> > > introduces driver registration helpers that care about setting the bus
> > > and owner. (The latter is missing in all drivers, so by using these
> > > helpers the drivers become more correct.)
> > >
> > > The patches #4 - #17 depend on the first two, so if they should be
> > > applied to their respective subsystem trees these must contain the first
> > > two patches first.
> >
> > Thanks Uwe for your efforts to clean up the boilerplate code for TEE bus
> > drivers.
>
> Thanks for your feedback. I will prepare a v2 and address your comments
> (whitespace issues and wrong callback in the shutdown method).
>
> > > Note that after patch #2 is applied, unconverted drivers provoke a
> > > warning in driver_register(), so it would be good for the user
> > > experience if the whole series goes in during a single merge window.
> >
> > +1
> >
> > I suggest the whole series goes via the Jens tree since there shouldn't
> > be any chances for conflict here.
> >
> > > So
> > > I guess an immutable branch containing the frist three patches that can
> > > be merged into the other subsystem trees would be sensible.
> > >
> > > After all patches are applied, tee_bus_type can be made private to
> > > drivers/tee as it's not used in other places any more.
> > >
> >
> > Feel free to make the tee_bus_type private as the last patch in the series
> > such that any followup driver follows this clean approach.
>
> There is a bit more to do for that than I'm willing to invest. With my
> patch series applied `tee_bus_type` is still used in
> drivers/tee/optee/device.c and drivers/tee/tee_core.c.
Oh I see, I guess we need to come with some helpers around device
register/unregister from TEE subsystem as well. Let's plan that for a
followup patch-set, I don't want this patch-set to be bloated more.
> Maybe it's
> sensible to merge these two files into a single one.
It's not possible as the design for TEE bus is to have TEE
implementation drivers like OP-TEE, AMD-TEE, TS-TEE, QTEE and so on to
register devices on the bus.
>
> The things I wonder about additionally are:
>
> - if CONFIG_OPTEE=n and CONFIG_TEE=y|m the tee bus is only used for
> drivers but not devices.
Yeah since the devices are rather added by the TEE implementation driver.
>
> - optee_register_device() calls device_create_file() on
> &optee_device->dev after device_register(&optee_device->dev).
> (Attention half-knowledge!) I think device_create_file() should not
> be called on an already registered device (or you have to send a
> uevent afterwards). This should probably use type attribute groups.
> (Or the need_supplicant attribute should be dropped as it isn't very
> useful. This would maybe be considered an ABI change however.)
The reasoning for this attribute should be explained by commit:
7269cba53d90 ("tee: optee: Fix supplicant based device enumeration").
In summary it's due to a weird dependency for devices we have with the
user-space daemon: tee-supplicant.
>
> - Why does optee_probe() in drivers/tee/optee/smc_abi.c unregister all
> optee devices in its error path (optee_unregister_devices())?
This is mostly to take care of if any device got registered before the
failure occured. Let me know if you have a better way to address that.
-Sumit
^ permalink raw reply
* Re: [PATCH] KEYS: trusted: Use get_random-fallback for TPM
From: James Bottomley @ 2025-12-16 6:48 UTC (permalink / raw)
To: Jarkko Sakkinen, Eric Biggers
Cc: linux-integrity, David Howells, Paul Moore, James Morris,
Serge E. Hallyn, Mimi Zohar, open list:KEYS/KEYRINGS,
open list:SECURITY SUBSYSTEM, open list, Jason A. Donenfeld
In-Reply-To: <aUB5IsJeWhFvX-cA@kernel.org>
On Mon, 2025-12-15 at 23:09 +0200, Jarkko Sakkinen wrote:
> Using combined entropy also decreases corrateral damage caused by
> e.g., a buggy TPM firmware, which does happen sometimes in the wild.
Just to allay concerns on this point: the random number generator of a
physical TPM is always based on a hardware entropy generating element.
NIST specifies (and FIPS testing requires) that this hardware element
conform to SP 800-90B which is about 84 pages of how a RNG should be
conditioned and tested (and certified), so there should be very little
chance of issues here.
While TPMs have had problems caused by buggy firmware in the past, it's
always affected areas that the FIPS testing doesn't cover in such depth
(like the Infineon weak prime problem). People should feel confident
in the TPM random number generator (particularly because it's the
primary boot time entropy source for the in-kernel RNG on most
laptops).
Regards,
James
^ permalink raw reply
* Re: [PATCH v15 00/28] x86: Secure Launch support for Intel TXT
From: Jarkko Sakkinen @ 2025-12-16 3:46 UTC (permalink / raw)
To: Ross Philipson
Cc: linux-kernel, x86, linux-integrity, linux-doc, linux-crypto,
kexec, linux-efi, iommu, dpsmith, tglx, mingo, bp, hpa,
dave.hansen, ardb, mjg59, James.Bottomley, peterhuewe, jgg, luto,
nivedita, herbert, davem, corbet, ebiederm, dwmw2, baolu.lu,
kanth.ghatraju, andrew.cooper3, trenchboot-devel
In-Reply-To: <20251215233316.1076248-1-ross.philipson@oracle.com>
On Mon, Dec 15, 2025 at 03:32:48PM -0800, Ross Philipson wrote:
> Secure Launch is a vendor-neutral approach to implementing TGC Dynamic
> Root of Trust (DRTM) support in the kernel. This is complementary to
> better known Static Root of Trust (SRTM) schemes such as UEFI SecureBoot.
>
> This series provides the common infrastructure along with Intel TXT
> support, without needing the tboot exokernel. Support for AMD SKINIT is
> pending the common infrastructure getting nailed down, and ARM are
> looking to build on it too.
>
> Originally, tboot were approached to see if they'd take support for
> other vendors, but they elected not to. Hence this approach instead.
>
> Work is being coordinated by the Trenchboot project, https://trenchboot.org/,
> organising Secure Launch support for upstream open source projects including
> Grub, iPXE and Xen. The goal of the Trenchboot project is to make DTRM easy
> to use. e.g. for Grub, it's simply adding "slaunch" as a command in the boot
> stanza. See https://trenchboot.org/user-docs/QUICKSTART/#linux-quick-start-guide
> for more details
>
> Patch set based on commit:
> torvalds/master/fd57572253bc356330dbe5b233c2e1d8426c66fd
>
> Depends on v3 of the following TPM patch set (note this patch
> set is being actively worked on separately):
> [PATCH v3 00/10] tpm: Decouple Trenchboot dependencies
> Message ID: 20250929194832.2913286-1-jarkko@kernel.org
>
> Finally we would like to thank everyone for their input and
> assistance. It has all been very helpful in improving the quality of
> our solution and in reviewing/strengthening our security posture.
>
> Thanks
> Ross Philipson and Daniel P. Smith
>
> Changes in v15:
>
> - Rewriting and reformatting of the cover letter, commit message and
> code comments per requests from maintainers.
> - Introduction of a early TPM driver in the x86 setup kernel to allow
> TPM extend command very early in the boot.
> - Remove previous TPM extending architecture that attempted to update
> the TPM PCRs later in the boot process.
> - Split slaunch.h into 2 files, with a new txt.h. The former contains
> platform agnostic definitions for the SL feature. The new txt.h file
> contains Intel TXT definitions from the public specs.
> - Split TPM headers up following the specifications where the
> technologies are defined.
> - Include set of split up TPM header files to allow TPM driver reuse
> in other environments (e.g. early kernel, x86).
> - Fix code formatting and type-os.
>
>
> Alec Brown (1):
> tpm: Remove main TPM header from TPM event log header
>
> Daniel P. Smith (6):
> tpm/tpm_tis: Close all localities
> tpm/tpm_tis: Address positive localities in tpm_tis_request_locality()
> Documentation/x86: Secure Launch kernel documentation
> x86: Add early SHA-1 support for Secure Launch early measurements
> x86: Add early SHA-256 support for Secure Launch early measurements
> x86: Secure Launch late initcall platform module
>
> Ross Philipson (21):
> tpm: Initial step to reorganize TPM public headers
> tpm: Move TPM1 specific definitions and functions to new headers
> tpm: Move TPM2 specific definitions and functions to new headers
> tpm: Move TPM common base definitions to new public common header
> tpm: Move platform specific definitions to the new PTP header
> tpm: Add TPM buffer support header for standalone reuse
> tpm/tpm_tis: Allow locality to be set to a different value
> tpm/sysfs: Show locality used by kernel
> x86: Secure Launch Kconfig
> x86: Secure Launch Resource Table header file
> x86: Secure Launch main header file
> x86/txt: Intel Trusted eXecution Technology (TXT) definitions
> x86/tpm: Early TPM PCR extending driver
> x86/msr: Add variable MTRR base/mask and x2apic ID registers
> x86/boot: Place TXT MLE header in the kernel_info section
> x86: Secure Launch kernel early boot stub
> x86: Secure Launch kernel late boot stub
> x86: Secure Launch SMP bringup support
> kexec: Secure Launch kexec SEXIT support
> x86/reboot: Secure Launch SEXIT support on reboot paths
> x86/efi: EFI stub DRTM launch support for Secure Launch
>
> Documentation/arch/x86/boot.rst | 21 +
> Documentation/security/index.rst | 1 +
> .../security/launch-integrity/index.rst | 11 +
> .../security/launch-integrity/principles.rst | 308 +++++++
> .../secure_launch_details.rst | 587 +++++++++++++
> .../secure_launch_overview.rst | 240 ++++++
> arch/x86/Kconfig | 14 +
> arch/x86/boot/compressed/Makefile | 8 +
> arch/x86/boot/compressed/early_tpm_extend.c | 601 ++++++++++++++
> arch/x86/boot/compressed/head_64.S | 29 +
> arch/x86/boot/compressed/kernel_info.S | 50 +-
> arch/x86/boot/compressed/sha1.c | 7 +
> arch/x86/boot/compressed/sha256.c | 6 +
> arch/x86/boot/compressed/sl_main.c | 638 +++++++++++++++
> arch/x86/boot/compressed/sl_stub.S | 770 ++++++++++++++++++
> arch/x86/boot/compressed/tpm.h | 42 +
> arch/x86/boot/compressed/vmlinux.lds.S | 7 +
> arch/x86/include/asm/msr-index.h | 5 +
> arch/x86/include/asm/realmode.h | 3 +
> arch/x86/include/asm/txt.h | 330 ++++++++
> arch/x86/include/uapi/asm/bootparam.h | 1 +
> arch/x86/kernel/Makefile | 2 +
> arch/x86/kernel/asm-offsets.c | 20 +
> arch/x86/kernel/reboot.c | 14 +
> arch/x86/kernel/setup.c | 3 +
> arch/x86/kernel/slaunch.c | 615 ++++++++++++++
> arch/x86/kernel/slmodule.c | 348 ++++++++
> arch/x86/kernel/smpboot.c | 47 +-
> arch/x86/realmode/init.c | 8 +
> arch/x86/realmode/rm/header.S | 3 +
> arch/x86/realmode/rm/trampoline_64.S | 32 +
> drivers/char/tpm/tpm-buf.c | 10 +-
> drivers/char/tpm/tpm-chip.c | 34 +-
> drivers/char/tpm/tpm-sysfs.c | 10 +
> drivers/char/tpm/tpm.h | 180 +---
> drivers/char/tpm/tpm1-cmd.c | 18 +-
> drivers/char/tpm/tpm1_structs.h | 97 +++
> drivers/char/tpm/tpm2-cmd.c | 32 +-
> drivers/char/tpm/tpm2-space.c | 13 -
> drivers/char/tpm/tpm2_structs.h | 58 ++
> drivers/char/tpm/tpm_tis_core.c | 21 +-
> drivers/char/tpm/tpm_tis_core.h | 64 +-
> drivers/firmware/efi/libstub/efistub.h | 8 +
> drivers/firmware/efi/libstub/x86-stub.c | 100 +++
> drivers/iommu/intel/dmar.c | 4 +
> include/keys/trusted_tpm.h | 1 -
> include/linux/slaunch.h | 251 ++++++
> include/linux/slr_table.h | 308 +++++++
> include/linux/tpm.h | 240 +-----
> include/linux/tpm1.h | 87 ++
> include/linux/tpm2.h | 247 ++++++
> include/linux/tpm_buf.h | 57 ++
> include/linux/tpm_command.h | 30 -
> include/linux/tpm_common.h | 99 +++
> include/linux/tpm_eventlog.h | 4 +-
> include/linux/tpm_ptp.h | 139 ++++
> kernel/kexec_core.c | 8 +
> security/keys/trusted-keys/trusted_tpm1.c | 1 -
> security/keys/trusted-keys/trusted_tpm2.c | 1 -
> 59 files changed, 6319 insertions(+), 574 deletions(-)
> create mode 100644 Documentation/security/launch-integrity/index.rst
> create mode 100644 Documentation/security/launch-integrity/principles.rst
> create mode 100644 Documentation/security/launch-integrity/secure_launch_details.rst
> create mode 100644 Documentation/security/launch-integrity/secure_launch_overview.rst
> create mode 100644 arch/x86/boot/compressed/early_tpm_extend.c
> create mode 100644 arch/x86/boot/compressed/sha1.c
> create mode 100644 arch/x86/boot/compressed/sha256.c
> create mode 100644 arch/x86/boot/compressed/sl_main.c
> create mode 100644 arch/x86/boot/compressed/sl_stub.S
> create mode 100644 arch/x86/boot/compressed/tpm.h
> create mode 100644 arch/x86/include/asm/txt.h
> create mode 100644 arch/x86/kernel/slaunch.c
> create mode 100644 arch/x86/kernel/slmodule.c
> create mode 100644 drivers/char/tpm/tpm1_structs.h
> create mode 100644 drivers/char/tpm/tpm2_structs.h
> create mode 100644 include/linux/slaunch.h
> create mode 100644 include/linux/slr_table.h
> create mode 100644 include/linux/tpm1.h
> create mode 100644 include/linux/tpm2.h
> create mode 100644 include/linux/tpm_buf.h
> delete mode 100644 include/linux/tpm_command.h
> create mode 100644 include/linux/tpm_common.h
> create mode 100644 include/linux/tpm_ptp.h
>
> --
> 2.43.7
>
Most likely I'll review this after the holidays (for heads up).
BR, Jarkko
^ permalink raw reply
* Re: [PATCH v15 13/28] x86: Secure Launch Kconfig
From: Randy Dunlap @ 2025-12-16 3:20 UTC (permalink / raw)
To: Ross Philipson, linux-kernel, x86, linux-integrity, linux-doc,
linux-crypto, kexec, linux-efi, iommu
Cc: dpsmith, tglx, mingo, bp, hpa, dave.hansen, ardb, mjg59,
James.Bottomley, peterhuewe, jarkko, jgg, luto, nivedita, herbert,
davem, corbet, ebiederm, dwmw2, baolu.lu, kanth.ghatraju,
andrew.cooper3, trenchboot-devel
In-Reply-To: <20251215233316.1076248-14-ross.philipson@oracle.com>
On 12/15/25 3:33 PM, Ross Philipson wrote:
> Add a Kconfig option for compiling in/out the Secure Launch feature.
> Secure Launch is controlled by a singel boolean on/off.
>
> Signed-off-by: Ross Philipson <ross.philipson@oracle.com>
> ---
> arch/x86/Kconfig | 14 ++++++++++++++
> 1 file changed, 14 insertions(+)
>
> diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
> index fa3b616af03a..9404d207c420 100644
> --- a/arch/x86/Kconfig
> +++ b/arch/x86/Kconfig
> @@ -1975,6 +1975,20 @@ config EFI_RUNTIME_MAP
>
> See also Documentation/ABI/testing/sysfs-firmware-efi-runtime-map.
>
> +config SECURE_LAUNCH
> + bool "Secure Launch DRTM support"
> + depends on X86_64 && X86_X2APIC && TCG_TIS && TCG_CRB
> + select CRYPTO_LIB_SHA1
> + select CRYPTO_LIB_SHA256
> + help
> + The Secure Launch feature allows a kernel to be launched directly
> + through a vendor neutral DTRM (Dynamic Root of Trust for Measurement)
DRTM
> + solution, with Intel TXT being one example. The DRTM establishes an
> + environment where the CPU measures the kernel image, employing the TPM,
> + before starting it. Secure Launch then continues the measurement chain
> + over kernel configuration information and other launch artifacts (e.g.
> + any initramfs image).
> +
> source "kernel/Kconfig.hz"
>
> config ARCH_SUPPORTS_KEXEC
--
~Randy
^ permalink raw reply
* [PATCHv3 2/2] kernel/kexec: Fix IMA when allocation happens in CMA area
From: Pingfan Liu @ 2025-12-16 1:48 UTC (permalink / raw)
To: kexec, linux-integrity
Cc: Pingfan Liu, Andrew Morton, Baoquan He, Mimi Zohar, Roberto Sassu,
Alexander Graf, Steven Chen, linux-kernel, stable
In-Reply-To: <20251216014852.8737-1-piliu@redhat.com>
*** Bug description ***
When I tested kexec with the latest kernel, I ran into the following warning:
[ 40.712410] ------------[ cut here ]------------
[ 40.712576] WARNING: CPU: 2 PID: 1562 at kernel/kexec_core.c:1001 kimage_map_segment+0x144/0x198
[...]
[ 40.816047] Call trace:
[ 40.818498] kimage_map_segment+0x144/0x198 (P)
[ 40.823221] ima_kexec_post_load+0x58/0xc0
[ 40.827246] __do_sys_kexec_file_load+0x29c/0x368
[...]
[ 40.855423] ---[ end trace 0000000000000000 ]---
*** How to reproduce ***
This bug is only triggered when the kexec target address is allocated in
the CMA area. If no CMA area is reserved in the kernel, use the "cma="
option in the kernel command line to reserve one.
*** Root cause ***
The commit 07d24902977e ("kexec: enable CMA based contiguous
allocation") allocates the kexec target address directly on the CMA area
to avoid copying during the jump. In this case, there is no IND_SOURCE
for the kexec segment. But the current implementation of
kimage_map_segment() assumes that IND_SOURCE pages exist and map them
into a contiguous virtual address by vmap().
*** Solution ***
If IMA segment is allocated in the CMA area, use its page_address()
directly.
Fixes: 07d24902977e ("kexec: enable CMA based contiguous allocation")
Signed-off-by: Pingfan Liu <piliu@redhat.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Baoquan He <bhe@redhat.com>
Cc: Alexander Graf <graf@amazon.com>
Cc: Steven Chen <chenste@linux.microsoft.com>
Cc: Mimi Zohar <zohar@linux.ibm.com>
Cc: linux-integrity@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: <stable@vger.kernel.org>
To: kexec@lists.infradead.org
---
v2 -> v3
improve commit log
kernel/kexec_core.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/kernel/kexec_core.c b/kernel/kexec_core.c
index 1a79c5b18d8f..95c585c6ddc3 100644
--- a/kernel/kexec_core.c
+++ b/kernel/kexec_core.c
@@ -960,13 +960,17 @@ void *kimage_map_segment(struct kimage *image, int idx)
kimage_entry_t *ptr, entry;
struct page **src_pages;
unsigned int npages;
+ struct page *cma;
void *vaddr = NULL;
int i;
+ cma = image->segment_cma[idx];
+ if (cma)
+ return page_address(cma);
+
addr = image->segment[idx].mem;
size = image->segment[idx].memsz;
eaddr = addr + size;
-
/*
* Collect the source pages and map them in a contiguous VA range.
*/
@@ -1007,7 +1011,8 @@ void *kimage_map_segment(struct kimage *image, int idx)
void kimage_unmap_segment(void *segment_buffer)
{
- vunmap(segment_buffer);
+ if (is_vmalloc_addr(segment_buffer))
+ vunmap(segment_buffer);
}
struct kexec_load_limit {
--
2.49.0
^ permalink raw reply related
* [PATCHv3 1/2] kernel/kexec: Change the prototype of kimage_map_segment()
From: Pingfan Liu @ 2025-12-16 1:48 UTC (permalink / raw)
To: kexec, linux-integrity
Cc: Pingfan Liu, Andrew Morton, Baoquan He, Mimi Zohar, Roberto Sassu,
Alexander Graf, Steven Chen, linux-kernel, stable
The kexec segment index will be required to extract the corresponding
information for that segment in kimage_map_segment(). Additionally,
kexec_segment already holds the kexec relocation destination address and
size. Therefore, the prototype of kimage_map_segment() can be changed.
Fixes: 07d24902977e ("kexec: enable CMA based contiguous allocation")
Signed-off-by: Pingfan Liu <piliu@redhat.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Baoquan He <bhe@redhat.com>
Cc: Mimi Zohar <zohar@linux.ibm.com>
Cc: Roberto Sassu <roberto.sassu@huawei.com>
Cc: Alexander Graf <graf@amazon.com>
Cc: Steven Chen <chenste@linux.microsoft.com>
Cc: linux-kernel@vger.kernel.org
Cc: <stable@vger.kernel.org>
To: kexec@lists.infradead.org
To: linux-integrity@vger.kernel.org
---
include/linux/kexec.h | 4 ++--
kernel/kexec_core.c | 9 ++++++---
security/integrity/ima/ima_kexec.c | 4 +---
3 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/include/linux/kexec.h b/include/linux/kexec.h
index ff7e231b0485..8a22bc9b8c6c 100644
--- a/include/linux/kexec.h
+++ b/include/linux/kexec.h
@@ -530,7 +530,7 @@ extern bool kexec_file_dbg_print;
#define kexec_dprintk(fmt, arg...) \
do { if (kexec_file_dbg_print) pr_info(fmt, ##arg); } while (0)
-extern void *kimage_map_segment(struct kimage *image, unsigned long addr, unsigned long size);
+extern void *kimage_map_segment(struct kimage *image, int idx);
extern void kimage_unmap_segment(void *buffer);
#else /* !CONFIG_KEXEC_CORE */
struct pt_regs;
@@ -540,7 +540,7 @@ static inline void __crash_kexec(struct pt_regs *regs) { }
static inline void crash_kexec(struct pt_regs *regs) { }
static inline int kexec_should_crash(struct task_struct *p) { return 0; }
static inline int kexec_crash_loaded(void) { return 0; }
-static inline void *kimage_map_segment(struct kimage *image, unsigned long addr, unsigned long size)
+static inline void *kimage_map_segment(struct kimage *image, int idx)
{ return NULL; }
static inline void kimage_unmap_segment(void *buffer) { }
#define kexec_in_progress false
diff --git a/kernel/kexec_core.c b/kernel/kexec_core.c
index 0f92acdd354d..1a79c5b18d8f 100644
--- a/kernel/kexec_core.c
+++ b/kernel/kexec_core.c
@@ -953,17 +953,20 @@ int kimage_load_segment(struct kimage *image, int idx)
return result;
}
-void *kimage_map_segment(struct kimage *image,
- unsigned long addr, unsigned long size)
+void *kimage_map_segment(struct kimage *image, int idx)
{
+ unsigned long addr, size, eaddr;
unsigned long src_page_addr, dest_page_addr = 0;
- unsigned long eaddr = addr + size;
kimage_entry_t *ptr, entry;
struct page **src_pages;
unsigned int npages;
void *vaddr = NULL;
int i;
+ addr = image->segment[idx].mem;
+ size = image->segment[idx].memsz;
+ eaddr = addr + size;
+
/*
* Collect the source pages and map them in a contiguous VA range.
*/
diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c
index 7362f68f2d8b..5beb69edd12f 100644
--- a/security/integrity/ima/ima_kexec.c
+++ b/security/integrity/ima/ima_kexec.c
@@ -250,9 +250,7 @@ void ima_kexec_post_load(struct kimage *image)
if (!image->ima_buffer_addr)
return;
- ima_kexec_buffer = kimage_map_segment(image,
- image->ima_buffer_addr,
- image->ima_buffer_size);
+ ima_kexec_buffer = kimage_map_segment(image, image->ima_segment_index);
if (!ima_kexec_buffer) {
pr_err("Could not map measurements buffer.\n");
return;
--
2.49.0
^ permalink raw reply related
* Re: [PATCH v15 17/28] x86: Add early SHA-1 support for Secure Launch early measurements
From: Eric Biggers @ 2025-12-16 0:21 UTC (permalink / raw)
To: Ross Philipson
Cc: linux-kernel, x86, linux-integrity, linux-doc, linux-crypto,
kexec, linux-efi, iommu, dpsmith, tglx, mingo, bp, hpa,
dave.hansen, ardb, mjg59, James.Bottomley, peterhuewe, jarkko,
jgg, luto, nivedita, herbert, davem, corbet, ebiederm, dwmw2,
baolu.lu, kanth.ghatraju, andrew.cooper3, trenchboot-devel
In-Reply-To: <20251215233316.1076248-18-ross.philipson@oracle.com>
On Mon, Dec 15, 2025 at 03:33:05PM -0800, Ross Philipson wrote:
> diff --git a/arch/x86/boot/compressed/sha1.c b/arch/x86/boot/compressed/sha1.c
> new file mode 100644
> index 000000000000..dd1b4cf5caf5
> --- /dev/null
> +++ b/arch/x86/boot/compressed/sha1.c
> @@ -0,0 +1,7 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2025 Apertus Solutions, LLC.
> + */
> +
> +#undef CONFIG_CRYPTO_LIB_SHA1_ARCH
> +#include "../../../../lib/crypto/sha1.c"
CONFIG_* options shouldn't be undefined like this. It seems that you're
trying to build the SHA-1 code into a pre-boot environment. This
problem was already solved in the SHA-256 code, by making
lib/crypto/sha256.c aware of __DISABLE_EXPORTS. The SHA-1 code should
use the same solution.
- Eric
^ permalink raw reply
* [PATCH v3] KEYS: trusted: Use get_random-fallback for TPM
From: Jarkko Sakkinen @ 2025-12-15 23:49 UTC (permalink / raw)
To: linux-integrity
Cc: Jarkko Sakkinen, Eric Biggers, David Howells, Paul Moore,
James Morris, Serge E. Hallyn, James Bottomley, Mimi Zohar,
open list:KEYS/KEYRINGS, open list:SECURITY SUBSYSTEM, open list
1. tpm2_get_random() is costly when TCG_TPM2_HMAC is enabled and thus its
use should be pooled rather than directly used. This both reduces
latency and improves its predictability.
2. Linux is better off overall if every subsystem uses the same source for
generating the random numbers required.
Thus, unset '.get_random', which causes fallback to kernel_get_random().
One might argue that TPM RNG should be used for the generated trusted keys,
so that they have matching entropy with the TPM internally generated
objects.
This argument does have some weight into it but as far cryptography goes,
FIPS certification sets the exact bar, not which exact FIPS certified RNG
will be used. Thus, the rational choice is obviously to pick the lowest
latency path, which is kernel RNG.
Finally, there is an actual defence in depth benefit when using kernel RNG
as it helps to mitigate TPM firmware bugs concerning RNG implementation,
given the obfuscation by the other entropy sources.
Reviewed-by: Eric Biggers <ebiggers@kernel.org>
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
v3:
- Fixed typos in the commit message.
- Moved the documentation comment to the correct location.
v2:
- Added Eric's reviewed-by tag.
- Addressed concerns from James by writing more details to the commit
message and documenting random number generation to the source
code.
---
security/keys/trusted-keys/trusted_tpm1.c | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c
index 636acb66a4f6..7ce7e31bcdfb 100644
--- a/security/keys/trusted-keys/trusted_tpm1.c
+++ b/security/keys/trusted-keys/trusted_tpm1.c
@@ -6,6 +6,16 @@
* See Documentation/security/keys/trusted-encrypted.rst
*/
+/**
+ * DOC: Random Number Generation
+ *
+ * tpm_get_random() was previously used here as the RNG in order to have equal
+ * entropy with the objects fully inside the TPM. However, as far as goes,
+ * kernel RNG is equally fine, as long as long as it is FIPS certified. Also,
+ * using kernel RNG has the benefit of mitigating bugs in the TPM firmware
+ * associated with the RNG.
+ */
+
#include <crypto/hash_info.h>
#include <crypto/sha1.h>
#include <crypto/utils.h>
@@ -936,11 +946,6 @@ static int trusted_tpm_unseal(struct trusted_key_payload *p, char *datablob)
return ret;
}
-static int trusted_tpm_get_random(unsigned char *key, size_t key_len)
-{
- return tpm_get_random(chip, key, key_len);
-}
-
static int __init init_digests(void)
{
int i;
@@ -992,6 +997,5 @@ struct trusted_key_ops trusted_key_tpm_ops = {
.init = trusted_tpm_init,
.seal = trusted_tpm_seal,
.unseal = trusted_tpm_unseal,
- .get_random = trusted_tpm_get_random,
.exit = trusted_tpm_exit,
};
--
2.39.5
^ permalink raw reply related
* [PATCH v15 26/28] x86/reboot: Secure Launch SEXIT support on reboot paths
From: Ross Philipson @ 2025-12-15 23:33 UTC (permalink / raw)
To: linux-kernel, x86, linux-integrity, linux-doc, linux-crypto,
kexec, linux-efi, iommu
Cc: ross.philipson, dpsmith, tglx, mingo, bp, hpa, dave.hansen, ardb,
mjg59, James.Bottomley, peterhuewe, jarkko, jgg, luto, nivedita,
herbert, davem, corbet, ebiederm, dwmw2, baolu.lu, kanth.ghatraju,
andrew.cooper3, trenchboot-devel
In-Reply-To: <20251215233316.1076248-1-ross.philipson@oracle.com>
If the MLE kernel is being powered off, rebooted or halted,
then SEXIT must be called. Note that the GETSEC[SEXIT] leaf
can only be called after a machine_shutdown() has been done on
these paths. The machine_shutdown() is not called on a few paths
like when poweroff action does not have a poweroff callback (into
ACPI code) or when an emergency reset is done. In these cases,
just the TXT registers are finalized but SEXIT is skipped.
Signed-off-by: Ross Philipson <ross.philipson@oracle.com>
Signed-off-by: Daniel P. Smith <dpsmith@apertussolutions.com>
---
arch/x86/kernel/reboot.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c
index 964f6b0a3d68..b1e6c1972f0b 100644
--- a/arch/x86/kernel/reboot.c
+++ b/arch/x86/kernel/reboot.c
@@ -13,6 +13,7 @@
#include <linux/objtool.h>
#include <linux/pgtable.h>
#include <linux/kexec.h>
+#include <linux/slaunch.h>
#include <acpi/reboot.h>
#include <asm/io.h>
#include <asm/apic.h>
@@ -778,6 +779,12 @@ static void native_machine_restart(char *__unused)
if (!reboot_force)
machine_shutdown();
+ /*
+ * The comments for slaunch_finalize() provides the explanation for the
+ * conditions required to do the SEXIT op reflected in the conditional
+ * parameter do_sexit.
+ */
+ slaunch_finalize(!reboot_force);
__machine_emergency_restart(0);
}
@@ -788,6 +795,8 @@ static void native_machine_halt(void)
tboot_shutdown(TB_SHUTDOWN_HALT);
+ slaunch_finalize(1);
+
stop_this_cpu(NULL);
}
@@ -796,8 +805,12 @@ static void native_machine_power_off(void)
if (kernel_can_power_off()) {
if (!reboot_force)
machine_shutdown();
+ slaunch_finalize(!reboot_force);
do_kernel_power_off();
+ } else {
+ slaunch_finalize(0);
}
+
/* A fallback in case there is no PM info available */
tboot_shutdown(TB_SHUTDOWN_HALT);
}
@@ -825,6 +838,7 @@ void machine_shutdown(void)
void machine_emergency_restart(void)
{
+ slaunch_finalize(0);
__machine_emergency_restart(1);
}
--
2.43.7
^ permalink raw reply related
* [PATCH v15 28/28] x86/efi: EFI stub DRTM launch support for Secure Launch
From: Ross Philipson @ 2025-12-15 23:33 UTC (permalink / raw)
To: linux-kernel, x86, linux-integrity, linux-doc, linux-crypto,
kexec, linux-efi, iommu
Cc: ross.philipson, dpsmith, tglx, mingo, bp, hpa, dave.hansen, ardb,
mjg59, James.Bottomley, peterhuewe, jarkko, jgg, luto, nivedita,
herbert, davem, corbet, ebiederm, dwmw2, baolu.lu, kanth.ghatraju,
andrew.cooper3, trenchboot-devel
In-Reply-To: <20251215233316.1076248-1-ross.philipson@oracle.com>
This support allows the DRTM launch to be initiated after an EFI stub
launch of the Linux kernel is done. This is accomplished by providing
a handler to jump to when a Secure Launch is in progress. This has to be
called after the EFI stub does Exit Boot Services.
Signed-off-by: Ross Philipson <ross.philipson@oracle.com>
Reviewed-by: Ard Biesheuvel <ardb@kernel.org>
---
drivers/firmware/efi/libstub/efistub.h | 8 ++
drivers/firmware/efi/libstub/x86-stub.c | 100 ++++++++++++++++++++++++
2 files changed, 108 insertions(+)
diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h
index f5ba032863a9..6e4cbf02500b 100644
--- a/drivers/firmware/efi/libstub/efistub.h
+++ b/drivers/firmware/efi/libstub/efistub.h
@@ -135,6 +135,14 @@ void efi_set_u64_split(u64 data, u32 *lo, u32 *hi)
*hi = upper_32_bits(data);
}
+static inline
+void efi_set_u64_form(u32 lo, u32 hi, u64 *data)
+{
+ u64 upper = hi;
+
+ *data = lo | upper << 32;
+}
+
/*
* Allocation types for calls to boottime->allocate_pages.
*/
diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c
index 761121a77f9e..e664dfddc173 100644
--- a/drivers/firmware/efi/libstub/x86-stub.c
+++ b/drivers/firmware/efi/libstub/x86-stub.c
@@ -9,6 +9,8 @@
#include <linux/efi.h>
#include <linux/pci.h>
#include <linux/stddef.h>
+#include <linux/slr_table.h>
+#include <linux/slaunch.h>
#include <asm/efi.h>
#include <asm/e820/types.h>
@@ -795,6 +797,101 @@ static efi_status_t efi_decompress_kernel(unsigned long *kernel_entry,
kernel_inittext_size);
}
+#if (IS_ENABLED(CONFIG_SECURE_LAUNCH))
+
+static bool efi_secure_launch_update_boot_params(struct slr_table *slrt,
+ struct boot_params *boot_params)
+{
+ struct slr_entry_intel_info *txt_info;
+ struct slr_entry_policy *policy;
+ bool updated = false;
+ int i;
+
+ txt_info = slr_next_entry_by_tag(slrt, NULL, SLR_ENTRY_INTEL_INFO);
+ if (!txt_info)
+ return false;
+
+ txt_info->boot_params_addr = (u64)boot_params;
+
+ policy = slr_next_entry_by_tag(slrt, NULL, SLR_ENTRY_ENTRY_POLICY);
+ if (!policy)
+ return false;
+
+ for (i = 0; i < policy->nr_entries; i++) {
+ if (policy->policy_entries[i].entity_type == SLR_ET_BOOT_PARAMS) {
+ policy->policy_entries[i].entity = (u64)boot_params;
+ updated = true;
+ break;
+ }
+ }
+
+ /*
+ * If this is a PE entry into EFI stub the mocked up boot params will
+ * be missing some of the setup header data needed for the second stage
+ * of the Secure Launch boot.
+ */
+ if (image) {
+ struct setup_header *hdr = (struct setup_header *)((u8 *)image->image_base +
+ offsetof(struct boot_params, hdr));
+ u64 cmdline_ptr;
+
+ boot_params->hdr.setup_sects = hdr->setup_sects;
+ boot_params->hdr.syssize = hdr->syssize;
+ boot_params->hdr.version = hdr->version;
+ boot_params->hdr.loadflags = hdr->loadflags;
+ boot_params->hdr.kernel_alignment = hdr->kernel_alignment;
+ boot_params->hdr.min_alignment = hdr->min_alignment;
+ boot_params->hdr.xloadflags = hdr->xloadflags;
+ boot_params->hdr.init_size = hdr->init_size;
+ boot_params->hdr.kernel_info_offset = hdr->kernel_info_offset;
+ efi_set_u64_form(boot_params->hdr.cmd_line_ptr, boot_params->ext_cmd_line_ptr,
+ &cmdline_ptr);
+ boot_params->hdr.cmdline_size = strlen((const char *)cmdline_ptr);
+ }
+
+ return updated;
+}
+
+static void efi_secure_launch(struct boot_params *boot_params)
+{
+ struct slr_entry_dl_info *dlinfo;
+ efi_guid_t guid = SLR_TABLE_GUID;
+ dl_handler_func handler_callback;
+ struct slr_table *slrt;
+
+ /*
+ * The presence of this table indicated a Secure Launch
+ * is being requested.
+ */
+ slrt = (struct slr_table *)get_efi_config_table(guid);
+ if (!slrt || slrt->magic != SLR_TABLE_MAGIC)
+ return;
+
+ /*
+ * Since the EFI stub library creates its own boot_params on entry, the
+ * SLRT and TXT heap have to be updated with this version.
+ */
+ if (!efi_secure_launch_update_boot_params(slrt, boot_params))
+ return;
+
+ /* Jump through DL stub to initiate Secure Launch */
+ dlinfo = slr_next_entry_by_tag(slrt, NULL, SLR_ENTRY_DL_INFO);
+
+ handler_callback = (dl_handler_func)dlinfo->dl_handler;
+
+ handler_callback(&dlinfo->bl_context);
+
+ unreachable();
+}
+
+#else
+
+static void efi_secure_launch(struct boot_params *boot_params)
+{
+}
+
+#endif /* IS_ENABLED(CONFIG_SECURE_LAUNCH) */
+
static void __noreturn enter_kernel(unsigned long kernel_addr,
struct boot_params *boot_params)
{
@@ -929,6 +1026,9 @@ void __noreturn efi_stub_entry(efi_handle_t handle,
goto fail;
}
+ /* If a Secure Launch is in progress, this never returns */
+ efi_secure_launch(boot_params);
+
/*
* Call the SEV init code while still running with the firmware's
* GDT/IDT, so #VC exceptions will be handled by EFI.
--
2.43.7
^ permalink raw reply related
* [PATCH v15 27/28] x86: Secure Launch late initcall platform module
From: Ross Philipson @ 2025-12-15 23:33 UTC (permalink / raw)
To: linux-kernel, x86, linux-integrity, linux-doc, linux-crypto,
kexec, linux-efi, iommu
Cc: ross.philipson, dpsmith, tglx, mingo, bp, hpa, dave.hansen, ardb,
mjg59, James.Bottomley, peterhuewe, jarkko, jgg, luto, nivedita,
herbert, davem, corbet, ebiederm, dwmw2, baolu.lu, kanth.ghatraju,
andrew.cooper3, trenchboot-devel
In-Reply-To: <20251215233316.1076248-1-ross.philipson@oracle.com>
From: "Daniel P. Smith" <dpsmith@apertussolutions.com>
The Secure Launch platform module is a late init module. During the
init call, the TPM event log is read and measurements taken in the
early boot stub code are located. These measurements are extended
into the TPM PCRs using the mainline TPM kernel driver.
The platform module also registers the securityfs nodes to allow
fetching and writing events events to the DRTM TPM event log. In
addition, on Intel, access to TXT register fields is made available
for reading.
Signed-off-by: Daniel P. Smith <dpsmith@apertussolutions.com>
Signed-off-by: garnetgrimm <grimmg@ainfosec.com>
Signed-off-by: Ross Philipson <ross.philipson@oracle.com>
---
arch/x86/kernel/Makefile | 1 +
arch/x86/kernel/slmodule.c | 348 +++++++++++++++++++++++++++++++++++++
2 files changed, 349 insertions(+)
create mode 100644 arch/x86/kernel/slmodule.c
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index 36ea2c12deed..c2025c8eac25 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -83,6 +83,7 @@ obj-$(CONFIG_IA32_EMULATION) += tls.o
obj-y += step.o
obj-$(CONFIG_INTEL_TXT) += tboot.o
obj-$(CONFIG_SECURE_LAUNCH) += slaunch.o
+obj-$(CONFIG_SECURE_LAUNCH) += slmodule.o
obj-$(CONFIG_ISA_DMA_API) += i8237.o
obj-y += stacktrace.o
obj-y += cpu/
diff --git a/arch/x86/kernel/slmodule.c b/arch/x86/kernel/slmodule.c
new file mode 100644
index 000000000000..407288c6c2d5
--- /dev/null
+++ b/arch/x86/kernel/slmodule.c
@@ -0,0 +1,348 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Secure Launch late validation/setup, securityfs exposure and finalization.
+ *
+ * Copyright (c) 2025 Apertus Solutions, LLC
+ * Copyright (c) 2025 Assured Information Security, Inc.
+ * Copyright (c) 2025, Oracle and/or its affiliates.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/linkage.h>
+#include <linux/mm.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/security.h>
+#include <linux/memblock.h>
+#include <linux/tpm.h>
+#include <asm/segment.h>
+#include <asm/sections.h>
+#include <crypto/sha2.h>
+#include <linux/slr_table.h>
+#include <linux/slaunch.h>
+
+/*
+ * The macro DECLARE_TXT_PUB_READ_U is used to read values from the TXT
+ * public registers as unsigned values.
+ */
+#define DECLARE_TXT_PUB_READ_U(size, fmt, msg_size) \
+static ssize_t txt_pub_read_u##size(unsigned int offset, \
+ loff_t *read_offset, \
+ size_t read_len, \
+ char __user *buf) \
+{ \
+ char msg_buffer[msg_size]; \
+ u##size reg_value = 0; \
+ void __iomem *txt; \
+ \
+ txt = ioremap(TXT_PUB_CONFIG_REGS_BASE, \
+ TXT_NR_CONFIG_PAGES * PAGE_SIZE); \
+ if (!txt) \
+ return -EFAULT; \
+ memcpy_fromio(®_value, txt + offset, sizeof(u##size)); \
+ iounmap(txt); \
+ snprintf(msg_buffer, msg_size, fmt, reg_value); \
+ return simple_read_from_buffer(buf, read_len, read_offset, \
+ &msg_buffer, msg_size); \
+}
+
+DECLARE_TXT_PUB_READ_U(8, "%#04x\n", 6);
+DECLARE_TXT_PUB_READ_U(32, "%#010x\n", 12);
+DECLARE_TXT_PUB_READ_U(64, "%#018llx\n", 20);
+
+#define DECLARE_TXT_FOPS(reg_name, reg_offset, reg_size) \
+static ssize_t txt_##reg_name##_read(struct file *flip, \
+ char __user *buf, size_t read_len, loff_t *read_offset) \
+{ \
+ return txt_pub_read_u##reg_size(reg_offset, read_offset, \
+ read_len, buf); \
+} \
+static const struct file_operations reg_name##_ops = { \
+ .read = txt_##reg_name##_read, \
+}
+
+DECLARE_TXT_FOPS(sts, TXT_CR_STS, 64);
+DECLARE_TXT_FOPS(ests, TXT_CR_ESTS, 8);
+DECLARE_TXT_FOPS(errorcode, TXT_CR_ERRORCODE, 32);
+DECLARE_TXT_FOPS(didvid, TXT_CR_DIDVID, 64);
+DECLARE_TXT_FOPS(e2sts, TXT_CR_E2STS, 64);
+DECLARE_TXT_FOPS(ver_emif, TXT_CR_VER_EMIF, 32);
+DECLARE_TXT_FOPS(scratchpad, TXT_CR_SCRATCHPAD, 64);
+
+/*
+ * Securityfs exposure
+ */
+struct memfile {
+ char *name;
+ void *addr;
+ size_t size;
+};
+
+static struct memfile sl_evtlog = { "eventlog", NULL, 0 };
+static void *txt_heap;
+static struct txt_heap_event_log_pointer2_1_element *evtlog21;
+static DEFINE_MUTEX(sl_evt_log_mutex);
+static struct tcg_efi_specid_event_head *efi_head;
+
+static ssize_t sl_evtlog_read(struct file *file, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ ssize_t size;
+
+ if (!sl_evtlog.addr)
+ return 0;
+
+ mutex_lock(&sl_evt_log_mutex);
+ size = simple_read_from_buffer(buf, count, pos, sl_evtlog.addr,
+ sl_evtlog.size);
+ mutex_unlock(&sl_evt_log_mutex);
+
+ return size;
+}
+
+static ssize_t sl_evtlog_write(struct file *file, const char __user *buf,
+ size_t datalen, loff_t *ppos)
+{
+ ssize_t result;
+ char *data;
+
+ if (!sl_evtlog.addr)
+ return 0;
+
+ /* No partial writes. */
+ result = -EINVAL;
+ if (*ppos != 0)
+ goto out;
+
+ data = memdup_user(buf, datalen);
+ if (IS_ERR(data)) {
+ result = PTR_ERR(data);
+ goto out;
+ }
+
+ mutex_lock(&sl_evt_log_mutex);
+ if (evtlog21)
+ result = tpm2_log_event(evtlog21, sl_evtlog.addr,
+ sl_evtlog.size, datalen, data);
+ else
+ result = tpm_log_event(sl_evtlog.addr, sl_evtlog.size,
+ datalen, data);
+ mutex_unlock(&sl_evt_log_mutex);
+
+ kfree(data);
+out:
+ return result;
+}
+
+static const struct file_operations sl_evtlog_ops = {
+ .read = sl_evtlog_read,
+ .write = sl_evtlog_write,
+ .llseek = default_llseek,
+};
+
+struct sfs_file {
+ const char *name;
+ const struct file_operations *fops;
+};
+
+#define SL_TXT_ENTRY_COUNT 7
+static const struct sfs_file sl_txt_files[] = {
+ { "sts", &sts_ops },
+ { "ests", &ests_ops },
+ { "errorcode", &errorcode_ops },
+ { "didvid", &didvid_ops },
+ { "ver_emif", &ver_emif_ops },
+ { "scratchpad", &scratchpad_ops },
+ { "e2sts", &e2sts_ops }
+};
+
+/* sysfs file handles */
+static struct dentry *slaunch_dir;
+static struct dentry *event_file;
+static struct dentry *txt_dir;
+static struct dentry *txt_entries[SL_TXT_ENTRY_COUNT];
+
+static long slaunch_expose_securityfs(void)
+{
+ long ret = 0;
+ int i;
+
+ slaunch_dir = securityfs_create_dir("slaunch", NULL);
+ if (IS_ERR(slaunch_dir))
+ return PTR_ERR(slaunch_dir);
+
+ if (slaunch_get_flags() & SL_FLAG_ARCH_TXT) {
+ txt_dir = securityfs_create_dir("txt", slaunch_dir);
+ if (IS_ERR(txt_dir)) {
+ ret = PTR_ERR(txt_dir);
+ goto remove_slaunch;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sl_txt_files); i++) {
+ txt_entries[i] =
+ securityfs_create_file(sl_txt_files[i].name, 0440, txt_dir,
+ NULL, sl_txt_files[i].fops);
+ if (IS_ERR(txt_entries[i])) {
+ ret = PTR_ERR(txt_entries[i]);
+ goto remove_files;
+ }
+ }
+ }
+
+ if (sl_evtlog.addr) {
+ event_file = securityfs_create_file(sl_evtlog.name, 0440,
+ slaunch_dir, NULL,
+ &sl_evtlog_ops);
+ if (IS_ERR(event_file)) {
+ ret = PTR_ERR(event_file);
+ goto remove_files;
+ }
+ }
+
+ return 0;
+
+remove_files:
+ if (slaunch_get_flags() & SL_FLAG_ARCH_TXT) {
+ while (--i >= 0)
+ securityfs_remove(txt_entries[i]);
+ securityfs_remove(txt_dir);
+ }
+
+remove_slaunch:
+ securityfs_remove(slaunch_dir);
+
+ return ret;
+}
+
+static void slaunch_teardown_securityfs(void)
+{
+ int i;
+
+ securityfs_remove(event_file);
+ if (sl_evtlog.addr) {
+ memunmap(sl_evtlog.addr);
+ sl_evtlog.addr = NULL;
+ }
+ sl_evtlog.size = 0;
+
+ if (slaunch_get_flags() & SL_FLAG_ARCH_TXT) {
+ for (i = 0; i < ARRAY_SIZE(sl_txt_files); i++)
+ securityfs_remove(txt_entries[i]);
+
+ securityfs_remove(txt_dir);
+
+ if (txt_heap) {
+ memunmap(txt_heap);
+ txt_heap = NULL;
+ }
+ }
+
+ securityfs_remove(slaunch_dir);
+}
+
+static void slaunch_intel_evtlog(void __iomem *txt)
+{
+ struct slr_entry_log_info *log_info;
+ struct txt_os_mle_data *params;
+ struct slr_table *slrt;
+ void *os_sinit_data;
+ u64 base, size;
+
+ memcpy_fromio(&base, txt + TXT_CR_HEAP_BASE, sizeof(base));
+ memcpy_fromio(&size, txt + TXT_CR_HEAP_SIZE, sizeof(size));
+
+ /* now map TXT heap */
+ txt_heap = memremap(base, size, MEMREMAP_WB);
+ if (!txt_heap)
+ slaunch_reset(txt, "Error failed to memremap TXT heap\n", SL_ERROR_HEAP_MAP);
+
+ params = (struct txt_os_mle_data *)txt_os_mle_data_start(txt_heap);
+
+ /* Get the SLRT and remap it */
+ slrt = memremap(params->slrt, sizeof(*slrt), MEMREMAP_WB);
+ if (!slrt)
+ slaunch_reset(txt, "Error failed to memremap SLR Table\n", SL_ERROR_SLRT_MAP);
+ size = slrt->size;
+ memunmap(slrt);
+
+ slrt = memremap(params->slrt, size, MEMREMAP_WB);
+ if (!slrt)
+ slaunch_reset(txt, "Error failed to memremap SLR Table\n", SL_ERROR_SLRT_MAP);
+
+ log_info = slr_next_entry_by_tag(slrt, NULL, SLR_ENTRY_LOG_INFO);
+ if (!log_info)
+ slaunch_reset(txt, "Error failed to memremap SLR Table\n", SL_ERROR_SLRT_MISSING_ENTRY);
+
+ sl_evtlog.size = log_info->size;
+ sl_evtlog.addr = memremap(log_info->addr, log_info->size, MEMREMAP_WB);
+ if (!sl_evtlog.addr)
+ slaunch_reset(txt, "Error failed to memremap TPM event log\n", SL_ERROR_EVENTLOG_MAP);
+
+ memunmap(slrt);
+
+ /* Determine if this is TPM 1.2 or 2.0 event log */
+ if (memcmp(sl_evtlog.addr + sizeof(struct tcg_pcr_event), TCG_SPECID_SIG, sizeof(TCG_SPECID_SIG)))
+ return; /* looks like it is not 2.0 */
+
+ /* For TPM 2.0 logs, the extended heap element must be located */
+ os_sinit_data = txt_os_sinit_data_start(txt_heap);
+
+ evtlog21 = txt_find_log2_1_element(os_sinit_data);
+
+ /*
+ * If this fails, things are in really bad shape. Any attempt to write
+ * events to the log will fail.
+ */
+ if (!evtlog21)
+ slaunch_reset(txt, "Error failed to find TPM20 event log element\n", SL_ERROR_TPM_INVALID_LOG20);
+
+ /* Save pointer to the EFI SpecID log header */
+ efi_head = (struct tcg_efi_specid_event_head *)(sl_evtlog.addr + sizeof(struct tcg_pcr_event));
+}
+
+static void slaunch_tpm_open_locality2(void __iomem *txt)
+{
+ struct tpm_chip *tpm;
+ int rc;
+
+ tpm = tpm_default_chip();
+ if (!tpm)
+ slaunch_reset(txt, "Could not get default TPM chip\n", SL_ERROR_TPM_INIT);
+
+ rc = tpm_chip_set_locality(tpm, 2);
+ if (rc)
+ slaunch_reset(txt, "Could not set TPM chip locality 2\n", SL_ERROR_TPM_INIT);
+}
+
+static int __init slaunch_module_init(void)
+{
+ void __iomem *txt;
+
+ /* Check to see if Secure Launch happened */
+ if ((slaunch_get_flags() & (SL_FLAG_ACTIVE|SL_FLAG_ARCH_TXT)) !=
+ (SL_FLAG_ACTIVE | SL_FLAG_ARCH_TXT))
+ return 0;
+
+ txt = ioremap(TXT_PRIV_CONFIG_REGS_BASE, TXT_NR_CONFIG_PAGES *
+ PAGE_SIZE);
+ if (!txt)
+ panic("Error ioremap of TXT priv registers\n");
+
+ /* Only Intel TXT is supported at this point */
+ slaunch_intel_evtlog(txt);
+ slaunch_tpm_open_locality2(txt);
+ iounmap(txt);
+
+ return slaunch_expose_securityfs();
+}
+
+static void __exit slaunch_module_exit(void)
+{
+ slaunch_teardown_securityfs();
+}
+
+late_initcall(slaunch_module_init);
+__exitcall(slaunch_module_exit);
--
2.43.7
^ permalink raw reply related
* [PATCH v15 22/28] x86: Secure Launch kernel early boot stub
From: Ross Philipson @ 2025-12-15 23:33 UTC (permalink / raw)
To: linux-kernel, x86, linux-integrity, linux-doc, linux-crypto,
kexec, linux-efi, iommu
Cc: ross.philipson, dpsmith, tglx, mingo, bp, hpa, dave.hansen, ardb,
mjg59, James.Bottomley, peterhuewe, jarkko, jgg, luto, nivedita,
herbert, davem, corbet, ebiederm, dwmw2, baolu.lu, kanth.ghatraju,
andrew.cooper3, trenchboot-devel
In-Reply-To: <20251215233316.1076248-1-ross.philipson@oracle.com>
The Secure Launch (SL) stub provides the entry point for Intel TXT to
jump to during the dynamic launch. The symbol sl_stub_entry is that entry
point and its offset into the kernel is conveyed to the launching code using
the Measured Launch Environment (MLE) header in the structure named mle_header.
The offset of the MLE header is set in the kernel_info.
The startup SL routines (in sl_stub.S) contain the very early dynamic launch setup
code responsible for setting up the basic operating environment to allow the normal
kernel startup_32 code to proceed. It is also responsible for properly waking
and handling the APs on Intel platforms.
The routine sl_main() runs after entering 64b mode in the setup kernel. It
is responsible for measuring configuration and module information before
it is used. An example of entities measured on Intel x86 are the boot params,
the kernel command line, the TXT heap, any external initramfs, etc. In addition
this routine does some early setup and validation of the environment like
locating the TPM event log and validating the location of various buffers to
ensure they are protected and not overlapping.
Signed-off-by: Daniel P. Smith <dpsmith@apertussolutions.com>
Signed-off-by: Ross Philipson <ross.philipson@oracle.com>
---
Documentation/arch/x86/boot.rst | 21 +
arch/x86/boot/compressed/Makefile | 2 +
arch/x86/boot/compressed/head_64.S | 29 +
arch/x86/boot/compressed/sl_main.c | 638 +++++++++++++++++++++
arch/x86/boot/compressed/sl_stub.S | 770 ++++++++++++++++++++++++++
arch/x86/include/uapi/asm/bootparam.h | 1 +
arch/x86/kernel/asm-offsets.c | 20 +
7 files changed, 1481 insertions(+)
create mode 100644 arch/x86/boot/compressed/sl_main.c
create mode 100644 arch/x86/boot/compressed/sl_stub.S
diff --git a/Documentation/arch/x86/boot.rst b/Documentation/arch/x86/boot.rst
index 77e6163288db..cb716f896392 100644
--- a/Documentation/arch/x86/boot.rst
+++ b/Documentation/arch/x86/boot.rst
@@ -482,6 +482,14 @@ Protocol: 2.00+
- If 1, KASLR enabled.
- If 0, KASLR disabled.
+ Bit 2 (kernel internal): SLAUNCH_FLAG
+
+ - Used internally by the setup kernel to communicate
+ Secure Launch status to the kernel proper.
+
+ - If 1, Secure Launch enabled.
+ - If 0, Secure Launch disabled.
+
Bit 5 (write): QUIET_FLAG
- If 0, print early messages.
@@ -1037,6 +1045,19 @@ Offset/size: 0x000c/4
This field contains maximal allowed type for setup_data and setup_indirect structs.
+============ =================
+Field name: mle_header_offset
+Offset/size: 0x0010/4
+============ =================
+
+ This field contains the offset to the Secure Launch Measured Launch Environment
+ (MLE) header. This offset is used to locate information needed during a secure
+ late launch using Intel TXT. If the offset is zero, the kernel does not have
+ Secure Launch capabilities. The MLE entry point is called from TXT on the BSP
+ following a successful measured launch. The specific state of the processors is
+ outlined in the TXT Software Development Guide, the latest can be found here:
+ https://www.intel.com/content/dam/www/public/us/en/documents/guides/intel-txt-software-development-guide.pdf
+
The Kernel Command Line
=======================
diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
index b108e0edf367..9b00772f2383 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -115,6 +115,8 @@ endif
slaunch-objs += $(obj)/sha1.o
slaunch-objs += $(obj)/sha256.o
slaunch-objs += $(obj)/early_tpm_extend.o
+slaunch-objs += $(obj)/sl_main.o
+slaunch-objs += $(obj)/sl_stub.o
vmlinux-objs-$(CONFIG_SECURE_LAUNCH) += $(slaunch-objs)
diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S
index d9dab940ff62..e841540416f0 100644
--- a/arch/x86/boot/compressed/head_64.S
+++ b/arch/x86/boot/compressed/head_64.S
@@ -412,6 +412,13 @@ SYM_CODE_START(startup_64)
pushq $0
popfq
+#ifdef CONFIG_SECURE_LAUNCH
+ /* Ensure the relocation region is covered by a PMR */
+ movq %rbx, %rdi
+ movl $(_bss - startup_32), %esi
+ callq sl_check_region
+#endif
+
/*
* Copy the compressed kernel to the end of our buffer
* where decompression in place becomes safe.
@@ -454,6 +461,28 @@ SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
shrq $3, %rcx
rep stosq
+#ifdef CONFIG_SECURE_LAUNCH
+ /*
+ * Have to do the final early sl stub work in 64b area.
+ *
+ * *********** NOTE ***********
+ *
+ * Several boot params get used before we get a chance to measure
+ * them in this call. This is a known issue and we currently don't
+ * have a solution. The scratch field doesn't matter. There is no
+ * obvious way to do anything about the use of kernel_alignment or
+ * init_size though these seem low risk with all the PMR and overlap
+ * checks in place.
+ */
+ movq %r15, %rdi
+ callq sl_main
+
+ /* Ensure the decompression location is covered by a PMR */
+ movq %rbp, %rdi
+ movq output_len(%rip), %rsi
+ callq sl_check_region
+#endif
+
call load_stage2_idt
/* Pass boot_params to initialize_identity_maps() */
diff --git a/arch/x86/boot/compressed/sl_main.c b/arch/x86/boot/compressed/sl_main.c
new file mode 100644
index 000000000000..ce927c97761c
--- /dev/null
+++ b/arch/x86/boot/compressed/sl_main.c
@@ -0,0 +1,638 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Secure Launch early measurement and validation routines.
+ *
+ * Copyright (c) 2025, Oracle and/or its affiliates.
+ */
+
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/linkage.h>
+#include <asm/segment.h>
+#include <asm/boot.h>
+#include <asm/msr.h>
+#include <asm/mtrr.h>
+#include <asm/processor-flags.h>
+#include <asm/asm-offsets.h>
+#include <asm/bootparam.h>
+#include <asm/bootparam_utils.h>
+#include <linux/slr_table.h>
+#include <linux/slaunch.h>
+
+#include "../msr.h"
+#include "tpm.h"
+
+#define CAPS_VARIABLE_MTRR_COUNT_MASK 0xff
+
+#define SL_TPM_LOG 1
+#define SL_TPM2_LOG 2
+
+static void *evtlog_base;
+static u32 evtlog_size;
+static struct txt_heap_event_log_pointer2_1_element *log21_elem;
+static u32 tpm_log_ver = SL_TPM_LOG;
+static u32 tpm_num_algs;
+static struct tcg_efi_specid_event_algs *tpm_algs;
+static u8 event_buf[PAGE_SIZE];
+
+/* Simple instance of a TPM chip object */
+static struct tpm_chip chip;
+
+extern u32 sl_cpu_type;
+extern u32 sl_mle_start;
+
+void __cold __noreturn __fortify_panic(const u8 reason, const size_t avail, const size_t size)
+{
+ asm volatile ("ud2");
+
+ unreachable();
+}
+
+static u64 sl_txt_read(u32 reg)
+{
+ return readq((void *)(u64)(TXT_PRIV_CONFIG_REGS_BASE + reg));
+}
+
+static void sl_txt_write(u32 reg, u64 val)
+{
+ writeq(val, (void *)(u64)(TXT_PRIV_CONFIG_REGS_BASE + reg));
+}
+
+static void __noreturn sl_txt_reset(u64 error)
+{
+ /* Reading the E2STS register acts as a barrier for TXT registers */
+ sl_txt_write(TXT_CR_ERRORCODE, error);
+ sl_txt_read(TXT_CR_E2STS);
+ sl_txt_write(TXT_CR_CMD_UNLOCK_MEM_CONFIG, 1);
+ sl_txt_read(TXT_CR_E2STS);
+ sl_txt_write(TXT_CR_CMD_RESET, 1);
+
+ for ( ; ; )
+ asm volatile ("hlt");
+
+ unreachable();
+}
+
+static inline u64 sl_rdmsr(u32 reg)
+{
+ struct msr m;
+
+ boot_rdmsr(reg, &m);
+
+ return m.q;
+}
+
+static struct slr_table *sl_locate_and_validate_slrt(void)
+{
+ struct txt_os_mle_data *os_mle_data;
+ struct slr_table *slrt;
+ void *txt_heap;
+
+ txt_heap = (void *)sl_txt_read(TXT_CR_HEAP_BASE);
+ os_mle_data = txt_os_mle_data_start(txt_heap);
+
+ if (!os_mle_data->slrt)
+ sl_txt_reset(SL_ERROR_INVALID_SLRT);
+
+ slrt = (struct slr_table *)os_mle_data->slrt;
+
+ if (slrt->magic != SLR_TABLE_MAGIC)
+ sl_txt_reset(SL_ERROR_INVALID_SLRT);
+
+ if (slrt->architecture != SLR_INTEL_TXT)
+ sl_txt_reset(SL_ERROR_INVALID_SLRT);
+
+ return slrt;
+}
+
+/*
+ * This is a validation routine that allows checking if a block of memory
+ * is protected from external access by being in a PMR range. If allow_hi is set,
+ * ranges above 4GB are allowed.
+ */
+static void sl_check_pmr_coverage(void *base, u32 size, bool allow_hi)
+{
+ struct txt_os_sinit_data *os_sinit_data;
+ void *end = base + size;
+ void *txt_heap;
+
+ if (!(sl_cpu_type & SL_CPU_INTEL))
+ return;
+
+ txt_heap = (void *)sl_txt_read(TXT_CR_HEAP_BASE);
+ os_sinit_data = txt_os_sinit_data_start(txt_heap);
+
+ if ((u64)end >= SZ_4G && (u64)base < SZ_4G)
+ sl_txt_reset(SL_ERROR_REGION_STRADDLE_4GB);
+
+ /*
+ * Note that the late stub code validates that the hi PMR covers
+ * all memory above 4G. At this point the code can only check that
+ * regions are within the hi PMR but that is sufficient.
+ */
+ if ((u64)end > SZ_4G && (u64)base >= SZ_4G) {
+ if (allow_hi) {
+ if (end >= (void *)(os_sinit_data->vtd_pmr_hi_base +
+ os_sinit_data->vtd_pmr_hi_size))
+ sl_txt_reset(SL_ERROR_BUFFER_BEYOND_PMR);
+ } else {
+ sl_txt_reset(SL_ERROR_REGION_ABOVE_4GB);
+ }
+ }
+
+ if (end >= (void *)os_sinit_data->vtd_pmr_lo_size)
+ sl_txt_reset(SL_ERROR_BUFFER_BEYOND_PMR);
+}
+
+/*
+ * Some MSRs are modified by the pre-launch code including the MTRRs.
+ * The early MLE code has to restore these values. This code validates
+ * the values after they are measured.
+ */
+static void sl_txt_validate_msrs(struct txt_os_mle_data *os_mle_data)
+{
+ struct slr_txt_mtrr_state *saved_bsp_mtrrs;
+ u64 mtrr_caps, mtrr_def_type, mtrr_var;
+ struct slr_entry_intel_info *txt_info;
+ u64 misc_en_msr;
+ u32 vcnt, i;
+
+ txt_info = (struct slr_entry_intel_info *)os_mle_data->txt_info;
+ saved_bsp_mtrrs = &txt_info->saved_bsp_mtrrs;
+
+ mtrr_caps = sl_rdmsr(MSR_MTRRcap);
+ vcnt = (u32)(mtrr_caps & CAPS_VARIABLE_MTRR_COUNT_MASK);
+
+ if (saved_bsp_mtrrs->mtrr_vcnt > vcnt)
+ sl_txt_reset(SL_ERROR_MTRR_INV_VCNT);
+ if (saved_bsp_mtrrs->mtrr_vcnt > TXT_OS_MLE_MAX_VARIABLE_MTRRS)
+ sl_txt_reset(SL_ERROR_MTRR_INV_VCNT);
+
+ mtrr_def_type = sl_rdmsr(MSR_MTRRdefType);
+ if (saved_bsp_mtrrs->default_mem_type != mtrr_def_type)
+ sl_txt_reset(SL_ERROR_MTRR_INV_DEF_TYPE);
+
+ for (i = 0; i < saved_bsp_mtrrs->mtrr_vcnt; i++) {
+ mtrr_var = sl_rdmsr(MTRRphysBase_MSR(i));
+ if (saved_bsp_mtrrs->mtrr_pair[i].mtrr_physbase != mtrr_var)
+ sl_txt_reset(SL_ERROR_MTRR_INV_BASE);
+ mtrr_var = sl_rdmsr(MTRRphysMask_MSR(i));
+ if (saved_bsp_mtrrs->mtrr_pair[i].mtrr_physmask != mtrr_var)
+ sl_txt_reset(SL_ERROR_MTRR_INV_MASK);
+ }
+
+ misc_en_msr = sl_rdmsr(MSR_IA32_MISC_ENABLE);
+ if (txt_info->saved_misc_enable_msr != misc_en_msr)
+ sl_txt_reset(SL_ERROR_MSR_INV_MISC_EN);
+}
+
+static void sl_find_drtm_event_log(struct slr_table *slrt)
+{
+ struct txt_os_sinit_data *os_sinit_data;
+ struct slr_entry_log_info *log_info;
+ void *txt_heap;
+
+ log_info = slr_next_entry_by_tag(slrt, NULL, SLR_ENTRY_LOG_INFO);
+ if (!log_info)
+ sl_txt_reset(SL_ERROR_SLRT_MISSING_ENTRY);
+
+ evtlog_base = (void *)log_info->addr;
+ evtlog_size = log_info->size;
+
+ txt_heap = (void *)sl_txt_read(TXT_CR_HEAP_BASE);
+
+ /*
+ * For TPM 2.0, the TXT event log 2.1 extended data structure has to also
+ * be located to find the actual log.
+ */
+ os_sinit_data = txt_os_sinit_data_start(txt_heap);
+
+ /*
+ * Only support version 6 and later that properly handle the
+ * list of ExtDataElements in the OS-SINIT structure.
+ */
+ if (os_sinit_data->version < 6)
+ sl_txt_reset(SL_ERROR_OS_SINIT_BAD_VERSION);
+
+ /* Find the TPM2.0 logging extended heap element */
+ log21_elem = txt_find_log2_1_element(os_sinit_data);
+
+ /* If found, this implies TPM2 log and family */
+ if (log21_elem)
+ tpm_log_ver = SL_TPM2_LOG;
+}
+
+static void sl_validate_event_log_buffer(void)
+{
+ struct txt_os_sinit_data *os_sinit_data;
+ void *txt_heap, *txt_end;
+ void *mle_base, *mle_end;
+ void *evtlog_end;
+
+ if ((u64)evtlog_size > (LLONG_MAX - (u64)evtlog_base))
+ sl_txt_reset(SL_ERROR_INTEGER_OVERFLOW);
+ evtlog_end = evtlog_base + evtlog_size;
+
+ txt_heap = (void *)sl_txt_read(TXT_CR_HEAP_BASE);
+ txt_end = txt_heap + sl_txt_read(TXT_CR_HEAP_SIZE);
+ os_sinit_data = txt_os_sinit_data_start(txt_heap);
+
+ mle_base = (void *)(u64)sl_mle_start;
+ mle_end = mle_base + os_sinit_data->mle_size;
+
+ /*
+ * This check is to ensure the event log buffer does not overlap with
+ * the MLE image.
+ */
+ if (evtlog_base >= mle_end && evtlog_end > mle_end)
+ goto pmr_check; /* above */
+
+ if (evtlog_end <= mle_base && evtlog_base < mle_base)
+ goto pmr_check; /* below */
+
+ sl_txt_reset(SL_ERROR_MLE_BUFFER_OVERLAP);
+
+pmr_check:
+ /*
+ * The TXT heap is protected by the DPR. If the TPM event log is
+ * inside the TXT heap, there is no need for a PMR check.
+ */
+ if (evtlog_base > txt_heap && evtlog_end < txt_end)
+ return;
+
+ sl_check_pmr_coverage(evtlog_base, evtlog_size, true);
+}
+
+static void sl_find_event_log_algorithms(void)
+{
+ struct tcg_efi_specid_event_head *efi_head =
+ (struct tcg_efi_specid_event_head *)(evtlog_base + sizeof(struct tcg_pcr_event));
+ u32 i;
+
+ if (efi_head->num_algs == 0)
+ sl_txt_reset(SL_ERROR_TPM_INVALID_ALGS);
+
+ tpm_algs = &efi_head->digest_sizes[0];
+ tpm_num_algs = efi_head->num_algs;
+
+ for (i = 0; i < tpm_num_algs; i++) {
+ if (tpm_algs[i].digest_size > TPM2_MAX_DIGEST_SIZE)
+ sl_txt_reset(SL_ERROR_TPM_INVALID_ALGS);
+ /* Alg ID 0 is invalid and maps to TPM_ALG_ERROR */
+ if (tpm_algs[i].alg_id == TPM_ALG_ERROR)
+ sl_txt_reset(SL_ERROR_TPM_INVALID_ALGS);
+ }
+}
+
+static void sl_tpm1_extend(u32 pcr, u32 event_type,
+ const u8 *data, u32 length,
+ const u8 *event_data, u32 event_size)
+{
+ u8 sha1_hash[SHA1_DIGEST_SIZE] = {0};
+ struct tcg_pcr_event *pcr_event;
+ u32 total_size;
+
+ /* Clear on each use */
+ memset(event_buf, 0, PAGE_SIZE);
+
+ pcr_event = (struct tcg_pcr_event *)event_buf;
+ pcr_event->pcr_idx = pcr;
+ pcr_event->event_type = event_type;
+ if (length > 0) {
+ sha1(data, length, &sha1_hash[0]);
+ memcpy(&pcr_event->digest[0], &sha1_hash[0], SHA1_DIGEST_SIZE);
+ }
+ pcr_event->event_size = event_size;
+ if (event_size > 0)
+ memcpy((u8 *)pcr_event + sizeof(*pcr_event),
+ event_data, event_size);
+
+ total_size = sizeof(*pcr_event) + event_size;
+
+ /* Do the TPM extend then log the event */
+ if (tpm1_pcr_extend(&chip, pcr, &sha1_hash[0]))
+ sl_txt_reset(SL_ERROR_TPM_EXTEND);
+
+ if (tpm_log_event(evtlog_base, evtlog_size, total_size, pcr_event))
+ sl_txt_reset(SL_ERROR_TPM_LOGGING_FAILED);
+}
+
+static void sl_tpm2_extend(u32 pcr, u32 event_type,
+ const u8 *data, u32 length,
+ const u8 *event_data, u32 event_size)
+{
+ struct sha256_ctx sctx256 = {0};
+ struct tcg_pcr_event2_head *head;
+ struct tcg_event_field *event;
+ u8 digest[TPM2_MAX_DIGEST_SIZE];
+ u32 total_size, alg_idx;
+ u16 *alg_ptr;
+ u8 *dgst_ptr;
+ int rc;
+
+ /* Clear on each use */
+ memset(event_buf, 0, PAGE_SIZE);
+
+ head = (struct tcg_pcr_event2_head *)event_buf;
+ head->pcr_idx = pcr;
+ head->event_type = event_type;
+ total_size = sizeof(*head);
+ alg_ptr = (u16 *)(event_buf + sizeof(*head));
+
+ for (alg_idx = 0; alg_idx < tpm_num_algs; alg_idx++) {
+ memset(digest, 0, TPM2_MAX_DIGEST_SIZE);
+
+ *alg_ptr = tpm_algs[alg_idx].alg_id;
+ dgst_ptr = (u8 *)alg_ptr + sizeof(u16);
+
+ if (tpm_algs[alg_idx].alg_id == TPM_ALG_SHA256) {
+ sha256_init(&sctx256);
+ sha256_update(&sctx256, data, length);
+ sha256_final(&sctx256, &digest[0]);
+ } else if (tpm_algs[alg_idx].alg_id == TPM_ALG_SHA1) {
+ sha1(data, length, &digest[0]);
+ } else {
+ /*
+ * If there are TPM banks in use that are not supported
+ * in software here, the PCR in that bank will be capped with
+ * the well-known value 1 as the Intel ACM does.
+ */
+ digest[0] = 0x01;
+ }
+
+ memcpy(dgst_ptr, &digest[0], tpm_algs[alg_idx].digest_size);
+ total_size += tpm_algs[alg_idx].digest_size + sizeof(u16);
+ alg_ptr = (u16 *)((u8 *)alg_ptr + tpm_algs[alg_idx].digest_size + sizeof(u16));
+
+ head->count++;
+ }
+
+ event = (struct tcg_event_field *)(event_buf + total_size);
+ event->event_size = event_size;
+ if (event_size > 0)
+ memcpy((u8 *)event + sizeof(*event), event_data, event_size);
+ total_size += sizeof(*event) + event_size;
+
+ /*
+ * Do the TPM extend then log the event. Note the digest list is packed in the event behind the
+ * event header.
+ */
+ rc = tpm2_pcr_extend(&chip, pcr, (struct tpm_digest *)(event_buf + sizeof(*head)), head->count);
+ if (rc)
+ sl_txt_reset(SL_ERROR_TPM_EXTEND);
+
+ if (tpm2_log_event(log21_elem, evtlog_base, evtlog_size, total_size, &event_buf[0]))
+ sl_txt_reset(SL_ERROR_TPM_LOGGING_FAILED);
+}
+
+static void sl_tpm_extend(u32 pcr, u32 type, const u8 *data, u32 length, const char *desc)
+{
+ if (chip.family == TPM_FAMILY_20)
+ sl_tpm2_extend(pcr, type, data, length, (const u8 *)desc, strlen(desc));
+ else
+ sl_tpm1_extend(pcr, type, data, length, (const u8 *)desc, strlen(desc));
+}
+
+static struct setup_data *sl_handle_setup_data(struct setup_data *curr,
+ struct slr_policy_entry *entry)
+{
+ struct setup_indirect *ind;
+ struct setup_data *next;
+
+ if (!curr)
+ return NULL;
+
+ next = (struct setup_data *)(unsigned long)curr->next;
+
+ /* SETUP_INDIRECT instances have to be handled differently */
+ if (curr->type == SETUP_INDIRECT) {
+ ind = (struct setup_indirect *)((u8 *)curr + offsetof(struct setup_data, data));
+
+ sl_check_pmr_coverage((void *)ind->addr, ind->len, true);
+
+ sl_tpm_extend(entry->pcr, SL_EVTYPE_SECURE_LAUNCH, (void *)ind->addr, ind->len,
+ entry->evt_info);
+
+ return next;
+ }
+
+ sl_check_pmr_coverage(((u8 *)curr) + sizeof(*curr),
+ curr->len, true);
+
+ sl_tpm_extend(entry->pcr, SL_EVTYPE_SECURE_LAUNCH, ((u8 *)curr) + sizeof(*curr), curr->len,
+ entry->evt_info);
+
+ return next;
+}
+
+/*
+ * The setup_data linked list in the boot_params (if present) must be
+ * processed element by element. Indirect elements need to have their
+ * pointers followed to the actual data to measure.
+ */
+static void sl_extend_setup_data(struct slr_policy_entry *entry)
+{
+ struct setup_data *data;
+
+ /*
+ * Measure any setup_data entries including e820 extended entries.
+ * Note that the e820 fixed entries are in the boot params structure
+ * itself and measured there.
+ */
+ data = (struct setup_data *)(unsigned long)entry->entity;
+ while (data)
+ data = sl_handle_setup_data(data, entry);
+}
+
+static void sl_extend_slrt(struct slr_policy_entry *entry)
+{
+ struct slr_table *slrt = (struct slr_table *)entry->entity;
+ struct slr_entry_intel_info *intel_info;
+ struct slr_entry_intel_info intel_tmp;
+
+ /*
+ * In revision one of the SLRT, the only table that needs to be
+ * measured is the Intel info table. Everything else is meta-data,
+ * addresses and sizes. Note the size of what to measure is not set.
+ * The flag SLR_POLICY_IMPLICIT_SIZE leaves it to the measuring code
+ * to sort out.
+ */
+ if (slrt->revision == 1) {
+ intel_info = slr_next_entry_by_tag(slrt, NULL, SLR_ENTRY_INTEL_INFO);
+ if (!intel_info)
+ sl_txt_reset(SL_ERROR_SLRT_MISSING_ENTRY);
+
+ /*
+ * Make a temp copy and zero out address fields since they should
+ * not be measured.
+ */
+ intel_tmp = *intel_info;
+ intel_tmp.boot_params_addr = 0;
+ intel_tmp.txt_heap = 0;
+
+ sl_tpm_extend(entry->pcr, SL_EVTYPE_SECURE_LAUNCH, (void *)&intel_tmp,
+ sizeof(*intel_info), entry->evt_info);
+ }
+}
+
+static void sl_extend_txt_os2mle(struct slr_policy_entry *entry)
+{
+ struct txt_os_mle_data *os_mle_data;
+ void *txt_heap;
+
+ txt_heap = (void *)sl_txt_read(TXT_CR_HEAP_BASE);
+ os_mle_data = txt_os_mle_data_start(txt_heap);
+
+ /*
+ * Version 1 of the OS-MLE heap structure has no fields to measure. It just
+ * has addresses and sizes and a scratch buffer.
+ */
+ if (os_mle_data->version == 1)
+ return;
+}
+
+/*
+ * Process all policy entries and extend the measurements to the evtlog. Note
+ * that some entries need special processing which is done in subroutines.
+ */
+static void sl_process_extend_policy(struct slr_table *slrt)
+{
+ struct slr_entry_policy *policy;
+ u16 i;
+
+ policy = slr_next_entry_by_tag(slrt, NULL, SLR_ENTRY_ENTRY_POLICY);
+ if (!policy)
+ sl_txt_reset(SL_ERROR_SLRT_MISSING_ENTRY);
+
+ for (i = 0; i < policy->nr_entries; i++) {
+ switch (policy->policy_entries[i].entity_type) {
+ case SLR_ET_SETUP_DATA:
+ sl_extend_setup_data(&policy->policy_entries[i]);
+ break;
+ case SLR_ET_SLRT:
+ sl_extend_slrt(&policy->policy_entries[i]);
+ break;
+ case SLR_ET_TXT_OS2MLE:
+ sl_extend_txt_os2mle(&policy->policy_entries[i]);
+ break;
+ case SLR_ET_UNUSED:
+ continue;
+ default:
+ sl_tpm_extend(policy->policy_entries[i].pcr, SL_EVTYPE_SECURE_LAUNCH,
+ (void *)policy->policy_entries[i].entity,
+ policy->policy_entries[i].size,
+ policy->policy_entries[i].evt_info);
+ }
+ }
+}
+
+/*
+ * Process all EFI config entries and extend the measurements to the evtlog
+ */
+static void sl_process_extend_uefi_config(struct slr_table *slrt)
+{
+ struct slr_entry_uefi_config *uefi_config;
+ u16 i;
+
+ uefi_config = slr_next_entry_by_tag(slrt, NULL, SLR_ENTRY_UEFI_CONFIG);
+
+ /* Optionally here depending on how SL kernel was booted */
+ if (!uefi_config)
+ return;
+
+ for (i = 0; i < uefi_config->nr_entries; i++) {
+ sl_tpm_extend(uefi_config->uefi_cfg_entries[i].pcr, SL_EVTYPE_SECURE_LAUNCH,
+ (void *)uefi_config->uefi_cfg_entries[i].cfg,
+ uefi_config->uefi_cfg_entries[i].size,
+ uefi_config->uefi_cfg_entries[i].evt_info);
+ }
+}
+
+asmlinkage __visible void sl_check_region(void *base, u32 size)
+{
+ sl_check_pmr_coverage(base, size, false);
+}
+
+asmlinkage __visible void sl_main(void *bootparams)
+{
+ struct boot_params *bp = (struct boot_params *)bootparams;
+ struct txt_os_mle_data *os_mle_data;
+ struct slr_table *slrt;
+ void *txt_heap;
+
+ /*
+ * Ensure loadflags do not indicate a secure launch was done
+ * unless it really was.
+ */
+ bp->hdr.loadflags &= ~SLAUNCH_FLAG;
+
+ /*
+ * Currently only Intel TXT is supported for Secure Launch. Testing
+ * this value also indicates that the kernel was booted successfully
+ * through the Secure Launch entry point and is in SMX mode.
+ */
+ if (!(sl_cpu_type & SL_CPU_INTEL))
+ return;
+
+ /* Find the SLRT setup by the pre-launch stage */
+ slrt = sl_locate_and_validate_slrt();
+
+ /* Locate the TPM event log. */
+ sl_find_drtm_event_log(slrt);
+
+ /* Validate the location of the event log buffer before using it */
+ sl_validate_event_log_buffer();
+
+ /*
+ * Find the TPM hash algorithms used by the ACM and recorded in the
+ * event log.
+ */
+ if (tpm_log_ver == SL_TPM2_LOG)
+ sl_find_event_log_algorithms();
+
+ /*
+ * Prepare the early TPM driver to do PCR extends for the DRTM
+ * measurements. On a successful DRTM launch, TPM locality 2
+ * should be available to open/acquire.
+ *
+ * Note that the early TPM driver does not use interrupts but
+ * rather polling for command completion (there is no infrastructure
+ * setup for servicing interrupts in the setup kernel).
+ */
+ if (early_tpm_init(&chip, TIS_MEM_X86_LPC_BASE))
+ sl_txt_reset(SL_ERROR_TPM_INIT);
+ if (tpm_tis_request_locality(&chip, TPM_LOCALITY_2) < 0)
+ sl_txt_reset(SL_ERROR_TPM_INIT);
+ if (chip.family == TPM_FAMILY_20 && tpm_log_ver != SL_TPM2_LOG)
+ sl_txt_reset(SL_ERROR_TPM_INIT);
+ tpm_tis_disable_interrupts(&chip);
+
+ /*
+ * Sanitize them before measuring. Set the SLAUNCH_FLAG early since if
+ * anything fails, the system will reset anyway.
+ */
+ sanitize_boot_params(bp);
+ bp->hdr.loadflags |= SLAUNCH_FLAG;
+
+ sl_check_pmr_coverage(bootparams, PAGE_SIZE, false);
+
+ /*
+ * Extend measurements into the TPM for entities specified in the
+ * SLRT policies.
+ */
+ sl_process_extend_policy(slrt);
+ sl_process_extend_uefi_config(slrt);
+
+ /* No PMR check is needed, the TXT heap is covered by the DPR */
+ txt_heap = (void *)sl_txt_read(TXT_CR_HEAP_BASE);
+ os_mle_data = txt_os_mle_data_start(txt_heap);
+
+ /*
+ * Now that the OS-MLE data is measured, ensure the MTRR and
+ * misc enable MSRs are what we expect.
+ */
+ sl_txt_validate_msrs(os_mle_data);
+
+ /* Shut down early TPM driver, release localities */
+ early_tpm_fini(&chip);
+}
diff --git a/arch/x86/boot/compressed/sl_stub.S b/arch/x86/boot/compressed/sl_stub.S
new file mode 100644
index 000000000000..f90807362215
--- /dev/null
+++ b/arch/x86/boot/compressed/sl_stub.S
@@ -0,0 +1,770 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Secure Launch protected mode entry point.
+ *
+ * Copyright (c) 2025, Oracle and/or its affiliates.
+ */
+ .code32
+ .text
+#include <linux/linkage.h>
+#include <asm/segment.h>
+#include <asm/msr.h>
+#include <asm/apicdef.h>
+#include <asm/trapnr.h>
+#include <asm/processor-flags.h>
+#include <asm/asm-offsets.h>
+#include <asm/bootparam.h>
+#include <asm/page_types.h>
+#include <asm/irq_vectors.h>
+#include <linux/slr_table.h>
+#include <linux/slaunch.h>
+
+/* CPUID: leaf 1, ECX, SMX feature bit */
+#define X86_FEATURE_BIT_SMX (1 << 6)
+
+#define IDT_VECTOR_LO_BITS 0
+#define IDT_VECTOR_HI_BITS 6
+
+/*
+ * See the comment in head_64.S for detailed information on what this macro
+ * and others like it are used for. The comment appears right at the top of
+ * the file.
+ */
+#define rva(X) ((X) - sl_stub_entry)
+
+/*
+ * The GETSEC op code is open coded because older versions of
+ * GCC do not support the getsec mnemonic.
+ */
+.macro GETSEC leaf
+ pushl %ebx
+ xorl %ebx, %ebx /* Must be zero for SMCTRL */
+ movl \leaf, %eax /* Leaf function */
+ .byte 0x0f, 0x37 /* GETSEC opcode */
+ popl %ebx
+.endm
+
+.macro TXT_RESET error
+ /*
+ * Set a sticky error value and reset. Note the movs to %eax act as
+ * TXT register barriers.
+ */
+ movl \error, (TXT_PRIV_CONFIG_REGS_BASE + TXT_CR_ERRORCODE)
+ movl (TXT_PRIV_CONFIG_REGS_BASE + TXT_CR_E2STS), %eax
+ movl $1, (TXT_PRIV_CONFIG_REGS_BASE + TXT_CR_CMD_NO_SECRETS)
+ movl (TXT_PRIV_CONFIG_REGS_BASE + TXT_CR_E2STS), %eax
+ movl $1, (TXT_PRIV_CONFIG_REGS_BASE + TXT_CR_CMD_UNLOCK_MEM_CONFIG)
+ movl (TXT_PRIV_CONFIG_REGS_BASE + TXT_CR_E2STS), %eax
+ movl $1, (TXT_PRIV_CONFIG_REGS_BASE + TXT_CR_CMD_RESET)
+1:
+ hlt
+ jmp 1b
+.endm
+
+ .code32
+SYM_FUNC_START(sl_stub_entry)
+ cli
+ cld
+
+ /*
+ * On entry, %ebx has the entry absolute offset to sl_stub_entry. The rva()
+ * macro is used to generate relative references using %ebx as a base, as
+ * to avoid absolute relocations, which would require fixups at runtime.
+ * Only %cs and %ds segments are known good after a TXT launch and can be
+ * used to establish a new GDT and segments.
+ */
+
+ /* Load GDT, set segment regs and lret to __SL32_CS */
+ leal rva(sl_gdt_desc)(%ebx), %eax
+ addl %eax, 2(%eax)
+ lgdt (%eax)
+
+ movl $(__SL32_DS), %eax
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+ movw %ax, %ss
+
+ /*
+ * Now that %ss is known good, take the first stack for the BSP. The
+ * AP stacks are only used on Intel.
+ */
+ leal rva(sl_stacks_end)(%ebx), %esp
+
+ leal rva(.Lsl_cs)(%ebx), %eax
+ pushl $(__SL32_CS)
+ pushl %eax
+ lret
+
+.Lsl_cs:
+ /* Save our base pointer reg and page table for MLE */
+ pushl %ebx
+ pushl %ecx
+
+ /* See if SMX feature is supported. */
+ movl $1, %eax
+ cpuid
+ testl $(X86_FEATURE_BIT_SMX), %ecx
+ jz .Ldo_unknown_cpu
+
+ popl %ecx
+ popl %ebx
+
+ /* Know it is Intel */
+ movl $(SL_CPU_INTEL), rva(sl_cpu_type)(%ebx)
+
+ /* Locate the base of the MLE using the page tables in %ecx */
+ call sl_find_mle_base
+
+ /* Increment CPU count for BSP */
+ incl rva(sl_txt_cpu_count)(%ebx)
+
+ /*
+ * On the BSP, enable SMI with GETSEC[SMCTRL] which were disabled by SENTER.
+ * NMIs were also disabled by SENTER. Since there is no IDT for the BSP,
+ * allow the mainline kernel to re-enable them in the normal course of
+ * booting.
+ */
+ GETSEC $(SMX_X86_GETSEC_SMCTRL)
+
+ /* Clear the TXT error registers for a clean start of day */
+ movl $0, (TXT_PRIV_CONFIG_REGS_BASE + TXT_CR_ERRORCODE)
+ movl $0xffffffff, (TXT_PRIV_CONFIG_REGS_BASE + TXT_CR_ESTS)
+
+ /* Read physical base of the TXT heap into %eax */
+ movl (TXT_PRIV_CONFIG_REGS_BASE + TXT_CR_HEAP_BASE), %eax
+ /* Read the size of the BIOS data into ECX (first 8 bytes) */
+ movl (%eax), %ecx
+ /* Skip over BIOS data and size of OS to MLE data section */
+ leal 8(%eax, %ecx), %eax
+
+ /* Need to verify the values in the OS-MLE struct passed in */
+ call sl_txt_verify_os_mle_struct
+
+ /*
+ * Get the boot params address from the TXT info table in the SLRT.
+ * Note %esi and %ebx MUST be preserved across calls and operations.
+ */
+ movl SL_txt_info(%eax), %edi
+ movl SL_boot_params_addr(%edi), %esi
+
+ /* Save %ebx so the APs can find their way home */
+ movl %ebx, (SL_mle_scratch + SL_SCRATCH_AP_EBX)(%eax)
+
+ /* Fetch the AP wake code block address from the heap */
+ movl SL_ap_wake_block(%eax), %edi
+ movl %edi, rva(sl_txt_ap_wake_block)(%ebx)
+
+ /* Store the offset in the AP wake block to the jmp address */
+ movl $(sl_ap_jmp_offset - sl_txt_ap_wake_begin), \
+ (SL_mle_scratch + SL_SCRATCH_AP_JMP_OFFSET)(%eax)
+
+ /* Store the offset in the AP wake block to the AP stacks block */
+ movl $(sl_stacks - sl_txt_ap_wake_begin), \
+ (SL_mle_scratch + SL_SCRATCH_AP_STACKS_OFFSET)(%eax)
+
+ /* %eax still is the base of the OS-MLE block, save it */
+ pushl %eax
+
+ /* Relocate the AP wake code to the safe block */
+ call sl_txt_reloc_ap_wake
+
+ /*
+ * Wake up all APs that are blocked in the ACM and wait for them to
+ * halt. This should be done before restoring the MTRRs so the ACM is
+ * still properly in WB memory.
+ */
+ call sl_txt_wake_aps
+
+ /* Restore OS-MLE in %eax */
+ popl %eax
+
+ /*
+ * %edi is used by this routine to find the MTRRs which are in the SLRT
+ * in the Intel info.
+ */
+ movl SL_txt_info(%eax), %edi
+ call sl_txt_load_regs
+
+ jmp .Lcpu_setup_done
+
+.Ldo_unknown_cpu:
+ /* Non-Intel CPUs are not yet supported */
+ ud2
+
+.Lcpu_setup_done:
+ /*
+ * Don't enable MCE at this point. The kernel will enable
+ * it on the BSP later when it is ready.
+ */
+
+ /* Done, jump to normal 32b pm entry */
+ jmp startup_32
+SYM_FUNC_END(sl_stub_entry)
+
+SYM_FUNC_START(sl_find_mle_base)
+ /* %ecx has PDPT, get first PD */
+ movl (%ecx), %eax
+ andl $(PAGE_MASK), %eax
+ /* Get first PT from first PDE */
+ movl (%eax), %eax
+ andl $(PAGE_MASK), %eax
+ /* Get MLE base from first PTE */
+ movl (%eax), %eax
+ andl $(PAGE_MASK), %eax
+
+ movl %eax, rva(sl_mle_start)(%ebx)
+ ret
+SYM_FUNC_END(sl_find_mle_base)
+
+SYM_FUNC_START(sl_check_buffer_mle_overlap)
+ /* %ecx: buffer begin %edx: buffer end */
+ /* %ebx: MLE begin %edi: MLE end */
+ /* %eax: region may be inside MLE */
+
+ cmpl %edi, %ecx
+ jb .Lnext_check
+ cmpl %edi, %edx
+ jbe .Lnext_check
+ jmp .Lvalid /* Buffer above MLE */
+
+.Lnext_check:
+ cmpl %ebx, %edx
+ ja .Linside_check
+ cmpl %ebx, %ecx
+ jae .Linside_check
+ jmp .Lvalid /* Buffer below MLE */
+
+.Linside_check:
+ cmpl $0, %eax
+ jz .Linvalid
+ cmpl %ebx, %ecx
+ jb .Linvalid
+ cmpl %edi, %edx
+ ja .Linvalid
+ jmp .Lvalid /* Buffer in MLE */
+
+.Linvalid:
+ TXT_RESET $(SL_ERROR_MLE_BUFFER_OVERLAP)
+
+.Lvalid:
+ ret
+SYM_FUNC_END(sl_check_buffer_mle_overlap)
+
+SYM_FUNC_START(sl_txt_verify_os_mle_struct)
+ pushl %ebx
+ /*
+ * %eax points to the base of the OS-MLE struct. Need to also
+ * read some values from the OS-SINIT struct too.
+ */
+ movl -8(%eax), %ecx
+ /* Skip over OS to MLE data section and size of OS-SINIT structure */
+ leal (%eax, %ecx), %edx
+
+ /* Load MLE image base absolute offset */
+ movl rva(sl_mle_start)(%ebx), %ebx
+
+ /* Verify the value of the low PMR base. It should always be 0. */
+ movl SL_vtd_pmr_lo_base(%edx), %esi
+ cmpl $0, %esi
+ jz .Lvalid_pmr_base
+ TXT_RESET $(SL_ERROR_LO_PMR_BASE)
+
+.Lvalid_pmr_base:
+ /* Grab some values from OS-SINIT structure */
+ movl SL_mle_size(%edx), %edi
+ addl %ebx, %edi
+ jc .Loverflow_detected
+ movl SL_vtd_pmr_lo_size(%edx), %esi
+
+ /* Check the AP wake block */
+ movl SL_ap_wake_block(%eax), %ecx
+ movl SL_ap_wake_block_size(%eax), %edx
+ addl %ecx, %edx
+ jc .Loverflow_detected
+ pushl %eax
+ xorl %eax, %eax
+ call sl_check_buffer_mle_overlap
+ popl %eax
+ cmpl %esi, %edx
+ ja .Lbuffer_beyond_pmr
+
+ /*
+ * Check the boot params. Note during a UEFI boot, the boot
+ * params will be inside the MLE image. Test for this case
+ * in the overlap case.
+ */
+ movl SL_boot_params_addr(%eax), %ecx
+ movl $(PAGE_SIZE), %edx
+ addl %ecx, %edx
+ jc .Loverflow_detected
+ pushl %eax
+ movl $1, %eax
+ call sl_check_buffer_mle_overlap
+ popl %eax
+ cmpl %esi, %edx
+ ja .Lbuffer_beyond_pmr
+
+ /* Check that the AP wake block is big enough */
+ cmpl $(sl_txt_ap_wake_end - sl_txt_ap_wake_begin), \
+ SL_ap_wake_block_size(%eax)
+ jae .Lwake_block_ok
+ TXT_RESET $(SL_ERROR_WAKE_BLOCK_TOO_SMALL)
+
+.Lwake_block_ok:
+ popl %ebx
+ ret
+
+.Loverflow_detected:
+ TXT_RESET $(SL_ERROR_INTEGER_OVERFLOW)
+
+.Lbuffer_beyond_pmr:
+ TXT_RESET $(SL_ERROR_BUFFER_BEYOND_PMR)
+SYM_FUNC_END(sl_txt_verify_os_mle_struct)
+
+SYM_FUNC_START(sl_txt_ap_entry)
+ cli
+ cld
+ /*
+ * AP entry point, first order of business is to find where we are and
+ * save it in %ebx.
+ */
+
+ /* Read physical base of heap into EAX */
+ movl (TXT_PRIV_CONFIG_REGS_BASE + TXT_CR_HEAP_BASE), %eax
+ /* Read the size of the BIOS data into ECX (first 8 bytes) */
+ movl (%eax), %ecx
+ /* Skip over BIOS data and size of OS to MLE data section */
+ leal 8(%eax, %ecx), %eax
+
+ /* Saved %ebx from the BSP and stash OS-MLE pointer */
+ movl (SL_mle_scratch + SL_SCRATCH_AP_EBX)(%eax), %ebx
+
+ /* Save TXT info ptr in %edi for call to sl_txt_load_regs */
+ movl SL_txt_info(%eax), %edi
+
+ /*
+ * Only the %cs and %ds segments are known good after waking the AP,
+ * as with entry on the BSP. First locate a stack to use then establish
+ * a new GDT and segments.
+ */
+
+ /* Lock and get our stack index */
+ movl $1, %ecx
+.Lspin:
+ xorl %eax, %eax
+ lock cmpxchgl %ecx, rva(sl_txt_spin_lock)(%ebx)
+ pause
+ jnz .Lspin
+
+ /* Increment the stack index and use the next value inside lock */
+ incl rva(sl_txt_stack_index)(%ebx)
+ movl rva(sl_txt_stack_index)(%ebx), %eax
+
+ /* Unlock */
+ movl $0, rva(sl_txt_spin_lock)(%ebx)
+
+ /* Location of the relocated AP wake block */
+ movl rva(sl_txt_ap_wake_block)(%ebx), %ecx
+
+ /* Load reloc GDT, set segment regs and lret to __SL32_CS */
+ lgdt (sl_ap_gdt_desc - sl_txt_ap_wake_begin)(%ecx)
+
+ movl $(__SL32_DS), %edx
+ movw %dx, %ds
+ movw %dx, %es
+ movw %dx, %fs
+ movw %dx, %gs
+ movw %dx, %ss
+
+ /* Load our reloc AP stack */
+ movl $(SL_BOOT_STACK_SIZE), %edx
+ mull %edx
+ leal (sl_stacks_end - sl_txt_ap_wake_begin)(%ecx), %esp
+ subl %eax, %esp
+
+ /* Switch to AP code segment */
+ leal rva(.Lsl_ap_cs)(%ebx), %eax
+ pushl $(__SL32_CS)
+ pushl %eax
+ lret
+
+.Lsl_ap_cs:
+ /* Load the relocated AP IDT */
+ lidt (sl_ap_idt_desc - sl_txt_ap_wake_begin)(%ecx)
+
+ /* Fixup MTRRs and misc enable MSR on APs too */
+ call sl_txt_load_regs
+
+ /* Enable SMI with GETSEC[SMCTRL] */
+ GETSEC $(SMX_X86_GETSEC_SMCTRL)
+
+ /* IRET-to-self can be used to enable NMIs which SENTER disabled */
+ leal rva(.Lnmi_enabled_ap)(%ebx), %eax
+ pushfl
+ pushl $(__SL32_CS)
+ pushl %eax
+ iret
+
+.Lnmi_enabled_ap:
+ /* Put APs in X2APIC mode like the BSP */
+ movl $(MSR_IA32_APICBASE), %ecx
+ rdmsr
+ orl $(XAPIC_ENABLE | X2APIC_ENABLE), %eax
+ wrmsr
+
+ /*
+ * Basically done, increment the CPU count and jump off to the AP
+ * wake block to wait.
+ */
+ lock incl rva(sl_txt_cpu_count)(%ebx)
+
+ /*
+ * Final jump to the AP wake block (see comment below). Here the APs
+ * will idle until the Secure Launch SMP MONITOR/MWAIT framework
+ * releases them to mainline kernel control.
+ */
+ movl rva(sl_txt_ap_wake_block)(%ebx), %eax
+ jmp *%eax
+SYM_FUNC_END(sl_txt_ap_entry)
+
+SYM_FUNC_START(sl_txt_reloc_ap_wake)
+ /*
+ * What is called the "AP wake block" is simply a chunk of protected
+ * memory that the bootloader handed the MLE. The MLE implementation will
+ * shuffle the AP entry point code from here in the setup kernel into this wake
+ * block where it cannot be overwritten by kernel decompression, relocation, etc.
+ */
+
+ /* Save boot params register */
+ pushl %esi
+
+ movl rva(sl_txt_ap_wake_block)(%ebx), %edi
+
+ /* Fixup AP IDT and GDT descriptor before relocating */
+ leal rva(sl_ap_idt_desc)(%ebx), %eax
+ addl %edi, 2(%eax)
+ leal rva(sl_ap_gdt_desc)(%ebx), %eax
+ addl %edi, 2(%eax)
+
+ /*
+ * Copy the AP wake code and AP GDT/IDT to the protected wake block
+ * provided by the loader. Destination already in %edi.
+ */
+ movl $(sl_txt_ap_wake_end - sl_txt_ap_wake_begin), %ecx
+ leal rva(sl_txt_ap_wake_begin)(%ebx), %esi
+ rep movsb
+
+ /* Setup the IDT for the APs to use in the relocation block */
+ movl rva(sl_txt_ap_wake_block)(%ebx), %ecx
+ addl $(sl_ap_idt - sl_txt_ap_wake_begin), %ecx
+ xorl %edx, %edx
+
+ /* Form the default reset vector relocation address */
+ movl rva(sl_txt_ap_wake_block)(%ebx), %esi
+ addl $(sl_txt_int_reset - sl_txt_ap_wake_begin), %esi
+
+1:
+ cmpw $(NR_VECTORS), %dx
+ jz .Lap_idt_done
+
+ cmpw $(X86_TRAP_NMI), %dx
+ jz 2f
+
+ /* Load all other fixed vectors with reset handler */
+ movl %esi, %eax
+ movw %ax, (IDT_VECTOR_LO_BITS)(%ecx)
+ shrl $16, %eax
+ movw %ax, (IDT_VECTOR_HI_BITS)(%ecx)
+ jmp 3f
+
+2:
+ /* Load single wake NMI IPI vector at the relocation address */
+ movl rva(sl_txt_ap_wake_block)(%ebx), %eax
+ addl $(sl_txt_int_nmi - sl_txt_ap_wake_begin), %eax
+ movw %ax, (IDT_VECTOR_LO_BITS)(%ecx)
+ shrl $16, %eax
+ movw %ax, (IDT_VECTOR_HI_BITS)(%ecx)
+
+3:
+ incw %dx
+ addl $8, %ecx
+ jmp 1b
+
+.Lap_idt_done:
+ popl %esi
+ ret
+SYM_FUNC_END(sl_txt_reloc_ap_wake)
+
+SYM_FUNC_START(sl_txt_load_regs)
+ /* Save base pointer register */
+ pushl %ebx
+
+ /*
+ * On Intel, the original variable MTRRs and Misc Enable MSR are
+ * restored on the BSP at early boot. Each AP will also restore
+ * its MTRRs and Misc Enable MSR.
+ */
+ pushl %edi
+ addl $(SL_saved_bsp_mtrrs), %edi
+ movl (%edi), %ebx
+ pushl %ebx /* default_mem_type lo */
+ addl $4, %edi
+ movl (%edi), %ebx
+ pushl %ebx /* default_mem_type hi */
+ addl $4, %edi
+ movl (%edi), %ebx /* mtrr_vcnt lo, don't care about hi part */
+ addl $8, %edi /* now at MTRR pair array */
+ /* Write the variable MTRRs */
+ movl $(MSR_MTRRphysBase0), %ecx
+1:
+ cmpl $0, %ebx
+ jz 2f
+
+ movl (%edi), %eax /* MTRRphysBaseX lo */
+ addl $4, %edi
+ movl (%edi), %edx /* MTRRphysBaseX hi */
+ wrmsr
+ addl $4, %edi
+ incl %ecx
+ movl (%edi), %eax /* MTRRphysMaskX lo */
+ addl $4, %edi
+ movl (%edi), %edx /* MTRRphysMaskX hi */
+ wrmsr
+ addl $4, %edi
+ incl %ecx
+
+ decl %ebx
+ jmp 1b
+2:
+ /* Write the default MTRR register */
+ popl %edx
+ popl %eax
+ movl $(MSR_MTRRdefType), %ecx
+ wrmsr
+
+ /* Return to beginning and write the misc enable msr */
+ popl %edi
+ addl $(SL_saved_misc_enable_msr), %edi
+ movl (%edi), %eax /* saved_misc_enable_msr lo */
+ addl $4, %edi
+ movl (%edi), %edx /* saved_misc_enable_msr hi */
+ movl $(MSR_IA32_MISC_ENABLE), %ecx
+ wrmsr
+
+ popl %ebx
+ ret
+SYM_FUNC_END(sl_txt_load_regs)
+
+SYM_FUNC_START(sl_txt_wake_aps)
+ /* Save boot params register */
+ pushl %esi
+
+ /*
+ * First setup the MLE join structure and load it into the TXT register.
+ * This structure defines the information needed to wake the APs and
+ * safely be joined with the DRTM.
+ */
+ leal rva(sl_gdt)(%ebx), %eax
+ leal rva(sl_txt_ap_entry)(%ebx), %ecx
+ leal rva(sl_smx_rlp_mle_join)(%ebx), %edx
+ movl %eax, SL_rlp_gdt_base(%edx)
+ movl %ecx, SL_rlp_entry_point(%edx)
+ movl %edx, (TXT_PRIV_CONFIG_REGS_BASE + TXT_CR_MLE_JOIN)
+
+ /* Another TXT heap walk to find various values needed to wake APs */
+ movl (TXT_PRIV_CONFIG_REGS_BASE + TXT_CR_HEAP_BASE), %eax
+ /* At BIOS data size, find the number of logical processors */
+ movl (SL_num_logical_procs + 8)(%eax), %edx
+ /* Skip over BIOS data */
+ movl (%eax), %ecx
+ addl %ecx, %eax
+ /* Skip over OS to MLE */
+ movl (%eax), %ecx
+ addl %ecx, %eax
+ /* At OS-SNIT size, get capabilities to know how to wake up the APs */
+ movl (SL_capabilities + 8)(%eax), %esi
+ /* Skip over OS to SNIT */
+ movl (%eax), %ecx
+ addl %ecx, %eax
+ /* At SINIT-MLE size, get the AP wake MONITOR address */
+ movl (SL_rlp_wakeup_addr + 8)(%eax), %edi
+
+ /* Determine how to wake up the APs */
+ testl $(1 << TXT_SINIT_MLE_CAP_RLP_WAKE_MONITOR), %esi
+ jz .Lwake_getsec
+
+ /* Wake using MWAIT MONITOR */
+ movl $1, (%edi)
+ jmp .Laps_awake
+
+.Lwake_getsec:
+ /* Wake using GETSEC(WAKEUP) */
+ GETSEC $(SMX_X86_GETSEC_WAKEUP)
+
+.Laps_awake:
+ /*
+ * All of the APs are woken up and rendezvous in the relocated wake
+ * block starting at sl_txt_ap_wake_begin. Wait for all of them to
+ * halt.
+ */
+ pause
+ cmpl rva(sl_txt_cpu_count)(%ebx), %edx
+ jne .Laps_awake
+
+ popl %esi
+ ret
+SYM_FUNC_END(sl_txt_wake_aps)
+
+/* This is the beginning of the relocated AP wake code block */
+ .global sl_txt_ap_wake_begin
+sl_txt_ap_wake_begin:
+ /*
+ * Note on the stack layout for the APs. The individual 128 byte stacks
+ * fully occupy 2 cache lines. The first is for the MONITOR address
+ * and the second contains the APICID written to it. Note the whole
+ * cache line is unused other than the monitor field; nothing else should
+ * write the cache line and wake the monitor.
+ *
+ * esp -> +-----------+
+ * | APIC ID |
+ * |-----------|
+ * | PAD[15] |
+ * |-----------|
+ * | PAD[15] |
+ * |-----------|
+ * | MONITOR |
+ * +-----------+
+ */
+
+ /* Get the LAPIC ID for each AP and stash it on the stack */
+ movl $(MSR_IA32_X2APIC_APICID), %ecx
+ rdmsr
+ pushl %eax
+
+ /*
+ * Get a pointer to the monitor location on this APs stack to test below
+ * after mwait returns. Currently %esp points to just past the pushed APIC
+ * ID value.
+ */
+ movl %esp, %edi
+ subl $(SL_BOOT_STACK_SIZE - 4), %edi
+ movl $0, (%edi)
+
+1:
+ /* Load eax and clear ecx/edx so no invalid extensions or hints are passed to monitor */
+ movl %edi, %eax
+ xorl %ecx, %ecx
+ xorl %edx, %edx
+
+ /*
+ * Arm the monitor and wait for it to be triggered by the SMP bringup code. The mwait
+ * instruction can return for a number of reasons. Test to see if it returned
+ * because the monitor was written to.
+ */
+ monitor
+
+ cmpl $0, (%eax)
+ jnz 2f
+
+ /* Clear eax since there are no hints sent to mwait */
+ xorl %eax, %eax
+
+ mwait
+ jmp 1b
+
+2:
+ /*
+ * This is the long absolute jump to the 32b Secure Launch protected mode stub
+ * code in sl_trampoline_start32() in the rmpiggy. The jump address will be
+ * fixed in the SMP boot code when the first AP is brought up. This whole area
+ * is provided and protected in the memory map by the prelaunch code.
+ */
+ .byte 0xea
+sl_ap_jmp_offset:
+ .long 0x00000000
+ .word __SL32_CS
+
+SYM_FUNC_START(sl_txt_int_nmi)
+ /* NMI context, just IRET */
+ iret
+SYM_FUNC_END(sl_txt_int_nmi)
+
+SYM_FUNC_START(sl_txt_int_reset)
+ TXT_RESET $(SL_ERROR_INV_AP_INTERRUPT)
+SYM_FUNC_END(sl_txt_int_reset)
+
+ .balign 8
+SYM_DATA_START_LOCAL(sl_ap_idt_desc)
+ .word sl_ap_idt_end - sl_ap_idt - 1 /* Limit */
+ .long sl_ap_idt - sl_txt_ap_wake_begin /* Base */
+SYM_DATA_END_LABEL(sl_ap_idt_desc, SYM_L_LOCAL, sl_ap_idt_desc_end)
+
+ .balign 8
+SYM_DATA_START_LOCAL(sl_ap_idt)
+ .rept NR_VECTORS
+ .word 0x0000 /* Offset 15 to 0 */
+ .word __SL32_CS /* Segment selector */
+ .word 0x8e00 /* Present, DPL=0, 32b Vector, Interrupt */
+ .word 0x0000 /* Offset 31 to 16 */
+ .endr
+SYM_DATA_END_LABEL(sl_ap_idt, SYM_L_LOCAL, sl_ap_idt_end)
+
+ .balign 8
+SYM_DATA_START_LOCAL(sl_ap_gdt_desc)
+ .word sl_ap_gdt_end - sl_ap_gdt - 1
+ .long sl_ap_gdt - sl_txt_ap_wake_begin
+SYM_DATA_END_LABEL(sl_ap_gdt_desc, SYM_L_LOCAL, sl_ap_gdt_desc_end)
+
+ .balign 8
+SYM_DATA_START_LOCAL(sl_ap_gdt)
+ .quad 0x0000000000000000 /* NULL */
+ .quad 0x00cf9a000000ffff /* __SL32_CS */
+ .quad 0x00cf92000000ffff /* __SL32_DS */
+SYM_DATA_END_LABEL(sl_ap_gdt, SYM_L_LOCAL, sl_ap_gdt_end)
+
+ /* Small stacks for BSP and APs to work with */
+ .balign 64
+SYM_DATA_START_LOCAL(sl_stacks)
+ .fill (SL_MAX_CPUS * SL_BOOT_STACK_SIZE), 1, 0
+SYM_DATA_END_LABEL(sl_stacks, SYM_L_LOCAL, sl_stacks_end)
+
+/* This is the end of the relocated AP wake code block */
+ .global sl_txt_ap_wake_end
+sl_txt_ap_wake_end:
+
+ .data
+ .balign 8
+SYM_DATA_START_LOCAL(sl_gdt_desc)
+ .word sl_gdt_end - sl_gdt - 1
+ .long sl_gdt - sl_gdt_desc
+SYM_DATA_END_LABEL(sl_gdt_desc, SYM_L_LOCAL, sl_gdt_desc_end)
+
+ .balign 8
+SYM_DATA_START_LOCAL(sl_gdt)
+ .quad 0x0000000000000000 /* NULL */
+ .quad 0x00cf9a000000ffff /* __SL32_CS */
+ .quad 0x00cf92000000ffff /* __SL32_DS */
+SYM_DATA_END_LABEL(sl_gdt, SYM_L_LOCAL, sl_gdt_end)
+
+ .balign 8
+SYM_DATA_START_LOCAL(sl_smx_rlp_mle_join)
+ .long sl_gdt_end - sl_gdt - 1 /* GDT limit */
+ .long 0x00000000 /* GDT base */
+ .long __SL32_CS /* Seg Sel - CS (DS, ES, SS = seg_sel+8) */
+ .long 0x00000000 /* Entry point physical address */
+SYM_DATA_END(sl_smx_rlp_mle_join)
+
+SYM_DATA(sl_cpu_type, .long 0x00000000)
+
+SYM_DATA(sl_mle_start, .long 0x00000000)
+
+SYM_DATA_LOCAL(sl_txt_spin_lock, .long 0x00000000)
+
+SYM_DATA_LOCAL(sl_txt_stack_index, .long 0x00000000)
+
+SYM_DATA_LOCAL(sl_txt_cpu_count, .long 0x00000000)
+
+SYM_DATA_LOCAL(sl_txt_ap_wake_block, .long 0x00000000)
diff --git a/arch/x86/include/uapi/asm/bootparam.h b/arch/x86/include/uapi/asm/bootparam.h
index dafbf581c515..8155fa899f50 100644
--- a/arch/x86/include/uapi/asm/bootparam.h
+++ b/arch/x86/include/uapi/asm/bootparam.h
@@ -12,6 +12,7 @@
/* loadflags */
#define LOADED_HIGH (1<<0)
#define KASLR_FLAG (1<<1)
+#define SLAUNCH_FLAG (1<<2)
#define QUIET_FLAG (1<<5)
#define KEEP_SEGMENTS (1<<6)
#define CAN_USE_HEAP (1<<7)
diff --git a/arch/x86/kernel/asm-offsets.c b/arch/x86/kernel/asm-offsets.c
index 32ba599a51f8..d800fcc6514a 100644
--- a/arch/x86/kernel/asm-offsets.c
+++ b/arch/x86/kernel/asm-offsets.c
@@ -13,6 +13,8 @@
#include <linux/hardirq.h>
#include <linux/suspend.h>
#include <linux/kbuild.h>
+#include <linux/slr_table.h>
+#include <linux/slaunch.h>
#include <asm/processor.h>
#include <asm/thread_info.h>
#include <asm/sigframe.h>
@@ -124,4 +126,22 @@ static void __used common(void)
OFFSET(ARIA_CTX_rounds, aria_ctx, rounds);
#endif
+#ifdef CONFIG_SECURE_LAUNCH
+ BLANK();
+ OFFSET(SL_txt_info, txt_os_mle_data, txt_info);
+ OFFSET(SL_mle_scratch, txt_os_mle_data, mle_scratch);
+ OFFSET(SL_ap_wake_block, txt_os_mle_data, ap_wake_block);
+ OFFSET(SL_ap_wake_block_size, txt_os_mle_data, ap_wake_block_size);
+ OFFSET(SL_boot_params_addr, slr_entry_intel_info, boot_params_addr);
+ OFFSET(SL_saved_misc_enable_msr, slr_entry_intel_info, saved_misc_enable_msr);
+ OFFSET(SL_saved_bsp_mtrrs, slr_entry_intel_info, saved_bsp_mtrrs);
+ OFFSET(SL_num_logical_procs, txt_bios_data, num_logical_procs);
+ OFFSET(SL_capabilities, txt_os_sinit_data, capabilities);
+ OFFSET(SL_mle_size, txt_os_sinit_data, mle_size);
+ OFFSET(SL_vtd_pmr_lo_base, txt_os_sinit_data, vtd_pmr_lo_base);
+ OFFSET(SL_vtd_pmr_lo_size, txt_os_sinit_data, vtd_pmr_lo_size);
+ OFFSET(SL_rlp_wakeup_addr, txt_sinit_mle_data, rlp_wakeup_addr);
+ OFFSET(SL_rlp_gdt_base, smx_rlp_mle_join, rlp_gdt_base);
+ OFFSET(SL_rlp_entry_point, smx_rlp_mle_join, rlp_entry_point);
+#endif
}
--
2.43.7
^ permalink raw reply related
* [PATCH v15 17/28] x86: Add early SHA-1 support for Secure Launch early measurements
From: Ross Philipson @ 2025-12-15 23:33 UTC (permalink / raw)
To: linux-kernel, x86, linux-integrity, linux-doc, linux-crypto,
kexec, linux-efi, iommu
Cc: ross.philipson, dpsmith, tglx, mingo, bp, hpa, dave.hansen, ardb,
mjg59, James.Bottomley, peterhuewe, jarkko, jgg, luto, nivedita,
herbert, davem, corbet, ebiederm, dwmw2, baolu.lu, kanth.ghatraju,
andrew.cooper3, trenchboot-devel
In-Reply-To: <20251215233316.1076248-1-ross.philipson@oracle.com>
From: "Daniel P. Smith" <dpsmith@apertussolutions.com>
Secure Launch is written to be compliant with the Intel TXT Measured Launch
Developer's Guide. The MLE Guide dictates that the system can be configured to
use both the SHA-1 and SHA-2 hashing algorithms.
Regardless of the preference towards SHA-2, if the firmware elected to start
with the SHA-1 and SHA-2 banks active and the dynamic launch was configured to
include SHA-1, Secure Launch is obligated to record measurements for all
algorithms requested in the launch configuration.
The user environment or the integrity management does not desire to use SHA-1,
it is free to just ignore the SHA-1 bank in any integrity operation with the
TPM. If there is a larger concern about the SHA-1 bank being active, it is free
to deliberately cap the SHA-1 PCRs, recording the event in the D-RTM log.
Signed-off-by: Daniel P. Smith <dpsmith@apertussolutions.com>
Signed-off-by: Ross Philipson <ross.philipson@oracle.com>
---
arch/x86/boot/compressed/Makefile | 4 ++++
arch/x86/boot/compressed/sha1.c | 7 +++++++
2 files changed, 11 insertions(+)
create mode 100644 arch/x86/boot/compressed/sha1.c
diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
index 74657589264d..69592146ced7 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -112,6 +112,10 @@ ifdef CONFIG_EFI_SBAT
$(obj)/sbat.o: $(CONFIG_EFI_SBAT_FILE)
endif
+slaunch-objs += $(obj)/sha1.o
+
+vmlinux-objs-$(CONFIG_SECURE_LAUNCH) += $(slaunch-objs)
+
$(obj)/vmlinux: $(vmlinux-objs-y) $(vmlinux-libs-y) FORCE
$(call if_changed,ld)
diff --git a/arch/x86/boot/compressed/sha1.c b/arch/x86/boot/compressed/sha1.c
new file mode 100644
index 000000000000..dd1b4cf5caf5
--- /dev/null
+++ b/arch/x86/boot/compressed/sha1.c
@@ -0,0 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025 Apertus Solutions, LLC.
+ */
+
+#undef CONFIG_CRYPTO_LIB_SHA1_ARCH
+#include "../../../../lib/crypto/sha1.c"
--
2.43.7
^ permalink raw reply related
* [PATCH v15 24/28] x86: Secure Launch SMP bringup support
From: Ross Philipson @ 2025-12-15 23:33 UTC (permalink / raw)
To: linux-kernel, x86, linux-integrity, linux-doc, linux-crypto,
kexec, linux-efi, iommu
Cc: ross.philipson, dpsmith, tglx, mingo, bp, hpa, dave.hansen, ardb,
mjg59, James.Bottomley, peterhuewe, jarkko, jgg, luto, nivedita,
herbert, davem, corbet, ebiederm, dwmw2, baolu.lu, kanth.ghatraju,
andrew.cooper3, trenchboot-devel
In-Reply-To: <20251215233316.1076248-1-ross.philipson@oracle.com>
On Intel, the APs are left in a well documented state after TXT performs
the late launch. Specifically they cannot have #INIT asserted on them so
a standard startup via INIT/SIPI/SIPI cannot be performed. Instead the
early SL stub code uses MONITOR and MWAIT to park the APs. The realmode/init.c
code updates the jump address for the waiting APs with the location of the
Secure Launch entry point in the rmpiggy image.
The rmpiggy image is a payload contained in the kernel used to start the
APs (in 16b or 32b modes). It is loaded at runtime so its location and entry point
must be updated in the long jump for the waiting APs by the running
kernel.
As the APs are woken up by writing the monitor, the APs jump to the Secure
Launch entry point in the rmpiggy which mimics what the real mode code would
do then jumps to the standard rmpiggy protected mode entry point.
Signed-off-by: Ross Philipson <ross.philipson@oracle.com>
---
arch/x86/include/asm/realmode.h | 3 ++
arch/x86/kernel/slaunch.c | 26 +++++++++++++++
arch/x86/kernel/smpboot.c | 47 ++++++++++++++++++++++++++--
arch/x86/realmode/init.c | 8 +++++
| 3 ++
arch/x86/realmode/rm/trampoline_64.S | 32 +++++++++++++++++++
6 files changed, 117 insertions(+), 2 deletions(-)
diff --git a/arch/x86/include/asm/realmode.h b/arch/x86/include/asm/realmode.h
index e406a1e92c63..e3336c49d26b 100644
--- a/arch/x86/include/asm/realmode.h
+++ b/arch/x86/include/asm/realmode.h
@@ -38,6 +38,9 @@ struct real_mode_header {
#ifdef CONFIG_X86_64
u32 machine_real_restart_seg;
#endif
+#ifdef CONFIG_SECURE_LAUNCH
+ u32 sl_trampoline_start32;
+#endif
};
/* This must match data at realmode/rm/trampoline_{32,64}.S */
diff --git a/arch/x86/kernel/slaunch.c b/arch/x86/kernel/slaunch.c
index 6958734fe5e9..0699cbf41753 100644
--- a/arch/x86/kernel/slaunch.c
+++ b/arch/x86/kernel/slaunch.c
@@ -507,3 +507,29 @@ void __init slaunch_setup(void)
if (boot_cpu_has(X86_FEATURE_SMX))
slaunch_setup_txt();
}
+
+/*
+ * After a launch, the APs are woken up, enter the DRTM and are left to
+ * wait for a wakeup call on a MONITOR address. The block where they are
+ * idle has a long jump to the AP startup code in the mainline kernel.
+ * This address has to be calculated at runtime and "fixed up" to point
+ * to the SL startup location in the rmpiggy SMP startup image. This image
+ * is loaded into separate memory at kernel start time.
+ */
+void __init slaunch_fixup_ap_wake_vector(void)
+{
+ struct sl_ap_wake_info *ap_wake_info;
+ u32 *ap_jmp_ptr;
+
+ if (!slaunch_is_txt_launch())
+ return;
+
+ ap_wake_info = slaunch_get_ap_wake_info();
+
+ ap_jmp_ptr = (u32 *)__va(ap_wake_info->ap_wake_block +
+ ap_wake_info->ap_jmp_offset);
+
+ *ap_jmp_ptr = real_mode_header->sl_trampoline_start32;
+
+ pr_info("TXT AP startup vector address updated\n");
+}
diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c
index eb289abece23..c351280b1cbc 100644
--- a/arch/x86/kernel/smpboot.c
+++ b/arch/x86/kernel/smpboot.c
@@ -61,6 +61,7 @@
#include <linux/cpuhotplug.h>
#include <linux/mc146818rtc.h>
#include <linux/acpi.h>
+#include <linux/slaunch.h>
#include <asm/acpi.h>
#include <asm/cacheinfo.h>
@@ -833,6 +834,45 @@ int common_cpu_up(unsigned int cpu, struct task_struct *idle)
return 0;
}
+#if (IS_ENABLED(CONFIG_SECURE_LAUNCH))
+
+/*
+ * TXT AP startup is quite different than normal. The APs cannot have #INIT
+ * asserted on them or receive SIPIs. The early Secure Launch code has parked
+ * the APs using MONITOR/MWAIT in the safe AP wake block area (details in
+ * sl_stub.S). The SMP boot will wake the APs by writing the MONITOR associated
+ * with the AP and have them jump to the protected mode code in the rmpiggy where
+ * the rest of the SMP boot of the AP will proceed normally.
+ *
+ * Intel Trusted Execution Technology (TXT) Software Development Guide
+ * Section 2.3 - MLE Initialization
+ */
+static void slaunch_wakeup_cpu_from_txt(int cpu, int apicid)
+{
+ struct sl_ap_stack_and_monitor *stack_monitor;
+ struct sl_ap_wake_info *ap_wake_info;
+
+ ap_wake_info = slaunch_get_ap_wake_info();
+
+ stack_monitor = (struct sl_ap_stack_and_monitor *)__va(ap_wake_info->ap_wake_block +
+ ap_wake_info->ap_stacks_offset);
+
+ for (unsigned int i = SL_MAX_CPUS - 1; i >= 0; i--) {
+ if (stack_monitor[i].apicid == apicid) {
+ stack_monitor[i].monitor = 1;
+ break;
+ }
+ }
+}
+
+#else
+
+static inline void slaunch_wakeup_cpu_from_txt(int cpu, int apicid)
+{
+}
+
+#endif /* IS_ENABLED(CONFIG_SECURE_LAUNCH) */
+
/*
* NOTE - on most systems this is a PHYSICAL apic ID, but on multiquad
* (ie clustered apic addressing mode), this is a LOGICAL apic ID.
@@ -842,7 +882,7 @@ int common_cpu_up(unsigned int cpu, struct task_struct *idle)
static int do_boot_cpu(u32 apicid, unsigned int cpu, struct task_struct *idle)
{
unsigned long start_ip = real_mode_header->trampoline_start;
- int ret;
+ int ret = 0;
#ifdef CONFIG_X86_64
/* If 64-bit wakeup method exists, use the 64-bit mode trampoline IP */
@@ -887,12 +927,15 @@ static int do_boot_cpu(u32 apicid, unsigned int cpu, struct task_struct *idle)
/*
* Wake up a CPU in difference cases:
+ * - Intel TXT DRTM launch uses its own method to wake the APs
* - Use a method from the APIC driver if one defined, with wakeup
* straight to 64-bit mode preferred over wakeup to RM.
* Otherwise,
* - Use an INIT boot APIC message
*/
- if (apic->wakeup_secondary_cpu_64)
+ if (slaunch_is_txt_launch())
+ slaunch_wakeup_cpu_from_txt(cpu, apicid);
+ else if (apic->wakeup_secondary_cpu_64)
ret = apic->wakeup_secondary_cpu_64(apicid, start_ip, cpu);
else if (apic->wakeup_secondary_cpu)
ret = apic->wakeup_secondary_cpu(apicid, start_ip, cpu);
diff --git a/arch/x86/realmode/init.c b/arch/x86/realmode/init.c
index 88be32026768..ded4dafc6a0a 100644
--- a/arch/x86/realmode/init.c
+++ b/arch/x86/realmode/init.c
@@ -4,6 +4,7 @@
#include <linux/memblock.h>
#include <linux/cc_platform.h>
#include <linux/pgtable.h>
+#include <linux/slaunch.h>
#include <asm/set_memory.h>
#include <asm/realmode.h>
@@ -213,6 +214,13 @@ void __init init_real_mode(void)
setup_real_mode();
set_real_mode_permissions();
+
+ /*
+ * If Secure Launch is active, it will use the rmpiggy to do the TXT AP
+ * startup. Secure Launch has its own entry stub in the rmpiggy and this prepares
+ * it for SMP boot.
+ */
+ slaunch_fixup_ap_wake_vector();
}
static int __init do_init_real_mode(void)
--git a/arch/x86/realmode/rm/header.S b/arch/x86/realmode/rm/header.S
index 2eb62be6d256..3b5cbcbbfc90 100644
--- a/arch/x86/realmode/rm/header.S
+++ b/arch/x86/realmode/rm/header.S
@@ -37,6 +37,9 @@ SYM_DATA_START(real_mode_header)
#ifdef CONFIG_X86_64
.long __KERNEL32_CS
#endif
+#ifdef CONFIG_SECURE_LAUNCH
+ .long pa_sl_trampoline_start32
+#endif
SYM_DATA_END(real_mode_header)
/* End signature, used to verify integrity */
diff --git a/arch/x86/realmode/rm/trampoline_64.S b/arch/x86/realmode/rm/trampoline_64.S
index 14d9c7daf90f..b0ce6205d7ea 100644
--- a/arch/x86/realmode/rm/trampoline_64.S
+++ b/arch/x86/realmode/rm/trampoline_64.S
@@ -122,6 +122,38 @@ SYM_CODE_END(sev_es_trampoline_start)
.section ".text32","ax"
.code32
+#ifdef CONFIG_SECURE_LAUNCH
+ .balign 4
+SYM_CODE_START(sl_trampoline_start32)
+ /*
+ * The early secure launch stub AP wakeup code has taken care of all
+ * the vagaries of launching out of TXT. This bit just mimics what the
+ * 16b entry code does and jumps off to the real startup_32.
+ */
+ cli
+ wbinvd
+
+ /*
+ * The %ebx provided is not terribly useful since it is the physical
+ * address of tb_trampoline_start and not the base of the image.
+ * Use pa_real_mode_base, which is fixed up, to get a run time
+ * base register to use for offsets to location that do not have
+ * pa_ symbols.
+ */
+ movl $pa_real_mode_base, %ebx
+
+ LOCK_AND_LOAD_REALMODE_ESP lock_pa=1
+
+ lgdt tr_gdt(%ebx)
+ lidt tr_idt(%ebx)
+
+ movw $__KERNEL_DS, %dx # Data segment descriptor
+
+ /* Jump to where the 16b code would have jumped */
+ ljmpl $__KERNEL32_CS, $pa_startup_32
+SYM_CODE_END(sl_trampoline_start32)
+#endif
+
.balign 4
SYM_CODE_START(startup_32)
movl %edx, %ss
--
2.43.7
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox