* Re: [PATCH v16 01/38] tpm: Initial step to reorganize TPM public headers
From: Jason Gunthorpe @ 2026-05-15 23:05 UTC (permalink / raw)
To: Jarkko Sakkinen
Cc: Ross Philipson, 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, luto,
nivedita, herbert, davem, corbet, ebiederm, dwmw2, baolu.lu,
kanth.ghatraju, daniel.kiper, andrew.cooper3, trenchboot-devel
In-Reply-To: <agemXwxVb9jvAbYM@kernel.org>
On Sat, May 16, 2026 at 02:03:59AM +0300, Jarkko Sakkinen wrote:
> LGTM
>
> I'll hold on from actual tags up until there is some consensus with the
> patch set.
This patch set is huge, and I know there is alot of interest now in
DRTM.
Can we please split out and progress the TPM reorg mini-series at the
front?
Jason
^ permalink raw reply
* [PATCH net v4 1/3] crypto/krb5, rxrpc: Fix lack of pre-decrypt/pre-verify length checks
From: David Howells @ 2026-05-15 23:05 UTC (permalink / raw)
To: netdev
Cc: David Howells, Hyunwoo Kim, Marc Dionne, Jakub Kicinski,
David S. Miller, Eric Dumazet, Paolo Abeni, Simon Horman,
linux-afs, linux-kernel, Jeffrey Altman, Herbert Xu, Chuck Lever,
linux-nfs, linux-crypto, stable
In-Reply-To: <20260515230516.2718212-1-dhowells@redhat.com>
Change the krb5 crypto library to provide facilities to precheck the length
of the message about to be decrypted or verified.
Fix AF_RXRPC to make use of this to validate DATA packets secured with
RxGK.
Fixes: 9d1d2b59341f ("rxrpc: rxgk: Implement the yfs-rxgk security class (GSSAPI)")
Closes: https://sashiko.dev/#/patchset/20260511160753.607296-1-dhowells%40redhat.com
Signed-off-by: David Howells <dhowells@redhat.com>
cc: Marc Dionne <marc.dionne@auristor.com>
cc: Jeffrey Altman <jaltman@auristor.com>
cc: Herbert Xu <herbert@gondor.apana.org.au>
cc: "David S. Miller" <davem@davemloft.net>
cc: Eric Dumazet <edumazet@google.com>
cc: Jakub Kicinski <kuba@kernel.org>
cc: Paolo Abeni <pabeni@redhat.com>
cc: Simon Horman <horms@kernel.org>
cc: Chuck Lever <chuck.lever@oracle.com>
cc: netdev@vger.kernel.org
cc: linux-afs@lists.infradead.org
cc: linux-nfs@vger.kernel.org
cc: linux-crypto@vger.kernel.org
cc: stable@vger.kernel.org
---
Documentation/crypto/krb5.rst | 17 ++++++++---
crypto/krb5/krb5_api.c | 54 +++++++++++++++++++++++++++++++----
include/crypto/krb5.h | 9 ++++--
include/trace/events/rxrpc.h | 1 +
net/rxrpc/rxgk.c | 15 ++++++++--
5 files changed, 81 insertions(+), 15 deletions(-)
diff --git a/Documentation/crypto/krb5.rst b/Documentation/crypto/krb5.rst
index beffa0133446..f62e07ac6811 100644
--- a/Documentation/crypto/krb5.rst
+++ b/Documentation/crypto/krb5.rst
@@ -158,13 +158,22 @@ returned.
When a message has been received, the location and size of the data with the
message can be determined by calling::
- void crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5,
- enum krb5_crypto_mode mode,
- size_t *_offset, size_t *_len);
+ int crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5,
+ enum krb5_crypto_mode mode,
+ size_t *_offset, size_t *_len);
The caller provides the offset and length of the message to the function, which
then alters those values to indicate the region containing the data (plus any
-padding). It is up to the caller to determine how much padding there is.
+padding). It is up to the caller to determine how much padding there is. The
+function returns an error if the length is too small or if the mode is
+unsupported. An additional function::
+
+ int crypto_krb5_check_data_len(const struct krb5_enctype *krb5,
+ enum krb5_crypto_mode mode,
+ size_t len, size_t min_content);
+
+is provided to just do a basic check that the decrypted/verified message would
+have a sufficient minimum payload.
Preparation Functions
---------------------
diff --git a/crypto/krb5/krb5_api.c b/crypto/krb5/krb5_api.c
index 23026d4206c8..c7ea40f900a7 100644
--- a/crypto/krb5/krb5_api.c
+++ b/crypto/krb5/krb5_api.c
@@ -134,27 +134,69 @@ EXPORT_SYMBOL(crypto_krb5_how_much_data);
* Find the offset and size of the data in a secure message so that this
* information can be used in the metadata buffer which will get added to the
* digest by crypto_krb5_verify_mic().
+ *
+ * Return: 0 if successful, -EBADMSG if the message is too short or -EINVAL if
+ * the mode is unsupported.
*/
-void crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5,
- enum krb5_crypto_mode mode,
- size_t *_offset, size_t *_len)
+int crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5,
+ enum krb5_crypto_mode mode,
+ size_t *_offset, size_t *_len)
{
switch (mode) {
case KRB5_CHECKSUM_MODE:
+ if (*_len < krb5->cksum_len)
+ return -EBADMSG;
*_offset += krb5->cksum_len;
*_len -= krb5->cksum_len;
- return;
+ return 0;
case KRB5_ENCRYPT_MODE:
+ if (*_len < krb5->conf_len + krb5->cksum_len)
+ return -EBADMSG;
*_offset += krb5->conf_len;
*_len -= krb5->conf_len + krb5->cksum_len;
- return;
+ return 0;
default:
WARN_ON_ONCE(1);
- return;
+ return -EINVAL;
}
}
EXPORT_SYMBOL(crypto_krb5_where_is_the_data);
+/**
+ * crypto_krb5_check_data_len - Check a message is big enough
+ * @krb5: The encoding to use.
+ * @mode: Mode of operation.
+ * @len: The length of the secure blob.
+ * @min_content: Minimum length of the content inside the blob.
+ *
+ * Check that a message is large enough to hold whatever bits the encryption
+ * type wants to glue on (nonce, checksum) plus a minimum amount of content.
+ *
+ * Return: 0 if successful, -EBADMSG if the message is too short or -EINVAL if
+ * the mode is unsupported.
+ */
+int crypto_krb5_check_data_len(const struct krb5_enctype *krb5,
+ enum krb5_crypto_mode mode,
+ size_t len, size_t min_content)
+{
+ switch (mode) {
+ case KRB5_CHECKSUM_MODE:
+ if (len < krb5->cksum_len ||
+ len - krb5->cksum_len < min_content)
+ return -EBADMSG;
+ return 0;
+ case KRB5_ENCRYPT_MODE:
+ if (len < krb5->conf_len + krb5->cksum_len ||
+ len - (krb5->conf_len + krb5->cksum_len) < min_content)
+ return -EBADMSG;
+ return 0;
+ default:
+ WARN_ON_ONCE(1);
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL(crypto_krb5_check_data_len);
+
/*
* Prepare the encryption with derived key data.
*/
diff --git a/include/crypto/krb5.h b/include/crypto/krb5.h
index 71dd38f59be1..aac3ecf88467 100644
--- a/include/crypto/krb5.h
+++ b/include/crypto/krb5.h
@@ -121,9 +121,12 @@ size_t crypto_krb5_how_much_buffer(const struct krb5_enctype *krb5,
size_t crypto_krb5_how_much_data(const struct krb5_enctype *krb5,
enum krb5_crypto_mode mode,
size_t *_buffer_size, size_t *_offset);
-void crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5,
- enum krb5_crypto_mode mode,
- size_t *_offset, size_t *_len);
+int crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5,
+ enum krb5_crypto_mode mode,
+ size_t *_offset, size_t *_len);
+int crypto_krb5_check_data_len(const struct krb5_enctype *krb5,
+ enum krb5_crypto_mode mode,
+ size_t len, size_t min_content);
struct crypto_aead *crypto_krb5_prepare_encryption(const struct krb5_enctype *krb5,
const struct krb5_buffer *TK,
u32 usage, gfp_t gfp);
diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h
index 573f2df3a2c9..704a10de6670 100644
--- a/include/trace/events/rxrpc.h
+++ b/include/trace/events/rxrpc.h
@@ -71,6 +71,7 @@
EM(rxkad_abort_resp_unknown_tkt, "rxkad-resp-unknown-tkt") \
EM(rxkad_abort_resp_version, "rxkad-resp-version") \
/* RxGK security errors */ \
+ EM(rxgk_abort_1_short_header, "rxgk1-short-hdr") \
EM(rxgk_abort_1_verify_mic_eproto, "rxgk1-vfy-mic-eproto") \
EM(rxgk_abort_2_decrypt_eproto, "rxgk2-dec-eproto") \
EM(rxgk_abort_2_short_data, "rxgk2-short-data") \
diff --git a/net/rxrpc/rxgk.c b/net/rxrpc/rxgk.c
index 0d5e654da918..26e723052a37 100644
--- a/net/rxrpc/rxgk.c
+++ b/net/rxrpc/rxgk.c
@@ -480,8 +480,12 @@ static int rxgk_verify_packet_integrity(struct rxrpc_call *call,
_enter("");
- crypto_krb5_where_is_the_data(gk->krb5, KRB5_CHECKSUM_MODE,
- &data_offset, &data_len);
+ if (crypto_krb5_where_is_the_data(gk->krb5, KRB5_CHECKSUM_MODE,
+ &data_offset, &data_len) < 0) {
+ ret = rxrpc_abort_eproto(call, skb, RXGK_PACKETSHORT,
+ rxgk_abort_1_short_header);
+ goto put_gk;
+ }
hdr = kzalloc_obj(*hdr, GFP_NOFS);
if (!hdr)
@@ -529,6 +533,13 @@ static int rxgk_verify_packet_encrypted(struct rxrpc_call *call,
_enter("");
+ if (crypto_krb5_check_data_len(gk->krb5, KRB5_ENCRYPT_MODE,
+ len, sizeof(hdr)) < 0) {
+ ret = rxrpc_abort_eproto(call, skb, RXGK_PACKETSHORT,
+ rxgk_abort_2_short_header);
+ goto error;
+ }
+
ret = rxgk_decrypt_skb(gk->krb5, gk->rx_enc, skb, &offset, &len, &ac);
if (ret < 0) {
if (ret != -ENOMEM)
^ permalink raw reply related
* Re: [PATCH v16 01/38] tpm: Initial step to reorganize TPM public headers
From: Jarkko Sakkinen @ 2026-05-15 23:03 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, daniel.kiper, andrew.cooper3, trenchboot-devel
In-Reply-To: <20260515211410.31440-2-ross.philipson@gmail.com>
On Fri, May 15, 2026 at 02:13:33PM -0700, Ross Philipson wrote:
> Consolidate TPM1 constants in tpm_command.h and remove duplicate
> constants from tpm1-cmd.c.
>
> Co-developed-by: Daniel P. Smith <dpsmith@apertussolutions.com>
> Signed-off-by: Daniel P. Smith <dpsmith@apertussolutions.com>
> Co-developed-by: Alec Brown <alec.r.brown@oracle.com>
> Signed-off-by: Alec Brown <alec.r.brown@oracle.com>
> Signed-off-by: Ross Philipson <ross.philipson@gmail.com>
> ---
> drivers/char/tpm/tpm-buf.c | 1 -
> drivers/char/tpm/tpm1-cmd.c | 14 +-------
> include/keys/trusted_tpm.h | 1 -
> include/linux/tpm.h | 2 ++
> include/linux/tpm_command.h | 41 ++++++++++++++++-------
> security/keys/trusted-keys/trusted_tpm1.c | 1 -
> security/keys/trusted-keys/trusted_tpm2.c | 1 -
> 7 files changed, 31 insertions(+), 30 deletions(-)
>
> diff --git a/drivers/char/tpm/tpm-buf.c b/drivers/char/tpm/tpm-buf.c
> index dc882fc9fa9e..4c4f450630df 100644
> --- a/drivers/char/tpm/tpm-buf.c
> +++ b/drivers/char/tpm/tpm-buf.c
> @@ -3,7 +3,6 @@
> * Handling of TPM command and other buffers.
> */
>
> -#include <linux/tpm_command.h>
> #include <linux/module.h>
> #include <linux/tpm.h>
>
> diff --git a/drivers/char/tpm/tpm1-cmd.c b/drivers/char/tpm/tpm1-cmd.c
> index b49a790f1bd5..664ca1fff2e8 100644
> --- a/drivers/char/tpm/tpm1-cmd.c
> +++ b/drivers/char/tpm/tpm1-cmd.c
> @@ -22,8 +22,6 @@
>
> #include "tpm.h"
>
> -#define TPM_MAX_ORDINAL 243
> -
> /*
> * Array with one entry per ordinal defining the maximum amount
> * of time the chip could take to return the result. The ordinal
> @@ -308,9 +306,6 @@ unsigned long tpm1_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal)
> return duration;
> }
>
> -#define TPM_ORD_STARTUP 153
> -#define TPM_ST_CLEAR 1
> -
> /**
> * tpm1_startup() - turn on the TPM
> * @chip: TPM chip to use
> @@ -459,7 +454,6 @@ int tpm1_get_timeouts(struct tpm_chip *chip)
> return 0;
> }
>
> -#define TPM_ORD_PCR_EXTEND 20
> int tpm1_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash,
> const char *log_msg)
> {
> @@ -478,7 +472,6 @@ int tpm1_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash,
> return rc;
> }
>
> -#define TPM_ORD_GET_CAP 101
> ssize_t tpm1_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap,
> const char *desc, size_t min_cap_length)
> {
> @@ -511,7 +504,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];
> @@ -580,13 +572,12 @@ int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
> return rc;
> }
>
> -#define TPM_ORD_PCRREAD 21
> int tpm1_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf)
> {
> struct tpm_buf buf;
> int rc;
>
> - rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_PCRREAD);
> + rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_PCR_READ);
> if (rc)
> return rc;
>
> @@ -609,7 +600,6 @@ int tpm1_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf)
> return rc;
> }
>
> -#define TPM_ORD_CONTINUE_SELFTEST 83
> /**
> * tpm1_continue_selftest() - run TPM's selftest
> * @chip: TPM chip to use
> @@ -726,8 +716,6 @@ int tpm1_auto_startup(struct tpm_chip *chip)
> return rc;
> }
>
> -#define TPM_ORD_SAVESTATE 152
> -
> /**
> * tpm1_pm_suspend() - pm suspend handler
> * @chip: TPM chip to use.
> diff --git a/include/keys/trusted_tpm.h b/include/keys/trusted_tpm.h
> index 0fadc6a4f166..3a0fa3bc8454 100644
> --- a/include/keys/trusted_tpm.h
> +++ b/include/keys/trusted_tpm.h
> @@ -3,7 +3,6 @@
> #define __TRUSTED_TPM_H
>
> #include <keys/trusted-type.h>
> -#include <linux/tpm_command.h>
>
> extern struct trusted_key_ops trusted_key_tpm_ops;
>
> diff --git a/include/linux/tpm.h b/include/linux/tpm.h
> index 202da079d500..1846d5485a2c 100644
> --- a/include/linux/tpm.h
> +++ b/include/linux/tpm.h
> @@ -25,6 +25,8 @@
> #include <crypto/hash_info.h>
> #include <crypto/aes.h>
>
> +#include <linux/tpm_command.h>
> +
> #define TPM_DIGEST_SIZE 20 /* Max TPM v1.2 PCR size */
>
> #define TPM2_MAX_DIGEST_SIZE SHA512_DIGEST_SIZE
> diff --git a/include/linux/tpm_command.h b/include/linux/tpm_command.h
> index f5c03e9c3913..174b043d8bbc 100644
> --- a/include/linux/tpm_command.h
> +++ b/include/linux/tpm_command.h
> @@ -3,27 +3,42 @@
> #define __LINUX_TPM_COMMAND_H__
>
> /*
> - * TPM Command constants from specifications at
> - * http://www.trustedcomputinggroup.org
> + * == TPM 1 Family Chips ==
> + *
> + * TPM 1.2 Main Specification:
> + * https://trustedcomputinggroup.org/resource/tpm-main-specification/
> */
>
> +#define TPM_MAX_ORDINAL 243
> +
> /* Command TAGS */
> -#define TPM_TAG_RQU_COMMAND 193
> -#define TPM_TAG_RQU_AUTH1_COMMAND 194
> -#define TPM_TAG_RQU_AUTH2_COMMAND 195
> -#define TPM_TAG_RSP_COMMAND 196
> -#define TPM_TAG_RSP_AUTH1_COMMAND 197
> -#define TPM_TAG_RSP_AUTH2_COMMAND 198
> +enum tpm_command_tags {
> + TPM_TAG_RQU_COMMAND = 193,
> + TPM_TAG_RQU_AUTH1_COMMAND = 194,
> + TPM_TAG_RQU_AUTH2_COMMAND = 195,
> + TPM_TAG_RSP_COMMAND = 196,
> + TPM_TAG_RSP_AUTH1_COMMAND = 197,
> + TPM_TAG_RSP_AUTH2_COMMAND = 198,
> +};
>
> /* Command Ordinals */
> -#define TPM_ORD_GETRANDOM 70
> -#define TPM_ORD_OSAP 11
> -#define TPM_ORD_OIAP 10
> -#define TPM_ORD_SEAL 23
> -#define TPM_ORD_UNSEAL 24
> +enum tpm_command_ordinals {
> + TPM_ORD_CONTINUE_SELFTEST = 83,
> + TPM_ORD_GET_CAP = 101,
> + TPM_ORD_GET_RANDOM = 70,
> + TPM_ORD_PCR_EXTEND = 20,
> + TPM_ORD_PCR_READ = 21,
> + TPM_ORD_OSAP = 11,
> + TPM_ORD_OIAP = 10,
> + TPM_ORD_SAVESTATE = 152,
> + TPM_ORD_SEAL = 23,
> + TPM_ORD_STARTUP = 153,
> + TPM_ORD_UNSEAL = 24,
> +};
>
> /* Other constants */
> #define SRKHANDLE 0x40000000
> #define TPM_NONCE_SIZE 20
> +#define TPM_ST_CLEAR 1
>
> #endif
> diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c
> index 6ea728f1eae6..0d3244af8de3 100644
> --- a/security/keys/trusted-keys/trusted_tpm1.c
> +++ b/security/keys/trusted-keys/trusted_tpm1.c
> @@ -18,7 +18,6 @@
> #include <keys/trusted-type.h>
> #include <linux/key-type.h>
> #include <linux/tpm.h>
> -#include <linux/tpm_command.h>
>
> #include <keys/trusted_tpm.h>
>
> diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
> index 6340823f8b53..29d79c05ed6b 100644
> --- a/security/keys/trusted-keys/trusted_tpm2.c
> +++ b/security/keys/trusted-keys/trusted_tpm2.c
> @@ -9,7 +9,6 @@
> #include <linux/string.h>
> #include <linux/err.h>
> #include <linux/tpm.h>
> -#include <linux/tpm_command.h>
>
> #include <keys/trusted-type.h>
> #include <keys/trusted_tpm.h>
> --
> 2.47.3
>
LGTM
I'll hold on from actual tags up until there is some consensus with the
patch set.
BR, Jarkko
^ permalink raw reply
* Re: [PATCH v16 29/38] x86/tpm: Early startup TPM PCR extending driver
From: Dave Hansen @ 2026-05-15 22:32 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,
daniel.kiper, andrew.cooper3, trenchboot-devel
In-Reply-To: <20260515211410.31440-30-ross.philipson@gmail.com>
On 5/15/26 14:14, Ross Philipson wrote:
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Based of the original tpm_tis.c implementation as found in the
> + * Linux 2.6 code base.
> + *
> + * Copyright (C) 2005, 2006 IBM Corporation
> + *
> + * Authors:
> + * Leendert van Doorn <leendert@watson.ibm.com>
> + * Kylene Hall <kjhall@us.ibm.com>
> + */
Hey guys,
Last time:
> https://lore.kernel.org/lkml/56929e8b-a7cf-4390-b4ec-0b4c2c32b311@intel.com/
So, fast forward another version and six months ... the "fishy" bits
ripped out of it, license changed and ... crickets in the changelogs and
cover letter from what I can see.
Second, the cadence on these is rather slow. I don't know what the delta
from the last one is, but it doesn't _look_ like a six month project.
It's really hard for reviewers to page stuff back in after that long.
Also, new since the last post, you got Sashiko'd:
> https://sashiko.dev/#/patchset/20260515211410.31440-1-ross.philipson%40gmail.com
I'd suggest working through those first before the maintainers take
another crack at this. There are a *LOT* of comments in there.
I'd appreciate if you'd go back through the v15 comments and make sure
you got all of them. I certainly felt like you didn't address at least
on of my questions (the licensing stuff).
^ permalink raw reply
* Re: [PATCH v16 15/38] Documentation/security: Secure Launch kernel documentation
From: Randy Dunlap @ 2026-05-15 22:02 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,
daniel.kiper, andrew.cooper3, trenchboot-devel
In-Reply-To: <20260515211410.31440-16-ross.philipson@gmail.com>
On 5/15/26 2:13 PM, Ross Philipson wrote:
> From: "Daniel P. Smith" <dpsmith@apertussolutions.com>
>
> Introduce documentation for the Linux Secure Launch feature.
>
> Co-developed-by: Ross Philipson <ross.philipson@gmail.com>
> Signed-off-by: Daniel P. Smith <dpsmith@apertussolutions.com>
> Signed-off-by: Ross Philipson <ross.philipson@gmail.com>
> ---
> Documentation/arch/x86/boot.rst | 8 +
> Documentation/security/index.rst | 1 +
> .../security/launch-integrity/index.rst | 9 +
> .../launch-integrity/secure_launch.rst | 681 ++++++++++++++++++
> 4 files changed, 699 insertions(+)
> create mode 100644 Documentation/security/launch-integrity/index.rst
> create mode 100644 Documentation/security/launch-integrity/secure_launch.rst
>
> diff --git a/Documentation/security/launch-integrity/secure_launch.rst b/Documentation/security/launch-integrity/secure_launch.rst
> new file mode 100644
> index 000000000000..b4c61fdceaf0
> --- /dev/null
> +++ b/Documentation/security/launch-integrity/secure_launch.rst
> @@ -0,0 +1,681 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +.. Copyright (c) 2019-2026 Daniel P. Smith <dpsmith@apertussolutions.com>
> +
> +======================
> +Secure Launch Overview
> +======================
> +
> +:Author: Daniel P. Smith
> +:Date: February 2026
maybe update?
> +
> +Overview
> +========
[snip]
> +Error Codes
> +===========
> +
> +The TXT specification defines the layout for TXT 32 bit error code values.
32-bit
> +The bit encodings indicate where the error originated (e.g. with the CPU,
> +in the SINIT ACM, in software). The error is written to a sticky TXT
> +register that persists across resets called TXT.ERRORCODE (see the TXT
> +MLE Development Guide). The errors defined by the Secure Launch feature are
> +those generated in the MLE software. They have the format::
> +
> + 0xc0008XXX
> +
> +The low 12 bits are free for defining the following Secure Launch specific
> +error codes.
[snip]
> +Resources
> +=========
> +
> +The TrenchBoot project:
> +
> +https://trenchboot.org
> +
> +Secure Launch Specification:
> +
> +https://trenchboot.org/specifications/Secure_Launch/
> +
> +Trusted Computing Group's D-RTM Architecture:
> +
> +https://trustedcomputinggroup.org/wp-content/uploads/TCG_D-RTM_Architecture_v1-0_Published_06172013.pdf
> +
> +TXT documentation in the Intel TXT MLE Development Guide:
> +
> +https://www.intel.com/content/dam/www/public/us/en/documents/guides/intel-txt-software-development-guide.pdf
> +
> +TXT instructions documentation in the Intel SDM Instruction Set volume:
> +
> +https://software.intel.com/en-us/articles/intel-sdm
> +
> +AMD SKINIT documentation in the System Programming manual:
> +
> +https://www.amd.com/system/files/TechDocs/24593.pdf
> +
> +GRUB Secure Launch support:
> +
> +https://github.com/TrenchBoot/grub/tree/grub-sl-fc-38-dlstub
> +
> +FOSDEM 2021: Secure Upgrades with DRTM
> +
> +https://archive.fosdem.org/2021/schedule/event/firmware_suwd/
> +
> +.. [1]
> + MLE: Measured Launch Environment is the binary runtime that is measured and
> + then run by the TXT SINIT ACM. The TXT MLE Development Guide describes the
> + requirements for the MLE in detail.
> +
> +.. [2]
> + PMR: Intel VTd has a feature in the IOMMU called Protected Memory Registers.
> + There are two of these registers and they allow all DMA to be blocked
> + to large areas of memory. The low PMR can cover all memory below 4Gb on 2Mb
> + boundaries. The high PMR can cover all RAM on the system, again on 2Mb
> + boundaries. This feature is used during a Secure Launch by TXT.
> +
> +.. [3]
> + Secure Launch Specification: https://trenchboot.org/specifications/Secure_Launch/
> +
> +.. [4]
> + ACM: Intel's Authenticated Code Module. This is the 32b bit binary blob that
Does "32b" mean something or should that be 32-bit?
> + is run securely by the GETSEC[SENTER] during a measured launch. It is described
> + in the Intel documentation on TXT and versions for various chipsets are
> + signed and distributed by Intel.
--
~Randy
^ permalink raw reply
* [PATCH v16 38/38] x86/boot: Legacy boot DRTM support for Secure Launch
From: Ross Philipson @ 2026-05-15 21:14 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,
daniel.kiper, andrew.cooper3, trenchboot-devel
In-Reply-To: <20260515211410.31440-1-ross.philipson@gmail.com>
From: Ard Biesheuvel <ardb@kernel.org>
Implement Secure Launch D-RTM of the decompressed kernel via a
callback interface exposed by the Secure Launch Resource Table (SLRT), a
reference to which is added to struct boot_params.
This permits a boot loader to set up the Secure Launch, allow the
decompressor to execute up to the point where it would otherwise boot the
core kernel, and at that point, perform the Dynamic Launch Event in a
architecture/vendor specific manner. This is similar to how EFI boot
achieves this, using a EFI protocol exposed by the boot loader.
This requires that the decompressor unpacks the kernel into the buffer that
it was started from itself, and so physical KASLR needs to be omitted
(although the boot loader is free to place the decompressor at any
suitably aligned locations in system memory, and so it can perform the
physical randomization itself).
It also relies on the demand paging logic in the decompressor, to ensure
that the SLRT and the entry point it describes are callable, at least to
the extent that allows the callback code to re-establish its own
execution environment.
Co-developed-by: Ross Philipson <ross.philipson@gmail.com>
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Signed-off-by: Ross Philipson <ross.philipson@gmail.com>
---
Documentation/arch/x86/zero-page.rst | 1 +
arch/x86/boot/compressed/misc.c | 51 ++++++++++++++++++++++++---
arch/x86/boot/compressed/pgtable_64.c | 7 ++++
arch/x86/include/uapi/asm/bootparam.h | 2 +-
4 files changed, 56 insertions(+), 5 deletions(-)
diff --git a/Documentation/arch/x86/zero-page.rst b/Documentation/arch/x86/zero-page.rst
index 45aa9cceb4f1..dd98b467929c 100644
--- a/Documentation/arch/x86/zero-page.rst
+++ b/Documentation/arch/x86/zero-page.rst
@@ -20,6 +20,7 @@ Offset/Size Proto Name Meaning
060/010 ALL ist_info Intel SpeedStep (IST) BIOS support information
(struct ist_info)
070/008 ALL acpi_rsdp_addr Physical address of ACPI RSDP table
+078/008 ALL slr_table_addr Physical address of Secure Launch Resource Table
080/010 ALL hd0_info hd0 disk parameter, OBSOLETE!!
090/010 ALL hd1_info hd1 disk parameter, OBSOLETE!!
0A0/010 ALL sys_desc_table System description table (struct sys_desc_table),
diff --git a/arch/x86/boot/compressed/misc.c b/arch/x86/boot/compressed/misc.c
index e3b5177bfa6f..eaaface4cd7d 100644
--- a/arch/x86/boot/compressed/misc.c
+++ b/arch/x86/boot/compressed/misc.c
@@ -17,6 +17,7 @@
#include "../string.h"
#include "../voffset.h"
#include <asm/bootparam_utils.h>
+#include <linux/slr_table.h>
/*
* WARNING!!
@@ -391,6 +392,36 @@ static void early_sev_detect(void)
lines = cols = 0;
}
+#ifdef CONFIG_SECURE_LAUNCH
+static void sl_initiate_launch(unsigned long table, unsigned long base)
+{
+ struct slr_table *slrt = (void *)table;
+ struct slr_entry_dl_info *dl_info;
+ struct slr_setup_dlme dlme;
+ dl_launch_func launch_fn;
+
+ dlme.dlme_base = base;
+ dlme.dlme_header_offset = mle_header_offset;
+ dlme.dlme_table = 0;
+
+ if (!slrt)
+ return;
+
+ dl_info = slr_next_entry_by_tag(slrt, NULL, SLR_ENTRY_DL_INFO);
+ if (!dl_info)
+ return;
+
+ launch_fn = (void *)dl_info->dl_launch;
+
+ /* Do the Dynamic Launch Event */
+ launch_fn(&dl_info->bl_context, &dlme);
+}
+#else
+static inline void sl_initiate_launch(unsigned long table, unsigned long base)
+{
+}
+#endif
+
/*
* The compressed kernel image (ZO), has been moved so that its position
* is against the end of the buffer used to hold the uncompressed kernel
@@ -491,10 +522,15 @@ asmlinkage __visible void *extract_kernel(void *rmode, unsigned char *output)
debug_putaddr(trampoline_32bit);
#endif
- choose_random_location((unsigned long)input_data, input_len,
- (unsigned long *)&output,
- needed_size,
- &virt_addr);
+ /*
+ * When doing a secure launch, the actual launch will be initiated by
+ * jumping back to the bootloader. Omit physical KASLR in that case, to
+ * avoid trampling on its code or data inadvertently.
+ */
+ if (!boot_params_ptr->slr_table_addr)
+ choose_random_location((unsigned long)input_data, input_len,
+ (unsigned long *)&output,
+ needed_size, &virt_addr);
/* Validate memory location choices. */
if ((unsigned long)output & (MIN_KERNEL_ALIGN - 1))
@@ -528,6 +564,13 @@ asmlinkage __visible void *extract_kernel(void *rmode, unsigned char *output)
debug_puthex(entry_offset);
debug_putstr(").\n");
+ /*
+ * Secure Launch involves calling back into the bootloader, so this
+ * needs to happen before disabling exception handling, to ensure that
+ * the entry point will be mapped on demand if needed.
+ */
+ sl_initiate_launch(boot_params_ptr->slr_table_addr, (unsigned long)output);
+
/* Disable exception handling before booting the kernel */
cleanup_exception_handling();
diff --git a/arch/x86/boot/compressed/pgtable_64.c b/arch/x86/boot/compressed/pgtable_64.c
index 3e9d651da73e..f82094669ac0 100644
--- a/arch/x86/boot/compressed/pgtable_64.c
+++ b/arch/x86/boot/compressed/pgtable_64.c
@@ -124,6 +124,13 @@ asmlinkage void configure_5level_paging(struct boot_params *bp, void *pgtable)
l5_required = !cmdline_find_option_bool("no5lvl");
+ /*
+ * Don't change the number of levels when doing a Secure Launch. The
+ * Secure Launch stub will take care of that if needed.
+ */
+ if (bp->slr_table_addr)
+ l5_required = l5_enabled;
+
if (l5_required) {
/* Initialize variables for 5-level paging */
__pgtable_l5_enabled = 1;
diff --git a/arch/x86/include/uapi/asm/bootparam.h b/arch/x86/include/uapi/asm/bootparam.h
index 8155fa899f50..bc2ef37096af 100644
--- a/arch/x86/include/uapi/asm/bootparam.h
+++ b/arch/x86/include/uapi/asm/bootparam.h
@@ -121,7 +121,7 @@ struct boot_params {
__u64 tboot_addr; /* 0x058 */
struct ist_info ist_info; /* 0x060 */
__u64 acpi_rsdp_addr; /* 0x070 */
- __u8 _pad3[8]; /* 0x078 */
+ __u64 slr_table_addr; /* 0x078 */
__u8 hd0_info[16]; /* obsolete! */ /* 0x080 */
__u8 hd1_info[16]; /* obsolete! */ /* 0x090 */
struct sys_desc_table sys_desc_table; /* obsolete! */ /* 0x0a0 */
--
2.47.3
^ permalink raw reply related
* [PATCH v16 37/38] x86/efistub: EFI stub DRTM support for Secure Launch
From: Ross Philipson @ 2026-05-15 21:14 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,
daniel.kiper, andrew.cooper3, trenchboot-devel
In-Reply-To: <20260515211410.31440-1-ross.philipson@gmail.com>
From: Ard Biesheuvel <ardb@kernel.org>
Invoke the Secure Launch protocol exposed by the boot loader at the
appropriate time to perform a measured launch of the decompressed
kernel after ExitBootServices().
Co-developed-by: Ross Philipson <ross.philipson@gmail.com>
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Signed-off-by: Ross Philipson <ross.philipson@gmail.com>
---
drivers/firmware/efi/libstub/Makefile | 1 +
drivers/firmware/efi/libstub/efistub.h | 24 ++++++++++++++
drivers/firmware/efi/libstub/x86-slaunch.c | 38 ++++++++++++++++++++++
drivers/firmware/efi/libstub/x86-stub.c | 27 ++++++++++++---
4 files changed, 86 insertions(+), 4 deletions(-)
create mode 100644 drivers/firmware/efi/libstub/x86-slaunch.c
diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
index e386ffd009b7..fd5eaf3142b2 100644
--- a/drivers/firmware/efi/libstub/Makefile
+++ b/drivers/firmware/efi/libstub/Makefile
@@ -86,6 +86,7 @@ lib-$(CONFIG_ARM) += arm32-stub.o
lib-$(CONFIG_ARM64) += kaslr.o arm64.o arm64-stub.o smbios.o
lib-$(CONFIG_X86) += x86-stub.o smbios.o
lib-$(CONFIG_X86_64) += x86-5lvl.o
+lib-$(CONFIG_SECURE_LAUNCH) += x86-slaunch.o
lib-$(CONFIG_RISCV) += kaslr.o riscv.o riscv-stub.o
lib-$(CONFIG_LOONGARCH) += loongarch.o loongarch-stub.o
diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h
index 979a21818cc1..18301ba3ae0f 100644
--- a/drivers/firmware/efi/libstub/efistub.h
+++ b/drivers/firmware/efi/libstub/efistub.h
@@ -1267,4 +1267,28 @@ void arch_accept_memory(phys_addr_t start, phys_addr_t end);
efi_status_t efi_zboot_decompress_init(unsigned long *alloc_size);
efi_status_t efi_zboot_decompress(u8 *out, unsigned long outlen);
+#ifdef CONFIG_SECURE_LAUNCH
+efi_status_t efi_secure_launch_init(efi_handle_t image_handle);
+efi_status_t efi_secure_launch_prepare(struct boot_params *boot_params,
+ phys_addr_t base);
+void efi_secure_launch(void);
+#else
+static inline
+efi_status_t efi_secure_launch_init(efi_handle_t image_handle)
+{
+ return EFI_UNSUPPORTED;
+}
+
+static inline
+efi_status_t efi_secure_launch_prepare(struct boot_params *boot_params,
+ phys_addr_t base)
+{
+ return EFI_SUCCESS;
+}
+
+static inline void efi_secure_launch(void)
+{
+}
+#endif
+
#endif
diff --git a/drivers/firmware/efi/libstub/x86-slaunch.c b/drivers/firmware/efi/libstub/x86-slaunch.c
new file mode 100644
index 000000000000..98ff15f94996
--- /dev/null
+++ b/drivers/firmware/efi/libstub/x86-slaunch.c
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/efi.h>
+#include <linux/pci.h>
+#include <linux/stddef.h>
+#include <linux/slr_efi.h>
+#include <linux/slaunch.h>
+
+#include <asm/boot.h>
+#include <asm/bootparam.h>
+#include <asm/efi.h>
+
+#include "efistub.h"
+
+static struct efi_slaunch_protocol *slaunch;
+
+efi_status_t efi_secure_launch_init(efi_handle_t image_handle)
+{
+ return efi_bs_call(handle_protocol, image_handle,
+ &EFI_SLAUNCH_PROTOCOL_GUID, (void **)&slaunch);
+}
+
+efi_status_t efi_secure_launch_prepare(struct boot_params *boot_params,
+ phys_addr_t base)
+{
+ if (!slaunch)
+ return EFI_SUCCESS;
+
+ return slaunch->setup_dlme(slaunch, base, mle_header_offset, (u64)boot_params);
+}
+
+void efi_secure_launch(void)
+{
+ if (!slaunch)
+ return;
+
+ slaunch->launch(slaunch);
+}
diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c
index cef32e2c82d8..339e63ae84ef 100644
--- a/drivers/firmware/efi/libstub/x86-stub.c
+++ b/drivers/firmware/efi/libstub/x86-stub.c
@@ -833,7 +833,8 @@ static efi_status_t parse_options(const char *cmdline)
}
static efi_status_t efi_decompress_kernel(unsigned long *kernel_entry,
- struct boot_params *boot_params)
+ struct boot_params *boot_params,
+ unsigned long alloc_limit)
{
unsigned long virt_addr = LOAD_PHYSICAL_ADDR;
unsigned long addr, alloc_size, entry;
@@ -877,8 +878,7 @@ static efi_status_t efi_decompress_kernel(unsigned long *kernel_entry,
status = efi_random_alloc(alloc_size, CONFIG_PHYSICAL_ALIGN, &addr,
seed[0], EFI_LOADER_CODE,
- LOAD_PHYSICAL_ADDR,
- EFI_X86_KERNEL_ALLOC_LIMIT);
+ LOAD_PHYSICAL_ADDR, alloc_limit);
if (status != EFI_SUCCESS)
return status;
@@ -890,6 +890,10 @@ static efi_status_t efi_decompress_kernel(unsigned long *kernel_entry,
*kernel_entry = addr + entry;
+ status = efi_secure_launch_prepare(boot_params, addr);
+ if (status != EFI_SUCCESS)
+ return status;
+
return efi_adjust_memory_range_protection(addr, kernel_text_size) ?:
efi_adjust_memory_range_protection(addr + kernel_inittext_offset,
kernel_inittext_size);
@@ -914,6 +918,7 @@ void __noreturn efi_stub_entry(efi_handle_t handle,
struct boot_params *boot_params)
{
+ unsigned long alloc_limit = EFI_X86_KERNEL_ALLOC_LIMIT;
efi_guid_t guid = EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID;
const struct linux_efi_initrd *initrd = NULL;
unsigned long kernel_entry;
@@ -925,6 +930,17 @@ void __noreturn efi_stub_entry(efi_handle_t handle,
if (efi_system_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
efi_exit(handle, EFI_INVALID_PARAMETER);
+ status = efi_secure_launch_init(handle);
+ switch (status) {
+ case EFI_SUCCESS:
+ alloc_limit = U32_MAX;
+ break;
+ case EFI_UNSUPPORTED:
+ break;
+ default:
+ efi_exit(handle, status);
+ }
+
if (!IS_ENABLED(CONFIG_EFI_HANDOVER_PROTOCOL) || !boot_params) {
status = efi_allocate_bootparams(handle, &boot_params);
if (status != EFI_SUCCESS)
@@ -974,7 +990,7 @@ void __noreturn efi_stub_entry(efi_handle_t handle,
if (efi_mem_encrypt > 0)
hdr->xloadflags |= XLF_MEM_ENCRYPTION;
- status = efi_decompress_kernel(&kernel_entry, boot_params);
+ status = efi_decompress_kernel(&kernel_entry, boot_params, alloc_limit);
if (status != EFI_SUCCESS) {
efi_err("Failed to decompress kernel\n");
goto fail;
@@ -1029,6 +1045,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();
+
/*
* Call the SEV init code while still running with the firmware's
* GDT/IDT, so #VC exceptions will be handled by EFI.
--
2.47.3
^ permalink raw reply related
* [PATCH v16 36/38] x86/slaunch: Secure Launch late initcall platform module
From: Ross Philipson @ 2026-05-15 21:14 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,
daniel.kiper, andrew.cooper3, trenchboot-devel
In-Reply-To: <20260515211410.31440-1-ross.philipson@gmail.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 from/to the DRTM TPM event log. In
addition, on Intel, access to TXT register fields is made available
for reading.
Co-developed-by: garnetgrimm <grimmg@ainfosec.com>
Signed-off-by: garnetgrimm <grimmg@ainfosec.com>
Co-developed-by: Ross Philipson <ross.philipson@gmail.com>
Signed-off-by: Daniel P. Smith <dpsmith@apertussolutions.com>
Signed-off-by: Ross Philipson <ross.philipson@gmail.com>
---
arch/x86/kernel/Makefile | 1 +
arch/x86/kernel/slmodule.c | 353 +++++++++++++++++++++++++++++++++++++
2 files changed, 354 insertions(+)
create mode 100644 arch/x86/kernel/slmodule.c
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index bf2471701662..8b039ed0a902 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -98,6 +98,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..9688249e274c
--- /dev/null
+++ b/arch/x86/kernel/slmodule.c
@@ -0,0 +1,353 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Secure Launch late validation/setup, securityfs exposure and finalization.
+ *
+ * Copyright (c) 2026 Apertus Solutions, LLC
+ * Copyright (c) 2026 Assured Information Security, Inc.
+ * Copyright (c) 2026, 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/efi.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 __init 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 memremap TXT heap failed\n", SL_ERROR_HEAP_MAP);
+
+ params = (struct txt_os_mle_data *)slaunch_txt_get_heap_table(txt_heap,
+ TXT_OS_MLE_DATA_TABLE);
+
+ /* Get the SLRT and remap it */
+ slrt = memremap(params->slrt, sizeof(*slrt), MEMREMAP_WB);
+ if (!slrt)
+ slaunch_reset(txt, "Error memremap SLR Table failed\n", SL_ERROR_SLRT_MAP);
+ size = slrt->size;
+ memunmap(slrt);
+
+ slrt = memremap(params->slrt, size, MEMREMAP_WB);
+ if (!slrt)
+ slaunch_reset(txt, "Error memremap SLR Table failed\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 SLR Table missing entry\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 memremap TPM event log failed\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 = slaunch_txt_get_heap_table(txt_heap, TXT_OS_SINIT_DATA_TABLE);
+
+ 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 locate TPM20 event log element failed\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 __init 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.47.3
^ permalink raw reply related
* [PATCH v16 35/38] reboot/slaunch: Secure Launch SEXIT support on reboot paths
From: Ross Philipson @ 2026-05-15 21:14 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,
daniel.kiper, andrew.cooper3, trenchboot-devel
In-Reply-To: <20260515211410.31440-1-ross.philipson@gmail.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.
Co-developed-by: Daniel P. Smith <dpsmith@apertussolutions.com>
Signed-off-by: Daniel P. Smith <dpsmith@apertussolutions.com>
Signed-off-by: Ross Philipson <ross.philipson@gmail.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 6032fa9ec753..87835706bb4f 100644
--- a/arch/x86/kernel/reboot.c
+++ b/arch/x86/kernel/reboot.c
@@ -14,6 +14,7 @@
#include <linux/pgtable.h>
#include <linux/kexec.h>
#include <linux/kvm_types.h>
+#include <linux/slaunch.h>
#include <acpi/reboot.h>
#include <asm/io.h>
#include <asm/apic.h>
@@ -779,6 +780,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);
}
@@ -789,6 +796,8 @@ static void native_machine_halt(void)
tboot_shutdown(TB_SHUTDOWN_HALT);
+ slaunch_finalize(1);
+
stop_this_cpu(NULL);
}
@@ -797,8 +806,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);
}
@@ -826,6 +839,7 @@ void machine_shutdown(void)
void machine_emergency_restart(void)
{
+ slaunch_finalize(0);
__machine_emergency_restart(1);
}
--
2.47.3
^ permalink raw reply related
* [PATCH v16 34/38] kexec/slaunch: Secure Launch kexec SEXIT support
From: Ross Philipson @ 2026-05-15 21:14 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,
daniel.kiper, andrew.cooper3, trenchboot-devel
In-Reply-To: <20260515211410.31440-1-ross.philipson@gmail.com>
Prior to running the next kernel via kexec, the Secure Launch code
closes down private SMX resources and does an SEXIT. This allows the
next kernel to start normally, effectively exiting the DRTM
environment.
The function slaunch_finalize() takes a boolean argument that controls
whether a GETSEC[SEXIT] can be issued. When true, the finalize code can
completely shutdown and exit the DRTM. This allows another kernel to
start normally and in turn can re-establish another DRTM session.
In cases where the machine has not been fully shutdown (e.g. when
machine_shutdown() was not called), the SEXIT step cannot be done
(SEXIT will fail if other processors are busy). In these cases SEXIT
is not attempted. This normally occurs on power off or reboot
operations where it doesn't really matter.
Co-developed-by: Daniel P. Smith <dpsmith@apertussolutions.com>
Signed-off-by: Daniel P. Smith <dpsmith@apertussolutions.com>
Signed-off-by: Ross Philipson <ross.philipson@gmail.com>
---
arch/x86/kernel/slaunch.c | 80 +++++++++++++++++++++++++++++++++++++++
kernel/kexec_core.c | 8 ++++
2 files changed, 88 insertions(+)
diff --git a/arch/x86/kernel/slaunch.c b/arch/x86/kernel/slaunch.c
index f6e6f1b7e18c..b34997a5f541 100644
--- a/arch/x86/kernel/slaunch.c
+++ b/arch/x86/kernel/slaunch.c
@@ -537,3 +537,83 @@ void __init slaunch_fixup_ap_wake_vector(void)
pr_info("TXT AP startup vector address updated\n");
}
+
+static inline void smx_getsec_sexit(void)
+{
+ asm volatile ("getsec\n" : : "a" (SMX_X86_GETSEC_SEXIT));
+}
+
+/*
+ * Used during kexec and on reboot paths to finalize the TXT state
+ * and do an SEXIT SMX operation, exiting the DRTM and disabling SMX mode.
+ */
+void slaunch_finalize(int do_sexit)
+{
+ u64 one = TXT_REGVALUE_ONE, val;
+ void __iomem *config;
+
+ if (!slaunch_is_txt_launch())
+ return;
+
+ config = ioremap(TXT_PRIV_CONFIG_REGS_BASE, TXT_NR_CONFIG_PAGES * PAGE_SIZE);
+ if (!config) {
+ pr_emerg("TXT: SEXIT failed to ioremap TXT private registers\n");
+ return;
+ }
+
+ /* Clear secrets bit for SEXIT */
+ memcpy_toio(config + TXT_CR_CMD_NO_SECRETS, &one, sizeof(one));
+ memcpy_fromio(&val, config + TXT_CR_E2STS, sizeof(val));
+
+ /* Unlock memory configurations */
+ memcpy_toio(config + TXT_CR_CMD_UNLOCK_MEM_CONFIG, &one, sizeof(one));
+ memcpy_fromio(&val, config + TXT_CR_E2STS, sizeof(val));
+
+ /* Close the TXT private register space */
+ memcpy_toio(config + TXT_CR_CMD_CLOSE_PRIVATE, &one, sizeof(one));
+ memcpy_fromio(&val, config + TXT_CR_E2STS, sizeof(val));
+
+ /*
+ * Calls to iounmap are skipped due to the system state this late in the
+ * kexec process. Local IRQs are disabled and iounmap causes a TLB flush
+ * which in turn causes a warning. Leaving these mappings is not an issue
+ * since the next kernel is going to completely re-setup memory management.
+ */
+
+ /* Map public registers and do a final read fence */
+ config = ioremap(TXT_PUB_CONFIG_REGS_BASE, TXT_NR_CONFIG_PAGES *
+ PAGE_SIZE);
+ if (!config) {
+ pr_emerg("TXT: SEXIT failed to ioremap TXT public registers\n");
+ return;
+ }
+
+ memcpy_fromio(&val, config + TXT_CR_E2STS, sizeof(val));
+
+ pr_emerg("TXT clear secrets bit and unlock memory complete.\n");
+
+ /*
+ * Mostly finalized but the system is still in SMX mode. At this point if the
+ * system has been quiesced, the APs are halted and the current process is
+ * running on the BSP, a final GETSEC(SEXIT) can be done exiting DRTM/SMX mode.
+ * This cannot be done on certain boot paths where the system has not been quiesced
+ * (e.g. where machine_shutdown() has not been called).
+ */
+ if (!do_sexit)
+ return;
+
+ if (smp_processor_id() != 0)
+ panic("TXT: SEXIT must be called on CPU 0\n");
+
+ /*
+ * In case SMX mode was disabled, enable it for SEXIT. Clearing the bit
+ * anytime during DRTM operation will not have an affect until the next
+ * GETSEC() op is performed.
+ */
+ cr4_set_bits(X86_CR4_SMXE);
+
+ /* Do the SEXIT SMX operation */
+ smx_getsec_sexit();
+
+ pr_info("TXT SEXIT complete.\n");
+}
diff --git a/kernel/kexec_core.c b/kernel/kexec_core.c
index 2fea396d29b9..dd284e5043ab 100644
--- a/kernel/kexec_core.c
+++ b/kernel/kexec_core.c
@@ -43,6 +43,7 @@
#include <linux/kmsg_dump.h>
#include <linux/dma-map-ops.h>
#include <linux/sysfs.h>
+#include <linux/slaunch.h>
#include <asm/page.h>
#include <asm/sections.h>
@@ -1201,6 +1202,13 @@ int kernel_kexec(void)
cpu_hotplug_enable();
pr_notice("Starting new kernel\n");
machine_shutdown();
+
+ /*
+ * If a Secure Launch is in progress and the current kernel is
+ * running as a DRTM with TXT, finalize the Secure Launch state
+ * and do the GETSEC(SEXIT) returning from SMX mode to do the KEXEC.
+ */
+ slaunch_finalize(1);
}
kmsg_dump(KMSG_DUMP_SHUTDOWN);
--
2.47.3
^ permalink raw reply related
* [PATCH v16 33/38] x86/slaunch: Secure Launch SMP bringup support
From: Ross Philipson @ 2026-05-15 21:14 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,
daniel.kiper, andrew.cooper3, trenchboot-devel
In-Reply-To: <20260515211410.31440-1-ross.philipson@gmail.com>
On Intel, the APs are left in a well documented state after TXT
performs the secure 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.
Co-developed-by: Daniel P. Smith <dpsmith@apertussolutions.com>
Signed-off-by: Daniel P. Smith <dpsmith@apertussolutions.com>
Signed-off-by: Ross Philipson <ross.philipson@gmail.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 0179ff855b62..f6e6f1b7e18c 100644
--- a/arch/x86/kernel/slaunch.c
+++ b/arch/x86/kernel/slaunch.c
@@ -511,3 +511,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 294a8ea60298..16a0f2718a38 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>
@@ -989,6 +990,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.
@@ -998,7 +1038,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 */
@@ -1043,12 +1083,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.47.3
^ permalink raw reply related
* [PATCH v16 32/38] x86/slaunch: Secure Launch kernel late boot initialization
From: Ross Philipson @ 2026-05-15 21:14 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,
daniel.kiper, andrew.cooper3, trenchboot-devel
In-Reply-To: <20260515211410.31440-1-ross.philipson@gmail.com>
The routine slaunch_setup() is called out of the x86 specific setup_arch()
routine during early kernel boot. After determining what platform is
present, various operations specific to that platform occur. This
includes finalizing setting for the platform late launch and verifying
that memory protections are in place. In addition this routine reserves
key memory regions used by Secure Launch (e.g. the TXT heap, AP startup
block etc) as well as fetching values needed later from the TXT heap
and SLRT.
Intel VT-d/IOMMU hardware provides special registers called Protected
Memory Regions (PMRs) that allow all memory to be protected from
DMA during a TXT DRTM launch. This coverage is validated during the
late setup process to ensure DMA protection is in place prior to
the IOMMUs being initialized and configured by the mainline kernel.
See the Intel Trusted Execution Technology - Measured Launch Environment
Developer's Guide for more details:
https://www.intel.com/content/dam/www/public/us/en/documents/guides/intel-txt-software-development-guide.pdf
Co-developed-by: Daniel P. Smith <dpsmith@apertussolutions.com>
Signed-off-by: Daniel P. Smith <dpsmith@apertussolutions.com>
Signed-off-by: Ross Philipson <ross.philipson@gmail.com>
---
arch/x86/kernel/Makefile | 1 +
arch/x86/kernel/setup.c | 3 +
arch/x86/kernel/slaunch.c | 513 +++++++++++++++++++++++++++++++++++++
drivers/iommu/intel/dmar.c | 4 +
4 files changed, 521 insertions(+)
create mode 100644 arch/x86/kernel/slaunch.c
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index 7e247064b7d0..bf2471701662 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -97,6 +97,7 @@ obj-$(CONFIG_X86_32) += tls.o
obj-$(CONFIG_IA32_EMULATION) += tls.o
obj-y += step.o
obj-$(CONFIG_INTEL_TXT) += tboot.o
+obj-$(CONFIG_SECURE_LAUNCH) += slaunch.o
obj-$(CONFIG_ISA_DMA_API) += i8237.o
obj-y += stacktrace.o
obj-y += cpu/
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index eebcc9db1a1b..82029d7640bf 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -25,6 +25,7 @@
#include <linux/sysfb.h>
#include <linux/swiotlb.h>
#include <linux/tboot.h>
+#include <linux/slaunch.h>
#include <linux/usb/xhci-dbgp.h>
#include <linux/vmalloc.h>
@@ -1027,6 +1028,8 @@ void __init setup_arch(char **cmdline_p)
early_gart_iommu_check();
#endif
+ slaunch_setup();
+
/*
* partially used pages are not usable - thus
* we are rounding upwards:
diff --git a/arch/x86/kernel/slaunch.c b/arch/x86/kernel/slaunch.c
new file mode 100644
index 000000000000..0179ff855b62
--- /dev/null
+++ b/arch/x86/kernel/slaunch.c
@@ -0,0 +1,513 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Secure Launch late validation/setup and finalization support.
+ *
+ * Copyright (c) 2026, Oracle and/or its affiliates.
+ * Copyright (c) 2026 Apertus Solutions, LLC
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#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 <asm/segment.h>
+#include <asm/sections.h>
+#include <asm/tlbflush.h>
+#include <asm/e820/api.h>
+#include <asm/setup.h>
+#include <asm/realmode.h>
+#include <linux/efi.h>
+#include <linux/slr_table.h>
+#include <linux/slaunch.h>
+
+static u32 sl_flags __ro_after_init;
+static struct sl_ap_wake_info ap_wake_info __ro_after_init;
+static u64 evtlog_addr __ro_after_init;
+static u32 evtlog_size __ro_after_init;
+static u64 vtd_pmr_lo_size __ro_after_init;
+
+/* This should be plenty of room */
+static u8 txt_dmar[PAGE_SIZE] __aligned(16);
+
+/*
+ * Get the Secure Launch flags that indicate what kind of launch is being done.
+ * E.g. a TXT launch is in progress or no Secure Launch is happening.
+ */
+u32 slaunch_get_flags(void)
+{
+ return sl_flags;
+}
+
+/*
+ * Return the AP wakeup information used in the SMP boot code to start up
+ * the APs that are parked using MONITOR/MWAIT.
+ */
+struct sl_ap_wake_info *slaunch_get_ap_wake_info(void)
+{
+ return &ap_wake_info;
+}
+
+/* Saved TXT heap map accessors for easy heap parsing */
+struct sl_txt_heap_info *__pi_sl_txt_get_heap_map(void);
+
+struct sl_txt_heap_info *__init slaunch_txt_get_heap_map(void)
+{
+ return __pi_sl_txt_get_heap_map();
+}
+
+void *__pi_sl_txt_get_heap_table(void *heap, u8 index);
+
+void *__init slaunch_txt_get_heap_table(void *heap, u8 index)
+{
+ return __pi_sl_txt_get_heap_table(heap, index);
+}
+
+/*
+ * On Intel platforms, TXT passes a safe copy of the DMAR ACPI table to the
+ * DRTM. The DRTM is supposed to use this instead of the one found in the
+ * ACPI tables.
+ */
+struct acpi_table_header *slaunch_get_dmar_table(struct acpi_table_header *dmar)
+{
+ /* The DMAR is only stashed and provided via TXT on Intel systems */
+ if (memcmp(txt_dmar, "DMAR", 4))
+ return dmar;
+
+ return (struct acpi_table_header *)(txt_dmar);
+}
+
+/*
+ * If running within a TXT established DRTM, this is the proper way to reset
+ * the system if a failure occurs or a security issue is found.
+ */
+static void __noreturn slaunch_txt_reset(void __iomem *txt, const char *msg, u64 error)
+{
+ u64 one = 1, val;
+
+ pr_err("%s", msg);
+
+ /*
+ * This performs a TXT reset with a sticky error code. The reads of
+ * TXT_CR_E2STS act as barriers.
+ */
+ memcpy_toio(txt + TXT_CR_ERRORCODE, &error, sizeof(error));
+ memcpy_fromio(&val, txt + TXT_CR_E2STS, sizeof(val));
+ memcpy_toio(txt + TXT_CR_CMD_NO_SECRETS, &one, sizeof(one));
+ memcpy_fromio(&val, txt + TXT_CR_E2STS, sizeof(val));
+ memcpy_toio(txt + TXT_CR_CMD_UNLOCK_MEM_CONFIG, &one, sizeof(one));
+ memcpy_fromio(&val, txt + TXT_CR_E2STS, sizeof(val));
+ memcpy_toio(txt + TXT_CR_CMD_RESET, &one, sizeof(one));
+
+ for ( ; ; )
+ asm volatile ("hlt");
+
+ unreachable();
+}
+
+/*
+ * Handle fatal errors during DRTM initialization.
+ */
+void __noreturn slaunch_reset(void *ctx, const char *msg, u64 error)
+{
+ if (slaunch_is_txt_launch())
+ slaunch_txt_reset(ctx, msg, error);
+
+ /* Generic handler for x86 */
+ pr_err("Secure Launch: %s - error: 0x%llx", msg, error);
+ asm volatile ("ud2");
+
+ unreachable();
+}
+
+/*
+ * The TXT heap is too big to map all at once with early_ioremap
+ * so it is done a table at a time.
+ */
+static void __init *txt_early_get_heap_table(void __iomem *txt, u32 type,
+ u32 bytes)
+{
+ struct sl_txt_heap_info *heap_map;
+ void *heap;
+ u64 base;
+
+ if (type >= TXT_SINIT_TABLE_MAX)
+ slaunch_reset(txt, "Error invalid table type for early heap walk\n",
+ SL_ERROR_HEAP_WALK);
+
+ memcpy_fromio(&base, txt + TXT_CR_HEAP_BASE, sizeof(base));
+
+ heap_map = slaunch_txt_get_heap_map();
+ base += heap_map[type].offset;
+
+ heap = early_memremap(base, bytes);
+ if (!heap)
+ slaunch_reset(txt, "Error early_memremap of heap section\n",
+ SL_ERROR_HEAP_MAP);
+
+ return heap;
+}
+
+static void __init txt_early_put_heap_table(void *addr, unsigned long size)
+{
+ early_memunmap(addr, size);
+}
+
+/*
+ * TXT uses a special set of VTd registers to protect all of memory from DMA
+ * until the IOMMU can be programmed to protect memory. There is the low
+ * memory PMR that can protect all memory up to 4G. The high memory PMR can
+ * be setup to protect all memory beyond 4Gb. Validate that these values cover
+ * what is expected.
+ */
+static void __init slaunch_verify_pmrs(void __iomem *txt)
+{
+ struct txt_os_sinit_data *os_sinit_data;
+ u32 field_offset, err = 0;
+ const char *errmsg = "";
+ unsigned long last_pfn;
+
+ field_offset = offsetof(struct txt_os_sinit_data, lcp_po_base);
+ os_sinit_data = txt_early_get_heap_table(txt, TXT_OS_SINIT_DATA_TABLE,
+ field_offset);
+
+ /* Save a copy */
+ vtd_pmr_lo_size = os_sinit_data->vtd_pmr_lo_size;
+
+ last_pfn = e820__end_of_ram_pfn();
+
+ /*
+ * First make sure the hi PMR covers all memory above 4G. In the
+ * unlikely case where there is < 4G on the system, the hi PMR will
+ * not be set.
+ */
+ if (os_sinit_data->vtd_pmr_hi_base != 0x0ULL) {
+ if (os_sinit_data->vtd_pmr_hi_base != 0x100000000ULL) {
+ err = SL_ERROR_HI_PMR_BASE;
+ errmsg = "Error hi PMR base\n";
+ goto out;
+ }
+
+ if (PFN_PHYS(last_pfn) > os_sinit_data->vtd_pmr_hi_base +
+ os_sinit_data->vtd_pmr_hi_size) {
+ err = SL_ERROR_HI_PMR_SIZE;
+ errmsg = "Error hi PMR size\n";
+ goto out;
+ }
+ }
+
+ /*
+ * Lo PMR base should always be 0. This was already checked in
+ * early stub.
+ */
+
+ /*
+ * Check that if the kernel was loaded below 4G, that it is protected
+ * by the lo PMR. Note this is the decompressed kernel. The ACM would
+ * have ensured the compressed kernel (the MLE image) was protected.
+ */
+ if (__pa_symbol(_end) < 0x100000000ULL &&
+ __pa_symbol(_end) > os_sinit_data->vtd_pmr_lo_size) {
+ err = SL_ERROR_LO_PMR_MLE;
+ errmsg = "Error lo PMR does not cover MLE kernel\n";
+ }
+
+ /*
+ * Other regions of interest like boot param, AP wake block, cmdline
+ * already checked for PMR coverage in the early stub code.
+ */
+
+out:
+ txt_early_put_heap_table(os_sinit_data, field_offset);
+
+ if (err)
+ slaunch_reset(txt, errmsg, err);
+}
+
+static void __init slaunch_txt_reserve_range(u64 base, u64 size)
+{
+ int type;
+
+ type = e820__get_entry_type(base, base + size - 1);
+ if (type == E820_TYPE_RAM) {
+ pr_info("memblock reserve base: %llx size: %llx\n", base, size);
+ memblock_reserve(base, size);
+ }
+}
+
+/*
+ * For Intel, certain regions of memory must be marked as reserved by putting
+ * them on the memblock reserved list if they are not already e820 reserved.
+ * This includes:
+ * - The TXT heap
+ * - The ACM area
+ * - The TXT private register bank
+ * - The MDR list sent to the MLE by the ACM (see TXT specification)
+ * (Normally the above are properly reserved by firmware but if it was not
+ * done, reserve them now)
+ * - The AP wake block
+ * - TPM log external to the TXT heap
+ *
+ * Also if the low PMR doesn't cover all memory < 4G, any RAM regions above
+ * the low PMR must be reserved too.
+ */
+static void __init slaunch_txt_reserve(void __iomem *txt)
+{
+ struct txt_sinit_memory_descriptor_record *mdr;
+ struct txt_sinit_mle_data *sinit_mle_data;
+ u64 base, size, heap_base, heap_size;
+ u32 mdrnum, mdroffset, mdrslen;
+ u32 field_offset, i;
+ void *mdrs;
+
+ base = TXT_PRIV_CONFIG_REGS_BASE;
+ size = TXT_PUB_CONFIG_REGS_BASE - TXT_PRIV_CONFIG_REGS_BASE;
+ slaunch_txt_reserve_range(base, size);
+
+ memcpy_fromio(&heap_base, txt + TXT_CR_HEAP_BASE, sizeof(heap_base));
+ memcpy_fromio(&heap_size, txt + TXT_CR_HEAP_SIZE, sizeof(heap_size));
+ slaunch_txt_reserve_range(heap_base, heap_size);
+
+ memcpy_fromio(&base, txt + TXT_CR_SINIT_BASE, sizeof(base));
+ memcpy_fromio(&size, txt + TXT_CR_SINIT_SIZE, sizeof(size));
+ slaunch_txt_reserve_range(base, size);
+
+ field_offset = offsetof(struct txt_sinit_mle_data,
+ sinit_vtd_dmar_table_size);
+ sinit_mle_data = txt_early_get_heap_table(txt, TXT_SINIT_MLE_DATA_TABLE,
+ field_offset);
+
+ mdrnum = sinit_mle_data->num_of_sinit_mdrs;
+ mdroffset = sinit_mle_data->sinit_mdrs_table_offset;
+
+ txt_early_put_heap_table(sinit_mle_data, field_offset);
+
+ if (!mdrnum)
+ goto nomdr;
+
+ mdrslen = mdrnum * sizeof(*mdr);
+
+ mdrs = txt_early_get_heap_table(txt, TXT_SINIT_MLE_DATA_TABLE,
+ mdroffset + mdrslen - 8);
+
+ mdr = mdrs + mdroffset - 8;
+
+ for (i = 0; i < mdrnum; i++, mdr++) {
+ /* Spec says some entries can have length 0, ignore them */
+ if (mdr->type > 0 && mdr->length > 0)
+ slaunch_txt_reserve_range(mdr->address, mdr->length);
+ }
+
+ txt_early_put_heap_table(mdrs, mdroffset + mdrslen - 8);
+
+nomdr:
+ slaunch_txt_reserve_range(ap_wake_info.ap_wake_block,
+ ap_wake_info.ap_wake_block_size);
+
+ /*
+ * Earlier checks ensured that the event log was properly situated
+ * either inside the TXT heap or outside. This is a check to see if the
+ * event log needs to be reserved. If it is in the TXT heap, it is
+ * already reserved.
+ */
+ if (evtlog_addr < heap_base || evtlog_addr > (heap_base + heap_size))
+ slaunch_txt_reserve_range(evtlog_addr, evtlog_size);
+
+ for (i = 0; i < e820_table->nr_entries; i++) {
+ base = e820_table->entries[i].addr;
+ size = e820_table->entries[i].size;
+ if (base >= vtd_pmr_lo_size && base < 0x100000000ULL)
+ slaunch_txt_reserve_range(base, size);
+ else if (base < vtd_pmr_lo_size && base + size > vtd_pmr_lo_size)
+ slaunch_txt_reserve_range(vtd_pmr_lo_size,
+ base + size - vtd_pmr_lo_size);
+ }
+}
+
+/*
+ * TXT stashes a safe copy of the DMAR ACPI table to prevent tampering.
+ * It is stored in the TXT heap. Fetch it from there and make it available
+ * to the IOMMU driver.
+ */
+static void __init slaunch_copy_dmar_table(void __iomem *txt)
+{
+ struct txt_sinit_mle_data *sinit_mle_data;
+ u32 field_offset, dmar_size, dmar_offset;
+ void *dmar;
+
+ field_offset = offsetof(struct txt_sinit_mle_data,
+ processor_scrtm_status);
+ sinit_mle_data = txt_early_get_heap_table(txt, TXT_SINIT_MLE_DATA_TABLE,
+ field_offset);
+
+ dmar_size = sinit_mle_data->sinit_vtd_dmar_table_size;
+ dmar_offset = sinit_mle_data->sinit_vtd_dmar_table_offset;
+
+ txt_early_put_heap_table(sinit_mle_data, field_offset);
+
+ if (!dmar_size || !dmar_offset)
+ slaunch_reset(txt, "Error DMAR table values\n", SL_ERROR_HEAP_INVALID_DMAR);
+
+ if (unlikely(dmar_size > PAGE_SIZE))
+ slaunch_reset(txt, "Error DMAR too big to store\n", SL_ERROR_HEAP_DMAR_SIZE);
+
+ dmar = txt_early_get_heap_table(txt, TXT_SINIT_MLE_DATA_TABLE,
+ dmar_offset + dmar_size - 8);
+ if (!dmar)
+ slaunch_reset(txt, "Error early_ioremap of DMAR\n", SL_ERROR_HEAP_DMAR_MAP);
+
+ memcpy(txt_dmar, dmar + dmar_offset - 8, dmar_size);
+
+ txt_early_put_heap_table(dmar, dmar_offset + dmar_size - 8);
+}
+
+/*
+ * The location of the safe AP wake code block is stored in the TXT heap.
+ * Fetch needed values here in the early init code for later use in SMP
+ * startup.
+ *
+ * Also the TPM event log values are in the SLRT and have to be fetched.
+ * They will be put on the memblock reserve list later.
+ */
+static void __init slaunch_fetch_values(void __iomem *txt)
+{
+ struct txt_os_mle_data *os_mle_data;
+ struct slr_entry_log_info *log_info;
+ u8 *jmp_offset, *stacks_offset;
+ struct slr_table *slrt;
+ u32 size;
+
+ os_mle_data = txt_early_get_heap_table(txt, TXT_OS_MLE_DATA_TABLE,
+ sizeof(*os_mle_data));
+
+ ap_wake_info.ap_wake_block = os_mle_data->ap_wake_block;
+ ap_wake_info.ap_wake_block_size = os_mle_data->ap_wake_block_size;
+
+ jmp_offset = os_mle_data->mle_scratch + SL_SCRATCH_AP_JMP_OFFSET;
+ ap_wake_info.ap_jmp_offset = *((u32 *)jmp_offset);
+
+ stacks_offset = os_mle_data->mle_scratch + SL_SCRATCH_AP_STACKS_OFFSET;
+ ap_wake_info.ap_stacks_offset = *((u32 *)stacks_offset);
+
+ slrt = (struct slr_table *)early_memremap(os_mle_data->slrt, sizeof(*slrt));
+ if (!slrt)
+ slaunch_reset(txt, "Error early_memremap of SLRT failed\n",
+ SL_ERROR_SLRT_MAP);
+
+ size = slrt->size;
+ early_memunmap(slrt, sizeof(*slrt));
+
+ slrt = (struct slr_table *)early_memremap(os_mle_data->slrt, size);
+ if (!slrt)
+ slaunch_reset(txt, "Error early_memremap of SLRT failed\n",
+ SL_ERROR_SLRT_MAP);
+
+ log_info = slr_next_entry_by_tag(slrt, NULL, SLR_ENTRY_LOG_INFO);
+
+ if (!log_info)
+ slaunch_reset(txt, "SLRT missing logging info entry\n",
+ SL_ERROR_SLRT_MISSING_ENTRY);
+
+ evtlog_addr = log_info->addr;
+ evtlog_size = log_info->size;
+
+ early_memunmap(slrt, size);
+
+ txt_early_put_heap_table(os_mle_data, sizeof(*os_mle_data));
+}
+
+/*
+ * Intel TXT specific late stub setup and validation called from within
+ * x86 specific setup_arch().
+ */
+static void __init slaunch_setup_txt(void)
+{
+ u64 one = TXT_REGVALUE_ONE, val;
+ void __iomem *txt;
+
+ /*
+ * See if SENTER was done by reading the status register in the
+ * public space. If the public register space cannot be read, TXT may
+ * be disabled.
+ */
+ txt = early_ioremap(TXT_PUB_CONFIG_REGS_BASE,
+ TXT_NR_CONFIG_PAGES * PAGE_SIZE);
+ if (!txt)
+ panic("Error early_ioremap in TXT setup failed\n");
+
+ memcpy_fromio(&val, txt + TXT_CR_STS, sizeof(val));
+ early_iounmap(txt, TXT_NR_CONFIG_PAGES * PAGE_SIZE);
+
+ /* SENTER should have been done */
+ if (!(val & TXT_SENTER_DONE_STS))
+ panic("Error TXT.STS SENTER_DONE not set\n");
+
+ /* SEXIT should have been cleared */
+ if (val & TXT_SEXIT_DONE_STS)
+ panic("Error TXT.STS SEXIT_DONE set\n");
+
+ /* Now we want to use the private register space */
+ txt = early_ioremap(TXT_PRIV_CONFIG_REGS_BASE,
+ TXT_NR_CONFIG_PAGES * PAGE_SIZE);
+ if (!txt) {
+ /* This is really bad, no where to go from here */
+ panic("Error early_ioremap of TXT priv registers\n");
+ }
+
+ /*
+ * Try to read the Intel VID from the TXT private registers to see if
+ * TXT measured launch happened properly and the private space is
+ * available.
+ */
+ memcpy_fromio(&val, txt + TXT_CR_DIDVID, sizeof(val));
+ if ((val & 0xffff) != 0x8086) {
+ /*
+ * Can't do a proper TXT reset since it appears something is
+ * wrong even though SENTER happened and it should be in SMX
+ * mode.
+ */
+ panic("Invalid TXT vendor ID, not in SMX mode\n");
+ }
+
+ /* Set flags so subsequent code knows the status of the launch */
+ sl_flags |= (SL_FLAG_ACTIVE | SL_FLAG_ARCH_TXT);
+
+ /*
+ * Reading the proper DIDVID from the private register space means we
+ * are in SMX mode and private registers are open for read/write.
+ */
+
+ /* On Intel, have to handle TPM localities via TXT */
+ memcpy_toio(txt + TXT_CR_CMD_SECRETS, &one, sizeof(one));
+ memcpy_fromio(&val, txt + TXT_CR_E2STS, sizeof(val));
+ memcpy_toio(txt + TXT_CR_CMD_OPEN_LOCALITY1, &one, sizeof(one));
+ memcpy_fromio(&val, txt + TXT_CR_E2STS, sizeof(val));
+
+ slaunch_fetch_values(txt);
+
+ slaunch_verify_pmrs(txt);
+
+ slaunch_txt_reserve(txt);
+
+ slaunch_copy_dmar_table(txt);
+
+ early_iounmap(txt, TXT_NR_CONFIG_PAGES * PAGE_SIZE);
+
+ pr_info("Intel TXT setup complete\n");
+}
+
+void __init slaunch_setup(void)
+{
+ /*
+ * If booted through secure launch entry point, the loadflags
+ * option will be set.
+ */
+ if (!(boot_params.hdr.loadflags & SLAUNCH_FLAG))
+ return;
+
+ if (boot_cpu_has(X86_FEATURE_SMX))
+ slaunch_setup_txt();
+}
diff --git a/drivers/iommu/intel/dmar.c b/drivers/iommu/intel/dmar.c
index 69222dbd2af0..f41b4f702565 100644
--- a/drivers/iommu/intel/dmar.c
+++ b/drivers/iommu/intel/dmar.c
@@ -28,6 +28,7 @@
#include <linux/iommu.h>
#include <linux/numa.h>
#include <linux/limits.h>
+#include <linux/slaunch.h>
#include <asm/irq_remapping.h>
#include "iommu.h"
@@ -661,6 +662,9 @@ parse_dmar_table(void)
*/
dmar_tbl = tboot_get_dmar_table(dmar_tbl);
+ /* If Secure Launch is active, it has similar logic */
+ dmar_tbl = slaunch_get_dmar_table(dmar_tbl);
+
dmar = (struct acpi_table_dmar *)dmar_tbl;
if (!dmar)
return -ENODEV;
--
2.47.3
^ permalink raw reply related
* [PATCH v16 31/38] x86/slaunch: Secure Launch kernel early boot initialization
From: Ross Philipson @ 2026-05-15 21:14 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,
daniel.kiper, andrew.cooper3, trenchboot-devel
In-Reply-To: <20260515211410.31440-1-ross.philipson@gmail.com>
The sl_main() routine is responsible for measuring configuration and module
information before it is used by the kernel. 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.
Co-developed-by: Daniel P. Smith <dpsmith@apertussolutions.com>
Signed-off-by: Daniel P. Smith <dpsmith@apertussolutions.com>
Signed-off-by: Ross Philipson <ross.philipson@gmail.com>
---
arch/x86/boot/startup/sl_main.c | 610 ++++++++++++++++++++++++++++++++
1 file changed, 610 insertions(+)
diff --git a/arch/x86/boot/startup/sl_main.c b/arch/x86/boot/startup/sl_main.c
index 1982cfb461dd..0110392470d0 100644
--- a/arch/x86/boot/startup/sl_main.c
+++ b/arch/x86/boot/startup/sl_main.c
@@ -15,14 +15,624 @@
#include <linux/efi.h>
#include <linux/slr_table.h>
#include <linux/slaunch.h>
+#include <linux/tpm_ptp.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 __initdata;
+
+static struct slr_table *slrt __initdata;
+
u32 sl_cpu_type __initdata;
u32 sl_mle_start __initdata;
void sl_main(void *bootparams);
+static void *txt_regs = (void *)TXT_PRIV_CONFIG_REGS_BASE;
+#define sl_txt_read(r) readq(txt_regs + r)
+#define sl_txt_write(r, v) writeq(v, txt_regs + r)
+
+static void *txt_heap __initdata;
+static struct sl_txt_heap_info txt_heap_map[TXT_SINIT_TABLE_MAX] __initdata;
+
+struct sl_txt_heap_info * __init sl_txt_get_heap_map(void);
+void * __init sl_txt_get_heap_table(void *heap, u8 index);
+
+struct sl_txt_heap_info * __init sl_txt_get_heap_map(void)
+{
+ return txt_heap_map;
+}
+
+void * __init sl_txt_get_heap_table(void *heap, u8 index)
+{
+ return heap + txt_heap_map[index].offset;
+}
+
+static void __init txt_parse_heap_map(void *heap)
+{
+ void *tmp = heap;
+
+ for (u8 i = 0; i < TXT_SINIT_TABLE_MAX; i++) {
+ txt_heap_map[i].size = *((u64 *) tmp);
+ txt_heap_map[i].offset = tmp - heap + sizeof(txt_heap_map[i].size);
+ tmp += txt_heap_map[i].size;
+ }
+}
+
+void __warn_printk(const char *fmt, ...)
+{
+}
+
+static void __noreturn __init 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 __init sl_rdmsr(u32 reg)
+{
+ struct msr m;
+
+ raw_rdmsr(reg, &m);
+
+ return m.q;
+}
+
+static struct slr_table *__init sl_locate_and_validate_slrt(void)
+{
+ struct txt_os_mle_data *os_mle_data;
+ struct slr_table *slrt;
+
+ os_mle_data = sl_txt_get_heap_table(txt_heap, TXT_OS_MLE_DATA_TABLE);
+
+ 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 __init sl_check_pmr_coverage(void *base, u32 size, bool allow_hi)
+{
+ struct txt_os_sinit_data *os_sinit_data;
+ void *end = base + size;
+
+ if (!(sl_cpu_type & SL_CPU_INTEL))
+ return;
+
+ os_sinit_data = sl_txt_get_heap_table(txt_heap, TXT_OS_SINIT_DATA_TABLE);
+
+ 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 __init 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 __init sl_find_drtm_event_log(struct slr_table *slrt)
+{
+ struct txt_os_sinit_data *os_sinit_data;
+ struct slr_entry_log_info *log_info;
+
+ 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;
+
+ /*
+ * 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 = sl_txt_get_heap_table(txt_heap, TXT_OS_SINIT_DATA_TABLE);
+
+ /*
+ * 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 __init sl_validate_event_log_buffer(void)
+{
+ struct txt_os_sinit_data *os_sinit_data;
+ void *mle_base, *mle_end;
+ void *evtlog_end;
+ void *txt_end;
+
+ if ((u64)evtlog_size > (LLONG_MAX - (u64)evtlog_base))
+ sl_txt_reset(SL_ERROR_INTEGER_OVERFLOW);
+ evtlog_end = evtlog_base + evtlog_size;
+
+ txt_end = txt_heap + sl_txt_read(TXT_CR_HEAP_SIZE);
+ os_sinit_data = sl_txt_get_heap_table(txt_heap, TXT_OS_SINIT_DATA_TABLE);
+
+ 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 __init 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 __init 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 __init sl_tpm2_extend(u32 pcr, u32 event_type,
+ const u8 *data, u32 length,
+ const u8 *event_data, u32 event_size)
+{
+ 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(data, length, &digest[0]);
+ } else if (tpm_algs[alg_idx].alg_id == TPM_ALG_SHA384) {
+ sha384(data, length, &digest[0]);
+ } else if (tpm_algs[alg_idx].alg_id == TPM_ALG_SHA512) {
+ sha512(data, length, &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 __init 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 void __init sl_handle_setup_data(struct setup_data *curr,
+ struct slr_policy_entry *entry)
+{
+ struct setup_indirect *ind;
+
+ /* 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);
+ }
+
+ 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);
+}
+
+/*
+ * 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 __init sl_extend_setup_data(struct slr_policy_entry *entry)
+{
+ struct setup_data *data = (void *)(unsigned long)entry->entity;
+
+ /*
+ * 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.
+ */
+ while (data) {
+ sl_handle_setup_data(data, entry);
+ data = (void *)(unsigned long)data->next;
+ }
+}
+
+static void __init 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 __init sl_extend_txt_os2mle(struct slr_policy_entry *entry)
+{
+ struct txt_os_mle_data *os_mle_data;
+
+ os_mle_data = sl_txt_get_heap_table(txt_heap, TXT_OS_MLE_DATA_TABLE);
+
+ /*
+ * 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 __init 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 __init 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 __init void sl_main(void *bootparams)
{
+ struct boot_params *bp = (struct boot_params *)bootparams;
+ struct txt_os_mle_data *os_mle_data;
+
+ /*
+ * 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;
+
+ txt_heap = (void *)sl_txt_read(TXT_CR_HEAP_BASE);
+ txt_parse_heap_map(txt_heap);
+
+ /* 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);
+
+ /*
+ * Set the SLAUNCH_FLAG early since if anything fails, the system will
+ * reset anyway.
+ */
+ 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 */
+ os_mle_data = sl_txt_get_heap_table(txt_heap, TXT_OS_MLE_DATA_TABLE);
+
+ /*
+ * 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);
}
--
2.47.3
^ permalink raw reply related
* [PATCH v16 30/38] x86/slaunch: Add MLE header and Secure Launch entrypoint to the core kernel
From: Ross Philipson @ 2026-05-15 21:14 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,
daniel.kiper, andrew.cooper3, trenchboot-devel
In-Reply-To: <20260515211410.31440-1-ross.philipson@gmail.com>
The Measured Launch Environment (MLE) header is an Intel TXT specific
structure that is used by the Intel ACM and Secure Launch implementation
to determine the location and attributes of the secure kernel being
launched.
Also introduce the sl_stub.S code to create a 32-bit Secure Launch entry
point into the core kernel and expose it via an MLE header. This is the
entry point for starting a Secure Launch kernel, handling the
post-launch CPU states and validating the environment.
Co-developed-by: Ard Biesheuvel <ardb@kernel.org>
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Co-developed-by: Daniel P. Smith <dpsmith@apertussolutions.com>
Signed-off-by: Daniel P. Smith <dpsmith@apertussolutions.com>
Signed-off-by: Ross Philipson <ross.philipson@gmail.com>
---
arch/x86/boot/compressed/Makefile | 2 +-
arch/x86/boot/compressed/misc.c | 4 +
arch/x86/boot/startup/Makefile | 1 +
arch/x86/boot/startup/sl_main.c | 28 +
arch/x86/include/asm/boot.h | 4 +
arch/x86/include/uapi/asm/bootparam.h | 1 +
arch/x86/kernel/Makefile | 1 +
arch/x86/kernel/asm-offsets.c | 22 +
arch/x86/kernel/sl_stub.S | 847 ++++++++++++++++++++++++++
arch/x86/kernel/vmlinux.lds.S | 5 +
arch/x86/tools/relocs.c | 1 +
11 files changed, 915 insertions(+), 1 deletion(-)
create mode 100644 arch/x86/boot/startup/sl_main.c
create mode 100644 arch/x86/kernel/sl_stub.S
diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
index b8b2b7bea1d3..8b2e234d18cb 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -76,7 +76,7 @@ LDFLAGS_vmlinux += -T
hostprogs := mkpiggy
HOST_EXTRACFLAGS += -I$(srctree)/tools/include
-sed-voffset := -e 's/^\([0-9a-fA-F]*\) [ABbCDGRSTtVW] \(_text\|__start_rodata\|_sinittext\|__inittext_end\|__bss_start\|_end\)$$/\#define VO_\2 _AC(0x\1,UL)/p'
+sed-voffset := -e 's/^\([0-9a-fA-F]*\) [ABbCDGRSTtVW] \(_text\|__start_rodata\|_sinittext\|mle_header\|__inittext_end\|__bss_start\|_end\)$$/\#define VO_\2 _AC(0x\1,UL)/p'
quiet_cmd_voffset = VOFFSET $@
cmd_voffset = $(NM) $< | sed -n $(sed-voffset) > $@
diff --git a/arch/x86/boot/compressed/misc.c b/arch/x86/boot/compressed/misc.c
index 0f41ca0e52c0..e3b5177bfa6f 100644
--- a/arch/x86/boot/compressed/misc.c
+++ b/arch/x86/boot/compressed/misc.c
@@ -336,6 +336,10 @@ const unsigned long kernel_inittext_offset = VO__sinittext - VO__text;
const unsigned long kernel_inittext_size = VO___inittext_end - VO__sinittext;
const unsigned long kernel_total_size = VO__end - VO__text;
+#ifdef CONFIG_SECURE_LAUNCH
+const unsigned long mle_header_offset = VO_mle_header - VO__text;
+#endif
+
static u8 boot_heap[BOOT_HEAP_SIZE] __aligned(4);
extern unsigned char input_data[];
diff --git a/arch/x86/boot/startup/Makefile b/arch/x86/boot/startup/Makefile
index ecf86ce5ebf7..c4b150a0253b 100644
--- a/arch/x86/boot/startup/Makefile
+++ b/arch/x86/boot/startup/Makefile
@@ -25,6 +25,7 @@ slaunch-objs += lib-sha1.o
slaunch-objs += lib-sha256.o
slaunch-objs += lib-sha512.o
slaunch-objs += tpm_drv.o
+slaunch-objs += sl_main.o
obj-$(CONFIG_SECURE_LAUNCH) += $(slaunch-objs)
pi-objs := $(patsubst %.o,$(obj)/%.o,$(obj-y))
diff --git a/arch/x86/boot/startup/sl_main.c b/arch/x86/boot/startup/sl_main.c
new file mode 100644
index 000000000000..1982cfb461dd
--- /dev/null
+++ b/arch/x86/boot/startup/sl_main.c
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Secure Launch early measurement and validation routines.
+ *
+ * Copyright (c) 2026, Oracle and/or its affiliates.
+ * Copyright (c) 2026 Apertus Solutions, LLC
+ */
+
+#include <asm/msr.h>
+#include <asm/mtrr.h>
+#include <asm/processor-flags.h>
+#include <asm/asm-offsets.h>
+#include <asm/bootparam.h>
+#include <asm/shared/msr.h>
+#include <linux/efi.h>
+#include <linux/slr_table.h>
+#include <linux/slaunch.h>
+
+#include "tpm.h"
+
+u32 sl_cpu_type __initdata;
+u32 sl_mle_start __initdata;
+
+void sl_main(void *bootparams);
+
+asmlinkage __visible __init void sl_main(void *bootparams)
+{
+}
diff --git a/arch/x86/include/asm/boot.h b/arch/x86/include/asm/boot.h
index f7b67cb73915..84e87e0d3d82 100644
--- a/arch/x86/include/asm/boot.h
+++ b/arch/x86/include/asm/boot.h
@@ -86,6 +86,10 @@ extern const unsigned long kernel_inittext_offset;
extern const unsigned long kernel_inittext_size;
extern const unsigned long kernel_total_size;
+#ifdef CONFIG_SECURE_LAUNCH
+extern const unsigned long mle_header_offset;
+#endif
+
unsigned long decompress_kernel(unsigned char *outbuf, unsigned long virt_addr,
void (*error)(char *x));
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/Makefile b/arch/x86/kernel/Makefile
index 47a32f583930..7e247064b7d0 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -89,6 +89,7 @@ obj-y += resource.o
obj-y += irqflags.o
obj-y += static_call.o
+obj-$(CONFIG_SECURE_LAUNCH) += sl_stub.o
obj-y += process.o
obj-y += fpu/
obj-y += ptrace.o
diff --git a/arch/x86/kernel/asm-offsets.c b/arch/x86/kernel/asm-offsets.c
index 081816888f7a..684e6552d973 100644
--- a/arch/x86/kernel/asm-offsets.c
+++ b/arch/x86/kernel/asm-offsets.c
@@ -13,6 +13,9 @@
#include <linux/hardirq.h>
#include <linux/suspend.h>
#include <linux/kbuild.h>
+#include <linux/efi.h>
+#include <linux/slr_table.h>
+#include <linux/slaunch.h>
#include <asm/processor.h>
#include <asm/thread_info.h>
#include <asm/sigframe.h>
@@ -133,4 +136,23 @@ static void __used common(void)
BLANK();
DEFINE(ALT_INSTR_SIZE, sizeof(struct alt_instr));
DEFINE(EXTABLE_SIZE, sizeof(struct exception_table_entry));
+
+#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
}
diff --git a/arch/x86/kernel/sl_stub.S b/arch/x86/kernel/sl_stub.S
new file mode 100644
index 000000000000..5121de563310
--- /dev/null
+++ b/arch/x86/kernel/sl_stub.S
@@ -0,0 +1,847 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Secure Launch protected mode entry point.
+ *
+ * Copyright (c) 2026, Oracle and/or its affiliates.
+ * Copyright (c) 2026 Assured Information Security, Inc.
+ */
+
+#include <linux/linkage.h>
+#include <linux/init.h>
+#include <asm/segment.h>
+#include <asm/msr.h>
+#include <asm/apicdef.h>
+#include <asm/trapnr.h>
+#include <asm/pgtable_types.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 <asm/unwind_hints.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
+ __INIT
+SYM_CODE_START(sl_stub_entry)
+ UNWIND_HINT_END_OF_STACK
+ /*
+ * 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:
+ UNWIND_HINT_END_OF_STACK
+ /* Save our base pointer reg and page table for MLE */
+ pushl %ebx
+ movl %ecx, %ebp
+
+ /* See if SMX feature is supported. */
+ movl $1, %eax
+ cpuid
+ testl $(X86_FEATURE_BIT_SMX), %ecx
+ jz .Ldo_unknown_cpu
+
+ popl %ebx
+
+ /* Know it is Intel */
+ movl $(SL_CPU_INTEL), rva(__pi_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.
+ */
+
+ /* Set up 1:1 mapping using 1G mappings in the page tables in %EBP */
+ xorl %ecx, %ecx
+ movl $128, %edx
+1: leal (,%ecx,4), %eax
+ .irpc l, 0123
+ movl $(\l * PUD_SIZE) | _PAGE_PRESENT | _PAGE_RW | _PAGE_PSE, (\l * 8)(%ebp,%eax,8)
+ movl %ecx, (\l * 8 + 4)(%ebp,%eax,8)
+ .endr
+ incl %ecx
+ cmpl %edx, %ecx
+ jc 1b
+
+ leal (_PAGE_PRESENT | _PAGE_RW)(%ebp), %edx
+ addl $PAGE_SIZE, %ebp
+
+ xorl %eax, %eax
+ movl %ebp, %edi
+ movl $PAGE_SIZE / 4, %ecx
+ rep stosl
+
+ movl %edx, (%ebp)
+ movl %ebp, %cr3
+
+ /* Enable PAE */
+ movl %cr4, %eax
+ btsl $X86_CR4_PAE_BIT, %eax
+ movl %eax, %cr4
+
+ /* Enable long mode */
+ movl $MSR_EFER, %ecx
+ rdmsr
+ btsl $_EFER_LME, %eax
+ wrmsr
+
+ /* Set up long return to 64-bit mode */
+ leal rva(2f)(%ebx), %eax
+ pushl $__SL64_CS
+ pushl %eax
+
+ /* Enable paging */
+ movl $CR0_STATE, %eax
+ movl %eax, %cr0
+ lretl
+
+ .code64
+ UNWIND_HINT_END_OF_STACK
+2: andq $~0xf, %rsp
+ movq %rsi, %r15
+ movq %rsi, %rdi
+ callq __pi_sl_main
+ movq %r15, %rsi
+ jmp startup_64
+SYM_CODE_END(sl_stub_entry)
+
+ .code32
+SYM_FUNC_START_LOCAL(sl_find_mle_base)
+ /* %ecx has PDPT, get first PD */
+ movl (%ebp), %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(__pi_sl_mle_start)(%ebx)
+ RET
+SYM_FUNC_END(sl_find_mle_base)
+
+SYM_FUNC_START_LOCAL(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_LOCAL(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(__pi_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_CODE_START_LOCAL(sl_txt_ap_entry)
+ UNWIND_HINT_END_OF_STACK
+ /*
+ * 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:
+ UNWIND_HINT_END_OF_STACK
+ /* 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:
+ UNWIND_HINT_END_OF_STACK
+ /* 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
+ int3
+SYM_CODE_END(sl_txt_ap_entry)
+
+SYM_FUNC_START_LOCAL(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_LOCAL(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_LOCAL(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)
+
+ __INITDATA
+/* This is the beginning of the relocated AP wake code block */
+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_CODE_START_LOCAL(sl_txt_int_nmi)
+ /* NMI context, just IRET */
+ iret
+SYM_CODE_END(sl_txt_int_nmi)
+
+SYM_FUNC_START_LOCAL(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 */
+sl_txt_ap_wake_end:
+
+ .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 */
+ .quad 0x00af9a000000ffff /* __SL64_CS */
+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_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)
+
+ __INITRODATA
+ .balign 8
+ /*
+ * The MLE Header per the TXT Specification, section 2.1
+ * MLE capabilities, see table 4. Capabilities set:
+ * bit 0: Support for GETSEC[WAKEUP] for RLP wakeup
+ * bit 1: Support for RLP wakeup using MONITOR address
+ * bit 2: The ECX register will contain the pointer to the MLE page table
+ * bit 5: TPM 1.2 family: Details/authorities PCR usage support
+ * bit 9: Supported format of TPM 2.0 event log - TCG compliant
+ */
+SYM_DATA_START(mle_header)
+0: .long 0x9082ac5a /* UUID0 */
+ .long 0x74a7476f /* UUID1 */
+ .long 0xa2555c0f /* UUID2 */
+ .long 0x42b651cb /* UUID3 */
+ .long 0x00000034 /* MLE header size */
+ .long 0x00020002 /* MLE version 2.2 */
+ .long __sl_stub_entry_offset - 0b /* Linear entry point of MLE (virt. address) */
+ .long 0x00000000 /* First valid page of MLE */
+ .long 0x00000000 /* Offset within binary of first byte of MLE */
+ .long __sl_mle_end_offset - 0b /* Offset within binary of last byte + 1 of MLE */
+ .long 0x00000227 /* Bit vector of MLE-supported capabilities */
+ .long 0x00000000 /* Starting linear address of command line (unused) */
+ .long 0x00000000 /* Ending linear address of command line (unused) */
+SYM_DATA_END(mle_header)
diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S
index 4711a35e706c..fc22a2b9c7d1 100644
--- a/arch/x86/kernel/vmlinux.lds.S
+++ b/arch/x86/kernel/vmlinux.lds.S
@@ -537,4 +537,9 @@ xen_elfnote_phys32_entry_value =
ABSOLUTE(xen_elfnote_phys32_entry) + ABSOLUTE(pvh_start_xen - LOAD_OFFSET);
#endif
+#ifdef CONFIG_SECURE_LAUNCH
+__sl_stub_entry_offset = ABSOLUTE(mle_header) + ABSOLUTE(sl_stub_entry - _text);
+__sl_mle_end_offset = ABSOLUTE(mle_header) + ABSOLUTE(__bss_start - _text);
+#endif
+
#include "../boot/startup/exports.h"
diff --git a/arch/x86/tools/relocs.c b/arch/x86/tools/relocs.c
index e5a2b9a912d1..d4a5fc607c54 100644
--- a/arch/x86/tools/relocs.c
+++ b/arch/x86/tools/relocs.c
@@ -88,6 +88,7 @@ static const char * const sym_regex_kernel[S_NSYMTYPES] = {
"__end_rodata|"
"__end_rodata_aligned|"
"__initramfs_start|"
+ "__sl_.+offset|"
"(jiffies|jiffies_64)|"
#if ELF_BITS == 64
"__end_rodata_hpage_align|"
--
2.47.3
^ permalink raw reply related
* [PATCH v16 29/38] x86/tpm: Early startup TPM PCR extending driver
From: Ross Philipson @ 2026-05-15 21:14 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,
daniel.kiper, andrew.cooper3, trenchboot-devel
In-Reply-To: <20260515211410.31440-1-ross.philipson@gmail.com>
Introduce a driver that can interact minimally with the TPM. This
allows the Secure Launch startup code to extend measurement values
into the TPM's DRTM PCR banks early in the launch process.
The driver implementation only currently supports basic initialization
and PCR extend commands. An extend command can be sent to both TPM 2.0 or
1.2 chip but only the TIS/FIFO interface is currently supported. The TCG
specs for these interface can be found here:
https://trustedcomputinggroup.org/resource/pc-client-work-group-pc-client-specific-tpm-interface-specification-tis/
https://trustedcomputinggroup.org/resource/tpm-2-0-mobile-command-response-buffer-interface-specification/
Note this TPM driver implementation relies as much as possible on
existing mainline kernel TPM code.
Co-developed-by: Daniel P. Smith <dpsmith@apertussolutions.com>
Signed-off-by: Daniel P. Smith <dpsmith@apertussolutions.com>
Co-developed-by: Alec Brown <alec.r.brown@oracle.com>
Signed-off-by: Alec Brown <alec.r.brown@oracle.com>
Signed-off-by: Ross Philipson <ross.philipson@gmail.com>
---
arch/x86/boot/startup/Makefile | 1 +
arch/x86/boot/startup/exports.h | 7 +
arch/x86/boot/startup/tpm.h | 47 +++
arch/x86/boot/startup/tpm_drv.c | 567 ++++++++++++++++++++++++++++++++
4 files changed, 622 insertions(+)
create mode 100644 arch/x86/boot/startup/tpm.h
create mode 100644 arch/x86/boot/startup/tpm_drv.c
diff --git a/arch/x86/boot/startup/Makefile b/arch/x86/boot/startup/Makefile
index 527cba7e4560..ecf86ce5ebf7 100644
--- a/arch/x86/boot/startup/Makefile
+++ b/arch/x86/boot/startup/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_AMD_MEM_ENCRYPT) += sme.o sev-startup.o
slaunch-objs += lib-sha1.o
slaunch-objs += lib-sha256.o
slaunch-objs += lib-sha512.o
+slaunch-objs += tpm_drv.o
obj-$(CONFIG_SECURE_LAUNCH) += $(slaunch-objs)
pi-objs := $(patsubst %.o,$(obj)/%.o,$(obj-y))
diff --git a/arch/x86/boot/startup/exports.h b/arch/x86/boot/startup/exports.h
index 01d2363dc445..4b82ddbd62a8 100644
--- a/arch/x86/boot/startup/exports.h
+++ b/arch/x86/boot/startup/exports.h
@@ -12,3 +12,10 @@ PROVIDE(snp_cpuid = __pi_snp_cpuid);
PROVIDE(snp_cpuid_get_table = __pi_snp_cpuid_get_table);
PROVIDE(svsm_issue_call = __pi_svsm_issue_call);
PROVIDE(svsm_process_result_codes = __pi_svsm_process_result_codes);
+
+#ifdef CONFIG_SECURE_LAUNCH
+__pi_bcmp = bcmp;
+__pi_memcmp = memcmp;
+__pi_strlen = strlen;
+__pi___WARN_trap = __WARN_trap;
+#endif
diff --git a/arch/x86/boot/startup/tpm.h b/arch/x86/boot/startup/tpm.h
new file mode 100644
index 000000000000..1a11396b68c6
--- /dev/null
+++ b/arch/x86/boot/startup/tpm.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * TPM early extend header file.
+ *
+ * Copyright (c) 2026 Apertus Solutions, LLC
+ * Copyright (c) 2026, Oracle and/or its affiliates.
+ */
+
+#ifndef BOOT_COMPRESSED_TPM_H
+#define BOOT_COMPRESSED_TPM_H
+
+enum early_tis_defaults {
+ TPM_TIMEOUT = 5, /* ms */
+ TIS_DURATION = 120000, /* 120 secs in ms */
+};
+
+enum tpm_family {
+ TPM_FAMILY_INVALID = 0,
+ TPM_FAMILY_12 = 1,
+ TPM_FAMILY_20 = 2
+};
+
+struct tpm_chip {
+ enum tpm_family family;
+ u64 baseaddr;
+ int locality;
+ int did;
+ int vid;
+
+ /* in ms */
+ ktime_t timeout_a;
+ ktime_t timeout_b;
+ ktime_t timeout_c;
+ ktime_t timeout_d;
+};
+
+bool tpm_tis_check_locality(struct tpm_chip *chip, int loc);
+void tpm_tis_release_locality(struct tpm_chip *chip);
+int tpm_tis_request_locality(struct tpm_chip *chip, int loc);
+void tpm_tis_disable_interrupts(struct tpm_chip *chip);
+int tpm1_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash);
+int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
+ struct tpm_digest *digests, u32 digest_count);
+int early_tpm_init(struct tpm_chip *chip, u64 baseaddr);
+int early_tpm_fini(struct tpm_chip *chip);
+
+#endif /* BOOT_COMPRESSED_TPM_H */
diff --git a/arch/x86/boot/startup/tpm_drv.c b/arch/x86/boot/startup/tpm_drv.c
new file mode 100644
index 000000000000..99971b06f768
--- /dev/null
+++ b/arch/x86/boot/startup/tpm_drv.c
@@ -0,0 +1,567 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Based of the original tpm_tis.c implementation as found in the
+ * Linux 2.6 code base.
+ *
+ * Copyright (C) 2005, 2006 IBM Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ */
+
+#include <crypto/sha2.h>
+#include <asm/io.h>
+
+#include <linux/tpm_command.h>
+#include <linux/tpm_ptp.h>
+#include <linux/tpm_buf.h>
+
+#include "tpm.h"
+
+static u8 tpm_buf_page[PAGE_SIZE];
+
+/*
+ * Single threaded environment only running on BSP. Use a single shared
+ * page for all TPM extend operations.
+ */
+static inline struct tpm_buf *tpm_buf_alloc_page(void)
+{
+ memset(tpm_buf_page, 0, PAGE_SIZE);
+ return (struct tpm_buf *)tpm_buf_page;
+}
+
+static inline void tpm_buf_free_page(void)
+{
+ memset(tpm_buf_page, 0, PAGE_SIZE);
+}
+
+/* Pull in TPM buffer management support */
+#include "../../../../drivers/char/tpm/tpm-buf.c"
+
+static u32 __init tpm_get_alg_size(u16 alg_id)
+{
+ switch (alg_id) {
+ case TPM_ALG_SHA1:
+ return TPM_DIGEST_SIZE;
+ case TPM_ALG_SHA256:
+ case TPM_ALG_SM3_256:
+ return SHA256_DIGEST_SIZE;
+ case TPM_ALG_SHA384:
+ return SHA384_DIGEST_SIZE;
+ case TPM_ALG_SHA512:
+ default:
+ return SHA512_DIGEST_SIZE;
+ };
+}
+
+static inline u8 tpm_read8(struct tpm_chip *chip, u32 field)
+{
+ return readb((void *)(chip->baseaddr | field));
+}
+
+static inline void tpm_write8(struct tpm_chip *chip, u32 field, u8 val)
+{
+ writeb(val, (void *)(chip->baseaddr | field));
+}
+
+static inline u32 tpm_read32(struct tpm_chip *chip, u32 field)
+{
+ return readl((void *)(chip->baseaddr | field));
+}
+
+static inline void tpm_write32(struct tpm_chip *chip, u32 field, u32 val)
+{
+ writel(val, (void *)(chip->baseaddr | field));
+}
+
+/*
+ * In the setup kernel environment, it is far too early to calibrate time.
+ * Assume a 5GHz processor (the upper end of the Fam19h range), allowing
+ * reasonable timeouts on slower systems.
+ */
+static unsigned long ticks_per_ms = (5UL * 1000 * 1000 /* CPU in KHz */);
+
+static inline ktime_t tpm_now_ms(void)
+{
+ return rdtsc()/ticks_per_ms;
+}
+
+static inline void tpm_mdelay(unsigned int msecs)
+{
+ unsigned long ticks = msecs * ticks_per_ms;
+ unsigned long s, e;
+
+ s = rdtsc();
+ do {
+ cpu_relax();
+ e = rdtsc();
+ } while ((e - s) < ticks);
+}
+
+static inline u8 __tis_status(struct tpm_chip *chip)
+{
+ return tpm_read8(chip, TPM_STS(chip->locality));
+}
+
+static inline void __tis_cancel(struct tpm_chip *chip)
+{
+ /* This causes the current command to be aborted */
+ tpm_write8(chip, TPM_STS(chip->locality), TPM_STS_COMMAND_READY);
+}
+
+static int __init __tis_get_burstcount(struct tpm_chip *chip)
+{
+ ktime_t stop;
+ int burstcnt;
+
+ stop = tpm_now_ms() + chip->timeout_d;
+ do {
+ burstcnt = tpm_read8(chip, (TPM_STS(chip->locality) + 1));
+ burstcnt += tpm_read8(chip, TPM_STS(chip->locality) + 2) << 8;
+
+ if (burstcnt)
+ return burstcnt;
+
+ tpm_mdelay(TPM_TIMEOUT);
+ } while (tpm_now_ms() < stop);
+
+ return -EBUSY;
+}
+
+static int __init __tis_wait_for_stat(struct tpm_chip *chip, u8 mask, ktime_t timeout)
+{
+ ktime_t stop;
+ u8 status;
+
+ if ((__tis_status(chip) & mask) == mask)
+ return 0;
+
+ stop = tpm_now_ms() + timeout;
+ do {
+ tpm_mdelay(TPM_TIMEOUT);
+
+ status = __tis_status(chip);
+ if ((status & mask) == mask)
+ return 0;
+ } while (tpm_now_ms() < stop);
+
+ return -ETIME;
+}
+
+static int __init __tis_recv_data(struct tpm_chip *chip, u8 *buf, int count)
+{
+ int size = 0;
+ int burstcnt;
+
+ while (size < count &&
+ __tis_wait_for_stat(chip,
+ TPM_STS_DATA_AVAIL | TPM_STS_VALID,
+ chip->timeout_c) == 0) {
+ burstcnt = __tis_get_burstcount(chip);
+
+ for ( ; burstcnt > 0 && size < count; --burstcnt)
+ buf[size++] = tpm_read8(chip, TPM_DATA_FIFO(chip->locality));
+ }
+
+ return size;
+}
+
+/**
+ * tpm_tis_check_locality - Check if the given locality is the active one
+ * @chip: The TPM chip instance
+ * @loc: The locality to check
+ *
+ * Return: true - locality active, false - not active
+ */
+bool __init tpm_tis_check_locality(struct tpm_chip *chip, int loc)
+{
+ u8 res = tpm_read8(chip, TPM_ACCESS(loc));
+
+ if ((res & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
+ (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) {
+ chip->locality = loc;
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * tpm_tis_release_locality - Release the active locality
+ * @chip: The TPM chip instance
+ */
+void __init tpm_tis_release_locality(struct tpm_chip *chip)
+{
+ u8 res = tpm_read8(chip, TPM_ACCESS(chip->locality));
+
+ if ((res & (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) ==
+ (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID))
+ tpm_write8(chip, TPM_ACCESS(chip->locality), TPM_ACCESS_RELINQUISH_LOCALITY);
+
+ chip->locality = 0;
+}
+
+/**
+ * tpm_tis_request_locality - Request to make the given locality the active one
+ * @chip: The TPM chip instance
+ * @loc: The locality to make active/set as current
+ *
+ * Return:
+ * >= 0 - Success, new active locality returned or locality already active
+ * < 0 - Error occurred
+ */
+int __init tpm_tis_request_locality(struct tpm_chip *chip, int loc)
+{
+ ktime_t stop;
+
+ if (tpm_tis_check_locality(chip, loc))
+ return loc;
+
+ /* Set the new locality */
+ tpm_write8(chip, TPM_ACCESS(loc), TPM_ACCESS_REQUEST_USE);
+
+ stop = tpm_now_ms() + chip->timeout_b;
+ do {
+ if (tpm_tis_check_locality(chip, loc))
+ return loc;
+
+ tpm_mdelay(TPM_TIMEOUT);
+ } while (tpm_now_ms() < stop);
+
+ return -1;
+}
+
+/**
+ * tpm_tis_disable_interrupts - Disable interrupts for the TPM, use polling mode only
+ * @chip: The TPM chip instance
+ */
+void __init tpm_tis_disable_interrupts(struct tpm_chip *chip)
+{
+ u32 intmask;
+
+ intmask = tpm_read32(chip, TPM_INT_ENABLE(chip->locality));
+ /* Disable everything to make sure it is in a consistent state */
+ intmask &= ~(TPM_GLOBAL_INT_ENABLE | TPM_INTF_CMD_READY_INT |
+ TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_STS_VALID_INT |
+ TPM_INTF_DATA_AVAIL_INT);
+ tpm_write32(chip, TPM_INT_ENABLE(chip->locality), intmask);
+}
+
+/**
+ * tpm_tis_recv - Receive response data from TPM via TIS FIFO
+ * @chip: The TPM chip instance
+ * @buf: The response buffer
+ * @count: Length of the response buffer
+ *
+ * Return:
+ * = 0 - Success, no response data
+ * > 0 - Success, value is the response data length
+ * < 0 - Error occurred
+ */
+static int __init tpm_tis_recv(struct tpm_chip *chip, u8 *buf, int count)
+{
+ int expected, status, size = 0, rc = -EIO;
+
+ if (count < TPM_HEADER_SIZE)
+ goto out;
+
+ /* Read first 10 bytes, including tag, paramsize, and result */
+ size = __tis_recv_data(chip, buf, TPM_HEADER_SIZE);
+ if (size < TPM_HEADER_SIZE)
+ goto out;
+
+ expected = be32_to_cpu(*((u32 *)(buf + 2)));
+ if (expected > count)
+ goto out;
+
+ size += __tis_recv_data(chip, &buf[TPM_HEADER_SIZE], expected - TPM_HEADER_SIZE);
+ if (size < expected) {
+ rc = -ETIME;
+ goto out;
+ }
+
+ __tis_wait_for_stat(chip, TPM_STS_VALID, chip->timeout_c);
+
+ status = __tis_status(chip);
+ if (status & TPM_STS_DATA_AVAIL) {
+ rc = -EIO;
+ goto out;
+ }
+
+ /* Done with receive, move to Command Ready state */
+ __tis_cancel(chip);
+
+ return size;
+out:
+ __tis_cancel(chip);
+ tpm_tis_release_locality(chip);
+ return rc;
+}
+
+/**
+ * tpm_tis_send - Send command to TPM via TIS FIFO
+ * @chip: The TPM chip instance
+ * @buf: The command buffer
+ * @len: Length of the command buffer to send
+ *
+ * Return:
+ * = len - Success, all data sent
+ * < 0 - Error occurred
+ */
+static int __init tpm_tis_send(struct tpm_chip *chip, u8 *buf, int len)
+{
+ int status, burstcnt = 0;
+ int count = 0;
+ int rc = 0;
+
+ status = __tis_status(chip);
+ if ((status & TPM_STS_COMMAND_READY) == 0) {
+ __tis_cancel(chip);
+ if (__tis_wait_for_stat(chip, TPM_STS_COMMAND_READY, chip->timeout_b) < 0) {
+ rc = -ETIME;
+ goto out_err;
+ }
+ }
+
+ while (count < len - 1) {
+ burstcnt = __tis_get_burstcount(chip);
+ for ( ; burstcnt > 0 && count < len - 1; --burstcnt)
+ tpm_write8(chip, TPM_DATA_FIFO(chip->locality), buf[count++]);
+
+ __tis_wait_for_stat(chip, TPM_STS_VALID, chip->timeout_c);
+ status = __tis_status(chip);
+ if ((status & TPM_STS_DATA_EXPECT) == 0) {
+ rc = -EIO;
+ goto out_err;
+ }
+ }
+
+ /* Write last byte */
+ tpm_write8(chip, TPM_DATA_FIFO(chip->locality), buf[count]);
+ __tis_wait_for_stat(chip, TPM_STS_VALID, chip->timeout_c);
+ status = __tis_status(chip);
+ if ((status & TPM_STS_DATA_EXPECT) != 0) {
+ rc = -EIO;
+ goto out_err;
+ }
+
+ /* Go and do it */
+ tpm_write8(chip, TPM_STS(chip->locality), TPM_STS_GO);
+
+ return len;
+
+out_err:
+ __tis_cancel(chip);
+ tpm_tis_release_locality(chip);
+ return rc;
+}
+
+/**
+ * tpm_tis_transmit - Transmit a TPM FIFO command
+ * @chip: The TPM chip instance
+ * @buf: The request and response buffer object
+ * @bufsize: Entire size available in buffer
+ *
+ * Return:
+ * = 0 - Success, no returned data
+ * > 0 - Success, value is the return data length
+ * < 0 - Error occurred
+ */
+static int __init tpm_tis_transmit(struct tpm_chip *chip, u8 *buf, u32 bufsize)
+{
+ ktime_t stop;
+ u32 count;
+ u8 status;
+ int rc;
+
+ count = be32_to_cpu(*((u32 *) (buf + 2)));
+ if (count == 0)
+ return -ENODATA;
+
+ if (count > bufsize)
+ return -E2BIG;
+
+ rc = tpm_tis_send(chip, buf, count);
+ if (rc < 0)
+ goto out;
+
+ stop = tpm_now_ms() + TIS_DURATION;
+ do {
+ status = __tis_status(chip);
+ if ((status & (TPM_STS_DATA_AVAIL | TPM_STS_VALID)) ==
+ (TPM_STS_DATA_AVAIL | TPM_STS_VALID))
+ goto out_recv;
+
+ if (status == TPM_STS_COMMAND_READY) {
+ rc = -ECANCELED;
+ goto out;
+ }
+
+ tpm_mdelay(TPM_TIMEOUT);
+ rmb();
+ } while (tpm_now_ms() < stop);
+
+ /* Cancel the command */
+ __tis_cancel(chip);
+ rc = -ETIME;
+ goto out;
+
+out_recv:
+ rc = tpm_tis_recv(chip, buf, bufsize);
+ if (rc >= 0) {
+ if (rc > 0 && rc < TPM_HEADER_SIZE)
+ return -EFAULT;
+ return rc;
+ }
+ /* Else return was an error, nothing to receive */
+
+out:
+ return rc;
+}
+
+/**
+ * tpm_find_interface_and_family - interface FIFO/CRB, family 2.0 or 1.2
+ * @chip: The TPM chip instance
+ *
+ * Return: TPM family ID enum
+ */
+static enum tpm_family __init tpm_find_interface_and_family(struct tpm_chip *chip)
+{
+ struct tpm_intf_capability intf_cap;
+ struct tpm_interface_id intf_id;
+
+ /* Sort out whether it is 1.x */
+ intf_cap.val = tpm_read32(chip, TPM_INTF_CAPS(0));
+ if ((intf_cap.interface_version == TPM_TIS_INTF_12) ||
+ (intf_cap.interface_version == TPM_TIS_INTF_13))
+ return TPM_FAMILY_12; /* Always TIS */
+
+ /* Assume that it is 2.0 but check if the interface is CRB */
+ intf_id.val = tpm_read32(chip, TPM_INTF_ID(0));
+ if (intf_id.interface_type == TPM_CRB_INTF_ACTIVE)
+ return TPM_FAMILY_INVALID;
+
+ /* Else TPM 2.0 with TIS interface */
+ return TPM_FAMILY_20;
+}
+
+/**
+ * tpm1_pcr_extend - send a TPM1 extend command to the device
+ * @chip: a TPM chip to use
+ * @pcr_idx: the PCR index to extend for the current locality
+ * @hash: the SHA1 hash digest to extend
+ *
+ * Return:
+ * * 0 - OK
+ * * -errno - A system error
+ * * TPM_RC - A TPM error
+ */
+int __init tpm1_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash)
+{
+ struct tpm_buf *buf = tpm_buf_alloc_page();
+ int rc = 0;
+
+ tpm_buf_init(buf, TPM_BUFSIZE);
+ tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_PCR_EXTEND);
+
+ tpm_buf_append_u32(buf, pcr_idx);
+ tpm_buf_append(buf, hash, TPM_DIGEST_SIZE);
+
+ rc = tpm_tis_transmit(chip, buf->data, PAGE_SIZE);
+
+ /* Ignoring output */
+ if (rc > 0)
+ rc = 0;
+
+ tpm_buf_free_page();
+
+ return rc;
+}
+
+/**
+ * tpm2_pcr_extend() - send a TPM2 extend command to the device
+ *
+ * @chip: TPM chip to use.
+ * @pcr_idx: index of the PCR.
+ * @digests: list of PCR banks and corresponding digest values to extend.
+ * @digest_count: count of digests to extend
+ *
+ * Return:
+ * * 0 - OK
+ * * -errno - A system error
+ * * TPM_RC - A TPM error
+ */
+int __init tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
+ struct tpm_digest *digests, u32 digest_count)
+{
+ struct tpm_buf *buf = tpm_buf_alloc_page();
+ int rc = 0, i;
+
+ tpm_buf_init(buf, TPM_BUFSIZE);
+ tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_EXTEND);
+
+ tpm_buf_append_u32(buf, pcr_idx);
+
+ /* Setup a NULL auth session for the command */
+ tpm_buf_append_u32(buf, 9);
+ /* auth handle */
+ tpm_buf_append_u32(buf, TPM2_RS_PW);
+ /* nonce */
+ tpm_buf_append_u16(buf, 0);
+ /* attributes */
+ tpm_buf_append_u8(buf, 0);
+ /* passphrase */
+ tpm_buf_append_u16(buf, 0);
+
+ tpm_buf_append_u32(buf, digest_count);
+
+ for (i = 0; i < digest_count; i++) {
+ tpm_buf_append_u16(buf, digests[i].alg_id);
+ tpm_buf_append(buf, (const unsigned char *)&digests[i].digest,
+ tpm_get_alg_size(digests[i].alg_id));
+ }
+
+ rc = tpm_tis_transmit(chip, buf->data, PAGE_SIZE);
+
+ /* Ignoring output */
+ if (rc > 0)
+ rc = 0;
+
+ tpm_buf_free_page();
+
+ return rc;
+}
+
+int __init early_tpm_init(struct tpm_chip *chip, u64 baseaddr)
+{
+ u32 didvid;
+
+ memset(chip, 0, sizeof(*chip));
+ chip->baseaddr = baseaddr;
+
+ chip->family = tpm_find_interface_and_family(chip);
+ if (chip->family == TPM_FAMILY_INVALID)
+ return TPM_ERR_INVALID_FAMILY;
+
+ /* Set default timeouts */
+ chip->timeout_a = TIS_SHORT_TIMEOUT;
+ chip->timeout_b = TIS_LONG_TIMEOUT;
+ chip->timeout_c = TIS_SHORT_TIMEOUT;
+ chip->timeout_d = TIS_SHORT_TIMEOUT;
+
+ /* Get the vendor and device ids */
+ didvid = tpm_read32(chip, TPM_DID_VID(0));
+ chip->did = didvid >> 16;
+ chip->vid = didvid & 0xFFFF;
+
+ return TPM_SUCCESS;
+}
+
+int __init early_tpm_fini(struct tpm_chip *chip)
+{
+ tpm_tis_release_locality(chip);
+ memset(chip, 0, sizeof(*chip));
+
+ return TPM_SUCCESS;
+}
--
2.47.3
^ permalink raw reply related
* [PATCH v16 28/38] x86: Add early SHA-384/512 support for Secure Launch early measurements
From: Ross Philipson @ 2026-05-15 21:14 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,
daniel.kiper, andrew.cooper3, trenchboot-devel
In-Reply-To: <20260515211410.31440-1-ross.philipson@gmail.com>
From: "Daniel P. Smith" <dpsmith@apertussolutions.com>
On newer TPM 2 implementations, SHA 384 and 512 banks may be available
for use. If these banks are enabled in firmware, they will be used for
the Dynamic Launch. The DLME will also use these algorithms to measure
configuration information into the TPM as early as possible before using
the values. This implementation uses the established approach of #including
the SHA-512 library directly in the early boot code.
Signed-off-by: Daniel P. Smith <dpsmith@apertussolutions.com>
Signed-off-by: Ross Philipson <ross.philipson@gmail.com>
---
arch/x86/boot/startup/Makefile | 1 +
arch/x86/boot/startup/lib-sha512.c | 6 ++++++
2 files changed, 7 insertions(+)
create mode 100644 arch/x86/boot/startup/lib-sha512.c
diff --git a/arch/x86/boot/startup/Makefile b/arch/x86/boot/startup/Makefile
index 071a90f23ae0..527cba7e4560 100644
--- a/arch/x86/boot/startup/Makefile
+++ b/arch/x86/boot/startup/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_AMD_MEM_ENCRYPT) += sme.o sev-startup.o
slaunch-objs += lib-sha1.o
slaunch-objs += lib-sha256.o
+slaunch-objs += lib-sha512.o
obj-$(CONFIG_SECURE_LAUNCH) += $(slaunch-objs)
pi-objs := $(patsubst %.o,$(obj)/%.o,$(obj-y))
diff --git a/arch/x86/boot/startup/lib-sha512.c b/arch/x86/boot/startup/lib-sha512.c
new file mode 100644
index 000000000000..2afd5c5935cd
--- /dev/null
+++ b/arch/x86/boot/startup/lib-sha512.c
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2026 Apertus Solutions, LLC
+ */
+
+#include "../../../../lib/crypto/sha512.c"
--
2.47.3
^ permalink raw reply related
* [PATCH v16 27/38] x86: Add early SHA-256 support for Secure Launch early measurements
From: Ross Philipson @ 2026-05-15 21:13 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,
daniel.kiper, andrew.cooper3, trenchboot-devel
In-Reply-To: <20260515211410.31440-1-ross.philipson@gmail.com>
From: "Daniel P. Smith" <dpsmith@apertussolutions.com>
The SHA-256 algorithm is necessary to measure configuration information
into the TPM as early as possible before using the values. This
implementation uses the established approach of #including the SHA-256
library directly in the early boot code.
Signed-off-by: Daniel P. Smith <dpsmith@apertussolutions.com>
Signed-off-by: Ross Philipson <ross.philipson@gmail.com>
---
arch/x86/boot/startup/Makefile | 1 +
arch/x86/boot/startup/lib-sha256.c | 6 ++++++
2 files changed, 7 insertions(+)
create mode 100644 arch/x86/boot/startup/lib-sha256.c
diff --git a/arch/x86/boot/startup/Makefile b/arch/x86/boot/startup/Makefile
index e283ee4c1f45..071a90f23ae0 100644
--- a/arch/x86/boot/startup/Makefile
+++ b/arch/x86/boot/startup/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_X86_64) += gdt_idt.o map_kernel.o
obj-$(CONFIG_AMD_MEM_ENCRYPT) += sme.o sev-startup.o
slaunch-objs += lib-sha1.o
+slaunch-objs += lib-sha256.o
obj-$(CONFIG_SECURE_LAUNCH) += $(slaunch-objs)
pi-objs := $(patsubst %.o,$(obj)/%.o,$(obj-y))
diff --git a/arch/x86/boot/startup/lib-sha256.c b/arch/x86/boot/startup/lib-sha256.c
new file mode 100644
index 000000000000..f60df97f9244
--- /dev/null
+++ b/arch/x86/boot/startup/lib-sha256.c
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2026 Apertus Solutions, LLC
+ */
+
+#include "../../../../lib/crypto/sha256.c"
--
2.47.3
^ permalink raw reply related
* [PATCH v16 26/38] x86: Add early SHA-1 support for Secure Launch early measurements
From: Ross Philipson @ 2026-05-15 21:13 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,
daniel.kiper, andrew.cooper3, trenchboot-devel
In-Reply-To: <20260515211410.31440-1-ross.philipson@gmail.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 DRTM log.
Signed-off-by: Daniel P. Smith <dpsmith@apertussolutions.com>
Signed-off-by: Ross Philipson <ross.philipson@gmail.com>
---
arch/x86/boot/startup/Makefile | 4 ++++
arch/x86/boot/startup/lib-sha1.c | 6 ++++++
2 files changed, 10 insertions(+)
create mode 100644 arch/x86/boot/startup/lib-sha1.c
diff --git a/arch/x86/boot/startup/Makefile b/arch/x86/boot/startup/Makefile
index 5e499cfb29b5..e283ee4c1f45 100644
--- a/arch/x86/boot/startup/Makefile
+++ b/arch/x86/boot/startup/Makefile
@@ -20,6 +20,10 @@ KCOV_INSTRUMENT := n
obj-$(CONFIG_X86_64) += gdt_idt.o map_kernel.o
obj-$(CONFIG_AMD_MEM_ENCRYPT) += sme.o sev-startup.o
+
+slaunch-objs += lib-sha1.o
+obj-$(CONFIG_SECURE_LAUNCH) += $(slaunch-objs)
+
pi-objs := $(patsubst %.o,$(obj)/%.o,$(obj-y))
lib-$(CONFIG_X86_64) += la57toggle.o
diff --git a/arch/x86/boot/startup/lib-sha1.c b/arch/x86/boot/startup/lib-sha1.c
new file mode 100644
index 000000000000..8d679d12f6bf
--- /dev/null
+++ b/arch/x86/boot/startup/lib-sha1.c
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2026 Apertus Solutions, LLC
+ */
+
+#include "../../../../lib/crypto/sha1.c"
--
2.47.3
^ permalink raw reply related
* [PATCH v16 25/38] x86/boot: Slight refactor of the 5 level paging logic
From: Ross Philipson @ 2026-05-15 21:13 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,
daniel.kiper, andrew.cooper3, trenchboot-devel
In-Reply-To: <20260515211410.31440-1-ross.philipson@gmail.com>
From: Ard Biesheuvel <ardb@kernel.org>
In preparation for adding Secure Launch support, which will require that
the number of paging levels remains untouched, tweak the logic slightly
so that the slaunch check can be inserted easily.
No functional change intended.
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Signed-off-by: Ross Philipson <ross.philipson@gmail.com>
---
arch/x86/boot/compressed/pgtable_64.c | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/arch/x86/boot/compressed/pgtable_64.c b/arch/x86/boot/compressed/pgtable_64.c
index 0e89e197e112..3e9d651da73e 100644
--- a/arch/x86/boot/compressed/pgtable_64.c
+++ b/arch/x86/boot/compressed/pgtable_64.c
@@ -102,6 +102,7 @@ static unsigned long find_trampoline_placement(void)
asmlinkage void configure_5level_paging(struct boot_params *bp, void *pgtable)
{
+ bool l5_enabled = native_read_cr4() & X86_CR4_LA57;
void (*toggle_la57)(void *cr3);
bool l5_required = false;
@@ -118,10 +119,12 @@ asmlinkage void configure_5level_paging(struct boot_params *bp, void *pgtable)
* + CPUID leaf 7 is supported
* + the leaf has the feature bit set
*/
- if (!cmdline_find_option_bool("no5lvl") &&
- native_cpuid_eax(0) >= 7 && (native_cpuid_ecx(7) & BIT(16))) {
- l5_required = true;
+ if (native_cpuid_eax(0) < 7 || !(native_cpuid_ecx(7) & BIT(16)))
+ return;
+
+ l5_required = !cmdline_find_option_bool("no5lvl");
+ if (l5_required) {
/* Initialize variables for 5-level paging */
__pgtable_l5_enabled = 1;
pgdir_shift = 48;
@@ -132,7 +135,7 @@ asmlinkage void configure_5level_paging(struct boot_params *bp, void *pgtable)
* The trampoline will not be used if the paging mode is already set to
* the desired one.
*/
- if (l5_required == !!(native_read_cr4() & X86_CR4_LA57))
+ if (l5_required == l5_enabled)
return;
trampoline_32bit = (unsigned long *)find_trampoline_placement();
--
2.47.3
^ permalink raw reply related
* [PATCH v16 24/38] x86/msr: Add variable MTRR base/mask and x2apic ID registers
From: Ross Philipson @ 2026-05-15 21:13 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,
daniel.kiper, andrew.cooper3, trenchboot-devel
In-Reply-To: <20260515211410.31440-1-ross.philipson@gmail.com>
Add the MSR values required by Secure Launch to locate particular CPU cores
during application processor (AP) startup, and restore the MTRR state after
an Intel TXT launch.
Signed-off-by: Ross Philipson <ross.philipson@gmail.com>
---
arch/x86/include/asm/msr-index.h | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index 6673601246b3..2a95e3389622 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -408,6 +408,9 @@
#define MSR_IA32_RTIT_OUTPUT_BASE 0x00000560
#define MSR_IA32_RTIT_OUTPUT_MASK 0x00000561
+#define MSR_MTRRphysBase0 0x00000200
+#define MSR_MTRRphysMask0 0x00000201
+
#define MSR_MTRRfix64K_00000 0x00000250
#define MSR_MTRRfix16K_80000 0x00000258
#define MSR_MTRRfix16K_A0000 0x00000259
@@ -960,6 +963,8 @@
#define MSR_IA32_APICBASE_ENABLE (1<<11)
#define MSR_IA32_APICBASE_BASE (0xfffff<<12)
+#define MSR_IA32_X2APIC_APICID 0x00000802
+
#define MSR_IA32_UCODE_WRITE 0x00000079
#define MSR_IA32_MCU_ENUMERATION 0x0000007b
--
2.47.3
^ permalink raw reply related
* [PATCH v16 23/38] x86: Allow WARN_trap() macro to be included in pre-boot environments
From: Ross Philipson @ 2026-05-15 21:13 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,
daniel.kiper, andrew.cooper3, trenchboot-devel
In-Reply-To: <20260515211410.31440-1-ross.philipson@gmail.com>
For pre-boot environments, do not use the static call definition of the
WARN_trap() macro.
Suggested-by: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Ross Philipson <ross.philipson@gmail.com>
---
arch/x86/include/asm/bug.h | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/arch/x86/include/asm/bug.h b/arch/x86/include/asm/bug.h
index 80c1696d8d59..7899768ae644 100644
--- a/arch/x86/include/asm/bug.h
+++ b/arch/x86/include/asm/bug.h
@@ -138,8 +138,14 @@ do { \
#ifdef HAVE_ARCH_BUG_FORMAT_ARGS
#ifndef __ASSEMBLER__
+
+#ifndef __DISABLE_EXPORTS
#include <linux/static_call_types.h>
DECLARE_STATIC_CALL(WARN_trap, __WARN_trap);
+#define WARN_trap(...) static_call_mod(WARN_trap)(__VA_ARGS__)
+#else /* __DISABLE_EXPORTS */
+#define WARN_trap(...) __WARN_trap(__VA_ARGS__)
+#endif /* __DISABLE_EXPORTS */
struct pt_regs;
struct sysv_va_list { /* from AMD64 System V ABI */
@@ -172,7 +178,7 @@ extern void *__warn_args(struct arch_va_list *args, struct pt_regs *regs);
#define __WARN_print_arg(flags, format, arg...) \
do { \
int __flags = (flags) | BUGFLAG_WARNING | BUGFLAG_ARGS ; \
- static_call_mod(WARN_trap)(__WARN_bug_entry(__flags, format), ## arg); \
+ WARN_trap(__WARN_bug_entry(__flags, format), ## arg); \
asm (""); /* inhibit tail-call optimization */ \
} while (0)
--
2.47.3
^ permalink raw reply related
* [PATCH v16 22/38] lib/crypto: Add SHA512 support for pre-boot environments
From: Ross Philipson @ 2026-05-15 21:13 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,
daniel.kiper, andrew.cooper3, trenchboot-devel
In-Reply-To: <20260515211410.31440-1-ross.philipson@gmail.com>
Allow the SHA512 library code in lib/crypto/sha512.c to be used in
pre-boot environments. Use the __DISABLE_EXPORTS macro to disable
function exports and define the proper values for that environment
as was done earlier for SHA256.
Signed-off-by: Ross Philipson <ross.philipson@gmail.com>
---
lib/crypto/sha512.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/lib/crypto/sha512.c b/lib/crypto/sha512.c
index 605eab51aabd..160e81d86da6 100644
--- a/lib/crypto/sha512.c
+++ b/lib/crypto/sha512.c
@@ -133,7 +133,7 @@ sha512_blocks_generic(struct sha512_block_state *state,
} while (--nblocks);
}
-#ifdef CONFIG_CRYPTO_LIB_SHA512_ARCH
+#if defined(CONFIG_CRYPTO_LIB_SHA512_ARCH) && !defined(__DISABLE_EXPORTS)
#include "sha512.h" /* $(SRCARCH)/sha512.h */
#else
#define sha512_blocks sha512_blocks_generic
@@ -250,6 +250,12 @@ void sha512(const u8 *data, size_t len, u8 out[SHA512_DIGEST_SIZE])
}
EXPORT_SYMBOL_GPL(sha512);
+/*
+ * Pre-boot environments (as indicated by __DISABLE_EXPORTS being defined)
+ * don't need the SHA2 HMAC support code.
+ */
+#ifndef __DISABLE_EXPORTS
+
static void __hmac_sha512_preparekey(struct sha512_block_state *istate,
struct sha512_block_state *ostate,
const u8 *raw_key, size_t raw_key_len,
@@ -406,6 +412,8 @@ void hmac_sha512_usingrawkey(const u8 *raw_key, size_t raw_key_len,
}
EXPORT_SYMBOL_GPL(hmac_sha512_usingrawkey);
+#endif /* !__DISABLE_EXPORTS */
+
#if defined(sha512_mod_init_arch) || defined(CONFIG_CRYPTO_FIPS)
static int __init sha512_mod_init(void)
{
--
2.47.3
^ permalink raw reply related
* [PATCH v16 21/38] lib/crypto: Add SHA1 support for pre-boot environments
From: Ross Philipson @ 2026-05-15 21:13 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,
daniel.kiper, andrew.cooper3, trenchboot-devel
In-Reply-To: <20260515211410.31440-1-ross.philipson@gmail.com>
Allow the SHA1 library code in lib/crypto/sha1.c to be used in
pre-boot environments. Use the __DISABLE_EXPORTS macro to disable
function exports and define the proper values for that environment
as was done earlier for SHA256.
Acked-by: Eric Biggers <ebiggers@kernel.org>
Signed-off-by: Ross Philipson <ross.philipson@gmail.com>
---
lib/crypto/sha1.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/lib/crypto/sha1.c b/lib/crypto/sha1.c
index daf18c862fdf..f30b5ff3b31e 100644
--- a/lib/crypto/sha1.c
+++ b/lib/crypto/sha1.c
@@ -125,7 +125,7 @@ static void __maybe_unused sha1_blocks_generic(struct sha1_block_state *state,
memzero_explicit(workspace, sizeof(workspace));
}
-#ifdef CONFIG_CRYPTO_LIB_SHA1_ARCH
+#if defined(CONFIG_CRYPTO_LIB_SHA1_ARCH) && !defined(__DISABLE_EXPORTS)
#include "sha1.h" /* $(SRCARCH)/sha1.h */
#else
#define sha1_blocks sha1_blocks_generic
@@ -207,6 +207,12 @@ void sha1(const u8 *data, size_t len, u8 out[SHA1_DIGEST_SIZE])
}
EXPORT_SYMBOL_GPL(sha1);
+/*
+ * Pre-boot environments (as indicated by __DISABLE_EXPORTS being defined)
+ * don't need the SHA1 HMAC support code.
+ */
+#ifndef __DISABLE_EXPORTS
+
static void __hmac_sha1_preparekey(struct sha1_block_state *istate,
struct sha1_block_state *ostate,
const u8 *raw_key, size_t raw_key_len)
@@ -302,6 +308,8 @@ void hmac_sha1_usingrawkey(const u8 *raw_key, size_t raw_key_len,
}
EXPORT_SYMBOL_GPL(hmac_sha1_usingrawkey);
+#endif /* !__DISABLE_EXPORTS */
+
#if defined(sha1_mod_init_arch) || defined(CONFIG_CRYPTO_FIPS)
static int __init sha1_mod_init(void)
{
--
2.47.3
^ permalink raw reply related
* [PATCH v16 20/38] x86/txt: Intel Trusted eXecution Technology (TXT) definitions
From: Ross Philipson @ 2026-05-15 21:13 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,
daniel.kiper, andrew.cooper3, trenchboot-devel
In-Reply-To: <20260515211410.31440-1-ross.philipson@gmail.com>
Intel TXT architectural specific definitions. See the specification
for detailed information:
https://www.intel.com/content/dam/www/public/us/en/documents/guides/intel-txt-software-development-guide.pdf
Signed-off-by: Ross Philipson <ross.philipson@gmail.com>
---
arch/x86/include/asm/txt.h | 281 +++++++++++++++++++++++++++++++++++++
1 file changed, 281 insertions(+)
create mode 100644 arch/x86/include/asm/txt.h
diff --git a/arch/x86/include/asm/txt.h b/arch/x86/include/asm/txt.h
new file mode 100644
index 000000000000..eaec915d4878
--- /dev/null
+++ b/arch/x86/include/asm/txt.h
@@ -0,0 +1,281 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Intel Trusted eXecution Technology (TXT) Definitions
+ *
+ * Copyright (c) 2026 Apertus Solutions, LLC
+ * Copyright (c) 2026, Oracle and/or its affiliates.
+ */
+
+#ifndef _ASM_X86_TXT_H
+#define _ASM_X86_TXT_H
+
+/*
+ * Intel Safer Mode Extensions (SMX)
+ *
+ * Intel SMX provides a programming interface to establish a Measured Launched
+ * Environment (MLE). The measurement and protection mechanisms are supported by the
+ * capabilities of an Intel Trusted Execution Technology (TXT) platform. SMX is
+ * the processor's programming interface in an Intel TXT platform.
+ *
+ * See:
+ * Intel SDM Volume 2 - 6.1 "Safer Mode Extensions Reference"
+ * Intel Trusted Execution Technology - Measured Launch Environment Developer's Guide
+ */
+
+/*
+ * SMX GETSEC Leaf Functions
+ */
+#define SMX_X86_GETSEC_SEXIT 5
+#define SMX_X86_GETSEC_SMCTRL 7
+#define SMX_X86_GETSEC_WAKEUP 8
+
+/*
+ * Intel Trusted Execution Technology MMIO Registers Banks
+ */
+#define TXT_PUB_CONFIG_REGS_BASE 0xfed30000
+#define TXT_PRIV_CONFIG_REGS_BASE 0xfed20000
+#define TXT_NR_CONFIG_PAGES ((TXT_PUB_CONFIG_REGS_BASE - \
+ TXT_PRIV_CONFIG_REGS_BASE) >> PAGE_SHIFT)
+
+/*
+ * Intel Trusted Execution Technology (TXT) Registers
+ */
+#define TXT_CR_STS 0x0000
+#define TXT_CR_ESTS 0x0008
+#define TXT_CR_ERRORCODE 0x0030
+#define TXT_CR_CMD_RESET 0x0038
+#define TXT_CR_CMD_CLOSE_PRIVATE 0x0048
+#define TXT_CR_DIDVID 0x0110
+#define TXT_CR_VER_EMIF 0x0200
+#define TXT_CR_CMD_UNLOCK_MEM_CONFIG 0x0218
+#define TXT_CR_SINIT_BASE 0x0270
+#define TXT_CR_SINIT_SIZE 0x0278
+#define TXT_CR_MLE_JOIN 0x0290
+#define TXT_CR_HEAP_BASE 0x0300
+#define TXT_CR_HEAP_SIZE 0x0308
+#define TXT_CR_SCRATCHPAD 0x0378
+#define TXT_CR_CMD_OPEN_LOCALITY1 0x0380
+#define TXT_CR_CMD_CLOSE_LOCALITY1 0x0388
+#define TXT_CR_CMD_OPEN_LOCALITY2 0x0390
+#define TXT_CR_CMD_CLOSE_LOCALITY2 0x0398
+#define TXT_CR_CMD_SECRETS 0x08e0
+#define TXT_CR_CMD_NO_SECRETS 0x08e8
+#define TXT_CR_E2STS 0x08f0
+
+/* TXT default register value */
+#define TXT_REGVALUE_ONE 0x1ULL
+
+/* TXTCR_STS status bits */
+#define TXT_SENTER_DONE_STS BIT(0)
+#define TXT_SEXIT_DONE_STS BIT(1)
+
+/*
+ * SINIT/MLE Capabilities Field Bit Definitions
+ */
+#define TXT_SINIT_MLE_CAP_RLP_WAKE_GETSEC 0
+#define TXT_SINIT_MLE_CAP_RLP_WAKE_MONITOR 1
+
+/*
+ * OS/MLE Secure Launch Specific Definitions
+ */
+#define TXT_OS_MLE_STRUCT_VERSION 1
+#define TXT_OS_MLE_MAX_VARIABLE_MTRRS 32
+
+#ifndef __ASSEMBLER__
+
+/*
+ * TXT Heap extended data elements.
+ */
+struct txt_heap_ext_data_element {
+ u32 type;
+ u32 size;
+ /* Data */
+} __packed;
+
+#define TXT_HEAP_EXTDATA_TYPE_END 0
+
+struct txt_heap_end_element {
+ u32 type;
+ u32 size;
+} __packed;
+
+#define TXT_HEAP_EXTDATA_TYPE_TPM_EVENT_LOG_PTR 5
+
+struct txt_heap_event_log_element {
+ u64 event_log_phys_addr;
+} __packed;
+
+#define TXT_HEAP_EXTDATA_TYPE_EVENT_LOG_POINTER2_1 8
+
+struct txt_heap_event_log_pointer2_1_element {
+ u64 phys_addr;
+ u32 allocated_event_container_size;
+ u32 first_record_offset;
+ u32 next_record_offset;
+} __packed;
+
+/*
+ * TXT specification defined BIOS data TXT Heap table
+ */
+struct txt_bios_data {
+ u32 version; /* Currently 5 for TPM 1.2 and 6 for TPM 2.0 */
+ u32 bios_sinit_size;
+ u64 reserved1;
+ u64 reserved2;
+ u32 num_logical_procs;
+ u32 sinit_flags;
+ u32 mle_flags;
+ /* Versions >= 5 with updates in version 6 */
+ /* Ext Data Elements */
+} __packed;
+
+/*
+ * TXT specification defined OS/SINIT TXT Heap table
+ */
+struct txt_os_sinit_data {
+ u32 version; /* Currently 6 for TPM 1.2 and 7 for TPM 2.0 */
+ u32 flags;
+ u64 mle_ptab;
+ u64 mle_size;
+ u64 mle_hdr_base;
+ u64 vtd_pmr_lo_base;
+ u64 vtd_pmr_lo_size;
+ u64 vtd_pmr_hi_base;
+ u64 vtd_pmr_hi_size;
+ u64 lcp_po_base;
+ u64 lcp_po_size;
+ u32 capabilities;
+ /* Version = 5 */
+ u64 efi_rsdt_ptr;
+ /* Versions >= 6 */
+ /* Ext Data Elements */
+} __packed;
+
+/*
+ * TXT specification defined SINIT/MLE TXT Heap table
+ */
+struct txt_sinit_mle_data {
+ u32 version; /* Current values are 6 through 9 */
+ /* Versions <= 8 */
+ u8 bios_acm_id[20];
+ u32 edx_senter_flags;
+ u64 mseg_valid;
+ u8 sinit_hash[20];
+ u8 mle_hash[20];
+ u8 stm_hash[20];
+ u8 lcp_policy_hash[20];
+ u32 lcp_policy_control;
+ /* Versions >= 7 */
+ u32 rlp_wakeup_addr;
+ u32 reserved;
+ u32 num_of_sinit_mdrs;
+ u32 sinit_mdrs_table_offset;
+ u32 sinit_vtd_dmar_table_size;
+ u32 sinit_vtd_dmar_table_offset;
+ /* Versions >= 8 */
+ u32 processor_scrtm_status;
+ /* Versions >= 9 */
+ /* Ext Data Elements */
+} __packed;
+
+/*
+ * TXT data reporting structure for memory types
+ */
+struct txt_sinit_memory_descriptor_record {
+ u64 address;
+ u64 length;
+ u8 type;
+ u8 reserved[7];
+} __packed;
+
+/*
+ * TXT data structure used by a responsive local processor (RLP) to start
+ * execution in response to a GETSEC[WAKEUP].
+ */
+struct smx_rlp_mle_join {
+ u32 rlp_gdt_limit;
+ u32 rlp_gdt_base;
+ u32 rlp_seg_sel; /* cs (ds, es, ss are seg_sel+8) */
+ u32 rlp_entry_point; /* phys addr */
+} __packed;
+
+/*
+ * TPM event log structures defined in both the TXT specification and
+ * the TCG documentation.
+ */
+#define TPM_EVTLOG_SIGNATURE "TXT Event Container"
+
+struct tpm_event_log_header {
+ char signature[20];
+ char reserved[12];
+ u8 container_ver_major;
+ u8 container_ver_minor;
+ u8 pcr_event_ver_major;
+ u8 pcr_event_ver_minor;
+ u32 container_size;
+ u32 pcr_events_offset;
+ u32 next_event_offset;
+ /* PCREvents[] */
+} __packed;
+
+/*
+ * Functions to extract data from the Intel TXT Heap Memory. The layout
+ * of the heap is as follows:
+ * +----------------------------+
+ * | Size Bios Data table (u64) |
+ * +----------------------------+
+ * | Bios Data table |
+ * +----------------------------+
+ * | Size OS MLE table (u64) |
+ * +----------------------------+
+ * | OS MLE table |
+ * +--------------------------- +
+ * | Size OS SINIT table (u64) |
+ * +----------------------------+
+ * | OS SINIT table |
+ * +----------------------------+
+ * | Size SINIT MLE table (u64) |
+ * +----------------------------+
+ * | SINIT MLE table |
+ * +----------------------------+
+ *
+ * NOTE: the table size fields include the 8 byte size field itself.
+ */
+enum {
+ TXT_BIOS_DATA_TABLE,
+ TXT_OS_MLE_DATA_TABLE,
+ TXT_OS_SINIT_DATA_TABLE,
+ TXT_SINIT_MLE_DATA_TABLE,
+ TXT_SINIT_TABLE_MAX,
+};
+
+/*
+ * Find the TPM v2 event log element in the TXT heap. This element contains
+ * the information about the size and location of the DRTM event log. Note
+ * this is a TXT specific structure.
+ *
+ * See:
+ * Intel Trusted Execution Technology -
+ * Measured Launch Environment Developer's Guide - Appendix C.
+ */
+static inline struct txt_heap_event_log_pointer2_1_element*
+txt_find_log2_1_element(struct txt_os_sinit_data *os_sinit_data)
+{
+#define ptr_after(p) ((void *)p + sizeof(*p))
+#define next_ext_elem(e) ((void *)e + e->size)
+ /* The extended element array is at the end of this table */
+ struct txt_heap_ext_data_element *ext_elem = ptr_after(os_sinit_data);
+
+ while (ext_elem->type != TXT_HEAP_EXTDATA_TYPE_END) {
+ if (ext_elem->type == TXT_HEAP_EXTDATA_TYPE_EVENT_LOG_POINTER2_1)
+ return ptr_after(ext_elem);
+
+ ext_elem = next_ext_elem(ext_elem);
+ }
+
+ return NULL;
+}
+
+#endif /* !__ASSEMBLER__ */
+
+#endif /* _ASM_X86_TXT_H */
--
2.47.3
^ permalink raw reply related
* [PATCH v16 19/38] x86: Secure Launch main header file
From: Ross Philipson @ 2026-05-15 21:13 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,
daniel.kiper, andrew.cooper3, trenchboot-devel
In-Reply-To: <20260515211410.31440-1-ross.philipson@gmail.com>
Introduce the main Secure Launch header file. This header file
contains the platform neutral feature definitions:
- Secure Launch error codes
- DRTM TPM event logging definitions and helper functions
- Other feature specific structures and definitions
Signed-off-by: Ross Philipson <ross.philipson@gmail.com>
---
include/linux/slaunch.h | 262 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 262 insertions(+)
create mode 100644 include/linux/slaunch.h
diff --git a/include/linux/slaunch.h b/include/linux/slaunch.h
new file mode 100644
index 000000000000..7874a49c27b5
--- /dev/null
+++ b/include/linux/slaunch.h
@@ -0,0 +1,262 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Main Secure Launch header file.
+ *
+ * Copyright (c) 2026 Apertus Solutions, LLC
+ * Copyright (c) 2026, Oracle and/or its affiliates.
+ */
+
+#ifndef _LINUX_SLAUNCH_H
+#define _LINUX_SLAUNCH_H
+
+#include <asm/txt.h>
+
+/*
+ * Secure Launch Defined State Flags
+ */
+#define SL_FLAG_ACTIVE 0x00000001
+#define SL_FLAG_ARCH_TXT 0x00000002
+
+/*
+ * Secure Launch CPU Type
+ */
+#define SL_CPU_INTEL 1
+
+#define __SL32_CS 0x0008
+#define __SL32_DS 0x0010
+#define __SL64_CS 0x0018
+
+/*
+ * Secure Launch Defined Error Codes used in MLE-initiated TXT resets.
+ *
+ * Intel Trusted Execution Technology (TXT) Software Development Guide
+ * Appendix I - ACM Error Codes
+ */
+#define SL_ERROR_GENERIC 0xc0008001
+#define SL_ERROR_TPM_INIT 0xc0008002
+#define SL_ERROR_TPM_INVALID_LOG20 0xc0008003
+#define SL_ERROR_TPM_LOGGING_FAILED 0xc0008004
+#define SL_ERROR_REGION_STRADDLE_4GB 0xc0008005
+#define SL_ERROR_TPM_EXTEND 0xc0008006
+#define SL_ERROR_MTRR_INV_VCNT 0xc0008007
+#define SL_ERROR_MTRR_INV_DEF_TYPE 0xc0008008
+#define SL_ERROR_MTRR_INV_BASE 0xc0008009
+#define SL_ERROR_MTRR_INV_MASK 0xc000800a
+#define SL_ERROR_MSR_INV_MISC_EN 0xc000800b
+#define SL_ERROR_INV_AP_INTERRUPT 0xc000800c
+#define SL_ERROR_INTEGER_OVERFLOW 0xc000800d
+#define SL_ERROR_HEAP_WALK 0xc000800e
+#define SL_ERROR_HEAP_MAP 0xc000800f
+#define SL_ERROR_REGION_ABOVE_4GB 0xc0008010
+#define SL_ERROR_HEAP_INVALID_DMAR 0xc0008011
+#define SL_ERROR_HEAP_DMAR_SIZE 0xc0008012
+#define SL_ERROR_HEAP_DMAR_MAP 0xc0008013
+#define SL_ERROR_HI_PMR_BASE 0xc0008014
+#define SL_ERROR_HI_PMR_SIZE 0xc0008015
+#define SL_ERROR_LO_PMR_BASE 0xc0008016
+#define SL_ERROR_LO_PMR_MLE 0xc0008017
+#define SL_ERROR_INITRD_TOO_BIG 0xc0008018
+#define SL_ERROR_HEAP_ZERO_OFFSET 0xc0008019
+#define SL_ERROR_WAKE_BLOCK_TOO_SMALL 0xc000801a
+#define SL_ERROR_MLE_BUFFER_OVERLAP 0xc000801b
+#define SL_ERROR_BUFFER_BEYOND_PMR 0xc000801c
+#define SL_ERROR_OS_SINIT_BAD_VERSION 0xc000801d
+#define SL_ERROR_EVENTLOG_MAP 0xc000801e
+#define SL_ERROR_TPM_INVALID_ALGS 0xc000801f
+#define SL_ERROR_TPM_EVENT_COUNT 0xc0008020
+#define SL_ERROR_TPM_INVALID_EVENT 0xc0008021
+#define SL_ERROR_INVALID_SLRT 0xc0008022
+#define SL_ERROR_SLRT_MISSING_ENTRY 0xc0008023
+#define SL_ERROR_SLRT_MAP 0xc0008024
+
+/*
+ * Secure Launch Defined Limits
+ */
+#define SL_MAX_CPUS 512
+#define SL_BOOT_STACK_SIZE 128
+
+/*
+ * Secure Launch event log entry type. The TXT specification defines the
+ * base event value as 0x400 for DRTM values.
+ *
+ * Intel Trusted Execution Technology (TXT) Software Development Guide
+ * Appendix F - TPM Event Log
+ */
+#define SL_EVTYPE_BASE 0x400
+#define SL_EVTYPE_SECURE_LAUNCH (SL_EVTYPE_BASE + 0x102)
+
+/*
+ * MLE scratch area offsets within TXT OS-MLE SL defined portion of the heap.
+ */
+#define SL_SCRATCH_AP_EBX 0
+#define SL_SCRATCH_AP_JMP_OFFSET 4
+#define SL_SCRATCH_AP_STACKS_OFFSET 8
+
+#ifndef __ASSEMBLER__
+
+#include <linux/io.h>
+#include <linux/tpm_eventlog.h>
+
+/*
+ * Secure Launch AP stack and monitor block
+ */
+struct sl_ap_stack_and_monitor {
+ u32 monitor;
+ u32 cache_pad[15];
+ u32 stack_pad[15];
+ u32 apicid;
+} __packed;
+
+/*
+ * Secure Launch AP wakeup information fetched in SMP boot code.
+ */
+struct sl_ap_wake_info {
+ u32 ap_wake_block;
+ u32 ap_wake_block_size;
+ u32 ap_jmp_offset;
+ u32 ap_stacks_offset;
+};
+
+/*
+ * TXT Heap Map structure
+ */
+struct sl_txt_heap_info {
+ u64 size;
+ u64 offset;
+};
+
+/*
+ * Secure Launch defined OS/MLE TXT Heap table
+ *
+ * This table is defined at the top level by the TXT specification
+ * but the format of this structure is implementation specific.
+ *
+ * Intel Trusted Execution Technology (TXT) Software Development Guide
+ * Appendix C - Intel TXT Heap Memory
+ */
+struct txt_os_mle_data {
+ u32 version;
+ u32 reserved;
+ u64 slrt;
+ u64 txt_info;
+ u32 ap_wake_block;
+ u32 ap_wake_block_size;
+ u8 mle_scratch[64];
+} __packed;
+
+#ifdef CONFIG_SECURE_LAUNCH
+
+/*
+ * TPM event logging functions.
+ */
+
+/*
+ * Log a TPM v1 formatted event to the given DRTM event log.
+ */
+static inline int tpm_log_event(void *evtlog_base, u32 evtlog_size,
+ u32 event_size, void *event)
+{
+ struct tpm_event_log_header *evtlog =
+ (struct tpm_event_log_header *)evtlog_base;
+
+ if (memcmp(evtlog->signature, TPM_EVTLOG_SIGNATURE,
+ sizeof(TPM_EVTLOG_SIGNATURE)))
+ return -EINVAL;
+
+ if (evtlog->container_size > evtlog_size)
+ return -EINVAL;
+
+ if (evtlog->next_event_offset + event_size > evtlog->container_size)
+ return -E2BIG;
+
+ memcpy(evtlog_base + evtlog->next_event_offset, event, event_size);
+ evtlog->next_event_offset += event_size;
+
+ return 0;
+}
+
+/*
+ * Log a TPM v2 formatted event to the given DRTM event log.
+ */
+static inline int tpm2_log_event(struct txt_heap_event_log_pointer2_1_element *elem,
+ void *evtlog_base, u32 evtlog_size,
+ u32 event_size, void *event)
+{
+ struct tcg_pcr_event *header =
+ (struct tcg_pcr_event *)evtlog_base;
+
+ /* Has to be at least big enough for the signature */
+ if (header->event_size < sizeof(TCG_SPECID_SIG))
+ return -EINVAL;
+
+ if (memcmp((u8 *)header + sizeof(struct tcg_pcr_event),
+ TCG_SPECID_SIG, sizeof(TCG_SPECID_SIG)))
+ return -EINVAL;
+
+ if (elem->allocated_event_container_size > evtlog_size)
+ return -EINVAL;
+
+ if (elem->next_record_offset + event_size >
+ elem->allocated_event_container_size)
+ return -E2BIG;
+
+ memcpy(evtlog_base + elem->next_record_offset, event, event_size);
+ elem->next_record_offset += event_size;
+
+ return 0;
+}
+
+/*
+ * External functions available in mainline kernel.
+ */
+void slaunch_setup(void);
+void slaunch_fixup_ap_wake_vector(void);
+u32 slaunch_get_flags(void);
+struct sl_txt_heap_info *slaunch_txt_get_heap_map(void);
+void *slaunch_txt_get_heap_table(void *heap, u8 index);
+struct sl_ap_wake_info *slaunch_get_ap_wake_info(void);
+struct acpi_table_header *slaunch_get_dmar_table(struct acpi_table_header *dmar);
+void __noreturn slaunch_reset(void *ctx, const char *msg, u64 error);
+void slaunch_finalize(int do_sexit);
+
+static inline bool slaunch_is_txt_launch(void)
+{
+ u32 mask = SL_FLAG_ACTIVE | SL_FLAG_ARCH_TXT;
+
+ return (slaunch_get_flags() & mask) == mask;
+}
+
+#else
+
+static inline void slaunch_setup(void)
+{
+}
+
+static inline void slaunch_fixup_ap_wake_vector(void)
+{
+}
+
+static inline u32 slaunch_get_flags(void)
+{
+ return 0;
+}
+
+static inline struct acpi_table_header *slaunch_get_dmar_table(struct acpi_table_header *dmar)
+{
+ return dmar;
+}
+
+static inline void slaunch_finalize(int do_sexit)
+{
+}
+
+static inline bool slaunch_is_txt_launch(void)
+{
+ return false;
+}
+
+#endif /* !CONFIG_SECURE_LAUNCH */
+
+#endif /* !__ASSEMBLER__ */
+
+#endif /* _LINUX_SLAUNCH_H */
--
2.47.3
^ 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