@samitouri / QOSamiQemu / commits / 2762cd51ee

linux-user/s390x: restore fpu_status rounding mode from FPC on sigreturn

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> Signed-off-by: Helge Deller <deller@gmx.de>

Matt Turner committed May 26, 2026 at 11:05 UTC 2762cd51ee033dccb3167110376dd125244cc819
3 files changed +20 -7
linux-user/s390x/signal.c
+5 -1
@@ -332,7 +332,11 @@ static void restore_sigregs(CPUS390XState *env, target_sigregs *sc)
332 for (i = 0; i < 16; i++) {
333 __get_user(env->aregs[i], &sc->regs.acrs[i]);
334 }
335 - __get_user(env->fpc, &sc->fpregs.fpc);
335 + {
336 + uint32_t fpc;
337 + __get_user(fpc, &sc->fpregs.fpc);
338 + cpu_s390x_load_fpc(env, fpc);
339 + }
340 for (i = 0; i < 16; i++) {
341 __get_user(*get_freg(env, i), &sc->fpregs.fprs[i]);
342 }
target/s390x/cpu.h
+1
@@ -895,6 +895,7 @@ void s390_init_sigp(void);
895 /* helper.c */
896 void s390_cpu_set_psw(CPUS390XState *env, uint64_t mask, uint64_t addr);
897 uint64_t s390_cpu_get_psw_mask(CPUS390XState *env);
898 +void cpu_s390x_load_fpc(CPUS390XState *env, uint32_t fpc);
899
900 /* outside of target/s390x/ */
901 S390CPU *s390_cpu_addr2state(uint16_t cpu_addr);
target/s390x/tcg/fpu_helper.c
+14 -6
@@ -1087,6 +1087,19 @@ static const int fpc_to_rnd[8] = {
1087 float_round_to_odd,
1088 };
1089
1090 +void cpu_s390x_load_fpc(CPUS390XState *env, uint32_t fpc)
1091 +{
1092 + /*
1093 + * Mimic kernel fpu_lfpc_safe(): a corrupt signal frame value that would
1094 + * trigger a specification exception instead results in FPC being set to 0.
1095 + */
1096 + if (fpc_to_rnd[fpc & 0x7] == -1 || fpc & 0x03030088u) {
1097 + fpc = 0;
1098 + }
1099 + env->fpc = fpc;
1100 + set_float_rounding_mode(fpc_to_rnd[fpc & 0x7], &env->fpu_status);
1101 +}
1102 +
1103 /* set fpc */
1104 void HELPER(sfpc)(CPUS390XState *env, uint64_t fpc)
1105 {
@@ -1094,12 +1107,7 @@ void HELPER(sfpc)(CPUS390XState *env, uint64_t fpc)
1107 (!s390_has_feat(S390_FEAT_FLOATING_POINT_EXT) && fpc & 0x4)) {
1108 tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC());
1109 }
1097 -
1098 - /* Install everything in the main FPC. */
1099 - env->fpc = fpc;
1100 -
1101 - /* Install the rounding mode in the shadow fpu_status. */
1102 - set_float_rounding_mode(fpc_to_rnd[fpc & 0x7], &env->fpu_status);
1110 + cpu_s390x_load_fpc(env, fpc);
1111 }
1112
1113 /* set fpc and signal */