diff options
author | David Gwynne <dlg@cvs.openbsd.org> | 2009-04-04 14:07:23 +0000 |
---|---|---|
committer | David Gwynne <dlg@cvs.openbsd.org> | 2009-04-04 14:07:23 +0000 |
commit | bd7040a137fcf34bad612e52527b2c4e9b82b186 (patch) | |
tree | 2fac372eb5aa59fa48d51493def6a4149fc39234 /sys | |
parent | e1a9d2593b31c1b9f444b5a94cce42847df80894 (diff) |
make bus_dmamap_load_raw respect the constraints of the dmamap we're
loading the raw memory into.
similair to the change made to src/sys/arch/amd64/amd6/bus_dma.c 1.18
Diffstat (limited to 'sys')
-rw-r--r-- | sys/arch/i386/i386/bus_dma.c | 73 |
1 files changed, 57 insertions, 16 deletions
diff --git a/sys/arch/i386/i386/bus_dma.c b/sys/arch/i386/i386/bus_dma.c index 70442e62202..ebfa4198da5 100644 --- a/sys/arch/i386/i386/bus_dma.c +++ b/sys/arch/i386/i386/bus_dma.c @@ -1,4 +1,4 @@ -/* $OpenBSD: bus_dma.c,v 1.7 2009/03/30 18:12:24 deraadt Exp $ */ +/* $OpenBSD: bus_dma.c,v 1.8 2009/04/04 14:07:22 dlg Exp $ */ /*- * Copyright (c) 1996, 1997 The NetBSD Foundation, Inc. * All rights reserved. @@ -275,27 +275,68 @@ int _bus_dmamap_load_raw(bus_dma_tag_t t, bus_dmamap_t map, bus_dma_segment_t *segs, int nsegs, bus_size_t size, int flags) { + bus_addr_t paddr, baddr, bmask, lastaddr = 0; + bus_size_t plen, sgsize; + int first = 1; + int i, seg = 0; + if (nsegs > map->_dm_segcnt || size > map->_dm_size) return (EINVAL); - /* - * Make sure we don't cross any boundaries. - */ - if (map->_dm_boundary) { - bus_addr_t bmask = ~(map->_dm_boundary - 1); - int i; - - for (i = 0; i < nsegs; i++) { - if (segs[i].ds_len > map->_dm_maxsegsz) - return (EINVAL); - if ((segs[i].ds_addr & bmask) != - ((segs[i].ds_addr + segs[i].ds_len - 1) & bmask)) - return (EINVAL); + bmask = ~(map->_dm_boundary - 1); + + for (i = 0; i < nsegs; i++) { + paddr = segs[i].ds_addr; + plen = segs[i].ds_len; + + while (plen > 0) { + /* + * Compute the segment size, and adjust counts. + */ + sgsize = PAGE_SIZE - ((u_long)paddr & PGOFSET); + if (plen < sgsize) + sgsize = plen; + + /* + * Make sure we don't cross any boundaries. + */ + if (map->_dm_boundary > 0) { + baddr = (paddr + map->_dm_boundary) & bmask; + if (sgsize > (baddr - paddr)) + sgsize = (baddr - paddr); + } + + /* + * Insert chunk into a segment, coalescing with + * previous segment if possible. + */ + if (first) { + map->dm_segs[seg].ds_addr = paddr; + map->dm_segs[seg].ds_len = sgsize; + first = 0; + } else { + if (paddr == lastaddr && + (map->dm_segs[seg].ds_len + sgsize) <= + map->_dm_maxsegsz && + (map->_dm_boundary == 0 || + (map->dm_segs[seg].ds_addr & bmask) == + (paddr & bmask))) + map->dm_segs[seg].ds_len += sgsize; + else { + if (++seg >= map->_dm_segcnt) + return (EINVAL); + map->dm_segs[seg].ds_addr = paddr; + map->dm_segs[seg].ds_len = sgsize; + } + } + + lastaddr = paddr + sgsize; + paddr += sgsize; + plen -= sgsize; } } - bcopy(segs, map->dm_segs, nsegs * sizeof(*segs)); - map->dm_nsegs = nsegs; + map->dm_nsegs = seg + 1; return (0); } |