Linux-Sgx Archive on lore.kernel.org
 help / color / Atom feed
* [PATCH v19 00/27] Intel SGX1 support
@ 2019-03-17 21:14 Jarkko Sakkinen
  2019-03-17 21:14 ` [PATCH v19 01/27] x86/cpufeatures: Add Intel-defined SGX feature bit Jarkko Sakkinen
                   ` (24 more replies)
  0 siblings, 25 replies; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-17 21:14 UTC (permalink / raw)
  To: x86, linux-sgx
  Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Jarkko Sakkinen

[-- Warning: decoded text below may be mangled --]
[-- Attachment #0: Type: text/plain; charset=a, Size: 21616 bytes --]

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


^ permalink raw reply	[flat|nested] 92+ messages in thread

* [PATCH v19 01/27] x86/cpufeatures: Add Intel-defined SGX feature bit
  2019-03-17 21:14 [PATCH v19 00/27] Intel SGX1 support Jarkko Sakkinen
@ 2019-03-17 21:14 ` Jarkko Sakkinen
  2019-03-17 21:14 ` [PATCH v19 02/27] x86/cpufeatures: Add SGX sub-features (as Linux-defined bits) Jarkko Sakkinen
                   ` (23 subsequent siblings)
  24 siblings, 0 replies; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-17 21:14 UTC (permalink / raw)
  To: x86, linux-sgx
  Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes, Kai Huang,
	Jarkko Sakkinen

From: Kai Huang <kai.huang@linux.intel.com>

X86_FEATURE_SGX reflects whether or not the CPU supports Intel's
Software Guard eXtensions (SGX).

Signed-off-by: Kai Huang <kai.huang@linux.intel.com>
Co-developed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Reviewed-by: Borislav Petkov <bp@suse.de>
---
 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


^ permalink raw reply	[flat|nested] 92+ messages in thread

* [PATCH v19 02/27] x86/cpufeatures: Add SGX sub-features (as Linux-defined bits)
  2019-03-17 21:14 [PATCH v19 00/27] Intel SGX1 support Jarkko Sakkinen
  2019-03-17 21:14 ` [PATCH v19 01/27] x86/cpufeatures: Add Intel-defined SGX feature bit Jarkko Sakkinen
@ 2019-03-17 21:14 ` Jarkko Sakkinen
  2019-03-17 21:14 ` [PATCH v19 03/27] x86/msr: Add IA32_FEATURE_CONTROL.SGX_ENABLE definition Jarkko Sakkinen
                   ` (22 subsequent siblings)
  24 siblings, 0 replies; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-17 21:14 UTC (permalink / raw)
  To: x86, linux-sgx
  Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Jarkko Sakkinen

From: Sean Christopherson <sean.j.christopherson@intel.com>

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 <sean.j.christopherson@intel.com>
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
---
 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


^ permalink raw reply	[flat|nested] 92+ messages in thread

* [PATCH v19 03/27] x86/msr: Add IA32_FEATURE_CONTROL.SGX_ENABLE definition
  2019-03-17 21:14 [PATCH v19 00/27] Intel SGX1 support Jarkko Sakkinen
  2019-03-17 21:14 ` [PATCH v19 01/27] x86/cpufeatures: Add Intel-defined SGX feature bit Jarkko Sakkinen
  2019-03-17 21:14 ` [PATCH v19 02/27] x86/cpufeatures: Add SGX sub-features (as Linux-defined bits) Jarkko Sakkinen
@ 2019-03-17 21:14 ` Jarkko Sakkinen
  2019-03-17 21:14 ` [PATCH v19 04/27] x86/cpufeatures: Add Intel-defined SGX_LC feature bit Jarkko Sakkinen
                   ` (21 subsequent siblings)
  24 siblings, 0 replies; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-17 21:14 UTC (permalink / raw)
  To: x86, linux-sgx
  Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Jarkko Sakkinen

From: Sean Christopherson <sean.j.christopherson@intel.com>

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 <sean.j.christopherson@intel.com>
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
---
 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


^ permalink raw reply	[flat|nested] 92+ messages in thread

* [PATCH v19 04/27] x86/cpufeatures: Add Intel-defined SGX_LC feature bit
  2019-03-17 21:14 [PATCH v19 00/27] Intel SGX1 support Jarkko Sakkinen
                   ` (2 preceding siblings ...)
  2019-03-17 21:14 ` [PATCH v19 03/27] x86/msr: Add IA32_FEATURE_CONTROL.SGX_ENABLE definition Jarkko Sakkinen
@ 2019-03-17 21:14 ` Jarkko Sakkinen
  2019-03-17 21:14 ` [PATCH v19 05/27] x86/msr: Add SGX Launch Control MSR definitions Jarkko Sakkinen
                   ` (20 subsequent siblings)
  24 siblings, 0 replies; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-17 21:14 UTC (permalink / raw)
  To: x86, linux-sgx
  Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes, Kai Huang,
	Jarkko Sakkinen

From: Kai Huang <kai.huang@linux.intel.com>

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 <kai.huang@linux.intel.com>
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
---
 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


^ permalink raw reply	[flat|nested] 92+ messages in thread

* [PATCH v19 05/27] x86/msr: Add SGX Launch Control MSR definitions
  2019-03-17 21:14 [PATCH v19 00/27] Intel SGX1 support Jarkko Sakkinen
                   ` (3 preceding siblings ...)
  2019-03-17 21:14 ` [PATCH v19 04/27] x86/cpufeatures: Add Intel-defined SGX_LC feature bit Jarkko Sakkinen
@ 2019-03-17 21:14 ` Jarkko Sakkinen
  2019-03-17 21:14 ` [PATCH v19 06/27] x86/mm: x86/sgx: Add new 'PF_SGX' page fault error code bit Jarkko Sakkinen
                   ` (19 subsequent siblings)
  24 siblings, 0 replies; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-17 21:14 UTC (permalink / raw)
  To: x86, linux-sgx
  Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes, Haim Cohen,
	Jarkko Sakkinen

From: Sean Christopherson <sean.j.christopherson@intel.com>

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 <sean.j.christopherson@intel.com>
Co-developed-by: Haim Cohen <haim.cohen@intel.com>
Signed-off-by: Haim Cohen <haim.cohen@intel.com>
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
---
 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


^ permalink raw reply	[flat|nested] 92+ messages in thread

* [PATCH v19 06/27] x86/mm: x86/sgx: Add new 'PF_SGX' page fault error code bit
  2019-03-17 21:14 [PATCH v19 00/27] Intel SGX1 support Jarkko Sakkinen
                   ` (4 preceding siblings ...)
  2019-03-17 21:14 ` [PATCH v19 05/27] x86/msr: Add SGX Launch Control MSR definitions Jarkko Sakkinen
@ 2019-03-17 21:14 ` Jarkko Sakkinen
  2019-03-17 21:14 ` [PATCH v19 07/27] x86/mm: x86/sgx: Signal SIGSEGV for userspace #PFs w/ PF_SGX Jarkko Sakkinen
                   ` (18 subsequent siblings)
  24 siblings, 0 replies; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-17 21:14 UTC (permalink / raw)
  To: x86, linux-sgx
  Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Dave Hansen, Jarkko Sakkinen

From: Sean Christopherson <sean.j.christopherson@intel.com>

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 <dave.hansen@linux.intel.com>
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
---
 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


^ permalink raw reply	[flat|nested] 92+ messages in thread

* [PATCH v19 07/27] x86/mm: x86/sgx: Signal SIGSEGV for userspace #PFs w/ PF_SGX
  2019-03-17 21:14 [PATCH v19 00/27] Intel SGX1 support Jarkko Sakkinen
                   ` (5 preceding siblings ...)
  2019-03-17 21:14 ` [PATCH v19 06/27] x86/mm: x86/sgx: Add new 'PF_SGX' page fault error code bit Jarkko Sakkinen
@ 2019-03-17 21:14 ` Jarkko Sakkinen
  2019-03-18 17:15   ` Dave Hansen
  2019-03-17 21:14 ` [PATCH v19 08/27] x86/cpu/intel: Detect SGX support and update caps appropriately Jarkko Sakkinen
                   ` (17 subsequent siblings)
  24 siblings, 1 reply; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-17 21:14 UTC (permalink / raw)
  To: x86, linux-sgx
  Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Andy Lutomirski, Dave Hansen, Jarkko Sakkinen

From: Sean Christopherson <sean.j.christopherson@intel.com>

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 <luto@amacapital.net>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
---
 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


^ permalink raw reply	[flat|nested] 92+ messages in thread

* [PATCH v19 08/27] x86/cpu/intel: Detect SGX support and update caps appropriately
  2019-03-17 21:14 [PATCH v19 00/27] Intel SGX1 support Jarkko Sakkinen
                   ` (6 preceding siblings ...)
  2019-03-17 21:14 ` [PATCH v19 07/27] x86/mm: x86/sgx: Signal SIGSEGV for userspace #PFs w/ PF_SGX Jarkko Sakkinen
@ 2019-03-17 21:14 ` Jarkko Sakkinen
  2019-03-17 21:14 ` [PATCH v19 09/27] x86/sgx: Add ENCLS architectural error codes Jarkko Sakkinen
                   ` (16 subsequent siblings)
  24 siblings, 0 replies; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-17 21:14 UTC (permalink / raw)
  To: x86, linux-sgx
  Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Jarkko Sakkinen

From: Sean Christopherson <sean.j.christopherson@intel.com>

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 <sean.j.christopherson@intel.com>
Co-developed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
---
 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


^ permalink raw reply	[flat|nested] 92+ messages in thread

* [PATCH v19 09/27] x86/sgx: Add ENCLS architectural error codes
  2019-03-17 21:14 [PATCH v19 00/27] Intel SGX1 support Jarkko Sakkinen
                   ` (7 preceding siblings ...)
  2019-03-17 21:14 ` [PATCH v19 08/27] x86/cpu/intel: Detect SGX support and update caps appropriately Jarkko Sakkinen
@ 2019-03-17 21:14 ` Jarkko Sakkinen
  2019-03-17 21:14 ` [PATCH v19 10/27] x86/sgx: Add SGX1 and SGX2 architectural data structures Jarkko Sakkinen
                   ` (15 subsequent siblings)
  24 siblings, 0 replies; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-17 21:14 UTC (permalink / raw)
  To: x86, linux-sgx
  Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Jarkko Sakkinen

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 <jarkko.sakkinen@linux.intel.com>
Co-developed-by: Sean Christopherson <sean.j.christopherson@intel.com>
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
---
 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


^ permalink raw reply	[flat|nested] 92+ messages in thread

* [PATCH v19 10/27] x86/sgx: Add SGX1 and SGX2 architectural data structures
  2019-03-17 21:14 [PATCH v19 00/27] Intel SGX1 support Jarkko Sakkinen
                   ` (8 preceding siblings ...)
  2019-03-17 21:14 ` [PATCH v19 09/27] x86/sgx: Add ENCLS architectural error codes Jarkko Sakkinen
@ 2019-03-17 21:14 ` Jarkko Sakkinen
  2019-03-17 21:14 ` [PATCH v19 11/27] x86/sgx: Add definitions for SGX's CPUID leaf and variable sub-leafs Jarkko Sakkinen
                   ` (14 subsequent siblings)
  24 siblings, 0 replies; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-17 21:14 UTC (permalink / raw)
  To: x86, linux-sgx
  Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Jarkko Sakkinen

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 <jarkko.sakkinen@linux.intel.com>
Co-developed-by: Sean Christopherson <sean.j.christopherson@intel.com>
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
---
 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


^ permalink raw reply	[flat|nested] 92+ messages in thread

* [PATCH v19 11/27] x86/sgx: Add definitions for SGX's CPUID leaf and variable sub-leafs
  2019-03-17 21:14 [PATCH v19 00/27] Intel SGX1 support Jarkko Sakkinen
                   ` (9 preceding siblings ...)
  2019-03-17 21:14 ` [PATCH v19 10/27] x86/sgx: Add SGX1 and SGX2 architectural data structures Jarkko Sakkinen
@ 2019-03-17 21:14 ` Jarkko Sakkinen
  2019-03-17 21:14 ` [PATCH v19 12/27] x86/sgx: Enumerate and track EPC sections Jarkko Sakkinen
                   ` (13 subsequent siblings)
  24 siblings, 0 replies; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-17 21:14 UTC (permalink / raw)
  To: x86, linux-sgx
  Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Jarkko Sakkinen

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 <jarkko.sakkinen@linux.intel.com>
Co-developed-by: Sean Christopherson <sean.j.christopherson@intel.com>
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
---
 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


^ permalink raw reply	[flat|nested] 92+ messages in thread

* [PATCH v19 12/27] x86/sgx: Enumerate and track EPC sections
  2019-03-17 21:14 [PATCH v19 00/27] Intel SGX1 support Jarkko Sakkinen
                   ` (10 preceding siblings ...)
  2019-03-17 21:14 ` [PATCH v19 11/27] x86/sgx: Add definitions for SGX's CPUID leaf and variable sub-leafs Jarkko Sakkinen
@ 2019-03-17 21:14 ` Jarkko Sakkinen
  2019-03-18 19:50   ` Sean Christopherson
  2019-03-17 21:14 ` [PATCH v19 13/27] x86/sgx: Add wrappers for ENCLS leaf functions Jarkko Sakkinen
                   ` (12 subsequent siblings)
  24 siblings, 1 reply; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-17 21:14 UTC (permalink / raw)
  To: x86, linux-sgx
  Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Jarkko Sakkinen, Suresh Siddha

From: Sean Christopherson <sean.j.christopherson@intel.com>

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 <sean.j.christopherson@intel.com>
Co-developed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Co-developed-by: Suresh Siddha <suresh.b.siddha@intel.com>
Signed-off-by: Suresh Siddha <suresh.b.siddha@intel.com>
Co-developed-by: Serge Ayoun <serge.ayoun@intel.com>
Signed-off-by: Serge Ayoun <serge.ayoun@intel.com>
---
 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, &section->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(&section->page_list)) {
+		page = list_first_entry(&section->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(&section->lock);
+	INIT_LIST_HEAD(&section->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


^ permalink raw reply	[flat|nested] 92+ messages in thread

* [PATCH v19 13/27] x86/sgx: Add wrappers for ENCLS leaf functions
  2019-03-17 21:14 [PATCH v19 00/27] Intel SGX1 support Jarkko Sakkinen
                   ` (11 preceding siblings ...)
  2019-03-17 21:14 ` [PATCH v19 12/27] x86/sgx: Enumerate and track EPC sections Jarkko Sakkinen
@ 2019-03-17 21:14 ` Jarkko Sakkinen
  2019-03-19 19:59   ` Sean Christopherson
  2019-03-17 21:14 ` [PATCH v19 16/27] x86/sgx: Add the Linux SGX Enclave Driver Jarkko Sakkinen
                   ` (11 subsequent siblings)
  24 siblings, 1 reply; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-17 21:14 UTC (permalink / raw)
  To: x86, linux-sgx
  Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Jarkko Sakkinen

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 <jarkko.sakkinen@linux.intel.com>
Co-developed-by: Sean Christopherson <sean.j.christopherson@intel.com>
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
---
 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


^ permalink raw reply	[flat|nested] 92+ messages in thread

* [PATCH v19 16/27] x86/sgx: Add the Linux SGX Enclave Driver
  2019-03-17 21:14 [PATCH v19 00/27] Intel SGX1 support Jarkko Sakkinen
                   ` (12 preceding siblings ...)
  2019-03-17 21:14 ` [PATCH v19 13/27] x86/sgx: Add wrappers for ENCLS leaf functions Jarkko Sakkinen
@ 2019-03-17 21:14 ` Jarkko Sakkinen
  2019-03-19 21:19   ` Sean Christopherson
  2019-03-19 23:00   ` Sean Christopherson
  2019-03-17 21:14 ` [PATCH v19 17/27] x86/sgx: Add provisioning Jarkko Sakkinen
                   ` (10 subsequent siblings)
  24 siblings, 2 replies; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-17 21:14 UTC (permalink / raw)
  To: x86, linux-sgx
  Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Jarkko Sakkinen, Suresh Siddha

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 <jarkko.sakkinen@linux.intel.com>
Co-developed-by: Sean Christopherson <sean.j.christopherson@intel.com>
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
Co-developed-by: Serge Ayoun <serge.ayoun@intel.com>
Signed-off-by: Serge Ayoun <serge.ayoun@intel.com>
Co-developed-by: Shay Katz-zamir <shay.katz-zamir@intel.com>
Signed-off-by: Shay Katz-zamir <shay.katz-zamir@intel.com>
Co-developed-by: Suresh Siddha <suresh.b.siddha@intel.com>
Signed-off-by: Suresh Siddha <suresh.b.siddha@intel.com>
---
 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:tlewis@mindspring.com>
 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(&current->mm->mmap_sem);
+	ret = sgx_encl_find(current->mm, secs->base, &vma);
+	if (ret != -ENOENT) {
+		if (!ret)
+			ret = -EINVAL;
+		up_read(&current->mm->mmap_sem);
+		return ret;
+	}
+
+	if (vma->vm_start != secs->base ||
+	    vma->vm_end != (secs->base + secs->size) ||
+	    vma->vm_pgoff != 0) {
+		ret = -EINVAL;
+		up_read(&current->mm->mmap_sem);
+		return ret;
+	}
+
+	vma->vm_private_data = encl;
+	up_read(&current->mm->mmap_sem);
+	return 0;
+}
+
+/**
+ * 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 <jarkko.sakkinen@linux.intel.com>");
+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


^ permalink raw reply	[flat|nested] 92+ messages in thread

* [PATCH v19 17/27] x86/sgx: Add provisioning
  2019-03-17 21:14 [PATCH v19 00/27] Intel SGX1 support Jarkko Sakkinen
                   ` (13 preceding siblings ...)
  2019-03-17 21:14 ` [PATCH v19 16/27] x86/sgx: Add the Linux SGX Enclave Driver Jarkko Sakkinen
@ 2019-03-17 21:14 ` Jarkko Sakkinen
  2019-03-19 20:09   ` Sean Christopherson
                     ` (2 more replies)
  2019-03-17 21:14 ` [PATCH v19 19/27] x86/sgx: ptrace() support for the SGX driver Jarkko Sakkinen
                   ` (9 subsequent siblings)
  24 siblings, 3 replies; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-17 21:14 UTC (permalink / raw)
  To: x86, linux-sgx
  Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Jarkko Sakkinen, James Morris, Serge E . Hallyn,
	linux-security-module

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, &params);

Cc: James Morris <jmorris@namei.org>
Cc: Serge E. Hallyn <serge@hallyn.com>
Cc: linux-security-module@vger.kernel.org
Suggested-by: Andy Lutomirski <luto@kernel.org>
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
---
 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


^ permalink raw reply	[flat|nested] 92+ messages in thread

* [PATCH v19 19/27] x86/sgx: ptrace() support for the SGX driver
  2019-03-17 21:14 [PATCH v19 00/27] Intel SGX1 support Jarkko Sakkinen
                   ` (14 preceding siblings ...)
  2019-03-17 21:14 ` [PATCH v19 17/27] x86/sgx: Add provisioning Jarkko Sakkinen
@ 2019-03-17 21:14 ` Jarkko Sakkinen
  2019-03-19 22:22   ` Sean Christopherson
  2019-03-17 21:14 ` [PATCH v19 20/27] x86/vdso: Add support for exception fixup in vDSO functions Jarkko Sakkinen
                   ` (8 subsequent siblings)
  24 siblings, 1 reply; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-17 21:14 UTC (permalink / raw)
  To: x86, linux-sgx
  Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Jarkko Sakkinen

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 <jarkko.sakkinen@linux.intel.com>
---
 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


^ permalink raw reply	[flat|nested] 92+ messages in thread

* [PATCH v19 20/27] x86/vdso: Add support for exception fixup in vDSO functions
  2019-03-17 21:14 [PATCH v19 00/27] Intel SGX1 support Jarkko Sakkinen
                   ` (15 preceding siblings ...)
  2019-03-17 21:14 ` [PATCH v19 19/27] x86/sgx: ptrace() support for the SGX driver Jarkko Sakkinen
@ 2019-03-17 21:14 ` Jarkko Sakkinen
  2019-03-17 21:14 ` [PATCH v19 21/27] x86/fault: Add helper function to sanitize error code Jarkko Sakkinen
                   ` (7 subsequent siblings)
  24 siblings, 0 replies; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-17 21:14 UTC (permalink / raw)
  To: x86, linux-sgx
  Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Andy Lutomirski, Jarkko Sakkinen, Dave Hansen

From: Sean Christopherson <sean.j.christopherson@intel.com>

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 <luto@amacapital.net>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Josh Triplett <josh@joshtriplett.org>
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
---
 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


^ permalink raw reply	[flat|nested] 92+ messages in thread

* [PATCH v19 21/27] x86/fault: Add helper function to sanitize error code
  2019-03-17 21:14 [PATCH v19 00/27] Intel SGX1 support Jarkko Sakkinen
                   ` (16 preceding siblings ...)
  2019-03-17 21:14 ` [PATCH v19 20/27] x86/vdso: Add support for exception fixup in vDSO functions Jarkko Sakkinen
@ 2019-03-17 21:14 ` Jarkko Sakkinen
  2019-03-17 21:14 ` [PATCH v19 22/27] x86/fault: Attempt to fixup unhandled #PF in vDSO before signaling Jarkko Sakkinen
                   ` (6 subsequent siblings)
  24 siblings, 0 replies; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-17 21:14 UTC (permalink / raw)
  To: x86, linux-sgx
  Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes

From: Sean Christopherson <sean.j.christopherson@intel.com>

...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 <sean.j.christopherson@intel.com>
---
 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


^ permalink raw reply	[flat|nested] 92+ messages in thread

* [PATCH v19 22/27] x86/fault: Attempt to fixup unhandled #PF in vDSO before signaling
  2019-03-17 21:14 [PATCH v19 00/27] Intel SGX1 support Jarkko Sakkinen
                   ` (17 preceding siblings ...)
  2019-03-17 21:14 ` [PATCH v19 21/27] x86/fault: Add helper function to sanitize error code Jarkko Sakkinen
@ 2019-03-17 21:14 ` Jarkko Sakkinen
  2019-03-17 21:14 ` [PATCH v19 23/27] x86/traps: Attempt to fixup exceptions " Jarkko Sakkinen
                   ` (5 subsequent siblings)
  24 siblings, 0 replies; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-17 21:14 UTC (permalink / raw)
  To: x86, linux-sgx
  Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Andy Lutomirski, Jarkko Sakkinen, Dave Hansen

From: Sean Christopherson <sean.j.christopherson@intel.com>

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 <luto@amacapital.net>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Josh Triplett <josh@joshtriplett.org>
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
---
 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


^ permalink raw reply	[flat|nested] 92+ messages in thread

* [PATCH v19 23/27] x86/traps: Attempt to fixup exceptions in vDSO before signaling
  2019-03-17 21:14 [PATCH v19 00/27] Intel SGX1 support Jarkko Sakkinen
                   ` (18 preceding siblings ...)
  2019-03-17 21:14 ` [PATCH v19 22/27] x86/fault: Attempt to fixup unhandled #PF in vDSO before signaling Jarkko Sakkinen
@ 2019-03-17 21:14 ` " Jarkko Sakkinen
  2019-03-17 21:14 ` [PATCH v19 25/27] x86/sgx: SGX documentation Jarkko Sakkinen
                   ` (4 subsequent siblings)
  24 siblings, 0 replies; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-17 21:14 UTC (permalink / raw)
  To: x86, linux-sgx
  Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Andy Lutomirski, Jarkko Sakkinen, Dave Hansen

From: Sean Christopherson <sean.j.christopherson@intel.com>

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 <luto@amacapital.net>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Josh Triplett <josh@joshtriplett.org>
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
---
 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


^ permalink raw reply	[flat|nested] 92+ messages in thread

* [PATCH v19 25/27] x86/sgx: SGX documentation
  2019-03-17 21:14 [PATCH v19 00/27] Intel SGX1 support Jarkko Sakkinen
                   ` (19 preceding siblings ...)
  2019-03-17 21:14 ` [PATCH v19 23/27] x86/traps: Attempt to fixup exceptions " Jarkko Sakkinen
@ 2019-03-17 21:14 ` Jarkko Sakkinen
  2019-03-20 17:14   ` Sean Christopherson
  2019-03-17 21:14 ` [PATCH v19 26/27] selftests/x86: Add a selftest for SGX Jarkko Sakkinen
                   ` (3 subsequent siblings)
  24 siblings, 1 reply; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-17 21:14 UTC (permalink / raw)
  To: x86, linux-sgx
  Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Jarkko Sakkinen

[-- Warning: decoded text below may be mangled --]
[-- Attachment #0: Type: text/plain; charset=a, Size: 11949 bytes --]

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 <jarkko.sakkinen@linux.intel.com>
Co-developed-by: Sean Christopherson <sean.j.christopherson@intel.com>
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
---
 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


^ permalink raw reply	[flat|nested] 92+ messages in thread

* [PATCH v19 26/27] selftests/x86: Add a selftest for SGX
  2019-03-17 21:14 [PATCH v19 00/27] Intel SGX1 support Jarkko Sakkinen
                   ` (20 preceding siblings ...)
  2019-03-17 21:14 ` [PATCH v19 25/27] x86/sgx: SGX documentation Jarkko Sakkinen
@ 2019-03-17 21:14 ` Jarkko Sakkinen
  2019-03-17 21:14 ` [PATCH v19 27/27] x86/sgx: Update MAINTAINERS Jarkko Sakkinen
                   ` (2 subsequent siblings)
  24 siblings, 0 replies; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-17 21:14 UTC (permalink / raw)
  To: x86, linux-sgx
  Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Jarkko Sakkinen

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 <jarkko.sakkinen@linux.intel.com>
---
 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


^ permalink raw reply	[flat|nested] 92+ messages in thread

* [PATCH v19 27/27] x86/sgx: Update MAINTAINERS
  2019-03-17 21:14 [PATCH v19 00/27] Intel SGX1 support Jarkko Sakkinen
                   ` (21 preceding siblings ...)
  2019-03-17 21:14 ` [PATCH v19 26/27] selftests/x86: Add a selftest for SGX Jarkko Sakkinen
@ 2019-03-17 21:14 ` Jarkko Sakkinen
  2019-03-19 17:12   ` Sean Christopherson
       [not found] ` <20190317211456.13927-19-jarkko.sakkinen@linux.intel.com>
  2019-03-19 23:41 ` [PATCH v19 00/27] Intel SGX1 support Sean Christopherson
  24 siblings, 1 reply; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-17 21:14 UTC (permalink / raw)
  To: x86, linux-sgx
  Cc: akpm, dave.hansen, sean.j.christopherson, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Jarkko Sakkinen

Add the maintainer information for the SGX subsystem.

Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
---
 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:	linux-gpio@vger.kernel.org
 S:	Maintained
 F:	drivers/gpio/gpio-intel-mid.c
 
+INTEL SGX
+M:	Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
+M:	Sean Christopherson <sean.j.christopherson@intel.com>
+L:	linux-sgx@vger.kernel.org
+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 <georgi.djakov@linaro.org>
 S:	Maintained
-- 
2.19.1


^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 07/27] x86/mm: x86/sgx: Signal SIGSEGV for userspace #PFs w/ PF_SGX
  2019-03-17 21:14 ` [PATCH v19 07/27] x86/mm: x86/sgx: Signal SIGSEGV for userspace #PFs w/ PF_SGX Jarkko Sakkinen
@ 2019-03-18 17:15   ` Dave Hansen
  2019-03-18 19:53     ` Sean Christopherson
  0 siblings, 1 reply; 92+ messages in thread
From: Dave Hansen @ 2019-03-18 17:15 UTC (permalink / raw)
  To: Jarkko Sakkinen, x86, linux-sgx
  Cc: akpm, sean.j.christopherson, nhorman, npmccallum, serge.ayoun,
	shay.katz-zamir, haitao.huang, andriy.shevchenko, tglx,
	kai.svahn, bp, josh, luto, kai.huang, rientjes, Andy Lutomirski,
	Dave Hansen

On 3/17/19 2:14 PM, Jarkko Sakkinen wrote:
> +	/*
> +	 * 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;

A sanitized error_code gets passed up to userspace.  Is that how
userspace figures out that this was a PF_SGX fault instead of something
more routine?


^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 12/27] x86/sgx: Enumerate and track EPC sections
  2019-03-17 21:14 ` [PATCH v19 12/27] x86/sgx: Enumerate and track EPC sections Jarkko Sakkinen
@ 2019-03-18 19:50   ` Sean Christopherson
  2019-03-21 14:40     ` Jarkko Sakkinen
  0 siblings, 1 reply; 92+ messages in thread
From: Sean Christopherson @ 2019-03-18 19:50 UTC (permalink / raw)
  To: Jarkko Sakkinen
  Cc: x86, linux-sgx, akpm, dave.hansen, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Suresh Siddha

On Sun, Mar 17, 2019 at 11:14:41PM +0200, Jarkko Sakkinen wrote:
> From: Sean Christopherson <sean.j.christopherson@intel.com>
> 
> 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 <sean.j.christopherson@intel.com>
> Co-developed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
> Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
> Co-developed-by: Suresh Siddha <suresh.b.siddha@intel.com>
> Signed-off-by: Suresh Siddha <suresh.b.siddha@intel.com>
> Co-developed-by: Serge Ayoun <serge.ayoun@intel.com>
> Signed-off-by: Serge Ayoun <serge.ayoun@intel.com>
> ---
>  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.

"enclave" is used before it's defined.  And the second sentence could be
tweaked to make it explicitly clear that hardware disallows cross-enclave
access.  E.g.:

	Intel(R) SGX is a set of CPU instructions that can be used by
	applications to set aside private regions of code and data, referred
	to as enclaves.  An enclave's private memory can only be accessed by
	code running within the enclave.  Accesses from outside the enclave,
	including other enclaves, are disallowed by hardware.

> +
> +	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.

This second paragraph can probably be dropped altogether.  A reader won't
know what PRMRR means unless they're already familiar with SGX.  And the
PRMRR+MEE implementation is not architectural, i.e. future hardware could
support EPC through some other mechanism.  SGX does more than just encrypt
memory, covering those details is probably best left to intel_sgx.rst.

> +
> +	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];

Dynamically allocating sgx_epc_sections isn't exactly difficult, and
AFAICT the static allocation is the primary motivation for capping
SGX_MAX_EPC_SECTIONS at such a low value (8).  I still think it makes
sense to define SGX_MAX_EPC_SECTIONS so that the section number can
be embedded in the offset, along with flags.  But the max can be
significantly higher, e.g. using 7 bits to support 128 sections.

I realize hardware is highly unlikely to have more than 8 sections, at
least for the near future, but IMO the small amount of extra complexity
is worth having a bit of breathing room.

> +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, &section->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(&section->page_list)) {
> +		page = list_first_entry(&section->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(&section->lock);
> +	INIT_LIST_HEAD(&section->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);
> +	}

Not sure if this is the correct location, but at some point the kernel
needs to sanitize the EPC during init.  EPC pages may be in an unknown
state, e.g. after kexec(), which will cause all manner of faults and
warnings.  Maybe the best approach is to sanitize on-demand, e.g. suppress
the first WARN due to unexpected ENCLS failure and purge the EPC at that
time.  The downside of that approach is that exposing EPC to a guest would
need to implement its own sanitization flow.

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

This should probably be "continue" rather than "return -ENODEV".  SGX
can still be used in the (extremely) unlikely event that there is usable
EPC and some unknown memory type enumerated.

> +		}
> +		if (i == SGX_MAX_EPC_SECTIONS) {
> +			pr_warn("sgx: More than "
> +				__stringify(SGX_MAX_EPC_SECTIONS)
> +				" EPC sections\n");

This isn't a very helpful message, e.g. it doesn't even imply that the
kernel is ignoring EPC sections.  It'd also be helpful to display the
sections that are being ignored.  Might also warrant pr_err() since it
means system resources are being ignored.

E.g.:

#define SGX_ARBITRARY_LOOP_TERMINATOR   1000

	for (i = 0; i < SGX_ARBITRARY_LOOP_TERMINATOR; i++) {
		...

		if (i >= SGX_MAX_EPC_SECTIONS) {
			pr_err("sgx: Reached max number of EPC sections (%u), "
			       "ignoring section 0x%llx-0x%llx\n",
			       pa, pa + size - 1);
		}
	}

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

Similar to encountering unknown sections, any reason why we wouldn't
continue here and use whatever EPC was successfuly initialized?

> +		}
> +
> +		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
> 

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 07/27] x86/mm: x86/sgx: Signal SIGSEGV for userspace #PFs w/ PF_SGX
  2019-03-18 17:15   ` Dave Hansen
@ 2019-03-18 19:53     ` Sean Christopherson
  0 siblings, 0 replies; 92+ messages in thread
From: Sean Christopherson @ 2019-03-18 19:53 UTC (permalink / raw)
  To: Dave Hansen
  Cc: Jarkko Sakkinen, x86, linux-sgx, akpm, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Andy Lutomirski, Dave Hansen

On Mon, Mar 18, 2019 at 10:15:18AM -0700, Dave Hansen wrote:
> On 3/17/19 2:14 PM, Jarkko Sakkinen wrote:
> > +	/*
> > +	 * 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;
> 
> A sanitized error_code gets passed up to userspace.  Is that how
> userspace figures out that this was a PF_SGX fault instead of something
> more routine?

Yes.  Technically userspace can survive without PF_SGX, e.g. assume that
SIGSEGV on EENTER itself indicates an EPCM fault, but the explicit flag
does make things easier and cleaner.

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 27/27] x86/sgx: Update MAINTAINERS
  2019-03-17 21:14 ` [PATCH v19 27/27] x86/sgx: Update MAINTAINERS Jarkko Sakkinen
@ 2019-03-19 17:12   ` Sean Christopherson
  2019-03-21 14:42     ` Jarkko Sakkinen
  0 siblings, 1 reply; 92+ messages in thread
From: Sean Christopherson @ 2019-03-19 17:12 UTC (permalink / raw)
  To: Jarkko Sakkinen
  Cc: x86, linux-sgx, akpm, dave.hansen, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes

On Sun, Mar 17, 2019 at 11:14:56PM +0200, Jarkko Sakkinen wrote:
> Add the maintainer information for the SGX subsystem.
> 
> Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
> ---
>  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:	linux-gpio@vger.kernel.org
>  S:	Maintained
>  F:	drivers/gpio/gpio-intel-mid.c
>  
> +INTEL SGX

Alphabetically, this should slot in between "INTEL RDMA RNIC DRIVER" and
"INTEL TELEMETRY DRIVER".

Tangentially related, is it worth sending a patch to rename "INTEL-MID GPIO DRIVER"
to "INTEL MID GPIO DRIVER" and drop "(R)" from "INTEL(R) TRACE HUB"
and "INTEL(R) TRUSTED EXECUTION TECHNOLOGY (TXT)"?  They're silly
renegades that muck up the ordering.

> +M:	Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
> +M:	Sean Christopherson <sean.j.christopherson@intel.com>
> +L:	linux-sgx@vger.kernel.org
> +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

arch/x86/include/asm/sgx.h doesn't exist in this version of the series.

> +F:	arch/x86/include/uapi/asm/sgx.h
> +F:	arch/x86/kernel/cpu/sgx/*

The trailing asterisk is unnecessary and wrong.  From MAINTAINER's docs:

        F: Files and directories with wildcard patterns.
           A trailing slash includes all files and subdirectory files.
           F:   drivers/net/    all files in and below drivers/net
           F:   drivers/net/*   all files in drivers/net, but not below

> +K:	\bSGX_
> +
>  INTERCONNECT API
>  M:	Georgi Djakov <georgi.djakov@linaro.org>
>  S:	Maintained
> -- 
> 2.19.1
> 

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 13/27] x86/sgx: Add wrappers for ENCLS leaf functions
  2019-03-17 21:14 ` [PATCH v19 13/27] x86/sgx: Add wrappers for ENCLS leaf functions Jarkko Sakkinen
@ 2019-03-19 19:59   ` Sean Christopherson
  2019-03-21 14:51     ` Jarkko Sakkinen
  0 siblings, 1 reply; 92+ messages in thread
From: Sean Christopherson @ 2019-03-19 19:59 UTC (permalink / raw)
  To: Jarkko Sakkinen
  Cc: x86, linux-sgx, akpm, dave.hansen, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes

On Sun, Mar 17, 2019 at 11:14:42PM +0200, Jarkko Sakkinen wrote:
> 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 <jarkko.sakkinen@linux.intel.com>
> Co-developed-by: Sean Christopherson <sean.j.christopherson@intel.com>
> Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
> ---
>  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.

"conflict" is a poor word choice.  The SDM's refers to EPC conflicts as
trying to concurrently access an EPC page from multiple logical CPUs.

Maybe "EPCM violation" or simply "EPCM fault"?

> + *
> + * Return: true if there was a fault other than an EPCM conflict
> + */
> +bool encls_failed(int ret)

I really dislike this name.  IMO, "encls_failed" is inaccurate and to some
extent an unnecessary abstraction.  Regardless of why it faulted, the
ENCLS instruction "failed".  Just because the fault originated in the EPCM
doesn't mean the instruction magically succeeded.

What if we inverted the logic, i.e. to identify EPCM violations?  And
rename encls_fault() to is_encls_fault(), and encls_returned_code()
to is_sgx_error_code().  E.g.:

bool is_epcm_violation(int ret)
{
	return is_encls_fault(ret) && ENCLS_TRAPNR(ret) == epcm_trapnr;
}

Then the usage becomes:

	if (is_sgx_error_code(ret) ||
	    (is_encls_fault(ret) && !is_epcm_violation(ret))
		blah blah blah

The usage is more verbose, but explicitly clear.

> +{
> +	int epcm_trapnr = boot_cpu_has(X86_FEATURE_SGX2) ?
> +			  X86_TRAP_PF : X86_TRAP_GP;

epcm_trapnr should be calculated once during init.

> +
> +	return encls_faulted(ret) && ENCLS_TRAPNR(ret) != epcm_trapnr;
> +}

Why isn't this function inlined in sgx.h?

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

I honestly can't remember, is there a reason ENCLS_TRAPNR is still a macro
and not an inline function?

> +
> +/* Issue a WARN() about an ENCLS leaf. */

If you're going to bother with a comment, might as well document the
interesting part, i.e. why we print both the decimal and hexidecimal
formats, which is the reason this macro exists at all.

> +#define ENCLS_WARN(r, name) {						\

Taking the warn condition and the raw error code in a single parameter
makes the call sites ugly, e.g.

	if (ret) {
		if (encls_failed(ret))
			ENCLS_WARN(ret, "EADD");
		return false;
	}

If the macro is instead something like:

#define ENCLS_WARN(do_warn, r, name) \
	WARN(w, "sgx: %s returned %d (0x%x)\n", (name), (r), (r); \

then we can drop the do-while wrapping and the callers are a little less
ugly, e.g.:

	if (ret) {
		ENCLS_WARN(encls_failed(ret), ret, "EADD");
		return ret;
	}


Or with the is_epcm_violation() variant:

	if (ret) {
		ENCLS_WARN(is_encls_fault(ret) && !is_epcm_violation(ret),
			   ret, "EADD");
		return ret;
	}

> +	do {								\
> +		int _r = (r);						\
> +		WARN(_r, "sgx: %s returned %d (0x%x)\n", (name), _r,	\
> +		     _r);						\

There's no need to wrap this, just add spaces before the '\'.

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

Nit: IMO checking for non-zero ret should be first, both from a readability
     perspective and from a code generation perspective.

> +}
> +
> +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
> 

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 17/27] x86/sgx: Add provisioning
  2019-03-17 21:14 ` [PATCH v19 17/27] x86/sgx: Add provisioning Jarkko Sakkinen
@ 2019-03-19 20:09   ` Sean Christopherson
  2019-03-21  2:08     ` Huang, Kai
  2019-03-21 14:30     ` Jarkko Sakkinen
  2019-03-21 14:38   ` Nathaniel McCallum
  2019-03-21 16:50   ` Andy Lutomirski
  2 siblings, 2 replies; 92+ messages in thread
From: Sean Christopherson @ 2019-03-19 20:09 UTC (permalink / raw)
  To: Jarkko Sakkinen
  Cc: x86, linux-sgx, akpm, dave.hansen, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	James Morris, Serge E . Hallyn, linux-security-module

On Sun, Mar 17, 2019 at 11:14:46PM +0200, Jarkko Sakkinen wrote:
> 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, &params);
> 
> Cc: James Morris <jmorris@namei.org>
> Cc: Serge E. Hallyn <serge@hallyn.com>
> Cc: linux-security-module@vger.kernel.org
> Suggested-by: Andy Lutomirski <luto@kernel.org>
> Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
> ---
>  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.

With the @data change (see below), this becomes something like:

 * Allow the enclave to request the attribute managed by the SGX security file
 * pointed at 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)

This should be:

	if (!attribute_file)
		return -EINVAL;

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

A cleanr approach would be to pass SGX_ATTR_PROVISIONKEY via @data to
securityfs_create_file().  Then you don't need to define dummy file_ops
for each file, i.e. a generic sgx_sec_fs_ops would suffice for the above
check.  And you don't have this weird hardcoding of the provision bit.

E.g.:

	if (attribute_file->f_op != &sgx_sec_fs_fops) {
		ret = -EINVAL;
		goto out;
	}

	ret = sgx_encl_get(params->addr, &encl);
	if (ret)
		goto out;

	encl->allowed_attributes |= (u64)attribute_file->private_data;

Since SGX doesn't support 32-bit builds we don't even need to worry about
the (very distant) future where SGX defines bits in the 63:32 range.


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

Per above, pass SGX_ATTR_PROVISIONKEY instead of NULL.

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

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 16/27] x86/sgx: Add the Linux SGX Enclave Driver
  2019-03-17 21:14 ` [PATCH v19 16/27] x86/sgx: Add the Linux SGX Enclave Driver Jarkko Sakkinen
@ 2019-03-19 21:19   ` Sean Christopherson
  2019-03-21 15:51     ` Jarkko Sakkinen
  2019-03-19 23:00   ` Sean Christopherson
  1 sibling, 1 reply; 92+ messages in thread
From: Sean Christopherson @ 2019-03-19 21:19 UTC (permalink / raw)
  To: Jarkko Sakkinen
  Cc: x86, linux-sgx, akpm, dave.hansen, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Suresh Siddha

On Sun, Mar 17, 2019 at 11:14:45PM +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 <jarkko.sakkinen@linux.intel.com>
> Co-developed-by: Sean Christopherson <sean.j.christopherson@intel.com>
> Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
> Co-developed-by: Serge Ayoun <serge.ayoun@intel.com>
> Signed-off-by: Serge Ayoun <serge.ayoun@intel.com>
> Co-developed-by: Shay Katz-zamir <shay.katz-zamir@intel.com>
> Signed-off-by: Shay Katz-zamir <shay.katz-zamir@intel.com>
> Co-developed-by: Suresh Siddha <suresh.b.siddha@intel.com>
> Signed-off-by: Suresh Siddha <suresh.b.siddha@intel.com>
> ---
>  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:tlewis@mindspring.com>
>  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
> -

Spurious whitespace change.

>  	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

IMO we should get rid of SGX_POWER_LOST_ENCLAVE and the SUSPEND flag.

  - Userspace needs to handle -EFAULT cleanly even if we hook into
    suspend/hibernate via sgx_encl_pm_notifier(), e.g. to handle virtual
    machine migration.
  - In the event that suspend is canceled after sgx_encl_pm_notifier()
    runs, we'll have prematurely invalidated the enclave.
  - Invalidating all enclaves could be slow on a system with GBs of EPC,
    i.e. probably not the best thing to do in the suspend path.

Removing SGX_POWER_LOST_ENCLAVE means we can drop all of the pm_notifier()
code, which will likely save us a bit of maintenance down the line.

> +
> +/**
> + * 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;
> +};

...

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

Yuck.  If we remove the driver specific Makefile then we can eliminate
the "../" prefix here.  E.g. in the main SGX Makefile:

obj-$(CONFIG_INTEL_SGX_DRIVER) += driver/main.o driver/ioctl.o

> +#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

...

> +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) {

Should we validate page_type itself?

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

...

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

These can be on a single line, e.g.: "int ret, i, 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;
> +}

...

> 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 <jarkko.sakkinen@linux.intel.com>");
> +MODULE_LICENSE("Dual BSD/GPL");

...

> +static const struct file_operations sgx_ctrl_fops = {

Why sgx_ctrl_fops instead of e.g. sgx_dev_fops, sgx_device_fops,
sgx_driver_fops, etc...

> +	.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),
> +	},
> +};

Utilizing the platform driver is unnecessary, adds complexity and IMO is
flat out wrong given the current direction of implementing SGX as a
full-blooded architectural feature.

  - All hardware information is readily available via CPUID
  - arch_initcall hooks obviates the need for ACPI autoprobe
  - EPC manager assumes it has full control over all EPC, i.e. EPC
    sections are not managed as independent "devices"
  - BIOS will enumerate a single ACPI entry regardless of the number
    of EPC sections, i.e. the ACPI entry is *only* useful for probing
  - Userspace driver matches the EPC device, but doesn't actually
    "own" the EPC

> +
> +static int __init sgx_drv_subsys_init(void)
> +{
> +     int ret;
> +
> +     ret = bus_register(&sgx_bus_type);

Do we really need a bus/class?  Allocating a chrdev region also seems like
overkill.  At this point there is exactly one SGX device, and while there
is a pretty good chance we'll end up with a virtualization specific device
for exposing EPC to guest, there's no guarantee said device will be SGX
specific.  Using a dynamic miscdevice would eliminate a big chunk of code.

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

Failure to allocate memory for one user of the enclave shouldn't kill
the enclave, e.g. failing during fork() shouldn't kill the enclave in
the the parent.  And marking an enclave dead without holding its lock
is all kinds of bad.

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

Not that it truly matters, but list_add() is the only thing that needs
to be protected with the spinlock, everything else can be done ahead of
time.

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

Isn't this unnecessary?  sgx_vma_open() had to have been called on this
VMA, otherwise we wouldn't be here.

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

Again, is retrieving the sgx_encl_mm necessary?  Isn't it impossible to
get here unless the vma has an reference to the enclave?  I.e. it should
be impossible to fault before open() or after close().

Dropping sgx_encl_get_mm() from both sgx_vma_close() and sgx_vma_fault()
would mean sgx_vma_open() is the only user (as of this patch), i.e. can
open code walking through the list and wouldn't need to confusing
mmdrop().  It'd be even cleaner if we can use an RCU list.

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

Any reason for while()+list_first_entry() instead of list_for_each_entry_safe()?

> +		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__);

I'm pretty sure we can simply use an RCU list.  Iteration is then simply
list_for_each_entry_rcu() instead of this custom walker.  Peeking into the
future, we don't need perfect accuracy for aging pages, e.g. working with
a stale snapshot is perfectly ok.

Zapping PTEs does need 100% accuracy, but that will be guaranteed via the
SGX_ENCL_PAGE_RECLAIMED flag.  Any mm added to the list after we start
zapping will be rejected by the page fault handler, i.e. won't be able
to reference the page being reclaimed.

> +
> +	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);
> +}

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 18/27] x86/sgx: Add swapping code to the core and SGX driver
       [not found] ` <20190317211456.13927-19-jarkko.sakkinen@linux.intel.com>
@ 2019-03-19 22:09   ` Sean Christopherson
  2019-03-21 14:59     ` Jarkko Sakkinen
  0 siblings, 1 reply; 92+ messages in thread
From: Sean Christopherson @ 2019-03-19 22:09 UTC (permalink / raw)
  To: Jarkko Sakkinen
  Cc: x86, linux-sgx, akpm, dave.hansen, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes

On Sun, Mar 17, 2019 at 11:14:47PM +0200, Jarkko Sakkinen wrote:

...

> ---
>  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.
> +

IMO this is an implementation detail that belongs in the docs, not in
the kconfig description.  At the least, it should refer to "EPC page(s)".

>  	For details, see Documentation/x86/intel_sgx.rst
>  
>  config EFI

...

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

Ick, letting the line poke out by a few chars seems preferable to wrapping "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;
> +}

...

> +
> +
> +	while (!list_empty(&encl->va_pages)) {
> +		va_page = list_first_entry(&encl->va_pages, struct sgx_va_page,
> +					   list);

list_for_each_entry_safe()?

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

I think you meant to introduce this in the ptrace support patch.

> +
> +/**
> + * sgx_alloc_page - allocate a VA page
      ^^^^^^^^^^^^^^
      sgx_alloc_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);

Hmm, maybe add ENCLS_WARN_ONCE?

> +		sgx_free_page(epc_page);
> +		return ERR_PTR(-EFAULT);
> +	}
> +
> +	return epc_page;
> +}
> +EXPORT_SYMBOL_GPL(sgx_alloc_va_page);

...

> 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

Th SGX_VA_SLOT_COUNT definition probably belongs in sgx_arch.h

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

Alternatively, sgx_calc_free_cnt() can be implemented in main.c and then
sgx_nr_epc_sections can remain static.

>  
>  /* A per-cpu cache for the last known values of IA32_SGXLEPUBKEYHASHx MSRs. */
>  static DEFINE_PER_CPU(u64 [4], sgx_lepubkeyhash_cache);

...

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

Not actually this patch, but the EREMOVE case can use ENCLS_WARN.

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

Yuck.  Definitely should look at using RCU list.  I think the whole
function would boil down to:

	list_for_each_entry_rcu(...) {
		down_read(&mm->mm->mmap_sem);
		ret = !sgx_encl_test_and_clear_young(next_mm->mm, page);
		up_read(&mm->mm->mmap_sem);

		if (ret || (encl->flags & SGX_ENCL_DEAD))
			break;
	}

	if (!ret || (encl->flags & SGX_ENCL_DEAD)) {
		mutex_lock(&encl->lock);
		page->desc |= SGX_ENCL_PAGE_RECLAIMED;
		mutex_unlock(&encl->lock);
	}
> +
> +		down_read(&next_mm->mm->mmap_sem);
> +		mutex_lock(&encl->lock);

Acquiring encl->lock just to check if its dead is a bit silly.

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

SGX_ENCL_PAGE_RECLAIMED needs to be while holding encl->lock.  Putting
everything together, I think the function would boil down to:

	list_for_each_entry_rcu(...) {
		if (encl->flags & SGX_ENCL_DEAD)
			break;

		down_read(&mm->mm->mmap_sem);
		ret = !sgx_encl_test_and_clear_young(next_mm->mm, page);
		up_read(&mm->mm->mmap_sem);

		if (!ret)
			return false;
	}

	mutex_lock(&encl->lock);
	page->desc |= SGX_ENCL_PAGE_RECLAIMED;
	mutex_unlock(&encl->lock);

	return true;

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

There's no need to acquire encl->lock, only mmap_sem needs to be held
to zap PTEs.

> +		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));
> +	}

Sending IPIs to flush CPUs out of the enclave is only necessary if the
enclave is alive, untracked and there are threads actively running in
the enclave.  I.e. calculate cpumask only when necessary.

This open coding of IPI sending made me realize the driver no long
invalidates an enclave if an ENCLS instruction fails unexpectedly.  That
is going to lead to absolute carnage if something does go wrong as there
will be no recovery path, i.e. the kernel log will be spammed to death
with ENCLS WARNings.  Debugging future development will be a nightmare if
a single ENCLS bug obliterates the kernel.

> +
> +	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");

Oof, this doesn't even return if ret != 0, e.g. we could WARN due to a
driver bug and then WARN again on EWB failure, or worse, somehow succeed
and continue on in some frankenstein state.

> +			}
> +
> +			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");

Yeesh, modding ENCLS_WARN to separate the warn condition and the raw error
code would eliminate both if statements.

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

This can be ENCLS_WARN.

> +	}
> +
> +	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(&section->lock);
> +			sgx_section_put_page(section, epc_page);
> +			spin_unlock(&section->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
> 

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 19/27] x86/sgx: ptrace() support for the SGX driver
  2019-03-17 21:14 ` [PATCH v19 19/27] x86/sgx: ptrace() support for the SGX driver Jarkko Sakkinen
@ 2019-03-19 22:22   ` Sean Christopherson
  2019-03-21 15:02     ` Jarkko Sakkinen
  0 siblings, 1 reply; 92+ messages in thread
From: Sean Christopherson @ 2019-03-19 22:22 UTC (permalink / raw)
  To: Jarkko Sakkinen
  Cc: x86, linux-sgx, akpm, dave.hansen, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes

On Sun, Mar 17, 2019 at 11:14:48PM +0200, Jarkko Sakkinen wrote:
> 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 <jarkko.sakkinen@linux.intel.com>
> ---
>  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);

Taking a lock indirectly via sgx_encl_reserve_page() and releasing it
directly via mutex_unlock() is a bit difficult to follow, having a helper
to pair up with sgx_encl_reserve_page() would be more readable.  Open
coding sgx_encl_reserve_page() would probably be even better since this
is the only user AFAICT.

All that being said, I prefer the old approach of marking the page RESERVED
instead of holding the enclave's lock, even though it was slightly more
complex.

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

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 16/27] x86/sgx: Add the Linux SGX Enclave Driver
  2019-03-17 21:14 ` [PATCH v19 16/27] x86/sgx: Add the Linux SGX Enclave Driver Jarkko Sakkinen
  2019-03-19 21:19   ` Sean Christopherson
@ 2019-03-19 23:00   ` Sean Christopherson
  2019-03-21 16:18     ` Jarkko Sakkinen
  1 sibling, 1 reply; 92+ messages in thread
From: Sean Christopherson @ 2019-03-19 23:00 UTC (permalink / raw)
  To: Jarkko Sakkinen
  Cc: x86, linux-sgx, akpm, dave.hansen, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Suresh Siddha

On Sun, Mar 17, 2019 at 11:14:45PM +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.


...

> 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

...

> +/**
> + * 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)) {

This is a use-after-free scenario if mm_count==0.  Once the count goes
to zero, __mmdrop() begins, at which point this code is racing against
free_mm().  What you want here (or rather, in flows where mm != current->mm
and you want to access PTEs) is mmget_not_zero(), i.e. "unless zero"
on mm_users.  mm_count prevents the mm_struct from being freed, but
doesn't protect the page tables.  mm_users protects the page tables,
i.e. lets us safely call sgx_encl_test_and_clear_young in the reclaimer.

To ensure liveliness of the mm itself, register an mmu_notifier for each
mm_struct (I think in sgx_vma_open()).  The enclave's .release callback
would then delete the mm from its list and drop its reference (exit_mmap()
holds a reference to mm_count so it's safe to do mmdrop() in the .release
callback).  E.g.:

static void sgx_vma_open(struct vm_area_struct *vma)
{
	...

	rcu_read_lock();
	list_for_each_entry_rcu(...) {
		if (vma->vm_mm == tmp->mm) {
			encl_mm = tmp;
			break;
		}
	}
	rcu_read_unlock();

	if (!encl_mm) {
		mm = kzalloc(sizeof(*mm), GFP_KERNEL);
		if (!mm) {
			goto error;

		encl_mm->encl = encl;
		encl_mm->mm = vma->vm_mm;

		if (mmu_notifier_register(&encl->mmu_notifier, encl_mm)) {
			kfree(encl_mm);
			goto error;
		}

		spin_lock(&encl->mm_lock);
		list_add(&encl_mm->list, &encl->mm_list);
		spin_unlock(&encl->mm_lock);
	}

	...
error:
	<not sure what should go here if we don't kill the enclave>
}

static void sgx_encl_mmu_release(struct mmu_notifier *mn, struct mm_struct *mm)
{
	struct sgx_encl_mm *encl_mm =
		container_of(mn, struct sgx_encl_mm, mmu_notifier);

	spin_lock(encl_mm->encl->mm_lock);
	list_del_rcu(&encl_mm->list);
	spin_unlock(encl_mm->encl->mm_lock);

	synchronize_rcu();

	mmdrop(mm);
}

Alternatively, the sgx_encl_mmu_release() could mark the encl_mm as dead
instead of removing it from the list, but I don't think that'd mesh well
with an RCU list, i.e. we'd need a regular lock-protected list and a
custom walker.

The only downside with the RCU approach that I can think of is that the
encl_mm would stay on the enclave's list until the enclave or the mm
itself died.  That could result in unnecessary IPIs during reclaim (or
invalidation), but that seems like a minor corner case that could be
avoided in userspace, e.g. don't mmap() an enclave unless you actually
plan on running it.

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

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 00/27] Intel SGX1 support
  2019-03-17 21:14 [PATCH v19 00/27] Intel SGX1 support Jarkko Sakkinen
                   ` (23 preceding siblings ...)
       [not found] ` <20190317211456.13927-19-jarkko.sakkinen@linux.intel.com>
@ 2019-03-19 23:41 ` Sean Christopherson
  2019-03-19 23:52   ` Jethro Beekman
  2019-03-21 16:00   ` Jarkko Sakkinen
  24 siblings, 2 replies; 92+ messages in thread
From: Sean Christopherson @ 2019-03-19 23:41 UTC (permalink / raw)
  To: Jarkko Sakkinen
  Cc: x86, linux-sgx, akpm, dave.hansen, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes

On Sun, Mar 17, 2019 at 11:14:29PM +0200, Jarkko Sakkinen wrote:
> Intel(R) SGX is a set of CPU instructions that can be used by applications
> to set aside private regions of code and data. The code outside the enclave
> is disallowed to access the memory inside the enclave by the CPU access
> control.  In a way you can think that SGX provides inverted sandbox. It
> protects the application from a malicious host.
> 
> 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:

...

> * 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.

I'm not convinced it's worth the effort to allow the driver to be compiled
as a module, especially if we drop the ACPI-based probing.  Making the
driver loadable means the kernel can easily end up in situations where it's
tracking EPC and running its reclaimer kthread, but the driver can't be
loaded and can *never* be loaded, e.g. because the platform doesn't support
Launch Control.

And IMO encl.{c,h} belongs in the "driver" code, but to let the driver be
loadable it got shoved into the core subsystem.  All of that code is
specific to running enclaves in the host, i.e. it shouldn't exist if I
compile out the driver entirely (in a future world where I want the core
SGX subsystem for virtualization purposes).

And yes, I realize this is a full 180 from my stance a year ago :)

> * 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.

It's not just forking, it's being able to hand off an enclave to an
already running process.  Andy also had (IMO) valid complaints about
completely ignoring @filep in the ioctls() and searching the vma to
find the enclave, e.g. the current code has to acquire mmap_sem just
to get a reference to the enclave and a process has to mmap() the
enclave to use any ioctl() other than ECREATE.

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 00/27] Intel SGX1 support
  2019-03-19 23:41 ` [PATCH v19 00/27] Intel SGX1 support Sean Christopherson
@ 2019-03-19 23:52   ` Jethro Beekman
  2019-03-20  0:22     ` Sean Christopherson
  2019-03-21 16:20     ` Jarkko Sakkinen
  2019-03-21 16:00   ` Jarkko Sakkinen
  1 sibling, 2 replies; 92+ messages in thread
From: Jethro Beekman @ 2019-03-19 23:52 UTC (permalink / raw)
  To: Sean Christopherson, Jarkko Sakkinen
  Cc: x86, linux-sgx, akpm, dave.hansen, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes

[-- Attachment #1: Type: text/plain, Size: 1559 bytes --]

On 2019-03-19 16:41, Sean Christopherson wrote:
> On Sun, Mar 17, 2019 at 11:14:29PM +0200, Jarkko Sakkinen wrote:
>> * 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.
> 
> I'm not convinced it's worth the effort to allow the driver to be compiled
> as a module, especially if we drop the ACPI-based probing.  Making the
> driver loadable means the kernel can easily end up in situations where it's
> tracking EPC and running its reclaimer kthread, but the driver can't be
> loaded and can *never* be loaded, e.g. because the platform doesn't support
> Launch Control.

Tracking EPC etc. is necessary for KVM anyway.

> And IMO encl.{c,h} belongs in the "driver" code, but to let the driver be
> loadable it got shoved into the core subsystem.  All of that code is
> specific to running enclaves in the host, i.e. it shouldn't exist if I
> compile out the driver entirely (in a future world where I want the core
> SGX subsystem for virtualization purposes).

Your argument here is "something that belongs in the driver isn't, 
therefore we shouldn't have a loadable driver". This seems backwards to 
me. Instead, we should see what interface would be needed so that this 
stuff *can* be in the driver.

> And yes, I realize this is a full 180 from my stance a year ago :)

I don't really want to rehash this argument but I think it should be a 
module.

--
Jethro Beekman | Fortanix


[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 3990 bytes --]

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 00/27] Intel SGX1 support
  2019-03-19 23:52   ` Jethro Beekman
@ 2019-03-20  0:22     ` Sean Christopherson
  2019-03-21 16:20     ` Jarkko Sakkinen
  1 sibling, 0 replies; 92+ messages in thread
From: Sean Christopherson @ 2019-03-20  0:22 UTC (permalink / raw)
  To: Jethro Beekman
  Cc: Jarkko Sakkinen, x86, linux-sgx, akpm, dave.hansen, nhorman,
	npmccallum, serge.ayoun, shay.katz-zamir, haitao.huang,
	andriy.shevchenko, tglx, kai.svahn, bp, josh, luto, kai.huang,
	rientjes

On Tue, Mar 19, 2019 at 11:52:32PM +0000, Jethro Beekman wrote:
> On 2019-03-19 16:41, Sean Christopherson wrote:
> >On Sun, Mar 17, 2019 at 11:14:29PM +0200, Jarkko Sakkinen wrote:
> >>* 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.
> >
> >I'm not convinced it's worth the effort to allow the driver to be compiled
> >as a module, especially if we drop the ACPI-based probing.  Making the
> >driver loadable means the kernel can easily end up in situations where it's
> >tracking EPC and running its reclaimer kthread, but the driver can't be
> >loaded and can *never* be loaded, e.g. because the platform doesn't support
> >Launch Control.
> 
> Tracking EPC etc. is necessary for KVM anyway.

This part isn't related to KVM.  As it's written, a kernel with SGX support
running on a non-LC system will allocate memory and spin up a kthread, and
then do absolutely nothing with it.

When KVM support is added, then yes, it's a slightly different story.  But
we end up in the same spot if the kernel isn't built with EPC virtualization
support.

> 
> >And IMO encl.{c,h} belongs in the "driver" code, but to let the driver be
> >loadable it got shoved into the core subsystem.  All of that code is
> >specific to running enclaves in the host, i.e. it shouldn't exist if I
> >compile out the driver entirely (in a future world where I want the core
> >SGX subsystem for virtualization purposes).
> 
> Your argument here is "something that belongs in the driver isn't, therefore
> we shouldn't have a loadable driver". This seems backwards to me. Instead,
> we should see what interface would be needed so that this stuff *can* be in
> the driver.

Speaking of rehashing arguments, that approach got nixed in a previous
revision because it requires implementing the EPC eviction flows via
callbacks.  Specifically, Dave Hansen argued that we shouldn't be adding
infrastructure (the callback framework, layer of indirection, etc...)
without any true need or user for it.

> >And yes, I realize this is a full 180 from my stance a year ago :)
> 
> I don't really want to rehash this argument but I think it should be a
> module.

I agree that ideally the userspace facing driver would be a module, but
practically speaking making the driver a module complicates the kernel
a bit and leads to some undesirable behavior.  A loadable module is
"nice", but I haven't seen a true use case that requires the driver to
be a module.

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 25/27] x86/sgx: SGX documentation
  2019-03-17 21:14 ` [PATCH v19 25/27] x86/sgx: SGX documentation Jarkko Sakkinen
@ 2019-03-20 17:14   ` Sean Christopherson
  2019-03-21 16:24     ` Jarkko Sakkinen
  0 siblings, 1 reply; 92+ messages in thread
From: Sean Christopherson @ 2019-03-20 17:14 UTC (permalink / raw)
  To: Jarkko Sakkinen
  Cc: x86, linux-sgx, akpm, dave.hansen, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes

On Sun, Mar 17, 2019 at 11:14:54PM +0200, Jarkko Sakkinen wrote:
> 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 <jarkko.sakkinen@linux.intel.com>
> Co-developed-by: Sean Christopherson <sean.j.christopherson@intel.com>
> Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
> ---
> +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.

The overview should probably be limited to architectural features, e.g. the
ISA, *architectural* EPC and EPCM behavior, etc...  Then add a dedicated
section on EPC implementation(s), which are micro-architecture specific.

> +
> +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
                                  ^^^^^^^

I'm 99.99999% certain the above should be "linear address range".  The SDM
SDM first introduces ELRANGE as working with logical addresses:

    Address space allocation is the specification of the range of logical
    addresses that the enclave may use. This range is called the ELRANGE.

but I think that's a documentation bug.  Subsequent references in the SDM
all refer to linear addresses, and the ELRANGE checks definitely work with
linear addresses.

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

This isn't correct.  ELRANGE protections are activated when only executing
inside an enclave.  An enclave's memory is protected by the EPC{M} when the
CPU is operating outside the enclave or in a different enclave.

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

The VM-Exit blurb is technically no longer accurate, e.g. a VMM can choose
to intercept RDTSC, but RDTSC is also allowed in enclaves.

> +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.

The page type descriptions could use a bit of improvement.  Tangentially
related, would it make sense to add a "Terminology" section given the
absurd number of acronyms used by SGX?

> +
> +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).

This section should also call out that the IA32_SGXLEPUBKEYHASHn MSRs also
affect EINIT tokens.  And simply being signed with the key referenced by
IA32_SGXLEPUBKEYHASHn doesn't grant access to the EINITTOKEN_KEY, the
enclave still needs to explicit request access, e.g. the kernel could deny
access to the EINITTOKEN_KEY by refusing to create enclaves that request
said key.

> +**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.

...

> +The PF_SGX bit is set if and only if the #PF is detected by the SGX Enclave Page

The ordering here is a bit backwards, e.g. the EPCM and its protections
should come first, and then describe the faults that can be signaled by
the EPCM.

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

Stale file reference.

> +   :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

Probably should leave off the exact section, e.g. "39.1.4" is already out
of date.

IMO this whole document could use an overhaul to uplevel the overview
and {micro-}architecture descriptions to make it a useful standalone doc
instead of a poor man's regurgitation of the SDM.  If there are no
objections, I'll start typing and reply with a fresh patch at some point.

Oh, and the vDSO interface needs to be added, I'll do that too.

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 17/27] x86/sgx: Add provisioning
  2019-03-19 20:09   ` Sean Christopherson
@ 2019-03-21  2:08     ` Huang, Kai
  2019-03-21 14:32       ` Jarkko Sakkinen
  2019-03-21 14:30     ` Jarkko Sakkinen
  1 sibling, 1 reply; 92+ messages in thread
From: Huang, Kai @ 2019-03-21  2:08 UTC (permalink / raw)
  To: jarkko.sakkinen, Christopherson, Sean J
  Cc: Svahn, Kai, nhorman, jmorris, rientjes, josh, tglx, Ayoun, Serge,
	Huang, Haitao, linux-security-module, x86, akpm, npmccallum,
	linux-sgx, luto, Katz-zamir, Shay, Hansen, Dave, bp, serge,
	andriy.shevchenko

On Tue, 2019-03-19 at 13:09 -0700, Sean Christopherson wrote:
> On Sun, Mar 17, 2019 at 11:14:46PM +0200, Jarkko Sakkinen wrote:
> > 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;
> >    };

Would you elaborate why the name is "token_fd"? I think *token* in SGX has more specific meaning?

> > 
> > 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, &params);
> > 
> > Cc: James Morris <jmorris@namei.org>
> > Cc: Serge E. Hallyn <serge@hallyn.com>
> > Cc: linux-security-module@vger.kernel.org
> > Suggested-by: Andy Lutomirski <luto@kernel.org>
> > Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
> > ---
> >  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.
> 
> With the @data change (see below), this becomes something like:
> 
>  * Allow the enclave to request the attribute managed by the SGX security file
>  * pointed at 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)
> 
> This should be:
> 
> 	if (!attribute_file)
> 		return -EINVAL;
> 
> > +		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;
> 
> A cleanr approach would be to pass SGX_ATTR_PROVISIONKEY via @data to
> securityfs_create_file().  Then you don't need to define dummy file_ops
> for each file, i.e. a generic sgx_sec_fs_ops would suffice for the above
> check.  And you don't have this weird hardcoding of the provision bit.
> 
> E.g.:
> 
> 	if (attribute_file->f_op != &sgx_sec_fs_fops) {
> 		ret = -EINVAL;
> 		goto out;
> 	}
> 
> 	ret = sgx_encl_get(params->addr, &encl);
> 	if (ret)
> 		goto out;
> 
> 	encl->allowed_attributes |= (u64)attribute_file->private_data;
> 
> Since SGX doesn't support 32-bit builds we don't even need to worry about
> the (very distant) future where SGX defines bits in the 63:32 range.
> 

Agree with Sean that passing SGX_ATTR_PROVISIONKEY via @data is more cleaner.

Thanks,
-Kai

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 17/27] x86/sgx: Add provisioning
  2019-03-19 20:09   ` Sean Christopherson
  2019-03-21  2:08     ` Huang, Kai
@ 2019-03-21 14:30     ` Jarkko Sakkinen
  1 sibling, 0 replies; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-21 14:30 UTC (permalink / raw)
  To: Sean Christopherson
  Cc: x86, linux-sgx, akpm, dave.hansen, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	James Morris, Serge E . Hallyn, linux-security-module

On Tue, Mar 19, 2019 at 01:09:12PM -0700, Sean Christopherson wrote:
> On Sun, Mar 17, 2019 at 11:14:46PM +0200, Jarkko Sakkinen wrote:
> > 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, &params);
> > 
> > Cc: James Morris <jmorris@namei.org>
> > Cc: Serge E. Hallyn <serge@hallyn.com>
> > Cc: linux-security-module@vger.kernel.org
> > Suggested-by: Andy Lutomirski <luto@kernel.org>
> > Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
> > ---
> >  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.
> 
> With the @data change (see below), this becomes something like:
> 
>  * Allow the enclave to request the attribute managed by the SGX security file
>  * pointed at by the parameter structure field attribute_fd.

I see your point but the current implementation is just a tiny bit
simpler and right now we have one single file.

But I would do to fix this patch right now is to rename
sgx_fs_provision_ops as sgx_fs_ops to point out that only single fops
are required.

The current implementation is "good enough" for handling the
provisioning key.

/Jarkko

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 17/27] x86/sgx: Add provisioning
  2019-03-21  2:08     ` Huang, Kai
@ 2019-03-21 14:32       ` Jarkko Sakkinen
  2019-03-21 21:41         ` Huang, Kai
  0 siblings, 1 reply; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-21 14:32 UTC (permalink / raw)
  To: Huang, Kai
  Cc: Christopherson, Sean J, Svahn, Kai, nhorman, jmorris, rientjes,
	josh, tglx, Ayoun, Serge, Huang, Haitao, linux-security-module,
	x86, akpm, npmccallum, linux-sgx, luto, Katz-zamir, Shay, Hansen,
	Dave, bp, serge, andriy.shevchenko

On Thu, Mar 21, 2019 at 02:08:36AM +0000, Huang, Kai wrote:
> On Tue, 2019-03-19 at 13:09 -0700, Sean Christopherson wrote:
> > On Sun, Mar 17, 2019 at 11:14:46PM +0200, Jarkko Sakkinen wrote:
> > > 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;
> > >    };
> 
> Would you elaborate why the name is "token_fd"? I think *token* in SGX
> has more specific meaning?

I'm open for other names.

/Jarkko

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 17/27] x86/sgx: Add provisioning
  2019-03-17 21:14 ` [PATCH v19 17/27] x86/sgx: Add provisioning Jarkko Sakkinen
  2019-03-19 20:09   ` Sean Christopherson
@ 2019-03-21 14:38   ` Nathaniel McCallum
  2019-03-22 11:22     ` Jarkko Sakkinen
  2019-03-21 16:50   ` Andy Lutomirski
  2 siblings, 1 reply; 92+ messages in thread
From: Nathaniel McCallum @ 2019-03-21 14:38 UTC (permalink / raw)
  To: Jarkko Sakkinen
  Cc: x86, linux-sgx, akpm, dave.hansen, sean.j.christopherson,
	Neil Horman, Ayoun, Serge, shay.katz-zamir, Huang, Haitao,
	andriy.shevchenko, tglx, Svahn, Kai, bp, josh, luto, kai.huang,
	rientjes, James Morris, Serge E . Hallyn, linux-security-module

On Sun, Mar 17, 2019 at 5:18 PM Jarkko Sakkinen
<jarkko.sakkinen@linux.intel.com> wrote:
>
> In order to provide a mechanism for devilering provisoning rights:

I'm not sure what nefarious spirits have to do with this patch.
Perhaps you meant "delivering provisioning"? ;)

> 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, &params);
>
> Cc: James Morris <jmorris@namei.org>
> Cc: Serge E. Hallyn <serge@hallyn.com>
> Cc: linux-security-module@vger.kernel.org
> Suggested-by: Andy Lutomirski <luto@kernel.org>
> Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
> ---
>  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
>

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 12/27] x86/sgx: Enumerate and track EPC sections
  2019-03-18 19:50   ` Sean Christopherson
@ 2019-03-21 14:40     ` Jarkko Sakkinen
  2019-03-21 15:28       ` Sean Christopherson
  0 siblings, 1 reply; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-21 14:40 UTC (permalink / raw)
  To: Sean Christopherson
  Cc: x86, linux-sgx, akpm, dave.hansen, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Suresh Siddha

On Mon, Mar 18, 2019 at 12:50:43PM -0700, Sean Christopherson wrote:
> On Sun, Mar 17, 2019 at 11:14:41PM +0200, Jarkko Sakkinen wrote:
> > From: Sean Christopherson <sean.j.christopherson@intel.com>
> > 
> > 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 <sean.j.christopherson@intel.com>
> > Co-developed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
> > Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
> > Co-developed-by: Suresh Siddha <suresh.b.siddha@intel.com>
> > Signed-off-by: Suresh Siddha <suresh.b.siddha@intel.com>
> > Co-developed-by: Serge Ayoun <serge.ayoun@intel.com>
> > Signed-off-by: Serge Ayoun <serge.ayoun@intel.com>
> > ---
> >  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.
> 
> "enclave" is used before it's defined.  And the second sentence could be
> tweaked to make it explicitly clear that hardware disallows cross-enclave
> access.  E.g.:
> 
> 	Intel(R) SGX is a set of CPU instructions that can be used by
> 	applications to set aside private regions of code and data, referred
> 	to as enclaves.  An enclave's private memory can only be accessed by
> 	code running within the enclave.  Accesses from outside the enclave,
> 	including other enclaves, are disallowed by hardware.

Agreed.

> 
> > +
> > +	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.
> 
> This second paragraph can probably be dropped altogether.  A reader won't
> know what PRMRR means unless they're already familiar with SGX.  And the
> PRMRR+MEE implementation is not architectural, i.e. future hardware could
> support EPC through some other mechanism.  SGX does more than just encrypt
> memory, covering those details is probably best left to intel_sgx.rst.

Ditto.

> 
> > +
> > +	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];
> 
> Dynamically allocating sgx_epc_sections isn't exactly difficult, and
> AFAICT the static allocation is the primary motivation for capping
> SGX_MAX_EPC_SECTIONS at such a low value (8).  I still think it makes
> sense to define SGX_MAX_EPC_SECTIONS so that the section number can
> be embedded in the offset, along with flags.  But the max can be
> significantly higher, e.g. using 7 bits to support 128 sections.
> 

I don't disagree with you but I think for the existing and forseeable
hardware this is good enough. Can be refined if there is ever need.

> I realize hardware is highly unlikely to have more than 8 sections, at
> least for the near future, but IMO the small amount of extra complexity
> is worth having a bit of breathing room.

Yup.

> 
> > +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, &section->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(&section->page_list)) {
> > +		page = list_first_entry(&section->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(&section->lock);
> > +	INIT_LIST_HEAD(&section->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);
> > +	}
> 
> Not sure if this is the correct location, but at some point the kernel
> needs to sanitize the EPC during init.  EPC pages may be in an unknown
> state, e.g. after kexec(), which will cause all manner of faults and
> warnings.  Maybe the best approach is to sanitize on-demand, e.g. suppress
> the first WARN due to unexpected ENCLS failure and purge the EPC at that
> time.  The downside of that approach is that exposing EPC to a guest would
> need to implement its own sanitization flow.

Hmm... Lets think this through. I'm just thinking how sanitization on
demand would actually work given the parent-child relationships.

> 
> > +
> > +	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;
> 
> This should probably be "continue" rather than "return -ENODEV".  SGX
> can still be used in the (extremely) unlikely event that there is usable
> EPC and some unknown memory type enumerated.

OK, lets do that. Maybe also pr_warn_once() should be used?

> 
> > +		}
> > +		if (i == SGX_MAX_EPC_SECTIONS) {
> > +			pr_warn("sgx: More than "
> > +				__stringify(SGX_MAX_EPC_SECTIONS)
> > +				" EPC sections\n");
> 
> This isn't a very helpful message, e.g. it doesn't even imply that the
> kernel is ignoring EPC sections.  It'd also be helpful to display the
> sections that are being ignored.  Might also warrant pr_err() since it
> means system resources are being ignored.
> 
> E.g.:
> 
> #define SGX_ARBITRARY_LOOP_TERMINATOR   1000
> 
> 	for (i = 0; i < SGX_ARBITRARY_LOOP_TERMINATOR; i++) {
> 		...
> 
> 		if (i >= SGX_MAX_EPC_SECTIONS) {
> 			pr_err("sgx: Reached max number of EPC sections (%u), "
> 			       "ignoring section 0x%llx-0x%llx\n",
> 			       pa, pa + size - 1);
> 		}

Fully agree with these proposals!

> 	}
> 
> > +			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;
> 
> Similar to encountering unknown sections, any reason why we wouldn't
> continue here and use whatever EPC was successfuly initialized?

Nope.

> 
> > +		}
> > +
> > +		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
> > 

/Jarkko

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 27/27] x86/sgx: Update MAINTAINERS
  2019-03-19 17:12   ` Sean Christopherson
@ 2019-03-21 14:42     ` Jarkko Sakkinen
  0 siblings, 0 replies; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-21 14:42 UTC (permalink / raw)
  To: Sean Christopherson
  Cc: x86, linux-sgx, akpm, dave.hansen, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes

On Tue, Mar 19, 2019 at 10:12:29AM -0700, Sean Christopherson wrote:
> On Sun, Mar 17, 2019 at 11:14:56PM +0200, Jarkko Sakkinen wrote:
> > Add the maintainer information for the SGX subsystem.
> > 
> > Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
> > ---
> >  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:	linux-gpio@vger.kernel.org
> >  S:	Maintained
> >  F:	drivers/gpio/gpio-intel-mid.c
> >  
> > +INTEL SGX
> 
> Alphabetically, this should slot in between "INTEL RDMA RNIC DRIVER" and
> "INTEL TELEMETRY DRIVER".
> 
> Tangentially related, is it worth sending a patch to rename "INTEL-MID GPIO DRIVER"
> to "INTEL MID GPIO DRIVER" and drop "(R)" from "INTEL(R) TRACE HUB"
> and "INTEL(R) TRUSTED EXECUTION TECHNOLOGY (TXT)"?  They're silly
> renegades that muck up the ordering.

My bad. Placed it wrongly when I rebased the series to v5.0.

> 
> > +M:	Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
> > +M:	Sean Christopherson <sean.j.christopherson@intel.com>
> > +L:	linux-sgx@vger.kernel.org
> > +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
> 
> arch/x86/include/asm/sgx.h doesn't exist in this version of the series.
> 
> > +F:	arch/x86/include/uapi/asm/sgx.h
> > +F:	arch/x86/kernel/cpu/sgx/*
> 
> The trailing asterisk is unnecessary and wrong.  From MAINTAINER's docs:
> 
>         F: Files and directories with wildcard patterns.
>            A trailing slash includes all files and subdirectory files.
>            F:   drivers/net/    all files in and below drivers/net
>            F:   drivers/net/*   all files in drivers/net, but not below
> 
> > +K:	\bSGX_
> > +
> >  INTERCONNECT API
> >  M:	Georgi Djakov <georgi.djakov@linaro.org>
> >  S:	Maintained
> > -- 
> > 2.19.1
> > 

Thanks. 

/Jarkko

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 13/27] x86/sgx: Add wrappers for ENCLS leaf functions
  2019-03-19 19:59   ` Sean Christopherson
@ 2019-03-21 14:51     ` Jarkko Sakkinen
  2019-03-21 15:40       ` Sean Christopherson
  0 siblings, 1 reply; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-21 14:51 UTC (permalink / raw)
  To: Sean Christopherson
  Cc: x86, linux-sgx, akpm, dave.hansen, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes

On Tue, Mar 19, 2019 at 12:59:07PM -0700, Sean Christopherson wrote:
> On Sun, Mar 17, 2019 at 11:14:42PM +0200, Jarkko Sakkinen wrote:
> > 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 <jarkko.sakkinen@linux.intel.com>
> > Co-developed-by: Sean Christopherson <sean.j.christopherson@intel.com>
> > Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
> > ---
> >  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.
> 
> "conflict" is a poor word choice.  The SDM's refers to EPC conflicts as
> trying to concurrently access an EPC page from multiple logical CPUs.
> 
> Maybe "EPCM violation" or simply "EPCM fault"?

I think violation is better as we have also legit faults.

> 
> > + *
> > + * Return: true if there was a fault other than an EPCM conflict
> > + */
> > +bool encls_failed(int ret)
> 
> I really dislike this name.  IMO, "encls_failed" is inaccurate and to some
> extent an unnecessary abstraction.  Regardless of why it faulted, the
> ENCLS instruction "failed".  Just because the fault originated in the EPCM
> doesn't mean the instruction magically succeeded.
> 
> What if we inverted the logic, i.e. to identify EPCM violations?  And
> rename encls_fault() to is_encls_fault(), and encls_returned_code()
> to is_sgx_error_code().  E.g.:
> 
> bool is_epcm_violation(int ret)
> {
> 	return is_encls_fault(ret) && ENCLS_TRAPNR(ret) == epcm_trapnr;
> }
> 
> Then the usage becomes:
> 
> 	if (is_sgx_error_code(ret) ||
> 	    (is_encls_fault(ret) && !is_epcm_violation(ret))
> 		blah blah blah
> 
> The usage is more verbose, but explicitly clear.

These sounds like legit proposals!

> 
> > +{
> > +	int epcm_trapnr = boot_cpu_has(X86_FEATURE_SGX2) ?
> > +			  X86_TRAP_PF : X86_TRAP_GP;
> 
> epcm_trapnr should be calculated once during init.

Depends whether it is inline or not.

> 
> > +
> > +	return encls_faulted(ret) && ENCLS_TRAPNR(ret) != epcm_trapnr;
> > +}
> 
> Why isn't this function inlined in sgx.h?

I like to put kprobe or ftrace filter to it.

> 
> > 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)
> 
> I honestly can't remember, is there a reason ENCLS_TRAPNR is still a macro
> and not an inline function?

Not at all. And also the value should be unsigned and fault flag
should be 0x80* because we never should get negative values.

> 
> > +
> > +/* Issue a WARN() about an ENCLS leaf. */
> 
> If you're going to bother with a comment, might as well document the
> interesting part, i.e. why we print both the decimal and hexidecimal
> formats, which is the reason this macro exists at all.
> 
> > +#define ENCLS_WARN(r, name) {						\
> 
> Taking the warn condition and the raw error code in a single parameter
> makes the call sites ugly, e.g.
> 
> 	if (ret) {
> 		if (encls_failed(ret))
> 			ENCLS_WARN(ret, "EADD");
> 		return false;
> 	}
> 
> If the macro is instead something like:
> 
> #define ENCLS_WARN(do_warn, r, name) \
> 	WARN(w, "sgx: %s returned %d (0x%x)\n", (name), (r), (r); \
> 
> then we can drop the do-while wrapping and the callers are a little less
> ugly, e.g.:
> 
> 	if (ret) {
> 		ENCLS_WARN(encls_failed(ret), ret, "EADD");
> 		return ret;
> 	}
> 
> 
> Or with the is_epcm_violation() variant:
> 
> 	if (ret) {
> 		ENCLS_WARN(is_encls_fault(ret) && !is_epcm_violation(ret),
> 			   ret, "EADD");
> 		return ret;
> 	}
> 
> > +	do {								\
> > +		int _r = (r);						\
> > +		WARN(_r, "sgx: %s returned %d (0x%x)\n", (name), _r,	\
> > +		     _r);						\
> 
> There's no need to wrap this, just add spaces before the '\'.
> 
> > +	} 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;
> 
> Nit: IMO checking for non-zero ret should be first, both from a readability
>      perspective and from a code generation perspective.
> 
> > +}
> > +
> > +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
> > 

All of the feedback looks legit. Thanks. And in addition we should
assumed unigned value from the macros.

/Jarkko

/Jarkko

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 18/27] x86/sgx: Add swapping code to the core and SGX driver
  2019-03-19 22:09   ` [PATCH v19 18/27] x86/sgx: Add swapping code to the core and SGX driver Sean Christopherson
@ 2019-03-21 14:59     ` Jarkko Sakkinen
  0 siblings, 0 replies; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-21 14:59 UTC (permalink / raw)
  To: Sean Christopherson
  Cc: x86, linux-sgx, akpm, dave.hansen, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes

> Yuck.  Definitely should look at using RCU list.  I think the whole
> function would boil down to:
> 
> 	list_for_each_entry_rcu(...) {
> 		down_read(&mm->mm->mmap_sem);
> 		ret = !sgx_encl_test_and_clear_young(next_mm->mm, page);
> 		up_read(&mm->mm->mmap_sem);
> 
> 		if (ret || (encl->flags & SGX_ENCL_DEAD))
> 			break;
> 	}
> 
> 	if (!ret || (encl->flags & SGX_ENCL_DEAD)) {
> 		mutex_lock(&encl->lock);
> 		page->desc |= SGX_ENCL_PAGE_RECLAIMED;
> 		mutex_unlock(&encl->lock);
> 	}

But yuo cnot

> > +
> > +		down_read(&next_mm->mm->mmap_sem);
> > +		mutex_lock(&encl->lock);
> 
> Acquiring encl->lock just to check if its dead is a bit silly.
> 
> > +
> > +		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;
> 
> SGX_ENCL_PAGE_RECLAIMED needs to be while holding encl->lock.  Putting
> everything together, I think the function would boil down to:
> 
> 	list_for_each_entry_rcu(...) {
> 		if (encl->flags & SGX_ENCL_DEAD)
> 			break;
> 
> 		down_read(&mm->mm->mmap_sem);
> 		ret = !sgx_encl_test_and_clear_young(next_mm->mm, page);
> 		up_read(&mm->mm->mmap_sem);
> 
> 		if (!ret)
> 			return false;
> 	}
> 
> 	mutex_lock(&encl->lock);
> 	page->desc |= SGX_ENCL_PAGE_RECLAIMED;
> 	mutex_unlock(&encl->lock);
> 
> 	return true;
> 
> > +	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);
> 
> There's no need to acquire encl->lock, only mmap_sem needs to be held
> to zap PTEs.
> 
> > +		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));
> > +	}
> 
> Sending IPIs to flush CPUs out of the enclave is only necessary if the
> enclave is alive, untracked and there are threads actively running in
> the enclave.  I.e. calculate cpumask only when necessary.
> 
> This open coding of IPI sending made me realize the driver no long
> invalidates an enclave if an ENCLS instruction fails unexpectedly.  That
> is going to lead to absolute carnage if something does go wrong as there
> will be no recovery path, i.e. the kernel log will be spammed to death
> with ENCLS WARNings.  Debugging future development will be a nightmare if
> a single ENCLS bug obliterates the kernel.

Responding below. I get your RCU idea but you cannot sleep inside normal
RCU. Also, the current implemntation deals with that mmap_sem cn be
gone. I'm open for using RCU (i.e. SRCU) if these can be somehow dealt
with.

/Jarkko

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 19/27] x86/sgx: ptrace() support for the SGX driver
  2019-03-19 22:22   ` Sean Christopherson
@ 2019-03-21 15:02     ` Jarkko Sakkinen
  0 siblings, 0 replies; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-21 15:02 UTC (permalink / raw)
  To: Sean Christopherson
  Cc: x86, linux-sgx, akpm, dave.hansen, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes

On Tue, Mar 19, 2019 at 03:22:01PM -0700, Sean Christopherson wrote:
> On Sun, Mar 17, 2019 at 11:14:48PM +0200, Jarkko Sakkinen wrote:
> > 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 <jarkko.sakkinen@linux.intel.com>
> > ---
> >  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);
> 
> Taking a lock indirectly via sgx_encl_reserve_page() and releasing it
> directly via mutex_unlock() is a bit difficult to follow, having a helper
> to pair up with sgx_encl_reserve_page() would be more readable.  Open
> coding sgx_encl_reserve_page() would probably be even better since this
> is the only user AFAICT.
> 
> All that being said, I prefer the old approach of marking the page RESERVED
> instead of holding the enclave's lock, even though it was slightly more
> complex.

For me the big rationale is to minimize the amount of state in the
context of this patch set. And this approach should work just fine.

/Jarkko

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 12/27] x86/sgx: Enumerate and track EPC sections
  2019-03-21 14:40     ` Jarkko Sakkinen
@ 2019-03-21 15:28       ` Sean Christopherson
  2019-03-22 10:19         ` Jarkko Sakkinen
  0 siblings, 1 reply; 92+ messages in thread
From: Sean Christopherson @ 2019-03-21 15:28 UTC (permalink / raw)
  To: Jarkko Sakkinen
  Cc: x86, linux-sgx, akpm, dave.hansen, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Suresh Siddha

On Thu, Mar 21, 2019 at 04:40:56PM +0200, Jarkko Sakkinen wrote:
> On Mon, Mar 18, 2019 at 12:50:43PM -0700, Sean Christopherson wrote:
> > On Sun, Mar 17, 2019 at 11:14:41PM +0200, Jarkko Sakkinen wrote:
> > Dynamically allocating sgx_epc_sections isn't exactly difficult, and
> > AFAICT the static allocation is the primary motivation for capping
> > SGX_MAX_EPC_SECTIONS at such a low value (8).  I still think it makes
> > sense to define SGX_MAX_EPC_SECTIONS so that the section number can
> > be embedded in the offset, along with flags.  But the max can be
> > significantly higher, e.g. using 7 bits to support 128 sections.
> > 
> 
> I don't disagree with you but I think for the existing and forseeable
> hardware this is good enough. Can be refined if there is ever need.

My concern is that there may be virtualization use cases that want to
expose more than 8 EPC sections to a guest.  I have no idea if this is
anything more than paranoia, but at the same time the cost to increase
support to 128+ sections is quite low.

> > I realize hardware is highly unlikely to have more than 8 sections, at
> > least for the near future, but IMO the small amount of extra complexity
> > is worth having a bit of breathing room.
> 
> Yup.
> 
> > > +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(&section->lock);
> > > +	INIT_LIST_HEAD(&section->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);
> > > +	}
> > 
> > Not sure if this is the correct location, but at some point the kernel
> > needs to sanitize the EPC during init.  EPC pages may be in an unknown
> > state, e.g. after kexec(), which will cause all manner of faults and
> > warnings.  Maybe the best approach is to sanitize on-demand, e.g. suppress
> > the first WARN due to unexpected ENCLS failure and purge the EPC at that
> > time.  The downside of that approach is that exposing EPC to a guest would
> > need to implement its own sanitization flow.
> 
> Hmm... Lets think this through. I'm just thinking how sanitization on
> demand would actually work given the parent-child relationships.

It's ugly.

  1. Temporarily disable EPC allocation and enclave fault handling
  2. Zap all TCS PTEs in all enclaves
  3. Flush all logical CPUs from enclaves via IPI
  4. Forcefully reclaim all EPC pages from enclaves
  5. EREMOVE all "free" EPC pages, track pages that fail with SGX_CHILD_PRESENT
  6. EREMOVE all EPC pages that failed with SGX_CHILD_PRESENT
  7. Disable SGX if any EREMOVE failed in step 6
  8. Re-enable EPC allocation and enclave fault handling

Exposing EPC to a VM would still require sanitization.

Sanitizing during boot is a lot cleaner, the primary concern is that it
will significantly increase boot time on systems with large EPCs.  If we
can somehow limit this to kexec() and that's the only scenario where the
EPC needs to be sanitized, then that would mitigate the boot time concern.

We might also be able to get away with unconditionally sanitizing the EPC
post-boot, e.g. via worker threads, returning -EBUSY for everything until
the EPC is good to go.

> 
> > 
> > > +
> > > +	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;
> > 
> > This should probably be "continue" rather than "return -ENODEV".  SGX
> > can still be used in the (extremely) unlikely event that there is usable
> > EPC and some unknown memory type enumerated.
> 
> OK, lets do that. Maybe also pr_warn_once() should be used?

Yeah, probably.  If we use pr_warn here, then we should also convert
pr_err("sgx: There are zero EPC sections.\n"); to use pr_warn() since
that'll likely fire at the same time.

> 
> > 
> > > +		}
> > > +		if (i == SGX_MAX_EPC_SECTIONS) {
> > > +			pr_warn("sgx: More than "
> > > +				__stringify(SGX_MAX_EPC_SECTIONS)
> > > +				" EPC sections\n");
> > 
> > This isn't a very helpful message, e.g. it doesn't even imply that the
> > kernel is ignoring EPC sections.  It'd also be helpful to display the
> > sections that are being ignored.  Might also warrant pr_err() since it
> > means system resources are being ignored.
> > 
> > E.g.:
> > 
> > #define SGX_ARBITRARY_LOOP_TERMINATOR   1000
> > 
> > 	for (i = 0; i < SGX_ARBITRARY_LOOP_TERMINATOR; i++) {
> > 		...
> > 
> > 		if (i >= SGX_MAX_EPC_SECTIONS) {
> > 			pr_err("sgx: Reached max number of EPC sections (%u), "
> > 			       "ignoring section 0x%llx-0x%llx\n",
> > 			       pa, pa + size - 1);
> > 		}
> 
> Fully agree with these proposals!
> 
> > 	}
> > 
> > > +			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;
> > 
> > Similar to encountering unknown sections, any reason why we wouldn't
> > continue here and use whatever EPC was successfuly initialized?
> 
> Nope.
> 
> > 
> > > +		}
> > > +
> > > +		sgx_nr_epc_sections++;
> > > +	}
> > > +
> > > +	if (!sgx_nr_epc_sections) {
> > > +		pr_err("sgx: There are zero EPC sections.\n");
> > > +		return -ENODEV;
> > > +	}
> > > +
> > > +	return 0;
> > > +}

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 13/27] x86/sgx: Add wrappers for ENCLS leaf functions
  2019-03-21 14:51     ` Jarkko Sakkinen
@ 2019-03-21 15:40       ` Sean Christopherson
  2019-03-22 11:00         ` Jarkko Sakkinen
  0 siblings, 1 reply; 92+ messages in thread
From: Sean Christopherson @ 2019-03-21 15:40 UTC (permalink / raw)
  To: Jarkko Sakkinen
  Cc: x86, linux-sgx, akpm, dave.hansen, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes

On Thu, Mar 21, 2019 at 04:51:53PM +0200, Jarkko Sakkinen wrote:
> On Tue, Mar 19, 2019 at 12:59:07PM -0700, Sean Christopherson wrote:
> > On Sun, Mar 17, 2019 at 11:14:42PM +0200, Jarkko Sakkinen wrote:
> > > +/**
> > > + * 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)
> > 
> > I honestly can't remember, is there a reason ENCLS_TRAPNR is still a macro
> > and not an inline function?
> 
> Not at all. And also the value should be unsigned and fault flag
> should be 0x80* because we never should get negative values.

From ENCLS itself, but we do have scenarios where the return code can
hold a system error code (negative value), an SGX error code (positive
value) or fault inforation.  E.g. sgx_encl_init() sets ret to -ERESTARTSYS,
and __sgx_encl_ewb() returns system error codes if get_backing() fails
and also directly returns the result of __ewb().  We could dance around
those issues, but IMO it's too fragile as it essentially requires a full
audit of every possible error path when modifying code.

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 16/27] x86/sgx: Add the Linux SGX Enclave Driver
  2019-03-19 21:19   ` Sean Christopherson
@ 2019-03-21 15:51     ` Jarkko Sakkinen
  2019-03-21 16:47       ` Sean Christopherson
  2019-03-26 13:26       ` Jarkko Sakkinen
  0 siblings, 2 replies; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-21 15:51 UTC (permalink / raw)
  To: Sean Christopherson
  Cc: x86, linux-sgx, akpm, dave.hansen, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Suresh Siddha

On Tue, Mar 19, 2019 at 02:19:51PM -0700, Sean Christopherson wrote:
> IMO we should get rid of SGX_POWER_LOST_ENCLAVE and the SUSPEND flag.
> 
>   - Userspace needs to handle -EFAULT cleanly even if we hook into
>     suspend/hibernate via sgx_encl_pm_notifier(), e.g. to handle virtual
>     machine migration.
>   - In the event that suspend is canceled after sgx_encl_pm_notifier()
>     runs, we'll have prematurely invalidated the enclave.
>   - Invalidating all enclaves could be slow on a system with GBs of EPC,
>     i.e. probably not the best thing to do in the suspend path.
> 
> Removing SGX_POWER_LOST_ENCLAVE means we can drop all of the pm_notifier()
> code, which will likely save us a bit of maintenance down the line.

I don't disgree. Isn't it a racy flag in the VM context i.e. because
suspend can happen without SGX core noticing it (running inside a VM)?
That would a bug.

> > +
> > +/**
> > + * 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;
> > +};
> 
> ...
> 
> > 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"
> 
> Yuck.  If we remove the driver specific Makefile then we can eliminate
> the "../" prefix here.  E.g. in the main SGX Makefile:
> 
> obj-$(CONFIG_INTEL_SGX_DRIVER) += driver/main.o driver/ioctl.o

I think this is a great idea.

> 
> > +#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
> 
> ...
> 
> > +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) {
> 
> Should we validate page_type itself?

Yes I don't see why not.

> 
> > +		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;
> > +}
> 
> ...
> 
> > +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;
> 
> These can be on a single line, e.g.: "int ret, i, j;".

I tend to put always all declarations to their own lines whenever I
write any code. When I wear my maintainer hat I tend to accept either
style for new code.

> 
> > +
> > +	/* 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;
> > +}
> 
> ...
> 
> > 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 <jarkko.sakkinen@linux.intel.com>");
> > +MODULE_LICENSE("Dual BSD/GPL");
> 
> ...
> 
> > +static const struct file_operations sgx_ctrl_fops = {
> 
> Why sgx_ctrl_fops instead of e.g. sgx_dev_fops, sgx_device_fops,
> sgx_driver_fops, etc...

sgx_dev_fops would be a better name.

> 
> > +	.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),
> > +	},
> > +};
> 
> Utilizing the platform driver is unnecessary, adds complexity and IMO is
> flat out wrong given the current direction of implementing SGX as a
> full-blooded architectural feature.
> 
>   - All hardware information is readily available via CPUID
>   - arch_initcall hooks obviates the need for ACPI autoprobe
>   - EPC manager assumes it has full control over all EPC, i.e. EPC
>     sections are not managed as independent "devices"
>   - BIOS will enumerate a single ACPI entry regardless of the number
>     of EPC sections, i.e. the ACPI entry is *only* useful for probing
>   - Userspace driver matches the EPC device, but doesn't actually
>     "own" the EPC

It is for hotplugging. I don't really have strong opinions of this but
having a driver for uapi allows things like blacklisting sgx.

> 
> > +
> > +static int __init sgx_drv_subsys_init(void)
> > +{
> > +     int ret;
> > +
> > +     ret = bus_register(&sgx_bus_type);
> 
> Do we really need a bus/class?  Allocating a chrdev region also seems like
> overkill.  At this point there is exactly one SGX device, and while there
> is a pretty good chance we'll end up with a virtualization specific device
> for exposing EPC to guest, there's no guarantee said device will be SGX
> specific.  Using a dynamic miscdevice would eliminate a big chunk of code.

AFAIK misc is not recommended for any new drivers as it has suvere
limitations like not allowing to add non-racy sysfs attributes. Whatever
the solution is, lets not use misc.

> 
> > +	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;
> 
> Failure to allocate memory for one user of the enclave shouldn't kill
> the enclave, e.g. failing during fork() shouldn't kill the enclave in
> the the parent.  And marking an enclave dead without holding its lock
> is all kinds of bad.

This is almost non-existent occasion. Agree with the locking though..
And I'm open for other fallbacks but given the rarity I think the
current one in sustainable.

> 
> > +			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);
> 
> Not that it truly matters, but list_add() is the only thing that needs
> to be protected with the spinlock, everything else can be done ahead of
> time.

True :-)

> 
> > +		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);
> 
> Isn't this unnecessary?  sgx_vma_open() had to have been called on this
> VMA, otherwise we wouldn't be here.

Not in the case when allocation fails in vma_open.

> 
> > +	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);
> 
> Again, is retrieving the sgx_encl_mm necessary?  Isn't it impossible to
> get here unless the vma has an reference to the enclave?  I.e. it should
> be impossible to fault before open() or after close().
> 
> Dropping sgx_encl_get_mm() from both sgx_vma_close() and sgx_vma_fault()
> would mean sgx_vma_open() is the only user (as of this patch), i.e. can
> open code walking through the list and wouldn't need to confusing
> mmdrop().  It'd be even cleaner if we can use an RCU list.

These checks handle the case when allocation fails in vma_open.

> 
> > +
> > +	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);
> 
> Any reason for while()+list_first_entry() instead of list_for_each_entry_safe()?

Just a preference.

> 
> > +		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__);
> 
> I'm pretty sure we can simply use an RCU list.  Iteration is then simply
> list_for_each_entry_rcu() instead of this custom walker.  Peeking into the
> future, we don't need perfect accuracy for aging pages, e.g. working with
> a stale snapshot is perfectly ok.
> 
> Zapping PTEs does need 100% accuracy, but that will be guaranteed via the
> SGX_ENCL_PAGE_RECLAIMED flag.  Any mm added to the list after we start
> zapping will be rejected by the page fault handler, i.e. won't be able
> to reference the page being reclaimed.

I'm not rejecting RCUs but still think that lock based implementation
could be more easy to stabilize, and (S)RCU based implemenation could
be something to improved after landing the patch set.

> 
> > +
> > +	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);
> > +}

/Jarkko

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 00/27] Intel SGX1 support
  2019-03-19 23:41 ` [PATCH v19 00/27] Intel SGX1 support Sean Christopherson
  2019-03-19 23:52   ` Jethro Beekman
@ 2019-03-21 16:00   ` Jarkko Sakkinen
  1 sibling, 0 replies; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-21 16:00 UTC (permalink / raw)
  To: Sean Christopherson
  Cc: x86, linux-sgx, akpm, dave.hansen, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes

On Tue, Mar 19, 2019 at 04:41:03PM -0700, Sean Christopherson wrote:
> On Sun, Mar 17, 2019 at 11:14:29PM +0200, Jarkko Sakkinen wrote:
> > Intel(R) SGX is a set of CPU instructions that can be used by applications
> > to set aside private regions of code and data. The code outside the enclave
> > is disallowed to access the memory inside the enclave by the CPU access
> > control.  In a way you can think that SGX provides inverted sandbox. It
> > protects the application from a malicious host.
> > 
> > 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:
> 
> ...
> 
> > * 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.
> 
> I'm not convinced it's worth the effort to allow the driver to be compiled
> as a module, especially if we drop the ACPI-based probing.  Making the
> driver loadable means the kernel can easily end up in situations where it's
> tracking EPC and running its reclaimer kthread, but the driver can't be
> loaded and can *never* be loaded, e.g. because the platform doesn't support
> Launch Control.
> 
> And IMO encl.{c,h} belongs in the "driver" code, but to let the driver be
> loadable it got shoved into the core subsystem.  All of that code is
> specific to running enclaves in the host, i.e. it shouldn't exist if I
> compile out the driver entirely (in a future world where I want the core
> SGX subsystem for virtualization purposes).
> 
> And yes, I realize this is a full 180 from my stance a year ago :)

I'm perfectly fine with removing platform driver is that is the common
opinion. I would still keep the bus (or equivalent) thought because it
gives possibility in the future add sysfs attributes.

> 
> > * 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.
> 
> It's not just forking, it's being able to hand off an enclave to an
> already running process.  Andy also had (IMO) valid complaints about
> completely ignoring @filep in the ioctls() and searching the vma to
> find the enclave, e.g. the current code has to acquire mmap_sem just
> to get a reference to the enclave and a process has to mmap() the
> enclave to use any ioctl() other than ECREATE.

I'm cool with this and internals are now in a shape that is trivial
to implement. Just would want an example of workload where that
would be useful. It is not only for decision making but also
for reflecting whether the change is done correctly.

We should probably also extend the selftest to do some trivial forking
or SCM_RIGHTS kind of thing depending on the API.

/Jarkko

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 16/27] x86/sgx: Add the Linux SGX Enclave Driver
  2019-03-19 23:00   ` Sean Christopherson
@ 2019-03-21 16:18     ` Jarkko Sakkinen
  2019-03-21 17:38       ` Sean Christopherson
  0 siblings, 1 reply; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-21 16:18 UTC (permalink / raw)
  To: Sean Christopherson
  Cc: x86, linux-sgx, akpm, dave.hansen, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Suresh Siddha

On Tue, Mar 19, 2019 at 04:00:47PM -0700, Sean Christopherson wrote:
> On Sun, Mar 17, 2019 at 11:14:45PM +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.
> 
> 
> ...
> 
> > 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
> 
> ...
> 
> > +/**
> > + * 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)) {
> 
> This is a use-after-free scenario if mm_count==0.  Once the count goes
> to zero, __mmdrop() begins, at which point this code is racing against
> free_mm().  What you want here (or rather, in flows where mm != current->mm
> and you want to access PTEs) is mmget_not_zero(), i.e. "unless zero"
> on mm_users.  mm_count prevents the mm_struct from being freed, but
> doesn't protect the page tables.  mm_users protects the page tables,
> i.e. lets us safely call sgx_encl_test_and_clear_young in the reclaimer.
> 
> To ensure liveliness of the mm itself, register an mmu_notifier for each
> mm_struct (I think in sgx_vma_open()).  The enclave's .release callback
> would then delete the mm from its list and drop its reference (exit_mmap()
> holds a reference to mm_count so it's safe to do mmdrop() in the .release
> callback).  E.g.:
> 
> static void sgx_vma_open(struct vm_area_struct *vma)
> {
> 	...
> 
> 	rcu_read_lock();
> 	list_for_each_entry_rcu(...) {
> 		if (vma->vm_mm == tmp->mm) {
> 			encl_mm = tmp;
> 			break;
> 		}
> 	}
> 	rcu_read_unlock();
> 
> 	if (!encl_mm) {
> 		mm = kzalloc(sizeof(*mm), GFP_KERNEL);
> 		if (!mm) {
> 			goto error;
> 
> 		encl_mm->encl = encl;
> 		encl_mm->mm = vma->vm_mm;
> 
> 		if (mmu_notifier_register(&encl->mmu_notifier, encl_mm)) {
> 			kfree(encl_mm);
> 			goto error;
> 		}

OK, thanks for catching the bug. I'm cool with adding MMU notifier back.
Just wondering when unregister should be called.

> 
> 		spin_lock(&encl->mm_lock);
> 		list_add(&encl_mm->list, &encl->mm_list);
> 		spin_unlock(&encl->mm_lock);
> 	}
> 
> 	...
> error:
> 	<not sure what should go here if we don't kill the enclave>
> }
> 
> static void sgx_encl_mmu_release(struct mmu_notifier *mn, struct mm_struct *mm)
> {
> 	struct sgx_encl_mm *encl_mm =
> 		container_of(mn, struct sgx_encl_mm, mmu_notifier);
> 
> 	spin_lock(encl_mm->encl->mm_lock);
> 	list_del_rcu(&encl_mm->list);
> 	spin_unlock(encl_mm->encl->mm_lock);
> 
> 	synchronize_rcu();
> 
> 	mmdrop(mm);
> }
> 
> Alternatively, the sgx_encl_mmu_release() could mark the encl_mm as dead
> instead of removing it from the list, but I don't think that'd mesh well
> with an RCU list, i.e. we'd need a regular lock-protected list and a
> custom walker.
> 
> The only downside with the RCU approach that I can think of is that the
> encl_mm would stay on the enclave's list until the enclave or the mm
> itself died.  That could result in unnecessary IPIs during reclaim (or
> invalidation), but that seems like a minor corner case that could be
> avoided in userspace, e.g. don't mmap() an enclave unless you actually
> plan on running it.

Yeah, that is really the root why ended up what I have i.e to be able
to move them real time. If they can be in the list forever, then RCU
is doable. I was wondering with your RCU comments how you would deal
with this.

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

/Jarkko

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 00/27] Intel SGX1 support
  2019-03-19 23:52   ` Jethro Beekman
  2019-03-20  0:22     ` Sean Christopherson
@ 2019-03-21 16:20     ` Jarkko Sakkinen
  1 sibling, 0 replies; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-21 16:20 UTC (permalink / raw)
  To: Jethro Beekman
  Cc: Sean Christopherson, x86, linux-sgx, akpm, dave.hansen, nhorman,
	npmccallum, serge.ayoun, shay.katz-zamir, haitao.huang,
	andriy.shevchenko, tglx, kai.svahn, bp, josh, luto, kai.huang,
	rientjes

On Tue, Mar 19, 2019 at 11:52:32PM +0000, Jethro Beekman wrote:
> > And IMO encl.{c,h} belongs in the "driver" code, but to let the driver be
> > loadable it got shoved into the core subsystem.  All of that code is
> > specific to running enclaves in the host, i.e. it shouldn't exist if I
> > compile out the driver entirely (in a future world where I want the core
> > SGX subsystem for virtualization purposes).
> 
> Your argument here is "something that belongs in the driver isn't, therefore
> we shouldn't have a loadable driver". This seems backwards to me. Instead,
> we should see what interface would be needed so that this stuff *can* be in
> the driver.

Yes, all the code that needs to be in there to deal with enclaves is
already there in v19. Only the ioctl's live in the driver.

The way I see it the driver part was wrong before and now the split
is way than before.

/Jarkko

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 25/27] x86/sgx: SGX documentation
  2019-03-20 17:14   ` Sean Christopherson
@ 2019-03-21 16:24     ` Jarkko Sakkinen
  0 siblings, 0 replies; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-21 16:24 UTC (permalink / raw)
  To: Sean Christopherson
  Cc: x86, linux-sgx, akpm, dave.hansen, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes

On Wed, Mar 20, 2019 at 10:14:22AM -0700, Sean Christopherson wrote:
> On Sun, Mar 17, 2019 at 11:14:54PM +0200, Jarkko Sakkinen wrote:
> > 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 <jarkko.sakkinen@linux.intel.com>
> > Co-developed-by: Sean Christopherson <sean.j.christopherson@intel.com>
> > Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
> > ---
> > +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.
> 
> The overview should probably be limited to architectural features, e.g. the
> ISA, *architectural* EPC and EPCM behavior, etc...  Then add a dedicated
> section on EPC implementation(s), which are micro-architecture specific.
> 
> > +
> > +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
>                                   ^^^^^^^
> 
> I'm 99.99999% certain the above should be "linear address range".  The SDM
> SDM first introduces ELRANGE as working with logical addresses:
> 
>     Address space allocation is the specification of the range of logical
>     addresses that the enclave may use. This range is called the ELRANGE.
> 
> but I think that's a documentation bug.  Subsequent references in the SDM
> all refer to linear addresses, and the ELRANGE checks definitely work with
> linear addresses.
> 
> > +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
> 
> This isn't correct.  ELRANGE protections are activated when only executing
> inside an enclave.  An enclave's memory is protected by the EPC{M} when the
> CPU is operating outside the enclave or in a different enclave.
> 
> > +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
> 
> The VM-Exit blurb is technically no longer accurate, e.g. a VMM can choose
> to intercept RDTSC, but RDTSC is also allowed in enclaves.
> 
> > +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.
> 
> The page type descriptions could use a bit of improvement.  Tangentially
> related, would it make sense to add a "Terminology" section given the
> absurd number of acronyms used by SGX?
> 
> > +
> > +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).
> 
> This section should also call out that the IA32_SGXLEPUBKEYHASHn MSRs also
> affect EINIT tokens.  And simply being signed with the key referenced by
> IA32_SGXLEPUBKEYHASHn doesn't grant access to the EINITTOKEN_KEY, the
> enclave still needs to explicit request access, e.g. the kernel could deny
> access to the EINITTOKEN_KEY by refusing to create enclaves that request
> said key.
> 
> > +**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.
> 
> ...
> 
> > +The PF_SGX bit is set if and only if the #PF is detected by the SGX Enclave Page
> 
> The ordering here is a bit backwards, e.g. the EPCM and its protections
> should come first, and then describe the faults that can be signaled by
> the EPCM.
> 
> > +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
> 
> Stale file reference.
> 
> > +   :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
> 
> Probably should leave off the exact section, e.g. "39.1.4" is already out
> of date.
> 
> IMO this whole document could use an overhaul to uplevel the overview
> and {micro-}architecture descriptions to make it a useful standalone doc
> instead of a poor man's regurgitation of the SDM.  If there are no
> objections, I'll start typing and reply with a fresh patch at some point.
> 
> Oh, and the vDSO interface needs to be added, I'll do that too.

Shortly: agree with the comments.

/Jarkko

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 16/27] x86/sgx: Add the Linux SGX Enclave Driver
  2019-03-21 15:51     ` Jarkko Sakkinen
@ 2019-03-21 16:47       ` Sean Christopherson
  2019-03-22 11:10         ` Jarkko Sakkinen
  2019-03-26 13:26       ` Jarkko Sakkinen
  1 sibling, 1 reply; 92+ messages in thread
From: Sean Christopherson @ 2019-03-21 16:47 UTC (permalink / raw)
  To: Jarkko Sakkinen
  Cc: x86, linux-sgx, akpm, dave.hansen, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Suresh Siddha

On Thu, Mar 21, 2019 at 05:51:11PM +0200, Jarkko Sakkinen wrote:
> On Tue, Mar 19, 2019 at 02:19:51PM -0700, Sean Christopherson wrote:
> > IMO we should get rid of SGX_POWER_LOST_ENCLAVE and the SUSPEND flag.
> > 
> >   - Userspace needs to handle -EFAULT cleanly even if we hook into
> >     suspend/hibernate via sgx_encl_pm_notifier(), e.g. to handle virtual
> >     machine migration.
> >   - In the event that suspend is canceled after sgx_encl_pm_notifier()
> >     runs, we'll have prematurely invalidated the enclave.
> >   - Invalidating all enclaves could be slow on a system with GBs of EPC,
> >     i.e. probably not the best thing to do in the suspend path.
> > 
> > Removing SGX_POWER_LOST_ENCLAVE means we can drop all of the pm_notifier()
> > code, which will likely save us a bit of maintenance down the line.
> 
> I don't disgree. Isn't it a racy flag in the VM context i.e. because
> suspend can happen without SGX core noticing it (running inside a VM)?
> That would a bug.


...

> > > +#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),
> > > +	},
> > > +};
> > 
> > Utilizing the platform driver is unnecessary, adds complexity and IMO is
> > flat out wrong given the current direction of implementing SGX as a
> > full-blooded architectural feature.
> > 
> >   - All hardware information is readily available via CPUID
> >   - arch_initcall hooks obviates the need for ACPI autoprobe
> >   - EPC manager assumes it has full control over all EPC, i.e. EPC
> >     sections are not managed as independent "devices"
> >   - BIOS will enumerate a single ACPI entry regardless of the number
> >     of EPC sections, i.e. the ACPI entry is *only* useful for probing
> >   - Userspace driver matches the EPC device, but doesn't actually
> >     "own" the EPC
> 
> It is for hotplugging. I don't really have strong opinions of this but
> having a driver for uapi allows things like blacklisting sgx.

Hotplugging what?  EPC can't be hotplugged, EPC enumeration through CPUID
won't change post-boot and the ACPI entry can't be relied upon for EPC
base/size information when there are multiple EPC sections.

> > > +
> > > +static int __init sgx_drv_subsys_init(void)
> > > +{
> > > +     int ret;
> > > +
> > > +     ret = bus_register(&sgx_bus_type);
> > 
> > Do we really need a bus/class?  Allocating a chrdev region also seems like
> > overkill.  At this point there is exactly one SGX device, and while there
> > is a pretty good chance we'll end up with a virtualization specific device
> > for exposing EPC to guest, there's no guarantee said device will be SGX
> > specific.  Using a dynamic miscdevice would eliminate a big chunk of code.
> 
> AFAIK misc is not recommended for any new drivers as it has suvere
> limitations like not allowing to add non-racy sysfs attributes. Whatever
> the solution is, lets not use misc.

Ah right, forgot about that.

> > > +	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_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;
> > 
> > Failure to allocate memory for one user of the enclave shouldn't kill
> > the enclave, e.g. failing during fork() shouldn't kill the enclave in
> > the the parent.  And marking an enclave dead without holding its lock
> > is all kinds of bad.
> 
> This is almost non-existent occasion. Agree with the locking though..
> And I'm open for other fallbacks but given the rarity I think the
> current one in sustainable.

What if we clear vm_private_data?  And maybe do a pr_warn_ratelimited()
so that userspace gets some form of notification that forking an enclave
failed.  A NULL encl is easy to check in the fault handler and any where
else we consume vmas.

> 
> > 
> > > +			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);
> > 
> > Not that it truly matters, but list_add() is the only thing that needs
> > to be protected with the spinlock, everything else can be done ahead of
> > time.
> 
> True :-)
> 
> > 
> > > +		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);
> > 
> > Isn't this unnecessary?  sgx_vma_open() had to have been called on this
> > VMA, otherwise we wouldn't be here.
> 
> Not in the case when allocation fails in vma_open.

Ah, I see the flow.  If we do keep the enclave killing behavior then I
think it'd make sense to let this be handled by checking SGX_ENCL_DEAD.
But AFAICT things will "just work" if we nullify vm_private_data.

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 17/27] x86/sgx: Add provisioning
  2019-03-17 21:14 ` [PATCH v19 17/27] x86/sgx: Add provisioning Jarkko Sakkinen
  2019-03-19 20:09   ` Sean Christopherson
  2019-03-21 14:38   ` Nathaniel McCallum
@ 2019-03-21 16:50   ` Andy Lutomirski
  2019-03-22 11:29     ` Jarkko Sakkinen
  2 siblings, 1 reply; 92+ messages in thread
From: Andy Lutomirski @ 2019-03-21 16:50 UTC (permalink / raw)
  To: Jarkko Sakkinen
  Cc: X86 ML, linux-sgx, Andrew Morton, Dave Hansen, Christopherson,
	Sean J, nhorman, npmccallum, Ayoun, Serge, Katz-zamir, Shay,
	Huang, Haitao, Andy Shevchenko, Thomas Gleixner, Svahn, Kai,
	Borislav Petkov, Josh Triplett, Andrew Lutomirski, Huang, Kai,
	David Rientjes, James Morris, Serge E . Hallyn, LSM List

On Sun, Mar 17, 2019 at 2:18 PM Jarkko Sakkinen
<jarkko.sakkinen@linux.intel.com> wrote:
>
> 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;
>    };

Here's a potential issue:

For container use, is it reasonable for a container manager to
bind-mount a file into securityfs?  Or would something in /dev make
this easier?

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 16/27] x86/sgx: Add the Linux SGX Enclave Driver
  2019-03-21 16:18     ` Jarkko Sakkinen
@ 2019-03-21 17:38       ` Sean Christopherson
  2019-03-22 11:17         ` Jarkko Sakkinen
  0 siblings, 1 reply; 92+ messages in thread
From: Sean Christopherson @ 2019-03-21 17:38 UTC (permalink / raw)
  To: Jarkko Sakkinen
  Cc: x86, linux-sgx, akpm, dave.hansen, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Suresh Siddha

On Thu, Mar 21, 2019 at 06:18:27PM +0200, Jarkko Sakkinen wrote:
> On Tue, Mar 19, 2019 at 04:00:47PM -0700, Sean Christopherson wrote:
> > On Sun, Mar 17, 2019 at 11:14:45PM +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.
> > 
> > 
> > ...
> > 
> > > 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
> > 
> > ...
> > 
> > > +/**
> > > + * 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)) {
> > 
> > This is a use-after-free scenario if mm_count==0.  Once the count goes
> > to zero, __mmdrop() begins, at which point this code is racing against
> > free_mm().  What you want here (or rather, in flows where mm != current->mm
> > and you want to access PTEs) is mmget_not_zero(), i.e. "unless zero"
> > on mm_users.  mm_count prevents the mm_struct from being freed, but
> > doesn't protect the page tables.  mm_users protects the page tables,
> > i.e. lets us safely call sgx_encl_test_and_clear_young in the reclaimer.
> > 
> > To ensure liveliness of the mm itself, register an mmu_notifier for each
> > mm_struct (I think in sgx_vma_open()).  The enclave's .release callback
> > would then delete the mm from its list and drop its reference (exit_mmap()
> > holds a reference to mm_count so it's safe to do mmdrop() in the .release
> > callback).  E.g.:
> > 
> > static void sgx_vma_open(struct vm_area_struct *vma)
> > {
> > 	...
> > 
> > 	rcu_read_lock();
> > 	list_for_each_entry_rcu(...) {
> > 		if (vma->vm_mm == tmp->mm) {
> > 			encl_mm = tmp;
> > 			break;
> > 		}
> > 	}
> > 	rcu_read_unlock();
> > 
> > 	if (!encl_mm) {
> > 		mm = kzalloc(sizeof(*mm), GFP_KERNEL);
> > 		if (!mm) {
> > 			goto error;
> > 
> > 		encl_mm->encl = encl;
> > 		encl_mm->mm = vma->vm_mm;
> > 
> > 		if (mmu_notifier_register(&encl->mmu_notifier, encl_mm)) {
> > 			kfree(encl_mm);
> > 			goto error;
> > 		}
> 
> OK, thanks for catching the bug. I'm cool with adding MMU notifier back.
> Just wondering when unregister should be called.

We'd need to refcount the encl_mm and unregister its callback when its
refcount goes to zero.  I dislike the idea of refcounting both encl and
encl_mm, but it does seem to be the most (only?) robust solution.

The alternative is to not refcount encl_mm, e.g. unregister the callback
when the encl itself is freed, but then there is no way to detect when
the last vma is closed, i.e. we have to hold the encl_mm until the entire
mm_struct dies.

> 
> > 
> > 		spin_lock(&encl->mm_lock);
> > 		list_add(&encl_mm->list, &encl->mm_list);
> > 		spin_unlock(&encl->mm_lock);
> > 	}
> > 
> > 	...
> > error:
> > 	<not sure what should go here if we don't kill the enclave>
> > }
> > 
> > static void sgx_encl_mmu_release(struct mmu_notifier *mn, struct mm_struct *mm)
> > {
> > 	struct sgx_encl_mm *encl_mm =
> > 		container_of(mn, struct sgx_encl_mm, mmu_notifier);
> > 
> > 	spin_lock(encl_mm->encl->mm_lock);
> > 	list_del_rcu(&encl_mm->list);
> > 	spin_unlock(encl_mm->encl->mm_lock);
> > 
> > 	synchronize_rcu();
> > 
> > 	mmdrop(mm);
> > }
> > 
> > Alternatively, the sgx_encl_mmu_release() could mark the encl_mm as dead
> > instead of removing it from the list, but I don't think that'd mesh well
> > with an RCU list, i.e. we'd need a regular lock-protected list and a
> > custom walker.
> > 
> > The only downside with the RCU approach that I can think of is that the
> > encl_mm would stay on the enclave's list until the enclave or the mm
> > itself died.  That could result in unnecessary IPIs during reclaim (or
> > invalidation), but that seems like a minor corner case that could be
> > avoided in userspace, e.g. don't mmap() an enclave unless you actually
> > plan on running it.
> 
> Yeah, that is really the root why ended up what I have i.e to be able
> to move them real time. If they can be in the list forever, then RCU
> is doable. I was wondering with your RCU comments how you would deal
> with this.

Aha!  Similar to the old 1:1 encl:mm approach, the release callback would
simply mark the associated entity "dead", in this case the encl_mm.  We'd
still refcount encl_mm and handle unregistering and whatnot when the last
vma is closed, i.e. refcount goes to zero.  Marking the encl_mm as dead
instead of trying to delete it from the list avoids scenarios where we're
holding a reference to the encl_mm but it's no longer on the list.

The synchronize_srcu() during release ensures we don't operate on a dead
enclave.  And the only time we'd take mm_lock is to insert/delete to/from
the list, i.e. vma open/close, thus sidestepping lock ordering issues
between mm_lock and mmap_sem.  Traversing the list in the fault handler
can be avoided by nullifying vm_private_data or by checking the liveliness
of the enclave iself.

E.g.:

static void sgx_encl_mmu_release(struct mmu_notifier *mn, struct mm_struct *mm)
{
        struct sgx_encl_mm *encl_mm =
                container_of(mn, struct sgx_encl_mm, mmu_notifier);

	encl_mm->dead = true;

        synchronize_srcu(&encl_srcu);
}


And reclaim looks something like:

static void sgx_reclaimer_block(struct sgx_epc_page *epc_page)
{
	...

	id = srcu_read_lock(&encl_srcu);

	list_for_each_entry_rcu(...) {
		if (encl_mm->dead)
			continue;

		down_read(&encl_mm->mm->mmap_sem);

		ret = sgx_encl_find(encl_mm->mm, addr, &vma);
		if (!ret && encl == vma->vm_private_data)
			zap_vma_ptes(vma, addr, PAGE_SIZE);

		up_read(&encl_mm->mm->mmap_sem);
	}

	srcu_read_unlock(&encl_srcu, id);

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



^ permalink raw reply	[flat|nested] 92+ messages in thread

* RE: [PATCH v19 17/27] x86/sgx: Add provisioning
  2019-03-21 14:32       ` Jarkko Sakkinen
@ 2019-03-21 21:41         ` Huang, Kai
  2019-03-22 11:31           ` Jarkko Sakkinen
  0 siblings, 1 reply; 92+ messages in thread
From: Huang, Kai @ 2019-03-21 21:41 UTC (permalink / raw)
  To: Jarkko Sakkinen
  Cc: Christopherson, Sean J, Svahn, Kai, nhorman, jmorris, rientjes,
	josh, tglx, Ayoun, Serge, Huang, Haitao, linux-security-module,
	x86, akpm, npmccallum, linux-sgx, luto, Katz-zamir, Shay, Hansen,
	Dave, bp, serge, andriy.shevchenko

> On Thu, Mar 21, 2019 at 02:08:36AM +0000, Huang, Kai wrote:
> > On Tue, 2019-03-19 at 13:09 -0700, Sean Christopherson wrote:
> > > On Sun, Mar 17, 2019 at 11:14:46PM +0200, Jarkko Sakkinen wrote:
> > > > 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;
> > > >    };
> >
> > Would you elaborate why the name is "token_fd"? I think *token* in SGX
> > has more specific meaning?
> 
> I'm open for other names.

How about attribute_fd, which is consistent with your IOCTL?

Thanks,
-Kai

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 12/27] x86/sgx: Enumerate and track EPC sections
  2019-03-21 15:28       ` Sean Christopherson
@ 2019-03-22 10:19         ` Jarkko Sakkinen
  2019-03-22 10:50           ` Jarkko Sakkinen
  0 siblings, 1 reply; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-22 10:19 UTC (permalink / raw)
  To: Sean Christopherson
  Cc: x86, linux-sgx, akpm, dave.hansen, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Suresh Siddha

On Thu, Mar 21, 2019 at 08:28:10AM -0700, Sean Christopherson wrote:
> On Thu, Mar 21, 2019 at 04:40:56PM +0200, Jarkko Sakkinen wrote:
> > On Mon, Mar 18, 2019 at 12:50:43PM -0700, Sean Christopherson wrote:
> > > On Sun, Mar 17, 2019 at 11:14:41PM +0200, Jarkko Sakkinen wrote:
> > > Dynamically allocating sgx_epc_sections isn't exactly difficult, and
> > > AFAICT the static allocation is the primary motivation for capping
> > > SGX_MAX_EPC_SECTIONS at such a low value (8).  I still think it makes
> > > sense to define SGX_MAX_EPC_SECTIONS so that the section number can
> > > be embedded in the offset, along with flags.  But the max can be
> > > significantly higher, e.g. using 7 bits to support 128 sections.
> > > 
> > 
> > I don't disagree with you but I think for the existing and forseeable
> > hardware this is good enough. Can be refined if there is ever need.
> 
> My concern is that there may be virtualization use cases that want to
> expose more than 8 EPC sections to a guest.  I have no idea if this is
> anything more than paranoia, but at the same time the cost to increase
> support to 128+ sections is quite low.
> 
> > > I realize hardware is highly unlikely to have more than 8 sections, at
> > > least for the near future, but IMO the small amount of extra complexity
> > > is worth having a bit of breathing room.
> > 
> > Yup.
> > 
> > > > +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(&section->lock);
> > > > +	INIT_LIST_HEAD(&section->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);
> > > > +	}
> > > 
> > > Not sure if this is the correct location, but at some point the kernel
> > > needs to sanitize the EPC during init.  EPC pages may be in an unknown
> > > state, e.g. after kexec(), which will cause all manner of faults and
> > > warnings.  Maybe the best approach is to sanitize on-demand, e.g. suppress
> > > the first WARN due to unexpected ENCLS failure and purge the EPC at that
> > > time.  The downside of that approach is that exposing EPC to a guest would
> > > need to implement its own sanitization flow.
> > 
> > Hmm... Lets think this through. I'm just thinking how sanitization on
> > demand would actually work given the parent-child relationships.
> 
> It's ugly.
> 
>   1. Temporarily disable EPC allocation and enclave fault handling
>   2. Zap all TCS PTEs in all enclaves
>   3. Flush all logical CPUs from enclaves via IPI
>   4. Forcefully reclaim all EPC pages from enclaves
>   5. EREMOVE all "free" EPC pages, track pages that fail with SGX_CHILD_PRESENT
>   6. EREMOVE all EPC pages that failed with SGX_CHILD_PRESENT
>   7. Disable SGX if any EREMOVE failed in step 6
>   8. Re-enable EPC allocation and enclave fault handling
> 
> Exposing EPC to a VM would still require sanitization.
> 
> Sanitizing during boot is a lot cleaner, the primary concern is that it
> will significantly increase boot time on systems with large EPCs.  If we
> can somehow limit this to kexec() and that's the only scenario where the
> EPC needs to be sanitized, then that would mitigate the boot time concern.
> 
> We might also be able to get away with unconditionally sanitizing the EPC
> post-boot, e.g. via worker threads, returning -EBUSY for everything until
> the EPC is good to go.

I like the worker threads approach better. It is something that is
maintainable. I don't see any better solution given the hierarchical
nature of enclaves. It is also fairly to implement without making
major changes to the other parts of the implementation.

I.e. every time the driver initializes:

1. Move all EPC first to a bad pool.
2. Let worker threads move EPC to the real allocation pool.

Then the OS can immediately start to use EPC.

Is this about along the lines what you had in mind?

/Jarkko

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 12/27] x86/sgx: Enumerate and track EPC sections
  2019-03-22 10:19         ` Jarkko Sakkinen
@ 2019-03-22 10:50           ` Jarkko Sakkinen
  0 siblings, 0 replies; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-22 10:50 UTC (permalink / raw)
  To: Sean Christopherson
  Cc: x86, linux-sgx, akpm, dave.hansen, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Suresh Siddha

On Fri, Mar 22, 2019 at 12:19:40PM +0200, Jarkko Sakkinen wrote:
> On Thu, Mar 21, 2019 at 08:28:10AM -0700, Sean Christopherson wrote:
> > On Thu, Mar 21, 2019 at 04:40:56PM +0200, Jarkko Sakkinen wrote:
> > > On Mon, Mar 18, 2019 at 12:50:43PM -0700, Sean Christopherson wrote:
> > > > On Sun, Mar 17, 2019 at 11:14:41PM +0200, Jarkko Sakkinen wrote:
> > > > Dynamically allocating sgx_epc_sections isn't exactly difficult, and
> > > > AFAICT the static allocation is the primary motivation for capping
> > > > SGX_MAX_EPC_SECTIONS at such a low value (8).  I still think it makes
> > > > sense to define SGX_MAX_EPC_SECTIONS so that the section number can
> > > > be embedded in the offset, along with flags.  But the max can be
> > > > significantly higher, e.g. using 7 bits to support 128 sections.
> > > > 
> > > 
> > > I don't disagree with you but I think for the existing and forseeable
> > > hardware this is good enough. Can be refined if there is ever need.
> > 
> > My concern is that there may be virtualization use cases that want to
> > expose more than 8 EPC sections to a guest.  I have no idea if this is
> > anything more than paranoia, but at the same time the cost to increase
> > support to 128+ sections is quite low.
> > 
> > > > I realize hardware is highly unlikely to have more than 8 sections, at
> > > > least for the near future, but IMO the small amount of extra complexity
> > > > is worth having a bit of breathing room.
> > > 
> > > Yup.
> > > 
> > > > > +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(&section->lock);
> > > > > +	INIT_LIST_HEAD(&section->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);
> > > > > +	}
> > > > 
> > > > Not sure if this is the correct location, but at some point the kernel
> > > > needs to sanitize the EPC during init.  EPC pages may be in an unknown
> > > > state, e.g. after kexec(), which will cause all manner of faults and
> > > > warnings.  Maybe the best approach is to sanitize on-demand, e.g. suppress
> > > > the first WARN due to unexpected ENCLS failure and purge the EPC at that
> > > > time.  The downside of that approach is that exposing EPC to a guest would
> > > > need to implement its own sanitization flow.
> > > 
> > > Hmm... Lets think this through. I'm just thinking how sanitization on
> > > demand would actually work given the parent-child relationships.
> > 
> > It's ugly.
> > 
> >   1. Temporarily disable EPC allocation and enclave fault handling
> >   2. Zap all TCS PTEs in all enclaves
> >   3. Flush all logical CPUs from enclaves via IPI
> >   4. Forcefully reclaim all EPC pages from enclaves
> >   5. EREMOVE all "free" EPC pages, track pages that fail with SGX_CHILD_PRESENT
> >   6. EREMOVE all EPC pages that failed with SGX_CHILD_PRESENT
> >   7. Disable SGX if any EREMOVE failed in step 6
> >   8. Re-enable EPC allocation and enclave fault handling
> > 
> > Exposing EPC to a VM would still require sanitization.
> > 
> > Sanitizing during boot is a lot cleaner, the primary concern is that it
> > will significantly increase boot time on systems with large EPCs.  If we
> > can somehow limit this to kexec() and that's the only scenario where the
> > EPC needs to be sanitized, then that would mitigate the boot time concern.
> > 
> > We might also be able to get away with unconditionally sanitizing the EPC
> > post-boot, e.g. via worker threads, returning -EBUSY for everything until
> > the EPC is good to go.
> 
> I like the worker threads approach better. It is something that is
> maintainable. I don't see any better solution given the hierarchical
> nature of enclaves. It is also fairly to implement without making
> major changes to the other parts of the implementation.
> 
> I.e. every time the driver initializes:
> 
> 1. Move all EPC first to a bad pool.
> 2. Let worker threads move EPC to the real allocation pool.
> 
> Then the OS can immediately start to use EPC.
> 
> Is this about along the lines what you had in mind?

We could even simplify this by using the already existing reclaimer
thread for the purpose.

/Jarkko

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 13/27] x86/sgx: Add wrappers for ENCLS leaf functions
  2019-03-21 15:40       ` Sean Christopherson
@ 2019-03-22 11:00         ` Jarkko Sakkinen
  2019-03-22 16:43           ` Sean Christopherson
  0 siblings, 1 reply; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-22 11:00 UTC (permalink / raw)
  To: Sean Christopherson
  Cc: x86, linux-sgx, akpm, dave.hansen, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes

On Thu, Mar 21, 2019 at 08:40:11AM -0700, Sean Christopherson wrote:
> On Thu, Mar 21, 2019 at 04:51:53PM +0200, Jarkko Sakkinen wrote:
> > On Tue, Mar 19, 2019 at 12:59:07PM -0700, Sean Christopherson wrote:
> > > On Sun, Mar 17, 2019 at 11:14:42PM +0200, Jarkko Sakkinen wrote:
> > > > +/**
> > > > + * 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)
> > > 
> > > I honestly can't remember, is there a reason ENCLS_TRAPNR is still a macro
> > > and not an inline function?
> > 
> > Not at all. And also the value should be unsigned and fault flag
> > should be 0x80* because we never should get negative values.
> 
> From ENCLS itself, but we do have scenarios where the return code can
> hold a system error code (negative value), an SGX error code (positive
> value) or fault inforation.  E.g. sgx_encl_init() sets ret to -ERESTARTSYS,
> and __sgx_encl_ewb() returns system error codes if get_backing() fails
> and also directly returns the result of __ewb().  We could dance around
> those issues, but IMO it's too fragile as it essentially requires a full
> audit of every possible error path when modifying code.

There should be zero of those now but if you find one we can fix that.
These inline functions should only deal with what comes from ENCLS. I
think it is cleanest to fix those sites. I think here the focus should
be on clarity.

In ioctl.c there are total 5 sites and all of them are good.

Also encl.c is all good.

From the sites that you mentioned sgx_encl_ewb() needs to be fixed. In
reclaim.c that seems to be the only problematic site.

These are pretty easy to grep with 'encls_'.

For me it looks like that assuming the positive value is absolutely
the right thing to do as long as sgx_encl_ewb() is fixed properly.

/Jarkko

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 16/27] x86/sgx: Add the Linux SGX Enclave Driver
  2019-03-21 16:47       ` Sean Christopherson
@ 2019-03-22 11:10         ` Jarkko Sakkinen
  0 siblings, 0 replies; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-22 11:10 UTC (permalink / raw)
  To: Sean Christopherson
  Cc: x86, linux-sgx, akpm, dave.hansen, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Suresh Siddha

On Thu, Mar 21, 2019 at 09:47:14AM -0700, Sean Christopherson wrote:
> Hotplugging what?  EPC can't be hotplugged, EPC enumeration through CPUID
> won't change post-boot and the ACPI entry can't be relied upon for EPC
> base/size information when there are multiple EPC sections.

AFAIK still there should be no multiple entries with the same id.

> What if we clear vm_private_data?  And maybe do a pr_warn_ratelimited()
> so that userspace gets some form of notification that forking an enclave
> failed.  A NULL encl is easy to check in the fault handler and any where
> else we consume vmas.

That might work.

> Ah, I see the flow.  If we do keep the enclave killing behavior then I
> think it'd make sense to let this be handled by checking SGX_ENCL_DEAD.
> But AFAICT things will "just work" if we nullify vm_private_data.

Yeah so I would refine this by nullifying vm_private_data as you
suggested. This will keep other processes alive having the enclave.
#PF handler shoud check that and SIGBUS.

/Jarkko

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 16/27] x86/sgx: Add the Linux SGX Enclave Driver
  2019-03-21 17:38       ` Sean Christopherson
@ 2019-03-22 11:17         ` Jarkko Sakkinen
  0 siblings, 0 replies; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-22 11:17 UTC (permalink / raw)
  To: Sean Christopherson
  Cc: x86, linux-sgx, akpm, dave.hansen, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Suresh Siddha

On Thu, Mar 21, 2019 at 10:38:13AM -0700, Sean Christopherson wrote:
> We'd need to refcount the encl_mm and unregister its callback when its
> refcount goes to zero.  I dislike the idea of refcounting both encl and
> encl_mm, but it does seem to be the most (only?) robust solution.
> 
> The alternative is to not refcount encl_mm, e.g. unregister the callback
> when the encl itself is freed, but then there is no way to detect when
> the last vma is closed, i.e. we have to hold the encl_mm until the entire
> mm_struct dies.

Yeah.

> Aha!  Similar to the old 1:1 encl:mm approach, the release callback would
> simply mark the associated entity "dead", in this case the encl_mm.  We'd
> still refcount encl_mm and handle unregistering and whatnot when the last
> vma is closed, i.e. refcount goes to zero.  Marking the encl_mm as dead
> instead of trying to delete it from the list avoids scenarios where we're
> holding a reference to the encl_mm but it's no longer on the list.
> 
> The synchronize_srcu() during release ensures we don't operate on a dead
> enclave.  And the only time we'd take mm_lock is to insert/delete to/from
> the list, i.e. vma open/close, thus sidestepping lock ordering issues
> between mm_lock and mmap_sem.  Traversing the list in the fault handler
> can be avoided by nullifying vm_private_data or by checking the liveliness
> of the enclave iself.

This sounds like like a good refinement! We can give it shot and it does
not completely turn over the implemention that I did, just makes it much
more streamlined. I can cope with this.

/Jarkko

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 17/27] x86/sgx: Add provisioning
  2019-03-21 14:38   ` Nathaniel McCallum
@ 2019-03-22 11:22     ` Jarkko Sakkinen
  0 siblings, 0 replies; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-22 11:22 UTC (permalink / raw)
  To: Nathaniel McCallum
  Cc: x86, linux-sgx, akpm, dave.hansen, sean.j.christopherson,
	Neil Horman, Ayoun, Serge, shay.katz-zamir, Huang, Haitao,
	andriy.shevchenko, tglx, Svahn, Kai, bp, josh, luto, kai.huang,
	rientjes, James Morris, Serge E . Hallyn, linux-security-module

On Thu, Mar 21, 2019 at 10:38:32AM -0400, Nathaniel McCallum wrote:
> On Sun, Mar 17, 2019 at 5:18 PM Jarkko Sakkinen
> <jarkko.sakkinen@linux.intel.com> wrote:
> > In order to provide a mechanism for devilering provisoning rights:
>
> I'm not sure what nefarious spirits have to do with this patch.
> Perhaps you meant "delivering provisioning"? ;)

Oops, yeah, maybe summoning won't this time :-)

/Jarkko

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 17/27] x86/sgx: Add provisioning
  2019-03-21 16:50   ` Andy Lutomirski
@ 2019-03-22 11:29     ` Jarkko Sakkinen
  2019-03-22 11:43       ` Jarkko Sakkinen
  0 siblings, 1 reply; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-22 11:29 UTC (permalink / raw)
  To: Andy Lutomirski
  Cc: X86 ML, linux-sgx, Andrew Morton, Dave Hansen, Christopherson,
	Sean J, nhorman, npmccallum, Ayoun, Serge, Katz-zamir, Shay,
	Huang, Haitao, Andy Shevchenko, Thomas Gleixner, Svahn, Kai,
	Borislav Petkov, Josh Triplett, Huang, Kai, David Rientjes,
	James Morris, Serge E . Hallyn, LSM List

On Thu, Mar 21, 2019 at 09:50:41AM -0700, Andy Lutomirski wrote:
> On Sun, Mar 17, 2019 at 2:18 PM Jarkko Sakkinen
> <jarkko.sakkinen@linux.intel.com> wrote:
> >
> > 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;
> >    };
> 
> Here's a potential issue:
> 
> For container use, is it reasonable for a container manager to
> bind-mount a file into securityfs?  Or would something in /dev make
> this easier?

I guess that is a valid point given that the securityfs contains the LSM
(e.g. SELinux or AppArmor) policy. So yeah, I think your are right what
you say.

I propose that we create /dev/sgx/enclave to act as the enclave manager
and /dev/sgx/provision for provisioning. Is this sustainable for you?

/Jarkko

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 17/27] x86/sgx: Add provisioning
  2019-03-21 21:41         ` Huang, Kai
@ 2019-03-22 11:31           ` Jarkko Sakkinen
  0 siblings, 0 replies; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-22 11:31 UTC (permalink / raw)
  To: Huang, Kai
  Cc: Christopherson, Sean J, Svahn, Kai, nhorman, jmorris, rientjes,
	josh, tglx, Ayoun, Serge, Huang, Haitao, linux-security-module,
	x86, akpm, npmccallum, linux-sgx, luto, Katz-zamir, Shay, Hansen,
	Dave, bp, serge, andriy.shevchenko

On Thu, Mar 21, 2019 at 09:41:37PM +0000, Huang, Kai wrote:
> > On Thu, Mar 21, 2019 at 02:08:36AM +0000, Huang, Kai wrote:
> > > On Tue, 2019-03-19 at 13:09 -0700, Sean Christopherson wrote:
> > > > On Sun, Mar 17, 2019 at 11:14:46PM +0200, Jarkko Sakkinen wrote:
> > > > > 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;
> > > > >    };
> > >
> > > Would you elaborate why the name is "token_fd"? I think *token* in SGX
> > > has more specific meaning?
> > 
> > I'm open for other names.
> 
> How about attribute_fd, which is consistent with your IOCTL?

I'm cool with that name! Thanks.

/Jarkko

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 17/27] x86/sgx: Add provisioning
  2019-03-22 11:29     ` Jarkko Sakkinen
@ 2019-03-22 11:43       ` Jarkko Sakkinen
  2019-03-22 18:20         ` Andy Lutomirski
  0 siblings, 1 reply; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-22 11:43 UTC (permalink / raw)
  To: Andy Lutomirski
  Cc: X86 ML, linux-sgx, Andrew Morton, Dave Hansen, Christopherson,
	Sean J, nhorman, npmccallum, Ayoun, Serge, Katz-zamir, Shay,
	Huang, Haitao, Andy Shevchenko, Thomas Gleixner, Svahn, Kai,
	Borislav Petkov, Josh Triplett, Huang, Kai, David Rientjes,
	James Morris, Serge E . Hallyn, LSM List

On Fri, Mar 22, 2019 at 01:29:38PM +0200, Jarkko Sakkinen wrote:
> On Thu, Mar 21, 2019 at 09:50:41AM -0700, Andy Lutomirski wrote:
> > On Sun, Mar 17, 2019 at 2:18 PM Jarkko Sakkinen
> > <jarkko.sakkinen@linux.intel.com> wrote:
> > >
> > > 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;
> > >    };
> > 
> > Here's a potential issue:
> > 
> > For container use, is it reasonable for a container manager to
> > bind-mount a file into securityfs?  Or would something in /dev make
> > this easier?
> 
> I guess that is a valid point given that the securityfs contains the LSM
> (e.g. SELinux or AppArmor) policy. So yeah, I think your are right what
> you say.
> 
> I propose that we create /dev/sgx/enclave to act as the enclave manager
> and /dev/sgx/provision for provisioning. Is this sustainable for you?

Hmm.. on 2nd thought the LSM policy or even DAC policy  would restrict
that the container manager can only access specific files inside
securityfs. With this conclusion I still think it is probably the best
place for seurity policy like things even for SGX. It is meant for that
anyway.

/Jarkko

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 13/27] x86/sgx: Add wrappers for ENCLS leaf functions
  2019-03-22 11:00         ` Jarkko Sakkinen
@ 2019-03-22 16:43           ` Sean Christopherson
  0 siblings, 0 replies; 92+ messages in thread
From: Sean Christopherson @ 2019-03-22 16:43 UTC (permalink / raw)
  To: Jarkko Sakkinen
  Cc: x86, linux-sgx, akpm, dave.hansen, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes

On Fri, Mar 22, 2019 at 01:00:14PM +0200, Jarkko Sakkinen wrote:
> On Thu, Mar 21, 2019 at 08:40:11AM -0700, Sean Christopherson wrote:
> > On Thu, Mar 21, 2019 at 04:51:53PM +0200, Jarkko Sakkinen wrote:
> > > On Tue, Mar 19, 2019 at 12:59:07PM -0700, Sean Christopherson wrote:
> > > > On Sun, Mar 17, 2019 at 11:14:42PM +0200, Jarkko Sakkinen wrote:
> > > > > +/**
> > > > > + * 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)
> > > > 
> > > > I honestly can't remember, is there a reason ENCLS_TRAPNR is still a macro
> > > > and not an inline function?
> > > 
> > > Not at all. And also the value should be unsigned and fault flag
> > > should be 0x80* because we never should get negative values.
> > 
> > From ENCLS itself, but we do have scenarios where the return code can
> > hold a system error code (negative value), an SGX error code (positive
> > value) or fault inforation.  E.g. sgx_encl_init() sets ret to -ERESTARTSYS,
> > and __sgx_encl_ewb() returns system error codes if get_backing() fails
> > and also directly returns the result of __ewb().  We could dance around
> > those issues, but IMO it's too fragile as it essentially requires a full
> > audit of every possible error path when modifying code.
> 
> There should be zero of those now but if you find one we can fix that.
> These inline functions should only deal with what comes from ENCLS. I
> think it is cleanest to fix those sites. I think here the focus should
> be on clarity.

To me, using bit 30 to denote a fault provides the most clarity.  Values
in the form of 0x4000xxxx aren't very common, i.e. regardless of where I
see such a pattern in a register, message, etc... I know there's a good
chance I'm dealing with an ENCLS fault.

> In ioctl.c there are total 5 sites and all of them are good.
> 
> Also encl.c is all good.
> 
> From the sites that you mentioned sgx_encl_ewb() needs to be fixed. In
> reclaim.c that seems to be the only problematic site.

This is the crux of my argument, e.g. "seems to be" doesn't exactly instill
confidence.  I just don't see what we gain by switching to bit 31, and on
the flip side we're forced to constantly audit the code because nearly all
of the affected paths are only exercised when something goes haywire.

> 
> These are pretty easy to grep with 'encls_'.
> 
> For me it looks like that assuming the positive value is absolutely
> the right thing to do as long as sgx_encl_ewb() is fixed properly.

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 17/27] x86/sgx: Add provisioning
  2019-03-22 11:43       ` Jarkko Sakkinen
@ 2019-03-22 18:20         ` Andy Lutomirski
  2019-03-25 14:55           ` Jarkko Sakkinen
  0 siblings, 1 reply; 92+ messages in thread
From: Andy Lutomirski @ 2019-03-22 18:20 UTC (permalink / raw)
  To: Jarkko Sakkinen
  Cc: Andy Lutomirski, X86 ML, linux-sgx, Andrew Morton, Dave Hansen,
	Christopherson, Sean J, nhorman, npmccallum, Ayoun, Serge,
	Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko,
	Thomas Gleixner, Svahn, Kai, Borislav Petkov, Josh Triplett,
	Huang, Kai, David Rientjes, James Morris, Serge E . Hallyn,
	LSM List

On Fri, Mar 22, 2019 at 4:43 AM Jarkko Sakkinen
<jarkko.sakkinen@linux.intel.com> wrote:
>
> On Fri, Mar 22, 2019 at 01:29:38PM +0200, Jarkko Sakkinen wrote:
> > On Thu, Mar 21, 2019 at 09:50:41AM -0700, Andy Lutomirski wrote:
> > > On Sun, Mar 17, 2019 at 2:18 PM Jarkko Sakkinen
> > > <jarkko.sakkinen@linux.intel.com> wrote:
> > > >
> > > > 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;
> > > >    };
> > >
> > > Here's a potential issue:
> > >
> > > For container use, is it reasonable for a container manager to
> > > bind-mount a file into securityfs?  Or would something in /dev make
> > > this easier?
> >
> > I guess that is a valid point given that the securityfs contains the LSM
> > (e.g. SELinux or AppArmor) policy. So yeah, I think your are right what
> > you say.
> >
> > I propose that we create /dev/sgx/enclave to act as the enclave manager
> > and /dev/sgx/provision for provisioning. Is this sustainable for you?
>
> Hmm.. on 2nd thought the LSM policy or even DAC policy  would restrict
> that the container manager can only access specific files inside
> securityfs. With this conclusion I still think it is probably the best
> place for seurity policy like things even for SGX. It is meant for that
> anyway.
>

LSM or DAC policy can certainly *restrict* it, but I suspect that most
container runtimes don't mount securityfs at all.  OTOH, the runtime
definitely needs to have a way to pass /dev/sgx/enclave (or whatever
it's called) through, so using another device node will definitely
work.

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 17/27] x86/sgx: Add provisioning
  2019-03-22 18:20         ` Andy Lutomirski
@ 2019-03-25 14:55           ` Jarkko Sakkinen
  2019-03-27  0:14             ` Sean Christopherson
  2019-04-05 10:18             ` Jarkko Sakkinen
  0 siblings, 2 replies; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-25 14:55 UTC (permalink / raw)
  To: Andy Lutomirski
  Cc: X86 ML, linux-sgx, Andrew Morton, Dave Hansen, Christopherson,
	Sean J, nhorman, npmccallum, Ayoun, Serge, Katz-zamir, Shay,
	Huang, Haitao, Andy Shevchenko, Thomas Gleixner, Svahn, Kai,
	Borislav Petkov, Josh Triplett, Huang, Kai, David Rientjes,
	James Morris, Serge E . Hallyn, LSM List

On Fri, Mar 22, 2019 at 11:20:30AM -0700, Andy Lutomirski wrote:
> On Fri, Mar 22, 2019 at 4:43 AM Jarkko Sakkinen
> <jarkko.sakkinen@linux.intel.com> wrote:
> >
> > On Fri, Mar 22, 2019 at 01:29:38PM +0200, Jarkko Sakkinen wrote:
> > > On Thu, Mar 21, 2019 at 09:50:41AM -0700, Andy Lutomirski wrote:
> > > > On Sun, Mar 17, 2019 at 2:18 PM Jarkko Sakkinen
> > > > <jarkko.sakkinen@linux.intel.com> wrote:
> > > > >
> > > > > 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;
> > > > >    };
> > > >
> > > > Here's a potential issue:
> > > >
> > > > For container use, is it reasonable for a container manager to
> > > > bind-mount a file into securityfs?  Or would something in /dev make
> > > > this easier?
> > >
> > > I guess that is a valid point given that the securityfs contains the LSM
> > > (e.g. SELinux or AppArmor) policy. So yeah, I think your are right what
> > > you say.
> > >
> > > I propose that we create /dev/sgx/enclave to act as the enclave manager
> > > and /dev/sgx/provision for provisioning. Is this sustainable for you?
> >
> > Hmm.. on 2nd thought the LSM policy or even DAC policy  would restrict
> > that the container manager can only access specific files inside
> > securityfs. With this conclusion I still think it is probably the best
> > place for seurity policy like things even for SGX. It is meant for that
> > anyway.
> >
> 
> LSM or DAC policy can certainly *restrict* it, but I suspect that most
> container runtimes don't mount securityfs at all.  OTOH, the runtime
> definitely needs to have a way to pass /dev/sgx/enclave (or whatever
> it's called) through, so using another device node will definitely
> work.

OK, I can cope with this argument. I go with the device names above for
v20.

/Jarkko

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 16/27] x86/sgx: Add the Linux SGX Enclave Driver
  2019-03-21 15:51     ` Jarkko Sakkinen
  2019-03-21 16:47       ` Sean Christopherson
@ 2019-03-26 13:26       ` Jarkko Sakkinen
  2019-03-26 23:58         ` Sean Christopherson
  1 sibling, 1 reply; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-26 13:26 UTC (permalink / raw)
  To: Sean Christopherson
  Cc: x86, linux-sgx, akpm, dave.hansen, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Suresh Siddha

On Thu, Mar 21, 2019 at 05:51:11PM +0200, Jarkko Sakkinen wrote:
> > Yuck.  If we remove the driver specific Makefile then we can eliminate
> > the "../" prefix here.  E.g. in the main SGX Makefile:
> > 
> > obj-$(CONFIG_INTEL_SGX_DRIVER) += driver/main.o driver/ioctl.o
> 
> I think this is a great idea.

On a 2nd thought not gonna do anything to that because it would
require to move driver.h and it is cleaner to keep all the driver
files in the same directory (and separated from the core).

There is nothing spurious in those includes anyway...

/Jarkko

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 16/27] x86/sgx: Add the Linux SGX Enclave Driver
  2019-03-26 13:26       ` Jarkko Sakkinen
@ 2019-03-26 23:58         ` Sean Christopherson
  2019-03-27  5:28           ` Jarkko Sakkinen
  0 siblings, 1 reply; 92+ messages in thread
From: Sean Christopherson @ 2019-03-26 23:58 UTC (permalink / raw)
  To: Jarkko Sakkinen
  Cc: x86, linux-sgx, akpm, dave.hansen, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Suresh Siddha

On Tue, Mar 26, 2019 at 03:26:50PM +0200, Jarkko Sakkinen wrote:
> On Thu, Mar 21, 2019 at 05:51:11PM +0200, Jarkko Sakkinen wrote:
> > > Yuck.  If we remove the driver specific Makefile then we can eliminate
> > > the "../" prefix here.  E.g. in the main SGX Makefile:
> > > 
> > > obj-$(CONFIG_INTEL_SGX_DRIVER) += driver/main.o driver/ioctl.o
> > 
> > I think this is a great idea.
> 
> On a 2nd thought not gonna do anything to that because it would
> require to move driver.h and it is cleaner to keep all the driver
> files in the same directory (and separated from the core).

What about collapsing driver/*.c into driver.c and moving driver.{c,h}
to the root sgx directory?  The bulk of driver/main.c is securityfs
and platform driver code, e.g. has a good chance of going away entirely
or being moved out of the "driver".  At that point there probably isn't
a strong reason to have driver/main.c and driver/ioctl.c.

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 17/27] x86/sgx: Add provisioning
  2019-03-25 14:55           ` Jarkko Sakkinen
@ 2019-03-27  0:14             ` Sean Christopherson
  2019-04-05 10:18             ` Jarkko Sakkinen
  1 sibling, 0 replies; 92+ messages in thread
From: Sean Christopherson @ 2019-03-27  0:14 UTC (permalink / raw)
  To: Jarkko Sakkinen
  Cc: Andy Lutomirski, X86 ML, linux-sgx, Andrew Morton, Dave Hansen,
	nhorman, npmccallum, Ayoun, Serge, Katz-zamir, Shay, Huang,
	Haitao, Andy Shevchenko, Thomas Gleixner, Svahn, Kai,
	Borislav Petkov, Josh Triplett, Huang, Kai, David Rientjes,
	James Morris, Serge E . Hallyn, LSM List

On Mon, Mar 25, 2019 at 04:55:03PM +0200, Jarkko Sakkinen wrote:
> On Fri, Mar 22, 2019 at 11:20:30AM -0700, Andy Lutomirski wrote:
> > On Fri, Mar 22, 2019 at 4:43 AM Jarkko Sakkinen
> > <jarkko.sakkinen@linux.intel.com> wrote:
> > >
> > > On Fri, Mar 22, 2019 at 01:29:38PM +0200, Jarkko Sakkinen wrote:
> > > > On Thu, Mar 21, 2019 at 09:50:41AM -0700, Andy Lutomirski wrote:
> > > > > On Sun, Mar 17, 2019 at 2:18 PM Jarkko Sakkinen
> > > > > <jarkko.sakkinen@linux.intel.com> wrote:
> > > > > >
> > > > > > 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;
> > > > > >    };
> > > > >
> > > > > Here's a potential issue:
> > > > >
> > > > > For container use, is it reasonable for a container manager to
> > > > > bind-mount a file into securityfs?  Or would something in /dev make
> > > > > this easier?
> > > >
> > > > I guess that is a valid point given that the securityfs contains the LSM
> > > > (e.g. SELinux or AppArmor) policy. So yeah, I think your are right what
> > > > you say.
> > > >
> > > > I propose that we create /dev/sgx/enclave to act as the enclave manager
> > > > and /dev/sgx/provision for provisioning. Is this sustainable for you?
> > >
> > > Hmm.. on 2nd thought the LSM policy or even DAC policy  would restrict
> > > that the container manager can only access specific files inside
> > > securityfs. With this conclusion I still think it is probably the best
> > > place for seurity policy like things even for SGX. It is meant for that
> > > anyway.
> > >
> > 
> > LSM or DAC policy can certainly *restrict* it, but I suspect that most
> > container runtimes don't mount securityfs at all.  OTOH, the runtime
> > definitely needs to have a way to pass /dev/sgx/enclave (or whatever
> > it's called) through, so using another device node will definitely
> > work.
> 
> OK, I can cope with this argument. I go with the device names above for
> v20.

Regardless of where the file ends up living, I think it should be owned
by the "core" SGX subsystem and not the driver.  At some point in the
past Andy requested that KVM intercept EINIT as needed to prevent
unauthorized access to the PROVISION_KEY (by running a KVM guest),
I.e. userspace may want to access /dev/sgx/provision or whatever even
if the driver is not loaded.  I don't see any reason to defer that change
to the KVM series.

If the ACPI-based autoprobing is removed, then sgx_init() can remove the
file if probing the driver fails, i.e. the kernel won't end up with a
file that userspace can't use for anything.

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 16/27] x86/sgx: Add the Linux SGX Enclave Driver
  2019-03-26 23:58         ` Sean Christopherson
@ 2019-03-27  5:28           ` Jarkko Sakkinen
  2019-03-27 17:57             ` Sean Christopherson
  2019-03-27 18:38             ` Jethro Beekman
  0 siblings, 2 replies; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-27  5:28 UTC (permalink / raw)
  To: Sean Christopherson
  Cc: x86, linux-sgx, akpm, dave.hansen, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Suresh Siddha

On Tue, Mar 26, 2019 at 04:58:52PM -0700, Sean Christopherson wrote:
> On Tue, Mar 26, 2019 at 03:26:50PM +0200, Jarkko Sakkinen wrote:
> > On Thu, Mar 21, 2019 at 05:51:11PM +0200, Jarkko Sakkinen wrote:
> > > > Yuck.  If we remove the driver specific Makefile then we can eliminate
> > > > the "../" prefix here.  E.g. in the main SGX Makefile:
> > > > 
> > > > obj-$(CONFIG_INTEL_SGX_DRIVER) += driver/main.o driver/ioctl.o
> > > 
> > > I think this is a great idea.
> > 
> > On a 2nd thought not gonna do anything to that because it would
> > require to move driver.h and it is cleaner to keep all the driver
> > files in the same directory (and separated from the core).
> 
> What about collapsing driver/*.c into driver.c and moving driver.{c,h}
> to the root sgx directory?  The bulk of driver/main.c is securityfs
> and platform driver code, e.g. has a good chance of going away entirely
> or being moved out of the "driver".  At that point there probably isn't
> a strong reason to have driver/main.c and driver/ioctl.c.

I think doing anything major would require to lock in whether to have
the LKM for the driver at all. If we wipe out the driver, then this is
just matter of moving dev management part to lets say dev.c.

Unless there is some real production use I can wipe it away. For v19
I wanted to fix it namely because in v18 LKM was just broken. It is
always good to make decisions based on working code.

/Jarkko

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 16/27] x86/sgx: Add the Linux SGX Enclave Driver
  2019-03-27  5:28           ` Jarkko Sakkinen
@ 2019-03-27 17:57             ` Sean Christopherson
  2019-03-27 18:38             ` Jethro Beekman
  1 sibling, 0 replies; 92+ messages in thread
From: Sean Christopherson @ 2019-03-27 17:57 UTC (permalink / raw)
  To: Jarkko Sakkinen
  Cc: x86, linux-sgx, akpm, dave.hansen, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Jethro Beekman

+Cc Jethro, AFAIK he is the only person who expressed a desire to have a
loadable module.

On Wed, Mar 27, 2019 at 07:28:30AM +0200, Jarkko Sakkinen wrote:
> On Tue, Mar 26, 2019 at 04:58:52PM -0700, Sean Christopherson wrote:
> > On Tue, Mar 26, 2019 at 03:26:50PM +0200, Jarkko Sakkinen wrote:
> > > On Thu, Mar 21, 2019 at 05:51:11PM +0200, Jarkko Sakkinen wrote:
> > > > > Yuck.  If we remove the driver specific Makefile then we can eliminate
> > > > > the "../" prefix here.  E.g. in the main SGX Makefile:
> > > > > 
> > > > > obj-$(CONFIG_INTEL_SGX_DRIVER) += driver/main.o driver/ioctl.o
> > > > 
> > > > I think this is a great idea.
> > > 
> > > On a 2nd thought not gonna do anything to that because it would
> > > require to move driver.h and it is cleaner to keep all the driver
> > > files in the same directory (and separated from the core).
> > 
> > What about collapsing driver/*.c into driver.c and moving driver.{c,h}
> > to the root sgx directory?  The bulk of driver/main.c is securityfs
> > and platform driver code, e.g. has a good chance of going away entirely
> > or being moved out of the "driver".  At that point there probably isn't
> > a strong reason to have driver/main.c and driver/ioctl.c.
> 
> I think doing anything major would require to lock in whether to have
> the LKM for the driver at all. If we wipe out the driver, then this is
> just matter of moving dev management part to lets say dev.c.
> 
> Unless there is some real production use I can wipe it away. For v19
> I wanted to fix it namely because in v18 LKM was just broken. It is
> always good to make decisions based on working code.
> 
> /Jarkko

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 16/27] x86/sgx: Add the Linux SGX Enclave Driver
  2019-03-27  5:28           ` Jarkko Sakkinen
  2019-03-27 17:57             ` Sean Christopherson
@ 2019-03-27 18:38             ` Jethro Beekman
  2019-03-27 20:06               ` Sean Christopherson
  2019-03-28 13:15               ` Jarkko Sakkinen
  1 sibling, 2 replies; 92+ messages in thread
From: Jethro Beekman @ 2019-03-27 18:38 UTC (permalink / raw)
  To: Jarkko Sakkinen, Sean Christopherson
  Cc: x86, linux-sgx, akpm, dave.hansen, nhorman, npmccallum,
	serge.ayoun, shay.katz-zamir, haitao.huang, andriy.shevchenko,
	tglx, kai.svahn, bp, josh, luto, kai.huang, rientjes,
	Suresh Siddha

[-- Attachment #1: Type: text/plain, Size: 2314 bytes --]

On 2019-03-26 22:28, Jarkko Sakkinen wrote:
> On Tue, Mar 26, 2019 at 04:58:52PM -0700, Sean Christopherson wrote:
>> On Tue, Mar 26, 2019 at 03:26:50PM +0200, Jarkko Sakkinen wrote:
>>> On Thu, Mar 21, 2019 at 05:51:11PM +0200, Jarkko Sakkinen wrote:
>>>>> Yuck.  If we remove the driver specific Makefile then we can eliminate
>>>>> the "../" prefix here.  E.g. in the main SGX Makefile:
>>>>>
>>>>> obj-$(CONFIG_INTEL_SGX_DRIVER) += driver/main.o driver/ioctl.o
>>>>
>>>> I think this is a great idea.
>>>
>>> On a 2nd thought not gonna do anything to that because it would
>>> require to move driver.h and it is cleaner to keep all the driver
>>> files in the same directory (and separated from the core).
>>
>> What about collapsing driver/*.c into driver.c and moving driver.{c,h}
>> to the root sgx directory?  The bulk of driver/main.c is securityfs
>> and platform driver code, e.g. has a good chance of going away entirely
>> or being moved out of the "driver".  At that point there probably isn't
>> a strong reason to have driver/main.c and driver/ioctl.c.
> 
> I think doing anything major would require to lock in whether to have
> the LKM for the driver at all. If we wipe out the driver, then this is
> just matter of moving dev management part to lets say dev.c.
> 
> Unless there is some real production use I can wipe it away. For v19
> I wanted to fix it namely because in v18 LKM was just broken. It is
> always good to make decisions based on working code.

It should be a module because things should be modules when possible. 
I'm not sure what the "Linux policy" is here but this seems obvious to me.

For example:

* Modules allow users to easily disable functionality that they don't 
use/is buggy for them/other reasons using blacklisting.
* Modules allow users to customize their functionality without having to 
rebuild the entire kernel.
* Modules allow developers to customize their modules without having to 
rebuild the entire kernel.

Specifically for SGX I can think of the following reasons as well:

* Module-based hypervisors may want to make EPC allocations for their 
guests.
* Easy experimentation with different EPC interfaces
* Easy experimentation with in-kernel LE

--
Jethro Beekman | Fortanix

> 
> /Jarkko
> 


[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 3990 bytes --]

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 16/27] x86/sgx: Add the Linux SGX Enclave Driver
  2019-03-27 18:38             ` Jethro Beekman
@ 2019-03-27 20:06               ` Sean Christopherson
  2019-03-28  1:21                 ` Jethro Beekman
  2019-03-28 13:19                 ` Jarkko Sakkinen
  2019-03-28 13:15               ` Jarkko Sakkinen
  1 sibling, 2 replies; 92+ messages in thread
From: Sean Christopherson @ 2019-03-27 20:06 UTC (permalink / raw)
  To: Jethro Beekman
  Cc: Jarkko Sakkinen, x86, linux-sgx, akpm, dave.hansen, nhorman,
	npmccallum, serge.ayoun, shay.katz-zamir, haitao.huang,
	andriy.shevchenko, tglx, kai.svahn, bp, josh, luto, kai.huang,
	rientjes, Suresh Siddha

On Wed, Mar 27, 2019 at 06:38:57PM +0000, Jethro Beekman wrote:
> On 2019-03-26 22:28, Jarkko Sakkinen wrote:
> >On Tue, Mar 26, 2019 at 04:58:52PM -0700, Sean Christopherson wrote:
> >>On Tue, Mar 26, 2019 at 03:26:50PM +0200, Jarkko Sakkinen wrote:
> >>>On Thu, Mar 21, 2019 at 05:51:11PM +0200, Jarkko Sakkinen wrote:
> >>>>>Yuck.  If we remove the driver specific Makefile then we can eliminate
> >>>>>the "../" prefix here.  E.g. in the main SGX Makefile:
> >>>>>
> >>>>>obj-$(CONFIG_INTEL_SGX_DRIVER) += driver/main.o driver/ioctl.o
> >>>>
> >>>>I think this is a great idea.
> >>>
> >>>On a 2nd thought not gonna do anything to that because it would
> >>>require to move driver.h and it is cleaner to keep all the driver
> >>>files in the same directory (and separated from the core).
> >>
> >>What about collapsing driver/*.c into driver.c and moving driver.{c,h}
> >>to the root sgx directory?  The bulk of driver/main.c is securityfs
> >>and platform driver code, e.g. has a good chance of going away entirely
> >>or being moved out of the "driver".  At that point there probably isn't
> >>a strong reason to have driver/main.c and driver/ioctl.c.
> >
> >I think doing anything major would require to lock in whether to have
> >the LKM for the driver at all. If we wipe out the driver, then this is
> >just matter of moving dev management part to lets say dev.c.
> >
> >Unless there is some real production use I can wipe it away. For v19
> >I wanted to fix it namely because in v18 LKM was just broken. It is
> >always good to make decisions based on working code.
> 
> It should be a module because things should be modules when possible. I'm
> not sure what the "Linux policy" is here but this seems obvious to me.
> 
> For example:
> 
> * Modules allow users to easily disable functionality that they don't use/is
> buggy for them/other reasons using blacklisting.
> * Modules allow users to customize their functionality without having to
> rebuild the entire kernel.
> * Modules allow developers to customize their modules without having to
> rebuild the entire kernel.

I agree with all of the above, but unfortunately blacklisting is really
the only benefit that would be realized by modularizing the driver.  The
"driver" at this point is just the device and its ioctls, the meat of
the functionality has been moved into the subsystem proper.  And the few
remaining tidbits of functionality, e.g. sgx_encl_page_alloc() and
sgx_encl_alloc(), probably should be moved out of ioctl.c as well.

Theoretically, we could revert to the old model, i.e. move a big pile of
code back into the driver, but that approach has a variety of technical
issues that IMO outweigh the benefits of having a module.

Tying into the your comment of "things should be modules when possible",
we've gradually come to the realization that truly modularizing the SGX
driver isn't possible, at least not without compromising other parts of
the design.

For example, relying on the EPC ACPI entry to autoprobe the driver falls
apart when virtualization support also wants to add an SGX device (or we
end up with a weird split model where the uapi driver is autoprobed via
ACPI and the virtualization device is probed by the SGX subsystem).

Relying on the EPC ACPI entry really shows its warts when systems
with multiple EPC sections show up.  The SGS BIOS writer's guide
(allegedly, I haven't personally read it) says that one and only one
EPC entry should be created in the ACPI tables, i.e. software must
use CPUID to enumerate the base+size of EPC sections.

In other words, the whole ACPI entry and platform device approach was a
hack purely to allow SGX to be implemented as an out-of-kernel driver.
If the darn ACPI hack had never been added in the first place, i.e.
CPUID is the only way to enumerate/probe SGX, then odds are we wouldn't
even be having this dicsussion and no one would bat an eye at SGX being
implemented as an Intel-specific feature that is baked into the kernel.

> Specifically for SGX I can think of the following reasons as well:
> 
> * Module-based hypervisors may want to make EPC allocations for their
> guests.
> * Easy experimentation with different EPC interfaces
> * Easy experimentation with in-kernel LE
> 
> --
> Jethro Beekman | Fortanix
> 
> >
> >/Jarkko
> >
> 



^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 16/27] x86/sgx: Add the Linux SGX Enclave Driver
  2019-03-27 20:06               ` Sean Christopherson
@ 2019-03-28  1:21                 ` Jethro Beekman
  2019-03-28 13:19                 ` Jarkko Sakkinen
  1 sibling, 0 replies; 92+ messages in thread
From: Jethro Beekman @ 2019-03-28  1:21 UTC (permalink / raw)
  To: Sean Christopherson
  Cc: Jarkko Sakkinen, x86, linux-sgx, akpm, dave.hansen, nhorman,
	npmccallum, serge.ayoun, shay.katz-zamir, haitao.huang,
	andriy.shevchenko, tglx, kai.svahn, bp, josh, luto, kai.huang,
	rientjes, Suresh Siddha

[-- Attachment #1: Type: text/plain, Size: 4674 bytes --]

On 2019-03-27 13:06, Sean Christopherson wrote:
> On Wed, Mar 27, 2019 at 06:38:57PM +0000, Jethro Beekman wrote:
>> On 2019-03-26 22:28, Jarkko Sakkinen wrote:
>>> On Tue, Mar 26, 2019 at 04:58:52PM -0700, Sean Christopherson wrote:
>>>> On Tue, Mar 26, 2019 at 03:26:50PM +0200, Jarkko Sakkinen wrote:
>>>>> On Thu, Mar 21, 2019 at 05:51:11PM +0200, Jarkko Sakkinen wrote:
>>>>>>> Yuck.  If we remove the driver specific Makefile then we can eliminate
>>>>>>> the "../" prefix here.  E.g. in the main SGX Makefile:
>>>>>>>
>>>>>>> obj-$(CONFIG_INTEL_SGX_DRIVER) += driver/main.o driver/ioctl.o
>>>>>>
>>>>>> I think this is a great idea.
>>>>>
>>>>> On a 2nd thought not gonna do anything to that because it would
>>>>> require to move driver.h and it is cleaner to keep all the driver
>>>>> files in the same directory (and separated from the core).
>>>>
>>>> What about collapsing driver/*.c into driver.c and moving driver.{c,h}
>>>> to the root sgx directory?  The bulk of driver/main.c is securityfs
>>>> and platform driver code, e.g. has a good chance of going away entirely
>>>> or being moved out of the "driver".  At that point there probably isn't
>>>> a strong reason to have driver/main.c and driver/ioctl.c.
>>>
>>> I think doing anything major would require to lock in whether to have
>>> the LKM for the driver at all. If we wipe out the driver, then this is
>>> just matter of moving dev management part to lets say dev.c.
>>>
>>> Unless there is some real production use I can wipe it away. For v19
>>> I wanted to fix it namely because in v18 LKM was just broken. It is
>>> always good to make decisions based on working code.
>>
>> It should be a module because things should be modules when possible. I'm
>> not sure what the "Linux policy" is here but this seems obvious to me.
>>
>> For example:
>>
>> * Modules allow users to easily disable functionality that they don't use/is
>> buggy for them/other reasons using blacklisting.
>> * Modules allow users to customize their functionality without having to
>> rebuild the entire kernel.
>> * Modules allow developers to customize their modules without having to
>> rebuild the entire kernel.
> 
> I agree with all of the above, but unfortunately blacklisting is really
> the only benefit that would be realized by modularizing the driver.  The

...

> Tying into the your comment of "things should be modules when possible",
> we've gradually come to the realization that truly modularizing the SGX
> driver isn't possible, at least not without compromising other parts of
> the design.

What do you mean? The interface that the kernel needs to provide to any 
EPC-using modules is some way to allocate/free EPC pages and some way to 
associate EPC pages with enclaves (so that the swapper can be 
intelligent). This is pretty much exactly what the API looks like in v19.

Note that you still need enclave tracking with KVM if you want the host 
kernel to be able to page out guest EPC pages. However, you probably 
wouldn't want to do this with VMAs, so maybe there's an opportunity to 
streamline the API here.

> For example, relying on the EPC ACPI entry to autoprobe the driver falls
> apart when virtualization support also wants to add an SGX device (or we
> end up with a weird split model where the uapi driver is autoprobed via
> ACPI and the virtualization device is probed by the SGX subsystem).
> 
> Relying on the EPC ACPI entry really shows its warts when systems
> with multiple EPC sections show up.  The SGS BIOS writer's guide
> (allegedly, I haven't personally read it) says that one and only one
> EPC entry should be created in the ACPI tables, i.e. software must
> use CPUID to enumerate the base+size of EPC sections.
> 
> In other words, the whole ACPI entry and platform device approach was a
> hack purely to allow SGX to be implemented as an out-of-kernel driver.
> If the darn ACPI hack had never been added in the first place, i.e.
> CPUID is the only way to enumerate/probe SGX, then odds are we wouldn't
> even be having this dicsussion and no one would bat an eye at SGX being
> implemented as an Intel-specific feature that is baked into the kernel.
> 
>> Specifically for SGX I can think of the following reasons as well:
>>
>> * Module-based hypervisors may want to make EPC allocations for their
>> guests.
>> * Easy experimentation with different EPC interfaces
>> * Easy experimentation with in-kernel LE

The only example you give here is ACPI autoprobe. I don't really care 
about this. I do care about the other things I mentioned.

--
Jethro Beekman | Fortanix


[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 3990 bytes --]

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 16/27] x86/sgx: Add the Linux SGX Enclave Driver
  2019-03-27 18:38             ` Jethro Beekman
  2019-03-27 20:06               ` Sean Christopherson
@ 2019-03-28 13:15               ` Jarkko Sakkinen
  1 sibling, 0 replies; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-28 13:15 UTC (permalink / raw)
  To: Jethro Beekman
  Cc: Sean Christopherson, x86, linux-sgx, akpm, dave.hansen, nhorman,
	npmccallum, serge.ayoun, shay.katz-zamir, haitao.huang,
	andriy.shevchenko, tglx, kai.svahn, bp, josh, luto, kai.huang,
	rientjes, Suresh Siddha

On Wed, Mar 27, 2019 at 06:38:57PM +0000, Jethro Beekman wrote:
> On 2019-03-26 22:28, Jarkko Sakkinen wrote:
> * Modules allow users to easily disable functionality that they don't use/is
> buggy for them/other reasons using blacklisting.

This is a valid point. People might want to minimize the uapi when they
tailor Linux for certain purposes without having to recompile the kernel
(lets say use the stock distro kernel). The motivation would be just to
minimize the potential attack surface.

I definitely buy this point.

> * Modules allow users to customize their functionality without having to
> rebuild the entire kernel.

Yep.

/Jarkko

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 16/27] x86/sgx: Add the Linux SGX Enclave Driver
  2019-03-27 20:06               ` Sean Christopherson
  2019-03-28  1:21                 ` Jethro Beekman
@ 2019-03-28 13:19                 ` Jarkko Sakkinen
  2019-03-28 19:05                   ` Andy Lutomirski
  1 sibling, 1 reply; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-28 13:19 UTC (permalink / raw)
  To: Sean Christopherson
  Cc: Jethro Beekman, x86, linux-sgx, akpm, dave.hansen, nhorman,
	npmccallum, serge.ayoun, shay.katz-zamir, haitao.huang,
	andriy.shevchenko, tglx, kai.svahn, bp, josh, luto, kai.huang,
	rientjes, Suresh Siddha

On Wed, Mar 27, 2019 at 01:06:11PM -0700, Sean Christopherson wrote:
> I agree with all of the above, but unfortunately blacklisting is really
> the only benefit that would be realized by modularizing the driver.  The
> "driver" at this point is just the device and its ioctls, the meat of
> the functionality has been moved into the subsystem proper.  And the few
> remaining tidbits of functionality, e.g. sgx_encl_page_alloc() and
> sgx_encl_alloc(), probably should be moved out of ioctl.c as well.

That is kind of core reason of having an LKM here, to wrap the uapi. It
is not about reducing size of the kernel. It is about ability to tailor
the uapi on stock kernels.

/Jarkko

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 16/27] x86/sgx: Add the Linux SGX Enclave Driver
  2019-03-28 13:19                 ` Jarkko Sakkinen
@ 2019-03-28 19:05                   ` Andy Lutomirski
  2019-03-29  9:43                     ` Jarkko Sakkinen
  2019-03-29 16:20                     ` Sean Christopherson
  0 siblings, 2 replies; 92+ messages in thread
From: Andy Lutomirski @ 2019-03-28 19:05 UTC (permalink / raw)
  To: Jarkko Sakkinen
  Cc: Sean Christopherson, Jethro Beekman, x86, linux-sgx, akpm,
	dave.hansen, nhorman, npmccallum, serge.ayoun, shay.katz-zamir,
	haitao.huang, andriy.shevchenko, tglx, kai.svahn, bp, josh, luto,
	kai.huang, rientjes, Suresh Siddha

On Thu, Mar 28, 2019 at 6:19 AM Jarkko Sakkinen
<jarkko.sakkinen@linux.intel.com> wrote:
>
> On Wed, Mar 27, 2019 at 01:06:11PM -0700, Sean Christopherson wrote:
> > I agree with all of the above, but unfortunately blacklisting is really
> > the only benefit that would be realized by modularizing the driver.  The
> > "driver" at this point is just the device and its ioctls, the meat of
> > the functionality has been moved into the subsystem proper.  And the few
> > remaining tidbits of functionality, e.g. sgx_encl_page_alloc() and
> > sgx_encl_alloc(), probably should be moved out of ioctl.c as well.
>
> That is kind of core reason of having an LKM here, to wrap the uapi. It
> is not about reducing size of the kernel. It is about ability to tailor
> the uapi on stock kernels.
>

As I see it, there is really only one significant benefit of modules:
reducing the size of the kernel in the case that the module isn't
loaded.  (And organizing module parameters, I suppose, but you don't,
strictly speaking, need to be able to build as a module to do that.)
SGX can be blacklisted by booting with nosgx (or, if if can't, then
that should be added).

--Andy

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 16/27] x86/sgx: Add the Linux SGX Enclave Driver
  2019-03-28 19:05                   ` Andy Lutomirski
@ 2019-03-29  9:43                     ` Jarkko Sakkinen
  2019-03-29 16:20                     ` Sean Christopherson
  1 sibling, 0 replies; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-03-29  9:43 UTC (permalink / raw)
  To: Andy Lutomirski
  Cc: Sean Christopherson, Jethro Beekman, x86, linux-sgx, akpm,
	dave.hansen, nhorman, npmccallum, serge.ayoun, shay.katz-zamir,
	haitao.huang, andriy.shevchenko, tglx, kai.svahn, bp, josh,
	kai.huang, rientjes, Suresh Siddha

On Thu, Mar 28, 2019 at 12:05:39PM -0700, Andy Lutomirski wrote:
> On Thu, Mar 28, 2019 at 6:19 AM Jarkko Sakkinen
> <jarkko.sakkinen@linux.intel.com> wrote:
> >
> > On Wed, Mar 27, 2019 at 01:06:11PM -0700, Sean Christopherson wrote:
> > > I agree with all of the above, but unfortunately blacklisting is really
> > > the only benefit that would be realized by modularizing the driver.  The
> > > "driver" at this point is just the device and its ioctls, the meat of
> > > the functionality has been moved into the subsystem proper.  And the few
> > > remaining tidbits of functionality, e.g. sgx_encl_page_alloc() and
> > > sgx_encl_alloc(), probably should be moved out of ioctl.c as well.
> >
> > That is kind of core reason of having an LKM here, to wrap the uapi. It
> > is not about reducing size of the kernel. It is about ability to tailor
> > the uapi on stock kernels.
> >
> 
> As I see it, there is really only one significant benefit of modules:
> reducing the size of the kernel in the case that the module isn't
> loaded.  (And organizing module parameters, I suppose, but you don't,
> strictly speaking, need to be able to build as a module to do that.)
> SGX can be blacklisted by booting with nosgx (or, if if can't, then
> that should be added).

That would be for me a feasible solution that I could live with and it
could be also used to disable all of SGX. Thanks.

/Jarkko

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 16/27] x86/sgx: Add the Linux SGX Enclave Driver
  2019-03-28 19:05                   ` Andy Lutomirski
  2019-03-29  9:43                     ` Jarkko Sakkinen
@ 2019-03-29 16:20                     ` Sean Christopherson
  2019-04-01 10:01                       ` Jarkko Sakkinen
  1 sibling, 1 reply; 92+ messages in thread
From: Sean Christopherson @ 2019-03-29 16:20 UTC (permalink / raw)
  To: Andy Lutomirski
  Cc: Jarkko Sakkinen, Jethro Beekman, x86, linux-sgx, akpm,
	dave.hansen, nhorman, npmccallum, serge.ayoun, shay.katz-zamir,
	haitao.huang, andriy.shevchenko, tglx, kai.svahn, bp, josh,
	kai.huang, rientjes, Suresh Siddha

On Thu, Mar 28, 2019 at 12:05:39PM -0700, Andy Lutomirski wrote:
> On Thu, Mar 28, 2019 at 6:19 AM Jarkko Sakkinen
> <jarkko.sakkinen@linux.intel.com> wrote:
> >
> > On Wed, Mar 27, 2019 at 01:06:11PM -0700, Sean Christopherson wrote:
> > > I agree with all of the above, but unfortunately blacklisting is really
> > > the only benefit that would be realized by modularizing the driver.  The
> > > "driver" at this point is just the device and its ioctls, the meat of
> > > the functionality has been moved into the subsystem proper.  And the few
> > > remaining tidbits of functionality, e.g. sgx_encl_page_alloc() and
> > > sgx_encl_alloc(), probably should be moved out of ioctl.c as well.
> >
> > That is kind of core reason of having an LKM here, to wrap the uapi. It
> > is not about reducing size of the kernel. It is about ability to tailor
> > the uapi on stock kernels.
> >
> 
> As I see it, there is really only one significant benefit of modules:
> reducing the size of the kernel in the case that the module isn't
> loaded.

This is a much more succinct explanation of what I was attempting to say.

>  (And organizing module parameters, I suppose, but you don't,
> strictly speaking, need to be able to build as a module to do that.)
> SGX can be blacklisted by booting with nosgx (or, if if can't, then
> that should be added).

The split lock detection series also proposed extending clearcpuid to
take flags by name, a dedicated 'nosgx' may not even be necessary in the
long run.

https://lkml.kernel.org/r/1549084491-57808-6-git-send-email-fenghua.yu@intel.com
https://lkml.kernel.org/r/1549084491-57808-7-git-send-email-fenghua.yu@intel.com
https://lkml.kernel.org/r/1549084491-57808-8-git-send-email-fenghua.yu@intel.com

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 16/27] x86/sgx: Add the Linux SGX Enclave Driver
  2019-03-29 16:20                     ` Sean Christopherson
@ 2019-04-01 10:01                       ` Jarkko Sakkinen
  2019-04-01 17:25                         ` Jethro Beekman
  0 siblings, 1 reply; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-04-01 10:01 UTC (permalink / raw)
  To: Sean Christopherson
  Cc: Andy Lutomirski, Jethro Beekman, x86, linux-sgx, akpm,
	dave.hansen, nhorman, npmccallum, serge.ayoun, shay.katz-zamir,
	haitao.huang, andriy.shevchenko, tglx, kai.svahn, bp, josh,
	kai.huang, rientjes, Suresh Siddha

On Fri, Mar 29, 2019 at 09:20:05AM -0700, Sean Christopherson wrote:
> On Thu, Mar 28, 2019 at 12:05:39PM -0700, Andy Lutomirski wrote:
> > On Thu, Mar 28, 2019 at 6:19 AM Jarkko Sakkinen
> > <jarkko.sakkinen@linux.intel.com> wrote:
> > >
> > > On Wed, Mar 27, 2019 at 01:06:11PM -0700, Sean Christopherson wrote:
> > > > I agree with all of the above, but unfortunately blacklisting is really
> > > > the only benefit that would be realized by modularizing the driver.  The
> > > > "driver" at this point is just the device and its ioctls, the meat of
> > > > the functionality has been moved into the subsystem proper.  And the few
> > > > remaining tidbits of functionality, e.g. sgx_encl_page_alloc() and
> > > > sgx_encl_alloc(), probably should be moved out of ioctl.c as well.
> > >
> > > That is kind of core reason of having an LKM here, to wrap the uapi. It
> > > is not about reducing size of the kernel. It is about ability to tailor
> > > the uapi on stock kernels.
> > >
> > 
> > As I see it, there is really only one significant benefit of modules:
> > reducing the size of the kernel in the case that the module isn't
> > loaded.
> 
> This is a much more succinct explanation of what I was attempting to say.
> 
> >  (And organizing module parameters, I suppose, but you don't,
> > strictly speaking, need to be able to build as a module to do that.)
> > SGX can be blacklisted by booting with nosgx (or, if if can't, then
> > that should be added).
> 
> The split lock detection series also proposed extending clearcpuid to
> take flags by name, a dedicated 'nosgx' may not even be necessary in the
> long run.
> 
> https://lkml.kernel.org/r/1549084491-57808-6-git-send-email-fenghua.yu@intel.com
> https://lkml.kernel.org/r/1549084491-57808-7-git-send-email-fenghua.yu@intel.com
> https://lkml.kernel.org/r/1549084491-57808-8-git-send-email-fenghua.yu@intel.com

OK, that's great. Then I just rework the LKM out...

/Jarkko

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 16/27] x86/sgx: Add the Linux SGX Enclave Driver
  2019-04-01 10:01                       ` Jarkko Sakkinen
@ 2019-04-01 17:25                         ` Jethro Beekman
  2019-04-01 22:57                           ` Jarkko Sakkinen
  0 siblings, 1 reply; 92+ messages in thread
From: Jethro Beekman @ 2019-04-01 17:25 UTC (permalink / raw)
  To: Jarkko Sakkinen, Sean Christopherson
  Cc: Andy Lutomirski, x86, linux-sgx, akpm, dave.hansen, nhorman,
	npmccallum, serge.ayoun, shay.katz-zamir, haitao.huang,
	andriy.shevchenko, tglx, kai.svahn, bp, josh, kai.huang,
	rientjes, Suresh Siddha

[-- Attachment #1: Type: text/plain, Size: 2143 bytes --]

On 2019-04-01 03:01, Jarkko Sakkinen wrote:
> On Fri, Mar 29, 2019 at 09:20:05AM -0700, Sean Christopherson wrote:
>> On Thu, Mar 28, 2019 at 12:05:39PM -0700, Andy Lutomirski wrote:
>>> On Thu, Mar 28, 2019 at 6:19 AM Jarkko Sakkinen
>>> <jarkko.sakkinen@linux.intel.com> wrote:
>>>>
>>>> On Wed, Mar 27, 2019 at 01:06:11PM -0700, Sean Christopherson wrote:
>>>>> I agree with all of the above, but unfortunately blacklisting is really
>>>>> the only benefit that would be realized by modularizing the driver.  The
>>>>> "driver" at this point is just the device and its ioctls, the meat of
>>>>> the functionality has been moved into the subsystem proper.  And the few
>>>>> remaining tidbits of functionality, e.g. sgx_encl_page_alloc() and
>>>>> sgx_encl_alloc(), probably should be moved out of ioctl.c as well.
>>>>
>>>> That is kind of core reason of having an LKM here, to wrap the uapi. It
>>>> is not about reducing size of the kernel. It is about ability to tailor
>>>> the uapi on stock kernels.
>>>>
>>>
>>> As I see it, there is really only one significant benefit of modules:
>>> reducing the size of the kernel in the case that the module isn't
>>> loaded.
>>
>> This is a much more succinct explanation of what I was attempting to say.
>>
>>>   (And organizing module parameters, I suppose, but you don't,
>>> strictly speaking, need to be able to build as a module to do that.)
>>> SGX can be blacklisted by booting with nosgx (or, if if can't, then
>>> that should be added).
>>
>> The split lock detection series also proposed extending clearcpuid to
>> take flags by name, a dedicated 'nosgx' may not even be necessary in the
>> long run.
>>
>> https://lkml.kernel.org/r/1549084491-57808-6-git-send-email-fenghua.yu@intel.com
>> https://lkml.kernel.org/r/1549084491-57808-7-git-send-email-fenghua.yu@intel.com
>> https://lkml.kernel.org/r/1549084491-57808-8-git-send-email-fenghua.yu@intel.com
> 
> OK, that's great. Then I just rework the LKM out...

Wait, why? I don't agree with Andy's "only" reason for LKMs, and it 
sounded like you agree.

--
Jethro Beekman | Fortanix


[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 3990 bytes --]

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 16/27] x86/sgx: Add the Linux SGX Enclave Driver
  2019-04-01 17:25                         ` Jethro Beekman
@ 2019-04-01 22:57                           ` Jarkko Sakkinen
  0 siblings, 0 replies; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-04-01 22:57 UTC (permalink / raw)
  To: Jethro Beekman
  Cc: Sean Christopherson, Andy Lutomirski, x86, linux-sgx, akpm,
	dave.hansen, nhorman, npmccallum, serge.ayoun, shay.katz-zamir,
	haitao.huang, andriy.shevchenko, tglx, kai.svahn, bp, josh,
	kai.huang, rientjes, Suresh Siddha

On Mon, Apr 01, 2019 at 05:25:11PM +0000, Jethro Beekman wrote:
> On 2019-04-01 03:01, Jarkko Sakkinen wrote:
> > On Fri, Mar 29, 2019 at 09:20:05AM -0700, Sean Christopherson wrote:
> > > On Thu, Mar 28, 2019 at 12:05:39PM -0700, Andy Lutomirski wrote:
> > > > On Thu, Mar 28, 2019 at 6:19 AM Jarkko Sakkinen
> > > > <jarkko.sakkinen@linux.intel.com> wrote:
> > > > > 
> > > > > On Wed, Mar 27, 2019 at 01:06:11PM -0700, Sean Christopherson wrote:
> > > > > > I agree with all of the above, but unfortunately blacklisting is really
> > > > > > the only benefit that would be realized by modularizing the driver.  The
> > > > > > "driver" at this point is just the device and its ioctls, the meat of
> > > > > > the functionality has been moved into the subsystem proper.  And the few
> > > > > > remaining tidbits of functionality, e.g. sgx_encl_page_alloc() and
> > > > > > sgx_encl_alloc(), probably should be moved out of ioctl.c as well.
> > > > > 
> > > > > That is kind of core reason of having an LKM here, to wrap the uapi. It
> > > > > is not about reducing size of the kernel. It is about ability to tailor
> > > > > the uapi on stock kernels.
> > > > > 
> > > > 
> > > > As I see it, there is really only one significant benefit of modules:
> > > > reducing the size of the kernel in the case that the module isn't
> > > > loaded.
> > > 
> > > This is a much more succinct explanation of what I was attempting to say.
> > > 
> > > >   (And organizing module parameters, I suppose, but you don't,
> > > > strictly speaking, need to be able to build as a module to do that.)
> > > > SGX can be blacklisted by booting with nosgx (or, if if can't, then
> > > > that should be added).
> > > 
> > > The split lock detection series also proposed extending clearcpuid to
> > > take flags by name, a dedicated 'nosgx' may not even be necessary in the
> > > long run.
> > > 
> > > https://lkml.kernel.org/r/1549084491-57808-6-git-send-email-fenghua.yu@intel.com
> > > https://lkml.kernel.org/r/1549084491-57808-7-git-send-email-fenghua.yu@intel.com
> > > https://lkml.kernel.org/r/1549084491-57808-8-git-send-email-fenghua.yu@intel.com
> > 
> > OK, that's great. Then I just rework the LKM out...
> 
> Wait, why? I don't agree with Andy's "only" reason for LKMs, and it sounded
> like you agree.

For me the rationale for having LKM was that you can enable/disable the
functionality. For that the kernel command line would be sufficient.

I will roll v20 w/o taking it away since this is still clearly something
not concluded (won't affect other changes).

/Jarkko

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 17/27] x86/sgx: Add provisioning
  2019-03-25 14:55           ` Jarkko Sakkinen
  2019-03-27  0:14             ` Sean Christopherson
@ 2019-04-05 10:18             ` Jarkko Sakkinen
  2019-04-05 13:53               ` Andy Lutomirski
  1 sibling, 1 reply; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-04-05 10:18 UTC (permalink / raw)
  To: Andy Lutomirski
  Cc: X86 ML, linux-sgx, Andrew Morton, Dave Hansen, Christopherson,
	Sean J, nhorman, npmccallum, Ayoun, Serge, Katz-zamir, Shay,
	Huang, Haitao, Andy Shevchenko, Thomas Gleixner, Svahn, Kai,
	Borislav Petkov, Josh Triplett, Huang, Kai, David Rientjes,
	James Morris, Serge E . Hallyn, LSM List

On Mon, Mar 25, 2019 at 04:55:03PM +0200, Jarkko Sakkinen wrote:
> > > Hmm.. on 2nd thought the LSM policy or even DAC policy  would restrict
> > > that the container manager can only access specific files inside
> > > securityfs. With this conclusion I still think it is probably the best
> > > place for seurity policy like things even for SGX. It is meant for that
> > > anyway.
> > >
> > 
> > LSM or DAC policy can certainly *restrict* it, but I suspect that most
> > container runtimes don't mount securityfs at all.  OTOH, the runtime
> > definitely needs to have a way to pass /dev/sgx/enclave (or whatever
> > it's called) through, so using another device node will definitely
> > work.
> 
> OK, I can cope with this argument. I go with the device names above for
> v20.

In v20 the refactoring would be with corresponding modes:

/dev/sgx 0755
/dev/sgx/enclave 0666
/dev/sgx/provision 0600

The problem that I'm facing is that with devnode callback of struct
device_type I can easily give the defaut mode for any of the files but
not for the /dev/sgx directory itself. How do I get the appropriate
mode for it?

/Jarkko

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 17/27] x86/sgx: Add provisioning
  2019-04-05 10:18             ` Jarkko Sakkinen
@ 2019-04-05 13:53               ` Andy Lutomirski
  2019-04-05 14:20                 ` Jarkko Sakkinen
  2019-04-05 14:21                 ` Greg KH
  0 siblings, 2 replies; 92+ messages in thread
From: Andy Lutomirski @ 2019-04-05 13:53 UTC (permalink / raw)
  To: Jarkko Sakkinen, Greg KH
  Cc: Andy Lutomirski, X86 ML, linux-sgx, Andrew Morton, Dave Hansen,
	Christopherson, Sean J, nhorman, npmccallum, Ayoun, Serge,
	Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko,
	Thomas Gleixner, Svahn, Kai, Borislav Petkov, Josh Triplett,
	Huang, Kai, David Rientjes, James Morris, Serge E . Hallyn,
	LSM List

On Fri, Apr 5, 2019 at 3:18 AM Jarkko Sakkinen
<jarkko.sakkinen@linux.intel.com> wrote:
>
> On Mon, Mar 25, 2019 at 04:55:03PM +0200, Jarkko Sakkinen wrote:
> > > > Hmm.. on 2nd thought the LSM policy or even DAC policy  would restrict
> > > > that the container manager can only access specific files inside
> > > > securityfs. With this conclusion I still think it is probably the best
> > > > place for seurity policy like things even for SGX. It is meant for that
> > > > anyway.
> > > >
> > >
> > > LSM or DAC policy can certainly *restrict* it, but I suspect that most
> > > container runtimes don't mount securityfs at all.  OTOH, the runtime
> > > definitely needs to have a way to pass /dev/sgx/enclave (or whatever
> > > it's called) through, so using another device node will definitely
> > > work.
> >
> > OK, I can cope with this argument. I go with the device names above for
> > v20.
>
> In v20 the refactoring would be with corresponding modes:
>
> /dev/sgx 0755
> /dev/sgx/enclave 0666
> /dev/sgx/provision 0600
>
> The problem that I'm facing is that with devnode callback of struct
> device_type I can easily give the defaut mode for any of the files but
> not for the /dev/sgx directory itself. How do I get the appropriate
> mode for it?
>

Hi Greg-

Do you know this one?

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 17/27] x86/sgx: Add provisioning
  2019-04-05 13:53               ` Andy Lutomirski
@ 2019-04-05 14:20                 ` Jarkko Sakkinen
  2019-04-05 14:34                   ` Greg KH
  2019-04-05 14:21                 ` Greg KH
  1 sibling, 1 reply; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-04-05 14:20 UTC (permalink / raw)
  To: Andy Lutomirski
  Cc: Greg KH, X86 ML, linux-sgx, Andrew Morton, Dave Hansen,
	Christopherson, Sean J, nhorman, npmccallum, Ayoun, Serge,
	Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko,
	Thomas Gleixner, Svahn, Kai, Borislav Petkov, Josh Triplett,
	Huang, Kai, David Rientjes, James Morris, Serge E . Hallyn,
	LSM List

On Fri, Apr 05, 2019 at 06:53:57AM -0700, Andy Lutomirski wrote:
> On Fri, Apr 5, 2019 at 3:18 AM Jarkko Sakkinen
> <jarkko.sakkinen@linux.intel.com> wrote:
> >
> > On Mon, Mar 25, 2019 at 04:55:03PM +0200, Jarkko Sakkinen wrote:
> > > > > Hmm.. on 2nd thought the LSM policy or even DAC policy  would restrict
> > > > > that the container manager can only access specific files inside
> > > > > securityfs. With this conclusion I still think it is probably the best
> > > > > place for seurity policy like things even for SGX. It is meant for that
> > > > > anyway.
> > > > >
> > > >
> > > > LSM or DAC policy can certainly *restrict* it, but I suspect that most
> > > > container runtimes don't mount securityfs at all.  OTOH, the runtime
> > > > definitely needs to have a way to pass /dev/sgx/enclave (or whatever
> > > > it's called) through, so using another device node will definitely
> > > > work.
> > >
> > > OK, I can cope with this argument. I go with the device names above for
> > > v20.
> >
> > In v20 the refactoring would be with corresponding modes:
> >
> > /dev/sgx 0755
> > /dev/sgx/enclave 0666
> > /dev/sgx/provision 0600
> >
> > The problem that I'm facing is that with devnode callback of struct
> > device_type I can easily give the defaut mode for any of the files but
> > not for the /dev/sgx directory itself. How do I get the appropriate
> > mode for it?
> >
> 
> Hi Greg-
> 
> Do you know this one?

I guess one option is to not do anything with the mode but instead
contribute rules to udev? I'm not too familiar with this but maybe
that is even recommended way?

/Jarkko

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 17/27] x86/sgx: Add provisioning
  2019-04-05 13:53               ` Andy Lutomirski
  2019-04-05 14:20                 ` Jarkko Sakkinen
@ 2019-04-05 14:21                 ` Greg KH
  1 sibling, 0 replies; 92+ messages in thread
From: Greg KH @ 2019-04-05 14:21 UTC (permalink / raw)
  To: Andy Lutomirski
  Cc: Jarkko Sakkinen, X86 ML, linux-sgx, Andrew Morton, Dave Hansen,
	Christopherson, Sean J, nhorman, npmccallum, Ayoun, Serge,
	Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko,
	Thomas Gleixner, Svahn, Kai, Borislav Petkov, Josh Triplett,
	Huang, Kai, David Rientjes, James Morris, Serge E . Hallyn,
	LSM List

On Fri, Apr 05, 2019 at 06:53:57AM -0700, Andy Lutomirski wrote:
> On Fri, Apr 5, 2019 at 3:18 AM Jarkko Sakkinen
> <jarkko.sakkinen@linux.intel.com> wrote:
> >
> > On Mon, Mar 25, 2019 at 04:55:03PM +0200, Jarkko Sakkinen wrote:
> > > > > Hmm.. on 2nd thought the LSM policy or even DAC policy  would restrict
> > > > > that the container manager can only access specific files inside
> > > > > securityfs. With this conclusion I still think it is probably the best
> > > > > place for seurity policy like things even for SGX. It is meant for that
> > > > > anyway.
> > > > >
> > > >
> > > > LSM or DAC policy can certainly *restrict* it, but I suspect that most
> > > > container runtimes don't mount securityfs at all.  OTOH, the runtime
> > > > definitely needs to have a way to pass /dev/sgx/enclave (or whatever
> > > > it's called) through, so using another device node will definitely
> > > > work.
> > >
> > > OK, I can cope with this argument. I go with the device names above for
> > > v20.
> >
> > In v20 the refactoring would be with corresponding modes:
> >
> > /dev/sgx 0755
> > /dev/sgx/enclave 0666
> > /dev/sgx/provision 0600
> >
> > The problem that I'm facing is that with devnode callback of struct
> > device_type I can easily give the defaut mode for any of the files but
> > not for the /dev/sgx directory itself. How do I get the appropriate
> > mode for it?
> >
> 
> Hi Greg-
> 
> Do you know this one?

You can't get the mode of the directory, it is always 0755 for devtmpfs,
is that a problem?  If so, write a udev rule to change it :)

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 17/27] x86/sgx: Add provisioning
  2019-04-05 14:20                 ` Jarkko Sakkinen
@ 2019-04-05 14:34                   ` Greg KH
  2019-04-09 13:37                     ` Jarkko Sakkinen
  0 siblings, 1 reply; 92+ messages in thread
From: Greg KH @ 2019-04-05 14:34 UTC (permalink / raw)
  To: Jarkko Sakkinen
  Cc: Andy Lutomirski, X86 ML, linux-sgx, Andrew Morton, Dave Hansen,
	Christopherson, Sean J, nhorman, npmccallum, Ayoun, Serge,
	Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko,
	Thomas Gleixner, Svahn, Kai, Borislav Petkov, Josh Triplett,
	Huang, Kai, David Rientjes, James Morris, Serge E . Hallyn,
	LSM List

On Fri, Apr 05, 2019 at 05:20:06PM +0300, Jarkko Sakkinen wrote:
> On Fri, Apr 05, 2019 at 06:53:57AM -0700, Andy Lutomirski wrote:
> > On Fri, Apr 5, 2019 at 3:18 AM Jarkko Sakkinen
> > <jarkko.sakkinen@linux.intel.com> wrote:
> > >
> > > On Mon, Mar 25, 2019 at 04:55:03PM +0200, Jarkko Sakkinen wrote:
> > > > > > Hmm.. on 2nd thought the LSM policy or even DAC policy  would restrict
> > > > > > that the container manager can only access specific files inside
> > > > > > securityfs. With this conclusion I still think it is probably the best
> > > > > > place for seurity policy like things even for SGX. It is meant for that
> > > > > > anyway.
> > > > > >
> > > > >
> > > > > LSM or DAC policy can certainly *restrict* it, but I suspect that most
> > > > > container runtimes don't mount securityfs at all.  OTOH, the runtime
> > > > > definitely needs to have a way to pass /dev/sgx/enclave (or whatever
> > > > > it's called) through, so using another device node will definitely
> > > > > work.
> > > >
> > > > OK, I can cope with this argument. I go with the device names above for
> > > > v20.
> > >
> > > In v20 the refactoring would be with corresponding modes:
> > >
> > > /dev/sgx 0755
> > > /dev/sgx/enclave 0666
> > > /dev/sgx/provision 0600
> > >
> > > The problem that I'm facing is that with devnode callback of struct
> > > device_type I can easily give the defaut mode for any of the files but
> > > not for the /dev/sgx directory itself. How do I get the appropriate
> > > mode for it?
> > >
> > 
> > Hi Greg-
> > 
> > Do you know this one?
> 
> I guess one option is to not do anything with the mode but instead
> contribute rules to udev? I'm not too familiar with this but maybe
> that is even recommended way?

Why do you care about the directory permissions?  They should not
matter, only the permissions on the device nodes themselves, right?


^ permalink raw reply	[flat|nested] 92+ messages in thread

* Re: [PATCH v19 17/27] x86/sgx: Add provisioning
  2019-04-05 14:34                   ` Greg KH
@ 2019-04-09 13:37                     ` Jarkko Sakkinen
  0 siblings, 0 replies; 92+ messages in thread
From: Jarkko Sakkinen @ 2019-04-09 13:37 UTC (permalink / raw)
  To: Greg KH
  Cc: Andy Lutomirski, X86 ML, linux-sgx, Andrew Morton, Dave Hansen,
	Christopherson, Sean J, nhorman, npmccallum, Ayoun, Serge,
	Katz-zamir, Shay, Huang, Haitao, Andy Shevchenko,
	Thomas Gleixner, Svahn, Kai, Borislav Petkov, Josh Triplett,
	Huang, Kai, David Rientjes, James Morris, Serge E . Hallyn,
	LSM List

On Fri, Apr 05, 2019 at 04:34:46PM +0200, Greg KH wrote:
> On Fri, Apr 05, 2019 at 05:20:06PM +0300, Jarkko Sakkinen wrote:
> > On Fri, Apr 05, 2019 at 06:53:57AM -0700, Andy Lutomirski wrote:
> > > On Fri, Apr 5, 2019 at 3:18 AM Jarkko Sakkinen
> > > <jarkko.sakkinen@linux.intel.com> wrote:
> > > >
> > > > On Mon, Mar 25, 2019 at 04:55:03PM +0200, Jarkko Sakkinen wrote:
> > > > > > > Hmm.. on 2nd thought the LSM policy or even DAC policy  would restrict
> > > > > > > that the container manager can only access specific files inside
> > > > > > > securityfs. With this conclusion I still think it is probably the best
> > > > > > > place for seurity policy like things even for SGX. It is meant for that
> > > > > > > anyway.
> > > > > > >
> > > > > >
> > > > > > LSM or DAC policy can certainly *restrict* it, but I suspect that most
> > > > > > container runtimes don't mount securityfs at all.  OTOH, the runtime
> > > > > > definitely needs to have a way to pass /dev/sgx/enclave (or whatever
> > > > > > it's called) through, so using another device node will definitely
> > > > > > work.
> > > > >
> > > > > OK, I can cope with this argument. I go with the device names above for
> > > > > v20.
> > > >
> > > > In v20 the refactoring would be with corresponding modes:
> > > >
> > > > /dev/sgx 0755
> > > > /dev/sgx/enclave 0666
> > > > /dev/sgx/provision 0600
> > > >
> > > > The problem that I'm facing is that with devnode callback of struct
> > > > device_type I can easily give the defaut mode for any of the files but
> > > > not for the /dev/sgx directory itself. How do I get the appropriate
> > > > mode for it?
> > > >
> > > 
> > > Hi Greg-
> > > 
> > > Do you know this one?
> > 
> > I guess one option is to not do anything with the mode but instead
> > contribute rules to udev? I'm not too familiar with this but maybe
> > that is even recommended way?
> 
> Why do you care about the directory permissions?  They should not
> matter, only the permissions on the device nodes themselves, right?

OK, based on your response to Andy I don't :-)

Thanks.

/Jarkko

^ permalink raw reply	[flat|nested] 92+ messages in thread

end of thread, back to index

Thread overview: 92+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-03-17 21:14 [PATCH v19 00/27] Intel SGX1 support Jarkko Sakkinen
2019-03-17 21:14 ` [PATCH v19 01/27] x86/cpufeatures: Add Intel-defined SGX feature bit Jarkko Sakkinen
2019-03-17 21:14 ` [PATCH v19 02/27] x86/cpufeatures: Add SGX sub-features (as Linux-defined bits) Jarkko Sakkinen
2019-03-17 21:14 ` [PATCH v19 03/27] x86/msr: Add IA32_FEATURE_CONTROL.SGX_ENABLE definition Jarkko Sakkinen
2019-03-17 21:14 ` [PATCH v19 04/27] x86/cpufeatures: Add Intel-defined SGX_LC feature bit Jarkko Sakkinen
2019-03-17 21:14 ` [PATCH v19 05/27] x86/msr: Add SGX Launch Control MSR definitions Jarkko Sakkinen
2019-03-17 21:14 ` [PATCH v19 06/27] x86/mm: x86/sgx: Add new 'PF_SGX' page fault error code bit Jarkko Sakkinen
2019-03-17 21:14 ` [PATCH v19 07/27] x86/mm: x86/sgx: Signal SIGSEGV for userspace #PFs w/ PF_SGX Jarkko Sakkinen
2019-03-18 17:15   ` Dave Hansen
2019-03-18 19:53     ` Sean Christopherson
2019-03-17 21:14 ` [PATCH v19 08/27] x86/cpu/intel: Detect SGX support and update caps appropriately Jarkko Sakkinen
2019-03-17 21:14 ` [PATCH v19 09/27] x86/sgx: Add ENCLS architectural error codes Jarkko Sakkinen
2019-03-17 21:14 ` [PATCH v19 10/27] x86/sgx: Add SGX1 and SGX2 architectural data structures Jarkko Sakkinen
2019-03-17 21:14 ` [PATCH v19 11/27] x86/sgx: Add definitions for SGX's CPUID leaf and variable sub-leafs Jarkko Sakkinen
2019-03-17 21:14 ` [PATCH v19 12/27] x86/sgx: Enumerate and track EPC sections Jarkko Sakkinen
2019-03-18 19:50   ` Sean Christopherson
2019-03-21 14:40     ` Jarkko Sakkinen
2019-03-21 15:28       ` Sean Christopherson
2019-03-22 10:19         ` Jarkko Sakkinen
2019-03-22 10:50           ` Jarkko Sakkinen
2019-03-17 21:14 ` [PATCH v19 13/27] x86/sgx: Add wrappers for ENCLS leaf functions Jarkko Sakkinen
2019-03-19 19:59   ` Sean Christopherson
2019-03-21 14:51     ` Jarkko Sakkinen
2019-03-21 15:40       ` Sean Christopherson
2019-03-22 11:00         ` Jarkko Sakkinen
2019-03-22 16:43           ` Sean Christopherson
2019-03-17 21:14 ` [PATCH v19 16/27] x86/sgx: Add the Linux SGX Enclave Driver Jarkko Sakkinen
2019-03-19 21:19   ` Sean Christopherson
2019-03-21 15:51     ` Jarkko Sakkinen
2019-03-21 16:47       ` Sean Christopherson
2019-03-22 11:10         ` Jarkko Sakkinen
2019-03-26 13:26       ` Jarkko Sakkinen
2019-03-26 23:58         ` Sean Christopherson
2019-03-27  5:28           ` Jarkko Sakkinen
2019-03-27 17:57             ` Sean Christopherson
2019-03-27 18:38             ` Jethro Beekman
2019-03-27 20:06               ` Sean Christopherson
2019-03-28  1:21                 ` Jethro Beekman
2019-03-28 13:19                 ` Jarkko Sakkinen
2019-03-28 19:05                   ` Andy Lutomirski
2019-03-29  9:43                     ` Jarkko Sakkinen
2019-03-29 16:20                     ` Sean Christopherson
2019-04-01 10:01                       ` Jarkko Sakkinen
2019-04-01 17:25                         ` Jethro Beekman
2019-04-01 22:57                           ` Jarkko Sakkinen
2019-03-28 13:15               ` Jarkko Sakkinen
2019-03-19 23:00   ` Sean Christopherson
2019-03-21 16:18     ` Jarkko Sakkinen
2019-03-21 17:38       ` Sean Christopherson
2019-03-22 11:17         ` Jarkko Sakkinen
2019-03-17 21:14 ` [PATCH v19 17/27] x86/sgx: Add provisioning Jarkko Sakkinen
2019-03-19 20:09   ` Sean Christopherson
2019-03-21  2:08     ` Huang, Kai
2019-03-21 14:32       ` Jarkko Sakkinen
2019-03-21 21:41         ` Huang, Kai
2019-03-22 11:31           ` Jarkko Sakkinen
2019-03-21 14:30     ` Jarkko Sakkinen
2019-03-21 14:38   ` Nathaniel McCallum
2019-03-22 11:22     ` Jarkko Sakkinen
2019-03-21 16:50   ` Andy Lutomirski
2019-03-22 11:29     ` Jarkko Sakkinen
2019-03-22 11:43       ` Jarkko Sakkinen
2019-03-22 18:20         ` Andy Lutomirski
2019-03-25 14:55           ` Jarkko Sakkinen
2019-03-27  0:14             ` Sean Christopherson
2019-04-05 10:18             ` Jarkko Sakkinen
2019-04-05 13:53               ` Andy Lutomirski
2019-04-05 14:20                 ` Jarkko Sakkinen
2019-04-05 14:34                   ` Greg KH
2019-04-09 13:37                     ` Jarkko Sakkinen
2019-04-05 14:21                 ` Greg KH
2019-03-17 21:14 ` [PATCH v19 19/27] x86/sgx: ptrace() support for the SGX driver Jarkko Sakkinen
2019-03-19 22:22   ` Sean Christopherson
2019-03-21 15:02     ` Jarkko Sakkinen
2019-03-17 21:14 ` [PATCH v19 20/27] x86/vdso: Add support for exception fixup in vDSO functions Jarkko Sakkinen
2019-03-17 21:14 ` [PATCH v19 21/27] x86/fault: Add helper function to sanitize error code Jarkko Sakkinen
2019-03-17 21:14 ` [PATCH v19 22/27] x86/fault: Attempt to fixup unhandled #PF in vDSO before signaling Jarkko Sakkinen
2019-03-17 21:14 ` [PATCH v19 23/27] x86/traps: Attempt to fixup exceptions " Jarkko Sakkinen
2019-03-17 21:14 ` [PATCH v19 25/27] x86/sgx: SGX documentation Jarkko Sakkinen
2019-03-20 17:14   ` Sean Christopherson
2019-03-21 16:24     ` Jarkko Sakkinen
2019-03-17 21:14 ` [PATCH v19 26/27] selftests/x86: Add a selftest for SGX Jarkko Sakkinen
2019-03-17 21:14 ` [PATCH v19 27/27] x86/sgx: Update MAINTAINERS Jarkko Sakkinen
2019-03-19 17:12   ` Sean Christopherson
2019-03-21 14:42     ` Jarkko Sakkinen
     [not found] ` <20190317211456.13927-19-jarkko.sakkinen@linux.intel.com>
2019-03-19 22:09   ` [PATCH v19 18/27] x86/sgx: Add swapping code to the core and SGX driver Sean Christopherson
2019-03-21 14:59     ` Jarkko Sakkinen
2019-03-19 23:41 ` [PATCH v19 00/27] Intel SGX1 support Sean Christopherson
2019-03-19 23:52   ` Jethro Beekman
2019-03-20  0:22     ` Sean Christopherson
2019-03-21 16:20     ` Jarkko Sakkinen
2019-03-21 16:00   ` Jarkko Sakkinen

Linux-Sgx Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-sgx/0 linux-sgx/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-sgx linux-sgx/ https://lore.kernel.org/linux-sgx \
		linux-sgx@vger.kernel.org linux-sgx@archiver.kernel.org
	public-inbox-index linux-sgx

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-sgx


AGPL code for this site: git clone https://public-inbox.org/ public-inbox