From c4075d61245dd3b782586d6f50221917d2f5ba85 Mon Sep 17 00:00:00 2001 From: "harshal.patil" Date: Wed, 19 Aug 2026 11:27:42 +0530 Subject: [PATCH 1/2] fix(mbedtls/aes): fix ESP32-S2 Crypto DMA stall on PSRAM output with partial blocks The ESP32-S2 Crypto DMA in-channel stalls silently when a receive descriptor list transitions from external to internal RAM. The AES driver hits this when a PSRAM-output operation has a trailing partial block, as the internal stream descriptor is linked after the external RAM data descriptors. - esp_aes_process_dma(): process the block-aligned part and the partial block as two separate DMA operations, keeping each descriptor list uniform - crypto_dma_ll_reset(): also reset the in-channel (per the TRM receive reset sequence), otherwise stale state from a preceding external-RAM operation corrupts the next operation's output The GCM DMA path is unaffected; it never operates on PSRAM buffers. --- .../hal/esp32s2/include/hal/crypto_dma_ll.h | 4 ++-- .../mbedtls/port/aes/dma/esp_aes_dma_core.c | 20 +++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/components/hal/esp32s2/include/hal/crypto_dma_ll.h b/components/hal/esp32s2/include/hal/crypto_dma_ll.h index 793aeb7364a..a3590f53ba4 100644 --- a/components/hal/esp32s2/include/hal/crypto_dma_ll.h +++ b/components/hal/esp32s2/include/hal/crypto_dma_ll.h @@ -62,8 +62,8 @@ static inline void crypto_dma_ll_reset_register(void) */ static inline void crypto_dma_ll_reset(void) { - SET_PERI_REG_MASK(CRYPTO_DMA_CONF0_REG, CONF0_REG_AHBM_RST | CONF0_REG_OUT_RST | CONF0_REG_AHBM_FIFO_RST); - CLEAR_PERI_REG_MASK(CRYPTO_DMA_CONF0_REG, CONF0_REG_AHBM_RST | CONF0_REG_OUT_RST | CONF0_REG_AHBM_FIFO_RST); + SET_PERI_REG_MASK(CRYPTO_DMA_CONF0_REG, CONF0_REG_AHBM_RST | CONF0_REG_IN_RST | CONF0_REG_OUT_RST | CONF0_REG_AHBM_FIFO_RST); + CLEAR_PERI_REG_MASK(CRYPTO_DMA_CONF0_REG, CONF0_REG_AHBM_RST | CONF0_REG_IN_RST | CONF0_REG_OUT_RST | CONF0_REG_AHBM_FIFO_RST); } /** diff --git a/components/mbedtls/port/aes/dma/esp_aes_dma_core.c b/components/mbedtls/port/aes/dma/esp_aes_dma_core.c index 812fddfa687..fcc8785bffe 100644 --- a/components/mbedtls/port/aes/dma/esp_aes_dma_core.c +++ b/components/mbedtls/port/aes/dma/esp_aes_dma_core.c @@ -1053,6 +1053,26 @@ int esp_aes_process_dma(esp_aes_context *ctx, const unsigned char *input, unsign return MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH; } +#if SOC_AES_CRYPTO_DMA && CONFIG_SPIRAM + /* The Crypto DMA in-channel stalls indefinitely (no descriptor error is raised) when a + receive descriptor list transitions from a buffer in external RAM to one in internal + RAM. Avoid linking the internal stream buffer descriptor after external-RAM data + descriptors by processing the block-aligned part and the trailing partial block as + two separate DMA operations. */ + if (block_bytes > 0 && stream_bytes > 0 && esp_ptr_external_ram(output)) { + ret = esp_aes_process_dma(ctx, input, output, block_bytes, NULL); + if (ret != 0) { + mbedtls_platform_zeroize(output, len); + return ret; + } + ret = esp_aes_process_dma(ctx, input + block_bytes, output + block_bytes, stream_bytes, stream_out); + if (ret != 0) { + mbedtls_platform_zeroize(output, len); + } + return ret; + } +#endif /* SOC_AES_CRYPTO_DMA && CONFIG_SPIRAM */ + if (block_bytes > 0) { /* Flush cache if input in external ram */ #if (CONFIG_SPIRAM && SOC_PSRAM_DMA_CAPABLE) From dd99e05dd1e411c911245277460bd4bda78044d9 Mon Sep 17 00:00:00 2001 From: "harshal.patil" Date: Wed, 19 Aug 2026 13:30:14 +0530 Subject: [PATCH 2/2] test(mbedtls): add partial-block PSRAM coverage for AES Extend the CTR test data length to 6433 bytes so the trailing partial block is exercised with external RAM buffers (which stalls the ESP32-S2 Crypto DMA on an unfixed driver). --- components/mbedtls/test_apps/main/test_aes.c | 4 ++++ .../mbedtls/test_apps/main/test_aes_params.h | 15 ++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/components/mbedtls/test_apps/main/test_aes.c b/components/mbedtls/test_apps/main/test_aes.c index 919381311bd..d870040def2 100644 --- a/components/mbedtls/test_apps/main/test_aes.c +++ b/components/mbedtls/test_apps/main/test_aes.c @@ -664,7 +664,11 @@ void aes_psram_one_buf_ctr_test(void) uint8_t nonce[16]; uint8_t key[16]; uint8_t stream_block[16]; +#if CONFIG_MBEDTLS_HARDWARE_AES size_t SZ = TEST_AES_CTR_DATA_LEN; +#else + size_t SZ = TEST_AES_CTR_DATA_LEN - (TEST_AES_CTR_DATA_LEN % 16); +#endif size_t ALIGNMENT_SIZE_BYTES = 32; memset(nonce, 0x2F, 16); memset(key, 0x1E, 16); diff --git a/components/mbedtls/test_apps/main/test_aes_params.h b/components/mbedtls/test_apps/main/test_aes_params.h index 0280212806d..1680f5bb4c8 100644 --- a/components/mbedtls/test_apps/main/test_aes_params.h +++ b/components/mbedtls/test_apps/main/test_aes_params.h @@ -8,11 +8,15 @@ #include -#define TEST_AES_CTR_DATA_LEN (32 * 200) +#define TEST_AES_CTR_DATA_LEN (32 * 200 + 33) /* Full reference ciphertext for AES-128-CTR with key=0x1E*16, IV=0x2F*16, - * plaintext=0x26*6400. Used to validate the entire ciphertext (not just the - * tail) in aes_ctr_alignment_test and aes_psram_one_buf_ctr_test. */ + * plaintext=0x26*6433. The length is deliberately not a multiple of the AES + * block size so that the trailing partial block handling is exercised, and + * the block-aligned part (6432 bytes) is a multiple of the PSRAM cache line + * size so that the direct external-RAM DMA path is taken. Used to validate + * the entire ciphertext in aes_ctr_alignment_test and + * aes_psram_one_buf_ctr_test. */ static const uint8_t expected_cipher_ctr[] = { 0xe8, 0x1d, 0x27, 0x57, 0x8d, 0x46, 0xd7, 0x62, 0xf7, 0x13, 0x77, 0x35, 0x6b, 0x73, 0xe6, 0x95, @@ -814,6 +818,11 @@ static const uint8_t expected_cipher_ctr[] = { 0xcf, 0x8a, 0x8d, 0x73, 0x8c, 0x6b, 0xfa, 0x4d, 0xd6, 0xc4, 0x18, 0x49, 0xdd, 0xc6, 0xbf, 0xc2, 0xb9, 0xf0, 0x09, 0x69, 0x45, 0x42, 0xc6, 0x05, + 0x5f, 0x42, 0xf6, 0x3b, 0x8e, 0x11, 0x43, 0xc8, + 0xc7, 0xd9, 0x85, 0xf3, 0xdc, 0x39, 0x6b, 0x33, + 0x98, 0x8c, 0xc0, 0xf5, 0x92, 0xb4, 0x04, 0xf9, + 0x2f, 0xdf, 0x12, 0xc0, 0xa1, 0xd2, 0xdc, 0x71, + 0x88, }; static const uint8_t long_input[] = {