summarylogtreecommitdiffstats
path: root/0016-x86-sgx-Support-adding-of-pages-to-an-initialized-en.patch
diff options
context:
space:
mode:
Diffstat (limited to '0016-x86-sgx-Support-adding-of-pages-to-an-initialized-en.patch')
-rw-r--r--0016-x86-sgx-Support-adding-of-pages-to-an-initialized-en.patch187
1 files changed, 187 insertions, 0 deletions
diff --git a/0016-x86-sgx-Support-adding-of-pages-to-an-initialized-en.patch b/0016-x86-sgx-Support-adding-of-pages-to-an-initialized-en.patch
new file mode 100644
index 000000000000..38ad636ed843
--- /dev/null
+++ b/0016-x86-sgx-Support-adding-of-pages-to-an-initialized-en.patch
@@ -0,0 +1,187 @@
+From 467c051f0ccb4a65846d9c174fbb4587e028d008 Mon Sep 17 00:00:00 2001
+From: Reinette Chatre <reinette.chatre@intel.com>
+Date: Wed, 13 Apr 2022 14:10:16 -0700
+Subject: [PATCH 16/31] x86/sgx: Support adding of pages to an initialized
+ enclave
+
+With SGX1 an enclave needs to be created with its maximum memory demands
+allocated. Pages cannot be added to an enclave after it is initialized.
+SGX2 introduces a new function, ENCLS[EAUG], that can be used to add
+pages to an initialized enclave. With SGX2 the enclave still needs to
+set aside address space for its maximum memory demands during enclave
+creation, but all pages need not be added before enclave initialization.
+Pages can be added during enclave runtime.
+
+Add support for dynamically adding pages to an initialized enclave,
+architecturally limited to RW permission at creation but allowed to
+obtain RWX permissions after trusted enclave runs EMODPE. Add pages
+via the page fault handler at the time an enclave address without a
+backing enclave page is accessed, potentially directly reclaiming
+pages if no free pages are available.
+
+The enclave is still required to run ENCLU[EACCEPT] on the page before
+it can be used. A useful flow is for the enclave to run ENCLU[EACCEPT]
+on an uninitialized address. This will trigger the page fault handler
+that will add the enclave page and return execution to the enclave to
+repeat the ENCLU[EACCEPT] instruction, this time successful.
+
+If the enclave accesses an uninitialized address in another way, for
+example by expanding the enclave stack to a page that has not yet been
+added, then the page fault handler would add the page on the first
+write but upon returning to the enclave the instruction that triggered
+the page fault would be repeated and since ENCLU[EACCEPT] was not run
+yet it would trigger a second page fault, this time with the SGX flag
+set in the page fault error code. This can only be recovered by entering
+the enclave again and directly running the ENCLU[EACCEPT] instruction on
+the now initialized address.
+
+Accessing an uninitialized address from outside the enclave also
+triggers this flow but the page will remain inaccessible (access will
+result in #PF) until accepted from within the enclave via
+ENCLU[EACCEPT].
+
+Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
+Tested-by: Jarkko Sakkinen <jarkko@kernel.org>
+Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
+---
+ arch/x86/kernel/cpu/sgx/encl.c | 117 +++++++++++++++++++++++++++++++++
+ 1 file changed, 117 insertions(+)
+
+diff --git a/arch/x86/kernel/cpu/sgx/encl.c b/arch/x86/kernel/cpu/sgx/encl.c
+index 8729b254b0cc..91d648f68080 100644
+--- a/arch/x86/kernel/cpu/sgx/encl.c
++++ b/arch/x86/kernel/cpu/sgx/encl.c
+@@ -153,6 +153,112 @@ struct sgx_encl_page *sgx_encl_load_page(struct sgx_encl *encl,
+ return __sgx_encl_load_page(encl, entry);
+ }
+
++/**
++ * sgx_encl_eaug_page() - Dynamically add page to initialized enclave
++ * @vma: VMA obtained from fault info from where page is accessed
++ * @encl: enclave accessing the page
++ * @addr: address that triggered the page fault
++ *
++ * When an initialized enclave accesses a page with no backing EPC page
++ * on a SGX2 system then the EPC can be added dynamically via the SGX2
++ * ENCLS[EAUG] instruction.
++ *
++ * Returns: Appropriate vm_fault_t: VM_FAULT_NOPAGE when PTE was installed
++ * successfully, VM_FAULT_SIGBUS or VM_FAULT_OOM as error otherwise.
++ */
++static vm_fault_t sgx_encl_eaug_page(struct vm_area_struct *vma,
++ struct sgx_encl *encl, unsigned long addr)
++{
++ struct sgx_pageinfo pginfo = {0};
++ struct sgx_encl_page *encl_page;
++ struct sgx_epc_page *epc_page;
++ struct sgx_va_page *va_page;
++ unsigned long phys_addr;
++ u64 secinfo_flags;
++ vm_fault_t vmret;
++ int ret;
++
++ if (!test_bit(SGX_ENCL_INITIALIZED, &encl->flags))
++ return VM_FAULT_SIGBUS;
++
++ /*
++ * Ignore internal permission checking for dynamically added pages.
++ * They matter only for data added during the pre-initialization
++ * phase. The enclave decides the permissions by the means of
++ * EACCEPT, EACCEPTCOPY and EMODPE.
++ */
++ secinfo_flags = SGX_SECINFO_R | SGX_SECINFO_W | SGX_SECINFO_X;
++ encl_page = sgx_encl_page_alloc(encl, addr - encl->base, secinfo_flags);
++ if (IS_ERR(encl_page))
++ return VM_FAULT_OOM;
++
++ epc_page = sgx_alloc_epc_page(encl_page, true);
++ if (IS_ERR(epc_page)) {
++ kfree(encl_page);
++ return VM_FAULT_SIGBUS;
++ }
++
++ mutex_lock(&encl->lock);
++
++ va_page = sgx_encl_grow(encl, false);
++ if (IS_ERR(va_page)) {
++ ret = PTR_ERR(va_page);
++ goto err_out_unlock;
++ }
++
++ if (va_page)
++ list_add(&va_page->list, &encl->va_pages);
++
++ ret = xa_insert(&encl->page_array, PFN_DOWN(encl_page->desc),
++ encl_page, GFP_KERNEL);
++ /*
++ * If ret == -EBUSY then page was created in another flow while
++ * running without encl->lock
++ */
++ if (ret)
++ goto err_out_shrink;
++
++ pginfo.secs = (unsigned long)sgx_get_epc_virt_addr(encl->secs.epc_page);
++ pginfo.addr = encl_page->desc & PAGE_MASK;
++ pginfo.metadata = 0;
++
++ ret = __eaug(&pginfo, sgx_get_epc_virt_addr(epc_page));
++ if (ret)
++ goto err_out;
++
++ encl_page->encl = encl;
++ encl_page->epc_page = epc_page;
++ encl_page->type = SGX_PAGE_TYPE_REG;
++ encl->secs_child_cnt++;
++
++ sgx_mark_page_reclaimable(encl_page->epc_page);
++
++ phys_addr = sgx_get_epc_phys_addr(epc_page);
++ /*
++ * Do not undo everything when creating PTE entry fails - next #PF
++ * would find page ready for a PTE.
++ */
++ vmret = vmf_insert_pfn(vma, addr, PFN_DOWN(phys_addr));
++ if (vmret != VM_FAULT_NOPAGE) {
++ mutex_unlock(&encl->lock);
++ return VM_FAULT_SIGBUS;
++ }
++ mutex_unlock(&encl->lock);
++ return VM_FAULT_NOPAGE;
++
++err_out:
++ xa_erase(&encl->page_array, PFN_DOWN(encl_page->desc));
++
++err_out_shrink:
++ sgx_encl_shrink(encl, va_page);
++err_out_unlock:
++ mutex_unlock(&encl->lock);
++ sgx_encl_free_epc_page(epc_page);
++ kfree(encl_page);
++
++ return VM_FAULT_SIGBUS;
++}
++
+ static vm_fault_t sgx_vma_fault(struct vm_fault *vmf)
+ {
+ unsigned long addr = (unsigned long)vmf->address;
+@@ -172,6 +278,17 @@ static vm_fault_t sgx_vma_fault(struct vm_fault *vmf)
+ if (unlikely(!encl))
+ return VM_FAULT_SIGBUS;
+
++ /*
++ * The page_array keeps track of all enclave pages, whether they
++ * are swapped out or not. If there is no entry for this page and
++ * the system supports SGX2 then it is possible to dynamically add
++ * a new enclave page. This is only possible for an initialized
++ * enclave that will be checked for right away.
++ */
++ if (cpu_feature_enabled(X86_FEATURE_SGX2) &&
++ (!xa_load(&encl->page_array, PFN_DOWN(addr))))
++ return sgx_encl_eaug_page(vma, encl, addr);
++
+ mutex_lock(&encl->lock);
+
+ entry = sgx_encl_load_page_in_vma(encl, addr, vma->vm_flags);
+--
+2.35.2
+