Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86/sgx: Initialize metadata for Enclave Page Cache (EPC) sections

Although carved out of normal DRAM, enclave memory is marked in the
system memory map as reserved and is not managed by the core mm. There
may be several regions spread across the system. Each contiguous region
is called an Enclave Page Cache (EPC) section. EPC sections are
enumerated via CPUID

Enclave pages can only be accessed when they are mapped as part of an
enclave, by a hardware thread running inside the enclave.

Parse CPUID data, create metadata for EPC pages and populate a simple
EPC page allocator. Although much smaller, ‘struct sgx_epc_page’
metadata is the SGX analog of the core mm ‘struct page’.

Similar to how the core mm’s page->flags encode zone and NUMA
information, embed the EPC section index to the first eight bits of
sgx_epc_page->desc. This allows a quick reverse lookup from EPC page to
EPC section. Existing client hardware supports only a single section,
while upcoming server hardware will support at most eight sections.
Thus, eight bits should be enough for long term needs.

Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
Co-developed-by: Serge Ayoun <serge.ayoun@intel.com>
Signed-off-by: Serge Ayoun <serge.ayoun@intel.com>
Co-developed-by: Jarkko Sakkinen <jarkko@kernel.org>
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
Signed-off-by: Borislav Petkov <bp@suse.de>
Acked-by: Jethro Beekman <jethro@fortanix.com>
Link: https://lkml.kernel.org/r/20201112220135.165028-6-jarkko@kernel.org

authored by

Sean Christopherson and committed by
Borislav Petkov
e7e05452 d205e0f1

+270
+17
arch/x86/Kconfig
··· 1930 1930 side channel attacks- equals the tsx=auto command line parameter. 1931 1931 endchoice 1932 1932 1933 + config X86_SGX 1934 + bool "Software Guard eXtensions (SGX)" 1935 + depends on X86_64 && CPU_SUP_INTEL 1936 + depends on CRYPTO=y 1937 + depends on CRYPTO_SHA256=y 1938 + select SRCU 1939 + select MMU_NOTIFIER 1940 + help 1941 + Intel(R) Software Guard eXtensions (SGX) is a set of CPU instructions 1942 + that can be used by applications to set aside private regions of code 1943 + and data, referred to as enclaves. An enclave's private memory can 1944 + only be accessed by code running within the enclave. Accesses from 1945 + outside the enclave, including other enclaves, are disallowed by 1946 + hardware. 1947 + 1948 + If unsure, say N. 1949 + 1933 1950 config EFI 1934 1951 bool "EFI runtime service support" 1935 1952 depends on ACPI
+1
arch/x86/kernel/cpu/Makefile
··· 48 48 obj-$(CONFIG_MTRR) += mtrr/ 49 49 obj-$(CONFIG_MICROCODE) += microcode/ 50 50 obj-$(CONFIG_X86_CPU_RESCTRL) += resctrl/ 51 + obj-$(CONFIG_X86_SGX) += sgx/ 51 52 52 53 obj-$(CONFIG_X86_LOCAL_APIC) += perfctr-watchdog.o 53 54
+2
arch/x86/kernel/cpu/sgx/Makefile
··· 1 + obj-y += \ 2 + main.o
+190
arch/x86/kernel/cpu/sgx/main.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright(c) 2016-20 Intel Corporation. */ 3 + 4 + #include <linux/freezer.h> 5 + #include <linux/highmem.h> 6 + #include <linux/kthread.h> 7 + #include <linux/pagemap.h> 8 + #include <linux/ratelimit.h> 9 + #include <linux/sched/mm.h> 10 + #include <linux/sched/signal.h> 11 + #include <linux/slab.h> 12 + #include "encls.h" 13 + 14 + struct sgx_epc_section sgx_epc_sections[SGX_MAX_EPC_SECTIONS]; 15 + static int sgx_nr_epc_sections; 16 + static struct task_struct *ksgxd_tsk; 17 + 18 + /* 19 + * Reset dirty EPC pages to uninitialized state. Laundry can be left with SECS 20 + * pages whose child pages blocked EREMOVE. 21 + */ 22 + static void sgx_sanitize_section(struct sgx_epc_section *section) 23 + { 24 + struct sgx_epc_page *page; 25 + LIST_HEAD(dirty); 26 + int ret; 27 + 28 + while (!list_empty(&section->laundry_list)) { 29 + if (kthread_should_stop()) 30 + return; 31 + 32 + spin_lock(&section->lock); 33 + 34 + page = list_first_entry(&section->laundry_list, 35 + struct sgx_epc_page, list); 36 + 37 + ret = __eremove(sgx_get_epc_virt_addr(page)); 38 + if (!ret) 39 + list_move(&page->list, &section->page_list); 40 + else 41 + list_move_tail(&page->list, &dirty); 42 + 43 + spin_unlock(&section->lock); 44 + 45 + cond_resched(); 46 + } 47 + 48 + list_splice(&dirty, &section->laundry_list); 49 + } 50 + 51 + static int ksgxd(void *p) 52 + { 53 + int i; 54 + 55 + set_freezable(); 56 + 57 + /* 58 + * Sanitize pages in order to recover from kexec(). The 2nd pass is 59 + * required for SECS pages, whose child pages blocked EREMOVE. 60 + */ 61 + for (i = 0; i < sgx_nr_epc_sections; i++) 62 + sgx_sanitize_section(&sgx_epc_sections[i]); 63 + 64 + for (i = 0; i < sgx_nr_epc_sections; i++) { 65 + sgx_sanitize_section(&sgx_epc_sections[i]); 66 + 67 + /* Should never happen. */ 68 + if (!list_empty(&sgx_epc_sections[i].laundry_list)) 69 + WARN(1, "EPC section %d has unsanitized pages.\n", i); 70 + } 71 + 72 + return 0; 73 + } 74 + 75 + static bool __init sgx_page_reclaimer_init(void) 76 + { 77 + struct task_struct *tsk; 78 + 79 + tsk = kthread_run(ksgxd, NULL, "ksgxd"); 80 + if (IS_ERR(tsk)) 81 + return false; 82 + 83 + ksgxd_tsk = tsk; 84 + 85 + return true; 86 + } 87 + 88 + static bool __init sgx_setup_epc_section(u64 phys_addr, u64 size, 89 + unsigned long index, 90 + struct sgx_epc_section *section) 91 + { 92 + unsigned long nr_pages = size >> PAGE_SHIFT; 93 + unsigned long i; 94 + 95 + section->virt_addr = memremap(phys_addr, size, MEMREMAP_WB); 96 + if (!section->virt_addr) 97 + return false; 98 + 99 + section->pages = vmalloc(nr_pages * sizeof(struct sgx_epc_page)); 100 + if (!section->pages) { 101 + memunmap(section->virt_addr); 102 + return false; 103 + } 104 + 105 + section->phys_addr = phys_addr; 106 + spin_lock_init(&section->lock); 107 + INIT_LIST_HEAD(&section->page_list); 108 + INIT_LIST_HEAD(&section->laundry_list); 109 + 110 + for (i = 0; i < nr_pages; i++) { 111 + section->pages[i].section = index; 112 + list_add_tail(&section->pages[i].list, &section->laundry_list); 113 + } 114 + 115 + return true; 116 + } 117 + 118 + /** 119 + * A section metric is concatenated in a way that @low bits 12-31 define the 120 + * bits 12-31 of the metric and @high bits 0-19 define the bits 32-51 of the 121 + * metric. 122 + */ 123 + static inline u64 __init sgx_calc_section_metric(u64 low, u64 high) 124 + { 125 + return (low & GENMASK_ULL(31, 12)) + 126 + ((high & GENMASK_ULL(19, 0)) << 32); 127 + } 128 + 129 + static bool __init sgx_page_cache_init(void) 130 + { 131 + u32 eax, ebx, ecx, edx, type; 132 + u64 pa, size; 133 + int i; 134 + 135 + for (i = 0; i < ARRAY_SIZE(sgx_epc_sections); i++) { 136 + cpuid_count(SGX_CPUID, i + SGX_CPUID_EPC, &eax, &ebx, &ecx, &edx); 137 + 138 + type = eax & SGX_CPUID_EPC_MASK; 139 + if (type == SGX_CPUID_EPC_INVALID) 140 + break; 141 + 142 + if (type != SGX_CPUID_EPC_SECTION) { 143 + pr_err_once("Unknown EPC section type: %u\n", type); 144 + break; 145 + } 146 + 147 + pa = sgx_calc_section_metric(eax, ebx); 148 + size = sgx_calc_section_metric(ecx, edx); 149 + 150 + pr_info("EPC section 0x%llx-0x%llx\n", pa, pa + size - 1); 151 + 152 + if (!sgx_setup_epc_section(pa, size, i, &sgx_epc_sections[i])) { 153 + pr_err("No free memory for an EPC section\n"); 154 + break; 155 + } 156 + 157 + sgx_nr_epc_sections++; 158 + } 159 + 160 + if (!sgx_nr_epc_sections) { 161 + pr_err("There are zero EPC sections.\n"); 162 + return false; 163 + } 164 + 165 + return true; 166 + } 167 + 168 + static void __init sgx_init(void) 169 + { 170 + int i; 171 + 172 + if (!boot_cpu_has(X86_FEATURE_SGX)) 173 + return; 174 + 175 + if (!sgx_page_cache_init()) 176 + return; 177 + 178 + if (!sgx_page_reclaimer_init()) 179 + goto err_page_cache; 180 + 181 + return; 182 + 183 + err_page_cache: 184 + for (i = 0; i < sgx_nr_epc_sections; i++) { 185 + vfree(sgx_epc_sections[i].pages); 186 + memunmap(sgx_epc_sections[i].virt_addr); 187 + } 188 + } 189 + 190 + device_initcall(sgx_init);
+60
arch/x86/kernel/cpu/sgx/sgx.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + #ifndef _X86_SGX_H 3 + #define _X86_SGX_H 4 + 5 + #include <linux/bitops.h> 6 + #include <linux/err.h> 7 + #include <linux/io.h> 8 + #include <linux/rwsem.h> 9 + #include <linux/types.h> 10 + #include <asm/asm.h> 11 + #include "arch.h" 12 + 13 + #undef pr_fmt 14 + #define pr_fmt(fmt) "sgx: " fmt 15 + 16 + #define SGX_MAX_EPC_SECTIONS 8 17 + 18 + struct sgx_epc_page { 19 + unsigned int section; 20 + struct list_head list; 21 + }; 22 + 23 + /* 24 + * The firmware can define multiple chunks of EPC to the different areas of the 25 + * physical memory e.g. for memory areas of the each node. This structure is 26 + * used to store EPC pages for one EPC section and virtual memory area where 27 + * the pages have been mapped. 28 + */ 29 + struct sgx_epc_section { 30 + unsigned long phys_addr; 31 + void *virt_addr; 32 + struct list_head page_list; 33 + struct list_head laundry_list; 34 + struct sgx_epc_page *pages; 35 + spinlock_t lock; 36 + }; 37 + 38 + extern struct sgx_epc_section sgx_epc_sections[SGX_MAX_EPC_SECTIONS]; 39 + 40 + static inline unsigned long sgx_get_epc_phys_addr(struct sgx_epc_page *page) 41 + { 42 + struct sgx_epc_section *section = &sgx_epc_sections[page->section]; 43 + unsigned long index; 44 + 45 + index = ((unsigned long)page - (unsigned long)section->pages) / sizeof(*page); 46 + 47 + return section->phys_addr + index * PAGE_SIZE; 48 + } 49 + 50 + static inline void *sgx_get_epc_virt_addr(struct sgx_epc_page *page) 51 + { 52 + struct sgx_epc_section *section = &sgx_epc_sections[page->section]; 53 + unsigned long index; 54 + 55 + index = ((unsigned long)page - (unsigned long)section->pages) / sizeof(*page); 56 + 57 + return section->virt_addr + index * PAGE_SIZE; 58 + } 59 + 60 + #endif /* _X86_SGX_H */