dm: core: Ensure DMA regions start up with the cache clean
authorSimon Glass <sjg@chromium.org>
Tue, 4 Apr 2017 19:00:19 +0000 (13:00 -0600)
committerSimon Glass <sjg@chromium.org>
Thu, 13 Apr 2017 20:44:49 +0000 (14:44 -0600)
There is a strange interaction with drivers which use DMA if the cache
starts off in a dirty state. Buffer space which the driver reads (but has
not previously written) can contain zero bytes from alloc_priv(). This can
cause corruption of the memory used by DMA for incoming data.

Fix this and add a comment to explain the problem.

This allows the dwc2 driver to work correctly with driver model, for
example.

Signed-off-by: Simon Glass <sjg@chromium.org>
drivers/core/device.c

index e1b0ebffc559c9ddf52c86c68b4404eafe966dae..09a115f753de4b5b6b579c1afbacb2c9a4166802 100644 (file)
@@ -255,8 +255,36 @@ static void *alloc_priv(int size, uint flags)
 
        if (flags & DM_FLAG_ALLOC_PRIV_DMA) {
                priv = memalign(ARCH_DMA_MINALIGN, size);
-               if (priv)
+               if (priv) {
                        memset(priv, '\0', size);
+
+                       /*
+                        * Ensure that the zero bytes are flushed to memory.
+                        * This prevents problems if the driver uses this as
+                        * both an input and an output buffer:
+                        *
+                        * 1. Zeroes written to buffer (here) and sit in the
+                        *      cache
+                        * 2. Driver issues a read command to DMA
+                        * 3. CPU runs out of cache space and evicts some cache
+                        *      data in the buffer, writing zeroes to RAM from
+                        *      the memset() above
+                        * 4. DMA completes
+                        * 5. Buffer now has some DMA data and some zeroes
+                        * 6. Data being read is now incorrect
+                        *
+                        * To prevent this, ensure that the cache is clean
+                        * within this range at the start. The driver can then
+                        * use normal flush-after-write, invalidate-before-read
+                        * procedures.
+                        *
+                        * TODO(sjg@chromium.org): Drop this microblaze
+                        * exception.
+                        */
+#ifndef CONFIG_MICROBLAZE
+                       flush_dcache_range((ulong)priv, (ulong)priv + size);
+#endif
+               }
        } else {
                priv = calloc(1, size);
        }