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 @@ | |||
55 | #include <asm/i387.h> | 55 | #include <asm/i387.h> |
56 | #include <asm/fpu-internal.h> | 56 | #include <asm/fpu-internal.h> |
57 | #include <asm/mce.h> | 57 | #include <asm/mce.h> |
58 | #include <asm/rcu.h> | ||
58 | 59 | ||
59 | #include <asm/mach_traps.h> | 60 | #include <asm/mach_traps.h> |
60 | 61 | ||
@@ -107,30 +108,45 @@ static inline void preempt_conditional_cli(struct pt_regs *regs) | |||
107 | dec_preempt_count(); | 108 | dec_preempt_count(); |
108 | } | 109 | } |
109 | 110 | ||
110 | static void __kprobes | 111 | static int __kprobes |
111 | do_trap(int trapnr, int signr, char *str, struct pt_regs *regs, | 112 | do_trap_no_signal(struct task_struct *tsk, int trapnr, char *str, |
112 | long error_code, siginfo_t *info) | 113 | struct pt_regs *regs, long error_code) |
113 | { | 114 | { |
114 | struct task_struct *tsk = current; | ||
115 | |||
116 | #ifdef CONFIG_X86_32 | 115 | #ifdef CONFIG_X86_32 |
117 | if (regs->flags & X86_VM_MASK) { | 116 | if (regs->flags & X86_VM_MASK) { |
118 | /* | 117 | /* |
119 | * traps 0, 1, 3, 4, and 5 should be forwarded to vm86. | 118 | * Traps 0, 1, 3, 4, and 5 should be forwarded to vm86. |
120 | * On nmi (interrupt 2), do_trap should not be called. | 119 | * On nmi (interrupt 2), do_trap should not be called. |
121 | */ | 120 | */ |
122 | if (trapnr < X86_TRAP_UD) | 121 | if (trapnr < X86_TRAP_UD) { |
123 | goto vm86_trap; | 122 | if (!handle_vm86_trap((struct kernel_vm86_regs *) regs, |
124 | goto trap_signal; | 123 | error_code, trapnr)) |
124 | return 0; | ||
125 | } | ||
126 | return -1; | ||
125 | } | 127 | } |
126 | #endif | 128 | #endif |
129 | if (!user_mode(regs)) { | ||
130 | if (!fixup_exception(regs)) { | ||
131 | tsk->thread.error_code = error_code; | ||
132 | tsk->thread.trap_nr = trapnr; | ||
133 | die(str, regs, error_code); | ||
134 | } | ||
135 | return 0; | ||
136 | } | ||
127 | 137 | ||
128 | if (!user_mode(regs)) | 138 | return -1; |
129 | goto kernel_trap; | 139 | } |
130 | 140 | ||
131 | #ifdef CONFIG_X86_32 | 141 | static void __kprobes |
132 | trap_signal: | 142 | do_trap(int trapnr, int signr, char *str, struct pt_regs *regs, |
133 | #endif | 143 | long error_code, siginfo_t *info) |
144 | { | ||
145 | struct task_struct *tsk = current; | ||
146 | |||
147 | |||
148 | if (!do_trap_no_signal(tsk, trapnr, str, regs, error_code)) | ||
149 | return; | ||
134 | /* | 150 | /* |
135 | * We want error_code and trap_nr set for userspace faults and | 151 | * We want error_code and trap_nr set for userspace faults and |
136 | * kernelspace faults which result in die(), but not | 152 | * kernelspace faults which result in die(), but not |
@@ -158,33 +174,20 @@ trap_signal: | |||
158 | force_sig_info(signr, info, tsk); | 174 | force_sig_info(signr, info, tsk); |
159 | else | 175 | else |
160 | force_sig(signr, tsk); | 176 | force_sig(signr, tsk); |
161 | return; | ||
162 | |||
163 | kernel_trap: | ||
164 | if (!fixup_exception(regs)) { | ||
165 | tsk->thread.error_code = error_code; | ||
166 | tsk->thread.trap_nr = trapnr; | ||
167 | die(str, regs, error_code); | ||
168 | } | ||
169 | return; | ||
170 | |||
171 | #ifdef CONFIG_X86_32 | ||
172 | vm86_trap: | ||
173 | if (handle_vm86_trap((struct kernel_vm86_regs *) regs, | ||
174 | error_code, trapnr)) | ||
175 | goto trap_signal; | ||
176 | return; | ||
177 | #endif | ||
178 | } | 177 | } |
179 | 178 | ||
180 | #define DO_ERROR(trapnr, signr, str, name) \ | 179 | #define DO_ERROR(trapnr, signr, str, name) \ |
181 | dotraplinkage void do_##name(struct pt_regs *regs, long error_code) \ | 180 | dotraplinkage void do_##name(struct pt_regs *regs, long error_code) \ |
182 | { \ | 181 | { \ |
183 | if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \ | 182 | exception_enter(regs); \ |
184 | == NOTIFY_STOP) \ | 183 | if (notify_die(DIE_TRAP, str, regs, error_code, \ |
184 | trapnr, signr) == NOTIFY_STOP) { \ | ||
185 | exception_exit(regs); \ | ||
185 | return; \ | 186 | return; \ |
187 | } \ | ||
186 | conditional_sti(regs); \ | 188 | conditional_sti(regs); \ |
187 | do_trap(trapnr, signr, str, regs, error_code, NULL); \ | 189 | do_trap(trapnr, signr, str, regs, error_code, NULL); \ |
190 | exception_exit(regs); \ | ||
188 | } | 191 | } |
189 | 192 | ||
190 | #define DO_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr) \ | 193 | #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) \ | |||
195 | info.si_errno = 0; \ | 198 | info.si_errno = 0; \ |
196 | info.si_code = sicode; \ | 199 | info.si_code = sicode; \ |
197 | info.si_addr = (void __user *)siaddr; \ | 200 | info.si_addr = (void __user *)siaddr; \ |
198 | if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \ | 201 | exception_enter(regs); \ |
199 | == NOTIFY_STOP) \ | 202 | if (notify_die(DIE_TRAP, str, regs, error_code, \ |
203 | trapnr, signr) == NOTIFY_STOP) { \ | ||
204 | exception_exit(regs); \ | ||
200 | return; \ | 205 | return; \ |
206 | } \ | ||
201 | conditional_sti(regs); \ | 207 | conditional_sti(regs); \ |
202 | do_trap(trapnr, signr, str, regs, error_code, &info); \ | 208 | do_trap(trapnr, signr, str, regs, error_code, &info); \ |
209 | exception_exit(regs); \ | ||
203 | } | 210 | } |
204 | 211 | ||
205 | DO_ERROR_INFO(X86_TRAP_DE, SIGFPE, "divide error", divide_error, FPE_INTDIV, | 212 | 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, | |||
222 | /* Runs on IST stack */ | 229 | /* Runs on IST stack */ |
223 | dotraplinkage void do_stack_segment(struct pt_regs *regs, long error_code) | 230 | dotraplinkage void do_stack_segment(struct pt_regs *regs, long error_code) |
224 | { | 231 | { |
232 | exception_enter(regs); | ||
225 | if (notify_die(DIE_TRAP, "stack segment", regs, error_code, | 233 | if (notify_die(DIE_TRAP, "stack segment", regs, error_code, |
226 | X86_TRAP_SS, SIGBUS) == NOTIFY_STOP) | 234 | X86_TRAP_SS, SIGBUS) != NOTIFY_STOP) { |
227 | return; | 235 | preempt_conditional_sti(regs); |
228 | preempt_conditional_sti(regs); | 236 | do_trap(X86_TRAP_SS, SIGBUS, "stack segment", regs, error_code, NULL); |
229 | do_trap(X86_TRAP_SS, SIGBUS, "stack segment", regs, error_code, NULL); | 237 | preempt_conditional_cli(regs); |
230 | preempt_conditional_cli(regs); | 238 | } |
239 | exception_exit(regs); | ||
231 | } | 240 | } |
232 | 241 | ||
233 | dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code) | 242 | 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) | |||
235 | static const char str[] = "double fault"; | 244 | static const char str[] = "double fault"; |
236 | struct task_struct *tsk = current; | 245 | struct task_struct *tsk = current; |
237 | 246 | ||
247 | exception_enter(regs); | ||
238 | /* Return not checked because double check cannot be ignored */ | 248 | /* Return not checked because double check cannot be ignored */ |
239 | notify_die(DIE_TRAP, str, regs, error_code, X86_TRAP_DF, SIGSEGV); | 249 | notify_die(DIE_TRAP, str, regs, error_code, X86_TRAP_DF, SIGSEGV); |
240 | 250 | ||
@@ -255,16 +265,29 @@ do_general_protection(struct pt_regs *regs, long error_code) | |||
255 | { | 265 | { |
256 | struct task_struct *tsk; | 266 | struct task_struct *tsk; |
257 | 267 | ||
268 | exception_enter(regs); | ||
258 | conditional_sti(regs); | 269 | conditional_sti(regs); |
259 | 270 | ||
260 | #ifdef CONFIG_X86_32 | 271 | #ifdef CONFIG_X86_32 |
261 | if (regs->flags & X86_VM_MASK) | 272 | if (regs->flags & X86_VM_MASK) { |
262 | goto gp_in_vm86; | 273 | local_irq_enable(); |
274 | handle_vm86_fault((struct kernel_vm86_regs *) regs, error_code); | ||
275 | goto exit; | ||
276 | } | ||
263 | #endif | 277 | #endif |
264 | 278 | ||
265 | tsk = current; | 279 | tsk = current; |
266 | if (!user_mode(regs)) | 280 | if (!user_mode(regs)) { |
267 | goto gp_in_kernel; | 281 | if (fixup_exception(regs)) |
282 | goto exit; | ||
283 | |||
284 | tsk->thread.error_code = error_code; | ||
285 | tsk->thread.trap_nr = X86_TRAP_GP; | ||
286 | if (notify_die(DIE_GPF, "general protection fault", regs, error_code, | ||
287 | X86_TRAP_GP, SIGSEGV) != NOTIFY_STOP) | ||
288 | die("general protection fault", regs, error_code); | ||
289 | goto exit; | ||
290 | } | ||
268 | 291 | ||
269 | tsk->thread.error_code = error_code; | 292 | tsk->thread.error_code = error_code; |
270 | tsk->thread.trap_nr = X86_TRAP_GP; | 293 | tsk->thread.trap_nr = X86_TRAP_GP; |
@@ -279,25 +302,8 @@ do_general_protection(struct pt_regs *regs, long error_code) | |||
279 | } | 302 | } |
280 | 303 | ||
281 | force_sig(SIGSEGV, tsk); | 304 | force_sig(SIGSEGV, tsk); |
282 | return; | 305 | exit: |
283 | 306 | exception_exit(regs); | |
284 | #ifdef CONFIG_X86_32 | ||
285 | gp_in_vm86: | ||
286 | local_irq_enable(); | ||
287 | handle_vm86_fault((struct kernel_vm86_regs *) regs, error_code); | ||
288 | return; | ||
289 | #endif | ||
290 | |||
291 | gp_in_kernel: | ||
292 | if (fixup_exception(regs)) | ||
293 | return; | ||
294 | |||
295 | tsk->thread.error_code = error_code; | ||
296 | tsk->thread.trap_nr = X86_TRAP_GP; | ||
297 | if (notify_die(DIE_GPF, "general protection fault", regs, error_code, | ||
298 | X86_TRAP_GP, SIGSEGV) == NOTIFY_STOP) | ||
299 | return; | ||
300 | die("general protection fault", regs, error_code); | ||
301 | } | 307 | } |
302 | 308 | ||
303 | /* May run on IST stack. */ | 309 | /* May run on IST stack. */ |
@@ -312,15 +318,16 @@ dotraplinkage void __kprobes notrace do_int3(struct pt_regs *regs, long error_co | |||
312 | ftrace_int3_handler(regs)) | 318 | ftrace_int3_handler(regs)) |
313 | return; | 319 | return; |
314 | #endif | 320 | #endif |
321 | exception_enter(regs); | ||
315 | #ifdef CONFIG_KGDB_LOW_LEVEL_TRAP | 322 | #ifdef CONFIG_KGDB_LOW_LEVEL_TRAP |
316 | if (kgdb_ll_trap(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP, | 323 | if (kgdb_ll_trap(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP, |
317 | SIGTRAP) == NOTIFY_STOP) | 324 | SIGTRAP) == NOTIFY_STOP) |
318 | return; | 325 | goto exit; |
319 | #endif /* CONFIG_KGDB_LOW_LEVEL_TRAP */ | 326 | #endif /* CONFIG_KGDB_LOW_LEVEL_TRAP */ |
320 | 327 | ||
321 | if (notify_die(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP, | 328 | if (notify_die(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP, |
322 | SIGTRAP) == NOTIFY_STOP) | 329 | SIGTRAP) == NOTIFY_STOP) |
323 | return; | 330 | goto exit; |
324 | 331 | ||
325 | /* | 332 | /* |
326 | * Let others (NMI) know that the debug stack is in use | 333 | * 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 | |||
331 | do_trap(X86_TRAP_BP, SIGTRAP, "int3", regs, error_code, NULL); | 338 | do_trap(X86_TRAP_BP, SIGTRAP, "int3", regs, error_code, NULL); |
332 | preempt_conditional_cli(regs); | 339 | preempt_conditional_cli(regs); |
333 | debug_stack_usage_dec(); | 340 | debug_stack_usage_dec(); |
341 | exit: | ||
342 | exception_exit(regs); | ||
334 | } | 343 | } |
335 | 344 | ||
336 | #ifdef CONFIG_X86_64 | 345 | #ifdef CONFIG_X86_64 |
@@ -391,6 +400,8 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code) | |||
391 | unsigned long dr6; | 400 | unsigned long dr6; |
392 | int si_code; | 401 | int si_code; |
393 | 402 | ||
403 | exception_enter(regs); | ||
404 | |||
394 | get_debugreg(dr6, 6); | 405 | get_debugreg(dr6, 6); |
395 | 406 | ||
396 | /* Filter out all the reserved bits which are preset to 1 */ | 407 | /* 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) | |||
406 | 417 | ||
407 | /* Catch kmemcheck conditions first of all! */ | 418 | /* Catch kmemcheck conditions first of all! */ |
408 | if ((dr6 & DR_STEP) && kmemcheck_trap(regs)) | 419 | if ((dr6 & DR_STEP) && kmemcheck_trap(regs)) |
409 | return; | 420 | goto exit; |
410 | 421 | ||
411 | /* DR6 may or may not be cleared by the CPU */ | 422 | /* DR6 may or may not be cleared by the CPU */ |
412 | set_debugreg(0, 6); | 423 | set_debugreg(0, 6); |
@@ -421,7 +432,7 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code) | |||
421 | 432 | ||
422 | if (notify_die(DIE_DEBUG, "debug", regs, PTR_ERR(&dr6), error_code, | 433 | if (notify_die(DIE_DEBUG, "debug", regs, PTR_ERR(&dr6), error_code, |
423 | SIGTRAP) == NOTIFY_STOP) | 434 | SIGTRAP) == NOTIFY_STOP) |
424 | return; | 435 | goto exit; |
425 | 436 | ||
426 | /* | 437 | /* |
427 | * Let others (NMI) know that the debug stack is in use | 438 | * 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) | |||
437 | X86_TRAP_DB); | 448 | X86_TRAP_DB); |
438 | preempt_conditional_cli(regs); | 449 | preempt_conditional_cli(regs); |
439 | debug_stack_usage_dec(); | 450 | debug_stack_usage_dec(); |
440 | return; | 451 | goto exit; |
441 | } | 452 | } |
442 | 453 | ||
443 | /* | 454 | /* |
@@ -458,7 +469,8 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code) | |||
458 | preempt_conditional_cli(regs); | 469 | preempt_conditional_cli(regs); |
459 | debug_stack_usage_dec(); | 470 | debug_stack_usage_dec(); |
460 | 471 | ||
461 | return; | 472 | exit: |
473 | exception_exit(regs); | ||
462 | } | 474 | } |
463 | 475 | ||
464 | /* | 476 | /* |
@@ -555,14 +567,17 @@ dotraplinkage void do_coprocessor_error(struct pt_regs *regs, long error_code) | |||
555 | #ifdef CONFIG_X86_32 | 567 | #ifdef CONFIG_X86_32 |
556 | ignore_fpu_irq = 1; | 568 | ignore_fpu_irq = 1; |
557 | #endif | 569 | #endif |
558 | 570 | exception_enter(regs); | |
559 | math_error(regs, error_code, X86_TRAP_MF); | 571 | math_error(regs, error_code, X86_TRAP_MF); |
572 | exception_exit(regs); | ||
560 | } | 573 | } |
561 | 574 | ||
562 | dotraplinkage void | 575 | dotraplinkage void |
563 | do_simd_coprocessor_error(struct pt_regs *regs, long error_code) | 576 | do_simd_coprocessor_error(struct pt_regs *regs, long error_code) |
564 | { | 577 | { |
578 | exception_enter(regs); | ||
565 | math_error(regs, error_code, X86_TRAP_XF); | 579 | math_error(regs, error_code, X86_TRAP_XF); |
580 | exception_exit(regs); | ||
566 | } | 581 | } |
567 | 582 | ||
568 | dotraplinkage void | 583 | dotraplinkage void |
@@ -613,11 +628,12 @@ void math_state_restore(void) | |||
613 | } | 628 | } |
614 | 629 | ||
615 | __thread_fpu_begin(tsk); | 630 | __thread_fpu_begin(tsk); |
631 | |||
616 | /* | 632 | /* |
617 | * Paranoid restore. send a SIGSEGV if we fail to restore the state. | 633 | * Paranoid restore. send a SIGSEGV if we fail to restore the state. |
618 | */ | 634 | */ |
619 | if (unlikely(restore_fpu_checking(tsk))) { | 635 | if (unlikely(restore_fpu_checking(tsk))) { |
620 | __thread_fpu_end(tsk); | 636 | drop_init_fpu(tsk); |
621 | force_sig(SIGSEGV, tsk); | 637 | force_sig(SIGSEGV, tsk); |
622 | return; | 638 | return; |
623 | } | 639 | } |
@@ -629,6 +645,9 @@ EXPORT_SYMBOL_GPL(math_state_restore); | |||
629 | dotraplinkage void __kprobes | 645 | dotraplinkage void __kprobes |
630 | do_device_not_available(struct pt_regs *regs, long error_code) | 646 | do_device_not_available(struct pt_regs *regs, long error_code) |
631 | { | 647 | { |
648 | exception_enter(regs); | ||
649 | BUG_ON(use_eager_fpu()); | ||
650 | |||
632 | #ifdef CONFIG_MATH_EMULATION | 651 | #ifdef CONFIG_MATH_EMULATION |
633 | if (read_cr0() & X86_CR0_EM) { | 652 | if (read_cr0() & X86_CR0_EM) { |
634 | struct math_emu_info info = { }; | 653 | struct math_emu_info info = { }; |
@@ -637,6 +656,7 @@ do_device_not_available(struct pt_regs *regs, long error_code) | |||
637 | 656 | ||
638 | info.regs = regs; | 657 | info.regs = regs; |
639 | math_emulate(&info); | 658 | math_emulate(&info); |
659 | exception_exit(regs); | ||
640 | return; | 660 | return; |
641 | } | 661 | } |
642 | #endif | 662 | #endif |
@@ -644,12 +664,15 @@ do_device_not_available(struct pt_regs *regs, long error_code) | |||
644 | #ifdef CONFIG_X86_32 | 664 | #ifdef CONFIG_X86_32 |
645 | conditional_sti(regs); | 665 | conditional_sti(regs); |
646 | #endif | 666 | #endif |
667 | exception_exit(regs); | ||
647 | } | 668 | } |
648 | 669 | ||
649 | #ifdef CONFIG_X86_32 | 670 | #ifdef CONFIG_X86_32 |
650 | dotraplinkage void do_iret_error(struct pt_regs *regs, long error_code) | 671 | dotraplinkage void do_iret_error(struct pt_regs *regs, long error_code) |
651 | { | 672 | { |
652 | siginfo_t info; | 673 | siginfo_t info; |
674 | |||
675 | exception_enter(regs); | ||
653 | local_irq_enable(); | 676 | local_irq_enable(); |
654 | 677 | ||
655 | info.si_signo = SIGILL; | 678 | info.si_signo = SIGILL; |
@@ -657,10 +680,11 @@ dotraplinkage void do_iret_error(struct pt_regs *regs, long error_code) | |||
657 | info.si_code = ILL_BADSTK; | 680 | info.si_code = ILL_BADSTK; |
658 | info.si_addr = NULL; | 681 | info.si_addr = NULL; |
659 | if (notify_die(DIE_TRAP, "iret exception", regs, error_code, | 682 | if (notify_die(DIE_TRAP, "iret exception", regs, error_code, |
660 | X86_TRAP_IRET, SIGILL) == NOTIFY_STOP) | 683 | X86_TRAP_IRET, SIGILL) != NOTIFY_STOP) { |
661 | return; | 684 | do_trap(X86_TRAP_IRET, SIGILL, "iret exception", regs, error_code, |
662 | do_trap(X86_TRAP_IRET, SIGILL, "iret exception", regs, error_code, | 685 | &info); |
663 | &info); | 686 | } |
687 | exception_exit(regs); | ||
664 | } | 688 | } |
665 | #endif | 689 | #endif |
666 | 690 | ||