Sat Mar 10 03:44:43 2018 UTC ()
Replace bus_dmamap_sync(9) op with a newer one taken from m68k/bus_dma.c.

This could fix memory corruption issue caused by PREREAD ops with regions
whose boundaries are not aligned at cacheline size.


(tsutsui)
diff -r1.59 -r1.60 src/sys/arch/atari/atari/bus.c

cvs diff -r1.59 -r1.60 src/sys/arch/atari/atari/bus.c (expand / switch to context diff)
--- src/sys/arch/atari/atari/bus.c 2018/01/20 17:37:15 1.59
+++ src/sys/arch/atari/atari/bus.c 2018/03/10 03:44:43 1.60
@@ -1,4 +1,4 @@
-/*	$NetBSD: bus.c,v 1.59 2018/01/20 17:37:15 tsutsui Exp $	*/
+/*	$NetBSD: bus.c,v 1.60 2018/03/10 03:44:43 tsutsui Exp $	*/
 
 /*-
  * Copyright (c) 1998 The NetBSD Foundation, Inc.
@@ -33,7 +33,7 @@
 #include "opt_m68k_arch.h"
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: bus.c,v 1.59 2018/01/20 17:37:15 tsutsui Exp $");
+__KERNEL_RCSID(0, "$NetBSD: bus.c,v 1.60 2018/03/10 03:44:43 tsutsui Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -548,46 +548,203 @@
  * by bus-specific DMA map synchronization functions.
  */
 void
-_bus_dmamap_sync(bus_dma_tag_t t, bus_dmamap_t map, bus_addr_t off,
+_bus_dmamap_sync(bus_dma_tag_t t, bus_dmamap_t map, bus_addr_t offset,
     bus_size_t len, int ops)
 {
 #if defined(M68040) || defined(M68060)
-	int	i, pa_off, inc, seglen;
-	u_long	pa, end_pa;
+	bus_addr_t p, e, ps, pe;
+	bus_size_t seglen;
+	bus_dma_segment_t *seg;
+	int i;
+#endif
 
-	pa_off = t->_displacement;
+#if defined(M68020) || defined(M68030)
+#if defined(M68040) || defined(M68060)
+	if (cputype == CPU_68020 || cputype == CPU_68030)
+#endif
+		/* assume no L2 physical cache */
+		return;
+#endif
 
-	/* Flush granularity */
-	inc = (len > 1024) ? PAGE_SIZE : 16;
+#if defined(M68040) || defined(M68060)
+	/* If the whole DMA map is uncached, do nothing. */
+	if ((map->_dm_flags & BUS_DMA_COHERENT) != 0)
+		return;
 
-	for (i = 0; i < map->dm_nsegs && len > 0; i++) {
-		if (map->dm_segs[i].ds_len <= off) {
+	/* Short-circuit for unsupported `ops' */
+	if ((ops & (BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE)) == 0)
+		return;
+
+	/*
+	 * flush/purge the cache.
+	 */
+	for (i = 0; i < map->dm_nsegs && len != 0; i++) {
+		seg = &map->dm_segs[i];
+		if (seg->ds_len <= offset) {
 			/* Segment irrelevant - before requested offset */
-			off -= map->dm_segs[i].ds_len;
+			offset -= seg->ds_len;
 			continue;
 		}
-		seglen = map->dm_segs[i].ds_len - off;
+
+		/*
+		 * Now at the first segment to sync; nail
+		 * each segment until we have exhausted the
+		 * length.
+		 */
+		seglen = seg->ds_len - offset;
 		if (seglen > len)
 			seglen = len;
-		len -= seglen;
-		pa = map->dm_segs[i].ds_addr + off - pa_off;
-		end_pa = pa + seglen;
 
-		if (inc == 16) {
-			pa &= ~15;
-			while (pa < end_pa) {
-				DCFL(pa);
-				pa += 16;
+		ps = seg->ds_addr + offset;
+		pe = ps + seglen;
+
+		if (ops & BUS_DMASYNC_PREWRITE) {
+			p = ps & ~CACHELINE_MASK;
+			e = (pe + CACHELINE_MASK) & ~CACHELINE_MASK;
+
+			/* flush cacheline */
+			while ((p < e) && (p & (CACHELINE_SIZE * 8 - 1)) != 0) {
+				DCFL(p);
+				p += CACHELINE_SIZE;
 			}
-		} else {
-			pa &= ~PGOFSET;
-			while (pa < end_pa) {
-				DCFP(pa);
-				pa += PAGE_SIZE;
+
+			/* flush cachelines per 128bytes */
+			while ((p < e) && (p & PAGE_MASK) != 0) {
+				DCFL(p);
+				p += CACHELINE_SIZE;
+				DCFL(p);
+				p += CACHELINE_SIZE;
+				DCFL(p);
+				p += CACHELINE_SIZE;
+				DCFL(p);
+				p += CACHELINE_SIZE;
+				DCFL(p);
+				p += CACHELINE_SIZE;
+				DCFL(p);
+				p += CACHELINE_SIZE;
+				DCFL(p);
+				p += CACHELINE_SIZE;
+				DCFL(p);
+				p += CACHELINE_SIZE;
 			}
+
+			/* flush page */
+			while (p + PAGE_SIZE <= e) {
+				DCFP(p);
+				p += PAGE_SIZE;
+			}
+
+			/* flush cachelines per 128bytes */
+			while (p + CACHELINE_SIZE * 8 <= e) {
+				DCFL(p);
+				p += CACHELINE_SIZE;
+				DCFL(p);
+				p += CACHELINE_SIZE;
+				DCFL(p);
+				p += CACHELINE_SIZE;
+				DCFL(p);
+				p += CACHELINE_SIZE;
+				DCFL(p);
+				p += CACHELINE_SIZE;
+				DCFL(p);
+				p += CACHELINE_SIZE;
+				DCFL(p);
+				p += CACHELINE_SIZE;
+				DCFL(p);
+				p += CACHELINE_SIZE;
+			}
+
+			/* flush cacheline */
+			while (p < e) {
+				DCFL(p);
+				p += CACHELINE_SIZE;
+			}
 		}
+
+		/*
+		 * Normally, the `PREREAD' flag instructs us to purge the
+		 * cache for the specified offset and length. However, if
+		 * the offset/length is not aligned to a cacheline boundary,
+		 * we may end up purging some legitimate data from the
+		 * start/end of the cache. In such a case, *flush* the
+		 * cachelines at the start and end of the required region.
+		 */
+		else if (ops & BUS_DMASYNC_PREREAD) {
+			/* flush cacheline on start boundary */
+			if (ps & CACHELINE_MASK) {
+				DCFL(ps & ~CACHELINE_MASK);
+			}
+
+			p = (ps + CACHELINE_MASK) & ~CACHELINE_MASK;
+			e = pe & ~CACHELINE_MASK;
+
+			/* purge cacheline */
+			while ((p < e) && (p & (CACHELINE_SIZE * 8 - 1)) != 0) {
+				DCPL(p);
+				p += CACHELINE_SIZE;
+			}
+
+			/* purge cachelines per 128bytes */
+			while ((p < e) && (p & PAGE_MASK) != 0) {
+				DCPL(p);
+				p += CACHELINE_SIZE;
+				DCPL(p);
+				p += CACHELINE_SIZE;
+				DCPL(p);
+				p += CACHELINE_SIZE;
+				DCPL(p);
+				p += CACHELINE_SIZE;
+				DCPL(p);
+				p += CACHELINE_SIZE;
+				DCPL(p);
+				p += CACHELINE_SIZE;
+				DCPL(p);
+				p += CACHELINE_SIZE;
+				DCPL(p);
+				p += CACHELINE_SIZE;
+			}
+
+			/* purge page */
+			while (p + PAGE_SIZE <= e) {
+				DCPP(p);
+				p += PAGE_SIZE;
+			}
+
+			/* purge cachelines per 128bytes */
+			while (p + CACHELINE_SIZE * 8 <= e) {
+				DCPL(p);
+				p += CACHELINE_SIZE;
+				DCPL(p);
+				p += CACHELINE_SIZE;
+				DCPL(p);
+				p += CACHELINE_SIZE;
+				DCPL(p);
+				p += CACHELINE_SIZE;
+				DCPL(p);
+				p += CACHELINE_SIZE;
+				DCPL(p);
+				p += CACHELINE_SIZE;
+				DCPL(p);
+				p += CACHELINE_SIZE;
+				DCPL(p);
+				p += CACHELINE_SIZE;
+			}
+
+			/* purge cacheline */
+			while (p < e) {
+				DCPL(p);
+				p += CACHELINE_SIZE;
+			}
+
+			/* flush cacheline on end boundary */
+			if (p < pe) {
+				DCFL(p);
+			}
+		}
+		offset = 0;
+		len -= seglen;
 	}
-#endif
+#endif	/* defined(M68040) || defined(M68060) */
 }
 
 /*