RE: [PATCH v2 01/17] hw/misc/aspeed_hace: Support the crypto command in direct access mode
Jamin Lin <[email protected]>
| Newsgroups | org.nongnu.qemu-arm,org.nongnu.qemu-devel |
|---|---|
| Message-ID | <TYZPR06MB49804F19F3CD19D3D7D48AA8FCDD2@TYZPR06MB4980.apcprd06.prod.outlook.com> |
Hi Kane, > Subject: RE: [PATCH v2 01/17] hw/misc/aspeed_hace: Support the crypto > command in direct access mode > > > -----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? > Thanks for the review and suggestion. CRYPT_IRQ is used to indicate the CRYPT ISR status in the HAC Engine Status Register (0x1C). Both CRYPT_CMD_ISR_EN and CRYPT_IRQ_EN are used to enable the CRYPT IRQ in the Crypto Engine Command Register (0x10). I will remove the duplicate CRYPT_IRQ_EN definition. > > +#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? > Will remove it Thanks, Jamin > > +/* 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