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:
Jarkko Sakkinen 2020-11-13 00:01:23 +02:00 committed by Borislav Petkov
parent 3fe0778eda
commit 888d249117
8 changed files with 180 additions and 0 deletions

View file

@ -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

View file

@ -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 */

View file

@ -1,4 +1,5 @@
obj-y += \
driver.o \
encl.o \
ioctl.o \
main.o

View file

@ -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,
};

View file

@ -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__ */

View file

@ -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);

View file

@ -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;

View file

@ -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;
}