aboutsummaryrefslogtreecommitdiff
path: root/target
diff options
context:
space:
mode:
Diffstat (limited to 'target')
-rw-r--r--target/s390x/mem_helper.c54
-rw-r--r--target/s390x/translate.c20
2 files changed, 52 insertions, 22 deletions
diff --git a/target/s390x/mem_helper.c b/target/s390x/mem_helper.c
index 83c32c4..e30020c 100644
--- a/target/s390x/mem_helper.c
+++ b/target/s390x/mem_helper.c
@@ -666,35 +666,55 @@ uint32_t HELPER(clcle)(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);
+ 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 & 0xff;
+ uint64_t len = MAX(src1len, src3len);
uint32_t cc = 0;
- if (!(destlen || srclen)) {
+ 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. For now, let's cap at 8k. */
+ if (len > 0x2000) {
+ len = 0x2000;
+ cc = 3;
}
- for (; destlen || srclen; src++, dest++, destlen--, srclen--) {
- uint8_t v1 = srclen ? cpu_ldub_data_ra(env, src, ra) : pad;
- uint8_t v2 = destlen ? cpu_ldub_data_ra(env, dest, ra) : pad;
- if (v1 != v2) {
- cc = (v1 < v2) ? 1 : 2;
+ for (; len; len--) {
+ uint8_t v1 = pad;
+ uint8_t v3 = pad;
+
+ if (src1len) {
+ v1 = cpu_ldub_data_ra(env, src1, ra);
+ }
+ if (src3len) {
+ v3 = cpu_ldub_data_ra(env, src3, ra);
+ }
+
+ if (v1 != v3) {
+ cc = (v1 < v3) ? 1 : 2;
break;
}
+
+ if (src1len) {
+ src1++;
+ src1len--;
+ }
+ if (src3len) {
+ src3++;
+ src3len--;
+ }
}
- set_length(env, r1 + 1, destlen);
- /* can't use srclen here, we trunc'ed it */
- set_length(env, r3 + 1, env->regs[r3 + 1] - src - env->regs[r3]);
- set_address(env, r1, dest);
- set_address(env, r3, src);
+ set_length(env, r1 + 1, src1len);
+ set_length(env, r3 + 1, src3len);
+ set_address(env, r1, src1);
+ set_address(env, r3, src3);
return cc;
}
diff --git a/target/s390x/translate.c b/target/s390x/translate.c
index a21de09..ecd0a91 100644
--- a/target/s390x/translate.c
+++ b/target/s390x/translate.c
@@ -1922,11 +1922,21 @@ static ExitStatus op_clc(DisasContext *s, DisasOps *o)
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));
- 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;
}