1 /* $OpenBSD: viommu.c,v 1.19 2017/05/25 03:19:39 dlg Exp $ */ 2 /* $NetBSD: iommu.c,v 1.47 2002/02/08 20:03:45 eeh Exp $ */ 3 4 /* 5 * Coptright (c) 2008 Mark Kettenis 6 * Copyright (c) 2003 Henric Jungheim 7 * Copyright (c) 2001, 2002 Eduardo Horvath 8 * Copyright (c) 1999, 2000 Matthew R. Green 9 * All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. The name of the author may not be used to endorse or promote products 20 * derived from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 /* 36 * UltraSPARC Hypervisor IOMMU support. 37 */ 38 39 #include <sys/param.h> 40 #include <sys/extent.h> 41 #include <sys/malloc.h> 42 #include <sys/systm.h> 43 #include <sys/proc.h> 44 #include <sys/device.h> 45 #include <sys/mbuf.h> 46 47 #include <uvm/uvm_extern.h> 48 49 #include <machine/bus.h> 50 #include <sparc64/sparc64/cache.h> 51 #include <sparc64/dev/iommureg.h> 52 #include <sparc64/dev/iommuvar.h> 53 #include <sparc64/dev/viommuvar.h> 54 55 #include <machine/autoconf.h> 56 #include <machine/cpu.h> 57 #include <machine/hypervisor.h> 58 59 #ifdef DDB 60 #include <machine/db_machdep.h> 61 #include <ddb/db_sym.h> 62 #include <ddb/db_extern.h> 63 #endif 64 65 #ifdef DEBUG 66 #define IDB_BUSDMA 0x1 67 #define IDB_IOMMU 0x2 68 #define IDB_INFO 0x4 69 #define IDB_SYNC 0x8 70 #define IDB_XXX 0x10 71 #define IDB_PRINT_MAP 0x20 72 #define IDB_BREAK 0x40 73 extern int iommudebug; 74 #define DPRINTF(l, s) do { if (iommudebug & l) printf s; } while (0) 75 #else 76 #define DPRINTF(l, s) 77 #endif 78 79 void viommu_enter(struct iommu_state *, struct strbuf_ctl *, bus_addr_t, 80 paddr_t, int); 81 void viommu_remove(struct iommu_state *, struct strbuf_ctl *, bus_addr_t); 82 int viommu_dvmamap_load_seg(bus_dma_tag_t, struct iommu_state *, 83 bus_dmamap_t, bus_dma_segment_t *, int, int, bus_size_t, bus_size_t); 84 int viommu_dvmamap_load_mlist(bus_dma_tag_t, struct iommu_state *, 85 bus_dmamap_t, struct pglist *, int, bus_size_t, bus_size_t); 86 int viommu_dvmamap_append_range(bus_dma_tag_t, bus_dmamap_t, paddr_t, 87 bus_size_t, int, bus_size_t); 88 int iommu_iomap_insert_page(struct iommu_map_state *, paddr_t); 89 bus_addr_t iommu_iomap_translate(struct iommu_map_state *, paddr_t); 90 void viommu_iomap_load_map(struct iommu_state *, struct iommu_map_state *, 91 bus_addr_t, int); 92 void viommu_iomap_unload_map(struct iommu_state *, struct iommu_map_state *); 93 struct iommu_map_state *viommu_iomap_create(int); 94 void iommu_iomap_destroy(struct iommu_map_state *); 95 void iommu_iomap_clear_pages(struct iommu_map_state *); 96 void _viommu_dvmamap_sync(bus_dma_tag_t, bus_dma_tag_t, bus_dmamap_t, 97 bus_addr_t, bus_size_t, int); 98 99 /* 100 * initialise the UltraSPARC IOMMU (Hypervisior): 101 * - allocate and setup the iotsb. 102 * - enable the IOMMU 103 * - create a private DVMA map. 104 */ 105 void 106 viommu_init(char *name, struct iommu_state *is, int tsbsize, 107 u_int32_t iovabase) 108 { 109 struct vm_page *m; 110 struct pglist mlist; 111 112 /* 113 * Setup the iommu. 114 * 115 * The sun4v iommu is accessed through the hypervisor so we will 116 * deal with it here.. 117 */ 118 is->is_tsbsize = tsbsize; 119 if (iovabase == (u_int32_t)-1) { 120 is->is_dvmabase = IOTSB_VSTART(is->is_tsbsize); 121 is->is_dvmaend = IOTSB_VEND; 122 } else { 123 is->is_dvmabase = iovabase; 124 is->is_dvmaend = iovabase + IOTSB_VSIZE(tsbsize) - 1; 125 } 126 127 TAILQ_INIT(&mlist); 128 if (uvm_pglistalloc(PAGE_SIZE, 0, -1, PAGE_SIZE, 0, &mlist, 1, 129 UVM_PLA_NOWAIT | UVM_PLA_ZERO) != 0) 130 panic("%s: no memory", __func__); 131 m = TAILQ_FIRST(&mlist); 132 is->is_scratch = VM_PAGE_TO_PHYS(m); 133 134 /* 135 * Allocate a dvma map. 136 */ 137 printf("dvma map %x-%x", is->is_dvmabase, is->is_dvmaend); 138 is->is_dvmamap = extent_create(name, 139 is->is_dvmabase, (u_long)is->is_dvmaend + 1, 140 M_DEVBUF, NULL, 0, EX_NOCOALESCE); 141 mtx_init(&is->is_mtx, IPL_HIGH); 142 143 printf("\n"); 144 } 145 146 /* 147 * Add an entry to the IOMMU table. 148 */ 149 void 150 viommu_enter(struct iommu_state *is, struct strbuf_ctl *sb, bus_addr_t va, 151 paddr_t pa, int flags) 152 { 153 u_int64_t tsbid = IOTSBSLOT(va, is->is_tsbsize); 154 paddr_t page_list[1], addr; 155 u_int64_t attr, nmapped; 156 int err; 157 158 KASSERT(sb == NULL); 159 160 #ifdef DIAGNOSTIC 161 if (va < is->is_dvmabase || (va + PAGE_MASK) > is->is_dvmaend) 162 panic("viommu_enter: va %#lx not in DVMA space", va); 163 #endif 164 165 attr = PCI_MAP_ATTR_READ | PCI_MAP_ATTR_WRITE; 166 if (flags & BUS_DMA_READ) 167 attr &= ~PCI_MAP_ATTR_READ; 168 if (flags & BUS_DMA_WRITE) 169 attr &= ~PCI_MAP_ATTR_WRITE; 170 171 page_list[0] = trunc_page(pa); 172 if (!pmap_extract(pmap_kernel(), (vaddr_t)page_list, &addr)) 173 panic("viommu_enter: pmap_extract failed"); 174 err = hv_pci_iommu_map(is->is_devhandle, tsbid, 1, attr, 175 addr, &nmapped); 176 if (err != H_EOK || nmapped != 1) 177 panic("hv_pci_iommu_map: err=%d", err); 178 } 179 180 /* 181 * Remove an entry from the IOMMU table. 182 */ 183 void 184 viommu_remove(struct iommu_state *is, struct strbuf_ctl *sb, bus_addr_t va) 185 { 186 u_int64_t tsbid = IOTSBSLOT(va, is->is_tsbsize); 187 u_int64_t ndemapped; 188 int err; 189 190 KASSERT(sb == NULL); 191 192 #ifdef DIAGNOSTIC 193 if (va < is->is_dvmabase || (va + PAGE_MASK) > is->is_dvmaend) 194 panic("iommu_remove: va 0x%lx not in DVMA space", (u_long)va); 195 if (va != trunc_page(va)) { 196 printf("iommu_remove: unaligned va: %lx\n", va); 197 va = trunc_page(va); 198 } 199 #endif 200 201 err = hv_pci_iommu_demap(is->is_devhandle, tsbid, 1, &ndemapped); 202 if (err != H_EOK || ndemapped != 1) 203 panic("hv_pci_iommu_unmap: err=%d", err); 204 } 205 206 /* 207 * IOMMU DVMA operations, sun4v hypervisor version. 208 */ 209 210 #define BUS_DMA_FIND_PARENT(t, fn) \ 211 if (t->_parent == NULL) \ 212 panic("null bus_dma parent (" #fn ")"); \ 213 for (t = t->_parent; t->fn == NULL; t = t->_parent) \ 214 if (t->_parent == NULL) \ 215 panic("no bus_dma " #fn " located"); 216 217 int 218 viommu_dvmamap_create(bus_dma_tag_t t, bus_dma_tag_t t0, 219 struct iommu_state *is, bus_size_t size, int nsegments, 220 bus_size_t maxsegsz, bus_size_t boundary, int flags, 221 bus_dmamap_t *dmamap) 222 { 223 int ret; 224 bus_dmamap_t map; 225 struct iommu_map_state *ims; 226 227 BUS_DMA_FIND_PARENT(t, _dmamap_create); 228 ret = (*t->_dmamap_create)(t, t0, size, nsegments, maxsegsz, boundary, 229 flags, &map); 230 231 if (ret) 232 return (ret); 233 234 ims = viommu_iomap_create(atop(round_page(size))); 235 236 if (ims == NULL) { 237 bus_dmamap_destroy(t0, map); 238 return (ENOMEM); 239 } 240 241 ims->ims_iommu = is; 242 map->_dm_cookie = ims; 243 244 *dmamap = map; 245 246 return (0); 247 } 248 249 void 250 viommu_dvmamap_destroy(bus_dma_tag_t t, bus_dma_tag_t t0, bus_dmamap_t map) 251 { 252 /* 253 * The specification (man page) requires a loaded 254 * map to be unloaded before it is destroyed. 255 */ 256 if (map->dm_nsegs) 257 bus_dmamap_unload(t0, map); 258 259 if (map->_dm_cookie) 260 iommu_iomap_destroy(map->_dm_cookie); 261 map->_dm_cookie = NULL; 262 263 BUS_DMA_FIND_PARENT(t, _dmamap_destroy); 264 (*t->_dmamap_destroy)(t, t0, map); 265 } 266 267 /* 268 * Load a contiguous kva buffer into a dmamap. The physical pages are 269 * not assumed to be contiguous. Two passes are made through the buffer 270 * and both call pmap_extract() for the same va->pa translations. It 271 * is possible to run out of pa->dvma mappings; the code should be smart 272 * enough to resize the iomap (when the "flags" permit allocation). It 273 * is trivial to compute the number of entries required (round the length 274 * up to the page size and then divide by the page size)... 275 */ 276 int 277 viommu_dvmamap_load(bus_dma_tag_t t, bus_dma_tag_t t0, bus_dmamap_t map, 278 void *buf, bus_size_t buflen, struct proc *p, int flags) 279 { 280 int err = 0; 281 bus_size_t sgsize; 282 u_long dvmaddr, sgstart, sgend; 283 bus_size_t align, boundary; 284 struct iommu_state *is; 285 struct iommu_map_state *ims = map->_dm_cookie; 286 pmap_t pmap; 287 288 #ifdef DIAGNOSTIC 289 if (ims == NULL) 290 panic("viommu_dvmamap_load: null map state"); 291 if (ims->ims_iommu == NULL) 292 panic("viommu_dvmamap_load: null iommu"); 293 #endif 294 is = ims->ims_iommu; 295 296 if (map->dm_nsegs) { 297 /* 298 * Is it still in use? _bus_dmamap_load should have taken care 299 * of this. 300 */ 301 #ifdef DIAGNOSTIC 302 panic("iommu_dvmamap_load: map still in use"); 303 #endif 304 bus_dmamap_unload(t0, map); 305 } 306 307 /* 308 * Make sure that on error condition we return "no valid mappings". 309 */ 310 map->dm_nsegs = 0; 311 312 if (buflen < 1 || buflen > map->_dm_size) { 313 DPRINTF(IDB_BUSDMA, 314 ("iommu_dvmamap_load(): error %d > %d -- " 315 "map size exceeded!\n", (int)buflen, (int)map->_dm_size)); 316 return (EINVAL); 317 } 318 319 /* 320 * A boundary presented to bus_dmamem_alloc() takes precedence 321 * over boundary in the map. 322 */ 323 if ((boundary = (map->dm_segs[0]._ds_boundary)) == 0) 324 boundary = map->_dm_boundary; 325 align = MAX(map->dm_segs[0]._ds_align, PAGE_SIZE); 326 327 pmap = p ? p->p_vmspace->vm_map.pmap : pmap_kernel(); 328 329 /* Count up the total number of pages we need */ 330 iommu_iomap_clear_pages(ims); 331 { /* Scope */ 332 bus_addr_t a, aend; 333 bus_addr_t addr = (bus_addr_t)buf; 334 int seg_len = buflen; 335 336 aend = round_page(addr + seg_len); 337 for (a = trunc_page(addr); a < aend; a += PAGE_SIZE) { 338 paddr_t pa; 339 340 if (pmap_extract(pmap, a, &pa) == FALSE) 341 panic("iomap pmap error addr 0x%lx\n", a); 342 343 err = iommu_iomap_insert_page(ims, pa); 344 if (err) { 345 printf("iomap insert error: %d for " 346 "va 0x%lx pa 0x%lx " 347 "(buf %p len %ld/%lx)\n", 348 err, a, pa, buf, buflen, buflen); 349 iommu_iomap_clear_pages(ims); 350 return (EFBIG); 351 } 352 } 353 } 354 if (flags & BUS_DMA_OVERRUN) { 355 err = iommu_iomap_insert_page(ims, is->is_scratch); 356 if (err) { 357 iommu_iomap_clear_pages(ims); 358 return (EFBIG); 359 } 360 } 361 sgsize = ims->ims_map.ipm_pagecnt * PAGE_SIZE; 362 363 mtx_enter(&is->is_mtx); 364 if (flags & BUS_DMA_24BIT) { 365 sgstart = MAX(is->is_dvmamap->ex_start, 0xff000000); 366 sgend = MIN(is->is_dvmamap->ex_end, 0xffffffff); 367 } else { 368 sgstart = is->is_dvmamap->ex_start; 369 sgend = is->is_dvmamap->ex_end; 370 } 371 372 /* 373 * If our segment size is larger than the boundary we need to 374 * split the transfer up into little pieces ourselves. 375 */ 376 err = extent_alloc_subregion_with_descr(is->is_dvmamap, sgstart, sgend, 377 sgsize, align, 0, (sgsize > boundary) ? 0 : boundary, 378 EX_NOWAIT | EX_BOUNDZERO, &ims->ims_er, (u_long *)&dvmaddr); 379 mtx_leave(&is->is_mtx); 380 381 #ifdef DEBUG 382 if (err || (dvmaddr == (bus_addr_t)-1)) { 383 printf("iommu_dvmamap_load(): extent_alloc(%d, %x) failed!\n", 384 (int)sgsize, flags); 385 #ifdef DDB 386 if (iommudebug & IDB_BREAK) 387 db_enter(); 388 #endif 389 } 390 #endif 391 if (err != 0) { 392 iommu_iomap_clear_pages(ims); 393 return (err); 394 } 395 396 /* Set the active DVMA map */ 397 map->_dm_dvmastart = dvmaddr; 398 map->_dm_dvmasize = sgsize; 399 400 map->dm_mapsize = buflen; 401 402 viommu_iomap_load_map(is, ims, dvmaddr, flags); 403 404 { /* Scope */ 405 bus_addr_t a, aend; 406 bus_addr_t addr = (bus_addr_t)buf; 407 int seg_len = buflen; 408 409 aend = round_page(addr + seg_len); 410 for (a = trunc_page(addr); a < aend; a += PAGE_SIZE) { 411 bus_addr_t pgstart; 412 bus_addr_t pgend; 413 paddr_t pa; 414 int pglen; 415 416 /* Yuck... Redoing the same pmap_extract... */ 417 if (pmap_extract(pmap, a, &pa) == FALSE) 418 panic("iomap pmap error addr 0x%lx\n", a); 419 420 pgstart = pa | (MAX(a, addr) & PAGE_MASK); 421 pgend = pa | (MIN(a + PAGE_SIZE - 1, 422 addr + seg_len - 1) & PAGE_MASK); 423 pglen = pgend - pgstart + 1; 424 425 if (pglen < 1) 426 continue; 427 428 err = viommu_dvmamap_append_range(t, map, pgstart, 429 pglen, flags, boundary); 430 if (err == EFBIG) 431 break; 432 else if (err) { 433 printf("iomap load seg page: %d for " 434 "va 0x%lx pa %lx (%lx - %lx) " 435 "for %d/0x%x\n", 436 err, a, pa, pgstart, pgend, pglen, pglen); 437 break; 438 } 439 } 440 } 441 442 if (err) 443 viommu_dvmamap_unload(t, t0, map); 444 445 return (err); 446 } 447 448 /* 449 * Load a dvmamap from an array of segs or an mlist (if the first 450 * "segs" entry's mlist is non-null). It calls iommu_dvmamap_load_segs() 451 * or iommu_dvmamap_load_mlist() for part of the 2nd pass through the 452 * mapping. This is ugly. A better solution would probably be to have 453 * function pointers for implementing the traversal. That way, there 454 * could be one core load routine for each of the three required algorithms 455 * (buffer, seg, and mlist). That would also mean that the traversal 456 * algorithm would then only need one implementation for each algorithm 457 * instead of two (one for populating the iomap and one for populating 458 * the dvma map). 459 */ 460 int 461 viommu_dvmamap_load_raw(bus_dma_tag_t t, bus_dma_tag_t t0, bus_dmamap_t map, 462 bus_dma_segment_t *segs, int nsegs, bus_size_t size, int flags) 463 { 464 int i; 465 int left; 466 int err = 0; 467 bus_size_t sgsize; 468 bus_size_t boundary, align; 469 u_long dvmaddr, sgstart, sgend; 470 struct iommu_state *is; 471 struct iommu_map_state *ims = map->_dm_cookie; 472 473 #ifdef DIAGNOSTIC 474 if (ims == NULL) 475 panic("viommu_dvmamap_load_raw: null map state"); 476 if (ims->ims_iommu == NULL) 477 panic("viommu_dvmamap_load_raw: null iommu"); 478 #endif 479 is = ims->ims_iommu; 480 481 if (map->dm_nsegs) { 482 /* Already in use?? */ 483 #ifdef DIAGNOSTIC 484 panic("iommu_dvmamap_load_raw: map still in use"); 485 #endif 486 bus_dmamap_unload(t0, map); 487 } 488 489 /* 490 * A boundary presented to bus_dmamem_alloc() takes precedence 491 * over boundary in the map. 492 */ 493 if ((boundary = segs[0]._ds_boundary) == 0) 494 boundary = map->_dm_boundary; 495 496 align = MAX(segs[0]._ds_align, PAGE_SIZE); 497 498 /* 499 * Make sure that on error condition we return "no valid mappings". 500 */ 501 map->dm_nsegs = 0; 502 503 iommu_iomap_clear_pages(ims); 504 if (segs[0]._ds_mlist) { 505 struct pglist *mlist = segs[0]._ds_mlist; 506 struct vm_page *m; 507 for (m = TAILQ_FIRST(mlist); m != NULL; 508 m = TAILQ_NEXT(m,pageq)) { 509 err = iommu_iomap_insert_page(ims, VM_PAGE_TO_PHYS(m)); 510 511 if(err) { 512 printf("iomap insert error: %d for " 513 "pa 0x%lx\n", err, VM_PAGE_TO_PHYS(m)); 514 iommu_iomap_clear_pages(ims); 515 return (EFBIG); 516 } 517 } 518 } else { 519 /* Count up the total number of pages we need */ 520 for (i = 0, left = size; left > 0 && i < nsegs; i++) { 521 bus_addr_t a, aend; 522 bus_size_t len = segs[i].ds_len; 523 bus_addr_t addr = segs[i].ds_addr; 524 int seg_len = MIN(left, len); 525 526 if (len < 1) 527 continue; 528 529 aend = round_page(addr + seg_len); 530 for (a = trunc_page(addr); a < aend; a += PAGE_SIZE) { 531 532 err = iommu_iomap_insert_page(ims, a); 533 if (err) { 534 printf("iomap insert error: %d for " 535 "pa 0x%lx\n", err, a); 536 iommu_iomap_clear_pages(ims); 537 return (EFBIG); 538 } 539 } 540 541 left -= seg_len; 542 } 543 } 544 if (flags & BUS_DMA_OVERRUN) { 545 err = iommu_iomap_insert_page(ims, is->is_scratch); 546 if (err) { 547 iommu_iomap_clear_pages(ims); 548 return (EFBIG); 549 } 550 } 551 sgsize = ims->ims_map.ipm_pagecnt * PAGE_SIZE; 552 553 mtx_enter(&is->is_mtx); 554 if (flags & BUS_DMA_24BIT) { 555 sgstart = MAX(is->is_dvmamap->ex_start, 0xff000000); 556 sgend = MIN(is->is_dvmamap->ex_end, 0xffffffff); 557 } else { 558 sgstart = is->is_dvmamap->ex_start; 559 sgend = is->is_dvmamap->ex_end; 560 } 561 562 /* 563 * If our segment size is larger than the boundary we need to 564 * split the transfer up into little pieces ourselves. 565 */ 566 err = extent_alloc_subregion_with_descr(is->is_dvmamap, sgstart, sgend, 567 sgsize, align, 0, (sgsize > boundary) ? 0 : boundary, 568 EX_NOWAIT | EX_BOUNDZERO, &ims->ims_er, (u_long *)&dvmaddr); 569 mtx_leave(&is->is_mtx); 570 571 if (err != 0) { 572 iommu_iomap_clear_pages(ims); 573 return (err); 574 } 575 576 #ifdef DEBUG 577 if (dvmaddr == (bus_addr_t)-1) { 578 printf("iommu_dvmamap_load_raw(): extent_alloc(%d, %x) " 579 "failed!\n", (int)sgsize, flags); 580 #ifdef DDB 581 if (iommudebug & IDB_BREAK) 582 db_enter(); 583 #else 584 panic(""); 585 #endif 586 } 587 #endif 588 589 /* Set the active DVMA map */ 590 map->_dm_dvmastart = dvmaddr; 591 map->_dm_dvmasize = sgsize; 592 593 map->dm_mapsize = size; 594 595 viommu_iomap_load_map(is, ims, dvmaddr, flags); 596 597 if (segs[0]._ds_mlist) 598 err = viommu_dvmamap_load_mlist(t, is, map, segs[0]._ds_mlist, 599 flags, size, boundary); 600 else 601 err = viommu_dvmamap_load_seg(t, is, map, segs, nsegs, 602 flags, size, boundary); 603 604 if (err) 605 viommu_dvmamap_unload(t, t0, map); 606 607 return (err); 608 } 609 610 /* 611 * Insert a range of addresses into a loaded map respecting the specified 612 * boundary and alignment restrictions. The range is specified by its 613 * physical address and length. The range cannot cross a page boundary. 614 * This code (along with most of the rest of the function in this file) 615 * assumes that the IOMMU page size is equal to PAGE_SIZE. 616 */ 617 int 618 viommu_dvmamap_append_range(bus_dma_tag_t t, bus_dmamap_t map, paddr_t pa, 619 bus_size_t length, int flags, bus_size_t boundary) 620 { 621 struct iommu_map_state *ims = map->_dm_cookie; 622 bus_addr_t sgstart, sgend, bd_mask; 623 bus_dma_segment_t *seg = NULL; 624 int i = map->dm_nsegs; 625 626 #ifdef DEBUG 627 if (ims == NULL) 628 panic("iommu_dvmamap_append_range: null map state"); 629 #endif 630 631 sgstart = iommu_iomap_translate(ims, pa); 632 sgend = sgstart + length - 1; 633 634 #ifdef DIAGNOSTIC 635 if (sgstart == 0 || sgstart > sgend) { 636 printf("append range invalid mapping for %lx " 637 "(0x%lx - 0x%lx)\n", pa, sgstart, sgend); 638 map->dm_nsegs = 0; 639 return (EINVAL); 640 } 641 #endif 642 643 #ifdef DEBUG 644 if (trunc_page(sgstart) != trunc_page(sgend)) { 645 printf("append range crossing page boundary! " 646 "pa %lx length %ld/0x%lx sgstart %lx sgend %lx\n", 647 pa, length, length, sgstart, sgend); 648 } 649 #endif 650 651 /* 652 * We will attempt to merge this range with the previous entry 653 * (if there is one). 654 */ 655 if (i > 0) { 656 seg = &map->dm_segs[i - 1]; 657 if (sgstart == seg->ds_addr + seg->ds_len) { 658 length += seg->ds_len; 659 sgstart = seg->ds_addr; 660 sgend = sgstart + length - 1; 661 } else 662 seg = NULL; 663 } 664 665 if (seg == NULL) { 666 seg = &map->dm_segs[i]; 667 if (++i > map->_dm_segcnt) { 668 map->dm_nsegs = 0; 669 return (EFBIG); 670 } 671 } 672 673 /* 674 * At this point, "i" is the index of the *next* bus_dma_segment_t 675 * (the segment count, aka map->dm_nsegs) and "seg" points to the 676 * *current* entry. "length", "sgstart", and "sgend" reflect what 677 * we intend to put in "*seg". No assumptions should be made about 678 * the contents of "*seg". Only "boundary" issue can change this 679 * and "boundary" is often zero, so explicitly test for that case 680 * (the test is strictly an optimization). 681 */ 682 if (boundary != 0) { 683 bd_mask = ~(boundary - 1); 684 685 while ((sgstart & bd_mask) != (sgend & bd_mask)) { 686 /* 687 * We are crossing a boundary so fill in the current 688 * segment with as much as possible, then grab a new 689 * one. 690 */ 691 692 seg->ds_addr = sgstart; 693 seg->ds_len = boundary - (sgstart & ~bd_mask); 694 695 sgstart += seg->ds_len; /* sgend stays the same */ 696 length -= seg->ds_len; 697 698 seg = &map->dm_segs[i]; 699 if (++i > map->_dm_segcnt) { 700 map->dm_nsegs = 0; 701 return (EFBIG); 702 } 703 } 704 } 705 706 seg->ds_addr = sgstart; 707 seg->ds_len = length; 708 map->dm_nsegs = i; 709 710 return (0); 711 } 712 713 /* 714 * Populate the iomap from a bus_dma_segment_t array. See note for 715 * iommu_dvmamap_load() * regarding page entry exhaustion of the iomap. 716 * This is less of a problem for load_seg, as the number of pages 717 * is usually similar to the number of segments (nsegs). 718 */ 719 int 720 viommu_dvmamap_load_seg(bus_dma_tag_t t, struct iommu_state *is, 721 bus_dmamap_t map, bus_dma_segment_t *segs, int nsegs, int flags, 722 bus_size_t size, bus_size_t boundary) 723 { 724 int i; 725 int left; 726 int seg; 727 728 /* 729 * This segs is made up of individual physical 730 * segments, probably by _bus_dmamap_load_uio() or 731 * _bus_dmamap_load_mbuf(). Ignore the mlist and 732 * load each one individually. 733 */ 734 735 /* 736 * Keep in mind that each segment could span 737 * multiple pages and that these are not always 738 * adjacent. The code is no longer adding dvma 739 * aliases to the IOMMU. The STC will not cross 740 * page boundaries anyway and a IOMMU table walk 741 * vs. what may be a streamed PCI DMA to a ring 742 * descriptor is probably a wash. It eases TLB 743 * pressure and in the worst possible case, it is 744 * only as bad a non-IOMMUed architecture. More 745 * importantly, the code is not quite as hairy. 746 * (It's bad enough as it is.) 747 */ 748 left = size; 749 seg = 0; 750 for (i = 0; left > 0 && i < nsegs; i++) { 751 bus_addr_t a, aend; 752 bus_size_t len = segs[i].ds_len; 753 bus_addr_t addr = segs[i].ds_addr; 754 int seg_len = MIN(left, len); 755 756 if (len < 1) 757 continue; 758 759 aend = round_page(addr + seg_len); 760 for (a = trunc_page(addr); a < aend; a += PAGE_SIZE) { 761 bus_addr_t pgstart; 762 bus_addr_t pgend; 763 int pglen; 764 int err; 765 766 pgstart = MAX(a, addr); 767 pgend = MIN(a + PAGE_SIZE - 1, addr + seg_len - 1); 768 pglen = pgend - pgstart + 1; 769 770 if (pglen < 1) 771 continue; 772 773 err = viommu_dvmamap_append_range(t, map, pgstart, 774 pglen, flags, boundary); 775 if (err == EFBIG) 776 return (err); 777 if (err) { 778 printf("iomap load seg page: %d for " 779 "pa 0x%lx (%lx - %lx for %d/%x\n", 780 err, a, pgstart, pgend, pglen, pglen); 781 return (err); 782 } 783 784 } 785 786 left -= seg_len; 787 } 788 return (0); 789 } 790 791 /* 792 * Populate the iomap from an mlist. See note for iommu_dvmamap_load() 793 * regarding page entry exhaustion of the iomap. 794 */ 795 int 796 viommu_dvmamap_load_mlist(bus_dma_tag_t t, struct iommu_state *is, 797 bus_dmamap_t map, struct pglist *mlist, int flags, 798 bus_size_t size, bus_size_t boundary) 799 { 800 struct vm_page *m; 801 paddr_t pa; 802 int err; 803 804 /* 805 * This was allocated with bus_dmamem_alloc. 806 * The pages are on an `mlist'. 807 */ 808 for (m = TAILQ_FIRST(mlist); m != NULL; m = TAILQ_NEXT(m,pageq)) { 809 pa = VM_PAGE_TO_PHYS(m); 810 811 err = viommu_dvmamap_append_range(t, map, pa, 812 MIN(PAGE_SIZE, size), flags, boundary); 813 if (err == EFBIG) 814 return (err); 815 if (err) { 816 printf("iomap load seg page: %d for pa 0x%lx " 817 "(%lx - %lx for %d/%x\n", err, pa, pa, 818 pa + PAGE_SIZE, PAGE_SIZE, PAGE_SIZE); 819 return (err); 820 } 821 if (size < PAGE_SIZE) 822 break; 823 size -= PAGE_SIZE; 824 } 825 826 return (0); 827 } 828 829 /* 830 * Unload a dvmamap. 831 */ 832 void 833 viommu_dvmamap_unload(bus_dma_tag_t t, bus_dma_tag_t t0, bus_dmamap_t map) 834 { 835 struct iommu_state *is; 836 struct iommu_map_state *ims = map->_dm_cookie; 837 bus_addr_t dvmaddr = map->_dm_dvmastart; 838 bus_size_t sgsize = map->_dm_dvmasize; 839 int error; 840 841 #ifdef DEBUG 842 if (ims == NULL) 843 panic("viommu_dvmamap_unload: null map state"); 844 if (ims->ims_iommu == NULL) 845 panic("viommu_dvmamap_unload: null iommu"); 846 #endif /* DEBUG */ 847 848 is = ims->ims_iommu; 849 850 /* Remove the IOMMU entries */ 851 viommu_iomap_unload_map(is, ims); 852 853 /* Clear the iomap */ 854 iommu_iomap_clear_pages(ims); 855 856 bus_dmamap_unload(t->_parent, map); 857 858 /* Mark the mappings as invalid. */ 859 map->dm_mapsize = 0; 860 map->dm_nsegs = 0; 861 862 mtx_enter(&is->is_mtx); 863 error = extent_free(is->is_dvmamap, dvmaddr, sgsize, EX_NOWAIT); 864 map->_dm_dvmastart = 0; 865 map->_dm_dvmasize = 0; 866 mtx_leave(&is->is_mtx); 867 if (error != 0) 868 printf("warning: %ld of DVMA space lost\n", sgsize); 869 } 870 871 void 872 viommu_dvmamap_sync(bus_dma_tag_t t, bus_dma_tag_t t0, bus_dmamap_t map, 873 bus_addr_t offset, bus_size_t len, int ops) 874 { 875 #ifdef DIAGNOSTIC 876 struct iommu_map_state *ims = map->_dm_cookie; 877 878 if (ims == NULL) 879 panic("viommu_dvmamap_sync: null map state"); 880 if (ims->ims_iommu == NULL) 881 panic("viommu_dvmamap_sync: null iommu"); 882 #endif 883 if (len == 0) 884 return; 885 886 if (ops & BUS_DMASYNC_PREWRITE) 887 __membar("#MemIssue"); 888 889 #if 0 890 if (ops & (BUS_DMASYNC_POSTREAD | BUS_DMASYNC_PREWRITE)) 891 _viommu_dvmamap_sync(t, t0, map, offset, len, ops); 892 #endif 893 894 if (ops & BUS_DMASYNC_POSTREAD) 895 __membar("#MemIssue"); 896 } 897 898 int 899 viommu_dvmamem_alloc(bus_dma_tag_t t, bus_dma_tag_t t0, bus_size_t size, 900 bus_size_t alignment, bus_size_t boundary, bus_dma_segment_t *segs, 901 int nsegs, int *rsegs, int flags) 902 { 903 904 DPRINTF(IDB_BUSDMA, ("iommu_dvmamem_alloc: sz %llx align %llx " 905 "bound %llx segp %p flags %d\n", (unsigned long long)size, 906 (unsigned long long)alignment, (unsigned long long)boundary, 907 segs, flags)); 908 BUS_DMA_FIND_PARENT(t, _dmamem_alloc); 909 return ((*t->_dmamem_alloc)(t, t0, size, alignment, boundary, 910 segs, nsegs, rsegs, flags | BUS_DMA_DVMA)); 911 } 912 913 void 914 viommu_dvmamem_free(bus_dma_tag_t t, bus_dma_tag_t t0, bus_dma_segment_t *segs, 915 int nsegs) 916 { 917 918 DPRINTF(IDB_BUSDMA, ("iommu_dvmamem_free: segp %p nsegs %d\n", 919 segs, nsegs)); 920 BUS_DMA_FIND_PARENT(t, _dmamem_free); 921 (*t->_dmamem_free)(t, t0, segs, nsegs); 922 } 923 924 /* 925 * Create a new iomap. 926 */ 927 struct iommu_map_state * 928 viommu_iomap_create(int n) 929 { 930 struct iommu_map_state *ims; 931 932 /* Safety for heavily fragmented data, such as mbufs */ 933 n += 4; 934 if (n < 16) 935 n = 16; 936 937 ims = malloc(sizeof(*ims) + (n - 1) * sizeof(ims->ims_map.ipm_map[0]), 938 M_DEVBUF, M_NOWAIT | M_ZERO); 939 if (ims == NULL) 940 return (NULL); 941 942 /* Initialize the map. */ 943 ims->ims_map.ipm_maxpage = n; 944 SPLAY_INIT(&ims->ims_map.ipm_tree); 945 946 return (ims); 947 } 948 949 /* 950 * Locate the iomap by filling in the pa->va mapping and inserting it 951 * into the IOMMU tables. 952 */ 953 void 954 viommu_iomap_load_map(struct iommu_state *is, struct iommu_map_state *ims, 955 bus_addr_t vmaddr, int flags) 956 { 957 struct iommu_page_map *ipm = &ims->ims_map; 958 struct iommu_page_entry *e; 959 int i; 960 961 for (i = 0, e = ipm->ipm_map; i < ipm->ipm_pagecnt; ++i, ++e) { 962 e->ipe_va = vmaddr; 963 viommu_enter(is, NULL, e->ipe_va, e->ipe_pa, flags); 964 vmaddr += PAGE_SIZE; 965 } 966 } 967 968 /* 969 * Remove the iomap from the IOMMU. 970 */ 971 void 972 viommu_iomap_unload_map(struct iommu_state *is, struct iommu_map_state *ims) 973 { 974 struct iommu_page_map *ipm = &ims->ims_map; 975 struct iommu_page_entry *e; 976 int i; 977 978 for (i = 0, e = ipm->ipm_map; i < ipm->ipm_pagecnt; ++i, ++e) 979 viommu_remove(is, NULL, e->ipe_va); 980 } 981