x86/entry/xen: Route #DB correctly on Xen PV
On Xen PV, #DB doesn't use IST. It still needs to be correctly routed
depending on whether it came from user or kernel mode.
Get rid of DECLARE/DEFINE_IDTENTRY_XEN -- it was too hard to follow the
logic. Instead, route #DB and NMI through DECLARE/DEFINE_IDTENTRY_RAW on
Xen, and do the right thing for #DB. Also add more warnings to the
exc_debug* handlers to make this type of failure more obvious.
This fixes various forms of corruption that happen when usermode
triggers #DB on Xen PV.
Fixes: 4c0dcd8350
("x86/entry: Implement user mode C entry points for #DB and #MCE")
Signed-off-by: Andy Lutomirski <luto@kernel.org>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lkml.kernel.org/r/4163e733cce0b41658e252c6c6b3464f33fdff17.1593795633.git.luto@kernel.org
This commit is contained in:
parent
3c73b81a91
commit
f41f082422
4 changed files with 44 additions and 25 deletions
|
@ -398,18 +398,6 @@ __visible noinstr void func(struct pt_regs *regs, \
|
||||||
#define DEFINE_IDTENTRY_DEBUG DEFINE_IDTENTRY_IST
|
#define DEFINE_IDTENTRY_DEBUG DEFINE_IDTENTRY_IST
|
||||||
#define DEFINE_IDTENTRY_DEBUG_USER DEFINE_IDTENTRY_NOIST
|
#define DEFINE_IDTENTRY_DEBUG_USER DEFINE_IDTENTRY_NOIST
|
||||||
|
|
||||||
/**
|
|
||||||
* DECLARE_IDTENTRY_XEN - Declare functions for XEN redirect IDT entry points
|
|
||||||
* @vector: Vector number (ignored for C)
|
|
||||||
* @func: Function name of the entry point
|
|
||||||
*
|
|
||||||
* Used for xennmi and xendebug redirections. No DEFINE as this is all ASM
|
|
||||||
* indirection magic.
|
|
||||||
*/
|
|
||||||
#define DECLARE_IDTENTRY_XEN(vector, func) \
|
|
||||||
asmlinkage void xen_asm_exc_xen##func(void); \
|
|
||||||
asmlinkage void asm_exc_xen##func(void)
|
|
||||||
|
|
||||||
#else /* !__ASSEMBLY__ */
|
#else /* !__ASSEMBLY__ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -469,10 +457,6 @@ __visible noinstr void func(struct pt_regs *regs, \
|
||||||
/* No ASM code emitted for NMI */
|
/* No ASM code emitted for NMI */
|
||||||
#define DECLARE_IDTENTRY_NMI(vector, func)
|
#define DECLARE_IDTENTRY_NMI(vector, func)
|
||||||
|
|
||||||
/* XEN NMI and DB wrapper */
|
|
||||||
#define DECLARE_IDTENTRY_XEN(vector, func) \
|
|
||||||
idtentry vector asm_exc_xen##func exc_##func has_error_code=0
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ASM code to emit the common vector entry stubs where each stub is
|
* ASM code to emit the common vector entry stubs where each stub is
|
||||||
* packed into 8 bytes.
|
* packed into 8 bytes.
|
||||||
|
@ -570,11 +554,15 @@ DECLARE_IDTENTRY_MCE(X86_TRAP_MC, exc_machine_check);
|
||||||
|
|
||||||
/* NMI */
|
/* NMI */
|
||||||
DECLARE_IDTENTRY_NMI(X86_TRAP_NMI, exc_nmi);
|
DECLARE_IDTENTRY_NMI(X86_TRAP_NMI, exc_nmi);
|
||||||
DECLARE_IDTENTRY_XEN(X86_TRAP_NMI, nmi);
|
#ifdef CONFIG_XEN_PV
|
||||||
|
DECLARE_IDTENTRY_RAW(X86_TRAP_NMI, xenpv_exc_nmi);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* #DB */
|
/* #DB */
|
||||||
DECLARE_IDTENTRY_DEBUG(X86_TRAP_DB, exc_debug);
|
DECLARE_IDTENTRY_DEBUG(X86_TRAP_DB, exc_debug);
|
||||||
DECLARE_IDTENTRY_XEN(X86_TRAP_DB, debug);
|
#ifdef CONFIG_XEN_PV
|
||||||
|
DECLARE_IDTENTRY_RAW(X86_TRAP_DB, xenpv_exc_debug);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* #DF */
|
/* #DF */
|
||||||
DECLARE_IDTENTRY_DF(X86_TRAP_DF, exc_double_fault);
|
DECLARE_IDTENTRY_DF(X86_TRAP_DF, exc_double_fault);
|
||||||
|
|
|
@ -865,6 +865,12 @@ static __always_inline void exc_debug_kernel(struct pt_regs *regs,
|
||||||
instrumentation_begin();
|
instrumentation_begin();
|
||||||
trace_hardirqs_off_finish();
|
trace_hardirqs_off_finish();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If something gets miswired and we end up here for a user mode
|
||||||
|
* #DB, we will malfunction.
|
||||||
|
*/
|
||||||
|
WARN_ON_ONCE(user_mode(regs));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Catch SYSENTER with TF set and clear DR_STEP. If this hit a
|
* Catch SYSENTER with TF set and clear DR_STEP. If this hit a
|
||||||
* watchpoint at the same time then that will still be handled.
|
* watchpoint at the same time then that will still be handled.
|
||||||
|
@ -883,6 +889,12 @@ static __always_inline void exc_debug_kernel(struct pt_regs *regs,
|
||||||
static __always_inline void exc_debug_user(struct pt_regs *regs,
|
static __always_inline void exc_debug_user(struct pt_regs *regs,
|
||||||
unsigned long dr6)
|
unsigned long dr6)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* If something gets miswired and we end up here for a kernel mode
|
||||||
|
* #DB, we will malfunction.
|
||||||
|
*/
|
||||||
|
WARN_ON_ONCE(!user_mode(regs));
|
||||||
|
|
||||||
idtentry_enter_user(regs);
|
idtentry_enter_user(regs);
|
||||||
instrumentation_begin();
|
instrumentation_begin();
|
||||||
|
|
||||||
|
|
|
@ -598,6 +598,26 @@ static void xen_write_ldt_entry(struct desc_struct *dt, int entrynum,
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_X86_64
|
#ifdef CONFIG_X86_64
|
||||||
|
void noist_exc_debug(struct pt_regs *regs);
|
||||||
|
|
||||||
|
DEFINE_IDTENTRY_RAW(xenpv_exc_nmi)
|
||||||
|
{
|
||||||
|
/* On Xen PV, NMI doesn't use IST. The C part is the sane as native. */
|
||||||
|
exc_nmi(regs);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_IDTENTRY_RAW(xenpv_exc_debug)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* There's no IST on Xen PV, but we still need to dispatch
|
||||||
|
* to the correct handler.
|
||||||
|
*/
|
||||||
|
if (user_mode(regs))
|
||||||
|
noist_exc_debug(regs);
|
||||||
|
else
|
||||||
|
exc_debug(regs);
|
||||||
|
}
|
||||||
|
|
||||||
struct trap_array_entry {
|
struct trap_array_entry {
|
||||||
void (*orig)(void);
|
void (*orig)(void);
|
||||||
void (*xen)(void);
|
void (*xen)(void);
|
||||||
|
@ -609,18 +629,18 @@ struct trap_array_entry {
|
||||||
.xen = xen_asm_##func, \
|
.xen = xen_asm_##func, \
|
||||||
.ist_okay = ist_ok }
|
.ist_okay = ist_ok }
|
||||||
|
|
||||||
#define TRAP_ENTRY_REDIR(func, xenfunc, ist_ok) { \
|
#define TRAP_ENTRY_REDIR(func, ist_ok) { \
|
||||||
.orig = asm_##func, \
|
.orig = asm_##func, \
|
||||||
.xen = xen_asm_##xenfunc, \
|
.xen = xen_asm_xenpv_##func, \
|
||||||
.ist_okay = ist_ok }
|
.ist_okay = ist_ok }
|
||||||
|
|
||||||
static struct trap_array_entry trap_array[] = {
|
static struct trap_array_entry trap_array[] = {
|
||||||
TRAP_ENTRY_REDIR(exc_debug, exc_xendebug, true ),
|
TRAP_ENTRY_REDIR(exc_debug, true ),
|
||||||
TRAP_ENTRY(exc_double_fault, true ),
|
TRAP_ENTRY(exc_double_fault, true ),
|
||||||
#ifdef CONFIG_X86_MCE
|
#ifdef CONFIG_X86_MCE
|
||||||
TRAP_ENTRY(exc_machine_check, true ),
|
TRAP_ENTRY(exc_machine_check, true ),
|
||||||
#endif
|
#endif
|
||||||
TRAP_ENTRY_REDIR(exc_nmi, exc_xennmi, true ),
|
TRAP_ENTRY_REDIR(exc_nmi, true ),
|
||||||
TRAP_ENTRY(exc_int3, false ),
|
TRAP_ENTRY(exc_int3, false ),
|
||||||
TRAP_ENTRY(exc_overflow, false ),
|
TRAP_ENTRY(exc_overflow, false ),
|
||||||
#ifdef CONFIG_IA32_EMULATION
|
#ifdef CONFIG_IA32_EMULATION
|
||||||
|
|
|
@ -29,10 +29,9 @@ _ASM_NOKPROBE(xen_\name)
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
xen_pv_trap asm_exc_divide_error
|
xen_pv_trap asm_exc_divide_error
|
||||||
xen_pv_trap asm_exc_debug
|
xen_pv_trap asm_xenpv_exc_debug
|
||||||
xen_pv_trap asm_exc_xendebug
|
|
||||||
xen_pv_trap asm_exc_int3
|
xen_pv_trap asm_exc_int3
|
||||||
xen_pv_trap asm_exc_xennmi
|
xen_pv_trap asm_xenpv_exc_nmi
|
||||||
xen_pv_trap asm_exc_overflow
|
xen_pv_trap asm_exc_overflow
|
||||||
xen_pv_trap asm_exc_bounds
|
xen_pv_trap asm_exc_bounds
|
||||||
xen_pv_trap asm_exc_invalid_op
|
xen_pv_trap asm_exc_invalid_op
|
||||||
|
|
Loading…
Add table
Reference in a new issue