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
v19:
* Took 3-4 months but in some sense this was more like a rewrite of most
of the corners of the source code. If I've forgotten to deal with some
feedback, please don't shout me. Make a remark and I will fix it for
the next version. Hopefully there won't be this big turnovers anymore.
* Validate SECS attributes properly against CPUID given attributes and
against allowed attributes. SECS attributes are the ones that are
enforced whereas SIGSTRUCT attributes tell what is required to run
the enclave.
* Add KSS (Key Sharing Support) to the enclave attributes.
* Deny MAP_PRIVATE as an enclave is always a shared memory entity.
* Revert back to shmem backing storage so that it can be easily shared
by multiple processes.
* Split the recognization of an ENCLS leaf failure by using three
functions to detect it: encsl_faulted(), encls_returned_code() and
sgx_failed(). encls_failed() is only caused by a spurious expections that
should never happen. Thus, it is not defined as an inline function in
order to easily insert a kprobe to it.
* Move low-level enclave management routines, page fault handler and page
reclaiming routines from driver to the core. These cannot be separated
from each other as they are heavily interdependent. The rationale is that
the core does not call any code from the driver.
* Allow the driver to be compiled as a module now that it no code is using
its routines and it only uses exported symbols. Now the driver is
essentially just a thin ioctl layer.
* Reworked the driver to maintain a list of mm_struct's. The VMA callbacks
add new entries to this list as the process is forked. Each entry has
its own refcount because they have a different life-cycle as the enclave
does. In effect @tgid and @mm have been removed from struct sgx_encl
and we allow forking by removing VM_DONTCOPY from vm flags.
* Generate a cpu mask in the reclaimer from the cpu mask's of all
mm_struct's. This will kick out the hardware threads out of the enclave
from multiple processes. It is not a local variable because it would
eat too much of the stack space but instead a field in struct
sgx_encl.
* Allow forking i.e. remove VM_DONTCOPY. I did not change the API
because the old API scaled to the workload that Andy described. The
codebase is now mostly API independent i.e. changing the API is a
small task. For me the proper trigger to chanage it is a as concrete
as possible workload that cannot be fulfilled. I hope you understand
my thinking here. I don't want to change anything w/o proper basis
but I'm ready to change anything if there is a proper basis. I do
not have any kind of attachment to any particular type of API.
* Add Sean's vDSO ENCLS(EENTER) patches and update selftest to use the
new vDSO.
v18:
* Update the ioctl-number.txt.
* Move the driver under arch/x86.
* Add SGX features (SGX, SGX1, SGX2) to the disabled-features.h.
* Rename the selftest as test_sgx (previously sgx-selftest).
* In order to enable process accounting, swap EPC pages and PCMD's to a VMA
instead of shmem.
* Allow only to initialize and run enclaves with a subset of
{DEBUG, MODE64BIT} set.
* Add SGX_IOC_ENCLAVE_SET_ATTRIBUTE to allow an enclave to have privileged
attributes e.g. PROVISIONKEY.
v17:
* Add a simple selftest.
* Fix a null pointer dereference to section->pages when its
allocation fails.
* Add Sean's description of the exception handling to the documentation.
v16:
* Fixed SOB's in the commits that were a bit corrupted in v15.
* Implemented exceptio handling properly to detect_sgx().
* Use GENMASK() to define SGX_CPUID_SUB_LEAF_TYPE_MASK.
* Updated the documentation to use rst definition lists.
* Added the missing Documentation/x86/index.rst, which has a link to
intel_sgx.rst. Now the SGX and uapi documentation is properly generated
with 'make htmldocs'.
* While enumerating EPC sections, if an undefined section is found, fail
the driver initialization instead of continuing the initialization.
* Issue a warning if there are more than %SGX_MAX_EPC_SECTIONS.
* Remove copyright notice from arch/x86/include/asm/sgx.h.
* Migrated from ioremap_cache() to memremap().
v15:
* Split into more digestable size patches.
* Lots of small fixes and clean ups.
* Signal a "plain" SIGSEGV on an EPCM violation.
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 (12):
x86/sgx: Add ENCLS architectural error codes
x86/sgx: Add SGX1 and SGX2 architectural data structures
x86/sgx: Add definitions for SGX's CPUID leaf and variable sub-leafs
x86/sgx: Add wrappers for ENCLS leaf functions
x86/sgx: Add functions to allocate and free EPC pages
x86/sgx: Add the Linux SGX Enclave Driver
x86/sgx: Add provisioning
x86/sgx: Add swapping code to the core and SGX driver
x86/sgx: ptrace() support for the SGX driver
x86/sgx: SGX documentation
selftests/x86: Add a selftest for SGX
x86/sgx: Update MAINTAINERS
Kai Huang (2):
x86/cpufeatures: Add Intel-defined SGX feature bit
x86/cpufeatures: Add Intel-defined SGX_LC feature bit
Sean Christopherson (13):
x86/cpufeatures: Add SGX sub-features (as Linux-defined bits)
x86/msr: Add IA32_FEATURE_CONTROL.SGX_ENABLE definition
x86/msr: Add SGX Launch Control MSR definitions
x86/mm: x86/sgx: Add new 'PF_SGX' page fault error code bit
x86/mm: x86/sgx: Signal SIGSEGV for userspace #PFs w/ PF_SGX
x86/cpu/intel: Detect SGX support and update caps appropriately
x86/sgx: Enumerate and track EPC sections
x86/sgx: Add sgx_einit() for initializing enclaves
x86/vdso: Add support for exception fixup in vDSO functions
x86/fault: Add helper function to sanitize error code
x86/fault: Attempt to fixup unhandled #PF in vDSO before signaling
x86/traps: Attempt to fixup exceptions in vDSO before signaling
x86/vdso: Add __vdso_sgx_enter_enclave() to wrap SGX enclave
transitions
Documentation/index.rst | 1 +
Documentation/ioctl/ioctl-number.txt | 1 +
Documentation/x86/index.rst | 10 +
Documentation/x86/sgx.rst | 234 +++++
MAINTAINERS | 12 +
arch/x86/Kconfig | 37 +
arch/x86/entry/vdso/Makefile | 6 +-
arch/x86/entry/vdso/extable.c | 37 +
arch/x86/entry/vdso/extable.h | 29 +
arch/x86/entry/vdso/vdso-layout.lds.S | 9 +-
arch/x86/entry/vdso/vdso.lds.S | 1 +
arch/x86/entry/vdso/vdso2c.h | 58 +-
arch/x86/entry/vdso/vsgx_enter_enclave.S | 97 ++
arch/x86/include/asm/cpufeatures.h | 24 +-
arch/x86/include/asm/disabled-features.h | 14 +-
arch/x86/include/asm/msr-index.h | 8 +
arch/x86/include/asm/traps.h | 1 +
arch/x86/include/asm/vdso.h | 5 +
arch/x86/include/uapi/asm/sgx.h | 90 ++
arch/x86/include/uapi/asm/sgx_errno.h | 91 ++
arch/x86/kernel/cpu/Makefile | 1 +
arch/x86/kernel/cpu/intel.c | 39 +
arch/x86/kernel/cpu/scattered.c | 2 +
arch/x86/kernel/cpu/sgx/Makefile | 5 +
arch/x86/kernel/cpu/sgx/arch.h | 424 +++++++++
arch/x86/kernel/cpu/sgx/driver/Makefile | 3 +
arch/x86/kernel/cpu/sgx/driver/driver.h | 38 +
arch/x86/kernel/cpu/sgx/driver/ioctl.c | 893 ++++++++++++++++++
arch/x86/kernel/cpu/sgx/driver/main.c | 337 +++++++
arch/x86/kernel/cpu/sgx/encl.c | 718 ++++++++++++++
arch/x86/kernel/cpu/sgx/encl.h | 126 +++
arch/x86/kernel/cpu/sgx/encls.c | 22 +
arch/x86/kernel/cpu/sgx/encls.h | 244 +++++
arch/x86/kernel/cpu/sgx/main.c | 356 +++++++
arch/x86/kernel/cpu/sgx/reclaim.c | 410 ++++++++
arch/x86/kernel/cpu/sgx/sgx.h | 101 ++
arch/x86/kernel/traps.c | 14 +
arch/x86/mm/fault.c | 44 +-
tools/arch/x86/include/asm/cpufeatures.h | 21 +-
tools/testing/selftests/x86/Makefile | 10 +
tools/testing/selftests/x86/sgx/Makefile | 48 +
tools/testing/selftests/x86/sgx/defines.h | 39 +
tools/testing/selftests/x86/sgx/encl.c | 20 +
tools/testing/selftests/x86/sgx/encl.lds | 33 +
.../selftests/x86/sgx/encl_bootstrap.S | 94 ++
tools/testing/selftests/x86/sgx/encl_piggy.S | 18 +
tools/testing/selftests/x86/sgx/encl_piggy.h | 14 +
tools/testing/selftests/x86/sgx/main.c | 280 ++++++
tools/testing/selftests/x86/sgx/sgx_call.S | 15 +
tools/testing/selftests/x86/sgx/sgxsign.c | 508 ++++++++++
.../testing/selftests/x86/sgx/signing_key.pem | 39 +
51 files changed, 5649 insertions(+), 32 deletions(-)
create mode 100644 Documentation/x86/index.rst
create mode 100644 Documentation/x86/sgx.rst
create mode 100644 arch/x86/entry/vdso/extable.c
create mode 100644 arch/x86/entry/vdso/extable.h
create mode 100644 arch/x86/entry/vdso/vsgx_enter_enclave.S
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/sgx/Makefile
create mode 100644 arch/x86/kernel/cpu/sgx/arch.h
create mode 100644 arch/x86/kernel/cpu/sgx/driver/Makefile
create mode 100644 arch/x86/kernel/cpu/sgx/driver/driver.h
create mode 100644 arch/x86/kernel/cpu/sgx/driver/ioctl.c
create mode 100644 arch/x86/kernel/cpu/sgx/driver/main.c
create mode 100644 arch/x86/kernel/cpu/sgx/encl.c
create mode 100644 arch/x86/kernel/cpu/sgx/encl.h
create mode 100644 arch/x86/kernel/cpu/sgx/encls.c
create mode 100644 arch/x86/kernel/cpu/sgx/encls.h
create mode 100644 arch/x86/kernel/cpu/sgx/main.c
create mode 100644 arch/x86/kernel/cpu/sgx/reclaim.c
create mode 100644 arch/x86/kernel/cpu/sgx/sgx.h
create mode 100644 tools/testing/selftests/x86/sgx/Makefile
create mode 100644 tools/testing/selftests/x86/sgx/defines.h
create mode 100644 tools/testing/selftests/x86/sgx/encl.c
create mode 100644 tools/testing/selftests/x86/sgx/encl.lds
create mode 100644 tools/testing/selftests/x86/sgx/encl_bootstrap.S
create mode 100644 tools/testing/selftests/x86/sgx/encl_piggy.S
create mode 100644 tools/testing/selftests/x86/sgx/encl_piggy.h
create mode 100644 tools/testing/selftests/x86/sgx/main.c
create mode 100644 tools/testing/selftests/x86/sgx/sgx_call.S
create mode 100644 tools/testing/selftests/x86/sgx/sgxsign.c
create mode 100644 tools/testing/selftests/x86/sgx/signing_key.pem
--
2.19.1
From: Kai Huang <[email protected]>
X86_FEATURE_SGX reflects whether or not the CPU supports Intel's
Software Guard eXtensions (SGX).
Signed-off-by: Kai Huang <[email protected]>
Co-developed-by: Jarkko Sakkinen <[email protected]>
Signed-off-by: Jarkko Sakkinen <[email protected]>
Reviewed-by: Borislav Petkov <[email protected]>
---
arch/x86/include/asm/cpufeatures.h | 1 +
arch/x86/include/asm/disabled-features.h | 8 +++++++-
2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
index 981ff9479648..a16325db4cff 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 */
diff --git a/arch/x86/include/asm/disabled-features.h b/arch/x86/include/asm/disabled-features.h
index a5ea841cc6d2..74de07d0f390 100644
--- a/arch/x86/include/asm/disabled-features.h
+++ b/arch/x86/include/asm/disabled-features.h
@@ -62,6 +62,12 @@
# define DISABLE_PTI (1 << (X86_FEATURE_PTI & 31))
#endif
+#ifdef CONFIG_INTEL_SGX
+# define DISABLE_SGX_CORE 0
+#else
+# define DISABLE_SGX_CORE (1 << (X86_FEATURE_SGX & 31))
+#endif
+
/*
* Make sure to add features to the correct mask
*/
@@ -74,7 +80,7 @@
#define DISABLED_MASK6 0
#define DISABLED_MASK7 (DISABLE_PTI)
#define DISABLED_MASK8 0
-#define DISABLED_MASK9 (DISABLE_MPX|DISABLE_SMAP)
+#define DISABLED_MASK9 (DISABLE_MPX|DISABLE_SMAP|DISABLE_SGX_CORE)
#define DISABLED_MASK10 0
#define DISABLED_MASK11 0
#define DISABLED_MASK12 0
--
2.19.1
From: Sean Christopherson <[email protected]>
CPUID_12_EAX is an Intel-defined feature bits leaf dedicated for SGX
that enumerates the SGX instruction sets that are supported by the
CPU, e.g. SGX1, SGX2, etc... Because Linux currently only cares about
two bits (SGX1 and SGX2) and there are currently only four documented
bits in total, relocate the bits to Linux-defined word 8 to conserve
space.
But, keep the bit positions identical between the Intel-defined value
and the Linux-defined value, e.g. keep SGX1 at bit 0. This allows KVM
to use its existing code for probing guest CPUID bits using Linux's
X86_FEATURE_* definitions. To do so, shift around some existing bits
to effectively reserve bits 0-7 of word 8 for SGX sub-features.
Signed-off-by: Sean Christopherson <[email protected]>
Signed-off-by: Jarkko Sakkinen <[email protected]>
---
arch/x86/include/asm/cpufeatures.h | 22 ++++++++++++++++------
arch/x86/include/asm/disabled-features.h | 6 +++++-
arch/x86/kernel/cpu/scattered.c | 2 ++
tools/arch/x86/include/asm/cpufeatures.h | 21 +++++++++++++++------
4 files changed, 38 insertions(+), 13 deletions(-)
diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
index a16325db4cff..313c58c04b51 100644
--- a/arch/x86/include/asm/cpufeatures.h
+++ b/arch/x86/include/asm/cpufeatures.h
@@ -222,12 +222,22 @@
#define X86_FEATURE_L1TF_PTEINV ( 7*32+29) /* "" L1TF workaround PTE inversion */
#define X86_FEATURE_IBRS_ENHANCED ( 7*32+30) /* Enhanced IBRS */
-/* Virtualization flags: Linux defined, word 8 */
-#define X86_FEATURE_TPR_SHADOW ( 8*32+ 0) /* Intel TPR Shadow */
-#define X86_FEATURE_VNMI ( 8*32+ 1) /* Intel Virtual NMI */
-#define X86_FEATURE_FLEXPRIORITY ( 8*32+ 2) /* Intel FlexPriority */
-#define X86_FEATURE_EPT ( 8*32+ 3) /* Intel Extended Page Table */
-#define X86_FEATURE_VPID ( 8*32+ 4) /* Intel Virtual Processor ID */
+/*
+ * Scattered Intel features: Linux defined, word 8.
+ *
+ * Note that the bit location of the SGX features is meaningful as KVM expects
+ * the Linux defined bit to match the Intel defined bit, e.g. X86_FEATURE_SGX1
+ * must remain at bit 0, SGX2 at bit 1, etc...
+ */
+#define X86_FEATURE_SGX1 ( 8*32+ 0) /* SGX1 leaf functions */
+#define X86_FEATURE_SGX2 ( 8*32+ 1) /* SGX2 leaf functions */
+/* Bits [0:7] are reserved for SGX */
+
+#define X86_FEATURE_TPR_SHADOW ( 8*32+ 8) /* Intel TPR Shadow */
+#define X86_FEATURE_VNMI ( 8*32+ 9) /* Intel Virtual NMI */
+#define X86_FEATURE_FLEXPRIORITY ( 8*32+10) /* Intel FlexPriority */
+#define X86_FEATURE_EPT ( 8*32+11) /* Intel Extended Page Table */
+#define X86_FEATURE_VPID ( 8*32+12) /* Intel Virtual Processor ID */
#define X86_FEATURE_VMMCALL ( 8*32+15) /* Prefer VMMCALL to VMCALL */
#define X86_FEATURE_XENPV ( 8*32+16) /* "" Xen paravirtual guest */
diff --git a/arch/x86/include/asm/disabled-features.h b/arch/x86/include/asm/disabled-features.h
index 74de07d0f390..926f9dc4d75a 100644
--- a/arch/x86/include/asm/disabled-features.h
+++ b/arch/x86/include/asm/disabled-features.h
@@ -34,12 +34,16 @@
# define DISABLE_CYRIX_ARR (1<<(X86_FEATURE_CYRIX_ARR & 31))
# define DISABLE_CENTAUR_MCR (1<<(X86_FEATURE_CENTAUR_MCR & 31))
# define DISABLE_PCID 0
+# define DISABLE_SGX1 0
+# define DISABLE_SGX2 0
#else
# define DISABLE_VME 0
# define DISABLE_K6_MTRR 0
# define DISABLE_CYRIX_ARR 0
# define DISABLE_CENTAUR_MCR 0
# define DISABLE_PCID (1<<(X86_FEATURE_PCID & 31))
+# define DISABLE_SGX1 (1<<(X86_FEATURE_SGX1 & 31))
+# define DISABLE_SGX2 (1<<(X86_FEATURE_SGX2 & 31))
#endif /* CONFIG_X86_64 */
#ifdef CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS
@@ -79,7 +83,7 @@
#define DISABLED_MASK5 0
#define DISABLED_MASK6 0
#define DISABLED_MASK7 (DISABLE_PTI)
-#define DISABLED_MASK8 0
+#define DISABLED_MASK8 (DISABLE_SGX1|DISABLE_SGX2)
#define DISABLED_MASK9 (DISABLE_MPX|DISABLE_SMAP|DISABLE_SGX_CORE)
#define DISABLED_MASK10 0
#define DISABLED_MASK11 0
diff --git a/arch/x86/kernel/cpu/scattered.c b/arch/x86/kernel/cpu/scattered.c
index 94aa1c72ca98..9a5f6cf947b9 100644
--- a/arch/x86/kernel/cpu/scattered.c
+++ b/arch/x86/kernel/cpu/scattered.c
@@ -31,6 +31,8 @@ static const struct cpuid_bit cpuid_bits[] = {
{ X86_FEATURE_CDP_L3, CPUID_ECX, 2, 0x00000010, 1 },
{ X86_FEATURE_CDP_L2, CPUID_ECX, 2, 0x00000010, 2 },
{ X86_FEATURE_MBA, CPUID_EBX, 3, 0x00000010, 0 },
+ { X86_FEATURE_SGX1, CPUID_EAX, 0, 0x00000012, 0 },
+ { X86_FEATURE_SGX2, CPUID_EAX, 1, 0x00000012, 0 },
{ X86_FEATURE_HW_PSTATE, CPUID_EDX, 7, 0x80000007, 0 },
{ X86_FEATURE_CPB, CPUID_EDX, 9, 0x80000007, 0 },
{ X86_FEATURE_PROC_FEEDBACK, CPUID_EDX, 11, 0x80000007, 0 },
diff --git a/tools/arch/x86/include/asm/cpufeatures.h b/tools/arch/x86/include/asm/cpufeatures.h
index 6d6122524711..f3a936c7fed4 100644
--- a/tools/arch/x86/include/asm/cpufeatures.h
+++ b/tools/arch/x86/include/asm/cpufeatures.h
@@ -222,12 +222,21 @@
#define X86_FEATURE_L1TF_PTEINV ( 7*32+29) /* "" L1TF workaround PTE inversion */
#define X86_FEATURE_IBRS_ENHANCED ( 7*32+30) /* Enhanced IBRS */
-/* Virtualization flags: Linux defined, word 8 */
-#define X86_FEATURE_TPR_SHADOW ( 8*32+ 0) /* Intel TPR Shadow */
-#define X86_FEATURE_VNMI ( 8*32+ 1) /* Intel Virtual NMI */
-#define X86_FEATURE_FLEXPRIORITY ( 8*32+ 2) /* Intel FlexPriority */
-#define X86_FEATURE_EPT ( 8*32+ 3) /* Intel Extended Page Table */
-#define X86_FEATURE_VPID ( 8*32+ 4) /* Intel Virtual Processor ID */
+/*
+ * Scattered Intel features: Linux defined, word 8.
+ *
+ * Note that the bit numbers of the SGX features are meaningful as KVM expects
+ * the Linux defined bit to match the Intel defined bit, e.g. X86_FEATURE_SGX1
+ * must remain at bit 0, SGX2 at bit 1, etc...
+ */
+#define X86_FEATURE_SGX1 ( 8*32+ 0) /* SGX1 leaf functions */
+#define X86_FEATURE_SGX2 ( 8*32+ 1) /* SGX2 leaf functions */
+
+#define X86_FEATURE_TPR_SHADOW ( 8*32+ 8) /* Intel TPR Shadow */
+#define X86_FEATURE_VNMI ( 8*32+ 9) /* Intel Virtual NMI */
+#define X86_FEATURE_FLEXPRIORITY ( 8*32+10) /* Intel FlexPriority */
+#define X86_FEATURE_EPT ( 8*32+11) /* Intel Extended Page Table */
+#define X86_FEATURE_VPID ( 8*32+12) /* Intel Virtual Processor ID */
#define X86_FEATURE_VMMCALL ( 8*32+15) /* Prefer VMMCALL to VMCALL */
#define X86_FEATURE_XENPV ( 8*32+16) /* "" Xen paravirtual guest */
--
2.19.1
From: Sean Christopherson <[email protected]>
Add a new IA32_FEATURE_CONTROL bit, SGX_ENABLE, which must be set in
order to execute SGX instructions, i.e. ENCL{S,U,V}. The existence of
the bit is enumerated by CPUID as X86_FEATURE_SGX. Like all other
flags in IA32_FEATURE_CONTROL, the MSR must be locked for SGX_ENABLE
to take effect.
Signed-off-by: Sean Christopherson <[email protected]>
Signed-off-by: Jarkko Sakkinen <[email protected]>
---
arch/x86/include/asm/msr-index.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index ca5bc0eacb95..6efaa8026c64 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -525,6 +525,7 @@
#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_LMCE (1<<20)
#define MSR_IA32_APICBASE 0x0000001b
--
2.19.1
From: Kai Huang <[email protected]>
X86_FEATURE_SGX_LC reflects whether or not the CPU supports SGX Launch
Control, i.e. enumerates the existence of IA32_FEATURE_CONTROL's
SGX_LE_WR bit and the IA32_SGXLEPUBKEYHASH MSRs.
Signed-off-by: Kai Huang <[email protected]>
Signed-off-by: Jarkko Sakkinen <[email protected]>
---
arch/x86/include/asm/cpufeatures.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
index 313c58c04b51..c5080842ecad 100644
--- a/arch/x86/include/asm/cpufeatures.h
+++ b/arch/x86/include/asm/cpufeatures.h
@@ -346,6 +346,7 @@
#define X86_FEATURE_CLDEMOTE (16*32+25) /* CLDEMOTE instruction */
#define X86_FEATURE_MOVDIRI (16*32+27) /* MOVDIRI instruction */
#define X86_FEATURE_MOVDIR64B (16*32+28) /* MOVDIR64B instruction */
+#define X86_FEATURE_SGX_LC (16*32+30) /* Software Guard Extensions Launch Control */
/* AMD-defined CPU features, CPUID level 0x80000007 (EBX), word 17 */
#define X86_FEATURE_OVERFLOW_RECOV (17*32+ 0) /* MCA overflow recovery support */
--
2.19.1
From: Sean Christopherson <[email protected]>
Add a new IA32_FEATURE_CONTROL bit, SGX_LE_WR. When set, SGX_LE_WR
allows software to write the SGXLEPUBKEYHASH MSRs (see below). The
The existence of the bit is enumerated by CPUID as X86_FEATURE_SGX_LC.
Like all other flags in IA32_FEATURE_CONTROL, the MSR must be locked
for SGX_LE_WR to take effect.
Add four MSRs, SGXLEPUBKEYHASH{0,1,2,3}, or in human readable form,
the SGX Launch Enclave Public Key Hash MSRs. These MSRs correspond to
the key that is used by the CPU to determine whether or not to allow
software to enter an enclave. When ENCLS[EINIT] is executed, which is
a prerequisite to entering the enclave, the CPU compares the key
(technically its hash) used to sign the enclave with the key hash
stored in the MSRs, and will reject EINIT if the keys do not match.
Enclaves can also be blessed by proxy, in which case a Launch Enclave
generates and signs an EINIT TOKEN. If a valid token is provided,
ENCLS[EINIT] compares the signer of the token against the MSRs instead
of the signer of the enclave. The SGXLEPUBKEYHASH MSRs only exist on
CPUs that support SGX Launch Control, enumerated by X86_FEATURE_SGX_LC.
CPUs without Launch Control use a hardcoded key for the ENCLS[EINIT]
checks. An internal hardcoded key is also used as the reset value for
the hash MSRs when they exist.
As a final note, the SGX_LEPUBKEYHASH MSRs can also be written by
pre-boot firmware prior to activating SGX (SGX activation is done by
setting bit 0 in MSR 0x7A). Thus, firmware can lock the MSRs to a
non-Intel value by writing the MSRs and locking IA32_FEATURE_CONTROL
without setting SGX_LE_WR.
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 | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index 6efaa8026c64..a4a22441d000 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -525,6 +525,7 @@
#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_LE_WR (1<<17)
#define FEATURE_CONTROL_SGX_ENABLE (1<<18)
#define FEATURE_CONTROL_LMCE (1<<20)
@@ -538,6 +539,12 @@
#define MSR_IA32_UCODE_WRITE 0x00000079
#define MSR_IA32_UCODE_REV 0x0000008b
+/* Intel SGX Launch Enclave Public Key Hash MSRs */
+#define MSR_IA32_SGXLEPUBKEYHASH0 0x0000008C
+#define MSR_IA32_SGXLEPUBKEYHASH1 0x0000008D
+#define MSR_IA32_SGXLEPUBKEYHASH2 0x0000008E
+#define MSR_IA32_SGXLEPUBKEYHASH3 0x0000008F
+
#define MSR_IA32_SMM_MONITOR_CTL 0x0000009b
#define MSR_IA32_SMBASE 0x0000009e
--
2.19.1
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).
Despite triggering a #PF, a #PF with PF_SGX has nothing to do with
paging.
Cc: Dave Hansen <[email protected]>
Signed-off-by: Sean Christopherson <[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 7d6f3f3fad78..c3e02912e9c7 100644
--- a/arch/x86/include/asm/traps.h
+++ b/arch/x86/include/asm/traps.h
@@ -171,5 +171,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.19.1
From: Sean Christopherson <[email protected]>
The PF_SGX bit is set if and only if the #PF is detected by the SGX
Enclave Page Cache Map (EPCM). The EPCM is a hardware-managed table
that enforces accesses to an enclave's EPC pages 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 EPCM 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 (see below). Regardless of
why the fault occurred, 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,
i.e. signalling SIGSEGV is actually the least severe action possible.
Although the primary purpose of the EPCM is to prevent a malicious or
compromised kernel from attacking an enclave, e.g. by modifying the
enclave's page tables, do not WARN on a #PF w/ PF_SGX set. The SGX
architecture effectively allows the CPU to invalidate all EPCM entries
at will and requires that software be prepared to handle an EPCM fault
at any time. The architecture defines this behavior because the EPCM
is encrypted with an ephemeral key that isn't exposed to software. As
such, the EPCM entries cannot be preserved across transitions that
result in a new key being used, e.g. CPU power down as part of an S3
transition or when a VM is live migrated to a new physical system.
Cc: Andy Lutomirski <[email protected]>
Cc: Dave Hansen <[email protected]>
Signed-off-by: Sean Christopherson <[email protected]>
Signed-off-by: Jarkko Sakkinen <[email protected]>
---
arch/x86/mm/fault.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
index 667f1da36208..78e2807fbede 100644
--- a/arch/x86/mm/fault.c
+++ b/arch/x86/mm/fault.c
@@ -1214,6 +1214,19 @@ 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, 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.
+ */
+ 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.19.1
Define the data structures used by various ENCLS functions needed for
Linux to support all SGX1 and SGX2 ENCLS leaf functions. This is not
an exhaustive representation of all SGX data structures as several are
only consumed by ENCLU (userspace), e.g. REPORT and KEYREQUEST, while
others are only consumed by future features, e.g. RDINFO.
Signed-off-by: Jarkko Sakkinen <[email protected]>
Co-developed-by: Sean Christopherson <[email protected]>
Signed-off-by: Sean Christopherson <[email protected]>
---
arch/x86/kernel/cpu/sgx/arch.h | 409 +++++++++++++++++++++++++++++++++
1 file changed, 409 insertions(+)
create mode 100644 arch/x86/kernel/cpu/sgx/arch.h
diff --git a/arch/x86/kernel/cpu/sgx/arch.h b/arch/x86/kernel/cpu/sgx/arch.h
new file mode 100644
index 000000000000..7a2f293db32c
--- /dev/null
+++ b/arch/x86/kernel/cpu/sgx/arch.h
@@ -0,0 +1,409 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/**
+ * Copyright(c) 2016-18 Intel Corporation.
+ *
+ * Contains data structures defined by the SGX architecture. 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>
+
+/**
+ * enum sgx_encls_leaves - ENCLS leaf functions
+ * %SGX_ECREATE: Create an enclave.
+ * %SGX_EADD: Add a page to an uninitialized enclave.
+ * %SGX_EINIT: Initialize an enclave, i.e. 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.
+ * %SGX_EAUG: Add a page to an initialized enclave.
+ * %SGX_EMODPR: Restrict an EPC page's permissions.
+ * %SGX_EMODT: Modify the page type of an EPC page.
+ */
+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_KSS: Allow to use key separation and sharing (KSS).
+ * %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_KSS = BIT(7),
+ SGX_ATTR_RESERVED_MASK = BIT_ULL(3) |
+ BIT_ULL(7) |
+ GENMASK_ULL(63, 8),
+ SGX_ATTR_ALLOWED_MASK = SGX_ATTR_DEBUG |
+ SGX_ATTR_MODE64BIT |
+ SGX_ATTR_KSS,
+};
+
+#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 4024
+
+/**
+ * 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;
+ u8 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_header - defines author of the enclave
+ * @header1: constant byte string
+ * @vendor: must be either 0x0000 or 0x8086
+ * @date: YYYYMMDD in BCD
+ * @header2: costant byte string
+ * @swdefined: software defined value
+ */
+struct sgx_sigstruct_header {
+ u64 header1[2];
+ u32 vendor;
+ u32 date;
+ u64 header2[2];
+ u32 swdefined;
+ u8 reserved1[84];
+} __packed;
+
+/**
+ * struct sgx_sigstruct_body - defines contents of the enclave
+ * @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
+ */
+struct sgx_sigstruct_body {
+ u32 miscselect;
+ u32 misc_mask;
+ u8 reserved2[20];
+ u64 attributes;
+ u64 xfrm;
+ u64 attributes_mask;
+ u64 xfrm_mask;
+ u8 mrenclave[32];
+ u8 reserved3[32];
+ u16 isvprodid;
+ u16 isvsvn;
+} __packed;
+
+/**
+ * struct sgx_sigstruct - an enclave signature
+ * @header: defines author of the enclave
+ * @modulus: the modulus of the public key
+ * @exponent: the exponent of the public key
+ * @signature: the signature calculated over the fields except modulus,
+ * @body: defines contents of the enclave
+ * @q1: a value used in RSA signature verification
+ * @q2: a value used in RSA signature verification
+ *
+ * Header and body are the parts that are actual signed. The remaining fields
+ * define the signature of the enclave.
+ */
+struct sgx_sigstruct {
+ struct sgx_sigstruct_header header;
+ u8 modulus[SGX_MODULUS_SIZE];
+ u32 exponent;
+ u8 signature[SGX_MODULUS_SIZE];
+ struct sgx_sigstruct_body body;
+ u8 reserved4[12];
+ u8 q1[SGX_MODULUS_SIZE];
+ u8 q2[SGX_MODULUS_SIZE];
+} __packed;
+
+#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 */
--
2.19.1
SGX defines its own CPUID leaf, 0x12, along with a variable number of
sub-leafs. Sub-leafs 0 and 1 are always available if SGX is supported
and enumerate various SGX features, e.g. instruction sets and enclave
capabilities. Sub-leafs 2+ are variable, both in their existence and
in what they enumerate. Bits 3:0 of EAX report the sub-leaf type,
with the remaining bits in EAX, EBX, ECX and EDX being type-specific.
Currently, the only known sub-leaf type enumerates an EPC section. An
EPC section is simply a range of EPC memory available to software.
The "list" of varaible SGX sub-leafs is NULL-terminated, i.e. software
is expected to query CPUID until an invalid sub-leaf is encountered.
Signed-off-by: Jarkko Sakkinen <[email protected]>
Co-developed-by: Sean Christopherson <[email protected]>
Signed-off-by: Sean Christopherson <[email protected]>
---
arch/x86/kernel/cpu/sgx/arch.h | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/arch/x86/kernel/cpu/sgx/arch.h b/arch/x86/kernel/cpu/sgx/arch.h
index 7a2f293db32c..e966cdd68040 100644
--- a/arch/x86/kernel/cpu/sgx/arch.h
+++ b/arch/x86/kernel/cpu/sgx/arch.h
@@ -10,6 +10,21 @@
#include <linux/types.h>
+#define SGX_CPUID 0x12
+#define SGX_CPUID_FIRST_VARIABLE_SUB_LEAF 2
+
+/**
+ * enum sgx_sub_leaf_types - SGX CPUID variable sub-leaf types
+ * %SGX_CPUID_SUB_LEAF_INVALID: Indicates this sub-leaf is invalid.
+ * %SGX_CPUID_SUB_LEAF_EPC_SECTION: Sub-leaf enumerates an EPC section.
+ * %SGX_CPUID_SUB_LEAF_TYPE_MASK: Mask for bits containing the type.
+ */
+enum sgx_sub_leaf_types {
+ SGX_CPUID_SUB_LEAF_INVALID = 0x0,
+ SGX_CPUID_SUB_LEAF_EPC_SECTION = 0x1,
+ SGX_CPUID_SUB_LEAF_TYPE_MASK = GENMASK(3, 0),
+};
+
/**
* enum sgx_encls_leaves - ENCLS leaf functions
* %SGX_ECREATE: Create an enclave.
--
2.19.1
ENCLS is an umbrella instruction for a variety of cpl0 SGX functions.
The ENCLS function that is executed is specified in EAX, with each
function potentially having more leaf-specific operands beyond EAX.
ENCLS introduces its own (positive value) error codes that (some)
leafs use to return failure information in EAX. Leafs that return
an error code also modify RFLAGS. And finally, ENCLS generates
ENCLS-specific non-fatal #GPs and #PFs, i.e. a bug-free kernel may
encounter faults on ENCLS that must be handled gracefully.
Because of the complexity involved in encoding ENCLS and handling its
assortment of failure paths, executing any given leaf is not a simple
matter of emitting ENCLS.
To enable adding support for ENCLS leafs with minimal fuss, add a
two-layer macro system along with an encoding scheme to allow wrappers
to return trap numbers along ENCLS-specific error codes. The bottom
layer of the macro system splits between the leafs that return an
error code and those that do not. The second layer generates the
correct input/output annotations based on the number of operands for
each leaf function.
Signed-off-by: Jarkko Sakkinen <[email protected]>
Co-developed-by: Sean Christopherson <[email protected]>
Signed-off-by: Sean Christopherson <[email protected]>
---
arch/x86/kernel/cpu/sgx/Makefile | 2 +-
arch/x86/kernel/cpu/sgx/encls.c | 21 +++
arch/x86/kernel/cpu/sgx/encls.h | 244 +++++++++++++++++++++++++++++++
3 files changed, 266 insertions(+), 1 deletion(-)
create mode 100644 arch/x86/kernel/cpu/sgx/encls.c
create mode 100644 arch/x86/kernel/cpu/sgx/encls.h
diff --git a/arch/x86/kernel/cpu/sgx/Makefile b/arch/x86/kernel/cpu/sgx/Makefile
index b666967fd570..20ce33655ff4 100644
--- a/arch/x86/kernel/cpu/sgx/Makefile
+++ b/arch/x86/kernel/cpu/sgx/Makefile
@@ -1 +1 @@
-obj-y += main.o
+obj-y += main.o encls.o
diff --git a/arch/x86/kernel/cpu/sgx/encls.c b/arch/x86/kernel/cpu/sgx/encls.c
new file mode 100644
index 000000000000..5045f1365e07
--- /dev/null
+++ b/arch/x86/kernel/cpu/sgx/encls.c
@@ -0,0 +1,21 @@
+#include <asm/cpufeature.h>
+#include <asm/traps.h>
+#include "encls.h"
+#include "sgx.h"
+
+/**
+ * encls_failed() - Check if an ENCLS leaf function failed
+ * @ret: the return value of an ENCLS leaf function call
+ *
+ * Check if an ENCLS leaf function failed. This is a condition where the leaf
+ * function causes a fault that is not caused by an EPCM conflict.
+ *
+ * Return: true if there was a fault other than an EPCM conflict
+ */
+bool encls_failed(int ret)
+{
+ int epcm_trapnr = boot_cpu_has(X86_FEATURE_SGX2) ?
+ X86_TRAP_PF : X86_TRAP_GP;
+
+ return encls_faulted(ret) && ENCLS_TRAPNR(ret) != epcm_trapnr;
+}
diff --git a/arch/x86/kernel/cpu/sgx/encls.h b/arch/x86/kernel/cpu/sgx/encls.h
new file mode 100644
index 000000000000..aea3b9d09936
--- /dev/null
+++ b/arch/x86/kernel/cpu/sgx/encls.h
@@ -0,0 +1,244 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+#ifndef _X86_ENCLS_H
+#define _X86_ENCLS_H
+
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/rwsem.h>
+#include <linux/types.h>
+#include <asm/asm.h>
+#include "arch.h"
+
+/**
+ * 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
+
+/**
+ * Retrieve the encoded trapnr from the specified return code.
+ */
+#define ENCLS_TRAPNR(r) ((r) & ~ENCLS_FAULT_FLAG)
+
+/* Issue a WARN() about an ENCLS leaf. */
+#define ENCLS_WARN(r, name) { \
+ do { \
+ int _r = (r); \
+ WARN(_r, "sgx: %s returned %d (0x%x)\n", (name), _r, \
+ _r); \
+ } while (0); \
+}
+
+/**
+ * encls_faulted() - Check if ENCLS leaf function faulted
+ * @ret: the return value of an ENCLS leaf function call
+ *
+ * Return: true if the fault flag is set
+ */
+static inline bool encls_faulted(int ret)
+{
+ return (ret & ENCLS_FAULT_FLAG) != 0;
+}
+
+/**
+ * encls_returned_code() - Check if an ENCLS leaf function returned a code
+ * @ret: the return value of an ENCLS leaf function call
+ *
+ * Check if an ENCLS leaf function returned an error or information code.
+ *
+ * Return: true if there was a fault other than an EPCM conflict
+ */
+static inline bool encls_returned_code(int ret)
+{
+ return !encls_faulted(ret) && ret;
+}
+
+bool encls_failed(int ret);
+
+/**
+ * __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 *secs)
+{
+ return __encls_2(SGX_ECREATE, pginfo, secs);
+}
+
+static inline int __eextend(void *secs, void *addr)
+{
+ return __encls_2(SGX_EEXTEND, secs, addr);
+}
+
+static inline int __eadd(struct sgx_pageinfo *pginfo, void *addr)
+{
+ return __encls_2(SGX_EADD, pginfo, addr);
+}
+
+static inline int __einit(void *sigstruct, struct sgx_einittoken *einittoken,
+ void *secs)
+{
+ return __encls_ret_3(SGX_EINIT, sigstruct, secs, einittoken);
+}
+
+static inline int __eremove(void *addr)
+{
+ return __encls_ret_1(SGX_EREMOVE, addr);
+}
+
+static inline int __edbgwr(void *addr, unsigned long *data)
+{
+ return __encls_2(SGX_EDGBWR, *data, addr);
+}
+
+static inline int __edbgrd(void *addr, unsigned long *data)
+{
+ return __encls_1_1(SGX_EDGBRD, *data, addr);
+}
+
+static inline int __etrack(void *addr)
+{
+ return __encls_ret_1(SGX_ETRACK, addr);
+}
+
+static inline int __eldu(struct sgx_pageinfo *pginfo, void *addr,
+ void *va)
+{
+ return __encls_ret_3(SGX_ELDU, pginfo, addr, va);
+}
+
+static inline int __eblock(void *addr)
+{
+ return __encls_ret_1(SGX_EBLOCK, addr);
+}
+
+static inline int __epa(void *addr)
+{
+ unsigned long rbx = SGX_PAGE_TYPE_VA;
+
+ return __encls_2(SGX_EPA, rbx, addr);
+}
+
+static inline int __ewb(struct sgx_pageinfo *pginfo, void *addr,
+ void *va)
+{
+ return __encls_ret_3(SGX_EWB, pginfo, addr, va);
+}
+
+static inline int __eaug(struct sgx_pageinfo *pginfo, void *addr)
+{
+ return __encls_2(SGX_EAUG, pginfo, addr);
+}
+
+static inline int __emodpr(struct sgx_secinfo *secinfo, void *addr)
+{
+ return __encls_ret_2(SGX_EMODPR, secinfo, addr);
+}
+
+static inline int __emodt(struct sgx_secinfo *secinfo, void *addr)
+{
+ return __encls_ret_2(SGX_EMODT, secinfo, addr);
+}
+
+#endif /* _X86_ENCLS_H */
--
2.19.1
From: Sean Christopherson <[email protected]>
Similar to other large Intel features such as VMX and TXT, SGX must be
explicitly enabled in IA32_FEATURE_CONTROL MSR to be truly usable.
Clear all SGX related capabilities if SGX is not fully enabled in
IA32_FEATURE_CONTROL or if the SGX1 instruction set isn't supported
(impossible on bare metal, theoretically possible in a VM if the VMM is
doing something weird).
Like SGX itself, SGX Launch Control must be explicitly enabled via a
flag in IA32_FEATURE_CONTROL. Clear the SGX_LC capability if Launch
Control is not fully enabled (or obviously if SGX itself is disabled).
Note that clearing X86_FEATURE_SGX_LC creates a bit of a conundrum
regarding the SGXLEPUBKEYHASH MSRs, as it may be desirable to read the
MSRs even if they are not writable, e.g. to query the configured key,
but clearing the capability leaves no breadcrum for discerning whether
or not the MSRs exist. But, such usage will be rare (KVM is the only
known case at this time) and not performance critical, so it's not
unreasonable to require the use of rdmsr_safe(). Clearing the cap bit
eliminates the need for an additional flag to track whether or not
Launch Control is truly enabled, which is what we care about the vast
majority of the time.
Signed-off-by: Sean Christopherson <[email protected]>
Co-developed-by: Jarkko Sakkinen <[email protected]>
Signed-off-by: Jarkko Sakkinen <[email protected]>
---
arch/x86/kernel/cpu/intel.c | 39 +++++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c
index fc3c07fe7df5..702497f34a96 100644
--- a/arch/x86/kernel/cpu/intel.c
+++ b/arch/x86/kernel/cpu/intel.c
@@ -596,6 +596,42 @@ static void detect_tme(struct cpuinfo_x86 *c)
c->x86_phys_bits -= keyid_bits;
}
+static void __maybe_unused detect_sgx(struct cpuinfo_x86 *c)
+{
+ unsigned long long fc;
+
+ rdmsrl(MSR_IA32_FEATURE_CONTROL, fc);
+ if (!(fc & FEATURE_CONTROL_LOCKED)) {
+ pr_err_once("sgx: The feature control MSR is not locked\n");
+ goto err_unsupported;
+ }
+
+ if (!(fc & FEATURE_CONTROL_SGX_ENABLE)) {
+ pr_err_once("sgx: SGX is not enabled in IA32_FEATURE_CONTROL MSR\n");
+ goto err_unsupported;
+ }
+
+ if (!cpu_has(c, X86_FEATURE_SGX1)) {
+ pr_err_once("sgx: SGX1 instruction set is not supported\n");
+ goto err_unsupported;
+ }
+
+ if (!(fc & FEATURE_CONTROL_SGX_LE_WR)) {
+ pr_info_once("sgx: The launch control MSRs are not writable\n");
+ goto err_msrs_rdonly;
+ }
+
+ return;
+
+err_unsupported:
+ setup_clear_cpu_cap(X86_FEATURE_SGX);
+ setup_clear_cpu_cap(X86_FEATURE_SGX1);
+ setup_clear_cpu_cap(X86_FEATURE_SGX2);
+
+err_msrs_rdonly:
+ setup_clear_cpu_cap(X86_FEATURE_SGX_LC);
+}
+
static void init_intel_energy_perf(struct cpuinfo_x86 *c)
{
u64 epb;
@@ -763,6 +799,9 @@ static void init_intel(struct cpuinfo_x86 *c)
if (cpu_has(c, X86_FEATURE_TME))
detect_tme(c);
+ if (IS_ENABLED(CONFIG_INTEL_SGX) && cpu_has(c, X86_FEATURE_SGX))
+ detect_sgx(c);
+
init_intel_energy_perf(c);
init_intel_misc_features(c);
--
2.19.1
From: Sean Christopherson <[email protected]>
Enumerate Enclave Page Cache (EPC) sections via CPUID and add the data
structures necessary to track EPC pages so that they can be allocated,
freed and managed. As a system may have multiple EPC sections, invoke
CPUID on SGX sub-leafs until an invalid leaf is encountered.
On NUMA systems, a node can have at most one 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.
For simplicity, support a maximum of eight EPC sections. Current
client hardware supports only a single section, while upcoming server
hardware will support at most eight sections. Bounding the number of
sections also allows the section ID to be embedded along with a page's
offset in a single unsigned long, enabling easy retrieval of both the
VA and PA for a given page.
Signed-off-by: Sean Christopherson <[email protected]>
Co-developed-by: Jarkko Sakkinen <[email protected]>
Signed-off-by: Jarkko Sakkinen <[email protected]>
Co-developed-by: Suresh Siddha <[email protected]>
Signed-off-by: Suresh Siddha <[email protected]>
Co-developed-by: Serge Ayoun <[email protected]>
Signed-off-by: Serge Ayoun <[email protected]>
---
arch/x86/Kconfig | 19 ++++
arch/x86/kernel/cpu/Makefile | 1 +
arch/x86/kernel/cpu/sgx/Makefile | 1 +
arch/x86/kernel/cpu/sgx/main.c | 149 +++++++++++++++++++++++++++++++
arch/x86/kernel/cpu/sgx/sgx.h | 62 +++++++++++++
5 files changed, 232 insertions(+)
create mode 100644 arch/x86/kernel/cpu/sgx/Makefile
create mode 100644 arch/x86/kernel/cpu/sgx/main.c
create mode 100644 arch/x86/kernel/cpu/sgx/sgx.h
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index c1f9b3cf437c..dc630208003f 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1921,6 +1921,25 @@ config X86_INTEL_MEMORY_PROTECTION_KEYS
If unsure, say y.
+config INTEL_SGX
+ bool "Intel SGX core functionality"
+ depends on X86_64 && CPU_SUP_INTEL
+ 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.
+
+ 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/kernel/cpu/Makefile b/arch/x86/kernel/cpu/Makefile
index cfd24f9f7614..d1163c0fd5d6 100644
--- a/arch/x86/kernel/cpu/Makefile
+++ b/arch/x86/kernel/cpu/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_X86_MCE) += mce/
obj-$(CONFIG_MTRR) += mtrr/
obj-$(CONFIG_MICROCODE) += microcode/
obj-$(CONFIG_X86_CPU_RESCTRL) += resctrl/
+obj-$(CONFIG_INTEL_SGX) += sgx/
obj-$(CONFIG_X86_LOCAL_APIC) += perfctr-watchdog.o
diff --git a/arch/x86/kernel/cpu/sgx/Makefile b/arch/x86/kernel/cpu/sgx/Makefile
new file mode 100644
index 000000000000..b666967fd570
--- /dev/null
+++ b/arch/x86/kernel/cpu/sgx/Makefile
@@ -0,0 +1 @@
+obj-y += main.o
diff --git a/arch/x86/kernel/cpu/sgx/main.c b/arch/x86/kernel/cpu/sgx/main.c
new file mode 100644
index 000000000000..18ce4acdd7ef
--- /dev/null
+++ b/arch/x86/kernel/cpu/sgx/main.c
@@ -0,0 +1,149 @@
+// 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 "arch.h"
+#include "sgx.h"
+
+struct sgx_epc_section sgx_epc_sections[SGX_MAX_EPC_SECTIONS];
+EXPORT_SYMBOL_GPL(sgx_epc_sections);
+
+static int sgx_nr_epc_sections;
+
+static void sgx_section_put_page(struct sgx_epc_section *section,
+ struct sgx_epc_page *page)
+{
+ list_add_tail(&page->list, §ion->page_list);
+ section->free_cnt++;
+}
+
+static __init void sgx_free_epc_section(struct sgx_epc_section *section)
+{
+ struct sgx_epc_page *page;
+
+ while (!list_empty(§ion->page_list)) {
+ page = list_first_entry(§ion->page_list,
+ struct sgx_epc_page, list);
+ list_del(&page->list);
+ kfree(page);
+ }
+ memunmap(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;
+ struct sgx_epc_page *page;
+ unsigned long i;
+
+ section->va = memremap(addr, size, MEMREMAP_WB);
+ if (!section->va)
+ return -ENOMEM;
+
+ section->pa = addr;
+ spin_lock_init(§ion->lock);
+ INIT_LIST_HEAD(§ion->page_list);
+
+ for (i = 0; i < nr_pages; i++) {
+ page = kzalloc(sizeof(*page), GFP_KERNEL);
+ if (!page)
+ goto out;
+ page->desc = (addr + (i << PAGE_SHIFT)) | index;
+ sgx_section_put_page(section, page);
+ }
+
+ 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, 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 + 1); i++) {
+ cpuid_count(SGX_CPUID, i + SGX_CPUID_FIRST_VARIABLE_SUB_LEAF,
+ &eax, &ebx, &ecx, &edx);
+
+ type = eax & SGX_CPUID_SUB_LEAF_TYPE_MASK;
+ if (type == SGX_CPUID_SUB_LEAF_INVALID)
+ break;
+ if (type != SGX_CPUID_SUB_LEAF_EPC_SECTION) {
+ pr_err_once("sgx: Unknown sub-leaf type: %u\n", type);
+ return -ENODEV;
+ }
+ if (i == SGX_MAX_EPC_SECTIONS) {
+ pr_warn("sgx: More than "
+ __stringify(SGX_MAX_EPC_SECTIONS)
+ " EPC sections\n");
+ break;
+ }
+
+ 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)
+{
+ int ret;
+
+ if (!boot_cpu_has(X86_FEATURE_SGX))
+ return false;
+
+ ret = sgx_page_cache_init();
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+arch_initcall(sgx_init);
diff --git a/arch/x86/kernel/cpu/sgx/sgx.h b/arch/x86/kernel/cpu/sgx/sgx.h
new file mode 100644
index 000000000000..228e3dae360d
--- /dev/null
+++ b/arch/x86/kernel/cpu/sgx/sgx.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+#ifndef _X86_SGX_H
+#define _X86_SGX_H
+
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/rwsem.h>
+#include <linux/types.h>
+#include <asm/asm.h>
+#include <uapi/asm/sgx_errno.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 *va;
+ struct list_head page_list;
+ unsigned long free_cnt;
+ spinlock_t lock;
+};
+
+#define SGX_MAX_EPC_SECTIONS 8
+
+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.
+ */
+enum sgx_epc_page_desc {
+ SGX_EPC_SECTION_MASK = GENMASK_ULL(3, 0),
+ /* 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 *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;
+}
+
+#endif /* _X86_SGX_H */
--
2.19.1
The SGX architecture defines an extensive set of error codes that are
used by ENCL{S,U,V} instructions to provide software with (somewhat)
precise error information. Though they are architectural, define the
known error codes in a separate file from sgx_arch.h so that they can
be exposed to userspace. For some ENCLS leafs, e.g. EINIT, returning
the exact error code on failure can enable userspace to make informed
decisions when an operation fails.
Signed-off-by: Jarkko Sakkinen <[email protected]>
Co-developed-by: Sean Christopherson <[email protected]>
Signed-off-by: Sean Christopherson <[email protected]>
---
arch/x86/include/uapi/asm/sgx_errno.h | 91 +++++++++++++++++++++++++++
1 file changed, 91 insertions(+)
create mode 100644 arch/x86/include/uapi/asm/sgx_errno.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.19.1
From: Sean Christopherson <[email protected]>
Add a helper function to perform ENCLS(EINIT) with the correct LE
hash MSR values. ENCLS[EINIT] initializes an enclave, verifying the
enclave's measurement and preparing it for execution, i.e. the enclave
cannot be run until it has been initialized. The measurement aspect
of EINIT references the MSR_IA32_SGXLEPUBKEYHASH* MSRs, with the CPU
comparing CPU compares the key (technically its hash) used to sign the
enclave[1] with the key hash stored in the MSRs, and will reject EINIT
if the keys do not match.
A per-cpu cache is used to avoid writing the MSRs as writing the MSRs
is extraordinarily expensive, e.g. 300-400 cycles per MSR. Because
the cache may become stale, force update the MSRs and retry EINIT if
the first EINIT fails due to an "invalid token". An invalid token
error does not necessarily mean the MSRs need to be updated, but the
cost of an unnecessary write is minimal relative to the cost of EINIT
itself.
[1] For EINIT's purposes, the effective signer of the enclave may be
the enclave's owner, or a separate Launch Enclave that has created
an EINIT token for the target enclave. When using an EINIT token,
the key used to sign the token must match the MSRs in order for
EINIT to succeed.
Signed-off-by: Sean Christopherson <[email protected]>
Co-developed-by: Jarkko Sakkinen <[email protected]>
Signed-off-by: Jarkko Sakkinen <[email protected]>
---
arch/x86/kernel/cpu/sgx/main.c | 51 ++++++++++++++++++++++++++++++++++
arch/x86/kernel/cpu/sgx/sgx.h | 2 ++
2 files changed, 53 insertions(+)
diff --git a/arch/x86/kernel/cpu/sgx/main.c b/arch/x86/kernel/cpu/sgx/main.c
index f88c8d86c2a8..e966f96837c7 100644
--- a/arch/x86/kernel/cpu/sgx/main.c
+++ b/arch/x86/kernel/cpu/sgx/main.c
@@ -17,6 +17,9 @@ EXPORT_SYMBOL_GPL(sgx_epc_sections);
static int sgx_nr_epc_sections;
+/* A per-cpu cache for the last known values of IA32_SGXLEPUBKEYHASHx MSRs. */
+static DEFINE_PER_CPU(u64 [4], sgx_lepubkeyhash_cache);
+
static void sgx_section_put_page(struct sgx_epc_section *section,
struct sgx_epc_page *page)
{
@@ -112,6 +115,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
+ *
+ * Execute ENCLS[EINIT], writing the IA32_SGXLEPUBKEYHASHx MSRs according
+ * to @lepubkeyhash (if possible and necessary).
+ *
+ * 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 (!boot_cpu_has(X86_FEATURE_SGX_LC))
+ return __einit(sigstruct, token, sgx_epc_addr(secs));
+
+ preempt_disable();
+ sgx_update_lepubkeyhash_msrs(lepubkeyhash, false);
+ ret = __einit(sigstruct, token, sgx_epc_addr(secs));
+ 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)
{
struct sgx_epc_page *page;
diff --git a/arch/x86/kernel/cpu/sgx/sgx.h b/arch/x86/kernel/cpu/sgx/sgx.h
index 86b4c74b6089..f90d02fbbbbd 100644
--- a/arch/x86/kernel/cpu/sgx/sgx.h
+++ b/arch/x86/kernel/cpu/sgx/sgx.h
@@ -62,5 +62,7 @@ static inline void *sgx_epc_addr(struct sgx_epc_page *page)
struct sgx_epc_page *sgx_alloc_page(void);
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 /* _X86_SGX_H */
--
2.19.1
In order to provide a mechanism for devilering provisoning rights:
1. Add a new file to the securityfs file called sgx/provision that works
as a token for allowing an enclave to have the provisioning privileges.
2. Add a new ioctl called SGX_IOC_ENCLAVE_SET_ATTRIBUTE that accepts the
following data structure:
struct sgx_enclave_set_attribute {
__u64 addr;
__u64 token_fd;
};
A daemon could sit on top of sgx/provision and send a file descriptor of
this file to a process that needs to be able to provision enclaves.
The way this API is used is more or less straight-forward. Lets assume that
dev_fd is a handle to /dev/sgx and prov_fd is a handle to sgx/provision.
You would allow SGX_IOC_ENCLAVE_CREATE to initialize an enclave with the
PROVISIONKEY attribute by
params.addr = <enclave address>;
params.token_fd = prov_fd;
ioctl(dev_fd, SGX_IOC_ENCLAVE_SET_ATTRIBUTE, ¶ms);
Cc: James Morris <[email protected]>
Cc: Serge E. Hallyn <[email protected]>
Cc: [email protected]
Suggested-by: Andy Lutomirski <[email protected]>
Signed-off-by: Jarkko Sakkinen <[email protected]>
---
arch/x86/include/uapi/asm/sgx.h | 13 +++++++
arch/x86/kernel/cpu/sgx/driver/ioctl.c | 43 +++++++++++++++++++++++
arch/x86/kernel/cpu/sgx/driver/main.c | 47 ++++++++++++++++++++++++++
3 files changed, 103 insertions(+)
diff --git a/arch/x86/include/uapi/asm/sgx.h b/arch/x86/include/uapi/asm/sgx.h
index aadf9c76e360..150a784db395 100644
--- a/arch/x86/include/uapi/asm/sgx.h
+++ b/arch/x86/include/uapi/asm/sgx.h
@@ -16,6 +16,8 @@
_IOW(SGX_MAGIC, 0x01, struct sgx_enclave_add_page)
#define SGX_IOC_ENCLAVE_INIT \
_IOW(SGX_MAGIC, 0x02, struct sgx_enclave_init)
+#define SGX_IOC_ENCLAVE_SET_ATTRIBUTE \
+ _IOW(SGX_MAGIC, 0x03, struct sgx_enclave_set_attribute)
/* IOCTL return values */
#define SGX_POWER_LOST_ENCLAVE 0x40000000
@@ -56,4 +58,15 @@ struct sgx_enclave_init {
__u64 sigstruct;
};
+/**
+ * struct sgx_enclave_set_attribute - parameter structure for the
+ * %SGX_IOC_ENCLAVE_INIT ioctl
+ * @addr: address within the ELRANGE
+ * @attribute_fd: file handle of the attribute file in the securityfs
+ */
+struct sgx_enclave_set_attribute {
+ __u64 addr;
+ __u64 attribute_fd;
+};
+
#endif /* _UAPI_ASM_X86_SGX_H */
diff --git a/arch/x86/kernel/cpu/sgx/driver/ioctl.c b/arch/x86/kernel/cpu/sgx/driver/ioctl.c
index 4b9a91b53b50..5d85bd3f7876 100644
--- a/arch/x86/kernel/cpu/sgx/driver/ioctl.c
+++ b/arch/x86/kernel/cpu/sgx/driver/ioctl.c
@@ -759,6 +759,46 @@ static long sgx_ioc_enclave_init(struct file *filep, unsigned int cmd,
return ret;
}
+/**
+ * sgx_ioc_enclave_set_attribute - handler for %SGX_IOC_ENCLAVE_SET_ATTRIBUTE
+ * @filep: open file to /dev/sgx
+ * @cmd: the command value
+ * @arg: pointer to a struct sgx_enclave_set_attribute instance
+ *
+ * Sets an attribute matching the attribute file that is pointed by the
+ * parameter structure field attribute_fd.
+ *
+ * Return: 0 on success, -errno otherwise
+ */
+static long sgx_ioc_enclave_set_attribute(struct file *filep, unsigned int cmd,
+ unsigned long arg)
+{
+ struct sgx_enclave_set_attribute *params = (void *)arg;
+ struct file *attribute_file;
+ struct sgx_encl *encl;
+ int ret;
+
+ attribute_file = fget(params->attribute_fd);
+ if (!attribute_file->f_op)
+ return -EINVAL;
+
+ if (attribute_file->f_op != &sgx_fs_provision_fops) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = sgx_encl_get(params->addr, &encl);
+ if (ret)
+ goto out;
+
+ encl->allowed_attributes |= SGX_ATTR_PROVISIONKEY;
+ kref_put(&encl->refcount, sgx_encl_release);
+
+out:
+ fput(attribute_file);
+ return ret;
+}
+
typedef long (*sgx_ioc_t)(struct file *filep, unsigned int cmd,
unsigned long arg);
@@ -778,6 +818,9 @@ long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
case SGX_IOC_ENCLAVE_INIT:
handler = sgx_ioc_enclave_init;
break;
+ case SGX_IOC_ENCLAVE_SET_ATTRIBUTE:
+ handler = sgx_ioc_enclave_set_attribute;
+ break;
default:
return -ENOIOCTLCMD;
}
diff --git a/arch/x86/kernel/cpu/sgx/driver/main.c b/arch/x86/kernel/cpu/sgx/driver/main.c
index 16f36cd0af04..9a5360dcad98 100644
--- a/arch/x86/kernel/cpu/sgx/driver/main.c
+++ b/arch/x86/kernel/cpu/sgx/driver/main.c
@@ -22,6 +22,11 @@ u64 sgx_attributes_reserved_mask;
u64 sgx_xfrm_reserved_mask = ~0x3;
u32 sgx_xsave_size_tbl[64];
+const struct file_operations sgx_fs_provision_fops;
+
+static struct dentry *sgx_fs;
+static struct dentry *sgx_fs_provision;
+
#ifdef CONFIG_COMPAT
static long sgx_compat_ioctl(struct file *filep, unsigned int cmd,
unsigned long arg)
@@ -147,6 +152,40 @@ static struct sgx_dev_ctx *sgxm_dev_ctx_alloc(struct device *parent)
return ctx;
}
+static int sgx_fs_init(struct device *dev)
+{
+ int ret;
+
+ sgx_fs = securityfs_create_dir(dev_name(dev), NULL);
+ if (IS_ERR(sgx_fs)) {
+ ret = PTR_ERR(sgx_fs);
+ goto err_sgx_fs;
+ }
+
+ sgx_fs_provision = securityfs_create_file("provision", 0600, sgx_fs,
+ NULL, &sgx_fs_provision_fops);
+ if (IS_ERR(sgx_fs)) {
+ ret = PTR_ERR(sgx_fs_provision);
+ goto err_sgx_fs_provision;
+ }
+
+ return 0;
+
+err_sgx_fs_provision:
+ securityfs_remove(sgx_fs);
+ sgx_fs_provision = NULL;
+
+err_sgx_fs:
+ sgx_fs = NULL;
+ return ret;
+}
+
+static void sgx_fs_remove(void)
+{
+ securityfs_remove(sgx_fs_provision);
+ securityfs_remove(sgx_fs);
+}
+
static int sgx_dev_init(struct device *parent)
{
struct sgx_dev_ctx *sgx_dev;
@@ -190,6 +229,10 @@ static int sgx_dev_init(struct device *parent)
if (!sgx_encl_wq)
return -ENOMEM;
+ ret = sgx_fs_init(&sgx_dev->ctrl_dev);
+ if (ret)
+ goto err_fs_init;
+
ret = cdev_device_add(&sgx_dev->ctrl_cdev, &sgx_dev->ctrl_dev);
if (ret)
goto err_device_add;
@@ -197,6 +240,9 @@ static int sgx_dev_init(struct device *parent)
return 0;
err_device_add:
+ sgx_fs_remove();
+
+err_fs_init:
destroy_workqueue(sgx_encl_wq);
return ret;
}
@@ -220,6 +266,7 @@ static int sgx_drv_remove(struct platform_device *pdev)
{
struct sgx_dev_ctx *ctx = dev_get_drvdata(&pdev->dev);
+ sgx_fs_remove();
cdev_device_del(&ctx->ctrl_cdev, &ctx->ctrl_dev);
destroy_workqueue(sgx_encl_wq);
--
2.19.1
Because the kernel is untrusted, swapping pages in/out of the Enclave
Page Cache (EPC) has specialized requirements:
* The kernel cannot directly access EPC memory, i.e. cannot copy data
to/from the EPC.
* To evict a page from the EPC, the kernel must "prove" to hardware that
are no valid TLB entries for said page since a stale TLB entry would
allow an attacker to bypass SGX access controls.
* When loading a page back into the EPC, hardware must be able to verify
the integrity and freshness of the data.
* When loading an enclave page, e.g. regular pages and Thread Control
Structures (TCS), hardware must be able to associate the page with a
Secure Enclave Control Structure (SECS).
To satisfy the above requirements, the CPU provides dedicated ENCLS
functions to support paging data in/out of the EPC:
* EBLOCK: Mark a page as blocked in the EPC Map (EPCM). Attempting
to access a blocked page that misses the TLB will fault.
* ETRACK: Activate blocking tracking. Hardware verifies that all
translations for pages marked as "blocked" have been flushed
from the TLB.
* EPA: Add version array page to the EPC. As the name suggests, a
VA page is an 512-entry array of version numbers that are
used to uniquely identify pages evicted from the EPC.
* EWB: Write back a page from EPC to memory, e.g. RAM. Software
must supply a VA slot, memory to hold the a Paging Crypto
Metadata (PCMD) of the page and obviously backing for the
evicted page.
* ELD{B,U}: Load a page in {un}blocked state from memory to EPC. The
driver only uses the ELDU variant as there is no use case
for loading a page as "blocked" in a bare metal environment.
To top things off, all of the above ENCLS functions are subject to
strict concurrency rules, e.g. many operations will #GP fault if two
or more operations attempt to access common pages/structures.
To put it succinctly, paging in/out of the EPC requires coordinating
with the SGX driver where all of an enclave's tracking resides. But,
simply shoving all reclaim logic into the driver is not desirable as
doing so has unwanted long term implications:
* Oversubscribing EPC to KVM guests, i.e. virtualizing SGX in KVM and
swapping a guest's EPC pages (without the guest's cooperation) needs
the same high level flows for reclaim but has painfully different
semantics in the details.
* Accounting EPC, i.e. adding an EPC cgroup controller, is desirable
as EPC is effectively a specialized memory type and even more scarce
than system memory. Providing a single touchpoint for EPC accounting
regardless of end consumer greatly simplifies the EPC controller.
* Allowing the userspace-facing driver to be built as a loaded module
is desirable, e.g. for debug, testing and development. The cgroup
infrastructure does not support dependencies on loadable modules.
* Separating EPC swapping from the driver once it has been tightly
coupled to the driver is non-trivial (speaking from experience).
So, although the SGX driver is currently the sole consumer of EPC,
encapsulate EPC swapping in the driver to minimize the dependencies
between the core SGX code and driver, and do so in a way that can be
extended to an abstracted interface with minimal effort.
To that end, add functions to swap EPC pages to the driver. The user
of these functions will be the core SGX subsystem, which will be enabled
in a future patch.
* sgx_encl_page_{get,put}() - Attempt to pin/unpin (the owner of) an EPC
page so that it can be operated on by a reclaimer.
* sgx_encl_page_reclaim() - Mark a 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() - EBLOCK an EPC page
* sgx_encl_page_write() - Evict an EPC page to the regular memory via
EWB. Activates ETRACK (via sgx_encl_track()) if necessary.
Since we also need to be able to fault pages back into the EPC, add a
page fault handler to allocate an EPC page and ELDU a previously evicted
page.
Wire up the EPC manager's reclaim flow to the SGX driver's swapping
functionality. In the long term there will be multiple users of the
EPC manager, e.g. SGX driver and KVM, thus the interface between the
EPC manager and the driver is fairly genericized and decoupled. But
to avoid adding unusued infrastructure, do not add any indirection
between the EPC manager and the SGX driver. This has the unfortunate
and odd side effect of preventing the SGX driver from being compiled
as a loadable module. However, this should be a temporary situation
that is remedied when a second user of EPC is added, i.e. KVM.
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.
Cc: Andrew Morton <[email protected]>
Signed-off-by: Jarkko Sakkinen <[email protected]>
Co-developed-by: Sean Christopherson <[email protected]>
Signed-off-by: Sean Christopherson <[email protected]>
Co-developed-by: Serge Ayoun <[email protected]>
Signed-off-by: Serge Ayoun <[email protected]>
Co-developed-by: Shay Katz-zamir <[email protected]>
Signed-off-by: Shay Katz-zamir <[email protected]>
---
arch/x86/Kconfig | 3 +
arch/x86/kernel/cpu/sgx/Makefile | 1 +
arch/x86/kernel/cpu/sgx/driver/ioctl.c | 59 +++-
arch/x86/kernel/cpu/sgx/encl.c | 267 +++++++++++++++-
arch/x86/kernel/cpu/sgx/encl.h | 38 +++
arch/x86/kernel/cpu/sgx/main.c | 96 +++++-
arch/x86/kernel/cpu/sgx/reclaim.c | 410 +++++++++++++++++++++++++
arch/x86/kernel/cpu/sgx/sgx.h | 34 +-
8 files changed, 887 insertions(+), 21 deletions(-)
create mode 100644 arch/x86/kernel/cpu/sgx/reclaim.c
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 34257b5659cc..424bd58fd299 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1953,6 +1953,9 @@ config INTEL_SGX_DRIVER
specifying the public key hash for the launch enclave are writable so
that Linux has the full control to run enclaves.
+ If the driver is enabled, the page reclaimer in the core will be
+ enabled. It reclaims pages in LRU fashion from enclaves.
+
For details, see Documentation/x86/intel_sgx.rst
config EFI
diff --git a/arch/x86/kernel/cpu/sgx/Makefile b/arch/x86/kernel/cpu/sgx/Makefile
index 48174e5fc181..d261693aa11d 100644
--- a/arch/x86/kernel/cpu/sgx/Makefile
+++ b/arch/x86/kernel/cpu/sgx/Makefile
@@ -1,4 +1,5 @@
obj-y += encl.o
obj-y += encls.o
obj-y += main.o
+obj-y += reclaim.o
obj-$(CONFIG_INTEL_SGX_DRIVER) += driver/
diff --git a/arch/x86/kernel/cpu/sgx/driver/ioctl.c b/arch/x86/kernel/cpu/sgx/driver/ioctl.c
index 5d85bd3f7876..23ef9ae08a65 100644
--- a/arch/x86/kernel/cpu/sgx/driver/ioctl.c
+++ b/arch/x86/kernel/cpu/sgx/driver/ioctl.c
@@ -46,6 +46,51 @@ static int sgx_encl_get(unsigned long addr, struct sgx_encl **encl)
return ret;
}
+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->flags & SGX_ENCL_DEAD) {
+ mutex_unlock(&encl->lock);
+ return -EFAULT;
+ }
+
+ 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->flags & SGX_ENCL_DEAD) {
+ sgx_free_page(va_page->epc_page);
+ kfree(va_page);
+ mutex_unlock(&encl->lock);
+ return -EFAULT;
+ } else 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;
+}
+
static bool sgx_process_add_page_req(struct sgx_add_page_req *req,
struct sgx_epc_page *epc_page)
{
@@ -104,6 +149,7 @@ static bool sgx_process_add_page_req(struct sgx_add_page_req *req,
encl_page->encl = encl;
encl_page->epc_page = epc_page;
encl->secs_child_cnt++;
+ sgx_mark_page_reclaimable(encl_page->epc_page);
return true;
}
@@ -134,7 +180,7 @@ static void sgx_add_page_worker(struct work_struct *work)
if (skip_rest)
goto next;
- epc_page = sgx_alloc_page();
+ epc_page = sgx_alloc_page(req->encl_page, true);
mutex_lock(&encl->lock);
@@ -247,6 +293,7 @@ static struct sgx_encl *sgx_encl_alloc(struct sgx_secs *secs)
encl->allowed_attributes = SGX_ATTR_ALLOWED_MASK;
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->work, sgx_add_page_worker);
@@ -310,7 +357,7 @@ static int sgx_encl_create(struct sgx_encl *encl, struct sgx_secs *secs)
struct sgx_epc_page *secs_epc;
long ret;
- secs_epc = sgx_alloc_page();
+ secs_epc = sgx_alloc_page(&encl->secs, true);
if (IS_ERR(secs_epc)) {
ret = PTR_ERR(secs_epc);
return ret;
@@ -319,6 +366,10 @@ static int sgx_encl_create(struct sgx_encl *encl, struct sgx_secs *secs)
encl->secs.encl = encl;
encl->secs.epc_page = secs_epc;
+ ret = sgx_encl_grow(encl);
+ if (ret)
+ return ret;
+
pginfo.addr = 0;
pginfo.contents = (unsigned long)secs;
pginfo.metadata = (unsigned long)&secinfo;
@@ -531,6 +582,10 @@ static int sgx_encl_add_page(struct sgx_encl *encl, unsigned long addr,
return ret;
}
+ ret = sgx_encl_grow(encl);
+ if (ret)
+ return ret;
+
mutex_lock(&encl->lock);
if (encl->flags & (SGX_ENCL_INITIALIZED | SGX_ENCL_DEAD)) {
diff --git a/arch/x86/kernel/cpu/sgx/encl.c b/arch/x86/kernel/cpu/sgx/encl.c
index bd8bcd748976..1b8874699dd3 100644
--- a/arch/x86/kernel/cpu/sgx/encl.c
+++ b/arch/x86/kernel/cpu/sgx/encl.c
@@ -7,11 +7,91 @@
#include <linux/sched/mm.h>
#include "arch.h"
#include "encl.h"
+#include "encls.h"
#include "sgx.h"
+static int __sgx_encl_eldu(struct sgx_encl_page *encl_page,
+ struct sgx_epc_page *epc_page)
+{
+ unsigned long addr = SGX_ENCL_PAGE_ADDR(encl_page);
+ unsigned long va_offset = SGX_ENCL_PAGE_VA_OFFSET(encl_page);
+ struct sgx_encl *encl = encl_page->encl;
+ pgoff_t page_index = sgx_encl_get_index(encl, encl_page);
+ pgoff_t pcmd_index = sgx_pcmd_index(encl, page_index);
+ unsigned long pcmd_offset = sgx_pcmd_offset(page_index);
+ struct sgx_pageinfo pginfo;
+ struct page *backing;
+ struct page *pcmd;
+ int ret;
+
+ backing = sgx_encl_get_backing_page(encl, page_index);
+ if (IS_ERR(backing)) {
+ ret = PTR_ERR(backing);
+ goto err_backing;
+ }
+
+ pcmd = sgx_encl_get_backing_page(encl, pcmd_index);
+ if (IS_ERR(pcmd)) {
+ ret = PTR_ERR(pcmd);
+ goto 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) {
+ if (encls_failed(ret) || encls_returned_code(ret))
+ ENCLS_WARN(ret, "ELDU");
+
+ ret = -EFAULT;
+ }
+
+ kunmap_atomic((void *)(unsigned long)(pginfo.metadata - pcmd_offset));
+ kunmap_atomic((void *)(unsigned long)pginfo.contents);
+
+ put_page(pcmd);
+
+err_pcmd:
+ put_page(backing);
+
+err_backing:
+ return ret;
+}
+
+static struct sgx_epc_page *sgx_encl_eldu(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_eldu(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;
+ encl_page->epc_page = epc_page;
+
+ return epc_page;
+}
+
static struct sgx_encl_page *sgx_encl_load_page(struct sgx_encl *encl,
unsigned long addr)
{
+ struct sgx_epc_page *epc_page;
struct sgx_encl_page *entry;
/* If process was forked, VMA is still there but vm_private_data is set
@@ -29,10 +109,27 @@ static struct sgx_encl_page *sgx_encl_load_page(struct sgx_encl *encl,
return ERR_PTR(-EFAULT);
/* Page is already resident in the EPC. */
- if (entry->epc_page)
+ if (entry->epc_page) {
+ if (entry->desc & SGX_ENCL_PAGE_RECLAIMED)
+ return ERR_PTR(-EBUSY);
+
return entry;
+ }
+
+ if (!(encl->secs.epc_page)) {
+ epc_page = sgx_encl_eldu(&encl->secs);
+ if (IS_ERR(epc_page))
+ return ERR_CAST(epc_page);
+ }
+
+ epc_page = entry->epc_page ? entry->epc_page : sgx_encl_eldu(entry);
+ if (IS_ERR(epc_page))
+ return ERR_CAST(epc_page);
- return ERR_PTR(-EFAULT);
+ encl->secs_child_cnt++;
+ sgx_mark_page_reclaimable(entry->epc_page);
+
+ return entry;
}
static struct sgx_encl_mm *sgx_encl_get_mm(struct sgx_encl *encl,
@@ -152,6 +249,8 @@ static unsigned int sgx_vma_fault(struct vm_fault *vmf)
goto out;
}
+ sgx_encl_test_and_clear_young(vma->vm_mm, entry);
+
out:
mutex_unlock(&encl->lock);
return ret;
@@ -203,6 +302,7 @@ EXPORT_SYMBOL_GPL(sgx_encl_find);
*/
void sgx_encl_destroy(struct sgx_encl *encl)
{
+ struct sgx_va_page *va_page;
struct sgx_encl_page *entry;
struct radix_tree_iter iter;
void **slot;
@@ -227,6 +327,15 @@ void sgx_encl_destroy(struct sgx_encl *encl)
sgx_free_page(encl->secs.epc_page);
encl->secs.epc_page = NULL;
}
+
+
+ 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);
+ }
}
EXPORT_SYMBOL_GPL(sgx_encl_destroy);
@@ -356,3 +465,157 @@ void sgx_encl_release_mm(struct kref *ref)
kfree(mm);
}
+
+static int sgx_encl_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_encl_test_and_clear_young() - Test and reset the accessed bit
+ * @mm: mm_struct that is checked
+ * @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.
+ *
+ * Return: 1 if the page has been recently accessed and 0 if not.
+ */
+int sgx_encl_test_and_clear_young(struct mm_struct *mm,
+ 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(mm, addr, &vma);
+ if (ret)
+ return 0;
+
+ if (encl != vma->vm_private_data)
+ return 0;
+
+ ret = apply_to_page_range(vma->vm_mm, addr, PAGE_SIZE,
+ sgx_encl_test_and_clear_young_cb, vma->vm_mm);
+ if (ret < 0)
+ return 0;
+
+ return ret;
+}
+
+/**
+ * sgx_encl_reserve_page() - Reserve an enclave page
+ * @encl: an enclave
+ * @addr: a page address
+ *
+ * Load an enclave page and lock the enclave so that the page can be used by
+ * EDBG* and EMOD*.
+ *
+ * Return:
+ * an enclave page on success
+ * -EFAULT if the load fails
+ */
+struct sgx_encl_page *sgx_encl_reserve_page(struct sgx_encl *encl,
+ unsigned long addr)
+{
+ struct sgx_encl_page *entry;
+
+ for ( ; ; ) {
+ mutex_lock(&encl->lock);
+
+ entry = sgx_encl_load_page(encl, addr);
+ if (PTR_ERR(entry) != -EBUSY)
+ break;
+
+ mutex_unlock(&encl->lock);
+ }
+
+ if (IS_ERR(entry))
+ mutex_unlock(&encl->lock);
+
+ return entry;
+}
+EXPORT_SYMBOL(sgx_encl_reserve_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) {
+ WARN_ONCE(1, "sgx: EPA returned %d (0x%x)", ret, ret);
+ sgx_free_page(epc_page);
+ return ERR_PTR(-EFAULT);
+ }
+
+ return epc_page;
+}
+EXPORT_SYMBOL_GPL(sgx_alloc_va_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;
+}
diff --git a/arch/x86/kernel/cpu/sgx/encl.h b/arch/x86/kernel/cpu/sgx/encl.h
index 374ad3396684..41c55e565e92 100644
--- a/arch/x86/kernel/cpu/sgx/encl.h
+++ b/arch/x86/kernel/cpu/sgx/encl.h
@@ -10,6 +10,10 @@
/**
* 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_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.
*
* The page address for SECS is zero and is used by the subsystem to recognize
@@ -18,6 +22,8 @@
enum sgx_encl_page_desc {
SGX_ENCL_PAGE_TCS = BIT(0),
/* Bits 11:3 are available when the page is not swapped. */
+ SGX_ENCL_PAGE_RECLAIMED = BIT(3),
+ SGX_ENCL_PAGE_VA_OFFSET_MASK = GENMASK_ULL(11, 3),
SGX_ENCL_PAGE_ADDR_MASK = PAGE_MASK,
};
@@ -29,6 +35,7 @@ enum sgx_encl_page_desc {
struct sgx_encl_page {
unsigned long desc;
struct sgx_epc_page *epc_page;
+ struct sgx_va_page *va_page;
struct sgx_encl *encl;
};
@@ -60,15 +67,37 @@ struct sgx_encl {
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 work;
struct sgx_encl_page secs;
struct notifier_block pm_notifier;
+ cpumask_t cpumask;
+};
+
+#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;
};
extern const struct vm_operations_struct sgx_vm_ops;
+static inline pgoff_t sgx_pcmd_index(struct sgx_encl *encl,
+ pgoff_t page_index)
+{
+ return PFN_DOWN(encl->size) + 1 + (page_index >> 5);
+}
+
+static inline unsigned long sgx_pcmd_offset(pgoff_t page_index)
+{
+ return (page_index & (PAGE_SIZE / sizeof(struct sgx_pcmd) - 1)) *
+ sizeof(struct sgx_pcmd);
+}
+
enum sgx_encl_mm_iter {
SGX_ENCL_MM_ITER_DONE = 0,
SGX_ENCL_MM_ITER_NEXT = 1,
@@ -84,5 +113,14 @@ struct page *sgx_encl_get_backing_page(struct sgx_encl *encl, pgoff_t index);
struct sgx_encl_mm *sgx_encl_next_mm(struct sgx_encl *encl,
struct sgx_encl_mm *mm, int *iter);
void sgx_encl_release_mm(struct kref *ref);
+int sgx_encl_test_and_clear_young(struct mm_struct *mm,
+ struct sgx_encl_page *page);
+struct sgx_encl_page *sgx_encl_reserve_page(struct sgx_encl *encl,
+ unsigned long addr);
+
+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);
#endif /* _X86_ENCL_H */
diff --git a/arch/x86/kernel/cpu/sgx/main.c b/arch/x86/kernel/cpu/sgx/main.c
index d88dc3d1d4a7..a9485a73c58c 100644
--- a/arch/x86/kernel/cpu/sgx/main.c
+++ b/arch/x86/kernel/cpu/sgx/main.c
@@ -17,13 +17,13 @@ EXPORT_SYMBOL_GPL(sgx_epc_sections);
bool sgx_enabled;
EXPORT_SYMBOL_GPL(sgx_enabled);
-static int sgx_nr_epc_sections;
+int sgx_nr_epc_sections;
/* A per-cpu cache for the last known values of IA32_SGXLEPUBKEYHASHx MSRs. */
static DEFINE_PER_CPU(u64 [4], sgx_lepubkeyhash_cache);
-static void sgx_section_put_page(struct sgx_epc_section *section,
- struct sgx_epc_page *page)
+void sgx_section_put_page(struct sgx_epc_section *section,
+ struct sgx_epc_page *page)
{
list_add_tail(&page->list, §ion->page_list);
section->free_cnt++;
@@ -44,16 +44,7 @@ static struct sgx_epc_page *sgx_section_get_page(
return page;
}
-/**
- * sgx_alloc_page - Allocate an EPC page
- *
- * Try to grab a page from the free EPC page list.
- *
- * Return:
- * a pointer to a &struct sgx_epc_page instance,
- * -errno on error
- */
-struct sgx_epc_page *sgx_alloc_page(void)
+static struct sgx_epc_page *sgx_try_alloc_page(void *owner)
{
struct sgx_epc_section *section;
struct sgx_epc_page *page;
@@ -65,11 +56,58 @@ struct sgx_epc_page *sgx_alloc_page(void)
page = sgx_section_get_page(section);
spin_unlock(§ion->lock);
- if (page)
+ if (page) {
+ page->owner = owner;
return page;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * sgx_alloc_page - Allocate an EPC page
+ * @owner: the owner of the EPC page
+ * @reclaim: reclaim pages if necessary
+ *
+ * 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 instance,
+ * -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();
}
- return ERR_PTR(-ENOMEM);
+ if (sgx_calc_free_cnt() < SGX_NR_LOW_PAGES)
+ wake_up(&ksgxswapd_waitq);
+
+ return entry;
}
EXPORT_SYMBOL_GPL(sgx_alloc_page);
@@ -77,10 +115,12 @@ EXPORT_SYMBOL_GPL(sgx_alloc_page);
* __sgx_free_page - Free an EPC page
* @page: pointer a previously allocated EPC page
*
- * EREMOVE an EPC page and insert it back to the list of free pages.
+ * EREMOVE an EPC page and insert it back to the list of free pages. If the
+ * page is reclaimable, delete it from the active page list.
*
* 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)
@@ -88,6 +128,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;
@@ -113,6 +170,7 @@ 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);
@@ -285,6 +343,12 @@ static __init int sgx_init(void)
if (ret)
return ret;
+ ret = sgx_page_reclaimer_init();
+ if (ret) {
+ sgx_page_cache_teardown();
+ return ret;
+ }
+
sgx_enabled = true;
return 0;
}
diff --git a/arch/x86/kernel/cpu/sgx/reclaim.c b/arch/x86/kernel/cpu/sgx/reclaim.c
new file mode 100644
index 000000000000..ba67576f6515
--- /dev/null
+++ b/arch/x86/kernel/cpu/sgx/reclaim.c
@@ -0,0 +1,410 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright(c) 2016-19 Intel Corporation.
+
+#include <linux/freezer.h>
+#include <linux/highmem.h>
+#include <linux/kthread.h>
+#include <linux/pagemap.h>
+#include <linux/ratelimit.h>
+#include <linux/slab.h>
+#include <linux/sched/mm.h>
+#include <linux/sched/signal.h>
+#include "driver/driver.h"
+#include "sgx.h"
+
+LIST_HEAD(sgx_active_page_list);
+DEFINE_SPINLOCK(sgx_active_page_list_lock);
+DECLARE_WAIT_QUEUE_HEAD(ksgxswapd_waitq);
+
+static struct task_struct *ksgxswapd_tsk;
+
+/**
+ * sgx_mark_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_mark_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_mark_page_reclaimable);
+
+bool sgx_reclaimer_get(struct sgx_epc_page *epc_page)
+{
+ struct sgx_encl_page *encl_page = epc_page->owner;
+ struct sgx_encl *encl = encl_page->encl;
+
+ return kref_get_unless_zero(&encl->refcount) != 0;
+}
+
+void sgx_reclaimer_put(struct sgx_epc_page *epc_page)
+{
+ struct sgx_encl_page *encl_page = epc_page->owner;
+ struct sgx_encl *encl = encl_page->encl;
+
+ kref_put(&encl->refcount, sgx_encl_release);
+}
+
+static bool sgx_reclaimer_evict(struct sgx_epc_page *epc_page)
+{
+ struct sgx_encl_page *page = epc_page->owner;
+ struct sgx_encl *encl = page->encl;
+ struct sgx_encl_mm *next_mm = NULL;
+ struct sgx_encl_mm *prev_mm = NULL;
+ bool ret = true;
+ int iter;
+
+ while (true) {
+ next_mm = sgx_encl_next_mm(encl, prev_mm, &iter);
+ if (prev_mm) {
+ mmdrop(prev_mm->mm);
+ kref_put(&prev_mm->refcount, sgx_encl_release_mm);
+ }
+ prev_mm = next_mm;
+
+ if (iter == SGX_ENCL_MM_ITER_DONE)
+ break;
+
+ if (iter == SGX_ENCL_MM_ITER_RESTART)
+ continue;
+
+ down_read(&next_mm->mm->mmap_sem);
+ mutex_lock(&encl->lock);
+
+ if (encl->flags & SGX_ENCL_DEAD) {
+ page->desc |= SGX_ENCL_PAGE_RECLAIMED;
+ ret = true;
+ goto out_stop;
+ }
+
+ ret = !sgx_encl_test_and_clear_young(next_mm->mm, page);
+ if (!ret)
+ goto out_stop;
+
+ mutex_unlock(&encl->lock);
+ up_read(&next_mm->mm->mmap_sem);
+ }
+
+ page->desc |= SGX_ENCL_PAGE_RECLAIMED;
+ return true;
+out_stop:
+ mutex_unlock(&encl->lock);
+ up_read(&next_mm->mm->mmap_sem);
+ mmdrop(next_mm->mm);
+ kref_put(&next_mm->refcount, sgx_encl_release_mm);
+ return ret;
+}
+
+static void sgx_reclaimer_block(struct sgx_epc_page *epc_page)
+{
+ struct sgx_encl_page *page = epc_page->owner;
+ unsigned long addr = SGX_ENCL_PAGE_ADDR(page);
+ struct sgx_encl *encl = page->encl;
+ struct sgx_encl_mm *next_mm = NULL;
+ struct sgx_encl_mm *prev_mm = NULL;
+ struct vm_area_struct *vma;
+ int iter;
+ int ret;
+
+ while (true) {
+ next_mm = sgx_encl_next_mm(encl, prev_mm, &iter);
+ if (prev_mm) {
+ mmdrop(prev_mm->mm);
+ kref_put(&prev_mm->refcount, sgx_encl_release_mm);
+ }
+ prev_mm = next_mm;
+
+ if (iter == SGX_ENCL_MM_ITER_DONE)
+ break;
+
+ if (iter == SGX_ENCL_MM_ITER_RESTART)
+ continue;
+
+ down_read(&next_mm->mm->mmap_sem);
+ mutex_lock(&encl->lock);
+
+ ret = sgx_encl_find(next_mm->mm, addr, &vma);
+ if (!ret && encl == vma->vm_private_data)
+ zap_vma_ptes(vma, addr, PAGE_SIZE);
+
+ mutex_unlock(&encl->lock);
+ up_read(&next_mm->mm->mmap_sem);
+ }
+
+ mutex_lock(&encl->lock);
+
+ if (!(encl->flags & SGX_ENCL_DEAD)) {
+ ret = __eblock(sgx_epc_addr(epc_page));
+ if (encls_failed(ret))
+ ENCLS_WARN(ret, "EBLOCK");
+ }
+
+ mutex_unlock(&encl->lock);
+}
+
+static int __sgx_encl_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 = epc_page->owner;
+ pgoff_t page_index = sgx_encl_get_index(encl, encl_page);
+ pgoff_t pcmd_index = sgx_pcmd_index(encl, page_index);
+ unsigned long pcmd_offset = sgx_pcmd_offset(page_index);
+ struct sgx_pageinfo pginfo;
+ struct page *backing;
+ struct page *pcmd;
+ int ret;
+
+ backing = sgx_encl_get_backing_page(encl, page_index);
+ if (IS_ERR(backing)) {
+ ret = PTR_ERR(backing);
+ goto err_backing;
+ }
+
+ pcmd = sgx_encl_get_backing_page(encl, pcmd_index);
+ if (IS_ERR(pcmd)) {
+ ret = PTR_ERR(pcmd);
+ goto err_pcmd;
+ }
+
+ 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);
+
+ set_page_dirty(pcmd);
+ put_page(pcmd);
+ set_page_dirty(backing);
+
+err_pcmd:
+ put_page(backing);
+
+err_backing:
+ return ret;
+}
+
+static void sgx_ipi_cb(void *info)
+{
+}
+
+static void sgx_encl_ewb(struct sgx_epc_page *epc_page, bool do_free)
+{
+ struct sgx_encl_page *encl_page = epc_page->owner;
+ struct sgx_encl *encl = encl_page->encl;
+ struct sgx_encl_mm *next_mm = NULL;
+ struct sgx_encl_mm *prev_mm = NULL;
+ struct sgx_va_page *va_page;
+ unsigned int va_offset;
+ int iter;
+ int ret;
+
+ cpumask_clear(&encl->cpumask);
+
+ while (true) {
+ next_mm = sgx_encl_next_mm(encl, prev_mm, &iter);
+ if (prev_mm) {
+ mmdrop(prev_mm->mm);
+ kref_put(&prev_mm->refcount, sgx_encl_release_mm);
+ }
+ prev_mm = next_mm;
+
+ if (iter == SGX_ENCL_MM_ITER_DONE)
+ break;
+
+ if (iter == SGX_ENCL_MM_ITER_RESTART)
+ continue;
+
+ cpumask_or(&encl->cpumask, &encl->cpumask,
+ mm_cpumask(next_mm->mm));
+ }
+
+ encl_page->desc &= ~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_encl_ewb(encl, epc_page, va_page, va_offset);
+ if (ret == SGX_NOT_TRACKED) {
+ ret = __etrack(sgx_epc_addr(encl->secs.epc_page));
+ if (ret) {
+ if (encls_failed(ret) ||
+ encls_returned_code(ret))
+ ENCLS_WARN(ret, "ETRACK");
+ }
+
+ ret = __sgx_encl_ewb(encl, epc_page, va_page,
+ va_offset);
+ if (ret == SGX_NOT_TRACKED) {
+ /* slow path, IPI needed */
+ on_each_cpu_mask(&encl->cpumask, sgx_ipi_cb,
+ NULL, 1);
+ ret = __sgx_encl_ewb(encl, epc_page, va_page,
+ va_offset);
+ }
+ }
+
+ if (ret)
+ if (encls_failed(ret) || encls_returned_code(ret))
+ ENCLS_WARN(ret, "EWB");
+
+ 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);
+
+ encl_page->epc_page = NULL;
+}
+
+static void sgx_reclaimer_write(struct sgx_epc_page *epc_page)
+{
+ struct sgx_encl_page *encl_page = epc_page->owner;
+ struct sgx_encl *encl = encl_page->encl;
+
+ mutex_lock(&encl->lock);
+
+ sgx_encl_ewb(epc_page, false);
+ encl->secs_child_cnt--;
+ if (!encl->secs_child_cnt &&
+ (encl->flags & (SGX_ENCL_DEAD | SGX_ENCL_INITIALIZED))) {
+ sgx_encl_ewb(encl->secs.epc_page, true);
+ }
+
+ mutex_unlock(&encl->lock);
+}
+
+/**
+ * sgx_reclaim_pages() - Reclaim EPC pages from the consumers
+ * 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.
+ */
+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_reclaimer_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_reclaimer_evict(epc_page))
+ continue;
+
+ sgx_reclaimer_put(epc_page);
+
+ spin_lock(&sgx_active_page_list_lock);
+ list_add_tail(&epc_page->list, &sgx_active_page_list);
+ spin_unlock(&sgx_active_page_list_lock);
+
+ chunk[i] = NULL;
+ }
+
+ for (i = 0; i < j; i++) {
+ epc_page = chunk[i];
+ if (epc_page)
+ sgx_reclaimer_block(epc_page);
+ }
+
+ for (i = 0; i < j; i++) {
+ epc_page = chunk[i];
+ if (epc_page) {
+ sgx_reclaimer_write(epc_page);
+ sgx_reclaimer_put(epc_page);
+ epc_page->desc &= ~SGX_EPC_PAGE_RECLAIMABLE;
+
+ section = sgx_epc_section(epc_page);
+ spin_lock(§ion->lock);
+ sgx_section_put_page(section, epc_page);
+ spin_unlock(§ion->lock);
+ }
+ }
+}
+
+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;
+}
+
+int sgx_page_reclaimer_init(void)
+{
+ struct task_struct *tsk;
+
+ tsk = kthread_run(ksgxswapd, NULL, "ksgxswapd");
+ if (IS_ERR(tsk))
+ return PTR_ERR(tsk);
+
+ ksgxswapd_tsk = tsk;
+
+ return 0;
+}
diff --git a/arch/x86/kernel/cpu/sgx/sgx.h b/arch/x86/kernel/cpu/sgx/sgx.h
index 2337b63ba487..ed587627ca81 100644
--- a/arch/x86/kernel/cpu/sgx/sgx.h
+++ b/arch/x86/kernel/cpu/sgx/sgx.h
@@ -12,6 +12,7 @@
struct sgx_epc_page {
unsigned long desc;
+ struct sgx_encl_page *owner;
struct list_head list;
};
@@ -42,9 +43,14 @@ extern bool sgx_enabled;
* 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 has been been marked as reclaimable.
+ * Pages need to be colored this way because a page
+ * can be out of the active page list in the
+ * process of being swapped out.
*/
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 */
};
@@ -60,10 +66,36 @@ static inline void *sgx_epc_addr(struct sgx_epc_page *page)
return section->va + (page->desc & PAGE_MASK) - section->pa;
}
-struct sgx_epc_page *sgx_alloc_page(void);
+void sgx_section_put_page(struct sgx_epc_section *section,
+ struct sgx_epc_page *page);
+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);
+/**
+ * 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,
+};
+
+extern int sgx_nr_epc_sections;
+extern struct list_head sgx_active_page_list;
+extern spinlock_t sgx_active_page_list_lock;
+extern struct wait_queue_head(ksgxswapd_waitq);
+
+void sgx_mark_page_reclaimable(struct sgx_epc_page *page);
+unsigned long sgx_calc_free_cnt(void);
+void sgx_reclaim_pages(void);
+int sgx_page_reclaimer_init(void);
+
#endif /* _X86_SGX_H */
--
2.19.1
At this time there is no support for reclaiming pages prior to the
owner explicitly freeing the page. As for freeing pages, because
freeing a page is expected to succeed in the vast majority of cases
and because most call sites will not be equipped to handle failure,
provide a variant for freeing a page that warns on failure, e.g. due
to ENCLS[EREMOVE] failing.
Signed-off-by: Jarkko Sakkinen <[email protected]>
Co-developed-by: Sean Christopherson <[email protected]>
Signed-off-by: Sean Christopherson <[email protected]>
---
arch/x86/kernel/cpu/sgx/main.c | 89 ++++++++++++++++++++++++++++++++++
arch/x86/kernel/cpu/sgx/sgx.h | 4 ++
2 files changed, 93 insertions(+)
diff --git a/arch/x86/kernel/cpu/sgx/main.c b/arch/x86/kernel/cpu/sgx/main.c
index 18ce4acdd7ef..f88c8d86c2a8 100644
--- a/arch/x86/kernel/cpu/sgx/main.c
+++ b/arch/x86/kernel/cpu/sgx/main.c
@@ -9,6 +9,7 @@
#include <linux/sched/signal.h>
#include <linux/slab.h>
#include "arch.h"
+#include "encls.h"
#include "sgx.h"
struct sgx_epc_section sgx_epc_sections[SGX_MAX_EPC_SECTIONS];
@@ -23,6 +24,94 @@ static void sgx_section_put_page(struct sgx_epc_section *section,
section->free_cnt++;
}
+static struct sgx_epc_page *sgx_section_get_page(
+ struct sgx_epc_section *section)
+{
+ struct sgx_epc_page *page;
+
+ if (!section->free_cnt)
+ return NULL;
+
+ page = list_first_entry(§ion->page_list,
+ struct sgx_epc_page, list);
+ list_del_init(&page->list);
+ section->free_cnt--;
+ return page;
+}
+
+/**
+ * sgx_alloc_page - Allocate an EPC page
+ *
+ * Try to grab a page from the free EPC page list.
+ *
+ * Return:
+ * a pointer to a &struct sgx_epc_page instance,
+ * -errno on error
+ */
+struct sgx_epc_page *sgx_alloc_page(void)
+{
+ 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(§ion->lock);
+ page = sgx_section_get_page(section);
+ spin_unlock(§ion->lock);
+
+ if (page)
+ return page;
+ }
+
+ return ERR_PTR(-ENOMEM);
+}
+EXPORT_SYMBOL_GPL(sgx_alloc_page);
+
+/**
+ * __sgx_free_page - Free an EPC page
+ * @page: pointer a previously allocated EPC page
+ *
+ * EREMOVE an EPC page and insert it back to the list of free pages.
+ *
+ * Return:
+ * 0 on success
+ * 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(§ion->lock);
+ sgx_section_put_page(section, page);
+ spin_unlock(§ion->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, and WARN
+ * if EREMOVE fails. 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: 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)
{
struct sgx_epc_page *page;
diff --git a/arch/x86/kernel/cpu/sgx/sgx.h b/arch/x86/kernel/cpu/sgx/sgx.h
index 228e3dae360d..86b4c74b6089 100644
--- a/arch/x86/kernel/cpu/sgx/sgx.h
+++ b/arch/x86/kernel/cpu/sgx/sgx.h
@@ -59,4 +59,8 @@ static inline void *sgx_epc_addr(struct sgx_epc_page *page)
return section->va + (page->desc & PAGE_MASK) - section->pa;
}
+struct sgx_epc_page *sgx_alloc_page(void);
+int __sgx_free_page(struct sgx_epc_page *page);
+void sgx_free_page(struct sgx_epc_page *page);
+
#endif /* _X86_SGX_H */
--
2.19.1
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]>
---
arch/x86/kernel/cpu/sgx/encl.c | 97 ++++++++++++++++++++++++++++++++++
1 file changed, 97 insertions(+)
diff --git a/arch/x86/kernel/cpu/sgx/encl.c b/arch/x86/kernel/cpu/sgx/encl.c
index 1b8874699dd3..5b5fc933ee19 100644
--- a/arch/x86/kernel/cpu/sgx/encl.c
+++ b/arch/x86/kernel/cpu/sgx/encl.c
@@ -256,10 +256,107 @@ static unsigned int sgx_vma_fault(struct vm_fault *vmf)
return ret;
}
+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)
+ return -EIO;
+
+ 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)
+ return -EIO;
+
+ 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) {
+ entry = sgx_encl_reserve_page(encl, (addr + i) & PAGE_MASK);
+ if (IS_ERR(entry)) {
+ ret = PTR_ERR(entry);
+ break;
+ }
+
+ 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)
+ goto out;
+
+ if (write) {
+ memcpy(data + offset, buf + i, cnt);
+ ret = sgx_edbgwr(encl, entry, align, data);
+ if (ret)
+ goto out;
+ } else
+ memcpy(buf + i, data + offset, cnt);
+
+out:
+ mutex_unlock(&encl->lock);
+
+ if (ret)
+ break;
+ }
+
+ 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,
};
EXPORT_SYMBOL_GPL(sgx_vm_ops);
--
2.19.1
From: Sean Christopherson <[email protected]>
The basic concept and implementation is very similar to the kernel's
exception fixup mechanism. The key differences are that the kernel
handler is hardcoded and the fixup entry addresses are relative to
the overall table as opposed to individual entries.
Hardcoding the kernel handler avoids the need to figure out how to
get userspace code to point at a kernel function. Given that the
expected usage is to propagate information to userspace, dumping all
fault information into registers is likely the desired behavior for
the vast majority of yet-to-be-created functions. Use registers
DI, SI and DX to communicate fault information, which follows Linux's
ABI for register consumption and hopefully avoids conflict with
hardware features that might leverage the fixup capabilities, e.g.
register usage for SGX instructions was at least partially designed
with calling conventions in mind.
Making fixup addresses relative to the overall table allows the table
to be stripped from the final vDSO image (it's a kernel construct)
without complicating the offset logic, e.g. entry-relative addressing
would also need to account for the table's location relative to the
image.
Regarding stripping the table, modify vdso2c to extract the table from
the raw, a.k.a. unstripped, data and dump it as a standalone byte array
in the resulting .c file. The original base of the table, its length
and a pointer to the byte array are captured in struct vdso_image.
Alternatively, the table could be dumped directly into the struct,
but because the number of entries can vary per image, that would
require either hardcoding a max sized table into the struct definition
or defining the table as a flexible length array. The flexible length
array approach has zero benefits, e.g. the base/size are still needed,
and prevents reusing the extraction code, while hardcoding the max size
adds ongoing maintenance just to avoid exporting the explicit size.
The immediate use case is for Intel Software Guard Extensions (SGX).
SGX introduces a new CPL3-only "enclave" mode that runs as a sort of
black box shared object that is hosted by an untrusted "normal" CPl3
process.
Entering an enclave can only be done through SGX-specific instructions,
EENTER and ERESUME, and is a non-trivial process. Because of the
complexity of transitioning to/from an enclave, the vast majority of
enclaves are expected to utilize a library to handle the actual
transitions. This is roughly analogous to how e.g. libc implementations
are used by most applications.
Another crucial characteristic of SGX enclaves is that they can generate
exceptions as part of their normal (at least as "normal" as SGX can be)
operation that need to be handled *in* the enclave and/or are unique
to SGX.
And because they are essentially fancy shared objects, a process can
host any number of enclaves, each of which can execute multiple threads
simultaneously.
Putting everything together, userspace enclaves will utilize a library
that must be prepared to handle any and (almost) all exceptions any time
at least one thread may be executing in an enclave. Leveraging signals
to handle the enclave exceptions is unpleasant, to put it mildly, e.g.
the SGX library must constantly (un)register its signal handler based
on whether or not at least one thread is executing in an enclave, and
filter and forward exceptions that aren't related to its enclaves. This
becomes particularly nasty when using multiple levels of libraries that
register signal handlers, e.g. running an enclave via cgo inside of the
Go runtime.
Enabling exception fixup in vDSO allows the kernel to provide a vDSO
function that wraps the low-level transitions to/from the enclave, i.e.
the EENTER and ERESUME instructions. The vDSO function can intercept
exceptions that would otherwise generate a signal and return the fault
information directly to its caller, thus avoiding the need to juggle
signal handlers.
Note that unlike the kernel's _ASM_EXTABLE_HANDLE implementation, the
'C' version of _ASM_VDSO_EXTABLE_HANDLE doesn't use a pre-compiled
assembly macro. Duplicating four lines of code is simpler than adding
the necessary infrastructure to generate pre-compiled assembly and the
intended benefit of massaging GCC's inlining algorithm is unlikely to
realized in the vDSO any time soon, if ever.
Suggested-by: Andy Lutomirski <[email protected]>
Cc: Andy Lutomirski <[email protected]>
Cc: Jarkko Sakkinen <[email protected]>
Cc: Dave Hansen <[email protected]>
Cc: Josh Triplett <[email protected]>
Signed-off-by: Sean Christopherson <[email protected]>
---
arch/x86/entry/vdso/Makefile | 4 +-
arch/x86/entry/vdso/extable.c | 37 +++++++++++++++++
arch/x86/entry/vdso/extable.h | 29 ++++++++++++++
arch/x86/entry/vdso/vdso-layout.lds.S | 9 ++++-
arch/x86/entry/vdso/vdso2c.h | 58 +++++++++++++++++++++++----
arch/x86/include/asm/vdso.h | 5 +++
6 files changed, 131 insertions(+), 11 deletions(-)
create mode 100644 arch/x86/entry/vdso/extable.c
create mode 100644 arch/x86/entry/vdso/extable.h
diff --git a/arch/x86/entry/vdso/Makefile b/arch/x86/entry/vdso/Makefile
index 5bfe2243a08f..34bcf87d358c 100644
--- a/arch/x86/entry/vdso/Makefile
+++ b/arch/x86/entry/vdso/Makefile
@@ -20,7 +20,7 @@ VDSO32-$(CONFIG_IA32_EMULATION) := y
vobjs-y := vdso-note.o vclock_gettime.o vgetcpu.o
# files to link into kernel
-obj-y += vma.o
+obj-y += vma.o extable.o
OBJECT_FILES_NON_STANDARD_vma.o := n
# vDSO images to build
@@ -115,7 +115,7 @@ $(obj)/%-x32.o: $(obj)/%.o FORCE
targets += vdsox32.lds $(vobjx32s-y)
-$(obj)/%.so: OBJCOPYFLAGS := -S
+$(obj)/%.so: OBJCOPYFLAGS := -S --remove-section __ex_table
$(obj)/%.so: $(obj)/%.so.dbg
$(call if_changed,objcopy)
diff --git a/arch/x86/entry/vdso/extable.c b/arch/x86/entry/vdso/extable.c
new file mode 100644
index 000000000000..49284d560d36
--- /dev/null
+++ b/arch/x86/entry/vdso/extable.c
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/err.h>
+#include <linux/mm.h>
+#include <asm/current.h>
+#include <asm/vdso.h>
+
+struct vdso_exception_table_entry {
+ int insn, fixup;
+};
+
+bool fixup_vdso_exception(struct pt_regs *regs, int trapnr,
+ unsigned long error_code, unsigned long fault_addr)
+{
+ const struct vdso_image *image = current->mm->context.vdso_image;
+ const struct vdso_exception_table_entry *extable;
+ unsigned int nr_entries, i;
+ unsigned long base;
+
+ if (!current->mm->context.vdso)
+ return false;
+
+ base = (unsigned long)current->mm->context.vdso + image->extable_base;
+ nr_entries = image->extable_len / (sizeof(*extable));
+ extable = image->extable;
+
+ for (i = 0; i < nr_entries; i++) {
+ if (regs->ip == base + extable[i].insn) {
+ regs->ip = base + extable[i].fixup;
+ regs->di = trapnr;
+ regs->si = error_code;
+ regs->dx = fault_addr;
+ return true;
+ }
+ }
+
+ return false;
+}
diff --git a/arch/x86/entry/vdso/extable.h b/arch/x86/entry/vdso/extable.h
new file mode 100644
index 000000000000..aafdac396948
--- /dev/null
+++ b/arch/x86/entry/vdso/extable.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __VDSO_EXTABLE_H
+#define __VDSO_EXTABLE_H
+
+/*
+ * Inject exception fixup for vDSO code. Unlike normal exception fixup,
+ * vDSO uses a dedicated handler the addresses are relative to the overall
+ * exception table, not each individual entry.
+ */
+#ifdef __ASSEMBLY__
+#define _ASM_VDSO_EXTABLE_HANDLE(from, to) \
+ ASM_VDSO_EXTABLE_HANDLE from to
+
+.macro ASM_VDSO_EXTABLE_HANDLE from:req to:req
+ .pushsection __ex_table, "a"
+ .long (\from) - __ex_table
+ .long (\to) - __ex_table
+ .popsection
+.endm
+#else
+#define _ASM_VDSO_EXTABLE_HANDLE(from, to) \
+ ".pushsection __ex_table, \"a\"\n" \
+ ".long (" #from ") - __ex_table\n" \
+ ".long (" #to ") - __ex_table\n" \
+ ".popsection\n"
+#endif
+
+#endif /* __VDSO_EXTABLE_H */
+
diff --git a/arch/x86/entry/vdso/vdso-layout.lds.S b/arch/x86/entry/vdso/vdso-layout.lds.S
index 93c6dc7812d0..8ef849064501 100644
--- a/arch/x86/entry/vdso/vdso-layout.lds.S
+++ b/arch/x86/entry/vdso/vdso-layout.lds.S
@@ -63,11 +63,18 @@ SECTIONS
* stuff that isn't used at runtime in between.
*/
- .text : { *(.text*) } :text =0x90909090,
+ .text : {
+ *(.text*)
+ *(.fixup)
+ } :text =0x90909090,
+
+
.altinstructions : { *(.altinstructions) } :text
.altinstr_replacement : { *(.altinstr_replacement) } :text
+ __ex_table : { *(__ex_table) } :text
+
/DISCARD/ : {
*(.discard)
*(.discard.*)
diff --git a/arch/x86/entry/vdso/vdso2c.h b/arch/x86/entry/vdso/vdso2c.h
index fa847a620f40..eca2f808bec3 100644
--- a/arch/x86/entry/vdso/vdso2c.h
+++ b/arch/x86/entry/vdso/vdso2c.h
@@ -5,6 +5,41 @@
* are built for 32-bit userspace.
*/
+static void BITSFUNC(copy)(FILE *outfile, const unsigned char *data, size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++) {
+ if (i % 10 == 0)
+ fprintf(outfile, "\n\t");
+ fprintf(outfile, "0x%02X, ", (int)(data)[i]);
+ }
+}
+
+
+/*
+ * Extract a section from the input data into a standalone blob. Used to
+ * capture kernel-only data that needs to persist indefinitely, e.g. the
+ * exception fixup tables, but only in the kernel, i.e. the section can
+ * be stripped from the final vDSO image.
+ */
+static void BITSFUNC(extract)(const unsigned char *data, size_t data_len,
+ FILE *outfile, ELF(Shdr) *sec, const char *name)
+{
+ unsigned long offset;
+ size_t len;
+
+ offset = (unsigned long)GET_LE(&sec->sh_offset);
+ len = (size_t)GET_LE(&sec->sh_size);
+
+ if (offset + len > data_len)
+ fail("section to extract overruns input data");
+
+ fprintf(outfile, "static const unsigned char %s[%lu] = {", name, len);
+ BITSFUNC(copy)(outfile, data + offset, len);
+ fprintf(outfile, "\n};\n\n");
+}
+
static void BITSFUNC(go)(void *raw_addr, size_t raw_len,
void *stripped_addr, size_t stripped_len,
FILE *outfile, const char *name)
@@ -14,9 +49,8 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len,
unsigned long mapping_size;
ELF(Ehdr) *hdr = (ELF(Ehdr) *)raw_addr;
int i;
- unsigned long j;
ELF(Shdr) *symtab_hdr = NULL, *strtab_hdr, *secstrings_hdr,
- *alt_sec = NULL;
+ *alt_sec = NULL, *extable_sec = NULL;
ELF(Dyn) *dyn = 0, *dyn_end = 0;
const char *secstrings;
INT_BITS syms[NSYMS] = {};
@@ -78,6 +112,8 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len,
if (!strcmp(secstrings + GET_LE(&sh->sh_name),
".altinstructions"))
alt_sec = sh;
+ if (!strcmp(secstrings + GET_LE(&sh->sh_name), "__ex_table"))
+ extable_sec = sh;
}
if (!symtab_hdr)
@@ -149,13 +185,11 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len,
fprintf(outfile,
"static unsigned char raw_data[%lu] __ro_after_init __aligned(PAGE_SIZE) = {",
mapping_size);
- for (j = 0; j < stripped_len; j++) {
- if (j % 10 == 0)
- fprintf(outfile, "\n\t");
- fprintf(outfile, "0x%02X, ",
- (int)((unsigned char *)stripped_addr)[j]);
- }
+ BITSFUNC(copy)(outfile, stripped_addr, stripped_len);
fprintf(outfile, "\n};\n\n");
+ if (extable_sec)
+ BITSFUNC(extract)(raw_addr, raw_len, outfile,
+ extable_sec, "extable");
fprintf(outfile, "const struct vdso_image %s = {\n", name);
fprintf(outfile, "\t.data = raw_data,\n");
@@ -166,6 +200,14 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len,
fprintf(outfile, "\t.alt_len = %lu,\n",
(unsigned long)GET_LE(&alt_sec->sh_size));
}
+ if (extable_sec) {
+ fprintf(outfile, "\t.extable_base = %lu,\n",
+ (unsigned long)GET_LE(&extable_sec->sh_offset));
+ fprintf(outfile, "\t.extable_len = %lu,\n",
+ (unsigned long)GET_LE(&extable_sec->sh_size));
+ fprintf(outfile, "\t.extable = extable,\n");
+ }
+
for (i = 0; i < NSYMS; i++) {
if (required_syms[i].export && syms[i])
fprintf(outfile, "\t.sym_%s = %" PRIi64 ",\n",
diff --git a/arch/x86/include/asm/vdso.h b/arch/x86/include/asm/vdso.h
index 27566e57e87d..1c8a6a8f7b59 100644
--- a/arch/x86/include/asm/vdso.h
+++ b/arch/x86/include/asm/vdso.h
@@ -15,6 +15,8 @@ struct vdso_image {
unsigned long size; /* Always a multiple of PAGE_SIZE */
unsigned long alt, alt_len;
+ unsigned long extable_base, extable_len;
+ const void *extable;
long sym_vvar_start; /* Negative offset to the vvar area */
@@ -45,6 +47,9 @@ extern void __init init_vdso_image(const struct vdso_image *image);
extern int map_vdso_once(const struct vdso_image *image, unsigned long addr);
+extern bool fixup_vdso_exception(struct pt_regs *regs, int trapnr,
+ unsigned long error_code,
+ unsigned long fault_addr);
#endif /* __ASSEMBLER__ */
#endif /* _ASM_X86_VDSO_H */
--
2.19.1
From: Sean Christopherson <[email protected]>
vDSO functions can now leverage an exception fixup mechanism similar to
kernel exception fixup. For vDSO exception fixup, the initial user is
Intel's Software Guard Extensions (SGX), which will wrap the low-level
transitions to/from the enclave, i.e. EENTER and ERESUME instructions,
in a vDSO function and leverage fixup to intercept exceptions that would
otherwise generate a signal. This allows the vDSO wrapper to return the
fault information directly to its caller, obviating the need for SGX
applications and libraries to juggle signal handlers.
Attempt to fixup vDSO exceptions immediately prior to populating and
sending signal information. Except for the delivery mechanism, an
exception in a vDSO function should be treated like any other exception
in userspace, e.g. any fault that is successfully handled by the kernel
should not be directly visible to userspace.
Suggested-by: Andy Lutomirski <[email protected]>
Cc: Andy Lutomirski <[email protected]>
Cc: Jarkko Sakkinen <[email protected]>
Cc: Dave Hansen <[email protected]>
Cc: Josh Triplett <[email protected]>
Signed-off-by: Sean Christopherson <[email protected]>
---
arch/x86/mm/fault.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
index 5babd515f616..13be0c67379e 100644
--- a/arch/x86/mm/fault.c
+++ b/arch/x86/mm/fault.c
@@ -28,6 +28,7 @@
#include <asm/mmu_context.h> /* vma_pkey() */
#include <asm/efi.h> /* efi_recover_from_page_fault()*/
#include <asm/desc.h> /* store_idt(), ... */
+#include <asm/vdso.h> /* fixup_vdso_exception() */
#define CREATE_TRACE_POINTS
#include <asm/trace/exceptions.h>
@@ -936,6 +937,9 @@ __bad_area_nosemaphore(struct pt_regs *regs, unsigned long error_code,
sanitize_error_code(address, &error_code);
+ if (fixup_vdso_exception(regs, X86_TRAP_PF, error_code, address))
+ return;
+
if (likely(show_unhandled_signals))
show_signal_msg(regs, error_code, address, tsk);
@@ -1055,6 +1059,9 @@ do_sigbus(struct pt_regs *regs, unsigned long error_code, unsigned long address,
sanitize_error_code(address, &error_code);
+ if (fixup_vdso_exception(regs, X86_TRAP_PF, error_code, address))
+ return;
+
set_signal_archinfo(address, error_code);
#ifdef CONFIG_MEMORY_FAILURE
--
2.19.1
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.
This commit adds the Linux SGX Enclave Driver that provides an ioctl API
to manage enclaves. The address range for an enclave, commonly referred
as ELRANGE in the documentation (e.g. Intel SDM), is reserved with
mmap() against /dev/sgx. After that a set ioctls is used to build
the enclave to the ELRANGE.
Signed-off-by: Jarkko Sakkinen <[email protected]>
Co-developed-by: Sean Christopherson <[email protected]>
Signed-off-by: Sean Christopherson <[email protected]>
Co-developed-by: Serge Ayoun <[email protected]>
Signed-off-by: Serge Ayoun <[email protected]>
Co-developed-by: Shay Katz-zamir <[email protected]>
Signed-off-by: Shay Katz-zamir <[email protected]>
Co-developed-by: Suresh Siddha <[email protected]>
Signed-off-by: Suresh Siddha <[email protected]>
---
Documentation/ioctl/ioctl-number.txt | 1 +
arch/x86/Kconfig | 17 +-
arch/x86/include/uapi/asm/sgx.h | 59 ++
arch/x86/kernel/cpu/sgx/Makefile | 5 +-
arch/x86/kernel/cpu/sgx/driver/Makefile | 3 +
arch/x86/kernel/cpu/sgx/driver/driver.h | 38 ++
arch/x86/kernel/cpu/sgx/driver/ioctl.c | 795 ++++++++++++++++++++++++
arch/x86/kernel/cpu/sgx/driver/main.c | 290 +++++++++
arch/x86/kernel/cpu/sgx/encl.c | 358 +++++++++++
arch/x86/kernel/cpu/sgx/encl.h | 88 +++
arch/x86/kernel/cpu/sgx/encls.c | 1 +
arch/x86/kernel/cpu/sgx/main.c | 3 +
arch/x86/kernel/cpu/sgx/sgx.h | 1 +
13 files changed, 1657 insertions(+), 2 deletions(-)
create mode 100644 arch/x86/include/uapi/asm/sgx.h
create mode 100644 arch/x86/kernel/cpu/sgx/driver/Makefile
create mode 100644 arch/x86/kernel/cpu/sgx/driver/driver.h
create mode 100644 arch/x86/kernel/cpu/sgx/driver/ioctl.c
create mode 100644 arch/x86/kernel/cpu/sgx/driver/main.c
create mode 100644 arch/x86/kernel/cpu/sgx/encl.c
create mode 100644 arch/x86/kernel/cpu/sgx/encl.h
diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index c9558146ac58..ef2694221cd0 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -312,6 +312,7 @@ Code Seq#(hex) Include File Comments
<mailto:[email protected]>
0xA3 90-9F linux/dtlk.h
0xA4 00-1F uapi/linux/tee.h Generic TEE subsystem
+0xA4 00-02 uapi/asm/sgx.h conflict!
0xAA 00-3F linux/uapi/linux/userfaultfd.h
0xAB 00-1F linux/nbd.h
0xAC 00-1F linux/raw.h
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index dc630208003f..34257b5659cc 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1925,7 +1925,6 @@ config INTEL_SGX
bool "Intel SGX core functionality"
depends on X86_64 && CPU_SUP_INTEL
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
@@ -1940,6 +1939,22 @@ config INTEL_SGX
If unsure, say N.
+config INTEL_SGX_DRIVER
+ tristate "Intel(R) SGX Driver"
+ default n
+ depends on X86_64 && CPU_SUP_INTEL && INTEL_SGX
+ select MMU_NOTIFIER
+ select CRYPTO
+ select CRYPTO_SHA256
+ help
+ This options enables the kernel SGX driver that allows to construct
+ enclaves to the process memory by using a device node (by default
+ /dev/sgx) and a set of ioctls. The driver requires that the MSRs
+ specifying the public key hash for the launch enclave are writable so
+ that Linux has the full control to run enclaves.
+
+ For details, see Documentation/x86/intel_sgx.rst
+
config EFI
bool "EFI runtime service support"
depends on ACPI
diff --git a/arch/x86/include/uapi/asm/sgx.h b/arch/x86/include/uapi/asm/sgx.h
new file mode 100644
index 000000000000..aadf9c76e360
--- /dev/null
+++ b/arch/x86/include/uapi/asm/sgx.h
@@ -0,0 +1,59 @@
+/* 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/arch/x86/kernel/cpu/sgx/Makefile b/arch/x86/kernel/cpu/sgx/Makefile
index 20ce33655ff4..48174e5fc181 100644
--- a/arch/x86/kernel/cpu/sgx/Makefile
+++ b/arch/x86/kernel/cpu/sgx/Makefile
@@ -1 +1,4 @@
-obj-y += main.o encls.o
+obj-y += encl.o
+obj-y += encls.o
+obj-y += main.o
+obj-$(CONFIG_INTEL_SGX_DRIVER) += driver/
diff --git a/arch/x86/kernel/cpu/sgx/driver/Makefile b/arch/x86/kernel/cpu/sgx/driver/Makefile
new file mode 100644
index 000000000000..01ebbbb06a47
--- /dev/null
+++ b/arch/x86/kernel/cpu/sgx/driver/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_INTEL_SGX_DRIVER) += sgx.o
+sgx-$(CONFIG_INTEL_SGX_DRIVER) += ioctl.o
+sgx-$(CONFIG_INTEL_SGX_DRIVER) += main.o
diff --git a/arch/x86/kernel/cpu/sgx/driver/driver.h b/arch/x86/kernel/cpu/sgx/driver/driver.h
new file mode 100644
index 000000000000..b736411b5317
--- /dev/null
+++ b/arch/x86/kernel/cpu/sgx/driver/driver.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/**
+ * Copyright(c) 2016-19 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/radix-tree.h>
+#include <linux/rwsem.h>
+#include <linux/sched.h>
+#include <linux/workqueue.h>
+#include <uapi/asm/sgx.h>
+#include "../arch.h"
+#include "../encl.h"
+#include "../encls.h"
+#include "../sgx.h"
+
+#define SGX_DRV_NR_DEVICES 1
+#define SGX_EINIT_SPIN_COUNT 20
+#define SGX_EINIT_SLEEP_COUNT 50
+#define SGX_EINIT_SLEEP_TIME 20
+
+extern struct workqueue_struct *sgx_encl_wq;
+extern u64 sgx_encl_size_max_32;
+extern u64 sgx_encl_size_max_64;
+extern u32 sgx_misc_reserved_mask;
+extern u64 sgx_attributes_reserved_mask;
+extern u64 sgx_xfrm_reserved_mask;
+extern u32 sgx_xsave_size_tbl[64];
+
+extern const struct file_operations sgx_fs_provision_fops;
+
+long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
+
+#endif /* __ARCH_X86_INTEL_SGX_H__ */
diff --git a/arch/x86/kernel/cpu/sgx/driver/ioctl.c b/arch/x86/kernel/cpu/sgx/driver/ioctl.c
new file mode 100644
index 000000000000..4b9a91b53b50
--- /dev/null
+++ b/arch/x86/kernel/cpu/sgx/driver/ioctl.c
@@ -0,0 +1,795 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright(c) 2016-19 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 "driver.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;
+};
+
+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;
+}
+
+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;
+ unsigned long page_index = sgx_encl_get_index(encl, encl_page);
+ struct sgx_secinfo secinfo;
+ struct sgx_pageinfo pginfo;
+ 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);
+
+ backing = sgx_encl_get_backing_page(encl, page_index);
+ if (IS_ERR(backing))
+ 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);
+
+ put_page(backing);
+
+ if (ret) {
+ if (encls_failed(ret))
+ ENCLS_WARN(ret, "EADD");
+ 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) {
+ if (encls_failed(ret))
+ ENCLS_WARN(ret, "EEXTEND");
+ return false;
+ }
+ }
+
+ encl_page->encl = encl;
+ encl_page->epc_page = epc_page;
+ encl->secs_child_cnt++;
+
+ 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, 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();
+
+ mutex_lock(&encl->lock);
+
+ if (IS_ERR(epc_page)) {
+ sgx_encl_destroy(encl);
+ skip_rest = true;
+ } else if (!sgx_process_add_page_req(req, epc_page)) {
+ sgx_free_page(epc_page);
+ sgx_encl_destroy(encl);
+ skip_rest = true;
+ }
+
+ mutex_unlock(&encl->lock);
+
+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->miscselect & sgx_misc_reserved_mask ||
+ secs->attributes & sgx_attributes_reserved_mask ||
+ secs->xfrm & sgx_xfrm_reserved_mask)
+ return -EINVAL;
+
+ if (secs->attributes & SGX_ATTR_MODE64BIT) {
+ if (secs->size > sgx_encl_size_max_64)
+ return -EINVAL;
+ } else 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)))
+ 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 struct sgx_encl *sgx_encl_alloc(struct sgx_secs *secs)
+{
+ unsigned long encl_size = secs->size + PAGE_SIZE;
+ unsigned long ssaframesize;
+ struct sgx_encl_mm *mm;
+ 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("SGX backing", encl_size + (encl_size >> 5),
+ VM_NORESERVE);
+ if (IS_ERR(backing))
+ return ERR_CAST(backing);
+
+ encl = kzalloc(sizeof(*encl), GFP_KERNEL);
+ if (!encl) {
+ fput(backing);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ mm = kzalloc(sizeof(*mm), GFP_KERNEL);
+ if (!mm) {
+ kfree(encl);
+ fput(backing);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ encl->secs_attributes = secs->attributes;
+ encl->allowed_attributes = SGX_ATTR_ALLOWED_MASK;
+ 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->work, sgx_add_page_worker);
+ INIT_LIST_HEAD(&encl->mm_list);
+ list_add(&mm->list, &encl->mm_list);
+ kref_init(&mm->refcount);
+ mm->mm = current->mm;
+ mm->encl = encl;
+ spin_lock_init(&encl->mm_lock);
+ encl->base = secs->base;
+ encl->size = secs->size;
+ encl->ssaframesize = secs->ssa_frame_size;
+ encl->backing = backing;
+
+ return encl;
+}
+
+static struct sgx_encl_page *sgx_encl_page_alloc(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;
+}
+
+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_encl_destroy(encl);
+ encl->flags |= SGX_ENCL_SUSPEND;
+ mutex_unlock(&encl->lock);
+ flush_work(&encl->work);
+ return NOTIFY_DONE;
+}
+
+static 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();
+ if (IS_ERR(secs_epc)) {
+ ret = PTR_ERR(secs_epc);
+ return ret;
+ }
+
+ encl->secs.encl = encl;
+ encl->secs.epc_page = secs_epc;
+
+ pginfo.addr = 0;
+ pginfo.contents = (unsigned long)secs;
+ pginfo.metadata = (unsigned long)&secinfo;
+ pginfo.secs = 0;
+ memset(&secinfo, 0, sizeof(secinfo));
+ ret = __ecreate((void *)&pginfo, sgx_epc_addr(secs_epc));
+
+ if (ret) {
+ pr_debug("ECREATE returned %ld\n", ret);
+ return ret;
+ }
+
+ if (secs->attributes & SGX_ATTR_DEBUG)
+ encl->flags |= SGX_ENCL_DEBUG;
+
+ 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(¤t->mm->mmap_sem);
+ ret = sgx_encl_find(current->mm, secs->base, &vma);
+ if (ret != -ENOENT) {
+ if (!ret)
+ ret = -EINVAL;
+ up_read(¤t->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(¤t->mm->mmap_sem);
+ return ret;
+ }
+
+ vma->vm_private_data = encl;
+ up_read(¤t->mm->mmap_sem);
+ return 0;
+}
+
+/**
+ * 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);
+ if (copy_from_user(secs, (void __user *)createp->src, sizeof(*secs))) {
+ ret = -EFAULT;
+ 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;
+}
+
+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_TRIM &&
+ 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)
+{
+ unsigned long page_index = sgx_encl_get_index(encl, encl_page);
+ u64 page_type = secinfo->flags & SGX_SECINFO_PAGE_TYPE_MASK;
+ struct sgx_add_page_req *req = NULL;
+ struct page *backing;
+ void *backing_ptr;
+ int empty;
+
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ backing = sgx_encl_get_backing_page(encl, page_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_encl_wq, &encl->work);
+ set_page_dirty(backing);
+ put_page(backing);
+ return 0;
+}
+
+static 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)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ encl_page = sgx_encl_page_alloc(encl, addr);
+ if (IS_ERR(encl_page)) {
+ ret = PTR_ERR(encl_page);
+ goto out;
+ }
+
+ ret = __sgx_encl_add_page(encl, encl_page, data, secinfo, mrmask);
+ if (ret) {
+ radix_tree_delete(&encl_page->encl->page_tree,
+ PFN_DOWN(encl_page->desc));
+ kfree(encl_page);
+ }
+
+out:
+ mutex_unlock(&encl->lock);
+ 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);
+
+ if (copy_from_user((void *)data, (void __user *)addp->src, PAGE_SIZE)) {
+ ret = -EFAULT;
+ 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;
+}
+
+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;
+}
+
+static 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;
+
+ /* Check that the required attributes have been authorized. */
+ if (encl->secs_attributes & ~encl->allowed_attributes)
+ return -EINVAL;
+
+ ret = sgx_get_key_hash(sigstruct->modulus, mrsigner);
+ if (ret)
+ return ret;
+
+ flush_work(&encl->work);
+
+ mutex_lock(&encl->lock);
+
+ if (encl->flags & SGX_ENCL_INITIALIZED)
+ goto err_out;
+
+ if (encl->flags & SGX_ENCL_DEAD) {
+ ret = -EFAULT;
+ goto err_out;
+ }
+
+ 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)) {
+ ret = -ERESTARTSYS;
+ goto err_out;
+ }
+ }
+
+ if (encls_faulted(ret)) {
+ if (encls_failed(ret))
+ ENCLS_WARN(ret, "EINIT");
+
+ sgx_encl_destroy(encl);
+ ret = -EFAULT;
+ } else if (encls_returned_code(ret)) {
+ pr_debug("EINIT returned %d\n", ret);
+ } else {
+ encl->flags |= SGX_ENCL_INITIALIZED;
+ }
+
+err_out:
+ mutex_unlock(&encl->lock);
+ 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.
+ *
+ * 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));
+
+ if (copy_from_user(sigstruct, (void __user *)initp->sigstruct,
+ sizeof(*sigstruct))) {
+ ret = -EFAULT;
+ 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;
+ }
+
+ return ret;
+}
diff --git a/arch/x86/kernel/cpu/sgx/driver/main.c b/arch/x86/kernel/cpu/sgx/driver/main.c
new file mode 100644
index 000000000000..16f36cd0af04
--- /dev/null
+++ b/arch/x86/kernel/cpu/sgx/driver/main.c
@@ -0,0 +1,290 @@
+// 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/mman.h>
+#include <linux/platform_device.h>
+#include <linux/security.h>
+#include <linux/suspend.h>
+#include <asm/traps.h>
+#include "driver.h"
+
+MODULE_DESCRIPTION("Intel SGX Enclave Driver");
+MODULE_AUTHOR("Jarkko Sakkinen <[email protected]>");
+MODULE_LICENSE("Dual BSD/GPL");
+
+struct workqueue_struct *sgx_encl_wq;
+u64 sgx_encl_size_max_32;
+u64 sgx_encl_size_max_64;
+u32 sgx_misc_reserved_mask;
+u64 sgx_attributes_reserved_mask;
+u64 sgx_xfrm_reserved_mask = ~0x3;
+u32 sgx_xsave_size_tbl[64];
+
+#ifdef CONFIG_COMPAT
+static long sgx_compat_ioctl(struct file *filep, unsigned int cmd,
+ unsigned long arg)
+{
+ return sgx_ioctl(filep, cmd, arg);
+}
+#endif
+
+static 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;
+
+ 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) || flags & MAP_PRIVATE)
+ 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_ctrl_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",
+};
+
+static char *sgx_devnode(struct device *dev, umode_t *mode,
+ kuid_t *uid, kgid_t *gid)
+{
+ if (mode)
+ *mode = 0666;
+ return kasprintf(GFP_KERNEL, "sgx");
+}
+
+static const struct device_type sgx_device_type = {
+ .name = "sgx",
+ .devnode = sgx_devnode,
+};
+
+struct sgx_dev_ctx {
+ struct device ctrl_dev;
+ struct cdev ctrl_cdev;
+};
+
+static dev_t sgx_devt;
+
+static void sgx_dev_release(struct device *dev)
+{
+ struct sgx_dev_ctx *ctx = container_of(dev, struct sgx_dev_ctx,
+ ctrl_dev);
+
+ kfree(ctx);
+}
+
+static struct sgx_dev_ctx *sgx_dev_ctx_alloc(struct device *parent)
+{
+ struct sgx_dev_ctx *ctx;
+ int ret;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return ERR_PTR(-ENOMEM);
+
+ device_initialize(&ctx->ctrl_dev);
+
+ ctx->ctrl_dev.bus = &sgx_bus_type;
+ ctx->ctrl_dev.type = &sgx_device_type;
+ ctx->ctrl_dev.parent = parent;
+ ctx->ctrl_dev.devt = MKDEV(MAJOR(sgx_devt), 0);
+ ctx->ctrl_dev.release = sgx_dev_release;
+
+ ret = dev_set_name(&ctx->ctrl_dev, "sgx");
+ if (ret) {
+ put_device(&ctx->ctrl_dev);
+ return ERR_PTR(ret);
+ }
+
+ cdev_init(&ctx->ctrl_cdev, &sgx_ctrl_fops);
+ ctx->ctrl_cdev.owner = THIS_MODULE;
+
+ dev_set_drvdata(parent, ctx);
+
+ return ctx;
+}
+
+static struct sgx_dev_ctx *sgxm_dev_ctx_alloc(struct device *parent)
+{
+ struct sgx_dev_ctx *ctx;
+ int rc;
+
+ ctx = sgx_dev_ctx_alloc(parent);
+ if (IS_ERR(ctx))
+ return ctx;
+
+ rc = devm_add_action_or_reset(parent, (void (*)(void *))put_device,
+ &ctx->ctrl_dev);
+ if (rc)
+ return ERR_PTR(rc);
+
+ return ctx;
+}
+
+static int sgx_dev_init(struct device *parent)
+{
+ struct sgx_dev_ctx *sgx_dev;
+ unsigned int eax;
+ unsigned int ebx;
+ unsigned int ecx;
+ unsigned int edx;
+ u64 attr_mask;
+ u64 xfrm_mask;
+ int ret;
+ int i;
+
+ cpuid_count(SGX_CPUID, 0, &eax, &ebx, &ecx, &edx);
+ sgx_misc_reserved_mask = ~ebx | SGX_MISC_RESERVED_MASK;
+ sgx_encl_size_max_64 = 1ULL << ((edx >> 8) & 0xFF);
+ sgx_encl_size_max_32 = 1ULL << (edx & 0xFF);
+
+ cpuid_count(SGX_CPUID, 1, &eax, &ebx, &ecx, &edx);
+
+ attr_mask = (((u64)ebx) << 32) + (u64)eax;
+ sgx_attributes_reserved_mask = ~attr_mask | SGX_ATTR_RESERVED_MASK;
+
+ if (boot_cpu_has(X86_FEATURE_OSXSAVE)) {
+ xfrm_mask = (((u64)edx) << 32) + (u64)ecx;
+
+ for (i = 2; i < 64; i++) {
+ cpuid_count(0x0D, i, &eax, &ebx, &ecx, &edx);
+ if ((1 << i) & xfrm_mask)
+ sgx_xsave_size_tbl[i] = eax + ebx;
+ }
+
+ sgx_xfrm_reserved_mask = ~xfrm_mask;
+ }
+
+ sgx_dev = sgxm_dev_ctx_alloc(parent);
+ if (IS_ERR(sgx_dev))
+ return PTR_ERR(sgx_dev);
+
+ sgx_encl_wq = alloc_workqueue("sgx-encl-wq",
+ WQ_UNBOUND | WQ_FREEZABLE, 1);
+ if (!sgx_encl_wq)
+ return -ENOMEM;
+
+ ret = cdev_device_add(&sgx_dev->ctrl_cdev, &sgx_dev->ctrl_dev);
+ if (ret)
+ goto err_device_add;
+
+ return 0;
+
+err_device_add:
+ destroy_workqueue(sgx_encl_wq);
+ return ret;
+}
+
+static int sgx_drv_probe(struct platform_device *pdev)
+{
+ if (!sgx_enabled) {
+ pr_info("sgx: SGX is not enabled in the core\n");
+ return -ENODEV;
+ }
+
+ if (!boot_cpu_has(X86_FEATURE_SGX_LC)) {
+ pr_info("sgx: The public key MSRs are not writable\n");
+ return -ENODEV;
+ }
+
+ return sgx_dev_init(&pdev->dev);
+}
+
+static int sgx_drv_remove(struct platform_device *pdev)
+{
+ struct sgx_dev_ctx *ctx = dev_get_drvdata(&pdev->dev);
+
+ cdev_device_del(&ctx->ctrl_cdev, &ctx->ctrl_dev);
+ destroy_workqueue(sgx_encl_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 = "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, SGX_DRV_NR_DEVICES, "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, SGX_DRV_NR_DEVICES);
+}
+
+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/arch/x86/kernel/cpu/sgx/encl.c b/arch/x86/kernel/cpu/sgx/encl.c
new file mode 100644
index 000000000000..bd8bcd748976
--- /dev/null
+++ b/arch/x86/kernel/cpu/sgx/encl.c
@@ -0,0 +1,358 @@
+// 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 <linux/suspend.h>
+#include <linux/sched/mm.h>
+#include "arch.h"
+#include "encl.h"
+#include "sgx.h"
+
+static struct sgx_encl_page *sgx_encl_load_page(struct sgx_encl *encl,
+ unsigned long addr)
+{
+ 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);
+
+ 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->epc_page)
+ return entry;
+
+ return ERR_PTR(-EFAULT);
+}
+
+static struct sgx_encl_mm *sgx_encl_get_mm(struct sgx_encl *encl,
+ struct mm_struct *mm)
+{
+ struct sgx_encl_mm *next_mm = NULL;
+ struct sgx_encl_mm *prev_mm = NULL;
+ int iter;
+
+ while (true) {
+ next_mm = sgx_encl_next_mm(encl, prev_mm, &iter);
+ if (prev_mm) {
+ mmdrop(prev_mm->mm);
+ kref_put(&prev_mm->refcount, sgx_encl_release_mm);
+ }
+ prev_mm = next_mm;
+
+ if (iter == SGX_ENCL_MM_ITER_DONE)
+ break;
+
+ if (iter == SGX_ENCL_MM_ITER_RESTART)
+ continue;
+
+ if (mm == next_mm->mm)
+ return next_mm;
+ }
+
+ return NULL;
+}
+
+static void sgx_vma_open(struct vm_area_struct *vma)
+{
+ struct sgx_encl *encl = vma->vm_private_data;
+ struct sgx_encl_mm *mm;
+
+ if (!encl)
+ return;
+
+ if (encl->flags & SGX_ENCL_DEAD)
+ goto out;
+
+ mm = sgx_encl_get_mm(encl, vma->vm_mm);
+ if (!mm) {
+ mm = kzalloc(sizeof(*mm), GFP_KERNEL);
+ if (!mm) {
+ encl->flags |= SGX_ENCL_DEAD;
+ goto out;
+ }
+
+ spin_lock(&encl->mm_lock);
+ mm->encl = encl;
+ mm->mm = vma->vm_mm;
+ list_add(&mm->list, &encl->mm_list);
+ kref_init(&mm->refcount);
+ spin_unlock(&encl->mm_lock);
+ } else {
+ mmdrop(mm->mm);
+ }
+
+out:
+ kref_get(&encl->refcount);
+}
+
+static void sgx_vma_close(struct vm_area_struct *vma)
+{
+ struct sgx_encl *encl = vma->vm_private_data;
+ struct sgx_encl_mm *mm;
+
+ if (!encl)
+ return;
+
+ mm = sgx_encl_get_mm(encl, vma->vm_mm);
+ if (mm) {
+ mmdrop(mm->mm);
+ kref_put(&mm->refcount, sgx_encl_release_mm);
+
+ /* Release kref for the VMA. */
+ kref_put(&mm->refcount, sgx_encl_release_mm);
+ }
+
+ kref_put(&encl->refcount, sgx_encl_release);
+}
+
+static unsigned 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 *encl = vma->vm_private_data;
+ struct sgx_encl_page *entry;
+ int ret = VM_FAULT_NOPAGE;
+ struct sgx_encl_mm *mm;
+ unsigned long pfn;
+
+ mm = sgx_encl_get_mm(encl, vma->vm_mm);
+ if (!mm)
+ return VM_FAULT_SIGBUS;
+
+ mmdrop(mm->mm);
+ kref_put(&mm->refcount, sgx_encl_release_mm);
+
+ mutex_lock(&encl->lock);
+
+ entry = sgx_encl_load_page(encl, addr);
+ if (IS_ERR(entry)) {
+ if (unlikely(PTR_ERR(entry) != -EBUSY))
+ ret = VM_FAULT_SIGBUS;
+
+ goto out;
+ }
+
+ if (!follow_pfn(vma, addr, &pfn))
+ goto out;
+
+ ret = vmf_insert_pfn(vma, addr, PFN_DOWN(entry->epc_page->desc));
+ if (ret != VM_FAULT_NOPAGE) {
+ ret = VM_FAULT_SIGBUS;
+ goto out;
+ }
+
+out:
+ mutex_unlock(&encl->lock);
+ return ret;
+}
+
+const struct vm_operations_struct sgx_vm_ops = {
+ .close = sgx_vma_close,
+ .open = sgx_vma_open,
+ .fault = sgx_vma_fault,
+};
+EXPORT_SYMBOL_GPL(sgx_vm_ops);
+
+/**
+ * sgx_encl_find - find an enclave
+ * @mm: mm struct of the current process
+ * @addr: address in the ELRANGE
+ * @vma: the resulting VMA
+ *
+ * Find an enclave identified by the given address. Give back a VMA that is
+ * part of the enclave and 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;
+}
+EXPORT_SYMBOL_GPL(sgx_encl_find);
+
+/**
+ * sgx_encl_destroy() - destroy enclave resources
+ * @encl: an &sgx_encl instance
+ */
+void sgx_encl_destroy(struct sgx_encl *encl)
+{
+ struct sgx_encl_page *entry;
+ struct radix_tree_iter iter;
+ void **slot;
+
+ encl->flags |= SGX_ENCL_DEAD;
+
+ radix_tree_for_each_slot(slot, &encl->page_tree, &iter, 0) {
+ entry = *slot;
+ if (entry->epc_page) {
+ if (!__sgx_free_page(entry->epc_page)) {
+ encl->secs_child_cnt--;
+ entry->epc_page = NULL;
+
+ }
+
+ radix_tree_delete(&entry->encl->page_tree,
+ PFN_DOWN(entry->desc));
+ }
+ }
+
+ if (!encl->secs_child_cnt && encl->secs.epc_page) {
+ sgx_free_page(encl->secs.epc_page);
+ encl->secs.epc_page = NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(sgx_encl_destroy);
+
+/**
+ * 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_mm *mm;
+
+ if (encl->pm_notifier.notifier_call)
+ unregister_pm_notifier(&encl->pm_notifier);
+
+ sgx_encl_destroy(encl);
+
+ if (encl->backing)
+ fput(encl->backing);
+
+ /* If sgx_encl_create() fails, can be non-empty */
+ while (!list_empty(&encl->mm_list)) {
+ mm = list_first_entry(&encl->mm_list, struct sgx_encl_mm, list);
+ list_del(&mm->list);
+ kfree(mm);
+ }
+
+ kfree(encl);
+}
+EXPORT_SYMBOL_GPL(sgx_encl_release);
+
+/**
+ * sgx_encl_get_index() - Convert a page descriptor to a page index
+ * @encl: an enclave
+ * @page: an enclave page
+ *
+ * Given an enclave page descriptor, convert it to a page index used to access
+ * backing storage. The backing page for SECS is located after the enclave
+ * pages.
+ */
+pgoff_t sgx_encl_get_index(struct sgx_encl *encl, struct sgx_encl_page *page)
+{
+ if (!PFN_DOWN(page->desc))
+ return PFN_DOWN(encl->size);
+
+ return PFN_DOWN(page->desc - encl->base);
+}
+EXPORT_SYMBOL_GPL(sgx_encl_get_index);
+
+/**
+ * sgx_encl_encl_get_backing_page() - Pin the backing page
+ * @encl: an enclave
+ * @index: page index
+ *
+ * Return: the pinned backing page
+ */
+struct page *sgx_encl_get_backing_page(struct sgx_encl *encl, pgoff_t index)
+{
+ struct inode *inode = encl->backing->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);
+}
+EXPORT_SYMBOL_GPL(sgx_encl_get_backing_page);
+
+/**
+ * sgx_encl_next_mm() - Iterate to the next mm
+ * @encl: an enclave
+ * @mm: an mm list entry
+ * @iter: iterator status
+ *
+ * Return: the enclave mm or NULL
+ */
+struct sgx_encl_mm *sgx_encl_next_mm(struct sgx_encl *encl,
+ struct sgx_encl_mm *mm, int *iter)
+{
+ struct list_head *entry;
+
+ WARN(!encl, "%s: encl is NULL", __func__);
+ WARN(!iter, "%s: iter is NULL", __func__);
+
+ spin_lock(&encl->mm_lock);
+
+ entry = mm ? mm->list.next : encl->mm_list.next;
+ WARN(!entry, "%s: entry is NULL", __func__);
+
+ if (entry == &encl->mm_list) {
+ mm = NULL;
+ *iter = SGX_ENCL_MM_ITER_DONE;
+ goto out;
+ }
+
+ mm = list_entry(entry, struct sgx_encl_mm, list);
+
+ if (!kref_get_unless_zero(&mm->refcount)) {
+ *iter = SGX_ENCL_MM_ITER_RESTART;
+ mm = NULL;
+ goto out;
+ }
+
+ if (!atomic_add_unless(&mm->mm->mm_count, 1, 0)) {
+ kref_put(&mm->refcount, sgx_encl_release_mm);
+ mm = NULL;
+ *iter = SGX_ENCL_MM_ITER_RESTART;
+ goto out;
+ }
+
+ *iter = SGX_ENCL_MM_ITER_NEXT;
+
+out:
+ spin_unlock(&encl->mm_lock);
+ return mm;
+}
+
+void sgx_encl_release_mm(struct kref *ref)
+{
+ struct sgx_encl_mm *mm =
+ container_of(ref, struct sgx_encl_mm, refcount);
+
+ spin_lock(&mm->encl->mm_lock);
+ list_del(&mm->list);
+ spin_unlock(&mm->encl->mm_lock);
+
+ kfree(mm);
+}
diff --git a/arch/x86/kernel/cpu/sgx/encl.h b/arch/x86/kernel/cpu/sgx/encl.h
new file mode 100644
index 000000000000..374ad3396684
--- /dev/null
+++ b/arch/x86/kernel/cpu/sgx/encl.h
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/**
+ * Copyright(c) 2016-19 Intel Corporation.
+ */
+#ifndef _X86_ENCL_H
+#define _X86_ENCL_H
+
+#include <linux/mmu_notifier.h>
+
+/**
+ * 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_ADDR_MASK: Holds the virtual address of the page.
+ *
+ * The page address for SECS is zero and is used by the subsystem to recognize
+ * the SECS page.
+ */
+enum sgx_encl_page_desc {
+ SGX_ENCL_PAGE_TCS = BIT(0),
+ /* 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;
+ struct sgx_epc_page *epc_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_mm {
+ struct sgx_encl *encl;
+ struct mm_struct *mm;
+ struct kref refcount;
+ struct list_head list;
+};
+
+struct sgx_encl {
+ unsigned int flags;
+ u64 secs_attributes;
+ u64 allowed_attributes;
+ unsigned int page_cnt;
+ unsigned int secs_child_cnt;
+ struct mutex lock;
+ struct list_head mm_list;
+ spinlock_t mm_lock;
+ 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 work;
+ struct sgx_encl_page secs;
+ struct notifier_block pm_notifier;
+};
+
+extern const struct vm_operations_struct sgx_vm_ops;
+
+enum sgx_encl_mm_iter {
+ SGX_ENCL_MM_ITER_DONE = 0,
+ SGX_ENCL_MM_ITER_NEXT = 1,
+ SGX_ENCL_MM_ITER_RESTART = 2,
+};
+
+int sgx_encl_find(struct mm_struct *mm, unsigned long addr,
+ struct vm_area_struct **vma);
+void sgx_encl_destroy(struct sgx_encl *encl);
+void sgx_encl_release(struct kref *ref);
+pgoff_t sgx_encl_get_index(struct sgx_encl *encl, struct sgx_encl_page *page);
+struct page *sgx_encl_get_backing_page(struct sgx_encl *encl, pgoff_t index);
+struct sgx_encl_mm *sgx_encl_next_mm(struct sgx_encl *encl,
+ struct sgx_encl_mm *mm, int *iter);
+void sgx_encl_release_mm(struct kref *ref);
+
+#endif /* _X86_ENCL_H */
diff --git a/arch/x86/kernel/cpu/sgx/encls.c b/arch/x86/kernel/cpu/sgx/encls.c
index 5045f1365e07..698cc526bfbf 100644
--- a/arch/x86/kernel/cpu/sgx/encls.c
+++ b/arch/x86/kernel/cpu/sgx/encls.c
@@ -19,3 +19,4 @@ bool encls_failed(int ret)
return encls_faulted(ret) && ENCLS_TRAPNR(ret) != epcm_trapnr;
}
+EXPORT_SYMBOL_GPL(encls_failed);
diff --git a/arch/x86/kernel/cpu/sgx/main.c b/arch/x86/kernel/cpu/sgx/main.c
index e966f96837c7..d88dc3d1d4a7 100644
--- a/arch/x86/kernel/cpu/sgx/main.c
+++ b/arch/x86/kernel/cpu/sgx/main.c
@@ -14,6 +14,8 @@
struct sgx_epc_section sgx_epc_sections[SGX_MAX_EPC_SECTIONS];
EXPORT_SYMBOL_GPL(sgx_epc_sections);
+bool sgx_enabled;
+EXPORT_SYMBOL_GPL(sgx_enabled);
static int sgx_nr_epc_sections;
@@ -283,6 +285,7 @@ static __init int sgx_init(void)
if (ret)
return ret;
+ sgx_enabled = true;
return 0;
}
diff --git a/arch/x86/kernel/cpu/sgx/sgx.h b/arch/x86/kernel/cpu/sgx/sgx.h
index f90d02fbbbbd..2337b63ba487 100644
--- a/arch/x86/kernel/cpu/sgx/sgx.h
+++ b/arch/x86/kernel/cpu/sgx/sgx.h
@@ -34,6 +34,7 @@ struct sgx_epc_section {
#define SGX_MAX_EPC_SECTIONS 8
extern struct sgx_epc_section sgx_epc_sections[SGX_MAX_EPC_SECTIONS];
+extern bool sgx_enabled;
/**
* enum sgx_epc_page_desc - bits and masks for an EPC page's descriptor
--
2.19.1
From: Sean Christopherson <[email protected]>
...to prepare for vDSO exception fixup, which will expose the error code
to userspace and runs before set_signal_archinfo(), i.e. suppresses the
signal when fixup is successful.
Signed-off-by: Sean Christopherson <[email protected]>
---
arch/x86/mm/fault.c | 24 +++++++++++++++++-------
1 file changed, 17 insertions(+), 7 deletions(-)
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
index 78e2807fbede..5babd515f616 100644
--- a/arch/x86/mm/fault.c
+++ b/arch/x86/mm/fault.c
@@ -719,6 +719,18 @@ pgtable_bad(struct pt_regs *regs, unsigned long error_code,
oops_end(flags, regs, sig);
}
+static void sanitize_error_code(unsigned long address,
+ unsigned long *error_code)
+{
+ /*
+ * To avoid leaking information about the kernel page
+ * table layout, pretend that user-mode accesses to
+ * kernel addresses are always protection faults.
+ */
+ if (address >= TASK_SIZE_MAX)
+ *error_code |= X86_PF_PROT;
+}
+
static void set_signal_archinfo(unsigned long address,
unsigned long error_code)
{
@@ -771,6 +783,8 @@ no_context(struct pt_regs *regs, unsigned long error_code,
* faulting through the emulate_vsyscall() logic.
*/
if (current->thread.sig_on_uaccess_err && signal) {
+ sanitize_error_code(address, &error_code);
+
set_signal_archinfo(address, error_code);
/* XXX: hwpoison faults will set the wrong code. */
@@ -920,13 +934,7 @@ __bad_area_nosemaphore(struct pt_regs *regs, unsigned long error_code,
if (is_errata100(regs, address))
return;
- /*
- * To avoid leaking information about the kernel page table
- * layout, pretend that user-mode accesses to kernel addresses
- * are always protection faults.
- */
- if (address >= TASK_SIZE_MAX)
- error_code |= X86_PF_PROT;
+ sanitize_error_code(address, &error_code);
if (likely(show_unhandled_signals))
show_signal_msg(regs, error_code, address, tsk);
@@ -1045,6 +1053,8 @@ do_sigbus(struct pt_regs *regs, unsigned long error_code, unsigned long address,
if (is_prefetch(regs, error_code, address))
return;
+ sanitize_error_code(address, &error_code);
+
set_signal_archinfo(address, error_code);
#ifdef CONFIG_MEMORY_FAILURE
--
2.19.1
Add a selftest for SGX. It is a trivial test where a simple enclave
copies one 64-bit word of memory between two memory locations given to
the enclave as arguments.
Signed-off-by: Jarkko Sakkinen <[email protected]>
---
tools/testing/selftests/x86/Makefile | 10 +
tools/testing/selftests/x86/sgx/Makefile | 48 ++
tools/testing/selftests/x86/sgx/defines.h | 39 ++
tools/testing/selftests/x86/sgx/encl.c | 20 +
tools/testing/selftests/x86/sgx/encl.lds | 33 ++
.../selftests/x86/sgx/encl_bootstrap.S | 94 ++++
tools/testing/selftests/x86/sgx/encl_piggy.S | 18 +
tools/testing/selftests/x86/sgx/encl_piggy.h | 14 +
tools/testing/selftests/x86/sgx/main.c | 280 ++++++++++
tools/testing/selftests/x86/sgx/sgx_call.S | 15 +
tools/testing/selftests/x86/sgx/sgxsign.c | 508 ++++++++++++++++++
.../testing/selftests/x86/sgx/signing_key.pem | 39 ++
12 files changed, 1118 insertions(+)
create mode 100644 tools/testing/selftests/x86/sgx/Makefile
create mode 100644 tools/testing/selftests/x86/sgx/defines.h
create mode 100644 tools/testing/selftests/x86/sgx/encl.c
create mode 100644 tools/testing/selftests/x86/sgx/encl.lds
create mode 100644 tools/testing/selftests/x86/sgx/encl_bootstrap.S
create mode 100644 tools/testing/selftests/x86/sgx/encl_piggy.S
create mode 100644 tools/testing/selftests/x86/sgx/encl_piggy.h
create mode 100644 tools/testing/selftests/x86/sgx/main.c
create mode 100644 tools/testing/selftests/x86/sgx/sgx_call.S
create mode 100644 tools/testing/selftests/x86/sgx/sgxsign.c
create mode 100644 tools/testing/selftests/x86/sgx/signing_key.pem
diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile
index 186520198de7..4fc9a42f56ea 100644
--- a/tools/testing/selftests/x86/Makefile
+++ b/tools/testing/selftests/x86/Makefile
@@ -1,4 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
+
+SUBDIRS_64 := sgx
+
all:
include ../lib.mk
@@ -67,6 +70,13 @@ all_32: $(BINARIES_32)
all_64: $(BINARIES_64)
+all_64: $(SUBDIRS_64)
+ @for DIR in $(SUBDIRS_64); do \
+ BUILD_TARGET=$(OUTPUT)/$$DIR; \
+ mkdir $$BUILD_TARGET -p; \
+ make OUTPUT=$$BUILD_TARGET -C $$DIR $@; \
+ done
+
EXTRA_CLEAN := $(BINARIES_32) $(BINARIES_64)
$(BINARIES_32): $(OUTPUT)/%_32: %.c
diff --git a/tools/testing/selftests/x86/sgx/Makefile b/tools/testing/selftests/x86/sgx/Makefile
new file mode 100644
index 000000000000..1fd6f2708e81
--- /dev/null
+++ b/tools/testing/selftests/x86/sgx/Makefile
@@ -0,0 +1,48 @@
+top_srcdir = ../../../../..
+
+include ../../lib.mk
+
+HOST_CFLAGS := -Wall -Werror -g $(INCLUDES) -fPIC
+ENCL_CFLAGS := -Wall -Werror -static -nostdlib -nostartfiles -fPIC \
+ -fno-stack-protector -mrdrnd $(INCLUDES)
+
+TEST_CUSTOM_PROGS := $(OUTPUT)/test_sgx
+all_64: $(TEST_CUSTOM_PROGS)
+
+$(TEST_CUSTOM_PROGS): $(OUTPUT)/main.o $(OUTPUT)/sgx_call.o \
+ $(OUTPUT)/encl_piggy.o
+ $(CC) $(HOST_CFLAGS) -o $@ $^
+
+$(OUTPUT)/main.o: main.c
+ $(CC) $(HOST_CFLAGS) -c $< -o $@
+
+$(OUTPUT)/sgx_call.o: sgx_call.S
+ $(CC) $(HOST_CFLAGS) -c $< -o $@
+
+$(OUTPUT)/encl_piggy.o: $(OUTPUT)/encl.bin $(OUTPUT)/encl.ss
+ $(CC) $(HOST_CFLAGS) -c encl_piggy.S -o $@
+
+$(OUTPUT)/encl.bin: $(OUTPUT)/encl.elf $(OUTPUT)/sgxsign
+ objcopy --remove-section=.got.plt -O binary $< $@
+
+$(OUTPUT)/encl.elf: $(OUTPUT)/encl.o $(OUTPUT)/encl_bootstrap.o
+ $(CC) $(ENCL_CFLAGS) -T encl.lds -o $@ $^
+
+$(OUTPUT)/encl.o: encl.c
+ $(CC) $(ENCL_CFLAGS) -c $< -o $@
+
+$(OUTPUT)/encl_bootstrap.o: encl_bootstrap.S
+ $(CC) $(ENCL_CFLAGS) -c $< -o $@
+
+$(OUTPUT)/encl.ss: $(OUTPUT)/encl.bin $(OUTPUT)/sgxsign
+ $(OUTPUT)/sgxsign signing_key.pem $(OUTPUT)/encl.bin $(OUTPUT)/encl.ss
+
+$(OUTPUT)/sgxsign: sgxsign.c
+ $(CC) -o $@ $< -lcrypto
+
+EXTRA_CLEAN := $(OUTPUT)/sgx-selftest $(OUTPUT)/sgx-selftest.o \
+ $(OUTPUT)/sgx_call.o $(OUTPUT)/encl.bin $(OUTPUT)/encl.ss \
+ $(OUTPUT)/encl.elf $(OUTPUT)/encl.o $(OUTPUT)/encl_bootstrap.o \
+ $(OUTPUT)/sgxsign
+
+.PHONY: clean
diff --git a/tools/testing/selftests/x86/sgx/defines.h b/tools/testing/selftests/x86/sgx/defines.h
new file mode 100644
index 000000000000..3ff73a9d9b93
--- /dev/null
+++ b/tools/testing/selftests/x86/sgx/defines.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright(c) 2016-19 Intel Corporation.
+ */
+
+#ifndef TYPES_H
+#define TYPES_H
+
+#include <stdint.h>
+
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+
+#define __aligned(x) __attribute__((__aligned__(x)))
+#define __packed __attribute__((packed))
+
+/* Derived from asm-generic/bitsperlong.h. */
+#if __x86_64__
+#define BITS_PER_LONG 64
+#else
+#define BITS_PER_LONG 32
+#endif
+#define BITS_PER_LONG_LONG 64
+
+/* Taken from linux/bits.h. */
+#define BIT(nr) (1UL << (nr))
+#define BIT_ULL(nr) (1ULL << (nr))
+#define GENMASK(h, l) \
+ (((~0UL) - (1UL << (l)) + 1) & (~0UL >> (BITS_PER_LONG - 1 - (h))))
+#define GENMASK_ULL(h, l) \
+ (((~0ULL) - (1ULL << (l)) + 1) & \
+ (~0ULL >> (BITS_PER_LONG_LONG - 1 - (h))))
+
+#include "../../../../../arch/x86/kernel/cpu/sgx/arch.h"
+#include "../../../../../arch/x86/include/uapi/asm/sgx.h"
+
+#endif /* TYPES_H */
diff --git a/tools/testing/selftests/x86/sgx/encl.c b/tools/testing/selftests/x86/sgx/encl.c
new file mode 100644
index 000000000000..ede915399742
--- /dev/null
+++ b/tools/testing/selftests/x86/sgx/encl.c
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright(c) 2016-18 Intel Corporation.
+
+#include <stddef.h>
+#include "defines.h"
+
+static void *memcpy(void *dest, const void *src, size_t n)
+{
+ size_t i;
+
+ for (i = 0; i < n; i++)
+ ((char *)dest)[i] = ((char *)src)[i];
+
+ return dest;
+}
+
+void encl_body(void *rdi, void *rsi)
+{
+ memcpy(rsi, rdi, 8);
+}
diff --git a/tools/testing/selftests/x86/sgx/encl.lds b/tools/testing/selftests/x86/sgx/encl.lds
new file mode 100644
index 000000000000..2ee01ac3ec79
--- /dev/null
+++ b/tools/testing/selftests/x86/sgx/encl.lds
@@ -0,0 +1,33 @@
+OUTPUT_FORMAT(elf64-x86-64)
+
+SECTIONS
+{
+ . = 0;
+ .tcs : {
+ *(.tcs*)
+ }
+
+ . = ALIGN(4096);
+ .text : {
+ *(.text*)
+ *(.rodata*)
+ }
+
+ . = ALIGN(4096);
+ .data : {
+ *(.data*)
+ }
+
+ /DISCARD/ : {
+ *(.data*)
+ *(.comment*)
+ *(.note*)
+ *(.debug*)
+ *(.eh_frame*)
+ }
+}
+
+ASSERT(!DEFINED(.altinstructions), "ALTERNATIVES are not supported in enclaves")
+ASSERT(!DEFINED(.altinstr_replacement), "ALTERNATIVES are not supported in enclaves")
+ASSERT(!DEFINED(.discard.retpoline_safe), "RETPOLINE ALTERNATIVES are not supported in enclaves")
+ASSERT(!DEFINED(.discard.nospec), "RETPOLINE ALTERNATIVES are not supported in enclaves")
diff --git a/tools/testing/selftests/x86/sgx/encl_bootstrap.S b/tools/testing/selftests/x86/sgx/encl_bootstrap.S
new file mode 100644
index 000000000000..d07f970ccdf9
--- /dev/null
+++ b/tools/testing/selftests/x86/sgx/encl_bootstrap.S
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/*
+ * Copyright(c) 2016-18 Intel Corporation.
+ */
+
+ .macro ENCLU
+ .byte 0x0f, 0x01, 0xd7
+ .endm
+
+ .section ".tcs", "a"
+ .balign 4096
+
+ .fill 1, 8, 0 # STATE (set by CPU)
+ .fill 1, 8, 0 # FLAGS
+ .quad encl_ssa # OSSA
+ .fill 1, 4, 0 # CSSA (set by CPU)
+ .fill 1, 4, 1 # NSSA
+ .quad encl_entry # OENTRY
+ .fill 1, 8, 0 # AEP (set by EENTER and ERESUME)
+ .fill 1, 8, 0 # OFSBASE
+ .fill 1, 8, 0 # OGSBASE
+ .fill 1, 4, 0xFFFFFFFF # FSLIMIT
+ .fill 1, 4, 0xFFFFFFFF # GSLIMIT
+ .fill 4024, 1, 0 # Reserved
+
+ .text
+
+encl_entry:
+ # RBX contains the base address for TCS, which is also the first address
+ # inside the enclave. By adding the value of le_stack_end to it, we get
+ # the absolute address for the stack.
+ lea (encl_stack)(%rbx), %rax
+ xchg %rsp, %rax
+ push %rax
+
+ push %rcx # push the address after EENTER
+ push %rbx # push the enclave base address
+
+ call encl_body
+
+ pop %rbx # pop the enclave base address
+
+ # Restore XSAVE registers to a synthetic state.
+ mov $0xFFFFFFFF, %rax
+ mov $0xFFFFFFFF, %rdx
+ lea (xsave_area)(%rbx), %rdi
+ fxrstor (%rdi)
+
+ # Clear GPRs.
+ xor %rcx, %rcx
+ xor %rdx, %rdx
+ xor %rdi, %rdi
+ xor %rsi, %rsi
+ xor %r8, %r8
+ xor %r9, %r9
+ xor %r10, %r10
+ xor %r11, %r11
+ xor %r12, %r12
+ xor %r13, %r13
+ xor %r14, %r14
+ xor %r15, %r15
+
+ # Reset status flags.
+ add %rdx, %rdx # OF = SF = AF = CF = 0; ZF = PF = 1
+
+ # Prepare EEXIT target by popping the address of the instruction after
+ # EENTER to RBX.
+ pop %rbx
+
+ # Restore the caller stack.
+ pop %rax
+ mov %rax, %rsp
+
+ # EEXIT
+ mov $4, %rax
+ enclu
+
+ .section ".data", "aw"
+
+encl_ssa:
+ .space 4096
+
+xsave_area:
+ .fill 1, 4, 0x037F # FCW
+ .fill 5, 4, 0
+ .fill 1, 4, 0x1F80 # MXCSR
+ .fill 1, 4, 0xFFFF # MXCSR_MASK
+ .fill 123, 4, 0
+ .fill 1, 4, 0x80000000 # XCOMP_BV[63] = 1, compaction mode
+ .fill 12, 4, 0
+
+ .balign 4096
+ .space 8192
+encl_stack:
diff --git a/tools/testing/selftests/x86/sgx/encl_piggy.S b/tools/testing/selftests/x86/sgx/encl_piggy.S
new file mode 100644
index 000000000000..542001658afb
--- /dev/null
+++ b/tools/testing/selftests/x86/sgx/encl_piggy.S
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/*
+ * Copyright(c) 2016-18 Intel Corporation.
+ */
+
+ .section ".rodata", "a"
+
+encl_bin:
+ .globl encl_bin
+ .incbin "encl.bin"
+encl_bin_end:
+ .globl encl_bin_end
+
+encl_ss:
+ .globl encl_ss
+ .incbin "encl.ss"
+encl_ss_end:
+ .globl encl_ss_end
diff --git a/tools/testing/selftests/x86/sgx/encl_piggy.h b/tools/testing/selftests/x86/sgx/encl_piggy.h
new file mode 100644
index 000000000000..ee8224f8cc8d
--- /dev/null
+++ b/tools/testing/selftests/x86/sgx/encl_piggy.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/*
+ * Copyright(c) 2016-18 Intel Corporation.
+ */
+
+#ifndef ENCL_PIGGY_H
+#define ENCL_PIGGY_H
+
+extern unsigned char encl_bin[];
+extern unsigned char encl_bin_end[];
+extern unsigned char encl_ss[];
+extern unsigned char encl_ss_end[];
+
+#endif /* ENCL_PIGGY_H */
diff --git a/tools/testing/selftests/x86/sgx/main.c b/tools/testing/selftests/x86/sgx/main.c
new file mode 100644
index 000000000000..61cf62fb300a
--- /dev/null
+++ b/tools/testing/selftests/x86/sgx/main.c
@@ -0,0 +1,280 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright(c) 2016-18 Intel Corporation.
+
+#include <elf.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include "encl_piggy.h"
+#include "defines.h"
+#include "../../../../../arch/x86/kernel/cpu/sgx/arch.h"
+#include "../../../../../arch/x86/include/uapi/asm/sgx.h"
+
+static const uint64_t MAGIC = 0x1122334455667788ULL;
+
+struct vdso_symtab {
+ Elf64_Sym *elf_symtab;
+ const char *elf_symstrtab;
+ Elf64_Word *elf_hashtab;
+};
+
+static void *vdso_get_base_addr(char *envp[])
+{
+ Elf64_auxv_t *auxv;
+ int i;
+
+ for (i = 0; envp[i]; i++);
+ auxv = (Elf64_auxv_t *)&envp[i + 1];
+
+ for (i = 0; auxv[i].a_type != AT_NULL; i++) {
+ if (auxv[i].a_type == AT_SYSINFO_EHDR)
+ return (void *)auxv[i].a_un.a_val;
+ }
+
+ return NULL;
+}
+
+static Elf64_Dyn *vdso_get_dyntab(void *addr)
+{
+ Elf64_Ehdr *ehdr = addr;
+ Elf64_Phdr *phdrtab = addr + ehdr->e_phoff;
+ int i;
+
+ for (i = 0; i < ehdr->e_phnum; i++)
+ if (phdrtab[i].p_type == PT_DYNAMIC)
+ return addr + phdrtab[i].p_offset;
+
+ return NULL;
+}
+
+static void *vdso_get_dyn(void *addr, Elf64_Dyn *dyntab, Elf64_Sxword tag)
+{
+ int i;
+
+ for (i = 0; dyntab[i].d_tag != DT_NULL; i++)
+ if (dyntab[i].d_tag == tag)
+ return addr + dyntab[i].d_un.d_ptr;
+
+ return NULL;
+}
+
+static bool vdso_get_symtab(void *addr, struct vdso_symtab *symtab)
+{
+ Elf64_Dyn *dyntab = vdso_get_dyntab(addr);
+
+ symtab->elf_symtab = vdso_get_dyn(addr, dyntab, DT_SYMTAB);
+ if (!symtab->elf_symtab)
+ return false;
+
+ symtab->elf_symstrtab = vdso_get_dyn(addr, dyntab, DT_STRTAB);
+ if (!symtab->elf_symstrtab)
+ return false;
+
+ symtab->elf_hashtab = vdso_get_dyn(addr, dyntab, DT_HASH);
+ if (!symtab->elf_hashtab)
+ return false;
+
+ return true;
+}
+
+static unsigned long elf_sym_hash(const char *name)
+{
+ unsigned long h = 0, high;
+
+ while (*name) {
+ h = (h << 4) + *name++;
+ high = h & 0xf0000000;
+
+ if (high)
+ h ^= high >> 24;
+
+ h &= ~high;
+ }
+
+ return h;
+}
+
+static Elf64_Sym *vdso_symtab_get(struct vdso_symtab *symtab, const char *name)
+{
+ Elf64_Word bucketnum = symtab->elf_hashtab[0];
+ Elf64_Word *buckettab = &symtab->elf_hashtab[2];
+ Elf64_Word *chaintab = &symtab->elf_hashtab[2 + bucketnum];
+ Elf64_Sym *sym;
+ Elf64_Word i;
+
+ for (i = buckettab[elf_sym_hash(name) % bucketnum]; i != STN_UNDEF;
+ i = chaintab[i]) {
+ sym = &symtab->elf_symtab[i];
+ if (!strcmp(name, &symtab->elf_symstrtab[sym->st_name]))
+ return sym;
+ }
+
+ return NULL;
+}
+
+static bool encl_create(int dev_fd, unsigned long bin_size,
+ struct sgx_secs *secs)
+{
+ struct sgx_enclave_create ioc;
+ void *base;
+ int rc;
+
+ memset(secs, 0, sizeof(*secs));
+ secs->ssa_frame_size = 1;
+ secs->attributes = SGX_ATTR_MODE64BIT;
+ secs->xfrm = 3;
+
+ for (secs->size = 4096; secs->size < bin_size; )
+ secs->size <<= 1;
+
+ base = mmap(NULL, secs->size, PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_SHARED, dev_fd, 0);
+ if (base == MAP_FAILED) {
+ perror("mmap");
+ return false;
+ }
+
+ secs->base = (uint64_t)base;
+
+ ioc.src = (unsigned long)secs;
+ rc = ioctl(dev_fd, SGX_IOC_ENCLAVE_CREATE, &ioc);
+ if (rc) {
+ fprintf(stderr, "ECREATE failed rc=%d.\n", rc);
+ munmap(base, secs->size);
+ return false;
+ }
+
+ return true;
+}
+
+static bool encl_add_page(int dev_fd, unsigned long addr, void *data,
+ uint64_t flags)
+{
+ struct sgx_enclave_add_page ioc;
+ struct sgx_secinfo secinfo;
+ int rc;
+
+ memset(&secinfo, 0, sizeof(secinfo));
+ secinfo.flags = flags;
+
+ ioc.secinfo = (unsigned long)&secinfo;
+ ioc.mrmask = 0xFFFF;
+ ioc.addr = addr;
+ ioc.src = (uint64_t)data;
+
+ rc = ioctl(dev_fd, SGX_IOC_ENCLAVE_ADD_PAGE, &ioc);
+ if (rc) {
+ fprintf(stderr, "EADD failed rc=%d.\n", rc);
+ return false;
+ }
+
+ return true;
+}
+
+static bool encl_load(struct sgx_secs *secs, unsigned long bin_size)
+{
+ struct sgx_enclave_init ioc;
+ uint64_t offset;
+ uint64_t flags;
+ int dev_fd;
+ int rc;
+
+ dev_fd = open("/dev/sgx", O_RDWR);
+ if (dev_fd < 0) {
+ fprintf(stderr, "Unable to open /dev/sgx\n");
+ return false;
+ }
+
+ if (!encl_create(dev_fd, bin_size, secs))
+ goto out_dev_fd;
+
+ for (offset = 0; offset < bin_size; offset += 0x1000) {
+ if (!offset)
+ flags = SGX_SECINFO_TCS;
+ else
+ flags = SGX_SECINFO_REG | SGX_SECINFO_R |
+ SGX_SECINFO_W | SGX_SECINFO_X;
+
+ if (!encl_add_page(dev_fd, secs->base + offset,
+ encl_bin + offset, flags))
+ goto out_map;
+ }
+
+ ioc.addr = secs->base;
+ ioc.sigstruct = (uint64_t)&encl_ss;
+ rc = ioctl(dev_fd, SGX_IOC_ENCLAVE_INIT, &ioc);
+ if (rc) {
+ printf("EINIT failed rc=%d\n", rc);
+ goto out_map;
+ }
+
+ close(dev_fd);
+ return true;
+out_map:
+ munmap((void *)secs->base, secs->size);
+out_dev_fd:
+ close(dev_fd);
+ return false;
+}
+
+void sgx_call(void *rdi, void *rsi, void *tcs,
+ struct sgx_enclave_exception *exception,
+ void *eenter);
+
+int main(int argc, char *argv[], char *envp[])
+{
+ unsigned long bin_size = encl_bin_end - encl_bin;
+ unsigned long ss_size = encl_ss_end - encl_ss;
+ struct sgx_enclave_exception exception;
+ Elf64_Sym *eenter_sym;
+ struct vdso_symtab symtab;
+ struct sgx_secs secs;
+ uint64_t result = 0;
+ void *eenter;
+ void *addr;
+
+ memset(&exception, 0, sizeof(exception));
+
+ addr = vdso_get_base_addr(envp);
+ if (!addr)
+ exit(1);
+
+ if (!vdso_get_symtab(addr, &symtab))
+ exit(1);
+
+ eenter_sym = vdso_symtab_get(&symtab, "__vdso_sgx_enter_enclave");
+ if (!eenter_sym)
+ exit(1);
+ eenter = addr + eenter_sym->st_value;
+
+ printf("Binary size %lu (0x%lx), SIGSTRUCT size %lu\n", bin_size,
+ bin_size, ss_size);
+ if (ss_size != sizeof(struct sgx_sigstruct)) {
+ fprintf(stderr, "The size of SIGSTRUCT should be %lu\n",
+ sizeof(struct sgx_sigstruct));
+ exit(1);
+ }
+
+ printf("Loading the enclave.\n");
+ if (!encl_load(&secs, bin_size))
+ exit(1);
+
+ printf("Input: 0x%lx\n", MAGIC);
+ sgx_call((void *)&MAGIC, &result, (void *)secs.base, &exception,
+ eenter);
+ if (result != MAGIC) {
+ fprintf(stderr, "0x%lx != 0x%lx\n", result, MAGIC);
+ exit(1);
+ }
+
+ printf("Output: 0x%lx\n", result);
+ exit(0);
+}
diff --git a/tools/testing/selftests/x86/sgx/sgx_call.S b/tools/testing/selftests/x86/sgx/sgx_call.S
new file mode 100644
index 000000000000..14bd0a044199
--- /dev/null
+++ b/tools/testing/selftests/x86/sgx/sgx_call.S
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/**
+* Copyright(c) 2016-18 Intel Corporation.
+*/
+
+ .text
+
+ .global sgx_call
+sgx_call:
+ push %rbx
+ mov $0x02, %rax
+ mov %rdx, %rbx
+ call *%r8
+ pop %rbx
+ ret
diff --git a/tools/testing/selftests/x86/sgx/sgxsign.c b/tools/testing/selftests/x86/sgx/sgxsign.c
new file mode 100644
index 000000000000..0b89823fc703
--- /dev/null
+++ b/tools/testing/selftests/x86/sgx/sgxsign.c
@@ -0,0 +1,508 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright(c) 2016-18 Intel Corporation.
+
+#define _GNU_SOURCE
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include "defines.h"
+
+struct sgx_sigstruct_payload {
+ struct sgx_sigstruct_header header;
+ struct sgx_sigstruct_body body;
+};
+
+static const char *sign_key_pass;
+
+static bool check_crypto_errors(void)
+{
+ int err;
+ bool had_errors = false;
+ const char *filename;
+ int line;
+ char str[256];
+
+ for ( ; ; ) {
+ if (ERR_peek_error() == 0)
+ break;
+
+ had_errors = true;
+ err = ERR_get_error_line(&filename, &line);
+ ERR_error_string_n(err, str, sizeof(str));
+ fprintf(stderr, "crypto: %s: %s:%d\n", str, filename, line);
+ }
+
+ return had_errors;
+}
+
+static void exit_usage(const char *program)
+{
+ fprintf(stderr,
+ "Usage: %s/sign-le <key> <enclave> <sigstruct>\n", program);
+ exit(1);
+}
+
+static int pem_passwd_cb(char *buf, int size, int rwflag, void *u)
+{
+ if (!sign_key_pass)
+ return -1;
+
+ strncpy(buf, sign_key_pass, size);
+ /* no retry */
+ sign_key_pass = NULL;
+
+ return strlen(buf) >= size ? size - 1 : strlen(buf);
+}
+
+static inline const BIGNUM *get_modulus(RSA *key)
+{
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ return key->n;
+#else
+ const BIGNUM *n;
+
+ RSA_get0_key(key, &n, NULL, NULL);
+ return n;
+#endif
+}
+
+static RSA *load_sign_key(const char *path)
+{
+ FILE *f;
+ RSA *key;
+
+ f = fopen(path, "rb");
+ if (!f) {
+ fprintf(stderr, "Unable to open %s\n", path);
+ return NULL;
+ }
+ key = RSA_new();
+ if (!PEM_read_RSAPrivateKey(f, &key, pem_passwd_cb, NULL))
+ return NULL;
+ fclose(f);
+
+ if (BN_num_bytes(get_modulus(key)) != SGX_MODULUS_SIZE) {
+ fprintf(stderr, "Invalid key size %d\n",
+ BN_num_bytes(get_modulus(key)));
+ RSA_free(key);
+ return NULL;
+ }
+
+ return key;
+}
+
+static void reverse_bytes(void *data, int length)
+{
+ int i = 0;
+ int j = length - 1;
+ uint8_t temp;
+ uint8_t *ptr = data;
+
+ while (i < j) {
+ temp = ptr[i];
+ ptr[i] = ptr[j];
+ ptr[j] = temp;
+ i++;
+ j--;
+ }
+}
+
+enum mrtags {
+ MRECREATE = 0x0045544145524345,
+ MREADD = 0x0000000044444145,
+ MREEXTEND = 0x00444E4554584545,
+};
+
+static bool mrenclave_update(EVP_MD_CTX *ctx, const void *data)
+{
+ if (!EVP_DigestUpdate(ctx, data, 64)) {
+ fprintf(stderr, "digest update failed\n");
+ return false;
+ }
+
+ return true;
+}
+
+static bool mrenclave_commit(EVP_MD_CTX *ctx, uint8_t *mrenclave)
+{
+ unsigned int size;
+
+ if (!EVP_DigestFinal_ex(ctx, (unsigned char *)mrenclave, &size)) {
+ fprintf(stderr, "digest commit failed\n");
+ return false;
+ }
+
+ if (size != 32) {
+ fprintf(stderr, "invalid digest size = %u\n", size);
+ return false;
+ }
+
+ return true;
+}
+
+struct mrecreate {
+ uint64_t tag;
+ uint32_t ssaframesize;
+ uint64_t size;
+ uint8_t reserved[44];
+} __attribute__((__packed__));
+
+
+static bool mrenclave_ecreate(EVP_MD_CTX *ctx, uint64_t blob_size)
+{
+ struct mrecreate mrecreate;
+ uint64_t encl_size;
+
+ for (encl_size = 0x1000; encl_size < blob_size; )
+ encl_size <<= 1;
+
+ memset(&mrecreate, 0, sizeof(mrecreate));
+ mrecreate.tag = MRECREATE;
+ mrecreate.ssaframesize = 1;
+ mrecreate.size = encl_size;
+
+ if (!EVP_DigestInit_ex(ctx, EVP_sha256(), NULL))
+ return false;
+
+ return mrenclave_update(ctx, &mrecreate);
+}
+
+struct mreadd {
+ uint64_t tag;
+ uint64_t offset;
+ uint64_t flags; /* SECINFO flags */
+ uint8_t reserved[40];
+} __attribute__((__packed__));
+
+static bool mrenclave_eadd(EVP_MD_CTX *ctx, uint64_t offset, uint64_t flags)
+{
+ struct mreadd mreadd;
+
+ memset(&mreadd, 0, sizeof(mreadd));
+ mreadd.tag = MREADD;
+ mreadd.offset = offset;
+ mreadd.flags = flags;
+
+ return mrenclave_update(ctx, &mreadd);
+}
+
+struct mreextend {
+ uint64_t tag;
+ uint64_t offset;
+ uint8_t reserved[48];
+} __attribute__((__packed__));
+
+static bool mrenclave_eextend(EVP_MD_CTX *ctx, uint64_t offset, uint8_t *data)
+{
+ struct mreextend mreextend;
+ int i;
+
+ for (i = 0; i < 0x1000; i += 0x100) {
+ memset(&mreextend, 0, sizeof(mreextend));
+ mreextend.tag = MREEXTEND;
+ mreextend.offset = offset + i;
+
+ if (!mrenclave_update(ctx, &mreextend))
+ return false;
+
+ if (!mrenclave_update(ctx, &data[i + 0x00]))
+ return false;
+
+ if (!mrenclave_update(ctx, &data[i + 0x40]))
+ return false;
+
+ if (!mrenclave_update(ctx, &data[i + 0x80]))
+ return false;
+
+ if (!mrenclave_update(ctx, &data[i + 0xC0]))
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * measure_encl - measure enclave
+ * @path: path to the enclave
+ * @mrenclave: measurement
+ *
+ * Calculates MRENCLAVE. Assumes that the very first page is a TCS page and
+ * following pages are regular pages. Does not measure the contents of the
+ * enclave as the signing tool is used at the moment only for the launch
+ * enclave, which is pass-through (everything gets a token).
+ */
+static bool measure_encl(const char *path, uint8_t *mrenclave)
+{
+ FILE *file;
+ struct stat sb;
+ EVP_MD_CTX *ctx;
+ uint64_t flags;
+ uint64_t offset;
+ uint8_t data[0x1000];
+ int rc;
+
+ ctx = EVP_MD_CTX_create();
+ if (!ctx)
+ return false;
+
+ file = fopen(path, "rb");
+ if (!file) {
+ perror("fopen");
+ EVP_MD_CTX_destroy(ctx);
+ return false;
+ }
+
+ rc = stat(path, &sb);
+ if (rc) {
+ perror("stat");
+ goto out;
+ }
+
+ if (!sb.st_size || sb.st_size & 0xfff) {
+ fprintf(stderr, "Invalid blob size %lu\n", sb.st_size);
+ goto out;
+ }
+
+ if (!mrenclave_ecreate(ctx, sb.st_size))
+ goto out;
+
+ for (offset = 0; offset < sb.st_size; offset += 0x1000) {
+ if (!offset)
+ flags = SGX_SECINFO_TCS;
+ else
+ flags = SGX_SECINFO_REG | SGX_SECINFO_R |
+ SGX_SECINFO_W | SGX_SECINFO_X;
+
+ if (!mrenclave_eadd(ctx, offset, flags))
+ goto out;
+
+ rc = fread(data, 1, 0x1000, file);
+ if (!rc)
+ break;
+ if (rc < 0x1000)
+ goto out;
+
+ if (!mrenclave_eextend(ctx, offset, data))
+ goto out;
+ }
+
+ if (!mrenclave_commit(ctx, mrenclave))
+ goto out;
+
+ fclose(file);
+ EVP_MD_CTX_destroy(ctx);
+ return true;
+out:
+ fclose(file);
+ EVP_MD_CTX_destroy(ctx);
+ return false;
+}
+
+/**
+ * sign_encl - sign enclave
+ * @sigstruct: pointer to SIGSTRUCT
+ * @key: 3072-bit RSA key
+ * @signature: byte array for the signature
+ *
+ * Calculates EMSA-PKCSv1.5 signature for the given SIGSTRUCT. The result is
+ * stored in big-endian format so that it can be further passed to OpenSSL
+ * libcrypto functions.
+ */
+static bool sign_encl(const struct sgx_sigstruct *sigstruct, RSA *key,
+ uint8_t *signature)
+{
+ struct sgx_sigstruct_payload payload;
+ unsigned int siglen;
+ uint8_t digest[SHA256_DIGEST_LENGTH];
+ bool ret;
+
+ memcpy(&payload.header, &sigstruct->header, sizeof(sigstruct->header));
+ memcpy(&payload.body, &sigstruct->body, sizeof(sigstruct->body));
+
+ SHA256((unsigned char *)&payload, sizeof(payload), digest);
+
+ ret = RSA_sign(NID_sha256, digest, SHA256_DIGEST_LENGTH, signature,
+ &siglen, key);
+
+ return ret;
+}
+
+struct q1q2_ctx {
+ BN_CTX *bn_ctx;
+ BIGNUM *m;
+ BIGNUM *s;
+ BIGNUM *q1;
+ BIGNUM *qr;
+ BIGNUM *q2;
+};
+
+static void free_q1q2_ctx(struct q1q2_ctx *ctx)
+{
+ BN_CTX_free(ctx->bn_ctx);
+ BN_free(ctx->m);
+ BN_free(ctx->s);
+ BN_free(ctx->q1);
+ BN_free(ctx->qr);
+ BN_free(ctx->q2);
+}
+
+static bool alloc_q1q2_ctx(const uint8_t *s, const uint8_t *m,
+ struct q1q2_ctx *ctx)
+{
+ ctx->bn_ctx = BN_CTX_new();
+ ctx->s = BN_bin2bn(s, SGX_MODULUS_SIZE, NULL);
+ ctx->m = BN_bin2bn(m, SGX_MODULUS_SIZE, NULL);
+ ctx->q1 = BN_new();
+ ctx->qr = BN_new();
+ ctx->q2 = BN_new();
+
+ if (!ctx->bn_ctx || !ctx->s || !ctx->m || !ctx->q1 || !ctx->qr ||
+ !ctx->q2) {
+ free_q1q2_ctx(ctx);
+ return false;
+ }
+
+ return true;
+}
+
+static bool calc_q1q2(const uint8_t *s, const uint8_t *m, uint8_t *q1,
+ uint8_t *q2)
+{
+ struct q1q2_ctx ctx;
+
+ if (!alloc_q1q2_ctx(s, m, &ctx)) {
+ fprintf(stderr, "Not enough memory for Q1Q2 calculation\n");
+ return false;
+ }
+
+ if (!BN_mul(ctx.q1, ctx.s, ctx.s, ctx.bn_ctx))
+ goto out;
+
+ if (!BN_div(ctx.q1, ctx.qr, ctx.q1, ctx.m, ctx.bn_ctx))
+ goto out;
+
+ if (BN_num_bytes(ctx.q1) > SGX_MODULUS_SIZE) {
+ fprintf(stderr, "Too large Q1 %d bytes\n",
+ BN_num_bytes(ctx.q1));
+ goto out;
+ }
+
+ if (!BN_mul(ctx.q2, ctx.s, ctx.qr, ctx.bn_ctx))
+ goto out;
+
+ if (!BN_div(ctx.q2, NULL, ctx.q2, ctx.m, ctx.bn_ctx))
+ goto out;
+
+ if (BN_num_bytes(ctx.q2) > SGX_MODULUS_SIZE) {
+ fprintf(stderr, "Too large Q2 %d bytes\n",
+ BN_num_bytes(ctx.q2));
+ goto out;
+ }
+
+ BN_bn2bin(ctx.q1, q1);
+ BN_bn2bin(ctx.q2, q2);
+
+ free_q1q2_ctx(&ctx);
+ return true;
+out:
+ free_q1q2_ctx(&ctx);
+ return false;
+}
+
+static bool save_sigstruct(const struct sgx_sigstruct *sigstruct,
+ const char *path)
+{
+ FILE *f = fopen(path, "wb");
+
+ if (!f) {
+ fprintf(stderr, "Unable to open %s\n", path);
+ return false;
+ }
+
+ fwrite(sigstruct, sizeof(*sigstruct), 1, f);
+ fclose(f);
+ return true;
+}
+
+int main(int argc, char **argv)
+{
+ uint64_t header1[2] = {0x000000E100000006, 0x0000000000010000};
+ uint64_t header2[2] = {0x0000006000000101, 0x0000000100000060};
+ struct sgx_sigstruct ss;
+ const char *program;
+ int opt;
+ RSA *sign_key;
+
+ memset(&ss, 0, sizeof(ss));
+ ss.header.header1[0] = header1[0];
+ ss.header.header1[1] = header1[1];
+ ss.header.header2[0] = header2[0];
+ ss.header.header2[1] = header2[1];
+ ss.exponent = 3;
+
+#ifndef CONFIG_EINITTOKENKEY
+ ss.body.attributes = SGX_ATTR_MODE64BIT;
+#else
+ ss.body.attributes = SGX_ATTR_MODE64BIT | SGX_ATTR_EINITTOKENKEY;
+#endif
+ ss.body.xfrm = 3,
+
+ sign_key_pass = getenv("KBUILD_SGX_SIGN_PIN");
+ program = argv[0];
+
+ do {
+ opt = getopt(argc, argv, "");
+ switch (opt) {
+ case -1:
+ break;
+ default:
+ exit_usage(program);
+ }
+ } while (opt != -1);
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 3)
+ exit_usage(program);
+
+ /* sanity check only */
+ if (check_crypto_errors())
+ exit(1);
+
+ sign_key = load_sign_key(argv[0]);
+ if (!sign_key)
+ goto out;
+
+ BN_bn2bin(get_modulus(sign_key), ss.modulus);
+
+ if (!measure_encl(argv[1], ss.body.mrenclave))
+ goto out;
+
+ if (!sign_encl(&ss, sign_key, ss.signature))
+ goto out;
+
+ if (!calc_q1q2(ss.signature, ss.modulus, ss.q1, ss.q2))
+ goto out;
+
+ /* convert to little endian */
+ reverse_bytes(ss.signature, SGX_MODULUS_SIZE);
+ reverse_bytes(ss.modulus, SGX_MODULUS_SIZE);
+ reverse_bytes(ss.q1, SGX_MODULUS_SIZE);
+ reverse_bytes(ss.q2, SGX_MODULUS_SIZE);
+
+ if (!save_sigstruct(&ss, argv[2]))
+ goto out;
+ exit(0);
+out:
+ check_crypto_errors();
+ exit(1);
+}
diff --git a/tools/testing/selftests/x86/sgx/signing_key.pem b/tools/testing/selftests/x86/sgx/signing_key.pem
new file mode 100644
index 000000000000..d76f21f19187
--- /dev/null
+++ b/tools/testing/selftests/x86/sgx/signing_key.pem
@@ -0,0 +1,39 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIG4wIBAAKCAYEApalGbq7Q+usM91CPtksu3D+b0Prc8gAFL6grM3mg85A5Bx8V
+cfMXPgtrw8EYFwQxDAvzZWwl+9VfOX0ECrFRBkOHcOiG0SnADN8+FLj1UiNUQwbp
+S6OzhNWuRcSbGraSOyUlVlV0yMQSvewyzGklOaXBe30AJqzIBc8QfdSxKuP8rs0Z
+ga6k/Bl73osrYKByILJTUUeZqjLERsE6GebsdzbWgKn8qVqng4ZS4yMNg6LeRlH3
++9CIPgg4jwpSLHcp7dq2qTIB9a0tGe9ayp+5FbucpB6U7ePold0EeRN6RlJGDF9k
+L93v8P5ykz5G5gYZ2g0K1X2sHIWV4huxPgv5PXgdyQYbK+6olqj0d5rjYuwX57Ul
+k6SroPS1U6UbdCjG5txM+BNGU0VpD0ZhrIRw0leQdnNcCO9sTJuInZrgYacSVJ7u
+mtB+uCt+uzUesc+l+xPRYA+9e14lLkZp7AAmo9FvL816XDI09deehJ3i/LmHKCRN
+tuqC5TprRjFwUr6dAgEDAoIBgG5w2Z8fNfycs0+LCnmHdJLVEotR6KFVWMpwHMz7
+wKJgJgS/Y6FMuilc8oKAuroCy11dTO5IGVKOP3uorVx2NgQtBPXwWeDGgAiU1A3Q
+o4wXjYIEm4fCd63jyYPYZ2ckYXzDbjmOTdstYdPyzIhGGNEZK6eoqsRzMAPfYFPj
+IMdCqHSIu6vJw1K7p+myHOsVoWshjODaZnF3LYSA0WaZ8vokjwBxUxuRxQJZjJds
+s60XPtmL+qfgWtQFewoG4XL6GuD8FcXccynRRtzrLtFNPIl9BQfWfjBBhTC1/Te1
+0Z6XbZvpdUTD9OfLB7SbR2OUFNpKQgriO0iYVdbW3cr7uu38Zwp4W1TX73DPjoi6
+KNooP6SGWd4mRJW2+dUmSYS4QNG8eVVZswKcploEIXlAKRsOe4kzJJ1iETugIe85
+uX8nd1WYEp65xwoRUg8hqng0MeyveVbXqNKuJG6tzNDt9kgFYo+hmC/oouAW2Dtc
+T9jdRAwKJXqA2Eg6OkgXCEv+kwKBwQDYaQiFMlFhsmLlqI+EzCUh7c941/cL7m6U
+7j98+8ngl0HgCEcrc10iJVCKakQW3YbPzAx3XkKTaGjWazvvrFarXIGlOud64B8a
+iWyQ7VdlnmZnNEdk+C83tI91OQeaTKqRLDGzKh29Ry/jL8Pcbazt+kDgxa0H7qJp
+roADUanLQuNkYubpbhFBh3xpa2EExaVq6rF7nIVsD8W9TrbmPKA4LgH7z0iy544D
+kVCNYsTjYDdUWP+WiSor8kCnnpjnN9sCgcEAw/eNezUD1UDf6OYFC9+5JZJFn4Tg
+mZMyN93JKIb199ffwnjtHUSjcyiWeesXucpzwtGbTcwQnDisSW4oneYKLSEBlBaq
+scqiUugyGZZOthFSCbdXYXMViK2vHrKlkse7GxVlROKcEhM/pRBrmjaGO8eWR+D4
+FO2wCXzVs3KgV6j779frw0vC54oHOxc9+Lu1rSHp4i+600koyvL/zF6U/5tZXIvN
+YW2yoiQJnjCmVA1pwbwV6KAUTPDTMnBK+YjnAoHBAJBGBa4hi5Z27JkbCliIGMFJ
+NPs6pLKe9GNJf6in2+sPgUAFhMeiPhbDiwbxgrnpBIqICE+ULGJFmzmc0p/IOceT
+ARjR76dAFLxbnbXzj5kURETNhO36yiUjCk4mBRGIcbYddndxaSjaH+zKgpLzyJ6m
+1esuc1qfFvEfAAI2cTIsl5hB70ZJYNZaUvDyQK3ZGPHxy6e9rkgKg9OJz0QoatAe
+q/002yHvtAJg4F5B2JeVejg7VQ8GHB1MKxppu0TP5wKBwQCCpQj8zgKOKz/wmViy
+lSYZDC5qWJW7t3bP6TDFr06lOpUsUJ4TgxeiGw778g/RMaKB4RIz3WBoJcgw9BsT
+7rFza1ZiucchMcGMmswRDt8kC4wGejpA92Owc8oUdxkMhSdnY5jYlxK2t3/DYEe8
+JFl9L7mFQKVjSSAGUzkiTGrlG1Kf5UfXh9dFBq98uilQfSPIwUaWynyM23CHTKqI
+Pw3/vOY9sojrnncWwrEUIG7is5vWfWPwargzSzd29YdRBe8CgcEAuRVewK/YeNOX
+B7ZG6gKKsfsvrGtY7FPETzLZAHjoVXYNea4LVZ2kn4hBXXlvw/4HD+YqcTt4wmif
+5JQlDvjNobUiKJZpzy7hklVhF7wZFl4pCF7Yh43q9iQ7gKTaeUG7MiaK+G8Zz8aY
+HW9rsiihbdZkccMvnPfO9334XMxl3HtBRzLstjUlbLB7Sdh+7tZ3JQidCOFNs5pE
+XyWwnASPu4tKfDahH1UUTp1uJcq/6716CSWg080avYxFcn75qqsb
+-----END RSA PRIVATE KEY-----
--
2.19.1
From: Sean Christopherson <[email protected]>
vDSO functions can now leverage an exception fixup mechanism similar to
kernel exception fixup. For vDSO exception fixup, the initial user is
Intel's Software Guard Extensions (SGX), which will wrap the low-level
transitions to/from the enclave, i.e. EENTER and ERESUME instructions,
in a vDSO function and leverage fixup to intercept exceptions that would
otherwise generate a signal. This allows the vDSO wrapper to return the
fault information directly to its caller, obviating the need for SGX
applications and libraries to juggle signal handlers.
Attempt to fixup vDSO exceptions immediately prior to populating and
sending signal information. Except for the delivery mechanism, an
exception in a vDSO function should be treated like any other exception
in userspace, e.g. any fault that is successfully handled by the kernel
should not be directly visible to userspace.
Although it's debatable whether or not all exceptions are of interest to
enclaves, defer to the vDSO fixup to decide whether to do fixup or
generate a signal. Future users of vDSO fixup, if there ever are any,
will undoubtedly have different requirements than SGX enclaves, e.g. the
fixup vs. signal logic can be made function specific if/when necessary.
Suggested-by: Andy Lutomirski <[email protected]>
Cc: Andy Lutomirski <[email protected]>
Cc: Jarkko Sakkinen <[email protected]>
Cc: Dave Hansen <[email protected]>
Cc: Josh Triplett <[email protected]>
Signed-off-by: Sean Christopherson <[email protected]>
---
arch/x86/kernel/traps.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index d26f9e9c3d83..02eda456c119 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -61,6 +61,7 @@
#include <asm/mpx.h>
#include <asm/vm86.h>
#include <asm/umip.h>
+#include <asm/vdso.h>
#ifdef CONFIG_X86_64
#include <asm/x86_init.h>
@@ -210,6 +211,9 @@ do_trap_no_signal(struct task_struct *tsk, int trapnr, const char *str,
tsk->thread.error_code = error_code;
tsk->thread.trap_nr = trapnr;
die(str, regs, error_code);
+ } else {
+ if (fixup_vdso_exception(regs, trapnr, error_code, 0))
+ return 0;
}
/*
@@ -561,6 +565,9 @@ do_general_protection(struct pt_regs *regs, long error_code)
return;
}
+ if (fixup_vdso_exception(regs, X86_TRAP_GP, error_code, 0))
+ return;
+
tsk->thread.error_code = error_code;
tsk->thread.trap_nr = X86_TRAP_GP;
@@ -775,6 +782,10 @@ dotraplinkage void do_debug(struct pt_regs *regs, long error_code)
SIGTRAP) == NOTIFY_STOP)
goto exit;
+ if (user_mode(regs) &&
+ fixup_vdso_exception(regs, X86_TRAP_DB, error_code, 0))
+ goto exit;
+
/*
* Let others (NMI) know that the debug stack is in use
* as we may switch to the interrupt stack.
@@ -855,6 +866,9 @@ static void math_error(struct pt_regs *regs, int error_code, int trapnr)
if (!si_code)
return;
+ if (fixup_vdso_exception(regs, trapnr, error_code, 0))
+ return;
+
force_sig_fault(SIGFPE, si_code,
(void __user *)uprobe_get_trap_addr(regs), task);
}
--
2.19.1
Add the maintainer information for the SGX subsystem.
Signed-off-by: Jarkko Sakkinen <[email protected]>
---
MAINTAINERS | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index e17ebf70b548..7abcb460dde9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8044,6 +8044,18 @@ L: [email protected]
S: Maintained
F: drivers/gpio/gpio-intel-mid.c
+INTEL SGX
+M: Jarkko Sakkinen <[email protected]>
+M: Sean Christopherson <[email protected]>
+L: [email protected]
+S: Maintained
+Q: https://patchwork.kernel.org/project/intel-sgx/list/
+T: git https://github.com/jsakkine-intel/linux-sgx.git
+F: arch/x86/include/asm/sgx.h
+F: arch/x86/include/uapi/asm/sgx.h
+F: arch/x86/kernel/cpu/sgx/*
+K: \bSGX_
+
INTERCONNECT API
M: Georgi Djakov <[email protected]>
S: Maintained
--
2.19.1
From: Sean Christopherson <[email protected]>
Intel Software Guard Extensions (SGX) SGX introduces a new CPL3-only
enclave mode that runs as a sort of black box shared object that is
hosted by an untrusted normal CPL3 process.
Enclave transitions have semantics that are a lovely blend of SYCALL,
SYSRET and VM-Exit. In a non-faulting scenario, entering and exiting
an enclave can only be done through SGX-specific instructions, EENTER
and EEXIT respectively. EENTER+EEXIT is analogous to SYSCALL+SYSRET,
e.g. EENTER/SYSCALL load RCX with the next RIP and EEXIT/SYSRET load
RIP from R{B,C}X.
But in a faulting/interrupting scenario, enclave transitions act more
like VM-Exit and VMRESUME. Maintaining the black box nature of the
enclave means that hardware must automatically switch CPU context when
an Asynchronous Exiting Event (AEE) occurs, an AEE being any interrupt
or exception (exceptions are AEEs because asynchronous in this context
is relative to the enclave and not CPU execution, e.g. the enclave
doesn't get an opportunity to save/fuzz CPU state).
Like VM-Exits, all AEEs jump to a common location, referred to as the
Asynchronous Exiting Point (AEP). The AEP is specified at enclave entry
via register passed to EENTER/ERESUME, similar to how the hypervisor
specifies the VM-Exit point (via VMCS.HOST_RIP at VMLAUNCH/VMRESUME).
Resuming the enclave/VM after the exiting event is handled is done via
ERESUME/VMRESUME respectively. In SGX, AEEs that are handled by the
kernel, e.g. INTR, NMI and most page faults, IRET will journey back to
the AEP which then ERESUMEs th enclave.
Enclaves also behave a bit like VMs in the sense that they can generate
exceptions as part of their normal operation that for all intents and
purposes need to handled in the enclave/VM. However, unlike VMX, SGX
doesn't allow the host to modify its guest's, a.k.a. enclave's, state,
as doing so would circumvent the enclave's security. So to handle an
exception, the enclave must first be re-entered through the normal
EENTER flow (SYSCALL/SYSRET behavior), and then resumed via ERESUME
(VMRESUME behavior) after the source of the exception is resolved.
All of the above is just the tip of the iceberg when it comes to running
an enclave. But, SGX was designed in such a way that the host process
can utilize a library to build, launch and run an enclave. This is
roughly analogous to how e.g. libc implementations are used by most
applications so that the application can focus on its business logic.
The big gotcha is that because enclaves can generate *and* handle
exceptions, any SGX library must be prepared to handle nearly any
exception at any time (well, any time a thread is executing in an
enclave). In Linux, this means the SGX library must register a
signal handler in order to intercept relevant exceptions and forward
them to the enclave (or in some cases, take action on behalf of the
enclave). Unfortunately, Linux's signal mechanism doesn't mesh well
with libraries, e.g. signal handlers are process wide, are difficult
to chain, etc... This becomes particularly nasty when using multiple
levels of libraries that register signal handlers, e.g. running an
enclave via cgo inside of the Go runtime.
In comes vDSO to save the day. Now that vDSO can fixup exceptions,
add a function, __vdso_sgx_enter_enclave(), to wrap enclave transitions
and intercept any exceptions that occur when running the enclave.
__vdso_sgx_enter_enclave() does NOT adhere to the x86-64 ABI and instead
uses a custom calling convention. The primary motivation is to avoid
issues that arise due to asynchronous enclave exits. The x86-64 ABI
requires that EFLAGS.DF, MXCSR and FCW be preserved by the callee, and
unfortunately for the vDSO, the aformentioned registers/bits are not
restored after an asynchronous exit, e.g. EFLAGS.DF is in an unknown
state while MXCSR and FCW are reset to their init values. So the vDSO
cannot simply pass the buck by requiring enclaves to adhere to the
x86-64 ABI. That leaves three somewhat reasonable options:
1) Save/restore non-volatile GPRs, MXCSR and FCW, and clear EFLAGS.DF
+ 100% compliant with the x86-64 ABI
+ Callable from any code
+ Minimal documentation required
- Restoring MXCSR/FCW is likely unnecessary 99% of the time
- Slow
2) Save/restore non-volatile GPRs and clear EFLAGS.DF
+ Mostly compliant with the x86-64 ABI
+ Callable from any code that doesn't use SIMD registers
- Need to document deviations from x86-64 ABI, i.e. MXCSR and FCW
3) Require the caller to save/restore everything.
+ Fast
+ Userspace can pass all GPRs to the enclave (minus EAX, RBX and RCX)
- Custom ABI
- For all intents and purposes must be called from an assembly wrapper
__vdso_sgx_enter_enclave() implements option (3). The custom ABI is
mostly a documentation issue, and even that is offset by the fact that
being more similar to hardware's ENCLU[EENTER/ERESUME] ABI reduces the
amount of documentation needed for the vDSO, e.g. options (2) and (3)
would need to document which registers are marshalled to/from enclaves.
Requiring an assembly wrapper imparts minimal pain on userspace as SGX
libraries and/or applications need a healthy chunk of assembly, e.g. in
the enclave, regardless of the vDSO's implementation.
Suggested-by: Andy Lutomirski <[email protected]>
Cc: Andy Lutomirski <[email protected]>
Cc: Jarkko Sakkinen <[email protected]>
Cc: Dave Hansen <[email protected]>
Cc: Josh Triplett <[email protected]>
Cc: Haitao Huang <[email protected]>
Cc: Jethro Beekman <[email protected]>
Cc: Dr. Greg Wettstein <[email protected]>
Signed-off-by: Sean Christopherson <[email protected]>
---
arch/x86/entry/vdso/Makefile | 2 +
arch/x86/entry/vdso/vdso.lds.S | 1 +
arch/x86/entry/vdso/vsgx_enter_enclave.S | 97 ++++++++++++++++++++++++
arch/x86/include/uapi/asm/sgx.h | 18 +++++
4 files changed, 118 insertions(+)
create mode 100644 arch/x86/entry/vdso/vsgx_enter_enclave.S
diff --git a/arch/x86/entry/vdso/Makefile b/arch/x86/entry/vdso/Makefile
index 34bcf87d358c..fb5b9960b192 100644
--- a/arch/x86/entry/vdso/Makefile
+++ b/arch/x86/entry/vdso/Makefile
@@ -18,6 +18,7 @@ VDSO32-$(CONFIG_IA32_EMULATION) := y
# files to link into the vdso
vobjs-y := vdso-note.o vclock_gettime.o vgetcpu.o
+vobjs-$(VDSO64-y) += vsgx_enter_enclave.o
# files to link into kernel
obj-y += vma.o extable.o
@@ -85,6 +86,7 @@ CFLAGS_REMOVE_vdso-note.o = -pg
CFLAGS_REMOVE_vclock_gettime.o = -pg
CFLAGS_REMOVE_vgetcpu.o = -pg
CFLAGS_REMOVE_vvar.o = -pg
+CFLAGS_REMOVE_vsgx_enter_enclave.o = -pg
#
# X32 processes use x32 vDSO to access 64bit kernel data.
diff --git a/arch/x86/entry/vdso/vdso.lds.S b/arch/x86/entry/vdso/vdso.lds.S
index d3a2dce4cfa9..50952a995a6c 100644
--- a/arch/x86/entry/vdso/vdso.lds.S
+++ b/arch/x86/entry/vdso/vdso.lds.S
@@ -25,6 +25,7 @@ VERSION {
__vdso_getcpu;
time;
__vdso_time;
+ __vdso_sgx_enter_enclave;
local: *;
};
}
diff --git a/arch/x86/entry/vdso/vsgx_enter_enclave.S b/arch/x86/entry/vdso/vsgx_enter_enclave.S
new file mode 100644
index 000000000000..af572adcd8ed
--- /dev/null
+++ b/arch/x86/entry/vdso/vsgx_enter_enclave.S
@@ -0,0 +1,97 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#include <linux/linkage.h>
+#include <asm/export.h>
+#include <asm/errno.h>
+
+#include "extable.h"
+
+#define EX_LEAF 0*8
+#define EX_TRAPNR 0*8+4
+#define EX_ERROR_CODE 0*8+6
+#define EX_ADDRESS 1*8
+
+.code64
+.section .text, "ax"
+
+/**
+ * __vdso_sgx_enter_enclave() - Enter an SGX enclave
+ *
+ * %eax: ENCLU leaf, must be EENTER or ERESUME
+ * %rbx: TCS, must be non-NULL
+ * %rcx: Optional pointer to 'struct sgx_enclave_exception'
+ *
+ * Return:
+ * 0 on a clean entry/exit to/from the enclave
+ * -EINVAL if ENCLU leaf is not allowed or if TCS is NULL
+ * -EFAULT if ENCLU or the enclave faults
+ *
+ * Note that __vdso_sgx_enter_enclave() is not compliant with the x86-64 ABI.
+ * All registers except RSP must be treated as volatile from the caller's
+ * perspective, including but not limited to GPRs, EFLAGS.DF, MXCSR, FCW, etc...
+ * Conversely, the enclave being run must preserve the untrusted RSP and stack.
+ *
+ * __vdso_sgx_enter_enclave(u32 leaf, void *tcs,
+ * struct sgx_enclave_exception *exception_info)
+ * {
+ * if (leaf != SGX_EENTER && leaf != SGX_ERESUME)
+ * return -EINVAL;
+ *
+ * if (!tcs)
+ * return -EINVAL;
+ *
+ * try {
+ * ENCLU[leaf];
+ * } catch (exception) {
+ * if (e)
+ * *e = exception;
+ * return -EFAULT;
+ * }
+ *
+ * return 0;
+ * }
+ */
+ENTRY(__vdso_sgx_enter_enclave)
+ /* EENTER <= leaf <= ERESUME */
+ cmp $0x2, %eax
+ jb bad_input
+
+ cmp $0x3, %eax
+ ja bad_input
+
+ /* TCS must be non-NULL */
+ test %rbx, %rbx
+ je bad_input
+
+ /* Save @exception_info */
+ push %rcx
+
+ /* Load AEP for ENCLU */
+ lea 1f(%rip), %rcx
+1: enclu
+
+ add $0x8, %rsp
+ xor %eax, %eax
+ ret
+
+bad_input:
+ mov $(-EINVAL), %rax
+ ret
+
+.pushsection .fixup, "ax"
+ /* Re-load @exception_info and fill it (if it's non-NULL) */
+2: pop %rcx
+ test %rcx, %rcx
+ je 3f
+
+ mov %eax, EX_LEAF(%rcx)
+ mov %di, EX_TRAPNR(%rcx)
+ mov %si, EX_ERROR_CODE(%rcx)
+ mov %rdx, EX_ADDRESS(%rcx)
+3: mov $(-EFAULT), %rax
+ ret
+.popsection
+
+_ASM_VDSO_EXTABLE_HANDLE(1b, 2b)
+
+ENDPROC(__vdso_sgx_enter_enclave)
diff --git a/arch/x86/include/uapi/asm/sgx.h b/arch/x86/include/uapi/asm/sgx.h
index 150a784db395..76834456ab07 100644
--- a/arch/x86/include/uapi/asm/sgx.h
+++ b/arch/x86/include/uapi/asm/sgx.h
@@ -69,4 +69,22 @@ struct sgx_enclave_set_attribute {
__u64 attribute_fd;
};
+/**
+ * struct sgx_enclave_exception - structure to report exceptions encountered in
+ * __vdso_sgx_enter_enclave()
+ *
+ * @leaf: ENCLU leaf from %rax at time of exception
+ * @trapnr: exception trap number, a.k.a. fault vector
+ * @error_cdde: exception error code
+ * @address: exception address, e.g. CR2 on a #PF
+ * @reserved: reserved for future use
+ */
+struct sgx_enclave_exception {
+ __u32 leaf;
+ __u16 trapnr;
+ __u16 error_code;
+ __u64 address;
+ __u64 reserved[2];
+};
+
#endif /* _UAPI_ASM_X86_SGX_H */
--
2.19.1
Documentation of the features of the Software Guard eXtensions (SGX),
the basic design choices for the core and driver functionality and
the UAPI.
Signed-off-by: Jarkko Sakkinen <[email protected]>
Co-developed-by: Sean Christopherson <[email protected]>
Signed-off-by: Sean Christopherson <[email protected]>
---
Documentation/index.rst | 1 +
Documentation/x86/index.rst | 10 ++
Documentation/x86/sgx.rst | 234 ++++++++++++++++++++++++++++++++++++
3 files changed, 245 insertions(+)
create mode 100644 Documentation/x86/index.rst
create mode 100644 Documentation/x86/sgx.rst
diff --git a/Documentation/index.rst b/Documentation/index.rst
index 80a421cb935e..3511400dc092 100644
--- a/Documentation/index.rst
+++ b/Documentation/index.rst
@@ -102,6 +102,7 @@ implementation.
:maxdepth: 2
sh/index
+ x86/index
Filesystem Documentation
------------------------
diff --git a/Documentation/x86/index.rst b/Documentation/x86/index.rst
new file mode 100644
index 000000000000..f137d7109052
--- /dev/null
+++ b/Documentation/x86/index.rst
@@ -0,0 +1,10 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+======================
+x86 Architecture Guide
+======================
+
+.. toctree::
+ :maxdepth: 2
+
+ sgx
diff --git a/Documentation/x86/sgx.rst b/Documentation/x86/sgx.rst
new file mode 100644
index 000000000000..72c3ea2e8889
--- /dev/null
+++ b/Documentation/x86/sgx.rst
@@ -0,0 +1,234 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==================================
+Intel(R) Software Guard eXtensions
+==================================
+
+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 an 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 to
+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.
+
+An 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 an enclave
+to exit and jump to an address outside the enclave given when the enclave is
+entered by using the leaf instruction ENCLS(EENTER).
+
+Protected memory
+----------------
+
+Enclave Page Cache (EPC)
+ Physical pages used with enclaves that are protected by the CPU from
+ unauthorized access.
+
+Enclave Page Cache Map (EPCM)
+ A database that describes the properties and state of the pages e.g. their
+ permissions or which enclave they belong to.
+
+Memory Encryption Engine (MEE) integrity tree
+ Autonomously updated integrity tree. The root of the tree located in on-die
+ SRAM.
+
+EPC data types
+--------------
+
+SGX Enclave Control Structure (SECS)
+ Describes the global properties of an enclave. Will not be mapped to the
+ ELRANGE.
+
+Regular (REG)
+ These pages contain code and data.
+
+Thread Control Structure (TCS)
+ The pages that define the entry points inside an enclave. An enclave can
+ only be entered through these entry points and each can host a single
+ hardware thread at a time.
+
+Version Array (VA)
+ The pages contain 64-bit version numbers for pages that have been swapped
+ outside the enclave. Each page has the capacity of 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 regenerated 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[SGX_LE_WR]** 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 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). It will check 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 the *ENCLS(EWB)* instruction 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.
+The *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 the *ENCLS(ETRACK)* instruction,
+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 counter value because that means that there might be hardware
+threads 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 writable MSRs. The launch is
+performed by setting the MSRs to the hash of the public key modulus of the
+enclave signer and a token with the valid bit set to zero.
+
+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.
+
+Exception handling
+------------------
+
+The PF_SGX bit is set if and only if the #PF is detected by the SGX Enclave Page
+Cache Map (EPCM). The EPCM is a hardware-managed table that enforces accesses to
+an enclave's EPC pages 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 EPCM is consulted only after an access walks the kernel's page tables, i.e.:
+
+1. the access was allowed by the kernel
+2. the kernel's tables have become less restrictive than the EPCM
+3. the kernel cannot fixup the cause of the fault
+
+Notably, (2) implies that either the kernel has botched the EPC mappings or the
+EPCM has been invalidated (see below). Regardless of why the fault occurred,
+userspace needs to be alerted so that it can take appropriate action, e.g.
+restart the enclave. This is reinforced by (3) as the kernel doesn't really
+have any other reasonable option, i.e. signalling SIGSEGV is actually the least
+severe action possible.
+
+Although the primary purpose of the EPCM is to prevent a malicious or
+compromised kernel from attacking an enclave, e.g. by modifying the enclave's
+page tables, do not WARN on a #PF with PF_SGX set. The SGX architecture
+effectively allows the CPU to invalidate all EPCM entries at will and requires
+that software be prepared to handle an EPCM fault at any time. The architecture
+defines this behavior because the EPCM is encrypted with an ephemeral key that
+isn't exposed to software. As such, the EPCM entries cannot be preserved across
+transitions that result in a new key being used, e.g. CPU power down as part of
+an S3 transition or when a VM is live migrated to a new physical system.
+
+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
+==========
+
+* A Memory Encryption Engine Suitable for General Purpose Processors
+ <https://eprint.iacr.org/2016/204.pdf>
+* System Programming Manual: 39.1.4 Intel® SGX Launch Control Configuration
--
2.19.1
> +/**
> + * __vdso_sgx_enter_enclave() - Enter an SGX enclave
> + *
> + * %eax: ENCLU leaf, must be EENTER or ERESUME
> + * %rbx: TCS, must be non-NULL
> + * %rcx: Optional pointer to 'struct sgx_enclave_exception'
> + *
> + * Return:
> + * 0 on a clean entry/exit to/from the enclave
> + * -EINVAL if ENCLU leaf is not allowed or if TCS is NULL
> + * -EFAULT if ENCLU or the enclave faults
> + *
> + * Note that __vdso_sgx_enter_enclave() is not compliant with the x86-
> 64 ABI.
> + * All registers except RSP must be treated as volatile from the
> +caller's
> + * perspective, including but not limited to GPRs, EFLAGS.DF, MXCSR,
> FCW, etc...
> + * Conversely, the enclave being run must preserve the untrusted RSP
> and stack.
By requiring preservation of RSP at both AEX and EEXIT, this precludes the possibility of using the untrusted stack as temporary storage by enclaves. While that looks reasonable at first glance, I'm afraid it isn't the case in reality. The untrusted stack is inarguably the most convenient way for data exchange between an enclave and its enclosing process, and is in fact being used for that purpose by almost all existing enclaves to date. Given the expectation that this API will be used by all future SGX application, it looks unwise to ban the most convenient and commonly used approach for data exchange.
Given an enclave can touch everything (registers and memory) of the enclosing process, it's reasonable to restrict the enclave by means of "calling convention" to allow the enclosing process to retain its context. And for that purpose, SGX ISA does offer 2 registers (i.e. RSP and RBP) for applications to choose. Instead of preserving RSP, I'd prefer RBP, which will end up with more flexibility in all SGX applications in future.
> + * __vdso_sgx_enter_enclave(u32 leaf, void *tcs,
> + * struct sgx_enclave_exception *exception_info)
> + * {
> + * if (leaf != SGX_EENTER && leaf != SGX_ERESUME)
> + * return -EINVAL;
> + *
> + * if (!tcs)
> + * return -EINVAL;
> + *
> + * try {
> + * ENCLU[leaf];
> + * } catch (exception) {
> + * if (e)
> + * *e = exception;
> + * return -EFAULT;
> + * }
> + *
> + * return 0;
> + * }
> + */
> +ENTRY(__vdso_sgx_enter_enclave)
> + /* EENTER <= leaf <= ERESUME */
> + cmp $0x2, %eax
> + jb bad_input
> +
> + cmp $0x3, %eax
> + ja bad_input
> +
> + /* TCS must be non-NULL */
> + test %rbx, %rbx
> + je bad_input
> +
> + /* Save @exception_info */
> + push %rcx
> +
> + /* Load AEP for ENCLU */
> + lea 1f(%rip), %rcx
> +1: enclu
> +
> + add $0x8, %rsp
> + xor %eax, %eax
> + ret
> +
> +bad_input:
> + mov $(-EINVAL), %rax
> + ret
> +
> +.pushsection .fixup, "ax"
> + /* Re-load @exception_info and fill it (if it's non-NULL) */
> +2: pop %rcx
> + test %rcx, %rcx
> + je 3f
> +
> + mov %eax, EX_LEAF(%rcx)
> + mov %di, EX_TRAPNR(%rcx)
> + mov %si, EX_ERROR_CODE(%rcx)
> + mov %rdx, EX_ADDRESS(%rcx)
> +3: mov $(-EFAULT), %rax
> + ret
> +.popsection
> +
> +_ASM_VDSO_EXTABLE_HANDLE(1b, 2b)
> +
> +ENDPROC(__vdso_sgx_enter_enclave)
Rather than preserving RSP, an alternative that preserves RBP will allow more flexibility inside SGX applications. Below is the assembly code based on that idea, that offers a superset of functionality over the current patch, yet at a cost of just 9 more lines of code (23 LOC here vs. 14 LOC in the patch).
/**
* __vdso_sgx_enter_enclave() - Enter an SGX enclave
*
* %eax: ENCLU leaf, must be either EENTER or ERESUME
* 0x08(%rsp): TCS
* 0x10(%rsp): Optional pointer to 'struct sgx_enclave_exception'
* 0x18(%rsp): Optional function pointer to 'sgx_exit_handler', defined below
* typedef int (*sgx_exit_handler)(struct sgx_enclave_exception *ex_info);
* return: Non-negative integer to indicate success, or a negative error
* code on failure.
*
* Note that __vdso_sgx_enter_enclave() is not compatible with x86_64 ABI.
* All registers except RBP must be treated as volatile from the caller's
* perspective, including but not limited to GPRs, EFLAGS.DF, MXCSR, FCW, etc...
* Enclave may decrement RSP, but must not increment it - i.e. existing content
* of the stack shall be preserved.
*/
__vdso_sgx_enter_enclave:
push %rbp
mov %rsp, %rbp
/* EENTER <= leaf <= ERESUME */
1: cmp $0x2, %eax
jb bad_input
cmp $0x3, %eax
ja bad_leaf
/* Load TCS and AEP */
mov 0x10(%rbp), %rbx
lea 2f(%rip), %rcx
2: enclu
mov 0x18(%rbp), %rcx
jrcxz 3f
/* Besides leaf, this instruction also zeros trapnr and error_code */
mov %rax, EX_LEAF(%rcx)
3: mov %rcx, %rdi
mov 0x20(%rbp), %rcx
jrcxz 4f
call *%rcx
jmp 1b
4: leave
ret
bad_leaf:
cmp $0, %eax
jle 4b
mov $(-EINVAL), %eax
jmp 4b
.pushsection .fixup, "ax"
5: mov 0x18(%rbp), %rcx
jrcxz 6f
mov %eax, EX_LEAF(%rcx)
mov %di, EX_TRAPNR(%rcx)
mov %si, EX_ERROR_CODE(%rcx)
mov %rdx, EX_ADDRESS(%rcx)
6: mov $(-EFAULT), %eax
jmp 3b
.popsection
_ASM_VDSO_EXTABLE_HANDLE(2b, 5b)
On Wed, Mar 20, 2019 at 11:30 AM Xing, Cedric <[email protected]> wrote:
>
> > +/**
> > + * __vdso_sgx_enter_enclave() - Enter an SGX enclave
> > + *
> > + * %eax: ENCLU leaf, must be EENTER or ERESUME
> > + * %rbx: TCS, must be non-NULL
> > + * %rcx: Optional pointer to 'struct sgx_enclave_exception'
> > + *
> > + * Return:
> > + * 0 on a clean entry/exit to/from the enclave
> > + * -EINVAL if ENCLU leaf is not allowed or if TCS is NULL
> > + * -EFAULT if ENCLU or the enclave faults
> > + *
> > + * Note that __vdso_sgx_enter_enclave() is not compliant with the x86-
> > 64 ABI.
> > + * All registers except RSP must be treated as volatile from the
> > +caller's
> > + * perspective, including but not limited to GPRs, EFLAGS.DF, MXCSR,
> > FCW, etc...
> > + * Conversely, the enclave being run must preserve the untrusted RSP
> > and stack.
>
> By requiring preservation of RSP at both AEX and EEXIT, this precludes the possibility of using the untrusted stack as temporary storage by enclaves. While that looks reasonable at first glance, I'm afraid it isn't the case in reality. The untrusted stack is inarguably the most convenient way for data exchange between an enclave and its enclosing process, and is in fact being used for that purpose by almost all existing enclaves to date. Given the expectation that this API will be used by all future SGX application, it looks unwise to ban the most convenient and commonly used approach for data exchange.
I'm going to go out on a limb and say that this is a good thing.
Using the untrusted stack as a way to exchange data is very
convenient, but that doesn't mean it's a good idea. Here are some
problems it causes:
- It prevents using a normal function to wrap enclave entry (as we're
seeing with this patch set).
- It makes quite a few unfortunate assumptions about the layout of
the untrusted stack. It assumes that the untrusted stack is
arbitrarily expandable, which is entirely untrue in languages like Go.
It assumes that the untrusted stack isn't further constrained by
various CFI mechanisms (e.g. CET), and, as of last time I checked, the
interaction between CET and SGX was still not specified. It also
assumes that the untrusted stack doesn't have ABI-imposed layout
restrictions related to unwinding, and, as far as I know, this means
that current enclaves with current enclave runtimes can interact quite
poorly with debuggers, exception handling, and various crash dumping
technologies.
- It will make it quite unpleasant to call into an enclave in a
coroutine depending on how the host untrusted runtime implements
coroutines.
So I think it's a *good* thing if the effect is to make enclave SDKs
change their memory management so that untrusted buffers are
explicitly supplied by the host runtime. Honestly, I would have much
preferred if the architecture did not give the enclave access to RSP
and RBP at all. (And, for that matter, RIP.)
On 2019-03-20 11:30, Xing, Cedric wrote:
>> +/**
>> + * __vdso_sgx_enter_enclave() - Enter an SGX enclave
>> + *
>> + * %eax: ENCLU leaf, must be EENTER or ERESUME
>> + * %rbx: TCS, must be non-NULL
>> + * %rcx: Optional pointer to 'struct sgx_enclave_exception'
>> + *
>> + * Return:
>> + * 0 on a clean entry/exit to/from the enclave
>> + * -EINVAL if ENCLU leaf is not allowed or if TCS is NULL
>> + * -EFAULT if ENCLU or the enclave faults
>> + *
>> + * Note that __vdso_sgx_enter_enclave() is not compliant with the x86-
>> 64 ABI.
>> + * All registers except RSP must be treated as volatile from the
>> +caller's
>> + * perspective, including but not limited to GPRs, EFLAGS.DF, MXCSR,
>> FCW, etc...
>> + * Conversely, the enclave being run must preserve the untrusted RSP
>> and stack.
>
> By requiring preservation of RSP at both AEX and EEXIT, this precludes the possibility of using the untrusted stack as temporary storage by enclaves. While that looks reasonable at first glance, I'm afraid it isn't the case in reality. The untrusted stack is inarguably the most convenient way for data exchange between an enclave and its enclosing process, and is in fact being used for that purpose by almost all existing enclaves to date. Given the expectation that this API will be used by all future SGX application, it looks unwise to ban the most convenient and commonly used approach for data exchange.
For reference, here's the code in the Intel toolchain responsible for
this:
https://github.com/intel/linux-sgx/blob/6a0b5ac71f8d16f04e0376f3b2168e80c773dd23/sdk/trts/trts.cpp#L125-L140
Regarding "almost all existing enclaves to date", enclaves built with
the Fortanix toolchain don't touch the untrusted stack.
--
Jethro Beekman | Fortanix
On Wed, Mar 20, 2019 at 11:30:26AM -0700, Xing, Cedric wrote:
> > +/**
> > + * __vdso_sgx_enter_enclave() - Enter an SGX enclave
> > + *
> > + * %eax: ENCLU leaf, must be EENTER or ERESUME
> > + * %rbx: TCS, must be non-NULL
> > + * %rcx: Optional pointer to 'struct sgx_enclave_exception'
> > + *
> > + * Return:
> > + * 0 on a clean entry/exit to/from the enclave
> > + * -EINVAL if ENCLU leaf is not allowed or if TCS is NULL
> > + * -EFAULT if ENCLU or the enclave faults
> > + *
> > + * Note that __vdso_sgx_enter_enclave() is not compliant with the x86-
> > 64 ABI.
> > + * All registers except RSP must be treated as volatile from the
> > +caller's
> > + * perspective, including but not limited to GPRs, EFLAGS.DF, MXCSR,
> > FCW, etc...
> > + * Conversely, the enclave being run must preserve the untrusted RSP
> > and stack.
>
> By requiring preservation of RSP at both AEX and EEXIT, this precludes
> the possibility of using the untrusted stack as temporary storage by
> enclaves. While that looks reasonable at first glance, I'm afraid it
> isn't the case in reality. The untrusted stack is inarguably the most
> convenient way for data exchange between an enclave and its enclosing
> process,
I vehemently disagree with "inarguably". IMO, passing data via registers
is much more convenient.
Even if you qualify your assertion with "data of arbitrary size unknown
at build time", I still disagree. Using the untrusted stack allows for
trickery when a debugger is involved, other than that I see no advantages
over allocating virtual memory and handing the pointer to the enclave
at launch time. Sure, it requires a few more lines of code to setup,
but it's literally ~20 LoC out of thousands required to sign, build and
launch an enclave, but it doesn't require playing games with the stack.
Not to mention that the entire concept of using the untrusted stack is
based on the assumption that the enclave is making ocalls, e.g. stateless
enclaves or libraries that use a message queue have zero need/benefit
for using the untrusted stack.
> and is in fact being used for that purpose by almost all existing enclaves
> to date.
That's a bit misleading, since almost all existing enclaves are built
against Intel's SDK, which just so happens to unconditionally use the
untrusted stack. It's not like all enclave developers made a concious
decision to use the untrusted stack. If Intel rewrote the SDK to use
a different method then one could argue that the new approach is the
most common method of passing data.
> Given the expectation that this API will be used by all future SGX
> application, it looks unwise to ban the most convenient and commonly
> used approach for data exchange.
>
> Given an enclave can touch everything (registers and memory) of the
> enclosing process, it's reasonable to restrict the enclave by means
> of "calling convention" to allow the enclosing process to retain its
> context. And for that purpose, SGX ISA does offer 2 registers (i.e.
> RSP and RBP) for applications to choose. Instead of preserving RSP,
> I'd prefer RBP, which will end up with more flexibility in all SGX
> applications in future.
I disagree that the SGX ISA intends for applications to choose between
preserving RSP and RBP, e.g. the SDM description of SSA.UR{B,S}P states:
Non-Enclave (outside) {RBP,stack} pointer. Saved by EENTER, restored on AEX.
To me, the "Saved/restored" wording implies that URBP and URSP should
*never* be touched by the enclave. Sure, the proposed vDSO interface
doesn't require RBP to be preserved, but only because the goal was to
deviate from hardware as little as possible, not because anyone wants
to encourage enclaves to muck with RBP.
On Wed, Mar 20, 2019 at 06:20:53PM +0200, Jarkko Sakkinen wrote:
> From: Kai Huang <[email protected]>
>
> X86_FEATURE_SGX reflects whether or not the CPU supports Intel's
> Software Guard eXtensions (SGX).
>
> Signed-off-by: Kai Huang <[email protected]>
> Co-developed-by: Jarkko Sakkinen <[email protected]>
> Signed-off-by: Jarkko Sakkinen <[email protected]>
> Reviewed-by: Borislav Petkov <[email protected]>
> ---
> arch/x86/include/asm/cpufeatures.h | 1 +
> arch/x86/include/asm/disabled-features.h | 8 +++++++-
> 2 files changed, 8 insertions(+), 1 deletion(-)
>
> diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
> index 981ff9479648..a16325db4cff 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 */
> diff --git a/arch/x86/include/asm/disabled-features.h b/arch/x86/include/asm/disabled-features.h
> index a5ea841cc6d2..74de07d0f390 100644
> --- a/arch/x86/include/asm/disabled-features.h
> +++ b/arch/x86/include/asm/disabled-features.h
> @@ -62,6 +62,12 @@
> # define DISABLE_PTI (1 << (X86_FEATURE_PTI & 31))
> #endif
>
> +#ifdef CONFIG_INTEL_SGX
> +# define DISABLE_SGX_CORE 0
> +#else
> +# define DISABLE_SGX_CORE (1 << (X86_FEATURE_SGX & 31))
> +#endif
> +
> /*
> * Make sure to add features to the correct mask
> */
> @@ -74,7 +80,7 @@
> #define DISABLED_MASK6 0
> #define DISABLED_MASK7 (DISABLE_PTI)
> #define DISABLED_MASK8 0
> -#define DISABLED_MASK9 (DISABLE_MPX|DISABLE_SMAP)
> +#define DISABLED_MASK9 (DISABLE_MPX|DISABLE_SMAP|DISABLE_SGX_CORE)
> #define DISABLED_MASK10 0
> #define DISABLED_MASK11 0
> #define DISABLED_MASK12 0
> --
> 2.19.1
>
Just out of curiosity, would it be worthwhile to separate out the cpufeature
patches here to post and integrate them separately? It would at least reduce
the size of this patch set slightly, as these aren't controversial changes
Neil
> Using the untrusted stack as a way to exchange data is very convenient,
> but that doesn't mean it's a good idea. Here are some problems it
> causes:
>
> - It prevents using a normal function to wrap enclave entry (as we're
> seeing with this patch set).
It doesn't prevent. It's all about what's agreed between the enclave and its hosting process. With the optional "exit/exception callback" set to null, this will behave exactly the same as in the current patch. That's what I meant by "flexibility" and "superset of functionality".
>
> - It makes quite a few unfortunate assumptions about the layout of the
> untrusted stack. It assumes that the untrusted stack is arbitrarily
> expandable, which is entirely untrue in languages like Go.
I'm with you that stack is not always good thing, hence I'm NOT ruling out any other approaches for exchanging data. But is stack "bad" enough to be ruled out completely? The point here is flexibility because the stack could be "good" for its convenience. After all, only buffers of "reasonable" sizes will be exchanged in most cases, and in the rare exceptions of stack overflow they'd probably get caught in validation anyway. The point here again is - flexibility. I'd say it's better to leave the choice to the SDK implementers than to force the choice on them.
> It assumes that the untrusted stack isn't further constrained by various
> CFI mechanisms (e.g. CET), and, as of last time I checked, the
> interaction between CET and SGX was still not specified.
I was one of the architects participating in the CET ISA definition. The assembly provided was crafted with CET in mind and will be fully compatible with CET.
> It also
> assumes that the untrusted stack doesn't have ABI-imposed layout
> restrictions related to unwinding, and, as far as I know, this means
> that current enclaves with current enclave runtimes can interact quite
> poorly with debuggers, exception handling, and various crash dumping
> technologies.
Per comments from the patch set, I guess it's been agreed that this vDSO function will NOT be x86_64 ABI compatible. So I'm not sure why stacking unwinding is relevant here. However, I'm with you that we should take debugging/exception handling/reporting/crash dumping into consideration by making this vDSO API x86_64 ABI compatible. IMO it's trivial and the performance overhead in negligible (dwarfed by ENCLU anyway. I'd be more than happy to provide a x86_64 ABI compatible version if that's also your preference.
> - It will make it quite unpleasant to call into an enclave in a
> coroutine depending on how the host untrusted runtime implements
> coroutines.
I'm not sure what you are referring to by "coroutine". But this vDSO API will be (expected to be) the only routine that actually calls into an enclave. Isn't that correct?
>
> So I think it's a *good* thing if the effect is to make enclave SDKs
> change their memory management so that untrusted buffers are explicitly
> supplied by the host runtime.
Intel SGX SDK will change no matter what. The point is flexibility, is to offer choices and let SDK implementers decide, instead of deciding for them ahead of time.
> Honestly, I would have much preferred if
> the architecture did not give the enclave access to RSP and RBP at all.
> (And, for that matter, RIP.)
This reminds me of PUSHA/POPA instructions. We once thought those instruction would be appreciated by compilers but the fact turns out that most compilers prefer a mix of caller-saved/callee-saved GPRs instead of treating all GPRs caller or callee saved. Then when we believed everyone would prefer a mix after so many years, an exception emerged as GO was invented. That said, flexibility is the point and is the most important thing ISA is always trying to offer. The rest is just software convention. So we decided not to enforce RBP/RSP, unless there are security implications - e.g. RIP - EEXIT will be considered an indirect branch instruction and will have to land on an ENDBR once CET comes out.
> > By requiring preservation of RSP at both AEX and EEXIT, this precludes
> the possibility of using the untrusted stack as temporary storage by
> enclaves. While that looks reasonable at first glance, I'm afraid it
> isn't the case in reality. The untrusted stack is inarguably the most
> convenient way for data exchange between an enclave and its enclosing
> process, and is in fact being used for that purpose by almost all
> existing enclaves to date. Given the expectation that this API will be
> used by all future SGX application, it looks unwise to ban the most
> convenient and commonly used approach for data exchange.
>
> For reference, here's the code in the Intel toolchain responsible for
> this:
> https://github.com/intel/linux-
> sgx/blob/6a0b5ac71f8d16f04e0376f3b2168e80c773dd23/sdk/trts/trts.cpp#L125
> -L140
>
> Regarding "almost all existing enclaves to date", enclaves built with
> the Fortanix toolchain don't touch the untrusted stack.
>
> --
> Jethro Beekman | Fortanix
Thanks for providing the references. Yes, not every enclave touches the untrusted stack so I used the word "almost".
Everything exists for a reason. By bringing up what is done today, I was trying to inspire thinking on the more important question of "why is it done this way today?".
> > By requiring preservation of RSP at both AEX and EEXIT, this precludes
> > the possibility of using the untrusted stack as temporary storage by
> > enclaves. While that looks reasonable at first glance, I'm afraid it
> > isn't the case in reality. The untrusted stack is inarguably the most
> > convenient way for data exchange between an enclave and its enclosing
> > process,
>
> I vehemently disagree with "inarguably". IMO, passing data via
> registers is much more convenient.
Which is the most convenient approach is always dependent on data size and/or even how the data is produced/consumed. It's kind of a spectrum and we're just talking in the sense of probability. You are right that "inarguably" is arguable if the buffer is small enough to fit in registers, and the producer/consumer also has access to registers.
>
> Even if you qualify your assertion with "data of arbitrary size unknown
> at build time", I still disagree. Using the untrusted stack allows for
> trickery when a debugger is involved, other than that I see no
> advantages over allocating virtual memory and handing the pointer to the
> enclave at launch time. Sure, it requires a few more lines of code to
> setup, but it's literally ~20 LoC out of thousands required to sign,
> build and launch an enclave, but it doesn't require playing games with
> the stack.
I'm NOT ruling out your approach.
And like you said, the untrusted stack enables certain trickery that helps debugging and also simplifies enclaves (even just a little). Then why are you trying to rule that out? Because of 9 LOC in vDSO?
>
> Not to mention that the entire concept of using the untrusted stack is
> based on the assumption that the enclave is making ocalls, e.g.
> stateless enclaves or libraries that use a message queue have zero
> need/benefit for using the untrusted stack.
Don't get me wrong. I never said enclaves would require untrusted stack to make ocalls, or ocalls would require untrusted stack to make. It's just a generic approach for sharing/exchanging data. Some enclaves my need it, others may not.
My question still remains: why do you want to rule it out?
> > and is in fact being used for that purpose by almost all existing
> > enclaves to date.
>
> That's a bit misleading, since almost all existing enclaves are built
> against Intel's SDK, which just so happens to unconditionally use the
> untrusted stack. It's not like all enclave developers made a concious
> decision to use the untrusted stack. If Intel rewrote the SDK to use a
> different method then one could argue that the new approach is the most
> common method of passing data.
Everything exists for a reason. It's unimportant what has been done. What matters is why that was done in that particular way. I was trying to inspire thinking.
>
> > Given the expectation that this API will be used by all future SGX
> > application, it looks unwise to ban the most convenient and commonly
> > used approach for data exchange.
> >
> > Given an enclave can touch everything (registers and memory) of the
> > enclosing process, it's reasonable to restrict the enclave by means of
> > "calling convention" to allow the enclosing process to retain its
> > context. And for that purpose, SGX ISA does offer 2 registers (i.e.
> > RSP and RBP) for applications to choose. Instead of preserving RSP,
> > I'd prefer RBP, which will end up with more flexibility in all SGX
> > applications in future.
>
> I disagree that the SGX ISA intends for applications to choose between
> preserving RSP and RBP, e.g. the SDM description of SSA.UR{B,S}P states:
>
> Non-Enclave (outside) {RBP,stack} pointer. Saved by EENTER, restored
> on AEX.
>
> To me, the "Saved/restored" wording implies that URBP and URSP should
> *never* be touched by the enclave. Sure, the proposed vDSO interface
> doesn't require RBP to be preserved, but only because the goal was to
> deviate from hardware as little as possible, not because anyone wants to
> encourage enclaves to muck with RBP.
I'm so sorry to tell you that you have misunderstood the SDM. If this is a common misunderstanding, I guess I could talk to the architect responsible for this SDM chapter to see if we could amend the language.
The purpose of restoring RSP is because software needs a stack to handle exception. Well, that's not 100% accurate because it's a user mode stack. Anyway, it tells the used part from the unused space in the stack. RBP on the other hand is NEVER required from interrupt/exception handling perspective, but we decided to add it because we'd like to offer a choice, just like I said earlier. The calling thread could then anchor its frame on either RSP or RBP.
On Wed, Mar 20, 2019 at 12:57:52PM -0700, Xing, Cedric wrote:
> > Using the untrusted stack as a way to exchange data is very convenient,
> > but that doesn't mean it's a good idea. Here are some problems it
> > causes:
> >
> > - It prevents using a normal function to wrap enclave entry (as we're
> > seeing with this patch set).
>
> It doesn't prevent.
Yes it does, keyword being "normal". Though I guess we could do a bit of
bikeshedding on the definition of normal...
> > - It makes quite a few unfortunate assumptions about the layout of the
> > untrusted stack. It assumes that the untrusted stack is arbitrarily
> > expandable, which is entirely untrue in languages like Go.
>
> I'm with you that stack is not always good thing, hence I'm NOT ruling
> out any other approaches for exchanging data. But is stack "bad" enough
> to be ruled out completely? The point here is flexibility because the
> stack could be "good" for its convenience. After all, only buffers of
> "reasonable" sizes will be exchanged in most cases, and in the rare
> exceptions of stack overflow they'd probably get caught in validation
> anyway. The point here again is - flexibility. I'd say it's better to
> leave the choice to the SDK implementers than to force the choice on them.
Actually, this series doesn't force anything on Intel's SDK, as there is
nothing in the documentation that states the vDSO *must* be used to enter
enclaves. In other words, unless it's expressly forbidden, applications
are free to enter enclaves directly and do as they wish with the untrusted
stack. The catch being that any such usage will need to deal with enclave
exceptions being delivered as signals, i.e. the vDSO implementation is a
carrot, not a stick.
AIUI, excepting libraries that want to manipulate the untrusted stack,
there is a general consensus that the proposed vDSO implementation is the
right approach, e.g. full x86_64 ABI compatibility was explored in the
past and was deemed to add unnecessary complexity and overhead.
The vDSO *could* be written in such a way that it supports preserving RBP
or RSP, but for all intents and purposes such an implementation would yield
two distinct ABIs that just happen to be implemented in a single function.
And *if* we want to deal with maintaining two ABIs, supporting the kernel's
existing signal ABI is a lot cleaner (from a kernel perspective) than
polluting the vDSO.
In other words, if there is a desire to support enclaves which modify
the untrusted stack, and it sounds like there is, then IMO our time is
better spent discussing whether or not to officially support the signal
ABI for enclaves.
> > It assumes that the untrusted stack isn't further constrained by various
> > CFI mechanisms (e.g. CET), and, as of last time I checked, the
> > interaction between CET and SGX was still not specified.
>
> I was one of the architects participating in the CET ISA definition.
> The assembly provided was crafted with CET in mind and will be fully
> compatible with CET.
>
> > It also
> > assumes that the untrusted stack doesn't have ABI-imposed layout
> > restrictions related to unwinding, and, as far as I know, this means
> > that current enclaves with current enclave runtimes can interact quite
> > poorly with debuggers, exception handling, and various crash dumping
> > technologies.
>
> Per comments from the patch set, I guess it's been agreed that this
> vDSO function will NOT be x86_64 ABI compatible. So I'm not sure why
> stacking unwinding is relevant here.
I think Andy's point is that a single PUSH (to save %rcx) won't break
unwinding, etc..., but unwinders and whantot will have a rough go of it
if %rsp points at complete garbage.
> However, I'm with you that we
> should take debugging/exception handling/reporting/crash dumping into
> consideration by making this vDSO API x86_64 ABI compatible. IMO it's
> trivial and the performance overhead in negligible (dwarfed by ENCLU
> anyway. I'd be more than happy to provide a x86_64 ABI compatible
> version if that's also your preference.
It's not just the performance cost, making __vdso_sgx_enter_enclave()
compatible with the x86_64 ABI adds complexity to both its code and its
documentation, e.g. to describe how data is marshalled to/from enclaves.
> On Wed, Mar 20, 2019 at 12:57:52PM -0700, Xing, Cedric wrote:
> > > Using the untrusted stack as a way to exchange data is very
> > > convenient, but that doesn't mean it's a good idea. Here are some
> > > problems it
> > > causes:
> > >
> > > - It prevents using a normal function to wrap enclave entry (as
> > > we're seeing with this patch set).
> >
> > It doesn't prevent.
>
> Yes it does, keyword being "normal". Though I guess we could do a bit
> of bikeshedding on the definition of normal...
I don't understand what you mean by "normal". As I said, I tend to have a x86_64 ABI compliant version and by saying that I mean it'd be a 100% "normal" function callable from C. And the version I provided in this thread is a trimmed down version that doesn't preserve any registers except RSP/RBP so a C wrapper will be necessary. Other than that I'm not aware of any anomalies. Could you elaborate on what "abnormal" operations necessary to invoke this vDSO under what circumstances? And it'll be very helpful if you could present a "normal" function to demonstrate how your code could work with it while mine couldn't.
> Actually, this series doesn't force anything on Intel's SDK, as there is
> nothing in the documentation that states the vDSO *must* be used to
> enter enclaves. In other words, unless it's expressly forbidden,
> applications are free to enter enclaves directly and do as they wish
> with the untrusted stack. The catch being that any such usage will need
> to deal with enclave exceptions being delivered as signals, i.e. the
> vDSO implementation is a carrot, not a stick.
If you want to bike-shedding on *must*, well, no one *must* use *anything* from the *anyone*! But is that the expectation? Or if you don't expect your API to be used then what are you doing here?
Intel SDK doesn't have to use this API. But we (the SDK team) are truly willing to use this API because we share the same concern with you over signals and would like to move to something better.
>
> AIUI, excepting libraries that want to manipulate the untrusted stack,
> there is a general consensus that the proposed vDSO implementation is
> the right approach, e.g. full x86_64 ABI compatibility was explored in
> the past and was deemed to add unnecessary complexity and overhead.
>
> The vDSO *could* be written in such a way that it supports preserving
> RBP or RSP, but for all intents and purposes such an implementation
> would yield two distinct ABIs that just happen to be implemented in a
> single function.
> And *if* we want to deal with maintaining two ABIs, supporting the
> kernel's existing signal ABI is a lot cleaner (from a kernel perspective)
> than polluting the vDSO.
Disagreed! What I'm proposing is one ABI - enclave preserves RBP! No requirements on RSP. Of course RSP is still interpreted as the line between vacant and used parts of the stack, or nothing will work regardless the proposal.
The hosting process may have an agreement with the enclave to preserve RSP. But that would be completely between them, and would be just a coincidence instead of a consequence of the ABI from the perspective of this vDSO API.
>
> In other words, if there is a desire to support enclaves which modify
> the untrusted stack, and it sounds like there is, then IMO our time is
> better spent discussing whether or not to officially support the signal
> ABI for enclaves.
Disagreed! We share the same concern over signals so let's work this out!
>
> > > It assumes that the untrusted stack isn't further constrained by
> > > various CFI mechanisms (e.g. CET), and, as of last time I checked,
> > > the interaction between CET and SGX was still not specified.
> >
> > I was one of the architects participating in the CET ISA definition.
> > The assembly provided was crafted with CET in mind and will be fully
> > compatible with CET.
> >
> > > It also
> > > assumes that the untrusted stack doesn't have ABI-imposed layout
> > > restrictions related to unwinding, and, as far as I know, this means
> > > that current enclaves with current enclave runtimes can interact
> > > quite poorly with debuggers, exception handling, and various crash
> > > dumping technologies.
> >
> > Per comments from the patch set, I guess it's been agreed that this
> > vDSO function will NOT be x86_64 ABI compatible. So I'm not sure why
> > stacking unwinding is relevant here.
>
> I think Andy's point is that a single PUSH (to save %rcx) won't break
> unwinding, etc..., but unwinders and whantot will have a rough go of it
> if %rsp points at complete garbage.
The unwanders use CFI directives to determine frame pointers. RSP is the frame point by default but could be easily changed - e.g. by ".cfi_def_cfa_register %rbp". I could add proper CFI directives if so desired. I omitted them because you omitted them too in your code (in case you don't know, CFI directives are needed around push/pop %rcx in your code to stay compliant with ELF/DWARF spec), and I didn't want to confuse those who didn't understand CFI.
>
> > However, I'm with you that we
> > should take debugging/exception handling/reporting/crash dumping into
> > consideration by making this vDSO API x86_64 ABI compatible. IMO it's
> > trivial and the performance overhead in negligible (dwarfed by ENCLU
> > anyway. I'd be more than happy to provide a x86_64 ABI compatible
> > version if that's also your preference.
>
> It's not just the performance cost, making __vdso_sgx_enter_enclave()
> compatible with the x86_64 ABI adds complexity to both its code and its
> documentation, e.g. to describe how data is marshalled to/from enclaves.
Well, technically speaking an ABI compliant API should *reduce* documentation. But given how easy it is to write a C compliant wrapper, I think a custom ABI is perfectly fine.
On Wed, Mar 20, 2019 at 03:41:53PM -0400, Neil Horman wrote:
> On Wed, Mar 20, 2019 at 06:20:53PM +0200, Jarkko Sakkinen wrote:
> > From: Kai Huang <[email protected]>
> >
> > X86_FEATURE_SGX reflects whether or not the CPU supports Intel's
> > Software Guard eXtensions (SGX).
> >
> > Signed-off-by: Kai Huang <[email protected]>
> > Co-developed-by: Jarkko Sakkinen <[email protected]>
> > Signed-off-by: Jarkko Sakkinen <[email protected]>
> > Reviewed-by: Borislav Petkov <[email protected]>
> > ---
> > arch/x86/include/asm/cpufeatures.h | 1 +
> > arch/x86/include/asm/disabled-features.h | 8 +++++++-
> > 2 files changed, 8 insertions(+), 1 deletion(-)
> >
> > diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
> > index 981ff9479648..a16325db4cff 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 */
> > diff --git a/arch/x86/include/asm/disabled-features.h b/arch/x86/include/asm/disabled-features.h
> > index a5ea841cc6d2..74de07d0f390 100644
> > --- a/arch/x86/include/asm/disabled-features.h
> > +++ b/arch/x86/include/asm/disabled-features.h
> > @@ -62,6 +62,12 @@
> > # define DISABLE_PTI (1 << (X86_FEATURE_PTI & 31))
> > #endif
> >
> > +#ifdef CONFIG_INTEL_SGX
> > +# define DISABLE_SGX_CORE 0
> > +#else
> > +# define DISABLE_SGX_CORE (1 << (X86_FEATURE_SGX & 31))
> > +#endif
> > +
> > /*
> > * Make sure to add features to the correct mask
> > */
> > @@ -74,7 +80,7 @@
> > #define DISABLED_MASK6 0
> > #define DISABLED_MASK7 (DISABLE_PTI)
> > #define DISABLED_MASK8 0
> > -#define DISABLED_MASK9 (DISABLE_MPX|DISABLE_SMAP)
> > +#define DISABLED_MASK9 (DISABLE_MPX|DISABLE_SMAP|DISABLE_SGX_CORE)
> > #define DISABLED_MASK10 0
> > #define DISABLED_MASK11 0
> > #define DISABLED_MASK12 0
> > --
> > 2.19.1
> >
> Just out of curiosity, would it be worthwhile to separate out the cpufeature
> patches here to post and integrate them separately? It would at least reduce
> the size of this patch set slightly, as these aren't controversial changes
I can prepare such patch set if that is what people want. I also see a
benefit of single patch though that you can git am and try out. Most of
the changes do not play much of a role without the functionality.
/Jarkko
On Wed, Mar 20, 2019 at 12:57 PM Xing, Cedric <[email protected]> wrote:
>
> > Using the untrusted stack as a way to exchange data is very convenient,
> > but that doesn't mean it's a good idea. Here are some problems it
> > causes:
> >
> > - It prevents using a normal function to wrap enclave entry (as we're
> > seeing with this patch set).
>
> It doesn't prevent. It's all about what's agreed between the enclave and its hosting process. With the optional "exit/exception callback" set to null, this will behave exactly the same as in the current patch. That's what I meant by "flexibility" and "superset of functionality".
>
> >
> > - It makes quite a few unfortunate assumptions about the layout of the
> > untrusted stack. It assumes that the untrusted stack is arbitrarily
> > expandable, which is entirely untrue in languages like Go.
>
> I'm with you that stack is not always good thing, hence I'm NOT ruling out any other approaches for exchanging data. But is stack "bad" enough to be ruled out completely? The point here is flexibility because the stack could be "good" for its convenience. After all, only buffers of "reasonable" sizes will be exchanged in most cases, and in the rare exceptions of stack overflow they'd probably get caught in validation anyway. The point here again is - flexibility. I'd say it's better to leave the choice to the SDK implementers than to force the choice on them.
>
> > It assumes that the untrusted stack isn't further constrained by various
> > CFI mechanisms (e.g. CET), and, as of last time I checked, the
> > interaction between CET and SGX was still not specified.
>
> I was one of the architects participating in the CET ISA definition. The assembly provided was crafted with CET in mind and will be fully compatible with CET.
>
> > It also
> > assumes that the untrusted stack doesn't have ABI-imposed layout
> > restrictions related to unwinding, and, as far as I know, this means
> > that current enclaves with current enclave runtimes can interact quite
> > poorly with debuggers, exception handling, and various crash dumping
> > technologies.
>
> Per comments from the patch set, I guess it's been agreed that this vDSO function will NOT be x86_64 ABI compatible. So I'm not sure why stacking unwinding is relevant here. However, I'm with you that we should take debugging/exception handling/reporting/crash dumping into consideration by making this vDSO API x86_64 ABI compatible. IMO it's trivial and the performance overhead in negligible (dwarfed by ENCLU anyway. I'd be more than happy to provide a x86_64 ABI compatible version if that's also your preference.
>
> > - It will make it quite unpleasant to call into an enclave in a
> > coroutine depending on how the host untrusted runtime implements
> > coroutines.
>
> I'm not sure what you are referring to by "coroutine". But this vDSO API will be (expected to be) the only routine that actually calls into an enclave. Isn't that correct?
I mean use in languages and runtimes that allow a function and its
callees to pause and then resume later. Something like (pseudocode,
obviously):
void invoke_the_enclave()
{
do_eenter_through_vdso();
}
void some_ocall_handler(void *ptr)
{
yield;
}
If the enclave has ptr pointing to the untrusted stack, then this gets
quite awkward for the runtime to handle efficiently. IMO a much nicer
approach would be:
void invoke_the_enclave()
{
char buffer[1024];
while (true)
{
eenter (through vdso);
if (exit was an ocall request) {
some_ocall_handler(buffer);
}
}
}
And now there is nothing funny happening behind the runtime's back
when some_ocall_handler tries to yield.
Hi Andy,
> > > - It will make it quite unpleasant to call into an enclave in a
> > > coroutine depending on how the host untrusted runtime implements
> > > coroutines.
> >
> > I'm not sure what you are referring to by "coroutine". But this vDSO
> API will be (expected to be) the only routine that actually calls into
> an enclave. Isn't that correct?
>
> I mean use in languages and runtimes that allow a function and its
> callees to pause and then resume later. Something like (pseudocode,
> obviously):
>
> void invoke_the_enclave()
> {
> do_eenter_through_vdso();
> }
>
> void some_ocall_handler(void *ptr)
> {
> yield;
> }
Thank you very much for your detailed explanation. This looks more about whether or not the untrusted stack will remain valid after EEXIT, than whether the ocall will be paused or not. As in your example code above, a problem may occur if "yield" destroys the stack of its caller. But is that a common behavior of "yield" (or any scheduler at all)?
Your point is well received though - Not every enclave can/shall assume existence or size of an untrusted stack. Therefore I've made sure my proposal will work no matter the enclave touches the untrusted stack or not.
>
> If the enclave has ptr pointing to the untrusted stack, then this gets
> quite awkward for the runtime to handle efficiently. IMO a much nicer
> approach would be:
>
> void invoke_the_enclave()
> {
> char buffer[1024];
> while (true)
> {
> eenter (through vdso);
> if (exit was an ocall request) {
> some_ocall_handler(buffer);
> }
> }
> }
>
> And now there is nothing funny happening behind the runtime's back when
> some_ocall_handler tries to yield.
Agreed.
In fact, Mr. Christopherson's API could be implemented using mine as a subroutine (please see below). So your "nicer approach" will continue to work as long as it works with current patch. However, please keep in mind that your "nicer" approach doesn't have to be the "only" approach.
The code snippet below shows an equivalent implementation of Mr. Christopherson's API using mine as a subroutine, except that RBP cannot be used as a parameter to the enclave because it will be overwritten before EENTER. To distinguish his API and mine, they are renamed to __vdso_sgx_enter_enclave_Christopherson and __vdso_sgx_enter_enclave_Xing, respectively.
__vdso_sgx_enter_enclave_Christopherson:
push $0 /* No "exit callback" provided */
push %rcx /* Optional pointer to 'struct sgx_enclave_exception' */
push %rbx /* TCS */
call __vdso_sgx_enter_enclave_Xing
add $24, %rsp
ret
Thanks!
-Cedric
On Wed, Mar 20, 2019 at 05:17:53PM -0700, Xing, Cedric wrote:
> > On Wed, Mar 20, 2019 at 12:57:52PM -0700, Xing, Cedric wrote:
> > > > Using the untrusted stack as a way to exchange data is very
> > > > convenient, but that doesn't mean it's a good idea. Here are some
> > > > problems it
> > > > causes:
> > > >
> > > > - It prevents using a normal function to wrap enclave entry (as
> > > > we're seeing with this patch set).
> > >
> > > It doesn't prevent.
> >
> > Yes it does, keyword being "normal". Though I guess we could do a bit
> > of bikeshedding on the definition of normal...
>
> I don't understand what you mean by "normal".
My apologies, I missed the code snippet in your original code, I was
thinking of a previous idea involving a JMP %rbp. Please ignore this
bit of discussion.
On Wed, Mar 20, 2019 at 01:38:04PM -0700, Xing, Cedric wrote:
> > > By requiring preservation of RSP at both AEX and EEXIT, this precludes
> > > the possibility of using the untrusted stack as temporary storage by
> > > enclaves. While that looks reasonable at first glance, I'm afraid it
> > > isn't the case in reality. The untrusted stack is inarguably the most
> > > convenient way for data exchange between an enclave and its enclosing
> > > process,
> >
> > I vehemently disagree with "inarguably". IMO, passing data via
> > registers is much more convenient.
>
> Which is the most convenient approach is always dependent on data size and/or even how the data is produced/consumed. It's kind of a spectrum and we're just talking in the sense of probability. You are right that "inarguably" is arguable if the buffer is small enough to fit in registers, and the producer/consumer also has access to registers.
>
> >
> > Even if you qualify your assertion with "data of arbitrary size unknown
> > at build time", I still disagree. Using the untrusted stack allows for
> > trickery when a debugger is involved, other than that I see no
> > advantages over allocating virtual memory and handing the pointer to the
> > enclave at launch time. Sure, it requires a few more lines of code to
> > setup, but it's literally ~20 LoC out of thousands required to sign,
> > build and launch an enclave, but it doesn't require playing games with
> > the stack.
>
> I'm NOT ruling out your approach.
>
> And like you said, the untrusted stack enables certain trickery that helps
> debugging and also simplifies enclaves (even just a little). Then why are
> you trying to rule that out? Because of 9 LOC in vDSO?
Although its just 9 LOC, consider its impact on someone who is looking at
the kernel's SGX support for the first time. Questions they may have when
looking at the vDSO code/documentation:
- What's an exit handler?
- Why is an exit handler optional? Don't I always want to handle exits?
- What value should my exit handler return?
- What should my exit handler do if it detects an error?
- Why would I want to preserve %rbp and not %rsp?
- Isn't it insecure to use the untrusted stack in my enclave?
AFAIK, the only reason to preserve %rbp instead of %rsp, i.e. support an
"exit handler" callback, is to be able to implement an o-call scheme using
the untrusted stack to pass data. Every idea I came up with for using the
callback, e.g. logging, handling stack corruptiong, testing hooks, etc...
was at worst no more difficult to implement when using a barebones vDSO.
So, given the choice between a) documenting and maintaining all the baggage
that comes with the exit handler and b) saying "go use signals", I chose
option b.
Hi Sean,
> Although its just 9 LOC, consider its impact on someone who is looking
> at
> the kernel's SGX support for the first time. Questions they may have
> when
> looking at the vDSO code/documentation:
>
> - What's an exit handler?
> - Why is an exit handler optional? Don't I always want to handle
> exits?
> - What value should my exit handler return?
> - What should my exit handler do if it detects an error?
> - Why would I want to preserve %rbp and not %rsp?
> - Isn't it insecure to use the untrusted stack in my enclave?
>
> AFAIK, the only reason to preserve %rbp instead of %rsp, i.e. support an
> "exit handler" callback, is to be able to implement an o-call scheme
> using
> the untrusted stack to pass data. Every idea I came up with for using
> the
> callback, e.g. logging, handling stack corruptiong, testing hooks,
> etc...
> was at worst no more difficult to implement when using a barebones vDSO.
>
> So, given the choice between a) documenting and maintaining all the
> baggage
> that comes with the exit handler and b) saying "go use signals", I chose
> option b.
Disagreed!
This API is NOT even x86_64 compatible and NOT intended to be used by average developers. Instead, this API will be used by SGX SDK vendors who have all the needed background/expertise. And flexibility is way more important to them than reduced documentation. Just imagine how much one needs to read to understand how SGX works, do you really think a function comprised of 20 or so LOC will be a big deal?
Anyway, the documentation needed IMO will not exceed even 1 page, which will be way shorter than most of docs in kernel source tree. I'll be more than happy to help you out if that's out of your competence!
Regarding maintenance, I see an API may require maintenance for 2 possible categories of reasons: 1) its interface cannot satisfy emerging applications; or 2) the infrastructure it relies on has changed. Generally speaking, a more generic API with less assumption/dependence on other components will impose lower maintenance cost in the long run. Comparing our proposals, they share the same dependences (i.e. SGX ISA and vDSO extable) but mine is more generic (as yours could be implemented using mine as a subroutine). Thus, I bet your proposal will impose higher maintenance cost in the long run.
-Cedric
> On Mar 23, 2019, at 10:36 AM, Xing, Cedric <[email protected]> wrote:
>
> Hi Sean,
>
>> Although its just 9 LOC, consider its impact on someone who is looking
>> at
>> the kernel's SGX support for the first time. Questions they may have
>> when
>> looking at the vDSO code/documentation:
>>
>> - What's an exit handler?
>> - Why is an exit handler optional? Don't I always want to handle
>> exits?
>> - What value should my exit handler return?
>> - What should my exit handler do if it detects an error?
>> - Why would I want to preserve %rbp and not %rsp?
>> - Isn't it insecure to use the untrusted stack in my enclave?
>>
>> AFAIK, the only reason to preserve %rbp instead of %rsp, i.e. support an
>> "exit handler" callback, is to be able to implement an o-call scheme
>> using
>> the untrusted stack to pass data. Every idea I came up with for using
>> the
>> callback, e.g. logging, handling stack corruptiong, testing hooks,
>> etc...
>> was at worst no more difficult to implement when using a barebones vDSO.
>>
>> So, given the choice between a) documenting and maintaining all the
>> baggage
>> that comes with the exit handler and b) saying "go use signals", I chose
>> option b.
>
> Disagreed!
>
> This API is NOT even x86_64 compatible and NOT intended to be used by average developers. Instead, this API will be used by SGX SDK vendors who have all the needed background/expertise. And flexibility is way more important to them than reduced documentation. Just imagine how much one needs to read to understand how SGX works, do you really think a function comprised of 20 or so LOC will be a big deal?
>
> Anyway, the documentation needed IMO will not exceed even 1 page, which will be way shorter than most of docs in kernel source tree. I'll be more than happy to help you out if that's out of your competence!
>
> Regarding maintenance, I see an API may require maintenance for 2 possible categories of reasons: 1) its interface cannot satisfy emerging applications; or 2) the infrastructure it relies on has changed. Generally speaking, a more generic API with less assumption/dependence on other components will impose lower maintenance cost in the long run. Comparing our proposals, they share the same dependences (i.e. SGX ISA and vDSO extable) but mine is more generic (as yours could be implemented using mine as a subroutine). Thus, I bet your proposal will impose higher maintenance cost in the long run.
>
>
I’m going to put my vDSO maintainer hat on for a minute. Cedric, your
proposal has the following issues related specifically to the vDSO:
It inherently contains indirect branches. This means that, on
retpoline configurations, it probably needs to use retpolines. This
is doable, but it’s nasty, and you need to worry about register
clobbers.
It uses effectively unbounded stack space. The vDSO timing functions
are already a problem for Go, and this is worse.
And with my vDSO hat back off, I find it disappointing that SGX SDKs
seem willing to couple the SGX enclaves so tightly to their host ABIs.
An *unmodified* SGX enclave should be able to run, without excessive
annoyance, in a Windows process, a Linux process, a C process, a Java
process, a Go process, and pretty much any other process. Saying
“I’ll just recompile it” is a bad solution — for enclaves that use
MRENCLAVE, you can’t, and for enclaves that use MRSIGNER, you need to
deal with the fact the protecting the signing key is a big deal.
Someone should be able to port the entire host program to a different
language without losing secrets and without access to a signing key.
Cedric, your proposal allows an enclave to muck with RSP, but not in a
way that’s particularly pleasant. Since the ISA is set in stone, we
can’t do anything about the enclave’s access to its caller’s
registers. I would love to see a straightforward way to run an
enclave such that it does not access the main untrusted stack at all —
uRSP and uRBP should be arbitrary values passed in the untrusted code,
and the values the enclave sets should be relayed back to the caller
but otherwise not have any effect. Sadly I see no way to do this
short of using GSBASE to store the real untrusted stack pointer.
Other than the segment bases, there appear to be literally zero
untrusted registers that are reliably preserved across an enclave
entry and exit. I suppose we should use a syscall to help.
Since the above tricks seem unlikely to make it into the kernel, I
think we’re doing everyone a favor if the Linux APIs strongly
encourage SDK authors to build enclaves in a way that they don’t make
problematic assumptions about the untrusted world. I would really like
to see enclaves generated by the Linux SDK work on Windows and vice
versa.
Hi Andy,
Thank you for your valuable feedbacks!
Per what you have been saying, your feedbacks come from different angles - i.e. functionality vs. security, but they are mixed up somehow. As an effort to make the discussion more constructive going forward, I'd like you to acknowledge that, in terms of functionality, my proposal is a superset of the current patch, as I have proven by implementing Sean's API using mine as a subroutine. That said, if you are satisfied with his, you should be satisfied with mine as well, from functional perspective. And because of that, I'll try to interpret/address your concerns from security perspective unless otherwise noted. I'm aware that there's still subtle difference between Sean's API and mine - e.g. my proposal consumes 24 bytes more stack space (for the same functionality, i.e. exit callback is null) than his, due to the additional parameters. But I don't believe that would become a "make it or break it" situation in practice.
> I’m going to put my vDSO maintainer hat on for a minute. Cedric, your
> proposal has the following issues related specifically to the vDSO:
>
> It inherently contains indirect branches. This means that, on retpoline
> configurations, it probably needs to use retpolines. This is doable,
> but it’s nasty, and you need to worry about register clobbers.
Only the weakest link matters in security. With dynamic linking in use, this additional indirect CALL can't make things worse. But I'm open to, and in fact also willing to, apply whatever mitigation that you think is satisfactory (or that has been applied to other indirect branches, such as in PLT), such as retpoline. Btw, don't worry about register clobbers because we have at least %rax at our disposal.
>
> It uses effectively unbounded stack space. The vDSO timing functions are
> already a problem for Go, and this is worse.
If targeting the same functionality (i.e. no exit callback), my API uses exactly 24 bytes more than Sean's. Is it really the case that those 24 bytes will break Go?
>
> And with my vDSO hat back off, I find it disappointing that SGX SDKs
> seem willing to couple the SGX enclaves so tightly to their host ABIs.
> An *unmodified* SGX enclave should be able to run, without excessive
> annoyance, in a Windows process, a Linux process, a C process, a Java
> process, a Go process, and pretty much any other process. Saying “I’ll
> just recompile it” is a bad solution — for enclaves that use MRENCLAVE,
> you can’t, and for enclaves that use MRSIGNER, you need to deal with the
> fact the protecting the signing key is a big deal.
> Someone should be able to port the entire host program to a different
> language without losing secrets and without access to a signing key.
I'm not sure which SGX SDKs you are referring to. But for Intel SGX SDK, we defined our own ABI that is consistent between Windows and Linux - i.e. there's no technical problem to load on Windows an enclave built on Linux or vice versa. In terms of what programming languages they can work with, I have to say it was designed exclusively for C/C++. Fortunately, there's usually a "native" interface (e.g. JNI, cgo, etc.) supported by a language runtime so it hasn't been a roadblock so far. Alternatively, the enclave vendor could ship an enclave along with an "interface" shared object that encapsulates all of the marshaling specifics, then the combination of that enclave and its "interface" shared object may be able to work "universally", which should be close to what you want.
The idea we had, when Intel SGX SDK was designed, was that different SDKs would be developed for different languages to take advantage of specific language features. That is similar to different programming languages were invented to target different usages. As we all know, every programming language has both advantages and disadvantages, hence no single language dominates. And that same idea applies to SGX SDKs. If there existed an SDK that worked with everything, probably it wouldn't work well with anything.
>
> Cedric, your proposal allows an enclave to muck with RSP, but not in a
> way that’s particularly pleasant.
From security perspective, it is SGX ISA, but NOT any particular ABI, that allows enclaves "to muck with RSP".
> Since the ISA is set in stone, we
> can’t do anything about the enclave’s access to its caller’s registers.
> I would love to see a straightforward way to run an enclave such that it
> does not access the main untrusted stack at all — uRSP and uRBP should
> be arbitrary values passed in the untrusted code, and the values the
> enclave sets should be relayed back to the caller but otherwise not have
> any effect. Sadly I see no way to do this short of using GSBASE to
> store the real untrusted stack pointer.
I understand your sadness. You are "hoping" SGX to be a sandbox technology (i.e. to prevent enclave from reaching out into the host) but that wasn't the security objective when SGX was defined.
Anyway, SGX is what it is. A restrictive ABI only takes away flexibilities from "good" enclaves but can NEVER restrict malicious ones, so Sean's ABI cannot offer what you want.
> Other than the segment bases, there appear to be literally zero
> untrusted registers that are reliably preserved across an enclave entry
> and exit. I suppose we should use a syscall to help.
The good news is with CET, there are viable solutions to implement bi-directional protection as you would hope. You are more than welcome to ask me offline for more details.
>
> Since the above tricks seem unlikely to make it into the kernel, I think
> we’re doing everyone a favor if the Linux APIs strongly encourage SDK
> authors to build enclaves in a way that they don’t make problematic
> assumptions about the untrusted world. I would really like to see
> enclaves generated by the Linux SDK work on Windows and vice versa.
As said in my previous email, this vDSO API isn't even compliant to x86_64 ABI and is absolutely NOT for average developers. Instead, host/enclave communications are expected to be handled by SDKs and those developers will be very aware of the limitations of their targeted environments, and will need the freedom to deploy optimal solutions.
I understand your intention to advocate the programming model that you believe is "right". But there are 7 billion people on this planet and the "right" thing for you could be "wrong" for others, especially in future usages/situations that can't be foreseen today. Software is stacked, with the lower layers being more generic and higher layers being more specific. This vDSO API is sitting at the bottom of the stack, therefore shall be as generic as possible. A better approach to advocate your idea is to wrap it (i.e. to implement it using the more generic vDSO API as a subroutine) in a library for the public to choose (and you can imagine others bearing different ideas will do the same). Then good ideas will stand out!
-Cedric
On Sun, Mar 24, 2019 at 01:59:48AM -0700, Xing, Cedric wrote:
> As said in my previous email, this vDSO API isn't even compliant to
> x86_64 ABI and is absolutely NOT for average developers. Instead,
> host/enclave communications are expected to be handled by SDKs and
> those developers will be very aware of the limitations of their targeted
> environments, and will need the freedom to deploy optimal solutions.
This statement epitomizes the difference in philosophies between Intel's
SGX SDK and much of the Linux community. The SDK, from edger8r to its
stack stitching, believes that it should dumb things down for the so
called "average" developer, even if doing so significantly increases the
complexity of the implementation. Linux generally favors the opposite
approach, preferring simplicitly in its implementations even if it means
setting a higher bar for the "average" developer/user.
That's not to say that that Linux doesn't have its fair share of complex
code or dumbed down interfaces, far from it. But generally speaking,
there is an emphasis on making the code itself approachable that I don't
see in Intel's SDK.
> I understand your intention to advocate the programming model that you
> believe is "right". But there are 7 billion people on this planet and
> the "right" thing for you could be "wrong" for others, especially in
> future usages/situations that can't be foreseen today.
IMO modifying SSA.U_R{B,SP} from within the enclave is akin to modifying
VMCS fields from the guest, which is technically possible on processors
that don't support VMCS caching, but I digress...
The above statements aren't intended to fan the flames or send us down
a rat hole of philosophical arguments. My intent is to point out that
I think we're at an impasse due to philosophical differences, i.e. no
amount of arguing will convince me that mucking with the untrusted stack
is anything short of insanity, and vice versa I doubt that Andy, I or
anyone else will convince you and the SDK team that forcing the SDK to
use an alternative form of enclave communication is a good thing.
> Software is stacked, with the lower layers being more generic and higher
> layers being more specific. This vDSO API is sitting at the bottom of
> the stack, therefore shall be as generic as possible. A better approach
> to advocate your idea is to wrap it (i.e. to implement it using the more
> generic vDSO API as a
It's not more generic, just different, e.g. uses %rbp instead of %rsp as
the anchor. Or am I missing something?
> subroutine) in a library for the public to choose (and you can imagine
> others bearing different ideas will do the same). Then good ideas will
> stand out!
I'd rather support two disparate vDSO implementations than implement the
barebones ABI as a wrapper to something heavier. E.g. I really don't want
to have to wade through a bunch of conditionals and stack accesses, (or
save/restore code if you're talking about making it compliant with the
x86_64 ABI) when I inevitably break something during kernel development.
Given all above, and that everyone involved wants to see SGX get accepted
into mainline sooner rather than later, I propose the following:
- Keep the barebones __vdso_sgx_enter_enclave() as is
- Explicitly state in the SGX documentation that userspace does *not*
have to use the vDSO, e.g. can enter enclaves directly and use signals
- Submit a patch to add second vDSO function to support the Intel SDK
use case (from Cedric or anyone from the SDK team)
I fully realize that the above approach saddles Cedric and the SDK team
with the extra task of justifying the need for two vDSO interfaces, and
likely reduces the probability of their proposal being accepted. But, we
don't *force* the SDK to be rewritten, and we gain a vDSO interface that
many people want and is acceptable to the maintainers (unless I've
horribly misread Andy's position).
On Sun, Mar 24, 2019 at 1:59 AM Xing, Cedric <[email protected]> wrote:
>
> Hi Andy,
>
> Thank you for your valuable feedbacks!
>
> Per what you have been saying, your feedbacks come from different angles - i.e. functionality vs. security, but they are mixed up somehow.
I think you're misunderstanding me. I'm not talking about security at
all here. SGX isn't a sandbox, full stop. I'm talking about the
degree to which an SGX enclave acts like a well-behaved black box.
>
> > I’m going to put my vDSO maintainer hat on for a minute. Cedric, your
> > proposal has the following issues related specifically to the vDSO:
> >
> > It inherently contains indirect branches. This means that, on retpoline
> > configurations, it probably needs to use retpolines. This is doable,
> > but it’s nasty, and you need to worry about register clobbers.
>
> Only the weakest link matters in security. With dynamic linking in use, this additional indirect CALL can't make things worse. But I'm open to, and in fact also willing to, apply whatever mitigation that you think is satisfactory (or that has been applied to other indirect branches, such as in PLT), such as retpoline. Btw, don't worry about register clobbers because we have at least %rax at our disposal.
There is no actual fundamental reason that dynamic linking has to work
this way, and in principle, one could even use retpolines to the call
the vDSO. In any event, the vDSO is currently compiled with
retpolines enabled, and if we decide to turn that off, it would be
decision to be made independently of SGX.
>
> >
> > It uses effectively unbounded stack space. The vDSO timing functions are
> > already a problem for Go, and this is worse.
>
> If targeting the same functionality (i.e. no exit callback), my API uses exactly 24 bytes more than Sean's. Is it really the case that those 24 bytes will break Go?
You're counting wrong. Your version uses 24 bytes + the stack size of
the exit handler + the amount of stack consumed by the enclave, which
is effectively unbounded. So this whole scheme becomes unusable on
anything other than a stack that is "large" for a totally undefined
value of large and that has guard pages.
>
> >
> > Cedric, your proposal allows an enclave to muck with RSP, but not in a
> > way that’s particularly pleasant.
>
> From security perspective, it is SGX ISA, but NOT any particular ABI, that allows enclaves "to muck with RSP".
Again, this has nothing to do with security. With your proposal, it's
not possible for the caller of an enclave to decide, in an ocall
handler, to pause and do something else. This isn't just theoretical.
Suppose someone wants to send a network request in an ocall handler.
With the current RSP approach, it's difficult to do this in a program
that uses poll / select / epoll -- you can't return out from the ocall
until you have an answer.
On Mon, Mar 25, 2019 at 11:03 AM Sean Christopherson
<[email protected]> wrote:
>
> On Sun, Mar 24, 2019 at 01:59:48AM -0700, Xing, Cedric wrote:
> > As said in my previous email, this vDSO API isn't even compliant to
> > x86_64 ABI and is absolutely NOT for average developers. Instead,
> > host/enclave communications are expected to be handled by SDKs and
> > those developers will be very aware of the limitations of their targeted
> > environments, and will need the freedom to deploy optimal solutions.
> I fully realize that the above approach saddles Cedric and the SDK team
> with the extra task of justifying the need for two vDSO interfaces, and
> likely reduces the probability of their proposal being accepted. But, we
> don't *force* the SDK to be rewritten, and we gain a vDSO interface that
> many people want and is acceptable to the maintainers (unless I've
> horribly misread Andy's position).
I don't think you've horribly misread it. I would like to keep the
stuff in the vDSO as minimal as possible. If we need to add a fancier
interface down the line, then that's fine.
> I think you're misunderstanding me. I'm not talking about security at
> all here. SGX isn't a sandbox, full stop. I'm talking about the degree
> to which an SGX enclave acts like a well-behaved black box.
Any meaningful communication requires an agreement in place. The host and the enclave could be either in agreement, or not in an agreement. In the former case, the enclave will behave while in the latter case it will misbehave. The thing is, if an agreement between them says - "Don't you enclave touch the stack", and if the enclave behaves, then it wouldn't touch the stack; or the enclave misbehaves, then that "agreement" CANNOT stop it from doing so, REGARDLESS what that agreement is.
The point is, an agreement must exist for host/enclave communication. The ABI limits what kinds of agreements they can bind to, but can NEVER enforce an agreement. The difference between Sean's ABI and mine is that mine is more relaxing (i.e. allows larger variety of agreements) but otherwise identical functionally. I truly hope you can understand that.
>
> >
> > > I’m going to put my vDSO maintainer hat on for a minute. Cedric,
> > > your proposal has the following issues related specifically to the
> vDSO:
> > >
> > > It inherently contains indirect branches. This means that, on
> > > retpoline configurations, it probably needs to use retpolines. This
> > > is doable, but it’s nasty, and you need to worry about register
> clobbers.
> >
> > Only the weakest link matters in security. With dynamic linking in
> use, this additional indirect CALL can't make things worse. But I'm open
> to, and in fact also willing to, apply whatever mitigation that you
> think is satisfactory (or that has been applied to other indirect
> branches, such as in PLT), such as retpoline. Btw, don't worry about
> register clobbers because we have at least %rax at our disposal.
>
> There is no actual fundamental reason that dynamic linking has to work
> this way, and in principle, one could even use retpolines to the call
> the vDSO. In any event, the vDSO is currently compiled with retpolines
> enabled, and if we decide to turn that off, it would be decision to be
> made independently of SGX.
Don't get me wrong! I'm just saying dynamic linking requires indirect branches so whatever mitigation used there can also apply here. I'm willing to implement the same mechanism as generally accepted in other occasions. If retpoline is the one then I will just do it.
Btw, retpoline won't work with CET though.
>
> >
> > >
> > > It uses effectively unbounded stack space. The vDSO timing functions
> > > are already a problem for Go, and this is worse.
> >
> > If targeting the same functionality (i.e. no exit callback), my API
> uses exactly 24 bytes more than Sean's. Is it really the case that those
> 24 bytes will break Go?
>
> You're counting wrong. Your version uses 24 bytes + the stack size of
> the exit handler + the amount of stack consumed by the enclave, which is
> effectively unbounded. So this whole scheme becomes unusable on
> anything other than a stack that is "large" for a totally undefined
> value of large and that has guard pages.
You misread. I said "targeting the same functionality", meaning no exit callback is used (because Sean's ABI doesn't support it). And because no callbacks will be made, only 24 more bytes will be needed.
And at this point I'm trying to stress the fact that my proposal is a superset of Sean's in terms of functionality - i.e. my proposal can do all that Sean's can do. For example, if the enclave is coded NOT to use the untrusted stack, e.g. in Fortanix's case, then it won't use the stack and the callback is unnecessary (and shall be set to NULL). That is, mine will work exactly the same as Sean's in the case of enclave not touching the stack. My apology for being excessively verbose here but your comment above prompts me that you may NOT have realized that my proposal will work exactly the SAME as Sean's when exit callback is absent (NULL).
>
> >
> > >
> > > Cedric, your proposal allows an enclave to muck with RSP, but not in
> > > a way that’s particularly pleasant.
> >
> > From security perspective, it is SGX ISA, but NOT any particular ABI,
> that allows enclaves "to muck with RSP".
>
> Again, this has nothing to do with security. With your proposal, it's
> not possible for the caller of an enclave to decide, in an ocall
> handler, to pause and do something else. This isn't just theoretical.
> Suppose someone wants to send a network request in an ocall handler.
> With the current RSP approach, it's difficult to do this in a program
> that uses poll / select / epoll -- you can't return out from the ocall
> until you have an answer.
Andy, your comment here further confirms that you have NOT understood my proposal.
In the case the o-call parameters are passed in registers (or separate buffers), exit handler is NOT needed (and should be set to NULL), and the API will just return at EEXIT, and then the caller can dispatch the o-call solely based on register values, which is exactly the SAME as Sean's proposal.
An exit handler is NOT necessary to support o-calls. It is needed ONLY if the host/enclave exchanges data on the stack.
My apology again for repeating myself, but my proposal is a SUPERSET of Sean's from functional perspective, and it will work in EXACTLY the SAME way as Sean's in ALL aspects when exit callback is absent. So you really have NOTHING to worry about from functional stand point! And as you said you don't worry about security either, then you really have NOTHING to worry about!
-Cedric
> On Mon, Mar 25, 2019 at 11:03 AM Sean Christopherson
> <[email protected]> wrote:
> >
> > On Sun, Mar 24, 2019 at 01:59:48AM -0700, Xing, Cedric wrote:
> > > As said in my previous email, this vDSO API isn't even compliant to
> > > x86_64 ABI and is absolutely NOT for average developers. Instead,
> > > host/enclave communications are expected to be handled by SDKs and
> > > those developers will be very aware of the limitations of their
> > > targeted environments, and will need the freedom to deploy optimal
> solutions.
>
> > I fully realize that the above approach saddles Cedric and the SDK
> > team with the extra task of justifying the need for two vDSO
> > interfaces, and likely reduces the probability of their proposal being
> > accepted. But, we don't *force* the SDK to be rewritten, and we gain
> > a vDSO interface that many people want and is acceptable to the
> > maintainers (unless I've horribly misread Andy's position).
>
> I don't think you've horribly misread it. I would like to keep the
> stuff in the vDSO as minimal as possible. If we need to add a fancier
> interface down the line, then that's fine.
Andy, I don't know "many people" is how many in Sean's email. I couldn't tell you how long it took us to settle on the current SGX ISA because you would just LAUGH! Why? Because it took insanely ridiculously long. Why that long? Because the h/w and u-code teams would like to trim down the ISA as much as possible. The fact is, whatever new is released, Intel will have to maintain it on all future processors FOREVER! That means significant/on-going cost to Intel. So any addition to ISA has to undergo extensive reviews that involve all kinds of experts from both within Intel and externally, and would usually take years, before you can see what you are seeing today. As I said in my earlier emails, RBP is NOT needed for interrupt/exception handlers, then how did RBP end up being restored at AEX? You can guess how many people were standing behind it! Sean has no clue! I can assure you!
Guess we've talked enough on the technical front. So let's talk about it on the business front. Let me take a step back. Let's say you are right, all enclaves would eventually be coded in the way you want. We (Intel SDK team) were convinced to follow your approach. But there were existing enclaves and a migration path would be needed. Would you like to support us? It'd be only 9 LOC on your side but how much would incur on our side? If you believe you are doing right thing, then acceptance is the next thing you should think of. You should offer an easy path for those who did "wrong" to get on your "right" boat. Don't you think so?
-Cedric
On Wed, 2019-03-20 at 18:21 +0200, 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.
>
> This commit adds the Linux SGX Enclave Driver that provides an ioctl API
> to manage enclaves. The address range for an enclave, commonly referred
> as ELRANGE in the documentation (e.g. Intel SDM), is reserved with
> mmap() against /dev/sgx. After that a set ioctls is used to build
> the enclave to the ELRANGE.
>
> Signed-off-by: Jarkko Sakkinen <[email protected]>
> Co-developed-by: Sean Christopherson <[email protected]>
> Signed-off-by: Sean Christopherson <[email protected]>
> Co-developed-by: Serge Ayoun <[email protected]>
> Signed-off-by: Serge Ayoun <[email protected]>
> Co-developed-by: Shay Katz-zamir <[email protected]>
> Signed-off-by: Shay Katz-zamir <[email protected]>
> Co-developed-by: Suresh Siddha <[email protected]>
> Signed-off-by: Suresh Siddha <[email protected]>
> ---
> Documentation/ioctl/ioctl-number.txt | 1 +
> arch/x86/Kconfig | 17 +-
> arch/x86/include/uapi/asm/sgx.h | 59 ++
> arch/x86/kernel/cpu/sgx/Makefile | 5 +-
> arch/x86/kernel/cpu/sgx/driver/Makefile | 3 +
> arch/x86/kernel/cpu/sgx/driver/driver.h | 38 ++
> arch/x86/kernel/cpu/sgx/driver/ioctl.c | 795 ++++++++++++++++++++++++
> arch/x86/kernel/cpu/sgx/driver/main.c | 290 +++++++++
> arch/x86/kernel/cpu/sgx/encl.c | 358 +++++++++++
> arch/x86/kernel/cpu/sgx/encl.h | 88 +++
> arch/x86/kernel/cpu/sgx/encls.c | 1 +
> arch/x86/kernel/cpu/sgx/main.c | 3 +
> arch/x86/kernel/cpu/sgx/sgx.h | 1 +
> 13 files changed, 1657 insertions(+), 2 deletions(-)
> create mode 100644 arch/x86/include/uapi/asm/sgx.h
> create mode 100644 arch/x86/kernel/cpu/sgx/driver/Makefile
> create mode 100644 arch/x86/kernel/cpu/sgx/driver/driver.h
> create mode 100644 arch/x86/kernel/cpu/sgx/driver/ioctl.c
> create mode 100644 arch/x86/kernel/cpu/sgx/driver/main.c
> create mode 100644 arch/x86/kernel/cpu/sgx/encl.c
> create mode 100644 arch/x86/kernel/cpu/sgx/encl.h
Shouldn't the driver be located somewhere under drivers/, but not under arch/x86?
I don't think x86 maintainers should have the burden to review every code change made to SGX driver?
Thanks,
-Kai
On Wed, 2019-03-20 at 18:21 +0200, Jarkko Sakkinen wrote:
> From: Sean Christopherson <[email protected]>
>
> Similar to other large Intel features such as VMX and TXT, SGX must be
> explicitly enabled in IA32_FEATURE_CONTROL MSR to be truly usable.
> Clear all SGX related capabilities if SGX is not fully enabled in
> IA32_FEATURE_CONTROL or if the SGX1 instruction set isn't supported
> (impossible on bare metal, theoretically possible in a VM if the VMM is
> doing something weird).
>
> Like SGX itself, SGX Launch Control must be explicitly enabled via a
> flag in IA32_FEATURE_CONTROL. Clear the SGX_LC capability if Launch
> Control is not fully enabled (or obviously if SGX itself is disabled).
>
> Note that clearing X86_FEATURE_SGX_LC creates a bit of a conundrum
> regarding the SGXLEPUBKEYHASH MSRs, as it may be desirable to read the
> MSRs even if they are not writable, e.g. to query the configured key,
> but clearing the capability leaves no breadcrum for discerning whether
> or not the MSRs exist. But, such usage will be rare (KVM is the only
> known case at this time) and not performance critical, so it's not
> unreasonable to require the use of rdmsr_safe(). Clearing the cap bit
> eliminates the need for an additional flag to track whether or not
> Launch Control is truly enabled, which is what we care about the vast
> majority of the time.
[Resend. Somehow my last reply doesn't show up in my mailbox so not sure whether I sent it
successfully or not. Sorry if you receving duplicated mails.]
However this is not consistent with HW behavior. If LC feature flag is not present, then MSRs should
have hash of Intel's key, which is not always the case here, when you expose SGX to KVM. Enclave in
KVM guest will get unexpected EINIT error when launing Intel enclave, if on HW MSRs are configured
to 3rd party value but locked to readonly.
My opition is we already have enough cases that violates HW behavior in SGX virtualization, let's
not have one more.
Besides, why do we "need an additional flag to track whether or not Launch Control is truly
enabled"? Doesn't driver only need to know whether MSRs are writable?
Thanks,
-Kai
>
> Signed-off-by: Sean Christopherson <[email protected]>
> Co-developed-by: Jarkko Sakkinen <[email protected]>
> Signed-off-by: Jarkko Sakkinen <[email protected]>
> ---
> arch/x86/kernel/cpu/intel.c | 39 +++++++++++++++++++++++++++++++++++++
> 1 file changed, 39 insertions(+)
>
> diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c
> index fc3c07fe7df5..702497f34a96 100644
> --- a/arch/x86/kernel/cpu/intel.c
> +++ b/arch/x86/kernel/cpu/intel.c
> @@ -596,6 +596,42 @@ static void detect_tme(struct cpuinfo_x86 *c)
> c->x86_phys_bits -= keyid_bits;
> }
>
> +static void __maybe_unused detect_sgx(struct cpuinfo_x86 *c)
> +{
> + unsigned long long fc;
> +
> + rdmsrl(MSR_IA32_FEATURE_CONTROL, fc);
> + if (!(fc & FEATURE_CONTROL_LOCKED)) {
> + pr_err_once("sgx: The feature control MSR is not locked\n");
> + goto err_unsupported;
> + }
> +
> + if (!(fc & FEATURE_CONTROL_SGX_ENABLE)) {
> + pr_err_once("sgx: SGX is not enabled in IA32_FEATURE_CONTROL MSR\n");
> + goto err_unsupported;
> + }
> +
> + if (!cpu_has(c, X86_FEATURE_SGX1)) {
> + pr_err_once("sgx: SGX1 instruction set is not supported\n");
> + goto err_unsupported;
> + }
> +
> + if (!(fc & FEATURE_CONTROL_SGX_LE_WR)) {
> + pr_info_once("sgx: The launch control MSRs are not writable\n");
> + goto err_msrs_rdonly;
> + }
> +
> + return;
> +
> +err_unsupported:
> + setup_clear_cpu_cap(X86_FEATURE_SGX);
> + setup_clear_cpu_cap(X86_FEATURE_SGX1);
> + setup_clear_cpu_cap(X86_FEATURE_SGX2);
> +
> +err_msrs_rdonly:
> + setup_clear_cpu_cap(X86_FEATURE_SGX_LC);
> +}
> +
> static void init_intel_energy_perf(struct cpuinfo_x86 *c)
> {
> u64 epb;
> @@ -763,6 +799,9 @@ static void init_intel(struct cpuinfo_x86 *c)
> if (cpu_has(c, X86_FEATURE_TME))
> detect_tme(c);
>
> + if (IS_ENABLED(CONFIG_INTEL_SGX) && cpu_has(c, X86_FEATURE_SGX))
> + detect_sgx(c);
> +
> init_intel_energy_perf(c);
>
> init_intel_misc_features(c);
On Tue, 26 Mar 2019, Huang, Kai wrote:
> On Wed, 2019-03-20 at 18:21 +0200, Jarkko Sakkinen wrote:
> > 13 files changed, 1657 insertions(+), 2 deletions(-)
> > create mode 100644 arch/x86/include/uapi/asm/sgx.h
> > create mode 100644 arch/x86/kernel/cpu/sgx/driver/Makefile
> > create mode 100644 arch/x86/kernel/cpu/sgx/driver/driver.h
> > create mode 100644 arch/x86/kernel/cpu/sgx/driver/ioctl.c
> > create mode 100644 arch/x86/kernel/cpu/sgx/driver/main.c
> > create mode 100644 arch/x86/kernel/cpu/sgx/encl.c
> > create mode 100644 arch/x86/kernel/cpu/sgx/encl.h
>
> Shouldn't the driver be located somewhere under drivers/, but not under arch/x86?
>
> I don't think x86 maintainers should have the burden to review every code
> change made to SGX driver?
I rather prefer to have it in x86. Why? Because driver code has a tendency
to get under the radar.
Thanks,
tglx
On Tue, Mar 26, 2019 at 05:17:40AM -0700, Huang, Kai wrote:
> On Wed, 2019-03-20 at 18:21 +0200, Jarkko Sakkinen wrote:
> > From: Sean Christopherson <[email protected]>
> >
> > Similar to other large Intel features such as VMX and TXT, SGX must be
> > explicitly enabled in IA32_FEATURE_CONTROL MSR to be truly usable.
> > Clear all SGX related capabilities if SGX is not fully enabled in
> > IA32_FEATURE_CONTROL or if the SGX1 instruction set isn't supported
> > (impossible on bare metal, theoretically possible in a VM if the VMM is
> > doing something weird).
> >
> > Like SGX itself, SGX Launch Control must be explicitly enabled via a
> > flag in IA32_FEATURE_CONTROL. Clear the SGX_LC capability if Launch
> > Control is not fully enabled (or obviously if SGX itself is disabled).
> >
> > Note that clearing X86_FEATURE_SGX_LC creates a bit of a conundrum
> > regarding the SGXLEPUBKEYHASH MSRs, as it may be desirable to read the
> > MSRs even if they are not writable, e.g. to query the configured key,
> > but clearing the capability leaves no breadcrum for discerning whether
> > or not the MSRs exist. But, such usage will be rare (KVM is the only
> > known case at this time) and not performance critical, so it's not
> > unreasonable to require the use of rdmsr_safe(). Clearing the cap bit
> > eliminates the need for an additional flag to track whether or not
> > Launch Control is truly enabled, which is what we care about the vast
> > majority of the time.
>
> [Resend. Somehow my last reply doesn't show up in my mailbox so not sure whether I sent it
> successfully or not. Sorry if you receving duplicated mails.]
>
> However this is not consistent with HW behavior. If LC feature flag is not present, then MSRs should
> have hash of Intel's key, which is not always the case here, when you expose SGX to KVM. Enclave in
> KVM guest will get unexpected EINIT error when launing Intel enclave, if on HW MSRs are configured
> to 3rd party value but locked to readonly.
Intel doesn't have a singular key. The internal reset value of the LE
pubkey hash MSRs is micro-architectural, i.e. can change without warning
on any given processor. All current processors with SGX support may use
the same reset value, but it's not something that customers should/can
rely on.
That being said, this in no way impacts KVM's ability to virtualize SGX,
e.g. KVM can directly do CPUID and {RD,WR}MSR to probe the capabilities
of the platform as needed.
> My opition is we already have enough cases that violates HW behavior in
> SGX virtualization, let's not have one more.
What are the other cases?
> Besides, why do we "need an additional flag to track whether or not
> Launch Control is truly enabled"? Doesn't driver only need to know whether
> MSRs are writable?
Yes, and that's why we're overloading X86_FEATURE_SGX_LC to be set if
and only if SGX_LC is supported *and* enabled, e.g. so that the kernel
can simply check X86_FEATURE_SGX_LC without having to also probe the MSRs.
On Tue, Mar 26, 2019 at 01:40:57PM +0100, Thomas Gleixner wrote:
> On Tue, 26 Mar 2019, Huang, Kai wrote:
> > On Wed, 2019-03-20 at 18:21 +0200, Jarkko Sakkinen wrote:
> > > 13 files changed, 1657 insertions(+), 2 deletions(-)
> > > create mode 100644 arch/x86/include/uapi/asm/sgx.h
> > > create mode 100644 arch/x86/kernel/cpu/sgx/driver/Makefile
> > > create mode 100644 arch/x86/kernel/cpu/sgx/driver/driver.h
> > > create mode 100644 arch/x86/kernel/cpu/sgx/driver/ioctl.c
> > > create mode 100644 arch/x86/kernel/cpu/sgx/driver/main.c
> > > create mode 100644 arch/x86/kernel/cpu/sgx/encl.c
> > > create mode 100644 arch/x86/kernel/cpu/sgx/encl.h
> >
> > Shouldn't the driver be located somewhere under drivers/, but not under arch/x86?
> >
> > I don't think x86 maintainers should have the burden to review every code
> > change made to SGX driver?
>
> I rather prefer to have it in x86. Why? Because driver code has a tendency
> to get under the radar.
And having everything under arch/x86 will likely reduce the maintenance
burden for everyone:
- Doesn't require taking changes through multiple trees or coordinating
acks from multiple maintainers.
- Significantly reduces the number of functions, macros, structs and
variables that needs to be exposed in asm/sgx.h (actually eliminates
it entirely at this point) which allows for sane code organization as
opposed to dumping everything in one big header.
- Mostly avoids bikeshedding over whether something is architectural
or belongs in the so called driver.
On Mon, Mar 25, 2019 at 9:53 PM Xing, Cedric <[email protected]> wrote:
>
> > On Mon, Mar 25, 2019 at 11:03 AM Sean Christopherson
> > <[email protected]> wrote:
> > >
> > > On Sun, Mar 24, 2019 at 01:59:48AM -0700, Xing, Cedric wrote:
> > > > As said in my previous email, this vDSO API isn't even compliant to
> > > > x86_64 ABI and is absolutely NOT for average developers. Instead,
> > > > host/enclave communications are expected to be handled by SDKs and
> > > > those developers will be very aware of the limitations of their
> > > > targeted environments, and will need the freedom to deploy optimal
> > solutions.
> >
> > > I fully realize that the above approach saddles Cedric and the SDK
> > > team with the extra task of justifying the need for two vDSO
> > > interfaces, and likely reduces the probability of their proposal being
> > > accepted. But, we don't *force* the SDK to be rewritten, and we gain
> > > a vDSO interface that many people want and is acceptable to the
> > > maintainers (unless I've horribly misread Andy's position).
> >
> > I don't think you've horribly misread it. I would like to keep the
> > stuff in the vDSO as minimal as possible. If we need to add a fancier
> > interface down the line, then that's fine.
>
> Andy, I don't know "many people" is how many in Sean's email. I couldn't tell you how long it took us to settle on the current SGX ISA because you would just LAUGH! Why? Because it took insanely ridiculously long. Why that long? Because the h/w and u-code teams would like to trim down the ISA as much as possible. The fact is, whatever new is released, Intel will have to maintain it on all future processors FOREVER! That means significant/on-going cost to Intel. So any addition to ISA has to undergo extensive reviews that involve all kinds of experts from both within Intel and externally, and would usually take years, before you can see what you are seeing today. As I said in my earlier emails, RBP is NOT needed for interrupt/exception handlers, then how did RBP end up being restored at AEX? You can guess how many people were standing behind it! Sean has no clue! I can assure you!
>
> Guess we've talked enough on the technical front. So let's talk about it on the business front. Let me take a step back. Let's say you are right, all enclaves would eventually be coded in the way you want. We (Intel SDK team) were convinced to follow your approach. But there were existing enclaves and a migration path would be needed. Would you like to support us? It'd be only 9 LOC on your side but how much would incur on our side? If you believe you are doing right thing, then acceptance is the next thing you should think of. You should offer an easy path for those who did "wrong" to get on your "right" boat. Don't you think so?
>
I suppose the real question is: are there a significant number of
users who will want to run enclaves created using an old SDK on Linux?
And will there actually be support for doing this in the software
stack?
If the answer to both questions is yes, then it seems like it could be
reasonable to support it in the vDSO. But I still think it should
probably be a different vDSO entry point so that the normal case
doesn't become more complicated.
> On Tue, Mar 26, 2019 at 01:40:57PM +0100, Thomas Gleixner wrote:
> > On Tue, 26 Mar 2019, Huang, Kai wrote:
> > > On Wed, 2019-03-20 at 18:21 +0200, Jarkko Sakkinen wrote:
> > > > 13 files changed, 1657 insertions(+), 2 deletions(-) create mode
> > > > 100644 arch/x86/include/uapi/asm/sgx.h create mode 100644
> > > > arch/x86/kernel/cpu/sgx/driver/Makefile
> > > > create mode 100644 arch/x86/kernel/cpu/sgx/driver/driver.h
> > > > create mode 100644 arch/x86/kernel/cpu/sgx/driver/ioctl.c
> > > > create mode 100644 arch/x86/kernel/cpu/sgx/driver/main.c
> > > > create mode 100644 arch/x86/kernel/cpu/sgx/encl.c create mode
> > > > 100644 arch/x86/kernel/cpu/sgx/encl.h
> > >
> > > Shouldn't the driver be located somewhere under drivers/, but not under
> arch/x86?
> > >
> > > I don't think x86 maintainers should have the burden to review every
> > > code change made to SGX driver?
> >
> > I rather prefer to have it in x86. Why? Because driver code has a
> > tendency to get under the radar.
>
> And having everything under arch/x86 will likely reduce the maintenance
> burden for everyone:
>
> - Doesn't require taking changes through multiple trees or coordinating
> acks from multiple maintainers.
>
> - Significantly reduces the number of functions, macros, structs and
> variables that needs to be exposed in asm/sgx.h (actually eliminates
> it entirely at this point) which allows for sane code organization as
> opposed to dumping everything in one big header.
>
> - Mostly avoids bikeshedding over whether something is architectural
> or belongs in the so called driver.
Sure. Thanks.
Thanks,
-Kai
>
> On Tue, Mar 26, 2019 at 05:17:40AM -0700, Huang, Kai wrote:
> > On Wed, 2019-03-20 at 18:21 +0200, Jarkko Sakkinen wrote:
> > > From: Sean Christopherson <[email protected]>
> > >
> > > Similar to other large Intel features such as VMX and TXT, SGX must
> > > be explicitly enabled in IA32_FEATURE_CONTROL MSR to be truly usable.
> > > Clear all SGX related capabilities if SGX is not fully enabled in
> > > IA32_FEATURE_CONTROL or if the SGX1 instruction set isn't supported
> > > (impossible on bare metal, theoretically possible in a VM if the VMM
> > > is doing something weird).
> > >
> > > Like SGX itself, SGX Launch Control must be explicitly enabled via a
> > > flag in IA32_FEATURE_CONTROL. Clear the SGX_LC capability if Launch
> > > Control is not fully enabled (or obviously if SGX itself is disabled).
> > >
> > > Note that clearing X86_FEATURE_SGX_LC creates a bit of a conundrum
> > > regarding the SGXLEPUBKEYHASH MSRs, as it may be desirable to read
> > > the MSRs even if they are not writable, e.g. to query the configured
> > > key, but clearing the capability leaves no breadcrum for discerning
> > > whether or not the MSRs exist. But, such usage will be rare (KVM is
> > > the only known case at this time) and not performance critical, so
> > > it's not unreasonable to require the use of rdmsr_safe(). Clearing
> > > the cap bit eliminates the need for an additional flag to track
> > > whether or not Launch Control is truly enabled, which is what we
> > > care about the vast majority of the time.
> >
> > [Resend. Somehow my last reply doesn't show up in my mailbox so not
> > sure whether I sent it successfully or not. Sorry if you receving
> > duplicated mails.]
> >
> > However this is not consistent with HW behavior. If LC feature flag is
> > not present, then MSRs should have hash of Intel's key, which is not
> > always the case here, when you expose SGX to KVM. Enclave in KVM guest
> > will get unexpected EINIT error when launing Intel enclave, if on HW MSRs
> are configured to 3rd party value but locked to readonly.
>
> Intel doesn't have a singular key. The internal reset value of the LE pubkey
> hash MSRs is micro-architectural, i.e. can change without warning on any
> given processor. All current processors with SGX support may use the same
> reset value, but it's not something that customers should/can rely on.
I don't think update of Intel hash would be frequent, like you said all current processors with SGX support may should be using the same value.
Thus I am expecting I can run Intel SDK on KVM guest if LC feature flag is cleared.
Even Intel has updated the key, SDK should mention those impacted machine should run SDK starting from some new version, so SDK should continue to run.
>
> That being said, this in no way impacts KVM's ability to virtualize SGX, e.g.
> KVM can directly do CPUID and {RD,WR}MSR to probe the capabilities of the
> platform as needed.
I am not following. KVM can do whatever it wants, but it cannot change the fact that KVM guest cannot run intel enclave if platform's MSRs are configured to 3rd party and locked.
Or am I misunderstanding?
>
> > My opition is we already have enough cases that violates HW behavior
> > in SGX virtualization, let's not have one more.
>
> What are the other cases?
One example is EPCM + EPC should be power of 2. And EPC migration (sudden loss of EPC). And if we apply some policy to restrict enclave during EINIT trap, etc.
But those are not the point. I should probably have not mentioned them.
>
> > Besides, why do we "need an additional flag to track whether or not
> > Launch Control is truly enabled"? Doesn't driver only need to know
> > whether MSRs are writable?
>
> Yes, and that's why we're overloading X86_FEATURE_SGX_LC to be set if and
> only if SGX_LC is supported *and* enabled, e.g. so that the kernel can simply
> check X86_FEATURE_SGX_LC without having to also probe the MSRs.
OK.
But IMO if what I mentioned above is correct, then that part should overweight the benefit you can get here.
Anyway who cares to do less/more thing in driver probe?
Thanks,
-Kai
On Tue, Mar 26, 2019 at 02:25:52PM -0700, Huang, Kai wrote:
> >
> > That being said, this in no way impacts KVM's ability to virtualize SGX, e.g.
> > KVM can directly do CPUID and {RD,WR}MSR to probe the capabilities of the
> > platform as needed.
>
> I am not following. KVM can do whatever it wants, but it cannot change the
> fact that KVM guest cannot run intel enclave if platform's MSRs are
> configured to 3rd party and locked.
>
> Or am I misunderstanding?
What does that have to do with this patch? The only thing this patch does
is clear a *software* bit that says "SGX LC is enabled" so that the kernel
can make the reasonable assumption that the MSRs are writable when
X86_FEATURE_SGX_LC=1.
>
> >
> > > My opition is we already have enough cases that violates HW behavior
> > > in SGX virtualization, let's not have one more.
> >
> > What are the other cases?
>
> One example is EPCM + EPC should be power of 2.
Again, that's a micro-architecture detail due to the use of range registers
to carve out the EPC. The only thing the SDM states regarding size and
alignment is:
The EPC is divided into EPC pages. An EPC page is 4KB in size and always
aligned on a 4KB boundary.
> And EPC migration (sudden loss of EPC).
Unfortunate, yes, but it does not violate the architecture in any way.
You can argue that it adds a wrinkle that isn't explicitly documented
in the SDM, but virtualization is full of things like that.
> And if we apply some policy to restrict enclave during EINIT trap, etc.
>
> But those are not the point. I should probably have not mentioned them.
>
> >
> > > Besides, why do we "need an additional flag to track whether or not
> > > Launch Control is truly enabled"? Doesn't driver only need to know
> > > whether MSRs are writable?
> >
> > Yes, and that's why we're overloading X86_FEATURE_SGX_LC to be set if and
> > only if SGX_LC is supported *and* enabled, e.g. so that the kernel can simply
> > check X86_FEATURE_SGX_LC without having to also probe the MSRs.
>
> OK.
>
> But IMO if what I mentioned above is correct, then that part should
> overweight the benefit you can get here.
Half dozen of one, six of the other. If we clear the flag, then KVM has
to manually probe the MSRs. If we don't clear the flag, then the driver
has to manually probe the MSRs.
Alternatively we could add another flag, but then we end up with
X86_FEATURE_SGX_LC and X86_FEATURE_SGX_LE_PUBKEY_HASH_WRITABLE, or maybe
X86_FEATURE_SGX_LC_REALLY_TRULY_ENABLED.
>
> Anyway who cares to do less/more thing in driver probe?
Exactly. KVM will probe the MSRs once during its own loading.
> On Tue, Mar 26, 2019 at 02:25:52PM -0700, Huang, Kai wrote:
> > >
> > > That being said, this in no way impacts KVM's ability to virtualize SGX, e.g.
> > > KVM can directly do CPUID and {RD,WR}MSR to probe the capabilities
> > > of the platform as needed.
> >
> > I am not following. KVM can do whatever it wants, but it cannot change
> > the fact that KVM guest cannot run intel enclave if platform's MSRs
> > are configured to 3rd party and locked.
> >
> > Or am I misunderstanding?
>
> What does that have to do with this patch? The only thing this patch does is
> clear a *software* bit that says "SGX LC is enabled" so that the kernel can
> make the reasonable assumption that the MSRs are writable when
> X86_FEATURE_SGX_LC=1.
Sorted out offline discussion with you. Will let you handle :)
Thanks,
-Kai
On Tue, Mar 26, 2019 at 12:01:31PM +0000, Huang, Kai wrote:
> On Wed, 2019-03-20 at 18:21 +0200, 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.
> >
> > This commit adds the Linux SGX Enclave Driver that provides an ioctl API
> > to manage enclaves. The address range for an enclave, commonly referred
> > as ELRANGE in the documentation (e.g. Intel SDM), is reserved with
> > mmap() against /dev/sgx. After that a set ioctls is used to build
> > the enclave to the ELRANGE.
> >
> > Signed-off-by: Jarkko Sakkinen <[email protected]>
> > Co-developed-by: Sean Christopherson <[email protected]>
> > Signed-off-by: Sean Christopherson <[email protected]>
> > Co-developed-by: Serge Ayoun <[email protected]>
> > Signed-off-by: Serge Ayoun <[email protected]>
> > Co-developed-by: Shay Katz-zamir <[email protected]>
> > Signed-off-by: Shay Katz-zamir <[email protected]>
> > Co-developed-by: Suresh Siddha <[email protected]>
> > Signed-off-by: Suresh Siddha <[email protected]>
> > ---
> > Documentation/ioctl/ioctl-number.txt | 1 +
> > arch/x86/Kconfig | 17 +-
> > arch/x86/include/uapi/asm/sgx.h | 59 ++
> > arch/x86/kernel/cpu/sgx/Makefile | 5 +-
> > arch/x86/kernel/cpu/sgx/driver/Makefile | 3 +
> > arch/x86/kernel/cpu/sgx/driver/driver.h | 38 ++
> > arch/x86/kernel/cpu/sgx/driver/ioctl.c | 795 ++++++++++++++++++++++++
> > arch/x86/kernel/cpu/sgx/driver/main.c | 290 +++++++++
> > arch/x86/kernel/cpu/sgx/encl.c | 358 +++++++++++
> > arch/x86/kernel/cpu/sgx/encl.h | 88 +++
> > arch/x86/kernel/cpu/sgx/encls.c | 1 +
> > arch/x86/kernel/cpu/sgx/main.c | 3 +
> > arch/x86/kernel/cpu/sgx/sgx.h | 1 +
> > 13 files changed, 1657 insertions(+), 2 deletions(-)
> > create mode 100644 arch/x86/include/uapi/asm/sgx.h
> > create mode 100644 arch/x86/kernel/cpu/sgx/driver/Makefile
> > create mode 100644 arch/x86/kernel/cpu/sgx/driver/driver.h
> > create mode 100644 arch/x86/kernel/cpu/sgx/driver/ioctl.c
> > create mode 100644 arch/x86/kernel/cpu/sgx/driver/main.c
> > create mode 100644 arch/x86/kernel/cpu/sgx/encl.c
> > create mode 100644 arch/x86/kernel/cpu/sgx/encl.h
>
> Shouldn't the driver be located somewhere under drivers/, but not under arch/x86?
>
> I don't think x86 maintainers should have the burden to review every code change made to SGX driver?
Not sure I got your angle. It is x86 tied code.
$ git grep tristate arch/x86/ | wc -l
22
/Jarkko
Hi Andy,
> From: [email protected] [mailto:linux-sgx-
> [email protected]] On Behalf Of Andy Lutomirski
>
> I suppose the real question is: are there a significant number of
> users who will want to run enclaves created using an old SDK on Linux?
> And will there actually be support for doing this in the software
> stack?
To your first question, I cannot share information of Intel customers or speak for them. But in general, people would like to stay with an old enclave usually because of: 1) attestation, because MRENCLAVE will change after rebuild; and/or 2) the need to support a mix of older and newer Linux kernels. So I'd say it'll be commonly desired, especially when this vDSO API is still "new" (so not available on every platform).
To your second question, Intel will support all "legacy" enclaves built with older SGX SDKs on newer kernels. And that's why we are so eager to find a migration path. I can't speak for other companies, but guess backward compatibility is always desirable.
>
> If the answer to both questions is yes, then it seems like it could be
> reasonable to support it in the vDSO. But I still think it should
> probably be a different vDSO entry point so that the normal case
> doesn't become more complicated.
I'll support whatever you think is more appropriate.
At the end, I'd like to give out the full version of my proposal, with your feedbacks (i.e. stack unwinder and Spectre variant 2) addressed. I'm a bit concerned by retpoline, which won't work (or be needed) when CET comes online. Are you looking to change it again then?
Here's the summary of the changes:
- Added CFI directives for proper unwinding.
- Defined sgx_ex_callback - the callback function on enclave exit/exception.
- Aligned stack properly before calling sgx_ex_callback (per x86_64 ABI).
- Used retpoline in place of indirect call.
- The block starting at label "4:" captures all the code necessary to support sgx_ex_call. It has grown longer due to retpoline.
/**
* __vdso_sgx_enter_enclave() - Enter an SGX enclave
*
* %eax: ENCLU leaf, must be either EENTER or ERESUME
* 0x08(%rsp): TCS
* 0x10(%rsp): Optional pointer to 'struct sgx_enclave_exception'
* 0x18(%rsp): Optional function pointer to 'sgx_ex_callback', whose
* definition will be given below. Note that this function, if
* present, shall follow x86_64 ABI.
* return: 0 (zero) on success, or a negative error code on failure.
*
* Note that __vdso_sgx_enter_enclave() is not compatible with x86_64 ABI.
* All registers except RBP must be treated as volatile from the caller's
* perspective, including but not limited to GPRs, EFLAGS.DF, MXCSR, FCW, etc...
* Enclave may decrement RSP, but must not increment it - i.e. existing content
* of the stack shall be preserved.
*
* sgx_ex_callback - A callback function to be invoked by
* __vdso_sgx_enter_enclave() upon exception or after the enclave exits.
*
* typedef int (*sgx_ex_callback)(long rdi, long rsi, long rdx,
* struct sgx_enclave_exception *ex_info, long r8, long r9,
* long rsp, void *tcs);
*
* Note that sgx_ex_callback shall be x86_64 ABI compliant.
*
* Note that other GPRs (except %rax, %rbx and %rcx) are also passed through to
* sgx_ex_callback, even though accessing them requires assembly code.
*/
__vdso_sgx_enter_enclave:
/* prolog */
.cfi_startproc
push %rbp
.cfi_adjust_cfa_offset 8
.cfi_rel_offset %rbp, 0
mov %rsp, %rbp
.cfi_def_cfa_register %rbp
1: /* EENTER <= leaf <= ERESUME */
cmp $0x2, %eax
jb 5f
cmp $0x3, %eax
ja 5f
/* Load TCS and AEP */
mov 0x10(%rbp), %rbx
lea 2f(%rip), %rcx
2: enclu
/* EEXIT path */
mov 0x18(%rbp), %rcx
jrcxz 3f
mov %eax, EX_LEAF(%rcx)
/* normalize return value */
3: xor %eax, %eax
4: /* call sgx_ex_callback if supplied */
cmpq $0, 0x20(%rbp)
jz 6f
/* align stack per x86_64 ABI */
mov %rsp, %rbx
and $-0x10, %rsp
/* parameters */
push 0x10(%rbp)
push %rbx
/* call *0x20(%rbp) using retpoline */
mov 0x20(%rbp), %rax
call 41f
/* stack cleanup */
mov %rbx, %rsp
jmp 1b
41: call 43f
42: pause
lfence
jmp 42b
43: mov %rax, (%rsp)
ret
5: /* bad leaf */
cmp $0, %eax
jle 6f
mov $(-EINVAL), %eax
6: /* epilog */
leave
.cfi_def_cfa %rsp, 8
ret
.cfi_endproc
.pushsection .fixup, "ax"
7: mov 0x18(%rbp), %rcx
jrcxz 8f
/* fill in ex_info */
mov %eax, EX_LEAF(%rcx)
mov %di, EX_TRAPNR(%rcx)
mov %si, EX_ERROR_CODE(%rcx)
mov %rdx, EX_ADDRESS(%rcx)
8: mov $(-EFAULT), %eax
jmp 4b
.popsection
_ASM_VDSO_EXTABLE_HANDLE(2b, 7b)
-Cedric
On Wed, Mar 27, 2019 at 9:23 PM Xing, Cedric <[email protected]> wrote:
>
> Hi Andy,
>
> > From: [email protected] [mailto:linux-sgx-
> > [email protected]] On Behalf Of Andy Lutomirski
> >
> > I suppose the real question is: are there a significant number of
> > users who will want to run enclaves created using an old SDK on Linux?
> > And will there actually be support for doing this in the software
> > stack?
>
> To your first question, I cannot share information of Intel customers or speak for them. But in general, people would like to stay with an old enclave usually because of: 1) attestation, because MRENCLAVE will change after rebuild; and/or 2) the need to support a mix of older and newer Linux kernels. So I'd say it'll be commonly desired, especially when this vDSO API is still "new" (so not available on every platform).
>
> To your second question, Intel will support all "legacy" enclaves built with older SGX SDKs on newer kernels. And that's why we are so eager to find a migration path. I can't speak for other companies, but guess backward compatibility is always desirable.
>
> >
> > If the answer to both questions is yes, then it seems like it could be
> > reasonable to support it in the vDSO. But I still think it should
> > probably be a different vDSO entry point so that the normal case
> > doesn't become more complicated.
>
> I'll support whatever you think is more appropriate.
>
> At the end, I'd like to give out the full version of my proposal, with your feedbacks (i.e. stack unwinder and Spectre variant 2) addressed. I'm a bit concerned by retpoline, which won't work (or be needed) when CET comes online. Are you looking to change it again then?
The kernel is buildable with and without retpolines. Presumably the
addition of CET support will need to address this everywhere,
including in the vDSO.
>
> Here's the summary of the changes:
> - Added CFI directives for proper unwinding.
> - Defined sgx_ex_callback - the callback function on enclave exit/exception.
> - Aligned stack properly before calling sgx_ex_callback (per x86_64 ABI).
> - Used retpoline in place of indirect call.
> - The block starting at label "4:" captures all the code necessary to support sgx_ex_call. It has grown longer due to retpoline.
>
> /**
> * __vdso_sgx_enter_enclave() - Enter an SGX enclave
> *
> * %eax: ENCLU leaf, must be either EENTER or ERESUME
> * 0x08(%rsp): TCS
> * 0x10(%rsp): Optional pointer to 'struct sgx_enclave_exception'
> * 0x18(%rsp): Optional function pointer to 'sgx_ex_callback', whose
> * definition will be given below. Note that this function, if
> * present, shall follow x86_64 ABI.
> * return: 0 (zero) on success, or a negative error code on failure.
> *
> * Note that __vdso_sgx_enter_enclave() is not compatible with x86_64 ABI.
> * All registers except RBP must be treated as volatile from the caller's
> * perspective, including but not limited to GPRs, EFLAGS.DF, MXCSR, FCW, etc...
> * Enclave may decrement RSP, but must not increment it - i.e. existing content
> * of the stack shall be preserved.
> *
> * sgx_ex_callback - A callback function to be invoked by
> * __vdso_sgx_enter_enclave() upon exception or after the enclave exits.
> *
> * typedef int (*sgx_ex_callback)(long rdi, long rsi, long rdx,
> * struct sgx_enclave_exception *ex_info, long r8, long r9,
> * long rsp, void *tcs);
> *
> * Note that sgx_ex_callback shall be x86_64 ABI compliant.
> *
> * Note that other GPRs (except %rax, %rbx and %rcx) are also passed through to
> * sgx_ex_callback, even though accessing them requires assembly code.
> */
> __vdso_sgx_enter_enclave:
> /* prolog */
> .cfi_startproc
> push %rbp
> .cfi_adjust_cfa_offset 8
> .cfi_rel_offset %rbp, 0
> mov %rsp, %rbp
> .cfi_def_cfa_register %rbp
>
> 1: /* EENTER <= leaf <= ERESUME */
> cmp $0x2, %eax
> jb 5f
> cmp $0x3, %eax
> ja 5f
>
> /* Load TCS and AEP */
> mov 0x10(%rbp), %rbx
> lea 2f(%rip), %rcx
>
> 2: enclu
>
> /* EEXIT path */
> mov 0x18(%rbp), %rcx
> jrcxz 3f
> mov %eax, EX_LEAF(%rcx)
> /* normalize return value */
> 3: xor %eax, %eax
>
> 4: /* call sgx_ex_callback if supplied */
> cmpq $0, 0x20(%rbp)
> jz 6f
> /* align stack per x86_64 ABI */
> mov %rsp, %rbx
> and $-0x10, %rsp
> /* parameters */
> push 0x10(%rbp)
> push %rbx
> /* call *0x20(%rbp) using retpoline */
> mov 0x20(%rbp), %rax
> call 41f
> /* stack cleanup */
> mov %rbx, %rsp
> jmp 1b
> 41: call 43f
> 42: pause
> lfence
> jmp 42b
> 43: mov %rax, (%rsp)
> ret
>
> 5: /* bad leaf */
> cmp $0, %eax
> jle 6f
> mov $(-EINVAL), %eax
>
> 6: /* epilog */
> leave
> .cfi_def_cfa %rsp, 8
> ret
> .cfi_endproc
>
> .pushsection .fixup, "ax"
> 7: mov 0x18(%rbp), %rcx
> jrcxz 8f
> /* fill in ex_info */
> mov %eax, EX_LEAF(%rcx)
> mov %di, EX_TRAPNR(%rcx)
> mov %si, EX_ERROR_CODE(%rcx)
> mov %rdx, EX_ADDRESS(%rcx)
> 8: mov $(-EFAULT), %eax
> jmp 4b
> .popsection
>
> _ASM_VDSO_EXTABLE_HANDLE(2b, 7b)
>
>
It's certainly making progress. I like the fact that the callback is
now unconditional (if non-NULL) rather than being used just in case of
certain exit types. But, if we go down this route, let's name and
document it appropriately -- just call the function "callback" and
document it as a function that is called just before
__vdso_sgx_enter_enclave returns, to be used if support for legacy
enclaves that push data onto the untrusted stack is needed. We should
further document that it's safe to longjmp out of it.
Also, the tests in tools/testing/selftests/x86/unwind_vdso.c should be
augmented to test this code.
Finally, why does the vDSO code bother checking whether the leaf is valid?
> It's certainly making progress. I like the fact that the callback is
> now unconditional (if non-NULL) rather than being used just in case of
> certain exit types. But, if we go down this route, let's name and
> document it appropriately -- just call the function "callback" and
> document it as a function that is called just before
> __vdso_sgx_enter_enclave returns, to be used if support for legacy
> enclaves that push data onto the untrusted stack is needed. We should
> further document that it's safe to longjmp out of it.
>
> Also, the tests in tools/testing/selftests/x86/unwind_vdso.c should be
> augmented to test this code.
>
> Finally, why does the vDSO code bother checking whether the leaf is
> valid?
I can document it. I'll look into unwind_vdso.c to see what kind of selftests will make sense here. And I'll send out a RFC patch with everything included. Or would you prefer to have my changes integrated into Jarkko's patch v20?
Different ENCLU leaf has different parameters. This vDSO API knows how to load up parameters only for EENTER and ERESUME so it errs on all other positive values. 0 and negative values are interpreted as return codes.
On Thu, Mar 28, 2019 at 11:19:25PM +0000, Xing, Cedric wrote:
> > It's certainly making progress. I like the fact that the callback is
> > now unconditional (if non-NULL) rather than being used just in case of
> > certain exit types. But, if we go down this route, let's name and
> > document it appropriately -- just call the function "callback" and
> > document it as a function that is called just before
> > __vdso_sgx_enter_enclave returns, to be used if support for legacy
> > enclaves that push data onto the untrusted stack is needed. We should
> > further document that it's safe to longjmp out of it.
> >
> > Also, the tests in tools/testing/selftests/x86/unwind_vdso.c should be
> > augmented to test this code.
> >
> > Finally, why does the vDSO code bother checking whether the leaf is
> > valid?
>
> I can document it. I'll look into unwind_vdso.c to see what kind of
> selftests will make sense here. And I'll send out a RFC patch with
> everything included. Or would you prefer to have my changes integrated
> into Jarkko's patch v20?
>
> Different ENCLU leaf has different parameters. This vDSO API knows how
> to load up parameters only for EENTER and ERESUME so it errs on all
> other positive values. 0 and negative values are interpreted as return
> codes.
Not gonna make it to v20. I'm aiming to send it early next week and have
already closed the content.
What this and also Sean's solution would need is the update to
Documentation/x86/sgx.rst explaining how it works in detail.
/Jarkko
On Fri, Mar 29, 2019 at 11:48:14AM +0200, Jarkko Sakkinen wrote:
Good morning to everyone, I hope the weekend is going well.
> Not gonna make it to v20. I'm aiming to send it early next week and have
> already closed the content.
What is the status of your jarkko-sgx/next and jarkko-sgx/master
branches with respect to all of this?
We have our SFLC patch series currently staged on top of
jarkko-sgx/next and we will re-stage them on top of whatever you are
pushing upstream from.
> /Jarkko
Have a good remainder of the weekend.
Dr. Greg
As always,
Dr. G.W. Wettstein, Ph.D. Enjellic Systems Development, LLC.
4206 N. 19th Ave. Specializing in information infra-structure
Fargo, ND 58102 development.
PH: 701-281-1686
FAX: 701-281-3949 EMAIL: [email protected]
------------------------------------------------------------------------------
"Can't they?
A 64bit number incremented every millisecond can grow for half a
billion years. As far as I'm concerned, that is forever."
-- Neil Brown
linux-raid
On Thu, Mar 28, 2019 at 12:18:40PM -0700, Andy Lutomirski wrote:
> Finally, why does the vDSO code bother checking whether the leaf is valid?
To sanity check the input to ensure the caller is attempting to enter an
enclave, i.e. the function is named __vdso_sgx_enter_enclave(), not
__vsgx_enclu(). And if the caller does pass the wrong leaf, EINVAL will
be easier to debug than EFAULT, especially on SGX1 hardware where the #GP
could be misconstrued as a loss of EPC.
I'm not dead set on the checks by any means, but it does seem appropriate.
And the cost is all of 3 uops and a few bytes of a code.