diff options
author | Nicolas Pitre <nico@cam.org> | 2006-06-22 17:18:45 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2006-06-22 17:18:45 -0400 |
commit | f606a6ff222dc7dceeb4d0e214ce4f55d9c6b0e6 (patch) | |
tree | 6a44374b8085d5523743268f67bae7fd70e25a43 /arch/arm/kernel | |
parent | 92b7eb8ffc0741f1fd5fbd5458a466d608310442 (diff) |
[ARM] 3626/1: ARM EABI: fix syscall restarting
Patch from Nicolas Pitre
The RESTARTBLOCK case currently store some code on the stack to invoke
sys_restart_syscall. However this is ABI dependent and there is a
mismatch with the way __NR_restart_syscall gets defined when the kernel
is compiled for EABI.
There is also a long standing bug in the thumb case since with OABI the
__NR_restart_syscall value includes __NR_SYSCALL_BASE which should not
be the case for Thumb syscalls.
Credits to Yauheni Kaliuta <yauheni.kaliuta@gmail.com> for finding the
EABI bug.
Signed-off-by: Nicolas Pitre <nico@cam.org>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/kernel')
-rw-r--r-- | arch/arm/kernel/signal.c | 21 |
1 files changed, 19 insertions, 2 deletions
diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index a0cd0a90a10d..e9fe78033361 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c | |||
@@ -665,17 +665,33 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) | |||
665 | if (syscall) { | 665 | if (syscall) { |
666 | if (regs->ARM_r0 == -ERESTART_RESTARTBLOCK) { | 666 | if (regs->ARM_r0 == -ERESTART_RESTARTBLOCK) { |
667 | if (thumb_mode(regs)) { | 667 | if (thumb_mode(regs)) { |
668 | regs->ARM_r7 = __NR_restart_syscall; | 668 | regs->ARM_r7 = __NR_restart_syscall - __NR_SYSCALL_BASE; |
669 | regs->ARM_pc -= 2; | 669 | regs->ARM_pc -= 2; |
670 | } else { | 670 | } else { |
671 | #if defined(CONFIG_AEABI) && !defined(CONFIG_OABI_COMPAT) | ||
672 | regs->ARM_r7 = __NR_restart_syscall; | ||
673 | regs->ARM_pc -= 4; | ||
674 | #else | ||
671 | u32 __user *usp; | 675 | u32 __user *usp; |
676 | u32 swival = __NR_restart_syscall; | ||
672 | 677 | ||
673 | regs->ARM_sp -= 12; | 678 | regs->ARM_sp -= 12; |
674 | usp = (u32 __user *)regs->ARM_sp; | 679 | usp = (u32 __user *)regs->ARM_sp; |
675 | 680 | ||
681 | /* | ||
682 | * Either we supports OABI only, or we have | ||
683 | * EABI with the OABI compat layer enabled. | ||
684 | * In the later case we don't know if user | ||
685 | * space is EABI or not, and if not we must | ||
686 | * not clobber r7. Always using the OABI | ||
687 | * syscall solves that issue and works for | ||
688 | * all those cases. | ||
689 | */ | ||
690 | swival = swival - __NR_SYSCAll_BASE + __NR_OABI_SYSCALL_BASE; | ||
691 | |||
676 | put_user(regs->ARM_pc, &usp[0]); | 692 | put_user(regs->ARM_pc, &usp[0]); |
677 | /* swi __NR_restart_syscall */ | 693 | /* swi __NR_restart_syscall */ |
678 | put_user(0xef000000 | __NR_restart_syscall, &usp[1]); | 694 | put_user(0xef000000 | swival, &usp[1]); |
679 | /* ldr pc, [sp], #12 */ | 695 | /* ldr pc, [sp], #12 */ |
680 | put_user(0xe49df00c, &usp[2]); | 696 | put_user(0xe49df00c, &usp[2]); |
681 | 697 | ||
@@ -683,6 +699,7 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) | |||
683 | (unsigned long)(usp + 3)); | 699 | (unsigned long)(usp + 3)); |
684 | 700 | ||
685 | regs->ARM_pc = regs->ARM_sp + 4; | 701 | regs->ARM_pc = regs->ARM_sp + 4; |
702 | #endif | ||
686 | } | 703 | } |
687 | } | 704 | } |
688 | if (regs->ARM_r0 == -ERESTARTNOHAND || | 705 | if (regs->ARM_r0 == -ERESTARTNOHAND || |