diff options
author | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2010-10-25 10:10:37 -0400 |
---|---|---|
committer | Martin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com> | 2010-10-25 10:10:19 -0400 |
commit | 1e54622e0403891b10f2105663e0f9dd595a1f17 (patch) | |
tree | 4d16341d7a3d0f3c46fcc275560a9206bccac07f /arch/s390/kernel/traps.c | |
parent | 84afdcee620b1640f2a145c07febae4ed68947f9 (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.c | 172 |
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 | ||
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 | ||
@@ -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 | ||
461 | static void illegal_op(struct pt_regs * regs, long interruption_code) | 455 | static 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 |
545 | asmlinkage void | 533 | asmlinkage void specification_exception(struct pt_regs *regs, |
546 | specification_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 |
607 | DO_ERROR_INFO(SIGILL, "specification exception", specification_exception, | 589 | DO_ERROR_INFO(specification_exception, SIGILL, ILL_ILLOPN, |
608 | ILL_ILLOPN, get_check_address(regs)); | 590 | "specification exception"); |
609 | #endif | 591 | #endif |
610 | 592 | ||
611 | static void data_exception(struct pt_regs * regs, long interruption_code) | 593 | static 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 | ||
701 | static void space_switch_exception(struct pt_regs * regs, long int_code) | 676 | static 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 | ||
716 | asmlinkage void kernel_stack_overflow(struct pt_regs * regs) | 692 | asmlinkage void kernel_stack_overflow(struct pt_regs * regs) |