aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/signal.c
diff options
context:
space:
mode:
authorSuresh Siddha <suresh.b.siddha@intel.com>2012-07-24 19:05:29 -0400
committerH. Peter Anvin <hpa@linux.intel.com>2012-09-18 18:51:48 -0400
commit72a671ced66db6d1c2bfff1c930a101ac8d08204 (patch)
treefb3f58fa735d2d3cffc636e3e0f8114a04b04623 /arch/x86/kernel/signal.c
parent0ca5bd0d886578ad0afeceaa83458c0f35cb3c6b (diff)
x86, fpu: Unify signal handling code paths for x86 and x86_64 kernels
Currently for x86 and x86_32 binaries, fpstate in the user sigframe is copied to/from the fpstate in the task struct. And in the case of signal delivery for x86_64 binaries, if the fpstate is live in the CPU registers, then the live state is copied directly to the user sigframe. Otherwise fpstate in the task struct is copied to the user sigframe. During restore, fpstate in the user sigframe is restored directly to the live CPU registers. Historically, different code paths led to different bugs. For example, x86_64 code path was not preemption safe till recently. Also there is lot of code duplication for support of new features like xsave etc. Unify signal handling code paths for x86 and x86_64 kernels. New strategy is as follows: Signal delivery: Both for 32/64-bit frames, align the core math frame area to 64bytes as needed by xsave (this where the main fpu/extended state gets copied to and excludes the legacy compatibility fsave header for the 32-bit [f]xsave frames). If the state is live, copy the register state directly to the user frame. If not live, copy the state in the thread struct to the user frame. And for 32-bit [f]xsave frames, construct the fsave header separately before the actual [f]xsave area. Signal return: As the 32-bit frames with [f]xstate has an additional 'fsave' header, copy everything back from the user sigframe to the fpstate in the task structure and reconstruct the fxstate from the 'fsave' header (Also user passed pointers may not be correctly aligned for any attempt to directly restore any partial state). At the next fpstate usage, everything will be restored to the live CPU registers. For all the 64-bit frames and the 32-bit fsave frame, restore the state from the user sigframe directly to the live CPU registers. 64-bit signals always restored the math frame directly, so we can expect the math frame pointer to be correctly aligned. For 32-bit fsave frames, there are no alignment requirements, so we can restore the state directly. "lat_sig catch" microbenchmark numbers (for x86, x86_64, x86_32 binaries) are with in the noise range with this change. Signed-off-by: Suresh Siddha <suresh.b.siddha@intel.com> Link: http://lkml.kernel.org/r/1343171129-2747-4-git-send-email-suresh.b.siddha@intel.com [ Merged in compilation fix ] Link: http://lkml.kernel.org/r/1344544736.8326.17.camel@sbsiddha-desk.sc.intel.com Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
Diffstat (limited to 'arch/x86/kernel/signal.c')
-rw-r--r--arch/x86/kernel/signal.c15
1 files changed, 8 insertions, 7 deletions
diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c
index bed431a38162..e10f96a7e047 100644
--- a/arch/x86/kernel/signal.c
+++ b/arch/x86/kernel/signal.c
@@ -114,7 +114,7 @@ int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc,
114 regs->orig_ax = -1; /* disable syscall checks */ 114 regs->orig_ax = -1; /* disable syscall checks */
115 115
116 get_user_ex(buf, &sc->fpstate); 116 get_user_ex(buf, &sc->fpstate);
117 err |= restore_i387_xstate(buf); 117 err |= restore_xstate_sig(buf, config_enabled(CONFIG_X86_32));
118 118
119 get_user_ex(*pax, &sc->ax); 119 get_user_ex(*pax, &sc->ax);
120 } get_user_catch(err); 120 } get_user_catch(err);
@@ -206,7 +206,9 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size,
206 void __user **fpstate) 206 void __user **fpstate)
207{ 207{
208 /* Default to using normal stack */ 208 /* Default to using normal stack */
209 unsigned long math_size = 0;
209 unsigned long sp = regs->sp; 210 unsigned long sp = regs->sp;
211 unsigned long buf_fx = 0;
210 int onsigstack = on_sig_stack(sp); 212 int onsigstack = on_sig_stack(sp);
211 213
212 /* redzone */ 214 /* redzone */
@@ -228,10 +230,8 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size,
228 } 230 }
229 231
230 if (used_math()) { 232 if (used_math()) {
231 sp -= sig_xstate_size; 233 sp = alloc_mathframe(sp, config_enabled(CONFIG_X86_32),
232#ifdef CONFIG_X86_64 234 &buf_fx, &math_size);
233 sp = round_down(sp, 64);
234#endif /* CONFIG_X86_64 */
235 *fpstate = (void __user *)sp; 235 *fpstate = (void __user *)sp;
236 } 236 }
237 237
@@ -244,8 +244,9 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size,
244 if (onsigstack && !likely(on_sig_stack(sp))) 244 if (onsigstack && !likely(on_sig_stack(sp)))
245 return (void __user *)-1L; 245 return (void __user *)-1L;
246 246
247 /* save i387 state */ 247 /* save i387 and extended state */
248 if (used_math() && save_i387_xstate(*fpstate) < 0) 248 if (used_math() &&
249 save_xstate_sig(*fpstate, (void __user *)buf_fx, math_size) < 0)
249 return (void __user *)-1L; 250 return (void __user *)-1L;
250 251
251 return (void __user *)sp; 252 return (void __user *)sp;