Skip to content

Commit 597cfe4

Browse files
joergroedelsuryasaimadhu
authored andcommitted
x86/boot/compressed/64: Setup a GHCB-based VC Exception handler
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 <[email protected]> Signed-off-by: Borislav Petkov <[email protected]> Link: https://lkml.kernel.org/r/[email protected]
1 parent c81d600 commit 597cfe4

File tree

10 files changed

+331
-1
lines changed

10 files changed

+331
-1
lines changed

arch/x86/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1521,6 +1521,7 @@ config AMD_MEM_ENCRYPT
15211521
select DYNAMIC_PHYSICAL_MASK
15221522
select ARCH_USE_MEMREMAP_PROT
15231523
select ARCH_HAS_FORCE_DMA_UNENCRYPTED
1524+
select INSTRUCTION_DECODER
15241525
help
15251526
Say yes to enable support for the encryption of system memory.
15261527
This requires an AMD processor that supports Secure Memory

arch/x86/boot/compressed/Makefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ KBUILD_CFLAGS += $(call cc-option,-fmacro-prefix-map=$(srctree)/=)
4444
KBUILD_CFLAGS += -fno-asynchronous-unwind-tables
4545
KBUILD_CFLAGS += -D__DISABLE_EXPORTS
4646

47+
# sev-es.c indirectly inludes inat-table.h which is generated during
48+
# compilation and stored in $(objtree). Add the directory to the includes so
49+
# that the compiler finds it even with out-of-tree builds (make O=/some/path).
50+
CFLAGS_sev-es.o += -I$(objtree)/arch/x86/lib/
51+
4752
KBUILD_AFLAGS := $(KBUILD_CFLAGS) -D__ASSEMBLY__
4853
GCOV_PROFILE := n
4954
UBSAN_SANITIZE :=n

arch/x86/boot/compressed/idt_64.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,9 @@ void load_stage2_idt(void)
4646

4747
set_idt_entry(X86_TRAP_PF, boot_page_fault);
4848

49+
#ifdef CONFIG_AMD_MEM_ENCRYPT
50+
set_idt_entry(X86_TRAP_VC, boot_stage2_vc);
51+
#endif
52+
4953
load_boot_idt(&boot_idt_desc);
5054
}

arch/x86/boot/compressed/idt_handlers_64.S

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,5 +72,6 @@ SYM_FUNC_END(\name)
7272
EXCEPTION_HANDLER boot_page_fault do_boot_page_fault error_code=1
7373

7474
#ifdef CONFIG_AMD_MEM_ENCRYPT
75-
EXCEPTION_HANDLER boot_stage1_vc do_vc_no_ghcb error_code=1
75+
EXCEPTION_HANDLER boot_stage1_vc do_vc_no_ghcb error_code=1
76+
EXCEPTION_HANDLER boot_stage2_vc do_boot_stage2_vc error_code=1
7677
#endif

arch/x86/boot/compressed/misc.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,13 @@ asmlinkage __visible void *extract_kernel(void *rmode, memptr heap,
442442
parse_elf(output);
443443
handle_relocations(output, output_len, virt_addr);
444444
debug_putstr("done.\nBooting the kernel.\n");
445+
446+
/*
447+
* Flush GHCB from cache and map it encrypted again when running as
448+
* SEV-ES guest.
449+
*/
450+
sev_es_shutdown_ghcb();
451+
445452
return output;
446453
}
447454

arch/x86/boot/compressed/misc.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,12 @@ static inline void console_init(void)
115115

116116
void set_sev_encryption_mask(void);
117117

118+
#ifdef CONFIG_AMD_MEM_ENCRYPT
119+
void sev_es_shutdown_ghcb(void);
120+
#else
121+
static inline void sev_es_shutdown_ghcb(void) { }
122+
#endif
123+
118124
/* acpi.c */
119125
#ifdef CONFIG_ACPI
120126
acpi_physical_address get_rsdp_addr(void);
@@ -144,5 +150,6 @@ extern struct desc_ptr boot_idt_desc;
144150
/* IDT Entry Points */
145151
void boot_page_fault(void);
146152
void boot_stage1_vc(void);
153+
void boot_stage2_vc(void);
147154

148155
#endif /* BOOT_COMPRESSED_MISC_H */

arch/x86/boot/compressed/sev-es.c

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,17 @@
1313
#include "misc.h"
1414

1515
#include <asm/sev-es.h>
16+
#include <asm/trapnr.h>
17+
#include <asm/trap_pf.h>
1618
#include <asm/msr-index.h>
1719
#include <asm/ptrace.h>
1820
#include <asm/svm.h>
1921

22+
#include "error.h"
23+
24+
struct ghcb boot_ghcb_page __aligned(PAGE_SIZE);
25+
struct ghcb *boot_ghcb;
26+
2027
static inline u64 sev_es_rd_ghcb_msr(void)
2128
{
2229
unsigned long low, high;
@@ -38,8 +45,112 @@ static inline void sev_es_wr_ghcb_msr(u64 val)
3845
"a"(low), "d" (high) : "memory");
3946
}
4047

48+
static enum es_result vc_decode_insn(struct es_em_ctxt *ctxt)
49+
{
50+
char buffer[MAX_INSN_SIZE];
51+
enum es_result ret;
52+
53+
memcpy(buffer, (unsigned char *)ctxt->regs->ip, MAX_INSN_SIZE);
54+
55+
insn_init(&ctxt->insn, buffer, MAX_INSN_SIZE, 1);
56+
insn_get_length(&ctxt->insn);
57+
58+
ret = ctxt->insn.immediate.got ? ES_OK : ES_DECODE_FAILED;
59+
60+
return ret;
61+
}
62+
63+
static enum es_result vc_write_mem(struct es_em_ctxt *ctxt,
64+
void *dst, char *buf, size_t size)
65+
{
66+
memcpy(dst, buf, size);
67+
68+
return ES_OK;
69+
}
70+
71+
static enum es_result vc_read_mem(struct es_em_ctxt *ctxt,
72+
void *src, char *buf, size_t size)
73+
{
74+
memcpy(buf, src, size);
75+
76+
return ES_OK;
77+
}
78+
4179
#undef __init
80+
#undef __pa
4281
#define __init
82+
#define __pa(x) ((unsigned long)(x))
83+
84+
#define __BOOT_COMPRESSED
85+
86+
/* Basic instruction decoding support needed */
87+
#include "../../lib/inat.c"
88+
#include "../../lib/insn.c"
4389

4490
/* Include code for early handlers */
4591
#include "../../kernel/sev-es-shared.c"
92+
93+
static bool early_setup_sev_es(void)
94+
{
95+
if (!sev_es_negotiate_protocol())
96+
sev_es_terminate(GHCB_SEV_ES_REASON_PROTOCOL_UNSUPPORTED);
97+
98+
if (set_page_decrypted((unsigned long)&boot_ghcb_page))
99+
return false;
100+
101+
/* Page is now mapped decrypted, clear it */
102+
memset(&boot_ghcb_page, 0, sizeof(boot_ghcb_page));
103+
104+
boot_ghcb = &boot_ghcb_page;
105+
106+
/* Initialize lookup tables for the instruction decoder */
107+
inat_init_tables();
108+
109+
return true;
110+
}
111+
112+
void sev_es_shutdown_ghcb(void)
113+
{
114+
if (!boot_ghcb)
115+
return;
116+
117+
/*
118+
* GHCB Page must be flushed from the cache and mapped encrypted again.
119+
* Otherwise the running kernel will see strange cache effects when
120+
* trying to use that page.
121+
*/
122+
if (set_page_encrypted((unsigned long)&boot_ghcb_page))
123+
error("Can't map GHCB page encrypted");
124+
}
125+
126+
void do_boot_stage2_vc(struct pt_regs *regs, unsigned long exit_code)
127+
{
128+
struct es_em_ctxt ctxt;
129+
enum es_result result;
130+
131+
if (!boot_ghcb && !early_setup_sev_es())
132+
sev_es_terminate(GHCB_SEV_ES_REASON_GENERAL_REQUEST);
133+
134+
vc_ghcb_invalidate(boot_ghcb);
135+
result = vc_init_em_ctxt(&ctxt, regs, exit_code);
136+
if (result != ES_OK)
137+
goto finish;
138+
139+
switch (exit_code) {
140+
default:
141+
result = ES_UNSUPPORTED;
142+
break;
143+
}
144+
145+
finish:
146+
if (result == ES_OK) {
147+
vc_finish_insn(&ctxt);
148+
} else if (result != ES_RETRY) {
149+
/*
150+
* For now, just halt the machine. That makes debugging easier,
151+
* later we just call sev_es_terminate() here.
152+
*/
153+
while (true)
154+
asm volatile("hlt\n");
155+
}
156+
}

arch/x86/include/asm/sev-es.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,14 @@
99
#define __ASM_ENCRYPTED_STATE_H
1010

1111
#include <linux/types.h>
12+
#include <asm/insn.h>
1213

14+
#define GHCB_SEV_INFO 0x001UL
15+
#define GHCB_SEV_INFO_REQ 0x002UL
16+
#define GHCB_INFO(v) ((v) & 0xfffUL)
17+
#define GHCB_PROTO_MAX(v) (((v) >> 48) & 0xffffUL)
18+
#define GHCB_PROTO_MIN(v) (((v) >> 32) & 0xffffUL)
19+
#define GHCB_PROTO_OUR 0x0001UL
1320
#define GHCB_SEV_CPUID_REQ 0x004UL
1421
#define GHCB_CPUID_REQ_EAX 0
1522
#define GHCB_CPUID_REQ_EBX 1
@@ -19,12 +26,44 @@
1926
(((unsigned long)reg & 3) << 30) | \
2027
(((unsigned long)fn) << 32))
2128

29+
#define GHCB_PROTOCOL_MAX 0x0001UL
30+
#define GHCB_DEFAULT_USAGE 0x0000UL
31+
2232
#define GHCB_SEV_CPUID_RESP 0x005UL
2333
#define GHCB_SEV_TERMINATE 0x100UL
34+
#define GHCB_SEV_TERMINATE_REASON(reason_set, reason_val) \
35+
(((((u64)reason_set) & 0x7) << 12) | \
36+
((((u64)reason_val) & 0xff) << 16))
37+
#define GHCB_SEV_ES_REASON_GENERAL_REQUEST 0
38+
#define GHCB_SEV_ES_REASON_PROTOCOL_UNSUPPORTED 1
2439

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

43+
enum es_result {
44+
ES_OK, /* All good */
45+
ES_UNSUPPORTED, /* Requested operation not supported */
46+
ES_VMM_ERROR, /* Unexpected state from the VMM */
47+
ES_DECODE_FAILED, /* Instruction decoding failed */
48+
ES_EXCEPTION, /* Instruction caused exception */
49+
ES_RETRY, /* Retry instruction emulation */
50+
};
51+
52+
struct es_fault_info {
53+
unsigned long vector;
54+
unsigned long error_code;
55+
unsigned long cr2;
56+
};
57+
58+
struct pt_regs;
59+
60+
/* ES instruction emulation context */
61+
struct es_em_ctxt {
62+
struct pt_regs *regs;
63+
struct insn insn;
64+
struct es_fault_info fi;
65+
};
66+
2867
void do_vc_no_ghcb(struct pt_regs *regs, unsigned long exit_code);
2968

3069
static inline u64 lower_bits(u64 val, unsigned int bits)

arch/x86/include/uapi/asm/svm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#define SVM_EXIT_WRITE_DR6 0x036
3030
#define SVM_EXIT_WRITE_DR7 0x037
3131
#define SVM_EXIT_EXCP_BASE 0x040
32+
#define SVM_EXIT_LAST_EXCP 0x05f
3233
#define SVM_EXIT_INTR 0x060
3334
#define SVM_EXIT_NMI 0x061
3435
#define SVM_EXIT_SMI 0x062

0 commit comments

Comments
 (0)