Skip to content

Commit

Permalink
x86/boot/compressed/64: Setup a GHCB-based VC Exception handler
Browse files Browse the repository at this point in the history
Install an exception handler for #VC exception that uses a GHCB. Also
add the infrastructure for handling different exit-codes by decoding
the instruction that caused the exception and error handling.

Signed-off-by: Joerg Roedel <jroedel@suse.de>
Signed-off-by: Borislav Petkov <bp@suse.de>
Link: https://lkml.kernel.org/r/20200907131613.12703-24-joro@8bytes.org
  • Loading branch information
Joerg Roedel authored and Borislav Petkov committed Sep 7, 2020
1 parent c81d600 commit 597cfe4
Show file tree
Hide file tree
Showing 10 changed files with 331 additions and 1 deletion.
1 change: 1 addition & 0 deletions arch/x86/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -1521,6 +1521,7 @@ config AMD_MEM_ENCRYPT
select DYNAMIC_PHYSICAL_MASK
select ARCH_USE_MEMREMAP_PROT
select ARCH_HAS_FORCE_DMA_UNENCRYPTED
select INSTRUCTION_DECODER
help
Say yes to enable support for the encryption of system memory.
This requires an AMD processor that supports Secure Memory
Expand Down
5 changes: 5 additions & 0 deletions arch/x86/boot/compressed/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ KBUILD_CFLAGS += $(call cc-option,-fmacro-prefix-map=$(srctree)/=)
KBUILD_CFLAGS += -fno-asynchronous-unwind-tables
KBUILD_CFLAGS += -D__DISABLE_EXPORTS

# sev-es.c indirectly inludes inat-table.h which is generated during
# compilation and stored in $(objtree). Add the directory to the includes so
# that the compiler finds it even with out-of-tree builds (make O=/some/path).
CFLAGS_sev-es.o += -I$(objtree)/arch/x86/lib/

KBUILD_AFLAGS := $(KBUILD_CFLAGS) -D__ASSEMBLY__
GCOV_PROFILE := n
UBSAN_SANITIZE :=n
Expand Down
4 changes: 4 additions & 0 deletions arch/x86/boot/compressed/idt_64.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,9 @@ void load_stage2_idt(void)

set_idt_entry(X86_TRAP_PF, boot_page_fault);

#ifdef CONFIG_AMD_MEM_ENCRYPT
set_idt_entry(X86_TRAP_VC, boot_stage2_vc);
#endif

load_boot_idt(&boot_idt_desc);
}
3 changes: 2 additions & 1 deletion arch/x86/boot/compressed/idt_handlers_64.S
Original file line number Diff line number Diff line change
Expand Up @@ -72,5 +72,6 @@ SYM_FUNC_END(\name)
EXCEPTION_HANDLER boot_page_fault do_boot_page_fault error_code=1

#ifdef CONFIG_AMD_MEM_ENCRYPT
EXCEPTION_HANDLER boot_stage1_vc do_vc_no_ghcb error_code=1
EXCEPTION_HANDLER boot_stage1_vc do_vc_no_ghcb error_code=1
EXCEPTION_HANDLER boot_stage2_vc do_boot_stage2_vc error_code=1
#endif
7 changes: 7 additions & 0 deletions arch/x86/boot/compressed/misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,13 @@ asmlinkage __visible void *extract_kernel(void *rmode, memptr heap,
parse_elf(output);
handle_relocations(output, output_len, virt_addr);
debug_putstr("done.\nBooting the kernel.\n");

/*
* Flush GHCB from cache and map it encrypted again when running as
* SEV-ES guest.
*/
sev_es_shutdown_ghcb();

return output;
}

Expand Down
7 changes: 7 additions & 0 deletions arch/x86/boot/compressed/misc.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,12 @@ static inline void console_init(void)

void set_sev_encryption_mask(void);

#ifdef CONFIG_AMD_MEM_ENCRYPT
void sev_es_shutdown_ghcb(void);
#else
static inline void sev_es_shutdown_ghcb(void) { }
#endif

/* acpi.c */
#ifdef CONFIG_ACPI
acpi_physical_address get_rsdp_addr(void);
Expand Down Expand Up @@ -144,5 +150,6 @@ extern struct desc_ptr boot_idt_desc;
/* IDT Entry Points */
void boot_page_fault(void);
void boot_stage1_vc(void);
void boot_stage2_vc(void);

#endif /* BOOT_COMPRESSED_MISC_H */
111 changes: 111 additions & 0 deletions arch/x86/boot/compressed/sev-es.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,17 @@
#include "misc.h"

#include <asm/sev-es.h>
#include <asm/trapnr.h>
#include <asm/trap_pf.h>
#include <asm/msr-index.h>
#include <asm/ptrace.h>
#include <asm/svm.h>

#include "error.h"

struct ghcb boot_ghcb_page __aligned(PAGE_SIZE);
struct ghcb *boot_ghcb;

static inline u64 sev_es_rd_ghcb_msr(void)
{
unsigned long low, high;
Expand All @@ -38,8 +45,112 @@ static inline void sev_es_wr_ghcb_msr(u64 val)
"a"(low), "d" (high) : "memory");
}

static enum es_result vc_decode_insn(struct es_em_ctxt *ctxt)
{
char buffer[MAX_INSN_SIZE];
enum es_result ret;

memcpy(buffer, (unsigned char *)ctxt->regs->ip, MAX_INSN_SIZE);

insn_init(&ctxt->insn, buffer, MAX_INSN_SIZE, 1);
insn_get_length(&ctxt->insn);

ret = ctxt->insn.immediate.got ? ES_OK : ES_DECODE_FAILED;

return ret;
}

static enum es_result vc_write_mem(struct es_em_ctxt *ctxt,
void *dst, char *buf, size_t size)
{
memcpy(dst, buf, size);

return ES_OK;
}

static enum es_result vc_read_mem(struct es_em_ctxt *ctxt,
void *src, char *buf, size_t size)
{
memcpy(buf, src, size);

return ES_OK;
}

#undef __init
#undef __pa
#define __init
#define __pa(x) ((unsigned long)(x))

#define __BOOT_COMPRESSED

/* Basic instruction decoding support needed */
#include "../../lib/inat.c"
#include "../../lib/insn.c"

/* Include code for early handlers */
#include "../../kernel/sev-es-shared.c"

static bool early_setup_sev_es(void)
{
if (!sev_es_negotiate_protocol())
sev_es_terminate(GHCB_SEV_ES_REASON_PROTOCOL_UNSUPPORTED);

if (set_page_decrypted((unsigned long)&boot_ghcb_page))
return false;

/* Page is now mapped decrypted, clear it */
memset(&boot_ghcb_page, 0, sizeof(boot_ghcb_page));

boot_ghcb = &boot_ghcb_page;

/* Initialize lookup tables for the instruction decoder */
inat_init_tables();

return true;
}

void sev_es_shutdown_ghcb(void)
{
if (!boot_ghcb)
return;

/*
* GHCB Page must be flushed from the cache and mapped encrypted again.
* Otherwise the running kernel will see strange cache effects when
* trying to use that page.
*/
if (set_page_encrypted((unsigned long)&boot_ghcb_page))
error("Can't map GHCB page encrypted");
}

void do_boot_stage2_vc(struct pt_regs *regs, unsigned long exit_code)
{
struct es_em_ctxt ctxt;
enum es_result result;

if (!boot_ghcb && !early_setup_sev_es())
sev_es_terminate(GHCB_SEV_ES_REASON_GENERAL_REQUEST);

vc_ghcb_invalidate(boot_ghcb);
result = vc_init_em_ctxt(&ctxt, regs, exit_code);
if (result != ES_OK)
goto finish;

switch (exit_code) {
default:
result = ES_UNSUPPORTED;
break;
}

finish:
if (result == ES_OK) {
vc_finish_insn(&ctxt);
} else if (result != ES_RETRY) {
/*
* For now, just halt the machine. That makes debugging easier,
* later we just call sev_es_terminate() here.
*/
while (true)
asm volatile("hlt\n");
}
}
39 changes: 39 additions & 0 deletions arch/x86/include/asm/sev-es.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,14 @@
#define __ASM_ENCRYPTED_STATE_H

#include <linux/types.h>
#include <asm/insn.h>

#define GHCB_SEV_INFO 0x001UL
#define GHCB_SEV_INFO_REQ 0x002UL
#define GHCB_INFO(v) ((v) & 0xfffUL)
#define GHCB_PROTO_MAX(v) (((v) >> 48) & 0xffffUL)
#define GHCB_PROTO_MIN(v) (((v) >> 32) & 0xffffUL)
#define GHCB_PROTO_OUR 0x0001UL
#define GHCB_SEV_CPUID_REQ 0x004UL
#define GHCB_CPUID_REQ_EAX 0
#define GHCB_CPUID_REQ_EBX 1
Expand All @@ -19,12 +26,44 @@
(((unsigned long)reg & 3) << 30) | \
(((unsigned long)fn) << 32))

#define GHCB_PROTOCOL_MAX 0x0001UL
#define GHCB_DEFAULT_USAGE 0x0000UL

#define GHCB_SEV_CPUID_RESP 0x005UL
#define GHCB_SEV_TERMINATE 0x100UL
#define GHCB_SEV_TERMINATE_REASON(reason_set, reason_val) \
(((((u64)reason_set) & 0x7) << 12) | \
((((u64)reason_val) & 0xff) << 16))
#define GHCB_SEV_ES_REASON_GENERAL_REQUEST 0
#define GHCB_SEV_ES_REASON_PROTOCOL_UNSUPPORTED 1

#define GHCB_SEV_GHCB_RESP_CODE(v) ((v) & 0xfff)
#define VMGEXIT() { asm volatile("rep; vmmcall\n\r"); }

enum es_result {
ES_OK, /* All good */
ES_UNSUPPORTED, /* Requested operation not supported */
ES_VMM_ERROR, /* Unexpected state from the VMM */
ES_DECODE_FAILED, /* Instruction decoding failed */
ES_EXCEPTION, /* Instruction caused exception */
ES_RETRY, /* Retry instruction emulation */
};

struct es_fault_info {
unsigned long vector;
unsigned long error_code;
unsigned long cr2;
};

struct pt_regs;

/* ES instruction emulation context */
struct es_em_ctxt {
struct pt_regs *regs;
struct insn insn;
struct es_fault_info fi;
};

void do_vc_no_ghcb(struct pt_regs *regs, unsigned long exit_code);

static inline u64 lower_bits(u64 val, unsigned int bits)
Expand Down
1 change: 1 addition & 0 deletions arch/x86/include/uapi/asm/svm.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#define SVM_EXIT_WRITE_DR6 0x036
#define SVM_EXIT_WRITE_DR7 0x037
#define SVM_EXIT_EXCP_BASE 0x040
#define SVM_EXIT_LAST_EXCP 0x05f
#define SVM_EXIT_INTR 0x060
#define SVM_EXIT_NMI 0x061
#define SVM_EXIT_SMI 0x062
Expand Down
Loading

0 comments on commit 597cfe4

Please sign in to comment.