Commit bdec97a8 authored by Will Deacon's avatar Will Deacon

arm64: compat: fix vfp save/restore across signal handlers in big-endian

When saving/restoring the VFP registers from a compat (AArch32)
signal frame, we rely on the compat registers forming a prefix of the
native register file and therefore make use of copy_{to,from}_user to
transfer between the native fpsimd_state and the compat_vfp_sigframe.

Unfortunately, this doesn't work so well in a big-endian environment.
Our fpsimd save/restore code operates directly on 128-bit quantities
(Q registers) whereas the compat_vfp_sigframe represents the registers
as an array of 64-bit (D) registers. The architecture packs the compat D
registers into the Q registers, with the least significant bytes holding
the lower register. Consequently, we need to swap the 64-bit halves when
converting between these two representations on a big-endian machine.

This patch replaces the __copy_{to,from}_user invocations in our
compat VFP signal handling code with explicit __put_user loops that
operate on 64-bit values and swap them accordingly.

Cc: <stable@vger.kernel.org>
Reviewed-by: default avatarCatalin Marinas <catalin.marinas@arm.com>
Signed-off-by: default avatarWill Deacon <will.deacon@arm.com>
parent e56d82a1
...@@ -212,14 +212,32 @@ int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from) ...@@ -212,14 +212,32 @@ int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from)
/* /*
* VFP save/restore code. * VFP save/restore code.
*
* We have to be careful with endianness, since the fpsimd context-switch
* code operates on 128-bit (Q) register values whereas the compat ABI
* uses an array of 64-bit (D) registers. Consequently, we need to swap
* the two halves of each Q register when running on a big-endian CPU.
*/ */
union __fpsimd_vreg {
__uint128_t raw;
struct {
#ifdef __AARCH64EB__
u64 hi;
u64 lo;
#else
u64 lo;
u64 hi;
#endif
};
};
static int compat_preserve_vfp_context(struct compat_vfp_sigframe __user *frame) static int compat_preserve_vfp_context(struct compat_vfp_sigframe __user *frame)
{ {
struct fpsimd_state *fpsimd = &current->thread.fpsimd_state; struct fpsimd_state *fpsimd = &current->thread.fpsimd_state;
compat_ulong_t magic = VFP_MAGIC; compat_ulong_t magic = VFP_MAGIC;
compat_ulong_t size = VFP_STORAGE_SIZE; compat_ulong_t size = VFP_STORAGE_SIZE;
compat_ulong_t fpscr, fpexc; compat_ulong_t fpscr, fpexc;
int err = 0; int i, err = 0;
/* /*
* Save the hardware registers to the fpsimd_state structure. * Save the hardware registers to the fpsimd_state structure.
...@@ -235,10 +253,15 @@ static int compat_preserve_vfp_context(struct compat_vfp_sigframe __user *frame) ...@@ -235,10 +253,15 @@ static int compat_preserve_vfp_context(struct compat_vfp_sigframe __user *frame)
/* /*
* Now copy the FP registers. Since the registers are packed, * Now copy the FP registers. Since the registers are packed,
* we can copy the prefix we want (V0-V15) as it is. * we can copy the prefix we want (V0-V15) as it is.
* FIXME: Won't work if big endian.
*/ */
err |= __copy_to_user(&frame->ufp.fpregs, fpsimd->vregs, for (i = 0; i < ARRAY_SIZE(frame->ufp.fpregs); i += 2) {
sizeof(frame->ufp.fpregs)); union __fpsimd_vreg vreg = {
.raw = fpsimd->vregs[i >> 1],
};
__put_user_error(vreg.lo, &frame->ufp.fpregs[i], err);
__put_user_error(vreg.hi, &frame->ufp.fpregs[i + 1], err);
}
/* Create an AArch32 fpscr from the fpsr and the fpcr. */ /* Create an AArch32 fpscr from the fpsr and the fpcr. */
fpscr = (fpsimd->fpsr & VFP_FPSCR_STAT_MASK) | fpscr = (fpsimd->fpsr & VFP_FPSCR_STAT_MASK) |
...@@ -263,7 +286,7 @@ static int compat_restore_vfp_context(struct compat_vfp_sigframe __user *frame) ...@@ -263,7 +286,7 @@ static int compat_restore_vfp_context(struct compat_vfp_sigframe __user *frame)
compat_ulong_t magic = VFP_MAGIC; compat_ulong_t magic = VFP_MAGIC;
compat_ulong_t size = VFP_STORAGE_SIZE; compat_ulong_t size = VFP_STORAGE_SIZE;
compat_ulong_t fpscr; compat_ulong_t fpscr;
int err = 0; int i, err = 0;
__get_user_error(magic, &frame->magic, err); __get_user_error(magic, &frame->magic, err);
__get_user_error(size, &frame->size, err); __get_user_error(size, &frame->size, err);
...@@ -273,12 +296,14 @@ static int compat_restore_vfp_context(struct compat_vfp_sigframe __user *frame) ...@@ -273,12 +296,14 @@ static int compat_restore_vfp_context(struct compat_vfp_sigframe __user *frame)
if (magic != VFP_MAGIC || size != VFP_STORAGE_SIZE) if (magic != VFP_MAGIC || size != VFP_STORAGE_SIZE)
return -EINVAL; return -EINVAL;
/* /* Copy the FP registers into the start of the fpsimd_state. */
* Copy the FP registers into the start of the fpsimd_state. for (i = 0; i < ARRAY_SIZE(frame->ufp.fpregs); i += 2) {
* FIXME: Won't work if big endian. union __fpsimd_vreg vreg;
*/
err |= __copy_from_user(fpsimd.vregs, frame->ufp.fpregs, __get_user_error(vreg.lo, &frame->ufp.fpregs[i], err);
sizeof(frame->ufp.fpregs)); __get_user_error(vreg.hi, &frame->ufp.fpregs[i + 1], err);
fpsimd.vregs[i >> 1] = vreg.raw;
}
/* Extract the fpsr and the fpcr from the fpscr */ /* Extract the fpsr and the fpcr from the fpscr */
__get_user_error(fpscr, &frame->ufp.fpscr, err); __get_user_error(fpscr, &frame->ufp.fpscr, err);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment