Commit 5583ff67 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'x86_sgx_for_v5.11' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull x86 SGC support from Borislav Petkov:
 "Intel Software Guard eXtensions enablement. This has been long in the
  making, we were one revision number short of 42. :)

  Intel SGX is new hardware functionality that can be used by
  applications to populate protected regions of user code and data
  called enclaves. Once activated, the new hardware protects enclave
  code and data from outside access and modification.

  Enclaves provide a place to store secrets and process data with those
  secrets. SGX has been used, for example, to decrypt video without
  exposing the decryption keys to nosy debuggers that might be used to
  subvert DRM. Software has generally been rewritten specifically to run
  in enclaves, but there are also projects that try to run limited
  unmodified software in enclaves.

  Most of the functionality is concentrated into arch/x86/kernel/cpu/sgx/
  except the addition of a new mprotect() hook to control enclave page
  permissions and support for vDSO exceptions fixup which will is used
  by SGX enclaves.

  All this work by Sean Christopherson, Jarkko Sakkinen and many others"

* tag 'x86_sgx_for_v5.11' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (30 commits)
  x86/sgx: Return -EINVAL on a zero length buffer in sgx_ioc_enclave_add_pages()
  x86/sgx: Fix a typo in kernel-doc markup
  x86/sgx: Fix sgx_ioc_enclave_provision() kernel-doc comment
  x86/sgx: Return -ERESTARTSYS in sgx_ioc_enclave_add_pages()
  selftests/sgx: Use a statically generated 3072-bit RSA key
  x86/sgx: Clarify 'laundry_list' locking
  x86/sgx: Update MAINTAINERS
  Documentation/x86: Document SGX kernel architecture
  x86/sgx: Add ptrace() support for the SGX driver
  x86/sgx: Add a page reclaimer
  selftests/x86: Add a selftest for SGX
  x86/vdso: Implement a vDSO for Intel SGX enclave call
  x86/traps: Attempt to fixup exceptions in vDSO before signaling
  x86/fault: Add a helper function to sanitize error code
  x86/vdso: Add support for exception fixup in vDSO functions
  x86/sgx: Add SGX_IOC_ENCLAVE_PROVISION
  x86/sgx: Add SGX_IOC_ENCLAVE_INIT
  x86/sgx: Add SGX_IOC_ENCLAVE_ADD_PAGES
  x86/sgx: Add SGX_IOC_ENCLAVE_CREATE
  x86/sgx: Add an SGX misc driver interface
  ...
parents 85fe40ca a4b9c48b
......@@ -3375,6 +3375,8 @@
nosep [BUGS=X86-32] Disables x86 SYSENTER/SYSEXIT support.
nosgx [X86-64,SGX] Disables Intel SGX kernel support.
nosmp [SMP] Tells an SMP kernel to act as a UP kernel,
and disable the IO APIC. legacy for "maxcpus=0".
......
......@@ -323,6 +323,7 @@ Code Seq# Include File Comments
<mailto:tlewis@mindspring.com>
0xA3 90-9F linux/dtlk.h
0xA4 00-1F uapi/linux/tee.h Generic TEE subsystem
0xA4 00-1F uapi/asm/sgx.h <mailto:linux-sgx@vger.kernel.org>
0xAA 00-3F linux/uapi/linux/userfaultfd.h
0xAB 00-1F linux/nbd.h
0xAC 00-1F linux/raw.h
......
......@@ -33,3 +33,4 @@ x86-specific Documentation
i386/index
x86_64/index
sva
sgx
.. SPDX-License-Identifier: GPL-2.0
===============================
Software Guard eXtensions (SGX)
===============================
Overview
========
Software Guard eXtensions (SGX) hardware enables for user space applications
to set aside private memory regions of code and data:
* Privileged (ring-0) ENCLS functions orchestrate the construction of the.
regions.
* Unprivileged (ring-3) ENCLU functions allow an application to enter and
execute inside the regions.
These memory regions are called enclaves. An enclave can be only entered at a
fixed set of entry points. Each entry point can hold a single hardware thread
at a time. While the enclave is loaded from a regular binary file by using
ENCLS functions, only the threads inside the enclave can access its memory. The
region is denied from outside access by the CPU, and encrypted before it leaves
from LLC.
The support can be determined by
``grep sgx /proc/cpuinfo``
SGX must both be supported in the processor and enabled by the BIOS. If SGX
appears to be unsupported on a system which has hardware support, ensure
support is enabled in the BIOS. If a BIOS presents a choice between "Enabled"
and "Software Enabled" modes for SGX, choose "Enabled".
Enclave Page Cache
==================
SGX utilizes an *Enclave Page Cache (EPC)* to store pages that are associated
with an enclave. It is contained in a BIOS-reserved region of physical memory.
Unlike pages used for regular memory, pages can only be accessed from outside of
the enclave during enclave construction with special, limited SGX instructions.
Only a CPU executing inside an enclave can directly access enclave memory.
However, a CPU executing inside an enclave may access normal memory outside the
enclave.
The kernel manages enclave memory similar to how it treats device memory.
Enclave Page Types
------------------
**SGX Enclave Control Structure (SECS)**
Enclave's address range, attributes and other global data are defined
by this structure.
**Regular (REG)**
Regular EPC pages contain the code and data of an enclave.
**Thread Control Structure (TCS)**
Thread Control Structure pages define the entry points to an enclave and
track the execution state of an enclave thread.
**Version Array (VA)**
Version Array pages contain 512 slots, each of which can contain a version
number for a page evicted from the EPC.
Enclave Page Cache Map
----------------------
The processor tracks EPC pages in a hardware metadata structure called the
*Enclave Page Cache Map (EPCM)*. The EPCM contains an entry for each EPC page
which describes the owning enclave, access rights and page type among the other
things.
EPCM permissions are separate from the normal page tables. This prevents the
kernel from, for instance, allowing writes to data which an enclave wishes to
remain read-only. EPCM permissions may only impose additional restrictions on
top of normal x86 page permissions.
For all intents and purposes, the SGX architecture allows the processor to
invalidate all EPCM entries at will. This requires that software be prepared to
handle an EPCM fault at any time. In practice, this can happen on events like
power transitions when the ephemeral key that encrypts enclave memory is lost.
Application interface
=====================
Enclave build functions
-----------------------
In addition to the traditional compiler and linker build process, SGX has a
separate enclave “build” process. Enclaves must be built before they can be
executed (entered). The first step in building an enclave is opening the
**/dev/sgx_enclave** device. Since enclave memory is protected from direct
access, special privileged instructions are Then used to copy data into enclave
pages and establish enclave page permissions.
.. kernel-doc:: arch/x86/kernel/cpu/sgx/ioctl.c
:functions: sgx_ioc_enclave_create
sgx_ioc_enclave_add_pages
sgx_ioc_enclave_init
sgx_ioc_enclave_provision
Enclave vDSO
------------
Entering an enclave can only be done through SGX-specific EENTER and ERESUME
functions, and is a non-trivial process. Because of the complexity of
transitioning to and from an enclave, enclaves typically utilize a library to
handle the actual transitions. This is roughly analogous to how glibc
implementations are used by most applications to wrap system calls.
Another crucial characteristic of enclaves is that they can generate exceptions
as part of their normal operation that need to be handled in the enclave or are
unique to SGX.
Instead of the traditional signal mechanism to handle these exceptions, SGX
can leverage special exception fixup provided by the vDSO. The kernel-provided
vDSO function wraps low-level transitions to/from the enclave like EENTER and
ERESUME. The vDSO function intercepts exceptions that would otherwise generate
a signal and return the fault information directly to its caller. This avoids
the need to juggle signal handlers.
.. kernel-doc:: arch/x86/include/uapi/asm/sgx.h
:functions: vdso_sgx_enter_enclave_t
ksgxd
=====
SGX support includes a kernel thread called *ksgxwapd*.
EPC sanitization
----------------
ksgxd is started when SGX initializes. Enclave memory is typically ready
For use when the processor powers on or resets. However, if SGX has been in
use since the reset, enclave pages may be in an inconsistent state. This might
occur after a crash and kexec() cycle, for instance. At boot, ksgxd
reinitializes all enclave pages so that they can be allocated and re-used.
The sanitization is done by going through EPC address space and applying the
EREMOVE function to each physical page. Some enclave pages like SECS pages have
hardware dependencies on other pages which prevents EREMOVE from functioning.
Executing two EREMOVE passes removes the dependencies.
Page reclaimer
--------------
Similar to the core kswapd, ksgxd, is responsible for managing the
overcommitment of enclave memory. If the system runs out of enclave memory,
*ksgxwapd* “swaps” enclave memory to normal memory.
Launch Control
==============
SGX provides a launch control mechanism. After all enclave pages have been
copied, kernel executes EINIT function, which initializes the enclave. Only after
this the CPU can execute inside the enclave.
ENIT function takes an RSA-3072 signature of the enclave measurement. The function
checks that the measurement is correct and signature is signed with the key
hashed to the four **IA32_SGXLEPUBKEYHASH{0, 1, 2, 3}** MSRs representing the
SHA256 of a public key.
Those MSRs can be configured by the BIOS to be either readable or writable.
Linux supports only writable configuration in order to give full control to the
kernel on launch control policy. Before calling EINIT function, the driver sets
the MSRs to match the enclave's signing key.
Encryption engines
==================
In order to conceal the enclave data while it is out of the CPU package, the
memory controller has an encryption engine to transparently encrypt and decrypt
enclave memory.
In CPUs prior to Ice Lake, the Memory Encryption Engine (MEE) is used to
encrypt pages leaving the CPU caches. MEE uses a n-ary Merkle tree with root in
SRAM to maintain integrity of the encrypted data. This provides integrity and
anti-replay protection but does not scale to large memory sizes because the time
required to update the Merkle tree grows logarithmically in relation to the
memory size.
CPUs starting from Icelake use Total Memory Encryption (TME) in the place of
MEE. TME-based SGX implementations do not have an integrity Merkle tree, which
means integrity and replay-attacks are not mitigated. B, it includes
additional changes to prevent cipher text from being returned and SW memory
aliases from being Created.
DMA to enclave memory is blocked by range registers on both MEE and TME systems
(SDM section 41.10).
Usage Models
============
Shared Library
--------------
Sensitive data and the code that acts on it is partitioned from the application
into a separate library. The library is then linked as a DSO which can be loaded
into an enclave. The application can then make individual function calls into
the enclave through special SGX instructions. A run-time within the enclave is
configured to marshal function parameters into and out of the enclave and to
call the correct library function.
Application Container
---------------------
An application may be loaded into a container enclave which is specially
configured with a library OS and run-time which permits the application to run.
The enclave run-time and library OS work together to execute the application
when a thread enters the enclave.
......@@ -9167,6 +9167,19 @@ F: Documentation/x86/intel_txt.rst
F: arch/x86/kernel/tboot.c
F: include/linux/tboot.h
INTEL SGX
M: Jarkko Sakkinen <jarkko@kernel.org>
L: linux-sgx@vger.kernel.org
S: Supported
Q: https://patchwork.kernel.org/project/intel-sgx/list/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/jarkko/linux-sgx.git
F: Documentation/x86/sgx.rst
F: arch/x86/entry/vdso/vsgx.S
F: arch/x86/include/uapi/asm/sgx.h
F: arch/x86/kernel/cpu/sgx/*
F: tools/testing/selftests/sgx/*
K: \bSGX_
INTERCONNECT API
M: Georgi Djakov <georgi.djakov@linaro.org>
L: linux-pm@vger.kernel.org
......
......@@ -1931,6 +1931,23 @@ config X86_INTEL_TSX_MODE_AUTO
side channel attacks- equals the tsx=auto command line parameter.
endchoice
config X86_SGX
bool "Software Guard eXtensions (SGX)"
depends on X86_64 && CPU_SUP_INTEL
depends on CRYPTO=y
depends on CRYPTO_SHA256=y
select SRCU
select MMU_NOTIFIER
help
Intel(R) 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, 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.
If unsure, say N.
config EFI
bool "EFI runtime service support"
depends on ACPI
......
......@@ -27,9 +27,10 @@ VDSO32-$(CONFIG_IA32_EMULATION) := y
vobjs-y := vdso-note.o vclock_gettime.o vgetcpu.o
vobjs32-y := vdso32/note.o vdso32/system_call.o vdso32/sigreturn.o
vobjs32-y += vdso32/vclock_gettime.o
vobjs-$(CONFIG_X86_SGX) += vsgx.o
# files to link into kernel
obj-y += vma.o
obj-y += vma.o extable.o
KASAN_SANITIZE_vma.o := y
UBSAN_SANITIZE_vma.o := y
KCSAN_SANITIZE_vma.o := y
......@@ -98,6 +99,7 @@ $(vobjs): KBUILD_CFLAGS := $(filter-out $(GCC_PLUGINS_CFLAGS) $(RETPOLINE_CFLAGS
CFLAGS_REMOVE_vclock_gettime.o = -pg
CFLAGS_REMOVE_vdso32/vclock_gettime.o = -pg
CFLAGS_REMOVE_vgetcpu.o = -pg
CFLAGS_REMOVE_vsgx.o = -pg
#
# X32 processes use x32 vDSO to access 64bit kernel data.
......@@ -128,8 +130,8 @@ $(obj)/%-x32.o: $(obj)/%.o FORCE
targets += vdsox32.lds $(vobjx32s-y)
$(obj)/%.so: OBJCOPYFLAGS := -S
$(obj)/%.so: $(obj)/%.so.dbg FORCE
$(obj)/%.so: OBJCOPYFLAGS := -S --remove-section __ex_table
$(obj)/%.so: $(obj)/%.so.dbg
$(call if_changed,objcopy)
$(obj)/vdsox32.so.dbg: $(obj)/vdsox32.lds $(vobjx32s) FORCE
......
// SPDX-License-Identifier: GPL-2.0
#include <linux/err.h>
#include <linux/mm.h>
#include <asm/current.h>
#include <asm/traps.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;
/*
* Do not attempt to fixup #DB or #BP. It's impossible to identify
* whether or not a #DB/#BP originated from within an SGX enclave and
* SGX enclaves are currently the only use case for vDSO fixup.
*/
if (trapnr == X86_TRAP_DB || trapnr == X86_TRAP_BP)
return false;
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;
}
/* 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 */
......@@ -75,11 +75,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.*)
......
......@@ -27,6 +27,7 @@ VERSION {
__vdso_time;
clock_getres;
__vdso_clock_getres;
__vdso_sgx_enter_enclave;
local: *;
};
}
......@@ -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 *image_name)
......@@ -15,7 +50,7 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len,
ELF(Ehdr) *hdr = (ELF(Ehdr) *)raw_addr;
unsigned long i, syms_nr;
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] = {};
......@@ -77,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)
......@@ -155,6 +192,9 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len,
(int)((unsigned char *)stripped_addr)[i]);
}
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", image_name);
fprintf(outfile, "\t.data = raw_data,\n");
......@@ -165,6 +205,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",
......
/* SPDX-License-Identifier: GPL-2.0 */
#include <linux/linkage.h>
#include <asm/export.h>
#include <asm/errno.h>
#include <asm/enclu.h>
#include "extable.h"
/* Relative to %rbp. */
#define SGX_ENCLAVE_OFFSET_OF_RUN 16
/* The offsets relative to struct sgx_enclave_run. */
#define SGX_ENCLAVE_RUN_TCS 0
#define SGX_ENCLAVE_RUN_LEAF 8
#define SGX_ENCLAVE_RUN_EXCEPTION_VECTOR 12
#define SGX_ENCLAVE_RUN_EXCEPTION_ERROR_CODE 14
#define SGX_ENCLAVE_RUN_EXCEPTION_ADDR 16
#define SGX_ENCLAVE_RUN_USER_HANDLER 24
#define SGX_ENCLAVE_RUN_USER_DATA 32 /* not used */
#define SGX_ENCLAVE_RUN_RESERVED_START 40
#define SGX_ENCLAVE_RUN_RESERVED_END 256
.code64
.section .text, "ax"
SYM_FUNC_START(__vdso_sgx_enter_enclave)
/* Prolog */
.cfi_startproc
push %rbp
.cfi_adjust_cfa_offset 8
.cfi_rel_offset %rbp, 0
mov %rsp, %rbp
.cfi_def_cfa_register %rbp
push %rbx
.cfi_rel_offset %rbx, -8
mov %ecx, %eax
.Lenter_enclave:
/* EENTER <= function <= ERESUME */
cmp $EENTER, %eax
jb .Linvalid_input
cmp $ERESUME, %eax
ja .Linvalid_input
mov SGX_ENCLAVE_OFFSET_OF_RUN(%rbp), %rcx
/* Validate that the reserved area contains only zeros. */
mov $SGX_ENCLAVE_RUN_RESERVED_START, %rbx
1:
cmpq $0, (%rcx, %rbx)
jne .Linvalid_input
add $8, %rbx
cmpq $SGX_ENCLAVE_RUN_RESERVED_END, %rbx
jne 1b
/* Load TCS and AEP */
mov SGX_ENCLAVE_RUN_TCS(%rcx), %rbx
lea .Lasync_exit_pointer(%rip), %rcx
/* Single ENCLU serving as both EENTER and AEP (ERESUME) */
.Lasync_exit_pointer:
.Lenclu_eenter_eresume:
enclu
/* EEXIT jumps here unless the enclave is doing something fancy. */
mov SGX_ENCLAVE_OFFSET_OF_RUN(%rbp), %rbx
/* Set exit_reason. */
movl $EEXIT, SGX_ENCLAVE_RUN_LEAF(%rbx)
/* Invoke userspace's exit handler if one was provided. */
.Lhandle_exit:
cmpq $0, SGX_ENCLAVE_RUN_USER_HANDLER(%rbx)
jne .Linvoke_userspace_handler
/* Success, in the sense that ENCLU was attempted. */
xor %eax, %eax
.Lout:
pop %rbx
leave
.cfi_def_cfa %rsp, 8
ret
/* The out-of-line code runs with the pre-leave stack frame. */
.cfi_def_cfa %rbp, 16
.Linvalid_input:
mov $(-EINVAL), %eax
jmp .Lout
.Lhandle_exception:
mov SGX_ENCLAVE_OFFSET_OF_RUN(%rbp), %rbx
/* Set the exception info. */
mov %eax, (SGX_ENCLAVE_RUN_LEAF)(%rbx)
mov %di, (SGX_ENCLAVE_RUN_EXCEPTION_VECTOR)(%rbx)
mov %si, (SGX_ENCLAVE_RUN_EXCEPTION_ERROR_CODE)(%rbx)
mov %rdx, (SGX_ENCLAVE_RUN_EXCEPTION_ADDR)(%rbx)
jmp .Lhandle_exit
.Linvoke_userspace_handler:
/* Pass the untrusted RSP (at exit) to the callback via %rcx. */
mov %rsp, %rcx
/* Save struct sgx_enclave_exception %rbx is about to be clobbered. */
mov %rbx, %rax
/* Save the untrusted RSP offset in %rbx (non-volatile register). */
mov %rsp, %rbx
and $0xf, %rbx
/*
* Align stack per x86_64 ABI. Note, %rsp needs to be 16-byte aligned
* _after_ pushing the parameters on the stack, hence the bonus push.
*/
and $-0x10, %rsp
push %rax
/* Push struct sgx_enclave_exception as a param to the callback. */
push %rax
/* Clear RFLAGS.DF per x86_64 ABI */
cld
/*
* Load the callback pointer to %rax and lfence for LVI (load value
* injection) protection before making the call.
*/
mov SGX_ENCLAVE_RUN_USER_HANDLER(%rax), %rax
lfence
call *%rax
/* Undo the post-exit %rsp adjustment. */
lea 0x10(%rsp, %rbx), %rsp
/*
* If the return from callback is zero or negative, return immediately,
* else re-execute ENCLU with the postive return value interpreted as
* the requested ENCLU function.
*/
cmp $0, %eax
jle .Lout
jmp .Lenter_enclave
.cfi_endproc
_ASM_VDSO_EXTABLE_HANDLE(.Lenclu_eenter_eresume, .Lhandle_exception)
SYM_FUNC_END(__vdso_sgx_enter_enclave)
......@@ -241,6 +241,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 */
......@@ -356,6 +357,7 @@
#define X86_FEATURE_MOVDIRI (16*32+27) /* MOVDIRI instruction */
#define X86_FEATURE_MOVDIR64B (16*32+28) /* MOVDIR64B instruction */
#define X86_FEATURE_ENQCMD (16*32+29) /* ENQCMD and ENQCMDS instructions */
#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 */
......
......@@ -62,6 +62,12 @@
# define DISABLE_ENQCMD (1 << (X86_FEATURE_ENQCMD & 31))
#endif
#ifdef CONFIG_X86_SGX
# define DISABLE_SGX 0
#else
# define DISABLE_SGX (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_SMAP)
#define DISABLED_MASK9 (DISABLE_SMAP|DISABLE_SGX)
#define DISABLED_MASK10 0
#define DISABLED_MASK11 0
#define DISABLED_MASK12 0
......
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _ASM_X86_ENCLU_H
#define _ASM_X86_ENCLU_H
#define EENTER 0x02
#define ERESUME 0x03
#define EEXIT 0x04
#endif /* _ASM_X86_ENCLU_H */
......@@ -610,6 +610,8 @@
#define FEAT_CTL_LOCKED BIT(0)
#define FEAT_CTL_VMX_ENABLED_INSIDE_SMX BIT(1)
#define FEAT_CTL_VMX_ENABLED_OUTSIDE_SMX BIT(2)
#define FEAT_CTL_SGX_LC_ENABLED BIT(17)
#define FEAT_CTL_SGX_ENABLED BIT(18)
#define FEAT_CTL_LMCE_ENABLED BIT(20)
#define MSR_IA32_TSC_ADJUST 0x0000003b
......@@ -629,6 +631,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
......
......@@ -11,6 +11,7 @@
* bit 3 == 1: use of reserved bit detected
* bit 4 == 1: fault was an instruction fetch
* bit 5 == 1: protection keys block access
* bit 15 == 1: SGX MMU page-fault
*/
enum x86_pf_error_code {
X86_PF_PROT = 1 << 0,
......@@ -19,6 +20,7 @@ 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_TRAP_PF_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 */
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
/*
* Copyright(c) 2016-20 Intel Corporation.
*/
#ifndef _UAPI_ASM_X86_SGX_H
#define _UAPI_ASM_X86_SGX_H
#include <linux/types.h>
#include <linux/ioctl.h>
/**
* enum sgx_page_flags - page control flags
* %SGX_PAGE_MEASURE: Measure the page contents with a sequence of
* ENCLS[EEXTEND] operations.
*/
enum sgx_page_flags {
SGX_PAGE_MEASURE = 0x01,
};
#define SGX_MAGIC 0xA4
#define SGX_IOC_ENCLAVE_CREATE \
_IOW(SGX_MAGIC, 0x00, struct sgx_enclave_create)
#define SGX_IOC_ENCLAVE_ADD_PAGES \
_IOWR(SGX_MAGIC, 0x01, struct sgx_enclave_add_pages)
#define SGX_IOC_ENCLAVE_INIT \
_IOW(SGX_MAGIC, 0x02, struct sgx_enclave_init)
#define SGX_IOC_ENCLAVE_PROVISION \
_IOW(SGX_MAGIC, 0x03, struct sgx_enclave_provision)
/**
* 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_pages - parameter structure for the
* %SGX_IOC_ENCLAVE_ADD_PAGE ioctl
* @src: start address for the page data
* @offset: starting page offset
* @length: length of the data (multiple of the page size)
* @secinfo: address for the SECINFO data
* @flags: page control flags
* @count: number of bytes added (multiple of the page size)
*/
struct sgx_enclave_add_pages {
__u64 src;
__u64 offset;
__u64 length;
__u64 secinfo;
__u64 flags;
__u64 count;
};
/**
* struct sgx_enclave_init - parameter structure for the
* %SGX_IOC_ENCLAVE_INIT ioctl
* @sigstruct: address for the SIGSTRUCT data
*/
struct sgx_enclave_init {
__u64 sigstruct;
};
/**
* struct sgx_enclave_provision - parameter structure for the
* %SGX_IOC_ENCLAVE_PROVISION ioctl
* @fd: file handle of /dev/sgx_provision
*/
struct sgx_enclave_provision {
__u64 fd;
};
struct sgx_enclave_run;
/**
* typedef sgx_enclave_user_handler_t - Exit handler function accepted by
* __vdso_sgx_enter_enclave()
* @run: The run instance given by the caller
*
* The register parameters contain the snapshot of their values at enclave
* exit. An invalid ENCLU function number will cause -EINVAL to be returned
* to the caller.
*
* Return:
* - <= 0: The given value is returned back to the caller.
* - > 0: ENCLU function to invoke, either EENTER or ERESUME.
*/
typedef int (*sgx_enclave_user_handler_t)(long rdi, long rsi, long rdx,
long rsp, long r8, long r9,
struct sgx_enclave_run *run);
/**
* struct sgx_enclave_run - the execution context of __vdso_sgx_enter_enclave()
* @tcs: TCS used to enter the enclave
* @function: The last seen ENCLU function (EENTER, ERESUME or EEXIT)
* @exception_vector: The interrupt vector of the exception
* @exception_error_code: The exception error code pulled out of the stack
* @exception_addr: The address that triggered the exception
* @user_handler: User provided callback run on exception
* @user_data: Data passed to the user handler
* @reserved Reserved for future extensions
*
* If @user_handler is provided, the handler will be invoked on all return paths
* of the normal flow. The user handler may transfer control, e.g. via a
* longjmp() call or a C++ exception, without returning to
* __vdso_sgx_enter_enclave().
*/
struct sgx_enclave_run {
__u64 tcs;
__u32 function;
__u16 exception_vector;
__u16 exception_error_code;
__u64 exception_addr;
__u64 user_handler;
__u64 user_data;
__u8 reserved[216];
};
/**
* typedef vdso_sgx_enter_enclave_t - Prototype for __vdso_sgx_enter_enclave(),
* a vDSO function to enter an SGX enclave.
* @rdi: Pass-through value for RDI
* @rsi: Pass-through value for RSI
* @rdx: Pass-through value for RDX
* @function: ENCLU function, must be EENTER or ERESUME
* @r8: Pass-through value for R8
* @r9: Pass-through value for R9
* @run: struct sgx_enclave_run, must be non-NULL
*
* NOTE: __vdso_sgx_enter_enclave() does not ensure full compliance with the
* x86-64 ABI, e.g. doesn't handle XSAVE state. Except for non-volatile
* general purpose registers, EFLAGS.DF, and RSP alignment, preserving/setting
* state in accordance with the x86-64 ABI is the responsibility of the enclave
* and its runtime, i.e. __vdso_sgx_enter_enclave() cannot be called from C
* code without careful consideration by both the enclave and its runtime.
*
* All general purpose registers except RAX, RBX and RCX are passed as-is to the
* enclave. RAX, RBX and RCX are consumed by EENTER and ERESUME and are loaded
* with @function, asynchronous exit pointer, and @run.tcs respectively.
*
* RBP and the stack are used to anchor __vdso_sgx_enter_enclave() to the
* pre-enclave state, e.g. to retrieve @run.exception and @run.user_handler
* after an enclave exit. All other registers are available for use by the
* enclave and its runtime, e.g. an enclave can push additional data onto the
* stack (and modify RSP) to pass information to the optional user handler (see
* below).
*
* Most exceptions reported on ENCLU, including those that occur within the
* enclave, are fixed up and reported synchronously instead of being delivered
* via a standard signal. Debug Exceptions (#DB) and Breakpoints (#BP) are
* never fixed up and are always delivered via standard signals. On synchrously
* reported exceptions, -EFAULT is returned and details about the exception are
* recorded in @run.exception, the optional sgx_enclave_exception struct.
*
* Return:
* - 0: ENCLU function was successfully executed.
* - -EINVAL: Invalid ENCL number (neither EENTER nor ERESUME).
*/
typedef int (*vdso_sgx_enter_enclave_t)(unsigned long rdi, unsigned long rsi,
unsigned long rdx, unsigned int function,
unsigned long r8, unsigned long r9,
struct sgx_enclave_run *run);
#endif /* _UAPI_ASM_X86_SGX_H */
......@@ -48,6 +48,7 @@ obj-$(CONFIG_X86_MCE) += mce/
obj-$(CONFIG_MTRR) += mtrr/
obj-$(CONFIG_MICROCODE) += microcode/
obj-$(CONFIG_X86_CPU_RESCTRL) += resctrl/
obj-$(CONFIG_X86_SGX) += sgx/
obj-$(CONFIG_X86_LOCAL_APIC) += perfctr-watchdog.o
......
......@@ -93,16 +93,41 @@ static void init_vmx_capabilities(struct cpuinfo_x86 *c)
}
#endif /* CONFIG_X86_VMX_FEATURE_NAMES */
static void clear_sgx_caps(void)
{
setup_clear_cpu_cap(X86_FEATURE_SGX);
setup_clear_cpu_cap(X86_FEATURE_SGX_LC);
}
static int __init nosgx(char *str)
{
clear_sgx_caps();
return 0;
}
early_param("nosgx", nosgx);
void init_ia32_feat_ctl(struct cpuinfo_x86 *c)
{
bool tboot = tboot_enabled();
bool enable_sgx;
u64 msr;
if (rdmsrl_safe(MSR_IA32_FEAT_CTL, &msr)) {
clear_cpu_cap(c, X86_FEATURE_VMX);
clear_sgx_caps();
return;
}
/*
* Enable SGX if and only if the kernel supports SGX and Launch Control
* is supported, i.e. disable SGX if the LE hash MSRs can't be written.
*/
enable_sgx = cpu_has(c, X86_FEATURE_SGX) &&
cpu_has(c, X86_FEATURE_SGX_LC) &&
IS_ENABLED(CONFIG_X86_SGX);
if (msr & FEAT_CTL_LOCKED)
goto update_caps;
......@@ -124,13 +149,16 @@ void init_ia32_feat_ctl(struct cpuinfo_x86 *c)
msr |= FEAT_CTL_VMX_ENABLED_INSIDE_SMX;
}
if (enable_sgx)
msr |= FEAT_CTL_SGX_ENABLED | FEAT_CTL_SGX_LC_ENABLED;
wrmsrl(MSR_IA32_FEAT_CTL, msr);
update_caps:
set_cpu_cap(c, X86_FEATURE_MSR_IA32_FEAT_CTL);
if (!cpu_has(c, X86_FEATURE_VMX))
return;
goto update_sgx;
if ( (tboot && !(msr & FEAT_CTL_VMX_ENABLED_INSIDE_SMX)) ||
(!tboot && !(msr & FEAT_CTL_VMX_ENABLED_OUTSIDE_SMX))) {
......@@ -143,4 +171,12 @@ void init_ia32_feat_ctl(struct cpuinfo_x86 *c)
init_vmx_capabilities(c);
#endif
}
update_sgx:
if (!(msr & FEAT_CTL_SGX_ENABLED) ||
!(msr & FEAT_CTL_SGX_LC_ENABLED) || !enable_sgx) {
if (enable_sgx)
pr_err_once("SGX disabled by BIOS\n");
clear_sgx_caps();
}
}
obj-y += \
driver.o \
encl.o \
ioctl.o \
main.o
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2016-20 Intel Corporation. */
#include <linux/acpi.h>
#include <linux/miscdevice.h>
#include <linux/mman.h>
#include <linux/security.h>
#include <linux/suspend.h>
#include <asm/traps.h>
#include "driver.h"
#include "encl.h"
u64 sgx_attributes_reserved_mask;
u64 sgx_xfrm_reserved_mask = ~0x3;
u32 sgx_misc_reserved_mask;
static int sgx_open(struct inode *inode, struct file *file)
{
struct sgx_encl *encl;
int ret;
encl = kzalloc(sizeof(*encl), GFP_KERNEL);
if (!encl)
return -ENOMEM;
kref_init(&encl->refcount);
xa_init(&encl->page_array);
mutex_init(&encl->lock);
INIT_LIST_HEAD(&encl->va_pages);
INIT_LIST_HEAD(&encl->mm_list);
spin_lock_init(&encl->mm_lock);
ret = init_srcu_struct(&encl->srcu);
if (ret) {
kfree(encl);
return ret;
}
file->private_data = encl;
return 0;
}
static int sgx_release(struct inode *inode, struct file *file)
{
struct sgx_encl *encl = file->private_data;
struct sgx_encl_mm *encl_mm;
/*
* Drain the remaining mm_list entries. At this point the list contains
* entries for processes, which have closed the enclave file but have
* not exited yet. The processes, which have exited, are gone from the
* list by sgx_mmu_notifier_release().
*/
for ( ; ; ) {
spin_lock(&encl->mm_lock);
if (list_empty(&encl->mm_list)) {
encl_mm = NULL;
} else {
encl_mm = list_first_entry(&encl->mm_list,
struct sgx_encl_mm, list);
list_del_rcu(&encl_mm->list);
}
spin_unlock(&encl->mm_lock);
/* The enclave is no longer mapped by any mm. */
if (!encl_mm)
break;
synchronize_srcu(&encl->srcu);
mmu_notifier_unregister(&encl_mm->mmu_notifier, encl_mm->mm);
kfree(encl_mm);
}
kref_put(&encl->refcount, sgx_encl_release);
return 0;
}
static int sgx_mmap(struct file *file, struct vm_area_struct *vma)
{
struct sgx_encl *encl = file->private_data;
int ret;
ret = sgx_encl_may_map(encl, vma->vm_start, vma->vm_end, vma->vm_flags);
if (ret)
return ret;
ret = sgx_encl_mm_add(encl, vma->vm_mm);
if (ret)
return ret;
vma->vm_ops = &sgx_vm_ops;
vma->vm_flags |= VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP | VM_IO;
vma->vm_private_data = encl;
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 ((flags & MAP_TYPE) == MAP_PRIVATE)
return -EINVAL;
if (flags & MAP_FIXED)
return addr;
return current->mm->get_unmapped_area(file, addr, len, pgoff, flags);
}
#ifdef CONFIG_COMPAT
static long sgx_compat_ioctl(struct file *filep, unsigned int cmd,
unsigned long arg)
{
return sgx_ioctl(filep, cmd, arg);
}
#endif
static const struct file_operations sgx_encl_fops = {
.owner = THIS_MODULE,
.open = sgx_open,
.release = sgx_release,
.unlocked_ioctl = sgx_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = sgx_compat_ioctl,
#endif
.mmap = sgx_mmap,
.get_unmapped_area = sgx_get_unmapped_area,
};
const struct file_operations sgx_provision_fops = {
.owner = THIS_MODULE,
};
static struct miscdevice sgx_dev_enclave = {
.minor = MISC_DYNAMIC_MINOR,
.name = "sgx_enclave",
.nodename = "sgx_enclave",
.fops = &sgx_encl_fops,
};
static struct miscdevice sgx_dev_provision = {
.minor = MISC_DYNAMIC_MINOR,
.name = "sgx_provision",
.nodename = "sgx_provision",
.fops = &sgx_provision_fops,
};
int __init sgx_drv_init(void)
{
unsigned int eax, ebx, ecx, edx;
u64 attr_mask;
u64 xfrm_mask;
int ret;
if (!cpu_feature_enabled(X86_FEATURE_SGX_LC))
return -ENODEV;
cpuid_count(SGX_CPUID, 0, &eax, &ebx, &ecx, &edx);
if (!(eax & 1)) {
pr_err("SGX disabled: SGX1 instruction support not available.\n");
return -ENODEV;
}
sgx_misc_reserved_mask = ~ebx | SGX_MISC_RESERVED_MASK;
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 (cpu_feature_enabled(X86_FEATURE_OSXSAVE)) {
xfrm_mask = (((u64)edx) << 32) + (u64)ecx;
sgx_xfrm_reserved_mask = ~xfrm_mask;
}
ret = misc_register(&sgx_dev_enclave);
if (ret)
return ret;
ret = misc_register(&sgx_dev_provision);
if (ret) {
misc_deregister(&sgx_dev_enclave);
return ret;
}
return 0;
}
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __ARCH_SGX_DRIVER_H__
#define __ARCH_SGX_DRIVER_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 "sgx.h"
#define SGX_EINIT_SPIN_COUNT 20
#define SGX_EINIT_SLEEP_COUNT 50
#define SGX_EINIT_SLEEP_TIME 20
extern u64 sgx_attributes_reserved_mask;
extern u64 sgx_xfrm_reserved_mask;
extern u32 sgx_misc_reserved_mask;
extern const struct file_operations sgx_provision_fops;
long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
int sgx_drv_init(void);
#endif /* __ARCH_X86_SGX_DRIVER_H__ */
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0 */
/**
* Copyright(c) 2016-20 Intel Corporation.
*
* Contains the software defined data structures for enclaves.
*/
#ifndef _X86_ENCL_H
#define _X86_ENCL_H
#include <linux/cpumask.h>
#include <linux/kref.h>
#include <linux/list.h>
#include <linux/mm_types.h>
#include <linux/mmu_notifier.h>
#include <linux/mutex.h>
#include <linux/notifier.h>
#include <linux/srcu.h>
#include <linux/workqueue.h>
#include <linux/xarray.h>
#include "sgx.h"
/* 'desc' bits holding the offset in the VA (version array) page. */
#define SGX_ENCL_PAGE_VA_OFFSET_MASK GENMASK_ULL(11, 3)
/* 'desc' bit marking that the page is being reclaimed. */
#define SGX_ENCL_PAGE_BEING_RECLAIMED BIT(3)
struct sgx_encl_page {
unsigned long desc;
unsigned long vm_max_prot_bits;
struct sgx_epc_page *epc_page;
struct sgx_encl *encl;
struct sgx_va_page *va_page;
};
enum sgx_encl_flags {
SGX_ENCL_IOCTL = BIT(0),
SGX_ENCL_DEBUG = BIT(1),
SGX_ENCL_CREATED = BIT(2),
SGX_ENCL_INITIALIZED = BIT(3),
};
struct sgx_encl_mm {
struct sgx_encl *encl;
struct mm_struct *mm;
struct list_head list;
struct mmu_notifier mmu_notifier;
};
struct sgx_encl {
unsigned long base;
unsigned long size;
unsigned long flags;
unsigned int page_cnt;
unsigned int secs_child_cnt;
struct mutex lock;
struct xarray page_array;
struct sgx_encl_page secs;
unsigned long attributes;
unsigned long attributes_mask;
cpumask_t cpumask;
struct file *backing;
struct kref refcount;
struct list_head va_pages;
unsigned long mm_list_version;
struct list_head mm_list;
spinlock_t mm_lock;
struct srcu_struct srcu;
};
#define SGX_VA_SLOT_COUNT 512
struct sgx_va_page {
struct sgx_epc_page *epc_page;
DECLARE_BITMAP(slots, SGX_VA_SLOT_COUNT);
struct list_head list;
};
struct sgx_backing {
pgoff_t page_index;
struct page *contents;
struct page *pcmd;
unsigned long pcmd_offset;
};
extern const struct vm_operations_struct sgx_vm_ops;
static inline int sgx_encl_find(struct mm_struct *mm, unsigned long addr,
struct vm_area_struct **vma)
{
struct vm_area_struct *result;
result = find_vma(mm, addr);
if (!result || result->vm_ops != &sgx_vm_ops || addr < result->vm_start)
return -EINVAL;
*vma = result;
return 0;
}
int sgx_encl_may_map(struct sgx_encl *encl, unsigned long start,
unsigned long end, unsigned long vm_flags);
void sgx_encl_release(struct kref *ref);
int sgx_encl_mm_add(struct sgx_encl *encl, struct mm_struct *mm);
int sgx_encl_get_backing(struct sgx_encl *encl, unsigned long page_index,
struct sgx_backing *backing);
void sgx_encl_put_backing(struct sgx_backing *backing, bool do_write);
int sgx_encl_test_and_clear_young(struct mm_struct *mm,
struct sgx_encl_page *page);
struct sgx_epc_page *sgx_alloc_va_page(void);
unsigned int sgx_alloc_va_slot(struct sgx_va_page *va_page);
void sgx_free_va_slot(struct sgx_va_page *va_page, unsigned int offset);
bool sgx_va_page_full(struct sgx_va_page *va_page);
#endif /* _X86_ENCL_H */
/* SPDX-License-Identifier: GPL-2.0 */
#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 <asm/traps.h>
#include "sgx.h"
enum sgx_encls_function {
ECREATE = 0x00,
EADD = 0x01,
EINIT = 0x02,
EREMOVE = 0x03,
EDGBRD = 0x04,
EDGBWR = 0x05,
EEXTEND = 0x06,
ELDU = 0x08,
EBLOCK = 0x09,
EPA = 0x0A,
EWB = 0x0B,
ETRACK = 0x0C,
};
/**
* 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 function. */
#define ENCLS_WARN(r, name) { \
do { \
int _r = (r); \
WARN_ONCE(_r, "%s returned %d (0x%x)\n", (name), _r, _r); \
} while (0); \
}
/**
* encls_failed() - Check if an ENCLS function failed
* @ret: the return value of an ENCLS function call
*
* Check if an ENCLS function failed. This happens when the function causes a
* fault that is not caused by an EPCM conflict or when the function returns a
* non-zero value.
*/
static inline bool encls_failed(int ret)
{
if (ret & ENCLS_FAULT_FLAG)
return ENCLS_TRAPNR(ret) != X86_TRAP_PF;
return !!ret;
}
/**
* __encls_ret_N - encode an ENCLS function that returns an error code in EAX
* @rax: function number
* @inputs: asm inputs for the function
*
* Emit assembly for an ENCLS function that returns an error code, e.g. EREMOVE.
* And because SGX isn't complex enough as it is, function 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 function that doesn't return an error code
* @rax: function number
* @rbx_out: optional output variable
* @inputs: asm inputs for the function
*
* Emit assembly for an ENCLS function 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 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(ECREATE, pginfo, secs);
}
static inline int __eextend(void *secs, void *addr)
{
return __encls_2(EEXTEND, secs, addr);
}
static inline int __eadd(struct sgx_pageinfo *pginfo, void *addr)
{
return __encls_2(EADD, pginfo, addr);
}
static inline int __einit(void *sigstruct, void *token, void *secs)
{
return __encls_ret_3(EINIT, sigstruct, secs, token);
}
static inline int __eremove(void *addr)
{
return __encls_ret_1(EREMOVE, addr);
}
static inline int __edbgwr(void *addr, unsigned long *data)
{
return __encls_2(EDGBWR, *data, addr);
}
static inline int __edbgrd(void *addr, unsigned long *data)
{
return __encls_1_1(EDGBRD, *data, addr);
}
static inline int __etrack(void *addr)
{
return __encls_ret_1(ETRACK, addr);
}
static inline int __eldu(struct sgx_pageinfo *pginfo, void *addr,
void *va)
{
return __encls_ret_3(ELDU, pginfo, addr, va);
}
static inline int __eblock(void *addr)
{
return __encls_ret_1(EBLOCK, addr);
}
static inline int __epa(void *addr)
{
unsigned long rbx = SGX_PAGE_TYPE_VA;
return __encls_2(EPA, rbx, addr);
}
static inline int __ewb(struct sgx_pageinfo *pginfo, void *addr,
void *va)
{
return __encls_ret_3(EWB, pginfo, addr, va);
}
#endif /* _X86_ENCLS_H */
This diff is collapsed.
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0 */
#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 "arch.h"
#undef pr_fmt
#define pr_fmt(fmt) "sgx: " fmt
#define SGX_MAX_EPC_SECTIONS 8
#define SGX_EEXTEND_BLOCK_SIZE 256
#define SGX_NR_TO_SCAN 16
#define SGX_NR_LOW_PAGES 32
#define SGX_NR_HIGH_PAGES 64
/* Pages, which are being tracked by the page reclaimer. */
#define SGX_EPC_PAGE_RECLAIMER_TRACKED BIT(0)
struct sgx_epc_page {
unsigned int section;
unsigned int flags;
struct sgx_encl_page *owner;
struct list_head list;
};
/*
* 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.
*
* 'lock' must be held before accessing 'page_list' or 'free_cnt'.
*/
struct sgx_epc_section {
unsigned long phys_addr;
void *virt_addr;
struct sgx_epc_page *pages;
spinlock_t lock;
struct list_head page_list;
unsigned long free_cnt;
/*
* Pages which need EREMOVE run on them before they can be
* used. Only safe to be accessed in ksgxd and init code.
* Not protected by locks.
*/
struct list_head init_laundry_list;
};
extern struct sgx_epc_section sgx_epc_sections[SGX_MAX_EPC_SECTIONS];
static inline unsigned long sgx_get_epc_phys_addr(struct sgx_epc_page *page)
{
struct sgx_epc_section *section = &sgx_epc_sections[page->section];
unsigned long index;
index = ((unsigned long)page - (unsigned long)section->pages) / sizeof(*page);
return section->phys_addr + index * PAGE_SIZE;
}
static inline void *sgx_get_epc_virt_addr(struct sgx_epc_page *page)
{
struct sgx_epc_section *section = &sgx_epc_sections[page->section];
unsigned long index;
index = ((unsigned long)page - (unsigned long)section->pages) / sizeof(*page);
return section->virt_addr + index * PAGE_SIZE;
}
struct sgx_epc_page *__sgx_alloc_epc_page(void);
void sgx_free_epc_page(struct sgx_epc_page *page);
void sgx_mark_page_reclaimable(struct sgx_epc_page *page);
int sgx_unmark_page_reclaimable(struct sgx_epc_page *page);
struct sgx_epc_page *sgx_alloc_epc_page(void *owner, bool reclaim);
#endif /* _X86_SGX_H */
......@@ -60,6 +60,7 @@
#include <asm/umip.h>
#include <asm/insn.h>
#include <asm/insn-eval.h>
#include <asm/vdso.h>
#ifdef CONFIG_X86_64
#include <asm/x86_init.h>
......@@ -117,6 +118,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;
}
/*
......@@ -550,6 +554,9 @@ DEFINE_IDTENTRY_ERRORCODE(exc_general_protection)
tsk->thread.error_code = error_code;
tsk->thread.trap_nr = X86_TRAP_GP;
if (fixup_vdso_exception(regs, X86_TRAP_GP, error_code, 0))
return;
show_signal(tsk, SIGSEGV, "", desc, regs, error_code);
force_sig(SIGSEGV);
goto exit;
......@@ -1048,6 +1055,9 @@ static void math_error(struct pt_regs *regs, int trapnr)
if (!si_code)
goto exit;
if (fixup_vdso_exception(regs, trapnr, 0, 0))
return;
force_sig_fault(SIGFPE, si_code,
(void __user *)uprobe_get_trap_addr(regs));
exit:
......
......@@ -30,6 +30,7 @@
#include <asm/cpu_entry_area.h> /* exception stack */
#include <asm/pgtable_areas.h> /* VMALLOC_START, ... */
#include <asm/kvm_para.h> /* kvm_handle_async_pf */
#include <asm/vdso.h> /* fixup_vdso_exception() */
#define CREATE_TRACE_POINTS
#include <asm/trace/exceptions.h>
......@@ -602,11 +603,9 @@ pgtable_bad(struct pt_regs *regs, unsigned long error_code,
oops_end(flags, regs, sig);
}
static void set_signal_archinfo(unsigned long address,
unsigned long error_code)
static void sanitize_error_code(unsigned long address,
unsigned long *error_code)
{
struct task_struct *tsk = current;
/*
* To avoid leaking information about the kernel page
* table layout, pretend that user-mode accesses to
......@@ -617,7 +616,13 @@ static void set_signal_archinfo(unsigned long address,
* information and does not appear to cause any problems.
*/
if (address >= TASK_SIZE_MAX)
error_code |= X86_PF_PROT;
*error_code |= X86_PF_PROT;
}
static void set_signal_archinfo(unsigned long address,
unsigned long error_code)
{
struct task_struct *tsk = current;
tsk->thread.trap_nr = X86_TRAP_PF;
tsk->thread.error_code = error_code | X86_PF_USER;
......@@ -658,6 +663,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. */
......@@ -806,13 +813,10 @@ __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 (fixup_vdso_exception(regs, X86_TRAP_PF, error_code, address))
return;
if (likely(show_unhandled_signals))
show_signal_msg(regs, error_code, address, tsk);
......@@ -931,6 +935,11 @@ 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);
if (fixup_vdso_exception(regs, X86_TRAP_PF, error_code, address))
return;
set_signal_archinfo(address, error_code);
#ifdef CONFIG_MEMORY_FAILURE
......@@ -1101,6 +1110,18 @@ access_error(unsigned long error_code, struct vm_area_struct *vma)
if (error_code & X86_PF_PK)
return 1;
/*
* SGX hardware blocked the access. This usually happens
* when the enclave memory contents have been destroyed, like
* after a suspend/resume cycle. In any case, the kernel can't
* fix the cause of the fault. Handle the fault as an access
* error even in cases where no actual access violation
* occurred. This allows userspace to rebuild the enclave in
* response to the signal.
*/
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
......
......@@ -559,6 +559,13 @@ struct vm_operations_struct {
void (*close)(struct vm_area_struct * area);
int (*split)(struct vm_area_struct * area, unsigned long addr);
int (*mremap)(struct vm_area_struct * area);
/*
* Called by mprotect() to make driver-specific permission
* checks before mprotect() is finalised. The VMA must not
* be modified. Returns 0 if eprotect() can proceed.
*/
int (*mprotect)(struct vm_area_struct *vma, unsigned long start,
unsigned long end, unsigned long newflags);
vm_fault_t (*fault)(struct vm_fault *vmf);
vm_fault_t (*huge_fault)(struct vm_fault *vmf,
enum page_entry_size pe_size);
......
......@@ -616,9 +616,16 @@ static int do_mprotect_pkey(unsigned long start, size_t len,
tmp = vma->vm_end;
if (tmp > end)
tmp = end;
if (vma->vm_ops && vma->vm_ops->mprotect)
error = vma->vm_ops->mprotect(vma, nstart, tmp, newflags);
if (error)
goto out;
error = mprotect_fixup(vma, &prev, nstart, tmp, newflags);
if (error)
goto out;
nstart = tmp;
if (nstart < prev->vm_end)
......
......@@ -50,6 +50,7 @@ TARGETS += openat2
TARGETS += rseq
TARGETS += rtc
TARGETS += seccomp
TARGETS += sgx
TARGETS += sigaltstack
TARGETS += size
TARGETS += sparc64
......
top_srcdir = ../../../..
include ../lib.mk
.PHONY: all clean
CAN_BUILD_X86_64 := $(shell ../x86/check_cc.sh $(CC) \
../x86/trivial_64bit_program.c)
ifndef OBJCOPY
OBJCOPY := $(CROSS_COMPILE)objcopy
endif
INCLUDES := -I$(top_srcdir)/tools/include
HOST_CFLAGS := -Wall -Werror -g $(INCLUDES) -fPIC -z noexecstack
ENCL_CFLAGS := -Wall -Werror -static -nostdlib -nostartfiles -fPIC \
-fno-stack-protector -mrdrnd $(INCLUDES)
TEST_CUSTOM_PROGS := $(OUTPUT)/test_sgx
ifeq ($(CAN_BUILD_X86_64), 1)
all: $(TEST_CUSTOM_PROGS) $(OUTPUT)/test_encl.elf
endif
$(OUTPUT)/test_sgx: $(OUTPUT)/main.o \
$(OUTPUT)/load.o \
$(OUTPUT)/sigstruct.o \
$(OUTPUT)/call.o \
$(OUTPUT)/sign_key.o
$(CC) $(HOST_CFLAGS) -o $@ $^ -lcrypto
$(OUTPUT)/main.o: main.c
$(CC) $(HOST_CFLAGS) -c $< -o $@
$(OUTPUT)/load.o: load.c
$(CC) $(HOST_CFLAGS) -c $< -o $@
$(OUTPUT)/sigstruct.o: sigstruct.c
$(CC) $(HOST_CFLAGS) -c $< -o $@
$(OUTPUT)/call.o: call.S
$(CC) $(HOST_CFLAGS) -c $< -o $@
$(OUTPUT)/sign_key.o: sign_key.S
$(CC) $(HOST_CFLAGS) -c $< -o $@
$(OUTPUT)/test_encl.elf: test_encl.lds test_encl.c test_encl_bootstrap.S
$(CC) $(ENCL_CFLAGS) -T $^ -o $@
EXTRA_CLEAN := \
$(OUTPUT)/test_encl.elf \
$(OUTPUT)/load.o \
$(OUTPUT)/call.o \
$(OUTPUT)/main.o \
$(OUTPUT)/sigstruct.o \
$(OUTPUT)/test_sgx \
$(OUTPUT)/test_sgx.o \
/* SPDX-License-Identifier: GPL-2.0 */
/**
* Copyright(c) 2016-20 Intel Corporation.
*/
.text
.global sgx_call_vdso
sgx_call_vdso:
.cfi_startproc
push %r15
.cfi_adjust_cfa_offset 8
.cfi_rel_offset %r15, 0
push %r14
.cfi_adjust_cfa_offset 8
.cfi_rel_offset %r14, 0
push %r13
.cfi_adjust_cfa_offset 8
.cfi_rel_offset %r13, 0
push %r12
.cfi_adjust_cfa_offset 8
.cfi_rel_offset %r12, 0
push %rbx
.cfi_adjust_cfa_offset 8
.cfi_rel_offset %rbx, 0
push $0
.cfi_adjust_cfa_offset 8
push 0x38(%rsp)
.cfi_adjust_cfa_offset 8
call *eenter(%rip)
add $0x10, %rsp
.cfi_adjust_cfa_offset -0x10
pop %rbx
.cfi_adjust_cfa_offset -8
pop %r12
.cfi_adjust_cfa_offset -8
pop %r13
.cfi_adjust_cfa_offset -8
pop %r14
.cfi_adjust_cfa_offset -8
pop %r15
.cfi_adjust_cfa_offset -8
ret
.cfi_endproc
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright(c) 2016-20 Intel Corporation.
*/
#ifndef DEFINES_H
#define DEFINES_H
#include <stdint.h>
#define PAGE_SIZE 4096
#define PAGE_MASK (~(PAGE_SIZE - 1))
#define __aligned(x) __attribute__((__aligned__(x)))
#define __packed __attribute__((packed))
#include "../../../../arch/x86/kernel/cpu/sgx/arch.h"
#include "../../../../arch/x86/include/asm/enclu.h"
#include "../../../../arch/x86/include/uapi/asm/sgx.h"
#endif /* DEFINES_H */
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2016-20 Intel Corporation. */
#include <assert.h>
#include <elf.h>
#include <errno.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 <sys/types.h>
#include "defines.h"
#include "main.h"
void encl_delete(struct encl *encl)
{
if (encl->encl_base)
munmap((void *)encl->encl_base, encl->encl_size);
if (encl->bin)
munmap(encl->bin, encl->bin_size);
if (encl->fd)
close(encl->fd);
if (encl->segment_tbl)
free(encl->segment_tbl);
memset(encl, 0, sizeof(*encl));
}
static bool encl_map_bin(const char *path, struct encl *encl)
{
struct stat sb;
void *bin;
int ret;
int fd;
fd = open(path, O_RDONLY);
if (fd == -1) {
perror("open()");
return false;
}
ret = stat(path, &sb);
if (ret) {
perror("stat()");
goto err;
}
bin = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (bin == MAP_FAILED) {
perror("mmap()");
goto err;
}
encl->bin = bin;
encl->bin_size = sb.st_size;
close(fd);
return true;
err:
close(fd);
return false;
}
static bool encl_ioc_create(struct encl *encl)
{
struct sgx_secs *secs = &encl->secs;
struct sgx_enclave_create ioc;
int rc;
assert(encl->encl_base != 0);
memset(secs, 0, sizeof(*secs));
secs->ssa_frame_size = 1;
secs->attributes = SGX_ATTR_MODE64BIT;
secs->xfrm = 3;
secs->base = encl->encl_base;
secs->size = encl->encl_size;
ioc.src = (unsigned long)secs;
rc = ioctl(encl->fd, SGX_IOC_ENCLAVE_CREATE, &ioc);
if (rc) {
fprintf(stderr, "SGX_IOC_ENCLAVE_CREATE failed: errno=%d\n",
errno);
munmap((void *)secs->base, encl->encl_size);
return false;
}
return true;
}
static bool encl_ioc_add_pages(struct encl *encl, struct encl_segment *seg)
{
struct sgx_enclave_add_pages ioc;
struct sgx_secinfo secinfo;
int rc;
memset(&secinfo, 0, sizeof(secinfo));
secinfo.flags = seg->flags;
ioc.src = (uint64_t)encl->src + seg->offset;
ioc.offset = seg->offset;
ioc.length = seg->size;
ioc.secinfo = (unsigned long)&secinfo;
ioc.flags = SGX_PAGE_MEASURE;
rc = ioctl(encl->fd, SGX_IOC_ENCLAVE_ADD_PAGES, &ioc);
if (rc < 0) {
fprintf(stderr, "SGX_IOC_ENCLAVE_ADD_PAGES failed: errno=%d.\n",
errno);
return false;
}
return true;
}
bool encl_load(const char *path, struct encl *encl)
{
Elf64_Phdr *phdr_tbl;
off_t src_offset;
Elf64_Ehdr *ehdr;
int i, j;
int ret;
memset(encl, 0, sizeof(*encl));
ret = open("/dev/sgx_enclave", O_RDWR);
if (ret < 0) {
fprintf(stderr, "Unable to open /dev/sgx_enclave\n");
goto err;
}
encl->fd = ret;
if (!encl_map_bin(path, encl))
goto err;
ehdr = encl->bin;
phdr_tbl = encl->bin + ehdr->e_phoff;
for (i = 0; i < ehdr->e_phnum; i++) {
Elf64_Phdr *phdr = &phdr_tbl[i];
if (phdr->p_type == PT_LOAD)
encl->nr_segments++;
}
encl->segment_tbl = calloc(encl->nr_segments,
sizeof(struct encl_segment));
if (!encl->segment_tbl)
goto err;
for (i = 0, j = 0; i < ehdr->e_phnum; i++) {
Elf64_Phdr *phdr = &phdr_tbl[i];
unsigned int flags = phdr->p_flags;
struct encl_segment *seg;
if (phdr->p_type != PT_LOAD)
continue;
seg = &encl->segment_tbl[j];
if (!!(flags & ~(PF_R | PF_W | PF_X))) {
fprintf(stderr,
"%d has invalid segment flags 0x%02x.\n", i,
phdr->p_flags);
goto err;
}
if (j == 0 && flags != (PF_R | PF_W)) {
fprintf(stderr,
"TCS has invalid segment flags 0x%02x.\n",
phdr->p_flags);
goto err;
}
if (j == 0) {
src_offset = phdr->p_offset & PAGE_MASK;
seg->prot = PROT_READ | PROT_WRITE;
seg->flags = SGX_PAGE_TYPE_TCS << 8;
} else {
seg->prot = (phdr->p_flags & PF_R) ? PROT_READ : 0;
seg->prot |= (phdr->p_flags & PF_W) ? PROT_WRITE : 0;
seg->prot |= (phdr->p_flags & PF_X) ? PROT_EXEC : 0;
seg->flags = (SGX_PAGE_TYPE_REG << 8) | seg->prot;
}
seg->offset = (phdr->p_offset & PAGE_MASK) - src_offset;
seg->size = (phdr->p_filesz + PAGE_SIZE - 1) & PAGE_MASK;
printf("0x%016lx 0x%016lx 0x%02x\n", seg->offset, seg->size,
seg->prot);
j++;
}
assert(j == encl->nr_segments);
encl->src = encl->bin + src_offset;
encl->src_size = encl->segment_tbl[j - 1].offset +
encl->segment_tbl[j - 1].size;
for (encl->encl_size = 4096; encl->encl_size < encl->src_size; )
encl->encl_size <<= 1;
return true;
err:
encl_delete(encl);
return false;
}
static bool encl_map_area(struct encl *encl)
{
size_t encl_size = encl->encl_size;
void *area;
area = mmap(NULL, encl_size * 2, PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (area == MAP_FAILED) {
perror("mmap");
return false;
}
encl->encl_base = ((uint64_t)area + encl_size - 1) & ~(encl_size - 1);
munmap(area, encl->encl_base - (uint64_t)area);
munmap((void *)(encl->encl_base + encl_size),
(uint64_t)area + encl_size - encl->encl_base);
return true;
}
bool encl_build(struct encl *encl)
{
struct sgx_enclave_init ioc;
int ret;
int i;
if (!encl_map_area(encl))
return false;
if (!encl_ioc_create(encl))
return false;
/*
* Pages must be added before mapping VMAs because their permissions
* cap the VMA permissions.
*/
for (i = 0; i < encl->nr_segments; i++) {
struct encl_segment *seg = &encl->segment_tbl[i];
if (!encl_ioc_add_pages(encl, seg))
return false;
}
ioc.sigstruct = (uint64_t)&encl->sigstruct;
ret = ioctl(encl->fd, SGX_IOC_ENCLAVE_INIT, &ioc);
if (ret) {
fprintf(stderr, "SGX_IOC_ENCLAVE_INIT failed: errno=%d\n",
errno);
return false;
}
return true;
}
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2016-20 Intel Corporation. */
#include <elf.h>
#include <errno.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 <sys/types.h>
#include "defines.h"
#include "main.h"
#include "../kselftest.h"
static const uint64_t MAGIC = 0x1122334455667788ULL;
vdso_sgx_enter_enclave_t eenter;
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;
}
bool report_results(struct sgx_enclave_run *run, int ret, uint64_t result,
const char *test)
{
bool valid = true;
if (ret) {
printf("FAIL: %s() returned: %d\n", test, ret);
valid = false;
}
if (run->function != EEXIT) {
printf("FAIL: %s() function, expected: %u, got: %u\n", test, EEXIT,
run->function);
valid = false;
}
if (result != MAGIC) {
printf("FAIL: %s(), expected: 0x%lx, got: 0x%lx\n", test, MAGIC,
result);
valid = false;
}
if (run->user_data) {
printf("FAIL: %s() user data, expected: 0x0, got: 0x%llx\n",
test, run->user_data);
valid = false;
}
return valid;
}
static int user_handler(long rdi, long rsi, long rdx, long ursp, long r8, long r9,
struct sgx_enclave_run *run)
{
run->user_data = 0;
return 0;
}
int main(int argc, char *argv[], char *envp[])
{
struct sgx_enclave_run run;
struct vdso_symtab symtab;
Elf64_Sym *eenter_sym;
uint64_t result = 0;
struct encl encl;
unsigned int i;
void *addr;
int ret;
memset(&run, 0, sizeof(run));
if (!encl_load("test_encl.elf", &encl)) {
encl_delete(&encl);
ksft_exit_skip("cannot load enclaves\n");
}
if (!encl_measure(&encl))
goto err;
if (!encl_build(&encl))
goto err;
/*
* An enclave consumer only must do this.
*/
for (i = 0; i < encl.nr_segments; i++) {
struct encl_segment *seg = &encl.segment_tbl[i];
addr = mmap((void *)encl.encl_base + seg->offset, seg->size,
seg->prot, MAP_SHARED | MAP_FIXED, encl.fd, 0);
if (addr == MAP_FAILED) {
fprintf(stderr, "mmap() failed, errno=%d.\n", errno);
exit(KSFT_FAIL);
}
}
memset(&run, 0, sizeof(run));
run.tcs = encl.encl_base;
addr = vdso_get_base_addr(envp);
if (!addr)
goto err;
if (!vdso_get_symtab(addr, &symtab))
goto err;
eenter_sym = vdso_symtab_get(&symtab, "__vdso_sgx_enter_enclave");
if (!eenter_sym)
goto err;
eenter = addr + eenter_sym->st_value;
ret = sgx_call_vdso((void *)&MAGIC, &result, 0, EENTER, NULL, NULL, &run);
if (!report_results(&run, ret, result, "sgx_call_vdso"))
goto err;
/* Invoke the vDSO directly. */
result = 0;
ret = eenter((unsigned long)&MAGIC, (unsigned long)&result, 0, EENTER,
0, 0, &run);
if (!report_results(&run, ret, result, "eenter"))
goto err;
/* And with an exit handler. */
run.user_handler = (__u64)user_handler;
run.user_data = 0xdeadbeef;
ret = eenter((unsigned long)&MAGIC, (unsigned long)&result, 0, EENTER,
0, 0, &run);
if (!report_results(&run, ret, result, "user_handler"))
goto err;
printf("SUCCESS\n");
encl_delete(&encl);
exit(KSFT_PASS);
err:
encl_delete(&encl);
exit(KSFT_FAIL);
}
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0 */
/**
* Copyright(c) 2016-20 Intel Corporation.
*/
.section ".rodata", "a"
sign_key:
.globl sign_key
.incbin "sign_key.pem"
sign_key_end:
.globl sign_key_end
This diff is collapsed.
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2016-20 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);
}
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment