mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2024-12-29 09:12:07 +00:00
4734406c39
With the recent rewrite of the fpu code exception handling for the
lfpc instruction within load_fpu_state() was erroneously removed.
Add it again to prevent that loading invalid floating point register
values cause an unhandled specification exception.
Fixes: 8c09871a95
("s390/fpu: limit save and restore to used registers")
Cc: stable@vger.kernel.org
Reported-by: Aristeu Rozanski <aris@redhat.com>
Tested-by: Aristeu Rozanski <aris@redhat.com>
Reviewed-by: Vasily Gorbik <gor@linux.ibm.com>
Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
194 lines
4.2 KiB
C
194 lines
4.2 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* In-kernel vector facility support functions
|
|
*
|
|
* Copyright IBM Corp. 2015
|
|
* Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
|
|
*/
|
|
#include <linux/kernel.h>
|
|
#include <linux/cpu.h>
|
|
#include <linux/sched.h>
|
|
#include <asm/fpu.h>
|
|
|
|
void __kernel_fpu_begin(struct kernel_fpu *state, int flags)
|
|
{
|
|
__vector128 *vxrs = state->vxrs;
|
|
int mask;
|
|
|
|
/*
|
|
* Limit the save to the FPU/vector registers already
|
|
* in use by the previous context.
|
|
*/
|
|
flags &= state->hdr.mask;
|
|
if (flags & KERNEL_FPC)
|
|
fpu_stfpc(&state->hdr.fpc);
|
|
if (!cpu_has_vx()) {
|
|
if (flags & KERNEL_VXR_LOW)
|
|
save_fp_regs_vx(vxrs);
|
|
return;
|
|
}
|
|
mask = flags & KERNEL_VXR;
|
|
if (mask == KERNEL_VXR) {
|
|
vxrs += fpu_vstm(0, 15, vxrs);
|
|
vxrs += fpu_vstm(16, 31, vxrs);
|
|
return;
|
|
}
|
|
if (mask == KERNEL_VXR_MID) {
|
|
vxrs += fpu_vstm(8, 23, vxrs);
|
|
return;
|
|
}
|
|
mask = flags & KERNEL_VXR_LOW;
|
|
if (mask) {
|
|
if (mask == KERNEL_VXR_LOW)
|
|
vxrs += fpu_vstm(0, 15, vxrs);
|
|
else if (mask == KERNEL_VXR_V0V7)
|
|
vxrs += fpu_vstm(0, 7, vxrs);
|
|
else
|
|
vxrs += fpu_vstm(8, 15, vxrs);
|
|
}
|
|
mask = flags & KERNEL_VXR_HIGH;
|
|
if (mask) {
|
|
if (mask == KERNEL_VXR_HIGH)
|
|
vxrs += fpu_vstm(16, 31, vxrs);
|
|
else if (mask == KERNEL_VXR_V16V23)
|
|
vxrs += fpu_vstm(16, 23, vxrs);
|
|
else
|
|
vxrs += fpu_vstm(24, 31, vxrs);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(__kernel_fpu_begin);
|
|
|
|
void __kernel_fpu_end(struct kernel_fpu *state, int flags)
|
|
{
|
|
__vector128 *vxrs = state->vxrs;
|
|
int mask;
|
|
|
|
/*
|
|
* Limit the restore to the FPU/vector registers of the
|
|
* previous context that have been overwritten by the
|
|
* current context.
|
|
*/
|
|
flags &= state->hdr.mask;
|
|
if (flags & KERNEL_FPC)
|
|
fpu_lfpc(&state->hdr.fpc);
|
|
if (!cpu_has_vx()) {
|
|
if (flags & KERNEL_VXR_LOW)
|
|
load_fp_regs_vx(vxrs);
|
|
return;
|
|
}
|
|
mask = flags & KERNEL_VXR;
|
|
if (mask == KERNEL_VXR) {
|
|
vxrs += fpu_vlm(0, 15, vxrs);
|
|
vxrs += fpu_vlm(16, 31, vxrs);
|
|
return;
|
|
}
|
|
if (mask == KERNEL_VXR_MID) {
|
|
vxrs += fpu_vlm(8, 23, vxrs);
|
|
return;
|
|
}
|
|
mask = flags & KERNEL_VXR_LOW;
|
|
if (mask) {
|
|
if (mask == KERNEL_VXR_LOW)
|
|
vxrs += fpu_vlm(0, 15, vxrs);
|
|
else if (mask == KERNEL_VXR_V0V7)
|
|
vxrs += fpu_vlm(0, 7, vxrs);
|
|
else
|
|
vxrs += fpu_vlm(8, 15, vxrs);
|
|
}
|
|
mask = flags & KERNEL_VXR_HIGH;
|
|
if (mask) {
|
|
if (mask == KERNEL_VXR_HIGH)
|
|
vxrs += fpu_vlm(16, 31, vxrs);
|
|
else if (mask == KERNEL_VXR_V16V23)
|
|
vxrs += fpu_vlm(16, 23, vxrs);
|
|
else
|
|
vxrs += fpu_vlm(24, 31, vxrs);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(__kernel_fpu_end);
|
|
|
|
void load_fpu_state(struct fpu *state, int flags)
|
|
{
|
|
__vector128 *vxrs = &state->vxrs[0];
|
|
int mask;
|
|
|
|
if (flags & KERNEL_FPC)
|
|
fpu_lfpc_safe(&state->fpc);
|
|
if (!cpu_has_vx()) {
|
|
if (flags & KERNEL_VXR_V0V7)
|
|
load_fp_regs_vx(state->vxrs);
|
|
return;
|
|
}
|
|
mask = flags & KERNEL_VXR;
|
|
if (mask == KERNEL_VXR) {
|
|
fpu_vlm(0, 15, &vxrs[0]);
|
|
fpu_vlm(16, 31, &vxrs[16]);
|
|
return;
|
|
}
|
|
if (mask == KERNEL_VXR_MID) {
|
|
fpu_vlm(8, 23, &vxrs[8]);
|
|
return;
|
|
}
|
|
mask = flags & KERNEL_VXR_LOW;
|
|
if (mask) {
|
|
if (mask == KERNEL_VXR_LOW)
|
|
fpu_vlm(0, 15, &vxrs[0]);
|
|
else if (mask == KERNEL_VXR_V0V7)
|
|
fpu_vlm(0, 7, &vxrs[0]);
|
|
else
|
|
fpu_vlm(8, 15, &vxrs[8]);
|
|
}
|
|
mask = flags & KERNEL_VXR_HIGH;
|
|
if (mask) {
|
|
if (mask == KERNEL_VXR_HIGH)
|
|
fpu_vlm(16, 31, &vxrs[16]);
|
|
else if (mask == KERNEL_VXR_V16V23)
|
|
fpu_vlm(16, 23, &vxrs[16]);
|
|
else
|
|
fpu_vlm(24, 31, &vxrs[24]);
|
|
}
|
|
}
|
|
|
|
void save_fpu_state(struct fpu *state, int flags)
|
|
{
|
|
__vector128 *vxrs = &state->vxrs[0];
|
|
int mask;
|
|
|
|
if (flags & KERNEL_FPC)
|
|
fpu_stfpc(&state->fpc);
|
|
if (!cpu_has_vx()) {
|
|
if (flags & KERNEL_VXR_LOW)
|
|
save_fp_regs_vx(state->vxrs);
|
|
return;
|
|
}
|
|
mask = flags & KERNEL_VXR;
|
|
if (mask == KERNEL_VXR) {
|
|
fpu_vstm(0, 15, &vxrs[0]);
|
|
fpu_vstm(16, 31, &vxrs[16]);
|
|
return;
|
|
}
|
|
if (mask == KERNEL_VXR_MID) {
|
|
fpu_vstm(8, 23, &vxrs[8]);
|
|
return;
|
|
}
|
|
mask = flags & KERNEL_VXR_LOW;
|
|
if (mask) {
|
|
if (mask == KERNEL_VXR_LOW)
|
|
fpu_vstm(0, 15, &vxrs[0]);
|
|
else if (mask == KERNEL_VXR_V0V7)
|
|
fpu_vstm(0, 7, &vxrs[0]);
|
|
else
|
|
fpu_vstm(8, 15, &vxrs[8]);
|
|
}
|
|
mask = flags & KERNEL_VXR_HIGH;
|
|
if (mask) {
|
|
if (mask == KERNEL_VXR_HIGH)
|
|
fpu_vstm(16, 31, &vxrs[16]);
|
|
else if (mask == KERNEL_VXR_V16V23)
|
|
fpu_vstm(16, 23, &vxrs[16]);
|
|
else
|
|
fpu_vstm(24, 31, &vxrs[24]);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(save_fpu_state);
|