aboutsummaryrefslogtreecommitdiff
path: root/target-i386
diff options
context:
space:
mode:
authorAndreas Gustafsson <gson@gson.org>2011-12-12 00:46:32 +0400
committermalc <av1474@comtv.ru>2011-12-12 00:46:32 +0400
commitf7e80adf3cc4b99b8230e6474d0518363773915f (patch)
treebfc311ea3ac0a4d42f68e00833197fbb22c3d6d4 /target-i386
parent217bfb445b54db618a30f3a39170bebd9fd9dbf2 (diff)
target-i386: fix cmpxchg instruction emulation
When the i386 cmpxchg instruction is executed with a memory operand and the comparison result is "unequal", do the memory write before changing the accumulator instead of the other way around, because otherwise the new accumulator value will incorrectly be used in the comparison when the instruction is restarted after a page fault. This bug was originally reported on 2010-04-25 as https://bugs.launchpad.net/qemu/+bug/569760 Signed-off-by: Andreas Gustafsson <gson@gson.org>
Diffstat (limited to 'target-i386')
-rw-r--r--target-i386/translate.c11
1 files changed, 7 insertions, 4 deletions
diff --git a/target-i386/translate.c b/target-i386/translate.c
index 1ef8d16ac7..8321bf39a5 100644
--- a/target-i386/translate.c
+++ b/target-i386/translate.c
@@ -4870,20 +4870,23 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start)
tcg_gen_sub_tl(t2, cpu_regs[R_EAX], t0);
gen_extu(ot, t2);
tcg_gen_brcondi_tl(TCG_COND_EQ, t2, 0, label1);
+ label2 = gen_new_label();
if (mod == 3) {
- label2 = gen_new_label();
gen_op_mov_reg_v(ot, R_EAX, t0);
tcg_gen_br(label2);
gen_set_label(label1);
gen_op_mov_reg_v(ot, rm, t1);
- gen_set_label(label2);
} else {
- tcg_gen_mov_tl(t1, t0);
+ /* perform no-op store cycle like physical cpu; must be
+ before changing accumulator to ensure idempotency if
+ the store faults and the instruction is restarted */
+ gen_op_st_v(ot + s->mem_index, t0, a0);
gen_op_mov_reg_v(ot, R_EAX, t0);
+ tcg_gen_br(label2);
gen_set_label(label1);
- /* always store */
gen_op_st_v(ot + s->mem_index, t1, a0);
}
+ gen_set_label(label2);
tcg_gen_mov_tl(cpu_cc_src, t0);
tcg_gen_mov_tl(cpu_cc_dst, t2);
s->cc_op = CC_OP_SUBB + ot;