diff options
author | Paul Mackerras <paulus@samba.org> | 2013-08-05 14:13:16 +1000 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2013-08-14 14:57:18 +1000 |
commit | 408a7e08b2112faf687dd629212e42998a7dbe48 (patch) | |
tree | 9c616a8507bd5451bd845c18a5442e2e3adf8814 /arch/powerpc/kernel/signal_64.c | |
parent | 1f7bf028763cacf5a5674731e8f679c4718f2cfa (diff) | |
download | lwn-408a7e08b2112faf687dd629212e42998a7dbe48.tar.gz lwn-408a7e08b2112faf687dd629212e42998a7dbe48.zip |
powerpc: Fix VRSAVE handling
Since 2002, the kernel has not saved VRSAVE on exception entry and
restored it on exit; rather, VRSAVE gets context-switched in _switch.
This means that when executing in process context in the kernel, the
userspace VRSAVE value is live in the VRSAVE register.
However, the signal code assumes that current->thread.vrsave holds
the current VRSAVE value, which is incorrect. Therefore, this
commit changes it to use the actual VRSAVE register instead. (It
still uses current->thread.vrsave as a temporary location to store
it in, as __get_user and __put_user can only transfer to/from a
variable, not an SPR.)
This also modifies the transactional memory code to save and restore
VRSAVE regardless of whether VMX is enabled in the MSR. This is
because accesses to VRSAVE are not controlled by the MSR.VEC bit,
but can happen at any time.
Signed-off-by: Paul Mackerras <paulus@samba.org>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc/kernel/signal_64.c')
-rw-r--r-- | arch/powerpc/kernel/signal_64.c | 8 |
1 files changed, 8 insertions, 0 deletions
diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c index cbd26928e04d..d0106b8a7484 100644 --- a/arch/powerpc/kernel/signal_64.c +++ b/arch/powerpc/kernel/signal_64.c @@ -114,6 +114,8 @@ static long setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, /* We always copy to/from vrsave, it's 0 if we don't have or don't * use altivec. */ + if (cpu_has_feature(CPU_FTR_ALTIVEC)) + current->thread.vrsave = mfspr(SPRN_VRSAVE); err |= __put_user(current->thread.vrsave, (u32 __user *)&v_regs[33]); #else /* CONFIG_ALTIVEC */ err |= __put_user(0, &sc->v_regs); @@ -217,6 +219,8 @@ static long setup_tm_sigcontexts(struct sigcontext __user *sc, /* We always copy to/from vrsave, it's 0 if we don't have or don't * use altivec. */ + if (cpu_has_feature(CPU_FTR_ALTIVEC)) + current->thread.vrsave = mfspr(SPRN_VRSAVE); err |= __put_user(current->thread.vrsave, (u32 __user *)&v_regs[33]); if (msr & MSR_VEC) err |= __put_user(current->thread.transact_vrsave, @@ -356,6 +360,8 @@ static long restore_sigcontext(struct pt_regs *regs, sigset_t *set, int sig, err |= __get_user(current->thread.vrsave, (u32 __user *)&v_regs[33]); else current->thread.vrsave = 0; + if (cpu_has_feature(CPU_FTR_ALTIVEC)) + mtspr(SPRN_VRSAVE, current->thread.vrsave); #endif /* CONFIG_ALTIVEC */ /* restore floating point */ err |= copy_fpr_from_user(current, &sc->fp_regs); @@ -484,6 +490,8 @@ static long restore_tm_sigcontexts(struct pt_regs *regs, current->thread.vrsave = 0; current->thread.transact_vrsave = 0; } + if (cpu_has_feature(CPU_FTR_ALTIVEC)) + mtspr(SPRN_VRSAVE, current->thread.vrsave); #endif /* CONFIG_ALTIVEC */ /* restore floating point */ err |= copy_fpr_from_user(current, &sc->fp_regs); |