Short Version: The SGX section->laundry_list structure is effectively thread-local, but declared next to some shared structures. Its semantics are clear as mud. Fix that. No functional changes. Compile tested only. Long Version: The SGX hardware keeps per-page metadata. This can provide things like permissions, integrity and replay protection. It also prevents things like having an enclave page mapped multiple times or shared between enclaves. But, that presents a problem for kexec()'d kernels (or any other kernel that does not run immediately after a hardware reset). This is because the last kernel may have been rude and forgotten to reset pages, which would trigger the "shared page" sanity check. To fix this, the SGX code "launders" the pages by running the EREMOVE instruction on all pages at boot. This is slow and can take a long time, so it is performed off in the SGX-specific ksgxd instead of being synchronous at boot. The init code hands the list of pages to launder in a per-SGX-section list: ->laundry_list. The only code to touch this list is the init code and ksgxd. This means that no locking is necessary for ->laundry_list. However, a lock is required for section->page_list, which is accessed while creating enclaves and by ksgxd. This lock (section->lock) is acquired by ksgxd while also processing ->laundry_list. It is easy to confuse the purpose of the locking as being for ->laundry_list and ->page_list. Rename ->laundry_list to ->init_laundry_list to make it clear that this is not normally used at runtime. Also add some comments clarifying the locking, and reorganize 'sgx_epc_section' to put 'lock' near the things it protects. Note: init_laundry_list is 128 bytes of wasted space at runtime. It could theoretically be dynamically allocated and then freed after the laundering process. But it would take nearly 128 bytes of extra instructions to do that. Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org> Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com> Signed-off-by: Borislav Petkov <bp@suse.de> Link: https://lkml.kernel.org/r/20201116222531.4834-1-dave.hansen@intel.com
87 lines
2.3 KiB
C
87 lines
2.3 KiB
C
/* 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 */
|