1 /*- 2 * Copyright (c) 1997, 1998 Justin T. Gibbs. 3 * Copyright (c) 2015-2016 The FreeBSD Foundation 4 * All rights reserved. 5 * 6 * Portions of this software were developed by Andrew Turner 7 * under sponsorship of the FreeBSD Foundation. 8 * 9 * Portions of this software were developed by Semihalf 10 * under sponsorship of the FreeBSD Foundation. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions, and the following disclaimer, 17 * without modification, immediately at the beginning of the file. 18 * 2. The name of the author may not be used to endorse or promote products 19 * derived from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 25 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #include <sys/cdefs.h> 35 __FBSDID("$FreeBSD$"); 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/malloc.h> 40 #include <sys/bus.h> 41 #include <sys/interrupt.h> 42 #include <sys/kernel.h> 43 #include <sys/ktr.h> 44 #include <sys/lock.h> 45 #include <sys/proc.h> 46 #include <sys/memdesc.h> 47 #include <sys/mutex.h> 48 #include <sys/sysctl.h> 49 #include <sys/uio.h> 50 51 #include <vm/vm.h> 52 #include <vm/vm_extern.h> 53 #include <vm/vm_kern.h> 54 #include <vm/vm_page.h> 55 #include <vm/vm_map.h> 56 57 #include <machine/atomic.h> 58 #include <machine/bus.h> 59 #include <machine/md_var.h> 60 #include <machine/bus_dma_impl.h> 61 62 #define MAX_BPAGES 4096 63 64 enum { 65 BF_COULD_BOUNCE = 0x01, 66 BF_MIN_ALLOC_COMP = 0x02, 67 BF_KMEM_ALLOC = 0x04, 68 BF_COHERENT = 0x10, 69 }; 70 71 struct bounce_page; 72 struct bounce_zone; 73 74 struct bus_dma_tag { 75 struct bus_dma_tag_common common; 76 int map_count; 77 int bounce_flags; 78 bus_dma_segment_t *segments; 79 struct bounce_zone *bounce_zone; 80 }; 81 82 static SYSCTL_NODE(_hw, OID_AUTO, busdma, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, 83 "Busdma parameters"); 84 85 struct sync_list { 86 vm_offset_t vaddr; /* kva of client data */ 87 bus_addr_t paddr; /* physical address */ 88 vm_page_t pages; /* starting page of client data */ 89 bus_size_t datacount; /* client data count */ 90 }; 91 92 struct bus_dmamap { 93 STAILQ_HEAD(, bounce_page) bpages; 94 int pagesneeded; 95 int pagesreserved; 96 bus_dma_tag_t dmat; 97 struct memdesc mem; 98 bus_dmamap_callback_t *callback; 99 void *callback_arg; 100 STAILQ_ENTRY(bus_dmamap) links; 101 u_int flags; 102 #define DMAMAP_COULD_BOUNCE (1 << 0) 103 #define DMAMAP_FROM_DMAMEM (1 << 1) 104 int sync_count; 105 struct sync_list slist[]; 106 }; 107 108 int run_filter(bus_dma_tag_t dmat, bus_addr_t paddr); 109 static void _bus_dmamap_count_pages(bus_dma_tag_t dmat, bus_dmamap_t map, 110 pmap_t pmap, void *buf, bus_size_t buflen, int flags); 111 static void _bus_dmamap_count_phys(bus_dma_tag_t dmat, bus_dmamap_t map, 112 vm_paddr_t buf, bus_size_t buflen, int flags); 113 114 static MALLOC_DEFINE(M_BUSDMA, "busdma", "busdma metadata"); 115 116 #define dmat_alignment(dmat) ((dmat)->common.alignment) 117 #define dmat_flags(dmat) ((dmat)->common.flags) 118 #define dmat_lowaddr(dmat) ((dmat)->common.lowaddr) 119 #define dmat_lockfunc(dmat) ((dmat)->common.lockfunc) 120 #define dmat_lockfuncarg(dmat) ((dmat)->common.lockfuncarg) 121 122 #include "../../kern/subr_busdma_bounce.c" 123 124 /* 125 * Allocate a device specific dma_tag. 126 */ 127 static int 128 bounce_bus_dma_tag_create(bus_dma_tag_t parent, bus_size_t alignment, 129 bus_addr_t boundary, bus_addr_t lowaddr, bus_addr_t highaddr, 130 bus_dma_filter_t *filter, void *filterarg, bus_size_t maxsize, 131 int nsegments, bus_size_t maxsegsz, int flags, bus_dma_lock_t *lockfunc, 132 void *lockfuncarg, bus_dma_tag_t *dmat) 133 { 134 bus_dma_tag_t newtag; 135 int error; 136 137 *dmat = NULL; 138 error = common_bus_dma_tag_create(parent != NULL ? &parent->common : 139 NULL, alignment, boundary, lowaddr, highaddr, filter, filterarg, 140 maxsize, nsegments, maxsegsz, flags, lockfunc, lockfuncarg, 141 sizeof (struct bus_dma_tag), (void **)&newtag); 142 if (error != 0) 143 return (error); 144 145 newtag->common.impl = &bus_dma_bounce_impl; 146 newtag->map_count = 0; 147 newtag->segments = NULL; 148 149 if ((flags & BUS_DMA_COHERENT) != 0) 150 newtag->bounce_flags |= BF_COHERENT; 151 152 if (parent != NULL) { 153 if ((newtag->common.filter != NULL || 154 (parent->bounce_flags & BF_COULD_BOUNCE) != 0)) 155 newtag->bounce_flags |= BF_COULD_BOUNCE; 156 157 /* Copy some flags from the parent */ 158 newtag->bounce_flags |= parent->bounce_flags & BF_COHERENT; 159 } 160 161 if (newtag->common.lowaddr < ptoa((vm_paddr_t)Maxmem) || 162 newtag->common.alignment > 1) 163 newtag->bounce_flags |= BF_COULD_BOUNCE; 164 165 if (((newtag->bounce_flags & BF_COULD_BOUNCE) != 0) && 166 (flags & BUS_DMA_ALLOCNOW) != 0) { 167 struct bounce_zone *bz; 168 169 /* Must bounce */ 170 if ((error = alloc_bounce_zone(newtag)) != 0) { 171 free(newtag, M_DEVBUF); 172 return (error); 173 } 174 bz = newtag->bounce_zone; 175 176 if (ptoa(bz->total_bpages) < maxsize) { 177 int pages; 178 179 pages = atop(round_page(maxsize)) - bz->total_bpages; 180 181 /* Add pages to our bounce pool */ 182 if (alloc_bounce_pages(newtag, pages) < pages) 183 error = ENOMEM; 184 } 185 /* Performed initial allocation */ 186 newtag->bounce_flags |= BF_MIN_ALLOC_COMP; 187 } else 188 error = 0; 189 190 if (error != 0) 191 free(newtag, M_DEVBUF); 192 else 193 *dmat = newtag; 194 CTR4(KTR_BUSDMA, "%s returned tag %p tag flags 0x%x error %d", 195 __func__, newtag, (newtag != NULL ? newtag->common.flags : 0), 196 error); 197 return (error); 198 } 199 200 static int 201 bounce_bus_dma_tag_destroy(bus_dma_tag_t dmat) 202 { 203 #ifdef KTR 204 bus_dma_tag_t dmat_copy = dmat; 205 #endif 206 bus_dma_tag_t parent; 207 int error; 208 209 error = 0; 210 211 if (dmat != NULL) { 212 if (dmat->map_count != 0) { 213 error = EBUSY; 214 goto out; 215 } 216 while (dmat != NULL) { 217 parent = (bus_dma_tag_t)dmat->common.parent; 218 atomic_subtract_int(&dmat->common.ref_count, 1); 219 if (dmat->common.ref_count == 0) { 220 if (dmat->segments != NULL) 221 free(dmat->segments, M_DEVBUF); 222 free(dmat, M_DEVBUF); 223 /* 224 * Last reference count, so 225 * release our reference 226 * count on our parent. 227 */ 228 dmat = parent; 229 } else 230 dmat = NULL; 231 } 232 } 233 out: 234 CTR3(KTR_BUSDMA, "%s tag %p error %d", __func__, dmat_copy, error); 235 return (error); 236 } 237 238 static bus_dmamap_t 239 alloc_dmamap(bus_dma_tag_t dmat, int flags) 240 { 241 u_long mapsize; 242 bus_dmamap_t map; 243 244 mapsize = sizeof(*map); 245 mapsize += sizeof(struct sync_list) * dmat->common.nsegments; 246 map = malloc(mapsize, M_DEVBUF, flags | M_ZERO); 247 if (map == NULL) 248 return (NULL); 249 250 /* Initialize the new map */ 251 STAILQ_INIT(&map->bpages); 252 253 return (map); 254 } 255 256 /* 257 * Allocate a handle for mapping from kva/uva/physical 258 * address space into bus device space. 259 */ 260 static int 261 bounce_bus_dmamap_create(bus_dma_tag_t dmat, int flags, bus_dmamap_t *mapp) 262 { 263 struct bounce_zone *bz; 264 int error, maxpages, pages; 265 266 error = 0; 267 268 if (dmat->segments == NULL) { 269 dmat->segments = (bus_dma_segment_t *)malloc( 270 sizeof(bus_dma_segment_t) * dmat->common.nsegments, 271 M_DEVBUF, M_NOWAIT); 272 if (dmat->segments == NULL) { 273 CTR3(KTR_BUSDMA, "%s: tag %p error %d", 274 __func__, dmat, ENOMEM); 275 return (ENOMEM); 276 } 277 } 278 279 *mapp = alloc_dmamap(dmat, M_NOWAIT); 280 if (*mapp == NULL) { 281 CTR3(KTR_BUSDMA, "%s: tag %p error %d", 282 __func__, dmat, ENOMEM); 283 return (ENOMEM); 284 } 285 286 /* 287 * Bouncing might be required if the driver asks for an active 288 * exclusion region, a data alignment that is stricter than 1, and/or 289 * an active address boundary. 290 */ 291 if (dmat->bounce_flags & BF_COULD_BOUNCE) { 292 /* Must bounce */ 293 if (dmat->bounce_zone == NULL) { 294 if ((error = alloc_bounce_zone(dmat)) != 0) { 295 free(*mapp, M_DEVBUF); 296 return (error); 297 } 298 } 299 bz = dmat->bounce_zone; 300 301 (*mapp)->flags = DMAMAP_COULD_BOUNCE; 302 303 /* 304 * Attempt to add pages to our pool on a per-instance 305 * basis up to a sane limit. 306 */ 307 if (dmat->common.alignment > 1) 308 maxpages = MAX_BPAGES; 309 else 310 maxpages = MIN(MAX_BPAGES, Maxmem - 311 atop(dmat->common.lowaddr)); 312 if ((dmat->bounce_flags & BF_MIN_ALLOC_COMP) == 0 || 313 (bz->map_count > 0 && bz->total_bpages < maxpages)) { 314 pages = MAX(atop(dmat->common.maxsize), 1); 315 pages = MIN(maxpages - bz->total_bpages, pages); 316 pages = MAX(pages, 1); 317 if (alloc_bounce_pages(dmat, pages) < pages) 318 error = ENOMEM; 319 if ((dmat->bounce_flags & BF_MIN_ALLOC_COMP) 320 == 0) { 321 if (error == 0) { 322 dmat->bounce_flags |= 323 BF_MIN_ALLOC_COMP; 324 } 325 } else 326 error = 0; 327 } 328 bz->map_count++; 329 } 330 if (error == 0) 331 dmat->map_count++; 332 else 333 free(*mapp, M_DEVBUF); 334 CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d", 335 __func__, dmat, dmat->common.flags, error); 336 return (error); 337 } 338 339 /* 340 * Destroy a handle for mapping from kva/uva/physical 341 * address space into bus device space. 342 */ 343 static int 344 bounce_bus_dmamap_destroy(bus_dma_tag_t dmat, bus_dmamap_t map) 345 { 346 347 /* Check we are destroying the correct map type */ 348 if ((map->flags & DMAMAP_FROM_DMAMEM) != 0) 349 panic("bounce_bus_dmamap_destroy: Invalid map freed\n"); 350 351 if (STAILQ_FIRST(&map->bpages) != NULL || map->sync_count != 0) { 352 CTR3(KTR_BUSDMA, "%s: tag %p error %d", __func__, dmat, EBUSY); 353 return (EBUSY); 354 } 355 if (dmat->bounce_zone) { 356 KASSERT((map->flags & DMAMAP_COULD_BOUNCE) != 0, 357 ("%s: Bounce zone when cannot bounce", __func__)); 358 dmat->bounce_zone->map_count--; 359 } 360 free(map, M_DEVBUF); 361 dmat->map_count--; 362 CTR2(KTR_BUSDMA, "%s: tag %p error 0", __func__, dmat); 363 return (0); 364 } 365 366 /* 367 * Allocate a piece of memory that can be efficiently mapped into 368 * bus device space based on the constraints lited in the dma tag. 369 * A dmamap to for use with dmamap_load is also allocated. 370 */ 371 static int 372 bounce_bus_dmamem_alloc(bus_dma_tag_t dmat, void** vaddr, int flags, 373 bus_dmamap_t *mapp) 374 { 375 /* 376 * XXX ARM64TODO: 377 * This bus_dma implementation requires IO-Coherent architecutre. 378 * If IO-Coherency is not guaranteed, the BUS_DMA_COHERENT flag has 379 * to be implented using non-cacheable memory. 380 */ 381 382 vm_memattr_t attr; 383 int mflags; 384 385 if (flags & BUS_DMA_NOWAIT) 386 mflags = M_NOWAIT; 387 else 388 mflags = M_WAITOK; 389 390 if (dmat->segments == NULL) { 391 dmat->segments = (bus_dma_segment_t *)malloc( 392 sizeof(bus_dma_segment_t) * dmat->common.nsegments, 393 M_DEVBUF, mflags); 394 if (dmat->segments == NULL) { 395 CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d", 396 __func__, dmat, dmat->common.flags, ENOMEM); 397 return (ENOMEM); 398 } 399 } 400 if (flags & BUS_DMA_ZERO) 401 mflags |= M_ZERO; 402 if (flags & BUS_DMA_NOCACHE) 403 attr = VM_MEMATTR_UNCACHEABLE; 404 else if ((flags & BUS_DMA_COHERENT) != 0 && 405 (dmat->bounce_flags & BF_COHERENT) == 0) 406 /* 407 * If we have a non-coherent tag, and are trying to allocate 408 * a coherent block of memory it needs to be uncached. 409 */ 410 attr = VM_MEMATTR_UNCACHEABLE; 411 else 412 attr = VM_MEMATTR_DEFAULT; 413 414 /* 415 * Create the map, but don't set the could bounce flag as 416 * this allocation should never bounce; 417 */ 418 *mapp = alloc_dmamap(dmat, mflags); 419 if (*mapp == NULL) { 420 CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d", 421 __func__, dmat, dmat->common.flags, ENOMEM); 422 return (ENOMEM); 423 } 424 (*mapp)->flags = DMAMAP_FROM_DMAMEM; 425 426 /* 427 * Allocate the buffer from the malloc(9) allocator if... 428 * - It's small enough to fit into a single power of two sized bucket. 429 * - The alignment is less than or equal to the maximum size 430 * - The low address requirement is fulfilled. 431 * else allocate non-contiguous pages if... 432 * - The page count that could get allocated doesn't exceed 433 * nsegments also when the maximum segment size is less 434 * than PAGE_SIZE. 435 * - The alignment constraint isn't larger than a page boundary. 436 * - There are no boundary-crossing constraints. 437 * else allocate a block of contiguous pages because one or more of the 438 * constraints is something that only the contig allocator can fulfill. 439 * 440 * NOTE: The (dmat->common.alignment <= dmat->maxsize) check 441 * below is just a quick hack. The exact alignment guarantees 442 * of malloc(9) need to be nailed down, and the code below 443 * should be rewritten to take that into account. 444 * 445 * In the meantime warn the user if malloc gets it wrong. 446 */ 447 if ((dmat->common.maxsize <= PAGE_SIZE) && 448 (dmat->common.alignment <= dmat->common.maxsize) && 449 dmat->common.lowaddr >= ptoa((vm_paddr_t)Maxmem) && 450 attr == VM_MEMATTR_DEFAULT) { 451 *vaddr = malloc(dmat->common.maxsize, M_DEVBUF, mflags); 452 } else if (dmat->common.nsegments >= 453 howmany(dmat->common.maxsize, MIN(dmat->common.maxsegsz, PAGE_SIZE)) && 454 dmat->common.alignment <= PAGE_SIZE && 455 (dmat->common.boundary % PAGE_SIZE) == 0) { 456 /* Page-based multi-segment allocations allowed */ 457 *vaddr = kmem_alloc_attr(dmat->common.maxsize, mflags, 458 0ul, dmat->common.lowaddr, attr); 459 dmat->bounce_flags |= BF_KMEM_ALLOC; 460 } else { 461 *vaddr = kmem_alloc_contig(dmat->common.maxsize, mflags, 462 0ul, dmat->common.lowaddr, dmat->common.alignment != 0 ? 463 dmat->common.alignment : 1ul, dmat->common.boundary, attr); 464 dmat->bounce_flags |= BF_KMEM_ALLOC; 465 } 466 if (*vaddr == NULL) { 467 CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d", 468 __func__, dmat, dmat->common.flags, ENOMEM); 469 free(*mapp, M_DEVBUF); 470 return (ENOMEM); 471 } else if (!vm_addr_align_ok(vtophys(*vaddr), dmat->common.alignment)) { 472 printf("bus_dmamem_alloc failed to align memory properly.\n"); 473 } 474 dmat->map_count++; 475 CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d", 476 __func__, dmat, dmat->common.flags, 0); 477 return (0); 478 } 479 480 /* 481 * Free a piece of memory and it's allociated dmamap, that was allocated 482 * via bus_dmamem_alloc. Make the same choice for free/contigfree. 483 */ 484 static void 485 bounce_bus_dmamem_free(bus_dma_tag_t dmat, void *vaddr, bus_dmamap_t map) 486 { 487 488 /* 489 * Check the map came from bounce_bus_dmamem_alloc, so the map 490 * should be NULL and the BF_KMEM_ALLOC flag cleared if malloc() 491 * was used and set if kmem_alloc_contig() was used. 492 */ 493 if ((map->flags & DMAMAP_FROM_DMAMEM) == 0) 494 panic("bus_dmamem_free: Invalid map freed\n"); 495 if ((dmat->bounce_flags & BF_KMEM_ALLOC) == 0) 496 free(vaddr, M_DEVBUF); 497 else 498 kmem_free(vaddr, dmat->common.maxsize); 499 free(map, M_DEVBUF); 500 dmat->map_count--; 501 CTR3(KTR_BUSDMA, "%s: tag %p flags 0x%x", __func__, dmat, 502 dmat->bounce_flags); 503 } 504 505 static void 506 _bus_dmamap_count_phys(bus_dma_tag_t dmat, bus_dmamap_t map, vm_paddr_t buf, 507 bus_size_t buflen, int flags) 508 { 509 bus_addr_t curaddr; 510 bus_size_t sgsize; 511 512 if ((map->flags & DMAMAP_COULD_BOUNCE) != 0 && map->pagesneeded == 0) { 513 /* 514 * Count the number of bounce pages 515 * needed in order to complete this transfer 516 */ 517 curaddr = buf; 518 while (buflen != 0) { 519 sgsize = MIN(buflen, dmat->common.maxsegsz); 520 if (bus_dma_run_filter(&dmat->common, curaddr)) { 521 sgsize = MIN(sgsize, 522 PAGE_SIZE - (curaddr & PAGE_MASK)); 523 map->pagesneeded++; 524 } 525 curaddr += sgsize; 526 buflen -= sgsize; 527 } 528 CTR1(KTR_BUSDMA, "pagesneeded= %d\n", map->pagesneeded); 529 } 530 } 531 532 static void 533 _bus_dmamap_count_pages(bus_dma_tag_t dmat, bus_dmamap_t map, pmap_t pmap, 534 void *buf, bus_size_t buflen, int flags) 535 { 536 vm_offset_t vaddr; 537 vm_offset_t vendaddr; 538 bus_addr_t paddr; 539 bus_size_t sg_len; 540 541 if ((map->flags & DMAMAP_COULD_BOUNCE) != 0 && map->pagesneeded == 0) { 542 CTR4(KTR_BUSDMA, "lowaddr= %d Maxmem= %d, boundary= %d, " 543 "alignment= %d", dmat->common.lowaddr, 544 ptoa((vm_paddr_t)Maxmem), 545 dmat->common.boundary, dmat->common.alignment); 546 CTR2(KTR_BUSDMA, "map= %p, pagesneeded= %d", map, 547 map->pagesneeded); 548 /* 549 * Count the number of bounce pages 550 * needed in order to complete this transfer 551 */ 552 vaddr = (vm_offset_t)buf; 553 vendaddr = (vm_offset_t)buf + buflen; 554 555 while (vaddr < vendaddr) { 556 sg_len = PAGE_SIZE - ((vm_offset_t)vaddr & PAGE_MASK); 557 if (pmap == kernel_pmap) 558 paddr = pmap_kextract(vaddr); 559 else 560 paddr = pmap_extract(pmap, vaddr); 561 if (bus_dma_run_filter(&dmat->common, paddr) != 0) { 562 sg_len = roundup2(sg_len, 563 dmat->common.alignment); 564 map->pagesneeded++; 565 } 566 vaddr += sg_len; 567 } 568 CTR1(KTR_BUSDMA, "pagesneeded= %d\n", map->pagesneeded); 569 } 570 } 571 572 /* 573 * Add a single contiguous physical range to the segment list. 574 */ 575 static bus_size_t 576 _bus_dmamap_addseg(bus_dma_tag_t dmat, bus_dmamap_t map, bus_addr_t curaddr, 577 bus_size_t sgsize, bus_dma_segment_t *segs, int *segp) 578 { 579 int seg; 580 581 /* 582 * Make sure we don't cross any boundaries. 583 */ 584 if (!vm_addr_bound_ok(curaddr, sgsize, dmat->common.boundary)) 585 sgsize = roundup2(curaddr, dmat->common.boundary) - curaddr; 586 587 /* 588 * Insert chunk into a segment, coalescing with 589 * previous segment if possible. 590 */ 591 seg = *segp; 592 if (seg == -1) { 593 seg = 0; 594 segs[seg].ds_addr = curaddr; 595 segs[seg].ds_len = sgsize; 596 } else { 597 if (curaddr == segs[seg].ds_addr + segs[seg].ds_len && 598 (segs[seg].ds_len + sgsize) <= dmat->common.maxsegsz && 599 vm_addr_bound_ok(segs[seg].ds_addr, 600 segs[seg].ds_len + sgsize, dmat->common.boundary)) 601 segs[seg].ds_len += sgsize; 602 else { 603 if (++seg >= dmat->common.nsegments) 604 return (0); 605 segs[seg].ds_addr = curaddr; 606 segs[seg].ds_len = sgsize; 607 } 608 } 609 *segp = seg; 610 return (sgsize); 611 } 612 613 /* 614 * Utility function to load a physical buffer. segp contains 615 * the starting segment on entrace, and the ending segment on exit. 616 */ 617 static int 618 bounce_bus_dmamap_load_phys(bus_dma_tag_t dmat, bus_dmamap_t map, 619 vm_paddr_t buf, bus_size_t buflen, int flags, bus_dma_segment_t *segs, 620 int *segp) 621 { 622 struct sync_list *sl; 623 bus_size_t sgsize; 624 bus_addr_t curaddr, sl_end; 625 int error; 626 627 if (segs == NULL) 628 segs = dmat->segments; 629 630 if ((dmat->bounce_flags & BF_COULD_BOUNCE) != 0) { 631 _bus_dmamap_count_phys(dmat, map, buf, buflen, flags); 632 if (map->pagesneeded != 0) { 633 error = _bus_dmamap_reserve_pages(dmat, map, flags); 634 if (error) 635 return (error); 636 } 637 } 638 639 sl = map->slist + map->sync_count - 1; 640 sl_end = 0; 641 642 while (buflen > 0) { 643 curaddr = buf; 644 sgsize = MIN(buflen, dmat->common.maxsegsz); 645 if (((dmat->bounce_flags & BF_COULD_BOUNCE) != 0) && 646 map->pagesneeded != 0 && 647 bus_dma_run_filter(&dmat->common, curaddr)) { 648 sgsize = MIN(sgsize, PAGE_SIZE - (curaddr & PAGE_MASK)); 649 curaddr = add_bounce_page(dmat, map, 0, curaddr, 650 sgsize); 651 } else if ((dmat->bounce_flags & BF_COHERENT) == 0) { 652 if (map->sync_count > 0) 653 sl_end = sl->paddr + sl->datacount; 654 655 if (map->sync_count == 0 || curaddr != sl_end) { 656 if (++map->sync_count > dmat->common.nsegments) 657 break; 658 sl++; 659 sl->vaddr = 0; 660 sl->paddr = curaddr; 661 sl->datacount = sgsize; 662 sl->pages = PHYS_TO_VM_PAGE(curaddr); 663 KASSERT(sl->pages != NULL, 664 ("%s: page at PA:0x%08lx is not in " 665 "vm_page_array", __func__, curaddr)); 666 } else 667 sl->datacount += sgsize; 668 } 669 sgsize = _bus_dmamap_addseg(dmat, map, curaddr, sgsize, segs, 670 segp); 671 if (sgsize == 0) 672 break; 673 buf += sgsize; 674 buflen -= sgsize; 675 } 676 677 /* 678 * Did we fit? 679 */ 680 return (buflen != 0 ? EFBIG : 0); /* XXX better return value here? */ 681 } 682 683 /* 684 * Utility function to load a linear buffer. segp contains 685 * the starting segment on entrace, and the ending segment on exit. 686 */ 687 static int 688 bounce_bus_dmamap_load_buffer(bus_dma_tag_t dmat, bus_dmamap_t map, void *buf, 689 bus_size_t buflen, pmap_t pmap, int flags, bus_dma_segment_t *segs, 690 int *segp) 691 { 692 struct sync_list *sl; 693 bus_size_t sgsize, max_sgsize; 694 bus_addr_t curaddr, sl_pend; 695 vm_offset_t kvaddr, vaddr, sl_vend; 696 int error; 697 698 if (segs == NULL) 699 segs = dmat->segments; 700 701 if ((dmat->bounce_flags & BF_COULD_BOUNCE) != 0) { 702 _bus_dmamap_count_pages(dmat, map, pmap, buf, buflen, flags); 703 if (map->pagesneeded != 0) { 704 error = _bus_dmamap_reserve_pages(dmat, map, flags); 705 if (error) 706 return (error); 707 } 708 } 709 710 sl = map->slist + map->sync_count - 1; 711 vaddr = (vm_offset_t)buf; 712 sl_pend = 0; 713 sl_vend = 0; 714 715 while (buflen > 0) { 716 /* 717 * Get the physical address for this segment. 718 */ 719 if (pmap == kernel_pmap) { 720 curaddr = pmap_kextract(vaddr); 721 kvaddr = vaddr; 722 } else { 723 curaddr = pmap_extract(pmap, vaddr); 724 kvaddr = 0; 725 } 726 727 /* 728 * Compute the segment size, and adjust counts. 729 */ 730 max_sgsize = MIN(buflen, dmat->common.maxsegsz); 731 sgsize = PAGE_SIZE - (curaddr & PAGE_MASK); 732 if (((dmat->bounce_flags & BF_COULD_BOUNCE) != 0) && 733 map->pagesneeded != 0 && 734 bus_dma_run_filter(&dmat->common, curaddr)) { 735 sgsize = roundup2(sgsize, dmat->common.alignment); 736 sgsize = MIN(sgsize, max_sgsize); 737 curaddr = add_bounce_page(dmat, map, kvaddr, curaddr, 738 sgsize); 739 } else if ((dmat->bounce_flags & BF_COHERENT) == 0) { 740 sgsize = MIN(sgsize, max_sgsize); 741 if (map->sync_count > 0) { 742 sl_pend = sl->paddr + sl->datacount; 743 sl_vend = sl->vaddr + sl->datacount; 744 } 745 746 if (map->sync_count == 0 || 747 (kvaddr != 0 && kvaddr != sl_vend) || 748 (curaddr != sl_pend)) { 749 if (++map->sync_count > dmat->common.nsegments) 750 goto cleanup; 751 sl++; 752 sl->vaddr = kvaddr; 753 sl->paddr = curaddr; 754 if (kvaddr != 0) { 755 sl->pages = NULL; 756 } else { 757 sl->pages = PHYS_TO_VM_PAGE(curaddr); 758 KASSERT(sl->pages != NULL, 759 ("%s: page at PA:0x%08lx is not " 760 "in vm_page_array", __func__, 761 curaddr)); 762 } 763 sl->datacount = sgsize; 764 } else 765 sl->datacount += sgsize; 766 } else { 767 sgsize = MIN(sgsize, max_sgsize); 768 } 769 sgsize = _bus_dmamap_addseg(dmat, map, curaddr, sgsize, segs, 770 segp); 771 if (sgsize == 0) 772 break; 773 vaddr += sgsize; 774 buflen -= sgsize; 775 } 776 777 cleanup: 778 /* 779 * Did we fit? 780 */ 781 return (buflen != 0 ? EFBIG : 0); /* XXX better return value here? */ 782 } 783 784 static void 785 bounce_bus_dmamap_waitok(bus_dma_tag_t dmat, bus_dmamap_t map, 786 struct memdesc *mem, bus_dmamap_callback_t *callback, void *callback_arg) 787 { 788 789 if ((map->flags & DMAMAP_COULD_BOUNCE) == 0) 790 return; 791 map->mem = *mem; 792 map->dmat = dmat; 793 map->callback = callback; 794 map->callback_arg = callback_arg; 795 } 796 797 static bus_dma_segment_t * 798 bounce_bus_dmamap_complete(bus_dma_tag_t dmat, bus_dmamap_t map, 799 bus_dma_segment_t *segs, int nsegs, int error) 800 { 801 802 if (segs == NULL) 803 segs = dmat->segments; 804 return (segs); 805 } 806 807 /* 808 * Release the mapping held by map. 809 */ 810 static void 811 bounce_bus_dmamap_unload(bus_dma_tag_t dmat, bus_dmamap_t map) 812 { 813 free_bounce_pages(dmat, map); 814 map->sync_count = 0; 815 } 816 817 static void 818 dma_preread_safe(vm_offset_t va, vm_size_t size) 819 { 820 /* 821 * Write back any partial cachelines immediately before and 822 * after the DMA region. 823 */ 824 if (va & (dcache_line_size - 1)) 825 cpu_dcache_wb_range(va, 1); 826 if ((va + size) & (dcache_line_size - 1)) 827 cpu_dcache_wb_range(va + size, 1); 828 829 cpu_dcache_inv_range(va, size); 830 } 831 832 static void 833 dma_dcache_sync(struct sync_list *sl, bus_dmasync_op_t op) 834 { 835 uint32_t len, offset; 836 vm_page_t m; 837 vm_paddr_t pa; 838 vm_offset_t va, tempva; 839 bus_size_t size; 840 841 offset = sl->paddr & PAGE_MASK; 842 m = sl->pages; 843 size = sl->datacount; 844 pa = sl->paddr; 845 846 for ( ; size != 0; size -= len, pa += len, offset = 0, ++m) { 847 tempva = 0; 848 if (sl->vaddr == 0) { 849 len = min(PAGE_SIZE - offset, size); 850 tempva = pmap_quick_enter_page(m); 851 va = tempva | offset; 852 KASSERT(pa == (VM_PAGE_TO_PHYS(m) | offset), 853 ("unexpected vm_page_t phys: 0x%16lx != 0x%16lx", 854 VM_PAGE_TO_PHYS(m) | offset, pa)); 855 } else { 856 len = sl->datacount; 857 va = sl->vaddr; 858 } 859 860 switch (op) { 861 case BUS_DMASYNC_PREWRITE: 862 case BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD: 863 cpu_dcache_wb_range(va, len); 864 break; 865 case BUS_DMASYNC_PREREAD: 866 /* 867 * An mbuf may start in the middle of a cacheline. There 868 * will be no cpu writes to the beginning of that line 869 * (which contains the mbuf header) while dma is in 870 * progress. Handle that case by doing a writeback of 871 * just the first cacheline before invalidating the 872 * overall buffer. Any mbuf in a chain may have this 873 * misalignment. Buffers which are not mbufs bounce if 874 * they are not aligned to a cacheline. 875 */ 876 dma_preread_safe(va, len); 877 break; 878 case BUS_DMASYNC_POSTREAD: 879 case BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE: 880 cpu_dcache_inv_range(va, len); 881 break; 882 default: 883 panic("unsupported combination of sync operations: " 884 "0x%08x\n", op); 885 } 886 887 if (tempva != 0) 888 pmap_quick_remove_page(tempva); 889 } 890 } 891 892 static void 893 bounce_bus_dmamap_sync(bus_dma_tag_t dmat, bus_dmamap_t map, 894 bus_dmasync_op_t op) 895 { 896 struct bounce_page *bpage; 897 struct sync_list *sl, *end; 898 vm_offset_t datavaddr, tempvaddr; 899 900 if (op == BUS_DMASYNC_POSTWRITE) 901 return; 902 903 if ((op & BUS_DMASYNC_POSTREAD) != 0) { 904 /* 905 * Wait for any DMA operations to complete before the bcopy. 906 */ 907 fence(); 908 } 909 910 if ((bpage = STAILQ_FIRST(&map->bpages)) != NULL) { 911 CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x op 0x%x " 912 "performing bounce", __func__, dmat, dmat->common.flags, 913 op); 914 915 if ((op & BUS_DMASYNC_PREWRITE) != 0) { 916 while (bpage != NULL) { 917 tempvaddr = 0; 918 datavaddr = bpage->datavaddr; 919 if (datavaddr == 0) { 920 tempvaddr = pmap_quick_enter_page( 921 bpage->datapage); 922 datavaddr = tempvaddr | bpage->dataoffs; 923 } 924 925 bcopy((void *)datavaddr, 926 (void *)bpage->vaddr, bpage->datacount); 927 if (tempvaddr != 0) 928 pmap_quick_remove_page(tempvaddr); 929 if ((dmat->bounce_flags & BF_COHERENT) == 0) 930 cpu_dcache_wb_range(bpage->vaddr, 931 bpage->datacount); 932 bpage = STAILQ_NEXT(bpage, links); 933 } 934 dmat->bounce_zone->total_bounced++; 935 } else if ((op & BUS_DMASYNC_PREREAD) != 0) { 936 while (bpage != NULL) { 937 if ((dmat->bounce_flags & BF_COHERENT) == 0) 938 cpu_dcache_wbinv_range(bpage->vaddr, 939 bpage->datacount); 940 bpage = STAILQ_NEXT(bpage, links); 941 } 942 } 943 944 if ((op & BUS_DMASYNC_POSTREAD) != 0) { 945 while (bpage != NULL) { 946 if ((dmat->bounce_flags & BF_COHERENT) == 0) 947 cpu_dcache_inv_range(bpage->vaddr, 948 bpage->datacount); 949 tempvaddr = 0; 950 datavaddr = bpage->datavaddr; 951 if (datavaddr == 0) { 952 tempvaddr = pmap_quick_enter_page( 953 bpage->datapage); 954 datavaddr = tempvaddr | bpage->dataoffs; 955 } 956 957 bcopy((void *)bpage->vaddr, 958 (void *)datavaddr, bpage->datacount); 959 960 if (tempvaddr != 0) 961 pmap_quick_remove_page(tempvaddr); 962 bpage = STAILQ_NEXT(bpage, links); 963 } 964 dmat->bounce_zone->total_bounced++; 965 } 966 } 967 968 /* 969 * Cache maintenance for normal (non-COHERENT non-bounce) buffers. 970 */ 971 if (map->sync_count != 0) { 972 sl = &map->slist[0]; 973 end = &map->slist[map->sync_count]; 974 CTR3(KTR_BUSDMA, "%s: tag %p op 0x%x " 975 "performing sync", __func__, dmat, op); 976 977 for ( ; sl != end; ++sl) 978 dma_dcache_sync(sl, op); 979 } 980 981 if ((op & (BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE)) != 0) { 982 /* 983 * Wait for the bcopy to complete before any DMA operations. 984 */ 985 fence(); 986 } 987 } 988 989 struct bus_dma_impl bus_dma_bounce_impl = { 990 .tag_create = bounce_bus_dma_tag_create, 991 .tag_destroy = bounce_bus_dma_tag_destroy, 992 .map_create = bounce_bus_dmamap_create, 993 .map_destroy = bounce_bus_dmamap_destroy, 994 .mem_alloc = bounce_bus_dmamem_alloc, 995 .mem_free = bounce_bus_dmamem_free, 996 .load_phys = bounce_bus_dmamap_load_phys, 997 .load_buffer = bounce_bus_dmamap_load_buffer, 998 .load_ma = bus_dmamap_load_ma_triv, 999 .map_waitok = bounce_bus_dmamap_waitok, 1000 .map_complete = bounce_bus_dmamap_complete, 1001 .map_unload = bounce_bus_dmamap_unload, 1002 .map_sync = bounce_bus_dmamap_sync 1003 }; 1004