1139825Simp/*- 286230Stmm * Copyright (c) 1999, 2000 Matthew R. Green 386230Stmm * All rights reserved. 486230Stmm * 586230Stmm * Redistribution and use in source and binary forms, with or without 686230Stmm * modification, are permitted provided that the following conditions 786230Stmm * are met: 886230Stmm * 1. Redistributions of source code must retain the above copyright 986230Stmm * notice, this list of conditions and the following disclaimer. 1086230Stmm * 2. Redistributions in binary form must reproduce the above copyright 1186230Stmm * notice, this list of conditions and the following disclaimer in the 1286230Stmm * documentation and/or other materials provided with the distribution. 1386230Stmm * 1486230Stmm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1586230Stmm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1686230Stmm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1786230Stmm * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1886230Stmm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 1986230Stmm * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 2086230Stmm * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 2186230Stmm * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 2286230Stmm * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2386230Stmm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2486230Stmm * SUCH DAMAGE. 25219567Smarius * 26219567Smarius * from: NetBSD: iommu.c,v 1.82 2008/05/30 02:29:37 mrg Exp 2786230Stmm */ 2886230Stmm/*- 29219567Smarius * Copyright (c) 1999-2002 Eduardo Horvath 30219567Smarius * Copyright (c) 2001-2003 Thomas Moestl 31220931Smarius * Copyright (c) 2007, 2009 Marius Strobl <marius@FreeBSD.org> 3286230Stmm * All rights reserved. 3386230Stmm * 3486230Stmm * Redistribution and use in source and binary forms, with or without 3586230Stmm * modification, are permitted provided that the following conditions 3686230Stmm * are met: 3786230Stmm * 1. Redistributions of source code must retain the above copyright 3886230Stmm * notice, this list of conditions and the following disclaimer. 3986230Stmm * 2. Redistributions in binary form must reproduce the above copyright 4086230Stmm * notice, this list of conditions and the following disclaimer in the 4186230Stmm * documentation and/or other materials provided with the distribution. 42219567Smarius * 3. The name of the author may not be used to endorse or promote products 43219567Smarius * derived from this software without specific prior written permission. 4486230Stmm * 45219567Smarius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 46219567Smarius * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 47219567Smarius * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 48219567Smarius * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 49219567Smarius * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 50219567Smarius * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 51219567Smarius * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 52219567Smarius * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 53219567Smarius * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 5486230Stmm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 5586230Stmm * SUCH DAMAGE. 5686230Stmm * 57219567Smarius * from: NetBSD: sbus.c,v 1.50 2002/06/20 18:26:24 eeh Exp 5886230Stmm */ 5986230Stmm 60167308Smarius#include <sys/cdefs.h> 61167308Smarius__FBSDID("$FreeBSD$"); 62167308Smarius 6386230Stmm/* 64167308Smarius * UltraSPARC IOMMU support; used by both the PCI and SBus code. 65117003Stmm * 66117003Stmm * TODO: 67117003Stmm * - Support sub-page boundaries. 68117003Stmm * - Fix alignment handling for small allocations (the possible page offset 69176995Smarius * of malloc()ed memory is not handled at all). Revise interaction of 70117003Stmm * alignment with the load_mbuf and load_uio functions. 71117003Stmm * - Handle lowaddr and highaddr in some way, and try to work out a way 72176995Smarius * for filter callbacks to work. Currently, only lowaddr is honored 73117003Stmm * in that no addresses above it are considered at all. 74117003Stmm * - Implement BUS_DMA_ALLOCNOW in bus_dma_tag_create as far as possible. 75117003Stmm * - Check the possible return values and callback error arguments; 76117003Stmm * the callback currently gets called in error conditions where it should 77117003Stmm * not be. 78117003Stmm * - When running out of DVMA space, return EINPROGRESS in the non- 79117003Stmm * BUS_DMA_NOWAIT case and delay the callback until sufficient space 80117003Stmm * becomes available. 8186230Stmm */ 82131376Smarius 8386230Stmm#include "opt_iommu.h" 8486230Stmm 8586230Stmm#include <sys/param.h> 8686230Stmm#include <sys/kernel.h> 87117390Stmm#include <sys/lock.h> 88108830Stmm#include <sys/malloc.h> 89108830Stmm#include <sys/mbuf.h> 90117390Stmm#include <sys/mutex.h> 91204152Smarius#include <sys/pcpu.h> 92108830Stmm#include <sys/proc.h> 9386230Stmm#include <sys/systm.h> 94108830Stmm#include <sys/uio.h> 9586230Stmm 9686230Stmm#include <vm/vm.h> 9786230Stmm#include <vm/pmap.h> 98108830Stmm#include <vm/vm_map.h> 9986230Stmm 100200923Smarius#include <machine/asi.h> 10186230Stmm#include <machine/bus.h> 10293070Stmm#include <machine/bus_private.h> 10386230Stmm#include <machine/iommureg.h> 10486230Stmm#include <machine/pmap.h> 10586230Stmm#include <machine/resource.h> 106200923Smarius#include <machine/ver.h> 10786230Stmm 10886230Stmm#include <sys/rman.h> 10986230Stmm 11086230Stmm#include <machine/iommuvar.h> 11186230Stmm 112108830Stmm/* 113185008Smarius * Tuning constants 114108830Stmm */ 115114484Stmm#define IOMMU_MAX_PRE (32 * 1024) 116108830Stmm#define IOMMU_MAX_PRE_SEG 3 117108830Stmm 118185008Smarius/* Threshold for using the streaming buffer */ 119117390Stmm#define IOMMU_STREAM_THRESH 128 120117390Stmm 121249132Smavstatic MALLOC_DEFINE(M_IOMMU, "dvmamem", "IOMMU DVMA Buffers"); 122100188Stmm 123100188Stmmstatic int iommu_strbuf_flush_sync(struct iommu_state *); 124100188Stmm#ifdef IOMMU_DIAG 125176995Smariusstatic void iommu_diag(struct iommu_state *, vm_offset_t va); 12686230Stmm#endif 12786230Stmm 128100188Stmm/* 129171730Smarius * Helpers 130117390Stmm */ 131176995Smarius#define IOMMU_READ8(is, reg, off) \ 132176995Smarius bus_space_read_8((is)->is_bustag, (is)->is_bushandle, \ 13390616Stmm (is)->reg + (off)) 13490616Stmm#define IOMMU_WRITE8(is, reg, off, v) \ 135176995Smarius bus_space_write_8((is)->is_bustag, (is)->is_bushandle, \ 13690616Stmm (is)->reg + (off), (v)) 13790616Stmm 138117390Stmm#define IOMMU_HAS_SB(is) \ 139117390Stmm ((is)->is_sb[0] != 0 || (is)->is_sb[1] != 0) 140117390Stmm 14193070Stmm/* 14293070Stmm * Always overallocate one page; this is needed to handle alignment of the 14393070Stmm * buffer, so it makes sense using a lazy allocation scheme. 14493070Stmm */ 14593070Stmm#define IOMMU_SIZE_ROUNDUP(sz) \ 14693070Stmm (round_io_page(sz) + IO_PAGE_SIZE) 14793070Stmm 148100188Stmm#define IOMMU_SET_TTE(is, va, tte) \ 149171730Smarius ((is)->is_tsb[IOTSBSLOT(va)] = (tte)) 150117390Stmm#define IOMMU_GET_TTE(is, va) \ 151171730Smarius (is)->is_tsb[IOTSBSLOT(va)] 15286230Stmm 153108830Stmm/* Resource helpers */ 154108830Stmm#define IOMMU_RES_START(res) \ 155108830Stmm ((bus_addr_t)rman_get_start(res) << IO_PAGE_SHIFT) 156108830Stmm#define IOMMU_RES_END(res) \ 157108830Stmm ((bus_addr_t)(rman_get_end(res) + 1) << IO_PAGE_SHIFT) 158108830Stmm#define IOMMU_RES_SIZE(res) \ 159108830Stmm ((bus_size_t)rman_get_size(res) << IO_PAGE_SHIFT) 160108830Stmm 161108830Stmm/* Helpers for struct bus_dmamap_res */ 162108830Stmm#define BDR_START(r) IOMMU_RES_START((r)->dr_res) 163108830Stmm#define BDR_END(r) IOMMU_RES_END((r)->dr_res) 164108830Stmm#define BDR_SIZE(r) IOMMU_RES_SIZE((r)->dr_res) 165108830Stmm 166171730Smarius/* Locking macros */ 167171730Smarius#define IS_LOCK(is) mtx_lock(&is->is_mtx) 168171730Smarius#define IS_LOCK_ASSERT(is) mtx_assert(&is->is_mtx, MA_OWNED) 169171730Smarius#define IS_UNLOCK(is) mtx_unlock(&is->is_mtx) 170117390Stmm 171176995Smarius/* Flush a page from the TLB. No locking required, since this is atomic. */ 172100188Stmmstatic __inline void 173100188Stmmiommu_tlb_flush(struct iommu_state *is, bus_addr_t va) 174100188Stmm{ 175100188Stmm 176200923Smarius if ((is->is_flags & IOMMU_FIRE) != 0) 177200923Smarius /* 178200923Smarius * Direct page flushing is not supported and also not 179200923Smarius * necessary due to cache snooping. 180200923Smarius */ 181200923Smarius return; 182171730Smarius IOMMU_WRITE8(is, is_iommu, IMR_FLUSH, va); 183100188Stmm} 184100188Stmm 185117390Stmm/* 186176995Smarius * Flush a page from the streaming buffer. No locking required, since this 187176995Smarius * is atomic. 188117390Stmm */ 189100188Stmmstatic __inline void 190100188Stmmiommu_strbuf_flushpg(struct iommu_state *is, bus_addr_t va) 191100188Stmm{ 192100188Stmm int i; 193100188Stmm 194171730Smarius for (i = 0; i < 2; i++) 195100188Stmm if (is->is_sb[i] != 0) 196100188Stmm IOMMU_WRITE8(is, is_sb[i], ISR_PGFLUSH, va); 197100188Stmm} 198100188Stmm 199117390Stmm/* 200117390Stmm * Flush an address from the streaming buffer(s); this is an asynchronous 201176995Smarius * operation. To make sure that it has completed, iommu_strbuf_sync() needs 202176995Smarius * to be called. No locking required. 203117390Stmm */ 204100188Stmmstatic __inline void 205117390Stmmiommu_strbuf_flush(struct iommu_state *is, bus_addr_t va) 206100188Stmm{ 207100188Stmm 208171730Smarius iommu_strbuf_flushpg(is, va); 209100188Stmm} 210100188Stmm 211117390Stmm/* Synchronize all outstanding flush operations. */ 21293070Stmmstatic __inline void 213117390Stmmiommu_strbuf_sync(struct iommu_state *is) 21493070Stmm{ 21593070Stmm 216117390Stmm IS_LOCK_ASSERT(is); 217171730Smarius iommu_strbuf_flush_sync(is); 218117390Stmm} 219117390Stmm 220117390Stmm/* LRU queue handling for lazy resource allocation. */ 221117390Stmmstatic __inline void 222117390Stmmiommu_map_insq(struct iommu_state *is, bus_dmamap_t map) 223117390Stmm{ 224117390Stmm 225117390Stmm IS_LOCK_ASSERT(is); 226108830Stmm if (!SLIST_EMPTY(&map->dm_reslist)) { 227108830Stmm if (map->dm_onq) 228171730Smarius TAILQ_REMOVE(&is->is_maplruq, map, dm_maplruq); 229171730Smarius TAILQ_INSERT_TAIL(&is->is_maplruq, map, dm_maplruq); 230108830Stmm map->dm_onq = 1; 23193070Stmm } 23293070Stmm} 23393070Stmm 23493070Stmmstatic __inline void 235117390Stmmiommu_map_remq(struct iommu_state *is, bus_dmamap_t map) 23693070Stmm{ 23793070Stmm 238117390Stmm IS_LOCK_ASSERT(is); 239108830Stmm if (map->dm_onq) 240171730Smarius TAILQ_REMOVE(&is->is_maplruq, map, dm_maplruq); 241108830Stmm map->dm_onq = 0; 24293070Stmm} 24393070Stmm 24493070Stmm/* 245167308Smarius * initialise the UltraSPARC IOMMU (PCI or SBus): 24686230Stmm * - allocate and setup the iotsb. 24786230Stmm * - enable the IOMMU 24886230Stmm * - initialise the streaming buffers (if they exist) 24986230Stmm * - create a private DVMA map. 25086230Stmm */ 25186230Stmmvoid 252200923Smariusiommu_init(const char *name, struct iommu_state *is, u_int tsbsize, 253200923Smarius uint32_t iovabase, u_int resvpg) 25486230Stmm{ 25586230Stmm vm_size_t size; 25690616Stmm vm_offset_t offs; 257200923Smarius uint64_t end, obpmap, obpptsb, tte; 258200923Smarius u_int maxtsbsize, obptsbentries, obptsbsize, slot, tsbentries; 25990616Stmm int i; 26086230Stmm 26186230Stmm /* 262200923Smarius * Setup the IOMMU. 26386230Stmm * 264200923Smarius * The sun4u IOMMU is part of the PCI or SBus controller so we 26586230Stmm * will deal with it here.. 26686230Stmm * 26786230Stmm * The IOMMU address space always ends at 0xffffe000, but the starting 26886230Stmm * address depends on the size of the map. The map size is 1024 * 2 ^ 26986230Stmm * is->is_tsbsize entries, where each entry is 8 bytes. The start of 27086230Stmm * the map can be calculated by (0xffffe000 << (8 + is->is_tsbsize)). 27186230Stmm */ 272200923Smarius if ((is->is_flags & IOMMU_FIRE) != 0) { 273200923Smarius maxtsbsize = IOMMU_TSB512K; 274200923Smarius /* 275200923Smarius * We enable bypass in order to be able to use a physical 276200923Smarius * address for the event queue base. 277200923Smarius */ 278200923Smarius is->is_cr = IOMMUCR_SE | IOMMUCR_CM_C_TLB_TBW | IOMMUCR_BE; 279200923Smarius } else { 280200923Smarius maxtsbsize = IOMMU_TSB128K; 281200923Smarius is->is_cr = (tsbsize << IOMMUCR_TSBSZ_SHIFT) | IOMMUCR_DE; 282200923Smarius } 283200923Smarius if (tsbsize > maxtsbsize) 284200923Smarius panic("%s: unsupported TSB size ", __func__); 285200923Smarius tsbentries = IOMMU_TSBENTRIES(tsbsize); 286200923Smarius is->is_cr |= IOMMUCR_EN; 28786230Stmm is->is_tsbsize = tsbsize; 28886230Stmm is->is_dvmabase = iovabase; 28986230Stmm if (iovabase == -1) 29086230Stmm is->is_dvmabase = IOTSB_VSTART(is->is_tsbsize); 29186230Stmm 292108802Stmm size = IOTSB_BASESZ << is->is_tsbsize; 293200923Smarius printf("%s: DVMA map: %#lx to %#lx %d entries%s\n", name, 294100188Stmm is->is_dvmabase, is->is_dvmabase + 295200923Smarius (size << (IO_PAGE_SHIFT - IOTTE_SHIFT)) - 1, tsbentries, 296185008Smarius IOMMU_HAS_SB(is) ? ", streaming buffer" : ""); 29786230Stmm 298171730Smarius /* 299171730Smarius * Set up resource mamangement. 300171730Smarius */ 301171730Smarius mtx_init(&is->is_mtx, "iommu", NULL, MTX_DEF); 302171730Smarius end = is->is_dvmabase + (size << (IO_PAGE_SHIFT - IOTTE_SHIFT)); 303171730Smarius is->is_dvma_rman.rm_type = RMAN_ARRAY; 304171730Smarius is->is_dvma_rman.rm_descr = "DVMA Memory"; 305171730Smarius if (rman_init(&is->is_dvma_rman) != 0 || 306171730Smarius rman_manage_region(&is->is_dvma_rman, 307171730Smarius (is->is_dvmabase >> IO_PAGE_SHIFT) + resvpg, 308171730Smarius (end >> IO_PAGE_SHIFT) - 1) != 0) 309171730Smarius panic("%s: could not initialize DVMA rman", __func__); 310171730Smarius TAILQ_INIT(&is->is_maplruq); 31186230Stmm 31286230Stmm /* 313171730Smarius * Allocate memory for I/O page tables. They need to be 314171730Smarius * physically contiguous. 315171730Smarius */ 316171730Smarius is->is_tsb = contigmalloc(size, M_DEVBUF, M_NOWAIT, 0, ~0UL, 317171730Smarius PAGE_SIZE, 0); 318200923Smarius if (is->is_tsb == NULL) 319171730Smarius panic("%s: contigmalloc failed", __func__); 320171730Smarius is->is_ptsb = pmap_kextract((vm_offset_t)is->is_tsb); 321171730Smarius bzero(is->is_tsb, size); 322171730Smarius 323171730Smarius /* 324200923Smarius * Add the PROM mappings to the kernel IOTSB if desired. 325200923Smarius * Note that the firmware of certain Darwin boards doesn't set 326200923Smarius * the TSB size correctly. 327200923Smarius */ 328200923Smarius if ((is->is_flags & IOMMU_FIRE) != 0) 329200923Smarius obptsbsize = (IOMMU_READ8(is, is_iommu, IMR_TSB) & 330200923Smarius IOMMUTB_TSBSZ_MASK) >> IOMMUTB_TSBSZ_SHIFT; 331200923Smarius else 332200923Smarius obptsbsize = (IOMMU_READ8(is, is_iommu, IMR_CTL) & 333200923Smarius IOMMUCR_TSBSZ_MASK) >> IOMMUCR_TSBSZ_SHIFT; 334200923Smarius obptsbentries = IOMMU_TSBENTRIES(obptsbsize); 335200923Smarius if (bootverbose) 336200923Smarius printf("%s: PROM IOTSB size: %d (%d entries)\n", name, 337200923Smarius obptsbsize, obptsbentries); 338200923Smarius if ((is->is_flags & IOMMU_PRESERVE_PROM) != 0 && 339204152Smarius !(PCPU_GET(impl) == CPU_IMPL_ULTRASPARCIIi && obptsbsize == 7)) { 340200923Smarius if (obptsbentries > tsbentries) 341200923Smarius panic("%s: PROM IOTSB entries exceed kernel", 342200923Smarius __func__); 343200923Smarius obpptsb = IOMMU_READ8(is, is_iommu, IMR_TSB) & 344200923Smarius IOMMUTB_TB_MASK; 345200923Smarius for (i = 0; i < obptsbentries; i++) { 346200923Smarius tte = ldxa(obpptsb + i * 8, ASI_PHYS_USE_EC); 347200923Smarius if ((tte & IOTTE_V) == 0) 348200923Smarius continue; 349200923Smarius slot = tsbentries - obptsbentries + i; 350200923Smarius if (bootverbose) 351200923Smarius printf("%s: adding PROM IOTSB slot %d " 352200923Smarius "(kernel slot %d) TTE: %#lx\n", name, 353200923Smarius i, slot, tte); 354200923Smarius obpmap = (is->is_dvmabase + slot * IO_PAGE_SIZE) >> 355200923Smarius IO_PAGE_SHIFT; 356200923Smarius if (rman_reserve_resource(&is->is_dvma_rman, obpmap, 357200923Smarius obpmap, IO_PAGE_SIZE >> IO_PAGE_SHIFT, RF_ACTIVE, 358200923Smarius NULL) == NULL) 359200923Smarius panic("%s: could not reserve PROM IOTSB slot " 360200923Smarius "%d (kernel slot %d)", __func__, i, slot); 361200923Smarius is->is_tsb[slot] = tte; 362200923Smarius } 363200923Smarius } 364200923Smarius 365200923Smarius /* 36686230Stmm * Initialize streaming buffer, if it is there. 36786230Stmm */ 368100188Stmm if (IOMMU_HAS_SB(is)) { 36990616Stmm /* 37090616Stmm * Find two 64-byte blocks in is_flush that are aligned on 37190616Stmm * a 64-byte boundary for flushing. 37290616Stmm */ 37390616Stmm offs = roundup2((vm_offset_t)is->is_flush, 37490616Stmm STRBUF_FLUSHSYNC_NBYTES); 37590616Stmm for (i = 0; i < 2; i++, offs += STRBUF_FLUSHSYNC_NBYTES) { 376200923Smarius is->is_flushva[i] = (uint64_t *)offs; 37790616Stmm is->is_flushpa[i] = pmap_kextract(offs); 37890616Stmm } 37990616Stmm } 38086230Stmm 38186230Stmm /* 382117390Stmm * Now actually start up the IOMMU. 38386230Stmm */ 38486230Stmm iommu_reset(is); 38586230Stmm} 38686230Stmm 38786230Stmm/* 38886230Stmm * Streaming buffers don't exist on the UltraSPARC IIi; we should have 38986230Stmm * detected that already and disabled them. If not, we will notice that 39086230Stmm * they aren't there when the STRBUF_EN bit does not remain. 39186230Stmm */ 39286230Stmmvoid 39386230Stmmiommu_reset(struct iommu_state *is) 39486230Stmm{ 395200923Smarius uint64_t tsb; 39690616Stmm int i; 39786230Stmm 398200923Smarius tsb = is->is_ptsb; 399200923Smarius if ((is->is_flags & IOMMU_FIRE) != 0) { 400200923Smarius tsb |= is->is_tsbsize; 401200923Smarius IOMMU_WRITE8(is, is_iommu, IMR_CACHE_INVAL, ~0ULL); 402200923Smarius } 403200923Smarius IOMMU_WRITE8(is, is_iommu, IMR_TSB, tsb); 404200923Smarius IOMMU_WRITE8(is, is_iommu, IMR_CTL, is->is_cr); 40586230Stmm 40690616Stmm for (i = 0; i < 2; i++) { 40790616Stmm if (is->is_sb[i] != 0) { 408185008Smarius IOMMU_WRITE8(is, is_sb[i], ISR_CTL, STRBUF_EN | 409185008Smarius ((is->is_flags & IOMMU_RERUN_DISABLE) != 0 ? 410185008Smarius STRBUF_RR_DIS : 0)); 41186230Stmm 412185008Smarius /* No streaming buffers? Disable them. */ 413185008Smarius if ((IOMMU_READ8(is, is_sb[i], ISR_CTL) & 414185008Smarius STRBUF_EN) == 0) 41590616Stmm is->is_sb[i] = 0; 41690616Stmm } 41790616Stmm } 418200923Smarius 419200923Smarius (void)IOMMU_READ8(is, is_iommu, IMR_CTL); 42086230Stmm} 42186230Stmm 42286230Stmm/* 423176995Smarius * Enter a mapping into the TSB. No locking required, since each TSB slot is 424117390Stmm * uniquely assigned to a single map. 42586230Stmm */ 426117390Stmmstatic void 427117390Stmmiommu_enter(struct iommu_state *is, vm_offset_t va, vm_paddr_t pa, 428117390Stmm int stream, int flags) 42986230Stmm{ 430200923Smarius uint64_t tte; 43186230Stmm 432108802Stmm KASSERT(va >= is->is_dvmabase, 433176995Smarius ("%s: va %#lx not in DVMA space", __func__, va)); 434171730Smarius KASSERT(pa <= is->is_pmaxaddr, 435176995Smarius ("%s: XXX: physical address too large (%#lx)", __func__, pa)); 43686230Stmm 43786230Stmm tte = MAKEIOTTE(pa, !(flags & BUS_DMA_NOWRITE), 438117390Stmm !(flags & BUS_DMA_NOCACHE), stream); 43986230Stmm 440100188Stmm IOMMU_SET_TTE(is, va, tte); 441100188Stmm iommu_tlb_flush(is, va); 44290616Stmm#ifdef IOMMU_DIAG 443117390Stmm IS_LOCK(is); 44486230Stmm iommu_diag(is, va); 445117390Stmm IS_UNLOCK(is); 44686230Stmm#endif 44786230Stmm} 44886230Stmm 44986230Stmm/* 450176995Smarius * Remove mappings created by iommu_enter(). Flush the streaming buffer, 451176995Smarius * but do not synchronize it. Returns whether a streaming buffer flush 452176995Smarius * was performed. 45386230Stmm */ 454117390Stmmstatic int 45586230Stmmiommu_remove(struct iommu_state *is, vm_offset_t va, vm_size_t len) 45686230Stmm{ 457200923Smarius int slot, streamed = 0; 45886230Stmm 45986230Stmm#ifdef IOMMU_DIAG 46086230Stmm iommu_diag(is, va); 46186230Stmm#endif 46286230Stmm 463100188Stmm KASSERT(va >= is->is_dvmabase, 464176995Smarius ("%s: va 0x%lx not in DVMA space", __func__, (u_long)va)); 465100188Stmm KASSERT(va + len >= va, 466176995Smarius ("%s: va 0x%lx + len 0x%lx wraps", __func__, (long)va, (long)len)); 467100188Stmm 46886230Stmm va = trunc_io_page(va); 46986230Stmm while (len > 0) { 470117390Stmm if ((IOMMU_GET_TTE(is, va) & IOTTE_STREAM) != 0) { 471117390Stmm streamed = 1; 472117390Stmm iommu_strbuf_flush(is, va); 473117390Stmm } 474100188Stmm len -= ulmin(len, IO_PAGE_SIZE); 475100188Stmm IOMMU_SET_TTE(is, va, 0); 476100188Stmm iommu_tlb_flush(is, va); 477200923Smarius if ((is->is_flags & IOMMU_FLUSH_CACHE) != 0) { 478200923Smarius slot = IOTSBSLOT(va); 479200923Smarius if (len <= IO_PAGE_SIZE || slot % 8 == 7) 480200923Smarius IOMMU_WRITE8(is, is_iommu, IMR_CACHE_FLUSH, 481200923Smarius is->is_ptsb + slot * 8); 482200923Smarius } 48386230Stmm va += IO_PAGE_SIZE; 48486230Stmm } 485117390Stmm return (streamed); 48686230Stmm} 48786230Stmm 488117390Stmm/* Decode an IOMMU fault for host bridge error handlers. */ 48993053Stmmvoid 49093053Stmmiommu_decode_fault(struct iommu_state *is, vm_offset_t phys) 49193053Stmm{ 49293053Stmm bus_addr_t va; 49393053Stmm long idx; 49493053Stmm 495171730Smarius idx = phys - is->is_ptsb; 496171730Smarius if (phys < is->is_ptsb || 49793053Stmm idx > (PAGE_SIZE << is->is_tsbsize)) 49893053Stmm return; 49993053Stmm va = is->is_dvmabase + 50093053Stmm (((bus_addr_t)idx >> IOTTE_SHIFT) << IO_PAGE_SHIFT); 50193053Stmm printf("IOMMU fault virtual address %#lx\n", (u_long)va); 50293053Stmm} 50393053Stmm 504117390Stmm/* 505117390Stmm * A barrier operation which makes sure that all previous streaming buffer 506117390Stmm * flushes complete before it returns. 507117390Stmm */ 50886230Stmmstatic int 509100188Stmmiommu_strbuf_flush_sync(struct iommu_state *is) 51086230Stmm{ 51186230Stmm struct timeval cur, end; 51290616Stmm int i; 51386230Stmm 514117390Stmm IS_LOCK_ASSERT(is); 515100188Stmm if (!IOMMU_HAS_SB(is)) 51686230Stmm return (0); 51786230Stmm 51886230Stmm /* 51986230Stmm * Streaming buffer flushes: 52086230Stmm * 52186230Stmm * 1 Tell strbuf to flush by storing va to strbuf_pgflush. If 52286230Stmm * we're not on a cache line boundary (64-bits): 52386230Stmm * 2 Store 0 in flag 52486230Stmm * 3 Store pointer to flag in flushsync 52586230Stmm * 4 wait till flushsync becomes 0x1 52686230Stmm * 527176995Smarius * If it takes more than .5 sec, something went wrong. 52886230Stmm */ 52990616Stmm *is->is_flushva[0] = 1; 53090616Stmm *is->is_flushva[1] = 1; 53186230Stmm membar(StoreStore); 53290616Stmm for (i = 0; i < 2; i++) { 53390616Stmm if (is->is_sb[i] != 0) { 53490616Stmm *is->is_flushva[i] = 0; 53590616Stmm IOMMU_WRITE8(is, is_sb[i], ISR_FLUSHSYNC, 53690616Stmm is->is_flushpa[i]); 53790616Stmm } 53890616Stmm } 53986230Stmm 540105545Stmm microuptime(&cur); 54186230Stmm end.tv_sec = 0; 542117390Stmm /* 543176995Smarius * 0.5s is the recommended timeout from the U2S manual. The actual 544117390Stmm * time required should be smaller by at least a factor of 1000. 545117390Stmm * We have no choice but to busy-wait. 546117390Stmm */ 54786230Stmm end.tv_usec = 500000; 54886230Stmm timevaladd(&end, &cur); 54986230Stmm 55090616Stmm while ((!*is->is_flushva[0] || !*is->is_flushva[1]) && 55190616Stmm timevalcmp(&cur, &end, <=)) 552105545Stmm microuptime(&cur); 55386230Stmm 55490616Stmm if (!*is->is_flushva[0] || !*is->is_flushva[1]) { 555167308Smarius panic("%s: flush timeout %ld, %ld at %#lx", __func__, 55690616Stmm *is->is_flushva[0], *is->is_flushva[1], is->is_flushpa[0]); 55786230Stmm } 558108802Stmm 559117390Stmm return (1); 56086230Stmm} 56186230Stmm 562117390Stmm/* Determine whether we may enable streaming on a mapping. */ 563117390Stmmstatic __inline int 564117390Stmmiommu_use_streaming(struct iommu_state *is, bus_dmamap_t map, bus_size_t size) 565117390Stmm{ 566117390Stmm 567117390Stmm return (size >= IOMMU_STREAM_THRESH && IOMMU_HAS_SB(is) && 568117390Stmm (map->dm_flags & DMF_COHERENT) == 0); 569117390Stmm} 570117390Stmm 571117390Stmm/* 572176995Smarius * Allocate DVMA virtual memory for a map. The map may not be on a queue, 573176995Smarius * so that it can be freely modified. 574117390Stmm */ 57590616Stmmstatic int 57693070Stmmiommu_dvma_valloc(bus_dma_tag_t t, struct iommu_state *is, bus_dmamap_t map, 57793070Stmm bus_size_t size) 57890616Stmm{ 579108830Stmm struct resource *res; 580108830Stmm struct bus_dmamap_res *bdr; 581109651Stmm bus_size_t align, sgsize; 58290616Stmm 583176995Smarius KASSERT(!map->dm_onq, ("%s: map on queue!", __func__)); 584108830Stmm if ((bdr = malloc(sizeof(*bdr), M_IOMMU, M_NOWAIT)) == NULL) 585108830Stmm return (EAGAIN); 58690616Stmm /* 58793070Stmm * If a boundary is specified, a map cannot be larger than it; however 58893070Stmm * we do not clip currently, as that does not play well with the lazy 58993070Stmm * allocation code. 59093070Stmm * Alignment to a page boundary is always enforced. 59190616Stmm */ 592108815Stmm align = (t->dt_alignment + IO_PAGE_MASK) >> IO_PAGE_SHIFT; 59393070Stmm sgsize = round_io_page(size) >> IO_PAGE_SHIFT; 594108815Stmm if (t->dt_boundary > 0 && t->dt_boundary < IO_PAGE_SIZE) 595167308Smarius panic("%s: illegal boundary specified", __func__); 596171730Smarius res = rman_reserve_resource_bound(&is->is_dvma_rman, 0L, 597109651Stmm t->dt_lowaddr >> IO_PAGE_SHIFT, sgsize, 598109651Stmm t->dt_boundary >> IO_PAGE_SHIFT, 59990616Stmm RF_ACTIVE | rman_make_alignment_flags(align), NULL); 600109651Stmm if (res == NULL) { 601109651Stmm free(bdr, M_IOMMU); 60290616Stmm return (ENOMEM); 603109651Stmm } 60490616Stmm 605108830Stmm bdr->dr_res = res; 606108830Stmm bdr->dr_used = 0; 607108830Stmm SLIST_INSERT_HEAD(&map->dm_reslist, bdr, dr_link); 60890616Stmm return (0); 60990616Stmm} 61090616Stmm 611108830Stmm/* Unload the map and mark all resources as unused, but do not free them. */ 61290616Stmmstatic void 613108830Stmmiommu_dvmamap_vunload(struct iommu_state *is, bus_dmamap_t map) 61490616Stmm{ 615108830Stmm struct bus_dmamap_res *r; 616117390Stmm int streamed = 0; 61790616Stmm 618185008Smarius IS_LOCK_ASSERT(is); /* for iommu_strbuf_sync() below */ 619108830Stmm SLIST_FOREACH(r, &map->dm_reslist, dr_link) { 620117390Stmm streamed |= iommu_remove(is, BDR_START(r), r->dr_used); 621108830Stmm r->dr_used = 0; 622108830Stmm } 623117390Stmm if (streamed) 624117390Stmm iommu_strbuf_sync(is); 625108830Stmm} 626108830Stmm 627108830Stmm/* Free a DVMA virtual memory resource. */ 628108830Stmmstatic __inline void 629108830Stmmiommu_dvma_vfree_res(bus_dmamap_t map, struct bus_dmamap_res *r) 630108830Stmm{ 631108830Stmm 632176995Smarius KASSERT(r->dr_used == 0, ("%s: resource busy!", __func__)); 633108830Stmm if (r->dr_res != NULL && rman_release_resource(r->dr_res) != 0) 63490616Stmm printf("warning: DVMA space lost\n"); 635108830Stmm SLIST_REMOVE(&map->dm_reslist, r, bus_dmamap_res, dr_link); 636108830Stmm free(r, M_IOMMU); 63790616Stmm} 63890616Stmm 639108830Stmm/* Free all DVMA virtual memory for a map. */ 640108830Stmmstatic void 641108830Stmmiommu_dvma_vfree(struct iommu_state *is, bus_dmamap_t map) 642108830Stmm{ 643108830Stmm 644117390Stmm IS_LOCK(is); 645117390Stmm iommu_map_remq(is, map); 646108830Stmm iommu_dvmamap_vunload(is, map); 647117390Stmm IS_UNLOCK(is); 648108830Stmm while (!SLIST_EMPTY(&map->dm_reslist)) 649108830Stmm iommu_dvma_vfree_res(map, SLIST_FIRST(&map->dm_reslist)); 650108830Stmm} 651108830Stmm 652108830Stmm/* Prune a map, freeing all unused DVMA resources. */ 653108830Stmmstatic bus_size_t 654117390Stmmiommu_dvma_vprune(struct iommu_state *is, bus_dmamap_t map) 655108830Stmm{ 656108830Stmm struct bus_dmamap_res *r, *n; 657108830Stmm bus_size_t freed = 0; 658108830Stmm 659117390Stmm IS_LOCK_ASSERT(is); 660108830Stmm for (r = SLIST_FIRST(&map->dm_reslist); r != NULL; r = n) { 661108830Stmm n = SLIST_NEXT(r, dr_link); 662108830Stmm if (r->dr_used == 0) { 663108830Stmm freed += BDR_SIZE(r); 664108830Stmm iommu_dvma_vfree_res(map, r); 665108830Stmm } 666108830Stmm } 667117390Stmm if (SLIST_EMPTY(&map->dm_reslist)) 668117390Stmm iommu_map_remq(is, map); 669108830Stmm return (freed); 670108830Stmm} 671108830Stmm 672108830Stmm/* 673108830Stmm * Try to find a suitably-sized (and if requested, -aligned) slab of DVMA 674117390Stmm * memory with IO page offset voffs. 675108830Stmm */ 676108830Stmmstatic bus_addr_t 677108830Stmmiommu_dvma_vfindseg(bus_dmamap_t map, vm_offset_t voffs, bus_size_t size, 678108830Stmm bus_addr_t amask) 679108830Stmm{ 680108830Stmm struct bus_dmamap_res *r; 681108830Stmm bus_addr_t dvmaddr, dvmend; 682108830Stmm 683176995Smarius KASSERT(!map->dm_onq, ("%s: map on queue!", __func__)); 684108830Stmm SLIST_FOREACH(r, &map->dm_reslist, dr_link) { 685108830Stmm dvmaddr = round_io_page(BDR_START(r) + r->dr_used); 686108830Stmm /* Alignment can only work with voffs == 0. */ 687108830Stmm dvmaddr = (dvmaddr + amask) & ~amask; 688108830Stmm dvmaddr += voffs; 689108830Stmm dvmend = dvmaddr + size; 690108830Stmm if (dvmend <= BDR_END(r)) { 691108830Stmm r->dr_used = dvmend - BDR_START(r); 692108830Stmm return (dvmaddr); 693108830Stmm } 694108830Stmm } 695108830Stmm return (0); 696108830Stmm} 697108830Stmm 698108830Stmm/* 699108830Stmm * Try to find or allocate a slab of DVMA space; see above. 700108830Stmm */ 701108830Stmmstatic int 702108830Stmmiommu_dvma_vallocseg(bus_dma_tag_t dt, struct iommu_state *is, bus_dmamap_t map, 703108830Stmm vm_offset_t voffs, bus_size_t size, bus_addr_t amask, bus_addr_t *addr) 704108830Stmm{ 705127344Stmm bus_dmamap_t tm, last; 706108830Stmm bus_addr_t dvmaddr, freed; 707117390Stmm int error, complete = 0; 708108830Stmm 709108830Stmm dvmaddr = iommu_dvma_vfindseg(map, voffs, size, amask); 710108830Stmm 711108830Stmm /* Need to allocate. */ 712108830Stmm if (dvmaddr == 0) { 713108830Stmm while ((error = iommu_dvma_valloc(dt, is, map, 714117390Stmm voffs + size)) == ENOMEM && !complete) { 715108830Stmm /* 716127344Stmm * Free the allocated DVMA of a few maps until 717108830Stmm * the required size is reached. This is an 718108830Stmm * approximation to not have to call the allocation 719108830Stmm * function too often; most likely one free run 720108830Stmm * will not suffice if not one map was large enough 721108830Stmm * itself due to fragmentation. 722108830Stmm */ 723117390Stmm IS_LOCK(is); 724108830Stmm freed = 0; 725171730Smarius last = TAILQ_LAST(&is->is_maplruq, iommu_maplruq_head); 726108830Stmm do { 727171730Smarius tm = TAILQ_FIRST(&is->is_maplruq); 728127344Stmm complete = tm == last; 729127344Stmm if (tm == NULL) 730117390Stmm break; 731117390Stmm freed += iommu_dvma_vprune(is, tm); 732117390Stmm /* Move to the end. */ 733117390Stmm iommu_map_insq(is, tm); 734127344Stmm } while (freed < size && !complete); 735117390Stmm IS_UNLOCK(is); 736108830Stmm } 737108830Stmm if (error != 0) 738108830Stmm return (error); 739108830Stmm dvmaddr = iommu_dvma_vfindseg(map, voffs, size, amask); 740176995Smarius KASSERT(dvmaddr != 0, ("%s: allocation failed unexpectedly!", 741176995Smarius __func__)); 742108830Stmm } 743108830Stmm *addr = dvmaddr; 744108830Stmm return (0); 745108830Stmm} 746108830Stmm 747116541Stmmstatic int 748116541Stmmiommu_dvmamem_alloc(bus_dma_tag_t dt, void **vaddr, int flags, 749116541Stmm bus_dmamap_t *mapp) 75086230Stmm{ 751116541Stmm struct iommu_state *is = dt->dt_cookie; 752118090Stmm int error, mflags; 75386230Stmm 75486230Stmm /* 755171730Smarius * XXX: This will break for 32 bit transfers on machines with more 756171730Smarius * than is->is_pmaxaddr memory. 75786230Stmm */ 758116541Stmm if ((error = sparc64_dma_alloc_map(dt, mapp)) != 0) 75986230Stmm return (error); 760118090Stmm 761118090Stmm if ((flags & BUS_DMA_NOWAIT) != 0) 762118090Stmm mflags = M_NOWAIT; 763118090Stmm else 764118090Stmm mflags = M_WAITOK; 765118090Stmm if ((flags & BUS_DMA_ZERO) != 0) 766118090Stmm mflags |= M_ZERO; 767118090Stmm 768118090Stmm if ((*vaddr = malloc(dt->dt_maxsize, M_IOMMU, mflags)) == NULL) { 76990616Stmm error = ENOMEM; 770116541Stmm sparc64_dma_free_map(dt, *mapp); 771108830Stmm return (error); 77290616Stmm } 773117390Stmm if ((flags & BUS_DMA_COHERENT) != 0) 774117390Stmm (*mapp)->dm_flags |= DMF_COHERENT; 77593070Stmm /* 776176995Smarius * Try to preallocate DVMA space. If this fails, it is retried at 777176995Smarius * load time. 77893070Stmm */ 779115316Sscottl iommu_dvma_valloc(dt, is, *mapp, IOMMU_SIZE_ROUNDUP(dt->dt_maxsize)); 780117390Stmm IS_LOCK(is); 781117390Stmm iommu_map_insq(is, *mapp); 782117390Stmm IS_UNLOCK(is); 78386230Stmm return (0); 78486230Stmm} 78586230Stmm 786116541Stmmstatic void 787116541Stmmiommu_dvmamem_free(bus_dma_tag_t dt, void *vaddr, bus_dmamap_t map) 78886230Stmm{ 789116541Stmm struct iommu_state *is = dt->dt_cookie; 79086230Stmm 791108830Stmm iommu_dvma_vfree(is, map); 792116541Stmm sparc64_dma_free_map(dt, map); 79386230Stmm free(vaddr, M_IOMMU); 79486230Stmm} 79586230Stmm 796116541Stmmstatic int 797116541Stmmiommu_dvmamap_create(bus_dma_tag_t dt, int flags, bus_dmamap_t *mapp) 79890616Stmm{ 799116541Stmm struct iommu_state *is = dt->dt_cookie; 800108830Stmm bus_size_t totsz, presz, currsz; 801108830Stmm int error, i, maxpre; 80290616Stmm 803116541Stmm if ((error = sparc64_dma_alloc_map(dt, mapp)) != 0) 80490616Stmm return (error); 805117390Stmm if ((flags & BUS_DMA_COHERENT) != 0) 806117390Stmm (*mapp)->dm_flags |= DMF_COHERENT; 80790616Stmm /* 808108830Stmm * Preallocate DVMA space; if this fails now, it is retried at load 809176995Smarius * time. Through bus_dmamap_load_mbuf() and bus_dmamap_load_uio(), 810176995Smarius * it is possible to have multiple discontiguous segments in a single 811176995Smarius * map, which is handled by allocating additional resources, instead 812176995Smarius * of increasing the size, to avoid fragmentation. 813176995Smarius * Clamp preallocation to IOMMU_MAX_PRE. In some situations we can 81493070Stmm * handle more; that case is handled by reallocating at map load time. 81590616Stmm */ 816167308Smarius totsz = ulmin(IOMMU_SIZE_ROUNDUP(dt->dt_maxsize), IOMMU_MAX_PRE); 817108830Stmm error = iommu_dvma_valloc(dt, is, *mapp, totsz); 818108830Stmm if (error != 0) 819108830Stmm return (0); 820108830Stmm /* 821108830Stmm * Try to be smart about preallocating some additional segments if 822108830Stmm * needed. 823108830Stmm */ 824108830Stmm maxpre = imin(dt->dt_nsegments, IOMMU_MAX_PRE_SEG); 825108830Stmm presz = dt->dt_maxsize / maxpre; 826109651Stmm for (i = 1; i < maxpre && totsz < IOMMU_MAX_PRE; i++) { 827108830Stmm currsz = round_io_page(ulmin(presz, IOMMU_MAX_PRE - totsz)); 828108830Stmm error = iommu_dvma_valloc(dt, is, *mapp, currsz); 829108830Stmm if (error != 0) 830108830Stmm break; 831108830Stmm totsz += currsz; 832108830Stmm } 833117390Stmm IS_LOCK(is); 834117390Stmm iommu_map_insq(is, *mapp); 835117390Stmm IS_UNLOCK(is); 83690616Stmm return (0); 83790616Stmm} 83890616Stmm 839116541Stmmstatic int 840116541Stmmiommu_dvmamap_destroy(bus_dma_tag_t dt, bus_dmamap_t map) 84190616Stmm{ 842116541Stmm struct iommu_state *is = dt->dt_cookie; 84390616Stmm 844108830Stmm iommu_dvma_vfree(is, map); 845116541Stmm sparc64_dma_free_map(dt, map); 846116541Stmm return (0); 84790616Stmm} 84890616Stmm 84986230Stmm/* 850251874Sscottl * Utility function to load a physical buffer. segp contains 851251874Sscottl * the starting segment on entrace, and the ending segment on exit. 85286230Stmm */ 853108830Stmmstatic int 854251874Sscottliommu_dvmamap_load_phys(bus_dma_tag_t dt, bus_dmamap_t map, vm_paddr_t buf, 855251874Sscottl bus_size_t buflen, int flags, bus_dma_segment_t *segs, int *segp) 85686230Stmm{ 857200923Smarius bus_addr_t amask, dvmaddr, dvmoffs; 858117003Stmm bus_size_t sgsize, esize; 859251874Sscottl struct iommu_state *is; 860251874Sscottl vm_offset_t voffs; 861113238Sjake vm_paddr_t curaddr; 862188456Smarius int error, firstpg, sgcnt; 863200923Smarius u_int slot; 86486230Stmm 865251874Sscottl is = dt->dt_cookie; 866251874Sscottl if (*segp == -1) { 867251874Sscottl if ((map->dm_flags & DMF_LOADED) != 0) { 868251874Sscottl#ifdef DIAGNOSTIC 869251874Sscottl printf("%s: map still in use\n", __func__); 870251874Sscottl#endif 871251874Sscottl bus_dmamap_unload(dt, map); 872251874Sscottl } 873251874Sscottl 874251874Sscottl /* 875251874Sscottl * Make sure that the map is not on a queue so that the 876251874Sscottl * resource list may be safely accessed and modified without 877251874Sscottl * needing the lock to cover the whole operation. 878251874Sscottl */ 879251874Sscottl IS_LOCK(is); 880251874Sscottl iommu_map_remq(is, map); 881251874Sscottl IS_UNLOCK(is); 882251874Sscottl 883251874Sscottl amask = dt->dt_alignment - 1; 884251874Sscottl } else 885251874Sscottl amask = 0; 886176995Smarius KASSERT(buflen != 0, ("%s: buflen == 0!", __func__)); 887108815Stmm if (buflen > dt->dt_maxsize) 88886230Stmm return (EINVAL); 88986230Stmm 890251874Sscottl if (segs == NULL) 891251874Sscottl segs = dt->dt_segments; 89293070Stmm 893251874Sscottl voffs = buf & IO_PAGE_MASK; 89486230Stmm 895108830Stmm /* Try to find a slab that is large enough. */ 896108830Stmm error = iommu_dvma_vallocseg(dt, is, map, voffs, buflen, amask, 897108830Stmm &dvmaddr); 898108830Stmm if (error != 0) 899108830Stmm return (error); 900108830Stmm 901108830Stmm sgcnt = *segp; 902108830Stmm firstpg = 1; 903188456Smarius map->dm_flags &= ~DMF_STREAMED; 904188456Smarius map->dm_flags |= iommu_use_streaming(is, map, buflen) != 0 ? 905188456Smarius DMF_STREAMED : 0; 90686230Stmm for (; buflen > 0; ) { 907251874Sscottl curaddr = buf; 90886230Stmm 90986230Stmm /* 91086230Stmm * Compute the segment size, and adjust counts. 91186230Stmm */ 912251874Sscottl sgsize = IO_PAGE_SIZE - ((u_long)buf & IO_PAGE_MASK); 91386230Stmm if (buflen < sgsize) 91486230Stmm sgsize = buflen; 91586230Stmm 916117003Stmm buflen -= sgsize; 917251874Sscottl buf += sgsize; 918117003Stmm 919200923Smarius dvmoffs = trunc_io_page(dvmaddr); 920200923Smarius iommu_enter(is, dvmoffs, trunc_io_page(curaddr), 921188456Smarius (map->dm_flags & DMF_STREAMED) != 0, flags); 922200923Smarius if ((is->is_flags & IOMMU_FLUSH_CACHE) != 0) { 923200923Smarius slot = IOTSBSLOT(dvmoffs); 924200923Smarius if (buflen <= 0 || slot % 8 == 7) 925200923Smarius IOMMU_WRITE8(is, is_iommu, IMR_CACHE_FLUSH, 926200923Smarius is->is_ptsb + slot * 8); 927200923Smarius } 92886230Stmm 929117003Stmm /* 930117003Stmm * Chop the chunk up into segments of at most maxsegsz, but try 931117003Stmm * to fill each segment as well as possible. 932117003Stmm */ 933117003Stmm if (!firstpg) { 934117003Stmm esize = ulmin(sgsize, 935140281Sscottl dt->dt_maxsegsz - segs[sgcnt].ds_len); 936140281Sscottl segs[sgcnt].ds_len += esize; 937117003Stmm sgsize -= esize; 938117003Stmm dvmaddr += esize; 939117003Stmm } 940117003Stmm while (sgsize > 0) { 94186230Stmm sgcnt++; 942131224Sscottl if (sgcnt >= dt->dt_nsegments) 943109651Stmm return (EFBIG); 944108830Stmm /* 945176995Smarius * No extra alignment here - the common practice in 946176995Smarius * the busdma code seems to be that only the first 947176995Smarius * segment needs to satisfy the alignment constraints 948176995Smarius * (and that only for bus_dmamem_alloc()ed maps). 949176995Smarius * It is assumed that such tags have maxsegsize >= 950176995Smarius * maxsize. 951108830Stmm */ 952117003Stmm esize = ulmin(sgsize, dt->dt_maxsegsz); 953140281Sscottl segs[sgcnt].ds_addr = dvmaddr; 954140281Sscottl segs[sgcnt].ds_len = esize; 955117003Stmm sgsize -= esize; 956117003Stmm dvmaddr += esize; 957117003Stmm } 958117003Stmm 959108830Stmm firstpg = 0; 96086230Stmm } 961108830Stmm *segp = sgcnt; 96286230Stmm return (0); 96386230Stmm} 96486230Stmm 965251874Sscottl/* 966251874Sscottl * IOMMU DVMA operations, common to PCI and SBus 967251874Sscottl */ 968116541Stmmstatic int 969251874Sscottliommu_dvmamap_load_buffer(bus_dma_tag_t dt, bus_dmamap_t map, void *buf, 970251874Sscottl bus_size_t buflen, pmap_t pmap, int flags, bus_dma_segment_t *segs, 971251874Sscottl int *segp) 972108830Stmm{ 973251874Sscottl bus_addr_t amask, dvmaddr, dvmoffs; 974251874Sscottl bus_size_t sgsize, esize; 975251874Sscottl struct iommu_state *is; 976251874Sscottl vm_offset_t vaddr, voffs; 977251874Sscottl vm_paddr_t curaddr; 978251874Sscottl int error, firstpg, sgcnt; 979251874Sscottl u_int slot; 98086230Stmm 981251874Sscottl is = dt->dt_cookie; 982251874Sscottl if (*segp == -1) { 983251874Sscottl if ((map->dm_flags & DMF_LOADED) != 0) { 984108830Stmm#ifdef DIAGNOSTIC 985251874Sscottl printf("%s: map still in use\n", __func__); 986108830Stmm#endif 987251874Sscottl bus_dmamap_unload(dt, map); 988251874Sscottl } 989108830Stmm 990251874Sscottl /* 991251874Sscottl * Make sure that the map is not on a queue so that the 992251874Sscottl * resource list may be safely accessed and modified without 993251874Sscottl * needing the lock to cover the whole operation. 994251874Sscottl */ 995251874Sscottl IS_LOCK(is); 996251874Sscottl iommu_map_remq(is, map); 997251874Sscottl IS_UNLOCK(is); 998117390Stmm 999251874Sscottl amask = dt->dt_alignment - 1; 1000251874Sscottl } else 1001251874Sscottl amask = 0; 1002251874Sscottl KASSERT(buflen != 0, ("%s: buflen == 0!", __func__)); 1003251874Sscottl if (buflen > dt->dt_maxsize) 1004251874Sscottl return (EINVAL); 1005108830Stmm 1006251874Sscottl if (segs == NULL) 1007251874Sscottl segs = dt->dt_segments; 1008108830Stmm 1009251874Sscottl vaddr = (vm_offset_t)buf; 1010251874Sscottl voffs = vaddr & IO_PAGE_MASK; 1011108830Stmm 1012251874Sscottl /* Try to find a slab that is large enough. */ 1013251874Sscottl error = iommu_dvma_vallocseg(dt, is, map, voffs, buflen, amask, 1014251874Sscottl &dvmaddr); 1015251874Sscottl if (error != 0) 1016251874Sscottl return (error); 1017108830Stmm 1018251874Sscottl sgcnt = *segp; 1019251874Sscottl firstpg = 1; 1020251874Sscottl map->dm_flags &= ~DMF_STREAMED; 1021251874Sscottl map->dm_flags |= iommu_use_streaming(is, map, buflen) != 0 ? 1022251874Sscottl DMF_STREAMED : 0; 1023251874Sscottl for (; buflen > 0; ) { 1024251874Sscottl /* 1025251874Sscottl * Get the physical address for this page. 1026251874Sscottl */ 1027251874Sscottl if (pmap == kernel_pmap) 1028251874Sscottl curaddr = pmap_kextract(vaddr); 1029251874Sscottl else 1030251874Sscottl curaddr = pmap_extract(pmap, vaddr); 1031108830Stmm 1032251874Sscottl /* 1033251874Sscottl * Compute the segment size, and adjust counts. 1034251874Sscottl */ 1035251874Sscottl sgsize = IO_PAGE_SIZE - ((u_long)vaddr & IO_PAGE_MASK); 1036251874Sscottl if (buflen < sgsize) 1037251874Sscottl sgsize = buflen; 1038108830Stmm 1039251874Sscottl buflen -= sgsize; 1040251874Sscottl vaddr += sgsize; 1041117390Stmm 1042251874Sscottl dvmoffs = trunc_io_page(dvmaddr); 1043251874Sscottl iommu_enter(is, dvmoffs, trunc_io_page(curaddr), 1044251874Sscottl (map->dm_flags & DMF_STREAMED) != 0, flags); 1045251874Sscottl if ((is->is_flags & IOMMU_FLUSH_CACHE) != 0) { 1046251874Sscottl slot = IOTSBSLOT(dvmoffs); 1047251874Sscottl if (buflen <= 0 || slot % 8 == 7) 1048251874Sscottl IOMMU_WRITE8(is, is_iommu, IMR_CACHE_FLUSH, 1049251874Sscottl is->is_ptsb + slot * 8); 1050108830Stmm } 1051108830Stmm 1052251874Sscottl /* 1053251874Sscottl * Chop the chunk up into segments of at most maxsegsz, but try 1054251874Sscottl * to fill each segment as well as possible. 1055251874Sscottl */ 1056251874Sscottl if (!firstpg) { 1057251874Sscottl esize = ulmin(sgsize, 1058251874Sscottl dt->dt_maxsegsz - segs[sgcnt].ds_len); 1059251874Sscottl segs[sgcnt].ds_len += esize; 1060251874Sscottl sgsize -= esize; 1061251874Sscottl dvmaddr += esize; 1062251874Sscottl } 1063251874Sscottl while (sgsize > 0) { 1064251874Sscottl sgcnt++; 1065251874Sscottl if (sgcnt >= dt->dt_nsegments) 1066251874Sscottl return (EFBIG); 1067251874Sscottl /* 1068251874Sscottl * No extra alignment here - the common practice in 1069251874Sscottl * the busdma code seems to be that only the first 1070251874Sscottl * segment needs to satisfy the alignment constraints 1071251874Sscottl * (and that only for bus_dmamem_alloc()ed maps). 1072251874Sscottl * It is assumed that such tags have maxsegsize >= 1073251874Sscottl * maxsize. 1074251874Sscottl */ 1075251874Sscottl esize = ulmin(sgsize, dt->dt_maxsegsz); 1076251874Sscottl segs[sgcnt].ds_addr = dvmaddr; 1077251874Sscottl segs[sgcnt].ds_len = esize; 1078251874Sscottl sgsize -= esize; 1079251874Sscottl dvmaddr += esize; 1080251874Sscottl } 1081251874Sscottl 1082251874Sscottl firstpg = 0; 1083108830Stmm } 1084251874Sscottl *segp = sgcnt; 1085251874Sscottl return (0); 1086108830Stmm} 1087108830Stmm 1088251874Sscottlstatic void 1089251874Sscottliommu_dvmamap_waitok(bus_dma_tag_t dmat, bus_dmamap_t map, 1090251874Sscottl struct memdesc *mem, bus_dmamap_callback_t *callback, void *callback_arg) 1091140281Sscottl{ 1092140281Sscottl} 1093140281Sscottl 1094251874Sscottlstatic bus_dma_segment_t * 1095251874Sscottliommu_dvmamap_complete(bus_dma_tag_t dt, bus_dmamap_t map, 1096251874Sscottl bus_dma_segment_t *segs, int nsegs, int error) 1097108830Stmm{ 1098116541Stmm struct iommu_state *is = dt->dt_cookie; 1099108830Stmm 1100117390Stmm IS_LOCK(is); 1101117390Stmm iommu_map_insq(is, map); 1102251874Sscottl if (error != 0) { 1103108830Stmm iommu_dvmamap_vunload(is, map); 1104117390Stmm IS_UNLOCK(is); 1105108830Stmm } else { 1106117390Stmm IS_UNLOCK(is); 1107117390Stmm map->dm_flags |= DMF_LOADED; 1108108830Stmm } 1109251874Sscottl if (segs == NULL) 1110251874Sscottl segs = dt->dt_segments; 1111251874Sscottl return (segs); 1112108830Stmm} 1113108830Stmm 1114116541Stmmstatic void 1115116541Stmmiommu_dvmamap_unload(bus_dma_tag_t dt, bus_dmamap_t map) 111686230Stmm{ 1117116541Stmm struct iommu_state *is = dt->dt_cookie; 111886230Stmm 1119117390Stmm if ((map->dm_flags & DMF_LOADED) == 0) 1120107470Stmm return; 1121117390Stmm IS_LOCK(is); 1122108830Stmm iommu_dvmamap_vunload(is, map); 1123117390Stmm iommu_map_insq(is, map); 1124117390Stmm IS_UNLOCK(is); 1125117390Stmm map->dm_flags &= ~DMF_LOADED; 112686230Stmm} 112786230Stmm 1128116541Stmmstatic void 1129116541Stmmiommu_dvmamap_sync(bus_dma_tag_t dt, bus_dmamap_t map, bus_dmasync_op_t op) 113086230Stmm{ 1131116541Stmm struct iommu_state *is = dt->dt_cookie; 1132108830Stmm struct bus_dmamap_res *r; 113386230Stmm vm_offset_t va; 113486230Stmm vm_size_t len; 1135117390Stmm int streamed = 0; 113686230Stmm 1137117390Stmm if ((map->dm_flags & DMF_LOADED) == 0) 1138117390Stmm return; 1139188456Smarius if ((map->dm_flags & DMF_STREAMED) != 0 && 1140117390Stmm ((op & BUS_DMASYNC_POSTREAD) != 0 || 1141167308Smarius (op & BUS_DMASYNC_PREWRITE) != 0)) { 1142117390Stmm IS_LOCK(is); 1143108830Stmm SLIST_FOREACH(r, &map->dm_reslist, dr_link) { 1144108830Stmm va = (vm_offset_t)BDR_START(r); 1145108830Stmm len = r->dr_used; 1146176995Smarius /* 1147176995Smarius * If we have a streaming buffer, flush it here 1148176995Smarius * first. 1149176995Smarius */ 1150108830Stmm while (len > 0) { 1151176995Smarius if ((IOMMU_GET_TTE(is, va) & 1152176995Smarius IOTTE_STREAM) != 0) { 1153117390Stmm streamed = 1; 1154117390Stmm iommu_strbuf_flush(is, va); 1155117390Stmm } 1156108830Stmm len -= ulmin(len, IO_PAGE_SIZE); 1157108830Stmm va += IO_PAGE_SIZE; 1158108830Stmm } 115986230Stmm } 1160117390Stmm if (streamed) 1161117390Stmm iommu_strbuf_sync(is); 1162117390Stmm IS_UNLOCK(is); 116386230Stmm } 1164117390Stmm if ((op & BUS_DMASYNC_PREWRITE) != 0) 1165117390Stmm membar(Sync); 116686230Stmm} 116786230Stmm 116886230Stmm#ifdef IOMMU_DIAG 116986230Stmm 117086230Stmm/* 117186230Stmm * Perform an IOMMU diagnostic access and print the tag belonging to va. 117286230Stmm */ 117386230Stmmstatic void 117486230Stmmiommu_diag(struct iommu_state *is, vm_offset_t va) 117586230Stmm{ 117686230Stmm int i; 1177178840Smarius uint64_t data, tag; 117886230Stmm 1179200923Smarius if ((is->is_flags & IOMMU_FIRE) != 0) 1180200923Smarius return; 1181117390Stmm IS_LOCK_ASSERT(is); 118290616Stmm IOMMU_WRITE8(is, is_dva, 0, trunc_io_page(va)); 118386230Stmm membar(StoreStore | StoreLoad); 1184167308Smarius printf("%s: tte entry %#lx", __func__, IOMMU_GET_TTE(is, va)); 118590616Stmm if (is->is_dtcmp != 0) { 1186108802Stmm printf(", tag compare register is %#lx\n", 118790616Stmm IOMMU_READ8(is, is_dtcmp, 0)); 118890616Stmm } else 118990616Stmm printf("\n"); 119086230Stmm for (i = 0; i < 16; i++) { 119190616Stmm tag = IOMMU_READ8(is, is_dtag, i * 8); 119290616Stmm data = IOMMU_READ8(is, is_ddram, i * 8); 1193167308Smarius printf("%s: tag %d: %#lx, vpn %#lx, err %lx; " 1194167308Smarius "data %#lx, pa %#lx, v %d, c %d\n", __func__, i, 119586230Stmm tag, (tag & IOMMU_DTAG_VPNMASK) << IOMMU_DTAG_VPNSHIFT, 119686230Stmm (tag & IOMMU_DTAG_ERRMASK) >> IOMMU_DTAG_ERRSHIFT, data, 119786230Stmm (data & IOMMU_DDATA_PGMASK) << IOMMU_DDATA_PGSHIFT, 119886230Stmm (data & IOMMU_DDATA_V) != 0, (data & IOMMU_DDATA_C) != 0); 119986230Stmm } 120086230Stmm} 120186230Stmm 120286230Stmm#endif /* IOMMU_DIAG */ 1203116541Stmm 1204116541Stmmstruct bus_dma_methods iommu_dma_methods = { 1205116541Stmm iommu_dvmamap_create, 1206116541Stmm iommu_dvmamap_destroy, 1207251874Sscottl iommu_dvmamap_load_phys, 1208251874Sscottl iommu_dvmamap_load_buffer, 1209251874Sscottl iommu_dvmamap_waitok, 1210251874Sscottl iommu_dvmamap_complete, 1211116541Stmm iommu_dvmamap_unload, 1212116541Stmm iommu_dvmamap_sync, 1213116541Stmm iommu_dvmamem_alloc, 1214116541Stmm iommu_dvmamem_free, 1215116541Stmm}; 1216