arch/arm/stm32h7: invalidate dcache after aligned SDMMC RX DMA#19506
Open
yangrui9501 wants to merge 1 commit into
Open
arch/arm/stm32h7: invalidate dcache after aligned SDMMC RX DMA#19506yangrui9501 wants to merge 1 commit into
yangrui9501 wants to merge 1 commit into
Conversation
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#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>
jerpelea
approved these changes
Jul 23, 2026
xiaoxiang781216
approved these changes
Jul 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The STM32H7 SDMMC receive DMA path has two completion branches in
stm32_recvdma(): an unaligned path that bounces through an internalbuffer and finishes with a CPU
memcpy(coherent by construction), and analigned path that DMAs directly into the caller's buffer.
The aligned path only invalidates the destination buffer before the
transfer, in
stm32_dmarecvsetup(). It never invalidates again after thetransfer completes. On the Cortex-M7, the D-cache can speculatively
prefetch into that (cacheable) buffer between the pre-DMA invalidate and
DMA completion, leaving stale cache lines that shadow the data the IDMA
just wrote to RAM. The CPU then reads a previously cached sector instead
of the freshly received one.
This fix adds the missing invalidate in
stm32_recvdma()'s aligned branch,once the transfer is complete and before the buffer is consumed. This
matches ST's AN4839 guidance: a cache invalidate is required after DMA
completion and before the CPU reads the region, not only before the
transfer starts. A very similar "invalidate too early" defect on STM32H7
SPI DMA is tracked separately in #11594 — this is the SDMMC instance of
the same class of bug.
Impact
stm32_recvdma()inarch/arm/src/stm32h7/stm32_sdmmc.cis touched.(that's the condition for taking it), so the added
up_invalidate_dcache()call cannot touch adjacent memory.coherent via its CPU
memcpy.Testing
Root-caused and verified on a PX4 FMUv6C board (STM32H743) where MAVLink
ULog-over-MAVLink downloads were intermittently corrupted after a firmware
layout change (unrelated custom module addition shifted the heap base by
~10.7 KB, which changed the absolute address / cache-set mapping of the SD
DMA buffers and turned a previously-latent hazard into a reproducible one).
32-byte aligned (== the Cortex-M7 D-cache line size), and byte-for-byte
identical to the data at the same offset in the previous 512-byte SD
sector (== the FAT single-sector cache buffer,
fs_buffer). This is theexact signature of a stale cache line shadowing the newly-DMA'd sector.
CONFIG_ARMV7M_DCACHE=nmade the corruption disappear, isolating the defect to D-cache coherency
(as opposed to, e.g., a DMA/peripheral conflict).
CONFIG_ARMV7M_DCACHE=yand this patchapplied, a downloaded 6,067,229-byte (5.8 MB) ULog file (spanning
thousands of SD sectors) was byte-for-byte identical (
cmp, matchingsha256) to the source file read directly off the SD card.
./tools/checkpatch.sh -g HEAD~1..HEADpasses clean on thiscommit.
Concrete example of one corrupted 32-byte cache line (offset
0x71ce0),showing the corrupted file, what it should contain, and where the
corrupted bytes actually came from (the previous 512-byte sector, at
offset
0x71ce0 - 512 = 0x71ae0):corrupted[0x71ce0:0x71d00] == reference[0x71ae0:0x71b00]byte-for-byte —i.e. the CPU read the D-cache line from the previous sector instead of
the sector the IDMA had just written, which is exactly the stale-line
scenario this patch invalidates against. This was cross-checked across 10
independent corrupted windows in the same file; all 10 showed the same
"equals previous 512-byte sector" pattern. (Full log files available on
request, but omitted here as they add no evidence beyond the above.)
I was not able to build/test the full
apache/nuttxtree standalone (noboard config wired up outside the PX4 build), but the touched code path is
identical between this tree and the PX4-maintained fork where the fix was
hardware-verified — the fix here is a direct, unmodified port of the
hardware-verified change.