x86/sgx: Add SGX_IOC_ENCLAVE_CREATE
Add an ioctl() that performs the ECREATE function of the ENCLS instruction, which creates an SGX Enclave Control Structure (SECS). Although the SECS is an in-memory data structure, it is present in enclave memory and is not directly accessible by software. Co-developed-by: Sean Christopherson <sean.j.christopherson@intel.com> Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com> Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org> Signed-off-by: Borislav Petkov <bp@suse.de> Tested-by: Jethro Beekman <jethro@fortanix.com> Link: https://lkml.kernel.org/r/20201112220135.165028-13-jarkko@kernel.org
This commit is contained in:
parent
3fe0778eda
commit
888d249117
|
@ -323,6 +323,7 @@ Code Seq# Include File Comments
|
|||
<mailto:tlewis@mindspring.com>
|
||||
0xA3 90-9F linux/dtlk.h
|
||||
0xA4 00-1F uapi/linux/tee.h Generic TEE subsystem
|
||||
0xA4 00-1F uapi/asm/sgx.h <mailto:linux-sgx@vger.kernel.org>
|
||||
0xAA 00-3F linux/uapi/linux/userfaultfd.h
|
||||
0xAB 00-1F linux/nbd.h
|
||||
0xAC 00-1F linux/raw.h
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
/*
|
||||
* Copyright(c) 2016-20 Intel Corporation.
|
||||
*/
|
||||
#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)
|
||||
|
||||
/**
|
||||
* 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;
|
||||
};
|
||||
|
||||
#endif /* _UAPI_ASM_X86_SGX_H */
|
|
@ -1,4 +1,5 @@
|
|||
obj-y += \
|
||||
driver.o \
|
||||
encl.o \
|
||||
ioctl.o \
|
||||
main.o
|
||||
|
|
|
@ -88,10 +88,22 @@ static unsigned long sgx_get_unmapped_area(struct file *file,
|
|||
return current->mm->get_unmapped_area(file, addr, len, pgoff, flags);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
static long sgx_compat_ioctl(struct file *filep, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
return sgx_ioctl(filep, cmd, arg);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct file_operations sgx_encl_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = sgx_open,
|
||||
.release = sgx_release,
|
||||
.unlocked_ioctl = sgx_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = sgx_compat_ioctl,
|
||||
#endif
|
||||
.mmap = sgx_mmap,
|
||||
.get_unmapped_area = sgx_get_unmapped_area,
|
||||
};
|
||||
|
|
|
@ -9,8 +9,11 @@
|
|||
#include <linux/rwsem.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <uapi/asm/sgx.h>
|
||||
#include "sgx.h"
|
||||
|
||||
long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
|
||||
|
||||
int sgx_drv_init(void);
|
||||
|
||||
#endif /* __ARCH_X86_SGX_DRIVER_H__ */
|
||||
|
|
|
@ -46,6 +46,7 @@ static vm_fault_t sgx_vma_fault(struct vm_fault *vmf)
|
|||
struct sgx_encl_page *entry;
|
||||
unsigned long phys_addr;
|
||||
struct sgx_encl *encl;
|
||||
unsigned long pfn;
|
||||
vm_fault_t ret;
|
||||
|
||||
encl = vma->vm_private_data;
|
||||
|
@ -61,6 +62,13 @@ static vm_fault_t sgx_vma_fault(struct vm_fault *vmf)
|
|||
|
||||
phys_addr = sgx_get_epc_phys_addr(entry->epc_page);
|
||||
|
||||
/* Check if another thread got here first to insert the PTE. */
|
||||
if (!follow_pfn(vma, addr, &pfn)) {
|
||||
mutex_unlock(&encl->lock);
|
||||
|
||||
return VM_FAULT_NOPAGE;
|
||||
}
|
||||
|
||||
ret = vmf_insert_pfn(vma, addr, PFN_DOWN(phys_addr));
|
||||
if (ret != VM_FAULT_NOPAGE) {
|
||||
mutex_unlock(&encl->lock);
|
||||
|
|
|
@ -26,9 +26,16 @@ struct sgx_encl_page {
|
|||
struct sgx_encl *encl;
|
||||
};
|
||||
|
||||
enum sgx_encl_flags {
|
||||
SGX_ENCL_IOCTL = BIT(0),
|
||||
SGX_ENCL_DEBUG = BIT(1),
|
||||
SGX_ENCL_CREATED = BIT(2),
|
||||
};
|
||||
|
||||
struct sgx_encl {
|
||||
unsigned long base;
|
||||
unsigned long size;
|
||||
unsigned long flags;
|
||||
unsigned int page_cnt;
|
||||
unsigned int secs_child_cnt;
|
||||
struct mutex lock;
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright(c) 2016-20 Intel Corporation. */
|
||||
|
||||
#include <asm/mman.h>
|
||||
#include <linux/mman.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/hashtable.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/sched/signal.h>
|
||||
#include <linux/shmem_fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/suspend.h>
|
||||
#include "driver.h"
|
||||
#include "encl.h"
|
||||
#include "encls.h"
|
||||
|
||||
static int sgx_encl_create(struct sgx_encl *encl, struct sgx_secs *secs)
|
||||
{
|
||||
struct sgx_epc_page *secs_epc;
|
||||
struct sgx_pageinfo pginfo;
|
||||
struct sgx_secinfo secinfo;
|
||||
unsigned long encl_size;
|
||||
long ret;
|
||||
|
||||
/* The extra page goes to SECS. */
|
||||
encl_size = secs->size + PAGE_SIZE;
|
||||
|
||||
secs_epc = __sgx_alloc_epc_page();
|
||||
if (IS_ERR(secs_epc))
|
||||
return PTR_ERR(secs_epc);
|
||||
|
||||
encl->secs.epc_page = secs_epc;
|
||||
|
||||
pginfo.addr = 0;
|
||||
pginfo.contents = (unsigned long)secs;
|
||||
pginfo.metadata = (unsigned long)&secinfo;
|
||||
pginfo.secs = 0;
|
||||
memset(&secinfo, 0, sizeof(secinfo));
|
||||
|
||||
ret = __ecreate((void *)&pginfo, sgx_get_epc_virt_addr(secs_epc));
|
||||
if (ret) {
|
||||
ret = -EIO;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if (secs->attributes & SGX_ATTR_DEBUG)
|
||||
set_bit(SGX_ENCL_DEBUG, &encl->flags);
|
||||
|
||||
encl->secs.encl = encl;
|
||||
encl->base = secs->base;
|
||||
encl->size = secs->size;
|
||||
|
||||
/* Set only after completion, as encl->lock has not been taken. */
|
||||
set_bit(SGX_ENCL_CREATED, &encl->flags);
|
||||
|
||||
return 0;
|
||||
|
||||
err_out:
|
||||
sgx_free_epc_page(encl->secs.epc_page);
|
||||
encl->secs.epc_page = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* sgx_ioc_enclave_create() - handler for %SGX_IOC_ENCLAVE_CREATE
|
||||
* @encl: An enclave pointer.
|
||||
* @arg: The ioctl argument.
|
||||
*
|
||||
* Allocate kernel data structures for the enclave and invoke ECREATE.
|
||||
*
|
||||
* Return:
|
||||
* - 0: Success.
|
||||
* - -EIO: ECREATE failed.
|
||||
* - -errno: POSIX error.
|
||||
*/
|
||||
static long sgx_ioc_enclave_create(struct sgx_encl *encl, void __user *arg)
|
||||
{
|
||||
struct sgx_enclave_create create_arg;
|
||||
void *secs;
|
||||
int ret;
|
||||
|
||||
if (test_bit(SGX_ENCL_CREATED, &encl->flags))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(&create_arg, arg, sizeof(create_arg)))
|
||||
return -EFAULT;
|
||||
|
||||
secs = kmalloc(PAGE_SIZE, GFP_KERNEL);
|
||||
if (!secs)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(secs, (void __user *)create_arg.src, PAGE_SIZE))
|
||||
ret = -EFAULT;
|
||||
else
|
||||
ret = sgx_encl_create(encl, secs);
|
||||
|
||||
kfree(secs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct sgx_encl *encl = filep->private_data;
|
||||
int ret;
|
||||
|
||||
if (test_and_set_bit(SGX_ENCL_IOCTL, &encl->flags))
|
||||
return -EBUSY;
|
||||
|
||||
switch (cmd) {
|
||||
case SGX_IOC_ENCLAVE_CREATE:
|
||||
ret = sgx_ioc_enclave_create(encl, (void __user *)arg);
|
||||
break;
|
||||
default:
|
||||
ret = -ENOIOCTLCMD;
|
||||
break;
|
||||
}
|
||||
|
||||
clear_bit(SGX_ENCL_IOCTL, &encl->flags);
|
||||
return ret;
|
||||
}
|
Loading…
Reference in New Issue