From 865eeea3cfc7afb100cb22c977750b3b8d6a5582 Mon Sep 17 00:00:00 2001 From: Yang-Rui Li Date: Thu, 23 Jul 2026 12:00:57 +0800 Subject: [PATCH] arch/arm/stm32h7: invalidate dcache after aligned SDMMC RX DMA The aligned direct-DMA receive path only invalidated the destination buffer before the transfer in stm32_dmarecvsetup(). On the Cortex-M7 the cache can speculatively prefetch into that cacheable buffer between the pre-DMA invalidate and DMA completion, leaving stale lines that shadow the data just written by the IDMA, so the CPU reads a previously cached sector instead of the freshly received data. Invalidate again in stm32_recvdma() once the aligned transfer completes, before the buffer is consumed. The buffer and length are cache-line aligned on this path, so no adjacent memory is affected. This matches the STM32 AN4839 guidance that a cache invalidate is required after DMA completion and before the CPU reads the updated region, not only before the transfer starts. A related instance of the same "invalidate too early" defect on STM32H7 SPI DMA is tracked in apache/nuttx#11594. Root-caused on a PX4 FMUv6C (STM32H743) board where MAVLink ULog downloads were intermittently corrupted: forensic diffing showed corrupted windows were exactly 32 bytes (the D-cache line size), cache-line aligned, and byte-for-byte equal to the previous 512-byte SD sector cached in the FAT single-sector buffer. Disabling the D-cache made the corruption disappear, isolating the defect to cache coherency. After this fix, downloaded files matched the source file byte-for-byte (sha256 identical) across a 5.8 MB log spanning thousands of sectors. Co-Authored-By: Claude Fable 5 Signed-off-by: Yang-Rui Li --- arch/arm/src/stm32h7/stm32_sdmmc.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/arch/arm/src/stm32h7/stm32_sdmmc.c b/arch/arm/src/stm32h7/stm32_sdmmc.c index f1419f258c87c..0383dbfa8d9f5 100644 --- a/arch/arm/src/stm32h7/stm32_sdmmc.c +++ b/arch/arm/src/stm32h7/stm32_sdmmc.c @@ -1359,7 +1359,22 @@ static void stm32_recvdma(struct stm32_dev_s *priv) } else { - /* In an aligned case, we have always received all blocks */ + /* In an aligned case, we have always received all blocks. + * + * The destination buffer was invalidated before the DMA in + * stm32_dmarecvsetup(), but on the Cortex-M7 the cache can + * speculatively prefetch into this (cacheable) buffer between that + * point and DMA completion, leaving stale lines that shadow the + * data just written by the IDMA. Invalidate again now that the + * transfer is complete, before the buffer is consumed, so the CPU + * reads the freshly received data instead of a previously cached + * sector. The buffer and length are cache-line aligned here (that + * is why this aligned path was taken), so no adjacent memory is + * affected. + */ + + up_invalidate_dcache((uintptr_t)priv->buffer, + (uintptr_t)priv->buffer + priv->receivecnt); priv->remaining = 0; }