2018-09-25 13:09:39

by Jarkko Sakkinen

[permalink] [raw]
Subject: [PATCH v14 00/19] Intel SGX1 support

Intel(R) SGX is a set of CPU instructions that can be used by applications
to set aside private regions of code and data. The code outside the enclave
is disallowed to access the memory inside the enclave by the CPU access
control. In a way you can think that SGX provides inverted sandbox. It
protects the application from a malicious host.

There is a new hardware unit in the processor called Memory Encryption
Engine (MEE) starting from the Skylake microacrhitecture. BIOS can define
one or many MEE regions that can hold enclave data by configuring them with
PRMRR registers.

The MEE automatically encrypts the data leaving the processor package to
the MEE regions. The data is encrypted using a random key whose life-time
is exactly one power cycle.

The current implementation requires that the firmware sets
IA32_SGXLEPUBKEYHASH* MSRs as writable so that ultimately the kernel can
decide what enclaves it wants run. The implementation does not create
any bottlenecks to support read-only MSRs later on.

You can tell if your CPU supports SGX by looking into /proc/cpuinfo:

cat /proc/cpuinfo | grep sgx

v14:
* Change the comment about X86_FEATURE_SGX_LC from “SGX launch
configuration” to “SGX launch control”.
* Move the SGX-related CPU feature flags as part of the Linux defined
virtual leaf 8.
* Add SGX_ prefix to the constants defining the ENCLS leaf functions.
* Use GENMASK*() and BIT*() in sgx_arch.h instead of raw hex numbers.
* Refine the long description for CONFIG_INTEL_SGX_CORE.
* Do not use pr_*_ratelimited() in the driver. The use of the rate limited
versions is legacy cruft from the prototyping phase.
* Detect sleep with SGX_INVALID_EINIT_TOKEN instead of counting power
cycles.
* Manually prefix with “sgx:” in the core SGX code instead of redefining
pr_fmt.
* Report if IA32_SGXLEPUBKEYHASHx MSRs are not writable in the driver
instead of core because it is a driver requirement.
* Change prompt to bool in the entry for CONFIG_INTEL_SGX_CORE because the
default is ‘n’.
* Rename struct sgx_epc_bank as struct sgx_epc_section in order to match
the SDM.
* Allocate struct sgx_epc_page instances one at a time.
* Use “__iomem void *” pointers for the mapped EPC memory consistently.
* Retry once on SGX_INVALID_TOKEN in sgx_einit() instead of counting power
cycles.
* Call enclave swapping operations directly from the driver instead of
calling them .indirectly through struct sgx_epc_page_ops because indirect
calls are not required yet as the patch set does not contain the KVM
support.
* Added special signal SEGV_SGXERR to notify about SGX EPCM violation
errors.

v13:
* Always use SGX_CPUID constant instead of a hardcoded value.
* Simplified and documented the macros and functions for ENCLS leaves.
* Enable sgx_free_page() to free active enclave pages on demand
in order to allow sgx_invalidate() to delete enclave pages.
It no longer performs EREMOVE if a page is in the process of
being reclaimed.
* Use PM notifier per enclave so that we don't have to traverse
the global list of active EPC pages to find enclaves.
* Removed unused SGX_LE_ROLLBACK constant from uapi/asm/sgx.h
* Always use ioremap() to map EPC banks as we only support 64-bit kernel.
* Invalidate IA32_SGXLEPUBKEYHASH cache used by sgx_einit() when going
to sleep.

v12:
* Split to more narrow scoped commits in order to ease the review process and
use co-developed-by tag for co-authors of commits instead of listing them in
the source files.
* Removed cruft EXPORT_SYMBOL() declarations and converted to static variables.
* Removed in-kernel LE i.e. this version of the SGX software stack only
supports unlocked IA32_SGXLEPUBKEYHASHx MSRs.
* Refined documentation on launching enclaves, swapping and enclave
construction.
* Refined sgx_arch.h to include alignment information for every struct that
requires it and removed structs that are not needed without an LE.
* Got rid of SGX_CPUID.
* SGX detection now prints log messages about firmware configuration issues.

v11:
* Polished ENCLS wrappers with refined exception handling.
* ksgxswapd was not stopped (regression in v5) in
sgx_page_cache_teardown(), which causes a leaked kthread after driver
deinitialization.
* Shutdown sgx_le_proxy when going to suspend because its EPC pages will be
invalidated when resuming, which will cause it not function properly
anymore.
* Set EINITTOKEN.VALID to zero for a token that is passed when
SGXLEPUBKEYHASH matches MRSIGNER as alloc_page() does not give a zero
page.
* Fixed the check in sgx_edbgrd() for a TCS page. Allowed to read offsets
around the flags field, which causes a #GP. Only flags read is readable.
* On read access memcpy() call inside sgx_vma_access() had src and dest
parameters in wrong order.
* The build issue with CONFIG_KASAN is now fixed. Added undefined symbols
to LE even if “KASAN_SANITIZE := false” was set in the makefile.
* Fixed a regression in the #PF handler. If a page has
SGX_ENCL_PAGE_RESERVED flag the #PF handler should unconditionally fail.
It did not, which caused weird races when trying to change other parts of
swapping code.
* EPC management has been refactored to a flat LRU cache and moved to
arch/x86. The swapper thread reads a cluster of EPC pages and swaps all
of them. It can now swap from multiple enclaves in the same round.
* For the sake of consistency with SGX_IOC_ENCLAVE_ADD_PAGE, return -EINVAL
when an enclave is already initialized or dead instead of zero.

v10:
* Cleaned up anon inode based IPC between the ring-0 and ring-3 parts
of the driver.
* Unset the reserved flag from an enclave page if EDBGRD/WR fails
(regression in v6).
* Close the anon inode when LE is stopped (regression in v9).
* Update the documentation with a more detailed description of SGX.

v9:
* Replaced kernel-LE IPC based on pipes with an anonymous inode.
The driver does not require anymore new exports.

v8:
* Check that public key MSRs match the LE public key hash in the
driver initialization when the MSRs are read-only.
* Fix the race in VA slot allocation by checking the fullness
immediately after succeesful allocation.
* Fix the race in hash mrsigner calculation between the launch
enclave and user enclaves by having a separate lock for hash
calculation.

v7:
* Fixed offset calculation in sgx_edbgr/wr(). Address was masked with PAGE_MASK
when it should have been masked with ~PAGE_MASK.
* Fixed a memory leak in sgx_ioc_enclave_create().
* Simplified swapping code by using a pointer array for a cluster
instead of a linked list.
* Squeezed struct sgx_encl_page to 32 bytes.
* Fixed deferencing of an RSA key on OpenSSL 1.1.0.
* Modified TC's CMAC to use kernel AES-NI. Restructured the code
a bit in order to better align with kernel conventions.

v6:
* Fixed semaphore underrun when accessing /dev/sgx from the launch enclave.
* In sgx_encl_create() s/IS_ERR(secs)/IS_ERR(encl)/.
* Removed virtualization chapter from the documentation.
* Changed the default filename for the signing key as signing_key.pem.
* Reworked EPC management in a way that instead of a linked list of
struct sgx_epc_page instances there is an array of integers that
encodes address and bank of an EPC page (the same data as 'pa' field
earlier). The locking has been moved to the EPC bank level instead
of a global lock.
* Relaxed locking requirements for EPC management. EPC pages can be
released back to the EPC bank concurrently.
* Cleaned up ptrace() code.
* Refined commit messages for new architectural constants.
* Sorted includes in every source file.
* Sorted local variable declarations according to the line length in
every function.
* Style fixes based on Darren's comments to sgx_le.c.

v5:
* Described IPC between the Launch Enclave and kernel in the commit messages.
* Fixed all relevant checkpatch.pl issues that I have forgot fix in earlier
versions except those that exist in the imported TinyCrypt code.
* Fixed spelling mistakes in the documentation.
* Forgot to check the return value of sgx_drv_subsys_init().
* Encapsulated properly page cache init and teardown.
* Collect epc pages to a temp list in sgx_add_epc_bank
* Removed SGX_ENCLAVE_INIT_ARCH constant.

v4:
* Tied life-cycle of the sgx_le_proxy process to /dev/sgx.
* Removed __exit annotation from sgx_drv_subsys_exit().
* Fixed a leak of a backing page in sgx_process_add_page_req() in the
case when vm_insert_pfn() fails.
* Removed unused symbol exports for sgx_page_cache.c.
* Updated sgx_alloc_page() to require encl parameter and documented the
behavior (Sean Christopherson).
* Refactored a more lean API for sgx_encl_find() and documented the behavior.
* Moved #PF handler to sgx_fault.c.
* Replaced subsys_system_register() with plain bus_register().
* Retry EINIT 2nd time only if MSRs are not locked.

v3:
* Check that FEATURE_CONTROL_LOCKED and FEATURE_CONTROL_SGX_ENABLE are set.
* Return -ERESTARTSYS in __sgx_encl_add_page() when sgx_alloc_page() fails.
* Use unused bits in epc_page->pa to store the bank number.
* Removed #ifdef for WQ_NONREENTRANT.
* If mmu_notifier_register() fails with -EINTR, return -ERESTARTSYS.
* Added --remove-section=.got.plt to objcopy flags in order to prevent a
dummy .got.plt, which will cause an inconsistent size for the LE.
* Documented sgx_encl_* functions.
* Added remark about AES implementation used inside the LE.
* Removed redundant sgx_sys_exit() from le/main.c.
* Fixed struct sgx_secinfo alignment from 128 to 64 bytes.
* Validate miscselect in sgx_encl_create().
* Fixed SSA frame size calculation to take the misc region into account.
* Implemented consistent exception handling to __encls() and __encls_ret().
* Implemented a proper device model in order to allow sysfs attributes
and in-kernel API.
* Cleaned up various "find enclave" implementations to the unified
sgx_encl_find().
* Validate that vm_pgoff is zero.
* Discard backing pages with shmem_truncate_range() after EADD.
* Added missing EEXTEND operations to LE signing and launch.
* Fixed SSA size for GPRS region from 168 to 184 bytes.
* Fixed the checks for TCS flags. Now DBGOPTIN is allowed.
* Check that TCS addresses are in ELRANGE and not just page aligned.
* Require kernel to be compiled with X64_64 and CPU_SUP_INTEL.
* Fixed an incorrect value for SGX_ATTR_DEBUG from 0x01 to 0x02.

v2:
* get_rand_uint32() changed the value of the pointer instead of value
where it is pointing at.
* Launch enclave incorrectly used sigstruct attributes-field instead of
enclave attributes-field.
* Removed unused struct sgx_add_page_req from sgx_ioctl.c
* Removed unused sgx_has_sgx2.
* Updated arch/x86/include/asm/sgx.h so that it provides stub
implementations when sgx in not enabled.
* Removed cruft rdmsr-calls from sgx_set_pubkeyhash_msrs().
* return -ENOMEM in sgx_alloc_page() when VA pages consume too much space
* removed unused global sgx_nr_pids
* moved sgx_encl_release to sgx_encl.c
* return -ERESTARTSYS instead of -EINTR in sgx_encl_init()
Jarkko Sakkinen (10):
x86/sgx: Update MAINTAINERS
x86/sgx: Architectural structures
x86/sgx: Add wrappers for ENCLS leaf functions
x86/sgx: Add data structures for tracking the EPC pages
x86/sgx: Enclave Page Cache (EPC) memory manager
platform/x86: Intel SGX driver
platform/x86: Add swapping functionality to the Intel SGX driver
x86/sgx: Add a simple swapper for the EPC memory manager
platform/x86: ptrace() support for the SGX driver
x86/sgx: Driver documentation

Kai Huang (1):
x86/cpufeature: Add SGX and SGX_LC CPU features

Sean Christopherson (8):
x86/cpufeatures: Add SGX feature bits
x86/msr: Add SGX definitions to msr-index.h
x86/mm: x86/sgx: Add new 'PF_SGX' page fault error code bit
x86/fault: x86/mm/pkeys: relocate stale comment regarding OSPKE
signal: x86/sgx: Add SIGSEGV siginfo code for SGX EPCM fault
x86/mm: x86/sgx: Signal SEGV_SGXERR for #PFs w/ PF_SGX
x86/sgx: Detect Intel SGX
x86/sgx: Add sgx_einit() for initializing enclaves

Documentation/index.rst | 1 +
Documentation/x86/intel_sgx.rst | 185 ++++
MAINTAINERS | 7 +
arch/x86/Kconfig | 18 +
arch/x86/include/asm/cpufeatures.h | 8 +
arch/x86/include/asm/msr-index.h | 8 +
arch/x86/include/asm/sgx.h | 326 ++++++
arch/x86/include/asm/sgx_arch.h | 403 ++++++++
arch/x86/include/asm/traps.h | 1 +
arch/x86/include/uapi/asm/sgx.h | 60 ++
arch/x86/include/uapi/asm/sgx_errno.h | 91 ++
arch/x86/kernel/cpu/Makefile | 1 +
arch/x86/kernel/cpu/common.c | 1 +
arch/x86/kernel/cpu/intel.c | 40 +
arch/x86/kernel/cpu/intel_sgx.c | 502 +++++++++
arch/x86/mm/fault.c | 30 +-
drivers/platform/x86/Kconfig | 2 +
drivers/platform/x86/Makefile | 1 +
drivers/platform/x86/intel_sgx/Kconfig | 20 +
drivers/platform/x86/intel_sgx/Makefile | 14 +
drivers/platform/x86/intel_sgx/sgx.h | 212 ++++
drivers/platform/x86/intel_sgx/sgx_encl.c | 954 ++++++++++++++++++
.../platform/x86/intel_sgx/sgx_encl_page.c | 177 ++++
drivers/platform/x86/intel_sgx/sgx_fault.c | 108 ++
drivers/platform/x86/intel_sgx/sgx_ioctl.c | 234 +++++
drivers/platform/x86/intel_sgx/sgx_main.c | 267 +++++
drivers/platform/x86/intel_sgx/sgx_util.c | 156 +++
drivers/platform/x86/intel_sgx/sgx_vma.c | 167 +++
include/uapi/asm-generic/siginfo.h | 4 +
29 files changed, 3990 insertions(+), 8 deletions(-)
create mode 100644 Documentation/x86/intel_sgx.rst
create mode 100644 arch/x86/include/asm/sgx.h
create mode 100644 arch/x86/include/asm/sgx_arch.h
create mode 100644 arch/x86/include/uapi/asm/sgx.h
create mode 100644 arch/x86/include/uapi/asm/sgx_errno.h
create mode 100644 arch/x86/kernel/cpu/intel_sgx.c
create mode 100644 drivers/platform/x86/intel_sgx/Kconfig
create mode 100644 drivers/platform/x86/intel_sgx/Makefile
create mode 100644 drivers/platform/x86/intel_sgx/sgx.h
create mode 100644 drivers/platform/x86/intel_sgx/sgx_encl.c
create mode 100644 drivers/platform/x86/intel_sgx/sgx_encl_page.c
create mode 100644 drivers/platform/x86/intel_sgx/sgx_fault.c
create mode 100644 drivers/platform/x86/intel_sgx/sgx_ioctl.c
create mode 100644 drivers/platform/x86/intel_sgx/sgx_main.c
create mode 100644 drivers/platform/x86/intel_sgx/sgx_util.c
create mode 100644 drivers/platform/x86/intel_sgx/sgx_vma.c

--
2.17.1



2018-09-25 13:12:46

by Jarkko Sakkinen

[permalink] [raw]
Subject: [PATCH v14 05/19] x86/msr: Add SGX definitions to msr-index.h

From: Sean Christopherson <[email protected]>

ENCLS and ENCLU are usable if and only if SGX_ENABLE is set and After
SGX is activated the IA32_SGXLEPUBKEYHASHn MSRs are writable if
SGX_LC_WR is set and the feature control is locked.

SGX related bits in IA32_FEATURE_CONTROL cannot be set before SGX is
activated by the pre-boot firmware. SGX activation is triggered by
setting bit 0 in the MSR 0x7a. Until SGX is activated, the LE hash MSRs
are writable to allow pre-boot firmware to lock down the LE root key
with a non-Intel value.

Signed-off-by: Sean Christopherson <[email protected]>
Co-developed-by: Haim Cohen <[email protected]>
Signed-off-by: Haim Cohen <[email protected]>
Signed-off-by: Jarkko Sakkinen <[email protected]>
---
arch/x86/include/asm/msr-index.h | 8 ++++++++
arch/x86/kernel/cpu/common.c | 1 +
2 files changed, 9 insertions(+)

diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index 4731f0cf97c5..44d7b68e7f52 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -486,6 +486,8 @@
#define FEATURE_CONTROL_LOCKED (1<<0)
#define FEATURE_CONTROL_VMXON_ENABLED_INSIDE_SMX (1<<1)
#define FEATURE_CONTROL_VMXON_ENABLED_OUTSIDE_SMX (1<<2)
+#define FEATURE_CONTROL_SGX_ENABLE (1<<18)
+#define FEATURE_CONTROL_SGX_LE_WR (1<<17)
#define FEATURE_CONTROL_LMCE (1<<20)

#define MSR_IA32_APICBASE 0x0000001b
@@ -552,6 +554,12 @@
#define PACKAGE_THERM_INT_LOW_ENABLE (1 << 1)
#define PACKAGE_THERM_INT_PLN_ENABLE (1 << 24)

+/* Intel SGX MSRs */
+#define MSR_IA32_SGXLEPUBKEYHASH0 0x0000008C
+#define MSR_IA32_SGXLEPUBKEYHASH1 0x0000008D
+#define MSR_IA32_SGXLEPUBKEYHASH2 0x0000008E
+#define MSR_IA32_SGXLEPUBKEYHASH3 0x0000008F
+
/* Thermal Thresholds Support */
#define THERM_INT_THRESHOLD0_ENABLE (1 << 15)
#define THERM_SHIFT_THRESHOLD0 8
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index 44c4ef3d989b..aedfd9657a6c 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -52,6 +52,7 @@
#include <asm/microcode_intel.h>
#include <asm/intel-family.h>
#include <asm/cpu_device_id.h>
+#include <asm/sgx_arch.h>

#ifdef CONFIG_X86_LOCAL_APIC
#include <asm/uv/uv.h>
--
2.17.1


2018-09-25 13:12:51

by Jarkko Sakkinen

[permalink] [raw]
Subject: [PATCH v14 02/19] x86/sgx: Architectural structures

Add arch/x86/include/asm/sgx_arch.h, which contains definitions for the
architectural data structures used by the CPU to implement the SGX.

Signed-off-by: Jarkko Sakkinen <[email protected]>
---
arch/x86/include/asm/sgx_arch.h | 403 ++++++++++++++++++++++++++
arch/x86/include/uapi/asm/sgx_errno.h | 91 ++++++
2 files changed, 494 insertions(+)
create mode 100644 arch/x86/include/asm/sgx_arch.h
create mode 100644 arch/x86/include/uapi/asm/sgx_errno.h

diff --git a/arch/x86/include/asm/sgx_arch.h b/arch/x86/include/asm/sgx_arch.h
new file mode 100644
index 000000000000..f2a58ac0843e
--- /dev/null
+++ b/arch/x86/include/asm/sgx_arch.h
@@ -0,0 +1,403 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/**
+ * Copyright(c) 2016-18 Intel Corporation.
+ *
+ * Contains the architectural data structures used by the CPU to implement SGX.
+ * The data structures defined by the Linux software stack should not be placed
+ * here.
+ */
+#ifndef _ASM_X86_SGX_ARCH_H
+#define _ASM_X86_SGX_ARCH_H
+
+#include <linux/types.h>
+#include <uapi/asm/sgx_errno.h>
+
+#define SGX_CPUID 0x12
+#define SGX_CPUID_SECTION 2
+#define SGX_MAX_EPC_SECTIONS 8
+
+/**
+ * enum sgx_section_types - SGX cpuid section subleaf types
+ * %SGX_CPUID_SECTION_INVALID: subleaf for the section is not valid
+ * %SGX_CPUID_SECTION_VALID: subleaf for the section is valid
+ * %SGX_CPUID_SECTION_MASK: bits that define the subleaf type
+ *
+ * The section subleaves are traversed in order up until an invalid subleaf is
+ * encountered. The information about the section type is contained in the
+ * first four bits of eax.
+ */
+enum sgx_section_types {
+ SGX_CPUID_SECTION_INVALID = 0x0,
+ SGX_CPUID_SECTION_VALID = 0x1,
+ SGX_CPUID_SECTION_MASK = 0xF,
+};
+
+/**
+ * enum sgx_encls_leaves - ENCLS leaf functions
+ * %SGX_ECREATE: Create an enclave.
+ * %SGX_EADD: Add a page to an enclave.
+ * %SGX_EINIT: Launch an enclave.
+ * %SGX_EREMOVE: Remove a page from an enclave.
+ * %SGX_EDBGRD: Read a word from an enclve (peek).
+ * %SGX_EDBGWR: Write a word to an enclave (poke).
+ * %SGX_EEXTEND: Measure 256 bytes of an added enclave page.
+ * %SGX_ELDB: Load a swapped page in blocked state.
+ * %SGX_ELDU: Load a swapped page in unblocked state.
+ * %SGX_EBLOCK: Change page state to blocked i.e. entering hardware
+ * threads cannot access it and create new TLB entries.
+ * %SGX_EPA: Create a Version Array (VA) page used to store isvsvn
+ * number for a swapped EPC page.
+ * %SGX_EWB: Swap an enclave page to the regular memory. Checks that
+ * all threads have exited that were in the previous
+ * shoot-down sequence.
+ * %SGX_ETRACK: Start a new shoot down sequence. Used to together with
+ * EBLOCK to make sure that a page is safe to swap.
+ */
+enum sgx_encls_leaves {
+ SGX_ECREATE = 0x00,
+ SGX_EADD = 0x01,
+ SGX_EINIT = 0x02,
+ SGX_EREMOVE = 0x03,
+ SGX_EDGBRD = 0x04,
+ SGX_EDGBWR = 0x05,
+ SGX_EEXTEND = 0x06,
+ SGX_ELDB = 0x07,
+ SGX_ELDU = 0x08,
+ SGX_EBLOCK = 0x09,
+ SGX_EPA = 0x0A,
+ SGX_EWB = 0x0B,
+ SGX_ETRACK = 0x0C,
+ SGX_EAUG = 0x0D,
+ SGX_EMODPR = 0x0E,
+ SGX_EMODT = 0x0F,
+};
+
+#define SGX_MODULUS_SIZE 384
+
+/**
+ * enum sgx_miscselect - additional information to an SSA frame
+ * %SGX_MISC_EXINFO: Report #PF or #GP to the SSA frame.
+ *
+ * Save State Area (SSA) is a stack inside the enclave used to store processor
+ * state when an exception or interrupt occurs. This enum defines additional
+ * information stored to an SSA frame.
+ */
+enum sgx_miscselect {
+ SGX_MISC_EXINFO = BIT(0),
+ SGX_MISC_RESERVED_MASK = GENMASK_ULL(63, 1)
+};
+
+#define SGX_SSA_GPRS_SIZE 182
+#define SGX_SSA_MISC_EXINFO_SIZE 16
+
+/**
+ * enum sgx_attributes - the attributes field in &struct sgx_secs
+ * %SGX_ATTR_INIT: Enclave can be entered (is initialized).
+ * %SGX_ATTR_DEBUG: Allow ENCLS(EDBGRD) and ENCLS(EDBGWR).
+ * %SGX_ATTR_MODE64BIT: Tell that this a 64-bit enclave.
+ * %SGX_ATTR_PROVISIONKEY: Allow to use provisioning keys for remote
+ * attestation.
+ * %SGX_ATTR_EINITTOKENKEY: Allow to use token signing key that is used to
+ * sign cryptographic tokens that can be passed to
+ * EINIT as an authorization to run an enclave.
+ */
+enum sgx_attribute {
+ SGX_ATTR_INIT = BIT(0),
+ SGX_ATTR_DEBUG = BIT(1),
+ SGX_ATTR_MODE64BIT = BIT(2),
+ SGX_ATTR_PROVISIONKEY = BIT(4),
+ SGX_ATTR_EINITTOKENKEY = BIT(5),
+ SGX_ATTR_RESERVED_MASK = BIT_ULL(3) | GENMASK_ULL(63, 6)
+};
+
+#define SGX_SECS_RESERVED1_SIZE 24
+#define SGX_SECS_RESERVED2_SIZE 32
+#define SGX_SECS_RESERVED3_SIZE 96
+#define SGX_SECS_RESERVED4_SIZE 3836
+
+/**
+ * struct sgx_secs - SGX Enclave Control Structure (SECS)
+ * @size: size of the address space
+ * @base: base address of the address space
+ * @ssa_frame_size: size of an SSA frame
+ * @miscselect: additional information stored to an SSA frame
+ * @attributes: attributes for enclave
+ * @xfrm: XSave-Feature Request Mask (subset of XCR0)
+ * @mrenclave: SHA256-hash of the enclave contents
+ * @mrsigner: SHA256-hash of the public key used to sign the SIGSTRUCT
+ * @isvprodid: a user-defined value that is used in key derivation
+ * @isvsvn: a user-defined value that is used in key derivation
+ *
+ * SGX Enclave Control Structure (SECS) is a special enclave page that is not
+ * visible in the address space. In fact, this structure defines the address
+ * range and other global attributes for the enclave and it is the first EPC
+ * page created for any enclave. It is moved from a temporary buffer to an EPC
+ * by the means of ENCLS(ECREATE) leaf.
+ */
+struct sgx_secs {
+ u64 size;
+ u64 base;
+ u32 ssa_frame_size;
+ u32 miscselect;
+ u8 reserved1[SGX_SECS_RESERVED1_SIZE];
+ u64 attributes;
+ u64 xfrm;
+ u32 mrenclave[8];
+ u8 reserved2[SGX_SECS_RESERVED2_SIZE];
+ u32 mrsigner[8];
+ u8 reserved3[SGX_SECS_RESERVED3_SIZE];
+ u16 isvprodid;
+ u16 isvsvn;
+ u8 reserved4[SGX_SECS_RESERVED4_SIZE];
+} __packed;
+
+/**
+ * enum sgx_tcs_flags - execution flags for TCS
+ * %SGX_TCS_DBGOPTIN: If enabled allows single-stepping and breakpoints
+ * inside an enclave. It is cleared by EADD but can
+ * be set later with EDBGWR.
+ */
+enum sgx_tcs_flags {
+ SGX_TCS_DBGOPTIN = 0x01,
+ SGX_TCS_RESERVED_MASK = GENMASK_ULL(63, 1)
+};
+
+#define SGX_TCS_RESERVED_SIZE 503
+
+/**
+ * struct sgx_tcs - Thread Control Structure (TCS)
+ * @state: used to mark an entered TCS
+ * @flags: execution flags (cleared by EADD)
+ * @ssa_offset: SSA stack offset relative to the enclave base
+ * @ssa_index: the current SSA frame index (cleard by EADD)
+ * @nr_ssa_frames: the number of frame in the SSA stack
+ * @entry_offset: entry point offset relative to the enclave base
+ * @exit_addr: address outside the enclave to exit on an exception or
+ * interrupt
+ * @fs_offset: offset relative to the enclave base to become FS
+ * segment inside the enclave
+ * @gs_offset: offset relative to the enclave base to become GS
+ * segment inside the enclave
+ * @fs_limit: size to become a new FS-limit (only 32-bit enclaves)
+ * @gs_limit: size to become a new GS-limit (only 32-bit enclaves)
+ *
+ * Thread Control Structure (TCS) is an enclave page visible in its address
+ * space that defines an entry point inside the enclave. A thread enters inside
+ * an enclave by supplying address of TCS to ENCLU(EENTER). A TCS can be entered
+ * by only one thread at a time.
+ */
+struct sgx_tcs {
+ u64 state;
+ u64 flags;
+ u64 ssa_offset;
+ u32 ssa_index;
+ u32 nr_ssa_frames;
+ u64 entry_offset;
+ u64 exit_addr;
+ u64 fs_offset;
+ u64 gs_offset;
+ u32 fs_limit;
+ u32 gs_limit;
+ u64 reserved[SGX_TCS_RESERVED_SIZE];
+} __packed;
+
+/**
+ * struct sgx_pageinfo - an enclave page descriptor
+ * @addr: address of the enclave page
+ * @contents: pointer to the page contents
+ * @metadata: pointer either to a SECINFO or PCMD instance
+ * @secs: address of the SECS page
+ */
+struct sgx_pageinfo {
+ u64 addr;
+ u64 contents;
+ u64 metadata;
+ u64 secs;
+} __packed __aligned(32);
+
+
+/**
+ * enum sgx_page_type - bits in the SECINFO flags defining the page type
+ * %SGX_PAGE_TYPE_SECS: a SECS page
+ * %SGX_PAGE_TYPE_TCS: a TCS page
+ * %SGX_PAGE_TYPE_REG: a regular page
+ * %SGX_PAGE_TYPE_VA: a VA page
+ * %SGX_PAGE_TYPE_TRIM: a page in trimmed state
+ */
+enum sgx_page_type {
+ SGX_PAGE_TYPE_SECS = 0,
+ SGX_PAGE_TYPE_TCS = 1,
+ SGX_PAGE_TYPE_REG = 2,
+ SGX_PAGE_TYPE_VA = 3,
+ SGX_PAGE_TYPE_TRIM = 4,
+ SGX_PAGE_TYPE_MASK = GENMASK(7, 0)
+};
+
+/**
+ * enum sgx_secinfo_flags - the flags field in &struct sgx_secinfo
+ * %SGX_SECINFO_R: allow read
+ * %SGX_SECINFO_W: allow write
+ * %SGX_SECINFO_X: allow execution
+ * %SGX_SECINFO_SECS: a SECS page
+ * %SGX_SECINFO_TCS: a TCS page
+ * %SGX_SECINFO_REG: a regular page
+ * %SGX_SECINFO_VA: a VA page
+ * %SGX_SECINFO_TRIM: a page in trimmed state
+ */
+enum sgx_secinfo_flags {
+ SGX_SECINFO_R = BIT(0),
+ SGX_SECINFO_W = BIT(1),
+ SGX_SECINFO_X = BIT(2),
+ SGX_SECINFO_PERMISSION_MASK = GENMASK_ULL(2, 0),
+ SGX_SECINFO_SECS = (SGX_PAGE_TYPE_SECS << 8),
+ SGX_SECINFO_TCS = (SGX_PAGE_TYPE_TCS << 8),
+ SGX_SECINFO_REG = (SGX_PAGE_TYPE_REG << 8),
+ SGX_SECINFO_VA = (SGX_PAGE_TYPE_VA << 8),
+ SGX_SECINFO_TRIM = (SGX_PAGE_TYPE_TRIM << 8),
+ SGX_SECINFO_PAGE_TYPE_MASK = (SGX_PAGE_TYPE_MASK << 8),
+ SGX_SECINFO_RESERVED_MASK = ~(SGX_SECINFO_PERMISSION_MASK |
+ SGX_SECINFO_PAGE_TYPE_MASK)
+};
+
+#define SGX_SECINFO_RESERVED_SIZE 56
+
+/**
+ * struct sgx_secinfo - describes attributes of an EPC page
+ * @flags: permissions and type
+ *
+ * Used together with ENCLS leaves that add or modify an EPC page to an
+ * enclave to define page permissions and type.
+ */
+struct sgx_secinfo {
+ u64 flags;
+ u8 reserved[SGX_SECINFO_RESERVED_SIZE];
+} __packed __aligned(64);
+
+#define SGX_PCMD_RESERVED_SIZE 40
+
+/**
+ * struct sgx_pcmd - Paging Crypto Metadata (PCMD)
+ * @enclave_id: enclave identifier
+ * @mac: MAC over PCMD, page contents and isvsvn
+ *
+ * PCMD is stored for every swapped page to the regular memory. When ELDU loads
+ * the page back it recalculates the MAC by using a isvsvn number stored in a
+ * VA page. Together these two structures bring integrity and rollback
+ * protection.
+ */
+struct sgx_pcmd {
+ struct sgx_secinfo secinfo;
+ u64 enclave_id;
+ u8 reserved[SGX_PCMD_RESERVED_SIZE];
+ u8 mac[16];
+} __packed __aligned(128);
+
+#define SGX_SIGSTRUCT_RESERVED1_SIZE 84
+#define SGX_SIGSTRUCT_RESERVED2_SIZE 20
+#define SGX_SIGSTRUCT_RESERVED3_SIZE 32
+#define SGX_SIGSTRUCT_RESERVED4_SIZE 12
+
+/**
+ * struct sgx_sigstruct - an enclave signature
+ * @header1: a constant byte string
+ * @vendor: must be either 0x0000 or 0x8086
+ * @date: YYYYMMDD in BCD
+ * @header2: a costant byte string
+ * @application: an application defined value
+ * @modulus: the modulus of the public key
+ * @exponent: the exponent of the public key
+ * @signature: the signature calculated over the fields except modulus,
+ * exponent, signature, reserved4, q1 and q2
+ * @miscselect: additional information stored to an SSA frame
+ * @misc_mask: required miscselect in SECS
+ * @attributes: attributes for enclave
+ * @xfrm: XSave-Feature Request Mask (subset of XCR0)
+ * @attributes_mask: required attributes in SECS
+ * @xfrm_mask: required XFRM in SECS
+ * @mrenclave: SHA256-hash of the enclave contents
+ * @isvprodid: a user-defined value that is used in key derivation
+ * @isvsvn: a user-defined value that is used in key derivation
+ * @q1: a value used in RSA signature verification
+ * @q2: a value used in RSA signature verification
+ */
+struct sgx_sigstruct {
+ u64 header1[2];
+ u32 vendor;
+ u32 date;
+ u64 header2[2];
+ u32 application;
+ u8 reserved1[SGX_SIGSTRUCT_RESERVED1_SIZE];
+ u8 modulus[SGX_MODULUS_SIZE];
+ u32 exponent;
+ u8 signature[SGX_MODULUS_SIZE];
+ u32 miscselect;
+ u32 misc_mask;
+ u8 reserved2[SGX_SIGSTRUCT_RESERVED2_SIZE];
+ u64 attributes;
+ u64 xfrm;
+ u64 attributes_mask;
+ u64 xfrm_mask;
+ u8 mrenclave[32];
+ u8 reserved3[SGX_SIGSTRUCT_RESERVED3_SIZE];
+ u16 isvprodid;
+ u16 isvsvn;
+ u8 reserved4[SGX_SIGSTRUCT_RESERVED4_SIZE];
+ u8 q1[SGX_MODULUS_SIZE];
+ u8 q2[SGX_MODULUS_SIZE];
+} __packed __aligned(4096);
+
+#define SGX_EINITTOKEN_RESERVED1_SIZE 11
+#define SGX_EINITTOKEN_RESERVED2_SIZE 32
+#define SGX_EINITTOKEN_RESERVED3_SIZE 32
+#define SGX_EINITTOKEN_RESERVED4_SIZE 24
+
+/**
+ * struct sgx_einittoken - a token permitting to launch an enclave
+ * @valid: one if valid and zero if invalid
+ * @attributes: attributes for enclave
+ * @xfrm: XSave-Feature Request Mask (subset of XCR0)
+ * @mrenclave: SHA256-hash of the enclave contents
+ * @mrsigner: SHA256-hash of the public key used to sign the
+ * SIGSTRUCT
+ * @le_cpusvn: a value that reflects the SGX implementation
+ * running in in the CPU
+ * @le_isvprodid: a user-defined value that is used in key
+ * derivation
+ * @le_isvsvn: a user-defined value that is used in key
+ * derivation
+ * @le_keyed_miscselect: LE's miscselect masked with the token keys
+ * miscselect
+ * @le_keyed_attributes: LE's attributes masked with the token keys
+ * attributes
+ * @le_keyed_xfrm: LE's XFRM masked with the token keys xfrm
+ * @salt: random salt for wear-out protection
+ * @mac: CMAC over the preceding fields
+ *
+ * An enclave with EINITTOKENKEY attribute can access a key with the same name
+ * by using ENCLS(EGETKEY) and use this to sign cryptographic tokens that can
+ * be passed to ENCLS(EINIT) to permit the launch of other enclaves. This is
+ * the only viable way to launch enclaves if IA32_SGXLEPUBKEYHASHn MSRs are
+ * locked assuming that there is a Launch Enclave (LE) available that can be
+ * used for generating these tokens.
+ */
+struct sgx_einittoken {
+ u32 valid;
+ u32 reserved1[SGX_EINITTOKEN_RESERVED1_SIZE];
+ u64 attributes;
+ u64 xfrm;
+ u8 mrenclave[32];
+ u8 reserved2[SGX_EINITTOKEN_RESERVED2_SIZE];
+ u8 mrsigner[32];
+ u8 reserved3[SGX_EINITTOKEN_RESERVED3_SIZE];
+ u8 le_cpusvn[16];
+ u16 le_isvprodid;
+ u16 le_isvsvn;
+ u8 reserved4[SGX_EINITTOKEN_RESERVED4_SIZE];
+ u32 le_keyed_miscselect;
+ u64 le_keyed_attributes;
+ u64 le_keyed_xfrm;
+ u8 salt[32];
+ u8 mac[16];
+} __packed __aligned(512);
+
+#endif /* _ASM_X86_SGX_ARCH_H */
diff --git a/arch/x86/include/uapi/asm/sgx_errno.h b/arch/x86/include/uapi/asm/sgx_errno.h
new file mode 100644
index 000000000000..48b87aed58d7
--- /dev/null
+++ b/arch/x86/include/uapi/asm/sgx_errno.h
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright(c) 2018 Intel Corporation.
+ *
+ * Contains the architecturally defined error codes that are returned by SGX
+ * instructions, e.g. ENCLS, and may be propagated to userspace via errno.
+ */
+
+#ifndef _UAPI_ASM_X86_SGX_ERRNO_H
+#define _UAPI_ASM_X86_SGX_ERRNO_H
+
+/**
+ * enum sgx_encls_leaves - return codes for ENCLS, ENCLU and ENCLV
+ * %SGX_SUCCESS: No error.
+ * %SGX_INVALID_SIG_STRUCT: SIGSTRUCT contains an invalid value.
+ * %SGX_INVALID_ATTRIBUTE: Enclave is not attempting to access a resource
+ * for which it is not authorized.
+ * %SGX_BLKSTATE: EPC page is already blocked.
+ * %SGX_INVALID_MEASUREMENT: SIGSTRUCT or EINITTOKEN contains an incorrect
+ * measurement.
+ * %SGX_NOTBLOCKABLE: EPC page type is not one which can be blocked.
+ * %SGX_PG_INVLD: EPC page is invalid (and cannot be blocked).
+ * %SGX_EPC_PAGE_CONFLICT: EPC page in use by another SGX instruction.
+ * %SGX_INVALID_SIGNATURE: Enclave's signature does not validate with
+ * public key enclosed in SIGSTRUCT.
+ * %SGX_MAC_COMPARE_FAIL: MAC check failed when reloading EPC page.
+ * %SGX_PAGE_NOT_BLOCKED: EPC page is not marked as blocked.
+ * %SGX_NOT_TRACKED: ETRACK has not been completed on the EPC page.
+ * %SGX_VA_SLOT_OCCUPIED: Version array slot contains a valid entry.
+ * %SGX_CHILD_PRESENT: Enclave has child pages present in the EPC.
+ * %SGX_ENCLAVE_ACT: Logical processors are currently executing
+ * inside the enclave.
+ * %SGX_ENTRYEPOCH_LOCKED: SECS locked for EPOCH update, i.e. an ETRACK is
+ * currently executing on the SECS.
+ * %SGX_INVALID_EINITTOKEN: EINITTOKEN is invalid and enclave signer's
+ * public key does not match IA32_SGXLEPUBKEYHASH.
+ * %SGX_PREV_TRK_INCMPL: All processors did not complete the previous
+ * tracking sequence.
+ * %SGX_PG_IS_SECS: Target EPC page is an SECS and cannot be
+ * blocked.
+ * %SGX_PAGE_ATTRIBUTES_MISMATCH: Attributes of the EPC page do not match
+ * the expected values.
+ * %SGX_PAGE_NOT_MODIFIABLE: EPC page cannot be modified because it is in
+ * the PENDING or MODIFIED state.
+ * %SGX_PAGE_NOT_DEBUGGABLE: EPC page cannot be modified because it is in
+ * the PENDING or MODIFIED state.
+ * %SGX_INVALID_COUNTER: {In,De}crementing a counter would cause it to
+ * {over,under}flow.
+ * %SGX_PG_NONEPC: Target page is not an EPC page.
+ * %SGX_TRACK_NOT_REQUIRED: Target page type does not require tracking.
+ * %SGX_INVALID_CPUSVN: Security version number reported by CPU is less
+ * than what is required by the enclave.
+ * %SGX_INVALID_ISVSVN: Security version number of enclave is less than
+ * what is required by the KEYREQUEST struct.
+ * %SGX_UNMASKED_EVENT: An unmasked event, e.g. INTR, was received
+ * while the instruction was executing.
+ * %SGX_INVALID_KEYNAME: Requested key is not supported by hardware.
+ */
+enum sgx_return_codes {
+ SGX_SUCCESS = 0,
+ SGX_INVALID_SIG_STRUCT = 1,
+ SGX_INVALID_ATTRIBUTE = 2,
+ SGX_BLKSTATE = 3,
+ SGX_INVALID_MEASUREMENT = 4,
+ SGX_NOTBLOCKABLE = 5,
+ SGX_PG_INVLD = 6,
+ SGX_EPC_PAGE_CONFLICT = 7,
+ SGX_INVALID_SIGNATURE = 8,
+ SGX_MAC_COMPARE_FAIL = 9,
+ SGX_PAGE_NOT_BLOCKED = 10,
+ SGX_NOT_TRACKED = 11,
+ SGX_VA_SLOT_OCCUPIED = 12,
+ SGX_CHILD_PRESENT = 13,
+ SGX_ENCLAVE_ACT = 14,
+ SGX_ENTRYEPOCH_LOCKED = 15,
+ SGX_INVALID_EINITTOKEN = 16,
+ SGX_PREV_TRK_INCMPL = 17,
+ SGX_PG_IS_SECS = 18,
+ SGX_PAGE_ATTRIBUTES_MISMATCH = 19,
+ SGX_PAGE_NOT_MODIFIABLE = 20,
+ SGX_PAGE_NOT_DEBUGGABLE = 21,
+ SGX_INVALID_COUNTER = 25,
+ SGX_PG_NONEPC = 26,
+ SGX_TRACK_NOT_REQUIRED = 27,
+ SGX_INVALID_CPUSVN = 32,
+ SGX_INVALID_ISVSVN = 64,
+ SGX_UNMASKED_EVENT = 128,
+ SGX_INVALID_KEYNAME = 256,
+};
+
+#endif /* _UAPI_ASM_X86_SGX_ERRNO_H */
--
2.17.1


2018-09-25 13:12:59

by Jarkko Sakkinen

[permalink] [raw]
Subject: [PATCH v14 04/19] x86/cpufeatures: Add SGX feature bits

From: Sean Christopherson <[email protected]>

Add SGX feature bits as part of the Linux defined leaf 8, which
currently contains virtualization flags. There are currently four
documented SGX feature bits, with more expected in the not-too-distant
future.

Signed-off-by: Sean Christopherson <[email protected]>
Co-developed-by: Jarkko Sakkinen <[email protected]>
Signed-off-by: Jarkko Sakkinen <[email protected]>
---
arch/x86/include/asm/cpufeatures.h | 8 +++++-
arch/x86/kernel/cpu/intel.c | 40 ++++++++++++++++++++++++++++++
2 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
index 7bb647f57d42..d6f4abe6d0b0 100644
--- a/arch/x86/include/asm/cpufeatures.h
+++ b/arch/x86/include/asm/cpufeatures.h
@@ -233,6 +233,12 @@
#define X86_FEATURE_XENPV ( 8*32+16) /* "" Xen paravirtual guest */
#define X86_FEATURE_EPT_AD ( 8*32+17) /* Intel Extended Page Table access-dirty bit */

+/* SGX flags: Linux defined, word 8 */
+#define X86_FEATURE_SGX1 ( 8*32+24) /* SGX1 leaf functions */
+#define X86_FEATURE_SGX2 ( 8*32+25) /* SGX2 leaf functions */
+#define X86_FEATURE_SGX_ENCLV ( 8*32+26) /* SGX ENCLV instruction, leafs E[INC|DEC]VIRTCHILD, ESETCONTEXT */
+#define X86_FEATURE_SGX_ENCLS_C ( 8*32+27) /* SGX ENCLS leafs ERDINFO, ETRACK, ELDBC and ELDUC */
+
/* Intel-defined CPU features, CPUID level 0x00000007:0 (EBX), word 9 */
#define X86_FEATURE_FSGSBASE ( 9*32+ 0) /* RDFSBASE, WRFSBASE, RDGSBASE, WRGSBASE instructions*/
#define X86_FEATURE_TSC_ADJUST ( 9*32+ 1) /* TSC adjustment MSR 0x3B */
@@ -332,7 +338,7 @@
#define X86_FEATURE_LA57 (16*32+16) /* 5-level page tables */
#define X86_FEATURE_RDPID (16*32+22) /* RDPID instruction */
#define X86_FEATURE_CLDEMOTE (16*32+25) /* CLDEMOTE instruction */
-#define X86_FEATURE_SGX_LC (16*32+30) /* supports SGX launch configuration */
+#define X86_FEATURE_SGX_LC (16*32+30) /* supports SGX launch control */

/* AMD-defined CPU features, CPUID level 0x80000007 (EBX), word 17 */
#define X86_FEATURE_OVERFLOW_RECOV (17*32+ 0) /* MCA overflow recovery support */
diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c
index fc3c07fe7df5..fcf188d5f9df 100644
--- a/arch/x86/kernel/cpu/intel.c
+++ b/arch/x86/kernel/cpu/intel.c
@@ -19,6 +19,7 @@
#include <asm/microcode_intel.h>
#include <asm/hwcap2.h>
#include <asm/elf.h>
+#include <asm/sgx_arch.h>

#ifdef CONFIG_X86_64
#include <linux/topology.h>
@@ -512,6 +513,42 @@ static void detect_vmx_virtcap(struct cpuinfo_x86 *c)
}
}

+static void detect_sgx(struct cpuinfo_x86 *c)
+{
+#define _X86_FEATURE_SGX1 BIT(0)
+#define _X86_FEATURE_SGX2 BIT(1)
+#define _X86_FEATURE_SGX_ENCLV BIT(5)
+#define _X86_FEATURE_SGX_ENCLS_C BIT(6)
+
+ unsigned int eax;
+
+ clear_cpu_cap(c, X86_FEATURE_SGX1);
+ clear_cpu_cap(c, X86_FEATURE_SGX2);
+ clear_cpu_cap(c, X86_FEATURE_SGX_ENCLV);
+ clear_cpu_cap(c, X86_FEATURE_SGX_ENCLS_C);
+
+ if (c->cpuid_level < SGX_CPUID) {
+ pr_err_once("x86/sgx: cannot enumerate CPUID leaf (0x%x)\n",
+ SGX_CPUID);
+ clear_cpu_cap(c, X86_FEATURE_SGX);
+ return;
+ }
+
+ eax = cpuid_eax(SGX_CPUID);
+
+ if (eax & _X86_FEATURE_SGX1)
+ set_cpu_cap(c, X86_FEATURE_SGX1);
+
+ if (eax & _X86_FEATURE_SGX2)
+ set_cpu_cap(c, X86_FEATURE_SGX2);
+
+ if (eax & _X86_FEATURE_SGX_ENCLV)
+ set_cpu_cap(c, X86_FEATURE_SGX_ENCLV);
+
+ if (eax & _X86_FEATURE_SGX_ENCLS_C)
+ set_cpu_cap(c, X86_FEATURE_SGX_ENCLS_C);
+}
+
#define MSR_IA32_TME_ACTIVATE 0x982

/* Helpers to access TME_ACTIVATE MSR */
@@ -760,6 +797,9 @@ static void init_intel(struct cpuinfo_x86 *c)
if (cpu_has(c, X86_FEATURE_VMX))
detect_vmx_virtcap(c);

+ if (cpu_has(c, X86_FEATURE_SGX))
+ detect_sgx(c);
+
if (cpu_has(c, X86_FEATURE_TME))
detect_tme(c);

--
2.17.1


2018-09-25 13:13:08

by Jarkko Sakkinen

[permalink] [raw]
Subject: [PATCH v14 03/19] x86/cpufeature: Add SGX and SGX_LC CPU features

From: Kai Huang <[email protected]>

Add X86_FEATURE_SGX and X86_FEATURE_SGX_LC that define the bits
determining whether the CPU supports SGX and user launch configuration
i.e. using a custom root key rather the Intel proprietary key for
enclave signing.

Signed-off-by: Kai Huang <[email protected]>
Signed-off-by: Jarkko Sakkinen <[email protected]>
---
arch/x86/include/asm/cpufeatures.h | 2 ++
1 file changed, 2 insertions(+)

diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
index 89a048c2faec..7bb647f57d42 100644
--- a/arch/x86/include/asm/cpufeatures.h
+++ b/arch/x86/include/asm/cpufeatures.h
@@ -236,6 +236,7 @@
/* Intel-defined CPU features, CPUID level 0x00000007:0 (EBX), word 9 */
#define X86_FEATURE_FSGSBASE ( 9*32+ 0) /* RDFSBASE, WRFSBASE, RDGSBASE, WRGSBASE instructions*/
#define X86_FEATURE_TSC_ADJUST ( 9*32+ 1) /* TSC adjustment MSR 0x3B */
+#define X86_FEATURE_SGX ( 9*32+ 2) /* Software Guard Extensions */
#define X86_FEATURE_BMI1 ( 9*32+ 3) /* 1st group bit manipulation extensions */
#define X86_FEATURE_HLE ( 9*32+ 4) /* Hardware Lock Elision */
#define X86_FEATURE_AVX2 ( 9*32+ 5) /* AVX2 instructions */
@@ -331,6 +332,7 @@
#define X86_FEATURE_LA57 (16*32+16) /* 5-level page tables */
#define X86_FEATURE_RDPID (16*32+22) /* RDPID instruction */
#define X86_FEATURE_CLDEMOTE (16*32+25) /* CLDEMOTE instruction */
+#define X86_FEATURE_SGX_LC (16*32+30) /* supports SGX launch configuration */

/* AMD-defined CPU features, CPUID level 0x80000007 (EBX), word 17 */
#define X86_FEATURE_OVERFLOW_RECOV (17*32+ 0) /* MCA overflow recovery support */
--
2.17.1


2018-09-25 13:13:42

by Jarkko Sakkinen

[permalink] [raw]
Subject: [PATCH v14 10/19] x86/sgx: Detect Intel SGX

From: Sean Christopherson <[email protected]>

Intel(R) SGX is a set of CPU instructions that can be used by applications
to set aside private regions of code and data. The code outside the enclave
is disallowed to access the memory inside the enclave by the CPU access
control.

Add a check for SGX to arch/x86 and a new config option, INTEL_SGX_CORE.
Expose a boolean variable 'sgx_enabled' to query whether or not the SGX
support is available.

Signed-off-by: Sean Christopherson <[email protected]>
Co-developed-by: Jarkko Sakkinen <[email protected]>
Co-developed-by: Suresh Siddha <[email protected]>
Signed-off-by: Suresh Siddha <[email protected]>
Signed-off-by: Jarkko Sakkinen <[email protected]>
---
arch/x86/Kconfig | 17 +++++++++++++
arch/x86/include/asm/sgx.h | 13 ++++++++++
arch/x86/kernel/cpu/Makefile | 1 +
arch/x86/kernel/cpu/intel_sgx.c | 44 +++++++++++++++++++++++++++++++++
4 files changed, 75 insertions(+)
create mode 100644 arch/x86/include/asm/sgx.h
create mode 100644 arch/x86/kernel/cpu/intel_sgx.c

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 1a0be022f91d..b47e1a144409 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1913,6 +1913,23 @@ config X86_INTEL_MEMORY_PROTECTION_KEYS

If unsure, say y.

+config INTEL_SGX_CORE
+ bool "Intel SGX core functionality"
+ depends on X86_64 && CPU_SUP_INTEL
+ help
+ Intel Software Guard eXtensions (SGX) CPU feature that allows ring 3
+ applications to create enclaves: private regions of memory that are
+ architecturally protected from unauthorized access and/or modification.
+
+ This option enables kernel recognition of SGX, high-level management
+ of the Enclave Page Cache (EPC), tracking and writing of SGX Launch
+ Enclave Hash MSRs, and allows for virtualization of SGX via KVM. By
+ itself, this option does not provide SGX support to userspace.
+
+ For details, see Documentation/x86/intel_sgx.rst
+
+ If unsure, say N.
+
config EFI
bool "EFI runtime service support"
depends on ACPI
diff --git a/arch/x86/include/asm/sgx.h b/arch/x86/include/asm/sgx.h
new file mode 100644
index 000000000000..f4f82f0453a9
--- /dev/null
+++ b/arch/x86/include/asm/sgx.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/**
+ * Copyright(c) 2016-18 Intel Corporation.
+ */
+#ifndef _ASM_X86_SGX_H
+#define _ASM_X86_SGX_H
+
+#include <linux/types.h>
+
+extern bool sgx_enabled;
+extern bool sgx_lc_enabled;
+
+#endif /* _ASM_X86_SGX_H */
diff --git a/arch/x86/kernel/cpu/Makefile b/arch/x86/kernel/cpu/Makefile
index 347137e80bf5..71876f2b35fc 100644
--- a/arch/x86/kernel/cpu/Makefile
+++ b/arch/x86/kernel/cpu/Makefile
@@ -38,6 +38,7 @@ obj-$(CONFIG_CPU_SUP_UMC_32) += umc.o
obj-$(CONFIG_INTEL_RDT) += intel_rdt.o intel_rdt_rdtgroup.o intel_rdt_monitor.o
obj-$(CONFIG_INTEL_RDT) += intel_rdt_ctrlmondata.o intel_rdt_pseudo_lock.o
CFLAGS_intel_rdt_pseudo_lock.o = -I$(src)
+obj-$(CONFIG_INTEL_SGX_CORE) += intel_sgx.o

obj-$(CONFIG_X86_MCE) += mcheck/
obj-$(CONFIG_MTRR) += mtrr/
diff --git a/arch/x86/kernel/cpu/intel_sgx.c b/arch/x86/kernel/cpu/intel_sgx.c
new file mode 100644
index 000000000000..138af9b9a39a
--- /dev/null
+++ b/arch/x86/kernel/cpu/intel_sgx.c
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright(c) 2016-17 Intel Corporation.
+
+#include <linux/freezer.h>
+#include <linux/highmem.h>
+#include <linux/kthread.h>
+#include <linux/pagemap.h>
+#include <linux/ratelimit.h>
+#include <linux/sched/signal.h>
+#include <linux/slab.h>
+#include <asm/sgx.h>
+
+bool sgx_enabled __ro_after_init;
+EXPORT_SYMBOL_GPL(sgx_enabled);
+bool sgx_lc_enabled __ro_after_init;
+EXPORT_SYMBOL_GPL(sgx_lc_enabled);
+
+static __init int sgx_init(void)
+{
+ unsigned long fc;
+
+ if (!boot_cpu_has(X86_FEATURE_SGX))
+ return false;
+
+ if (!boot_cpu_has(X86_FEATURE_SGX1))
+ return false;
+
+ rdmsrl(MSR_IA32_FEATURE_CONTROL, fc);
+ if (!(fc & FEATURE_CONTROL_LOCKED)) {
+ pr_err("sgx: IA32_FEATURE_CONTROL MSR not locked\n");
+ return false;
+ }
+
+ if (!(fc & FEATURE_CONTROL_SGX_ENABLE)) {
+ pr_info("sgx: disabled by the firmware\n");
+ return false;
+ }
+
+ sgx_enabled = true;
+ sgx_lc_enabled = !!(fc & FEATURE_CONTROL_SGX_LE_WR);
+ return 0;
+}
+
+arch_initcall(sgx_init);
--
2.17.1


2018-09-25 13:13:52

by Jarkko Sakkinen

[permalink] [raw]
Subject: [PATCH v14 11/19] x86/sgx: Add wrappers for ENCLS leaf functions

Add wrappers for Intel(R) SGX ENCLS opcode leaf functions except
ENCLS(EINIT). ENCLS invokes privileged functions for managing (creation,
initialization and swapping) and debugging enclaves.

Signed-off-by: Jarkko Sakkinen <[email protected]>
Co-developed-by: Sean Christopherson <[email protected]>
Signed-off-by: Sean Christopherson <[email protected]>
---
arch/x86/include/asm/sgx.h | 244 +++++++++++++++++++++++++++++++++++++
1 file changed, 244 insertions(+)

diff --git a/arch/x86/include/asm/sgx.h b/arch/x86/include/asm/sgx.h
index f4f82f0453a9..e66e2572011e 100644
--- a/arch/x86/include/asm/sgx.h
+++ b/arch/x86/include/asm/sgx.h
@@ -10,4 +10,248 @@
extern bool sgx_enabled;
extern bool sgx_lc_enabled;

+/**
+ * ENCLS_FAULT_FLAG - flag signifying an ENCLS return code is a trapnr
+ *
+ * ENCLS has its own (positive value) error codes and also generates
+ * ENCLS specific #GP and #PF faults. And the ENCLS values get munged
+ * with system error codes as everything percolates back up the stack.
+ * Unfortunately (for us), we need to precisely identify each unique
+ * error code, e.g. the action taken if EWB fails varies based on the
+ * type of fault and on the exact SGX error code, i.e. we can't simply
+ * convert all faults to -EFAULT.
+ *
+ * To make all three error types coexist, we set bit 30 to identify an
+ * ENCLS fault. Bit 31 (technically bits N:31) is used to differentiate
+ * between positive (faults and SGX error codes) and negative (system
+ * error codes) values.
+ */
+#define ENCLS_FAULT_FLAG 0x40000000
+
+/**
+ * Check for a fault by looking for a postive value with the fault
+ * flag set. The postive value check is needed to filter out system
+ * error codes since negative values will have all higher order bits
+ * set, including ENCLS_FAULT_FLAG.
+ */
+#define IS_ENCLS_FAULT(r) ((int)(r) > 0 && ((r) & ENCLS_FAULT_FLAG))
+
+/**
+ * Retrieve the encoded trapnr from the specified return code.
+ */
+#define ENCLS_TRAPNR(r) ((r) & ~ENCLS_FAULT_FLAG)
+
+/**
+ * encls_to_err - translate an ENCLS fault or SGX code into a system error code
+ * @ret: positive value return code
+ *
+ * Translate a postive return code, e.g. from ENCLS, into a system error
+ * code. Primarily used by functions that cannot return a non-negative
+ * error code, e.g. kernel callbacks.
+ *
+ * Return:
+ * 0 on success,
+ * -errno on failure
+ */
+static inline int encls_to_err(int ret)
+{
+ if (IS_ENCLS_FAULT(ret))
+ return -EFAULT;
+
+ switch (ret) {
+ case SGX_UNMASKED_EVENT:
+ return -EINTR;
+ case SGX_INVALID_SIG_STRUCT:
+ case SGX_INVALID_ATTRIBUTE:
+ case SGX_INVALID_MEASUREMENT:
+ case SGX_INVALID_EINITTOKEN:
+ case SGX_INVALID_CPUSVN:
+ case SGX_INVALID_ISVSVN:
+ case SGX_INVALID_KEYNAME:
+ return -EINVAL;
+ case SGX_ENCLAVE_ACT:
+ case SGX_CHILD_PRESENT:
+ case SGX_ENTRYEPOCH_LOCKED:
+ case SGX_PREV_TRK_INCMPL:
+ case SGX_PAGE_NOT_MODIFIABLE:
+ case SGX_PAGE_NOT_DEBUGGABLE:
+ return -EBUSY;
+ default:
+ return -EIO;
+ };
+}
+
+/**
+ * __encls_ret_N - encode an ENCLS leaf that returns an error code in EAX
+ * @rax: leaf number
+ * @inputs: asm inputs for the leaf
+ *
+ * Emit assembly for an ENCLS leaf that returns an error code, e.g. EREMOVE.
+ * And because SGX isn't complex enough as it is, leafs that return an error
+ * code also modify flags.
+ *
+ * Return:
+ * 0 on success,
+ * SGX error code on failure
+ */
+#define __encls_ret_N(rax, inputs...) \
+ ({ \
+ int ret; \
+ asm volatile( \
+ "1: .byte 0x0f, 0x01, 0xcf;\n\t" \
+ "2:\n" \
+ ".section .fixup,\"ax\"\n" \
+ "3: orl $"__stringify(ENCLS_FAULT_FLAG)",%%eax\n" \
+ " jmp 2b\n" \
+ ".previous\n" \
+ _ASM_EXTABLE_FAULT(1b, 3b) \
+ : "=a"(ret) \
+ : "a"(rax), inputs \
+ : "memory", "cc"); \
+ ret; \
+ })
+
+#define __encls_ret_1(rax, rcx) \
+ ({ \
+ __encls_ret_N(rax, "c"(rcx)); \
+ })
+
+#define __encls_ret_2(rax, rbx, rcx) \
+ ({ \
+ __encls_ret_N(rax, "b"(rbx), "c"(rcx)); \
+ })
+
+#define __encls_ret_3(rax, rbx, rcx, rdx) \
+ ({ \
+ __encls_ret_N(rax, "b"(rbx), "c"(rcx), "d"(rdx)); \
+ })
+
+/**
+ * __encls_N - encode an ENCLS leaf that doesn't return an error code
+ * @rax: leaf number
+ * @rbx_out: optional output variable
+ * @inputs: asm inputs for the leaf
+ *
+ * Emit assembly for an ENCLS leaf that does not return an error code,
+ * e.g. ECREATE. Leaves without error codes either succeed or fault.
+ * @rbx_out is an optional parameter for use by EDGBRD, which returns
+ * the the requested value in RBX.
+ *
+ * Return:
+ * 0 on success,
+ * trapnr with ENCLS_FAULT_FLAG set on fault
+ */
+#define __encls_N(rax, rbx_out, inputs...) \
+ ({ \
+ int ret; \
+ asm volatile( \
+ "1: .byte 0x0f, 0x01, 0xcf;\n\t" \
+ " xor %%eax,%%eax;\n" \
+ "2:\n" \
+ ".section .fixup,\"ax\"\n" \
+ "3: orl $"__stringify(ENCLS_FAULT_FLAG)",%%eax\n" \
+ " jmp 2b\n" \
+ ".previous\n" \
+ _ASM_EXTABLE_FAULT(1b, 3b) \
+ : "=a"(ret), "=b"(rbx_out) \
+ : "a"(rax), inputs \
+ : "memory"); \
+ ret; \
+ })
+
+#define __encls_2(rax, rbx, rcx) \
+ ({ \
+ unsigned long ign_rbx_out; \
+ __encls_N(rax, ign_rbx_out, "b"(rbx), "c"(rcx)); \
+ })
+
+#define __encls_1_1(rax, data, rcx) \
+ ({ \
+ unsigned long rbx_out; \
+ int ret = __encls_N(rax, rbx_out, "c"(rcx)); \
+ if (!ret) \
+ data = rbx_out; \
+ ret; \
+ })
+
+static inline int __ecreate(struct sgx_pageinfo *pginfo, void __iomem *secs)
+{
+ return __encls_2(SGX_ECREATE, pginfo, secs);
+}
+
+static inline int __eextend(void __iomem *secs, void __iomem *addr)
+{
+ return __encls_2(SGX_EEXTEND, secs, addr);
+}
+
+static inline int __eadd(struct sgx_pageinfo *pginfo, void __iomem *addr)
+{
+ return __encls_2(SGX_EADD, pginfo, addr);
+}
+
+static inline int __einit(void *sigstruct, struct sgx_einittoken *einittoken,
+ void __iomem *secs)
+{
+ return __encls_ret_3(SGX_EINIT, sigstruct, secs, einittoken);
+}
+
+static inline int __eremove(void __iomem *addr)
+{
+ return __encls_ret_1(SGX_EREMOVE, addr);
+}
+
+static inline int __edbgwr(void __iomem *addr, unsigned long *data)
+{
+ return __encls_2(SGX_EDGBWR, *data, addr);
+}
+
+static inline int __edbgrd(void __iomem *addr, unsigned long *data)
+{
+ return __encls_1_1(SGX_EDGBRD, *data, addr);
+}
+
+static inline int __etrack(void __iomem *addr)
+{
+ return __encls_ret_1(SGX_ETRACK, addr);
+}
+
+static inline int __eldu(struct sgx_pageinfo *pginfo, void __iomem *addr,
+ void *va)
+{
+ return __encls_ret_3(SGX_ELDU, pginfo, addr, va);
+}
+
+static inline int __eblock(void __iomem *addr)
+{
+ return __encls_ret_1(SGX_EBLOCK, addr);
+}
+
+static inline int __epa(void __iomem *addr)
+{
+ unsigned long rbx = SGX_PAGE_TYPE_VA;
+
+ return __encls_2(SGX_EPA, rbx, addr);
+}
+
+static inline int __ewb(struct sgx_pageinfo *pginfo, void __iomem *addr,
+ void __iomem *va)
+{
+ return __encls_ret_3(SGX_EWB, pginfo, addr, va);
+}
+
+static inline int __eaug(struct sgx_pageinfo *pginfo, void __iomem *addr)
+{
+ return __encls_2(SGX_EAUG, pginfo, addr);
+}
+
+static inline int __emodpr(struct sgx_secinfo *secinfo, void __iomem *addr)
+{
+ return __encls_ret_2(SGX_EMODPR, secinfo, addr);
+}
+
+static inline int __emodt(struct sgx_secinfo *secinfo, void __iomem *addr)
+{
+ return __encls_ret_2(SGX_EMODT, secinfo, addr);
+}
+
#endif /* _ASM_X86_SGX_H */
--
2.17.1


2018-09-25 13:13:59

by Jarkko Sakkinen

[permalink] [raw]
Subject: [PATCH v14 09/19] x86/mm: x86/sgx: Signal SEGV_SGXERR for #PFs w/ PF_SGX

From: Sean Christopherson <[email protected]>

Signal SIGSEGV(SEGV_SGXERR) for all faults with PF_SGX set in the
error code. The PF_SGX bit is set if and only if the #PF is detected
by the Enclave Page Cache Map (EPCM), which is consulted only after
an access walks the kernel's page tables, i.e.:

a. the access was allowed by the kernel
b. the kernel's tables have become less restrictive than the EPCM
c. the kernel cannot fixup the cause of the fault

Noteably, (b) implies that either the kernel has botched the EPC
mappings or the EPCM has been invalidated due to a power event. In
either case, userspace needs to be alerted so that it can take
appropriate action, e.g. restart the enclave. This is reinforced
by (c) as the kernel doesn't really have any other reasonable option,
e.g. we could kill the task or panic, but neither is warranted.

Signed-off-by: Sean Christopherson <[email protected]>
Cc: Dave Hansen <[email protected]>
Signed-off-by: Jarkko Sakkinen <[email protected]>
---
arch/x86/mm/fault.c | 20 +++++++++++++++++---
1 file changed, 17 insertions(+), 3 deletions(-)

diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
index 85d20516b2f3..3fb2b2838d6c 100644
--- a/arch/x86/mm/fault.c
+++ b/arch/x86/mm/fault.c
@@ -960,10 +960,13 @@ static noinline void
bad_area_access_error(struct pt_regs *regs, unsigned long error_code,
unsigned long address, struct vm_area_struct *vma)
{
+ int si_code = SEGV_ACCERR;
+
if (bad_area_access_from_pkeys(error_code, vma))
- __bad_area(regs, error_code, address, vma, SEGV_PKUERR);
- else
- __bad_area(regs, error_code, address, vma, SEGV_ACCERR);
+ si_code = SEGV_PKUERR;
+ else if (unlikely(error_code & X86_PF_SGX))
+ si_code = SEGV_SGXERR;
+ __bad_area(regs, error_code, address, vma, si_code);
}

static void
@@ -1153,6 +1156,17 @@ access_error(unsigned long error_code, struct vm_area_struct *vma)
if (error_code & X86_PF_PK)
return 1;

+ /*
+ * Access is blocked by the Enclave Page Cache Map (EPCM),
+ * i.e. the access is allowed by the PTE but not the EPCM.
+ * This usually happens when the EPCM is yanked out from
+ * under us, e.g. by hardware after a suspend/resume cycle.
+ * In any case, there is nothing that can be done by the
+ * kernel to resolve the fault (short of killing the task).
+ */
+ if (unlikely(error_code & X86_PF_SGX))
+ return 1;
+
/*
* Make sure to check the VMA so that we do not perform
* faults just to hit a X86_PF_PK as soon as we fill in a
--
2.17.1


2018-09-25 13:13:59

by Jarkko Sakkinen

[permalink] [raw]
Subject: [PATCH v14 01/19] x86/sgx: Update MAINTAINERS

Add the maintainer information for the SGX subsystem.

Signed-off-by: Jarkko Sakkinen <[email protected]>
---
MAINTAINERS | 7 +++++++
1 file changed, 7 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 02a39617ec82..a5597a89c7a9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7595,6 +7595,13 @@ L: [email protected]
S: Maintained
F: drivers/gpio/gpio-intel-mid.c

+INTEL SGX
+M: Jarkko Sakkinen <[email protected]>
+L: [email protected]
+Q: https://patchwork.kernel.org/project/intel-sgx/list/
+F: drivers/platform/x86/intel_sgx/
+K: \bSGX_
+
INVENSENSE MPU-3050 GYROSCOPE DRIVER
M: Linus Walleij <[email protected]>
L: [email protected]
--
2.17.1


2018-09-25 13:14:06

by Jarkko Sakkinen

[permalink] [raw]
Subject: [PATCH v14 12/19] x86/sgx: Add data structures for tracking the EPC pages

Add data structures to track Enclave Page Cache (EPC) pages. EPC is
divided into multiple banks (1-N) of which addresses and sizes can be
enumerated with CPUID by the OS.

On NUMA systems a node can have at most bank. A bank can be at most part of
two nodes. SGX supports both nodes with a single memory controller and also
sub-cluster nodes with severals memory controllers on a single die.

Signed-off-by: Jarkko Sakkinen <[email protected]>
Co-developed-by: Serge Ayoun <[email protected]>
Co-developed-by: Sean Christopherson <[email protected]>
Signed-off-by: Serge Ayoun <[email protected]>
Signed-off-by: Sean Christopherson <[email protected]>
---
arch/x86/include/asm/sgx.h | 55 +++++++++++++++
arch/x86/kernel/cpu/intel_sgx.c | 115 ++++++++++++++++++++++++++++++++
2 files changed, 170 insertions(+)

diff --git a/arch/x86/include/asm/sgx.h b/arch/x86/include/asm/sgx.h
index e66e2572011e..468e609147cd 100644
--- a/arch/x86/include/asm/sgx.h
+++ b/arch/x86/include/asm/sgx.h
@@ -5,10 +5,65 @@
#ifndef _ASM_X86_SGX_H
#define _ASM_X86_SGX_H

+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/rwsem.h>
#include <linux/types.h>
+#include <asm/sgx_arch.h>
+#include <asm/asm.h>
+
+struct sgx_epc_page {
+ unsigned long desc;
+ struct list_head list;
+};
+
+/**
+ * struct sgx_epc_section
+ *
+ * The firmware can define multiple chunks of EPC to the different areas of the
+ * physical memory e.g. for memory areas of the each node. This structure is
+ * used to store EPC pages for one EPC section and virtual memory area where
+ * the pages have been mapped.
+ */
+struct sgx_epc_section {
+ unsigned long pa;
+ void __iomem *va;
+ struct sgx_epc_page **pages;
+ unsigned long free_cnt;
+ spinlock_t lock;
+};

extern bool sgx_enabled;
extern bool sgx_lc_enabled;
+extern struct sgx_epc_section sgx_epc_sections[SGX_MAX_EPC_SECTIONS];
+
+/*
+ * enum sgx_epc_page_desc - bits and masks for an EPC page's descriptor
+ * %SGX_EPC_SECTION_MASK: SGX allows to have multiple EPC sections in the
+ * physical memory. The existing and near-future
+ * hardware defines at most eight sections, hence
+ * three bits to hold a section.
+ * %SGX_EPC_PAGE_RECLAIMABLE: The page page is reclaimable. Used when freeing
+ * a page to know that we also need to remove the
+ * page from the list of reclaimable pages.
+ */
+enum sgx_epc_page_desc {
+ SGX_EPC_SECTION_MASK = GENMASK_ULL(3, 0),
+ SGX_EPC_PAGE_RECLAIMABLE = BIT(4),
+ /* bits 12-63 are reserved for the physical page address of the page */
+};
+
+static inline struct sgx_epc_section *sgx_epc_section(struct sgx_epc_page *page)
+{
+ return &sgx_epc_sections[page->desc & SGX_EPC_SECTION_MASK];
+}
+
+static inline void __iomem *sgx_epc_addr(struct sgx_epc_page *page)
+{
+ struct sgx_epc_section *section = sgx_epc_section(page);
+
+ return section->va + (page->desc & PAGE_MASK) - section->pa;
+}

/**
* ENCLS_FAULT_FLAG - flag signifying an ENCLS return code is a trapnr
diff --git a/arch/x86/kernel/cpu/intel_sgx.c b/arch/x86/kernel/cpu/intel_sgx.c
index 138af9b9a39a..b24d6287442d 100644
--- a/arch/x86/kernel/cpu/intel_sgx.c
+++ b/arch/x86/kernel/cpu/intel_sgx.c
@@ -14,10 +14,121 @@ bool sgx_enabled __ro_after_init;
EXPORT_SYMBOL_GPL(sgx_enabled);
bool sgx_lc_enabled __ro_after_init;
EXPORT_SYMBOL_GPL(sgx_lc_enabled);
+struct sgx_epc_section sgx_epc_sections[SGX_MAX_EPC_SECTIONS];
+EXPORT_SYMBOL_GPL(sgx_epc_sections);
+
+static int sgx_nr_epc_sections;
+
+static __init void sgx_free_epc_section(struct sgx_epc_section *section)
+{
+ int i;
+
+ for (i = 0; i < section->free_cnt && section->pages[i]; i++)
+ kfree(section->pages[i]);
+ kfree(section->pages);
+ iounmap(section->va);
+}
+
+static __init int sgx_init_epc_section(u64 addr, u64 size, unsigned long index,
+ struct sgx_epc_section *section)
+{
+ unsigned long nr_pages = size >> PAGE_SHIFT;
+ unsigned long i;
+
+ section->va = ioremap_cache(addr, size);
+ if (!section->va)
+ return -ENOMEM;
+
+ section->pa = addr;
+ section->free_cnt = nr_pages;
+ spin_lock_init(&section->lock);
+
+ section->pages = kcalloc(nr_pages, sizeof(struct sgx_epc_page *),
+ GFP_KERNEL);
+ if (!section->pages)
+ goto out;
+
+ for (i = 0; i < nr_pages; i++) {
+ section->pages[i] = kzalloc(sizeof(struct sgx_epc_page),
+ GFP_KERNEL);
+ if (!section->pages[i])
+ goto out;
+
+ section->pages[i]->desc = (addr + (i << PAGE_SHIFT)) | index;
+ }
+
+ return 0;
+out:
+ sgx_free_epc_section(section);
+ return -ENOMEM;
+}
+
+static __init void sgx_page_cache_teardown(void)
+{
+ int i;
+
+ for (i = 0; i < sgx_nr_epc_sections; i++)
+ sgx_free_epc_section(&sgx_epc_sections[i]);
+}
+
+/**
+ * A section metric is concatenated in a way that @low bits 12-31 define the
+ * bits 12-31 of the metric and @high bits 0-19 define the bits 32-51 of the
+ * metric.
+ */
+static inline u64 sgx_calc_section_metric(u64 low, u64 high)
+{
+ return (low & GENMASK_ULL(31, 12)) +
+ ((high & GENMASK_ULL(19, 0)) << 32);
+}
+
+static __init int sgx_page_cache_init(void)
+{
+ u32 eax, ebx, ecx, edx;
+ u32 section_type;
+ u64 pa, size;
+ int ret;
+ int i;
+
+ BUILD_BUG_ON(SGX_MAX_EPC_SECTIONS > (SGX_EPC_SECTION_MASK + 1));
+
+ for (i = 0; i < SGX_MAX_EPC_SECTIONS; i++) {
+ cpuid_count(SGX_CPUID, SGX_CPUID_SECTION + i, &eax, &ebx, &ecx,
+ &edx);
+ section_type = eax & SGX_CPUID_SECTION_MASK;
+ if (section_type == SGX_CPUID_SECTION_INVALID)
+ break;
+ if (section_type != SGX_CPUID_SECTION_VALID) {
+ pr_err("sgx: Unexpected section type: %u\n",
+ section_type);
+ return -ENODEV;
+ };
+
+ pa = sgx_calc_section_metric(eax, ebx);
+ size = sgx_calc_section_metric(ecx, edx);
+ pr_info("sgx: EPC section 0x%llx-0x%llx\n", pa, pa + size - 1);
+
+ ret = sgx_init_epc_section(pa, size, i, &sgx_epc_sections[i]);
+ if (ret) {
+ sgx_page_cache_teardown();
+ return ret;
+ }
+
+ sgx_nr_epc_sections++;
+ }
+
+ if (!sgx_nr_epc_sections) {
+ pr_err("sgx: There are zero EPC sections.\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}

static __init int sgx_init(void)
{
unsigned long fc;
+ int ret;

if (!boot_cpu_has(X86_FEATURE_SGX))
return false;
@@ -36,6 +147,10 @@ static __init int sgx_init(void)
return false;
}

+ ret = sgx_page_cache_init();
+ if (ret)
+ return ret;
+
sgx_enabled = true;
sgx_lc_enabled = !!(fc & FEATURE_CONTROL_SGX_LE_WR);
return 0;
--
2.17.1


2018-09-25 13:14:09

by Jarkko Sakkinen

[permalink] [raw]
Subject: [PATCH v14 08/19] signal: x86/sgx: Add SIGSEGV siginfo code for SGX EPCM fault

From: Sean Christopherson <[email protected]>

The SGX Enclave Page Cache Map (EPCM) is a hardware-managed table
that enforces accesses to an enclave's EPC page in addition to the
software-managed kernel page tables, i.e. the effective permissions
for an EPC page are a logical AND of the kernel's page tables and
the corresponding EPCM entry. The primary purpose of the EPCM is
to prevent a malcious or compromised kernel from attacking an enclave
by modifying the enclave's page tables. The EPCM entires for an
enclave are populated when the enclave is built and verified, using
metadata provided by the enclave that is included in the measurement
used to verify the enclave.

In normal operation of a properly functioning, non-malicious kernel
(and enclave), the EPCM permissions will never trigger a fault, i.e.
the kernel may make the permissions for an EPC page more restrictive,
e.g. mark it not-present to swap out the EPC page, but the kernel will
never make its permissions less restrictive.

But, there is a legitimate scenario in which the kernel's page tables
can become less restrictive than the EPCM: on current hardware all
enclaves are destroyed (by hardware) on a transition to S3 or lower
sleep states, i.e. all EPCM entries are invalid (not-present) after
the system resumes from its sleep state.

Unfortunately, on CPUs that support only SGX1, EPCM violations result
in a #GP. The upside of the #GP is that no kernel changes are needed
to deal with the EPCM being blasted away by hardware, e.g. userspace
gets a SIGSEGV, assumes the EPCM was lost and restarts its enclave
and everyone is happy. The downside is that userspace has to assume
the SIGSEGV was because the EPC was lost (or possibly do some leg work
to rule out other causes).

In SGX2, the oddity of delivering a #GP due to what are inherently
paging related violations is remedied. CPUs that support SGX2 deliver
EPCM violations as #PFs with a new SGX error code bit set. So, now
that hardware provides us with a way to unequivocally determine that
a fault was due to a EPCM violation, define a signfo code for SIGSEGV
so that the information can be passed onto userspace.

Cc: Dave Hansen <[email protected]>
Signed-off-by: Sean Christopherson <[email protected]>
Signed-off-by: Jarkko Sakkinen <[email protected]>
---
include/uapi/asm-generic/siginfo.h | 4 ++++
1 file changed, 4 insertions(+)

diff --git a/include/uapi/asm-generic/siginfo.h b/include/uapi/asm-generic/siginfo.h
index 80e2a7227205..fdd898e2325b 100644
--- a/include/uapi/asm-generic/siginfo.h
+++ b/include/uapi/asm-generic/siginfo.h
@@ -225,7 +225,11 @@ typedef struct siginfo {
#else
# define SEGV_PKUERR 4 /* failed protection key checks */
#endif
+#ifdef __x86_64__
+#define SEGV_SGXERR 5 /* SGX Enclave Page Cache Map fault */
+#else
#define SEGV_ACCADI 5 /* ADI not enabled for mapped object */
+#endif
#define SEGV_ADIDERR 6 /* Disrupting MCD error */
#define SEGV_ADIPERR 7 /* Precise MCD exception */
#define NSIGSEGV 7
--
2.17.1


2018-09-25 13:14:28

by Jarkko Sakkinen

[permalink] [raw]
Subject: [PATCH v14 13/19] x86/sgx: Enclave Page Cache (EPC) memory manager

Add a Enclave Page Cache (EPC) memory manager that can be used to
allocate and free EPC pages.

Signed-off-by: Jarkko Sakkinen <[email protected]>
Co-developed-by: Sean Christopherson <[email protected]>
Signed-off-by: Sean Christopherson <[email protected]>
---
arch/x86/include/asm/sgx.h | 7 ++-
arch/x86/kernel/cpu/intel_sgx.c | 86 +++++++++++++++++++++++++++++++++
2 files changed, 92 insertions(+), 1 deletion(-)

diff --git a/arch/x86/include/asm/sgx.h b/arch/x86/include/asm/sgx.h
index 468e609147cd..6ea5a6dbd36d 100644
--- a/arch/x86/include/asm/sgx.h
+++ b/arch/x86/include/asm/sgx.h
@@ -14,6 +14,7 @@

struct sgx_epc_page {
unsigned long desc;
+ void *owner;
struct list_head list;
};

@@ -65,7 +66,7 @@ static inline void __iomem *sgx_epc_addr(struct sgx_epc_page *page)
return section->va + (page->desc & PAGE_MASK) - section->pa;
}

-/**
+/*
* ENCLS_FAULT_FLAG - flag signifying an ENCLS return code is a trapnr
*
* ENCLS has its own (positive value) error codes and also generates
@@ -309,4 +310,8 @@ static inline int __emodt(struct sgx_secinfo *secinfo, void __iomem *addr)
return __encls_ret_2(SGX_EMODT, secinfo, addr);
}

+struct sgx_epc_page *sgx_alloc_page(void *owner, bool reclaim);
+int __sgx_free_page(struct sgx_epc_page *page);
+void sgx_free_page(struct sgx_epc_page *page);
+
#endif /* _ASM_X86_SGX_H */
diff --git a/arch/x86/kernel/cpu/intel_sgx.c b/arch/x86/kernel/cpu/intel_sgx.c
index b24d6287442d..ef30d6730859 100644
--- a/arch/x86/kernel/cpu/intel_sgx.c
+++ b/arch/x86/kernel/cpu/intel_sgx.c
@@ -19,6 +19,92 @@ EXPORT_SYMBOL_GPL(sgx_epc_sections);

static int sgx_nr_epc_sections;

+/**
+ * sgx_alloc_page - Allocate an EPC page
+ * @owner: the owner of the EPC page
+ * @reclaim: wait and reclaim pages up until we get one
+ *
+ * Try to grab a page from the free EPC page list. If there is a free page
+ * available, it is returned to the caller. The @reclaim parameter hints
+ * the EPC memory manager to swap pages when required.
+ *
+ * Return:
+ * a pointer to a &struct sgx_epc_page instace,
+ * -errno on error
+ */
+struct sgx_epc_page *sgx_alloc_page(void *owner, bool reclaim)
+{
+ struct sgx_epc_section *section;
+ struct sgx_epc_page *page;
+ int i;
+
+ for (i = 0; i < sgx_nr_epc_sections; i++) {
+ section = &sgx_epc_sections[i];
+ spin_lock(&section->lock);
+ if (section->free_cnt) {
+ page = section->pages[section->free_cnt - 1];
+ section->free_cnt--;
+ }
+ spin_unlock(&section->lock);
+
+ if (page) {
+ page->owner = owner;
+ return page;
+ }
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(sgx_alloc_page);
+
+/**
+ * __sgx_free_page - Free an EPC page
+ * @page: pointer a previously allocated EPC page
+ *
+ * Invaldate an EPC page and insert it back to the list of free pages.
+ *
+ * Return:
+ * 0 on success
+ * -EBUSY if the page cannot be removed from the active list
+ * SGX error code if EREMOVE fails
+ */
+int __sgx_free_page(struct sgx_epc_page *page)
+{
+ struct sgx_epc_section *section = sgx_epc_section(page);
+ int ret;
+
+ ret = __eremove(sgx_epc_addr(page));
+ if (ret)
+ return ret;
+
+ spin_lock(&section->lock);
+ section->pages[section->free_cnt++] = page;
+ spin_unlock(&section->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(__sgx_free_page);
+
+/**
+ * sgx_free_page - Free an EPC page and WARN on failure
+ * @page: pointer to a previously allocated EPC page
+ *
+ * EREMOVE an EPC page and insert it back to the list of free pages.
+ * If the page is reclaimable, deletes it from the active page list.
+ * WARN on any failure. For use when the call site cannot (or chooses
+ * not to) handle failure, i.e. the page is leaked on failure.
+ */
+void sgx_free_page(struct sgx_epc_page *page)
+{
+ int ret;
+
+ ret = __sgx_free_page(page);
+ WARN(ret < 0, "sgx: cannot free page, reclaim in-progress");
+ WARN(ret > 0, "sgx: EREMOVE returned %d (0x%x)", ret, ret);
+}
+EXPORT_SYMBOL_GPL(sgx_free_page);
+
+
static __init void sgx_free_epc_section(struct sgx_epc_section *section)
{
int i;
--
2.17.1


2018-09-25 13:14:43

by Jarkko Sakkinen

[permalink] [raw]
Subject: [PATCH v14 15/19] platform/x86: Intel SGX driver

Intel Software Guard eXtensions (SGX) is a set of CPU instructions that
can be used by applications to set aside private regions of code and
data. The code outside the enclave is disallowed to access the memory
inside the enclave by the CPU access control.

SGX driver provides a ioctl API for loading and initializing enclaves.
Address range for enclaves is reserved with mmap() and they are
destroyed with munmap(). Enclave construction, measurement and
initialization is done with the provided the ioctl API.

Signed-off-by: Jarkko Sakkinen <[email protected]>
Co-developed-by: Sean Christopherson <[email protected]>
Co-developed-by: Serge Ayoun <[email protected]>
Co-developed-by: Shay Katz-zamir <[email protected]>
Co-developed-by: Suresh Siddha <[email protected]>
Signed-off-by: Sean Christopherson <[email protected]>
Signed-off-by: Serge Ayoun <[email protected]>
Signed-off-by: Shay Katz-zamir <[email protected]>
Signed-off-by: Suresh Siddha <[email protected]>
---
arch/x86/include/uapi/asm/sgx.h | 60 ++
drivers/platform/x86/Kconfig | 2 +
drivers/platform/x86/Makefile | 1 +
drivers/platform/x86/intel_sgx/Kconfig | 20 +
drivers/platform/x86/intel_sgx/Makefile | 12 +
drivers/platform/x86/intel_sgx/sgx.h | 180 +++++
drivers/platform/x86/intel_sgx/sgx_encl.c | 826 +++++++++++++++++++++
drivers/platform/x86/intel_sgx/sgx_ioctl.c | 234 ++++++
drivers/platform/x86/intel_sgx/sgx_main.c | 267 +++++++
drivers/platform/x86/intel_sgx/sgx_util.c | 85 +++
drivers/platform/x86/intel_sgx/sgx_vma.c | 43 ++
11 files changed, 1730 insertions(+)
create mode 100644 arch/x86/include/uapi/asm/sgx.h
create mode 100644 drivers/platform/x86/intel_sgx/Kconfig
create mode 100644 drivers/platform/x86/intel_sgx/Makefile
create mode 100644 drivers/platform/x86/intel_sgx/sgx.h
create mode 100644 drivers/platform/x86/intel_sgx/sgx_encl.c
create mode 100644 drivers/platform/x86/intel_sgx/sgx_ioctl.c
create mode 100644 drivers/platform/x86/intel_sgx/sgx_main.c
create mode 100644 drivers/platform/x86/intel_sgx/sgx_util.c
create mode 100644 drivers/platform/x86/intel_sgx/sgx_vma.c

diff --git a/arch/x86/include/uapi/asm/sgx.h b/arch/x86/include/uapi/asm/sgx.h
new file mode 100644
index 000000000000..58bd5d5c3c9d
--- /dev/null
+++ b/arch/x86/include/uapi/asm/sgx.h
@@ -0,0 +1,60 @@
+
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/**
+ * Copyright(c) 2016-18 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)
+#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)
+
+/* IOCTL return values */
+#define SGX_POWER_LOST_ENCLAVE 0x40000000
+
+/**
+ * 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;
+};
+
+/**
+ * struct sgx_enclave_add_page - parameter structure for the
+ * %SGX_IOC_ENCLAVE_ADD_PAGE ioctl
+ * @addr: address within the ELRANGE
+ * @src: address for the page data
+ * @secinfo: address for the SECINFO data
+ * @mrmask: bitmask for the measured 256 byte chunks
+ */
+struct sgx_enclave_add_page {
+ __u64 addr;
+ __u64 src;
+ __u64 secinfo;
+ __u16 mrmask;
+} __attribute__((__packed__));
+
+
+/**
+ * struct sgx_enclave_init - parameter structure for the
+ * %SGX_IOC_ENCLAVE_INIT ioctl
+ * @addr: address within the ELRANGE
+ * @sigstruct: address for the SIGSTRUCT data
+ */
+struct sgx_enclave_init {
+ __u64 addr;
+ __u64 sigstruct;
+};
+
+#endif /* _UAPI_ASM_X86_SGX_H */
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 0c1aa6c314f5..66a1ab6235e2 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -1229,6 +1229,8 @@ config I2C_MULTI_INSTANTIATE
To compile this driver as a module, choose M here: the module
will be called i2c-multi-instantiate.

+source "drivers/platform/x86/intel_sgx/Kconfig"
+
endif # X86_PLATFORM_DEVICES

config PMC_ATOM
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index e6d1becf81ce..d0db8c66ed3c 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -92,3 +92,4 @@ obj-$(CONFIG_MLX_PLATFORM) += mlx-platform.o
obj-$(CONFIG_INTEL_TURBO_MAX_3) += intel_turbo_max_3.o
obj-$(CONFIG_INTEL_CHTDC_TI_PWRBTN) += intel_chtdc_ti_pwrbtn.o
obj-$(CONFIG_I2C_MULTI_INSTANTIATE) += i2c-multi-instantiate.o
+obj-$(CONFIG_INTEL_SGX) += intel_sgx/
diff --git a/drivers/platform/x86/intel_sgx/Kconfig b/drivers/platform/x86/intel_sgx/Kconfig
new file mode 100644
index 000000000000..7d22d44acce9
--- /dev/null
+++ b/drivers/platform/x86/intel_sgx/Kconfig
@@ -0,0 +1,20 @@
+#
+# Intel SGX
+#
+
+config INTEL_SGX
+ tristate "Intel(R) SGX Driver"
+ depends on X86_64 && CPU_SUP_INTEL && INTEL_SGX_CORE
+ select MMU_NOTIFIER
+ select CRYPTO
+ select CRYPTO_SHA256
+ help
+ Intel(R) SGX is a set of CPU instructions that can be used by
+ applications to set aside private regions of code and data. The code
+ outside the enclave is disallowed to access the memory inside the
+ enclave by the CPU access control.
+
+ The firmware uses PRMRR registers to reserve an area of physical memory
+ called Enclave Page Cache (EPC). There is a hardware unit in the
+ processor called Memory Encryption Engine. The MEE encrypts and decrypts
+ the EPC pages as they enter and leave the processor package.
diff --git a/drivers/platform/x86/intel_sgx/Makefile b/drivers/platform/x86/intel_sgx/Makefile
new file mode 100644
index 000000000000..117e97effeff
--- /dev/null
+++ b/drivers/platform/x86/intel_sgx/Makefile
@@ -0,0 +1,12 @@
+#
+# Intel SGX
+#
+
+obj-$(CONFIG_INTEL_SGX) += intel_sgx.o
+
+intel_sgx-$(CONFIG_INTEL_SGX) += \
+ sgx_encl.o \
+ sgx_ioctl.o \
+ sgx_main.o \
+ sgx_util.o \
+ sgx_vma.o \
diff --git a/drivers/platform/x86/intel_sgx/sgx.h b/drivers/platform/x86/intel_sgx/sgx.h
new file mode 100644
index 000000000000..67bd8ea1d53d
--- /dev/null
+++ b/drivers/platform/x86/intel_sgx/sgx.h
@@ -0,0 +1,180 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/**
+ * Copyright(c) 2016-18 Intel Corporation.
+ */
+#ifndef __ARCH_INTEL_SGX_H__
+#define __ARCH_INTEL_SGX_H__
+
+#include <crypto/hash.h>
+#include <linux/kref.h>
+#include <linux/mmu_notifier.h>
+#include <linux/mmu_notifier.h>
+#include <linux/radix-tree.h>
+#include <linux/radix-tree.h>
+#include <linux/rbtree.h>
+#include <linux/rwsem.h>
+#include <linux/sched.h>
+#include <linux/workqueue.h>
+#include <asm/sgx.h>
+#include <uapi/asm/sgx.h>
+
+#define sgx_pr(level, encl, fmt, ...) \
+ pr_ ## level("sgx: [%d:0x%p] " fmt, pid_nr((encl)->tgid), \
+ (void *)(encl)->base, ##__VA_ARGS__)
+#define sgx_dbg(encl, fmt, ...) \
+ sgx_pr(debug, encl, fmt, ##__VA_ARGS__)
+#define sgx_info(encl, fmt, ...) \
+ sgx_pr(info, encl, fmt, ##__VA_ARGS__)
+#define sgx_warn(encl, fmt, ...) \
+ sgx_pr(warn, encl, fmt, ##__VA_ARGS__)
+#define sgx_err(encl, fmt, ...) \
+ sgx_pr(err, encl, fmt, ##__VA_ARGS__)
+#define sgx_crit(encl, fmt, ...) \
+ sgx_pr(crit, encl, fmt, ##__VA_ARGS__)
+
+#define SGX_EINIT_SPIN_COUNT 20
+#define SGX_EINIT_SLEEP_COUNT 50
+#define SGX_EINIT_SLEEP_TIME 20
+
+/**
+ * enum sgx_encl_page_desc - defines bits for an enclave page's descriptor
+ * %SGX_ENCL_PAGE_TCS: The page is a TCS page.
+ * %SGX_ENCL_PAGE_LOADED: The page is not swapped.
+ * %SGX_ENCL_PAGE_ADDR_MASK: Holds the virtual address of the page.
+ */
+enum sgx_encl_page_desc {
+ SGX_ENCL_PAGE_TCS = BIT(0),
+ SGX_ENCL_PAGE_LOADED = BIT(1),
+ /* Bits 11:3 are available when the page is not swapped. */
+ SGX_ENCL_PAGE_ADDR_MASK = PAGE_MASK,
+};
+
+#define SGX_ENCL_PAGE_ADDR(encl_page) \
+ ((encl_page)->desc & SGX_ENCL_PAGE_ADDR_MASK)
+#define SGX_ENCL_PAGE_VA_OFFSET(encl_page) \
+ ((encl_page)->desc & SGX_ENCL_PAGE_VA_OFFSET_MASK)
+
+struct sgx_encl_page {
+ unsigned long desc;
+ union {
+ struct sgx_epc_page *epc_page;
+ struct sgx_va_page *va_page;
+ };
+ struct sgx_encl *encl;
+};
+
+enum sgx_encl_flags {
+ SGX_ENCL_INITIALIZED = BIT(0),
+ SGX_ENCL_DEBUG = BIT(1),
+ SGX_ENCL_SUSPEND = BIT(2),
+ SGX_ENCL_DEAD = BIT(3),
+};
+
+struct sgx_encl {
+ unsigned int flags;
+ uint64_t attributes;
+ uint64_t xfrm;
+ unsigned int page_cnt;
+ unsigned int secs_child_cnt;
+ struct mutex lock;
+ struct mm_struct *mm;
+ struct file *backing;
+ struct kref refcount;
+ unsigned long base;
+ unsigned long size;
+ unsigned long ssaframesize;
+ struct radix_tree_root page_tree;
+ struct list_head add_page_reqs;
+ struct work_struct add_page_work;
+ struct sgx_encl_page secs;
+ struct pid *tgid;
+ struct mmu_notifier mmu_notifier;
+ struct notifier_block pm_notifier;
+};
+
+static inline pgoff_t sgx_encl_page_backing_index(struct sgx_encl_page *page,
+ struct sgx_encl *encl)
+{
+ /* The backing page for SECS is located after the enclave pages. */
+ if (!PFN_DOWN(page->desc))
+ return PFN_DOWN(encl->size);
+
+ return PFN_DOWN(page->desc - encl->base);
+}
+
+extern struct workqueue_struct *sgx_add_page_wq;
+extern u64 sgx_encl_size_max_32;
+extern u64 sgx_encl_size_max_64;
+extern u64 sgx_xfrm_mask;
+extern u32 sgx_misc_reserved;
+extern u32 sgx_xsave_size_tbl[64];
+extern int sgx_epcm_trapnr;
+
+extern const struct vm_operations_struct sgx_vm_ops;
+
+int sgx_encl_find(struct mm_struct *mm, unsigned long addr,
+ struct vm_area_struct **vma);
+void sgx_invalidate(struct sgx_encl *encl, bool flush_cpus);
+
+/**
+ * SGX_INVD - invalidate an enclave on failure, i.e. if ret != 0
+ *
+ * @ret: a return code to check
+ * @encl: pointer to an enclave
+ * @fmt: message for WARN if failure is detected
+ * @...: optional arguments used by @fmt
+ *
+ * SGX_INVD is used in flows where an error, i.e. @ret is non-zero, is
+ * indicative of a driver bug. Invalidate @encl if @ret indicates an
+ * error and WARN on error unless the error was due to a fault signaled
+ * by the EPCM.
+ *
+ * Faults from the EPCM occur in normal kernel operation, e.g. due to
+ * misonfigured mprotect() from userspace or because the EPCM invalidated
+ * all EPC pages. The EPCM invalidates the EPC on transitions to S3 or
+ * lower sleep states, and VMMs emulate loss of EPC when migrating VMs.
+ *
+ * Defined as a macro instead of a function so that WARN can provide a
+ * more precise trace.
+ */
+#define SGX_INVD(ret, encl, fmt, ...) \
+do { \
+ if (unlikely(ret)) { \
+ int trapnr = IS_ENCLS_FAULT(ret) ? ENCLS_TRAPNR(ret) : 0; \
+ WARN(trapnr != sgx_epcm_trapnr, fmt, ##__VA_ARGS__); \
+ sgx_invalidate(encl, true); \
+ } \
+} while (0)
+
+struct sgx_encl *sgx_encl_alloc(struct sgx_secs *secs);
+int sgx_encl_create(struct sgx_encl *encl, struct sgx_secs *secs);
+struct sgx_encl_page *sgx_encl_alloc_page(struct sgx_encl *encl,
+ unsigned long addr);
+void sgx_encl_free_page(struct sgx_encl_page *encl_page);
+int sgx_encl_add_page(struct sgx_encl *encl, unsigned long addr, void *data,
+ struct sgx_secinfo *secinfo, unsigned int mrmask);
+int sgx_encl_init(struct sgx_encl *encl, struct sgx_sigstruct *sigstruct,
+ struct sgx_einittoken *einittoken);
+void sgx_encl_block(struct sgx_encl_page *encl_page);
+void sgx_encl_track(struct sgx_encl *encl);
+int sgx_encl_load_page(struct sgx_encl_page *encl_page,
+ struct sgx_epc_page *epc_page);
+void sgx_encl_release(struct kref *ref);
+
+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
+
+struct sgx_encl_page *sgx_fault_page(struct vm_area_struct *vma,
+ unsigned long addr,
+ bool do_reserve);
+
+int sgx_test_and_clear_young(struct sgx_encl_page *page);
+void sgx_flush_cpus(struct sgx_encl *encl);
+void sgx_set_page_loaded(struct sgx_encl_page *encl_page,
+ struct sgx_epc_page *epc_page);
+struct page *sgx_get_backing(struct file *file, pgoff_t index);
+void sgx_put_backing(struct page *backing_page, bool write);
+
+#endif /* __ARCH_X86_INTEL_SGX_H__ */
diff --git a/drivers/platform/x86/intel_sgx/sgx_encl.c b/drivers/platform/x86/intel_sgx/sgx_encl.c
new file mode 100644
index 000000000000..b9ecdc20d67e
--- /dev/null
+++ b/drivers/platform/x86/intel_sgx/sgx_encl.c
@@ -0,0 +1,826 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright(c) 2016-18 Intel Corporation.
+
+#include <asm/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 "sgx.h"
+
+struct sgx_add_page_req {
+ struct sgx_encl *encl;
+ struct sgx_encl_page *encl_page;
+ struct sgx_secinfo secinfo;
+ unsigned long mrmask;
+ struct list_head list;
+};
+
+/**
+ * sgx_encl_find - find an enclave
+ * @mm: mm struct of the current process
+ * @addr: address in the ELRANGE
+ * @vma: the resulting VMA
+ *
+ * Finds an enclave identified by the given address. Gives back the VMA, that
+ * is part of the enclave, located in that address. The VMA is given back if it
+ * is a proper enclave VMA even if an &sgx_encl instance does not exist
+ * yet (enclave creation has not been performed).
+ *
+ * Return:
+ * 0 on success,
+ * -EINVAL if an enclave was not found,
+ * -ENOENT if the enclave has not been created yet
+ */
+int sgx_encl_find(struct mm_struct *mm, unsigned long addr,
+ struct vm_area_struct **vma)
+{
+ struct vm_area_struct *result;
+ struct sgx_encl *encl;
+
+ result = find_vma(mm, addr);
+ if (!result || result->vm_ops != &sgx_vm_ops || addr < result->vm_start)
+ return -EINVAL;
+
+ encl = result->vm_private_data;
+ *vma = result;
+
+ return encl ? 0 : -ENOENT;
+}
+
+/**
+ * sgx_invalidate - kill an enclave
+ * @encl: an &sgx_encl instance
+ * @flush_cpus Set if there can be active threads inside the enclave.
+ *
+ * Mark the enclave as dead and immediately free its EPC pages (but not
+ * its resources). For active enclaves, the entry points to the enclave
+ * are destroyed first and hardware threads are kicked out so that the
+ * EPC pages can be safely manipulated.
+ */
+void sgx_invalidate(struct sgx_encl *encl, bool flush_cpus)
+{
+ struct sgx_encl_page *entry;
+ struct radix_tree_iter iter;
+ struct vm_area_struct *vma;
+ unsigned long addr;
+ void **slot;
+
+ if (encl->flags & SGX_ENCL_DEAD)
+ return;
+
+ encl->flags |= SGX_ENCL_DEAD;
+ if (flush_cpus) {
+ radix_tree_for_each_slot(slot, &encl->page_tree, &iter, 0) {
+ entry = *slot;
+ addr = SGX_ENCL_PAGE_ADDR(entry);
+ if ((entry->desc & SGX_ENCL_PAGE_LOADED) &&
+ (entry->desc & SGX_ENCL_PAGE_TCS) &&
+ !sgx_encl_find(encl->mm, addr, &vma))
+ zap_vma_ptes(vma, addr, PAGE_SIZE);
+ }
+ sgx_flush_cpus(encl);
+ }
+ radix_tree_for_each_slot(slot, &encl->page_tree, &iter, 0) {
+ entry = *slot;
+ /* If the page has RECLAIMED set, it is being reclaimed so we
+ * need to check that and let the swapper thread to free the
+ * page if this is the case.
+ */
+ if (entry->desc & SGX_ENCL_PAGE_LOADED) {
+ if (!__sgx_free_page(entry->epc_page))
+ entry->desc &= ~SGX_ENCL_PAGE_LOADED;
+ }
+ }
+}
+
+static bool sgx_process_add_page_req(struct sgx_add_page_req *req,
+ struct sgx_epc_page *epc_page)
+{
+ struct sgx_encl_page *encl_page = req->encl_page;
+ struct sgx_encl *encl = req->encl;
+ struct sgx_secinfo secinfo;
+ struct sgx_pageinfo pginfo;
+ struct vm_area_struct *vma;
+ pgoff_t backing_index;
+ struct page *backing;
+ unsigned long addr;
+ int ret;
+ int i;
+
+ if (encl->flags & (SGX_ENCL_SUSPEND | SGX_ENCL_DEAD))
+ return false;
+
+ addr = SGX_ENCL_PAGE_ADDR(encl_page);
+ ret = sgx_encl_find(encl->mm, addr, &vma);
+ if (ret)
+ return false;
+
+ backing_index = sgx_encl_page_backing_index(encl_page, encl);
+ backing = sgx_get_backing(encl->backing, backing_index);
+ if (IS_ERR(backing))
+ return false;
+
+ ret = vm_insert_pfn(vma, addr, PFN_DOWN(epc_page->desc));
+ if (ret) {
+ sgx_err(encl, "%s: vm_insert_pfn() returned %d\n", __func__,
+ ret);
+ sgx_put_backing(backing, false);
+ return false;
+ }
+
+ /*
+ * The SECINFO field must be 64-byte aligned, copy it to a local
+ * variable that is guaranteed to be aligned as req->secinfo may
+ * or may not be 64-byte aligned, e.g. req may have been allocated
+ * via kzalloc which is not aware of __aligned attributes.
+ */
+ memcpy(&secinfo, &req->secinfo, sizeof(secinfo));
+
+ pginfo.secs = (unsigned long)sgx_epc_addr(encl->secs.epc_page);
+ pginfo.addr = addr;
+ pginfo.metadata = (unsigned long)&secinfo;
+ pginfo.contents = (unsigned long)kmap_atomic(backing);
+ ret = __eadd(&pginfo, sgx_epc_addr(epc_page));
+ kunmap_atomic((void *)(unsigned long)pginfo.contents);
+
+ sgx_put_backing(backing, false);
+ if (ret) {
+ sgx_err(encl, "EADD returned %d\n", ret);
+ zap_vma_ptes(vma, addr, PAGE_SIZE);
+ return false;
+ }
+
+ for_each_set_bit(i, &req->mrmask, 16) {
+ ret = __eextend(sgx_epc_addr(encl->secs.epc_page),
+ sgx_epc_addr(epc_page) + (i * 0x100));
+ if (ret) {
+ sgx_err(encl, "EEXTEND returned %d\n", ret);
+ zap_vma_ptes(vma, addr, PAGE_SIZE);
+ return ret;
+ }
+ }
+
+ encl_page->encl = encl;
+ encl->secs_child_cnt++;
+ sgx_set_page_loaded(encl_page, epc_page);
+ sgx_test_and_clear_young(encl_page);
+ return true;
+}
+
+static void sgx_add_page_worker(struct work_struct *work)
+{
+ struct sgx_add_page_req *req;
+ bool skip_rest = false;
+ bool is_empty = false;
+ struct sgx_encl *encl;
+ struct sgx_epc_page *epc_page;
+
+ encl = container_of(work, struct sgx_encl, add_page_work);
+
+ do {
+ schedule();
+
+ mutex_lock(&encl->lock);
+ if (encl->flags & SGX_ENCL_DEAD)
+ skip_rest = true;
+
+ 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)
+ goto next;
+
+ epc_page = sgx_alloc_page(req->encl_page, true);
+ down_read(&encl->mm->mmap_sem);
+ mutex_lock(&encl->lock);
+
+ if (IS_ERR(epc_page)) {
+ sgx_invalidate(encl, false);
+ skip_rest = true;
+ } else if (!sgx_process_add_page_req(req, epc_page)) {
+ sgx_free_page(epc_page);
+ sgx_invalidate(encl, false);
+ skip_rest = true;
+ }
+
+ mutex_unlock(&encl->lock);
+ up_read(&encl->mm->mmap_sem);
+
+next:
+ kfree(req);
+ } while (!kref_put(&encl->refcount, sgx_encl_release) && !is_empty);
+}
+
+static u32 sgx_calc_ssaframesize(u32 miscselect, u64 xfrm)
+{
+ u32 size_max = PAGE_SIZE;
+ u32 size;
+ int i;
+
+ for (i = 2; i < 64; i++) {
+ if (!((1 << i) & xfrm))
+ continue;
+
+ size = SGX_SSA_GPRS_SIZE + sgx_xsave_size_tbl[i];
+ if (miscselect & SGX_MISC_EXINFO)
+ size += SGX_SSA_MISC_EXINFO_SIZE;
+
+ if (size > size_max)
+ size_max = size;
+ }
+
+ return PFN_UP(size_max);
+}
+
+static int sgx_validate_secs(const struct sgx_secs *secs,
+ unsigned long ssaframesize)
+{
+ if (secs->size < (2 * PAGE_SIZE) || !is_power_of_2(secs->size))
+ return -EINVAL;
+
+ if (secs->base & (secs->size - 1))
+ return -EINVAL;
+
+ if (secs->attributes & SGX_ATTR_RESERVED_MASK ||
+ secs->miscselect & sgx_misc_reserved)
+ return -EINVAL;
+
+ if (secs->attributes & SGX_ATTR_MODE64BIT) {
+ if (secs->size > sgx_encl_size_max_64)
+ return -EINVAL;
+ } else {
+ /* On 64-bit architecture allow 32-bit encls only in
+ * the compatibility mode.
+ */
+ if (!test_thread_flag(TIF_ADDR32))
+ return -EINVAL;
+ if (secs->size > sgx_encl_size_max_32)
+ return -EINVAL;
+ }
+
+ if (!(secs->xfrm & XFEATURE_MASK_FP) ||
+ !(secs->xfrm & XFEATURE_MASK_SSE) ||
+ (((secs->xfrm >> XFEATURE_BNDREGS) & 1) !=
+ ((secs->xfrm >> XFEATURE_BNDCSR) & 1)) ||
+ (secs->xfrm & ~sgx_xfrm_mask))
+ return -EINVAL;
+
+ if (!secs->ssa_frame_size || ssaframesize > secs->ssa_frame_size)
+ return -EINVAL;
+
+ if (memchr_inv(secs->reserved1, 0, SGX_SECS_RESERVED1_SIZE) ||
+ memchr_inv(secs->reserved2, 0, SGX_SECS_RESERVED2_SIZE) ||
+ memchr_inv(secs->reserved3, 0, SGX_SECS_RESERVED3_SIZE) ||
+ memchr_inv(secs->reserved4, 0, SGX_SECS_RESERVED4_SIZE))
+ return -EINVAL;
+
+ 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_encl_alloc - allocate memory for an enclave and set attributes
+ *
+ * @secs: SECS data (must be page aligned)
+ *
+ * Allocates a new &sgx_encl instance. Validates SECS attributes, creates
+ * backing storage for the enclave and sets enclave attributes to sane initial
+ * values.
+ *
+ * Return:
+ * an &sgx_encl instance,
+ * -errno otherwise
+ */
+struct sgx_encl *sgx_encl_alloc(struct sgx_secs *secs)
+{
+ unsigned long ssaframesize;
+ struct sgx_encl *encl;
+ struct file *backing;
+
+ ssaframesize = sgx_calc_ssaframesize(secs->miscselect, secs->xfrm);
+ if (sgx_validate_secs(secs, ssaframesize))
+ return ERR_PTR(-EINVAL);
+
+ backing = shmem_file_setup("[dev/sgx]", secs->size + PAGE_SIZE,
+ VM_NORESERVE);
+ if (IS_ERR(backing))
+ return ERR_CAST(backing);
+
+ encl = kzalloc(sizeof(*encl), GFP_KERNEL);
+ if (!encl) {
+ fput(backing);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ encl->attributes = secs->attributes;
+ encl->xfrm = secs->xfrm;
+
+ kref_init(&encl->refcount);
+ INIT_LIST_HEAD(&encl->add_page_reqs);
+ INIT_RADIX_TREE(&encl->page_tree, GFP_KERNEL);
+ 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->ssaframesize = secs->ssa_frame_size;
+ encl->backing = backing;
+
+ return encl;
+}
+
+static int sgx_encl_pm_notifier(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct sgx_encl *encl = container_of(nb, struct sgx_encl, pm_notifier);
+
+ if (action != PM_SUSPEND_PREPARE && action != PM_HIBERNATION_PREPARE)
+ return NOTIFY_DONE;
+
+ mutex_lock(&encl->lock);
+ sgx_invalidate(encl, false);
+ encl->flags |= SGX_ENCL_SUSPEND;
+ mutex_unlock(&encl->lock);
+ flush_work(&encl->add_page_work);
+ return NOTIFY_DONE;
+}
+
+/**
+ * sgx_encl_create - create an enclave
+ *
+ * @encl: an enclave
+ * @secs: page aligned SECS data
+ *
+ * Validates SECS attributes, allocates an EPC page for the SECS and creates
+ * the enclave by performing ECREATE.
+ *
+ * Return:
+ * 0 on success,
+ * -errno otherwise
+ */
+int sgx_encl_create(struct sgx_encl *encl, struct sgx_secs *secs)
+{
+ struct vm_area_struct *vma;
+ struct sgx_pageinfo pginfo;
+ struct sgx_secinfo secinfo;
+ struct sgx_epc_page *secs_epc;
+ long ret;
+
+ secs_epc = sgx_alloc_page(&encl->secs, true);
+ if (IS_ERR(secs_epc)) {
+ ret = PTR_ERR(secs_epc);
+ return ret;
+ }
+
+ sgx_set_page_loaded(&encl->secs, secs_epc);
+ encl->secs.encl = encl;
+ encl->tgid = get_pid(task_tgid(current));
+
+ 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_epc_addr(secs_epc));
+
+ if (ret) {
+ sgx_dbg(encl, "ECREATE returned %ld\n", ret);
+ return ret;
+ }
+
+ if (secs->attributes & SGX_ATTR_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) {
+ if (ret == -EINTR)
+ ret = -ERESTARTSYS;
+ encl->mmu_notifier.ops = NULL;
+ return ret;
+ }
+
+ encl->pm_notifier.notifier_call = &sgx_encl_pm_notifier;
+ ret = register_pm_notifier(&encl->pm_notifier);
+ if (ret) {
+ encl->pm_notifier.notifier_call = NULL;
+ return ret;
+ }
+
+ down_read(&current->mm->mmap_sem);
+ ret = sgx_encl_find(current->mm, secs->base, &vma);
+ if (ret != -ENOENT) {
+ if (!ret)
+ ret = -EINVAL;
+ up_read(&current->mm->mmap_sem);
+ return ret;
+ }
+
+ if (vma->vm_start != secs->base ||
+ vma->vm_end != (secs->base + secs->size) ||
+ vma->vm_pgoff != 0) {
+ ret = -EINVAL;
+ up_read(&current->mm->mmap_sem);
+ return ret;
+ }
+
+ vma->vm_private_data = encl;
+ up_read(&current->mm->mmap_sem);
+ return 0;
+}
+
+static int sgx_validate_secinfo(struct sgx_secinfo *secinfo)
+{
+ u64 page_type = secinfo->flags & SGX_SECINFO_PAGE_TYPE_MASK;
+ u64 perm = secinfo->flags & SGX_SECINFO_PERMISSION_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 < SGX_SECINFO_RESERVED_SIZE; i++)
+ if (secinfo->reserved[i])
+ return -EINVAL;
+
+ return 0;
+}
+
+static bool sgx_validate_offset(struct sgx_encl *encl, unsigned long offset)
+{
+ if (offset & (PAGE_SIZE - 1))
+ return false;
+
+ if (offset >= encl->size)
+ return false;
+
+ return true;
+}
+
+static int sgx_validate_tcs(struct sgx_encl *encl, struct sgx_tcs *tcs)
+{
+ int i;
+
+ if (tcs->flags & SGX_TCS_RESERVED_MASK)
+ return -EINVAL;
+
+ if (tcs->flags & SGX_TCS_DBGOPTIN)
+ return -EINVAL;
+
+ if (!sgx_validate_offset(encl, tcs->ssa_offset))
+ return -EINVAL;
+
+ if (!sgx_validate_offset(encl, tcs->fs_offset))
+ return -EINVAL;
+
+ if (!sgx_validate_offset(encl, tcs->gs_offset))
+ return -EINVAL;
+
+ if ((tcs->fs_limit & 0xFFF) != 0xFFF)
+ return -EINVAL;
+
+ if ((tcs->gs_limit & 0xFFF) != 0xFFF)
+ return -EINVAL;
+
+ for (i = 0; i < SGX_TCS_RESERVED_SIZE; i++)
+ if (tcs->reserved[i])
+ return -EINVAL;
+
+ return 0;
+}
+
+static int __sgx_encl_add_page(struct sgx_encl *encl,
+ struct sgx_encl_page *encl_page,
+ void *data,
+ struct sgx_secinfo *secinfo,
+ unsigned int mrmask)
+{
+ u64 page_type = secinfo->flags & SGX_SECINFO_PAGE_TYPE_MASK;
+ struct sgx_add_page_req *req = NULL;
+ pgoff_t backing_index;
+ struct page *backing;
+ void *backing_ptr;
+ int empty;
+
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ backing_index = sgx_encl_page_backing_index(encl_page, encl);
+ backing = sgx_get_backing(encl->backing, backing_index);
+ if (IS_ERR(backing)) {
+ kfree(req);
+ return PTR_ERR(backing);
+ }
+ backing_ptr = kmap(backing);
+ memcpy(backing_ptr, data, PAGE_SIZE);
+ kunmap(backing);
+ if (page_type == SGX_SECINFO_TCS)
+ encl_page->desc |= SGX_ENCL_PAGE_TCS;
+ memcpy(&req->secinfo, secinfo, sizeof(*secinfo));
+ req->encl = encl;
+ req->encl_page = encl_page;
+ req->mrmask = 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 */);
+ return 0;
+}
+
+/**
+ * sgx_encl_alloc_page - allocate a new enclave page
+ * @encl: an enclave
+ * @addr: page address in the ELRANGE
+ *
+ * Return:
+ * an &sgx_encl_page instance on success,
+ * -errno otherwise
+ */
+struct sgx_encl_page *sgx_encl_alloc_page(struct sgx_encl *encl,
+ unsigned long addr)
+{
+ struct sgx_encl_page *encl_page;
+ int ret;
+
+ if (radix_tree_lookup(&encl->page_tree, PFN_DOWN(addr)))
+ return ERR_PTR(-EEXIST);
+ encl_page = kzalloc(sizeof(*encl_page), GFP_KERNEL);
+ if (!encl_page)
+ return ERR_PTR(-ENOMEM);
+ encl_page->desc = addr;
+ encl_page->encl = encl;
+ ret = radix_tree_insert(&encl->page_tree, PFN_DOWN(encl_page->desc),
+ encl_page);
+ if (ret) {
+ kfree(encl_page);
+ return ERR_PTR(ret);
+ }
+ return encl_page;
+}
+
+/**
+ * sgx_encl_free_page - free an enclave page
+ * @encl_page: an enclave page
+ */
+void sgx_encl_free_page(struct sgx_encl_page *encl_page)
+{
+ radix_tree_delete(&encl_page->encl->page_tree,
+ PFN_DOWN(encl_page->desc));
+ if (encl_page->desc & SGX_ENCL_PAGE_LOADED)
+ sgx_free_page(encl_page->epc_page);
+ kfree(encl_page);
+}
+
+/**
+ * sgx_encl_add_page - add a page to the enclave
+ *
+ * @encl: an enclave
+ * @addr: page address in the ELRANGE
+ * @data: page data
+ * @secinfo: page permissions
+ * @mrmask: bitmask to select the 256 byte chunks to be measured
+ *
+ * Creates a new enclave page and enqueues an EADD operation that will be
+ * processed by a worker thread later on.
+ *
+ * Return:
+ * 0 on success,
+ * -errno otherwise
+ */
+int sgx_encl_add_page(struct sgx_encl *encl, unsigned long addr, void *data,
+ struct sgx_secinfo *secinfo, unsigned int mrmask)
+{
+ u64 page_type = secinfo->flags & SGX_SECINFO_PAGE_TYPE_MASK;
+ struct sgx_encl_page *encl_page;
+ int ret;
+
+ if (sgx_validate_secinfo(secinfo))
+ return -EINVAL;
+ if (page_type == SGX_SECINFO_TCS) {
+ ret = sgx_validate_tcs(encl, data);
+ if (ret)
+ return ret;
+ }
+ mutex_lock(&encl->lock);
+ if (encl->flags & (SGX_ENCL_INITIALIZED | SGX_ENCL_DEAD)) {
+ mutex_unlock(&encl->lock);
+ return -EINVAL;
+ }
+ encl_page = sgx_encl_alloc_page(encl, addr);
+ if (IS_ERR(encl_page)) {
+ mutex_unlock(&encl->lock);
+ return PTR_ERR(encl_page);
+ }
+ ret = __sgx_encl_add_page(encl, encl_page, data, secinfo, mrmask);
+ if (ret)
+ sgx_encl_free_page(encl_page);
+ mutex_unlock(&encl->lock);
+ return ret;
+}
+
+static int __sgx_get_key_hash(struct crypto_shash *tfm, const void *modulus,
+ void *hash)
+{
+ SHASH_DESC_ON_STACK(shash, tfm);
+
+ shash->tfm = tfm;
+ shash->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+
+ return crypto_shash_digest(shash, modulus, SGX_MODULUS_SIZE, hash);
+}
+
+static int sgx_get_key_hash(const void *modulus, void *hash)
+{
+ struct crypto_shash *tfm;
+ int ret;
+
+ tfm = crypto_alloc_shash("sha256", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(tfm))
+ return PTR_ERR(tfm);
+
+ ret = __sgx_get_key_hash(tfm, modulus, hash);
+
+ crypto_free_shash(tfm);
+ return ret;
+}
+
+/**
+ * sgx_encl_init - perform EINIT for the given enclave
+ *
+ * @encl: an enclave
+ * @sigstruct: SIGSTRUCT for the enclave
+ * @token: EINITTOKEN for the enclave
+ *
+ * Retries a few times in order to perform EINIT operation on an enclave
+ * because there could be potentially an interrupt storm.
+ *
+ * Return:
+ * 0 on success,
+ * SGX error code on EINIT failure,
+ * -errno otherwise
+ */
+int sgx_encl_init(struct sgx_encl *encl, struct sgx_sigstruct *sigstruct,
+ struct sgx_einittoken *token)
+{
+ u64 mrsigner[4];
+ int ret;
+ int i;
+ int j;
+
+ ret = sgx_get_key_hash(sigstruct->modulus, mrsigner);
+ if (ret)
+ return ret;
+
+ flush_work(&encl->add_page_work);
+
+ mutex_lock(&encl->lock);
+
+ if (encl->flags & SGX_ENCL_INITIALIZED) {
+ mutex_unlock(&encl->lock);
+ return 0;
+ }
+ if (encl->flags & SGX_ENCL_DEAD) {
+ mutex_unlock(&encl->lock);
+ return -EFAULT;
+ }
+
+ for (i = 0; i < SGX_EINIT_SLEEP_COUNT; i++) {
+ for (j = 0; j < SGX_EINIT_SPIN_COUNT; j++) {
+ ret = sgx_einit(sigstruct, token, encl->secs.epc_page,
+ mrsigner);
+ if (ret == SGX_UNMASKED_EVENT)
+ continue;
+ else
+ break;
+ }
+
+ if (ret != SGX_UNMASKED_EVENT)
+ break;
+
+ msleep_interruptible(SGX_EINIT_SLEEP_TIME);
+ if (signal_pending(current)) {
+ mutex_unlock(&encl->lock);
+ return -ERESTARTSYS;
+ }
+ }
+
+ if (ret > 0)
+ sgx_dbg(encl, "EINIT returned %d\n", ret);
+ else if (!ret)
+ encl->flags |= SGX_ENCL_INITIALIZED;
+ mutex_unlock(&encl->lock);
+
+ return ret;
+}
+
+/**
+ * sgx_encl_block - block an enclave page
+ * @encl_page: an enclave page
+ *
+ * Changes the state of the associated EPC page to blocked.
+ */
+void sgx_encl_block(struct sgx_encl_page *encl_page)
+{
+ unsigned long addr = SGX_ENCL_PAGE_ADDR(encl_page);
+ struct sgx_encl *encl = encl_page->encl;
+ struct vm_area_struct *vma;
+ int ret;
+
+ if (encl->flags & SGX_ENCL_DEAD)
+ return;
+
+ ret = sgx_encl_find(encl->mm, addr, &vma);
+ if (ret || encl != vma->vm_private_data)
+ return;
+
+ zap_vma_ptes(vma, addr, PAGE_SIZE);
+ ret = __eblock(sgx_epc_addr(encl_page->epc_page));
+ SGX_INVD(ret, encl, "EBLOCK returned %d (0x%x)", ret, ret);
+}
+
+/**
+ * sgx_encl_track - start tracking pages in the blocked state
+ * @encl: an enclave
+ *
+ * Start blocking accesses for pages in the blocked state for threads that enter
+ * inside the enclave by executing the ETRACK leaf instruction. This starts a
+ * shootdown sequence for threads that entered before ETRACK.
+ *
+ * The caller must take care (with an IPI when necessary) to make sure that the
+ * previous shootdown sequence was completed before calling this function. If
+ * this is not the case, the callee prints a critical error to the klog and
+ * kills the enclave.
+ */
+void sgx_encl_track(struct sgx_encl *encl)
+{
+ int ret = __etrack(sgx_epc_addr(encl->secs.epc_page));
+
+ SGX_INVD(ret, encl, "ETRACK returned %d\n", ret);
+}
+
+/**
+ * sgx_encl_release - destroy an enclave instance
+ * @kref: address of a kref inside &sgx_encl
+ *
+ * Used together with kref_put(). Frees all the resources associated with the
+ * enclave and the instance itself.
+ */
+void sgx_encl_release(struct kref *ref)
+{
+ struct sgx_encl *encl = container_of(ref, struct sgx_encl, refcount);
+ struct sgx_encl_page *entry;
+ struct radix_tree_iter iter;
+ void **slot;
+
+ if (encl->mmu_notifier.ops)
+ mmu_notifier_unregister_no_release(&encl->mmu_notifier,
+ encl->mm);
+
+ if (encl->pm_notifier.notifier_call)
+ unregister_pm_notifier(&encl->pm_notifier);
+
+ radix_tree_for_each_slot(slot, &encl->page_tree, &iter, 0) {
+ entry = *slot;
+ sgx_encl_free_page(entry);
+ }
+
+ if (encl->tgid)
+ put_pid(encl->tgid);
+
+ if (encl->secs.desc & SGX_ENCL_PAGE_LOADED)
+ sgx_free_page(encl->secs.epc_page);
+
+ if (encl->backing)
+ fput(encl->backing);
+
+ kfree(encl);
+}
diff --git a/drivers/platform/x86/intel_sgx/sgx_ioctl.c b/drivers/platform/x86/intel_sgx/sgx_ioctl.c
new file mode 100644
index 000000000000..4edf1cc956b1
--- /dev/null
+++ b/drivers/platform/x86/intel_sgx/sgx_ioctl.c
@@ -0,0 +1,234 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright(c) 2016-18 Intel Corporation.
+
+#include <asm/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/slab.h>
+#include "sgx.h"
+
+static int sgx_encl_get(unsigned long addr, struct sgx_encl **encl)
+{
+ struct mm_struct *mm = current->mm;
+ struct vm_area_struct *vma;
+ int ret;
+
+ if (addr & (PAGE_SIZE - 1))
+ return -EINVAL;
+
+ down_read(&mm->mmap_sem);
+
+ ret = sgx_encl_find(mm, addr, &vma);
+ if (!ret) {
+ *encl = vma->vm_private_data;
+
+ if ((*encl)->flags & SGX_ENCL_SUSPEND)
+ ret = SGX_POWER_LOST_ENCLAVE;
+ else
+ kref_get(&(*encl)->refcount);
+ }
+
+ up_read(&mm->mmap_sem);
+ return ret;
+}
+
+/**
+ * sgx_ioc_enclave_create - handler for %SGX_IOC_ENCLAVE_CREATE
+ * @filep: open file to /dev/sgx
+ * @cmd: the command value
+ * @arg: pointer to an &sgx_enclave_create instance
+ *
+ * Validates SECS attributes, allocates an EPC page for the SECS and performs
+ * ECREATE.
+ *
+ * Return:
+ * 0 on success,
+ * -errno otherwise
+ */
+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;
+ struct page *secs_page;
+ struct sgx_secs *secs;
+ struct sgx_encl *encl;
+ int ret;
+
+ secs_page = alloc_page(GFP_HIGHUSER);
+ if (!secs_page)
+ return -ENOMEM;
+
+ secs = kmap(secs_page);
+ ret = copy_from_user(secs, (void __user *)createp->src, sizeof(*secs));
+ if (ret)
+ goto out;
+
+ encl = sgx_encl_alloc(secs);
+ if (IS_ERR(encl)) {
+ ret = PTR_ERR(encl);
+ goto out;
+ }
+
+ ret = sgx_encl_create(encl, secs);
+ if (ret)
+ kref_put(&encl->refcount, sgx_encl_release);
+
+out:
+ kunmap(secs_page);
+ __free_page(secs_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 an &sgx_enclave_add_page instance
+ *
+ * Creates a new enclave page and enqueues an EADD operation that will be
+ * processed by a worker thread later on.
+ *
+ * Return:
+ * 0 on success,
+ * -errno otherwise
+ */
+static long sgx_ioc_enclave_add_page(struct file *filep, unsigned int cmd,
+ unsigned long arg)
+{
+ struct sgx_enclave_add_page *addp = (void *)arg;
+ struct sgx_secinfo secinfo;
+ struct sgx_encl *encl;
+ struct page *data_page;
+ void *data;
+ int ret;
+
+ ret = sgx_encl_get(addp->addr, &encl);
+ if (ret)
+ return ret;
+
+ if (copy_from_user(&secinfo, (void __user *)addp->secinfo,
+ sizeof(secinfo))) {
+ kref_put(&encl->refcount, sgx_encl_release);
+ return -EFAULT;
+ }
+
+ data_page = alloc_page(GFP_HIGHUSER);
+ if (!data_page) {
+ kref_put(&encl->refcount, sgx_encl_release);
+ return -ENOMEM;
+ }
+
+ data = kmap(data_page);
+
+ ret = copy_from_user((void *)data, (void __user *)addp->src, PAGE_SIZE);
+ if (ret)
+ goto out;
+
+ ret = sgx_encl_add_page(encl, addp->addr, data, &secinfo, addp->mrmask);
+ if (ret)
+ goto out;
+
+out:
+ kref_put(&encl->refcount, sgx_encl_release);
+ kunmap(data_page);
+ __free_page(data_page);
+ 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 an &sgx_enclave_init instance
+ *
+ * Flushes the remaining enqueued EADD operations and performs EINIT. Does not
+ * allow the EINITTOKENKEY attribute for an enclave.
+ *
+ * Return:
+ * 0 on success,
+ * SGX error code on EINIT failure,
+ * -errno otherwise
+ */
+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;
+ struct sgx_sigstruct *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);
+ memset(einittoken, 0, sizeof(*einittoken));
+
+ ret = copy_from_user(sigstruct, (void __user *)initp->sigstruct,
+ sizeof(*sigstruct));
+ if (ret)
+ goto out;
+ if (sigstruct->attributes & SGX_ATTR_EINITTOKENKEY) {
+ ret = EINVAL;
+ goto out;
+ }
+
+ ret = sgx_encl_get(initp->addr, &encl);
+ if (ret)
+ goto out;
+
+ ret = sgx_encl_init(encl, sigstruct, einittoken);
+
+ kref_put(&encl->refcount, sgx_encl_release);
+
+out:
+ 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;
+ }
+ if (IS_ENCLS_FAULT(ret))
+ return -EFAULT;
+ return ret;
+}
diff --git a/drivers/platform/x86/intel_sgx/sgx_main.c b/drivers/platform/x86/intel_sgx/sgx_main.c
new file mode 100644
index 000000000000..e5cb1c16955d
--- /dev/null
+++ b/drivers/platform/x86/intel_sgx/sgx_main.c
@@ -0,0 +1,267 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright(c) 2016-18 Intel Corporation.
+
+#include <linux/acpi.h>
+#include <linux/cdev.h>
+#include <linux/platform_device.h>
+#include <linux/suspend.h>
+#include <asm/traps.h>
+#include "sgx.h"
+
+MODULE_DESCRIPTION("Intel SGX Driver");
+MODULE_AUTHOR("Jarkko Sakkinen <[email protected]>");
+MODULE_LICENSE("Dual BSD/GPL");
+
+struct workqueue_struct *sgx_add_page_wq;
+u64 sgx_encl_size_max_32;
+u64 sgx_encl_size_max_64;
+u64 sgx_xfrm_mask = 0x3;
+u32 sgx_misc_reserved;
+u32 sgx_xsave_size_tbl[64];
+int sgx_epcm_trapnr;
+
+#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;
+
+ if (len > sgx_encl_size_max_64)
+ return -EINVAL;
+
+ if (len > sgx_encl_size_max_32 && test_thread_flag(TIF_ADDR32))
+ return -EINVAL;
+
+ 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 bus_type sgx_bus_type = {
+ .name = "sgx",
+};
+
+struct sgx_context {
+ struct device dev;
+ struct cdev cdev;
+};
+
+static dev_t sgx_devt;
+
+static void sgx_dev_release(struct device *dev)
+{
+ struct sgx_context *ctx = container_of(dev, struct sgx_context, dev);
+
+ kfree(ctx);
+}
+
+static struct sgx_context *sgx_ctx_alloc(struct device *parent)
+{
+ struct sgx_context *ctx;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return ERR_PTR(-ENOMEM);
+
+ device_initialize(&ctx->dev);
+
+ ctx->dev.bus = &sgx_bus_type;
+ ctx->dev.parent = parent;
+ ctx->dev.devt = MKDEV(MAJOR(sgx_devt), 0);
+ ctx->dev.release = sgx_dev_release;
+
+ dev_set_name(&ctx->dev, "sgx");
+
+ cdev_init(&ctx->cdev, &sgx_fops);
+ ctx->cdev.owner = THIS_MODULE;
+
+ dev_set_drvdata(parent, ctx);
+
+ return ctx;
+}
+
+static struct sgx_context *sgxm_ctx_alloc(struct device *parent)
+{
+ struct sgx_context *ctx;
+ int rc;
+
+ ctx = sgx_ctx_alloc(parent);
+ if (IS_ERR(ctx))
+ return ctx;
+
+ rc = devm_add_action_or_reset(parent, (void (*)(void *))put_device,
+ &ctx->dev);
+ if (rc) {
+ kfree(ctx);
+ return ERR_PTR(rc);
+ }
+
+ return ctx;
+}
+
+static int sgx_dev_init(struct device *parent)
+{
+ struct sgx_context *sgx_dev;
+ unsigned int eax;
+ unsigned int ebx;
+ unsigned int ecx;
+ unsigned int edx;
+ int ret;
+ int i;
+
+ sgx_dev = sgxm_ctx_alloc(parent);
+
+ cpuid_count(SGX_CPUID, 0, &eax, &ebx, &ecx, &edx);
+ /* Only allow misc bits supported by the driver. */
+ sgx_misc_reserved = ~ebx | SGX_MISC_RESERVED_MASK;
+ sgx_encl_size_max_64 = 1ULL << ((edx >> 8) & 0xFF);
+ sgx_encl_size_max_32 = 1ULL << (edx & 0xFF);
+
+ if (boot_cpu_has(X86_FEATURE_OSXSAVE)) {
+ cpuid_count(SGX_CPUID, 1, &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_xsave_size_tbl[i] = eax + ebx;
+ }
+ }
+
+ sgx_epcm_trapnr = boot_cpu_has(X86_FEATURE_SGX2) ? X86_TRAP_PF :
+ X86_TRAP_GP;
+
+ sgx_add_page_wq = alloc_workqueue("intel_sgx-add-page-wq",
+ WQ_UNBOUND | WQ_FREEZABLE, 1);
+ if (!sgx_add_page_wq)
+ return -ENOMEM;
+
+ ret = cdev_device_add(&sgx_dev->cdev, &sgx_dev->dev);
+ if (ret)
+ goto out_workqueue;
+
+ return 0;
+out_workqueue:
+ destroy_workqueue(sgx_add_page_wq);
+ return ret;
+}
+
+static int sgx_drv_probe(struct platform_device *pdev)
+{
+ if (!sgx_enabled)
+ return -ENODEV;
+
+ if (!sgx_lc_enabled) {
+ pr_warn("sgx: IA32_SGXLEPUBKEYHASHx MSRs are not writable\n");
+ return -ENODEV;
+ }
+
+ return sgx_dev_init(&pdev->dev);
+}
+
+static int sgx_drv_remove(struct platform_device *pdev)
+{
+ struct sgx_context *ctx = dev_get_drvdata(&pdev->dev);
+
+ cdev_device_del(&ctx->cdev, &ctx->dev);
+ destroy_workqueue(sgx_add_page_wq);
+
+ 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",
+ .acpi_match_table = ACPI_PTR(sgx_device_ids),
+ },
+};
+
+static int __init sgx_drv_subsys_init(void)
+{
+ int ret;
+
+ ret = bus_register(&sgx_bus_type);
+ if (ret)
+ return ret;
+
+ ret = alloc_chrdev_region(&sgx_devt, 0, 1, "sgx");
+ if (ret < 0) {
+ bus_unregister(&sgx_bus_type);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void sgx_drv_subsys_exit(void)
+{
+ bus_unregister(&sgx_bus_type);
+ unregister_chrdev_region(sgx_devt, 1);
+}
+
+static int __init sgx_drv_init(void)
+{
+ int ret;
+
+ ret = sgx_drv_subsys_init();
+ if (ret)
+ return ret;
+
+ ret = platform_driver_register(&sgx_drv);
+ if (ret)
+ sgx_drv_subsys_exit();
+
+ return ret;
+}
+module_init(sgx_drv_init);
+
+static void __exit sgx_drv_exit(void)
+{
+ platform_driver_unregister(&sgx_drv);
+ sgx_drv_subsys_exit();
+}
+module_exit(sgx_drv_exit);
diff --git a/drivers/platform/x86/intel_sgx/sgx_util.c b/drivers/platform/x86/intel_sgx/sgx_util.c
new file mode 100644
index 000000000000..cbea4c0e794b
--- /dev/null
+++ b/drivers/platform/x86/intel_sgx/sgx_util.c
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright(c) 2016-18 Intel Corporation.
+
+#include <linux/mm.h>
+#include <linux/shmem_fs.h>
+#include "sgx.h"
+
+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 page to be tested for recent access
+ *
+ * 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)
+{
+ unsigned long addr = SGX_ENCL_PAGE_ADDR(page);
+ struct sgx_encl *encl = page->encl;
+ struct vm_area_struct *vma;
+ int ret;
+
+ ret = sgx_encl_find(encl->mm, addr, &vma);
+ if (ret)
+ return 0;
+
+ if (encl != vma->vm_private_data)
+ return 0;
+
+ return apply_to_page_range(vma->vm_mm, addr, PAGE_SIZE,
+ sgx_test_and_clear_young_cb, vma->vm_mm);
+}
+
+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_set_page_loaded - associate an EPC page with an enclave page
+ * @encl_page: an enclave page
+ * @epc_page: the EPC page to attach to @encl_page
+ */
+void sgx_set_page_loaded(struct sgx_encl_page *encl_page,
+ struct sgx_epc_page *epc_page)
+{
+ encl_page->desc |= SGX_ENCL_PAGE_LOADED;
+ encl_page->epc_page = epc_page;
+}
+
+struct page *sgx_get_backing(struct file *file, pgoff_t index)
+{
+ struct inode *inode = file->f_path.dentry->d_inode;
+ struct address_space *mapping = inode->i_mapping;
+ gfp_t gfpmask = mapping_gfp_mask(mapping);
+
+ 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);
+}
diff --git a/drivers/platform/x86/intel_sgx/sgx_vma.c b/drivers/platform/x86/intel_sgx/sgx_vma.c
new file mode 100644
index 000000000000..17e95a0c734c
--- /dev/null
+++ b/drivers/platform/x86/intel_sgx/sgx_vma.c
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright(c) 2016-18 Intel Corporation.
+
+#include <asm/mman.h>
+#include <linux/delay.h>
+#include <linux/file.h>
+#include <linux/hashtable.h>
+#include <linux/highmem.h>
+#include <linux/mm.h>
+#include <linux/ratelimit.h>
+#include <linux/slab.h>
+#include "sgx.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);
+ sgx_invalidate(encl, true);
+ mutex_unlock(&encl->lock);
+ kref_put(&encl->refcount, sgx_encl_release);
+}
+
+const struct vm_operations_struct sgx_vm_ops = {
+ .close = sgx_vma_close,
+ .open = sgx_vma_open,
+};
--
2.17.1


2018-09-25 13:14:52

by Jarkko Sakkinen

[permalink] [raw]
Subject: [PATCH v14 16/19] platform/x86: Add swapping functionality to the Intel SGX driver

Add functions to swap EPC pages to the driver:

* sgx_encl_page_get() pins and sgx_encl_page_put() unpins an enclave
page.
* sgx_encl_page_reclaim() tries to mark page as being reclaimed. The
page is considered reclaimable if it hasn't been accessed recently and
it isn't reserved by the driver for other use.
* sgx_encl_page_block() and sgx_encl_page_track() implement EBLOCK and
ETRACK operations.
* sgx_encl_page_write() writes the page to the regular memory with EWB.

This commit also contains the #PF handler for loading the swapped pages
back.

Signed-off-by: Jarkko Sakkinen <[email protected]>
Co-developed-by: Sean Christopherson <[email protected]>
Co-developed-by: Serge Ayoun <[email protected]>
Co-developed-by: Shay Katz-zamir <[email protected]>
Signed-off-by: Sean Christopherson <[email protected]>
Signed-off-by: Serge Ayoun <[email protected]>
Signed-off-by: Shay Katz-zamir <[email protected]>
---
drivers/platform/x86/intel_sgx/Makefile | 2 +
drivers/platform/x86/intel_sgx/sgx.h | 32 ++++
drivers/platform/x86/intel_sgx/sgx_encl.c | 129 ++++++++++++-
.../platform/x86/intel_sgx/sgx_encl_page.c | 177 ++++++++++++++++++
drivers/platform/x86/intel_sgx/sgx_fault.c | 107 +++++++++++
drivers/platform/x86/intel_sgx/sgx_util.c | 71 +++++++
drivers/platform/x86/intel_sgx/sgx_vma.c | 15 ++
7 files changed, 532 insertions(+), 1 deletion(-)
create mode 100644 drivers/platform/x86/intel_sgx/sgx_encl_page.c
create mode 100644 drivers/platform/x86/intel_sgx/sgx_fault.c

diff --git a/drivers/platform/x86/intel_sgx/Makefile b/drivers/platform/x86/intel_sgx/Makefile
index 117e97effeff..5023c5ebd57d 100644
--- a/drivers/platform/x86/intel_sgx/Makefile
+++ b/drivers/platform/x86/intel_sgx/Makefile
@@ -6,6 +6,8 @@ obj-$(CONFIG_INTEL_SGX) += intel_sgx.o

intel_sgx-$(CONFIG_INTEL_SGX) += \
sgx_encl.o \
+ sgx_encl_page.o \
+ sgx_fault.o \
sgx_ioctl.o \
sgx_main.o \
sgx_util.o \
diff --git a/drivers/platform/x86/intel_sgx/sgx.h b/drivers/platform/x86/intel_sgx/sgx.h
index 67bd8ea1d53d..f707701e422d 100644
--- a/drivers/platform/x86/intel_sgx/sgx.h
+++ b/drivers/platform/x86/intel_sgx/sgx.h
@@ -36,16 +36,32 @@
#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;
+};
+
/**
* enum sgx_encl_page_desc - defines bits for an enclave page's descriptor
* %SGX_ENCL_PAGE_TCS: The page is a TCS page.
* %SGX_ENCL_PAGE_LOADED: The page is not swapped.
+ * %SGX_ENCL_PAGE_RESERVED: The page cannot be reclaimed.
+ * %SGX_ENCL_PAGE_RECLAIMED: The page is in the process of being
+ * reclaimed.
+ * %SGX_ENCL_PAGE_VA_OFFSET_MASK: Holds the offset in the Version Array
+ * (VA) page for a swapped page.
* %SGX_ENCL_PAGE_ADDR_MASK: Holds the virtual address of the page.
*/
enum sgx_encl_page_desc {
SGX_ENCL_PAGE_TCS = BIT(0),
SGX_ENCL_PAGE_LOADED = BIT(1),
/* Bits 11:3 are available when the page is not swapped. */
+ SGX_ENCL_PAGE_RESERVED = BIT(3),
+ SGX_ENCL_PAGE_RECLAIMED = BIT(4),
+ SGX_ENCL_PAGE_VA_OFFSET_MASK = GENMASK_ULL(11, 3),
SGX_ENCL_PAGE_ADDR_MASK = PAGE_MASK,
};

@@ -79,10 +95,12 @@ struct sgx_encl {
struct mutex lock;
struct mm_struct *mm;
struct file *backing;
+ struct file *pcmd;
struct kref refcount;
unsigned long base;
unsigned long size;
unsigned long ssaframesize;
+ struct list_head va_pages;
struct radix_tree_root page_tree;
struct list_head add_page_reqs;
struct work_struct add_page_work;
@@ -102,6 +120,16 @@ static inline pgoff_t sgx_encl_page_backing_index(struct sgx_encl_page *page,
return PFN_DOWN(page->desc - encl->base);
}

+static inline int sgx_encl_page_pcmd_offset(struct sgx_encl_page *page,
+ struct sgx_encl *encl)
+{
+ int index;
+
+ index = sgx_encl_page_backing_index(page, encl);
+ return (index & (PAGE_SIZE / sizeof(struct sgx_pcmd) - 1)) *
+ sizeof(struct sgx_pcmd);
+}
+
extern struct workqueue_struct *sgx_add_page_wq;
extern u64 sgx_encl_size_max_32;
extern u64 sgx_encl_size_max_64;
@@ -174,6 +202,10 @@ int sgx_test_and_clear_young(struct sgx_encl_page *page);
void sgx_flush_cpus(struct sgx_encl *encl);
void sgx_set_page_loaded(struct sgx_encl_page *encl_page,
struct sgx_epc_page *epc_page);
+struct sgx_epc_page *sgx_alloc_va_page(void);
+unsigned int sgx_alloc_va_slot(struct sgx_va_page *va_page);
+void sgx_free_va_slot(struct sgx_va_page *va_page, unsigned int offset);
+bool sgx_va_page_full(struct sgx_va_page *va_page);
struct page *sgx_get_backing(struct file *file, pgoff_t index);
void sgx_put_backing(struct page *backing_page, bool write);

diff --git a/drivers/platform/x86/intel_sgx/sgx_encl.c b/drivers/platform/x86/intel_sgx/sgx_encl.c
index b9ecdc20d67e..f3306bd58978 100644
--- a/drivers/platform/x86/intel_sgx/sgx_encl.c
+++ b/drivers/platform/x86/intel_sgx/sgx_encl.c
@@ -92,7 +92,8 @@ void sgx_invalidate(struct sgx_encl *encl, bool flush_cpus)
* need to check that and let the swapper thread to free the
* page if this is the case.
*/
- if (entry->desc & SGX_ENCL_PAGE_LOADED) {
+ if (entry->desc & SGX_ENCL_PAGE_LOADED &&
+ !(entry->desc & SGX_ENCL_PAGE_RECLAIMED)) {
if (!__sgx_free_page(entry->epc_page))
entry->desc &= ~SGX_ENCL_PAGE_LOADED;
}
@@ -301,6 +302,41 @@ static const struct mmu_notifier_ops sgx_mmu_notifier_ops = {
.release = sgx_mmu_notifier_release,
};

+static int sgx_encl_grow(struct sgx_encl *encl)
+{
+ struct sgx_va_page *va_page;
+ int ret;
+
+ BUILD_BUG_ON(SGX_VA_SLOT_COUNT !=
+ (SGX_ENCL_PAGE_VA_OFFSET_MASK >> 3) + 1);
+
+ mutex_lock(&encl->lock);
+ if (!(encl->page_cnt % SGX_VA_SLOT_COUNT)) {
+ mutex_unlock(&encl->lock);
+
+ va_page = kzalloc(sizeof(*va_page), GFP_KERNEL);
+ if (!va_page)
+ return -ENOMEM;
+ va_page->epc_page = sgx_alloc_va_page();
+ if (IS_ERR(va_page->epc_page)) {
+ ret = PTR_ERR(va_page->epc_page);
+ kfree(va_page);
+ return ret;
+ }
+
+ mutex_lock(&encl->lock);
+ if (encl->page_cnt % SGX_VA_SLOT_COUNT) {
+ sgx_free_page(va_page->epc_page);
+ kfree(va_page);
+ } else {
+ list_add(&va_page->list, &encl->va_pages);
+ }
+ }
+ encl->page_cnt++;
+ mutex_unlock(&encl->lock);
+ return 0;
+}
+
/**
* sgx_encl_alloc - allocate memory for an enclave and set attributes
*
@@ -319,6 +355,7 @@ struct sgx_encl *sgx_encl_alloc(struct sgx_secs *secs)
unsigned long ssaframesize;
struct sgx_encl *encl;
struct file *backing;
+ struct file *pcmd;

ssaframesize = sgx_calc_ssaframesize(secs->miscselect, secs->xfrm);
if (sgx_validate_secs(secs, ssaframesize))
@@ -329,9 +366,17 @@ struct sgx_encl *sgx_encl_alloc(struct sgx_secs *secs)
if (IS_ERR(backing))
return ERR_CAST(backing);

+ pcmd = shmem_file_setup("[dev/sgx]", (secs->size + PAGE_SIZE) >> 5,
+ VM_NORESERVE);
+ if (IS_ERR(pcmd)) {
+ fput(backing);
+ return ERR_CAST(pcmd);
+ }
+
encl = kzalloc(sizeof(*encl), GFP_KERNEL);
if (!encl) {
fput(backing);
+ fput(pcmd);
return ERR_PTR(-ENOMEM);
}

@@ -340,6 +385,7 @@ struct sgx_encl *sgx_encl_alloc(struct sgx_secs *secs)

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);
mutex_init(&encl->lock);
INIT_WORK(&encl->add_page_work, sgx_add_page_worker);
@@ -349,6 +395,7 @@ struct sgx_encl *sgx_encl_alloc(struct sgx_secs *secs)
encl->size = secs->size;
encl->ssaframesize = secs->ssa_frame_size;
encl->backing = backing;
+ encl->pcmd = pcmd;

return encl;
}
@@ -400,6 +447,10 @@ int sgx_encl_create(struct sgx_encl *encl, struct sgx_secs *secs)
encl->secs.encl = encl;
encl->tgid = get_pid(task_tgid(current));

+ ret = sgx_encl_grow(encl);
+ if (ret)
+ return ret;
+
pginfo.addr = 0;
pginfo.contents = (unsigned long)secs;
pginfo.metadata = (unsigned long)&secinfo;
@@ -630,6 +681,9 @@ int sgx_encl_add_page(struct sgx_encl *encl, unsigned long addr, void *data,
if (ret)
return ret;
}
+ ret = sgx_encl_grow(encl);
+ if (ret)
+ return ret;
mutex_lock(&encl->lock);
if (encl->flags & (SGX_ENCL_INITIALIZED | SGX_ENCL_DEAD)) {
mutex_unlock(&encl->lock);
@@ -787,6 +841,67 @@ void sgx_encl_track(struct sgx_encl *encl)
SGX_INVD(ret, encl, "ETRACK returned %d\n", ret);
}

+/**
+ * sgx_encl_load_page - load an enclave page
+ * @encl_page: an enclave page
+ * @epc_page: an EPC page
+ *
+ * Loads an enclave page from the regular memory to the EPC. The pages, which
+ * are not children of a SECS (eg SECS itself and VA pages) should set their
+ * address to zero.
+ *
+ * Return:
+ * 0 on success,
+ * -errno or SGX error code on failure
+ */
+int sgx_encl_load_page(struct sgx_encl_page *encl_page,
+ struct sgx_epc_page *epc_page)
+{
+ unsigned long addr = SGX_ENCL_PAGE_ADDR(encl_page);
+ struct sgx_encl *encl = encl_page->encl;
+ struct sgx_pageinfo pginfo;
+ unsigned long pcmd_offset;
+ unsigned long va_offset;
+ pgoff_t backing_index;
+ struct page *backing;
+ struct page *pcmd;
+ int ret;
+
+ backing_index = sgx_encl_page_backing_index(encl_page, encl);
+ pcmd_offset = sgx_encl_page_pcmd_offset(encl_page, encl);
+ va_offset = SGX_ENCL_PAGE_VA_OFFSET(encl_page);
+
+ backing = sgx_get_backing(encl->backing, backing_index);
+ if (IS_ERR(backing))
+ return PTR_ERR(backing);
+
+ pcmd = sgx_get_backing(encl->pcmd, backing_index >> 5);
+ if (IS_ERR(pcmd)) {
+ sgx_put_backing(backing, false);
+ return PTR_ERR(pcmd);
+ }
+
+ pginfo.addr = addr;
+ pginfo.contents = (unsigned long)kmap_atomic(backing);
+ pginfo.metadata = (unsigned long)kmap_atomic(pcmd) + pcmd_offset;
+ pginfo.secs = addr ? (unsigned long)sgx_epc_addr(encl->secs.epc_page) :
+ 0;
+
+ ret = __eldu(&pginfo, sgx_epc_addr(epc_page),
+ sgx_epc_addr(encl_page->va_page->epc_page) + va_offset);
+ if (ret) {
+ sgx_err(encl, "ELDU returned %d\n", ret);
+ ret = encls_to_err(ret);
+ }
+
+ kunmap_atomic((void *)(unsigned long)(pginfo.metadata - pcmd_offset));
+ kunmap_atomic((void *)(unsigned long)pginfo.contents);
+
+ sgx_put_backing(pcmd, false);
+ sgx_put_backing(backing, false);
+ return ret;
+}
+
/**
* sgx_encl_release - destroy an enclave instance
* @kref: address of a kref inside &sgx_encl
@@ -799,6 +914,7 @@ void sgx_encl_release(struct kref *ref)
struct sgx_encl *encl = container_of(ref, struct sgx_encl, refcount);
struct sgx_encl_page *entry;
struct radix_tree_iter iter;
+ struct sgx_va_page *va_page;
void **slot;

if (encl->mmu_notifier.ops)
@@ -816,11 +932,22 @@ void sgx_encl_release(struct kref *ref)
if (encl->tgid)
put_pid(encl->tgid);

+ 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);
+ kfree(va_page);
+ }
+
if (encl->secs.desc & SGX_ENCL_PAGE_LOADED)
sgx_free_page(encl->secs.epc_page);

if (encl->backing)
fput(encl->backing);

+ if (encl->pcmd)
+ fput(encl->pcmd);
+
kfree(encl);
}
diff --git a/drivers/platform/x86/intel_sgx/sgx_encl_page.c b/drivers/platform/x86/intel_sgx/sgx_encl_page.c
new file mode 100644
index 000000000000..7318a1ffeffd
--- /dev/null
+++ b/drivers/platform/x86/intel_sgx/sgx_encl_page.c
@@ -0,0 +1,177 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright(c) 2016-18 Intel Corporation.
+
+#include <linux/device.h>
+#include <linux/freezer.h>
+#include <linux/highmem.h>
+#include <linux/kthread.h>
+#include <linux/ratelimit.h>
+#include <linux/sched/signal.h>
+#include <linux/slab.h>
+#include "sgx.h"
+
+static inline struct sgx_encl_page *to_encl_page(struct sgx_epc_page *epc_page)
+{
+ return (struct sgx_encl_page *)epc_page->owner;
+}
+
+bool sgx_encl_page_get(struct sgx_epc_page *epc_page)
+{
+ struct sgx_encl_page *encl_page = to_encl_page(epc_page);
+ struct sgx_encl *encl = encl_page->encl;
+
+ return kref_get_unless_zero(&encl->refcount) != 0;
+}
+
+void sgx_encl_page_put(struct sgx_epc_page *epc_page)
+{
+ struct sgx_encl_page *encl_page = to_encl_page(epc_page);
+ struct sgx_encl *encl = encl_page->encl;
+
+ kref_put(&encl->refcount, sgx_encl_release);
+}
+
+bool sgx_encl_page_reclaim(struct sgx_epc_page *epc_page)
+{
+ struct sgx_encl_page *encl_page = to_encl_page(epc_page);
+ struct sgx_encl *encl = encl_page->encl;
+ bool ret;
+
+ down_read(&encl->mm->mmap_sem);
+ mutex_lock(&encl->lock);
+
+ if (encl->flags & SGX_ENCL_DEAD)
+ ret = true;
+ else if (encl_page->desc & SGX_ENCL_PAGE_RESERVED)
+ ret = false;
+ else
+ ret = !sgx_test_and_clear_young(encl_page);
+ if (ret)
+ encl_page->desc |= SGX_ENCL_PAGE_RECLAIMED;
+
+ mutex_unlock(&encl->lock);
+ up_read(&encl->mm->mmap_sem);
+
+ return ret;
+}
+
+void sgx_encl_page_block(struct sgx_epc_page *epc_page)
+{
+ struct sgx_encl_page *encl_page = to_encl_page(epc_page);
+ struct sgx_encl *encl = encl_page->encl;
+
+ down_read(&encl->mm->mmap_sem);
+ mutex_lock(&encl->lock);
+ sgx_encl_block(encl_page);
+ mutex_unlock(&encl->lock);
+ up_read(&encl->mm->mmap_sem);
+}
+
+static int sgx_ewb(struct sgx_encl *encl, struct sgx_epc_page *epc_page,
+ struct sgx_va_page *va_page, unsigned int va_offset)
+{
+ struct sgx_encl_page *encl_page = to_encl_page(epc_page);
+ int pcmd_offset = sgx_encl_page_pcmd_offset(encl_page, encl);
+ struct sgx_pageinfo pginfo;
+ pgoff_t backing_index;
+ struct page *backing;
+ struct page *pcmd;
+ int ret;
+
+ backing_index = sgx_encl_page_backing_index(encl_page, encl);
+
+ backing = sgx_get_backing(encl->backing, backing_index);
+ if (IS_ERR(backing)) {
+ ret = PTR_ERR(backing);
+ return ret;
+ }
+
+ pcmd = sgx_get_backing(encl->pcmd, backing_index >> 5);
+ if (IS_ERR(pcmd)) {
+ ret = PTR_ERR(pcmd);
+ sgx_put_backing(backing, true);
+ return ret;
+ }
+
+ pginfo.addr = 0;
+ pginfo.contents = (unsigned long)kmap_atomic(backing);
+ pginfo.metadata = (unsigned long)kmap_atomic(pcmd) + pcmd_offset;
+ pginfo.secs = 0;
+ ret = __ewb(&pginfo, sgx_epc_addr(epc_page),
+ sgx_epc_addr(va_page->epc_page) + va_offset);
+ kunmap_atomic((void *)(unsigned long)(pginfo.metadata - pcmd_offset));
+ kunmap_atomic((void *)(unsigned long)pginfo.contents);
+
+ sgx_put_backing(pcmd, true);
+ sgx_put_backing(backing, true);
+
+ return ret;
+}
+
+/**
+ * sgx_write_page - write a page to the regular memory
+ *
+ * Writes an EPC page to the shmem file associated with the enclave. Flushes
+ * CPUs and retries if there are hardware threads that can potentially have TLB
+ * entries to the page (indicated by SGX_NOT_TRACKED). Clears the reserved flag
+ * after the page is swapped.
+ *
+ * @epc_page: an EPC page
+ */
+static void sgx_write_page(struct sgx_epc_page *epc_page, bool do_free)
+{
+ struct sgx_encl_page *encl_page = to_encl_page(epc_page);
+ struct sgx_encl *encl = encl_page->encl;
+ struct sgx_va_page *va_page;
+ unsigned int va_offset;
+ int ret;
+
+ encl_page->desc &= ~(SGX_ENCL_PAGE_LOADED | SGX_ENCL_PAGE_RECLAIMED);
+
+ if (!(encl->flags & SGX_ENCL_DEAD)) {
+ va_page = list_first_entry(&encl->va_pages, struct sgx_va_page,
+ list);
+ va_offset = sgx_alloc_va_slot(va_page);
+ if (sgx_va_page_full(va_page))
+ list_move_tail(&va_page->list, &encl->va_pages);
+
+ ret = sgx_ewb(encl, epc_page, va_page, va_offset);
+ if (ret == SGX_NOT_TRACKED) {
+ sgx_encl_track(encl);
+ ret = sgx_ewb(encl, epc_page, va_page, va_offset);
+ if (ret == SGX_NOT_TRACKED) {
+ /* slow path, IPI needed */
+ sgx_flush_cpus(encl);
+ ret = sgx_ewb(encl, epc_page, va_page,
+ va_offset);
+ }
+ }
+ SGX_INVD(ret, encl, "EWB returned %d\n", ret);
+
+ SGX_INVD(encl_page->desc & SGX_ENCL_PAGE_VA_OFFSET_MASK, encl,
+ "Flags set in VA offset area: %lx", encl_page->desc);
+ encl_page->desc |= va_offset;
+ encl_page->va_page = va_page;
+ } else if (!do_free) {
+ ret = __eremove(sgx_epc_addr(epc_page));
+ WARN(ret, "EREMOVE returned %d\n", ret);
+ }
+
+ if (do_free)
+ sgx_free_page(epc_page);
+}
+
+void sgx_encl_page_write(struct sgx_epc_page *epc_page)
+{
+ struct sgx_encl_page *encl_page = to_encl_page(epc_page);
+ struct sgx_encl *encl = encl_page->encl;
+
+ down_read(&encl->mm->mmap_sem);
+ mutex_lock(&encl->lock);
+ sgx_write_page(epc_page, false);
+ encl->secs_child_cnt--;
+ if (!encl->secs_child_cnt && (encl->flags & SGX_ENCL_INITIALIZED))
+ sgx_write_page(encl->secs.epc_page, true);
+ mutex_unlock(&encl->lock);
+ up_read(&encl->mm->mmap_sem);
+}
diff --git a/drivers/platform/x86/intel_sgx/sgx_fault.c b/drivers/platform/x86/intel_sgx/sgx_fault.c
new file mode 100644
index 000000000000..2f459329f29c
--- /dev/null
+++ b/drivers/platform/x86/intel_sgx/sgx_fault.c
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright(c) 2016-18 Intel Corporation.
+
+#include <linux/highmem.h>
+#include <linux/sched/mm.h>
+#include "sgx.h"
+
+static struct sgx_epc_page *__sgx_load_faulted_page(
+ struct sgx_encl_page *encl_page)
+{
+ unsigned long va_offset = SGX_ENCL_PAGE_VA_OFFSET(encl_page);
+ struct sgx_encl *encl = encl_page->encl;
+ struct sgx_epc_page *epc_page;
+ int ret;
+
+ epc_page = sgx_alloc_page(encl_page, false);
+ if (IS_ERR(epc_page))
+ return epc_page;
+ ret = sgx_encl_load_page(encl_page, epc_page);
+ if (ret) {
+ sgx_free_page(epc_page);
+ return ERR_PTR(ret);
+ }
+ sgx_free_va_slot(encl_page->va_page, va_offset);
+ list_move(&encl_page->va_page->list, &encl->va_pages);
+ encl_page->desc &= ~SGX_ENCL_PAGE_VA_OFFSET_MASK;
+ sgx_set_page_loaded(encl_page, epc_page);
+ return epc_page;
+}
+
+static struct sgx_encl_page *__sgx_fault_page(struct vm_area_struct *vma,
+ unsigned long addr,
+ bool do_reserve)
+{
+ struct sgx_encl *encl = vma->vm_private_data;
+ struct sgx_epc_page *epc_page;
+ struct sgx_encl_page *entry;
+ int rc = 0;
+
+ if ((encl->flags & SGX_ENCL_DEAD) ||
+ !(encl->flags & SGX_ENCL_INITIALIZED))
+ return ERR_PTR(-EFAULT);
+
+ entry = radix_tree_lookup(&encl->page_tree, addr >> PAGE_SHIFT);
+ if (!entry)
+ return ERR_PTR(-EFAULT);
+
+ /* Page is already resident in the EPC. */
+ if (entry->desc & SGX_ENCL_PAGE_LOADED) {
+ if (entry->desc & SGX_ENCL_PAGE_RESERVED) {
+ sgx_dbg(encl, "EPC page 0x%p is already reserved\n",
+ (void *)SGX_ENCL_PAGE_ADDR(entry));
+ return ERR_PTR(-EBUSY);
+ }
+ if (entry->desc & SGX_ENCL_PAGE_RECLAIMED) {
+ sgx_dbg(encl, "EPC page 0x%p is being reclaimed\n",
+ (void *)SGX_ENCL_PAGE_ADDR(entry));
+ return ERR_PTR(-EBUSY);
+ }
+ if (do_reserve)
+ entry->desc |= SGX_ENCL_PAGE_RESERVED;
+ return entry;
+ }
+
+ if (!(encl->secs.desc & SGX_ENCL_PAGE_LOADED)) {
+ epc_page = __sgx_load_faulted_page(&encl->secs);
+ if (IS_ERR(epc_page))
+ return ERR_CAST(epc_page);
+ }
+ epc_page = __sgx_load_faulted_page(entry);
+ if (IS_ERR(epc_page))
+ return ERR_CAST(epc_page);
+
+ encl->secs_child_cnt++;
+ sgx_test_and_clear_young(entry);
+ if (do_reserve)
+ entry->desc |= SGX_ENCL_PAGE_RESERVED;
+
+ rc = vm_insert_pfn(vma, addr, PFN_DOWN(entry->epc_page->desc));
+ SGX_INVD(rc, encl, "%s: vm_insert_pfn() returned %d\n", __func__, rc);
+ if (rc)
+ return ERR_PTR(rc);
+
+ return entry;
+}
+
+struct sgx_encl_page *sgx_fault_page(struct vm_area_struct *vma,
+ unsigned long addr, bool do_reserve)
+{
+ struct sgx_encl *encl = vma->vm_private_data;
+ struct sgx_encl_page *entry;
+
+ /* If process was forked, VMA is still there but vm_private_data is set
+ * to NULL.
+ */
+ if (!encl)
+ return ERR_PTR(-EFAULT);
+ do {
+ mutex_lock(&encl->lock);
+ entry = __sgx_fault_page(vma, addr, do_reserve);
+ mutex_unlock(&encl->lock);
+ if (!do_reserve)
+ break;
+ } while (PTR_ERR(entry) == -EBUSY);
+
+ return entry;
+}
diff --git a/drivers/platform/x86/intel_sgx/sgx_util.c b/drivers/platform/x86/intel_sgx/sgx_util.c
index cbea4c0e794b..ddd50449db46 100644
--- a/drivers/platform/x86/intel_sgx/sgx_util.c
+++ b/drivers/platform/x86/intel_sgx/sgx_util.c
@@ -67,6 +67,77 @@ void sgx_set_page_loaded(struct sgx_encl_page *encl_page,
encl_page->epc_page = epc_page;
}

+/**
+ * sgx_alloc_page - allocate a VA page
+ *
+ * Allocates an &sgx_epc_page instance and converts it to a VA page.
+ *
+ * Return:
+ * a &struct sgx_va_page instance,
+ * -errno otherwise
+ */
+struct sgx_epc_page *sgx_alloc_va_page(void)
+{
+ struct sgx_epc_page *epc_page;
+ int ret;
+
+ epc_page = sgx_alloc_page(NULL, true);
+ if (IS_ERR(epc_page))
+ return ERR_CAST(epc_page);
+
+ ret = __epa(sgx_epc_addr(epc_page));
+ if (ret) {
+ pr_crit("sgx: EPA failed\n");
+ sgx_free_page(epc_page);
+ return ERR_PTR(ret);
+ }
+
+ return epc_page;
+}
+
+/**
+ * sgx_alloc_va_slot - allocate a VA slot
+ * @va_page: a &struct sgx_va_page instance
+ *
+ * Allocates a slot from a &struct sgx_va_page instance.
+ *
+ * Return: offset of the slot inside the VA page
+ */
+unsigned int sgx_alloc_va_slot(struct sgx_va_page *va_page)
+{
+ int slot = find_first_zero_bit(va_page->slots, SGX_VA_SLOT_COUNT);
+
+ if (slot < SGX_VA_SLOT_COUNT)
+ set_bit(slot, va_page->slots);
+
+ return slot << 3;
+}
+
+/**
+ * sgx_free_va_slot - free a VA slot
+ * @va_page: a &struct sgx_va_page instance
+ * @offset: offset of the slot inside the VA page
+ *
+ * Frees a slot from a &struct sgx_va_page instance.
+ */
+void sgx_free_va_slot(struct sgx_va_page *va_page, unsigned int offset)
+{
+ clear_bit(offset >> 3, va_page->slots);
+}
+
+/**
+ * sgx_va_page_full - is the VA page full?
+ * @va_page: a &struct sgx_va_page instance
+ *
+ * Return: true if all slots have been taken
+ */
+bool sgx_va_page_full(struct sgx_va_page *va_page)
+{
+ int slot = find_first_zero_bit(va_page->slots, SGX_VA_SLOT_COUNT);
+
+ return slot == SGX_VA_SLOT_COUNT;
+}
+
struct page *sgx_get_backing(struct file *file, pgoff_t index)
{
struct inode *inode = file->f_path.dentry->d_inode;
diff --git a/drivers/platform/x86/intel_sgx/sgx_vma.c b/drivers/platform/x86/intel_sgx/sgx_vma.c
index 17e95a0c734c..cc0993b4fd40 100644
--- a/drivers/platform/x86/intel_sgx/sgx_vma.c
+++ b/drivers/platform/x86/intel_sgx/sgx_vma.c
@@ -37,7 +37,22 @@ static void sgx_vma_close(struct vm_area_struct *vma)
kref_put(&encl->refcount, sgx_encl_release);
}

+static int sgx_vma_fault(struct vm_fault *vmf)
+{
+ unsigned long addr = (unsigned long)vmf->address;
+ struct vm_area_struct *vma = vmf->vma;
+ 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;
+}
+
const struct vm_operations_struct sgx_vm_ops = {
.close = sgx_vma_close,
.open = sgx_vma_open,
+ .fault = sgx_vma_fault,
};
--
2.17.1


2018-09-25 13:15:01

by Jarkko Sakkinen

[permalink] [raw]
Subject: [PATCH v14 18/19] platform/x86: ptrace() support for the SGX driver

Add VMA callbacks for ptrace() that can be used with debug enclaves.
With debug enclaves data can be read and write the memory word at a time
by using ENCLS(EDBGRD) and ENCLS(EDBGWR) leaf instructions.

Signed-off-by: Jarkko Sakkinen <[email protected]>
---
drivers/platform/x86/intel_sgx/sgx_vma.c | 109 +++++++++++++++++++++++
1 file changed, 109 insertions(+)

diff --git a/drivers/platform/x86/intel_sgx/sgx_vma.c b/drivers/platform/x86/intel_sgx/sgx_vma.c
index cc0993b4fd40..df604e4d0d0a 100644
--- a/drivers/platform/x86/intel_sgx/sgx_vma.c
+++ b/drivers/platform/x86/intel_sgx/sgx_vma.c
@@ -51,8 +51,117 @@ static int sgx_vma_fault(struct vm_fault *vmf)
return VM_FAULT_SIGBUS;
}

+static int sgx_edbgrd(struct sgx_encl *encl, struct sgx_encl_page *page,
+ unsigned long addr, void *data)
+{
+ unsigned long offset;
+ int ret;
+
+ offset = addr & ~PAGE_MASK;
+
+ if ((page->desc & SGX_ENCL_PAGE_TCS) &&
+ offset > offsetof(struct sgx_tcs, gs_limit))
+ return -ECANCELED;
+
+ ret = __edbgrd(sgx_epc_addr(page->epc_page) + offset, data);
+ if (ret) {
+ sgx_dbg(encl, "EDBGRD returned %d\n", ret);
+ return encls_to_err(ret);
+ }
+
+ return 0;
+}
+
+static int sgx_edbgwr(struct sgx_encl *encl, struct sgx_encl_page *page,
+ unsigned long addr, void *data)
+{
+ unsigned long offset;
+ int ret;
+
+ offset = addr & ~PAGE_MASK;
+
+ /* Writing anything else than flags will cause #GP */
+ if ((page->desc & SGX_ENCL_PAGE_TCS) &&
+ offset != offsetof(struct sgx_tcs, flags))
+ return -ECANCELED;
+
+ ret = __edbgwr(sgx_epc_addr(page->epc_page) + offset, data);
+ if (ret) {
+ sgx_dbg(encl, "EDBGWR returned %d\n", ret);
+ return encls_to_err(ret);
+ }
+
+ return 0;
+}
+
+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;
+ unsigned long align;
+ char data[sizeof(unsigned long)];
+ int offset;
+ int cnt;
+ 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;
+
+ for (i = 0; i < len; i += cnt) {
+ if (!entry || !((addr + i) & (PAGE_SIZE - 1))) {
+ if (entry)
+ entry->desc &= ~SGX_ENCL_PAGE_RESERVED;
+
+ entry = sgx_fault_page(vma, (addr + i) & PAGE_MASK,
+ true);
+ if (IS_ERR(entry)) {
+ ret = PTR_ERR(entry);
+ entry = NULL;
+ break;
+ }
+ }
+
+ /* Locking is not needed because only immutable fields of the
+ * page are accessed and page itself is reserved so that it
+ * cannot be swapped out in the middle.
+ */
+
+ align = ALIGN_DOWN(addr + i, sizeof(unsigned long));
+ offset = (addr + i) & (sizeof(unsigned long) - 1);
+ cnt = sizeof(unsigned long) - offset;
+ cnt = min(cnt, len - i);
+
+ ret = sgx_edbgrd(encl, entry, align, data);
+ if (ret)
+ break;
+ if (write) {
+ memcpy(data + offset, buf + i, cnt);
+ ret = sgx_edbgwr(encl, entry, align, data);
+ if (ret)
+ break;
+ } else
+ memcpy(buf + i, data + offset, cnt);
+ }
+
+ if (entry)
+ entry->desc &= ~SGX_ENCL_PAGE_RESERVED;
+
+ return ret < 0 ? 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,
};
--
2.17.1


2018-09-25 13:15:11

by Jarkko Sakkinen

[permalink] [raw]
Subject: [PATCH v14 19/19] x86/sgx: Driver documentation

Documentation of the features of the Software Guard eXtensions used
by the Linux kernel and basic design choices for the core and driver
and functionality.

Signed-off-by: Jarkko Sakkinen <[email protected]>
---
Documentation/index.rst | 1 +
Documentation/x86/intel_sgx.rst | 185 ++++++++++++++++++++++++++++++++
2 files changed, 186 insertions(+)
create mode 100644 Documentation/x86/intel_sgx.rst

diff --git a/Documentation/index.rst b/Documentation/index.rst
index 5db7e87c7cb1..1cdc139adb40 100644
--- a/Documentation/index.rst
+++ b/Documentation/index.rst
@@ -104,6 +104,7 @@ implementation.
:maxdepth: 2

sh/index
+ x86/index

Filesystem Documentation
------------------------
diff --git a/Documentation/x86/intel_sgx.rst b/Documentation/x86/intel_sgx.rst
new file mode 100644
index 000000000000..f6b7979c41f2
--- /dev/null
+++ b/Documentation/x86/intel_sgx.rst
@@ -0,0 +1,185 @@
+===================
+Intel(R) SGX driver
+===================
+
+Introduction
+============
+
+Intel(R) SGX is a set of CPU instructions that can be used by applications to
+set aside private regions of code and data. The code outside the enclave is
+disallowed to access the memory inside the enclave by the CPU access control.
+In a way you can think that SGX provides inverted sandbox. It protects the
+application from a malicious host.
+
+You can tell if your CPU supports SGX by looking into ``/proc/cpuinfo``:
+
+ ``cat /proc/cpuinfo | grep sgx``
+
+Overview of SGX
+===============
+
+SGX has a set of data structures to maintain information about the enclaves and
+their security properties. BIOS reserves a fixed size region of physical memory
+for these structures by setting Processor Reserved Memory Range Registers
+(PRMRR).
+
+This memory range is protected from outside access by the CPU and all the data
+coming in and out of the CPU package is encrypted by a key that is generated for
+each boot cycle.
+
+Enclaves execute in ring-3 in a special enclave submode using pages from the
+reserved memory range. A fixed logical address range for the enclave is reserved
+by ENCLS(ECREATE), a leaf instruction used to create enclaves. It is referred in
+the documentation commonly as the ELRANGE.
+
+Every memory access to the ELRANGE is asserted by the CPU. If the CPU is not
+executing in the enclave mode inside the enclave, #GP is raised. On the other
+hand enclave code can make memory accesses both inside and outside of the
+ELRANGE.
+
+Enclave can only execute code inside the ELRANGE. Instructions that may cause
+VMEXIT, IO instructions and instructions that require a privilege change are
+prohibited inside the enclave. Interrupts and exceptions always cause enclave
+to exit and jump to an address outside the enclave given when the enclave is
+entered by using the leaf instruction ENCLS(EENTER).
+
+Data types
+----------
+
+The protected memory range contains the following data:
+
+* **Enclave Page Cache (EPC):** protected pages
+* **Enclave Page Cache Map (EPCM):** a database that describes the state of the
+ pages and link them to an enclave.
+
+EPC has a number of different types of pages:
+
+* **SGX Enclave Control Structure (SECS)**: describes the global
+ properties of an enclave.
+* **Regular (REG):** code and data pages in the ELRANGE.
+* **Thread Control Structure (TCS):** pages that define entry points inside an
+ enclave. The enclave can only be entered through these entry points and each
+ can host a single hardware thread at a time.
+* **Version Array (VA)**: 64-bit version numbers for pages that have been
+ swapped outside the enclave. Each page contains 512 version numbers.
+
+Launch control
+--------------
+
+To launch an enclave, two structures must be provided for ENCLS(EINIT):
+
+1. **SIGSTRUCT:** signed measurement of the enclave binary.
+2. **EINITTOKEN:** a cryptographic token CMAC-signed with a AES256-key called
+ *launch key*, which is re-generated for each boot cycle.
+
+The CPU holds a SHA256 hash of a 3072-bit RSA public key inside
+IA32_SGXLEPUBKEYHASHn MSRs. Enclaves with a SIGSTRUCT that is signed with this
+key do not require a valid EINITTOKEN and can be authorized with special
+privileges. One of those privileges is ability to acquire the launch key with
+ENCLS(EGETKEY).
+
+**IA32_FEATURE_CONTROL[17]** is used by the BIOS configure whether
+IA32_SGXLEPUBKEYHASH MSRs are read-only or read-write before locking the
+feature control register and handing over control to the operating system.
+
+Enclave construction
+--------------------
+
+The construction is started by filling out the SECS that contains enclave
+address range, privileged attributes and measurement of TCS and REG pages (pages
+that will be mapped to the address range) among the other things. This structure
+is passed out to the ENCLS(ECREATE) together with a physical address of a page
+in EPC that will hold the SECS.
+
+The pages are added with ENCLS(EADD) and measured with ENCLS(EEXTEND) i.e.
+SHA256 hash MRENCLAVE residing in the SECS is extended with the page data.
+
+After all of the pages have been added, the enclave is initialized with
+ENCLS(EINIT). ENCLS(INIT) checks that the SIGSTRUCT is signed with the contained
+public key. If the given EINITTOKEN has the valid bit set, the CPU checks that
+the token is valid (CMAC'd with the launch key). If the token is not valid,
+the CPU will check whether the enclave is signed with a key matching to the
+IA32_SGXLEPUBKEYHASHn MSRs.
+
+Swapping pages
+--------------
+
+Enclave pages can be swapped out with ENCLS(EWB) to the unprotected memory. In
+addition to the EPC page, ENCLS(EWB) takes in a VA page and address for PCMD
+structure (Page Crypto MetaData) as input. The VA page will seal a version
+number for the page. PCMD is 128 byte structure that contains tracking
+information for the page, most importantly its MAC. With these structures the
+enclave is sealed and rollback protected while it resides in the unprotected
+memory.
+
+Before the page can be swapped out it must not have any active TLB references.
+ENCLS(EBLOCK) instruction moves a page to the *blocked* state, which means
+that no new TLB entries can be created to it by the hardware threads.
+
+After this a shootdown sequence is started with ENCLS(ETRACK), which sets an
+increased counter value to the entering hardware threads. ENCLS(EWB) will
+return SGX_NOT_TRACKED error while there are still threads with the earlier
+couner value because that means that there might be hardware thread inside
+the enclave with TLB entries to pages that are to be swapped.
+
+Kernel internals
+================
+
+Requirements
+------------
+
+Because SGX has an ever evolving and expanding feature set, it's possible for
+a BIOS or VMM to configure a system in such a way that not all CPUs are equal,
+e.g. where Launch Control is only enabled on a subset of CPUs. Linux does
+*not* support such a heterogeneous system configuration, nor does it even
+attempt to play nice in the face of a misconfigured system. With the exception
+of Launch Control's hash MSRs, which can vary per CPU, Linux assumes that all
+CPUs have a configuration that is identical to the boot CPU.
+
+
+Roles and responsibilities
+--------------------------
+
+SGX introduces system resources, e.g. EPC memory, that must be accessible to
+multiple entities, e.g. the native kernel driver (to expose SGX to userspace)
+and KVM (to expose SGX to VMs), ideally without introducing any dependencies
+between each SGX entity. To that end, the kernel owns and manages the shared
+system resources, i.e. the EPC and Launch Control MSRs, and defines functions
+that provide appropriate access to the shared resources. SGX support for
+user space and VMs is left to the SGX platform driver and KVM respectively.
+
+Launching enclaves
+------------------
+
+The current kernel implementation supports only unlocked MSRs i.e.
+FEATURE_CONTROL_SGX_LE_WR must be set. The launch is performed by setting the
+MSRs to the hash of the public key modulus of the enclave signer, which is one
+f the fields in the SIGSTRUCT.
+
+EPC management
+--------------
+
+Due to the unique requirements for swapping EPC pages, and because EPC pages
+(currently) do not have associated page structures, management of the EPC is
+not handled by the standard Linux swapper. SGX directly handles swapping
+of EPC pages, including a kthread to initiate reclaim and a rudimentary LRU
+mechanism. The consumers of EPC pages, e.g. the SGX driver, are required to
+implement function callbacks that can be invoked by the kernel to age,
+swap, and/or forcefully reclaim a target EPC page. In effect, the kernel
+controls what happens and when, while the consumers (driver, KVM, etc..) do
+the actual work.
+
+SGX uapi
+========
+
+.. kernel-doc:: drivers/platform/x86/intel_sgx/sgx_ioctl.c
+ :functions: sgx_ioc_enclave_create
+ sgx_ioc_enclave_add_page
+ sgx_ioc_enclave_init
+
+.. kernel-doc:: arch/x86/include/uapi/asm/sgx.h
+
+References
+==========
+
+* System Programming Manual: 39.1.4 Intel® SGX Launch Control Configuration
--
2.17.1


2018-09-25 13:15:20

by Jarkko Sakkinen

[permalink] [raw]
Subject: [PATCH v14 17/19] x86/sgx: Add a simple swapper for the EPC memory manager

The swapper thread ksgxswapd reclaims pages on the event when the number
of free EPC pages goes below %SGX_NR_LOW_PAGES up until it reaches
%SGX_NR_HIGH_PAGES.

Pages are reclaimed in LRU fashion from a global list. The consumers
take care of calling EBLOCK (block page from new accesses), ETRACK
(restart counting the entering hardware threads) and EWB (write page to
the regular memory) because executing these operations usually (if not
always) requires to do some subsystem-internal locking operations.

Signed-off-by: Jarkko Sakkinen <[email protected]>
Co-developed-by: Sean Christopherson <[email protected]>
Co-developed-by: Serge Ayoun <[email protected]>
Co-developed-by: Shay Katz-zamir <[email protected]>
Signed-off-by: Sean Christopherson <[email protected]>
Signed-off-by: Serge Ayoun <[email protected]>
Signed-off-by: Shay Katz-zamir <[email protected]>
---
arch/x86/Kconfig | 1 +
arch/x86/include/asm/sgx.h | 7 +
arch/x86/kernel/cpu/intel_sgx.c | 226 ++++++++++++++++++++-
drivers/platform/x86/intel_sgx/sgx_encl.c | 3 +-
drivers/platform/x86/intel_sgx/sgx_fault.c | 1 +
5 files changed, 226 insertions(+), 12 deletions(-)

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index b47e1a144409..5c329dfd0fd9 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1916,6 +1916,7 @@ config X86_INTEL_MEMORY_PROTECTION_KEYS
config INTEL_SGX_CORE
bool "Intel SGX core functionality"
depends on X86_64 && CPU_SUP_INTEL
+ select INTEL_SGX
help
Intel Software Guard eXtensions (SGX) CPU feature that allows ring 3
applications to create enclaves: private regions of memory that are
diff --git a/arch/x86/include/asm/sgx.h b/arch/x86/include/asm/sgx.h
index d5c535db094c..6bc4f3a38119 100644
--- a/arch/x86/include/asm/sgx.h
+++ b/arch/x86/include/asm/sgx.h
@@ -315,5 +315,12 @@ int __sgx_free_page(struct sgx_epc_page *page);
void sgx_free_page(struct sgx_epc_page *page);
int sgx_einit(struct sgx_sigstruct *sigstruct, struct sgx_einittoken *token,
struct sgx_epc_page *secs, u64 *lepubkeyhash);
+void sgx_page_reclaimable(struct sgx_epc_page *page);
+
+bool sgx_encl_page_get(struct sgx_epc_page *epc_page);
+void sgx_encl_page_put(struct sgx_epc_page *epc_page);
+bool sgx_encl_page_reclaim(struct sgx_epc_page *epc_page);
+void sgx_encl_page_block(struct sgx_epc_page *epc_page);
+void sgx_encl_page_write(struct sgx_epc_page *epc_page);

#endif /* _ASM_X86_SGX_H */
diff --git a/arch/x86/kernel/cpu/intel_sgx.c b/arch/x86/kernel/cpu/intel_sgx.c
index e36572f320c7..365e0f1aa378 100644
--- a/arch/x86/kernel/cpu/intel_sgx.c
+++ b/arch/x86/kernel/cpu/intel_sgx.c
@@ -10,6 +10,20 @@
#include <linux/slab.h>
#include <asm/sgx.h>

+/**
+ * enum sgx_swap_constants - the constants used by the swapping code
+ * %SGX_NR_TO_SCAN: the number of pages to scan in a single round
+ * %SGX_NR_LOW_PAGES: the low watermark for ksgxswapd when it starts to swap
+ * pages.
+ * %SGX_NR_HIGH_PAGES: the high watermark for ksgxswapd what it stops swapping
+ * pages.
+ */
+enum sgx_swap_constants {
+ SGX_NR_TO_SCAN = 16,
+ SGX_NR_LOW_PAGES = 32,
+ SGX_NR_HIGH_PAGES = 64,
+};
+
bool sgx_enabled __ro_after_init;
EXPORT_SYMBOL_GPL(sgx_enabled);
bool sgx_lc_enabled __ro_after_init;
@@ -18,6 +32,10 @@ struct sgx_epc_section sgx_epc_sections[SGX_MAX_EPC_SECTIONS];
EXPORT_SYMBOL_GPL(sgx_epc_sections);

static int sgx_nr_epc_sections;
+static LIST_HEAD(sgx_active_page_list);
+static DEFINE_SPINLOCK(sgx_active_page_list_lock);
+static struct task_struct *ksgxswapd_tsk;
+static DECLARE_WAIT_QUEUE_HEAD(ksgxswapd_waitq);

/* The cache for the last known values of IA32_SGXLEPUBKEYHASHx MSRs for each
* CPU. The entries are initialized when they are first used by sgx_einit().
@@ -25,19 +43,113 @@ static int sgx_nr_epc_sections;
static DEFINE_PER_CPU(u64 [4], sgx_lepubkeyhash_cache);

/**
- * sgx_alloc_page - Allocate an EPC page
- * @owner: the owner of the EPC page
- * @reclaim: wait and reclaim pages up until we get one
+ * sgx_reclaim_pages - reclaim EPC pages from the consumers
*
- * Try to grab a page from the free EPC page list. If there is a free page
- * available, it is returned to the caller. The @reclaim parameter hints
- * the EPC memory manager to swap pages when required.
- *
- * Return:
- * a pointer to a &struct sgx_epc_page instace,
- * -errno on error
+ * Takes a fixed chunk of pages from the global list of consumed EPC pages and
+ * tries to swap them. Only the pages that are either being freed by the
+ * consumer or actively used are skipped.
*/
-struct sgx_epc_page *sgx_alloc_page(void *owner, bool reclaim)
+static void sgx_reclaim_pages(void)
+{
+ struct sgx_epc_page *chunk[SGX_NR_TO_SCAN + 1];
+ struct sgx_epc_page *epc_page;
+ struct sgx_epc_section *section;
+ int i, j;
+
+ spin_lock(&sgx_active_page_list_lock);
+ for (i = 0, j = 0; i < SGX_NR_TO_SCAN; i++) {
+ if (list_empty(&sgx_active_page_list))
+ break;
+
+ epc_page = list_first_entry(&sgx_active_page_list,
+ struct sgx_epc_page, list);
+ list_del_init(&epc_page->list);
+
+ if (sgx_encl_page_get(epc_page))
+ chunk[j++] = epc_page;
+ else
+ /* The owner is freeing the page. No need to add the
+ * page back to the list of reclaimable pages.
+ */
+ epc_page->desc &= ~SGX_EPC_PAGE_RECLAIMABLE;
+ }
+ spin_unlock(&sgx_active_page_list_lock);
+
+ for (i = 0; i < j; i++) {
+ epc_page = chunk[i];
+ if (sgx_encl_page_reclaim(epc_page))
+ continue;
+
+ spin_lock(&sgx_active_page_list_lock);
+ list_add_tail(&epc_page->list, &sgx_active_page_list);
+ spin_unlock(&sgx_active_page_list_lock);
+
+ sgx_encl_page_put(epc_page);
+ chunk[i] = NULL;
+ }
+
+ for (i = 0; i < j; i++) {
+ epc_page = chunk[i];
+ if (epc_page)
+ sgx_encl_page_block(epc_page);
+ }
+
+ for (i = 0; i < j; i++) {
+ epc_page = chunk[i];
+ if (epc_page) {
+ sgx_encl_page_write(epc_page);
+ sgx_encl_page_put(epc_page);
+ epc_page->desc &= ~SGX_EPC_PAGE_RECLAIMABLE;
+
+ section = sgx_epc_section(epc_page);
+ spin_lock(&section->lock);
+ section->pages[section->free_cnt++] = epc_page;
+ spin_unlock(&section->lock);
+ }
+ }
+}
+
+static unsigned long sgx_calc_free_cnt(void)
+{
+ struct sgx_epc_section *section;
+ unsigned long free_cnt = 0;
+ int i;
+
+ for (i = 0; i < sgx_nr_epc_sections; i++) {
+ section = &sgx_epc_sections[i];
+ free_cnt += section->free_cnt;
+ }
+
+ return free_cnt;
+}
+
+static inline bool sgx_should_reclaim(void)
+{
+ return sgx_calc_free_cnt() < SGX_NR_HIGH_PAGES &&
+ !list_empty(&sgx_active_page_list);
+}
+
+static int ksgxswapd(void *p)
+{
+ set_freezable();
+
+ while (!kthread_should_stop()) {
+ if (try_to_freeze())
+ continue;
+
+ wait_event_freezable(ksgxswapd_waitq, kthread_should_stop() ||
+ sgx_should_reclaim());
+
+ if (sgx_should_reclaim())
+ sgx_reclaim_pages();
+
+ cond_resched();
+ }
+
+ return 0;
+}
+
+static struct sgx_epc_page *sgx_try_alloc_page(void *owner)
{
struct sgx_epc_section *section;
struct sgx_epc_page *page;
@@ -60,6 +172,51 @@ struct sgx_epc_page *sgx_alloc_page(void *owner, bool reclaim)

return NULL;
}
+
+/**
+ * sgx_alloc_page - Allocate an EPC page
+ * @owner: the owner of the EPC page
+ * @reclaim: wait and reclaim pages up until we get one
+ *
+ * Try to grab a page from the free EPC page list. If there is a free page
+ * available, it is returned to the caller. The @reclaim parameter hints
+ * the EPC memory manager to swap pages when required.
+ *
+ * Return:
+ * a pointer to a &struct sgx_epc_page instace,
+ * -errno on error
+ */
+struct sgx_epc_page *sgx_alloc_page(void *owner, bool reclaim)
+{
+ struct sgx_epc_page *entry;
+
+ for ( ; ; ) {
+ entry = sgx_try_alloc_page(owner);
+ if (entry)
+ break;
+
+ if (list_empty(&sgx_active_page_list))
+ return ERR_PTR(-ENOMEM);
+
+ if (!reclaim) {
+ entry = ERR_PTR(-EBUSY);
+ break;
+ }
+
+ if (signal_pending(current)) {
+ entry = ERR_PTR(-ERESTARTSYS);
+ break;
+ }
+
+ sgx_reclaim_pages();
+ schedule();
+ }
+
+ if (sgx_calc_free_cnt() < SGX_NR_LOW_PAGES)
+ wake_up(&ksgxswapd_waitq);
+
+ return entry;
+}
EXPORT_SYMBOL_GPL(sgx_alloc_page);

/**
@@ -78,6 +235,23 @@ int __sgx_free_page(struct sgx_epc_page *page)
struct sgx_epc_section *section = sgx_epc_section(page);
int ret;

+ /*
+ * Remove the page from the active list if necessary. If the page
+ * is actively being reclaimed, i.e. RECLAIMABLE is set but the
+ * page isn't on the active list, return -EBUSY as we can't free
+ * the page at this time since it is "owned" by the reclaimer.
+ */
+ spin_lock(&sgx_active_page_list_lock);
+ if (page->desc & SGX_EPC_PAGE_RECLAIMABLE) {
+ if (list_empty(&page->list)) {
+ spin_unlock(&sgx_active_page_list_lock);
+ return -EBUSY;
+ }
+ list_del(&page->list);
+ page->desc &= ~SGX_EPC_PAGE_RECLAIMABLE;
+ }
+ spin_unlock(&sgx_active_page_list_lock);
+
ret = __eremove(sgx_epc_addr(page));
if (ret)
return ret;
@@ -158,6 +332,23 @@ int sgx_einit(struct sgx_sigstruct *sigstruct, struct sgx_einittoken *token,
}
EXPORT_SYMBOL(sgx_einit);

+/**
+ * sgx_page_reclaimable - mark a page as reclaimable
+ *
+ * @page: EPC page
+ *
+ * Mark a page as reclaimable and add it to the active page list. Pages
+ * are automatically removed from the active list when freed.
+ */
+void sgx_page_reclaimable(struct sgx_epc_page *page)
+{
+ spin_lock(&sgx_active_page_list_lock);
+ page->desc |= SGX_EPC_PAGE_RECLAIMABLE;
+ list_add_tail(&page->list, &sgx_active_page_list);
+ spin_unlock(&sgx_active_page_list_lock);
+}
+EXPORT_SYMBOL_GPL(sgx_page_reclaimable);
+
static __init void sgx_free_epc_section(struct sgx_epc_section *section)
{
int i;
@@ -206,6 +397,11 @@ static __init void sgx_page_cache_teardown(void)
{
int i;

+ if (ksgxswapd_tsk) {
+ kthread_stop(ksgxswapd_tsk);
+ ksgxswapd_tsk = NULL;
+ }
+
for (i = 0; i < sgx_nr_epc_sections; i++)
sgx_free_epc_section(&sgx_epc_sections[i]);
}
@@ -266,6 +462,7 @@ static __init int sgx_page_cache_init(void)

static __init int sgx_init(void)
{
+ struct task_struct *tsk;
unsigned long fc;
int ret;

@@ -290,6 +487,13 @@ static __init int sgx_init(void)
if (ret)
return ret;

+ tsk = kthread_run(ksgxswapd, NULL, "ksgxswapd");
+ if (IS_ERR(tsk)) {
+ sgx_page_cache_teardown();
+ return PTR_ERR(tsk);
+ }
+ ksgxswapd_tsk = tsk;
+
sgx_enabled = true;
sgx_lc_enabled = !!(fc & FEATURE_CONTROL_SGX_LE_WR);
return 0;
diff --git a/drivers/platform/x86/intel_sgx/sgx_encl.c b/drivers/platform/x86/intel_sgx/sgx_encl.c
index f3306bd58978..d49eac05a88b 100644
--- a/drivers/platform/x86/intel_sgx/sgx_encl.c
+++ b/drivers/platform/x86/intel_sgx/sgx_encl.c
@@ -171,6 +171,7 @@ static bool sgx_process_add_page_req(struct sgx_add_page_req *req,
encl->secs_child_cnt++;
sgx_set_page_loaded(encl_page, epc_page);
sgx_test_and_clear_young(encl_page);
+ sgx_page_reclaimable(encl_page->epc_page);
return true;
}

@@ -890,7 +891,7 @@ int sgx_encl_load_page(struct sgx_encl_page *encl_page,
ret = __eldu(&pginfo, sgx_epc_addr(epc_page),
sgx_epc_addr(encl_page->va_page->epc_page) + va_offset);
if (ret) {
- sgx_err(encl, "ELDU returned %d\n", ret);
+ SGX_INVD(ret, encl, "ELDU returned %d (0x%x)", ret, ret);
ret = encls_to_err(ret);
}

diff --git a/drivers/platform/x86/intel_sgx/sgx_fault.c b/drivers/platform/x86/intel_sgx/sgx_fault.c
index 2f459329f29c..32d9e67d7556 100644
--- a/drivers/platform/x86/intel_sgx/sgx_fault.c
+++ b/drivers/platform/x86/intel_sgx/sgx_fault.c
@@ -73,6 +73,7 @@ static struct sgx_encl_page *__sgx_fault_page(struct vm_area_struct *vma,

encl->secs_child_cnt++;
sgx_test_and_clear_young(entry);
+ sgx_page_reclaimable(entry->epc_page);
if (do_reserve)
entry->desc |= SGX_ENCL_PAGE_RESERVED;

--
2.17.1


2018-09-25 13:15:50

by Jarkko Sakkinen

[permalink] [raw]
Subject: [PATCH v14 06/19] x86/mm: x86/sgx: Add new 'PF_SGX' page fault error code bit

From: Sean Christopherson <[email protected]>

The SGX bit is set in the #PF error code if and only if the fault is
detected by the Enclave Page Cache Map (EPCM), a hardware-managed
table that enforces the paging permissions defined by the enclave,
e.g. to prevent the kernel from changing the permissions of an
enclave's page(s).

Signed-off-by: Sean Christopherson <[email protected]>
Cc: Dave Hansen <[email protected]>
Signed-off-by: Jarkko Sakkinen <[email protected]>
---
arch/x86/include/asm/traps.h | 1 +
1 file changed, 1 insertion(+)

diff --git a/arch/x86/include/asm/traps.h b/arch/x86/include/asm/traps.h
index 3de69330e6c5..165c93dd700e 100644
--- a/arch/x86/include/asm/traps.h
+++ b/arch/x86/include/asm/traps.h
@@ -162,5 +162,6 @@ enum x86_pf_error_code {
X86_PF_RSVD = 1 << 3,
X86_PF_INSTR = 1 << 4,
X86_PF_PK = 1 << 5,
+ X86_PF_SGX = 1 << 15,
};
#endif /* _ASM_X86_TRAPS_H */
--
2.17.1


2018-09-25 13:16:07

by Jarkko Sakkinen

[permalink] [raw]
Subject: [PATCH v14 14/19] x86/sgx: Add sgx_einit() for initializing enclaves

From: Sean Christopherson <[email protected]>

Add a function to perform ENCLS(EINIT), which initializes an enclave,
which can be used by a driver for running enclaves and VMMs.

Writing the LE hash MSRs is extraordinarily expensive, e.g. 3-4x slower
than normal MSRs, so we use a per-cpu cache to track the last known value
of the MSRs to avoid unnecessarily writing the MSRs with the current value.

Signed-off-by: Sean Christopherson <[email protected]>
Co-developed-by: Jarkko Sakkinen <[email protected]>
Signed-off-by: Jarkko Sakkinen <[email protected]>
---
arch/x86/include/asm/sgx.h | 2 ++
arch/x86/kernel/cpu/intel_sgx.c | 53 +++++++++++++++++++++++++++++++++
2 files changed, 55 insertions(+)

diff --git a/arch/x86/include/asm/sgx.h b/arch/x86/include/asm/sgx.h
index 6ea5a6dbd36d..d5c535db094c 100644
--- a/arch/x86/include/asm/sgx.h
+++ b/arch/x86/include/asm/sgx.h
@@ -313,5 +313,7 @@ static inline int __emodt(struct sgx_secinfo *secinfo, void __iomem *addr)
struct sgx_epc_page *sgx_alloc_page(void *owner, bool reclaim);
int __sgx_free_page(struct sgx_epc_page *page);
void sgx_free_page(struct sgx_epc_page *page);
+int sgx_einit(struct sgx_sigstruct *sigstruct, struct sgx_einittoken *token,
+ struct sgx_epc_page *secs, u64 *lepubkeyhash);

#endif /* _ASM_X86_SGX_H */
diff --git a/arch/x86/kernel/cpu/intel_sgx.c b/arch/x86/kernel/cpu/intel_sgx.c
index ef30d6730859..e36572f320c7 100644
--- a/arch/x86/kernel/cpu/intel_sgx.c
+++ b/arch/x86/kernel/cpu/intel_sgx.c
@@ -19,6 +19,11 @@ EXPORT_SYMBOL_GPL(sgx_epc_sections);

static int sgx_nr_epc_sections;

+/* The cache for the last known values of IA32_SGXLEPUBKEYHASHx MSRs for each
+ * CPU. The entries are initialized when they are first used by sgx_einit().
+ */
+static DEFINE_PER_CPU(u64 [4], sgx_lepubkeyhash_cache);
+
/**
* sgx_alloc_page - Allocate an EPC page
* @owner: the owner of the EPC page
@@ -104,6 +109,54 @@ void sgx_free_page(struct sgx_epc_page *page)
}
EXPORT_SYMBOL_GPL(sgx_free_page);

+static void sgx_update_lepubkeyhash_msrs(u64 *lepubkeyhash, bool enforce)
+{
+ u64 *cache;
+ int i;
+
+ cache = per_cpu(sgx_lepubkeyhash_cache, smp_processor_id());
+ for (i = 0; i < 4; i++) {
+ if (enforce || (lepubkeyhash[i] != cache[i])) {
+ wrmsrl(MSR_IA32_SGXLEPUBKEYHASH0 + i, lepubkeyhash[i]);
+ cache[i] = lepubkeyhash[i];
+ }
+ }
+}
+
+/**
+ * sgx_einit - initialize an enclave
+ * @sigstruct: a pointer a SIGSTRUCT
+ * @token: a pointer an EINITTOKEN (optional)
+ * @secs: a pointer a SECS
+ * @lepubkeyhash: the desired value for IA32_SGXLEPUBKEYHASHx MSRs
+ *
+ * Try to perform EINIT operation. If the MSRs are writable, they are updated
+ * according to @lepubkeyhash.
+ *
+ * Return:
+ * 0 on success,
+ * -errno or SGX error on failure
+ */
+int sgx_einit(struct sgx_sigstruct *sigstruct, struct sgx_einittoken *token,
+ struct sgx_epc_page *secs, u64 *lepubkeyhash)
+{
+ int ret;
+
+ if (!sgx_lc_enabled)
+ return __einit(sigstruct, token, sgx_epc_addr(secs));
+
+ preempt_disable();
+ sgx_update_lepubkeyhash_msrs(lepubkeyhash, false);
+ ret = __einit(sigstruct, token, sgx_epc_addr(secs));
+ /* A legit case because MSRs are reset after waking up. */
+ if (ret == SGX_INVALID_EINITTOKEN) {
+ sgx_update_lepubkeyhash_msrs(lepubkeyhash, true);
+ ret = __einit(sigstruct, token, sgx_epc_addr(secs));
+ }
+ preempt_enable();
+ return ret;
+}
+EXPORT_SYMBOL(sgx_einit);

static __init void sgx_free_epc_section(struct sgx_epc_section *section)
{
--
2.17.1


2018-09-25 13:16:48

by Jarkko Sakkinen

[permalink] [raw]
Subject: [PATCH v14 07/19] x86/fault: x86/mm/pkeys: relocate stale comment regarding OSPKE

From: Sean Christopherson <[email protected]>

The code being referenced was moved to a separate helper function
but the comment was left behind.

Fixes: 33a709b25a76 ("mm/gup, x86/mm/pkeys: Check VMAs and PTEs for protection keys")
Cc: Dave Hansen <[email protected]>
Signed-off-by: Sean Christopherson <[email protected]>
Signed-off-by: Jarkko Sakkinen <[email protected]>
---
arch/x86/mm/fault.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
index 47bebfe6efa7..85d20516b2f3 100644
--- a/arch/x86/mm/fault.c
+++ b/arch/x86/mm/fault.c
@@ -940,6 +940,11 @@ static inline bool bad_area_access_from_pkeys(unsigned long error_code,
/* This code is always called on the current mm */
bool foreign = false;

+ /*
+ * This OSPKE check is not strictly necessary at runtime.
+ * But, doing it this way allows compiler optimizations
+ * if pkeys are compiled out.
+ */
if (!boot_cpu_has(X86_FEATURE_OSPKE))
return false;
if (error_code & X86_PF_PK)
@@ -955,11 +960,6 @@ static noinline void
bad_area_access_error(struct pt_regs *regs, unsigned long error_code,
unsigned long address, struct vm_area_struct *vma)
{
- /*
- * This OSPKE check is not strictly necessary at runtime.
- * But, doing it this way allows compiler optimizations
- * if pkeys are compiled out.
- */
if (bad_area_access_from_pkeys(error_code, vma))
__bad_area(regs, error_code, address, vma, SEGV_PKUERR);
else
--
2.17.1


2018-09-25 13:27:46

by Jonathan Corbet

[permalink] [raw]
Subject: Re: [PATCH v14 19/19] x86/sgx: Driver documentation

On Tue, 25 Sep 2018 16:06:56 +0300
Jarkko Sakkinen <[email protected]> wrote:

> Documentation of the features of the Software Guard eXtensions used
> by the Linux kernel and basic design choices for the core and driver
> and functionality.
>
> Signed-off-by: Jarkko Sakkinen <[email protected]>
> ---
> Documentation/index.rst | 1 +
> Documentation/x86/intel_sgx.rst | 185 ++++++++++++++++++++++++++++++++
> 2 files changed, 186 insertions(+)
> create mode 100644 Documentation/x86/intel_sgx.rst
>
> diff --git a/Documentation/index.rst b/Documentation/index.rst
> index 5db7e87c7cb1..1cdc139adb40 100644
> --- a/Documentation/index.rst
> +++ b/Documentation/index.rst
> @@ -104,6 +104,7 @@ implementation.
> :maxdepth: 2
>
> sh/index
> + x86/index

So you're adding this reference to x86/index, but the actual file
(Documentation/x86/index.rst) doesn't exist; that will break the docs
build. I do appreciate your doing your document in RST, though!

Who is the intended audience for this document? I see a bit of UAPI
stuff that might be better placed in the userspace-api manual, mixed with
stuff that user-space developers are unlikely to take much interest in.
Might it be worth splitting this into two pieces?

Thanks,

jon

2018-09-25 16:49:47

by Borislav Petkov

[permalink] [raw]
Subject: Re: [PATCH v14 04/19] x86/cpufeatures: Add SGX feature bits

On Tue, Sep 25, 2018 at 04:06:41PM +0300, Jarkko Sakkinen wrote:
> From: Sean Christopherson <[email protected]>
>
> Add SGX feature bits as part of the Linux defined leaf 8, which
> currently contains virtualization flags. There are currently four
> documented SGX feature bits, with more expected in the not-too-distant
> future.

...

> +static void detect_sgx(struct cpuinfo_x86 *c)
> +{
> +#define _X86_FEATURE_SGX1 BIT(0)
> +#define _X86_FEATURE_SGX2 BIT(1)
> +#define _X86_FEATURE_SGX_ENCLV BIT(5)
> +#define _X86_FEATURE_SGX_ENCLS_C BIT(6)
> +
> + unsigned int eax;
> +
> + clear_cpu_cap(c, X86_FEATURE_SGX1);
> + clear_cpu_cap(c, X86_FEATURE_SGX2);
> + clear_cpu_cap(c, X86_FEATURE_SGX_ENCLV);
> + clear_cpu_cap(c, X86_FEATURE_SGX_ENCLS_C);
> +
> + if (c->cpuid_level < SGX_CPUID) {
> + pr_err_once("x86/sgx: cannot enumerate CPUID leaf (0x%x)\n",
> + SGX_CPUID);
> + clear_cpu_cap(c, X86_FEATURE_SGX);
> + return;
> + }
> +
> + eax = cpuid_eax(SGX_CPUID);
> +
> + if (eax & _X86_FEATURE_SGX1)
> + set_cpu_cap(c, X86_FEATURE_SGX1);
> +
> + if (eax & _X86_FEATURE_SGX2)
> + set_cpu_cap(c, X86_FEATURE_SGX2);
> +
> + if (eax & _X86_FEATURE_SGX_ENCLV)
> + set_cpu_cap(c, X86_FEATURE_SGX_ENCLV);
> +
> + if (eax & _X86_FEATURE_SGX_ENCLS_C)
> + set_cpu_cap(c, X86_FEATURE_SGX_ENCLS_C);


Look at arch/x86/kernel/cpu/scattered.c for how to do this properly.

--
Regards/Gruss,
Boris.

Good mailing practices for 400: avoid top-posting and trim the reply.

2018-09-25 20:03:07

by Randy Dunlap

[permalink] [raw]
Subject: Re: [PATCH v14 12/19] x86/sgx: Add data structures for tracking the EPC pages

On 9/25/18 6:06 AM, Jarkko Sakkinen wrote:
> Add data structures to track Enclave Page Cache (EPC) pages. EPC is
> divided into multiple banks (1-N) of which addresses and sizes can be
> enumerated with CPUID by the OS.
>
> On NUMA systems a node can have at most bank. A bank can be at most part of
> two nodes. SGX supports both nodes with a single memory controller and also
> sub-cluster nodes with severals memory controllers on a single die.
>
> Signed-off-by: Jarkko Sakkinen <[email protected]>
> Co-developed-by: Serge Ayoun <[email protected]>
> Co-developed-by: Sean Christopherson <[email protected]>
> Signed-off-by: Serge Ayoun <[email protected]>
> Signed-off-by: Sean Christopherson <[email protected]>
> ---
> arch/x86/include/asm/sgx.h | 55 +++++++++++++++
> arch/x86/kernel/cpu/intel_sgx.c | 115 ++++++++++++++++++++++++++++++++
> 2 files changed, 170 insertions(+)

Hi,

Several comment blocks begin with "/**" but they are either not intended
to be kernel-doc notation or they are incomplete kernel-doc notation.

>
> diff --git a/arch/x86/include/asm/sgx.h b/arch/x86/include/asm/sgx.h
> index e66e2572011e..468e609147cd 100644
> --- a/arch/x86/include/asm/sgx.h
> +++ b/arch/x86/include/asm/sgx.h
> @@ -5,10 +5,65 @@
> #ifndef _ASM_X86_SGX_H
> #define _ASM_X86_SGX_H
>
> +#include <linux/bitops.h>
> +#include <linux/err.h>
> +#include <linux/rwsem.h>
> #include <linux/types.h>
> +#include <asm/sgx_arch.h>
> +#include <asm/asm.h>
> +
> +struct sgx_epc_page {
> + unsigned long desc;
> + struct list_head list;
> +};
> +
> +/**
> + * struct sgx_epc_section
> + *
> + * The firmware can define multiple chunks of EPC to the different areas of the
> + * physical memory e.g. for memory areas of the each node. This structure is
> + * used to store EPC pages for one EPC section and virtual memory area where
> + * the pages have been mapped.
> + */

Incomplete...

> +struct sgx_epc_section {
> + unsigned long pa;
> + void __iomem *va;
> + struct sgx_epc_page **pages;
> + unsigned long free_cnt;
> + spinlock_t lock;
> +};
>
> extern bool sgx_enabled;
> extern bool sgx_lc_enabled;
> +extern struct sgx_epc_section sgx_epc_sections[SGX_MAX_EPC_SECTIONS];
> +
> +/*
> + * enum sgx_epc_page_desc - bits and masks for an EPC page's descriptor
> + * %SGX_EPC_SECTION_MASK: SGX allows to have multiple EPC sections in the
> + * physical memory. The existing and near-future
> + * hardware defines at most eight sections, hence
> + * three bits to hold a section.
> + * %SGX_EPC_PAGE_RECLAIMABLE: The page page is reclaimable. Used when freeing
> + * a page to know that we also need to remove the
> + * page from the list of reclaimable pages.
> + */

OTOH, this one is close to ready for kernel-doc but it does not begin with
"/**". But that's OK, your choice.

> +enum sgx_epc_page_desc {
> + SGX_EPC_SECTION_MASK = GENMASK_ULL(3, 0),
> + SGX_EPC_PAGE_RECLAIMABLE = BIT(4),
> + /* bits 12-63 are reserved for the physical page address of the page */
> +};
> +
> +static inline struct sgx_epc_section *sgx_epc_section(struct sgx_epc_page *page)
> +{
> + return &sgx_epc_sections[page->desc & SGX_EPC_SECTION_MASK];
> +}
> +
> +static inline void __iomem *sgx_epc_addr(struct sgx_epc_page *page)
> +{
> + struct sgx_epc_section *section = sgx_epc_section(page);
> +
> + return section->va + (page->desc & PAGE_MASK) - section->pa;
> +}
>
> /**
> * ENCLS_FAULT_FLAG - flag signifying an ENCLS return code is a trapnr
> diff --git a/arch/x86/kernel/cpu/intel_sgx.c b/arch/x86/kernel/cpu/intel_sgx.c
> index 138af9b9a39a..b24d6287442d 100644
> --- a/arch/x86/kernel/cpu/intel_sgx.c
> +++ b/arch/x86/kernel/cpu/intel_sgx.c
> @@ -14,10 +14,121 @@ bool sgx_enabled __ro_after_init;
> EXPORT_SYMBOL_GPL(sgx_enabled);
> bool sgx_lc_enabled __ro_after_init;
> EXPORT_SYMBOL_GPL(sgx_lc_enabled);
> +struct sgx_epc_section sgx_epc_sections[SGX_MAX_EPC_SECTIONS];
> +EXPORT_SYMBOL_GPL(sgx_epc_sections);
> +
> +static int sgx_nr_epc_sections;
> +
> +static __init void sgx_free_epc_section(struct sgx_epc_section *section)
> +{
> + int i;
> +
> + for (i = 0; i < section->free_cnt && section->pages[i]; i++)
> + kfree(section->pages[i]);
> + kfree(section->pages);
> + iounmap(section->va);
> +}
> +
> +static __init int sgx_init_epc_section(u64 addr, u64 size, unsigned long index,
> + struct sgx_epc_section *section)
> +{
> + unsigned long nr_pages = size >> PAGE_SHIFT;
> + unsigned long i;
> +
> + section->va = ioremap_cache(addr, size);
> + if (!section->va)
> + return -ENOMEM;
> +
> + section->pa = addr;
> + section->free_cnt = nr_pages;
> + spin_lock_init(&section->lock);
> +
> + section->pages = kcalloc(nr_pages, sizeof(struct sgx_epc_page *),
> + GFP_KERNEL);
> + if (!section->pages)
> + goto out;
> +
> + for (i = 0; i < nr_pages; i++) {
> + section->pages[i] = kzalloc(sizeof(struct sgx_epc_page),
> + GFP_KERNEL);
> + if (!section->pages[i])
> + goto out;
> +
> + section->pages[i]->desc = (addr + (i << PAGE_SHIFT)) | index;
> + }
> +
> + return 0;
> +out:
> + sgx_free_epc_section(section);
> + return -ENOMEM;
> +}
> +
> +static __init void sgx_page_cache_teardown(void)
> +{
> + int i;
> +
> + for (i = 0; i < sgx_nr_epc_sections; i++)
> + sgx_free_epc_section(&sgx_epc_sections[i]);
> +}
> +
> +/**
> + * A section metric is concatenated in a way that @low bits 12-31 define the
> + * bits 12-31 of the metric and @high bits 0-19 define the bits 32-51 of the
> + * metric.
> + */

not kernel-doc notation.

> +static inline u64 sgx_calc_section_metric(u64 low, u64 high)
> +{
> + return (low & GENMASK_ULL(31, 12)) +
> + ((high & GENMASK_ULL(19, 0)) << 32);
> +}

thanks,
--
~Randy

2018-09-25 20:03:37

by Randy Dunlap

[permalink] [raw]
Subject: [PATCH v14 10/19] x86/sgx: Detect Intel SGX

On 9/25/18 6:06 AM, Jarkko Sakkinen wrote:
> diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
> index 1a0be022f91d..b47e1a144409 100644
> --- a/arch/x86/Kconfig
> +++ b/arch/x86/Kconfig
> @@ -1913,6 +1913,23 @@ config X86_INTEL_MEMORY_PROTECTION_KEYS
>
> If unsure, say y.
>
> +config INTEL_SGX_CORE
> + bool "Intel SGX core functionality"
> + depends on X86_64 && CPU_SUP_INTEL
> + help
> + Intel Software Guard eXtensions (SGX) CPU feature that allows ring 3
> + applications to create enclaves: private regions of memory that are
> + architecturally protected from unauthorized access and/or modification.
> +
> + This option enables kernel recognition of SGX, high-level management
> + of the Enclave Page Cache (EPC), tracking and writing of SGX Launch
> + Enclave Hash MSRs, and allows for virtualization of SGX via KVM. By
> + itself, this option does not provide SGX support to userspace.
> +
> + For details, see Documentation/x86/intel_sgx.rst
> +
> + If unsure, say N.
> +

Hi,
coding-style.rst says that help text should be indented with
one tab + 2 spaces.

thanks.

--
~Randy

2018-09-25 20:04:10

by Randy Dunlap

[permalink] [raw]
Subject: [PATCH v14 11/19] x86/sgx: Add wrappers for ENCLS leaf functions

On 9/25/18 6:06 AM, Jarkko Sakkinen wrote:
> Add wrappers for Intel(R) SGX ENCLS opcode leaf functions except
> ENCLS(EINIT). ENCLS invokes privileged functions for managing (creation,
> initialization and swapping) and debugging enclaves.
>
> Signed-off-by: Jarkko Sakkinen <[email protected]>
> Co-developed-by: Sean Christopherson <[email protected]>
> Signed-off-by: Sean Christopherson <[email protected]>
> ---
> arch/x86/include/asm/sgx.h | 244 +++++++++++++++++++++++++++++++++++++
> 1 file changed, 244 insertions(+)
>
> diff --git a/arch/x86/include/asm/sgx.h b/arch/x86/include/asm/sgx.h
> index f4f82f0453a9..e66e2572011e 100644
> --- a/arch/x86/include/asm/sgx.h
> +++ b/arch/x86/include/asm/sgx.h
> @@ -10,4 +10,248 @@
> extern bool sgx_enabled;
> extern bool sgx_lc_enabled;
>

Hi,
Please don't use "/**" to begin comment blocks that are not kernel-doc
notation, like the 3 below. Note: I might have seen one (or some) of
these "fixed" in other patches in this series (not sure).


> +/**
> + * ENCLS_FAULT_FLAG - flag signifying an ENCLS return code is a trapnr
> + *
> + * ENCLS has its own (positive value) error codes and also generates
> + * ENCLS specific #GP and #PF faults. And the ENCLS values get munged
> + * with system error codes as everything percolates back up the stack.
> + * Unfortunately (for us), we need to precisely identify each unique
> + * error code, e.g. the action taken if EWB fails varies based on the
> + * type of fault and on the exact SGX error code, i.e. we can't simply
> + * convert all faults to -EFAULT.
> + *
> + * To make all three error types coexist, we set bit 30 to identify an
> + * ENCLS fault. Bit 31 (technically bits N:31) is used to differentiate
> + * between positive (faults and SGX error codes) and negative (system
> + * error codes) values.
> + */
> +#define ENCLS_FAULT_FLAG 0x40000000
> +
> +/**
> + * Check for a fault by looking for a postive value with the fault
> + * flag set. The postive value check is needed to filter out system
> + * error codes since negative values will have all higher order bits
> + * set, including ENCLS_FAULT_FLAG.
> + */
> +#define IS_ENCLS_FAULT(r) ((int)(r) > 0 && ((r) & ENCLS_FAULT_FLAG))
> +
> +/**
> + * Retrieve the encoded trapnr from the specified return code.
> + */
> +#define ENCLS_TRAPNR(r) ((r) & ~ENCLS_FAULT_FLAG)

thanks,
--
~Randy

2018-09-25 22:54:26

by Andy Lutomirski

[permalink] [raw]
Subject: Re: [PATCH v14 09/19] x86/mm: x86/sgx: Signal SEGV_SGXERR for #PFs w/ PF_SGX

Minor nit:

On Tue, Sep 25, 2018 at 6:12 AM Jarkko Sakkinen
<[email protected]> wrote:
>
> From: Sean Christopherson <[email protected]>
>

> by (c) as the kernel doesn't really have any other reasonable option,
> e.g. we could kill the task or panic, but neither is warranted.

Not killing the task is quite nice, but...

> + /*
> + * Access is blocked by the Enclave Page Cache Map (EPCM),
> + * i.e. the access is allowed by the PTE but not the EPCM.
> + * This usually happens when the EPCM is yanked out from
> + * under us, e.g. by hardware after a suspend/resume cycle.
> + * In any case, there is nothing that can be done by the
> + * kernel to resolve the fault (short of killing the task).

Maybe s/killing the task/sending a signal/?

--Andy

2018-09-26 11:12:46

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [PATCH v14 04/19] x86/cpufeatures: Add SGX feature bits

On Tue, Sep 25, 2018 at 06:48:54PM +0200, Borislav Petkov wrote:
> > + eax = cpuid_eax(SGX_CPUID);
> > +
> > + if (eax & _X86_FEATURE_SGX1)
> > + set_cpu_cap(c, X86_FEATURE_SGX1);
> > +
> > + if (eax & _X86_FEATURE_SGX2)
> > + set_cpu_cap(c, X86_FEATURE_SGX2);
> > +
> > + if (eax & _X86_FEATURE_SGX_ENCLV)
> > + set_cpu_cap(c, X86_FEATURE_SGX_ENCLV);
> > +
> > + if (eax & _X86_FEATURE_SGX_ENCLS_C)
> > + set_cpu_cap(c, X86_FEATURE_SGX_ENCLS_C);
>
>
> Look at arch/x86/kernel/cpu/scattered.c for how to do this properly.

Thank you. I guess I understand what you want me to do i.e.

+ { X86_FEATURE_SGX1, CPUID_EAX, 0, 0x00000012, 0 },
+ { X86_FEATURE_SGX2, CPUID_EAX, 1, 0x00000012, 0 },
+ { X86_FEATURE_ENCLV, CPUID_EAX, 5, 0x00000012, 0 },
+ { X86_FEATURE_ENCLS_C, CPUID_EAX, 6, 0x00000012, 0 },

What puzzles me is that I cannot find any call site for
get_scattered_cpuid_leaf():

$ git grep get_scattered
arch/x86/kernel/cpu/cpu.h:extern u32 get_scattered_cpuid_leaf(unsigned int level,
arch/x86/kernel/cpu/scattered.c:u32 get_scattered_cpuid_leaf(unsigned int level, unsigned int sub_leaf,
arch/x86/kernel/cpu/scattered.c:EXPORT_SYMBOL_GPL(get_scattered_cpuid_leaf);

/Jarkko

2018-09-26 11:36:49

by Borislav Petkov

[permalink] [raw]
Subject: Re: [PATCH v14 04/19] x86/cpufeatures: Add SGX feature bits

On Wed, Sep 26, 2018 at 02:11:39PM +0300, Jarkko Sakkinen wrote:
> Thank you. I guess I understand what you want me to do i.e.
>
> + { X86_FEATURE_SGX1, CPUID_EAX, 0, 0x00000012, 0 },
> + { X86_FEATURE_SGX2, CPUID_EAX, 1, 0x00000012, 0 },
> + { X86_FEATURE_ENCLV, CPUID_EAX, 5, 0x00000012, 0 },
> + { X86_FEATURE_ENCLS_C, CPUID_EAX, 6, 0x00000012, 0 },

Yap.

> What puzzles me is that I cannot find any call site for
> get_scattered_cpuid_leaf():
>
> $ git grep get_scattered
> arch/x86/kernel/cpu/cpu.h:extern u32 get_scattered_cpuid_leaf(unsigned int level,
> arch/x86/kernel/cpu/scattered.c:u32 get_scattered_cpuid_leaf(unsigned int level, unsigned int sub_leaf,
> arch/x86/kernel/cpu/scattered.c:EXPORT_SYMBOL_GPL(get_scattered_cpuid_leaf);

That used to be used in kvm last, see b7b27aa011a1df42728d1768fc181d9ce69e6911,
which removed it.

And it got added by

47bdf3378d62 ("x86/cpuid: Provide get_scattered_cpuid_leaf()")

Lemme add those people to CC.

If no one wants it, feel free to decomission it.

--
Regards/Gruss,
Boris.

Good mailing practices for 400: avoid top-posting and trim the reply.

2018-09-26 17:36:26

by Sean Christopherson

[permalink] [raw]
Subject: Re: [PATCH v14 09/19] x86/mm: x86/sgx: Signal SEGV_SGXERR for #PFs w/ PF_SGX

On Tue, Sep 25, 2018 at 03:53:48PM -0700, Andy Lutomirski wrote:
> Minor nit:
>
> On Tue, Sep 25, 2018 at 6:12 AM Jarkko Sakkinen
> <[email protected]> wrote:
> >
> > From: Sean Christopherson <[email protected]>
> >
>
> > by (c) as the kernel doesn't really have any other reasonable option,
> > e.g. we could kill the task or panic, but neither is warranted.
>
> Not killing the task is quite nice, but...
>
> > + /*
> > + * Access is blocked by the Enclave Page Cache Map (EPCM),
> > + * i.e. the access is allowed by the PTE but not the EPCM.
> > + * This usually happens when the EPCM is yanked out from
> > + * under us, e.g. by hardware after a suspend/resume cycle.
> > + * In any case, there is nothing that can be done by the
> > + * kernel to resolve the fault (short of killing the task).
>
> Maybe s/killing the task/sending a signal/?

My intent was to document that, unlike all other page faults, the
kernel can't fix the source of the fault even if it were omniscient.

How about this? With formatting changes since it's long-winded...

/*
* Access is blocked by the Enclave Page Cache Map (EPCM), i.e. the
* access is allowed by the PTE but not the EPCM. This usually happens
* when the EPCM is yanked out from under us, e.g. by hardware after a
* suspend/resume cycle. In any case, software, i.e. the kernel, can't
* fix the source of the fault as the EPCM can't be directly modified
* by software. Handle the fault as an access error in order to signal
* userspace, e.g. so that userspace can rebuild their enclave(s), even
* though userspace may not have actually violated access permissions.
*/


2018-09-26 18:13:48

by Andy Lutomirski

[permalink] [raw]
Subject: Re: [PATCH v14 09/19] x86/mm: x86/sgx: Signal SEGV_SGXERR for #PFs w/ PF_SGX


> On Sep 26, 2018, at 10:35 AM, Sean Christopherson <[email protected]> wrote:
>
>> On Tue, Sep 25, 2018 at 03:53:48PM -0700, Andy Lutomirski wrote:
>> Minor nit:
>>
>> On Tue, Sep 25, 2018 at 6:12 AM Jarkko Sakkinen
>> <[email protected]> wrote:
>>>
>>> From: Sean Christopherson <[email protected]>
>>>
>>
>>> by (c) as the kernel doesn't really have any other reasonable option,
>>> e.g. we could kill the task or panic, but neither is warranted.
>>
>> Not killing the task is quite nice, but...
>>
>>> + /*
>>> + * Access is blocked by the Enclave Page Cache Map (EPCM),
>>> + * i.e. the access is allowed by the PTE but not the EPCM.
>>> + * This usually happens when the EPCM is yanked out from
>>> + * under us, e.g. by hardware after a suspend/resume cycle.
>>> + * In any case, there is nothing that can be done by the
>>> + * kernel to resolve the fault (short of killing the task).
>>
>> Maybe s/killing the task/sending a signal/?
>
> My intent was to document that, unlike all other page faults, the
> kernel can't fix the source of the fault even if it were omniscient.
>
> How about this? With formatting changes since it's long-winded...
>
> /*
> * Access is blocked by the Enclave Page Cache Map (EPCM), i.e. the
> * access is allowed by the PTE but not the EPCM. This usually happens
> * when the EPCM is yanked out from under us, e.g. by hardware after a
> * suspend/resume cycle. In any case, software, i.e. the kernel, can't
> * fix the source of the fault as the EPCM can't be directly modified
> * by software. Handle the fault as an access error in order to signal
> * userspace, e.g. so that userspace can rebuild their enclave(s), even
> * though userspace may not have actually violated access permissions.
> */
>

Looks good to me.

2018-09-26 19:15:08

by Sean Christopherson

[permalink] [raw]
Subject: Re: [PATCH v14 08/19] signal: x86/sgx: Add SIGSEGV siginfo code for SGX EPCM fault

On Tue, Sep 25, 2018 at 04:06:45PM +0300, Jarkko Sakkinen wrote:
> From: Sean Christopherson <[email protected]>
>
> The SGX Enclave Page Cache Map (EPCM) is a hardware-managed table
> that enforces accesses to an enclave's EPC page in addition to the
> software-managed kernel page tables, i.e. the effective permissions
> for an EPC page are a logical AND of the kernel's page tables and
> the corresponding EPCM entry. The primary purpose of the EPCM is
> to prevent a malcious or compromised kernel from attacking an enclave
> by modifying the enclave's page tables. The EPCM entires for an
> enclave are populated when the enclave is built and verified, using
> metadata provided by the enclave that is included in the measurement
> used to verify the enclave.
>
> In normal operation of a properly functioning, non-malicious kernel
> (and enclave), the EPCM permissions will never trigger a fault, i.e.
> the kernel may make the permissions for an EPC page more restrictive,
> e.g. mark it not-present to swap out the EPC page, but the kernel will
> never make its permissions less restrictive.
>
> But, there is a legitimate scenario in which the kernel's page tables
> can become less restrictive than the EPCM: on current hardware all
> enclaves are destroyed (by hardware) on a transition to S3 or lower
> sleep states, i.e. all EPCM entries are invalid (not-present) after
> the system resumes from its sleep state.
>
> Unfortunately, on CPUs that support only SGX1, EPCM violations result
> in a #GP. The upside of the #GP is that no kernel changes are needed
> to deal with the EPCM being blasted away by hardware, e.g. userspace
> gets a SIGSEGV, assumes the EPCM was lost and restarts its enclave
> and everyone is happy. The downside is that userspace has to assume
> the SIGSEGV was because the EPC was lost (or possibly do some leg work
> to rule out other causes).
>
> In SGX2, the oddity of delivering a #GP due to what are inherently
> paging related violations is remedied. CPUs that support SGX2 deliver
> EPCM violations as #PFs with a new SGX error code bit set. So, now
> that hardware provides us with a way to unequivocally determine that
> a fault was due to a EPCM violation, define a signfo code for SIGSEGV
> so that the information can be passed onto userspace.
>
> Cc: Dave Hansen <[email protected]>
> Signed-off-by: Sean Christopherson <[email protected]>
> Signed-off-by: Jarkko Sakkinen <[email protected]>
> ---
> include/uapi/asm-generic/siginfo.h | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/include/uapi/asm-generic/siginfo.h b/include/uapi/asm-generic/siginfo.h
> index 80e2a7227205..fdd898e2325b 100644
> --- a/include/uapi/asm-generic/siginfo.h
> +++ b/include/uapi/asm-generic/siginfo.h
> @@ -225,7 +225,11 @@ typedef struct siginfo {
> #else
> # define SEGV_PKUERR 4 /* failed protection key checks */
> #endif
> +#ifdef __x86_64__

Argh, this needs to be "#if defined(__i386__) || defined(__x86_64__)"
otherwise 32-bit builds break on later patches that use SEGV_SGXERR,
e.g. the errors flagged by the 0-DAY bot.

> +#define SEGV_SGXERR 5 /* SGX Enclave Page Cache Map fault */
> +#else
> #define SEGV_ACCADI 5 /* ADI not enabled for mapped object */
> +#endif
> #define SEGV_ADIDERR 6 /* Disrupting MCD error */
> #define SEGV_ADIPERR 7 /* Precise MCD exception */
> #define NSIGSEGV 7
> --
> 2.17.1
>

2018-09-26 20:24:00

by Dave Hansen

[permalink] [raw]
Subject: Re: [PATCH v14 09/19] x86/mm: x86/sgx: Signal SEGV_SGXERR for #PFs w/ PF_SGX

On 09/26/2018 11:12 AM, Andy Lutomirski wrote:
>> e omniscient.
>>
>> How about this? With formatting changes since it's long-winded...
>>
>> /*
>> * Access is blocked by the Enclave Page Cache Map (EPCM), i.e. the
>> * access is allowed by the PTE but not the EPCM. This usually happens
>> * when the EPCM is yanked out from under us, e.g. by hardware after a
>> * suspend/resume cycle. In any case, software, i.e. the kernel, can't
>> * fix the source of the fault as the EPCM can't be directly modified
>> * by software. Handle the fault as an access error in order to signal
>> * userspace, e.g. so that userspace can rebuild their enclave(s), even
>> * though userspace may not have actually violated access permissions.
>> */
>>
> Looks good to me.

Including the actual architectural definition of the bit might add some
clarity. The SDM explicitly says (Vol 3a section 4.7):

The fault resulted from violation of SGX-specific access-control
requirements.

Which totally squares with returning true from access_error().

There's also a tidbit that says:

This flag is 1 if the exception is unrelated to paging and
resulted from violation of SGX-specific access-control
requirements. ... such a violation can occur only if there
is no ordinary page fault...

This is pretty important. It means that *none* of the other
paging-related stuff that we're doing applies.

We also need to clarify how this can happen. Is it through something
than an app does, or is it solely when the hardware does something under
the covers, like suspend/resume.

2018-09-26 20:44:29

by Sean Christopherson

[permalink] [raw]
Subject: Re: [PATCH v14 09/19] x86/mm: x86/sgx: Signal SEGV_SGXERR for #PFs w/ PF_SGX

On Wed, Sep 26, 2018 at 01:16:59PM -0700, Dave Hansen wrote:
> On 09/26/2018 11:12 AM, Andy Lutomirski wrote:
> >> e omniscient.
> >>
> >> How about this? With formatting changes since it's long-winded...
> >>
> >> /*
> >> * Access is blocked by the Enclave Page Cache Map (EPCM), i.e. the
> >> * access is allowed by the PTE but not the EPCM. This usually happens
> >> * when the EPCM is yanked out from under us, e.g. by hardware after a
> >> * suspend/resume cycle. In any case, software, i.e. the kernel, can't
> >> * fix the source of the fault as the EPCM can't be directly modified
> >> * by software. Handle the fault as an access error in order to signal
> >> * userspace, e.g. so that userspace can rebuild their enclave(s), even
> >> * though userspace may not have actually violated access permissions.
> >> */
> >>
> > Looks good to me.
>
> Including the actual architectural definition of the bit might add some
> clarity. The SDM explicitly says (Vol 3a section 4.7):
>
> The fault resulted from violation of SGX-specific access-control
> requirements.
>
> Which totally squares with returning true from access_error().
>
> There's also a tidbit that says:
>
> This flag is 1 if the exception is unrelated to paging and
> resulted from violation of SGX-specific access-control
> requirements. ... such a violation can occur only if there
> is no ordinary page fault...
>
> This is pretty important. It means that *none* of the other
> paging-related stuff that we're doing applies.
>
> We also need to clarify how this can happen. Is it through something
> than an app does, or is it solely when the hardware does something under
> the covers, like suspend/resume.

Are you looking for something in the changelog, the comment, or just
a response? If it's the latter...

On bare metal with a bug-free kernel, the only scenario I'm aware of
where we'll encounter these faults is when hardware pulls the rug out
from under us. In a virtualized environment all bets are off because
the architecture allows VMMs to silently "destroy" the EPC at will,
e.g. KVM, and I believe Hyper-V, will take advantage of this behavior
to support live migration. Post migration, the destination system
will generate PF_SGX because the EPC{M} can't be migrated between
system, i.e. the destination EPCM sees all EPC pages as invalid.


2018-09-26 20:55:51

by Dave Hansen

[permalink] [raw]
Subject: Re: [PATCH v14 09/19] x86/mm: x86/sgx: Signal SEGV_SGXERR for #PFs w/ PF_SGX

On 09/26/2018 01:44 PM, Sean Christopherson wrote:
> On Wed, Sep 26, 2018 at 01:16:59PM -0700, Dave Hansen wrote:
>> We also need to clarify how this can happen. Is it through something
>> than an app does, or is it solely when the hardware does something under
>> the covers, like suspend/resume.
>
> Are you looking for something in the changelog, the comment, or just
> a response? If it's the latter...

Comments, please.

> On bare metal with a bug-free kernel, the only scenario I'm aware of
> where we'll encounter these faults is when hardware pulls the rug out
> from under us. In a virtualized environment all bets are off because
> the architecture allows VMMs to silently "destroy" the EPC at will,
> e.g. KVM, and I believe Hyper-V, will take advantage of this behavior
> to support live migration. Post migration, the destination system
> will generate PF_SGX because the EPC{M} can't be migrated between
> system, i.e. the destination EPCM sees all EPC pages as invalid.

OK, cool.

That's good background fodder for the changelog.

But, for the comment, I'm happy with something like this:

/*
* The fault resulted from violation of SGX-specific access-
* controls. This is expected to be the result of some lower
* layer action (CPU suspend/resume, VM migration) and is
* not related to anything the OS did. Treat it as an access
* error to ensure it is passed up to the app via a signal where
* it can be handled.
*/

I really don't think we need to delve too deeply into the relationship
between EPCM and PTEs or anything. Let's just say, "it's not the
kernel's fault, it's not the app's fault, so throw up our hands".

2018-09-26 21:16:28

by Andy Lutomirski

[permalink] [raw]
Subject: Re: [PATCH v14 09/19] x86/mm: x86/sgx: Signal SEGV_SGXERR for #PFs w/ PF_SGX

On Wed, Sep 26, 2018 at 1:55 PM Dave Hansen <[email protected]> wrote:
>
> On 09/26/2018 01:44 PM, Sean Christopherson wrote:
> > On Wed, Sep 26, 2018 at 01:16:59PM -0700, Dave Hansen wrote:
> >> We also need to clarify how this can happen. Is it through something
> >> than an app does, or is it solely when the hardware does something under
> >> the covers, like suspend/resume.
> >
> > Are you looking for something in the changelog, the comment, or just
> > a response? If it's the latter...
>
> Comments, please.
>
> > On bare metal with a bug-free kernel, the only scenario I'm aware of
> > where we'll encounter these faults is when hardware pulls the rug out
> > from under us. In a virtualized environment all bets are off because
> > the architecture allows VMMs to silently "destroy" the EPC at will,
> > e.g. KVM, and I believe Hyper-V, will take advantage of this behavior
> > to support live migration. Post migration, the destination system
> > will generate PF_SGX because the EPC{M} can't be migrated between
> > system, i.e. the destination EPCM sees all EPC pages as invalid.
>
> OK, cool.
>
> That's good background fodder for the changelog.
>
> But, for the comment, I'm happy with something like this:
>
> /*
> * The fault resulted from violation of SGX-specific access-
> * controls. This is expected to be the result of some lower
> * layer action (CPU suspend/resume, VM migration) and is
> * not related to anything the OS did. Treat it as an access
> * error to ensure it is passed up to the app via a signal where
> * it can be handled.
> */
>
> I really don't think we need to delve too deeply into the relationship
> between EPCM and PTEs or anything. Let's just say, "it's not the
> kernel's fault, it's not the app's fault, so throw up our hands".

There is a non-nitpicky consideration here. Logically, user code is
going to do this (totally made-up pseudocode):

enclave_t enclave = load_and_init_enclave(...);
int ret = sgx_run(enclave, some pointers to non-enclave-memory buffers, ...);

and, with the code in this patch, a correct implementation of
sgx_run() requires installing a signal handler. This is nasty, since
signal handlers, expecially for something like SIGSEGV or SIGBUS, are
not fantastic to say the least in libraries.

Could we perhaps have a little vDSO entry (or syscall, I suppose) that
runs an enclave an returns an error code, and rig up the #PF handler
to check if the error happened in the vDSO entry and fix it up rather
than sending a signal?

On Windows, this is much less of a concern, because Windows has real
scoped fault handling. But Linux doesn't, at least not yet.


--
Andy Lutomirski
AMA Capital Management, LLC

2018-09-26 21:46:39

by Dave Hansen

[permalink] [raw]
Subject: Re: [PATCH v14 09/19] x86/mm: x86/sgx: Signal SEGV_SGXERR for #PFs w/ PF_SGX

On 09/26/2018 02:15 PM, Andy Lutomirski wrote:
> Could we perhaps have a little vDSO entry (or syscall, I suppose) that
> runs an enclave an returns an error code, and rig up the #PF handler
> to check if the error happened in the vDSO entry and fix it up rather
> than sending a signal?

Yeah, signals suck.

So, instead of doing the enclave entry instruction (EENTER is it?), the
app would do the vDSO call. It would have some calling convention, like
"set %rax to 0 before entering". Then, we just teach the page fault
handler about the %RIP in the vDSO that can fault and how to move one
instruction later, munge %RIP to a value that tells about the error,
then return from the fault. It would basically be like the kernel
exception tables, but for userspace. Right?

How would a syscall work, though? I assume we can't just enter the
enclave from ring0.

2018-09-26 22:38:39

by Andy Lutomirski

[permalink] [raw]
Subject: Re: [PATCH v14 09/19] x86/mm: x86/sgx: Signal SEGV_SGXERR for #PFs w/ PF_SGX

On Wed, Sep 26, 2018 at 2:45 PM Dave Hansen <[email protected]> wrote:
>
> On 09/26/2018 02:15 PM, Andy Lutomirski wrote:
> > Could we perhaps have a little vDSO entry (or syscall, I suppose) that
> > runs an enclave an returns an error code, and rig up the #PF handler
> > to check if the error happened in the vDSO entry and fix it up rather
> > than sending a signal?
>
> Yeah, signals suck.
>
> So, instead of doing the enclave entry instruction (EENTER is it?), the
> app would do the vDSO call. It would have some calling convention, like
> "set %rax to 0 before entering". Then, we just teach the page fault
> handler about the %RIP in the vDSO that can fault and how to move one
> instruction later, munge %RIP to a value that tells about the error,
> then return from the fault. It would basically be like the kernel
> exception tables, but for userspace. Right?

Yeah. Maybe like this:

xorl %eax,%eax
eenter_insn:
ENCLU[whatever]
eenter_landing_pad:
ret

And the kernel would use the existing vdso2c vdso-symbol-finding
mechanism to do the fixup.

>
> How would a syscall work, though? I assume we can't just enter the
> enclave from ring0.

My understanding of how AEX works is a bit vague, but maybe a syscall
could reuse the mechanism? The vDSO approach seems considerably
simpler.

We do need to make sure that a fault that happens on or after return
from an AEX event does the right thing. But I'm still vague on how
that works, sigh.

--Andy

2018-09-27 13:12:38

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [PATCH v14 12/19] x86/sgx: Add data structures for tracking the EPC pages

On Tue, Sep 25, 2018 at 01:00:42PM -0700, Randy Dunlap wrote:
> On 9/25/18 6:06 AM, Jarkko Sakkinen wrote:
> > Add data structures to track Enclave Page Cache (EPC) pages. EPC is
> > divided into multiple banks (1-N) of which addresses and sizes can be
> > enumerated with CPUID by the OS.
> >
> > On NUMA systems a node can have at most bank. A bank can be at most part of
> > two nodes. SGX supports both nodes with a single memory controller and also
> > sub-cluster nodes with severals memory controllers on a single die.
> >
> > Signed-off-by: Jarkko Sakkinen <[email protected]>
> > Co-developed-by: Serge Ayoun <[email protected]>
> > Co-developed-by: Sean Christopherson <[email protected]>
> > Signed-off-by: Serge Ayoun <[email protected]>
> > Signed-off-by: Sean Christopherson <[email protected]>
> > ---
> > arch/x86/include/asm/sgx.h | 55 +++++++++++++++
> > arch/x86/kernel/cpu/intel_sgx.c | 115 ++++++++++++++++++++++++++++++++
> > 2 files changed, 170 insertions(+)
>
> Hi,
>
> Several comment blocks begin with "/**" but they are either not intended
> to be kernel-doc notation or they are incomplete kernel-doc notation.

I'll enumerate them through.

Thank you for the feedback.

/Jarkko

2018-09-27 13:13:14

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [PATCH v14 11/19] x86/sgx: Add wrappers for ENCLS leaf functions

On Tue, Sep 25, 2018 at 01:01:47PM -0700, Randy Dunlap wrote:
> Hi,
> Please don't use "/**" to begin comment blocks that are not kernel-doc
> notation, like the 3 below. Note: I might have seen one (or some) of
> these "fixed" in other patches in this series (not sure).

Thanks Randy.

/Jarkko

2018-09-27 13:14:40

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [PATCH v14 10/19] x86/sgx: Detect Intel SGX

On Tue, Sep 25, 2018 at 01:02:14PM -0700, Randy Dunlap wrote:
> On 9/25/18 6:06 AM, Jarkko Sakkinen wrote:
> > diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
> > index 1a0be022f91d..b47e1a144409 100644
> > --- a/arch/x86/Kconfig
> > +++ b/arch/x86/Kconfig
> > @@ -1913,6 +1913,23 @@ config X86_INTEL_MEMORY_PROTECTION_KEYS
> >
> > If unsure, say y.
> >
> > +config INTEL_SGX_CORE
> > + bool "Intel SGX core functionality"
> > + depends on X86_64 && CPU_SUP_INTEL
> > + help
> > + Intel Software Guard eXtensions (SGX) CPU feature that allows ring 3
> > + applications to create enclaves: private regions of memory that are
> > + architecturally protected from unauthorized access and/or modification.
> > +
> > + This option enables kernel recognition of SGX, high-level management
> > + of the Enclave Page Cache (EPC), tracking and writing of SGX Launch
> > + Enclave Hash MSRs, and allows for virtualization of SGX via KVM. By
> > + itself, this option does not provide SGX support to userspace.
> > +
> > + For details, see Documentation/x86/intel_sgx.rst
> > +
> > + If unsure, say N.
> > +
>
> Hi,
> coding-style.rst says that help text should be indented with
> one tab + 2 spaces.
>
> thanks.

Thank you.

/Jarkko

2018-09-27 13:15:30

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [PATCH v14 09/19] x86/mm: x86/sgx: Signal SEGV_SGXERR for #PFs w/ PF_SGX

On Tue, Sep 25, 2018 at 03:53:48PM -0700, Andy Lutomirski wrote:
> Minor nit:
>
> On Tue, Sep 25, 2018 at 6:12 AM Jarkko Sakkinen
> <[email protected]> wrote:
> >
> > From: Sean Christopherson <[email protected]>
> >
>
> > by (c) as the kernel doesn't really have any other reasonable option,
> > e.g. we could kill the task or panic, but neither is warranted.
>
> Not killing the task is quite nice, but...
>
> > + /*
> > + * Access is blocked by the Enclave Page Cache Map (EPCM),
> > + * i.e. the access is allowed by the PTE but not the EPCM.
> > + * This usually happens when the EPCM is yanked out from
> > + * under us, e.g. by hardware after a suspend/resume cycle.
> > + * In any case, there is nothing that can be done by the
> > + * kernel to resolve the fault (short of killing the task).
>
> Maybe s/killing the task/sending a signal/?

Agreed. Thanks.

/Jarkko

2018-09-27 13:17:36

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [PATCH v14 04/19] x86/cpufeatures: Add SGX feature bits

On Wed, Sep 26, 2018 at 01:36:17PM +0200, Borislav Petkov wrote:
> On Wed, Sep 26, 2018 at 02:11:39PM +0300, Jarkko Sakkinen wrote:
> > Thank you. I guess I understand what you want me to do i.e.
> >
> > + { X86_FEATURE_SGX1, CPUID_EAX, 0, 0x00000012, 0 },
> > + { X86_FEATURE_SGX2, CPUID_EAX, 1, 0x00000012, 0 },
> > + { X86_FEATURE_ENCLV, CPUID_EAX, 5, 0x00000012, 0 },
> > + { X86_FEATURE_ENCLS_C, CPUID_EAX, 6, 0x00000012, 0 },
>
> Yap.
>
> > What puzzles me is that I cannot find any call site for
> > get_scattered_cpuid_leaf():
> >
> > $ git grep get_scattered
> > arch/x86/kernel/cpu/cpu.h:extern u32 get_scattered_cpuid_leaf(unsigned int level,
> > arch/x86/kernel/cpu/scattered.c:u32 get_scattered_cpuid_leaf(unsigned int level, unsigned int sub_leaf,
> > arch/x86/kernel/cpu/scattered.c:EXPORT_SYMBOL_GPL(get_scattered_cpuid_leaf);
>
> That used to be used in kvm last, see b7b27aa011a1df42728d1768fc181d9ce69e6911,
> which removed it.
>
> And it got added by
>
> 47bdf3378d62 ("x86/cpuid: Provide get_scattered_cpuid_leaf()")
>
> Lemme add those people to CC.
>
> If no one wants it, feel free to decomission it.

Still kind of leaves me puzzled about the situation :-) Why this wasn't
used for example to replace detect_vmx_virtcap()?

/Jarkko

2018-09-27 13:47:18

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [PATCH v14 09/19] x86/mm: x86/sgx: Signal SEGV_SGXERR for #PFs w/ PF_SGX

On Wed, Sep 26, 2018 at 01:16:59PM -0700, Dave Hansen wrote:
> On 09/26/2018 11:12 AM, Andy Lutomirski wrote:
> >> e omniscient.
> >>
> >> How about this? With formatting changes since it's long-winded...
> >>
> >> /*
> >> * Access is blocked by the Enclave Page Cache Map (EPCM), i.e. the
> >> * access is allowed by the PTE but not the EPCM. This usually happens
> >> * when the EPCM is yanked out from under us, e.g. by hardware after a
> >> * suspend/resume cycle. In any case, software, i.e. the kernel, can't
> >> * fix the source of the fault as the EPCM can't be directly modified
> >> * by software. Handle the fault as an access error in order to signal
> >> * userspace, e.g. so that userspace can rebuild their enclave(s), even
> >> * though userspace may not have actually violated access permissions.
> >> */
> >>
> > Looks good to me.
>
> Including the actual architectural definition of the bit might add some
> clarity. The SDM explicitly says (Vol 3a section 4.7):
>
> The fault resulted from violation of SGX-specific access-control
> requirements.
>
> Which totally squares with returning true from access_error().
>
> There's also a tidbit that says:
>
> This flag is 1 if the exception is unrelated to paging and
> resulted from violation of SGX-specific access-control
> requirements. ... such a violation can occur only if there
> is no ordinary page fault...
>
> This is pretty important. It means that *none* of the other
> paging-related stuff that we're doing applies.
>
> We also need to clarify how this can happen. Is it through something
> than an app does, or is it solely when the hardware does something under
> the covers, like suspend/resume.

When you change page permissions lets say with mprotect after the and
try to do an invalid access according to the EPCM permissions this can
happen.

/Jarkko

2018-09-27 13:52:22

by Borislav Petkov

[permalink] [raw]
Subject: Re: [PATCH v14 04/19] x86/cpufeatures: Add SGX feature bits

On Thu, Sep 27, 2018 at 04:16:42PM +0300, Jarkko Sakkinen wrote:
> Still kind of leaves me puzzled about the situation :-) Why this wasn't
> used for example to replace detect_vmx_virtcap()?

I don't understand - detect_vmx_virtcap() is setting X86_FEATURE bits
based on MSR settings. get_scattered_cpuid_leaf() is regenerating a
CPUID leaf from the scattered bits.

Looks like apples and oranges to me...

--
Regards/Gruss,
Boris.

Good mailing practices for 400: avoid top-posting and trim the reply.

2018-09-27 13:58:15

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [PATCH v14 09/19] x86/mm: x86/sgx: Signal SEGV_SGXERR for #PFs w/ PF_SGX

On Wed, Sep 26, 2018 at 02:45:17PM -0700, Dave Hansen wrote:
> On 09/26/2018 02:15 PM, Andy Lutomirski wrote:
> > Could we perhaps have a little vDSO entry (or syscall, I suppose) that
> > runs an enclave an returns an error code, and rig up the #PF handler
> > to check if the error happened in the vDSO entry and fix it up rather
> > than sending a signal?
>
> Yeah, signals suck.
>
> So, instead of doing the enclave entry instruction (EENTER is it?), the
> app would do the vDSO call. It would have some calling convention, like
> "set %rax to 0 before entering". Then, we just teach the page fault
> handler about the %RIP in the vDSO that can fault and how to move one
> instruction later, munge %RIP to a value that tells about the error,
> then return from the fault. It would basically be like the kernel
> exception tables, but for userspace. Right?
>
> How would a syscall work, though? I assume we can't just enter the
> enclave from ring0.

Enclave cannot be entered from ring-0.

For me this plan sounds simple and sound.

/Jarkko

2018-09-27 14:22:53

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [PATCH v14 09/19] x86/mm: x86/sgx: Signal SEGV_SGXERR for #PFs w/ PF_SGX

On Wed, Sep 26, 2018 at 03:37:45PM -0700, Andy Lutomirski wrote:
> Yeah. Maybe like this: > > xorl %eax,%eax > eenter_insn:
> ENCLU[whatever]
> eenter_landing_pad:
> ret
>
> And the kernel would use the existing vdso2c vdso-symbol-finding
> mechanism to do the fixup.
>
> >
> > How would a syscall work, though? I assume we can't just enter the
> > enclave from ring0.
>
> My understanding of how AEX works is a bit vague, but maybe a syscall
> could reuse the mechanism? The vDSO approach seems considerably
> simpler.
>
> We do need to make sure that a fault that happens on or after return
> from an AEX event does the right thing. But I'm still vague on how
> that works, sigh.
>
> --Andy

Returning from AEX does not differ from any other memory access event so
AFAIK it should be handled right with the proposed solution already.
For convenience I think we could have a fixed trampoline for AEX e.g.
this how it is implemented in the open source LE that I did:

sgx_get_token:
push %rbx
mov $0x02, %rax
mov %rsi, %rbx
mov %rdx, %rsi
mov $sgx_async_exit, %rcx
sgx_async_exit:
ENCLU
pop %rbx
ret

BTW, if I converted the in-kernel LE as a standalone test program, would
that be useful for basic testing of the series?

/Jarkko

2018-09-27 14:42:51

by Andy Lutomirski

[permalink] [raw]
Subject: Re: [PATCH v14 09/19] x86/mm: x86/sgx: Signal SEGV_SGXERR for #PFs w/ PF_SGX



> On Sep 27, 2018, at 7:21 AM, Jarkko Sakkinen <[email protected]> wrote:
>
>> On Wed, Sep 26, 2018 at 03:37:45PM -0700, Andy Lutomirski wrote:
>> Yeah. Maybe like this: > > xorl %eax,%eax > eenter_insn:
>> ENCLU[whatever]
>> eenter_landing_pad:
>> ret
>>
>> And the kernel would use the existing vdso2c vdso-symbol-finding
>> mechanism to do the fixup.
>>
>>>
>>> How would a syscall work, though? I assume we can't just enter the
>>> enclave from ring0.
>>
>> My understanding of how AEX works is a bit vague, but maybe a syscall
>> could reuse the mechanism? The vDSO approach seems considerably
>> simpler.
>>
>> We do need to make sure that a fault that happens on or after return
>> from an AEX event does the right thing. But I'm still vague on how
>> that works, sigh.
>>
>> --Andy
>
> Returning from AEX does not differ from any other memory access event so
> AFAIK it should be handled right with the proposed solution already.
> For convenience I think we could have a fixed trampoline for AEX e.g.
> this how it is implemented in the open source LE that I did:
>
> sgx_get_token:
> push %rbx
> mov $0x02, %rax
> mov %rsi, %rbx
> mov %rdx, %rsi
> mov $sgx_async_exit, %rcx
> sgx_async_exit:
> ENCLU
> pop %rbx
> ret
>
> BTW, if I converted the in-kernel LE as a standalone test program, would
> that be useful for basic testing of the series?
>
>

Definitely. Especially if you stick it in selftests/x86 and make it exit cleanly (error code 0) on unsupported hardware.

2018-09-27 14:53:30

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [PATCH v14 04/19] x86/cpufeatures: Add SGX feature bits

On Thu, Sep 27, 2018 at 03:51:39PM +0200, Borislav Petkov wrote:
> On Thu, Sep 27, 2018 at 04:16:42PM +0300, Jarkko Sakkinen wrote:
> > Still kind of leaves me puzzled about the situation :-) Why this wasn't
> > used for example to replace detect_vmx_virtcap()?
>
> I don't understand - detect_vmx_virtcap() is setting X86_FEATURE bits
> based on MSR settings. get_scattered_cpuid_leaf() is regenerating a
> CPUID leaf from the scattered bits.
>
> Looks like apples and oranges to me...
>
> --
> Regards/Gruss,
> Boris.
>
> Good mailing practices for 400: avoid top-posting and trim the reply.

Ugh, sorry, I was not thinking clearly :-)

/Jarkko

2018-09-27 15:01:54

by Dave Hansen

[permalink] [raw]
Subject: Re: [PATCH v14 09/19] x86/mm: x86/sgx: Signal SEGV_SGXERR for #PFs w/ PF_SGX

On 09/27/2018 06:42 AM, Jarkko Sakkinen wrote:
>> This flag is 1 if the exception is unrelated to paging and
>> resulted from violation of SGX-specific access-control
>> requirements. ... such a violation can occur only if there
>> is no ordinary page fault...
>>
>> This is pretty important. It means that *none* of the other
>> paging-related stuff that we're doing applies.
>>
>> We also need to clarify how this can happen. Is it through something
>> than an app does, or is it solely when the hardware does something under
>> the covers, like suspend/resume.
> When you change page permissions lets say with mprotect after the and
> try to do an invalid access according to the EPCM permissions this can
> happen.

So, there are pages that are non-executable, non-readable, or
non-writable both via the page tables and via underlying SGX
permissions. Then, we allow an mprotect() and a later access will
result in one of these SGX faults?

What permissions are these, exactly? Is it even a good idea to let that
mprotect() go through in the first place?

Either way, it sounds like we have some new conditions to spell out in
that comment.

2018-09-27 15:41:57

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [PATCH v14 09/19] x86/mm: x86/sgx: Signal SEGV_SGXERR for #PFs w/ PF_SGX

On Thu, Sep 27, 2018 at 07:58:41AM -0700, Dave Hansen wrote:
> On 09/27/2018 06:42 AM, Jarkko Sakkinen wrote:
> >> This flag is 1 if the exception is unrelated to paging and
> >> resulted from violation of SGX-specific access-control
> >> requirements. ... such a violation can occur only if there
> >> is no ordinary page fault...
> >>
> >> This is pretty important. It means that *none* of the other
> >> paging-related stuff that we're doing applies.
> >>
> >> We also need to clarify how this can happen. Is it through something
> >> than an app does, or is it solely when the hardware does something under
> >> the covers, like suspend/resume.
> > When you change page permissions lets say with mprotect after the and
> > try to do an invalid access according to the EPCM permissions this can
> > happen.
>
> So, there are pages that are non-executable, non-readable, or
> non-writable both via the page tables and via underlying SGX
> permissions. Then, we allow an mprotect() and a later access will
> result in one of these SGX faults?

The permissions are intersection of PTE and EPCM permissions.

EPCM permissions are part of the enclave measurement. For SGX1 they are
static. For SGX2 they can be changed with EMODPR/EACCEPT protocol (i.e.
measurement can be updated after enclave initialization).

> What permissions are these, exactly? Is it even a good idea to let that
> mprotect() go through in the first place?

You define RWX for each page when you do EADD.

> Either way, it sounds like we have some new conditions to spell out in
> that comment.

Agreed.

/Jarkko

2018-09-27 15:55:47

by Dave Hansen

[permalink] [raw]
Subject: Re: [PATCH v14 09/19] x86/mm: x86/sgx: Signal SEGV_SGXERR for #PFs w/ PF_SGX

On 09/27/2018 08:39 AM, Jarkko Sakkinen wrote:
> On Thu, Sep 27, 2018 at 07:58:41AM -0700, Dave Hansen wrote:
>> On 09/27/2018 06:42 AM, Jarkko Sakkinen wrote:
>>>> This flag is 1 if the exception is unrelated to paging and
>>>> resulted from violation of SGX-specific access-control
>>>> requirements. ... such a violation can occur only if there
>>>> is no ordinary page fault...
>>>>
>>>> This is pretty important. It means that *none* of the other
>>>> paging-related stuff that we're doing applies.
>>>>
>>>> We also need to clarify how this can happen. Is it through something
>>>> than an app does, or is it solely when the hardware does something under
>>>> the covers, like suspend/resume.
>>> When you change page permissions lets say with mprotect after the and
>>> try to do an invalid access according to the EPCM permissions this can
>>> happen.
>>
>> So, there are pages that are non-executable, non-readable, or
>> non-writable both via the page tables and via underlying SGX
>> permissions. Then, we allow an mprotect() and a later access will
>> result in one of these SGX faults?
>
> The permissions are intersection of PTE and EPCM permissions.

Right, but this *fault* bit is not.

> EPCM permissions are part of the enclave measurement. For SGX1 they are
> static. For SGX2 they can be changed with EMODPR/EACCEPT protocol (i.e.
> measurement can be updated after enclave initialization).

What does this all have to do with enclave measurement?

>> What permissions are these, exactly? Is it even a good idea to let that
>> mprotect() go through in the first place?
>
> You define RWX for each page when you do EADD.

Are those permissions reflected into the VMAs mapping the enclave memory?


2018-09-27 18:43:11

by Eric W. Biederman

[permalink] [raw]
Subject: Re: [PATCH v14 08/19] signal: x86/sgx: Add SIGSEGV siginfo code for SGX EPCM fault

Jarkko Sakkinen <[email protected]> writes:

> From: Sean Christopherson <[email protected]>
>
> The SGX Enclave Page Cache Map (EPCM) is a hardware-managed table
> that enforces accesses to an enclave's EPC page in addition to the
> software-managed kernel page tables, i.e. the effective permissions
> for an EPC page are a logical AND of the kernel's page tables and
> the corresponding EPCM entry. The primary purpose of the EPCM is
> to prevent a malcious or compromised kernel from attacking an enclave
> by modifying the enclave's page tables. The EPCM entires for an
> enclave are populated when the enclave is built and verified, using
> metadata provided by the enclave that is included in the measurement
> used to verify the enclave.
>
> In normal operation of a properly functioning, non-malicious kernel
> (and enclave), the EPCM permissions will never trigger a fault, i.e.
> the kernel may make the permissions for an EPC page more restrictive,
> e.g. mark it not-present to swap out the EPC page, but the kernel will
> never make its permissions less restrictive.
>
> But, there is a legitimate scenario in which the kernel's page tables
> can become less restrictive than the EPCM: on current hardware all
> enclaves are destroyed (by hardware) on a transition to S3 or lower
> sleep states, i.e. all EPCM entries are invalid (not-present) after
> the system resumes from its sleep state.
>
> Unfortunately, on CPUs that support only SGX1, EPCM violations result
> in a #GP. The upside of the #GP is that no kernel changes are needed
> to deal with the EPCM being blasted away by hardware, e.g. userspace
> gets a SIGSEGV, assumes the EPCM was lost and restarts its enclave
> and everyone is happy. The downside is that userspace has to assume
> the SIGSEGV was because the EPC was lost (or possibly do some leg work
> to rule out other causes).
>
> In SGX2, the oddity of delivering a #GP due to what are inherently
> paging related violations is remedied. CPUs that support SGX2 deliver
> EPCM violations as #PFs with a new SGX error code bit set. So, now
> that hardware provides us with a way to unequivocally determine that
> a fault was due to a EPCM violation, define a signfo code for SIGSEGV
> so that the information can be passed onto userspace.
>
> Cc: Dave Hansen <[email protected]>
> Signed-off-by: Sean Christopherson <[email protected]>
> Signed-off-by: Jarkko Sakkinen <[email protected]>
> ---
> include/uapi/asm-generic/siginfo.h | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/include/uapi/asm-generic/siginfo.h b/include/uapi/asm-generic/siginfo.h
> index 80e2a7227205..fdd898e2325b 100644
> --- a/include/uapi/asm-generic/siginfo.h
> +++ b/include/uapi/asm-generic/siginfo.h
> @@ -225,7 +225,11 @@ typedef struct siginfo {
> #else
> # define SEGV_PKUERR 4 /* failed protection key checks */
> #endif
> +#ifdef __x86_64__
> +#define SEGV_SGXERR 5 /* SGX Enclave Page Cache Map fault */
> +#else
> #define SEGV_ACCADI 5 /* ADI not enabled for mapped object */
> +#endif

Don't do this crazy ifdef thing. si_codes are not supposed to be per
architecture. There are a few historical bugs but with a 32bit space
it is just stupid to add #ifdefs.

Just set.
#define SEGV_SGXERR 8 and increase NSIGSEGV

Anything else is just asking for trouble. Especially when you want to
get SGX working on itaninum.

> #define SEGV_ADIDERR 6 /* Disrupting MCD error */
> #define SEGV_ADIPERR 7 /* Precise MCD exception */
> #define NSIGSEGV 7

Eric

2018-09-27 19:44:32

by Eric W. Biederman

[permalink] [raw]
Subject: Re: [PATCH v14 09/19] x86/mm: x86/sgx: Signal SEGV_SGXERR for #PFs w/ PF_SGX

Jarkko Sakkinen <[email protected]> writes:

> From: Sean Christopherson <[email protected]>
>
> Signal SIGSEGV(SEGV_SGXERR) for all faults with PF_SGX set in the
> error code. The PF_SGX bit is set if and only if the #PF is detected
> by the Enclave Page Cache Map (EPCM), which is consulted only after
> an access walks the kernel's page tables, i.e.:
>
> a. the access was allowed by the kernel
> b. the kernel's tables have become less restrictive than the EPCM
> c. the kernel cannot fixup the cause of the fault
>
> Noteably, (b) implies that either the kernel has botched the EPC
> mappings or the EPCM has been invalidated due to a power event. In
> either case, userspace needs to be alerted so that it can take
> appropriate action, e.g. restart the enclave. This is reinforced
> by (c) as the kernel doesn't really have any other reasonable option,
> e.g. we could kill the task or panic, but neither is warranted.
>
> Signed-off-by: Sean Christopherson <[email protected]>
> Cc: Dave Hansen <[email protected]>
> Signed-off-by: Jarkko Sakkinen <[email protected]>
> ---
> arch/x86/mm/fault.c | 20 +++++++++++++++++---
> 1 file changed, 17 insertions(+), 3 deletions(-)
>
> diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
> index 85d20516b2f3..3fb2b2838d6c 100644
> --- a/arch/x86/mm/fault.c
> +++ b/arch/x86/mm/fault.c
> @@ -960,10 +960,13 @@ static noinline void
> bad_area_access_error(struct pt_regs *regs, unsigned long error_code,
> unsigned long address, struct vm_area_struct *vma)
> {
> + int si_code = SEGV_ACCERR;
> +
> if (bad_area_access_from_pkeys(error_code, vma))
> - __bad_area(regs, error_code, address, vma, SEGV_PKUERR);
> - else
> - __bad_area(regs, error_code, address, vma, SEGV_ACCERR);
> + si_code = SEGV_PKUERR;
> + else if (unlikely(error_code & X86_PF_SGX))
> + si_code = SEGV_SGXERR;
> + __bad_area(regs, error_code, address, vma, si_code);
> }

This conflicts with a cleanup in this area I have sitting in linux-next.
It isn't in the x86 tree but you can find my siginfo tree at:

git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace.git siginfo-next

In my tree bad area no longer takes a vma parameter.

If you are going to make changes to the fault handling code this cycle
please let's figure out how to build it on top of my clean ups.

Thank you,
Eric Biederman



2018-09-28 12:24:00

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [PATCH v14 09/19] x86/mm: x86/sgx: Signal SEGV_SGXERR for #PFs w/ PF_SGX

On Thu, 2018-09-27 at 21:43 +0200, Eric W. Biederman wrote:
> Jarkko Sakkinen <[email protected]> writes:
>
> > From: Sean Christopherson <[email protected]>
> >
> > Signal SIGSEGV(SEGV_SGXERR) for all faults with PF_SGX set in the
> > error code. The PF_SGX bit is set if and only if the #PF is detected
> > by the Enclave Page Cache Map (EPCM), which is consulted only after
> > an access walks the kernel's page tables, i.e.:
> >
> > a. the access was allowed by the kernel
> > b. the kernel's tables have become less restrictive than the EPCM
> > c. the kernel cannot fixup the cause of the fault
> >
> > Noteably, (b) implies that either the kernel has botched the EPC
> > mappings or the EPCM has been invalidated due to a power event. In
> > either case, userspace needs to be alerted so that it can take
> > appropriate action, e.g. restart the enclave. This is reinforced
> > by (c) as the kernel doesn't really have any other reasonable option,
> > e.g. we could kill the task or panic, but neither is warranted.
> >
> > Signed-off-by: Sean Christopherson <[email protected]>
> > Cc: Dave Hansen <[email protected]>
> > Signed-off-by: Jarkko Sakkinen <[email protected]>
> > ---
> > arch/x86/mm/fault.c | 20 +++++++++++++++++---
> > 1 file changed, 17 insertions(+), 3 deletions(-)
> >
> > diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
> > index 85d20516b2f3..3fb2b2838d6c 100644
> > --- a/arch/x86/mm/fault.c
> > +++ b/arch/x86/mm/fault.c
> > @@ -960,10 +960,13 @@ static noinline void
> > bad_area_access_error(struct pt_regs *regs, unsigned long error_code,
> > unsigned long address, struct vm_area_struct *vma)
> > {
> > + int si_code = SEGV_ACCERR;
> > +
> > if (bad_area_access_from_pkeys(error_code, vma))
> > - __bad_area(regs, error_code, address, vma, SEGV_PKUERR);
> > - else
> > - __bad_area(regs, error_code, address, vma, SEGV_ACCERR);
> > + si_code = SEGV_PKUERR;
> > + else if (unlikely(error_code & X86_PF_SGX))
> > + si_code = SEGV_SGXERR;
> > + __bad_area(regs, error_code, address, vma, si_code);
> > }
>
> This conflicts with a cleanup in this area I have sitting in linux-next.
> It isn't in the x86 tree but you can find my siginfo tree at:
>
> git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace.git
> siginfo-next
>
> In my tree bad area no longer takes a vma parameter.
>
> If you are going to make changes to the fault handling code this cycle
> please let's figure out how to build it on top of my clean ups.

We are now going with the proposed vdso solution for v15. Thank you for
your feedback. I'll sync with you if that route would show non-feasible
(still prototyping the vdso solution).

> Thank you,
> Eric Biederman

/Jarkko

2018-10-01 14:30:07

by Sean Christopherson

[permalink] [raw]
Subject: Re: [PATCH v14 09/19] x86/mm: x86/sgx: Signal SEGV_SGXERR for #PFs w/ PF_SGX

On Wed, 2018-09-26 at 14:15 -0700, Andy Lutomirski wrote:
> On Wed, Sep 26, 2018 at 1:55 PM Dave Hansen <[email protected]> wrote:
> >
> >
> > On 09/26/2018 01:44 PM, Sean Christopherson wrote:
> > >
> > > On Wed, Sep 26, 2018 at 01:16:59PM -0700, Dave Hansen wrote:
> > > >
> > > > We also need to clarify how this can happen.  Is it through something
> > > > than an app does, or is it solely when the hardware does something under
> > > > the covers, like suspend/resume.
> > > Are you looking for something in the changelog, the comment, or just
> > > a response?  If it's the latter...
> > Comments, please.
> >
> > >
> > > On bare metal with a bug-free kernel, the only scenario I'm aware of
> > > where we'll encounter these faults is when hardware pulls the rug out
> > > from under us.  In a virtualized environment all bets are off because
> > > the architecture allows VMMs to silently "destroy" the EPC at will,
> > > e.g. KVM, and I believe Hyper-V, will take advantage of this behavior
> > > to support live migration.  Post migration, the destination system
> > > will generate PF_SGX because the EPC{M} can't be migrated between
> > > system, i.e. the destination EPCM sees all EPC pages as invalid.
> > OK, cool.
> >
> > That's good background fodder for the changelog.
> >
> > But, for the comment, I'm happy with something like this:
> >
> >         /*
> >          * The fault resulted from violation of SGX-specific access-
> >          * controls.  This is expected to be the result of some lower
> >          * layer action (CPU suspend/resume, VM migration) and is
> >          * not related to anything the OS did.  Treat it as an access
> >          * error to ensure it is passed up to the app via a signal where
> >          * it can be handled.
> >          */
> >
> > I really don't think we need to delve too deeply into the relationship
> > between EPCM and PTEs or anything.  Let's just say, "it's not the
> > kernel's fault, it's not the app's fault, so throw up our hands".
> There is a non-nitpicky consideration here.  Logically, user code is
> going to do this (totally made-up pseudocode):
>
> enclave_t enclave = load_and_init_enclave(...);
> int ret = sgx_run(enclave, some pointers to non-enclave-memory buffers, ...);
>
> and, with the code in this patch, a correct implementation of
> sgx_run() requires installing a signal handler.  This is nasty, since
> signal handlers, expecially for something like SIGSEGV or SIGBUS, are
> not fantastic to say the least in libraries.
>
> Could we perhaps have a little vDSO entry (or syscall, I suppose) that
> runs an enclave an returns an error code, and rig up the #PF handler
> to check if the error happened in the vDSO entry and fix it up rather
> than sending a signal?


If we want to avoid having to install a signal handler then I'm pretty
sure we'd need to fixup all #GPs and "bad access" #PFs that occur on
EENTER or in the enclave, not just PF_SGX faults.  SGX1 hardware takes
a #GP instead of a #PF on EPCM faults, and SGX2 hardware allows enclaves
to allocate/free/adjust EPC pages at runtime, e.g. an enclave runtime
might want to intercept #PFs from within the enclave so that the enclave
can dynamically grow its stack.

> On Windows, this is much less of a concern, because Windows has real
> scoped fault handling. But Linux doesn't, at least not yet.
>
>
> --
> Andy Lutomirski
> AMA Capital Management, LLC

2018-10-01 14:42:05

by Dave Hansen

[permalink] [raw]
Subject: Re: [PATCH v14 09/19] x86/mm: x86/sgx: Signal SEGV_SGXERR for #PFs w/ PF_SGX

On 10/01/2018 07:29 AM, Sean Christopherson wrote:
>> Could we perhaps have a little vDSO entry (or syscall, I suppose) that
>> runs an enclave an returns an error code, and rig up the #PF handler
>> to check if the error happened in the vDSO entry and fix it up rather
>> than sending a signal?
>
> If we want to avoid having to install a signal handler then I'm pretty
> sure we'd need to fixup all #GPs and "bad access" #PFs that occur on
> EENTER or in the enclave, not just PF_SGX faults.  SGX1 hardware takes
> a #GP instead of a #PF on EPCM faults, and SGX2 hardware allows enclaves
> to allocate/free/adjust EPC pages at runtime, e.g. an enclave runtime
> might want to intercept #PFs from within the enclave so that the enclave
> can dynamically grow its stack.

I think the technique Andy describes can be used for that as well. It
basically works for any case where we know which instructions will take
an exception (any exception), call the instruction from a fixed
location, and know the fault(s) it can throw.

To me, it's almost like turning these faulting instructions into mini
syscall instructions. They enter the kernel only when they need help,
though, instead of always.


2018-10-01 21:45:36

by Jethro Beekman

[permalink] [raw]
Subject: Re: [PATCH v14 09/19] x86/mm: x86/sgx: Signal SEGV_SGXERR for #PFs w/ PF_SGX

On 2018-09-27 06:56, Jarkko Sakkinen wrote:
> On Wed, Sep 26, 2018 at 02:45:17PM -0700, Dave Hansen wrote:
>> On 09/26/2018 02:15 PM, Andy Lutomirski wrote:
>>> Could we perhaps have a little vDSO entry (or syscall, I suppose) that
>>> runs an enclave an returns an error code, and rig up the #PF handler
>>> to check if the error happened in the vDSO entry and fix it up rather
>>> than sending a signal?
>
> For me this plan sounds simple and sound.

I support avoiding the need for a signal handler for various
SGX-specific operations. Looking forward to v15.

I have some thoughts regarding the design of the vDSO function. Please
consider the following as you work on the next patch set.

1) Even though the vDSO function exists, userspace may still call
`ENCLU[EENTER]` manually, so the fault handling as described in the
current patch should also be maintained.

2) All the information that would normally be provided through the
signal handler (x86 fault number, reason) should be provided to userspace.

3) vDSO functions should be provided for all standard non-enclave ENCLU
leafs, and should support most ways that an application might want to
use. This includes:

* EENTER with a automatic AEX handler (as in Jarkko's sgx_get_token example)
* EENTER & ERESUME with a user-specified AEX handler, or possibly just a
special return value from the ENCLU function on AEX

4) I think the vDSO functions should have a special calling convention
(not conforming to the standard SysV ABI), such that most registers are
passed between user space and enclave space untouched. Basically, only
RAX, RBX, RCX are available as input and output registers.

--
Jethro Beekman | Fortanix


Attachments:
smime.p7s (3.90 kB)
S/MIME Cryptographic Signature

2018-10-01 22:06:47

by Dave Hansen

[permalink] [raw]
Subject: Re: [PATCH v14 09/19] x86/mm: x86/sgx: Signal SEGV_SGXERR for #PFs w/ PF_SGX

On 10/01/2018 02:42 PM, Jethro Beekman wrote:
>
> 1) Even though the vDSO function exists, userspace may still call
> `ENCLU[EENTER]` manually, so the fault handling as described in the
> current patch should also be maintained.

Why?

2018-10-02 00:07:56

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [PATCH v14 09/19] x86/mm: x86/sgx: Signal SEGV_SGXERR for #PFs w/ PF_SGX

On Mon, Oct 01, 2018 at 07:29:03AM -0700, Sean Christopherson wrote:
> On Wed, 2018-09-26 at 14:15 -0700, Andy Lutomirski wrote:
> > runs an enclave an returns an error code, and rig up the #PF handler
> > to check if the error happened in the vDSO entry and fix it up rather
> > than sending a signal?
>
>
> If we want to avoid having to install a signal handler then I'm pretty
> sure we'd need to fixup all #GPs and "bad access" #PFs that occur on
> EENTER or in the enclave, not just PF_SGX faults. ?SGX1 hardware takes
> a #GP instead of a #PF on EPCM faults, and SGX2 hardware allows enclaves
> to allocate/free/adjust EPC pages at runtime, e.g. an enclave runtime
> might want to intercept #PFs from within the enclave so that the enclave
> can dynamically grow its stack.

If I've understood Andy's proposal correctly, the run-time would get the
same information as with a signal. The delivery path for this
information would be just different.

/Jarkko

2018-10-02 00:32:04

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [PATCH v14 09/19] x86/mm: x86/sgx: Signal SEGV_SGXERR for #PFs w/ PF_SGX

On Mon, Oct 01, 2018 at 09:42:48PM +0000, Jethro Beekman wrote:
> 1) Even though the vDSO function exists, userspace may still call
> `ENCLU[EENTER]` manually, so the fault handling as described in the current
> patch should also be maintained.

You mean the way it was is in v13 and not the way it is in v14?

> 2) All the information that would normally be provided through the signal
> handler (x86 fault number, reason) should be provided to userspace.

As I've understood it, this should be just a change in the delivery path.

/Jarkko

2018-10-04 18:01:47

by Sean Christopherson

[permalink] [raw]
Subject: Re: [PATCH v14 15/19] platform/x86: Intel SGX driver

On Tue, 2018-09-25 at 16:06 +0300, Jarkko Sakkinen wrote:
> Intel Software Guard eXtensions (SGX) is a set of CPU instructions that
> can be used by applications to set aside private regions of code and
> data. The code outside the enclave is disallowed to access the memory
> inside the enclave by the CPU access control.
>
> SGX driver provides a ioctl API for loading and initializing enclaves.
> Address range for enclaves is reserved with mmap() and they are
> destroyed with munmap(). Enclave construction, measurement and
> initialization is done with the provided the ioctl API.

...

> +/**
> + * sgx_ioc_enclave_init - handler for %SGX_IOC_ENCLAVE_INIT
> + *
> + * @filep: open file to /dev/sgx
> + * @cmd: the command value
> + * @arg: pointer to an &sgx_enclave_init instance
> + *
> + * Flushes the remaining enqueued EADD operations and performs EINIT. Does not
> + * allow the EINITTOKENKEY attribute for an enclave.
> + *
> + * Return:
> + *   0 on success,
> + *   SGX error code on EINIT failure,
> + *   -errno otherwise
> + */
> +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;
> + struct sgx_sigstruct *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);
> + memset(einittoken, 0, sizeof(*einittoken));
> +
> + ret = copy_from_user(sigstruct, (void __user *)initp->sigstruct,
> +      sizeof(*sigstruct));
> + if (ret)
> + goto out;
> + if (sigstruct->attributes & SGX_ATTR_EINITTOKENKEY) {
> + ret = EINVAL;

This should be "ret = -EINVAL".

> + goto out;
> + }
> +
> + ret = sgx_encl_get(initp->addr, &encl);
> + if (ret)
> + goto out;
> +
> + ret = sgx_encl_init(encl, sigstruct, einittoken);
> +
> + kref_put(&encl->refcount, sgx_encl_release);
> +
> +out:
> + kunmap(initp_page);
> + __free_page(initp_page);
> + return ret;
> +}


2018-10-05 11:34:50

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [PATCH v14 15/19] platform/x86: Intel SGX driver

On Thu, Oct 04, 2018 at 11:01:17AM -0700, Sean Christopherson wrote:
> On Tue, 2018-09-25 at 16:06 +0300, Jarkko Sakkinen wrote:
> > Intel Software Guard eXtensions (SGX) is a set of CPU instructions that
> > can be used by applications to set aside private regions of code and
> > data. The code outside the enclave is disallowed to access the memory
> > inside the enclave by the CPU access control.
> >
> > SGX driver provides a ioctl API for loading and initializing enclaves.
> > Address range for enclaves is reserved with mmap() and they are
> > destroyed with munmap(). Enclave construction, measurement and
> > initialization is done with the provided the ioctl API.
>
> ...
>
> > +/**
> > + * sgx_ioc_enclave_init - handler for %SGX_IOC_ENCLAVE_INIT
> > + *
> > + * @filep: open file to /dev/sgx
> > + * @cmd: the command value
> > + * @arg: pointer to an &sgx_enclave_init instance
> > + *
> > + * Flushes the remaining enqueued EADD operations and performs EINIT. Does not
> > + * allow the EINITTOKENKEY attribute for an enclave.
> > + *
> > + * Return:
> > + *???0 on success,
> > + *???SGX error code on EINIT failure,
> > + *???-errno otherwise
> > + */
> > +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;
> > + struct sgx_sigstruct *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);
> > + memset(einittoken, 0, sizeof(*einittoken));
> > +
> > + ret = copy_from_user(sigstruct, (void __user *)initp->sigstruct,
> > + ?????sizeof(*sigstruct));
> > + if (ret)
> > + goto out;
> > + if (sigstruct->attributes & SGX_ATTR_EINITTOKENKEY) {
> > + ret = EINVAL;
>
> This should be "ret = -EINVAL".

Ouch :-( Thank you for spotting this out.

/Jarkko

2018-10-15 20:55:15

by Pavel Machek

[permalink] [raw]
Subject: Re: [PATCH v14 19/19] x86/sgx: Driver documentation

On Tue 2018-09-25 16:06:56, Jarkko Sakkinen wrote:
> Documentation of the features of the Software Guard eXtensions used
> by the Linux kernel and basic design choices for the core and driver
> and functionality.
>
> Signed-off-by: Jarkko Sakkinen <[email protected]>

> --- /dev/null
> +++ b/Documentation/x86/intel_sgx.rst
> @@ -0,0 +1,185 @@
> +===================
> +Intel(R) SGX driver
> +===================
> +
> +Introduction
> +============
> +
> +Intel(R) SGX is a set of CPU instructions that can be used by applications to
> +set aside private regions of code and data. The code outside the enclave is
> +disallowed to access the memory inside the enclave by the CPU access control.
> +In a way you can think that SGX provides inverted sandbox. It protects the
> +application from a malicious host.

Well, recently hardware had some problems keeping its
promises. So... what about rowhammer, meltdown and spectre?

Which ones apply, which ones do not, and on what cpu generations?

> +Overview of SGX
> +===============
> +
> +SGX has a set of data structures to maintain information about the enclaves and
> +their security properties. BIOS reserves a fixed size region of physical memory
> +for these structures by setting Processor Reserved Memory Range Registers
> +(PRMRR).
> +
> +This memory range is protected from outside access by the CPU and all the data
> +coming in and out of the CPU package is encrypted by a key that is generated for
> +each boot cycle.

Encryption, that sounds nice, but it is hard to do right. If SGX
protected code changes single bit in its memory, how many bits will be
changed in physical RAM?

Can we get security people to look at this and perhaps tells us what
properties it has?

Thanks,
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html


Attachments:
(No filename) (1.91 kB)
signature.asc (188.00 B)
Digital signature
Download all attachments

2018-10-17 23:46:20

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [PATCH v14 19/19] x86/sgx: Driver documentation

On Mon, 15 Oct 2018, Pavel Machek wrote:
> On Tue 2018-09-25 16:06:56, Jarkko Sakkinen wrote:
>> +Intel(R) SGX is a set of CPU instructions that can be used by applications to
>> +set aside private regions of code and data. The code outside the enclave is
>> +disallowed to access the memory inside the enclave by the CPU access control.
>> +In a way you can think that SGX provides inverted sandbox. It protects the
>> +application from a malicious host.
>
> Well, recently hardware had some problems keeping its
> promises. So... what about rowhammer, meltdown and spectre?

Doesn't hardware always have this problem over time?

> Which ones apply, which ones do not, and on what cpu generations?

Definitely should be refined.

Meltdowns approach AFAIK does not work because reads outside the enclave
will always have a predefined value (-1) but only if the page is present,
which was later exploited in the Foreshadow attack.

> Encryption, that sounds nice, but it is hard to do right. If SGX
> protected code changes single bit in its memory, how many bits will be
> changed in physical RAM?

512-bit blocks and merkle tree based mac. It is pretty well documented
in https://eprint.iacr.org/2016/204.pdf. I'll take not to myself to add
this to the references.

Thanks for the feedback. The ocumentation is hard to drive forward w/o it.

/Jarkko

2018-10-17 23:57:29

by Dave Hansen

[permalink] [raw]
Subject: Re: [PATCH v14 19/19] x86/sgx: Driver documentation

On 10/15/2018 01:54 PM, Pavel Machek wrote:
>> +Intel(R) SGX is a set of CPU instructions that can be used by applications to
>> +set aside private regions of code and data. The code outside the enclave is
>> +disallowed to access the memory inside the enclave by the CPU access control.
>> +In a way you can think that SGX provides inverted sandbox. It protects the
>> +application from a malicious host.
> Well, recently hardware had some problems keeping its
> promises. So... what about rowhammer, meltdown and spectre?

There's a ton of documentation out there about what kinds of protections
SGX provides. I don't think this is an appropriate place to have an
exhaustive discussion about it. But, there's extensive discussion of it
on Intel's security site:

https://software.intel.com/security-software-guidance/

There's documentation on how L1TF affects SGX here:

https://software.intel.com/security-software-guidance/software-guidance/l1-terminal-fault

Or Spectre v2 here:

https://software.intel.com/security-software-guidance/software-guidance/bounds-check-bypass

> Which ones apply, which ones do not, and on what cpu generations?

The CVEs list this in pretty exhaustive detail. The L1TF/SGX one, for
example:

https://nvd.nist.gov/vuln/detail/CVE-2018-3615

Lists a bunch of processor models.


2018-10-18 09:58:13

by Pavel Machek

[permalink] [raw]
Subject: Re: [PATCH v14 19/19] x86/sgx: Driver documentation

On Thu 2018-10-18 02:45:27, Jarkko Sakkinen wrote:
> On Mon, 15 Oct 2018, Pavel Machek wrote:
> >On Tue 2018-09-25 16:06:56, Jarkko Sakkinen wrote:
> >>+Intel(R) SGX is a set of CPU instructions that can be used by applications to
> >>+set aside private regions of code and data. The code outside the enclave is
> >>+disallowed to access the memory inside the enclave by the CPU access control.
> >>+In a way you can think that SGX provides inverted sandbox. It protects the
> >>+application from a malicious host.
> >
> >Well, recently hardware had some problems keeping its
> >promises. So... what about rowhammer, meltdown and spectre?
>
> Doesn't hardware always have this problem over time?

No, not really.

In this case, tries to protect from hardware "attacks" done by machine
owner. That job is theoretically impossible, so you have harder
situation than most..

> >Which ones apply, which ones do not, and on what cpu generations?
>
> Definitely should be refined.
>
> Meltdowns approach AFAIK does not work because reads outside the enclave
> will always have a predefined value (-1) but only if the page is present,
> which was later exploited in the Foreshadow attack.

What about L1tf and https://github.com/lsds/spectre-attack-sgx ?

Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html


Attachments:
(No filename) (1.40 kB)
signature.asc (188.00 B)
Digital signature
Download all attachments

2018-10-20 00:00:53

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [PATCH v14 19/19] x86/sgx: Driver documentation

On Thu, 18 Oct 2018, Pavel Machek wrote:
>> Definitely should be refined.
>>
>> Meltdowns approach AFAIK does not work because reads outside the enclave
>> will always have a predefined value (-1) but only if the page is present,
>> which was later exploited in the Foreshadow attack.
>
> What about L1tf and https://github.com/lsds/spectre-attack-sgx ?

L1TF is the vuln and Foreshadow is the attack taking advantage of the
vuln. I didn't mean to patch the documention in my response or give
extensive list of the vulns if you expected that.

For kernel documentation it does make sense to give a threat model
but not enumerate every possible vuln.

/Jarkko

2018-10-31 21:31:36

by Sean Christopherson

[permalink] [raw]
Subject: Re: [PATCH v14 09/19] x86/mm: x86/sgx: Signal SEGV_SGXERR for #PFs w/ PF_SGX

On Mon, Oct 01, 2018 at 03:03:30PM -0700, Dave Hansen wrote:
> On 10/01/2018 02:42 PM, Jethro Beekman wrote:
> >
> > 1) Even though the vDSO function exists, userspace may still call
> > `ENCLU[EENTER]` manually, so the fault handling as described in the
> > current patch should also be maintained.
>
> Why?

Circling back to this question, what if we take the easy way out and
simply signal SIGSEGV without an SGX-specific code? I.e. treat #PF
with X86_PF_SGX as an access error, no more no less. That should be
sufficient for userspace to function, albeit with a little more effort,
but presumably no more than would be needed to run on SGX1 hardware.

AFAIK there isn't a way to prevent userspace from manually invoking
EENTER, short of doing some really nasty text poking or PTE swizzling.
We could declare using EENTER as unsupported, but that seems like
cutting off the nose to spite the face. Supporting userspace EENTER
in a limited capacity would allow people to do whatever crazy tricks
they're wont to do without having to deal with absurd requests for
the vDSO interface.

If we go this route we could also add the vDSO stuff after basic SGX
support is in mainline, obviously with approval from the powers that
be.

2018-10-31 21:36:17

by Dave Hansen

[permalink] [raw]
Subject: Re: [PATCH v14 09/19] x86/mm: x86/sgx: Signal SEGV_SGXERR for #PFs w/ PF_SGX

On 10/31/18 2:30 PM, Sean Christopherson wrote:
> On Mon, Oct 01, 2018 at 03:03:30PM -0700, Dave Hansen wrote:
>> On 10/01/2018 02:42 PM, Jethro Beekman wrote:
>>>
>>> 1) Even though the vDSO function exists, userspace may still call
>>> `ENCLU[EENTER]` manually, so the fault handling as described in the
>>> current patch should also be maintained.
>>
>> Why?
>
> Circling back to this question, what if we take the easy way out and
> simply signal SIGSEGV without an SGX-specific code? I.e. treat #PF
> with X86_PF_SGX as an access error, no more no less. That should be
> sufficient for userspace to function, albeit with a little more effort,
> but presumably no more than would be needed to run on SGX1 hardware.

There are two sides to this ABI: what the kernel does to support SGX and
what userspace does. If we do what you suggest, we remove any (most?)
needed kernel changes and foist the burden entirely into userspace.
But, we end up with two ABIs: the old one and the new vDSO one.

IOW, once we start doing SIGSEGV, we have to do it forever, despite if
we have a newer mechanism.

> AFAIK there isn't a way to prevent userspace from manually invoking
> EENTER, short of doing some really nasty text poking or PTE swizzling.
> We could declare using EENTER as unsupported,

Yep, userspace can call it all it wants, and we can also say that
calling it outside the vdso is "undefined".

2018-11-01 17:45:33

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [PATCH v14 09/19] x86/mm: x86/sgx: Signal SEGV_SGXERR for #PFs w/ PF_SGX



On Thu, 1 Nov 2018, Jarkko Sakkinen wrote:

> On Wed, 31 Oct 2018, Sean Christopherson wrote:
>> On Mon, Oct 01, 2018 at 03:03:30PM -0700, Dave Hansen wrote:
>>> On 10/01/2018 02:42 PM, Jethro Beekman wrote:
>>>>
>>>> 1) Even though the vDSO function exists, userspace may still call
>>>> `ENCLU[EENTER]` manually, so the fault handling as described in the
>>>> current patch should also be maintained.
>>>
>>> Why?
>>
>> Circling back to this question, what if we take the easy way out and
>> simply signal SIGSEGV without an SGX-specific code? I.e. treat #PF
>> with X86_PF_SGX as an access error, no more no less. That should be
>> sufficient for userspace to function, albeit with a little more effort,
>> but presumably no more than would be needed to run on SGX1 hardware.
>>
>> AFAIK there isn't a way to prevent userspace from manually invoking
>> EENTER, short of doing some really nasty text poking or PTE swizzling.
>> We could declare using EENTER as unsupported, but that seems like
>> cutting off the nose to spite the face. Supporting userspace EENTER
>> in a limited capacity would allow people to do whatever crazy tricks
>> they're wont to do without having to deal with absurd requests for
>> the vDSO interface.
>>
>> If we go this route we could also add the vDSO stuff after basic SGX
>> support is in mainline, obviously with approval from the powers that
>> be.
>>
>
> Yeah, this would give stable behavior when vDSO functions are not
> available.
>
> Here's a question: if we implement this behavior, could be upstream
> series without vDSO's first and after those changes have been landed
> we would continue with the vDSO's?

Right, it was in your last paragraph, sorry. Yeah, I fully support this
idea. It will be easier also to work on the vDSO's once we have something
landed (instead of working on a moving platform).

/Jarkko

2018-11-01 18:12:09

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [PATCH v14 09/19] x86/mm: x86/sgx: Signal SEGV_SGXERR for #PFs w/ PF_SGX

On Wed, 31 Oct 2018, Sean Christopherson wrote:
> On Mon, Oct 01, 2018 at 03:03:30PM -0700, Dave Hansen wrote:
>> On 10/01/2018 02:42 PM, Jethro Beekman wrote:
>>>
>>> 1) Even though the vDSO function exists, userspace may still call
>>> `ENCLU[EENTER]` manually, so the fault handling as described in the
>>> current patch should also be maintained.
>>
>> Why?
>
> Circling back to this question, what if we take the easy way out and
> simply signal SIGSEGV without an SGX-specific code? I.e. treat #PF
> with X86_PF_SGX as an access error, no more no less. That should be
> sufficient for userspace to function, albeit with a little more effort,
> but presumably no more than would be needed to run on SGX1 hardware.
>
> AFAIK there isn't a way to prevent userspace from manually invoking
> EENTER, short of doing some really nasty text poking or PTE swizzling.
> We could declare using EENTER as unsupported, but that seems like
> cutting off the nose to spite the face. Supporting userspace EENTER
> in a limited capacity would allow people to do whatever crazy tricks
> they're wont to do without having to deal with absurd requests for
> the vDSO interface.
>
> If we go this route we could also add the vDSO stuff after basic SGX
> support is in mainline, obviously with approval from the powers that
> be.
>

Yeah, this would give stable behavior when vDSO functions are not
available.

Here's a question: if we implement this behavior, could be upstream
series without vDSO's first and after those changes have been landed
we would continue with the vDSO's?

/Jarkko