Commit 9e2f4023 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'x86_sgx_for_v6.0-2022-08-03.1' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull x86 SGX updates from Dave Hansen:
 "A set of x86/sgx changes focused on implementing the "SGX2" features,
  plus a minor cleanup:

   - SGX2 ISA support which makes enclave memory management much more
     dynamic. For instance, enclaves can now change enclave page
     permissions on the fly.

   - Removal of an unused structure member"

* tag 'x86_sgx_for_v6.0-2022-08-03.1' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (32 commits)
  x86/sgx: Drop 'page_index' from sgx_backing
  selftests/sgx: Page removal stress test
  selftests/sgx: Test reclaiming of untouched page
  selftests/sgx: Test invalid access to removed enclave page
  selftests/sgx: Test faulty enclave behavior
  selftests/sgx: Test complete changing of page type flow
  selftests/sgx: Introduce TCS initialization enclave operation
  selftests/sgx: Introduce dynamic entry point
  selftests/sgx: Test two different SGX2 EAUG flows
  selftests/sgx: Add test for TCS page permission changes
  selftests/sgx: Add test for EPCM permission changes
  Documentation/x86: Introduce enclave runtime management section
  x86/sgx: Free up EPC pages directly to support large page ranges
  x86/sgx: Support complete page removal
  x86/sgx: Support modifying SGX page type
  x86/sgx: Tighten accessible memory range after enclave initialization
  x86/sgx: Support adding of pages to an initialized enclave
  x86/sgx: Support restricting of enclave page permissions
  x86/sgx: Support VA page allocation without reclaiming
  x86/sgx: Export sgx_encl_page_alloc()
  ...
parents 3bd6e585 e0a5915f
......@@ -100,6 +100,21 @@ pages and establish enclave page permissions.
sgx_ioc_enclave_init
sgx_ioc_enclave_provision
Enclave runtime management
--------------------------
Systems supporting SGX2 additionally support changes to initialized
enclaves: modifying enclave page permissions and type, and dynamically
adding and removing of enclave pages. When an enclave accesses an address
within its address range that does not have a backing page then a new
regular page will be dynamically added to the enclave. The enclave is
still required to run EACCEPT on the new page before it can be used.
.. kernel-doc:: arch/x86/kernel/cpu/sgx/ioctl.c
:functions: sgx_ioc_enclave_restrict_permissions
sgx_ioc_enclave_modify_types
sgx_ioc_enclave_remove_pages
Enclave vDSO
------------
......
......@@ -65,17 +65,22 @@ enum sgx_encls_function {
/**
* enum sgx_return_code - The return code type for ENCLS, ENCLU and ENCLV
* %SGX_EPC_PAGE_CONFLICT: Page is being written by other ENCLS function.
* %SGX_NOT_TRACKED: Previous ETRACK's shootdown sequence has not
* been completed yet.
* %SGX_CHILD_PRESENT SECS has child pages present in the EPC.
* %SGX_INVALID_EINITTOKEN: EINITTOKEN is invalid and enclave signer's
* public key does not match IA32_SGXLEPUBKEYHASH.
* %SGX_PAGE_NOT_MODIFIABLE: The EPC page cannot be modified because it
* is in the PENDING or MODIFIED state.
* %SGX_UNMASKED_EVENT: An unmasked event, e.g. INTR, was received
*/
enum sgx_return_code {
SGX_EPC_PAGE_CONFLICT = 7,
SGX_NOT_TRACKED = 11,
SGX_CHILD_PRESENT = 13,
SGX_INVALID_EINITTOKEN = 16,
SGX_PAGE_NOT_MODIFIABLE = 20,
SGX_UNMASKED_EVENT = 128,
};
......@@ -234,6 +239,9 @@ struct sgx_pageinfo {
* %SGX_PAGE_TYPE_REG: a regular page
* %SGX_PAGE_TYPE_VA: a VA page
* %SGX_PAGE_TYPE_TRIM: a page in trimmed state
*
* Make sure when making changes to this enum that its values can still fit
* in the bitfield within &struct sgx_encl_page
*/
enum sgx_page_type {
SGX_PAGE_TYPE_SECS,
......
......@@ -29,6 +29,12 @@ enum sgx_page_flags {
_IOW(SGX_MAGIC, 0x03, struct sgx_enclave_provision)
#define SGX_IOC_VEPC_REMOVE_ALL \
_IO(SGX_MAGIC, 0x04)
#define SGX_IOC_ENCLAVE_RESTRICT_PERMISSIONS \
_IOWR(SGX_MAGIC, 0x05, struct sgx_enclave_restrict_permissions)
#define SGX_IOC_ENCLAVE_MODIFY_TYPES \
_IOWR(SGX_MAGIC, 0x06, struct sgx_enclave_modify_types)
#define SGX_IOC_ENCLAVE_REMOVE_PAGES \
_IOWR(SGX_MAGIC, 0x07, struct sgx_enclave_remove_pages)
/**
* struct sgx_enclave_create - parameter structure for the
......@@ -76,6 +82,62 @@ struct sgx_enclave_provision {
__u64 fd;
};
/**
* struct sgx_enclave_restrict_permissions - parameters for ioctl
* %SGX_IOC_ENCLAVE_RESTRICT_PERMISSIONS
* @offset: starting page offset (page aligned relative to enclave base
* address defined in SECS)
* @length: length of memory (multiple of the page size)
* @permissions:new permission bits for pages in range described by @offset
* and @length
* @result: (output) SGX result code of ENCLS[EMODPR] function
* @count: (output) bytes successfully changed (multiple of page size)
*/
struct sgx_enclave_restrict_permissions {
__u64 offset;
__u64 length;
__u64 permissions;
__u64 result;
__u64 count;
};
/**
* struct sgx_enclave_modify_types - parameters for ioctl
* %SGX_IOC_ENCLAVE_MODIFY_TYPES
* @offset: starting page offset (page aligned relative to enclave base
* address defined in SECS)
* @length: length of memory (multiple of the page size)
* @page_type: new type for pages in range described by @offset and @length
* @result: (output) SGX result code of ENCLS[EMODT] function
* @count: (output) bytes successfully changed (multiple of page size)
*/
struct sgx_enclave_modify_types {
__u64 offset;
__u64 length;
__u64 page_type;
__u64 result;
__u64 count;
};
/**
* struct sgx_enclave_remove_pages - %SGX_IOC_ENCLAVE_REMOVE_PAGES parameters
* @offset: starting page offset (page aligned relative to enclave base
* address defined in SECS)
* @length: length of memory (multiple of the page size)
* @count: (output) bytes successfully changed (multiple of page size)
*
* Regular (PT_REG) or TCS (PT_TCS) can be removed from an initialized
* enclave if the system supports SGX2. First, the %SGX_IOC_ENCLAVE_MODIFY_TYPES
* ioctl() should be used to change the page type to PT_TRIM. After that
* succeeds ENCLU[EACCEPT] should be run from within the enclave and then
* %SGX_IOC_ENCLAVE_REMOVE_PAGES can be used to complete the page removal.
*/
struct sgx_enclave_remove_pages {
__u64 offset;
__u64 length;
__u64 count;
};
struct sgx_enclave_run;
/**
......
This diff is collapsed.
......@@ -27,7 +27,8 @@
struct sgx_encl_page {
unsigned long desc;
unsigned long vm_max_prot_bits;
unsigned long vm_max_prot_bits:8;
enum sgx_page_type type:16;
struct sgx_epc_page *epc_page;
struct sgx_encl *encl;
struct sgx_va_page *va_page;
......@@ -78,7 +79,6 @@ struct sgx_va_page {
};
struct sgx_backing {
pgoff_t page_index;
struct page *contents;
struct page *pcmd;
unsigned long pcmd_offset;
......@@ -106,6 +106,7 @@ int sgx_encl_may_map(struct sgx_encl *encl, unsigned long start,
bool current_is_ksgxd(void);
void sgx_encl_release(struct kref *ref);
int sgx_encl_mm_add(struct sgx_encl *encl, struct mm_struct *mm);
const cpumask_t *sgx_encl_cpumask(struct sgx_encl *encl);
int sgx_encl_lookup_backing(struct sgx_encl *encl, unsigned long page_index,
struct sgx_backing *backing);
int sgx_encl_alloc_backing(struct sgx_encl *encl, unsigned long page_index,
......@@ -113,11 +114,18 @@ int sgx_encl_alloc_backing(struct sgx_encl *encl, unsigned long page_index,
void sgx_encl_put_backing(struct sgx_backing *backing);
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);
struct sgx_encl_page *sgx_encl_page_alloc(struct sgx_encl *encl,
unsigned long offset,
u64 secinfo_flags);
void sgx_zap_enclave_ptes(struct sgx_encl *encl, unsigned long addr);
struct sgx_epc_page *sgx_alloc_va_page(bool reclaim);
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);
void sgx_encl_free_epc_page(struct sgx_epc_page *page);
struct sgx_encl_page *sgx_encl_load_page(struct sgx_encl *encl,
unsigned long addr);
struct sgx_va_page *sgx_encl_grow(struct sgx_encl *encl, bool reclaim);
void sgx_encl_shrink(struct sgx_encl *encl, struct sgx_va_page *va_page);
#endif /* _X86_ENCL_H */
......@@ -136,57 +136,71 @@ static inline bool encls_failed(int ret)
ret; \
})
/* Initialize an EPC page into an SGX Enclave Control Structure (SECS) page. */
static inline int __ecreate(struct sgx_pageinfo *pginfo, void *secs)
{
return __encls_2(ECREATE, pginfo, secs);
}
/* Hash a 256 byte region of an enclave page to SECS:MRENCLAVE. */
static inline int __eextend(void *secs, void *addr)
{
return __encls_2(EEXTEND, secs, addr);
}
/*
* Associate an EPC page to an enclave either as a REG or TCS page
* populated with the provided data.
*/
static inline int __eadd(struct sgx_pageinfo *pginfo, void *addr)
{
return __encls_2(EADD, pginfo, addr);
}
/* Finalize enclave build, initialize enclave for user code execution. */
static inline int __einit(void *sigstruct, void *token, void *secs)
{
return __encls_ret_3(EINIT, sigstruct, secs, token);
}
/* Disassociate EPC page from its enclave and mark it as unused. */
static inline int __eremove(void *addr)
{
return __encls_ret_1(EREMOVE, addr);
}
/* Copy data to an EPC page belonging to a debug enclave. */
static inline int __edbgwr(void *addr, unsigned long *data)
{
return __encls_2(EDGBWR, *data, addr);
}
/* Copy data from an EPC page belonging to a debug enclave. */
static inline int __edbgrd(void *addr, unsigned long *data)
{
return __encls_1_1(EDGBRD, *data, addr);
}
/* Track that software has completed the required TLB address clears. */
static inline int __etrack(void *addr)
{
return __encls_ret_1(ETRACK, addr);
}
/* Load, verify, and unblock an EPC page. */
static inline int __eldu(struct sgx_pageinfo *pginfo, void *addr,
void *va)
{
return __encls_ret_3(ELDU, pginfo, addr, va);
}
/* Make EPC page inaccessible to enclave, ready to be written to memory. */
static inline int __eblock(void *addr)
{
return __encls_ret_1(EBLOCK, addr);
}
/* Initialize an EPC page into a Version Array (VA) page. */
static inline int __epa(void *addr)
{
unsigned long rbx = SGX_PAGE_TYPE_VA;
......@@ -194,10 +208,29 @@ static inline int __epa(void *addr)
return __encls_2(EPA, rbx, addr);
}
/* Invalidate an EPC page and write it out to main memory. */
static inline int __ewb(struct sgx_pageinfo *pginfo, void *addr,
void *va)
{
return __encls_ret_3(EWB, pginfo, addr, va);
}
/* Restrict the EPCM permissions of an EPC page. */
static inline int __emodpr(struct sgx_secinfo *secinfo, void *addr)
{
return __encls_ret_2(EMODPR, secinfo, addr);
}
/* Change the type of an EPC page. */
static inline int __emodt(struct sgx_secinfo *secinfo, void *addr)
{
return __encls_ret_2(EMODT, secinfo, addr);
}
/* Zero a page of EPC memory and add it to an initialized enclave. */
static inline int __eaug(struct sgx_pageinfo *pginfo, void *addr)
{
return __encls_2(EAUG, pginfo, addr);
}
#endif /* _X86_ENCLS_H */
This diff is collapsed.
......@@ -137,36 +137,9 @@ static void sgx_reclaimer_block(struct sgx_epc_page *epc_page)
struct sgx_encl_page *page = epc_page->owner;
unsigned long addr = page->desc & PAGE_MASK;
struct sgx_encl *encl = page->encl;
unsigned long mm_list_version;
struct sgx_encl_mm *encl_mm;
struct vm_area_struct *vma;
int idx, ret;
do {
mm_list_version = encl->mm_list_version;
/* Pairs with smp_rmb() in sgx_encl_mm_add(). */
smp_rmb();
idx = srcu_read_lock(&encl->srcu);
list_for_each_entry_rcu(encl_mm, &encl->mm_list, list) {
if (!mmget_not_zero(encl_mm->mm))
continue;
mmap_read_lock(encl_mm->mm);
ret = sgx_encl_find(encl_mm->mm, addr, &vma);
if (!ret && encl == vma->vm_private_data)
zap_vma_ptes(vma, addr, PAGE_SIZE);
mmap_read_unlock(encl_mm->mm);
mmput_async(encl_mm->mm);
}
int ret;
srcu_read_unlock(&encl->srcu, idx);
} while (unlikely(encl->mm_list_version != mm_list_version));
sgx_zap_enclave_ptes(encl, addr);
mutex_lock(&encl->lock);
......@@ -201,39 +174,10 @@ static int __sgx_encl_ewb(struct sgx_epc_page *epc_page, void *va_slot,
return ret;
}
static void sgx_ipi_cb(void *info)
void sgx_ipi_cb(void *info)
{
}
static const cpumask_t *sgx_encl_ewb_cpumask(struct sgx_encl *encl)
{
cpumask_t *cpumask = &encl->cpumask;
struct sgx_encl_mm *encl_mm;
int idx;
/*
* Can race with sgx_encl_mm_add(), but ETRACK has already been
* executed, which means that the CPUs running in the new mm will enter
* into the enclave with a fresh epoch.
*/
cpumask_clear(cpumask);
idx = srcu_read_lock(&encl->srcu);
list_for_each_entry_rcu(encl_mm, &encl->mm_list, list) {
if (!mmget_not_zero(encl_mm->mm))
continue;
cpumask_or(cpumask, cpumask, mm_cpumask(encl_mm->mm));
mmput_async(encl_mm->mm);
}
srcu_read_unlock(&encl->srcu, idx);
return cpumask;
}
/*
* Swap page to the regular memory transformed to the blocked state by using
* EBLOCK, which means that it can no longer be referenced (no new TLB entries).
......@@ -280,7 +224,7 @@ static void sgx_encl_ewb(struct sgx_epc_page *epc_page,
* miss cpus that entered the enclave between
* generating the mask and incrementing epoch.
*/
on_each_cpu_mask(sgx_encl_ewb_cpumask(encl),
on_each_cpu_mask(sgx_encl_cpumask(encl),
sgx_ipi_cb, NULL, 1);
ret = __sgx_encl_ewb(epc_page, va_slot, backing);
}
......@@ -431,6 +375,17 @@ static bool sgx_should_reclaim(unsigned long watermark)
!list_empty(&sgx_active_page_list);
}
/*
* sgx_reclaim_direct() should be called (without enclave's mutex held)
* in locations where SGX memory resources might be low and might be
* needed in order to make forward progress.
*/
void sgx_reclaim_direct(void)
{
if (sgx_should_reclaim(SGX_NR_LOW_PAGES))
sgx_reclaim_pages();
}
static int ksgxd(void *p)
{
set_freezable();
......
......@@ -86,10 +86,13 @@ static inline void *sgx_get_epc_virt_addr(struct sgx_epc_page *page)
struct sgx_epc_page *__sgx_alloc_epc_page(void);
void sgx_free_epc_page(struct sgx_epc_page *page);
void sgx_reclaim_direct(void);
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);
void sgx_ipi_cb(void *info);
#ifdef CONFIG_X86_SGX_KVM
int __init sgx_vepc_init(void);
#else
......
......@@ -24,6 +24,9 @@ enum encl_op_type {
ENCL_OP_PUT_TO_ADDRESS,
ENCL_OP_GET_FROM_ADDRESS,
ENCL_OP_NOP,
ENCL_OP_EACCEPT,
ENCL_OP_EMODPE,
ENCL_OP_INIT_TCS_PAGE,
ENCL_OP_MAX,
};
......@@ -53,4 +56,24 @@ struct encl_op_get_from_addr {
uint64_t addr;
};
struct encl_op_eaccept {
struct encl_op_header header;
uint64_t epc_addr;
uint64_t flags;
uint64_t ret;
};
struct encl_op_emodpe {
struct encl_op_header header;
uint64_t epc_addr;
uint64_t flags;
};
struct encl_op_init_tcs_page {
struct encl_op_header header;
uint64_t tcs_page;
uint64_t ssa;
uint64_t entry;
};
#endif /* DEFINES_H */
......@@ -130,6 +130,47 @@ static bool encl_ioc_add_pages(struct encl *encl, struct encl_segment *seg)
return true;
}
/*
* Parse the enclave code's symbol table to locate and return address of
* the provided symbol
*/
uint64_t encl_get_entry(struct encl *encl, const char *symbol)
{
Elf64_Shdr *sections;
Elf64_Sym *symtab;
Elf64_Ehdr *ehdr;
char *sym_names;
int num_sym;
int i;
ehdr = encl->bin;
sections = encl->bin + ehdr->e_shoff;
for (i = 0; i < ehdr->e_shnum; i++) {
if (sections[i].sh_type == SHT_SYMTAB) {
symtab = (Elf64_Sym *)((char *)encl->bin + sections[i].sh_offset);
num_sym = sections[i].sh_size / sections[i].sh_entsize;
break;
}
}
for (i = 0; i < ehdr->e_shnum; i++) {
if (sections[i].sh_type == SHT_STRTAB) {
sym_names = (char *)encl->bin + sections[i].sh_offset;
break;
}
}
for (i = 0; i < num_sym; i++) {
Elf64_Sym *sym = &symtab[i];
if (!strcmp(symbol, sym_names + sym->st_name))
return (uint64_t)sym->st_value;
}
return 0;
}
bool encl_load(const char *path, struct encl *encl, unsigned long heap_size)
{
const char device_path[] = "/dev/sgx_enclave";
......
This diff is collapsed.
......@@ -38,6 +38,7 @@ void encl_delete(struct encl *ctx);
bool encl_load(const char *path, struct encl *encl, unsigned long heap_size);
bool encl_measure(struct encl *encl);
bool encl_build(struct encl *encl);
uint64_t encl_get_entry(struct encl *encl, const char *symbol);
int sgx_enter_enclave(void *rdi, void *rsi, long rdx, u32 function, void *r8, void *r9,
struct sgx_enclave_run *run);
......
......@@ -11,6 +11,42 @@
*/
static uint8_t encl_buffer[8192] = { 1 };
enum sgx_enclu_function {
EACCEPT = 0x5,
EMODPE = 0x6,
};
static void do_encl_emodpe(void *_op)
{
struct sgx_secinfo secinfo __aligned(sizeof(struct sgx_secinfo)) = {0};
struct encl_op_emodpe *op = _op;
secinfo.flags = op->flags;
asm volatile(".byte 0x0f, 0x01, 0xd7"
:
: "a" (EMODPE),
"b" (&secinfo),
"c" (op->epc_addr));
}
static void do_encl_eaccept(void *_op)
{
struct sgx_secinfo secinfo __aligned(sizeof(struct sgx_secinfo)) = {0};
struct encl_op_eaccept *op = _op;
int rax;
secinfo.flags = op->flags;
asm volatile(".byte 0x0f, 0x01, 0xd7"
: "=a" (rax)
: "a" (EACCEPT),
"b" (&secinfo),
"c" (op->epc_addr));
op->ret = rax;
}
static void *memcpy(void *dest, const void *src, size_t n)
{
size_t i;
......@@ -21,6 +57,35 @@ static void *memcpy(void *dest, const void *src, size_t n)
return dest;
}
static void *memset(void *dest, int c, size_t n)
{
size_t i;
for (i = 0; i < n; i++)
((char *)dest)[i] = c;
return dest;
}
static void do_encl_init_tcs_page(void *_op)
{
struct encl_op_init_tcs_page *op = _op;
void *tcs = (void *)op->tcs_page;
uint32_t val_32;
memset(tcs, 0, 16); /* STATE and FLAGS */
memcpy(tcs + 16, &op->ssa, 8); /* OSSA */
memset(tcs + 24, 0, 4); /* CSSA */
val_32 = 1;
memcpy(tcs + 28, &val_32, 4); /* NSSA */
memcpy(tcs + 32, &op->entry, 8); /* OENTRY */
memset(tcs + 40, 0, 24); /* AEP, OFSBASE, OGSBASE */
val_32 = 0xFFFFFFFF;
memcpy(tcs + 64, &val_32, 4); /* FSLIMIT */
memcpy(tcs + 68, &val_32, 4); /* GSLIMIT */
memset(tcs + 72, 0, 4024); /* Reserved */
}
static void do_encl_op_put_to_buf(void *op)
{
struct encl_op_put_to_buf *op2 = op;
......@@ -62,6 +127,9 @@ void encl_body(void *rdi, void *rsi)
do_encl_op_put_to_addr,
do_encl_op_get_from_addr,
do_encl_op_nop,
do_encl_eaccept,
do_encl_emodpe,
do_encl_init_tcs_page,
};
struct encl_op_header *op = (struct encl_op_header *)rdi;
......
......@@ -45,6 +45,12 @@ encl_entry:
# TCS #2. By adding the value of encl_stack to it, we get
# the absolute address for the stack.
lea (encl_stack)(%rbx), %rax
jmp encl_entry_core
encl_dyn_entry:
# Entry point for dynamically created TCS page expected to follow
# its stack directly.
lea -1(%rbx), %rax
encl_entry_core:
xchg %rsp, %rax
push %rax
......
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