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.
This commit is contained in:
parent
5b76dcbd29
commit
f5f6a58b85
|
@ -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.
|
* Copyright (c) 1998 The NetBSD Foundation, Inc.
|
||||||
|
@ -33,7 +33,7 @@
|
||||||
#include "opt_m68k_arch.h"
|
#include "opt_m68k_arch.h"
|
||||||
|
|
||||||
#include <sys/cdefs.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/param.h>
|
||||||
#include <sys/systm.h>
|
#include <sys/systm.h>
|
||||||
|
@ -548,46 +548,203 @@ _bus_dmamap_unload(bus_dma_tag_t t, bus_dmamap_t map)
|
||||||
* by bus-specific DMA map synchronization functions.
|
* by bus-specific DMA map synchronization functions.
|
||||||
*/
|
*/
|
||||||
void
|
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)
|
bus_size_t len, int ops)
|
||||||
{
|
{
|
||||||
#if defined(M68040) || defined(M68060)
|
#if defined(M68040) || defined(M68060)
|
||||||
int i, pa_off, inc, seglen;
|
bus_addr_t p, e, ps, pe;
|
||||||
u_long pa, end_pa;
|
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 */
|
#if defined(M68040) || defined(M68060)
|
||||||
inc = (len > 1024) ? PAGE_SIZE : 16;
|
/* 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++) {
|
/* Short-circuit for unsupported `ops' */
|
||||||
if (map->dm_segs[i].ds_len <= off) {
|
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 */
|
/* Segment irrelevant - before requested offset */
|
||||||
off -= map->dm_segs[i].ds_len;
|
offset -= seg->ds_len;
|
||||||
continue;
|
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)
|
if (seglen > len)
|
||||||
seglen = len;
|
seglen = len;
|
||||||
len -= seglen;
|
|
||||||
pa = map->dm_segs[i].ds_addr + off - pa_off;
|
|
||||||
end_pa = pa + seglen;
|
|
||||||
|
|
||||||
if (inc == 16) {
|
ps = seg->ds_addr + offset;
|
||||||
pa &= ~15;
|
pe = ps + seglen;
|
||||||
while (pa < end_pa) {
|
|
||||||
DCFL(pa);
|
if (ops & BUS_DMASYNC_PREWRITE) {
|
||||||
pa += 16;
|
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;
|
/* flush cachelines per 128bytes */
|
||||||
while (pa < end_pa) {
|
while ((p < e) && (p & PAGE_MASK) != 0) {
|
||||||
DCFP(pa);
|
DCFL(p);
|
||||||
pa += PAGE_SIZE;
|
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);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
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 /* defined(M68040) || defined(M68060) */
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Reference in New Issue