[PULL 70/83] hw/misc/aspeed_hace: Support scatter-gather mode for the crypto command

Cédric Le Goater <[email protected]>
Newsgroups org.nongnu.qemu-arm,org.nongnu.qemu-devel
Message-ID <[email protected]>
From: Jamin Lin <[email protected]>

The AST2600 and later crypto engines drive the source and destination
through scatter-gather lists (HACE10[18]/[19]) rather than the single
contiguous buffers used by the AST2500 direct access mode. Each SG list
entry is a length word (SG_LIST_LEN_LAST marks the final entry) followed
by a DRAM address, matching the hash engine layout.

Add a crypt_prepare_sg() helper that gathers the source into / scatters
the destination out of the bounce buffer by walking the SG list, and
select it or the existing crypt_prepare_direct() from do_crypt_operation
based on HACE10[18], mirroring the hash engine's direct/scatter-gather
dispatch.

Signed-off-by: Jamin Lin <[email protected]>
Reviewed-by: Kane Chen <[email protected]>
Link: https://lore.kernel.org/qemu-devel/[email protected]
Signed-off-by: Cédric Le Goater <[email protected]>
---
 hw/misc/aspeed_hace.c | 70 +++++++++++++++++++++++++++++++++++++++----
 1 file changed, 64 insertions(+), 6 deletions(-)

diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c
index 0f35d74137b1..646ff504762d 100644
--- a/hw/misc/aspeed_hace.c
+++ b/hw/misc/aspeed_hace.c
@@ -608,13 +608,58 @@ static bool crypt_prepare_direct(AspeedHACEState *s, uint64_t addr,
 }
 
 /*
- * Perform an AES/DES/3DES ECB/CBC operation in direct access mode: the source
- * and destination are single contiguous buffers (HACE00/HACE04) and the IV/key
- * come from the context buffer (HACE08). For CBC the resulting chaining IV is
- * written back to the context buffer so the driver can continue the chain.
+ * Scatter-gather mode: the source/destination register points at an SG list
+ * whose entries are a length word (SG_LIST_LEN_LAST flags the final entry)
+ * followed by a DRAM address, matching the hash engine layout. Gather @len
+ * bytes into @buf, or scatter @buf back out when @to_dram is true.
+ * Returns true on success.
+ */
+static bool crypt_prepare_sg(AspeedHACEState *s, uint64_t addr,
+                             uint8_t *buf, uint32_t len, bool to_dram)
+{
+    uint32_t copied = 0;
+    uint32_t sg_addr;
+    uint32_t sg_len;
+    uint32_t entry;
+    int i;
+
+    for (i = 0; i < ASPEED_HACE_MAX_SG && copied < len; i++) {
+        entry = address_space_ldl_le(&s->dram_as, addr,
+                                     MEMTXATTRS_UNSPECIFIED, NULL);
+        sg_addr = address_space_ldl_le(&s->dram_as, addr + SG_LIST_LEN_SIZE,
+                                       MEMTXATTRS_UNSPECIFIED, NULL);
+        sg_len = entry & SG_LIST_LEN_MASK;
+
+        sg_addr &= SG_LIST_ADDR_MASK;
+        addr += SG_LIST_ENTRY_SIZE;
+
+        if (sg_len > len - copied) {
+            sg_len = len - copied;
+        }
+        if (address_space_rw(&s->dram_as, sg_addr, MEMTXATTRS_UNSPECIFIED,
+                             buf + copied, sg_len, to_dram)) {
+            return false;
+        }
+        copied += sg_len;
+
+        if (entry & SG_LIST_LEN_LAST) {
+            break;
+        }
+    }
+
+    return copied == len;
+}
+
+/*
+ * Perform an AES/DES/3DES ECB/CBC operation. The source and destination are
+ * either single contiguous buffers (direct access mode) or scatter-gather
+ * lists (HACE10[18]/[19]), addressed by HACE00/HACE04; the IV/key come from
+ * the context buffer (HACE08). For CBC the resulting chaining IV is written
+ * back to the context buffer so the driver can continue the chain.
  */
 static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
 {
+    bool sg_mode = cmd & CRYPT_CMD_SRC_SG_CTRL;
     uint32_t len = s->regs[R_CRYPT_DATA_LEN];
     bool encrypt = cmd & CRYPT_CMD_ENCRYPT;
     g_autoptr(QCryptoCipher) cipher = NULL;
@@ -631,6 +676,7 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
     size_t iv_offset;
     size_t blocklen;
     size_t keylen;
+    bool status;
 
     if (len == 0) {
         return;
@@ -684,8 +730,14 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
     src_buf = g_malloc0(len);
     dst_buf = g_malloc0(len);
 
+    /* Gather the source into the bounce buffer, per the selected mode. */
     src_addr = s->regs[R_CRYPT_SRC];
-    if (!crypt_prepare_direct(s, src_addr, src_buf, len, false)) {
+    if (sg_mode) {
+        status = crypt_prepare_sg(s, src_addr, src_buf, len, false);
+    } else {
+        status = crypt_prepare_direct(s, src_addr, src_buf, len, false);
+    }
+    if (!status) {
         qemu_log_mask(LOG_GUEST_ERROR,
                       "%s: Failed to read src, addr=0x%" HWADDR_PRIx "\n",
                       __func__, src_addr);
@@ -714,8 +766,14 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
         }
     }
 
+    /* Scatter the result back out, per the selected mode. */
     dst_addr = s->regs[R_CRYPT_DEST];
-    if (!crypt_prepare_direct(s, dst_addr, dst_buf, len, true)) {
+    if (sg_mode) {
+        status = crypt_prepare_sg(s, dst_addr, dst_buf, len, true);
+    } else {
+        status = crypt_prepare_direct(s, dst_addr, dst_buf, len, true);
+    }
+    if (!status) {
         qemu_log_mask(LOG_GUEST_ERROR,
                       "%s: Failed to write dst, addr=0x%" HWADDR_PRIx "\n",
                       __func__, dst_addr);
-- 
2.55.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.