diff options
Diffstat (limited to 'arch/powerpc/kernel/signal_32.c')
-rw-r--r-- | arch/powerpc/kernel/signal_32.c | 50 |
1 files changed, 38 insertions, 12 deletions
diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c index e6474a45cef5..2d47cc79e5b3 100644 --- a/arch/powerpc/kernel/signal_32.c +++ b/arch/powerpc/kernel/signal_32.c | |||
@@ -470,9 +470,9 @@ static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame, | |||
470 | return 1; | 470 | return 1; |
471 | 471 | ||
472 | if (sigret) { | 472 | if (sigret) { |
473 | /* Set up the sigreturn trampoline: li r0,sigret; sc */ | 473 | /* Set up the sigreturn trampoline: li 0,sigret; sc */ |
474 | if (__put_user(0x38000000UL + sigret, &frame->tramp[0]) | 474 | if (__put_user(PPC_INST_ADDI + sigret, &frame->tramp[0]) |
475 | || __put_user(0x44000002UL, &frame->tramp[1])) | 475 | || __put_user(PPC_INST_SC, &frame->tramp[1])) |
476 | return 1; | 476 | return 1; |
477 | flush_icache_range((unsigned long) &frame->tramp[0], | 477 | flush_icache_range((unsigned long) &frame->tramp[0], |
478 | (unsigned long) &frame->tramp[2]); | 478 | (unsigned long) &frame->tramp[2]); |
@@ -619,9 +619,9 @@ static int save_tm_user_regs(struct pt_regs *regs, | |||
619 | if (__put_user(msr, &frame->mc_gregs[PT_MSR])) | 619 | if (__put_user(msr, &frame->mc_gregs[PT_MSR])) |
620 | return 1; | 620 | return 1; |
621 | if (sigret) { | 621 | if (sigret) { |
622 | /* Set up the sigreturn trampoline: li r0,sigret; sc */ | 622 | /* Set up the sigreturn trampoline: li 0,sigret; sc */ |
623 | if (__put_user(0x38000000UL + sigret, &frame->tramp[0]) | 623 | if (__put_user(PPC_INST_ADDI + sigret, &frame->tramp[0]) |
624 | || __put_user(0x44000002UL, &frame->tramp[1])) | 624 | || __put_user(PPC_INST_SC, &frame->tramp[1])) |
625 | return 1; | 625 | return 1; |
626 | flush_icache_range((unsigned long) &frame->tramp[0], | 626 | flush_icache_range((unsigned long) &frame->tramp[0], |
627 | (unsigned long) &frame->tramp[2]); | 627 | (unsigned long) &frame->tramp[2]); |
@@ -848,7 +848,23 @@ static long restore_tm_user_regs(struct pt_regs *regs, | |||
848 | /* If TM bits are set to the reserved value, it's an invalid context */ | 848 | /* If TM bits are set to the reserved value, it's an invalid context */ |
849 | if (MSR_TM_RESV(msr_hi)) | 849 | if (MSR_TM_RESV(msr_hi)) |
850 | return 1; | 850 | return 1; |
851 | /* Pull in the MSR TM bits from the user context */ | 851 | |
852 | /* | ||
853 | * Disabling preemption, since it is unsafe to be preempted | ||
854 | * with MSR[TS] set without recheckpointing. | ||
855 | */ | ||
856 | preempt_disable(); | ||
857 | |||
858 | /* | ||
859 | * CAUTION: | ||
860 | * After regs->MSR[TS] being updated, make sure that get_user(), | ||
861 | * put_user() or similar functions are *not* called. These | ||
862 | * functions can generate page faults which will cause the process | ||
863 | * to be de-scheduled with MSR[TS] set but without calling | ||
864 | * tm_recheckpoint(). This can cause a bug. | ||
865 | * | ||
866 | * Pull in the MSR TM bits from the user context | ||
867 | */ | ||
852 | regs->msr = (regs->msr & ~MSR_TS_MASK) | (msr_hi & MSR_TS_MASK); | 868 | regs->msr = (regs->msr & ~MSR_TS_MASK) | (msr_hi & MSR_TS_MASK); |
853 | /* Now, recheckpoint. This loads up all of the checkpointed (older) | 869 | /* Now, recheckpoint. This loads up all of the checkpointed (older) |
854 | * registers, including FP and V[S]Rs. After recheckpointing, the | 870 | * registers, including FP and V[S]Rs. After recheckpointing, the |
@@ -873,6 +889,8 @@ static long restore_tm_user_regs(struct pt_regs *regs, | |||
873 | } | 889 | } |
874 | #endif | 890 | #endif |
875 | 891 | ||
892 | preempt_enable(); | ||
893 | |||
876 | return 0; | 894 | return 0; |
877 | } | 895 | } |
878 | #endif | 896 | #endif |
@@ -1140,11 +1158,11 @@ SYSCALL_DEFINE0(rt_sigreturn) | |||
1140 | { | 1158 | { |
1141 | struct rt_sigframe __user *rt_sf; | 1159 | struct rt_sigframe __user *rt_sf; |
1142 | struct pt_regs *regs = current_pt_regs(); | 1160 | struct pt_regs *regs = current_pt_regs(); |
1161 | int tm_restore = 0; | ||
1143 | #ifdef CONFIG_PPC_TRANSACTIONAL_MEM | 1162 | #ifdef CONFIG_PPC_TRANSACTIONAL_MEM |
1144 | struct ucontext __user *uc_transact; | 1163 | struct ucontext __user *uc_transact; |
1145 | unsigned long msr_hi; | 1164 | unsigned long msr_hi; |
1146 | unsigned long tmp; | 1165 | unsigned long tmp; |
1147 | int tm_restore = 0; | ||
1148 | #endif | 1166 | #endif |
1149 | /* Always make any pending restarted system calls return -EINTR */ | 1167 | /* Always make any pending restarted system calls return -EINTR */ |
1150 | current->restart_block.fn = do_no_restart_syscall; | 1168 | current->restart_block.fn = do_no_restart_syscall; |
@@ -1192,11 +1210,19 @@ SYSCALL_DEFINE0(rt_sigreturn) | |||
1192 | goto bad; | 1210 | goto bad; |
1193 | } | 1211 | } |
1194 | } | 1212 | } |
1195 | if (!tm_restore) | 1213 | if (!tm_restore) { |
1196 | /* Fall through, for non-TM restore */ | 1214 | /* |
1215 | * Unset regs->msr because ucontext MSR TS is not | ||
1216 | * set, and recheckpoint was not called. This avoid | ||
1217 | * hitting a TM Bad thing at RFID | ||
1218 | */ | ||
1219 | regs->msr &= ~MSR_TS_MASK; | ||
1220 | } | ||
1221 | /* Fall through, for non-TM restore */ | ||
1197 | #endif | 1222 | #endif |
1198 | if (do_setcontext(&rt_sf->uc, regs, 1)) | 1223 | if (!tm_restore) |
1199 | goto bad; | 1224 | if (do_setcontext(&rt_sf->uc, regs, 1)) |
1225 | goto bad; | ||
1200 | 1226 | ||
1201 | /* | 1227 | /* |
1202 | * It's not clear whether or why it is desirable to save the | 1228 | * It's not clear whether or why it is desirable to save the |