linux-user/s390x/signal.c | 6 +++++- target/s390x/cpu.h | 1 + target/s390x/tcg/fpu_helper.c | 20 ++++++++++++++------ 3 files changed, 20 insertions(+), 7 deletions(-)
QEMU keeps the s390x floating-point control register (FPC) in env->fpc.
The rounding mode bits [2:0] of FPC are reflected into the derived
env->fpu_status via set_float_rounding_mode(); every architectural
write to FPC goes through HELPER(sfpc) which keeps the two in sync.
restore_sigregs() restored FPC with a direct assignment:
__get_user(env->fpc, &sc->fpregs.fpc);
This wrote env->fpc correctly but never updated env->fpu_status, so on
sigreturn the interrupted code resumed with whatever rounding mode the
signal handler last installed in fpu_status.
Factor the two-step "write fpc + sync fpu_status" logic out of
HELPER(sfpc) into cpu_s390x_load_fpc(), declare it in cpu.h, and call
it from restore_sigregs() in place of the direct assignment.
cpu_s390x_load_fpc() partially reuses the sanity check from
HELPER(sfpc): if the FPC value has an invalid rounding mode or reserved
bits set, it falls back to 0, matching the kernel's fpu_lfpc_safe()
behavior where a corrupt signal frame value causes a specification
exception and 0 is used instead.
HELPER(sfpc) now calls cpu_s390x_load_fpc() after its full
specification-exception check, including the FEAT_FLOATING_POINT_EXT
test that is not needed for the signal restore path.
Fixes: 2941e0fa05 ("linux-user/s390x: Save/restore fpc when handling a signal")
Cc: qemu-stable@nongnu.org
Signed-off-by: Matt Turner <mattst88@gmail.com>
---
linux-user/s390x/signal.c | 6 +++++-
target/s390x/cpu.h | 1 +
target/s390x/tcg/fpu_helper.c | 20 ++++++++++++++------
3 files changed, 20 insertions(+), 7 deletions(-)
diff --git ./linux-user/s390x/signal.c ./linux-user/s390x/signal.c
index 96d1c8d11c..28ad80bde4 100644
--- ./linux-user/s390x/signal.c
+++ ./linux-user/s390x/signal.c
@@ -332,7 +332,11 @@ static void restore_sigregs(CPUS390XState *env, target_sigregs *sc)
for (i = 0; i < 16; i++) {
__get_user(env->aregs[i], &sc->regs.acrs[i]);
}
- __get_user(env->fpc, &sc->fpregs.fpc);
+ {
+ uint32_t fpc;
+ __get_user(fpc, &sc->fpregs.fpc);
+ cpu_s390x_load_fpc(env, fpc);
+ }
for (i = 0; i < 16; i++) {
__get_user(*get_freg(env, i), &sc->fpregs.fprs[i]);
}
diff --git ./target/s390x/cpu.h ./target/s390x/cpu.h
index 3acbe83f0f..f55b79ef8a 100644
--- ./target/s390x/cpu.h
+++ ./target/s390x/cpu.h
@@ -895,6 +895,7 @@ void s390_init_sigp(void);
/* helper.c */
void s390_cpu_set_psw(CPUS390XState *env, uint64_t mask, uint64_t addr);
uint64_t s390_cpu_get_psw_mask(CPUS390XState *env);
+void cpu_s390x_load_fpc(CPUS390XState *env, uint32_t fpc);
/* outside of target/s390x/ */
S390CPU *s390_cpu_addr2state(uint16_t cpu_addr);
diff --git ./target/s390x/tcg/fpu_helper.c ./target/s390x/tcg/fpu_helper.c
index 6ca0b7162b..107025e675 100644
--- ./target/s390x/tcg/fpu_helper.c
+++ ./target/s390x/tcg/fpu_helper.c
@@ -1087,6 +1087,19 @@ static const int fpc_to_rnd[8] = {
float_round_to_odd,
};
+void cpu_s390x_load_fpc(CPUS390XState *env, uint32_t fpc)
+{
+ /*
+ * Mimic kernel fpu_lfpc_safe(): a corrupt signal frame value that would
+ * trigger a specification exception instead results in FPC being set to 0.
+ */
+ if (fpc_to_rnd[fpc & 0x7] == -1 || fpc & 0x03030088u) {
+ fpc = 0;
+ }
+ env->fpc = fpc;
+ set_float_rounding_mode(fpc_to_rnd[fpc & 0x7], &env->fpu_status);
+}
+
/* set fpc */
void HELPER(sfpc)(CPUS390XState *env, uint64_t fpc)
{
@@ -1094,12 +1107,7 @@ void HELPER(sfpc)(CPUS390XState *env, uint64_t fpc)
(!s390_has_feat(S390_FEAT_FLOATING_POINT_EXT) && fpc & 0x4)) {
tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC());
}
-
- /* Install everything in the main FPC. */
- env->fpc = fpc;
-
- /* Install the rounding mode in the shadow fpu_status. */
- set_float_rounding_mode(fpc_to_rnd[fpc & 0x7], &env->fpu_status);
+ cpu_s390x_load_fpc(env, fpc);
}
/* set fpc and signal */
--
2.53.0
On 5/26/26 17:05, Matt Turner wrote:
> QEMU keeps the s390x floating-point control register (FPC) in env->fpc.
> The rounding mode bits [2:0] of FPC are reflected into the derived
> env->fpu_status via set_float_rounding_mode(); every architectural
> write to FPC goes through HELPER(sfpc) which keeps the two in sync.
>
> restore_sigregs() restored FPC with a direct assignment:
>
> __get_user(env->fpc, &sc->fpregs.fpc);
>
> This wrote env->fpc correctly but never updated env->fpu_status, so on
> sigreturn the interrupted code resumed with whatever rounding mode the
> signal handler last installed in fpu_status.
>
> Factor the two-step "write fpc + sync fpu_status" logic out of
> HELPER(sfpc) into cpu_s390x_load_fpc(), declare it in cpu.h, and call
> it from restore_sigregs() in place of the direct assignment.
>
> cpu_s390x_load_fpc() partially reuses the sanity check from
> HELPER(sfpc): if the FPC value has an invalid rounding mode or reserved
> bits set, it falls back to 0, matching the kernel's fpu_lfpc_safe()
> behavior where a corrupt signal frame value causes a specification
> exception and 0 is used instead.
>
> HELPER(sfpc) now calls cpu_s390x_load_fpc() after its full
> specification-exception check, including the FEAT_FLOATING_POINT_EXT
> test that is not needed for the signal restore path.
>
> Fixes: 2941e0fa05 ("linux-user/s390x: Save/restore fpc when handling a signal")
> Cc: qemu-stable@nongnu.org
> Signed-off-by: Matt Turner <mattst88@gmail.com>
> ---
> linux-user/s390x/signal.c | 6 +++++-
> target/s390x/cpu.h | 1 +
> target/s390x/tcg/fpu_helper.c | 20 ++++++++++++++------
> 3 files changed, 20 insertions(+), 7 deletions(-)
>
> diff --git ./linux-user/s390x/signal.c ./linux-user/s390x/signal.c
> index 96d1c8d11c..28ad80bde4 100644
> --- ./linux-user/s390x/signal.c
> +++ ./linux-user/s390x/signal.c
> @@ -332,7 +332,11 @@ static void restore_sigregs(CPUS390XState *env, target_sigregs *sc)
> for (i = 0; i < 16; i++) {
> __get_user(env->aregs[i], &sc->regs.acrs[i]);
> }
> - __get_user(env->fpc, &sc->fpregs.fpc);
> + {
> + uint32_t fpc;
to avoid putting this block into braces, can't fpc be moved some lines above,
where the other variables are declared?
> + __get_user(fpc, &sc->fpregs.fpc);
> + cpu_s390x_load_fpc(env, fpc);
> + }
> for (i = 0; i < 16; i++) {
> __get_user(*get_freg(env, i), &sc->fpregs.fprs[i]);
> }
> diff --git ./target/s390x/cpu.h ./target/s390x/cpu.h
> index 3acbe83f0f..f55b79ef8a 100644
> --- ./target/s390x/cpu.h
> +++ ./target/s390x/cpu.h
> @@ -895,6 +895,7 @@ void s390_init_sigp(void);
> /* helper.c */
> void s390_cpu_set_psw(CPUS390XState *env, uint64_t mask, uint64_t addr);
> uint64_t s390_cpu_get_psw_mask(CPUS390XState *env);
> +void cpu_s390x_load_fpc(CPUS390XState *env, uint32_t fpc);
>
> /* outside of target/s390x/ */
> S390CPU *s390_cpu_addr2state(uint16_t cpu_addr);
> diff --git ./target/s390x/tcg/fpu_helper.c ./target/s390x/tcg/fpu_helper.c
> index 6ca0b7162b..107025e675 100644
> --- ./target/s390x/tcg/fpu_helper.c
> +++ ./target/s390x/tcg/fpu_helper.c
> @@ -1087,6 +1087,19 @@ static const int fpc_to_rnd[8] = {
> float_round_to_odd,
> };
>
> +void cpu_s390x_load_fpc(CPUS390XState *env, uint32_t fpc)
> +{
> + /*
> + * Mimic kernel fpu_lfpc_safe(): a corrupt signal frame value that would
> + * trigger a specification exception instead results in FPC being set to 0.
> + */
> + if (fpc_to_rnd[fpc & 0x7] == -1 || fpc & 0x03030088u) {
> + fpc = 0;
> + }
> + env->fpc = fpc;
> + set_float_rounding_mode(fpc_to_rnd[fpc & 0x7], &env->fpu_status);
> +}
> +
> /* set fpc */
> void HELPER(sfpc)(CPUS390XState *env, uint64_t fpc)
> {
> @@ -1094,12 +1107,7 @@ void HELPER(sfpc)(CPUS390XState *env, uint64_t fpc)
> (!s390_has_feat(S390_FEAT_FLOATING_POINT_EXT) && fpc & 0x4)) {
> tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC());
> }
> -
> - /* Install everything in the main FPC. */
> - env->fpc = fpc;
> -
> - /* Install the rounding mode in the shadow fpu_status. */
> - set_float_rounding_mode(fpc_to_rnd[fpc & 0x7], &env->fpu_status);
> + cpu_s390x_load_fpc(env, fpc);
> }
>
> /* set fpc and signal */
On 5/26/26 17:10, Helge Deller wrote:
> On 5/26/26 17:05, Matt Turner wrote:
>> QEMU keeps the s390x floating-point control register (FPC) in env->fpc.
>> The rounding mode bits [2:0] of FPC are reflected into the derived
>> env->fpu_status via set_float_rounding_mode(); every architectural
>> write to FPC goes through HELPER(sfpc) which keeps the two in sync.
>>
>> restore_sigregs() restored FPC with a direct assignment:
>>
>> __get_user(env->fpc, &sc->fpregs.fpc);
>>
>> This wrote env->fpc correctly but never updated env->fpu_status, so on
>> sigreturn the interrupted code resumed with whatever rounding mode the
>> signal handler last installed in fpu_status.
>>
>> Factor the two-step "write fpc + sync fpu_status" logic out of
>> HELPER(sfpc) into cpu_s390x_load_fpc(), declare it in cpu.h, and call
>> it from restore_sigregs() in place of the direct assignment.
>>
>> cpu_s390x_load_fpc() partially reuses the sanity check from
>> HELPER(sfpc): if the FPC value has an invalid rounding mode or reserved
>> bits set, it falls back to 0, matching the kernel's fpu_lfpc_safe()
>> behavior where a corrupt signal frame value causes a specification
>> exception and 0 is used instead.
>>
>> HELPER(sfpc) now calls cpu_s390x_load_fpc() after its full
>> specification-exception check, including the FEAT_FLOATING_POINT_EXT
>> test that is not needed for the signal restore path.
>>
>> Fixes: 2941e0fa05 ("linux-user/s390x: Save/restore fpc when handling a signal")
>> Cc: qemu-stable@nongnu.org
>> Signed-off-by: Matt Turner <mattst88@gmail.com>
>> ---
>> linux-user/s390x/signal.c | 6 +++++-
>> target/s390x/cpu.h | 1 +
>> target/s390x/tcg/fpu_helper.c | 20 ++++++++++++++------
>> 3 files changed, 20 insertions(+), 7 deletions(-)
>>
>> diff --git ./linux-user/s390x/signal.c ./linux-user/s390x/signal.c
>> index 96d1c8d11c..28ad80bde4 100644
>> --- ./linux-user/s390x/signal.c
>> +++ ./linux-user/s390x/signal.c
>> @@ -332,7 +332,11 @@ static void restore_sigregs(CPUS390XState *env, target_sigregs *sc)
>> for (i = 0; i < 16; i++) {
>> __get_user(env->aregs[i], &sc->regs.acrs[i]);
>> }
>> - __get_user(env->fpc, &sc->fpregs.fpc);
>> + {
>> + uint32_t fpc;
>
> to avoid putting this block into braces, can't fpc be moved some lines above,
> where the other variables are declared?
Forget this. ^^^
I see it's used like this in various places across architectures, so
let's keep it like you have in this patch here.
Helge
© 2016 - 2026 Red Hat, Inc.