> -----Original Message-----
> From: Jamin Lin <jamin_lin@aspeedtech.com>
> Sent: Wednesday, July 15, 2026 11:33 AM
> To: Daniel P. Berrangé <berrange@redhat.com>; Cédric Le Goater
> <clg@kaod.org>; Peter Maydell <peter.maydell@linaro.org>; Steven Lee
> <steven_lee@aspeedtech.com>; Troy Lee <leetroy@gmail.com>; Kane Chen
> <kane_chen@aspeedtech.com>; Andrew Jeffery
> <andrew@codeconstruct.com.au>; Joel Stanley <joel@jms.id.au>; Eric Blake
> <eblake@redhat.com>; Markus Armbruster <armbru@redhat.com>; Fabiano
> Rosas <farosas@suse.de>; Laurent Vivier <lvivier@redhat.com>; Paolo Bonzini
> <pbonzini@redhat.com>; open list:All patches CC here
> <qemu-devel@nongnu.org>; open list:ASPEED BMCs
> <qemu-arm@nongnu.org>
> Cc: Jamin Lin <jamin_lin@aspeedtech.com>; Troy Lee
> <troy_lee@aspeedtech.com>
> Subject: [PATCH v2 04/17] hw/misc/aspeed_hace: Support the CTR mode for
> the crypto command
>
> 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
> 3d3f1fc8dd..09d0515271 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;
>
> @@ -729,8 +750,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];
> @@ -751,7 +778,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)); @@
> -759,7 +786,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)); @@
> -792,13 +819,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
Reviewed-by: Kane Chen <kane_chen@aspeedtech.com>