140d951bcSKonstantin Belousov /*- 240d951bcSKonstantin Belousov * SPDX-License-Identifier: BSD-2-Clause 340d951bcSKonstantin Belousov * 465b133e5SKonstantin Belousov * Copyright (c) 2013, 2014, 2024 The FreeBSD Foundation 540d951bcSKonstantin Belousov * 640d951bcSKonstantin Belousov * This software was developed by Konstantin Belousov <kib@FreeBSD.org> 740d951bcSKonstantin Belousov * under sponsorship from the FreeBSD Foundation. 840d951bcSKonstantin Belousov * 940d951bcSKonstantin Belousov * Redistribution and use in source and binary forms, with or without 1040d951bcSKonstantin Belousov * modification, are permitted provided that the following conditions 1140d951bcSKonstantin Belousov * are met: 1240d951bcSKonstantin Belousov * 1. Redistributions of source code must retain the above copyright 1340d951bcSKonstantin Belousov * notice, this list of conditions and the following disclaimer. 1440d951bcSKonstantin Belousov * 2. Redistributions in binary form must reproduce the above copyright 1540d951bcSKonstantin Belousov * notice, this list of conditions and the following disclaimer in the 1640d951bcSKonstantin Belousov * documentation and/or other materials provided with the distribution. 1740d951bcSKonstantin Belousov * 1840d951bcSKonstantin Belousov * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1940d951bcSKonstantin Belousov * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2040d951bcSKonstantin Belousov * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2140d951bcSKonstantin Belousov * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2240d951bcSKonstantin Belousov * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2340d951bcSKonstantin Belousov * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2440d951bcSKonstantin Belousov * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2540d951bcSKonstantin Belousov * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2640d951bcSKonstantin Belousov * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2740d951bcSKonstantin Belousov * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2840d951bcSKonstantin Belousov * SUCH DAMAGE. 2940d951bcSKonstantin Belousov */ 3040d951bcSKonstantin Belousov 315967352aSKonstantin Belousov #include "opt_acpi.h" 325967352aSKonstantin Belousov #if defined(__amd64__) 335967352aSKonstantin Belousov #define DEV_APIC 345967352aSKonstantin Belousov #else 355967352aSKonstantin Belousov #include "opt_apic.h" 365967352aSKonstantin Belousov #endif 37e9d948cfSKonstantin Belousov #include "opt_ddb.h" 385967352aSKonstantin Belousov 3940d951bcSKonstantin Belousov #include <sys/systm.h> 405967352aSKonstantin Belousov #include <sys/bus.h> 41ad794e6dSKonstantin Belousov #include <sys/kernel.h> 4240d951bcSKonstantin Belousov #include <sys/lock.h> 43ad794e6dSKonstantin Belousov #include <sys/malloc.h> 4440d951bcSKonstantin Belousov #include <sys/memdesc.h> 4540d951bcSKonstantin Belousov #include <sys/mutex.h> 4640d951bcSKonstantin Belousov #include <sys/sf_buf.h> 4740d951bcSKonstantin Belousov #include <sys/sysctl.h> 4840d951bcSKonstantin Belousov #include <sys/proc.h> 4940d951bcSKonstantin Belousov #include <sys/sched.h> 505967352aSKonstantin Belousov #include <sys/rman.h> 5140d951bcSKonstantin Belousov #include <sys/rwlock.h> 5240d951bcSKonstantin Belousov #include <sys/taskqueue.h> 5340d951bcSKonstantin Belousov #include <sys/tree.h> 5440d951bcSKonstantin Belousov #include <vm/vm.h> 55ad794e6dSKonstantin Belousov #include <vm/vm_extern.h> 56ad794e6dSKonstantin Belousov #include <vm/vm_kern.h> 57ad794e6dSKonstantin Belousov #include <vm/vm_map.h> 5840d951bcSKonstantin Belousov #include <vm/vm_object.h> 59ad794e6dSKonstantin Belousov #include <vm/vm_page.h> 6040d951bcSKonstantin Belousov #include <dev/pci/pcireg.h> 615967352aSKonstantin Belousov #include <dev/pci/pcivar.h> 6240d951bcSKonstantin Belousov #include <machine/atomic.h> 6340d951bcSKonstantin Belousov #include <machine/bus.h> 6440d951bcSKonstantin Belousov #include <machine/cpu.h> 6540d951bcSKonstantin Belousov #include <x86/include/busdma_impl.h> 6640d951bcSKonstantin Belousov #include <dev/iommu/busdma_iommu.h> 6740d951bcSKonstantin Belousov #include <dev/iommu/iommu.h> 6840d951bcSKonstantin Belousov #include <x86/iommu/x86_iommu.h> 6965b133e5SKonstantin Belousov #include <x86/iommu/iommu_intrmap.h> 705967352aSKonstantin Belousov #ifdef DEV_APIC 715967352aSKonstantin Belousov #include "pcib_if.h" 725967352aSKonstantin Belousov #include <machine/intr_machdep.h> 735967352aSKonstantin Belousov #include <x86/apicreg.h> 745967352aSKonstantin Belousov #include <x86/apicvar.h> 755967352aSKonstantin Belousov #endif 7640d951bcSKonstantin Belousov 7740d951bcSKonstantin Belousov vm_page_t 7840d951bcSKonstantin Belousov iommu_pgalloc(vm_object_t obj, vm_pindex_t idx, int flags) 7940d951bcSKonstantin Belousov { 8040d951bcSKonstantin Belousov vm_page_t m; 8140d951bcSKonstantin Belousov int zeroed, aflags; 8240d951bcSKonstantin Belousov 8340d951bcSKonstantin Belousov zeroed = (flags & IOMMU_PGF_ZERO) != 0 ? VM_ALLOC_ZERO : 0; 8440d951bcSKonstantin Belousov aflags = zeroed | VM_ALLOC_NOBUSY | VM_ALLOC_SYSTEM | VM_ALLOC_NODUMP | 8540d951bcSKonstantin Belousov ((flags & IOMMU_PGF_WAITOK) != 0 ? VM_ALLOC_WAITFAIL : 8640d951bcSKonstantin Belousov VM_ALLOC_NOWAIT); 8740d951bcSKonstantin Belousov for (;;) { 8840d951bcSKonstantin Belousov if ((flags & IOMMU_PGF_OBJL) == 0) 8940d951bcSKonstantin Belousov VM_OBJECT_WLOCK(obj); 9040d951bcSKonstantin Belousov m = vm_page_lookup(obj, idx); 9140d951bcSKonstantin Belousov if ((flags & IOMMU_PGF_NOALLOC) != 0 || m != NULL) { 9240d951bcSKonstantin Belousov if ((flags & IOMMU_PGF_OBJL) == 0) 9340d951bcSKonstantin Belousov VM_OBJECT_WUNLOCK(obj); 9440d951bcSKonstantin Belousov break; 9540d951bcSKonstantin Belousov } 9640d951bcSKonstantin Belousov m = vm_page_alloc_contig(obj, idx, aflags, 1, 0, 9740d951bcSKonstantin Belousov iommu_high, PAGE_SIZE, 0, VM_MEMATTR_DEFAULT); 9840d951bcSKonstantin Belousov if ((flags & IOMMU_PGF_OBJL) == 0) 9940d951bcSKonstantin Belousov VM_OBJECT_WUNLOCK(obj); 10040d951bcSKonstantin Belousov if (m != NULL) { 10140d951bcSKonstantin Belousov if (zeroed && (m->flags & PG_ZERO) == 0) 10240d951bcSKonstantin Belousov pmap_zero_page(m); 10340d951bcSKonstantin Belousov atomic_add_int(&iommu_tbl_pagecnt, 1); 10440d951bcSKonstantin Belousov break; 10540d951bcSKonstantin Belousov } 10640d951bcSKonstantin Belousov if ((flags & IOMMU_PGF_WAITOK) == 0) 10740d951bcSKonstantin Belousov break; 10840d951bcSKonstantin Belousov } 10940d951bcSKonstantin Belousov return (m); 11040d951bcSKonstantin Belousov } 11140d951bcSKonstantin Belousov 11240d951bcSKonstantin Belousov void 113f713ed66SKonstantin Belousov iommu_pgfree(vm_object_t obj, vm_pindex_t idx, int flags, 114f713ed66SKonstantin Belousov struct iommu_map_entry *entry) 11540d951bcSKonstantin Belousov { 11640d951bcSKonstantin Belousov vm_page_t m; 11740d951bcSKonstantin Belousov 11840d951bcSKonstantin Belousov if ((flags & IOMMU_PGF_OBJL) == 0) 11940d951bcSKonstantin Belousov VM_OBJECT_WLOCK(obj); 12040d951bcSKonstantin Belousov m = vm_page_grab(obj, idx, VM_ALLOC_NOCREAT); 12140d951bcSKonstantin Belousov if (m != NULL) { 122f713ed66SKonstantin Belousov if (entry == NULL) { 12340d951bcSKonstantin Belousov vm_page_free(m); 12440d951bcSKonstantin Belousov atomic_subtract_int(&iommu_tbl_pagecnt, 1); 125f713ed66SKonstantin Belousov } else { 126f713ed66SKonstantin Belousov vm_page_remove_xbusy(m); /* keep page busy */ 127f713ed66SKonstantin Belousov SLIST_INSERT_HEAD(&entry->pgtbl_free, m, plinks.s.ss); 128f713ed66SKonstantin Belousov } 12940d951bcSKonstantin Belousov } 13040d951bcSKonstantin Belousov if ((flags & IOMMU_PGF_OBJL) == 0) 13140d951bcSKonstantin Belousov VM_OBJECT_WUNLOCK(obj); 13240d951bcSKonstantin Belousov } 13340d951bcSKonstantin Belousov 13440d951bcSKonstantin Belousov void * 13540d951bcSKonstantin Belousov iommu_map_pgtbl(vm_object_t obj, vm_pindex_t idx, int flags, 13640d951bcSKonstantin Belousov struct sf_buf **sf) 13740d951bcSKonstantin Belousov { 13840d951bcSKonstantin Belousov vm_page_t m; 13940d951bcSKonstantin Belousov bool allocated; 14040d951bcSKonstantin Belousov 14140d951bcSKonstantin Belousov if ((flags & IOMMU_PGF_OBJL) == 0) 14240d951bcSKonstantin Belousov VM_OBJECT_WLOCK(obj); 14340d951bcSKonstantin Belousov m = vm_page_lookup(obj, idx); 14440d951bcSKonstantin Belousov if (m == NULL && (flags & IOMMU_PGF_ALLOC) != 0) { 14540d951bcSKonstantin Belousov m = iommu_pgalloc(obj, idx, flags | IOMMU_PGF_OBJL); 14640d951bcSKonstantin Belousov allocated = true; 14740d951bcSKonstantin Belousov } else 14840d951bcSKonstantin Belousov allocated = false; 14940d951bcSKonstantin Belousov if (m == NULL) { 15040d951bcSKonstantin Belousov if ((flags & IOMMU_PGF_OBJL) == 0) 15140d951bcSKonstantin Belousov VM_OBJECT_WUNLOCK(obj); 15240d951bcSKonstantin Belousov return (NULL); 15340d951bcSKonstantin Belousov } 15440d951bcSKonstantin Belousov /* Sleepable allocations cannot fail. */ 15540d951bcSKonstantin Belousov if ((flags & IOMMU_PGF_WAITOK) != 0) 15640d951bcSKonstantin Belousov VM_OBJECT_WUNLOCK(obj); 15740d951bcSKonstantin Belousov sched_pin(); 15840d951bcSKonstantin Belousov *sf = sf_buf_alloc(m, SFB_CPUPRIVATE | ((flags & IOMMU_PGF_WAITOK) 15940d951bcSKonstantin Belousov == 0 ? SFB_NOWAIT : 0)); 16040d951bcSKonstantin Belousov if (*sf == NULL) { 16140d951bcSKonstantin Belousov sched_unpin(); 16240d951bcSKonstantin Belousov if (allocated) { 16340d951bcSKonstantin Belousov VM_OBJECT_ASSERT_WLOCKED(obj); 164f713ed66SKonstantin Belousov iommu_pgfree(obj, m->pindex, flags | IOMMU_PGF_OBJL, 165f713ed66SKonstantin Belousov NULL); 16640d951bcSKonstantin Belousov } 16740d951bcSKonstantin Belousov if ((flags & IOMMU_PGF_OBJL) == 0) 16840d951bcSKonstantin Belousov VM_OBJECT_WUNLOCK(obj); 16940d951bcSKonstantin Belousov return (NULL); 17040d951bcSKonstantin Belousov } 17140d951bcSKonstantin Belousov if ((flags & (IOMMU_PGF_WAITOK | IOMMU_PGF_OBJL)) == 17240d951bcSKonstantin Belousov (IOMMU_PGF_WAITOK | IOMMU_PGF_OBJL)) 17340d951bcSKonstantin Belousov VM_OBJECT_WLOCK(obj); 17440d951bcSKonstantin Belousov else if ((flags & (IOMMU_PGF_WAITOK | IOMMU_PGF_OBJL)) == 0) 17540d951bcSKonstantin Belousov VM_OBJECT_WUNLOCK(obj); 17640d951bcSKonstantin Belousov return ((void *)sf_buf_kva(*sf)); 17740d951bcSKonstantin Belousov } 17840d951bcSKonstantin Belousov 17940d951bcSKonstantin Belousov void 18040d951bcSKonstantin Belousov iommu_unmap_pgtbl(struct sf_buf *sf) 18140d951bcSKonstantin Belousov { 18240d951bcSKonstantin Belousov 18340d951bcSKonstantin Belousov sf_buf_free(sf); 18440d951bcSKonstantin Belousov sched_unpin(); 18540d951bcSKonstantin Belousov } 18640d951bcSKonstantin Belousov 18740d951bcSKonstantin Belousov iommu_haddr_t iommu_high; 18840d951bcSKonstantin Belousov int iommu_tbl_pagecnt; 18940d951bcSKonstantin Belousov 19040d951bcSKonstantin Belousov SYSCTL_NODE(_hw_iommu, OID_AUTO, dmar, CTLFLAG_RD | CTLFLAG_MPSAFE, 19140d951bcSKonstantin Belousov NULL, ""); 1920386b245SKonstantin Belousov SYSCTL_INT(_hw_iommu, OID_AUTO, tbl_pagecnt, CTLFLAG_RD, 19340d951bcSKonstantin Belousov &iommu_tbl_pagecnt, 0, 1940386b245SKonstantin Belousov "Count of pages used for IOMMU pagetables"); 1950386b245SKonstantin Belousov 1960386b245SKonstantin Belousov int iommu_qi_batch_coalesce = 100; 1970386b245SKonstantin Belousov SYSCTL_INT(_hw_iommu, OID_AUTO, batch_coalesce, CTLFLAG_RWTUN, 1980386b245SKonstantin Belousov &iommu_qi_batch_coalesce, 0, 1990386b245SKonstantin Belousov "Number of qi batches between interrupt"); 20065b133e5SKonstantin Belousov 20199e3d96fSKonstantin Belousov static struct iommu_unit * 20299e3d96fSKonstantin Belousov x86_no_iommu_find(device_t dev, bool verbose) 20399e3d96fSKonstantin Belousov { 20499e3d96fSKonstantin Belousov return (NULL); 20599e3d96fSKonstantin Belousov } 20699e3d96fSKonstantin Belousov 20799e3d96fSKonstantin Belousov static int 20899e3d96fSKonstantin Belousov x86_no_iommu_alloc_msi_intr(device_t src, u_int *cookies, u_int count) 20999e3d96fSKonstantin Belousov { 21099e3d96fSKonstantin Belousov return (EOPNOTSUPP); 21199e3d96fSKonstantin Belousov } 21299e3d96fSKonstantin Belousov 21399e3d96fSKonstantin Belousov static int 21499e3d96fSKonstantin Belousov x86_no_iommu_map_msi_intr(device_t src, u_int cpu, u_int vector, 21599e3d96fSKonstantin Belousov u_int cookie, uint64_t *addr, uint32_t *data) 21699e3d96fSKonstantin Belousov { 21799e3d96fSKonstantin Belousov return (EOPNOTSUPP); 21899e3d96fSKonstantin Belousov } 21999e3d96fSKonstantin Belousov 22099e3d96fSKonstantin Belousov static int 22199e3d96fSKonstantin Belousov x86_no_iommu_unmap_msi_intr(device_t src, u_int cookie) 22299e3d96fSKonstantin Belousov { 22399e3d96fSKonstantin Belousov return (0); 22499e3d96fSKonstantin Belousov } 22599e3d96fSKonstantin Belousov 22699e3d96fSKonstantin Belousov static int 22799e3d96fSKonstantin Belousov x86_no_iommu_map_ioapic_intr(u_int ioapic_id, u_int cpu, u_int vector, 22899e3d96fSKonstantin Belousov bool edge, bool activehi, int irq, u_int *cookie, uint32_t *hi, 22999e3d96fSKonstantin Belousov uint32_t *lo) 23099e3d96fSKonstantin Belousov { 23199e3d96fSKonstantin Belousov return (EOPNOTSUPP); 23299e3d96fSKonstantin Belousov } 23399e3d96fSKonstantin Belousov 23499e3d96fSKonstantin Belousov static int 23599e3d96fSKonstantin Belousov x86_no_iommu_unmap_ioapic_intr(u_int ioapic_id, u_int *cookie) 23699e3d96fSKonstantin Belousov { 23799e3d96fSKonstantin Belousov return (0); 23899e3d96fSKonstantin Belousov } 23999e3d96fSKonstantin Belousov 24099e3d96fSKonstantin Belousov static struct x86_iommu x86_no_iommu = { 24199e3d96fSKonstantin Belousov .find = x86_no_iommu_find, 24299e3d96fSKonstantin Belousov .alloc_msi_intr = x86_no_iommu_alloc_msi_intr, 24399e3d96fSKonstantin Belousov .map_msi_intr = x86_no_iommu_map_msi_intr, 24499e3d96fSKonstantin Belousov .unmap_msi_intr = x86_no_iommu_unmap_msi_intr, 24599e3d96fSKonstantin Belousov .map_ioapic_intr = x86_no_iommu_map_ioapic_intr, 24699e3d96fSKonstantin Belousov .unmap_ioapic_intr = x86_no_iommu_unmap_ioapic_intr, 24799e3d96fSKonstantin Belousov }; 24899e3d96fSKonstantin Belousov 24999e3d96fSKonstantin Belousov static struct x86_iommu *x86_iommu = &x86_no_iommu; 25065b133e5SKonstantin Belousov 25165b133e5SKonstantin Belousov void 25265b133e5SKonstantin Belousov set_x86_iommu(struct x86_iommu *x) 25365b133e5SKonstantin Belousov { 25499e3d96fSKonstantin Belousov MPASS(x86_iommu == &x86_no_iommu); 25565b133e5SKonstantin Belousov x86_iommu = x; 25665b133e5SKonstantin Belousov } 25765b133e5SKonstantin Belousov 25865b133e5SKonstantin Belousov struct x86_iommu * 25965b133e5SKonstantin Belousov get_x86_iommu(void) 26065b133e5SKonstantin Belousov { 26165b133e5SKonstantin Belousov return (x86_iommu); 26265b133e5SKonstantin Belousov } 26365b133e5SKonstantin Belousov 26465b133e5SKonstantin Belousov void 26565b133e5SKonstantin Belousov iommu_domain_unload_entry(struct iommu_map_entry *entry, bool free, 26665b133e5SKonstantin Belousov bool cansleep) 26765b133e5SKonstantin Belousov { 26865b133e5SKonstantin Belousov x86_iommu->domain_unload_entry(entry, free, cansleep); 26965b133e5SKonstantin Belousov } 27065b133e5SKonstantin Belousov 27165b133e5SKonstantin Belousov void 27265b133e5SKonstantin Belousov iommu_domain_unload(struct iommu_domain *iodom, 27365b133e5SKonstantin Belousov struct iommu_map_entries_tailq *entries, bool cansleep) 27465b133e5SKonstantin Belousov { 27565b133e5SKonstantin Belousov x86_iommu->domain_unload(iodom, entries, cansleep); 27665b133e5SKonstantin Belousov } 27765b133e5SKonstantin Belousov 27865b133e5SKonstantin Belousov struct iommu_ctx * 27965b133e5SKonstantin Belousov iommu_get_ctx(struct iommu_unit *iommu, device_t dev, uint16_t rid, 28065b133e5SKonstantin Belousov bool id_mapped, bool rmrr_init) 28165b133e5SKonstantin Belousov { 28265b133e5SKonstantin Belousov return (x86_iommu->get_ctx(iommu, dev, rid, id_mapped, rmrr_init)); 28365b133e5SKonstantin Belousov } 28465b133e5SKonstantin Belousov 28565b133e5SKonstantin Belousov void 28665b133e5SKonstantin Belousov iommu_free_ctx_locked(struct iommu_unit *iommu, struct iommu_ctx *context) 28765b133e5SKonstantin Belousov { 28865b133e5SKonstantin Belousov x86_iommu->free_ctx_locked(iommu, context); 28965b133e5SKonstantin Belousov } 29065b133e5SKonstantin Belousov 29165b133e5SKonstantin Belousov struct iommu_unit * 29265b133e5SKonstantin Belousov iommu_find(device_t dev, bool verbose) 29365b133e5SKonstantin Belousov { 29465b133e5SKonstantin Belousov return (x86_iommu->find(dev, verbose)); 29565b133e5SKonstantin Belousov } 29665b133e5SKonstantin Belousov 29765b133e5SKonstantin Belousov int 29865b133e5SKonstantin Belousov iommu_alloc_msi_intr(device_t src, u_int *cookies, u_int count) 29965b133e5SKonstantin Belousov { 30065b133e5SKonstantin Belousov return (x86_iommu->alloc_msi_intr(src, cookies, count)); 30165b133e5SKonstantin Belousov } 30265b133e5SKonstantin Belousov 30365b133e5SKonstantin Belousov int 30465b133e5SKonstantin Belousov iommu_map_msi_intr(device_t src, u_int cpu, u_int vector, u_int cookie, 30565b133e5SKonstantin Belousov uint64_t *addr, uint32_t *data) 30665b133e5SKonstantin Belousov { 30765b133e5SKonstantin Belousov return (x86_iommu->map_msi_intr(src, cpu, vector, cookie, 30865b133e5SKonstantin Belousov addr, data)); 30965b133e5SKonstantin Belousov } 31065b133e5SKonstantin Belousov 31165b133e5SKonstantin Belousov int 31265b133e5SKonstantin Belousov iommu_unmap_msi_intr(device_t src, u_int cookie) 31365b133e5SKonstantin Belousov { 31465b133e5SKonstantin Belousov return (x86_iommu->unmap_msi_intr(src, cookie)); 31565b133e5SKonstantin Belousov } 31665b133e5SKonstantin Belousov 31765b133e5SKonstantin Belousov int 31865b133e5SKonstantin Belousov iommu_map_ioapic_intr(u_int ioapic_id, u_int cpu, u_int vector, bool edge, 31965b133e5SKonstantin Belousov bool activehi, int irq, u_int *cookie, uint32_t *hi, uint32_t *lo) 32065b133e5SKonstantin Belousov { 32165b133e5SKonstantin Belousov return (x86_iommu->map_ioapic_intr(ioapic_id, cpu, vector, edge, 32265b133e5SKonstantin Belousov activehi, irq, cookie, hi, lo)); 32365b133e5SKonstantin Belousov } 32465b133e5SKonstantin Belousov 32565b133e5SKonstantin Belousov int 32665b133e5SKonstantin Belousov iommu_unmap_ioapic_intr(u_int ioapic_id, u_int *cookie) 32765b133e5SKonstantin Belousov { 32865b133e5SKonstantin Belousov return (x86_iommu->unmap_ioapic_intr(ioapic_id, cookie)); 32965b133e5SKonstantin Belousov } 330ad794e6dSKonstantin Belousov 331ba33e74cSKonstantin Belousov void 332ba33e74cSKonstantin Belousov iommu_unit_pre_instantiate_ctx(struct iommu_unit *unit) 333ba33e74cSKonstantin Belousov { 334ba33e74cSKonstantin Belousov x86_iommu->unit_pre_instantiate_ctx(unit); 335ba33e74cSKonstantin Belousov } 336ba33e74cSKonstantin Belousov 337ad794e6dSKonstantin Belousov #define IOMMU2X86C(iommu) (x86_iommu->get_x86_common(iommu)) 338ad794e6dSKonstantin Belousov 339ad794e6dSKonstantin Belousov static bool 340ad794e6dSKonstantin Belousov iommu_qi_seq_processed(struct iommu_unit *unit, 341ad794e6dSKonstantin Belousov const struct iommu_qi_genseq *pseq) 342ad794e6dSKonstantin Belousov { 343ad794e6dSKonstantin Belousov struct x86_unit_common *x86c; 344ad794e6dSKonstantin Belousov u_int gen; 345ad794e6dSKonstantin Belousov 346ad794e6dSKonstantin Belousov x86c = IOMMU2X86C(unit); 347ad794e6dSKonstantin Belousov gen = x86c->inv_waitd_gen; 348eb832642SKonstantin Belousov return (pseq->gen < gen || (pseq->gen == gen && pseq->seq <= 349eb832642SKonstantin Belousov atomic_load_64(&x86c->inv_waitd_seq_hw))); 350ad794e6dSKonstantin Belousov } 351ad794e6dSKonstantin Belousov 352ad794e6dSKonstantin Belousov void 353ad794e6dSKonstantin Belousov iommu_qi_emit_wait_seq(struct iommu_unit *unit, struct iommu_qi_genseq *pseq, 354ad794e6dSKonstantin Belousov bool emit_wait) 355ad794e6dSKonstantin Belousov { 356ad794e6dSKonstantin Belousov struct x86_unit_common *x86c; 357ad794e6dSKonstantin Belousov struct iommu_qi_genseq gsec; 358ad794e6dSKonstantin Belousov uint32_t seq; 359ad794e6dSKonstantin Belousov 360ad794e6dSKonstantin Belousov KASSERT(pseq != NULL, ("wait descriptor with no place for seq")); 361ad794e6dSKonstantin Belousov IOMMU_ASSERT_LOCKED(unit); 362ad794e6dSKonstantin Belousov x86c = IOMMU2X86C(unit); 363ad794e6dSKonstantin Belousov 364ad794e6dSKonstantin Belousov if (x86c->inv_waitd_seq == 0xffffffff) { 365ad794e6dSKonstantin Belousov gsec.gen = x86c->inv_waitd_gen; 366ad794e6dSKonstantin Belousov gsec.seq = x86c->inv_waitd_seq; 367ad794e6dSKonstantin Belousov x86_iommu->qi_ensure(unit, 1); 368ad794e6dSKonstantin Belousov x86_iommu->qi_emit_wait_descr(unit, gsec.seq, false, 369ad794e6dSKonstantin Belousov true, false); 370ad794e6dSKonstantin Belousov x86_iommu->qi_advance_tail(unit); 371ad794e6dSKonstantin Belousov while (!iommu_qi_seq_processed(unit, &gsec)) 372ad794e6dSKonstantin Belousov cpu_spinwait(); 373ad794e6dSKonstantin Belousov x86c->inv_waitd_gen++; 374ad794e6dSKonstantin Belousov x86c->inv_waitd_seq = 1; 375ad794e6dSKonstantin Belousov } 376ad794e6dSKonstantin Belousov seq = x86c->inv_waitd_seq++; 377ad794e6dSKonstantin Belousov pseq->gen = x86c->inv_waitd_gen; 378ad794e6dSKonstantin Belousov pseq->seq = seq; 379ad794e6dSKonstantin Belousov if (emit_wait) { 380ad794e6dSKonstantin Belousov x86_iommu->qi_ensure(unit, 1); 381ad794e6dSKonstantin Belousov x86_iommu->qi_emit_wait_descr(unit, seq, true, true, false); 382ad794e6dSKonstantin Belousov } 383ad794e6dSKonstantin Belousov } 384ad794e6dSKonstantin Belousov 385ad794e6dSKonstantin Belousov /* 386ad794e6dSKonstantin Belousov * To avoid missed wakeups, callers must increment the unit's waiters count 387ad794e6dSKonstantin Belousov * before advancing the tail past the wait descriptor. 388ad794e6dSKonstantin Belousov */ 389ad794e6dSKonstantin Belousov void 390ad794e6dSKonstantin Belousov iommu_qi_wait_for_seq(struct iommu_unit *unit, const struct iommu_qi_genseq * 391ad794e6dSKonstantin Belousov gseq, bool nowait) 392ad794e6dSKonstantin Belousov { 393ad794e6dSKonstantin Belousov struct x86_unit_common *x86c; 394ad794e6dSKonstantin Belousov 395ad794e6dSKonstantin Belousov IOMMU_ASSERT_LOCKED(unit); 396ad794e6dSKonstantin Belousov x86c = IOMMU2X86C(unit); 397ad794e6dSKonstantin Belousov 398ad794e6dSKonstantin Belousov KASSERT(x86c->inv_seq_waiters > 0, ("%s: no waiters", __func__)); 399ad794e6dSKonstantin Belousov while (!iommu_qi_seq_processed(unit, gseq)) { 400ad794e6dSKonstantin Belousov if (cold || nowait) { 401ad794e6dSKonstantin Belousov cpu_spinwait(); 402ad794e6dSKonstantin Belousov } else { 403ad794e6dSKonstantin Belousov msleep(&x86c->inv_seq_waiters, &unit->lock, 0, 404ad794e6dSKonstantin Belousov "dmarse", hz); 405ad794e6dSKonstantin Belousov } 406ad794e6dSKonstantin Belousov } 407ad794e6dSKonstantin Belousov x86c->inv_seq_waiters--; 408ad794e6dSKonstantin Belousov } 409ad794e6dSKonstantin Belousov 410ad794e6dSKonstantin Belousov /* 411ad794e6dSKonstantin Belousov * The caller must not be using the entry's dmamap_link field. 412ad794e6dSKonstantin Belousov */ 413ad794e6dSKonstantin Belousov void 414ad794e6dSKonstantin Belousov iommu_qi_invalidate_locked(struct iommu_domain *domain, 415ad794e6dSKonstantin Belousov struct iommu_map_entry *entry, bool emit_wait) 416ad794e6dSKonstantin Belousov { 417ad794e6dSKonstantin Belousov struct iommu_unit *unit; 418ad794e6dSKonstantin Belousov struct x86_unit_common *x86c; 419ad794e6dSKonstantin Belousov 420ad794e6dSKonstantin Belousov unit = domain->iommu; 421ad794e6dSKonstantin Belousov x86c = IOMMU2X86C(unit); 422ad794e6dSKonstantin Belousov IOMMU_ASSERT_LOCKED(unit); 423ad794e6dSKonstantin Belousov 424ad794e6dSKonstantin Belousov x86_iommu->qi_invalidate_emit(domain, entry->start, entry->end - 425ad794e6dSKonstantin Belousov entry->start, &entry->gseq, emit_wait); 426ad794e6dSKonstantin Belousov 427ad794e6dSKonstantin Belousov /* 428ad794e6dSKonstantin Belousov * To avoid a data race in dmar_qi_task(), the entry's gseq must be 429ad794e6dSKonstantin Belousov * initialized before the entry is added to the TLB flush list, and the 430ad794e6dSKonstantin Belousov * entry must be added to that list before the tail is advanced. More 431ad794e6dSKonstantin Belousov * precisely, the tail must not be advanced past the wait descriptor 432ad794e6dSKonstantin Belousov * that will generate the interrupt that schedules dmar_qi_task() for 433ad794e6dSKonstantin Belousov * execution before the entry is added to the list. While an earlier 434ad794e6dSKonstantin Belousov * call to dmar_qi_ensure() might have advanced the tail, it will not 435ad794e6dSKonstantin Belousov * advance it past the wait descriptor. 436ad794e6dSKonstantin Belousov * 437ad794e6dSKonstantin Belousov * See the definition of struct dmar_unit for more information on 438ad794e6dSKonstantin Belousov * synchronization. 439ad794e6dSKonstantin Belousov */ 440ad794e6dSKonstantin Belousov entry->tlb_flush_next = NULL; 441ad794e6dSKonstantin Belousov atomic_store_rel_ptr((uintptr_t *)&x86c->tlb_flush_tail-> 442ad794e6dSKonstantin Belousov tlb_flush_next, (uintptr_t)entry); 443ad794e6dSKonstantin Belousov x86c->tlb_flush_tail = entry; 444ad794e6dSKonstantin Belousov 445ad794e6dSKonstantin Belousov x86_iommu->qi_advance_tail(unit); 446ad794e6dSKonstantin Belousov } 447ad794e6dSKonstantin Belousov 448ad794e6dSKonstantin Belousov void 449ad794e6dSKonstantin Belousov iommu_qi_invalidate_sync(struct iommu_domain *domain, iommu_gaddr_t base, 450ad794e6dSKonstantin Belousov iommu_gaddr_t size, bool cansleep) 451ad794e6dSKonstantin Belousov { 452ad794e6dSKonstantin Belousov struct iommu_unit *unit; 453ad794e6dSKonstantin Belousov struct iommu_qi_genseq gseq; 454ad794e6dSKonstantin Belousov 455ad794e6dSKonstantin Belousov unit = domain->iommu; 456ad794e6dSKonstantin Belousov IOMMU_LOCK(unit); 457ad794e6dSKonstantin Belousov x86_iommu->qi_invalidate_emit(domain, base, size, &gseq, true); 458ad794e6dSKonstantin Belousov 459ad794e6dSKonstantin Belousov /* 460ad794e6dSKonstantin Belousov * To avoid a missed wakeup in iommu_qi_task(), the unit's 461ad794e6dSKonstantin Belousov * waiters count must be incremented before the tail is 462ad794e6dSKonstantin Belousov * advanced. 463ad794e6dSKonstantin Belousov */ 464ad794e6dSKonstantin Belousov IOMMU2X86C(unit)->inv_seq_waiters++; 465ad794e6dSKonstantin Belousov 466ad794e6dSKonstantin Belousov x86_iommu->qi_advance_tail(unit); 467ad794e6dSKonstantin Belousov iommu_qi_wait_for_seq(unit, &gseq, !cansleep); 468ad794e6dSKonstantin Belousov IOMMU_UNLOCK(unit); 469ad794e6dSKonstantin Belousov } 470ad794e6dSKonstantin Belousov 471ad794e6dSKonstantin Belousov void 472ad794e6dSKonstantin Belousov iommu_qi_drain_tlb_flush(struct iommu_unit *unit) 473ad794e6dSKonstantin Belousov { 474ad794e6dSKonstantin Belousov struct x86_unit_common *x86c; 475ad794e6dSKonstantin Belousov struct iommu_map_entry *entry, *head; 476ad794e6dSKonstantin Belousov 477ad794e6dSKonstantin Belousov x86c = IOMMU2X86C(unit); 478ad794e6dSKonstantin Belousov for (head = x86c->tlb_flush_head;; head = entry) { 479ad794e6dSKonstantin Belousov entry = (struct iommu_map_entry *) 480ad794e6dSKonstantin Belousov atomic_load_acq_ptr((uintptr_t *)&head->tlb_flush_next); 481ad794e6dSKonstantin Belousov if (entry == NULL || 482ad794e6dSKonstantin Belousov !iommu_qi_seq_processed(unit, &entry->gseq)) 483ad794e6dSKonstantin Belousov break; 484ad794e6dSKonstantin Belousov x86c->tlb_flush_head = entry; 485ad794e6dSKonstantin Belousov iommu_gas_free_entry(head); 486ad794e6dSKonstantin Belousov if ((entry->flags & IOMMU_MAP_ENTRY_RMRR) != 0) 487ad794e6dSKonstantin Belousov iommu_gas_free_region(entry); 488ad794e6dSKonstantin Belousov else 489ad794e6dSKonstantin Belousov iommu_gas_free_space(entry); 490ad794e6dSKonstantin Belousov } 491ad794e6dSKonstantin Belousov } 492ad794e6dSKonstantin Belousov 493ad794e6dSKonstantin Belousov void 494ad794e6dSKonstantin Belousov iommu_qi_common_init(struct iommu_unit *unit, task_fn_t qi_task) 495ad794e6dSKonstantin Belousov { 496ad794e6dSKonstantin Belousov struct x86_unit_common *x86c; 497ad794e6dSKonstantin Belousov u_int qi_sz; 498ad794e6dSKonstantin Belousov 499ad794e6dSKonstantin Belousov x86c = IOMMU2X86C(unit); 500ad794e6dSKonstantin Belousov 501ad794e6dSKonstantin Belousov x86c->tlb_flush_head = x86c->tlb_flush_tail = 502ad794e6dSKonstantin Belousov iommu_gas_alloc_entry(NULL, 0); 503ad794e6dSKonstantin Belousov TASK_INIT(&x86c->qi_task, 0, qi_task, unit); 504ad794e6dSKonstantin Belousov x86c->qi_taskqueue = taskqueue_create_fast("iommuqf", M_WAITOK, 505ad794e6dSKonstantin Belousov taskqueue_thread_enqueue, &x86c->qi_taskqueue); 506ad794e6dSKonstantin Belousov taskqueue_start_threads(&x86c->qi_taskqueue, 1, PI_AV, 507ad794e6dSKonstantin Belousov "iommu%d qi taskq", unit->unit); 508ad794e6dSKonstantin Belousov 509ad794e6dSKonstantin Belousov x86c->inv_waitd_gen = 0; 510ad794e6dSKonstantin Belousov x86c->inv_waitd_seq = 1; 511ad794e6dSKonstantin Belousov 512ad794e6dSKonstantin Belousov qi_sz = 3; 513ad794e6dSKonstantin Belousov TUNABLE_INT_FETCH("hw.iommu.qi_size", &qi_sz); 514ad794e6dSKonstantin Belousov if (qi_sz > x86c->qi_buf_maxsz) 515ad794e6dSKonstantin Belousov qi_sz = x86c->qi_buf_maxsz; 516ad794e6dSKonstantin Belousov x86c->inv_queue_size = (1ULL << qi_sz) * PAGE_SIZE; 517ad794e6dSKonstantin Belousov /* Reserve one descriptor to prevent wraparound. */ 518ad794e6dSKonstantin Belousov x86c->inv_queue_avail = x86c->inv_queue_size - 519ad794e6dSKonstantin Belousov x86c->qi_cmd_sz; 520ad794e6dSKonstantin Belousov 521ad794e6dSKonstantin Belousov /* 522ad794e6dSKonstantin Belousov * The invalidation queue reads by DMARs/AMDIOMMUs are always 523ad794e6dSKonstantin Belousov * coherent. 524ad794e6dSKonstantin Belousov */ 525ad794e6dSKonstantin Belousov x86c->inv_queue = kmem_alloc_contig(x86c->inv_queue_size, 526ad794e6dSKonstantin Belousov M_WAITOK | M_ZERO, 0, iommu_high, PAGE_SIZE, 0, 527ad794e6dSKonstantin Belousov VM_MEMATTR_DEFAULT); 528ad794e6dSKonstantin Belousov x86c->inv_waitd_seq_hw_phys = pmap_kextract( 529ad794e6dSKonstantin Belousov (vm_offset_t)&x86c->inv_waitd_seq_hw); 530ad794e6dSKonstantin Belousov } 531ad794e6dSKonstantin Belousov 532ad794e6dSKonstantin Belousov void 533ad794e6dSKonstantin Belousov iommu_qi_common_fini(struct iommu_unit *unit, void (*disable_qi)( 534ad794e6dSKonstantin Belousov struct iommu_unit *)) 535ad794e6dSKonstantin Belousov { 536ad794e6dSKonstantin Belousov struct x86_unit_common *x86c; 537ad794e6dSKonstantin Belousov struct iommu_qi_genseq gseq; 538ad794e6dSKonstantin Belousov 539ad794e6dSKonstantin Belousov x86c = IOMMU2X86C(unit); 540ad794e6dSKonstantin Belousov 541ad794e6dSKonstantin Belousov taskqueue_drain(x86c->qi_taskqueue, &x86c->qi_task); 542ad794e6dSKonstantin Belousov taskqueue_free(x86c->qi_taskqueue); 543ad794e6dSKonstantin Belousov x86c->qi_taskqueue = NULL; 544ad794e6dSKonstantin Belousov 545ad794e6dSKonstantin Belousov IOMMU_LOCK(unit); 546ad794e6dSKonstantin Belousov /* quisce */ 547ad794e6dSKonstantin Belousov x86_iommu->qi_ensure(unit, 1); 548ad794e6dSKonstantin Belousov iommu_qi_emit_wait_seq(unit, &gseq, true); 549ad794e6dSKonstantin Belousov /* See iommu_qi_invalidate_locked(). */ 550ad794e6dSKonstantin Belousov x86c->inv_seq_waiters++; 551ad794e6dSKonstantin Belousov x86_iommu->qi_advance_tail(unit); 552ad794e6dSKonstantin Belousov iommu_qi_wait_for_seq(unit, &gseq, false); 553ad794e6dSKonstantin Belousov /* only after the quisce, disable queue */ 554ad794e6dSKonstantin Belousov disable_qi(unit); 555ad794e6dSKonstantin Belousov KASSERT(x86c->inv_seq_waiters == 0, 556ad794e6dSKonstantin Belousov ("iommu%d: waiters on disabled queue", unit->unit)); 557ad794e6dSKonstantin Belousov IOMMU_UNLOCK(unit); 558ad794e6dSKonstantin Belousov 559ad794e6dSKonstantin Belousov kmem_free(x86c->inv_queue, x86c->inv_queue_size); 560ad794e6dSKonstantin Belousov x86c->inv_queue = NULL; 561ad794e6dSKonstantin Belousov x86c->inv_queue_size = 0; 562ad794e6dSKonstantin Belousov } 5635967352aSKonstantin Belousov 5645967352aSKonstantin Belousov int 5655967352aSKonstantin Belousov iommu_alloc_irq(struct iommu_unit *unit, int idx) 5665967352aSKonstantin Belousov { 5675967352aSKonstantin Belousov device_t dev, pcib; 5685967352aSKonstantin Belousov struct iommu_msi_data *dmd; 5695967352aSKonstantin Belousov uint64_t msi_addr; 5705967352aSKonstantin Belousov uint32_t msi_data; 5715967352aSKonstantin Belousov int error; 5725967352aSKonstantin Belousov 5735967352aSKonstantin Belousov MPASS(idx >= 0 || idx < IOMMU_MAX_MSI); 5745967352aSKonstantin Belousov 5755967352aSKonstantin Belousov dev = unit->dev; 5765967352aSKonstantin Belousov dmd = &IOMMU2X86C(unit)->intrs[idx]; 5775967352aSKonstantin Belousov pcib = device_get_parent(device_get_parent(dev)); /* Really not pcib */ 5785967352aSKonstantin Belousov error = PCIB_ALLOC_MSIX(pcib, dev, &dmd->irq); 5795967352aSKonstantin Belousov if (error != 0) { 5805967352aSKonstantin Belousov device_printf(dev, "cannot allocate %s interrupt, %d\n", 5815967352aSKonstantin Belousov dmd->name, error); 5825967352aSKonstantin Belousov goto err1; 5835967352aSKonstantin Belousov } 5845967352aSKonstantin Belousov error = bus_set_resource(dev, SYS_RES_IRQ, dmd->irq_rid, 5855967352aSKonstantin Belousov dmd->irq, 1); 5865967352aSKonstantin Belousov if (error != 0) { 5875967352aSKonstantin Belousov device_printf(dev, "cannot set %s interrupt resource, %d\n", 5885967352aSKonstantin Belousov dmd->name, error); 5895967352aSKonstantin Belousov goto err2; 5905967352aSKonstantin Belousov } 5915967352aSKonstantin Belousov dmd->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, 5925967352aSKonstantin Belousov &dmd->irq_rid, RF_ACTIVE); 5935967352aSKonstantin Belousov if (dmd->irq_res == NULL) { 5945967352aSKonstantin Belousov device_printf(dev, 5955967352aSKonstantin Belousov "cannot allocate resource for %s interrupt\n", dmd->name); 5965967352aSKonstantin Belousov error = ENXIO; 5975967352aSKonstantin Belousov goto err3; 5985967352aSKonstantin Belousov } 5995967352aSKonstantin Belousov error = bus_setup_intr(dev, dmd->irq_res, INTR_TYPE_MISC, 6005967352aSKonstantin Belousov dmd->handler, NULL, unit, &dmd->intr_handle); 6015967352aSKonstantin Belousov if (error != 0) { 6025967352aSKonstantin Belousov device_printf(dev, "cannot setup %s interrupt, %d\n", 6035967352aSKonstantin Belousov dmd->name, error); 6045967352aSKonstantin Belousov goto err4; 6055967352aSKonstantin Belousov } 6065967352aSKonstantin Belousov bus_describe_intr(dev, dmd->irq_res, dmd->intr_handle, "%s", dmd->name); 6075967352aSKonstantin Belousov error = PCIB_MAP_MSI(pcib, dev, dmd->irq, &msi_addr, &msi_data); 6085967352aSKonstantin Belousov if (error != 0) { 6095967352aSKonstantin Belousov device_printf(dev, "cannot map %s interrupt, %d\n", 6105967352aSKonstantin Belousov dmd->name, error); 6115967352aSKonstantin Belousov goto err5; 6125967352aSKonstantin Belousov } 6135967352aSKonstantin Belousov 6145967352aSKonstantin Belousov dmd->msi_data = msi_data; 6155967352aSKonstantin Belousov dmd->msi_addr = msi_addr; 6165967352aSKonstantin Belousov 6175967352aSKonstantin Belousov return (0); 6185967352aSKonstantin Belousov 6195967352aSKonstantin Belousov err5: 6205967352aSKonstantin Belousov bus_teardown_intr(dev, dmd->irq_res, dmd->intr_handle); 6215967352aSKonstantin Belousov err4: 6225967352aSKonstantin Belousov bus_release_resource(dev, SYS_RES_IRQ, dmd->irq_rid, dmd->irq_res); 6235967352aSKonstantin Belousov err3: 6245967352aSKonstantin Belousov bus_delete_resource(dev, SYS_RES_IRQ, dmd->irq_rid); 6255967352aSKonstantin Belousov err2: 6265967352aSKonstantin Belousov PCIB_RELEASE_MSIX(pcib, dev, dmd->irq); 6275967352aSKonstantin Belousov dmd->irq = -1; 6285967352aSKonstantin Belousov err1: 6295967352aSKonstantin Belousov return (error); 6305967352aSKonstantin Belousov } 6315967352aSKonstantin Belousov 6325967352aSKonstantin Belousov void 6335967352aSKonstantin Belousov iommu_release_intr(struct iommu_unit *unit, int idx) 6345967352aSKonstantin Belousov { 6355967352aSKonstantin Belousov device_t dev; 6365967352aSKonstantin Belousov struct iommu_msi_data *dmd; 6375967352aSKonstantin Belousov 6385967352aSKonstantin Belousov MPASS(idx >= 0 || idx < IOMMU_MAX_MSI); 6395967352aSKonstantin Belousov 6405967352aSKonstantin Belousov dmd = &IOMMU2X86C(unit)->intrs[idx]; 6415967352aSKonstantin Belousov if (dmd->handler == NULL || dmd->irq == -1) 6425967352aSKonstantin Belousov return; 6435967352aSKonstantin Belousov dev = unit->dev; 6445967352aSKonstantin Belousov 6455967352aSKonstantin Belousov bus_teardown_intr(dev, dmd->irq_res, dmd->intr_handle); 6465967352aSKonstantin Belousov bus_release_resource(dev, SYS_RES_IRQ, dmd->irq_rid, dmd->irq_res); 6475967352aSKonstantin Belousov bus_delete_resource(dev, SYS_RES_IRQ, dmd->irq_rid); 6485967352aSKonstantin Belousov PCIB_RELEASE_MSIX(device_get_parent(device_get_parent(dev)), 6495967352aSKonstantin Belousov dev, dmd->irq); 6505967352aSKonstantin Belousov dmd->irq = -1; 6515967352aSKonstantin Belousov } 652ff54674bSKonstantin Belousov 653ff54674bSKonstantin Belousov void 654ff54674bSKonstantin Belousov iommu_device_tag_init(struct iommu_ctx *ctx, device_t dev) 655ff54674bSKonstantin Belousov { 656ff54674bSKonstantin Belousov bus_addr_t maxaddr; 657ff54674bSKonstantin Belousov 658ff54674bSKonstantin Belousov maxaddr = MIN(ctx->domain->end, BUS_SPACE_MAXADDR); 659ff54674bSKonstantin Belousov ctx->tag->common.impl = &bus_dma_iommu_impl; 660ff54674bSKonstantin Belousov ctx->tag->common.boundary = 0; 661ff54674bSKonstantin Belousov ctx->tag->common.lowaddr = maxaddr; 662ff54674bSKonstantin Belousov ctx->tag->common.highaddr = maxaddr; 663ff54674bSKonstantin Belousov ctx->tag->common.maxsize = maxaddr; 664ff54674bSKonstantin Belousov ctx->tag->common.nsegments = BUS_SPACE_UNRESTRICTED; 665ff54674bSKonstantin Belousov ctx->tag->common.maxsegsz = maxaddr; 666ff54674bSKonstantin Belousov ctx->tag->ctx = ctx; 667ff54674bSKonstantin Belousov ctx->tag->owner = dev; 668ff54674bSKonstantin Belousov } 669b3042e3aSKonstantin Belousov 670b3042e3aSKonstantin Belousov void 671b3042e3aSKonstantin Belousov iommu_domain_free_entry(struct iommu_map_entry *entry, bool free) 672b3042e3aSKonstantin Belousov { 673b3042e3aSKonstantin Belousov if ((entry->flags & IOMMU_MAP_ENTRY_RMRR) != 0) 674b3042e3aSKonstantin Belousov iommu_gas_free_region(entry); 675b3042e3aSKonstantin Belousov else 676b3042e3aSKonstantin Belousov iommu_gas_free_space(entry); 677b3042e3aSKonstantin Belousov if (free) 678b3042e3aSKonstantin Belousov iommu_gas_free_entry(entry); 679b3042e3aSKonstantin Belousov else 680b3042e3aSKonstantin Belousov entry->flags = 0; 681b3042e3aSKonstantin Belousov } 682b3042e3aSKonstantin Belousov 68329e22704SKonstantin Belousov /* 68429e22704SKonstantin Belousov * Index of the pte for the guest address base in the page table at 68529e22704SKonstantin Belousov * the level lvl. 68629e22704SKonstantin Belousov */ 68729e22704SKonstantin Belousov int 68829e22704SKonstantin Belousov pglvl_pgtbl_pte_off(int pglvl, iommu_gaddr_t base, int lvl) 68929e22704SKonstantin Belousov { 69029e22704SKonstantin Belousov 69129e22704SKonstantin Belousov base >>= IOMMU_PAGE_SHIFT + (pglvl - lvl - 1) * 69229e22704SKonstantin Belousov IOMMU_NPTEPGSHIFT; 69329e22704SKonstantin Belousov return (base & IOMMU_PTEMASK); 69429e22704SKonstantin Belousov } 69529e22704SKonstantin Belousov 69629e22704SKonstantin Belousov /* 69729e22704SKonstantin Belousov * Returns the page index of the page table page in the page table 69829e22704SKonstantin Belousov * object, which maps the given address base at the page table level 69929e22704SKonstantin Belousov * lvl. 70029e22704SKonstantin Belousov */ 70129e22704SKonstantin Belousov vm_pindex_t 70229e22704SKonstantin Belousov pglvl_pgtbl_get_pindex(int pglvl, iommu_gaddr_t base, int lvl) 70329e22704SKonstantin Belousov { 70429e22704SKonstantin Belousov vm_pindex_t idx, pidx; 70529e22704SKonstantin Belousov int i; 70629e22704SKonstantin Belousov 70729e22704SKonstantin Belousov KASSERT(lvl >= 0 && lvl < pglvl, 70829e22704SKonstantin Belousov ("wrong lvl %d %d", pglvl, lvl)); 70929e22704SKonstantin Belousov 71029e22704SKonstantin Belousov for (pidx = idx = 0, i = 0; i < lvl; i++, pidx = idx) { 71129e22704SKonstantin Belousov idx = pglvl_pgtbl_pte_off(pglvl, base, i) + 71229e22704SKonstantin Belousov pidx * IOMMU_NPTEPG + 1; 71329e22704SKonstantin Belousov } 71429e22704SKonstantin Belousov return (idx); 71529e22704SKonstantin Belousov } 71629e22704SKonstantin Belousov 71729e22704SKonstantin Belousov /* 71829e22704SKonstantin Belousov * Calculate the total amount of page table pages needed to map the 71929e22704SKonstantin Belousov * whole bus address space on the context with the selected agaw. 72029e22704SKonstantin Belousov */ 72129e22704SKonstantin Belousov vm_pindex_t 72229e22704SKonstantin Belousov pglvl_max_pages(int pglvl) 72329e22704SKonstantin Belousov { 72429e22704SKonstantin Belousov vm_pindex_t res; 72529e22704SKonstantin Belousov int i; 72629e22704SKonstantin Belousov 72729e22704SKonstantin Belousov for (res = 0, i = pglvl; i > 0; i--) { 72829e22704SKonstantin Belousov res *= IOMMU_NPTEPG; 72929e22704SKonstantin Belousov res++; 73029e22704SKonstantin Belousov } 73129e22704SKonstantin Belousov return (res); 73229e22704SKonstantin Belousov } 73329e22704SKonstantin Belousov 73429e22704SKonstantin Belousov iommu_gaddr_t 73529e22704SKonstantin Belousov pglvl_page_size(int total_pglvl, int lvl) 73629e22704SKonstantin Belousov { 73729e22704SKonstantin Belousov int rlvl; 73829e22704SKonstantin Belousov static const iommu_gaddr_t pg_sz[] = { 73929e22704SKonstantin Belousov (iommu_gaddr_t)IOMMU_PAGE_SIZE, 74029e22704SKonstantin Belousov (iommu_gaddr_t)IOMMU_PAGE_SIZE << IOMMU_NPTEPGSHIFT, 74129e22704SKonstantin Belousov (iommu_gaddr_t)IOMMU_PAGE_SIZE << (2 * IOMMU_NPTEPGSHIFT), 74229e22704SKonstantin Belousov (iommu_gaddr_t)IOMMU_PAGE_SIZE << (3 * IOMMU_NPTEPGSHIFT), 74329e22704SKonstantin Belousov (iommu_gaddr_t)IOMMU_PAGE_SIZE << (4 * IOMMU_NPTEPGSHIFT), 74429e22704SKonstantin Belousov (iommu_gaddr_t)IOMMU_PAGE_SIZE << (5 * IOMMU_NPTEPGSHIFT), 74529e22704SKonstantin Belousov (iommu_gaddr_t)IOMMU_PAGE_SIZE << (6 * IOMMU_NPTEPGSHIFT), 74629e22704SKonstantin Belousov }; 74729e22704SKonstantin Belousov 74829e22704SKonstantin Belousov KASSERT(lvl >= 0 && lvl < total_pglvl, 74929e22704SKonstantin Belousov ("total %d lvl %d", total_pglvl, lvl)); 75029e22704SKonstantin Belousov rlvl = total_pglvl - lvl - 1; 75129e22704SKonstantin Belousov KASSERT(rlvl < nitems(pg_sz), ("sizeof pg_sz lvl %d", lvl)); 75229e22704SKonstantin Belousov return (pg_sz[rlvl]); 75329e22704SKonstantin Belousov } 754e9d948cfSKonstantin Belousov 755*b08d332dSKonstantin Belousov void 756*b08d332dSKonstantin Belousov iommu_device_set_iommu_prop(device_t dev, device_t iommu) 757*b08d332dSKonstantin Belousov { 758*b08d332dSKonstantin Belousov device_t iommu_dev; 759*b08d332dSKonstantin Belousov int error; 760*b08d332dSKonstantin Belousov 761*b08d332dSKonstantin Belousov bus_topo_lock(); 762*b08d332dSKonstantin Belousov error = device_get_prop(dev, DEV_PROP_NAME_IOMMU, (void **)&iommu_dev); 763*b08d332dSKonstantin Belousov if (error == ENOENT) 764*b08d332dSKonstantin Belousov device_set_prop(dev, DEV_PROP_NAME_IOMMU, iommu, NULL, NULL); 765*b08d332dSKonstantin Belousov bus_topo_unlock(); 766*b08d332dSKonstantin Belousov } 767*b08d332dSKonstantin Belousov 768e9d948cfSKonstantin Belousov #ifdef DDB 769e9d948cfSKonstantin Belousov #include <ddb/ddb.h> 770e9d948cfSKonstantin Belousov #include <ddb/db_lex.h> 771e9d948cfSKonstantin Belousov 772e9d948cfSKonstantin Belousov void 773e9d948cfSKonstantin Belousov iommu_db_print_domain_entry(const struct iommu_map_entry *entry) 774e9d948cfSKonstantin Belousov { 775e9d948cfSKonstantin Belousov struct iommu_map_entry *l, *r; 776e9d948cfSKonstantin Belousov 777e9d948cfSKonstantin Belousov db_printf( 778e9d948cfSKonstantin Belousov " start %jx end %jx first %jx last %jx free_down %jx flags %x ", 779e9d948cfSKonstantin Belousov entry->start, entry->end, entry->first, entry->last, 780e9d948cfSKonstantin Belousov entry->free_down, entry->flags); 781e9d948cfSKonstantin Belousov db_printf("left "); 782e9d948cfSKonstantin Belousov l = RB_LEFT(entry, rb_entry); 783e9d948cfSKonstantin Belousov if (l == NULL) 784e9d948cfSKonstantin Belousov db_printf("NULL "); 785e9d948cfSKonstantin Belousov else 786e9d948cfSKonstantin Belousov db_printf("%jx ", l->start); 787e9d948cfSKonstantin Belousov db_printf("right "); 788e9d948cfSKonstantin Belousov r = RB_RIGHT(entry, rb_entry); 789e9d948cfSKonstantin Belousov if (r == NULL) 790e9d948cfSKonstantin Belousov db_printf("NULL"); 791e9d948cfSKonstantin Belousov else 792e9d948cfSKonstantin Belousov db_printf("%jx", r->start); 793e9d948cfSKonstantin Belousov db_printf("\n"); 794e9d948cfSKonstantin Belousov } 795e9d948cfSKonstantin Belousov 796e9d948cfSKonstantin Belousov void 797e9d948cfSKonstantin Belousov iommu_db_print_ctx(struct iommu_ctx *ctx) 798e9d948cfSKonstantin Belousov { 799e9d948cfSKonstantin Belousov db_printf( 800e9d948cfSKonstantin Belousov " @%p pci%d:%d:%d refs %d flags %#x loads %lu unloads %lu\n", 801e9d948cfSKonstantin Belousov ctx, pci_get_bus(ctx->tag->owner), 802e9d948cfSKonstantin Belousov pci_get_slot(ctx->tag->owner), 803e9d948cfSKonstantin Belousov pci_get_function(ctx->tag->owner), ctx->refs, 804e9d948cfSKonstantin Belousov ctx->flags, ctx->loads, ctx->unloads); 805e9d948cfSKonstantin Belousov } 806c9e22c74SKonstantin Belousov 807c9e22c74SKonstantin Belousov void 808c9e22c74SKonstantin Belousov iommu_db_domain_print_contexts(struct iommu_domain *iodom) 809c9e22c74SKonstantin Belousov { 810c9e22c74SKonstantin Belousov struct iommu_ctx *ctx; 811c9e22c74SKonstantin Belousov 812c9e22c74SKonstantin Belousov if (LIST_EMPTY(&iodom->contexts)) 813c9e22c74SKonstantin Belousov return; 814c9e22c74SKonstantin Belousov 815c9e22c74SKonstantin Belousov db_printf(" Contexts:\n"); 816c9e22c74SKonstantin Belousov LIST_FOREACH(ctx, &iodom->contexts, link) 817c9e22c74SKonstantin Belousov iommu_db_print_ctx(ctx); 818c9e22c74SKonstantin Belousov } 819c9e22c74SKonstantin Belousov 820c9e22c74SKonstantin Belousov void 821c9e22c74SKonstantin Belousov iommu_db_domain_print_mappings(struct iommu_domain *iodom) 822c9e22c74SKonstantin Belousov { 823c9e22c74SKonstantin Belousov struct iommu_map_entry *entry; 824c9e22c74SKonstantin Belousov 825c9e22c74SKonstantin Belousov db_printf(" mapped:\n"); 826c9e22c74SKonstantin Belousov RB_FOREACH(entry, iommu_gas_entries_tree, &iodom->rb_root) { 827c9e22c74SKonstantin Belousov iommu_db_print_domain_entry(entry); 828c9e22c74SKonstantin Belousov if (db_pager_quit) 829c9e22c74SKonstantin Belousov break; 830c9e22c74SKonstantin Belousov } 831c9e22c74SKonstantin Belousov if (db_pager_quit) 832c9e22c74SKonstantin Belousov return; 833c9e22c74SKonstantin Belousov db_printf(" unloading:\n"); 834c9e22c74SKonstantin Belousov TAILQ_FOREACH(entry, &iodom->unload_entries, dmamap_link) { 835c9e22c74SKonstantin Belousov iommu_db_print_domain_entry(entry); 836c9e22c74SKonstantin Belousov if (db_pager_quit) 837c9e22c74SKonstantin Belousov break; 838c9e22c74SKonstantin Belousov } 839c9e22c74SKonstantin Belousov } 840c9e22c74SKonstantin Belousov 841e9d948cfSKonstantin Belousov #endif 842