aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--target/s390x/cpu.h7
-rw-r--r--target/s390x/cpu_models.c36
-rw-r--r--target/s390x/fpu_helper.c27
-rw-r--r--target/s390x/helper.c7
-rw-r--r--target/s390x/helper.h28
-rw-r--r--target/s390x/insn-data.def66
-rw-r--r--target/s390x/machine.c19
-rw-r--r--target/s390x/mem_helper.c1325
-rw-r--r--target/s390x/misc_helper.c4
-rw-r--r--target/s390x/mmu_helper.c4
-rw-r--r--target/s390x/translate.c526
11 files changed, 1553 insertions, 496 deletions
diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h
index c74b419..a4d31df 100644
--- a/target/s390x/cpu.h
+++ b/target/s390x/cpu.h
@@ -107,6 +107,8 @@ typedef struct CPUS390XState {
uint64_t cc_dst;
uint64_t cc_vr;
+ uint64_t ex_value;
+
uint64_t __excp_addr;
uint64_t psa;
@@ -393,7 +395,7 @@ static inline void cpu_get_tb_cpu_state(CPUS390XState* env, target_ulong *pc,
target_ulong *cs_base, uint32_t *flags)
{
*pc = env->psw.addr;
- *cs_base = 0;
+ *cs_base = env->ex_value;
*flags = ((env->psw.mask >> 32) & ~FLAG_MASK_CC) |
((env->psw.mask & PSW_MASK_32) ? FLAG_MASK_32 : 0);
}
@@ -1033,6 +1035,8 @@ struct sysib_322 {
#define _SEGMENT_ENTRY_RO 0x200 /* page protection bit */
#define _SEGMENT_ENTRY_INV 0x20 /* invalid segment table entry */
+#define VADDR_PX 0xff000 /* page index bits */
+
#define _PAGE_RO 0x200 /* HW read-only bit */
#define _PAGE_INVALID 0x400 /* HW invalid bit */
#define _PAGE_RES0 0x800 /* bit must be zero */
@@ -1084,6 +1088,7 @@ struct sysib_322 {
#define SIGP_ORDER_MASK 0x000000ff
void load_psw(CPUS390XState *env, uint64_t mask, uint64_t addr);
+target_ulong mmu_real2abs(CPUS390XState *env, target_ulong raddr);
int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc,
target_ulong *raddr, int *flags, bool exc);
int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code);
diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c
index 8d27363..fc3cb25 100644
--- a/target/s390x/cpu_models.c
+++ b/target/s390x/cpu_models.c
@@ -658,6 +658,32 @@ static void check_compatibility(const S390CPUModel *max_model,
"available in the configuration: ");
}
+/**
+ * The base TCG CPU model "qemu" is based on the z900. However, we already
+ * can also emulate some additional features of later CPU generations, so
+ * we add these additional feature bits here.
+ */
+static void add_qemu_cpu_model_features(S390FeatBitmap fbm)
+{
+ static const int feats[] = {
+ S390_FEAT_STFLE,
+ S390_FEAT_EXTENDED_IMMEDIATE,
+ S390_FEAT_EXTENDED_TRANSLATION_2,
+ S390_FEAT_LONG_DISPLACEMENT,
+ S390_FEAT_LONG_DISPLACEMENT_FAST,
+ S390_FEAT_ETF2_ENH,
+ S390_FEAT_STORE_CLOCK_FAST,
+ S390_FEAT_GENERAL_INSTRUCTIONS_EXT,
+ S390_FEAT_EXECUTE_EXT,
+ S390_FEAT_STFLE_45,
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(feats); i++) {
+ set_bit(feats[i], fbm);
+ }
+}
+
static S390CPUModel *get_max_cpu_model(Error **errp)
{
static S390CPUModel max_model;
@@ -670,10 +696,11 @@ static S390CPUModel *get_max_cpu_model(Error **errp)
if (kvm_enabled()) {
kvm_s390_get_host_cpu_model(&max_model, errp);
} else {
- /* TCG emulates a z900 */
+ /* TCG emulates a z900 (with some optional additional features) */
max_model.def = &s390_cpu_defs[0];
bitmap_copy(max_model.features, max_model.def->default_feat,
S390_FEAT_MAX);
+ add_qemu_cpu_model_features(max_model.features);
}
if (!*errp) {
cached = true;
@@ -925,11 +952,14 @@ static void s390_host_cpu_model_initfn(Object *obj)
static void s390_qemu_cpu_model_initfn(Object *obj)
{
+ static S390CPUDef s390_qemu_cpu_defs;
S390CPU *cpu = S390_CPU(obj);
cpu->model = g_malloc0(sizeof(*cpu->model));
- /* TCG emulates a z900 */
- cpu->model->def = &s390_cpu_defs[0];
+ /* TCG emulates a z900 (with some optional additional features) */
+ memcpy(&s390_qemu_cpu_defs, &s390_cpu_defs[0], sizeof(s390_qemu_cpu_defs));
+ add_qemu_cpu_model_features(s390_qemu_cpu_defs.full_feat);
+ cpu->model->def = &s390_qemu_cpu_defs;
bitmap_copy(cpu->model->features, cpu->model->def->default_feat,
S390_FEAT_MAX);
}
diff --git a/target/s390x/fpu_helper.c b/target/s390x/fpu_helper.c
index e604e9f..26f124f 100644
--- a/target/s390x/fpu_helper.c
+++ b/target/s390x/fpu_helper.c
@@ -585,6 +585,33 @@ uint64_t HELPER(fixb)(CPUS390XState *env, uint64_t ah, uint64_t al, uint32_t m3)
return RET128(ret);
}
+/* 32-bit FP compare and signal */
+uint32_t HELPER(keb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
+{
+ int cmp = float32_compare(f1, f2, &env->fpu_status);
+ handle_exceptions(env, GETPC());
+ return float_comp_to_cc(env, cmp);
+}
+
+/* 64-bit FP compare and signal */
+uint32_t HELPER(kdb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
+{
+ int cmp = float64_compare(f1, f2, &env->fpu_status);
+ handle_exceptions(env, GETPC());
+ return float_comp_to_cc(env, cmp);
+}
+
+/* 128-bit FP compare and signal */
+uint32_t HELPER(kxb)(CPUS390XState *env, uint64_t ah, uint64_t al,
+ uint64_t bh, uint64_t bl)
+{
+ int cmp = float128_compare(make_float128(ah, al),
+ make_float128(bh, bl),
+ &env->fpu_status);
+ handle_exceptions(env, GETPC());
+ return float_comp_to_cc(env, cmp);
+}
+
/* 32-bit FP multiply and add */
uint64_t HELPER(maeb)(CPUS390XState *env, uint64_t f1,
uint64_t f2, uint64_t f3)
diff --git a/target/s390x/helper.c b/target/s390x/helper.c
index 4f8aadf..a8d20c5 100644
--- a/target/s390x/helper.c
+++ b/target/s390x/helper.c
@@ -204,7 +204,7 @@ int s390_cpu_handle_mmu_fault(CPUState *cs, vaddr orig_vaddr,
if (raddr > ram_size) {
DPRINTF("%s: raddr %" PRIx64 " > ram_size %" PRIx64 "\n", __func__,
(uint64_t)raddr, (uint64_t)ram_size);
- trigger_pgm_exception(env, PGM_ADDRESSING, ILEN_LATER);
+ trigger_pgm_exception(env, PGM_ADDRESSING, ILEN_LATER_INC);
return 1;
}
@@ -642,6 +642,11 @@ bool s390_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
S390CPU *cpu = S390_CPU(cs);
CPUS390XState *env = &cpu->env;
+ if (env->ex_value) {
+ /* Execution of the target insn is indivisible from
+ the parent EXECUTE insn. */
+ return false;
+ }
if (env->psw.mask & PSW_MASK_EXT) {
s390_cpu_do_interrupt(cs);
return true;
diff --git a/target/s390x/helper.h b/target/s390x/helper.h
index 0b70770..69249a5 100644
--- a/target/s390x/helper.h
+++ b/target/s390x/helper.h
@@ -3,8 +3,10 @@ DEF_HELPER_FLAGS_4(nc, TCG_CALL_NO_WG, i32, env, i32, i64, i64)
DEF_HELPER_FLAGS_4(oc, TCG_CALL_NO_WG, i32, env, i32, i64, i64)
DEF_HELPER_FLAGS_4(xc, TCG_CALL_NO_WG, i32, env, i32, i64, i64)
DEF_HELPER_FLAGS_4(mvc, TCG_CALL_NO_WG, void, env, i32, i64, i64)
+DEF_HELPER_FLAGS_4(mvcin, TCG_CALL_NO_WG, void, env, i32, i64, i64)
DEF_HELPER_FLAGS_4(clc, TCG_CALL_NO_WG, i32, env, i32, i64, i64)
DEF_HELPER_3(mvcl, i32, env, i32, i32)
+DEF_HELPER_3(clcl, i32, env, i32, i32)
DEF_HELPER_FLAGS_4(clm, TCG_CALL_NO_WG, i32, env, i32, i32, i64)
DEF_HELPER_FLAGS_3(divs32, TCG_CALL_NO_WG, s64, env, s64, s64)
DEF_HELPER_FLAGS_3(divu32, TCG_CALL_NO_WG, i64, env, i64, i64)
@@ -12,13 +14,18 @@ DEF_HELPER_FLAGS_3(divs64, TCG_CALL_NO_WG, s64, env, s64, s64)
DEF_HELPER_FLAGS_4(divu64, TCG_CALL_NO_WG, i64, env, i64, i64, i64)
DEF_HELPER_4(srst, i64, env, i64, i64, i64)
DEF_HELPER_4(clst, i64, env, i64, i64, i64)
-DEF_HELPER_4(mvpg, void, env, i64, i64, i64)
+DEF_HELPER_FLAGS_4(mvn, TCG_CALL_NO_WG, void, env, i32, i64, i64)
+DEF_HELPER_FLAGS_4(mvo, TCG_CALL_NO_WG, void, env, i32, i64, i64)
+DEF_HELPER_FLAGS_4(mvpg, TCG_CALL_NO_WG, i32, env, i64, i64, i64)
+DEF_HELPER_FLAGS_4(mvz, TCG_CALL_NO_WG, void, env, i32, i64, i64)
DEF_HELPER_4(mvst, i64, env, i64, i64, i64)
-DEF_HELPER_5(ex, i32, env, i32, i64, i64, i64)
+DEF_HELPER_4(ex, void, env, i32, i64, i64)
DEF_HELPER_FLAGS_4(stam, TCG_CALL_NO_WG, void, env, i32, i64, i32)
DEF_HELPER_FLAGS_4(lam, TCG_CALL_NO_WG, void, env, i32, i64, i32)
DEF_HELPER_4(mvcle, i32, env, i32, i64, i32)
+DEF_HELPER_4(mvclu, i32, env, i32, i64, i32)
DEF_HELPER_4(clcle, i32, env, i32, i64, i32)
+DEF_HELPER_4(clclu, i32, env, i32, i64, i32)
DEF_HELPER_3(cegb, i64, env, s64, i32)
DEF_HELPER_3(cdgb, i64, env, s64, i32)
DEF_HELPER_3(cxgb, i64, env, s64, i32)
@@ -49,6 +56,9 @@ DEF_HELPER_FLAGS_3(lexb, TCG_CALL_NO_WG, i64, env, i64, i64)
DEF_HELPER_FLAGS_3(ceb, TCG_CALL_NO_WG_SE, i32, env, i64, i64)
DEF_HELPER_FLAGS_3(cdb, TCG_CALL_NO_WG_SE, i32, env, i64, i64)
DEF_HELPER_FLAGS_5(cxb, TCG_CALL_NO_WG_SE, i32, env, i64, i64, i64, i64)
+DEF_HELPER_FLAGS_3(keb, TCG_CALL_NO_WG, i32, env, i64, i64)
+DEF_HELPER_FLAGS_3(kdb, TCG_CALL_NO_WG, i32, env, i64, i64)
+DEF_HELPER_FLAGS_5(kxb, TCG_CALL_NO_WG, i32, env, i64, i64, i64, i64)
DEF_HELPER_FLAGS_3(cgeb, TCG_CALL_NO_WG, i64, env, i64, i32)
DEF_HELPER_FLAGS_3(cgdb, TCG_CALL_NO_WG, i64, env, i64, i32)
DEF_HELPER_FLAGS_4(cgxb, TCG_CALL_NO_WG, i64, env, i64, i64, i32)
@@ -75,10 +85,17 @@ DEF_HELPER_FLAGS_2(sqeb, TCG_CALL_NO_WG, i64, env, i64)
DEF_HELPER_FLAGS_2(sqdb, TCG_CALL_NO_WG, i64, env, i64)
DEF_HELPER_FLAGS_3(sqxb, TCG_CALL_NO_WG, i64, env, i64, i64)
DEF_HELPER_FLAGS_1(cvd, TCG_CALL_NO_RWG_SE, i64, s32)
+DEF_HELPER_FLAGS_4(pack, TCG_CALL_NO_WG, void, env, i32, i64, i64)
+DEF_HELPER_FLAGS_4(pka, TCG_CALL_NO_WG, void, env, i64, i64, i32)
+DEF_HELPER_FLAGS_4(pku, TCG_CALL_NO_WG, void, env, i64, i64, i32)
DEF_HELPER_FLAGS_4(unpk, TCG_CALL_NO_WG, void, env, i32, i64, i64)
+DEF_HELPER_FLAGS_4(unpka, TCG_CALL_NO_WG, i32, env, i64, i32, i64)
+DEF_HELPER_FLAGS_4(unpku, TCG_CALL_NO_WG, i32, env, i64, i32, i64)
+DEF_HELPER_FLAGS_3(tp, TCG_CALL_NO_WG, i32, env, i64, i32)
DEF_HELPER_FLAGS_4(tr, TCG_CALL_NO_WG, void, env, i32, i64, i64)
DEF_HELPER_4(tre, i64, env, i64, i64, i64)
DEF_HELPER_4(trt, i32, env, i32, i64, i64)
+DEF_HELPER_5(trXX, i32, env, i32, i32, i32, i32)
DEF_HELPER_4(cksm, i64, env, i64, i64, i64)
DEF_HELPER_FLAGS_5(calc_cc, TCG_CALL_NO_RWG_SE, i32, env, i32, i64, i64, i64)
DEF_HELPER_FLAGS_2(sfpc, TCG_CALL_NO_RWG, void, env, i64)
@@ -86,6 +103,8 @@ DEF_HELPER_FLAGS_2(sfas, TCG_CALL_NO_WG, void, env, i64)
DEF_HELPER_FLAGS_1(popcnt, TCG_CALL_NO_RWG_SE, i64, i64)
DEF_HELPER_FLAGS_1(stfl, TCG_CALL_NO_RWG, void, env)
DEF_HELPER_2(stfle, i32, env, i64)
+DEF_HELPER_FLAGS_2(lpq, TCG_CALL_NO_WG, i64, env, i64)
+DEF_HELPER_FLAGS_4(stpq, TCG_CALL_NO_WG, void, env, i64, i64, i64)
#ifndef CONFIG_USER_ONLY
DEF_HELPER_3(servc, i32, env, i64, i64)
@@ -102,17 +121,18 @@ DEF_HELPER_FLAGS_4(lctl, TCG_CALL_NO_WG, void, env, i32, i64, i32)
DEF_HELPER_FLAGS_4(lctlg, TCG_CALL_NO_WG, void, env, i32, i64, i32)
DEF_HELPER_FLAGS_4(stctl, TCG_CALL_NO_WG, void, env, i32, i64, i32)
DEF_HELPER_FLAGS_4(stctg, TCG_CALL_NO_WG, void, env, i32, i64, i32)
+DEF_HELPER_FLAGS_2(testblock, TCG_CALL_NO_WG, i32, env, i64)
DEF_HELPER_FLAGS_2(tprot, TCG_CALL_NO_RWG, i32, i64, i64)
DEF_HELPER_FLAGS_2(iske, TCG_CALL_NO_RWG_SE, i64, env, i64)
DEF_HELPER_FLAGS_3(sske, TCG_CALL_NO_RWG, void, env, i64, i64)
DEF_HELPER_FLAGS_2(rrbe, TCG_CALL_NO_RWG, i32, env, i64)
-DEF_HELPER_3(csp, i32, env, i32, i64)
DEF_HELPER_4(mvcs, i32, env, i64, i64, i64)
DEF_HELPER_4(mvcp, i32, env, i64, i64, i64)
DEF_HELPER_4(sigp, i32, env, i64, i32, i64)
DEF_HELPER_FLAGS_2(sacf, TCG_CALL_NO_WG, void, env, i64)
-DEF_HELPER_FLAGS_3(ipte, TCG_CALL_NO_RWG, void, env, i64, i64)
+DEF_HELPER_FLAGS_4(ipte, TCG_CALL_NO_RWG, void, env, i64, i64, i32)
DEF_HELPER_FLAGS_1(ptlb, TCG_CALL_NO_RWG, void, env)
+DEF_HELPER_FLAGS_1(purge, TCG_CALL_NO_RWG, void, env)
DEF_HELPER_2(lra, i64, env, i64)
DEF_HELPER_FLAGS_2(lura, TCG_CALL_NO_WG, i64, env, i64)
DEF_HELPER_FLAGS_2(lurag, TCG_CALL_NO_WG, i64, env, i64)
diff --git a/target/s390x/insn-data.def b/target/s390x/insn-data.def
index 55a7c52..73dd05d 100644
--- a/target/s390x/insn-data.def
+++ b/target/s390x/insn-data.def
@@ -154,6 +154,12 @@
C(0xb349, CXBR, RRE, Z, x1_o, x2_o, 0, 0, cxb, 0)
C(0xed09, CEB, RXE, Z, e1, m2_32u, 0, 0, ceb, 0)
C(0xed19, CDB, RXE, Z, f1_o, m2_64, 0, 0, cdb, 0)
+/* COMPARE AND SIGNAL */
+ C(0xb308, KEBR, RRE, Z, e1, e2, 0, 0, keb, 0)
+ C(0xb318, KDBR, RRE, Z, f1_o, f2_o, 0, 0, kdb, 0)
+ C(0xb348, KXBR, RRE, Z, x1_o, x2_o, 0, 0, kxb, 0)
+ C(0xed08, KEB, RXE, Z, e1, m2_32u, 0, 0, keb, 0)
+ C(0xed18, KDB, RXE, Z, f1_o, m2_64, 0, 0, kdb, 0)
/* COMPARE IMMEDIATE */
C(0xc20d, CFI, RIL_a, EI, r1, i2, 0, 0, 0, cmps32)
C(0xc20c, CGFI, RIL_a, EI, r1, i2, 0, 0, 0, cmps64)
@@ -210,8 +216,12 @@
C(0xc60e, CLGFRL, RIL_b, GIE, r1_o, mri2_32u, 0, 0, 0, cmpu64)
C(0xc607, CLHRL, RIL_b, GIE, r1_o, mri2_16u, 0, 0, 0, cmpu32)
C(0xc606, CLGHRL, RIL_b, GIE, r1_o, mri2_16u, 0, 0, 0, cmpu64)
+/* COMPARE LOGICAL LONG */
+ C(0x0f00, CLCL, RR_a, Z, 0, 0, 0, 0, clcl, 0)
/* COMPARE LOGICAL LONG EXTENDED */
C(0xa900, CLCLE, RS_a, Z, 0, a2, 0, 0, clcle, 0)
+/* COMPARE LOGICAL LONG UNICODE */
+ C(0xeb8f, CLCLU, RSY_a, E2, 0, a2, 0, 0, clclu, 0)
/* COMPARE LOGICAL CHARACTERS UNDER MASK */
C(0xbd00, CLM, RS_b, Z, r1_o, a2, 0, 0, clm, 0)
C(0xeb21, CLMY, RSY_b, LD, r1_o, a2, 0, 0, clm, 0)
@@ -327,9 +337,9 @@
C(0xeb57, XIY, SIY, LD, m1_8u, i2_8u, new, m1_8, xor, nz64)
/* EXECUTE */
- C(0x4400, EX, RX_a, Z, r1_o, a2, 0, 0, ex, 0)
+ C(0x4400, EX, RX_a, Z, 0, a2, 0, 0, ex, 0)
/* EXECUTE RELATIVE LONG */
- C(0xc600, EXRL, RIL_b, EE, r1_o, ri2, 0, 0, ex, 0)
+ C(0xc600, EXRL, RIL_b, EE, 0, ri2, 0, 0, ex, 0)
/* EXTRACT ACCESS */
C(0xb24f, EAR, RRE, Z, 0, 0, new, r1_32, ear, 0)
@@ -507,6 +517,8 @@
/* LOAD PAIR DISJOINT */
D(0xc804, LPD, SSF, ILA, 0, 0, new_P, r3_P32, lpd, 0, MO_TEUL)
D(0xc805, LPDG, SSF, ILA, 0, 0, new_P, r3_P64, lpd, 0, MO_TEQ)
+/* LOAD PAIR FROM QUADWORD */
+ C(0xe38f, LPQ, RXY_a, Z, 0, a2, r1_P, 0, lpq, 0)
/* LOAD POSITIVE */
C(0x1000, LPR, RR_a, Z, 0, r2_32s, new, r1_32, abs, abs32)
C(0xb900, LPGR, RRE, Z, 0, r2, r1, 0, abs, abs64)
@@ -564,14 +576,26 @@
C(0xe548, MVGHI, SIL, GIE, la1, i2, 0, m1_64, mov2, 0)
C(0x9200, MVI, SI, Z, la1, i2, 0, m1_8, mov2, 0)
C(0xeb52, MVIY, SIY, LD, la1, i2, 0, m1_8, mov2, 0)
+/* MOVE INVERSE */
+ C(0xe800, MVCIN, SS_a, Z, la1, a2, 0, 0, mvcin, 0)
/* MOVE LONG */
C(0x0e00, MVCL, RR_a, Z, 0, 0, 0, 0, mvcl, 0)
/* MOVE LONG EXTENDED */
C(0xa800, MVCLE, RS_a, Z, 0, a2, 0, 0, mvcle, 0)
+/* MOVE LONG UNICODE */
+ C(0xeb8e, MVCLU, RSY_a, E2, 0, a2, 0, 0, mvclu, 0)
+/* MOVE NUMERICS */
+ C(0xd100, MVN, SS_a, Z, la1, a2, 0, 0, mvn, 0)
/* MOVE PAGE */
C(0xb254, MVPG, RRE, Z, r1_o, r2_o, 0, 0, mvpg, 0)
/* MOVE STRING */
C(0xb255, MVST, RRE, Z, r1_o, r2_o, 0, 0, mvst, 0)
+/* MOVE WITH OFFSET */
+ /* Really format SS_b, but we pack both lengths into one argument
+ for the helper call, so we might as well leave one 8-bit field. */
+ C(0xf100, MVO, SS_a, Z, la1, a2, 0, 0, mvo, 0)
+/* MOVE ZONES */
+ C(0xd300, MVZ, SS_a, Z, la1, a2, 0, 0, mvz, 0)
/* MULTIPLY */
C(0x1c00, MR, RR_a, Z, r1p1_32s, r2_32s, new, r1_D32, mul, 0)
@@ -639,6 +663,15 @@
C(0x9600, OI, SI, Z, m1_8u, i2_8u, new, m1_8, or, nz64)
C(0xeb56, OIY, SIY, LD, m1_8u, i2_8u, new, m1_8, or, nz64)
+/* PACK */
+ /* Really format SS_b, but we pack both lengths into one argument
+ for the helper call, so we might as well leave one 8-bit field. */
+ C(0xf200, PACK, SS_a, Z, la1, a2, 0, 0, pack, 0)
+/* PACK ASCII */
+ C(0xe900, PKA, SS_f, E2, la1, a2, 0, 0, pka, 0)
+/* PACK UNICODE */
+ C(0xe100, PKU, SS_f, E2, la1, a2, 0, 0, pku, 0)
+
/* PREFETCH */
/* Implemented as nops of course. */
C(0xe336, PFD, RXY_b, GIE, 0, 0, 0, 0, 0, 0)
@@ -763,6 +796,8 @@
/* STORE ACCESS MULTIPLE */
C(0x9b00, STAM, RS_a, Z, 0, a2, 0, 0, stam, 0)
C(0xeb9b, STAMY, RSY_a, LD, 0, a2, 0, 0, stam, 0)
+/* STORE PAIR TO QUADWORD */
+ C(0xe38e, STPQ, RXY_a, Z, 0, a2, r1_P, 0, stpq, 0)
/* SUBTRACT */
C(0x1b00, SR, RR_a, Z, r1, r2, new, r1_32, sub, subs32)
@@ -810,11 +845,20 @@
/* SUPERVISOR CALL */
C(0x0a00, SVC, I, Z, 0, 0, 0, 0, svc, 0)
+/* TEST ADDRESSING MODE */
+ C(0x010b, TAM, E, Z, 0, 0, 0, 0, tam, 0)
+
+/* TEST AND SET */
+ C(0x9300, TS, S, Z, 0, a2, 0, 0, ts, 0)
+
/* TEST DATA CLASS */
C(0xed10, TCEB, RXE, Z, e1, a2, 0, 0, tceb, 0)
C(0xed11, TCDB, RXE, Z, f1_o, a2, 0, 0, tcdb, 0)
C(0xed12, TCXB, RXE, Z, x1_o, a2, 0, 0, tcxb, 0)
+/* TEST DECIMAL */
+ C(0xebc0, TP, RSL, E2, la1, 0, 0, 0, tp, 0)
+
/* TEST UNDER MASK */
C(0x9100, TM, SI, Z, m1_8u, i2_8u, 0, 0, 0, tm32)
C(0xeb51, TMY, SIY, LD, m1_8u, i2_8u, 0, 0, 0, tm32)
@@ -830,14 +874,28 @@
/* TRANSLATE EXTENDED */
C(0xb2a5, TRE, RRE, Z, 0, r2, r1_P, 0, tre, 0)
+/* TRANSLATE ONE TO ONE */
+ C(0xb993, TROO, RRF_c, E2, 0, 0, 0, 0, trXX, 0)
+/* TRANSLATE ONE TO TWO */
+ C(0xb992, TROT, RRF_c, E2, 0, 0, 0, 0, trXX, 0)
+/* TRANSLATE TWO TO ONE */
+ C(0xb991, TRTO, RRF_c, E2, 0, 0, 0, 0, trXX, 0)
+/* TRANSLATE TWO TO TWO */
+ C(0xb990, TRTT, RRF_c, E2, 0, 0, 0, 0, trXX, 0)
+
/* UNPACK */
/* Really format SS_b, but we pack both lengths into one argument
for the helper call, so we might as well leave one 8-bit field. */
C(0xf300, UNPK, SS_a, Z, la1, a2, 0, 0, unpk, 0)
+/* UNPACK ASCII */
+ C(0xea00, UNPKA, SS_a, E2, la1, a2, 0, 0, unpka, 0)
+/* UNPACK UNICODE */
+ C(0xe200, UNPKU, SS_a, E2, la1, a2, 0, 0, unpku, 0)
#ifndef CONFIG_USER_ONLY
/* COMPARE AND SWAP AND PURGE */
- C(0xb250, CSP, RRE, Z, 0, ra2, 0, 0, csp, 0)
+ D(0xb250, CSP, RRE, Z, r1_32u, ra2, r1_P, 0, csp, 0, MO_TEUL)
+ D(0xb98a, CSPG, RRE, DAT_ENH, r1_o, ra2, r1_P, 0, csp, 0, MO_TEQ)
/* DIAGNOSE (KVM hypercall) */
C(0x8300, DIAG, RSI, Z, 0, 0, 0, 0, diag, 0)
/* INSERT STORAGE KEY EXTENDED */
@@ -918,6 +976,8 @@
/* STORE USING REAL ADDRESS */
C(0xb246, STURA, RRE, Z, r1_o, r2_o, 0, 0, stura, 0)
C(0xb925, STURG, RRE, Z, r1_o, r2_o, 0, 0, sturg, 0)
+/* TEST BLOCK */
+ C(0xb22c, TB, RRE, Z, 0, r2_o, 0, 0, testblock, 0)
/* TEST PROTECTION */
C(0xe501, TPROT, SSE, Z, la1, a2, 0, 0, tprot, 0)
diff --git a/target/s390x/machine.c b/target/s390x/machine.c
index 8503fa1..8f908bb 100644
--- a/target/s390x/machine.c
+++ b/target/s390x/machine.c
@@ -34,6 +34,7 @@ static int cpu_post_load(void *opaque, int version_id)
return 0;
}
+
static void cpu_pre_save(void *opaque)
{
S390CPU *cpu = opaque;
@@ -156,6 +157,23 @@ const VMStateDescription vmstate_riccb = {
}
};
+static bool exval_needed(void *opaque)
+{
+ S390CPU *cpu = opaque;
+ return cpu->env.ex_value != 0;
+}
+
+const VMStateDescription vmstate_exval = {
+ .name = "cpu/exval",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = exval_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(env.ex_value, S390CPU),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
const VMStateDescription vmstate_s390_cpu = {
.name = "cpu",
.post_load = cpu_post_load,
@@ -188,6 +206,7 @@ const VMStateDescription vmstate_s390_cpu = {
&vmstate_fpu,
&vmstate_vregs,
&vmstate_riccb,
+ &vmstate_exval,
NULL
},
};
diff --git a/target/s390x/mem_helper.c b/target/s390x/mem_helper.c
index f6e5bce..80caab9 100644
--- a/target/s390x/mem_helper.c
+++ b/target/s390x/mem_helper.c
@@ -20,6 +20,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
+#include "exec/address-spaces.h"
#include "exec/helper-proto.h"
#include "exec/exec-all.h"
#include "exec/cpu_ldst.h"
@@ -40,15 +41,9 @@
void tlb_fill(CPUState *cs, target_ulong addr, MMUAccessType access_type,
int mmu_idx, uintptr_t retaddr)
{
- int ret;
-
- ret = s390_cpu_handle_mmu_fault(cs, addr, access_type, mmu_idx);
+ int ret = s390_cpu_handle_mmu_fault(cs, addr, access_type, mmu_idx);
if (unlikely(ret != 0)) {
- if (likely(retaddr)) {
- /* now we have a real cpu fault */
- cpu_restore_state(cs, retaddr);
- }
- cpu_loop_exit(cs);
+ cpu_loop_exit_restore(cs, retaddr);
}
}
@@ -62,18 +57,61 @@ void tlb_fill(CPUState *cs, target_ulong addr, MMUAccessType access_type,
#endif
/* Reduce the length so that addr + len doesn't cross a page boundary. */
-static inline uint64_t adj_len_to_page(uint64_t len, uint64_t addr)
+static inline uint32_t adj_len_to_page(uint32_t len, uint64_t addr)
{
#ifndef CONFIG_USER_ONLY
if ((addr & ~TARGET_PAGE_MASK) + len - 1 >= TARGET_PAGE_SIZE) {
- return -addr & ~TARGET_PAGE_MASK;
+ return -(addr | TARGET_PAGE_MASK);
}
#endif
return len;
}
+/* Trigger a SPECIFICATION exception if an address or a length is not
+ naturally aligned. */
+static inline void check_alignment(CPUS390XState *env, uint64_t v,
+ int wordsize, uintptr_t ra)
+{
+ if (v % wordsize) {
+ CPUState *cs = CPU(s390_env_get_cpu(env));
+ cpu_restore_state(cs, ra);
+ program_interrupt(env, PGM_SPECIFICATION, 6);
+ }
+}
+
+/* Load a value from memory according to its size. */
+static inline uint64_t cpu_ldusize_data_ra(CPUS390XState *env, uint64_t addr,
+ int wordsize, uintptr_t ra)
+{
+ switch (wordsize) {
+ case 1:
+ return cpu_ldub_data_ra(env, addr, ra);
+ case 2:
+ return cpu_lduw_data_ra(env, addr, ra);
+ default:
+ abort();
+ }
+}
+
+/* Store a to memory according to its size. */
+static inline void cpu_stsize_data_ra(CPUS390XState *env, uint64_t addr,
+ uint64_t value, int wordsize,
+ uintptr_t ra)
+{
+ switch (wordsize) {
+ case 1:
+ cpu_stb_data_ra(env, addr, value, ra);
+ break;
+ case 2:
+ cpu_stw_data_ra(env, addr, value, ra);
+ break;
+ default:
+ abort();
+ }
+}
+
static void fast_memset(CPUS390XState *env, uint64_t dest, uint8_t byte,
- uint32_t l)
+ uint32_t l, uintptr_t ra)
{
int mmu_idx = cpu_mmu_index(env, false);
@@ -81,14 +119,14 @@ static void fast_memset(CPUS390XState *env, uint64_t dest, uint8_t byte,
void *p = tlb_vaddr_to_host(env, dest, MMU_DATA_STORE, mmu_idx);
if (p) {
/* Access to the whole page in write mode granted. */
- int l_adj = adj_len_to_page(l, dest);
+ uint32_t l_adj = adj_len_to_page(l, dest);
memset(p, byte, l_adj);
dest += l_adj;
l -= l_adj;
} else {
/* We failed to get access to the whole page. The next write
access will likely fill the QEMU TLB for the next iteration. */
- cpu_stb_data(env, dest, byte);
+ cpu_stb_data_ra(env, dest, byte, ra);
dest++;
l--;
}
@@ -96,7 +134,7 @@ static void fast_memset(CPUS390XState *env, uint64_t dest, uint8_t byte,
}
static void fast_memmove(CPUS390XState *env, uint64_t dest, uint64_t src,
- uint32_t l)
+ uint32_t l, uintptr_t ra)
{
int mmu_idx = cpu_mmu_index(env, false);
@@ -105,7 +143,7 @@ static void fast_memmove(CPUS390XState *env, uint64_t dest, uint64_t src,
void *dest_p = tlb_vaddr_to_host(env, dest, MMU_DATA_STORE, mmu_idx);
if (src_p && dest_p) {
/* Access to both whole pages granted. */
- int l_adj = adj_len_to_page(l, src);
+ uint32_t l_adj = adj_len_to_page(l, src);
l_adj = adj_len_to_page(l_adj, dest);
memmove(dest_p, src_p, l_adj);
src += l_adj;
@@ -115,7 +153,7 @@ static void fast_memmove(CPUS390XState *env, uint64_t dest, uint64_t src,
/* We failed to get access to one or both whole pages. The next
read or write access will likely fill the QEMU TLB for the
next iteration. */
- cpu_stb_data(env, dest, cpu_ldub_data(env, src));
+ cpu_stb_data_ra(env, dest, cpu_ldub_data_ra(env, src, ra), ra);
src++;
dest++;
l--;
@@ -124,140 +162,233 @@ static void fast_memmove(CPUS390XState *env, uint64_t dest, uint64_t src,
}
/* and on array */
-uint32_t HELPER(nc)(CPUS390XState *env, uint32_t l, uint64_t dest,
- uint64_t src)
+static uint32_t do_helper_nc(CPUS390XState *env, uint32_t l, uint64_t dest,
+ uint64_t src, uintptr_t ra)
{
- int i;
- unsigned char x;
- uint32_t cc = 0;
+ uint32_t i;
+ uint8_t c = 0;
HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
__func__, l, dest, src);
+
for (i = 0; i <= l; i++) {
- x = cpu_ldub_data(env, dest + i) & cpu_ldub_data(env, src + i);
- if (x) {
- cc = 1;
- }
- cpu_stb_data(env, dest + i, x);
+ uint8_t x = cpu_ldub_data_ra(env, src + i, ra);
+ x &= cpu_ldub_data_ra(env, dest + i, ra);
+ c |= x;
+ cpu_stb_data_ra(env, dest + i, x, ra);
}
- return cc;
+ return c != 0;
}
-/* xor on array */
-uint32_t HELPER(xc)(CPUS390XState *env, uint32_t l, uint64_t dest,
+uint32_t HELPER(nc)(CPUS390XState *env, uint32_t l, uint64_t dest,
uint64_t src)
{
- int i;
- unsigned char x;
- uint32_t cc = 0;
+ return do_helper_nc(env, l, dest, src, GETPC());
+}
+
+/* xor on array */
+static uint32_t do_helper_xc(CPUS390XState *env, uint32_t l, uint64_t dest,
+ uint64_t src, uintptr_t ra)
+{
+ uint32_t i;
+ uint8_t c = 0;
HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
__func__, l, dest, src);
/* xor with itself is the same as memset(0) */
if (src == dest) {
- fast_memset(env, dest, 0, l + 1);
+ fast_memset(env, dest, 0, l + 1, ra);
return 0;
}
for (i = 0; i <= l; i++) {
- x = cpu_ldub_data(env, dest + i) ^ cpu_ldub_data(env, src + i);
- if (x) {
- cc = 1;
- }
- cpu_stb_data(env, dest + i, x);
+ uint8_t x = cpu_ldub_data_ra(env, src + i, ra);
+ x ^= cpu_ldub_data_ra(env, dest + i, ra);
+ c |= x;
+ cpu_stb_data_ra(env, dest + i, x, ra);
}
- return cc;
+ return c != 0;
}
-/* or on array */
-uint32_t HELPER(oc)(CPUS390XState *env, uint32_t l, uint64_t dest,
+uint32_t HELPER(xc)(CPUS390XState *env, uint32_t l, uint64_t dest,
uint64_t src)
{
- int i;
- unsigned char x;
- uint32_t cc = 0;
+ return do_helper_xc(env, l, dest, src, GETPC());
+}
+
+/* or on array */
+static uint32_t do_helper_oc(CPUS390XState *env, uint32_t l, uint64_t dest,
+ uint64_t src, uintptr_t ra)
+{
+ uint32_t i;
+ uint8_t c = 0;
HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
__func__, l, dest, src);
+
for (i = 0; i <= l; i++) {
- x = cpu_ldub_data(env, dest + i) | cpu_ldub_data(env, src + i);
- if (x) {
- cc = 1;
- }
- cpu_stb_data(env, dest + i, x);
+ uint8_t x = cpu_ldub_data_ra(env, src + i, ra);
+ x |= cpu_ldub_data_ra(env, dest + i, ra);
+ c |= x;
+ cpu_stb_data_ra(env, dest + i, x, ra);
}
- return cc;
+ return c != 0;
+}
+
+uint32_t HELPER(oc)(CPUS390XState *env, uint32_t l, uint64_t dest,
+ uint64_t src)
+{
+ return do_helper_oc(env, l, dest, src, GETPC());
}
/* memmove */
-void HELPER(mvc)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src)
+static uint32_t do_helper_mvc(CPUS390XState *env, uint32_t l, uint64_t dest,
+ uint64_t src, uintptr_t ra)
{
- int i = 0;
+ uint32_t i;
HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
__func__, l, dest, src);
+ /* mvc and memmove do not behave the same when areas overlap! */
/* mvc with source pointing to the byte after the destination is the
same as memset with the first source byte */
- if (dest == (src + 1)) {
- fast_memset(env, dest, cpu_ldub_data(env, src), l + 1);
- return;
+ if (dest == src + 1) {
+ fast_memset(env, dest, cpu_ldub_data_ra(env, src, ra), l + 1, ra);
+ } else if (dest < src || src + l < dest) {
+ fast_memmove(env, dest, src, l + 1, ra);
+ } else {
+ /* slow version with byte accesses which always work */
+ for (i = 0; i <= l; i++) {
+ uint8_t x = cpu_ldub_data_ra(env, src + i, ra);
+ cpu_stb_data_ra(env, dest + i, x, ra);
+ }
}
- /* mvc and memmove do not behave the same when areas overlap! */
- if ((dest < src) || (src + l < dest)) {
- fast_memmove(env, dest, src, l + 1);
- return;
+ return env->cc_op;
+}
+
+void HELPER(mvc)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src)
+{
+ do_helper_mvc(env, l, dest, src, GETPC());
+}
+
+/* move inverse */
+void HELPER(mvcin)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src)
+{
+ uintptr_t ra = GETPC();
+ int i;
+
+ for (i = 0; i <= l; i++) {
+ uint8_t v = cpu_ldub_data_ra(env, src - i, ra);
+ cpu_stb_data_ra(env, dest + i, v, ra);
}
+}
+
+/* move numerics */
+void HELPER(mvn)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src)
+{
+ uintptr_t ra = GETPC();
+ int i;
- /* slow version with byte accesses which always work */
for (i = 0; i <= l; i++) {
- cpu_stb_data(env, dest + i, cpu_ldub_data(env, src + i));
+ uint8_t v = cpu_ldub_data_ra(env, dest + i, ra) & 0xf0;
+ v |= cpu_ldub_data_ra(env, src + i, ra) & 0x0f;
+ cpu_stb_data_ra(env, dest + i, v, ra);
}
}
-/* compare unsigned byte arrays */
-uint32_t HELPER(clc)(CPUS390XState *env, uint32_t l, uint64_t s1, uint64_t s2)
+/* move with offset */
+void HELPER(mvo)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src)
{
+ uintptr_t ra = GETPC();
+ int len_dest = l >> 4;
+ int len_src = l & 0xf;
+ uint8_t byte_dest, byte_src;
int i;
- unsigned char x, y;
- uint32_t cc;
+
+ src += len_src;
+ dest += len_dest;
+
+ /* Handle rightmost byte */
+ byte_src = cpu_ldub_data_ra(env, src, ra);
+ byte_dest = cpu_ldub_data_ra(env, dest, ra);
+ byte_dest = (byte_dest & 0x0f) | (byte_src << 4);
+ cpu_stb_data_ra(env, dest, byte_dest, ra);
+
+ /* Process remaining bytes from right to left */
+ for (i = 1; i <= len_dest; i++) {
+ byte_dest = byte_src >> 4;
+ if (len_src - i >= 0) {
+ byte_src = cpu_ldub_data_ra(env, src - i, ra);
+ } else {
+ byte_src = 0;
+ }
+ byte_dest |= byte_src << 4;
+ cpu_stb_data_ra(env, dest - i, byte_dest, ra);
+ }
+}
+
+/* move zones */
+void HELPER(mvz)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src)
+{
+ uintptr_t ra = GETPC();
+ int i;
+
+ for (i = 0; i <= l; i++) {
+ uint8_t b = cpu_ldub_data_ra(env, dest + i, ra) & 0x0f;
+ b |= cpu_ldub_data_ra(env, src + i, ra) & 0xf0;
+ cpu_stb_data_ra(env, dest + i, b, ra);
+ }
+}
+
+/* compare unsigned byte arrays */
+static uint32_t do_helper_clc(CPUS390XState *env, uint32_t l, uint64_t s1,
+ uint64_t s2, uintptr_t ra)
+{
+ uint32_t i;
+ uint32_t cc = 0;
HELPER_LOG("%s l %d s1 %" PRIx64 " s2 %" PRIx64 "\n",
__func__, l, s1, s2);
+
for (i = 0; i <= l; i++) {
- x = cpu_ldub_data(env, s1 + i);
- y = cpu_ldub_data(env, s2 + i);
+ uint8_t x = cpu_ldub_data_ra(env, s1 + i, ra);
+ uint8_t y = cpu_ldub_data_ra(env, s2 + i, ra);
HELPER_LOG("%02x (%c)/%02x (%c) ", x, x, y, y);
if (x < y) {
cc = 1;
- goto done;
+ break;
} else if (x > y) {
cc = 2;
- goto done;
+ break;
}
}
- cc = 0;
- done:
+
HELPER_LOG("\n");
return cc;
}
+uint32_t HELPER(clc)(CPUS390XState *env, uint32_t l, uint64_t s1, uint64_t s2)
+{
+ return do_helper_clc(env, l, s1, s2, GETPC());
+}
+
/* compare logical under mask */
uint32_t HELPER(clm)(CPUS390XState *env, uint32_t r1, uint32_t mask,
uint64_t addr)
{
- uint8_t r, d;
- uint32_t cc;
+ uintptr_t ra = GETPC();
+ uint32_t cc = 0;
HELPER_LOG("%s: r1 0x%x mask 0x%x addr 0x%" PRIx64 "\n", __func__, r1,
mask, addr);
- cc = 0;
+
while (mask) {
if (mask & 8) {
- d = cpu_ldub_data(env, addr);
- r = (r1 & 0xff000000UL) >> 24;
+ uint8_t d = cpu_ldub_data_ra(env, addr, ra);
+ uint8_t r = extract32(r1, 24, 8);
HELPER_LOG("mask 0x%x %02x/%02x (0x%" PRIx64 ") ", mask, r, d,
addr);
if (r < d) {
@@ -272,45 +403,88 @@ uint32_t HELPER(clm)(CPUS390XState *env, uint32_t r1, uint32_t mask,
mask = (mask << 1) & 0xf;
r1 <<= 8;
}
+
HELPER_LOG("\n");
return cc;
}
-static inline uint64_t fix_address(CPUS390XState *env, uint64_t a)
+static inline uint64_t wrap_address(CPUS390XState *env, uint64_t a)
{
- /* 31-Bit mode */
if (!(env->psw.mask & PSW_MASK_64)) {
- a &= 0x7fffffff;
+ if (!(env->psw.mask & PSW_MASK_32)) {
+ /* 24-Bit mode */
+ a &= 0x00ffffff;
+ } else {
+ /* 31-Bit mode */
+ a &= 0x7fffffff;
+ }
}
return a;
}
-static inline uint64_t get_address(CPUS390XState *env, int x2, int b2, int d2)
+static inline uint64_t get_address(CPUS390XState *env, int reg)
+{
+ return wrap_address(env, env->regs[reg]);
+}
+
+static inline void set_address(CPUS390XState *env, int reg, uint64_t address)
{
- uint64_t r = d2;
- if (x2) {
- r += env->regs[x2];
+ if (env->psw.mask & PSW_MASK_64) {
+ /* 64-Bit mode */
+ env->regs[reg] = address;
+ } else {
+ if (!(env->psw.mask & PSW_MASK_32)) {
+ /* 24-Bit mode. According to the PoO it is implementation
+ dependent if bits 32-39 remain unchanged or are set to
+ zeros. Choose the former so that the function can also be
+ used for TRT. */
+ env->regs[reg] = deposit64(env->regs[reg], 0, 24, address);
+ } else {
+ /* 31-Bit mode. According to the PoO it is implementation
+ dependent if bit 32 remains unchanged or is set to zero.
+ Choose the latter so that the function can also be used for
+ TRT. */
+ address &= 0x7fffffff;
+ env->regs[reg] = deposit64(env->regs[reg], 0, 32, address);
+ }
}
- if (b2) {
- r += env->regs[b2];
+}
+
+static inline uint64_t wrap_length(CPUS390XState *env, uint64_t length)
+{
+ if (!(env->psw.mask & PSW_MASK_64)) {
+ /* 24-Bit and 31-Bit mode */
+ length &= 0x7fffffff;
}
- return fix_address(env, r);
+ return length;
}
-static inline uint64_t get_address_31fix(CPUS390XState *env, int reg)
+static inline uint64_t get_length(CPUS390XState *env, int reg)
{
- return fix_address(env, env->regs[reg]);
+ return wrap_length(env, env->regs[reg]);
+}
+
+static inline void set_length(CPUS390XState *env, int reg, uint64_t length)
+{
+ if (env->psw.mask & PSW_MASK_64) {
+ /* 64-Bit mode */
+ env->regs[reg] = length;
+ } else {
+ /* 24-Bit and 31-Bit mode */
+ env->regs[reg] = deposit64(env->regs[reg], 0, 32, length);
+ }
}
/* search string (c is byte to search, r2 is string, r1 end of string) */
uint64_t HELPER(srst)(CPUS390XState *env, uint64_t r0, uint64_t end,
uint64_t str)
{
+ uintptr_t ra = GETPC();
uint32_t len;
uint8_t v, c = r0;
- str = fix_address(env, str);
- end = fix_address(env, end);
+ str = wrap_address(env, str);
+ end = wrap_address(env, end);
/* Assume for now that R2 is unmodified. */
env->retxl = str;
@@ -323,7 +497,7 @@ uint64_t HELPER(srst)(CPUS390XState *env, uint64_t r0, uint64_t end,
env->cc_op = 2;
return end;
}
- v = cpu_ldub_data(env, str + len);
+ v = cpu_ldub_data_ra(env, str + len, ra);
if (v == c) {
/* Character found. Set R1 to the location; R2 is unmodified. */
env->cc_op = 1;
@@ -340,17 +514,18 @@ uint64_t HELPER(srst)(CPUS390XState *env, uint64_t r0, uint64_t end,
/* unsigned string compare (c is string terminator) */
uint64_t HELPER(clst)(CPUS390XState *env, uint64_t c, uint64_t s1, uint64_t s2)
{
+ uintptr_t ra = GETPC();
uint32_t len;
c = c & 0xff;
- s1 = fix_address(env, s1);
- s2 = fix_address(env, s2);
+ s1 = wrap_address(env, s1);
+ s2 = wrap_address(env, s2);
/* Lest we fail to service interrupts in a timely manner, limit the
amount of work we're willing to do. For now, let's cap at 8k. */
for (len = 0; len < 0x2000; ++len) {
- uint8_t v1 = cpu_ldub_data(env, s1 + len);
- uint8_t v2 = cpu_ldub_data(env, s2 + len);
+ uint8_t v1 = cpu_ldub_data_ra(env, s1 + len, ra);
+ uint8_t v2 = cpu_ldub_data_ra(env, s2 + len, ra);
if (v1 == v2) {
if (v1 == c) {
/* Equal. CC=0, and don't advance the registers. */
@@ -375,27 +550,29 @@ uint64_t HELPER(clst)(CPUS390XState *env, uint64_t c, uint64_t s1, uint64_t s2)
}
/* move page */
-void HELPER(mvpg)(CPUS390XState *env, uint64_t r0, uint64_t r1, uint64_t r2)
+uint32_t HELPER(mvpg)(CPUS390XState *env, uint64_t r0, uint64_t r1, uint64_t r2)
{
- /* XXX missing r0 handling */
- env->cc_op = 0;
- fast_memmove(env, r1, r2, TARGET_PAGE_SIZE);
+ /* ??? missing r0 handling, which includes access keys, but more
+ importantly optional suppression of the exception! */
+ fast_memmove(env, r1, r2, TARGET_PAGE_SIZE, GETPC());
+ return 0; /* data moved */
}
/* string copy (c is string terminator) */
uint64_t HELPER(mvst)(CPUS390XState *env, uint64_t c, uint64_t d, uint64_t s)
{
+ uintptr_t ra = GETPC();
uint32_t len;
c = c & 0xff;
- d = fix_address(env, d);
- s = fix_address(env, s);
+ d = wrap_address(env, d);
+ s = wrap_address(env, s);
/* Lest we fail to service interrupts in a timely manner, limit the
amount of work we're willing to do. For now, let's cap at 8k. */
for (len = 0; len < 0x2000; ++len) {
- uint8_t v = cpu_ldub_data(env, s + len);
- cpu_stb_data(env, d + len, v);
+ uint8_t v = cpu_ldub_data_ra(env, s + len, ra);
+ cpu_stb_data_ra(env, d + len, v, ra);
if (v == c) {
/* Complete. Set CC=1 and advance R1. */
env->cc_op = 1;
@@ -410,124 +587,14 @@ uint64_t HELPER(mvst)(CPUS390XState *env, uint64_t c, uint64_t d, uint64_t s)
return d + len;
}
-static uint32_t helper_icm(CPUS390XState *env, uint32_t r1, uint64_t address,
- uint32_t mask)
-{
- int pos = 24; /* top of the lower half of r1 */
- uint64_t rmask = 0xff000000ULL;
- uint8_t val = 0;
- int ccd = 0;
- uint32_t cc = 0;
-
- while (mask) {
- if (mask & 8) {
- env->regs[r1] &= ~rmask;
- val = cpu_ldub_data(env, address);
- if ((val & 0x80) && !ccd) {
- cc = 1;
- }
- ccd = 1;
- if (val && cc == 0) {
- cc = 2;
- }
- env->regs[r1] |= (uint64_t)val << pos;
- address++;
- }
- mask = (mask << 1) & 0xf;
- pos -= 8;
- rmask >>= 8;
- }
-
- return cc;
-}
-
-/* execute instruction
- this instruction executes an insn modified with the contents of r1
- it does not change the executed instruction in memory
- it does not change the program counter
- in other words: tricky...
- currently implemented by interpreting the cases it is most commonly used in
-*/
-uint32_t HELPER(ex)(CPUS390XState *env, uint32_t cc, uint64_t v1,
- uint64_t addr, uint64_t ret)
-{
- S390CPU *cpu = s390_env_get_cpu(env);
- uint16_t insn = cpu_lduw_code(env, addr);
-
- HELPER_LOG("%s: v1 0x%lx addr 0x%lx insn 0x%x\n", __func__, v1, addr,
- insn);
- if ((insn & 0xf0ff) == 0xd000) {
- uint32_t l, insn2, b1, b2, d1, d2;
-
- l = v1 & 0xff;
- insn2 = cpu_ldl_code(env, addr + 2);
- b1 = (insn2 >> 28) & 0xf;
- b2 = (insn2 >> 12) & 0xf;
- d1 = (insn2 >> 16) & 0xfff;
- d2 = insn2 & 0xfff;
- switch (insn & 0xf00) {
- case 0x200:
- helper_mvc(env, l, get_address(env, 0, b1, d1),
- get_address(env, 0, b2, d2));
- break;
- case 0x400:
- cc = helper_nc(env, l, get_address(env, 0, b1, d1),
- get_address(env, 0, b2, d2));
- break;
- case 0x500:
- cc = helper_clc(env, l, get_address(env, 0, b1, d1),
- get_address(env, 0, b2, d2));
- break;
- case 0x600:
- cc = helper_oc(env, l, get_address(env, 0, b1, d1),
- get_address(env, 0, b2, d2));
- break;
- case 0x700:
- cc = helper_xc(env, l, get_address(env, 0, b1, d1),
- get_address(env, 0, b2, d2));
- break;
- case 0xc00:
- helper_tr(env, l, get_address(env, 0, b1, d1),
- get_address(env, 0, b2, d2));
- break;
- case 0xd00:
- cc = helper_trt(env, l, get_address(env, 0, b1, d1),
- get_address(env, 0, b2, d2));
- break;
- default:
- goto abort;
- }
- } else if ((insn & 0xff00) == 0x0a00) {
- /* supervisor call */
- HELPER_LOG("%s: svc %ld via execute\n", __func__, (insn | v1) & 0xff);
- env->psw.addr = ret - 4;
- env->int_svc_code = (insn | v1) & 0xff;
- env->int_svc_ilen = 4;
- helper_exception(env, EXCP_SVC);
- } else if ((insn & 0xff00) == 0xbf00) {
- uint32_t insn2, r1, r3, b2, d2;
-
- insn2 = cpu_ldl_code(env, addr + 2);
- r1 = (insn2 >> 20) & 0xf;
- r3 = (insn2 >> 16) & 0xf;
- b2 = (insn2 >> 12) & 0xf;
- d2 = insn2 & 0xfff;
- cc = helper_icm(env, r1, get_address(env, 0, b2, d2), r3);
- } else {
- abort:
- cpu_abort(CPU(cpu), "EXECUTE on instruction prefix 0x%x not implemented\n",
- insn);
- }
- return cc;
-}
-
/* load access registers r1 to r3 from memory at a2 */
void HELPER(lam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
{
+ uintptr_t ra = GETPC();
int i;
for (i = r1;; i = (i + 1) % 16) {
- env->aregs[i] = cpu_ldl_data(env, a2);
+ env->aregs[i] = cpu_ldl_data_ra(env, a2, ra);
a2 += 4;
if (i == r3) {
@@ -539,10 +606,11 @@ void HELPER(lam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
/* store access registers r1 to r3 in memory at a2 */
void HELPER(stam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
{
+ uintptr_t ra = GETPC();
int i;
for (i = r1;; i = (i + 1) % 16) {
- cpu_stl_data(env, a2, env->aregs[i]);
+ cpu_stl_data_ra(env, a2, env->aregs[i], ra);
a2 += 4;
if (i == r3) {
@@ -551,131 +619,230 @@ void HELPER(stam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
}
}
-/* move long */
-uint32_t HELPER(mvcl)(CPUS390XState *env, uint32_t r1, uint32_t r2)
+/* move long helper */
+static inline uint32_t do_mvcl(CPUS390XState *env,
+ uint64_t *dest, uint64_t *destlen,
+ uint64_t *src, uint64_t *srclen,
+ uint16_t pad, int wordsize, uintptr_t ra)
{
- uint64_t destlen = env->regs[r1 + 1] & 0xffffff;
- uint64_t dest = get_address_31fix(env, r1);
- uint64_t srclen = env->regs[r2 + 1] & 0xffffff;
- uint64_t src = get_address_31fix(env, r2);
- uint8_t pad = env->regs[r2 + 1] >> 24;
- uint8_t v;
+ uint64_t len = MIN(*srclen, *destlen);
uint32_t cc;
- if (destlen == srclen) {
+ if (*destlen == *srclen) {
cc = 0;
- } else if (destlen < srclen) {
+ } else if (*destlen < *srclen) {
cc = 1;
} else {
cc = 2;
}
- if (srclen > destlen) {
- srclen = destlen;
- }
+ /* Copy the src array */
+ fast_memmove(env, *dest, *src, len, ra);
+ *src += len;
+ *srclen -= len;
+ *dest += len;
+ *destlen -= len;
- for (; destlen && srclen; src++, dest++, destlen--, srclen--) {
- v = cpu_ldub_data(env, src);
- cpu_stb_data(env, dest, v);
+ /* Pad the remaining area */
+ if (wordsize == 1) {
+ fast_memset(env, *dest, pad, *destlen, ra);
+ *dest += *destlen;
+ *destlen = 0;
+ } else {
+ /* If remaining length is odd, pad with odd byte first. */
+ if (*destlen & 1) {
+ cpu_stb_data_ra(env, *dest, pad & 0xff, ra);
+ *dest += 1;
+ *destlen -= 1;
+ }
+ /* The remaining length is even, pad using words. */
+ for (; *destlen; *dest += 2, *destlen -= 2) {
+ cpu_stw_data_ra(env, *dest, pad, ra);
+ }
}
- for (; destlen; dest++, destlen--) {
- cpu_stb_data(env, dest, pad);
- }
+ return cc;
+}
+
+/* move long */
+uint32_t HELPER(mvcl)(CPUS390XState *env, uint32_t r1, uint32_t r2)
+{
+ uintptr_t ra = GETPC();
+ uint64_t destlen = env->regs[r1 + 1] & 0xffffff;
+ uint64_t dest = get_address(env, r1);
+ uint64_t srclen = env->regs[r2 + 1] & 0xffffff;
+ uint64_t src = get_address(env, r2);
+ uint8_t pad = env->regs[r2 + 1] >> 24;
+ uint32_t cc;
- env->regs[r1 + 1] = destlen;
- /* can't use srclen here, we trunc'ed it */
- env->regs[r2 + 1] -= src - env->regs[r2];
- env->regs[r1] = dest;
- env->regs[r2] = src;
+ cc = do_mvcl(env, &dest, &destlen, &src, &srclen, pad, 1, ra);
+
+ env->regs[r1 + 1] = deposit64(env->regs[r1 + 1], 0, 24, destlen);
+ env->regs[r2 + 1] = deposit64(env->regs[r2 + 1], 0, 24, srclen);
+ set_address(env, r1, dest);
+ set_address(env, r2, src);
return cc;
}
-/* move long extended another memcopy insn with more bells and whistles */
+/* move long extended */
uint32_t HELPER(mvcle)(CPUS390XState *env, uint32_t r1, uint64_t a2,
uint32_t r3)
{
- uint64_t destlen = env->regs[r1 + 1];
- uint64_t dest = env->regs[r1];
- uint64_t srclen = env->regs[r3 + 1];
- uint64_t src = env->regs[r3];
- uint8_t pad = a2 & 0xff;
- uint8_t v;
+ uintptr_t ra = GETPC();
+ uint64_t destlen = get_length(env, r1 + 1);
+ uint64_t dest = get_address(env, r1);
+ uint64_t srclen = get_length(env, r3 + 1);
+ uint64_t src = get_address(env, r3);
+ uint8_t pad = a2;
uint32_t cc;
- if (!(env->psw.mask & PSW_MASK_64)) {
- destlen = (uint32_t)destlen;
- srclen = (uint32_t)srclen;
- dest &= 0x7fffffff;
- src &= 0x7fffffff;
- }
+ cc = do_mvcl(env, &dest, &destlen, &src, &srclen, pad, 1, ra);
- if (destlen == srclen) {
- cc = 0;
- } else if (destlen < srclen) {
- cc = 1;
- } else {
- cc = 2;
- }
+ set_length(env, r1 + 1, destlen);
+ set_length(env, r3 + 1, srclen);
+ set_address(env, r1, dest);
+ set_address(env, r3, src);
- if (srclen > destlen) {
- srclen = destlen;
- }
+ return cc;
+}
- for (; destlen && srclen; src++, dest++, destlen--, srclen--) {
- v = cpu_ldub_data(env, src);
- cpu_stb_data(env, dest, v);
- }
+/* move long unicode */
+uint32_t HELPER(mvclu)(CPUS390XState *env, uint32_t r1, uint64_t a2,
+ uint32_t r3)
+{
+ uintptr_t ra = GETPC();
+ uint64_t destlen = get_length(env, r1 + 1);
+ uint64_t dest = get_address(env, r1);
+ uint64_t srclen = get_length(env, r3 + 1);
+ uint64_t src = get_address(env, r3);
+ uint16_t pad = a2;
+ uint32_t cc;
- for (; destlen; dest++, destlen--) {
- cpu_stb_data(env, dest, pad);
- }
+ cc = do_mvcl(env, &dest, &destlen, &src, &srclen, pad, 2, ra);
- env->regs[r1 + 1] = destlen;
- /* can't use srclen here, we trunc'ed it */
- /* FIXME: 31-bit mode! */
- env->regs[r3 + 1] -= src - env->regs[r3];
- env->regs[r1] = dest;
- env->regs[r3] = src;
+ set_length(env, r1 + 1, destlen);
+ set_length(env, r3 + 1, srclen);
+ set_address(env, r1, dest);
+ set_address(env, r3, src);
return cc;
}
-/* compare logical long extended memcompare insn with padding */
-uint32_t HELPER(clcle)(CPUS390XState *env, uint32_t r1, uint64_t a2,
- uint32_t r3)
+/* compare logical long helper */
+static inline uint32_t do_clcl(CPUS390XState *env,
+ uint64_t *src1, uint64_t *src1len,
+ uint64_t *src3, uint64_t *src3len,
+ uint16_t pad, uint64_t limit,
+ int wordsize, uintptr_t ra)
{
- uint64_t destlen = env->regs[r1 + 1];
- uint64_t dest = get_address_31fix(env, r1);
- uint64_t srclen = env->regs[r3 + 1];
- uint64_t src = get_address_31fix(env, r3);
- uint8_t pad = a2 & 0xff;
- uint8_t v1 = 0, v2 = 0;
+ uint64_t len = MAX(*src1len, *src3len);
uint32_t cc = 0;
- if (!(destlen || srclen)) {
+ check_alignment(env, *src1len | *src3len, wordsize, ra);
+
+ if (!len) {
return cc;
}
- if (srclen > destlen) {
- srclen = destlen;
+ /* Lest we fail to service interrupts in a timely manner, limit the
+ amount of work we're willing to do. */
+ if (len > limit) {
+ len = limit;
+ cc = 3;
}
- for (; destlen || srclen; src++, dest++, destlen--, srclen--) {
- v1 = srclen ? cpu_ldub_data(env, src) : pad;
- v2 = destlen ? cpu_ldub_data(env, dest) : pad;
- if (v1 != v2) {
- cc = (v1 < v2) ? 1 : 2;
+ for (; len; len -= wordsize) {
+ uint16_t v1 = pad;
+ uint16_t v3 = pad;
+
+ if (*src1len) {
+ v1 = cpu_ldusize_data_ra(env, *src1, wordsize, ra);
+ }
+ if (*src3len) {
+ v3 = cpu_ldusize_data_ra(env, *src3, wordsize, ra);
+ }
+
+ if (v1 != v3) {
+ cc = (v1 < v3) ? 1 : 2;
break;
}
+
+ if (*src1len) {
+ *src1 += wordsize;
+ *src1len -= wordsize;
+ }
+ if (*src3len) {
+ *src3 += wordsize;
+ *src3len -= wordsize;
+ }
}
- env->regs[r1 + 1] = destlen;
- /* can't use srclen here, we trunc'ed it */
- env->regs[r3 + 1] -= src - env->regs[r3];
- env->regs[r1] = dest;
- env->regs[r3] = src;
+ return cc;
+}
+
+
+/* compare logical long */
+uint32_t HELPER(clcl)(CPUS390XState *env, uint32_t r1, uint32_t r2)
+{
+ uintptr_t ra = GETPC();
+ uint64_t src1len = extract64(env->regs[r1 + 1], 0, 24);
+ uint64_t src1 = get_address(env, r1);
+ uint64_t src3len = extract64(env->regs[r2 + 1], 0, 24);
+ uint64_t src3 = get_address(env, r2);
+ uint8_t pad = env->regs[r2 + 1] >> 24;
+ uint32_t cc;
+
+ cc = do_clcl(env, &src1, &src1len, &src3, &src3len, pad, -1, 1, ra);
+
+ env->regs[r1 + 1] = deposit64(env->regs[r1 + 1], 0, 24, src1len);
+ env->regs[r2 + 1] = deposit64(env->regs[r2 + 1], 0, 24, src3len);
+ set_address(env, r1, src1);
+ set_address(env, r2, src3);
+
+ return cc;
+}
+
+/* compare logical long extended memcompare insn with padding */
+uint32_t HELPER(clcle)(CPUS390XState *env, uint32_t r1, uint64_t a2,
+ uint32_t r3)
+{
+ uintptr_t ra = GETPC();
+ uint64_t src1len = get_length(env, r1 + 1);
+ uint64_t src1 = get_address(env, r1);
+ uint64_t src3len = get_length(env, r3 + 1);
+ uint64_t src3 = get_address(env, r3);
+ uint8_t pad = a2;
+ uint32_t cc;
+
+ cc = do_clcl(env, &src1, &src1len, &src3, &src3len, pad, 0x2000, 1, ra);
+
+ set_length(env, r1 + 1, src1len);
+ set_length(env, r3 + 1, src3len);
+ set_address(env, r1, src1);
+ set_address(env, r3, src3);
+
+ return cc;
+}
+
+/* compare logical long unicode memcompare insn with padding */
+uint32_t HELPER(clclu)(CPUS390XState *env, uint32_t r1, uint64_t a2,
+ uint32_t r3)
+{
+ uintptr_t ra = GETPC();
+ uint64_t src1len = get_length(env, r1 + 1);
+ uint64_t src1 = get_address(env, r1);
+ uint64_t src3len = get_length(env, r3 + 1);
+ uint64_t src3 = get_address(env, r3);
+ uint16_t pad = a2;
+ uint32_t cc = 0;
+
+ cc = do_clcl(env, &src1, &src1len, &src3, &src3len, pad, 0x1000, 2, ra);
+
+ set_length(env, r1 + 1, src1len);
+ set_length(env, r3 + 1, src3len);
+ set_address(env, r1, src1);
+ set_address(env, r3, src3);
return cc;
}
@@ -684,6 +851,7 @@ uint32_t HELPER(clcle)(CPUS390XState *env, uint32_t r1, uint64_t a2,
uint64_t HELPER(cksm)(CPUS390XState *env, uint64_t r1,
uint64_t src, uint64_t src_len)
{
+ uintptr_t ra = GETPC();
uint64_t max_len, len;
uint64_t cksm = (uint32_t)r1;
@@ -693,21 +861,21 @@ uint64_t HELPER(cksm)(CPUS390XState *env, uint64_t r1,
/* Process full words as available. */
for (len = 0; len + 4 <= max_len; len += 4, src += 4) {
- cksm += (uint32_t)cpu_ldl_data(env, src);
+ cksm += (uint32_t)cpu_ldl_data_ra(env, src, ra);
}
switch (max_len - len) {
case 1:
- cksm += cpu_ldub_data(env, src) << 24;
+ cksm += cpu_ldub_data_ra(env, src, ra) << 24;
len += 1;
break;
case 2:
- cksm += cpu_lduw_data(env, src) << 16;
+ cksm += cpu_lduw_data_ra(env, src, ra) << 16;
len += 2;
break;
case 3:
- cksm += cpu_lduw_data(env, src) << 16;
- cksm += cpu_ldub_data(env, src + 2) << 8;
+ cksm += cpu_lduw_data_ra(env, src, ra) << 16;
+ cksm += cpu_ldub_data_ra(env, src + 2, ra) << 8;
len += 3;
break;
}
@@ -726,9 +894,94 @@ uint64_t HELPER(cksm)(CPUS390XState *env, uint64_t r1,
return len;
}
+void HELPER(pack)(CPUS390XState *env, uint32_t len, uint64_t dest, uint64_t src)
+{
+ uintptr_t ra = GETPC();
+ int len_dest = len >> 4;
+ int len_src = len & 0xf;
+ uint8_t b;
+
+ dest += len_dest;
+ src += len_src;
+
+ /* last byte is special, it only flips the nibbles */
+ b = cpu_ldub_data_ra(env, src, ra);
+ cpu_stb_data_ra(env, dest, (b << 4) | (b >> 4), ra);
+ src--;
+ len_src--;
+
+ /* now pack every value */
+ while (len_dest >= 0) {
+ b = 0;
+
+ if (len_src > 0) {
+ b = cpu_ldub_data_ra(env, src, ra) & 0x0f;
+ src--;
+ len_src--;
+ }
+ if (len_src > 0) {
+ b |= cpu_ldub_data_ra(env, src, ra) << 4;
+ src--;
+ len_src--;
+ }
+
+ len_dest--;
+ dest--;
+ cpu_stb_data_ra(env, dest, b, ra);
+ }
+}
+
+static inline void do_pkau(CPUS390XState *env, uint64_t dest, uint64_t src,
+ uint32_t srclen, int ssize, uintptr_t ra)
+{
+ int i;
+ /* The destination operand is always 16 bytes long. */
+ const int destlen = 16;
+
+ /* The operands are processed from right to left. */
+ src += srclen - 1;
+ dest += destlen - 1;
+
+ for (i = 0; i < destlen; i++) {
+ uint8_t b = 0;
+
+ /* Start with a positive sign */
+ if (i == 0) {
+ b = 0xc;
+ } else if (srclen > ssize) {
+ b = cpu_ldub_data_ra(env, src, ra) & 0x0f;
+ src -= ssize;
+ srclen -= ssize;
+ }
+
+ if (srclen > ssize) {
+ b |= cpu_ldub_data_ra(env, src, ra) << 4;
+ src -= ssize;
+ srclen -= ssize;
+ }
+
+ cpu_stb_data_ra(env, dest, b, ra);
+ dest--;
+ }
+}
+
+
+void HELPER(pka)(CPUS390XState *env, uint64_t dest, uint64_t src,
+ uint32_t srclen)
+{
+ do_pkau(env, dest, src, srclen, 1, GETPC());
+}
+
+void HELPER(pku)(CPUS390XState *env, uint64_t dest, uint64_t src,
+ uint32_t srclen)
+{
+ do_pkau(env, dest, src, srclen, 2, GETPC());
+}
+
void HELPER(unpk)(CPUS390XState *env, uint32_t len, uint64_t dest,
uint64_t src)
{
+ uintptr_t ra = GETPC();
int len_dest = len >> 4;
int len_src = len & 0xf;
uint8_t b;
@@ -738,8 +991,8 @@ void HELPER(unpk)(CPUS390XState *env, uint32_t len, uint64_t dest,
src += len_src;
/* last byte is special, it only flips the nibbles */
- b = cpu_ldub_data(env, src);
- cpu_stb_data(env, dest, (b << 4) | (b >> 4));
+ b = cpu_ldub_data_ra(env, src, ra);
+ cpu_stb_data_ra(env, dest, (b << 4) | (b >> 4), ra);
src--;
len_src--;
@@ -749,7 +1002,7 @@ void HELPER(unpk)(CPUS390XState *env, uint32_t len, uint64_t dest,
uint8_t cur_byte = 0;
if (len_src > 0) {
- cur_byte = cpu_ldub_data(env, src);
+ cur_byte = cpu_ldub_data_ra(env, src, ra);
}
len_dest--;
@@ -768,29 +1021,124 @@ void HELPER(unpk)(CPUS390XState *env, uint32_t len, uint64_t dest,
/* zone bits */
cur_byte |= 0xf0;
- cpu_stb_data(env, dest, cur_byte);
+ cpu_stb_data_ra(env, dest, cur_byte, ra);
}
}
-void HELPER(tr)(CPUS390XState *env, uint32_t len, uint64_t array,
- uint64_t trans)
+static inline uint32_t do_unpkau(CPUS390XState *env, uint64_t dest,
+ uint32_t destlen, int dsize, uint64_t src,
+ uintptr_t ra)
{
int i;
+ uint32_t cc;
+ uint8_t b;
+ /* The source operand is always 16 bytes long. */
+ const int srclen = 16;
- for (i = 0; i <= len; i++) {
- uint8_t byte = cpu_ldub_data(env, array + i);
- uint8_t new_byte = cpu_ldub_data(env, trans + byte);
+ /* The operands are processed from right to left. */
+ src += srclen - 1;
+ dest += destlen - dsize;
+
+ /* Check for the sign. */
+ b = cpu_ldub_data_ra(env, src, ra);
+ src--;
+ switch (b & 0xf) {
+ case 0xa:
+ case 0xc:
+ case 0xe ... 0xf:
+ cc = 0; /* plus */
+ break;
+ case 0xb:
+ case 0xd:
+ cc = 1; /* minus */
+ break;
+ default:
+ case 0x0 ... 0x9:
+ cc = 3; /* invalid */
+ break;
+ }
- cpu_stb_data(env, array + i, new_byte);
+ /* Now pad every nibble with 0x30, advancing one nibble at a time. */
+ for (i = 0; i < destlen; i += dsize) {
+ if (i == (31 * dsize)) {
+ /* If length is 32/64 bytes, the leftmost byte is 0. */
+ b = 0;
+ } else if (i % (2 * dsize)) {
+ b = cpu_ldub_data_ra(env, src, ra);
+ src--;
+ } else {
+ b >>= 4;
+ }
+ cpu_stsize_data_ra(env, dest, 0x30 + (b & 0xf), dsize, ra);
+ dest -= dsize;
}
+
+ return cc;
+}
+
+uint32_t HELPER(unpka)(CPUS390XState *env, uint64_t dest, uint32_t destlen,
+ uint64_t src)
+{
+ return do_unpkau(env, dest, destlen, 1, src, GETPC());
+}
+
+uint32_t HELPER(unpku)(CPUS390XState *env, uint64_t dest, uint32_t destlen,
+ uint64_t src)
+{
+ return do_unpkau(env, dest, destlen, 2, src, GETPC());
+}
+
+uint32_t HELPER(tp)(CPUS390XState *env, uint64_t dest, uint32_t destlen)
+{
+ uintptr_t ra = GETPC();
+ uint32_t cc = 0;
+ int i;
+
+ for (i = 0; i < destlen; i++) {
+ uint8_t b = cpu_ldub_data_ra(env, dest + i, ra);
+ /* digit */
+ cc |= (b & 0xf0) > 0x90 ? 2 : 0;
+
+ if (i == (destlen - 1)) {
+ /* sign */
+ cc |= (b & 0xf) < 0xa ? 1 : 0;
+ } else {
+ /* digit */
+ cc |= (b & 0xf) > 0x9 ? 2 : 0;
+ }
+ }
+
+ return cc;
+}
+
+static uint32_t do_helper_tr(CPUS390XState *env, uint32_t len, uint64_t array,
+ uint64_t trans, uintptr_t ra)
+{
+ uint32_t i;
+
+ for (i = 0; i <= len; i++) {
+ uint8_t byte = cpu_ldub_data_ra(env, array + i, ra);
+ uint8_t new_byte = cpu_ldub_data_ra(env, trans + byte, ra);
+ cpu_stb_data_ra(env, array + i, new_byte, ra);
+ }
+
+ return env->cc_op;
+}
+
+void HELPER(tr)(CPUS390XState *env, uint32_t len, uint64_t array,
+ uint64_t trans)
+{
+ do_helper_tr(env, len, array, trans, GETPC());
}
uint64_t HELPER(tre)(CPUS390XState *env, uint64_t array,
uint64_t len, uint64_t trans)
{
+ uintptr_t ra = GETPC();
uint8_t end = env->regs[0] & 0xff;
uint64_t l = len;
uint64_t i;
+ uint32_t cc = 0;
if (!(env->psw.mask & PSW_MASK_64)) {
array &= 0x7fffffff;
@@ -801,47 +1149,95 @@ uint64_t HELPER(tre)(CPUS390XState *env, uint64_t array,
amount of work we're willing to do. For now, let's cap at 8k. */
if (l > 0x2000) {
l = 0x2000;
- env->cc_op = 3;
- } else {
- env->cc_op = 0;
+ cc = 3;
}
for (i = 0; i < l; i++) {
uint8_t byte, new_byte;
- byte = cpu_ldub_data(env, array + i);
+ byte = cpu_ldub_data_ra(env, array + i, ra);
if (byte == end) {
- env->cc_op = 1;
+ cc = 1;
break;
}
- new_byte = cpu_ldub_data(env, trans + byte);
- cpu_stb_data(env, array + i, new_byte);
+ new_byte = cpu_ldub_data_ra(env, trans + byte, ra);
+ cpu_stb_data_ra(env, array + i, new_byte, ra);
}
+ env->cc_op = cc;
env->retxl = len - i;
return array + i;
}
+static uint32_t do_helper_trt(CPUS390XState *env, uint32_t len, uint64_t array,
+ uint64_t trans, uintptr_t ra)
+{
+ uint32_t i;
+
+ for (i = 0; i <= len; i++) {
+ uint8_t byte = cpu_ldub_data_ra(env, array + i, ra);
+ uint8_t sbyte = cpu_ldub_data_ra(env, trans + byte, ra);
+
+ if (sbyte != 0) {
+ set_address(env, 1, array + i);
+ env->regs[2] = deposit64(env->regs[2], 0, 8, sbyte);
+ return (i == len) ? 2 : 1;
+ }
+ }
+
+ return 0;
+}
+
uint32_t HELPER(trt)(CPUS390XState *env, uint32_t len, uint64_t array,
uint64_t trans)
{
- uint32_t cc = 0;
+ return do_helper_trt(env, len, array, trans, GETPC());
+}
+
+/* Translate one/two to one/two */
+uint32_t HELPER(trXX)(CPUS390XState *env, uint32_t r1, uint32_t r2,
+ uint32_t tst, uint32_t sizes)
+{
+ uintptr_t ra = GETPC();
+ int dsize = (sizes & 1) ? 1 : 2;
+ int ssize = (sizes & 2) ? 1 : 2;
+ uint64_t tbl = get_address(env, 1) & ~7;
+ uint64_t dst = get_address(env, r1);
+ uint64_t len = get_length(env, r1 + 1);
+ uint64_t src = get_address(env, r2);
+ uint32_t cc = 3;
int i;
- for (i = 0; i <= len; i++) {
- uint8_t byte = cpu_ldub_data(env, array + i);
- uint8_t sbyte = cpu_ldub_data(env, trans + byte);
+ check_alignment(env, len, ssize, ra);
- if (sbyte != 0) {
- env->regs[1] = array + i;
- env->regs[2] = (env->regs[2] & ~0xff) | sbyte;
- cc = (i == len) ? 2 : 1;
+ /* Lest we fail to service interrupts in a timely manner, */
+ /* limit the amount of work we're willing to do. */
+ for (i = 0; i < 0x2000; i++) {
+ uint16_t sval = cpu_ldusize_data_ra(env, src, ssize, ra);
+ uint64_t tble = tbl + (sval * dsize);
+ uint16_t dval = cpu_ldusize_data_ra(env, tble, dsize, ra);
+ if (dval == tst) {
+ cc = 1;
+ break;
+ }
+ cpu_stsize_data_ra(env, dst, dval, dsize, ra);
+
+ len -= ssize;
+ src += ssize;
+ dst += dsize;
+
+ if (len == 0) {
+ cc = 0;
break;
}
}
+ set_address(env, r1, dst);
+ set_length(env, r1 + 1, len);
+ set_address(env, r2, src);
+
return cc;
}
@@ -866,6 +1262,8 @@ void HELPER(cdsg)(CPUS390XState *env, uint64_t addr,
} else {
uint64_t oldh, oldl;
+ check_alignment(env, addr, 16, ra);
+
oldh = cpu_ldq_data_ra(env, addr + 0, ra);
oldl = cpu_ldq_data_ra(env, addr + 8, ra);
@@ -887,20 +1285,20 @@ void HELPER(cdsg)(CPUS390XState *env, uint64_t addr,
#if !defined(CONFIG_USER_ONLY)
void HELPER(lctlg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
{
+ uintptr_t ra = GETPC();
S390CPU *cpu = s390_env_get_cpu(env);
bool PERchanged = false;
- int i;
uint64_t src = a2;
- uint64_t val;
+ uint32_t i;
for (i = r1;; i = (i + 1) % 16) {
- val = cpu_ldq_data(env, src);
+ uint64_t val = cpu_ldq_data_ra(env, src, ra);
if (env->cregs[i] != val && i >= 9 && i <= 11) {
PERchanged = true;
}
env->cregs[i] = val;
HELPER_LOG("load ctl %d from 0x%" PRIx64 " == 0x%" PRIx64 "\n",
- i, src, env->cregs[i]);
+ i, src, val);
src += sizeof(uint64_t);
if (i == r3) {
@@ -917,18 +1315,19 @@ void HELPER(lctlg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
void HELPER(lctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
{
+ uintptr_t ra = GETPC();
S390CPU *cpu = s390_env_get_cpu(env);
bool PERchanged = false;
- int i;
uint64_t src = a2;
- uint32_t val;
+ uint32_t i;
for (i = r1;; i = (i + 1) % 16) {
- val = cpu_ldl_data(env, src);
+ uint32_t val = cpu_ldl_data_ra(env, src, ra);
if ((uint32_t)env->cregs[i] != val && i >= 9 && i <= 11) {
PERchanged = true;
}
- env->cregs[i] = (env->cregs[i] & 0xFFFFFFFF00000000ULL) | val;
+ env->cregs[i] = deposit64(env->cregs[i], 0, 32, val);
+ HELPER_LOG("load ctl %d from 0x%" PRIx64 " == 0x%x\n", i, src, val);
src += sizeof(uint32_t);
if (i == r3) {
@@ -945,11 +1344,12 @@ void HELPER(lctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
void HELPER(stctg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
{
- int i;
+ uintptr_t ra = GETPC();
uint64_t dest = a2;
+ uint32_t i;
for (i = r1;; i = (i + 1) % 16) {
- cpu_stq_data(env, dest, env->cregs[i]);
+ cpu_stq_data_ra(env, dest, env->cregs[i], ra);
dest += sizeof(uint64_t);
if (i == r3) {
@@ -960,11 +1360,12 @@ void HELPER(stctg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
void HELPER(stctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
{
- int i;
+ uintptr_t ra = GETPC();
uint64_t dest = a2;
+ uint32_t i;
for (i = r1;; i = (i + 1) % 16) {
- cpu_stl_data(env, dest, env->cregs[i]);
+ cpu_stl_data_ra(env, dest, env->cregs[i], ra);
dest += sizeof(uint32_t);
if (i == r3) {
@@ -973,10 +1374,39 @@ void HELPER(stctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
}
}
+uint32_t HELPER(testblock)(CPUS390XState *env, uint64_t real_addr)
+{
+ uintptr_t ra = GETPC();
+ CPUState *cs = CPU(s390_env_get_cpu(env));
+ uint64_t abs_addr;
+ int i;
+
+ real_addr = wrap_address(env, real_addr);
+ abs_addr = mmu_real2abs(env, real_addr) & TARGET_PAGE_MASK;
+ if (!address_space_access_valid(&address_space_memory, abs_addr,
+ TARGET_PAGE_SIZE, true)) {
+ cpu_restore_state(cs, ra);
+ program_interrupt(env, PGM_ADDRESSING, 4);
+ return 1;
+ }
+
+ /* Check low-address protection */
+ if ((env->cregs[0] & CR0_LOWPROT) && real_addr < 0x2000) {
+ cpu_restore_state(cs, ra);
+ program_interrupt(env, PGM_PROTECTION, 4);
+ return 1;
+ }
+
+ for (i = 0; i < TARGET_PAGE_SIZE; i += 8) {
+ stq_phys(cs->as, abs_addr + i, 0);
+ }
+
+ return 0;
+}
+
uint32_t HELPER(tprot)(uint64_t a1, uint64_t a2)
{
/* XXX implement */
-
return 0;
}
@@ -985,7 +1415,7 @@ uint64_t HELPER(iske)(CPUS390XState *env, uint64_t r2)
{
static S390SKeysState *ss;
static S390SKeysClass *skeyclass;
- uint64_t addr = get_address(env, 0, 0, r2);
+ uint64_t addr = wrap_address(env, r2);
uint8_t key;
if (addr > ram_size) {
@@ -1008,7 +1438,7 @@ void HELPER(sske)(CPUS390XState *env, uint64_t r1, uint64_t r2)
{
static S390SKeysState *ss;
static S390SKeysClass *skeyclass;
- uint64_t addr = get_address(env, 0, 0, r2);
+ uint64_t addr = wrap_address(env, r2);
uint8_t key;
if (addr > ram_size) {
@@ -1063,32 +1493,9 @@ uint32_t HELPER(rrbe)(CPUS390XState *env, uint64_t r2)
return re >> 1;
}
-/* compare and swap and purge */
-uint32_t HELPER(csp)(CPUS390XState *env, uint32_t r1, uint64_t r2)
-{
- S390CPU *cpu = s390_env_get_cpu(env);
- uint32_t cc;
- uint32_t o1 = env->regs[r1];
- uint64_t a2 = r2 & ~3ULL;
- uint32_t o2 = cpu_ldl_data(env, a2);
-
- if (o1 == o2) {
- cpu_stl_data(env, a2, env->regs[(r1 + 1) & 15]);
- if (r2 & 0x3) {
- /* flush TLB / ALB */
- tlb_flush(CPU(cpu));
- }
- cc = 0;
- } else {
- env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | o2;
- cc = 1;
- }
-
- return cc;
-}
-
uint32_t HELPER(mvcs)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
{
+ uintptr_t ra = GETPC();
int cc = 0, i;
HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
@@ -1102,7 +1509,8 @@ uint32_t HELPER(mvcs)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
/* XXX replace w/ memcpy */
for (i = 0; i < l; i++) {
- cpu_stb_secondary(env, a1 + i, cpu_ldub_primary(env, a2 + i));
+ uint8_t x = cpu_ldub_primary_ra(env, a2 + i, ra);
+ cpu_stb_secondary_ra(env, a1 + i, x, ra);
}
return cc;
@@ -1110,6 +1518,7 @@ uint32_t HELPER(mvcs)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
uint32_t HELPER(mvcp)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
{
+ uintptr_t ra = GETPC();
int cc = 0, i;
HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
@@ -1123,36 +1532,45 @@ uint32_t HELPER(mvcp)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
/* XXX replace w/ memcpy */
for (i = 0; i < l; i++) {
- cpu_stb_primary(env, a1 + i, cpu_ldub_secondary(env, a2 + i));
+ uint8_t x = cpu_ldub_secondary_ra(env, a2 + i, ra);
+ cpu_stb_primary_ra(env, a1 + i, x, ra);
}
return cc;
}
/* invalidate pte */
-void HELPER(ipte)(CPUS390XState *env, uint64_t pte_addr, uint64_t vaddr)
+void HELPER(ipte)(CPUS390XState *env, uint64_t pto, uint64_t vaddr,
+ uint32_t m4)
{
CPUState *cs = CPU(s390_env_get_cpu(env));
uint64_t page = vaddr & TARGET_PAGE_MASK;
- uint64_t pte = 0;
+ uint64_t pte_addr, pte;
- /* XXX broadcast to other CPUs */
+ /* Compute the page table entry address */
+ pte_addr = (pto & _SEGMENT_ENTRY_ORIGIN);
+ pte_addr += (vaddr & VADDR_PX) >> 9;
- /* XXX Linux is nice enough to give us the exact pte address.
- According to spec we'd have to find it out ourselves */
- /* XXX Linux is fine with overwriting the pte, the spec requires
- us to only set the invalid bit */
- stq_phys(cs->as, pte_addr, pte | _PAGE_INVALID);
+ /* Mark the page table entry as invalid */
+ pte = ldq_phys(cs->as, pte_addr);
+ pte |= _PAGE_INVALID;
+ stq_phys(cs->as, pte_addr, pte);
/* XXX we exploit the fact that Linux passes the exact virtual
address here - it's not obliged to! */
- tlb_flush_page(cs, page);
+ /* XXX: the LC bit should be considered as 0 if the local-TLB-clearing
+ facility is not installed. */
+ if (m4 & 1) {
+ tlb_flush_page(cs, page);
+ } else {
+ tlb_flush_page_all_cpus_synced(cs, page);
+ }
/* XXX 31-bit hack */
- if (page & 0x80000000) {
- tlb_flush_page(cs, page & ~0x80000000);
+ if (m4 & 1) {
+ tlb_flush_page(cs, page ^ 0x80000000);
} else {
- tlb_flush_page(cs, page | 0x80000000);
+ tlb_flush_page_all_cpus_synced(cs, page ^ 0x80000000);
}
}
@@ -1164,19 +1582,27 @@ void HELPER(ptlb)(CPUS390XState *env)
tlb_flush(CPU(cpu));
}
+/* flush global tlb */
+void HELPER(purge)(CPUS390XState *env)
+{
+ S390CPU *cpu = s390_env_get_cpu(env);
+
+ tlb_flush_all_cpus_synced(CPU(cpu));
+}
+
/* load using real address */
uint64_t HELPER(lura)(CPUS390XState *env, uint64_t addr)
{
CPUState *cs = CPU(s390_env_get_cpu(env));
- return (uint32_t)ldl_phys(cs->as, get_address(env, 0, 0, addr));
+ return (uint32_t)ldl_phys(cs->as, wrap_address(env, addr));
}
uint64_t HELPER(lurag)(CPUS390XState *env, uint64_t addr)
{
CPUState *cs = CPU(s390_env_get_cpu(env));
- return ldq_phys(cs->as, get_address(env, 0, 0, addr));
+ return ldq_phys(cs->as, wrap_address(env, addr));
}
/* store using real address */
@@ -1184,7 +1610,7 @@ void HELPER(stura)(CPUS390XState *env, uint64_t addr, uint64_t v1)
{
CPUState *cs = CPU(s390_env_get_cpu(env));
- stl_phys(cs->as, get_address(env, 0, 0, addr), (uint32_t)v1);
+ stl_phys(cs->as, wrap_address(env, addr), (uint32_t)v1);
if ((env->psw.mask & PSW_MASK_PER) &&
(env->cregs[9] & PER_CR9_EVENT_STORE) &&
@@ -1199,7 +1625,7 @@ void HELPER(sturg)(CPUS390XState *env, uint64_t addr, uint64_t v1)
{
CPUState *cs = CPU(s390_env_get_cpu(env));
- stq_phys(cs->as, get_address(env, 0, 0, addr), v1);
+ stq_phys(cs->as, wrap_address(env, addr), v1);
if ((env->psw.mask & PSW_MASK_PER) &&
(env->cregs[9] & PER_CR9_EVENT_STORE) &&
@@ -1215,17 +1641,17 @@ uint64_t HELPER(lra)(CPUS390XState *env, uint64_t addr)
{
CPUState *cs = CPU(s390_env_get_cpu(env));
uint32_t cc = 0;
- int old_exc = cs->exception_index;
uint64_t asc = env->psw.mask & PSW_MASK_ASC;
uint64_t ret;
- int flags;
+ int old_exc, flags;
/* XXX incomplete - has more corner cases */
if (!(env->psw.mask & PSW_MASK_64) && (addr >> 32)) {
+ cpu_restore_state(cs, GETPC());
program_interrupt(env, PGM_SPECIAL_OP, 2);
}
- cs->exception_index = old_exc;
+ old_exc = cs->exception_index;
if (mmu_translate(env, addr, 0, asc, &ret, &flags, true)) {
cc = 3;
}
@@ -1240,3 +1666,126 @@ uint64_t HELPER(lra)(CPUS390XState *env, uint64_t addr)
return ret;
}
#endif
+
+/* load pair from quadword */
+uint64_t HELPER(lpq)(CPUS390XState *env, uint64_t addr)
+{
+ uintptr_t ra = GETPC();
+ uint64_t hi, lo;
+
+ if (parallel_cpus) {
+#ifndef CONFIG_ATOMIC128
+ cpu_loop_exit_atomic(ENV_GET_CPU(env), ra);
+#else
+ int mem_idx = cpu_mmu_index(env, false);
+ TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx);
+ Int128 v = helper_atomic_ldo_be_mmu(env, addr, oi, ra);
+ hi = int128_gethi(v);
+ lo = int128_getlo(v);
+#endif
+ } else {
+ check_alignment(env, addr, 16, ra);
+
+ hi = cpu_ldq_data_ra(env, addr + 0, ra);
+ lo = cpu_ldq_data_ra(env, addr + 8, ra);
+ }
+
+ env->retxl = lo;
+ return hi;
+}
+
+/* store pair to quadword */
+void HELPER(stpq)(CPUS390XState *env, uint64_t addr,
+ uint64_t low, uint64_t high)
+{
+ uintptr_t ra = GETPC();
+
+ if (parallel_cpus) {
+#ifndef CONFIG_ATOMIC128
+ cpu_loop_exit_atomic(ENV_GET_CPU(env), ra);
+#else
+ int mem_idx = cpu_mmu_index(env, false);
+ TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx);
+
+ Int128 v = int128_make128(low, high);
+ helper_atomic_sto_be_mmu(env, addr, v, oi, ra);
+#endif
+ } else {
+ check_alignment(env, addr, 16, ra);
+
+ cpu_stq_data_ra(env, addr + 0, high, ra);
+ cpu_stq_data_ra(env, addr + 8, low, ra);
+ }
+}
+
+/* Execute instruction. This instruction executes an insn modified with
+ the contents of r1. It does not change the executed instruction in memory;
+ it does not change the program counter.
+
+ Perform this by recording the modified instruction in env->ex_value.
+ This will be noticed by cpu_get_tb_cpu_state and thus tb translation.
+*/
+void HELPER(ex)(CPUS390XState *env, uint32_t ilen, uint64_t r1, uint64_t addr)
+{
+ uint64_t insn = cpu_lduw_code(env, addr);
+ uint8_t opc = insn >> 8;
+
+ /* Or in the contents of R1[56:63]. */
+ insn |= r1 & 0xff;
+
+ /* Load the rest of the instruction. */
+ insn <<= 48;
+ switch (get_ilen(opc)) {
+ case 2:
+ break;
+ case 4:
+ insn |= (uint64_t)cpu_lduw_code(env, addr + 2) << 32;
+ break;
+ case 6:
+ insn |= (uint64_t)(uint32_t)cpu_ldl_code(env, addr + 2) << 16;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ /* The very most common cases can be sped up by avoiding a new TB. */
+ if ((opc & 0xf0) == 0xd0) {
+ typedef uint32_t (*dx_helper)(CPUS390XState *, uint32_t, uint64_t,
+ uint64_t, uintptr_t);
+ static const dx_helper dx[16] = {
+ [0x2] = do_helper_mvc,
+ [0x4] = do_helper_nc,
+ [0x5] = do_helper_clc,
+ [0x6] = do_helper_oc,
+ [0x7] = do_helper_xc,
+ [0xc] = do_helper_tr,
+ [0xd] = do_helper_trt,
+ };
+ dx_helper helper = dx[opc & 0xf];
+
+ if (helper) {
+ uint32_t l = extract64(insn, 48, 8);
+ uint32_t b1 = extract64(insn, 44, 4);
+ uint32_t d1 = extract64(insn, 32, 12);
+ uint32_t b2 = extract64(insn, 28, 4);
+ uint32_t d2 = extract64(insn, 16, 12);
+ uint64_t a1 = wrap_address(env, env->regs[b1] + d1);
+ uint64_t a2 = wrap_address(env, env->regs[b2] + d2);
+
+ env->cc_op = helper(env, l, a1, a2, 0);
+ env->psw.addr += ilen;
+ return;
+ }
+ } else if (opc == 0x0a) {
+ env->int_svc_code = extract64(insn, 48, 8);
+ env->int_svc_ilen = ilen;
+ helper_exception(env, EXCP_SVC);
+ g_assert_not_reached();
+ }
+
+ /* Record the insn we want to execute as well as the ilen to use
+ during the execution of the target insn. This will also ensure
+ that ex_value is non-zero, which flags that we are in a state
+ that requires such execution. */
+ env->ex_value = insn | ilen;
+}
diff --git a/target/s390x/misc_helper.c b/target/s390x/misc_helper.c
index 1b9f448..edcdf17 100644
--- a/target/s390x/misc_helper.c
+++ b/target/s390x/misc_helper.c
@@ -80,8 +80,6 @@ void HELPER(exception)(CPUS390XState *env, uint32_t excp)
cpu_loop_exit(cs);
}
-#ifndef CONFIG_USER_ONLY
-
void program_interrupt(CPUS390XState *env, uint32_t code, int ilen)
{
S390CPU *cpu = s390_env_get_cpu(env);
@@ -108,6 +106,8 @@ void program_interrupt(CPUS390XState *env, uint32_t code, int ilen)
}
}
+#ifndef CONFIG_USER_ONLY
+
/* SCLP service call */
uint32_t HELPER(servc)(CPUS390XState *env, uint64_t r1, uint64_t r2)
{
diff --git a/target/s390x/mmu_helper.c b/target/s390x/mmu_helper.c
index b11a027..501e390 100644
--- a/target/s390x/mmu_helper.c
+++ b/target/s390x/mmu_helper.c
@@ -108,7 +108,7 @@ static void trigger_page_fault(CPUS390XState *env, target_ulong vaddr,
* Translate real address to absolute (= physical)
* address by taking care of the prefix mapping.
*/
-static target_ulong mmu_real2abs(CPUS390XState *env, target_ulong raddr)
+target_ulong mmu_real2abs(CPUS390XState *env, target_ulong raddr)
{
if (raddr < 0x2000) {
return raddr + env->psa; /* Map the lowcore. */
@@ -143,8 +143,6 @@ static int mmu_translate_pte(CPUS390XState *env, target_ulong vaddr,
return 0;
}
-#define VADDR_PX 0xff000 /* Page index bits */
-
/* Decode segment table entry */
static int mmu_translate_segment(CPUS390XState *env, target_ulong vaddr,
uint64_t asc, uint64_t st_entry,
diff --git a/target/s390x/translate.c b/target/s390x/translate.c
index 628fb86..95f91d4 100644
--- a/target/s390x/translate.c
+++ b/target/s390x/translate.c
@@ -57,7 +57,9 @@ struct DisasContext {
struct TranslationBlock *tb;
const DisasInsn *insn;
DisasFields *fields;
+ uint64_t ex_value;
uint64_t pc, next_pc;
+ uint32_t ilen;
enum cc_op cc_op;
bool singlestep_enabled;
};
@@ -349,7 +351,7 @@ static void gen_program_exception(DisasContext *s, int code)
tcg_gen_st_i32(tmp, cpu_env, offsetof(CPUS390XState, int_pgm_code));
tcg_temp_free_i32(tmp);
- tmp = tcg_const_i32(s->next_pc - s->pc);
+ tmp = tcg_const_i32(s->ilen);
tcg_gen_st_i32(tmp, cpu_env, offsetof(CPUS390XState, int_pgm_ilen));
tcg_temp_free_i32(tmp);
@@ -1167,6 +1169,8 @@ typedef enum {
the PC (for whatever reason), so there's no need to do it again on
exiting the TB. */
EXIT_PC_UPDATED,
+ /* We have updated the PC and CC values. */
+ EXIT_PC_CC_UPDATED,
/* We are exiting the TB, but have neither emitted a goto_tb, nor
updated the PC for the next instruction to be executed. */
EXIT_PC_STALE,
@@ -1200,6 +1204,8 @@ typedef enum DisasFacility {
FAC_SFLE, /* store facility list extended */
FAC_ILA, /* interlocked access facility 1 */
FAC_LPP, /* load-program-parameter */
+ FAC_DAT_ENH, /* DAT-enhancement */
+ FAC_E2, /* extended-translation facility 2 */
} DisasFacility;
struct DisasInsn {
@@ -1871,7 +1877,6 @@ static ExitStatus op_cksm(DisasContext *s, DisasOps *o)
int r2 = get_field(s->fields, r2);
TCGv_i64 len = tcg_temp_new_i64();
- potential_page_fault(s);
gen_helper_cksm(len, cpu_env, o->in1, o->in2, regs[r2 + 1]);
set_cc_static(s);
return_low128(o->out);
@@ -1906,7 +1911,6 @@ static ExitStatus op_clc(DisasContext *s, DisasOps *o)
tcg_gen_qemu_ld64(cc_dst, o->in2, get_mem_index(s));
break;
default:
- potential_page_fault(s);
vl = tcg_const_i32(l);
gen_helper_clc(cc_op, cpu_env, vl, o->addr1, o->in2);
tcg_temp_free_i32(vl);
@@ -1917,14 +1921,65 @@ static ExitStatus op_clc(DisasContext *s, DisasOps *o)
return NO_EXIT;
}
+static ExitStatus op_clcl(DisasContext *s, DisasOps *o)
+{
+ int r1 = get_field(s->fields, r1);
+ int r2 = get_field(s->fields, r2);
+ TCGv_i32 t1, t2;
+
+ /* r1 and r2 must be even. */
+ if (r1 & 1 || r2 & 1) {
+ gen_program_exception(s, PGM_SPECIFICATION);
+ return EXIT_NORETURN;
+ }
+
+ t1 = tcg_const_i32(r1);
+ t2 = tcg_const_i32(r2);
+ gen_helper_clcl(cc_op, cpu_env, t1, t2);
+ tcg_temp_free_i32(t1);
+ tcg_temp_free_i32(t2);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
static ExitStatus op_clcle(DisasContext *s, DisasOps *o)
{
- TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1));
- TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3));
- potential_page_fault(s);
- gen_helper_clcle(cc_op, cpu_env, r1, o->in2, r3);
- tcg_temp_free_i32(r1);
- tcg_temp_free_i32(r3);
+ int r1 = get_field(s->fields, r1);
+ int r3 = get_field(s->fields, r3);
+ TCGv_i32 t1, t3;
+
+ /* r1 and r3 must be even. */
+ if (r1 & 1 || r3 & 1) {
+ gen_program_exception(s, PGM_SPECIFICATION);
+ return EXIT_NORETURN;
+ }
+
+ t1 = tcg_const_i32(r1);
+ t3 = tcg_const_i32(r3);
+ gen_helper_clcle(cc_op, cpu_env, t1, o->in2, t3);
+ tcg_temp_free_i32(t1);
+ tcg_temp_free_i32(t3);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
+static ExitStatus op_clclu(DisasContext *s, DisasOps *o)
+{
+ int r1 = get_field(s->fields, r1);
+ int r3 = get_field(s->fields, r3);
+ TCGv_i32 t1, t3;
+
+ /* r1 and r3 must be even. */
+ if (r1 & 1 || r3 & 1) {
+ gen_program_exception(s, PGM_SPECIFICATION);
+ return EXIT_NORETURN;
+ }
+
+ t1 = tcg_const_i32(r1);
+ t3 = tcg_const_i32(r3);
+ gen_helper_clclu(cc_op, cpu_env, t1, o->in2, t3);
+ tcg_temp_free_i32(t1);
+ tcg_temp_free_i32(t3);
set_cc_static(s);
return NO_EXIT;
}
@@ -1934,7 +1989,6 @@ static ExitStatus op_clm(DisasContext *s, DisasOps *o)
TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3));
TCGv_i32 t1 = tcg_temp_new_i32();
tcg_gen_extrl_i64_i32(t1, o->in1);
- potential_page_fault(s);
gen_helper_clm(cc_op, cpu_env, t1, m3, o->in2);
set_cc_static(s);
tcg_temp_free_i32(t1);
@@ -1944,7 +1998,6 @@ static ExitStatus op_clm(DisasContext *s, DisasOps *o)
static ExitStatus op_clst(DisasContext *s, DisasOps *o)
{
- potential_page_fault(s);
gen_helper_clst(o->in1, cpu_env, regs[0], o->in1, o->in2);
set_cc_static(s);
return_low128(o->in2);
@@ -2011,11 +2064,45 @@ static ExitStatus op_cdsg(DisasContext *s, DisasOps *o)
#ifndef CONFIG_USER_ONLY
static ExitStatus op_csp(DisasContext *s, DisasOps *o)
{
- TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1));
+ TCGMemOp mop = s->insn->data;
+ TCGv_i64 addr, old, cc;
+ TCGLabel *lab = gen_new_label();
+
+ /* Note that in1 = R1 (zero-extended expected value),
+ out = R1 (original reg), out2 = R1+1 (new value). */
+
check_privileged(s);
- gen_helper_csp(cc_op, cpu_env, r1, o->in2);
- tcg_temp_free_i32(r1);
- set_cc_static(s);
+ addr = tcg_temp_new_i64();
+ old = tcg_temp_new_i64();
+ tcg_gen_andi_i64(addr, o->in2, -1ULL << (mop & MO_SIZE));
+ tcg_gen_atomic_cmpxchg_i64(old, addr, o->in1, o->out2,
+ get_mem_index(s), mop | MO_ALIGN);
+ tcg_temp_free_i64(addr);
+
+ /* Are the memory and expected values (un)equal? */
+ cc = tcg_temp_new_i64();
+ tcg_gen_setcond_i64(TCG_COND_NE, cc, o->in1, old);
+ tcg_gen_extrl_i64_i32(cc_op, cc);
+
+ /* Write back the output now, so that it happens before the
+ following branch, so that we don't need local temps. */
+ if ((mop & MO_SIZE) == MO_32) {
+ tcg_gen_deposit_i64(o->out, o->out, old, 0, 32);
+ } else {
+ tcg_gen_mov_i64(o->out, old);
+ }
+ tcg_temp_free_i64(old);
+
+ /* If the comparison was equal, and the LSB of R2 was set,
+ then we need to flush the TLB (for all cpus). */
+ tcg_gen_xori_i64(cc, cc, 1);
+ tcg_gen_and_i64(cc, cc, o->in2);
+ tcg_gen_brcondi_i64(TCG_COND_EQ, cc, 0, lab);
+ tcg_temp_free_i64(cc);
+
+ gen_helper_purge(cpu_env);
+ gen_set_label(lab);
+
return NO_EXIT;
}
#endif
@@ -2158,27 +2245,34 @@ static ExitStatus op_epsw(DisasContext *s, DisasOps *o)
static ExitStatus op_ex(DisasContext *s, DisasOps *o)
{
- /* ??? Perhaps a better way to implement EXECUTE is to set a bit in
- tb->flags, (ab)use the tb->cs_base field as the address of
- the template in memory, and grab 8 bits of tb->flags/cflags for
- the contents of the register. We would then recognize all this
- in gen_intermediate_code_internal, generating code for exactly
- one instruction. This new TB then gets executed normally.
-
- On the other hand, this seems to be mostly used for modifying
- MVC inside of memcpy, which needs a helper call anyway. So
- perhaps this doesn't bear thinking about any further. */
+ int r1 = get_field(s->fields, r1);
+ TCGv_i32 ilen;
+ TCGv_i64 v1;
- TCGv_i64 tmp;
+ /* Nested EXECUTE is not allowed. */
+ if (unlikely(s->ex_value)) {
+ gen_program_exception(s, PGM_EXECUTE);
+ return EXIT_NORETURN;
+ }
update_psw_addr(s);
- gen_op_calc_cc(s);
+ update_cc_op(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);
+ if (r1 == 0) {
+ v1 = tcg_const_i64(0);
+ } else {
+ v1 = regs[r1];
+ }
- return NO_EXIT;
+ ilen = tcg_const_i32(s->ilen);
+ gen_helper_ex(cpu_env, ilen, v1, o->in2);
+ tcg_temp_free_i32(ilen);
+
+ if (r1 == 0) {
+ tcg_temp_free_i64(v1);
+ }
+
+ return EXIT_PC_CC_UPDATED;
}
static ExitStatus op_fieb(DisasContext *s, DisasOps *o)
@@ -2316,8 +2410,12 @@ static ExitStatus op_ipm(DisasContext *s, DisasOps *o)
#ifndef CONFIG_USER_ONLY
static ExitStatus op_ipte(DisasContext *s, DisasOps *o)
{
+ TCGv_i32 m4;
+
check_privileged(s);
- gen_helper_ipte(cpu_env, o->in1, o->in2);
+ m4 = tcg_const_i32(get_field(s->fields, m4));
+ gen_helper_ipte(cpu_env, o->in1, o->in2, m4);
+ tcg_temp_free_i32(m4);
return NO_EXIT;
}
@@ -2329,6 +2427,27 @@ static ExitStatus op_iske(DisasContext *s, DisasOps *o)
}
#endif
+static ExitStatus op_keb(DisasContext *s, DisasOps *o)
+{
+ gen_helper_keb(cc_op, cpu_env, o->in1, o->in2);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
+static ExitStatus op_kdb(DisasContext *s, DisasOps *o)
+{
+ gen_helper_kdb(cc_op, cpu_env, o->in1, o->in2);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
+static ExitStatus op_kxb(DisasContext *s, DisasOps *o)
+{
+ gen_helper_kxb(cc_op, cpu_env, o->out, o->out2, o->in1, o->in2);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
static ExitStatus op_laa(DisasContext *s, DisasOps *o)
{
/* The real output is indeed the original value in memory;
@@ -2550,7 +2669,6 @@ static ExitStatus op_lctl(DisasContext *s, DisasOps *o)
TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1));
TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3));
check_privileged(s);
- potential_page_fault(s);
gen_helper_lctl(cpu_env, r1, o->in2, r3);
tcg_temp_free_i32(r1);
tcg_temp_free_i32(r3);
@@ -2562,7 +2680,6 @@ static ExitStatus op_lctlg(DisasContext *s, DisasOps *o)
TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1));
TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3));
check_privileged(s);
- potential_page_fault(s);
gen_helper_lctlg(cpu_env, r1, o->in2, r3);
tcg_temp_free_i32(r1);
tcg_temp_free_i32(r3);
@@ -2572,7 +2689,6 @@ static ExitStatus op_lctlg(DisasContext *s, DisasOps *o)
static ExitStatus op_lra(DisasContext *s, DisasOps *o)
{
check_privileged(s);
- potential_page_fault(s);
gen_helper_lra(o->out, cpu_env, o->in2);
set_cc_static(s);
return NO_EXIT;
@@ -2629,7 +2745,6 @@ static ExitStatus op_lam(DisasContext *s, DisasOps *o)
{
TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1));
TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3));
- potential_page_fault(s);
gen_helper_lam(cpu_env, r1, o->in2, r3);
tcg_temp_free_i32(r1);
tcg_temp_free_i32(r3);
@@ -2794,6 +2909,13 @@ static ExitStatus op_lpd(DisasContext *s, DisasOps *o)
return NO_EXIT;
}
+static ExitStatus op_lpq(DisasContext *s, DisasOps *o)
+{
+ gen_helper_lpq(o->out, cpu_env, o->in2);
+ return_low128(o->out2);
+ return NO_EXIT;
+}
+
#ifndef CONFIG_USER_ONLY
static ExitStatus op_lura(DisasContext *s, DisasOps *o)
{
@@ -2871,32 +2993,78 @@ static ExitStatus op_movx(DisasContext *s, DisasOps *o)
static ExitStatus op_mvc(DisasContext *s, DisasOps *o)
{
TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1));
- potential_page_fault(s);
gen_helper_mvc(cpu_env, l, o->addr1, o->in2);
tcg_temp_free_i32(l);
return NO_EXIT;
}
+static ExitStatus op_mvcin(DisasContext *s, DisasOps *o)
+{
+ TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1));
+ gen_helper_mvcin(cpu_env, l, o->addr1, o->in2);
+ tcg_temp_free_i32(l);
+ return NO_EXIT;
+}
+
static ExitStatus op_mvcl(DisasContext *s, DisasOps *o)
{
- TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1));
- TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2));
- potential_page_fault(s);
- gen_helper_mvcl(cc_op, cpu_env, r1, r2);
- tcg_temp_free_i32(r1);
- tcg_temp_free_i32(r2);
+ int r1 = get_field(s->fields, r1);
+ int r2 = get_field(s->fields, r2);
+ TCGv_i32 t1, t2;
+
+ /* r1 and r2 must be even. */
+ if (r1 & 1 || r2 & 1) {
+ gen_program_exception(s, PGM_SPECIFICATION);
+ return EXIT_NORETURN;
+ }
+
+ t1 = tcg_const_i32(r1);
+ t2 = tcg_const_i32(r2);
+ gen_helper_mvcl(cc_op, cpu_env, t1, t2);
+ tcg_temp_free_i32(t1);
+ tcg_temp_free_i32(t2);
set_cc_static(s);
return NO_EXIT;
}
static ExitStatus op_mvcle(DisasContext *s, DisasOps *o)
{
- TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1));
- TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3));
- potential_page_fault(s);
- gen_helper_mvcle(cc_op, cpu_env, r1, o->in2, r3);
- tcg_temp_free_i32(r1);
- tcg_temp_free_i32(r3);
+ int r1 = get_field(s->fields, r1);
+ int r3 = get_field(s->fields, r3);
+ TCGv_i32 t1, t3;
+
+ /* r1 and r3 must be even. */
+ if (r1 & 1 || r3 & 1) {
+ gen_program_exception(s, PGM_SPECIFICATION);
+ return EXIT_NORETURN;
+ }
+
+ t1 = tcg_const_i32(r1);
+ t3 = tcg_const_i32(r3);
+ gen_helper_mvcle(cc_op, cpu_env, t1, o->in2, t3);
+ tcg_temp_free_i32(t1);
+ tcg_temp_free_i32(t3);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
+static ExitStatus op_mvclu(DisasContext *s, DisasOps *o)
+{
+ int r1 = get_field(s->fields, r1);
+ int r3 = get_field(s->fields, r3);
+ TCGv_i32 t1, t3;
+
+ /* r1 and r3 must be even. */
+ if (r1 & 1 || r3 & 1) {
+ gen_program_exception(s, PGM_SPECIFICATION);
+ return EXIT_NORETURN;
+ }
+
+ t1 = tcg_const_i32(r1);
+ t3 = tcg_const_i32(r3);
+ gen_helper_mvclu(cc_op, cpu_env, t1, o->in2, t3);
+ tcg_temp_free_i32(t1);
+ tcg_temp_free_i32(t3);
set_cc_static(s);
return NO_EXIT;
}
@@ -2906,7 +3074,6 @@ static ExitStatus op_mvcp(DisasContext *s, DisasOps *o)
{
int r1 = get_field(s->fields, l1);
check_privileged(s);
- potential_page_fault(s);
gen_helper_mvcp(cc_op, cpu_env, regs[r1], o->addr1, o->in2);
set_cc_static(s);
return NO_EXIT;
@@ -2916,30 +3083,51 @@ static ExitStatus op_mvcs(DisasContext *s, DisasOps *o)
{
int r1 = get_field(s->fields, l1);
check_privileged(s);
- potential_page_fault(s);
gen_helper_mvcs(cc_op, cpu_env, regs[r1], o->addr1, o->in2);
set_cc_static(s);
return NO_EXIT;
}
#endif
+static ExitStatus op_mvn(DisasContext *s, DisasOps *o)
+{
+ TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1));
+ gen_helper_mvn(cpu_env, l, o->addr1, o->in2);
+ tcg_temp_free_i32(l);
+ return NO_EXIT;
+}
+
+static ExitStatus op_mvo(DisasContext *s, DisasOps *o)
+{
+ TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1));
+ gen_helper_mvo(cpu_env, l, o->addr1, o->in2);
+ tcg_temp_free_i32(l);
+ return NO_EXIT;
+}
+
static ExitStatus op_mvpg(DisasContext *s, DisasOps *o)
{
- potential_page_fault(s);
- gen_helper_mvpg(cpu_env, regs[0], o->in1, o->in2);
+ gen_helper_mvpg(cc_op, cpu_env, regs[0], o->in1, o->in2);
set_cc_static(s);
return NO_EXIT;
}
static ExitStatus op_mvst(DisasContext *s, DisasOps *o)
{
- potential_page_fault(s);
gen_helper_mvst(o->in1, cpu_env, regs[0], o->in1, o->in2);
set_cc_static(s);
return_low128(o->in2);
return NO_EXIT;
}
+static ExitStatus op_mvz(DisasContext *s, DisasOps *o)
+{
+ TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1));
+ gen_helper_mvz(cpu_env, l, o->addr1, o->in2);
+ tcg_temp_free_i32(l);
+ return NO_EXIT;
+}
+
static ExitStatus op_mul(DisasContext *s, DisasOps *o)
{
tcg_gen_mul_i64(o->out, o->in1, o->in2);
@@ -3048,7 +3236,6 @@ static ExitStatus op_nabsf128(DisasContext *s, DisasOps *o)
static ExitStatus op_nc(DisasContext *s, DisasOps *o)
{
TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1));
- potential_page_fault(s);
gen_helper_nc(cc_op, cpu_env, l, o->addr1, o->in2);
tcg_temp_free_i32(l);
set_cc_static(s);
@@ -3083,7 +3270,6 @@ static ExitStatus op_negf128(DisasContext *s, DisasOps *o)
static ExitStatus op_oc(DisasContext *s, DisasOps *o)
{
TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1));
- potential_page_fault(s);
gen_helper_oc(cc_op, cpu_env, l, o->addr1, o->in2);
tcg_temp_free_i32(l);
set_cc_static(s);
@@ -3112,6 +3298,46 @@ static ExitStatus op_ori(DisasContext *s, DisasOps *o)
return NO_EXIT;
}
+static ExitStatus op_pack(DisasContext *s, DisasOps *o)
+{
+ TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1));
+ gen_helper_pack(cpu_env, l, o->addr1, o->in2);
+ tcg_temp_free_i32(l);
+ return NO_EXIT;
+}
+
+static ExitStatus op_pka(DisasContext *s, DisasOps *o)
+{
+ int l2 = get_field(s->fields, l2) + 1;
+ TCGv_i32 l;
+
+ /* The length must not exceed 32 bytes. */
+ if (l2 > 32) {
+ gen_program_exception(s, PGM_SPECIFICATION);
+ return EXIT_NORETURN;
+ }
+ l = tcg_const_i32(l2);
+ gen_helper_pka(cpu_env, o->addr1, o->in2, l);
+ tcg_temp_free_i32(l);
+ return NO_EXIT;
+}
+
+static ExitStatus op_pku(DisasContext *s, DisasOps *o)
+{
+ int l2 = get_field(s->fields, l2) + 1;
+ TCGv_i32 l;
+
+ /* The length must be even and should not exceed 64 bytes. */
+ if ((l2 & 1) || (l2 > 64)) {
+ gen_program_exception(s, PGM_SPECIFICATION);
+ return EXIT_NORETURN;
+ }
+ l = tcg_const_i32(l2);
+ gen_helper_pku(cpu_env, o->addr1, o->in2, l);
+ tcg_temp_free_i32(l);
+ return NO_EXIT;
+}
+
static ExitStatus op_popcnt(DisasContext *s, DisasOps *o)
{
gen_helper_popcnt(o->out, o->in2);
@@ -3632,7 +3858,6 @@ static ExitStatus op_stctg(DisasContext *s, DisasOps *o)
TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1));
TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3));
check_privileged(s);
- potential_page_fault(s);
gen_helper_stctg(cpu_env, r1, o->in2, r3);
tcg_temp_free_i32(r1);
tcg_temp_free_i32(r3);
@@ -3644,7 +3869,6 @@ static ExitStatus op_stctl(DisasContext *s, DisasOps *o)
TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1));
TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3));
check_privileged(s);
- potential_page_fault(s);
gen_helper_stctl(cpu_env, r1, o->in2, r3);
tcg_temp_free_i32(r1);
tcg_temp_free_i32(r3);
@@ -3876,7 +4100,6 @@ static ExitStatus op_stam(DisasContext *s, DisasOps *o)
{
TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1));
TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3));
- potential_page_fault(s);
gen_helper_stam(cpu_env, r1, o->in2, r3);
tcg_temp_free_i32(r1);
tcg_temp_free_i32(r3);
@@ -3980,9 +4203,14 @@ static ExitStatus op_stmh(DisasContext *s, DisasOps *o)
return NO_EXIT;
}
+static ExitStatus op_stpq(DisasContext *s, DisasOps *o)
+{
+ gen_helper_stpq(cpu_env, o->in2, o->out2, o->out);
+ return NO_EXIT;
+}
+
static ExitStatus op_srst(DisasContext *s, DisasOps *o)
{
- potential_page_fault(s);
gen_helper_srst(o->in1, cpu_env, regs[0], o->in1, o->in2);
set_cc_static(s);
return_low128(o->in2);
@@ -4032,7 +4260,7 @@ static ExitStatus op_svc(DisasContext *s, DisasOps *o)
tcg_gen_st_i32(t, cpu_env, offsetof(CPUS390XState, int_svc_code));
tcg_temp_free_i32(t);
- t = tcg_const_i32(s->next_pc - s->pc);
+ t = tcg_const_i32(s->ilen);
tcg_gen_st_i32(t, cpu_env, offsetof(CPUS390XState, int_svc_ilen));
tcg_temp_free_i32(t);
@@ -4040,6 +4268,16 @@ static ExitStatus op_svc(DisasContext *s, DisasOps *o)
return EXIT_NORETURN;
}
+static ExitStatus op_tam(DisasContext *s, DisasOps *o)
+{
+ int cc = 0;
+
+ cc |= (s->tb->flags & FLAG_MASK_64) ? 2 : 0;
+ cc |= (s->tb->flags & FLAG_MASK_32) ? 1 : 0;
+ gen_op_movi_cc(s, cc);
+ return NO_EXIT;
+}
+
static ExitStatus op_tceb(DisasContext *s, DisasOps *o)
{
gen_helper_tceb(cc_op, cpu_env, o->in1, o->in2);
@@ -4062,19 +4300,36 @@ static ExitStatus op_tcxb(DisasContext *s, DisasOps *o)
}
#ifndef CONFIG_USER_ONLY
+
+static ExitStatus op_testblock(DisasContext *s, DisasOps *o)
+{
+ check_privileged(s);
+ gen_helper_testblock(cc_op, cpu_env, o->in2);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
static ExitStatus op_tprot(DisasContext *s, DisasOps *o)
{
- potential_page_fault(s);
gen_helper_tprot(cc_op, o->addr1, o->in2);
set_cc_static(s);
return NO_EXIT;
}
+
#endif
+static ExitStatus op_tp(DisasContext *s, DisasOps *o)
+{
+ TCGv_i32 l1 = tcg_const_i32(get_field(s->fields, l1) + 1);
+ gen_helper_tp(cc_op, cpu_env, o->addr1, l1);
+ tcg_temp_free_i32(l1);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
static ExitStatus op_tr(DisasContext *s, DisasOps *o)
{
TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1));
- potential_page_fault(s);
gen_helper_tr(cpu_env, l, o->addr1, o->in2);
tcg_temp_free_i32(l);
set_cc_static(s);
@@ -4083,7 +4338,6 @@ static ExitStatus op_tr(DisasContext *s, DisasOps *o)
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);
@@ -4093,22 +4347,95 @@ static ExitStatus op_tre(DisasContext *s, DisasOps *o)
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_trXX(DisasContext *s, DisasOps *o)
+{
+ TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1));
+ TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2));
+ TCGv_i32 sizes = tcg_const_i32(s->insn->opc & 3);
+ TCGv_i32 tst = tcg_temp_new_i32();
+ int m3 = get_field(s->fields, m3);
+
+ /* XXX: the C bit in M3 should be considered as 0 when the
+ ETF2-enhancement facility is not installed. */
+ if (m3 & 1) {
+ tcg_gen_movi_i32(tst, -1);
+ } else {
+ tcg_gen_extrl_i64_i32(tst, regs[0]);
+ if (s->insn->opc & 3) {
+ tcg_gen_ext8u_i32(tst, tst);
+ } else {
+ tcg_gen_ext16u_i32(tst, tst);
+ }
+ }
+ gen_helper_trXX(cc_op, cpu_env, r1, r2, tst, sizes);
+
+ tcg_temp_free_i32(r1);
+ tcg_temp_free_i32(r2);
+ tcg_temp_free_i32(sizes);
+ tcg_temp_free_i32(tst);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
+static ExitStatus op_ts(DisasContext *s, DisasOps *o)
+{
+ TCGv_i32 t1 = tcg_const_i32(0xff);
+ tcg_gen_atomic_xchg_i32(t1, o->in2, t1, get_mem_index(s), MO_UB);
+ tcg_gen_extract_i32(cc_op, t1, 7, 1);
+ tcg_temp_free_i32(t1);
+ 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));
- potential_page_fault(s);
gen_helper_unpk(cpu_env, l, o->addr1, o->in2);
tcg_temp_free_i32(l);
return NO_EXIT;
}
+static ExitStatus op_unpka(DisasContext *s, DisasOps *o)
+{
+ int l1 = get_field(s->fields, l1) + 1;
+ TCGv_i32 l;
+
+ /* The length must not exceed 32 bytes. */
+ if (l1 > 32) {
+ gen_program_exception(s, PGM_SPECIFICATION);
+ return EXIT_NORETURN;
+ }
+ l = tcg_const_i32(l1);
+ gen_helper_unpka(cc_op, cpu_env, o->addr1, l, o->in2);
+ tcg_temp_free_i32(l);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
+static ExitStatus op_unpku(DisasContext *s, DisasOps *o)
+{
+ int l1 = get_field(s->fields, l1) + 1;
+ TCGv_i32 l;
+
+ /* The length must be even and should not exceed 64 bytes. */
+ if ((l1 & 1) || (l1 > 64)) {
+ gen_program_exception(s, PGM_SPECIFICATION);
+ return EXIT_NORETURN;
+ }
+ l = tcg_const_i32(l1);
+ gen_helper_unpku(cc_op, cpu_env, o->addr1, l, o->in2);
+ tcg_temp_free_i32(l);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
+
static ExitStatus op_xc(DisasContext *s, DisasOps *o)
{
int d1 = get_field(s->fields, d1);
@@ -4156,7 +4483,6 @@ static ExitStatus op_xc(DisasContext *s, DisasOps *o)
/* But in general we'll defer to a helper. */
o->in2 = get_address(s, 0, b2, d2);
t32 = tcg_const_i32(l);
- potential_page_fault(s);
gen_helper_xc(cc_op, cpu_env, t32, o->addr1, o->in2);
tcg_temp_free_i32(t32);
set_cc_static(s);
@@ -5163,24 +5489,36 @@ static const DisasInsn *extract_insn(CPUS390XState *env, DisasContext *s,
int op, op2, ilen;
const DisasInsn *info;
- insn = ld_code2(env, pc);
- op = (insn >> 8) & 0xff;
- ilen = get_ilen(op);
- s->next_pc = s->pc + ilen;
+ if (unlikely(s->ex_value)) {
+ /* Drop the EX data now, so that it's clear on exception paths. */
+ TCGv_i64 zero = tcg_const_i64(0);
+ tcg_gen_st_i64(zero, cpu_env, offsetof(CPUS390XState, ex_value));
+ tcg_temp_free_i64(zero);
- switch (ilen) {
- case 2:
- insn = insn << 48;
- break;
- case 4:
- insn = ld_code4(env, pc) << 32;
- break;
- case 6:
- insn = (insn << 48) | (ld_code4(env, pc + 2) << 16);
- break;
- default:
- abort();
+ /* Extract the values saved by EXECUTE. */
+ insn = s->ex_value & 0xffffffffffff0000ull;
+ ilen = s->ex_value & 0xf;
+ op = insn >> 56;
+ } else {
+ insn = ld_code2(env, pc);
+ op = (insn >> 8) & 0xff;
+ ilen = get_ilen(op);
+ switch (ilen) {
+ case 2:
+ insn = insn << 48;
+ break;
+ case 4:
+ insn = ld_code4(env, pc) << 32;
+ break;
+ case 6:
+ insn = (insn << 48) | (ld_code4(env, pc + 2) << 16);
+ break;
+ default:
+ g_assert_not_reached();
+ }
}
+ s->next_pc = s->pc + ilen;
+ s->ilen = ilen;
/* We can't actually determine the insn format until we've looked up
the full insn opcode. Which we can't do without locating the
@@ -5397,6 +5735,7 @@ void gen_intermediate_code(CPUS390XState *env, struct TranslationBlock *tb)
dc.tb = tb;
dc.pc = pc_start;
dc.cc_op = CC_OP_DYNAMIC;
+ dc.ex_value = tb->cs_base;
do_debug = dc.singlestep_enabled = cs->singlestep_enabled;
next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE;
@@ -5431,10 +5770,7 @@ void gen_intermediate_code(CPUS390XState *env, struct TranslationBlock *tb)
gen_io_start();
}
- status = NO_EXIT;
- if (status == NO_EXIT) {
- status = translate_one(env, &dc);
- }
+ status = translate_one(env, &dc);
/* If we reach a page boundary, are single stepping,
or exhaust instruction count, stop generation. */
@@ -5443,7 +5779,8 @@ void gen_intermediate_code(CPUS390XState *env, struct TranslationBlock *tb)
|| tcg_op_buf_full()
|| num_insns >= max_insns
|| singlestep
- || cs->singlestep_enabled)) {
+ || cs->singlestep_enabled
+ || dc.ex_value)) {
status = EXIT_PC_STALE;
}
} while (status == NO_EXIT);
@@ -5463,6 +5800,8 @@ void gen_intermediate_code(CPUS390XState *env, struct TranslationBlock *tb)
/* Next TB starts off with CC_OP_DYNAMIC, so make sure the
cc op type is in env */
update_cc_op(&dc);
+ /* FALLTHRU */
+ case EXIT_PC_CC_UPDATED:
/* Exit the TB, either by raising a debug exception or by return. */
if (do_debug) {
gen_exception(EXCP_DEBUG);
@@ -5485,9 +5824,14 @@ void gen_intermediate_code(CPUS390XState *env, struct TranslationBlock *tb)
if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)
&& qemu_log_in_addr_range(pc_start)) {
qemu_log_lock();
- qemu_log("IN: %s\n", lookup_symbol(pc_start));
- log_target_disas(cs, pc_start, dc.pc - pc_start, 1);
- qemu_log("\n");
+ if (unlikely(dc.ex_value)) {
+ /* ??? Unfortunately log_target_disas can't use host memory. */
+ qemu_log("IN: EXECUTE %016" PRIx64 "\n", dc.ex_value);
+ } else {
+ qemu_log("IN: %s\n", lookup_symbol(pc_start));
+ log_target_disas(cs, pc_start, dc.pc - pc_start, 1);
+ qemu_log("\n");
+ }
qemu_log_unlock();
}
#endif