aboutsummaryrefslogtreecommitdiff
path: root/linux-user/main.c
diff options
context:
space:
mode:
authorRichard Henderson <rth@twiddle.net>2015-08-24 07:55:47 -0700
committerRichard Henderson <rth@twiddle.net>2015-09-15 07:45:34 -0700
commit0583b2332355dadb2d4681fe5a4eca882eb5b889 (patch)
treee0fa40969603242b560ba1630a9535ea7d75c4a5 /linux-user/main.c
parent03b217b168edfb45501146e98f60a6aea6bbb943 (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.c166
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);