aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390/kernel/traps.c
diff options
context:
space:
mode:
authorMartin Schwidefsky <schwidefsky@de.ibm.com>2011-12-27 05:27:18 -0500
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2011-12-27 05:27:12 -0500
commitaa33c8cbbae2eb98489a3a363099b362146a8f4c (patch)
tree315ac880b4a4af8f7c0c2822c2c5e5817033a5ab /arch/s390/kernel/traps.c
parent679e2ea73366cac81ede4104e6d3048cb806df2c (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.c168
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
46void (*pgm_check_table[128])(struct pt_regs *, long, unsigned long); 46void (*pgm_check_table[128])(struct pt_regs *regs);
47 47
48int show_unhandled_signals; 48int show_unhandled_signals;
49 49
@@ -234,7 +234,7 @@ void show_regs(struct pt_regs *regs)
234 234
235static DEFINE_SPINLOCK(die_lock); 235static DEFINE_SPINLOCK(die_lock);
236 236
237void die(const char * str, struct pt_regs * regs, long err) 237void 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
270static void inline report_user_fault(struct pt_regs *regs, long int_code, 270static 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
290static inline void __kprobes do_trap(long pgm_int_code, int signr, char *str, 289static 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
295static 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
319static 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
326void __kprobes do_per_trap(struct pt_regs *regs) 327void __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
342static void default_trap_handler(struct pt_regs *regs, long pgm_int_code, 343static 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) \
353static void name(struct pt_regs *regs, long pgm_int_code, \ 353static 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
364DO_ERROR_INFO(addressing_exception, SIGILL, ILL_ILLADR, 358DO_ERROR_INFO(addressing_exception, SIGILL, ILL_ILLADR,
@@ -388,42 +382,34 @@ DO_ERROR_INFO(special_op_exception, SIGILL, ILL_ILLOPN,
388DO_ERROR_INFO(translation_exception, SIGILL, ILL_ILLOPN, 382DO_ERROR_INFO(translation_exception, SIGILL, ILL_ILLOPN,
389 "translation exception") 383 "translation exception")
390 384
391static inline void do_fp_trap(struct pt_regs *regs, void __user *location, 385static 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
418static void __kprobes illegal_op(struct pt_regs *regs, long pgm_int_code, 405static 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
500void specification_exception(struct pt_regs *regs, long pgm_int_code, 474void 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
555DO_ERROR_INFO(specification_exception, SIGILL, ILL_ILLOPN, 520DO_ERROR_INFO(specification_exception, SIGILL, ILL_ILLOPN,
556 "specification exception"); 521 "specification exception");
557#endif 522#endif
558 523
559static void data_exception(struct pt_regs *regs, long pgm_int_code, 524static 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
642static void space_switch_exception(struct pt_regs *regs, long pgm_int_code, 599static 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
658void __kprobes kernel_stack_overflow(struct pt_regs * regs) 608void __kprobes kernel_stack_overflow(struct pt_regs * regs)