Annotation of sys/arch/sparc64/dev/iommu.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: iommu.c,v 1.47 2007/05/29 09:53:59 sobrado Exp $ */
! 2: /* $NetBSD: iommu.c,v 1.47 2002/02/08 20:03:45 eeh Exp $ */
! 3:
! 4: /*
! 5: * Copyright (c) 2003 Henric Jungheim
! 6: * Copyright (c) 2001, 2002 Eduardo Horvath
! 7: * Copyright (c) 1999, 2000 Matthew R. Green
! 8: * All rights reserved.
! 9: *
! 10: * Redistribution and use in source and binary forms, with or without
! 11: * modification, are permitted provided that the following conditions
! 12: * are met:
! 13: * 1. Redistributions of source code must retain the above copyright
! 14: * notice, this list of conditions and the following disclaimer.
! 15: * 2. Redistributions in binary form must reproduce the above copyright
! 16: * notice, this list of conditions and the following disclaimer in the
! 17: * documentation and/or other materials provided with the distribution.
! 18: * 3. 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 ``AS IS'' AND ANY EXPRESS OR
! 22: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
! 23: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
! 24: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
! 25: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
! 26: * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
! 27: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
! 28: * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
! 29: * 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: /*
! 35: * UltraSPARC IOMMU support; used by both the sbus and pci code.
! 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/mbuf.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 DDB
! 55: #include <machine/db_machdep.h>
! 56: #include <ddb/db_sym.h>
! 57: #include <ddb/db_extern.h>
! 58: #endif
! 59:
! 60: #ifdef DEBUG
! 61: #define IDB_BUSDMA 0x1
! 62: #define IDB_IOMMU 0x2
! 63: #define IDB_INFO 0x4
! 64: #define IDB_SYNC 0x8
! 65: #define IDB_XXX 0x10
! 66: #define IDB_PRINT_MAP 0x20
! 67: #define IDB_BREAK 0x40
! 68: int iommudebug = IDB_INFO;
! 69: #define DPRINTF(l, s) do { if (iommudebug & l) printf s; } while (0)
! 70: #else
! 71: #define DPRINTF(l, s)
! 72: #endif
! 73:
! 74: void iommu_enter(struct iommu_state *, struct strbuf_ctl *, vaddr_t, paddr_t,
! 75: int);
! 76: void iommu_remove(struct iommu_state *, struct strbuf_ctl *, vaddr_t);
! 77: int iommu_dvmamap_sync_range(struct strbuf_ctl*, vaddr_t, bus_size_t);
! 78: int iommu_strbuf_flush_done(struct iommu_map_state *);
! 79: int iommu_dvmamap_load_seg(bus_dma_tag_t, struct iommu_state *,
! 80: bus_dmamap_t, bus_dma_segment_t *, int, int, bus_size_t, bus_size_t);
! 81: int iommu_dvmamap_load_mlist(bus_dma_tag_t, struct iommu_state *,
! 82: bus_dmamap_t, struct pglist *, int, bus_size_t, bus_size_t);
! 83: int iommu_dvmamap_validate_map(bus_dma_tag_t, struct iommu_state *,
! 84: bus_dmamap_t);
! 85: void iommu_dvmamap_print_map(bus_dma_tag_t, struct iommu_state *,
! 86: bus_dmamap_t);
! 87: int iommu_dvmamap_append_range(bus_dma_tag_t, bus_dmamap_t, paddr_t,
! 88: bus_size_t, int, bus_size_t);
! 89: int64_t iommu_tsb_entry(struct iommu_state *, vaddr_t);
! 90: void strbuf_reset(struct strbuf_ctl *);
! 91: int iommu_iomap_insert_page(struct iommu_map_state *, paddr_t);
! 92: vaddr_t iommu_iomap_translate(struct iommu_map_state *, paddr_t);
! 93: int iommu_iomap_load_map(struct iommu_state *, struct iommu_map_state *,
! 94: vaddr_t, int);
! 95: int iommu_iomap_unload_map(struct iommu_state *, struct iommu_map_state *);
! 96: struct iommu_map_state *iommu_iomap_create(int);
! 97: void iommu_iomap_destroy(struct iommu_map_state *);
! 98: void iommu_iomap_clear_pages(struct iommu_map_state *);
! 99: void _iommu_dvmamap_sync(bus_dma_tag_t, bus_dma_tag_t, bus_dmamap_t,
! 100: bus_addr_t, bus_size_t, int);
! 101:
! 102: /*
! 103: * Initiate an STC entry flush.
! 104: */
! 105: static inline void
! 106: iommu_strbuf_flush(struct strbuf_ctl *sb, vaddr_t va)
! 107: {
! 108: #ifdef DEBUG
! 109: if (sb->sb_flush == NULL) {
! 110: printf("iommu_strbuf_flush: attempting to flush w/o STC\n");
! 111: return;
! 112: }
! 113: #endif
! 114:
! 115: bus_space_write_8(sb->sb_bustag, sb->sb_sb,
! 116: STRBUFREG(strbuf_pgflush), va);
! 117: }
! 118:
! 119: /*
! 120: * initialise the UltraSPARC IOMMU (SBus or PCI):
! 121: * - allocate and setup the iotsb.
! 122: * - enable the IOMMU
! 123: * - initialise the streaming buffers (if they exist)
! 124: * - create a private DVMA map.
! 125: */
! 126: void
! 127: iommu_init(char *name, struct iommu_state *is, int tsbsize, u_int32_t iovabase)
! 128: {
! 129: psize_t size;
! 130: vaddr_t va;
! 131: paddr_t pa;
! 132: struct vm_page *m;
! 133: struct pglist mlist;
! 134:
! 135: /*
! 136: * Setup the iommu.
! 137: *
! 138: * The sun4u iommu is part of the SBus or PCI controller so we will
! 139: * deal with it here..
! 140: *
! 141: * For sysio and psycho/psycho+ the IOMMU address space always ends at
! 142: * 0xffffe000, but the starting address depends on the size of the
! 143: * map. The map size is 1024 * 2 ^ is->is_tsbsize entries, where each
! 144: * entry is 8 bytes. The start of the map can be calculated by
! 145: * (0xffffe000 << (8 + is->is_tsbsize)).
! 146: *
! 147: * But sabre and hummingbird use a different scheme that seems to
! 148: * be hard-wired, so we read the start and size from the PROM and
! 149: * just use those values.
! 150: */
! 151: is->is_cr = IOMMUCR_EN;
! 152: is->is_tsbsize = tsbsize;
! 153: if (iovabase == (u_int32_t)-1) {
! 154: is->is_dvmabase = IOTSB_VSTART(is->is_tsbsize);
! 155: is->is_dvmaend = IOTSB_VEND;
! 156: } else {
! 157: is->is_dvmabase = iovabase;
! 158: is->is_dvmaend = iovabase + IOTSB_VSIZE(tsbsize) - 1;
! 159: }
! 160:
! 161: /*
! 162: * Allocate memory for I/O pagetables. They need to be physically
! 163: * contiguous.
! 164: */
! 165:
! 166: size = PAGE_SIZE << is->is_tsbsize;
! 167: TAILQ_INIT(&mlist);
! 168: if (uvm_pglistalloc((psize_t)size, (paddr_t)0, (paddr_t)-1,
! 169: (paddr_t)PAGE_SIZE, (paddr_t)0, &mlist, 1, 0) != 0)
! 170: panic("iommu_init: no memory");
! 171:
! 172: va = uvm_km_valloc(kernel_map, size);
! 173: if (va == 0)
! 174: panic("iommu_init: no memory");
! 175: is->is_tsb = (int64_t *)va;
! 176:
! 177: m = TAILQ_FIRST(&mlist);
! 178: is->is_ptsb = VM_PAGE_TO_PHYS(m);
! 179:
! 180: /* Map the pages */
! 181: for (; m != NULL; m = TAILQ_NEXT(m,pageq)) {
! 182: pa = VM_PAGE_TO_PHYS(m);
! 183: pmap_enter(pmap_kernel(), va, pa | PMAP_NVC,
! 184: VM_PROT_READ|VM_PROT_WRITE,
! 185: VM_PROT_READ|VM_PROT_WRITE|PMAP_WIRED);
! 186: va += PAGE_SIZE;
! 187: }
! 188: pmap_update(pmap_kernel());
! 189: memset(is->is_tsb, 0, size);
! 190:
! 191: #ifdef DEBUG
! 192: if (iommudebug & IDB_INFO) {
! 193: /* Probe the iommu */
! 194: /* The address or contents of the regs...? */
! 195: printf("iommu regs at: cr=%lx tsb=%lx flush=%lx\n",
! 196: (u_long)bus_space_vaddr(is->is_bustag, is->is_iommu) +
! 197: IOMMUREG(iommu_cr),
! 198: (u_long)bus_space_vaddr(is->is_bustag, is->is_iommu) +
! 199: IOMMUREG(iommu_tsb),
! 200: (u_long)bus_space_vaddr(is->is_bustag, is->is_iommu) +
! 201: IOMMUREG(iommu_flush));
! 202: printf("iommu cr=%llx tsb=%llx\n",
! 203: IOMMUREG_READ(is, iommu_cr),
! 204: IOMMUREG_READ(is, iommu_tsb));
! 205: printf("TSB base %p phys %llx\n",
! 206: (void *)is->is_tsb, (unsigned long long)is->is_ptsb);
! 207: delay(1000000); /* 1 s */
! 208: }
! 209: #endif
! 210:
! 211: /*
! 212: * Now all the hardware's working we need to allocate a dvma map.
! 213: */
! 214: printf("dvma map %x-%x, ", is->is_dvmabase, is->is_dvmaend);
! 215: printf("iotdb %llx-%llx",
! 216: (unsigned long long)is->is_ptsb,
! 217: (unsigned long long)(is->is_ptsb + size));
! 218: is->is_dvmamap = extent_create(name,
! 219: is->is_dvmabase, (u_long)is->is_dvmaend + 1,
! 220: M_DEVBUF, 0, 0, EX_NOWAIT);
! 221:
! 222: /*
! 223: * Set the TSB size. The relevant bits were moved to the TSB
! 224: * base register in the PCIe host bridges.
! 225: */
! 226: if (strncmp(name, "pyro", 4) == 0)
! 227: is->is_ptsb |= is->is_tsbsize;
! 228: else
! 229: is->is_cr |= (is->is_tsbsize << 16);
! 230:
! 231: /*
! 232: * Now actually start up the IOMMU.
! 233: */
! 234: iommu_reset(is);
! 235: printf("\n");
! 236: }
! 237:
! 238: /*
! 239: * Streaming buffers don't exist on the UltraSPARC IIi/e; we should have
! 240: * detected that already and disabled them. If not, we will notice that
! 241: * they aren't there when the STRBUF_EN bit does not remain.
! 242: */
! 243: void
! 244: iommu_reset(struct iommu_state *is)
! 245: {
! 246: int i;
! 247:
! 248: IOMMUREG_WRITE(is, iommu_tsb, is->is_ptsb);
! 249:
! 250: /* Enable IOMMU */
! 251: IOMMUREG_WRITE(is, iommu_cr, is->is_cr);
! 252:
! 253: for (i = 0; i < 2; ++i) {
! 254: struct strbuf_ctl *sb = is->is_sb[i];
! 255:
! 256: if (sb == NULL)
! 257: continue;
! 258:
! 259: sb->sb_iommu = is;
! 260: strbuf_reset(sb);
! 261:
! 262: if (sb->sb_flush)
! 263: printf(", STC%d enabled", i);
! 264: }
! 265: }
! 266:
! 267: /*
! 268: * Initialize one STC.
! 269: */
! 270: void
! 271: strbuf_reset(struct strbuf_ctl *sb)
! 272: {
! 273: if(sb->sb_flush == NULL)
! 274: return;
! 275:
! 276: bus_space_write_8(sb->sb_bustag, sb->sb_sb,
! 277: STRBUFREG(strbuf_ctl), STRBUF_EN);
! 278:
! 279: membar(Lookaside);
! 280:
! 281: /* No streaming buffers? Disable them */
! 282: if (bus_space_read_8(sb->sb_bustag, sb->sb_sb,
! 283: STRBUFREG(strbuf_ctl)) == 0) {
! 284: sb->sb_flush = NULL;
! 285: } else {
! 286: /*
! 287: * locate the pa of the flush buffer
! 288: */
! 289: if (pmap_extract(pmap_kernel(),
! 290: (vaddr_t)sb->sb_flush, &sb->sb_flushpa) == FALSE)
! 291: sb->sb_flush = NULL;
! 292: }
! 293: }
! 294:
! 295: /*
! 296: * Add an entry to the IOMMU table.
! 297: *
! 298: * The entry is marked streaming if an STC was detected and
! 299: * the BUS_DMA_STREAMING flag is set.
! 300: */
! 301: void
! 302: iommu_enter(struct iommu_state *is, struct strbuf_ctl *sb, vaddr_t va,
! 303: paddr_t pa, int flags)
! 304: {
! 305: int64_t tte;
! 306: volatile int64_t *tte_ptr = &is->is_tsb[IOTSBSLOT(va,is->is_tsbsize)];
! 307:
! 308: #ifdef DIAGNOSTIC
! 309: if (va < is->is_dvmabase || (va + PAGE_MASK) > is->is_dvmaend)
! 310: panic("iommu_enter: va %#lx not in DVMA space", va);
! 311:
! 312: tte = *tte_ptr;
! 313:
! 314: if (tte & IOTTE_V) {
! 315: printf("Overwriting valid tte entry (dva %lx pa %lx "
! 316: "&tte %p tte %llx)\n", va, pa, tte_ptr, tte);
! 317: extent_print(is->is_dvmamap);
! 318: panic("IOMMU overwrite");
! 319: }
! 320: #endif
! 321:
! 322: tte = MAKEIOTTE(pa, !(flags & BUS_DMA_NOWRITE),
! 323: !(flags & BUS_DMA_NOCACHE), (flags & BUS_DMA_STREAMING));
! 324:
! 325: DPRINTF(IDB_IOMMU, ("Clearing TSB slot %d for va %p\n",
! 326: (int)IOTSBSLOT(va,is->is_tsbsize), (void *)(u_long)va));
! 327:
! 328: *tte_ptr = tte;
! 329:
! 330: /*
! 331: * Why bother to flush this va? It should only be relevant for
! 332: * V ==> V or V ==> non-V transitions. The former is illegal and
! 333: * the latter is never done here. It is true that this provides
! 334: * some protection against a misbehaving master using an address
! 335: * after it should. The IOMMU documentations specifically warns
! 336: * that the consequences of a simultaneous IOMMU flush and DVMA
! 337: * access to the same address are undefined. (By that argument,
! 338: * the STC should probably be flushed as well.) Note that if
! 339: * a bus master keeps using a memory region after it has been
! 340: * unmapped, the specific behavior of the IOMMU is likely to
! 341: * be the least of our worries.
! 342: */
! 343: IOMMUREG_WRITE(is, iommu_flush, va);
! 344:
! 345: DPRINTF(IDB_IOMMU, ("iommu_enter: va %lx pa %lx TSB[%lx]@%p=%lx\n",
! 346: va, (long)pa, (u_long)IOTSBSLOT(va,is->is_tsbsize),
! 347: (void *)(u_long)&is->is_tsb[IOTSBSLOT(va,is->is_tsbsize)],
! 348: (u_long)tte));
! 349: }
! 350:
! 351: /*
! 352: * Remove an entry from the IOMMU table.
! 353: *
! 354: * The entry is flushed from the STC if an STC is detected and the TSB
! 355: * entry has the IOTTE_STREAM flags set. It should be impossible for
! 356: * the TSB entry to have this flag set without the BUS_DMA_STREAMING
! 357: * flag, but better to be safe. (The IOMMU will be ignored as long
! 358: * as an STC entry exists.)
! 359: */
! 360: void
! 361: iommu_remove(struct iommu_state *is, struct strbuf_ctl *sb, vaddr_t va)
! 362: {
! 363: int64_t *tte_ptr = &is->is_tsb[IOTSBSLOT(va, is->is_tsbsize)];
! 364: int64_t tte;
! 365:
! 366: #ifdef DIAGNOSTIC
! 367: if (va < is->is_dvmabase || (va + PAGE_MASK) > is->is_dvmaend)
! 368: panic("iommu_remove: va 0x%lx not in DVMA space", (u_long)va);
! 369: if (va != trunc_page(va)) {
! 370: printf("iommu_remove: unaligned va: %lx\n", va);
! 371: va = trunc_page(va);
! 372: }
! 373: #endif
! 374: tte = *tte_ptr;
! 375:
! 376: DPRINTF(IDB_IOMMU, ("iommu_remove: va %lx TSB[%llx]@%p\n",
! 377: va, tte, tte_ptr));
! 378:
! 379: #ifdef DIAGNOSTIC
! 380: if ((tte & IOTTE_V) == 0) {
! 381: printf("Removing invalid tte entry (dva %lx &tte %p "
! 382: "tte %llx)\n", va, tte_ptr, tte);
! 383: extent_print(is->is_dvmamap);
! 384: panic("IOMMU remove overwrite");
! 385: }
! 386: #endif
! 387:
! 388: *tte_ptr = tte & ~IOTTE_V;
! 389:
! 390: /*
! 391: * IO operations are strongly ordered WRT each other. It is
! 392: * unclear how they relate to normal memory accesses.
! 393: */
! 394: membar(StoreStore);
! 395:
! 396: IOMMUREG_WRITE(is, iommu_flush, va);
! 397:
! 398: if (sb && (tte & IOTTE_STREAM))
! 399: iommu_strbuf_flush(sb, va);
! 400:
! 401: /* Should we sync the iommu and stc here? */
! 402: }
! 403:
! 404: /*
! 405: * Find the physical address of a DVMA address (debug routine).
! 406: */
! 407: paddr_t
! 408: iommu_extract(struct iommu_state *is, vaddr_t dva)
! 409: {
! 410: int64_t tte = 0;
! 411:
! 412: if (dva >= is->is_dvmabase && dva <= is->is_dvmaend)
! 413: tte = is->is_tsb[IOTSBSLOT(dva, is->is_tsbsize)];
! 414:
! 415: return (tte & IOTTE_PAMASK);
! 416: }
! 417:
! 418: /*
! 419: * Lookup a TSB entry for a given DVMA (debug routine).
! 420: */
! 421: int64_t
! 422: iommu_lookup_tte(struct iommu_state *is, vaddr_t dva)
! 423: {
! 424: int64_t tte = 0;
! 425:
! 426: if (dva >= is->is_dvmabase && dva <= is->is_dvmaend)
! 427: tte = is->is_tsb[IOTSBSLOT(dva, is->is_tsbsize)];
! 428:
! 429: return (tte);
! 430: }
! 431:
! 432: /*
! 433: * Lookup a TSB entry at a given physical address (debug routine).
! 434: */
! 435: int64_t
! 436: iommu_fetch_tte(struct iommu_state *is, paddr_t pa)
! 437: {
! 438: int64_t tte = 0;
! 439:
! 440: if (pa >= is->is_ptsb && pa < is->is_ptsb +
! 441: (PAGE_SIZE << is->is_tsbsize))
! 442: tte = ldxa(pa, ASI_PHYS_CACHED);
! 443:
! 444: return (tte);
! 445: }
! 446:
! 447: /*
! 448: * Fetch a TSB entry with some sanity checking.
! 449: */
! 450: int64_t
! 451: iommu_tsb_entry(struct iommu_state *is, vaddr_t dva)
! 452: {
! 453: int64_t tte;
! 454:
! 455: if (dva < is->is_dvmabase || dva > is->is_dvmaend)
! 456: panic("invalid dva: %llx", (long long)dva);
! 457:
! 458: tte = is->is_tsb[IOTSBSLOT(dva,is->is_tsbsize)];
! 459:
! 460: if ((tte & IOTTE_V) == 0)
! 461: panic("iommu_tsb_entry: invalid entry %lx", dva);
! 462:
! 463: return (tte);
! 464: }
! 465:
! 466: /*
! 467: * Initiate and then block until an STC flush synchronization has completed.
! 468: */
! 469: int
! 470: iommu_strbuf_flush_done(struct iommu_map_state *ims)
! 471: {
! 472: struct strbuf_ctl *sb = ims->ims_sb;
! 473: struct strbuf_flush *sf = &ims->ims_flush;
! 474: struct timeval cur, flushtimeout;
! 475: struct timeval to = { 0, 500000 };
! 476: u_int64_t flush;
! 477: int timeout_started = 0;
! 478:
! 479: #ifdef DIAGNOSTIC
! 480: if (sb == NULL) {
! 481: panic("iommu_strbuf_flush_done: invalid flush buffer");
! 482: }
! 483: #endif
! 484:
! 485: /*
! 486: * Streaming buffer flushes:
! 487: *
! 488: * 1 Tell strbuf to flush by storing va to strbuf_pgflush.
! 489: * 2 Store 0 in flag
! 490: * 3 Store pointer to flag in flushsync
! 491: * 4 wait till flushsync becomes 0x1
! 492: *
! 493: * If it takes more than .5 sec, something went very, very wrong.
! 494: */
! 495:
! 496: /*
! 497: * If we're reading from ASI_PHYS_CACHED, then we'll write to
! 498: * it too. No need to tempt fate or learn about Si bugs or such.
! 499: * FreeBSD just uses normal "volatile" reads/writes...
! 500: */
! 501:
! 502: stxa(sf->sbf_flushpa, ASI_PHYS_CACHED, 0);
! 503:
! 504: /*
! 505: * Insure any previous strbuf operations are complete and that
! 506: * memory is initialized before the IOMMU uses it.
! 507: * Is this Needed? How are IO and memory operations ordered?
! 508: */
! 509: membar(StoreStore);
! 510:
! 511: bus_space_write_8(sb->sb_bustag, sb->sb_sb,
! 512: STRBUFREG(strbuf_flushsync), sf->sbf_flushpa);
! 513:
! 514: DPRINTF(IDB_IOMMU,
! 515: ("iommu_strbuf_flush_done: flush = %llx pa = %lx\n",
! 516: ldxa(sf->sbf_flushpa, ASI_PHYS_CACHED), sf->sbf_flushpa));
! 517:
! 518: membar(StoreLoad | Lookaside);
! 519:
! 520: for(;;) {
! 521: int i;
! 522:
! 523: /*
! 524: * Try to shave a few instruction cycles off the average
! 525: * latency by only checking the elapsed time every few
! 526: * fetches.
! 527: */
! 528: for (i = 0; i < 1000; ++i) {
! 529: membar(LoadLoad);
! 530: /* Bypass non-coherent D$ */
! 531: /* non-coherent...? Huh? */
! 532: flush = ldxa(sf->sbf_flushpa, ASI_PHYS_CACHED);
! 533:
! 534: if (flush) {
! 535: DPRINTF(IDB_IOMMU,
! 536: ("iommu_strbuf_flush_done: flushed\n"));
! 537: return (0);
! 538: }
! 539: }
! 540:
! 541: microtime(&cur);
! 542:
! 543: if (timeout_started) {
! 544: if (timercmp(&cur, &flushtimeout, >))
! 545: panic("STC timeout at %lx (%lld)",
! 546: sf->sbf_flushpa, flush);
! 547: } else {
! 548: timeradd(&cur, &to, &flushtimeout);
! 549:
! 550: timeout_started = 1;
! 551:
! 552: DPRINTF(IDB_IOMMU,
! 553: ("iommu_strbuf_flush_done: flush = %llx pa = %lx "
! 554: "now=%lx:%lx until = %lx:%lx\n",
! 555: ldxa(sf->sbf_flushpa, ASI_PHYS_CACHED),
! 556: sf->sbf_flushpa, cur.tv_sec, cur.tv_usec,
! 557: flushtimeout.tv_sec, flushtimeout.tv_usec));
! 558: }
! 559: }
! 560: }
! 561:
! 562: /*
! 563: * IOMMU DVMA operations, common to SBus and PCI.
! 564: */
! 565:
! 566: #define BUS_DMA_FIND_PARENT(t, fn) \
! 567: if (t->_parent == NULL) \
! 568: panic("null bus_dma parent (" #fn ")"); \
! 569: for (t = t->_parent; t->fn == NULL; t = t->_parent) \
! 570: if (t->_parent == NULL) \
! 571: panic("no bus_dma " #fn " located");
! 572:
! 573: int
! 574: iommu_dvmamap_create(bus_dma_tag_t t, bus_dma_tag_t t0, struct strbuf_ctl *sb,
! 575: bus_size_t size, int nsegments, bus_size_t maxsegsz, bus_size_t boundary,
! 576: int flags, bus_dmamap_t *dmamap)
! 577: {
! 578: int ret;
! 579: bus_dmamap_t map;
! 580: struct iommu_map_state *ims;
! 581:
! 582: BUS_DMA_FIND_PARENT(t, _dmamap_create);
! 583: ret = (*t->_dmamap_create)(t, t0, size, nsegments, maxsegsz, boundary,
! 584: flags, &map);
! 585:
! 586: if (ret)
! 587: return (ret);
! 588:
! 589: ims = iommu_iomap_create(atop(round_page(size)));
! 590:
! 591: if (ims == NULL) {
! 592: bus_dmamap_destroy(t0, map);
! 593: return (ENOMEM);
! 594: }
! 595:
! 596: ims->ims_sb = sb;
! 597: map->_dm_cookie = ims;
! 598:
! 599: #ifdef DIAGNOSTIC
! 600: if (ims->ims_sb == NULL)
! 601: panic("iommu_dvmamap_create: null sb");
! 602: if (ims->ims_sb->sb_iommu == NULL)
! 603: panic("iommu_dvmamap_create: null iommu");
! 604: #endif
! 605: *dmamap = map;
! 606:
! 607: return (0);
! 608: }
! 609:
! 610: void
! 611: iommu_dvmamap_destroy(bus_dma_tag_t t, bus_dma_tag_t t0, bus_dmamap_t map)
! 612: {
! 613: /*
! 614: * The specification (man page) requires a loaded
! 615: * map to be unloaded before it is destroyed.
! 616: */
! 617: if (map->dm_nsegs)
! 618: bus_dmamap_unload(t0, map);
! 619:
! 620: if (map->_dm_cookie)
! 621: iommu_iomap_destroy(map->_dm_cookie);
! 622: map->_dm_cookie = NULL;
! 623:
! 624: BUS_DMA_FIND_PARENT(t, _dmamap_destroy);
! 625: (*t->_dmamap_destroy)(t, t0, map);
! 626: }
! 627:
! 628: /*
! 629: * Load a contiguous kva buffer into a dmamap. The physical pages are
! 630: * not assumed to be contiguous. Two passes are made through the buffer
! 631: * and both call pmap_extract() for the same va->pa translations. It
! 632: * is possible to run out of pa->dvma mappings; the code should be smart
! 633: * enough to resize the iomap (when the "flags" permit allocation). It
! 634: * is trivial to compute the number of entries required (round the length
! 635: * up to the page size and then divide by the page size)...
! 636: */
! 637: int
! 638: iommu_dvmamap_load(bus_dma_tag_t t, bus_dma_tag_t t0, bus_dmamap_t map,
! 639: void *buf, bus_size_t buflen, struct proc *p, int flags)
! 640: {
! 641: int s;
! 642: int err = 0;
! 643: bus_size_t sgsize;
! 644: u_long dvmaddr, sgstart, sgend;
! 645: bus_size_t align, boundary;
! 646: struct iommu_state *is;
! 647: struct iommu_map_state *ims = map->_dm_cookie;
! 648: pmap_t pmap;
! 649:
! 650: #ifdef DIAGNOSTIC
! 651: if (ims == NULL)
! 652: panic("iommu_dvmamap_load: null map state");
! 653: #endif
! 654: #ifdef DEBUG
! 655: if (ims->ims_sb == NULL)
! 656: panic("iommu_dvmamap_load: null sb");
! 657: if (ims->ims_sb->sb_iommu == NULL)
! 658: panic("iommu_dvmamap_load: null iommu");
! 659: #endif /* DEBUG */
! 660: is = ims->ims_sb->sb_iommu;
! 661:
! 662: if (map->dm_nsegs) {
! 663: /*
! 664: * Is it still in use? _bus_dmamap_load should have taken care
! 665: * of this.
! 666: */
! 667: #ifdef DIAGNOSTIC
! 668: panic("iommu_dvmamap_load: map still in use");
! 669: #endif
! 670: bus_dmamap_unload(t0, map);
! 671: }
! 672:
! 673: /*
! 674: * Make sure that on error condition we return "no valid mappings".
! 675: */
! 676: map->dm_nsegs = 0;
! 677:
! 678: if (buflen < 1 || buflen > map->_dm_size) {
! 679: DPRINTF(IDB_BUSDMA,
! 680: ("iommu_dvmamap_load(): error %d > %d -- "
! 681: "map size exceeded!\n", (int)buflen, (int)map->_dm_size));
! 682: return (EINVAL);
! 683: }
! 684:
! 685: /*
! 686: * A boundary presented to bus_dmamem_alloc() takes precedence
! 687: * over boundary in the map.
! 688: */
! 689: if ((boundary = (map->dm_segs[0]._ds_boundary)) == 0)
! 690: boundary = map->_dm_boundary;
! 691: align = MAX(map->dm_segs[0]._ds_align, PAGE_SIZE);
! 692:
! 693: pmap = p ? p->p_vmspace->vm_map.pmap : pmap = pmap_kernel();
! 694:
! 695: /* Count up the total number of pages we need */
! 696: iommu_iomap_clear_pages(ims);
! 697: { /* Scope */
! 698: bus_addr_t a, aend;
! 699: bus_addr_t addr = (vaddr_t)buf;
! 700: int seg_len = buflen;
! 701:
! 702: aend = round_page(addr + seg_len);
! 703: for (a = trunc_page(addr); a < aend; a += PAGE_SIZE) {
! 704: paddr_t pa;
! 705:
! 706: if (pmap_extract(pmap, a, &pa) == FALSE) {
! 707: printf("iomap pmap error addr 0x%llx\n", a);
! 708: iommu_iomap_clear_pages(ims);
! 709: return (EFBIG);
! 710: }
! 711:
! 712: err = iommu_iomap_insert_page(ims, pa);
! 713: if (err) {
! 714: printf("iomap insert error: %d for "
! 715: "va 0x%llx pa 0x%lx "
! 716: "(buf %p len %lld/%llx)\n",
! 717: err, a, pa, buf, buflen, buflen);
! 718: iommu_dvmamap_print_map(t, is, map);
! 719: iommu_iomap_clear_pages(ims);
! 720: return (EFBIG);
! 721: }
! 722: }
! 723: }
! 724: sgsize = ims->ims_map.ipm_pagecnt * PAGE_SIZE;
! 725:
! 726: if (flags & BUS_DMA_24BIT) {
! 727: sgstart = MAX(is->is_dvmamap->ex_start, 0xff000000);
! 728: sgend = MIN(is->is_dvmamap->ex_end, 0xffffffff);
! 729: } else {
! 730: sgstart = is->is_dvmamap->ex_start;
! 731: sgend = is->is_dvmamap->ex_end;
! 732: }
! 733:
! 734: /*
! 735: * If our segment size is larger than the boundary we need to
! 736: * split the transfer up into little pieces ourselves.
! 737: */
! 738: s = splhigh();
! 739: err = extent_alloc_subregion(is->is_dvmamap, sgstart, sgend,
! 740: sgsize, align, 0, (sgsize > boundary) ? 0 : boundary,
! 741: EX_NOWAIT | EX_BOUNDZERO, (u_long *)&dvmaddr);
! 742: splx(s);
! 743:
! 744: #ifdef DEBUG
! 745: if (err || (dvmaddr == (bus_addr_t)-1)) {
! 746: printf("iommu_dvmamap_load(): extent_alloc(%d, %x) failed!\n",
! 747: (int)sgsize, flags);
! 748: #ifdef DDB
! 749: if (iommudebug & IDB_BREAK)
! 750: Debugger();
! 751: #endif
! 752: }
! 753: #endif
! 754: if (err != 0)
! 755: return (err);
! 756:
! 757: if (dvmaddr == (bus_addr_t)-1)
! 758: return (ENOMEM);
! 759:
! 760: /* Set the active DVMA map */
! 761: map->_dm_dvmastart = dvmaddr;
! 762: map->_dm_dvmasize = sgsize;
! 763:
! 764: map->dm_mapsize = buflen;
! 765:
! 766: #ifdef DEBUG
! 767: iommu_dvmamap_validate_map(t, is, map);
! 768: #endif
! 769:
! 770: if (iommu_iomap_load_map(is, ims, dvmaddr, flags))
! 771: return (EFBIG);
! 772:
! 773: { /* Scope */
! 774: bus_addr_t a, aend;
! 775: bus_addr_t addr = (vaddr_t)buf;
! 776: int seg_len = buflen;
! 777:
! 778: aend = round_page(addr + seg_len);
! 779: for (a = trunc_page(addr); a < aend; a += PAGE_SIZE) {
! 780: bus_addr_t pgstart;
! 781: bus_addr_t pgend;
! 782: paddr_t pa;
! 783: int pglen;
! 784:
! 785: /* Yuck... Redoing the same pmap_extract... */
! 786: if (pmap_extract(pmap, a, &pa) == FALSE) {
! 787: printf("iomap pmap error addr 0x%llx\n", a);
! 788: iommu_iomap_clear_pages(ims);
! 789: return (EFBIG);
! 790: }
! 791:
! 792: pgstart = pa | (MAX(a, addr) & PAGE_MASK);
! 793: pgend = pa | (MIN(a + PAGE_SIZE - 1,
! 794: addr + seg_len - 1) & PAGE_MASK);
! 795: pglen = pgend - pgstart + 1;
! 796:
! 797: if (pglen < 1)
! 798: continue;
! 799:
! 800: err = iommu_dvmamap_append_range(t, map, pgstart,
! 801: pglen, flags, boundary);
! 802: if (err == EFBIG)
! 803: return (err);
! 804: if (err) {
! 805: printf("iomap load seg page: %d for "
! 806: "va 0x%llx pa %lx (%llx - %llx) "
! 807: "for %d/0x%x\n",
! 808: err, a, pa, pgstart, pgend, pglen, pglen);
! 809: return (err);
! 810: }
! 811: }
! 812: }
! 813:
! 814: #ifdef DIAGNOSTIC
! 815: iommu_dvmamap_validate_map(t, is, map);
! 816: #endif
! 817:
! 818: #ifdef DEBUG
! 819: if (err)
! 820: printf("**** iommu_dvmamap_load failed with error %d\n",
! 821: err);
! 822:
! 823: if (err || (iommudebug & IDB_PRINT_MAP)) {
! 824: iommu_dvmamap_print_map(t, is, map);
! 825: #ifdef DDB
! 826: if (iommudebug & IDB_BREAK)
! 827: Debugger();
! 828: #endif
! 829: }
! 830: #endif
! 831:
! 832: return (err);
! 833: }
! 834:
! 835: /*
! 836: * Load a dvmamap from an array of segs or an mlist (if the first
! 837: * "segs" entry's mlist is non-null). It calls iommu_dvmamap_load_segs()
! 838: * or iommu_dvmamap_load_mlist() for part of the 2nd pass through the
! 839: * mapping. This is ugly. A better solution would probably be to have
! 840: * function pointers for implementing the traversal. That way, there
! 841: * could be one core load routine for each of the three required algorithms
! 842: * (buffer, seg, and mlist). That would also mean that the traversal
! 843: * algorithm would then only need one implementation for each algorithm
! 844: * instead of two (one for populating the iomap and one for populating
! 845: * the dvma map).
! 846: */
! 847: int
! 848: iommu_dvmamap_load_raw(bus_dma_tag_t t, bus_dma_tag_t t0, bus_dmamap_t map,
! 849: bus_dma_segment_t *segs, int nsegs, bus_size_t size, int flags)
! 850: {
! 851: int i, s;
! 852: int left;
! 853: int err = 0;
! 854: bus_size_t sgsize;
! 855: bus_size_t boundary, align;
! 856: u_long dvmaddr, sgstart, sgend;
! 857: struct iommu_state *is;
! 858: struct iommu_map_state *ims = map->_dm_cookie;
! 859:
! 860: #ifdef DIAGNOSTIC
! 861: if (ims == NULL)
! 862: panic("iommu_dvmamap_load_raw: null map state");
! 863: #endif
! 864: #ifdef DEBUG
! 865: if (ims->ims_sb == NULL)
! 866: panic("iommu_dvmamap_load_raw: null sb");
! 867: if (ims->ims_sb->sb_iommu == NULL)
! 868: panic("iommu_dvmamap_load_raw: null iommu");
! 869: #endif /* DEBUG */
! 870: is = ims->ims_sb->sb_iommu;
! 871:
! 872: if (map->dm_nsegs) {
! 873: /* Already in use?? */
! 874: #ifdef DIAGNOSTIC
! 875: panic("iommu_dvmamap_load_raw: map still in use");
! 876: #endif
! 877: bus_dmamap_unload(t0, map);
! 878: }
! 879:
! 880: /*
! 881: * A boundary presented to bus_dmamem_alloc() takes precedence
! 882: * over boundary in the map.
! 883: */
! 884: if ((boundary = segs[0]._ds_boundary) == 0)
! 885: boundary = map->_dm_boundary;
! 886:
! 887: align = MAX(segs[0]._ds_align, PAGE_SIZE);
! 888:
! 889: /*
! 890: * Make sure that on error condition we return "no valid mappings".
! 891: */
! 892: map->dm_nsegs = 0;
! 893:
! 894: iommu_iomap_clear_pages(ims);
! 895: if (segs[0]._ds_mlist) {
! 896: struct pglist *mlist = segs[0]._ds_mlist;
! 897: struct vm_page *m;
! 898: for (m = TAILQ_FIRST(mlist); m != NULL;
! 899: m = TAILQ_NEXT(m,pageq)) {
! 900: err = iommu_iomap_insert_page(ims, VM_PAGE_TO_PHYS(m));
! 901:
! 902: if(err) {
! 903: printf("iomap insert error: %d for "
! 904: "pa 0x%lx\n", err, VM_PAGE_TO_PHYS(m));
! 905: iommu_dvmamap_print_map(t, is, map);
! 906: iommu_iomap_clear_pages(ims);
! 907: return (EFBIG);
! 908: }
! 909: }
! 910: } else {
! 911: /* Count up the total number of pages we need */
! 912: for (i = 0, left = size; left > 0 && i < nsegs; i++) {
! 913: bus_addr_t a, aend;
! 914: bus_size_t len = segs[i].ds_len;
! 915: bus_addr_t addr = segs[i].ds_addr;
! 916: int seg_len = MIN(left, len);
! 917:
! 918: if (len < 1)
! 919: continue;
! 920:
! 921: aend = round_page(addr + seg_len);
! 922: for (a = trunc_page(addr); a < aend; a += PAGE_SIZE) {
! 923:
! 924: err = iommu_iomap_insert_page(ims, a);
! 925: if (err) {
! 926: printf("iomap insert error: %d for "
! 927: "pa 0x%llx\n", err, a);
! 928: iommu_dvmamap_print_map(t, is, map);
! 929: iommu_iomap_clear_pages(ims);
! 930: return (EFBIG);
! 931: }
! 932: }
! 933:
! 934: left -= seg_len;
! 935: }
! 936: }
! 937: sgsize = ims->ims_map.ipm_pagecnt * PAGE_SIZE;
! 938:
! 939: if (flags & BUS_DMA_24BIT) {
! 940: sgstart = MAX(is->is_dvmamap->ex_start, 0xff000000);
! 941: sgend = MIN(is->is_dvmamap->ex_end, 0xffffffff);
! 942: } else {
! 943: sgstart = is->is_dvmamap->ex_start;
! 944: sgend = is->is_dvmamap->ex_end;
! 945: }
! 946:
! 947: /*
! 948: * If our segment size is larger than the boundary we need to
! 949: * split the transfer up into little pieces ourselves.
! 950: */
! 951: s = splhigh();
! 952: err = extent_alloc_subregion(is->is_dvmamap, sgstart, sgend,
! 953: sgsize, align, 0, (sgsize > boundary) ? 0 : boundary,
! 954: EX_NOWAIT | EX_BOUNDZERO, (u_long *)&dvmaddr);
! 955: splx(s);
! 956:
! 957: if (err != 0)
! 958: return (err);
! 959:
! 960: #ifdef DEBUG
! 961: if (dvmaddr == (bus_addr_t)-1) {
! 962: printf("iommu_dvmamap_load_raw(): extent_alloc(%d, %x) "
! 963: "failed!\n", (int)sgsize, flags);
! 964: #ifdef DDB
! 965: if (iommudebug & IDB_BREAK)
! 966: Debugger();
! 967: #else
! 968: panic("");
! 969: #endif
! 970: }
! 971: #endif
! 972: if (dvmaddr == (bus_addr_t)-1)
! 973: return (ENOMEM);
! 974:
! 975: /* Set the active DVMA map */
! 976: map->_dm_dvmastart = dvmaddr;
! 977: map->_dm_dvmasize = sgsize;
! 978:
! 979: map->dm_mapsize = size;
! 980:
! 981: #ifdef DEBUG
! 982: iommu_dvmamap_validate_map(t, is, map);
! 983: #endif
! 984:
! 985: if (iommu_iomap_load_map(is, ims, dvmaddr, flags))
! 986: return (EFBIG);
! 987:
! 988: if (segs[0]._ds_mlist)
! 989: err = iommu_dvmamap_load_mlist(t, is, map, segs[0]._ds_mlist,
! 990: flags, size, boundary);
! 991: else
! 992: err = iommu_dvmamap_load_seg(t, is, map, segs, nsegs,
! 993: flags, size, boundary);
! 994:
! 995: if (err)
! 996: iommu_iomap_unload_map(is, ims);
! 997:
! 998: #ifdef DIAGNOSTIC
! 999: /* The map should be valid even if the load failed */
! 1000: if (iommu_dvmamap_validate_map(t, is, map)) {
! 1001: printf("load size %lld/0x%llx\n", size, size);
! 1002: if (segs[0]._ds_mlist)
! 1003: printf("mlist %p\n", segs[0]._ds_mlist);
! 1004: else {
! 1005: long tot_len = 0;
! 1006: long clip_len = 0;
! 1007: printf("segs %p nsegs %d\n", segs, nsegs);
! 1008:
! 1009: left = size;
! 1010: for(i = 0; i < nsegs; i++) {
! 1011: bus_size_t len = segs[i].ds_len;
! 1012: bus_addr_t addr = segs[i].ds_addr;
! 1013: int seg_len = MIN(left, len);
! 1014:
! 1015: printf("addr %llx len %lld/0x%llx seg_len "
! 1016: "%d/0x%x left %d/0x%x\n", addr, len, len,
! 1017: seg_len, seg_len, left, left);
! 1018:
! 1019: left -= seg_len;
! 1020:
! 1021: clip_len += seg_len;
! 1022: tot_len += segs[i].ds_len;
! 1023: }
! 1024: printf("total length %ld/0x%lx total seg. "
! 1025: "length %ld/0x%lx\n", tot_len, tot_len, clip_len,
! 1026: clip_len);
! 1027: }
! 1028:
! 1029: if (err == 0)
! 1030: err = 1;
! 1031: }
! 1032:
! 1033: #endif
! 1034:
! 1035: #ifdef DEBUG
! 1036: if (err)
! 1037: printf("**** iommu_dvmamap_load_raw failed with error %d\n",
! 1038: err);
! 1039:
! 1040: if (err || (iommudebug & IDB_PRINT_MAP)) {
! 1041: iommu_dvmamap_print_map(t, is, map);
! 1042: #ifdef DDB
! 1043: if (iommudebug & IDB_BREAK)
! 1044: Debugger();
! 1045: #endif
! 1046: }
! 1047: #endif
! 1048:
! 1049: return (err);
! 1050: }
! 1051:
! 1052: /*
! 1053: * Insert a range of addresses into a loaded map respecting the specified
! 1054: * boundary and alignment restrictions. The range is specified by its
! 1055: * physical address and length. The range cannot cross a page boundary.
! 1056: * This code (along with most of the rest of the function in this file)
! 1057: * assumes that the IOMMU page size is equal to PAGE_SIZE.
! 1058: */
! 1059: int
! 1060: iommu_dvmamap_append_range(bus_dma_tag_t t, bus_dmamap_t map, paddr_t pa,
! 1061: bus_size_t length, int flags, bus_size_t boundary)
! 1062: {
! 1063: struct iommu_map_state *ims = map->_dm_cookie;
! 1064: bus_addr_t sgstart, sgend, bd_mask;
! 1065: bus_dma_segment_t *seg = NULL;
! 1066: int i = map->dm_nsegs;
! 1067:
! 1068: #ifdef DEBUG
! 1069: if (ims == NULL)
! 1070: panic("iommu_dvmamap_append_range: null map state");
! 1071: #endif
! 1072:
! 1073: sgstart = iommu_iomap_translate(ims, pa);
! 1074: sgend = sgstart + length - 1;
! 1075:
! 1076: #ifdef DIAGNOSTIC
! 1077: if (sgstart == NULL || sgstart > sgend) {
! 1078: printf("append range invalid mapping for %lx "
! 1079: "(0x%llx - 0x%llx)\n", pa, sgstart, sgend);
! 1080: map->dm_nsegs = 0;
! 1081: return (EINVAL);
! 1082: }
! 1083: #endif
! 1084:
! 1085: #ifdef DEBUG
! 1086: if (trunc_page(sgstart) != trunc_page(sgend)) {
! 1087: printf("append range crossing page boundary! "
! 1088: "pa %lx length %lld/0x%llx sgstart %llx sgend %llx\n",
! 1089: pa, length, length, sgstart, sgend);
! 1090: }
! 1091: #endif
! 1092:
! 1093: /*
! 1094: * We will attempt to merge this range with the previous entry
! 1095: * (if there is one).
! 1096: */
! 1097: if (i > 0) {
! 1098: seg = &map->dm_segs[i - 1];
! 1099: if (sgstart == seg->ds_addr + seg->ds_len) {
! 1100: length += seg->ds_len;
! 1101: sgstart = seg->ds_addr;
! 1102: sgend = sgstart + length - 1;
! 1103: } else
! 1104: seg = NULL;
! 1105: }
! 1106:
! 1107: if (seg == NULL) {
! 1108: seg = &map->dm_segs[i];
! 1109: if (++i > map->_dm_segcnt) {
! 1110: map->dm_nsegs = 0;
! 1111: return (EFBIG);
! 1112: }
! 1113: }
! 1114:
! 1115: /*
! 1116: * At this point, "i" is the index of the *next* bus_dma_segment_t
! 1117: * (the segment count, aka map->dm_nsegs) and "seg" points to the
! 1118: * *current* entry. "length", "sgstart", and "sgend" reflect what
! 1119: * we intend to put in "*seg". No assumptions should be made about
! 1120: * the contents of "*seg". Only "boundary" issue can change this
! 1121: * and "boundary" is often zero, so explicitly test for that case
! 1122: * (the test is strictly an optimization).
! 1123: */
! 1124: if (boundary != 0) {
! 1125: bd_mask = ~(boundary - 1);
! 1126:
! 1127: while ((sgstart & bd_mask) != (sgend & bd_mask)) {
! 1128: /*
! 1129: * We are crossing a boundary so fill in the current
! 1130: * segment with as much as possible, then grab a new
! 1131: * one.
! 1132: */
! 1133:
! 1134: seg->ds_addr = sgstart;
! 1135: seg->ds_len = boundary - (sgstart & bd_mask);
! 1136:
! 1137: sgstart += seg->ds_len; /* sgend stays the same */
! 1138: length -= seg->ds_len;
! 1139:
! 1140: seg = &map->dm_segs[i];
! 1141: if (++i > map->_dm_segcnt) {
! 1142: map->dm_nsegs = 0;
! 1143: return (EFBIG);
! 1144: }
! 1145: }
! 1146: }
! 1147:
! 1148: seg->ds_addr = sgstart;
! 1149: seg->ds_len = length;
! 1150: map->dm_nsegs = i;
! 1151:
! 1152: return (0);
! 1153: }
! 1154:
! 1155: /*
! 1156: * Populate the iomap from a bus_dma_segment_t array. See note for
! 1157: * iommu_dvmamap_load() * regarding page entry exhaustion of the iomap.
! 1158: * This is less of a problem for load_seg, as the number of pages
! 1159: * is usually similar to the number of segments (nsegs).
! 1160: */
! 1161: int
! 1162: iommu_dvmamap_load_seg(bus_dma_tag_t t, struct iommu_state *is,
! 1163: bus_dmamap_t map, bus_dma_segment_t *segs, int nsegs, int flags,
! 1164: bus_size_t size, bus_size_t boundary)
! 1165: {
! 1166: int i;
! 1167: int left;
! 1168: int seg;
! 1169:
! 1170: /*
! 1171: * This segs is made up of individual physical
! 1172: * segments, probably by _bus_dmamap_load_uio() or
! 1173: * _bus_dmamap_load_mbuf(). Ignore the mlist and
! 1174: * load each one individually.
! 1175: */
! 1176:
! 1177: /*
! 1178: * Keep in mind that each segment could span
! 1179: * multiple pages and that these are not always
! 1180: * adjacent. The code is no longer adding dvma
! 1181: * aliases to the IOMMU. The STC will not cross
! 1182: * page boundaries anyway and a IOMMU table walk
! 1183: * vs. what may be a streamed PCI DMA to a ring
! 1184: * descriptor is probably a wash. It eases TLB
! 1185: * pressure and in the worst possible case, it is
! 1186: * only as bad a non-IOMMUed architecture. More
! 1187: * importantly, the code is not quite as hairy.
! 1188: * (It's bad enough as it is.)
! 1189: */
! 1190: left = size;
! 1191: seg = 0;
! 1192: for (i = 0; left > 0 && i < nsegs; i++) {
! 1193: bus_addr_t a, aend;
! 1194: bus_size_t len = segs[i].ds_len;
! 1195: bus_addr_t addr = segs[i].ds_addr;
! 1196: int seg_len = MIN(left, len);
! 1197:
! 1198: if (len < 1)
! 1199: continue;
! 1200:
! 1201: aend = round_page(addr + seg_len);
! 1202: for (a = trunc_page(addr); a < aend; a += PAGE_SIZE) {
! 1203: bus_addr_t pgstart;
! 1204: bus_addr_t pgend;
! 1205: int pglen;
! 1206: int err;
! 1207:
! 1208: pgstart = MAX(a, addr);
! 1209: pgend = MIN(a + PAGE_SIZE - 1, addr + seg_len - 1);
! 1210: pglen = pgend - pgstart + 1;
! 1211:
! 1212: if (pglen < 1)
! 1213: continue;
! 1214:
! 1215: err = iommu_dvmamap_append_range(t, map, pgstart,
! 1216: pglen, flags, boundary);
! 1217: if (err == EFBIG)
! 1218: return (err);
! 1219: if (err) {
! 1220: printf("iomap load seg page: %d for "
! 1221: "pa 0x%llx (%llx - %llx for %d/%x\n",
! 1222: err, a, pgstart, pgend, pglen, pglen);
! 1223: return (err);
! 1224: }
! 1225:
! 1226: }
! 1227:
! 1228: left -= seg_len;
! 1229: }
! 1230: return (0);
! 1231: }
! 1232:
! 1233: /*
! 1234: * Populate the iomap from an mlist. See note for iommu_dvmamap_load()
! 1235: * regarding page entry exhaustion of the iomap.
! 1236: */
! 1237: int
! 1238: iommu_dvmamap_load_mlist(bus_dma_tag_t t, struct iommu_state *is,
! 1239: bus_dmamap_t map, struct pglist *mlist, int flags,
! 1240: bus_size_t size, bus_size_t boundary)
! 1241: {
! 1242: struct vm_page *m;
! 1243: paddr_t pa;
! 1244: int err;
! 1245:
! 1246: /*
! 1247: * This was allocated with bus_dmamem_alloc.
! 1248: * The pages are on an `mlist'.
! 1249: */
! 1250: for (m = TAILQ_FIRST(mlist); m != NULL; m = TAILQ_NEXT(m,pageq)) {
! 1251: pa = VM_PAGE_TO_PHYS(m);
! 1252:
! 1253: err = iommu_dvmamap_append_range(t, map, pa, PAGE_SIZE,
! 1254: flags, boundary);
! 1255: if (err == EFBIG)
! 1256: return (err);
! 1257: if (err) {
! 1258: printf("iomap load seg page: %d for pa 0x%lx "
! 1259: "(%lx - %lx for %d/%x\n", err, pa, pa,
! 1260: pa + PAGE_SIZE, PAGE_SIZE, PAGE_SIZE);
! 1261: return (err);
! 1262: }
! 1263: }
! 1264:
! 1265: return (0);
! 1266: }
! 1267:
! 1268: /*
! 1269: * Unload a dvmamap.
! 1270: */
! 1271: void
! 1272: iommu_dvmamap_unload(bus_dma_tag_t t, bus_dma_tag_t t0, bus_dmamap_t map)
! 1273: {
! 1274: struct iommu_state *is;
! 1275: struct iommu_map_state *ims = map->_dm_cookie;
! 1276: bus_addr_t dvmaddr = map->_dm_dvmastart;
! 1277: bus_size_t sgsize = map->_dm_dvmasize;
! 1278: int error, s;
! 1279:
! 1280: #ifdef DEBUG
! 1281: if (ims == NULL)
! 1282: panic("iommu_dvmamap_unload: null map state");
! 1283: if (ims->ims_sb == NULL)
! 1284: panic("iommu_dvmamap_unload: null sb");
! 1285: if (ims->ims_sb->sb_iommu == NULL)
! 1286: panic("iommu_dvmamap_unload: null iommu");
! 1287: #endif /* DEBUG */
! 1288:
! 1289: is = ims->ims_sb->sb_iommu;
! 1290:
! 1291: /* Flush the iommu */
! 1292: #ifdef DEBUG
! 1293: if (dvmaddr == 0) {
! 1294: printf("iommu_dvmamap_unload: No dvmastart\n");
! 1295: #ifdef DDB
! 1296: if (iommudebug & IDB_BREAK)
! 1297: Debugger();
! 1298: #endif
! 1299: return;
! 1300: }
! 1301: iommu_dvmamap_validate_map(t, is, map);
! 1302:
! 1303: if (iommudebug & IDB_PRINT_MAP)
! 1304: iommu_dvmamap_print_map(t, is, map);
! 1305: #endif /* DEBUG */
! 1306:
! 1307: /* Remove the IOMMU entries */
! 1308: iommu_iomap_unload_map(is, ims);
! 1309:
! 1310: /* Clear the iomap */
! 1311: iommu_iomap_clear_pages(ims);
! 1312:
! 1313: bus_dmamap_unload(t->_parent, map);
! 1314:
! 1315: /* Mark the mappings as invalid. */
! 1316: map->dm_mapsize = 0;
! 1317: map->dm_nsegs = 0;
! 1318:
! 1319: s = splhigh();
! 1320: error = extent_free(is->is_dvmamap, dvmaddr,
! 1321: sgsize, EX_NOWAIT);
! 1322: map->_dm_dvmastart = 0;
! 1323: map->_dm_dvmasize = 0;
! 1324: splx(s);
! 1325: if (error != 0)
! 1326: printf("warning: %qd of DVMA space lost\n", sgsize);
! 1327: }
! 1328:
! 1329: /*
! 1330: * Perform internal consistency checking on a dvmamap.
! 1331: */
! 1332: int
! 1333: iommu_dvmamap_validate_map(bus_dma_tag_t t, struct iommu_state *is,
! 1334: bus_dmamap_t map)
! 1335: {
! 1336: int err = 0;
! 1337: int seg;
! 1338:
! 1339: if (trunc_page(map->_dm_dvmastart) != map->_dm_dvmastart) {
! 1340: printf("**** dvmastart address not page aligned: %llx",
! 1341: map->_dm_dvmastart);
! 1342: err = 1;
! 1343: }
! 1344: if (trunc_page(map->_dm_dvmasize) != map->_dm_dvmasize) {
! 1345: printf("**** dvmasize not a multiple of page size: %llx",
! 1346: map->_dm_dvmasize);
! 1347: err = 1;
! 1348: }
! 1349: if (map->_dm_dvmastart < is->is_dvmabase ||
! 1350: (round_page(map->_dm_dvmastart + map->_dm_dvmasize) - 1) >
! 1351: is->is_dvmaend) {
! 1352: printf("dvmaddr %llx len %llx out of range %x - %x\n",
! 1353: map->_dm_dvmastart, map->_dm_dvmasize,
! 1354: is->is_dvmabase, is->is_dvmaend);
! 1355: err = 1;
! 1356: }
! 1357: for (seg = 0; seg < map->dm_nsegs; seg++) {
! 1358: if (map->dm_segs[seg].ds_addr == 0 ||
! 1359: map->dm_segs[seg].ds_len == 0) {
! 1360: printf("seg %d null segment dvmaddr %llx len %llx for "
! 1361: "range %llx len %llx\n",
! 1362: seg,
! 1363: map->dm_segs[seg].ds_addr,
! 1364: map->dm_segs[seg].ds_len,
! 1365: map->_dm_dvmastart, map->_dm_dvmasize);
! 1366: err = 1;
! 1367: } else if (map->dm_segs[seg].ds_addr < map->_dm_dvmastart ||
! 1368: round_page(map->dm_segs[seg].ds_addr +
! 1369: map->dm_segs[seg].ds_len) >
! 1370: map->_dm_dvmastart + map->_dm_dvmasize) {
! 1371: printf("seg %d dvmaddr %llx len %llx out of "
! 1372: "range %llx len %llx\n",
! 1373: seg,
! 1374: map->dm_segs[seg].ds_addr,
! 1375: map->dm_segs[seg].ds_len,
! 1376: map->_dm_dvmastart, map->_dm_dvmasize);
! 1377: err = 1;
! 1378: }
! 1379: }
! 1380:
! 1381: if (err) {
! 1382: iommu_dvmamap_print_map(t, is, map);
! 1383: #if defined(DDB) && defined(DEBUG)
! 1384: if (iommudebug & IDB_BREAK)
! 1385: Debugger();
! 1386: #endif
! 1387: }
! 1388:
! 1389: return (err);
! 1390: }
! 1391:
! 1392: void
! 1393: iommu_dvmamap_print_map(bus_dma_tag_t t, struct iommu_state *is,
! 1394: bus_dmamap_t map)
! 1395: {
! 1396: int seg, i;
! 1397: long full_len, source_len;
! 1398: struct mbuf *m;
! 1399:
! 1400: printf("DVMA %x for %x, mapping %p: dvstart %llx dvsize %llx "
! 1401: "size %lld/%llx maxsegsz %llx boundary %llx segcnt %d "
! 1402: "flags %x type %d source %p "
! 1403: "cookie %p mapsize %llx nsegs %d\n",
! 1404: is ? is->is_dvmabase : 0, is ? is->is_dvmaend : 0, map,
! 1405: map->_dm_dvmastart, map->_dm_dvmasize,
! 1406: map->_dm_size, map->_dm_size, map->_dm_maxsegsz, map->_dm_boundary,
! 1407: map->_dm_segcnt, map->_dm_flags, map->_dm_type,
! 1408: map->_dm_source, map->_dm_cookie, map->dm_mapsize,
! 1409: map->dm_nsegs);
! 1410:
! 1411: full_len = 0;
! 1412: for (seg = 0; seg < map->dm_nsegs; seg++) {
! 1413: printf("seg %d dvmaddr %llx pa %lx len %llx (tte %llx)\n",
! 1414: seg, map->dm_segs[seg].ds_addr,
! 1415: is ? iommu_extract(is, map->dm_segs[seg].ds_addr) : 0,
! 1416: map->dm_segs[seg].ds_len,
! 1417: is ? iommu_lookup_tte(is, map->dm_segs[seg].ds_addr) : 0);
! 1418: full_len += map->dm_segs[seg].ds_len;
! 1419: }
! 1420: printf("total length = %ld/0x%lx\n", full_len, full_len);
! 1421:
! 1422: if (map->_dm_source) switch (map->_dm_type) {
! 1423: case _DM_TYPE_MBUF:
! 1424: m = map->_dm_source;
! 1425: if (m->m_flags & M_PKTHDR)
! 1426: printf("source PKTHDR mbuf (%p) hdr len = %d/0x%x:\n",
! 1427: m, m->m_pkthdr.len, m->m_pkthdr.len);
! 1428: else
! 1429: printf("source mbuf (%p):\n", m);
! 1430:
! 1431: source_len = 0;
! 1432: for ( ; m; m = m->m_next) {
! 1433: vaddr_t vaddr = mtod(m, vaddr_t);
! 1434: long len = m->m_len;
! 1435: paddr_t pa;
! 1436:
! 1437: if (pmap_extract(pmap_kernel(), vaddr, &pa))
! 1438: printf("kva %lx pa %lx len %ld/0x%lx\n",
! 1439: vaddr, pa, len, len);
! 1440: else
! 1441: printf("kva %lx pa <invalid> len %ld/0x%lx\n",
! 1442: vaddr, len, len);
! 1443:
! 1444: source_len += len;
! 1445: }
! 1446:
! 1447: if (full_len != source_len)
! 1448: printf("mbuf length %ld/0x%lx is %s than mapping "
! 1449: "length %ld/0x%lx\n", source_len, source_len,
! 1450: (source_len > full_len) ? "greater" : "less",
! 1451: full_len, full_len);
! 1452: else
! 1453: printf("mbuf length %ld/0x%lx\n", source_len,
! 1454: source_len);
! 1455: break;
! 1456: case _DM_TYPE_LOAD:
! 1457: case _DM_TYPE_SEGS:
! 1458: case _DM_TYPE_UIO:
! 1459: default:
! 1460: break;
! 1461: }
! 1462:
! 1463: if (map->_dm_cookie) {
! 1464: struct iommu_map_state *ims = map->_dm_cookie;
! 1465: struct iommu_page_map *ipm = &ims->ims_map;
! 1466:
! 1467: printf("page map (%p) of size %d with %d entries\n",
! 1468: ipm, ipm->ipm_maxpage, ipm->ipm_pagecnt);
! 1469: for (i = 0; i < ipm->ipm_pagecnt; ++i) {
! 1470: struct iommu_page_entry *e = &ipm->ipm_map[i];
! 1471: printf("%d: vmaddr 0x%lx pa 0x%lx\n", i,
! 1472: e->ipe_va, e->ipe_pa);
! 1473: }
! 1474: } else
! 1475: printf("iommu map state (cookie) is NULL\n");
! 1476: }
! 1477:
! 1478: void
! 1479: _iommu_dvmamap_sync(bus_dma_tag_t t, bus_dma_tag_t t0, bus_dmamap_t map,
! 1480: bus_addr_t offset, bus_size_t len, int ops)
! 1481: {
! 1482: struct iommu_state *is;
! 1483: struct iommu_map_state *ims = map->_dm_cookie;
! 1484: struct strbuf_ctl *sb;
! 1485: bus_size_t count;
! 1486: int i, needsflush = 0;
! 1487:
! 1488: sb = ims->ims_sb;
! 1489: is = sb->sb_iommu;
! 1490:
! 1491: for (i = 0; i < map->dm_nsegs; i++) {
! 1492: if (offset < map->dm_segs[i].ds_len)
! 1493: break;
! 1494: offset -= map->dm_segs[i].ds_len;
! 1495: }
! 1496:
! 1497: if (i == map->dm_nsegs)
! 1498: panic("iommu_dvmamap_sync: too short %llu", offset);
! 1499:
! 1500: for (; len > 0 && i < map->dm_nsegs; i++) {
! 1501: count = MIN(map->dm_segs[i].ds_len - offset, len);
! 1502: if (count > 0 && iommu_dvmamap_sync_range(sb,
! 1503: map->dm_segs[i].ds_addr + offset, count))
! 1504: needsflush = 1;
! 1505: len -= count;
! 1506: }
! 1507:
! 1508: #ifdef DIAGNOSTIC
! 1509: if (i == map->dm_nsegs && len > 0)
! 1510: panic("iommu_dvmamap_sync: leftover %llu", len);
! 1511: #endif
! 1512:
! 1513: if (needsflush)
! 1514: iommu_strbuf_flush_done(ims);
! 1515: }
! 1516:
! 1517: void
! 1518: iommu_dvmamap_sync(bus_dma_tag_t t, bus_dma_tag_t t0, bus_dmamap_t map,
! 1519: bus_addr_t offset, bus_size_t len, int ops)
! 1520: {
! 1521: struct iommu_map_state *ims = map->_dm_cookie;
! 1522:
! 1523: #ifdef DIAGNOSTIC
! 1524: if (ims == NULL)
! 1525: panic("iommu_dvmamap_sync: null map state");
! 1526: if (ims->ims_sb == NULL)
! 1527: panic("iommu_dvmamap_sync: null sb");
! 1528: if (ims->ims_sb->sb_iommu == NULL)
! 1529: panic("iommu_dvmamap_sync: null iommu");
! 1530: #endif
! 1531: if (len == 0)
! 1532: return;
! 1533:
! 1534: if (ops & BUS_DMASYNC_PREWRITE)
! 1535: membar(MemIssue);
! 1536:
! 1537: if ((ims->ims_flags & IOMMU_MAP_STREAM) &&
! 1538: (ops & (BUS_DMASYNC_POSTREAD | BUS_DMASYNC_PREWRITE)))
! 1539: _iommu_dvmamap_sync(t, t0, map, offset, len, ops);
! 1540:
! 1541: if (ops & BUS_DMASYNC_POSTREAD)
! 1542: membar(MemIssue);
! 1543: }
! 1544:
! 1545: /*
! 1546: * Flush an individual dma segment, returns non-zero if the streaming buffers
! 1547: * need flushing afterwards.
! 1548: */
! 1549: int
! 1550: iommu_dvmamap_sync_range(struct strbuf_ctl *sb, vaddr_t va, bus_size_t len)
! 1551: {
! 1552: vaddr_t vaend;
! 1553: #ifdef DIAGNOSTIC
! 1554: struct iommu_state *is = sb->sb_iommu;
! 1555:
! 1556: if (va < is->is_dvmabase || va > is->is_dvmaend)
! 1557: panic("invalid va: %llx", (long long)va);
! 1558:
! 1559: if ((is->is_tsb[IOTSBSLOT(va, is->is_tsbsize)] & IOTTE_STREAM) == 0) {
! 1560: printf("iommu_dvmamap_sync_range: attempting to flush "
! 1561: "non-streaming entry\n");
! 1562: return (0);
! 1563: }
! 1564: #endif
! 1565:
! 1566: vaend = (va + len + PAGE_MASK) & ~PAGE_MASK;
! 1567: va &= ~PAGE_MASK;
! 1568:
! 1569: #ifdef DIAGNOSTIC
! 1570: if (va < is->is_dvmabase || (vaend - 1) > is->is_dvmaend)
! 1571: panic("invalid va range: %llx to %llx (%x to %x)",
! 1572: (long long)va, (long long)vaend,
! 1573: is->is_dvmabase,
! 1574: is->is_dvmaend);
! 1575: #endif
! 1576:
! 1577: for ( ; va <= vaend; va += PAGE_SIZE) {
! 1578: DPRINTF(IDB_BUSDMA,
! 1579: ("iommu_dvmamap_sync_range: flushing va %p\n",
! 1580: (void *)(u_long)va));
! 1581: iommu_strbuf_flush(sb, va);
! 1582: }
! 1583:
! 1584: return (1);
! 1585: }
! 1586:
! 1587: int
! 1588: iommu_dvmamem_alloc(bus_dma_tag_t t, bus_dma_tag_t t0, bus_size_t size,
! 1589: bus_size_t alignment, bus_size_t boundary, bus_dma_segment_t *segs,
! 1590: int nsegs, int *rsegs, int flags)
! 1591: {
! 1592:
! 1593: DPRINTF(IDB_BUSDMA, ("iommu_dvmamem_alloc: sz %llx align %llx "
! 1594: "bound %llx segp %p flags %d\n", (unsigned long long)size,
! 1595: (unsigned long long)alignment, (unsigned long long)boundary,
! 1596: segs, flags));
! 1597: BUS_DMA_FIND_PARENT(t, _dmamem_alloc);
! 1598: return ((*t->_dmamem_alloc)(t, t0, size, alignment, boundary,
! 1599: segs, nsegs, rsegs, flags | BUS_DMA_DVMA));
! 1600: }
! 1601:
! 1602: void
! 1603: iommu_dvmamem_free(bus_dma_tag_t t, bus_dma_tag_t t0, bus_dma_segment_t *segs,
! 1604: int nsegs)
! 1605: {
! 1606:
! 1607: DPRINTF(IDB_BUSDMA, ("iommu_dvmamem_free: segp %p nsegs %d\n",
! 1608: segs, nsegs));
! 1609: BUS_DMA_FIND_PARENT(t, _dmamem_free);
! 1610: (*t->_dmamem_free)(t, t0, segs, nsegs);
! 1611: }
! 1612:
! 1613: /*
! 1614: * Map the DVMA mappings into the kernel pmap.
! 1615: * Check the flags to see whether we're streaming or coherent.
! 1616: */
! 1617: int
! 1618: iommu_dvmamem_map(bus_dma_tag_t t, bus_dma_tag_t t0, bus_dma_segment_t *segs,
! 1619: int nsegs, size_t size, caddr_t *kvap, int flags)
! 1620: {
! 1621: struct vm_page *m;
! 1622: vaddr_t va;
! 1623: bus_addr_t addr;
! 1624: struct pglist *mlist;
! 1625: bus_addr_t cbit = 0;
! 1626:
! 1627: DPRINTF(IDB_BUSDMA, ("iommu_dvmamem_map: segp %p nsegs %d size %lx\n",
! 1628: segs, nsegs, size));
! 1629:
! 1630: /*
! 1631: * Allocate some space in the kernel map, and then map these pages
! 1632: * into this space.
! 1633: */
! 1634: size = round_page(size);
! 1635: va = uvm_km_valloc(kernel_map, size);
! 1636: if (va == 0)
! 1637: return (ENOMEM);
! 1638:
! 1639: *kvap = (caddr_t)va;
! 1640:
! 1641: /*
! 1642: * digest flags:
! 1643: */
! 1644: #if 0
! 1645: if (flags & BUS_DMA_COHERENT) /* Disable vcache */
! 1646: cbit |= PMAP_NVC;
! 1647: #endif
! 1648: if (flags & BUS_DMA_NOCACHE) /* sideffects */
! 1649: cbit |= PMAP_NC;
! 1650:
! 1651: /*
! 1652: * Now take this and map it into the CPU.
! 1653: */
! 1654: mlist = segs[0]._ds_mlist;
! 1655: TAILQ_FOREACH(m, mlist, pageq) {
! 1656: #ifdef DIAGNOSTIC
! 1657: if (size == 0)
! 1658: panic("iommu_dvmamem_map: size botch");
! 1659: #endif
! 1660: addr = VM_PAGE_TO_PHYS(m);
! 1661: DPRINTF(IDB_BUSDMA, ("iommu_dvmamem_map: "
! 1662: "mapping va %lx at %llx\n", va,
! 1663: (unsigned long long)addr | cbit));
! 1664: pmap_enter(pmap_kernel(), va, addr | cbit,
! 1665: VM_PROT_READ | VM_PROT_WRITE,
! 1666: VM_PROT_READ | VM_PROT_WRITE | PMAP_WIRED);
! 1667: va += PAGE_SIZE;
! 1668: size -= PAGE_SIZE;
! 1669: }
! 1670: pmap_update(pmap_kernel());
! 1671:
! 1672: return (0);
! 1673: }
! 1674:
! 1675: /*
! 1676: * Unmap DVMA mappings from kernel
! 1677: */
! 1678: void
! 1679: iommu_dvmamem_unmap(bus_dma_tag_t t, bus_dma_tag_t t0, caddr_t kva,
! 1680: size_t size)
! 1681: {
! 1682:
! 1683: DPRINTF(IDB_BUSDMA, ("iommu_dvmamem_unmap: kvm %p size %lx\n",
! 1684: kva, size));
! 1685:
! 1686: #ifdef DIAGNOSTIC
! 1687: if ((u_long)kva & PAGE_MASK)
! 1688: panic("iommu_dvmamem_unmap");
! 1689: #endif
! 1690:
! 1691: size = round_page(size);
! 1692: pmap_remove(pmap_kernel(), (vaddr_t)kva, size);
! 1693: pmap_update(pmap_kernel());
! 1694: uvm_km_free(kernel_map, (vaddr_t)kva, size);
! 1695: }
! 1696:
! 1697: /*
! 1698: * Create a new iomap.
! 1699: */
! 1700: struct iommu_map_state *
! 1701: iommu_iomap_create(int n)
! 1702: {
! 1703: struct iommu_map_state *ims;
! 1704: struct strbuf_flush *sbf;
! 1705: vaddr_t va;
! 1706:
! 1707: /* Safety for heavily fragmented data, such as mbufs */
! 1708: n += 4;
! 1709: if (n < 16)
! 1710: n = 16;
! 1711:
! 1712: ims = malloc(sizeof(*ims) + (n - 1) * sizeof(ims->ims_map.ipm_map[0]),
! 1713: M_DEVBUF, M_NOWAIT);
! 1714: if (ims == NULL)
! 1715: return (NULL);
! 1716:
! 1717: memset(ims, 0, sizeof *ims);
! 1718:
! 1719: /* Initialize the map. */
! 1720: ims->ims_map.ipm_maxpage = n;
! 1721: SPLAY_INIT(&ims->ims_map.ipm_tree);
! 1722:
! 1723: /* Initialize the flush area. */
! 1724: sbf = &ims->ims_flush;
! 1725: va = (vaddr_t)&sbf->sbf_area[0x40];
! 1726: va &= ~0x3f;
! 1727: pmap_extract(pmap_kernel(), va, &sbf->sbf_flushpa);
! 1728: sbf->sbf_flush = (void *)va;
! 1729:
! 1730: return (ims);
! 1731: }
! 1732:
! 1733: /*
! 1734: * Destroy an iomap.
! 1735: */
! 1736: void
! 1737: iommu_iomap_destroy(struct iommu_map_state *ims)
! 1738: {
! 1739: #ifdef DIAGNOSTIC
! 1740: if (ims->ims_map.ipm_pagecnt > 0)
! 1741: printf("iommu_iomap_destroy: %d page entries in use\n",
! 1742: ims->ims_map.ipm_pagecnt);
! 1743: #endif
! 1744:
! 1745: free(ims, M_DEVBUF);
! 1746: }
! 1747:
! 1748: /*
! 1749: * Utility function used by splay tree to order page entries by pa.
! 1750: */
! 1751: static inline int
! 1752: iomap_compare(struct iommu_page_entry *a, struct iommu_page_entry *b)
! 1753: {
! 1754: return ((a->ipe_pa > b->ipe_pa) ? 1 :
! 1755: (a->ipe_pa < b->ipe_pa) ? -1 : 0);
! 1756: }
! 1757:
! 1758: SPLAY_PROTOTYPE(iommu_page_tree, iommu_page_entry, ipe_node, iomap_compare);
! 1759:
! 1760: SPLAY_GENERATE(iommu_page_tree, iommu_page_entry, ipe_node, iomap_compare);
! 1761:
! 1762: /*
! 1763: * Insert a pa entry in the iomap.
! 1764: */
! 1765: int
! 1766: iommu_iomap_insert_page(struct iommu_map_state *ims, paddr_t pa)
! 1767: {
! 1768: struct iommu_page_map *ipm = &ims->ims_map;
! 1769: struct iommu_page_entry *e;
! 1770:
! 1771: if (ipm->ipm_pagecnt >= ipm->ipm_maxpage) {
! 1772: struct iommu_page_entry ipe;
! 1773:
! 1774: ipe.ipe_pa = pa;
! 1775: if (SPLAY_FIND(iommu_page_tree, &ipm->ipm_tree, &ipe))
! 1776: return (0);
! 1777:
! 1778: return (ENOMEM);
! 1779: }
! 1780:
! 1781: e = &ipm->ipm_map[ipm->ipm_pagecnt];
! 1782:
! 1783: e->ipe_pa = pa;
! 1784: e->ipe_va = NULL;
! 1785:
! 1786: e = SPLAY_INSERT(iommu_page_tree, &ipm->ipm_tree, e);
! 1787:
! 1788: /* Duplicates are okay, but only count them once. */
! 1789: if (e)
! 1790: return (0);
! 1791:
! 1792: ++ipm->ipm_pagecnt;
! 1793:
! 1794: return (0);
! 1795: }
! 1796:
! 1797: /*
! 1798: * Locate the iomap by filling in the pa->va mapping and inserting it
! 1799: * into the IOMMU tables.
! 1800: */
! 1801: int
! 1802: iommu_iomap_load_map(struct iommu_state *is, struct iommu_map_state *ims,
! 1803: vaddr_t vmaddr, int flags)
! 1804: {
! 1805: struct iommu_page_map *ipm = &ims->ims_map;
! 1806: struct iommu_page_entry *e;
! 1807: struct strbuf_ctl *sb = ims->ims_sb;
! 1808: int i;
! 1809:
! 1810: if (sb->sb_flush == NULL)
! 1811: flags &= ~BUS_DMA_STREAMING;
! 1812:
! 1813: if (flags & BUS_DMA_STREAMING)
! 1814: ims->ims_flags |= IOMMU_MAP_STREAM;
! 1815: else
! 1816: ims->ims_flags &= ~IOMMU_MAP_STREAM;
! 1817:
! 1818: for (i = 0, e = ipm->ipm_map; i < ipm->ipm_pagecnt; ++i, ++e) {
! 1819: e->ipe_va = vmaddr;
! 1820: iommu_enter(is, sb, e->ipe_va, e->ipe_pa, flags);
! 1821: vmaddr += PAGE_SIZE;
! 1822: }
! 1823:
! 1824: return (0);
! 1825: }
! 1826:
! 1827: /*
! 1828: * Remove the iomap from the IOMMU.
! 1829: */
! 1830: int
! 1831: iommu_iomap_unload_map(struct iommu_state *is, struct iommu_map_state *ims)
! 1832: {
! 1833: struct iommu_page_map *ipm = &ims->ims_map;
! 1834: struct iommu_page_entry *e;
! 1835: struct strbuf_ctl *sb = ims->ims_sb;
! 1836: int i;
! 1837:
! 1838: for (i = 0, e = ipm->ipm_map; i < ipm->ipm_pagecnt; ++i, ++e)
! 1839: iommu_remove(is, sb, e->ipe_va);
! 1840:
! 1841: return (0);
! 1842: }
! 1843:
! 1844: /*
! 1845: * Translate a physical address (pa) into a DVMA address.
! 1846: */
! 1847: vaddr_t
! 1848: iommu_iomap_translate(struct iommu_map_state *ims, paddr_t pa)
! 1849: {
! 1850: struct iommu_page_map *ipm = &ims->ims_map;
! 1851: struct iommu_page_entry *e;
! 1852: struct iommu_page_entry pe;
! 1853: paddr_t offset = pa & PAGE_MASK;
! 1854:
! 1855: pe.ipe_pa = trunc_page(pa);
! 1856:
! 1857: e = SPLAY_FIND(iommu_page_tree, &ipm->ipm_tree, &pe);
! 1858:
! 1859: if (e == NULL)
! 1860: return (NULL);
! 1861:
! 1862: return (e->ipe_va | offset);
! 1863: }
! 1864:
! 1865: /*
! 1866: * Clear the iomap table and tree.
! 1867: */
! 1868: void
! 1869: iommu_iomap_clear_pages(struct iommu_map_state *ims)
! 1870: {
! 1871: ims->ims_map.ipm_pagecnt = 0;
! 1872: SPLAY_INIT(&ims->ims_map.ipm_tree);
! 1873: }
! 1874:
CVSweb