Recreate linux-sgx-driver as a subtree rather than a submodule. (#21)

* Remove linux-sgx-driver as a submodule.
* Squashed 'sgx-jvm/linux-sgx-driver/' content from commit cd516380e

git-subtree-dir: sgx-jvm/linux-sgx-driver
git-subtree-split: cd516380e5ffa008505dc0e86ddbf45276021219
This commit is contained in:
Chris Rankin 2017-08-12 16:27:33 +01:00 committed by Michal Kit
parent 2cfadd8b95
commit aa82c55c87
16 changed files with 3700 additions and 4 deletions

3
.gitmodules vendored
View File

@ -1,3 +0,0 @@
[submodule "linux-sgx-driver"]
path = sgx-jvm/linux-sgx-driver
url = git@github.com:01org/linux-sgx-driver.git

@ -1 +0,0 @@
Subproject commit 3abcf829226e7fdf46ae221cf1f9f9562d503fa5

7
sgx-jvm/linux-sgx-driver/.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
*.ko
*.o
*.cmd
*.mod.*
*.symvers
*.order
*.tmp_versions

View File

@ -0,0 +1,4 @@
Intel(R) SGX driver is licensed under GNU General Public License, version 2 (GPL-2.0)
https://opensource.org/licenses/GPL-2.0

View File

@ -0,0 +1,23 @@
ifneq ($(KERNELRELEASE),)
isgx-y := \
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
PWD := $(shell pwd)
default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
install: default
$(MAKE) INSTALL_MOD_DIR=kernel/drivers/intel/sgx -C $(KDIR) M=$(PWD) modules_install
sh -c "cat /etc/modules | grep -Fxq isgx || echo isgx >> /etc/modules"
endif
clean:
rm -vrf *.o *.ko *.order *.symvers *.mod.c .tmp_versions .*o.cmd

View File

@ -0,0 +1,70 @@
Intel(R) Software Guard Extensions for Linux\* OS
================================================
# linux-sgx-driver
Introduction
------------
Intel(R) Software Guard Extensions (Intel(R) SGX) is an Intel technology for application developers seeking to protect select code and data from disclosure or modification.
The Linux SGX software stack is comprised of the Intel(R) SGX driver, the Intel(R) SGX SDK, and the Intel(R) SGX Platform Software. The Intel(R) SGX SDK and Intel(R) SGX PSW are hosted in the [linux-sgx](https://github.com/01org/linux-sgx) project.
The [linux-sgx-driver](https://github.com/01org/linux-sgx-driver) project hosts the out-of-tree driver for the Linux Intel(R) SGX software stack, which will be used until the driver upstreaming process is complete.
License
-------
See License.txt for details.
Contributing
-------
Starting from 05/2017, we are importing the sgx driver code from the in-kernel sgx repository located at git-hub: https://github.com/jsakkine-intel/linux-sgx.git. Any contribution should be done there. Future versions of the sgx driver code will be imported later on. The motivation behind this decision is to maintain a single source code of the sgx linux driver.
An additional directory inker2ext/ has been created, it contains a script and a patch file that can be used in order to separately generate the code base of the sgx external module; it can be used in case someone wants the newest sgx driver as an external module and does not want to wait for next update.
Documentation
-------------
- [Intel(R) SGX for Linux\* OS](https://01.org/intel-softwareguard-extensions) project home page on [01.org](http://01.org)
- [Intel(R) SGX Programming Reference](https://software.intel.com/sites/default/files/managed/48/88/329298-002.pdf)
Build and Install the Intel(R) SGX Driver
-----------------------------------------
### Prerequisites
- Ensure that you have the following required operating systems:
* Ubuntu* Desktop-16.04-LTS 64bits
* Red Hat Enterprise Linux Server release 7.2 64bits
* CentOS 7.3.1611 64bits
- Ensure that you have the following required hardware:
* 6th Generation Intel(R) Core(TM) Processor (code named Skylake)
* 7th Generation Intel(R) Core(TM) Processor (code named Kaby Lake)
- Configure the system with the **SGX hardware enabled** option.
### Build the Intel(R) SGX Driver
To build Intel(R) SGX driver, change the directory to the driver path and enter the following command:
```
$ make
```
You can find the driver *isgx.ko* generated in the same directory.
### Install the Intel(R) SGX Driver
To install the Intel(R) SGX driver, enter the following command with root privilege:
```
$ sudo mkdir -p "/lib/modules/"`uname -r`"/kernel/drivers/intel/sgx"
$ sudo cp isgx.ko "/lib/modules/"`uname -r`"/kernel/drivers/intel/sgx"
$ sudo sh -c "cat /etc/modules | grep -Fxq isgx || echo isgx >> /etc/modules"
$ sudo /sbin/depmod
$ sudo /sbin/modprobe isgx
```
On Red Hat Enterprise Linux Server or CentOS, need to run below command on each reboot
```
$ sudo /sbin/modprobe isgx
```
### Uninstall the Intel(R) SGX Driver
Before uninstall the Intel(R) SGX driver, make sure the aesmd service is stopped. See the topic, Start or Stop aesmd Service, on how to stop the aesmd service.
To uninstall the Intel(R) SGX driver, enter the following commands:
```
$ sudo /sbin/modprobe -r isgx
$ sudo rm -rf "/lib/modules/"`uname -r`"/kernel/drivers/intel/sgx"
$ sudo /sbin/depmod
$ sudo /bin/sed -i '/^isgx$/d' /etc/modules
```

View File

@ -0,0 +1,141 @@
From 06bc2c551c06d45e2fe06dd54e7ebace7cbc674b Mon Sep 17 00:00:00 2001
From: Serge Ayoun <serge.ayoun@intel.com>
Date: Wed, 3 May 2017 15:55:19 +0300
Subject: [PATCH] isgx: internal to external tree changes
Signed-off-by: Serge Ayoun <serge.ayoun@intel.com>
---
drivers/platform/x86/intel_sgx/sgx.h | 5 +++--
drivers/platform/x86/intel_sgx/sgx_ioctl.c | 6 +++++-
drivers/platform/x86/intel_sgx/sgx_main.c | 5 ++++-
drivers/platform/x86/intel_sgx/sgx_page_cache.c | 6 +++++-
drivers/platform/x86/intel_sgx/sgx_util.c | 6 +++++-
drivers/platform/x86/intel_sgx/sgx_vma.c | 11 +++++++++++
6 files changed, 33 insertions(+), 6 deletions(-)
diff --git a/drivers/platform/x86/intel_sgx/sgx.h b/drivers/platform/x86/intel_sgx/sgx.h
index e59ff01..01dcfb3 100644
--- a/drivers/platform/x86/intel_sgx/sgx.h
+++ b/drivers/platform/x86/intel_sgx/sgx.h
@@ -60,15 +60,16 @@
#ifndef __ARCH_INTEL_SGX_H__
#define __ARCH_INTEL_SGX_H__
-#include <uapi/asm/sgx.h>
+#include "sgx_user.h"
#include <linux/kref.h>
+#include <linux/version.h>
#include <linux/rbtree.h>
#include <linux/rwsem.h>
#include <linux/sched.h>
#include <linux/workqueue.h>
#include <linux/mmu_notifier.h>
#include <linux/radix-tree.h>
-#include <asm/sgx.h>
+#include "sgx_arch.h"
#define SGX_EINIT_SPIN_COUNT 20
#define SGX_EINIT_SLEEP_COUNT 50
diff --git a/drivers/platform/x86/intel_sgx/sgx_ioctl.c b/drivers/platform/x86/intel_sgx/sgx_ioctl.c
index ba7c0d2..e1db58c 100644
--- a/drivers/platform/x86/intel_sgx/sgx_ioctl.c
+++ b/drivers/platform/x86/intel_sgx/sgx_ioctl.c
@@ -64,7 +64,11 @@
#include <linux/file.h>
#include <linux/highmem.h>
#include <linux/ratelimit.h>
-#include <linux/sched/signal.h>
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0))
+ #include <linux/sched/signal.h>
+#else
+ #include <linux/signal.h>
+#endif
#include <linux/slab.h>
#include <linux/hashtable.h>
#include <linux/shmem_fs.h>
diff --git a/drivers/platform/x86/intel_sgx/sgx_main.c b/drivers/platform/x86/intel_sgx/sgx_main.c
index 5890643..70b95c1 100644
--- a/drivers/platform/x86/intel_sgx/sgx_main.c
+++ b/drivers/platform/x86/intel_sgx/sgx_main.c
@@ -78,6 +78,9 @@
MODULE_DESCRIPTION(DRV_DESCRIPTION);
MODULE_AUTHOR("Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>");
MODULE_VERSION(DRV_VERSION);
+#ifndef X86_FEATURE_SGX
+#define X86_FEATURE_SGX (9 * 32 + 2)
+#endif
/*
* Global data.
@@ -156,7 +159,7 @@ static unsigned long sgx_get_unmapped_area(struct file *file,
};
static struct miscdevice sgx_dev = {
- .name = "sgx",
+ .name = "isgx",
.fops = &sgx_fops,
.mode = 0666,
};
diff --git a/drivers/platform/x86/intel_sgx/sgx_page_cache.c b/drivers/platform/x86/intel_sgx/sgx_page_cache.c
index 59a67cb..0cb9b4e 100644
--- a/drivers/platform/x86/intel_sgx/sgx_page_cache.c
+++ b/drivers/platform/x86/intel_sgx/sgx_page_cache.c
@@ -63,7 +63,11 @@
#include <linux/highmem.h>
#include <linux/kthread.h>
#include <linux/ratelimit.h>
-#include <linux/sched/signal.h>
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0))
+ #include <linux/sched/signal.h>
+#else
+ #include <linux/signal.h>
+#endif
#include <linux/slab.h>
#define SGX_NR_LOW_EPC_PAGES_DEFAULT 32
diff --git a/drivers/platform/x86/intel_sgx/sgx_util.c b/drivers/platform/x86/intel_sgx/sgx_util.c
index 1e9fa18..4226f2a 100644
--- a/drivers/platform/x86/intel_sgx/sgx_util.c
+++ b/drivers/platform/x86/intel_sgx/sgx_util.c
@@ -61,7 +61,11 @@
#include "sgx.h"
#include <linux/highmem.h>
#include <linux/shmem_fs.h>
-#include <linux/sched/mm.h>
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0))
+ #include <linux/sched/signal.h>
+#else
+ #include <linux/signal.h>
+#endif
struct page *sgx_get_backing(struct sgx_encl *encl,
struct sgx_encl_page *entry,
diff --git a/drivers/platform/x86/intel_sgx/sgx_vma.c b/drivers/platform/x86/intel_sgx/sgx_vma.c
index f83c0b8..4dbe26c 100644
--- a/drivers/platform/x86/intel_sgx/sgx_vma.c
+++ b/drivers/platform/x86/intel_sgx/sgx_vma.c
@@ -94,10 +94,21 @@ static void sgx_vma_close(struct vm_area_struct *vma)
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);
--
1.9.1

View File

@ -0,0 +1,27 @@
#! /bin/bash
# Should run within git-hub sgx driver directory:
# Usage:
# kernel_2_extern <in-kernel-root-path> <patch-file-name>
pa=`pwd`
file="$1/arch/x86/include/asm/sgx.h"
if [ ! -f $file ]; then
echo "Missing file $file"
exit
fi
cp $file sgx_arch.h
file="$1/arch/x86/include/uapi/asm/sgx.h"
if [ ! -f $file ]; then
echo "Missing file $file"
exit
fi
cp $file sgx_user.h
cd $1
git apply $pa/$2
cd $pa
cp $1/drivers/platform/x86/intel_sgx/sgx* .

View File

@ -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 <jarkko.sakkinen@linux.intel.com>
* 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 <jarkko.sakkinen@linux.intel.com>
* Suresh Siddha <suresh.b.siddha@intel.com>
* Serge Ayoun <serge.ayoun@intel.com>
* Shay Katz-zamir <shay.katz-zamir@intel.com>
*/
#ifndef __ARCH_INTEL_SGX_H__
#define __ARCH_INTEL_SGX_H__
#include "sgx_user.h"
#include <linux/kref.h>
#include <linux/version.h>
#include <linux/rbtree.h>
#include <linux/rwsem.h>
#include <linux/sched.h>
#include <linux/workqueue.h>
#include <linux/mmu_notifier.h>
#include <linux/radix-tree.h>
#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__ */

View File

@ -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 <jarkko.sakkinen@linux.intel.com>
* 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 <jarkko.sakkinen@linux.intel.com>
* Suresh Siddha <suresh.b.siddha@intel.com>
* Serge Ayoun <serge.ayoun@intel.com>
* Shay Katz-zamir <shay.katz-zamir@intel.com>
*/
#ifndef _ASM_X86_SGX_H
#define _ASM_X86_SGX_H
#include <asm/asm.h>
#include <linux/bitops.h>
#include <linux/err.h>
#include <linux/types.h>
#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 */

View File

@ -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 <jarkko.sakkinen@linux.intel.com>
* 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 <jarkko.sakkinen@linux.intel.com>
* Suresh Siddha <suresh.b.siddha@intel.com>
* Serge Ayoun <serge.ayoun@intel.com>
* Shay Katz-zamir <shay.katz-zamir@intel.com>
* Sean Christopherson <sean.j.christopherson@intel.com>
*/
#include "sgx.h"
#include <asm/mman.h>
#include <linux/delay.h>
#include <linux/file.h>
#include <linux/highmem.h>
#include <linux/ratelimit.h>
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0))
#include <linux/sched/signal.h>
#else
#include <linux/signal.h>
#endif
#include <linux/slab.h>
#include <linux/hashtable.h>
#include <linux/shmem_fs.h>
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(&current->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(&current->mm->mmap_sem);
goto out;
}
vma->vm_private_data = encl;
up_read(&current->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;
}

View File

@ -0,0 +1,451 @@
/*
* 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 <jarkko.sakkinen@linux.intel.com>
* 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 <jarkko.sakkinen@linux.intel.com>
* Suresh Siddha <suresh.b.siddha@intel.com>
* Serge Ayoun <serge.ayoun@intel.com>
* Shay Katz-zamir <shay.katz-zamir@intel.com>
* Sean Christopherson <sean.j.christopherson@intel.com>
*/
#include "sgx.h"
#include <linux/acpi.h>
#include <linux/file.h>
#include <linux/highmem.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/suspend.h>
#include <linux/hashtable.h>
#include <linux/kthread.h>
#include <linux/platform_device.h>
#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 <jarkko.sakkinen@linux.intel.com>");
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 >> 8) & 0xFF);
#endif
sgx_encl_size_max_32 = 1ULL << (edx & 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 atomic_t sgx_init_flag = ATOMIC_INIT(0);
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;
if (atomic_cmpxchg(&sgx_init_flag, 0, 1)) {
pr_warn("intel_sgx: second initialization call skipped\n");
return 0;
}
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;
if (!atomic_cmpxchg(&sgx_init_flag, 1, 0)) {
pr_warn("intel_sgx: second release call skipped\n");
return 0;
}
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),
},
};
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0))
module_platform_driver(sgx_drv);
#else
static struct platform_device *pdev;
int init_sgx_module(void)
{
platform_driver_register(&sgx_drv);
pdev = platform_device_register_simple("intel_sgx", 0, NULL, 0);
if (IS_ERR(pdev))
pr_err("platform_device_register_simple failed\n");
return 0;
}
void cleanup_sgx_module(void)
{
dev_set_uevent_suppress(&pdev->dev, true);
platform_device_unregister(pdev);
platform_driver_unregister(&sgx_drv);
}
module_init(init_sgx_module);
module_exit(cleanup_sgx_module);
#endif
MODULE_LICENSE("Dual BSD/GPL");

View File

@ -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 <jarkko.sakkinen@linux.intel.com>
* 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 <jarkko.sakkinen@linux.intel.com>
* Suresh Siddha <suresh.b.siddha@intel.com>
* Serge Ayoun <serge.ayoun@intel.com>
* Shay Katz-zamir <shay.katz-zamir@intel.com>
* Sean Christopherson <sean.j.christopherson@intel.com>
*/
#include "sgx.h"
#include <linux/freezer.h>
#include <linux/highmem.h>
#include <linux/kthread.h>
#include <linux/ratelimit.h>
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0))
#include <linux/sched/signal.h>
#else
#include <linux/signal.h>
#endif
#include <linux/slab.h>
#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);

View File

@ -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 <jarkko.sakkinen@linux.intel.com>
* 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 <jarkko.sakkinen@linux.intel.com>
* Suresh Siddha <suresh.b.siddha@intel.com>
* Serge Ayoun <serge.ayoun@intel.com>
* Shay Katz-zamir <shay.katz-zamir@intel.com>
*/
#ifndef _UAPI_ASM_X86_SGX_H
#define _UAPI_ASM_X86_SGX_H
#include <linux/types.h>
#include <linux/ioctl.h>
#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 */

View File

@ -0,0 +1,437 @@
/*
* 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 <jarkko.sakkinen@linux.intel.com>
* 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 <jarkko.sakkinen@linux.intel.com>
* Suresh Siddha <suresh.b.siddha@intel.com>
* Serge Ayoun <serge.ayoun@intel.com>
* Shay Katz-zamir <shay.katz-zamir@intel.com>
* Sean Christopherson <sean.j.christopherson@intel.com>
*/
#include "sgx.h"
#include <linux/highmem.h>
#include <linux/shmem_fs.h>
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0))
#include <linux/sched/signal.h>
#else
#include <linux/signal.h>
#endif
#include "linux/file.h"
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);
}

View File

@ -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 <jarkko.sakkinen@linux.intel.com>
* 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 <jarkko.sakkinen@linux.intel.com>
* Suresh Siddha <suresh.b.siddha@intel.com>
* Serge Ayoun <serge.ayoun@intel.com>
* Shay Katz-zamir <shay.katz-zamir@intel.com>
* Sean Christopherson <sean.j.christopherson@intel.com>
*/
#include "sgx.h"
#include <asm/mman.h>
#include <linux/delay.h>
#include <linux/file.h>
#include <linux/highmem.h>
#include <linux/ratelimit.h>
#include <linux/slab.h>
#include <linux/hashtable.h>
#include <linux/shmem_fs.h>
#include <linux/mm.h>
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,
};