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:
Peter Maydell 2017-07-18 17:54:51 +01:00
commit ff3351d449
5 changed files with 696 additions and 25 deletions

View file

@ -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,

View file

@ -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)

View file

@ -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)

View file

@ -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);
}

View file

@ -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"