All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v1 00/15] Support the ASPEED HACE crypto command
@ 2026-07-14  7:29 Jamin Lin
  2026-07-14  7:29 ` [PATCH v1 01/15] hw/misc/aspeed_hace: Support the crypto command in direct access mode Jamin Lin
                   ` (15 more replies)
  0 siblings, 16 replies; 23+ messages in thread
From: Jamin Lin @ 2026-07-14  7:29 UTC (permalink / raw)
  To: Daniel P. Berrangé, Cédric Le Goater, Peter Maydell,
	Steven Lee, Troy Lee, Kane Chen, Andrew Jeffery, Joel Stanley,
	Eric Blake, Markus Armbruster, Fabiano Rosas, Laurent Vivier,
	Paolo Bonzini, open list:All patches CC here,
	open list:ASPEED BMCs
  Cc: Jamin Lin, Troy Lee

The ASPEED HACE model only emulated the hash command; the crypto (cipher)
command was stubbed out. On the AST2700 the kernel runs the crypto driver
self-tests at boot, so without crypto emulation booting floods the log
with failures:

  root@ast2700-default:~# dmesg | grep "self"
[    6.078446] alg: self-tests for ctr(des) using aspeed-ctr-des failed (rc=-22)
[    6.079295] alg: self-tests for ctr(des) using aspeed-ctr-des failed (rc=-22)
[    6.089924] alg: self-tests for ctr(des3_ede) using aspeed-ctr-tdes failed (rc=-22)
[    6.090629] alg: self-tests for ctr(des3_ede) using aspeed-ctr-tdes failed (rc=-22)
[    6.100134] alg: self-tests for cbc(des3_ede) using aspeed-cbc-tdes failed (rc=-22)
[    6.100416] alg: self-tests for ecb(des3_ede) using aspeed-ecb-tdes failed (rc=-22)
[    6.100576] alg: self-tests for cbc(des3_ede) using aspeed-cbc-tdes failed (rc=-22)
[    6.101411] alg: self-tests for ecb(des3_ede) using aspeed-ecb-tdes failed (rc=-22)
[    6.152954] alg: self-tests for ecb(des) using aspeed-ecb-des failed (rc=-22)
[    6.153701] alg: self-tests for ecb(des) using aspeed-ecb-des failed (rc=-22)
[    6.180101] alg: self-tests for cbc(des) using aspeed-cbc-des failed (rc=-22)
[    6.180747] alg: self-tests for cbc(des) using aspeed-cbc-des failed (rc=-22)
[    6.206437] alg: self-tests for ctr(aes) using aspeed-ctr-aes failed (rc=-22)
[    6.206910] alg: self-tests for ctr(aes) using aspeed-ctr-aes failed (rc=-22)
[    6.215313] alg: self-tests for cbc(aes) using aspeed-cbc-aes failed (rc=-22)
[    6.215698] alg: self-tests for cbc(aes) using aspeed-cbc-aes failed (rc=-22)
[    6.227173] alg: self-tests for ecb(aes) using aspeed-ecb-aes failed (rc=-22)
[    6.228027] alg: self-tests for ecb(aes) using aspeed-ecb-aes failed (rc=-22)
[    6.276878] alg: self-tests for gcm(aes) using aspeed-gcm-aes failed (rc=-22)
[    6.277541] alg: self-tests for gcm(aes) using aspeed-gcm-aes failed (rc=-22)

This series implements the HACE crypto command: AES/DES/3DES in ECB/CBC/CTR
over direct and scatter-gather DMA, and AES-GCM with 64-bit DMA on the
AST2700. It also adds GCM to the qcrypto cipher API (gcrypt backend) that
the AST2700 path needs, plus qtest and unit-test coverage.

With crypto emulated the self-tests pass, the warnings above disappear, and
the temporary 'cryptomgr.notests=1' boot workaround in the AST2700
functional tests is dropped.

v1:
  1. Support the crypto command in direct access mode
  2. Support scatter-gather mode for the crypto command
  3. Support the AES-GCM mode for the crypto command
  4. Support 64-bit DMA for the crypto command
  5. Test the crypto command
  6. Drop the AST2700 crypto self-test/model workaround
  
Jamin Lin (15):
  hw/misc/aspeed_hace: Support the crypto command in direct access mode
  tests/qtest/aspeed-hace: Test the crypto command on the AST2500
  hw/misc/aspeed_hace: Support scatter-gather mode for the crypto
    command
  hw/misc/aspeed_hace: Support the CTR mode for the crypto command
  tests/qtest/aspeed-hace: Test the crypto command on the AST2600
  tests/qtest/aspeed-hace: Test the crypto command on the AST1030
  crypto/cipher: Add GCM to QCryptoCipherMode
  crypto/cipher: Add setaad/gettag for AEAD modes
  crypto/cipher-gcrypt: Implement AES-GCM
  tests/unit/test-crypto-cipher: Test AES-GCM mode
  hw/misc/aspeed_hace: Support 64-bit DMA for the crypto command
  hw/misc/aspeed_hace: Support the AES-GCM mode for the crypto command
  hw/misc/aspeed_hace: Enable the crypto command on the AST2700
  tests/qtest/aspeed-hace: Test the crypto command on the AST2700
  tests/functional/aarch64/test_aspeed_ast2700: Drop the AST2700 crypto
    self-test workaround

 qapi/crypto.json                              |   4 +-
 crypto/cipherpriv.h                           |   8 +
 include/crypto/cipher.h                       |  36 +
 include/hw/misc/aspeed_hace.h                 |   1 -
 tests/qtest/aspeed-hace-utils.h               |  20 +
 crypto/cipher.c                               |  32 +
 hw/misc/aspeed_hace.c                         | 454 +++++++++++-
 tests/qtest/aspeed-hace-utils.c               | 662 ++++++++++++++++++
 tests/qtest/aspeed_hace-test.c                |  18 +
 tests/qtest/ast2700-hace-test.c               |   9 +
 tests/unit/test-crypto-cipher.c               | 240 +++++++
 crypto/cipher-gcrypt.c.inc                    | 101 +++
 .../aarch64/test_aspeed_ast2700a1.py          |   5 -
 .../aarch64/test_aspeed_ast2700a2.py          |   5 -
 .../aarch64/test_aspeed_ast2700fc.py          |   5 -
 15 files changed, 1563 insertions(+), 37 deletions(-)

-- 
2.43.0


^ permalink raw reply	[flat|nested] 23+ messages in thread

* [PATCH v1 01/15] hw/misc/aspeed_hace: Support the crypto command in direct access mode
  2026-07-14  7:29 [PATCH v1 00/15] Support the ASPEED HACE crypto command Jamin Lin
@ 2026-07-14  7:29 ` Jamin Lin
  2026-07-14  7:29 ` [PATCH v1 02/15] tests/qtest/aspeed-hace: Test the crypto command on the AST2500 Jamin Lin
                   ` (14 subsequent siblings)
  15 siblings, 0 replies; 23+ messages in thread
From: Jamin Lin @ 2026-07-14  7:29 UTC (permalink / raw)
  To: Daniel P. Berrangé, Cédric Le Goater, Peter Maydell,
	Steven Lee, Troy Lee, Kane Chen, Andrew Jeffery, Joel Stanley,
	Eric Blake, Markus Armbruster, Fabiano Rosas, Laurent Vivier,
	Paolo Bonzini, open list:All patches CC here,
	open list:ASPEED BMCs
  Cc: Jamin Lin, Troy Lee

The crypt command register was previously stubbed out. Implement it for
the direct access mode, where HACE00/HACE04 point directly at contiguous
source and destination buffers. AES-128/192/256, DES and 3DES are
supported in ECB and CBC modes via the qcrypto cipher API; the IV and
key are read from the context buffer (HACE08) and, for CBC, the
resulting chaining IV is written back to the context.

The completion interrupt is now raised for every HACE variant as the
hardware does, which fixes the crypt command hang on the AST2500, AST2600
and AST1030. The AST2700 crypto engine still needs 64-bit DMA and
AES-GCM, which are added later, so it keeps its temporary interrupt-only
workaround until then.

For debugging, the context, source and destination buffers are dumped
through the existing aspeed_hace_hexdump trace event (disabled by
default). CTR mode, scatter-gather mode and AES-GCM are added separately.

Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
---
 hw/misc/aspeed_hace.c | 276 +++++++++++++++++++++++++++++++++++++++---
 1 file changed, 262 insertions(+), 14 deletions(-)

diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c
index c61efe50c4..42d8b38be3 100644
--- a/hw/misc/aspeed_hace.c
+++ b/hw/misc/aspeed_hace.c
@@ -18,11 +18,44 @@
 #include "qapi/error.h"
 #include "migration/vmstate.h"
 #include "crypto/hash.h"
+#include "crypto/cipher.h"
 #include "hw/core/qdev-properties.h"
 #include "hw/core/irq.h"
 #include "trace.h"
 
-#define R_CRYPT_CMD     (0x10 / 4)
+/* Crypto engine registers */
+#define R_CRYPT_SRC         (0x00 / 4)
+#define R_CRYPT_DEST        (0x04 / 4)
+#define R_CRYPT_CONTEXT     (0x08 / 4)
+#define R_CRYPT_DATA_LEN    (0x0c / 4)
+/* HACE0C[27:0] holds the crypto data length */
+#define  CRYPT_DATA_LEN_MASK    0x0FFFFFFF
+#define R_CRYPT_CMD         (0x10 / 4)
+/* Crypto engine command register (HACE10) bits */
+#define  CRYPT_CMD_ENCRYPT          BIT(7)
+#define  CRYPT_CMD_ISR_EN           BIT(12)
+#define  CRYPT_CMD_DES_SELECT       BIT(16)
+#define  CRYPT_CMD_TRIPLE_DES       BIT(17)
+#define  CRYPT_CMD_SRC_SG_CTRL      BIT(18)
+#define  CRYPT_CMD_DST_SG_CTRL      BIT(19)
+/* Operation mode HACE10[6:4] */
+#define  CRYPT_CMD_OP_MODE_MASK     (0x7 << 4)
+#define  CRYPT_CMD_ECB              (0x0 << 4)
+#define  CRYPT_CMD_CBC              (0x1 << 4)
+/* AES key length HACE10[3:2] */
+#define  CRYPT_CMD_AES_KEY_LEN_MASK (0x3 << 2)
+#define  CRYPT_CMD_AES256           (0x2 << 2)
+#define  CRYPT_CMD_AES192           (0x1 << 2)
+#define  CRYPT_CMD_AES128           (0x0 << 2)
+
+/*
+ * Crypto context buffer layout (HACE08). The IV is at the start of the buffer
+ * (DES places its 8 byte IV at offset 8) and the cipher key at offset 0x10.
+ */
+#define CRYPT_CTX_IV_OFFSET         0x00
+#define CRYPT_CTX_DES_IV_OFFSET     0x08
+#define CRYPT_CTX_KEY_OFFSET        0x10
+#define CRYPT_CTX_SIZE              0x30
 
 #define R_STATUS        (0x1c / 4)
 #define HASH_IRQ        BIT(9)
@@ -501,6 +534,209 @@ static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode,
     }
 }
 
+static bool crypt_aes_alg(uint32_t cmd, QCryptoCipherAlgo *alg, size_t *keylen)
+{
+    switch (cmd & CRYPT_CMD_AES_KEY_LEN_MASK) {
+    case CRYPT_CMD_AES128:
+        *alg = QCRYPTO_CIPHER_ALGO_AES_128;
+        *keylen = 16;
+        break;
+    case CRYPT_CMD_AES192:
+        *alg = QCRYPTO_CIPHER_ALGO_AES_192;
+        *keylen = 24;
+        break;
+    case CRYPT_CMD_AES256:
+        *alg = QCRYPTO_CIPHER_ALGO_AES_256;
+        *keylen = 32;
+        break;
+    default:
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Decode the crypto command register into a libqcrypto algorithm/mode pair
+ * and the block/IV geometry. Returns false for unsupported selections.
+ */
+static bool crypt_decode_cmd(uint32_t cmd, QCryptoCipherAlgo *alg,
+                             QCryptoCipherMode *mode, size_t *keylen,
+                             size_t *blocklen, size_t *iv_offset)
+{
+    if (cmd & CRYPT_CMD_DES_SELECT) {
+        *blocklen = 8;
+        *iv_offset = CRYPT_CTX_DES_IV_OFFSET;
+        if (cmd & CRYPT_CMD_TRIPLE_DES) {
+            *alg = QCRYPTO_CIPHER_ALGO_3DES;
+            *keylen = 24;
+        } else {
+            *alg = QCRYPTO_CIPHER_ALGO_DES;
+            *keylen = 8;
+        }
+    } else {
+        *blocklen = 16;
+        *iv_offset = CRYPT_CTX_IV_OFFSET;
+        if (!crypt_aes_alg(cmd, alg, keylen)) {
+            return false;
+        }
+    }
+
+    switch (cmd & CRYPT_CMD_OP_MODE_MASK) {
+    case CRYPT_CMD_ECB:
+        *mode = QCRYPTO_CIPHER_MODE_ECB;
+        break;
+    case CRYPT_CMD_CBC:
+        *mode = QCRYPTO_CIPHER_MODE_CBC;
+        break;
+    default:
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Direct access mode: the source/destination register (HACE00/HACE04) points
+ * at a single contiguous buffer in DRAM. Copy @len bytes between it and the
+ * bounce buffer @buf; when @to_dram is true @buf is written out, otherwise it
+ * is read in. Returns true on success.
+ */
+static bool crypt_prepare_direct(AspeedHACEState *s, uint64_t addr,
+                                 uint8_t *buf, uint32_t len, bool to_dram)
+{
+    return !address_space_rw(&s->dram_as, addr, MEMTXATTRS_UNSPECIFIED,
+                             buf, len, to_dram);
+}
+
+/*
+ * 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.
+ */
+static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
+{
+    uint32_t len = s->regs[R_CRYPT_DATA_LEN];
+    bool encrypt = cmd & CRYPT_CMD_ENCRYPT;
+    g_autoptr(QCryptoCipher) cipher = NULL;
+    g_autofree uint8_t *src_buf = NULL;
+    g_autofree uint8_t *dst_buf = NULL;
+    uint8_t ctx[CRYPT_CTX_SIZE];
+    Error *local_err = NULL;
+    QCryptoCipherMode mode;
+    QCryptoCipherAlgo alg;
+    const uint8_t *next_iv;
+    uint64_t ctx_addr;
+    uint64_t src_addr;
+    uint64_t dst_addr;
+    size_t iv_offset;
+    size_t blocklen;
+    size_t keylen;
+
+    if (len == 0) {
+        return;
+    }
+
+    if (!crypt_decode_cmd(cmd, &alg, &mode, &keylen, &blocklen, &iv_offset)) {
+        qemu_log_mask(LOG_UNIMP,
+                      "%s: Unsupported crypt command 0x%x\n", __func__, cmd);
+        return;
+    }
+
+    /* Fetch the IV and key from the context buffer in DRAM. */
+    ctx_addr = s->regs[R_CRYPT_CONTEXT];
+    if (address_space_read(&s->dram_as, ctx_addr, MEMTXATTRS_UNSPECIFIED,
+                           ctx, sizeof(ctx))) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: Failed to read context, addr=0x%" HWADDR_PRIx "\n",
+                      __func__, ctx_addr);
+        return;
+    }
+
+    if (trace_event_get_state_backends(TRACE_ASPEED_HACE_HEXDUMP)) {
+        hace_hexdump("context", (char *)ctx, sizeof(ctx));
+    }
+
+    cipher = qcrypto_cipher_new(alg, mode, ctx + CRYPT_CTX_KEY_OFFSET, keylen,
+                                &local_err);
+    if (cipher == NULL) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: qcrypto cipher new failed: %s\n",
+                      __func__, error_get_pretty(local_err));
+        error_free(local_err);
+        return;
+    }
+
+    if (mode != QCRYPTO_CIPHER_MODE_ECB &&
+        qcrypto_cipher_setiv(cipher, ctx + iv_offset, blocklen,
+                             &local_err) < 0) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: qcrypto cipher setiv failed: %s\n",
+                      __func__, error_get_pretty(local_err));
+        error_free(local_err);
+        return;
+    }
+
+    src_buf = g_malloc0(len);
+    dst_buf = g_malloc0(len);
+
+    src_addr = s->regs[R_CRYPT_SRC];
+    if (!crypt_prepare_direct(s, src_addr, src_buf, len, false)) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: Failed to read src, addr=0x%" HWADDR_PRIx "\n",
+                      __func__, src_addr);
+        return;
+    }
+
+    if (trace_event_get_state_backends(TRACE_ASPEED_HACE_HEXDUMP)) {
+        hace_hexdump("src", (char *)src_buf, len);
+    }
+
+    if (encrypt) {
+        if (qcrypto_cipher_encrypt(cipher, src_buf, dst_buf, len,
+                                   &local_err) < 0) {
+            qemu_log_mask(LOG_GUEST_ERROR, "%s: encrypt failed: %s\n",
+                          __func__, error_get_pretty(local_err));
+            error_free(local_err);
+            return;
+        }
+    } else {
+        if (qcrypto_cipher_decrypt(cipher, src_buf, dst_buf, len,
+                                   &local_err) < 0) {
+            qemu_log_mask(LOG_GUEST_ERROR, "%s: decrypt failed: %s\n",
+                          __func__, error_get_pretty(local_err));
+            error_free(local_err);
+            return;
+        }
+    }
+
+    dst_addr = s->regs[R_CRYPT_DEST];
+    if (!crypt_prepare_direct(s, dst_addr, dst_buf, len, true)) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: Failed to write dst, addr=0x%" HWADDR_PRIx "\n",
+                      __func__, dst_addr);
+        return;
+    }
+
+    if (trace_event_get_state_backends(TRACE_ASPEED_HACE_HEXDUMP)) {
+        hace_hexdump("dst", (char *)dst_buf, len);
+    }
+
+    if (mode == QCRYPTO_CIPHER_MODE_CBC) {
+        /*
+         * CBC chains on the last ciphertext block: the final block of the
+         * output when encrypting, or of the input when decrypting. Write it
+         * back as the IV for the next request.
+         */
+        next_iv = (encrypt ? dst_buf : src_buf) + len - blocklen;
+        if (address_space_write(&s->dram_as, ctx_addr + iv_offset,
+                                MEMTXATTRS_UNSPECIFIED, next_iv, blocklen)) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "%s: Failed to write IV, addr=0x%" HWADDR_PRIx "\n",
+                          __func__, ctx_addr + iv_offset);
+        }
+    }
+}
+
 static uint64_t aspeed_hace_read(void *opaque, hwaddr addr, unsigned int size)
 {
     AspeedHACEState *s = ASPEED_HACE(opaque);
@@ -531,16 +767,22 @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data,
                 qemu_irq_lower(s->irq);
             }
         }
-        if (ahc->raise_crypt_interrupt_workaround) {
-            if (data & CRYPT_IRQ) {
-                data &= ~CRYPT_IRQ;
+        if (data & CRYPT_IRQ) {
+            data &= ~CRYPT_IRQ;
 
-                if (s->regs[addr] & CRYPT_IRQ) {
-                    qemu_irq_lower(s->irq);
-                }
+            if (s->regs[addr] & CRYPT_IRQ) {
+                qemu_irq_lower(s->irq);
             }
         }
         break;
+    case R_CRYPT_SRC:
+    case R_CRYPT_DEST:
+    case R_CRYPT_CONTEXT:
+        data &= ahc->src_mask;
+        break;
+    case R_CRYPT_DATA_LEN:
+        data &= CRYPT_DATA_LEN_MASK;
+        break;
     case R_HASH_SRC:
         data &= ahc->src_mask;
         break;
@@ -589,13 +831,19 @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data,
         break;
     }
     case R_CRYPT_CMD:
-        qemu_log_mask(LOG_UNIMP, "%s: Crypt commands not implemented\n",
-                       __func__);
-        if (ahc->raise_crypt_interrupt_workaround) {
-            s->regs[R_STATUS] |= CRYPT_IRQ;
-            if (data & CRYPT_IRQ_EN) {
-                qemu_irq_raise(s->irq);
-            }
+        /*
+         * The AST2700 crypto engine needs 64-bit DMA and AES-GCM, which are
+         * added later; until then it keeps the temporary workaround of only
+         * raising the completion interrupt without running the command.
+         */
+        if (!ahc->raise_crypt_interrupt_workaround) {
+            do_crypt_operation(s, data);
+        }
+
+        /* Hardware raises the crypt interrupt once the command finishes. */
+        s->regs[R_STATUS] |= CRYPT_IRQ;
+        if (data & CRYPT_CMD_ISR_EN) {
+            qemu_irq_raise(s->irq);
         }
         break;
     case R_HASH_SRC_HI:
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v1 02/15] tests/qtest/aspeed-hace: Test the crypto command on the AST2500
  2026-07-14  7:29 [PATCH v1 00/15] Support the ASPEED HACE crypto command Jamin Lin
  2026-07-14  7:29 ` [PATCH v1 01/15] hw/misc/aspeed_hace: Support the crypto command in direct access mode Jamin Lin
@ 2026-07-14  7:29 ` Jamin Lin
  2026-07-14  7:29 ` [PATCH v1 03/15] hw/misc/aspeed_hace: Support scatter-gather mode for the crypto command Jamin Lin
                   ` (13 subsequent siblings)
  15 siblings, 0 replies; 23+ messages in thread
From: Jamin Lin @ 2026-07-14  7:29 UTC (permalink / raw)
  To: Daniel P. Berrangé, Cédric Le Goater, Peter Maydell,
	Steven Lee, Troy Lee, Kane Chen, Andrew Jeffery, Joel Stanley,
	Eric Blake, Markus Armbruster, Fabiano Rosas, Laurent Vivier,
	Paolo Bonzini, open list:All patches CC here,
	open list:ASPEED BMCs
  Cc: Jamin Lin, Troy Lee

Add a crypto known-answer test harness and exercise the AST2500, which
uses the crypto engine's direct access mode. Each mode (AES/DES/3DES in
ECB and CBC) is a separate test that checks the ciphertext, the
plaintext round-trip and, for CBC, the chaining IV written back to the
context buffer.

The key/IV/plaintext/ciphertext values are taken verbatim from the Linux
kernel crypto self-test templates in crypto/testmgr.h.

Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
---
 tests/qtest/aspeed-hace-utils.h |  16 ++
 tests/qtest/aspeed-hace-utils.c | 304 ++++++++++++++++++++++++++++++++
 tests/qtest/aspeed_hace-test.c  |   6 +
 3 files changed, 326 insertions(+)

diff --git a/tests/qtest/aspeed-hace-utils.h b/tests/qtest/aspeed-hace-utils.h
index 27ab2bb975..13feaa61e4 100644
--- a/tests/qtest/aspeed-hace-utils.h
+++ b/tests/qtest/aspeed-hace-utils.h
@@ -79,5 +79,21 @@ void aspeed_test_sha512_accum(const char *machine, const uint32_t base,
 void aspeed_test_addresses(const char *machine, const uint32_t base,
                            const struct AspeedMasks *expected);
 
+/*
+ * Cipher modes a SoC's crypto engine supports, for aspeed_add_crypto_tests().
+ */
+enum {
+    CRYPT_MODE_ECB = 1 << 0,
+    CRYPT_MODE_CBC = 1 << 1,
+};
+
+/*
+ * Register the crypto known-answer tests that @modes selects (a mask of
+ * CRYPT_MODE_*) for the given machine. Each test is named
+ * "<prefix>/hace/crypto/<mode>".
+ */
+void aspeed_add_crypto_tests(const char *prefix, const char *machine,
+                             uint32_t base, uint64_t dram, uint32_t modes);
+
 #endif /* TESTS_ASPEED_HACE_UTILS_H */
 
diff --git a/tests/qtest/aspeed-hace-utils.c b/tests/qtest/aspeed-hace-utils.c
index 25450a296b..4deb88dbcc 100644
--- a/tests/qtest/aspeed-hace-utils.c
+++ b/tests/qtest/aspeed-hace-utils.c
@@ -645,3 +645,307 @@ void aspeed_test_addresses(const char *machine, const uint32_t base,
     qtest_quit(s);
 }
 
+/*
+ * Crypto engine register layout (offsets from the HACE base).
+ */
+#define HACE_CRYPTO_SRC          0x00
+#define HACE_CRYPTO_DEST         0x04
+#define HACE_CRYPTO_CONTEXT      0x08
+#define HACE_CRYPTO_DATA_LEN     0x0c
+#define HACE_CRYPTO_CMD          0x10
+
+/* Crypto command bits */
+#define HACE_CMD_ENCRYPT         BIT(7)
+#define HACE_CMD_ISR_EN          BIT(12)
+#define HACE_CMD_DES_SELECT      BIT(16)
+#define HACE_CMD_TRIPLE_DES      BIT(17)
+#define HACE_CMD_SRC_SG_CTRL     BIT(18)
+#define HACE_CMD_DST_SG_CTRL     BIT(19)
+#define HACE_CMD_OP_MODE_MASK    (0x7 << 4)
+#define HACE_CMD_ECB             (0x0 << 4)
+#define HACE_CMD_CBC             (0x1 << 4)
+#define HACE_CMD_AES128          (0x0 << 2)
+
+/* Context buffer layout: IV (DES at +8), key at +0x10 */
+#define HACE_CTX_KEY_OFFSET      0x10
+#define HACE_CTX_SIZE            0x30
+
+/*
+ * Crypto known-answer test vectors, taken verbatim from the Linux kernel
+ * crypto self-test templates in crypto/testmgr.h:
+ *
+ *   https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/crypto/testmgr.h?h=v6.18
+ *
+ * The originating template is noted above each block. CTR and the longer CBC
+ * vectors are truncated to a single block (still a valid known-answer test as
+ * the first block only depends on the IV).
+ */
+
+/* aes_tv_template[0] (FIPS-197) */
+static const uint8_t aes128_ecb_key[16] = {
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
+static const uint8_t aes128_ecb_ptext[16] = {
+    0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+    0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff };
+static const uint8_t aes128_ecb_ctext[16] = {
+    0x69, 0xc4, 0xe0, 0xd8, 0x6a, 0x7b, 0x04, 0x30,
+    0xd8, 0xcd, 0xb7, 0x80, 0x70, 0xb4, 0xc5, 0x5a };
+
+/* aes_cbc_tv_template[0] (RFC 3602) */
+static const uint8_t aes128_cbc_key[16] = {
+    0x06, 0xa9, 0x21, 0x40, 0x36, 0xb8, 0xa1, 0x5b,
+    0x51, 0x2e, 0x03, 0xd5, 0x34, 0x12, 0x00, 0x06 };
+static const uint8_t aes128_cbc_iv[16] = {
+    0x3d, 0xaf, 0xba, 0x42, 0x9d, 0x9e, 0xb4, 0x30,
+    0xb4, 0x22, 0xda, 0x80, 0x2c, 0x9f, 0xac, 0x41 };
+static const uint8_t aes128_cbc_ptext[16] = {
+    0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x62,
+    0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x6d, 0x73, 0x67 };
+static const uint8_t aes128_cbc_ctext[16] = {
+    0xe3, 0x53, 0x77, 0x9c, 0x10, 0x79, 0xae, 0xb8,
+    0x27, 0x08, 0x94, 0x2d, 0xbe, 0x77, 0x18, 0x1a };
+static const uint8_t aes128_cbc_ivout[16] = {
+    0xe3, 0x53, 0x77, 0x9c, 0x10, 0x79, 0xae, 0xb8,
+    0x27, 0x08, 0x94, 0x2d, 0xbe, 0x77, 0x18, 0x1a };
+
+/* des_tv_template[0] (Applied Cryptography) */
+static const uint8_t des_ecb_key[8] = {
+    0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef };
+static const uint8_t des_ecb_ptext[8] = {
+    0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xe7 };
+static const uint8_t des_ecb_ctext[8] = {
+    0xc9, 0x57, 0x44, 0x25, 0x6a, 0x5e, 0xd3, 0x1d };
+
+/* des_cbc_tv_template[0] (OpenSSL), first block */
+static const uint8_t des_cbc_key[8] = {
+    0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef };
+static const uint8_t des_cbc_iv[8] = {
+    0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10 };
+static const uint8_t des_cbc_ptext[8] = {
+    0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x20 };
+static const uint8_t des_cbc_ctext[8] = {
+    0xcc, 0xd1, 0x73, 0xff, 0xab, 0x20, 0x39, 0xf4 };
+
+/* des3_ede_tv_template[0] (OpenSSL) */
+static const uint8_t tdes_ecb_key[24] = {
+    0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
+    0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
+    0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10 };
+static const uint8_t tdes_ecb_ptext[8] = {
+    0x73, 0x6f, 0x6d, 0x65, 0x64, 0x61, 0x74, 0x61 };
+static const uint8_t tdes_ecb_ctext[8] = {
+    0x18, 0xd7, 0x48, 0xe5, 0x63, 0x62, 0x05, 0x72 };
+
+/* des3_ede_cbc_tv_template[0] (OpenSSL), first block */
+static const uint8_t tdes_cbc_key[24] = {
+    0xe9, 0xc0, 0xff, 0x2e, 0x76, 0x0b, 0x64, 0x24,
+    0x44, 0x4d, 0x99, 0x5a, 0x12, 0xd6, 0x40, 0xc0,
+    0xea, 0xc2, 0x84, 0xe8, 0x14, 0x95, 0xdb, 0xe8 };
+static const uint8_t tdes_cbc_iv[8] = {
+    0x7d, 0x33, 0x88, 0x93, 0x0f, 0x93, 0xb2, 0x42 };
+static const uint8_t tdes_cbc_ptext[8] = {
+    0x6f, 0x54, 0x20, 0x6f, 0x61, 0x4d, 0x79, 0x6e };
+static const uint8_t tdes_cbc_ctext[8] = {
+    0x0e, 0x2d, 0xb6, 0x97, 0x3c, 0x56, 0x33, 0xf4 };
+
+typedef struct CryptTest {
+    const uint8_t *ptext;
+    const uint8_t *ctext;
+    /* expected context IV after encrypt, or NULL */
+    const uint8_t *iv_out;
+    const uint8_t *key;
+    const uint8_t *iv;
+    const char *name;
+    /* algorithm | mode | key size selection */
+    uint32_t cmd;
+    size_t keylen;
+    size_t ivlen;
+    size_t len;
+} CryptTest;
+
+static const CryptTest crypt_tests[] = {
+    {
+        .name = "aes128-ecb",
+        .cmd = HACE_CMD_AES128 | HACE_CMD_ECB,
+        .key = aes128_ecb_key,
+        .keylen = sizeof(aes128_ecb_key),
+        .ptext = aes128_ecb_ptext,
+        .ctext = aes128_ecb_ctext,
+        .len = sizeof(aes128_ecb_ptext),
+    },
+    {
+        .name = "aes128-cbc",
+        .cmd = HACE_CMD_AES128 | HACE_CMD_CBC,
+        .key = aes128_cbc_key,
+        .keylen = sizeof(aes128_cbc_key),
+        .iv = aes128_cbc_iv,
+        .ivlen = sizeof(aes128_cbc_iv),
+        .ptext = aes128_cbc_ptext,
+        .ctext = aes128_cbc_ctext,
+        .iv_out = aes128_cbc_ivout,
+        .len = sizeof(aes128_cbc_ptext),
+    },
+    {
+        .name = "des-ecb",
+        .cmd = HACE_CMD_DES_SELECT | HACE_CMD_ECB,
+        .key = des_ecb_key,
+        .keylen = sizeof(des_ecb_key),
+        .ptext = des_ecb_ptext,
+        .ctext = des_ecb_ctext,
+        .len = sizeof(des_ecb_ptext),
+    },
+    {
+        .name = "des-cbc",
+        .cmd = HACE_CMD_DES_SELECT | HACE_CMD_CBC,
+        .key = des_cbc_key,
+        .keylen = sizeof(des_cbc_key),
+        .iv = des_cbc_iv,
+        .ivlen = sizeof(des_cbc_iv),
+        .ptext = des_cbc_ptext,
+        .ctext = des_cbc_ctext,
+        .len = sizeof(des_cbc_ptext),
+    },
+    {
+        .name = "des3_ede-ecb",
+        .cmd = HACE_CMD_DES_SELECT | HACE_CMD_TRIPLE_DES | HACE_CMD_ECB,
+        .key = tdes_ecb_key,
+        .keylen = sizeof(tdes_ecb_key),
+        .ptext = tdes_ecb_ptext,
+        .ctext = tdes_ecb_ctext,
+        .len = sizeof(tdes_ecb_ptext),
+    },
+    {
+        .name = "des3_ede-cbc",
+        .cmd = HACE_CMD_DES_SELECT | HACE_CMD_TRIPLE_DES | HACE_CMD_CBC,
+        .key = tdes_cbc_key,
+        .keylen = sizeof(tdes_cbc_key),
+        .iv = tdes_cbc_iv,
+        .ivlen = sizeof(tdes_cbc_iv),
+        .ptext = tdes_cbc_ptext,
+        .ctext = tdes_cbc_ctext,
+        .len = sizeof(tdes_cbc_ptext),
+    },
+};
+
+/* DRAM offsets for the crypto test source, destination and context buffers. */
+#define CRYPT_OFF_SRC   0x10000
+#define CRYPT_OFF_DST   0x20000
+#define CRYPT_OFF_CTX   0x30000
+
+/* Describes one registered crypto test (qtest_add_data_func() data pointer). */
+typedef struct AspeedCryptoTest {
+    const char *machine;
+    uint64_t dram;
+    uint32_t base;
+    int index;
+} AspeedCryptoTest;
+
+/* Map a command's operation mode (HACE10[6:4]) to a CRYPT_MODE_* flag. */
+static uint32_t crypt_mode_flag(uint32_t cmd)
+{
+    switch (cmd & HACE_CMD_OP_MODE_MASK) {
+    case HACE_CMD_ECB:
+        return CRYPT_MODE_ECB;
+    case HACE_CMD_CBC:
+        return CRYPT_MODE_CBC;
+    default:
+        return 0;
+    }
+}
+
+static void crypt_write_ctx(QTestState *s, uint64_t ctx_addr,
+                            const CryptTest *t)
+{
+    size_t iv_off = (t->cmd & HACE_CMD_DES_SELECT) ? 8 : 0;
+    uint8_t ctx[HACE_CTX_SIZE] = { 0 };
+
+    if (t->iv) {
+        memcpy(ctx + iv_off, t->iv, t->ivlen);
+    }
+    memcpy(ctx + HACE_CTX_KEY_OFFSET, t->key, t->keylen);
+    qtest_memwrite(s, ctx_addr, ctx, sizeof(ctx));
+}
+
+/* Run one crypto operation in direct access mode and read back the result. */
+static void crypt_run_direct(QTestState *s, uint32_t base, uint64_t dram,
+                             const CryptTest *t, bool encrypt, uint8_t *out)
+{
+    const uint8_t *in = encrypt ? t->ptext : t->ctext;
+    uint32_t cmd = t->cmd | HACE_CMD_ISR_EN;
+    uint64_t src = dram + CRYPT_OFF_SRC;
+    uint64_t dst = dram + CRYPT_OFF_DST;
+    uint64_t ctx = dram + CRYPT_OFF_CTX;
+
+    if (encrypt) {
+        cmd |= HACE_CMD_ENCRYPT;
+    }
+
+    crypt_write_ctx(s, ctx, t);
+    qtest_memwrite(s, src, in, t->len);
+
+    qtest_writel(s, base + HACE_CRYPTO_SRC, (uint32_t)src);
+    qtest_writel(s, base + HACE_CRYPTO_DEST, (uint32_t)dst);
+    qtest_writel(s, base + HACE_CRYPTO_CONTEXT, (uint32_t)ctx);
+    qtest_writel(s, base + HACE_CRYPTO_DATA_LEN, t->len);
+    qtest_writel(s, base + HACE_CRYPTO_CMD, cmd);
+
+    g_assert_cmphex(qtest_readl(s, base + HACE_STS) & HACE_CRYPTO_ISR, ==,
+                    HACE_CRYPTO_ISR);
+    qtest_writel(s, base + HACE_STS, HACE_CRYPTO_ISR);
+
+    qtest_memread(s, dst, out, t->len);
+}
+
+static void aspeed_test_crypto_direct(const void *data)
+{
+    const AspeedCryptoTest *c = data;
+    const CryptTest *t = &crypt_tests[c->index];
+    QTestState *s = qtest_init(c->machine);
+    uint8_t out[64];
+    uint8_t iv[16];
+    size_t iv_off;
+
+    g_assert_cmpuint(t->len, <=, sizeof(out));
+
+    /* Encrypt: ptext -> ctext */
+    crypt_run_direct(s, c->base, c->dram, t, true, out);
+    g_assert_cmpmem(out, t->len, t->ctext, t->len);
+
+    if (t->iv_out) {
+        iv_off = (t->cmd & HACE_CMD_DES_SELECT) ? 8 : 0;
+        qtest_memread(s, c->dram + CRYPT_OFF_CTX + iv_off, iv, t->ivlen);
+        g_assert_cmpmem(iv, t->ivlen, t->iv_out, t->ivlen);
+    }
+
+    /* Decrypt: ctext -> ptext */
+    crypt_run_direct(s, c->base, c->dram, t, false, out);
+    g_assert_cmpmem(out, t->len, t->ptext, t->len);
+
+    qtest_quit(s);
+}
+
+void aspeed_add_crypto_tests(const char *prefix, const char *machine,
+                             uint32_t base, uint64_t dram, uint32_t modes)
+{
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(crypt_tests); i++) {
+        g_autofree char *path = NULL;
+        AspeedCryptoTest *t;
+
+        if (!(modes & crypt_mode_flag(crypt_tests[i].cmd))) {
+            continue;
+        }
+
+        path = g_strdup_printf("%s/hace/crypto/%s", prefix,
+                               crypt_tests[i].name);
+        t = g_new0(AspeedCryptoTest, 1);
+        t->machine = machine;
+        t->base = base;
+        t->dram = dram;
+        t->index = i;
+        qtest_add_data_func_full(path, t, aspeed_test_crypto_direct, g_free);
+    }
+}
+
diff --git a/tests/qtest/aspeed_hace-test.c b/tests/qtest/aspeed_hace-test.c
index 38777020ca..4cb4c475e9 100644
--- a/tests/qtest/aspeed_hace-test.c
+++ b/tests/qtest/aspeed_hace-test.c
@@ -229,6 +229,12 @@ int main(int argc, char **argv)
     qtest_add_func("ast2500/hace/sha256", test_sha256_ast2500);
     qtest_add_func("ast2500/hace/md5", test_md5_ast2500);
 
+    /*
+     * The AST2500 crypto engine uses direct access mode and supports ECB/CBC.
+     */
+    aspeed_add_crypto_tests("ast2500", "-machine ast2500-evb", 0x1e6e3000,
+                            0x80000000, CRYPT_MODE_ECB | CRYPT_MODE_CBC);
+
     qtest_add_func("ast2400/hace/addresses", test_addresses_ast2400);
     qtest_add_func("ast2400/hace/sha512", test_sha512_ast2400);
     qtest_add_func("ast2400/hace/sha256", test_sha256_ast2400);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v1 03/15] hw/misc/aspeed_hace: Support scatter-gather mode for the crypto command
  2026-07-14  7:29 [PATCH v1 00/15] Support the ASPEED HACE crypto command Jamin Lin
  2026-07-14  7:29 ` [PATCH v1 01/15] hw/misc/aspeed_hace: Support the crypto command in direct access mode Jamin Lin
  2026-07-14  7:29 ` [PATCH v1 02/15] tests/qtest/aspeed-hace: Test the crypto command on the AST2500 Jamin Lin
@ 2026-07-14  7:29 ` Jamin Lin
  2026-07-14  7:29 ` [PATCH v1 04/15] hw/misc/aspeed_hace: Support the CTR " Jamin Lin
                   ` (12 subsequent siblings)
  15 siblings, 0 replies; 23+ messages in thread
From: Jamin Lin @ 2026-07-14  7:29 UTC (permalink / raw)
  To: Daniel P. Berrangé, Cédric Le Goater, Peter Maydell,
	Steven Lee, Troy Lee, Kane Chen, Andrew Jeffery, Joel Stanley,
	Eric Blake, Markus Armbruster, Fabiano Rosas, Laurent Vivier,
	Paolo Bonzini, open list:All patches CC here,
	open list:ASPEED BMCs
  Cc: Jamin Lin, Troy Lee

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 <jamin_lin@aspeedtech.com>
---
 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 42d8b38be3..59eb1aeae3 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;
@@ -679,8 +725,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);
@@ -709,8 +761,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


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v1 04/15] hw/misc/aspeed_hace: Support the CTR mode for the crypto command
  2026-07-14  7:29 [PATCH v1 00/15] Support the ASPEED HACE crypto command Jamin Lin
                   ` (2 preceding siblings ...)
  2026-07-14  7:29 ` [PATCH v1 03/15] hw/misc/aspeed_hace: Support scatter-gather mode for the crypto command Jamin Lin
@ 2026-07-14  7:29 ` Jamin Lin
  2026-07-14  7:29 ` [PATCH v1 05/15] tests/qtest/aspeed-hace: Test the crypto command on the AST2600 Jamin Lin
                   ` (11 subsequent siblings)
  15 siblings, 0 replies; 23+ messages in thread
From: Jamin Lin @ 2026-07-14  7:29 UTC (permalink / raw)
  To: Daniel P. Berrangé, Cédric Le Goater, Peter Maydell,
	Steven Lee, Troy Lee, Kane Chen, Andrew Jeffery, Joel Stanley,
	Eric Blake, Markus Armbruster, Fabiano Rosas, Laurent Vivier,
	Paolo Bonzini, open list:All patches CC here,
	open list:ASPEED BMCs
  Cc: Jamin Lin, Troy Lee

The AST2600, AST1030 and later crypto engines add AES/DES/3DES CTR mode
(HACE10[6:4] = 0b100) on top of the ECB/CBC modes shared with the
AST2500. Decode the CTR selection, round the working buffers up to a
whole block so the stream-like final block is still processed a block at
a time, and write the counter advanced by the number of blocks consumed
back to the context buffer so the driver can continue across requests.

Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
---
 hw/misc/aspeed_hace.c | 50 ++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 45 insertions(+), 5 deletions(-)

diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c
index 59eb1aeae3..b7a7c0b7ff 100644
--- a/hw/misc/aspeed_hace.c
+++ b/hw/misc/aspeed_hace.c
@@ -42,6 +42,7 @@
 #define  CRYPT_CMD_OP_MODE_MASK     (0x7 << 4)
 #define  CRYPT_CMD_ECB              (0x0 << 4)
 #define  CRYPT_CMD_CBC              (0x1 << 4)
+#define  CRYPT_CMD_CTR              (0x4 << 4)
 /* AES key length HACE10[3:2] */
 #define  CRYPT_CMD_AES_KEY_LEN_MASK (0x3 << 2)
 #define  CRYPT_CMD_AES256           (0x2 << 2)
@@ -589,6 +590,9 @@ static bool crypt_decode_cmd(uint32_t cmd, QCryptoCipherAlgo *alg,
     case CRYPT_CMD_CBC:
         *mode = QCRYPTO_CIPHER_MODE_CBC;
         break;
+    case CRYPT_CMD_CTR:
+        *mode = QCRYPTO_CIPHER_MODE_CTR;
+        break;
     default:
         return false;
     }
@@ -652,6 +656,22 @@ static bool crypt_prepare_sg(AspeedHACEState *s, uint64_t addr,
     return copied == len;
 }
 
+/*
+ * Add @add to the big-endian counter block @ctr (@len bytes) in place, so the
+ * CTR mode counter can be advanced by the number of blocks just consumed.
+ */
+static void crypt_be_add(uint8_t *ctr, size_t len, uint64_t add)
+{
+    size_t i = len;
+
+    while (i > 0 && add) {
+        i--;
+        add += ctr[i];
+        ctr[i] = add & 0xff;
+        add >>= 8;
+    }
+}
+
 /*
  * Perform an AES/DES/3DES ECB/CBC operation. The source and destination are
  * either single contiguous buffers (direct access mode) or scatter-gather
@@ -677,6 +697,7 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
     uint64_t dst_addr;
     size_t iv_offset;
     size_t blocklen;
+    size_t buf_len;
     size_t keylen;
     bool status;
 
@@ -722,8 +743,14 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
         return;
     }
 
-    src_buf = g_malloc0(len);
-    dst_buf = g_malloc0(len);
+    /*
+     * Round the working buffers up to a whole block. Block modes are already
+     * block-aligned; the stream-like CTR mode may leave a partial final block
+     * that the engine still processes a full block at a time.
+     */
+    buf_len = QEMU_ALIGN_UP(len, blocklen);
+    src_buf = g_malloc0(buf_len);
+    dst_buf = g_malloc0(buf_len);
 
     /* Gather the source into the bounce buffer, per the selected mode. */
     src_addr = s->regs[R_CRYPT_SRC];
@@ -744,7 +771,7 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
     }
 
     if (encrypt) {
-        if (qcrypto_cipher_encrypt(cipher, src_buf, dst_buf, len,
+        if (qcrypto_cipher_encrypt(cipher, src_buf, dst_buf, buf_len,
                                    &local_err) < 0) {
             qemu_log_mask(LOG_GUEST_ERROR, "%s: encrypt failed: %s\n",
                           __func__, error_get_pretty(local_err));
@@ -752,7 +779,7 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
             return;
         }
     } else {
-        if (qcrypto_cipher_decrypt(cipher, src_buf, dst_buf, len,
+        if (qcrypto_cipher_decrypt(cipher, src_buf, dst_buf, buf_len,
                                    &local_err) < 0) {
             qemu_log_mask(LOG_GUEST_ERROR, "%s: decrypt failed: %s\n",
                           __func__, error_get_pretty(local_err));
@@ -785,13 +812,26 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
          * output when encrypting, or of the input when decrypting. Write it
          * back as the IV for the next request.
          */
-        next_iv = (encrypt ? dst_buf : src_buf) + len - blocklen;
+        next_iv = (encrypt ? dst_buf : src_buf) + buf_len - blocklen;
         if (address_space_write(&s->dram_as, ctx_addr + iv_offset,
                                 MEMTXATTRS_UNSPECIFIED, next_iv, blocklen)) {
             qemu_log_mask(LOG_GUEST_ERROR,
                           "%s: Failed to write IV, addr=0x%" HWADDR_PRIx "\n",
                           __func__, ctx_addr + iv_offset);
         }
+    } else if (mode == QCRYPTO_CIPHER_MODE_CTR) {
+        /*
+         * CTR chains on the counter, which advances by one per block. Add the
+         * number of blocks processed (buf_len / blocklen) and write it back.
+         */
+        crypt_be_add(ctx + iv_offset, blocklen, buf_len / blocklen);
+        if (address_space_write(&s->dram_as, ctx_addr + iv_offset,
+                                MEMTXATTRS_UNSPECIFIED, ctx + iv_offset,
+                                blocklen)) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "%s: Failed to write IV, addr=0x%" HWADDR_PRIx "\n",
+                          __func__, ctx_addr + iv_offset);
+        }
     }
 }
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v1 05/15] tests/qtest/aspeed-hace: Test the crypto command on the AST2600
  2026-07-14  7:29 [PATCH v1 00/15] Support the ASPEED HACE crypto command Jamin Lin
                   ` (3 preceding siblings ...)
  2026-07-14  7:29 ` [PATCH v1 04/15] hw/misc/aspeed_hace: Support the CTR " Jamin Lin
@ 2026-07-14  7:29 ` Jamin Lin
  2026-07-14 10:16   ` Cédric Le Goater
  2026-07-14  7:29 ` [PATCH v1 06/15] tests/qtest/aspeed-hace: Test the crypto command on the AST1030 Jamin Lin
                   ` (10 subsequent siblings)
  15 siblings, 1 reply; 23+ messages in thread
From: Jamin Lin @ 2026-07-14  7:29 UTC (permalink / raw)
  To: Daniel P. Berrangé, Cédric Le Goater, Peter Maydell,
	Steven Lee, Troy Lee, Kane Chen, Andrew Jeffery, Joel Stanley,
	Eric Blake, Markus Armbruster, Fabiano Rosas, Laurent Vivier,
	Paolo Bonzini, open list:All patches CC here,
	open list:ASPEED BMCs
  Cc: Jamin Lin, Troy Lee

Extend the crypto known-answer tests to cover the AST2600 crypto engine,
which drives the source and destination through scatter-gather lists and
adds CTR mode on top of the ECB/CBC modes shared with the AST2500.

Add a scatter-gather runner that describes each buffer with three
non-adjacent fragments to exercise the gather/scatter path, add
AES/DES/3DES CTR vectors (verifying the counter written back to the
context buffer), and give aspeed_add_crypto_tests() a mode mask and a
scatter-gather flag so each SoC registers exactly the modes and transfer
method it supports. Register the AST2600 with ECB/CBC/CTR in
scatter-gather mode.

Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
---
 tests/qtest/aspeed-hace-utils.h |   7 +-
 tests/qtest/aspeed-hace-utils.c | 211 +++++++++++++++++++++++++++++++-
 tests/qtest/aspeed_hace-test.c  |   8 +-
 3 files changed, 218 insertions(+), 8 deletions(-)

diff --git a/tests/qtest/aspeed-hace-utils.h b/tests/qtest/aspeed-hace-utils.h
index 13feaa61e4..82b0b3f93d 100644
--- a/tests/qtest/aspeed-hace-utils.h
+++ b/tests/qtest/aspeed-hace-utils.h
@@ -85,15 +85,18 @@ void aspeed_test_addresses(const char *machine, const uint32_t base,
 enum {
     CRYPT_MODE_ECB = 1 << 0,
     CRYPT_MODE_CBC = 1 << 1,
+    CRYPT_MODE_CTR = 1 << 2,
 };
 
 /*
  * Register the crypto known-answer tests that @modes selects (a mask of
  * CRYPT_MODE_*) for the given machine. Each test is named
- * "<prefix>/hace/crypto/<mode>".
+ * "<prefix>/hace/crypto/<mode>". @sg selects scatter-gather mode (used by the
+ * AST2600 and later) instead of the AST2500 direct access mode.
  */
 void aspeed_add_crypto_tests(const char *prefix, const char *machine,
-                             uint32_t base, uint64_t dram, uint32_t modes);
+                             uint32_t base, uint64_t dram, uint32_t modes,
+                             bool sg);
 
 #endif /* TESTS_ASPEED_HACE_UTILS_H */
 
diff --git a/tests/qtest/aspeed-hace-utils.c b/tests/qtest/aspeed-hace-utils.c
index 4deb88dbcc..0b91a4e61a 100644
--- a/tests/qtest/aspeed-hace-utils.c
+++ b/tests/qtest/aspeed-hace-utils.c
@@ -664,6 +664,7 @@ void aspeed_test_addresses(const char *machine, const uint32_t base,
 #define HACE_CMD_OP_MODE_MASK    (0x7 << 4)
 #define HACE_CMD_ECB             (0x0 << 4)
 #define HACE_CMD_CBC             (0x1 << 4)
+#define HACE_CMD_CTR             (0x4 << 4)
 #define HACE_CMD_AES128          (0x0 << 2)
 
 /* Context buffer layout: IV (DES at +8), key at +0x10 */
@@ -749,6 +750,49 @@ static const uint8_t tdes_cbc_ptext[8] = {
 static const uint8_t tdes_cbc_ctext[8] = {
     0x0e, 0x2d, 0xb6, 0x97, 0x3c, 0x56, 0x33, 0xf4 };
 
+/* aes_ctr_tv_template[0] (NIST SP800-38A F.5.1), first block */
+static const uint8_t aes128_ctr_key[16] = {
+    0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+    0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c };
+static const uint8_t aes128_ctr_iv[16] = {
+    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+    0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff };
+static const uint8_t aes128_ctr_ptext[16] = {
+    0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+    0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a };
+static const uint8_t aes128_ctr_ctext[16] = {
+    0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26,
+    0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce };
+static const uint8_t aes128_ctr_ivout[16] = {
+    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+    0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xff, 0x00 };
+
+/* des_ctr_tv_template[0] (Crypto++), first block */
+static const uint8_t des_ctr_key[8] = {
+    0xc9, 0x83, 0xa6, 0xc9, 0xec, 0x0f, 0x32, 0x55 };
+static const uint8_t des_ctr_iv[8] = {
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd };
+static const uint8_t des_ctr_ptext[8] = {
+    0x50, 0xb9, 0x22, 0xae, 0x17, 0x80, 0x0c, 0x75 };
+static const uint8_t des_ctr_ctext[8] = {
+    0x2f, 0x96, 0x06, 0x0f, 0x50, 0xc9, 0x68, 0x03 };
+static const uint8_t des_ctr_ivout[8] = {
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe };
+
+/* des3_ede_ctr_tv_template[0] (Crypto++), first block */
+static const uint8_t tdes_ctr_key[24] = {
+    0x9c, 0xd6, 0xf3, 0x9c, 0xb9, 0x5a, 0x67, 0x00,
+    0x5a, 0x67, 0x00, 0x2d, 0xce, 0xeb, 0x2d, 0xce,
+    0xeb, 0xb4, 0x51, 0x72, 0xb4, 0x51, 0x72, 0x1f };
+static const uint8_t tdes_ctr_iv[8] = {
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+static const uint8_t tdes_ctr_ptext[8] = {
+    0x05, 0xec, 0x77, 0xfb, 0x42, 0xd5, 0x59, 0x20 };
+static const uint8_t tdes_ctr_ctext[8] = {
+    0x07, 0xc2, 0x08, 0x20, 0x72, 0x1f, 0x49, 0xef };
+static const uint8_t tdes_ctr_ivout[8] = {
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
 typedef struct CryptTest {
     const uint8_t *ptext;
     const uint8_t *ctext;
@@ -826,12 +870,59 @@ static const CryptTest crypt_tests[] = {
         .ctext = tdes_cbc_ctext,
         .len = sizeof(tdes_cbc_ptext),
     },
+    {
+        .name = "aes128-ctr",
+        .cmd = HACE_CMD_AES128 | HACE_CMD_CTR,
+        .key = aes128_ctr_key,
+        .keylen = sizeof(aes128_ctr_key),
+        .iv = aes128_ctr_iv,
+        .ivlen = sizeof(aes128_ctr_iv),
+        .ptext = aes128_ctr_ptext,
+        .ctext = aes128_ctr_ctext,
+        .iv_out = aes128_ctr_ivout,
+        .len = sizeof(aes128_ctr_ptext),
+    },
+    {
+        .name = "des-ctr",
+        .cmd = HACE_CMD_DES_SELECT | HACE_CMD_CTR,
+        .key = des_ctr_key,
+        .keylen = sizeof(des_ctr_key),
+        .iv = des_ctr_iv,
+        .ivlen = sizeof(des_ctr_iv),
+        .ptext = des_ctr_ptext,
+        .ctext = des_ctr_ctext,
+        .iv_out = des_ctr_ivout,
+        .len = sizeof(des_ctr_ptext),
+    },
+    {
+        .name = "des3_ede-ctr",
+        .cmd = HACE_CMD_DES_SELECT | HACE_CMD_TRIPLE_DES | HACE_CMD_CTR,
+        .key = tdes_ctr_key,
+        .keylen = sizeof(tdes_ctr_key),
+        .iv = tdes_ctr_iv,
+        .ivlen = sizeof(tdes_ctr_iv),
+        .ptext = tdes_ctr_ptext,
+        .ctext = tdes_ctr_ctext,
+        .iv_out = tdes_ctr_ivout,
+        .len = sizeof(tdes_ctr_ptext),
+    },
 };
 
 /* DRAM offsets for the crypto test source, destination and context buffers. */
 #define CRYPT_OFF_SRC   0x10000
 #define CRYPT_OFF_DST   0x20000
 #define CRYPT_OFF_CTX   0x30000
+/* Scatter-gather list offsets (each list has CRYPT_SG_FRAGS entries). */
+#define CRYPT_OFF_SRC_SG  0x40000
+#define CRYPT_OFF_DST_SG  0x50000
+/*
+ * The scatter-gather tests split each buffer into CRYPT_SG_FRAGS fragments,
+ * each placed CRYPT_SG_FRAG_STRIDE apart so the fragments never abut. The gaps
+ * make the test fail if the engine ignores the list and reads one contiguous
+ * block.
+ */
+#define CRYPT_SG_FRAGS         3
+#define CRYPT_SG_FRAG_STRIDE   0x1000
 
 /* Describes one registered crypto test (qtest_add_data_func() data pointer). */
 typedef struct AspeedCryptoTest {
@@ -839,6 +930,7 @@ typedef struct AspeedCryptoTest {
     uint64_t dram;
     uint32_t base;
     int index;
+    bool sg;
 } AspeedCryptoTest;
 
 /* Map a command's operation mode (HACE10[6:4]) to a CRYPT_MODE_* flag. */
@@ -849,6 +941,8 @@ static uint32_t crypt_mode_flag(uint32_t cmd)
         return CRYPT_MODE_ECB;
     case HACE_CMD_CBC:
         return CRYPT_MODE_CBC;
+    case HACE_CMD_CTR:
+        return CRYPT_MODE_CTR;
     default:
         return 0;
     }
@@ -897,7 +991,104 @@ static void crypt_run_direct(QTestState *s, uint32_t base, uint64_t dram,
     qtest_memread(s, dst, out, t->len);
 }
 
-static void aspeed_test_crypto_direct(const void *data)
+/*
+ * Byte range [*frag_off, *frag_off + *frag_len) of fragment @index when an
+ * @len-byte buffer is split into CRYPT_SG_FRAGS pieces; the last piece takes
+ * the remainder of an uneven split.
+ */
+static void crypt_frag_range(uint32_t len, int index,
+                             uint32_t *frag_off, uint32_t *frag_len)
+{
+    uint32_t base = len / CRYPT_SG_FRAGS;
+
+    *frag_off = base * index;
+    *frag_len = (index == CRYPT_SG_FRAGS - 1) ? len - *frag_off : base;
+}
+
+/*
+ * Scatter [in, len) across CRYPT_SG_FRAGS buffers based at @base_off and spaced
+ * CRYPT_SG_FRAG_STRIDE apart, then build the SG list describing them at @list.
+ * When @in is NULL only the list is built (used for the destination, which the
+ * engine fills in).
+ */
+static void crypt_make_sg(QTestState *s, uint64_t dram, uint32_t base_off,
+                          uint64_t list, const uint8_t *in, uint32_t len)
+{
+    struct AspeedSgList sg[CRYPT_SG_FRAGS];
+    uint32_t frag_off;
+    uint32_t frag_len;
+    uint64_t buf;
+    int i;
+
+    for (i = 0; i < CRYPT_SG_FRAGS; i++) {
+        crypt_frag_range(len, i, &frag_off, &frag_len);
+        buf = dram + base_off + i * CRYPT_SG_FRAG_STRIDE;
+
+        if (in) {
+            qtest_memwrite(s, buf, in + frag_off, frag_len);
+        }
+        sg[i].len = cpu_to_le32(frag_len | (i == CRYPT_SG_FRAGS - 1 ?
+                                            SG_LIST_LEN_LAST : 0));
+        sg[i].addr = cpu_to_le32((uint32_t)buf);
+    }
+
+    qtest_memwrite(s, list, sg, sizeof(sg));
+}
+
+/* Gather a scatter-gathered result back from the CRYPT_SG_FRAGS buffers. */
+static void crypt_gather_sg(QTestState *s, uint64_t dram, uint32_t base_off,
+                            uint8_t *out, uint32_t len)
+{
+    uint32_t frag_off;
+    uint32_t frag_len;
+    int i;
+
+    for (i = 0; i < CRYPT_SG_FRAGS; i++) {
+        crypt_frag_range(len, i, &frag_off, &frag_len);
+        qtest_memread(s, dram + base_off + i * CRYPT_SG_FRAG_STRIDE,
+                      out + frag_off, frag_len);
+    }
+}
+
+/*
+ * Run one block-cipher (ECB/CBC/CTR) operation in scatter-gather mode and read
+ * back the result. The source and destination are each split across
+ * CRYPT_SG_FRAGS non-adjacent DRAM buffers described by an SG list; the gaps
+ * ensure the test fails if the engine ignores the list and reads one
+ * contiguous block.
+ */
+static void crypt_run_sg(QTestState *s, uint32_t base, uint64_t dram,
+                         const CryptTest *t, bool encrypt, uint8_t *out)
+{
+    const uint8_t *in = encrypt ? t->ptext : t->ctext;
+    uint64_t src_sg = dram + CRYPT_OFF_SRC_SG;
+    uint64_t dst_sg = dram + CRYPT_OFF_DST_SG;
+    uint64_t ctx = dram + CRYPT_OFF_CTX;
+    uint32_t cmd = t->cmd | HACE_CMD_ISR_EN | HACE_CMD_SRC_SG_CTRL |
+                   HACE_CMD_DST_SG_CTRL;
+
+    if (encrypt) {
+        cmd |= HACE_CMD_ENCRYPT;
+    }
+
+    crypt_write_ctx(s, ctx, t);
+    crypt_make_sg(s, dram, CRYPT_OFF_SRC, src_sg, in, t->len);
+    crypt_make_sg(s, dram, CRYPT_OFF_DST, dst_sg, NULL, t->len);
+
+    qtest_writel(s, base + HACE_CRYPTO_SRC, (uint32_t)src_sg);
+    qtest_writel(s, base + HACE_CRYPTO_DEST, (uint32_t)dst_sg);
+    qtest_writel(s, base + HACE_CRYPTO_CONTEXT, (uint32_t)ctx);
+    qtest_writel(s, base + HACE_CRYPTO_DATA_LEN, t->len);
+    qtest_writel(s, base + HACE_CRYPTO_CMD, cmd);
+
+    g_assert_cmphex(qtest_readl(s, base + HACE_STS) & HACE_CRYPTO_ISR, ==,
+                    HACE_CRYPTO_ISR);
+    qtest_writel(s, base + HACE_STS, HACE_CRYPTO_ISR);
+
+    crypt_gather_sg(s, dram, CRYPT_OFF_DST, out, t->len);
+}
+
+static void aspeed_test_crypto(const void *data)
 {
     const AspeedCryptoTest *c = data;
     const CryptTest *t = &crypt_tests[c->index];
@@ -909,7 +1100,11 @@ static void aspeed_test_crypto_direct(const void *data)
     g_assert_cmpuint(t->len, <=, sizeof(out));
 
     /* Encrypt: ptext -> ctext */
-    crypt_run_direct(s, c->base, c->dram, t, true, out);
+    if (c->sg) {
+        crypt_run_sg(s, c->base, c->dram, t, true, out);
+    } else {
+        crypt_run_direct(s, c->base, c->dram, t, true, out);
+    }
     g_assert_cmpmem(out, t->len, t->ctext, t->len);
 
     if (t->iv_out) {
@@ -919,14 +1114,19 @@ static void aspeed_test_crypto_direct(const void *data)
     }
 
     /* Decrypt: ctext -> ptext */
-    crypt_run_direct(s, c->base, c->dram, t, false, out);
+    if (c->sg) {
+        crypt_run_sg(s, c->base, c->dram, t, false, out);
+    } else {
+        crypt_run_direct(s, c->base, c->dram, t, false, out);
+    }
     g_assert_cmpmem(out, t->len, t->ptext, t->len);
 
     qtest_quit(s);
 }
 
 void aspeed_add_crypto_tests(const char *prefix, const char *machine,
-                             uint32_t base, uint64_t dram, uint32_t modes)
+                             uint32_t base, uint64_t dram, uint32_t modes,
+                             bool sg)
 {
     int i;
 
@@ -945,7 +1145,8 @@ void aspeed_add_crypto_tests(const char *prefix, const char *machine,
         t->base = base;
         t->dram = dram;
         t->index = i;
-        qtest_add_data_func_full(path, t, aspeed_test_crypto_direct, g_free);
+        t->sg = sg;
+        qtest_add_data_func_full(path, t, aspeed_test_crypto, g_free);
     }
 }
 
diff --git a/tests/qtest/aspeed_hace-test.c b/tests/qtest/aspeed_hace-test.c
index 4cb4c475e9..61a3e3feb5 100644
--- a/tests/qtest/aspeed_hace-test.c
+++ b/tests/qtest/aspeed_hace-test.c
@@ -224,6 +224,12 @@ int main(int argc, char **argv)
     qtest_add_func("ast2600/hace/sha384_accum", test_sha384_accum_ast2600);
     qtest_add_func("ast2600/hace/sha256_accum", test_sha256_accum_ast2600);
 
+    /* The AST2600 crypto engine uses scatter-gather mode and adds CTR. */
+    aspeed_add_crypto_tests("ast2600", "-machine ast2600-evb", 0x1e6d0000,
+                            0x80000000,
+                            CRYPT_MODE_ECB | CRYPT_MODE_CBC | CRYPT_MODE_CTR,
+                            true);
+
     qtest_add_func("ast2500/hace/addresses", test_addresses_ast2500);
     qtest_add_func("ast2500/hace/sha512", test_sha512_ast2500);
     qtest_add_func("ast2500/hace/sha256", test_sha256_ast2500);
@@ -233,7 +239,7 @@ int main(int argc, char **argv)
      * The AST2500 crypto engine uses direct access mode and supports ECB/CBC.
      */
     aspeed_add_crypto_tests("ast2500", "-machine ast2500-evb", 0x1e6e3000,
-                            0x80000000, CRYPT_MODE_ECB | CRYPT_MODE_CBC);
+                            0x80000000, CRYPT_MODE_ECB | CRYPT_MODE_CBC, false);
 
     qtest_add_func("ast2400/hace/addresses", test_addresses_ast2400);
     qtest_add_func("ast2400/hace/sha512", test_sha512_ast2400);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v1 06/15] tests/qtest/aspeed-hace: Test the crypto command on the AST1030
  2026-07-14  7:29 [PATCH v1 00/15] Support the ASPEED HACE crypto command Jamin Lin
                   ` (4 preceding siblings ...)
  2026-07-14  7:29 ` [PATCH v1 05/15] tests/qtest/aspeed-hace: Test the crypto command on the AST2600 Jamin Lin
@ 2026-07-14  7:29 ` Jamin Lin
  2026-07-14  7:29 ` [PATCH v1 07/15] crypto/cipher: Add GCM to QCryptoCipherMode Jamin Lin
                   ` (9 subsequent siblings)
  15 siblings, 0 replies; 23+ messages in thread
From: Jamin Lin @ 2026-07-14  7:29 UTC (permalink / raw)
  To: Daniel P. Berrangé, Cédric Le Goater, Peter Maydell,
	Steven Lee, Troy Lee, Kane Chen, Andrew Jeffery, Joel Stanley,
	Eric Blake, Markus Armbruster, Fabiano Rosas, Laurent Vivier,
	Paolo Bonzini, open list:All patches CC here,
	open list:ASPEED BMCs
  Cc: Jamin Lin, Troy Lee

The AST1030 reuses the AST2600 crypto engine, so it drives the same
scatter-gather transfers and supports the same ECB/CBC/CTR modes. Reuse
the crypto known-answer tests to cover it, registering the AST1030 with
the same modes and scatter-gather flag as the AST2600.

Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
---
 tests/qtest/aspeed_hace-test.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/tests/qtest/aspeed_hace-test.c b/tests/qtest/aspeed_hace-test.c
index 61a3e3feb5..42130df1e2 100644
--- a/tests/qtest/aspeed_hace-test.c
+++ b/tests/qtest/aspeed_hace-test.c
@@ -210,6 +210,12 @@ int main(int argc, char **argv)
     qtest_add_func("ast1030/hace/sha384_accum", test_sha384_accum_ast1030);
     qtest_add_func("ast1030/hace/sha256_accum", test_sha256_accum_ast1030);
 
+    /* The AST1030 reuses the AST2600 crypto engine (scatter-gather, CTR). */
+    aspeed_add_crypto_tests("ast1030", "-machine ast1030-evb", 0x7e6d0000,
+                            0x00000000,
+                            CRYPT_MODE_ECB | CRYPT_MODE_CBC | CRYPT_MODE_CTR,
+                            true);
+
     qtest_add_func("ast2600/hace/addresses", test_addresses_ast2600);
     qtest_add_func("ast2600/hace/sha512", test_sha512_ast2600);
     qtest_add_func("ast2600/hace/sha384", test_sha384_ast2600);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v1 07/15] crypto/cipher: Add GCM to QCryptoCipherMode
  2026-07-14  7:29 [PATCH v1 00/15] Support the ASPEED HACE crypto command Jamin Lin
                   ` (5 preceding siblings ...)
  2026-07-14  7:29 ` [PATCH v1 06/15] tests/qtest/aspeed-hace: Test the crypto command on the AST1030 Jamin Lin
@ 2026-07-14  7:29 ` Jamin Lin
  2026-07-14  8:37   ` Daniel P. Berrangé
  2026-07-14  7:29 ` [PATCH v1 08/15] crypto/cipher: Add setaad/gettag for AEAD modes Jamin Lin
                   ` (8 subsequent siblings)
  15 siblings, 1 reply; 23+ messages in thread
From: Jamin Lin @ 2026-07-14  7:29 UTC (permalink / raw)
  To: Daniel P. Berrangé, Cédric Le Goater, Peter Maydell,
	Steven Lee, Troy Lee, Kane Chen, Andrew Jeffery, Joel Stanley,
	Eric Blake, Markus Armbruster, Fabiano Rosas, Laurent Vivier,
	Paolo Bonzini, open list:All patches CC here,
	open list:ASPEED BMCs
  Cc: Jamin Lin, Troy Lee

Introduce the GCM cipher mode so authenticated encryption can be built
on top of the existing qcrypto_cipher API. GCM is an IV-based mode, so
register it in mode_need_iv. No backend advertises it yet, so it stays
unsupported until a backend and the AAD/tag helpers are added in the
following patches.

Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
---
 qapi/crypto.json | 4 +++-
 crypto/cipher.c  | 1 +
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/qapi/crypto.json b/qapi/crypto.json
index 2b55befef9..6e3a98ff68 100644
--- a/qapi/crypto.json
+++ b/qapi/crypto.json
@@ -121,10 +121,12 @@
 #
 # @ctr: Counter (Since 2.8)
 #
+# @gcm: Galois/Counter Mode (Since 11.2)
+#
 # Since: 2.6
 ##
 { 'enum': 'QCryptoCipherMode',
-  'data': ['ecb', 'cbc', 'xts', 'ctr']}
+  'data': ['ecb', 'cbc', 'xts', 'ctr', 'gcm']}
 
 ##
 # @QCryptoIVGenAlgo:
diff --git a/crypto/cipher.c b/crypto/cipher.c
index 515165e0dc..52d071a90c 100644
--- a/crypto/cipher.c
+++ b/crypto/cipher.c
@@ -66,6 +66,7 @@ static const bool mode_need_iv[QCRYPTO_CIPHER_MODE__MAX] = {
     [QCRYPTO_CIPHER_MODE_CBC] = true,
     [QCRYPTO_CIPHER_MODE_XTS] = true,
     [QCRYPTO_CIPHER_MODE_CTR] = true,
+    [QCRYPTO_CIPHER_MODE_GCM] = true,
 };
 
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v1 08/15] crypto/cipher: Add setaad/gettag for AEAD modes
  2026-07-14  7:29 [PATCH v1 00/15] Support the ASPEED HACE crypto command Jamin Lin
                   ` (6 preceding siblings ...)
  2026-07-14  7:29 ` [PATCH v1 07/15] crypto/cipher: Add GCM to QCryptoCipherMode Jamin Lin
@ 2026-07-14  7:29 ` Jamin Lin
  2026-07-14  8:37   ` Daniel P. Berrangé
  2026-07-14  7:29 ` [PATCH v1 09/15] crypto/cipher-gcrypt: Implement AES-GCM Jamin Lin
                   ` (7 subsequent siblings)
  15 siblings, 1 reply; 23+ messages in thread
From: Jamin Lin @ 2026-07-14  7:29 UTC (permalink / raw)
  To: Daniel P. Berrangé, Cédric Le Goater, Peter Maydell,
	Steven Lee, Troy Lee, Kane Chen, Andrew Jeffery, Joel Stanley,
	Eric Blake, Markus Armbruster, Fabiano Rosas, Laurent Vivier,
	Paolo Bonzini, open list:All patches CC here,
	open list:ASPEED BMCs
  Cc: Jamin Lin, Troy Lee

AEAD modes such as GCM authenticate optional associated data (AAD) and
produce an authentication tag, which the block-cipher encrypt/decrypt
interface cannot express. Add qcrypto_cipher_setaad() and
qcrypto_cipher_gettag() plus the matching backend driver hooks. The
generic front-end reports an error when the selected mode's driver does
not implement them, so calling them on a non-AEAD mode fails cleanly.

Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
---
 crypto/cipherpriv.h     |  8 ++++++++
 include/crypto/cipher.h | 36 ++++++++++++++++++++++++++++++++++++
 crypto/cipher.c         | 31 +++++++++++++++++++++++++++++++
 3 files changed, 75 insertions(+)

diff --git a/crypto/cipherpriv.h b/crypto/cipherpriv.h
index 64737ce961..c4f995f369 100644
--- a/crypto/cipherpriv.h
+++ b/crypto/cipherpriv.h
@@ -34,6 +34,14 @@ struct QCryptoCipherDriver {
                         const uint8_t *iv, size_t niv,
                         Error **errp);
 
+    int (*cipher_setaad)(QCryptoCipher *cipher,
+                         const uint8_t *aad, size_t len,
+                         Error **errp);
+
+    int (*cipher_gettag)(QCryptoCipher *cipher,
+                         uint8_t *tag, size_t len,
+                         Error **errp);
+
     void (*cipher_free)(QCryptoCipher *cipher);
 };
 
diff --git a/include/crypto/cipher.h b/include/crypto/cipher.h
index 92939310ef..2e361411b9 100644
--- a/include/crypto/cipher.h
+++ b/include/crypto/cipher.h
@@ -235,4 +235,40 @@ int qcrypto_cipher_setiv(QCryptoCipher *cipher,
                          const uint8_t *iv, size_t niv,
                          Error **errp);
 
+/**
+ * qcrypto_cipher_setaad:
+ * @cipher: the cipher object
+ * @aad: the associated data to authenticate
+ * @len: the length of @aad
+ * @errp: pointer to a NULL-initialized error object
+ *
+ * For AEAD modes such as GCM, feed the associated data (AAD) that is
+ * authenticated but not encrypted. It must be called after
+ * qcrypto_cipher_setiv() and before the first encrypt/decrypt call. It is
+ * an error to call this on a mode that is not an AEAD mode.
+ *
+ * Returns: 0 on success, -1 on error
+ */
+int qcrypto_cipher_setaad(QCryptoCipher *cipher,
+                          const uint8_t *aad, size_t len,
+                          Error **errp);
+
+/**
+ * qcrypto_cipher_gettag:
+ * @cipher: the cipher object
+ * @tag: buffer to receive the authentication tag
+ * @len: the length of @tag
+ * @errp: pointer to a NULL-initialized error object
+ *
+ * For AEAD modes such as GCM, read back the authentication tag computed
+ * over the associated data and the message. It must be called after the
+ * encrypt/decrypt operation. It is an error to call this on a mode that is
+ * not an AEAD mode.
+ *
+ * Returns: 0 on success, -1 on error
+ */
+int qcrypto_cipher_gettag(QCryptoCipher *cipher,
+                          uint8_t *tag, size_t len,
+                          Error **errp);
+
 #endif /* QCRYPTO_CIPHER_H */
diff --git a/crypto/cipher.c b/crypto/cipher.c
index 52d071a90c..67014aad6f 100644
--- a/crypto/cipher.c
+++ b/crypto/cipher.c
@@ -205,6 +205,37 @@ int qcrypto_cipher_setiv(QCryptoCipher *cipher,
 }
 
 
+int qcrypto_cipher_setaad(QCryptoCipher *cipher,
+                          const uint8_t *aad, size_t len,
+                          Error **errp)
+{
+    const QCryptoCipherDriver *drv = cipher->driver;
+
+    if (!drv->cipher_setaad) {
+        error_setg(errp, "The cipher mode does not support associated data");
+        return -1;
+    }
+
+    return drv->cipher_setaad(cipher, aad, len, errp);
+}
+
+
+int qcrypto_cipher_gettag(QCryptoCipher *cipher,
+                          uint8_t *tag, size_t len,
+                          Error **errp)
+{
+    const QCryptoCipherDriver *drv = cipher->driver;
+
+    if (!drv->cipher_gettag) {
+        error_setg(errp,
+                   "The cipher mode does not produce an authentication tag");
+        return -1;
+    }
+
+    return drv->cipher_gettag(cipher, tag, len, errp);
+}
+
+
 void qcrypto_cipher_free(QCryptoCipher *cipher)
 {
     if (cipher) {
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v1 09/15] crypto/cipher-gcrypt: Implement AES-GCM
  2026-07-14  7:29 [PATCH v1 00/15] Support the ASPEED HACE crypto command Jamin Lin
                   ` (7 preceding siblings ...)
  2026-07-14  7:29 ` [PATCH v1 08/15] crypto/cipher: Add setaad/gettag for AEAD modes Jamin Lin
@ 2026-07-14  7:29 ` Jamin Lin
  2026-07-14  8:39   ` Daniel P. Berrangé
  2026-07-14  7:29 ` [PATCH v1 10/15] tests/unit/test-crypto-cipher: Test AES-GCM mode Jamin Lin
                   ` (6 subsequent siblings)
  15 siblings, 1 reply; 23+ messages in thread
From: Jamin Lin @ 2026-07-14  7:29 UTC (permalink / raw)
  To: Daniel P. Berrangé, Cédric Le Goater, Peter Maydell,
	Steven Lee, Troy Lee, Kane Chen, Andrew Jeffery, Joel Stanley,
	Eric Blake, Markus Armbruster, Fabiano Rosas, Laurent Vivier,
	Paolo Bonzini, open list:All patches CC here,
	open list:ASPEED BMCs
  Cc: Jamin Lin, Troy Lee

Map QCRYPTO_CIPHER_MODE_GCM to GCRY_CIPHER_MODE_GCM and advertise it in
qcrypto_cipher_supports() for 128-bit block ciphers. Add a GCM driver
whose setiv accepts the (typically 96-bit) nonce, whose encrypt/decrypt
do not require block-aligned lengths, and which implements setaad via
gcry_cipher_authenticate() and gettag via gcry_cipher_gettag().

Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
---
 crypto/cipher-gcrypt.c.inc | 101 +++++++++++++++++++++++++++++++++++++
 1 file changed, 101 insertions(+)

diff --git a/crypto/cipher-gcrypt.c.inc b/crypto/cipher-gcrypt.c.inc
index 12eb9ddb5a..fce09a3c77 100644
--- a/crypto/cipher-gcrypt.c.inc
+++ b/crypto/cipher-gcrypt.c.inc
@@ -65,6 +65,8 @@ static int qcrypto_cipher_mode_to_gcry_mode(QCryptoCipherMode mode)
         return GCRY_CIPHER_MODE_CBC;
     case QCRYPTO_CIPHER_MODE_CTR:
         return GCRY_CIPHER_MODE_CTR;
+    case QCRYPTO_CIPHER_MODE_GCM:
+        return GCRY_CIPHER_MODE_GCM;
     default:
         return GCRY_CIPHER_MODE_NONE;
     }
@@ -104,6 +106,10 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgo alg,
     case QCRYPTO_CIPHER_MODE_XTS:
     case QCRYPTO_CIPHER_MODE_CTR:
         return true;
+    case QCRYPTO_CIPHER_MODE_GCM:
+        /* GCM requires a 128-bit block cipher. */
+        return gcry_cipher_get_algo_blklen(
+                   qcrypto_cipher_alg_to_gcry_alg(alg)) == 16;
     default:
         return false;
     }
@@ -228,6 +234,99 @@ static const struct QCryptoCipherDriver qcrypto_gcrypt_ctr_driver = {
     .cipher_free = qcrypto_gcrypt_ctx_free,
 };
 
+/*
+ * GCM is an AEAD stream mode: the IV/nonce need not match the block size,
+ * the message length need not be a multiple of the block size, associated
+ * data is fed with gcry_cipher_authenticate() and the authentication tag is
+ * read back with gcry_cipher_gettag().
+ */
+static int qcrypto_gcrypt_gcm_setiv(QCryptoCipher *cipher,
+                                    const uint8_t *iv, size_t niv,
+                                    Error **errp)
+{
+    QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
+    gcry_error_t err;
+
+    gcry_cipher_reset(ctx->handle);
+    err = gcry_cipher_setiv(ctx->handle, iv, niv);
+    if (err != 0) {
+        error_setg(errp, "Cannot set IV: %s", gcry_strerror(err));
+        return -1;
+    }
+
+    return 0;
+}
+
+static int qcrypto_gcrypt_gcm_setaad(QCryptoCipher *cipher,
+                                     const uint8_t *aad, size_t len,
+                                     Error **errp)
+{
+    QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
+    gcry_error_t err;
+
+    err = gcry_cipher_authenticate(ctx->handle, aad, len);
+    if (err != 0) {
+        error_setg(errp, "Cannot set AAD: %s", gcry_strerror(err));
+        return -1;
+    }
+
+    return 0;
+}
+
+static int qcrypto_gcrypt_gcm_encrypt(QCryptoCipher *cipher, const void *in,
+                                      void *out, size_t len, Error **errp)
+{
+    QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
+    gcry_error_t err;
+
+    err = gcry_cipher_encrypt(ctx->handle, out, len, in, len);
+    if (err != 0) {
+        error_setg(errp, "Cannot encrypt data: %s", gcry_strerror(err));
+        return -1;
+    }
+
+    return 0;
+}
+
+static int qcrypto_gcrypt_gcm_decrypt(QCryptoCipher *cipher, const void *in,
+                                      void *out, size_t len, Error **errp)
+{
+    QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
+    gcry_error_t err;
+
+    err = gcry_cipher_decrypt(ctx->handle, out, len, in, len);
+    if (err != 0) {
+        error_setg(errp, "Cannot decrypt data: %s", gcry_strerror(err));
+        return -1;
+    }
+
+    return 0;
+}
+
+static int qcrypto_gcrypt_gcm_gettag(QCryptoCipher *cipher,
+                                     uint8_t *tag, size_t len, Error **errp)
+{
+    QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
+    gcry_error_t err;
+
+    err = gcry_cipher_gettag(ctx->handle, tag, len);
+    if (err != 0) {
+        error_setg(errp, "Cannot get tag: %s", gcry_strerror(err));
+        return -1;
+    }
+
+    return 0;
+}
+
+static const struct QCryptoCipherDriver qcrypto_gcrypt_gcm_driver = {
+    .cipher_encrypt = qcrypto_gcrypt_gcm_encrypt,
+    .cipher_decrypt = qcrypto_gcrypt_gcm_decrypt,
+    .cipher_setiv = qcrypto_gcrypt_gcm_setiv,
+    .cipher_setaad = qcrypto_gcrypt_gcm_setaad,
+    .cipher_gettag = qcrypto_gcrypt_gcm_gettag,
+    .cipher_free = qcrypto_gcrypt_ctx_free,
+};
+
 static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgo alg,
                                              QCryptoCipherMode mode,
                                              const uint8_t *key,
@@ -259,6 +358,8 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgo alg,
 
     if (mode == QCRYPTO_CIPHER_MODE_CTR) {
         drv = &qcrypto_gcrypt_ctr_driver;
+    } else if (mode == QCRYPTO_CIPHER_MODE_GCM) {
+        drv = &qcrypto_gcrypt_gcm_driver;
     } else {
         drv = &qcrypto_gcrypt_driver;
     }
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v1 10/15] tests/unit/test-crypto-cipher: Test AES-GCM mode
  2026-07-14  7:29 [PATCH v1 00/15] Support the ASPEED HACE crypto command Jamin Lin
                   ` (8 preceding siblings ...)
  2026-07-14  7:29 ` [PATCH v1 09/15] crypto/cipher-gcrypt: Implement AES-GCM Jamin Lin
@ 2026-07-14  7:29 ` Jamin Lin
  2026-07-14  7:29 ` [PATCH v1 11/15] hw/misc/aspeed_hace: Support 64-bit DMA for the crypto command Jamin Lin
                   ` (5 subsequent siblings)
  15 siblings, 0 replies; 23+ messages in thread
From: Jamin Lin @ 2026-07-14  7:29 UTC (permalink / raw)
  To: Daniel P. Berrangé, Cédric Le Goater, Peter Maydell,
	Steven Lee, Troy Lee, Kane Chen, Andrew Jeffery, Joel Stanley,
	Eric Blake, Markus Armbruster, Fabiano Rosas, Laurent Vivier,
	Paolo Bonzini, open list:All patches CC here,
	open list:ASPEED BMCs
  Cc: Jamin Lin, Troy Lee

Exercise the new GCM mode and the setaad/gettag helpers with the
canonical AES-GCM test vectors from the GCM specification (McGrew &
Viega, also NIST SP 800-38D): AES-128 and AES-256, with and without
associated data. Each vector is run through encrypt (checking the
ciphertext and the generated tag) and decrypt (checking the recovered
plaintext and the recomputed tag).

Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
---
 tests/unit/test-crypto-cipher.c | 240 ++++++++++++++++++++++++++++++++
 1 file changed, 240 insertions(+)

diff --git a/tests/unit/test-crypto-cipher.c b/tests/unit/test-crypto-cipher.c
index 1331d558cf..670262b39e 100644
--- a/tests/unit/test-crypto-cipher.c
+++ b/tests/unit/test-crypto-cipher.c
@@ -810,6 +810,230 @@ static void test_cipher_short_plaintext(void)
     qcrypto_cipher_free(cipher);
 }
 
+typedef struct QCryptoCipherGcmTestData QCryptoCipherGcmTestData;
+struct QCryptoCipherGcmTestData {
+    const char *path;
+    QCryptoCipherAlgo alg;
+    const char *key;
+    const char *iv;
+    /* associated data, or NULL for none */
+    const char *aad;
+    const char *plaintext;
+    const char *ciphertext;
+    const char *tag;
+};
+
+/*
+ * AES-GCM test vectors from "The Galois/Counter Mode of Operation (GCM)"
+ * (McGrew & Viega, also NIST SP 800-38D), with a 96-bit IV and a 128-bit
+ * tag. Each entry's "Test case N" label is the numbered test case from that
+ * document (Appendix B / the GCM specification's test vectors).
+ */
+static QCryptoCipherGcmTestData gcm_test_data[] = {
+    {
+        /* Test case 2 */
+        .path = "/crypto/cipher/aes-128-gcm/2",
+        .alg = QCRYPTO_CIPHER_ALGO_AES_128,
+        .key = "00000000000000000000000000000000",
+        .iv = "000000000000000000000000",
+        .plaintext = "00000000000000000000000000000000",
+        .ciphertext = "0388dace60b6a392f328c2b971b2fe78",
+        .tag = "ab6e47d42cec13bdf53a67b21257bddf",
+    },
+    {
+        /* Test case 3 (no AAD) */
+        .path = "/crypto/cipher/aes-128-gcm/3",
+        .alg = QCRYPTO_CIPHER_ALGO_AES_128,
+        .key = "feffe9928665731c6d6a8f9467308308",
+        .iv = "cafebabefacedbaddecaf888",
+        .plaintext =
+            "d9313225f88406e5a55909c5aff5269a"
+            "86a7a9531534f7da2e4c303d8a318a72"
+            "1c3c0c95956809532fcf0e2449a6b525"
+            "b16aedf5aa0de657ba637b391aafd255",
+        .ciphertext =
+            "42831ec2217774244b7221b784d0d49c"
+            "e3aa212f2c02a4e035c17e2329aca12e"
+            "21d514b25466931c7d8f6a5aac84aa05"
+            "1ba30b396a0aac973d58e091473f5985",
+        .tag = "4d5c2af327cd64a62cf35abd2ba6fab4",
+    },
+    {
+        /* Test case 4 (with AAD) */
+        .path = "/crypto/cipher/aes-128-gcm/4",
+        .alg = QCRYPTO_CIPHER_ALGO_AES_128,
+        .key = "feffe9928665731c6d6a8f9467308308",
+        .iv = "cafebabefacedbaddecaf888",
+        .aad = "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+        .plaintext =
+            "d9313225f88406e5a55909c5aff5269a"
+            "86a7a9531534f7da2e4c303d8a318a72"
+            "1c3c0c95956809532fcf0e2449a6b525"
+            "b16aedf5aa0de657ba637b39",
+        .ciphertext =
+            "42831ec2217774244b7221b784d0d49c"
+            "e3aa212f2c02a4e035c17e2329aca12e"
+            "21d514b25466931c7d8f6a5aac84aa05"
+            "1ba30b396a0aac973d58e091",
+        .tag = "5bc94fbc3221a5db94fae95ae7121a47",
+    },
+    {
+        /* Test case 15 (AES-256, no AAD) */
+        .path = "/crypto/cipher/aes-256-gcm/15",
+        .alg = QCRYPTO_CIPHER_ALGO_AES_256,
+        .key =
+            "feffe9928665731c6d6a8f9467308308"
+            "feffe9928665731c6d6a8f9467308308",
+        .iv = "cafebabefacedbaddecaf888",
+        .plaintext =
+            "d9313225f88406e5a55909c5aff5269a"
+            "86a7a9531534f7da2e4c303d8a318a72"
+            "1c3c0c95956809532fcf0e2449a6b525"
+            "b16aedf5aa0de657ba637b391aafd255",
+        .ciphertext =
+            "522dc1f099567d07f47f37a32a84427d"
+            "643a8cdcbfe5c0c97598a2bd2555d1aa"
+            "8cb08e48590dbb3da7b08b1056828838"
+            "c5f61e6393ba7a0abcc9f662898015ad",
+        .tag = "b094dac5d93471bdec1a502270e3cc6c",
+    },
+    {
+        /* Test case 16 (AES-256, with AAD) */
+        .path = "/crypto/cipher/aes-256-gcm/16",
+        .alg = QCRYPTO_CIPHER_ALGO_AES_256,
+        .key =
+            "feffe9928665731c6d6a8f9467308308"
+            "feffe9928665731c6d6a8f9467308308",
+        .iv = "cafebabefacedbaddecaf888",
+        .aad = "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+        .plaintext =
+            "d9313225f88406e5a55909c5aff5269a"
+            "86a7a9531534f7da2e4c303d8a318a72"
+            "1c3c0c95956809532fcf0e2449a6b525"
+            "b16aedf5aa0de657ba637b39",
+        .ciphertext =
+            "522dc1f099567d07f47f37a32a84427d"
+            "643a8cdcbfe5c0c97598a2bd2555d1aa"
+            "8cb08e48590dbb3da7b08b1056828838"
+            "c5f61e6393ba7a0abcc9f662",
+        .tag = "76fc6ece0f4e1768cddf8853bb2d551b",
+    },
+};
+
+static void test_cipher_gcm(const void *opaque)
+{
+    const QCryptoCipherGcmTestData *data = opaque;
+    g_autofree uint8_t *key = NULL;
+    g_autofree uint8_t *iv = NULL;
+    g_autofree uint8_t *aad = NULL;
+    g_autofree uint8_t *ptext = NULL;
+    g_autofree uint8_t *ctext = NULL;
+    g_autofree uint8_t *tagexp = NULL;
+    g_autofree uint8_t *out = NULL;
+    uint8_t tag[16];
+    size_t nkey;
+    size_t niv;
+    size_t naad = 0;
+    size_t nptext;
+    size_t nctext;
+    size_t ntag;
+    QCryptoCipher *cipher;
+
+    nkey = unhex_string(data->key, &key);
+    niv = unhex_string(data->iv, &iv);
+    nptext = unhex_string(data->plaintext, &ptext);
+    nctext = unhex_string(data->ciphertext, &ctext);
+    ntag = unhex_string(data->tag, &tagexp);
+    if (data->aad) {
+        naad = unhex_string(data->aad, &aad);
+    }
+
+    g_assert_cmpint(nptext, ==, nctext);
+    g_assert_cmpint(ntag, ==, sizeof(tag));
+    out = g_new0(uint8_t, nptext);
+
+    /* Encrypt: plaintext -> ciphertext, then read back the tag. */
+    cipher = qcrypto_cipher_new(data->alg, QCRYPTO_CIPHER_MODE_GCM,
+                                key, nkey, &error_abort);
+    g_assert(cipher != NULL);
+    g_assert(qcrypto_cipher_setiv(cipher, iv, niv, &error_abort) == 0);
+    if (naad) {
+        g_assert(qcrypto_cipher_setaad(cipher, aad, naad, &error_abort) == 0);
+    }
+    g_assert(qcrypto_cipher_encrypt(cipher, ptext, out, nptext,
+                                    &error_abort) == 0);
+    g_assert_cmpmem(out, nptext, ctext, nctext);
+    g_assert(qcrypto_cipher_gettag(cipher, tag, sizeof(tag),
+                                   &error_abort) == 0);
+    g_assert_cmpmem(tag, sizeof(tag), tagexp, ntag);
+    qcrypto_cipher_free(cipher);
+
+    /* Decrypt: ciphertext -> plaintext, recomputed tag must match. */
+    memset(out, 0, nptext);
+    cipher = qcrypto_cipher_new(data->alg, QCRYPTO_CIPHER_MODE_GCM,
+                                key, nkey, &error_abort);
+    g_assert(cipher != NULL);
+    g_assert(qcrypto_cipher_setiv(cipher, iv, niv, &error_abort) == 0);
+    if (naad) {
+        g_assert(qcrypto_cipher_setaad(cipher, aad, naad, &error_abort) == 0);
+    }
+    g_assert(qcrypto_cipher_decrypt(cipher, ctext, out, nctext,
+                                    &error_abort) == 0);
+    g_assert_cmpmem(out, nctext, ptext, nptext);
+    g_assert(qcrypto_cipher_gettag(cipher, tag, sizeof(tag),
+                                   &error_abort) == 0);
+    g_assert_cmpmem(tag, sizeof(tag), tagexp, ntag);
+    qcrypto_cipher_free(cipher);
+}
+
+/*
+ * Corrupt one ciphertext byte and confirm the recomputed GCM tag no longer
+ * matches: the authentication tag must detect tampering.
+ */
+static void test_cipher_gcm_tamper(const void *opaque)
+{
+    const QCryptoCipherGcmTestData *data = opaque;
+    g_autofree uint8_t *key = NULL;
+    g_autofree uint8_t *iv = NULL;
+    g_autofree uint8_t *aad = NULL;
+    g_autofree uint8_t *ctext = NULL;
+    g_autofree uint8_t *tagexp = NULL;
+    g_autofree uint8_t *out = NULL;
+    uint8_t tag[16];
+    size_t nkey;
+    size_t niv;
+    size_t naad = 0;
+    size_t nctext;
+    size_t ntag;
+    QCryptoCipher *cipher;
+
+    nkey = unhex_string(data->key, &key);
+    niv = unhex_string(data->iv, &iv);
+    nctext = unhex_string(data->ciphertext, &ctext);
+    ntag = unhex_string(data->tag, &tagexp);
+    if (data->aad) {
+        naad = unhex_string(data->aad, &aad);
+    }
+    out = g_new0(uint8_t, nctext);
+
+    /* Flip one ciphertext bit before decrypting. */
+    ctext[0] ^= 0x01;
+
+    cipher = qcrypto_cipher_new(data->alg, QCRYPTO_CIPHER_MODE_GCM,
+                                key, nkey, &error_abort);
+    g_assert(cipher != NULL);
+    g_assert(qcrypto_cipher_setiv(cipher, iv, niv, &error_abort) == 0);
+    if (naad) {
+        g_assert(qcrypto_cipher_setaad(cipher, aad, naad, &error_abort) == 0);
+    }
+    g_assert(qcrypto_cipher_decrypt(cipher, ctext, out, nctext,
+                                    &error_abort) == 0);
+    g_assert(qcrypto_cipher_gettag(cipher, tag, sizeof(tag),
+                                   &error_abort) == 0);
+    g_assert(memcmp(tag, tagexp, ntag) != 0);
+    qcrypto_cipher_free(cipher);
+}
+
 int main(int argc, char **argv)
 {
     size_t i;
@@ -828,6 +1052,22 @@ int main(int argc, char **argv)
         }
     }
 
+    for (i = 0; i < G_N_ELEMENTS(gcm_test_data); i++) {
+        if (qcrypto_cipher_supports(gcm_test_data[i].alg,
+                                    QCRYPTO_CIPHER_MODE_GCM)) {
+            g_autofree char *tamper = g_strdup_printf("%s/tamper",
+                                                      gcm_test_data[i].path);
+
+            g_test_add_data_func(gcm_test_data[i].path, &gcm_test_data[i],
+                                 test_cipher_gcm);
+            g_test_add_data_func(tamper, &gcm_test_data[i],
+                                 test_cipher_gcm_tamper);
+        } else {
+            g_printerr("# skip unsupported %s:gcm\n",
+                       QCryptoCipherAlgo_str(gcm_test_data[i].alg));
+        }
+    }
+
     if (qcrypto_cipher_supports(QCRYPTO_CIPHER_ALGO_AES_256,
                                 QCRYPTO_CIPHER_MODE_CBC)) {
         g_test_add_func("/crypto/cipher/null-iv",
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v1 11/15] hw/misc/aspeed_hace: Support 64-bit DMA for the crypto command
  2026-07-14  7:29 [PATCH v1 00/15] Support the ASPEED HACE crypto command Jamin Lin
                   ` (9 preceding siblings ...)
  2026-07-14  7:29 ` [PATCH v1 10/15] tests/unit/test-crypto-cipher: Test AES-GCM mode Jamin Lin
@ 2026-07-14  7:29 ` Jamin Lin
  2026-07-14  7:29 ` [PATCH v1 12/15] hw/misc/aspeed_hace: Support the AES-GCM mode " Jamin Lin
                   ` (4 subsequent siblings)
  15 siblings, 0 replies; 23+ messages in thread
From: Jamin Lin @ 2026-07-14  7:29 UTC (permalink / raw)
  To: Daniel P. Berrangé, Cédric Le Goater, Peter Maydell,
	Steven Lee, Troy Lee, Kane Chen, Andrew Jeffery, Joel Stanley,
	Eric Blake, Markus Armbruster, Fabiano Rosas, Laurent Vivier,
	Paolo Bonzini, open list:All patches CC here,
	open list:ASPEED BMCs
  Cc: Jamin Lin, Troy Lee

The AST2700 crypto engine addresses DRAM with 64 bits, supplying the high
half of the source, destination and context addresses through HACE80,
HACE84 and HACE88. Add those registers and a crypt_get_addr() helper that
combines the low and high halves when the SoC has 64-bit DMA, mirroring
the hash engine. SoCs without 64-bit DMA (AST2500/AST2600/AST1030) ignore
the high registers, so their behaviour is unchanged.

Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
---
 hw/misc/aspeed_hace.c | 33 ++++++++++++++++++++++++++++++---
 1 file changed, 30 insertions(+), 3 deletions(-)

diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c
index b7a7c0b7ff..60dedee6a3 100644
--- a/hw/misc/aspeed_hace.c
+++ b/hw/misc/aspeed_hace.c
@@ -58,6 +58,11 @@
 #define CRYPT_CTX_KEY_OFFSET        0x10
 #define CRYPT_CTX_SIZE              0x30
 
+/* AST2700 64-bit DMA high address registers for the crypto command */
+#define R_CRYPT_SRC_HI      (0x80 / 4)
+#define R_CRYPT_DEST_HI     (0x84 / 4)
+#define R_CRYPT_CONTEXT_HI  (0x88 / 4)
+
 #define R_STATUS        (0x1c / 4)
 #define HASH_IRQ        BIT(9)
 #define CRYPT_IRQ       BIT(12)
@@ -672,6 +677,19 @@ static void crypt_be_add(uint8_t *ctr, size_t len, uint64_t add)
     }
 }
 
+static uint64_t crypt_get_addr(AspeedHACEState *s, int reg, int reg_hi)
+{
+    AspeedHACEClass *ahc = ASPEED_HACE_GET_CLASS(s);
+    uint64_t addr;
+
+    addr = deposit64(0, 0, 32, s->regs[reg]);
+    if (ahc->has_dma64) {
+        addr = deposit64(addr, 32, 32, s->regs[reg_hi]);
+    }
+
+    return addr;
+}
+
 /*
  * Perform an AES/DES/3DES ECB/CBC operation. The source and destination are
  * either single contiguous buffers (direct access mode) or scatter-gather
@@ -712,7 +730,7 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
     }
 
     /* Fetch the IV and key from the context buffer in DRAM. */
-    ctx_addr = s->regs[R_CRYPT_CONTEXT];
+    ctx_addr = crypt_get_addr(s, R_CRYPT_CONTEXT, R_CRYPT_CONTEXT_HI);
     if (address_space_read(&s->dram_as, ctx_addr, MEMTXATTRS_UNSPECIFIED,
                            ctx, sizeof(ctx))) {
         qemu_log_mask(LOG_GUEST_ERROR,
@@ -753,7 +771,7 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
     dst_buf = g_malloc0(buf_len);
 
     /* Gather the source into the bounce buffer, per the selected mode. */
-    src_addr = s->regs[R_CRYPT_SRC];
+    src_addr = crypt_get_addr(s, R_CRYPT_SRC, R_CRYPT_SRC_HI);
     if (sg_mode) {
         status = crypt_prepare_sg(s, src_addr, src_buf, len, false);
     } else {
@@ -789,7 +807,7 @@ 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];
+    dst_addr = crypt_get_addr(s, R_CRYPT_DEST, R_CRYPT_DEST_HI);
     if (sg_mode) {
         status = crypt_prepare_sg(s, dst_addr, dst_buf, len, true);
     } else {
@@ -953,6 +971,15 @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data,
     case R_HASH_KEY_BUFF_HI:
         data &= ahc->key_hi_mask;
         break;
+    case R_CRYPT_SRC_HI:
+        data &= ahc->src_hi_mask;
+        break;
+    case R_CRYPT_DEST_HI:
+        data &= ahc->dest_hi_mask;
+        break;
+    case R_CRYPT_CONTEXT_HI:
+        data &= ahc->key_hi_mask;
+        break;
     default:
         break;
     }
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v1 12/15] hw/misc/aspeed_hace: Support the AES-GCM mode for the crypto command
  2026-07-14  7:29 [PATCH v1 00/15] Support the ASPEED HACE crypto command Jamin Lin
                   ` (10 preceding siblings ...)
  2026-07-14  7:29 ` [PATCH v1 11/15] hw/misc/aspeed_hace: Support 64-bit DMA for the crypto command Jamin Lin
@ 2026-07-14  7:29 ` Jamin Lin
  2026-07-14  7:29 ` [PATCH v1 13/15] hw/misc/aspeed_hace: Enable the crypto command on the AST2700 Jamin Lin
                   ` (3 subsequent siblings)
  15 siblings, 0 replies; 23+ messages in thread
From: Jamin Lin @ 2026-07-14  7:29 UTC (permalink / raw)
  To: Daniel P. Berrangé, Cédric Le Goater, Peter Maydell,
	Steven Lee, Troy Lee, Kane Chen, Andrew Jeffery, Joel Stanley,
	Eric Blake, Markus Armbruster, Fabiano Rosas, Laurent Vivier,
	Paolo Bonzini, open list:All patches CC here,
	open list:ASPEED BMCs
  Cc: Jamin Lin, Troy Lee

Implement the AES-GCM mode (HACE10[6:4] = 0b101) used by the AST2700
crypto engine: decode the GCM selection, read the 96-bit IV from the
context buffer, operate on the exact data length (GCM handles a partial
final block itself), and write the 128-bit authentication tag to the tag
buffer (HACE18/HACE8C). The hardware GCM path is only used without
associated data (the driver falls back to software otherwise), so AAD is
not modelled and a non-zero HACE14 is reported as unimplemented.

Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
---
 hw/misc/aspeed_hace.c | 70 ++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 62 insertions(+), 8 deletions(-)

diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c
index 60dedee6a3..b84f83c0d3 100644
--- a/hw/misc/aspeed_hace.c
+++ b/hw/misc/aspeed_hace.c
@@ -31,6 +31,9 @@
 /* HACE0C[27:0] holds the crypto data length */
 #define  CRYPT_DATA_LEN_MASK    0x0FFFFFFF
 #define R_CRYPT_CMD         (0x10 / 4)
+/* AES-GCM associated data length (HACE14) and tag write buffer (HACE18) */
+#define R_CRYPT_GCM_ADD_LEN (0x14 / 4)
+#define R_CRYPT_GCM_TAG     (0x18 / 4)
 /* Crypto engine command register (HACE10) bits */
 #define  CRYPT_CMD_ENCRYPT          BIT(7)
 #define  CRYPT_CMD_ISR_EN           BIT(12)
@@ -43,6 +46,7 @@
 #define  CRYPT_CMD_ECB              (0x0 << 4)
 #define  CRYPT_CMD_CBC              (0x1 << 4)
 #define  CRYPT_CMD_CTR              (0x4 << 4)
+#define  CRYPT_CMD_GCM              (0x5 << 4)
 /* AES key length HACE10[3:2] */
 #define  CRYPT_CMD_AES_KEY_LEN_MASK (0x3 << 2)
 #define  CRYPT_CMD_AES256           (0x2 << 2)
@@ -58,10 +62,15 @@
 #define CRYPT_CTX_KEY_OFFSET        0x10
 #define CRYPT_CTX_SIZE              0x30
 
+/* AES-GCM uses a 96-bit IV and a 128-bit authentication tag */
+#define CRYPT_GCM_IV_LEN            12
+#define CRYPT_GCM_TAG_LEN          16
+
 /* AST2700 64-bit DMA high address registers for the crypto command */
 #define R_CRYPT_SRC_HI      (0x80 / 4)
 #define R_CRYPT_DEST_HI     (0x84 / 4)
 #define R_CRYPT_CONTEXT_HI  (0x88 / 4)
+#define R_CRYPT_GCM_TAG_HI  (0x8c / 4)
 
 #define R_STATUS        (0x1c / 4)
 #define HASH_IRQ        BIT(9)
@@ -598,6 +607,9 @@ static bool crypt_decode_cmd(uint32_t cmd, QCryptoCipherAlgo *alg,
     case CRYPT_CMD_CTR:
         *mode = QCRYPTO_CIPHER_MODE_CTR;
         break;
+    case CRYPT_CMD_GCM:
+        *mode = QCRYPTO_CIPHER_MODE_GCM;
+        break;
     default:
         return false;
     }
@@ -691,11 +703,12 @@ static uint64_t crypt_get_addr(AspeedHACEState *s, int reg, int reg_hi)
 }
 
 /*
- * 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.
+ * Perform an AES/DES/3DES ECB/CBC/CTR or AES-GCM 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 and CTR the resulting
+ * chaining state is written back to the context buffer so the driver can
+ * continue; for GCM the authentication tag is written to the tag buffer.
  */
 static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
 {
@@ -705,6 +718,7 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
     g_autoptr(QCryptoCipher) cipher = NULL;
     g_autofree uint8_t *src_buf = NULL;
     g_autofree uint8_t *dst_buf = NULL;
+    uint8_t tag[CRYPT_GCM_TAG_LEN];
     uint8_t ctx[CRYPT_CTX_SIZE];
     Error *local_err = NULL;
     QCryptoCipherMode mode;
@@ -713,10 +727,13 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
     uint64_t ctx_addr;
     uint64_t src_addr;
     uint64_t dst_addr;
+    uint64_t tag_addr;
+    uint32_t aad_len;
     size_t iv_offset;
     size_t blocklen;
     size_t buf_len;
     size_t keylen;
+    size_t ivlen;
     bool status;
 
     if (len == 0) {
@@ -729,6 +746,20 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
         return;
     }
 
+    /* GCM uses a 96-bit IV; the block modes use a full-block IV. */
+    ivlen = (mode == QCRYPTO_CIPHER_MODE_GCM) ? CRYPT_GCM_IV_LEN : blocklen;
+
+    /*
+     * The hardware GCM path is only exercised without associated data (the
+     * driver falls back to software when there is any), so AAD is not modelled.
+     */
+    aad_len = s->regs[R_CRYPT_GCM_ADD_LEN] & CRYPT_DATA_LEN_MASK;
+    if (mode == QCRYPTO_CIPHER_MODE_GCM && aad_len != 0) {
+        qemu_log_mask(LOG_UNIMP,
+                      "%s: GCM associated data is not implemented\n", __func__);
+        return;
+    }
+
     /* Fetch the IV and key from the context buffer in DRAM. */
     ctx_addr = crypt_get_addr(s, R_CRYPT_CONTEXT, R_CRYPT_CONTEXT_HI);
     if (address_space_read(&s->dram_as, ctx_addr, MEMTXATTRS_UNSPECIFIED,
@@ -753,7 +784,7 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
     }
 
     if (mode != QCRYPTO_CIPHER_MODE_ECB &&
-        qcrypto_cipher_setiv(cipher, ctx + iv_offset, blocklen,
+        qcrypto_cipher_setiv(cipher, ctx + iv_offset, ivlen,
                              &local_err) < 0) {
         qemu_log_mask(LOG_GUEST_ERROR, "%s: qcrypto cipher setiv failed: %s\n",
                       __func__, error_get_pretty(local_err));
@@ -764,9 +795,11 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
     /*
      * Round the working buffers up to a whole block. Block modes are already
      * block-aligned; the stream-like CTR mode may leave a partial final block
-     * that the engine still processes a full block at a time.
+     * that the engine still processes a full block at a time. GCM handles a
+     * partial final block itself, so it operates on the exact length.
      */
-    buf_len = QEMU_ALIGN_UP(len, blocklen);
+    buf_len = (mode == QCRYPTO_CIPHER_MODE_GCM) ?
+              len : QEMU_ALIGN_UP(len, blocklen);
     src_buf = g_malloc0(buf_len);
     dst_buf = g_malloc0(buf_len);
 
@@ -850,6 +883,24 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
                           "%s: Failed to write IV, addr=0x%" HWADDR_PRIx "\n",
                           __func__, ctx_addr + iv_offset);
         }
+    } else if (mode == QCRYPTO_CIPHER_MODE_GCM) {
+        /*
+         * GCM authenticates the message and writes the resulting tag to the
+         * dedicated tag buffer (HACE18/HACE8C).
+         */
+        if (qcrypto_cipher_gettag(cipher, tag, sizeof(tag), &local_err) < 0) {
+            qemu_log_mask(LOG_GUEST_ERROR, "%s: qcrypto cipher gettag failed: "
+                          "%s\n", __func__, error_get_pretty(local_err));
+            error_free(local_err);
+            return;
+        }
+        tag_addr = crypt_get_addr(s, R_CRYPT_GCM_TAG, R_CRYPT_GCM_TAG_HI);
+        if (address_space_write(&s->dram_as, tag_addr, MEMTXATTRS_UNSPECIFIED,
+                                tag, sizeof(tag))) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "%s: Failed to write tag, addr=0x%" HWADDR_PRIx "\n",
+                          __func__, tag_addr);
+        }
     }
 }
 
@@ -980,6 +1031,9 @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data,
     case R_CRYPT_CONTEXT_HI:
         data &= ahc->key_hi_mask;
         break;
+    case R_CRYPT_GCM_TAG_HI:
+        data &= ahc->dest_hi_mask;
+        break;
     default:
         break;
     }
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v1 13/15] hw/misc/aspeed_hace: Enable the crypto command on the AST2700
  2026-07-14  7:29 [PATCH v1 00/15] Support the ASPEED HACE crypto command Jamin Lin
                   ` (11 preceding siblings ...)
  2026-07-14  7:29 ` [PATCH v1 12/15] hw/misc/aspeed_hace: Support the AES-GCM mode " Jamin Lin
@ 2026-07-14  7:29 ` Jamin Lin
  2026-07-14  7:29 ` [PATCH v1 14/15] tests/qtest/aspeed-hace: Test " Jamin Lin
                   ` (2 subsequent siblings)
  15 siblings, 0 replies; 23+ messages in thread
From: Jamin Lin @ 2026-07-14  7:29 UTC (permalink / raw)
  To: Daniel P. Berrangé, Cédric Le Goater, Peter Maydell,
	Steven Lee, Troy Lee, Kane Chen, Andrew Jeffery, Joel Stanley,
	Eric Blake, Markus Armbruster, Fabiano Rosas, Laurent Vivier,
	Paolo Bonzini, open list:All patches CC here,
	open list:ASPEED BMCs
  Cc: Jamin Lin, Troy Lee

With direct/scatter-gather access, 64-bit DMA and AES-GCM all in place,
the AST2700 crypto engine is now fully modelled. Drop its temporary
interrupt-only workaround so the crypto command runs for real, like the
other HACE variants.

Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
---
 include/hw/misc/aspeed_hace.h |  1 -
 hw/misc/aspeed_hace.c         | 15 +--------------
 2 files changed, 1 insertion(+), 15 deletions(-)

diff --git a/include/hw/misc/aspeed_hace.h b/include/hw/misc/aspeed_hace.h
index b5416b0cb5..9b0e7683fa 100644
--- a/include/hw/misc/aspeed_hace.h
+++ b/include/hw/misc/aspeed_hace.h
@@ -49,7 +49,6 @@ struct AspeedHACEClass {
     uint32_t key_mask;
     uint32_t hash_mask;
     uint64_t nr_regs;
-    bool raise_crypt_interrupt_workaround;
     uint32_t src_hi_mask;
     uint32_t dest_hi_mask;
     uint32_t key_hi_mask;
diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c
index b84f83c0d3..33b6861523 100644
--- a/hw/misc/aspeed_hace.c
+++ b/hw/misc/aspeed_hace.c
@@ -998,14 +998,7 @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data,
         break;
     }
     case R_CRYPT_CMD:
-        /*
-         * The AST2700 crypto engine needs 64-bit DMA and AES-GCM, which are
-         * added later; until then it keeps the temporary workaround of only
-         * raising the completion interrupt without running the command.
-         */
-        if (!ahc->raise_crypt_interrupt_workaround) {
-            do_crypt_operation(s, data);
-        }
+        do_crypt_operation(s, data);
 
         /* Hardware raises the crypt interrupt once the command finishes. */
         s->regs[R_STATUS] |= CRYPT_IRQ;
@@ -1209,12 +1202,6 @@ static void aspeed_ast2700_hace_class_init(ObjectClass *klass, const void *data)
     ahc->dest_hi_mask = 0x00000003;
     ahc->key_hi_mask = 0x00000003;
 
-    /*
-     * Currently, it does not support the CRYPT command. Instead, it only
-     * sends an interrupt to notify the firmware that the crypt command
-     * has completed. It is a temporary workaround.
-     */
-    ahc->raise_crypt_interrupt_workaround = true;
     ahc->has_dma64 = true;
 }
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v1 14/15] tests/qtest/aspeed-hace: Test the crypto command on the AST2700
  2026-07-14  7:29 [PATCH v1 00/15] Support the ASPEED HACE crypto command Jamin Lin
                   ` (12 preceding siblings ...)
  2026-07-14  7:29 ` [PATCH v1 13/15] hw/misc/aspeed_hace: Enable the crypto command on the AST2700 Jamin Lin
@ 2026-07-14  7:29 ` Jamin Lin
  2026-07-14  7:29 ` [PATCH v1 15/15] tests/functional/aarch64/test_aspeed_ast2700: Drop the AST2700 crypto self-test workaround Jamin Lin
  2026-07-14  8:47 ` [PATCH v1 00/15] Support the ASPEED HACE crypto command Daniel P. Berrangé
  15 siblings, 0 replies; 23+ messages in thread
From: Jamin Lin @ 2026-07-14  7:29 UTC (permalink / raw)
  To: Daniel P. Berrangé, Cédric Le Goater, Peter Maydell,
	Steven Lee, Troy Lee, Kane Chen, Andrew Jeffery, Joel Stanley,
	Eric Blake, Markus Armbruster, Fabiano Rosas, Laurent Vivier,
	Paolo Bonzini, open list:All patches CC here,
	open list:ASPEED BMCs
  Cc: Jamin Lin, Troy Lee

Cover the AST2700 crypto engine, which drives 64-bit scatter-gather DMA
and adds AES-GCM on top of the ECB/CBC/CTR modes shared with the AST2600.
Add AES-128 and AES-256 GCM known-answer vectors (GCM specification /
NIST SP 800-38D, no associated data) and a dedicated GCM runner that
programs the tag buffer and reads the tag back, checking it after both
encryption and decryption. Register the AST2700 with all four modes.

Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
---
 tests/qtest/aspeed-hace-utils.h |   1 +
 tests/qtest/aspeed-hace-utils.c | 159 +++++++++++++++++++++++++++++++-
 tests/qtest/ast2700-hace-test.c |   9 ++
 3 files changed, 168 insertions(+), 1 deletion(-)

diff --git a/tests/qtest/aspeed-hace-utils.h b/tests/qtest/aspeed-hace-utils.h
index 82b0b3f93d..a5601a3d65 100644
--- a/tests/qtest/aspeed-hace-utils.h
+++ b/tests/qtest/aspeed-hace-utils.h
@@ -86,6 +86,7 @@ enum {
     CRYPT_MODE_ECB = 1 << 0,
     CRYPT_MODE_CBC = 1 << 1,
     CRYPT_MODE_CTR = 1 << 2,
+    CRYPT_MODE_GCM = 1 << 3,
 };
 
 /*
diff --git a/tests/qtest/aspeed-hace-utils.c b/tests/qtest/aspeed-hace-utils.c
index 0b91a4e61a..b421221c79 100644
--- a/tests/qtest/aspeed-hace-utils.c
+++ b/tests/qtest/aspeed-hace-utils.c
@@ -653,6 +653,8 @@ void aspeed_test_addresses(const char *machine, const uint32_t base,
 #define HACE_CRYPTO_CONTEXT      0x08
 #define HACE_CRYPTO_DATA_LEN     0x0c
 #define HACE_CRYPTO_CMD          0x10
+#define HACE_CRYPTO_GCM_ADD_LEN  0x14
+#define HACE_CRYPTO_GCM_TAG      0x18
 
 /* Crypto command bits */
 #define HACE_CMD_ENCRYPT         BIT(7)
@@ -665,7 +667,9 @@ void aspeed_test_addresses(const char *machine, const uint32_t base,
 #define HACE_CMD_ECB             (0x0 << 4)
 #define HACE_CMD_CBC             (0x1 << 4)
 #define HACE_CMD_CTR             (0x4 << 4)
+#define HACE_CMD_GCM             (0x5 << 4)
 #define HACE_CMD_AES128          (0x0 << 2)
+#define HACE_CMD_AES256          (0x2 << 2)
 
 /* Context buffer layout: IV (DES at +8), key at +0x10 */
 #define HACE_CTX_KEY_OFFSET      0x10
@@ -793,11 +797,66 @@ static const uint8_t tdes_ctr_ctext[8] = {
 static const uint8_t tdes_ctr_ivout[8] = {
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
 
+/*
+ * aes_gcm_tv_template[2] (AES-128) and [9] (AES-256), from the McGrew & Viega
+ * GCM spec (also NIST SP 800-38D), no AAD. Both cases share this plaintext/IV.
+ */
+static const uint8_t aes_gcm_ptext[64] = {
+    0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5,
+    0xa5, 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a,
+    0x86, 0xa7, 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda,
+    0x2e, 0x4c, 0x30, 0x3d, 0x8a, 0x31, 0x8a, 0x72,
+    0x1c, 0x3c, 0x0c, 0x95, 0x95, 0x68, 0x09, 0x53,
+    0x2f, 0xcf, 0x0e, 0x24, 0x49, 0xa6, 0xb5, 0x25,
+    0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d, 0xe6, 0x57,
+    0xba, 0x63, 0x7b, 0x39, 0x1a, 0xaf, 0xd2, 0x55 };
+static const uint8_t aes_gcm_iv[12] = {
+    0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad,
+    0xde, 0xca, 0xf8, 0x88 };
+
+/* aes_gcm_tv_template[2] (AES-128) */
+static const uint8_t aes128_gcm_key[16] = {
+    0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c,
+    0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08 };
+static const uint8_t aes128_gcm_ctext[64] = {
+    0x42, 0x83, 0x1e, 0xc2, 0x21, 0x77, 0x74, 0x24,
+    0x4b, 0x72, 0x21, 0xb7, 0x84, 0xd0, 0xd4, 0x9c,
+    0xe3, 0xaa, 0x21, 0x2f, 0x2c, 0x02, 0xa4, 0xe0,
+    0x35, 0xc1, 0x7e, 0x23, 0x29, 0xac, 0xa1, 0x2e,
+    0x21, 0xd5, 0x14, 0xb2, 0x54, 0x66, 0x93, 0x1c,
+    0x7d, 0x8f, 0x6a, 0x5a, 0xac, 0x84, 0xaa, 0x05,
+    0x1b, 0xa3, 0x0b, 0x39, 0x6a, 0x0a, 0xac, 0x97,
+    0x3d, 0x58, 0xe0, 0x91, 0x47, 0x3f, 0x59, 0x85 };
+static const uint8_t aes128_gcm_tag[16] = {
+    0x4d, 0x5c, 0x2a, 0xf3, 0x27, 0xcd, 0x64, 0xa6,
+    0x2c, 0xf3, 0x5a, 0xbd, 0x2b, 0xa6, 0xfa, 0xb4 };
+
+/* aes_gcm_tv_template[9] (AES-256) */
+static const uint8_t aes256_gcm_key[32] = {
+    0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c,
+    0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08,
+    0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c,
+    0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08 };
+static const uint8_t aes256_gcm_ctext[64] = {
+    0x52, 0x2d, 0xc1, 0xf0, 0x99, 0x56, 0x7d, 0x07,
+    0xf4, 0x7f, 0x37, 0xa3, 0x2a, 0x84, 0x42, 0x7d,
+    0x64, 0x3a, 0x8c, 0xdc, 0xbf, 0xe5, 0xc0, 0xc9,
+    0x75, 0x98, 0xa2, 0xbd, 0x25, 0x55, 0xd1, 0xaa,
+    0x8c, 0xb0, 0x8e, 0x48, 0x59, 0x0d, 0xbb, 0x3d,
+    0xa7, 0xb0, 0x8b, 0x10, 0x56, 0x82, 0x88, 0x38,
+    0xc5, 0xf6, 0x1e, 0x63, 0x93, 0xba, 0x7a, 0x0a,
+    0xbc, 0xc9, 0xf6, 0x62, 0x89, 0x80, 0x15, 0xad };
+static const uint8_t aes256_gcm_tag[16] = {
+    0xb0, 0x94, 0xda, 0xc5, 0xd9, 0x34, 0x71, 0xbd,
+    0xec, 0x1a, 0x50, 0x22, 0x70, 0xe3, 0xcc, 0x6c };
+
 typedef struct CryptTest {
     const uint8_t *ptext;
     const uint8_t *ctext;
     /* expected context IV after encrypt, or NULL */
     const uint8_t *iv_out;
+    /* expected GCM authentication tag, or NULL for non-AEAD modes */
+    const uint8_t *tag;
     const uint8_t *key;
     const uint8_t *iv;
     const char *name;
@@ -805,6 +864,7 @@ typedef struct CryptTest {
     uint32_t cmd;
     size_t keylen;
     size_t ivlen;
+    size_t taglen;
     size_t len;
 } CryptTest;
 
@@ -906,6 +966,32 @@ static const CryptTest crypt_tests[] = {
         .iv_out = tdes_ctr_ivout,
         .len = sizeof(tdes_ctr_ptext),
     },
+    {
+        .name = "aes128-gcm",
+        .cmd = HACE_CMD_AES128 | HACE_CMD_GCM,
+        .key = aes128_gcm_key,
+        .keylen = sizeof(aes128_gcm_key),
+        .iv = aes_gcm_iv,
+        .ivlen = sizeof(aes_gcm_iv),
+        .ptext = aes_gcm_ptext,
+        .ctext = aes128_gcm_ctext,
+        .tag = aes128_gcm_tag,
+        .taglen = sizeof(aes128_gcm_tag),
+        .len = sizeof(aes_gcm_ptext),
+    },
+    {
+        .name = "aes256-gcm",
+        .cmd = HACE_CMD_AES256 | HACE_CMD_GCM,
+        .key = aes256_gcm_key,
+        .keylen = sizeof(aes256_gcm_key),
+        .iv = aes_gcm_iv,
+        .ivlen = sizeof(aes_gcm_iv),
+        .ptext = aes_gcm_ptext,
+        .ctext = aes256_gcm_ctext,
+        .tag = aes256_gcm_tag,
+        .taglen = sizeof(aes256_gcm_tag),
+        .len = sizeof(aes_gcm_ptext),
+    },
 };
 
 /* DRAM offsets for the crypto test source, destination and context buffers. */
@@ -923,6 +1009,8 @@ static const CryptTest crypt_tests[] = {
  */
 #define CRYPT_SG_FRAGS         3
 #define CRYPT_SG_FRAG_STRIDE   0x1000
+/* DRAM offset for the AES-GCM authentication tag write buffer. */
+#define CRYPT_OFF_TAG          0x60000
 
 /* Describes one registered crypto test (qtest_add_data_func() data pointer). */
 typedef struct AspeedCryptoTest {
@@ -943,6 +1031,8 @@ static uint32_t crypt_mode_flag(uint32_t cmd)
         return CRYPT_MODE_CBC;
     case HACE_CMD_CTR:
         return CRYPT_MODE_CTR;
+    case HACE_CMD_GCM:
+        return CRYPT_MODE_GCM;
     default:
         return 0;
     }
@@ -1088,6 +1178,47 @@ static void crypt_run_sg(QTestState *s, uint32_t base, uint64_t dram,
     crypt_gather_sg(s, dram, CRYPT_OFF_DST, out, t->len);
 }
 
+/*
+ * Run one AES-GCM operation in scatter-gather mode: like crypt_run_sg() but
+ * also program the tag write buffer (HACE18) with no associated data, and read
+ * the authentication tag back into @out_tag.
+ */
+static void crypt_run_gcm(QTestState *s, uint32_t base, uint64_t dram,
+                          const CryptTest *t, bool encrypt, uint8_t *out,
+                          uint8_t *out_tag)
+{
+    const uint8_t *in = encrypt ? t->ptext : t->ctext;
+    uint64_t src_sg = dram + CRYPT_OFF_SRC_SG;
+    uint64_t dst_sg = dram + CRYPT_OFF_DST_SG;
+    uint64_t ctx = dram + CRYPT_OFF_CTX;
+    uint32_t cmd = t->cmd | HACE_CMD_ISR_EN | HACE_CMD_SRC_SG_CTRL |
+                   HACE_CMD_DST_SG_CTRL;
+
+    if (encrypt) {
+        cmd |= HACE_CMD_ENCRYPT;
+    }
+
+    crypt_write_ctx(s, ctx, t);
+    crypt_make_sg(s, dram, CRYPT_OFF_SRC, src_sg, in, t->len);
+    crypt_make_sg(s, dram, CRYPT_OFF_DST, dst_sg, NULL, t->len);
+
+    qtest_writel(s, base + HACE_CRYPTO_SRC, (uint32_t)src_sg);
+    qtest_writel(s, base + HACE_CRYPTO_DEST, (uint32_t)dst_sg);
+    qtest_writel(s, base + HACE_CRYPTO_CONTEXT, (uint32_t)ctx);
+    qtest_writel(s, base + HACE_CRYPTO_DATA_LEN, t->len);
+    qtest_writel(s, base + HACE_CRYPTO_GCM_ADD_LEN, 0);
+    qtest_writel(s, base + HACE_CRYPTO_GCM_TAG,
+                 (uint32_t)(dram + CRYPT_OFF_TAG));
+    qtest_writel(s, base + HACE_CRYPTO_CMD, cmd);
+
+    g_assert_cmphex(qtest_readl(s, base + HACE_STS) & HACE_CRYPTO_ISR, ==,
+                    HACE_CRYPTO_ISR);
+    qtest_writel(s, base + HACE_STS, HACE_CRYPTO_ISR);
+
+    crypt_gather_sg(s, dram, CRYPT_OFF_DST, out, t->len);
+    qtest_memread(s, dram + CRYPT_OFF_TAG, out_tag, t->taglen);
+}
+
 static void aspeed_test_crypto(const void *data)
 {
     const AspeedCryptoTest *c = data;
@@ -1124,6 +1255,29 @@ static void aspeed_test_crypto(const void *data)
     qtest_quit(s);
 }
 
+static void aspeed_test_crypto_gcm(const void *data)
+{
+    const AspeedCryptoTest *c = data;
+    const CryptTest *t = &crypt_tests[c->index];
+    QTestState *s = qtest_init(c->machine);
+    uint8_t out[64];
+    uint8_t tag[16];
+
+    g_assert_cmpuint(t->len, <=, sizeof(out));
+
+    /* Encrypt: ptext -> ctext, then check the authentication tag. */
+    crypt_run_gcm(s, c->base, c->dram, t, true, out, tag);
+    g_assert_cmpmem(out, t->len, t->ctext, t->len);
+    g_assert_cmpmem(tag, t->taglen, t->tag, t->taglen);
+
+    /* Decrypt: ctext -> ptext, the recomputed tag must match. */
+    crypt_run_gcm(s, c->base, c->dram, t, false, out, tag);
+    g_assert_cmpmem(out, t->len, t->ptext, t->len);
+    g_assert_cmpmem(tag, t->taglen, t->tag, t->taglen);
+
+    qtest_quit(s);
+}
+
 void aspeed_add_crypto_tests(const char *prefix, const char *machine,
                              uint32_t base, uint64_t dram, uint32_t modes,
                              bool sg)
@@ -1131,6 +1285,7 @@ void aspeed_add_crypto_tests(const char *prefix, const char *machine,
     int i;
 
     for (i = 0; i < ARRAY_SIZE(crypt_tests); i++) {
+        bool is_gcm = crypt_mode_flag(crypt_tests[i].cmd) == CRYPT_MODE_GCM;
         g_autofree char *path = NULL;
         AspeedCryptoTest *t;
 
@@ -1146,7 +1301,9 @@ void aspeed_add_crypto_tests(const char *prefix, const char *machine,
         t->dram = dram;
         t->index = i;
         t->sg = sg;
-        qtest_add_data_func_full(path, t, aspeed_test_crypto, g_free);
+        qtest_add_data_func_full(path, t,
+                                 is_gcm ? aspeed_test_crypto_gcm :
+                                 aspeed_test_crypto, g_free);
     }
 }
 
diff --git a/tests/qtest/ast2700-hace-test.c b/tests/qtest/ast2700-hace-test.c
index 508a34dd6c..3f0217d635 100644
--- a/tests/qtest/ast2700-hace-test.c
+++ b/tests/qtest/ast2700-hace-test.c
@@ -94,5 +94,14 @@ int main(int argc, char **argv)
     qtest_add_func("ast2700/hace/sha384_accum", test_sha384_accum_ast2700);
     qtest_add_func("ast2700/hace/sha256_accum", test_sha256_accum_ast2700);
 
+    /*
+     * The AST2700 crypto engine uses scatter-gather with 64-bit DMA and adds
+     * AES-GCM on top of the ECB/CBC/CTR modes shared with the AST2600.
+     */
+    aspeed_add_crypto_tests("ast2700", "-machine ast2700-evb", 0x12070000,
+                            0x400000000,
+                            CRYPT_MODE_ECB | CRYPT_MODE_CBC | CRYPT_MODE_CTR |
+                            CRYPT_MODE_GCM, true);
+
     return g_test_run();
 }
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v1 15/15] tests/functional/aarch64/test_aspeed_ast2700: Drop the AST2700 crypto self-test workaround
  2026-07-14  7:29 [PATCH v1 00/15] Support the ASPEED HACE crypto command Jamin Lin
                   ` (13 preceding siblings ...)
  2026-07-14  7:29 ` [PATCH v1 14/15] tests/qtest/aspeed-hace: Test " Jamin Lin
@ 2026-07-14  7:29 ` Jamin Lin
  2026-07-14  8:47 ` [PATCH v1 00/15] Support the ASPEED HACE crypto command Daniel P. Berrangé
  15 siblings, 0 replies; 23+ messages in thread
From: Jamin Lin @ 2026-07-14  7:29 UTC (permalink / raw)
  To: Daniel P. Berrangé, Cédric Le Goater, Peter Maydell,
	Steven Lee, Troy Lee, Kane Chen, Andrew Jeffery, Joel Stanley,
	Eric Blake, Markus Armbruster, Fabiano Rosas, Laurent Vivier,
	Paolo Bonzini, open list:All patches CC here,
	open list:ASPEED BMCs
  Cc: Jamin Lin, Troy Lee

The AST2700 boot tests appended 'cryptomgr.notests=1' to the kernel
command line to skip the Linux crypto manager self-tests.

Now that the HACE crypto engine emulates the crypto command, the
self-tests pass, so drop the workaround and let them run. This also
exercises the HACE crypto emulation during the functional boot tests.

Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
---
 tests/functional/aarch64/test_aspeed_ast2700a1.py | 5 -----
 tests/functional/aarch64/test_aspeed_ast2700a2.py | 5 -----
 tests/functional/aarch64/test_aspeed_ast2700fc.py | 5 -----
 3 files changed, 15 deletions(-)

diff --git a/tests/functional/aarch64/test_aspeed_ast2700a1.py b/tests/functional/aarch64/test_aspeed_ast2700a1.py
index b0c08854da..bc535449b5 100755
--- a/tests/functional/aarch64/test_aspeed_ast2700a1.py
+++ b/tests/functional/aarch64/test_aspeed_ast2700a1.py
@@ -51,10 +51,6 @@ def verify_vbootrom_firmware_flow(self):
         wait_for_console_pattern(self, 'pass')
         wait_for_console_pattern(self, 'Jumping to BL31 (Trusted Firmware-A)')
 
-    def disable_kernel_crypto_selftest(self):
-         exec_command_and_wait_for_pattern(self,
-            'setenv bootargs "${bootargs} cryptomgr.notests=1"', '=>')
-
     def enable_ast2700_pcie2(self):
         exec_command_and_wait_for_pattern(self,
             'cp 100420000 403000000 900000', '=>')
@@ -71,7 +67,6 @@ def verify_openbmc_boot_start(self, enable_pcie=True):
         wait_for_console_pattern(self, 'U-Boot 2023.10')
         wait_for_console_pattern(self, 'Hit any key to stop autoboot')
         exec_command_and_wait_for_pattern(self, '\012', '=>')
-        self.disable_kernel_crypto_selftest()
         if enable_pcie:
             self.enable_ast2700_pcie2()
         else:
diff --git a/tests/functional/aarch64/test_aspeed_ast2700a2.py b/tests/functional/aarch64/test_aspeed_ast2700a2.py
index ed414999f4..5aea0a9dd0 100755
--- a/tests/functional/aarch64/test_aspeed_ast2700a2.py
+++ b/tests/functional/aarch64/test_aspeed_ast2700a2.py
@@ -51,10 +51,6 @@ def verify_vbootrom_firmware_flow(self):
         wait_for_console_pattern(self, 'pass')
         wait_for_console_pattern(self, 'Jumping to BL31 (Trusted Firmware-A)')
 
-    def disable_kernel_crypto_selftest(self):
-         exec_command_and_wait_for_pattern(self,
-            'setenv bootargs "${bootargs} cryptomgr.notests=1"', '=>')
-
     def enable_ast2700_pcie2(self):
         exec_command_and_wait_for_pattern(self,
             'cp 100420000 403000000 900000', '=>')
@@ -71,7 +67,6 @@ def verify_openbmc_boot_start(self, enable_pcie=True):
         wait_for_console_pattern(self, 'U-Boot 2023.10')
         wait_for_console_pattern(self, 'Hit any key to stop autoboot')
         exec_command_and_wait_for_pattern(self, '\012', '=>')
-        self.disable_kernel_crypto_selftest()
         if enable_pcie:
             self.enable_ast2700_pcie2()
         else:
diff --git a/tests/functional/aarch64/test_aspeed_ast2700fc.py b/tests/functional/aarch64/test_aspeed_ast2700fc.py
index df889134ed..1648fa9dba 100755
--- a/tests/functional/aarch64/test_aspeed_ast2700fc.py
+++ b/tests/functional/aarch64/test_aspeed_ast2700fc.py
@@ -27,10 +27,6 @@ def do_test_aarch64_aspeed_sdk_start(self, image):
 
         self.vm.launch()
 
-    def disable_kernel_crypto_selftest(self):
-         exec_command_and_wait_for_pattern(self,
-            'setenv bootargs "${bootargs} cryptomgr.notests=1"', '=>')
-
     def enable_ast2700_pcie2(self):
         exec_command_and_wait_for_pattern(self,
             'cp 100420000 403000000 900000', '=>')
@@ -47,7 +43,6 @@ def verify_openbmc_boot_and_login(self, name):
         wait_for_console_pattern(self, 'U-Boot 2023.10')
         wait_for_console_pattern(self, 'Hit any key to stop autoboot')
         exec_command_and_wait_for_pattern(self, '\012', '=>')
-        self.disable_kernel_crypto_selftest()
         self.enable_ast2700_pcie2()
         wait_for_console_pattern(self, 'Starting kernel ...')
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* Re: [PATCH v1 07/15] crypto/cipher: Add GCM to QCryptoCipherMode
  2026-07-14  7:29 ` [PATCH v1 07/15] crypto/cipher: Add GCM to QCryptoCipherMode Jamin Lin
@ 2026-07-14  8:37   ` Daniel P. Berrangé
  0 siblings, 0 replies; 23+ messages in thread
From: Daniel P. Berrangé @ 2026-07-14  8:37 UTC (permalink / raw)
  To: Jamin Lin
  Cc: Cédric Le Goater, Peter Maydell, Steven Lee, Troy Lee,
	Kane Chen, Andrew Jeffery, Joel Stanley, Eric Blake,
	Markus Armbruster, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
	open list:All patches CC here, open list:ASPEED BMCs, Troy Lee

On Tue, Jul 14, 2026 at 07:29:11AM +0000, Jamin Lin wrote:
> Introduce the GCM cipher mode so authenticated encryption can be built
> on top of the existing qcrypto_cipher API. GCM is an IV-based mode, so
> register it in mode_need_iv. No backend advertises it yet, so it stays
> unsupported until a backend and the AAD/tag helpers are added in the
> following patches.
> 
> Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
> ---
>  qapi/crypto.json | 4 +++-
>  crypto/cipher.c  | 1 +
>  2 files changed, 4 insertions(+), 1 deletion(-)

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Acked-by: Daniel P. Berrangé <berrange@redhat.com>


With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v1 08/15] crypto/cipher: Add setaad/gettag for AEAD modes
  2026-07-14  7:29 ` [PATCH v1 08/15] crypto/cipher: Add setaad/gettag for AEAD modes Jamin Lin
@ 2026-07-14  8:37   ` Daniel P. Berrangé
  0 siblings, 0 replies; 23+ messages in thread
From: Daniel P. Berrangé @ 2026-07-14  8:37 UTC (permalink / raw)
  To: Jamin Lin
  Cc: Cédric Le Goater, Peter Maydell, Steven Lee, Troy Lee,
	Kane Chen, Andrew Jeffery, Joel Stanley, Eric Blake,
	Markus Armbruster, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
	open list:All patches CC here, open list:ASPEED BMCs, Troy Lee

On Tue, Jul 14, 2026 at 07:29:13AM +0000, Jamin Lin wrote:
> AEAD modes such as GCM authenticate optional associated data (AAD) and
> produce an authentication tag, which the block-cipher encrypt/decrypt
> interface cannot express. Add qcrypto_cipher_setaad() and
> qcrypto_cipher_gettag() plus the matching backend driver hooks. The
> generic front-end reports an error when the selected mode's driver does
> not implement them, so calling them on a non-AEAD mode fails cleanly.
> 
> Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
> ---
>  crypto/cipherpriv.h     |  8 ++++++++
>  include/crypto/cipher.h | 36 ++++++++++++++++++++++++++++++++++++
>  crypto/cipher.c         | 31 +++++++++++++++++++++++++++++++
>  3 files changed, 75 insertions(+)

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>


With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v1 09/15] crypto/cipher-gcrypt: Implement AES-GCM
  2026-07-14  7:29 ` [PATCH v1 09/15] crypto/cipher-gcrypt: Implement AES-GCM Jamin Lin
@ 2026-07-14  8:39   ` Daniel P. Berrangé
  2026-07-14  8:57     ` Jamin Lin
  0 siblings, 1 reply; 23+ messages in thread
From: Daniel P. Berrangé @ 2026-07-14  8:39 UTC (permalink / raw)
  To: Jamin Lin
  Cc: Cédric Le Goater, Peter Maydell, Steven Lee, Troy Lee,
	Kane Chen, Andrew Jeffery, Joel Stanley, Eric Blake,
	Markus Armbruster, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
	open list:All patches CC here, open list:ASPEED BMCs, Troy Lee

On Tue, Jul 14, 2026 at 07:29:14AM +0000, Jamin Lin wrote:
> Map QCRYPTO_CIPHER_MODE_GCM to GCRY_CIPHER_MODE_GCM and advertise it in
> qcrypto_cipher_supports() for 128-bit block ciphers. Add a GCM driver
> whose setiv accepts the (typically 96-bit) nonce, whose encrypt/decrypt
> do not require block-aligned lengths, and which implements setaad via
> gcry_cipher_authenticate() and gettag via gcry_cipher_gettag().
> 
> Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
> ---
>  crypto/cipher-gcrypt.c.inc | 101 +++++++++++++++++++++++++++++++++++++
>  1 file changed, 101 insertions(+)
> 
> diff --git a/crypto/cipher-gcrypt.c.inc b/crypto/cipher-gcrypt.c.inc
> index 12eb9ddb5a..fce09a3c77 100644
> --- a/crypto/cipher-gcrypt.c.inc
> +++ b/crypto/cipher-gcrypt.c.inc
> @@ -65,6 +65,8 @@ static int qcrypto_cipher_mode_to_gcry_mode(QCryptoCipherMode mode)
>          return GCRY_CIPHER_MODE_CBC;
>      case QCRYPTO_CIPHER_MODE_CTR:
>          return GCRY_CIPHER_MODE_CTR;
> +    case QCRYPTO_CIPHER_MODE_GCM:
> +        return GCRY_CIPHER_MODE_GCM;
>      default:
>          return GCRY_CIPHER_MODE_NONE;
>      }
> @@ -104,6 +106,10 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgo alg,
>      case QCRYPTO_CIPHER_MODE_XTS:
>      case QCRYPTO_CIPHER_MODE_CTR:
>          return true;
> +    case QCRYPTO_CIPHER_MODE_GCM:
> +        /* GCM requires a 128-bit block cipher. */
> +        return gcry_cipher_get_algo_blklen(
> +                   qcrypto_cipher_alg_to_gcry_alg(alg)) == 16;
>      default:
>          return false;
>      }
> @@ -228,6 +234,99 @@ static const struct QCryptoCipherDriver qcrypto_gcrypt_ctr_driver = {
>      .cipher_free = qcrypto_gcrypt_ctx_free,
>  };
>  
> +/*
> + * GCM is an AEAD stream mode: the IV/nonce need not match the block size,
> + * the message length need not be a multiple of the block size, associated
> + * data is fed with gcry_cipher_authenticate() and the authentication tag is
> + * read back with gcry_cipher_gettag().
> + */
> +static int qcrypto_gcrypt_gcm_setiv(QCryptoCipher *cipher,
> +                                    const uint8_t *iv, size_t niv,
> +                                    Error **errp)
> +{
> +    QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
> +    gcry_error_t err;
> +
> +    gcry_cipher_reset(ctx->handle);
> +    err = gcry_cipher_setiv(ctx->handle, iv, niv);
> +    if (err != 0) {
> +        error_setg(errp, "Cannot set IV: %s", gcry_strerror(err));
> +        return -1;
> +    }
> +
> +    return 0;
> +}
>
> +static int qcrypto_gcrypt_gcm_encrypt(QCryptoCipher *cipher, const void *in,
> +                                      void *out, size_t len, Error **errp)
> +{
> +    QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
> +    gcry_error_t err;
> +
> +    err = gcry_cipher_encrypt(ctx->handle, out, len, in, len);
> +    if (err != 0) {
> +        error_setg(errp, "Cannot encrypt data: %s", gcry_strerror(err));
> +        return -1;
> +    }
> +
> +    return 0;
> +}
> +
> +static int qcrypto_gcrypt_gcm_decrypt(QCryptoCipher *cipher, const void *in,
> +                                      void *out, size_t len, Error **errp)
> +{
> +    QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
> +    gcry_error_t err;
> +
> +    err = gcry_cipher_decrypt(ctx->handle, out, len, in, len);
> +    if (err != 0) {
> +        error_setg(errp, "Cannot decrypt data: %s", gcry_strerror(err));
> +        return -1;
> +    }
> +
> +    return 0;
> +}

Is there a reason you can't use the common qcrypto_gcrypt_setiv,
qcrypto_gcrypt_decrypt and qcrypto_gcrypt_encrypt methods ?

> +
> +static int qcrypto_gcrypt_gcm_gettag(QCryptoCipher *cipher,
> +                                     uint8_t *tag, size_t len, Error **errp)
> +{
> +    QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
> +    gcry_error_t err;
> +
> +    err = gcry_cipher_gettag(ctx->handle, tag, len);
> +    if (err != 0) {
> +        error_setg(errp, "Cannot get tag: %s", gcry_strerror(err));
> +        return -1;
> +    }
> +
> +    return 0;
> +}
> +
> +static const struct QCryptoCipherDriver qcrypto_gcrypt_gcm_driver = {
> +    .cipher_encrypt = qcrypto_gcrypt_gcm_encrypt,
> +    .cipher_decrypt = qcrypto_gcrypt_gcm_decrypt,
> +    .cipher_setiv = qcrypto_gcrypt_gcm_setiv,
> +    .cipher_setaad = qcrypto_gcrypt_gcm_setaad,
> +    .cipher_gettag = qcrypto_gcrypt_gcm_gettag,
> +    .cipher_free = qcrypto_gcrypt_ctx_free,
> +};
> +
>  static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgo alg,
>                                               QCryptoCipherMode mode,
>                                               const uint8_t *key,
> @@ -259,6 +358,8 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgo alg,
>  
>      if (mode == QCRYPTO_CIPHER_MODE_CTR) {
>          drv = &qcrypto_gcrypt_ctr_driver;
> +    } else if (mode == QCRYPTO_CIPHER_MODE_GCM) {
> +        drv = &qcrypto_gcrypt_gcm_driver;
>      } else {
>          drv = &qcrypto_gcrypt_driver;
>      }
> -- 
> 2.43.0
> 
> 

With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v1 00/15] Support the ASPEED HACE crypto command
  2026-07-14  7:29 [PATCH v1 00/15] Support the ASPEED HACE crypto command Jamin Lin
                   ` (14 preceding siblings ...)
  2026-07-14  7:29 ` [PATCH v1 15/15] tests/functional/aarch64/test_aspeed_ast2700: Drop the AST2700 crypto self-test workaround Jamin Lin
@ 2026-07-14  8:47 ` Daniel P. Berrangé
  15 siblings, 0 replies; 23+ messages in thread
From: Daniel P. Berrangé @ 2026-07-14  8:47 UTC (permalink / raw)
  To: Jamin Lin
  Cc: Cédric Le Goater, Peter Maydell, Steven Lee, Troy Lee,
	Kane Chen, Andrew Jeffery, Joel Stanley, Eric Blake,
	Markus Armbruster, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
	open list:All patches CC here, open list:ASPEED BMCs, Troy Lee

On Tue, Jul 14, 2026 at 07:29:01AM +0000, Jamin Lin wrote:
>  qapi/crypto.json                              |   4 +-
>  crypto/cipherpriv.h                           |   8 +
>  include/crypto/cipher.h                       |  36 +
>  include/hw/misc/aspeed_hace.h                 |   1 -
>  tests/qtest/aspeed-hace-utils.h               |  20 +
>  crypto/cipher.c                               |  32 +
>  hw/misc/aspeed_hace.c                         | 454 +++++++++++-
>  tests/qtest/aspeed-hace-utils.c               | 662 ++++++++++++++++++
>  tests/qtest/aspeed_hace-test.c                |  18 +
>  tests/qtest/ast2700-hace-test.c               |   9 +
>  tests/unit/test-crypto-cipher.c               | 240 +++++++
>  crypto/cipher-gcrypt.c.inc                    | 101 +++

Both nettle and gnutls also support GCM mode, so can you impl those
too.

>  .../aarch64/test_aspeed_ast2700a1.py          |   5 -
>  .../aarch64/test_aspeed_ast2700a2.py          |   5 -
>  .../aarch64/test_aspeed_ast2700fc.py          |   5 -
>  15 files changed, 1563 insertions(+), 37 deletions(-)
> 
> -- 
> 2.43.0
> 
> 

With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



^ permalink raw reply	[flat|nested] 23+ messages in thread

* RE: [PATCH v1 09/15] crypto/cipher-gcrypt: Implement AES-GCM
  2026-07-14  8:39   ` Daniel P. Berrangé
@ 2026-07-14  8:57     ` Jamin Lin
  2026-07-14  9:31       ` Daniel P. Berrangé
  0 siblings, 1 reply; 23+ messages in thread
From: Jamin Lin @ 2026-07-14  8:57 UTC (permalink / raw)
  To: Daniel P. Berrangé
  Cc: Cédric Le Goater, Peter Maydell, Steven Lee, Troy Lee,
	Kane Chen, Andrew Jeffery, Joel Stanley, Eric Blake,
	Markus Armbruster, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
	open list:All patches CC here, open list:ASPEED BMCs, Troy Lee

Hi Daniel

> Subject: Re: [PATCH v1 09/15] crypto/cipher-gcrypt: Implement AES-GCM
> 
> On Tue, Jul 14, 2026 at 07:29:14AM +0000, Jamin Lin wrote:
> > Map QCRYPTO_CIPHER_MODE_GCM to GCRY_CIPHER_MODE_GCM and
> advertise it
> > in
> > qcrypto_cipher_supports() for 128-bit block ciphers. Add a GCM driver
> > whose setiv accepts the (typically 96-bit) nonce, whose
> > encrypt/decrypt do not require block-aligned lengths, and which
> > implements setaad via
> > gcry_cipher_authenticate() and gettag via gcry_cipher_gettag().
> >
> > Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
> > ---
> >  crypto/cipher-gcrypt.c.inc | 101
> > +++++++++++++++++++++++++++++++++++++
> >  1 file changed, 101 insertions(+)
> >
> > diff --git a/crypto/cipher-gcrypt.c.inc b/crypto/cipher-gcrypt.c.inc
> > index 12eb9ddb5a..fce09a3c77 100644
> > --- a/crypto/cipher-gcrypt.c.inc
> > +++ b/crypto/cipher-gcrypt.c.inc
> > @@ -65,6 +65,8 @@ static int
> qcrypto_cipher_mode_to_gcry_mode(QCryptoCipherMode mode)
> >          return GCRY_CIPHER_MODE_CBC;
> >      case QCRYPTO_CIPHER_MODE_CTR:
> >          return GCRY_CIPHER_MODE_CTR;
> > +    case QCRYPTO_CIPHER_MODE_GCM:
> > +        return GCRY_CIPHER_MODE_GCM;
> >      default:
> >          return GCRY_CIPHER_MODE_NONE;
> >      }
> > @@ -104,6 +106,10 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgo
> alg,
> >      case QCRYPTO_CIPHER_MODE_XTS:
> >      case QCRYPTO_CIPHER_MODE_CTR:
> >          return true;
> > +    case QCRYPTO_CIPHER_MODE_GCM:
> > +        /* GCM requires a 128-bit block cipher. */
> > +        return gcry_cipher_get_algo_blklen(
> > +                   qcrypto_cipher_alg_to_gcry_alg(alg)) == 16;
> >      default:
> >          return false;
> >      }
> > @@ -228,6 +234,99 @@ static const struct QCryptoCipherDriver
> qcrypto_gcrypt_ctr_driver = {
> >      .cipher_free = qcrypto_gcrypt_ctx_free,  };
> >
> > +/*
> > + * GCM is an AEAD stream mode: the IV/nonce need not match the block
> > +size,
> > + * the message length need not be a multiple of the block size,
> > +associated
> > + * data is fed with gcry_cipher_authenticate() and the authentication
> > +tag is
> > + * read back with gcry_cipher_gettag().
> > + */
> > +static int qcrypto_gcrypt_gcm_setiv(QCryptoCipher *cipher,
> > +                                    const uint8_t *iv, size_t niv,
> > +                                    Error **errp) {
> > +    QCryptoCipherGcrypt *ctx = container_of(cipher,
> QCryptoCipherGcrypt, base);
> > +    gcry_error_t err;
> > +
> > +    gcry_cipher_reset(ctx->handle);
> > +    err = gcry_cipher_setiv(ctx->handle, iv, niv);
> > +    if (err != 0) {
> > +        error_setg(errp, "Cannot set IV: %s", gcry_strerror(err));
> > +        return -1;
> > +    }
> > +
> > +    return 0;
> > +}
> >
> > +static int qcrypto_gcrypt_gcm_encrypt(QCryptoCipher *cipher, const void
> *in,
> > +                                      void *out, size_t len, Error
> > +**errp) {
> > +    QCryptoCipherGcrypt *ctx = container_of(cipher,
> QCryptoCipherGcrypt, base);
> > +    gcry_error_t err;
> > +
> > +    err = gcry_cipher_encrypt(ctx->handle, out, len, in, len);
> > +    if (err != 0) {
> > +        error_setg(errp, "Cannot encrypt data: %s", gcry_strerror(err));
> > +        return -1;
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +static int qcrypto_gcrypt_gcm_decrypt(QCryptoCipher *cipher, const void
> *in,
> > +                                      void *out, size_t len, Error
> > +**errp) {
> > +    QCryptoCipherGcrypt *ctx = container_of(cipher,
> QCryptoCipherGcrypt, base);
> > +    gcry_error_t err;
> > +
> > +    err = gcry_cipher_decrypt(ctx->handle, out, len, in, len);
> > +    if (err != 0) {
> > +        error_setg(errp, "Cannot decrypt data: %s", gcry_strerror(err));
> > +        return -1;
> > +    }
> > +
> > +    return 0;
> > +}
> 
> Is there a reason you can't use the common qcrypto_gcrypt_setiv,
> qcrypto_gcrypt_decrypt and qcrypto_gcrypt_encrypt methods ?
> 

Thanks for the review and suggestion.

The GCM path differs from the common methods in two input checks that GCM legitimately violates:

  - qcrypto_gcrypt_setiv() rejects niv != blocksize, but GCM uses a
    96-bit (12-byte) nonce.
  - qcrypto_gcrypt_encrypt()/decrypt() reject a length that is not a
    multiple of the block size, but GCM must accept arbitrary lengths
    (e.g. the 60-byte NIST test vectors).

So reusing the common methods means relaxing those two checks for GCM,
e.g.:
  /* qcrypto_gcrypt_setiv() */
  if (cipher->mode != QCRYPTO_CIPHER_MODE_GCM && niv != ctx->blocksize) {
      error_setg(errp, "Expected IV size %zu not %zu", ctx->blocksize, niv);
      return -1;
  }
  /* qcrypto_gcrypt_encrypt() / _decrypt() */
  if (cipher->mode != QCRYPTO_CIPHER_MODE_GCM &&
      (len & (ctx->blocksize - 1))) {
      error_setg(errp, "Length %zu must be a multiple of block size %zu",
                 len, ctx->blocksize);
      return -1;
  }

I kept them separate to avoid adding GCM special-cases to the shared
block-cipher path, but I'm happy to switch the GCM driver to the common
qcrypto_gcrypt_{setiv,encrypt,decrypt} (dropping the GCM-specific ones and
keeping only the AEAD setaad/gettag hooks)

If you prefer. I'll make that change in v2 if that's the direction you'd like.

Thanks,
Jamin

> > +static int qcrypto_gcrypt_gcm_gettag(QCryptoCipher *cipher,
> > +                                     uint8_t *tag, size_t len, Error
> > +**errp) {
> > +    QCryptoCipherGcrypt *ctx = container_of(cipher,
> QCryptoCipherGcrypt, base);
> > +    gcry_error_t err;
> > +
> > +    err = gcry_cipher_gettag(ctx->handle, tag, len);
> > +    if (err != 0) {
> > +        error_setg(errp, "Cannot get tag: %s", gcry_strerror(err));
> > +        return -1;
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +static const struct QCryptoCipherDriver qcrypto_gcrypt_gcm_driver = {
> > +    .cipher_encrypt = qcrypto_gcrypt_gcm_encrypt,
> > +    .cipher_decrypt = qcrypto_gcrypt_gcm_decrypt,
> > +    .cipher_setiv = qcrypto_gcrypt_gcm_setiv,
> > +    .cipher_setaad = qcrypto_gcrypt_gcm_setaad,
> > +    .cipher_gettag = qcrypto_gcrypt_gcm_gettag,
> > +    .cipher_free = qcrypto_gcrypt_ctx_free, };
> > +
> >  static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgo alg,
> >
> QCryptoCipherMode mode,
> >                                               const uint8_t *key,
> @@
> > -259,6 +358,8 @@ static QCryptoCipher
> > *qcrypto_cipher_ctx_new(QCryptoCipherAlgo alg,
> >
> >      if (mode == QCRYPTO_CIPHER_MODE_CTR) {
> >          drv = &qcrypto_gcrypt_ctr_driver;
> > +    } else if (mode == QCRYPTO_CIPHER_MODE_GCM) {
> > +        drv = &qcrypto_gcrypt_gcm_driver;
> >      } else {
> >          drv = &qcrypto_gcrypt_driver;
> >      }
> > --
> > 2.43.0
> >
> >
> 
> With regards,
> Daniel
> --
> |: https://berrange.com       ~~
> https://hachyderm.io/@berrange :|
> |: https://libvirt.org          ~~          https://entangle-photo.org :|
> |: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v1 09/15] crypto/cipher-gcrypt: Implement AES-GCM
  2026-07-14  8:57     ` Jamin Lin
@ 2026-07-14  9:31       ` Daniel P. Berrangé
  0 siblings, 0 replies; 23+ messages in thread
From: Daniel P. Berrangé @ 2026-07-14  9:31 UTC (permalink / raw)
  To: Jamin Lin
  Cc: Cédric Le Goater, Peter Maydell, Steven Lee, Troy Lee,
	Kane Chen, Andrew Jeffery, Joel Stanley, Eric Blake,
	Markus Armbruster, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
	open list:All patches CC here, open list:ASPEED BMCs, Troy Lee

On Tue, Jul 14, 2026 at 08:57:24AM +0000, Jamin Lin wrote:
> Hi Daniel
> 
> > Subject: Re: [PATCH v1 09/15] crypto/cipher-gcrypt: Implement AES-GCM
> > 
> > On Tue, Jul 14, 2026 at 07:29:14AM +0000, Jamin Lin wrote:
> > > Map QCRYPTO_CIPHER_MODE_GCM to GCRY_CIPHER_MODE_GCM and
> > advertise it
> > > in
> > > qcrypto_cipher_supports() for 128-bit block ciphers. Add a GCM driver
> > > whose setiv accepts the (typically 96-bit) nonce, whose
> > > encrypt/decrypt do not require block-aligned lengths, and which
> > > implements setaad via
> > > gcry_cipher_authenticate() and gettag via gcry_cipher_gettag().
> > >
> > > Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
> > > ---
> > >  crypto/cipher-gcrypt.c.inc | 101
> > > +++++++++++++++++++++++++++++++++++++
> > >  1 file changed, 101 insertions(+)
> > >
> > > diff --git a/crypto/cipher-gcrypt.c.inc b/crypto/cipher-gcrypt.c.inc
> > > index 12eb9ddb5a..fce09a3c77 100644
> > > --- a/crypto/cipher-gcrypt.c.inc
> > > +++ b/crypto/cipher-gcrypt.c.inc
> > > @@ -65,6 +65,8 @@ static int
> > qcrypto_cipher_mode_to_gcry_mode(QCryptoCipherMode mode)
> > >          return GCRY_CIPHER_MODE_CBC;
> > >      case QCRYPTO_CIPHER_MODE_CTR:
> > >          return GCRY_CIPHER_MODE_CTR;
> > > +    case QCRYPTO_CIPHER_MODE_GCM:
> > > +        return GCRY_CIPHER_MODE_GCM;
> > >      default:
> > >          return GCRY_CIPHER_MODE_NONE;
> > >      }
> > > @@ -104,6 +106,10 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgo
> > alg,
> > >      case QCRYPTO_CIPHER_MODE_XTS:
> > >      case QCRYPTO_CIPHER_MODE_CTR:
> > >          return true;
> > > +    case QCRYPTO_CIPHER_MODE_GCM:
> > > +        /* GCM requires a 128-bit block cipher. */
> > > +        return gcry_cipher_get_algo_blklen(
> > > +                   qcrypto_cipher_alg_to_gcry_alg(alg)) == 16;
> > >      default:
> > >          return false;
> > >      }
> > > @@ -228,6 +234,99 @@ static const struct QCryptoCipherDriver
> > qcrypto_gcrypt_ctr_driver = {
> > >      .cipher_free = qcrypto_gcrypt_ctx_free,  };
> > >
> > > +/*
> > > + * GCM is an AEAD stream mode: the IV/nonce need not match the block
> > > +size,
> > > + * the message length need not be a multiple of the block size,
> > > +associated
> > > + * data is fed with gcry_cipher_authenticate() and the authentication
> > > +tag is
> > > + * read back with gcry_cipher_gettag().
> > > + */
> > > +static int qcrypto_gcrypt_gcm_setiv(QCryptoCipher *cipher,
> > > +                                    const uint8_t *iv, size_t niv,
> > > +                                    Error **errp) {
> > > +    QCryptoCipherGcrypt *ctx = container_of(cipher,
> > QCryptoCipherGcrypt, base);
> > > +    gcry_error_t err;
> > > +
> > > +    gcry_cipher_reset(ctx->handle);
> > > +    err = gcry_cipher_setiv(ctx->handle, iv, niv);
> > > +    if (err != 0) {
> > > +        error_setg(errp, "Cannot set IV: %s", gcry_strerror(err));
> > > +        return -1;
> > > +    }
> > > +
> > > +    return 0;
> > > +}
> > >
> > > +static int qcrypto_gcrypt_gcm_encrypt(QCryptoCipher *cipher, const void
> > *in,
> > > +                                      void *out, size_t len, Error
> > > +**errp) {
> > > +    QCryptoCipherGcrypt *ctx = container_of(cipher,
> > QCryptoCipherGcrypt, base);
> > > +    gcry_error_t err;
> > > +
> > > +    err = gcry_cipher_encrypt(ctx->handle, out, len, in, len);
> > > +    if (err != 0) {
> > > +        error_setg(errp, "Cannot encrypt data: %s", gcry_strerror(err));
> > > +        return -1;
> > > +    }
> > > +
> > > +    return 0;
> > > +}
> > > +
> > > +static int qcrypto_gcrypt_gcm_decrypt(QCryptoCipher *cipher, const void
> > *in,
> > > +                                      void *out, size_t len, Error
> > > +**errp) {
> > > +    QCryptoCipherGcrypt *ctx = container_of(cipher,
> > QCryptoCipherGcrypt, base);
> > > +    gcry_error_t err;
> > > +
> > > +    err = gcry_cipher_decrypt(ctx->handle, out, len, in, len);
> > > +    if (err != 0) {
> > > +        error_setg(errp, "Cannot decrypt data: %s", gcry_strerror(err));
> > > +        return -1;
> > > +    }
> > > +
> > > +    return 0;
> > > +}
> > 
> > Is there a reason you can't use the common qcrypto_gcrypt_setiv,
> > qcrypto_gcrypt_decrypt and qcrypto_gcrypt_encrypt methods ?
> > 
> 
> Thanks for the review and suggestion.
> 
> The GCM path differs from the common methods in two input checks that GCM legitimately violates:
> 
>   - qcrypto_gcrypt_setiv() rejects niv != blocksize, but GCM uses a
>     96-bit (12-byte) nonce.
>   - qcrypto_gcrypt_encrypt()/decrypt() reject a length that is not a
>     multiple of the block size, but GCM must accept arbitrary lengths
>     (e.g. the 60-byte NIST test vectors).
> 
> So reusing the common methods means relaxing those two checks for GCM,
> e.g.:
>   /* qcrypto_gcrypt_setiv() */
>   if (cipher->mode != QCRYPTO_CIPHER_MODE_GCM && niv != ctx->blocksize) {
>       error_setg(errp, "Expected IV size %zu not %zu", ctx->blocksize, niv);
>       return -1;
>   }
>   /* qcrypto_gcrypt_encrypt() / _decrypt() */
>   if (cipher->mode != QCRYPTO_CIPHER_MODE_GCM &&
>       (len & (ctx->blocksize - 1))) {
>       error_setg(errp, "Length %zu must be a multiple of block size %zu",
>                  len, ctx->blocksize);
>       return -1;
>   }
> 
> I kept them separate to avoid adding GCM special-cases to the shared
> block-cipher path, but I'm happy to switch the GCM driver to the common
> qcrypto_gcrypt_{setiv,encrypt,decrypt} (dropping the GCM-specific ones and
> keeping only the AEAD setaad/gettag hooks)
> 
> If you prefer. I'll make that change in v2 if that's the direction you'd like.

No, leave it as it is.

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Acked-by: Daniel P. Berrangé <berrange@redhat.com>




With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v1 05/15] tests/qtest/aspeed-hace: Test the crypto command on the AST2600
  2026-07-14  7:29 ` [PATCH v1 05/15] tests/qtest/aspeed-hace: Test the crypto command on the AST2600 Jamin Lin
@ 2026-07-14 10:16   ` Cédric Le Goater
  0 siblings, 0 replies; 23+ messages in thread
From: Cédric Le Goater @ 2026-07-14 10:16 UTC (permalink / raw)
  To: Jamin Lin, Daniel P. Berrangé, Peter Maydell, Steven Lee,
	Troy Lee, Kane Chen, Andrew Jeffery, Joel Stanley, Eric Blake,
	Markus Armbruster, Fabiano Rosas, Laurent Vivier, Paolo Bonzini,
	open list:All patches CC here, open list:ASPEED BMCs
  Cc: Troy Lee

On 7/14/26 09:29, Jamin Lin wrote:
> Extend the crypto known-answer tests to cover the AST2600 crypto engine,
> which drives the source and destination through scatter-gather lists and
> adds CTR mode on top of the ECB/CBC modes shared with the AST2500.
> 
> Add a scatter-gather runner that describes each buffer with three
> non-adjacent fragments to exercise the gather/scatter path, add
> AES/DES/3DES CTR vectors (verifying the counter written back to the
> context buffer), and give aspeed_add_crypto_tests() a mode mask and a
> scatter-gather flag so each SoC registers exactly the modes and transfer
> method it supports. Register the AST2600 with ECB/CBC/CTR in
> scatter-gather mode.
> 
> Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
> ---
>   tests/qtest/aspeed-hace-utils.h |   7 +-
>   tests/qtest/aspeed-hace-utils.c | 211 +++++++++++++++++++++++++++++++-
>   tests/qtest/aspeed_hace-test.c  |   8 +-
>   3 files changed, 218 insertions(+), 8 deletions(-)



On my system, the build use the gnutls crypto backend
(no CONFIG_GCRYPT or CONFIG_NETTLE) and the test fails.

I think a qcrypto_cipher_supports() call is required.

Thanks,

C.

> 
> diff --git a/tests/qtest/aspeed-hace-utils.h b/tests/qtest/aspeed-hace-utils.h
> index 13feaa61e4..82b0b3f93d 100644
> --- a/tests/qtest/aspeed-hace-utils.h
> +++ b/tests/qtest/aspeed-hace-utils.h
> @@ -85,15 +85,18 @@ void aspeed_test_addresses(const char *machine, const uint32_t base,
>   enum {
>       CRYPT_MODE_ECB = 1 << 0,
>       CRYPT_MODE_CBC = 1 << 1,
> +    CRYPT_MODE_CTR = 1 << 2,
>   };
>   
>   /*
>    * Register the crypto known-answer tests that @modes selects (a mask of
>    * CRYPT_MODE_*) for the given machine. Each test is named
> - * "<prefix>/hace/crypto/<mode>".
> + * "<prefix>/hace/crypto/<mode>". @sg selects scatter-gather mode (used by the
> + * AST2600 and later) instead of the AST2500 direct access mode.
>    */
>   void aspeed_add_crypto_tests(const char *prefix, const char *machine,
> -                             uint32_t base, uint64_t dram, uint32_t modes);
> +                             uint32_t base, uint64_t dram, uint32_t modes,
> +                             bool sg);
>   
>   #endif /* TESTS_ASPEED_HACE_UTILS_H */
>   
> diff --git a/tests/qtest/aspeed-hace-utils.c b/tests/qtest/aspeed-hace-utils.c
> index 4deb88dbcc..0b91a4e61a 100644
> --- a/tests/qtest/aspeed-hace-utils.c
> +++ b/tests/qtest/aspeed-hace-utils.c
> @@ -664,6 +664,7 @@ void aspeed_test_addresses(const char *machine, const uint32_t base,
>   #define HACE_CMD_OP_MODE_MASK    (0x7 << 4)
>   #define HACE_CMD_ECB             (0x0 << 4)
>   #define HACE_CMD_CBC             (0x1 << 4)
> +#define HACE_CMD_CTR             (0x4 << 4)
>   #define HACE_CMD_AES128          (0x0 << 2)
>   
>   /* Context buffer layout: IV (DES at +8), key at +0x10 */
> @@ -749,6 +750,49 @@ static const uint8_t tdes_cbc_ptext[8] = {
>   static const uint8_t tdes_cbc_ctext[8] = {
>       0x0e, 0x2d, 0xb6, 0x97, 0x3c, 0x56, 0x33, 0xf4 };
>   
> +/* aes_ctr_tv_template[0] (NIST SP800-38A F.5.1), first block */
> +static const uint8_t aes128_ctr_key[16] = {
> +    0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
> +    0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c };
> +static const uint8_t aes128_ctr_iv[16] = {
> +    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
> +    0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff };
> +static const uint8_t aes128_ctr_ptext[16] = {
> +    0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
> +    0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a };
> +static const uint8_t aes128_ctr_ctext[16] = {
> +    0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26,
> +    0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce };
> +static const uint8_t aes128_ctr_ivout[16] = {
> +    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
> +    0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xff, 0x00 };
> +
> +/* des_ctr_tv_template[0] (Crypto++), first block */
> +static const uint8_t des_ctr_key[8] = {
> +    0xc9, 0x83, 0xa6, 0xc9, 0xec, 0x0f, 0x32, 0x55 };
> +static const uint8_t des_ctr_iv[8] = {
> +    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd };
> +static const uint8_t des_ctr_ptext[8] = {
> +    0x50, 0xb9, 0x22, 0xae, 0x17, 0x80, 0x0c, 0x75 };
> +static const uint8_t des_ctr_ctext[8] = {
> +    0x2f, 0x96, 0x06, 0x0f, 0x50, 0xc9, 0x68, 0x03 };
> +static const uint8_t des_ctr_ivout[8] = {
> +    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe };
> +
> +/* des3_ede_ctr_tv_template[0] (Crypto++), first block */
> +static const uint8_t tdes_ctr_key[24] = {
> +    0x9c, 0xd6, 0xf3, 0x9c, 0xb9, 0x5a, 0x67, 0x00,
> +    0x5a, 0x67, 0x00, 0x2d, 0xce, 0xeb, 0x2d, 0xce,
> +    0xeb, 0xb4, 0x51, 0x72, 0xb4, 0x51, 0x72, 0x1f };
> +static const uint8_t tdes_ctr_iv[8] = {
> +    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
> +static const uint8_t tdes_ctr_ptext[8] = {
> +    0x05, 0xec, 0x77, 0xfb, 0x42, 0xd5, 0x59, 0x20 };
> +static const uint8_t tdes_ctr_ctext[8] = {
> +    0x07, 0xc2, 0x08, 0x20, 0x72, 0x1f, 0x49, 0xef };
> +static const uint8_t tdes_ctr_ivout[8] = {
> +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
> +
>   typedef struct CryptTest {
>       const uint8_t *ptext;
>       const uint8_t *ctext;
> @@ -826,12 +870,59 @@ static const CryptTest crypt_tests[] = {
>           .ctext = tdes_cbc_ctext,
>           .len = sizeof(tdes_cbc_ptext),
>       },
> +    {
> +        .name = "aes128-ctr",
> +        .cmd = HACE_CMD_AES128 | HACE_CMD_CTR,
> +        .key = aes128_ctr_key,
> +        .keylen = sizeof(aes128_ctr_key),
> +        .iv = aes128_ctr_iv,
> +        .ivlen = sizeof(aes128_ctr_iv),
> +        .ptext = aes128_ctr_ptext,
> +        .ctext = aes128_ctr_ctext,
> +        .iv_out = aes128_ctr_ivout,
> +        .len = sizeof(aes128_ctr_ptext),
> +    },
> +    {
> +        .name = "des-ctr",
> +        .cmd = HACE_CMD_DES_SELECT | HACE_CMD_CTR,
> +        .key = des_ctr_key,
> +        .keylen = sizeof(des_ctr_key),
> +        .iv = des_ctr_iv,
> +        .ivlen = sizeof(des_ctr_iv),
> +        .ptext = des_ctr_ptext,
> +        .ctext = des_ctr_ctext,
> +        .iv_out = des_ctr_ivout,
> +        .len = sizeof(des_ctr_ptext),
> +    },
> +    {
> +        .name = "des3_ede-ctr",
> +        .cmd = HACE_CMD_DES_SELECT | HACE_CMD_TRIPLE_DES | HACE_CMD_CTR,
> +        .key = tdes_ctr_key,
> +        .keylen = sizeof(tdes_ctr_key),
> +        .iv = tdes_ctr_iv,
> +        .ivlen = sizeof(tdes_ctr_iv),
> +        .ptext = tdes_ctr_ptext,
> +        .ctext = tdes_ctr_ctext,
> +        .iv_out = tdes_ctr_ivout,
> +        .len = sizeof(tdes_ctr_ptext),
> +    },
>   };
>   
>   /* DRAM offsets for the crypto test source, destination and context buffers. */
>   #define CRYPT_OFF_SRC   0x10000
>   #define CRYPT_OFF_DST   0x20000
>   #define CRYPT_OFF_CTX   0x30000
> +/* Scatter-gather list offsets (each list has CRYPT_SG_FRAGS entries). */
> +#define CRYPT_OFF_SRC_SG  0x40000
> +#define CRYPT_OFF_DST_SG  0x50000
> +/*
> + * The scatter-gather tests split each buffer into CRYPT_SG_FRAGS fragments,
> + * each placed CRYPT_SG_FRAG_STRIDE apart so the fragments never abut. The gaps
> + * make the test fail if the engine ignores the list and reads one contiguous
> + * block.
> + */
> +#define CRYPT_SG_FRAGS         3
> +#define CRYPT_SG_FRAG_STRIDE   0x1000
>   
>   /* Describes one registered crypto test (qtest_add_data_func() data pointer). */
>   typedef struct AspeedCryptoTest {
> @@ -839,6 +930,7 @@ typedef struct AspeedCryptoTest {
>       uint64_t dram;
>       uint32_t base;
>       int index;
> +    bool sg;
>   } AspeedCryptoTest;
>   
>   /* Map a command's operation mode (HACE10[6:4]) to a CRYPT_MODE_* flag. */
> @@ -849,6 +941,8 @@ static uint32_t crypt_mode_flag(uint32_t cmd)
>           return CRYPT_MODE_ECB;
>       case HACE_CMD_CBC:
>           return CRYPT_MODE_CBC;
> +    case HACE_CMD_CTR:
> +        return CRYPT_MODE_CTR;
>       default:
>           return 0;
>       }
> @@ -897,7 +991,104 @@ static void crypt_run_direct(QTestState *s, uint32_t base, uint64_t dram,
>       qtest_memread(s, dst, out, t->len);
>   }
>   
> -static void aspeed_test_crypto_direct(const void *data)
> +/*
> + * Byte range [*frag_off, *frag_off + *frag_len) of fragment @index when an
> + * @len-byte buffer is split into CRYPT_SG_FRAGS pieces; the last piece takes
> + * the remainder of an uneven split.
> + */
> +static void crypt_frag_range(uint32_t len, int index,
> +                             uint32_t *frag_off, uint32_t *frag_len)
> +{
> +    uint32_t base = len / CRYPT_SG_FRAGS;
> +
> +    *frag_off = base * index;
> +    *frag_len = (index == CRYPT_SG_FRAGS - 1) ? len - *frag_off : base;
> +}
> +
> +/*
> + * Scatter [in, len) across CRYPT_SG_FRAGS buffers based at @base_off and spaced
> + * CRYPT_SG_FRAG_STRIDE apart, then build the SG list describing them at @list.
> + * When @in is NULL only the list is built (used for the destination, which the
> + * engine fills in).
> + */
> +static void crypt_make_sg(QTestState *s, uint64_t dram, uint32_t base_off,
> +                          uint64_t list, const uint8_t *in, uint32_t len)
> +{
> +    struct AspeedSgList sg[CRYPT_SG_FRAGS];
> +    uint32_t frag_off;
> +    uint32_t frag_len;
> +    uint64_t buf;
> +    int i;
> +
> +    for (i = 0; i < CRYPT_SG_FRAGS; i++) {
> +        crypt_frag_range(len, i, &frag_off, &frag_len);
> +        buf = dram + base_off + i * CRYPT_SG_FRAG_STRIDE;
> +
> +        if (in) {
> +            qtest_memwrite(s, buf, in + frag_off, frag_len);
> +        }
> +        sg[i].len = cpu_to_le32(frag_len | (i == CRYPT_SG_FRAGS - 1 ?
> +                                            SG_LIST_LEN_LAST : 0));
> +        sg[i].addr = cpu_to_le32((uint32_t)buf);
> +    }
> +
> +    qtest_memwrite(s, list, sg, sizeof(sg));
> +}
> +
> +/* Gather a scatter-gathered result back from the CRYPT_SG_FRAGS buffers. */
> +static void crypt_gather_sg(QTestState *s, uint64_t dram, uint32_t base_off,
> +                            uint8_t *out, uint32_t len)
> +{
> +    uint32_t frag_off;
> +    uint32_t frag_len;
> +    int i;
> +
> +    for (i = 0; i < CRYPT_SG_FRAGS; i++) {
> +        crypt_frag_range(len, i, &frag_off, &frag_len);
> +        qtest_memread(s, dram + base_off + i * CRYPT_SG_FRAG_STRIDE,
> +                      out + frag_off, frag_len);
> +    }
> +}
> +
> +/*
> + * Run one block-cipher (ECB/CBC/CTR) operation in scatter-gather mode and read
> + * back the result. The source and destination are each split across
> + * CRYPT_SG_FRAGS non-adjacent DRAM buffers described by an SG list; the gaps
> + * ensure the test fails if the engine ignores the list and reads one
> + * contiguous block.
> + */
> +static void crypt_run_sg(QTestState *s, uint32_t base, uint64_t dram,
> +                         const CryptTest *t, bool encrypt, uint8_t *out)
> +{
> +    const uint8_t *in = encrypt ? t->ptext : t->ctext;
> +    uint64_t src_sg = dram + CRYPT_OFF_SRC_SG;
> +    uint64_t dst_sg = dram + CRYPT_OFF_DST_SG;
> +    uint64_t ctx = dram + CRYPT_OFF_CTX;
> +    uint32_t cmd = t->cmd | HACE_CMD_ISR_EN | HACE_CMD_SRC_SG_CTRL |
> +                   HACE_CMD_DST_SG_CTRL;
> +
> +    if (encrypt) {
> +        cmd |= HACE_CMD_ENCRYPT;
> +    }
> +
> +    crypt_write_ctx(s, ctx, t);
> +    crypt_make_sg(s, dram, CRYPT_OFF_SRC, src_sg, in, t->len);
> +    crypt_make_sg(s, dram, CRYPT_OFF_DST, dst_sg, NULL, t->len);
> +
> +    qtest_writel(s, base + HACE_CRYPTO_SRC, (uint32_t)src_sg);
> +    qtest_writel(s, base + HACE_CRYPTO_DEST, (uint32_t)dst_sg);
> +    qtest_writel(s, base + HACE_CRYPTO_CONTEXT, (uint32_t)ctx);
> +    qtest_writel(s, base + HACE_CRYPTO_DATA_LEN, t->len);
> +    qtest_writel(s, base + HACE_CRYPTO_CMD, cmd);
> +
> +    g_assert_cmphex(qtest_readl(s, base + HACE_STS) & HACE_CRYPTO_ISR, ==,
> +                    HACE_CRYPTO_ISR);
> +    qtest_writel(s, base + HACE_STS, HACE_CRYPTO_ISR);
> +
> +    crypt_gather_sg(s, dram, CRYPT_OFF_DST, out, t->len);
> +}
> +
> +static void aspeed_test_crypto(const void *data)
>   {
>       const AspeedCryptoTest *c = data;
>       const CryptTest *t = &crypt_tests[c->index];
> @@ -909,7 +1100,11 @@ static void aspeed_test_crypto_direct(const void *data)
>       g_assert_cmpuint(t->len, <=, sizeof(out));
>   
>       /* Encrypt: ptext -> ctext */
> -    crypt_run_direct(s, c->base, c->dram, t, true, out);
> +    if (c->sg) {
> +        crypt_run_sg(s, c->base, c->dram, t, true, out);
> +    } else {
> +        crypt_run_direct(s, c->base, c->dram, t, true, out);
> +    }
>       g_assert_cmpmem(out, t->len, t->ctext, t->len);
>   
>       if (t->iv_out) {
> @@ -919,14 +1114,19 @@ static void aspeed_test_crypto_direct(const void *data)
>       }
>   
>       /* Decrypt: ctext -> ptext */
> -    crypt_run_direct(s, c->base, c->dram, t, false, out);
> +    if (c->sg) {
> +        crypt_run_sg(s, c->base, c->dram, t, false, out);
> +    } else {
> +        crypt_run_direct(s, c->base, c->dram, t, false, out);
> +    }
>       g_assert_cmpmem(out, t->len, t->ptext, t->len);
>   
>       qtest_quit(s);
>   }
>   
>   void aspeed_add_crypto_tests(const char *prefix, const char *machine,
> -                             uint32_t base, uint64_t dram, uint32_t modes)
> +                             uint32_t base, uint64_t dram, uint32_t modes,
> +                             bool sg)
>   {
>       int i;
>   
> @@ -945,7 +1145,8 @@ void aspeed_add_crypto_tests(const char *prefix, const char *machine,
>           t->base = base;
>           t->dram = dram;
>           t->index = i;
> -        qtest_add_data_func_full(path, t, aspeed_test_crypto_direct, g_free);
> +        t->sg = sg;
> +        qtest_add_data_func_full(path, t, aspeed_test_crypto, g_free);
>       }
>   }
>   
> diff --git a/tests/qtest/aspeed_hace-test.c b/tests/qtest/aspeed_hace-test.c
> index 4cb4c475e9..61a3e3feb5 100644
> --- a/tests/qtest/aspeed_hace-test.c
> +++ b/tests/qtest/aspeed_hace-test.c
> @@ -224,6 +224,12 @@ int main(int argc, char **argv)
>       qtest_add_func("ast2600/hace/sha384_accum", test_sha384_accum_ast2600);
>       qtest_add_func("ast2600/hace/sha256_accum", test_sha256_accum_ast2600);
>   
> +    /* The AST2600 crypto engine uses scatter-gather mode and adds CTR. */
> +    aspeed_add_crypto_tests("ast2600", "-machine ast2600-evb", 0x1e6d0000,
> +                            0x80000000,
> +                            CRYPT_MODE_ECB | CRYPT_MODE_CBC | CRYPT_MODE_CTR,
> +                            true);
> +
>       qtest_add_func("ast2500/hace/addresses", test_addresses_ast2500);
>       qtest_add_func("ast2500/hace/sha512", test_sha512_ast2500);
>       qtest_add_func("ast2500/hace/sha256", test_sha256_ast2500);
> @@ -233,7 +239,7 @@ int main(int argc, char **argv)
>        * The AST2500 crypto engine uses direct access mode and supports ECB/CBC.
>        */
>       aspeed_add_crypto_tests("ast2500", "-machine ast2500-evb", 0x1e6e3000,
> -                            0x80000000, CRYPT_MODE_ECB | CRYPT_MODE_CBC);
> +                            0x80000000, CRYPT_MODE_ECB | CRYPT_MODE_CBC, false);
>   
>       qtest_add_func("ast2400/hace/addresses", test_addresses_ast2400);
>       qtest_add_func("ast2400/hace/sha512", test_sha512_ast2400);



^ permalink raw reply	[flat|nested] 23+ messages in thread

end of thread, other threads:[~2026-07-14 10:17 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-14  7:29 [PATCH v1 00/15] Support the ASPEED HACE crypto command Jamin Lin
2026-07-14  7:29 ` [PATCH v1 01/15] hw/misc/aspeed_hace: Support the crypto command in direct access mode Jamin Lin
2026-07-14  7:29 ` [PATCH v1 02/15] tests/qtest/aspeed-hace: Test the crypto command on the AST2500 Jamin Lin
2026-07-14  7:29 ` [PATCH v1 03/15] hw/misc/aspeed_hace: Support scatter-gather mode for the crypto command Jamin Lin
2026-07-14  7:29 ` [PATCH v1 04/15] hw/misc/aspeed_hace: Support the CTR " Jamin Lin
2026-07-14  7:29 ` [PATCH v1 05/15] tests/qtest/aspeed-hace: Test the crypto command on the AST2600 Jamin Lin
2026-07-14 10:16   ` Cédric Le Goater
2026-07-14  7:29 ` [PATCH v1 06/15] tests/qtest/aspeed-hace: Test the crypto command on the AST1030 Jamin Lin
2026-07-14  7:29 ` [PATCH v1 07/15] crypto/cipher: Add GCM to QCryptoCipherMode Jamin Lin
2026-07-14  8:37   ` Daniel P. Berrangé
2026-07-14  7:29 ` [PATCH v1 08/15] crypto/cipher: Add setaad/gettag for AEAD modes Jamin Lin
2026-07-14  8:37   ` Daniel P. Berrangé
2026-07-14  7:29 ` [PATCH v1 09/15] crypto/cipher-gcrypt: Implement AES-GCM Jamin Lin
2026-07-14  8:39   ` Daniel P. Berrangé
2026-07-14  8:57     ` Jamin Lin
2026-07-14  9:31       ` Daniel P. Berrangé
2026-07-14  7:29 ` [PATCH v1 10/15] tests/unit/test-crypto-cipher: Test AES-GCM mode Jamin Lin
2026-07-14  7:29 ` [PATCH v1 11/15] hw/misc/aspeed_hace: Support 64-bit DMA for the crypto command Jamin Lin
2026-07-14  7:29 ` [PATCH v1 12/15] hw/misc/aspeed_hace: Support the AES-GCM mode " Jamin Lin
2026-07-14  7:29 ` [PATCH v1 13/15] hw/misc/aspeed_hace: Enable the crypto command on the AST2700 Jamin Lin
2026-07-14  7:29 ` [PATCH v1 14/15] tests/qtest/aspeed-hace: Test " Jamin Lin
2026-07-14  7:29 ` [PATCH v1 15/15] tests/functional/aarch64/test_aspeed_ast2700: Drop the AST2700 crypto self-test workaround Jamin Lin
2026-07-14  8:47 ` [PATCH v1 00/15] Support the ASPEED HACE crypto command Daniel P. Berrangé

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.