diff mbox series

[v14,18/19] x86: Secure Launch late initcall platform module

Message ID 20250421162712.77452-19-ross.philipson@oracle.com
State New
Headers show
Series x86: Trenchboot secure dynamic launch Linux kernel support | expand

Commit Message

Ross Philipson April 21, 2025, 4:27 p.m. UTC
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
access to TXT register fields on Intel along with the fetching of
and writing events to the late launch TPM log.

Signed-off-by: Daniel P. Smith <dpsmith@apertussolutions.com>
Signed-off-by: garnetgrimm <grimmg@ainfosec.com>
Signed-off-by: Ross Philipson <ross.philipson@oracle.com>
---
 arch/x86/kernel/Makefile   |   1 +
 arch/x86/kernel/slmodule.c | 508 +++++++++++++++++++++++++++++++++++++
 2 files changed, 509 insertions(+)
 create mode 100644 arch/x86/kernel/slmodule.c

Comments

Andy Lutomirski April 28, 2025, 5:38 p.m. UTC | #1
> On Apr 21, 2025, at 9:36 AM, Ross Philipson <ross.philipson@oracle.com> wrote:
>
> 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.

I read through some of the TPM and TXT docs, and I haven’t found a
clear explanation of exactly what gets hashed into which PCR.  (Mostly
because the docs are full of TXT-specific terms.)

But I’m really struggling to understand how the security model ends up
being consistent with this late_initcall thing. We measure some state
into the event log, and then we do a whole bunch of things (everything
from the very beginning of loading the kernel proper to the whenever
in the late_initcall stage this code runs), and then we actually
extend the PCRs.  It seems to me that this may involve a whole lot of
crossing fingers that an attacker can’t find a way to get the kernel
to execute code that changes the event log in memory prior to
extending PCRs such that attacker-controlled values get written.  Even
if the design is, in principle, sound, the attack surface seems much,
much larger than it deserves to be.

Is there some reason for all this complexity instead of extending the
PCRs at the early stage when the measurements are taken?


>
> The platform module also registers the securityfs nodes to allow
> access to TXT register fields on Intel along with the fetching of
> and writing events to the late launch TPM log.
>
> Signed-off-by: Daniel P. Smith <dpsmith@apertussolutions.com>
> Signed-off-by: garnetgrimm <grimmg@ainfosec.com>
> Signed-off-by: Ross Philipson <ross.philipson@oracle.com>
> ---
> arch/x86/kernel/Makefile   |   1 +
> arch/x86/kernel/slmodule.c | 508 +++++++++++++++++++++++++++++++++++++
> 2 files changed, 509 insertions(+)
> create mode 100644 arch/x86/kernel/slmodule.c
>
> diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
> index a7ca7ca16174..bed87b1c49a2 100644
> --- a/arch/x86/kernel/Makefile
> +++ b/arch/x86/kernel/Makefile
> @@ -83,6 +83,7 @@ obj-$(CONFIG_IA32_EMULATION)    += tls.o
> obj-y                += step.o
> obj-$(CONFIG_INTEL_TXT)        += tboot.o
> obj-$(CONFIG_SECURE_LAUNCH)    += slaunch.o
> +obj-$(CONFIG_SECURE_LAUNCH)    += slmodule.o
> obj-$(CONFIG_ISA_DMA_API)    += i8237.o
> obj-y                += stacktrace.o
> obj-y                += cpu/
> diff --git a/arch/x86/kernel/slmodule.c b/arch/x86/kernel/slmodule.c
> new file mode 100644
> index 000000000000..64010bac038c
> --- /dev/null
> +++ b/arch/x86/kernel/slmodule.c
> @@ -0,0 +1,508 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Secure Launch late validation/setup, securityfs exposure and finalization.
> + *
> + * Copyright (c) 2025 Apertus Solutions, LLC
> + * Copyright (c) 2025 Assured Information Security, Inc.
> + * Copyright (c) 2025, Oracle and/or its affiliates.
> + */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> +#include <linux/fs.h>
> +#include <linux/init.h>
> +#include <linux/linkage.h>
> +#include <linux/mm.h>
> +#include <linux/io.h>
> +#include <linux/uaccess.h>
> +#include <linux/security.h>
> +#include <linux/memblock.h>
> +#include <linux/tpm.h>
> +#include <asm/segment.h>
> +#include <asm/sections.h>
> +#include <crypto/sha2.h>
> +#include <linux/slr_table.h>
> +#include <linux/slaunch.h>
> +
> +/*
> + * The macro DECLARE_TXT_PUB_READ_U is used to read values from the TXT
> + * public registers as unsigned values.
> + */
> +#define DECLARE_TXT_PUB_READ_U(size, fmt, msg_size)            \
> +static ssize_t txt_pub_read_u##size(unsigned int offset,        \
> +        loff_t *read_offset,                    \
> +        size_t read_len,                    \
> +        char __user *buf)                    \
> +{                                    \
> +    char msg_buffer[msg_size];                    \
> +    u##size reg_value = 0;                        \
> +    void __iomem *txt;                        \
> +                                    \
> +    txt = ioremap(TXT_PUB_CONFIG_REGS_BASE,                \
> +            TXT_NR_CONFIG_PAGES * PAGE_SIZE);        \
> +    if (!txt)                            \
> +        return -EFAULT;                        \
> +    memcpy_fromio(&reg_value, txt + offset, sizeof(u##size));    \
> +    iounmap(txt);                            \
> +    snprintf(msg_buffer, msg_size, fmt, reg_value);            \
> +    return simple_read_from_buffer(buf, read_len, read_offset,    \
> +            &msg_buffer, msg_size);                \
> +}
> +
> +DECLARE_TXT_PUB_READ_U(8, "%#04x\n", 6);
> +DECLARE_TXT_PUB_READ_U(32, "%#010x\n", 12);
> +DECLARE_TXT_PUB_READ_U(64, "%#018llx\n", 20);
> +
> +#define DECLARE_TXT_FOPS(reg_name, reg_offset, reg_size)        \
> +static ssize_t txt_##reg_name##_read(struct file *flip,            \
> +        char __user *buf, size_t read_len, loff_t *read_offset)    \
> +{                                    \
> +    return txt_pub_read_u##reg_size(reg_offset, read_offset,    \
> +            read_len, buf);                    \
> +}                                    \
> +static const struct file_operations reg_name##_ops = {            \
> +    .read = txt_##reg_name##_read,                    \
> +}
> +
> +DECLARE_TXT_FOPS(sts, TXT_CR_STS, 64);
> +DECLARE_TXT_FOPS(ests, TXT_CR_ESTS, 8);
> +DECLARE_TXT_FOPS(errorcode, TXT_CR_ERRORCODE, 32);
> +DECLARE_TXT_FOPS(didvid, TXT_CR_DIDVID, 64);
> +DECLARE_TXT_FOPS(e2sts, TXT_CR_E2STS, 64);
> +DECLARE_TXT_FOPS(ver_emif, TXT_CR_VER_EMIF, 32);
> +DECLARE_TXT_FOPS(scratchpad, TXT_CR_SCRATCHPAD, 64);
> +
> +/*
> + * Securityfs exposure
> + */
> +struct memfile {
> +    char *name;
> +    void *addr;
> +    size_t size;
> +};
> +
> +static struct memfile sl_evtlog = {"eventlog", NULL, 0};
> +static void *txt_heap;
> +static struct txt_heap_event_log_pointer2_1_element *evtlog21;
> +static DEFINE_MUTEX(sl_evt_log_mutex);
> +static struct tcg_efi_specid_event_head *efi_head;
> +
> +static ssize_t sl_evtlog_read(struct file *file, char __user *buf,
> +                  size_t count, loff_t *pos)
> +{
> +    ssize_t size;
> +
> +    if (!sl_evtlog.addr)
> +        return 0;
> +
> +    mutex_lock(&sl_evt_log_mutex);
> +    size = simple_read_from_buffer(buf, count, pos, sl_evtlog.addr,
> +                       sl_evtlog.size);
> +    mutex_unlock(&sl_evt_log_mutex);
> +
> +    return size;
> +}
> +
> +static ssize_t sl_evtlog_write(struct file *file, const char __user *buf,
> +                   size_t datalen, loff_t *ppos)
> +{
> +    ssize_t result;
> +    char *data;
> +
> +    if (!sl_evtlog.addr)
> +        return 0;
> +
> +    /* No partial writes. */
> +    result = -EINVAL;
> +    if (*ppos != 0)
> +        goto out;
> +
> +    data = memdup_user(buf, datalen);
> +    if (IS_ERR(data)) {
> +        result = PTR_ERR(data);
> +        goto out;
> +    }
> +
> +    mutex_lock(&sl_evt_log_mutex);
> +    if (evtlog21)
> +        result = tpm2_log_event(evtlog21, sl_evtlog.addr,
> +                    sl_evtlog.size, datalen, data);
> +    else
> +        result = tpm_log_event(sl_evtlog.addr, sl_evtlog.size,
> +                       datalen, data);
> +    mutex_unlock(&sl_evt_log_mutex);
> +
> +    kfree(data);
> +out:
> +    return result;
> +}
> +
> +static const struct file_operations sl_evtlog_ops = {
> +    .read = sl_evtlog_read,
> +    .write = sl_evtlog_write,
> +    .llseek    = default_llseek,
> +};
> +
> +struct sfs_file {
> +    const char *name;
> +    const struct file_operations *fops;
> +};
> +
> +#define SL_TXT_ENTRY_COUNT    7
> +static const struct sfs_file sl_txt_files[] = {
> +    { "sts", &sts_ops },
> +    { "ests", &ests_ops },
> +    { "errorcode", &errorcode_ops },
> +    { "didvid", &didvid_ops },
> +    { "ver_emif", &ver_emif_ops },
> +    { "scratchpad", &scratchpad_ops },
> +    { "e2sts", &e2sts_ops }
> +};
> +
> +/* sysfs file handles */
> +static struct dentry *slaunch_dir;
> +static struct dentry *event_file;
> +static struct dentry *txt_dir;
> +static struct dentry *txt_entries[SL_TXT_ENTRY_COUNT];
> +
> +static long slaunch_expose_securityfs(void)
> +{
> +    long ret = 0;
> +    int i;
> +
> +    slaunch_dir = securityfs_create_dir("slaunch", NULL);
> +    if (IS_ERR(slaunch_dir))
> +        return PTR_ERR(slaunch_dir);
> +
> +    if (slaunch_get_flags() & SL_FLAG_ARCH_TXT) {
> +        txt_dir = securityfs_create_dir("txt", slaunch_dir);
> +        if (IS_ERR(txt_dir)) {
> +            ret = PTR_ERR(txt_dir);
> +            goto remove_slaunch;
> +        }
> +
> +        for (i = 0; i < ARRAY_SIZE(sl_txt_files); i++) {
> +            txt_entries[i] = securityfs_create_file(
> +                        sl_txt_files[i].name, 0440,
> +                        txt_dir, NULL,
> +                        sl_txt_files[i].fops);
> +            if (IS_ERR(txt_entries[i])) {
> +                ret = PTR_ERR(txt_entries[i]);
> +                goto remove_files;
> +            }
> +        }
> +    }
> +
> +    if (sl_evtlog.addr) {
> +        event_file = securityfs_create_file(sl_evtlog.name, 0440,
> +                            slaunch_dir, NULL,
> +                            &sl_evtlog_ops);
> +        if (IS_ERR(event_file)) {
> +            ret = PTR_ERR(event_file);
> +            goto remove_files;
> +        }
> +    }
> +
> +    return 0;
> +
> +remove_files:
> +    if (slaunch_get_flags() & SL_FLAG_ARCH_TXT) {
> +        while (--i >= 0)
> +            securityfs_remove(txt_entries[i]);
> +        securityfs_remove(txt_dir);
> +    }
> +
> +remove_slaunch:
> +    securityfs_remove(slaunch_dir);
> +
> +    return ret;
> +}
> +
> +static void slaunch_teardown_securityfs(void)
> +{
> +    int i;
> +
> +    securityfs_remove(event_file);
> +    if (sl_evtlog.addr) {
> +        memunmap(sl_evtlog.addr);
> +        sl_evtlog.addr = NULL;
> +    }
> +    sl_evtlog.size = 0;
> +
> +    if (slaunch_get_flags() & SL_FLAG_ARCH_TXT) {
> +        for (i = 0; i < ARRAY_SIZE(sl_txt_files); i++)
> +            securityfs_remove(txt_entries[i]);
> +
> +        securityfs_remove(txt_dir);
> +
> +        if (txt_heap) {
> +            memunmap(txt_heap);
> +            txt_heap = NULL;
> +        }
> +    }
> +
> +    securityfs_remove(slaunch_dir);
> +}
> +
> +static void slaunch_intel_evtlog(void __iomem *txt)
> +{
> +    struct slr_entry_log_info *log_info;
> +    struct txt_os_mle_data *params;
> +    struct slr_table *slrt;
> +    void *os_sinit_data;
> +    u64 base, size;
> +
> +    memcpy_fromio(&base, txt + TXT_CR_HEAP_BASE, sizeof(base));
> +    memcpy_fromio(&size, txt + TXT_CR_HEAP_SIZE, sizeof(size));
> +
> +    /* now map TXT heap */
> +    txt_heap = memremap(base, size, MEMREMAP_WB);
> +    if (!txt_heap)
> +        slaunch_txt_reset(txt, "Error failed to memremap TXT heap\n",
> +                  SL_ERROR_HEAP_MAP);
> +
> +    params = (struct txt_os_mle_data *)txt_os_mle_data_start(txt_heap);
> +
> +    /* Get the SLRT and remap it */
> +    slrt = memremap(params->slrt, sizeof(*slrt), MEMREMAP_WB);
> +    if (!slrt)
> +        slaunch_txt_reset(txt, "Error failed to memremap SLR Table\n",
> +                  SL_ERROR_SLRT_MAP);
> +    size = slrt->size;
> +    memunmap(slrt);
> +
> +    slrt = memremap(params->slrt, size, MEMREMAP_WB);
> +    if (!slrt)
> +        slaunch_txt_reset(txt, "Error failed to memremap SLR Table\n",
> +                  SL_ERROR_SLRT_MAP);
> +
> +    log_info = slr_next_entry_by_tag(slrt, NULL, SLR_ENTRY_LOG_INFO);
> +    if (!log_info)
> +        slaunch_txt_reset(txt, "Error failed to memremap SLR Table\n",
> +                  SL_ERROR_SLRT_MISSING_ENTRY);
> +
> +    sl_evtlog.size = log_info->size;
> +    sl_evtlog.addr = memremap(log_info->addr, log_info->size,
> +                  MEMREMAP_WB);
> +    if (!sl_evtlog.addr)
> +        slaunch_txt_reset(txt, "Error failed to memremap TPM event log\n",
> +                  SL_ERROR_EVENTLOG_MAP);
> +
> +    memunmap(slrt);
> +
> +    /* Determine if this is TPM 1.2 or 2.0 event log */
> +    if (memcmp(sl_evtlog.addr + sizeof(struct tcg_pcr_event),
> +            TCG_SPECID_SIG, sizeof(TCG_SPECID_SIG)))
> +        return; /* looks like it is not 2.0 */
> +
> +    /* For TPM 2.0 logs, the extended heap element must be located */
> +    os_sinit_data = txt_os_sinit_data_start(txt_heap);
> +
> +    evtlog21 = tpm2_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_txt_reset(txt, "Error failed to find TPM20 event log element\n",
> +                  SL_ERROR_TPM_INVALID_LOG20);
> +
> +    /* Save pointer to the EFI SpecID log header */
> +    efi_head = (struct tcg_efi_specid_event_head *)(sl_evtlog.addr + sizeof(struct tcg_pcr_event));
> +}
> +
> +static void slaunch_tpm2_extend_event(struct tpm_chip *tpm, void __iomem *txt,
> +                      struct tcg_pcr_event2_head *event)
> +{
> +    u16 *alg_id_field = (u16 *)((u8 *)event + sizeof(*event));
> +    struct tpm_digest *digests;
> +    u8 *dptr;
> +    int ret;
> +    u32 i;
> +
> +    /*
> +     * Early SL code ensured the TPM algorithm information passed via
> +     * the log is valid. Small sanity check here.
> +     */
> +    if (event->count != efi_head->num_algs)
> +        slaunch_txt_reset(txt, "Event digest count mismatch with event log\n",
> +                  SL_ERROR_TPM_EVENT_COUNT);
> +
> +    digests = kzalloc(efi_head->num_algs * sizeof(*digests), GFP_KERNEL);
> +    if (!digests)
> +        slaunch_txt_reset(txt, "Failed to allocate array of digests\n",
> +                  SL_ERROR_GENERIC);
> +
> +    for (i = 0; i < event->count; i++) {
> +        dptr = (u8 *)alg_id_field + sizeof(u16);
> +
> +        /* Setup each digest for the extend */
> +        digests[i].alg_id = efi_head->digest_sizes[i].alg_id;
> +        memcpy(&digests[i].digest[0], dptr,
> +            efi_head->digest_sizes[i].digest_size);
> +
> +        alg_id_field = (u16 *)((u8 *)alg_id_field + sizeof(u16) +
> +                efi_head->digest_sizes[i].digest_size);
> +    }
> +
> +    ret = tpm_pcr_extend(tpm, event->pcr_idx, digests);
> +    if (ret) {
> +        pr_err("Error extending TPM20 PCR, result: %d\n", ret);
> +        slaunch_txt_reset(txt, "Failed to extend TPM20 PCR\n",
> +                  SL_ERROR_TPM_EXTEND);
> +    }
> +
> +    kfree(digests);
> +}
> +
> +static void slaunch_tpm2_extend(struct tpm_chip *tpm, void __iomem *txt)
> +{
> +    struct tcg_pcr_event *event_header;
> +    struct tcg_pcr_event2_head *event;
> +    int start = 0, end = 0, size;
> +
> +    event_header = (struct tcg_pcr_event *)(sl_evtlog.addr +
> +                        evtlog21->first_record_offset);
> +
> +    /* Skip first TPM 1.2 event to get to first TPM 2.0 event */
> +    event = (struct tcg_pcr_event2_head *)((u8 *)event_header + sizeof(*event_header) +
> +                           event_header->event_size);
> +
> +    while ((void  *)event < sl_evtlog.addr + evtlog21->next_record_offset) {
> +        size = __calc_tpm2_event_size(event, event_header, false);
> +        if (!size)
> +            slaunch_txt_reset(txt, "TPM20 invalid event in event log\n",
> +                      SL_ERROR_TPM_INVALID_EVENT);
> +
> +        /*
> +         * Marker events indicate where the Secure Launch early stub
> +         * started and ended adding post launch events. As they are
> +         * encountered, switch the event type to NO_ACTION so they
> +         * ignored in when the event log is processed since they are
> +         * not really measurements.
> +         */
> +        if (event->event_type == TXT_EVTYPE_SLAUNCH_END) {
> +            event->event_type = NO_ACTION;
> +            end = 1;
> +            break;
> +        } else if (event->event_type == TXT_EVTYPE_SLAUNCH_START) {
> +            event->event_type = NO_ACTION;
> +            start = 1;
> +            goto next;
> +        }
> +
> +        if (start)
> +            slaunch_tpm2_extend_event(tpm, txt, event);
> +
> +next:
> +        event = (struct tcg_pcr_event2_head *)((u8 *)event + size);
> +    }
> +
> +    if (!start || !end)
> +        slaunch_txt_reset(txt, "Missing start or end events for extending TPM20 PCRs\n",
> +                  SL_ERROR_TPM_EXTEND);
> +}
> +
> +static void slaunch_tpm_extend(struct tpm_chip *tpm, void __iomem *txt)
> +{
> +    struct tpm_event_log_header *event_header;
> +    struct tcg_pcr_event *event;
> +    struct tpm_digest digest;
> +    int start = 0, end = 0;
> +    int size, ret;
> +
> +    event_header = (struct tpm_event_log_header *)sl_evtlog.addr;
> +    event = (struct tcg_pcr_event *)((u8 *)event_header +
> +                sizeof(*event_header));
> +
> +    while ((void  *)event < sl_evtlog.addr + event_header->next_event_offset) {
> +        size = sizeof(*event) + event->event_size;
> +
> +        /*
> +         * See comments in slaunch_tpm2_extend() concerning these special
> +         * event types.
> +         */
> +        if (event->event_type == TXT_EVTYPE_SLAUNCH_END) {
> +            event->event_type = NO_ACTION;
> +            end = 1;
> +            break;
> +        } else if (event->event_type == TXT_EVTYPE_SLAUNCH_START) {
> +            event->event_type = NO_ACTION;
> +            start = 1;
> +            goto next;
> +        }
> +
> +        if (start) {
> +            memset(&digest.digest[0], 0, TPM_MAX_DIGEST_SIZE);
> +            digest.alg_id = TPM_ALG_SHA1;
> +            memcpy(&digest.digest[0], &event->digest[0],
> +                   SHA1_DIGEST_SIZE);
> +
> +            ret = tpm_pcr_extend(tpm, event->pcr_idx, &digest);
> +            if (ret) {
> +                pr_err("Error extending TPM12 PCR, result: %d\n", ret);
> +                slaunch_txt_reset(txt, "Failed to extend TPM12 PCR\n",
> +                          SL_ERROR_TPM_EXTEND);
> +            }
> +        }
> +
> +next:
> +        event = (struct tcg_pcr_event *)((u8 *)event + size);
> +    }
> +
> +    if (!start || !end)
> +        slaunch_txt_reset(txt, "Missing start or end events for extending TPM12 PCRs\n",
> +                  SL_ERROR_TPM_EXTEND);
> +}
> +
> +static void slaunch_pcr_extend(void __iomem *txt)
> +{
> +    struct tpm_chip *tpm;
> +    int rc;
> +
> +    tpm = tpm_default_chip();
> +    if (!tpm)
> +        slaunch_txt_reset(txt, "Could not get default TPM chip\n",
> +                  SL_ERROR_TPM_INIT);
> +
> +    rc = tpm_chip_set_locality(tpm, 2);
> +    if (rc)
> +        slaunch_txt_reset(txt, "Could not set TPM chip locality 2\n",
> +                  SL_ERROR_TPM_INIT);
> +
> +    if (evtlog21)
> +        slaunch_tpm2_extend(tpm, txt);
> +    else
> +        slaunch_tpm_extend(tpm, txt);
> +}
> +
> +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_pcr_extend(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.39.3
>
Daniel P. Smith April 30, 2025, 1:40 a.m. UTC | #2
On 4/28/25 13:38, Andy Lutomirski wrote:
>> On Apr 21, 2025, at 9:36 AM, Ross Philipson <ross.philipson@oracle.com> wrote:
>>
>> 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.
> 
> I read through some of the TPM and TXT docs, and I haven’t found a
> clear explanation of exactly what gets hashed into which PCR.  (Mostly
> because the docs are full of TXT-specific terms.)


For Intel TXT, the general approach is detailed in section 1.10.2 of the 
TXT Software Development Guide[1]. I point you at the Detail and 
Authorities Usage section because the ability to do Legacy Usage has 
been unavailable for some time.

In section 1.10.2.1, the dialogue explains how and what the initial 
measurement is into PCR17. After that is Table 1, which provides a 
listing of all the measurements the ACM could make before starting the 
MLE. Just as an FYI, on Gen 9 and later, the STM measurement will be 
present and will be the hash of the PPAM module.[2]

Section 1.10.2.2 gives a similar treatment to PCR18.

[1] 
https://www.intel.com/content/dam/www/public/us/en/documents/guides/intel-txt-software-development-guide.pdf
[2] 
https://www.intel.com/content/dam/www/central-libraries/us/en/documents/drtm-based-computing-whitepaper.pdf


> But I’m really struggling to understand how the security model ends up
> being consistent with this late_initcall thing. We measure some state
> into the event log, and then we do a whole bunch of things (everything
> from the very beginning of loading the kernel proper to the whenever
> in the late_initcall stage this code runs), and then we actually
> extend the PCRs.  It seems to me that this may involve a whole lot of
> crossing fingers that an attacker can’t find a way to get the kernel
> to execute code that changes the event log in memory prior to
> extending PCRs such that attacker-controlled values get written.  Even
> if the design is, in principle, sound, the attack surface seems much,
> much larger than it deserves to be.


There is a more fundemental flaw to your scenario, but before covering 
that, consider what measurements could be tampered with that are made by 
the setup kernel:

  - Kernel Setup Data
  - TrenchBoot's SLRT
  - Boot Params
  - Command line
  - EFI Memory Map
  - EFI configuration items, populated by efi-stub (currently unused)
  - External Ramdisk

Outside of the case of an external ramdisk, the attacker can only 
pretend valid configuration data was passed to it.

Correct me if I am wrong, but I don't think that is what is bothering 
you. You are either concerned with one of two cases here. Either you are 
concerned that the attacker may be able to hide the loading of a corrupt 
kernel or that the attacker can corrupt the kernel after loading. In the 
first case, the answer is no; the attacker cannot. The kernel and the 
initrd if it was packed in the kernel are measured and sent to the TPM 
by the ACM running in cache-as-ram before execution begins.

The second case is the flawed scenario, a strawman, if you will. This is 
a runtime-integrity problem that is outside the scope/protections of 
load-time-integrity solutions such as SRTM and DRTM. If the correct 
kernel was loaded and measured, but an attacker already has a position 
in the system that they can corrupt the kernel before the user-space 
init process can be run, then they already won. The attacker is in fact 
already in a more superior position in the system that would not be 
detected or exposed by when config data measurements are sent to the TPM.


> Is there some reason for all this complexity instead of extending the
> PCRs at the early stage when the measurements are taken?


We did have TPM logic in the setup kernel at one point. Within their 
rights, the TPM maintainers took the position that the only TPM 
interface logic should be the existing driver. If we wanted to interface 
with the TPM, we must #include the existing code into the setup kernel. 
The problem is that the mainline TPM driver uses mainline kernel 
features not present in the setup kernel.

v/r,
dps

>>
>> The platform module also registers the securityfs nodes to allow
>> access to TXT register fields on Intel along with the fetching of
>> and writing events to the late launch TPM log.
>>
>> Signed-off-by: Daniel P. Smith <dpsmith@apertussolutions.com>
>> Signed-off-by: garnetgrimm <grimmg@ainfosec.com>
>> Signed-off-by: Ross Philipson <ross.philipson@oracle.com>
>> ---
>> arch/x86/kernel/Makefile   |   1 +
>> arch/x86/kernel/slmodule.c | 508 +++++++++++++++++++++++++++++++++++++
>> 2 files changed, 509 insertions(+)
>> create mode 100644 arch/x86/kernel/slmodule.c
>>
>> diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
>> index a7ca7ca16174..bed87b1c49a2 100644
>> --- a/arch/x86/kernel/Makefile
>> +++ b/arch/x86/kernel/Makefile
>> @@ -83,6 +83,7 @@ obj-$(CONFIG_IA32_EMULATION)    += tls.o
>> obj-y                += step.o
>> obj-$(CONFIG_INTEL_TXT)        += tboot.o
>> obj-$(CONFIG_SECURE_LAUNCH)    += slaunch.o
>> +obj-$(CONFIG_SECURE_LAUNCH)    += slmodule.o
>> obj-$(CONFIG_ISA_DMA_API)    += i8237.o
>> obj-y                += stacktrace.o
>> obj-y                += cpu/
>> diff --git a/arch/x86/kernel/slmodule.c b/arch/x86/kernel/slmodule.c
>> new file mode 100644
>> index 000000000000..64010bac038c
>> --- /dev/null
>> +++ b/arch/x86/kernel/slmodule.c
>> @@ -0,0 +1,508 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Secure Launch late validation/setup, securityfs exposure and finalization.
>> + *
>> + * Copyright (c) 2025 Apertus Solutions, LLC
>> + * Copyright (c) 2025 Assured Information Security, Inc.
>> + * Copyright (c) 2025, Oracle and/or its affiliates.
>> + */
>> +
>> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>> +
>> +#include <linux/fs.h>
>> +#include <linux/init.h>
>> +#include <linux/linkage.h>
>> +#include <linux/mm.h>
>> +#include <linux/io.h>
>> +#include <linux/uaccess.h>
>> +#include <linux/security.h>
>> +#include <linux/memblock.h>
>> +#include <linux/tpm.h>
>> +#include <asm/segment.h>
>> +#include <asm/sections.h>
>> +#include <crypto/sha2.h>
>> +#include <linux/slr_table.h>
>> +#include <linux/slaunch.h>
>> +
>> +/*
>> + * The macro DECLARE_TXT_PUB_READ_U is used to read values from the TXT
>> + * public registers as unsigned values.
>> + */
>> +#define DECLARE_TXT_PUB_READ_U(size, fmt, msg_size)            \
>> +static ssize_t txt_pub_read_u##size(unsigned int offset,        \
>> +        loff_t *read_offset,                    \
>> +        size_t read_len,                    \
>> +        char __user *buf)                    \
>> +{                                    \
>> +    char msg_buffer[msg_size];                    \
>> +    u##size reg_value = 0;                        \
>> +    void __iomem *txt;                        \
>> +                                    \
>> +    txt = ioremap(TXT_PUB_CONFIG_REGS_BASE,                \
>> +            TXT_NR_CONFIG_PAGES * PAGE_SIZE);        \
>> +    if (!txt)                            \
>> +        return -EFAULT;                        \
>> +    memcpy_fromio(&reg_value, txt + offset, sizeof(u##size));    \
>> +    iounmap(txt);                            \
>> +    snprintf(msg_buffer, msg_size, fmt, reg_value);            \
>> +    return simple_read_from_buffer(buf, read_len, read_offset,    \
>> +            &msg_buffer, msg_size);                \
>> +}
>> +
>> +DECLARE_TXT_PUB_READ_U(8, "%#04x\n", 6);
>> +DECLARE_TXT_PUB_READ_U(32, "%#010x\n", 12);
>> +DECLARE_TXT_PUB_READ_U(64, "%#018llx\n", 20);
>> +
>> +#define DECLARE_TXT_FOPS(reg_name, reg_offset, reg_size)        \
>> +static ssize_t txt_##reg_name##_read(struct file *flip,            \
>> +        char __user *buf, size_t read_len, loff_t *read_offset)    \
>> +{                                    \
>> +    return txt_pub_read_u##reg_size(reg_offset, read_offset,    \
>> +            read_len, buf);                    \
>> +}                                    \
>> +static const struct file_operations reg_name##_ops = {            \
>> +    .read = txt_##reg_name##_read,                    \
>> +}
>> +
>> +DECLARE_TXT_FOPS(sts, TXT_CR_STS, 64);
>> +DECLARE_TXT_FOPS(ests, TXT_CR_ESTS, 8);
>> +DECLARE_TXT_FOPS(errorcode, TXT_CR_ERRORCODE, 32);
>> +DECLARE_TXT_FOPS(didvid, TXT_CR_DIDVID, 64);
>> +DECLARE_TXT_FOPS(e2sts, TXT_CR_E2STS, 64);
>> +DECLARE_TXT_FOPS(ver_emif, TXT_CR_VER_EMIF, 32);
>> +DECLARE_TXT_FOPS(scratchpad, TXT_CR_SCRATCHPAD, 64);
>> +
>> +/*
>> + * Securityfs exposure
>> + */
>> +struct memfile {
>> +    char *name;
>> +    void *addr;
>> +    size_t size;
>> +};
>> +
>> +static struct memfile sl_evtlog = {"eventlog", NULL, 0};
>> +static void *txt_heap;
>> +static struct txt_heap_event_log_pointer2_1_element *evtlog21;
>> +static DEFINE_MUTEX(sl_evt_log_mutex);
>> +static struct tcg_efi_specid_event_head *efi_head;
>> +
>> +static ssize_t sl_evtlog_read(struct file *file, char __user *buf,
>> +                  size_t count, loff_t *pos)
>> +{
>> +    ssize_t size;
>> +
>> +    if (!sl_evtlog.addr)
>> +        return 0;
>> +
>> +    mutex_lock(&sl_evt_log_mutex);
>> +    size = simple_read_from_buffer(buf, count, pos, sl_evtlog.addr,
>> +                       sl_evtlog.size);
>> +    mutex_unlock(&sl_evt_log_mutex);
>> +
>> +    return size;
>> +}
>> +
>> +static ssize_t sl_evtlog_write(struct file *file, const char __user *buf,
>> +                   size_t datalen, loff_t *ppos)
>> +{
>> +    ssize_t result;
>> +    char *data;
>> +
>> +    if (!sl_evtlog.addr)
>> +        return 0;
>> +
>> +    /* No partial writes. */
>> +    result = -EINVAL;
>> +    if (*ppos != 0)
>> +        goto out;
>> +
>> +    data = memdup_user(buf, datalen);
>> +    if (IS_ERR(data)) {
>> +        result = PTR_ERR(data);
>> +        goto out;
>> +    }
>> +
>> +    mutex_lock(&sl_evt_log_mutex);
>> +    if (evtlog21)
>> +        result = tpm2_log_event(evtlog21, sl_evtlog.addr,
>> +                    sl_evtlog.size, datalen, data);
>> +    else
>> +        result = tpm_log_event(sl_evtlog.addr, sl_evtlog.size,
>> +                       datalen, data);
>> +    mutex_unlock(&sl_evt_log_mutex);
>> +
>> +    kfree(data);
>> +out:
>> +    return result;
>> +}
>> +
>> +static const struct file_operations sl_evtlog_ops = {
>> +    .read = sl_evtlog_read,
>> +    .write = sl_evtlog_write,
>> +    .llseek    = default_llseek,
>> +};
>> +
>> +struct sfs_file {
>> +    const char *name;
>> +    const struct file_operations *fops;
>> +};
>> +
>> +#define SL_TXT_ENTRY_COUNT    7
>> +static const struct sfs_file sl_txt_files[] = {
>> +    { "sts", &sts_ops },
>> +    { "ests", &ests_ops },
>> +    { "errorcode", &errorcode_ops },
>> +    { "didvid", &didvid_ops },
>> +    { "ver_emif", &ver_emif_ops },
>> +    { "scratchpad", &scratchpad_ops },
>> +    { "e2sts", &e2sts_ops }
>> +};
>> +
>> +/* sysfs file handles */
>> +static struct dentry *slaunch_dir;
>> +static struct dentry *event_file;
>> +static struct dentry *txt_dir;
>> +static struct dentry *txt_entries[SL_TXT_ENTRY_COUNT];
>> +
>> +static long slaunch_expose_securityfs(void)
>> +{
>> +    long ret = 0;
>> +    int i;
>> +
>> +    slaunch_dir = securityfs_create_dir("slaunch", NULL);
>> +    if (IS_ERR(slaunch_dir))
>> +        return PTR_ERR(slaunch_dir);
>> +
>> +    if (slaunch_get_flags() & SL_FLAG_ARCH_TXT) {
>> +        txt_dir = securityfs_create_dir("txt", slaunch_dir);
>> +        if (IS_ERR(txt_dir)) {
>> +            ret = PTR_ERR(txt_dir);
>> +            goto remove_slaunch;
>> +        }
>> +
>> +        for (i = 0; i < ARRAY_SIZE(sl_txt_files); i++) {
>> +            txt_entries[i] = securityfs_create_file(
>> +                        sl_txt_files[i].name, 0440,
>> +                        txt_dir, NULL,
>> +                        sl_txt_files[i].fops);
>> +            if (IS_ERR(txt_entries[i])) {
>> +                ret = PTR_ERR(txt_entries[i]);
>> +                goto remove_files;
>> +            }
>> +        }
>> +    }
>> +
>> +    if (sl_evtlog.addr) {
>> +        event_file = securityfs_create_file(sl_evtlog.name, 0440,
>> +                            slaunch_dir, NULL,
>> +                            &sl_evtlog_ops);
>> +        if (IS_ERR(event_file)) {
>> +            ret = PTR_ERR(event_file);
>> +            goto remove_files;
>> +        }
>> +    }
>> +
>> +    return 0;
>> +
>> +remove_files:
>> +    if (slaunch_get_flags() & SL_FLAG_ARCH_TXT) {
>> +        while (--i >= 0)
>> +            securityfs_remove(txt_entries[i]);
>> +        securityfs_remove(txt_dir);
>> +    }
>> +
>> +remove_slaunch:
>> +    securityfs_remove(slaunch_dir);
>> +
>> +    return ret;
>> +}
>> +
>> +static void slaunch_teardown_securityfs(void)
>> +{
>> +    int i;
>> +
>> +    securityfs_remove(event_file);
>> +    if (sl_evtlog.addr) {
>> +        memunmap(sl_evtlog.addr);
>> +        sl_evtlog.addr = NULL;
>> +    }
>> +    sl_evtlog.size = 0;
>> +
>> +    if (slaunch_get_flags() & SL_FLAG_ARCH_TXT) {
>> +        for (i = 0; i < ARRAY_SIZE(sl_txt_files); i++)
>> +            securityfs_remove(txt_entries[i]);
>> +
>> +        securityfs_remove(txt_dir);
>> +
>> +        if (txt_heap) {
>> +            memunmap(txt_heap);
>> +            txt_heap = NULL;
>> +        }
>> +    }
>> +
>> +    securityfs_remove(slaunch_dir);
>> +}
>> +
>> +static void slaunch_intel_evtlog(void __iomem *txt)
>> +{
>> +    struct slr_entry_log_info *log_info;
>> +    struct txt_os_mle_data *params;
>> +    struct slr_table *slrt;
>> +    void *os_sinit_data;
>> +    u64 base, size;
>> +
>> +    memcpy_fromio(&base, txt + TXT_CR_HEAP_BASE, sizeof(base));
>> +    memcpy_fromio(&size, txt + TXT_CR_HEAP_SIZE, sizeof(size));
>> +
>> +    /* now map TXT heap */
>> +    txt_heap = memremap(base, size, MEMREMAP_WB);
>> +    if (!txt_heap)
>> +        slaunch_txt_reset(txt, "Error failed to memremap TXT heap\n",
>> +                  SL_ERROR_HEAP_MAP);
>> +
>> +    params = (struct txt_os_mle_data *)txt_os_mle_data_start(txt_heap);
>> +
>> +    /* Get the SLRT and remap it */
>> +    slrt = memremap(params->slrt, sizeof(*slrt), MEMREMAP_WB);
>> +    if (!slrt)
>> +        slaunch_txt_reset(txt, "Error failed to memremap SLR Table\n",
>> +                  SL_ERROR_SLRT_MAP);
>> +    size = slrt->size;
>> +    memunmap(slrt);
>> +
>> +    slrt = memremap(params->slrt, size, MEMREMAP_WB);
>> +    if (!slrt)
>> +        slaunch_txt_reset(txt, "Error failed to memremap SLR Table\n",
>> +                  SL_ERROR_SLRT_MAP);
>> +
>> +    log_info = slr_next_entry_by_tag(slrt, NULL, SLR_ENTRY_LOG_INFO);
>> +    if (!log_info)
>> +        slaunch_txt_reset(txt, "Error failed to memremap SLR Table\n",
>> +                  SL_ERROR_SLRT_MISSING_ENTRY);
>> +
>> +    sl_evtlog.size = log_info->size;
>> +    sl_evtlog.addr = memremap(log_info->addr, log_info->size,
>> +                  MEMREMAP_WB);
>> +    if (!sl_evtlog.addr)
>> +        slaunch_txt_reset(txt, "Error failed to memremap TPM event log\n",
>> +                  SL_ERROR_EVENTLOG_MAP);
>> +
>> +    memunmap(slrt);
>> +
>> +    /* Determine if this is TPM 1.2 or 2.0 event log */
>> +    if (memcmp(sl_evtlog.addr + sizeof(struct tcg_pcr_event),
>> +            TCG_SPECID_SIG, sizeof(TCG_SPECID_SIG)))
>> +        return; /* looks like it is not 2.0 */
>> +
>> +    /* For TPM 2.0 logs, the extended heap element must be located */
>> +    os_sinit_data = txt_os_sinit_data_start(txt_heap);
>> +
>> +    evtlog21 = tpm2_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_txt_reset(txt, "Error failed to find TPM20 event log element\n",
>> +                  SL_ERROR_TPM_INVALID_LOG20);
>> +
>> +    /* Save pointer to the EFI SpecID log header */
>> +    efi_head = (struct tcg_efi_specid_event_head *)(sl_evtlog.addr + sizeof(struct tcg_pcr_event));
>> +}
>> +
>> +static void slaunch_tpm2_extend_event(struct tpm_chip *tpm, void __iomem *txt,
>> +                      struct tcg_pcr_event2_head *event)
>> +{
>> +    u16 *alg_id_field = (u16 *)((u8 *)event + sizeof(*event));
>> +    struct tpm_digest *digests;
>> +    u8 *dptr;
>> +    int ret;
>> +    u32 i;
>> +
>> +    /*
>> +     * Early SL code ensured the TPM algorithm information passed via
>> +     * the log is valid. Small sanity check here.
>> +     */
>> +    if (event->count != efi_head->num_algs)
>> +        slaunch_txt_reset(txt, "Event digest count mismatch with event log\n",
>> +                  SL_ERROR_TPM_EVENT_COUNT);
>> +
>> +    digests = kzalloc(efi_head->num_algs * sizeof(*digests), GFP_KERNEL);
>> +    if (!digests)
>> +        slaunch_txt_reset(txt, "Failed to allocate array of digests\n",
>> +                  SL_ERROR_GENERIC);
>> +
>> +    for (i = 0; i < event->count; i++) {
>> +        dptr = (u8 *)alg_id_field + sizeof(u16);
>> +
>> +        /* Setup each digest for the extend */
>> +        digests[i].alg_id = efi_head->digest_sizes[i].alg_id;
>> +        memcpy(&digests[i].digest[0], dptr,
>> +            efi_head->digest_sizes[i].digest_size);
>> +
>> +        alg_id_field = (u16 *)((u8 *)alg_id_field + sizeof(u16) +
>> +                efi_head->digest_sizes[i].digest_size);
>> +    }
>> +
>> +    ret = tpm_pcr_extend(tpm, event->pcr_idx, digests);
>> +    if (ret) {
>> +        pr_err("Error extending TPM20 PCR, result: %d\n", ret);
>> +        slaunch_txt_reset(txt, "Failed to extend TPM20 PCR\n",
>> +                  SL_ERROR_TPM_EXTEND);
>> +    }
>> +
>> +    kfree(digests);
>> +}
>> +
>> +static void slaunch_tpm2_extend(struct tpm_chip *tpm, void __iomem *txt)
>> +{
>> +    struct tcg_pcr_event *event_header;
>> +    struct tcg_pcr_event2_head *event;
>> +    int start = 0, end = 0, size;
>> +
>> +    event_header = (struct tcg_pcr_event *)(sl_evtlog.addr +
>> +                        evtlog21->first_record_offset);
>> +
>> +    /* Skip first TPM 1.2 event to get to first TPM 2.0 event */
>> +    event = (struct tcg_pcr_event2_head *)((u8 *)event_header + sizeof(*event_header) +
>> +                           event_header->event_size);
>> +
>> +    while ((void  *)event < sl_evtlog.addr + evtlog21->next_record_offset) {
>> +        size = __calc_tpm2_event_size(event, event_header, false);
>> +        if (!size)
>> +            slaunch_txt_reset(txt, "TPM20 invalid event in event log\n",
>> +                      SL_ERROR_TPM_INVALID_EVENT);
>> +
>> +        /*
>> +         * Marker events indicate where the Secure Launch early stub
>> +         * started and ended adding post launch events. As they are
>> +         * encountered, switch the event type to NO_ACTION so they
>> +         * ignored in when the event log is processed since they are
>> +         * not really measurements.
>> +         */
>> +        if (event->event_type == TXT_EVTYPE_SLAUNCH_END) {
>> +            event->event_type = NO_ACTION;
>> +            end = 1;
>> +            break;
>> +        } else if (event->event_type == TXT_EVTYPE_SLAUNCH_START) {
>> +            event->event_type = NO_ACTION;
>> +            start = 1;
>> +            goto next;
>> +        }
>> +
>> +        if (start)
>> +            slaunch_tpm2_extend_event(tpm, txt, event);
>> +
>> +next:
>> +        event = (struct tcg_pcr_event2_head *)((u8 *)event + size);
>> +    }
>> +
>> +    if (!start || !end)
>> +        slaunch_txt_reset(txt, "Missing start or end events for extending TPM20 PCRs\n",
>> +                  SL_ERROR_TPM_EXTEND);
>> +}
>> +
>> +static void slaunch_tpm_extend(struct tpm_chip *tpm, void __iomem *txt)
>> +{
>> +    struct tpm_event_log_header *event_header;
>> +    struct tcg_pcr_event *event;
>> +    struct tpm_digest digest;
>> +    int start = 0, end = 0;
>> +    int size, ret;
>> +
>> +    event_header = (struct tpm_event_log_header *)sl_evtlog.addr;
>> +    event = (struct tcg_pcr_event *)((u8 *)event_header +
>> +                sizeof(*event_header));
>> +
>> +    while ((void  *)event < sl_evtlog.addr + event_header->next_event_offset) {
>> +        size = sizeof(*event) + event->event_size;
>> +
>> +        /*
>> +         * See comments in slaunch_tpm2_extend() concerning these special
>> +         * event types.
>> +         */
>> +        if (event->event_type == TXT_EVTYPE_SLAUNCH_END) {
>> +            event->event_type = NO_ACTION;
>> +            end = 1;
>> +            break;
>> +        } else if (event->event_type == TXT_EVTYPE_SLAUNCH_START) {
>> +            event->event_type = NO_ACTION;
>> +            start = 1;
>> +            goto next;
>> +        }
>> +
>> +        if (start) {
>> +            memset(&digest.digest[0], 0, TPM_MAX_DIGEST_SIZE);
>> +            digest.alg_id = TPM_ALG_SHA1;
>> +            memcpy(&digest.digest[0], &event->digest[0],
>> +                   SHA1_DIGEST_SIZE);
>> +
>> +            ret = tpm_pcr_extend(tpm, event->pcr_idx, &digest);
>> +            if (ret) {
>> +                pr_err("Error extending TPM12 PCR, result: %d\n", ret);
>> +                slaunch_txt_reset(txt, "Failed to extend TPM12 PCR\n",
>> +                          SL_ERROR_TPM_EXTEND);
>> +            }
>> +        }
>> +
>> +next:
>> +        event = (struct tcg_pcr_event *)((u8 *)event + size);
>> +    }
>> +
>> +    if (!start || !end)
>> +        slaunch_txt_reset(txt, "Missing start or end events for extending TPM12 PCRs\n",
>> +                  SL_ERROR_TPM_EXTEND);
>> +}
>> +
>> +static void slaunch_pcr_extend(void __iomem *txt)
>> +{
>> +    struct tpm_chip *tpm;
>> +    int rc;
>> +
>> +    tpm = tpm_default_chip();
>> +    if (!tpm)
>> +        slaunch_txt_reset(txt, "Could not get default TPM chip\n",
>> +                  SL_ERROR_TPM_INIT);
>> +
>> +    rc = tpm_chip_set_locality(tpm, 2);
>> +    if (rc)
>> +        slaunch_txt_reset(txt, "Could not set TPM chip locality 2\n",
>> +                  SL_ERROR_TPM_INIT);
>> +
>> +    if (evtlog21)
>> +        slaunch_tpm2_extend(tpm, txt);
>> +    else
>> +        slaunch_tpm_extend(tpm, txt);
>> +}
>> +
>> +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_pcr_extend(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.39.3
>>
Andy Lutomirski April 30, 2025, 6:51 p.m. UTC | #3
On Tue, Apr 29, 2025 at 6:41 PM Daniel P. Smith
<dpsmith@apertussolutions.com> wrote:
>
> On 4/28/25 13:38, Andy Lutomirski wrote:
> >> On Apr 21, 2025, at 9:36 AM, Ross Philipson <ross.philipson@oracle.com> wrote:
> >>
> >> 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.
> >
> > I read through some of the TPM and TXT docs, and I haven’t found a
> > clear explanation of exactly what gets hashed into which PCR.  (Mostly
> > because the docs are full of TXT-specific terms.)
>
>
> For Intel TXT, the general approach is detailed in section 1.10.2 of the
> TXT Software Development Guide[1]. I point you at the Detail and
> Authorities Usage section because the ability to do Legacy Usage has
> been unavailable for some time.
>
> In section 1.10.2.1, the dialogue explains how and what the initial
> measurement is into PCR17. After that is Table 1, which provides a
> listing of all the measurements the ACM could make before starting the
> MLE. Just as an FYI, on Gen 9 and later, the STM measurement will be
> present and will be the hash of the PPAM module.[2]
>
> Section 1.10.2.2 gives a similar treatment to PCR18.
>
> [1]
> https://www.intel.com/content/dam/www/public/us/en/documents/guides/intel-txt-software-development-guide.pdf
> [2]
> https://www.intel.com/content/dam/www/central-libraries/us/en/documents/drtm-based-computing-whitepaper.pdf
>
>
> > But I’m really struggling to understand how the security model ends up
> > being consistent with this late_initcall thing. We measure some state
> > into the event log, and then we do a whole bunch of things (everything
> > from the very beginning of loading the kernel proper to the whenever
> > in the late_initcall stage this code runs), and then we actually
> > extend the PCRs.  It seems to me that this may involve a whole lot of
> > crossing fingers that an attacker can’t find a way to get the kernel
> > to execute code that changes the event log in memory prior to
> > extending PCRs such that attacker-controlled values get written.  Even
> > if the design is, in principle, sound, the attack surface seems much,
> > much larger than it deserves to be.

I hate to be obnoxious, but your email kind of exemplifies why I, and
I think many developers, REALLY dislike the TXT and related specs.
It's full of magic words that mean nothing to anyone not immersed in
this particular ecosystem.

>
>
> There is a more fundemental flaw to your scenario, but before covering
> that, consider what measurements could be tampered with that are made by
> the setup kernel:

What is the "setup kernel"?  Do you mean the early code in the kernel?

>
>   - Kernel Setup Data
>   - TrenchBoot's SLRT
>   - Boot Params
>   - Command line
>   - EFI Memory Map
>   - EFI configuration items, populated by efi-stub (currently unused)
>   - External Ramdisk

Are you saying that all of these items are measured by the early
loader (and *not* measured by the ACM or otherwise by anything that is
trustworthy and runs before the early code)?

>
> Outside of the case of an external ramdisk, the attacker can only
> pretend valid configuration data was passed to it.
>
> Correct me if I am wrong, but I don't think that is what is bothering
> you. You are either concerned with one of two cases here. Either you are
> concerned that the attacker may be able to hide the loading of a corrupt
> kernel or that the attacker can corrupt the kernel after loading.

Here is my concern:

Suppose there is a set of measurements that an attacker wants to
replicate.  Some of these measurements are done prior to transferring
control to the early code that's in this patchset (call these
before-Linux measurements) and some are done by the loaded kernel
(let's call these Linux measurements).

I am concerned that the attacker will load a combination of things
that have the correct before-Linux measurements but the wrong
after-Linux measurements.  (Wrong in the sense that, *if those
measurements actually landed in the PCRs, then the attacker would
lose*.)  *But* the attacker carefully chooses what they're loading to
gain control of the system prior to the actual PCR extension.  Then
the attacker extends the PCR with the hash that they want to
replicate, and the attacker wins.

For the security model to make any sense at all, then it needs to be
impossible for the attacker to gain control prior to the early kernel
code running without changing the before-Linux measurements.  But
there is a huge gap between when the early Linux code runs and when
the late initcalls run, and the attacker has that entire window to
break your security.

> first case, the answer is no; the attacker cannot. The kernel and the
> initrd if it was packed in the kernel are measured and sent to the TPM
> by the ACM running in cache-as-ram before execution begins.
>
> The second case is the flawed scenario, a strawman, if you will. This is
> a runtime-integrity problem that is outside the scope/protections of
> load-time-integrity solutions such as SRTM and DRTM. If the correct
> kernel was loaded and measured, but an attacker already has a position
> in the system that they can corrupt the kernel before the user-space
> init process can be run, then they already won.

I'm arguing that it seems like that this patchset has a
runtime-integrity problem.  It's outside the scope of the TXT spec per
se.  It's in the scope of *the Linux kernel*, and anyone who wants to
trust that the Linux DRTM code actually works needs to factor in this
giant weakness.

And you haven't explained why there is no way for an attacker to
corrupt the process between the early kernel code and the late
measurement code.


> > Is there some reason for all this complexity instead of extending the
> > PCRs at the early stage when the measurements are taken?
>
>
> We did have TPM logic in the setup kernel at one point. Within their
> rights, the TPM maintainers took the position that the only TPM
> interface logic should be the existing driver.

Hey TPM maintainers, I think this is nonsense, or maybe someone has
misinterpreted something that someone else said.  I understand that
avoiding code duplication is nice.  I understand that, at runtime, all
TPM access ought to go through the driver.  But, if the driver is
incapable of working during the very very early kernel load, then
there should be an alternate interface.  Kind of like how we have
early_printk instead of saying "well, there should only be one printk,
so instead of having early_printk, we'll just have a big buffer of
messages and log them eventually".  Or kind of like how we might call
EFI boot service functions during early boot.

--Andy
diff mbox series

Patch

diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index a7ca7ca16174..bed87b1c49a2 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -83,6 +83,7 @@  obj-$(CONFIG_IA32_EMULATION)	+= tls.o
 obj-y				+= step.o
 obj-$(CONFIG_INTEL_TXT)		+= tboot.o
 obj-$(CONFIG_SECURE_LAUNCH)	+= slaunch.o
+obj-$(CONFIG_SECURE_LAUNCH)	+= slmodule.o
 obj-$(CONFIG_ISA_DMA_API)	+= i8237.o
 obj-y				+= stacktrace.o
 obj-y				+= cpu/
diff --git a/arch/x86/kernel/slmodule.c b/arch/x86/kernel/slmodule.c
new file mode 100644
index 000000000000..64010bac038c
--- /dev/null
+++ b/arch/x86/kernel/slmodule.c
@@ -0,0 +1,508 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Secure Launch late validation/setup, securityfs exposure and finalization.
+ *
+ * Copyright (c) 2025 Apertus Solutions, LLC
+ * Copyright (c) 2025 Assured Information Security, Inc.
+ * Copyright (c) 2025, Oracle and/or its affiliates.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/linkage.h>
+#include <linux/mm.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/security.h>
+#include <linux/memblock.h>
+#include <linux/tpm.h>
+#include <asm/segment.h>
+#include <asm/sections.h>
+#include <crypto/sha2.h>
+#include <linux/slr_table.h>
+#include <linux/slaunch.h>
+
+/*
+ * The macro DECLARE_TXT_PUB_READ_U is used to read values from the TXT
+ * public registers as unsigned values.
+ */
+#define DECLARE_TXT_PUB_READ_U(size, fmt, msg_size)			\
+static ssize_t txt_pub_read_u##size(unsigned int offset,		\
+		loff_t *read_offset,					\
+		size_t read_len,					\
+		char __user *buf)					\
+{									\
+	char msg_buffer[msg_size];					\
+	u##size reg_value = 0;						\
+	void __iomem *txt;						\
+									\
+	txt = ioremap(TXT_PUB_CONFIG_REGS_BASE,				\
+			TXT_NR_CONFIG_PAGES * PAGE_SIZE);		\
+	if (!txt)							\
+		return -EFAULT;						\
+	memcpy_fromio(&reg_value, txt + offset, sizeof(u##size));	\
+	iounmap(txt);							\
+	snprintf(msg_buffer, msg_size, fmt, reg_value);			\
+	return simple_read_from_buffer(buf, read_len, read_offset,	\
+			&msg_buffer, msg_size);				\
+}
+
+DECLARE_TXT_PUB_READ_U(8, "%#04x\n", 6);
+DECLARE_TXT_PUB_READ_U(32, "%#010x\n", 12);
+DECLARE_TXT_PUB_READ_U(64, "%#018llx\n", 20);
+
+#define DECLARE_TXT_FOPS(reg_name, reg_offset, reg_size)		\
+static ssize_t txt_##reg_name##_read(struct file *flip,			\
+		char __user *buf, size_t read_len, loff_t *read_offset)	\
+{									\
+	return txt_pub_read_u##reg_size(reg_offset, read_offset,	\
+			read_len, buf);					\
+}									\
+static const struct file_operations reg_name##_ops = {			\
+	.read = txt_##reg_name##_read,					\
+}
+
+DECLARE_TXT_FOPS(sts, TXT_CR_STS, 64);
+DECLARE_TXT_FOPS(ests, TXT_CR_ESTS, 8);
+DECLARE_TXT_FOPS(errorcode, TXT_CR_ERRORCODE, 32);
+DECLARE_TXT_FOPS(didvid, TXT_CR_DIDVID, 64);
+DECLARE_TXT_FOPS(e2sts, TXT_CR_E2STS, 64);
+DECLARE_TXT_FOPS(ver_emif, TXT_CR_VER_EMIF, 32);
+DECLARE_TXT_FOPS(scratchpad, TXT_CR_SCRATCHPAD, 64);
+
+/*
+ * Securityfs exposure
+ */
+struct memfile {
+	char *name;
+	void *addr;
+	size_t size;
+};
+
+static struct memfile sl_evtlog = {"eventlog", NULL, 0};
+static void *txt_heap;
+static struct txt_heap_event_log_pointer2_1_element *evtlog21;
+static DEFINE_MUTEX(sl_evt_log_mutex);
+static struct tcg_efi_specid_event_head *efi_head;
+
+static ssize_t sl_evtlog_read(struct file *file, char __user *buf,
+			      size_t count, loff_t *pos)
+{
+	ssize_t size;
+
+	if (!sl_evtlog.addr)
+		return 0;
+
+	mutex_lock(&sl_evt_log_mutex);
+	size = simple_read_from_buffer(buf, count, pos, sl_evtlog.addr,
+				       sl_evtlog.size);
+	mutex_unlock(&sl_evt_log_mutex);
+
+	return size;
+}
+
+static ssize_t sl_evtlog_write(struct file *file, const char __user *buf,
+			       size_t datalen, loff_t *ppos)
+{
+	ssize_t result;
+	char *data;
+
+	if (!sl_evtlog.addr)
+		return 0;
+
+	/* No partial writes. */
+	result = -EINVAL;
+	if (*ppos != 0)
+		goto out;
+
+	data = memdup_user(buf, datalen);
+	if (IS_ERR(data)) {
+		result = PTR_ERR(data);
+		goto out;
+	}
+
+	mutex_lock(&sl_evt_log_mutex);
+	if (evtlog21)
+		result = tpm2_log_event(evtlog21, sl_evtlog.addr,
+					sl_evtlog.size, datalen, data);
+	else
+		result = tpm_log_event(sl_evtlog.addr, sl_evtlog.size,
+				       datalen, data);
+	mutex_unlock(&sl_evt_log_mutex);
+
+	kfree(data);
+out:
+	return result;
+}
+
+static const struct file_operations sl_evtlog_ops = {
+	.read = sl_evtlog_read,
+	.write = sl_evtlog_write,
+	.llseek	= default_llseek,
+};
+
+struct sfs_file {
+	const char *name;
+	const struct file_operations *fops;
+};
+
+#define SL_TXT_ENTRY_COUNT	7
+static const struct sfs_file sl_txt_files[] = {
+	{ "sts", &sts_ops },
+	{ "ests", &ests_ops },
+	{ "errorcode", &errorcode_ops },
+	{ "didvid", &didvid_ops },
+	{ "ver_emif", &ver_emif_ops },
+	{ "scratchpad", &scratchpad_ops },
+	{ "e2sts", &e2sts_ops }
+};
+
+/* sysfs file handles */
+static struct dentry *slaunch_dir;
+static struct dentry *event_file;
+static struct dentry *txt_dir;
+static struct dentry *txt_entries[SL_TXT_ENTRY_COUNT];
+
+static long slaunch_expose_securityfs(void)
+{
+	long ret = 0;
+	int i;
+
+	slaunch_dir = securityfs_create_dir("slaunch", NULL);
+	if (IS_ERR(slaunch_dir))
+		return PTR_ERR(slaunch_dir);
+
+	if (slaunch_get_flags() & SL_FLAG_ARCH_TXT) {
+		txt_dir = securityfs_create_dir("txt", slaunch_dir);
+		if (IS_ERR(txt_dir)) {
+			ret = PTR_ERR(txt_dir);
+			goto remove_slaunch;
+		}
+
+		for (i = 0; i < ARRAY_SIZE(sl_txt_files); i++) {
+			txt_entries[i] = securityfs_create_file(
+						sl_txt_files[i].name, 0440,
+						txt_dir, NULL,
+						sl_txt_files[i].fops);
+			if (IS_ERR(txt_entries[i])) {
+				ret = PTR_ERR(txt_entries[i]);
+				goto remove_files;
+			}
+		}
+	}
+
+	if (sl_evtlog.addr) {
+		event_file = securityfs_create_file(sl_evtlog.name, 0440,
+						    slaunch_dir, NULL,
+						    &sl_evtlog_ops);
+		if (IS_ERR(event_file)) {
+			ret = PTR_ERR(event_file);
+			goto remove_files;
+		}
+	}
+
+	return 0;
+
+remove_files:
+	if (slaunch_get_flags() & SL_FLAG_ARCH_TXT) {
+		while (--i >= 0)
+			securityfs_remove(txt_entries[i]);
+		securityfs_remove(txt_dir);
+	}
+
+remove_slaunch:
+	securityfs_remove(slaunch_dir);
+
+	return ret;
+}
+
+static void slaunch_teardown_securityfs(void)
+{
+	int i;
+
+	securityfs_remove(event_file);
+	if (sl_evtlog.addr) {
+		memunmap(sl_evtlog.addr);
+		sl_evtlog.addr = NULL;
+	}
+	sl_evtlog.size = 0;
+
+	if (slaunch_get_flags() & SL_FLAG_ARCH_TXT) {
+		for (i = 0; i < ARRAY_SIZE(sl_txt_files); i++)
+			securityfs_remove(txt_entries[i]);
+
+		securityfs_remove(txt_dir);
+
+		if (txt_heap) {
+			memunmap(txt_heap);
+			txt_heap = NULL;
+		}
+	}
+
+	securityfs_remove(slaunch_dir);
+}
+
+static void slaunch_intel_evtlog(void __iomem *txt)
+{
+	struct slr_entry_log_info *log_info;
+	struct txt_os_mle_data *params;
+	struct slr_table *slrt;
+	void *os_sinit_data;
+	u64 base, size;
+
+	memcpy_fromio(&base, txt + TXT_CR_HEAP_BASE, sizeof(base));
+	memcpy_fromio(&size, txt + TXT_CR_HEAP_SIZE, sizeof(size));
+
+	/* now map TXT heap */
+	txt_heap = memremap(base, size, MEMREMAP_WB);
+	if (!txt_heap)
+		slaunch_txt_reset(txt, "Error failed to memremap TXT heap\n",
+				  SL_ERROR_HEAP_MAP);
+
+	params = (struct txt_os_mle_data *)txt_os_mle_data_start(txt_heap);
+
+	/* Get the SLRT and remap it */
+	slrt = memremap(params->slrt, sizeof(*slrt), MEMREMAP_WB);
+	if (!slrt)
+		slaunch_txt_reset(txt, "Error failed to memremap SLR Table\n",
+				  SL_ERROR_SLRT_MAP);
+	size = slrt->size;
+	memunmap(slrt);
+
+	slrt = memremap(params->slrt, size, MEMREMAP_WB);
+	if (!slrt)
+		slaunch_txt_reset(txt, "Error failed to memremap SLR Table\n",
+				  SL_ERROR_SLRT_MAP);
+
+	log_info = slr_next_entry_by_tag(slrt, NULL, SLR_ENTRY_LOG_INFO);
+	if (!log_info)
+		slaunch_txt_reset(txt, "Error failed to memremap SLR Table\n",
+				  SL_ERROR_SLRT_MISSING_ENTRY);
+
+	sl_evtlog.size = log_info->size;
+	sl_evtlog.addr = memremap(log_info->addr, log_info->size,
+				  MEMREMAP_WB);
+	if (!sl_evtlog.addr)
+		slaunch_txt_reset(txt, "Error failed to memremap TPM event log\n",
+				  SL_ERROR_EVENTLOG_MAP);
+
+	memunmap(slrt);
+
+	/* Determine if this is TPM 1.2 or 2.0 event log */
+	if (memcmp(sl_evtlog.addr + sizeof(struct tcg_pcr_event),
+		    TCG_SPECID_SIG, sizeof(TCG_SPECID_SIG)))
+		return; /* looks like it is not 2.0 */
+
+	/* For TPM 2.0 logs, the extended heap element must be located */
+	os_sinit_data = txt_os_sinit_data_start(txt_heap);
+
+	evtlog21 = tpm2_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_txt_reset(txt, "Error failed to find TPM20 event log element\n",
+				  SL_ERROR_TPM_INVALID_LOG20);
+
+	/* Save pointer to the EFI SpecID log header */
+	efi_head = (struct tcg_efi_specid_event_head *)(sl_evtlog.addr + sizeof(struct tcg_pcr_event));
+}
+
+static void slaunch_tpm2_extend_event(struct tpm_chip *tpm, void __iomem *txt,
+				      struct tcg_pcr_event2_head *event)
+{
+	u16 *alg_id_field = (u16 *)((u8 *)event + sizeof(*event));
+	struct tpm_digest *digests;
+	u8 *dptr;
+	int ret;
+	u32 i;
+
+	/*
+	 * Early SL code ensured the TPM algorithm information passed via
+	 * the log is valid. Small sanity check here.
+	 */
+	if (event->count != efi_head->num_algs)
+		slaunch_txt_reset(txt, "Event digest count mismatch with event log\n",
+				  SL_ERROR_TPM_EVENT_COUNT);
+
+	digests = kzalloc(efi_head->num_algs * sizeof(*digests), GFP_KERNEL);
+	if (!digests)
+		slaunch_txt_reset(txt, "Failed to allocate array of digests\n",
+				  SL_ERROR_GENERIC);
+
+	for (i = 0; i < event->count; i++) {
+		dptr = (u8 *)alg_id_field + sizeof(u16);
+
+		/* Setup each digest for the extend */
+		digests[i].alg_id = efi_head->digest_sizes[i].alg_id;
+		memcpy(&digests[i].digest[0], dptr,
+			efi_head->digest_sizes[i].digest_size);
+
+		alg_id_field = (u16 *)((u8 *)alg_id_field + sizeof(u16) +
+				efi_head->digest_sizes[i].digest_size);
+	}
+
+	ret = tpm_pcr_extend(tpm, event->pcr_idx, digests);
+	if (ret) {
+		pr_err("Error extending TPM20 PCR, result: %d\n", ret);
+		slaunch_txt_reset(txt, "Failed to extend TPM20 PCR\n",
+				  SL_ERROR_TPM_EXTEND);
+	}
+
+	kfree(digests);
+}
+
+static void slaunch_tpm2_extend(struct tpm_chip *tpm, void __iomem *txt)
+{
+	struct tcg_pcr_event *event_header;
+	struct tcg_pcr_event2_head *event;
+	int start = 0, end = 0, size;
+
+	event_header = (struct tcg_pcr_event *)(sl_evtlog.addr +
+						evtlog21->first_record_offset);
+
+	/* Skip first TPM 1.2 event to get to first TPM 2.0 event */
+	event = (struct tcg_pcr_event2_head *)((u8 *)event_header + sizeof(*event_header) +
+					       event_header->event_size);
+
+	while ((void  *)event < sl_evtlog.addr + evtlog21->next_record_offset) {
+		size = __calc_tpm2_event_size(event, event_header, false);
+		if (!size)
+			slaunch_txt_reset(txt, "TPM20 invalid event in event log\n",
+					  SL_ERROR_TPM_INVALID_EVENT);
+
+		/*
+		 * Marker events indicate where the Secure Launch early stub
+		 * started and ended adding post launch events. As they are
+		 * encountered, switch the event type to NO_ACTION so they
+		 * ignored in when the event log is processed since they are
+		 * not really measurements.
+		 */
+		if (event->event_type == TXT_EVTYPE_SLAUNCH_END) {
+			event->event_type = NO_ACTION;
+			end = 1;
+			break;
+		} else if (event->event_type == TXT_EVTYPE_SLAUNCH_START) {
+			event->event_type = NO_ACTION;
+			start = 1;
+			goto next;
+		}
+
+		if (start)
+			slaunch_tpm2_extend_event(tpm, txt, event);
+
+next:
+		event = (struct tcg_pcr_event2_head *)((u8 *)event + size);
+	}
+
+	if (!start || !end)
+		slaunch_txt_reset(txt, "Missing start or end events for extending TPM20 PCRs\n",
+				  SL_ERROR_TPM_EXTEND);
+}
+
+static void slaunch_tpm_extend(struct tpm_chip *tpm, void __iomem *txt)
+{
+	struct tpm_event_log_header *event_header;
+	struct tcg_pcr_event *event;
+	struct tpm_digest digest;
+	int start = 0, end = 0;
+	int size, ret;
+
+	event_header = (struct tpm_event_log_header *)sl_evtlog.addr;
+	event = (struct tcg_pcr_event *)((u8 *)event_header +
+				sizeof(*event_header));
+
+	while ((void  *)event < sl_evtlog.addr + event_header->next_event_offset) {
+		size = sizeof(*event) + event->event_size;
+
+		/*
+		 * See comments in slaunch_tpm2_extend() concerning these special
+		 * event types.
+		 */
+		if (event->event_type == TXT_EVTYPE_SLAUNCH_END) {
+			event->event_type = NO_ACTION;
+			end = 1;
+			break;
+		} else if (event->event_type == TXT_EVTYPE_SLAUNCH_START) {
+			event->event_type = NO_ACTION;
+			start = 1;
+			goto next;
+		}
+
+		if (start) {
+			memset(&digest.digest[0], 0, TPM_MAX_DIGEST_SIZE);
+			digest.alg_id = TPM_ALG_SHA1;
+			memcpy(&digest.digest[0], &event->digest[0],
+			       SHA1_DIGEST_SIZE);
+
+			ret = tpm_pcr_extend(tpm, event->pcr_idx, &digest);
+			if (ret) {
+				pr_err("Error extending TPM12 PCR, result: %d\n", ret);
+				slaunch_txt_reset(txt, "Failed to extend TPM12 PCR\n",
+						  SL_ERROR_TPM_EXTEND);
+			}
+		}
+
+next:
+		event = (struct tcg_pcr_event *)((u8 *)event + size);
+	}
+
+	if (!start || !end)
+		slaunch_txt_reset(txt, "Missing start or end events for extending TPM12 PCRs\n",
+				  SL_ERROR_TPM_EXTEND);
+}
+
+static void slaunch_pcr_extend(void __iomem *txt)
+{
+	struct tpm_chip *tpm;
+	int rc;
+
+	tpm = tpm_default_chip();
+	if (!tpm)
+		slaunch_txt_reset(txt, "Could not get default TPM chip\n",
+				  SL_ERROR_TPM_INIT);
+
+	rc = tpm_chip_set_locality(tpm, 2);
+	if (rc)
+		slaunch_txt_reset(txt, "Could not set TPM chip locality 2\n",
+				  SL_ERROR_TPM_INIT);
+
+	if (evtlog21)
+		slaunch_tpm2_extend(tpm, txt);
+	else
+		slaunch_tpm_extend(tpm, txt);
+}
+
+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_pcr_extend(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);