aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390/kernel/traps.c
diff options
context:
space:
mode:
authorMartin Schwidefsky <schwidefsky@de.ibm.com>2010-10-25 10:10:37 -0400
committerMartin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com>2010-10-25 10:10:19 -0400
commit1e54622e0403891b10f2105663e0f9dd595a1f17 (patch)
tree4d16341d7a3d0f3c46fcc275560a9206bccac07f /arch/s390/kernel/traps.c
parent84afdcee620b1640f2a145c07febae4ed68947f9 (diff)
[S390] cleanup lowcore access from program checks
Read all required fields for program checks from the lowcore in the first level interrupt handler in entry[64].S. If the context that caused the fault was enabled for interrupts we can now re-enable the irqs in entry[64].S. Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'arch/s390/kernel/traps.c')
-rw-r--r--arch/s390/kernel/traps.c172
1 files changed, 74 insertions, 98 deletions
diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c
index 5d8f0f3d025..e9b063e7d75 100644
--- a/arch/s390/kernel/traps.c
+++ b/arch/s390/kernel/traps.c
@@ -329,27 +329,19 @@ int is_valid_bugaddr(unsigned long addr)
329 return 1; 329 return 1;
330} 330}
331 331
332static void __kprobes inline do_trap(long interruption_code, int signr, 332static inline void __kprobes do_trap(long pgm_int_code, int signr, char *str,
333 char *str, struct pt_regs *regs, 333 struct pt_regs *regs, siginfo_t *info)
334 siginfo_t *info)
335{ 334{
336 /* 335 if (notify_die(DIE_TRAP, str, regs, pgm_int_code,
337 * We got all needed information from the lowcore and can 336 pgm_int_code, signr) == NOTIFY_STOP)
338 * now safely switch on interrupts.
339 */
340 if (regs->psw.mask & PSW_MASK_PSTATE)
341 local_irq_enable();
342
343 if (notify_die(DIE_TRAP, str, regs, interruption_code,
344 interruption_code, signr) == NOTIFY_STOP)
345 return; 337 return;
346 338
347 if (regs->psw.mask & PSW_MASK_PSTATE) { 339 if (regs->psw.mask & PSW_MASK_PSTATE) {
348 struct task_struct *tsk = current; 340 struct task_struct *tsk = current;
349 341
350 tsk->thread.trap_no = interruption_code & 0xffff; 342 tsk->thread.trap_no = pgm_int_code & 0xffff;
351 force_sig_info(signr, info, tsk); 343 force_sig_info(signr, info, tsk);
352 report_user_fault(regs, interruption_code, signr); 344 report_user_fault(regs, pgm_int_code, signr);
353 } else { 345 } else {
354 const struct exception_table_entry *fixup; 346 const struct exception_table_entry *fixup;
355 fixup = search_exception_tables(regs->psw.addr & PSW_ADDR_INSN); 347 fixup = search_exception_tables(regs->psw.addr & PSW_ADDR_INSN);
@@ -361,14 +353,16 @@ static void __kprobes inline do_trap(long interruption_code, int signr,
361 btt = report_bug(regs->psw.addr & PSW_ADDR_INSN, regs); 353 btt = report_bug(regs->psw.addr & PSW_ADDR_INSN, regs);
362 if (btt == BUG_TRAP_TYPE_WARN) 354 if (btt == BUG_TRAP_TYPE_WARN)
363 return; 355 return;
364 die(str, regs, interruption_code); 356 die(str, regs, pgm_int_code);
365 } 357 }
366 } 358 }
367} 359}
368 360
369static inline void __user *get_check_address(struct pt_regs *regs) 361static inline void __user *get_psw_address(struct pt_regs *regs,
362 long pgm_int_code)
370{ 363{
371 return (void __user *)((regs->psw.addr-S390_lowcore.pgm_ilc) & PSW_ADDR_INSN); 364 return (void __user *)
365 ((regs->psw.addr - (pgm_int_code >> 16)) & PSW_ADDR_INSN);
372} 366}
373 367
374void __kprobes do_single_step(struct pt_regs *regs) 368void __kprobes do_single_step(struct pt_regs *regs)
@@ -381,57 +375,57 @@ void __kprobes do_single_step(struct pt_regs *regs)
381 force_sig(SIGTRAP, current); 375 force_sig(SIGTRAP, current);
382} 376}
383 377
384static void default_trap_handler(struct pt_regs * regs, long interruption_code) 378static void default_trap_handler(struct pt_regs *regs, long pgm_int_code,
379 unsigned long trans_exc_code)
385{ 380{
386 if (regs->psw.mask & PSW_MASK_PSTATE) { 381 if (regs->psw.mask & PSW_MASK_PSTATE) {
387 local_irq_enable(); 382 report_user_fault(regs, pgm_int_code, SIGSEGV);
388 report_user_fault(regs, interruption_code, SIGSEGV);
389 do_exit(SIGSEGV); 383 do_exit(SIGSEGV);
390 } else 384 } else
391 die("Unknown program exception", regs, interruption_code); 385 die("Unknown program exception", regs, pgm_int_code);
392} 386}
393 387
394#define DO_ERROR_INFO(signr, str, name, sicode, siaddr) \ 388#define DO_ERROR_INFO(name, signr, sicode, str) \
395static void name(struct pt_regs * regs, long interruption_code) \ 389static void name(struct pt_regs *regs, long pgm_int_code, \
390 unsigned long trans_exc_code) \
396{ \ 391{ \
397 siginfo_t info; \ 392 siginfo_t info; \
398 info.si_signo = signr; \ 393 info.si_signo = signr; \
399 info.si_errno = 0; \ 394 info.si_errno = 0; \
400 info.si_code = sicode; \ 395 info.si_code = sicode; \
401 info.si_addr = siaddr; \ 396 info.si_addr = get_psw_address(regs, pgm_int_code); \
402 do_trap(interruption_code, signr, str, regs, &info); \ 397 do_trap(pgm_int_code, signr, str, regs, &info); \
403} 398}
404 399
405DO_ERROR_INFO(SIGILL, "addressing exception", addressing_exception, 400DO_ERROR_INFO(addressing_exception, SIGILL, ILL_ILLADR,
406 ILL_ILLADR, get_check_address(regs)) 401 "addressing exception")
407DO_ERROR_INFO(SIGILL, "execute exception", execute_exception, 402DO_ERROR_INFO(execute_exception, SIGILL, ILL_ILLOPN,
408 ILL_ILLOPN, get_check_address(regs)) 403 "execute exception")
409DO_ERROR_INFO(SIGFPE, "fixpoint divide exception", divide_exception, 404DO_ERROR_INFO(divide_exception, SIGFPE, FPE_INTDIV,
410 FPE_INTDIV, get_check_address(regs)) 405 "fixpoint divide exception")
411DO_ERROR_INFO(SIGFPE, "fixpoint overflow exception", overflow_exception, 406DO_ERROR_INFO(overflow_exception, SIGFPE, FPE_INTOVF,
412 FPE_INTOVF, get_check_address(regs)) 407 "fixpoint overflow exception")
413DO_ERROR_INFO(SIGFPE, "HFP overflow exception", hfp_overflow_exception, 408DO_ERROR_INFO(hfp_overflow_exception, SIGFPE, FPE_FLTOVF,
414 FPE_FLTOVF, get_check_address(regs)) 409 "HFP overflow exception")
415DO_ERROR_INFO(SIGFPE, "HFP underflow exception", hfp_underflow_exception, 410DO_ERROR_INFO(hfp_underflow_exception, SIGFPE, FPE_FLTUND,
416 FPE_FLTUND, get_check_address(regs)) 411 "HFP underflow exception")
417DO_ERROR_INFO(SIGFPE, "HFP significance exception", hfp_significance_exception, 412DO_ERROR_INFO(hfp_significance_exception, SIGFPE, FPE_FLTRES,
418 FPE_FLTRES, get_check_address(regs)) 413 "HFP significance exception")
419DO_ERROR_INFO(SIGFPE, "HFP divide exception", hfp_divide_exception, 414DO_ERROR_INFO(hfp_divide_exception, SIGFPE, FPE_FLTDIV,
420 FPE_FLTDIV, get_check_address(regs)) 415 "HFP divide exception")
421DO_ERROR_INFO(SIGFPE, "HFP square root exception", hfp_sqrt_exception, 416DO_ERROR_INFO(hfp_sqrt_exception, SIGFPE, FPE_FLTINV,
422 FPE_FLTINV, get_check_address(regs)) 417 "HFP square root exception")
423DO_ERROR_INFO(SIGILL, "operand exception", operand_exception, 418DO_ERROR_INFO(operand_exception, SIGILL, ILL_ILLOPN,
424 ILL_ILLOPN, get_check_address(regs)) 419 "operand exception")
425DO_ERROR_INFO(SIGILL, "privileged operation", privileged_op, 420DO_ERROR_INFO(privileged_op, SIGILL, ILL_PRVOPC,
426 ILL_PRVOPC, get_check_address(regs)) 421 "privileged operation")
427DO_ERROR_INFO(SIGILL, "special operation exception", special_op_exception, 422DO_ERROR_INFO(special_op_exception, SIGILL, ILL_ILLOPN,
428 ILL_ILLOPN, get_check_address(regs)) 423 "special operation exception")
429DO_ERROR_INFO(SIGILL, "translation exception", translation_exception, 424DO_ERROR_INFO(translation_exception, SIGILL, ILL_ILLOPN,
430 ILL_ILLOPN, get_check_address(regs)) 425 "translation exception")
431 426
432static inline void 427static inline void do_fp_trap(struct pt_regs *regs, void __user *location,
433do_fp_trap(struct pt_regs *regs, void __user *location, 428 int fpc, long pgm_int_code)
434 int fpc, long interruption_code)
435{ 429{
436 siginfo_t si; 430 siginfo_t si;
437 431
@@ -454,25 +448,19 @@ do_fp_trap(struct pt_regs *regs, void __user *location,
454 si.si_code = FPE_FLTRES; 448 si.si_code = FPE_FLTRES;
455 } 449 }
456 current->thread.ieee_instruction_pointer = (addr_t) location; 450 current->thread.ieee_instruction_pointer = (addr_t) location;
457 do_trap(interruption_code, SIGFPE, 451 do_trap(pgm_int_code, SIGFPE,
458 "floating point exception", regs, &si); 452 "floating point exception", regs, &si);
459} 453}
460 454
461static void illegal_op(struct pt_regs * regs, long interruption_code) 455static void illegal_op(struct pt_regs *regs, long pgm_int_code,
456 unsigned long trans_exc_code)
462{ 457{
463 siginfo_t info; 458 siginfo_t info;
464 __u8 opcode[6]; 459 __u8 opcode[6];
465 __u16 __user *location; 460 __u16 __user *location;
466 int signal = 0; 461 int signal = 0;
467 462
468 location = get_check_address(regs); 463 location = get_psw_address(regs, pgm_int_code);
469
470 /*
471 * We got all needed information from the lowcore and can
472 * now safely switch on interrupts.
473 */
474 if (regs->psw.mask & PSW_MASK_PSTATE)
475 local_irq_enable();
476 464
477 if (regs->psw.mask & PSW_MASK_PSTATE) { 465 if (regs->psw.mask & PSW_MASK_PSTATE) {
478 if (get_user(*((__u16 *) opcode), (__u16 __user *) location)) 466 if (get_user(*((__u16 *) opcode), (__u16 __user *) location))
@@ -512,7 +500,7 @@ static void illegal_op(struct pt_regs * regs, long interruption_code)
512 * If we get an illegal op in kernel mode, send it through the 500 * If we get an illegal op in kernel mode, send it through the
513 * kprobes notifier. If kprobes doesn't pick it up, SIGILL 501 * kprobes notifier. If kprobes doesn't pick it up, SIGILL
514 */ 502 */
515 if (notify_die(DIE_BPT, "bpt", regs, interruption_code, 503 if (notify_die(DIE_BPT, "bpt", regs, pgm_int_code,
516 3, SIGTRAP) != NOTIFY_STOP) 504 3, SIGTRAP) != NOTIFY_STOP)
517 signal = SIGILL; 505 signal = SIGILL;
518 } 506 }
@@ -520,13 +508,13 @@ static void illegal_op(struct pt_regs * regs, long interruption_code)
520#ifdef CONFIG_MATHEMU 508#ifdef CONFIG_MATHEMU
521 if (signal == SIGFPE) 509 if (signal == SIGFPE)
522 do_fp_trap(regs, location, 510 do_fp_trap(regs, location,
523 current->thread.fp_regs.fpc, interruption_code); 511 current->thread.fp_regs.fpc, pgm_int_code);
524 else if (signal == SIGSEGV) { 512 else if (signal == SIGSEGV) {
525 info.si_signo = signal; 513 info.si_signo = signal;
526 info.si_errno = 0; 514 info.si_errno = 0;
527 info.si_code = SEGV_MAPERR; 515 info.si_code = SEGV_MAPERR;
528 info.si_addr = (void __user *) location; 516 info.si_addr = (void __user *) location;
529 do_trap(interruption_code, signal, 517 do_trap(pgm_int_code, signal,
530 "user address fault", regs, &info); 518 "user address fault", regs, &info);
531 } else 519 } else
532#endif 520#endif
@@ -535,28 +523,22 @@ static void illegal_op(struct pt_regs * regs, long interruption_code)
535 info.si_errno = 0; 523 info.si_errno = 0;
536 info.si_code = ILL_ILLOPC; 524 info.si_code = ILL_ILLOPC;
537 info.si_addr = (void __user *) location; 525 info.si_addr = (void __user *) location;
538 do_trap(interruption_code, signal, 526 do_trap(pgm_int_code, signal,
539 "illegal operation", regs, &info); 527 "illegal operation", regs, &info);
540 } 528 }
541} 529}
542 530
543 531
544#ifdef CONFIG_MATHEMU 532#ifdef CONFIG_MATHEMU
545asmlinkage void 533asmlinkage void specification_exception(struct pt_regs *regs,
546specification_exception(struct pt_regs * regs, long interruption_code) 534 long pgm_int_code,
535 unsigned long trans_exc_code)
547{ 536{
548 __u8 opcode[6]; 537 __u8 opcode[6];
549 __u16 __user *location = NULL; 538 __u16 __user *location = NULL;
550 int signal = 0; 539 int signal = 0;
551 540
552 location = (__u16 __user *) get_check_address(regs); 541 location = (__u16 __user *) get_psw_address(regs, pgm_int_code);
553
554 /*
555 * We got all needed information from the lowcore and can
556 * now safely switch on interrupts.
557 */
558 if (regs->psw.mask & PSW_MASK_PSTATE)
559 local_irq_enable();
560 542
561 if (regs->psw.mask & PSW_MASK_PSTATE) { 543 if (regs->psw.mask & PSW_MASK_PSTATE) {
562 get_user(*((__u16 *) opcode), location); 544 get_user(*((__u16 *) opcode), location);
@@ -592,35 +574,29 @@ specification_exception(struct pt_regs * regs, long interruption_code)
592 574
593 if (signal == SIGFPE) 575 if (signal == SIGFPE)
594 do_fp_trap(regs, location, 576 do_fp_trap(regs, location,
595 current->thread.fp_regs.fpc, interruption_code); 577 current->thread.fp_regs.fpc, pgm_int_code);
596 else if (signal) { 578 else if (signal) {
597 siginfo_t info; 579 siginfo_t info;
598 info.si_signo = signal; 580 info.si_signo = signal;
599 info.si_errno = 0; 581 info.si_errno = 0;
600 info.si_code = ILL_ILLOPN; 582 info.si_code = ILL_ILLOPN;
601 info.si_addr = location; 583 info.si_addr = location;
602 do_trap(interruption_code, signal, 584 do_trap(pgm_int_code, signal,
603 "specification exception", regs, &info); 585 "specification exception", regs, &info);
604 } 586 }
605} 587}
606#else 588#else
607DO_ERROR_INFO(SIGILL, "specification exception", specification_exception, 589DO_ERROR_INFO(specification_exception, SIGILL, ILL_ILLOPN,
608 ILL_ILLOPN, get_check_address(regs)); 590 "specification exception");
609#endif 591#endif
610 592
611static void data_exception(struct pt_regs * regs, long interruption_code) 593static void data_exception(struct pt_regs *regs, long pgm_int_code,
594 unsigned long trans_exc_code)
612{ 595{
613 __u16 __user *location; 596 __u16 __user *location;
614 int signal = 0; 597 int signal = 0;
615 598
616 location = get_check_address(regs); 599 location = get_psw_address(regs, pgm_int_code);
617
618 /*
619 * We got all needed information from the lowcore and can
620 * now safely switch on interrupts.
621 */
622 if (regs->psw.mask & PSW_MASK_PSTATE)
623 local_irq_enable();
624 600
625 if (MACHINE_HAS_IEEE) 601 if (MACHINE_HAS_IEEE)
626 asm volatile("stfpc %0" : "=m" (current->thread.fp_regs.fpc)); 602 asm volatile("stfpc %0" : "=m" (current->thread.fp_regs.fpc));
@@ -686,19 +662,19 @@ static void data_exception(struct pt_regs * regs, long interruption_code)
686 signal = SIGILL; 662 signal = SIGILL;
687 if (signal == SIGFPE) 663 if (signal == SIGFPE)
688 do_fp_trap(regs, location, 664 do_fp_trap(regs, location,
689 current->thread.fp_regs.fpc, interruption_code); 665 current->thread.fp_regs.fpc, pgm_int_code);
690 else if (signal) { 666 else if (signal) {
691 siginfo_t info; 667 siginfo_t info;
692 info.si_signo = signal; 668 info.si_signo = signal;
693 info.si_errno = 0; 669 info.si_errno = 0;
694 info.si_code = ILL_ILLOPN; 670 info.si_code = ILL_ILLOPN;
695 info.si_addr = location; 671 info.si_addr = location;
696 do_trap(interruption_code, signal, 672 do_trap(pgm_int_code, signal, "data exception", regs, &info);
697 "data exception", regs, &info);
698 } 673 }
699} 674}
700 675
701static void space_switch_exception(struct pt_regs * regs, long int_code) 676static void space_switch_exception(struct pt_regs *regs, long pgm_int_code,
677 unsigned long trans_exc_code)
702{ 678{
703 siginfo_t info; 679 siginfo_t info;
704 680
@@ -709,8 +685,8 @@ static void space_switch_exception(struct pt_regs * regs, long int_code)
709 info.si_signo = SIGILL; 685 info.si_signo = SIGILL;
710 info.si_errno = 0; 686 info.si_errno = 0;
711 info.si_code = ILL_PRVOPC; 687 info.si_code = ILL_PRVOPC;
712 info.si_addr = get_check_address(regs); 688 info.si_addr = get_psw_address(regs, pgm_int_code);
713 do_trap(int_code, SIGILL, "space switch event", regs, &info); 689 do_trap(pgm_int_code, SIGILL, "space switch event", regs, &info);
714} 690}
715 691
716asmlinkage void kernel_stack_overflow(struct pt_regs * regs) 692asmlinkage void kernel_stack_overflow(struct pt_regs * regs)