Commit f41f0824 authored by Andy Lutomirski's avatar Andy Lutomirski Committed by Thomas Gleixner
Browse files

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: 4c0dcd83 ("x86/entry: Implement user mode C entry points for #DB and #MCE")
Signed-off-by: default avatarAndy Lutomirski <luto@kernel.org>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Acked-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lkml.kernel.org/r/4163e733cce0b41658e252c6c6b3464f33fdff17.1593795633.git.luto@kernel.org
parent 3c73b81a
Loading
Loading
Loading
Loading
+6 −18
Original line number Diff line number Diff line
@@ -398,18 +398,6 @@ __visible noinstr void func(struct pt_regs *regs, \
#define DEFINE_IDTENTRY_DEBUG		DEFINE_IDTENTRY_IST
#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__ */

/*
@@ -469,10 +457,6 @@ __visible noinstr void func(struct pt_regs *regs, \
/* No ASM code emitted for NMI */
#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
 * packed into 8 bytes.
@@ -570,11 +554,15 @@ DECLARE_IDTENTRY_MCE(X86_TRAP_MC, exc_machine_check);

/* 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 */
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 */
DECLARE_IDTENTRY_DF(X86_TRAP_DF,	exc_double_fault);
+12 −0
Original line number Diff line number Diff line
@@ -865,6 +865,12 @@ static __always_inline void exc_debug_kernel(struct pt_regs *regs,
	instrumentation_begin();
	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
	 * 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,
					   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);
	instrumentation_begin();

+24 −4
Original line number Diff line number Diff line
@@ -598,6 +598,26 @@ static void xen_write_ldt_entry(struct desc_struct *dt, int entrynum,
}

#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 {
	void (*orig)(void);
	void (*xen)(void);
@@ -609,18 +629,18 @@ struct trap_array_entry {
	.xen		= xen_asm_##func,		\
	.ist_okay	= ist_ok }

#define TRAP_ENTRY_REDIR(func, xenfunc, ist_ok) {	\
#define TRAP_ENTRY_REDIR(func, ist_ok) {		\
	.orig		= asm_##func,			\
	.xen		= xen_asm_##xenfunc,		\
	.xen		= xen_asm_xenpv_##func,		\
	.ist_okay	= ist_ok }

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  ),
#ifdef CONFIG_X86_MCE
	TRAP_ENTRY(exc_machine_check,			true  ),
#endif
	TRAP_ENTRY_REDIR(exc_nmi, exc_xennmi,		true  ),
	TRAP_ENTRY_REDIR(exc_nmi,			true  ),
	TRAP_ENTRY(exc_int3,				false ),
	TRAP_ENTRY(exc_overflow,			false ),
#ifdef CONFIG_IA32_EMULATION
+2 −3
Original line number Diff line number Diff line
@@ -29,10 +29,9 @@ _ASM_NOKPROBE(xen_\name)
.endm

xen_pv_trap asm_exc_divide_error
xen_pv_trap asm_exc_debug
xen_pv_trap asm_exc_xendebug
xen_pv_trap asm_xenpv_exc_debug
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_bounds
xen_pv_trap asm_exc_invalid_op