|
@@ -55,6 +55,7 @@
|
|
#include <asm/i387.h>
|
|
#include <asm/i387.h>
|
|
#include <asm/fpu-internal.h>
|
|
#include <asm/fpu-internal.h>
|
|
#include <asm/mce.h>
|
|
#include <asm/mce.h>
|
|
|
|
+#include <asm/rcu.h>
|
|
|
|
|
|
#include <asm/mach_traps.h>
|
|
#include <asm/mach_traps.h>
|
|
|
|
|
|
@@ -180,11 +181,15 @@ vm86_trap:
|
|
#define DO_ERROR(trapnr, signr, str, name) \
|
|
#define DO_ERROR(trapnr, signr, str, name) \
|
|
dotraplinkage void do_##name(struct pt_regs *regs, long error_code) \
|
|
dotraplinkage void do_##name(struct pt_regs *regs, long error_code) \
|
|
{ \
|
|
{ \
|
|
- if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \
|
|
|
|
- == NOTIFY_STOP) \
|
|
|
|
|
|
+ exception_enter(regs); \
|
|
|
|
+ if (notify_die(DIE_TRAP, str, regs, error_code, \
|
|
|
|
+ trapnr, signr) == NOTIFY_STOP) { \
|
|
|
|
+ exception_exit(regs); \
|
|
return; \
|
|
return; \
|
|
|
|
+ } \
|
|
conditional_sti(regs); \
|
|
conditional_sti(regs); \
|
|
do_trap(trapnr, signr, str, regs, error_code, NULL); \
|
|
do_trap(trapnr, signr, str, regs, error_code, NULL); \
|
|
|
|
+ exception_exit(regs); \
|
|
}
|
|
}
|
|
|
|
|
|
#define DO_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr) \
|
|
#define DO_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr) \
|
|
@@ -195,11 +200,15 @@ dotraplinkage void do_##name(struct pt_regs *regs, long error_code) \
|
|
info.si_errno = 0; \
|
|
info.si_errno = 0; \
|
|
info.si_code = sicode; \
|
|
info.si_code = sicode; \
|
|
info.si_addr = (void __user *)siaddr; \
|
|
info.si_addr = (void __user *)siaddr; \
|
|
- if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \
|
|
|
|
- == NOTIFY_STOP) \
|
|
|
|
|
|
+ exception_enter(regs); \
|
|
|
|
+ if (notify_die(DIE_TRAP, str, regs, error_code, \
|
|
|
|
+ trapnr, signr) == NOTIFY_STOP) { \
|
|
|
|
+ exception_exit(regs); \
|
|
return; \
|
|
return; \
|
|
|
|
+ } \
|
|
conditional_sti(regs); \
|
|
conditional_sti(regs); \
|
|
do_trap(trapnr, signr, str, regs, error_code, &info); \
|
|
do_trap(trapnr, signr, str, regs, error_code, &info); \
|
|
|
|
+ exception_exit(regs); \
|
|
}
|
|
}
|
|
|
|
|
|
DO_ERROR_INFO(X86_TRAP_DE, SIGFPE, "divide error", divide_error, FPE_INTDIV,
|
|
DO_ERROR_INFO(X86_TRAP_DE, SIGFPE, "divide error", divide_error, FPE_INTDIV,
|
|
@@ -222,12 +231,14 @@ DO_ERROR_INFO(X86_TRAP_AC, SIGBUS, "alignment check", alignment_check,
|
|
/* Runs on IST stack */
|
|
/* Runs on IST stack */
|
|
dotraplinkage void do_stack_segment(struct pt_regs *regs, long error_code)
|
|
dotraplinkage void do_stack_segment(struct pt_regs *regs, long error_code)
|
|
{
|
|
{
|
|
|
|
+ exception_enter(regs);
|
|
if (notify_die(DIE_TRAP, "stack segment", regs, error_code,
|
|
if (notify_die(DIE_TRAP, "stack segment", regs, error_code,
|
|
- X86_TRAP_SS, SIGBUS) == NOTIFY_STOP)
|
|
|
|
- return;
|
|
|
|
- preempt_conditional_sti(regs);
|
|
|
|
- do_trap(X86_TRAP_SS, SIGBUS, "stack segment", regs, error_code, NULL);
|
|
|
|
- preempt_conditional_cli(regs);
|
|
|
|
|
|
+ X86_TRAP_SS, SIGBUS) != NOTIFY_STOP) {
|
|
|
|
+ preempt_conditional_sti(regs);
|
|
|
|
+ do_trap(X86_TRAP_SS, SIGBUS, "stack segment", regs, error_code, NULL);
|
|
|
|
+ preempt_conditional_cli(regs);
|
|
|
|
+ }
|
|
|
|
+ exception_exit(regs);
|
|
}
|
|
}
|
|
|
|
|
|
dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code)
|
|
dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code)
|
|
@@ -235,6 +246,7 @@ dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code)
|
|
static const char str[] = "double fault";
|
|
static const char str[] = "double fault";
|
|
struct task_struct *tsk = current;
|
|
struct task_struct *tsk = current;
|
|
|
|
|
|
|
|
+ exception_enter(regs);
|
|
/* Return not checked because double check cannot be ignored */
|
|
/* Return not checked because double check cannot be ignored */
|
|
notify_die(DIE_TRAP, str, regs, error_code, X86_TRAP_DF, SIGSEGV);
|
|
notify_die(DIE_TRAP, str, regs, error_code, X86_TRAP_DF, SIGSEGV);
|
|
|
|
|
|
@@ -255,27 +267,28 @@ do_general_protection(struct pt_regs *regs, long error_code)
|
|
{
|
|
{
|
|
struct task_struct *tsk;
|
|
struct task_struct *tsk;
|
|
|
|
|
|
|
|
+ exception_enter(regs);
|
|
conditional_sti(regs);
|
|
conditional_sti(regs);
|
|
|
|
|
|
#ifdef CONFIG_X86_32
|
|
#ifdef CONFIG_X86_32
|
|
if (regs->flags & X86_VM_MASK) {
|
|
if (regs->flags & X86_VM_MASK) {
|
|
local_irq_enable();
|
|
local_irq_enable();
|
|
handle_vm86_fault((struct kernel_vm86_regs *) regs, error_code);
|
|
handle_vm86_fault((struct kernel_vm86_regs *) regs, error_code);
|
|
- return;
|
|
|
|
|
|
+ goto exit;
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
|
|
tsk = current;
|
|
tsk = current;
|
|
if (!user_mode(regs)) {
|
|
if (!user_mode(regs)) {
|
|
if (fixup_exception(regs))
|
|
if (fixup_exception(regs))
|
|
- return;
|
|
|
|
|
|
+ goto exit;
|
|
|
|
|
|
tsk->thread.error_code = error_code;
|
|
tsk->thread.error_code = error_code;
|
|
tsk->thread.trap_nr = X86_TRAP_GP;
|
|
tsk->thread.trap_nr = X86_TRAP_GP;
|
|
- if (!notify_die(DIE_GPF, "general protection fault", regs, error_code,
|
|
|
|
- X86_TRAP_GP, SIGSEGV) == NOTIFY_STOP)
|
|
|
|
|
|
+ if (notify_die(DIE_GPF, "general protection fault", regs, error_code,
|
|
|
|
+ X86_TRAP_GP, SIGSEGV) != NOTIFY_STOP)
|
|
die("general protection fault", regs, error_code);
|
|
die("general protection fault", regs, error_code);
|
|
- return;
|
|
|
|
|
|
+ goto exit;
|
|
}
|
|
}
|
|
|
|
|
|
tsk->thread.error_code = error_code;
|
|
tsk->thread.error_code = error_code;
|
|
@@ -291,7 +304,8 @@ do_general_protection(struct pt_regs *regs, long error_code)
|
|
}
|
|
}
|
|
|
|
|
|
force_sig(SIGSEGV, tsk);
|
|
force_sig(SIGSEGV, tsk);
|
|
- return;
|
|
|
|
|
|
+exit:
|
|
|
|
+ exception_exit(regs);
|
|
}
|
|
}
|
|
|
|
|
|
/* May run on IST stack. */
|
|
/* May run on IST stack. */
|
|
@@ -306,15 +320,16 @@ dotraplinkage void __kprobes notrace do_int3(struct pt_regs *regs, long error_co
|
|
ftrace_int3_handler(regs))
|
|
ftrace_int3_handler(regs))
|
|
return;
|
|
return;
|
|
#endif
|
|
#endif
|
|
|
|
+ exception_enter(regs);
|
|
#ifdef CONFIG_KGDB_LOW_LEVEL_TRAP
|
|
#ifdef CONFIG_KGDB_LOW_LEVEL_TRAP
|
|
if (kgdb_ll_trap(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP,
|
|
if (kgdb_ll_trap(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP,
|
|
SIGTRAP) == NOTIFY_STOP)
|
|
SIGTRAP) == NOTIFY_STOP)
|
|
- return;
|
|
|
|
|
|
+ goto exit;
|
|
#endif /* CONFIG_KGDB_LOW_LEVEL_TRAP */
|
|
#endif /* CONFIG_KGDB_LOW_LEVEL_TRAP */
|
|
|
|
|
|
if (notify_die(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP,
|
|
if (notify_die(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP,
|
|
SIGTRAP) == NOTIFY_STOP)
|
|
SIGTRAP) == NOTIFY_STOP)
|
|
- return;
|
|
|
|
|
|
+ goto exit;
|
|
|
|
|
|
/*
|
|
/*
|
|
* Let others (NMI) know that the debug stack is in use
|
|
* Let others (NMI) know that the debug stack is in use
|
|
@@ -325,6 +340,8 @@ dotraplinkage void __kprobes notrace do_int3(struct pt_regs *regs, long error_co
|
|
do_trap(X86_TRAP_BP, SIGTRAP, "int3", regs, error_code, NULL);
|
|
do_trap(X86_TRAP_BP, SIGTRAP, "int3", regs, error_code, NULL);
|
|
preempt_conditional_cli(regs);
|
|
preempt_conditional_cli(regs);
|
|
debug_stack_usage_dec();
|
|
debug_stack_usage_dec();
|
|
|
|
+exit:
|
|
|
|
+ exception_exit(regs);
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_X86_64
|
|
#ifdef CONFIG_X86_64
|
|
@@ -385,6 +402,8 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code)
|
|
unsigned long dr6;
|
|
unsigned long dr6;
|
|
int si_code;
|
|
int si_code;
|
|
|
|
|
|
|
|
+ exception_enter(regs);
|
|
|
|
+
|
|
get_debugreg(dr6, 6);
|
|
get_debugreg(dr6, 6);
|
|
|
|
|
|
/* Filter out all the reserved bits which are preset to 1 */
|
|
/* Filter out all the reserved bits which are preset to 1 */
|
|
@@ -400,7 +419,7 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code)
|
|
|
|
|
|
/* Catch kmemcheck conditions first of all! */
|
|
/* Catch kmemcheck conditions first of all! */
|
|
if ((dr6 & DR_STEP) && kmemcheck_trap(regs))
|
|
if ((dr6 & DR_STEP) && kmemcheck_trap(regs))
|
|
- return;
|
|
|
|
|
|
+ goto exit;
|
|
|
|
|
|
/* DR6 may or may not be cleared by the CPU */
|
|
/* DR6 may or may not be cleared by the CPU */
|
|
set_debugreg(0, 6);
|
|
set_debugreg(0, 6);
|
|
@@ -415,7 +434,7 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code)
|
|
|
|
|
|
if (notify_die(DIE_DEBUG, "debug", regs, PTR_ERR(&dr6), error_code,
|
|
if (notify_die(DIE_DEBUG, "debug", regs, PTR_ERR(&dr6), error_code,
|
|
SIGTRAP) == NOTIFY_STOP)
|
|
SIGTRAP) == NOTIFY_STOP)
|
|
- return;
|
|
|
|
|
|
+ goto exit;
|
|
|
|
|
|
/*
|
|
/*
|
|
* Let others (NMI) know that the debug stack is in use
|
|
* Let others (NMI) know that the debug stack is in use
|
|
@@ -431,7 +450,7 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code)
|
|
X86_TRAP_DB);
|
|
X86_TRAP_DB);
|
|
preempt_conditional_cli(regs);
|
|
preempt_conditional_cli(regs);
|
|
debug_stack_usage_dec();
|
|
debug_stack_usage_dec();
|
|
- return;
|
|
|
|
|
|
+ goto exit;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -452,7 +471,8 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code)
|
|
preempt_conditional_cli(regs);
|
|
preempt_conditional_cli(regs);
|
|
debug_stack_usage_dec();
|
|
debug_stack_usage_dec();
|
|
|
|
|
|
- return;
|
|
|
|
|
|
+exit:
|
|
|
|
+ exception_exit(regs);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -549,14 +569,17 @@ dotraplinkage void do_coprocessor_error(struct pt_regs *regs, long error_code)
|
|
#ifdef CONFIG_X86_32
|
|
#ifdef CONFIG_X86_32
|
|
ignore_fpu_irq = 1;
|
|
ignore_fpu_irq = 1;
|
|
#endif
|
|
#endif
|
|
-
|
|
|
|
|
|
+ exception_enter(regs);
|
|
math_error(regs, error_code, X86_TRAP_MF);
|
|
math_error(regs, error_code, X86_TRAP_MF);
|
|
|
|
+ exception_exit(regs);
|
|
}
|
|
}
|
|
|
|
|
|
dotraplinkage void
|
|
dotraplinkage void
|
|
do_simd_coprocessor_error(struct pt_regs *regs, long error_code)
|
|
do_simd_coprocessor_error(struct pt_regs *regs, long error_code)
|
|
{
|
|
{
|
|
|
|
+ exception_enter(regs);
|
|
math_error(regs, error_code, X86_TRAP_XF);
|
|
math_error(regs, error_code, X86_TRAP_XF);
|
|
|
|
+ exception_exit(regs);
|
|
}
|
|
}
|
|
|
|
|
|
dotraplinkage void
|
|
dotraplinkage void
|
|
@@ -623,6 +646,7 @@ EXPORT_SYMBOL_GPL(math_state_restore);
|
|
dotraplinkage void __kprobes
|
|
dotraplinkage void __kprobes
|
|
do_device_not_available(struct pt_regs *regs, long error_code)
|
|
do_device_not_available(struct pt_regs *regs, long error_code)
|
|
{
|
|
{
|
|
|
|
+ exception_enter(regs);
|
|
#ifdef CONFIG_MATH_EMULATION
|
|
#ifdef CONFIG_MATH_EMULATION
|
|
if (read_cr0() & X86_CR0_EM) {
|
|
if (read_cr0() & X86_CR0_EM) {
|
|
struct math_emu_info info = { };
|
|
struct math_emu_info info = { };
|
|
@@ -631,6 +655,7 @@ do_device_not_available(struct pt_regs *regs, long error_code)
|
|
|
|
|
|
info.regs = regs;
|
|
info.regs = regs;
|
|
math_emulate(&info);
|
|
math_emulate(&info);
|
|
|
|
+ exception_exit(regs);
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
@@ -638,12 +663,15 @@ do_device_not_available(struct pt_regs *regs, long error_code)
|
|
#ifdef CONFIG_X86_32
|
|
#ifdef CONFIG_X86_32
|
|
conditional_sti(regs);
|
|
conditional_sti(regs);
|
|
#endif
|
|
#endif
|
|
|
|
+ exception_exit(regs);
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_X86_32
|
|
#ifdef CONFIG_X86_32
|
|
dotraplinkage void do_iret_error(struct pt_regs *regs, long error_code)
|
|
dotraplinkage void do_iret_error(struct pt_regs *regs, long error_code)
|
|
{
|
|
{
|
|
siginfo_t info;
|
|
siginfo_t info;
|
|
|
|
+
|
|
|
|
+ exception_enter(regs);
|
|
local_irq_enable();
|
|
local_irq_enable();
|
|
|
|
|
|
info.si_signo = SIGILL;
|
|
info.si_signo = SIGILL;
|
|
@@ -651,10 +679,11 @@ dotraplinkage void do_iret_error(struct pt_regs *regs, long error_code)
|
|
info.si_code = ILL_BADSTK;
|
|
info.si_code = ILL_BADSTK;
|
|
info.si_addr = NULL;
|
|
info.si_addr = NULL;
|
|
if (notify_die(DIE_TRAP, "iret exception", regs, error_code,
|
|
if (notify_die(DIE_TRAP, "iret exception", regs, error_code,
|
|
- X86_TRAP_IRET, SIGILL) == NOTIFY_STOP)
|
|
|
|
- return;
|
|
|
|
- do_trap(X86_TRAP_IRET, SIGILL, "iret exception", regs, error_code,
|
|
|
|
- &info);
|
|
|
|
|
|
+ X86_TRAP_IRET, SIGILL) != NOTIFY_STOP) {
|
|
|
|
+ do_trap(X86_TRAP_IRET, SIGILL, "iret exception", regs, error_code,
|
|
|
|
+ &info);
|
|
|
|
+ }
|
|
|
|
+ exception_exit(regs);
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
|