[RFC PATCH] platform/x86: Add sysfs interface for Intel TXT status

From: Jonathan McDowell
Date: Wed Mar 09 2022 - 05:40:15 EST


(This is an RFC to see if the approach is generally acceptable; unlike
the previous driver this exposes the information purely as read-only
status information, so userspace can make an informed decision about the
system state without having to poke about in /dev/mem. There are still a
few extra registers I'm trying to dig up information for before a proper
submission.)

This module provides read-only access to the Intel TXT (Trusted
Execution Technology) status registers, allowing userspace to determine
the status of measured boot and whether the dynamic root of trust for
measurement (DRTM) has been fully enabled.

Tools such as txt-stat from tboot
<https://sourceforge.net/projects/tboot/> can make use of this driver to
display state rather than relying on access to /dev/mem.

See Documentation/x86/intel_txt.rst for more information about Intel
TXT.

Signed-off-by: Jonathan McDowell <noodles@xxxxxx>
---
arch/x86/include/asm/txt.h | 34 +++++
drivers/platform/x86/intel/Kconfig | 14 ++
drivers/platform/x86/intel/Makefile | 2 +
drivers/platform/x86/intel/txt_sysfs.c | 185 +++++++++++++++++++++++++
4 files changed, 235 insertions(+)
create mode 100644 arch/x86/include/asm/txt.h
create mode 100644 drivers/platform/x86/intel/txt_sysfs.c

diff --git a/arch/x86/include/asm/txt.h b/arch/x86/include/asm/txt.h
new file mode 100644
index 000000000000..9c90d894c1af
--- /dev/null
+++ b/arch/x86/include/asm/txt.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * txt.h: Intel TXT (Trusted Execution Technology) related definitions
+ */
+
+#ifndef __TXT_H
+#define __TXT_H
+
+#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)
+
+/*
+ * TXT configuration registers (offsets from TXT_{PUB,PRIV}_CONFIG_REGS_BASE)
+ */
+
+#define TXT_CR_STS 0x0000
+#define TXT_CR_ESTS 0x0008
+#define TXT_CR_ERRORCODE 0x0030
+#define TXT_CR_ACMSTS 0x00A0
+#define TXT_CR_VER_FSBIF 0x0100
+#define TXT_CR_DIDVID 0x0110
+#define TXT_CR_VER_QPIIF 0x0200
+#define TXT_CR_SINIT_BASE 0x0270
+#define TXT_CR_SINIT_SIZE 0x0278
+#define TXT_CR_HEAP_BASE 0x0300
+#define TXT_CR_HEAP_SIZE 0x0308
+#define TXT_CR_DPR 0x0330
+#define TXT_CR_PUBLIC_KEY 0x0400
+#define TXT_CR_E2STS 0x08f0
+
+#endif /* __TXT_H */
diff --git a/drivers/platform/x86/intel/Kconfig b/drivers/platform/x86/intel/Kconfig
index 8e65086bb6c8..ae8d032b4499 100644
--- a/drivers/platform/x86/intel/Kconfig
+++ b/drivers/platform/x86/intel/Kconfig
@@ -159,6 +159,20 @@ config INTEL_TURBO_MAX_3
This driver is only required when the system is not using Hardware
P-States (HWP). In HWP mode, priority can be read from ACPI tables.

+config INTEL_TXT_SYSFS
+ tristate "Intel TXT sysfs driver"
+ depends on HAVE_INTEL_TXT && SECURITYFS
+ help
+ This driver exports the TXT (Trusted Execution Technology) public
+ configuration space to user space via sysfs.
+
+ These registers provide details about the status of the platform's
+ measured launch and execution environment, allowing userspace to
+ make trust based decisions. See tboot
+ <https://sourceforge.net/projects/tboot/> and TrenchBoot
+ <https://trenchboot.org/> for projects which implement the
+ appropriate boot flow to fully enable these features.
+
config INTEL_UNCORE_FREQ_CONTROL
tristate "Intel Uncore frequency control driver"
depends on X86_64
diff --git a/drivers/platform/x86/intel/Makefile b/drivers/platform/x86/intel/Makefile
index 35f2066578b2..ac73ac61090a 100644
--- a/drivers/platform/x86/intel/Makefile
+++ b/drivers/platform/x86/intel/Makefile
@@ -26,6 +26,8 @@ intel_int0002_vgpio-y := int0002_vgpio.o
obj-$(CONFIG_INTEL_INT0002_VGPIO) += intel_int0002_vgpio.o
intel_oaktrail-y := oaktrail.o
obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o
+intel_txt_sysfs-y := txt_sysfs.o
+obj-$(CONFIG_INTEL_TXT_SYSFS) += intel_txt_sysfs.o
intel_vsec-y := vsec.o
obj-$(CONFIG_INTEL_VSEC) += intel_vsec.o

diff --git a/drivers/platform/x86/intel/txt_sysfs.c b/drivers/platform/x86/intel/txt_sysfs.c
new file mode 100644
index 000000000000..a8e51abc6854
--- /dev/null
+++ b/drivers/platform/x86/intel/txt_sysfs.c
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This module can be used to access below resources
+ * - TXT config space
+ * - SMX parameter
+ *
+ * Intel TXT (Trusted Execution Technology) will provide higher
+ * assurance of system configuration and initial state as well as
+ * data reset protection. It also helps solve real end user concerns
+ * about having confidence that their hardware is running the VMM
+ * or kernel that it was configured with, especially since they may
+ * be responsible for providing such assurances to VMs and services
+ * running on it.
+ *
+ * See Documentation/intel_txt.txt for more information about
+ * Intel TXT.
+ *
+ * Intel TXT configuration registers are a subset of chipset registers.
+ * These chipset registers that interact with SMX are accessed from two
+ * regions of memory, which represent the public and private configuration
+ * spaces, by system software using memory read/write protocols.
+ *
+ * Safer Mode Extensions (SMX) provide a processor's programming
+ * interface in an Intel TXT platform for system software to establish
+ * a measured environment within the platform to support trust decisions
+ * by end users.
+ *
+ * Data can be found below
+ * /sys/devices/platform/intel_txt/...
+ */
+
+#include <linux/byteorder/little_endian.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/security.h>
+#include <linux/seq_file.h>
+
+#include <asm/cpu_device_id.h>
+#include <asm/txt.h>
+
+#define DEV_NAME "intel_txt"
+static struct platform_device *txt_pdev;
+
+static int txt_enabled_show(struct seq_file *m, void *v);
+static int txt_key_show(struct seq_file *m, void *v);
+static int txt_reg_show(struct seq_file *m, void *v);
+
+/* securityfs related data */
+static struct dentry *txt_dir;
+static void *txt_pub_regs;
+static struct txt_securityfs_file {
+ char *name;
+ u32 ofs;
+ int (*show)(struct seq_file *m, void *v);
+ struct dentry *dentry;
+} txt_securityfs_files[] = {
+ /* Raw registers */
+ { .name = "acmsts", .ofs = TXT_CR_ACMSTS, .show = txt_reg_show },
+ { .name = "didvid", .ofs = TXT_CR_DIDVID, .show = txt_reg_show },
+ { .name = "dpr", .ofs = TXT_CR_DPR, .show = txt_reg_show },
+ { .name = "errorcode", .ofs = TXT_CR_ERRORCODE, .show = txt_reg_show },
+ { .name = "ests", .ofs = TXT_CR_ESTS, .show = txt_reg_show },
+ { .name = "sts", .ofs = TXT_CR_STS, .show = txt_reg_show },
+
+ { .name = "enabled", .show = txt_enabled_show },
+ { .name = "publickey", .show = txt_key_show },
+};
+
+/* Shows if TXT has been enabled */
+static int txt_enabled_show(struct seq_file *m, void *v)
+{
+ /* If the BIOS has enabled TXT then the heap base will be set */
+ seq_printf(m, "%d\n", *(u64 *)(txt_pub_regs + TXT_CR_HEAP_BASE) != 0);
+
+ return 0;
+}
+
+/* Shows the 256 bit hash of the public key */
+static int txt_key_show(struct seq_file *m, void *v)
+{
+ seq_printf(m, "%016llx%016llx%016llx%016llx\n",
+ cpu_to_be64(*(u64 *)(txt_pub_regs + TXT_CR_PUBLIC_KEY)),
+ cpu_to_be64(*(u64 *)(txt_pub_regs + TXT_CR_PUBLIC_KEY + 8)),
+ cpu_to_be64(*(u64 *)(txt_pub_regs + TXT_CR_PUBLIC_KEY + 16)),
+ cpu_to_be64(*(u64 *)(txt_pub_regs + TXT_CR_PUBLIC_KEY + 24)));
+
+ return 0;
+}
+
+/* Show a generic 64 bit register */
+static int txt_reg_show(struct seq_file *m, void *v)
+{
+ struct txt_securityfs_file *ctx = m->private;
+
+ seq_printf(m, "%016llx\n", *(u64 *)(txt_pub_regs + ctx->ofs));
+
+ return 0;
+}
+
+static int txt_pub_reg_open(struct inode *inode, struct file *filp)
+{
+ struct txt_securityfs_file *ctx = file_inode(filp)->i_private;
+
+ return single_open(filp, ctx->show, ctx);
+}
+
+static const struct file_operations txt_pub_reg_fops = {
+ .open = txt_pub_reg_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct x86_cpu_id smx_cpu_id[] = {
+ X86_MATCH_FEATURE(X86_FEATURE_SMX, NULL),
+ {}
+};
+MODULE_DEVICE_TABLE(x86cpu, smx_cpu_id);
+
+static int __init txt_sysfs_init(void)
+{
+ int i;
+ int err;
+
+ if (!x86_match_cpu(smx_cpu_id))
+ return -ENODEV;
+
+ txt_pdev = platform_device_register_simple(DEV_NAME, -1, NULL, 0);
+ if (IS_ERR(txt_pdev))
+ return PTR_ERR(txt_pdev);
+
+ txt_pub_regs = devm_ioremap(&txt_pdev->dev, TXT_PUB_CONFIG_REGS_BASE,
+ TXT_NR_CONFIG_PAGES * PAGE_SIZE);
+ if (!txt_pub_regs)
+ goto out;
+
+ txt_dir = securityfs_create_dir("txt", NULL);
+ if (IS_ERR(txt_dir)) {
+ err = PTR_ERR(txt_dir);
+ txt_dir = NULL;
+ goto out;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(txt_securityfs_files); i++) {
+ txt_securityfs_files[i].dentry = securityfs_create_file(
+ txt_securityfs_files[i].name, 0444, txt_dir,
+ &txt_securityfs_files[i],
+ &txt_pub_reg_fops);
+ if (IS_ERR(txt_securityfs_files[i].dentry)) {
+ err = PTR_ERR(txt_securityfs_files[i].dentry);
+ txt_securityfs_files[i].dentry = NULL;
+ goto out;
+ }
+ }
+
+ return 0;
+out:
+ for (i = 0; i < ARRAY_SIZE(txt_securityfs_files); i++) {
+ if (txt_securityfs_files[i].dentry != NULL)
+ securityfs_remove(txt_securityfs_files[i].dentry);
+ }
+ if (txt_dir)
+ securityfs_remove(txt_dir);
+ platform_device_unregister(txt_pdev);
+ return err;
+}
+
+static void __exit txt_sysfs_exit(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(txt_securityfs_files); i++) {
+ if (txt_securityfs_files[i].dentry != NULL)
+ securityfs_remove(txt_securityfs_files[i].dentry);
+ }
+ securityfs_remove(txt_dir);
+ platform_device_unregister(txt_pdev);
+}
+
+module_init(txt_sysfs_init);
+module_exit(txt_sysfs_exit);
+
+MODULE_LICENSE("GPL");
--
2.34.1