@samitouri / QOSamiQemu / commits / 604cec223c

hw/misc/aspeed_hace: Support scatter-gather mode for the crypto command

The AST2600 and later crypto engines drive the source and destination through scatter-gather lists (HACE10[18]/[19]) rather than the single contiguous buffers used by the AST2500 direct access mode. Each SG list entry is a length word (SG_LIST_LEN_LAST marks the final entry) followed by a DRAM address, matching the hash engine layout. Add a crypt_prepare_sg() helper that gathers the source into / scatters the destination out of the bounce buffer by walking the SG list, and select it or the existing crypt_prepare_direct() from do_crypt_operation based on HACE10[18], mirroring the hash engine's direct/scatter-gather dispatch. 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-4-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater <clg@redhat.com>

Jamin Lin committed Aug 11, 2026 at 06:01 UTC 604cec223c4bf20b8fef7788263b13d13fed3708
1 file changed +64 -6
hw/misc/aspeed_hace.c
+64 -6
@@ -608,13 +608,58 @@ static bool crypt_prepare_direct(AspeedHACEState *s, uint64_t addr,
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.
611 + * Scatter-gather mode: the source/destination register points at an SG list
612 + * whose entries are a length word (SG_LIST_LEN_LAST flags the final entry)
613 + * followed by a DRAM address, matching the hash engine layout. Gather @len
614 + * bytes into @buf, or scatter @buf back out when @to_dram is true.
615 + * Returns true on success.
616 + */
617 +static bool crypt_prepare_sg(AspeedHACEState *s, uint64_t addr,
618 + uint8_t *buf, uint32_t len, bool to_dram)
619 +{
620 + uint32_t copied = 0;
621 + uint32_t sg_addr;
622 + uint32_t sg_len;
623 + uint32_t entry;
624 + int i;
625 +
626 + for (i = 0; i < ASPEED_HACE_MAX_SG && copied < len; i++) {
627 + entry = address_space_ldl_le(&s->dram_as, addr,
628 + MEMTXATTRS_UNSPECIFIED, NULL);
629 + sg_addr = address_space_ldl_le(&s->dram_as, addr + SG_LIST_LEN_SIZE,
630 + MEMTXATTRS_UNSPECIFIED, NULL);
631 + sg_len = entry & SG_LIST_LEN_MASK;
632 +
633 + sg_addr &= SG_LIST_ADDR_MASK;
634 + addr += SG_LIST_ENTRY_SIZE;
635 +
636 + if (sg_len > len - copied) {
637 + sg_len = len - copied;
638 + }
639 + if (address_space_rw(&s->dram_as, sg_addr, MEMTXATTRS_UNSPECIFIED,
640 + buf + copied, sg_len, to_dram)) {
641 + return false;
642 + }
643 + copied += sg_len;
644 +
645 + if (entry & SG_LIST_LEN_LAST) {
646 + break;
647 + }
648 + }
649 +
650 + return copied == len;
651 +}
652 +
653 +/*
654 + * Perform an AES/DES/3DES ECB/CBC operation. The source and destination are
655 + * either single contiguous buffers (direct access mode) or scatter-gather
656 + * lists (HACE10[18]/[19]), addressed by HACE00/HACE04; the IV/key come from
657 + * the context buffer (HACE08). For CBC the resulting chaining IV is written
658 + * back to the context buffer so the driver can continue the chain.
659 */
660 static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
661 {
662 + bool sg_mode = cmd & CRYPT_CMD_SRC_SG_CTRL;
663 uint32_t len = s->regs[R_CRYPT_DATA_LEN];
664 bool encrypt = cmd & CRYPT_CMD_ENCRYPT;
665 g_autoptr(QCryptoCipher) cipher = NULL;
@@ -631,6 +676,7 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
676 size_t iv_offset;
677 size_t blocklen;
678 size_t keylen;
679 + bool status;
680
681 if (len == 0) {
682 return;
@@ -684,8 +730,14 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
730 src_buf = g_malloc0(len);
731 dst_buf = g_malloc0(len);
732
733 + /* Gather the source into the bounce buffer, per the selected mode. */
734 src_addr = s->regs[R_CRYPT_SRC];
688 - if (!crypt_prepare_direct(s, src_addr, src_buf, len, false)) {
735 + if (sg_mode) {
736 + status = crypt_prepare_sg(s, src_addr, src_buf, len, false);
737 + } else {
738 + status = crypt_prepare_direct(s, src_addr, src_buf, len, false);
739 + }
740 + if (!status) {
741 qemu_log_mask(LOG_GUEST_ERROR,
742 "%s: Failed to read src, addr=0x%" HWADDR_PRIx "\n",
743 __func__, src_addr);
@@ -714,8 +766,14 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
766 }
767 }
768
769 + /* Scatter the result back out, per the selected mode. */
770 dst_addr = s->regs[R_CRYPT_DEST];
718 - if (!crypt_prepare_direct(s, dst_addr, dst_buf, len, true)) {
771 + if (sg_mode) {
772 + status = crypt_prepare_sg(s, dst_addr, dst_buf, len, true);
773 + } else {
774 + status = crypt_prepare_direct(s, dst_addr, dst_buf, len, true);
775 + }
776 + if (!status) {
777 qemu_log_mask(LOG_GUEST_ERROR,
778 "%s: Failed to write dst, addr=0x%" HWADDR_PRIx "\n",
779 __func__, dst_addr);