xref: /freebsd/sys/x86/iommu/intel_intrmap.c (revision 0a110d5b172e20b84652aa92d5e0a7f0cd4fa323)
1*0a110d5bSKonstantin Belousov /*-
2*0a110d5bSKonstantin Belousov  * Copyright (c) 2015 The FreeBSD Foundation
3*0a110d5bSKonstantin Belousov  * All rights reserved.
4*0a110d5bSKonstantin Belousov  *
5*0a110d5bSKonstantin Belousov  * This software was developed by Konstantin Belousov <kib@FreeBSD.org>
6*0a110d5bSKonstantin Belousov  * under sponsorship from the FreeBSD Foundation.
7*0a110d5bSKonstantin Belousov  *
8*0a110d5bSKonstantin Belousov  * Redistribution and use in source and binary forms, with or without
9*0a110d5bSKonstantin Belousov  * modification, are permitted provided that the following conditions
10*0a110d5bSKonstantin Belousov  * are met:
11*0a110d5bSKonstantin Belousov  * 1. Redistributions of source code must retain the above copyright
12*0a110d5bSKonstantin Belousov  *    notice, this list of conditions and the following disclaimer.
13*0a110d5bSKonstantin Belousov  * 2. Redistributions in binary form must reproduce the above copyright
14*0a110d5bSKonstantin Belousov  *    notice, this list of conditions and the following disclaimer in the
15*0a110d5bSKonstantin Belousov  *    documentation and/or other materials provided with the distribution.
16*0a110d5bSKonstantin Belousov  *
17*0a110d5bSKonstantin Belousov  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18*0a110d5bSKonstantin Belousov  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19*0a110d5bSKonstantin Belousov  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20*0a110d5bSKonstantin Belousov  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21*0a110d5bSKonstantin Belousov  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22*0a110d5bSKonstantin Belousov  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23*0a110d5bSKonstantin Belousov  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24*0a110d5bSKonstantin Belousov  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25*0a110d5bSKonstantin Belousov  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26*0a110d5bSKonstantin Belousov  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27*0a110d5bSKonstantin Belousov  * SUCH DAMAGE.
28*0a110d5bSKonstantin Belousov  */
29*0a110d5bSKonstantin Belousov 
30*0a110d5bSKonstantin Belousov #include <sys/cdefs.h>
31*0a110d5bSKonstantin Belousov __FBSDID("$FreeBSD$");
32*0a110d5bSKonstantin Belousov 
33*0a110d5bSKonstantin Belousov #include <sys/param.h>
34*0a110d5bSKonstantin Belousov #include <sys/systm.h>
35*0a110d5bSKonstantin Belousov #include <sys/bus.h>
36*0a110d5bSKonstantin Belousov #include <sys/kernel.h>
37*0a110d5bSKonstantin Belousov #include <sys/lock.h>
38*0a110d5bSKonstantin Belousov #include <sys/malloc.h>
39*0a110d5bSKonstantin Belousov #include <sys/memdesc.h>
40*0a110d5bSKonstantin Belousov #include <sys/rman.h>
41*0a110d5bSKonstantin Belousov #include <sys/rwlock.h>
42*0a110d5bSKonstantin Belousov #include <sys/taskqueue.h>
43*0a110d5bSKonstantin Belousov #include <sys/tree.h>
44*0a110d5bSKonstantin Belousov #include <sys/vmem.h>
45*0a110d5bSKonstantin Belousov #include <machine/bus.h>
46*0a110d5bSKonstantin Belousov #include <machine/intr_machdep.h>
47*0a110d5bSKonstantin Belousov #include <vm/vm.h>
48*0a110d5bSKonstantin Belousov #include <vm/vm_extern.h>
49*0a110d5bSKonstantin Belousov #include <vm/vm_kern.h>
50*0a110d5bSKonstantin Belousov #include <vm/vm_object.h>
51*0a110d5bSKonstantin Belousov #include <vm/vm_page.h>
52*0a110d5bSKonstantin Belousov #include <x86/include/apicreg.h>
53*0a110d5bSKonstantin Belousov #include <x86/include/apicvar.h>
54*0a110d5bSKonstantin Belousov #include <x86/include/busdma_impl.h>
55*0a110d5bSKonstantin Belousov #include <x86/iommu/intel_reg.h>
56*0a110d5bSKonstantin Belousov #include <x86/iommu/busdma_dmar.h>
57*0a110d5bSKonstantin Belousov #include <x86/iommu/intel_dmar.h>
58*0a110d5bSKonstantin Belousov #include <dev/pci/pcivar.h>
59*0a110d5bSKonstantin Belousov #include <x86/iommu/iommu_intrmap.h>
60*0a110d5bSKonstantin Belousov 
61*0a110d5bSKonstantin Belousov static struct dmar_unit *dmar_ir_find(device_t src, uint16_t *rid,
62*0a110d5bSKonstantin Belousov     int *is_dmar);
63*0a110d5bSKonstantin Belousov static void dmar_ir_program_irte(struct dmar_unit *unit, u_int idx,
64*0a110d5bSKonstantin Belousov     uint64_t low, uint16_t rid);
65*0a110d5bSKonstantin Belousov static int dmar_ir_free_irte(struct dmar_unit *unit, u_int cookie);
66*0a110d5bSKonstantin Belousov 
67*0a110d5bSKonstantin Belousov int
68*0a110d5bSKonstantin Belousov iommu_alloc_msi_intr(device_t src, u_int *cookies, u_int count)
69*0a110d5bSKonstantin Belousov {
70*0a110d5bSKonstantin Belousov 	struct dmar_unit *unit;
71*0a110d5bSKonstantin Belousov 	vmem_addr_t vmem_res;
72*0a110d5bSKonstantin Belousov 	u_int idx, i;
73*0a110d5bSKonstantin Belousov 	int error;
74*0a110d5bSKonstantin Belousov 
75*0a110d5bSKonstantin Belousov 	unit = dmar_ir_find(src, NULL, NULL);
76*0a110d5bSKonstantin Belousov 	if (unit == NULL || !unit->ir_enabled) {
77*0a110d5bSKonstantin Belousov 		for (i = 0; i < count; i++)
78*0a110d5bSKonstantin Belousov 			cookies[i] = -1;
79*0a110d5bSKonstantin Belousov 		return (EOPNOTSUPP);
80*0a110d5bSKonstantin Belousov 	}
81*0a110d5bSKonstantin Belousov 
82*0a110d5bSKonstantin Belousov 	error = vmem_alloc(unit->irtids, count, M_FIRSTFIT | M_NOWAIT,
83*0a110d5bSKonstantin Belousov 	    &vmem_res);
84*0a110d5bSKonstantin Belousov 	if (error != 0) {
85*0a110d5bSKonstantin Belousov 		KASSERT(error != EOPNOTSUPP,
86*0a110d5bSKonstantin Belousov 		    ("impossible EOPNOTSUPP from vmem"));
87*0a110d5bSKonstantin Belousov 		return (error);
88*0a110d5bSKonstantin Belousov 	}
89*0a110d5bSKonstantin Belousov 	idx = vmem_res;
90*0a110d5bSKonstantin Belousov 	for (i = 0; i < count; i++)
91*0a110d5bSKonstantin Belousov 		cookies[i] = idx + i;
92*0a110d5bSKonstantin Belousov 	return (0);
93*0a110d5bSKonstantin Belousov }
94*0a110d5bSKonstantin Belousov 
95*0a110d5bSKonstantin Belousov int
96*0a110d5bSKonstantin Belousov iommu_map_msi_intr(device_t src, u_int cpu, u_int vector, u_int cookie,
97*0a110d5bSKonstantin Belousov     uint64_t *addr, uint32_t *data)
98*0a110d5bSKonstantin Belousov {
99*0a110d5bSKonstantin Belousov 	struct dmar_unit *unit;
100*0a110d5bSKonstantin Belousov 	uint64_t low;
101*0a110d5bSKonstantin Belousov 	uint16_t rid;
102*0a110d5bSKonstantin Belousov 	int is_dmar;
103*0a110d5bSKonstantin Belousov 
104*0a110d5bSKonstantin Belousov 	unit = dmar_ir_find(src, &rid, &is_dmar);
105*0a110d5bSKonstantin Belousov 	if (is_dmar) {
106*0a110d5bSKonstantin Belousov 		KASSERT(unit == NULL, ("DMAR cannot translate itself"));
107*0a110d5bSKonstantin Belousov 
108*0a110d5bSKonstantin Belousov 		/*
109*0a110d5bSKonstantin Belousov 		 * See VT-d specification, 5.1.6 Remapping Hardware -
110*0a110d5bSKonstantin Belousov 		 * Interrupt Programming.
111*0a110d5bSKonstantin Belousov 		 */
112*0a110d5bSKonstantin Belousov 		*data = vector;
113*0a110d5bSKonstantin Belousov 		*addr = MSI_INTEL_ADDR_BASE | ((cpu & 0xff) << 12);
114*0a110d5bSKonstantin Belousov 		if (x2apic_mode)
115*0a110d5bSKonstantin Belousov 			*addr |= ((uint64_t)cpu & 0xffffff00) << 32;
116*0a110d5bSKonstantin Belousov 		else
117*0a110d5bSKonstantin Belousov 			KASSERT(cpu <= 0xff, ("cpu id too big %d", cpu));
118*0a110d5bSKonstantin Belousov 		return (0);
119*0a110d5bSKonstantin Belousov 	}
120*0a110d5bSKonstantin Belousov 	if (unit == NULL || !unit->ir_enabled || cookie == -1)
121*0a110d5bSKonstantin Belousov 		return (EOPNOTSUPP);
122*0a110d5bSKonstantin Belousov 
123*0a110d5bSKonstantin Belousov 	low = (DMAR_X2APIC(unit) ? DMAR_IRTE1_DST_x2APIC(cpu) :
124*0a110d5bSKonstantin Belousov 	    DMAR_IRTE1_DST_xAPIC(cpu)) | DMAR_IRTE1_V(vector) |
125*0a110d5bSKonstantin Belousov 	    DMAR_IRTE1_DLM_FM | DMAR_IRTE1_TM_EDGE | DMAR_IRTE1_RH_DIRECT |
126*0a110d5bSKonstantin Belousov 	    DMAR_IRTE1_DM_PHYSICAL | DMAR_IRTE1_P;
127*0a110d5bSKonstantin Belousov 	dmar_ir_program_irte(unit, cookie, low, rid);
128*0a110d5bSKonstantin Belousov 
129*0a110d5bSKonstantin Belousov 	if (addr != NULL) {
130*0a110d5bSKonstantin Belousov 		/*
131*0a110d5bSKonstantin Belousov 		 * See VT-d specification, 5.1.5.2 MSI and MSI-X
132*0a110d5bSKonstantin Belousov 		 * Register Programming.
133*0a110d5bSKonstantin Belousov 		 */
134*0a110d5bSKonstantin Belousov 		*addr = MSI_INTEL_ADDR_BASE | ((cookie & 0x7fff) << 5) |
135*0a110d5bSKonstantin Belousov 		    ((cookie & 0x8000) << 2) | 0x18;
136*0a110d5bSKonstantin Belousov 		*data = 0;
137*0a110d5bSKonstantin Belousov 	}
138*0a110d5bSKonstantin Belousov 	return (0);
139*0a110d5bSKonstantin Belousov }
140*0a110d5bSKonstantin Belousov 
141*0a110d5bSKonstantin Belousov int
142*0a110d5bSKonstantin Belousov iommu_unmap_msi_intr(device_t src, u_int cookie)
143*0a110d5bSKonstantin Belousov {
144*0a110d5bSKonstantin Belousov 	struct dmar_unit *unit;
145*0a110d5bSKonstantin Belousov 
146*0a110d5bSKonstantin Belousov 	if (cookie == -1)
147*0a110d5bSKonstantin Belousov 		return (0);
148*0a110d5bSKonstantin Belousov 	unit = dmar_ir_find(src, NULL, NULL);
149*0a110d5bSKonstantin Belousov 	return (dmar_ir_free_irte(unit, cookie));
150*0a110d5bSKonstantin Belousov }
151*0a110d5bSKonstantin Belousov 
152*0a110d5bSKonstantin Belousov int
153*0a110d5bSKonstantin Belousov iommu_map_ioapic_intr(u_int ioapic_id, u_int cpu, u_int vector, bool edge,
154*0a110d5bSKonstantin Belousov     bool activehi, int irq, u_int *cookie, uint32_t *hi, uint32_t *lo)
155*0a110d5bSKonstantin Belousov {
156*0a110d5bSKonstantin Belousov 	struct dmar_unit *unit;
157*0a110d5bSKonstantin Belousov 	vmem_addr_t vmem_res;
158*0a110d5bSKonstantin Belousov 	uint64_t low, iorte;
159*0a110d5bSKonstantin Belousov 	u_int idx;
160*0a110d5bSKonstantin Belousov 	int error;
161*0a110d5bSKonstantin Belousov 	uint16_t rid;
162*0a110d5bSKonstantin Belousov 
163*0a110d5bSKonstantin Belousov 	unit = dmar_find_ioapic(ioapic_id, &rid);
164*0a110d5bSKonstantin Belousov 	if (unit == NULL || !unit->ir_enabled) {
165*0a110d5bSKonstantin Belousov 		*cookie = -1;
166*0a110d5bSKonstantin Belousov 		return (EOPNOTSUPP);
167*0a110d5bSKonstantin Belousov 	}
168*0a110d5bSKonstantin Belousov 
169*0a110d5bSKonstantin Belousov 	error = vmem_alloc(unit->irtids, 1, M_FIRSTFIT | M_NOWAIT, &vmem_res);
170*0a110d5bSKonstantin Belousov 	if (error != 0) {
171*0a110d5bSKonstantin Belousov 		KASSERT(error != EOPNOTSUPP,
172*0a110d5bSKonstantin Belousov 		    ("impossible EOPNOTSUPP from vmem"));
173*0a110d5bSKonstantin Belousov 		return (error);
174*0a110d5bSKonstantin Belousov 	}
175*0a110d5bSKonstantin Belousov 	idx = vmem_res;
176*0a110d5bSKonstantin Belousov 	low = 0;
177*0a110d5bSKonstantin Belousov 	switch (irq) {
178*0a110d5bSKonstantin Belousov 	case IRQ_EXTINT:
179*0a110d5bSKonstantin Belousov 		low |= DMAR_IRTE1_DLM_ExtINT;
180*0a110d5bSKonstantin Belousov 		break;
181*0a110d5bSKonstantin Belousov 	case IRQ_NMI:
182*0a110d5bSKonstantin Belousov 		low |= DMAR_IRTE1_DLM_NMI;
183*0a110d5bSKonstantin Belousov 		break;
184*0a110d5bSKonstantin Belousov 	case IRQ_SMI:
185*0a110d5bSKonstantin Belousov 		low |= DMAR_IRTE1_DLM_SMI;
186*0a110d5bSKonstantin Belousov 		break;
187*0a110d5bSKonstantin Belousov 	default:
188*0a110d5bSKonstantin Belousov 		KASSERT(vector != 0, ("No vector for IRQ %u", irq));
189*0a110d5bSKonstantin Belousov 		low |= DMAR_IRTE1_DLM_FM | DMAR_IRTE1_V(vector);
190*0a110d5bSKonstantin Belousov 		break;
191*0a110d5bSKonstantin Belousov 	}
192*0a110d5bSKonstantin Belousov 	low |= (DMAR_X2APIC(unit) ? DMAR_IRTE1_DST_x2APIC(cpu) :
193*0a110d5bSKonstantin Belousov 	    DMAR_IRTE1_DST_xAPIC(cpu)) |
194*0a110d5bSKonstantin Belousov 	    (edge ? DMAR_IRTE1_TM_EDGE : DMAR_IRTE1_TM_LEVEL) |
195*0a110d5bSKonstantin Belousov 	    DMAR_IRTE1_RH_DIRECT | DMAR_IRTE1_DM_PHYSICAL | DMAR_IRTE1_P;
196*0a110d5bSKonstantin Belousov 	dmar_ir_program_irte(unit, idx, low, rid);
197*0a110d5bSKonstantin Belousov 
198*0a110d5bSKonstantin Belousov 	if (hi != NULL) {
199*0a110d5bSKonstantin Belousov 		/*
200*0a110d5bSKonstantin Belousov 		 * See VT-d specification, 5.1.5.1 I/OxAPIC
201*0a110d5bSKonstantin Belousov 		 * Programming.
202*0a110d5bSKonstantin Belousov 		 */
203*0a110d5bSKonstantin Belousov 		iorte = (1ULL << 48) | ((uint64_t)(idx & 0x7fff) << 49) |
204*0a110d5bSKonstantin Belousov 		    ((idx & 0x8000) != 0 ? (1 << 11) : 0) |
205*0a110d5bSKonstantin Belousov 		    (edge ? IOART_TRGREDG : IOART_TRGRLVL) |
206*0a110d5bSKonstantin Belousov 		    (activehi ? IOART_INTAHI : IOART_INTALO) |
207*0a110d5bSKonstantin Belousov 		    IOART_DELFIXED | vector;
208*0a110d5bSKonstantin Belousov 		*hi = iorte >> 32;
209*0a110d5bSKonstantin Belousov 		*lo = iorte;
210*0a110d5bSKonstantin Belousov 	}
211*0a110d5bSKonstantin Belousov 	*cookie = idx;
212*0a110d5bSKonstantin Belousov 	return (0);
213*0a110d5bSKonstantin Belousov }
214*0a110d5bSKonstantin Belousov 
215*0a110d5bSKonstantin Belousov int
216*0a110d5bSKonstantin Belousov iommu_unmap_ioapic_intr(u_int ioapic_id, u_int *cookie)
217*0a110d5bSKonstantin Belousov {
218*0a110d5bSKonstantin Belousov 	struct dmar_unit *unit;
219*0a110d5bSKonstantin Belousov 	u_int idx;
220*0a110d5bSKonstantin Belousov 
221*0a110d5bSKonstantin Belousov 	idx = *cookie;
222*0a110d5bSKonstantin Belousov 	if (idx == -1)
223*0a110d5bSKonstantin Belousov 		return (0);
224*0a110d5bSKonstantin Belousov 	*cookie = -1;
225*0a110d5bSKonstantin Belousov 	unit = dmar_find_ioapic(ioapic_id, NULL);
226*0a110d5bSKonstantin Belousov 	KASSERT(unit != NULL && unit->ir_enabled,
227*0a110d5bSKonstantin Belousov 	    ("unmap: cookie %d unit %p", idx, unit));
228*0a110d5bSKonstantin Belousov 	return (dmar_ir_free_irte(unit, idx));
229*0a110d5bSKonstantin Belousov }
230*0a110d5bSKonstantin Belousov 
231*0a110d5bSKonstantin Belousov static struct dmar_unit *
232*0a110d5bSKonstantin Belousov dmar_ir_find(device_t src, uint16_t *rid, int *is_dmar)
233*0a110d5bSKonstantin Belousov {
234*0a110d5bSKonstantin Belousov 	devclass_t src_class;
235*0a110d5bSKonstantin Belousov 	struct dmar_unit *unit;
236*0a110d5bSKonstantin Belousov 
237*0a110d5bSKonstantin Belousov 	/*
238*0a110d5bSKonstantin Belousov 	 * We need to determine if the interrupt source generates FSB
239*0a110d5bSKonstantin Belousov 	 * interrupts.  If yes, it is either DMAR, in which case
240*0a110d5bSKonstantin Belousov 	 * interrupts are not remapped.  Or it is HPET, and interrupts
241*0a110d5bSKonstantin Belousov 	 * are remapped.  For HPET, source id is reported by HPET
242*0a110d5bSKonstantin Belousov 	 * record in DMAR ACPI table.
243*0a110d5bSKonstantin Belousov 	 */
244*0a110d5bSKonstantin Belousov 	if (is_dmar != NULL)
245*0a110d5bSKonstantin Belousov 		*is_dmar = FALSE;
246*0a110d5bSKonstantin Belousov 	src_class = device_get_devclass(src);
247*0a110d5bSKonstantin Belousov 	if (src_class == devclass_find("dmar")) {
248*0a110d5bSKonstantin Belousov 		unit = NULL;
249*0a110d5bSKonstantin Belousov 		if (is_dmar != NULL)
250*0a110d5bSKonstantin Belousov 			*is_dmar = TRUE;
251*0a110d5bSKonstantin Belousov 	} else if (src_class == devclass_find("hpet")) {
252*0a110d5bSKonstantin Belousov 		unit = dmar_find_hpet(src, rid);
253*0a110d5bSKonstantin Belousov 	} else {
254*0a110d5bSKonstantin Belousov 		unit = dmar_find(src);
255*0a110d5bSKonstantin Belousov 		if (unit != NULL && rid != NULL)
256*0a110d5bSKonstantin Belousov 			dmar_get_requester(src, rid);
257*0a110d5bSKonstantin Belousov 	}
258*0a110d5bSKonstantin Belousov 	return (unit);
259*0a110d5bSKonstantin Belousov }
260*0a110d5bSKonstantin Belousov 
261*0a110d5bSKonstantin Belousov static void
262*0a110d5bSKonstantin Belousov dmar_ir_program_irte(struct dmar_unit *unit, u_int idx, uint64_t low,
263*0a110d5bSKonstantin Belousov     uint16_t rid)
264*0a110d5bSKonstantin Belousov {
265*0a110d5bSKonstantin Belousov 	dmar_irte_t *irte;
266*0a110d5bSKonstantin Belousov 	uint64_t high;
267*0a110d5bSKonstantin Belousov 
268*0a110d5bSKonstantin Belousov 	KASSERT(idx < unit->irte_cnt,
269*0a110d5bSKonstantin Belousov 	    ("bad cookie %d %d", idx, unit->irte_cnt));
270*0a110d5bSKonstantin Belousov 	irte = &(unit->irt[idx]);
271*0a110d5bSKonstantin Belousov 	high = DMAR_IRTE2_SVT_RID | DMAR_IRTE2_SQ_RID |
272*0a110d5bSKonstantin Belousov 	    DMAR_IRTE2_SID_RID(rid);
273*0a110d5bSKonstantin Belousov 	device_printf(unit->dev,
274*0a110d5bSKonstantin Belousov 	    "programming irte[%d] rid %#x high %#jx low %#jx\n",
275*0a110d5bSKonstantin Belousov 	    idx, rid, (uintmax_t)high, (uintmax_t)low);
276*0a110d5bSKonstantin Belousov 	DMAR_LOCK(unit);
277*0a110d5bSKonstantin Belousov 	if ((irte->irte1 & DMAR_IRTE1_P) != 0) {
278*0a110d5bSKonstantin Belousov 		/*
279*0a110d5bSKonstantin Belousov 		 * The rte is already valid.  Assume that the request
280*0a110d5bSKonstantin Belousov 		 * is to remap the interrupt for balancing.  Only low
281*0a110d5bSKonstantin Belousov 		 * word of rte needs to be changed.  Assert that the
282*0a110d5bSKonstantin Belousov 		 * high word contains expected value.
283*0a110d5bSKonstantin Belousov 		 */
284*0a110d5bSKonstantin Belousov 		KASSERT(irte->irte2 == high,
285*0a110d5bSKonstantin Belousov 		    ("irte2 mismatch, %jx %jx", (uintmax_t)irte->irte2,
286*0a110d5bSKonstantin Belousov 		    (uintmax_t)high));
287*0a110d5bSKonstantin Belousov 		dmar_pte_update(&irte->irte1, low);
288*0a110d5bSKonstantin Belousov 	} else {
289*0a110d5bSKonstantin Belousov 		dmar_pte_store(&irte->irte2, high);
290*0a110d5bSKonstantin Belousov 		dmar_pte_store(&irte->irte1, low);
291*0a110d5bSKonstantin Belousov 	}
292*0a110d5bSKonstantin Belousov 	dmar_qi_invalidate_iec(unit, idx, 1);
293*0a110d5bSKonstantin Belousov 	DMAR_UNLOCK(unit);
294*0a110d5bSKonstantin Belousov 
295*0a110d5bSKonstantin Belousov }
296*0a110d5bSKonstantin Belousov 
297*0a110d5bSKonstantin Belousov static int
298*0a110d5bSKonstantin Belousov dmar_ir_free_irte(struct dmar_unit *unit, u_int cookie)
299*0a110d5bSKonstantin Belousov {
300*0a110d5bSKonstantin Belousov 	dmar_irte_t *irte;
301*0a110d5bSKonstantin Belousov 
302*0a110d5bSKonstantin Belousov 	KASSERT(unit != NULL && unit->ir_enabled,
303*0a110d5bSKonstantin Belousov 	    ("unmap: cookie %d unit %p", cookie, unit));
304*0a110d5bSKonstantin Belousov 	KASSERT(cookie < unit->irte_cnt,
305*0a110d5bSKonstantin Belousov 	    ("bad cookie %u %u", cookie, unit->irte_cnt));
306*0a110d5bSKonstantin Belousov 	irte = &(unit->irt[cookie]);
307*0a110d5bSKonstantin Belousov 	dmar_pte_clear(&irte->irte1);
308*0a110d5bSKonstantin Belousov 	dmar_pte_clear(&irte->irte2);
309*0a110d5bSKonstantin Belousov 	DMAR_LOCK(unit);
310*0a110d5bSKonstantin Belousov 	dmar_qi_invalidate_iec(unit, cookie, 1);
311*0a110d5bSKonstantin Belousov 	DMAR_UNLOCK(unit);
312*0a110d5bSKonstantin Belousov 	vmem_free(unit->irtids, cookie, 1);
313*0a110d5bSKonstantin Belousov 	return (0);
314*0a110d5bSKonstantin Belousov }
315*0a110d5bSKonstantin Belousov 
316*0a110d5bSKonstantin Belousov static u_int
317*0a110d5bSKonstantin Belousov clp2(u_int v)
318*0a110d5bSKonstantin Belousov {
319*0a110d5bSKonstantin Belousov 
320*0a110d5bSKonstantin Belousov 	return (powerof2(v) ? v : 1 << fls(v));
321*0a110d5bSKonstantin Belousov }
322*0a110d5bSKonstantin Belousov 
323*0a110d5bSKonstantin Belousov int
324*0a110d5bSKonstantin Belousov dmar_init_irt(struct dmar_unit *unit)
325*0a110d5bSKonstantin Belousov {
326*0a110d5bSKonstantin Belousov 
327*0a110d5bSKonstantin Belousov 	if ((unit->hw_ecap & DMAR_ECAP_IR) == 0)
328*0a110d5bSKonstantin Belousov 		return (0);
329*0a110d5bSKonstantin Belousov 	unit->ir_enabled = 1;
330*0a110d5bSKonstantin Belousov 	TUNABLE_INT_FETCH("hw.dmar.ir", &unit->ir_enabled);
331*0a110d5bSKonstantin Belousov 	if (!unit->ir_enabled)
332*0a110d5bSKonstantin Belousov 		return (0);
333*0a110d5bSKonstantin Belousov 	if (!unit->qi_enabled) {
334*0a110d5bSKonstantin Belousov 		unit->ir_enabled = 0;
335*0a110d5bSKonstantin Belousov 		if (bootverbose)
336*0a110d5bSKonstantin Belousov 			device_printf(unit->dev,
337*0a110d5bSKonstantin Belousov 	     "QI disabled, disabling interrupt remapping\n");
338*0a110d5bSKonstantin Belousov 		return (0);
339*0a110d5bSKonstantin Belousov 	}
340*0a110d5bSKonstantin Belousov 	unit->irte_cnt = clp2(NUM_IO_INTS);
341*0a110d5bSKonstantin Belousov 	unit->irt = (dmar_irte_t *)(uintptr_t)kmem_alloc_contig(kernel_arena,
342*0a110d5bSKonstantin Belousov 	    unit->irte_cnt * sizeof(dmar_irte_t), M_ZERO | M_WAITOK, 0,
343*0a110d5bSKonstantin Belousov 	    dmar_high, PAGE_SIZE, 0, DMAR_IS_COHERENT(unit) ?
344*0a110d5bSKonstantin Belousov 	    VM_MEMATTR_DEFAULT : VM_MEMATTR_UNCACHEABLE);
345*0a110d5bSKonstantin Belousov 	if (unit->irt == NULL)
346*0a110d5bSKonstantin Belousov 		return (ENOMEM);
347*0a110d5bSKonstantin Belousov 	unit->irt_phys = pmap_kextract((vm_offset_t)unit->irt);
348*0a110d5bSKonstantin Belousov 	unit->irtids = vmem_create("dmarirt", 0, unit->irte_cnt, 1, 0,
349*0a110d5bSKonstantin Belousov 	    M_FIRSTFIT | M_NOWAIT);
350*0a110d5bSKonstantin Belousov 	DMAR_LOCK(unit);
351*0a110d5bSKonstantin Belousov 	dmar_load_irt_ptr(unit);
352*0a110d5bSKonstantin Belousov 	dmar_qi_invalidate_iec_glob(unit);
353*0a110d5bSKonstantin Belousov 	DMAR_UNLOCK(unit);
354*0a110d5bSKonstantin Belousov 
355*0a110d5bSKonstantin Belousov 	/*
356*0a110d5bSKonstantin Belousov 	 * Initialize mappings for already configured interrupt pins.
357*0a110d5bSKonstantin Belousov 	 * Required, because otherwise the interrupts fault without
358*0a110d5bSKonstantin Belousov 	 * irtes.
359*0a110d5bSKonstantin Belousov 	 */
360*0a110d5bSKonstantin Belousov 	intr_reprogram();
361*0a110d5bSKonstantin Belousov 
362*0a110d5bSKonstantin Belousov 	DMAR_LOCK(unit);
363*0a110d5bSKonstantin Belousov 	dmar_enable_ir(unit);
364*0a110d5bSKonstantin Belousov 	DMAR_UNLOCK(unit);
365*0a110d5bSKonstantin Belousov 	return (0);
366*0a110d5bSKonstantin Belousov }
367*0a110d5bSKonstantin Belousov 
368*0a110d5bSKonstantin Belousov void
369*0a110d5bSKonstantin Belousov dmar_fini_irt(struct dmar_unit *unit)
370*0a110d5bSKonstantin Belousov {
371*0a110d5bSKonstantin Belousov 
372*0a110d5bSKonstantin Belousov 	unit->ir_enabled = 0;
373*0a110d5bSKonstantin Belousov 	if (unit->irt != NULL) {
374*0a110d5bSKonstantin Belousov 		dmar_disable_ir(unit);
375*0a110d5bSKonstantin Belousov 		dmar_qi_invalidate_iec_glob(unit);
376*0a110d5bSKonstantin Belousov 		vmem_destroy(unit->irtids);
377*0a110d5bSKonstantin Belousov 		kmem_free(kernel_arena, (vm_offset_t)unit->irt,
378*0a110d5bSKonstantin Belousov 		    unit->irte_cnt * sizeof(dmar_irte_t));
379*0a110d5bSKonstantin Belousov 	}
380*0a110d5bSKonstantin Belousov }
381