diff mbox

[v2,16/16] target-s390x: fix MVC instruction when areas overlap

Message ID 1433365796-1118-17-git-send-email-aurelien@aurel32.net
State New
Headers show

Commit Message

Aurelien Jarno June 3, 2015, 9:09 p.m. UTC
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).

Cc: Alexander Graf <agraf@suse.de>
Cc: Richard Henderson <rth@twiddle.net>
Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
---
 target-s390x/mem_helper.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

Comments

Richard Henderson June 3, 2015, 9:54 p.m. UTC | #1
On 06/03/2015 02:09 PM, Aurelien Jarno wrote:
> 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).
>
> Cc: Alexander Graf <agraf@suse.de>
> Cc: Richard Henderson <rth@twiddle.net>
> Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
> ---
>   target-s390x/mem_helper.c | 7 ++++---
>   1 file changed, 4 insertions(+), 3 deletions(-)
>

Reviewed-by: Richard Henderson <rth@twiddle.net>


r~
diff mbox

Patch

diff --git a/target-s390x/mem_helper.c b/target-s390x/mem_helper.c
index 04500ab..b4e5d44 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));
     }