diff options
author | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2011-12-27 05:27:18 -0500 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2011-12-27 05:27:12 -0500 |
commit | aa33c8cbbae2eb98489a3a363099b362146a8f4c (patch) | |
tree | 315ac880b4a4af8f7c0c2822c2c5e5817033a5ab /arch/s390/kernel/traps.c | |
parent | 679e2ea73366cac81ede4104e6d3048cb806df2c (diff) |
[S390] cleanup trap handling
Move the program interruption code and the translation exception identifier
to the pt_regs structure as 'int_code' and 'int_parm_long' and make the
first level interrupt handler in entry[64].S store the two values. That
makes it possible to drop 'prot_addr' and 'trap_no' from the thread_struct
and to reduce the number of arguments to a lot of functions. Finally
un-inline do_trap. Overall this saves 5812 bytes in the .text section of
the 64 bit kernel.
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'arch/s390/kernel/traps.c')
-rw-r--r-- | arch/s390/kernel/traps.c | 168 |
1 files changed, 59 insertions, 109 deletions
diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c index a9807dd86276..dc6cc1b0ae66 100644 --- a/arch/s390/kernel/traps.c +++ b/arch/s390/kernel/traps.c | |||
@@ -43,7 +43,7 @@ | |||
43 | #include <asm/debug.h> | 43 | #include <asm/debug.h> |
44 | #include "entry.h" | 44 | #include "entry.h" |
45 | 45 | ||
46 | void (*pgm_check_table[128])(struct pt_regs *, long, unsigned long); | 46 | void (*pgm_check_table[128])(struct pt_regs *regs); |
47 | 47 | ||
48 | int show_unhandled_signals; | 48 | int show_unhandled_signals; |
49 | 49 | ||
@@ -234,7 +234,7 @@ void show_regs(struct pt_regs *regs) | |||
234 | 234 | ||
235 | static DEFINE_SPINLOCK(die_lock); | 235 | static DEFINE_SPINLOCK(die_lock); |
236 | 236 | ||
237 | void die(const char * str, struct pt_regs * regs, long err) | 237 | void die(struct pt_regs *regs, const char *str) |
238 | { | 238 | { |
239 | static int die_counter; | 239 | static int die_counter; |
240 | 240 | ||
@@ -243,7 +243,7 @@ void die(const char * str, struct pt_regs * regs, long err) | |||
243 | console_verbose(); | 243 | console_verbose(); |
244 | spin_lock_irq(&die_lock); | 244 | spin_lock_irq(&die_lock); |
245 | bust_spinlocks(1); | 245 | bust_spinlocks(1); |
246 | printk("%s: %04lx [#%d] ", str, err & 0xffff, ++die_counter); | 246 | printk("%s: %04x [#%d] ", str, regs->int_code & 0xffff, ++die_counter); |
247 | #ifdef CONFIG_PREEMPT | 247 | #ifdef CONFIG_PREEMPT |
248 | printk("PREEMPT "); | 248 | printk("PREEMPT "); |
249 | #endif | 249 | #endif |
@@ -254,7 +254,7 @@ void die(const char * str, struct pt_regs * regs, long err) | |||
254 | printk("DEBUG_PAGEALLOC"); | 254 | printk("DEBUG_PAGEALLOC"); |
255 | #endif | 255 | #endif |
256 | printk("\n"); | 256 | printk("\n"); |
257 | notify_die(DIE_OOPS, str, regs, err, current->thread.trap_no, SIGSEGV); | 257 | notify_die(DIE_OOPS, str, regs, 0, regs->int_code & 0xffff, SIGSEGV); |
258 | show_regs(regs); | 258 | show_regs(regs); |
259 | bust_spinlocks(0); | 259 | bust_spinlocks(0); |
260 | add_taint(TAINT_DIE); | 260 | add_taint(TAINT_DIE); |
@@ -267,8 +267,7 @@ void die(const char * str, struct pt_regs * regs, long err) | |||
267 | do_exit(SIGSEGV); | 267 | do_exit(SIGSEGV); |
268 | } | 268 | } |
269 | 269 | ||
270 | static void inline report_user_fault(struct pt_regs *regs, long int_code, | 270 | static inline void report_user_fault(struct pt_regs *regs, int signr) |
271 | int signr) | ||
272 | { | 271 | { |
273 | if ((task_pid_nr(current) > 1) && !show_unhandled_signals) | 272 | if ((task_pid_nr(current) > 1) && !show_unhandled_signals) |
274 | return; | 273 | return; |
@@ -276,7 +275,7 @@ static void inline report_user_fault(struct pt_regs *regs, long int_code, | |||
276 | return; | 275 | return; |
277 | if (!printk_ratelimit()) | 276 | if (!printk_ratelimit()) |
278 | return; | 277 | return; |
279 | printk("User process fault: interruption code 0x%lX ", int_code); | 278 | printk("User process fault: interruption code 0x%X ", regs->int_code); |
280 | print_vma_addr("in ", regs->psw.addr & PSW_ADDR_INSN); | 279 | print_vma_addr("in ", regs->psw.addr & PSW_ADDR_INSN); |
281 | printk("\n"); | 280 | printk("\n"); |
282 | show_regs(regs); | 281 | show_regs(regs); |
@@ -287,19 +286,28 @@ int is_valid_bugaddr(unsigned long addr) | |||
287 | return 1; | 286 | return 1; |
288 | } | 287 | } |
289 | 288 | ||
290 | static inline void __kprobes do_trap(long pgm_int_code, int signr, char *str, | 289 | static inline void __user *get_psw_address(struct pt_regs *regs) |
291 | struct pt_regs *regs, siginfo_t *info) | ||
292 | { | 290 | { |
293 | if (notify_die(DIE_TRAP, str, regs, pgm_int_code, | 291 | return (void __user *) |
294 | pgm_int_code, signr) == NOTIFY_STOP) | 292 | ((regs->psw.addr - (regs->int_code >> 16)) & PSW_ADDR_INSN); |
293 | } | ||
294 | |||
295 | static void __kprobes do_trap(struct pt_regs *regs, | ||
296 | int si_signo, int si_code, char *str) | ||
297 | { | ||
298 | siginfo_t info; | ||
299 | |||
300 | if (notify_die(DIE_TRAP, str, regs, 0, | ||
301 | regs->int_code, si_signo) == NOTIFY_STOP) | ||
295 | return; | 302 | return; |
296 | 303 | ||
297 | if (regs->psw.mask & PSW_MASK_PSTATE) { | 304 | if (regs->psw.mask & PSW_MASK_PSTATE) { |
298 | struct task_struct *tsk = current; | 305 | info.si_signo = si_signo; |
299 | 306 | info.si_errno = 0; | |
300 | tsk->thread.trap_no = pgm_int_code & 0xffff; | 307 | info.si_code = si_code; |
301 | force_sig_info(signr, info, tsk); | 308 | info.si_addr = get_psw_address(regs); |
302 | report_user_fault(regs, pgm_int_code, signr); | 309 | force_sig_info(si_signo, &info, current); |
310 | report_user_fault(regs, si_signo); | ||
303 | } else { | 311 | } else { |
304 | const struct exception_table_entry *fixup; | 312 | const struct exception_table_entry *fixup; |
305 | fixup = search_exception_tables(regs->psw.addr & PSW_ADDR_INSN); | 313 | fixup = search_exception_tables(regs->psw.addr & PSW_ADDR_INSN); |
@@ -311,18 +319,11 @@ static inline void __kprobes do_trap(long pgm_int_code, int signr, char *str, | |||
311 | btt = report_bug(regs->psw.addr & PSW_ADDR_INSN, regs); | 319 | btt = report_bug(regs->psw.addr & PSW_ADDR_INSN, regs); |
312 | if (btt == BUG_TRAP_TYPE_WARN) | 320 | if (btt == BUG_TRAP_TYPE_WARN) |
313 | return; | 321 | return; |
314 | die(str, regs, pgm_int_code); | 322 | die(regs, str); |
315 | } | 323 | } |
316 | } | 324 | } |
317 | } | 325 | } |
318 | 326 | ||
319 | static inline void __user *get_psw_address(struct pt_regs *regs, | ||
320 | long pgm_int_code) | ||
321 | { | ||
322 | return (void __user *) | ||
323 | ((regs->psw.addr - (pgm_int_code >> 16)) & PSW_ADDR_INSN); | ||
324 | } | ||
325 | |||
326 | void __kprobes do_per_trap(struct pt_regs *regs) | 327 | void __kprobes do_per_trap(struct pt_regs *regs) |
327 | { | 328 | { |
328 | siginfo_t info; | 329 | siginfo_t info; |
@@ -339,26 +340,19 @@ void __kprobes do_per_trap(struct pt_regs *regs) | |||
339 | force_sig_info(SIGTRAP, &info, current); | 340 | force_sig_info(SIGTRAP, &info, current); |
340 | } | 341 | } |
341 | 342 | ||
342 | static void default_trap_handler(struct pt_regs *regs, long pgm_int_code, | 343 | static void default_trap_handler(struct pt_regs *regs) |
343 | unsigned long trans_exc_code) | ||
344 | { | 344 | { |
345 | if (regs->psw.mask & PSW_MASK_PSTATE) { | 345 | if (regs->psw.mask & PSW_MASK_PSTATE) { |
346 | report_user_fault(regs, pgm_int_code, SIGSEGV); | 346 | report_user_fault(regs, SIGSEGV); |
347 | do_exit(SIGSEGV); | 347 | do_exit(SIGSEGV); |
348 | } else | 348 | } else |
349 | die("Unknown program exception", regs, pgm_int_code); | 349 | die(regs, "Unknown program exception"); |
350 | } | 350 | } |
351 | 351 | ||
352 | #define DO_ERROR_INFO(name, signr, sicode, str) \ | 352 | #define DO_ERROR_INFO(name, signr, sicode, str) \ |
353 | static void name(struct pt_regs *regs, long pgm_int_code, \ | 353 | static void name(struct pt_regs *regs) \ |
354 | unsigned long trans_exc_code) \ | ||
355 | { \ | 354 | { \ |
356 | siginfo_t info; \ | 355 | do_trap(regs, signr, sicode, str); \ |
357 | info.si_signo = signr; \ | ||
358 | info.si_errno = 0; \ | ||
359 | info.si_code = sicode; \ | ||
360 | info.si_addr = get_psw_address(regs, pgm_int_code); \ | ||
361 | do_trap(pgm_int_code, signr, str, regs, &info); \ | ||
362 | } | 356 | } |
363 | 357 | ||
364 | DO_ERROR_INFO(addressing_exception, SIGILL, ILL_ILLADR, | 358 | DO_ERROR_INFO(addressing_exception, SIGILL, ILL_ILLADR, |
@@ -388,42 +382,34 @@ DO_ERROR_INFO(special_op_exception, SIGILL, ILL_ILLOPN, | |||
388 | DO_ERROR_INFO(translation_exception, SIGILL, ILL_ILLOPN, | 382 | DO_ERROR_INFO(translation_exception, SIGILL, ILL_ILLOPN, |
389 | "translation exception") | 383 | "translation exception") |
390 | 384 | ||
391 | static inline void do_fp_trap(struct pt_regs *regs, void __user *location, | 385 | static inline void do_fp_trap(struct pt_regs *regs, int fpc) |
392 | int fpc, long pgm_int_code) | ||
393 | { | 386 | { |
394 | siginfo_t si; | 387 | int si_code = 0; |
395 | |||
396 | si.si_signo = SIGFPE; | ||
397 | si.si_errno = 0; | ||
398 | si.si_addr = location; | ||
399 | si.si_code = 0; | ||
400 | /* FPC[2] is Data Exception Code */ | 388 | /* FPC[2] is Data Exception Code */ |
401 | if ((fpc & 0x00000300) == 0) { | 389 | if ((fpc & 0x00000300) == 0) { |
402 | /* bits 6 and 7 of DXC are 0 iff IEEE exception */ | 390 | /* bits 6 and 7 of DXC are 0 iff IEEE exception */ |
403 | if (fpc & 0x8000) /* invalid fp operation */ | 391 | if (fpc & 0x8000) /* invalid fp operation */ |
404 | si.si_code = FPE_FLTINV; | 392 | si_code = FPE_FLTINV; |
405 | else if (fpc & 0x4000) /* div by 0 */ | 393 | else if (fpc & 0x4000) /* div by 0 */ |
406 | si.si_code = FPE_FLTDIV; | 394 | si_code = FPE_FLTDIV; |
407 | else if (fpc & 0x2000) /* overflow */ | 395 | else if (fpc & 0x2000) /* overflow */ |
408 | si.si_code = FPE_FLTOVF; | 396 | si_code = FPE_FLTOVF; |
409 | else if (fpc & 0x1000) /* underflow */ | 397 | else if (fpc & 0x1000) /* underflow */ |
410 | si.si_code = FPE_FLTUND; | 398 | si_code = FPE_FLTUND; |
411 | else if (fpc & 0x0800) /* inexact */ | 399 | else if (fpc & 0x0800) /* inexact */ |
412 | si.si_code = FPE_FLTRES; | 400 | si_code = FPE_FLTRES; |
413 | } | 401 | } |
414 | do_trap(pgm_int_code, SIGFPE, | 402 | do_trap(regs, SIGFPE, si_code, "floating point exception"); |
415 | "floating point exception", regs, &si); | ||
416 | } | 403 | } |
417 | 404 | ||
418 | static void __kprobes illegal_op(struct pt_regs *regs, long pgm_int_code, | 405 | static void __kprobes illegal_op(struct pt_regs *regs) |
419 | unsigned long trans_exc_code) | ||
420 | { | 406 | { |
421 | siginfo_t info; | 407 | siginfo_t info; |
422 | __u8 opcode[6]; | 408 | __u8 opcode[6]; |
423 | __u16 __user *location; | 409 | __u16 __user *location; |
424 | int signal = 0; | 410 | int signal = 0; |
425 | 411 | ||
426 | location = get_psw_address(regs, pgm_int_code); | 412 | location = get_psw_address(regs); |
427 | 413 | ||
428 | if (regs->psw.mask & PSW_MASK_PSTATE) { | 414 | if (regs->psw.mask & PSW_MASK_PSTATE) { |
429 | if (get_user(*((__u16 *) opcode), (__u16 __user *) location)) | 415 | if (get_user(*((__u16 *) opcode), (__u16 __user *) location)) |
@@ -467,44 +453,31 @@ static void __kprobes illegal_op(struct pt_regs *regs, long pgm_int_code, | |||
467 | * If we get an illegal op in kernel mode, send it through the | 453 | * If we get an illegal op in kernel mode, send it through the |
468 | * kprobes notifier. If kprobes doesn't pick it up, SIGILL | 454 | * kprobes notifier. If kprobes doesn't pick it up, SIGILL |
469 | */ | 455 | */ |
470 | if (notify_die(DIE_BPT, "bpt", regs, pgm_int_code, | 456 | if (notify_die(DIE_BPT, "bpt", regs, 0, |
471 | 3, SIGTRAP) != NOTIFY_STOP) | 457 | 3, SIGTRAP) != NOTIFY_STOP) |
472 | signal = SIGILL; | 458 | signal = SIGILL; |
473 | } | 459 | } |
474 | 460 | ||
475 | #ifdef CONFIG_MATHEMU | 461 | #ifdef CONFIG_MATHEMU |
476 | if (signal == SIGFPE) | 462 | if (signal == SIGFPE) |
477 | do_fp_trap(regs, location, | 463 | do_fp_trap(regs, current->thread.fp_regs.fpc); |
478 | current->thread.fp_regs.fpc, pgm_int_code); | 464 | else if (signal == SIGSEGV) |
479 | else if (signal == SIGSEGV) { | 465 | do_trap(regs, signal, SEGV_MAPERR, "user address fault"); |
480 | info.si_signo = signal; | 466 | else |
481 | info.si_errno = 0; | ||
482 | info.si_code = SEGV_MAPERR; | ||
483 | info.si_addr = (void __user *) location; | ||
484 | do_trap(pgm_int_code, signal, | ||
485 | "user address fault", regs, &info); | ||
486 | } else | ||
487 | #endif | 467 | #endif |
488 | if (signal) { | 468 | if (signal) |
489 | info.si_signo = signal; | 469 | do_trap(regs, signal, ILL_ILLOPC, "illegal operation"); |
490 | info.si_errno = 0; | ||
491 | info.si_code = ILL_ILLOPC; | ||
492 | info.si_addr = (void __user *) location; | ||
493 | do_trap(pgm_int_code, signal, | ||
494 | "illegal operation", regs, &info); | ||
495 | } | ||
496 | } | 470 | } |
497 | 471 | ||
498 | 472 | ||
499 | #ifdef CONFIG_MATHEMU | 473 | #ifdef CONFIG_MATHEMU |
500 | void specification_exception(struct pt_regs *regs, long pgm_int_code, | 474 | void specification_exception(struct pt_regs *regs) |
501 | unsigned long trans_exc_code) | ||
502 | { | 475 | { |
503 | __u8 opcode[6]; | 476 | __u8 opcode[6]; |
504 | __u16 __user *location = NULL; | 477 | __u16 __user *location = NULL; |
505 | int signal = 0; | 478 | int signal = 0; |
506 | 479 | ||
507 | location = (__u16 __user *) get_psw_address(regs, pgm_int_code); | 480 | location = (__u16 __user *) get_psw_address(regs); |
508 | 481 | ||
509 | if (regs->psw.mask & PSW_MASK_PSTATE) { | 482 | if (regs->psw.mask & PSW_MASK_PSTATE) { |
510 | get_user(*((__u16 *) opcode), location); | 483 | get_user(*((__u16 *) opcode), location); |
@@ -539,30 +512,21 @@ void specification_exception(struct pt_regs *regs, long pgm_int_code, | |||
539 | signal = SIGILL; | 512 | signal = SIGILL; |
540 | 513 | ||
541 | if (signal == SIGFPE) | 514 | if (signal == SIGFPE) |
542 | do_fp_trap(regs, location, | 515 | do_fp_trap(regs, current->thread.fp_regs.fpc); |
543 | current->thread.fp_regs.fpc, pgm_int_code); | 516 | else if (signal) |
544 | else if (signal) { | 517 | do_trap(regs, signal, ILL_ILLOPN, "specification exception"); |
545 | siginfo_t info; | ||
546 | info.si_signo = signal; | ||
547 | info.si_errno = 0; | ||
548 | info.si_code = ILL_ILLOPN; | ||
549 | info.si_addr = location; | ||
550 | do_trap(pgm_int_code, signal, | ||
551 | "specification exception", regs, &info); | ||
552 | } | ||
553 | } | 518 | } |
554 | #else | 519 | #else |
555 | DO_ERROR_INFO(specification_exception, SIGILL, ILL_ILLOPN, | 520 | DO_ERROR_INFO(specification_exception, SIGILL, ILL_ILLOPN, |
556 | "specification exception"); | 521 | "specification exception"); |
557 | #endif | 522 | #endif |
558 | 523 | ||
559 | static void data_exception(struct pt_regs *regs, long pgm_int_code, | 524 | static void data_exception(struct pt_regs *regs) |
560 | unsigned long trans_exc_code) | ||
561 | { | 525 | { |
562 | __u16 __user *location; | 526 | __u16 __user *location; |
563 | int signal = 0; | 527 | int signal = 0; |
564 | 528 | ||
565 | location = get_psw_address(regs, pgm_int_code); | 529 | location = get_psw_address(regs); |
566 | 530 | ||
567 | if (MACHINE_HAS_IEEE) | 531 | if (MACHINE_HAS_IEEE) |
568 | asm volatile("stfpc %0" : "=m" (current->thread.fp_regs.fpc)); | 532 | asm volatile("stfpc %0" : "=m" (current->thread.fp_regs.fpc)); |
@@ -627,32 +591,18 @@ static void data_exception(struct pt_regs *regs, long pgm_int_code, | |||
627 | else | 591 | else |
628 | signal = SIGILL; | 592 | signal = SIGILL; |
629 | if (signal == SIGFPE) | 593 | if (signal == SIGFPE) |
630 | do_fp_trap(regs, location, | 594 | do_fp_trap(regs, current->thread.fp_regs.fpc); |
631 | current->thread.fp_regs.fpc, pgm_int_code); | 595 | else if (signal) |
632 | else if (signal) { | 596 | do_trap(regs, signal, ILL_ILLOPN, "data exception"); |
633 | siginfo_t info; | ||
634 | info.si_signo = signal; | ||
635 | info.si_errno = 0; | ||
636 | info.si_code = ILL_ILLOPN; | ||
637 | info.si_addr = location; | ||
638 | do_trap(pgm_int_code, signal, "data exception", regs, &info); | ||
639 | } | ||
640 | } | 597 | } |
641 | 598 | ||
642 | static void space_switch_exception(struct pt_regs *regs, long pgm_int_code, | 599 | static void space_switch_exception(struct pt_regs *regs) |
643 | unsigned long trans_exc_code) | ||
644 | { | 600 | { |
645 | siginfo_t info; | ||
646 | |||
647 | /* Set user psw back to home space mode. */ | 601 | /* Set user psw back to home space mode. */ |
648 | if (regs->psw.mask & PSW_MASK_PSTATE) | 602 | if (regs->psw.mask & PSW_MASK_PSTATE) |
649 | regs->psw.mask |= PSW_ASC_HOME; | 603 | regs->psw.mask |= PSW_ASC_HOME; |
650 | /* Send SIGILL. */ | 604 | /* Send SIGILL. */ |
651 | info.si_signo = SIGILL; | 605 | do_trap(regs, SIGILL, ILL_PRVOPC, "space switch event"); |
652 | info.si_errno = 0; | ||
653 | info.si_code = ILL_PRVOPC; | ||
654 | info.si_addr = get_psw_address(regs, pgm_int_code); | ||
655 | do_trap(pgm_int_code, SIGILL, "space switch event", regs, &info); | ||
656 | } | 606 | } |
657 | 607 | ||
658 | void __kprobes kernel_stack_overflow(struct pt_regs * regs) | 608 | void __kprobes kernel_stack_overflow(struct pt_regs * regs) |