RE: [PATCH v2 01/17] hw/misc/aspeed_hace: Support the crypto command in direct access mode

Kane Chen <[email protected]>
Newsgroups org.nongnu.qemu-arm,org.nongnu.qemu-devel
Message-ID <SI6PR06MB7631D594BB6D342FEC137AD4F7DE2@SI6PR06MB7631.apcprd06.prod.outlook.com>
> -----Original Message-----
> From: Jamin Lin <[email protected]>
> Sent: Wednesday, July 15, 2026 11:33 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 01/17] hw/misc/aspeed_hace: Support the crypto
> command in direct access mode
> 
> The crypt command register was previously stubbed out. Implement it for the
> direct access mode, where HACE00/HACE04 point directly at contiguous source
> and destination buffers. AES-128/192/256, DES and 3DES are supported in ECB
> and CBC modes via the qcrypto cipher API; the IV and key are read from the
> context buffer (HACE08) and, for CBC, the resulting chaining IV is written back
> to the context.
> 
> The completion interrupt is now raised for every HACE variant as the hardware
> does, which fixes the crypt command hang on the AST2500, AST2600 and
> AST1030. The AST2700 crypto engine still needs 64-bit DMA and AES-GCM,
> which are added later, so it keeps its temporary interrupt-only workaround
> until then.
> 
> For debugging, the context, source and destination buffers are dumped through
> the existing aspeed_hace_hexdump trace event (disabled by default). CTR
> mode, scatter-gather mode and AES-GCM are added separately.
> 
> Signed-off-by: Jamin Lin <[email protected]>
> ---
>  hw/misc/aspeed_hace.c | 283
> +++++++++++++++++++++++++++++++++++++++---
>  1 file changed, 269 insertions(+), 14 deletions(-)
> 
> diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index
> c61efe50c4..de4399f322 100644
> --- a/hw/misc/aspeed_hace.c
> +++ b/hw/misc/aspeed_hace.c
> @@ -18,11 +18,44 @@
>  #include "qapi/error.h"
>  #include "migration/vmstate.h"
>  #include "crypto/hash.h"
> +#include "crypto/cipher.h"
>  #include "hw/core/qdev-properties.h"
>  #include "hw/core/irq.h"
>  #include "trace.h"
> 
> -#define R_CRYPT_CMD     (0x10 / 4)
> +/* Crypto engine registers */
> +#define R_CRYPT_SRC         (0x00 / 4)
> +#define R_CRYPT_DEST        (0x04 / 4)
> +#define R_CRYPT_CONTEXT     (0x08 / 4)
> +#define R_CRYPT_DATA_LEN    (0x0c / 4)
> +/* HACE0C[27:0] holds the crypto data length */
> +#define  CRYPT_DATA_LEN_MASK    0x0FFFFFFF
> +#define R_CRYPT_CMD         (0x10 / 4)
> +/* Crypto engine command register (HACE10) bits */
> +#define  CRYPT_CMD_ENCRYPT          BIT(7)
> +#define  CRYPT_CMD_ISR_EN           BIT(12)

Regarding CRYPT_CMD_ISR_EN, the current code already has similar definitions, such as CRYPT_IRQ and CRYPT_IRQ_EN. Would it make sense to migrate these definitions together for consistency?

> +#define  CRYPT_CMD_DES_SELECT       BIT(16)
> +#define  CRYPT_CMD_TRIPLE_DES       BIT(17)
> +#define  CRYPT_CMD_SRC_SG_CTRL      BIT(18)
> +#define  CRYPT_CMD_DST_SG_CTRL      BIT(19)

CRYPT_CMD_DST_SG_CTRL is currently defined but not used anywhere. Could we defer adding this definition until it is actually needed?

> +/* Operation mode HACE10[6:4] */
> +#define  CRYPT_CMD_OP_MODE_MASK     (0x7 << 4)
> +#define  CRYPT_CMD_ECB              (0x0 << 4)
> +#define  CRYPT_CMD_CBC              (0x1 << 4)
> +/* AES key length HACE10[3:2] */
> +#define  CRYPT_CMD_AES_KEY_LEN_MASK (0x3 << 2)
> +#define  CRYPT_CMD_AES256           (0x2 << 2)
> +#define  CRYPT_CMD_AES192           (0x1 << 2)
> +#define  CRYPT_CMD_AES128           (0x0 << 2)
> +
> +/*
> + * Crypto context buffer layout (HACE08). The IV is at the start of the
> +buffer
> + * (DES places its 8 byte IV at offset 8) and the cipher key at offset 0x10.
> + */
> +#define CRYPT_CTX_IV_OFFSET         0x00
> +#define CRYPT_CTX_DES_IV_OFFSET     0x08
> +#define CRYPT_CTX_KEY_OFFSET        0x10
> +#define CRYPT_CTX_SIZE              0x30
> 
>  #define R_STATUS        (0x1c / 4)
>  #define HASH_IRQ        BIT(9)
> @@ -501,6 +534,216 @@ static void do_hash_operation(AspeedHACEState *s,
> int algo, bool sg_mode,
>      }
>  }
> 
> +static bool crypt_aes_alg(uint32_t cmd, QCryptoCipherAlgo *alg, size_t
> +*keylen) {
> +    switch (cmd & CRYPT_CMD_AES_KEY_LEN_MASK) {
> +    case CRYPT_CMD_AES128:
> +        *alg = QCRYPTO_CIPHER_ALGO_AES_128;
> +        *keylen = 16;
> +        break;
> +    case CRYPT_CMD_AES192:
> +        *alg = QCRYPTO_CIPHER_ALGO_AES_192;
> +        *keylen = 24;
> +        break;
> +    case CRYPT_CMD_AES256:
> +        *alg = QCRYPTO_CIPHER_ALGO_AES_256;
> +        *keylen = 32;
> +        break;
> +    default:
> +        return false;
> +    }
> +
> +    return true;
> +}
> +
> +/*
> + * Decode the crypto command register into a libqcrypto algorithm/mode
> +pair
> + * and the block/IV geometry. Returns false for unsupported selections.
> + */
> +static bool crypt_decode_cmd(uint32_t cmd, QCryptoCipherAlgo *alg,
> +                             QCryptoCipherMode *mode, size_t
> *keylen,
> +                             size_t *blocklen, size_t *iv_offset) {
> +    if (cmd & CRYPT_CMD_DES_SELECT) {
> +        *blocklen = 8;
> +        *iv_offset = CRYPT_CTX_DES_IV_OFFSET;
> +        if (cmd & CRYPT_CMD_TRIPLE_DES) {
> +            *alg = QCRYPTO_CIPHER_ALGO_3DES;
> +            *keylen = 24;
> +        } else {
> +            *alg = QCRYPTO_CIPHER_ALGO_DES;
> +            *keylen = 8;
> +        }
> +    } else {
> +        *blocklen = 16;
> +        *iv_offset = CRYPT_CTX_IV_OFFSET;
> +        if (!crypt_aes_alg(cmd, alg, keylen)) {
> +            return false;
> +        }
> +    }
> +
> +    switch (cmd & CRYPT_CMD_OP_MODE_MASK) {
> +    case CRYPT_CMD_ECB:
> +        *mode = QCRYPTO_CIPHER_MODE_ECB;
> +        break;
> +    case CRYPT_CMD_CBC:
> +        *mode = QCRYPTO_CIPHER_MODE_CBC;
> +        break;
> +    default:
> +        return false;
> +    }
> +
> +    return true;
> +}
> +
> +/*
> + * Direct access mode: the source/destination register (HACE00/HACE04)
> +points
> + * at a single contiguous buffer in DRAM. Copy @len bytes between it
> +and the
> + * bounce buffer @buf; when @to_dram is true @buf is written out,
> +otherwise it
> + * is read in. Returns true on success.
> + */
> +static bool crypt_prepare_direct(AspeedHACEState *s, uint64_t addr,
> +                                 uint8_t *buf, uint32_t len, bool
> +to_dram) {
> +    return !address_space_rw(&s->dram_as, addr,
> MEMTXATTRS_UNSPECIFIED,
> +                             buf, len, to_dram); }
> +
> +/*
> + * Perform an AES/DES/3DES ECB/CBC operation in direct access mode: the
> +source
> + * and destination are single contiguous buffers (HACE00/HACE04) and
> +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.
> + */
> +static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd) {
> +    uint32_t len = s->regs[R_CRYPT_DATA_LEN];
> +    bool encrypt = cmd & CRYPT_CMD_ENCRYPT;
> +    g_autoptr(QCryptoCipher) cipher = NULL;
> +    g_autofree uint8_t *src_buf = NULL;
> +    g_autofree uint8_t *dst_buf = NULL;
> +    uint8_t ctx[CRYPT_CTX_SIZE];
> +    Error *local_err = NULL;
> +    QCryptoCipherMode mode;
> +    QCryptoCipherAlgo alg;
> +    const uint8_t *next_iv;
> +    uint64_t ctx_addr;
> +    uint64_t src_addr;
> +    uint64_t dst_addr;
> +    size_t iv_offset;
> +    size_t blocklen;
> +    size_t keylen;
> +
> +    if (len == 0) {
> +        return;
> +    }
> +
> +    if (!crypt_decode_cmd(cmd, &alg, &mode, &keylen, &blocklen,
> &iv_offset)) {
> +        qemu_log_mask(LOG_UNIMP,
> +                      "%s: Unsupported crypt command 0x%x\n",
> __func__, cmd);
> +        return;
> +    }
> +
> +    if (!qcrypto_cipher_supports(alg, mode)) {
> +        qemu_log_mask(LOG_UNIMP,
> +                      "%s: cipher mode not supported by the crypto
> backend\n",
> +                      __func__);
> +        return;
> +    }
> +
> +    /* Fetch the IV and key from the context buffer in DRAM. */
> +    ctx_addr = s->regs[R_CRYPT_CONTEXT];
> +    if (address_space_read(&s->dram_as, ctx_addr,
> MEMTXATTRS_UNSPECIFIED,
> +                           ctx, sizeof(ctx))) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "%s: Failed to read context, addr=0x%"
> HWADDR_PRIx "\n",
> +                      __func__, ctx_addr);
> +        return;
> +    }
> +
> +    if (trace_event_get_state_backends(TRACE_ASPEED_HACE_HEXDUMP))
> {
> +        hace_hexdump("context", (char *)ctx, sizeof(ctx));
> +    }
> +
> +    cipher = qcrypto_cipher_new(alg, mode, ctx + CRYPT_CTX_KEY_OFFSET,
> keylen,
> +                                &local_err);
> +    if (cipher == NULL) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: qcrypto cipher new
> failed: %s\n",
> +                      __func__, error_get_pretty(local_err));
> +        error_free(local_err);
> +        return;
> +    }
> +
> +    if (mode != QCRYPTO_CIPHER_MODE_ECB &&
> +        qcrypto_cipher_setiv(cipher, ctx + iv_offset, blocklen,
> +                             &local_err) < 0) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: qcrypto cipher setiv
> failed: %s\n",
> +                      __func__, error_get_pretty(local_err));
> +        error_free(local_err);
> +        return;
> +    }
> +
> +    src_buf = g_malloc0(len);
> +    dst_buf = g_malloc0(len);
> +
> +    src_addr = s->regs[R_CRYPT_SRC];
> +    if (!crypt_prepare_direct(s, src_addr, src_buf, len, false)) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "%s: Failed to read src, addr=0x%" HWADDR_PRIx
> "\n",
> +                      __func__, src_addr);
> +        return;
> +    }
> +
> +    if (trace_event_get_state_backends(TRACE_ASPEED_HACE_HEXDUMP))
> {
> +        hace_hexdump("src", (char *)src_buf, len);
> +    }
> +
> +    if (encrypt) {
> +        if (qcrypto_cipher_encrypt(cipher, src_buf, dst_buf, len,
> +                                   &local_err) < 0) {
> +            qemu_log_mask(LOG_GUEST_ERROR, "%s: encrypt failed:
> %s\n",
> +                          __func__, error_get_pretty(local_err));
> +            error_free(local_err);
> +            return;
> +        }
> +    } else {
> +        if (qcrypto_cipher_decrypt(cipher, src_buf, dst_buf, len,
> +                                   &local_err) < 0) {
> +            qemu_log_mask(LOG_GUEST_ERROR, "%s: decrypt failed:
> %s\n",
> +                          __func__, error_get_pretty(local_err));
> +            error_free(local_err);
> +            return;
> +        }
> +    }
> +
> +    dst_addr = s->regs[R_CRYPT_DEST];
> +    if (!crypt_prepare_direct(s, dst_addr, dst_buf, len, true)) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "%s: Failed to write dst, addr=0x%" HWADDR_PRIx
> "\n",
> +                      __func__, dst_addr);
> +        return;
> +    }
> +
> +    if (trace_event_get_state_backends(TRACE_ASPEED_HACE_HEXDUMP))
> {
> +        hace_hexdump("dst", (char *)dst_buf, len);
> +    }
> +
> +    if (mode == QCRYPTO_CIPHER_MODE_CBC) {
> +        /*
> +         * CBC chains on the last ciphertext block: the final block of the
> +         * 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;
> +        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);
> +        }
> +    }
> +}
> +
>  static uint64_t aspeed_hace_read(void *opaque, hwaddr addr, unsigned int
> size)  {
>      AspeedHACEState *s = ASPEED_HACE(opaque); @@ -531,16 +774,22
> @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data,
>                  qemu_irq_lower(s->irq);
>              }
>          }
> -        if (ahc->raise_crypt_interrupt_workaround) {
> -            if (data & CRYPT_IRQ) {
> -                data &= ~CRYPT_IRQ;
> +        if (data & CRYPT_IRQ) {
> +            data &= ~CRYPT_IRQ;
> 
> -                if (s->regs[addr] & CRYPT_IRQ) {
> -                    qemu_irq_lower(s->irq);
> -                }
> +            if (s->regs[addr] & CRYPT_IRQ) {
> +                qemu_irq_lower(s->irq);
>              }
>          }
>          break;
> +    case R_CRYPT_SRC:
> +    case R_CRYPT_DEST:
> +    case R_CRYPT_CONTEXT:
> +        data &= ahc->src_mask;
> +        break;
> +    case R_CRYPT_DATA_LEN:
> +        data &= CRYPT_DATA_LEN_MASK;
> +        break;
>      case R_HASH_SRC:
>          data &= ahc->src_mask;
>          break;
> @@ -589,13 +838,19 @@ static void aspeed_hace_write(void *opaque,
> hwaddr addr, uint64_t data,
>          break;
>      }
>      case R_CRYPT_CMD:
> -        qemu_log_mask(LOG_UNIMP, "%s: Crypt commands not
> implemented\n",
> -                       __func__);
> -        if (ahc->raise_crypt_interrupt_workaround) {
> -            s->regs[R_STATUS] |= CRYPT_IRQ;
> -            if (data & CRYPT_IRQ_EN) {
> -                qemu_irq_raise(s->irq);
> -            }
> +        /*
> +         * The AST2700 crypto engine needs 64-bit DMA and AES-GCM,
> which are
> +         * added later; until then it keeps the temporary workaround of
> only
> +         * raising the completion interrupt without running the command.
> +         */
> +        if (!ahc->raise_crypt_interrupt_workaround) {
> +            do_crypt_operation(s, data);
> +        }
> +
> +        /* Hardware raises the crypt interrupt once the command finishes.
> */
> +        s->regs[R_STATUS] |= CRYPT_IRQ;
> +        if (data & CRYPT_CMD_ISR_EN) {
> +            qemu_irq_raise(s->irq);
>          }
>          break;
>      case R_HASH_SRC_HI:
> --
> 2.43.0
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.