yangrui9501 opened a new pull request, #19506:
URL: https://github.com/apache/nuttx/pull/19506
## Summary
The STM32H7 SDMMC receive DMA path has two completion branches in
`stm32_recvdma()`: an *unaligned* path that bounces through an internal
buffer and finishes with a CPU `memcpy` (coherent by construction), and an
*aligned* 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 the
transfer 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
* Only the aligned RX completion branch of `stm32_recvdma()` in
`arch/arm/src/stm32h7/stm32_sdmmc.c` is touched.
* The buffer and length are guaranteed cache-line aligned on this path
(that's the condition for taking it), so the added
`up_invalidate_dcache()` call cannot touch adjacent memory.
* The unaligned (bounce-buffer) path is untouched — it was already
coherent via its CPU `memcpy`.
* No config, Kconfig, or board-level changes.
## 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).
* **Corruption fingerprint:** every corrupted byte range was ≤32 bytes,
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 the
exact signature of a stale cache line shadowing the newly-DMA'd sector.
* **Control experiment:** temporarily building with `CONFIG_ARMV7M_DCACHE=n`
made the corruption disappear, isolating the defect to D-cache coherency
(as opposed to, e.g., a DMA/peripheral conflict).
* **Fix verification:** with `CONFIG_ARMV7M_DCACHE=y` and this patch
applied, a downloaded 6,067,229-byte (5.8 MB) ULog file (spanning
thousands of SD sectors) was byte-for-byte identical (`cmp`, matching
sha256) to the source file read directly off the SD card.
* Verified `./tools/checkpatch.sh -g HEAD~1..HEAD` passes clean on this
commit.
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 file @ 0x00071ce0 (the stale cache line):
00071ce0 00 00 00 68 00 00 00 00 00 00 a9 53 1e 02 00 00 ...h.......S....
00071cf0 00 00 00 00 00 00 ae 0f 00 00 00 00 00 00 00 00 ................
reference file @ same offset (the correct data that was overwritten in RAM):
00071ce0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 04 ................
00071cf0 00 00 01 00 00 00 2a 00 44 03 00 87 6e 1e 02 00 ......*.D...n...
reference file @ offset - 512 (the previous SD sector, still sitting in
the stale D-cache line the CPU read instead of RAM):
00071ae0 00 00 00 68 00 00 00 00 00 00 a9 53 1e 02 00 00 ...h.......S....
00071af0 00 00 00 00 00 00 ae 0f 00 00 00 00 00 00 00 00 ................
```
`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/nuttx` tree standalone (no
board 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.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]