162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright(c) 2016-20 Intel Corporation. */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <asm/mman.h> 562306a36Sopenharmony_ci#include <asm/sgx.h> 662306a36Sopenharmony_ci#include <linux/mman.h> 762306a36Sopenharmony_ci#include <linux/delay.h> 862306a36Sopenharmony_ci#include <linux/file.h> 962306a36Sopenharmony_ci#include <linux/hashtable.h> 1062306a36Sopenharmony_ci#include <linux/highmem.h> 1162306a36Sopenharmony_ci#include <linux/ratelimit.h> 1262306a36Sopenharmony_ci#include <linux/sched/signal.h> 1362306a36Sopenharmony_ci#include <linux/shmem_fs.h> 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci#include <linux/suspend.h> 1662306a36Sopenharmony_ci#include "driver.h" 1762306a36Sopenharmony_ci#include "encl.h" 1862306a36Sopenharmony_ci#include "encls.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistruct sgx_va_page *sgx_encl_grow(struct sgx_encl *encl, bool reclaim) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci struct sgx_va_page *va_page = NULL; 2362306a36Sopenharmony_ci void *err; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci BUILD_BUG_ON(SGX_VA_SLOT_COUNT != 2662306a36Sopenharmony_ci (SGX_ENCL_PAGE_VA_OFFSET_MASK >> 3) + 1); 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci if (!(encl->page_cnt % SGX_VA_SLOT_COUNT)) { 2962306a36Sopenharmony_ci va_page = kzalloc(sizeof(*va_page), GFP_KERNEL); 3062306a36Sopenharmony_ci if (!va_page) 3162306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci va_page->epc_page = sgx_alloc_va_page(reclaim); 3462306a36Sopenharmony_ci if (IS_ERR(va_page->epc_page)) { 3562306a36Sopenharmony_ci err = ERR_CAST(va_page->epc_page); 3662306a36Sopenharmony_ci kfree(va_page); 3762306a36Sopenharmony_ci return err; 3862306a36Sopenharmony_ci } 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci WARN_ON_ONCE(encl->page_cnt % SGX_VA_SLOT_COUNT); 4162306a36Sopenharmony_ci } 4262306a36Sopenharmony_ci encl->page_cnt++; 4362306a36Sopenharmony_ci return va_page; 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_civoid sgx_encl_shrink(struct sgx_encl *encl, struct sgx_va_page *va_page) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci encl->page_cnt--; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci if (va_page) { 5162306a36Sopenharmony_ci sgx_encl_free_epc_page(va_page->epc_page); 5262306a36Sopenharmony_ci list_del(&va_page->list); 5362306a36Sopenharmony_ci kfree(va_page); 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic int sgx_encl_create(struct sgx_encl *encl, struct sgx_secs *secs) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci struct sgx_epc_page *secs_epc; 6062306a36Sopenharmony_ci struct sgx_va_page *va_page; 6162306a36Sopenharmony_ci struct sgx_pageinfo pginfo; 6262306a36Sopenharmony_ci struct sgx_secinfo secinfo; 6362306a36Sopenharmony_ci unsigned long encl_size; 6462306a36Sopenharmony_ci struct file *backing; 6562306a36Sopenharmony_ci long ret; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci va_page = sgx_encl_grow(encl, true); 6862306a36Sopenharmony_ci if (IS_ERR(va_page)) 6962306a36Sopenharmony_ci return PTR_ERR(va_page); 7062306a36Sopenharmony_ci else if (va_page) 7162306a36Sopenharmony_ci list_add(&va_page->list, &encl->va_pages); 7262306a36Sopenharmony_ci /* else the tail page of the VA page list had free slots. */ 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci /* The extra page goes to SECS. */ 7562306a36Sopenharmony_ci encl_size = secs->size + PAGE_SIZE; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci backing = shmem_file_setup("SGX backing", encl_size + (encl_size >> 5), 7862306a36Sopenharmony_ci VM_NORESERVE); 7962306a36Sopenharmony_ci if (IS_ERR(backing)) { 8062306a36Sopenharmony_ci ret = PTR_ERR(backing); 8162306a36Sopenharmony_ci goto err_out_shrink; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci encl->backing = backing; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci secs_epc = sgx_alloc_epc_page(&encl->secs, true); 8762306a36Sopenharmony_ci if (IS_ERR(secs_epc)) { 8862306a36Sopenharmony_ci ret = PTR_ERR(secs_epc); 8962306a36Sopenharmony_ci goto err_out_backing; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci encl->secs.epc_page = secs_epc; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci pginfo.addr = 0; 9562306a36Sopenharmony_ci pginfo.contents = (unsigned long)secs; 9662306a36Sopenharmony_ci pginfo.metadata = (unsigned long)&secinfo; 9762306a36Sopenharmony_ci pginfo.secs = 0; 9862306a36Sopenharmony_ci memset(&secinfo, 0, sizeof(secinfo)); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci ret = __ecreate((void *)&pginfo, sgx_get_epc_virt_addr(secs_epc)); 10162306a36Sopenharmony_ci if (ret) { 10262306a36Sopenharmony_ci ret = -EIO; 10362306a36Sopenharmony_ci goto err_out; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci if (secs->attributes & SGX_ATTR_DEBUG) 10762306a36Sopenharmony_ci set_bit(SGX_ENCL_DEBUG, &encl->flags); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci encl->secs.encl = encl; 11062306a36Sopenharmony_ci encl->secs.type = SGX_PAGE_TYPE_SECS; 11162306a36Sopenharmony_ci encl->base = secs->base; 11262306a36Sopenharmony_ci encl->size = secs->size; 11362306a36Sopenharmony_ci encl->attributes = secs->attributes; 11462306a36Sopenharmony_ci encl->attributes_mask = SGX_ATTR_UNPRIV_MASK; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci /* Set only after completion, as encl->lock has not been taken. */ 11762306a36Sopenharmony_ci set_bit(SGX_ENCL_CREATED, &encl->flags); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci return 0; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cierr_out: 12262306a36Sopenharmony_ci sgx_encl_free_epc_page(encl->secs.epc_page); 12362306a36Sopenharmony_ci encl->secs.epc_page = NULL; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cierr_out_backing: 12662306a36Sopenharmony_ci fput(encl->backing); 12762306a36Sopenharmony_ci encl->backing = NULL; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cierr_out_shrink: 13062306a36Sopenharmony_ci sgx_encl_shrink(encl, va_page); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci return ret; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/** 13662306a36Sopenharmony_ci * sgx_ioc_enclave_create() - handler for %SGX_IOC_ENCLAVE_CREATE 13762306a36Sopenharmony_ci * @encl: An enclave pointer. 13862306a36Sopenharmony_ci * @arg: The ioctl argument. 13962306a36Sopenharmony_ci * 14062306a36Sopenharmony_ci * Allocate kernel data structures for the enclave and invoke ECREATE. 14162306a36Sopenharmony_ci * 14262306a36Sopenharmony_ci * Return: 14362306a36Sopenharmony_ci * - 0: Success. 14462306a36Sopenharmony_ci * - -EIO: ECREATE failed. 14562306a36Sopenharmony_ci * - -errno: POSIX error. 14662306a36Sopenharmony_ci */ 14762306a36Sopenharmony_cistatic long sgx_ioc_enclave_create(struct sgx_encl *encl, void __user *arg) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci struct sgx_enclave_create create_arg; 15062306a36Sopenharmony_ci void *secs; 15162306a36Sopenharmony_ci int ret; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (test_bit(SGX_ENCL_CREATED, &encl->flags)) 15462306a36Sopenharmony_ci return -EINVAL; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (copy_from_user(&create_arg, arg, sizeof(create_arg))) 15762306a36Sopenharmony_ci return -EFAULT; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci secs = kmalloc(PAGE_SIZE, GFP_KERNEL); 16062306a36Sopenharmony_ci if (!secs) 16162306a36Sopenharmony_ci return -ENOMEM; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if (copy_from_user(secs, (void __user *)create_arg.src, PAGE_SIZE)) 16462306a36Sopenharmony_ci ret = -EFAULT; 16562306a36Sopenharmony_ci else 16662306a36Sopenharmony_ci ret = sgx_encl_create(encl, secs); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci kfree(secs); 16962306a36Sopenharmony_ci return ret; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic int sgx_validate_secinfo(struct sgx_secinfo *secinfo) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci u64 perm = secinfo->flags & SGX_SECINFO_PERMISSION_MASK; 17562306a36Sopenharmony_ci u64 pt = secinfo->flags & SGX_SECINFO_PAGE_TYPE_MASK; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (pt != SGX_SECINFO_REG && pt != SGX_SECINFO_TCS) 17862306a36Sopenharmony_ci return -EINVAL; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if ((perm & SGX_SECINFO_W) && !(perm & SGX_SECINFO_R)) 18162306a36Sopenharmony_ci return -EINVAL; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* 18462306a36Sopenharmony_ci * CPU will silently overwrite the permissions as zero, which means 18562306a36Sopenharmony_ci * that we need to validate it ourselves. 18662306a36Sopenharmony_ci */ 18762306a36Sopenharmony_ci if (pt == SGX_SECINFO_TCS && perm) 18862306a36Sopenharmony_ci return -EINVAL; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (secinfo->flags & SGX_SECINFO_RESERVED_MASK) 19162306a36Sopenharmony_ci return -EINVAL; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (memchr_inv(secinfo->reserved, 0, sizeof(secinfo->reserved))) 19462306a36Sopenharmony_ci return -EINVAL; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci return 0; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic int __sgx_encl_add_page(struct sgx_encl *encl, 20062306a36Sopenharmony_ci struct sgx_encl_page *encl_page, 20162306a36Sopenharmony_ci struct sgx_epc_page *epc_page, 20262306a36Sopenharmony_ci struct sgx_secinfo *secinfo, unsigned long src) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci struct sgx_pageinfo pginfo; 20562306a36Sopenharmony_ci struct vm_area_struct *vma; 20662306a36Sopenharmony_ci struct page *src_page; 20762306a36Sopenharmony_ci int ret; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci /* Deny noexec. */ 21062306a36Sopenharmony_ci vma = find_vma(current->mm, src); 21162306a36Sopenharmony_ci if (!vma) 21262306a36Sopenharmony_ci return -EFAULT; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (!(vma->vm_flags & VM_MAYEXEC)) 21562306a36Sopenharmony_ci return -EACCES; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci ret = get_user_pages(src, 1, 0, &src_page); 21862306a36Sopenharmony_ci if (ret < 1) 21962306a36Sopenharmony_ci return -EFAULT; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci pginfo.secs = (unsigned long)sgx_get_epc_virt_addr(encl->secs.epc_page); 22262306a36Sopenharmony_ci pginfo.addr = encl_page->desc & PAGE_MASK; 22362306a36Sopenharmony_ci pginfo.metadata = (unsigned long)secinfo; 22462306a36Sopenharmony_ci pginfo.contents = (unsigned long)kmap_local_page(src_page); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci ret = __eadd(&pginfo, sgx_get_epc_virt_addr(epc_page)); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci kunmap_local((void *)pginfo.contents); 22962306a36Sopenharmony_ci put_page(src_page); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci return ret ? -EIO : 0; 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci/* 23562306a36Sopenharmony_ci * If the caller requires measurement of the page as a proof for the content, 23662306a36Sopenharmony_ci * use EEXTEND to add a measurement for 256 bytes of the page. Repeat this 23762306a36Sopenharmony_ci * operation until the entire page is measured." 23862306a36Sopenharmony_ci */ 23962306a36Sopenharmony_cistatic int __sgx_encl_extend(struct sgx_encl *encl, 24062306a36Sopenharmony_ci struct sgx_epc_page *epc_page) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci unsigned long offset; 24362306a36Sopenharmony_ci int ret; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci for (offset = 0; offset < PAGE_SIZE; offset += SGX_EEXTEND_BLOCK_SIZE) { 24662306a36Sopenharmony_ci ret = __eextend(sgx_get_epc_virt_addr(encl->secs.epc_page), 24762306a36Sopenharmony_ci sgx_get_epc_virt_addr(epc_page) + offset); 24862306a36Sopenharmony_ci if (ret) { 24962306a36Sopenharmony_ci if (encls_failed(ret)) 25062306a36Sopenharmony_ci ENCLS_WARN(ret, "EEXTEND"); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci return -EIO; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci return 0; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic int sgx_encl_add_page(struct sgx_encl *encl, unsigned long src, 26062306a36Sopenharmony_ci unsigned long offset, struct sgx_secinfo *secinfo, 26162306a36Sopenharmony_ci unsigned long flags) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci struct sgx_encl_page *encl_page; 26462306a36Sopenharmony_ci struct sgx_epc_page *epc_page; 26562306a36Sopenharmony_ci struct sgx_va_page *va_page; 26662306a36Sopenharmony_ci int ret; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci encl_page = sgx_encl_page_alloc(encl, offset, secinfo->flags); 26962306a36Sopenharmony_ci if (IS_ERR(encl_page)) 27062306a36Sopenharmony_ci return PTR_ERR(encl_page); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci epc_page = sgx_alloc_epc_page(encl_page, true); 27362306a36Sopenharmony_ci if (IS_ERR(epc_page)) { 27462306a36Sopenharmony_ci kfree(encl_page); 27562306a36Sopenharmony_ci return PTR_ERR(epc_page); 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci va_page = sgx_encl_grow(encl, true); 27962306a36Sopenharmony_ci if (IS_ERR(va_page)) { 28062306a36Sopenharmony_ci ret = PTR_ERR(va_page); 28162306a36Sopenharmony_ci goto err_out_free; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci mmap_read_lock(current->mm); 28562306a36Sopenharmony_ci mutex_lock(&encl->lock); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* 28862306a36Sopenharmony_ci * Adding to encl->va_pages must be done under encl->lock. Ditto for 28962306a36Sopenharmony_ci * deleting (via sgx_encl_shrink()) in the error path. 29062306a36Sopenharmony_ci */ 29162306a36Sopenharmony_ci if (va_page) 29262306a36Sopenharmony_ci list_add(&va_page->list, &encl->va_pages); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* 29562306a36Sopenharmony_ci * Insert prior to EADD in case of OOM. EADD modifies MRENCLAVE, i.e. 29662306a36Sopenharmony_ci * can't be gracefully unwound, while failure on EADD/EXTEND is limited 29762306a36Sopenharmony_ci * to userspace errors (or kernel/hardware bugs). 29862306a36Sopenharmony_ci */ 29962306a36Sopenharmony_ci ret = xa_insert(&encl->page_array, PFN_DOWN(encl_page->desc), 30062306a36Sopenharmony_ci encl_page, GFP_KERNEL); 30162306a36Sopenharmony_ci if (ret) 30262306a36Sopenharmony_ci goto err_out_unlock; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci ret = __sgx_encl_add_page(encl, encl_page, epc_page, secinfo, 30562306a36Sopenharmony_ci src); 30662306a36Sopenharmony_ci if (ret) 30762306a36Sopenharmony_ci goto err_out; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* 31062306a36Sopenharmony_ci * Complete the "add" before doing the "extend" so that the "add" 31162306a36Sopenharmony_ci * isn't in a half-baked state in the extremely unlikely scenario 31262306a36Sopenharmony_ci * the enclave will be destroyed in response to EEXTEND failure. 31362306a36Sopenharmony_ci */ 31462306a36Sopenharmony_ci encl_page->encl = encl; 31562306a36Sopenharmony_ci encl_page->epc_page = epc_page; 31662306a36Sopenharmony_ci encl_page->type = (secinfo->flags & SGX_SECINFO_PAGE_TYPE_MASK) >> 8; 31762306a36Sopenharmony_ci encl->secs_child_cnt++; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (flags & SGX_PAGE_MEASURE) { 32062306a36Sopenharmony_ci ret = __sgx_encl_extend(encl, epc_page); 32162306a36Sopenharmony_ci if (ret) 32262306a36Sopenharmony_ci goto err_out; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci sgx_mark_page_reclaimable(encl_page->epc_page); 32662306a36Sopenharmony_ci mutex_unlock(&encl->lock); 32762306a36Sopenharmony_ci mmap_read_unlock(current->mm); 32862306a36Sopenharmony_ci return ret; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cierr_out: 33162306a36Sopenharmony_ci xa_erase(&encl->page_array, PFN_DOWN(encl_page->desc)); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cierr_out_unlock: 33462306a36Sopenharmony_ci sgx_encl_shrink(encl, va_page); 33562306a36Sopenharmony_ci mutex_unlock(&encl->lock); 33662306a36Sopenharmony_ci mmap_read_unlock(current->mm); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cierr_out_free: 33962306a36Sopenharmony_ci sgx_encl_free_epc_page(epc_page); 34062306a36Sopenharmony_ci kfree(encl_page); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci return ret; 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci/* 34662306a36Sopenharmony_ci * Ensure user provided offset and length values are valid for 34762306a36Sopenharmony_ci * an enclave. 34862306a36Sopenharmony_ci */ 34962306a36Sopenharmony_cistatic int sgx_validate_offset_length(struct sgx_encl *encl, 35062306a36Sopenharmony_ci unsigned long offset, 35162306a36Sopenharmony_ci unsigned long length) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci if (!IS_ALIGNED(offset, PAGE_SIZE)) 35462306a36Sopenharmony_ci return -EINVAL; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci if (!length || !IS_ALIGNED(length, PAGE_SIZE)) 35762306a36Sopenharmony_ci return -EINVAL; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (offset + length < offset) 36062306a36Sopenharmony_ci return -EINVAL; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (offset + length - PAGE_SIZE >= encl->size) 36362306a36Sopenharmony_ci return -EINVAL; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci return 0; 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci/** 36962306a36Sopenharmony_ci * sgx_ioc_enclave_add_pages() - The handler for %SGX_IOC_ENCLAVE_ADD_PAGES 37062306a36Sopenharmony_ci * @encl: an enclave pointer 37162306a36Sopenharmony_ci * @arg: a user pointer to a struct sgx_enclave_add_pages instance 37262306a36Sopenharmony_ci * 37362306a36Sopenharmony_ci * Add one or more pages to an uninitialized enclave, and optionally extend the 37462306a36Sopenharmony_ci * measurement with the contents of the page. The SECINFO and measurement mask 37562306a36Sopenharmony_ci * are applied to all pages. 37662306a36Sopenharmony_ci * 37762306a36Sopenharmony_ci * A SECINFO for a TCS is required to always contain zero permissions because 37862306a36Sopenharmony_ci * CPU silently zeros them. Allowing anything else would cause a mismatch in 37962306a36Sopenharmony_ci * the measurement. 38062306a36Sopenharmony_ci * 38162306a36Sopenharmony_ci * mmap()'s protection bits are capped by the page permissions. For each page 38262306a36Sopenharmony_ci * address, the maximum protection bits are computed with the following 38362306a36Sopenharmony_ci * heuristics: 38462306a36Sopenharmony_ci * 38562306a36Sopenharmony_ci * 1. A regular page: PROT_R, PROT_W and PROT_X match the SECINFO permissions. 38662306a36Sopenharmony_ci * 2. A TCS page: PROT_R | PROT_W. 38762306a36Sopenharmony_ci * 38862306a36Sopenharmony_ci * mmap() is not allowed to surpass the minimum of the maximum protection bits 38962306a36Sopenharmony_ci * within the given address range. 39062306a36Sopenharmony_ci * 39162306a36Sopenharmony_ci * The function deinitializes kernel data structures for enclave and returns 39262306a36Sopenharmony_ci * -EIO in any of the following conditions: 39362306a36Sopenharmony_ci * 39462306a36Sopenharmony_ci * - Enclave Page Cache (EPC), the physical memory holding enclaves, has 39562306a36Sopenharmony_ci * been invalidated. This will cause EADD and EEXTEND to fail. 39662306a36Sopenharmony_ci * - If the source address is corrupted somehow when executing EADD. 39762306a36Sopenharmony_ci * 39862306a36Sopenharmony_ci * Return: 39962306a36Sopenharmony_ci * - 0: Success. 40062306a36Sopenharmony_ci * - -EACCES: The source page is located in a noexec partition. 40162306a36Sopenharmony_ci * - -ENOMEM: Out of EPC pages. 40262306a36Sopenharmony_ci * - -EINTR: The call was interrupted before data was processed. 40362306a36Sopenharmony_ci * - -EIO: Either EADD or EEXTEND failed because invalid source address 40462306a36Sopenharmony_ci * or power cycle. 40562306a36Sopenharmony_ci * - -errno: POSIX error. 40662306a36Sopenharmony_ci */ 40762306a36Sopenharmony_cistatic long sgx_ioc_enclave_add_pages(struct sgx_encl *encl, void __user *arg) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci struct sgx_enclave_add_pages add_arg; 41062306a36Sopenharmony_ci struct sgx_secinfo secinfo; 41162306a36Sopenharmony_ci unsigned long c; 41262306a36Sopenharmony_ci int ret; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci if (!test_bit(SGX_ENCL_CREATED, &encl->flags) || 41562306a36Sopenharmony_ci test_bit(SGX_ENCL_INITIALIZED, &encl->flags)) 41662306a36Sopenharmony_ci return -EINVAL; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci if (copy_from_user(&add_arg, arg, sizeof(add_arg))) 41962306a36Sopenharmony_ci return -EFAULT; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci if (!IS_ALIGNED(add_arg.src, PAGE_SIZE)) 42262306a36Sopenharmony_ci return -EINVAL; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci if (sgx_validate_offset_length(encl, add_arg.offset, add_arg.length)) 42562306a36Sopenharmony_ci return -EINVAL; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (copy_from_user(&secinfo, (void __user *)add_arg.secinfo, 42862306a36Sopenharmony_ci sizeof(secinfo))) 42962306a36Sopenharmony_ci return -EFAULT; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci if (sgx_validate_secinfo(&secinfo)) 43262306a36Sopenharmony_ci return -EINVAL; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci for (c = 0 ; c < add_arg.length; c += PAGE_SIZE) { 43562306a36Sopenharmony_ci if (signal_pending(current)) { 43662306a36Sopenharmony_ci if (!c) 43762306a36Sopenharmony_ci ret = -ERESTARTSYS; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci break; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (need_resched()) 44362306a36Sopenharmony_ci cond_resched(); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci ret = sgx_encl_add_page(encl, add_arg.src + c, add_arg.offset + c, 44662306a36Sopenharmony_ci &secinfo, add_arg.flags); 44762306a36Sopenharmony_ci if (ret) 44862306a36Sopenharmony_ci break; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci add_arg.count = c; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (copy_to_user(arg, &add_arg, sizeof(add_arg))) 45462306a36Sopenharmony_ci return -EFAULT; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci return ret; 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_cistatic int __sgx_get_key_hash(struct crypto_shash *tfm, const void *modulus, 46062306a36Sopenharmony_ci void *hash) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci SHASH_DESC_ON_STACK(shash, tfm); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci shash->tfm = tfm; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci return crypto_shash_digest(shash, modulus, SGX_MODULUS_SIZE, hash); 46762306a36Sopenharmony_ci} 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_cistatic int sgx_get_key_hash(const void *modulus, void *hash) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci struct crypto_shash *tfm; 47262306a36Sopenharmony_ci int ret; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci tfm = crypto_alloc_shash("sha256", 0, CRYPTO_ALG_ASYNC); 47562306a36Sopenharmony_ci if (IS_ERR(tfm)) 47662306a36Sopenharmony_ci return PTR_ERR(tfm); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci ret = __sgx_get_key_hash(tfm, modulus, hash); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci crypto_free_shash(tfm); 48162306a36Sopenharmony_ci return ret; 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cistatic int sgx_encl_init(struct sgx_encl *encl, struct sgx_sigstruct *sigstruct, 48562306a36Sopenharmony_ci void *token) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci u64 mrsigner[4]; 48862306a36Sopenharmony_ci int i, j; 48962306a36Sopenharmony_ci void *addr; 49062306a36Sopenharmony_ci int ret; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci /* 49362306a36Sopenharmony_ci * Deny initializing enclaves with attributes (namely provisioning) 49462306a36Sopenharmony_ci * that have not been explicitly allowed. 49562306a36Sopenharmony_ci */ 49662306a36Sopenharmony_ci if (encl->attributes & ~encl->attributes_mask) 49762306a36Sopenharmony_ci return -EACCES; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci /* 50062306a36Sopenharmony_ci * Attributes should not be enforced *only* against what's available on 50162306a36Sopenharmony_ci * platform (done in sgx_encl_create) but checked and enforced against 50262306a36Sopenharmony_ci * the mask for enforcement in sigstruct. For example an enclave could 50362306a36Sopenharmony_ci * opt to sign with AVX bit in xfrm, but still be loadable on a platform 50462306a36Sopenharmony_ci * without it if the sigstruct->body.attributes_mask does not turn that 50562306a36Sopenharmony_ci * bit on. 50662306a36Sopenharmony_ci */ 50762306a36Sopenharmony_ci if (sigstruct->body.attributes & sigstruct->body.attributes_mask & 50862306a36Sopenharmony_ci sgx_attributes_reserved_mask) 50962306a36Sopenharmony_ci return -EINVAL; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci if (sigstruct->body.miscselect & sigstruct->body.misc_mask & 51262306a36Sopenharmony_ci sgx_misc_reserved_mask) 51362306a36Sopenharmony_ci return -EINVAL; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci if (sigstruct->body.xfrm & sigstruct->body.xfrm_mask & 51662306a36Sopenharmony_ci sgx_xfrm_reserved_mask) 51762306a36Sopenharmony_ci return -EINVAL; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci ret = sgx_get_key_hash(sigstruct->modulus, mrsigner); 52062306a36Sopenharmony_ci if (ret) 52162306a36Sopenharmony_ci return ret; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci mutex_lock(&encl->lock); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci /* 52662306a36Sopenharmony_ci * ENCLS[EINIT] is interruptible because it has such a high latency, 52762306a36Sopenharmony_ci * e.g. 50k+ cycles on success. If an IRQ/NMI/SMI becomes pending, 52862306a36Sopenharmony_ci * EINIT may fail with SGX_UNMASKED_EVENT so that the event can be 52962306a36Sopenharmony_ci * serviced. 53062306a36Sopenharmony_ci */ 53162306a36Sopenharmony_ci for (i = 0; i < SGX_EINIT_SLEEP_COUNT; i++) { 53262306a36Sopenharmony_ci for (j = 0; j < SGX_EINIT_SPIN_COUNT; j++) { 53362306a36Sopenharmony_ci addr = sgx_get_epc_virt_addr(encl->secs.epc_page); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci preempt_disable(); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci sgx_update_lepubkeyhash(mrsigner); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci ret = __einit(sigstruct, token, addr); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci preempt_enable(); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if (ret == SGX_UNMASKED_EVENT) 54462306a36Sopenharmony_ci continue; 54562306a36Sopenharmony_ci else 54662306a36Sopenharmony_ci break; 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci if (ret != SGX_UNMASKED_EVENT) 55062306a36Sopenharmony_ci break; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci msleep_interruptible(SGX_EINIT_SLEEP_TIME); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci if (signal_pending(current)) { 55562306a36Sopenharmony_ci ret = -ERESTARTSYS; 55662306a36Sopenharmony_ci goto err_out; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci if (encls_faulted(ret)) { 56162306a36Sopenharmony_ci if (encls_failed(ret)) 56262306a36Sopenharmony_ci ENCLS_WARN(ret, "EINIT"); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci ret = -EIO; 56562306a36Sopenharmony_ci } else if (ret) { 56662306a36Sopenharmony_ci pr_debug("EINIT returned %d\n", ret); 56762306a36Sopenharmony_ci ret = -EPERM; 56862306a36Sopenharmony_ci } else { 56962306a36Sopenharmony_ci set_bit(SGX_ENCL_INITIALIZED, &encl->flags); 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_cierr_out: 57362306a36Sopenharmony_ci mutex_unlock(&encl->lock); 57462306a36Sopenharmony_ci return ret; 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci/** 57862306a36Sopenharmony_ci * sgx_ioc_enclave_init() - handler for %SGX_IOC_ENCLAVE_INIT 57962306a36Sopenharmony_ci * @encl: an enclave pointer 58062306a36Sopenharmony_ci * @arg: userspace pointer to a struct sgx_enclave_init instance 58162306a36Sopenharmony_ci * 58262306a36Sopenharmony_ci * Flush any outstanding enqueued EADD operations and perform EINIT. The 58362306a36Sopenharmony_ci * Launch Enclave Public Key Hash MSRs are rewritten as necessary to match 58462306a36Sopenharmony_ci * the enclave's MRSIGNER, which is caculated from the provided sigstruct. 58562306a36Sopenharmony_ci * 58662306a36Sopenharmony_ci * Return: 58762306a36Sopenharmony_ci * - 0: Success. 58862306a36Sopenharmony_ci * - -EPERM: Invalid SIGSTRUCT. 58962306a36Sopenharmony_ci * - -EIO: EINIT failed because of a power cycle. 59062306a36Sopenharmony_ci * - -errno: POSIX error. 59162306a36Sopenharmony_ci */ 59262306a36Sopenharmony_cistatic long sgx_ioc_enclave_init(struct sgx_encl *encl, void __user *arg) 59362306a36Sopenharmony_ci{ 59462306a36Sopenharmony_ci struct sgx_sigstruct *sigstruct; 59562306a36Sopenharmony_ci struct sgx_enclave_init init_arg; 59662306a36Sopenharmony_ci void *token; 59762306a36Sopenharmony_ci int ret; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci if (!test_bit(SGX_ENCL_CREATED, &encl->flags) || 60062306a36Sopenharmony_ci test_bit(SGX_ENCL_INITIALIZED, &encl->flags)) 60162306a36Sopenharmony_ci return -EINVAL; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci if (copy_from_user(&init_arg, arg, sizeof(init_arg))) 60462306a36Sopenharmony_ci return -EFAULT; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci /* 60762306a36Sopenharmony_ci * 'sigstruct' must be on a page boundary and 'token' on a 512 byte 60862306a36Sopenharmony_ci * boundary. kmalloc() will give this alignment when allocating 60962306a36Sopenharmony_ci * PAGE_SIZE bytes. 61062306a36Sopenharmony_ci */ 61162306a36Sopenharmony_ci sigstruct = kmalloc(PAGE_SIZE, GFP_KERNEL); 61262306a36Sopenharmony_ci if (!sigstruct) 61362306a36Sopenharmony_ci return -ENOMEM; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci token = (void *)((unsigned long)sigstruct + PAGE_SIZE / 2); 61662306a36Sopenharmony_ci memset(token, 0, SGX_LAUNCH_TOKEN_SIZE); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci if (copy_from_user(sigstruct, (void __user *)init_arg.sigstruct, 61962306a36Sopenharmony_ci sizeof(*sigstruct))) { 62062306a36Sopenharmony_ci ret = -EFAULT; 62162306a36Sopenharmony_ci goto out; 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci /* 62562306a36Sopenharmony_ci * A legacy field used with Intel signed enclaves. These used to mean 62662306a36Sopenharmony_ci * regular and architectural enclaves. The CPU only accepts these values 62762306a36Sopenharmony_ci * but they do not have any other meaning. 62862306a36Sopenharmony_ci * 62962306a36Sopenharmony_ci * Thus, reject any other values. 63062306a36Sopenharmony_ci */ 63162306a36Sopenharmony_ci if (sigstruct->header.vendor != 0x0000 && 63262306a36Sopenharmony_ci sigstruct->header.vendor != 0x8086) { 63362306a36Sopenharmony_ci ret = -EINVAL; 63462306a36Sopenharmony_ci goto out; 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci ret = sgx_encl_init(encl, sigstruct, token); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ciout: 64062306a36Sopenharmony_ci kfree(sigstruct); 64162306a36Sopenharmony_ci return ret; 64262306a36Sopenharmony_ci} 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci/** 64562306a36Sopenharmony_ci * sgx_ioc_enclave_provision() - handler for %SGX_IOC_ENCLAVE_PROVISION 64662306a36Sopenharmony_ci * @encl: an enclave pointer 64762306a36Sopenharmony_ci * @arg: userspace pointer to a struct sgx_enclave_provision instance 64862306a36Sopenharmony_ci * 64962306a36Sopenharmony_ci * Allow ATTRIBUTE.PROVISION_KEY for an enclave by providing a file handle to 65062306a36Sopenharmony_ci * /dev/sgx_provision. 65162306a36Sopenharmony_ci * 65262306a36Sopenharmony_ci * Return: 65362306a36Sopenharmony_ci * - 0: Success. 65462306a36Sopenharmony_ci * - -errno: Otherwise. 65562306a36Sopenharmony_ci */ 65662306a36Sopenharmony_cistatic long sgx_ioc_enclave_provision(struct sgx_encl *encl, void __user *arg) 65762306a36Sopenharmony_ci{ 65862306a36Sopenharmony_ci struct sgx_enclave_provision params; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci if (copy_from_user(¶ms, arg, sizeof(params))) 66162306a36Sopenharmony_ci return -EFAULT; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci return sgx_set_attribute(&encl->attributes_mask, params.fd); 66462306a36Sopenharmony_ci} 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci/* 66762306a36Sopenharmony_ci * Ensure enclave is ready for SGX2 functions. Readiness is checked 66862306a36Sopenharmony_ci * by ensuring the hardware supports SGX2 and the enclave is initialized 66962306a36Sopenharmony_ci * and thus able to handle requests to modify pages within it. 67062306a36Sopenharmony_ci */ 67162306a36Sopenharmony_cistatic int sgx_ioc_sgx2_ready(struct sgx_encl *encl) 67262306a36Sopenharmony_ci{ 67362306a36Sopenharmony_ci if (!(cpu_feature_enabled(X86_FEATURE_SGX2))) 67462306a36Sopenharmony_ci return -ENODEV; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci if (!test_bit(SGX_ENCL_INITIALIZED, &encl->flags)) 67762306a36Sopenharmony_ci return -EINVAL; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci return 0; 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci/* 68362306a36Sopenharmony_ci * Some SGX functions require that no cached linear-to-physical address 68462306a36Sopenharmony_ci * mappings are present before they can succeed. Collaborate with 68562306a36Sopenharmony_ci * hardware via ENCLS[ETRACK] to ensure that all cached 68662306a36Sopenharmony_ci * linear-to-physical address mappings belonging to all threads of 68762306a36Sopenharmony_ci * the enclave are cleared. See sgx_encl_cpumask() for details. 68862306a36Sopenharmony_ci * 68962306a36Sopenharmony_ci * Must be called with enclave's mutex held from the time the 69062306a36Sopenharmony_ci * SGX function requiring that no cached linear-to-physical mappings 69162306a36Sopenharmony_ci * are present is executed until this ETRACK flow is complete. 69262306a36Sopenharmony_ci */ 69362306a36Sopenharmony_cistatic int sgx_enclave_etrack(struct sgx_encl *encl) 69462306a36Sopenharmony_ci{ 69562306a36Sopenharmony_ci void *epc_virt; 69662306a36Sopenharmony_ci int ret; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci epc_virt = sgx_get_epc_virt_addr(encl->secs.epc_page); 69962306a36Sopenharmony_ci ret = __etrack(epc_virt); 70062306a36Sopenharmony_ci if (ret) { 70162306a36Sopenharmony_ci /* 70262306a36Sopenharmony_ci * ETRACK only fails when there is an OS issue. For 70362306a36Sopenharmony_ci * example, two consecutive ETRACK was sent without 70462306a36Sopenharmony_ci * completed IPI between. 70562306a36Sopenharmony_ci */ 70662306a36Sopenharmony_ci pr_err_once("ETRACK returned %d (0x%x)", ret, ret); 70762306a36Sopenharmony_ci /* 70862306a36Sopenharmony_ci * Send IPIs to kick CPUs out of the enclave and 70962306a36Sopenharmony_ci * try ETRACK again. 71062306a36Sopenharmony_ci */ 71162306a36Sopenharmony_ci on_each_cpu_mask(sgx_encl_cpumask(encl), sgx_ipi_cb, NULL, 1); 71262306a36Sopenharmony_ci ret = __etrack(epc_virt); 71362306a36Sopenharmony_ci if (ret) { 71462306a36Sopenharmony_ci pr_err_once("ETRACK repeat returned %d (0x%x)", 71562306a36Sopenharmony_ci ret, ret); 71662306a36Sopenharmony_ci return -EFAULT; 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci } 71962306a36Sopenharmony_ci on_each_cpu_mask(sgx_encl_cpumask(encl), sgx_ipi_cb, NULL, 1); 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci return 0; 72262306a36Sopenharmony_ci} 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci/** 72562306a36Sopenharmony_ci * sgx_enclave_restrict_permissions() - Restrict EPCM permissions 72662306a36Sopenharmony_ci * @encl: Enclave to which the pages belong. 72762306a36Sopenharmony_ci * @modp: Checked parameters from user on which pages need modifying and 72862306a36Sopenharmony_ci * their new permissions. 72962306a36Sopenharmony_ci * 73062306a36Sopenharmony_ci * Return: 73162306a36Sopenharmony_ci * - 0: Success. 73262306a36Sopenharmony_ci * - -errno: Otherwise. 73362306a36Sopenharmony_ci */ 73462306a36Sopenharmony_cistatic long 73562306a36Sopenharmony_cisgx_enclave_restrict_permissions(struct sgx_encl *encl, 73662306a36Sopenharmony_ci struct sgx_enclave_restrict_permissions *modp) 73762306a36Sopenharmony_ci{ 73862306a36Sopenharmony_ci struct sgx_encl_page *entry; 73962306a36Sopenharmony_ci struct sgx_secinfo secinfo; 74062306a36Sopenharmony_ci unsigned long addr; 74162306a36Sopenharmony_ci unsigned long c; 74262306a36Sopenharmony_ci void *epc_virt; 74362306a36Sopenharmony_ci int ret; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci memset(&secinfo, 0, sizeof(secinfo)); 74662306a36Sopenharmony_ci secinfo.flags = modp->permissions & SGX_SECINFO_PERMISSION_MASK; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci for (c = 0 ; c < modp->length; c += PAGE_SIZE) { 74962306a36Sopenharmony_ci addr = encl->base + modp->offset + c; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci sgx_reclaim_direct(); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci mutex_lock(&encl->lock); 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci entry = sgx_encl_load_page(encl, addr); 75662306a36Sopenharmony_ci if (IS_ERR(entry)) { 75762306a36Sopenharmony_ci ret = PTR_ERR(entry) == -EBUSY ? -EAGAIN : -EFAULT; 75862306a36Sopenharmony_ci goto out_unlock; 75962306a36Sopenharmony_ci } 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci /* 76262306a36Sopenharmony_ci * Changing EPCM permissions is only supported on regular 76362306a36Sopenharmony_ci * SGX pages. Attempting this change on other pages will 76462306a36Sopenharmony_ci * result in #PF. 76562306a36Sopenharmony_ci */ 76662306a36Sopenharmony_ci if (entry->type != SGX_PAGE_TYPE_REG) { 76762306a36Sopenharmony_ci ret = -EINVAL; 76862306a36Sopenharmony_ci goto out_unlock; 76962306a36Sopenharmony_ci } 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci /* 77262306a36Sopenharmony_ci * Apart from ensuring that read-access remains, do not verify 77362306a36Sopenharmony_ci * the permission bits requested. Kernel has no control over 77462306a36Sopenharmony_ci * how EPCM permissions can be relaxed from within the enclave. 77562306a36Sopenharmony_ci * ENCLS[EMODPR] can only remove existing EPCM permissions, 77662306a36Sopenharmony_ci * attempting to set new permissions will be ignored by the 77762306a36Sopenharmony_ci * hardware. 77862306a36Sopenharmony_ci */ 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci /* Change EPCM permissions. */ 78162306a36Sopenharmony_ci epc_virt = sgx_get_epc_virt_addr(entry->epc_page); 78262306a36Sopenharmony_ci ret = __emodpr(&secinfo, epc_virt); 78362306a36Sopenharmony_ci if (encls_faulted(ret)) { 78462306a36Sopenharmony_ci /* 78562306a36Sopenharmony_ci * All possible faults should be avoidable: 78662306a36Sopenharmony_ci * parameters have been checked, will only change 78762306a36Sopenharmony_ci * permissions of a regular page, and no concurrent 78862306a36Sopenharmony_ci * SGX1/SGX2 ENCLS instructions since these 78962306a36Sopenharmony_ci * are protected with mutex. 79062306a36Sopenharmony_ci */ 79162306a36Sopenharmony_ci pr_err_once("EMODPR encountered exception %d\n", 79262306a36Sopenharmony_ci ENCLS_TRAPNR(ret)); 79362306a36Sopenharmony_ci ret = -EFAULT; 79462306a36Sopenharmony_ci goto out_unlock; 79562306a36Sopenharmony_ci } 79662306a36Sopenharmony_ci if (encls_failed(ret)) { 79762306a36Sopenharmony_ci modp->result = ret; 79862306a36Sopenharmony_ci ret = -EFAULT; 79962306a36Sopenharmony_ci goto out_unlock; 80062306a36Sopenharmony_ci } 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci ret = sgx_enclave_etrack(encl); 80362306a36Sopenharmony_ci if (ret) { 80462306a36Sopenharmony_ci ret = -EFAULT; 80562306a36Sopenharmony_ci goto out_unlock; 80662306a36Sopenharmony_ci } 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci mutex_unlock(&encl->lock); 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci ret = 0; 81262306a36Sopenharmony_ci goto out; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ciout_unlock: 81562306a36Sopenharmony_ci mutex_unlock(&encl->lock); 81662306a36Sopenharmony_ciout: 81762306a36Sopenharmony_ci modp->count = c; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci return ret; 82062306a36Sopenharmony_ci} 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci/** 82362306a36Sopenharmony_ci * sgx_ioc_enclave_restrict_permissions() - handler for 82462306a36Sopenharmony_ci * %SGX_IOC_ENCLAVE_RESTRICT_PERMISSIONS 82562306a36Sopenharmony_ci * @encl: an enclave pointer 82662306a36Sopenharmony_ci * @arg: userspace pointer to a &struct sgx_enclave_restrict_permissions 82762306a36Sopenharmony_ci * instance 82862306a36Sopenharmony_ci * 82962306a36Sopenharmony_ci * SGX2 distinguishes between relaxing and restricting the enclave page 83062306a36Sopenharmony_ci * permissions maintained by the hardware (EPCM permissions) of pages 83162306a36Sopenharmony_ci * belonging to an initialized enclave (after SGX_IOC_ENCLAVE_INIT). 83262306a36Sopenharmony_ci * 83362306a36Sopenharmony_ci * EPCM permissions cannot be restricted from within the enclave, the enclave 83462306a36Sopenharmony_ci * requires the kernel to run the privileged level 0 instructions ENCLS[EMODPR] 83562306a36Sopenharmony_ci * and ENCLS[ETRACK]. An attempt to relax EPCM permissions with this call 83662306a36Sopenharmony_ci * will be ignored by the hardware. 83762306a36Sopenharmony_ci * 83862306a36Sopenharmony_ci * Return: 83962306a36Sopenharmony_ci * - 0: Success 84062306a36Sopenharmony_ci * - -errno: Otherwise 84162306a36Sopenharmony_ci */ 84262306a36Sopenharmony_cistatic long sgx_ioc_enclave_restrict_permissions(struct sgx_encl *encl, 84362306a36Sopenharmony_ci void __user *arg) 84462306a36Sopenharmony_ci{ 84562306a36Sopenharmony_ci struct sgx_enclave_restrict_permissions params; 84662306a36Sopenharmony_ci long ret; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci ret = sgx_ioc_sgx2_ready(encl); 84962306a36Sopenharmony_ci if (ret) 85062306a36Sopenharmony_ci return ret; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci if (copy_from_user(¶ms, arg, sizeof(params))) 85362306a36Sopenharmony_ci return -EFAULT; 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci if (sgx_validate_offset_length(encl, params.offset, params.length)) 85662306a36Sopenharmony_ci return -EINVAL; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci if (params.permissions & ~SGX_SECINFO_PERMISSION_MASK) 85962306a36Sopenharmony_ci return -EINVAL; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci /* 86262306a36Sopenharmony_ci * Fail early if invalid permissions requested to prevent ENCLS[EMODPR] 86362306a36Sopenharmony_ci * from faulting later when the CPU does the same check. 86462306a36Sopenharmony_ci */ 86562306a36Sopenharmony_ci if ((params.permissions & SGX_SECINFO_W) && 86662306a36Sopenharmony_ci !(params.permissions & SGX_SECINFO_R)) 86762306a36Sopenharmony_ci return -EINVAL; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci if (params.result || params.count) 87062306a36Sopenharmony_ci return -EINVAL; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci ret = sgx_enclave_restrict_permissions(encl, ¶ms); 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci if (copy_to_user(arg, ¶ms, sizeof(params))) 87562306a36Sopenharmony_ci return -EFAULT; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci return ret; 87862306a36Sopenharmony_ci} 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci/** 88162306a36Sopenharmony_ci * sgx_enclave_modify_types() - Modify type of SGX enclave pages 88262306a36Sopenharmony_ci * @encl: Enclave to which the pages belong. 88362306a36Sopenharmony_ci * @modt: Checked parameters from user about which pages need modifying 88462306a36Sopenharmony_ci * and their new page type. 88562306a36Sopenharmony_ci * 88662306a36Sopenharmony_ci * Return: 88762306a36Sopenharmony_ci * - 0: Success 88862306a36Sopenharmony_ci * - -errno: Otherwise 88962306a36Sopenharmony_ci */ 89062306a36Sopenharmony_cistatic long sgx_enclave_modify_types(struct sgx_encl *encl, 89162306a36Sopenharmony_ci struct sgx_enclave_modify_types *modt) 89262306a36Sopenharmony_ci{ 89362306a36Sopenharmony_ci unsigned long max_prot_restore; 89462306a36Sopenharmony_ci enum sgx_page_type page_type; 89562306a36Sopenharmony_ci struct sgx_encl_page *entry; 89662306a36Sopenharmony_ci struct sgx_secinfo secinfo; 89762306a36Sopenharmony_ci unsigned long prot; 89862306a36Sopenharmony_ci unsigned long addr; 89962306a36Sopenharmony_ci unsigned long c; 90062306a36Sopenharmony_ci void *epc_virt; 90162306a36Sopenharmony_ci int ret; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci page_type = modt->page_type & SGX_PAGE_TYPE_MASK; 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci /* 90662306a36Sopenharmony_ci * The only new page types allowed by hardware are PT_TCS and PT_TRIM. 90762306a36Sopenharmony_ci */ 90862306a36Sopenharmony_ci if (page_type != SGX_PAGE_TYPE_TCS && page_type != SGX_PAGE_TYPE_TRIM) 90962306a36Sopenharmony_ci return -EINVAL; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci memset(&secinfo, 0, sizeof(secinfo)); 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci secinfo.flags = page_type << 8; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci for (c = 0 ; c < modt->length; c += PAGE_SIZE) { 91662306a36Sopenharmony_ci addr = encl->base + modt->offset + c; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci sgx_reclaim_direct(); 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci mutex_lock(&encl->lock); 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci entry = sgx_encl_load_page(encl, addr); 92362306a36Sopenharmony_ci if (IS_ERR(entry)) { 92462306a36Sopenharmony_ci ret = PTR_ERR(entry) == -EBUSY ? -EAGAIN : -EFAULT; 92562306a36Sopenharmony_ci goto out_unlock; 92662306a36Sopenharmony_ci } 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci /* 92962306a36Sopenharmony_ci * Borrow the logic from the Intel SDM. Regular pages 93062306a36Sopenharmony_ci * (SGX_PAGE_TYPE_REG) can change type to SGX_PAGE_TYPE_TCS 93162306a36Sopenharmony_ci * or SGX_PAGE_TYPE_TRIM but TCS pages can only be trimmed. 93262306a36Sopenharmony_ci * CET pages not supported yet. 93362306a36Sopenharmony_ci */ 93462306a36Sopenharmony_ci if (!(entry->type == SGX_PAGE_TYPE_REG || 93562306a36Sopenharmony_ci (entry->type == SGX_PAGE_TYPE_TCS && 93662306a36Sopenharmony_ci page_type == SGX_PAGE_TYPE_TRIM))) { 93762306a36Sopenharmony_ci ret = -EINVAL; 93862306a36Sopenharmony_ci goto out_unlock; 93962306a36Sopenharmony_ci } 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci max_prot_restore = entry->vm_max_prot_bits; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci /* 94462306a36Sopenharmony_ci * Once a regular page becomes a TCS page it cannot be 94562306a36Sopenharmony_ci * changed back. So the maximum allowed protection reflects 94662306a36Sopenharmony_ci * the TCS page that is always RW from kernel perspective but 94762306a36Sopenharmony_ci * will be inaccessible from within enclave. Before doing 94862306a36Sopenharmony_ci * so, do make sure that the new page type continues to 94962306a36Sopenharmony_ci * respect the originally vetted page permissions. 95062306a36Sopenharmony_ci */ 95162306a36Sopenharmony_ci if (entry->type == SGX_PAGE_TYPE_REG && 95262306a36Sopenharmony_ci page_type == SGX_PAGE_TYPE_TCS) { 95362306a36Sopenharmony_ci if (~entry->vm_max_prot_bits & (VM_READ | VM_WRITE)) { 95462306a36Sopenharmony_ci ret = -EPERM; 95562306a36Sopenharmony_ci goto out_unlock; 95662306a36Sopenharmony_ci } 95762306a36Sopenharmony_ci prot = PROT_READ | PROT_WRITE; 95862306a36Sopenharmony_ci entry->vm_max_prot_bits = calc_vm_prot_bits(prot, 0); 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci /* 96162306a36Sopenharmony_ci * Prevent page from being reclaimed while mutex 96262306a36Sopenharmony_ci * is released. 96362306a36Sopenharmony_ci */ 96462306a36Sopenharmony_ci if (sgx_unmark_page_reclaimable(entry->epc_page)) { 96562306a36Sopenharmony_ci ret = -EAGAIN; 96662306a36Sopenharmony_ci goto out_entry_changed; 96762306a36Sopenharmony_ci } 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci /* 97062306a36Sopenharmony_ci * Do not keep encl->lock because of dependency on 97162306a36Sopenharmony_ci * mmap_lock acquired in sgx_zap_enclave_ptes(). 97262306a36Sopenharmony_ci */ 97362306a36Sopenharmony_ci mutex_unlock(&encl->lock); 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci sgx_zap_enclave_ptes(encl, addr); 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci mutex_lock(&encl->lock); 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci sgx_mark_page_reclaimable(entry->epc_page); 98062306a36Sopenharmony_ci } 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci /* Change EPC type */ 98362306a36Sopenharmony_ci epc_virt = sgx_get_epc_virt_addr(entry->epc_page); 98462306a36Sopenharmony_ci ret = __emodt(&secinfo, epc_virt); 98562306a36Sopenharmony_ci if (encls_faulted(ret)) { 98662306a36Sopenharmony_ci /* 98762306a36Sopenharmony_ci * All possible faults should be avoidable: 98862306a36Sopenharmony_ci * parameters have been checked, will only change 98962306a36Sopenharmony_ci * valid page types, and no concurrent 99062306a36Sopenharmony_ci * SGX1/SGX2 ENCLS instructions since these are 99162306a36Sopenharmony_ci * protected with mutex. 99262306a36Sopenharmony_ci */ 99362306a36Sopenharmony_ci pr_err_once("EMODT encountered exception %d\n", 99462306a36Sopenharmony_ci ENCLS_TRAPNR(ret)); 99562306a36Sopenharmony_ci ret = -EFAULT; 99662306a36Sopenharmony_ci goto out_entry_changed; 99762306a36Sopenharmony_ci } 99862306a36Sopenharmony_ci if (encls_failed(ret)) { 99962306a36Sopenharmony_ci modt->result = ret; 100062306a36Sopenharmony_ci ret = -EFAULT; 100162306a36Sopenharmony_ci goto out_entry_changed; 100262306a36Sopenharmony_ci } 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci ret = sgx_enclave_etrack(encl); 100562306a36Sopenharmony_ci if (ret) { 100662306a36Sopenharmony_ci ret = -EFAULT; 100762306a36Sopenharmony_ci goto out_unlock; 100862306a36Sopenharmony_ci } 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci entry->type = page_type; 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci mutex_unlock(&encl->lock); 101362306a36Sopenharmony_ci } 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci ret = 0; 101662306a36Sopenharmony_ci goto out; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ciout_entry_changed: 101962306a36Sopenharmony_ci entry->vm_max_prot_bits = max_prot_restore; 102062306a36Sopenharmony_ciout_unlock: 102162306a36Sopenharmony_ci mutex_unlock(&encl->lock); 102262306a36Sopenharmony_ciout: 102362306a36Sopenharmony_ci modt->count = c; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci return ret; 102662306a36Sopenharmony_ci} 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci/** 102962306a36Sopenharmony_ci * sgx_ioc_enclave_modify_types() - handler for %SGX_IOC_ENCLAVE_MODIFY_TYPES 103062306a36Sopenharmony_ci * @encl: an enclave pointer 103162306a36Sopenharmony_ci * @arg: userspace pointer to a &struct sgx_enclave_modify_types instance 103262306a36Sopenharmony_ci * 103362306a36Sopenharmony_ci * Ability to change the enclave page type supports the following use cases: 103462306a36Sopenharmony_ci * 103562306a36Sopenharmony_ci * * It is possible to add TCS pages to an enclave by changing the type of 103662306a36Sopenharmony_ci * regular pages (%SGX_PAGE_TYPE_REG) to TCS (%SGX_PAGE_TYPE_TCS) pages. 103762306a36Sopenharmony_ci * With this support the number of threads supported by an initialized 103862306a36Sopenharmony_ci * enclave can be increased dynamically. 103962306a36Sopenharmony_ci * 104062306a36Sopenharmony_ci * * Regular or TCS pages can dynamically be removed from an initialized 104162306a36Sopenharmony_ci * enclave by changing the page type to %SGX_PAGE_TYPE_TRIM. Changing the 104262306a36Sopenharmony_ci * page type to %SGX_PAGE_TYPE_TRIM marks the page for removal with actual 104362306a36Sopenharmony_ci * removal done by handler of %SGX_IOC_ENCLAVE_REMOVE_PAGES ioctl() called 104462306a36Sopenharmony_ci * after ENCLU[EACCEPT] is run on %SGX_PAGE_TYPE_TRIM page from within the 104562306a36Sopenharmony_ci * enclave. 104662306a36Sopenharmony_ci * 104762306a36Sopenharmony_ci * Return: 104862306a36Sopenharmony_ci * - 0: Success 104962306a36Sopenharmony_ci * - -errno: Otherwise 105062306a36Sopenharmony_ci */ 105162306a36Sopenharmony_cistatic long sgx_ioc_enclave_modify_types(struct sgx_encl *encl, 105262306a36Sopenharmony_ci void __user *arg) 105362306a36Sopenharmony_ci{ 105462306a36Sopenharmony_ci struct sgx_enclave_modify_types params; 105562306a36Sopenharmony_ci long ret; 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci ret = sgx_ioc_sgx2_ready(encl); 105862306a36Sopenharmony_ci if (ret) 105962306a36Sopenharmony_ci return ret; 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci if (copy_from_user(¶ms, arg, sizeof(params))) 106262306a36Sopenharmony_ci return -EFAULT; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci if (sgx_validate_offset_length(encl, params.offset, params.length)) 106562306a36Sopenharmony_ci return -EINVAL; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci if (params.page_type & ~SGX_PAGE_TYPE_MASK) 106862306a36Sopenharmony_ci return -EINVAL; 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci if (params.result || params.count) 107162306a36Sopenharmony_ci return -EINVAL; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci ret = sgx_enclave_modify_types(encl, ¶ms); 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci if (copy_to_user(arg, ¶ms, sizeof(params))) 107662306a36Sopenharmony_ci return -EFAULT; 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci return ret; 107962306a36Sopenharmony_ci} 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci/** 108262306a36Sopenharmony_ci * sgx_encl_remove_pages() - Remove trimmed pages from SGX enclave 108362306a36Sopenharmony_ci * @encl: Enclave to which the pages belong 108462306a36Sopenharmony_ci * @params: Checked parameters from user on which pages need to be removed 108562306a36Sopenharmony_ci * 108662306a36Sopenharmony_ci * Return: 108762306a36Sopenharmony_ci * - 0: Success. 108862306a36Sopenharmony_ci * - -errno: Otherwise. 108962306a36Sopenharmony_ci */ 109062306a36Sopenharmony_cistatic long sgx_encl_remove_pages(struct sgx_encl *encl, 109162306a36Sopenharmony_ci struct sgx_enclave_remove_pages *params) 109262306a36Sopenharmony_ci{ 109362306a36Sopenharmony_ci struct sgx_encl_page *entry; 109462306a36Sopenharmony_ci struct sgx_secinfo secinfo; 109562306a36Sopenharmony_ci unsigned long addr; 109662306a36Sopenharmony_ci unsigned long c; 109762306a36Sopenharmony_ci void *epc_virt; 109862306a36Sopenharmony_ci int ret; 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci memset(&secinfo, 0, sizeof(secinfo)); 110162306a36Sopenharmony_ci secinfo.flags = SGX_SECINFO_R | SGX_SECINFO_W | SGX_SECINFO_X; 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci for (c = 0 ; c < params->length; c += PAGE_SIZE) { 110462306a36Sopenharmony_ci addr = encl->base + params->offset + c; 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci sgx_reclaim_direct(); 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci mutex_lock(&encl->lock); 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci entry = sgx_encl_load_page(encl, addr); 111162306a36Sopenharmony_ci if (IS_ERR(entry)) { 111262306a36Sopenharmony_ci ret = PTR_ERR(entry) == -EBUSY ? -EAGAIN : -EFAULT; 111362306a36Sopenharmony_ci goto out_unlock; 111462306a36Sopenharmony_ci } 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci if (entry->type != SGX_PAGE_TYPE_TRIM) { 111762306a36Sopenharmony_ci ret = -EPERM; 111862306a36Sopenharmony_ci goto out_unlock; 111962306a36Sopenharmony_ci } 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci /* 112262306a36Sopenharmony_ci * ENCLS[EMODPR] is a no-op instruction used to inform if 112362306a36Sopenharmony_ci * ENCLU[EACCEPT] was run from within the enclave. If 112462306a36Sopenharmony_ci * ENCLS[EMODPR] is run with RWX on a trimmed page that is 112562306a36Sopenharmony_ci * not yet accepted then it will return 112662306a36Sopenharmony_ci * %SGX_PAGE_NOT_MODIFIABLE, after the trimmed page is 112762306a36Sopenharmony_ci * accepted the instruction will encounter a page fault. 112862306a36Sopenharmony_ci */ 112962306a36Sopenharmony_ci epc_virt = sgx_get_epc_virt_addr(entry->epc_page); 113062306a36Sopenharmony_ci ret = __emodpr(&secinfo, epc_virt); 113162306a36Sopenharmony_ci if (!encls_faulted(ret) || ENCLS_TRAPNR(ret) != X86_TRAP_PF) { 113262306a36Sopenharmony_ci ret = -EPERM; 113362306a36Sopenharmony_ci goto out_unlock; 113462306a36Sopenharmony_ci } 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci if (sgx_unmark_page_reclaimable(entry->epc_page)) { 113762306a36Sopenharmony_ci ret = -EBUSY; 113862306a36Sopenharmony_ci goto out_unlock; 113962306a36Sopenharmony_ci } 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci /* 114262306a36Sopenharmony_ci * Do not keep encl->lock because of dependency on 114362306a36Sopenharmony_ci * mmap_lock acquired in sgx_zap_enclave_ptes(). 114462306a36Sopenharmony_ci */ 114562306a36Sopenharmony_ci mutex_unlock(&encl->lock); 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci sgx_zap_enclave_ptes(encl, addr); 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci mutex_lock(&encl->lock); 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci sgx_encl_free_epc_page(entry->epc_page); 115262306a36Sopenharmony_ci encl->secs_child_cnt--; 115362306a36Sopenharmony_ci entry->epc_page = NULL; 115462306a36Sopenharmony_ci xa_erase(&encl->page_array, PFN_DOWN(entry->desc)); 115562306a36Sopenharmony_ci sgx_encl_shrink(encl, NULL); 115662306a36Sopenharmony_ci kfree(entry); 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci mutex_unlock(&encl->lock); 115962306a36Sopenharmony_ci } 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci ret = 0; 116262306a36Sopenharmony_ci goto out; 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ciout_unlock: 116562306a36Sopenharmony_ci mutex_unlock(&encl->lock); 116662306a36Sopenharmony_ciout: 116762306a36Sopenharmony_ci params->count = c; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci return ret; 117062306a36Sopenharmony_ci} 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci/** 117362306a36Sopenharmony_ci * sgx_ioc_enclave_remove_pages() - handler for %SGX_IOC_ENCLAVE_REMOVE_PAGES 117462306a36Sopenharmony_ci * @encl: an enclave pointer 117562306a36Sopenharmony_ci * @arg: userspace pointer to &struct sgx_enclave_remove_pages instance 117662306a36Sopenharmony_ci * 117762306a36Sopenharmony_ci * Final step of the flow removing pages from an initialized enclave. The 117862306a36Sopenharmony_ci * complete flow is: 117962306a36Sopenharmony_ci * 118062306a36Sopenharmony_ci * 1) User changes the type of the pages to be removed to %SGX_PAGE_TYPE_TRIM 118162306a36Sopenharmony_ci * using the %SGX_IOC_ENCLAVE_MODIFY_TYPES ioctl(). 118262306a36Sopenharmony_ci * 2) User approves the page removal by running ENCLU[EACCEPT] from within 118362306a36Sopenharmony_ci * the enclave. 118462306a36Sopenharmony_ci * 3) User initiates actual page removal using the 118562306a36Sopenharmony_ci * %SGX_IOC_ENCLAVE_REMOVE_PAGES ioctl() that is handled here. 118662306a36Sopenharmony_ci * 118762306a36Sopenharmony_ci * First remove any page table entries pointing to the page and then proceed 118862306a36Sopenharmony_ci * with the actual removal of the enclave page and data in support of it. 118962306a36Sopenharmony_ci * 119062306a36Sopenharmony_ci * VA pages are not affected by this removal. It is thus possible that the 119162306a36Sopenharmony_ci * enclave may end up with more VA pages than needed to support all its 119262306a36Sopenharmony_ci * pages. 119362306a36Sopenharmony_ci * 119462306a36Sopenharmony_ci * Return: 119562306a36Sopenharmony_ci * - 0: Success 119662306a36Sopenharmony_ci * - -errno: Otherwise 119762306a36Sopenharmony_ci */ 119862306a36Sopenharmony_cistatic long sgx_ioc_enclave_remove_pages(struct sgx_encl *encl, 119962306a36Sopenharmony_ci void __user *arg) 120062306a36Sopenharmony_ci{ 120162306a36Sopenharmony_ci struct sgx_enclave_remove_pages params; 120262306a36Sopenharmony_ci long ret; 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci ret = sgx_ioc_sgx2_ready(encl); 120562306a36Sopenharmony_ci if (ret) 120662306a36Sopenharmony_ci return ret; 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci if (copy_from_user(¶ms, arg, sizeof(params))) 120962306a36Sopenharmony_ci return -EFAULT; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci if (sgx_validate_offset_length(encl, params.offset, params.length)) 121262306a36Sopenharmony_ci return -EINVAL; 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci if (params.count) 121562306a36Sopenharmony_ci return -EINVAL; 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci ret = sgx_encl_remove_pages(encl, ¶ms); 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci if (copy_to_user(arg, ¶ms, sizeof(params))) 122062306a36Sopenharmony_ci return -EFAULT; 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci return ret; 122362306a36Sopenharmony_ci} 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_cilong sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) 122662306a36Sopenharmony_ci{ 122762306a36Sopenharmony_ci struct sgx_encl *encl = filep->private_data; 122862306a36Sopenharmony_ci int ret; 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci if (test_and_set_bit(SGX_ENCL_IOCTL, &encl->flags)) 123162306a36Sopenharmony_ci return -EBUSY; 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci switch (cmd) { 123462306a36Sopenharmony_ci case SGX_IOC_ENCLAVE_CREATE: 123562306a36Sopenharmony_ci ret = sgx_ioc_enclave_create(encl, (void __user *)arg); 123662306a36Sopenharmony_ci break; 123762306a36Sopenharmony_ci case SGX_IOC_ENCLAVE_ADD_PAGES: 123862306a36Sopenharmony_ci ret = sgx_ioc_enclave_add_pages(encl, (void __user *)arg); 123962306a36Sopenharmony_ci break; 124062306a36Sopenharmony_ci case SGX_IOC_ENCLAVE_INIT: 124162306a36Sopenharmony_ci ret = sgx_ioc_enclave_init(encl, (void __user *)arg); 124262306a36Sopenharmony_ci break; 124362306a36Sopenharmony_ci case SGX_IOC_ENCLAVE_PROVISION: 124462306a36Sopenharmony_ci ret = sgx_ioc_enclave_provision(encl, (void __user *)arg); 124562306a36Sopenharmony_ci break; 124662306a36Sopenharmony_ci case SGX_IOC_ENCLAVE_RESTRICT_PERMISSIONS: 124762306a36Sopenharmony_ci ret = sgx_ioc_enclave_restrict_permissions(encl, 124862306a36Sopenharmony_ci (void __user *)arg); 124962306a36Sopenharmony_ci break; 125062306a36Sopenharmony_ci case SGX_IOC_ENCLAVE_MODIFY_TYPES: 125162306a36Sopenharmony_ci ret = sgx_ioc_enclave_modify_types(encl, (void __user *)arg); 125262306a36Sopenharmony_ci break; 125362306a36Sopenharmony_ci case SGX_IOC_ENCLAVE_REMOVE_PAGES: 125462306a36Sopenharmony_ci ret = sgx_ioc_enclave_remove_pages(encl, (void __user *)arg); 125562306a36Sopenharmony_ci break; 125662306a36Sopenharmony_ci default: 125762306a36Sopenharmony_ci ret = -ENOIOCTLCMD; 125862306a36Sopenharmony_ci break; 125962306a36Sopenharmony_ci } 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci clear_bit(SGX_ENCL_IOCTL, &encl->flags); 126262306a36Sopenharmony_ci return ret; 126362306a36Sopenharmony_ci} 1264