x86/sgx: Add SGX_IOC_ENCLAVE_CREATE
authorJarkko Sakkinen <jarkko@kernel.org>
Thu, 12 Nov 2020 22:01:23 +0000 (00:01 +0200)
committerBorislav Petkov <bp@suse.de>
Wed, 18 Nov 2020 17:02:49 +0000 (18:02 +0100)
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
Documentation/userspace-api/ioctl/ioctl-number.rst
arch/x86/include/uapi/asm/sgx.h [new file with mode: 0644]
arch/x86/kernel/cpu/sgx/Makefile
arch/x86/kernel/cpu/sgx/driver.c
arch/x86/kernel/cpu/sgx/driver.h
arch/x86/kernel/cpu/sgx/encl.c
arch/x86/kernel/cpu/sgx/encl.h
arch/x86/kernel/cpu/sgx/ioctl.c [new file with mode: 0644]

index 55a2d9b2ce33c425927e9f4ebddcb5957ebc7e1b..a4c75a28c83920732d89fa3e1be66724a163a06b 100644 (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
diff --git a/arch/x86/include/uapi/asm/sgx.h b/arch/x86/include/uapi/asm/sgx.h
new file mode 100644 (file)
index 0000000..f31bb17
--- /dev/null
@@ -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 */
index 3fc45112073560477159fe8b2b313706eb945581..91d3dc784a2925ec39d8a2251822df36d41ec017 100644 (file)
@@ -1,4 +1,5 @@
 obj-y += \
        driver.o \
        encl.o \
+       ioctl.o \
        main.o
index c2810e1c7cf18deffb6be18b6e051871a4d871be..ee947b721d8d6af94aa3f517d5c3d7b526efb6b5 100644 (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,
 };
index cda9c43b7543d2513091895d683b0d797a6f4ff5..a728e8e848bd606d8317e7b3094415fc87f43379 100644 (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__ */
index b9d445db7ff1545a24cf46caa163697e8edf2a29..57eff300f487882a50ff4b3298f37e84503193b2 100644 (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);
index 1df8011fa23d8951bd6edd0842729e838a7dfdae..7cc175825b0122769a43eb82f0d1aa660ab12b46 100644 (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;
diff --git a/arch/x86/kernel/cpu/sgx/ioctl.c b/arch/x86/kernel/cpu/sgx/ioctl.c
new file mode 100644 (file)
index 0000000..1355490
--- /dev/null
@@ -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;
+}