Skip to content

Commit 1aa9aa8

Browse files
joergroedelsuryasaimadhu
authored andcommitted
x86/sev-es: Setup GHCB-based boot #VC handler
Add the infrastructure to handle #VC exceptions when the kernel runs on virtual addresses and has mapped a GHCB. This handler will be used until the runtime #VC handler takes over. Since the handler runs very early, disable instrumentation for sev-es.c. [ bp: Make vc_ghcb_invalidate() __always_inline so that it can be inlined in noinstr functions like __sev_es_nmi_complete(). ] Signed-off-by: Joerg Roedel <[email protected]> Signed-off-by: Borislav Petkov <[email protected]> Link: https://lkml.kernel.org/r/[email protected]
1 parent 74d8d9d commit 1aa9aa8

File tree

9 files changed

+176
-8
lines changed

9 files changed

+176
-8
lines changed

arch/x86/include/asm/realmode.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ extern unsigned char real_mode_blob_end[];
5757
extern unsigned long initial_code;
5858
extern unsigned long initial_gs;
5959
extern unsigned long initial_stack;
60+
#ifdef CONFIG_AMD_MEM_ENCRYPT
61+
extern unsigned long initial_vc_handler;
62+
#endif
6063

6164
extern unsigned char real_mode_blob[];
6265
extern unsigned char real_mode_relocs[];

arch/x86/include/asm/segment.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@
230230
#define NUM_EXCEPTION_VECTORS 32
231231

232232
/* Bitmask of exception vectors which push an error code on the stack: */
233-
#define EXCEPTION_ERRCODE_MASK 0x00027d00
233+
#define EXCEPTION_ERRCODE_MASK 0x20027d00
234234

235235
#define GDT_SIZE (GDT_ENTRIES*8)
236236
#define GDT_ENTRY_TLS_ENTRIES 3

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,5 +75,7 @@ static inline u64 lower_bits(u64 val, unsigned int bits)
7575

7676
/* Early IDT entry points for #VC handler */
7777
extern void vc_no_ghcb(void);
78+
extern void vc_boot_ghcb(void);
79+
extern bool handle_vc_boot_ghcb(struct pt_regs *regs);
7880

7981
#endif

arch/x86/kernel/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,15 @@ CFLAGS_REMOVE_kvmclock.o = -pg
2020
CFLAGS_REMOVE_ftrace.o = -pg
2121
CFLAGS_REMOVE_early_printk.o = -pg
2222
CFLAGS_REMOVE_head64.o = -pg
23+
CFLAGS_REMOVE_sev-es.o = -pg
2324
endif
2425

2526
KASAN_SANITIZE_head$(BITS).o := n
2627
KASAN_SANITIZE_dumpstack.o := n
2728
KASAN_SANITIZE_dumpstack_$(BITS).o := n
2829
KASAN_SANITIZE_stacktrace.o := n
2930
KASAN_SANITIZE_paravirt.o := n
31+
KASAN_SANITIZE_sev-es.o := n
3032

3133
# With some compiler versions the generated code results in boot hangs, caused
3234
# by several compilation units. To be safe, disable all instrumentation.

arch/x86/kernel/head64.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,10 @@ void __init do_early_exception(struct pt_regs *regs, int trapnr)
406406
early_make_pgtable(native_read_cr2()))
407407
return;
408408

409+
if (IS_ENABLED(CONFIG_AMD_MEM_ENCRYPT) &&
410+
trapnr == X86_TRAP_VC && handle_vc_boot_ghcb(regs))
411+
return;
412+
409413
early_fixup_exception(regs, trapnr);
410414
}
411415

@@ -575,6 +579,10 @@ static void startup_64_load_idt(unsigned long physbase)
575579
/* This is used when running on kernel addresses */
576580
void early_setup_idt(void)
577581
{
582+
/* VMM Communication Exception */
583+
if (IS_ENABLED(CONFIG_AMD_MEM_ENCRYPT))
584+
set_bringup_idt_handler(bringup_idt_table, X86_TRAP_VC, vc_boot_ghcb);
585+
578586
bringup_idt_descr.address = (unsigned long)bringup_idt_table;
579587
native_load_idt(&bringup_idt_descr);
580588
}

arch/x86/kernel/head_64.S

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,13 +279,49 @@ SYM_CODE_START(start_cpu0)
279279
movq initial_stack(%rip), %rsp
280280
jmp .Ljump_to_C_code
281281
SYM_CODE_END(start_cpu0)
282+
#endif
283+
284+
#ifdef CONFIG_AMD_MEM_ENCRYPT
285+
/*
286+
* VC Exception handler used during early boot when running on kernel
287+
* addresses, but before the switch to the idt_table can be made.
288+
* The early_idt_handler_array can't be used here because it calls into a lot
289+
* of __init code and this handler is also used during CPU offlining/onlining.
290+
* Therefore this handler ends up in the .text section so that it stays around
291+
* when .init.text is freed.
292+
*/
293+
SYM_CODE_START_NOALIGN(vc_boot_ghcb)
294+
UNWIND_HINT_IRET_REGS offset=8
295+
296+
/* Build pt_regs */
297+
PUSH_AND_CLEAR_REGS
298+
299+
/* Call C handler */
300+
movq %rsp, %rdi
301+
movq ORIG_RAX(%rsp), %rsi
302+
movq initial_vc_handler(%rip), %rax
303+
ANNOTATE_RETPOLINE_SAFE
304+
call *%rax
305+
306+
/* Unwind pt_regs */
307+
POP_REGS
308+
309+
/* Remove Error Code */
310+
addq $8, %rsp
311+
312+
/* Pure iret required here - don't use INTERRUPT_RETURN */
313+
iretq
314+
SYM_CODE_END(vc_boot_ghcb)
282315
#endif
283316

284317
/* Both SMP bootup and ACPI suspend change these variables */
285318
__REFDATA
286319
.balign 8
287320
SYM_DATA(initial_code, .quad x86_64_start_kernel)
288321
SYM_DATA(initial_gs, .quad INIT_PER_CPU_VAR(fixed_percpu_data))
322+
#ifdef CONFIG_AMD_MEM_ENCRYPT
323+
SYM_DATA(initial_vc_handler, .quad handle_vc_boot_ghcb)
324+
#endif
289325

290326
/*
291327
* The SIZEOF_PTREGS gap is a convention which helps the in-kernel unwinder

arch/x86/kernel/sev-es-shared.c

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
* and is included directly into both code-bases.
1010
*/
1111

12-
static void __maybe_unused sev_es_terminate(unsigned int reason)
12+
static void sev_es_terminate(unsigned int reason)
1313
{
1414
u64 val = GHCB_SEV_TERMINATE;
1515

@@ -27,7 +27,7 @@ static void __maybe_unused sev_es_terminate(unsigned int reason)
2727
asm volatile("hlt\n" : : : "memory");
2828
}
2929

30-
static bool __maybe_unused sev_es_negotiate_protocol(void)
30+
static bool sev_es_negotiate_protocol(void)
3131
{
3232
u64 val;
3333

@@ -46,7 +46,7 @@ static bool __maybe_unused sev_es_negotiate_protocol(void)
4646
return true;
4747
}
4848

49-
static void __maybe_unused vc_ghcb_invalidate(struct ghcb *ghcb)
49+
static __always_inline void vc_ghcb_invalidate(struct ghcb *ghcb)
5050
{
5151
memset(ghcb->save.valid_bitmap, 0, sizeof(ghcb->save.valid_bitmap));
5252
}
@@ -58,9 +58,9 @@ static bool vc_decoding_needed(unsigned long exit_code)
5858
exit_code <= SVM_EXIT_LAST_EXCP);
5959
}
6060

61-
static enum es_result __maybe_unused vc_init_em_ctxt(struct es_em_ctxt *ctxt,
62-
struct pt_regs *regs,
63-
unsigned long exit_code)
61+
static enum es_result vc_init_em_ctxt(struct es_em_ctxt *ctxt,
62+
struct pt_regs *regs,
63+
unsigned long exit_code)
6464
{
6565
enum es_result ret = ES_OK;
6666

@@ -73,7 +73,7 @@ static enum es_result __maybe_unused vc_init_em_ctxt(struct es_em_ctxt *ctxt,
7373
return ret;
7474
}
7575

76-
static void __maybe_unused vc_finish_insn(struct es_em_ctxt *ctxt)
76+
static void vc_finish_insn(struct es_em_ctxt *ctxt)
7777
{
7878
ctxt->regs->ip += ctxt->insn.length;
7979
}

arch/x86/kernel/sev-es.c

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
* Author: Joerg Roedel <[email protected]>
88
*/
99

10+
#include <linux/sched/debug.h> /* For show_regs() */
1011
#include <linux/kernel.h>
12+
#include <linux/printk.h>
1113
#include <linux/mm.h>
1214

1315
#include <asm/sev-es.h>
@@ -18,6 +20,18 @@
1820
#include <asm/trapnr.h>
1921
#include <asm/svm.h>
2022

23+
/* For early boot hypervisor communication in SEV-ES enabled guests */
24+
static struct ghcb boot_ghcb_page __bss_decrypted __aligned(PAGE_SIZE);
25+
26+
/*
27+
* Needs to be in the .data section because we need it NULL before bss is
28+
* cleared
29+
*/
30+
static struct ghcb __initdata *boot_ghcb;
31+
32+
/* Needed in vc_early_forward_exception */
33+
void do_early_exception(struct pt_regs *regs, int trapnr);
34+
2135
static inline u64 sev_es_rd_ghcb_msr(void)
2236
{
2337
return __rdmsr(MSR_AMD64_SEV_ES_GHCB);
@@ -161,3 +175,105 @@ static enum es_result vc_read_mem(struct es_em_ctxt *ctxt,
161175

162176
/* Include code shared with pre-decompression boot stage */
163177
#include "sev-es-shared.c"
178+
179+
/*
180+
* This function runs on the first #VC exception after the kernel
181+
* switched to virtual addresses.
182+
*/
183+
static bool __init sev_es_setup_ghcb(void)
184+
{
185+
/* First make sure the hypervisor talks a supported protocol. */
186+
if (!sev_es_negotiate_protocol())
187+
return false;
188+
189+
/*
190+
* Clear the boot_ghcb. The first exception comes in before the bss
191+
* section is cleared.
192+
*/
193+
memset(&boot_ghcb_page, 0, PAGE_SIZE);
194+
195+
/* Alright - Make the boot-ghcb public */
196+
boot_ghcb = &boot_ghcb_page;
197+
198+
return true;
199+
}
200+
201+
static void __init vc_early_forward_exception(struct es_em_ctxt *ctxt)
202+
{
203+
int trapnr = ctxt->fi.vector;
204+
205+
if (trapnr == X86_TRAP_PF)
206+
native_write_cr2(ctxt->fi.cr2);
207+
208+
ctxt->regs->orig_ax = ctxt->fi.error_code;
209+
do_early_exception(ctxt->regs, trapnr);
210+
}
211+
212+
static enum es_result vc_handle_exitcode(struct es_em_ctxt *ctxt,
213+
struct ghcb *ghcb,
214+
unsigned long exit_code)
215+
{
216+
enum es_result result;
217+
218+
switch (exit_code) {
219+
default:
220+
/*
221+
* Unexpected #VC exception
222+
*/
223+
result = ES_UNSUPPORTED;
224+
}
225+
226+
return result;
227+
}
228+
229+
bool __init handle_vc_boot_ghcb(struct pt_regs *regs)
230+
{
231+
unsigned long exit_code = regs->orig_ax;
232+
struct es_em_ctxt ctxt;
233+
enum es_result result;
234+
235+
/* Do initial setup or terminate the guest */
236+
if (unlikely(boot_ghcb == NULL && !sev_es_setup_ghcb()))
237+
sev_es_terminate(GHCB_SEV_ES_REASON_GENERAL_REQUEST);
238+
239+
vc_ghcb_invalidate(boot_ghcb);
240+
241+
result = vc_init_em_ctxt(&ctxt, regs, exit_code);
242+
if (result == ES_OK)
243+
result = vc_handle_exitcode(&ctxt, boot_ghcb, exit_code);
244+
245+
/* Done - now check the result */
246+
switch (result) {
247+
case ES_OK:
248+
vc_finish_insn(&ctxt);
249+
break;
250+
case ES_UNSUPPORTED:
251+
early_printk("PANIC: Unsupported exit-code 0x%02lx in early #VC exception (IP: 0x%lx)\n",
252+
exit_code, regs->ip);
253+
goto fail;
254+
case ES_VMM_ERROR:
255+
early_printk("PANIC: Failure in communication with VMM (exit-code 0x%02lx IP: 0x%lx)\n",
256+
exit_code, regs->ip);
257+
goto fail;
258+
case ES_DECODE_FAILED:
259+
early_printk("PANIC: Failed to decode instruction (exit-code 0x%02lx IP: 0x%lx)\n",
260+
exit_code, regs->ip);
261+
goto fail;
262+
case ES_EXCEPTION:
263+
vc_early_forward_exception(&ctxt);
264+
break;
265+
case ES_RETRY:
266+
/* Nothing to do */
267+
break;
268+
default:
269+
BUG();
270+
}
271+
272+
return true;
273+
274+
fail:
275+
show_regs(regs);
276+
277+
while (true)
278+
halt();
279+
}

arch/x86/mm/extable.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <xen/xen.h>
66

77
#include <asm/fpu/internal.h>
8+
#include <asm/sev-es.h>
89
#include <asm/traps.h>
910
#include <asm/kdebug.h>
1011

0 commit comments

Comments
 (0)