@samitouri / QOSamiQemu / commits / 7a1a61b7ac

hw/misc/aspeed_hace: Support the AES-GCM mode for the crypto command

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

Jamin Lin committed Aug 11, 2026 at 06:01 UTC 7a1a61b7ace9a4f7dad5cc3d4d69459029360961
1 file changed +62 -8
hw/misc/aspeed_hace.c
+62 -8
@@ -31,6 +31,9 @@
31 /* HACE0C[27:0] holds the crypto data length */
32 #define CRYPT_DATA_LEN_MASK 0x0FFFFFFF
33 #define R_CRYPT_CMD (0x10 / 4)
34 +/* AES-GCM associated data length (HACE14) and tag write buffer (HACE18) */
35 +#define R_CRYPT_GCM_ADD_LEN (0x14 / 4)
36 +#define R_CRYPT_GCM_TAG (0x18 / 4)
37 /* Crypto engine command register (HACE10) bits */
38 #define CRYPT_CMD_ENCRYPT BIT(7)
39 #define CRYPT_CMD_ISR_EN BIT(12)
@@ -42,6 +45,7 @@
45 #define CRYPT_CMD_ECB (0x0 << 4)
46 #define CRYPT_CMD_CBC (0x1 << 4)
47 #define CRYPT_CMD_CTR (0x4 << 4)
48 +#define CRYPT_CMD_GCM (0x5 << 4)
49 /* AES key length HACE10[3:2] */
50 #define CRYPT_CMD_AES_KEY_LEN_MASK (0x3 << 2)
51 #define CRYPT_CMD_AES256 (0x2 << 2)
@@ -57,10 +61,15 @@
61 #define CRYPT_CTX_KEY_OFFSET 0x10
62 #define CRYPT_CTX_SIZE 0x30
63
64 +/* AES-GCM uses a 96-bit IV and a 128-bit authentication tag */
65 +#define CRYPT_GCM_IV_LEN 12
66 +#define CRYPT_GCM_TAG_LEN 16
67 +
68 /* AST2700 64-bit DMA high address registers for the crypto command */
69 #define R_CRYPT_SRC_HI (0x80 / 4)
70 #define R_CRYPT_DEST_HI (0x84 / 4)
71 #define R_CRYPT_CONTEXT_HI (0x88 / 4)
72 +#define R_CRYPT_GCM_TAG_HI (0x8c / 4)
73
74 #define R_STATUS (0x1c / 4)
75 #define HASH_IRQ BIT(9)
@@ -596,6 +605,9 @@ static bool crypt_decode_cmd(uint32_t cmd, QCryptoCipherAlgo *alg,
605 case CRYPT_CMD_CTR:
606 *mode = QCRYPTO_CIPHER_MODE_CTR;
607 break;
608 + case CRYPT_CMD_GCM:
609 + *mode = QCRYPTO_CIPHER_MODE_GCM;
610 + break;
611 default:
612 return false;
613 }
@@ -689,11 +701,12 @@ static uint64_t crypt_get_addr(AspeedHACEState *s, int reg, int reg_hi)
701 }
702
703 /*
692 - * Perform an AES/DES/3DES ECB/CBC operation. The source and destination are
693 - * either single contiguous buffers (direct access mode) or scatter-gather
694 - * lists (HACE10[18]/[19]), addressed by HACE00/HACE04; the IV/key come from
695 - * the context buffer (HACE08). For CBC the resulting chaining IV is written
696 - * back to the context buffer so the driver can continue the chain.
704 + * Perform an AES/DES/3DES ECB/CBC/CTR or AES-GCM operation. The source and
705 + * destination are either single contiguous buffers (direct access mode) or
706 + * scatter-gather lists (HACE10[18]/[19]), addressed by HACE00/HACE04; the
707 + * IV/key come from the context buffer (HACE08). For CBC and CTR the resulting
708 + * chaining state is written back to the context buffer so the driver can
709 + * continue; for GCM the authentication tag is written to the tag buffer.
710 */
711 static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
712 {
@@ -703,6 +716,7 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
716 g_autoptr(QCryptoCipher) cipher = NULL;
717 g_autofree uint8_t *src_buf = NULL;
718 g_autofree uint8_t *dst_buf = NULL;
719 + uint8_t tag[CRYPT_GCM_TAG_LEN];
720 uint8_t ctx[CRYPT_CTX_SIZE];
721 Error *local_err = NULL;
722 QCryptoCipherMode mode;
@@ -711,10 +725,13 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
725 uint64_t ctx_addr;
726 uint64_t src_addr;
727 uint64_t dst_addr;
728 + uint64_t tag_addr;
729 + uint32_t aad_len;
730 size_t iv_offset;
731 size_t blocklen;
732 size_t buf_len;
733 size_t keylen;
734 + size_t ivlen;
735 bool status;
736
737 if (len == 0) {
@@ -734,6 +751,20 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
751 return;
752 }
753
754 + /* GCM uses a 96-bit IV; the block modes use a full-block IV. */
755 + ivlen = (mode == QCRYPTO_CIPHER_MODE_GCM) ? CRYPT_GCM_IV_LEN : blocklen;
756 +
757 + /*
758 + * The hardware GCM path is only exercised without associated data (the
759 + * driver falls back to software when there is any), so AAD is not modelled.
760 + */
761 + aad_len = s->regs[R_CRYPT_GCM_ADD_LEN];
762 + if (mode == QCRYPTO_CIPHER_MODE_GCM && aad_len != 0) {
763 + qemu_log_mask(LOG_UNIMP,
764 + "%s: GCM associated data is not implemented\n", __func__);
765 + return;
766 + }
767 +
768 /* Fetch the IV and key from the context buffer in DRAM. */
769 ctx_addr = crypt_get_addr(s, R_CRYPT_CONTEXT, R_CRYPT_CONTEXT_HI);
770 if (address_space_read(&s->dram_as, ctx_addr, MEMTXATTRS_UNSPECIFIED,
@@ -758,7 +789,7 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
789 }
790
791 if (mode != QCRYPTO_CIPHER_MODE_ECB &&
761 - qcrypto_cipher_setiv(cipher, ctx + iv_offset, blocklen,
792 + qcrypto_cipher_setiv(cipher, ctx + iv_offset, ivlen,
793 &local_err) < 0) {
794 qemu_log_mask(LOG_GUEST_ERROR, "%s: qcrypto cipher setiv failed: %s\n",
795 __func__, error_get_pretty(local_err));
@@ -769,9 +800,11 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
800 /*
801 * Round the working buffers up to a whole block. Block modes are already
802 * block-aligned; the stream-like CTR mode may leave a partial final block
772 - * that the engine still processes a full block at a time.
803 + * that the engine still processes a full block at a time. GCM handles a
804 + * partial final block itself, so it operates on the exact length.
805 */
774 - buf_len = QEMU_ALIGN_UP(len, blocklen);
806 + buf_len = (mode == QCRYPTO_CIPHER_MODE_GCM) ?
807 + len : QEMU_ALIGN_UP(len, blocklen);
808 src_buf = g_malloc0(buf_len);
809 dst_buf = g_malloc0(buf_len);
810
@@ -855,6 +888,24 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
888 "%s: Failed to write IV, addr=0x%" HWADDR_PRIx "\n",
889 __func__, ctx_addr + iv_offset);
890 }
891 + } else if (mode == QCRYPTO_CIPHER_MODE_GCM) {
892 + /*
893 + * GCM authenticates the message and writes the resulting tag to the
894 + * dedicated tag buffer (HACE18/HACE8C).
895 + */
896 + if (qcrypto_cipher_gettag(cipher, tag, sizeof(tag), &local_err) < 0) {
897 + qemu_log_mask(LOG_GUEST_ERROR, "%s: qcrypto cipher gettag failed: "
898 + "%s\n", __func__, error_get_pretty(local_err));
899 + error_free(local_err);
900 + return;
901 + }
902 + tag_addr = crypt_get_addr(s, R_CRYPT_GCM_TAG, R_CRYPT_GCM_TAG_HI);
903 + if (address_space_write(&s->dram_as, tag_addr, MEMTXATTRS_UNSPECIFIED,
904 + tag, sizeof(tag))) {
905 + qemu_log_mask(LOG_GUEST_ERROR,
906 + "%s: Failed to write tag, addr=0x%" HWADDR_PRIx "\n",
907 + __func__, tag_addr);
908 + }
909 }
910 }
911
@@ -899,9 +950,11 @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data,
950 case R_CRYPT_SRC:
951 case R_CRYPT_DEST:
952 case R_CRYPT_CONTEXT:
953 + case R_CRYPT_GCM_TAG:
954 data &= ahc->src_mask;
955 break;
956 case R_CRYPT_DATA_LEN:
957 + case R_CRYPT_GCM_ADD_LEN:
958 data &= CRYPT_DATA_LEN_MASK;
959 break;
960 case R_HASH_SRC:
@@ -980,6 +1033,7 @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data,
1033 data &= ahc->src_hi_mask;
1034 break;
1035 case R_CRYPT_DEST_HI:
1036 + case R_CRYPT_GCM_TAG_HI:
1037 data &= ahc->dest_hi_mask;
1038 break;
1039 case R_CRYPT_CONTEXT_HI: