[BACK]Return to iommu.c CVS log [TXT][DIR] Up to [local] / sys / arch / sparc64 / dev

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