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