Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

powerpc: Prevent memory corruption due to cache invalidation of unaligned DMA buffer

On PowerPC processors with non-coherent cache architectures the DMA
subsystem calls invalidate_dcache_range() before performing a DMA read
operation. If the address and length of the DMA buffer are not aligned
to a cache-line boundary this can result in memory outside of the DMA
buffer being invalidated in the cache. If this memory has an
uncommitted store then the data will be lost and a subsequent read of
that address will result in an old value being returned from main memory.

Only when the DMA buffer starts on a cache-line boundary and is an exact
mutiple of the cache-line size can invalidate_dcache_range() be called,
otherwise flush_dcache_range() must be called. flush_dcache_range()
will first flush uncommitted writes, and then invalidate the cache.

Signed-off-by: Andrew Lewis <andrew-lewis at netspace.net.au>
Signed-off-by: Paul Mackerras <paulus@samba.org>

authored by

Andrew Lewis and committed by
Paul Mackerras
03d70617 9d4ae9fc

+9 -2
+9 -2
arch/powerpc/lib/dma-noncoherent.c
··· 348 348 switch (direction) { 349 349 case DMA_NONE: 350 350 BUG(); 351 - case DMA_FROM_DEVICE: /* invalidate only */ 352 - invalidate_dcache_range(start, end); 351 + case DMA_FROM_DEVICE: 352 + /* 353 + * invalidate only when cache-line aligned otherwise there is 354 + * the potential for discarding uncommitted data from the cache 355 + */ 356 + if ((start & (L1_CACHE_BYTES - 1)) || (size & (L1_CACHE_BYTES - 1))) 357 + flush_dcache_range(start, end); 358 + else 359 + invalidate_dcache_range(start, end); 353 360 break; 354 361 case DMA_TO_DEVICE: /* writeback only */ 355 362 clean_dcache_range(start, end);