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]

Reply via email to