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]>
---
 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 de4399f322..3d3f1fc8dd 100644
--- a/hw/misc/aspeed_hace.c
+++ b/hw/misc/aspeed_hace.c
@@ -610,13 +610,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;
@@ -633,6 +678,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;
@@ -686,8 +732,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);
@@ -716,8 +768,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.43.0

Reply via email to