[PATCH v13 04/18] target/s390x: Add helper functions for copy memory to and from guest

Harald Freudenberger <[email protected]> Mon, 3 Aug 2026 18:12:21 +0200
Newsgroups org.nongnu.qemu-devel,org.kernel.vger.linux-s390
Message-ID <[email protected]>
Add some simple helper functions to copy memory from guest to a local
buffer and the other way around:

- read_guest_wrap_u8()
- write_guest_wrap_u8()
- read_guest_wrap_u32()
- write_guest_wrap_u32()
- read_guest_wrap_u64()
- write_guest_wrap_u64()

The reader functions read from guest memory into an array of
u8/u32/u64 with BE conversion. Similar the writer functions write an
array of u8/u32/u64 into guest memory with BE conversion.

All these functions are intended to be used for the crypto
implementations thus are located in crypto_helper.h.

Rework and simplify the sha 256 and sha 512 implementations to use
these helper functions.

Signed-off-by: Harald Freudenberger <[email protected]>
---
 target/s390x/tcg/cpacf_sha256.c  |  63 ++++---------------
 target/s390x/tcg/cpacf_sha512.c  |  63 ++++---------------
 target/s390x/tcg/crypto_helper.c |   1 +
 target/s390x/tcg/crypto_helper.h | 100 +++++++++++++++++++++++++++++++
 4 files changed, 123 insertions(+), 104 deletions(-)
 create mode 100644 target/s390x/tcg/crypto_helper.h

diff --git a/target/s390x/tcg/cpacf_sha256.c b/target/s390x/tcg/cpacf_sha256.c
index 7e57e497a3..f895f9e20c 100644
--- a/target/s390x/tcg/cpacf_sha256.c
+++ b/target/s390x/tcg/cpacf_sha256.c
@@ -18,6 +18,7 @@
 #include "accel/tcg/cpu-ldst-common.h"
 #include "accel/tcg/cpu-mmu-index.h"
 #include "target/s390x/tcg/cpacf.h"
+#include "target/s390x/tcg/crypto_helper.h"
 
 static uint32_t R(uint32_t x, int c)
 {
@@ -103,53 +104,13 @@ static void sha256_bda_be32(uint32_t a[8], uint32_t w[16])
     sha256_bda(a, t);
 }
 
-static void sha256_read_icv(CPUS390XState *env, const int mmu_idx,
-                            uint64_t addr, uint32_t a[8], uintptr_t ra)
-{
-    const MemOpIdx oi = make_memop_idx(MO_BE | MO_32 | MO_UNALN, mmu_idx);
-
-    for (int i = 0; i < 8; i++, addr += 4) {
-        a[i] = cpu_ldl_mmu(env, wrap_address(env, addr), oi, ra);
-    }
-}
-
-static void sha256_write_ocv(CPUS390XState *env, const int mmu_idx,
-                             uint64_t addr, uint32_t a[8], uintptr_t ra)
-{
-    const MemOpIdx oi = make_memop_idx(MO_BE | MO_32 | MO_UNALN, mmu_idx);
-
-    for (int i = 0; i < 8; i++, addr += 4) {
-        cpu_stl_mmu(env, wrap_address(env, addr), a[i], oi, ra);
-    }
-}
-
-static void sha256_read_block(CPUS390XState *env, const int mmu_idx,
-                              uint64_t addr, uint32_t a[16], uintptr_t ra)
-{
-    const MemOpIdx oi = make_memop_idx(MO_BE | MO_32 | MO_UNALN, mmu_idx);
-
-    for (int i = 0; i < 16; i++, addr += 4) {
-        a[i] = cpu_ldl_mmu(env, wrap_address(env, addr), oi, ra);
-    }
-}
-
-static void sha256_read_mbl_be32(CPUS390XState *env, const int mmu_idx,
-                                 uint64_t addr, uint8_t a[8], uintptr_t ra)
-{
-    const MemOpIdx oi = make_memop_idx(MO_8, mmu_idx);
-
-    for (int i = 0; i < 8; i++, addr += 1) {
-        a[i] = cpu_ldb_mmu(env, wrap_address(env, addr), oi, ra);
-    }
-}
-
 int cpacf_sha256(CPUS390XState *env, const int mmu_idx, uintptr_t ra,
                  uint64_t param_addr, uint64_t *message_reg, uint64_t *len_reg,
                  uint32_t type)
 {
     enum { MAX_BLOCKS_PER_RUN = 128 }; /* 128 * 64 = 8K */
     uint64_t len = *len_reg, processed = 0;
-    int i, message_reg_len = 64;
+    int message_reg_len = 64;
     uint32_t a[8];
 
     g_assert(type == S390_FEAT_TYPE_KIMD || type == S390_FEAT_TYPE_KLMD);
@@ -164,7 +125,8 @@ int cpacf_sha256(CPUS390XState *env, const int mmu_idx, uintptr_t ra,
         tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
     }
 
-    sha256_read_icv(env, mmu_idx, param_addr, a, ra);
+    /* read icv (8 * u32) */
+    read_guest_wrap_u32(env, mmu_idx, ra, param_addr, a, 8);
 
     /* Process full blocks first. */
     for (; len >= 64; len -= 64, processed += 64) {
@@ -174,21 +136,18 @@ int cpacf_sha256(CPUS390XState *env, const int mmu_idx, uintptr_t ra,
             break;
         }
 
-        sha256_read_block(env, mmu_idx, *message_reg + processed, w, ra);
+        /* read sha256 block (16 * u32) */
+        read_guest_wrap_u32(env, mmu_idx, ra, *message_reg + processed, w, 16);
         sha256_bda(a, w);
     }
 
     /* KLMD: Process partial/empty block last. */
     if (type == S390_FEAT_TYPE_KLMD && len < 64) {
-        const MemOpIdx oi = make_memop_idx(MO_8, mmu_idx);
         uint8_t x[64];
 
-        /* Read the remainder of the message byte-per-byte. */
-        for (i = 0; i < len; i++) {
-            uint64_t addr = wrap_address(env, *message_reg + processed + i);
+        /* Read the remainder of the message. */
+        read_guest_wrap_u8(env, mmu_idx, ra, *message_reg + processed, x, len);
 
-            x[i] = cpu_ldb_mmu(env, addr, oi, ra);
-        }
         /* Pad the remainder with zero and set the top bit. */
         memset(x + len, 0, 64 - len);
         x[len] = 0x80;
@@ -198,13 +157,13 @@ int cpacf_sha256(CPUS390XState *env, const int mmu_idx, uintptr_t ra,
          * or use an additional one.
          */
         if (len < 56) {
-            sha256_read_mbl_be32(env, mmu_idx, param_addr + 32, x + 56, ra);
+            read_guest_wrap_u8(env, mmu_idx, ra, param_addr + 32, x + 56, 8);
         }
         sha256_bda_be32(a, (uint32_t *)x);
 
         if (len >= 56) {
             memset(x, 0, 56);
-            sha256_read_mbl_be32(env, mmu_idx, param_addr + 32, x + 56, ra);
+            read_guest_wrap_u8(env, mmu_idx, ra, param_addr + 32, x + 56, 8);
             sha256_bda_be32(a, (uint32_t *)x);
         }
 
@@ -219,7 +178,7 @@ int cpacf_sha256(CPUS390XState *env, const int mmu_idx, uintptr_t ra,
      * TODO: if writing fails halfway through (e.g., when crossing page
      * boundaries), we're in trouble. We'd need something like access_prepare().
      */
-    sha256_write_ocv(env, mmu_idx, param_addr, a, ra);
+    write_guest_wrap_u32(env, mmu_idx, ra, param_addr, a, 8);
     *message_reg = deposit64(*message_reg, 0, message_reg_len,
                              *message_reg + processed);
     *len_reg -= processed;
diff --git a/target/s390x/tcg/cpacf_sha512.c b/target/s390x/tcg/cpacf_sha512.c
index ebfecc70f7..fa42eff336 100644
--- a/target/s390x/tcg/cpacf_sha512.c
+++ b/target/s390x/tcg/cpacf_sha512.c
@@ -17,6 +17,7 @@
 #include "accel/tcg/cpu-ldst-common.h"
 #include "accel/tcg/cpu-mmu-index.h"
 #include "target/s390x/tcg/cpacf.h"
+#include "target/s390x/tcg/crypto_helper.h"
 
 static uint64_t R(uint64_t x, int c)
 {
@@ -118,53 +119,13 @@ static void sha512_bda_be64(uint64_t a[8], uint64_t w[16])
     sha512_bda(a, t);
 }
 
-static void sha512_read_icv(CPUS390XState *env, const int mmu_idx,
-                            uint64_t addr, uint64_t a[8], uintptr_t ra)
-{
-    const MemOpIdx oi = make_memop_idx(MO_BE | MO_64 | MO_UNALN, mmu_idx);
-
-    for (int i = 0; i < 8; i++, addr += 8) {
-        a[i] = cpu_ldq_mmu(env, wrap_address(env, addr), oi, ra);
-    }
-}
-
-static void sha512_write_ocv(CPUS390XState *env, const int mmu_idx,
-                             uint64_t addr, uint64_t a[8], uintptr_t ra)
-{
-    const MemOpIdx oi = make_memop_idx(MO_BE | MO_64 | MO_UNALN, mmu_idx);
-
-    for (int i = 0; i < 8; i++, addr += 8) {
-        cpu_stq_mmu(env, wrap_address(env, addr), a[i], oi, ra);
-    }
-}
-
-static void sha512_read_block(CPUS390XState *env, const int mmu_idx,
-                              uint64_t addr, uint64_t a[16], uintptr_t ra)
-{
-    const MemOpIdx oi = make_memop_idx(MO_BE | MO_64 | MO_UNALN, mmu_idx);
-
-    for (int i = 0; i < 16; i++, addr += 8) {
-        a[i] = cpu_ldq_mmu(env, wrap_address(env, addr), oi, ra);
-    }
-}
-
-static void sha512_read_mbl_be64(CPUS390XState *env, const int mmu_idx,
-                                 uint64_t addr, uint8_t a[16], uintptr_t ra)
-{
-    const MemOpIdx oi = make_memop_idx(MO_8, mmu_idx);
-
-    for (int i = 0; i < 16; i++, addr += 1) {
-        a[i] = cpu_ldb_mmu(env, wrap_address(env, addr), oi, ra);
-    }
-}
-
 int cpacf_sha512(CPUS390XState *env, const int mmu_idx, uintptr_t ra,
                  uint64_t param_addr, uint64_t *message_reg, uint64_t *len_reg,
                  uint32_t type)
 {
     enum { MAX_BLOCKS_PER_RUN = 64 }; /* Arbitrary: keep interactivity. */
     uint64_t len = *len_reg, a[8], processed = 0;
-    int i, message_reg_len = 64;
+    int message_reg_len = 64;
 
     g_assert(type == S390_FEAT_TYPE_KIMD || type == S390_FEAT_TYPE_KLMD);
 
@@ -178,7 +139,8 @@ int cpacf_sha512(CPUS390XState *env, const int mmu_idx, uintptr_t ra,
         tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
     }
 
-    sha512_read_icv(env, mmu_idx, param_addr, a, ra);
+    /* read icv (8 * u64) */
+    read_guest_wrap_u64(env, mmu_idx, ra, param_addr, a, 8);
 
     /* Process full blocks first. */
     for (; len >= 128; len -= 128, processed += 128) {
@@ -188,21 +150,18 @@ int cpacf_sha512(CPUS390XState *env, const int mmu_idx, uintptr_t ra,
             break;
         }
 
-        sha512_read_block(env, mmu_idx, *message_reg + processed, w, ra);
+        /* read sha512 block (16 * u64) */
+        read_guest_wrap_u64(env, mmu_idx, ra, *message_reg + processed, w, 16);
         sha512_bda(a, w);
     }
 
     /* KLMD: Process partial/empty block last. */
     if (type == S390_FEAT_TYPE_KLMD && len < 128) {
-        const MemOpIdx oi = make_memop_idx(MO_8, mmu_idx);
         uint8_t x[128];
 
-        /* Read the remainder of the message byte-per-byte. */
-        for (i = 0; i < len; i++) {
-            uint64_t addr = wrap_address(env, *message_reg + processed + i);
+        /* Read the remainder of the message. */
+        read_guest_wrap_u8(env, mmu_idx, ra, *message_reg + processed, x, len);
 
-            x[i] = cpu_ldb_mmu(env, addr, oi, ra);
-        }
         /* Pad the remainder with zero and set the top bit. */
         memset(x + len, 0, 128 - len);
         x[len] = 128;
@@ -212,13 +171,13 @@ int cpacf_sha512(CPUS390XState *env, const int mmu_idx, uintptr_t ra,
          * or use an additional one.
          */
         if (len < 112) {
-            sha512_read_mbl_be64(env, mmu_idx, param_addr + 64, x + 112, ra);
+            read_guest_wrap_u8(env, mmu_idx, ra, param_addr + 64, x + 112, 16);
         }
         sha512_bda_be64(a, (uint64_t *)x);
 
         if (len >= 112) {
             memset(x, 0, 112);
-            sha512_read_mbl_be64(env, mmu_idx, param_addr + 64, x + 112, ra);
+            read_guest_wrap_u8(env, mmu_idx, ra, param_addr + 64, x + 112, 16);
             sha512_bda_be64(a, (uint64_t *)x);
         }
 
@@ -233,7 +192,7 @@ int cpacf_sha512(CPUS390XState *env, const int mmu_idx, uintptr_t ra,
      * TODO: if writing fails halfway through (e.g., when crossing page
      * boundaries), we're in trouble. We'd need something like access_prepare().
      */
-    sha512_write_ocv(env, mmu_idx, param_addr, a, ra);
+    write_guest_wrap_u64(env, mmu_idx, ra, param_addr, a, 8);
     *message_reg = deposit64(*message_reg, 0, message_reg_len,
                              *message_reg + processed);
     *len_reg -= processed;
diff --git a/target/s390x/tcg/crypto_helper.c b/target/s390x/tcg/crypto_helper.c
index 6c296f6731..d996caf56a 100644
--- a/target/s390x/tcg/crypto_helper.c
+++ b/target/s390x/tcg/crypto_helper.c
@@ -20,6 +20,7 @@
 #include "accel/tcg/cpu-ldst-common.h"
 #include "accel/tcg/cpu-mmu-index.h"
 #include "target/s390x/tcg/cpacf.h"
+#include "target/s390x/tcg/crypto_helper.h"
 
 static void fill_buf_random(CPUS390XState *env, const int mmu_idx, uintptr_t ra,
                             uint64_t *buf_reg, uint64_t *len_reg)
diff --git a/target/s390x/tcg/crypto_helper.h b/target/s390x/tcg/crypto_helper.h
new file mode 100644
index 0000000000..2364e46029
--- /dev/null
+++ b/target/s390x/tcg/crypto_helper.h
@@ -0,0 +1,100 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Crypto helper functions
+ */
+
+#ifndef TARGET_S390_CRYPTO_HELPER_H
+#define TARGET_S390_CRYPTO_HELPER_H
+
+/*
+ * helper function to read len * u8 from guest to local buffer
+ */
+static inline void read_guest_wrap_u8(CPUS390XState *env, const int mmu_idx,
+                                      const uintptr_t ra, uint64_t guest_addr,
+                                      uint8_t *dest, size_t len)
+{
+    const MemOpIdx oi = make_memop_idx(MO_8, mmu_idx);
+
+    for (size_t i = 0; i < len; i++, guest_addr++) {
+        uint64_t waddr = wrap_address(env, guest_addr);
+        dest[i] = cpu_ldb_mmu(env, waddr, oi, ra);
+    }
+}
+
+/*
+ * helper function to write len * u8 from local buffer to guest
+ */
+static inline void write_guest_wrap_u8(CPUS390XState *env, const int mmu_idx,
+                                       const uintptr_t ra, uint64_t guest_addr,
+                                       const uint8_t *src, size_t len)
+{
+    const MemOpIdx oi = make_memop_idx(MO_8, mmu_idx);
+
+    for (size_t i = 0; i < len; i++, guest_addr++) {
+        uint64_t waddr = wrap_address(env, guest_addr);
+        cpu_stb_mmu(env, waddr, src[i], oi, ra);
+    }
+}
+
+/*
+ * helper function to read len * u32 from guest to local buffer
+ */
+static inline void read_guest_wrap_u32(CPUS390XState *env, const int mmu_idx,
+                                      const uintptr_t ra, uint64_t guest_addr,
+                                      uint32_t *dest, size_t len)
+{
+    const MemOpIdx oi = make_memop_idx(MO_BE | MO_32 | MO_UNALN, mmu_idx);
+
+    for (size_t i = 0; i < len; i++, guest_addr += 4) {
+        uint64_t waddr = wrap_address(env, guest_addr);
+        dest[i] = cpu_ldl_mmu(env, waddr, oi, ra);
+    }
+}
+
+/*
+ * helper function to write len * u32 from local buffer to guest
+ */
+static inline void write_guest_wrap_u32(CPUS390XState *env, const int mmu_idx,
+                                        const uintptr_t ra, uint64_t guest_addr,
+                                        const uint32_t *src, size_t len)
+{
+    const MemOpIdx oi = make_memop_idx(MO_BE | MO_32 | MO_UNALN, mmu_idx);
+
+    for (size_t i = 0; i < len; i++, guest_addr += 4) {
+        uint64_t waddr = wrap_address(env, guest_addr);
+        cpu_stl_mmu(env, waddr, src[i], oi, ra);
+    }
+}
+
+/*
+ * helper function to read len * u64 from guest to local buffer
+ */
+static inline void read_guest_wrap_u64(CPUS390XState *env, const int mmu_idx,
+                                      const uintptr_t ra, uint64_t guest_addr,
+                                      uint64_t *dest, size_t len)
+{
+    const MemOpIdx oi = make_memop_idx(MO_BE | MO_64 | MO_UNALN, mmu_idx);
+
+    for (size_t i = 0; i < len; i++, guest_addr += 8) {
+        uint64_t waddr = wrap_address(env, guest_addr);
+        dest[i] = cpu_ldq_mmu(env, waddr, oi, ra);
+    }
+}
+
+/*
+ * helper function to write len * u64 from local buffer to guest
+ */
+static inline void write_guest_wrap_u64(CPUS390XState *env, const int mmu_idx,
+                                        const uintptr_t ra, uint64_t guest_addr,
+                                        const uint64_t *src, size_t len)
+{
+    const MemOpIdx oi = make_memop_idx(MO_BE | MO_64 | MO_UNALN, mmu_idx);
+
+    for (size_t i = 0; i < len; i++, guest_addr += 8) {
+        uint64_t waddr = wrap_address(env, guest_addr);
+        cpu_stq_mmu(env, waddr, src[i], oi, ra);
+    }
+}
+
+#endif /* TARGET_S390_CRYPTO_HELPER_H */
-- 
2.43.0