diff options
-rw-r--r-- | Documentation/powerpc/transactional_memory.txt | 19 | ||||
-rw-r--r-- | arch/powerpc/include/asm/processor.h | 13 | ||||
-rw-r--r-- | arch/powerpc/include/asm/signal.h | 3 | ||||
-rw-r--r-- | arch/powerpc/kernel/signal.c | 40 | ||||
-rw-r--r-- | arch/powerpc/kernel/signal.h | 2 | ||||
-rw-r--r-- | arch/powerpc/kernel/signal_32.c | 10 | ||||
-rw-r--r-- | arch/powerpc/kernel/signal_64.c | 23 |
7 files changed, 74 insertions, 36 deletions
diff --git a/Documentation/powerpc/transactional_memory.txt b/Documentation/powerpc/transactional_memory.txt index c54bf3127651..dc23e58ae264 100644 --- a/Documentation/powerpc/transactional_memory.txt +++ b/Documentation/powerpc/transactional_memory.txt | |||
@@ -147,6 +147,25 @@ Example signal handler: | |||
147 | fix_the_problem(ucp->dar); | 147 | fix_the_problem(ucp->dar); |
148 | } | 148 | } |
149 | 149 | ||
150 | When in an active transaction that takes a signal, we need to be careful with | ||
151 | the stack. It's possible that the stack has moved back up after the tbegin. | ||
152 | The obvious case here is when the tbegin is called inside a function that | ||
153 | returns before a tend. In this case, the stack is part of the checkpointed | ||
154 | transactional memory state. If we write over this non transactionally or in | ||
155 | suspend, we are in trouble because if we get a tm abort, the program counter and | ||
156 | stack pointer will be back at the tbegin but our in memory stack won't be valid | ||
157 | anymore. | ||
158 | |||
159 | To avoid this, when taking a signal in an active transaction, we need to use | ||
160 | the stack pointer from the checkpointed state, rather than the speculated | ||
161 | state. This ensures that the signal context (written tm suspended) will be | ||
162 | written below the stack required for the rollback. The transaction is aborted | ||
163 | becuase of the treclaim, so any memory written between the tbegin and the | ||
164 | signal will be rolled back anyway. | ||
165 | |||
166 | For signals taken in non-TM or suspended mode, we use the | ||
167 | normal/non-checkpointed stack pointer. | ||
168 | |||
150 | 169 | ||
151 | Failure cause codes used by kernel | 170 | Failure cause codes used by kernel |
152 | ================================== | 171 | ================================== |
diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h index 594db6bc093c..14a658363698 100644 --- a/arch/powerpc/include/asm/processor.h +++ b/arch/powerpc/include/asm/processor.h | |||
@@ -409,21 +409,16 @@ static inline void prefetchw(const void *x) | |||
409 | #endif | 409 | #endif |
410 | 410 | ||
411 | #ifdef CONFIG_PPC64 | 411 | #ifdef CONFIG_PPC64 |
412 | static inline unsigned long get_clean_sp(struct pt_regs *regs, int is_32) | 412 | static inline unsigned long get_clean_sp(unsigned long sp, int is_32) |
413 | { | 413 | { |
414 | unsigned long sp; | ||
415 | |||
416 | if (is_32) | 414 | if (is_32) |
417 | sp = regs->gpr[1] & 0x0ffffffffUL; | 415 | return sp & 0x0ffffffffUL; |
418 | else | ||
419 | sp = regs->gpr[1]; | ||
420 | |||
421 | return sp; | 416 | return sp; |
422 | } | 417 | } |
423 | #else | 418 | #else |
424 | static inline unsigned long get_clean_sp(struct pt_regs *regs, int is_32) | 419 | static inline unsigned long get_clean_sp(unsigned long sp, int is_32) |
425 | { | 420 | { |
426 | return regs->gpr[1]; | 421 | return sp; |
427 | } | 422 | } |
428 | #endif | 423 | #endif |
429 | 424 | ||
diff --git a/arch/powerpc/include/asm/signal.h b/arch/powerpc/include/asm/signal.h index fbe66c463891..9322c28aebd2 100644 --- a/arch/powerpc/include/asm/signal.h +++ b/arch/powerpc/include/asm/signal.h | |||
@@ -3,5 +3,8 @@ | |||
3 | 3 | ||
4 | #define __ARCH_HAS_SA_RESTORER | 4 | #define __ARCH_HAS_SA_RESTORER |
5 | #include <uapi/asm/signal.h> | 5 | #include <uapi/asm/signal.h> |
6 | #include <uapi/asm/ptrace.h> | ||
7 | |||
8 | extern unsigned long get_tm_stackpointer(struct pt_regs *regs); | ||
6 | 9 | ||
7 | #endif /* _ASM_POWERPC_SIGNAL_H */ | 10 | #endif /* _ASM_POWERPC_SIGNAL_H */ |
diff --git a/arch/powerpc/kernel/signal.c b/arch/powerpc/kernel/signal.c index 577a8aa69c6e..457e97aa2945 100644 --- a/arch/powerpc/kernel/signal.c +++ b/arch/powerpc/kernel/signal.c | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <asm/uaccess.h> | 18 | #include <asm/uaccess.h> |
19 | #include <asm/unistd.h> | 19 | #include <asm/unistd.h> |
20 | #include <asm/debug.h> | 20 | #include <asm/debug.h> |
21 | #include <asm/tm.h> | ||
21 | 22 | ||
22 | #include "signal.h" | 23 | #include "signal.h" |
23 | 24 | ||
@@ -30,13 +31,13 @@ int show_unhandled_signals = 1; | |||
30 | /* | 31 | /* |
31 | * Allocate space for the signal frame | 32 | * Allocate space for the signal frame |
32 | */ | 33 | */ |
33 | void __user * get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, | 34 | void __user * get_sigframe(struct k_sigaction *ka, unsigned long sp, |
34 | size_t frame_size, int is_32) | 35 | size_t frame_size, int is_32) |
35 | { | 36 | { |
36 | unsigned long oldsp, newsp; | 37 | unsigned long oldsp, newsp; |
37 | 38 | ||
38 | /* Default to using normal stack */ | 39 | /* Default to using normal stack */ |
39 | oldsp = get_clean_sp(regs, is_32); | 40 | oldsp = get_clean_sp(sp, is_32); |
40 | 41 | ||
41 | /* Check for alt stack */ | 42 | /* Check for alt stack */ |
42 | if ((ka->sa.sa_flags & SA_ONSTACK) && | 43 | if ((ka->sa.sa_flags & SA_ONSTACK) && |
@@ -175,3 +176,38 @@ void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags) | |||
175 | 176 | ||
176 | user_enter(); | 177 | user_enter(); |
177 | } | 178 | } |
179 | |||
180 | unsigned long get_tm_stackpointer(struct pt_regs *regs) | ||
181 | { | ||
182 | /* When in an active transaction that takes a signal, we need to be | ||
183 | * careful with the stack. It's possible that the stack has moved back | ||
184 | * up after the tbegin. The obvious case here is when the tbegin is | ||
185 | * called inside a function that returns before a tend. In this case, | ||
186 | * the stack is part of the checkpointed transactional memory state. | ||
187 | * If we write over this non transactionally or in suspend, we are in | ||
188 | * trouble because if we get a tm abort, the program counter and stack | ||
189 | * pointer will be back at the tbegin but our in memory stack won't be | ||
190 | * valid anymore. | ||
191 | * | ||
192 | * To avoid this, when taking a signal in an active transaction, we | ||
193 | * need to use the stack pointer from the checkpointed state, rather | ||
194 | * than the speculated state. This ensures that the signal context | ||
195 | * (written tm suspended) will be written below the stack required for | ||
196 | * the rollback. The transaction is aborted becuase of the treclaim, | ||
197 | * so any memory written between the tbegin and the signal will be | ||
198 | * rolled back anyway. | ||
199 | * | ||
200 | * For signals taken in non-TM or suspended mode, we use the | ||
201 | * normal/non-checkpointed stack pointer. | ||
202 | */ | ||
203 | |||
204 | #ifdef CONFIG_PPC_TRANSACTIONAL_MEM | ||
205 | if (MSR_TM_ACTIVE(regs->msr)) { | ||
206 | tm_enable(); | ||
207 | tm_reclaim(¤t->thread, regs->msr, TM_CAUSE_SIGNAL); | ||
208 | if (MSR_TM_TRANSACTIONAL(regs->msr)) | ||
209 | return current->thread.ckpt_regs.gpr[1]; | ||
210 | } | ||
211 | #endif | ||
212 | return regs->gpr[1]; | ||
213 | } | ||
diff --git a/arch/powerpc/kernel/signal.h b/arch/powerpc/kernel/signal.h index ec84c901ceab..c69b9aeb9f23 100644 --- a/arch/powerpc/kernel/signal.h +++ b/arch/powerpc/kernel/signal.h | |||
@@ -12,7 +12,7 @@ | |||
12 | 12 | ||
13 | extern void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags); | 13 | extern void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags); |
14 | 14 | ||
15 | extern void __user * get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, | 15 | extern void __user * get_sigframe(struct k_sigaction *ka, unsigned long sp, |
16 | size_t frame_size, int is_32); | 16 | size_t frame_size, int is_32); |
17 | 17 | ||
18 | extern int handle_signal32(unsigned long sig, struct k_sigaction *ka, | 18 | extern int handle_signal32(unsigned long sig, struct k_sigaction *ka, |
diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c index 95068bf569ad..201385c3a1ae 100644 --- a/arch/powerpc/kernel/signal_32.c +++ b/arch/powerpc/kernel/signal_32.c | |||
@@ -503,12 +503,6 @@ static int save_tm_user_regs(struct pt_regs *regs, | |||
503 | { | 503 | { |
504 | unsigned long msr = regs->msr; | 504 | unsigned long msr = regs->msr; |
505 | 505 | ||
506 | /* tm_reclaim rolls back all reg states, updating thread.ckpt_regs, | ||
507 | * thread.transact_fpr[], thread.transact_vr[], etc. | ||
508 | */ | ||
509 | tm_enable(); | ||
510 | tm_reclaim(¤t->thread, msr, TM_CAUSE_SIGNAL); | ||
511 | |||
512 | /* Make sure floating point registers are stored in regs */ | 506 | /* Make sure floating point registers are stored in regs */ |
513 | flush_fp_to_thread(current); | 507 | flush_fp_to_thread(current); |
514 | 508 | ||
@@ -965,7 +959,7 @@ int handle_rt_signal32(unsigned long sig, struct k_sigaction *ka, | |||
965 | 959 | ||
966 | /* Set up Signal Frame */ | 960 | /* Set up Signal Frame */ |
967 | /* Put a Real Time Context onto stack */ | 961 | /* Put a Real Time Context onto stack */ |
968 | rt_sf = get_sigframe(ka, regs, sizeof(*rt_sf), 1); | 962 | rt_sf = get_sigframe(ka, get_tm_stackpointer(regs), sizeof(*rt_sf), 1); |
969 | addr = rt_sf; | 963 | addr = rt_sf; |
970 | if (unlikely(rt_sf == NULL)) | 964 | if (unlikely(rt_sf == NULL)) |
971 | goto badframe; | 965 | goto badframe; |
@@ -1403,7 +1397,7 @@ int handle_signal32(unsigned long sig, struct k_sigaction *ka, | |||
1403 | unsigned long tramp; | 1397 | unsigned long tramp; |
1404 | 1398 | ||
1405 | /* Set up Signal Frame */ | 1399 | /* Set up Signal Frame */ |
1406 | frame = get_sigframe(ka, regs, sizeof(*frame), 1); | 1400 | frame = get_sigframe(ka, get_tm_stackpointer(regs), sizeof(*frame), 1); |
1407 | if (unlikely(frame == NULL)) | 1401 | if (unlikely(frame == NULL)) |
1408 | goto badframe; | 1402 | goto badframe; |
1409 | sc = (struct sigcontext __user *) &frame->sctx; | 1403 | sc = (struct sigcontext __user *) &frame->sctx; |
diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c index c1794286098c..345947367ec0 100644 --- a/arch/powerpc/kernel/signal_64.c +++ b/arch/powerpc/kernel/signal_64.c | |||
@@ -154,11 +154,12 @@ static long setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, | |||
154 | * As above, but Transactional Memory is in use, so deliver sigcontexts | 154 | * As above, but Transactional Memory is in use, so deliver sigcontexts |
155 | * containing checkpointed and transactional register states. | 155 | * containing checkpointed and transactional register states. |
156 | * | 156 | * |
157 | * To do this, we treclaim to gather both sets of registers and set up the | 157 | * To do this, we treclaim (done before entering here) to gather both sets of |
158 | * 'normal' sigcontext registers with rolled-back register values such that a | 158 | * registers and set up the 'normal' sigcontext registers with rolled-back |
159 | * simple signal handler sees a correct checkpointed register state. | 159 | * register values such that a simple signal handler sees a correct |
160 | * If interested, a TM-aware sighandler can examine the transactional registers | 160 | * checkpointed register state. If interested, a TM-aware sighandler can |
161 | * in the 2nd sigcontext to determine the real origin of the signal. | 161 | * examine the transactional registers in the 2nd sigcontext to determine the |
162 | * real origin of the signal. | ||
162 | */ | 163 | */ |
163 | static long setup_tm_sigcontexts(struct sigcontext __user *sc, | 164 | static long setup_tm_sigcontexts(struct sigcontext __user *sc, |
164 | struct sigcontext __user *tm_sc, | 165 | struct sigcontext __user *tm_sc, |
@@ -184,16 +185,6 @@ static long setup_tm_sigcontexts(struct sigcontext __user *sc, | |||
184 | 185 | ||
185 | BUG_ON(!MSR_TM_ACTIVE(regs->msr)); | 186 | BUG_ON(!MSR_TM_ACTIVE(regs->msr)); |
186 | 187 | ||
187 | /* tm_reclaim rolls back all reg states, saving checkpointed (older) | ||
188 | * GPRs to thread.ckpt_regs and (if used) FPRs to (newer) | ||
189 | * thread.transact_fp and/or VRs to (newer) thread.transact_vr. | ||
190 | * THEN we save out FP/VRs, if necessary, to the checkpointed (older) | ||
191 | * thread.fr[]/vr[]s. The transactional (newer) GPRs are on the | ||
192 | * stack, in *regs. | ||
193 | */ | ||
194 | tm_enable(); | ||
195 | tm_reclaim(¤t->thread, msr, TM_CAUSE_SIGNAL); | ||
196 | |||
197 | flush_fp_to_thread(current); | 188 | flush_fp_to_thread(current); |
198 | 189 | ||
199 | #ifdef CONFIG_ALTIVEC | 190 | #ifdef CONFIG_ALTIVEC |
@@ -711,7 +702,7 @@ int handle_rt_signal64(int signr, struct k_sigaction *ka, siginfo_t *info, | |||
711 | unsigned long newsp = 0; | 702 | unsigned long newsp = 0; |
712 | long err = 0; | 703 | long err = 0; |
713 | 704 | ||
714 | frame = get_sigframe(ka, regs, sizeof(*frame), 0); | 705 | frame = get_sigframe(ka, get_tm_stackpointer(regs), sizeof(*frame), 0); |
715 | if (unlikely(frame == NULL)) | 706 | if (unlikely(frame == NULL)) |
716 | goto badframe; | 707 | goto badframe; |
717 | 708 | ||