target-alpha: Fix load-locked/store-conditional.

Use an exception plus start_exclusive to implement the compare-and-swap.
This follows the example set by the MIPS and PPC ports.

Signed-off-by: Richard Henderson <rth@twiddle.net>
Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
This commit is contained in:
Richard Henderson 2010-04-07 15:42:26 -07:00 committed by Aurelien Jarno
parent 8aa3fa2038
commit 6910b8f66a
5 changed files with 193 additions and 79 deletions

View File

@ -2349,6 +2349,51 @@ void cpu_loop(CPUM68KState *env)
#endif /* TARGET_M68K */ #endif /* TARGET_M68K */
#ifdef TARGET_ALPHA #ifdef TARGET_ALPHA
static void do_store_exclusive(CPUAlphaState *env, int reg, int quad)
{
target_ulong addr, val, tmp;
target_siginfo_t info;
int ret = 0;
addr = env->lock_addr;
tmp = env->lock_st_addr;
env->lock_addr = -1;
env->lock_st_addr = 0;
start_exclusive();
mmap_lock();
if (addr == tmp) {
if (quad ? get_user_s64(val, addr) : get_user_s32(val, addr)) {
goto do_sigsegv;
}
if (val == env->lock_value) {
tmp = env->ir[reg];
if (quad ? put_user_u64(tmp, addr) : put_user_u32(tmp, addr)) {
goto do_sigsegv;
}
ret = 1;
}
}
env->ir[reg] = ret;
env->pc += 4;
mmap_unlock();
end_exclusive();
return;
do_sigsegv:
mmap_unlock();
end_exclusive();
info.si_signo = TARGET_SIGSEGV;
info.si_errno = 0;
info.si_code = TARGET_SEGV_MAPERR;
info._sifields._sigfault._addr = addr;
queue_signal(env, TARGET_SIGSEGV, &info);
}
void cpu_loop (CPUState *env) void cpu_loop (CPUState *env)
{ {
int trapnr; int trapnr;
@ -2373,6 +2418,7 @@ void cpu_loop (CPUState *env)
exit(1); exit(1);
break; break;
case EXCP_ARITH: case EXCP_ARITH:
env->lock_addr = -1;
info.si_signo = TARGET_SIGFPE; info.si_signo = TARGET_SIGFPE;
info.si_errno = 0; info.si_errno = 0;
info.si_code = TARGET_FPE_FLTINV; info.si_code = TARGET_FPE_FLTINV;
@ -2384,6 +2430,7 @@ void cpu_loop (CPUState *env)
exit(1); exit(1);
break; break;
case EXCP_DFAULT: case EXCP_DFAULT:
env->lock_addr = -1;
info.si_signo = TARGET_SIGSEGV; info.si_signo = TARGET_SIGSEGV;
info.si_errno = 0; info.si_errno = 0;
info.si_code = 0; /* ??? SEGV_MAPERR vs SEGV_ACCERR. */ info.si_code = 0; /* ??? SEGV_MAPERR vs SEGV_ACCERR. */
@ -2407,6 +2454,7 @@ void cpu_loop (CPUState *env)
exit(1); exit(1);
break; break;
case EXCP_UNALIGN: case EXCP_UNALIGN:
env->lock_addr = -1;
info.si_signo = TARGET_SIGBUS; info.si_signo = TARGET_SIGBUS;
info.si_errno = 0; info.si_errno = 0;
info.si_code = TARGET_BUS_ADRALN; info.si_code = TARGET_BUS_ADRALN;
@ -2415,6 +2463,7 @@ void cpu_loop (CPUState *env)
break; break;
case EXCP_OPCDEC: case EXCP_OPCDEC:
do_sigill: do_sigill:
env->lock_addr = -1;
info.si_signo = TARGET_SIGILL; info.si_signo = TARGET_SIGILL;
info.si_errno = 0; info.si_errno = 0;
info.si_code = TARGET_ILL_ILLOPC; info.si_code = TARGET_ILL_ILLOPC;
@ -2425,6 +2474,7 @@ void cpu_loop (CPUState *env)
/* No-op. Linux simply re-enables the FPU. */ /* No-op. Linux simply re-enables the FPU. */
break; break;
case EXCP_CALL_PAL ... (EXCP_CALL_PALP - 1): case EXCP_CALL_PAL ... (EXCP_CALL_PALP - 1):
env->lock_addr = -1;
switch ((trapnr >> 6) | 0x80) { switch ((trapnr >> 6) | 0x80) {
case 0x80: case 0x80:
/* BPT */ /* BPT */
@ -2514,11 +2564,16 @@ void cpu_loop (CPUState *env)
case EXCP_DEBUG: case EXCP_DEBUG:
info.si_signo = gdb_handlesig (env, TARGET_SIGTRAP); info.si_signo = gdb_handlesig (env, TARGET_SIGTRAP);
if (info.si_signo) { if (info.si_signo) {
env->lock_addr = -1;
info.si_errno = 0; info.si_errno = 0;
info.si_code = TARGET_TRAP_BRKPT; info.si_code = TARGET_TRAP_BRKPT;
queue_signal(env, info.si_signo, &info); queue_signal(env, info.si_signo, &info);
} }
break; break;
case EXCP_STL_C:
case EXCP_STQ_C:
do_store_exclusive(env, env->error_code, trapnr - EXCP_STL_C);
break;
default: default:
printf ("Unhandled trap: 0x%x\n", trapnr); printf ("Unhandled trap: 0x%x\n", trapnr);
cpu_dump_state(env, stderr, fprintf, 0); cpu_dump_state(env, stderr, fprintf, 0);

View File

@ -355,11 +355,13 @@ struct CPUAlphaState {
uint64_t ir[31]; uint64_t ir[31];
float64 fir[31]; float64 fir[31];
uint64_t pc; uint64_t pc;
uint64_t lock;
uint32_t pcc[2]; uint32_t pcc[2];
uint64_t ipr[IPR_LAST]; uint64_t ipr[IPR_LAST];
uint64_t ps; uint64_t ps;
uint64_t unique; uint64_t unique;
uint64_t lock_addr;
uint64_t lock_st_addr;
uint64_t lock_value;
float_status fp_status; float_status fp_status;
/* The following fields make up the FPCR, but in FP_STATUS format. */ /* The following fields make up the FPCR, but in FP_STATUS format. */
uint8_t fpcr_exc_status; uint8_t fpcr_exc_status;
@ -440,6 +442,8 @@ enum {
/* Pseudo exception for console */ /* Pseudo exception for console */
EXCP_CONSOLE_DISPATCH = 0x4001, EXCP_CONSOLE_DISPATCH = 0x4001,
EXCP_CONSOLE_FIXUP = 0x4002, EXCP_CONSOLE_FIXUP = 0x4002,
EXCP_STL_C = 0x4003,
EXCP_STQ_C = 0x4004,
}; };
/* Arithmetic exception */ /* Arithmetic exception */

View File

@ -557,12 +557,15 @@ void cpu_dump_state (CPUState *env, FILE *f,
if ((i % 3) == 2) if ((i % 3) == 2)
cpu_fprintf(f, "\n"); cpu_fprintf(f, "\n");
} }
cpu_fprintf(f, "\n");
cpu_fprintf(f, "lock_a " TARGET_FMT_lx " lock_v " TARGET_FMT_lx "\n",
env->lock_addr, env->lock_value);
for (i = 0; i < 31; i++) { for (i = 0; i < 31; i++) {
cpu_fprintf(f, "FIR%02d " TARGET_FMT_lx " ", i, cpu_fprintf(f, "FIR%02d " TARGET_FMT_lx " ", i,
*((uint64_t *)(&env->fir[i]))); *((uint64_t *)(&env->fir[i])));
if ((i % 3) == 2) if ((i % 3) == 2)
cpu_fprintf(f, "\n"); cpu_fprintf(f, "\n");
} }
cpu_fprintf(f, "\nlock " TARGET_FMT_lx "\n", env->lock); cpu_fprintf(f, "\n");
} }

View File

@ -1159,6 +1159,7 @@ void helper_hw_rei (void)
env->pc = env->ipr[IPR_EXC_ADDR] & ~3; env->pc = env->ipr[IPR_EXC_ADDR] & ~3;
env->ipr[IPR_EXC_ADDR] = env->ipr[IPR_EXC_ADDR] & 1; env->ipr[IPR_EXC_ADDR] = env->ipr[IPR_EXC_ADDR] & 1;
env->intr_flag = 0; env->intr_flag = 0;
env->lock_addr = -1;
/* XXX: re-enable interrupts and memory mapping */ /* XXX: re-enable interrupts and memory mapping */
} }
@ -1167,6 +1168,7 @@ void helper_hw_ret (uint64_t a)
env->pc = a & ~3; env->pc = a & ~3;
env->ipr[IPR_EXC_ADDR] = a & 1; env->ipr[IPR_EXC_ADDR] = a & 1;
env->intr_flag = 0; env->intr_flag = 0;
env->lock_addr = -1;
/* XXX: re-enable interrupts and memory mapping */ /* XXX: re-enable interrupts and memory mapping */
} }

View File

@ -86,7 +86,9 @@ static TCGv_ptr cpu_env;
static TCGv cpu_ir[31]; static TCGv cpu_ir[31];
static TCGv cpu_fir[31]; static TCGv cpu_fir[31];
static TCGv cpu_pc; static TCGv cpu_pc;
static TCGv cpu_lock; static TCGv cpu_lock_addr;
static TCGv cpu_lock_st_addr;
static TCGv cpu_lock_value;
#ifdef CONFIG_USER_ONLY #ifdef CONFIG_USER_ONLY
static TCGv cpu_uniq; static TCGv cpu_uniq;
#endif #endif
@ -123,8 +125,15 @@ static void alpha_translate_init(void)
cpu_pc = tcg_global_mem_new_i64(TCG_AREG0, cpu_pc = tcg_global_mem_new_i64(TCG_AREG0,
offsetof(CPUState, pc), "pc"); offsetof(CPUState, pc), "pc");
cpu_lock = tcg_global_mem_new_i64(TCG_AREG0, cpu_lock_addr = tcg_global_mem_new_i64(TCG_AREG0,
offsetof(CPUState, lock), "lock"); offsetof(CPUState, lock_addr),
"lock_addr");
cpu_lock_st_addr = tcg_global_mem_new_i64(TCG_AREG0,
offsetof(CPUState, lock_st_addr),
"lock_st_addr");
cpu_lock_value = tcg_global_mem_new_i64(TCG_AREG0,
offsetof(CPUState, lock_value),
"lock_value");
#ifdef CONFIG_USER_ONLY #ifdef CONFIG_USER_ONLY
cpu_uniq = tcg_global_mem_new_i64(TCG_AREG0, cpu_uniq = tcg_global_mem_new_i64(TCG_AREG0,
@ -189,14 +198,16 @@ static inline void gen_qemu_lds(TCGv t0, TCGv t1, int flags)
static inline void gen_qemu_ldl_l(TCGv t0, TCGv t1, int flags) static inline void gen_qemu_ldl_l(TCGv t0, TCGv t1, int flags)
{ {
tcg_gen_mov_i64(cpu_lock, t1);
tcg_gen_qemu_ld32s(t0, t1, flags); tcg_gen_qemu_ld32s(t0, t1, flags);
tcg_gen_mov_i64(cpu_lock_addr, t1);
tcg_gen_mov_i64(cpu_lock_value, t0);
} }
static inline void gen_qemu_ldq_l(TCGv t0, TCGv t1, int flags) static inline void gen_qemu_ldq_l(TCGv t0, TCGv t1, int flags)
{ {
tcg_gen_mov_i64(cpu_lock, t1);
tcg_gen_qemu_ld64(t0, t1, flags); tcg_gen_qemu_ld64(t0, t1, flags);
tcg_gen_mov_i64(cpu_lock_addr, t1);
tcg_gen_mov_i64(cpu_lock_value, t0);
} }
static inline void gen_load_mem(DisasContext *ctx, static inline void gen_load_mem(DisasContext *ctx,
@ -205,25 +216,31 @@ static inline void gen_load_mem(DisasContext *ctx,
int ra, int rb, int32_t disp16, int fp, int ra, int rb, int32_t disp16, int fp,
int clear) int clear)
{ {
TCGv addr; TCGv addr, va;
if (unlikely(ra == 31)) /* LDQ_U with ra $31 is UNOP. Other various loads are forms of
prefetches, which we can treat as nops. No worries about
missed exceptions here. */
if (unlikely(ra == 31)) {
return; return;
}
addr = tcg_temp_new(); addr = tcg_temp_new();
if (rb != 31) { if (rb != 31) {
tcg_gen_addi_i64(addr, cpu_ir[rb], disp16); tcg_gen_addi_i64(addr, cpu_ir[rb], disp16);
if (clear) if (clear) {
tcg_gen_andi_i64(addr, addr, ~0x7); tcg_gen_andi_i64(addr, addr, ~0x7);
}
} else { } else {
if (clear) if (clear) {
disp16 &= ~0x7; disp16 &= ~0x7;
}
tcg_gen_movi_i64(addr, disp16); tcg_gen_movi_i64(addr, disp16);
} }
if (fp)
tcg_gen_qemu_load(cpu_fir[ra], addr, ctx->mem_idx); va = (fp ? cpu_fir[ra] : cpu_ir[ra]);
else tcg_gen_qemu_load(va, addr, ctx->mem_idx);
tcg_gen_qemu_load(cpu_ir[ra], addr, ctx->mem_idx);
tcg_temp_free(addr); tcg_temp_free(addr);
} }
@ -257,73 +274,105 @@ static inline void gen_qemu_sts(TCGv t0, TCGv t1, int flags)
tcg_temp_free_i32(tmp32); tcg_temp_free_i32(tmp32);
} }
static inline void gen_qemu_stl_c(TCGv t0, TCGv t1, int flags)
{
int l1, l2;
l1 = gen_new_label();
l2 = gen_new_label();
tcg_gen_brcond_i64(TCG_COND_NE, cpu_lock, t1, l1);
tcg_gen_qemu_st32(t0, t1, flags);
tcg_gen_movi_i64(t0, 1);
tcg_gen_br(l2);
gen_set_label(l1);
tcg_gen_movi_i64(t0, 0);
gen_set_label(l2);
tcg_gen_movi_i64(cpu_lock, -1);
}
static inline void gen_qemu_stq_c(TCGv t0, TCGv t1, int flags)
{
int l1, l2;
l1 = gen_new_label();
l2 = gen_new_label();
tcg_gen_brcond_i64(TCG_COND_NE, cpu_lock, t1, l1);
tcg_gen_qemu_st64(t0, t1, flags);
tcg_gen_movi_i64(t0, 1);
tcg_gen_br(l2);
gen_set_label(l1);
tcg_gen_movi_i64(t0, 0);
gen_set_label(l2);
tcg_gen_movi_i64(cpu_lock, -1);
}
static inline void gen_store_mem(DisasContext *ctx, static inline void gen_store_mem(DisasContext *ctx,
void (*tcg_gen_qemu_store)(TCGv t0, TCGv t1, void (*tcg_gen_qemu_store)(TCGv t0, TCGv t1,
int flags), int flags),
int ra, int rb, int32_t disp16, int fp, int ra, int rb, int32_t disp16, int fp,
int clear, int local) int clear)
{ {
TCGv addr; TCGv addr, va;
if (local)
addr = tcg_temp_local_new(); addr = tcg_temp_new();
else
addr = tcg_temp_new();
if (rb != 31) { if (rb != 31) {
tcg_gen_addi_i64(addr, cpu_ir[rb], disp16); tcg_gen_addi_i64(addr, cpu_ir[rb], disp16);
if (clear) if (clear) {
tcg_gen_andi_i64(addr, addr, ~0x7); tcg_gen_andi_i64(addr, addr, ~0x7);
}
} else { } else {
if (clear) if (clear) {
disp16 &= ~0x7; disp16 &= ~0x7;
}
tcg_gen_movi_i64(addr, disp16); tcg_gen_movi_i64(addr, disp16);
} }
if (ra != 31) {
if (fp) if (ra == 31) {
tcg_gen_qemu_store(cpu_fir[ra], addr, ctx->mem_idx); va = tcg_const_i64(0);
else
tcg_gen_qemu_store(cpu_ir[ra], addr, ctx->mem_idx);
} else { } else {
TCGv zero; va = (fp ? cpu_fir[ra] : cpu_ir[ra]);
if (local)
zero = tcg_const_local_i64(0);
else
zero = tcg_const_i64(0);
tcg_gen_qemu_store(zero, addr, ctx->mem_idx);
tcg_temp_free(zero);
} }
tcg_gen_qemu_store(va, addr, ctx->mem_idx);
tcg_temp_free(addr); tcg_temp_free(addr);
if (ra == 31) {
tcg_temp_free(va);
}
}
static ExitStatus gen_store_conditional(DisasContext *ctx, int ra, int rb,
int32_t disp16, int quad)
{
TCGv addr;
if (ra == 31) {
/* ??? Don't bother storing anything. The user can't tell
the difference, since the zero register always reads zero. */
return NO_EXIT;
}
#if defined(CONFIG_USER_ONLY)
addr = cpu_lock_st_addr;
#else
addr = tcg_local_new();
#endif
if (rb != 31) {
tcg_gen_addi_i64(addr, cpu_ir[rb], disp16);
} else {
tcg_gen_movi_i64(addr, disp16);
}
#if defined(CONFIG_USER_ONLY)
/* ??? This is handled via a complicated version of compare-and-swap
in the cpu_loop. Hopefully one day we'll have a real CAS opcode
in TCG so that this isn't necessary. */
return gen_excp(ctx, quad ? EXCP_STQ_C : EXCP_STL_C, ra);
#else
/* ??? In system mode we are never multi-threaded, so CAS can be
implemented via a non-atomic load-compare-store sequence. */
{
int lab_fail, lab_done;
TCGv val;
lab_fail = gen_new_label();
lab_done = gen_new_label();
tcg_gen_brcond(TCG_COND_NE, addr, cpu_lock_addr, lab_fail);
val = tcg_temp_new();
if (quad) {
tcg_gen_qemu_ld64(val, addr, ctx->mem_idx);
} else {
tcg_gen_qemu_ld32s(val, addr, ctx->mem_idx);
}
tcg_gen_brcond(TCG_COND_NE, val, cpu_lock_value, lab_fail);
if (quad) {
tcg_gen_qemu_st64(cpu_ir[ra], addr, ctx->mem_idx);
} else {
tcg_gen_qemu_st32(cpu_ir[ra], addr, ctx->mem_idx);
}
tcg_gen_movi_i64(cpu_ir[ra], 1);
tcg_gen_br(lab_done);
gen_set_label(lab_fail);
tcg_gen_movi_i64(cpu_ir[ra], 0);
gen_set_label(lab_done);
tcg_gen_movi_i64(cpu_lock_addr, -1);
tcg_temp_free(addr);
return NO_EXIT;
}
#endif
} }
static int use_goto_tb(DisasContext *ctx, uint64_t dest) static int use_goto_tb(DisasContext *ctx, uint64_t dest)
@ -1533,15 +1582,15 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn)
break; break;
case 0x0D: case 0x0D:
/* STW */ /* STW */
gen_store_mem(ctx, &tcg_gen_qemu_st16, ra, rb, disp16, 0, 0, 0); gen_store_mem(ctx, &tcg_gen_qemu_st16, ra, rb, disp16, 0, 0);
break; break;
case 0x0E: case 0x0E:
/* STB */ /* STB */
gen_store_mem(ctx, &tcg_gen_qemu_st8, ra, rb, disp16, 0, 0, 0); gen_store_mem(ctx, &tcg_gen_qemu_st8, ra, rb, disp16, 0, 0);
break; break;
case 0x0F: case 0x0F:
/* STQ_U */ /* STQ_U */
gen_store_mem(ctx, &tcg_gen_qemu_st64, ra, rb, disp16, 0, 1, 0); gen_store_mem(ctx, &tcg_gen_qemu_st64, ra, rb, disp16, 0, 1);
break; break;
case 0x10: case 0x10:
switch (fn7) { switch (fn7) {
@ -2974,19 +3023,19 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn)
break; break;
case 0x24: case 0x24:
/* STF */ /* STF */
gen_store_mem(ctx, &gen_qemu_stf, ra, rb, disp16, 1, 0, 0); gen_store_mem(ctx, &gen_qemu_stf, ra, rb, disp16, 1, 0);
break; break;
case 0x25: case 0x25:
/* STG */ /* STG */
gen_store_mem(ctx, &gen_qemu_stg, ra, rb, disp16, 1, 0, 0); gen_store_mem(ctx, &gen_qemu_stg, ra, rb, disp16, 1, 0);
break; break;
case 0x26: case 0x26:
/* STS */ /* STS */
gen_store_mem(ctx, &gen_qemu_sts, ra, rb, disp16, 1, 0, 0); gen_store_mem(ctx, &gen_qemu_sts, ra, rb, disp16, 1, 0);
break; break;
case 0x27: case 0x27:
/* STT */ /* STT */
gen_store_mem(ctx, &tcg_gen_qemu_st64, ra, rb, disp16, 1, 0, 0); gen_store_mem(ctx, &tcg_gen_qemu_st64, ra, rb, disp16, 1, 0);
break; break;
case 0x28: case 0x28:
/* LDL */ /* LDL */
@ -3006,19 +3055,19 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn)
break; break;
case 0x2C: case 0x2C:
/* STL */ /* STL */
gen_store_mem(ctx, &tcg_gen_qemu_st32, ra, rb, disp16, 0, 0, 0); gen_store_mem(ctx, &tcg_gen_qemu_st32, ra, rb, disp16, 0, 0);
break; break;
case 0x2D: case 0x2D:
/* STQ */ /* STQ */
gen_store_mem(ctx, &tcg_gen_qemu_st64, ra, rb, disp16, 0, 0, 0); gen_store_mem(ctx, &tcg_gen_qemu_st64, ra, rb, disp16, 0, 0);
break; break;
case 0x2E: case 0x2E:
/* STL_C */ /* STL_C */
gen_store_mem(ctx, &gen_qemu_stl_c, ra, rb, disp16, 0, 0, 1); ret = gen_store_conditional(ctx, ra, rb, disp16, 0);
break; break;
case 0x2F: case 0x2F:
/* STQ_C */ /* STQ_C */
gen_store_mem(ctx, &gen_qemu_stq_c, ra, rb, disp16, 0, 0, 1); ret = gen_store_conditional(ctx, ra, rb, disp16, 1);
break; break;
case 0x30: case 0x30:
/* BR */ /* BR */
@ -3284,6 +3333,7 @@ CPUAlphaState * cpu_alpha_init (const char *cpu_model)
#else #else
pal_init(env); pal_init(env);
#endif #endif
env->lock_addr = -1;
/* Initialize IPR */ /* Initialize IPR */
#if defined (CONFIG_USER_ONLY) #if defined (CONFIG_USER_ONLY)