1 /* $NetBSD: iommu.c,v 1.48 2002/02/21 02:42:27 eeh Exp $ */ 2 3 /* 4 * Copyright (c) 2001, 2002 Eduardo Horvath 5 * Copyright (c) 1999, 2000 Matthew R. Green 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. The name of the author may not be used to endorse or promote products 17 * derived from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 /* 33 * UltraSPARC IOMMU support; used by both the sbus and pci code. 34 */ 35 #include "opt_ddb.h" 36 37 #include <sys/param.h> 38 #include <sys/extent.h> 39 #include <sys/malloc.h> 40 #include <sys/systm.h> 41 #include <sys/device.h> 42 #include <sys/proc.h> 43 44 #include <uvm/uvm_extern.h> 45 46 #include <machine/bus.h> 47 #include <sparc64/sparc64/cache.h> 48 #include <sparc64/dev/iommureg.h> 49 #include <sparc64/dev/iommuvar.h> 50 51 #include <machine/autoconf.h> 52 #include <machine/cpu.h> 53 54 #ifdef DEBUG 55 #define IDB_BUSDMA 0x1 56 #define IDB_IOMMU 0x2 57 #define IDB_INFO 0x4 58 #define IDB_SYNC 0x8 59 int iommudebug = 0x0; 60 #define DPRINTF(l, s) do { if (iommudebug & l) printf s; } while (0) 61 #else 62 #define DPRINTF(l, s) 63 #endif 64 65 #define iommu_strbuf_flush(i,v) do { \ 66 if ((i)->is_sb[0]) \ 67 bus_space_write_8((i)->is_bustag, \ 68 (bus_space_handle_t)(u_long) \ 69 &(i)->is_sb[0]->strbuf_pgflush, \ 70 0, (v)); \ 71 if ((i)->is_sb[1]) \ 72 bus_space_write_8((i)->is_bustag, \ 73 (bus_space_handle_t)(u_long) \ 74 &(i)->is_sb[1]->strbuf_pgflush, \ 75 0, (v)); \ 76 } while (0) 77 78 static int iommu_strbuf_flush_done __P((struct iommu_state *)); 79 80 /* 81 * initialise the UltraSPARC IOMMU (SBUS or PCI): 82 * - allocate and setup the iotsb. 83 * - enable the IOMMU 84 * - initialise the streaming buffers (if they exist) 85 * - create a private DVMA map. 86 */ 87 void 88 iommu_init(name, is, tsbsize, iovabase) 89 char *name; 90 struct iommu_state *is; 91 int tsbsize; 92 u_int32_t iovabase; 93 { 94 psize_t size; 95 vaddr_t va; 96 paddr_t pa; 97 struct vm_page *m; 98 struct pglist mlist; 99 100 /* 101 * Setup the iommu. 102 * 103 * The sun4u iommu is part of the SBUS or PCI controller so we will 104 * deal with it here.. 105 * 106 * For sysio and psycho/psycho+ the IOMMU address space always ends at 107 * 0xffffe000, but the starting address depends on the size of the 108 * map. The map size is 1024 * 2 ^ is->is_tsbsize entries, where each 109 * entry is 8 bytes. The start of the map can be calculated by 110 * (0xffffe000 << (8 + is->is_tsbsize)). 111 * 112 * But sabre and hummingbird use a different scheme that seems to 113 * be hard-wired, so we read the start and size from the PROM and 114 * just use those values. 115 */ 116 is->is_cr = (tsbsize << 16) | IOMMUCR_EN; 117 is->is_tsbsize = tsbsize; 118 if (iovabase == -1) { 119 is->is_dvmabase = IOTSB_VSTART(is->is_tsbsize); 120 is->is_dvmaend = IOTSB_VEND; 121 } else { 122 is->is_dvmabase = iovabase; 123 is->is_dvmaend = iovabase + IOTSB_VSIZE(tsbsize); 124 } 125 126 /* 127 * Allocate memory for I/O pagetables. They need to be physically 128 * contiguous. 129 */ 130 131 size = NBPG<<(is->is_tsbsize); 132 TAILQ_INIT(&mlist); 133 if (uvm_pglistalloc((psize_t)size, (paddr_t)0, (paddr_t)-1, 134 (paddr_t)NBPG, (paddr_t)0, &mlist, 1, 0) != 0) 135 panic("iommu_init: no memory"); 136 137 va = uvm_km_valloc(kernel_map, size); 138 if (va == 0) 139 panic("iommu_init: no memory"); 140 is->is_tsb = (int64_t *)va; 141 142 m = TAILQ_FIRST(&mlist); 143 is->is_ptsb = VM_PAGE_TO_PHYS(m); 144 145 /* Map the pages */ 146 for (; m != NULL; m = TAILQ_NEXT(m,pageq)) { 147 pa = VM_PAGE_TO_PHYS(m); 148 pmap_enter(pmap_kernel(), va, pa | PMAP_NVC, 149 VM_PROT_READ|VM_PROT_WRITE, 150 VM_PROT_READ|VM_PROT_WRITE|PMAP_WIRED); 151 va += NBPG; 152 } 153 pmap_update(pmap_kernel()); 154 bzero(is->is_tsb, size); 155 156 #ifdef DEBUG 157 if (iommudebug & IDB_INFO) 158 { 159 /* Probe the iommu */ 160 struct iommureg *regs = is->is_iommu; 161 162 printf("iommu regs at: cr=%lx tsb=%lx flush=%lx\n", 163 (u_long)®s->iommu_cr, 164 (u_long)®s->iommu_tsb, 165 (u_long)®s->iommu_flush); 166 printf("iommu cr=%llx tsb=%llx\n", (unsigned long long)regs->iommu_cr, (unsigned long long)regs->iommu_tsb); 167 printf("TSB base %p phys %llx\n", (void *)is->is_tsb, (unsigned long long)is->is_ptsb); 168 delay(1000000); /* 1 s */ 169 } 170 #endif 171 172 /* 173 * Initialize streaming buffer, if it is there. 174 */ 175 if (is->is_sb[0] || is->is_sb[1]) 176 (void)pmap_extract(pmap_kernel(), (vaddr_t)&is->is_flush[0], 177 (paddr_t *)&is->is_flushpa); 178 179 /* 180 * now actually start up the IOMMU 181 */ 182 iommu_reset(is); 183 184 /* 185 * Now all the hardware's working we need to allocate a dvma map. 186 */ 187 printf("DVMA map: %x to %x\n", 188 (unsigned int)is->is_dvmabase, 189 (unsigned int)is->is_dvmaend); 190 printf("IOTSB: %llx to %llx\n", 191 (unsigned long long)is->is_ptsb, 192 (unsigned long long)(is->is_ptsb + size)); 193 is->is_dvmamap = extent_create(name, 194 is->is_dvmabase, is->is_dvmaend - NBPG, 195 M_DEVBUF, 0, 0, EX_NOWAIT); 196 } 197 198 /* 199 * Streaming buffers don't exist on the UltraSPARC IIi; we should have 200 * detected that already and disabled them. If not, we will notice that 201 * they aren't there when the STRBUF_EN bit does not remain. 202 */ 203 void 204 iommu_reset(is) 205 struct iommu_state *is; 206 { 207 struct iommu_strbuf *sb; 208 int i; 209 210 /* Need to do 64-bit stores */ 211 bus_space_write_8(is->is_bustag, 212 (bus_space_handle_t)(u_long)&is->is_iommu->iommu_tsb, 213 0, is->is_ptsb); 214 /* Enable IOMMU in diagnostic mode */ 215 bus_space_write_8(is->is_bustag, 216 (bus_space_handle_t)(u_long)&is->is_iommu->iommu_cr, 217 0, is->is_cr|IOMMUCR_DE); 218 219 for (i=0; i<2; i++) { 220 if ((sb = is->is_sb[i]) != NULL) { 221 222 /* Enable diagnostics mode? */ 223 bus_space_write_8(is->is_bustag, 224 (bus_space_handle_t)(u_long)&sb->strbuf_ctl, 225 0, STRBUF_EN); 226 227 /* No streaming buffers? Disable them */ 228 if (bus_space_read_8(is->is_bustag, 229 (bus_space_handle_t)(u_long)&sb->strbuf_ctl, 230 0) == 0) 231 is->is_sb[i] = 0; 232 } 233 } 234 } 235 236 /* 237 * Here are the iommu control routines. 238 */ 239 void 240 iommu_enter(is, va, pa, flags) 241 struct iommu_state *is; 242 vaddr_t va; 243 int64_t pa; 244 int flags; 245 { 246 int64_t tte; 247 248 #ifdef DIAGNOSTIC 249 if (va < is->is_dvmabase || va > is->is_dvmaend) 250 panic("iommu_enter: va %#lx not in DVMA space", va); 251 #endif 252 253 tte = MAKEIOTTE(pa, !(flags&BUS_DMA_NOWRITE), !(flags&BUS_DMA_NOCACHE), 254 (flags&BUS_DMA_STREAMING)); 255 tte |= (flags & 0xff000LL)<<(4*8);/* DEBUG */ 256 257 /* Is the streamcache flush really needed? */ 258 if (is->is_sb[0] || is->is_sb[1]) { 259 iommu_strbuf_flush(is, va); 260 iommu_strbuf_flush_done(is); 261 } 262 DPRINTF(IDB_IOMMU, ("Clearing TSB slot %d for va %p\n", 263 (int)IOTSBSLOT(va,is->is_tsbsize), (void *)(u_long)va)); 264 is->is_tsb[IOTSBSLOT(va,is->is_tsbsize)] = tte; 265 bus_space_write_8(is->is_bustag, (bus_space_handle_t)(u_long) 266 &is->is_iommu->iommu_flush, 0, va); 267 DPRINTF(IDB_IOMMU, ("iommu_enter: va %lx pa %lx TSB[%lx]@%p=%lx\n", 268 va, (long)pa, (u_long)IOTSBSLOT(va,is->is_tsbsize), 269 (void *)(u_long)&is->is_tsb[IOTSBSLOT(va,is->is_tsbsize)], 270 (u_long)tte)); 271 } 272 273 274 /* 275 * Find the value of a DVMA address (debug routine). 276 */ 277 paddr_t 278 iommu_extract(is, dva) 279 struct iommu_state *is; 280 vaddr_t dva; 281 { 282 int64_t tte = 0; 283 284 if (dva >= is->is_dvmabase && dva < is->is_dvmaend) 285 tte = is->is_tsb[IOTSBSLOT(dva,is->is_tsbsize)]; 286 287 if ((tte&IOTTE_V) == 0) 288 return ((paddr_t)-1L); 289 return (tte&IOTTE_PAMASK); 290 } 291 292 /* 293 * iommu_remove: removes mappings created by iommu_enter 294 * 295 * Only demap from IOMMU if flag is set. 296 * 297 * XXX: this function needs better internal error checking. 298 */ 299 void 300 iommu_remove(is, va, len) 301 struct iommu_state *is; 302 vaddr_t va; 303 size_t len; 304 { 305 306 #ifdef DIAGNOSTIC 307 if (va < is->is_dvmabase || va > is->is_dvmaend) 308 panic("iommu_remove: va 0x%lx not in DVMA space", (u_long)va); 309 if ((long)(va + len) < (long)va) 310 panic("iommu_remove: va 0x%lx + len 0x%lx wraps", 311 (long) va, (long) len); 312 if (len & ~0xfffffff) 313 panic("iommu_remove: rediculous len 0x%lx", (u_long)len); 314 #endif 315 316 va = trunc_page(va); 317 DPRINTF(IDB_IOMMU, ("iommu_remove: va %lx TSB[%lx]@%p\n", 318 va, (u_long)IOTSBSLOT(va,is->is_tsbsize), 319 &is->is_tsb[IOTSBSLOT(va,is->is_tsbsize)])); 320 while (len > 0) { 321 DPRINTF(IDB_IOMMU, ("iommu_remove: clearing TSB slot %d for va %p size %lx\n", 322 (int)IOTSBSLOT(va,is->is_tsbsize), (void *)(u_long)va, (u_long)len)); 323 if (is->is_sb[0] || is->is_sb[0]) { 324 DPRINTF(IDB_IOMMU, ("iommu_remove: flushing va %p TSB[%lx]@%p=%lx, %lu bytes left\n", 325 (void *)(u_long)va, (long)IOTSBSLOT(va,is->is_tsbsize), 326 (void *)(u_long)&is->is_tsb[IOTSBSLOT(va,is->is_tsbsize)], 327 (long)(is->is_tsb[IOTSBSLOT(va,is->is_tsbsize)]), 328 (u_long)len)); 329 iommu_strbuf_flush(is, va); 330 if (len <= NBPG) 331 iommu_strbuf_flush_done(is); 332 DPRINTF(IDB_IOMMU, ("iommu_remove: flushed va %p TSB[%lx]@%p=%lx, %lu bytes left\n", 333 (void *)(u_long)va, (long)IOTSBSLOT(va,is->is_tsbsize), 334 (void *)(u_long)&is->is_tsb[IOTSBSLOT(va,is->is_tsbsize)], 335 (long)(is->is_tsb[IOTSBSLOT(va,is->is_tsbsize)]), 336 (u_long)len)); 337 } 338 339 if (len <= NBPG) 340 len = 0; 341 else 342 len -= NBPG; 343 344 /* XXX Zero-ing the entry would not require RMW */ 345 is->is_tsb[IOTSBSLOT(va,is->is_tsbsize)] &= ~IOTTE_V; 346 bus_space_write_8(is->is_bustag, (bus_space_handle_t)(u_long) 347 &is->is_iommu->iommu_flush, 0, va); 348 va += NBPG; 349 } 350 } 351 352 static int 353 iommu_strbuf_flush_done(is) 354 struct iommu_state *is; 355 { 356 struct timeval cur, flushtimeout; 357 358 #define BUMPTIME(t, usec) { \ 359 register volatile struct timeval *tp = (t); \ 360 register long us; \ 361 \ 362 tp->tv_usec = us = tp->tv_usec + (usec); \ 363 if (us >= 1000000) { \ 364 tp->tv_usec = us - 1000000; \ 365 tp->tv_sec++; \ 366 } \ 367 } 368 369 if (!is->is_sb[0] && !is->is_sb[1]) 370 return (0); 371 372 /* 373 * Streaming buffer flushes: 374 * 375 * 1 Tell strbuf to flush by storing va to strbuf_pgflush. If 376 * we're not on a cache line boundary (64-bits): 377 * 2 Store 0 in flag 378 * 3 Store pointer to flag in flushsync 379 * 4 wait till flushsync becomes 0x1 380 * 381 * If it takes more than .5 sec, something 382 * went wrong. 383 */ 384 385 is->is_flush[0] = 1; 386 is->is_flush[1] = 1; 387 if (is->is_sb[0]) { 388 is->is_flush[0] = 0; 389 bus_space_write_8(is->is_bustag, (bus_space_handle_t)(u_long) 390 &is->is_sb[0]->strbuf_flushsync, 0, is->is_flushpa); 391 } 392 if (is->is_sb[1]) { 393 is->is_flush[0] = 1; 394 bus_space_write_8(is->is_bustag, (bus_space_handle_t)(u_long) 395 &is->is_sb[1]->strbuf_flushsync, 0, is->is_flushpa + 8); 396 } 397 398 microtime(&flushtimeout); 399 cur = flushtimeout; 400 BUMPTIME(&flushtimeout, 500000); /* 1/2 sec */ 401 402 DPRINTF(IDB_IOMMU, ("iommu_strbuf_flush_done: flush = %lx,%lx " 403 "at va = %lx pa = %lx now=%lx:%lx until = %lx:%lx\n", 404 (long)is->is_flush[0], (long)is->is_flush[1], 405 (long)&is->is_flush[0], (long)is->is_flushpa, 406 cur.tv_sec, cur.tv_usec, 407 flushtimeout.tv_sec, flushtimeout.tv_usec)); 408 409 /* Bypass non-coherent D$ */ 410 while ((!ldxa(is->is_flushpa, ASI_PHYS_CACHED) || 411 !ldxa(is->is_flushpa + 8, ASI_PHYS_CACHED)) && 412 ((cur.tv_sec <= flushtimeout.tv_sec) && 413 (cur.tv_usec <= flushtimeout.tv_usec))) 414 microtime(&cur); 415 416 #ifdef DIAGNOSTIC 417 if (!ldxa(is->is_flushpa, ASI_PHYS_CACHED) || 418 !ldxa(is->is_flushpa + 8, ASI_PHYS_CACHED)) { 419 printf("iommu_strbuf_flush_done: flush timeout %p,%p at %p\n", 420 (void *)(u_long)is->is_flush[0], 421 (void *)(u_long)is->is_flush[1], 422 (void *)(u_long)is->is_flushpa); /* panic? */ 423 #ifdef DDB 424 Debugger(); 425 #endif 426 } 427 #endif 428 DPRINTF(IDB_IOMMU, ("iommu_strbuf_flush_done: flushed\n")); 429 return (is->is_flush[0] && is->is_flush[1]); 430 } 431 432 /* 433 * IOMMU DVMA operations, common to SBUS and PCI. 434 */ 435 int 436 iommu_dvmamap_load(t, is, map, buf, buflen, p, flags) 437 bus_dma_tag_t t; 438 struct iommu_state *is; 439 bus_dmamap_t map; 440 void *buf; 441 bus_size_t buflen; 442 struct proc *p; 443 int flags; 444 { 445 int s; 446 int err; 447 bus_size_t sgsize; 448 paddr_t curaddr; 449 u_long dvmaddr, sgstart, sgend; 450 bus_size_t align, boundary; 451 vaddr_t vaddr = (vaddr_t)buf; 452 int seg; 453 pmap_t pmap; 454 455 if (map->dm_nsegs) { 456 /* Already in use?? */ 457 #ifdef DIAGNOSTIC 458 printf("iommu_dvmamap_load: map still in use\n"); 459 #endif 460 bus_dmamap_unload(t, map); 461 } 462 /* 463 * Make sure that on error condition we return "no valid mappings". 464 */ 465 map->dm_nsegs = 0; 466 467 if (buflen > map->_dm_size) { 468 DPRINTF(IDB_BUSDMA, 469 ("iommu_dvmamap_load(): error %d > %d -- " 470 "map size exceeded!\n", (int)buflen, (int)map->_dm_size)); 471 return (EINVAL); 472 } 473 474 sgsize = round_page(buflen + ((int)vaddr & PGOFSET)); 475 476 /* 477 * A boundary presented to bus_dmamem_alloc() takes precedence 478 * over boundary in the map. 479 */ 480 if ((boundary = (map->dm_segs[0]._ds_boundary)) == 0) 481 boundary = map->_dm_boundary; 482 align = max(map->dm_segs[0]._ds_align, NBPG); 483 s = splhigh(); 484 /* 485 * If our segment size is larger than the boundary we need to 486 * split the transfer up int little pieces ourselves. 487 */ 488 err = extent_alloc(is->is_dvmamap, sgsize, align, 489 (sgsize > boundary) ? 0 : boundary, 490 EX_NOWAIT|EX_BOUNDZERO, (u_long *)&dvmaddr); 491 splx(s); 492 493 #ifdef DEBUG 494 if (err || (dvmaddr == (bus_addr_t)-1)) 495 { 496 printf("iommu_dvmamap_load(): extent_alloc(%d, %x) failed!\n", 497 (int)sgsize, flags); 498 #ifdef DDB 499 Debugger(); 500 #endif 501 } 502 #endif 503 if (err != 0) 504 return (err); 505 506 if (dvmaddr == (bus_addr_t)-1) 507 return (ENOMEM); 508 509 /* Set the active DVMA map */ 510 map->_dm_dvmastart = dvmaddr; 511 map->_dm_dvmasize = sgsize; 512 513 /* 514 * Now split the DVMA range into segments, not crossing 515 * the boundary. 516 */ 517 seg = 0; 518 sgstart = dvmaddr + (vaddr & PGOFSET); 519 sgend = sgstart + buflen - 1; 520 map->dm_segs[seg].ds_addr = sgstart; 521 DPRINTF(IDB_INFO, ("iommu_dvmamap_load: boundary %lx boundary-1 %lx " 522 "~(boundary-1) %lx\n", boundary, (boundary-1), ~(boundary-1))); 523 while ((sgstart & ~(boundary - 1)) != (sgend & ~(boundary - 1))) { 524 /* Oops. We crossed a boundary. Split the xfer. */ 525 DPRINTF(IDB_INFO, ("iommu_dvmamap_load: " 526 "seg %d start %lx size %lx\n", seg, 527 (long)map->dm_segs[seg].ds_addr, 528 map->dm_segs[seg].ds_len)); 529 map->dm_segs[seg].ds_len = sgstart & (boundary - 1); 530 if (++seg > map->_dm_segcnt) { 531 /* Too many segments. Fail the operation. */ 532 DPRINTF(IDB_INFO, ("iommu_dvmamap_load: " 533 "too many segments %d\n", seg)); 534 s = splhigh(); 535 /* How can this fail? And if it does what can we do? */ 536 err = extent_free(is->is_dvmamap, 537 dvmaddr, sgsize, EX_NOWAIT); 538 map->_dm_dvmastart = 0; 539 map->_dm_dvmasize = 0; 540 splx(s); 541 return (E2BIG); 542 } 543 sgstart = roundup(sgstart, boundary); 544 map->dm_segs[seg].ds_addr = sgstart; 545 } 546 map->dm_segs[seg].ds_len = sgend - sgstart + 1; 547 DPRINTF(IDB_INFO, ("iommu_dvmamap_load: " 548 "seg %d start %lx size %lx\n", seg, 549 (long)map->dm_segs[seg].ds_addr, map->dm_segs[seg].ds_len)); 550 map->dm_nsegs = seg+1; 551 map->dm_mapsize = buflen; 552 553 if (p != NULL) 554 pmap = p->p_vmspace->vm_map.pmap; 555 else 556 pmap = pmap_kernel(); 557 558 for (; buflen > 0; ) { 559 /* 560 * Get the physical address for this page. 561 */ 562 if (pmap_extract(pmap, (vaddr_t)vaddr, &curaddr) == FALSE) { 563 bus_dmamap_unload(t, map); 564 return (-1); 565 } 566 567 /* 568 * Compute the segment size, and adjust counts. 569 */ 570 sgsize = NBPG - ((u_long)vaddr & PGOFSET); 571 if (buflen < sgsize) 572 sgsize = buflen; 573 574 DPRINTF(IDB_BUSDMA, 575 ("iommu_dvmamap_load: map %p loading va %p " 576 "dva %lx at pa %lx\n", 577 map, (void *)vaddr, (long)dvmaddr, 578 (long)(curaddr&~(NBPG-1)))); 579 iommu_enter(is, trunc_page(dvmaddr), trunc_page(curaddr), 580 flags|0x4000); 581 582 dvmaddr += PAGE_SIZE; 583 vaddr += sgsize; 584 buflen -= sgsize; 585 } 586 #ifdef DIAGNOSTIC 587 for (seg = 0; seg < map->dm_nsegs; seg++) { 588 if (map->dm_segs[seg].ds_addr < is->is_dvmabase || 589 map->dm_segs[seg].ds_addr > is->is_dvmaend) { 590 printf("seg %d dvmaddr %lx out of range %x - %x\n", 591 seg, (long)map->dm_segs[seg].ds_addr, 592 is->is_dvmabase, is->is_dvmaend); 593 Debugger(); 594 } 595 } 596 #endif 597 return (0); 598 } 599 600 601 void 602 iommu_dvmamap_unload(t, is, map) 603 bus_dma_tag_t t; 604 struct iommu_state *is; 605 bus_dmamap_t map; 606 { 607 int error, s; 608 bus_size_t sgsize; 609 610 /* Flush the iommu */ 611 #ifdef DEBUG 612 if (!map->_dm_dvmastart) { 613 printf("iommu_dvmamap_unload: No dvmastart is zero\n"); 614 #ifdef DDB 615 Debugger(); 616 #endif 617 } 618 #endif 619 iommu_remove(is, map->_dm_dvmastart, map->_dm_dvmasize); 620 621 /* Flush the caches */ 622 bus_dmamap_unload(t->_parent, map); 623 624 /* Mark the mappings as invalid. */ 625 map->dm_mapsize = 0; 626 map->dm_nsegs = 0; 627 628 s = splhigh(); 629 error = extent_free(is->is_dvmamap, map->_dm_dvmastart, 630 map->_dm_dvmasize, EX_NOWAIT); 631 map->_dm_dvmastart = 0; 632 map->_dm_dvmasize = 0; 633 splx(s); 634 if (error != 0) 635 printf("warning: %qd of DVMA space lost\n", (long long)sgsize); 636 637 /* Clear the map */ 638 } 639 640 641 int 642 iommu_dvmamap_load_raw(t, is, map, segs, nsegs, flags, size) 643 bus_dma_tag_t t; 644 struct iommu_state *is; 645 bus_dmamap_t map; 646 bus_dma_segment_t *segs; 647 int nsegs; 648 int flags; 649 bus_size_t size; 650 { 651 struct vm_page *m; 652 int i, j, s; 653 int left; 654 int err; 655 bus_size_t sgsize; 656 paddr_t pa; 657 bus_size_t boundary, align; 658 u_long dvmaddr, sgstart, sgend; 659 struct pglist *mlist; 660 int pagesz = PAGE_SIZE; 661 int npg = 0; /* DEBUG */ 662 663 if (map->dm_nsegs) { 664 /* Already in use?? */ 665 #ifdef DIAGNOSTIC 666 printf("iommu_dvmamap_load_raw: map still in use\n"); 667 #endif 668 bus_dmamap_unload(t, map); 669 } 670 671 /* 672 * A boundary presented to bus_dmamem_alloc() takes precedence 673 * over boundary in the map. 674 */ 675 if ((boundary = segs[0]._ds_boundary) == 0) 676 boundary = map->_dm_boundary; 677 678 align = max(segs[0]._ds_align, pagesz); 679 680 /* 681 * Make sure that on error condition we return "no valid mappings". 682 */ 683 map->dm_nsegs = 0; 684 /* Count up the total number of pages we need */ 685 pa = segs[0].ds_addr; 686 sgsize = 0; 687 left = size; 688 for (i=0; left && i<nsegs; i++) { 689 if (round_page(pa) != round_page(segs[i].ds_addr)) 690 sgsize = round_page(sgsize); 691 sgsize += min(left, segs[i].ds_len); 692 left -= segs[i].ds_len; 693 pa = segs[i].ds_addr + segs[i].ds_len; 694 } 695 sgsize = round_page(sgsize); 696 697 s = splhigh(); 698 /* 699 * If our segment size is larger than the boundary we need to 700 * split the transfer up into little pieces ourselves. 701 */ 702 err = extent_alloc(is->is_dvmamap, sgsize, align, 703 (sgsize > boundary) ? 0 : boundary, 704 ((flags & BUS_DMA_NOWAIT) == 0 ? EX_WAITOK : EX_NOWAIT) | 705 EX_BOUNDZERO, (u_long *)&dvmaddr); 706 splx(s); 707 708 if (err != 0) 709 return (err); 710 711 #ifdef DEBUG 712 if (dvmaddr == (bus_addr_t)-1) 713 { 714 printf("iommu_dvmamap_load_raw(): extent_alloc(%d, %x) failed!\n", 715 (int)sgsize, flags); 716 Debugger(); 717 } 718 #endif 719 if (dvmaddr == (bus_addr_t)-1) 720 return (ENOMEM); 721 722 /* Set the active DVMA map */ 723 map->_dm_dvmastart = dvmaddr; 724 map->_dm_dvmasize = sgsize; 725 726 if ((mlist = segs[0]._ds_mlist) == NULL) { 727 u_long prev_va = NULL; 728 paddr_t prev_pa = 0; 729 int end = 0, offset; 730 731 /* 732 * This segs is made up of individual physical 733 * segments, probably by _bus_dmamap_load_uio() or 734 * _bus_dmamap_load_mbuf(). Ignore the mlist and 735 * load each one individually. 736 */ 737 map->dm_mapsize = size; 738 739 j = 0; 740 for (i = 0; i < nsegs ; i++) { 741 742 pa = segs[i].ds_addr; 743 offset = (pa & PGOFSET); 744 pa = trunc_page(pa); 745 dvmaddr = trunc_page(dvmaddr); 746 left = min(size, segs[i].ds_len); 747 748 DPRINTF(IDB_INFO, ("iommu_dvmamap_load_raw: converting " 749 "physseg %d start %lx size %lx\n", i, 750 (long)segs[i].ds_addr, segs[i].ds_len)); 751 752 if ((pa == prev_pa) && 753 ((offset != 0) || (end != offset))) { 754 /* We can re-use this mapping */ 755 #ifdef DEBUG 756 if (iommudebug & 0x10) printf("reusing dva %lx prev %lx pa %lx prev %lx\n", 757 dvmaddr, prev_va, pa, prev_pa); 758 #endif 759 dvmaddr = prev_va; 760 } 761 762 sgstart = dvmaddr + offset; 763 sgend = sgstart + left - 1; 764 765 /* Are the segments virtually adjacent? */ 766 if ((j > 0) && (end == offset) && 767 ((offset == 0) || (pa == prev_pa))) { 768 /* Just append to the previous segment. */ 769 #ifdef DEBUG 770 if (iommudebug & 0x10) { 771 printf("appending: offset %x pa %lx prev %lx dva %lx prev %lx\n", 772 offset, pa, prev_pa, dvmaddr, prev_va); 773 } 774 #endif 775 776 map->dm_segs[--j].ds_len += left; 777 DPRINTF(IDB_INFO, ("iommu_dvmamap_load_raw: " 778 "appending seg %d start %lx size %lx\n", j, 779 (long)map->dm_segs[j].ds_addr, 780 map->dm_segs[j].ds_len)); 781 } else { 782 map->dm_segs[j].ds_addr = sgstart; 783 map->dm_segs[j].ds_len = left; 784 DPRINTF(IDB_INFO, ("iommu_dvmamap_load_raw: " 785 "seg %d start %lx size %lx\n", j, 786 (long)map->dm_segs[j].ds_addr, 787 map->dm_segs[j].ds_len)); 788 } 789 end = (offset + left) & PGOFSET; 790 791 /* Check for boundary issues */ 792 while ((sgstart & ~(boundary - 1)) != 793 (sgend & ~(boundary - 1))) { 794 /* Need a new segment. */ 795 map->dm_segs[j].ds_len = 796 sgstart & (boundary - 1); 797 DPRINTF(IDB_INFO, ("iommu_dvmamap_load_raw: " 798 "seg %d start %lx size %lx\n", j, 799 (long)map->dm_segs[j].ds_addr, 800 map->dm_segs[j].ds_len)); 801 if (++j > map->_dm_segcnt) { 802 iommu_dvmamap_unload(t, is, map); 803 return (E2BIG); 804 } 805 sgstart = roundup(sgstart, boundary); 806 map->dm_segs[j].ds_addr = sgstart; 807 map->dm_segs[j].ds_len = sgend - sgstart + 1; 808 } 809 810 if (sgsize == 0) 811 panic("iommu_dmamap_load_raw: size botch"); 812 813 /* Now map a series of pages. */ 814 while (dvmaddr < sgend) { 815 DPRINTF(IDB_BUSDMA, 816 ("iommu_dvmamap_load_raw: map %p " 817 "loading va %lx at pa %lx\n", 818 map, (long)dvmaddr, 819 (long)(pa))); 820 /* Enter it if we haven't before. */ 821 if (prev_va != dvmaddr) 822 #ifdef DEBUG 823 { if (iommudebug & 0x10) printf("seg %d:%d entering dvma %lx, prev %lx pa %lx\n", i,j, dvmaddr, prev_va, pa); 824 #endif 825 iommu_enter(is, prev_va = dvmaddr, 826 prev_pa = pa, 827 flags|(++npg<<12)); 828 #ifdef DEBUG 829 } else if (iommudebug & 0x10) printf("seg %d:%d skipping dvma %lx, prev %lx\n", i,j, dvmaddr, prev_va); 830 #endif 831 832 dvmaddr += pagesz; 833 pa += pagesz; 834 } 835 836 size -= left; 837 ++j; 838 } 839 840 map->dm_nsegs = j; 841 #ifdef DIAGNOSTIC 842 { int seg; 843 for (seg = 0; seg < map->dm_nsegs; seg++) { 844 if (map->dm_segs[seg].ds_addr < is->is_dvmabase || 845 map->dm_segs[seg].ds_addr > is->is_dvmaend) { 846 printf("seg %d dvmaddr %lx out of range %x - %x\n", 847 seg, (long)map->dm_segs[seg].ds_addr, 848 is->is_dvmabase, is->is_dvmaend); 849 Debugger(); 850 } 851 } 852 } 853 #endif 854 return (0); 855 } 856 /* 857 * This was allocated with bus_dmamem_alloc. 858 * The pages are on an `mlist'. 859 */ 860 map->dm_mapsize = size; 861 i = 0; 862 sgstart = dvmaddr; 863 sgend = sgstart + size - 1; 864 map->dm_segs[i].ds_addr = sgstart; 865 while ((sgstart & ~(boundary - 1)) != (sgend & ~(boundary - 1))) { 866 /* Oops. We crossed a boundary. Split the xfer. */ 867 map->dm_segs[i].ds_len = sgstart & (boundary - 1); 868 DPRINTF(IDB_INFO, ("iommu_dvmamap_load_raw: " 869 "seg %d start %lx size %lx\n", i, 870 (long)map->dm_segs[i].ds_addr, 871 map->dm_segs[i].ds_len)); 872 if (++i > map->_dm_segcnt) { 873 /* Too many segments. Fail the operation. */ 874 s = splhigh(); 875 /* How can this fail? And if it does what can we do? */ 876 err = extent_free(is->is_dvmamap, 877 dvmaddr, sgsize, EX_NOWAIT); 878 map->_dm_dvmastart = 0; 879 map->_dm_dvmasize = 0; 880 splx(s); 881 return (E2BIG); 882 } 883 sgstart = roundup(sgstart, boundary); 884 map->dm_segs[i].ds_addr = sgstart; 885 } 886 DPRINTF(IDB_INFO, ("iommu_dvmamap_load_raw: " 887 "seg %d start %lx size %lx\n", i, 888 (long)map->dm_segs[i].ds_addr, map->dm_segs[i].ds_len)); 889 map->dm_segs[i].ds_len = sgend - sgstart + 1; 890 891 for (m = TAILQ_FIRST(mlist); m != NULL; m = TAILQ_NEXT(m,pageq)) { 892 if (sgsize == 0) 893 panic("iommu_dmamap_load_raw: size botch"); 894 pa = VM_PAGE_TO_PHYS(m); 895 896 DPRINTF(IDB_BUSDMA, 897 ("iommu_dvmamap_load_raw: map %p loading va %lx at pa %lx\n", 898 map, (long)dvmaddr, (long)(pa))); 899 iommu_enter(is, dvmaddr, pa, flags|0x8000); 900 901 dvmaddr += pagesz; 902 sgsize -= pagesz; 903 } 904 map->dm_mapsize = size; 905 map->dm_nsegs = i+1; 906 #ifdef DIAGNOSTIC 907 { int seg; 908 for (seg = 0; seg < map->dm_nsegs; seg++) { 909 if (map->dm_segs[seg].ds_addr < is->is_dvmabase || 910 map->dm_segs[seg].ds_addr > is->is_dvmaend) { 911 printf("seg %d dvmaddr %lx out of range %x - %x\n", 912 seg, (long)map->dm_segs[seg].ds_addr, 913 is->is_dvmabase, is->is_dvmaend); 914 Debugger(); 915 } 916 } 917 } 918 #endif 919 return (0); 920 } 921 922 void 923 iommu_dvmamap_sync(t, is, map, offset, len, ops) 924 bus_dma_tag_t t; 925 struct iommu_state *is; 926 bus_dmamap_t map; 927 bus_addr_t offset; 928 bus_size_t len; 929 int ops; 930 { 931 vaddr_t va = map->dm_segs[0].ds_addr + offset; 932 933 /* 934 * We only support one DMA segment; supporting more makes this code 935 * too unweildy. 936 */ 937 938 if (ops & BUS_DMASYNC_PREREAD) { 939 DPRINTF(IDB_SYNC, 940 ("iommu_dvmamap_sync: syncing va %p len %lu " 941 "BUS_DMASYNC_PREREAD\n", (void *)(u_long)va, (u_long)len)); 942 943 /* Nothing to do */; 944 } 945 if (ops & BUS_DMASYNC_POSTREAD) { 946 DPRINTF(IDB_SYNC, 947 ("iommu_dvmamap_sync: syncing va %p len %lu " 948 "BUS_DMASYNC_POSTREAD\n", (void *)(u_long)va, (u_long)len)); 949 /* if we have a streaming buffer, flush it here first */ 950 if (is->is_sb[0] || is->is_sb[1]) 951 while (len > 0) { 952 DPRINTF(IDB_BUSDMA, 953 ("iommu_dvmamap_sync: flushing va %p, %lu " 954 "bytes left\n", (void *)(u_long)va, (u_long)len)); 955 iommu_strbuf_flush(is, va); 956 if (len <= NBPG) { 957 iommu_strbuf_flush_done(is); 958 len = 0; 959 } else 960 len -= NBPG; 961 va += NBPG; 962 } 963 } 964 if (ops & BUS_DMASYNC_PREWRITE) { 965 DPRINTF(IDB_SYNC, 966 ("iommu_dvmamap_sync: syncing va %p len %lu " 967 "BUS_DMASYNC_PREWRITE\n", (void *)(u_long)va, (u_long)len)); 968 /* if we have a streaming buffer, flush it here first */ 969 if (is->is_sb[0] || is->is_sb[1]) 970 while (len > 0) { 971 DPRINTF(IDB_BUSDMA, 972 ("iommu_dvmamap_sync: flushing va %p, %lu " 973 "bytes left\n", (void *)(u_long)va, (u_long)len)); 974 iommu_strbuf_flush(is, va); 975 if (len <= NBPG) { 976 iommu_strbuf_flush_done(is); 977 len = 0; 978 } else 979 len -= NBPG; 980 va += NBPG; 981 } 982 } 983 if (ops & BUS_DMASYNC_POSTWRITE) { 984 DPRINTF(IDB_SYNC, 985 ("iommu_dvmamap_sync: syncing va %p len %lu " 986 "BUS_DMASYNC_POSTWRITE\n", (void *)(u_long)va, (u_long)len)); 987 /* Nothing to do */; 988 } 989 } 990 991 int 992 iommu_dvmamem_alloc(t, is, size, alignment, boundary, segs, nsegs, rsegs, flags) 993 bus_dma_tag_t t; 994 struct iommu_state *is; 995 bus_size_t size, alignment, boundary; 996 bus_dma_segment_t *segs; 997 int nsegs; 998 int *rsegs; 999 int flags; 1000 { 1001 1002 DPRINTF(IDB_BUSDMA, ("iommu_dvmamem_alloc: sz %llx align %llx bound %llx " 1003 "segp %p flags %d\n", (unsigned long long)size, 1004 (unsigned long long)alignment, (unsigned long long)boundary, 1005 segs, flags)); 1006 return (bus_dmamem_alloc(t->_parent, size, alignment, boundary, 1007 segs, nsegs, rsegs, flags|BUS_DMA_DVMA)); 1008 } 1009 1010 void 1011 iommu_dvmamem_free(t, is, segs, nsegs) 1012 bus_dma_tag_t t; 1013 struct iommu_state *is; 1014 bus_dma_segment_t *segs; 1015 int nsegs; 1016 { 1017 1018 DPRINTF(IDB_BUSDMA, ("iommu_dvmamem_free: segp %p nsegs %d\n", 1019 segs, nsegs)); 1020 bus_dmamem_free(t->_parent, segs, nsegs); 1021 } 1022 1023 /* 1024 * Map the DVMA mappings into the kernel pmap. 1025 * Check the flags to see whether we're streaming or coherent. 1026 */ 1027 int 1028 iommu_dvmamem_map(t, is, segs, nsegs, size, kvap, flags) 1029 bus_dma_tag_t t; 1030 struct iommu_state *is; 1031 bus_dma_segment_t *segs; 1032 int nsegs; 1033 size_t size; 1034 caddr_t *kvap; 1035 int flags; 1036 { 1037 struct vm_page *m; 1038 vaddr_t va; 1039 bus_addr_t addr; 1040 struct pglist *mlist; 1041 int cbit; 1042 1043 DPRINTF(IDB_BUSDMA, ("iommu_dvmamem_map: segp %p nsegs %d size %lx\n", 1044 segs, nsegs, size)); 1045 1046 /* 1047 * Allocate some space in the kernel map, and then map these pages 1048 * into this space. 1049 */ 1050 size = round_page(size); 1051 va = uvm_km_valloc(kernel_map, size); 1052 if (va == 0) 1053 return (ENOMEM); 1054 1055 *kvap = (caddr_t)va; 1056 1057 /* 1058 * digest flags: 1059 */ 1060 cbit = 0; 1061 if (flags & BUS_DMA_COHERENT) /* Disable vcache */ 1062 cbit |= PMAP_NVC; 1063 if (flags & BUS_DMA_NOCACHE) /* sideffects */ 1064 cbit |= PMAP_NC; 1065 1066 /* 1067 * Now take this and map it into the CPU. 1068 */ 1069 mlist = segs[0]._ds_mlist; 1070 for (m = mlist->tqh_first; m != NULL; m = m->pageq.tqe_next) { 1071 #ifdef DIAGNOSTIC 1072 if (size == 0) 1073 panic("iommu_dvmamem_map: size botch"); 1074 #endif 1075 addr = VM_PAGE_TO_PHYS(m); 1076 DPRINTF(IDB_BUSDMA, ("iommu_dvmamem_map: " 1077 "mapping va %lx at %llx\n", va, (unsigned long long)addr | cbit)); 1078 pmap_enter(pmap_kernel(), va, addr | cbit, 1079 VM_PROT_READ | VM_PROT_WRITE, 1080 VM_PROT_READ | VM_PROT_WRITE | PMAP_WIRED); 1081 va += PAGE_SIZE; 1082 size -= PAGE_SIZE; 1083 } 1084 pmap_update(pmap_kernel()); 1085 1086 return (0); 1087 } 1088 1089 /* 1090 * Unmap DVMA mappings from kernel 1091 */ 1092 void 1093 iommu_dvmamem_unmap(t, is, kva, size) 1094 bus_dma_tag_t t; 1095 struct iommu_state *is; 1096 caddr_t kva; 1097 size_t size; 1098 { 1099 1100 DPRINTF(IDB_BUSDMA, ("iommu_dvmamem_unmap: kvm %p size %lx\n", 1101 kva, size)); 1102 1103 #ifdef DIAGNOSTIC 1104 if ((u_long)kva & PGOFSET) 1105 panic("iommu_dvmamem_unmap"); 1106 #endif 1107 1108 size = round_page(size); 1109 pmap_remove(pmap_kernel(), (vaddr_t)kva, size); 1110 pmap_update(pmap_kernel()); 1111 #if 0 1112 /* 1113 * XXX ? is this necessary? i think so and i think other 1114 * implementations are missing it. 1115 */ 1116 uvm_km_free(kernel_map, (vaddr_t)kva, size); 1117 #endif 1118 } 1119