summaryrefslogtreecommitdiff
path: root/target-s390x/translate.c
diff options
context:
space:
mode:
Diffstat (limited to 'target-s390x/translate.c')
-rw-r--r--target-s390x/translate.c556
1 files changed, 503 insertions, 53 deletions
diff --git a/target-s390x/translate.c b/target-s390x/translate.c
index fbffd3066d..42f52c70c6 100644
--- a/target-s390x/translate.c
+++ b/target-s390x/translate.c
@@ -150,6 +150,7 @@ void s390_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
static TCGv_i64 psw_addr;
static TCGv_i64 psw_mask;
+static TCGv_i64 gbea;
static TCGv_i32 cc_op;
static TCGv_i64 cc_src;
@@ -173,6 +174,9 @@ void s390x_translate_init(void)
psw_mask = tcg_global_mem_new_i64(TCG_AREG0,
offsetof(CPUS390XState, psw.mask),
"psw_mask");
+ gbea = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUS390XState, gbea),
+ "gbea");
cc_op = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUS390XState, cc_op),
"cc_op");
@@ -249,6 +253,46 @@ static void update_psw_addr(DisasContext *s)
tcg_gen_movi_i64(psw_addr, s->pc);
}
+static void per_branch(DisasContext *s, bool to_next)
+{
+#ifndef CONFIG_USER_ONLY
+ tcg_gen_movi_i64(gbea, s->pc);
+
+ if (s->tb->flags & FLAG_MASK_PER) {
+ TCGv_i64 next_pc = to_next ? tcg_const_i64(s->next_pc) : psw_addr;
+ gen_helper_per_branch(cpu_env, gbea, next_pc);
+ if (to_next) {
+ tcg_temp_free_i64(next_pc);
+ }
+ }
+#endif
+}
+
+static void per_branch_cond(DisasContext *s, TCGCond cond,
+ TCGv_i64 arg1, TCGv_i64 arg2)
+{
+#ifndef CONFIG_USER_ONLY
+ if (s->tb->flags & FLAG_MASK_PER) {
+ TCGLabel *lab = gen_new_label();
+ tcg_gen_brcond_i64(tcg_invert_cond(cond), arg1, arg2, lab);
+
+ tcg_gen_movi_i64(gbea, s->pc);
+ gen_helper_per_branch(cpu_env, gbea, psw_addr);
+
+ gen_set_label(lab);
+ } else {
+ TCGv_i64 pc = tcg_const_i64(s->pc);
+ tcg_gen_movcond_i64(cond, gbea, arg1, arg2, gbea, pc);
+ tcg_temp_free_i64(pc);
+ }
+#endif
+}
+
+static void per_breaking_event(DisasContext *s)
+{
+ tcg_gen_movi_i64(gbea, s->pc);
+}
+
static void update_cc_op(DisasContext *s)
{
if (s->cc_op != CC_OP_DYNAMIC && s->cc_op != CC_OP_STATIC) {
@@ -320,7 +364,21 @@ static void gen_program_exception(DisasContext *s, int code)
static inline void gen_illegal_opcode(DisasContext *s)
{
- gen_program_exception(s, PGM_SPECIFICATION);
+ gen_program_exception(s, PGM_OPERATION);
+}
+
+static inline void gen_trap(DisasContext *s)
+{
+ TCGv_i32 t;
+
+ /* Set DXC to 0xff. */
+ t = tcg_temp_new_i32();
+ tcg_gen_ld_i32(t, cpu_env, offsetof(CPUS390XState, fpc));
+ tcg_gen_ori_i32(t, t, 0xff00);
+ tcg_gen_st_i32(t, cpu_env, offsetof(CPUS390XState, fpc));
+ tcg_temp_free_i32(t);
+
+ gen_program_exception(s, PGM_DATA);
}
#ifndef CONFIG_USER_ONLY
@@ -554,7 +612,8 @@ static int use_goto_tb(DisasContext *s, uint64_t dest)
return (((dest & TARGET_PAGE_MASK) == (s->tb->pc & TARGET_PAGE_MASK)
|| (dest & TARGET_PAGE_MASK) == ((s->pc - 1) & TARGET_PAGE_MASK))
&& !s->singlestep_enabled
- && !(s->tb->cflags & CF_LAST_IO));
+ && !(s->tb->cflags & CF_LAST_IO)
+ && !(s->tb->flags & FLAG_MASK_PER));
}
static void account_noninline_branch(DisasContext *s, int cc_op)
@@ -987,6 +1046,7 @@ enum DisasFieldIndexC {
};
struct DisasFields {
+ uint64_t raw_insn;
unsigned op:8;
unsigned op2:8;
unsigned presentC:16;
@@ -1119,6 +1179,8 @@ typedef enum DisasFacility {
FAC_HFP_MA, /* HFP multiply-and-add/subtract */
FAC_HW, /* high-word */
FAC_IEEEE_SIM, /* IEEE exception sumilation */
+ FAC_MIE, /* miscellaneous-instruction-extensions */
+ FAC_LAT, /* load-and-trap */
FAC_LOC, /* load/store on condition */
FAC_LD, /* long displacement */
FAC_PC, /* population count */
@@ -1165,16 +1227,19 @@ static void help_l2_shift(DisasContext *s, DisasFields *f,
static ExitStatus help_goto_direct(DisasContext *s, uint64_t dest)
{
if (dest == s->next_pc) {
+ per_branch(s, true);
return NO_EXIT;
}
if (use_goto_tb(s, dest)) {
update_cc_op(s);
+ per_breaking_event(s);
tcg_gen_goto_tb(0);
tcg_gen_movi_i64(psw_addr, dest);
tcg_gen_exit_tb((uintptr_t)s->tb);
return EXIT_GOTO_TB;
} else {
tcg_gen_movi_i64(psw_addr, dest);
+ per_branch(s, false);
return EXIT_PC_UPDATED;
}
}
@@ -1194,6 +1259,7 @@ static ExitStatus help_branch(DisasContext *s, DisasCompare *c,
if (is_imm) {
if (dest == s->next_pc) {
/* Branch to next. */
+ per_branch(s, true);
ret = NO_EXIT;
goto egress;
}
@@ -1209,6 +1275,7 @@ static ExitStatus help_branch(DisasContext *s, DisasCompare *c,
}
if (c->cond == TCG_COND_ALWAYS) {
tcg_gen_mov_i64(psw_addr, cdest);
+ per_branch(s, false);
ret = EXIT_PC_UPDATED;
goto egress;
}
@@ -1233,6 +1300,7 @@ static ExitStatus help_branch(DisasContext *s, DisasCompare *c,
/* Branch taken. */
gen_set_label(lab);
+ per_breaking_event(s);
tcg_gen_goto_tb(1);
tcg_gen_movi_i64(psw_addr, dest);
tcg_gen_exit_tb((uintptr_t)s->tb + 1);
@@ -1264,6 +1332,7 @@ static ExitStatus help_branch(DisasContext *s, DisasCompare *c,
if (is_imm) {
tcg_gen_movi_i64(psw_addr, dest);
}
+ per_breaking_event(s);
ret = EXIT_PC_UPDATED;
}
} else {
@@ -1279,6 +1348,7 @@ static ExitStatus help_branch(DisasContext *s, DisasCompare *c,
if (c->is_64) {
tcg_gen_movcond_i64(c->cond, psw_addr, c->u.s64.a, c->u.s64.b,
cdest, next);
+ per_branch_cond(s, c->cond, c->u.s64.a, c->u.s64.b);
} else {
TCGv_i32 t0 = tcg_temp_new_i32();
TCGv_i64 t1 = tcg_temp_new_i64();
@@ -1287,6 +1357,7 @@ static ExitStatus help_branch(DisasContext *s, DisasCompare *c,
tcg_gen_extu_i32_i64(t1, t0);
tcg_temp_free_i32(t0);
tcg_gen_movcond_i64(TCG_COND_NE, psw_addr, t1, z, cdest, next);
+ per_branch_cond(s, TCG_COND_NE, t1, z);
tcg_temp_free_i64(t1);
tcg_temp_free_i64(z);
}
@@ -1310,7 +1381,13 @@ static ExitStatus help_branch(DisasContext *s, DisasCompare *c,
static ExitStatus op_abs(DisasContext *s, DisasOps *o)
{
- gen_helper_abs_i64(o->out, o->in2);
+ TCGv_i64 z, n;
+ z = tcg_const_i64(0);
+ n = tcg_temp_new_i64();
+ tcg_gen_neg_i64(n, o->in2);
+ tcg_gen_movcond_i64(TCG_COND_LT, o->out, o->in2, z, n, o->in2);
+ tcg_temp_free_i64(n);
+ tcg_temp_free_i64(z);
return NO_EXIT;
}
@@ -1413,6 +1490,7 @@ static ExitStatus op_bas(DisasContext *s, DisasOps *o)
tcg_gen_movi_i64(o->out, pc_to_link_info(s, s->next_pc));
if (!TCGV_IS_UNUSED_I64(o->in2)) {
tcg_gen_mov_i64(psw_addr, o->in2);
+ per_branch(s, false);
return EXIT_PC_UPDATED;
} else {
return NO_EXIT;
@@ -1460,6 +1538,30 @@ static ExitStatus op_bct32(DisasContext *s, DisasOps *o)
return help_branch(s, &c, is_imm, imm, o->in2);
}
+static ExitStatus op_bcth(DisasContext *s, DisasOps *o)
+{
+ int r1 = get_field(s->fields, r1);
+ int imm = get_field(s->fields, i2);
+ DisasCompare c;
+ TCGv_i64 t;
+
+ c.cond = TCG_COND_NE;
+ c.is_64 = false;
+ c.g1 = false;
+ c.g2 = false;
+
+ t = tcg_temp_new_i64();
+ tcg_gen_shri_i64(t, regs[r1], 32);
+ tcg_gen_subi_i64(t, t, 1);
+ store_reg32h_i64(r1, t);
+ c.u.s32.a = tcg_temp_new_i32();
+ c.u.s32.b = tcg_const_i32(0);
+ tcg_gen_trunc_i64_i32(c.u.s32.a, t);
+ tcg_temp_free_i64(t);
+
+ return help_branch(s, &c, 1, imm, o->in2);
+}
+
static ExitStatus op_bct64(DisasContext *s, DisasOps *o)
{
int r1 = get_field(s->fields, r1);
@@ -1961,7 +2063,6 @@ static ExitStatus op_ct(DisasContext *s, DisasOps *o)
{
int m3 = get_field(s->fields, m3);
TCGLabel *lab = gen_new_label();
- TCGv_i32 t;
TCGCond c;
c = tcg_invert_cond(ltgt_cond[m3]);
@@ -1970,15 +2071,8 @@ static ExitStatus op_ct(DisasContext *s, DisasOps *o)
}
tcg_gen_brcond_i64(c, o->in1, o->in2, lab);
- /* Set DXC to 0xff. */
- t = tcg_temp_new_i32();
- tcg_gen_ld_i32(t, cpu_env, offsetof(CPUS390XState, fpc));
- tcg_gen_ori_i32(t, t, 0xff00);
- tcg_gen_st_i32(t, cpu_env, offsetof(CPUS390XState, fpc));
- tcg_temp_free_i32(t);
-
/* Trap. */
- gen_program_exception(s, PGM_DATA);
+ gen_trap(s);
gen_set_label(lab);
return NO_EXIT;
@@ -1987,15 +2081,19 @@ static ExitStatus op_ct(DisasContext *s, DisasOps *o)
#ifndef CONFIG_USER_ONLY
static ExitStatus op_diag(DisasContext *s, DisasOps *o)
{
- TCGv_i32 tmp;
+ TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1));
+ TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3));
+ TCGv_i32 func_code = tcg_const_i32(get_field(s->fields, i2));
check_privileged(s);
- potential_page_fault(s);
+ update_psw_addr(s);
+ gen_op_calc_cc(s);
- /* We pretend the format is RX_a so that D2 is the field we want. */
- tmp = tcg_const_i32(get_field(s->fields, d2) & 0xfff);
- gen_helper_diag(regs[2], cpu_env, tmp, regs[2], regs[1]);
- tcg_temp_free_i32(tmp);
+ gen_helper_diag(cpu_env, r1, r3, func_code);
+
+ tcg_temp_free_i32(func_code);
+ tcg_temp_free_i32(r3);
+ tcg_temp_free_i32(r1);
return NO_EXIT;
}
#endif
@@ -2101,13 +2199,37 @@ static ExitStatus op_ex(DisasContext *s, DisasOps *o)
TCGv_i64 tmp;
update_psw_addr(s);
- update_cc_op(s);
+ gen_op_calc_cc(s);
tmp = tcg_const_i64(s->next_pc);
gen_helper_ex(cc_op, cpu_env, cc_op, o->in1, o->in2, tmp);
tcg_temp_free_i64(tmp);
- set_cc_static(s);
+ return NO_EXIT;
+}
+
+static ExitStatus op_fieb(DisasContext *s, DisasOps *o)
+{
+ TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3));
+ gen_helper_fieb(o->out, cpu_env, o->in2, m3);
+ tcg_temp_free_i32(m3);
+ return NO_EXIT;
+}
+
+static ExitStatus op_fidb(DisasContext *s, DisasOps *o)
+{
+ TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3));
+ gen_helper_fidb(o->out, cpu_env, o->in2, m3);
+ tcg_temp_free_i32(m3);
+ return NO_EXIT;
+}
+
+static ExitStatus op_fixb(DisasContext *s, DisasOps *o)
+{
+ TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3));
+ gen_helper_fixb(o->out, cpu_env, o->in1, o->in2, m3);
+ return_low128(o->out2);
+ tcg_temp_free_i32(m3);
return NO_EXIT;
}
@@ -2320,6 +2442,61 @@ static ExitStatus op_ld64(DisasContext *s, DisasOps *o)
return NO_EXIT;
}
+static ExitStatus op_lat(DisasContext *s, DisasOps *o)
+{
+ TCGLabel *lab = gen_new_label();
+ store_reg32_i64(get_field(s->fields, r1), o->in2);
+ /* The value is stored even in case of trap. */
+ tcg_gen_brcondi_i64(TCG_COND_NE, o->in2, 0, lab);
+ gen_trap(s);
+ gen_set_label(lab);
+ return NO_EXIT;
+}
+
+static ExitStatus op_lgat(DisasContext *s, DisasOps *o)
+{
+ TCGLabel *lab = gen_new_label();
+ tcg_gen_qemu_ld64(o->out, o->in2, get_mem_index(s));
+ /* The value is stored even in case of trap. */
+ tcg_gen_brcondi_i64(TCG_COND_NE, o->out, 0, lab);
+ gen_trap(s);
+ gen_set_label(lab);
+ return NO_EXIT;
+}
+
+static ExitStatus op_lfhat(DisasContext *s, DisasOps *o)
+{
+ TCGLabel *lab = gen_new_label();
+ store_reg32h_i64(get_field(s->fields, r1), o->in2);
+ /* The value is stored even in case of trap. */
+ tcg_gen_brcondi_i64(TCG_COND_NE, o->in2, 0, lab);
+ gen_trap(s);
+ gen_set_label(lab);
+ return NO_EXIT;
+}
+
+static ExitStatus op_llgfat(DisasContext *s, DisasOps *o)
+{
+ TCGLabel *lab = gen_new_label();
+ tcg_gen_qemu_ld32u(o->out, o->in2, get_mem_index(s));
+ /* The value is stored even in case of trap. */
+ tcg_gen_brcondi_i64(TCG_COND_NE, o->out, 0, lab);
+ gen_trap(s);
+ gen_set_label(lab);
+ return NO_EXIT;
+}
+
+static ExitStatus op_llgtat(DisasContext *s, DisasOps *o)
+{
+ TCGLabel *lab = gen_new_label();
+ tcg_gen_andi_i64(o->out, o->in2, 0x7fffffff);
+ /* The value is stored even in case of trap. */
+ tcg_gen_brcondi_i64(TCG_COND_NE, o->out, 0, lab);
+ gen_trap(s);
+ gen_set_label(lab);
+ return NO_EXIT;
+}
+
static ExitStatus op_loc(DisasContext *s, DisasOps *o)
{
DisasCompare c;
@@ -2388,6 +2565,7 @@ static ExitStatus op_lpsw(DisasContext *s, DisasOps *o)
TCGv_i64 t1, t2;
check_privileged(s);
+ per_breaking_event(s);
t1 = tcg_temp_new_i64();
t2 = tcg_temp_new_i64();
@@ -2407,6 +2585,7 @@ static ExitStatus op_lpswe(DisasContext *s, DisasOps *o)
TCGv_i64 t1, t2;
check_privileged(s);
+ per_breaking_event(s);
t1 = tcg_temp_new_i64();
t2 = tcg_temp_new_i64();
@@ -2435,21 +2614,45 @@ static ExitStatus op_lm32(DisasContext *s, DisasOps *o)
{
int r1 = get_field(s->fields, r1);
int r3 = get_field(s->fields, r3);
- TCGv_i64 t = tcg_temp_new_i64();
- TCGv_i64 t4 = tcg_const_i64(4);
+ TCGv_i64 t1, t2;
- while (1) {
- tcg_gen_qemu_ld32u(t, o->in2, get_mem_index(s));
- store_reg32_i64(r1, t);
- if (r1 == r3) {
- break;
- }
- tcg_gen_add_i64(o->in2, o->in2, t4);
+ /* Only one register to read. */
+ t1 = tcg_temp_new_i64();
+ if (unlikely(r1 == r3)) {
+ tcg_gen_qemu_ld32u(t1, o->in2, get_mem_index(s));
+ store_reg32_i64(r1, t1);
+ tcg_temp_free(t1);
+ return NO_EXIT;
+ }
+
+ /* First load the values of the first and last registers to trigger
+ possible page faults. */
+ t2 = tcg_temp_new_i64();
+ tcg_gen_qemu_ld32u(t1, o->in2, get_mem_index(s));
+ tcg_gen_addi_i64(t2, o->in2, 4 * ((r3 - r1) & 15));
+ tcg_gen_qemu_ld32u(t2, t2, get_mem_index(s));
+ store_reg32_i64(r1, t1);
+ store_reg32_i64(r3, t2);
+
+ /* Only two registers to read. */
+ if (((r1 + 1) & 15) == r3) {
+ tcg_temp_free(t2);
+ tcg_temp_free(t1);
+ return NO_EXIT;
+ }
+
+ /* Then load the remaining registers. Page fault can't occur. */
+ r3 = (r3 - 1) & 15;
+ tcg_gen_movi_i64(t2, 4);
+ while (r1 != r3) {
r1 = (r1 + 1) & 15;
+ tcg_gen_add_i64(o->in2, o->in2, t2);
+ tcg_gen_qemu_ld32u(t1, o->in2, get_mem_index(s));
+ store_reg32_i64(r1, t1);
}
+ tcg_temp_free(t2);
+ tcg_temp_free(t1);
- tcg_temp_free_i64(t);
- tcg_temp_free_i64(t4);
return NO_EXIT;
}
@@ -2457,21 +2660,45 @@ static ExitStatus op_lmh(DisasContext *s, DisasOps *o)
{
int r1 = get_field(s->fields, r1);
int r3 = get_field(s->fields, r3);
- TCGv_i64 t = tcg_temp_new_i64();
- TCGv_i64 t4 = tcg_const_i64(4);
+ TCGv_i64 t1, t2;
- while (1) {
- tcg_gen_qemu_ld32u(t, o->in2, get_mem_index(s));
- store_reg32h_i64(r1, t);
- if (r1 == r3) {
- break;
- }
- tcg_gen_add_i64(o->in2, o->in2, t4);
+ /* Only one register to read. */
+ t1 = tcg_temp_new_i64();
+ if (unlikely(r1 == r3)) {
+ tcg_gen_qemu_ld32u(t1, o->in2, get_mem_index(s));
+ store_reg32h_i64(r1, t1);
+ tcg_temp_free(t1);
+ return NO_EXIT;
+ }
+
+ /* First load the values of the first and last registers to trigger
+ possible page faults. */
+ t2 = tcg_temp_new_i64();
+ tcg_gen_qemu_ld32u(t1, o->in2, get_mem_index(s));
+ tcg_gen_addi_i64(t2, o->in2, 4 * ((r3 - r1) & 15));
+ tcg_gen_qemu_ld32u(t2, t2, get_mem_index(s));
+ store_reg32h_i64(r1, t1);
+ store_reg32h_i64(r3, t2);
+
+ /* Only two registers to read. */
+ if (((r1 + 1) & 15) == r3) {
+ tcg_temp_free(t2);
+ tcg_temp_free(t1);
+ return NO_EXIT;
+ }
+
+ /* Then load the remaining registers. Page fault can't occur. */
+ r3 = (r3 - 1) & 15;
+ tcg_gen_movi_i64(t2, 4);
+ while (r1 != r3) {
r1 = (r1 + 1) & 15;
+ tcg_gen_add_i64(o->in2, o->in2, t2);
+ tcg_gen_qemu_ld32u(t1, o->in2, get_mem_index(s));
+ store_reg32h_i64(r1, t1);
}
+ tcg_temp_free(t2);
+ tcg_temp_free(t1);
- tcg_temp_free_i64(t);
- tcg_temp_free_i64(t4);
return NO_EXIT;
}
@@ -2479,18 +2706,40 @@ static ExitStatus op_lm64(DisasContext *s, DisasOps *o)
{
int r1 = get_field(s->fields, r1);
int r3 = get_field(s->fields, r3);
- TCGv_i64 t8 = tcg_const_i64(8);
+ TCGv_i64 t1, t2;
- while (1) {
+ /* Only one register to read. */
+ if (unlikely(r1 == r3)) {
tcg_gen_qemu_ld64(regs[r1], o->in2, get_mem_index(s));
- if (r1 == r3) {
- break;
- }
- tcg_gen_add_i64(o->in2, o->in2, t8);
+ return NO_EXIT;
+ }
+
+ /* First load the values of the first and last registers to trigger
+ possible page faults. */
+ t1 = tcg_temp_new_i64();
+ t2 = tcg_temp_new_i64();
+ tcg_gen_qemu_ld64(t1, o->in2, get_mem_index(s));
+ tcg_gen_addi_i64(t2, o->in2, 8 * ((r3 - r1) & 15));
+ tcg_gen_qemu_ld64(regs[r3], t2, get_mem_index(s));
+ tcg_gen_mov_i64(regs[r1], t1);
+ tcg_temp_free(t2);
+
+ /* Only two registers to read. */
+ if (((r1 + 1) & 15) == r3) {
+ tcg_temp_free(t1);
+ return NO_EXIT;
+ }
+
+ /* Then load the remaining registers. Page fault can't occur. */
+ r3 = (r3 - 1) & 15;
+ tcg_gen_movi_i64(t1, 8);
+ while (r1 != r3) {
r1 = (r1 + 1) & 15;
+ tcg_gen_add_i64(o->in2, o->in2, t1);
+ tcg_gen_qemu_ld64(regs[r1], o->in2, get_mem_index(s));
}
+ tcg_temp_free(t1);
- tcg_temp_free_i64(t8);
return NO_EXIT;
}
@@ -2521,6 +2770,41 @@ static ExitStatus op_mov2(DisasContext *s, DisasOps *o)
return NO_EXIT;
}
+static ExitStatus op_mov2e(DisasContext *s, DisasOps *o)
+{
+ int b2 = get_field(s->fields, b2);
+ TCGv ar1 = tcg_temp_new_i64();
+
+ o->out = o->in2;
+ o->g_out = o->g_in2;
+ TCGV_UNUSED_I64(o->in2);
+ o->g_in2 = false;
+
+ switch (s->tb->flags & FLAG_MASK_ASC) {
+ case PSW_ASC_PRIMARY >> 32:
+ tcg_gen_movi_i64(ar1, 0);
+ break;
+ case PSW_ASC_ACCREG >> 32:
+ tcg_gen_movi_i64(ar1, 1);
+ break;
+ case PSW_ASC_SECONDARY >> 32:
+ if (b2) {
+ tcg_gen_ld32u_i64(ar1, cpu_env, offsetof(CPUS390XState, aregs[b2]));
+ } else {
+ tcg_gen_movi_i64(ar1, 0);
+ }
+ break;
+ case PSW_ASC_HOME >> 32:
+ tcg_gen_movi_i64(ar1, 2);
+ break;
+ }
+
+ tcg_gen_st32_i64(ar1, cpu_env, offsetof(CPUS390XState, aregs[1]));
+ tcg_temp_free_i64(ar1);
+
+ return NO_EXIT;
+}
+
static ExitStatus op_movx(DisasContext *s, DisasOps *o)
{
o->out = o->in1;
@@ -2681,7 +2965,13 @@ static ExitStatus op_msdb(DisasContext *s, DisasOps *o)
static ExitStatus op_nabs(DisasContext *s, DisasOps *o)
{
- gen_helper_nabs_i64(o->out, o->in2);
+ TCGv_i64 z, n;
+ z = tcg_const_i64(0);
+ n = tcg_temp_new_i64();
+ tcg_gen_neg_i64(n, o->in2);
+ tcg_gen_movcond_i64(TCG_COND_GE, o->out, o->in2, z, n, o->in2);
+ tcg_temp_free_i64(n);
+ tcg_temp_free_i64(z);
return NO_EXIT;
}
@@ -3356,11 +3646,93 @@ static ExitStatus op_spx(DisasContext *s, DisasOps *o)
return NO_EXIT;
}
-static ExitStatus op_subchannel(DisasContext *s, DisasOps *o)
+static ExitStatus op_xsch(DisasContext *s, DisasOps *o)
+{
+ check_privileged(s);
+ potential_page_fault(s);
+ gen_helper_xsch(cpu_env, regs[1]);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
+static ExitStatus op_csch(DisasContext *s, DisasOps *o)
+{
+ check_privileged(s);
+ potential_page_fault(s);
+ gen_helper_csch(cpu_env, regs[1]);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
+static ExitStatus op_hsch(DisasContext *s, DisasOps *o)
{
check_privileged(s);
- /* Not operational. */
- gen_op_movi_cc(s, 3);
+ potential_page_fault(s);
+ gen_helper_hsch(cpu_env, regs[1]);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
+static ExitStatus op_msch(DisasContext *s, DisasOps *o)
+{
+ check_privileged(s);
+ potential_page_fault(s);
+ gen_helper_msch(cpu_env, regs[1], o->in2);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
+static ExitStatus op_rchp(DisasContext *s, DisasOps *o)
+{
+ check_privileged(s);
+ potential_page_fault(s);
+ gen_helper_rchp(cpu_env, regs[1]);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
+static ExitStatus op_rsch(DisasContext *s, DisasOps *o)
+{
+ check_privileged(s);
+ potential_page_fault(s);
+ gen_helper_rsch(cpu_env, regs[1]);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
+static ExitStatus op_ssch(DisasContext *s, DisasOps *o)
+{
+ check_privileged(s);
+ potential_page_fault(s);
+ gen_helper_ssch(cpu_env, regs[1], o->in2);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
+static ExitStatus op_stsch(DisasContext *s, DisasOps *o)
+{
+ check_privileged(s);
+ potential_page_fault(s);
+ gen_helper_stsch(cpu_env, regs[1], o->in2);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
+static ExitStatus op_tsch(DisasContext *s, DisasOps *o)
+{
+ check_privileged(s);
+ potential_page_fault(s);
+ gen_helper_tsch(cpu_env, regs[1], o->in2);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
+static ExitStatus op_chsc(DisasContext *s, DisasOps *o)
+{
+ check_privileged(s);
+ potential_page_fault(s);
+ gen_helper_chsc(cpu_env, o->in2);
+ set_cc_static(s);
return NO_EXIT;
}
@@ -3646,6 +4018,25 @@ static ExitStatus op_tr(DisasContext *s, DisasOps *o)
return NO_EXIT;
}
+static ExitStatus op_tre(DisasContext *s, DisasOps *o)
+{
+ potential_page_fault(s);
+ gen_helper_tre(o->out, cpu_env, o->out, o->out2, o->in2);
+ return_low128(o->out2);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
+static ExitStatus op_trt(DisasContext *s, DisasOps *o)
+{
+ TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1));
+ potential_page_fault(s);
+ gen_helper_trt(cc_op, cpu_env, l, o->addr1, o->in2);
+ tcg_temp_free_i32(l);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
static ExitStatus op_unpk(DisasContext *s, DisasOps *o)
{
TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1));
@@ -3989,6 +4380,12 @@ static void wout_r1_32(DisasContext *s, DisasFields *f, DisasOps *o)
}
#define SPEC_wout_r1_32 0
+static void wout_r1_32h(DisasContext *s, DisasFields *f, DisasOps *o)
+{
+ store_reg32h_i64(get_field(f, r1), o->out);
+}
+#define SPEC_wout_r1_32h 0
+
static void wout_r1_P32(DisasContext *s, DisasFields *f, DisasOps *o)
{
int r1 = get_field(f, r1);
@@ -4159,6 +4556,13 @@ static void in1_r2(DisasContext *s, DisasFields *f, DisasOps *o)
}
#define SPEC_in1_r2 0
+static void in1_r2_sr32(DisasContext *s, DisasFields *f, DisasOps *o)
+{
+ o->in1 = tcg_temp_new_i64();
+ tcg_gen_shri_i64(o->in1, regs[get_field(f, r2)], 32);
+}
+#define SPEC_in1_r2_sr32 0
+
static void in1_r3(DisasContext *s, DisasFields *f, DisasOps *o)
{
o->in1 = load_reg(get_field(f, r3));
@@ -4372,6 +4776,13 @@ static void in2_r3(DisasContext *s, DisasFields *f, DisasOps *o)
}
#define SPEC_in2_r3 0
+static void in2_r3_sr32(DisasContext *s, DisasFields *f, DisasOps *o)
+{
+ o->in2 = tcg_temp_new_i64();
+ tcg_gen_shri_i64(o->in2, regs[get_field(f, r3)], 32);
+}
+#define SPEC_in2_r3_sr32 0
+
static void in2_r2_32s(DisasContext *s, DisasFields *f, DisasOps *o)
{
o->in2 = tcg_temp_new_i64();
@@ -4386,6 +4797,13 @@ static void in2_r2_32u(DisasContext *s, DisasFields *f, DisasOps *o)
}
#define SPEC_in2_r2_32u 0
+static void in2_r2_sr32(DisasContext *s, DisasFields *f, DisasOps *o)
+{
+ o->in2 = tcg_temp_new_i64();
+ tcg_gen_shri_i64(o->in2, regs[get_field(f, r2)], 32);
+}
+#define SPEC_in2_r2_sr32 0
+
static void in2_e2(DisasContext *s, DisasFields *f, DisasOps *o)
{
o->in2 = load_freg32_i64(get_field(f, r2));
@@ -4565,6 +4983,14 @@ static void in2_i2_32u_shl(DisasContext *s, DisasFields *f, DisasOps *o)
}
#define SPEC_in2_i2_32u_shl 0
+#ifndef CONFIG_USER_ONLY
+static void in2_insn(DisasContext *s, DisasFields *f, DisasOps *o)
+{
+ o->in2 = tcg_const_i64(s->fields->raw_insn);
+}
+#define SPEC_in2_insn 0
+#endif
+
/* ====================================================================== */
/* Find opc within the table of insns. This is formulated as a switch
@@ -4741,6 +5167,7 @@ static const DisasInsn *extract_insn(CPUS390XState *env, DisasContext *s,
}
memset(f, 0, sizeof(*f));
+ f->raw_insn = insn;
f->op = op;
f->op2 = op2;
@@ -4777,6 +5204,14 @@ static ExitStatus translate_one(CPUS390XState *env, DisasContext *s)
return EXIT_NORETURN;
}
+#ifndef CONFIG_USER_ONLY
+ if (s->tb->flags & FLAG_MASK_PER) {
+ TCGv_i64 addr = tcg_const_i64(s->pc);
+ gen_helper_per_ifetch(cpu_env, addr);
+ tcg_temp_free_i64(addr);
+ }
+#endif
+
/* Check for insn specification exceptions. */
if (insn->spec) {
int spec = insn->spec, excp = 0, r;
@@ -4864,6 +5299,21 @@ static ExitStatus translate_one(CPUS390XState *env, DisasContext *s)
tcg_temp_free_i64(o.addr1);
}
+#ifndef CONFIG_USER_ONLY
+ if (s->tb->flags & FLAG_MASK_PER) {
+ /* An exception might be triggered, save PSW if not already done. */
+ if (ret == NO_EXIT || ret == EXIT_PC_STALE) {
+ tcg_gen_movi_i64(psw_addr, s->next_pc);
+ }
+
+ /* Save off cc. */
+ update_cc_op(s);
+
+ /* Call the helper to check for a possible PER exception. */
+ gen_helper_per_check_exception(cpu_env);
+ }
+#endif
+
/* Advance to the next instruction. */
s->pc = s->next_pc;
return ret;