Commit 7de7ac8d authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'x86_urgent_for_v5.13_rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull x86 fixes from Borislav Petkov:

 - Fix how SEV handles MMIO accesses by forwarding potential page faults
   instead of killing the machine and by using the accessors with the
   exact functionality needed when accessing memory.

 - Fix a confusion with Clang LTO compiler switches passed to the it

 - Handle the case gracefully when VMGEXIT has been executed in
   userspace

* tag 'x86_urgent_for_v5.13_rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  x86/sev-es: Use __put_user()/__get_user() for data accesses
  x86/sev-es: Forward page-faults which happen during emulation
  x86/sev-es: Don't return NULL from sev_es_get_ghcb()
  x86/build: Fix location of '-plugin-opt=' flags
  x86/sev-es: Invalidate the GHCB after completing VMGEXIT
  x86/sev-es: Move sev_es_put_ghcb() in prep for follow on patch
parents 28ceac69 4954f5b8
...@@ -178,11 +178,6 @@ ifeq ($(ACCUMULATE_OUTGOING_ARGS), 1) ...@@ -178,11 +178,6 @@ ifeq ($(ACCUMULATE_OUTGOING_ARGS), 1)
KBUILD_CFLAGS += $(call cc-option,-maccumulate-outgoing-args,) KBUILD_CFLAGS += $(call cc-option,-maccumulate-outgoing-args,)
endif endif
ifdef CONFIG_LTO_CLANG
KBUILD_LDFLAGS += -plugin-opt=-code-model=kernel \
-plugin-opt=-stack-alignment=$(if $(CONFIG_X86_32),4,8)
endif
# Workaround for a gcc prelease that unfortunately was shipped in a suse release # Workaround for a gcc prelease that unfortunately was shipped in a suse release
KBUILD_CFLAGS += -Wno-sign-compare KBUILD_CFLAGS += -Wno-sign-compare
# #
...@@ -202,7 +197,12 @@ ifdef CONFIG_RETPOLINE ...@@ -202,7 +197,12 @@ ifdef CONFIG_RETPOLINE
endif endif
endif endif
KBUILD_LDFLAGS := -m elf_$(UTS_MACHINE) KBUILD_LDFLAGS += -m elf_$(UTS_MACHINE)
ifdef CONFIG_LTO_CLANG
KBUILD_LDFLAGS += -plugin-opt=-code-model=kernel \
-plugin-opt=-stack-alignment=$(if $(CONFIG_X86_32),4,8)
endif
ifdef CONFIG_X86_NEED_RELOCS ifdef CONFIG_X86_NEED_RELOCS
LDFLAGS_vmlinux := --emit-relocs --discard-none LDFLAGS_vmlinux := --emit-relocs --discard-none
......
...@@ -63,6 +63,7 @@ static bool sev_es_negotiate_protocol(void) ...@@ -63,6 +63,7 @@ static bool sev_es_negotiate_protocol(void)
static __always_inline void vc_ghcb_invalidate(struct ghcb *ghcb) static __always_inline void vc_ghcb_invalidate(struct ghcb *ghcb)
{ {
ghcb->save.sw_exit_code = 0;
memset(ghcb->save.valid_bitmap, 0, sizeof(ghcb->save.valid_bitmap)); memset(ghcb->save.valid_bitmap, 0, sizeof(ghcb->save.valid_bitmap));
} }
......
...@@ -203,8 +203,18 @@ static __always_inline struct ghcb *sev_es_get_ghcb(struct ghcb_state *state) ...@@ -203,8 +203,18 @@ static __always_inline struct ghcb *sev_es_get_ghcb(struct ghcb_state *state)
if (unlikely(data->ghcb_active)) { if (unlikely(data->ghcb_active)) {
/* GHCB is already in use - save its contents */ /* GHCB is already in use - save its contents */
if (unlikely(data->backup_ghcb_active)) if (unlikely(data->backup_ghcb_active)) {
return NULL; /*
* Backup-GHCB is also already in use. There is no way
* to continue here so just kill the machine. To make
* panic() work, mark GHCBs inactive so that messages
* can be printed out.
*/
data->ghcb_active = false;
data->backup_ghcb_active = false;
panic("Unable to handle #VC exception! GHCB and Backup GHCB are already in use");
}
/* Mark backup_ghcb active before writing to it */ /* Mark backup_ghcb active before writing to it */
data->backup_ghcb_active = true; data->backup_ghcb_active = true;
...@@ -221,24 +231,6 @@ static __always_inline struct ghcb *sev_es_get_ghcb(struct ghcb_state *state) ...@@ -221,24 +231,6 @@ static __always_inline struct ghcb *sev_es_get_ghcb(struct ghcb_state *state)
return ghcb; return ghcb;
} }
static __always_inline void sev_es_put_ghcb(struct ghcb_state *state)
{
struct sev_es_runtime_data *data;
struct ghcb *ghcb;
data = this_cpu_read(runtime_data);
ghcb = &data->ghcb_page;
if (state->ghcb) {
/* Restore GHCB from Backup */
*ghcb = *state->ghcb;
data->backup_ghcb_active = false;
state->ghcb = NULL;
} else {
data->ghcb_active = false;
}
}
/* Needed in vc_early_forward_exception */ /* Needed in vc_early_forward_exception */
void do_early_exception(struct pt_regs *regs, int trapnr); void do_early_exception(struct pt_regs *regs, int trapnr);
...@@ -323,31 +315,44 @@ static enum es_result vc_write_mem(struct es_em_ctxt *ctxt, ...@@ -323,31 +315,44 @@ static enum es_result vc_write_mem(struct es_em_ctxt *ctxt,
u16 d2; u16 d2;
u8 d1; u8 d1;
/* If instruction ran in kernel mode and the I/O buffer is in kernel space */ /*
if (!user_mode(ctxt->regs) && !access_ok(target, size)) { * This function uses __put_user() independent of whether kernel or user
memcpy(dst, buf, size); * memory is accessed. This works fine because __put_user() does no
return ES_OK; * sanity checks of the pointer being accessed. All that it does is
} * to report when the access failed.
*
* Also, this function runs in atomic context, so __put_user() is not
* allowed to sleep. The page-fault handler detects that it is running
* in atomic context and will not try to take mmap_sem and handle the
* fault, so additional pagefault_enable()/disable() calls are not
* needed.
*
* The access can't be done via copy_to_user() here because
* vc_write_mem() must not use string instructions to access unsafe
* memory. The reason is that MOVS is emulated by the #VC handler by
* splitting the move up into a read and a write and taking a nested #VC
* exception on whatever of them is the MMIO access. Using string
* instructions here would cause infinite nesting.
*/
switch (size) { switch (size) {
case 1: case 1:
memcpy(&d1, buf, 1); memcpy(&d1, buf, 1);
if (put_user(d1, target)) if (__put_user(d1, target))
goto fault; goto fault;
break; break;
case 2: case 2:
memcpy(&d2, buf, 2); memcpy(&d2, buf, 2);
if (put_user(d2, target)) if (__put_user(d2, target))
goto fault; goto fault;
break; break;
case 4: case 4:
memcpy(&d4, buf, 4); memcpy(&d4, buf, 4);
if (put_user(d4, target)) if (__put_user(d4, target))
goto fault; goto fault;
break; break;
case 8: case 8:
memcpy(&d8, buf, 8); memcpy(&d8, buf, 8);
if (put_user(d8, target)) if (__put_user(d8, target))
goto fault; goto fault;
break; break;
default: default:
...@@ -378,30 +383,43 @@ static enum es_result vc_read_mem(struct es_em_ctxt *ctxt, ...@@ -378,30 +383,43 @@ static enum es_result vc_read_mem(struct es_em_ctxt *ctxt,
u16 d2; u16 d2;
u8 d1; u8 d1;
/* If instruction ran in kernel mode and the I/O buffer is in kernel space */ /*
if (!user_mode(ctxt->regs) && !access_ok(s, size)) { * This function uses __get_user() independent of whether kernel or user
memcpy(buf, src, size); * memory is accessed. This works fine because __get_user() does no
return ES_OK; * sanity checks of the pointer being accessed. All that it does is
} * to report when the access failed.
*
* Also, this function runs in atomic context, so __get_user() is not
* allowed to sleep. The page-fault handler detects that it is running
* in atomic context and will not try to take mmap_sem and handle the
* fault, so additional pagefault_enable()/disable() calls are not
* needed.
*
* The access can't be done via copy_from_user() here because
* vc_read_mem() must not use string instructions to access unsafe
* memory. The reason is that MOVS is emulated by the #VC handler by
* splitting the move up into a read and a write and taking a nested #VC
* exception on whatever of them is the MMIO access. Using string
* instructions here would cause infinite nesting.
*/
switch (size) { switch (size) {
case 1: case 1:
if (get_user(d1, s)) if (__get_user(d1, s))
goto fault; goto fault;
memcpy(buf, &d1, 1); memcpy(buf, &d1, 1);
break; break;
case 2: case 2:
if (get_user(d2, s)) if (__get_user(d2, s))
goto fault; goto fault;
memcpy(buf, &d2, 2); memcpy(buf, &d2, 2);
break; break;
case 4: case 4:
if (get_user(d4, s)) if (__get_user(d4, s))
goto fault; goto fault;
memcpy(buf, &d4, 4); memcpy(buf, &d4, 4);
break; break;
case 8: case 8:
if (get_user(d8, s)) if (__get_user(d8, s))
goto fault; goto fault;
memcpy(buf, &d8, 8); memcpy(buf, &d8, 8);
break; break;
...@@ -461,6 +479,29 @@ static enum es_result vc_slow_virt_to_phys(struct ghcb *ghcb, struct es_em_ctxt ...@@ -461,6 +479,29 @@ static enum es_result vc_slow_virt_to_phys(struct ghcb *ghcb, struct es_em_ctxt
/* Include code shared with pre-decompression boot stage */ /* Include code shared with pre-decompression boot stage */
#include "sev-shared.c" #include "sev-shared.c"
static __always_inline void sev_es_put_ghcb(struct ghcb_state *state)
{
struct sev_es_runtime_data *data;
struct ghcb *ghcb;
data = this_cpu_read(runtime_data);
ghcb = &data->ghcb_page;
if (state->ghcb) {
/* Restore GHCB from Backup */
*ghcb = *state->ghcb;
data->backup_ghcb_active = false;
state->ghcb = NULL;
} else {
/*
* Invalidate the GHCB so a VMGEXIT instruction issued
* from userspace won't appear to be valid.
*/
vc_ghcb_invalidate(ghcb);
data->ghcb_active = false;
}
}
void noinstr __sev_es_nmi_complete(void) void noinstr __sev_es_nmi_complete(void)
{ {
struct ghcb_state state; struct ghcb_state state;
...@@ -1255,6 +1296,10 @@ static __always_inline void vc_forward_exception(struct es_em_ctxt *ctxt) ...@@ -1255,6 +1296,10 @@ static __always_inline void vc_forward_exception(struct es_em_ctxt *ctxt)
case X86_TRAP_UD: case X86_TRAP_UD:
exc_invalid_op(ctxt->regs); exc_invalid_op(ctxt->regs);
break; break;
case X86_TRAP_PF:
write_cr2(ctxt->fi.cr2);
exc_page_fault(ctxt->regs, error_code);
break;
case X86_TRAP_AC: case X86_TRAP_AC:
exc_alignment_check(ctxt->regs, error_code); exc_alignment_check(ctxt->regs, error_code);
break; break;
...@@ -1284,7 +1329,6 @@ static __always_inline bool on_vc_fallback_stack(struct pt_regs *regs) ...@@ -1284,7 +1329,6 @@ static __always_inline bool on_vc_fallback_stack(struct pt_regs *regs)
*/ */
DEFINE_IDTENTRY_VC_SAFE_STACK(exc_vmm_communication) DEFINE_IDTENTRY_VC_SAFE_STACK(exc_vmm_communication)
{ {
struct sev_es_runtime_data *data = this_cpu_read(runtime_data);
irqentry_state_t irq_state; irqentry_state_t irq_state;
struct ghcb_state state; struct ghcb_state state;
struct es_em_ctxt ctxt; struct es_em_ctxt ctxt;
...@@ -1310,16 +1354,6 @@ DEFINE_IDTENTRY_VC_SAFE_STACK(exc_vmm_communication) ...@@ -1310,16 +1354,6 @@ DEFINE_IDTENTRY_VC_SAFE_STACK(exc_vmm_communication)
*/ */
ghcb = sev_es_get_ghcb(&state); ghcb = sev_es_get_ghcb(&state);
if (!ghcb) {
/*
* Mark GHCBs inactive so that panic() is able to print the
* message.
*/
data->ghcb_active = false;
data->backup_ghcb_active = false;
panic("Unable to handle #VC exception! GHCB and Backup GHCB are already in use");
}
vc_ghcb_invalidate(ghcb); vc_ghcb_invalidate(ghcb);
result = vc_init_em_ctxt(&ctxt, regs, error_code); result = vc_init_em_ctxt(&ctxt, regs, error_code);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment