@samitouri / QOSamiQemu / commits / 33399898b8

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> Reviewed-by: Kane Chen <kane_chen@aspeedtech.com> Link: https://lore.kernel.org/qemu-devel/20260811060115.1849266-5-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater <clg@redhat.com>

Jamin Lin committed Aug 11, 2026 at 06:01 UTC 33399898b820f34f679f6740b1e0d73993e5d034
1 file changed +45 -5
hw/misc/aspeed_hace.c
+45 -5
@@ -41,6 +41,7 @@
41 #define CRYPT_CMD_OP_MODE_MASK (0x7 << 4)
42 #define CRYPT_CMD_ECB (0x0 << 4)
43 #define CRYPT_CMD_CBC (0x1 << 4)
44 +#define CRYPT_CMD_CTR (0x4 << 4)
45 /* AES key length HACE10[3:2] */
46 #define CRYPT_CMD_AES_KEY_LEN_MASK (0x3 << 2)
47 #define CRYPT_CMD_AES256 (0x2 << 2)
@@ -587,6 +588,9 @@ static bool crypt_decode_cmd(uint32_t cmd, QCryptoCipherAlgo *alg,
588 case CRYPT_CMD_CBC:
589 *mode = QCRYPTO_CIPHER_MODE_CBC;
590 break;
591 + case CRYPT_CMD_CTR:
592 + *mode = QCRYPTO_CIPHER_MODE_CTR;
593 + break;
594 default:
595 return false;
596 }
@@ -650,6 +654,22 @@ static bool crypt_prepare_sg(AspeedHACEState *s, uint64_t addr,
654 return copied == len;
655 }
656
657 +/*
658 + * Add @add to the big-endian counter block @ctr (@len bytes) in place, so the
659 + * CTR mode counter can be advanced by the number of blocks just consumed.
660 + */
661 +static void crypt_be_add(uint8_t *ctr, size_t len, uint64_t add)
662 +{
663 + size_t i = len;
664 +
665 + while (i > 0 && add) {
666 + i--;
667 + add += ctr[i];
668 + ctr[i] = add & 0xff;
669 + add >>= 8;
670 + }
671 +}
672 +
673 /*
674 * Perform an AES/DES/3DES ECB/CBC operation. The source and destination are
675 * either single contiguous buffers (direct access mode) or scatter-gather
@@ -675,6 +695,7 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
695 uint64_t dst_addr;
696 size_t iv_offset;
697 size_t blocklen;
698 + size_t buf_len;
699 size_t keylen;
700 bool status;
701
@@ -727,8 +748,14 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
748 return;
749 }
750
730 - src_buf = g_malloc0(len);
731 - dst_buf = g_malloc0(len);
751 + /*
752 + * Round the working buffers up to a whole block. Block modes are already
753 + * block-aligned; the stream-like CTR mode may leave a partial final block
754 + * that the engine still processes a full block at a time.
755 + */
756 + buf_len = QEMU_ALIGN_UP(len, blocklen);
757 + src_buf = g_malloc0(buf_len);
758 + dst_buf = g_malloc0(buf_len);
759
760 /* Gather the source into the bounce buffer, per the selected mode. */
761 src_addr = s->regs[R_CRYPT_SRC];
@@ -749,7 +776,7 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
776 }
777
778 if (encrypt) {
752 - if (qcrypto_cipher_encrypt(cipher, src_buf, dst_buf, len,
779 + if (qcrypto_cipher_encrypt(cipher, src_buf, dst_buf, buf_len,
780 &local_err) < 0) {
781 qemu_log_mask(LOG_GUEST_ERROR, "%s: encrypt failed: %s\n",
782 __func__, error_get_pretty(local_err));
@@ -757,7 +784,7 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
784 return;
785 }
786 } else {
760 - if (qcrypto_cipher_decrypt(cipher, src_buf, dst_buf, len,
787 + if (qcrypto_cipher_decrypt(cipher, src_buf, dst_buf, buf_len,
788 &local_err) < 0) {
789 qemu_log_mask(LOG_GUEST_ERROR, "%s: decrypt failed: %s\n",
790 __func__, error_get_pretty(local_err));
@@ -790,13 +817,26 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
817 * output when encrypting, or of the input when decrypting. Write it
818 * back as the IV for the next request.
819 */
793 - next_iv = (encrypt ? dst_buf : src_buf) + len - blocklen;
820 + next_iv = (encrypt ? dst_buf : src_buf) + buf_len - blocklen;
821 if (address_space_write(&s->dram_as, ctx_addr + iv_offset,
822 MEMTXATTRS_UNSPECIFIED, next_iv, blocklen)) {
823 qemu_log_mask(LOG_GUEST_ERROR,
824 "%s: Failed to write IV, addr=0x%" HWADDR_PRIx "\n",
825 __func__, ctx_addr + iv_offset);
826 }
827 + } else if (mode == QCRYPTO_CIPHER_MODE_CTR) {
828 + /*
829 + * CTR chains on the counter, which advances by one per block. Add the
830 + * number of blocks processed (buf_len / blocklen) and write it back.
831 + */
832 + crypt_be_add(ctx + iv_offset, blocklen, buf_len / blocklen);
833 + if (address_space_write(&s->dram_as, ctx_addr + iv_offset,
834 + MEMTXATTRS_UNSPECIFIED, ctx + iv_offset,
835 + blocklen)) {
836 + qemu_log_mask(LOG_GUEST_ERROR,
837 + "%s: Failed to write IV, addr=0x%" HWADDR_PRIx "\n",
838 + __func__, ctx_addr + iv_offset);
839 + }
840 }
841 }
842