Files
Angie Chinchilla 7490f2e8c9 check in sgx2.patch for latest rebase
Signed-off-by: Angie Chinchilla <angie.v.chinchilla@intel.com>
2017-11-30 16:14:33 -05:00

1077 lines
30 KiB
Diff

From fc0cb151466bc6197506ab7e6dc5e3f516ca8d7a Mon Sep 17 00:00:00 2001
From: Angie Chinchilla <angie.v.chinchilla@intel.com>
Date: Mon, 27 Nov 2017 00:15:00 -0500
Subject: [PATCH] SGX 2.0 Implementation
Rebased 2.0 patch applies to linux-sgx-driver:master
commit-id 03435d33de0bcca6c5777f23ac161249b9158f1e
Authors:
Serge Ayoun <serge.ayoun@intel.com>
Angie Chinchilla <angie.v.chinchilla@intel.com>
Shay Katz-zamir <shay.katz-zamir@intel.com>
Cedric Xing <cedric.xing@intel.com>
Signed-off-by: Angie Chinchilla <angie.v.chinchilla@intel.com>
---
Makefile | 3 +-
sgx.h | 29 +++-
sgx_arch.h | 2 +
sgx_encl.c | 30 ++--
sgx_encl2.c | 446 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
sgx_ioctl.c | 134 ++++++++++++++++-
sgx_main.c | 5 +-
sgx_page_cache.c | 2 +-
sgx_user.h | 25 ++++
sgx_util.c | 40 +++--
sgx_vma.c | 10 +-
11 files changed, 698 insertions(+), 28 deletions(-)
create mode 100644 sgx_encl2.c
diff --git a/Makefile b/Makefile
index 4b5edaf..c88f38c 100644
--- a/Makefile
+++ b/Makefile
@@ -5,7 +5,8 @@ ifneq ($(KERNELRELEASE),)
sgx_ioctl.o \
sgx_vma.o \
sgx_util.o\
- sgx_encl.o
+ sgx_encl.o \
+ sgx_encl2.o
obj-m += isgx.o
else
KDIR := /lib/modules/$(shell uname -r)/build
diff --git a/sgx.h b/sgx.h
index bfc24c0..46dfc0f 100644
--- a/sgx.h
+++ b/sgx.h
@@ -69,12 +69,14 @@
#include <linux/workqueue.h>
#include <linux/mmu_notifier.h>
#include <linux/radix-tree.h>
+#include <linux/mm.h>
#include "sgx_arch.h"
#include "sgx_user.h"
#define SGX_EINIT_SPIN_COUNT 20
#define SGX_EINIT_SLEEP_COUNT 50
#define SGX_EINIT_SLEEP_TIME 20
+#define SGX_EDMM_SPIN_COUNT 20
#define SGX_VA_SLOT_COUNT 512
@@ -110,9 +112,21 @@ static inline void sgx_free_va_slot(struct sgx_va_page *page,
clear_bit(offset >> 3, page->slots);
}
+static inline bool sgx_va_slots_empty(struct sgx_va_page *page)
+{
+ int slot = find_first_bit(page->slots, SGX_VA_SLOT_COUNT);
+
+ if (slot == SGX_VA_SLOT_COUNT)
+ return true;
+
+ return false;
+}
+
enum sgx_encl_page_flags {
SGX_ENCL_PAGE_TCS = BIT(0),
SGX_ENCL_PAGE_RESERVED = BIT(1),
+ SGX_ENCL_PAGE_TRIM = BIT(2),
+ SGX_ENCL_PAGE_ADDED = BIT(3),
};
struct sgx_encl_page {
@@ -160,6 +174,7 @@ struct sgx_encl {
struct sgx_tgid_ctx *tgid_ctx;
struct list_head encl_list;
struct mmu_notifier mmu_notifier;
+ unsigned int shadow_epoch;
};
struct sgx_epc_bank {
@@ -178,6 +193,7 @@ extern u64 sgx_encl_size_max_64;
extern u64 sgx_xfrm_mask;
extern u32 sgx_misc_reserved;
extern u32 sgx_xsave_size_tbl[64];
+extern bool sgx_has_sgx2;
extern const struct vm_operations_struct sgx_vm_ops;
@@ -205,6 +221,8 @@ int sgx_encl_add_page(struct sgx_encl *encl, unsigned long addr, void *data,
struct sgx_secinfo *secinfo, unsigned int mrmask);
int sgx_encl_init(struct sgx_encl *encl, struct sgx_sigstruct *sigstruct,
struct sgx_einittoken *einittoken);
+struct sgx_encl_page *sgx_encl_augment(struct vm_area_struct *vma,
+ unsigned long addr, bool write);
void sgx_encl_release(struct kref *ref);
long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
@@ -234,7 +252,8 @@ enum sgx_fault_flags {
struct sgx_encl_page *sgx_fault_page(struct vm_area_struct *vma,
unsigned long addr,
- unsigned int flags);
+ unsigned int flags,
+ struct vm_fault *vmf);
extern struct mutex sgx_tgid_ctx_mutex;
@@ -249,6 +268,12 @@ void 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);
void sgx_eblock(struct sgx_encl *encl, struct sgx_epc_page *epc_page);
-void sgx_etrack(struct sgx_encl *encl);
+void sgx_etrack(struct sgx_encl *encl, unsigned int epoch);
+void sgx_ipi_cb(void *info);
+int sgx_eldu(struct sgx_encl *encl, struct sgx_encl_page *encl_page,
+ struct sgx_epc_page *epc_page, bool is_secs);
+long modify_range(struct sgx_range *rg, unsigned long flags);
+int remove_page(struct sgx_encl *encl, unsigned long address, bool trim);
+int sgx_get_encl(unsigned long addr, struct sgx_encl **encl);
#endif /* __ARCH_X86_INTEL_SGX_H__ */
diff --git a/sgx_arch.h b/sgx_arch.h
index dcb620e..a609fd6 100644
--- a/sgx_arch.h
+++ b/sgx_arch.h
@@ -139,6 +139,7 @@ enum sgx_page_type {
SGX_PAGE_TYPE_TCS = 0x01,
SGX_PAGE_TYPE_REG = 0x02,
SGX_PAGE_TYPE_VA = 0x03,
+ SGX_PAGE_TYPE_TRIM = 0x04,
};
enum sgx_secinfo_flags {
@@ -148,6 +149,7 @@ enum sgx_secinfo_flags {
SGX_SECINFO_SECS = (SGX_PAGE_TYPE_SECS << 8),
SGX_SECINFO_TCS = (SGX_PAGE_TYPE_TCS << 8),
SGX_SECINFO_REG = (SGX_PAGE_TYPE_REG << 8),
+ SGX_SECINFO_TRIM = (SGX_PAGE_TYPE_TRIM << 8),
};
struct sgx_secinfo {
diff --git a/sgx_encl.c b/sgx_encl.c
index 2669509..ccab446 100644
--- a/sgx_encl.c
+++ b/sgx_encl.c
@@ -279,6 +279,7 @@ static bool sgx_process_add_page_req(struct sgx_add_page_req *req,
encl_page->epc_page = epc_page;
sgx_test_and_clear_young(encl_page, encl);
list_add_tail(&epc_page->list, &encl->load_list);
+ encl_page->flags |= SGX_ENCL_PAGE_ADDED;
return true;
}
@@ -431,8 +432,9 @@ static const struct mmu_notifier_ops sgx_mmu_notifier_ops = {
.release = sgx_mmu_notifier_release,
};
-static int sgx_init_page(struct sgx_encl *encl, struct sgx_encl_page *entry,
- unsigned long addr, unsigned int alloc_flags)
+int sgx_init_page(struct sgx_encl *encl, struct sgx_encl_page *entry,
+ unsigned long addr, unsigned int alloc_flags,
+ struct sgx_epc_page **va_src, bool already_locked)
{
struct sgx_va_page *va_page;
struct sgx_epc_page *epc_page = NULL;
@@ -451,10 +453,15 @@ static int sgx_init_page(struct sgx_encl *encl, struct sgx_encl_page *entry,
if (!va_page)
return -ENOMEM;
- epc_page = sgx_alloc_page(alloc_flags);
- if (IS_ERR(epc_page)) {
- kfree(va_page);
- return PTR_ERR(epc_page);
+ if (va_src) {
+ epc_page = *va_src;
+ *va_src = NULL;
+ } else {
+ epc_page = sgx_alloc_page(alloc_flags);
+ if (IS_ERR(epc_page)) {
+ kfree(va_page);
+ return PTR_ERR(epc_page);
+ }
}
vaddr = sgx_get_page(epc_page);
@@ -481,9 +488,11 @@ static int sgx_init_page(struct sgx_encl *encl, struct sgx_encl_page *entry,
va_page->epc_page = epc_page;
va_offset = sgx_alloc_va_slot(va_page);
- mutex_lock(&encl->lock);
+ if (!already_locked)
+ mutex_lock(&encl->lock);
list_add(&va_page->list, &encl->va_pages);
- mutex_unlock(&encl->lock);
+ if (!already_locked)
+ mutex_unlock(&encl->lock);
}
entry->va_page = va_page;
@@ -596,7 +605,8 @@ int sgx_encl_create(struct sgx_secs *secs)
if (ret)
goto out;
- ret = sgx_init_page(encl, &encl->secs, encl->base + encl->size, 0);
+ ret = sgx_init_page(encl, &encl->secs, encl->base + encl->size, 0,
+ NULL, false);
if (ret)
goto out;
@@ -766,7 +776,7 @@ static int __sgx_encl_add_page(struct sgx_encl *encl,
return ret;
}
- ret = sgx_init_page(encl, encl_page, addr, 0);
+ ret = sgx_init_page(encl, encl_page, addr, 0, NULL, false);
if (ret)
return ret;
diff --git a/sgx_encl2.c b/sgx_encl2.c
new file mode 100644
index 0000000..6a5556d
--- /dev/null
+++ b/sgx_encl2.c
@@ -0,0 +1,446 @@
+/*
+ * 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-2017 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 <jarkko.sakkinen@linux.intel.com>
+ * Intel Finland Oy - BIC 0357606-4 - Westendinkatu 7, 02160 Espoo
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016-2017 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:
+ *
+ * Serge Ayoun <serge.ayoun@intel.com>
+ * Angie Chinchilla <angie.v.chinchilla@intel.com>
+ * Shay Katz-zamir <shay.katz-zamir@intel.com>
+ * Cedric Xing <cedric.xing@intel.com>
+ */
+
+#include "sgx.h"
+#include <linux/ratelimit.h>
+#include <linux/slab.h>
+
+#define SGX_NR_MOD_CHUNK_PAGES 16
+
+int sgx_init_page(struct sgx_encl *encl, struct sgx_encl_page *entry,
+ unsigned long addr, unsigned int alloc_flags,
+ struct sgx_epc_page **va_src, bool already_locked);
+/**
+ * sgx_encl_augment() - adds a page to an enclave
+ * @addr: virtual address where the page should be added
+ *
+ * the address is checked against the dynamic ranges defined for
+ * the enclave. If it matches one, a page is added at the
+ * corresponding location
+ *
+ * Note: Invoking function must already hold the encl->lock
+ */
+struct sgx_encl_page *sgx_encl_augment(struct vm_area_struct *vma,
+ unsigned long addr,
+ bool write)
+{
+ struct sgx_pageinfo pginfo;
+ struct sgx_epc_page *epc_page, *va_page = NULL;
+ struct sgx_epc_page *secs_epc_page = NULL;
+ struct sgx_encl_page *encl_page;
+ struct sgx_encl *encl = (struct sgx_encl *) vma->vm_private_data;
+ void *epc_va;
+ void *secs_va;
+ int ret = -EFAULT;
+
+ if (!sgx_has_sgx2)
+ return ERR_PTR(-EFAULT);
+
+ /* if vma area is not writable then we will not eaug */
+ if (unlikely(!(vma->vm_flags & VM_WRITE)))
+ return ERR_PTR(-EFAULT);
+
+ addr &= ~(PAGE_SIZE-1);
+
+ /* Note: Invoking function holds the encl->lock */
+
+ epc_page = sgx_alloc_page(SGX_ALLOC_ATOMIC);
+ if (IS_ERR(epc_page)) {
+ return ERR_PTR(PTR_ERR(epc_page));
+ }
+
+ va_page = sgx_alloc_page(SGX_ALLOC_ATOMIC);
+ if (IS_ERR(va_page)) {
+ sgx_free_page(epc_page, encl);
+ return ERR_PTR(PTR_ERR(va_page));
+ }
+
+ encl_page = kzalloc(sizeof(struct sgx_encl_page), GFP_KERNEL);
+ if (!encl_page) {
+ sgx_free_page(epc_page, encl);
+ sgx_free_page(va_page, encl);
+ return ERR_PTR(-EFAULT);
+ }
+
+ if (!(encl->flags & SGX_ENCL_INITIALIZED))
+ goto out;
+
+ if (encl->flags & (SGX_ENCL_SUSPEND | SGX_ENCL_DEAD))
+ goto out;
+
+ /*
+ if ((rg->rg_desc.flags & SGX_GROW_DOWN_FLAG) && !write)
+ goto out;
+ */
+
+ /* Start the augmenting process */
+ ret = sgx_init_page(encl, encl_page, addr, 0, &va_page, true);
+ if (ret)
+ goto out;
+
+ /* If SECS is evicted then reload it first */
+ /* Same steps as in sgx_do_fault */
+ if (encl->flags & SGX_ENCL_SECS_EVICTED) {
+ secs_epc_page = sgx_alloc_page(SGX_ALLOC_ATOMIC);
+ if (IS_ERR(secs_epc_page)) {
+ ret = PTR_ERR(secs_epc_page);
+ secs_epc_page = NULL;
+ goto out;
+ }
+
+ ret = sgx_eldu(encl, &encl->secs, secs_epc_page, true);
+ if (ret)
+ goto out;
+
+ encl->secs.epc_page = secs_epc_page;
+ encl->flags &= ~SGX_ENCL_SECS_EVICTED;
+
+ /* Do not free */
+ secs_epc_page = NULL;
+ }
+
+ secs_va = sgx_get_page(encl->secs.epc_page);
+ epc_va = sgx_get_page(epc_page);
+
+ pginfo.srcpge = 0;
+ pginfo.secinfo = 0;
+ pginfo.linaddr = addr;
+ pginfo.secs = (unsigned long) secs_va;
+
+ ret = __eaug(&pginfo, epc_va);
+ if (ret) {
+ pr_err("sgx: eaug failure with ret=%d\n", ret);
+ goto out;
+ }
+
+ ret = vm_insert_pfn(vma, encl_page->addr, PFN_DOWN(epc_page->pa));
+ sgx_put_page(epc_va);
+ sgx_put_page(secs_va);
+ if (ret) {
+ pr_err("sgx: vm_insert_pfn failure with ret=%d\n", ret);
+ goto out;
+ }
+
+ epc_page->encl_page = encl_page;
+ encl_page->epc_page = epc_page;
+ encl->secs_child_cnt++;
+
+ ret = radix_tree_insert(&encl->page_tree, encl_page->addr >> PAGE_SHIFT,
+ encl_page);
+ if (ret) {
+ pr_err("sgx: radix_tree_insert failed with ret=%d\n", ret);
+ goto out;
+ }
+ sgx_test_and_clear_young(encl_page, encl);
+ list_add_tail(&encl_page->epc_page->list, &encl->load_list);
+ encl_page->flags |= SGX_ENCL_PAGE_ADDED;
+
+ if (va_page)
+ sgx_free_page(va_page, encl);
+ if (secs_epc_page)
+ sgx_free_page(secs_epc_page, encl);
+
+ /*
+ * Write operation corresponds to stack extension
+ * In this case the #PF is caused by a write operation,
+ * most probably a push.
+ * We return SIGBUS such that the OS invokes the enclave's exception
+ * handler which will execute eaccept.
+ */
+ if (write)
+ return ERR_PTR(-EFAULT);
+
+ return encl_page;
+
+out:
+ if (encl_page->va_offset)
+ sgx_free_va_slot(encl_page->va_page, encl_page->va_offset);
+ sgx_free_page(epc_page, encl);
+ if (va_page)
+ sgx_free_page(va_page, encl);
+ kfree(encl_page);
+ if (secs_epc_page)
+ sgx_free_page(secs_epc_page, encl);
+
+ if ((ret == -EBUSY)||(ret == -ERESTARTSYS))
+ return ERR_PTR(ret);
+
+ return ERR_PTR(-EFAULT);
+}
+
+static int isolate_range(struct sgx_encl *encl,
+ struct sgx_range *rg, struct list_head *list)
+{
+ unsigned long address, end;
+ struct sgx_encl_page *encl_page;
+ struct vm_area_struct *vma;
+ int ret;
+
+ address = rg->start_addr;
+ end = address + rg->nr_pages * PAGE_SIZE;
+
+ ret = sgx_encl_find(encl->mm, address, &vma);
+ if (ret || encl != vma->vm_private_data)
+ return -EINVAL;
+
+ for (; address < end; address += PAGE_SIZE) {
+ encl_page = ERR_PTR(-EBUSY);
+ while (encl_page == ERR_PTR(-EBUSY))
+ /* bring back page in case it was evicted */
+ encl_page = sgx_fault_page(vma, address,
+ SGX_FAULT_RESERVE, NULL);
+
+ if (IS_ERR(encl_page)) {
+ sgx_err(encl, "sgx: No page found at address 0x%lx\n",
+ address);
+ return PTR_ERR(encl_page);
+ }
+
+ /* We do not need the reserved bit anymore as page
+ * is removed from the load list
+ */
+ mutex_lock(&encl->lock);
+ list_move_tail(&encl_page->epc_page->list, list);
+ encl_page->flags &= ~SGX_ENCL_PAGE_RESERVED;
+ mutex_unlock(&encl->lock);
+ }
+
+ return 0;
+}
+
+static int __modify_range(struct sgx_encl *encl,
+ struct sgx_range *rg, struct sgx_secinfo *secinfo)
+{
+ struct sgx_encl_page *encl_page;
+ struct sgx_epc_page *epc_page, *tmp;
+ LIST_HEAD(list);
+ bool emodt = secinfo->flags & (SGX_SECINFO_TRIM | SGX_SECINFO_TCS);
+ unsigned int epoch = 0;
+ void *epc_va;
+ int ret = 0, cnt, status = 0;
+
+ ret = isolate_range(encl, rg, &list);
+ if (ret)
+ goto out;
+
+ if (list_empty(&list))
+ goto out;
+
+ /* EMODT / EMODPR */
+ list_for_each_entry_safe(epc_page, tmp, &list, list) {
+ encl_page = epc_page->encl_page;
+ if (!emodt && (encl_page->flags & SGX_ENCL_PAGE_TCS)) {
+ sgx_err(encl, "sgx: illegal request: page at\
+ address=0x%lx is a TCS, req flags=0x%llx\n",
+ encl_page->addr, secinfo->flags);
+ ret = -EINVAL;
+ continue;
+ }
+ mutex_lock(&encl->lock);
+ epc_va = sgx_get_page(epc_page);
+ status = SGX_LOCKFAIL;
+ cnt = 0;
+ while (SGX_LOCKFAIL == status && cnt < SGX_EDMM_SPIN_COUNT) {
+ if (emodt) {
+ status = __emodt(secinfo, epc_va);
+ if (!status)
+ encl_page->flags |= SGX_ENCL_PAGE_TCS;
+ } else
+ status = __emodpr(secinfo, epc_va);
+ cnt++;
+ }
+
+ epoch = encl->shadow_epoch;
+ sgx_put_page(epc_va);
+ mutex_unlock(&encl->lock);
+
+ if (status) {
+ sgx_err(encl, "sgx: Page at address=0x%lx \
+ can't be modified err=%d req flags=0x%llx\n",
+ encl_page->addr, status, secinfo->flags);
+ ret = (ret) ? ret : status;
+ } else {
+ if (SGX_SECINFO_TRIM == secinfo->flags)
+ encl_page->flags |= SGX_ENCL_PAGE_TRIM;
+ }
+ }
+
+ /* ETRACK */
+ mutex_lock(&encl->lock);
+ sgx_etrack(encl, epoch);
+ mutex_unlock(&encl->lock);
+
+ smp_call_function(sgx_ipi_cb, NULL, 1);
+
+out:
+ if (!list_empty(&list)) {
+ mutex_lock(&encl->lock);
+ list_splice(&list, &encl->load_list);
+ mutex_unlock(&encl->lock);
+ }
+
+ return ret;
+}
+
+long modify_range(struct sgx_range *rg, unsigned long flags)
+{
+ struct sgx_encl *encl;
+ struct sgx_secinfo secinfo;
+ struct sgx_range _rg;
+ unsigned long end = rg->start_addr + rg->nr_pages * PAGE_SIZE;
+ int ret = 0;
+
+ if (!sgx_has_sgx2)
+ return -ENOSYS;
+
+ if (rg->start_addr & (PAGE_SIZE - 1))
+ return -EINVAL;
+
+ if (!rg->nr_pages)
+ return -EINVAL;
+
+ ret = sgx_get_encl(rg->start_addr, &encl);
+ if (ret) {
+ pr_warn("sgx: No enclave found at start addr 0x%lx ret=%d\n",
+ rg->start_addr, ret);
+ return ret;
+ }
+
+ if (end > encl->base + encl->size) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ memset(&secinfo, 0, sizeof(secinfo));
+ secinfo.flags = flags;
+
+ /*
+ * Modifying the range by chunks of 16 pages:
+ * these pages are removed from the load list. Bigger chunks
+ * may empty EPC load lists and stall SGX.
+ */
+ for (_rg.start_addr = rg->start_addr;
+ _rg.start_addr < end;
+ rg->nr_pages -= SGX_NR_MOD_CHUNK_PAGES,
+ _rg.start_addr += SGX_NR_MOD_CHUNK_PAGES*PAGE_SIZE) {
+ _rg.nr_pages = rg->nr_pages > 0x10 ? 0x10 : rg->nr_pages;
+ ret = __modify_range(encl, &_rg, &secinfo);
+ if (ret)
+ break;
+ }
+
+out:
+ kref_put(&encl->refcount, sgx_encl_release);
+ return ret;
+}
+
+int remove_page(struct sgx_encl *encl, unsigned long address, bool trim)
+{
+ struct sgx_encl_page *encl_page;
+ struct vm_area_struct *vma;
+ struct sgx_va_page *va_page;
+ int ret;
+
+ ret = sgx_encl_find(encl->mm, address, &vma);
+ if (ret || encl != vma->vm_private_data)
+ return -EINVAL;
+
+ encl_page = sgx_fault_page(vma, address, SGX_FAULT_RESERVE, NULL);
+ if (IS_ERR(encl_page))
+ return (PTR_ERR(encl_page) == -EBUSY) ? -EBUSY : -EINVAL;
+
+ if (trim && !(encl_page->flags & SGX_ENCL_PAGE_TRIM)) {
+ encl_page->flags &= ~SGX_ENCL_PAGE_RESERVED;
+ return -EINVAL;
+ }
+
+ if (!(encl_page->flags & SGX_ENCL_PAGE_ADDED)) {
+ encl_page->flags &= ~SGX_ENCL_PAGE_RESERVED;
+ return -EINVAL;
+ }
+
+ mutex_lock(&encl->lock);
+
+ radix_tree_delete(&encl->page_tree, encl_page->addr >> PAGE_SHIFT);
+ va_page = encl_page->va_page;
+
+ if (va_page) {
+ sgx_free_va_slot(va_page, encl_page->va_offset);
+
+ if (sgx_va_slots_empty(va_page)) {
+ list_del(&va_page->list);
+ sgx_free_page(va_page->epc_page, encl);
+ kfree(va_page);
+ }
+ }
+
+ if (encl_page->epc_page) {
+ list_del(&encl_page->epc_page->list);
+ encl_page->epc_page->encl_page = NULL;
+ zap_vma_ptes(vma, encl_page->addr, PAGE_SIZE);
+ sgx_free_page(encl_page->epc_page, encl);
+ encl->secs_child_cnt--;
+ }
+
+ mutex_unlock(&encl->lock);
+
+ kfree(encl_page);
+
+ return 0;
+}
diff --git a/sgx_ioctl.c b/sgx_ioctl.c
index f7540fc..0b3476d 100644
--- a/sgx_ioctl.c
+++ b/sgx_ioctl.c
@@ -73,7 +73,7 @@
#include <linux/hashtable.h>
#include <linux/shmem_fs.h>
-static int sgx_get_encl(unsigned long addr, struct sgx_encl **encl)
+int sgx_get_encl(unsigned long addr, struct sgx_encl **encl)
{
struct mm_struct *mm = current->mm;
struct vm_area_struct *vma;
@@ -251,6 +251,123 @@ out:
return ret;
}
+long sgx_ioc_page_modpr(struct file *filep, unsigned int cmd,
+ unsigned long arg)
+{
+ struct sgx_modification_param *p =
+ (struct sgx_modification_param *) arg;
+
+ /*
+ * Only RWX flags in mask are allowed
+ * Restricting WR w/o RD is not allowed
+ */
+ if (p->flags & ~(SGX_SECINFO_R | SGX_SECINFO_W | SGX_SECINFO_X))
+ return -EINVAL;
+ if (!(p->flags & SGX_SECINFO_R) &&
+ (p->flags & SGX_SECINFO_W))
+ return -EINVAL;
+ return modify_range(&p->range, p->flags);
+}
+
+/**
+ * sgx_ioc_page_to_tcs() - Pages defined in range are switched to TCS.
+ * These pages should be of type REG.
+ * eaccept needs to be invoked after return.
+ * @arg range address of pages to be switched
+ */
+long sgx_ioc_page_to_tcs(struct file *filep, unsigned int cmd,
+ unsigned long arg)
+{
+ return modify_range((struct sgx_range *)arg, SGX_SECINFO_TCS);
+}
+
+/**
+ * sgx_ioc_trim_page() - Pages defined in range are being trimmed.
+ * These pages still belong to the enclave and can not be removed until
+ * eaccept has been invoked
+ * @arg range address of pages to be trimmed
+ */
+long sgx_ioc_trim_page(struct file *filep, unsigned int cmd,
+ unsigned long arg)
+{
+ return modify_range((struct sgx_range *)arg, SGX_SECINFO_TRIM);
+}
+
+/**
+ * sgx_ioc_page_notify_accept() - Pages defined in range will be moved to
+ * the trimmed list, i.e. they can be freely removed from now. These pages
+ * should have PT_TRIM page type and should have been eaccepted priorly
+ * @arg range address of pages
+ */
+long sgx_ioc_page_notify_accept(struct file *filep, unsigned int cmd,
+ unsigned long arg)
+{
+ struct sgx_range *rg;
+ unsigned long address, end;
+ struct sgx_encl *encl;
+ int ret, tmp_ret = 0;
+
+ if (!sgx_has_sgx2)
+ return -ENOSYS;
+
+ rg = (struct sgx_range *)arg;
+
+ address = rg->start_addr;
+ address &= ~(PAGE_SIZE-1);
+ end = address + rg->nr_pages * PAGE_SIZE;
+
+ ret = sgx_get_encl(address, &encl);
+ if (ret) {
+ pr_warn("sgx: No enclave found at start address 0x%lx\n",
+ address);
+ return ret;
+ }
+
+ for (; address < end; address += PAGE_SIZE) {
+ tmp_ret = remove_page(encl, address, true);
+ if (tmp_ret) {
+ sgx_dbg(encl, "sgx: remove failed, addr=0x%lx ret=%d\n",
+ address, tmp_ret);
+ ret = tmp_ret;
+ continue;
+ }
+ }
+
+ kref_put(&encl->refcount, sgx_encl_release);
+
+ return ret;
+}
+
+/**
+ * sgx_ioc_page_remove() - Pages defined by address will be removed
+ * @arg address of page
+ */
+long sgx_ioc_page_remove(struct file *filep, unsigned int cmd,
+ unsigned long arg)
+{
+ struct sgx_encl *encl;
+ unsigned long address = *((unsigned long *) arg);
+ int ret;
+
+ if (!sgx_has_sgx2)
+ return -ENOSYS;
+
+ if (sgx_get_encl(address, &encl)) {
+ pr_warn("sgx: No enclave found at start address 0x%lx\n",
+ address);
+ return -EINVAL;
+ }
+
+ ret = remove_page(encl, address, false);
+ if (ret) {
+ pr_warn("sgx: Failed to remove page, address=0x%lx ret=%d\n",
+ address, ret);
+ }
+
+ kref_put(&encl->refcount, sgx_encl_release);
+ return ret;
+}
+
typedef long (*sgx_ioc_t)(struct file *filep, unsigned int cmd,
unsigned long arg);
@@ -270,6 +387,21 @@ long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
case SGX_IOC_ENCLAVE_INIT:
handler = sgx_ioc_enclave_init;
break;
+ case SGX_IOC_ENCLAVE_EMODPR:
+ handler = sgx_ioc_page_modpr;
+ break;
+ case SGX_IOC_ENCLAVE_MKTCS:
+ handler = sgx_ioc_page_to_tcs;
+ break;
+ case SGX_IOC_ENCLAVE_TRIM:
+ handler = sgx_ioc_trim_page;
+ break;
+ case SGX_IOC_ENCLAVE_NOTIFY_ACCEPT:
+ handler = sgx_ioc_page_notify_accept;
+ break;
+ case SGX_IOC_ENCLAVE_PAGE_REMOVE:
+ handler = sgx_ioc_page_remove;
+ break;
default:
return -ENOIOCTLCMD;
}
diff --git a/sgx_main.c b/sgx_main.c
index 7ff3864..7b7c384 100644
--- a/sgx_main.c
+++ b/sgx_main.c
@@ -71,7 +71,7 @@
#include <linux/platform_device.h>
#define DRV_DESCRIPTION "Intel SGX Driver"
-#define DRV_VERSION "0.10"
+#define DRV_VERSION "0.11"
MODULE_DESCRIPTION(DRV_DESCRIPTION);
MODULE_AUTHOR("Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>");
@@ -95,6 +95,7 @@ u64 sgx_encl_size_max_64;
u64 sgx_xfrm_mask = 0x3;
u32 sgx_misc_reserved;
u32 sgx_xsave_size_tbl[64];
+bool sgx_has_sgx2;
#ifdef CONFIG_COMPAT
long sgx_compat_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
@@ -325,6 +326,8 @@ static int sgx_drv_probe(struct platform_device *pdev)
return -ENODEV;
}
+ sgx_has_sgx2 = (eax & 2) != 0;
+
return sgx_dev_init(&pdev->dev);
}
diff --git a/sgx_page_cache.c b/sgx_page_cache.c
index 8472037..b4bc985 100644
--- a/sgx_page_cache.c
+++ b/sgx_page_cache.c
@@ -335,7 +335,7 @@ static void sgx_write_pages(struct sgx_encl *encl, struct list_head *src)
}
/* ETRACK */
- sgx_etrack(encl);
+ sgx_etrack(encl, encl->shadow_epoch);
/* EWB */
while (!list_empty(src)) {
diff --git a/sgx_user.h b/sgx_user.h
index a15f87b..50f0931 100644
--- a/sgx_user.h
+++ b/sgx_user.h
@@ -69,6 +69,16 @@
_IOW(SGX_MAGIC, 0x01, struct sgx_enclave_add_page)
#define SGX_IOC_ENCLAVE_INIT \
_IOW(SGX_MAGIC, 0x02, struct sgx_enclave_init)
+#define SGX_IOC_ENCLAVE_EMODPR \
+ _IOW(SGX_MAGIC, 0x09, struct sgx_modification_param)
+#define SGX_IOC_ENCLAVE_MKTCS \
+ _IOW(SGX_MAGIC, 0x0a, struct sgx_range)
+#define SGX_IOC_ENCLAVE_TRIM \
+ _IOW(SGX_MAGIC, 0x0b, struct sgx_range)
+#define SGX_IOC_ENCLAVE_NOTIFY_ACCEPT \
+ _IOW(SGX_MAGIC, 0x0c, struct sgx_range)
+#define SGX_IOC_ENCLAVE_PAGE_REMOVE \
+ _IOW(SGX_MAGIC, 0x0d, unsigned long)
/* SGX leaf instruction return values */
#define SGX_SUCCESS 0
@@ -90,6 +100,7 @@
#define SGX_INVALID_EINITTOKEN 16
#define SGX_PREV_TRK_INCMPL 17
#define SGX_PG_IS_SECS 18
+#define SGX_PAGE_NOT_MODIFIABLE 20
#define SGX_INVALID_CPUSVN 32
#define SGX_INVALID_ISVSVN 64
#define SGX_UNMASKED_EVENT 128
@@ -136,4 +147,18 @@ struct sgx_enclave_init {
__u64 einittoken;
} __attribute__((__packed__));
+/*
+ * SGX2.0 definitions
+ */
+
+struct sgx_range {
+ unsigned long start_addr;
+ unsigned int nr_pages;
+};
+
+struct sgx_modification_param {
+ struct sgx_range range;
+ unsigned long flags;
+};
+
#endif /* _UAPI_ASM_X86_SGX_H */
diff --git a/sgx_util.c b/sgx_util.c
index ff0e40a..fd5022a 100644
--- a/sgx_util.c
+++ b/sgx_util.c
@@ -135,7 +135,7 @@ void sgx_invalidate(struct sgx_encl *encl, bool flush_cpus)
sgx_flush_cpus(encl);
}
-static void sgx_ipi_cb(void *info)
+void sgx_ipi_cb(void *info)
{
}
@@ -144,10 +144,10 @@ void sgx_flush_cpus(struct sgx_encl *encl)
on_each_cpu_mask(mm_cpumask(encl->mm), sgx_ipi_cb, NULL, 1);
}
-static int sgx_eldu(struct sgx_encl *encl,
- struct sgx_encl_page *encl_page,
- struct sgx_epc_page *epc_page,
- bool is_secs)
+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;
@@ -212,7 +212,8 @@ out:
static struct sgx_encl_page *sgx_do_fault(struct vm_area_struct *vma,
unsigned long addr,
- unsigned int flags)
+ unsigned int flags,
+ struct vm_fault *vmf)
{
struct sgx_encl *encl = vma->vm_private_data;
struct sgx_encl_page *entry;
@@ -220,6 +221,7 @@ static struct sgx_encl_page *sgx_do_fault(struct vm_area_struct *vma,
struct sgx_epc_page *secs_epc_page = NULL;
bool reserve = (flags & SGX_FAULT_RESERVE) != 0;
int rc = 0;
+ bool write = (vmf) ? (FAULT_FLAG_WRITE & vmf->flags) : false;
/* If process was forked, VMA is still there but vm_private_data is set
* to NULL.
@@ -230,6 +232,14 @@ static struct sgx_encl_page *sgx_do_fault(struct vm_area_struct *vma,
mutex_lock(&encl->lock);
entry = radix_tree_lookup(&encl->page_tree, addr >> PAGE_SHIFT);
+ if (vmf && !entry) {
+ entry = sgx_encl_augment(vma, addr, write);
+ goto out;
+ }
+
+ /* No entry found can not happen in 'reloading an evicted page'
+ * flow.
+ */
if (!entry) {
rc = -EFAULT;
goto out;
@@ -331,12 +341,13 @@ out:
struct sgx_encl_page *sgx_fault_page(struct vm_area_struct *vma,
unsigned long addr,
- unsigned int flags)
+ unsigned int flags,
+ struct vm_fault *vmf)
{
struct sgx_encl_page *entry;
do {
- entry = sgx_do_fault(vma, addr, flags);
+ entry = sgx_do_fault(vma, addr, flags, vmf);
if (!(flags & SGX_FAULT_RESERVE))
break;
} while (PTR_ERR(entry) == -EBUSY);
@@ -360,16 +371,25 @@ void sgx_eblock(struct sgx_encl *encl, struct sgx_epc_page *epc_page)
}
-void sgx_etrack(struct sgx_encl *encl)
+void sgx_etrack(struct sgx_encl *encl, unsigned int epoch)
{
void *epc;
int ret;
+ /* If someone already called etrack in the meantime */
+ if (epoch < encl->shadow_epoch)
+ return;
+
epc = sgx_get_page(encl->secs.epc_page);
ret = __etrack(epc);
sgx_put_page(epc);
+ encl->shadow_epoch++;
- if (ret) {
+ if (ret == SGX_PREV_TRK_INCMPL) {
+ sgx_dbg(encl, "ETRACK returned %d\n", ret);
+ smp_call_function(sgx_ipi_cb, NULL, 1);
+ BUG_ON(__etrack(epc));
+ } else if (ret) {
sgx_crit(encl, "ETRACK returned %d\n", ret);
sgx_invalidate(encl, true);
}
diff --git a/sgx_vma.c b/sgx_vma.c
index dae9eb9..bcd1047 100644
--- a/sgx_vma.c
+++ b/sgx_vma.c
@@ -76,6 +76,12 @@ static void sgx_vma_open(struct vm_area_struct *vma)
if (!encl)
return;
+ /* protect from fork */
+ if (encl->mm != current->mm) {
+ vma->vm_private_data = NULL;
+ return;
+ }
+
/* kref cannot underflow because ECREATE ioctl checks that there is only
* one single VMA for the enclave before proceeding.
*/
@@ -113,7 +119,7 @@ static int sgx_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
#endif
struct sgx_encl_page *entry;
- entry = sgx_fault_page(vma, addr, 0);
+ entry = sgx_fault_page(vma, addr, 0, vmf);
if (!IS_ERR(entry) || PTR_ERR(entry) == -EBUSY)
return VM_FAULT_NOPAGE;
@@ -212,7 +218,7 @@ static int sgx_vma_access(struct vm_area_struct *vma, unsigned long addr,
entry->flags &= ~SGX_ENCL_PAGE_RESERVED;
entry = sgx_fault_page(vma, (addr + i) & PAGE_MASK,
- SGX_FAULT_RESERVE);
+ SGX_FAULT_RESERVE, NULL);
if (IS_ERR(entry)) {
ret = PTR_ERR(entry);
entry = NULL;
--
2.7.4