Queued s390x tcg patches, v2
-----BEGIN PGP SIGNATURE----- iQEcBAABAgAGBQJZbSqPAAoJEK0ScMxN0Ceb4k4H/Rzr1EuPTW+ulL9eKVOhEqYP YxUQzdDRlYHuuIz/X2UauVLTmf6tTEpJMjgLDU1EmaQhv6LC2u552SBvjJG9r+G5 opE5BIELZ6021oUmsdjhGLfJtTdOnS1eUaAh0HSeqrsJBRd+cuG0r0EoQv9A5BTc uEsvtgFbvKDsy87biVyoaiId0aq+tPo7laOBTnU73m9XJeXK+uXbVOdastvzJAP1 Szm4H3z436JR/KOFrchCuFK1w79abeOk/vIR7h0VwWXvyed/T/rvxtS8V5iZwLpg Qmmq4HzmgjVZbscHRp2pyDz9/5+sJJYs9GylorLpIL7UdUfvkQEEa7BaYu9O1Xg= =cM42 -----END PGP SIGNATURE----- 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>
This commit is contained in:
commit
ff3351d449
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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->retxl = str + len;
|
||||
env->cc_op = 3;
|
||||
return end;
|
||||
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->cc_op = 3;
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue