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