diff options
author | Aurelien Jarno <aurelien@aurel32.net> | 2015-06-03 23:09:56 +0200 |
---|---|---|
committer | Alexander Graf <agraf@suse.de> | 2015-06-05 01:37:59 +0200 |
commit | 068593deea6cc61b06243a33c7fcfadb1650b654 (patch) | |
tree | 60200713decc615d49fbde769f6f3729ca63ce4a /target-s390x | |
parent | a3084e8055067b3fe8ed653a609021d2ab368564 (diff) |
target-s390x: fix MVC instruction when areas overlap
The MVC instruction and the memmove C funtion do not have the same
semantic when memory areas overlap:
MVC: When the operands overlap, the result is obtained as if the
operands were processed one byte at a time and each result byte were
stored immediately after fetching the necessary operand byte.
memmove: Copying takes place as though the bytes in src are first copied
into a temporary array that does not overlap src or dest, and the bytes
are then copied from the temporary array to dest.
The behaviour is therefore the same when the destination is at a lower
address than the source, but not in the other case. This is actually a
trick for propagating a value to an area. While the current code detects
that and call memset in that case, it only does for 1-byte value. This
trick can and is used for propagating two or more bytes to an area.
In the softmmu case, the call to mvc_fast_memmove is correct as the
above tests verify that source and destination are each within a page,
and both in a different page. The part doing the move 8 bytes by 8 bytes
is wrong and we need to check that if the source and destination
overlap, they do with a distance of minimum 8 bytes before copying 8
bytes at a time.
In the user code, we should check check that the destination is at a
lower address than source or than the end of the source is at a lower
address than the destination before calling memmove. In the opposite
case we fallback to the same code as the softmmu one. Note that l
represents (length - 1).
Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
Reviewed-by: Richard Henderson <rth@twiddle.net>
Signed-off-by: Alexander Graf <agraf@suse.de>
Diffstat (limited to 'target-s390x')
-rw-r--r-- | target-s390x/mem_helper.c | 7 |
1 files changed, 4 insertions, 3 deletions
diff --git a/target-s390x/mem_helper.c b/target-s390x/mem_helper.c index 04500ab06a..b4e5d44011 100644 --- a/target-s390x/mem_helper.c +++ b/target-s390x/mem_helper.c @@ -213,21 +213,22 @@ void HELPER(mvc)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) if (dest == (src + 1)) { memset(g2h(dest), cpu_ldub_data(env, src), l + 1); return; - } else { + /* mvc and memmove do not behave the same when areas overlap! */ + } else if ((dest < src) || (src + l < dest)) { memmove(g2h(dest), g2h(src), l + 1); return; } #endif /* handle the parts that fit into 8-byte loads/stores */ - if (dest != (src + 1)) { + if ((dest + 8 <= src) || (src + 8 <= dest)) { for (i = 0; i < l_64; i++) { cpu_stq_data(env, dest + x, cpu_ldq_data(env, src + x)); x += 8; } } - /* slow version crossing pages with byte accesses */ + /* slow version with byte accesses which always work */ for (i = x; i <= l; i++) { cpu_stb_data(env, dest + i, cpu_ldub_data(env, src + i)); } |