aboutsummaryrefslogtreecommitdiff
path: root/target/s390x/translate.c
diff options
context:
space:
mode:
Diffstat (limited to 'target/s390x/translate.c')
-rw-r--r--target/s390x/translate.c245
1 files changed, 140 insertions, 105 deletions
diff --git a/target/s390x/translate.c b/target/s390x/translate.c
index 01c62176bf..4c48c593cd 100644
--- a/target/s390x/translate.c
+++ b/target/s390x/translate.c
@@ -1194,6 +1194,7 @@ typedef enum DisasFacility {
FAC_SCF, /* store clock fast */
FAC_SFLE, /* store facility list extended */
FAC_ILA, /* interlocked access facility 1 */
+ FAC_LPP, /* load-program-parameter */
} DisasFacility;
struct DisasInsn {
@@ -1517,6 +1518,21 @@ static ExitStatus op_bc(DisasContext *s, DisasOps *o)
int imm = is_imm ? get_field(s->fields, i2) : 0;
DisasCompare c;
+ /* BCR with R2 = 0 causes no branching */
+ if (have_field(s->fields, r2) && get_field(s->fields, r2) == 0) {
+ if (m1 == 14) {
+ /* Perform serialization */
+ /* FIXME: check for fast-BCR-serialization facility */
+ tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC);
+ }
+ if (m1 == 15) {
+ /* Perform serialization */
+ /* FIXME: perform checkpoint-synchronisation */
+ tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC);
+ }
+ return NO_EXIT;
+ }
+
disas_jcc(s, &c, m1);
return help_branch(s, &c, is_imm, imm, o->in2);
}
@@ -1942,102 +1958,47 @@ static ExitStatus op_cps(DisasContext *s, DisasOps *o)
static ExitStatus op_cs(DisasContext *s, DisasOps *o)
{
- /* FIXME: needs an atomic solution for CONFIG_USER_ONLY. */
int d2 = get_field(s->fields, d2);
int b2 = get_field(s->fields, b2);
- int is_64 = s->insn->data;
- TCGv_i64 addr, mem, cc, z;
+ TCGv_i64 addr, cc;
/* Note that in1 = R3 (new value) and
in2 = (zero-extended) R1 (expected value). */
- /* Load the memory into the (temporary) output. While the PoO only talks
- about moving the memory to R1 on inequality, if we include equality it
- means that R1 is equal to the memory in all conditions. */
addr = get_address(s, 0, b2, d2);
- if (is_64) {
- tcg_gen_qemu_ld64(o->out, addr, get_mem_index(s));
- } else {
- tcg_gen_qemu_ld32u(o->out, addr, get_mem_index(s));
- }
+ tcg_gen_atomic_cmpxchg_i64(o->out, addr, o->in2, o->in1,
+ get_mem_index(s), s->insn->data | MO_ALIGN);
+ tcg_temp_free_i64(addr);
/* Are the memory and expected values (un)equal? Note that this setcond
produces the output CC value, thus the NE sense of the test. */
cc = tcg_temp_new_i64();
tcg_gen_setcond_i64(TCG_COND_NE, cc, o->in2, o->out);
-
- /* If the memory and expected values are equal (CC==0), copy R3 to MEM.
- Recall that we are allowed to unconditionally issue the store (and
- thus any possible write trap), so (re-)store the original contents
- of MEM in case of inequality. */
- z = tcg_const_i64(0);
- mem = tcg_temp_new_i64();
- tcg_gen_movcond_i64(TCG_COND_EQ, mem, cc, z, o->in1, o->out);
- if (is_64) {
- tcg_gen_qemu_st64(mem, addr, get_mem_index(s));
- } else {
- tcg_gen_qemu_st32(mem, addr, get_mem_index(s));
- }
- tcg_temp_free_i64(z);
- tcg_temp_free_i64(mem);
- tcg_temp_free_i64(addr);
-
- /* Store CC back to cc_op. Wait until after the store so that any
- exception gets the old cc_op value. */
tcg_gen_extrl_i64_i32(cc_op, cc);
tcg_temp_free_i64(cc);
set_cc_static(s);
+
return NO_EXIT;
}
static ExitStatus op_cdsg(DisasContext *s, DisasOps *o)
{
- /* FIXME: needs an atomic solution for CONFIG_USER_ONLY. */
int r1 = get_field(s->fields, r1);
int r3 = get_field(s->fields, r3);
int d2 = get_field(s->fields, d2);
int b2 = get_field(s->fields, b2);
- TCGv_i64 addrh, addrl, memh, meml, outh, outl, cc, z;
+ TCGv_i64 addr;
+ TCGv_i32 t_r1, t_r3;
/* Note that R1:R1+1 = expected value and R3:R3+1 = new value. */
+ addr = get_address(s, 0, b2, d2);
+ t_r1 = tcg_const_i32(r1);
+ t_r3 = tcg_const_i32(r3);
+ gen_helper_cdsg(cpu_env, addr, t_r1, t_r3);
+ tcg_temp_free_i64(addr);
+ tcg_temp_free_i32(t_r1);
+ tcg_temp_free_i32(t_r3);
- addrh = get_address(s, 0, b2, d2);
- addrl = get_address(s, 0, b2, d2 + 8);
- outh = tcg_temp_new_i64();
- outl = tcg_temp_new_i64();
-
- tcg_gen_qemu_ld64(outh, addrh, get_mem_index(s));
- tcg_gen_qemu_ld64(outl, addrl, get_mem_index(s));
-
- /* Fold the double-word compare with arithmetic. */
- cc = tcg_temp_new_i64();
- z = tcg_temp_new_i64();
- tcg_gen_xor_i64(cc, outh, regs[r1]);
- tcg_gen_xor_i64(z, outl, regs[r1 + 1]);
- tcg_gen_or_i64(cc, cc, z);
- tcg_gen_movi_i64(z, 0);
- tcg_gen_setcond_i64(TCG_COND_NE, cc, cc, z);
-
- memh = tcg_temp_new_i64();
- meml = tcg_temp_new_i64();
- tcg_gen_movcond_i64(TCG_COND_EQ, memh, cc, z, regs[r3], outh);
- tcg_gen_movcond_i64(TCG_COND_EQ, meml, cc, z, regs[r3 + 1], outl);
- tcg_temp_free_i64(z);
-
- tcg_gen_qemu_st64(memh, addrh, get_mem_index(s));
- tcg_gen_qemu_st64(meml, addrl, get_mem_index(s));
- tcg_temp_free_i64(memh);
- tcg_temp_free_i64(meml);
- tcg_temp_free_i64(addrh);
- tcg_temp_free_i64(addrl);
-
- /* Save back state now that we've passed all exceptions. */
- tcg_gen_mov_i64(regs[r1], outh);
- tcg_gen_mov_i64(regs[r1 + 1], outl);
- tcg_gen_extrl_i64_i32(cc_op, cc);
- tcg_temp_free_i64(outh);
- tcg_temp_free_i64(outl);
- tcg_temp_free_i64(cc);
set_cc_static(s);
return NO_EXIT;
}
@@ -2363,6 +2324,50 @@ static ExitStatus op_iske(DisasContext *s, DisasOps *o)
}
#endif
+static ExitStatus op_laa(DisasContext *s, DisasOps *o)
+{
+ /* The real output is indeed the original value in memory;
+ recompute the addition for the computation of CC. */
+ tcg_gen_atomic_fetch_add_i64(o->in2, o->in2, o->in1, get_mem_index(s),
+ s->insn->data | MO_ALIGN);
+ /* However, we need to recompute the addition for setting CC. */
+ tcg_gen_add_i64(o->out, o->in1, o->in2);
+ return NO_EXIT;
+}
+
+static ExitStatus op_lan(DisasContext *s, DisasOps *o)
+{
+ /* The real output is indeed the original value in memory;
+ recompute the addition for the computation of CC. */
+ tcg_gen_atomic_fetch_and_i64(o->in2, o->in2, o->in1, get_mem_index(s),
+ s->insn->data | MO_ALIGN);
+ /* However, we need to recompute the operation for setting CC. */
+ tcg_gen_and_i64(o->out, o->in1, o->in2);
+ return NO_EXIT;
+}
+
+static ExitStatus op_lao(DisasContext *s, DisasOps *o)
+{
+ /* The real output is indeed the original value in memory;
+ recompute the addition for the computation of CC. */
+ tcg_gen_atomic_fetch_or_i64(o->in2, o->in2, o->in1, get_mem_index(s),
+ s->insn->data | MO_ALIGN);
+ /* However, we need to recompute the operation for setting CC. */
+ tcg_gen_or_i64(o->out, o->in1, o->in2);
+ return NO_EXIT;
+}
+
+static ExitStatus op_lax(DisasContext *s, DisasOps *o)
+{
+ /* The real output is indeed the original value in memory;
+ recompute the addition for the computation of CC. */
+ tcg_gen_atomic_fetch_xor_i64(o->in2, o->in2, o->in1, get_mem_index(s),
+ s->insn->data | MO_ALIGN);
+ /* However, we need to recompute the operation for setting CC. */
+ tcg_gen_xor_i64(o->out, o->in1, o->in2);
+ return NO_EXIT;
+}
+
static ExitStatus op_ldeb(DisasContext *s, DisasOps *o)
{
gen_helper_ldeb(o->out, cpu_env, o->in2);
@@ -2558,6 +2563,7 @@ static ExitStatus op_lctlg(DisasContext *s, DisasOps *o)
tcg_temp_free_i32(r3);
return NO_EXIT;
}
+
static ExitStatus op_lra(DisasContext *s, DisasOps *o)
{
check_privileged(s);
@@ -2567,6 +2573,14 @@ static ExitStatus op_lra(DisasContext *s, DisasOps *o)
return NO_EXIT;
}
+static ExitStatus op_lpp(DisasContext *s, DisasOps *o)
+{
+ check_privileged(s);
+
+ tcg_gen_st_i64(o->in2, cpu_env, offsetof(CPUS390XState, pp));
+ return NO_EXIT;
+}
+
static ExitStatus op_lpsw(DisasContext *s, DisasOps *o)
{
TCGv_i64 t1, t2;
@@ -2750,6 +2764,31 @@ static ExitStatus op_lm64(DisasContext *s, DisasOps *o)
return NO_EXIT;
}
+static ExitStatus op_lpd(DisasContext *s, DisasOps *o)
+{
+ TCGv_i64 a1, a2;
+ TCGMemOp mop = s->insn->data;
+
+ /* In a parallel context, stop the world and single step. */
+ if (parallel_cpus) {
+ potential_page_fault(s);
+ gen_exception(EXCP_ATOMIC);
+ return EXIT_NORETURN;
+ }
+
+ /* In a serial context, perform the two loads ... */
+ a1 = get_address(s, 0, get_field(s->fields, b1), get_field(s->fields, d1));
+ a2 = get_address(s, 0, get_field(s->fields, b2), get_field(s->fields, d2));
+ tcg_gen_qemu_ld_i64(o->out, a1, get_mem_index(s), mop | MO_ALIGN);
+ tcg_gen_qemu_ld_i64(o->out2, a2, get_mem_index(s), mop | MO_ALIGN);
+ tcg_temp_free_i64(a1);
+ tcg_temp_free_i64(a2);
+
+ /* ... and indicate that we performed them while interlocked. */
+ gen_op_movi_cc(s, 0);
+ return NO_EXIT;
+}
+
#ifndef CONFIG_USER_ONLY
static ExitStatus op_lura(DisasContext *s, DisasOps *o)
{
@@ -3382,6 +3421,7 @@ static ExitStatus op_sigp(DisasContext *s, DisasOps *o)
check_privileged(s);
potential_page_fault(s);
gen_helper_sigp(cc_op, cpu_env, o->in2, r1, o->in1);
+ set_cc_static(s);
tcg_temp_free_i32(r1);
return NO_EXIT;
}
@@ -3628,15 +3668,8 @@ static ExitStatus op_spt(DisasContext *s, DisasOps *o)
static ExitStatus op_stfl(DisasContext *s, DisasOps *o)
{
- TCGv_i64 f, a;
- /* We really ought to have more complete indication of facilities
- that we implement. Address this when STFLE is implemented. */
check_privileged(s);
- f = tcg_const_i64(0xc0000000);
- a = tcg_const_i64(200);
- tcg_gen_qemu_st32(f, a, get_mem_index(s));
- tcg_temp_free_i64(f);
- tcg_temp_free_i64(a);
+ gen_helper_stfl(cpu_env);
return NO_EXIT;
}
@@ -3802,6 +3835,14 @@ static ExitStatus op_sturg(DisasContext *s, DisasOps *o)
}
#endif
+static ExitStatus op_stfle(DisasContext *s, DisasOps *o)
+{
+ potential_page_fault(s);
+ gen_helper_stfle(cc_op, cpu_env, o->in2);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
static ExitStatus op_st8(DisasContext *s, DisasOps *o)
{
tcg_gen_qemu_st8(o->in1, o->in2, get_mem_index(s));
@@ -4420,6 +4461,22 @@ static void wout_r1_D32(DisasContext *s, DisasFields *f, DisasOps *o)
}
#define SPEC_wout_r1_D32 SPEC_r1_even
+static void wout_r3_P32(DisasContext *s, DisasFields *f, DisasOps *o)
+{
+ int r3 = get_field(f, r3);
+ store_reg32_i64(r3, o->out);
+ store_reg32_i64(r3 + 1, o->out2);
+}
+#define SPEC_wout_r3_P32 SPEC_r3_even
+
+static void wout_r3_P64(DisasContext *s, DisasFields *f, DisasOps *o)
+{
+ int r3 = get_field(f, r3);
+ store_reg(r3, o->out);
+ store_reg(r3 + 1, o->out2);
+}
+#define SPEC_wout_r3_P64 SPEC_r3_even
+
static void wout_e1(DisasContext *s, DisasFields *f, DisasOps *o)
{
store_freg32_i64(get_field(f, r1), o->out);
@@ -4486,21 +4543,17 @@ static void wout_m2_32(DisasContext *s, DisasFields *f, DisasOps *o)
}
#define SPEC_wout_m2_32 0
-static void wout_m2_32_r1_atomic(DisasContext *s, DisasFields *f, DisasOps *o)
+static void wout_in2_r1(DisasContext *s, DisasFields *f, DisasOps *o)
{
- /* XXX release reservation */
- tcg_gen_qemu_st32(o->out, o->addr1, get_mem_index(s));
- store_reg32_i64(get_field(f, r1), o->in2);
+ store_reg(get_field(f, r1), o->in2);
}
-#define SPEC_wout_m2_32_r1_atomic 0
+#define SPEC_wout_in2_r1 0
-static void wout_m2_64_r1_atomic(DisasContext *s, DisasFields *f, DisasOps *o)
+static void wout_in2_r1_32(DisasContext *s, DisasFields *f, DisasOps *o)
{
- /* XXX release reservation */
- tcg_gen_qemu_st64(o->out, o->addr1, get_mem_index(s));
- store_reg(get_field(f, r1), o->in2);
+ store_reg32_i64(get_field(f, r1), o->in2);
}
-#define SPEC_wout_m2_64_r1_atomic 0
+#define SPEC_wout_in2_r1_32 0
/* ====================================================================== */
/* The "INput 1" generators. These load the first operand to an insn. */
@@ -4944,24 +4997,6 @@ static void in2_mri2_64(DisasContext *s, DisasFields *f, DisasOps *o)
}
#define SPEC_in2_mri2_64 0
-static void in2_m2_32s_atomic(DisasContext *s, DisasFields *f, DisasOps *o)
-{
- /* XXX should reserve the address */
- in1_la2(s, f, o);
- o->in2 = tcg_temp_new_i64();
- tcg_gen_qemu_ld32s(o->in2, o->addr1, get_mem_index(s));
-}
-#define SPEC_in2_m2_32s_atomic 0
-
-static void in2_m2_64_atomic(DisasContext *s, DisasFields *f, DisasOps *o)
-{
- /* XXX should reserve the address */
- in1_la2(s, f, o);
- o->in2 = tcg_temp_new_i64();
- tcg_gen_qemu_ld64(o->in2, o->addr1, get_mem_index(s));
-}
-#define SPEC_in2_m2_64_atomic 0
-
static void in2_i2(DisasContext *s, DisasFields *f, DisasOps *o)
{
o->in2 = tcg_const_i64(get_field(f, i2));