1*86be9f0dSKonstantin Belousov /*- 2*86be9f0dSKonstantin Belousov * Copyright (c) 2013 The FreeBSD Foundation 3*86be9f0dSKonstantin Belousov * All rights reserved. 4*86be9f0dSKonstantin Belousov * 5*86be9f0dSKonstantin Belousov * This software was developed by Konstantin Belousov <kib@FreeBSD.org> 6*86be9f0dSKonstantin Belousov * under sponsorship from the FreeBSD Foundation. 7*86be9f0dSKonstantin Belousov * 8*86be9f0dSKonstantin Belousov * Redistribution and use in source and binary forms, with or without 9*86be9f0dSKonstantin Belousov * modification, are permitted provided that the following conditions 10*86be9f0dSKonstantin Belousov * are met: 11*86be9f0dSKonstantin Belousov * 1. Redistributions of source code must retain the above copyright 12*86be9f0dSKonstantin Belousov * notice, this list of conditions and the following disclaimer. 13*86be9f0dSKonstantin Belousov * 2. Redistributions in binary form must reproduce the above copyright 14*86be9f0dSKonstantin Belousov * notice, this list of conditions and the following disclaimer in the 15*86be9f0dSKonstantin Belousov * documentation and/or other materials provided with the distribution. 16*86be9f0dSKonstantin Belousov * 17*86be9f0dSKonstantin Belousov * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18*86be9f0dSKonstantin Belousov * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19*86be9f0dSKonstantin Belousov * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20*86be9f0dSKonstantin Belousov * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21*86be9f0dSKonstantin Belousov * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22*86be9f0dSKonstantin Belousov * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23*86be9f0dSKonstantin Belousov * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24*86be9f0dSKonstantin Belousov * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25*86be9f0dSKonstantin Belousov * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26*86be9f0dSKonstantin Belousov * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27*86be9f0dSKonstantin Belousov * SUCH DAMAGE. 28*86be9f0dSKonstantin Belousov */ 29*86be9f0dSKonstantin Belousov 30*86be9f0dSKonstantin Belousov #include <sys/cdefs.h> 31*86be9f0dSKonstantin Belousov __FBSDID("$FreeBSD$"); 32*86be9f0dSKonstantin Belousov 33*86be9f0dSKonstantin Belousov #include <sys/param.h> 34*86be9f0dSKonstantin Belousov #include <sys/systm.h> 35*86be9f0dSKonstantin Belousov #include <sys/malloc.h> 36*86be9f0dSKonstantin Belousov #include <sys/bus.h> 37*86be9f0dSKonstantin Belousov #include <sys/interrupt.h> 38*86be9f0dSKonstantin Belousov #include <sys/kernel.h> 39*86be9f0dSKonstantin Belousov #include <sys/ktr.h> 40*86be9f0dSKonstantin Belousov #include <sys/limits.h> 41*86be9f0dSKonstantin Belousov #include <sys/lock.h> 42*86be9f0dSKonstantin Belousov #include <sys/memdesc.h> 43*86be9f0dSKonstantin Belousov #include <sys/mutex.h> 44*86be9f0dSKonstantin Belousov #include <sys/proc.h> 45*86be9f0dSKonstantin Belousov #include <sys/rwlock.h> 46*86be9f0dSKonstantin Belousov #include <sys/rman.h> 47*86be9f0dSKonstantin Belousov #include <sys/sysctl.h> 48*86be9f0dSKonstantin Belousov #include <sys/taskqueue.h> 49*86be9f0dSKonstantin Belousov #include <sys/tree.h> 50*86be9f0dSKonstantin Belousov #include <sys/uio.h> 51*86be9f0dSKonstantin Belousov #include <vm/vm.h> 52*86be9f0dSKonstantin Belousov #include <vm/vm_extern.h> 53*86be9f0dSKonstantin Belousov #include <vm/vm_kern.h> 54*86be9f0dSKonstantin Belousov #include <vm/vm_object.h> 55*86be9f0dSKonstantin Belousov #include <vm/vm_page.h> 56*86be9f0dSKonstantin Belousov #include <vm/vm_pager.h> 57*86be9f0dSKonstantin Belousov #include <vm/vm_map.h> 58*86be9f0dSKonstantin Belousov #include <machine/atomic.h> 59*86be9f0dSKonstantin Belousov #include <machine/bus.h> 60*86be9f0dSKonstantin Belousov #include <machine/md_var.h> 61*86be9f0dSKonstantin Belousov #include <machine/specialreg.h> 62*86be9f0dSKonstantin Belousov #include <x86/include/busdma_impl.h> 63*86be9f0dSKonstantin Belousov #include <x86/iommu/intel_reg.h> 64*86be9f0dSKonstantin Belousov #include <x86/iommu/busdma_dmar.h> 65*86be9f0dSKonstantin Belousov #include <x86/iommu/intel_dmar.h> 66*86be9f0dSKonstantin Belousov #include <dev/pci/pcivar.h> 67*86be9f0dSKonstantin Belousov 68*86be9f0dSKonstantin Belousov static MALLOC_DEFINE(M_DMAR_CTX, "dmar_ctx", "Intel DMAR Context"); 69*86be9f0dSKonstantin Belousov 70*86be9f0dSKonstantin Belousov static void dmar_ctx_unload_task(void *arg, int pending); 71*86be9f0dSKonstantin Belousov 72*86be9f0dSKonstantin Belousov static void 73*86be9f0dSKonstantin Belousov dmar_ensure_ctx_page(struct dmar_unit *dmar, int bus) 74*86be9f0dSKonstantin Belousov { 75*86be9f0dSKonstantin Belousov struct sf_buf *sf; 76*86be9f0dSKonstantin Belousov dmar_root_entry_t *re; 77*86be9f0dSKonstantin Belousov vm_page_t ctxm; 78*86be9f0dSKonstantin Belousov 79*86be9f0dSKonstantin Belousov /* 80*86be9f0dSKonstantin Belousov * Allocated context page must be linked. 81*86be9f0dSKonstantin Belousov */ 82*86be9f0dSKonstantin Belousov ctxm = dmar_pgalloc(dmar->ctx_obj, 1 + bus, DMAR_PGF_NOALLOC); 83*86be9f0dSKonstantin Belousov if (ctxm != NULL) 84*86be9f0dSKonstantin Belousov return; 85*86be9f0dSKonstantin Belousov 86*86be9f0dSKonstantin Belousov /* 87*86be9f0dSKonstantin Belousov * Page not present, allocate and link. Note that other 88*86be9f0dSKonstantin Belousov * thread might execute this sequence in parallel. This 89*86be9f0dSKonstantin Belousov * should be safe, because the context entries written by both 90*86be9f0dSKonstantin Belousov * threads are equal. 91*86be9f0dSKonstantin Belousov */ 92*86be9f0dSKonstantin Belousov TD_PREP_PINNED_ASSERT; 93*86be9f0dSKonstantin Belousov ctxm = dmar_pgalloc(dmar->ctx_obj, 1 + bus, DMAR_PGF_ZERO | 94*86be9f0dSKonstantin Belousov DMAR_PGF_WAITOK); 95*86be9f0dSKonstantin Belousov re = dmar_map_pgtbl(dmar->ctx_obj, 0, DMAR_PGF_NOALLOC, &sf); 96*86be9f0dSKonstantin Belousov re += bus; 97*86be9f0dSKonstantin Belousov dmar_pte_store(&re->r1, DMAR_ROOT_R1_P | (DMAR_ROOT_R1_CTP_MASK & 98*86be9f0dSKonstantin Belousov VM_PAGE_TO_PHYS(ctxm))); 99*86be9f0dSKonstantin Belousov dmar_unmap_pgtbl(sf, DMAR_IS_COHERENT(dmar)); 100*86be9f0dSKonstantin Belousov TD_PINNED_ASSERT; 101*86be9f0dSKonstantin Belousov } 102*86be9f0dSKonstantin Belousov 103*86be9f0dSKonstantin Belousov static dmar_ctx_entry_t * 104*86be9f0dSKonstantin Belousov dmar_map_ctx_entry(struct dmar_ctx *ctx, struct sf_buf **sfp) 105*86be9f0dSKonstantin Belousov { 106*86be9f0dSKonstantin Belousov dmar_ctx_entry_t *ctxp; 107*86be9f0dSKonstantin Belousov 108*86be9f0dSKonstantin Belousov ctxp = dmar_map_pgtbl(ctx->dmar->ctx_obj, 1 + ctx->bus, 109*86be9f0dSKonstantin Belousov DMAR_PGF_NOALLOC | DMAR_PGF_WAITOK, sfp); 110*86be9f0dSKonstantin Belousov ctxp += ((ctx->slot & 0x1f) << 3) + (ctx->func & 0x7); 111*86be9f0dSKonstantin Belousov return (ctxp); 112*86be9f0dSKonstantin Belousov } 113*86be9f0dSKonstantin Belousov 114*86be9f0dSKonstantin Belousov static void 115*86be9f0dSKonstantin Belousov ctx_tag_init(struct dmar_ctx *ctx) 116*86be9f0dSKonstantin Belousov { 117*86be9f0dSKonstantin Belousov bus_addr_t maxaddr; 118*86be9f0dSKonstantin Belousov 119*86be9f0dSKonstantin Belousov maxaddr = MIN(ctx->end, BUS_SPACE_MAXADDR); 120*86be9f0dSKonstantin Belousov ctx->ctx_tag.common.ref_count = 1; /* Prevent free */ 121*86be9f0dSKonstantin Belousov ctx->ctx_tag.common.impl = &bus_dma_dmar_impl; 122*86be9f0dSKonstantin Belousov ctx->ctx_tag.common.boundary = PCI_DMA_BOUNDARY; 123*86be9f0dSKonstantin Belousov ctx->ctx_tag.common.lowaddr = maxaddr; 124*86be9f0dSKonstantin Belousov ctx->ctx_tag.common.highaddr = maxaddr; 125*86be9f0dSKonstantin Belousov ctx->ctx_tag.common.maxsize = maxaddr; 126*86be9f0dSKonstantin Belousov ctx->ctx_tag.common.nsegments = BUS_SPACE_UNRESTRICTED; 127*86be9f0dSKonstantin Belousov ctx->ctx_tag.common.maxsegsz = maxaddr; 128*86be9f0dSKonstantin Belousov ctx->ctx_tag.ctx = ctx; 129*86be9f0dSKonstantin Belousov /* XXXKIB initialize tag further */ 130*86be9f0dSKonstantin Belousov } 131*86be9f0dSKonstantin Belousov 132*86be9f0dSKonstantin Belousov static void 133*86be9f0dSKonstantin Belousov ctx_id_entry_init(struct dmar_ctx *ctx, dmar_ctx_entry_t *ctxp) 134*86be9f0dSKonstantin Belousov { 135*86be9f0dSKonstantin Belousov struct dmar_unit *unit; 136*86be9f0dSKonstantin Belousov vm_page_t ctx_root; 137*86be9f0dSKonstantin Belousov 138*86be9f0dSKonstantin Belousov unit = ctx->dmar; 139*86be9f0dSKonstantin Belousov KASSERT(ctxp->ctx1 == 0 && ctxp->ctx2 == 0, 140*86be9f0dSKonstantin Belousov ("dmar%d: initialized ctx entry %d:%d:%d 0x%jx 0x%jx", 141*86be9f0dSKonstantin Belousov unit->unit, ctx->bus, ctx->slot, ctx->func, ctxp->ctx1, 142*86be9f0dSKonstantin Belousov ctxp->ctx2)); 143*86be9f0dSKonstantin Belousov ctxp->ctx2 = DMAR_CTX2_DID(ctx->domain); 144*86be9f0dSKonstantin Belousov ctxp->ctx2 |= ctx->awlvl; 145*86be9f0dSKonstantin Belousov if ((ctx->flags & DMAR_CTX_IDMAP) != 0 && 146*86be9f0dSKonstantin Belousov (unit->hw_ecap & DMAR_ECAP_PT) != 0) { 147*86be9f0dSKonstantin Belousov KASSERT(ctx->pgtbl_obj == NULL, 148*86be9f0dSKonstantin Belousov ("ctx %p non-null pgtbl_obj", ctx)); 149*86be9f0dSKonstantin Belousov dmar_pte_store(&ctxp->ctx1, DMAR_CTX1_T_PASS | DMAR_CTX1_P); 150*86be9f0dSKonstantin Belousov } else { 151*86be9f0dSKonstantin Belousov ctx_root = dmar_pgalloc(ctx->pgtbl_obj, 0, DMAR_PGF_NOALLOC); 152*86be9f0dSKonstantin Belousov dmar_pte_store(&ctxp->ctx1, DMAR_CTX1_T_UNTR | 153*86be9f0dSKonstantin Belousov (DMAR_CTX1_ASR_MASK & VM_PAGE_TO_PHYS(ctx_root)) | 154*86be9f0dSKonstantin Belousov DMAR_CTX1_P); 155*86be9f0dSKonstantin Belousov } 156*86be9f0dSKonstantin Belousov } 157*86be9f0dSKonstantin Belousov 158*86be9f0dSKonstantin Belousov static int 159*86be9f0dSKonstantin Belousov ctx_init_rmrr(struct dmar_ctx *ctx, device_t dev) 160*86be9f0dSKonstantin Belousov { 161*86be9f0dSKonstantin Belousov struct dmar_map_entries_tailq rmrr_entries; 162*86be9f0dSKonstantin Belousov struct dmar_map_entry *entry, *entry1; 163*86be9f0dSKonstantin Belousov vm_page_t *ma; 164*86be9f0dSKonstantin Belousov dmar_gaddr_t start, end; 165*86be9f0dSKonstantin Belousov vm_pindex_t size, i; 166*86be9f0dSKonstantin Belousov int error, error1; 167*86be9f0dSKonstantin Belousov 168*86be9f0dSKonstantin Belousov error = 0; 169*86be9f0dSKonstantin Belousov TAILQ_INIT(&rmrr_entries); 170*86be9f0dSKonstantin Belousov dmar_ctx_parse_rmrr(ctx, dev, &rmrr_entries); 171*86be9f0dSKonstantin Belousov TAILQ_FOREACH_SAFE(entry, &rmrr_entries, unroll_link, entry1) { 172*86be9f0dSKonstantin Belousov /* 173*86be9f0dSKonstantin Belousov * VT-d specification requires that the start of an 174*86be9f0dSKonstantin Belousov * RMRR entry is 4k-aligned. Buggy BIOSes put 175*86be9f0dSKonstantin Belousov * anything into the start and end fields. Truncate 176*86be9f0dSKonstantin Belousov * and round as neccesary. 177*86be9f0dSKonstantin Belousov * 178*86be9f0dSKonstantin Belousov * We also allow the overlapping RMRR entries, see 179*86be9f0dSKonstantin Belousov * dmar_gas_alloc_region(). 180*86be9f0dSKonstantin Belousov */ 181*86be9f0dSKonstantin Belousov start = entry->start; 182*86be9f0dSKonstantin Belousov end = entry->end; 183*86be9f0dSKonstantin Belousov entry->start = trunc_page(start); 184*86be9f0dSKonstantin Belousov entry->end = round_page(end); 185*86be9f0dSKonstantin Belousov size = OFF_TO_IDX(entry->end - entry->start); 186*86be9f0dSKonstantin Belousov ma = malloc(sizeof(vm_page_t) * size, M_TEMP, M_WAITOK); 187*86be9f0dSKonstantin Belousov for (i = 0; i < size; i++) { 188*86be9f0dSKonstantin Belousov ma[i] = vm_page_getfake(entry->start + PAGE_SIZE * i, 189*86be9f0dSKonstantin Belousov VM_MEMATTR_DEFAULT); 190*86be9f0dSKonstantin Belousov } 191*86be9f0dSKonstantin Belousov error1 = dmar_gas_map_region(ctx, entry, DMAR_MAP_ENTRY_READ | 192*86be9f0dSKonstantin Belousov DMAR_MAP_ENTRY_WRITE, DMAR_GM_CANWAIT, ma); 193*86be9f0dSKonstantin Belousov /* 194*86be9f0dSKonstantin Belousov * Non-failed RMRR entries are owned by context rb 195*86be9f0dSKonstantin Belousov * tree. Get rid of the failed entry, but do not stop 196*86be9f0dSKonstantin Belousov * the loop. Rest of the parsed RMRR entries are 197*86be9f0dSKonstantin Belousov * loaded and removed on the context destruction. 198*86be9f0dSKonstantin Belousov */ 199*86be9f0dSKonstantin Belousov if (error1 == 0 && entry->end != entry->start) { 200*86be9f0dSKonstantin Belousov DMAR_LOCK(ctx->dmar); 201*86be9f0dSKonstantin Belousov ctx->flags |= DMAR_CTX_RMRR; 202*86be9f0dSKonstantin Belousov DMAR_UNLOCK(ctx->dmar); 203*86be9f0dSKonstantin Belousov } else { 204*86be9f0dSKonstantin Belousov if (error1 != 0) { 205*86be9f0dSKonstantin Belousov device_printf(dev, 206*86be9f0dSKonstantin Belousov "dmar%d failed to map RMRR region (%jx, %jx) %d\n", 207*86be9f0dSKonstantin Belousov ctx->dmar->unit, start, end, error1); 208*86be9f0dSKonstantin Belousov error = error1; 209*86be9f0dSKonstantin Belousov } 210*86be9f0dSKonstantin Belousov TAILQ_REMOVE(&rmrr_entries, entry, unroll_link); 211*86be9f0dSKonstantin Belousov dmar_gas_free_entry(ctx, entry); 212*86be9f0dSKonstantin Belousov } 213*86be9f0dSKonstantin Belousov for (i = 0; i < size; i++) 214*86be9f0dSKonstantin Belousov vm_page_putfake(ma[i]); 215*86be9f0dSKonstantin Belousov free(ma, M_TEMP); 216*86be9f0dSKonstantin Belousov } 217*86be9f0dSKonstantin Belousov return (error); 218*86be9f0dSKonstantin Belousov } 219*86be9f0dSKonstantin Belousov 220*86be9f0dSKonstantin Belousov static struct dmar_ctx * 221*86be9f0dSKonstantin Belousov dmar_get_ctx_alloc(struct dmar_unit *dmar, int bus, int slot, int func) 222*86be9f0dSKonstantin Belousov { 223*86be9f0dSKonstantin Belousov struct dmar_ctx *ctx; 224*86be9f0dSKonstantin Belousov 225*86be9f0dSKonstantin Belousov ctx = malloc(sizeof(*ctx), M_DMAR_CTX, M_WAITOK | M_ZERO); 226*86be9f0dSKonstantin Belousov RB_INIT(&ctx->rb_root); 227*86be9f0dSKonstantin Belousov TAILQ_INIT(&ctx->unload_entries); 228*86be9f0dSKonstantin Belousov TASK_INIT(&ctx->unload_task, 0, dmar_ctx_unload_task, ctx); 229*86be9f0dSKonstantin Belousov mtx_init(&ctx->lock, "dmarctx", NULL, MTX_DEF); 230*86be9f0dSKonstantin Belousov ctx->dmar = dmar; 231*86be9f0dSKonstantin Belousov ctx->bus = bus; 232*86be9f0dSKonstantin Belousov ctx->slot = slot; 233*86be9f0dSKonstantin Belousov ctx->func = func; 234*86be9f0dSKonstantin Belousov return (ctx); 235*86be9f0dSKonstantin Belousov } 236*86be9f0dSKonstantin Belousov 237*86be9f0dSKonstantin Belousov static void 238*86be9f0dSKonstantin Belousov dmar_ctx_dtr(struct dmar_ctx *ctx, bool gas_inited, bool pgtbl_inited) 239*86be9f0dSKonstantin Belousov { 240*86be9f0dSKonstantin Belousov 241*86be9f0dSKonstantin Belousov if (gas_inited) { 242*86be9f0dSKonstantin Belousov DMAR_CTX_LOCK(ctx); 243*86be9f0dSKonstantin Belousov dmar_gas_fini_ctx(ctx); 244*86be9f0dSKonstantin Belousov DMAR_CTX_UNLOCK(ctx); 245*86be9f0dSKonstantin Belousov } 246*86be9f0dSKonstantin Belousov if (pgtbl_inited) { 247*86be9f0dSKonstantin Belousov if (ctx->pgtbl_obj != NULL) 248*86be9f0dSKonstantin Belousov DMAR_CTX_PGLOCK(ctx); 249*86be9f0dSKonstantin Belousov ctx_free_pgtbl(ctx); 250*86be9f0dSKonstantin Belousov } 251*86be9f0dSKonstantin Belousov mtx_destroy(&ctx->lock); 252*86be9f0dSKonstantin Belousov free(ctx, M_DMAR_CTX); 253*86be9f0dSKonstantin Belousov } 254*86be9f0dSKonstantin Belousov 255*86be9f0dSKonstantin Belousov struct dmar_ctx * 256*86be9f0dSKonstantin Belousov dmar_get_ctx(struct dmar_unit *dmar, device_t dev, bool id_mapped, bool rmrr_init) 257*86be9f0dSKonstantin Belousov { 258*86be9f0dSKonstantin Belousov struct dmar_ctx *ctx, *ctx1; 259*86be9f0dSKonstantin Belousov dmar_ctx_entry_t *ctxp; 260*86be9f0dSKonstantin Belousov struct sf_buf *sf; 261*86be9f0dSKonstantin Belousov int bus, slot, func, error, mgaw; 262*86be9f0dSKonstantin Belousov bool enable; 263*86be9f0dSKonstantin Belousov 264*86be9f0dSKonstantin Belousov bus = pci_get_bus(dev); 265*86be9f0dSKonstantin Belousov slot = pci_get_slot(dev); 266*86be9f0dSKonstantin Belousov func = pci_get_function(dev); 267*86be9f0dSKonstantin Belousov enable = false; 268*86be9f0dSKonstantin Belousov TD_PREP_PINNED_ASSERT; 269*86be9f0dSKonstantin Belousov DMAR_LOCK(dmar); 270*86be9f0dSKonstantin Belousov ctx = dmar_find_ctx_locked(dmar, bus, slot, func); 271*86be9f0dSKonstantin Belousov error = 0; 272*86be9f0dSKonstantin Belousov if (ctx == NULL) { 273*86be9f0dSKonstantin Belousov /* 274*86be9f0dSKonstantin Belousov * Perform the allocations which require sleep or have 275*86be9f0dSKonstantin Belousov * higher chance to succeed if the sleep is allowed. 276*86be9f0dSKonstantin Belousov */ 277*86be9f0dSKonstantin Belousov DMAR_UNLOCK(dmar); 278*86be9f0dSKonstantin Belousov dmar_ensure_ctx_page(dmar, bus); 279*86be9f0dSKonstantin Belousov ctx1 = dmar_get_ctx_alloc(dmar, bus, slot, func); 280*86be9f0dSKonstantin Belousov 281*86be9f0dSKonstantin Belousov if (id_mapped) { 282*86be9f0dSKonstantin Belousov /* 283*86be9f0dSKonstantin Belousov * For now, use the maximal usable physical 284*86be9f0dSKonstantin Belousov * address of the installed memory to 285*86be9f0dSKonstantin Belousov * calculate the mgaw. It is useful for the 286*86be9f0dSKonstantin Belousov * identity mapping, and less so for the 287*86be9f0dSKonstantin Belousov * virtualized bus address space. 288*86be9f0dSKonstantin Belousov */ 289*86be9f0dSKonstantin Belousov ctx1->end = ptoa(Maxmem); 290*86be9f0dSKonstantin Belousov mgaw = dmar_maxaddr2mgaw(dmar, ctx1->end, false); 291*86be9f0dSKonstantin Belousov error = ctx_set_agaw(ctx1, mgaw); 292*86be9f0dSKonstantin Belousov if (error != 0) { 293*86be9f0dSKonstantin Belousov dmar_ctx_dtr(ctx1, false, false); 294*86be9f0dSKonstantin Belousov TD_PINNED_ASSERT; 295*86be9f0dSKonstantin Belousov return (NULL); 296*86be9f0dSKonstantin Belousov } 297*86be9f0dSKonstantin Belousov } else { 298*86be9f0dSKonstantin Belousov ctx1->end = BUS_SPACE_MAXADDR; 299*86be9f0dSKonstantin Belousov mgaw = dmar_maxaddr2mgaw(dmar, ctx1->end, true); 300*86be9f0dSKonstantin Belousov error = ctx_set_agaw(ctx1, mgaw); 301*86be9f0dSKonstantin Belousov if (error != 0) { 302*86be9f0dSKonstantin Belousov dmar_ctx_dtr(ctx1, false, false); 303*86be9f0dSKonstantin Belousov TD_PINNED_ASSERT; 304*86be9f0dSKonstantin Belousov return (NULL); 305*86be9f0dSKonstantin Belousov } 306*86be9f0dSKonstantin Belousov /* Use all supported address space for remapping. */ 307*86be9f0dSKonstantin Belousov ctx1->end = 1ULL << (ctx1->agaw - 1); 308*86be9f0dSKonstantin Belousov } 309*86be9f0dSKonstantin Belousov 310*86be9f0dSKonstantin Belousov 311*86be9f0dSKonstantin Belousov dmar_gas_init_ctx(ctx1); 312*86be9f0dSKonstantin Belousov if (id_mapped) { 313*86be9f0dSKonstantin Belousov if ((dmar->hw_ecap & DMAR_ECAP_PT) == 0) { 314*86be9f0dSKonstantin Belousov ctx1->pgtbl_obj = ctx_get_idmap_pgtbl(ctx1, 315*86be9f0dSKonstantin Belousov ctx1->end); 316*86be9f0dSKonstantin Belousov } 317*86be9f0dSKonstantin Belousov ctx1->flags |= DMAR_CTX_IDMAP; 318*86be9f0dSKonstantin Belousov } else { 319*86be9f0dSKonstantin Belousov error = ctx_alloc_pgtbl(ctx1); 320*86be9f0dSKonstantin Belousov if (error != 0) { 321*86be9f0dSKonstantin Belousov dmar_ctx_dtr(ctx1, true, false); 322*86be9f0dSKonstantin Belousov TD_PINNED_ASSERT; 323*86be9f0dSKonstantin Belousov return (NULL); 324*86be9f0dSKonstantin Belousov } 325*86be9f0dSKonstantin Belousov /* Disable local apic region access */ 326*86be9f0dSKonstantin Belousov error = dmar_gas_reserve_region(ctx1, 0xfee00000, 327*86be9f0dSKonstantin Belousov 0xfeefffff + 1); 328*86be9f0dSKonstantin Belousov if (error != 0) { 329*86be9f0dSKonstantin Belousov dmar_ctx_dtr(ctx1, true, true); 330*86be9f0dSKonstantin Belousov TD_PINNED_ASSERT; 331*86be9f0dSKonstantin Belousov return (NULL); 332*86be9f0dSKonstantin Belousov } 333*86be9f0dSKonstantin Belousov error = ctx_init_rmrr(ctx1, dev); 334*86be9f0dSKonstantin Belousov if (error != 0) { 335*86be9f0dSKonstantin Belousov dmar_ctx_dtr(ctx1, true, true); 336*86be9f0dSKonstantin Belousov TD_PINNED_ASSERT; 337*86be9f0dSKonstantin Belousov return (NULL); 338*86be9f0dSKonstantin Belousov } 339*86be9f0dSKonstantin Belousov } 340*86be9f0dSKonstantin Belousov ctxp = dmar_map_ctx_entry(ctx1, &sf); 341*86be9f0dSKonstantin Belousov DMAR_LOCK(dmar); 342*86be9f0dSKonstantin Belousov 343*86be9f0dSKonstantin Belousov /* 344*86be9f0dSKonstantin Belousov * Recheck the contexts, other thread might have 345*86be9f0dSKonstantin Belousov * already allocated needed one. 346*86be9f0dSKonstantin Belousov */ 347*86be9f0dSKonstantin Belousov ctx = dmar_find_ctx_locked(dmar, bus, slot, func); 348*86be9f0dSKonstantin Belousov if (ctx == NULL) { 349*86be9f0dSKonstantin Belousov ctx = ctx1; 350*86be9f0dSKonstantin Belousov ctx->domain = alloc_unrl(dmar->domids); 351*86be9f0dSKonstantin Belousov if (ctx->domain == -1) { 352*86be9f0dSKonstantin Belousov DMAR_UNLOCK(dmar); 353*86be9f0dSKonstantin Belousov dmar_unmap_pgtbl(sf, true); 354*86be9f0dSKonstantin Belousov dmar_ctx_dtr(ctx, true, true); 355*86be9f0dSKonstantin Belousov TD_PINNED_ASSERT; 356*86be9f0dSKonstantin Belousov return (NULL); 357*86be9f0dSKonstantin Belousov } 358*86be9f0dSKonstantin Belousov ctx_tag_init(ctx); 359*86be9f0dSKonstantin Belousov 360*86be9f0dSKonstantin Belousov /* 361*86be9f0dSKonstantin Belousov * This is the first activated context for the 362*86be9f0dSKonstantin Belousov * DMAR unit. Enable the translation after 363*86be9f0dSKonstantin Belousov * everything is set up. 364*86be9f0dSKonstantin Belousov */ 365*86be9f0dSKonstantin Belousov if (LIST_EMPTY(&dmar->contexts)) 366*86be9f0dSKonstantin Belousov enable = true; 367*86be9f0dSKonstantin Belousov LIST_INSERT_HEAD(&dmar->contexts, ctx, link); 368*86be9f0dSKonstantin Belousov ctx_id_entry_init(ctx, ctxp); 369*86be9f0dSKonstantin Belousov device_printf(dev, 370*86be9f0dSKonstantin Belousov "dmar%d pci%d:%d:%d:%d domain %d mgaw %d agaw %d\n", 371*86be9f0dSKonstantin Belousov dmar->unit, dmar->segment, bus, slot, 372*86be9f0dSKonstantin Belousov func, ctx->domain, ctx->mgaw, ctx->agaw); 373*86be9f0dSKonstantin Belousov } else { 374*86be9f0dSKonstantin Belousov dmar_ctx_dtr(ctx1, true, true); 375*86be9f0dSKonstantin Belousov } 376*86be9f0dSKonstantin Belousov dmar_unmap_pgtbl(sf, DMAR_IS_COHERENT(dmar)); 377*86be9f0dSKonstantin Belousov } 378*86be9f0dSKonstantin Belousov ctx->refs++; 379*86be9f0dSKonstantin Belousov if ((ctx->flags & DMAR_CTX_RMRR) != 0) 380*86be9f0dSKonstantin Belousov ctx->refs++; /* XXXKIB */ 381*86be9f0dSKonstantin Belousov 382*86be9f0dSKonstantin Belousov /* 383*86be9f0dSKonstantin Belousov * If dmar declares Caching Mode as Set, follow 11.5 "Caching 384*86be9f0dSKonstantin Belousov * Mode Consideration" and do the (global) invalidation of the 385*86be9f0dSKonstantin Belousov * negative TLB entries. 386*86be9f0dSKonstantin Belousov */ 387*86be9f0dSKonstantin Belousov if ((dmar->hw_cap & DMAR_CAP_CM) != 0 || enable) { 388*86be9f0dSKonstantin Belousov error = dmar_inv_ctx_glob(dmar); 389*86be9f0dSKonstantin Belousov if (error == 0 && 390*86be9f0dSKonstantin Belousov (dmar->hw_ecap & DMAR_ECAP_DI) != 0) 391*86be9f0dSKonstantin Belousov error = dmar_inv_iotlb_glob(dmar); 392*86be9f0dSKonstantin Belousov if (error != 0) { 393*86be9f0dSKonstantin Belousov dmar_free_ctx_locked(dmar, ctx); 394*86be9f0dSKonstantin Belousov TD_PINNED_ASSERT; 395*86be9f0dSKonstantin Belousov return (NULL); 396*86be9f0dSKonstantin Belousov } 397*86be9f0dSKonstantin Belousov } 398*86be9f0dSKonstantin Belousov if (enable && !rmrr_init) { 399*86be9f0dSKonstantin Belousov error = dmar_enable_translation(dmar); 400*86be9f0dSKonstantin Belousov if (error != 0) { 401*86be9f0dSKonstantin Belousov dmar_free_ctx_locked(dmar, ctx); 402*86be9f0dSKonstantin Belousov TD_PINNED_ASSERT; 403*86be9f0dSKonstantin Belousov return (NULL); 404*86be9f0dSKonstantin Belousov } 405*86be9f0dSKonstantin Belousov } 406*86be9f0dSKonstantin Belousov DMAR_UNLOCK(dmar); 407*86be9f0dSKonstantin Belousov TD_PINNED_ASSERT; 408*86be9f0dSKonstantin Belousov return (ctx); 409*86be9f0dSKonstantin Belousov } 410*86be9f0dSKonstantin Belousov 411*86be9f0dSKonstantin Belousov void 412*86be9f0dSKonstantin Belousov dmar_free_ctx_locked(struct dmar_unit *dmar, struct dmar_ctx *ctx) 413*86be9f0dSKonstantin Belousov { 414*86be9f0dSKonstantin Belousov struct sf_buf *sf; 415*86be9f0dSKonstantin Belousov dmar_ctx_entry_t *ctxp; 416*86be9f0dSKonstantin Belousov 417*86be9f0dSKonstantin Belousov DMAR_ASSERT_LOCKED(dmar); 418*86be9f0dSKonstantin Belousov KASSERT(ctx->refs >= 1, 419*86be9f0dSKonstantin Belousov ("dmar %p ctx %p refs %u", dmar, ctx, ctx->refs)); 420*86be9f0dSKonstantin Belousov 421*86be9f0dSKonstantin Belousov /* 422*86be9f0dSKonstantin Belousov * If our reference is not last, only the dereference should 423*86be9f0dSKonstantin Belousov * be performed. 424*86be9f0dSKonstantin Belousov */ 425*86be9f0dSKonstantin Belousov if (ctx->refs > 1) { 426*86be9f0dSKonstantin Belousov ctx->refs--; 427*86be9f0dSKonstantin Belousov DMAR_UNLOCK(dmar); 428*86be9f0dSKonstantin Belousov return; 429*86be9f0dSKonstantin Belousov } 430*86be9f0dSKonstantin Belousov 431*86be9f0dSKonstantin Belousov KASSERT((ctx->flags & DMAR_CTX_RMRR) == 0, 432*86be9f0dSKonstantin Belousov ("lost ref on RMRR ctx %p", ctx)); 433*86be9f0dSKonstantin Belousov KASSERT((ctx->flags & DMAR_CTX_DISABLED) == 0, 434*86be9f0dSKonstantin Belousov ("lost ref on disabled ctx %p", ctx)); 435*86be9f0dSKonstantin Belousov 436*86be9f0dSKonstantin Belousov /* 437*86be9f0dSKonstantin Belousov * Otherwise, the context entry must be cleared before the 438*86be9f0dSKonstantin Belousov * page table is destroyed. The mapping of the context 439*86be9f0dSKonstantin Belousov * entries page could require sleep, unlock the dmar. 440*86be9f0dSKonstantin Belousov */ 441*86be9f0dSKonstantin Belousov DMAR_UNLOCK(dmar); 442*86be9f0dSKonstantin Belousov TD_PREP_PINNED_ASSERT; 443*86be9f0dSKonstantin Belousov ctxp = dmar_map_ctx_entry(ctx, &sf); 444*86be9f0dSKonstantin Belousov DMAR_LOCK(dmar); 445*86be9f0dSKonstantin Belousov KASSERT(ctx->refs >= 1, 446*86be9f0dSKonstantin Belousov ("dmar %p ctx %p refs %u", dmar, ctx, ctx->refs)); 447*86be9f0dSKonstantin Belousov 448*86be9f0dSKonstantin Belousov /* 449*86be9f0dSKonstantin Belousov * Other thread might have referenced the context, in which 450*86be9f0dSKonstantin Belousov * case again only the dereference should be performed. 451*86be9f0dSKonstantin Belousov */ 452*86be9f0dSKonstantin Belousov if (ctx->refs > 1) { 453*86be9f0dSKonstantin Belousov ctx->refs--; 454*86be9f0dSKonstantin Belousov DMAR_UNLOCK(dmar); 455*86be9f0dSKonstantin Belousov dmar_unmap_pgtbl(sf, DMAR_IS_COHERENT(dmar)); 456*86be9f0dSKonstantin Belousov TD_PINNED_ASSERT; 457*86be9f0dSKonstantin Belousov return; 458*86be9f0dSKonstantin Belousov } 459*86be9f0dSKonstantin Belousov 460*86be9f0dSKonstantin Belousov KASSERT((ctx->flags & DMAR_CTX_RMRR) == 0, 461*86be9f0dSKonstantin Belousov ("lost ref on RMRR ctx %p", ctx)); 462*86be9f0dSKonstantin Belousov KASSERT((ctx->flags & DMAR_CTX_DISABLED) == 0, 463*86be9f0dSKonstantin Belousov ("lost ref on disabled ctx %p", ctx)); 464*86be9f0dSKonstantin Belousov 465*86be9f0dSKonstantin Belousov /* 466*86be9f0dSKonstantin Belousov * Clear the context pointer and flush the caches. 467*86be9f0dSKonstantin Belousov * XXXKIB: cannot do this if any RMRR entries are still present. 468*86be9f0dSKonstantin Belousov */ 469*86be9f0dSKonstantin Belousov dmar_pte_clear(&ctxp->ctx1); 470*86be9f0dSKonstantin Belousov ctxp->ctx2 = 0; 471*86be9f0dSKonstantin Belousov dmar_inv_ctx_glob(dmar); 472*86be9f0dSKonstantin Belousov if ((dmar->hw_ecap & DMAR_ECAP_DI) != 0) 473*86be9f0dSKonstantin Belousov dmar_inv_iotlb_glob(dmar); 474*86be9f0dSKonstantin Belousov LIST_REMOVE(ctx, link); 475*86be9f0dSKonstantin Belousov DMAR_UNLOCK(dmar); 476*86be9f0dSKonstantin Belousov 477*86be9f0dSKonstantin Belousov /* 478*86be9f0dSKonstantin Belousov * The rest of the destruction is invisible for other users of 479*86be9f0dSKonstantin Belousov * the dmar unit. 480*86be9f0dSKonstantin Belousov */ 481*86be9f0dSKonstantin Belousov taskqueue_drain(dmar->delayed_taskqueue, &ctx->unload_task); 482*86be9f0dSKonstantin Belousov KASSERT(TAILQ_EMPTY(&ctx->unload_entries), 483*86be9f0dSKonstantin Belousov ("unfinished unloads %p", ctx)); 484*86be9f0dSKonstantin Belousov dmar_unmap_pgtbl(sf, DMAR_IS_COHERENT(dmar)); 485*86be9f0dSKonstantin Belousov free_unr(dmar->domids, ctx->domain); 486*86be9f0dSKonstantin Belousov dmar_ctx_dtr(ctx, true, true); 487*86be9f0dSKonstantin Belousov TD_PINNED_ASSERT; 488*86be9f0dSKonstantin Belousov } 489*86be9f0dSKonstantin Belousov 490*86be9f0dSKonstantin Belousov void 491*86be9f0dSKonstantin Belousov dmar_free_ctx(struct dmar_ctx *ctx) 492*86be9f0dSKonstantin Belousov { 493*86be9f0dSKonstantin Belousov struct dmar_unit *dmar; 494*86be9f0dSKonstantin Belousov 495*86be9f0dSKonstantin Belousov dmar = ctx->dmar; 496*86be9f0dSKonstantin Belousov DMAR_LOCK(dmar); 497*86be9f0dSKonstantin Belousov dmar_free_ctx_locked(dmar, ctx); 498*86be9f0dSKonstantin Belousov } 499*86be9f0dSKonstantin Belousov 500*86be9f0dSKonstantin Belousov struct dmar_ctx * 501*86be9f0dSKonstantin Belousov dmar_find_ctx_locked(struct dmar_unit *dmar, int bus, int slot, int func) 502*86be9f0dSKonstantin Belousov { 503*86be9f0dSKonstantin Belousov struct dmar_ctx *ctx; 504*86be9f0dSKonstantin Belousov 505*86be9f0dSKonstantin Belousov DMAR_ASSERT_LOCKED(dmar); 506*86be9f0dSKonstantin Belousov 507*86be9f0dSKonstantin Belousov LIST_FOREACH(ctx, &dmar->contexts, link) { 508*86be9f0dSKonstantin Belousov if (ctx->bus == bus && ctx->slot == slot && ctx->func == func) 509*86be9f0dSKonstantin Belousov return (ctx); 510*86be9f0dSKonstantin Belousov } 511*86be9f0dSKonstantin Belousov return (NULL); 512*86be9f0dSKonstantin Belousov } 513*86be9f0dSKonstantin Belousov 514*86be9f0dSKonstantin Belousov void 515*86be9f0dSKonstantin Belousov dmar_ctx_unload(struct dmar_ctx *ctx, struct dmar_map_entries_tailq *entries, 516*86be9f0dSKonstantin Belousov bool cansleep) 517*86be9f0dSKonstantin Belousov { 518*86be9f0dSKonstantin Belousov struct dmar_map_entry *entry; 519*86be9f0dSKonstantin Belousov int error; 520*86be9f0dSKonstantin Belousov 521*86be9f0dSKonstantin Belousov while ((entry = TAILQ_FIRST(entries)) != NULL) { 522*86be9f0dSKonstantin Belousov KASSERT((entry->flags & DMAR_MAP_ENTRY_MAP) != 0, 523*86be9f0dSKonstantin Belousov ("not mapped entry %p %p", ctx, entry)); 524*86be9f0dSKonstantin Belousov TAILQ_REMOVE(entries, entry, dmamap_link); 525*86be9f0dSKonstantin Belousov error = ctx_unmap_buf(ctx, entry->start, entry->end - 526*86be9f0dSKonstantin Belousov entry->start, cansleep ? DMAR_PGF_WAITOK : 0); 527*86be9f0dSKonstantin Belousov KASSERT(error == 0, ("unmap %p error %d", ctx, error)); 528*86be9f0dSKonstantin Belousov DMAR_CTX_LOCK(ctx); 529*86be9f0dSKonstantin Belousov dmar_gas_free_space(ctx, entry); 530*86be9f0dSKonstantin Belousov DMAR_CTX_UNLOCK(ctx); 531*86be9f0dSKonstantin Belousov dmar_gas_free_entry(ctx, entry); 532*86be9f0dSKonstantin Belousov } 533*86be9f0dSKonstantin Belousov } 534*86be9f0dSKonstantin Belousov 535*86be9f0dSKonstantin Belousov static void 536*86be9f0dSKonstantin Belousov dmar_ctx_unload_task(void *arg, int pending) 537*86be9f0dSKonstantin Belousov { 538*86be9f0dSKonstantin Belousov struct dmar_ctx *ctx; 539*86be9f0dSKonstantin Belousov struct dmar_map_entries_tailq entries; 540*86be9f0dSKonstantin Belousov 541*86be9f0dSKonstantin Belousov ctx = arg; 542*86be9f0dSKonstantin Belousov TAILQ_INIT(&entries); 543*86be9f0dSKonstantin Belousov 544*86be9f0dSKonstantin Belousov for (;;) { 545*86be9f0dSKonstantin Belousov DMAR_CTX_LOCK(ctx); 546*86be9f0dSKonstantin Belousov TAILQ_SWAP(&ctx->unload_entries, &entries, dmar_map_entry, 547*86be9f0dSKonstantin Belousov dmamap_link); 548*86be9f0dSKonstantin Belousov DMAR_CTX_UNLOCK(ctx); 549*86be9f0dSKonstantin Belousov if (TAILQ_EMPTY(&entries)) 550*86be9f0dSKonstantin Belousov break; 551*86be9f0dSKonstantin Belousov dmar_ctx_unload(ctx, &entries, true); 552*86be9f0dSKonstantin Belousov } 553*86be9f0dSKonstantin Belousov } 554