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