aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Mackerras <paulus@samba.org>2014-01-12 23:56:29 -0500
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2014-01-14 21:59:11 -0500
commitd31626f70b6103f4d9153b75d07e0e8795728cc9 (patch)
tree1c6b3b954f7d1746e6ecaf06a5a31524d5254f68
parentae39c58c2e56209ae3503286ffe2338d5e9a4bed (diff)
powerpc: Don't corrupt transactional state when using FP/VMX in kernel
Currently, when we have a process using the transactional memory facilities on POWER8 (that is, the processor is in transactional or suspended state), and the process enters the kernel and the kernel then uses the floating-point or vector (VMX/Altivec) facility, we end up corrupting the user-visible FP/VMX/VSX state. This happens, for example, if a page fault causes a copy-on-write operation, because the copy_page function will use VMX to do the copy on POWER8. The test program below demonstrates the bug. The bug happens because when FP/VMX state for a transactional process is stored in the thread_struct, we store the checkpointed state in .fp_state/.vr_state and the transactional (current) state in .transact_fp/.transact_vr. However, when the kernel wants to use FP/VMX, it calls enable_kernel_fp() or enable_kernel_altivec(), which saves the current state in .fp_state/.vr_state. Furthermore, when we return to the user process we return with FP/VMX/VSX disabled. The next time the process uses FP/VMX/VSX, we don't know which set of state (the current register values, .fp_state/.vr_state, or .transact_fp/.transact_vr) we should be using, since we have no way to tell if we are still in the same transaction, and if not, whether the previous transaction succeeded or failed. Thus it is necessary to strictly adhere to the rule that if FP has been enabled at any point in a transaction, we must keep FP enabled for the user process with the current transactional state in the FP registers, until we detect that it is no longer in a transaction. Similarly for VMX; once enabled it must stay enabled until the process is no longer transactional. In order to keep this rule, we add a new thread_info flag which we test when returning from the kernel to userspace, called TIF_RESTORE_TM. This flag indicates that there is FP/VMX/VSX state to be restored before entering userspace, and when it is set the .tm_orig_msr field in the thread_struct indicates what state needs to be restored. The restoration is done by restore_tm_state(). The TIF_RESTORE_TM bit is set by new giveup_fpu/altivec_maybe_transactional helpers, which are called from enable_kernel_fp/altivec, giveup_vsx, and flush_fp/altivec_to_thread instead of giveup_fpu/altivec. The other thing to be done is to get the transactional FP/VMX/VSX state from .fp_state/.vr_state when doing reclaim, if that state has been saved there by giveup_fpu/altivec_maybe_transactional. Having done this, we set the FP/VMX bit in the thread's MSR after reclaim to indicate that that part of the state is now valid (having been reclaimed from the processor's checkpointed state). Finally, in the signal handling code, we move the clearing of the transactional state bits in the thread's MSR a bit earlier, before calling flush_fp_to_thread(), so that we don't unnecessarily set the TIF_RESTORE_TM bit. This is the test program: /* Michael Neuling 4/12/2013 * * See if the altivec state is leaked out of an aborted transaction due to * kernel vmx copy loops. * * gcc -m64 htm_vmxcopy.c -o htm_vmxcopy * */ /* We don't use all of these, but for reference: */ int main(int argc, char *argv[]) { long double vecin = 1.3; long double vecout; unsigned long pgsize = getpagesize(); int i; int fd; int size = pgsize*16; char tmpfile[] = "/tmp/page_faultXXXXXX"; char buf[pgsize]; char *a; uint64_t aborted = 0; fd = mkstemp(tmpfile); assert(fd >= 0); memset(buf, 0, pgsize); for (i = 0; i < size; i += pgsize) assert(write(fd, buf, pgsize) == pgsize); unlink(tmpfile); a = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); assert(a != MAP_FAILED); asm __volatile__( "lxvd2x 40,0,%[vecinptr] ; " // set 40 to initial value TBEGIN "beq 3f ;" TSUSPEND "xxlxor 40,40,40 ; " // set 40 to 0 "std 5, 0(%[map]) ;" // cause kernel vmx copy page TABORT TRESUME TEND "li %[res], 0 ;" "b 5f ;" "3: ;" // Abort handler "li %[res], 1 ;" "5: ;" "stxvd2x 40,0,%[vecoutptr] ; " : [res]"=r"(aborted) : [vecinptr]"r"(&vecin), [vecoutptr]"r"(&vecout), [map]"r"(a) : "memory", "r0", "r3", "r4", "r5", "r6", "r7"); if (aborted && (vecin != vecout)){ printf("FAILED: vector state leaked on abort %f != %f\n", (double)vecin, (double)vecout); exit(1); } munmap(a, size); close(fd); printf("PASSED!\n"); return 0; } Signed-off-by: Paul Mackerras <paulus@samba.org> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
-rw-r--r--arch/powerpc/include/asm/processor.h2
-rw-r--r--arch/powerpc/include/asm/thread_info.h5
-rw-r--r--arch/powerpc/include/asm/tm.h1
-rw-r--r--arch/powerpc/kernel/entry_64.S12
-rw-r--r--arch/powerpc/kernel/fpu.S16
-rw-r--r--arch/powerpc/kernel/process.c146
-rw-r--r--arch/powerpc/kernel/signal.c3
-rw-r--r--arch/powerpc/kernel/signal_32.c21
-rw-r--r--arch/powerpc/kernel/signal_64.c14
-rw-r--r--arch/powerpc/kernel/traps.c12
-rw-r--r--arch/powerpc/kernel/vector.S10
11 files changed, 195 insertions, 47 deletions
diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h
index fc14a38c7ccf..232a2fa5b483 100644
--- a/arch/powerpc/include/asm/processor.h
+++ b/arch/powerpc/include/asm/processor.h
@@ -373,6 +373,8 @@ extern int set_endian(struct task_struct *tsk, unsigned int val);
373extern int get_unalign_ctl(struct task_struct *tsk, unsigned long adr); 373extern int get_unalign_ctl(struct task_struct *tsk, unsigned long adr);
374extern int set_unalign_ctl(struct task_struct *tsk, unsigned int val); 374extern int set_unalign_ctl(struct task_struct *tsk, unsigned int val);
375 375
376extern void fp_enable(void);
377extern void vec_enable(void);
376extern void load_fp_state(struct thread_fp_state *fp); 378extern void load_fp_state(struct thread_fp_state *fp);
377extern void store_fp_state(struct thread_fp_state *fp); 379extern void store_fp_state(struct thread_fp_state *fp);
378extern void load_vr_state(struct thread_vr_state *vr); 380extern void load_vr_state(struct thread_vr_state *vr);
diff --git a/arch/powerpc/include/asm/thread_info.h b/arch/powerpc/include/asm/thread_info.h
index fc2bf414c584..b034ecdb7c74 100644
--- a/arch/powerpc/include/asm/thread_info.h
+++ b/arch/powerpc/include/asm/thread_info.h
@@ -91,6 +91,7 @@ static inline struct thread_info *current_thread_info(void)
91#define TIF_POLLING_NRFLAG 3 /* true if poll_idle() is polling 91#define TIF_POLLING_NRFLAG 3 /* true if poll_idle() is polling
92 TIF_NEED_RESCHED */ 92 TIF_NEED_RESCHED */
93#define TIF_32BIT 4 /* 32 bit binary */ 93#define TIF_32BIT 4 /* 32 bit binary */
94#define TIF_RESTORE_TM 5 /* need to restore TM FP/VEC/VSX */
94#define TIF_SYSCALL_AUDIT 7 /* syscall auditing active */ 95#define TIF_SYSCALL_AUDIT 7 /* syscall auditing active */
95#define TIF_SINGLESTEP 8 /* singlestepping active */ 96#define TIF_SINGLESTEP 8 /* singlestepping active */
96#define TIF_NOHZ 9 /* in adaptive nohz mode */ 97#define TIF_NOHZ 9 /* in adaptive nohz mode */
@@ -113,6 +114,7 @@ static inline struct thread_info *current_thread_info(void)
113#define _TIF_NEED_RESCHED (1<<TIF_NEED_RESCHED) 114#define _TIF_NEED_RESCHED (1<<TIF_NEED_RESCHED)
114#define _TIF_POLLING_NRFLAG (1<<TIF_POLLING_NRFLAG) 115#define _TIF_POLLING_NRFLAG (1<<TIF_POLLING_NRFLAG)
115#define _TIF_32BIT (1<<TIF_32BIT) 116#define _TIF_32BIT (1<<TIF_32BIT)
117#define _TIF_RESTORE_TM (1<<TIF_RESTORE_TM)
116#define _TIF_SYSCALL_AUDIT (1<<TIF_SYSCALL_AUDIT) 118#define _TIF_SYSCALL_AUDIT (1<<TIF_SYSCALL_AUDIT)
117#define _TIF_SINGLESTEP (1<<TIF_SINGLESTEP) 119#define _TIF_SINGLESTEP (1<<TIF_SINGLESTEP)
118#define _TIF_SECCOMP (1<<TIF_SECCOMP) 120#define _TIF_SECCOMP (1<<TIF_SECCOMP)
@@ -128,7 +130,8 @@ static inline struct thread_info *current_thread_info(void)
128 _TIF_NOHZ) 130 _TIF_NOHZ)
129 131
130#define _TIF_USER_WORK_MASK (_TIF_SIGPENDING | _TIF_NEED_RESCHED | \ 132#define _TIF_USER_WORK_MASK (_TIF_SIGPENDING | _TIF_NEED_RESCHED | \
131 _TIF_NOTIFY_RESUME | _TIF_UPROBE) 133 _TIF_NOTIFY_RESUME | _TIF_UPROBE | \
134 _TIF_RESTORE_TM)
132#define _TIF_PERSYSCALL_MASK (_TIF_RESTOREALL|_TIF_NOERROR) 135#define _TIF_PERSYSCALL_MASK (_TIF_RESTOREALL|_TIF_NOERROR)
133 136
134/* Bits in local_flags */ 137/* Bits in local_flags */
diff --git a/arch/powerpc/include/asm/tm.h b/arch/powerpc/include/asm/tm.h
index 9dfbc34bdbf5..0c9f8b74dd97 100644
--- a/arch/powerpc/include/asm/tm.h
+++ b/arch/powerpc/include/asm/tm.h
@@ -15,6 +15,7 @@ extern void do_load_up_transact_altivec(struct thread_struct *thread);
15extern void tm_enable(void); 15extern void tm_enable(void);
16extern void tm_reclaim(struct thread_struct *thread, 16extern void tm_reclaim(struct thread_struct *thread,
17 unsigned long orig_msr, uint8_t cause); 17 unsigned long orig_msr, uint8_t cause);
18extern void tm_reclaim_current(uint8_t cause);
18extern void tm_recheckpoint(struct thread_struct *thread, 19extern void tm_recheckpoint(struct thread_struct *thread,
19 unsigned long orig_msr); 20 unsigned long orig_msr);
20extern void tm_abort(uint8_t cause); 21extern void tm_abort(uint8_t cause);
diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S
index bbfb0294b354..662c6dd98072 100644
--- a/arch/powerpc/kernel/entry_64.S
+++ b/arch/powerpc/kernel/entry_64.S
@@ -664,8 +664,16 @@ _GLOBAL(ret_from_except_lite)
664 bl .restore_interrupts 664 bl .restore_interrupts
665 SCHEDULE_USER 665 SCHEDULE_USER
666 b .ret_from_except_lite 666 b .ret_from_except_lite
667 6672:
6682: bl .save_nvgprs 668#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
669 andi. r0,r4,_TIF_USER_WORK_MASK & ~_TIF_RESTORE_TM
670 bne 3f /* only restore TM if nothing else to do */
671 addi r3,r1,STACK_FRAME_OVERHEAD
672 bl .restore_tm_state
673 b restore
6743:
675#endif
676 bl .save_nvgprs
669 bl .restore_interrupts 677 bl .restore_interrupts
670 addi r3,r1,STACK_FRAME_OVERHEAD 678 addi r3,r1,STACK_FRAME_OVERHEAD
671 bl .do_notify_resume 679 bl .do_notify_resume
diff --git a/arch/powerpc/kernel/fpu.S b/arch/powerpc/kernel/fpu.S
index f7f5b8bed68f..9ad236e5d2c9 100644
--- a/arch/powerpc/kernel/fpu.S
+++ b/arch/powerpc/kernel/fpu.S
@@ -81,6 +81,22 @@ END_FTR_SECTION_IFSET(CPU_FTR_VSX)
81#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */ 81#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
82 82
83/* 83/*
84 * Enable use of the FPU, and VSX if possible, for the caller.
85 */
86_GLOBAL(fp_enable)
87 mfmsr r3
88 ori r3,r3,MSR_FP
89#ifdef CONFIG_VSX
90BEGIN_FTR_SECTION
91 oris r3,r3,MSR_VSX@h
92END_FTR_SECTION_IFSET(CPU_FTR_VSX)
93#endif
94 SYNC
95 MTMSRD(r3)
96 isync /* (not necessary for arch 2.02 and later) */
97 blr
98
99/*
84 * Load state from memory into FP registers including FPSCR. 100 * Load state from memory into FP registers including FPSCR.
85 * Assumes the caller has enabled FP in the MSR. 101 * Assumes the caller has enabled FP in the MSR.
86 */ 102 */
diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
index bf8e136316e2..3573d186505f 100644
--- a/arch/powerpc/kernel/process.c
+++ b/arch/powerpc/kernel/process.c
@@ -73,6 +73,48 @@ struct task_struct *last_task_used_vsx = NULL;
73struct task_struct *last_task_used_spe = NULL; 73struct task_struct *last_task_used_spe = NULL;
74#endif 74#endif
75 75
76#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
77void giveup_fpu_maybe_transactional(struct task_struct *tsk)
78{
79 /*
80 * If we are saving the current thread's registers, and the
81 * thread is in a transactional state, set the TIF_RESTORE_TM
82 * bit so that we know to restore the registers before
83 * returning to userspace.
84 */
85 if (tsk == current && tsk->thread.regs &&
86 MSR_TM_ACTIVE(tsk->thread.regs->msr) &&
87 !test_thread_flag(TIF_RESTORE_TM)) {
88 tsk->thread.tm_orig_msr = tsk->thread.regs->msr;
89 set_thread_flag(TIF_RESTORE_TM);
90 }
91
92 giveup_fpu(tsk);
93}
94
95void giveup_altivec_maybe_transactional(struct task_struct *tsk)
96{
97 /*
98 * If we are saving the current thread's registers, and the
99 * thread is in a transactional state, set the TIF_RESTORE_TM
100 * bit so that we know to restore the registers before
101 * returning to userspace.
102 */
103 if (tsk == current && tsk->thread.regs &&
104 MSR_TM_ACTIVE(tsk->thread.regs->msr) &&
105 !test_thread_flag(TIF_RESTORE_TM)) {
106 tsk->thread.tm_orig_msr = tsk->thread.regs->msr;
107 set_thread_flag(TIF_RESTORE_TM);
108 }
109
110 giveup_altivec(tsk);
111}
112
113#else
114#define giveup_fpu_maybe_transactional(tsk) giveup_fpu(tsk)
115#define giveup_altivec_maybe_transactional(tsk) giveup_altivec(tsk)
116#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
117
76#ifdef CONFIG_PPC_FPU 118#ifdef CONFIG_PPC_FPU
77/* 119/*
78 * Make sure the floating-point register state in the 120 * Make sure the floating-point register state in the
@@ -101,13 +143,13 @@ void flush_fp_to_thread(struct task_struct *tsk)
101 */ 143 */
102 BUG_ON(tsk != current); 144 BUG_ON(tsk != current);
103#endif 145#endif
104 giveup_fpu(tsk); 146 giveup_fpu_maybe_transactional(tsk);
105 } 147 }
106 preempt_enable(); 148 preempt_enable();
107 } 149 }
108} 150}
109EXPORT_SYMBOL_GPL(flush_fp_to_thread); 151EXPORT_SYMBOL_GPL(flush_fp_to_thread);
110#endif 152#endif /* CONFIG_PPC_FPU */
111 153
112void enable_kernel_fp(void) 154void enable_kernel_fp(void)
113{ 155{
@@ -115,11 +157,11 @@ void enable_kernel_fp(void)
115 157
116#ifdef CONFIG_SMP 158#ifdef CONFIG_SMP
117 if (current->thread.regs && (current->thread.regs->msr & MSR_FP)) 159 if (current->thread.regs && (current->thread.regs->msr & MSR_FP))
118 giveup_fpu(current); 160 giveup_fpu_maybe_transactional(current);
119 else 161 else
120 giveup_fpu(NULL); /* just enables FP for kernel */ 162 giveup_fpu(NULL); /* just enables FP for kernel */
121#else 163#else
122 giveup_fpu(last_task_used_math); 164 giveup_fpu_maybe_transactional(last_task_used_math);
123#endif /* CONFIG_SMP */ 165#endif /* CONFIG_SMP */
124} 166}
125EXPORT_SYMBOL(enable_kernel_fp); 167EXPORT_SYMBOL(enable_kernel_fp);
@@ -131,11 +173,11 @@ void enable_kernel_altivec(void)
131 173
132#ifdef CONFIG_SMP 174#ifdef CONFIG_SMP
133 if (current->thread.regs && (current->thread.regs->msr & MSR_VEC)) 175 if (current->thread.regs && (current->thread.regs->msr & MSR_VEC))
134 giveup_altivec(current); 176 giveup_altivec_maybe_transactional(current);
135 else 177 else
136 giveup_altivec_notask(); 178 giveup_altivec_notask();
137#else 179#else
138 giveup_altivec(last_task_used_altivec); 180 giveup_altivec_maybe_transactional(last_task_used_altivec);
139#endif /* CONFIG_SMP */ 181#endif /* CONFIG_SMP */
140} 182}
141EXPORT_SYMBOL(enable_kernel_altivec); 183EXPORT_SYMBOL(enable_kernel_altivec);
@@ -152,7 +194,7 @@ void flush_altivec_to_thread(struct task_struct *tsk)
152#ifdef CONFIG_SMP 194#ifdef CONFIG_SMP
153 BUG_ON(tsk != current); 195 BUG_ON(tsk != current);
154#endif 196#endif
155 giveup_altivec(tsk); 197 giveup_altivec_maybe_transactional(tsk);
156 } 198 }
157 preempt_enable(); 199 preempt_enable();
158 } 200 }
@@ -181,8 +223,8 @@ EXPORT_SYMBOL(enable_kernel_vsx);
181 223
182void giveup_vsx(struct task_struct *tsk) 224void giveup_vsx(struct task_struct *tsk)
183{ 225{
184 giveup_fpu(tsk); 226 giveup_fpu_maybe_transactional(tsk);
185 giveup_altivec(tsk); 227 giveup_altivec_maybe_transactional(tsk);
186 __giveup_vsx(tsk); 228 __giveup_vsx(tsk);
187} 229}
188 230
@@ -478,7 +520,48 @@ static inline bool hw_brk_match(struct arch_hw_breakpoint *a,
478 return false; 520 return false;
479 return true; 521 return true;
480} 522}
523
481#ifdef CONFIG_PPC_TRANSACTIONAL_MEM 524#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
525static void tm_reclaim_thread(struct thread_struct *thr,
526 struct thread_info *ti, uint8_t cause)
527{
528 unsigned long msr_diff = 0;
529
530 /*
531 * If FP/VSX registers have been already saved to the
532 * thread_struct, move them to the transact_fp array.
533 * We clear the TIF_RESTORE_TM bit since after the reclaim
534 * the thread will no longer be transactional.
535 */
536 if (test_ti_thread_flag(ti, TIF_RESTORE_TM)) {
537 msr_diff = thr->tm_orig_msr & ~thr->regs->msr;
538 if (msr_diff & MSR_FP)
539 memcpy(&thr->transact_fp, &thr->fp_state,
540 sizeof(struct thread_fp_state));
541 if (msr_diff & MSR_VEC)
542 memcpy(&thr->transact_vr, &thr->vr_state,
543 sizeof(struct thread_vr_state));
544 clear_ti_thread_flag(ti, TIF_RESTORE_TM);
545 msr_diff &= MSR_FP | MSR_VEC | MSR_VSX | MSR_FE0 | MSR_FE1;
546 }
547
548 tm_reclaim(thr, thr->regs->msr, cause);
549
550 /* Having done the reclaim, we now have the checkpointed
551 * FP/VSX values in the registers. These might be valid
552 * even if we have previously called enable_kernel_fp() or
553 * flush_fp_to_thread(), so update thr->regs->msr to
554 * indicate their current validity.
555 */
556 thr->regs->msr |= msr_diff;
557}
558
559void tm_reclaim_current(uint8_t cause)
560{
561 tm_enable();
562 tm_reclaim_thread(&current->thread, current_thread_info(), cause);
563}
564
482static inline void tm_reclaim_task(struct task_struct *tsk) 565static inline void tm_reclaim_task(struct task_struct *tsk)
483{ 566{
484 /* We have to work out if we're switching from/to a task that's in the 567 /* We have to work out if we're switching from/to a task that's in the
@@ -501,9 +584,11 @@ static inline void tm_reclaim_task(struct task_struct *tsk)
501 584
502 /* Stash the original thread MSR, as giveup_fpu et al will 585 /* Stash the original thread MSR, as giveup_fpu et al will
503 * modify it. We hold onto it to see whether the task used 586 * modify it. We hold onto it to see whether the task used
504 * FP & vector regs. 587 * FP & vector regs. If the TIF_RESTORE_TM flag is set,
588 * tm_orig_msr is already set.
505 */ 589 */
506 thr->tm_orig_msr = thr->regs->msr; 590 if (!test_ti_thread_flag(task_thread_info(tsk), TIF_RESTORE_TM))
591 thr->tm_orig_msr = thr->regs->msr;
507 592
508 TM_DEBUG("--- tm_reclaim on pid %d (NIP=%lx, " 593 TM_DEBUG("--- tm_reclaim on pid %d (NIP=%lx, "
509 "ccr=%lx, msr=%lx, trap=%lx)\n", 594 "ccr=%lx, msr=%lx, trap=%lx)\n",
@@ -511,7 +596,7 @@ static inline void tm_reclaim_task(struct task_struct *tsk)
511 thr->regs->ccr, thr->regs->msr, 596 thr->regs->ccr, thr->regs->msr,
512 thr->regs->trap); 597 thr->regs->trap);
513 598
514 tm_reclaim(thr, thr->regs->msr, TM_CAUSE_RESCHED); 599 tm_reclaim_thread(thr, task_thread_info(tsk), TM_CAUSE_RESCHED);
515 600
516 TM_DEBUG("--- tm_reclaim on pid %d complete\n", 601 TM_DEBUG("--- tm_reclaim on pid %d complete\n",
517 tsk->pid); 602 tsk->pid);
@@ -587,6 +672,43 @@ static inline void __switch_to_tm(struct task_struct *prev)
587 tm_reclaim_task(prev); 672 tm_reclaim_task(prev);
588 } 673 }
589} 674}
675
676/*
677 * This is called if we are on the way out to userspace and the
678 * TIF_RESTORE_TM flag is set. It checks if we need to reload
679 * FP and/or vector state and does so if necessary.
680 * If userspace is inside a transaction (whether active or
681 * suspended) and FP/VMX/VSX instructions have ever been enabled
682 * inside that transaction, then we have to keep them enabled
683 * and keep the FP/VMX/VSX state loaded while ever the transaction
684 * continues. The reason is that if we didn't, and subsequently
685 * got a FP/VMX/VSX unavailable interrupt inside a transaction,
686 * we don't know whether it's the same transaction, and thus we
687 * don't know which of the checkpointed state and the transactional
688 * state to use.
689 */
690void restore_tm_state(struct pt_regs *regs)
691{
692 unsigned long msr_diff;
693
694 clear_thread_flag(TIF_RESTORE_TM);
695 if (!MSR_TM_ACTIVE(regs->msr))
696 return;
697
698 msr_diff = current->thread.tm_orig_msr & ~regs->msr;
699 msr_diff &= MSR_FP | MSR_VEC | MSR_VSX;
700 if (msr_diff & MSR_FP) {
701 fp_enable();
702 load_fp_state(&current->thread.fp_state);
703 regs->msr |= current->thread.fpexc_mode;
704 }
705 if (msr_diff & MSR_VEC) {
706 vec_enable();
707 load_vr_state(&current->thread.vr_state);
708 }
709 regs->msr |= msr_diff;
710}
711
590#else 712#else
591#define tm_recheckpoint_new_task(new) 713#define tm_recheckpoint_new_task(new)
592#define __switch_to_tm(prev) 714#define __switch_to_tm(prev)
diff --git a/arch/powerpc/kernel/signal.c b/arch/powerpc/kernel/signal.c
index 457e97aa2945..8fc4177ed65a 100644
--- a/arch/powerpc/kernel/signal.c
+++ b/arch/powerpc/kernel/signal.c
@@ -203,8 +203,7 @@ unsigned long get_tm_stackpointer(struct pt_regs *regs)
203 203
204#ifdef CONFIG_PPC_TRANSACTIONAL_MEM 204#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
205 if (MSR_TM_ACTIVE(regs->msr)) { 205 if (MSR_TM_ACTIVE(regs->msr)) {
206 tm_enable(); 206 tm_reclaim_current(TM_CAUSE_SIGNAL);
207 tm_reclaim(&current->thread, regs->msr, TM_CAUSE_SIGNAL);
208 if (MSR_TM_TRANSACTIONAL(regs->msr)) 207 if (MSR_TM_TRANSACTIONAL(regs->msr))
209 return current->thread.ckpt_regs.gpr[1]; 208 return current->thread.ckpt_regs.gpr[1];
210 } 209 }
diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c
index 68027bfa5f8e..6ce69e6f1fcb 100644
--- a/arch/powerpc/kernel/signal_32.c
+++ b/arch/powerpc/kernel/signal_32.c
@@ -519,6 +519,13 @@ static int save_tm_user_regs(struct pt_regs *regs,
519{ 519{
520 unsigned long msr = regs->msr; 520 unsigned long msr = regs->msr;
521 521
522 /* Remove TM bits from thread's MSR. The MSR in the sigcontext
523 * just indicates to userland that we were doing a transaction, but we
524 * don't want to return in transactional state. This also ensures
525 * that flush_fp_to_thread won't set TIF_RESTORE_TM again.
526 */
527 regs->msr &= ~MSR_TS_MASK;
528
522 /* Make sure floating point registers are stored in regs */ 529 /* Make sure floating point registers are stored in regs */
523 flush_fp_to_thread(current); 530 flush_fp_to_thread(current);
524 531
@@ -1056,13 +1063,6 @@ int handle_rt_signal32(unsigned long sig, struct k_sigaction *ka,
1056 /* enter the signal handler in native-endian mode */ 1063 /* enter the signal handler in native-endian mode */
1057 regs->msr &= ~MSR_LE; 1064 regs->msr &= ~MSR_LE;
1058 regs->msr |= (MSR_KERNEL & MSR_LE); 1065 regs->msr |= (MSR_KERNEL & MSR_LE);
1059#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
1060 /* Remove TM bits from thread's MSR. The MSR in the sigcontext
1061 * just indicates to userland that we were doing a transaction, but we
1062 * don't want to return in transactional state:
1063 */
1064 regs->msr &= ~MSR_TS_MASK;
1065#endif
1066 return 1; 1066 return 1;
1067 1067
1068badframe: 1068badframe:
@@ -1484,13 +1484,6 @@ int handle_signal32(unsigned long sig, struct k_sigaction *ka,
1484 regs->nip = (unsigned long) ka->sa.sa_handler; 1484 regs->nip = (unsigned long) ka->sa.sa_handler;
1485 /* enter the signal handler in big-endian mode */ 1485 /* enter the signal handler in big-endian mode */
1486 regs->msr &= ~MSR_LE; 1486 regs->msr &= ~MSR_LE;
1487#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
1488 /* Remove TM bits from thread's MSR. The MSR in the sigcontext
1489 * just indicates to userland that we were doing a transaction, but we
1490 * don't want to return in transactional state:
1491 */
1492 regs->msr &= ~MSR_TS_MASK;
1493#endif
1494 return 1; 1487 return 1;
1495 1488
1496badframe: 1489badframe:
diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c
index 42991045349f..e35bf773df7a 100644
--- a/arch/powerpc/kernel/signal_64.c
+++ b/arch/powerpc/kernel/signal_64.c
@@ -192,6 +192,13 @@ static long setup_tm_sigcontexts(struct sigcontext __user *sc,
192 192
193 BUG_ON(!MSR_TM_ACTIVE(regs->msr)); 193 BUG_ON(!MSR_TM_ACTIVE(regs->msr));
194 194
195 /* Remove TM bits from thread's MSR. The MSR in the sigcontext
196 * just indicates to userland that we were doing a transaction, but we
197 * don't want to return in transactional state. This also ensures
198 * that flush_fp_to_thread won't set TIF_RESTORE_TM again.
199 */
200 regs->msr &= ~MSR_TS_MASK;
201
195 flush_fp_to_thread(current); 202 flush_fp_to_thread(current);
196 203
197#ifdef CONFIG_ALTIVEC 204#ifdef CONFIG_ALTIVEC
@@ -749,13 +756,6 @@ int handle_rt_signal64(int signr, struct k_sigaction *ka, siginfo_t *info,
749 756
750 /* Make sure signal handler doesn't get spurious FP exceptions */ 757 /* Make sure signal handler doesn't get spurious FP exceptions */
751 current->thread.fp_state.fpscr = 0; 758 current->thread.fp_state.fpscr = 0;
752#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
753 /* Remove TM bits from thread's MSR. The MSR in the sigcontext
754 * just indicates to userland that we were doing a transaction, but we
755 * don't want to return in transactional state:
756 */
757 regs->msr &= ~MSR_TS_MASK;
758#endif
759 759
760 /* Set up to return from userspace. */ 760 /* Set up to return from userspace. */
761 if (vdso64_rt_sigtramp && current->mm->context.vdso_base) { 761 if (vdso64_rt_sigtramp && current->mm->context.vdso_base) {
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
index 330841766b09..26e1358ff0bc 100644
--- a/arch/powerpc/kernel/traps.c
+++ b/arch/powerpc/kernel/traps.c
@@ -1399,7 +1399,6 @@ void fp_unavailable_tm(struct pt_regs *regs)
1399 1399
1400 TM_DEBUG("FP Unavailable trap whilst transactional at 0x%lx, MSR=%lx\n", 1400 TM_DEBUG("FP Unavailable trap whilst transactional at 0x%lx, MSR=%lx\n",
1401 regs->nip, regs->msr); 1401 regs->nip, regs->msr);
1402 tm_enable();
1403 1402
1404 /* We can only have got here if the task started using FP after 1403 /* We can only have got here if the task started using FP after
1405 * beginning the transaction. So, the transactional regs are just a 1404 * beginning the transaction. So, the transactional regs are just a
@@ -1408,8 +1407,7 @@ void fp_unavailable_tm(struct pt_regs *regs)
1408 * transaction, and probably retry but now with FP enabled. So the 1407 * transaction, and probably retry but now with FP enabled. So the
1409 * checkpointed FP registers need to be loaded. 1408 * checkpointed FP registers need to be loaded.
1410 */ 1409 */
1411 tm_reclaim(&current->thread, current->thread.regs->msr, 1410 tm_reclaim_current(TM_CAUSE_FAC_UNAV);
1412 TM_CAUSE_FAC_UNAV);
1413 /* Reclaim didn't save out any FPRs to transact_fprs. */ 1411 /* Reclaim didn't save out any FPRs to transact_fprs. */
1414 1412
1415 /* Enable FP for the task: */ 1413 /* Enable FP for the task: */
@@ -1432,9 +1430,7 @@ void altivec_unavailable_tm(struct pt_regs *regs)
1432 TM_DEBUG("Vector Unavailable trap whilst transactional at 0x%lx," 1430 TM_DEBUG("Vector Unavailable trap whilst transactional at 0x%lx,"
1433 "MSR=%lx\n", 1431 "MSR=%lx\n",
1434 regs->nip, regs->msr); 1432 regs->nip, regs->msr);
1435 tm_enable(); 1433 tm_reclaim_current(TM_CAUSE_FAC_UNAV);
1436 tm_reclaim(&current->thread, current->thread.regs->msr,
1437 TM_CAUSE_FAC_UNAV);
1438 regs->msr |= MSR_VEC; 1434 regs->msr |= MSR_VEC;
1439 tm_recheckpoint(&current->thread, regs->msr); 1435 tm_recheckpoint(&current->thread, regs->msr);
1440 current->thread.used_vr = 1; 1436 current->thread.used_vr = 1;
@@ -1455,10 +1451,8 @@ void vsx_unavailable_tm(struct pt_regs *regs)
1455 "MSR=%lx\n", 1451 "MSR=%lx\n",
1456 regs->nip, regs->msr); 1452 regs->nip, regs->msr);
1457 1453
1458 tm_enable();
1459 /* This reclaims FP and/or VR regs if they're already enabled */ 1454 /* This reclaims FP and/or VR regs if they're already enabled */
1460 tm_reclaim(&current->thread, current->thread.regs->msr, 1455 tm_reclaim_current(TM_CAUSE_FAC_UNAV);
1461 TM_CAUSE_FAC_UNAV);
1462 1456
1463 regs->msr |= MSR_VEC | MSR_FP | current->thread.fpexc_mode | 1457 regs->msr |= MSR_VEC | MSR_FP | current->thread.fpexc_mode |
1464 MSR_VSX; 1458 MSR_VSX;
diff --git a/arch/powerpc/kernel/vector.S b/arch/powerpc/kernel/vector.S
index 0458a9aaba9d..74f8050518d6 100644
--- a/arch/powerpc/kernel/vector.S
+++ b/arch/powerpc/kernel/vector.S
@@ -37,6 +37,16 @@ _GLOBAL(do_load_up_transact_altivec)
37#endif 37#endif
38 38
39/* 39/*
40 * Enable use of VMX/Altivec for the caller.
41 */
42_GLOBAL(vec_enable)
43 mfmsr r3
44 oris r3,r3,MSR_VEC@h
45 MTMSRD(r3)
46 isync
47 blr
48
49/*
40 * Load state from memory into VMX registers including VSCR. 50 * Load state from memory into VMX registers including VSCR.
41 * Assumes the caller has enabled VMX in the MSR. 51 * Assumes the caller has enabled VMX in the MSR.
42 */ 52 */