> -----Original Message-----
> From: Jamin Lin <[email protected]>
> Sent: Wednesday, July 15, 2026 11:34 AM
> To: Daniel P. BerrangĂ© <[email protected]>; CĂ©dric Le Goater
> <[email protected]>; Peter Maydell <[email protected]>; Steven Lee
> <[email protected]>; Troy Lee <[email protected]>; Kane Chen
> <[email protected]>; Andrew Jeffery
> <[email protected]>; Joel Stanley <[email protected]>; Eric Blake
> <[email protected]>; Markus Armbruster <[email protected]>; Fabiano
> Rosas <[email protected]>; Laurent Vivier <[email protected]>; Paolo Bonzini
> <[email protected]>; open list:All patches CC here
> <[email protected]>; open list:ASPEED BMCs
> <[email protected]>
> Cc: Jamin Lin <[email protected]>; Troy Lee
> <[email protected]>
> Subject: [PATCH v2 14/17] 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 <[email protected]>
> ---
>  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
> a807cc1422..927759b48f 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) {
> @@ -736,6 +753,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, @@ -760,7 +791,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)); @@
> -771,9 +802,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);
> 
> @@ -857,6 +890,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);
> +        }
>      }
>  }
> 
> @@ -987,6 +1038,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

Reviewed-by: Kane Chen <[email protected]>

Reply via email to