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(¶ms, 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(¶ms, 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, ¶ms);
880ff08530aSReinette Chatre
881ff08530aSReinette Chatre if (copy_to_user(arg, ¶ms, 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(¶ms, 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, ¶ms);
108145d546b8SReinette Chatre
108245d546b8SReinette Chatre if (copy_to_user(arg, ¶ms, 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(¶ms, 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, ¶ms);
12259849bb27SReinette Chatre
12269849bb27SReinette Chatre if (copy_to_user(arg, ¶ms, 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