diff --git a/Makefile b/Makefile index c789fb7..896d397 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,10 @@ ifneq ($(KERNELRELEASE),) isgx-y := \ - isgx_main.o \ - isgx_page_cache.o \ - isgx_ioctl.o \ - isgx_vma.o \ - isgx_util.o + sgx_main.o \ + sgx_page_cache.o \ + sgx_ioctl.o \ + sgx_vma.o \ + sgx_util.o obj-m += isgx.o else KDIR := /lib/modules/$(shell uname -r)/build diff --git a/isgx.h b/isgx.h deleted file mode 100644 index 4f82427..0000000 --- a/isgx.h +++ /dev/null @@ -1,238 +0,0 @@ -/* - * (C) Copyright 2015 Intel Corporation - * - * Authors: - * - * Jarkko Sakkinen - * Suresh Siddha - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 - * of the License. - */ - -#ifndef __ARCH_ISGX_H__ -#define __ARCH_ISGX_H__ - -#include "isgx_user.h" -#include "isgx_arch.h" -#include -#include -#include -#include -#include -#include - -/* Number of times to spin before going to sleep because of an interrupt - * storm. - */ -#define EINIT_SPIN_COUNT 20 - -/* Number of tries in total before giving up with EINIT. During each try - * EINIT is called the number of times specified by EINIT_SPINT_COUNT. - */ -#define EINIT_TRY_COUNT 50 - -/* Time to sleep between each try. */ -#define EINIT_BACKOFF_TIME 20 - -#define ISGX_ENCLAVE_PAGE_TCS 0x1 -#define ISGX_ENCLAVE_PAGE_RESERVED 0x2 - -struct isgx_epc_page { - resource_size_t pa; - struct list_head free_list; -}; - -#define ISGX_VA_SLOT_COUNT 512 - -struct isgx_va_page { - struct isgx_epc_page *epc_page; - DECLARE_BITMAP(slots, ISGX_VA_SLOT_COUNT); - struct list_head list; -}; - -/** - * isgx_alloc_va_slot() - allocate VA slot from a VA page - * - * @page: VA page - * - * Returns offset to a free VA slot. If there are no free slots, an offset of - * PAGE_SIZE is returned. - */ -static inline unsigned int isgx_alloc_va_slot(struct isgx_va_page *page) -{ - int slot = find_first_zero_bit(page->slots, ISGX_VA_SLOT_COUNT); - - if (slot < ISGX_VA_SLOT_COUNT) - set_bit(slot, page->slots); - - return slot << 3; -} - -/** - * isgx_free_va_slot() - free VA slot from a VA page - * - * @page: VA page - * @offset: the offset of the VA slot - * - * Releases VA slot. - */ -static inline void isgx_free_va_slot(struct isgx_va_page *page, - unsigned int offset) -{ - clear_bit(offset >> 3, page->slots); -} - -struct isgx_enclave_page { - unsigned long addr; - unsigned int flags; - struct isgx_epc_page *epc_page; - struct list_head load_list; - struct isgx_va_page *va_page; - unsigned int va_offset; - struct pcmd pcmd; - struct rb_node node; -}; - -#define ISGX_ENCLAVE_INITIALIZED 0x01 -#define ISGX_ENCLAVE_DEBUG 0x02 -#define ISGX_ENCLAVE_SECS_EVICTED 0x04 -#define ISGX_ENCLAVE_SUSPEND 0x08 - -struct isgx_vma { - struct vm_area_struct *vma; - struct list_head vma_list; -}; - -struct isgx_tgid_ctx { - struct pid *tgid; - atomic_t epc_cnt; - struct kref refcount; - struct list_head enclave_list; - struct list_head list; -}; - -struct isgx_enclave { - unsigned int flags; - struct task_struct *owner; - struct mm_struct *mm; - struct file *backing; - struct list_head vma_list; - struct list_head load_list; - struct kref refcount; - struct mutex lock; - unsigned long base; - unsigned long size; - struct list_head va_pages; - struct rb_root enclave_rb; - struct list_head add_page_reqs; - struct work_struct add_page_work; - unsigned int secs_child_cnt; - struct isgx_enclave_page secs_page; - struct isgx_tgid_ctx *tgid_ctx; - struct list_head enclave_list; -}; - -extern struct workqueue_struct *isgx_add_page_wq; -extern resource_size_t isgx_epc_base; -extern unsigned long isgx_epc_size; -#ifdef CONFIG_X86_64 -extern void *isgx_epc_mem; -#endif -extern u64 isgx_enclave_size_max_32; -extern u64 isgx_enclave_size_max_64; -extern u64 isgx_xfrm_mask; -extern u32 isgx_ssaframesize_tbl[64]; - -extern struct vm_operations_struct isgx_vm_ops; -extern atomic_t isgx_nr_pids; - -/* Message macros */ -#define isgx_dbg(encl, fmt, ...) \ - pr_debug_ratelimited("isgx: [%d:0x%p] " fmt, \ - pid_nr((encl)->tgid_ctx->tgid), \ - (void *)(encl)->base, ##__VA_ARGS__) -#define isgx_info(encl, fmt, ...) \ - pr_info_ratelimited("isgx: [%d:0x%p] " fmt, \ - pid_nr((encl)->tgid_ctx->tgid), \ - (void *)(encl)->base, ##__VA_ARGS__) -#define isgx_warn(encl, fmt, ...) \ - pr_warn_ratelimited("isgx: [%d:0x%p] " fmt, \ - pid_nr((encl)->tgid_ctx->tgid), \ - (void *)(encl)->base, ##__VA_ARGS__) -#define isgx_err(encl, fmt, ...) \ - pr_err_ratelimited("isgx: [%d:0x%p] " fmt, \ - pid_nr((encl)->tgid_ctx->tgid), \ - (void *)(encl)->base, ##__VA_ARGS__) - -/* - * Ioctl subsystem. - */ - -long isgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); -void isgx_add_page_worker(struct work_struct *work); - -/* - * Utility functions - */ - -void *isgx_get_epc_page(struct isgx_epc_page *entry); -void isgx_put_epc_page(void *epc_page_vaddr); -struct page *isgx_get_backing_page(struct isgx_enclave* enclave, - struct isgx_enclave_page* entry, - bool write); -void isgx_insert_pte(struct isgx_enclave *enclave, - struct isgx_enclave_page *enclave_page, - struct isgx_epc_page *epc_page, - struct vm_area_struct *vma); -int isgx_eremove(struct isgx_epc_page *epc_page); -int isgx_test_and_clear_young(struct isgx_enclave *enclave, - unsigned long addr); -struct isgx_vma *isgx_find_vma(struct isgx_enclave *enclave, - unsigned long addr); -void isgx_zap_tcs_ptes(struct isgx_enclave *enclave, - struct vm_area_struct *vma); -bool isgx_pin_mm(struct isgx_enclave *encl); -void isgx_unpin_mm(struct isgx_enclave *encl); -void isgx_invalidate(struct isgx_enclave *encl); -int isgx_find_enclave(struct mm_struct *mm, unsigned long addr, - struct vm_area_struct **vma); -struct isgx_enclave_page *isgx_enclave_find_page(struct isgx_enclave *enclave, - unsigned long enclave_la); -void isgx_enclave_release(struct kref *ref); -void release_tgid_ctx(struct kref *ref); - -/* - * Page cache subsystem. - */ - -#define ISGX_NR_LOW_EPC_PAGES_DEFAULT 32 -#define ISGX_NR_SWAP_CLUSTER_MAX 16 - -extern struct mutex isgx_tgid_ctx_mutex; -extern struct list_head isgx_tgid_ctx_list; -extern unsigned int isgx_nr_total_epc_pages; -extern unsigned int isgx_nr_free_epc_pages; -extern unsigned int isgx_nr_low_epc_pages; -extern struct task_struct *kisgxswapd_tsk; - -enum isgx_alloc_flags { - ISGX_ALLOC_ATOMIC = BIT(0), -}; - -enum isgx_free_flags { - ISGX_FREE_EREMOVE = BIT(0), -}; - -int kisgxswapd(void *p); -int isgx_page_cache_init(resource_size_t start, unsigned long size); -void isgx_page_cache_teardown(void); -struct isgx_epc_page *isgx_alloc_epc_page( - struct isgx_tgid_ctx *tgid_epc_cnt, unsigned int flags); -void isgx_free_epc_page(struct isgx_epc_page *entry, - struct isgx_enclave *encl, - unsigned int flags); - -#endif /* __ARCH_X86_ISGX_H__ */ diff --git a/isgx_arch.h b/isgx_arch.h deleted file mode 100644 index 23edfa2..0000000 --- a/isgx_arch.h +++ /dev/null @@ -1,283 +0,0 @@ -/* - * (C) Copyright 2015 Intel Corporation - * - * Authors: - * - * Jarkko Sakkinen - * Suresh Siddha - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 - * of the License. - */ - -#ifndef _X86_ISGX_ARCH_H -#define _X86_ISGX_ARCH_H - -#include -#include -#include - -#define MSR_ISGX_PRMRR 0x01f4 -#define SGX_CPUID 0x12 - -enum { - PAGE_TYPE_SECS, - PAGE_TYPE_TCS, - PAGE_TYPE_REG, - PAGE_TYPE_VA, - }; - -/* Workaround for kernel versions < 3.13 */ -#undef BIT_ULL -#undef GENMASK_ULL -#define BIT_ULL(nr) (1ULL << (nr)) -#define GENMASK_ULL(h, l) (((U64_C(1) << ((h) - (l) + 1)) - 1) << (l)) - -/* SECINFO flags */ -enum isgx_secinfo_flags { - SGX_SECINFO_R = 0x01, - SGX_SECINFO_W = 0x02, - SGX_SECINFO_X = 0x04, -}; - -/* SECINFO page types */ -enum isgx_secinfo_pt { - SGX_SECINFO_SECS = 0x000ULL, - SGX_SECINFO_TCS = 0x100ULL, - SGX_SECINFO_REG = 0x200ULL, -}; - -struct isgx_secinfo { - __u64 flags; - __u64 reserved[7]; -} __attribute__((aligned(128))); - -struct isgx_einittoken { - __u32 valid; - __u8 reserved1[206]; - __u16 isvsvnle; - __u8 reserved2[92]; -} __attribute__((aligned(512))); - -enum isgx_secs_attributes { - ISGX_SECS_A_DEBUG = BIT_ULL(1), - ISGX_SECS_A_MODE64BIT = BIT_ULL(2), - ISGX_SECS_A_PROVISION_KEY = BIT_ULL(4), - ISGX_SECS_A_LICENSE_KEY = BIT_ULL(5), - ISGX_SECS_A_RESERVED_MASK = (BIT_ULL(0) | - BIT_ULL(3) | - GENMASK_ULL(63, 6)), -}; - -#define ISGX_SECS_RESERVED1_SIZE 28 -#define ISGX_SECS_RESERVED2_SIZE 32 -#define ISGX_SECS_RESERVED3_SIZE 96 -#define ISGX_SECS_RESERVED4_SIZE 3836 - -struct isgx_secs { - u64 size; - u64 base; - u32 ssaframesize; - uint8_t reserved1[ISGX_SECS_RESERVED1_SIZE]; - u64 flags; - u64 xfrm; - u32 mrenclave[8]; - uint8_t reserved2[ISGX_SECS_RESERVED2_SIZE]; - u32 mrsigner[8]; - uint8_t reserved3[ISGX_SECS_RESERVED3_SIZE]; - u16 isvvprodid; - u16 isvsvn; - uint8_t reserved[ISGX_SECS_RESERVED4_SIZE]; -}; - -struct isgx_tcs { - u64 state; - u64 flags; - u64 ossa; - u32 cssa; - u32 nssa; - u64 oentry; - u64 aep; - u64 ofsbase; - u64 ogsbase; - u32 fslimit; - u32 gslimit; - u64 reserved[503]; -}; - -enum isgx_secifo_masks { - ISGX_SECINFO_PERMISSION_MASK = GENMASK_ULL(2, 0), - ISGX_SECINFO_PAGE_TYPE_MASK = GENMASK_ULL(15, 8), - ISGX_SECINFO_RESERVED_MASK = (GENMASK_ULL(7, 3) | - GENMASK_ULL(63, 16)), -}; - -struct pcmd { - struct isgx_secinfo secinfo; - u64 enclave_id; - u8 reserved[40]; - u8 mac[16]; -}; - -struct page_info { - u64 linaddr; - u64 srcpge; - union { - u64 secinfo; - u64 pcmd; - }; - u64 secs; -} __attribute__((aligned(32))); - -#define SIGSTRUCT_SIZE 1808 -#define EINITTOKEN_SIZE 304 - -enum { - ECREATE = 0x0, - EADD = 0x1, - EINIT = 0x2, - EREMOVE = 0x3, - EDGBRD = 0x4, - EDGBWR = 0x5, - EEXTEND = 0x6, - ELDU = 0x8, - EBLOCK = 0x9, - EPA = 0xA, - EWB = 0xB, - ETRACK = 0xC, -}; - -#define __encls_ret(rax, rbx, rcx, rdx) \ - ({ \ - int ret; \ - asm volatile("1: .byte 0x0f, 0x01, 0xcf;\n\t" \ - "2: \n" \ - ".section .fixup,\"ax\"\n" \ - "3: jmp 2b\n" \ - ".previous\n" \ - _ASM_EXTABLE(1b, 3b) \ - : "=a"(ret) \ - : "a"(rax), "b"(rbx), "c"(rcx), "d"(rdx) \ - : "memory"); \ - ret; \ - }) - -#ifdef CONFIG_X86_64 -#define __encls(rax, rbx, rcx, rdx...) \ - ({ \ - int ret; \ - asm volatile("1: .byte 0x0f, 0x01, 0xcf;\n\t" \ - " xor %%eax,%%eax;\n" \ - "2: \n" \ - ".section .fixup,\"ax\"\n" \ - "3: movq $-1,%%rax\n" \ - " jmp 2b\n" \ - ".previous\n" \ - _ASM_EXTABLE(1b, 3b) \ - : "=a"(ret), "=b"(rbx), "=c"(rcx) \ - : "a"(rax), "b"(rbx), "c"(rcx), rdx \ - : "memory"); \ - ret; \ - }) -#else -#define __encls(rax, rbx, rcx, rdx...) \ - ({ \ - int ret; \ - asm volatile("1: .byte 0x0f, 0x01, 0xcf;\n\t" \ - " xor %%eax,%%eax;\n" \ - "2: \n" \ - ".section .fixup,\"ax\"\n" \ - "3: mov $-1,%%eax\n" \ - " jmp 2b\n" \ - ".previous\n" \ - _ASM_EXTABLE(1b, 3b) \ - : "=a"(ret), "=b"(rbx), "=c"(rcx) \ - : "a"(rax), "b"(rbx), "c"(rcx), rdx \ - : "memory"); \ - ret; \ - }) -#endif - -static inline unsigned long __ecreate(struct page_info *pginfo, void *secs) -{ - return __encls(ECREATE, pginfo, secs, "d"(0)); -} - -static inline int __eextend(void *secs, void *epc) -{ - return __encls(EEXTEND, secs, epc, "d"(0)); -} - -static inline int __eadd(struct page_info *pginfo, void *epc) -{ - return __encls(EADD, pginfo, epc, "d"(0)); -} - -static inline int __einit(void *sigstruct, struct isgx_einittoken *einittoken, - void *secs) -{ - return __encls_ret(EINIT, sigstruct, secs, einittoken); -} - -static inline int __eremove(void *epc) -{ - unsigned long rbx = 0; - unsigned long rdx = 0; - - return __encls_ret(EREMOVE, rbx, epc, rdx); -} - -static inline int __edbgwr(void *epc, unsigned long *data) -{ - return __encls(EDGBWR, *data, epc, "d"(0)); -} - -static inline int __edbgrd(void *epc, unsigned long *data) -{ - unsigned long rbx = 0; - int ret; - - ret = __encls(EDGBRD, rbx, epc, "d"(0)); - if (!ret) - *(unsigned long *) data = rbx; - - return ret; -} - -static inline int __etrack(void *epc) -{ - unsigned long rbx = 0; - unsigned long rdx = 0; - - return __encls_ret(ETRACK, rbx, epc, rdx); -} - -static inline int __eldu(unsigned long rbx, unsigned long rcx, - unsigned long rdx) -{ - return __encls_ret(ELDU, rbx, rcx, rdx); -} - -static inline int __eblock(unsigned long rcx) -{ - unsigned long rbx = 0; - unsigned long rdx = 0; - - return __encls_ret(EBLOCK, rbx, rcx, rdx); -} - -static inline int __epa(void *epc) -{ - unsigned long rbx = PAGE_TYPE_VA; - - return __encls(EPA, rbx, epc, "d"(0)); -} - -static inline int __ewb(struct page_info *pginfo, void *epc, void *va) -{ - return __encls_ret(EWB, pginfo, epc, va); -} - -#endif /* _X86_ISGX_ARCH_H */ diff --git a/isgx_ioctl.c b/isgx_ioctl.c deleted file mode 100644 index 0ee0f32..0000000 --- a/isgx_ioctl.c +++ /dev/null @@ -1,872 +0,0 @@ -/* - * (C) Copyright 2015 Intel Corporation - * - * Authors: - * - * Jarkko Sakkinen - * Suresh Siddha - * Serge Ayoun - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 - * of the License. - */ - -#include "isgx.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct isgx_add_page_req { - struct isgx_enclave *enclave; - struct isgx_enclave_page *enclave_page; - struct isgx_secinfo secinfo; - u16 mrmask; - struct list_head list; -}; - -static u16 isgx_isvsvnle_min = 0; -atomic_t isgx_nr_pids = ATOMIC_INIT(0); - -static struct isgx_tgid_ctx *find_tgid_epc_cnt(struct pid *tgid) -{ - struct isgx_tgid_ctx *ctx; - - list_for_each_entry(ctx, &isgx_tgid_ctx_list, list) - if (pid_nr(ctx->tgid) == pid_nr(tgid)) - return ctx; - - return NULL; -} - -static int add_tgid_ctx(struct isgx_enclave *enclave) -{ - struct isgx_tgid_ctx *ctx; - struct pid *tgid = get_pid(task_tgid(current)); - - mutex_lock(&isgx_tgid_ctx_mutex); - - ctx = find_tgid_epc_cnt(tgid); - if (ctx) { - kref_get(&ctx->refcount); - enclave->tgid_ctx = ctx; - mutex_unlock(&isgx_tgid_ctx_mutex); - put_pid(tgid); - return 0; - } - - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (!ctx) { - mutex_unlock(&isgx_tgid_ctx_mutex); - put_pid(tgid); - return -ENOMEM; - } - - ctx->tgid = tgid; - kref_init(&ctx->refcount); - INIT_LIST_HEAD(&ctx->enclave_list); - - list_add(&ctx->list, &isgx_tgid_ctx_list); - atomic_inc(&isgx_nr_pids); - - enclave->tgid_ctx = ctx; - - mutex_unlock(&isgx_tgid_ctx_mutex); - return 0; -} -void release_tgid_ctx(struct kref *ref) -{ - struct isgx_tgid_ctx *pe = - container_of(ref, struct isgx_tgid_ctx, refcount); - mutex_lock(&isgx_tgid_ctx_mutex); - list_del(&pe->list); - atomic_dec(&isgx_nr_pids); - mutex_unlock(&isgx_tgid_ctx_mutex); - put_pid(pe->tgid); - kfree(pe); -} -static int enclave_rb_insert(struct rb_root *root, - struct isgx_enclave_page *data) -{ - struct rb_node **new = &(root->rb_node), *parent = NULL; - - /* Figure out where to put new node */ - while (*new) { - struct isgx_enclave_page *this = - container_of(*new, struct isgx_enclave_page, node); - - parent = *new; - if (data->addr < this->addr) - new = &((*new)->rb_left); - else if (data->addr > this->addr) - new = &((*new)->rb_right); - else - return -1; - } - - /* Add new node and rebalance tree. */ - rb_link_node(&data->node, parent, new); - rb_insert_color(&data->node, root); - - return 0; -} - -/** - * construct_enclave_page() - populate a new enclave page instance - * @enclave an enclave - * @entry the enclave page to be populated - * @addr the linear address of the enclave page - * - * Allocates VA slot for the enclave page and fills out its fields. Returns - * an error code on failure that can be either a POSIX error code or one of the - * error codes defined in isgx_user.h. - */ -static int construct_enclave_page(struct isgx_enclave *enclave, - struct isgx_enclave_page *entry, - unsigned long addr) -{ - struct isgx_va_page *va_page; - struct isgx_epc_page *epc_page = NULL; - unsigned int va_offset = PAGE_SIZE; - void *vaddr; - int ret = 0; - - list_for_each_entry(va_page, &enclave->va_pages, list) { - va_offset = isgx_alloc_va_slot(va_page); - if (va_offset < PAGE_SIZE) - break; - } - - if (va_offset == PAGE_SIZE) { - va_page = kzalloc(sizeof(*va_page), GFP_KERNEL); - if (!va_page) - return -ENOMEM; - - epc_page = isgx_alloc_epc_page(NULL, 0); - if (IS_ERR(epc_page)) { - kfree(va_page); - return PTR_ERR(epc_page); - } - - vaddr = isgx_get_epc_page(epc_page); - BUG_ON(!vaddr); - ret = __epa(vaddr); - isgx_put_epc_page(vaddr); - if (ret) { - isgx_err(enclave, "EPA returned %d\n", ret); - isgx_free_epc_page(epc_page, NULL, ISGX_FREE_EREMOVE); - kfree(va_page); - /* This probably a driver bug. Better to crash cleanly - * than let the failing driver to run. - */ - BUG(); - } - - va_page->epc_page = epc_page; - va_offset = isgx_alloc_va_slot(va_page); - list_add(&va_page->list, &enclave->va_pages); - } - - entry->va_page = va_page; - entry->va_offset = va_offset; - entry->addr = addr; - - return 0; -} - -static int get_enclave(unsigned long addr, struct isgx_enclave **enclave) -{ - struct mm_struct *mm = current->mm; - struct vm_area_struct *vma; - int ret; - - down_read(&mm->mmap_sem); - - ret = isgx_find_enclave(mm, addr, &vma); - if (!ret) { - *enclave = vma->vm_private_data; - kref_get(&(*enclave)->refcount); - } - - up_read(&mm->mmap_sem); - - return ret; -} - -static int validate_secs(const struct isgx_secs *secs) -{ - u32 needed_ssaframesize = 1; - u32 tmp; - int i; - - if (secs->flags & ISGX_SECS_A_RESERVED_MASK) - return -EINVAL; - - if (secs->flags & ISGX_SECS_A_MODE64BIT) { -#ifdef CONFIG_X86_64 - if (secs->size > isgx_enclave_size_max_64) - return -EINVAL; -#else - return -EINVAL; -#endif - } else { - /* On 64-bit architecture allow 32-bit enclaves only in - * the compatibility mode. - */ -#ifdef CONFIG_X86_64 - if (!test_thread_flag(TIF_ADDR32)) - return -EINVAL; -#endif - if (secs->size > isgx_enclave_size_max_32) - return -EINVAL; - } - - if ((secs->xfrm & 0x3) != 0x3 || (secs->xfrm & ~isgx_xfrm_mask)) - return -EINVAL; - - /* SKL quirk */ - if ((secs->xfrm & BIT(3)) != (secs->xfrm & BIT(4))) - return -EINVAL; - - for (i = 2; i < 64; i++) { - tmp = isgx_ssaframesize_tbl[i]; - if (((1 << i) & secs->xfrm) && (tmp > needed_ssaframesize)) - needed_ssaframesize = tmp; - } - - if (!secs->ssaframesize || !needed_ssaframesize || - needed_ssaframesize > secs->ssaframesize) - return -EINVAL; - - /* Must be power of two */ - if (secs->size == 0 || (secs->size & (secs->size - 1)) != 0) - return -EINVAL; - - for (i = 0; i < ISGX_SECS_RESERVED1_SIZE; i++) - if (secs->reserved1[i]) - return -EINVAL; - - for (i = 0; i < ISGX_SECS_RESERVED2_SIZE; i++) - if (secs->reserved2[i]) - return -EINVAL; - - for (i = 0; i < ISGX_SECS_RESERVED3_SIZE; i++) - if (secs->reserved3[i]) - return -EINVAL; - - for (i = 0; i < ISGX_SECS_RESERVED4_SIZE; i++) - if (secs->reserved[i]) - return -EINVAL; - - return 0; -} - -static long isgx_ioctl_enclave_create(struct file *filep, unsigned int cmd, - unsigned long arg) -{ - struct page_info pginfo; - struct isgx_secinfo secinfo; - struct sgx_enclave_create *createp = (struct sgx_enclave_create *)arg; - struct isgx_enclave *enclave = NULL; - struct isgx_secs *secs = NULL; - struct isgx_epc_page *secs_epc_page; - struct vm_area_struct *vma; - struct isgx_vma *evma; - void *secs_vaddr = NULL; - struct file *backing; - long ret; - - secs = kzalloc(sizeof(*secs), GFP_KERNEL); - if (!secs) - return -ENOMEM; - - ret = copy_from_user(secs, (void *)createp->src, sizeof (*secs)); - if (ret) { - kfree(secs); - return ret; - } - - if (validate_secs(secs)) { - kfree(secs); - return -EINVAL; - } - - backing = shmem_file_setup("Intel SGX backing storage", - secs->size + PAGE_SIZE, - VM_NORESERVE); - if (IS_ERR((void *) backing)) { - pr_debug("isgx: [%d] vm_mmap() for the backing of size 0x%lx returned %ld\n", - pid_nr(task_tgid(current->group_leader)), - (unsigned long) secs->size, - ret); - kfree(secs); - return PTR_ERR((void *) backing); - } - - enclave = kzalloc(sizeof(struct isgx_enclave), GFP_KERNEL); - if (!enclave) { - fput(backing); - ret = -ENOMEM; - goto out; - } - - kref_init(&enclave->refcount); - INIT_LIST_HEAD(&enclave->add_page_reqs); - INIT_LIST_HEAD(&enclave->va_pages); - INIT_LIST_HEAD(&enclave->vma_list); - INIT_LIST_HEAD(&enclave->load_list); - INIT_LIST_HEAD(&enclave->enclave_list); - mutex_init(&enclave->lock); - INIT_WORK(&enclave->add_page_work, isgx_add_page_worker); - - enclave->owner = current->group_leader; - enclave->mm = current->mm; - enclave->base = secs->base; - enclave->size = secs->size; - enclave->backing = backing; - - ret = add_tgid_ctx(enclave); - if (ret) - goto out; - - secs_epc_page = isgx_alloc_epc_page(NULL, 0); - if (IS_ERR(secs_epc_page)) { - ret = PTR_ERR(secs_epc_page); - secs_epc_page = NULL; - goto out; - } - - enclave->secs_page.epc_page = secs_epc_page; - - ret = construct_enclave_page(enclave, &enclave->secs_page, - enclave->base + enclave->size); - if (ret) - goto out; - - secs_vaddr = isgx_get_epc_page(enclave->secs_page.epc_page); - - pginfo.srcpge = (unsigned long) secs; - pginfo.linaddr = 0; - pginfo.secinfo = (unsigned long) &secinfo; - pginfo.secs = 0; - memset(&secinfo, 0, sizeof(secinfo)); - ret = __ecreate((void *) &pginfo, secs_vaddr); - - isgx_put_epc_page(secs_vaddr); - - if (ret) { - isgx_info(enclave, "ECREATE returned %d\n", (int)ret); - goto out; - } - - if (secs->flags & ISGX_SECS_A_DEBUG) - enclave->flags |= ISGX_ENCLAVE_DEBUG; - - down_read(¤t->mm->mmap_sem); - vma = find_vma(current->mm, secs->base); - if (!vma || vma->vm_ops != &isgx_vm_ops || - vma->vm_start != secs->base || - vma->vm_end != (secs->base + secs->size)) { - up_read(¤t->mm->mmap_sem); - ret = -EINVAL; - goto out; - } - evma = kzalloc(sizeof(struct isgx_vma), GFP_KERNEL); - if (evma) { - evma->vma = vma; - list_add_tail(&evma->vma_list, &enclave->vma_list); - vma->vm_private_data = enclave; - } else { - ret = -ENOMEM; - } - up_read(¤t->mm->mmap_sem); - - mutex_lock(&isgx_tgid_ctx_mutex); - list_add_tail(&enclave->enclave_list, &enclave->tgid_ctx->enclave_list); - mutex_unlock(&isgx_tgid_ctx_mutex); -out: - if (ret && enclave) - kref_put(&enclave->refcount, isgx_enclave_release); - kfree(secs); - return ret; -} - -static int validate_secinfo(struct isgx_secinfo *secinfo) -{ - u64 perm = secinfo->flags & ISGX_SECINFO_PERMISSION_MASK; - u64 page_type = secinfo->flags & ISGX_SECINFO_PAGE_TYPE_MASK; - int i; - - if ((secinfo->flags & ISGX_SECINFO_RESERVED_MASK) || - ((perm & SGX_SECINFO_W) && !(perm & SGX_SECINFO_R)) || - (page_type != SGX_SECINFO_TCS && - page_type != SGX_SECINFO_REG)) - return -EINVAL; - - for (i = 0; i < sizeof(secinfo->reserved) / sizeof(u64); i++) - if (secinfo->reserved[i]) - return -EINVAL; - - return 0; -} - -static int validate_tcs(struct isgx_tcs *tcs) -{ - int i; - - /* If FLAGS is not zero, ECALL will fail. */ - if ((tcs->flags != 0) || - (tcs->ossa & (PAGE_SIZE - 1)) || - (tcs->ofsbase & (PAGE_SIZE - 1)) || - (tcs->ogsbase & (PAGE_SIZE - 1)) || - ((tcs->fslimit & 0xFFF) != 0xFFF) || - ((tcs->gslimit & 0xFFF) != 0xFFF)) - return -EINVAL; - - for (i = 0; i < sizeof(tcs->reserved)/sizeof(u64); i++) - if (tcs->reserved[i]) - return -EINVAL; - - return 0; -} - -static int __enclave_add_page(struct isgx_enclave *enclave, - struct isgx_enclave_page *enclave_page, - struct sgx_enclave_add_page *addp, - struct isgx_secinfo *secinfo) -{ - u64 page_type = secinfo->flags & ISGX_SECINFO_PAGE_TYPE_MASK; - struct isgx_tcs *tcs; - struct page *backing_page; - struct isgx_add_page_req *req = NULL; - int ret; - int empty; - void *user_vaddr; - void *tmp_vaddr; - struct page *tmp_page; - - tmp_page = alloc_page(GFP_HIGHUSER); - if (!tmp_page) - return -ENOMEM; - - tmp_vaddr = kmap(tmp_page); - ret = copy_from_user((void *)tmp_vaddr, (void *)addp->src, PAGE_SIZE); - kunmap(tmp_page); - if (ret) { - __free_page(tmp_page); - return -EFAULT; - } - - if (validate_secinfo(secinfo)) { - __free_page(tmp_page); - return -EINVAL; - } - - if (page_type == SGX_SECINFO_TCS) { - tcs = (struct isgx_tcs *) kmap(tmp_page); - ret = validate_tcs(tcs); - kunmap(tmp_page); - if (ret) { - __free_page(tmp_page); - return ret; - } - } - - ret = construct_enclave_page(enclave, enclave_page, addp->addr); - if (ret) { - __free_page(tmp_page); - return -EINVAL; - } - - down_read(&enclave->mm->mmap_sem); - mutex_lock(&enclave->lock); - - if (enclave->flags & ISGX_ENCLAVE_INITIALIZED) { - ret = -EINVAL; - goto out; - } - - if (isgx_enclave_find_page(enclave, addp->addr)) { - ret = -EEXIST; - goto out; - } - - if (!(req = kzalloc(sizeof(*req), GFP_KERNEL))) { - ret = -ENOMEM; - goto out; - } - - backing_page = isgx_get_backing_page(enclave, enclave_page, true); - if (IS_ERR((void *) backing_page)) { - ret = PTR_ERR((void *) backing_page); - goto out; - } - - user_vaddr = kmap(backing_page); - tmp_vaddr = kmap(tmp_page); - memcpy(user_vaddr, tmp_vaddr, PAGE_SIZE); - kunmap(backing_page); - kunmap(tmp_page); - - if (page_type == SGX_SECINFO_TCS) - enclave_page->flags |= ISGX_ENCLAVE_PAGE_TCS; - - memcpy(&req->secinfo, secinfo, sizeof(*secinfo)); - - req->enclave = enclave; - req->enclave_page = enclave_page; - req->mrmask = addp->mrmask; - empty = list_empty(&enclave->add_page_reqs); - kref_get(&enclave->refcount); - list_add_tail(&req->list, &enclave->add_page_reqs); - if (empty) - queue_work(isgx_add_page_wq, &enclave->add_page_work); - - set_page_dirty(backing_page); - put_page(backing_page); -out: - if (ret) { - kfree(req); - isgx_free_va_slot(enclave_page->va_page, - enclave_page->va_offset); - } else - BUG_ON(enclave_rb_insert(&enclave->enclave_rb, enclave_page)); - - mutex_unlock(&enclave->lock); - up_read(&enclave->mm->mmap_sem); - __free_page(tmp_page); - return ret; -} - -static long isgx_ioctl_enclave_add_page(struct file *filep, unsigned int cmd, - unsigned long arg) -{ - struct sgx_enclave_add_page *addp; - struct isgx_enclave *enclave; - struct isgx_enclave_page *page; - struct isgx_secinfo secinfo; - int ret; - - addp = (struct sgx_enclave_add_page *) arg; - - if (addp->addr & (PAGE_SIZE - 1)) - return -EINVAL; - - if (copy_from_user(&secinfo, (void __user *) addp->secinfo, - sizeof(secinfo))) - return -EFAULT; - - ret = get_enclave(addp->addr, &enclave); - if (ret) - return ret; - - if (addp->addr < enclave->base || - addp->addr > (enclave->base + enclave->size - PAGE_SIZE)) { - kref_put(&enclave->refcount, isgx_enclave_release); - return -EINVAL; - } - - page = kzalloc(sizeof(*page), GFP_KERNEL); - if (!page) { - kref_put(&enclave->refcount, isgx_enclave_release); - return -ENOMEM; - } - - ret = __enclave_add_page(enclave, page, addp, &secinfo); - kref_put(&enclave->refcount, isgx_enclave_release); - - if (ret) - kfree(page); - - return ret; -} - -static int __isgx_enclave_init(struct isgx_enclave *enclave, - char *sigstruct, - struct isgx_einittoken *einittoken) -{ - int ret = SGX_UNMASKED_EVENT; - void *secs_va = NULL; - int i; - int j; - - if (einittoken->valid && einittoken->isvsvnle < isgx_isvsvnle_min) - return SGX_LE_ROLLBACK; - - for (i = 0; i < EINIT_TRY_COUNT; i++) { - for (j = 0; j < EINIT_SPIN_COUNT; j++) { - mutex_lock(&enclave->lock); - secs_va = isgx_get_epc_page(enclave->secs_page.epc_page); - ret = __einit(sigstruct, einittoken, secs_va); - isgx_put_epc_page(secs_va); - mutex_unlock(&enclave->lock); - if (ret == SGX_UNMASKED_EVENT) - continue; - else - break; - } - - if (ret != SGX_UNMASKED_EVENT) - goto out; - - msleep_interruptible(EINIT_BACKOFF_TIME); - if (signal_pending(current)) - return -EINTR; - } - -out: - if (ret) - isgx_info(enclave, "EINIT returned %d\n", ret); - else { - enclave->flags |= ISGX_ENCLAVE_INITIALIZED; - - if (einittoken->isvsvnle > isgx_isvsvnle_min) - isgx_isvsvnle_min = einittoken->isvsvnle; - } - - return ret; -} - -static long isgx_ioctl_enclave_init(struct file *filep, unsigned int cmd, - unsigned long arg) -{ - int ret = -EINVAL; - struct sgx_enclave_init *initp = (struct sgx_enclave_init *) arg; - unsigned long enclave_id = initp->addr; - char *sigstruct; - struct isgx_einittoken *einittoken; - struct isgx_enclave *enclave; - struct page *initp_page; - - initp_page = alloc_page(GFP_HIGHUSER); - if (!initp_page) - return -ENOMEM; - - sigstruct = kmap(initp_page); - einittoken = (struct isgx_einittoken *) - ((unsigned long) sigstruct + PAGE_SIZE / 2); - - ret = copy_from_user(sigstruct, (void *)initp->sigstruct, - SIGSTRUCT_SIZE); - if (ret) - goto out_free_page; - - ret = copy_from_user(einittoken, (void *)initp->einittoken, - EINITTOKEN_SIZE); - if (ret) - goto out_free_page; - - ret = get_enclave(enclave_id, &enclave); - if (ret) - goto out_free_page; - - mutex_lock(&enclave->lock); - if (enclave->flags & ISGX_ENCLAVE_INITIALIZED) { - ret = -EINVAL; - mutex_unlock(&enclave->lock); - goto out; - } - mutex_unlock(&enclave->lock); - - flush_work(&enclave->add_page_work); - - ret = __isgx_enclave_init(enclave, sigstruct, einittoken); -out: - kref_put(&enclave->refcount, isgx_enclave_release); -out_free_page: - kunmap(initp_page); - __free_page(initp_page); - return ret; -} - -typedef long (*isgx_ioctl_t)(struct file *filep, unsigned int cmd, - unsigned long arg); - -long isgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) -{ - char data[256]; - isgx_ioctl_t handler = NULL; - long ret; - - switch (cmd) { - case SGX_IOC_ENCLAVE_CREATE: - handler = isgx_ioctl_enclave_create; - break; - case SGX_IOC_ENCLAVE_ADD_PAGE: - handler = isgx_ioctl_enclave_add_page; - break; - case SGX_IOC_ENCLAVE_INIT: - handler = isgx_ioctl_enclave_init; - break; - default: - return -EINVAL; - } - - if (copy_from_user(data, (void __user *) arg, _IOC_SIZE(cmd))) - return -EFAULT; - - ret = handler(filep, cmd, (unsigned long) ((void *) data)); - - if (!ret && (cmd & IOC_OUT)) { - if (copy_to_user((void __user *) arg, data, _IOC_SIZE(cmd))) - return -EFAULT; - } - - return ret; -} - -static int do_eadd(struct isgx_epc_page *secs_page, - struct isgx_epc_page *epc_page, - unsigned long linaddr, - struct isgx_secinfo *secinfo, - struct page *backing_page) -{ - struct page_info pginfo; - void *epc_page_vaddr; - int ret; - - pginfo.srcpge = (unsigned long) kmap_atomic(backing_page); - pginfo.secs = (unsigned long) isgx_get_epc_page(secs_page); - epc_page_vaddr = isgx_get_epc_page(epc_page); - - pginfo.linaddr = linaddr; - pginfo.secinfo = (unsigned long) secinfo; - ret = __eadd(&pginfo, epc_page_vaddr); - - isgx_put_epc_page(epc_page_vaddr); - isgx_put_epc_page((void *) (unsigned long) pginfo.secs); - kunmap_atomic((void *) (unsigned long) pginfo.srcpge); - - return ret; -} - -static int sgx_measure_page(struct isgx_epc_page *secs_page, - struct isgx_epc_page *epc_page, - u16 mrmask) -{ - void *secs; - void *epc; - int ret = 0; - int i, j; - - for (i = 0, j = 1; i < 0x1000 && !ret; i += 0x100, j <<= 1) { - if (!(j & mrmask)) - continue; - - secs = isgx_get_epc_page(secs_page); - epc = isgx_get_epc_page(epc_page); - - ret = __eextend(secs, (void *)((unsigned long)epc + i)); - - isgx_put_epc_page(epc); - isgx_put_epc_page(secs); - } - - return ret; -} - -static bool process_add_page_req(struct isgx_add_page_req *req) -{ - struct page *backing_page; - struct isgx_epc_page *epc_page; - struct isgx_enclave_page *enclave_page = req->enclave_page; - unsigned int mrmask = req->mrmask; - struct isgx_enclave *enclave = req->enclave; - unsigned free_flags = 0; - struct vm_area_struct *vma; - int ret; - - epc_page = isgx_alloc_epc_page(enclave->tgid_ctx, 0); - if (IS_ERR(epc_page)) - return false; - - if (!isgx_pin_mm(enclave)) { - isgx_free_epc_page(epc_page, enclave, 0); - return false; - } - - mutex_lock(&enclave->lock); - - if (list_empty(&enclave->vma_list) || - isgx_find_enclave(enclave->mm, enclave_page->addr, &vma)) - goto out; - - backing_page = isgx_get_backing_page(enclave, enclave_page, - false /* write */); - if (IS_ERR(backing_page)) - goto out; - - /* Do not race with do_exit() */ - if (!atomic_read(&enclave->mm->mm_users)) { - put_page(backing_page); - goto out; - } - - isgx_insert_pte(enclave, enclave_page, epc_page, vma); - ret = do_eadd(enclave->secs_page.epc_page, epc_page, - enclave_page->addr, &req->secinfo, backing_page); - - put_page(backing_page); - free_flags |= ISGX_FREE_EREMOVE; - if (ret) { - isgx_dbg(enclave, "EADD returned %d\n", ret); - goto out; - } - - enclave->secs_child_cnt++; - - ret = sgx_measure_page(enclave->secs_page.epc_page, epc_page, mrmask); - if (ret) { - isgx_dbg(enclave, "EEXTEND returned %d\n", ret); - goto out; - } - - isgx_test_and_clear_young(enclave, enclave_page->addr); - - enclave_page->epc_page = epc_page; - list_add_tail(&enclave_page->load_list, &enclave->load_list); - - mutex_unlock(&enclave->lock); - isgx_unpin_mm(enclave); - return true; -out: - isgx_free_epc_page(epc_page, enclave, free_flags); - mutex_unlock(&enclave->lock); - isgx_unpin_mm(enclave); - return false; -} - -void isgx_add_page_worker(struct work_struct *work) -{ - struct isgx_enclave *enclave; - struct isgx_add_page_req *req; - bool skip_rest = false; - bool is_empty = false; - - enclave = container_of(work, struct isgx_enclave, add_page_work); - - do { - schedule(); - - mutex_lock(&enclave->lock); - req = list_first_entry(&enclave->add_page_reqs, - struct isgx_add_page_req, list); - list_del(&req->list); - is_empty = list_empty(&enclave->add_page_reqs); - mutex_unlock(&enclave->lock); - - if (!skip_rest) - if (!process_add_page_req(req)) - skip_rest = true; - - kfree(req); - } while (!kref_put(&enclave->refcount, isgx_enclave_release) && - !is_empty); -} diff --git a/isgx_main.c b/isgx_main.c deleted file mode 100644 index 6ac030b..0000000 --- a/isgx_main.c +++ /dev/null @@ -1,350 +0,0 @@ -/* - * (C) Copyright 2015 Intel Corporation - * - * Authors: - * - * Jarkko Sakkinen - * Suresh Siddha - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 - * of the License. - */ - -#include "isgx.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRV_DESCRIPTION "Intel SGX Driver" -#define DRV_VERSION "0.10" - -#define ENCLAVE_SIZE_MAX_64 (64ULL * 1024ULL * 1024ULL * 1024ULL) -#define ENCLAVE_SIZE_MAX_32 (2ULL * 1024ULL * 1024ULL * 1024ULL) - -MODULE_DESCRIPTION(DRV_DESCRIPTION); -MODULE_AUTHOR("Jarkko Sakkinen "); -MODULE_VERSION(DRV_VERSION); - -/* - * Global data. - */ - -struct workqueue_struct *isgx_add_page_wq; -resource_size_t isgx_epc_base = 0; -unsigned long isgx_epc_size = 0; -#ifdef CONFIG_X86_64 -void *isgx_epc_mem; -#endif -u64 isgx_enclave_size_max_32 = ENCLAVE_SIZE_MAX_32; -u64 isgx_enclave_size_max_64 = ENCLAVE_SIZE_MAX_64; -u64 isgx_xfrm_mask = 0x3; -u32 isgx_ssaframesize_tbl[64]; - -/* - * Local data. - */ - - -static int isgx_mmap(struct file *file, struct vm_area_struct *vma); - -static unsigned long isgx_get_unmapped_area(struct file *file, - unsigned long addr, - unsigned long len, - unsigned long pgoff, - unsigned long flags); - -#ifdef CONFIG_COMPAT -static long isgx_compat_ioctl(struct file *filep, unsigned int cmd, - unsigned long arg) -{ - return isgx_ioctl(filep, cmd, arg); -} -#endif - -static const struct file_operations isgx_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = isgx_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = isgx_compat_ioctl, -#endif - .mmap = isgx_mmap, - .get_unmapped_area = isgx_get_unmapped_area, -}; - -static struct miscdevice isgx_dev = { - .name = "isgx", - .fops = &isgx_fops, - .mode = S_IRUGO | S_IWUGO, -}; - -static int isgx_power_event(struct notifier_block *this, unsigned long event, - void *ptr); - - -static struct notifier_block isgx_pm_notifier = { - .notifier_call = isgx_power_event, -}; - -static int isgx_mmap(struct file *file, struct vm_area_struct *vma) -{ - vma->vm_ops = &isgx_vm_ops; -#if !defined(VM_RESERVED) - vma->vm_flags |= VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP | VM_IO; -#else - vma->vm_flags |= VM_PFNMAP | VM_DONTEXPAND | VM_RESERVED | VM_IO; -#endif - - return 0; -} - -static void __isgx_enable_in_cr4(void *v) -{ -#if 0 - write_cr4(read_cr4() | (1 << 15)); -#endif -} - -static void isgx_enable(void) -{ - __isgx_enable_in_cr4(NULL); - smp_call_function(__isgx_enable_in_cr4, NULL, 1); -} - -static void __isgx_disable_in_cr4(void *v) -{ -#if 0 - write_cr4(read_cr4() & ~(1 << 15)); -#endif -} - -/* Should be in arch/x86/include/asm/cpufeature.h when upstreamed. */ -#ifndef X86_FEATURE_SGX -#define X86_FEATURE_SGX (9 * 32 + 2) -#endif - -static int isgx_init_platform(void) -{ - unsigned int eax, ebx, ecx, edx; - int i; - - cpuid(0, &eax, &ebx, &ecx, &edx); - if (eax < SGX_CPUID) { - pr_err("isgx: CPUID is missing the SGX leaf instruction\n"); - return -ENODEV; - } - - if (!boot_cpu_has(X86_FEATURE_SGX)) { - pr_err("isgx: CPU is missing the SGX feature\n"); - return -ENODEV; - } - - cpuid_count(SGX_CPUID, 0x0, &eax, &ebx, &ecx, &edx); - if (!(eax & 1)) { - pr_err("isgx: CPU does not support the SGX 1.0 instruction set\n"); - return -ENODEV; - } - - if (boot_cpu_has(X86_FEATURE_OSXSAVE)) { - cpuid_count(SGX_CPUID, 0x1, &eax, &ebx, &ecx, &edx); - isgx_xfrm_mask = (((u64) edx) << 32) + (u64) ecx; - for (i = 2; i < 64; i++) { - cpuid_count(0x0D, i, &eax, &ebx, &ecx, &edx); - if ((1 << i) & isgx_xfrm_mask) - isgx_ssaframesize_tbl[i] = - (168 + eax + ebx + PAGE_SIZE - 1) / - PAGE_SIZE; - } - } - - cpuid_count(SGX_CPUID, 0x0, &eax, &ebx, &ecx, &edx); - if (edx & 0xFFFF) { -#ifdef CONFIG_X86_64 - isgx_enclave_size_max_64 = 1ULL << ((edx >> 8) & 0xFF); -#endif - isgx_enclave_size_max_32 = 1ULL << (edx & 0xFF); - } - - cpuid_count(SGX_CPUID, 0x2, &eax, &ebx, &ecx, &edx); - - /* The should be at least one EPC area or something is wrong. */ - BUG_ON((eax & 0xf) != 0x1); - isgx_epc_base = (((u64) (ebx & 0xfffff)) << 32) + - (u64) (eax & 0xfffff000); - isgx_epc_size = (((u64) (edx & 0xfffff)) << 32) + - (u64) (ecx & 0xfffff000); - - if (!isgx_epc_base) - return -ENODEV; - - return 0; -} - -static int __init isgx_init(void) -{ - unsigned int wq_flags; - int ret; - - pr_info("isgx: " DRV_DESCRIPTION " v" DRV_VERSION "\n"); - - if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL) - return -ENODEV; - - ret = isgx_init_platform(); - if (ret) - return ret; - - pr_info("isgx: EPC memory range 0x%Lx-0x%Lx\n", isgx_epc_base, - isgx_epc_base + isgx_epc_size); - -#ifdef CONFIG_X86_64 - isgx_epc_mem = ioremap_cache(isgx_epc_base, isgx_epc_size); - if (!isgx_epc_mem) - return -ENOMEM; -#endif - - ret = isgx_page_cache_init(isgx_epc_base, isgx_epc_size); - if (ret) - goto out_iounmap; - - wq_flags = WQ_UNBOUND | WQ_FREEZABLE; -#ifdef WQ_NON_REENETRANT - wq_flags |= WQ_NON_REENTRANT; -#endif - isgx_add_page_wq = alloc_workqueue("isgx-add-page-wq", wq_flags, 1); - if (!isgx_add_page_wq) { - pr_err("isgx: alloc_workqueue() failed\n"); - ret = -ENOMEM; - goto out_iounmap; - } - - ret = misc_register(&isgx_dev); - if (ret) { - pr_err("isgx: misc_register() failed\n"); - goto out_workqueue; - } - - ret = register_pm_notifier(&isgx_pm_notifier); - if (ret) { - pr_err("isgx: register_pm_notifier() failed\n"); - goto out_misc; - } - - isgx_enable(); - - return 0; -out_misc: - misc_deregister(&isgx_dev); -out_workqueue: - destroy_workqueue(isgx_add_page_wq); -out_iounmap: -#ifdef CONFIG_X86_64 - iounmap(isgx_epc_mem); -#endif - return ret; -} - -static void __exit isgx_exit(void) -{ - misc_deregister(&isgx_dev); - destroy_workqueue(isgx_add_page_wq); -#ifdef CONFIG_X86_64 - iounmap(isgx_epc_mem); -#endif - unregister_pm_notifier(&isgx_pm_notifier); - isgx_page_cache_teardown(); - - __isgx_disable_in_cr4(NULL); - smp_call_function(__isgx_disable_in_cr4, NULL, 1); -} - -static unsigned long isgx_get_unmapped_area(struct file *file, - unsigned long addr, - unsigned long len, - unsigned long pgoff, - unsigned long flags) -{ - - if (len < 2 * PAGE_SIZE || (len & (len - 1))) - return -EINVAL; - - /* On 64-bit architecture, allow mmap() to exceed 32-bit enclave - * limit only if the task is not running in 32-bit compatibility - * mode. - */ - if (len > isgx_enclave_size_max_32) -#ifdef CONFIG_X86_64 - if (test_thread_flag(TIF_ADDR32)) - return -EINVAL; -#else - return -EINVAL; -#endif - -#ifdef CONFIG_X86_64 - if (len > isgx_enclave_size_max_64) - return -EINVAL; -#endif - - addr = current->mm->get_unmapped_area(file, addr, 2*len, pgoff, flags); - - if (IS_ERR_VALUE(addr)) - return addr; - - addr = (addr + (len - 1)) & ~(len - 1); - - return addr; -} - -static int isgx_suspend(void) -{ - struct isgx_tgid_ctx *ctx; - struct isgx_enclave *encl; - - kthread_stop(kisgxswapd_tsk); - kisgxswapd_tsk = NULL; - - list_for_each_entry(ctx, &isgx_tgid_ctx_list, list) { - list_for_each_entry(encl, &ctx->enclave_list, enclave_list) { - isgx_invalidate(encl); - encl->flags |= ISGX_ENCLAVE_SUSPEND; - } - } - - return NOTIFY_OK; -} - -static int isgx_resume(void) -{ - pr_info("isgx: resume\n"); - - isgx_enable(); - kisgxswapd_tsk = kthread_run(kisgxswapd, NULL, "kisgxswapd"); - - return NOTIFY_OK; -} - -static int isgx_power_event(struct notifier_block *this, unsigned long event, - void *ptr) -{ - switch (event) { - case PM_POST_HIBERNATION: - case PM_POST_SUSPEND: - return isgx_resume(); - case PM_HIBERNATION_PREPARE: - case PM_SUSPEND_PREPARE: - return isgx_suspend(); - default: - return NOTIFY_DONE; - } -} - -module_init(isgx_init); -module_exit(isgx_exit); -MODULE_LICENSE("GPL"); diff --git a/isgx_page_cache.c b/isgx_page_cache.c deleted file mode 100644 index f71408f..0000000 --- a/isgx_page_cache.c +++ /dev/null @@ -1,488 +0,0 @@ -/* - * (C) Copyright 2015 Intel Corporation - * - * Authors: - * - * Jarkko Sakkinen - * Suresh Siddha - * Serge Ayoun - * Shay Katz-zamir - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 - * of the License. - */ - -#include "isgx.h" -#include -#include -#include -#include -#include -#include - -static LIST_HEAD(isgx_free_list); -static DEFINE_SPINLOCK(isgx_free_list_lock); - -LIST_HEAD(isgx_tgid_ctx_list); -DEFINE_MUTEX(isgx_tgid_ctx_mutex); -unsigned int isgx_nr_total_epc_pages; -unsigned int isgx_nr_free_epc_pages; -unsigned int isgx_nr_low_epc_pages = ISGX_NR_LOW_EPC_PAGES_DEFAULT; -unsigned int isgx_nr_high_epc_pages; -struct task_struct *kisgxswapd_tsk; -static DECLARE_WAIT_QUEUE_HEAD(kisgxswapd_waitq); - -static struct isgx_tgid_ctx *isolate_ctx(unsigned long nr_to_scan) -{ - struct isgx_tgid_ctx *ctx; - int i; - - for (i = 0, ctx = NULL; i < nr_to_scan; i++, ctx = NULL) { - schedule(); - - mutex_lock(&isgx_tgid_ctx_mutex); - if (list_empty(&isgx_tgid_ctx_list)) { - mutex_unlock(&isgx_tgid_ctx_mutex); - continue; - } - - ctx = list_first_entry(&isgx_tgid_ctx_list, - struct isgx_tgid_ctx, - list); - list_move_tail(&ctx->list, &isgx_tgid_ctx_list); - - if (kref_get_unless_zero(&ctx->refcount)) { - mutex_unlock(&isgx_tgid_ctx_mutex); - break; - } - - mutex_unlock(&isgx_tgid_ctx_mutex); - kref_put(&ctx->refcount, release_tgid_ctx); - } - - return ctx; -} - -static struct isgx_enclave *isolate_enclave(unsigned long nr_to_scan) -{ - struct isgx_enclave *encl; - struct isgx_tgid_ctx *ctx; - int i; - - ctx = isolate_ctx(nr_to_scan); - if (!ctx) - return NULL; - - for (i = 0, encl = NULL; i < nr_to_scan; i++, encl = NULL) { - mutex_lock(&isgx_tgid_ctx_mutex); - if (list_empty(&ctx->enclave_list)) { - mutex_unlock(&isgx_tgid_ctx_mutex); - break; - } - - encl = list_first_entry(&ctx->enclave_list, struct isgx_enclave, - enclave_list); - list_move_tail(&encl->enclave_list, &ctx->enclave_list); - if (kref_get_unless_zero(&encl->refcount)) { - mutex_unlock(&isgx_tgid_ctx_mutex); - break; - } - - mutex_unlock(&isgx_tgid_ctx_mutex); - } - - kref_put(&ctx->refcount, release_tgid_ctx); - return encl; -} - -static struct isgx_enclave *isolate_cluster(struct list_head *dst, - unsigned long nr_to_scan) -{ - struct isgx_enclave *enclave; - struct isgx_enclave_page *entry; - int i; - - enclave = isolate_enclave(nr_to_scan); - if (!enclave) - return NULL; - - if (!isgx_pin_mm(enclave)) { - kref_put(&enclave->refcount, isgx_enclave_release); - return NULL; - } - - for (i = 0; i < nr_to_scan; i++) { - mutex_lock(&enclave->lock); - if (list_empty(&enclave->load_list)) { - mutex_unlock(&enclave->lock); - break; - } - - entry = list_first_entry(&enclave->load_list, - struct isgx_enclave_page, - load_list); - - if (!(entry->flags & ISGX_ENCLAVE_PAGE_RESERVED)) { - if (!isgx_test_and_clear_young(enclave, entry->addr)) { - entry->flags |= ISGX_ENCLAVE_PAGE_RESERVED; - list_move_tail(&entry->load_list, dst); - } - } else { - list_move_tail(&entry->load_list, &enclave->load_list); - } - - mutex_unlock(&enclave->lock); - } - - isgx_unpin_mm(enclave); - return enclave; -} - -static void isgx_ipi_cb(void *info) -{ -} - -static void do_eblock(struct isgx_epc_page *epc_page) -{ - void *vaddr; - vaddr = isgx_get_epc_page(epc_page); - BUG_ON(__eblock((unsigned long) vaddr)); - isgx_put_epc_page(vaddr); -} - -static void do_etrack(struct isgx_epc_page *epc_page) -{ - void *epc; - epc = isgx_get_epc_page(epc_page); - BUG_ON(__etrack(epc)); - isgx_put_epc_page(epc); -} - -static int do_ewb(struct isgx_enclave *enclave, - struct isgx_enclave_page *enclave_page, - struct page *backing_page) -{ - struct page_info pginfo; - void *epc; - void *va; - int ret; - - pginfo.srcpge = (unsigned long) kmap_atomic(backing_page); - epc = isgx_get_epc_page(enclave_page->epc_page); - va = isgx_get_epc_page(enclave_page->va_page->epc_page); - - pginfo.pcmd = (unsigned long) &enclave_page->pcmd; - pginfo.linaddr = 0; - pginfo.secs = 0; - ret = __ewb(&pginfo, epc, - (void *)((unsigned long) va + enclave_page->va_offset)); - - isgx_put_epc_page(va); - isgx_put_epc_page(epc); - kunmap_atomic((void *) pginfo.srcpge); - - if (ret != 0 && ret != SGX_NOT_TRACKED) - isgx_err(enclave, "EWB returned %d\n", ret); - - return ret; -} - - -static void evict_cluster(struct isgx_enclave *enclave, struct list_head *src) -{ - struct isgx_enclave_page *entry; - struct isgx_enclave_page *tmp; - struct page *pages[ISGX_NR_SWAP_CLUSTER_MAX+1]; - struct isgx_vma *evma; - int cnt = 0; - int i = 0; - int ret; - - if (list_empty(src)) - return; - - if (!isgx_pin_mm(enclave)) { - while (!list_empty(src)) { - entry = list_first_entry(src, struct isgx_enclave_page, - load_list); - list_del(&entry->load_list); - mutex_lock(&enclave->lock); - isgx_free_epc_page(entry->epc_page, enclave, - ISGX_FREE_EREMOVE); - entry->epc_page = NULL; - entry->flags &= ~ISGX_ENCLAVE_PAGE_RESERVED; - mutex_unlock(&enclave->lock); - } - - return; - } - - /* EBLOCK */ - - list_for_each_entry_safe(entry, tmp, src, load_list) { - mutex_lock(&enclave->lock); - evma = isgx_find_vma(enclave, entry->addr); - if (!evma) { - list_del(&entry->load_list); - isgx_free_epc_page(entry->epc_page, enclave, - ISGX_FREE_EREMOVE); - entry->epc_page = NULL; - entry->flags &= ~ISGX_ENCLAVE_PAGE_RESERVED; - mutex_unlock(&enclave->lock); - continue; - } - - pages[cnt] = isgx_get_backing_page(enclave, entry, true); - if (IS_ERR((void *) pages[cnt])) { - list_del(&entry->load_list); - list_add_tail(&entry->load_list, &enclave->load_list); - entry->flags &= ~ISGX_ENCLAVE_PAGE_RESERVED; - mutex_unlock(&enclave->lock); - continue; - } - - zap_vma_ptes(evma->vma, entry->addr, PAGE_SIZE); - do_eblock(entry->epc_page); - cnt++; - mutex_unlock(&enclave->lock); - } - - /* ETRACK */ - - mutex_lock(&enclave->lock); - do_etrack(enclave->secs_page.epc_page); - mutex_unlock(&enclave->lock); - - /* EWB */ - - mutex_lock(&enclave->lock); - i = 0; - - while (!list_empty(src)) { - entry = list_first_entry(src, struct isgx_enclave_page, load_list); - list_del(&entry->load_list); - - evma = isgx_find_vma(enclave, entry->addr); - if (evma) { - ret = do_ewb(enclave, entry, pages[i]); - BUG_ON(ret != 0 && ret != SGX_NOT_TRACKED); - /* Only kick out threads with an IPI if needed. */ - if (ret) { - smp_call_function(isgx_ipi_cb, NULL, 1); - BUG_ON(do_ewb(enclave, entry, pages[i])); - } - enclave->secs_child_cnt--; - - isgx_free_epc_page(entry->epc_page, enclave, 0); - } else { - isgx_free_epc_page(entry->epc_page, enclave, - ISGX_FREE_EREMOVE); - } - - if (evma != NULL) - set_page_dirty(pages[i]); - put_page(pages[i++]); - - entry->epc_page = NULL; - entry->flags &= ~ISGX_ENCLAVE_PAGE_RESERVED; - } - - /* Allow SECS page eviction only when the enclave is initialized. */ - if (!enclave->secs_child_cnt && - (enclave->flags & ISGX_ENCLAVE_INITIALIZED)) { - pages[cnt] = isgx_get_backing_page(enclave, &enclave->secs_page, - true); - if (!IS_ERR((void *) pages[cnt])) { - BUG_ON(do_ewb(enclave, &enclave->secs_page, - pages[cnt])); - enclave->flags |= ISGX_ENCLAVE_SECS_EVICTED; - - /* The secs page is not accounted (for unknown reason to - * me). - */ - isgx_free_epc_page(enclave->secs_page.epc_page, NULL, 0); - set_page_dirty(pages[cnt]); - put_page(pages[cnt]); - - enclave->secs_page.epc_page = NULL; - } - } - - mutex_unlock(&enclave->lock); - BUG_ON(i != cnt); - - isgx_unpin_mm(enclave); -} - -int kisgxswapd(void *p) -{ - struct isgx_enclave *encl; - LIST_HEAD(cluster); - DEFINE_WAIT(wait); - unsigned int nr_free; - unsigned int nr_high; - - for ( ; ; ) { - if (kthread_should_stop()) - break; - - spin_lock(&isgx_free_list_lock); - nr_free = isgx_nr_free_epc_pages; - nr_high = isgx_nr_high_epc_pages; - spin_unlock(&isgx_free_list_lock); - - - if (nr_free < nr_high) { - encl = isolate_cluster(&cluster, ISGX_NR_SWAP_CLUSTER_MAX); - if (encl) { - evict_cluster(encl, &cluster); - kref_put(&encl->refcount, isgx_enclave_release); - } - - schedule(); - } else { - prepare_to_wait(&kisgxswapd_waitq, - &wait, TASK_INTERRUPTIBLE); - - if (!kthread_should_stop()) - schedule(); - - finish_wait(&kisgxswapd_waitq, &wait); - } - } - - pr_info("%s: done\n", __func__); - return 0; -} - -int isgx_page_cache_init(resource_size_t start, unsigned long size) -{ - unsigned long i; - struct isgx_epc_page *new_epc_page, *entry; - struct list_head *parser, *temp; - - for (i = 0; i < size; i += PAGE_SIZE) { - new_epc_page = kzalloc(sizeof(struct isgx_epc_page), GFP_KERNEL); - if (!new_epc_page) - goto err_freelist; - new_epc_page->pa = start + i; - - spin_lock(&isgx_free_list_lock); - list_add_tail(&new_epc_page->free_list, &isgx_free_list); - isgx_nr_total_epc_pages++; - isgx_nr_free_epc_pages++; - spin_unlock(&isgx_free_list_lock); - } - - isgx_nr_high_epc_pages = 2 * isgx_nr_low_epc_pages; - kisgxswapd_tsk = kthread_run(kisgxswapd, NULL, "kisgxswapd"); - - return 0; -err_freelist: - list_for_each_safe(parser, temp, &isgx_free_list) { - spin_lock(&isgx_free_list_lock); - entry = list_entry(parser, struct isgx_epc_page, free_list); - list_del(&entry->free_list); - spin_unlock(&isgx_free_list_lock); - kfree(entry); - } - return -ENOMEM; -} - -void isgx_page_cache_teardown(void) -{ - struct isgx_epc_page *entry; - struct list_head *parser, *temp; - - if (kisgxswapd_tsk) - kthread_stop(kisgxswapd_tsk); - - spin_lock(&isgx_free_list_lock); - list_for_each_safe(parser, temp, &isgx_free_list) { - entry = list_entry(parser, struct isgx_epc_page, free_list); - list_del(&entry->free_list); - kfree(entry); - } - spin_unlock(&isgx_free_list_lock); -} - -static struct isgx_epc_page *isgx_alloc_epc_page_fast(void) -{ - struct isgx_epc_page *entry = NULL; - - spin_lock(&isgx_free_list_lock); - - if (!list_empty(&isgx_free_list)) { - entry = list_first_entry(&isgx_free_list, struct isgx_epc_page, - free_list); - list_del(&entry->free_list); - isgx_nr_free_epc_pages--; - } - - spin_unlock(&isgx_free_list_lock); - - return entry; -} - -struct isgx_epc_page *isgx_alloc_epc_page( - struct isgx_tgid_ctx *tgid_epc_cnt, - unsigned int flags) -{ - struct isgx_enclave *encl; - LIST_HEAD(cluster); - struct isgx_epc_page *entry; - - for ( ; ; ) { - entry = isgx_alloc_epc_page_fast(); - if (entry) { - if (tgid_epc_cnt) - atomic_inc(&tgid_epc_cnt->epc_cnt); - break; - } else if (flags & ISGX_ALLOC_ATOMIC) { - entry = ERR_PTR(-EBUSY); - break; - } - - if (signal_pending(current)) { - entry = ERR_PTR(-ERESTARTSYS); - break; - } - - encl = isolate_cluster(&cluster, ISGX_NR_SWAP_CLUSTER_MAX); - if (encl) { - evict_cluster(encl, &cluster); - kref_put(&encl->refcount, isgx_enclave_release); - } - - schedule(); - } - - if (isgx_nr_free_epc_pages < isgx_nr_low_epc_pages) - wake_up(&kisgxswapd_waitq); - - return entry; -} - -void isgx_free_epc_page(struct isgx_epc_page *entry, - struct isgx_enclave *encl, - unsigned int flags) -{ - BUG_ON(!entry); - - if (encl) { - atomic_dec(&encl->tgid_ctx->epc_cnt); - - if (encl->flags & ISGX_ENCLAVE_SUSPEND) - flags &= ~ISGX_FREE_EREMOVE; - } - - if (flags & ISGX_FREE_EREMOVE) - isgx_eremove(entry); - - spin_lock(&isgx_free_list_lock); - list_add(&entry->free_list, &isgx_free_list); - isgx_nr_free_epc_pages++; - spin_unlock(&isgx_free_list_lock); -} diff --git a/isgx_user.h b/isgx_user.h deleted file mode 100644 index 65c3c5a..0000000 --- a/isgx_user.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * (C) Copyright 2016 Intel Corporation - * - * Authors: - * - * Jarkko Sakkinen - * Suresh Siddha - * Serge Ayoun - * Shay Katz-zamir - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 - * of the License. - */ - -#ifndef _UAPI_ASM_X86_SGX_H -#define _UAPI_ASM_X86_SGX_H - -#include -#include - -#define SGX_MAGIC 0xA4 - -#define SGX_IOC_ENCLAVE_CREATE \ - _IOW(SGX_MAGIC, 0x00, struct sgx_enclave_create) -#define SGX_IOC_ENCLAVE_ADD_PAGE \ - _IOW(SGX_MAGIC, 0x01, struct sgx_enclave_add_page) -#define SGX_IOC_ENCLAVE_INIT \ - _IOW(SGX_MAGIC, 0x02, struct sgx_enclave_init) - -/* SGX leaf instruction return values */ -#define SGX_SUCCESS 0 -#define SGX_INVALID_SIG_STRUCT 1 -#define SGX_INVALID_ATTRIBUTE 2 -#define SGX_BLKSTATE 3 -#define SGX_INVALID_MEASUREMENT 4 -#define SGX_NOTBLOCKABLE 5 -#define SGX_PG_INVLD 6 -#define SGX_LOCKFAIL 7 -#define SGX_INVALID_SIGNATURE 8 -#define SGX_MAC_COMPARE_FAIL 9 -#define SGX_PAGE_NOT_BLOCKED 10 -#define SGX_NOT_TRACKED 11 -#define SGX_VA_SLOT_OCCUPIED 12 -#define SGX_CHILD_PRESENT 13 -#define SGX_ENCLAVE_ACT 14 -#define SGX_ENTRYEPOCH_LOCKED 15 -#define SGX_INVALID_LICENSE 16 -#define SGX_PREV_TRK_INCMPL 17 -#define SGX_PG_IS_SECS 18 -#define SGX_INVALID_CPUSVN 32 -#define SGX_INVALID_ISVSVN 64 -#define SGX_UNMASKED_EVENT 128 -#define SGX_INVALID_KEYNAME 256 - -/* IOCTL return values */ -#define SGX_POWER_LOST_ENCLAVE 0x40000000 -#define SGX_LE_ROLLBACK 0x40000001 - -struct sgx_enclave_create { - __u64 src; -} __attribute__((packed)); - -struct sgx_enclave_add_page { - __u64 addr; - __u64 src; - __u64 secinfo; - __u16 mrmask; -} __attribute__((packed)); - -struct sgx_enclave_init { - __u64 addr; - __u64 sigstruct; - __u64 einittoken; -} __attribute__((packed)); - -#endif /* _UAPI_ASM_X86_SGX_H */ diff --git a/isgx_util.c b/isgx_util.c deleted file mode 100644 index 7234ce0..0000000 --- a/isgx_util.c +++ /dev/null @@ -1,345 +0,0 @@ -/* - * (C) Copyright 2015 Intel Corporation - * - * Authors: - * - * Jarkko Sakkinen - * Suresh Siddha - * Serge Ayoun - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 - * of the License. - */ - -#include "isgx.h" -#include -#include -#include - -void *isgx_get_epc_page(struct isgx_epc_page *entry) -{ -#ifdef CONFIG_X86_32 - return kmap_atomic_pfn(PFN_DOWN(entry->pa)); -#else - return isgx_epc_mem + (entry->pa - isgx_epc_base); -#endif -} - -void isgx_put_epc_page(void *epc_page_vaddr) -{ -#ifdef CONFIG_X86_32 - kunmap_atomic(epc_page_vaddr); -#else -#endif -} - -struct page *isgx_get_backing_page(struct isgx_enclave* enclave, - struct isgx_enclave_page* entry, - bool write) -{ - struct page *backing; - struct inode *inode; - struct address_space *mapping; - gfp_t gfpmask; - pgoff_t index; - - inode = enclave->backing->f_path.dentry->d_inode; - mapping = inode->i_mapping; - gfpmask = mapping_gfp_mask(mapping); - - index = (entry->addr - enclave->base) >> PAGE_SHIFT; - backing = shmem_read_mapping_page_gfp(mapping, index, gfpmask); - return backing; -} - -void isgx_insert_pte(struct isgx_enclave *enclave, - struct isgx_enclave_page *enclave_page, - struct isgx_epc_page *epc_page, - struct vm_area_struct *vma) -{ - int ret; -#ifdef CONFIG_X86_32 - void *ioremap_vaddr = ioremap_cache(epc_page->pa, PAGE_SIZE); - BUG_ON(!ioremap_vaddr); -#endif - ret = vm_insert_pfn(vma, enclave_page->addr, PFN_DOWN(epc_page->pa)); -#ifdef CONFIG_X86_32 - iounmap(ioremap_vaddr); -#endif - if (ret) { - isgx_err(enclave, "vm_insert_pfn() returned %d\n", ret); - BUG(); - } -} - -int isgx_eremove(struct isgx_epc_page *epc_page) -{ - void *epc; - int ret; - - epc = isgx_get_epc_page(epc_page); - ret = __eremove(epc); - isgx_put_epc_page(epc); - - if (ret) - pr_err("EREMOVE returned %d\n", ret); - - return ret; -} - -static int isgx_test_and_clear_young_cb(pte_t *ptep, pgtable_t token, - unsigned long addr, void *data) -{ - int ret = pte_young(*ptep); - - if (ret) { - pte_t pte = pte_mkold(*ptep); - set_pte_at((struct mm_struct *) data, addr, ptep, pte); - } - - return ret; -} - -/** - * isgx_test_and_clear_young() - is the enclave page recently accessed? - * @enclave: enclave - * @addr: address of the enclave page - * - * Checks the 'A' bit from the PTE corresponding to the enclave page and - * clears it. - */ -int isgx_test_and_clear_young(struct isgx_enclave *enclave, - unsigned long addr) -{ - struct isgx_vma *evma = isgx_find_vma(enclave, addr); - if (!evma) - return 0; - - return apply_to_page_range(enclave->mm, addr, PAGE_SIZE, - isgx_test_and_clear_young_cb, enclave->mm); -} - -/** - * isgx_find_vma() - find VMA for the enclave address - * @enclave: the enclave to be searched - * @addr: the linear address to query - * - * Finds VMA for the given address of the enclave. Returns the VMA if - * there is one containing the given address. - */ -struct isgx_vma *isgx_find_vma(struct isgx_enclave *enclave, - unsigned long addr) -{ - struct isgx_vma *tmp; - struct isgx_vma *evma; - - list_for_each_entry_safe(evma, tmp, &enclave->vma_list, vma_list) { - if (evma->vma->vm_start <= addr && evma->vma->vm_end > addr) - return evma; - } - - isgx_dbg(enclave, "cannot find VMA at 0x%lx\n", addr); - return NULL; -} - -/** - * isgx_zap_tcs_ptes() - clear PTEs that contain TCS pages from some enclave VMA. - * @enclave an enclave - * @vma: a VMA of the enclave - */ -void isgx_zap_tcs_ptes(struct isgx_enclave *enclave, struct vm_area_struct *vma) -{ - struct isgx_enclave_page *entry; - struct rb_node *rb; - - BUG_ON(vma->vm_private_data != NULL && vma->vm_private_data != enclave); - BUG_ON(vma->vm_ops != &isgx_vm_ops); - - rb = rb_first(&enclave->enclave_rb); - while (rb) { - entry = container_of(rb, struct isgx_enclave_page, node); - rb = rb_next(rb); - if (entry->epc_page && (entry->flags & ISGX_ENCLAVE_PAGE_TCS) - && entry->addr >= vma->vm_start - && entry->addr < vma->vm_end) - zap_vma_ptes(vma, entry->addr, PAGE_SIZE); - } -} - -/** - * isgx_pin_mm - pin the mm_struct of an enclave - * - * @encl: an enclave - * - * Locks down mmap_sem of an enclave if it still has VMAs and was not suspended. - * Returns true if this the case. - */ -bool isgx_pin_mm(struct isgx_enclave *encl) -{ - if (encl->flags & ISGX_ENCLAVE_SUSPEND) - return false; - - mutex_lock(&encl->lock); - if (!list_empty(&encl->vma_list)) { - atomic_inc(&encl->mm->mm_count); - } else { - mutex_unlock(&encl->lock); - return false; - } - mutex_unlock(&encl->lock); - - down_read(&encl->mm->mmap_sem); - - if (list_empty(&encl->vma_list)) { - isgx_unpin_mm(encl); - return false; - } - - return true; -} - -/** - * isgx_unpin_mm - unpin the mm_struct of an enclave - * - * @encl: an enclave - * - * Unlocks the mmap_sem. - */ -void isgx_unpin_mm(struct isgx_enclave *encl) -{ - up_read(&encl->mm->mmap_sem); - mmdrop(encl->mm); -} - -/** - * isgx_unpin_mm - invalidate the enclave - * - * @encl: an enclave - * - * Unmap TCS pages and empty the VMA list. - */ -void isgx_invalidate(struct isgx_enclave *encl) -{ - struct isgx_vma *vma; - - list_for_each_entry(vma, &encl->vma_list, vma_list) - isgx_zap_tcs_ptes(encl, vma->vma); - - while (!list_empty(&encl->vma_list)) { - vma = list_first_entry(&encl->vma_list, struct isgx_vma, - vma_list); - list_del(&vma->vma_list); - kfree(vma); - } -} - -/** - * isgx_find_enclave() - find enclave given a virtual address - * @mm: the address space where we query the enclave - * @addr: the virtual address to query - * @vma: VMA if an enclave is found or NULL if not - * - * Finds an enclave given a virtual address and a address space where to seek it - * from. The return value is zero on success. Otherwise, it is either positive - * for SGX specific errors or negative for the system errors. - */ -int isgx_find_enclave(struct mm_struct *mm, unsigned long addr, - struct vm_area_struct **vma) -{ - struct isgx_enclave *enclave; - - *vma = find_vma(mm, addr); - - if (!(*vma) || (*vma)->vm_ops != &isgx_vm_ops || addr < (*vma)->vm_start) - return -EINVAL; - - /* Is ECREATE already done? */ - enclave = (*vma)->vm_private_data; - if (!enclave) - return -ENOENT; - - if (enclave->flags & ISGX_ENCLAVE_SUSPEND) { - isgx_info(enclave, "suspend ID has been changed"); - return SGX_POWER_LOST_ENCLAVE; - } - - return 0; -} - -/** - * isgx_enclave_find_page() - find an enclave page - * @encl: the enclave to query - * @addr: the virtual address to query - */ -struct isgx_enclave_page *isgx_enclave_find_page(struct isgx_enclave *enclave, - unsigned long enclave_la) -{ - struct rb_node *node = enclave->enclave_rb.rb_node; - - while (node) { - struct isgx_enclave_page *data = - container_of(node, struct isgx_enclave_page, node); - - if (data->addr > enclave_la) - node = node->rb_left; - else if (data->addr < enclave_la) - node = node->rb_right; - else - return data; - } - - return NULL; -} - -void isgx_enclave_release(struct kref *ref) -{ - struct rb_node *rb1, *rb2; - struct isgx_enclave_page *entry; - struct isgx_va_page *va_page; - struct isgx_enclave *enclave = - container_of(ref, struct isgx_enclave, refcount); - - mutex_lock(&isgx_tgid_ctx_mutex); - if (!list_empty(&enclave->enclave_list)) - list_del(&enclave->enclave_list); - - mutex_unlock(&isgx_tgid_ctx_mutex); - - rb1 = rb_first(&enclave->enclave_rb); - while (rb1) { - entry = container_of(rb1, struct isgx_enclave_page, node); - rb2 = rb_next(rb1); - rb_erase(rb1, &enclave->enclave_rb); - if (entry->epc_page) { - list_del(&entry->load_list); - isgx_free_epc_page(entry->epc_page, enclave, - ISGX_FREE_EREMOVE); - } - kfree(entry); - rb1 = rb2; - } - - while (!list_empty(&enclave->va_pages)) { - va_page = list_first_entry(&enclave->va_pages, - struct isgx_va_page, list); - list_del(&va_page->list); - isgx_free_epc_page(va_page->epc_page, NULL, ISGX_FREE_EREMOVE); - kfree(va_page); - } - - if (enclave->secs_page.epc_page) - isgx_free_epc_page(enclave->secs_page.epc_page, NULL, - ISGX_FREE_EREMOVE); - - enclave->secs_page.epc_page = NULL; - - if (enclave->tgid_ctx) - kref_put(&enclave->tgid_ctx->refcount, release_tgid_ctx); - - if (enclave->backing) - fput(enclave->backing); - - kfree(enclave); -} diff --git a/isgx_vma.c b/isgx_vma.c deleted file mode 100644 index fffa3cc..0000000 --- a/isgx_vma.c +++ /dev/null @@ -1,390 +0,0 @@ -/* - * (C) Copyright 2015 Intel Corporation - * - * Authors: - * - * Jarkko Sakkinen - * Suresh Siddha - * Serge Ayoun - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 - * of the License. - */ - -#include "isgx.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void isgx_vma_open(struct vm_area_struct *vma) -{ - struct isgx_enclave *enclave; - struct isgx_vma *evma; - - /* Was vm_private_data nullified as a result of the previous fork? */ - enclave = vma->vm_private_data; - if (!enclave) - goto out_fork; - - /* Was the process forked? mm_struct changes when the process is - * forked. - */ - mutex_lock(&enclave->lock); - evma = list_first_entry(&enclave->vma_list, - struct isgx_vma, vma_list); - if (evma->vma->vm_mm != vma->vm_mm) { - mutex_unlock(&enclave->lock); - goto out_fork; - } - mutex_unlock(&enclave->lock); - - mutex_lock(&enclave->lock); - if (!list_empty(&enclave->vma_list)) { - evma = kzalloc(sizeof(struct isgx_vma), GFP_KERNEL); - if (!evma) { - isgx_invalidate(enclave); - } else { - evma->vma = vma; - list_add_tail(&evma->vma_list, &enclave->vma_list); - } - } - mutex_unlock(&enclave->lock); - - kref_get(&enclave->refcount); - return; -out_fork: - zap_vma_ptes(vma, vma->vm_start, vma->vm_end-vma->vm_start); - vma->vm_private_data = NULL; - return; -} - -static void isgx_vma_close(struct vm_area_struct *vma) -{ - struct isgx_enclave *enclave = vma->vm_private_data; - struct isgx_vma *evma; - - /* If process was forked, VMA is still there but - * vm_private_data is set to NULL. - */ - if (!enclave) - return; - - mutex_lock(&enclave->lock); - - /* On vma_close() we remove the vma from vma_list - * there is a possibility that evma is not found - * in case vma_open() has failed on memory allocation - * and vma list has then been emptied - */ - evma = isgx_find_vma(enclave, vma->vm_start); - if (evma) { - list_del(&evma->vma_list); - kfree(evma); - } - - vma->vm_private_data = NULL; - - isgx_zap_tcs_ptes(enclave, vma); - zap_vma_ptes(vma, vma->vm_start, vma->vm_end - vma->vm_start); - - mutex_unlock(&enclave->lock); - - kref_put(&enclave->refcount, isgx_enclave_release); -} - -static void do_eldu(struct isgx_enclave *enclave, - struct isgx_enclave_page *enclave_page, - struct isgx_epc_page *epc_page, - struct page *backing_page, - bool is_secs) -{ - struct page_info pginfo; - void *secs_ptr = NULL; - void *epc_ptr; - void *va_ptr; - int ret; - - pginfo.srcpge = (unsigned long) kmap_atomic(backing_page); - if (!is_secs) - secs_ptr = isgx_get_epc_page(enclave->secs_page.epc_page); - pginfo.secs = (unsigned long) secs_ptr; - - epc_ptr = isgx_get_epc_page(epc_page); - va_ptr = isgx_get_epc_page(enclave_page->va_page->epc_page); - - pginfo.linaddr = is_secs ? 0 : enclave_page->addr; - pginfo.pcmd = (unsigned long) &enclave_page->pcmd; - - ret = __eldu((unsigned long) &pginfo, - (unsigned long) epc_ptr, - (unsigned long) va_ptr + - enclave_page->va_offset); - - isgx_put_epc_page(va_ptr); - isgx_put_epc_page(epc_ptr); - - if (!is_secs) - isgx_put_epc_page(secs_ptr); - - kunmap_atomic((void *) (unsigned long) pginfo.srcpge); - BUG_ON(ret); -} - -static struct isgx_enclave_page *isgx_vma_do_fault(struct vm_area_struct *vma, - unsigned long addr, - int reserve) -{ - struct isgx_enclave *enclave = vma->vm_private_data; - struct isgx_enclave_page *entry; - struct isgx_epc_page *epc_page; - struct isgx_epc_page *secs_epc_page = NULL; - struct page *backing_page; - - /* If process was forked, VMA is still there but vm_private_data is set - * to NULL. - */ - if (!enclave) - return ERR_PTR(-EFAULT); - - entry = isgx_enclave_find_page(enclave, addr); - if (!entry) - return ERR_PTR(-EFAULT); - - /* We use atomic allocation in the #PF handler in order to avoid ABBA - * deadlock with mmap_sems. - */ - epc_page = isgx_alloc_epc_page(enclave->tgid_ctx, ISGX_ALLOC_ATOMIC); - if (IS_ERR(epc_page)) - return (struct isgx_enclave_page *) epc_page; - - /* The SECS page is not currently accounted. */ - secs_epc_page = isgx_alloc_epc_page(NULL, ISGX_ALLOC_ATOMIC); - if (IS_ERR(secs_epc_page)) { - isgx_free_epc_page(epc_page, enclave, 0); - return (struct isgx_enclave_page *) secs_epc_page; - } - - mutex_lock(&enclave->lock); - - if (list_empty(&enclave->vma_list)) { - entry = ERR_PTR(-EFAULT); - goto out; - } - - if (!(enclave->flags & ISGX_ENCLAVE_INITIALIZED)) { - isgx_dbg(enclave, "cannot fault, unitialized\n"); - entry = ERR_PTR(-EFAULT); - goto out; - } - - if (reserve && (entry->flags & ISGX_ENCLAVE_PAGE_RESERVED)) { - isgx_dbg(enclave, "cannot fault, 0x%lx is reserved\n", - entry->addr); - entry = ERR_PTR(-EBUSY); - goto out; - } - - /* Legal race condition, page is already faulted. */ - if (entry->epc_page) { - if (reserve) - entry->flags |= ISGX_ENCLAVE_PAGE_RESERVED; - goto out; - } - - /* If SECS is evicted then reload it first */ - if (enclave->flags & ISGX_ENCLAVE_SECS_EVICTED) { - backing_page = isgx_get_backing_page(enclave, - &enclave->secs_page, - false); - if (IS_ERR((void *) backing_page)) { - entry = (void *) backing_page; - goto out; - } - - do_eldu(enclave, &enclave->secs_page, secs_epc_page, - backing_page, true /* is_secs */); - put_page(backing_page); - - enclave->secs_page.epc_page = secs_epc_page; - enclave->flags &= ~ISGX_ENCLAVE_SECS_EVICTED; - - /* Do not free */ - secs_epc_page = NULL; - } - - backing_page = isgx_get_backing_page(enclave, entry, false); - if (IS_ERR((void *) backing_page)) { - entry = (void *) backing_page; - goto out; - } - - do_eldu(enclave, entry, epc_page, backing_page, false /* is_secs */); - isgx_insert_pte(enclave, entry, epc_page, vma); - put_page(backing_page); - enclave->secs_child_cnt++; - - entry->epc_page = epc_page; - - if (reserve) - entry->flags |= ISGX_ENCLAVE_PAGE_RESERVED; - - /* Do not free */ - epc_page = NULL; - - list_add_tail(&entry->load_list, &enclave->load_list); -out: - mutex_unlock(&enclave->lock); - if (epc_page) - isgx_free_epc_page(epc_page, enclave, 0); - if (secs_epc_page) - isgx_free_epc_page(secs_epc_page, NULL, 0); - return entry; -} - -static int isgx_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) -{ - unsigned long addr = (unsigned long) vmf->virtual_address; - struct isgx_enclave_page *entry; - - entry = isgx_vma_do_fault(vma, addr, 0); - - if (!IS_ERR(entry) || PTR_ERR(entry) == -EBUSY) - return VM_FAULT_NOPAGE; - else - return VM_FAULT_SIGBUS; -} - -static inline int isgx_vma_access_word(struct isgx_enclave *enclave, - unsigned long addr, - void *buf, - int len, - int write, - struct isgx_enclave_page *enclave_page, - int i) -{ - char data[sizeof(unsigned long)]; - int align, cnt, offset; - void *vaddr; - int ret; - - offset = ((addr + i) & (PAGE_SIZE - 1)) & - ~(sizeof(unsigned long) - 1); - align = (addr + i) & (sizeof (unsigned long) - 1); - cnt = sizeof(unsigned long) - align; - cnt = min(cnt, len - i); - - if (write) { - if (enclave_page->flags & ISGX_ENCLAVE_PAGE_TCS && - (offset < 8 || (offset + (len - i)) > 16)) - return -ECANCELED; - - if (align || (cnt != sizeof (unsigned long))) { - vaddr = isgx_get_epc_page(enclave_page->epc_page); - ret = __edbgrd((void *)((unsigned long) vaddr + offset), - (unsigned long *) data); - isgx_put_epc_page(vaddr); - if (ret) { - isgx_dbg(enclave, "EDBGRD returned %d\n", ret); - return -EFAULT; - } - } - - memcpy(data + align, buf + i, cnt); - vaddr = isgx_get_epc_page(enclave_page->epc_page); - ret = __edbgwr((void *)((unsigned long) vaddr + offset), - (unsigned long *) data); - isgx_put_epc_page(vaddr); - if (ret) { - isgx_dbg(enclave, "EDBGWR returned %d\n", ret); - return -EFAULT; - } - } else { - if (enclave_page->flags & ISGX_ENCLAVE_PAGE_TCS && - (offset + (len - i)) > 72) - return -ECANCELED; - - vaddr = isgx_get_epc_page(enclave_page->epc_page); - ret = __edbgrd((void *)((unsigned long) vaddr + offset), - (unsigned long *) data); - isgx_put_epc_page(vaddr); - if (ret) { - isgx_dbg(enclave, "EDBGRD returned %d\n", ret); - return -EFAULT; - } - - memcpy(buf + i, data + align, cnt); - } - - return cnt; -} - -static int isgx_vma_access(struct vm_area_struct *vma, unsigned long addr, - void *buf, int len, int write) -{ - struct isgx_enclave *enclave = vma->vm_private_data; - struct isgx_enclave_page *enclave_page = NULL; - const char *op_str = write ? "EDBGWR" : "EDBGRD"; - int ret = 0; - int i; - - /* If process was forked, VMA is still there but vm_private_data is set - * to NULL. - */ - if (!enclave) - return -EFAULT; - - if (!(enclave->flags & ISGX_ENCLAVE_DEBUG) || - !(enclave->flags & ISGX_ENCLAVE_INITIALIZED) || - (enclave->flags & ISGX_ENCLAVE_SUSPEND)) - return -EFAULT; - - isgx_dbg(enclave, "%s addr=0x%lx, len=%d\n", op_str, addr, len); - - for (i = 0; i < len; i += ret) { - if (!enclave_page || !((addr + i) & (PAGE_SIZE - 1))) { - if (enclave_page) - enclave_page->flags &= ~ISGX_ENCLAVE_PAGE_RESERVED; - - for (enclave_page = ERR_PTR(-EBUSY); - enclave_page == ERR_PTR(-EBUSY); - enclave_page = isgx_vma_do_fault( - vma, - (addr + i) & PAGE_MASK, - 1)); - - if (IS_ERR(enclave_page)) { - ret = PTR_ERR(enclave_page); - enclave_page = NULL; - break; - } - } - - /* No locks are needed because used fields are immutable after - * intialization. - */ - ret = isgx_vma_access_word(enclave, addr, buf, len, write, - enclave_page, i); - if (ret < 0) - break; - } - - if (enclave_page) - enclave_page->flags &= ~ISGX_ENCLAVE_PAGE_RESERVED; - - return (ret < 0 && ret != -ECANCELED) ? ret : i; -} - -struct vm_operations_struct isgx_vm_ops = { - .close = isgx_vma_close, - .open = isgx_vma_open, - .fault = isgx_vma_fault, - .access = isgx_vma_access, -}; diff --git a/sgx.h b/sgx.h new file mode 100644 index 0000000..01dcfb3 --- /dev/null +++ b/sgx.h @@ -0,0 +1,234 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2016 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * Contact Information: + * Jarkko Sakkinen + * Intel Finland Oy - BIC 0357606-4 - Westendinkatu 7, 02160 Espoo + * + * BSD LICENSE + * + * Copyright(c) 2016 Intel Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: + * + * Jarkko Sakkinen + * Suresh Siddha + * Serge Ayoun + * Shay Katz-zamir + */ + +#ifndef __ARCH_INTEL_SGX_H__ +#define __ARCH_INTEL_SGX_H__ + +#include "sgx_user.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "sgx_arch.h" + +#define SGX_EINIT_SPIN_COUNT 20 +#define SGX_EINIT_SLEEP_COUNT 50 +#define SGX_EINIT_SLEEP_TIME 20 + +#define SGX_VA_SLOT_COUNT 512 + +struct sgx_va_page { + struct sgx_epc_page *epc_page; + DECLARE_BITMAP(slots, SGX_VA_SLOT_COUNT); + struct list_head list; +}; + +static inline unsigned int sgx_alloc_va_slot(struct sgx_va_page *page) +{ + int slot = find_first_zero_bit(page->slots, SGX_VA_SLOT_COUNT); + + if (slot < SGX_VA_SLOT_COUNT) + set_bit(slot, page->slots); + + return slot << 3; +} + +static inline void sgx_free_va_slot(struct sgx_va_page *page, + unsigned int offset) +{ + clear_bit(offset >> 3, page->slots); +} + +enum sgx_encl_page_flags { + SGX_ENCL_PAGE_TCS = BIT(0), + SGX_ENCL_PAGE_RESERVED = BIT(1), +}; + +struct sgx_encl_page { + unsigned long addr; + unsigned int flags; + struct sgx_epc_page *epc_page; + struct list_head load_list; + struct sgx_va_page *va_page; + unsigned int va_offset; +}; + +struct sgx_tgid_ctx { + struct pid *tgid; + struct kref refcount; + struct list_head encl_list; + struct list_head list; +}; + +enum sgx_encl_flags { + SGX_ENCL_INITIALIZED = BIT(0), + SGX_ENCL_DEBUG = BIT(1), + SGX_ENCL_SECS_EVICTED = BIT(2), + SGX_ENCL_SUSPEND = BIT(3), + SGX_ENCL_DEAD = BIT(4), +}; + +struct sgx_encl { + unsigned int flags; + unsigned int secs_child_cnt; + struct mutex lock; + struct mm_struct *mm; + struct file *backing; + struct file *pcmd; + struct list_head load_list; + struct kref refcount; + unsigned long base; + unsigned long size; + struct list_head va_pages; + struct radix_tree_root page_tree; + struct list_head add_page_reqs; + struct work_struct add_page_work; + struct sgx_encl_page secs_page; + struct sgx_tgid_ctx *tgid_ctx; + struct list_head encl_list; + struct mmu_notifier mmu_notifier; +}; + +struct sgx_epc_bank { +#ifdef CONFIG_X86_64 + void *mem; +#endif + unsigned long start; + unsigned long end; +}; + +extern struct workqueue_struct *sgx_add_page_wq; +extern struct sgx_epc_bank sgx_epc_banks[]; +extern int sgx_nr_epc_banks; +extern u64 sgx_encl_size_max_32; +extern u64 sgx_encl_size_max_64; +extern u64 sgx_xfrm_mask; +extern u32 sgx_ssaframesize_tbl[64]; +extern bool sgx_has_sgx2; + +extern const struct vm_operations_struct sgx_vm_ops; +extern atomic_t sgx_nr_pids; + +#define sgx_pr_ratelimited(level, encl, fmt, ...) \ + pr_ ## level ## _ratelimited("intel_sgx: [%d:0x%p] " fmt, \ + pid_nr((encl)->tgid_ctx->tgid), \ + (void *)(encl)->base, ##__VA_ARGS__) + +#define sgx_dbg(encl, fmt, ...) sgx_pr_ratelimited(debug, encl, fmt, ##__VA_ARGS__) +#define sgx_info(encl, fmt, ...) sgx_pr_ratelimited(info, encl, fmt, ##__VA_ARGS__) +#define sgx_warn(encl, fmt, ...) sgx_pr_ratelimited(warn, encl, fmt, ##__VA_ARGS__) +#define sgx_err(encl, fmt, ...) sgx_pr_ratelimited(err, encl, fmt, ##__VA_ARGS__) +#define sgx_crit(encl, fmt, ...) sgx_pr_ratelimited(crit, encl, fmt, ##__VA_ARGS__) + +long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); +#ifdef CONFIG_COMPAT +long sgx_compat_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); +#endif + +/* Utility functions */ +int sgx_test_and_clear_young(struct sgx_encl_page *page, struct sgx_encl *encl); +struct page *sgx_get_backing(struct sgx_encl *encl, + struct sgx_encl_page *entry, + bool pcmd); +void sgx_put_backing(struct page *backing, bool write); +void sgx_insert_pte(struct sgx_encl *encl, + struct sgx_encl_page *encl_page, + struct sgx_epc_page *epc_page, + struct vm_area_struct *vma); +int sgx_eremove(struct sgx_epc_page *epc_page); +struct vm_area_struct *sgx_find_vma(struct sgx_encl *encl, unsigned long addr); +void sgx_zap_tcs_ptes(struct sgx_encl *encl, + struct vm_area_struct *vma); +void sgx_invalidate(struct sgx_encl *encl, bool flush_cpus); +void sgx_flush_cpus(struct sgx_encl *encl); +int sgx_find_encl(struct mm_struct *mm, unsigned long addr, + struct vm_area_struct **vma); + +enum sgx_fault_flags { + SGX_FAULT_RESERVE = BIT(0), +}; + +struct sgx_encl_page *sgx_fault_page(struct vm_area_struct *vma, + unsigned long addr, + unsigned int flags); + +void sgx_encl_release(struct kref *ref); +void sgx_tgid_ctx_release(struct kref *ref); + +extern struct mutex sgx_tgid_ctx_mutex; +extern struct list_head sgx_tgid_ctx_list; +extern struct task_struct *ksgxswapd_tsk; + +enum sgx_alloc_flags { + SGX_ALLOC_ATOMIC = BIT(0), +}; + +int ksgxswapd(void *p); +int sgx_page_cache_init(resource_size_t start, unsigned long size); +void sgx_page_cache_teardown(void); +struct sgx_epc_page *sgx_alloc_page(unsigned int flags); +int sgx_free_page(struct sgx_epc_page *entry, struct sgx_encl *encl); +void *sgx_get_page(struct sgx_epc_page *entry); +void sgx_put_page(void *epc_page_vaddr); + +#endif /* __ARCH_X86_INTEL_SGX_H__ */ diff --git a/sgx_arch.h b/sgx_arch.h new file mode 100644 index 0000000..233d3a6 --- /dev/null +++ b/sgx_arch.h @@ -0,0 +1,354 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2016 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * Contact Information: + * Jarkko Sakkinen + * Intel Finland Oy - BIC 0357606-4 - Westendinkatu 7, 02160 Espoo + * + * BSD LICENSE + * + * Copyright(c) 2016 Intel Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: + * + * Jarkko Sakkinen + * Suresh Siddha + * Serge Ayoun + * Shay Katz-zamir + */ + +#ifndef _ASM_X86_SGX_H +#define _ASM_X86_SGX_H + +#include +#include +#include +#include + +#define SGX_CPUID 0x12 + +enum sgx_page_type { + SGX_PAGE_TYPE_SECS = 0x00, + SGX_PAGE_TYPE_TCS = 0x01, + SGX_PAGE_TYPE_REG = 0x02, + SGX_PAGE_TYPE_VA = 0x03, +}; + +enum sgx_secinfo_flags { + SGX_SECINFO_R = 0x01, + SGX_SECINFO_W = 0x02, + SGX_SECINFO_X = 0x04, + SGX_SECINFO_SECS = 0x000ULL, + SGX_SECINFO_TCS = 0x100ULL, + SGX_SECINFO_REG = 0x200ULL, +}; + +struct sgx_secinfo { + u64 flags; + u64 reserved[7]; +} __aligned(128); + +struct sgx_einittoken { + u32 valid; + u8 reserved1[206]; + u16 isvsvnle; + u8 reserved2[92]; +} __aligned(512); + +enum isgx_secs_attributes { + SGX_SECS_A_DEBUG = BIT_ULL(1), + SGX_SECS_A_MODE64BIT = BIT_ULL(2), + SGX_SECS_A_PROVISION_KEY = BIT_ULL(4), + SGX_SECS_A_LICENSE_KEY = BIT_ULL(5), + SGX_SECS_A_RESERVED_MASK = (BIT_ULL(0) | + BIT_ULL(3) | + GENMASK_ULL(63, 6)), +}; + +#define SGX_SECS_RESERVED1_SIZE 28 +#define SGX_SECS_RESERVED2_SIZE 32 +#define SGX_SECS_RESERVED3_SIZE 96 +#define SGX_SECS_RESERVED4_SIZE 3836 + +struct sgx_secs { + u64 size; + u64 base; + u32 ssaframesize; + uint8_t reserved1[SGX_SECS_RESERVED1_SIZE]; + u64 flags; + u64 xfrm; + u32 mrenclave[8]; + uint8_t reserved2[SGX_SECS_RESERVED2_SIZE]; + u32 mrsigner[8]; + uint8_t reserved3[SGX_SECS_RESERVED3_SIZE]; + u16 isvvprodid; + u16 isvsvn; + uint8_t reserved[SGX_SECS_RESERVED4_SIZE]; +}; + +struct sgx_tcs { + u64 state; + u64 flags; + u64 ossa; + u32 cssa; + u32 nssa; + u64 oentry; + u64 aep; + u64 ofsbase; + u64 ogsbase; + u32 fslimit; + u32 gslimit; + u64 reserved[503]; +}; + +enum sgx_secinfo_masks { + SGX_SECINFO_PERMISSION_MASK = GENMASK_ULL(2, 0), + SGX_SECINFO_PAGE_TYPE_MASK = GENMASK_ULL(15, 8), + SGX_SECINFO_RESERVED_MASK = (GENMASK_ULL(7, 3) | + GENMASK_ULL(63, 16)), +}; + +struct sgx_pcmd { + struct sgx_secinfo secinfo; + u64 enclave_id; + u8 reserved[40]; + u8 mac[16]; +}; + +struct sgx_page_info { + u64 linaddr; + u64 srcpge; + union { + u64 secinfo; + u64 pcmd; + }; + u64 secs; +} __aligned(32); + +#define SIGSTRUCT_SIZE 1808 +#define EINITTOKEN_SIZE 304 + +enum { + ECREATE = 0x0, + EADD = 0x1, + EINIT = 0x2, + EREMOVE = 0x3, + EDGBRD = 0x4, + EDGBWR = 0x5, + EEXTEND = 0x6, + ELDU = 0x8, + EBLOCK = 0x9, + EPA = 0xA, + EWB = 0xB, + ETRACK = 0xC, + EAUG = 0xD, + EMODPR = 0xE, + EMODT = 0xF, +}; + +#define __encls_ret(rax, rbx, rcx, rdx) \ + ({ \ + int ret; \ + asm volatile( \ + "1: .byte 0x0f, 0x01, 0xcf;\n\t" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: jmp 2b\n" \ + ".previous\n" \ + _ASM_EXTABLE(1b, 3b) \ + : "=a"(ret) \ + : "a"(rax), "b"(rbx), "c"(rcx), "d"(rdx) \ + : "memory"); \ + ret; \ + }) + +#ifdef CONFIG_X86_64 +#define __encls(rax, rbx, rcx, rdx...) \ + ({ \ + int ret; \ + asm volatile( \ + "1: .byte 0x0f, 0x01, 0xcf;\n\t" \ + " xor %%eax,%%eax;\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: movq $-1,%%rax\n" \ + " jmp 2b\n" \ + ".previous\n" \ + _ASM_EXTABLE(1b, 3b) \ + : "=a"(ret), "=b"(rbx), "=c"(rcx) \ + : "a"(rax), "b"(rbx), "c"(rcx), rdx \ + : "memory"); \ + ret; \ + }) +#else +#define __encls(rax, rbx, rcx, rdx...) \ + ({ \ + int ret; \ + asm volatile( \ + "1: .byte 0x0f, 0x01, 0xcf;\n\t" \ + " xor %%eax,%%eax;\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: mov $-1,%%eax\n" \ + " jmp 2b\n" \ + ".previous\n" \ + _ASM_EXTABLE(1b, 3b) \ + : "=a"(ret), "=b"(rbx), "=c"(rcx) \ + : "a"(rax), "b"(rbx), "c"(rcx), rdx \ + : "memory"); \ + ret; \ + }) +#endif + +static inline unsigned long __ecreate(struct sgx_page_info *pginfo, void *secs) +{ + return __encls(ECREATE, pginfo, secs, "d"(0)); +} + +static inline int __eextend(void *secs, void *epc) +{ + return __encls(EEXTEND, secs, epc, "d"(0)); +} + +static inline int __eadd(struct sgx_page_info *pginfo, void *epc) +{ + return __encls(EADD, pginfo, epc, "d"(0)); +} + +static inline int __einit(void *sigstruct, struct sgx_einittoken *einittoken, + void *secs) +{ + return __encls_ret(EINIT, sigstruct, secs, einittoken); +} + +static inline int __eremove(void *epc) +{ + unsigned long rbx = 0; + unsigned long rdx = 0; + + return __encls_ret(EREMOVE, rbx, epc, rdx); +} + +static inline int __edbgwr(void *epc, unsigned long *data) +{ + return __encls(EDGBWR, *data, epc, "d"(0)); +} + +static inline int __edbgrd(void *epc, unsigned long *data) +{ + unsigned long rbx = 0; + int ret; + + ret = __encls(EDGBRD, rbx, epc, "d"(0)); + if (!ret) + *(unsigned long *) data = rbx; + + return ret; +} + +static inline int __etrack(void *epc) +{ + unsigned long rbx = 0; + unsigned long rdx = 0; + + return __encls_ret(ETRACK, rbx, epc, rdx); +} + +static inline int __eldu(unsigned long rbx, unsigned long rcx, + unsigned long rdx) +{ + return __encls_ret(ELDU, rbx, rcx, rdx); +} + +static inline int __eblock(unsigned long rcx) +{ + unsigned long rbx = 0; + unsigned long rdx = 0; + + return __encls_ret(EBLOCK, rbx, rcx, rdx); +} + +static inline int __epa(void *epc) +{ + unsigned long rbx = SGX_PAGE_TYPE_VA; + + return __encls(EPA, rbx, epc, "d"(0)); +} + +static inline int __ewb(struct sgx_page_info *pginfo, void *epc, void *va) +{ + return __encls_ret(EWB, pginfo, epc, va); +} + +static inline int __eaug(struct sgx_page_info *pginfo, void *epc) +{ + return __encls(EAUG, pginfo, epc, "d"(0)); +} + +static inline int __emodpr(struct sgx_secinfo *secinfo, void *epc) +{ + unsigned long rdx = 0; + + return __encls_ret(EMODPR, secinfo, epc, rdx); +} + +static inline int __emodt(struct sgx_secinfo *secinfo, void *epc) +{ + unsigned long rdx = 0; + + return __encls_ret(EMODT, secinfo, epc, rdx); +} + +struct sgx_encl; + +struct sgx_epc_page { + resource_size_t pa; + struct list_head free_list; +}; + +extern struct sgx_epc_page *sgx_alloc_page(unsigned int flags); +extern int sgx_free_page(struct sgx_epc_page *entry, struct sgx_encl *encl); +extern void *sgx_get_page(struct sgx_epc_page *entry); +extern void sgx_put_page(void *epc_page_vaddr); + +#endif /* _ASM_X86_SGX_H */ diff --git a/sgx_ioctl.c b/sgx_ioctl.c new file mode 100644 index 0000000..e1db58c --- /dev/null +++ b/sgx_ioctl.c @@ -0,0 +1,963 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2016 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * Contact Information: + * Jarkko Sakkinen + * Intel Finland Oy - BIC 0357606-4 - Westendinkatu 7, 02160 Espoo + * + * BSD LICENSE + * + * Copyright(c) 2016 Intel Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: + * + * Jarkko Sakkinen + * Suresh Siddha + * Serge Ayoun + * Shay Katz-zamir + * Sean Christopherson + */ + +#include "sgx.h" +#include +#include +#include +#include +#include +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)) + #include +#else + #include +#endif +#include +#include +#include + +struct sgx_add_page_req { + struct sgx_encl *encl; + struct sgx_encl_page *encl_page; + struct sgx_secinfo secinfo; + u16 mrmask; + struct list_head list; +}; + +static u16 sgx_isvsvnle_min; +atomic_t sgx_nr_pids = ATOMIC_INIT(0); + +static struct sgx_tgid_ctx *sgx_find_tgid_ctx(struct pid *tgid) +{ + struct sgx_tgid_ctx *ctx; + + list_for_each_entry(ctx, &sgx_tgid_ctx_list, list) + if (pid_nr(ctx->tgid) == pid_nr(tgid)) + return ctx; + + return NULL; +} + +static int sgx_add_to_tgid_ctx(struct sgx_encl *encl) +{ + struct sgx_tgid_ctx *ctx; + struct pid *tgid = get_pid(task_tgid(current)); + + mutex_lock(&sgx_tgid_ctx_mutex); + + ctx = sgx_find_tgid_ctx(tgid); + if (ctx) { + if (kref_get_unless_zero(&ctx->refcount)) { + encl->tgid_ctx = ctx; + mutex_unlock(&sgx_tgid_ctx_mutex); + put_pid(tgid); + return 0; + } + else + list_del_init(&ctx->list); + } + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) { + mutex_unlock(&sgx_tgid_ctx_mutex); + put_pid(tgid); + return -ENOMEM; + } + + ctx->tgid = tgid; + kref_init(&ctx->refcount); + INIT_LIST_HEAD(&ctx->encl_list); + + list_add(&ctx->list, &sgx_tgid_ctx_list); + atomic_inc(&sgx_nr_pids); + + encl->tgid_ctx = ctx; + + mutex_unlock(&sgx_tgid_ctx_mutex); + return 0; +} + +void sgx_tgid_ctx_release(struct kref *ref) +{ + struct sgx_tgid_ctx *pe = + container_of(ref, struct sgx_tgid_ctx, refcount); + mutex_lock(&sgx_tgid_ctx_mutex); + list_del(&pe->list); + atomic_dec(&sgx_nr_pids); + mutex_unlock(&sgx_tgid_ctx_mutex); + put_pid(pe->tgid); + kfree(pe); +} + +static int sgx_find_and_get_encl(unsigned long addr, struct sgx_encl **encl) +{ + struct mm_struct *mm = current->mm; + struct vm_area_struct *vma; + int ret; + + down_read(&mm->mmap_sem); + + ret = sgx_find_encl(mm, addr, &vma); + if (!ret) { + *encl = vma->vm_private_data; + kref_get(&(*encl)->refcount); + } + + up_read(&mm->mmap_sem); + + return ret; +} + +static int sgx_measure(struct sgx_epc_page *secs_page, + struct sgx_epc_page *epc_page, + u16 mrmask) +{ + void *secs; + void *epc; + int ret = 0; + int i, j; + + for (i = 0, j = 1; i < 0x1000 && !ret; i += 0x100, j <<= 1) { + if (!(j & mrmask)) + continue; + + secs = sgx_get_page(secs_page); + epc = sgx_get_page(epc_page); + + ret = __eextend(secs, (void *)((unsigned long)epc + i)); + + sgx_put_page(epc); + sgx_put_page(secs); + } + + return ret; +} + +static int sgx_add_page(struct sgx_epc_page *secs_page, + struct sgx_epc_page *epc_page, + unsigned long linaddr, + struct sgx_secinfo *secinfo, + struct page *backing) +{ + struct sgx_page_info pginfo; + void *epc_page_vaddr; + int ret; + + pginfo.srcpge = (unsigned long)kmap_atomic(backing); + pginfo.secs = (unsigned long)sgx_get_page(secs_page); + epc_page_vaddr = sgx_get_page(epc_page); + + pginfo.linaddr = linaddr; + pginfo.secinfo = (unsigned long)secinfo; + ret = __eadd(&pginfo, epc_page_vaddr); + + sgx_put_page(epc_page_vaddr); + sgx_put_page((void *)(unsigned long)pginfo.secs); + kunmap_atomic((void *)(unsigned long)pginfo.srcpge); + + return ret; +} + +static bool sgx_process_add_page_req(struct sgx_add_page_req *req) +{ + struct page *backing; + struct sgx_epc_page *epc_page; + struct sgx_encl_page *encl_page = req->encl_page; + struct sgx_encl *encl = req->encl; + struct vm_area_struct *vma; + int ret; + + epc_page = sgx_alloc_page(0); + if (IS_ERR(epc_page)) + return false; + + down_read(&encl->mm->mmap_sem); + + mutex_lock(&encl->lock); + + if (encl->flags & SGX_ENCL_DEAD) + goto out; + + if (sgx_find_encl(encl->mm, encl_page->addr, &vma)) + goto out; + + backing = sgx_get_backing(encl, encl_page, false); + if (IS_ERR(backing)) + goto out; + + /* Do not race with do_exit() */ + if (!atomic_read(&encl->mm->mm_users)) { + sgx_put_backing(backing, 0); + goto out; + } + + ret = vm_insert_pfn(vma, encl_page->addr, PFN_DOWN(epc_page->pa)); + if (ret) + goto out; + + ret = sgx_add_page(encl->secs_page.epc_page, epc_page, + encl_page->addr, &req->secinfo, backing); + + sgx_put_backing(backing, 0); + if (ret) { + sgx_warn(encl, "EADD returned %d\n", ret); + zap_vma_ptes(vma, encl_page->addr, PAGE_SIZE); + goto out; + } + + encl->secs_child_cnt++; + + ret = sgx_measure(encl->secs_page.epc_page, epc_page, req->mrmask); + if (ret) { + sgx_warn(encl, "EEXTEND returned %d\n", ret); + zap_vma_ptes(vma, encl_page->addr, PAGE_SIZE); + goto out; + } + + encl_page->epc_page = epc_page; + sgx_test_and_clear_young(encl_page, encl); + list_add_tail(&encl_page->load_list, &encl->load_list); + + mutex_unlock(&encl->lock); + up_read(&encl->mm->mmap_sem); + return true; +out: + sgx_free_page(epc_page, encl); + mutex_unlock(&encl->lock); + up_read(&encl->mm->mmap_sem); + return false; +} + +static void sgx_add_page_worker(struct work_struct *work) +{ + struct sgx_encl *encl; + struct sgx_add_page_req *req; + bool skip_rest = false; + bool is_empty = false; + + encl = container_of(work, struct sgx_encl, add_page_work); + + do { + schedule(); + + if (encl->flags & SGX_ENCL_DEAD) + skip_rest = true; + + mutex_lock(&encl->lock); + req = list_first_entry(&encl->add_page_reqs, + struct sgx_add_page_req, list); + list_del(&req->list); + is_empty = list_empty(&encl->add_page_reqs); + mutex_unlock(&encl->lock); + + if (!skip_rest) { + if (!sgx_process_add_page_req(req)) { + skip_rest = true; + sgx_dbg(encl, "EADD failed 0x%p\n", + (void *)req->encl_page->addr); + } + } + + kfree(req); + } while (!kref_put(&encl->refcount, sgx_encl_release) && + !is_empty); +} + +static int sgx_validate_secs(const struct sgx_secs *secs) +{ + u32 needed_ssaframesize = 1; + u32 tmp; + int i; + + if (secs->flags & SGX_SECS_A_RESERVED_MASK) + return -EINVAL; + + if (secs->flags & SGX_SECS_A_MODE64BIT) { +#ifdef CONFIG_X86_64 + if (secs->size > sgx_encl_size_max_64) + return -EINVAL; +#else + return -EINVAL; +#endif + } else { + /* On 64-bit architecture allow 32-bit encls only in + * the compatibility mode. + */ +#ifdef CONFIG_X86_64 + if (!test_thread_flag(TIF_ADDR32)) + return -EINVAL; +#endif + if (secs->size > sgx_encl_size_max_32) + return -EINVAL; + } + + if ((secs->xfrm & 0x3) != 0x3 || (secs->xfrm & ~sgx_xfrm_mask)) + return -EINVAL; + + /* Check that BNDREGS and BNDCSR are equal. */ + if (((secs->xfrm >> 3) & 1) != ((secs->xfrm >> 4) & 1)) + return -EINVAL; + + for (i = 2; i < 64; i++) { + tmp = sgx_ssaframesize_tbl[i]; + if (((1 << i) & secs->xfrm) && (tmp > needed_ssaframesize)) + needed_ssaframesize = tmp; + } + + if (!secs->ssaframesize || !needed_ssaframesize || + needed_ssaframesize > secs->ssaframesize) + return -EINVAL; + + /* Must be power of two */ + if (secs->size == 0 || (secs->size & (secs->size - 1)) != 0) + return -EINVAL; + + for (i = 0; i < SGX_SECS_RESERVED1_SIZE; i++) + if (secs->reserved1[i]) + return -EINVAL; + + for (i = 0; i < SGX_SECS_RESERVED2_SIZE; i++) + if (secs->reserved2[i]) + return -EINVAL; + + for (i = 0; i < SGX_SECS_RESERVED3_SIZE; i++) + if (secs->reserved3[i]) + return -EINVAL; + + for (i = 0; i < SGX_SECS_RESERVED4_SIZE; i++) + if (secs->reserved[i]) + return -EINVAL; + + return 0; +} + +static int sgx_init_page(struct sgx_encl *encl, + struct sgx_encl_page *entry, + unsigned long addr) +{ + struct sgx_va_page *va_page; + struct sgx_epc_page *epc_page = NULL; + unsigned int va_offset = PAGE_SIZE; + void *vaddr; + int ret = 0; + + list_for_each_entry(va_page, &encl->va_pages, list) { + va_offset = sgx_alloc_va_slot(va_page); + if (va_offset < PAGE_SIZE) + break; + } + + if (va_offset == PAGE_SIZE) { + va_page = kzalloc(sizeof(*va_page), GFP_KERNEL); + if (!va_page) + return -ENOMEM; + + epc_page = sgx_alloc_page(0); + if (IS_ERR(epc_page)) { + kfree(va_page); + return PTR_ERR(epc_page); + } + + vaddr = sgx_get_page(epc_page); + if (!vaddr) { + sgx_warn(encl, "kmap of a new VA page failed %d\n", + ret); + sgx_free_page(epc_page, encl); + kfree(va_page); + return -EFAULT; + } + + ret = __epa(vaddr); + sgx_put_page(vaddr); + + if (ret) { + sgx_warn(encl, "EPA returned %d\n", ret); + sgx_free_page(epc_page, encl); + kfree(va_page); + return -EFAULT; + } + + va_page->epc_page = epc_page; + va_offset = sgx_alloc_va_slot(va_page); + + mutex_lock(&encl->lock); + list_add(&va_page->list, &encl->va_pages); + mutex_unlock(&encl->lock); + } + + entry->va_page = va_page; + entry->va_offset = va_offset; + entry->addr = addr; + + return 0; +} + +static void sgx_mmu_notifier_release(struct mmu_notifier *mn, + struct mm_struct *mm) +{ + struct sgx_encl *encl = + container_of(mn, struct sgx_encl, mmu_notifier); + + mutex_lock(&encl->lock); + encl->flags |= SGX_ENCL_DEAD; + mutex_unlock(&encl->lock); +} + +static const struct mmu_notifier_ops sgx_mmu_notifier_ops = { + .release = sgx_mmu_notifier_release, +}; + +/** + * sgx_ioc_enclave_create - handler for SGX_IOC_ENCLAVE_CREATE + * @filep: open file to /dev/sgx + * @cmd: the command value + * @arg: pointer to the struct sgx_enclave_create + * + * Creates meta-data for an enclave and executes ENCLS(ECREATE) + */ +static long sgx_ioc_enclave_create(struct file *filep, unsigned int cmd, + unsigned long arg) +{ + struct sgx_enclave_create *createp = (struct sgx_enclave_create *)arg; + unsigned long src = (unsigned long)createp->src; + struct sgx_page_info pginfo; + struct sgx_secinfo secinfo; + struct sgx_encl *encl = NULL; + struct sgx_secs *secs = NULL; + struct sgx_epc_page *secs_epc; + struct vm_area_struct *vma; + void *secs_vaddr = NULL; + struct file *backing; + struct file *pcmd; + long ret; + + secs = kzalloc(sizeof(*secs), GFP_KERNEL); + if (!secs) + return -ENOMEM; + + ret = copy_from_user(secs, (void __user *)src, sizeof(*secs)); + if (ret) { + kfree(secs); + return ret; + } + + if (sgx_validate_secs(secs)) { + kfree(secs); + return -EINVAL; + } + + backing = shmem_file_setup("dev/sgx", secs->size + PAGE_SIZE, + VM_NORESERVE); + if (IS_ERR(backing)) { + ret = PTR_ERR(backing); + goto out; + } + + pcmd = shmem_file_setup("dev/sgx", + (secs->size + PAGE_SIZE) >> 5, + VM_NORESERVE); + if (IS_ERR(pcmd)) { + fput(backing); + ret = PTR_ERR(pcmd); + goto out; + } + + encl = kzalloc(sizeof(*encl), GFP_KERNEL); + if (!encl) { + fput(backing); + fput(pcmd); + ret = -ENOMEM; + goto out; + } + + kref_init(&encl->refcount); + INIT_LIST_HEAD(&encl->add_page_reqs); + INIT_LIST_HEAD(&encl->va_pages); + INIT_RADIX_TREE(&encl->page_tree, GFP_KERNEL); + INIT_LIST_HEAD(&encl->load_list); + INIT_LIST_HEAD(&encl->encl_list); + mutex_init(&encl->lock); + INIT_WORK(&encl->add_page_work, sgx_add_page_worker); + + encl->mm = current->mm; + encl->base = secs->base; + encl->size = secs->size; + encl->backing = backing; + encl->pcmd = pcmd; + + secs_epc = sgx_alloc_page(0); + if (IS_ERR(secs_epc)) { + ret = PTR_ERR(secs_epc); + secs_epc = NULL; + goto out; + } + + ret = sgx_add_to_tgid_ctx(encl); + if (ret) + goto out; + + ret = sgx_init_page(encl, &encl->secs_page, + encl->base + encl->size); + if (ret) + goto out; + + secs_vaddr = sgx_get_page(secs_epc); + + pginfo.srcpge = (unsigned long)secs; + pginfo.linaddr = 0; + pginfo.secinfo = (unsigned long)&secinfo; + pginfo.secs = 0; + memset(&secinfo, 0, sizeof(secinfo)); + ret = __ecreate((void *)&pginfo, secs_vaddr); + + sgx_put_page(secs_vaddr); + + if (ret) { + sgx_dbg(encl, "ECREATE returned %ld\n", ret); + ret = -EFAULT; + goto out; + } + + encl->secs_page.epc_page = secs_epc; + createp->src = (unsigned long)encl->base; + + if (secs->flags & SGX_SECS_A_DEBUG) + encl->flags |= SGX_ENCL_DEBUG; + + + encl->mmu_notifier.ops = &sgx_mmu_notifier_ops; + ret = mmu_notifier_register(&encl->mmu_notifier, encl->mm); + if (ret) { + encl->mmu_notifier.ops = NULL; + goto out; + } + + down_read(¤t->mm->mmap_sem); + vma = find_vma(current->mm, secs->base); + if (!vma || vma->vm_ops != &sgx_vm_ops || + vma->vm_start != secs->base || + vma->vm_end != (secs->base + secs->size)) { + ret = -EINVAL; + up_read(¤t->mm->mmap_sem); + goto out; + } + vma->vm_private_data = encl; + up_read(¤t->mm->mmap_sem); + + mutex_lock(&sgx_tgid_ctx_mutex); + list_add_tail(&encl->encl_list, &encl->tgid_ctx->encl_list); + mutex_unlock(&sgx_tgid_ctx_mutex); + +out: + if (ret && encl) + kref_put(&encl->refcount, sgx_encl_release); + kfree(secs); + return ret; +} + +static int sgx_validate_secinfo(struct sgx_secinfo *secinfo) +{ + u64 perm = secinfo->flags & SGX_SECINFO_PERMISSION_MASK; + u64 page_type = secinfo->flags & SGX_SECINFO_PAGE_TYPE_MASK; + int i; + + if ((secinfo->flags & SGX_SECINFO_RESERVED_MASK) || + ((perm & SGX_SECINFO_W) && !(perm & SGX_SECINFO_R)) || + (page_type != SGX_SECINFO_TCS && + page_type != SGX_SECINFO_REG)) + return -EINVAL; + + for (i = 0; i < sizeof(secinfo->reserved) / sizeof(u64); i++) + if (secinfo->reserved[i]) + return -EINVAL; + + return 0; +} + +static int sgx_validate_tcs(struct sgx_tcs *tcs) +{ + int i; + + /* If FLAGS is not zero, ECALL will fail. */ + if ((tcs->flags != 0) || + (tcs->ossa & (PAGE_SIZE - 1)) || + (tcs->ofsbase & (PAGE_SIZE - 1)) || + (tcs->ogsbase & (PAGE_SIZE - 1)) || + ((tcs->fslimit & 0xFFF) != 0xFFF) || + ((tcs->gslimit & 0xFFF) != 0xFFF)) + return -EINVAL; + + for (i = 0; i < sizeof(tcs->reserved) / sizeof(u64); i++) + if (tcs->reserved[i]) + return -EINVAL; + + return 0; +} + +static int __encl_add_page(struct sgx_encl *encl, + struct sgx_encl_page *encl_page, + struct sgx_enclave_add_page *addp, + struct sgx_secinfo *secinfo) +{ + u64 page_type = secinfo->flags & SGX_SECINFO_PAGE_TYPE_MASK; + unsigned long src = (unsigned long)addp->src; + struct sgx_tcs *tcs; + struct page *backing; + struct sgx_add_page_req *req = NULL; + int ret; + int empty; + void *user_vaddr; + void *tmp_vaddr; + struct page *tmp_page; + + tmp_page = alloc_page(GFP_HIGHUSER); + if (!tmp_page) + return -ENOMEM; + + tmp_vaddr = kmap(tmp_page); + ret = copy_from_user((void *)tmp_vaddr, (void __user *)src, PAGE_SIZE); + kunmap(tmp_page); + if (ret) { + __free_page(tmp_page); + return -EFAULT; + } + + if (sgx_validate_secinfo(secinfo)) { + __free_page(tmp_page); + return -EINVAL; + } + + if (page_type == SGX_SECINFO_TCS) { + tcs = (struct sgx_tcs *)kmap(tmp_page); + ret = sgx_validate_tcs(tcs); + kunmap(tmp_page); + if (ret) { + __free_page(tmp_page); + return ret; + } + } + + ret = sgx_init_page(encl, encl_page, addp->addr); + if (ret) { + __free_page(tmp_page); + return -EINVAL; + } + + mutex_lock(&encl->lock); + + if (encl->flags & (SGX_ENCL_INITIALIZED | SGX_ENCL_DEAD)) { + ret = -EINVAL; + goto out; + } + + if (radix_tree_lookup(&encl->page_tree, addp->addr >> PAGE_SHIFT)) { + ret = -EEXIST; + goto out; + } + + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) { + ret = -ENOMEM; + goto out; + } + + backing = sgx_get_backing(encl, encl_page, false); + if (IS_ERR((void *)backing)) { + ret = PTR_ERR((void *)backing); + goto out; + } + + ret = radix_tree_insert(&encl->page_tree, encl_page->addr >> PAGE_SHIFT, + encl_page); + if (ret) { + sgx_put_backing(backing, false /* write */); + goto out; + } + + user_vaddr = kmap(backing); + tmp_vaddr = kmap(tmp_page); + memcpy(user_vaddr, tmp_vaddr, PAGE_SIZE); + kunmap(backing); + kunmap(tmp_page); + + if (page_type == SGX_SECINFO_TCS) + encl_page->flags |= SGX_ENCL_PAGE_TCS; + + memcpy(&req->secinfo, secinfo, sizeof(*secinfo)); + + req->encl = encl; + req->encl_page = encl_page; + req->mrmask = addp->mrmask; + empty = list_empty(&encl->add_page_reqs); + kref_get(&encl->refcount); + list_add_tail(&req->list, &encl->add_page_reqs); + if (empty) + queue_work(sgx_add_page_wq, &encl->add_page_work); + + sgx_put_backing(backing, true /* write */); +out: + + if (ret) { + kfree(req); + sgx_free_va_slot(encl_page->va_page, + encl_page->va_offset); + } + + mutex_unlock(&encl->lock); + __free_page(tmp_page); + return ret; +} + +/** + * sgx_ioc_enclave_add_page - handler for SGX_IOC_ENCLAVE_ADD_PAGE + * + * @filep: open file to /dev/sgx + * @cmd: the command value + * @arg: pointer to the struct sgx_enclave_add_page + * + * Creates meta-data for an enclave page and enqueues ENCLS(EADD) that will + * be processed by a worker thread later on. + */ +static long sgx_ioc_enclave_add_page(struct file *filep, unsigned int cmd, + unsigned long arg) +{ + struct sgx_enclave_add_page *addp = (void *)arg; + unsigned long secinfop = (unsigned long)addp->secinfo; + struct sgx_encl *encl; + struct sgx_encl_page *page; + struct sgx_secinfo secinfo; + int ret; + + if (addp->addr & (PAGE_SIZE - 1)) + return -EINVAL; + + if (copy_from_user(&secinfo, (void __user *)secinfop, sizeof(secinfo))) + return -EFAULT; + + ret = sgx_find_and_get_encl(addp->addr, &encl); + if (ret) + return ret; + + if (addp->addr < encl->base || + addp->addr > (encl->base + encl->size - PAGE_SIZE)) { + kref_put(&encl->refcount, sgx_encl_release); + return -EINVAL; + } + + page = kzalloc(sizeof(*page), GFP_KERNEL); + if (!page) { + kref_put(&encl->refcount, sgx_encl_release); + return -ENOMEM; + } + + ret = __encl_add_page(encl, page, addp, &secinfo); + kref_put(&encl->refcount, sgx_encl_release); + + if (ret) + kfree(page); + + return ret; +} + +static int __sgx_encl_init(struct sgx_encl *encl, char *sigstruct, + struct sgx_einittoken *einittoken) +{ + int ret = SGX_UNMASKED_EVENT; + struct sgx_epc_page *secs_epc = encl->secs_page.epc_page; + void *secs_va = NULL; + int i; + int j; + + if (einittoken->valid && einittoken->isvsvnle < sgx_isvsvnle_min) + return SGX_LE_ROLLBACK; + + for (i = 0; i < SGX_EINIT_SLEEP_COUNT; i++) { + for (j = 0; j < SGX_EINIT_SPIN_COUNT; j++) { + mutex_lock(&encl->lock); + secs_va = sgx_get_page(secs_epc); + ret = __einit(sigstruct, einittoken, secs_va); + sgx_put_page(secs_va); + mutex_unlock(&encl->lock); + if (ret == SGX_UNMASKED_EVENT) + continue; + else + break; + } + + if (ret != SGX_UNMASKED_EVENT) + goto out; + + msleep_interruptible(SGX_EINIT_SLEEP_TIME); + if (signal_pending(current)) + return -EINTR; + } + +out: + if (ret) { + sgx_dbg(encl, "EINIT returned %d\n", ret); + } else { + encl->flags |= SGX_ENCL_INITIALIZED; + + if (einittoken->isvsvnle > sgx_isvsvnle_min) + sgx_isvsvnle_min = einittoken->isvsvnle; + } + + return ret; +} + +/** + * sgx_ioc_enclave_init - handler for SGX_IOC_ENCLAVE_INIT + * + * @filep: open file to /dev/sgx + * @cmd: the command value + * @arg: pointer to the struct sgx_enclave_init + * + * Flushes the remaining enqueued ENCLS(EADD) operations and executes + * ENCLS(EINIT). Does a number of retries because EINIT might fail because of an + * interrupt storm. + */ +static long sgx_ioc_enclave_init(struct file *filep, unsigned int cmd, + unsigned long arg) +{ + struct sgx_enclave_init *initp = (struct sgx_enclave_init *)arg; + unsigned long sigstructp = (unsigned long)initp->sigstruct; + unsigned long einittokenp = (unsigned long)initp->einittoken; + unsigned long encl_id = initp->addr; + char *sigstruct; + struct sgx_einittoken *einittoken; + struct sgx_encl *encl; + struct page *initp_page; + int ret; + + initp_page = alloc_page(GFP_HIGHUSER); + if (!initp_page) + return -ENOMEM; + + sigstruct = kmap(initp_page); + einittoken = (struct sgx_einittoken *) + ((unsigned long)sigstruct + PAGE_SIZE / 2); + + ret = copy_from_user(sigstruct, (void __user *)sigstructp, + SIGSTRUCT_SIZE); + if (ret) + goto out_free_page; + + ret = copy_from_user(einittoken, (void __user *)einittokenp, + EINITTOKEN_SIZE); + if (ret) + goto out_free_page; + + ret = sgx_find_and_get_encl(encl_id, &encl); + if (ret) + goto out_free_page; + + mutex_lock(&encl->lock); + if (encl->flags & SGX_ENCL_INITIALIZED) { + ret = -EINVAL; + mutex_unlock(&encl->lock); + goto out; + } + mutex_unlock(&encl->lock); + + flush_work(&encl->add_page_work); + + ret = __sgx_encl_init(encl, sigstruct, einittoken); +out: + kref_put(&encl->refcount, sgx_encl_release); +out_free_page: + kunmap(initp_page); + __free_page(initp_page); + return ret; +} + +typedef long (*sgx_ioc_t)(struct file *filep, unsigned int cmd, + unsigned long arg); + +long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) +{ + char data[256]; + sgx_ioc_t handler = NULL; + long ret; + + switch (cmd) { + case SGX_IOC_ENCLAVE_CREATE: + handler = sgx_ioc_enclave_create; + break; + case SGX_IOC_ENCLAVE_ADD_PAGE: + handler = sgx_ioc_enclave_add_page; + break; + case SGX_IOC_ENCLAVE_INIT: + handler = sgx_ioc_enclave_init; + break; + default: + return -ENOIOCTLCMD; + } + + if (copy_from_user(data, (void __user *)arg, _IOC_SIZE(cmd))) + return -EFAULT; + + ret = handler(filep, cmd, (unsigned long)((void *)data)); + if (!ret && (cmd & IOC_OUT)) { + if (copy_to_user((void __user *)arg, data, _IOC_SIZE(cmd))) + return -EFAULT; + } + + return ret; +} diff --git a/sgx_main.c b/sgx_main.c new file mode 100644 index 0000000..70b95c1 --- /dev/null +++ b/sgx_main.c @@ -0,0 +1,417 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2016 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * Contact Information: + * Jarkko Sakkinen + * Intel Finland Oy - BIC 0357606-4 - Westendinkatu 7, 02160 Espoo + * + * BSD LICENSE + * + * Copyright(c) 2016 Intel Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: + * + * Jarkko Sakkinen + * Suresh Siddha + * Serge Ayoun + * Shay Katz-zamir + * Sean Christopherson + */ + +#include "sgx.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_DESCRIPTION "Intel SGX Driver" +#define DRV_VERSION "0.10" + +#define ENCL_SIZE_MAX_64 (64ULL * 1024ULL * 1024ULL * 1024ULL) +#define ENCL_SIZE_MAX_32 (2ULL * 1024ULL * 1024ULL * 1024ULL) + +MODULE_DESCRIPTION(DRV_DESCRIPTION); +MODULE_AUTHOR("Jarkko Sakkinen "); +MODULE_VERSION(DRV_VERSION); +#ifndef X86_FEATURE_SGX +#define X86_FEATURE_SGX (9 * 32 + 2) +#endif + +/* + * Global data. + */ + +struct workqueue_struct *sgx_add_page_wq; +#define SGX_MAX_EPC_BANKS 8 +struct sgx_epc_bank sgx_epc_banks[SGX_MAX_EPC_BANKS]; +int sgx_nr_epc_banks; +u64 sgx_encl_size_max_32 = ENCL_SIZE_MAX_32; +u64 sgx_encl_size_max_64 = ENCL_SIZE_MAX_64; +u64 sgx_xfrm_mask = 0x3; +u32 sgx_ssaframesize_tbl[64]; +bool sgx_has_sgx2; + +#ifdef CONFIG_COMPAT +long sgx_compat_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) +{ + return sgx_ioctl(filep, cmd, arg); +} +#endif + +static int sgx_mmap(struct file *file, struct vm_area_struct *vma) +{ + vma->vm_ops = &sgx_vm_ops; + vma->vm_flags |= VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP | VM_IO | + VM_DONTCOPY; + + return 0; +} + +static unsigned long sgx_get_unmapped_area(struct file *file, + unsigned long addr, + unsigned long len, + unsigned long pgoff, + unsigned long flags) +{ + if (len < 2 * PAGE_SIZE || (len & (len - 1))) + return -EINVAL; + + /* On 64-bit architecture, allow mmap() to exceed 32-bit encl + * limit only if the task is not running in 32-bit compatibility + * mode. + */ + if (len > sgx_encl_size_max_32) +#ifdef CONFIG_X86_64 + if (test_thread_flag(TIF_ADDR32)) + return -EINVAL; +#else + return -EINVAL; +#endif + +#ifdef CONFIG_X86_64 + if (len > sgx_encl_size_max_64) + return -EINVAL; +#endif + + addr = current->mm->get_unmapped_area(file, addr, 2 * len, pgoff, + flags); + if (IS_ERR_VALUE(addr)) + return addr; + + addr = (addr + (len - 1)) & ~(len - 1); + + return addr; +} + +static const struct file_operations sgx_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = sgx_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = sgx_compat_ioctl, +#endif + .mmap = sgx_mmap, + .get_unmapped_area = sgx_get_unmapped_area, +}; + +static struct miscdevice sgx_dev = { + .name = "isgx", + .fops = &sgx_fops, + .mode = 0666, +}; + +static int sgx_init_platform(void) +{ + unsigned int eax, ebx, ecx, edx; + unsigned long size; + int i; + + cpuid(0, &eax, &ebx, &ecx, &edx); + if (eax < SGX_CPUID) { + pr_err("intel_sgx: CPUID is missing the SGX leaf instruction\n"); + return -ENODEV; + } + + if (!boot_cpu_has(X86_FEATURE_SGX)) { + pr_err("intel_sgx: CPU is missing the SGX feature\n"); + return -ENODEV; + } + + cpuid_count(SGX_CPUID, 0x0, &eax, &ebx, &ecx, &edx); + if (!(eax & 1)) { + pr_err("intel_sgx: CPU does not support the SGX 1.0 instruction set\n"); + return -ENODEV; + } + + if (boot_cpu_has(X86_FEATURE_OSXSAVE)) { + cpuid_count(SGX_CPUID, 0x1, &eax, &ebx, &ecx, &edx); + sgx_xfrm_mask = (((u64)edx) << 32) + (u64)ecx; + for (i = 2; i < 64; i++) { + cpuid_count(0x0D, i, &eax, &ebx, &ecx, &edx); + if ((1 << i) & sgx_xfrm_mask) + sgx_ssaframesize_tbl[i] = + (168 + eax + ebx + PAGE_SIZE - 1) / + PAGE_SIZE; + } + } + + cpuid_count(SGX_CPUID, 0x0, &eax, &ebx, &ecx, &edx); + if (edx & 0xFFFF) { +#ifdef CONFIG_X86_64 + sgx_encl_size_max_64 = 1ULL << (edx & 0xFF); +#endif + sgx_encl_size_max_32 = 1ULL << ((edx >> 8) & 0xFF); + } + + sgx_nr_epc_banks = 0; + do { + cpuid_count(SGX_CPUID, sgx_nr_epc_banks + 2, + &eax, &ebx, &ecx, &edx); + if (eax & 0xf) { + sgx_epc_banks[sgx_nr_epc_banks].start = + (((u64) (ebx & 0xfffff)) << 32) + + (u64) (eax & 0xfffff000); + size = (((u64) (edx & 0xfffff)) << 32) + + (u64) (ecx & 0xfffff000); + sgx_epc_banks[sgx_nr_epc_banks].end = + sgx_epc_banks[sgx_nr_epc_banks].start + size; + if (!sgx_epc_banks[sgx_nr_epc_banks].start) + return -ENODEV; + sgx_nr_epc_banks++; + } else { + break; + } + } while (sgx_nr_epc_banks < SGX_MAX_EPC_BANKS); + + /* There should be at least one EPC area or something is wrong. */ + if (!sgx_nr_epc_banks) { + WARN_ON(1); + return 1; + } + + return 0; +} + +static int sgx_pm_suspend(struct device *dev) +{ + struct sgx_tgid_ctx *ctx; + struct sgx_encl *encl; + + kthread_stop(ksgxswapd_tsk); + ksgxswapd_tsk = NULL; + + list_for_each_entry(ctx, &sgx_tgid_ctx_list, list) { + list_for_each_entry(encl, &ctx->encl_list, encl_list) { + sgx_invalidate(encl, false); + encl->flags |= SGX_ENCL_SUSPEND; + flush_work(&encl->add_page_work); + } + } + + return 0; +} + +static int sgx_pm_resume(struct device *dev) +{ + ksgxswapd_tsk = kthread_run(ksgxswapd, NULL, "kswapd"); + return 0; +} + +static SIMPLE_DEV_PM_OPS(sgx_drv_pm, sgx_pm_suspend, sgx_pm_resume); + +static int sgx_dev_init(struct device *dev) +{ + unsigned int wq_flags; + int ret; + int i; + + pr_info("intel_sgx: " DRV_DESCRIPTION " v" DRV_VERSION "\n"); + + if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL) + return -ENODEV; + + ret = sgx_init_platform(); + if (ret) + return ret; + + pr_info("intel_sgx: Number of EPCs %d\n", sgx_nr_epc_banks); + + for (i = 0; i < sgx_nr_epc_banks; i++) { + pr_info("intel_sgx: EPC memory range 0x%lx-0x%lx\n", + sgx_epc_banks[i].start, sgx_epc_banks[i].end); +#ifdef CONFIG_X86_64 + sgx_epc_banks[i].mem = ioremap_cache(sgx_epc_banks[i].start, + sgx_epc_banks[i].end - sgx_epc_banks[i].start); + if (!sgx_epc_banks[i].mem) { + sgx_nr_epc_banks = i; + ret = -ENOMEM; + goto out_iounmap; + } +#endif + ret = sgx_page_cache_init(sgx_epc_banks[i].start, + sgx_epc_banks[i].end - sgx_epc_banks[i].start); + if (ret) { + sgx_nr_epc_banks = i+1; + goto out_iounmap; + } + } + + wq_flags = WQ_UNBOUND | WQ_FREEZABLE; +#ifdef WQ_NON_REENETRANT + wq_flags |= WQ_NON_REENTRANT; +#endif + sgx_add_page_wq = alloc_workqueue("intel_sgx-add-page-wq", wq_flags, 1); + if (!sgx_add_page_wq) { + pr_err("intel_sgx: alloc_workqueue() failed\n"); + ret = -ENOMEM; + goto out_iounmap; + } + + sgx_dev.parent = dev; + ret = misc_register(&sgx_dev); + if (ret) { + pr_err("intel_sgx: misc_register() failed\n"); + goto out_workqueue; + } + + return 0; +out_workqueue: + destroy_workqueue(sgx_add_page_wq); +out_iounmap: +#ifdef CONFIG_X86_64 + for (i = 0; i < sgx_nr_epc_banks; i++) + iounmap(sgx_epc_banks[i].mem); +#endif + return ret; +} + +static int sgx_drv_probe(struct platform_device *pdev) +{ + unsigned int eax, ebx, ecx, edx; + int i; + + if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL) + return -ENODEV; + + cpuid(0, &eax, &ebx, &ecx, &edx); + if (eax < SGX_CPUID) { + pr_err("intel_sgx: CPUID is missing the SGX leaf instruction\n"); + return -ENODEV; + } + + if (!boot_cpu_has(X86_FEATURE_SGX)) { + pr_err("intel_sgx: CPU is missing the SGX feature\n"); + return -ENODEV; + } + + cpuid_count(SGX_CPUID, 0x0, &eax, &ebx, &ecx, &edx); + if (!(eax & 1)) { + pr_err("intel_sgx: CPU does not support the SGX 1.0 instruction set\n"); + return -ENODEV; + } + + sgx_has_sgx2 = (eax & 2) != 0; + + if (boot_cpu_has(X86_FEATURE_OSXSAVE)) { + cpuid_count(SGX_CPUID, 0x1, &eax, &ebx, &ecx, &edx); + sgx_xfrm_mask = (((u64)edx) << 32) + (u64)ecx; + for (i = 2; i < 64; i++) { + cpuid_count(0x0D, i, &eax, &ebx, &ecx, &edx); + if ((1 << i) & sgx_xfrm_mask) + sgx_ssaframesize_tbl[i] = + (168 + eax + ebx + PAGE_SIZE - 1) / + PAGE_SIZE; + } + } + + cpuid_count(SGX_CPUID, 0x0, &eax, &ebx, &ecx, &edx); + if (edx & 0xFFFF) { +#ifdef CONFIG_X86_64 + sgx_encl_size_max_64 = 2ULL << (edx & 0xFF); +#endif + sgx_encl_size_max_32 = 2ULL << ((edx >> 8) & 0xFF); + } + + return sgx_dev_init(&pdev->dev); +} + +static int sgx_drv_remove(struct platform_device *pdev) +{ + int i; + + misc_deregister(&sgx_dev); + destroy_workqueue(sgx_add_page_wq); +#ifdef CONFIG_X86_64 + for (i = 0; i < sgx_nr_epc_banks; i++) + iounmap(sgx_epc_banks[i].mem); +#endif + sgx_page_cache_teardown(); + + return 0; +} + +#ifdef CONFIG_ACPI +static struct acpi_device_id sgx_device_ids[] = { + {"INT0E0C", 0}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, sgx_device_ids); +#endif + +static struct platform_driver sgx_drv = { + .probe = sgx_drv_probe, + .remove = sgx_drv_remove, + .driver = { + .name = "intel_sgx", + .pm = &sgx_drv_pm, + .acpi_match_table = ACPI_PTR(sgx_device_ids), + }, +}; + +module_platform_driver(sgx_drv); + +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sgx_page_cache.c b/sgx_page_cache.c new file mode 100644 index 0000000..0cb9b4e --- /dev/null +++ b/sgx_page_cache.c @@ -0,0 +1,603 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2016 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * Contact Information: + * Jarkko Sakkinen + * Intel Finland Oy - BIC 0357606-4 - Westendinkatu 7, 02160 Espoo + * + * BSD LICENSE + * + * Copyright(c) 2016 Intel Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: + * + * Jarkko Sakkinen + * Suresh Siddha + * Serge Ayoun + * Shay Katz-zamir + * Sean Christopherson + */ + +#include "sgx.h" +#include +#include +#include +#include +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)) + #include +#else + #include +#endif +#include + +#define SGX_NR_LOW_EPC_PAGES_DEFAULT 32 +#define SGX_NR_SWAP_CLUSTER_MAX 16 + +static LIST_HEAD(sgx_free_list); +static DEFINE_SPINLOCK(sgx_free_list_lock); + +LIST_HEAD(sgx_tgid_ctx_list); +DEFINE_MUTEX(sgx_tgid_ctx_mutex); +static unsigned int sgx_nr_total_epc_pages; +static unsigned int sgx_nr_free_pages; +static unsigned int sgx_nr_low_pages = SGX_NR_LOW_EPC_PAGES_DEFAULT; +static unsigned int sgx_nr_high_pages; +struct task_struct *ksgxswapd_tsk; +static DECLARE_WAIT_QUEUE_HEAD(ksgxswapd_waitq); + + +static int sgx_test_and_clear_young_cb(pte_t *ptep, pgtable_t token, + unsigned long addr, void *data) +{ + pte_t pte; + int ret; + + ret = pte_young(*ptep); + if (ret) { + pte = pte_mkold(*ptep); + set_pte_at((struct mm_struct *)data, addr, ptep, pte); + } + + return ret; +} + +/** + * sgx_test_and_clear_young() - Test and reset the accessed bit + * @page: enclave EPC page to be tested for recent access + * @encl: enclave which owns @page + * + * Checks the Access (A) bit from the PTE corresponding to the + * enclave page and clears it. Returns 1 if the page has been + * recently accessed and 0 if not. + */ +int sgx_test_and_clear_young(struct sgx_encl_page *page, struct sgx_encl *encl) +{ + struct vm_area_struct *vma = sgx_find_vma(encl, page->addr); + + if (!vma) + return 0; + + return apply_to_page_range(vma->vm_mm, page->addr, PAGE_SIZE, + sgx_test_and_clear_young_cb, vma->vm_mm); +} + +static struct sgx_tgid_ctx *sgx_isolate_tgid_ctx(unsigned long nr_to_scan) +{ + struct sgx_tgid_ctx *ctx = NULL; + int i; + + mutex_lock(&sgx_tgid_ctx_mutex); + + if (list_empty(&sgx_tgid_ctx_list)) { + mutex_unlock(&sgx_tgid_ctx_mutex); + return NULL; + } + + for (i = 0; i < nr_to_scan; i++) { + /* Peek TGID context from the head. */ + ctx = list_first_entry(&sgx_tgid_ctx_list, + struct sgx_tgid_ctx, + list); + + /* Move to the tail so that we do not encounter it in the + * next iteration. + */ + list_move_tail(&ctx->list, &sgx_tgid_ctx_list); + + /* Non-empty TGID context? */ + if (!list_empty(&ctx->encl_list) && + kref_get_unless_zero(&ctx->refcount)) + break; + + ctx = NULL; + } + + mutex_unlock(&sgx_tgid_ctx_mutex); + + return ctx; +} + +static struct sgx_encl *sgx_isolate_encl(struct sgx_tgid_ctx *ctx, + unsigned long nr_to_scan) +{ + struct sgx_encl *encl = NULL; + int i; + + mutex_lock(&sgx_tgid_ctx_mutex); + + if (list_empty(&ctx->encl_list)) { + mutex_unlock(&sgx_tgid_ctx_mutex); + return NULL; + } + + for (i = 0; i < nr_to_scan; i++) { + /* Peek encl from the head. */ + encl = list_first_entry(&ctx->encl_list, struct sgx_encl, + encl_list); + + /* Move to the tail so that we do not encounter it in the + * next iteration. + */ + list_move_tail(&encl->encl_list, &ctx->encl_list); + + /* Enclave with faulted pages? */ + if (!list_empty(&encl->load_list) && + kref_get_unless_zero(&encl->refcount)) + break; + + encl = NULL; + } + + mutex_unlock(&sgx_tgid_ctx_mutex); + + return encl; +} + +static void sgx_isolate_pages(struct sgx_encl *encl, + struct list_head *dst, + unsigned long nr_to_scan) +{ + struct sgx_encl_page *entry; + int i; + + mutex_lock(&encl->lock); + + if (encl->flags & SGX_ENCL_DEAD) + goto out; + + for (i = 0; i < nr_to_scan; i++) { + if (list_empty(&encl->load_list)) + break; + + entry = list_first_entry(&encl->load_list, + struct sgx_encl_page, + load_list); + + if (!sgx_test_and_clear_young(entry, encl) && + !(entry->flags & SGX_ENCL_PAGE_RESERVED)) { + entry->flags |= SGX_ENCL_PAGE_RESERVED; + list_move_tail(&entry->load_list, dst); + } else { + list_move_tail(&entry->load_list, &encl->load_list); + } + } +out: + mutex_unlock(&encl->lock); +} + +static void sgx_eblock(struct sgx_encl *encl, + struct sgx_epc_page *epc_page) +{ + void *vaddr; + int ret; + + vaddr = sgx_get_page(epc_page); + ret = __eblock((unsigned long)vaddr); + sgx_put_page(vaddr); + + if (ret) { + sgx_crit(encl, "EBLOCK returned %d\n", ret); + sgx_invalidate(encl, true); + } + +} + +static void sgx_etrack(struct sgx_encl *encl) +{ + void *epc; + int ret; + + epc = sgx_get_page(encl->secs_page.epc_page); + ret = __etrack(epc); + sgx_put_page(epc); + + if (ret) { + sgx_crit(encl, "ETRACK returned %d\n", ret); + sgx_invalidate(encl, true); + } +} + +static int __sgx_ewb(struct sgx_encl *encl, + struct sgx_encl_page *encl_page) +{ + struct sgx_page_info pginfo; + struct page *backing; + struct page *pcmd; + unsigned long pcmd_offset; + void *epc; + void *va; + int ret; + + pcmd_offset = ((encl_page->addr >> PAGE_SHIFT) & 31) * 128; + + backing = sgx_get_backing(encl, encl_page, false); + if (IS_ERR(backing)) { + ret = PTR_ERR(backing); + sgx_warn(encl, "pinning the backing page for EWB failed with %d\n", + ret); + return ret; + } + + pcmd = sgx_get_backing(encl, encl_page, true); + if (IS_ERR(pcmd)) { + ret = PTR_ERR(pcmd); + sgx_warn(encl, "pinning the pcmd page for EWB failed with %d\n", + ret); + goto out; + } + + epc = sgx_get_page(encl_page->epc_page); + va = sgx_get_page(encl_page->va_page->epc_page); + + pginfo.srcpge = (unsigned long)kmap_atomic(backing); + pginfo.pcmd = (unsigned long)kmap_atomic(pcmd) + pcmd_offset; + pginfo.linaddr = 0; + pginfo.secs = 0; + ret = __ewb(&pginfo, epc, + (void *)((unsigned long)va + encl_page->va_offset)); + kunmap_atomic((void *)(unsigned long)(pginfo.pcmd - pcmd_offset)); + kunmap_atomic((void *)(unsigned long)pginfo.srcpge); + + sgx_put_page(va); + sgx_put_page(epc); + sgx_put_backing(pcmd, true); + +out: + sgx_put_backing(backing, true); + return ret; +} + +static bool sgx_ewb(struct sgx_encl *encl, + struct sgx_encl_page *entry) +{ + int ret = __sgx_ewb(encl, entry); + + if (ret == SGX_NOT_TRACKED) { + /* slow path, IPI needed */ + sgx_flush_cpus(encl); + ret = __sgx_ewb(encl, entry); + } + + if (ret) { + /* make enclave inaccessible */ + sgx_invalidate(encl, true); + if (ret > 0) + sgx_err(encl, "EWB returned %d, enclave killed\n", ret); + return false; + } + + return true; +} + +static void sgx_evict_page(struct sgx_encl_page *entry, + struct sgx_encl *encl) +{ + sgx_ewb(encl, entry); + sgx_free_page(entry->epc_page, encl); + entry->epc_page = NULL; + entry->flags &= ~SGX_ENCL_PAGE_RESERVED; +} + +static void sgx_write_pages(struct sgx_encl *encl, struct list_head *src) +{ + struct sgx_encl_page *entry; + struct sgx_encl_page *tmp; + struct vm_area_struct *vma; + + if (list_empty(src)) + return; + + entry = list_first_entry(src, struct sgx_encl_page, load_list); + + mutex_lock(&encl->lock); + + /* EBLOCK */ + list_for_each_entry_safe(entry, tmp, src, load_list) { + vma = sgx_find_vma(encl, entry->addr); + if (vma) { + zap_vma_ptes(vma, entry->addr, PAGE_SIZE); + } + + sgx_eblock(encl, entry->epc_page); + } + + /* ETRACK */ + sgx_etrack(encl); + + /* EWB */ + while (!list_empty(src)) { + entry = list_first_entry(src, struct sgx_encl_page, + load_list); + list_del(&entry->load_list); + sgx_evict_page(entry, encl); + encl->secs_child_cnt--; + } + + if (!encl->secs_child_cnt && (encl->flags & SGX_ENCL_INITIALIZED)) { + sgx_evict_page(&encl->secs_page, encl); + encl->flags |= SGX_ENCL_SECS_EVICTED; + } + + mutex_unlock(&encl->lock); +} + +static void sgx_swap_pages(unsigned long nr_to_scan) +{ + struct sgx_tgid_ctx *ctx; + struct sgx_encl *encl; + LIST_HEAD(cluster); + + ctx = sgx_isolate_tgid_ctx(nr_to_scan); + if (!ctx) + return; + + encl = sgx_isolate_encl(ctx, nr_to_scan); + if (!encl) + goto out; + + down_read(&encl->mm->mmap_sem); + sgx_isolate_pages(encl, &cluster, nr_to_scan); + sgx_write_pages(encl, &cluster); + up_read(&encl->mm->mmap_sem); + + kref_put(&encl->refcount, sgx_encl_release); +out: + kref_put(&ctx->refcount, sgx_tgid_ctx_release); +} + +int ksgxswapd(void *p) +{ + while (!kthread_should_stop()) { + wait_event_interruptible(ksgxswapd_waitq, + kthread_should_stop() || + sgx_nr_free_pages < sgx_nr_high_pages); + + if (sgx_nr_free_pages < sgx_nr_high_pages) + sgx_swap_pages(SGX_NR_SWAP_CLUSTER_MAX); + } + + pr_info("%s: done\n", __func__); + return 0; +} + +int sgx_page_cache_init(resource_size_t start, unsigned long size) +{ + unsigned long i; + struct sgx_epc_page *new_epc_page, *entry; + struct list_head *parser, *temp; + + for (i = 0; i < size; i += PAGE_SIZE) { + new_epc_page = kzalloc(sizeof(*new_epc_page), GFP_KERNEL); + if (!new_epc_page) + goto err_freelist; + new_epc_page->pa = start + i; + + spin_lock(&sgx_free_list_lock); + list_add_tail(&new_epc_page->free_list, &sgx_free_list); + sgx_nr_total_epc_pages++; + sgx_nr_free_pages++; + spin_unlock(&sgx_free_list_lock); + } + + sgx_nr_high_pages = 2 * sgx_nr_low_pages; + ksgxswapd_tsk = kthread_run(ksgxswapd, NULL, "ksgxswapd"); + + return 0; +err_freelist: + list_for_each_safe(parser, temp, &sgx_free_list) { + spin_lock(&sgx_free_list_lock); + entry = list_entry(parser, struct sgx_epc_page, free_list); + list_del(&entry->free_list); + spin_unlock(&sgx_free_list_lock); + kfree(entry); + } + return -ENOMEM; +} + +void sgx_page_cache_teardown(void) +{ + struct sgx_epc_page *entry; + struct list_head *parser, *temp; + + if (ksgxswapd_tsk) + kthread_stop(ksgxswapd_tsk); + + spin_lock(&sgx_free_list_lock); + list_for_each_safe(parser, temp, &sgx_free_list) { + entry = list_entry(parser, struct sgx_epc_page, free_list); + list_del(&entry->free_list); + kfree(entry); + } + spin_unlock(&sgx_free_list_lock); +} + +static struct sgx_epc_page *sgx_alloc_page_fast(void) +{ + struct sgx_epc_page *entry = NULL; + + spin_lock(&sgx_free_list_lock); + + if (!list_empty(&sgx_free_list)) { + entry = list_first_entry(&sgx_free_list, struct sgx_epc_page, + free_list); + list_del(&entry->free_list); + sgx_nr_free_pages--; + } + + spin_unlock(&sgx_free_list_lock); + + return entry; +} + +/** + * sgx_alloc_page - allocate an EPC page + * @flags: allocation flags + * + * Try to grab a page from the free EPC page list. If there is a free page + * available, it is returned to the caller. If called with SGX_ALLOC_ATOMIC, + * the function will return immediately if the list is empty. Otherwise, it + * will swap pages up until there is a free page available. Before returning + * the low watermark is checked and ksgxswapd is waken up if we are below it. + * + * Return: an EPC page or a system error code + */ +struct sgx_epc_page *sgx_alloc_page(unsigned int flags) +{ + struct sgx_epc_page *entry; + + for ( ; ; ) { + entry = sgx_alloc_page_fast(); + if (entry) + break; + + if (flags & SGX_ALLOC_ATOMIC) { + entry = ERR_PTR(-EBUSY); + break; + } + + if (signal_pending(current)) { + entry = ERR_PTR(-ERESTARTSYS); + break; + } + + sgx_swap_pages(SGX_NR_SWAP_CLUSTER_MAX); + schedule(); + } + + if (sgx_nr_free_pages < sgx_nr_low_pages) + wake_up(&ksgxswapd_waitq); + + return entry; +} +EXPORT_SYMBOL(sgx_alloc_page); + +/** + * sgx_free_page - free an EPC page + * + * EREMOVE an EPC page and insert it back to the list of free pages. Optionally, + * an enclave can be given as a parameter. If the enclave is given, the + * resulting error is printed out loud as a critical error. It is an indicator + * of a driver bug if that would happen. + * + * If the enclave is not given as a parameter (like in the case when VMM uses + * this function)), it is fully up to the caller to deal with the return value, + * including printing it to the klog if it wants to do such a thing. + * + * @entry: an EPC page + * @encl: the enclave who owns the EPC page (optional) + * + * Return: SGX error code + */ +int sgx_free_page(struct sgx_epc_page *entry, struct sgx_encl *encl) +{ + void *epc; + int ret; + + epc = sgx_get_page(entry); + ret = __eremove(epc); + sgx_put_page(epc); + + if (ret) { + if (encl) + sgx_crit(encl, "EREMOVE returned %d\n", ret); + + return ret; + } + + spin_lock(&sgx_free_list_lock); + list_add(&entry->free_list, &sgx_free_list); + sgx_nr_free_pages++; + spin_unlock(&sgx_free_list_lock); + + return 0; +} +EXPORT_SYMBOL(sgx_free_page); + +void *sgx_get_page(struct sgx_epc_page *entry) +{ +#ifdef CONFIG_X86_32 + return kmap_atomic_pfn(PFN_DOWN(entry->pa)); +#else + int i; + + for (i = 0; i < sgx_nr_epc_banks; i++) { + if (entry->pa < sgx_epc_banks[i].end && + entry->pa >= sgx_epc_banks[i].start) { + return sgx_epc_banks[i].mem + + (entry->pa - sgx_epc_banks[i].start); + } + } + + return NULL; +#endif +} +EXPORT_SYMBOL(sgx_get_page); + +void sgx_put_page(void *epc_page_vaddr) +{ +#ifdef CONFIG_X86_32 + kunmap_atomic(epc_page_vaddr); +#else +#endif +} +EXPORT_SYMBOL(sgx_put_page); diff --git a/sgx_user.h b/sgx_user.h new file mode 100644 index 0000000..503f6be --- /dev/null +++ b/sgx_user.h @@ -0,0 +1,145 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2016 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * Contact Information: + * Jarkko Sakkinen + * Intel Finland Oy - BIC 0357606-4 - Westendinkatu 7, 02160 Espoo + * + * BSD LICENSE + * + * Copyright(c) 2016 Intel Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: + * + * Jarkko Sakkinen + * Suresh Siddha + * Serge Ayoun + * Shay Katz-zamir + */ + +#ifndef _UAPI_ASM_X86_SGX_H +#define _UAPI_ASM_X86_SGX_H + +#include +#include + +#define SGX_MAGIC 0xA4 + +#define SGX_IOC_ENCLAVE_CREATE \ + _IOW(SGX_MAGIC, 0x00, struct sgx_enclave_create) +#define SGX_IOC_ENCLAVE_ADD_PAGE \ + _IOW(SGX_MAGIC, 0x01, struct sgx_enclave_add_page) +#define SGX_IOC_ENCLAVE_INIT \ + _IOW(SGX_MAGIC, 0x02, struct sgx_enclave_init) + +/* SGX leaf instruction return values */ +#define SGX_SUCCESS 0 +#define SGX_INVALID_SIG_STRUCT 1 +#define SGX_INVALID_ATTRIBUTE 2 +#define SGX_BLKSTATE 3 +#define SGX_INVALID_MEASUREMENT 4 +#define SGX_NOTBLOCKABLE 5 +#define SGX_PG_INVLD 6 +#define SGX_LOCKFAIL 7 +#define SGX_INVALID_SIGNATURE 8 +#define SGX_MAC_COMPARE_FAIL 9 +#define SGX_PAGE_NOT_BLOCKED 10 +#define SGX_NOT_TRACKED 11 +#define SGX_VA_SLOT_OCCUPIED 12 +#define SGX_CHILD_PRESENT 13 +#define SGX_ENCLAVE_ACT 14 +#define SGX_ENTRYEPOCH_LOCKED 15 +#define SGX_INVALID_LICENSE 16 +#define SGX_PREV_TRK_INCMPL 17 +#define SGX_PG_IS_SECS 18 +#define SGX_INVALID_CPUSVN 32 +#define SGX_INVALID_ISVSVN 64 +#define SGX_UNMASKED_EVENT 128 +#define SGX_INVALID_KEYNAME 256 + +/* IOCTL return values */ +#define SGX_POWER_LOST_ENCLAVE 0x40000000 +#define SGX_LE_ROLLBACK 0x40000001 + +/** + * struct sgx_enclave_create - parameter structure for the + * %SGX_IOC_ENCLAVE_CREATE ioctl + * @src: address for the SECS page data + */ +struct sgx_enclave_create { + __u64 src; +} __packed; + +/** + * struct sgx_enclave_add_page - parameter structure for the + * %SGX_IOC_ENCLAVE_ADD_PAGE ioctl + * @addr: address in the ELRANGE + * @src: address for the page data + * @secinfo: address for the SECINFO data + * @mrmask: bitmask for the 256 byte chunks that are to be measured + */ +struct sgx_enclave_add_page { + __u64 addr; + __u64 src; + __u64 secinfo; + __u16 mrmask; +} __packed; + +/** + * struct sgx_enclave_init - parameter structure for the + * %SGX_IOC_ENCLAVE_INIT ioctl + * @addr: address in the ELRANGE + * @sigstruct: address for the page data + * @einittoken: address for the SECINFO data + */ +struct sgx_enclave_init { + __u64 addr; + __u64 sigstruct; + __u64 einittoken; +} __packed; + +struct sgx_enclave_destroy { + __u64 addr; +} __packed; + +#endif /* _UAPI_ASM_X86_SGX_H */ diff --git a/sgx_util.c b/sgx_util.c new file mode 100644 index 0000000..4226f2a --- /dev/null +++ b/sgx_util.c @@ -0,0 +1,436 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2016 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * Contact Information: + * Jarkko Sakkinen + * Intel Finland Oy - BIC 0357606-4 - Westendinkatu 7, 02160 Espoo + * + * BSD LICENSE + * + * Copyright(c) 2016 Intel Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: + * + * Jarkko Sakkinen + * Suresh Siddha + * Serge Ayoun + * Shay Katz-zamir + * Sean Christopherson + */ + +#include "sgx.h" +#include +#include +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)) + #include +#else + #include +#endif + +struct page *sgx_get_backing(struct sgx_encl *encl, + struct sgx_encl_page *entry, + bool pcmd) +{ + struct inode *inode; + struct address_space *mapping; + gfp_t gfpmask; + pgoff_t index; + + if (pcmd) + inode = encl->pcmd->f_path.dentry->d_inode; + else + inode = encl->backing->f_path.dentry->d_inode; + + mapping = inode->i_mapping; + gfpmask = mapping_gfp_mask(mapping); + + if (pcmd) + index = (entry->addr - encl->base) >> (PAGE_SHIFT + 5); + else + index = (entry->addr - encl->base) >> PAGE_SHIFT; + + return shmem_read_mapping_page_gfp(mapping, index, gfpmask); +} + +void sgx_put_backing(struct page *backing_page, bool write) +{ + if (write) + set_page_dirty(backing_page); + + put_page(backing_page); +} + +struct vm_area_struct *sgx_find_vma(struct sgx_encl *encl, unsigned long addr) +{ + struct vm_area_struct *vma; + + vma = find_vma(encl->mm, addr); + if (vma && encl == vma->vm_private_data) + return vma; + + sgx_dbg(encl, "cannot find VMA at 0x%lx\n", addr); + return NULL; +} + +void sgx_zap_tcs_ptes(struct sgx_encl *encl, struct vm_area_struct *vma) +{ + struct sgx_encl_page *entry; + + list_for_each_entry(entry, &encl->load_list, load_list) { + if ((entry->flags & SGX_ENCL_PAGE_TCS) && + entry->addr >= vma->vm_start && + entry->addr < vma->vm_end) + zap_vma_ptes(vma, entry->addr, PAGE_SIZE); + } +} + +void sgx_invalidate(struct sgx_encl *encl, bool flush_cpus) +{ + struct vm_area_struct *vma; + unsigned long addr; + + for (addr = encl->base; addr < (encl->base + encl->size); + addr = vma->vm_end) { + vma = sgx_find_vma(encl, addr); + if (vma) + sgx_zap_tcs_ptes(encl, vma); + else + break; + } + + encl->flags |= SGX_ENCL_DEAD; + + if (flush_cpus) + sgx_flush_cpus(encl); +} + +static void sgx_ipi_cb(void *info) +{ +} + +void sgx_flush_cpus(struct sgx_encl *encl) +{ + on_each_cpu_mask(mm_cpumask(encl->mm), sgx_ipi_cb, NULL, 1); +} + +/** + * sgx_find_encl - find an enclave + * @mm: mm struct of the current process + * @addr: address in the ELRANGE + * @vma: the VMA that is located in the given address + * + * Finds an enclave identified by the given address. Gives back the VMA, that is + * part of the enclave, located in that address. + */ +int sgx_find_encl(struct mm_struct *mm, unsigned long addr, + struct vm_area_struct **vma) +{ + struct sgx_encl *encl; + + *vma = find_vma(mm, addr); + if (!(*vma) || (*vma)->vm_ops != &sgx_vm_ops || + addr < (*vma)->vm_start) + return -EINVAL; + + encl = (*vma)->vm_private_data; + if (!encl) { + pr_debug("%s: VMA exists but there is no enclave at 0x%p\n", + __func__, (void *)addr); + return -EINVAL; + } + + if (encl->flags & SGX_ENCL_SUSPEND) + return SGX_POWER_LOST_ENCLAVE; + + return 0; +} + +static int sgx_eldu(struct sgx_encl *encl, + struct sgx_encl_page *encl_page, + struct sgx_epc_page *epc_page, + bool is_secs) +{ + struct page *backing; + struct page *pcmd; + unsigned long pcmd_offset; + struct sgx_page_info pginfo; + void *secs_ptr = NULL; + void *epc_ptr; + void *va_ptr; + int ret; + + pcmd_offset = ((encl_page->addr >> PAGE_SHIFT) & 31) * 128; + + backing = sgx_get_backing(encl, encl_page, false); + if (IS_ERR(backing)) { + ret = PTR_ERR(backing); + sgx_warn(encl, "pinning the backing page for ELDU failed with %d\n", + ret); + return ret; + } + + pcmd = sgx_get_backing(encl, encl_page, true); + if (IS_ERR(pcmd)) { + ret = PTR_ERR(pcmd); + sgx_warn(encl, "pinning the pcmd page for EWB failed with %d\n", + ret); + goto out; + } + + if (!is_secs) + secs_ptr = sgx_get_page(encl->secs_page.epc_page); + + epc_ptr = sgx_get_page(epc_page); + va_ptr = sgx_get_page(encl_page->va_page->epc_page); + pginfo.srcpge = (unsigned long)kmap_atomic(backing); + pginfo.pcmd = (unsigned long)kmap_atomic(pcmd) + pcmd_offset; + pginfo.linaddr = is_secs ? 0 : encl_page->addr; + pginfo.secs = (unsigned long)secs_ptr; + + ret = __eldu((unsigned long)&pginfo, + (unsigned long)epc_ptr, + (unsigned long)va_ptr + + encl_page->va_offset); + if (ret) { + sgx_err(encl, "ELDU returned %d\n", ret); + ret = -EFAULT; + } + + kunmap_atomic((void *)(unsigned long)(pginfo.pcmd - pcmd_offset)); + kunmap_atomic((void *)(unsigned long)pginfo.srcpge); + sgx_put_page(va_ptr); + sgx_put_page(epc_ptr); + + if (!is_secs) + sgx_put_page(secs_ptr); + + sgx_put_backing(pcmd, false); + +out: + sgx_put_backing(backing, false); + return ret; +} + +static struct sgx_encl_page *sgx_do_fault(struct vm_area_struct *vma, + unsigned long addr, unsigned int flags) +{ + struct sgx_encl *encl = vma->vm_private_data; + struct sgx_encl_page *entry; + struct sgx_epc_page *epc_page = NULL; + struct sgx_epc_page *secs_epc_page = NULL; + bool reserve = (flags & SGX_FAULT_RESERVE) != 0; + int rc = 0; + + /* If process was forked, VMA is still there but vm_private_data is set + * to NULL. + */ + if (!encl) + return ERR_PTR(-EFAULT); + + mutex_lock(&encl->lock); + + entry = radix_tree_lookup(&encl->page_tree, addr >> PAGE_SHIFT); + if (!entry) { + rc = -EFAULT; + goto out; + } + + if (encl->flags & SGX_ENCL_DEAD) { + rc = -EFAULT; + goto out; + } + + if (!(encl->flags & SGX_ENCL_INITIALIZED)) { + sgx_dbg(encl, "cannot fault, unitialized\n"); + rc = -EFAULT; + goto out; + } + + if (reserve && (entry->flags & SGX_ENCL_PAGE_RESERVED)) { + sgx_dbg(encl, "cannot fault, 0x%p is reserved\n", + (void *)entry->addr); + rc = -EBUSY; + goto out; + } + + /* Legal race condition, page is already faulted. */ + if (entry->epc_page) { + if (reserve) + entry->flags |= SGX_ENCL_PAGE_RESERVED; + goto out; + } + + epc_page = sgx_alloc_page(SGX_ALLOC_ATOMIC); + if (IS_ERR(epc_page)) { + rc = PTR_ERR(epc_page); + epc_page = NULL; + goto out; + } + + /* If SECS is evicted then reload it first */ + if (encl->flags & SGX_ENCL_SECS_EVICTED) { + secs_epc_page = sgx_alloc_page(SGX_ALLOC_ATOMIC); + if (IS_ERR(secs_epc_page)) { + rc = PTR_ERR(secs_epc_page); + secs_epc_page = NULL; + goto out; + } + + rc = sgx_eldu(encl, &encl->secs_page, secs_epc_page, true); + if (rc) + goto out; + + encl->secs_page.epc_page = secs_epc_page; + encl->flags &= ~SGX_ENCL_SECS_EVICTED; + + /* Do not free */ + secs_epc_page = NULL; + } + + rc = sgx_eldu(encl, entry, epc_page, false /* is_secs */); + if (rc) + goto out; + + /* Track the EPC page even if vm_insert_pfn fails; we need to ensure + * the EPC page is properly freed and we can't do EREMOVE right away + * because EREMOVE may fail due to an active cpu in the enclave. We + * can't call vm_insert_pfn before sgx_eldu because SKL signals #GP + * instead of #PF if the EPC page is invalid. + */ + encl->secs_child_cnt++; + + entry->epc_page = epc_page; + + if (reserve) + entry->flags |= SGX_ENCL_PAGE_RESERVED; + + /* Do not free */ + epc_page = NULL; + list_add_tail(&entry->load_list, &encl->load_list); + + rc = vm_insert_pfn(vma, entry->addr, PFN_DOWN(entry->epc_page->pa)); + if (rc) { + /* Kill the enclave if vm_insert_pfn fails; failure only occurs + * if there is a driver bug or an unrecoverable issue, e.g. OOM. + */ + sgx_crit(encl, "vm_insert_pfn returned %d\n", rc); + sgx_invalidate(encl, true); + goto out; + } + + sgx_test_and_clear_young(entry, encl); +out: + mutex_unlock(&encl->lock); + if (epc_page) + sgx_free_page(epc_page, encl); + if (secs_epc_page) + sgx_free_page(secs_epc_page, encl); + return rc ? ERR_PTR(rc) : entry; +} + +struct sgx_encl_page *sgx_fault_page(struct vm_area_struct *vma, + unsigned long addr, + unsigned int flags) +{ + struct sgx_encl_page *entry; + + do { + entry = sgx_do_fault(vma, addr, flags); + if (!(flags & SGX_FAULT_RESERVE)) + break; + } while (PTR_ERR(entry) == -EBUSY); + + return entry; +} + +void sgx_encl_release(struct kref *ref) +{ + struct sgx_encl_page *entry; + struct sgx_va_page *va_page; + struct sgx_encl *encl = container_of(ref, struct sgx_encl, refcount); + struct radix_tree_iter iter; + void **slot; + + mutex_lock(&sgx_tgid_ctx_mutex); + if (!list_empty(&encl->encl_list)) + list_del(&encl->encl_list); + mutex_unlock(&sgx_tgid_ctx_mutex); + + if (encl->mmu_notifier.ops) + mmu_notifier_unregister_no_release(&encl->mmu_notifier, + encl->mm); + + radix_tree_for_each_slot(slot, &encl->page_tree, &iter, 0) { + entry = *slot; + if (entry->epc_page) { + list_del(&entry->load_list); + sgx_free_page(entry->epc_page, encl); + } + radix_tree_delete(&encl->page_tree, entry->addr >> PAGE_SHIFT); + kfree(entry); + } + + while (!list_empty(&encl->va_pages)) { + va_page = list_first_entry(&encl->va_pages, + struct sgx_va_page, list); + list_del(&va_page->list); + sgx_free_page(va_page->epc_page, encl); + kfree(va_page); + } + + if (encl->secs_page.epc_page) + sgx_free_page(encl->secs_page.epc_page, encl); + + encl->secs_page.epc_page = NULL; + + if (encl->tgid_ctx) + kref_put(&encl->tgid_ctx->refcount, sgx_tgid_ctx_release); + + if (encl->backing) + fput(encl->backing); + + if (encl->pcmd) + fput(encl->pcmd); + + kfree(encl); +} diff --git a/sgx_vma.c b/sgx_vma.c new file mode 100644 index 0000000..4dbe26c --- /dev/null +++ b/sgx_vma.c @@ -0,0 +1,241 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2016 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * Contact Information: + * Jarkko Sakkinen + * Intel Finland Oy - BIC 0357606-4 - Westendinkatu 7, 02160 Espoo + * + * BSD LICENSE + * + * Copyright(c) 2016 Intel Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: + * + * Jarkko Sakkinen + * Suresh Siddha + * Serge Ayoun + * Shay Katz-zamir + * Sean Christopherson + */ + +#include "sgx.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void sgx_vma_open(struct vm_area_struct *vma) +{ + struct sgx_encl *encl = vma->vm_private_data; + if (!encl) + return; + + /* kref cannot underflow because ECREATE ioctl checks that there is only + * one single VMA for the enclave before proceeding. + */ + kref_get(&encl->refcount); +} + +static void sgx_vma_close(struct vm_area_struct *vma) +{ + struct sgx_encl *encl = vma->vm_private_data; + if (!encl) + return; + + mutex_lock(&encl->lock); + zap_vma_ptes(vma, vma->vm_start, vma->vm_end - vma->vm_start); + encl->flags |= SGX_ENCL_DEAD; + mutex_unlock(&encl->lock); + kref_put(&encl->refcount, sgx_encl_release); +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)) +static int sgx_vma_fault(struct vm_fault *vmf) +{ + struct vm_area_struct *vma = vmf->vma; +#else +static int sgx_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +{ +#endif + + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0)) + unsigned long addr = (unsigned long)vmf->address; +#else + unsigned long addr = (unsigned long) vmf->virtual_address; +#endif + struct sgx_encl_page *entry; + + entry = sgx_fault_page(vma, addr, 0); + + if (!IS_ERR(entry) || PTR_ERR(entry) == -EBUSY) + return VM_FAULT_NOPAGE; + else + return VM_FAULT_SIGBUS; +} + +static inline int sgx_vma_access_word(struct sgx_encl *encl, + unsigned long addr, + void *buf, + int len, + int write, + struct sgx_encl_page *encl_page, + int i) +{ + char data[sizeof(unsigned long)]; + int align, cnt, offset; + void *vaddr; + int ret; + + offset = ((addr + i) & (PAGE_SIZE - 1)) & ~(sizeof(unsigned long) - 1); + align = (addr + i) & (sizeof(unsigned long) - 1); + cnt = sizeof(unsigned long) - align; + cnt = min(cnt, len - i); + + if (write) { + if (encl_page->flags & SGX_ENCL_PAGE_TCS && + (offset < 8 || (offset + (len - i)) > 16)) + return -ECANCELED; + + if (align || (cnt != sizeof(unsigned long))) { + vaddr = sgx_get_page(encl_page->epc_page); + ret = __edbgrd((void *)((unsigned long)vaddr + offset), + (unsigned long *)data); + sgx_put_page(vaddr); + if (ret) { + sgx_dbg(encl, "EDBGRD returned %d\n", ret); + return -EFAULT; + } + } + + memcpy(data + align, buf + i, cnt); + vaddr = sgx_get_page(encl_page->epc_page); + ret = __edbgwr((void *)((unsigned long)vaddr + offset), + (unsigned long *)data); + sgx_put_page(vaddr); + if (ret) { + sgx_dbg(encl, "EDBGWR returned %d\n", ret); + return -EFAULT; + } + } else { + if (encl_page->flags & SGX_ENCL_PAGE_TCS && + (offset + (len - i)) > 72) + return -ECANCELED; + + vaddr = sgx_get_page(encl_page->epc_page); + ret = __edbgrd((void *)((unsigned long)vaddr + offset), + (unsigned long *)data); + sgx_put_page(vaddr); + if (ret) { + sgx_dbg(encl, "EDBGRD returned %d\n", ret); + return -EFAULT; + } + + memcpy(buf + i, data + align, cnt); + } + + return cnt; +} + +static int sgx_vma_access(struct vm_area_struct *vma, unsigned long addr, + void *buf, int len, int write) +{ + struct sgx_encl *encl = vma->vm_private_data; + struct sgx_encl_page *entry = NULL; + const char *op_str = write ? "EDBGWR" : "EDBGRD"; + int ret = 0; + int i; + + /* If process was forked, VMA is still there but vm_private_data is set + * to NULL. + */ + if (!encl) + return -EFAULT; + + if (!(encl->flags & SGX_ENCL_DEBUG) || + !(encl->flags & SGX_ENCL_INITIALIZED) || + (encl->flags & SGX_ENCL_DEAD)) + return -EFAULT; + + sgx_dbg(encl, "%s addr=0x%lx, len=%d\n", op_str, addr, len); + + for (i = 0; i < len; i += ret) { + if (!entry || !((addr + i) & (PAGE_SIZE - 1))) { + if (entry) + entry->flags &= ~SGX_ENCL_PAGE_RESERVED; + + entry = sgx_fault_page(vma, (addr + i) & PAGE_MASK, + SGX_FAULT_RESERVE); + if (IS_ERR(entry)) { + ret = PTR_ERR(entry); + entry = NULL; + break; + } + } + + /* No locks are needed because used fields are immutable after + * intialization. + */ + ret = sgx_vma_access_word(encl, addr, buf, len, write, + entry, i); + if (ret < 0) + break; + } + + if (entry) + entry->flags &= ~SGX_ENCL_PAGE_RESERVED; + + return (ret < 0 && ret != -ECANCELED) ? ret : i; +} + +const struct vm_operations_struct sgx_vm_ops = { + .close = sgx_vma_close, + .open = sgx_vma_open, + .fault = sgx_vma_fault, + .access = sgx_vma_access, +};