diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2017-07-18 17:54:51 +0100 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2017-07-18 17:54:51 +0100 |
commit | ff3351d4495c07501aa75e46aec3f494f51d29e1 (patch) | |
tree | 1a658ee31b3c74f7c280fa7a11c834b9a4645711 | |
parent | 9d836fc552cce747a0946473726ff25262130351 (diff) | |
parent | dbdaaff43adc49f1debd935b1fd58e2b47ba7676 (diff) |
Merge remote-tracking branch 'remotes/rth/tags/pull-s390-20170717' into staging
Queued s390x tcg patches, v2
# gpg: Signature made Mon 17 Jul 2017 22:22:23 BST
# gpg: using RSA key 0xAD1270CC4DD0279B
# gpg: Good signature from "Richard Henderson <rth7680@gmail.com>"
# gpg: aka "Richard Henderson <rth@redhat.com>"
# gpg: aka "Richard Henderson <rth@twiddle.net>"
# Primary key fingerprint: 9CB1 8DDA F8E8 49AD 2AFC 16A4 AD12 70CC 4DD0 279B
* remotes/rth/tags/pull-s390-20170717:
target/s390x: Fix risbg handling
target/s390x: Allow to enable "idtes" feature for TCG
target/s390x: Mark ETF3 and ETF3_ENH facilities as available
target/s390x: Implement TRTR
target/s390x: Implement SRSTU
target/s390x: Tidy SRST
target/s390x: Implement CONVERT UNICODE insns
target/s390x: Implement CSST
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r-- | target/s390x/cpu_models.c | 5 | ||||
-rw-r--r-- | target/s390x/helper.h | 11 | ||||
-rw-r--r-- | target/s390x/insn-data.def | 21 | ||||
-rw-r--r-- | target/s390x/mem_helper.c | 585 | ||||
-rw-r--r-- | target/s390x/translate.c | 99 |
5 files changed, 696 insertions, 25 deletions
diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index c654279a6c..f4f73b888f 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -779,14 +779,19 @@ static void add_qemu_cpu_model_features(S390FeatBitmap fbm) { static const int feats[] = { S390_FEAT_DAT_ENH, + S390_FEAT_IDTE_SEGMENT, S390_FEAT_STFLE, S390_FEAT_EXTENDED_IMMEDIATE, S390_FEAT_EXTENDED_TRANSLATION_2, + S390_FEAT_EXTENDED_TRANSLATION_3, S390_FEAT_LONG_DISPLACEMENT, S390_FEAT_LONG_DISPLACEMENT_FAST, S390_FEAT_ETF2_ENH, S390_FEAT_STORE_CLOCK_FAST, S390_FEAT_MOVE_WITH_OPTIONAL_SPEC, + S390_FEAT_ETF3_ENH, + S390_FEAT_COMPARE_AND_SWAP_AND_STORE, + S390_FEAT_COMPARE_AND_SWAP_AND_STORE_2, S390_FEAT_GENERAL_INSTRUCTIONS_EXT, S390_FEAT_EXECUTE_EXT, S390_FEAT_FLOATING_POINT_SUPPPORT_ENH, diff --git a/target/s390x/helper.h b/target/s390x/helper.h index 964097b2ce..4b0290774e 100644 --- a/target/s390x/helper.h +++ b/target/s390x/helper.h @@ -12,7 +12,8 @@ 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) 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_3(srst, void, env, i32, i32) +DEF_HELPER_3(srstu, void, env, i32, i32) DEF_HELPER_4(clst, i64, 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) @@ -33,6 +34,7 @@ DEF_HELPER_3(celgb, i64, env, i64, i32) DEF_HELPER_3(cdlgb, i64, env, i64, i32) DEF_HELPER_3(cxlgb, i64, env, i64, i32) DEF_HELPER_4(cdsg, void, env, i64, i32, i32) +DEF_HELPER_4(csst, i32, env, i32, i64, i64) DEF_HELPER_FLAGS_3(aeb, TCG_CALL_NO_WG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(adb, TCG_CALL_NO_WG, i64, env, i64, i64) DEF_HELPER_FLAGS_5(axb, TCG_CALL_NO_WG, i64, env, i64, i64, i64, i64) @@ -95,6 +97,7 @@ 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_4(trtr, 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) @@ -106,6 +109,12 @@ 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) DEF_HELPER_4(mvcos, i32, env, i64, i64, i64) +DEF_HELPER_4(cu12, i32, env, i32, i32, i32) +DEF_HELPER_4(cu14, i32, env, i32, i32, i32) +DEF_HELPER_4(cu21, i32, env, i32, i32, i32) +DEF_HELPER_4(cu24, i32, env, i32, i32, i32) +DEF_HELPER_4(cu41, i32, env, i32, i32, i32) +DEF_HELPER_4(cu42, i32, env, i32, i32, i32) #ifndef CONFIG_USER_ONLY DEF_HELPER_3(servc, i32, env, i64, i64) diff --git a/target/s390x/insn-data.def b/target/s390x/insn-data.def index d3bb8516ed..ad84c748e3 100644 --- a/target/s390x/insn-data.def +++ b/target/s390x/insn-data.def @@ -265,6 +265,8 @@ D(0xbb00, CDS, RS_a, Z, r3_D32, r1_D32, new, r1_D32, cs, 0, MO_TEQ) D(0xeb31, CDSY, RSY_a, LD, r3_D32, r1_D32, new, r1_D32, cs, 0, MO_TEQ) C(0xeb3e, CDSG, RSY_a, Z, 0, 0, 0, 0, cdsg, 0) +/* COMPARE AND SWAP AND STORE */ + C(0xc802, CSST, SSF, CASS, la1, a2, 0, 0, csst, 0) /* COMPARE AND TRAP */ D(0xb972, CRT, RRF_c, GIE, r1_32s, r2_32s, 0, 0, ct, 0, 0) @@ -311,6 +313,19 @@ C(0xb3a1, CDLGBR, RRF_e, FPE, 0, r2_o, f1, 0, cdlgb, 0) C(0xb3a2, CXLGBR, RRF_e, FPE, 0, r2_o, x1, 0, cxlgb, 0) +/* CONVERT UTF-8 TO UTF-16 */ + D(0xb2a7, CU12, RRF_c, Z, 0, 0, 0, 0, cuXX, 0, 12) +/* CONVERT UTF-8 TO UTF-32 */ + D(0xb9b0, CU14, RRF_c, ETF3, 0, 0, 0, 0, cuXX, 0, 14) +/* CONVERT UTF-16 to UTF-8 */ + D(0xb2a6, CU21, RRF_c, Z, 0, 0, 0, 0, cuXX, 0, 21) +/* CONVERT UTF-16 to UTF-32 */ + D(0xb9b1, CU24, RRF_c, ETF3, 0, 0, 0, 0, cuXX, 0, 24) +/* CONVERT UTF-32 to UTF-8 */ + D(0xb9b2, CU41, RRF_c, ETF3, 0, 0, 0, 0, cuXX, 0, 41) +/* CONVERT UTF-32 to UTF-16 */ + D(0xb9b3, CU42, RRF_c, ETF3, 0, 0, 0, 0, cuXX, 0, 42) + /* DIVIDE */ C(0x1d00, DR, RR_a, Z, r1_D32, r2_32s, new_P, r1_P32, divs32, 0) C(0x5d00, D, RX_a, Z, r1_D32, m2_32s, new_P, r1_P32, divs32, 0) @@ -721,7 +736,9 @@ C(0xec57, RXSBG, RIE_f, GIE, 0, r2, r1, 0, rosbg, 0) /* SEARCH STRING */ - C(0xb25e, SRST, RRE, Z, r1_o, r2_o, 0, 0, srst, 0) + C(0xb25e, SRST, RRE, Z, 0, 0, 0, 0, srst, 0) +/* SEARCH STRING UNICODE */ + C(0xb9be, SRSTU, RRE, ETF3, 0, 0, 0, 0, srstu, 0) /* SET ACCESS */ C(0xb24e, SAR, RRE, Z, 0, r2_o, 0, 0, sar, 0) @@ -899,6 +916,8 @@ C(0xdc00, TR, SS_a, Z, la1, a2, 0, 0, tr, 0) /* TRANSLATE AND TEST */ C(0xdd00, TRT, SS_a, Z, la1, a2, 0, 0, trt, 0) +/* TRANSLATE AND TEST REVERSE */ + C(0xd000, TRTR, SS_a, ETF3, la1, a2, 0, 0, trtr, 0) /* TRANSLATE EXTENDED */ C(0xb2a5, TRE, RRE, Z, 0, r2, r1_P, 0, tre, 0) diff --git a/target/s390x/mem_helper.c b/target/s390x/mem_helper.c index ede84711d1..cdc78aa3d4 100644 --- a/target/s390x/mem_helper.c +++ b/target/s390x/mem_helper.c @@ -538,18 +538,21 @@ static inline void set_length(CPUS390XState *env, int reg, uint64_t 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) +void HELPER(srst)(CPUS390XState *env, uint32_t r1, uint32_t r2) { uintptr_t ra = GETPC(); + uint64_t end, str; uint32_t len; - uint8_t v, c = r0; + uint8_t v, c = env->regs[0]; - str = wrap_address(env, str); - end = wrap_address(env, end); + /* Bits 32-55 must contain all 0. */ + if (env->regs[0] & 0xffffff00u) { + cpu_restore_state(ENV_GET_CPU(env), ra); + program_interrupt(env, PGM_SPECIFICATION, 6); + } - /* Assume for now that R2 is unmodified. */ - env->retxl = str; + str = get_address(env, r2); + end = get_address(env, r1); /* 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. */ @@ -557,20 +560,61 @@ uint64_t HELPER(srst)(CPUS390XState *env, uint64_t r0, uint64_t end, if (str + len == end) { /* Character not found. R1 & R2 are unmodified. */ env->cc_op = 2; - return end; + return; } 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; - return str + len; + set_address(env, r1, str + len); + return; + } + } + + /* CPU-determined bytes processed. Advance R2 to next byte to process. */ + env->cc_op = 3; + set_address(env, r2, str + len); +} + +void HELPER(srstu)(CPUS390XState *env, uint32_t r1, uint32_t r2) +{ + uintptr_t ra = GETPC(); + uint32_t len; + uint16_t v, c = env->regs[0]; + uint64_t end, str, adj_end; + + /* Bits 32-47 of R0 must be zero. */ + if (env->regs[0] & 0xffff0000u) { + cpu_restore_state(ENV_GET_CPU(env), ra); + program_interrupt(env, PGM_SPECIFICATION, 6); + } + + str = get_address(env, r2); + end = get_address(env, r1); + + /* If the LSB of the two addresses differ, use one extra byte. */ + adj_end = end + ((str ^ end) & 1); + + /* 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 += 2) { + if (str + len == adj_end) { + /* End of input found. */ + env->cc_op = 2; + return; + } + v = cpu_lduw_data_ra(env, str + len, ra); + if (v == c) { + /* Character found. Set R1 to the location; R2 is unmodified. */ + env->cc_op = 1; + set_address(env, r1, str + len); + return; } } /* CPU-determined bytes processed. Advance R2 to next byte to process. */ - env->retxl = str + len; env->cc_op = 3; - return end; + set_address(env, r2, str + len); } /* unsigned string compare (c is string terminator) */ @@ -1233,17 +1277,18 @@ uint64_t HELPER(tre)(CPUS390XState *env, uint64_t array, return array + i; } -static uint32_t do_helper_trt(CPUS390XState *env, uint32_t len, uint64_t array, - uint64_t trans, uintptr_t ra) +static inline uint32_t do_helper_trt(CPUS390XState *env, int len, + uint64_t array, uint64_t trans, + int inc, uintptr_t ra) { - uint32_t i; + int i; for (i = 0; i <= len; i++) { - uint8_t byte = cpu_ldub_data_ra(env, array + i, ra); + uint8_t byte = cpu_ldub_data_ra(env, array + i * inc, ra); uint8_t sbyte = cpu_ldub_data_ra(env, trans + byte, ra); if (sbyte != 0) { - set_address(env, 1, array + i); + set_address(env, 1, array + i * inc); env->regs[2] = deposit64(env->regs[2], 0, 8, sbyte); return (i == len) ? 2 : 1; } @@ -1255,7 +1300,13 @@ static uint32_t do_helper_trt(CPUS390XState *env, uint32_t len, uint64_t array, uint32_t HELPER(trt)(CPUS390XState *env, uint32_t len, uint64_t array, uint64_t trans) { - return do_helper_trt(env, len, array, trans, GETPC()); + return do_helper_trt(env, len, array, trans, 1, GETPC()); +} + +uint32_t HELPER(trtr)(CPUS390XState *env, uint32_t len, uint64_t array, + uint64_t trans) +{ + return do_helper_trt(env, len, array, trans, -1, GETPC()); } /* Translate one/two to one/two */ @@ -1353,6 +1404,195 @@ void HELPER(cdsg)(CPUS390XState *env, uint64_t addr, env->regs[r1 + 1] = int128_getlo(oldv); } +uint32_t HELPER(csst)(CPUS390XState *env, uint32_t r3, uint64_t a1, uint64_t a2) +{ +#if !defined(CONFIG_USER_ONLY) || defined(CONFIG_ATOMIC128) + uint32_t mem_idx = cpu_mmu_index(env, false); +#endif + uintptr_t ra = GETPC(); + uint32_t fc = extract32(env->regs[0], 0, 8); + uint32_t sc = extract32(env->regs[0], 8, 8); + uint64_t pl = get_address(env, 1) & -16; + uint64_t svh, svl; + uint32_t cc; + + /* Sanity check the function code and storage characteristic. */ + if (fc > 1 || sc > 3) { + if (!s390_has_feat(S390_FEAT_COMPARE_AND_SWAP_AND_STORE_2)) { + goto spec_exception; + } + if (fc > 2 || sc > 4 || (fc == 2 && (r3 & 1))) { + goto spec_exception; + } + } + + /* Sanity check the alignments. */ + if (extract32(a1, 0, 4 << fc) || extract32(a2, 0, 1 << sc)) { + goto spec_exception; + } + + /* Sanity check writability of the store address. */ +#ifndef CONFIG_USER_ONLY + probe_write(env, a2, mem_idx, ra); +#endif + + /* Note that the compare-and-swap is atomic, and the store is atomic, but + the complete operation is not. Therefore we do not need to assert serial + context in order to implement this. That said, restart early if we can't + support either operation that is supposed to be atomic. */ + if (parallel_cpus) { + int mask = 0; +#if !defined(CONFIG_ATOMIC64) + mask = -8; +#elif !defined(CONFIG_ATOMIC128) + mask = -16; +#endif + if (((4 << fc) | (1 << sc)) & mask) { + cpu_loop_exit_atomic(ENV_GET_CPU(env), ra); + } + } + + /* All loads happen before all stores. For simplicity, load the entire + store value area from the parameter list. */ + svh = cpu_ldq_data_ra(env, pl + 16, ra); + svl = cpu_ldq_data_ra(env, pl + 24, ra); + + switch (fc) { + case 0: + { + uint32_t nv = cpu_ldl_data_ra(env, pl, ra); + uint32_t cv = env->regs[r3]; + uint32_t ov; + + if (parallel_cpus) { +#ifdef CONFIG_USER_ONLY + uint32_t *haddr = g2h(a1); + ov = atomic_cmpxchg__nocheck(haddr, cv, nv); +#else + TCGMemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mem_idx); + ov = helper_atomic_cmpxchgl_be_mmu(env, a1, cv, nv, oi, ra); +#endif + } else { + ov = cpu_ldl_data_ra(env, a1, ra); + cpu_stl_data_ra(env, a1, (ov == cv ? nv : ov), ra); + } + cc = (ov != cv); + env->regs[r3] = deposit64(env->regs[r3], 32, 32, ov); + } + break; + + case 1: + { + uint64_t nv = cpu_ldq_data_ra(env, pl, ra); + uint64_t cv = env->regs[r3]; + uint64_t ov; + + if (parallel_cpus) { +#ifdef CONFIG_ATOMIC64 +# ifdef CONFIG_USER_ONLY + uint64_t *haddr = g2h(a1); + ov = atomic_cmpxchg__nocheck(haddr, cv, nv); +# else + TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN, mem_idx); + ov = helper_atomic_cmpxchgq_be_mmu(env, a1, cv, nv, oi, ra); +# endif +#else + /* Note that we asserted !parallel_cpus above. */ + g_assert_not_reached(); +#endif + } else { + ov = cpu_ldq_data_ra(env, a1, ra); + cpu_stq_data_ra(env, a1, (ov == cv ? nv : ov), ra); + } + cc = (ov != cv); + env->regs[r3] = ov; + } + break; + + case 2: + { + uint64_t nvh = cpu_ldq_data_ra(env, pl, ra); + uint64_t nvl = cpu_ldq_data_ra(env, pl + 8, ra); + Int128 nv = int128_make128(nvl, nvh); + Int128 cv = int128_make128(env->regs[r3 + 1], env->regs[r3]); + Int128 ov; + + if (parallel_cpus) { +#ifdef CONFIG_ATOMIC128 + TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx); + ov = helper_atomic_cmpxchgo_be_mmu(env, a1, cv, nv, oi, ra); + cc = !int128_eq(ov, cv); +#else + /* Note that we asserted !parallel_cpus above. */ + g_assert_not_reached(); +#endif + } else { + uint64_t oh = cpu_ldq_data_ra(env, a1 + 0, ra); + uint64_t ol = cpu_ldq_data_ra(env, a1 + 8, ra); + + ov = int128_make128(ol, oh); + cc = !int128_eq(ov, cv); + if (cc) { + nv = ov; + } + + cpu_stq_data_ra(env, a1 + 0, int128_gethi(nv), ra); + cpu_stq_data_ra(env, a1 + 8, int128_getlo(nv), ra); + } + + env->regs[r3 + 0] = int128_gethi(ov); + env->regs[r3 + 1] = int128_getlo(ov); + } + break; + + default: + g_assert_not_reached(); + } + + /* Store only if the comparison succeeded. Note that above we use a pair + of 64-bit big-endian loads, so for sc < 3 we must extract the value + from the most-significant bits of svh. */ + if (cc == 0) { + switch (sc) { + case 0: + cpu_stb_data_ra(env, a2, svh >> 56, ra); + break; + case 1: + cpu_stw_data_ra(env, a2, svh >> 48, ra); + break; + case 2: + cpu_stl_data_ra(env, a2, svh >> 32, ra); + break; + case 3: + cpu_stq_data_ra(env, a2, svh, ra); + break; + case 4: + if (parallel_cpus) { +#ifdef CONFIG_ATOMIC128 + TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx); + Int128 sv = int128_make128(svl, svh); + helper_atomic_sto_be_mmu(env, a2, sv, oi, ra); +#else + /* Note that we asserted !parallel_cpus above. */ + g_assert_not_reached(); +#endif + } else { + cpu_stq_data_ra(env, a2 + 0, svh, ra); + cpu_stq_data_ra(env, a2 + 8, svl, ra); + } + default: + g_assert_not_reached(); + } + } + + return cc; + + spec_exception: + cpu_restore_state(ENV_GET_CPU(env), ra); + program_interrupt(env, PGM_SPECIFICATION, 6); + g_assert_not_reached(); +} + #if !defined(CONFIG_USER_ONLY) void HELPER(lctlg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) { @@ -1886,7 +2126,6 @@ void HELPER(ex)(CPUS390XState *env, uint32_t ilen, uint64_t r1, uint64_t addr) [0x6] = do_helper_oc, [0x7] = do_helper_xc, [0xc] = do_helper_tr, - [0xd] = do_helper_trt, }; dx_helper helper = dx[opc & 0xf]; @@ -2007,3 +2246,313 @@ uint32_t HELPER(mvcos)(CPUS390XState *env, uint64_t dest, uint64_t src, return cc; } + +/* Decode a Unicode character. A return value < 0 indicates success, storing + the UTF-32 result into OCHAR and the input length into OLEN. A return + value >= 0 indicates failure, and the CC value to be returned. */ +typedef int (*decode_unicode_fn)(CPUS390XState *env, uint64_t addr, + uint64_t ilen, bool enh_check, uintptr_t ra, + uint32_t *ochar, uint32_t *olen); + +/* Encode a Unicode character. A return value < 0 indicates success, storing + the bytes into ADDR and the output length into OLEN. A return value >= 0 + indicates failure, and the CC value to be returned. */ +typedef int (*encode_unicode_fn)(CPUS390XState *env, uint64_t addr, + uint64_t ilen, uintptr_t ra, uint32_t c, + uint32_t *olen); + +static int decode_utf8(CPUS390XState *env, uint64_t addr, uint64_t ilen, + bool enh_check, uintptr_t ra, + uint32_t *ochar, uint32_t *olen) +{ + uint8_t s0, s1, s2, s3; + uint32_t c, l; + + if (ilen < 1) { + return 0; + } + s0 = cpu_ldub_data_ra(env, addr, ra); + if (s0 <= 0x7f) { + /* one byte character */ + l = 1; + c = s0; + } else if (s0 <= (enh_check ? 0xc1 : 0xbf)) { + /* invalid character */ + return 2; + } else if (s0 <= 0xdf) { + /* two byte character */ + l = 2; + if (ilen < 2) { + return 0; + } + s1 = cpu_ldub_data_ra(env, addr + 1, ra); + c = s0 & 0x1f; + c = (c << 6) | (s1 & 0x3f); + if (enh_check && (s1 & 0xc0) != 0x80) { + return 2; + } + } else if (s0 <= 0xef) { + /* three byte character */ + l = 3; + if (ilen < 3) { + return 0; + } + s1 = cpu_ldub_data_ra(env, addr + 1, ra); + s2 = cpu_ldub_data_ra(env, addr + 2, ra); + c = s0 & 0x0f; + c = (c << 6) | (s1 & 0x3f); + c = (c << 6) | (s2 & 0x3f); + /* Fold the byte-by-byte range descriptions in the PoO into + tests against the complete value. It disallows encodings + that could be smaller, and the UTF-16 surrogates. */ + if (enh_check + && ((s1 & 0xc0) != 0x80 + || (s2 & 0xc0) != 0x80 + || c < 0x1000 + || (c >= 0xd800 && c <= 0xdfff))) { + return 2; + } + } else if (s0 <= (enh_check ? 0xf4 : 0xf7)) { + /* four byte character */ + l = 4; + if (ilen < 4) { + return 0; + } + s1 = cpu_ldub_data_ra(env, addr + 1, ra); + s2 = cpu_ldub_data_ra(env, addr + 2, ra); + s3 = cpu_ldub_data_ra(env, addr + 3, ra); + c = s0 & 0x07; + c = (c << 6) | (s1 & 0x3f); + c = (c << 6) | (s2 & 0x3f); + c = (c << 6) | (s3 & 0x3f); + /* See above. */ + if (enh_check + && ((s1 & 0xc0) != 0x80 + || (s2 & 0xc0) != 0x80 + || (s3 & 0xc0) != 0x80 + || c < 0x010000 + || c > 0x10ffff)) { + return 2; + } + } else { + /* invalid character */ + return 2; + } + + *ochar = c; + *olen = l; + return -1; +} + +static int decode_utf16(CPUS390XState *env, uint64_t addr, uint64_t ilen, + bool enh_check, uintptr_t ra, + uint32_t *ochar, uint32_t *olen) +{ + uint16_t s0, s1; + uint32_t c, l; + + if (ilen < 2) { + return 0; + } + s0 = cpu_lduw_data_ra(env, addr, ra); + if ((s0 & 0xfc00) != 0xd800) { + /* one word character */ + l = 2; + c = s0; + } else { + /* two word character */ + l = 4; + if (ilen < 4) { + return 0; + } + s1 = cpu_lduw_data_ra(env, addr + 2, ra); + c = extract32(s0, 6, 4) + 1; + c = (c << 6) | (s0 & 0x3f); + c = (c << 10) | (s1 & 0x3ff); + if (enh_check && (s1 & 0xfc00) != 0xdc00) { + /* invalid surrogate character */ + return 2; + } + } + + *ochar = c; + *olen = l; + return -1; +} + +static int decode_utf32(CPUS390XState *env, uint64_t addr, uint64_t ilen, + bool enh_check, uintptr_t ra, + uint32_t *ochar, uint32_t *olen) +{ + uint32_t c; + + if (ilen < 4) { + return 0; + } + c = cpu_ldl_data_ra(env, addr, ra); + if ((c >= 0xd800 && c <= 0xdbff) || c > 0x10ffff) { + /* invalid unicode character */ + return 2; + } + + *ochar = c; + *olen = 4; + return -1; +} + +static int encode_utf8(CPUS390XState *env, uint64_t addr, uint64_t ilen, + uintptr_t ra, uint32_t c, uint32_t *olen) +{ + uint8_t d[4]; + uint32_t l, i; + + if (c <= 0x7f) { + /* one byte character */ + l = 1; + d[0] = c; + } else if (c <= 0x7ff) { + /* two byte character */ + l = 2; + d[1] = 0x80 | extract32(c, 0, 6); + d[0] = 0xc0 | extract32(c, 6, 5); + } else if (c <= 0xffff) { + /* three byte character */ + l = 3; + d[2] = 0x80 | extract32(c, 0, 6); + d[1] = 0x80 | extract32(c, 6, 6); + d[0] = 0xe0 | extract32(c, 12, 4); + } else { + /* four byte character */ + l = 4; + d[3] = 0x80 | extract32(c, 0, 6); + d[2] = 0x80 | extract32(c, 6, 6); + d[1] = 0x80 | extract32(c, 12, 6); + d[0] = 0xf0 | extract32(c, 18, 3); + } + + if (ilen < l) { + return 1; + } + for (i = 0; i < l; ++i) { + cpu_stb_data_ra(env, addr + i, d[i], ra); + } + + *olen = l; + return -1; +} + +static int encode_utf16(CPUS390XState *env, uint64_t addr, uint64_t ilen, + uintptr_t ra, uint32_t c, uint32_t *olen) +{ + uint16_t d0, d1; + + if (c <= 0xffff) { + /* one word character */ + if (ilen < 2) { + return 1; + } + cpu_stw_data_ra(env, addr, c, ra); + *olen = 2; + } else { + /* two word character */ + if (ilen < 4) { + return 1; + } + d1 = 0xdc00 | extract32(c, 0, 10); + d0 = 0xd800 | extract32(c, 10, 6); + d0 = deposit32(d0, 6, 4, extract32(c, 16, 5) - 1); + cpu_stw_data_ra(env, addr + 0, d0, ra); + cpu_stw_data_ra(env, addr + 2, d1, ra); + *olen = 4; + } + + return -1; +} + +static int encode_utf32(CPUS390XState *env, uint64_t addr, uint64_t ilen, + uintptr_t ra, uint32_t c, uint32_t *olen) +{ + if (ilen < 4) { + return 1; + } + cpu_stl_data_ra(env, addr, c, ra); + *olen = 4; + return -1; +} + +static inline uint32_t convert_unicode(CPUS390XState *env, uint32_t r1, + uint32_t r2, uint32_t m3, uintptr_t ra, + decode_unicode_fn decode, + encode_unicode_fn encode) +{ + uint64_t dst = get_address(env, r1); + uint64_t dlen = get_length(env, r1 + 1); + uint64_t src = get_address(env, r2); + uint64_t slen = get_length(env, r2 + 1); + bool enh_check = m3 & 1; + int cc, i; + + /* 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 256. */ + for (i = 0; i < 256; ++i) { + uint32_t c, ilen, olen; + + cc = decode(env, src, slen, enh_check, ra, &c, &ilen); + if (unlikely(cc >= 0)) { + break; + } + cc = encode(env, dst, dlen, ra, c, &olen); + if (unlikely(cc >= 0)) { + break; + } + + src += ilen; + slen -= ilen; + dst += olen; + dlen -= olen; + cc = 3; + } + + set_address(env, r1, dst); + set_length(env, r1 + 1, dlen); + set_address(env, r2, src); + set_length(env, r2 + 1, slen); + + return cc; +} + +uint32_t HELPER(cu12)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3) +{ + return convert_unicode(env, r1, r2, m3, GETPC(), + decode_utf8, encode_utf16); +} + +uint32_t HELPER(cu14)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3) +{ + return convert_unicode(env, r1, r2, m3, GETPC(), + decode_utf8, encode_utf32); +} + +uint32_t HELPER(cu21)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3) +{ + return convert_unicode(env, r1, r2, m3, GETPC(), + decode_utf16, encode_utf8); +} + +uint32_t HELPER(cu24)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3) +{ + return convert_unicode(env, r1, r2, m3, GETPC(), + decode_utf16, encode_utf32); +} + +uint32_t HELPER(cu41)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3) +{ + return convert_unicode(env, r1, r2, m3, GETPC(), + decode_utf32, encode_utf8); +} + +uint32_t HELPER(cu42)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3) +{ + return convert_unicode(env, r1, r2, m3, GETPC(), + decode_utf32, encode_utf16); +} diff --git a/target/s390x/translate.c b/target/s390x/translate.c index 592d6b0f38..1dffcee884 100644 --- a/target/s390x/translate.c +++ b/target/s390x/translate.c @@ -2033,6 +2033,18 @@ static ExitStatus op_cdsg(DisasContext *s, DisasOps *o) return NO_EXIT; } +static ExitStatus op_csst(DisasContext *s, DisasOps *o) +{ + int r3 = get_field(s->fields, r3); + TCGv_i32 t_r3 = tcg_const_i32(r3); + + gen_helper_csst(cc_op, cpu_env, t_r3, o->in1, o->in2); + tcg_temp_free_i32(t_r3); + + set_cc_static(s); + return NO_EXIT; +} + #ifndef CONFIG_USER_ONLY static ExitStatus op_csp(DisasContext *s, DisasOps *o) { @@ -2110,6 +2122,56 @@ static ExitStatus op_ct(DisasContext *s, DisasOps *o) return NO_EXIT; } +static ExitStatus op_cuXX(DisasContext *s, DisasOps *o) +{ + int m3 = get_field(s->fields, m3); + int r1 = get_field(s->fields, r1); + int r2 = get_field(s->fields, r2); + TCGv_i32 tr1, tr2, chk; + + /* R1 and R2 must both be even. */ + if ((r1 | r2) & 1) { + gen_program_exception(s, PGM_SPECIFICATION); + return EXIT_NORETURN; + } + if (!s390_has_feat(S390_FEAT_ETF3_ENH)) { + m3 = 0; + } + + tr1 = tcg_const_i32(r1); + tr2 = tcg_const_i32(r2); + chk = tcg_const_i32(m3); + + switch (s->insn->data) { + case 12: + gen_helper_cu12(cc_op, cpu_env, tr1, tr2, chk); + break; + case 14: + gen_helper_cu14(cc_op, cpu_env, tr1, tr2, chk); + break; + case 21: + gen_helper_cu21(cc_op, cpu_env, tr1, tr2, chk); + break; + case 24: + gen_helper_cu24(cc_op, cpu_env, tr1, tr2, chk); + break; + case 41: + gen_helper_cu41(cc_op, cpu_env, tr1, tr2, chk); + break; + case 42: + gen_helper_cu42(cc_op, cpu_env, tr1, tr2, chk); + break; + default: + g_assert_not_reached(); + } + + tcg_temp_free_i32(tr1); + tcg_temp_free_i32(tr2); + tcg_temp_free_i32(chk); + set_cc_static(s); + return NO_EXIT; +} + #ifndef CONFIG_USER_ONLY static ExitStatus op_diag(DisasContext *s, DisasOps *o) { @@ -3417,8 +3479,8 @@ static ExitStatus op_risbg(DisasContext *s, DisasOps *o) } /* In some cases we can implement this with extract. */ - if (imask == 0 && pos == 0 && len > 0 && rot + len <= 64) { - tcg_gen_extract_i64(o->out, o->in2, rot, len); + if (imask == 0 && pos == 0 && len > 0 && len <= rot) { + tcg_gen_extract_i64(o->out, o->in2, 64 - rot, len); return NO_EXIT; } @@ -4225,9 +4287,27 @@ static ExitStatus op_stpq(DisasContext *s, DisasOps *o) static ExitStatus op_srst(DisasContext *s, DisasOps *o) { - gen_helper_srst(o->in1, cpu_env, regs[0], o->in1, o->in2); + TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); + TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2)); + + gen_helper_srst(cpu_env, r1, r2); + + tcg_temp_free_i32(r1); + tcg_temp_free_i32(r2); + set_cc_static(s); + return NO_EXIT; +} + +static ExitStatus op_srstu(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)); + + gen_helper_srstu(cpu_env, r1, r2); + + tcg_temp_free_i32(r1); + tcg_temp_free_i32(r2); set_cc_static(s); - return_low128(o->in2); return NO_EXIT; } @@ -4367,6 +4447,15 @@ static ExitStatus op_trt(DisasContext *s, DisasOps *o) return NO_EXIT; } +static ExitStatus op_trtr(DisasContext *s, DisasOps *o) +{ + TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); + gen_helper_trtr(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)); @@ -5437,7 +5526,6 @@ enum DisasInsnEnum { /* Give smaller names to the various facilities. */ #define FAC_Z S390_FEAT_ZARCH #define FAC_CASS S390_FEAT_COMPARE_AND_SWAP_AND_STORE -#define FAC_CASS2 S390_FEAT_COMPARE_AND_SWAP_AND_STORE_2 #define FAC_DFP S390_FEAT_DFP #define FAC_DFPR S390_FEAT_FLOATING_POINT_SUPPPORT_ENH /* DFP-rounding */ #define FAC_DO S390_FEAT_STFLE_45 /* distinct-operands */ @@ -5466,6 +5554,7 @@ enum DisasInsnEnum { #define FAC_EH S390_FEAT_STFLE_49 /* execution-hint */ #define FAC_PPA S390_FEAT_STFLE_49 /* processor-assist */ #define FAC_LZRB S390_FEAT_STFLE_53 /* load-and-zero-rightmost-byte */ +#define FAC_ETF3 S390_FEAT_EXTENDED_TRANSLATION_3 static const DisasInsn insn_info[] = { #include "insn-data.def" |