diff mbox series

[v2,04/28] s390x/tcg: MVCL: Process max 2k bytes at a time

Message ID 20190906075750.14791-5-david@redhat.com (mailing list archive)
State New, archived
Headers show
Series s390x/tcg: mem_helper: Fault-safe handling | expand

Commit Message

David Hildenbrand Sept. 6, 2019, 7:57 a.m. UTC
Process max 2k 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."

MVCL handling is quite different than MVCLE/MVCLU handling, so split up
the handlers.

We'll deal with fast_memmove() and fast_memset() not probing
reads/writes properly later. Also, we'll defer interrupt handling, as
that will require more thought, add a TODO for that.

Signed-off-by: David Hildenbrand <david@redhat.com>
---
 target/s390x/mem_helper.c | 44 +++++++++++++++++++++++++++++++++------
 1 file changed, 38 insertions(+), 6 deletions(-)

Comments

Richard Henderson Sept. 11, 2019, 2:52 p.m. UTC | #1
On 9/6/19 3:57 AM, David Hildenbrand wrote:
> Process max 2k 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."
> 
> MVCL handling is quite different than MVCLE/MVCLU handling, so split up
> the handlers.
> 
> We'll deal with fast_memmove() and fast_memset() not probing
> reads/writes properly later. Also, we'll defer interrupt handling, as
> that will require more thought, add a TODO for that.
> 
> Signed-off-by: David Hildenbrand <david@redhat.com>
> ---
>  target/s390x/mem_helper.c | 44 +++++++++++++++++++++++++++++++++------
>  1 file changed, 38 insertions(+), 6 deletions(-)
> 
> diff --git a/target/s390x/mem_helper.c b/target/s390x/mem_helper.c
> index 2361ed6d54..2e22c183bd 100644
> --- a/target/s390x/mem_helper.c
> +++ b/target/s390x/mem_helper.c
> @@ -799,19 +799,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.
> +     * Process up to 2k bytes.
> +     */
> +    while (destlen) {
> +        cur_len = MIN(destlen, 2048);

The language in the PoP is horribly written, and thus confusing.  I can't
believe it really means what it appears to say, about access exceptions not
being recognized.

The code within Hercules breaks the action at every 2k address boundary -- for
both src and dest.  That's the only way that actually makes sense to me, as
otherwise we end up allowing userspace to read/write into a page without
permission.  Which is a security hole.


r~
Richard Henderson Sept. 11, 2019, 3:07 p.m. UTC | #2
On 9/11/19 10:52 AM, Richard Henderson wrote:
> The code within Hercules breaks the action at every 2k address boundary -- for
> both src and dest.  That's the only way that actually makes sense to me, as
> otherwise we end up allowing userspace to read/write into a page without
> permission.  Which is a security hole.

Also, doesn't "2k" come from the old esa/360 page size?

Which means that we could break at 4k pages instead of 2k now
and the program wouldn't really be able to tell the difference.


r~
David Hildenbrand Sept. 11, 2019, 4:12 p.m. UTC | #3
On 11.09.19 17:07, Richard Henderson wrote:
> On 9/11/19 10:52 AM, Richard Henderson wrote:
>> The code within Hercules breaks the action at every 2k address boundary -- for
>> both src and dest.  That's the only way that actually makes sense to me, as
>> otherwise we end up allowing userspace to read/write into a page without
>> permission.  Which is a security hole.
> 
> Also, doesn't "2k" come from the old esa/360 page size?

I have no idea, I was very confused with that.

> 
> Which means that we could break at 4k pages instead of 2k now
> and the program wouldn't really be able to tell the difference.

What I had in a previous iteration was to simply process until the end
of the page(s), to not cross pages (there is one special case with 2k
vs. 4k when crossing pages when wrapping and running into a
low-address-protection). So essentially what you suggest. I can add that.
diff mbox series

Patch

diff --git a/target/s390x/mem_helper.c b/target/s390x/mem_helper.c
index 2361ed6d54..2e22c183bd 100644
--- a/target/s390x/mem_helper.c
+++ b/target/s390x/mem_helper.c
@@ -799,19 +799,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.
+     * Process up to 2k bytes.
+     */
+    while (destlen) {
+        cur_len = MIN(destlen, 2048);
+        if (!srclen) {
+            fast_memset(env, dest, pad, cur_len, ra);
+        } else {
+            cur_len = MIN(cur_len, srclen);
+
+            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;
 }