Executing fmovem.l fpstate, %fpcr/%fpsr results in incorrect values
being loaded into the fpu control registers.
When fpu control registers are moved to/from memory, they must follow a
strict order regardless of the addressing mode: FPCR is always at the
lowest address, FPSR at the next, and FPIAR at the highest address.
The current implementation in gen_op_fmove_fcr() processes the bitmask
incorrectly and uses the wrong loop direction for pre-decrement mode,
mapping the registers to the wrong memory addresses.
Fix this by correcting the loop iteration order and mask checking to
follow the architectural memory layout.
Fixes: ba62494483ab ("target-m68k: add FPCR and FPSR")
Reported-by: Andreas Schwab <[email protected]>
Closes: https://lore.kernel.org/qemu-devel/[email protected]
Signed-off-by: Kuan-Wei Chiu <[email protected]>
---
target/m68k/translate.c | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/target/m68k/translate.c b/target/m68k/translate.c
index 138c89d3e5..8d8531b1ad 100644
--- a/target/m68k/translate.c
+++ b/target/m68k/translate.c
@@ -4837,24 +4837,26 @@ static void gen_op_fmove_fcr(CPUM68KState *env,
DisasContext *s,
*/
if (is_write && mode == 4) {
- for (i = 2; i >= 0; i--, mask >>= 1) {
- if (mask & 1) {
+ for (i = 0; i < 3; i++) {
+ if (mask & (1 << i)) {
gen_qemu_store_fcr(s, addr, 1 << i);
- if (mask != 1) {
+ mask &= ~(1 << i);
+ if (mask != 0) {
tcg_gen_subi_i32(addr, addr, opsize_bytes(OS_LONG));
}
}
}
tcg_gen_mov_i32(AREG(insn, 0), addr);
} else {
- for (i = 0; i < 3; i++, mask >>= 1) {
- if (mask & 1) {
+ for (i = 2; i >= 0; i--) {
+ if (mask & (1 << i)) {
if (is_write) {
gen_qemu_store_fcr(s, addr, 1 << i);
} else {
gen_qemu_load_fcr(s, addr, 1 << i);
}
- if (mask != 1 || mode == 3) {
+ mask &= ~(1 << i);
+ if (mask != 0 || mode == 3) {
tcg_gen_addi_i32(addr, addr, opsize_bytes(OS_LONG));
}
}
--
2.54.0.563.g4f69b47b94-goog