diff options
Diffstat (limited to 'sys/kern')
-rw-r--r-- | sys/kern/subr_bus_dma.c | 69 | ||||
-rw-r--r-- | sys/kern/subr_uio.c | 54 |
2 files changed, 108 insertions, 15 deletions
diff --git a/sys/kern/subr_bus_dma.c b/sys/kern/subr_bus_dma.c index a16d8c8..ae30276 100644 --- a/sys/kern/subr_bus_dma.c +++ b/sys/kern/subr_bus_dma.c @@ -54,19 +54,32 @@ __FBSDID("$FreeBSD$"); #include <machine/bus.h> /* - * Load a list of virtual addresses. + * Load up data starting at offset within a region specified by a + * list of virtual address ranges until either length or the region + * are exhausted. */ static int _bus_dmamap_load_vlist(bus_dma_tag_t dmat, bus_dmamap_t map, bus_dma_segment_t *list, int sglist_cnt, struct pmap *pmap, int *nsegs, - int flags) + int flags, size_t offset, size_t length) { int error; error = 0; - for (; sglist_cnt > 0; sglist_cnt--, list++) { - error = _bus_dmamap_load_buffer(dmat, map, - (void *)(uintptr_t)list->ds_addr, list->ds_len, pmap, + for (; sglist_cnt > 0 && length != 0; sglist_cnt--, list++) { + char *addr; + size_t ds_len; + + KASSERT((offset < list->ds_len), + ("Invalid mid-segment offset")); + addr = (char *)(uintptr_t)list->ds_addr + offset; + ds_len = list->ds_len - offset; + offset = 0; + if (ds_len > length) + ds_len = length; + length -= ds_len; + KASSERT((ds_len != 0), ("Segment length is zero")); + error = _bus_dmamap_load_buffer(dmat, map, addr, ds_len, pmap, flags, NULL, nsegs); if (error) break; @@ -118,22 +131,48 @@ _bus_dmamap_load_mbuf_sg(bus_dma_tag_t dmat, bus_dmamap_t map, } /* + * Load tlen data starting at offset within a region specified by a list of + * physical pages. + */ +static int +_bus_dmamap_load_pages(bus_dma_tag_t dmat, bus_dmamap_t map, + vm_page_t *pages, bus_size_t tlen, int offset, int *nsegs, int flags) +{ + vm_paddr_t paddr; + bus_size_t len; + int error, i; + + for (i = 0, error = 0; error == 0 && tlen > 0; i++, tlen -= len) { + len = min(PAGE_SIZE - offset, tlen); + paddr = VM_PAGE_TO_PHYS(pages[i]) + offset; + error = _bus_dmamap_load_phys(dmat, map, paddr, len, + flags, NULL, nsegs); + offset = 0; + } + return (error); +} + +/* * Load from block io. */ static int _bus_dmamap_load_bio(bus_dma_tag_t dmat, bus_dmamap_t map, struct bio *bio, int *nsegs, int flags) { - int error; - if ((bio->bio_flags & BIO_UNMAPPED) == 0) { - error = _bus_dmamap_load_buffer(dmat, map, bio->bio_data, - bio->bio_bcount, kernel_pmap, flags, NULL, nsegs); - } else { - error = _bus_dmamap_load_ma(dmat, map, bio->bio_ma, - bio->bio_bcount, bio->bio_ma_offset, flags, NULL, nsegs); + if ((bio->bio_flags & BIO_VLIST) != 0) { + bus_dma_segment_t *segs = (bus_dma_segment_t *)bio->bio_data; + return (_bus_dmamap_load_vlist(dmat, map, segs, bio->bio_ma_n, + kernel_pmap, nsegs, flags, bio->bio_ma_offset, + bio->bio_bcount)); } - return (error); + + if ((bio->bio_flags & BIO_UNMAPPED) != 0) + return (_bus_dmamap_load_pages(dmat, map, bio->bio_ma, + bio->bio_bcount, bio->bio_ma_offset, nsegs, flags)); + + return (_bus_dmamap_load_buffer(dmat, map, bio->bio_data, + bio->bio_bcount, kernel_pmap, flags, NULL, nsegs)); } int @@ -219,7 +258,7 @@ _bus_dmamap_load_ccb(bus_dma_tag_t dmat, bus_dmamap_t map, union ccb *ccb, case CAM_DATA_SG: error = _bus_dmamap_load_vlist(dmat, map, (bus_dma_segment_t *)data_ptr, sglist_cnt, kernel_pmap, - nsegs, flags); + nsegs, flags, 0, dxfer_len); break; case CAM_DATA_SG_PADDR: error = _bus_dmamap_load_plist(dmat, map, @@ -494,7 +533,7 @@ bus_dmamap_load_mem(bus_dma_tag_t dmat, bus_dmamap_t map, break; case MEMDESC_VLIST: error = _bus_dmamap_load_vlist(dmat, map, mem->u.md_list, - mem->md_opaque, kernel_pmap, &nsegs, flags); + mem->md_opaque, kernel_pmap, &nsegs, flags, 0, SIZE_T_MAX); break; case MEMDESC_PLIST: error = _bus_dmamap_load_plist(dmat, map, mem->u.md_list, diff --git a/sys/kern/subr_uio.c b/sys/kern/subr_uio.c index 87892fd..3712f92 100644 --- a/sys/kern/subr_uio.c +++ b/sys/kern/subr_uio.c @@ -62,6 +62,8 @@ __FBSDID("$FreeBSD$"); #include <vm/vm_pageout.h> #include <vm/vm_map.h> +#include <machine/bus.h> + SYSCTL_INT(_kern, KERN_IOV_MAX, iov_max, CTLFLAG_RD, SYSCTL_NULL_INT_PTR, UIO_MAXIOV, "Maximum number of elements in an I/O vector; sysconf(_SC_IOV_MAX)"); @@ -136,6 +138,58 @@ physcopyout(vm_paddr_t src, void *dst, size_t len) #undef PHYS_PAGE_COUNT int +physcopyin_vlist(bus_dma_segment_t *src, off_t offset, vm_paddr_t dst, + size_t len) +{ + size_t seg_len; + int error; + + error = 0; + while (offset >= src->ds_len) { + offset -= src->ds_len; + src++; + } + + while (len > 0 && error == 0) { + seg_len = MIN(src->ds_len - offset, len); + error = physcopyin((void *)(uintptr_t)(src->ds_addr + offset), + dst, seg_len); + offset = 0; + src++; + len -= seg_len; + dst += seg_len; + } + + return (error); +} + +int +physcopyout_vlist(vm_paddr_t src, bus_dma_segment_t *dst, off_t offset, + size_t len) +{ + size_t seg_len; + int error; + + error = 0; + while (offset >= dst->ds_len) { + offset -= dst->ds_len; + dst++; + } + + while (len > 0 && error == 0) { + seg_len = MIN(dst->ds_len - offset, len); + error = physcopyout(src, (void *)(uintptr_t)(dst->ds_addr + + offset), seg_len); + offset = 0; + dst++; + len -= seg_len; + src += seg_len; + } + + return (error); +} + +int uiomove(void *cp, int n, struct uio *uio) { |