@samitouri / QOSamiQemu / commits / c264c5175e

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

Jamin Lin committed Aug 11, 2026 at 06:01 UTC c264c5175ee0672169923eb61d3bd3dd7018db1a
1 file changed +268 -15
hw/misc/aspeed_hace.c
+268 -15
@@ -18,11 +18,43 @@
18 #include "qapi/error.h"
19 #include "migration/vmstate.h"
20 #include "crypto/hash.h"
21 +#include "crypto/cipher.h"
22 #include "hw/core/qdev-properties.h"
23 #include "hw/core/irq.h"
24 #include "trace.h"
25
25 -#define R_CRYPT_CMD (0x10 / 4)
26 +/* Crypto engine registers */
27 +#define R_CRYPT_SRC (0x00 / 4)
28 +#define R_CRYPT_DEST (0x04 / 4)
29 +#define R_CRYPT_CONTEXT (0x08 / 4)
30 +#define R_CRYPT_DATA_LEN (0x0c / 4)
31 +/* HACE0C[27:0] holds the crypto data length */
32 +#define CRYPT_DATA_LEN_MASK 0x0FFFFFFF
33 +#define R_CRYPT_CMD (0x10 / 4)
34 +/* Crypto engine command register (HACE10) bits */
35 +#define CRYPT_CMD_ENCRYPT BIT(7)
36 +#define CRYPT_CMD_ISR_EN BIT(12)
37 +#define CRYPT_CMD_DES_SELECT BIT(16)
38 +#define CRYPT_CMD_TRIPLE_DES BIT(17)
39 +#define CRYPT_CMD_SRC_SG_CTRL BIT(18)
40 +/* Operation mode HACE10[6:4] */
41 +#define CRYPT_CMD_OP_MODE_MASK (0x7 << 4)
42 +#define CRYPT_CMD_ECB (0x0 << 4)
43 +#define CRYPT_CMD_CBC (0x1 << 4)
44 +/* AES key length HACE10[3:2] */
45 +#define CRYPT_CMD_AES_KEY_LEN_MASK (0x3 << 2)
46 +#define CRYPT_CMD_AES256 (0x2 << 2)
47 +#define CRYPT_CMD_AES192 (0x1 << 2)
48 +#define CRYPT_CMD_AES128 (0x0 << 2)
49 +
50 +/*
51 + * Crypto context buffer layout (HACE08). The IV is at the start of the buffer
52 + * (DES places its 8 byte IV at offset 8) and the cipher key at offset 0x10.
53 + */
54 +#define CRYPT_CTX_IV_OFFSET 0x00
55 +#define CRYPT_CTX_DES_IV_OFFSET 0x08
56 +#define CRYPT_CTX_KEY_OFFSET 0x10
57 +#define CRYPT_CTX_SIZE 0x30
58
59 #define R_STATUS (0x1c / 4)
60 #define HASH_IRQ BIT(9)
@@ -65,7 +97,6 @@
97 /* Other cmd bits */
98 #define HASH_IRQ_EN BIT(9)
99 #define HASH_SG_EN BIT(18)
68 -#define CRYPT_IRQ_EN BIT(12)
100 /* Scatter-gather data list */
101 #define SG_LIST_LEN_SIZE 4
102 #define SG_LIST_LEN_MASK 0x0FFFFFFF
@@ -501,6 +532,216 @@ static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode,
532 }
533 }
534
535 +static bool crypt_aes_alg(uint32_t cmd, QCryptoCipherAlgo *alg, size_t *keylen)
536 +{
537 + switch (cmd & CRYPT_CMD_AES_KEY_LEN_MASK) {
538 + case CRYPT_CMD_AES128:
539 + *alg = QCRYPTO_CIPHER_ALGO_AES_128;
540 + *keylen = 16;
541 + break;
542 + case CRYPT_CMD_AES192:
543 + *alg = QCRYPTO_CIPHER_ALGO_AES_192;
544 + *keylen = 24;
545 + break;
546 + case CRYPT_CMD_AES256:
547 + *alg = QCRYPTO_CIPHER_ALGO_AES_256;
548 + *keylen = 32;
549 + break;
550 + default:
551 + return false;
552 + }
553 +
554 + return true;
555 +}
556 +
557 +/*
558 + * Decode the crypto command register into a libqcrypto algorithm/mode pair
559 + * and the block/IV geometry. Returns false for unsupported selections.
560 + */
561 +static bool crypt_decode_cmd(uint32_t cmd, QCryptoCipherAlgo *alg,
562 + QCryptoCipherMode *mode, size_t *keylen,
563 + size_t *blocklen, size_t *iv_offset)
564 +{
565 + if (cmd & CRYPT_CMD_DES_SELECT) {
566 + *blocklen = 8;
567 + *iv_offset = CRYPT_CTX_DES_IV_OFFSET;
568 + if (cmd & CRYPT_CMD_TRIPLE_DES) {
569 + *alg = QCRYPTO_CIPHER_ALGO_3DES;
570 + *keylen = 24;
571 + } else {
572 + *alg = QCRYPTO_CIPHER_ALGO_DES;
573 + *keylen = 8;
574 + }
575 + } else {
576 + *blocklen = 16;
577 + *iv_offset = CRYPT_CTX_IV_OFFSET;
578 + if (!crypt_aes_alg(cmd, alg, keylen)) {
579 + return false;
580 + }
581 + }
582 +
583 + switch (cmd & CRYPT_CMD_OP_MODE_MASK) {
584 + case CRYPT_CMD_ECB:
585 + *mode = QCRYPTO_CIPHER_MODE_ECB;
586 + break;
587 + case CRYPT_CMD_CBC:
588 + *mode = QCRYPTO_CIPHER_MODE_CBC;
589 + break;
590 + default:
591 + return false;
592 + }
593 +
594 + return true;
595 +}
596 +
597 +/*
598 + * Direct access mode: the source/destination register (HACE00/HACE04) points
599 + * at a single contiguous buffer in DRAM. Copy @len bytes between it and the
600 + * bounce buffer @buf; when @to_dram is true @buf is written out, otherwise it
601 + * is read in. Returns true on success.
602 + */
603 +static bool crypt_prepare_direct(AspeedHACEState *s, uint64_t addr,
604 + uint8_t *buf, uint32_t len, bool to_dram)
605 +{
606 + return !address_space_rw(&s->dram_as, addr, MEMTXATTRS_UNSPECIFIED,
607 + buf, len, to_dram);
608 +}
609 +
610 +/*
611 + * Perform an AES/DES/3DES ECB/CBC operation in direct access mode: the source
612 + * and destination are single contiguous buffers (HACE00/HACE04) and the IV/key
613 + * come from the context buffer (HACE08). For CBC the resulting chaining IV is
614 + * written back to the context buffer so the driver can continue the chain.
615 + */
616 +static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
617 +{
618 + uint32_t len = s->regs[R_CRYPT_DATA_LEN];
619 + bool encrypt = cmd & CRYPT_CMD_ENCRYPT;
620 + g_autoptr(QCryptoCipher) cipher = NULL;
621 + g_autofree uint8_t *src_buf = NULL;
622 + g_autofree uint8_t *dst_buf = NULL;
623 + uint8_t ctx[CRYPT_CTX_SIZE];
624 + Error *local_err = NULL;
625 + QCryptoCipherMode mode;
626 + QCryptoCipherAlgo alg;
627 + const uint8_t *next_iv;
628 + uint64_t ctx_addr;
629 + uint64_t src_addr;
630 + uint64_t dst_addr;
631 + size_t iv_offset;
632 + size_t blocklen;
633 + size_t keylen;
634 +
635 + if (len == 0) {
636 + return;
637 + }
638 +
639 + if (!crypt_decode_cmd(cmd, &alg, &mode, &keylen, &blocklen, &iv_offset)) {
640 + qemu_log_mask(LOG_UNIMP,
641 + "%s: Unsupported crypt command 0x%x\n", __func__, cmd);
642 + return;
643 + }
644 +
645 + if (!qcrypto_cipher_supports(alg, mode)) {
646 + qemu_log_mask(LOG_UNIMP,
647 + "%s: cipher mode not supported by the crypto backend\n",
648 + __func__);
649 + return;
650 + }
651 +
652 + /* Fetch the IV and key from the context buffer in DRAM. */
653 + ctx_addr = s->regs[R_CRYPT_CONTEXT];
654 + if (address_space_read(&s->dram_as, ctx_addr, MEMTXATTRS_UNSPECIFIED,
655 + ctx, sizeof(ctx))) {
656 + qemu_log_mask(LOG_GUEST_ERROR,
657 + "%s: Failed to read context, addr=0x%" HWADDR_PRIx "\n",
658 + __func__, ctx_addr);
659 + return;
660 + }
661 +
662 + if (trace_event_get_state_backends(TRACE_ASPEED_HACE_HEXDUMP)) {
663 + hace_hexdump("context", (char *)ctx, sizeof(ctx));
664 + }
665 +
666 + cipher = qcrypto_cipher_new(alg, mode, ctx + CRYPT_CTX_KEY_OFFSET, keylen,
667 + &local_err);
668 + if (cipher == NULL) {
669 + qemu_log_mask(LOG_GUEST_ERROR, "%s: qcrypto cipher new failed: %s\n",
670 + __func__, error_get_pretty(local_err));
671 + error_free(local_err);
672 + return;
673 + }
674 +
675 + if (mode != QCRYPTO_CIPHER_MODE_ECB &&
676 + qcrypto_cipher_setiv(cipher, ctx + iv_offset, blocklen,
677 + &local_err) < 0) {
678 + qemu_log_mask(LOG_GUEST_ERROR, "%s: qcrypto cipher setiv failed: %s\n",
679 + __func__, error_get_pretty(local_err));
680 + error_free(local_err);
681 + return;
682 + }
683 +
684 + src_buf = g_malloc0(len);
685 + dst_buf = g_malloc0(len);
686 +
687 + src_addr = s->regs[R_CRYPT_SRC];
688 + if (!crypt_prepare_direct(s, src_addr, src_buf, len, false)) {
689 + qemu_log_mask(LOG_GUEST_ERROR,
690 + "%s: Failed to read src, addr=0x%" HWADDR_PRIx "\n",
691 + __func__, src_addr);
692 + return;
693 + }
694 +
695 + if (trace_event_get_state_backends(TRACE_ASPEED_HACE_HEXDUMP)) {
696 + hace_hexdump("src", (char *)src_buf, len);
697 + }
698 +
699 + if (encrypt) {
700 + if (qcrypto_cipher_encrypt(cipher, src_buf, dst_buf, len,
701 + &local_err) < 0) {
702 + qemu_log_mask(LOG_GUEST_ERROR, "%s: encrypt failed: %s\n",
703 + __func__, error_get_pretty(local_err));
704 + error_free(local_err);
705 + return;
706 + }
707 + } else {
708 + if (qcrypto_cipher_decrypt(cipher, src_buf, dst_buf, len,
709 + &local_err) < 0) {
710 + qemu_log_mask(LOG_GUEST_ERROR, "%s: decrypt failed: %s\n",
711 + __func__, error_get_pretty(local_err));
712 + error_free(local_err);
713 + return;
714 + }
715 + }
716 +
717 + dst_addr = s->regs[R_CRYPT_DEST];
718 + if (!crypt_prepare_direct(s, dst_addr, dst_buf, len, true)) {
719 + qemu_log_mask(LOG_GUEST_ERROR,
720 + "%s: Failed to write dst, addr=0x%" HWADDR_PRIx "\n",
721 + __func__, dst_addr);
722 + return;
723 + }
724 +
725 + if (trace_event_get_state_backends(TRACE_ASPEED_HACE_HEXDUMP)) {
726 + hace_hexdump("dst", (char *)dst_buf, len);
727 + }
728 +
729 + if (mode == QCRYPTO_CIPHER_MODE_CBC) {
730 + /*
731 + * CBC chains on the last ciphertext block: the final block of the
732 + * output when encrypting, or of the input when decrypting. Write it
733 + * back as the IV for the next request.
734 + */
735 + next_iv = (encrypt ? dst_buf : src_buf) + len - blocklen;
736 + if (address_space_write(&s->dram_as, ctx_addr + iv_offset,
737 + MEMTXATTRS_UNSPECIFIED, next_iv, blocklen)) {
738 + qemu_log_mask(LOG_GUEST_ERROR,
739 + "%s: Failed to write IV, addr=0x%" HWADDR_PRIx "\n",
740 + __func__, ctx_addr + iv_offset);
741 + }
742 + }
743 +}
744 +
745 static uint64_t aspeed_hace_read(void *opaque, hwaddr addr, unsigned int size)
746 {
747 AspeedHACEState *s = ASPEED_HACE(opaque);
@@ -531,16 +772,22 @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data,
772 qemu_irq_lower(s->irq);
773 }
774 }
534 - if (ahc->raise_crypt_interrupt_workaround) {
535 - if (data & CRYPT_IRQ) {
536 - data &= ~CRYPT_IRQ;
775 + if (data & CRYPT_IRQ) {
776 + data &= ~CRYPT_IRQ;
777
538 - if (s->regs[addr] & CRYPT_IRQ) {
539 - qemu_irq_lower(s->irq);
540 - }
778 + if (s->regs[addr] & CRYPT_IRQ) {
779 + qemu_irq_lower(s->irq);
780 }
781 }
782 break;
783 + case R_CRYPT_SRC:
784 + case R_CRYPT_DEST:
785 + case R_CRYPT_CONTEXT:
786 + data &= ahc->src_mask;
787 + break;
788 + case R_CRYPT_DATA_LEN:
789 + data &= CRYPT_DATA_LEN_MASK;
790 + break;
791 case R_HASH_SRC:
792 data &= ahc->src_mask;
793 break;
@@ -589,13 +836,19 @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data,
836 break;
837 }
838 case R_CRYPT_CMD:
592 - qemu_log_mask(LOG_UNIMP, "%s: Crypt commands not implemented\n",
593 - __func__);
594 - if (ahc->raise_crypt_interrupt_workaround) {
595 - s->regs[R_STATUS] |= CRYPT_IRQ;
596 - if (data & CRYPT_IRQ_EN) {
597 - qemu_irq_raise(s->irq);
598 - }
839 + /*
840 + * The AST2700 crypto engine needs 64-bit DMA and AES-GCM, which are
841 + * added later; until then it keeps the temporary workaround of only
842 + * raising the completion interrupt without running the command.
843 + */
844 + if (!ahc->raise_crypt_interrupt_workaround) {
845 + do_crypt_operation(s, data);
846 + }
847 +
848 + /* Hardware raises the crypt interrupt once the command finishes. */
849 + s->regs[R_STATUS] |= CRYPT_IRQ;
850 + if (data & CRYPT_CMD_ISR_EN) {
851 + qemu_irq_raise(s->irq);
852 }
853 break;
854 case R_HASH_SRC_HI: