10a110d5bSKonstantin Belousov /*- 20a110d5bSKonstantin Belousov * Copyright (c) 2015 The FreeBSD Foundation 30a110d5bSKonstantin Belousov * 40a110d5bSKonstantin Belousov * This software was developed by Konstantin Belousov <kib@FreeBSD.org> 50a110d5bSKonstantin Belousov * under sponsorship from the FreeBSD Foundation. 60a110d5bSKonstantin Belousov * 70a110d5bSKonstantin Belousov * Redistribution and use in source and binary forms, with or without 80a110d5bSKonstantin Belousov * modification, are permitted provided that the following conditions 90a110d5bSKonstantin Belousov * are met: 100a110d5bSKonstantin Belousov * 1. Redistributions of source code must retain the above copyright 110a110d5bSKonstantin Belousov * notice, this list of conditions and the following disclaimer. 120a110d5bSKonstantin Belousov * 2. Redistributions in binary form must reproduce the above copyright 130a110d5bSKonstantin Belousov * notice, this list of conditions and the following disclaimer in the 140a110d5bSKonstantin Belousov * documentation and/or other materials provided with the distribution. 150a110d5bSKonstantin Belousov * 160a110d5bSKonstantin Belousov * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 170a110d5bSKonstantin Belousov * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 180a110d5bSKonstantin Belousov * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 190a110d5bSKonstantin Belousov * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 200a110d5bSKonstantin Belousov * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 210a110d5bSKonstantin Belousov * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 220a110d5bSKonstantin Belousov * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 230a110d5bSKonstantin Belousov * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 240a110d5bSKonstantin Belousov * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 250a110d5bSKonstantin Belousov * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 260a110d5bSKonstantin Belousov * SUCH DAMAGE. 270a110d5bSKonstantin Belousov */ 280a110d5bSKonstantin Belousov 290a110d5bSKonstantin Belousov #include <sys/param.h> 300a110d5bSKonstantin Belousov #include <sys/systm.h> 310a110d5bSKonstantin Belousov #include <sys/bus.h> 320a110d5bSKonstantin Belousov #include <sys/kernel.h> 330a110d5bSKonstantin Belousov #include <sys/lock.h> 340a110d5bSKonstantin Belousov #include <sys/malloc.h> 350a110d5bSKonstantin Belousov #include <sys/memdesc.h> 36e2e050c8SConrad Meyer #include <sys/mutex.h> 370a110d5bSKonstantin Belousov #include <sys/rman.h> 380a110d5bSKonstantin Belousov #include <sys/rwlock.h> 39c8597a1fSRuslan Bukin #include <sys/sysctl.h> 400a110d5bSKonstantin Belousov #include <sys/taskqueue.h> 410a110d5bSKonstantin Belousov #include <sys/tree.h> 420a110d5bSKonstantin Belousov #include <sys/vmem.h> 430a110d5bSKonstantin Belousov #include <vm/vm.h> 440a110d5bSKonstantin Belousov #include <vm/vm_extern.h> 450a110d5bSKonstantin Belousov #include <vm/vm_kern.h> 460a110d5bSKonstantin Belousov #include <vm/vm_object.h> 470a110d5bSKonstantin Belousov #include <vm/vm_page.h> 48c8597a1fSRuslan Bukin #include <dev/pci/pcireg.h> 49c8597a1fSRuslan Bukin #include <dev/pci/pcivar.h> 50c8597a1fSRuslan Bukin #include <machine/bus.h> 51c8597a1fSRuslan Bukin #include <machine/intr_machdep.h> 520a110d5bSKonstantin Belousov #include <x86/include/apicreg.h> 530a110d5bSKonstantin Belousov #include <x86/include/apicvar.h> 540a110d5bSKonstantin Belousov #include <x86/include/busdma_impl.h> 55f2b2f317SRuslan Bukin #include <dev/iommu/busdma_iommu.h> 56c8597a1fSRuslan Bukin #include <x86/iommu/intel_reg.h> 5740d951bcSKonstantin Belousov #include <x86/iommu/x86_iommu.h> 580a110d5bSKonstantin Belousov #include <x86/iommu/intel_dmar.h> 590a110d5bSKonstantin Belousov #include <x86/iommu/iommu_intrmap.h> 600a110d5bSKonstantin Belousov 610a110d5bSKonstantin Belousov static struct dmar_unit *dmar_ir_find(device_t src, uint16_t *rid, 620a110d5bSKonstantin Belousov int *is_dmar); 630a110d5bSKonstantin Belousov static void dmar_ir_program_irte(struct dmar_unit *unit, u_int idx, 640a110d5bSKonstantin Belousov uint64_t low, uint16_t rid); 650a110d5bSKonstantin Belousov static int dmar_ir_free_irte(struct dmar_unit *unit, u_int cookie); 660a110d5bSKonstantin Belousov 670a110d5bSKonstantin Belousov int 6865b133e5SKonstantin Belousov dmar_alloc_msi_intr(device_t src, u_int *cookies, u_int count) 690a110d5bSKonstantin Belousov { 700a110d5bSKonstantin Belousov struct dmar_unit *unit; 710a110d5bSKonstantin Belousov vmem_addr_t vmem_res; 720a110d5bSKonstantin Belousov u_int idx, i; 730a110d5bSKonstantin Belousov int error; 740a110d5bSKonstantin Belousov 750a110d5bSKonstantin Belousov unit = dmar_ir_find(src, NULL, NULL); 760a110d5bSKonstantin Belousov if (unit == NULL || !unit->ir_enabled) { 770a110d5bSKonstantin Belousov for (i = 0; i < count; i++) 780a110d5bSKonstantin Belousov cookies[i] = -1; 790a110d5bSKonstantin Belousov return (EOPNOTSUPP); 800a110d5bSKonstantin Belousov } 810a110d5bSKonstantin Belousov 820a110d5bSKonstantin Belousov error = vmem_alloc(unit->irtids, count, M_FIRSTFIT | M_NOWAIT, 830a110d5bSKonstantin Belousov &vmem_res); 840a110d5bSKonstantin Belousov if (error != 0) { 850a110d5bSKonstantin Belousov KASSERT(error != EOPNOTSUPP, 860a110d5bSKonstantin Belousov ("impossible EOPNOTSUPP from vmem")); 870a110d5bSKonstantin Belousov return (error); 880a110d5bSKonstantin Belousov } 890a110d5bSKonstantin Belousov idx = vmem_res; 900a110d5bSKonstantin Belousov for (i = 0; i < count; i++) 910a110d5bSKonstantin Belousov cookies[i] = idx + i; 920a110d5bSKonstantin Belousov return (0); 930a110d5bSKonstantin Belousov } 940a110d5bSKonstantin Belousov 950a110d5bSKonstantin Belousov int 9665b133e5SKonstantin Belousov dmar_map_msi_intr(device_t src, u_int cpu, u_int vector, u_int cookie, 970a110d5bSKonstantin Belousov uint64_t *addr, uint32_t *data) 980a110d5bSKonstantin Belousov { 990a110d5bSKonstantin Belousov struct dmar_unit *unit; 1000a110d5bSKonstantin Belousov uint64_t low; 1010a110d5bSKonstantin Belousov uint16_t rid; 1020a110d5bSKonstantin Belousov int is_dmar; 1030a110d5bSKonstantin Belousov 1040a110d5bSKonstantin Belousov unit = dmar_ir_find(src, &rid, &is_dmar); 1050a110d5bSKonstantin Belousov if (is_dmar) { 1060a110d5bSKonstantin Belousov KASSERT(unit == NULL, ("DMAR cannot translate itself")); 1070a110d5bSKonstantin Belousov 1080a110d5bSKonstantin Belousov /* 1090a110d5bSKonstantin Belousov * See VT-d specification, 5.1.6 Remapping Hardware - 1100a110d5bSKonstantin Belousov * Interrupt Programming. 1110a110d5bSKonstantin Belousov */ 1120a110d5bSKonstantin Belousov *data = vector; 1130a110d5bSKonstantin Belousov *addr = MSI_INTEL_ADDR_BASE | ((cpu & 0xff) << 12); 1140a110d5bSKonstantin Belousov if (x2apic_mode) 1150a110d5bSKonstantin Belousov *addr |= ((uint64_t)cpu & 0xffffff00) << 32; 1160a110d5bSKonstantin Belousov else 1170a110d5bSKonstantin Belousov KASSERT(cpu <= 0xff, ("cpu id too big %d", cpu)); 1180a110d5bSKonstantin Belousov return (0); 1190a110d5bSKonstantin Belousov } 1200a110d5bSKonstantin Belousov if (unit == NULL || !unit->ir_enabled || cookie == -1) 1210a110d5bSKonstantin Belousov return (EOPNOTSUPP); 1220a110d5bSKonstantin Belousov 1230a110d5bSKonstantin Belousov low = (DMAR_X2APIC(unit) ? DMAR_IRTE1_DST_x2APIC(cpu) : 1240a110d5bSKonstantin Belousov DMAR_IRTE1_DST_xAPIC(cpu)) | DMAR_IRTE1_V(vector) | 1250a110d5bSKonstantin Belousov DMAR_IRTE1_DLM_FM | DMAR_IRTE1_TM_EDGE | DMAR_IRTE1_RH_DIRECT | 1260a110d5bSKonstantin Belousov DMAR_IRTE1_DM_PHYSICAL | DMAR_IRTE1_P; 1270a110d5bSKonstantin Belousov dmar_ir_program_irte(unit, cookie, low, rid); 1280a110d5bSKonstantin Belousov 1290a110d5bSKonstantin Belousov if (addr != NULL) { 1300a110d5bSKonstantin Belousov /* 1310a110d5bSKonstantin Belousov * See VT-d specification, 5.1.5.2 MSI and MSI-X 1320a110d5bSKonstantin Belousov * Register Programming. 1330a110d5bSKonstantin Belousov */ 1340a110d5bSKonstantin Belousov *addr = MSI_INTEL_ADDR_BASE | ((cookie & 0x7fff) << 5) | 1350a110d5bSKonstantin Belousov ((cookie & 0x8000) << 2) | 0x18; 1360a110d5bSKonstantin Belousov *data = 0; 1370a110d5bSKonstantin Belousov } 1380a110d5bSKonstantin Belousov return (0); 1390a110d5bSKonstantin Belousov } 1400a110d5bSKonstantin Belousov 1410a110d5bSKonstantin Belousov int 14265b133e5SKonstantin Belousov dmar_unmap_msi_intr(device_t src, u_int cookie) 1430a110d5bSKonstantin Belousov { 1440a110d5bSKonstantin Belousov struct dmar_unit *unit; 1450a110d5bSKonstantin Belousov 1460a110d5bSKonstantin Belousov if (cookie == -1) 1470a110d5bSKonstantin Belousov return (0); 1480a110d5bSKonstantin Belousov unit = dmar_ir_find(src, NULL, NULL); 1490a110d5bSKonstantin Belousov return (dmar_ir_free_irte(unit, cookie)); 1500a110d5bSKonstantin Belousov } 1510a110d5bSKonstantin Belousov 1520a110d5bSKonstantin Belousov int 15365b133e5SKonstantin Belousov dmar_map_ioapic_intr(u_int ioapic_id, u_int cpu, u_int vector, bool edge, 1540a110d5bSKonstantin Belousov bool activehi, int irq, u_int *cookie, uint32_t *hi, uint32_t *lo) 1550a110d5bSKonstantin Belousov { 1560a110d5bSKonstantin Belousov struct dmar_unit *unit; 1570a110d5bSKonstantin Belousov vmem_addr_t vmem_res; 1580a110d5bSKonstantin Belousov uint64_t low, iorte; 1590a110d5bSKonstantin Belousov u_int idx; 1600a110d5bSKonstantin Belousov int error; 1610a110d5bSKonstantin Belousov uint16_t rid; 1620a110d5bSKonstantin Belousov 1630a110d5bSKonstantin Belousov unit = dmar_find_ioapic(ioapic_id, &rid); 1640a110d5bSKonstantin Belousov if (unit == NULL || !unit->ir_enabled) { 1650a110d5bSKonstantin Belousov *cookie = -1; 1660a110d5bSKonstantin Belousov return (EOPNOTSUPP); 1670a110d5bSKonstantin Belousov } 1680a110d5bSKonstantin Belousov 1690a110d5bSKonstantin Belousov error = vmem_alloc(unit->irtids, 1, M_FIRSTFIT | M_NOWAIT, &vmem_res); 1700a110d5bSKonstantin Belousov if (error != 0) { 1710a110d5bSKonstantin Belousov KASSERT(error != EOPNOTSUPP, 1720a110d5bSKonstantin Belousov ("impossible EOPNOTSUPP from vmem")); 1730a110d5bSKonstantin Belousov return (error); 1740a110d5bSKonstantin Belousov } 1750a110d5bSKonstantin Belousov idx = vmem_res; 1760a110d5bSKonstantin Belousov low = 0; 1770a110d5bSKonstantin Belousov switch (irq) { 1780a110d5bSKonstantin Belousov case IRQ_EXTINT: 1790a110d5bSKonstantin Belousov low |= DMAR_IRTE1_DLM_ExtINT; 1800a110d5bSKonstantin Belousov break; 1810a110d5bSKonstantin Belousov case IRQ_NMI: 1820a110d5bSKonstantin Belousov low |= DMAR_IRTE1_DLM_NMI; 1830a110d5bSKonstantin Belousov break; 1840a110d5bSKonstantin Belousov case IRQ_SMI: 1850a110d5bSKonstantin Belousov low |= DMAR_IRTE1_DLM_SMI; 1860a110d5bSKonstantin Belousov break; 1870a110d5bSKonstantin Belousov default: 1880a110d5bSKonstantin Belousov KASSERT(vector != 0, ("No vector for IRQ %u", irq)); 1890a110d5bSKonstantin Belousov low |= DMAR_IRTE1_DLM_FM | DMAR_IRTE1_V(vector); 1900a110d5bSKonstantin Belousov break; 1910a110d5bSKonstantin Belousov } 1920a110d5bSKonstantin Belousov low |= (DMAR_X2APIC(unit) ? DMAR_IRTE1_DST_x2APIC(cpu) : 1930a110d5bSKonstantin Belousov DMAR_IRTE1_DST_xAPIC(cpu)) | 1940a110d5bSKonstantin Belousov (edge ? DMAR_IRTE1_TM_EDGE : DMAR_IRTE1_TM_LEVEL) | 1950a110d5bSKonstantin Belousov DMAR_IRTE1_RH_DIRECT | DMAR_IRTE1_DM_PHYSICAL | DMAR_IRTE1_P; 1960a110d5bSKonstantin Belousov dmar_ir_program_irte(unit, idx, low, rid); 1970a110d5bSKonstantin Belousov 1980a110d5bSKonstantin Belousov if (hi != NULL) { 1990a110d5bSKonstantin Belousov /* 2000a110d5bSKonstantin Belousov * See VT-d specification, 5.1.5.1 I/OxAPIC 2010a110d5bSKonstantin Belousov * Programming. 2020a110d5bSKonstantin Belousov */ 2030a110d5bSKonstantin Belousov iorte = (1ULL << 48) | ((uint64_t)(idx & 0x7fff) << 49) | 2040a110d5bSKonstantin Belousov ((idx & 0x8000) != 0 ? (1 << 11) : 0) | 2050a110d5bSKonstantin Belousov (edge ? IOART_TRGREDG : IOART_TRGRLVL) | 2060a110d5bSKonstantin Belousov (activehi ? IOART_INTAHI : IOART_INTALO) | 2070a110d5bSKonstantin Belousov IOART_DELFIXED | vector; 2080a110d5bSKonstantin Belousov *hi = iorte >> 32; 2090a110d5bSKonstantin Belousov *lo = iorte; 2100a110d5bSKonstantin Belousov } 2110a110d5bSKonstantin Belousov *cookie = idx; 2120a110d5bSKonstantin Belousov return (0); 2130a110d5bSKonstantin Belousov } 2140a110d5bSKonstantin Belousov 2150a110d5bSKonstantin Belousov int 21665b133e5SKonstantin Belousov dmar_unmap_ioapic_intr(u_int ioapic_id, u_int *cookie) 2170a110d5bSKonstantin Belousov { 2180a110d5bSKonstantin Belousov struct dmar_unit *unit; 2190a110d5bSKonstantin Belousov u_int idx; 2200a110d5bSKonstantin Belousov 2210a110d5bSKonstantin Belousov idx = *cookie; 2220a110d5bSKonstantin Belousov if (idx == -1) 2230a110d5bSKonstantin Belousov return (0); 2240a110d5bSKonstantin Belousov *cookie = -1; 2250a110d5bSKonstantin Belousov unit = dmar_find_ioapic(ioapic_id, NULL); 2260a110d5bSKonstantin Belousov KASSERT(unit != NULL && unit->ir_enabled, 2270a110d5bSKonstantin Belousov ("unmap: cookie %d unit %p", idx, unit)); 2280a110d5bSKonstantin Belousov return (dmar_ir_free_irte(unit, idx)); 2290a110d5bSKonstantin Belousov } 2300a110d5bSKonstantin Belousov 2310a110d5bSKonstantin Belousov static struct dmar_unit * 2320a110d5bSKonstantin Belousov dmar_ir_find(device_t src, uint16_t *rid, int *is_dmar) 2330a110d5bSKonstantin Belousov { 2340a110d5bSKonstantin Belousov devclass_t src_class; 2350a110d5bSKonstantin Belousov struct dmar_unit *unit; 2360a110d5bSKonstantin Belousov 2370a110d5bSKonstantin Belousov /* 2380a110d5bSKonstantin Belousov * We need to determine if the interrupt source generates FSB 2390a110d5bSKonstantin Belousov * interrupts. If yes, it is either DMAR, in which case 2400a110d5bSKonstantin Belousov * interrupts are not remapped. Or it is HPET, and interrupts 2410a110d5bSKonstantin Belousov * are remapped. For HPET, source id is reported by HPET 2420a110d5bSKonstantin Belousov * record in DMAR ACPI table. 2430a110d5bSKonstantin Belousov */ 2440a110d5bSKonstantin Belousov if (is_dmar != NULL) 2450a110d5bSKonstantin Belousov *is_dmar = FALSE; 2460a110d5bSKonstantin Belousov src_class = device_get_devclass(src); 2470a110d5bSKonstantin Belousov if (src_class == devclass_find("dmar")) { 2480a110d5bSKonstantin Belousov unit = NULL; 2490a110d5bSKonstantin Belousov if (is_dmar != NULL) 2500a110d5bSKonstantin Belousov *is_dmar = TRUE; 2510a110d5bSKonstantin Belousov } else if (src_class == devclass_find("hpet")) { 2520a110d5bSKonstantin Belousov unit = dmar_find_hpet(src, rid); 2530a110d5bSKonstantin Belousov } else { 254f9feb091SKonstantin Belousov unit = dmar_find(src, bootverbose); 2550a110d5bSKonstantin Belousov if (unit != NULL && rid != NULL) 25659e37c8aSRuslan Bukin iommu_get_requester(src, rid); 2570a110d5bSKonstantin Belousov } 2580a110d5bSKonstantin Belousov return (unit); 2590a110d5bSKonstantin Belousov } 2600a110d5bSKonstantin Belousov 2610a110d5bSKonstantin Belousov static void 2620a110d5bSKonstantin Belousov dmar_ir_program_irte(struct dmar_unit *unit, u_int idx, uint64_t low, 2630a110d5bSKonstantin Belousov uint16_t rid) 2640a110d5bSKonstantin Belousov { 2650a110d5bSKonstantin Belousov dmar_irte_t *irte; 2660a110d5bSKonstantin Belousov uint64_t high; 2670a110d5bSKonstantin Belousov 2680a110d5bSKonstantin Belousov KASSERT(idx < unit->irte_cnt, 2690a110d5bSKonstantin Belousov ("bad cookie %d %d", idx, unit->irte_cnt)); 2700a110d5bSKonstantin Belousov irte = &(unit->irt[idx]); 2710a110d5bSKonstantin Belousov high = DMAR_IRTE2_SVT_RID | DMAR_IRTE2_SQ_RID | 2720a110d5bSKonstantin Belousov DMAR_IRTE2_SID_RID(rid); 2731ad4a031SKonstantin Belousov if (bootverbose) { 274164fdee1SKonstantin Belousov device_printf(unit->iommu.dev, 2750a110d5bSKonstantin Belousov "programming irte[%d] rid %#x high %#jx low %#jx\n", 2760a110d5bSKonstantin Belousov idx, rid, (uintmax_t)high, (uintmax_t)low); 2771ad4a031SKonstantin Belousov } 2780a110d5bSKonstantin Belousov DMAR_LOCK(unit); 2790a110d5bSKonstantin Belousov if ((irte->irte1 & DMAR_IRTE1_P) != 0) { 2800a110d5bSKonstantin Belousov /* 2810a110d5bSKonstantin Belousov * The rte is already valid. Assume that the request 2820a110d5bSKonstantin Belousov * is to remap the interrupt for balancing. Only low 2830a110d5bSKonstantin Belousov * word of rte needs to be changed. Assert that the 2840a110d5bSKonstantin Belousov * high word contains expected value. 2850a110d5bSKonstantin Belousov */ 2860a110d5bSKonstantin Belousov KASSERT(irte->irte2 == high, 2870a110d5bSKonstantin Belousov ("irte2 mismatch, %jx %jx", (uintmax_t)irte->irte2, 2880a110d5bSKonstantin Belousov (uintmax_t)high)); 2890a110d5bSKonstantin Belousov dmar_pte_update(&irte->irte1, low); 2900a110d5bSKonstantin Belousov } else { 2910a110d5bSKonstantin Belousov dmar_pte_store(&irte->irte2, high); 2920a110d5bSKonstantin Belousov dmar_pte_store(&irte->irte1, low); 2930a110d5bSKonstantin Belousov } 2940a110d5bSKonstantin Belousov dmar_qi_invalidate_iec(unit, idx, 1); 2950a110d5bSKonstantin Belousov DMAR_UNLOCK(unit); 2960a110d5bSKonstantin Belousov 2970a110d5bSKonstantin Belousov } 2980a110d5bSKonstantin Belousov 2990a110d5bSKonstantin Belousov static int 3000a110d5bSKonstantin Belousov dmar_ir_free_irte(struct dmar_unit *unit, u_int cookie) 3010a110d5bSKonstantin Belousov { 3020a110d5bSKonstantin Belousov dmar_irte_t *irte; 3030a110d5bSKonstantin Belousov 3040a110d5bSKonstantin Belousov KASSERT(unit != NULL && unit->ir_enabled, 3050a110d5bSKonstantin Belousov ("unmap: cookie %d unit %p", cookie, unit)); 3060a110d5bSKonstantin Belousov KASSERT(cookie < unit->irte_cnt, 3070a110d5bSKonstantin Belousov ("bad cookie %u %u", cookie, unit->irte_cnt)); 3080a110d5bSKonstantin Belousov irte = &(unit->irt[cookie]); 3090a110d5bSKonstantin Belousov dmar_pte_clear(&irte->irte1); 3100a110d5bSKonstantin Belousov dmar_pte_clear(&irte->irte2); 3110a110d5bSKonstantin Belousov DMAR_LOCK(unit); 3120a110d5bSKonstantin Belousov dmar_qi_invalidate_iec(unit, cookie, 1); 3130a110d5bSKonstantin Belousov DMAR_UNLOCK(unit); 3140a110d5bSKonstantin Belousov vmem_free(unit->irtids, cookie, 1); 3150a110d5bSKonstantin Belousov return (0); 3160a110d5bSKonstantin Belousov } 3170a110d5bSKonstantin Belousov 3180a110d5bSKonstantin Belousov int 3190a110d5bSKonstantin Belousov dmar_init_irt(struct dmar_unit *unit) 3200a110d5bSKonstantin Belousov { 321*d50403a6SKonstantin Belousov SYSCTL_ADD_INT(&unit->iommu.sysctl_ctx, 322*d50403a6SKonstantin Belousov SYSCTL_CHILDREN(device_get_sysctl_tree(unit->iommu.dev)), 323*d50403a6SKonstantin Belousov OID_AUTO, "ir", CTLFLAG_RD, &unit->ir_enabled, 0, 324*d50403a6SKonstantin Belousov "Interrupt remapping ops enabled"); 3250a110d5bSKonstantin Belousov if ((unit->hw_ecap & DMAR_ECAP_IR) == 0) 3260a110d5bSKonstantin Belousov return (0); 3270a110d5bSKonstantin Belousov unit->ir_enabled = 1; 3280a110d5bSKonstantin Belousov TUNABLE_INT_FETCH("hw.dmar.ir", &unit->ir_enabled); 32922bf8cf3SKonstantin Belousov TUNABLE_INT_FETCH("hw.iommu.ir", &unit->ir_enabled); 3300a110d5bSKonstantin Belousov if (!unit->ir_enabled) 3310a110d5bSKonstantin Belousov return (0); 3320a110d5bSKonstantin Belousov if (!unit->qi_enabled) { 3330a110d5bSKonstantin Belousov unit->ir_enabled = 0; 3340a110d5bSKonstantin Belousov if (bootverbose) 335164fdee1SKonstantin Belousov device_printf(unit->iommu.dev, 3360a110d5bSKonstantin Belousov "QI disabled, disabling interrupt remapping\n"); 3370a110d5bSKonstantin Belousov return (0); 3380a110d5bSKonstantin Belousov } 3398502c68dSDoug Moore unit->irte_cnt = roundup_pow_of_two(num_io_irqs); 340f49fd63aSJohn Baldwin unit->irt = kmem_alloc_contig(unit->irte_cnt * sizeof(dmar_irte_t), 34140d951bcSKonstantin Belousov M_ZERO | M_WAITOK, 0, iommu_high, PAGE_SIZE, 0, 342f49fd63aSJohn Baldwin DMAR_IS_COHERENT(unit) ? 3430a110d5bSKonstantin Belousov VM_MEMATTR_DEFAULT : VM_MEMATTR_UNCACHEABLE); 3440a110d5bSKonstantin Belousov if (unit->irt == NULL) 3450a110d5bSKonstantin Belousov return (ENOMEM); 3460a110d5bSKonstantin Belousov unit->irt_phys = pmap_kextract((vm_offset_t)unit->irt); 3470a110d5bSKonstantin Belousov unit->irtids = vmem_create("dmarirt", 0, unit->irte_cnt, 1, 0, 3480a110d5bSKonstantin Belousov M_FIRSTFIT | M_NOWAIT); 3490a110d5bSKonstantin Belousov DMAR_LOCK(unit); 3500a110d5bSKonstantin Belousov dmar_load_irt_ptr(unit); 3510a110d5bSKonstantin Belousov dmar_qi_invalidate_iec_glob(unit); 3520a110d5bSKonstantin Belousov DMAR_UNLOCK(unit); 3530a110d5bSKonstantin Belousov 3540a110d5bSKonstantin Belousov /* 3550a110d5bSKonstantin Belousov * Initialize mappings for already configured interrupt pins. 3560a110d5bSKonstantin Belousov * Required, because otherwise the interrupts fault without 3570a110d5bSKonstantin Belousov * irtes. 3580a110d5bSKonstantin Belousov */ 3590a110d5bSKonstantin Belousov intr_reprogram(); 3600a110d5bSKonstantin Belousov 3610a110d5bSKonstantin Belousov DMAR_LOCK(unit); 3620a110d5bSKonstantin Belousov dmar_enable_ir(unit); 3630a110d5bSKonstantin Belousov DMAR_UNLOCK(unit); 3640a110d5bSKonstantin Belousov return (0); 3650a110d5bSKonstantin Belousov } 3660a110d5bSKonstantin Belousov 3670a110d5bSKonstantin Belousov void 3680a110d5bSKonstantin Belousov dmar_fini_irt(struct dmar_unit *unit) 3690a110d5bSKonstantin Belousov { 3700a110d5bSKonstantin Belousov 3710a110d5bSKonstantin Belousov unit->ir_enabled = 0; 3720a110d5bSKonstantin Belousov if (unit->irt != NULL) { 3730a110d5bSKonstantin Belousov dmar_disable_ir(unit); 3740a110d5bSKonstantin Belousov dmar_qi_invalidate_iec_glob(unit); 3750a110d5bSKonstantin Belousov vmem_destroy(unit->irtids); 376f49fd63aSJohn Baldwin kmem_free(unit->irt, unit->irte_cnt * sizeof(dmar_irte_t)); 3770a110d5bSKonstantin Belousov } 3780a110d5bSKonstantin Belousov } 379