Skip to content

Commit

Permalink
Merge tag 'x86_cc_for_v6.5' of git://git.kernel.org/pub/scm/linux/ker…
Browse files Browse the repository at this point in the history
…nel/git/tip/tip

Pull x86 confidential computing update from Borislav Petkov:

 - Add support for unaccepted memory as specified in the UEFI spec v2.9.

   The gist of it all is that Intel TDX and AMD SEV-SNP confidential
   computing guests define the notion of accepting memory before using
   it and thus preventing a whole set of attacks against such guests
   like memory replay and the like.

   There are a couple of strategies of how memory should be accepted -
   the current implementation does an on-demand way of accepting.

* tag 'x86_cc_for_v6.5' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  virt: sevguest: Add CONFIG_CRYPTO dependency
  x86/efi: Safely enable unaccepted memory in UEFI
  x86/sev: Add SNP-specific unaccepted memory support
  x86/sev: Use large PSC requests if applicable
  x86/sev: Allow for use of the early boot GHCB for PSC requests
  x86/sev: Put PSC struct on the stack in prep for unaccepted memory support
  x86/sev: Fix calculation of end address based on number of pages
  x86/tdx: Add unaccepted memory support
  x86/tdx: Refactor try_accept_one()
  x86/tdx: Make _tdx_hypercall() and __tdx_module_call() available in boot stub
  efi/unaccepted: Avoid load_unaligned_zeropad() stepping into unaccepted memory
  efi: Add unaccepted memory support
  x86/boot/compressed: Handle unaccepted memory
  efi/libstub: Implement support for unaccepted memory
  efi/x86: Get full memory map in allocate_e820()
  mm: Add support for unaccepted memory
  • Loading branch information
torvalds committed Jun 26, 2023
2 parents 3e5822e + 84b9b44 commit 2c96136
Show file tree
Hide file tree
Showing 44 changed files with 1,448 additions and 307 deletions.
4 changes: 4 additions & 0 deletions arch/x86/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -887,9 +887,11 @@ config INTEL_TDX_GUEST
bool "Intel TDX (Trust Domain Extensions) - Guest Support"
depends on X86_64 && CPU_SUP_INTEL
depends on X86_X2APIC
depends on EFI_STUB
select ARCH_HAS_CC_PLATFORM
select X86_MEM_ENCRYPT
select X86_MCE
select UNACCEPTED_MEMORY
help
Support running as a guest under Intel TDX. Without this support,
the guest kernel can not boot or run under TDX.
Expand Down Expand Up @@ -1544,11 +1546,13 @@ config X86_MEM_ENCRYPT
config AMD_MEM_ENCRYPT
bool "AMD Secure Memory Encryption (SME) support"
depends on X86_64 && CPU_SUP_AMD
depends on EFI_STUB
select DMA_COHERENT_POOL
select ARCH_USE_MEMREMAP_PROT
select INSTRUCTION_DECODER
select ARCH_HAS_CC_PLATFORM
select X86_MEM_ENCRYPT
select UNACCEPTED_MEMORY
help
Say yes to enable support for the encryption of system memory.
This requires an AMD processor that supports Secure Memory
Expand Down
3 changes: 2 additions & 1 deletion arch/x86/boot/compressed/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ ifdef CONFIG_X86_64
endif

vmlinux-objs-$(CONFIG_ACPI) += $(obj)/acpi.o
vmlinux-objs-$(CONFIG_INTEL_TDX_GUEST) += $(obj)/tdx.o $(obj)/tdcall.o
vmlinux-objs-$(CONFIG_INTEL_TDX_GUEST) += $(obj)/tdx.o $(obj)/tdcall.o $(obj)/tdx-shared.o
vmlinux-objs-$(CONFIG_UNACCEPTED_MEMORY) += $(obj)/mem.o

vmlinux-objs-$(CONFIG_EFI) += $(obj)/efi.o
vmlinux-objs-$(CONFIG_EFI_MIXED) += $(obj)/efi_mixed.o
Expand Down
10 changes: 10 additions & 0 deletions arch/x86/boot/compressed/efi.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ typedef guid_t efi_guid_t __aligned(__alignof__(u32));
#define ACPI_TABLE_GUID EFI_GUID(0xeb9d2d30, 0x2d88, 0x11d3, 0x9a, 0x16, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d)
#define ACPI_20_TABLE_GUID EFI_GUID(0x8868e871, 0xe4f1, 0x11d3, 0xbc, 0x22, 0x00, 0x80, 0xc7, 0x3c, 0x88, 0x81)
#define EFI_CC_BLOB_GUID EFI_GUID(0x067b1f5f, 0xcf26, 0x44c5, 0x85, 0x54, 0x93, 0xd7, 0x77, 0x91, 0x2d, 0x42)
#define LINUX_EFI_UNACCEPTED_MEM_TABLE_GUID EFI_GUID(0xd5d1de3c, 0x105c, 0x44f9, 0x9e, 0xa9, 0xbc, 0xef, 0x98, 0x12, 0x00, 0x31)

#define EFI32_LOADER_SIGNATURE "EL32"
#define EFI64_LOADER_SIGNATURE "EL64"
Expand All @@ -32,6 +33,7 @@ typedef struct {
} efi_table_hdr_t;

#define EFI_CONVENTIONAL_MEMORY 7
#define EFI_UNACCEPTED_MEMORY 15

#define EFI_MEMORY_MORE_RELIABLE \
((u64)0x0000000000010000ULL) /* higher reliability */
Expand Down Expand Up @@ -104,6 +106,14 @@ struct efi_setup_data {
u64 reserved[8];
};

struct efi_unaccepted_memory {
u32 version;
u32 unit_size;
u64 phys_base;
u64 size;
unsigned long bitmap[];
};

static inline int efi_guidcmp (efi_guid_t left, efi_guid_t right)
{
return memcmp(&left, &right, sizeof (efi_guid_t));
Expand Down
19 changes: 19 additions & 0 deletions arch/x86/boot/compressed/error.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,22 @@ void error(char *m)
while (1)
asm("hlt");
}

/* EFI libstub provides vsnprintf() */
#ifdef CONFIG_EFI_STUB
void panic(const char *fmt, ...)
{
static char buf[1024];
va_list args;
int len;

va_start(args, fmt);
len = vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);

if (len && buf[len - 1] == '\n')
buf[len - 1] = '\0';

error(buf);
}
#endif
1 change: 1 addition & 0 deletions arch/x86/boot/compressed/error.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@

void warn(char *m);
void error(char *m) __noreturn;
void panic(const char *fmt, ...) __noreturn __cold;

#endif /* BOOT_COMPRESSED_ERROR_H */
40 changes: 28 additions & 12 deletions arch/x86/boot/compressed/kaslr.c
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,33 @@ static bool process_mem_region(struct mem_vector *region,
}

#ifdef CONFIG_EFI

/*
* Only EFI_CONVENTIONAL_MEMORY and EFI_UNACCEPTED_MEMORY (if supported) are
* guaranteed to be free.
*
* Pick free memory more conservatively than the EFI spec allows: according to
* the spec, EFI_BOOT_SERVICES_{CODE|DATA} are also free memory and thus
* available to place the kernel image into, but in practice there's firmware
* where using that memory leads to crashes. Buggy vendor EFI code registers
* for an event that triggers on SetVirtualAddressMap(). The handler assumes
* that EFI_BOOT_SERVICES_DATA memory has not been touched by loader yet, which
* is probably true for Windows.
*
* Preserve EFI_BOOT_SERVICES_* regions until after SetVirtualAddressMap().
*/
static inline bool memory_type_is_free(efi_memory_desc_t *md)
{
if (md->type == EFI_CONVENTIONAL_MEMORY)
return true;

if (IS_ENABLED(CONFIG_UNACCEPTED_MEMORY) &&
md->type == EFI_UNACCEPTED_MEMORY)
return true;

return false;
}

/*
* Returns true if we processed the EFI memmap, which we prefer over the E820
* table if it is available.
Expand Down Expand Up @@ -716,18 +743,7 @@ process_efi_entries(unsigned long minimum, unsigned long image_size)
for (i = 0; i < nr_desc; i++) {
md = efi_early_memdesc_ptr(pmap, e->efi_memdesc_size, i);

/*
* Here we are more conservative in picking free memory than
* the EFI spec allows:
*
* According to the spec, EFI_BOOT_SERVICES_{CODE|DATA} are also
* free memory and thus available to place the kernel image into,
* but in practice there's firmware where using that memory leads
* to crashes.
*
* Only EFI_CONVENTIONAL_MEMORY is guaranteed to be free.
*/
if (md->type != EFI_CONVENTIONAL_MEMORY)
if (!memory_type_is_free(md))
continue;

if (efi_soft_reserve_enabled() &&
Expand Down
86 changes: 86 additions & 0 deletions arch/x86/boot/compressed/mem.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// SPDX-License-Identifier: GPL-2.0-only

#include "error.h"
#include "misc.h"
#include "tdx.h"
#include "sev.h"
#include <asm/shared/tdx.h>

/*
* accept_memory() and process_unaccepted_memory() called from EFI stub which
* runs before decompresser and its early_tdx_detect().
*
* Enumerate TDX directly from the early users.
*/
static bool early_is_tdx_guest(void)
{
static bool once;
static bool is_tdx;

if (!IS_ENABLED(CONFIG_INTEL_TDX_GUEST))
return false;

if (!once) {
u32 eax, sig[3];

cpuid_count(TDX_CPUID_LEAF_ID, 0, &eax,
&sig[0], &sig[2], &sig[1]);
is_tdx = !memcmp(TDX_IDENT, sig, sizeof(sig));
once = true;
}

return is_tdx;
}

void arch_accept_memory(phys_addr_t start, phys_addr_t end)
{
/* Platform-specific memory-acceptance call goes here */
if (early_is_tdx_guest()) {
if (!tdx_accept_memory(start, end))
panic("TDX: Failed to accept memory\n");
} else if (sev_snp_enabled()) {
snp_accept_memory(start, end);
} else {
error("Cannot accept memory: unknown platform\n");
}
}

bool init_unaccepted_memory(void)
{
guid_t guid = LINUX_EFI_UNACCEPTED_MEM_TABLE_GUID;
struct efi_unaccepted_memory *table;
unsigned long cfg_table_pa;
unsigned int cfg_table_len;
enum efi_type et;
int ret;

et = efi_get_type(boot_params);
if (et == EFI_TYPE_NONE)
return false;

ret = efi_get_conf_table(boot_params, &cfg_table_pa, &cfg_table_len);
if (ret) {
warn("EFI config table not found.");
return false;
}

table = (void *)efi_find_vendor_table(boot_params, cfg_table_pa,
cfg_table_len, guid);
if (!table)
return false;

if (table->version != 1)
error("Unknown version of unaccepted memory table\n");

/*
* In many cases unaccepted_table is already set by EFI stub, but it
* has to be initialized again to cover cases when the table is not
* allocated by EFI stub or EFI stub copied the kernel image with
* efi_relocate_kernel() before the variable is set.
*
* It must be initialized before the first usage of accept_memory().
*/
unaccepted_table = table;

return true;
}
6 changes: 6 additions & 0 deletions arch/x86/boot/compressed/misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,12 @@ asmlinkage __visible void *extract_kernel(void *rmode, memptr heap,
#endif

debug_putstr("\nDecompressing Linux... ");

if (init_unaccepted_memory()) {
debug_putstr("Accepting memory... ");
accept_memory(__pa(output), __pa(output) + needed_size);
}

__decompress(input_data, input_len, NULL, NULL, output, output_len,
NULL, error);
entry_offset = parse_elf(output);
Expand Down
10 changes: 10 additions & 0 deletions arch/x86/boot/compressed/misc.h
Original file line number Diff line number Diff line change
Expand Up @@ -247,4 +247,14 @@ static inline unsigned long efi_find_vendor_table(struct boot_params *bp,
}
#endif /* CONFIG_EFI */

#ifdef CONFIG_UNACCEPTED_MEMORY
bool init_unaccepted_memory(void);
#else
static inline bool init_unaccepted_memory(void) { return false; }
#endif

/* Defined in EFI stub */
extern struct efi_unaccepted_memory *unaccepted_table;
void accept_memory(phys_addr_t start, phys_addr_t end);

#endif /* BOOT_COMPRESSED_MISC_H */
54 changes: 53 additions & 1 deletion arch/x86/boot/compressed/sev.c
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ static enum es_result vc_read_mem(struct es_em_ctxt *ctxt,
/* Include code for early handlers */
#include "../../kernel/sev-shared.c"

static inline bool sev_snp_enabled(void)
bool sev_snp_enabled(void)
{
return sev_status & MSR_AMD64_SEV_SNP_ENABLED;
}
Expand Down Expand Up @@ -181,6 +181,58 @@ static bool early_setup_ghcb(void)
return true;
}

static phys_addr_t __snp_accept_memory(struct snp_psc_desc *desc,
phys_addr_t pa, phys_addr_t pa_end)
{
struct psc_hdr *hdr;
struct psc_entry *e;
unsigned int i;

hdr = &desc->hdr;
memset(hdr, 0, sizeof(*hdr));

e = desc->entries;

i = 0;
while (pa < pa_end && i < VMGEXIT_PSC_MAX_ENTRY) {
hdr->end_entry = i;

e->gfn = pa >> PAGE_SHIFT;
e->operation = SNP_PAGE_STATE_PRIVATE;
if (IS_ALIGNED(pa, PMD_SIZE) && (pa_end - pa) >= PMD_SIZE) {
e->pagesize = RMP_PG_SIZE_2M;
pa += PMD_SIZE;
} else {
e->pagesize = RMP_PG_SIZE_4K;
pa += PAGE_SIZE;
}

e++;
i++;
}

if (vmgexit_psc(boot_ghcb, desc))
sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_PSC);

pvalidate_pages(desc);

return pa;
}

void snp_accept_memory(phys_addr_t start, phys_addr_t end)
{
struct snp_psc_desc desc = {};
unsigned int i;
phys_addr_t pa;

if (!boot_ghcb && !early_setup_ghcb())
sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_PSC);

pa = start;
while (pa < end)
pa = __snp_accept_memory(&desc, pa, end);
}

void sev_es_shutdown_ghcb(void)
{
if (!boot_ghcb)
Expand Down
23 changes: 23 additions & 0 deletions arch/x86/boot/compressed/sev.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* AMD SEV header for early boot related functions.
*
* Author: Tom Lendacky <thomas.lendacky@amd.com>
*/

#ifndef BOOT_COMPRESSED_SEV_H
#define BOOT_COMPRESSED_SEV_H

#ifdef CONFIG_AMD_MEM_ENCRYPT

bool sev_snp_enabled(void);
void snp_accept_memory(phys_addr_t start, phys_addr_t end);

#else

static inline bool sev_snp_enabled(void) { return false; }
static inline void snp_accept_memory(phys_addr_t start, phys_addr_t end) { }

#endif

#endif
2 changes: 2 additions & 0 deletions arch/x86/boot/compressed/tdx-shared.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#include "error.h"
#include "../../coco/tdx/tdx-shared.c"
2 changes: 1 addition & 1 deletion arch/x86/coco/tdx/Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# SPDX-License-Identifier: GPL-2.0

obj-y += tdx.o tdcall.o
obj-y += tdx.o tdx-shared.o tdcall.o
Loading

0 comments on commit 2c96136

Please sign in to comment.