1*3024e8afSRuslan Bukin /*- 2*3024e8afSRuslan Bukin * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3*3024e8afSRuslan Bukin * 4*3024e8afSRuslan Bukin * Copyright (c) 2013 The FreeBSD Foundation 5*3024e8afSRuslan Bukin * All rights reserved. 6*3024e8afSRuslan Bukin * 7*3024e8afSRuslan Bukin * This software was developed by Konstantin Belousov <kib@FreeBSD.org> 8*3024e8afSRuslan Bukin * under sponsorship from the FreeBSD Foundation. 9*3024e8afSRuslan Bukin * 10*3024e8afSRuslan Bukin * Redistribution and use in source and binary forms, with or without 11*3024e8afSRuslan Bukin * modification, are permitted provided that the following conditions 12*3024e8afSRuslan Bukin * are met: 13*3024e8afSRuslan Bukin * 1. Redistributions of source code must retain the above copyright 14*3024e8afSRuslan Bukin * notice, this list of conditions and the following disclaimer. 15*3024e8afSRuslan Bukin * 2. Redistributions in binary form must reproduce the above copyright 16*3024e8afSRuslan Bukin * notice, this list of conditions and the following disclaimer in the 17*3024e8afSRuslan Bukin * documentation and/or other materials provided with the distribution. 18*3024e8afSRuslan Bukin * 19*3024e8afSRuslan Bukin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20*3024e8afSRuslan Bukin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21*3024e8afSRuslan Bukin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22*3024e8afSRuslan Bukin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23*3024e8afSRuslan Bukin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24*3024e8afSRuslan Bukin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25*3024e8afSRuslan Bukin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26*3024e8afSRuslan Bukin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27*3024e8afSRuslan Bukin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28*3024e8afSRuslan Bukin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29*3024e8afSRuslan Bukin * SUCH DAMAGE. 30*3024e8afSRuslan Bukin */ 31*3024e8afSRuslan Bukin 32*3024e8afSRuslan Bukin #include <sys/cdefs.h> 33*3024e8afSRuslan Bukin __FBSDID("$FreeBSD$"); 34*3024e8afSRuslan Bukin 35*3024e8afSRuslan Bukin #define RB_AUGMENT(entry) iommu_gas_augment_entry(entry) 36*3024e8afSRuslan Bukin 37*3024e8afSRuslan Bukin #include <sys/param.h> 38*3024e8afSRuslan Bukin #include <sys/systm.h> 39*3024e8afSRuslan Bukin #include <sys/malloc.h> 40*3024e8afSRuslan Bukin #include <sys/bus.h> 41*3024e8afSRuslan Bukin #include <sys/interrupt.h> 42*3024e8afSRuslan Bukin #include <sys/kernel.h> 43*3024e8afSRuslan Bukin #include <sys/ktr.h> 44*3024e8afSRuslan Bukin #include <sys/lock.h> 45*3024e8afSRuslan Bukin #include <sys/proc.h> 46*3024e8afSRuslan Bukin #include <sys/rwlock.h> 47*3024e8afSRuslan Bukin #include <sys/memdesc.h> 48*3024e8afSRuslan Bukin #include <sys/mutex.h> 49*3024e8afSRuslan Bukin #include <sys/sysctl.h> 50*3024e8afSRuslan Bukin #include <sys/rman.h> 51*3024e8afSRuslan Bukin #include <sys/taskqueue.h> 52*3024e8afSRuslan Bukin #include <sys/tree.h> 53*3024e8afSRuslan Bukin #include <sys/uio.h> 54*3024e8afSRuslan Bukin #include <sys/vmem.h> 55*3024e8afSRuslan Bukin #include <dev/pci/pcivar.h> 56*3024e8afSRuslan Bukin #include <vm/vm.h> 57*3024e8afSRuslan Bukin #include <vm/vm_extern.h> 58*3024e8afSRuslan Bukin #include <vm/vm_kern.h> 59*3024e8afSRuslan Bukin #include <vm/vm_object.h> 60*3024e8afSRuslan Bukin #include <vm/vm_page.h> 61*3024e8afSRuslan Bukin #include <vm/vm_map.h> 62*3024e8afSRuslan Bukin #include <vm/uma.h> 63*3024e8afSRuslan Bukin #include <machine/atomic.h> 64*3024e8afSRuslan Bukin #include <machine/bus.h> 65*3024e8afSRuslan Bukin #include <machine/md_var.h> 66*3024e8afSRuslan Bukin #if defined(__amd64__) || defined(__i386__) 67*3024e8afSRuslan Bukin #include <machine/specialreg.h> 68*3024e8afSRuslan Bukin #include <x86/include/busdma_impl.h> 69*3024e8afSRuslan Bukin #include <x86/iommu/intel_reg.h> 70*3024e8afSRuslan Bukin #include <dev/iommu/busdma_iommu.h> 71*3024e8afSRuslan Bukin #include <dev/iommu/iommu.h> 72*3024e8afSRuslan Bukin #include <dev/pci/pcireg.h> 73*3024e8afSRuslan Bukin #include <x86/iommu/intel_dmar.h> 74*3024e8afSRuslan Bukin #endif 75*3024e8afSRuslan Bukin 76*3024e8afSRuslan Bukin /* 77*3024e8afSRuslan Bukin * Guest Address Space management. 78*3024e8afSRuslan Bukin */ 79*3024e8afSRuslan Bukin 80*3024e8afSRuslan Bukin static uma_zone_t iommu_map_entry_zone; 81*3024e8afSRuslan Bukin 82*3024e8afSRuslan Bukin static void 83*3024e8afSRuslan Bukin intel_gas_init(void) 84*3024e8afSRuslan Bukin { 85*3024e8afSRuslan Bukin 86*3024e8afSRuslan Bukin iommu_map_entry_zone = uma_zcreate("IOMMU_MAP_ENTRY", 87*3024e8afSRuslan Bukin sizeof(struct iommu_map_entry), NULL, NULL, 88*3024e8afSRuslan Bukin NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_NODUMP); 89*3024e8afSRuslan Bukin } 90*3024e8afSRuslan Bukin SYSINIT(intel_gas, SI_SUB_DRIVERS, SI_ORDER_FIRST, intel_gas_init, NULL); 91*3024e8afSRuslan Bukin 92*3024e8afSRuslan Bukin struct iommu_map_entry * 93*3024e8afSRuslan Bukin iommu_gas_alloc_entry(struct iommu_domain *domain, u_int flags) 94*3024e8afSRuslan Bukin { 95*3024e8afSRuslan Bukin struct iommu_map_entry *res; 96*3024e8afSRuslan Bukin 97*3024e8afSRuslan Bukin KASSERT((flags & ~(DMAR_PGF_WAITOK)) == 0, 98*3024e8afSRuslan Bukin ("unsupported flags %x", flags)); 99*3024e8afSRuslan Bukin 100*3024e8afSRuslan Bukin res = uma_zalloc(iommu_map_entry_zone, ((flags & DMAR_PGF_WAITOK) != 101*3024e8afSRuslan Bukin 0 ? M_WAITOK : M_NOWAIT) | M_ZERO); 102*3024e8afSRuslan Bukin if (res != NULL) { 103*3024e8afSRuslan Bukin res->domain = domain; 104*3024e8afSRuslan Bukin atomic_add_int(&domain->entries_cnt, 1); 105*3024e8afSRuslan Bukin } 106*3024e8afSRuslan Bukin return (res); 107*3024e8afSRuslan Bukin } 108*3024e8afSRuslan Bukin 109*3024e8afSRuslan Bukin void 110*3024e8afSRuslan Bukin iommu_gas_free_entry(struct iommu_domain *domain, struct iommu_map_entry *entry) 111*3024e8afSRuslan Bukin { 112*3024e8afSRuslan Bukin 113*3024e8afSRuslan Bukin KASSERT(domain == (struct iommu_domain *)entry->domain, 114*3024e8afSRuslan Bukin ("mismatched free domain %p entry %p entry->domain %p", domain, 115*3024e8afSRuslan Bukin entry, entry->domain)); 116*3024e8afSRuslan Bukin atomic_subtract_int(&domain->entries_cnt, 1); 117*3024e8afSRuslan Bukin uma_zfree(iommu_map_entry_zone, entry); 118*3024e8afSRuslan Bukin } 119*3024e8afSRuslan Bukin 120*3024e8afSRuslan Bukin static int 121*3024e8afSRuslan Bukin iommu_gas_cmp_entries(struct iommu_map_entry *a, struct iommu_map_entry *b) 122*3024e8afSRuslan Bukin { 123*3024e8afSRuslan Bukin 124*3024e8afSRuslan Bukin /* Last entry have zero size, so <= */ 125*3024e8afSRuslan Bukin KASSERT(a->start <= a->end, ("inverted entry %p (%jx, %jx)", 126*3024e8afSRuslan Bukin a, (uintmax_t)a->start, (uintmax_t)a->end)); 127*3024e8afSRuslan Bukin KASSERT(b->start <= b->end, ("inverted entry %p (%jx, %jx)", 128*3024e8afSRuslan Bukin b, (uintmax_t)b->start, (uintmax_t)b->end)); 129*3024e8afSRuslan Bukin KASSERT(a->end <= b->start || b->end <= a->start || 130*3024e8afSRuslan Bukin a->end == a->start || b->end == b->start, 131*3024e8afSRuslan Bukin ("overlapping entries %p (%jx, %jx) %p (%jx, %jx)", 132*3024e8afSRuslan Bukin a, (uintmax_t)a->start, (uintmax_t)a->end, 133*3024e8afSRuslan Bukin b, (uintmax_t)b->start, (uintmax_t)b->end)); 134*3024e8afSRuslan Bukin 135*3024e8afSRuslan Bukin if (a->end < b->end) 136*3024e8afSRuslan Bukin return (-1); 137*3024e8afSRuslan Bukin else if (b->end < a->end) 138*3024e8afSRuslan Bukin return (1); 139*3024e8afSRuslan Bukin return (0); 140*3024e8afSRuslan Bukin } 141*3024e8afSRuslan Bukin 142*3024e8afSRuslan Bukin static void 143*3024e8afSRuslan Bukin iommu_gas_augment_entry(struct iommu_map_entry *entry) 144*3024e8afSRuslan Bukin { 145*3024e8afSRuslan Bukin struct iommu_map_entry *child; 146*3024e8afSRuslan Bukin iommu_gaddr_t free_down; 147*3024e8afSRuslan Bukin 148*3024e8afSRuslan Bukin free_down = 0; 149*3024e8afSRuslan Bukin if ((child = RB_LEFT(entry, rb_entry)) != NULL) { 150*3024e8afSRuslan Bukin free_down = MAX(free_down, child->free_down); 151*3024e8afSRuslan Bukin free_down = MAX(free_down, entry->start - child->last); 152*3024e8afSRuslan Bukin entry->first = child->first; 153*3024e8afSRuslan Bukin } else 154*3024e8afSRuslan Bukin entry->first = entry->start; 155*3024e8afSRuslan Bukin 156*3024e8afSRuslan Bukin if ((child = RB_RIGHT(entry, rb_entry)) != NULL) { 157*3024e8afSRuslan Bukin free_down = MAX(free_down, child->free_down); 158*3024e8afSRuslan Bukin free_down = MAX(free_down, child->first - entry->end); 159*3024e8afSRuslan Bukin entry->last = child->last; 160*3024e8afSRuslan Bukin } else 161*3024e8afSRuslan Bukin entry->last = entry->end; 162*3024e8afSRuslan Bukin entry->free_down = free_down; 163*3024e8afSRuslan Bukin } 164*3024e8afSRuslan Bukin 165*3024e8afSRuslan Bukin RB_GENERATE(iommu_gas_entries_tree, iommu_map_entry, rb_entry, 166*3024e8afSRuslan Bukin iommu_gas_cmp_entries); 167*3024e8afSRuslan Bukin 168*3024e8afSRuslan Bukin #ifdef INVARIANTS 169*3024e8afSRuslan Bukin static void 170*3024e8afSRuslan Bukin iommu_gas_check_free(struct iommu_domain *domain) 171*3024e8afSRuslan Bukin { 172*3024e8afSRuslan Bukin struct iommu_map_entry *entry, *l, *r; 173*3024e8afSRuslan Bukin iommu_gaddr_t v; 174*3024e8afSRuslan Bukin 175*3024e8afSRuslan Bukin RB_FOREACH(entry, iommu_gas_entries_tree, &domain->rb_root) { 176*3024e8afSRuslan Bukin KASSERT(domain == (struct iommu_domain *)entry->domain, 177*3024e8afSRuslan Bukin ("mismatched free domain %p entry %p entry->domain %p", 178*3024e8afSRuslan Bukin domain, entry, entry->domain)); 179*3024e8afSRuslan Bukin l = RB_LEFT(entry, rb_entry); 180*3024e8afSRuslan Bukin r = RB_RIGHT(entry, rb_entry); 181*3024e8afSRuslan Bukin v = 0; 182*3024e8afSRuslan Bukin if (l != NULL) { 183*3024e8afSRuslan Bukin v = MAX(v, l->free_down); 184*3024e8afSRuslan Bukin v = MAX(v, entry->start - l->last); 185*3024e8afSRuslan Bukin } 186*3024e8afSRuslan Bukin if (r != NULL) { 187*3024e8afSRuslan Bukin v = MAX(v, r->free_down); 188*3024e8afSRuslan Bukin v = MAX(v, r->first - entry->end); 189*3024e8afSRuslan Bukin } 190*3024e8afSRuslan Bukin MPASS(entry->free_down == v); 191*3024e8afSRuslan Bukin } 192*3024e8afSRuslan Bukin } 193*3024e8afSRuslan Bukin #endif 194*3024e8afSRuslan Bukin 195*3024e8afSRuslan Bukin static bool 196*3024e8afSRuslan Bukin iommu_gas_rb_insert(struct iommu_domain *domain, struct iommu_map_entry *entry) 197*3024e8afSRuslan Bukin { 198*3024e8afSRuslan Bukin struct iommu_map_entry *found; 199*3024e8afSRuslan Bukin 200*3024e8afSRuslan Bukin found = RB_INSERT(iommu_gas_entries_tree, 201*3024e8afSRuslan Bukin &domain->rb_root, entry); 202*3024e8afSRuslan Bukin return (found == NULL); 203*3024e8afSRuslan Bukin } 204*3024e8afSRuslan Bukin 205*3024e8afSRuslan Bukin static void 206*3024e8afSRuslan Bukin iommu_gas_rb_remove(struct iommu_domain *domain, struct iommu_map_entry *entry) 207*3024e8afSRuslan Bukin { 208*3024e8afSRuslan Bukin 209*3024e8afSRuslan Bukin RB_REMOVE(iommu_gas_entries_tree, &domain->rb_root, entry); 210*3024e8afSRuslan Bukin } 211*3024e8afSRuslan Bukin 212*3024e8afSRuslan Bukin void 213*3024e8afSRuslan Bukin iommu_gas_init_domain(struct iommu_domain *domain) 214*3024e8afSRuslan Bukin { 215*3024e8afSRuslan Bukin struct iommu_map_entry *begin, *end; 216*3024e8afSRuslan Bukin 217*3024e8afSRuslan Bukin begin = iommu_gas_alloc_entry(domain, DMAR_PGF_WAITOK); 218*3024e8afSRuslan Bukin end = iommu_gas_alloc_entry(domain, DMAR_PGF_WAITOK); 219*3024e8afSRuslan Bukin 220*3024e8afSRuslan Bukin IOMMU_DOMAIN_LOCK(domain); 221*3024e8afSRuslan Bukin KASSERT(domain->entries_cnt == 2, ("dirty domain %p", domain)); 222*3024e8afSRuslan Bukin KASSERT(RB_EMPTY(&domain->rb_root), 223*3024e8afSRuslan Bukin ("non-empty entries %p", domain)); 224*3024e8afSRuslan Bukin 225*3024e8afSRuslan Bukin begin->start = 0; 226*3024e8afSRuslan Bukin begin->end = IOMMU_PAGE_SIZE; 227*3024e8afSRuslan Bukin begin->flags = IOMMU_MAP_ENTRY_PLACE | IOMMU_MAP_ENTRY_UNMAPPED; 228*3024e8afSRuslan Bukin iommu_gas_rb_insert(domain, begin); 229*3024e8afSRuslan Bukin 230*3024e8afSRuslan Bukin end->start = domain->end; 231*3024e8afSRuslan Bukin end->end = domain->end; 232*3024e8afSRuslan Bukin end->flags = IOMMU_MAP_ENTRY_PLACE | IOMMU_MAP_ENTRY_UNMAPPED; 233*3024e8afSRuslan Bukin iommu_gas_rb_insert(domain, end); 234*3024e8afSRuslan Bukin 235*3024e8afSRuslan Bukin domain->first_place = begin; 236*3024e8afSRuslan Bukin domain->last_place = end; 237*3024e8afSRuslan Bukin domain->flags |= DMAR_DOMAIN_GAS_INITED; 238*3024e8afSRuslan Bukin IOMMU_DOMAIN_UNLOCK(domain); 239*3024e8afSRuslan Bukin } 240*3024e8afSRuslan Bukin 241*3024e8afSRuslan Bukin void 242*3024e8afSRuslan Bukin iommu_gas_fini_domain(struct iommu_domain *domain) 243*3024e8afSRuslan Bukin { 244*3024e8afSRuslan Bukin struct iommu_map_entry *entry, *entry1; 245*3024e8afSRuslan Bukin 246*3024e8afSRuslan Bukin IOMMU_DOMAIN_ASSERT_LOCKED(domain); 247*3024e8afSRuslan Bukin KASSERT(domain->entries_cnt == 2, 248*3024e8afSRuslan Bukin ("domain still in use %p", domain)); 249*3024e8afSRuslan Bukin 250*3024e8afSRuslan Bukin entry = RB_MIN(iommu_gas_entries_tree, &domain->rb_root); 251*3024e8afSRuslan Bukin KASSERT(entry->start == 0, ("start entry start %p", domain)); 252*3024e8afSRuslan Bukin KASSERT(entry->end == IOMMU_PAGE_SIZE, ("start entry end %p", domain)); 253*3024e8afSRuslan Bukin KASSERT(entry->flags == IOMMU_MAP_ENTRY_PLACE, 254*3024e8afSRuslan Bukin ("start entry flags %p", domain)); 255*3024e8afSRuslan Bukin RB_REMOVE(iommu_gas_entries_tree, &domain->rb_root, entry); 256*3024e8afSRuslan Bukin iommu_gas_free_entry(domain, entry); 257*3024e8afSRuslan Bukin 258*3024e8afSRuslan Bukin entry = RB_MAX(iommu_gas_entries_tree, &domain->rb_root); 259*3024e8afSRuslan Bukin KASSERT(entry->start == domain->end, ("end entry start %p", domain)); 260*3024e8afSRuslan Bukin KASSERT(entry->end == domain->end, ("end entry end %p", domain)); 261*3024e8afSRuslan Bukin KASSERT(entry->flags == IOMMU_MAP_ENTRY_PLACE, 262*3024e8afSRuslan Bukin ("end entry flags %p", domain)); 263*3024e8afSRuslan Bukin RB_REMOVE(iommu_gas_entries_tree, &domain->rb_root, entry); 264*3024e8afSRuslan Bukin iommu_gas_free_entry(domain, entry); 265*3024e8afSRuslan Bukin 266*3024e8afSRuslan Bukin RB_FOREACH_SAFE(entry, iommu_gas_entries_tree, &domain->rb_root, 267*3024e8afSRuslan Bukin entry1) { 268*3024e8afSRuslan Bukin KASSERT((entry->flags & IOMMU_MAP_ENTRY_RMRR) != 0, 269*3024e8afSRuslan Bukin ("non-RMRR entry left %p", domain)); 270*3024e8afSRuslan Bukin RB_REMOVE(iommu_gas_entries_tree, &domain->rb_root, 271*3024e8afSRuslan Bukin entry); 272*3024e8afSRuslan Bukin iommu_gas_free_entry(domain, entry); 273*3024e8afSRuslan Bukin } 274*3024e8afSRuslan Bukin } 275*3024e8afSRuslan Bukin 276*3024e8afSRuslan Bukin struct iommu_gas_match_args { 277*3024e8afSRuslan Bukin struct iommu_domain *domain; 278*3024e8afSRuslan Bukin iommu_gaddr_t size; 279*3024e8afSRuslan Bukin int offset; 280*3024e8afSRuslan Bukin const struct bus_dma_tag_common *common; 281*3024e8afSRuslan Bukin u_int gas_flags; 282*3024e8afSRuslan Bukin struct iommu_map_entry *entry; 283*3024e8afSRuslan Bukin }; 284*3024e8afSRuslan Bukin 285*3024e8afSRuslan Bukin /* 286*3024e8afSRuslan Bukin * The interval [beg, end) is a free interval between two iommu_map_entries. 287*3024e8afSRuslan Bukin * maxaddr is an upper bound on addresses that can be allocated. Try to 288*3024e8afSRuslan Bukin * allocate space in the free interval, subject to the conditions expressed 289*3024e8afSRuslan Bukin * by a, and return 'true' if and only if the allocation attempt succeeds. 290*3024e8afSRuslan Bukin */ 291*3024e8afSRuslan Bukin static bool 292*3024e8afSRuslan Bukin iommu_gas_match_one(struct iommu_gas_match_args *a, iommu_gaddr_t beg, 293*3024e8afSRuslan Bukin iommu_gaddr_t end, iommu_gaddr_t maxaddr) 294*3024e8afSRuslan Bukin { 295*3024e8afSRuslan Bukin iommu_gaddr_t bs, start; 296*3024e8afSRuslan Bukin 297*3024e8afSRuslan Bukin a->entry->start = roundup2(beg + IOMMU_PAGE_SIZE, 298*3024e8afSRuslan Bukin a->common->alignment); 299*3024e8afSRuslan Bukin if (a->entry->start + a->size > maxaddr) 300*3024e8afSRuslan Bukin return (false); 301*3024e8afSRuslan Bukin 302*3024e8afSRuslan Bukin /* IOMMU_PAGE_SIZE to create gap after new entry. */ 303*3024e8afSRuslan Bukin if (a->entry->start < beg + IOMMU_PAGE_SIZE || 304*3024e8afSRuslan Bukin a->entry->start + a->size + a->offset + IOMMU_PAGE_SIZE > end) 305*3024e8afSRuslan Bukin return (false); 306*3024e8afSRuslan Bukin 307*3024e8afSRuslan Bukin /* No boundary crossing. */ 308*3024e8afSRuslan Bukin if (iommu_test_boundary(a->entry->start + a->offset, a->size, 309*3024e8afSRuslan Bukin a->common->boundary)) 310*3024e8afSRuslan Bukin return (true); 311*3024e8afSRuslan Bukin 312*3024e8afSRuslan Bukin /* 313*3024e8afSRuslan Bukin * The start + offset to start + offset + size region crosses 314*3024e8afSRuslan Bukin * the boundary. Check if there is enough space after the 315*3024e8afSRuslan Bukin * next boundary after the beg. 316*3024e8afSRuslan Bukin */ 317*3024e8afSRuslan Bukin bs = rounddown2(a->entry->start + a->offset + a->common->boundary, 318*3024e8afSRuslan Bukin a->common->boundary); 319*3024e8afSRuslan Bukin start = roundup2(bs, a->common->alignment); 320*3024e8afSRuslan Bukin /* IOMMU_PAGE_SIZE to create gap after new entry. */ 321*3024e8afSRuslan Bukin if (start + a->offset + a->size + IOMMU_PAGE_SIZE <= end && 322*3024e8afSRuslan Bukin start + a->offset + a->size <= maxaddr && 323*3024e8afSRuslan Bukin iommu_test_boundary(start + a->offset, a->size, 324*3024e8afSRuslan Bukin a->common->boundary)) { 325*3024e8afSRuslan Bukin a->entry->start = start; 326*3024e8afSRuslan Bukin return (true); 327*3024e8afSRuslan Bukin } 328*3024e8afSRuslan Bukin 329*3024e8afSRuslan Bukin /* 330*3024e8afSRuslan Bukin * Not enough space to align at the requested boundary, or 331*3024e8afSRuslan Bukin * boundary is smaller than the size, but allowed to split. 332*3024e8afSRuslan Bukin * We already checked that start + size does not overlap maxaddr. 333*3024e8afSRuslan Bukin * 334*3024e8afSRuslan Bukin * XXXKIB. It is possible that bs is exactly at the start of 335*3024e8afSRuslan Bukin * the next entry, then we do not have gap. Ignore for now. 336*3024e8afSRuslan Bukin */ 337*3024e8afSRuslan Bukin if ((a->gas_flags & IOMMU_MF_CANSPLIT) != 0) { 338*3024e8afSRuslan Bukin a->size = bs - a->entry->start; 339*3024e8afSRuslan Bukin return (true); 340*3024e8afSRuslan Bukin } 341*3024e8afSRuslan Bukin 342*3024e8afSRuslan Bukin return (false); 343*3024e8afSRuslan Bukin } 344*3024e8afSRuslan Bukin 345*3024e8afSRuslan Bukin static void 346*3024e8afSRuslan Bukin iommu_gas_match_insert(struct iommu_gas_match_args *a) 347*3024e8afSRuslan Bukin { 348*3024e8afSRuslan Bukin bool found; 349*3024e8afSRuslan Bukin 350*3024e8afSRuslan Bukin /* 351*3024e8afSRuslan Bukin * The prev->end is always aligned on the page size, which 352*3024e8afSRuslan Bukin * causes page alignment for the entry->start too. The size 353*3024e8afSRuslan Bukin * is checked to be multiple of the page size. 354*3024e8afSRuslan Bukin * 355*3024e8afSRuslan Bukin * The page sized gap is created between consequent 356*3024e8afSRuslan Bukin * allocations to ensure that out-of-bounds accesses fault. 357*3024e8afSRuslan Bukin */ 358*3024e8afSRuslan Bukin a->entry->end = a->entry->start + a->size; 359*3024e8afSRuslan Bukin 360*3024e8afSRuslan Bukin found = iommu_gas_rb_insert(a->domain, a->entry); 361*3024e8afSRuslan Bukin KASSERT(found, ("found dup %p start %jx size %jx", 362*3024e8afSRuslan Bukin a->domain, (uintmax_t)a->entry->start, (uintmax_t)a->size)); 363*3024e8afSRuslan Bukin a->entry->flags = IOMMU_MAP_ENTRY_MAP; 364*3024e8afSRuslan Bukin } 365*3024e8afSRuslan Bukin 366*3024e8afSRuslan Bukin static int 367*3024e8afSRuslan Bukin iommu_gas_lowermatch(struct iommu_gas_match_args *a, struct iommu_map_entry *entry) 368*3024e8afSRuslan Bukin { 369*3024e8afSRuslan Bukin struct iommu_map_entry *child; 370*3024e8afSRuslan Bukin 371*3024e8afSRuslan Bukin child = RB_RIGHT(entry, rb_entry); 372*3024e8afSRuslan Bukin if (child != NULL && entry->end < a->common->lowaddr && 373*3024e8afSRuslan Bukin iommu_gas_match_one(a, entry->end, child->first, 374*3024e8afSRuslan Bukin a->common->lowaddr)) { 375*3024e8afSRuslan Bukin iommu_gas_match_insert(a); 376*3024e8afSRuslan Bukin return (0); 377*3024e8afSRuslan Bukin } 378*3024e8afSRuslan Bukin if (entry->free_down < a->size + a->offset + IOMMU_PAGE_SIZE) 379*3024e8afSRuslan Bukin return (ENOMEM); 380*3024e8afSRuslan Bukin if (entry->first >= a->common->lowaddr) 381*3024e8afSRuslan Bukin return (ENOMEM); 382*3024e8afSRuslan Bukin child = RB_LEFT(entry, rb_entry); 383*3024e8afSRuslan Bukin if (child != NULL && 0 == iommu_gas_lowermatch(a, child)) 384*3024e8afSRuslan Bukin return (0); 385*3024e8afSRuslan Bukin if (child != NULL && child->last < a->common->lowaddr && 386*3024e8afSRuslan Bukin iommu_gas_match_one(a, child->last, entry->start, 387*3024e8afSRuslan Bukin a->common->lowaddr)) { 388*3024e8afSRuslan Bukin iommu_gas_match_insert(a); 389*3024e8afSRuslan Bukin return (0); 390*3024e8afSRuslan Bukin } 391*3024e8afSRuslan Bukin child = RB_RIGHT(entry, rb_entry); 392*3024e8afSRuslan Bukin if (child != NULL && 0 == iommu_gas_lowermatch(a, child)) 393*3024e8afSRuslan Bukin return (0); 394*3024e8afSRuslan Bukin return (ENOMEM); 395*3024e8afSRuslan Bukin } 396*3024e8afSRuslan Bukin 397*3024e8afSRuslan Bukin static int 398*3024e8afSRuslan Bukin iommu_gas_uppermatch(struct iommu_gas_match_args *a, struct iommu_map_entry *entry) 399*3024e8afSRuslan Bukin { 400*3024e8afSRuslan Bukin struct iommu_map_entry *child; 401*3024e8afSRuslan Bukin 402*3024e8afSRuslan Bukin if (entry->free_down < a->size + a->offset + IOMMU_PAGE_SIZE) 403*3024e8afSRuslan Bukin return (ENOMEM); 404*3024e8afSRuslan Bukin if (entry->last < a->common->highaddr) 405*3024e8afSRuslan Bukin return (ENOMEM); 406*3024e8afSRuslan Bukin child = RB_LEFT(entry, rb_entry); 407*3024e8afSRuslan Bukin if (child != NULL && 0 == iommu_gas_uppermatch(a, child)) 408*3024e8afSRuslan Bukin return (0); 409*3024e8afSRuslan Bukin if (child != NULL && child->last >= a->common->highaddr && 410*3024e8afSRuslan Bukin iommu_gas_match_one(a, child->last, entry->start, 411*3024e8afSRuslan Bukin a->domain->end)) { 412*3024e8afSRuslan Bukin iommu_gas_match_insert(a); 413*3024e8afSRuslan Bukin return (0); 414*3024e8afSRuslan Bukin } 415*3024e8afSRuslan Bukin child = RB_RIGHT(entry, rb_entry); 416*3024e8afSRuslan Bukin if (child != NULL && entry->end >= a->common->highaddr && 417*3024e8afSRuslan Bukin iommu_gas_match_one(a, entry->end, child->first, 418*3024e8afSRuslan Bukin a->domain->end)) { 419*3024e8afSRuslan Bukin iommu_gas_match_insert(a); 420*3024e8afSRuslan Bukin return (0); 421*3024e8afSRuslan Bukin } 422*3024e8afSRuslan Bukin if (child != NULL && 0 == iommu_gas_uppermatch(a, child)) 423*3024e8afSRuslan Bukin return (0); 424*3024e8afSRuslan Bukin return (ENOMEM); 425*3024e8afSRuslan Bukin } 426*3024e8afSRuslan Bukin 427*3024e8afSRuslan Bukin static int 428*3024e8afSRuslan Bukin iommu_gas_find_space(struct iommu_domain *domain, 429*3024e8afSRuslan Bukin const struct bus_dma_tag_common *common, iommu_gaddr_t size, 430*3024e8afSRuslan Bukin int offset, u_int flags, struct iommu_map_entry *entry) 431*3024e8afSRuslan Bukin { 432*3024e8afSRuslan Bukin struct iommu_gas_match_args a; 433*3024e8afSRuslan Bukin int error; 434*3024e8afSRuslan Bukin 435*3024e8afSRuslan Bukin IOMMU_DOMAIN_ASSERT_LOCKED(domain); 436*3024e8afSRuslan Bukin KASSERT(entry->flags == 0, ("dirty entry %p %p", domain, entry)); 437*3024e8afSRuslan Bukin KASSERT((size & IOMMU_PAGE_MASK) == 0, ("size %jx", (uintmax_t)size)); 438*3024e8afSRuslan Bukin 439*3024e8afSRuslan Bukin a.domain = domain; 440*3024e8afSRuslan Bukin a.size = size; 441*3024e8afSRuslan Bukin a.offset = offset; 442*3024e8afSRuslan Bukin a.common = common; 443*3024e8afSRuslan Bukin a.gas_flags = flags; 444*3024e8afSRuslan Bukin a.entry = entry; 445*3024e8afSRuslan Bukin 446*3024e8afSRuslan Bukin /* Handle lower region. */ 447*3024e8afSRuslan Bukin if (common->lowaddr > 0) { 448*3024e8afSRuslan Bukin error = iommu_gas_lowermatch(&a, 449*3024e8afSRuslan Bukin RB_ROOT(&domain->rb_root)); 450*3024e8afSRuslan Bukin if (error == 0) 451*3024e8afSRuslan Bukin return (0); 452*3024e8afSRuslan Bukin KASSERT(error == ENOMEM, 453*3024e8afSRuslan Bukin ("error %d from iommu_gas_lowermatch", error)); 454*3024e8afSRuslan Bukin } 455*3024e8afSRuslan Bukin /* Handle upper region. */ 456*3024e8afSRuslan Bukin if (common->highaddr >= domain->end) 457*3024e8afSRuslan Bukin return (ENOMEM); 458*3024e8afSRuslan Bukin error = iommu_gas_uppermatch(&a, RB_ROOT(&domain->rb_root)); 459*3024e8afSRuslan Bukin KASSERT(error == ENOMEM, 460*3024e8afSRuslan Bukin ("error %d from iommu_gas_uppermatch", error)); 461*3024e8afSRuslan Bukin return (error); 462*3024e8afSRuslan Bukin } 463*3024e8afSRuslan Bukin 464*3024e8afSRuslan Bukin static int 465*3024e8afSRuslan Bukin iommu_gas_alloc_region(struct iommu_domain *domain, struct iommu_map_entry *entry, 466*3024e8afSRuslan Bukin u_int flags) 467*3024e8afSRuslan Bukin { 468*3024e8afSRuslan Bukin struct iommu_map_entry *next, *prev; 469*3024e8afSRuslan Bukin bool found; 470*3024e8afSRuslan Bukin 471*3024e8afSRuslan Bukin IOMMU_DOMAIN_ASSERT_LOCKED(domain); 472*3024e8afSRuslan Bukin 473*3024e8afSRuslan Bukin if ((entry->start & IOMMU_PAGE_MASK) != 0 || 474*3024e8afSRuslan Bukin (entry->end & IOMMU_PAGE_MASK) != 0) 475*3024e8afSRuslan Bukin return (EINVAL); 476*3024e8afSRuslan Bukin if (entry->start >= entry->end) 477*3024e8afSRuslan Bukin return (EINVAL); 478*3024e8afSRuslan Bukin if (entry->end >= domain->end) 479*3024e8afSRuslan Bukin return (EINVAL); 480*3024e8afSRuslan Bukin 481*3024e8afSRuslan Bukin next = RB_NFIND(iommu_gas_entries_tree, &domain->rb_root, entry); 482*3024e8afSRuslan Bukin KASSERT(next != NULL, ("next must be non-null %p %jx", domain, 483*3024e8afSRuslan Bukin (uintmax_t)entry->start)); 484*3024e8afSRuslan Bukin prev = RB_PREV(iommu_gas_entries_tree, &domain->rb_root, next); 485*3024e8afSRuslan Bukin /* prev could be NULL */ 486*3024e8afSRuslan Bukin 487*3024e8afSRuslan Bukin /* 488*3024e8afSRuslan Bukin * Adapt to broken BIOSes which specify overlapping RMRR 489*3024e8afSRuslan Bukin * entries. 490*3024e8afSRuslan Bukin * 491*3024e8afSRuslan Bukin * XXXKIB: this does not handle a case when prev or next 492*3024e8afSRuslan Bukin * entries are completely covered by the current one, which 493*3024e8afSRuslan Bukin * extends both ways. 494*3024e8afSRuslan Bukin */ 495*3024e8afSRuslan Bukin if (prev != NULL && prev->end > entry->start && 496*3024e8afSRuslan Bukin (prev->flags & IOMMU_MAP_ENTRY_PLACE) == 0) { 497*3024e8afSRuslan Bukin if ((flags & IOMMU_MF_RMRR) == 0 || 498*3024e8afSRuslan Bukin (prev->flags & IOMMU_MAP_ENTRY_RMRR) == 0) 499*3024e8afSRuslan Bukin return (EBUSY); 500*3024e8afSRuslan Bukin entry->start = prev->end; 501*3024e8afSRuslan Bukin } 502*3024e8afSRuslan Bukin if (next->start < entry->end && 503*3024e8afSRuslan Bukin (next->flags & IOMMU_MAP_ENTRY_PLACE) == 0) { 504*3024e8afSRuslan Bukin if ((flags & IOMMU_MF_RMRR) == 0 || 505*3024e8afSRuslan Bukin (next->flags & IOMMU_MAP_ENTRY_RMRR) == 0) 506*3024e8afSRuslan Bukin return (EBUSY); 507*3024e8afSRuslan Bukin entry->end = next->start; 508*3024e8afSRuslan Bukin } 509*3024e8afSRuslan Bukin if (entry->end == entry->start) 510*3024e8afSRuslan Bukin return (0); 511*3024e8afSRuslan Bukin 512*3024e8afSRuslan Bukin if (prev != NULL && prev->end > entry->start) { 513*3024e8afSRuslan Bukin /* This assumes that prev is the placeholder entry. */ 514*3024e8afSRuslan Bukin iommu_gas_rb_remove(domain, prev); 515*3024e8afSRuslan Bukin prev = NULL; 516*3024e8afSRuslan Bukin } 517*3024e8afSRuslan Bukin if (next->start < entry->end) { 518*3024e8afSRuslan Bukin iommu_gas_rb_remove(domain, next); 519*3024e8afSRuslan Bukin next = NULL; 520*3024e8afSRuslan Bukin } 521*3024e8afSRuslan Bukin 522*3024e8afSRuslan Bukin found = iommu_gas_rb_insert(domain, entry); 523*3024e8afSRuslan Bukin KASSERT(found, ("found RMRR dup %p start %jx end %jx", 524*3024e8afSRuslan Bukin domain, (uintmax_t)entry->start, (uintmax_t)entry->end)); 525*3024e8afSRuslan Bukin if ((flags & IOMMU_MF_RMRR) != 0) 526*3024e8afSRuslan Bukin entry->flags = IOMMU_MAP_ENTRY_RMRR; 527*3024e8afSRuslan Bukin 528*3024e8afSRuslan Bukin #ifdef INVARIANTS 529*3024e8afSRuslan Bukin struct iommu_map_entry *ip, *in; 530*3024e8afSRuslan Bukin ip = RB_PREV(iommu_gas_entries_tree, &domain->rb_root, entry); 531*3024e8afSRuslan Bukin in = RB_NEXT(iommu_gas_entries_tree, &domain->rb_root, entry); 532*3024e8afSRuslan Bukin KASSERT(prev == NULL || ip == prev, 533*3024e8afSRuslan Bukin ("RMRR %p (%jx %jx) prev %p (%jx %jx) ins prev %p (%jx %jx)", 534*3024e8afSRuslan Bukin entry, entry->start, entry->end, prev, 535*3024e8afSRuslan Bukin prev == NULL ? 0 : prev->start, prev == NULL ? 0 : prev->end, 536*3024e8afSRuslan Bukin ip, ip == NULL ? 0 : ip->start, ip == NULL ? 0 : ip->end)); 537*3024e8afSRuslan Bukin KASSERT(next == NULL || in == next, 538*3024e8afSRuslan Bukin ("RMRR %p (%jx %jx) next %p (%jx %jx) ins next %p (%jx %jx)", 539*3024e8afSRuslan Bukin entry, entry->start, entry->end, next, 540*3024e8afSRuslan Bukin next == NULL ? 0 : next->start, next == NULL ? 0 : next->end, 541*3024e8afSRuslan Bukin in, in == NULL ? 0 : in->start, in == NULL ? 0 : in->end)); 542*3024e8afSRuslan Bukin #endif 543*3024e8afSRuslan Bukin 544*3024e8afSRuslan Bukin return (0); 545*3024e8afSRuslan Bukin } 546*3024e8afSRuslan Bukin 547*3024e8afSRuslan Bukin void 548*3024e8afSRuslan Bukin iommu_gas_free_space(struct iommu_domain *domain, struct iommu_map_entry *entry) 549*3024e8afSRuslan Bukin { 550*3024e8afSRuslan Bukin 551*3024e8afSRuslan Bukin IOMMU_DOMAIN_ASSERT_LOCKED(domain); 552*3024e8afSRuslan Bukin KASSERT((entry->flags & (IOMMU_MAP_ENTRY_PLACE | IOMMU_MAP_ENTRY_RMRR | 553*3024e8afSRuslan Bukin IOMMU_MAP_ENTRY_MAP)) == IOMMU_MAP_ENTRY_MAP, 554*3024e8afSRuslan Bukin ("permanent entry %p %p", domain, entry)); 555*3024e8afSRuslan Bukin 556*3024e8afSRuslan Bukin iommu_gas_rb_remove(domain, entry); 557*3024e8afSRuslan Bukin entry->flags &= ~IOMMU_MAP_ENTRY_MAP; 558*3024e8afSRuslan Bukin #ifdef INVARIANTS 559*3024e8afSRuslan Bukin if (iommu_check_free) 560*3024e8afSRuslan Bukin iommu_gas_check_free(domain); 561*3024e8afSRuslan Bukin #endif 562*3024e8afSRuslan Bukin } 563*3024e8afSRuslan Bukin 564*3024e8afSRuslan Bukin void 565*3024e8afSRuslan Bukin iommu_gas_free_region(struct iommu_domain *domain, struct iommu_map_entry *entry) 566*3024e8afSRuslan Bukin { 567*3024e8afSRuslan Bukin struct iommu_map_entry *next, *prev; 568*3024e8afSRuslan Bukin 569*3024e8afSRuslan Bukin IOMMU_DOMAIN_ASSERT_LOCKED(domain); 570*3024e8afSRuslan Bukin KASSERT((entry->flags & (IOMMU_MAP_ENTRY_PLACE | IOMMU_MAP_ENTRY_RMRR | 571*3024e8afSRuslan Bukin IOMMU_MAP_ENTRY_MAP)) == IOMMU_MAP_ENTRY_RMRR, 572*3024e8afSRuslan Bukin ("non-RMRR entry %p %p", domain, entry)); 573*3024e8afSRuslan Bukin 574*3024e8afSRuslan Bukin prev = RB_PREV(iommu_gas_entries_tree, &domain->rb_root, entry); 575*3024e8afSRuslan Bukin next = RB_NEXT(iommu_gas_entries_tree, &domain->rb_root, entry); 576*3024e8afSRuslan Bukin iommu_gas_rb_remove(domain, entry); 577*3024e8afSRuslan Bukin entry->flags &= ~IOMMU_MAP_ENTRY_RMRR; 578*3024e8afSRuslan Bukin 579*3024e8afSRuslan Bukin if (prev == NULL) 580*3024e8afSRuslan Bukin iommu_gas_rb_insert(domain, domain->first_place); 581*3024e8afSRuslan Bukin if (next == NULL) 582*3024e8afSRuslan Bukin iommu_gas_rb_insert(domain, domain->last_place); 583*3024e8afSRuslan Bukin } 584*3024e8afSRuslan Bukin 585*3024e8afSRuslan Bukin int 586*3024e8afSRuslan Bukin iommu_gas_map(struct iommu_domain *domain, 587*3024e8afSRuslan Bukin const struct bus_dma_tag_common *common, iommu_gaddr_t size, int offset, 588*3024e8afSRuslan Bukin u_int eflags, u_int flags, vm_page_t *ma, struct iommu_map_entry **res) 589*3024e8afSRuslan Bukin { 590*3024e8afSRuslan Bukin struct iommu_map_entry *entry; 591*3024e8afSRuslan Bukin int error; 592*3024e8afSRuslan Bukin 593*3024e8afSRuslan Bukin KASSERT((flags & ~(IOMMU_MF_CANWAIT | IOMMU_MF_CANSPLIT)) == 0, 594*3024e8afSRuslan Bukin ("invalid flags 0x%x", flags)); 595*3024e8afSRuslan Bukin 596*3024e8afSRuslan Bukin entry = iommu_gas_alloc_entry(domain, 597*3024e8afSRuslan Bukin (flags & IOMMU_MF_CANWAIT) != 0 ? DMAR_PGF_WAITOK : 0); 598*3024e8afSRuslan Bukin if (entry == NULL) 599*3024e8afSRuslan Bukin return (ENOMEM); 600*3024e8afSRuslan Bukin IOMMU_DOMAIN_LOCK(domain); 601*3024e8afSRuslan Bukin error = iommu_gas_find_space(domain, common, size, offset, flags, 602*3024e8afSRuslan Bukin entry); 603*3024e8afSRuslan Bukin if (error == ENOMEM) { 604*3024e8afSRuslan Bukin IOMMU_DOMAIN_UNLOCK(domain); 605*3024e8afSRuslan Bukin iommu_gas_free_entry(domain, entry); 606*3024e8afSRuslan Bukin return (error); 607*3024e8afSRuslan Bukin } 608*3024e8afSRuslan Bukin #ifdef INVARIANTS 609*3024e8afSRuslan Bukin if (iommu_check_free) 610*3024e8afSRuslan Bukin iommu_gas_check_free(domain); 611*3024e8afSRuslan Bukin #endif 612*3024e8afSRuslan Bukin KASSERT(error == 0, 613*3024e8afSRuslan Bukin ("unexpected error %d from iommu_gas_find_entry", error)); 614*3024e8afSRuslan Bukin KASSERT(entry->end < domain->end, ("allocated GPA %jx, max GPA %jx", 615*3024e8afSRuslan Bukin (uintmax_t)entry->end, (uintmax_t)domain->end)); 616*3024e8afSRuslan Bukin entry->flags |= eflags; 617*3024e8afSRuslan Bukin IOMMU_DOMAIN_UNLOCK(domain); 618*3024e8afSRuslan Bukin 619*3024e8afSRuslan Bukin error = domain_map_buf(domain, entry->start, entry->end - entry->start, 620*3024e8afSRuslan Bukin ma, eflags, 621*3024e8afSRuslan Bukin ((flags & IOMMU_MF_CANWAIT) != 0 ? DMAR_PGF_WAITOK : 0)); 622*3024e8afSRuslan Bukin if (error == ENOMEM) { 623*3024e8afSRuslan Bukin iommu_domain_unload_entry(entry, true); 624*3024e8afSRuslan Bukin return (error); 625*3024e8afSRuslan Bukin } 626*3024e8afSRuslan Bukin KASSERT(error == 0, 627*3024e8afSRuslan Bukin ("unexpected error %d from domain_map_buf", error)); 628*3024e8afSRuslan Bukin 629*3024e8afSRuslan Bukin *res = entry; 630*3024e8afSRuslan Bukin return (0); 631*3024e8afSRuslan Bukin } 632*3024e8afSRuslan Bukin 633*3024e8afSRuslan Bukin int 634*3024e8afSRuslan Bukin iommu_gas_map_region(struct iommu_domain *domain, struct iommu_map_entry *entry, 635*3024e8afSRuslan Bukin u_int eflags, u_int flags, vm_page_t *ma) 636*3024e8afSRuslan Bukin { 637*3024e8afSRuslan Bukin iommu_gaddr_t start; 638*3024e8afSRuslan Bukin int error; 639*3024e8afSRuslan Bukin 640*3024e8afSRuslan Bukin KASSERT(entry->flags == 0, ("used RMRR entry %p %p %x", domain, 641*3024e8afSRuslan Bukin entry, entry->flags)); 642*3024e8afSRuslan Bukin KASSERT((flags & ~(IOMMU_MF_CANWAIT | IOMMU_MF_RMRR)) == 0, 643*3024e8afSRuslan Bukin ("invalid flags 0x%x", flags)); 644*3024e8afSRuslan Bukin 645*3024e8afSRuslan Bukin start = entry->start; 646*3024e8afSRuslan Bukin IOMMU_DOMAIN_LOCK(domain); 647*3024e8afSRuslan Bukin error = iommu_gas_alloc_region(domain, entry, flags); 648*3024e8afSRuslan Bukin if (error != 0) { 649*3024e8afSRuslan Bukin IOMMU_DOMAIN_UNLOCK(domain); 650*3024e8afSRuslan Bukin return (error); 651*3024e8afSRuslan Bukin } 652*3024e8afSRuslan Bukin entry->flags |= eflags; 653*3024e8afSRuslan Bukin IOMMU_DOMAIN_UNLOCK(domain); 654*3024e8afSRuslan Bukin if (entry->end == entry->start) 655*3024e8afSRuslan Bukin return (0); 656*3024e8afSRuslan Bukin 657*3024e8afSRuslan Bukin error = domain_map_buf(domain, entry->start, entry->end - entry->start, 658*3024e8afSRuslan Bukin ma + OFF_TO_IDX(start - entry->start), eflags, 659*3024e8afSRuslan Bukin ((flags & IOMMU_MF_CANWAIT) != 0 ? DMAR_PGF_WAITOK : 0)); 660*3024e8afSRuslan Bukin if (error == ENOMEM) { 661*3024e8afSRuslan Bukin iommu_domain_unload_entry(entry, false); 662*3024e8afSRuslan Bukin return (error); 663*3024e8afSRuslan Bukin } 664*3024e8afSRuslan Bukin KASSERT(error == 0, 665*3024e8afSRuslan Bukin ("unexpected error %d from domain_map_buf", error)); 666*3024e8afSRuslan Bukin 667*3024e8afSRuslan Bukin return (0); 668*3024e8afSRuslan Bukin } 669*3024e8afSRuslan Bukin 670*3024e8afSRuslan Bukin int 671*3024e8afSRuslan Bukin iommu_gas_reserve_region(struct iommu_domain *domain, iommu_gaddr_t start, 672*3024e8afSRuslan Bukin iommu_gaddr_t end) 673*3024e8afSRuslan Bukin { 674*3024e8afSRuslan Bukin struct iommu_map_entry *entry; 675*3024e8afSRuslan Bukin int error; 676*3024e8afSRuslan Bukin 677*3024e8afSRuslan Bukin entry = iommu_gas_alloc_entry(domain, DMAR_PGF_WAITOK); 678*3024e8afSRuslan Bukin entry->start = start; 679*3024e8afSRuslan Bukin entry->end = end; 680*3024e8afSRuslan Bukin IOMMU_DOMAIN_LOCK(domain); 681*3024e8afSRuslan Bukin error = iommu_gas_alloc_region(domain, entry, IOMMU_MF_CANWAIT); 682*3024e8afSRuslan Bukin if (error == 0) 683*3024e8afSRuslan Bukin entry->flags |= IOMMU_MAP_ENTRY_UNMAPPED; 684*3024e8afSRuslan Bukin IOMMU_DOMAIN_UNLOCK(domain); 685*3024e8afSRuslan Bukin if (error != 0) 686*3024e8afSRuslan Bukin iommu_gas_free_entry(domain, entry); 687*3024e8afSRuslan Bukin return (error); 688*3024e8afSRuslan Bukin } 689*3024e8afSRuslan Bukin 690*3024e8afSRuslan Bukin struct iommu_map_entry * 691*3024e8afSRuslan Bukin iommu_map_alloc_entry(struct iommu_domain *domain, u_int flags) 692*3024e8afSRuslan Bukin { 693*3024e8afSRuslan Bukin struct iommu_map_entry *res; 694*3024e8afSRuslan Bukin 695*3024e8afSRuslan Bukin res = iommu_gas_alloc_entry(domain, flags); 696*3024e8afSRuslan Bukin 697*3024e8afSRuslan Bukin return (res); 698*3024e8afSRuslan Bukin } 699*3024e8afSRuslan Bukin 700*3024e8afSRuslan Bukin void 701*3024e8afSRuslan Bukin iommu_map_free_entry(struct iommu_domain *domain, struct iommu_map_entry *entry) 702*3024e8afSRuslan Bukin { 703*3024e8afSRuslan Bukin 704*3024e8afSRuslan Bukin iommu_gas_free_entry(domain, entry); 705*3024e8afSRuslan Bukin } 706*3024e8afSRuslan Bukin 707*3024e8afSRuslan Bukin int 708*3024e8afSRuslan Bukin iommu_map(struct iommu_domain *domain, 709*3024e8afSRuslan Bukin const struct bus_dma_tag_common *common, iommu_gaddr_t size, int offset, 710*3024e8afSRuslan Bukin u_int eflags, u_int flags, vm_page_t *ma, struct iommu_map_entry **res) 711*3024e8afSRuslan Bukin { 712*3024e8afSRuslan Bukin int error; 713*3024e8afSRuslan Bukin 714*3024e8afSRuslan Bukin error = iommu_gas_map(domain, common, size, offset, eflags, flags, 715*3024e8afSRuslan Bukin ma, res); 716*3024e8afSRuslan Bukin 717*3024e8afSRuslan Bukin return (error); 718*3024e8afSRuslan Bukin } 719*3024e8afSRuslan Bukin 720*3024e8afSRuslan Bukin int 721*3024e8afSRuslan Bukin iommu_map_region(struct iommu_domain *domain, struct iommu_map_entry *entry, 722*3024e8afSRuslan Bukin u_int eflags, u_int flags, vm_page_t *ma) 723*3024e8afSRuslan Bukin { 724*3024e8afSRuslan Bukin int error; 725*3024e8afSRuslan Bukin 726*3024e8afSRuslan Bukin error = iommu_gas_map_region(domain, entry, eflags, flags, ma); 727*3024e8afSRuslan Bukin 728*3024e8afSRuslan Bukin return (error); 729*3024e8afSRuslan Bukin } 730