diff options
author | Richard Henderson <rth@twiddle.net> | 2015-08-24 07:55:47 -0700 |
---|---|---|
committer | Richard Henderson <rth@twiddle.net> | 2015-09-15 07:45:34 -0700 |
commit | 0583b2332355dadb2d4681fe5a4eca882eb5b889 (patch) | |
tree | e0fa40969603242b560ba1630a9535ea7d75c4a5 /linux-user/main.c | |
parent | 03b217b168edfb45501146e98f60a6aea6bbb943 (diff) |
target-tilegx: Handle atomic instructions
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Richard Henderson <rth@twiddle.net>
Diffstat (limited to 'linux-user/main.c')
-rw-r--r-- | linux-user/main.c | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/linux-user/main.c b/linux-user/main.c index 4b579c9d23..782037da84 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -3436,6 +3436,148 @@ static void gen_sigill_reg(CPUTLGState *env) queue_signal(env, info.si_signo, &info); } +static void set_regval(CPUTLGState *env, uint8_t reg, uint64_t val) +{ + if (unlikely(reg >= TILEGX_R_COUNT)) { + switch (reg) { + case TILEGX_R_SN: + case TILEGX_R_ZERO: + return; + case TILEGX_R_IDN0: + case TILEGX_R_IDN1: + case TILEGX_R_UDN0: + case TILEGX_R_UDN1: + case TILEGX_R_UDN2: + case TILEGX_R_UDN3: + gen_sigill_reg(env); + return; + default: + g_assert_not_reached(); + } + } + env->regs[reg] = val; +} + +/* + * Compare the 8-byte contents of the CmpValue SPR with the 8-byte value in + * memory at the address held in the first source register. If the values are + * not equal, then no memory operation is performed. If the values are equal, + * the 8-byte quantity from the second source register is written into memory + * at the address held in the first source register. In either case, the result + * of the instruction is the value read from memory. The compare and write to + * memory are atomic and thus can be used for synchronization purposes. This + * instruction only operates for addresses aligned to a 8-byte boundary. + * Unaligned memory access causes an Unaligned Data Reference interrupt. + * + * Functional Description (64-bit) + * uint64_t memVal = memoryReadDoubleWord (rf[SrcA]); + * rf[Dest] = memVal; + * if (memVal == SPR[CmpValueSPR]) + * memoryWriteDoubleWord (rf[SrcA], rf[SrcB]); + * + * Functional Description (32-bit) + * uint64_t memVal = signExtend32 (memoryReadWord (rf[SrcA])); + * rf[Dest] = memVal; + * if (memVal == signExtend32 (SPR[CmpValueSPR])) + * memoryWriteWord (rf[SrcA], rf[SrcB]); + * + * + * This function also processes exch and exch4 which need not process SPR. + */ +static void do_exch(CPUTLGState *env, bool quad, bool cmp) +{ + target_ulong addr; + target_long val, sprval; + + start_exclusive(); + + addr = env->atomic_srca; + if (quad ? get_user_s64(val, addr) : get_user_s32(val, addr)) { + goto sigsegv_maperr; + } + + if (cmp) { + if (quad) { + sprval = env->spregs[TILEGX_SPR_CMPEXCH]; + } else { + sprval = sextract64(env->spregs[TILEGX_SPR_CMPEXCH], 0, 32); + } + } + + if (!cmp || val == sprval) { + target_long valb = env->atomic_srcb; + if (quad ? put_user_u64(valb, addr) : put_user_u32(valb, addr)) { + goto sigsegv_maperr; + } + } + + set_regval(env, env->atomic_dstr, val); + end_exclusive(); + return; + + sigsegv_maperr: + end_exclusive(); + gen_sigsegv_maperr(env, addr); +} + +static void do_fetch(CPUTLGState *env, int trapnr, bool quad) +{ + int8_t write = 1; + target_ulong addr; + target_long val, valb; + + start_exclusive(); + + addr = env->atomic_srca; + valb = env->atomic_srcb; + if (quad ? get_user_s64(val, addr) : get_user_s32(val, addr)) { + goto sigsegv_maperr; + } + + switch (trapnr) { + case TILEGX_EXCP_OPCODE_FETCHADD: + case TILEGX_EXCP_OPCODE_FETCHADD4: + valb += val; + break; + case TILEGX_EXCP_OPCODE_FETCHADDGEZ: + valb += val; + if (valb < 0) { + write = 0; + } + break; + case TILEGX_EXCP_OPCODE_FETCHADDGEZ4: + valb += val; + if ((int32_t)valb < 0) { + write = 0; + } + break; + case TILEGX_EXCP_OPCODE_FETCHAND: + case TILEGX_EXCP_OPCODE_FETCHAND4: + valb &= val; + break; + case TILEGX_EXCP_OPCODE_FETCHOR: + case TILEGX_EXCP_OPCODE_FETCHOR4: + valb |= val; + break; + default: + g_assert_not_reached(); + } + + if (write) { + if (quad ? put_user_u64(valb, addr) : put_user_u32(valb, addr)) { + goto sigsegv_maperr; + } + } + + set_regval(env, env->atomic_dstr, val); + end_exclusive(); + return; + + sigsegv_maperr: + end_exclusive(); + gen_sigsegv_maperr(env, addr); +} + void cpu_loop(CPUTLGState *env) { CPUState *cs = CPU(tilegx_env_get_cpu(env)); @@ -3456,6 +3598,30 @@ void cpu_loop(CPUTLGState *env) ? - env->regs[TILEGX_R_RE] : 0; break; + case TILEGX_EXCP_OPCODE_EXCH: + do_exch(env, true, false); + break; + case TILEGX_EXCP_OPCODE_EXCH4: + do_exch(env, false, false); + break; + case TILEGX_EXCP_OPCODE_CMPEXCH: + do_exch(env, true, true); + break; + case TILEGX_EXCP_OPCODE_CMPEXCH4: + do_exch(env, false, true); + break; + case TILEGX_EXCP_OPCODE_FETCHADD: + case TILEGX_EXCP_OPCODE_FETCHADDGEZ: + case TILEGX_EXCP_OPCODE_FETCHAND: + case TILEGX_EXCP_OPCODE_FETCHOR: + do_fetch(env, trapnr, true); + break; + case TILEGX_EXCP_OPCODE_FETCHADD4: + case TILEGX_EXCP_OPCODE_FETCHADDGEZ4: + case TILEGX_EXCP_OPCODE_FETCHAND4: + case TILEGX_EXCP_OPCODE_FETCHOR4: + do_fetch(env, trapnr, false); + break; case TILEGX_EXCP_REG_IDN_ACCESS: case TILEGX_EXCP_REG_UDN_ACCESS: gen_sigill_reg(env); |