aboutsummaryrefslogtreecommitdiffstats
path: root/arch/openrisc
diff options
context:
space:
mode:
authorJonas Bonn <jonas@southpole.se>2012-02-19 11:36:53 -0500
committerJonas Bonn <jonas@southpole.se>2014-01-09 04:57:21 -0500
commit10f67dbf6add97751050f294d4c8e0cc1e5c2c23 (patch)
tree2a4365dc7fa9cc9ea4b1bb8a99ae83f353172d29 /arch/openrisc
parentd6e0a2dd12f4067a5bcefb8bbd8ddbeff800afbc (diff)
openrisc: Rework signal handling
The mainline signal handling code for OpenRISC has been buggy since day one with respect to syscall restart. This patch significantly reworks the signal handling code: i) Move the "work pending" loop to C code (borrowed from ARM arch) ii) Allow a tracer to muck about with the IP and skip syscall restart in that case (again, borrowed from ARM) iii) Make signal handling WRT syscall restart actually work v) Make the signal handling code look more like that of other architectures so that it's easier for others to follow Reported-by: Anders Nystrom <anders@southpole.se> Signed-off-by: Jonas Bonn <jonas@southpole.se>
Diffstat (limited to 'arch/openrisc')
-rw-r--r--arch/openrisc/kernel/entry.S59
-rw-r--r--arch/openrisc/kernel/signal.c198
2 files changed, 139 insertions, 118 deletions
diff --git a/arch/openrisc/kernel/entry.S b/arch/openrisc/kernel/entry.S
index d8a455ede5a7..fec8bf97d806 100644
--- a/arch/openrisc/kernel/entry.S
+++ b/arch/openrisc/kernel/entry.S
@@ -853,37 +853,44 @@ UNHANDLED_EXCEPTION(_vector_0x1f00,0x1f00)
853 853
854/* ========================================================[ return ] === */ 854/* ========================================================[ return ] === */
855 855
856_resume_userspace:
857 DISABLE_INTERRUPTS(r3,r4)
858 l.lwz r4,TI_FLAGS(r10)
859 l.andi r13,r4,_TIF_WORK_MASK
860 l.sfeqi r13,0
861 l.bf _restore_all
862 l.nop
863
856_work_pending: 864_work_pending:
857 /* 865 l.lwz r5,PT_ORIG_GPR11(r1)
858 * if (current_thread_info->flags & _TIF_NEED_RESCHED) 866 l.sfltsi r5,0
859 * schedule(); 867 l.bnf 1f
860 */
861 l.lwz r5,TI_FLAGS(r10)
862 l.andi r3,r5,_TIF_NEED_RESCHED
863 l.sfnei r3,0
864 l.bnf _work_notifysig
865 l.nop 868 l.nop
866 l.jal schedule 869 l.andi r5,r5,0
8701:
871 l.jal do_work_pending
872 l.ori r3,r1,0 /* pt_regs */
873
874 l.sfeqi r11,0
875 l.bf _restore_all
867 l.nop 876 l.nop
868 l.j _resume_userspace 877 l.sfltsi r11,0
878 l.bnf 1f
869 l.nop 879 l.nop
870 880 l.and r11,r11,r0
871/* Handle pending signals and notify-resume requests. 881 l.ori r11,r11,__NR_restart_syscall
872 * do_notify_resume must be passed the latest pushed pt_regs, not 882 l.j _syscall_check_trace_enter
873 * necessarily the "userspace" ones. Also, pt_regs->syscallno
874 * must be set so that the syscall restart functionality works.
875 */
876_work_notifysig:
877 l.jal do_notify_resume
878 l.ori r3,r1,0 /* pt_regs */
879
880_resume_userspace:
881 DISABLE_INTERRUPTS(r3,r4)
882 l.lwz r3,TI_FLAGS(r10)
883 l.andi r3,r3,_TIF_WORK_MASK
884 l.sfnei r3,0
885 l.bf _work_pending
886 l.nop 883 l.nop
8841:
885 l.lwz r11,PT_ORIG_GPR11(r1)
886 /* Restore arg registers */
887 l.lwz r3,PT_GPR3(r1)
888 l.lwz r4,PT_GPR4(r1)
889 l.lwz r5,PT_GPR5(r1)
890 l.lwz r6,PT_GPR6(r1)
891 l.lwz r7,PT_GPR7(r1)
892 l.j _syscall_check_trace_enter
893 l.lwz r8,PT_GPR8(r1)
887 894
888_restore_all: 895_restore_all:
889 RESTORE_ALL 896 RESTORE_ALL
diff --git a/arch/openrisc/kernel/signal.c b/arch/openrisc/kernel/signal.c
index ae167f7e081a..c277ec82783d 100644
--- a/arch/openrisc/kernel/signal.c
+++ b/arch/openrisc/kernel/signal.c
@@ -28,24 +28,24 @@
28#include <linux/tracehook.h> 28#include <linux/tracehook.h>
29 29
30#include <asm/processor.h> 30#include <asm/processor.h>
31#include <asm/syscall.h>
31#include <asm/ucontext.h> 32#include <asm/ucontext.h>
32#include <asm/uaccess.h> 33#include <asm/uaccess.h>
33 34
34#define DEBUG_SIG 0 35#define DEBUG_SIG 0
35 36
36struct rt_sigframe { 37struct rt_sigframe {
37 struct siginfo *pinfo;
38 void *puc;
39 struct siginfo info; 38 struct siginfo info;
40 struct ucontext uc; 39 struct ucontext uc;
41 unsigned char retcode[16]; /* trampoline code */ 40 unsigned char retcode[16]; /* trampoline code */
42}; 41};
43 42
44static int restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc) 43static int restore_sigcontext(struct pt_regs *regs,
44 struct sigcontext __user *sc)
45{ 45{
46 unsigned int err = 0; 46 int err = 0;
47 47
48 /* Alwys make any pending restarted system call return -EINTR */ 48 /* Always make any pending restarted system calls return -EINTR */
49 current_thread_info()->restart_block.fn = do_no_restart_syscall; 49 current_thread_info()->restart_block.fn = do_no_restart_syscall;
50 50
51 /* 51 /*
@@ -53,25 +53,21 @@ static int restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc)
53 * (sc is already checked for VERIFY_READ since the sigframe was 53 * (sc is already checked for VERIFY_READ since the sigframe was
54 * checked in sys_sigreturn previously) 54 * checked in sys_sigreturn previously)
55 */ 55 */
56 if (__copy_from_user(regs, sc->regs.gpr, 32 * sizeof(unsigned long))) 56 err |= __copy_from_user(regs, sc->regs.gpr, 32 * sizeof(unsigned long));
57 goto badframe; 57 err |= __copy_from_user(&regs->pc, &sc->regs.pc, sizeof(unsigned long));
58 if (__copy_from_user(&regs->pc, &sc->regs.pc, sizeof(unsigned long))) 58 err |= __copy_from_user(&regs->sr, &sc->regs.sr, sizeof(unsigned long));
59 goto badframe;
60 if (__copy_from_user(&regs->sr, &sc->regs.sr, sizeof(unsigned long)))
61 goto badframe;
62 59
63 /* make sure the SM-bit is cleared so user-mode cannot fool us */ 60 /* make sure the SM-bit is cleared so user-mode cannot fool us */
64 regs->sr &= ~SPR_SR_SM; 61 regs->sr &= ~SPR_SR_SM;
65 62
63 regs->orig_gpr11 = -1; /* Avoid syscall restart checks */
64
66 /* TODO: the other ports use regs->orig_XX to disable syscall checks 65 /* TODO: the other ports use regs->orig_XX to disable syscall checks
67 * after this completes, but we don't use that mechanism. maybe we can 66 * after this completes, but we don't use that mechanism. maybe we can
68 * use it now ? 67 * use it now ?
69 */ 68 */
70 69
71 return err; 70 return err;
72
73badframe:
74 return 1;
75} 71}
76 72
77asmlinkage long _sys_rt_sigreturn(struct pt_regs *regs) 73asmlinkage long _sys_rt_sigreturn(struct pt_regs *regs)
@@ -111,21 +107,18 @@ badframe:
111 * Set up a signal frame. 107 * Set up a signal frame.
112 */ 108 */
113 109
114static int setup_sigcontext(struct sigcontext *sc, struct pt_regs *regs, 110static int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
115 unsigned long mask)
116{ 111{
117 int err = 0; 112 int err = 0;
118 113
119 /* copy the regs */ 114 /* copy the regs */
120 115 /* There should be no need to save callee-saved registers here...
116 * ...but we save them anyway. Revisit this
117 */
121 err |= __copy_to_user(sc->regs.gpr, regs, 32 * sizeof(unsigned long)); 118 err |= __copy_to_user(sc->regs.gpr, regs, 32 * sizeof(unsigned long));
122 err |= __copy_to_user(&sc->regs.pc, &regs->pc, sizeof(unsigned long)); 119 err |= __copy_to_user(&sc->regs.pc, &regs->pc, sizeof(unsigned long));
123 err |= __copy_to_user(&sc->regs.sr, &regs->sr, sizeof(unsigned long)); 120 err |= __copy_to_user(&sc->regs.sr, &regs->sr, sizeof(unsigned long));
124 121
125 /* then some other stuff */
126
127 err |= __put_user(mask, &sc->oldmask);
128
129 return err; 122 return err;
130} 123}
131 124
@@ -181,24 +174,18 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
181 int err = 0; 174 int err = 0;
182 175
183 frame = get_sigframe(ka, regs, sizeof(*frame)); 176 frame = get_sigframe(ka, regs, sizeof(*frame));
184
185 if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) 177 if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
186 goto give_sigsegv; 178 goto give_sigsegv;
187 179
188 err |= __put_user(&frame->info, &frame->pinfo); 180 /* Create siginfo. */
189 err |= __put_user(&frame->uc, &frame->puc);
190
191 if (ka->sa.sa_flags & SA_SIGINFO) 181 if (ka->sa.sa_flags & SA_SIGINFO)
192 err |= copy_siginfo_to_user(&frame->info, info); 182 err |= copy_siginfo_to_user(&frame->info, info);
193 if (err)
194 goto give_sigsegv;
195 183
196 /* Clear all the bits of the ucontext we don't use. */ 184 /* Create the ucontext. */
197 err |= __clear_user(&frame->uc, offsetof(struct ucontext, uc_mcontext));
198 err |= __put_user(0, &frame->uc.uc_flags); 185 err |= __put_user(0, &frame->uc.uc_flags);
199 err |= __put_user(NULL, &frame->uc.uc_link); 186 err |= __put_user(NULL, &frame->uc.uc_link);
200 err |= __save_altstack(&frame->uc.uc_stack, regs->sp); 187 err |= __save_altstack(&frame->uc.uc_stack, regs->sp);
201 err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, set->sig[0]); 188 err |= setup_sigcontext(regs, &frame->uc.uc_mcontext);
202 189
203 err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); 190 err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
204 191
@@ -207,9 +194,12 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
207 194
208 /* trampoline - the desired return ip is the retcode itself */ 195 /* trampoline - the desired return ip is the retcode itself */
209 return_ip = (unsigned long)&frame->retcode; 196 return_ip = (unsigned long)&frame->retcode;
210 /* This is l.ori r11,r0,__NR_sigreturn, l.sys 1 */ 197 /* This is:
211 err |= __put_user(0xa960, (short *)(frame->retcode + 0)); 198 l.ori r11,r0,__NR_sigreturn
212 err |= __put_user(__NR_rt_sigreturn, (short *)(frame->retcode + 2)); 199 l.sys 1
200 */
201 err |= __put_user(0xa960, (short *)(frame->retcode + 0));
202 err |= __put_user(__NR_rt_sigreturn, (short *)(frame->retcode + 2));
213 err |= __put_user(0x20000001, (unsigned long *)(frame->retcode + 4)); 203 err |= __put_user(0x20000001, (unsigned long *)(frame->retcode + 4));
214 err |= __put_user(0x15000000, (unsigned long *)(frame->retcode + 8)); 204 err |= __put_user(0x15000000, (unsigned long *)(frame->retcode + 8));
215 205
@@ -262,82 +252,106 @@ handle_signal(unsigned long sig,
262 * mode below. 252 * mode below.
263 */ 253 */
264 254
265void do_signal(struct pt_regs *regs) 255int do_signal(struct pt_regs *regs, int syscall)
266{ 256{
267 siginfo_t info; 257 siginfo_t info;
268 int signr; 258 int signr;
269 struct k_sigaction ka; 259 struct k_sigaction ka;
270 260 unsigned long continue_addr = 0;
271 /* 261 unsigned long restart_addr = 0;
272 * We want the common case to go fast, which 262 unsigned long retval = 0;
273 * is why we may in certain cases get here from 263 int restart = 0;
274 * kernel mode. Just return without doing anything 264
275 * if so. 265 if (syscall) {
276 */ 266 continue_addr = regs->pc;
277 if (!user_mode(regs)) 267 restart_addr = continue_addr - 4;
278 return; 268 retval = regs->gpr[11];
279 269
280 signr = get_signal_to_deliver(&info, &ka, regs, NULL); 270 /*
281 271 * Setup syscall restart here so that a debugger will
282 /* If we are coming out of a syscall then we need 272 * see the already changed PC.
283 * to check if the syscall was interrupted and wants to be 273 */
284 * restarted after handling the signal. If so, the original 274 switch (retval) {
285 * syscall number is put back into r11 and the PC rewound to
286 * point at the l.sys instruction that resulted in the
287 * original syscall. Syscall results other than the four
288 * below mean that the syscall executed to completion and no
289 * restart is necessary.
290 */
291 if (regs->orig_gpr11) {
292 int restart = 0;
293
294 switch (regs->gpr[11]) {
295 case -ERESTART_RESTARTBLOCK: 275 case -ERESTART_RESTARTBLOCK:
276 restart = -2;
277 /* Fall through */
296 case -ERESTARTNOHAND: 278 case -ERESTARTNOHAND:
297 /* Restart if there is no signal handler */
298 restart = (signr <= 0);
299 break;
300 case -ERESTARTSYS: 279 case -ERESTARTSYS:
301 /* Restart if there no signal handler or
302 * SA_RESTART flag is set */
303 restart = (signr <= 0 || (ka.sa.sa_flags & SA_RESTART));
304 break;
305 case -ERESTARTNOINTR: 280 case -ERESTARTNOINTR:
306 /* Always restart */ 281 restart++;
307 restart = 1; 282 regs->gpr[11] = regs->orig_gpr11;
283 regs->pc = restart_addr;
308 break; 284 break;
309 } 285 }
286 }
310 287
311 if (restart) { 288 /*
312 if (regs->gpr[11] == -ERESTART_RESTARTBLOCK) 289 * Get the signal to deliver. When running under ptrace, at this
313 regs->gpr[11] = __NR_restart_syscall; 290 * point the debugger may change all our registers ...
314 else 291 */
315 regs->gpr[11] = regs->orig_gpr11; 292 signr = get_signal_to_deliver(&info, &ka, regs, NULL);
316 regs->pc -= 4; 293 /*
317 } else { 294 * Depending on the signal settings we may need to revert the
318 regs->gpr[11] = -EINTR; 295 * decision to restart the system call. But skip this if a
296 * debugger has chosen to restart at a different PC.
297 */
298 if (signr > 0) {
299 if (unlikely(restart) && regs->pc == restart_addr) {
300 if (retval == -ERESTARTNOHAND ||
301 retval == -ERESTART_RESTARTBLOCK
302 || (retval == -ERESTARTSYS
303 && !(ka.sa.sa_flags & SA_RESTART))) {
304 /* No automatic restart */
305 regs->gpr[11] = -EINTR;
306 regs->pc = continue_addr;
307 }
319 } 308 }
320 }
321 309
322 if (signr <= 0) {
323 /* no signal to deliver so we just put the saved sigmask
324 * back */
325 restore_saved_sigmask();
326 } else { /* signr > 0 */
327 /* Whee! Actually deliver the signal. */
328 handle_signal(signr, &info, &ka, regs); 310 handle_signal(signr, &info, &ka, regs);
311 } else {
312 /* no handler */
313 restore_saved_sigmask();
314 /*
315 * Restore pt_regs PC as syscall restart will be handled by
316 * kernel without return to userspace
317 */
318 if (unlikely(restart) && regs->pc == restart_addr) {
319 regs->pc = continue_addr;
320 return restart;
321 }
329 } 322 }
330 323
331 return; 324 return 0;
332} 325}
333 326
334asmlinkage void do_notify_resume(struct pt_regs *regs) 327asmlinkage int
328do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall)
335{ 329{
336 if (current_thread_info()->flags & _TIF_SIGPENDING) 330 do {
337 do_signal(regs); 331 if (likely(thread_flags & _TIF_NEED_RESCHED)) {
338 332 schedule();
339 if (current_thread_info()->flags & _TIF_NOTIFY_RESUME) { 333 } else {
340 clear_thread_flag(TIF_NOTIFY_RESUME); 334 if (unlikely(!user_mode(regs)))
341 tracehook_notify_resume(regs); 335 return 0;
342 } 336 local_irq_enable();
337 if (thread_flags & _TIF_SIGPENDING) {
338 int restart = do_signal(regs, syscall);
339 if (unlikely(restart)) {
340 /*
341 * Restart without handlers.
342 * Deal with it without leaving
343 * the kernel space.
344 */
345 return restart;
346 }
347 syscall = 0;
348 } else {
349 clear_thread_flag(TIF_NOTIFY_RESUME);
350 tracehook_notify_resume(regs);
351 }
352 }
353 local_irq_disable();
354 thread_flags = current_thread_info()->flags;
355 } while (thread_flags & _TIF_WORK_MASK);
356 return 0;
343} 357}