summaryrefslogtreecommitdiff
path: root/src/security
diff options
context:
space:
mode:
Diffstat (limited to 'src/security')
-rw-r--r--src/security/intel/txt/Makefile.inc2
-rw-r--r--src/security/intel/txt/getsec_sclean.S181
-rw-r--r--src/security/intel/txt/txt_getsec.h3
3 files changed, 186 insertions, 0 deletions
diff --git a/src/security/intel/txt/Makefile.inc b/src/security/intel/txt/Makefile.inc
index 712ab589d5..762256f6e3 100644
--- a/src/security/intel/txt/Makefile.inc
+++ b/src/security/intel/txt/Makefile.inc
@@ -1,5 +1,7 @@
ifeq ($(CONFIG_INTEL_TXT),y)
+romstage-y += getsec_sclean.S
+
romstage-y += common.c
romstage-$(CONFIG_INTEL_TXT_LOGGING) += logging.c
diff --git a/src/security/intel/txt/getsec_sclean.S b/src/security/intel/txt/getsec_sclean.S
new file mode 100644
index 0000000000..e240a2faaa
--- /dev/null
+++ b/src/security/intel/txt/getsec_sclean.S
@@ -0,0 +1,181 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <cpu/x86/cr.h>
+#include <cpu/x86/mtrr.h>
+#include <cpu/x86/msr.h>
+
+#include "getsec_mtrr_setup.inc"
+
+#define MTRR_HIGH_MASK $((1 << (CONFIG_CPU_ADDR_BITS - 32)) - 1)
+
+#define NO_EVICT_MODE 0x2e0
+
+.align 4
+.text
+
+/*
+ * void getsec_sclean(const uint32_t acm_base, const uint32_t acm_size);
+ */
+.global getsec_sclean
+getsec_sclean:
+ /*
+ * At this point, it is certain that the BIOS ACM will be run.
+ * This requires tearing down CAR, which cannot be undone.
+ *
+ * From here onwards, the only way out is to reset the system.
+ */
+
+ /* Enable SMXE, SSE and debug extensions */
+ movl %cr4, %eax
+ orl $(CR4_OSFXSR | CR4_DE | CR4_SMXE), %eax
+ movl %eax, %cr4
+
+ /*
+ * Save arguments into SSE registers. We need to tear down CAR
+ * before launching the BIOS ACM, which will destroy the stack.
+ */
+ movd 4(%esp), %xmm2 /* acm_base */
+ movd 8(%esp), %xmm3 /* acm_size */
+
+ /* Disable cache */
+ movl %cr0, %eax
+ orl $(CR0_CD | CR0_NE), %eax
+ andl $(~(CR0_NW)), %eax
+ movl %eax, %cr0
+
+ /* Invalidate the cache */
+ invd
+
+ /* Disable MTRRs */
+ movl $(MTRR_DEF_TYPE_MSR), %ecx
+ xorl %eax, %eax
+ xorl %edx, %edx
+ wrmsr
+
+ /* Disable NEM, needs to be done in two steps */
+ movl $NO_EVICT_MODE, %ecx
+ rdmsr
+ andl $~2, %eax /* Clear NEM Run bit */
+ wrmsr
+ andl $~1, %eax /* Clear NEM Setup bit */
+ wrmsr
+
+ /* Invalidate the cache, again */
+ invd
+
+ /*
+ * Clear variable MTRRs
+ * Chapter 2.2.5.1
+ * Intel TXT Software Development Guide (Document: 315168-015)
+ */
+ movl $(MTRR_CAP_MSR), %ecx
+ rdmsr
+ andl $(0xff), %eax
+ movl %eax, %ebx
+
+ xorl %eax, %eax
+ xorl %edx, %edx
+
+ jmp cond_clear_var_mtrrs
+
+body_clear_var_mtrrs:
+
+ decl %ebx
+ movl %ebx, %ecx
+ shll %ecx
+ addl $(MTRR_PHYS_BASE(0)), %ecx
+ wrmsr
+ incl %ecx /* MTRR_PHYS_MASK */
+ wrmsr
+
+cond_clear_var_mtrrs:
+
+ cmpl $0, %ebx
+ jnz body_clear_var_mtrrs
+
+ /*
+ * Setup BIOS ACM as WB
+ * Chapter A.1.1
+ * Intel TXT Software Development Guide (Document: 315168-015)
+ */
+
+ /* Determine size of AC module */
+ movd %xmm2, %eax /* acm_base */
+ movd %xmm3, %ebx /* acm_size */
+
+ /* Round up to page size */
+ addl $(0xfff), %ebx
+ andl $(~0xfff), %ebx /* Aligned to a page (4 KiB) */
+
+ /* Use SSE registers to store local variables */
+ movd %eax, %xmm0
+ movd %ebx, %xmm1
+
+ /*
+ * Important note: The MTRRs must cache less than a page (4 KiB)
+ * of unused memory after the BIOS ACM. Not doing so on Haswell
+ * will cause a TXT reset with Class Code 5, Major Error Code 2.
+ *
+ * The caller must have checked that there are enough variable
+ * MTRRs to cache the ACM size prior to invoking this routine.
+ */
+ SET_UP_MTRRS_FOR_BIOS_ACM
+
+ /* Enable variable MTRRs */
+ movl $MTRR_DEF_TYPE_MSR, %ecx
+ rdmsr
+ orl $MTRR_DEF_TYPE_EN, %eax
+ wrmsr
+
+ /* Enable cache - CR0_NW is and stays clear */
+ movl %cr0, %eax
+ andl $~(CR0_CD), %eax
+ movl %eax, %cr0
+
+ /*
+ * Get function arguments.
+ * It's important to pass the exact ACM size as it's used by getsec to verify
+ * the integrity of ACM. Unlike the size for MTRR programming, which needs to
+ * be power of two.
+ *
+ * Note: Do not forget that CAR has been torn down, so the stack doesn't exist.
+ */
+ movl $2, %eax /* GETSEC[ENTERACCS] */
+ movd %xmm2, %ebx /* acm_base */
+ movd %xmm3, %ecx /* acm_size */
+ movl $0, %edx /* reserved, must be zero */
+ movl $0, %edi /* must be zero */
+ movl $0, %esi /* SCLEAN */
+
+ getsec
+
+ /*
+ * The platform state after SCLEAN is undefined. The only sane
+ * thing to do afterwards is to reset the platform. Note that
+ * the BIOS ACM should already reset the platform, so this code
+ * may not always be reached, but keep it here just to be sure.
+ */
+#if 1
+ movw $0xcf8, %dx
+ movl $0x8000F8AC, %eax
+ outl %eax, %dx
+
+ movw $0xcfc, %dx
+ inl %dx, %eax
+ andl $~(1 << 20), %eax
+ outl %eax, %dx
+#endif
+
+ movw $0xcf9, %dx
+ movb $0, %al
+ outb %al, %dx
+
+ movw $0xcf9, %dx
+ movb $0x0e, %al
+ outb %al, %dx
+
+ cli
+
+ hlt
+
+ ret
diff --git a/src/security/intel/txt/txt_getsec.h b/src/security/intel/txt/txt_getsec.h
index 78171a7d5a..f949c7d361 100644
--- a/src/security/intel/txt/txt_getsec.h
+++ b/src/security/intel/txt/txt_getsec.h
@@ -20,4 +20,7 @@ void getsec_enteraccs(const uint32_t esi,
const uint32_t acm_base,
const uint32_t acm_size);
+void getsec_sclean(const uint32_t acm_base,
+ const uint32_t acm_size);
+
#endif /* SECURITY_INTEL_TXT_REGISTER_H_ */