aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorPaul Mackerras <paulus@samba.org>2006-03-07 21:24:22 -0500
committerPaul Mackerras <paulus@samba.org>2006-03-07 21:24:22 -0500
commit1bd79336a426c5e4f3bab142407059ceb12cadf9 (patch)
treedd8767b0ab3ce310c7df049822100e3838b37a97 /arch
parentab1b55e21f6977e420341727e9f4a50691057b5e (diff)
powerpc: Fix various syscall/signal/swapcontext bugs
A careful reading of the recent changes to the system call entry/exit paths revealed several problems, plus some things that could be simplified and improved: * 32-bit wasn't testing the _TIF_NOERROR bit in the syscall fast exit path, so it was only doing anything with it once it saw some other bit being set. In other words, the noerror behaviour would apply to the next system call where we had to reschedule or deliver a signal, which is not necessarily the current system call. * 32-bit wasn't doing the call to ptrace_notify in the syscall exit path when the _TIF_SINGLESTEP bit was set. * _TIF_RESTOREALL was in both _TIF_USER_WORK_MASK and _TIF_PERSYSCALL_MASK, which is odd since _TIF_RESTOREALL is only set by system calls. I took it out of _TIF_USER_WORK_MASK. * On 64-bit, _TIF_RESTOREALL wasn't causing the non-volatile registers to be restored (unless perhaps a signal was delivered or the syscall was traced or single-stepped). Thus the non-volatile registers weren't restored on exit from a signal handler. We probably got away with it mostly because signal handlers written in C wouldn't alter the non-volatile registers. * On 32-bit I simplified the code and made it more like 64-bit by making the syscall exit path jump to ret_from_except to handle preemption and signal delivery. * 32-bit was calling do_signal unnecessarily when _TIF_RESTOREALL was set - but I think because of that 32-bit was actually restoring the non-volatile registers on exit from a signal handler. * I changed the order of enabling interrupts and saving the non-volatile registers before calling do_syscall_trace_leave; now we enable interrupts first. Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch')
-rw-r--r--arch/powerpc/kernel/asm-offsets.c1
-rw-r--r--arch/powerpc/kernel/entry_32.S95
-rw-r--r--arch/powerpc/kernel/entry_64.S94
-rw-r--r--arch/powerpc/kernel/ptrace.c5
-rw-r--r--arch/powerpc/kernel/signal_32.c19
-rw-r--r--arch/powerpc/kernel/signal_64.c9
-rw-r--r--arch/powerpc/kernel/systbl.S2
-rw-r--r--arch/ppc/kernel/asm-offsets.c1
-rw-r--r--arch/ppc/kernel/entry.S95
9 files changed, 76 insertions, 245 deletions
diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
index 840aad43a98b..c9a660e4c2db 100644
--- a/arch/powerpc/kernel/asm-offsets.c
+++ b/arch/powerpc/kernel/asm-offsets.c
@@ -92,7 +92,6 @@ int main(void)
92 92
93 DEFINE(TI_FLAGS, offsetof(struct thread_info, flags)); 93 DEFINE(TI_FLAGS, offsetof(struct thread_info, flags));
94 DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count)); 94 DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count));
95 DEFINE(TI_SIGFRAME, offsetof(struct thread_info, nvgprs_frame));
96 DEFINE(TI_TASK, offsetof(struct thread_info, task)); 95 DEFINE(TI_TASK, offsetof(struct thread_info, task));
97#ifdef CONFIG_PPC32 96#ifdef CONFIG_PPC32
98 DEFINE(TI_EXECDOMAIN, offsetof(struct thread_info, exec_domain)); 97 DEFINE(TI_EXECDOMAIN, offsetof(struct thread_info, exec_domain));
diff --git a/arch/powerpc/kernel/entry_32.S b/arch/powerpc/kernel/entry_32.S
index f20a67261ec7..4827ca1ec89b 100644
--- a/arch/powerpc/kernel/entry_32.S
+++ b/arch/powerpc/kernel/entry_32.S
@@ -227,7 +227,7 @@ ret_from_syscall:
227 MTMSRD(r10) 227 MTMSRD(r10)
228 lwz r9,TI_FLAGS(r12) 228 lwz r9,TI_FLAGS(r12)
229 li r8,-_LAST_ERRNO 229 li r8,-_LAST_ERRNO
230 andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_RESTOREALL|_TIF_RESTORE_SIGMASK) 230 andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP|_TIF_USER_WORK_MASK|_TIF_PERSYSCALL_MASK)
231 bne- syscall_exit_work 231 bne- syscall_exit_work
232 cmplw 0,r3,r8 232 cmplw 0,r3,r8
233 blt+ syscall_exit_cont 233 blt+ syscall_exit_cont
@@ -287,8 +287,10 @@ syscall_dotrace:
287 287
288syscall_exit_work: 288syscall_exit_work:
289 andi. r0,r9,_TIF_RESTOREALL 289 andi. r0,r9,_TIF_RESTOREALL
290 bne- 2f 290 beq+ 0f
291 cmplw 0,r3,r8 291 REST_NVGPRS(r1)
292 b 2f
2930: cmplw 0,r3,r8
292 blt+ 1f 294 blt+ 1f
293 andi. r0,r9,_TIF_NOERROR 295 andi. r0,r9,_TIF_NOERROR
294 bne- 1f 296 bne- 1f
@@ -302,9 +304,7 @@ syscall_exit_work:
3022: andi. r0,r9,(_TIF_PERSYSCALL_MASK) 3042: andi. r0,r9,(_TIF_PERSYSCALL_MASK)
303 beq 4f 305 beq 4f
304 306
305 /* Clear per-syscall TIF flags if any are set, but _leave_ 307 /* Clear per-syscall TIF flags if any are set. */
306 _TIF_SAVE_NVGPRS set in r9 since we haven't dealt with that
307 yet. */
308 308
309 li r11,_TIF_PERSYSCALL_MASK 309 li r11,_TIF_PERSYSCALL_MASK
310 addi r12,r12,TI_FLAGS 310 addi r12,r12,TI_FLAGS
@@ -318,8 +318,13 @@ syscall_exit_work:
318 subi r12,r12,TI_FLAGS 318 subi r12,r12,TI_FLAGS
319 319
3204: /* Anything which requires enabling interrupts? */ 3204: /* Anything which requires enabling interrupts? */
321 andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP|_TIF_SAVE_NVGPRS) 321 andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP)
322 beq 7f 322 beq ret_from_except
323
324 /* Re-enable interrupts */
325 ori r10,r10,MSR_EE
326 SYNC
327 MTMSRD(r10)
323 328
324 /* Save NVGPRS if they're not saved already */ 329 /* Save NVGPRS if they're not saved already */
325 lwz r4,_TRAP(r1) 330 lwz r4,_TRAP(r1)
@@ -328,71 +333,11 @@ syscall_exit_work:
328 SAVE_NVGPRS(r1) 333 SAVE_NVGPRS(r1)
329 li r4,0xc00 334 li r4,0xc00
330 stw r4,_TRAP(r1) 335 stw r4,_TRAP(r1)
331 3365:
332 /* Re-enable interrupts */
3335: ori r10,r10,MSR_EE
334 SYNC
335 MTMSRD(r10)
336
337 andi. r0,r9,_TIF_SAVE_NVGPRS
338 bne save_user_nvgprs
339
340save_user_nvgprs_cont:
341 andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP)
342 beq 7f
343
344 addi r3,r1,STACK_FRAME_OVERHEAD 337 addi r3,r1,STACK_FRAME_OVERHEAD
345 bl do_syscall_trace_leave 338 bl do_syscall_trace_leave
346 REST_NVGPRS(r1) 339 b ret_from_except_full
347
3486: lwz r3,GPR3(r1)
349 LOAD_MSR_KERNEL(r10,MSR_KERNEL) /* doesn't include MSR_EE */
350 SYNC
351 MTMSRD(r10) /* disable interrupts again */
352 rlwinm r12,r1,0,0,(31-THREAD_SHIFT) /* current_thread_info() */
353 lwz r9,TI_FLAGS(r12)
3547:
355 andi. r0,r9,_TIF_NEED_RESCHED
356 bne 8f
357 lwz r5,_MSR(r1)
358 andi. r5,r5,MSR_PR
359 beq ret_from_except
360 andi. r0,r9,_TIF_SIGPENDING|_TIF_RESTORE_SIGMASK
361 beq ret_from_except
362 b do_user_signal
3638:
364 ori r10,r10,MSR_EE
365 SYNC
366 MTMSRD(r10) /* re-enable interrupts */
367 bl schedule
368 b 6b
369
370save_user_nvgprs:
371 lwz r8,TI_SIGFRAME(r12)
372
373.macro savewords start, end
374 1: stw \start,4*(\start)(r8)
375 .section __ex_table,"a"
376 .align 2
377 .long 1b,save_user_nvgprs_fault
378 .previous
379 .if \end - \start
380 savewords "(\start+1)",\end
381 .endif
382.endm
383 savewords 14,31
384 b save_user_nvgprs_cont
385
386
387save_user_nvgprs_fault:
388 li r3,11 /* SIGSEGV */
389 lwz r4,TI_TASK(r12)
390 bl force_sigsegv
391 340
392 rlwinm r12,r1,0,0,(31-THREAD_SHIFT) /* current_thread_info() */
393 lwz r9,TI_FLAGS(r12)
394 b save_user_nvgprs_cont
395
396#ifdef SHOW_SYSCALLS 341#ifdef SHOW_SYSCALLS
397do_show_syscall: 342do_show_syscall:
398#ifdef SHOW_SYSCALLS_TASK 343#ifdef SHOW_SYSCALLS_TASK
@@ -490,6 +435,14 @@ ppc_clone:
490 stw r0,_TRAP(r1) /* register set saved */ 435 stw r0,_TRAP(r1) /* register set saved */
491 b sys_clone 436 b sys_clone
492 437
438 .globl ppc_swapcontext
439ppc_swapcontext:
440 SAVE_NVGPRS(r1)
441 lwz r0,_TRAP(r1)
442 rlwinm r0,r0,0,0,30 /* clear LSB to indicate full */
443 stw r0,_TRAP(r1) /* register set saved */
444 b sys_swapcontext
445
493/* 446/*
494 * Top-level page fault handling. 447 * Top-level page fault handling.
495 * This is in assembler because if do_page_fault tells us that 448 * This is in assembler because if do_page_fault tells us that
@@ -683,7 +636,7 @@ user_exc_return: /* r10 contains MSR_KERNEL here */
683 /* Check current_thread_info()->flags */ 636 /* Check current_thread_info()->flags */
684 rlwinm r9,r1,0,0,(31-THREAD_SHIFT) 637 rlwinm r9,r1,0,0,(31-THREAD_SHIFT)
685 lwz r9,TI_FLAGS(r9) 638 lwz r9,TI_FLAGS(r9)
686 andi. r0,r9,(_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_RESTOREALL|_TIF_RESTORE_SIGMASK) 639 andi. r0,r9,(_TIF_SIGPENDING|_TIF_RESTORE_SIGMASK|_TIF_NEED_RESCHED)
687 bne do_work 640 bne do_work
688 641
689restore_user: 642restore_user:
diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S
index 388f861b8ed1..24be0cf86d7f 100644
--- a/arch/powerpc/kernel/entry_64.S
+++ b/arch/powerpc/kernel/entry_64.S
@@ -160,7 +160,7 @@ syscall_exit:
160 mtmsrd r10,1 160 mtmsrd r10,1
161 ld r9,TI_FLAGS(r12) 161 ld r9,TI_FLAGS(r12)
162 li r11,-_LAST_ERRNO 162 li r11,-_LAST_ERRNO
163 andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP|_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_RESTOREALL|_TIF_SAVE_NVGPRS|_TIF_NOERROR|_TIF_RESTORE_SIGMASK) 163 andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP|_TIF_USER_WORK_MASK|_TIF_PERSYSCALL_MASK)
164 bne- syscall_exit_work 164 bne- syscall_exit_work
165 cmpld r3,r11 165 cmpld r3,r11
166 ld r5,_CCR(r1) 166 ld r5,_CCR(r1)
@@ -216,8 +216,10 @@ syscall_exit_work:
216 If TIF_NOERROR is set, just save r3 as it is. */ 216 If TIF_NOERROR is set, just save r3 as it is. */
217 217
218 andi. r0,r9,_TIF_RESTOREALL 218 andi. r0,r9,_TIF_RESTOREALL
219 bne- 2f 219 beq+ 0f
220 cmpld r3,r11 /* r10 is -LAST_ERRNO */ 220 REST_NVGPRS(r1)
221 b 2f
2220: cmpld r3,r11 /* r10 is -LAST_ERRNO */
221 blt+ 1f 223 blt+ 1f
222 andi. r0,r9,_TIF_NOERROR 224 andi. r0,r9,_TIF_NOERROR
223 bne- 1f 225 bne- 1f
@@ -229,9 +231,7 @@ syscall_exit_work:
2292: andi. r0,r9,(_TIF_PERSYSCALL_MASK) 2312: andi. r0,r9,(_TIF_PERSYSCALL_MASK)
230 beq 4f 232 beq 4f
231 233
232 /* Clear per-syscall TIF flags if any are set, but _leave_ 234 /* Clear per-syscall TIF flags if any are set. */
233 _TIF_SAVE_NVGPRS set in r9 since we haven't dealt with that
234 yet. */
235 235
236 li r11,_TIF_PERSYSCALL_MASK 236 li r11,_TIF_PERSYSCALL_MASK
237 addi r12,r12,TI_FLAGS 237 addi r12,r12,TI_FLAGS
@@ -240,10 +240,9 @@ syscall_exit_work:
240 stdcx. r10,0,r12 240 stdcx. r10,0,r12
241 bne- 3b 241 bne- 3b
242 subi r12,r12,TI_FLAGS 242 subi r12,r12,TI_FLAGS
243 243
2444: bl .save_nvgprs 2444: /* Anything else left to do? */
245 /* Anything else left to do? */ 245 andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP)
246 andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP|_TIF_SAVE_NVGPRS)
247 beq .ret_from_except_lite 246 beq .ret_from_except_lite
248 247
249 /* Re-enable interrupts */ 248 /* Re-enable interrupts */
@@ -251,26 +250,10 @@ syscall_exit_work:
251 ori r10,r10,MSR_EE 250 ori r10,r10,MSR_EE
252 mtmsrd r10,1 251 mtmsrd r10,1
253 252
254 andi. r0,r9,_TIF_SAVE_NVGPRS 253 bl .save_nvgprs
255 bne save_user_nvgprs
256
257 /* If tracing, re-enable interrupts and do it */
258save_user_nvgprs_cont:
259 andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP)
260 beq 5f
261
262 addi r3,r1,STACK_FRAME_OVERHEAD 254 addi r3,r1,STACK_FRAME_OVERHEAD
263 bl .do_syscall_trace_leave 255 bl .do_syscall_trace_leave
264 REST_NVGPRS(r1) 256 b .ret_from_except
265 clrrdi r12,r1,THREAD_SHIFT
266
267 /* Disable interrupts again and handle other work if any */
2685: mfmsr r10
269 rldicl r10,r10,48,1
270 rotldi r10,r10,16
271 mtmsrd r10,1
272
273 b .ret_from_except_lite
274 257
275/* Save non-volatile GPRs, if not already saved. */ 258/* Save non-volatile GPRs, if not already saved. */
276_GLOBAL(save_nvgprs) 259_GLOBAL(save_nvgprs)
@@ -282,51 +265,6 @@ _GLOBAL(save_nvgprs)
282 std r0,_TRAP(r1) 265 std r0,_TRAP(r1)
283 blr 266 blr
284 267
285
286save_user_nvgprs:
287 ld r10,TI_SIGFRAME(r12)
288 andi. r0,r9,_TIF_32BIT
289 beq- save_user_nvgprs_64
290
291 /* 32-bit save to userspace */
292
293.macro savewords start, end
294 1: stw \start,4*(\start)(r10)
295 .section __ex_table,"a"
296 .align 3
297 .llong 1b,save_user_nvgprs_fault
298 .previous
299 .if \end - \start
300 savewords "(\start+1)",\end
301 .endif
302.endm
303 savewords 14,31
304 b save_user_nvgprs_cont
305
306save_user_nvgprs_64:
307 /* 64-bit save to userspace */
308
309.macro savelongs start, end
310 1: std \start,8*(\start)(r10)
311 .section __ex_table,"a"
312 .align 3
313 .llong 1b,save_user_nvgprs_fault
314 .previous
315 .if \end - \start
316 savelongs "(\start+1)",\end
317 .endif
318.endm
319 savelongs 14,31
320 b save_user_nvgprs_cont
321
322save_user_nvgprs_fault:
323 li r3,11 /* SIGSEGV */
324 ld r4,TI_TASK(r12)
325 bl .force_sigsegv
326
327 clrrdi r12,r1,THREAD_SHIFT
328 ld r9,TI_FLAGS(r12)
329 b save_user_nvgprs_cont
330 268
331/* 269/*
332 * The sigsuspend and rt_sigsuspend system calls can call do_signal 270 * The sigsuspend and rt_sigsuspend system calls can call do_signal
@@ -352,6 +290,16 @@ _GLOBAL(ppc_clone)
352 bl .sys_clone 290 bl .sys_clone
353 b syscall_exit 291 b syscall_exit
354 292
293_GLOBAL(ppc32_swapcontext)
294 bl .save_nvgprs
295 bl .compat_sys_swapcontext
296 b syscall_exit
297
298_GLOBAL(ppc64_swapcontext)
299 bl .save_nvgprs
300 bl .sys_swapcontext
301 b syscall_exit
302
355_GLOBAL(ret_from_fork) 303_GLOBAL(ret_from_fork)
356 bl .schedule_tail 304 bl .schedule_tail
357 REST_NVGPRS(r1) 305 REST_NVGPRS(r1)
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
index 400793c71304..bcb83574335b 100644
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -561,10 +561,7 @@ void do_syscall_trace_leave(struct pt_regs *regs)
561 regs->result); 561 regs->result);
562 562
563 if ((test_thread_flag(TIF_SYSCALL_TRACE) 563 if ((test_thread_flag(TIF_SYSCALL_TRACE)
564#ifdef CONFIG_PPC64 564 || test_thread_flag(TIF_SINGLESTEP))
565 || test_thread_flag(TIF_SINGLESTEP)
566#endif
567 )
568 && (current->ptrace & PT_PTRACED)) 565 && (current->ptrace & PT_PTRACED))
569 do_syscall_trace(); 566 do_syscall_trace();
570} 567}
diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c
index bd837b5dbf06..d7a4e814974d 100644
--- a/arch/powerpc/kernel/signal_32.c
+++ b/arch/powerpc/kernel/signal_32.c
@@ -151,10 +151,7 @@ static inline int save_general_regs(struct pt_regs *regs,
151 elf_greg_t64 *gregs = (elf_greg_t64 *)regs; 151 elf_greg_t64 *gregs = (elf_greg_t64 *)regs;
152 int i; 152 int i;
153 153
154 if (!FULL_REGS(regs)) { 154 WARN_ON(!FULL_REGS(regs));
155 set_thread_flag(TIF_SAVE_NVGPRS);
156 current_thread_info()->nvgprs_frame = frame->mc_gregs;
157 }
158 155
159 for (i = 0; i <= PT_RESULT; i ++) { 156 for (i = 0; i <= PT_RESULT; i ++) {
160 if (i == 14 && !FULL_REGS(regs)) 157 if (i == 14 && !FULL_REGS(regs))
@@ -215,15 +212,7 @@ static inline int get_old_sigaction(struct k_sigaction *new_ka,
215static inline int save_general_regs(struct pt_regs *regs, 212static inline int save_general_regs(struct pt_regs *regs,
216 struct mcontext __user *frame) 213 struct mcontext __user *frame)
217{ 214{
218 if (!FULL_REGS(regs)) { 215 WARN_ON(!FULL_REGS(regs));
219 /* Zero out the unsaved GPRs to avoid information
220 leak, and set TIF_SAVE_NVGPRS to ensure that the
221 registers do actually get saved later. */
222 memset(&regs->gpr[14], 0, 18 * sizeof(unsigned long));
223 current_thread_info()->nvgprs_frame = &frame->mc_gregs;
224 set_thread_flag(TIF_SAVE_NVGPRS);
225 }
226
227 return __copy_to_user(&frame->mc_gregs, regs, GP_REGS_SIZE); 216 return __copy_to_user(&frame->mc_gregs, regs, GP_REGS_SIZE);
228} 217}
229 218
@@ -826,8 +815,8 @@ static int do_setcontext(struct ucontext __user *ucp, struct pt_regs *regs, int
826} 815}
827 816
828long sys_swapcontext(struct ucontext __user *old_ctx, 817long sys_swapcontext(struct ucontext __user *old_ctx,
829 struct ucontext __user *new_ctx, 818 struct ucontext __user *new_ctx,
830 int ctx_size, int r6, int r7, int r8, struct pt_regs *regs) 819 int ctx_size, int r6, int r7, int r8, struct pt_regs *regs)
831{ 820{
832 unsigned char tmp; 821 unsigned char tmp;
833 822
diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c
index 497a5d3df359..4324f8a8ba24 100644
--- a/arch/powerpc/kernel/signal_64.c
+++ b/arch/powerpc/kernel/signal_64.c
@@ -118,14 +118,7 @@ static long setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs,
118 err |= __put_user(0, &sc->v_regs); 118 err |= __put_user(0, &sc->v_regs);
119#endif /* CONFIG_ALTIVEC */ 119#endif /* CONFIG_ALTIVEC */
120 err |= __put_user(&sc->gp_regs, &sc->regs); 120 err |= __put_user(&sc->gp_regs, &sc->regs);
121 if (!FULL_REGS(regs)) { 121 WARN_ON(!FULL_REGS(regs));
122 /* Zero out the unsaved GPRs to avoid information
123 leak, and set TIF_SAVE_NVGPRS to ensure that the
124 registers do actually get saved later. */
125 memset(&regs->gpr[14], 0, 18 * sizeof(unsigned long));
126 set_thread_flag(TIF_SAVE_NVGPRS);
127 current_thread_info()->nvgprs_frame = &sc->gp_regs;
128 }
129 err |= __copy_to_user(&sc->gp_regs, regs, GP_REGS_SIZE); 122 err |= __copy_to_user(&sc->gp_regs, regs, GP_REGS_SIZE);
130 err |= __copy_to_user(&sc->fp_regs, &current->thread.fpr, FP_REGS_SIZE); 123 err |= __copy_to_user(&sc->fp_regs, &current->thread.fpr, FP_REGS_SIZE);
131 err |= __put_user(signr, &sc->signal); 124 err |= __put_user(signr, &sc->signal);
diff --git a/arch/powerpc/kernel/systbl.S b/arch/powerpc/kernel/systbl.S
index 8a9f994ed917..1ad55f0466fd 100644
--- a/arch/powerpc/kernel/systbl.S
+++ b/arch/powerpc/kernel/systbl.S
@@ -288,7 +288,7 @@ COMPAT_SYS(clock_settime)
288COMPAT_SYS(clock_gettime) 288COMPAT_SYS(clock_gettime)
289COMPAT_SYS(clock_getres) 289COMPAT_SYS(clock_getres)
290COMPAT_SYS(clock_nanosleep) 290COMPAT_SYS(clock_nanosleep)
291COMPAT_SYS(swapcontext) 291SYSX(ppc64_swapcontext,ppc32_swapcontext,ppc_swapcontext)
292COMPAT_SYS(tgkill) 292COMPAT_SYS(tgkill)
293COMPAT_SYS(utimes) 293COMPAT_SYS(utimes)
294COMPAT_SYS(statfs64) 294COMPAT_SYS(statfs64)
diff --git a/arch/ppc/kernel/asm-offsets.c b/arch/ppc/kernel/asm-offsets.c
index 7964bf660e92..77e4dc780f8c 100644
--- a/arch/ppc/kernel/asm-offsets.c
+++ b/arch/ppc/kernel/asm-offsets.c
@@ -131,7 +131,6 @@ main(void)
131 DEFINE(CPU_SPEC_FEATURES, offsetof(struct cpu_spec, cpu_features)); 131 DEFINE(CPU_SPEC_FEATURES, offsetof(struct cpu_spec, cpu_features));
132 DEFINE(CPU_SPEC_SETUP, offsetof(struct cpu_spec, cpu_setup)); 132 DEFINE(CPU_SPEC_SETUP, offsetof(struct cpu_spec, cpu_setup));
133 133
134 DEFINE(TI_SIGFRAME, offsetof(struct thread_info, nvgprs_frame));
135 DEFINE(TI_TASK, offsetof(struct thread_info, task)); 134 DEFINE(TI_TASK, offsetof(struct thread_info, task));
136 DEFINE(TI_EXECDOMAIN, offsetof(struct thread_info, exec_domain)); 135 DEFINE(TI_EXECDOMAIN, offsetof(struct thread_info, exec_domain));
137 DEFINE(TI_FLAGS, offsetof(struct thread_info, flags)); 136 DEFINE(TI_FLAGS, offsetof(struct thread_info, flags));
diff --git a/arch/ppc/kernel/entry.S b/arch/ppc/kernel/entry.S
index a48b950722a1..3a2815978488 100644
--- a/arch/ppc/kernel/entry.S
+++ b/arch/ppc/kernel/entry.S
@@ -227,7 +227,7 @@ ret_from_syscall:
227 MTMSRD(r10) 227 MTMSRD(r10)
228 lwz r9,TI_FLAGS(r12) 228 lwz r9,TI_FLAGS(r12)
229 li r8,-_LAST_ERRNO 229 li r8,-_LAST_ERRNO
230 andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_RESTOREALL) 230 andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP|_TIF_USER_WORK_MASK|_TIF_PERSYSCALL_MASK)
231 bne- syscall_exit_work 231 bne- syscall_exit_work
232 cmplw 0,r3,r8 232 cmplw 0,r3,r8
233 blt+ syscall_exit_cont 233 blt+ syscall_exit_cont
@@ -287,8 +287,10 @@ syscall_dotrace:
287 287
288syscall_exit_work: 288syscall_exit_work:
289 andi. r0,r9,_TIF_RESTOREALL 289 andi. r0,r9,_TIF_RESTOREALL
290 bne- 2f 290 beq+ 0f
291 cmplw 0,r3,r8 291 REST_NVGPRS(r1)
292 b 2f
2930: cmplw 0,r3,r8
292 blt+ 1f 294 blt+ 1f
293 andi. r0,r9,_TIF_NOERROR 295 andi. r0,r9,_TIF_NOERROR
294 bne- 1f 296 bne- 1f
@@ -302,9 +304,7 @@ syscall_exit_work:
3022: andi. r0,r9,(_TIF_PERSYSCALL_MASK) 3042: andi. r0,r9,(_TIF_PERSYSCALL_MASK)
303 beq 4f 305 beq 4f
304 306
305 /* Clear per-syscall TIF flags if any are set, but _leave_ 307 /* Clear per-syscall TIF flags if any are set. */
306 _TIF_SAVE_NVGPRS set in r9 since we haven't dealt with that
307 yet. */
308 308
309 li r11,_TIF_PERSYSCALL_MASK 309 li r11,_TIF_PERSYSCALL_MASK
310 addi r12,r12,TI_FLAGS 310 addi r12,r12,TI_FLAGS
@@ -318,8 +318,13 @@ syscall_exit_work:
318 subi r12,r12,TI_FLAGS 318 subi r12,r12,TI_FLAGS
319 319
3204: /* Anything which requires enabling interrupts? */ 3204: /* Anything which requires enabling interrupts? */
321 andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP|_TIF_SAVE_NVGPRS) 321 andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP)
322 beq 7f 322 beq ret_from_except
323
324 /* Re-enable interrupts */
325 ori r10,r10,MSR_EE
326 SYNC
327 MTMSRD(r10)
323 328
324 /* Save NVGPRS if they're not saved already */ 329 /* Save NVGPRS if they're not saved already */
325 lwz r4,TRAP(r1) 330 lwz r4,TRAP(r1)
@@ -328,71 +333,11 @@ syscall_exit_work:
328 SAVE_NVGPRS(r1) 333 SAVE_NVGPRS(r1)
329 li r4,0xc00 334 li r4,0xc00
330 stw r4,TRAP(r1) 335 stw r4,TRAP(r1)
331 3365:
332 /* Re-enable interrupts */
3335: ori r10,r10,MSR_EE
334 SYNC
335 MTMSRD(r10)
336
337 andi. r0,r9,_TIF_SAVE_NVGPRS
338 bne save_user_nvgprs
339
340save_user_nvgprs_cont:
341 andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP)
342 beq 7f
343
344 addi r3,r1,STACK_FRAME_OVERHEAD 337 addi r3,r1,STACK_FRAME_OVERHEAD
345 bl do_syscall_trace_leave 338 bl do_syscall_trace_leave
346 REST_NVGPRS(r1) 339 b ret_from_except_full
347
3486: lwz r3,GPR3(r1)
349 LOAD_MSR_KERNEL(r10,MSR_KERNEL) /* doesn't include MSR_EE */
350 SYNC
351 MTMSRD(r10) /* disable interrupts again */
352 rlwinm r12,r1,0,0,18 /* current_thread_info() */
353 lwz r9,TI_FLAGS(r12)
3547:
355 andi. r0,r9,_TIF_NEED_RESCHED
356 bne 8f
357 lwz r5,_MSR(r1)
358 andi. r5,r5,MSR_PR
359 beq ret_from_except
360 andi. r0,r9,_TIF_SIGPENDING
361 beq ret_from_except
362 b do_user_signal
3638:
364 ori r10,r10,MSR_EE
365 SYNC
366 MTMSRD(r10) /* re-enable interrupts */
367 bl schedule
368 b 6b
369
370save_user_nvgprs:
371 lwz r8,TI_SIGFRAME(r12)
372
373.macro savewords start, end
374 1: stw \start,4*(\start)(r8)
375 .section __ex_table,"a"
376 .align 2
377 .long 1b,save_user_nvgprs_fault
378 .previous
379 .if \end - \start
380 savewords "(\start+1)",\end
381 .endif
382.endm
383 savewords 14,31
384 b save_user_nvgprs_cont
385
386
387save_user_nvgprs_fault:
388 li r3,11 /* SIGSEGV */
389 lwz r4,TI_TASK(r12)
390 bl force_sigsegv
391 340
392 rlwinm r12,r1,0,0,18 /* current_thread_info() */
393 lwz r9,TI_FLAGS(r12)
394 b save_user_nvgprs_cont
395
396#ifdef SHOW_SYSCALLS 341#ifdef SHOW_SYSCALLS
397do_show_syscall: 342do_show_syscall:
398#ifdef SHOW_SYSCALLS_TASK 343#ifdef SHOW_SYSCALLS_TASK
@@ -490,6 +435,14 @@ ppc_clone:
490 stw r0,TRAP(r1) /* register set saved */ 435 stw r0,TRAP(r1) /* register set saved */
491 b sys_clone 436 b sys_clone
492 437
438 .globl ppc_swapcontext
439ppc_swapcontext:
440 SAVE_NVGPRS(r1)
441 lwz r0,TRAP(r1)
442 rlwinm r0,r0,0,0,30 /* clear LSB to indicate full */
443 stw r0,TRAP(r1) /* register set saved */
444 b sys_swapcontext
445
493/* 446/*
494 * Top-level page fault handling. 447 * Top-level page fault handling.
495 * This is in assembler because if do_page_fault tells us that 448 * This is in assembler because if do_page_fault tells us that
@@ -683,7 +636,7 @@ user_exc_return: /* r10 contains MSR_KERNEL here */
683 /* Check current_thread_info()->flags */ 636 /* Check current_thread_info()->flags */
684 rlwinm r9,r1,0,0,18 637 rlwinm r9,r1,0,0,18
685 lwz r9,TI_FLAGS(r9) 638 lwz r9,TI_FLAGS(r9)
686 andi. r0,r9,(_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_RESTOREALL) 639 andi. r0,r9,(_TIF_SIGPENDING|_TIF_RESTORE_SIGMASK|_TIF_NEED_RESCHED)
687 bne do_work 640 bne do_work
688 641
689restore_user: 642restore_user: