diff options
Diffstat (limited to 'arch/s390/kernel/traps.c')
-rw-r--r-- | arch/s390/kernel/traps.c | 173 |
1 files changed, 74 insertions, 99 deletions
diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c index 5d8f0f3d0250..70640822621a 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 | ||
332 | static void __kprobes inline do_trap(long interruption_code, int signr, | 332 | static 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 | ||
369 | static inline void __user *get_check_address(struct pt_regs *regs) | 361 | static 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 | ||
374 | void __kprobes do_single_step(struct pt_regs *regs) | 368 | void __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 | ||
384 | static void default_trap_handler(struct pt_regs * regs, long interruption_code) | 378 | static 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) \ |
395 | static void name(struct pt_regs * regs, long interruption_code) \ | 389 | static 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 | ||
405 | DO_ERROR_INFO(SIGILL, "addressing exception", addressing_exception, | 400 | DO_ERROR_INFO(addressing_exception, SIGILL, ILL_ILLADR, |
406 | ILL_ILLADR, get_check_address(regs)) | 401 | "addressing exception") |
407 | DO_ERROR_INFO(SIGILL, "execute exception", execute_exception, | 402 | DO_ERROR_INFO(execute_exception, SIGILL, ILL_ILLOPN, |
408 | ILL_ILLOPN, get_check_address(regs)) | 403 | "execute exception") |
409 | DO_ERROR_INFO(SIGFPE, "fixpoint divide exception", divide_exception, | 404 | DO_ERROR_INFO(divide_exception, SIGFPE, FPE_INTDIV, |
410 | FPE_INTDIV, get_check_address(regs)) | 405 | "fixpoint divide exception") |
411 | DO_ERROR_INFO(SIGFPE, "fixpoint overflow exception", overflow_exception, | 406 | DO_ERROR_INFO(overflow_exception, SIGFPE, FPE_INTOVF, |
412 | FPE_INTOVF, get_check_address(regs)) | 407 | "fixpoint overflow exception") |
413 | DO_ERROR_INFO(SIGFPE, "HFP overflow exception", hfp_overflow_exception, | 408 | DO_ERROR_INFO(hfp_overflow_exception, SIGFPE, FPE_FLTOVF, |
414 | FPE_FLTOVF, get_check_address(regs)) | 409 | "HFP overflow exception") |
415 | DO_ERROR_INFO(SIGFPE, "HFP underflow exception", hfp_underflow_exception, | 410 | DO_ERROR_INFO(hfp_underflow_exception, SIGFPE, FPE_FLTUND, |
416 | FPE_FLTUND, get_check_address(regs)) | 411 | "HFP underflow exception") |
417 | DO_ERROR_INFO(SIGFPE, "HFP significance exception", hfp_significance_exception, | 412 | DO_ERROR_INFO(hfp_significance_exception, SIGFPE, FPE_FLTRES, |
418 | FPE_FLTRES, get_check_address(regs)) | 413 | "HFP significance exception") |
419 | DO_ERROR_INFO(SIGFPE, "HFP divide exception", hfp_divide_exception, | 414 | DO_ERROR_INFO(hfp_divide_exception, SIGFPE, FPE_FLTDIV, |
420 | FPE_FLTDIV, get_check_address(regs)) | 415 | "HFP divide exception") |
421 | DO_ERROR_INFO(SIGFPE, "HFP square root exception", hfp_sqrt_exception, | 416 | DO_ERROR_INFO(hfp_sqrt_exception, SIGFPE, FPE_FLTINV, |
422 | FPE_FLTINV, get_check_address(regs)) | 417 | "HFP square root exception") |
423 | DO_ERROR_INFO(SIGILL, "operand exception", operand_exception, | 418 | DO_ERROR_INFO(operand_exception, SIGILL, ILL_ILLOPN, |
424 | ILL_ILLOPN, get_check_address(regs)) | 419 | "operand exception") |
425 | DO_ERROR_INFO(SIGILL, "privileged operation", privileged_op, | 420 | DO_ERROR_INFO(privileged_op, SIGILL, ILL_PRVOPC, |
426 | ILL_PRVOPC, get_check_address(regs)) | 421 | "privileged operation") |
427 | DO_ERROR_INFO(SIGILL, "special operation exception", special_op_exception, | 422 | DO_ERROR_INFO(special_op_exception, SIGILL, ILL_ILLOPN, |
428 | ILL_ILLOPN, get_check_address(regs)) | 423 | "special operation exception") |
429 | DO_ERROR_INFO(SIGILL, "translation exception", translation_exception, | 424 | DO_ERROR_INFO(translation_exception, SIGILL, ILL_ILLOPN, |
430 | ILL_ILLOPN, get_check_address(regs)) | 425 | "translation exception") |
431 | 426 | ||
432 | static inline void | 427 | static inline void do_fp_trap(struct pt_regs *regs, void __user *location, |
433 | do_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 | ||
@@ -453,26 +447,19 @@ do_fp_trap(struct pt_regs *regs, void __user *location, | |||
453 | else if (fpc & 0x0800) /* inexact */ | 447 | else if (fpc & 0x0800) /* inexact */ |
454 | si.si_code = FPE_FLTRES; | 448 | si.si_code = FPE_FLTRES; |
455 | } | 449 | } |
456 | current->thread.ieee_instruction_pointer = (addr_t) location; | 450 | do_trap(pgm_int_code, SIGFPE, |
457 | do_trap(interruption_code, SIGFPE, | ||
458 | "floating point exception", regs, &si); | 451 | "floating point exception", regs, &si); |
459 | } | 452 | } |
460 | 453 | ||
461 | static void illegal_op(struct pt_regs * regs, long interruption_code) | 454 | static void illegal_op(struct pt_regs *regs, long pgm_int_code, |
455 | unsigned long trans_exc_code) | ||
462 | { | 456 | { |
463 | siginfo_t info; | 457 | siginfo_t info; |
464 | __u8 opcode[6]; | 458 | __u8 opcode[6]; |
465 | __u16 __user *location; | 459 | __u16 __user *location; |
466 | int signal = 0; | 460 | int signal = 0; |
467 | 461 | ||
468 | location = get_check_address(regs); | 462 | 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 | 463 | ||
477 | if (regs->psw.mask & PSW_MASK_PSTATE) { | 464 | if (regs->psw.mask & PSW_MASK_PSTATE) { |
478 | if (get_user(*((__u16 *) opcode), (__u16 __user *) location)) | 465 | if (get_user(*((__u16 *) opcode), (__u16 __user *) location)) |
@@ -512,7 +499,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 | 499 | * If we get an illegal op in kernel mode, send it through the |
513 | * kprobes notifier. If kprobes doesn't pick it up, SIGILL | 500 | * kprobes notifier. If kprobes doesn't pick it up, SIGILL |
514 | */ | 501 | */ |
515 | if (notify_die(DIE_BPT, "bpt", regs, interruption_code, | 502 | if (notify_die(DIE_BPT, "bpt", regs, pgm_int_code, |
516 | 3, SIGTRAP) != NOTIFY_STOP) | 503 | 3, SIGTRAP) != NOTIFY_STOP) |
517 | signal = SIGILL; | 504 | signal = SIGILL; |
518 | } | 505 | } |
@@ -520,13 +507,13 @@ static void illegal_op(struct pt_regs * regs, long interruption_code) | |||
520 | #ifdef CONFIG_MATHEMU | 507 | #ifdef CONFIG_MATHEMU |
521 | if (signal == SIGFPE) | 508 | if (signal == SIGFPE) |
522 | do_fp_trap(regs, location, | 509 | do_fp_trap(regs, location, |
523 | current->thread.fp_regs.fpc, interruption_code); | 510 | current->thread.fp_regs.fpc, pgm_int_code); |
524 | else if (signal == SIGSEGV) { | 511 | else if (signal == SIGSEGV) { |
525 | info.si_signo = signal; | 512 | info.si_signo = signal; |
526 | info.si_errno = 0; | 513 | info.si_errno = 0; |
527 | info.si_code = SEGV_MAPERR; | 514 | info.si_code = SEGV_MAPERR; |
528 | info.si_addr = (void __user *) location; | 515 | info.si_addr = (void __user *) location; |
529 | do_trap(interruption_code, signal, | 516 | do_trap(pgm_int_code, signal, |
530 | "user address fault", regs, &info); | 517 | "user address fault", regs, &info); |
531 | } else | 518 | } else |
532 | #endif | 519 | #endif |
@@ -535,28 +522,22 @@ static void illegal_op(struct pt_regs * regs, long interruption_code) | |||
535 | info.si_errno = 0; | 522 | info.si_errno = 0; |
536 | info.si_code = ILL_ILLOPC; | 523 | info.si_code = ILL_ILLOPC; |
537 | info.si_addr = (void __user *) location; | 524 | info.si_addr = (void __user *) location; |
538 | do_trap(interruption_code, signal, | 525 | do_trap(pgm_int_code, signal, |
539 | "illegal operation", regs, &info); | 526 | "illegal operation", regs, &info); |
540 | } | 527 | } |
541 | } | 528 | } |
542 | 529 | ||
543 | 530 | ||
544 | #ifdef CONFIG_MATHEMU | 531 | #ifdef CONFIG_MATHEMU |
545 | asmlinkage void | 532 | asmlinkage void specification_exception(struct pt_regs *regs, |
546 | specification_exception(struct pt_regs * regs, long interruption_code) | 533 | long pgm_int_code, |
534 | unsigned long trans_exc_code) | ||
547 | { | 535 | { |
548 | __u8 opcode[6]; | 536 | __u8 opcode[6]; |
549 | __u16 __user *location = NULL; | 537 | __u16 __user *location = NULL; |
550 | int signal = 0; | 538 | int signal = 0; |
551 | 539 | ||
552 | location = (__u16 __user *) get_check_address(regs); | 540 | 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 | 541 | ||
561 | if (regs->psw.mask & PSW_MASK_PSTATE) { | 542 | if (regs->psw.mask & PSW_MASK_PSTATE) { |
562 | get_user(*((__u16 *) opcode), location); | 543 | get_user(*((__u16 *) opcode), location); |
@@ -592,35 +573,29 @@ specification_exception(struct pt_regs * regs, long interruption_code) | |||
592 | 573 | ||
593 | if (signal == SIGFPE) | 574 | if (signal == SIGFPE) |
594 | do_fp_trap(regs, location, | 575 | do_fp_trap(regs, location, |
595 | current->thread.fp_regs.fpc, interruption_code); | 576 | current->thread.fp_regs.fpc, pgm_int_code); |
596 | else if (signal) { | 577 | else if (signal) { |
597 | siginfo_t info; | 578 | siginfo_t info; |
598 | info.si_signo = signal; | 579 | info.si_signo = signal; |
599 | info.si_errno = 0; | 580 | info.si_errno = 0; |
600 | info.si_code = ILL_ILLOPN; | 581 | info.si_code = ILL_ILLOPN; |
601 | info.si_addr = location; | 582 | info.si_addr = location; |
602 | do_trap(interruption_code, signal, | 583 | do_trap(pgm_int_code, signal, |
603 | "specification exception", regs, &info); | 584 | "specification exception", regs, &info); |
604 | } | 585 | } |
605 | } | 586 | } |
606 | #else | 587 | #else |
607 | DO_ERROR_INFO(SIGILL, "specification exception", specification_exception, | 588 | DO_ERROR_INFO(specification_exception, SIGILL, ILL_ILLOPN, |
608 | ILL_ILLOPN, get_check_address(regs)); | 589 | "specification exception"); |
609 | #endif | 590 | #endif |
610 | 591 | ||
611 | static void data_exception(struct pt_regs * regs, long interruption_code) | 592 | static void data_exception(struct pt_regs *regs, long pgm_int_code, |
593 | unsigned long trans_exc_code) | ||
612 | { | 594 | { |
613 | __u16 __user *location; | 595 | __u16 __user *location; |
614 | int signal = 0; | 596 | int signal = 0; |
615 | 597 | ||
616 | location = get_check_address(regs); | 598 | 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 | 599 | ||
625 | if (MACHINE_HAS_IEEE) | 600 | if (MACHINE_HAS_IEEE) |
626 | asm volatile("stfpc %0" : "=m" (current->thread.fp_regs.fpc)); | 601 | asm volatile("stfpc %0" : "=m" (current->thread.fp_regs.fpc)); |
@@ -686,19 +661,19 @@ static void data_exception(struct pt_regs * regs, long interruption_code) | |||
686 | signal = SIGILL; | 661 | signal = SIGILL; |
687 | if (signal == SIGFPE) | 662 | if (signal == SIGFPE) |
688 | do_fp_trap(regs, location, | 663 | do_fp_trap(regs, location, |
689 | current->thread.fp_regs.fpc, interruption_code); | 664 | current->thread.fp_regs.fpc, pgm_int_code); |
690 | else if (signal) { | 665 | else if (signal) { |
691 | siginfo_t info; | 666 | siginfo_t info; |
692 | info.si_signo = signal; | 667 | info.si_signo = signal; |
693 | info.si_errno = 0; | 668 | info.si_errno = 0; |
694 | info.si_code = ILL_ILLOPN; | 669 | info.si_code = ILL_ILLOPN; |
695 | info.si_addr = location; | 670 | info.si_addr = location; |
696 | do_trap(interruption_code, signal, | 671 | do_trap(pgm_int_code, signal, "data exception", regs, &info); |
697 | "data exception", regs, &info); | ||
698 | } | 672 | } |
699 | } | 673 | } |
700 | 674 | ||
701 | static void space_switch_exception(struct pt_regs * regs, long int_code) | 675 | static void space_switch_exception(struct pt_regs *regs, long pgm_int_code, |
676 | unsigned long trans_exc_code) | ||
702 | { | 677 | { |
703 | siginfo_t info; | 678 | siginfo_t info; |
704 | 679 | ||
@@ -709,8 +684,8 @@ static void space_switch_exception(struct pt_regs * regs, long int_code) | |||
709 | info.si_signo = SIGILL; | 684 | info.si_signo = SIGILL; |
710 | info.si_errno = 0; | 685 | info.si_errno = 0; |
711 | info.si_code = ILL_PRVOPC; | 686 | info.si_code = ILL_PRVOPC; |
712 | info.si_addr = get_check_address(regs); | 687 | info.si_addr = get_psw_address(regs, pgm_int_code); |
713 | do_trap(int_code, SIGILL, "space switch event", regs, &info); | 688 | do_trap(pgm_int_code, SIGILL, "space switch event", regs, &info); |
714 | } | 689 | } |
715 | 690 | ||
716 | asmlinkage void kernel_stack_overflow(struct pt_regs * regs) | 691 | asmlinkage void kernel_stack_overflow(struct pt_regs * regs) |