diff options
Diffstat (limited to 'arch/x86/kernel/traps.c')
| -rw-r--r-- | arch/x86/kernel/traps.c | 174 | 
1 files changed, 99 insertions, 75 deletions
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index b481341c936..8276dc6794c 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -55,6 +55,7 @@  #include <asm/i387.h>  #include <asm/fpu-internal.h>  #include <asm/mce.h> +#include <asm/rcu.h>  #include <asm/mach_traps.h> @@ -107,30 +108,45 @@ static inline void preempt_conditional_cli(struct pt_regs *regs)  	dec_preempt_count();  } -static void __kprobes -do_trap(int trapnr, int signr, char *str, struct pt_regs *regs, -	long error_code, siginfo_t *info) +static int __kprobes +do_trap_no_signal(struct task_struct *tsk, int trapnr, char *str, +		  struct pt_regs *regs,	long error_code)  { -	struct task_struct *tsk = current; -  #ifdef CONFIG_X86_32  	if (regs->flags & X86_VM_MASK) {  		/* -		 * traps 0, 1, 3, 4, and 5 should be forwarded to vm86. +		 * Traps 0, 1, 3, 4, and 5 should be forwarded to vm86.  		 * On nmi (interrupt 2), do_trap should not be called.  		 */ -		if (trapnr < X86_TRAP_UD) -			goto vm86_trap; -		goto trap_signal; +		if (trapnr < X86_TRAP_UD) { +			if (!handle_vm86_trap((struct kernel_vm86_regs *) regs, +						error_code, trapnr)) +				return 0; +		} +		return -1;  	}  #endif +	if (!user_mode(regs)) { +		if (!fixup_exception(regs)) { +			tsk->thread.error_code = error_code; +			tsk->thread.trap_nr = trapnr; +			die(str, regs, error_code); +		} +		return 0; +	} -	if (!user_mode(regs)) -		goto kernel_trap; +	return -1; +} -#ifdef CONFIG_X86_32 -trap_signal: -#endif +static void __kprobes +do_trap(int trapnr, int signr, char *str, struct pt_regs *regs, +	long error_code, siginfo_t *info) +{ +	struct task_struct *tsk = current; + + +	if (!do_trap_no_signal(tsk, trapnr, str, regs, error_code)) +		return;  	/*  	 * We want error_code and trap_nr set for userspace faults and  	 * kernelspace faults which result in die(), but not @@ -158,33 +174,20 @@ trap_signal:  		force_sig_info(signr, info, tsk);  	else  		force_sig(signr, tsk); -	return; - -kernel_trap: -	if (!fixup_exception(regs)) { -		tsk->thread.error_code = error_code; -		tsk->thread.trap_nr = trapnr; -		die(str, regs, error_code); -	} -	return; - -#ifdef CONFIG_X86_32 -vm86_trap: -	if (handle_vm86_trap((struct kernel_vm86_regs *) regs, -						error_code, trapnr)) -		goto trap_signal; -	return; -#endif  }  #define DO_ERROR(trapnr, signr, str, name)				\  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;							\ +	}								\  	conditional_sti(regs);						\  	do_trap(trapnr, signr, str, regs, error_code, NULL);		\ +	exception_exit(regs);						\  }  #define DO_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr)		\ @@ -195,11 +198,15 @@ dotraplinkage void do_##name(struct pt_regs *regs, long error_code)	\  	info.si_errno = 0;						\  	info.si_code = sicode;						\  	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;							\ +	}								\  	conditional_sti(regs);						\  	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, @@ -222,12 +229,14 @@ DO_ERROR_INFO(X86_TRAP_AC, SIGBUS, "alignment check", alignment_check,  /* Runs on IST stack */  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, -			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) @@ -235,6 +244,7 @@ dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code)  	static const char str[] = "double fault";  	struct task_struct *tsk = current; +	exception_enter(regs);  	/* Return not checked because double check cannot be ignored */  	notify_die(DIE_TRAP, str, regs, error_code, X86_TRAP_DF, SIGSEGV); @@ -255,16 +265,29 @@ do_general_protection(struct pt_regs *regs, long error_code)  {  	struct task_struct *tsk; +	exception_enter(regs);  	conditional_sti(regs);  #ifdef CONFIG_X86_32 -	if (regs->flags & X86_VM_MASK) -		goto gp_in_vm86; +	if (regs->flags & X86_VM_MASK) { +		local_irq_enable(); +		handle_vm86_fault((struct kernel_vm86_regs *) regs, error_code); +		goto exit; +	}  #endif  	tsk = current; -	if (!user_mode(regs)) -		goto gp_in_kernel; +	if (!user_mode(regs)) { +		if (fixup_exception(regs)) +			goto exit; + +		tsk->thread.error_code = error_code; +		tsk->thread.trap_nr = X86_TRAP_GP; +		if (notify_die(DIE_GPF, "general protection fault", regs, error_code, +			       X86_TRAP_GP, SIGSEGV) != NOTIFY_STOP) +			die("general protection fault", regs, error_code); +		goto exit; +	}  	tsk->thread.error_code = error_code;  	tsk->thread.trap_nr = X86_TRAP_GP; @@ -279,25 +302,8 @@ do_general_protection(struct pt_regs *regs, long error_code)  	}  	force_sig(SIGSEGV, tsk); -	return; - -#ifdef CONFIG_X86_32 -gp_in_vm86: -	local_irq_enable(); -	handle_vm86_fault((struct kernel_vm86_regs *) regs, error_code); -	return; -#endif - -gp_in_kernel: -	if (fixup_exception(regs)) -		return; - -	tsk->thread.error_code = error_code; -	tsk->thread.trap_nr = X86_TRAP_GP; -	if (notify_die(DIE_GPF, "general protection fault", regs, error_code, -			X86_TRAP_GP, SIGSEGV) == NOTIFY_STOP) -		return; -	die("general protection fault", regs, error_code); +exit: +	exception_exit(regs);  }  /* May run on IST stack. */ @@ -312,15 +318,16 @@ dotraplinkage void __kprobes notrace do_int3(struct pt_regs *regs, long error_co  	    ftrace_int3_handler(regs))  		return;  #endif +	exception_enter(regs);  #ifdef CONFIG_KGDB_LOW_LEVEL_TRAP  	if (kgdb_ll_trap(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP,  				SIGTRAP) == NOTIFY_STOP) -		return; +		goto exit;  #endif /* CONFIG_KGDB_LOW_LEVEL_TRAP */  	if (notify_die(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP,  			SIGTRAP) == NOTIFY_STOP) -		return; +		goto exit;  	/*  	 * Let others (NMI) know that the debug stack is in use @@ -331,6 +338,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);  	preempt_conditional_cli(regs);  	debug_stack_usage_dec(); +exit: +	exception_exit(regs);  }  #ifdef CONFIG_X86_64 @@ -391,6 +400,8 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code)  	unsigned long dr6;  	int si_code; +	exception_enter(regs); +  	get_debugreg(dr6, 6);  	/* Filter out all the reserved bits which are preset to 1 */ @@ -406,7 +417,7 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code)  	/* Catch kmemcheck conditions first of all! */  	if ((dr6 & DR_STEP) && kmemcheck_trap(regs)) -		return; +		goto exit;  	/* DR6 may or may not be cleared by the CPU */  	set_debugreg(0, 6); @@ -421,7 +432,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,  							SIGTRAP) == NOTIFY_STOP) -		return; +		goto exit;  	/*  	 * Let others (NMI) know that the debug stack is in use @@ -437,7 +448,7 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code)  					X86_TRAP_DB);  		preempt_conditional_cli(regs);  		debug_stack_usage_dec(); -		return; +		goto exit;  	}  	/* @@ -458,7 +469,8 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code)  	preempt_conditional_cli(regs);  	debug_stack_usage_dec(); -	return; +exit: +	exception_exit(regs);  }  /* @@ -555,14 +567,17 @@ dotraplinkage void do_coprocessor_error(struct pt_regs *regs, long error_code)  #ifdef CONFIG_X86_32  	ignore_fpu_irq = 1;  #endif - +	exception_enter(regs);  	math_error(regs, error_code, X86_TRAP_MF); +	exception_exit(regs);  }  dotraplinkage void  do_simd_coprocessor_error(struct pt_regs *regs, long error_code)  { +	exception_enter(regs);  	math_error(regs, error_code, X86_TRAP_XF); +	exception_exit(regs);  }  dotraplinkage void @@ -613,11 +628,12 @@ void math_state_restore(void)  	}  	__thread_fpu_begin(tsk); +  	/*  	 * Paranoid restore. send a SIGSEGV if we fail to restore the state.  	 */  	if (unlikely(restore_fpu_checking(tsk))) { -		__thread_fpu_end(tsk); +		drop_init_fpu(tsk);  		force_sig(SIGSEGV, tsk);  		return;  	} @@ -629,6 +645,9 @@ EXPORT_SYMBOL_GPL(math_state_restore);  dotraplinkage void __kprobes  do_device_not_available(struct pt_regs *regs, long error_code)  { +	exception_enter(regs); +	BUG_ON(use_eager_fpu()); +  #ifdef CONFIG_MATH_EMULATION  	if (read_cr0() & X86_CR0_EM) {  		struct math_emu_info info = { }; @@ -637,6 +656,7 @@ do_device_not_available(struct pt_regs *regs, long error_code)  		info.regs = regs;  		math_emulate(&info); +		exception_exit(regs);  		return;  	}  #endif @@ -644,12 +664,15 @@ do_device_not_available(struct pt_regs *regs, long error_code)  #ifdef CONFIG_X86_32  	conditional_sti(regs);  #endif +	exception_exit(regs);  }  #ifdef CONFIG_X86_32  dotraplinkage void do_iret_error(struct pt_regs *regs, long error_code)  {  	siginfo_t info; + +	exception_enter(regs);  	local_irq_enable();  	info.si_signo = SIGILL; @@ -657,10 +680,11 @@ dotraplinkage void do_iret_error(struct pt_regs *regs, long error_code)  	info.si_code = ILL_BADSTK;  	info.si_addr = NULL;  	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  |