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 <noreply@anthropic.com>
Signed-off-by: Yang-Rui Li <yang77567789@gmail.com>
This commit is contained in:
Yang-Rui Li 2026-07-23 12:00:57 +08:00 committed by Xiang Xiao
parent 6fa47d6de4
commit 2cb7b7c03e

View file

@ -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;
}