xref: /openbmc/linux/arch/x86/kernel/cpu/sgx/ioctl.c (revision 8ebc80a25f9d9bf7a8e368b266d5b740c485c362)
1888d2491SJarkko Sakkinen // SPDX-License-Identifier: GPL-2.0
2888d2491SJarkko Sakkinen /*  Copyright(c) 2016-20 Intel Corporation. */
3888d2491SJarkko Sakkinen 
4888d2491SJarkko Sakkinen #include <asm/mman.h>
5b3754e5dSSean Christopherson #include <asm/sgx.h>
6888d2491SJarkko Sakkinen #include <linux/mman.h>
7888d2491SJarkko Sakkinen #include <linux/delay.h>
8888d2491SJarkko Sakkinen #include <linux/file.h>
9888d2491SJarkko Sakkinen #include <linux/hashtable.h>
10888d2491SJarkko Sakkinen #include <linux/highmem.h>
11888d2491SJarkko Sakkinen #include <linux/ratelimit.h>
12888d2491SJarkko Sakkinen #include <linux/sched/signal.h>
13888d2491SJarkko Sakkinen #include <linux/shmem_fs.h>
14888d2491SJarkko Sakkinen #include <linux/slab.h>
15888d2491SJarkko Sakkinen #include <linux/suspend.h>
16888d2491SJarkko Sakkinen #include "driver.h"
17888d2491SJarkko Sakkinen #include "encl.h"
18888d2491SJarkko Sakkinen #include "encls.h"
19888d2491SJarkko Sakkinen 
sgx_encl_grow(struct sgx_encl * encl,bool reclaim)20a76e7f1fSReinette Chatre struct sgx_va_page *sgx_encl_grow(struct sgx_encl *encl, bool reclaim)
211728ab54SJarkko Sakkinen {
221728ab54SJarkko Sakkinen 	struct sgx_va_page *va_page = NULL;
231728ab54SJarkko Sakkinen 	void *err;
241728ab54SJarkko Sakkinen 
251728ab54SJarkko Sakkinen 	BUILD_BUG_ON(SGX_VA_SLOT_COUNT !=
261728ab54SJarkko Sakkinen 		(SGX_ENCL_PAGE_VA_OFFSET_MASK >> 3) + 1);
271728ab54SJarkko Sakkinen 
281728ab54SJarkko Sakkinen 	if (!(encl->page_cnt % SGX_VA_SLOT_COUNT)) {
291728ab54SJarkko Sakkinen 		va_page = kzalloc(sizeof(*va_page), GFP_KERNEL);
301728ab54SJarkko Sakkinen 		if (!va_page)
311728ab54SJarkko Sakkinen 			return ERR_PTR(-ENOMEM);
321728ab54SJarkko Sakkinen 
33a76e7f1fSReinette Chatre 		va_page->epc_page = sgx_alloc_va_page(reclaim);
341728ab54SJarkko Sakkinen 		if (IS_ERR(va_page->epc_page)) {
351728ab54SJarkko Sakkinen 			err = ERR_CAST(va_page->epc_page);
361728ab54SJarkko Sakkinen 			kfree(va_page);
371728ab54SJarkko Sakkinen 			return err;
381728ab54SJarkko Sakkinen 		}
391728ab54SJarkko Sakkinen 
401728ab54SJarkko Sakkinen 		WARN_ON_ONCE(encl->page_cnt % SGX_VA_SLOT_COUNT);
411728ab54SJarkko Sakkinen 	}
421728ab54SJarkko Sakkinen 	encl->page_cnt++;
431728ab54SJarkko Sakkinen 	return va_page;
441728ab54SJarkko Sakkinen }
451728ab54SJarkko Sakkinen 
sgx_encl_shrink(struct sgx_encl * encl,struct sgx_va_page * va_page)463a535141SReinette Chatre void sgx_encl_shrink(struct sgx_encl *encl, struct sgx_va_page *va_page)
471728ab54SJarkko Sakkinen {
481728ab54SJarkko Sakkinen 	encl->page_cnt--;
491728ab54SJarkko Sakkinen 
501728ab54SJarkko Sakkinen 	if (va_page) {
51b0c7459bSKai Huang 		sgx_encl_free_epc_page(va_page->epc_page);
521728ab54SJarkko Sakkinen 		list_del(&va_page->list);
531728ab54SJarkko Sakkinen 		kfree(va_page);
541728ab54SJarkko Sakkinen 	}
551728ab54SJarkko Sakkinen }
561728ab54SJarkko Sakkinen 
sgx_encl_create(struct sgx_encl * encl,struct sgx_secs * secs)57888d2491SJarkko Sakkinen static int sgx_encl_create(struct sgx_encl *encl, struct sgx_secs *secs)
58888d2491SJarkko Sakkinen {
59888d2491SJarkko Sakkinen 	struct sgx_epc_page *secs_epc;
601728ab54SJarkko Sakkinen 	struct sgx_va_page *va_page;
61888d2491SJarkko Sakkinen 	struct sgx_pageinfo pginfo;
62888d2491SJarkko Sakkinen 	struct sgx_secinfo secinfo;
63888d2491SJarkko Sakkinen 	unsigned long encl_size;
641728ab54SJarkko Sakkinen 	struct file *backing;
65888d2491SJarkko Sakkinen 	long ret;
66888d2491SJarkko Sakkinen 
67*47053726SJarkko Sakkinen 	/*
68*47053726SJarkko Sakkinen 	 * ECREATE would detect this too, but checking here also ensures
69*47053726SJarkko Sakkinen 	 * that the 'encl_size' calculations below can never overflow.
70*47053726SJarkko Sakkinen 	 */
71*47053726SJarkko Sakkinen 	if (!is_power_of_2(secs->size))
72*47053726SJarkko Sakkinen 		return -EINVAL;
73*47053726SJarkko Sakkinen 
74a76e7f1fSReinette Chatre 	va_page = sgx_encl_grow(encl, true);
751728ab54SJarkko Sakkinen 	if (IS_ERR(va_page))
761728ab54SJarkko Sakkinen 		return PTR_ERR(va_page);
771728ab54SJarkko Sakkinen 	else if (va_page)
781728ab54SJarkko Sakkinen 		list_add(&va_page->list, &encl->va_pages);
791728ab54SJarkko Sakkinen 	/* else the tail page of the VA page list had free slots. */
801728ab54SJarkko Sakkinen 
81888d2491SJarkko Sakkinen 	/* The extra page goes to SECS. */
82888d2491SJarkko Sakkinen 	encl_size = secs->size + PAGE_SIZE;
83888d2491SJarkko Sakkinen 
841728ab54SJarkko Sakkinen 	backing = shmem_file_setup("SGX backing", encl_size + (encl_size >> 5),
851728ab54SJarkko Sakkinen 				   VM_NORESERVE);
861728ab54SJarkko Sakkinen 	if (IS_ERR(backing)) {
871728ab54SJarkko Sakkinen 		ret = PTR_ERR(backing);
881728ab54SJarkko Sakkinen 		goto err_out_shrink;
891728ab54SJarkko Sakkinen 	}
901728ab54SJarkko Sakkinen 
911728ab54SJarkko Sakkinen 	encl->backing = backing;
921728ab54SJarkko Sakkinen 
931728ab54SJarkko Sakkinen 	secs_epc = sgx_alloc_epc_page(&encl->secs, true);
941728ab54SJarkko Sakkinen 	if (IS_ERR(secs_epc)) {
951728ab54SJarkko Sakkinen 		ret = PTR_ERR(secs_epc);
961728ab54SJarkko Sakkinen 		goto err_out_backing;
971728ab54SJarkko Sakkinen 	}
98888d2491SJarkko Sakkinen 
99888d2491SJarkko Sakkinen 	encl->secs.epc_page = secs_epc;
100888d2491SJarkko Sakkinen 
101888d2491SJarkko Sakkinen 	pginfo.addr = 0;
102888d2491SJarkko Sakkinen 	pginfo.contents = (unsigned long)secs;
103888d2491SJarkko Sakkinen 	pginfo.metadata = (unsigned long)&secinfo;
104888d2491SJarkko Sakkinen 	pginfo.secs = 0;
105888d2491SJarkko Sakkinen 	memset(&secinfo, 0, sizeof(secinfo));
106888d2491SJarkko Sakkinen 
107888d2491SJarkko Sakkinen 	ret = __ecreate((void *)&pginfo, sgx_get_epc_virt_addr(secs_epc));
108888d2491SJarkko Sakkinen 	if (ret) {
109888d2491SJarkko Sakkinen 		ret = -EIO;
110888d2491SJarkko Sakkinen 		goto err_out;
111888d2491SJarkko Sakkinen 	}
112888d2491SJarkko Sakkinen 
113888d2491SJarkko Sakkinen 	if (secs->attributes & SGX_ATTR_DEBUG)
114888d2491SJarkko Sakkinen 		set_bit(SGX_ENCL_DEBUG, &encl->flags);
115888d2491SJarkko Sakkinen 
116888d2491SJarkko Sakkinen 	encl->secs.encl = encl;
1178cb7b502SReinette Chatre 	encl->secs.type = SGX_PAGE_TYPE_SECS;
118888d2491SJarkko Sakkinen 	encl->base = secs->base;
119888d2491SJarkko Sakkinen 	encl->size = secs->size;
1209d0c151bSJarkko Sakkinen 	encl->attributes = secs->attributes;
121370839c2SDave Hansen 	encl->attributes_mask = SGX_ATTR_UNPRIV_MASK;
122888d2491SJarkko Sakkinen 
123888d2491SJarkko Sakkinen 	/* Set only after completion, as encl->lock has not been taken. */
124888d2491SJarkko Sakkinen 	set_bit(SGX_ENCL_CREATED, &encl->flags);
125888d2491SJarkko Sakkinen 
126888d2491SJarkko Sakkinen 	return 0;
127888d2491SJarkko Sakkinen 
128888d2491SJarkko Sakkinen err_out:
129b0c7459bSKai Huang 	sgx_encl_free_epc_page(encl->secs.epc_page);
130888d2491SJarkko Sakkinen 	encl->secs.epc_page = NULL;
131888d2491SJarkko Sakkinen 
1321728ab54SJarkko Sakkinen err_out_backing:
1331728ab54SJarkko Sakkinen 	fput(encl->backing);
1341728ab54SJarkko Sakkinen 	encl->backing = NULL;
1351728ab54SJarkko Sakkinen 
1361728ab54SJarkko Sakkinen err_out_shrink:
1371728ab54SJarkko Sakkinen 	sgx_encl_shrink(encl, va_page);
1381728ab54SJarkko Sakkinen 
139888d2491SJarkko Sakkinen 	return ret;
140888d2491SJarkko Sakkinen }
141888d2491SJarkko Sakkinen 
142888d2491SJarkko Sakkinen /**
143888d2491SJarkko Sakkinen  * sgx_ioc_enclave_create() - handler for %SGX_IOC_ENCLAVE_CREATE
144888d2491SJarkko Sakkinen  * @encl:	An enclave pointer.
145888d2491SJarkko Sakkinen  * @arg:	The ioctl argument.
146888d2491SJarkko Sakkinen  *
147888d2491SJarkko Sakkinen  * Allocate kernel data structures for the enclave and invoke ECREATE.
148888d2491SJarkko Sakkinen  *
149888d2491SJarkko Sakkinen  * Return:
150888d2491SJarkko Sakkinen  * - 0:		Success.
151888d2491SJarkko Sakkinen  * - -EIO:	ECREATE failed.
152888d2491SJarkko Sakkinen  * - -errno:	POSIX error.
153888d2491SJarkko Sakkinen  */
sgx_ioc_enclave_create(struct sgx_encl * encl,void __user * arg)154888d2491SJarkko Sakkinen static long sgx_ioc_enclave_create(struct sgx_encl *encl, void __user *arg)
155888d2491SJarkko Sakkinen {
156888d2491SJarkko Sakkinen 	struct sgx_enclave_create create_arg;
157888d2491SJarkko Sakkinen 	void *secs;
158888d2491SJarkko Sakkinen 	int ret;
159888d2491SJarkko Sakkinen 
160888d2491SJarkko Sakkinen 	if (test_bit(SGX_ENCL_CREATED, &encl->flags))
161888d2491SJarkko Sakkinen 		return -EINVAL;
162888d2491SJarkko Sakkinen 
163888d2491SJarkko Sakkinen 	if (copy_from_user(&create_arg, arg, sizeof(create_arg)))
164888d2491SJarkko Sakkinen 		return -EFAULT;
165888d2491SJarkko Sakkinen 
166888d2491SJarkko Sakkinen 	secs = kmalloc(PAGE_SIZE, GFP_KERNEL);
167888d2491SJarkko Sakkinen 	if (!secs)
168888d2491SJarkko Sakkinen 		return -ENOMEM;
169888d2491SJarkko Sakkinen 
170888d2491SJarkko Sakkinen 	if (copy_from_user(secs, (void __user *)create_arg.src, PAGE_SIZE))
171888d2491SJarkko Sakkinen 		ret = -EFAULT;
172888d2491SJarkko Sakkinen 	else
173888d2491SJarkko Sakkinen 		ret = sgx_encl_create(encl, secs);
174888d2491SJarkko Sakkinen 
175888d2491SJarkko Sakkinen 	kfree(secs);
176888d2491SJarkko Sakkinen 	return ret;
177888d2491SJarkko Sakkinen }
178888d2491SJarkko Sakkinen 
sgx_validate_secinfo(struct sgx_secinfo * secinfo)179c6d26d37SJarkko Sakkinen static int sgx_validate_secinfo(struct sgx_secinfo *secinfo)
180c6d26d37SJarkko Sakkinen {
181c6d26d37SJarkko Sakkinen 	u64 perm = secinfo->flags & SGX_SECINFO_PERMISSION_MASK;
182c6d26d37SJarkko Sakkinen 	u64 pt   = secinfo->flags & SGX_SECINFO_PAGE_TYPE_MASK;
183c6d26d37SJarkko Sakkinen 
184c6d26d37SJarkko Sakkinen 	if (pt != SGX_SECINFO_REG && pt != SGX_SECINFO_TCS)
185c6d26d37SJarkko Sakkinen 		return -EINVAL;
186c6d26d37SJarkko Sakkinen 
187c6d26d37SJarkko Sakkinen 	if ((perm & SGX_SECINFO_W) && !(perm & SGX_SECINFO_R))
188c6d26d37SJarkko Sakkinen 		return -EINVAL;
189c6d26d37SJarkko Sakkinen 
190c6d26d37SJarkko Sakkinen 	/*
191c6d26d37SJarkko Sakkinen 	 * CPU will silently overwrite the permissions as zero, which means
192c6d26d37SJarkko Sakkinen 	 * that we need to validate it ourselves.
193c6d26d37SJarkko Sakkinen 	 */
194c6d26d37SJarkko Sakkinen 	if (pt == SGX_SECINFO_TCS && perm)
195c6d26d37SJarkko Sakkinen 		return -EINVAL;
196c6d26d37SJarkko Sakkinen 
197c6d26d37SJarkko Sakkinen 	if (secinfo->flags & SGX_SECINFO_RESERVED_MASK)
198c6d26d37SJarkko Sakkinen 		return -EINVAL;
199c6d26d37SJarkko Sakkinen 
200c6d26d37SJarkko Sakkinen 	if (memchr_inv(secinfo->reserved, 0, sizeof(secinfo->reserved)))
201c6d26d37SJarkko Sakkinen 		return -EINVAL;
202c6d26d37SJarkko Sakkinen 
203c6d26d37SJarkko Sakkinen 	return 0;
204c6d26d37SJarkko Sakkinen }
205c6d26d37SJarkko Sakkinen 
__sgx_encl_add_page(struct sgx_encl * encl,struct sgx_encl_page * encl_page,struct sgx_epc_page * epc_page,struct sgx_secinfo * secinfo,unsigned long src)206c6d26d37SJarkko Sakkinen static int __sgx_encl_add_page(struct sgx_encl *encl,
207c6d26d37SJarkko Sakkinen 			       struct sgx_encl_page *encl_page,
208c6d26d37SJarkko Sakkinen 			       struct sgx_epc_page *epc_page,
209c6d26d37SJarkko Sakkinen 			       struct sgx_secinfo *secinfo, unsigned long src)
210c6d26d37SJarkko Sakkinen {
211c6d26d37SJarkko Sakkinen 	struct sgx_pageinfo pginfo;
212c6d26d37SJarkko Sakkinen 	struct vm_area_struct *vma;
213c6d26d37SJarkko Sakkinen 	struct page *src_page;
214c6d26d37SJarkko Sakkinen 	int ret;
215c6d26d37SJarkko Sakkinen 
216c6d26d37SJarkko Sakkinen 	/* Deny noexec. */
217c6d26d37SJarkko Sakkinen 	vma = find_vma(current->mm, src);
218c6d26d37SJarkko Sakkinen 	if (!vma)
219c6d26d37SJarkko Sakkinen 		return -EFAULT;
220c6d26d37SJarkko Sakkinen 
221c6d26d37SJarkko Sakkinen 	if (!(vma->vm_flags & VM_MAYEXEC))
222c6d26d37SJarkko Sakkinen 		return -EACCES;
223c6d26d37SJarkko Sakkinen 
22454d02069SLorenzo Stoakes 	ret = get_user_pages(src, 1, 0, &src_page);
225c6d26d37SJarkko Sakkinen 	if (ret < 1)
226c6d26d37SJarkko Sakkinen 		return -EFAULT;
227c6d26d37SJarkko Sakkinen 
228c6d26d37SJarkko Sakkinen 	pginfo.secs = (unsigned long)sgx_get_epc_virt_addr(encl->secs.epc_page);
229c6d26d37SJarkko Sakkinen 	pginfo.addr = encl_page->desc & PAGE_MASK;
230c6d26d37SJarkko Sakkinen 	pginfo.metadata = (unsigned long)secinfo;
23189e927bbSKristen Carlson Accardi 	pginfo.contents = (unsigned long)kmap_local_page(src_page);
232c6d26d37SJarkko Sakkinen 
233c6d26d37SJarkko Sakkinen 	ret = __eadd(&pginfo, sgx_get_epc_virt_addr(epc_page));
234c6d26d37SJarkko Sakkinen 
23589e927bbSKristen Carlson Accardi 	kunmap_local((void *)pginfo.contents);
236c6d26d37SJarkko Sakkinen 	put_page(src_page);
237c6d26d37SJarkko Sakkinen 
238c6d26d37SJarkko Sakkinen 	return ret ? -EIO : 0;
239c6d26d37SJarkko Sakkinen }
240c6d26d37SJarkko Sakkinen 
241c6d26d37SJarkko Sakkinen /*
242c6d26d37SJarkko Sakkinen  * If the caller requires measurement of the page as a proof for the content,
243c6d26d37SJarkko Sakkinen  * use EEXTEND to add a measurement for 256 bytes of the page. Repeat this
244c6d26d37SJarkko Sakkinen  * operation until the entire page is measured."
245c6d26d37SJarkko Sakkinen  */
__sgx_encl_extend(struct sgx_encl * encl,struct sgx_epc_page * epc_page)246c6d26d37SJarkko Sakkinen static int __sgx_encl_extend(struct sgx_encl *encl,
247c6d26d37SJarkko Sakkinen 			     struct sgx_epc_page *epc_page)
248c6d26d37SJarkko Sakkinen {
249c6d26d37SJarkko Sakkinen 	unsigned long offset;
250c6d26d37SJarkko Sakkinen 	int ret;
251c6d26d37SJarkko Sakkinen 
252c6d26d37SJarkko Sakkinen 	for (offset = 0; offset < PAGE_SIZE; offset += SGX_EEXTEND_BLOCK_SIZE) {
253c6d26d37SJarkko Sakkinen 		ret = __eextend(sgx_get_epc_virt_addr(encl->secs.epc_page),
254c6d26d37SJarkko Sakkinen 				sgx_get_epc_virt_addr(epc_page) + offset);
255c6d26d37SJarkko Sakkinen 		if (ret) {
256c6d26d37SJarkko Sakkinen 			if (encls_failed(ret))
257c6d26d37SJarkko Sakkinen 				ENCLS_WARN(ret, "EEXTEND");
258c6d26d37SJarkko Sakkinen 
259c6d26d37SJarkko Sakkinen 			return -EIO;
260c6d26d37SJarkko Sakkinen 		}
261c6d26d37SJarkko Sakkinen 	}
262c6d26d37SJarkko Sakkinen 
263c6d26d37SJarkko Sakkinen 	return 0;
264c6d26d37SJarkko Sakkinen }
265c6d26d37SJarkko Sakkinen 
sgx_encl_add_page(struct sgx_encl * encl,unsigned long src,unsigned long offset,struct sgx_secinfo * secinfo,unsigned long flags)266c6d26d37SJarkko Sakkinen static int sgx_encl_add_page(struct sgx_encl *encl, unsigned long src,
267c6d26d37SJarkko Sakkinen 			     unsigned long offset, struct sgx_secinfo *secinfo,
268c6d26d37SJarkko Sakkinen 			     unsigned long flags)
269c6d26d37SJarkko Sakkinen {
270c6d26d37SJarkko Sakkinen 	struct sgx_encl_page *encl_page;
271c6d26d37SJarkko Sakkinen 	struct sgx_epc_page *epc_page;
2721728ab54SJarkko Sakkinen 	struct sgx_va_page *va_page;
273c6d26d37SJarkko Sakkinen 	int ret;
274c6d26d37SJarkko Sakkinen 
275c6d26d37SJarkko Sakkinen 	encl_page = sgx_encl_page_alloc(encl, offset, secinfo->flags);
276c6d26d37SJarkko Sakkinen 	if (IS_ERR(encl_page))
277c6d26d37SJarkko Sakkinen 		return PTR_ERR(encl_page);
278c6d26d37SJarkko Sakkinen 
2791728ab54SJarkko Sakkinen 	epc_page = sgx_alloc_epc_page(encl_page, true);
280c6d26d37SJarkko Sakkinen 	if (IS_ERR(epc_page)) {
281c6d26d37SJarkko Sakkinen 		kfree(encl_page);
282c6d26d37SJarkko Sakkinen 		return PTR_ERR(epc_page);
283c6d26d37SJarkko Sakkinen 	}
284c6d26d37SJarkko Sakkinen 
285a76e7f1fSReinette Chatre 	va_page = sgx_encl_grow(encl, true);
2861728ab54SJarkko Sakkinen 	if (IS_ERR(va_page)) {
2871728ab54SJarkko Sakkinen 		ret = PTR_ERR(va_page);
2881728ab54SJarkko Sakkinen 		goto err_out_free;
2891728ab54SJarkko Sakkinen 	}
2901728ab54SJarkko Sakkinen 
291c6d26d37SJarkko Sakkinen 	mmap_read_lock(current->mm);
292c6d26d37SJarkko Sakkinen 	mutex_lock(&encl->lock);
293c6d26d37SJarkko Sakkinen 
294c6d26d37SJarkko Sakkinen 	/*
2951728ab54SJarkko Sakkinen 	 * Adding to encl->va_pages must be done under encl->lock.  Ditto for
2961728ab54SJarkko Sakkinen 	 * deleting (via sgx_encl_shrink()) in the error path.
2971728ab54SJarkko Sakkinen 	 */
2981728ab54SJarkko Sakkinen 	if (va_page)
2991728ab54SJarkko Sakkinen 		list_add(&va_page->list, &encl->va_pages);
3001728ab54SJarkko Sakkinen 
3011728ab54SJarkko Sakkinen 	/*
302c6d26d37SJarkko Sakkinen 	 * Insert prior to EADD in case of OOM.  EADD modifies MRENCLAVE, i.e.
303c6d26d37SJarkko Sakkinen 	 * can't be gracefully unwound, while failure on EADD/EXTEND is limited
304c6d26d37SJarkko Sakkinen 	 * to userspace errors (or kernel/hardware bugs).
305c6d26d37SJarkko Sakkinen 	 */
306c6d26d37SJarkko Sakkinen 	ret = xa_insert(&encl->page_array, PFN_DOWN(encl_page->desc),
307c6d26d37SJarkko Sakkinen 			encl_page, GFP_KERNEL);
308c6d26d37SJarkko Sakkinen 	if (ret)
309c6d26d37SJarkko Sakkinen 		goto err_out_unlock;
310c6d26d37SJarkko Sakkinen 
311c6d26d37SJarkko Sakkinen 	ret = __sgx_encl_add_page(encl, encl_page, epc_page, secinfo,
312c6d26d37SJarkko Sakkinen 				  src);
313c6d26d37SJarkko Sakkinen 	if (ret)
314c6d26d37SJarkko Sakkinen 		goto err_out;
315c6d26d37SJarkko Sakkinen 
316c6d26d37SJarkko Sakkinen 	/*
317c6d26d37SJarkko Sakkinen 	 * Complete the "add" before doing the "extend" so that the "add"
318c6d26d37SJarkko Sakkinen 	 * isn't in a half-baked state in the extremely unlikely scenario
319c6d26d37SJarkko Sakkinen 	 * the enclave will be destroyed in response to EEXTEND failure.
320c6d26d37SJarkko Sakkinen 	 */
321c6d26d37SJarkko Sakkinen 	encl_page->encl = encl;
322c6d26d37SJarkko Sakkinen 	encl_page->epc_page = epc_page;
3238cb7b502SReinette Chatre 	encl_page->type = (secinfo->flags & SGX_SECINFO_PAGE_TYPE_MASK) >> 8;
324c6d26d37SJarkko Sakkinen 	encl->secs_child_cnt++;
325c6d26d37SJarkko Sakkinen 
326c6d26d37SJarkko Sakkinen 	if (flags & SGX_PAGE_MEASURE) {
327c6d26d37SJarkko Sakkinen 		ret = __sgx_encl_extend(encl, epc_page);
328c6d26d37SJarkko Sakkinen 		if (ret)
329c6d26d37SJarkko Sakkinen 			goto err_out;
330c6d26d37SJarkko Sakkinen 	}
331c6d26d37SJarkko Sakkinen 
3321728ab54SJarkko Sakkinen 	sgx_mark_page_reclaimable(encl_page->epc_page);
333c6d26d37SJarkko Sakkinen 	mutex_unlock(&encl->lock);
334c6d26d37SJarkko Sakkinen 	mmap_read_unlock(current->mm);
335c6d26d37SJarkko Sakkinen 	return ret;
336c6d26d37SJarkko Sakkinen 
337c6d26d37SJarkko Sakkinen err_out:
338c6d26d37SJarkko Sakkinen 	xa_erase(&encl->page_array, PFN_DOWN(encl_page->desc));
339c6d26d37SJarkko Sakkinen 
340c6d26d37SJarkko Sakkinen err_out_unlock:
3411728ab54SJarkko Sakkinen 	sgx_encl_shrink(encl, va_page);
342c6d26d37SJarkko Sakkinen 	mutex_unlock(&encl->lock);
343c6d26d37SJarkko Sakkinen 	mmap_read_unlock(current->mm);
344c6d26d37SJarkko Sakkinen 
3451728ab54SJarkko Sakkinen err_out_free:
346b0c7459bSKai Huang 	sgx_encl_free_epc_page(epc_page);
347c6d26d37SJarkko Sakkinen 	kfree(encl_page);
348c6d26d37SJarkko Sakkinen 
349c6d26d37SJarkko Sakkinen 	return ret;
350c6d26d37SJarkko Sakkinen }
351c6d26d37SJarkko Sakkinen 
352dda03e2cSReinette Chatre /*
353dda03e2cSReinette Chatre  * Ensure user provided offset and length values are valid for
354dda03e2cSReinette Chatre  * an enclave.
355dda03e2cSReinette Chatre  */
sgx_validate_offset_length(struct sgx_encl * encl,unsigned long offset,unsigned long length)356dda03e2cSReinette Chatre static int sgx_validate_offset_length(struct sgx_encl *encl,
357dda03e2cSReinette Chatre 				      unsigned long offset,
358dda03e2cSReinette Chatre 				      unsigned long length)
359dda03e2cSReinette Chatre {
360dda03e2cSReinette Chatre 	if (!IS_ALIGNED(offset, PAGE_SIZE))
361dda03e2cSReinette Chatre 		return -EINVAL;
362dda03e2cSReinette Chatre 
363dda03e2cSReinette Chatre 	if (!length || !IS_ALIGNED(length, PAGE_SIZE))
364dda03e2cSReinette Chatre 		return -EINVAL;
365dda03e2cSReinette Chatre 
366f0861f49SBorys Popławski 	if (offset + length < offset)
367f0861f49SBorys Popławski 		return -EINVAL;
368f0861f49SBorys Popławski 
369dda03e2cSReinette Chatre 	if (offset + length - PAGE_SIZE >= encl->size)
370dda03e2cSReinette Chatre 		return -EINVAL;
371dda03e2cSReinette Chatre 
372dda03e2cSReinette Chatre 	return 0;
373dda03e2cSReinette Chatre }
374dda03e2cSReinette Chatre 
375c6d26d37SJarkko Sakkinen /**
376c6d26d37SJarkko Sakkinen  * sgx_ioc_enclave_add_pages() - The handler for %SGX_IOC_ENCLAVE_ADD_PAGES
377c6d26d37SJarkko Sakkinen  * @encl:       an enclave pointer
378c6d26d37SJarkko Sakkinen  * @arg:	a user pointer to a struct sgx_enclave_add_pages instance
379c6d26d37SJarkko Sakkinen  *
380c6d26d37SJarkko Sakkinen  * Add one or more pages to an uninitialized enclave, and optionally extend the
381c6d26d37SJarkko Sakkinen  * measurement with the contents of the page. The SECINFO and measurement mask
382c6d26d37SJarkko Sakkinen  * are applied to all pages.
383c6d26d37SJarkko Sakkinen  *
384c6d26d37SJarkko Sakkinen  * A SECINFO for a TCS is required to always contain zero permissions because
385c6d26d37SJarkko Sakkinen  * CPU silently zeros them. Allowing anything else would cause a mismatch in
386c6d26d37SJarkko Sakkinen  * the measurement.
387c6d26d37SJarkko Sakkinen  *
388c6d26d37SJarkko Sakkinen  * mmap()'s protection bits are capped by the page permissions. For each page
389c6d26d37SJarkko Sakkinen  * address, the maximum protection bits are computed with the following
390c6d26d37SJarkko Sakkinen  * heuristics:
391c6d26d37SJarkko Sakkinen  *
392c6d26d37SJarkko Sakkinen  * 1. A regular page: PROT_R, PROT_W and PROT_X match the SECINFO permissions.
393c6d26d37SJarkko Sakkinen  * 2. A TCS page: PROT_R | PROT_W.
394c6d26d37SJarkko Sakkinen  *
395c6d26d37SJarkko Sakkinen  * mmap() is not allowed to surpass the minimum of the maximum protection bits
396c6d26d37SJarkko Sakkinen  * within the given address range.
397c6d26d37SJarkko Sakkinen  *
398c6d26d37SJarkko Sakkinen  * The function deinitializes kernel data structures for enclave and returns
399c6d26d37SJarkko Sakkinen  * -EIO in any of the following conditions:
400c6d26d37SJarkko Sakkinen  *
401c6d26d37SJarkko Sakkinen  * - Enclave Page Cache (EPC), the physical memory holding enclaves, has
402c6d26d37SJarkko Sakkinen  *   been invalidated. This will cause EADD and EEXTEND to fail.
403c6d26d37SJarkko Sakkinen  * - If the source address is corrupted somehow when executing EADD.
404c6d26d37SJarkko Sakkinen  *
405c6d26d37SJarkko Sakkinen  * Return:
406c6d26d37SJarkko Sakkinen  * - 0:		Success.
407c6d26d37SJarkko Sakkinen  * - -EACCES:	The source page is located in a noexec partition.
408c6d26d37SJarkko Sakkinen  * - -ENOMEM:	Out of EPC pages.
409c6d26d37SJarkko Sakkinen  * - -EINTR:	The call was interrupted before data was processed.
410c6d26d37SJarkko Sakkinen  * - -EIO:	Either EADD or EEXTEND failed because invalid source address
411c6d26d37SJarkko Sakkinen  *		or power cycle.
412c6d26d37SJarkko Sakkinen  * - -errno:	POSIX error.
413c6d26d37SJarkko Sakkinen  */
sgx_ioc_enclave_add_pages(struct sgx_encl * encl,void __user * arg)414c6d26d37SJarkko Sakkinen static long sgx_ioc_enclave_add_pages(struct sgx_encl *encl, void __user *arg)
415c6d26d37SJarkko Sakkinen {
416c6d26d37SJarkko Sakkinen 	struct sgx_enclave_add_pages add_arg;
417c6d26d37SJarkko Sakkinen 	struct sgx_secinfo secinfo;
418c6d26d37SJarkko Sakkinen 	unsigned long c;
419c6d26d37SJarkko Sakkinen 	int ret;
420c6d26d37SJarkko Sakkinen 
4219d0c151bSJarkko Sakkinen 	if (!test_bit(SGX_ENCL_CREATED, &encl->flags) ||
4229d0c151bSJarkko Sakkinen 	    test_bit(SGX_ENCL_INITIALIZED, &encl->flags))
423c6d26d37SJarkko Sakkinen 		return -EINVAL;
424c6d26d37SJarkko Sakkinen 
425c6d26d37SJarkko Sakkinen 	if (copy_from_user(&add_arg, arg, sizeof(add_arg)))
426c6d26d37SJarkko Sakkinen 		return -EFAULT;
427c6d26d37SJarkko Sakkinen 
428dda03e2cSReinette Chatre 	if (!IS_ALIGNED(add_arg.src, PAGE_SIZE))
429c6d26d37SJarkko Sakkinen 		return -EINVAL;
430c6d26d37SJarkko Sakkinen 
431dda03e2cSReinette Chatre 	if (sgx_validate_offset_length(encl, add_arg.offset, add_arg.length))
432c6d26d37SJarkko Sakkinen 		return -EINVAL;
433c6d26d37SJarkko Sakkinen 
434c6d26d37SJarkko Sakkinen 	if (copy_from_user(&secinfo, (void __user *)add_arg.secinfo,
435c6d26d37SJarkko Sakkinen 			   sizeof(secinfo)))
436c6d26d37SJarkko Sakkinen 		return -EFAULT;
437c6d26d37SJarkko Sakkinen 
438c6d26d37SJarkko Sakkinen 	if (sgx_validate_secinfo(&secinfo))
439c6d26d37SJarkko Sakkinen 		return -EINVAL;
440c6d26d37SJarkko Sakkinen 
441c6d26d37SJarkko Sakkinen 	for (c = 0 ; c < add_arg.length; c += PAGE_SIZE) {
442c6d26d37SJarkko Sakkinen 		if (signal_pending(current)) {
443c6d26d37SJarkko Sakkinen 			if (!c)
44414132a5bSJarkko Sakkinen 				ret = -ERESTARTSYS;
445c6d26d37SJarkko Sakkinen 
446c6d26d37SJarkko Sakkinen 			break;
447c6d26d37SJarkko Sakkinen 		}
448c6d26d37SJarkko Sakkinen 
449c6d26d37SJarkko Sakkinen 		if (need_resched())
450c6d26d37SJarkko Sakkinen 			cond_resched();
451c6d26d37SJarkko Sakkinen 
452c6d26d37SJarkko Sakkinen 		ret = sgx_encl_add_page(encl, add_arg.src + c, add_arg.offset + c,
453c6d26d37SJarkko Sakkinen 					&secinfo, add_arg.flags);
454c6d26d37SJarkko Sakkinen 		if (ret)
455c6d26d37SJarkko Sakkinen 			break;
456c6d26d37SJarkko Sakkinen 	}
457c6d26d37SJarkko Sakkinen 
458c6d26d37SJarkko Sakkinen 	add_arg.count = c;
459c6d26d37SJarkko Sakkinen 
460c6d26d37SJarkko Sakkinen 	if (copy_to_user(arg, &add_arg, sizeof(add_arg)))
461c6d26d37SJarkko Sakkinen 		return -EFAULT;
462c6d26d37SJarkko Sakkinen 
463c6d26d37SJarkko Sakkinen 	return ret;
464c6d26d37SJarkko Sakkinen }
465c6d26d37SJarkko Sakkinen 
__sgx_get_key_hash(struct crypto_shash * tfm,const void * modulus,void * hash)4669d0c151bSJarkko Sakkinen static int __sgx_get_key_hash(struct crypto_shash *tfm, const void *modulus,
4679d0c151bSJarkko Sakkinen 			      void *hash)
4689d0c151bSJarkko Sakkinen {
4699d0c151bSJarkko Sakkinen 	SHASH_DESC_ON_STACK(shash, tfm);
4709d0c151bSJarkko Sakkinen 
4719d0c151bSJarkko Sakkinen 	shash->tfm = tfm;
4729d0c151bSJarkko Sakkinen 
4739d0c151bSJarkko Sakkinen 	return crypto_shash_digest(shash, modulus, SGX_MODULUS_SIZE, hash);
4749d0c151bSJarkko Sakkinen }
4759d0c151bSJarkko Sakkinen 
sgx_get_key_hash(const void * modulus,void * hash)4769d0c151bSJarkko Sakkinen static int sgx_get_key_hash(const void *modulus, void *hash)
4779d0c151bSJarkko Sakkinen {
4789d0c151bSJarkko Sakkinen 	struct crypto_shash *tfm;
4799d0c151bSJarkko Sakkinen 	int ret;
4809d0c151bSJarkko Sakkinen 
4819d0c151bSJarkko Sakkinen 	tfm = crypto_alloc_shash("sha256", 0, CRYPTO_ALG_ASYNC);
4829d0c151bSJarkko Sakkinen 	if (IS_ERR(tfm))
4839d0c151bSJarkko Sakkinen 		return PTR_ERR(tfm);
4849d0c151bSJarkko Sakkinen 
4859d0c151bSJarkko Sakkinen 	ret = __sgx_get_key_hash(tfm, modulus, hash);
4869d0c151bSJarkko Sakkinen 
4879d0c151bSJarkko Sakkinen 	crypto_free_shash(tfm);
4889d0c151bSJarkko Sakkinen 	return ret;
4899d0c151bSJarkko Sakkinen }
4909d0c151bSJarkko Sakkinen 
sgx_encl_init(struct sgx_encl * encl,struct sgx_sigstruct * sigstruct,void * token)4919d0c151bSJarkko Sakkinen static int sgx_encl_init(struct sgx_encl *encl, struct sgx_sigstruct *sigstruct,
4929d0c151bSJarkko Sakkinen 			 void *token)
4939d0c151bSJarkko Sakkinen {
4949d0c151bSJarkko Sakkinen 	u64 mrsigner[4];
49573916b6aSKai Huang 	int i, j;
4969d0c151bSJarkko Sakkinen 	void *addr;
4979d0c151bSJarkko Sakkinen 	int ret;
4989d0c151bSJarkko Sakkinen 
4999d0c151bSJarkko Sakkinen 	/*
5009d0c151bSJarkko Sakkinen 	 * Deny initializing enclaves with attributes (namely provisioning)
5019d0c151bSJarkko Sakkinen 	 * that have not been explicitly allowed.
5029d0c151bSJarkko Sakkinen 	 */
5039d0c151bSJarkko Sakkinen 	if (encl->attributes & ~encl->attributes_mask)
5049d0c151bSJarkko Sakkinen 		return -EACCES;
5059d0c151bSJarkko Sakkinen 
5069d0c151bSJarkko Sakkinen 	/*
5079d0c151bSJarkko Sakkinen 	 * Attributes should not be enforced *only* against what's available on
5089d0c151bSJarkko Sakkinen 	 * platform (done in sgx_encl_create) but checked and enforced against
5099d0c151bSJarkko Sakkinen 	 * the mask for enforcement in sigstruct. For example an enclave could
5109d0c151bSJarkko Sakkinen 	 * opt to sign with AVX bit in xfrm, but still be loadable on a platform
5119d0c151bSJarkko Sakkinen 	 * without it if the sigstruct->body.attributes_mask does not turn that
5129d0c151bSJarkko Sakkinen 	 * bit on.
5139d0c151bSJarkko Sakkinen 	 */
5149d0c151bSJarkko Sakkinen 	if (sigstruct->body.attributes & sigstruct->body.attributes_mask &
5159d0c151bSJarkko Sakkinen 	    sgx_attributes_reserved_mask)
5169d0c151bSJarkko Sakkinen 		return -EINVAL;
5179d0c151bSJarkko Sakkinen 
5189d0c151bSJarkko Sakkinen 	if (sigstruct->body.miscselect & sigstruct->body.misc_mask &
5199d0c151bSJarkko Sakkinen 	    sgx_misc_reserved_mask)
5209d0c151bSJarkko Sakkinen 		return -EINVAL;
5219d0c151bSJarkko Sakkinen 
5229d0c151bSJarkko Sakkinen 	if (sigstruct->body.xfrm & sigstruct->body.xfrm_mask &
5239d0c151bSJarkko Sakkinen 	    sgx_xfrm_reserved_mask)
5249d0c151bSJarkko Sakkinen 		return -EINVAL;
5259d0c151bSJarkko Sakkinen 
5269d0c151bSJarkko Sakkinen 	ret = sgx_get_key_hash(sigstruct->modulus, mrsigner);
5279d0c151bSJarkko Sakkinen 	if (ret)
5289d0c151bSJarkko Sakkinen 		return ret;
5299d0c151bSJarkko Sakkinen 
5309d0c151bSJarkko Sakkinen 	mutex_lock(&encl->lock);
5319d0c151bSJarkko Sakkinen 
5329d0c151bSJarkko Sakkinen 	/*
5339d0c151bSJarkko Sakkinen 	 * ENCLS[EINIT] is interruptible because it has such a high latency,
5349d0c151bSJarkko Sakkinen 	 * e.g. 50k+ cycles on success. If an IRQ/NMI/SMI becomes pending,
5359d0c151bSJarkko Sakkinen 	 * EINIT may fail with SGX_UNMASKED_EVENT so that the event can be
5369d0c151bSJarkko Sakkinen 	 * serviced.
5379d0c151bSJarkko Sakkinen 	 */
5389d0c151bSJarkko Sakkinen 	for (i = 0; i < SGX_EINIT_SLEEP_COUNT; i++) {
5399d0c151bSJarkko Sakkinen 		for (j = 0; j < SGX_EINIT_SPIN_COUNT; j++) {
5409d0c151bSJarkko Sakkinen 			addr = sgx_get_epc_virt_addr(encl->secs.epc_page);
5419d0c151bSJarkko Sakkinen 
5429d0c151bSJarkko Sakkinen 			preempt_disable();
5439d0c151bSJarkko Sakkinen 
54473916b6aSKai Huang 			sgx_update_lepubkeyhash(mrsigner);
5459d0c151bSJarkko Sakkinen 
5469d0c151bSJarkko Sakkinen 			ret = __einit(sigstruct, token, addr);
5479d0c151bSJarkko Sakkinen 
5489d0c151bSJarkko Sakkinen 			preempt_enable();
5499d0c151bSJarkko Sakkinen 
5509d0c151bSJarkko Sakkinen 			if (ret == SGX_UNMASKED_EVENT)
5519d0c151bSJarkko Sakkinen 				continue;
5529d0c151bSJarkko Sakkinen 			else
5539d0c151bSJarkko Sakkinen 				break;
5549d0c151bSJarkko Sakkinen 		}
5559d0c151bSJarkko Sakkinen 
5569d0c151bSJarkko Sakkinen 		if (ret != SGX_UNMASKED_EVENT)
5579d0c151bSJarkko Sakkinen 			break;
5589d0c151bSJarkko Sakkinen 
5599d0c151bSJarkko Sakkinen 		msleep_interruptible(SGX_EINIT_SLEEP_TIME);
5609d0c151bSJarkko Sakkinen 
5619d0c151bSJarkko Sakkinen 		if (signal_pending(current)) {
5629d0c151bSJarkko Sakkinen 			ret = -ERESTARTSYS;
5639d0c151bSJarkko Sakkinen 			goto err_out;
5649d0c151bSJarkko Sakkinen 		}
5659d0c151bSJarkko Sakkinen 	}
5669d0c151bSJarkko Sakkinen 
567a67136b4SSean Christopherson 	if (encls_faulted(ret)) {
5689d0c151bSJarkko Sakkinen 		if (encls_failed(ret))
5699d0c151bSJarkko Sakkinen 			ENCLS_WARN(ret, "EINIT");
5709d0c151bSJarkko Sakkinen 
5719d0c151bSJarkko Sakkinen 		ret = -EIO;
5729d0c151bSJarkko Sakkinen 	} else if (ret) {
5739d0c151bSJarkko Sakkinen 		pr_debug("EINIT returned %d\n", ret);
5749d0c151bSJarkko Sakkinen 		ret = -EPERM;
5759d0c151bSJarkko Sakkinen 	} else {
5769d0c151bSJarkko Sakkinen 		set_bit(SGX_ENCL_INITIALIZED, &encl->flags);
5779d0c151bSJarkko Sakkinen 	}
5789d0c151bSJarkko Sakkinen 
5799d0c151bSJarkko Sakkinen err_out:
5809d0c151bSJarkko Sakkinen 	mutex_unlock(&encl->lock);
5819d0c151bSJarkko Sakkinen 	return ret;
5829d0c151bSJarkko Sakkinen }
5839d0c151bSJarkko Sakkinen 
5849d0c151bSJarkko Sakkinen /**
5859d0c151bSJarkko Sakkinen  * sgx_ioc_enclave_init() - handler for %SGX_IOC_ENCLAVE_INIT
5869d0c151bSJarkko Sakkinen  * @encl:	an enclave pointer
5879d0c151bSJarkko Sakkinen  * @arg:	userspace pointer to a struct sgx_enclave_init instance
5889d0c151bSJarkko Sakkinen  *
5899d0c151bSJarkko Sakkinen  * Flush any outstanding enqueued EADD operations and perform EINIT.  The
5909d0c151bSJarkko Sakkinen  * Launch Enclave Public Key Hash MSRs are rewritten as necessary to match
5919d0c151bSJarkko Sakkinen  * the enclave's MRSIGNER, which is caculated from the provided sigstruct.
5929d0c151bSJarkko Sakkinen  *
5939d0c151bSJarkko Sakkinen  * Return:
5949d0c151bSJarkko Sakkinen  * - 0:		Success.
5959d0c151bSJarkko Sakkinen  * - -EPERM:	Invalid SIGSTRUCT.
5969d0c151bSJarkko Sakkinen  * - -EIO:	EINIT failed because of a power cycle.
5979d0c151bSJarkko Sakkinen  * - -errno:	POSIX error.
5989d0c151bSJarkko Sakkinen  */
sgx_ioc_enclave_init(struct sgx_encl * encl,void __user * arg)5999d0c151bSJarkko Sakkinen static long sgx_ioc_enclave_init(struct sgx_encl *encl, void __user *arg)
6009d0c151bSJarkko Sakkinen {
6019d0c151bSJarkko Sakkinen 	struct sgx_sigstruct *sigstruct;
6029d0c151bSJarkko Sakkinen 	struct sgx_enclave_init init_arg;
6039d0c151bSJarkko Sakkinen 	void *token;
6049d0c151bSJarkko Sakkinen 	int ret;
6059d0c151bSJarkko Sakkinen 
6069d0c151bSJarkko Sakkinen 	if (!test_bit(SGX_ENCL_CREATED, &encl->flags) ||
6079d0c151bSJarkko Sakkinen 	    test_bit(SGX_ENCL_INITIALIZED, &encl->flags))
6089d0c151bSJarkko Sakkinen 		return -EINVAL;
6099d0c151bSJarkko Sakkinen 
6109d0c151bSJarkko Sakkinen 	if (copy_from_user(&init_arg, arg, sizeof(init_arg)))
6119d0c151bSJarkko Sakkinen 		return -EFAULT;
6129d0c151bSJarkko Sakkinen 
613633b0616SIra Weiny 	/*
614633b0616SIra Weiny 	 * 'sigstruct' must be on a page boundary and 'token' on a 512 byte
615633b0616SIra Weiny 	 * boundary.  kmalloc() will give this alignment when allocating
616633b0616SIra Weiny 	 * PAGE_SIZE bytes.
617633b0616SIra Weiny 	 */
618633b0616SIra Weiny 	sigstruct = kmalloc(PAGE_SIZE, GFP_KERNEL);
619633b0616SIra Weiny 	if (!sigstruct)
6209d0c151bSJarkko Sakkinen 		return -ENOMEM;
6219d0c151bSJarkko Sakkinen 
6229d0c151bSJarkko Sakkinen 	token = (void *)((unsigned long)sigstruct + PAGE_SIZE / 2);
6239d0c151bSJarkko Sakkinen 	memset(token, 0, SGX_LAUNCH_TOKEN_SIZE);
6249d0c151bSJarkko Sakkinen 
6259d0c151bSJarkko Sakkinen 	if (copy_from_user(sigstruct, (void __user *)init_arg.sigstruct,
6269d0c151bSJarkko Sakkinen 			   sizeof(*sigstruct))) {
6279d0c151bSJarkko Sakkinen 		ret = -EFAULT;
6289d0c151bSJarkko Sakkinen 		goto out;
6299d0c151bSJarkko Sakkinen 	}
6309d0c151bSJarkko Sakkinen 
6319d0c151bSJarkko Sakkinen 	/*
6329d0c151bSJarkko Sakkinen 	 * A legacy field used with Intel signed enclaves. These used to mean
6339d0c151bSJarkko Sakkinen 	 * regular and architectural enclaves. The CPU only accepts these values
6349d0c151bSJarkko Sakkinen 	 * but they do not have any other meaning.
6359d0c151bSJarkko Sakkinen 	 *
6369d0c151bSJarkko Sakkinen 	 * Thus, reject any other values.
6379d0c151bSJarkko Sakkinen 	 */
6389d0c151bSJarkko Sakkinen 	if (sigstruct->header.vendor != 0x0000 &&
6399d0c151bSJarkko Sakkinen 	    sigstruct->header.vendor != 0x8086) {
6409d0c151bSJarkko Sakkinen 		ret = -EINVAL;
6419d0c151bSJarkko Sakkinen 		goto out;
6429d0c151bSJarkko Sakkinen 	}
6439d0c151bSJarkko Sakkinen 
6449d0c151bSJarkko Sakkinen 	ret = sgx_encl_init(encl, sigstruct, token);
6459d0c151bSJarkko Sakkinen 
6469d0c151bSJarkko Sakkinen out:
647633b0616SIra Weiny 	kfree(sigstruct);
6489d0c151bSJarkko Sakkinen 	return ret;
6499d0c151bSJarkko Sakkinen }
6509d0c151bSJarkko Sakkinen 
651c82c6186SJarkko Sakkinen /**
652c82c6186SJarkko Sakkinen  * sgx_ioc_enclave_provision() - handler for %SGX_IOC_ENCLAVE_PROVISION
653afe76ecaSBorislav Petkov  * @encl:	an enclave pointer
654c82c6186SJarkko Sakkinen  * @arg:	userspace pointer to a struct sgx_enclave_provision instance
655c82c6186SJarkko Sakkinen  *
656c82c6186SJarkko Sakkinen  * Allow ATTRIBUTE.PROVISION_KEY for an enclave by providing a file handle to
657c82c6186SJarkko Sakkinen  * /dev/sgx_provision.
658c82c6186SJarkko Sakkinen  *
659c82c6186SJarkko Sakkinen  * Return:
660c82c6186SJarkko Sakkinen  * - 0:		Success.
661c82c6186SJarkko Sakkinen  * - -errno:	Otherwise.
662c82c6186SJarkko Sakkinen  */
sgx_ioc_enclave_provision(struct sgx_encl * encl,void __user * arg)663c82c6186SJarkko Sakkinen static long sgx_ioc_enclave_provision(struct sgx_encl *encl, void __user *arg)
664c82c6186SJarkko Sakkinen {
665c82c6186SJarkko Sakkinen 	struct sgx_enclave_provision params;
666c82c6186SJarkko Sakkinen 
667c82c6186SJarkko Sakkinen 	if (copy_from_user(&params, arg, sizeof(params)))
668c82c6186SJarkko Sakkinen 		return -EFAULT;
669c82c6186SJarkko Sakkinen 
670b3754e5dSSean Christopherson 	return sgx_set_attribute(&encl->attributes_mask, params.fd);
671c82c6186SJarkko Sakkinen }
6729d0c151bSJarkko Sakkinen 
673ff08530aSReinette Chatre /*
674ff08530aSReinette Chatre  * Ensure enclave is ready for SGX2 functions. Readiness is checked
675ff08530aSReinette Chatre  * by ensuring the hardware supports SGX2 and the enclave is initialized
676ff08530aSReinette Chatre  * and thus able to handle requests to modify pages within it.
677ff08530aSReinette Chatre  */
sgx_ioc_sgx2_ready(struct sgx_encl * encl)678ff08530aSReinette Chatre static int sgx_ioc_sgx2_ready(struct sgx_encl *encl)
679ff08530aSReinette Chatre {
680ff08530aSReinette Chatre 	if (!(cpu_feature_enabled(X86_FEATURE_SGX2)))
681ff08530aSReinette Chatre 		return -ENODEV;
682ff08530aSReinette Chatre 
683ff08530aSReinette Chatre 	if (!test_bit(SGX_ENCL_INITIALIZED, &encl->flags))
684ff08530aSReinette Chatre 		return -EINVAL;
685ff08530aSReinette Chatre 
686ff08530aSReinette Chatre 	return 0;
687ff08530aSReinette Chatre }
688ff08530aSReinette Chatre 
689ff08530aSReinette Chatre /*
690ff08530aSReinette Chatre  * Some SGX functions require that no cached linear-to-physical address
691ff08530aSReinette Chatre  * mappings are present before they can succeed. Collaborate with
692ff08530aSReinette Chatre  * hardware via ENCLS[ETRACK] to ensure that all cached
693ff08530aSReinette Chatre  * linear-to-physical address mappings belonging to all threads of
694ff08530aSReinette Chatre  * the enclave are cleared. See sgx_encl_cpumask() for details.
695ff08530aSReinette Chatre  *
696ff08530aSReinette Chatre  * Must be called with enclave's mutex held from the time the
697ff08530aSReinette Chatre  * SGX function requiring that no cached linear-to-physical mappings
698ff08530aSReinette Chatre  * are present is executed until this ETRACK flow is complete.
699ff08530aSReinette Chatre  */
sgx_enclave_etrack(struct sgx_encl * encl)700ff08530aSReinette Chatre static int sgx_enclave_etrack(struct sgx_encl *encl)
701ff08530aSReinette Chatre {
702ff08530aSReinette Chatre 	void *epc_virt;
703ff08530aSReinette Chatre 	int ret;
704ff08530aSReinette Chatre 
705ff08530aSReinette Chatre 	epc_virt = sgx_get_epc_virt_addr(encl->secs.epc_page);
706ff08530aSReinette Chatre 	ret = __etrack(epc_virt);
707ff08530aSReinette Chatre 	if (ret) {
708ff08530aSReinette Chatre 		/*
709ff08530aSReinette Chatre 		 * ETRACK only fails when there is an OS issue. For
710ff08530aSReinette Chatre 		 * example, two consecutive ETRACK was sent without
711ff08530aSReinette Chatre 		 * completed IPI between.
712ff08530aSReinette Chatre 		 */
713ff08530aSReinette Chatre 		pr_err_once("ETRACK returned %d (0x%x)", ret, ret);
714ff08530aSReinette Chatre 		/*
715ff08530aSReinette Chatre 		 * Send IPIs to kick CPUs out of the enclave and
716ff08530aSReinette Chatre 		 * try ETRACK again.
717ff08530aSReinette Chatre 		 */
718ff08530aSReinette Chatre 		on_each_cpu_mask(sgx_encl_cpumask(encl), sgx_ipi_cb, NULL, 1);
719ff08530aSReinette Chatre 		ret = __etrack(epc_virt);
720ff08530aSReinette Chatre 		if (ret) {
721ff08530aSReinette Chatre 			pr_err_once("ETRACK repeat returned %d (0x%x)",
722ff08530aSReinette Chatre 				    ret, ret);
723ff08530aSReinette Chatre 			return -EFAULT;
724ff08530aSReinette Chatre 		}
725ff08530aSReinette Chatre 	}
726ff08530aSReinette Chatre 	on_each_cpu_mask(sgx_encl_cpumask(encl), sgx_ipi_cb, NULL, 1);
727ff08530aSReinette Chatre 
728ff08530aSReinette Chatre 	return 0;
729ff08530aSReinette Chatre }
730ff08530aSReinette Chatre 
731ff08530aSReinette Chatre /**
732ff08530aSReinette Chatre  * sgx_enclave_restrict_permissions() - Restrict EPCM permissions
733ff08530aSReinette Chatre  * @encl:	Enclave to which the pages belong.
734ff08530aSReinette Chatre  * @modp:	Checked parameters from user on which pages need modifying and
735ff08530aSReinette Chatre  *              their new permissions.
736ff08530aSReinette Chatre  *
737ff08530aSReinette Chatre  * Return:
738ff08530aSReinette Chatre  * - 0:		Success.
739ff08530aSReinette Chatre  * - -errno:	Otherwise.
740ff08530aSReinette Chatre  */
741ff08530aSReinette Chatre static long
sgx_enclave_restrict_permissions(struct sgx_encl * encl,struct sgx_enclave_restrict_permissions * modp)742ff08530aSReinette Chatre sgx_enclave_restrict_permissions(struct sgx_encl *encl,
743ff08530aSReinette Chatre 				 struct sgx_enclave_restrict_permissions *modp)
744ff08530aSReinette Chatre {
745ff08530aSReinette Chatre 	struct sgx_encl_page *entry;
746ff08530aSReinette Chatre 	struct sgx_secinfo secinfo;
747ff08530aSReinette Chatre 	unsigned long addr;
748ff08530aSReinette Chatre 	unsigned long c;
749ff08530aSReinette Chatre 	void *epc_virt;
750ff08530aSReinette Chatre 	int ret;
751ff08530aSReinette Chatre 
752ff08530aSReinette Chatre 	memset(&secinfo, 0, sizeof(secinfo));
753ff08530aSReinette Chatre 	secinfo.flags = modp->permissions & SGX_SECINFO_PERMISSION_MASK;
754ff08530aSReinette Chatre 
755ff08530aSReinette Chatre 	for (c = 0 ; c < modp->length; c += PAGE_SIZE) {
756ff08530aSReinette Chatre 		addr = encl->base + modp->offset + c;
757ff08530aSReinette Chatre 
758a0506b3bSReinette Chatre 		sgx_reclaim_direct();
759a0506b3bSReinette Chatre 
760ff08530aSReinette Chatre 		mutex_lock(&encl->lock);
761ff08530aSReinette Chatre 
762ff08530aSReinette Chatre 		entry = sgx_encl_load_page(encl, addr);
763ff08530aSReinette Chatre 		if (IS_ERR(entry)) {
764ff08530aSReinette Chatre 			ret = PTR_ERR(entry) == -EBUSY ? -EAGAIN : -EFAULT;
765ff08530aSReinette Chatre 			goto out_unlock;
766ff08530aSReinette Chatre 		}
767ff08530aSReinette Chatre 
768ff08530aSReinette Chatre 		/*
769ff08530aSReinette Chatre 		 * Changing EPCM permissions is only supported on regular
770ff08530aSReinette Chatre 		 * SGX pages. Attempting this change on other pages will
771ff08530aSReinette Chatre 		 * result in #PF.
772ff08530aSReinette Chatre 		 */
773ff08530aSReinette Chatre 		if (entry->type != SGX_PAGE_TYPE_REG) {
774ff08530aSReinette Chatre 			ret = -EINVAL;
775ff08530aSReinette Chatre 			goto out_unlock;
776ff08530aSReinette Chatre 		}
777ff08530aSReinette Chatre 
778ff08530aSReinette Chatre 		/*
779ff08530aSReinette Chatre 		 * Apart from ensuring that read-access remains, do not verify
780ff08530aSReinette Chatre 		 * the permission bits requested. Kernel has no control over
781ff08530aSReinette Chatre 		 * how EPCM permissions can be relaxed from within the enclave.
782ff08530aSReinette Chatre 		 * ENCLS[EMODPR] can only remove existing EPCM permissions,
783ff08530aSReinette Chatre 		 * attempting to set new permissions will be ignored by the
784ff08530aSReinette Chatre 		 * hardware.
785ff08530aSReinette Chatre 		 */
786ff08530aSReinette Chatre 
787ff08530aSReinette Chatre 		/* Change EPCM permissions. */
788ff08530aSReinette Chatre 		epc_virt = sgx_get_epc_virt_addr(entry->epc_page);
789ff08530aSReinette Chatre 		ret = __emodpr(&secinfo, epc_virt);
790ff08530aSReinette Chatre 		if (encls_faulted(ret)) {
791ff08530aSReinette Chatre 			/*
792ff08530aSReinette Chatre 			 * All possible faults should be avoidable:
793ff08530aSReinette Chatre 			 * parameters have been checked, will only change
794ff08530aSReinette Chatre 			 * permissions of a regular page, and no concurrent
795ff08530aSReinette Chatre 			 * SGX1/SGX2 ENCLS instructions since these
796ff08530aSReinette Chatre 			 * are protected with mutex.
797ff08530aSReinette Chatre 			 */
798ff08530aSReinette Chatre 			pr_err_once("EMODPR encountered exception %d\n",
799ff08530aSReinette Chatre 				    ENCLS_TRAPNR(ret));
800ff08530aSReinette Chatre 			ret = -EFAULT;
801ff08530aSReinette Chatre 			goto out_unlock;
802ff08530aSReinette Chatre 		}
803ff08530aSReinette Chatre 		if (encls_failed(ret)) {
804ff08530aSReinette Chatre 			modp->result = ret;
805ff08530aSReinette Chatre 			ret = -EFAULT;
806ff08530aSReinette Chatre 			goto out_unlock;
807ff08530aSReinette Chatre 		}
808ff08530aSReinette Chatre 
809ff08530aSReinette Chatre 		ret = sgx_enclave_etrack(encl);
810ff08530aSReinette Chatre 		if (ret) {
811ff08530aSReinette Chatre 			ret = -EFAULT;
812ff08530aSReinette Chatre 			goto out_unlock;
813ff08530aSReinette Chatre 		}
814ff08530aSReinette Chatre 
815ff08530aSReinette Chatre 		mutex_unlock(&encl->lock);
816ff08530aSReinette Chatre 	}
817ff08530aSReinette Chatre 
818ff08530aSReinette Chatre 	ret = 0;
819ff08530aSReinette Chatre 	goto out;
820ff08530aSReinette Chatre 
821ff08530aSReinette Chatre out_unlock:
822ff08530aSReinette Chatre 	mutex_unlock(&encl->lock);
823ff08530aSReinette Chatre out:
824ff08530aSReinette Chatre 	modp->count = c;
825ff08530aSReinette Chatre 
826ff08530aSReinette Chatre 	return ret;
827ff08530aSReinette Chatre }
828ff08530aSReinette Chatre 
829ff08530aSReinette Chatre /**
830ff08530aSReinette Chatre  * sgx_ioc_enclave_restrict_permissions() - handler for
831ff08530aSReinette Chatre  *                                        %SGX_IOC_ENCLAVE_RESTRICT_PERMISSIONS
832ff08530aSReinette Chatre  * @encl:	an enclave pointer
833ff08530aSReinette Chatre  * @arg:	userspace pointer to a &struct sgx_enclave_restrict_permissions
834ff08530aSReinette Chatre  *		instance
835ff08530aSReinette Chatre  *
836ff08530aSReinette Chatre  * SGX2 distinguishes between relaxing and restricting the enclave page
837ff08530aSReinette Chatre  * permissions maintained by the hardware (EPCM permissions) of pages
838ff08530aSReinette Chatre  * belonging to an initialized enclave (after SGX_IOC_ENCLAVE_INIT).
839ff08530aSReinette Chatre  *
840ff08530aSReinette Chatre  * EPCM permissions cannot be restricted from within the enclave, the enclave
841ff08530aSReinette Chatre  * requires the kernel to run the privileged level 0 instructions ENCLS[EMODPR]
842ff08530aSReinette Chatre  * and ENCLS[ETRACK]. An attempt to relax EPCM permissions with this call
843ff08530aSReinette Chatre  * will be ignored by the hardware.
844ff08530aSReinette Chatre  *
845ff08530aSReinette Chatre  * Return:
846ff08530aSReinette Chatre  * - 0:		Success
847ff08530aSReinette Chatre  * - -errno:	Otherwise
848ff08530aSReinette Chatre  */
sgx_ioc_enclave_restrict_permissions(struct sgx_encl * encl,void __user * arg)849ff08530aSReinette Chatre static long sgx_ioc_enclave_restrict_permissions(struct sgx_encl *encl,
850ff08530aSReinette Chatre 						 void __user *arg)
851ff08530aSReinette Chatre {
852ff08530aSReinette Chatre 	struct sgx_enclave_restrict_permissions params;
853ff08530aSReinette Chatre 	long ret;
854ff08530aSReinette Chatre 
855ff08530aSReinette Chatre 	ret = sgx_ioc_sgx2_ready(encl);
856ff08530aSReinette Chatre 	if (ret)
857ff08530aSReinette Chatre 		return ret;
858ff08530aSReinette Chatre 
859ff08530aSReinette Chatre 	if (copy_from_user(&params, arg, sizeof(params)))
860ff08530aSReinette Chatre 		return -EFAULT;
861ff08530aSReinette Chatre 
862ff08530aSReinette Chatre 	if (sgx_validate_offset_length(encl, params.offset, params.length))
863ff08530aSReinette Chatre 		return -EINVAL;
864ff08530aSReinette Chatre 
865ff08530aSReinette Chatre 	if (params.permissions & ~SGX_SECINFO_PERMISSION_MASK)
866ff08530aSReinette Chatre 		return -EINVAL;
867ff08530aSReinette Chatre 
868ff08530aSReinette Chatre 	/*
869ff08530aSReinette Chatre 	 * Fail early if invalid permissions requested to prevent ENCLS[EMODPR]
870ff08530aSReinette Chatre 	 * from faulting later when the CPU does the same check.
871ff08530aSReinette Chatre 	 */
872ff08530aSReinette Chatre 	if ((params.permissions & SGX_SECINFO_W) &&
873ff08530aSReinette Chatre 	    !(params.permissions & SGX_SECINFO_R))
874ff08530aSReinette Chatre 		return -EINVAL;
875ff08530aSReinette Chatre 
876ff08530aSReinette Chatre 	if (params.result || params.count)
877ff08530aSReinette Chatre 		return -EINVAL;
878ff08530aSReinette Chatre 
879ff08530aSReinette Chatre 	ret = sgx_enclave_restrict_permissions(encl, &params);
880ff08530aSReinette Chatre 
881ff08530aSReinette Chatre 	if (copy_to_user(arg, &params, sizeof(params)))
882ff08530aSReinette Chatre 		return -EFAULT;
883ff08530aSReinette Chatre 
884ff08530aSReinette Chatre 	return ret;
885ff08530aSReinette Chatre }
886ff08530aSReinette Chatre 
88745d546b8SReinette Chatre /**
88845d546b8SReinette Chatre  * sgx_enclave_modify_types() - Modify type of SGX enclave pages
88945d546b8SReinette Chatre  * @encl:	Enclave to which the pages belong.
89045d546b8SReinette Chatre  * @modt:	Checked parameters from user about which pages need modifying
89145d546b8SReinette Chatre  *              and their new page type.
89245d546b8SReinette Chatre  *
89345d546b8SReinette Chatre  * Return:
89445d546b8SReinette Chatre  * - 0:		Success
89545d546b8SReinette Chatre  * - -errno:	Otherwise
89645d546b8SReinette Chatre  */
sgx_enclave_modify_types(struct sgx_encl * encl,struct sgx_enclave_modify_types * modt)89745d546b8SReinette Chatre static long sgx_enclave_modify_types(struct sgx_encl *encl,
89845d546b8SReinette Chatre 				     struct sgx_enclave_modify_types *modt)
89945d546b8SReinette Chatre {
90045d546b8SReinette Chatre 	unsigned long max_prot_restore;
90145d546b8SReinette Chatre 	enum sgx_page_type page_type;
90245d546b8SReinette Chatre 	struct sgx_encl_page *entry;
90345d546b8SReinette Chatre 	struct sgx_secinfo secinfo;
90445d546b8SReinette Chatre 	unsigned long prot;
90545d546b8SReinette Chatre 	unsigned long addr;
90645d546b8SReinette Chatre 	unsigned long c;
90745d546b8SReinette Chatre 	void *epc_virt;
90845d546b8SReinette Chatre 	int ret;
90945d546b8SReinette Chatre 
91045d546b8SReinette Chatre 	page_type = modt->page_type & SGX_PAGE_TYPE_MASK;
91145d546b8SReinette Chatre 
91245d546b8SReinette Chatre 	/*
91345d546b8SReinette Chatre 	 * The only new page types allowed by hardware are PT_TCS and PT_TRIM.
91445d546b8SReinette Chatre 	 */
91545d546b8SReinette Chatre 	if (page_type != SGX_PAGE_TYPE_TCS && page_type != SGX_PAGE_TYPE_TRIM)
91645d546b8SReinette Chatre 		return -EINVAL;
91745d546b8SReinette Chatre 
91845d546b8SReinette Chatre 	memset(&secinfo, 0, sizeof(secinfo));
91945d546b8SReinette Chatre 
92045d546b8SReinette Chatre 	secinfo.flags = page_type << 8;
92145d546b8SReinette Chatre 
92245d546b8SReinette Chatre 	for (c = 0 ; c < modt->length; c += PAGE_SIZE) {
92345d546b8SReinette Chatre 		addr = encl->base + modt->offset + c;
92445d546b8SReinette Chatre 
925a0506b3bSReinette Chatre 		sgx_reclaim_direct();
926a0506b3bSReinette Chatre 
92745d546b8SReinette Chatre 		mutex_lock(&encl->lock);
92845d546b8SReinette Chatre 
92945d546b8SReinette Chatre 		entry = sgx_encl_load_page(encl, addr);
93045d546b8SReinette Chatre 		if (IS_ERR(entry)) {
93145d546b8SReinette Chatre 			ret = PTR_ERR(entry) == -EBUSY ? -EAGAIN : -EFAULT;
93245d546b8SReinette Chatre 			goto out_unlock;
93345d546b8SReinette Chatre 		}
93445d546b8SReinette Chatre 
93545d546b8SReinette Chatre 		/*
93645d546b8SReinette Chatre 		 * Borrow the logic from the Intel SDM. Regular pages
93745d546b8SReinette Chatre 		 * (SGX_PAGE_TYPE_REG) can change type to SGX_PAGE_TYPE_TCS
93845d546b8SReinette Chatre 		 * or SGX_PAGE_TYPE_TRIM but TCS pages can only be trimmed.
93945d546b8SReinette Chatre 		 * CET pages not supported yet.
94045d546b8SReinette Chatre 		 */
94145d546b8SReinette Chatre 		if (!(entry->type == SGX_PAGE_TYPE_REG ||
94245d546b8SReinette Chatre 		      (entry->type == SGX_PAGE_TYPE_TCS &&
94345d546b8SReinette Chatre 		       page_type == SGX_PAGE_TYPE_TRIM))) {
94445d546b8SReinette Chatre 			ret = -EINVAL;
94545d546b8SReinette Chatre 			goto out_unlock;
94645d546b8SReinette Chatre 		}
94745d546b8SReinette Chatre 
94845d546b8SReinette Chatre 		max_prot_restore = entry->vm_max_prot_bits;
94945d546b8SReinette Chatre 
95045d546b8SReinette Chatre 		/*
95145d546b8SReinette Chatre 		 * Once a regular page becomes a TCS page it cannot be
95245d546b8SReinette Chatre 		 * changed back. So the maximum allowed protection reflects
95345d546b8SReinette Chatre 		 * the TCS page that is always RW from kernel perspective but
95445d546b8SReinette Chatre 		 * will be inaccessible from within enclave. Before doing
95545d546b8SReinette Chatre 		 * so, do make sure that the new page type continues to
95645d546b8SReinette Chatre 		 * respect the originally vetted page permissions.
95745d546b8SReinette Chatre 		 */
95845d546b8SReinette Chatre 		if (entry->type == SGX_PAGE_TYPE_REG &&
95945d546b8SReinette Chatre 		    page_type == SGX_PAGE_TYPE_TCS) {
96045d546b8SReinette Chatre 			if (~entry->vm_max_prot_bits & (VM_READ | VM_WRITE)) {
96145d546b8SReinette Chatre 				ret = -EPERM;
96245d546b8SReinette Chatre 				goto out_unlock;
96345d546b8SReinette Chatre 			}
96445d546b8SReinette Chatre 			prot = PROT_READ | PROT_WRITE;
96545d546b8SReinette Chatre 			entry->vm_max_prot_bits = calc_vm_prot_bits(prot, 0);
96645d546b8SReinette Chatre 
96745d546b8SReinette Chatre 			/*
96845d546b8SReinette Chatre 			 * Prevent page from being reclaimed while mutex
96945d546b8SReinette Chatre 			 * is released.
97045d546b8SReinette Chatre 			 */
97145d546b8SReinette Chatre 			if (sgx_unmark_page_reclaimable(entry->epc_page)) {
97245d546b8SReinette Chatre 				ret = -EAGAIN;
97345d546b8SReinette Chatre 				goto out_entry_changed;
97445d546b8SReinette Chatre 			}
97545d546b8SReinette Chatre 
97645d546b8SReinette Chatre 			/*
97745d546b8SReinette Chatre 			 * Do not keep encl->lock because of dependency on
97845d546b8SReinette Chatre 			 * mmap_lock acquired in sgx_zap_enclave_ptes().
97945d546b8SReinette Chatre 			 */
98045d546b8SReinette Chatre 			mutex_unlock(&encl->lock);
98145d546b8SReinette Chatre 
98245d546b8SReinette Chatre 			sgx_zap_enclave_ptes(encl, addr);
98345d546b8SReinette Chatre 
98445d546b8SReinette Chatre 			mutex_lock(&encl->lock);
98545d546b8SReinette Chatre 
98645d546b8SReinette Chatre 			sgx_mark_page_reclaimable(entry->epc_page);
98745d546b8SReinette Chatre 		}
98845d546b8SReinette Chatre 
98945d546b8SReinette Chatre 		/* Change EPC type */
99045d546b8SReinette Chatre 		epc_virt = sgx_get_epc_virt_addr(entry->epc_page);
99145d546b8SReinette Chatre 		ret = __emodt(&secinfo, epc_virt);
99245d546b8SReinette Chatre 		if (encls_faulted(ret)) {
99345d546b8SReinette Chatre 			/*
99445d546b8SReinette Chatre 			 * All possible faults should be avoidable:
99545d546b8SReinette Chatre 			 * parameters have been checked, will only change
99645d546b8SReinette Chatre 			 * valid page types, and no concurrent
99745d546b8SReinette Chatre 			 * SGX1/SGX2 ENCLS instructions since these are
99845d546b8SReinette Chatre 			 * protected with mutex.
99945d546b8SReinette Chatre 			 */
100045d546b8SReinette Chatre 			pr_err_once("EMODT encountered exception %d\n",
100145d546b8SReinette Chatre 				    ENCLS_TRAPNR(ret));
100245d546b8SReinette Chatre 			ret = -EFAULT;
100345d546b8SReinette Chatre 			goto out_entry_changed;
100445d546b8SReinette Chatre 		}
100545d546b8SReinette Chatre 		if (encls_failed(ret)) {
100645d546b8SReinette Chatre 			modt->result = ret;
100745d546b8SReinette Chatre 			ret = -EFAULT;
100845d546b8SReinette Chatre 			goto out_entry_changed;
100945d546b8SReinette Chatre 		}
101045d546b8SReinette Chatre 
101145d546b8SReinette Chatre 		ret = sgx_enclave_etrack(encl);
101245d546b8SReinette Chatre 		if (ret) {
101345d546b8SReinette Chatre 			ret = -EFAULT;
101445d546b8SReinette Chatre 			goto out_unlock;
101545d546b8SReinette Chatre 		}
101645d546b8SReinette Chatre 
101745d546b8SReinette Chatre 		entry->type = page_type;
101845d546b8SReinette Chatre 
101945d546b8SReinette Chatre 		mutex_unlock(&encl->lock);
102045d546b8SReinette Chatre 	}
102145d546b8SReinette Chatre 
102245d546b8SReinette Chatre 	ret = 0;
102345d546b8SReinette Chatre 	goto out;
102445d546b8SReinette Chatre 
102545d546b8SReinette Chatre out_entry_changed:
102645d546b8SReinette Chatre 	entry->vm_max_prot_bits = max_prot_restore;
102745d546b8SReinette Chatre out_unlock:
102845d546b8SReinette Chatre 	mutex_unlock(&encl->lock);
102945d546b8SReinette Chatre out:
103045d546b8SReinette Chatre 	modt->count = c;
103145d546b8SReinette Chatre 
103245d546b8SReinette Chatre 	return ret;
103345d546b8SReinette Chatre }
103445d546b8SReinette Chatre 
103545d546b8SReinette Chatre /**
103645d546b8SReinette Chatre  * sgx_ioc_enclave_modify_types() - handler for %SGX_IOC_ENCLAVE_MODIFY_TYPES
103745d546b8SReinette Chatre  * @encl:	an enclave pointer
103845d546b8SReinette Chatre  * @arg:	userspace pointer to a &struct sgx_enclave_modify_types instance
103945d546b8SReinette Chatre  *
104045d546b8SReinette Chatre  * Ability to change the enclave page type supports the following use cases:
104145d546b8SReinette Chatre  *
104245d546b8SReinette Chatre  * * It is possible to add TCS pages to an enclave by changing the type of
104345d546b8SReinette Chatre  *   regular pages (%SGX_PAGE_TYPE_REG) to TCS (%SGX_PAGE_TYPE_TCS) pages.
104445d546b8SReinette Chatre  *   With this support the number of threads supported by an initialized
104545d546b8SReinette Chatre  *   enclave can be increased dynamically.
104645d546b8SReinette Chatre  *
104745d546b8SReinette Chatre  * * Regular or TCS pages can dynamically be removed from an initialized
104845d546b8SReinette Chatre  *   enclave by changing the page type to %SGX_PAGE_TYPE_TRIM. Changing the
104945d546b8SReinette Chatre  *   page type to %SGX_PAGE_TYPE_TRIM marks the page for removal with actual
105045d546b8SReinette Chatre  *   removal done by handler of %SGX_IOC_ENCLAVE_REMOVE_PAGES ioctl() called
105145d546b8SReinette Chatre  *   after ENCLU[EACCEPT] is run on %SGX_PAGE_TYPE_TRIM page from within the
105245d546b8SReinette Chatre  *   enclave.
105345d546b8SReinette Chatre  *
105445d546b8SReinette Chatre  * Return:
105545d546b8SReinette Chatre  * - 0:		Success
105645d546b8SReinette Chatre  * - -errno:	Otherwise
105745d546b8SReinette Chatre  */
sgx_ioc_enclave_modify_types(struct sgx_encl * encl,void __user * arg)105845d546b8SReinette Chatre static long sgx_ioc_enclave_modify_types(struct sgx_encl *encl,
105945d546b8SReinette Chatre 					 void __user *arg)
106045d546b8SReinette Chatre {
106145d546b8SReinette Chatre 	struct sgx_enclave_modify_types params;
106245d546b8SReinette Chatre 	long ret;
106345d546b8SReinette Chatre 
106445d546b8SReinette Chatre 	ret = sgx_ioc_sgx2_ready(encl);
106545d546b8SReinette Chatre 	if (ret)
106645d546b8SReinette Chatre 		return ret;
106745d546b8SReinette Chatre 
106845d546b8SReinette Chatre 	if (copy_from_user(&params, arg, sizeof(params)))
106945d546b8SReinette Chatre 		return -EFAULT;
107045d546b8SReinette Chatre 
107145d546b8SReinette Chatre 	if (sgx_validate_offset_length(encl, params.offset, params.length))
107245d546b8SReinette Chatre 		return -EINVAL;
107345d546b8SReinette Chatre 
107445d546b8SReinette Chatre 	if (params.page_type & ~SGX_PAGE_TYPE_MASK)
107545d546b8SReinette Chatre 		return -EINVAL;
107645d546b8SReinette Chatre 
107745d546b8SReinette Chatre 	if (params.result || params.count)
107845d546b8SReinette Chatre 		return -EINVAL;
107945d546b8SReinette Chatre 
108045d546b8SReinette Chatre 	ret = sgx_enclave_modify_types(encl, &params);
108145d546b8SReinette Chatre 
108245d546b8SReinette Chatre 	if (copy_to_user(arg, &params, sizeof(params)))
108345d546b8SReinette Chatre 		return -EFAULT;
108445d546b8SReinette Chatre 
108545d546b8SReinette Chatre 	return ret;
108645d546b8SReinette Chatre }
108745d546b8SReinette Chatre 
10889849bb27SReinette Chatre /**
10899849bb27SReinette Chatre  * sgx_encl_remove_pages() - Remove trimmed pages from SGX enclave
10909849bb27SReinette Chatre  * @encl:	Enclave to which the pages belong
10919849bb27SReinette Chatre  * @params:	Checked parameters from user on which pages need to be removed
10929849bb27SReinette Chatre  *
10939849bb27SReinette Chatre  * Return:
10949849bb27SReinette Chatre  * - 0:		Success.
10959849bb27SReinette Chatre  * - -errno:	Otherwise.
10969849bb27SReinette Chatre  */
sgx_encl_remove_pages(struct sgx_encl * encl,struct sgx_enclave_remove_pages * params)10979849bb27SReinette Chatre static long sgx_encl_remove_pages(struct sgx_encl *encl,
10989849bb27SReinette Chatre 				  struct sgx_enclave_remove_pages *params)
10999849bb27SReinette Chatre {
11009849bb27SReinette Chatre 	struct sgx_encl_page *entry;
11019849bb27SReinette Chatre 	struct sgx_secinfo secinfo;
11029849bb27SReinette Chatre 	unsigned long addr;
11039849bb27SReinette Chatre 	unsigned long c;
11049849bb27SReinette Chatre 	void *epc_virt;
11059849bb27SReinette Chatre 	int ret;
11069849bb27SReinette Chatre 
11079849bb27SReinette Chatre 	memset(&secinfo, 0, sizeof(secinfo));
11089849bb27SReinette Chatre 	secinfo.flags = SGX_SECINFO_R | SGX_SECINFO_W | SGX_SECINFO_X;
11099849bb27SReinette Chatre 
11109849bb27SReinette Chatre 	for (c = 0 ; c < params->length; c += PAGE_SIZE) {
11119849bb27SReinette Chatre 		addr = encl->base + params->offset + c;
11129849bb27SReinette Chatre 
1113a0506b3bSReinette Chatre 		sgx_reclaim_direct();
1114a0506b3bSReinette Chatre 
11159849bb27SReinette Chatre 		mutex_lock(&encl->lock);
11169849bb27SReinette Chatre 
11179849bb27SReinette Chatre 		entry = sgx_encl_load_page(encl, addr);
11189849bb27SReinette Chatre 		if (IS_ERR(entry)) {
11199849bb27SReinette Chatre 			ret = PTR_ERR(entry) == -EBUSY ? -EAGAIN : -EFAULT;
11209849bb27SReinette Chatre 			goto out_unlock;
11219849bb27SReinette Chatre 		}
11229849bb27SReinette Chatre 
11239849bb27SReinette Chatre 		if (entry->type != SGX_PAGE_TYPE_TRIM) {
11249849bb27SReinette Chatre 			ret = -EPERM;
11259849bb27SReinette Chatre 			goto out_unlock;
11269849bb27SReinette Chatre 		}
11279849bb27SReinette Chatre 
11289849bb27SReinette Chatre 		/*
11299849bb27SReinette Chatre 		 * ENCLS[EMODPR] is a no-op instruction used to inform if
11309849bb27SReinette Chatre 		 * ENCLU[EACCEPT] was run from within the enclave. If
11319849bb27SReinette Chatre 		 * ENCLS[EMODPR] is run with RWX on a trimmed page that is
11329849bb27SReinette Chatre 		 * not yet accepted then it will return
11339849bb27SReinette Chatre 		 * %SGX_PAGE_NOT_MODIFIABLE, after the trimmed page is
11349849bb27SReinette Chatre 		 * accepted the instruction will encounter a page fault.
11359849bb27SReinette Chatre 		 */
11369849bb27SReinette Chatre 		epc_virt = sgx_get_epc_virt_addr(entry->epc_page);
11379849bb27SReinette Chatre 		ret = __emodpr(&secinfo, epc_virt);
11389849bb27SReinette Chatre 		if (!encls_faulted(ret) || ENCLS_TRAPNR(ret) != X86_TRAP_PF) {
11399849bb27SReinette Chatre 			ret = -EPERM;
11409849bb27SReinette Chatre 			goto out_unlock;
11419849bb27SReinette Chatre 		}
11429849bb27SReinette Chatre 
11439849bb27SReinette Chatre 		if (sgx_unmark_page_reclaimable(entry->epc_page)) {
11449849bb27SReinette Chatre 			ret = -EBUSY;
11459849bb27SReinette Chatre 			goto out_unlock;
11469849bb27SReinette Chatre 		}
11479849bb27SReinette Chatre 
11489849bb27SReinette Chatre 		/*
11499849bb27SReinette Chatre 		 * Do not keep encl->lock because of dependency on
11509849bb27SReinette Chatre 		 * mmap_lock acquired in sgx_zap_enclave_ptes().
11519849bb27SReinette Chatre 		 */
11529849bb27SReinette Chatre 		mutex_unlock(&encl->lock);
11539849bb27SReinette Chatre 
11549849bb27SReinette Chatre 		sgx_zap_enclave_ptes(encl, addr);
11559849bb27SReinette Chatre 
11569849bb27SReinette Chatre 		mutex_lock(&encl->lock);
11579849bb27SReinette Chatre 
11589849bb27SReinette Chatre 		sgx_encl_free_epc_page(entry->epc_page);
11599849bb27SReinette Chatre 		encl->secs_child_cnt--;
11609849bb27SReinette Chatre 		entry->epc_page = NULL;
11619849bb27SReinette Chatre 		xa_erase(&encl->page_array, PFN_DOWN(entry->desc));
11629849bb27SReinette Chatre 		sgx_encl_shrink(encl, NULL);
11639849bb27SReinette Chatre 		kfree(entry);
11649849bb27SReinette Chatre 
11659849bb27SReinette Chatre 		mutex_unlock(&encl->lock);
11669849bb27SReinette Chatre 	}
11679849bb27SReinette Chatre 
11689849bb27SReinette Chatre 	ret = 0;
11699849bb27SReinette Chatre 	goto out;
11709849bb27SReinette Chatre 
11719849bb27SReinette Chatre out_unlock:
11729849bb27SReinette Chatre 	mutex_unlock(&encl->lock);
11739849bb27SReinette Chatre out:
11749849bb27SReinette Chatre 	params->count = c;
11759849bb27SReinette Chatre 
11769849bb27SReinette Chatre 	return ret;
11779849bb27SReinette Chatre }
11789849bb27SReinette Chatre 
11799849bb27SReinette Chatre /**
11809849bb27SReinette Chatre  * sgx_ioc_enclave_remove_pages() - handler for %SGX_IOC_ENCLAVE_REMOVE_PAGES
11819849bb27SReinette Chatre  * @encl:	an enclave pointer
11829849bb27SReinette Chatre  * @arg:	userspace pointer to &struct sgx_enclave_remove_pages instance
11839849bb27SReinette Chatre  *
11849849bb27SReinette Chatre  * Final step of the flow removing pages from an initialized enclave. The
11859849bb27SReinette Chatre  * complete flow is:
11869849bb27SReinette Chatre  *
11879849bb27SReinette Chatre  * 1) User changes the type of the pages to be removed to %SGX_PAGE_TYPE_TRIM
11889849bb27SReinette Chatre  *    using the %SGX_IOC_ENCLAVE_MODIFY_TYPES ioctl().
11899849bb27SReinette Chatre  * 2) User approves the page removal by running ENCLU[EACCEPT] from within
11909849bb27SReinette Chatre  *    the enclave.
11919849bb27SReinette Chatre  * 3) User initiates actual page removal using the
11929849bb27SReinette Chatre  *    %SGX_IOC_ENCLAVE_REMOVE_PAGES ioctl() that is handled here.
11939849bb27SReinette Chatre  *
11949849bb27SReinette Chatre  * First remove any page table entries pointing to the page and then proceed
11959849bb27SReinette Chatre  * with the actual removal of the enclave page and data in support of it.
11969849bb27SReinette Chatre  *
11979849bb27SReinette Chatre  * VA pages are not affected by this removal. It is thus possible that the
11989849bb27SReinette Chatre  * enclave may end up with more VA pages than needed to support all its
11999849bb27SReinette Chatre  * pages.
12009849bb27SReinette Chatre  *
12019849bb27SReinette Chatre  * Return:
12029849bb27SReinette Chatre  * - 0:		Success
12039849bb27SReinette Chatre  * - -errno:	Otherwise
12049849bb27SReinette Chatre  */
sgx_ioc_enclave_remove_pages(struct sgx_encl * encl,void __user * arg)12059849bb27SReinette Chatre static long sgx_ioc_enclave_remove_pages(struct sgx_encl *encl,
12069849bb27SReinette Chatre 					 void __user *arg)
12079849bb27SReinette Chatre {
12089849bb27SReinette Chatre 	struct sgx_enclave_remove_pages params;
12099849bb27SReinette Chatre 	long ret;
12109849bb27SReinette Chatre 
12119849bb27SReinette Chatre 	ret = sgx_ioc_sgx2_ready(encl);
12129849bb27SReinette Chatre 	if (ret)
12139849bb27SReinette Chatre 		return ret;
12149849bb27SReinette Chatre 
12159849bb27SReinette Chatre 	if (copy_from_user(&params, arg, sizeof(params)))
12169849bb27SReinette Chatre 		return -EFAULT;
12179849bb27SReinette Chatre 
12189849bb27SReinette Chatre 	if (sgx_validate_offset_length(encl, params.offset, params.length))
12199849bb27SReinette Chatre 		return -EINVAL;
12209849bb27SReinette Chatre 
12219849bb27SReinette Chatre 	if (params.count)
12229849bb27SReinette Chatre 		return -EINVAL;
12239849bb27SReinette Chatre 
12249849bb27SReinette Chatre 	ret = sgx_encl_remove_pages(encl, &params);
12259849bb27SReinette Chatre 
12269849bb27SReinette Chatre 	if (copy_to_user(arg, &params, sizeof(params)))
12279849bb27SReinette Chatre 		return -EFAULT;
12289849bb27SReinette Chatre 
12299849bb27SReinette Chatre 	return ret;
12309849bb27SReinette Chatre }
12319849bb27SReinette Chatre 
sgx_ioctl(struct file * filep,unsigned int cmd,unsigned long arg)1232888d2491SJarkko Sakkinen long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
1233888d2491SJarkko Sakkinen {
1234888d2491SJarkko Sakkinen 	struct sgx_encl *encl = filep->private_data;
1235888d2491SJarkko Sakkinen 	int ret;
1236888d2491SJarkko Sakkinen 
1237888d2491SJarkko Sakkinen 	if (test_and_set_bit(SGX_ENCL_IOCTL, &encl->flags))
1238888d2491SJarkko Sakkinen 		return -EBUSY;
1239888d2491SJarkko Sakkinen 
1240888d2491SJarkko Sakkinen 	switch (cmd) {
1241888d2491SJarkko Sakkinen 	case SGX_IOC_ENCLAVE_CREATE:
1242888d2491SJarkko Sakkinen 		ret = sgx_ioc_enclave_create(encl, (void __user *)arg);
1243888d2491SJarkko Sakkinen 		break;
1244c6d26d37SJarkko Sakkinen 	case SGX_IOC_ENCLAVE_ADD_PAGES:
1245c6d26d37SJarkko Sakkinen 		ret = sgx_ioc_enclave_add_pages(encl, (void __user *)arg);
1246c6d26d37SJarkko Sakkinen 		break;
12479d0c151bSJarkko Sakkinen 	case SGX_IOC_ENCLAVE_INIT:
12489d0c151bSJarkko Sakkinen 		ret = sgx_ioc_enclave_init(encl, (void __user *)arg);
12499d0c151bSJarkko Sakkinen 		break;
1250c82c6186SJarkko Sakkinen 	case SGX_IOC_ENCLAVE_PROVISION:
1251c82c6186SJarkko Sakkinen 		ret = sgx_ioc_enclave_provision(encl, (void __user *)arg);
1252c82c6186SJarkko Sakkinen 		break;
1253ff08530aSReinette Chatre 	case SGX_IOC_ENCLAVE_RESTRICT_PERMISSIONS:
1254ff08530aSReinette Chatre 		ret = sgx_ioc_enclave_restrict_permissions(encl,
1255ff08530aSReinette Chatre 							   (void __user *)arg);
1256ff08530aSReinette Chatre 		break;
125745d546b8SReinette Chatre 	case SGX_IOC_ENCLAVE_MODIFY_TYPES:
125845d546b8SReinette Chatre 		ret = sgx_ioc_enclave_modify_types(encl, (void __user *)arg);
125945d546b8SReinette Chatre 		break;
12609849bb27SReinette Chatre 	case SGX_IOC_ENCLAVE_REMOVE_PAGES:
12619849bb27SReinette Chatre 		ret = sgx_ioc_enclave_remove_pages(encl, (void __user *)arg);
12629849bb27SReinette Chatre 		break;
1263888d2491SJarkko Sakkinen 	default:
1264888d2491SJarkko Sakkinen 		ret = -ENOIOCTLCMD;
1265888d2491SJarkko Sakkinen 		break;
1266888d2491SJarkko Sakkinen 	}
1267888d2491SJarkko Sakkinen 
1268888d2491SJarkko Sakkinen 	clear_bit(SGX_ENCL_IOCTL, &encl->flags);
1269888d2491SJarkko Sakkinen 	return ret;
1270888d2491SJarkko Sakkinen }
1271