RE: [PATCH v2 14/17] hw/misc/aspeed_hace: Support the AES-GCM mode for the crypto command

Kane Chen <[email protected]>
Newsgroups org.nongnu.qemu-arm,org.nongnu.qemu-devel
Message-ID <SI6PR06MB76317E683525A5CAC78EC7A0F7DE2@SI6PR06MB7631.apcprd06.prod.outlook.com>
> -----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]>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.