aboutsummaryrefslogtreecommitdiff
path: root/target
diff options
context:
space:
mode:
authorDavid Hildenbrand <david@redhat.com>2019-09-03 14:56:19 +0200
committerDavid Hildenbrand <david@redhat.com>2019-09-23 09:28:29 +0200
commitf1c2e27cb5140b385e87f0d8be7606fb79aaa1f2 (patch)
tree25fd83a30a8d566d5744655a3e5c88d0a48a2ba2 /target
parentfbc17598d509be10d5d2349090bbb889ccb4f055 (diff)
s390x/tcg: MVCL: Process max 4k bytes at a time
Process max 4k bytes at a time, writing back registers between the accesses. The instruction is interruptible. "For operands longer than 2K bytes, access exceptions are not recognized for locations more than 2K bytes beyond the current location being processed." Note that on z/Architecture, 2k vs. 4k access cannot get differentiated as long as pages are not crossed. This seems to be a leftover from ESA/390. Simply stay within single pages. MVCL handling is quite different than MVCLE/MVCLU handling, so split up the handlers. Defer interrupt handling, as that will require more thought, add a TODO for that. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: David Hildenbrand <david@redhat.com>
Diffstat (limited to 'target')
-rw-r--r--target/s390x/mem_helper.c44
1 files changed, 38 insertions, 6 deletions
diff --git a/target/s390x/mem_helper.c b/target/s390x/mem_helper.c
index 223312a4b1..58ab2e48e3 100644
--- a/target/s390x/mem_helper.c
+++ b/target/s390x/mem_helper.c
@@ -798,19 +798,51 @@ uint32_t HELPER(mvcl)(CPUS390XState *env, uint32_t r1, uint32_t r2)
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;
+ uint32_t cc, cur_len;
if (is_destructive_overlap(env, dest, src, MIN(srclen, destlen))) {
cc = 3;
+ } else if (srclen == destlen) {
+ cc = 0;
+ } else if (destlen < srclen) {
+ cc = 1;
} else {
- cc = do_mvcl(env, &dest, &destlen, &src, &srclen, pad, 1, ra);
+ cc = 2;
}
- 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_zero(env, r1, dest);
- set_address_zero(env, r2, src);
+ /* We might have to zero-out some bits even if there was no action. */
+ if (unlikely(!destlen || cc == 3)) {
+ set_address_zero(env, r2, src);
+ set_address_zero(env, r1, dest);
+ return cc;
+ } else if (!srclen) {
+ set_address_zero(env, r2, src);
+ }
+ /*
+ * Only perform one type of type of operation (move/pad) in one step.
+ * Stay within single pages.
+ */
+ while (destlen) {
+ cur_len = MIN(destlen, -(dest | TARGET_PAGE_MASK));
+ if (!srclen) {
+ fast_memset(env, dest, pad, cur_len, ra);
+ } else {
+ cur_len = MIN(MIN(srclen, -(src | TARGET_PAGE_MASK)), cur_len);
+
+ fast_memmove(env, dest, src, cur_len, ra);
+ src = wrap_address(env, src + cur_len);
+ srclen -= cur_len;
+ env->regs[r2 + 1] = deposit64(env->regs[r2 + 1], 0, 24, srclen);
+ set_address_zero(env, r2, src);
+ }
+ dest = wrap_address(env, dest + cur_len);
+ destlen -= cur_len;
+ env->regs[r1 + 1] = deposit64(env->regs[r1 + 1], 0, 24, destlen);
+ set_address_zero(env, r1, dest);
+
+ /* TODO: Deliver interrupts. */
+ }
return cc;
}