@@ -8,7 +8,7 @@
*
* Author(s):
* Daniel P. Smith <dpsmith@apertussolutions.com>
- *
+ * Garnet T. Grimm <grimmg@ainfosec.com>
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -493,3 +493,294 @@ void __init slaunch_setup(void)
vendor[3] == INTEL_CPUID_MFGID_EDX)
slaunch_setup_intel();
}
+
+#define SL_FS_ENTRIES 10
+/* root directory node must be last */
+#define SL_ROOT_DIR_ENTRY (SL_FS_ENTRIES - 1)
+#define SL_TXT_DIR_ENTRY (SL_FS_ENTRIES - 2)
+#define SL_TXT_FILE_FIRST (SL_TXT_DIR_ENTRY - 1)
+#define SL_TXT_ENTRY_COUNT 7
+
+#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) \
+{ \
+ void __iomem *txt; \
+ char msg_buffer[msg_size]; \
+ u##size reg_value = 0; \
+ txt = ioremap(TXT_PUB_CONFIG_REGS_BASE, \
+ TXT_NR_CONFIG_PAGES * PAGE_SIZE); \
+ if (IS_ERR(txt)) \
+ return PTR_ERR(txt); \
+ 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", 0, 0};
+static void *txt_heap;
+static struct txt_heap_event_log_pointer2_1_element __iomem *evtlog20;
+static DEFINE_MUTEX(sl_evt_log_mutex);
+
+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)
+{
+ char *data;
+ ssize_t result;
+
+ 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 (evtlog20)
+ result = tpm20_log_event(evtlog20, sl_evtlog.addr,
+ datalen, data);
+ else
+ result = tpm12_log_event(sl_evtlog.addr, 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,
+};
+
+static struct dentry *fs_entries[SL_FS_ENTRIES];
+
+struct sfs_file {
+ int parent;
+ const char *name;
+ const struct file_operations *fops;
+};
+
+static const struct sfs_file sl_files[] = {
+ { SL_TXT_DIR_ENTRY, "sts", &sts_ops },
+ { SL_TXT_DIR_ENTRY, "ests", &ests_ops },
+ { SL_TXT_DIR_ENTRY, "errorcode", &errorcode_ops },
+ { SL_TXT_DIR_ENTRY, "didvid", &didvid_ops },
+ { SL_TXT_DIR_ENTRY, "ver_emif", &ver_emif_ops },
+ { SL_TXT_DIR_ENTRY, "scratchpad", &scratchpad_ops },
+ { SL_TXT_DIR_ENTRY, "e2sts", &e2sts_ops }
+};
+
+static int sl_create_file(int entry, int parent, const char *name,
+ const struct file_operations *ops)
+{
+ if (entry < 0 || entry > SL_TXT_DIR_ENTRY)
+ return -EINVAL;
+ fs_entries[entry] = securityfs_create_file(name, 0440,
+ fs_entries[parent], NULL, ops);
+ if (IS_ERR(fs_entries[entry])) {
+ pr_err("Error creating securityfs %s file\n", name);
+ return PTR_ERR(fs_entries[entry]);
+ }
+ return 0;
+}
+
+static long slaunch_expose_securityfs(void)
+{
+ long ret = 0;
+ int i = 0;
+
+ fs_entries[SL_ROOT_DIR_ENTRY] = securityfs_create_dir("slaunch", NULL);
+ if (IS_ERR(fs_entries[SL_ROOT_DIR_ENTRY])) {
+ pr_err("Error creating securityfs slaunch root directory\n");
+ ret = PTR_ERR(fs_entries[SL_ROOT_DIR_ENTRY]);
+ goto err;
+ }
+
+ if (sl_flags & SL_FLAG_ARCH_TXT) {
+ fs_entries[SL_TXT_DIR_ENTRY] = securityfs_create_dir("txt",
+ fs_entries[SL_ROOT_DIR_ENTRY]);
+ if (IS_ERR(fs_entries[SL_TXT_DIR_ENTRY])) {
+ pr_err("Error creating securityfs txt directory\n");
+ ret = PTR_ERR(fs_entries[SL_TXT_DIR_ENTRY]);
+ goto err_dir;
+ }
+
+ for (i = 0; i < SL_TXT_ENTRY_COUNT; i++) {
+ ret = sl_create_file(SL_TXT_FILE_FIRST - i,
+ sl_files[i].parent, sl_files[i].name,
+ sl_files[i].fops);
+ if (ret)
+ goto err_dir;
+ }
+ }
+
+ if (sl_evtlog.addr > 0) {
+ ret = sl_create_file(0, SL_ROOT_DIR_ENTRY, sl_evtlog.name,
+ &sl_evtlog_ops);
+ if (ret)
+ goto err_dir;
+ }
+
+ return 0;
+
+err_dir:
+ for (i = 0; i <= SL_ROOT_DIR_ENTRY; i++)
+ securityfs_remove(fs_entries[i]);
+err:
+ return ret;
+}
+
+static void slaunch_teardown_securityfs(void)
+{
+ int i;
+
+ for (i = 0; i < SL_FS_ENTRIES; i++)
+ securityfs_remove(fs_entries[i]);
+
+ if (sl_flags & SL_FLAG_ARCH_TXT) {
+ if (sl_evtlog.addr) {
+ memunmap(sl_evtlog.addr);
+ sl_evtlog.addr = NULL;
+ }
+ sl_evtlog.size = 0;
+ if (txt_heap) {
+ memunmap(txt_heap);
+ txt_heap = NULL;
+ }
+ }
+}
+
+static void slaunch_intel_evtlog(void)
+{
+ void __iomem *config;
+ struct txt_os_mle_data *params;
+ void *os_sinit_data;
+ u64 base, size;
+
+ config = ioremap(TXT_PUB_CONFIG_REGS_BASE, TXT_NR_CONFIG_PAGES *
+ PAGE_SIZE);
+ if (!config) {
+ pr_err("Error failed to ioremap TXT reqs\n");
+ return;
+ }
+
+ memcpy_fromio(&base, config + TXT_CR_HEAP_BASE, sizeof(u64));
+ memcpy_fromio(&size, config + TXT_CR_HEAP_SIZE, sizeof(u64));
+ iounmap(config);
+
+ /* now map TXT heap */
+ txt_heap = memremap(base, size, MEMREMAP_WB);
+ if (!txt_heap) {
+ pr_err("Error failed to memremap TXT heap\n");
+ return;
+ }
+
+ params = (struct txt_os_mle_data *)txt_os_mle_data_start(txt_heap);
+
+ sl_evtlog.size = params->evtlog_size;
+ sl_evtlog.addr = memremap(params->evtlog_addr, params->evtlog_size,
+ MEMREMAP_WB);
+ if (!sl_evtlog.addr) {
+ pr_err("Error failed to memremap TPM event log\n");
+ return;
+ }
+
+ /* Determine if this is TPM 1.2 or 2.0 event log */
+ if (memcmp(sl_evtlog.addr + sizeof(struct tpm12_pcr_event),
+ TPM20_EVTLOG_SIGNATURE, sizeof(TPM20_EVTLOG_SIGNATURE)))
+ 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);
+
+ evtlog20 = tpm20_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 (!evtlog20)
+ pr_err("Error failed to find TPM20 event log element\n");
+}
+
+static int __init slaunch_late_init(void)
+{
+ /* Check to see if Secure Launch happened */
+ if (!(sl_flags & (SL_FLAG_ACTIVE|SL_FLAG_ARCH_TXT)))
+ return 0;
+
+ /* Only Intel TXT is supported at this point */
+ slaunch_intel_evtlog();
+
+ return slaunch_expose_securityfs();
+}
+
+static void __exit slaunch_exit(void)
+{
+ slaunch_teardown_securityfs();
+}
+
+late_initcall(slaunch_late_init);
+
+__exitcall(slaunch_exit);