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(&params, 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(&params, 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, &params);
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	if (copy_to_user(arg, &params, 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(&params, 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, &params);
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	if (copy_to_user(arg, &params, 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(&params, 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, &params);
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci	if (copy_to_user(arg, &params, 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