1257251Skib/*-
2280260Skib * Copyright (c) 2013-2015 The FreeBSD Foundation
3257251Skib * All rights reserved.
4257251Skib *
5257251Skib * This software was developed by Konstantin Belousov <kib@FreeBSD.org>
6257251Skib * under sponsorship from the FreeBSD Foundation.
7257251Skib *
8257251Skib * Redistribution and use in source and binary forms, with or without
9257251Skib * modification, are permitted provided that the following conditions
10257251Skib * are met:
11257251Skib * 1. Redistributions of source code must retain the above copyright
12257251Skib *    notice, this list of conditions and the following disclaimer.
13257251Skib * 2. Redistributions in binary form must reproduce the above copyright
14257251Skib *    notice, this list of conditions and the following disclaimer in the
15257251Skib *    documentation and/or other materials provided with the distribution.
16257251Skib *
17257251Skib * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18257251Skib * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19257251Skib * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20257251Skib * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21257251Skib * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22257251Skib * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23257251Skib * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24257251Skib * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25257251Skib * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26257251Skib * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27257251Skib * SUCH DAMAGE.
28257251Skib */
29257251Skib
30257251Skib#include <sys/cdefs.h>
31257251Skib__FBSDID("$FreeBSD: stable/11/sys/x86/iommu/intel_drv.c 323921 2017-09-22 10:51:32Z kib $");
32257251Skib
33257251Skib#include "opt_acpi.h"
34268351Smarcel#if defined(__amd64__)
35257251Skib#define	DEV_APIC
36257251Skib#else
37257251Skib#include "opt_apic.h"
38257251Skib#endif
39257251Skib#include "opt_ddb.h"
40257251Skib
41257251Skib#include <sys/param.h>
42257251Skib#include <sys/bus.h>
43257251Skib#include <sys/kernel.h>
44257251Skib#include <sys/lock.h>
45257251Skib#include <sys/malloc.h>
46257251Skib#include <sys/memdesc.h>
47257251Skib#include <sys/module.h>
48257251Skib#include <sys/rman.h>
49257251Skib#include <sys/rwlock.h>
50257251Skib#include <sys/smp.h>
51257251Skib#include <sys/taskqueue.h>
52257251Skib#include <sys/tree.h>
53280260Skib#include <sys/vmem.h>
54257251Skib#include <machine/bus.h>
55257251Skib#include <contrib/dev/acpica/include/acpi.h>
56257251Skib#include <contrib/dev/acpica/include/accommon.h>
57257251Skib#include <dev/acpica/acpivar.h>
58257251Skib#include <vm/vm.h>
59257251Skib#include <vm/vm_extern.h>
60257251Skib#include <vm/vm_kern.h>
61257251Skib#include <vm/vm_object.h>
62257251Skib#include <vm/vm_page.h>
63257251Skib#include <vm/vm_pager.h>
64257251Skib#include <vm/vm_map.h>
65257251Skib#include <x86/include/busdma_impl.h>
66257251Skib#include <x86/iommu/intel_reg.h>
67257251Skib#include <x86/iommu/busdma_dmar.h>
68257251Skib#include <x86/iommu/intel_dmar.h>
69280260Skib#include <dev/pci/pcireg.h>
70257251Skib#include <dev/pci/pcivar.h>
71257251Skib
72257251Skib#ifdef DEV_APIC
73257251Skib#include "pcib_if.h"
74323921Skib#include <machine/intr_machdep.h>
75323921Skib#include <x86/apicreg.h>
76323921Skib#include <x86/apicvar.h>
77257251Skib#endif
78257251Skib
79257512Skib#define	DMAR_FAULT_IRQ_RID	0
80257512Skib#define	DMAR_QI_IRQ_RID		1
81257512Skib#define	DMAR_REG_RID		2
82257251Skib
83257251Skibstatic devclass_t dmar_devclass;
84257251Skibstatic device_t *dmar_devs;
85257251Skibstatic int dmar_devcnt;
86257251Skib
87257251Skibtypedef int (*dmar_iter_t)(ACPI_DMAR_HEADER *, void *);
88257251Skib
89257251Skibstatic void
90257251Skibdmar_iterate_tbl(dmar_iter_t iter, void *arg)
91257251Skib{
92257251Skib	ACPI_TABLE_DMAR *dmartbl;
93257251Skib	ACPI_DMAR_HEADER *dmarh;
94257251Skib	char *ptr, *ptrend;
95257251Skib	ACPI_STATUS status;
96257251Skib
97257251Skib	status = AcpiGetTable(ACPI_SIG_DMAR, 1, (ACPI_TABLE_HEADER **)&dmartbl);
98257251Skib	if (ACPI_FAILURE(status))
99257251Skib		return;
100257251Skib	ptr = (char *)dmartbl + sizeof(*dmartbl);
101257251Skib	ptrend = (char *)dmartbl + dmartbl->Header.Length;
102257251Skib	for (;;) {
103257251Skib		if (ptr >= ptrend)
104257251Skib			break;
105257251Skib		dmarh = (ACPI_DMAR_HEADER *)ptr;
106257251Skib		if (dmarh->Length <= 0) {
107257251Skib			printf("dmar_identify: corrupted DMAR table, l %d\n",
108257251Skib			    dmarh->Length);
109257251Skib			break;
110257251Skib		}
111257251Skib		ptr += dmarh->Length;
112257251Skib		if (!iter(dmarh, arg))
113257251Skib			break;
114257251Skib	}
115316335Skib	AcpiPutTable((ACPI_TABLE_HEADER *)dmartbl);
116257251Skib}
117257251Skib
118257251Skibstruct find_iter_args {
119257251Skib	int i;
120257251Skib	ACPI_DMAR_HARDWARE_UNIT *res;
121257251Skib};
122257251Skib
123257251Skibstatic int
124257251Skibdmar_find_iter(ACPI_DMAR_HEADER *dmarh, void *arg)
125257251Skib{
126257251Skib	struct find_iter_args *fia;
127257251Skib
128257251Skib	if (dmarh->Type != ACPI_DMAR_TYPE_HARDWARE_UNIT)
129257251Skib		return (1);
130257251Skib
131257251Skib	fia = arg;
132257251Skib	if (fia->i == 0) {
133257251Skib		fia->res = (ACPI_DMAR_HARDWARE_UNIT *)dmarh;
134257251Skib		return (0);
135257251Skib	}
136257251Skib	fia->i--;
137257251Skib	return (1);
138257251Skib}
139257251Skib
140257251Skibstatic ACPI_DMAR_HARDWARE_UNIT *
141257251Skibdmar_find_by_index(int idx)
142257251Skib{
143257251Skib	struct find_iter_args fia;
144257251Skib
145257251Skib	fia.i = idx;
146257251Skib	fia.res = NULL;
147257251Skib	dmar_iterate_tbl(dmar_find_iter, &fia);
148257251Skib	return (fia.res);
149257251Skib}
150257251Skib
151257251Skibstatic int
152257251Skibdmar_count_iter(ACPI_DMAR_HEADER *dmarh, void *arg)
153257251Skib{
154257251Skib
155257251Skib	if (dmarh->Type == ACPI_DMAR_TYPE_HARDWARE_UNIT)
156257251Skib		dmar_devcnt++;
157257251Skib	return (1);
158257251Skib}
159257251Skib
160257251Skibstatic int dmar_enable = 0;
161257251Skibstatic void
162257251Skibdmar_identify(driver_t *driver, device_t parent)
163257251Skib{
164257251Skib	ACPI_TABLE_DMAR *dmartbl;
165257251Skib	ACPI_DMAR_HARDWARE_UNIT *dmarh;
166257251Skib	ACPI_STATUS status;
167257251Skib	int i, error;
168257251Skib
169257251Skib	if (acpi_disabled("dmar"))
170257251Skib		return;
171257251Skib	TUNABLE_INT_FETCH("hw.dmar.enable", &dmar_enable);
172257251Skib	if (!dmar_enable)
173257251Skib		return;
174257251Skib#ifdef INVARIANTS
175257251Skib	TUNABLE_INT_FETCH("hw.dmar.check_free", &dmar_check_free);
176257251Skib#endif
177257251Skib	TUNABLE_INT_FETCH("hw.dmar.match_verbose", &dmar_match_verbose);
178257251Skib	status = AcpiGetTable(ACPI_SIG_DMAR, 1, (ACPI_TABLE_HEADER **)&dmartbl);
179257251Skib	if (ACPI_FAILURE(status))
180257251Skib		return;
181257251Skib	haw = dmartbl->Width + 1;
182257251Skib	if ((1ULL << (haw + 1)) > BUS_SPACE_MAXADDR)
183257251Skib		dmar_high = BUS_SPACE_MAXADDR;
184257251Skib	else
185257251Skib		dmar_high = 1ULL << (haw + 1);
186257251Skib	if (bootverbose) {
187257251Skib		printf("DMAR HAW=%d flags=<%b>\n", dmartbl->Width,
188257251Skib		    (unsigned)dmartbl->Flags,
189257251Skib		    "\020\001INTR_REMAP\002X2APIC_OPT_OUT");
190257251Skib	}
191316335Skib	AcpiPutTable((ACPI_TABLE_HEADER *)dmartbl);
192257251Skib
193257251Skib	dmar_iterate_tbl(dmar_count_iter, NULL);
194257251Skib	if (dmar_devcnt == 0)
195257251Skib		return;
196257251Skib	dmar_devs = malloc(sizeof(device_t) * dmar_devcnt, M_DEVBUF,
197257251Skib	    M_WAITOK | M_ZERO);
198257251Skib	for (i = 0; i < dmar_devcnt; i++) {
199257251Skib		dmarh = dmar_find_by_index(i);
200257251Skib		if (dmarh == NULL) {
201257251Skib			printf("dmar_identify: cannot find HWUNIT %d\n", i);
202257251Skib			continue;
203257251Skib		}
204257251Skib		dmar_devs[i] = BUS_ADD_CHILD(parent, 1, "dmar", i);
205257251Skib		if (dmar_devs[i] == NULL) {
206257251Skib			printf("dmar_identify: cannot create instance %d\n", i);
207257251Skib			continue;
208257251Skib		}
209257251Skib		error = bus_set_resource(dmar_devs[i], SYS_RES_MEMORY,
210257251Skib		    DMAR_REG_RID, dmarh->Address, PAGE_SIZE);
211257251Skib		if (error != 0) {
212257251Skib			printf(
213257251Skib	"dmar%d: unable to alloc register window at 0x%08jx: error %d\n",
214257251Skib			    i, (uintmax_t)dmarh->Address, error);
215257251Skib			device_delete_child(parent, dmar_devs[i]);
216257251Skib			dmar_devs[i] = NULL;
217257251Skib		}
218257251Skib	}
219257251Skib}
220257251Skib
221257251Skibstatic int
222257251Skibdmar_probe(device_t dev)
223257251Skib{
224257251Skib
225257251Skib	if (acpi_get_handle(dev) != NULL)
226257251Skib		return (ENXIO);
227257251Skib	device_set_desc(dev, "DMA remap");
228257511Skib	return (BUS_PROBE_NOWILDCARD);
229257251Skib}
230257251Skib
231257251Skibstatic void
232257512Skibdmar_release_intr(device_t dev, struct dmar_unit *unit, int idx)
233257512Skib{
234257512Skib	struct dmar_msi_data *dmd;
235257512Skib
236257512Skib	dmd = &unit->intrs[idx];
237257512Skib	if (dmd->irq == -1)
238257512Skib		return;
239257512Skib	bus_teardown_intr(dev, dmd->irq_res, dmd->intr_handle);
240257512Skib	bus_release_resource(dev, SYS_RES_IRQ, dmd->irq_rid, dmd->irq_res);
241257512Skib	bus_delete_resource(dev, SYS_RES_IRQ, dmd->irq_rid);
242257512Skib	PCIB_RELEASE_MSIX(device_get_parent(device_get_parent(dev)),
243257512Skib	    dev, dmd->irq);
244257512Skib	dmd->irq = -1;
245257512Skib}
246257512Skib
247257512Skibstatic void
248257251Skibdmar_release_resources(device_t dev, struct dmar_unit *unit)
249257251Skib{
250257512Skib	int i;
251257251Skib
252257251Skib	dmar_fini_busdma(unit);
253280260Skib	dmar_fini_irt(unit);
254257512Skib	dmar_fini_qi(unit);
255257251Skib	dmar_fini_fault_log(unit);
256257512Skib	for (i = 0; i < DMAR_INTR_TOTAL; i++)
257257512Skib		dmar_release_intr(dev, unit, i);
258257251Skib	if (unit->regs != NULL) {
259257251Skib		bus_deactivate_resource(dev, SYS_RES_MEMORY, unit->reg_rid,
260257251Skib		    unit->regs);
261257251Skib		bus_release_resource(dev, SYS_RES_MEMORY, unit->reg_rid,
262257251Skib		    unit->regs);
263257251Skib		unit->regs = NULL;
264257251Skib	}
265257251Skib	if (unit->domids != NULL) {
266257251Skib		delete_unrhdr(unit->domids);
267257251Skib		unit->domids = NULL;
268257251Skib	}
269257251Skib	if (unit->ctx_obj != NULL) {
270257251Skib		vm_object_deallocate(unit->ctx_obj);
271257251Skib		unit->ctx_obj = NULL;
272257251Skib	}
273257251Skib}
274257251Skib
275257251Skibstatic int
276257512Skibdmar_alloc_irq(device_t dev, struct dmar_unit *unit, int idx)
277257251Skib{
278257251Skib	device_t pcib;
279257512Skib	struct dmar_msi_data *dmd;
280257251Skib	uint64_t msi_addr;
281257251Skib	uint32_t msi_data;
282257251Skib	int error;
283257251Skib
284257512Skib	dmd = &unit->intrs[idx];
285257251Skib	pcib = device_get_parent(device_get_parent(dev)); /* Really not pcib */
286257512Skib	error = PCIB_ALLOC_MSIX(pcib, dev, &dmd->irq);
287257251Skib	if (error != 0) {
288257512Skib		device_printf(dev, "cannot allocate %s interrupt, %d\n",
289257512Skib		    dmd->name, error);
290257251Skib		goto err1;
291257251Skib	}
292257512Skib	error = bus_set_resource(dev, SYS_RES_IRQ, dmd->irq_rid,
293257512Skib	    dmd->irq, 1);
294257251Skib	if (error != 0) {
295257512Skib		device_printf(dev, "cannot set %s interrupt resource, %d\n",
296257512Skib		    dmd->name, error);
297257251Skib		goto err2;
298257251Skib	}
299257512Skib	dmd->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
300257512Skib	    &dmd->irq_rid, RF_ACTIVE);
301257512Skib	if (dmd->irq_res == NULL) {
302257512Skib		device_printf(dev,
303257512Skib		    "cannot allocate resource for %s interrupt\n", dmd->name);
304257251Skib		error = ENXIO;
305257251Skib		goto err3;
306257251Skib	}
307257512Skib	error = bus_setup_intr(dev, dmd->irq_res, INTR_TYPE_MISC,
308257512Skib	    dmd->handler, NULL, unit, &dmd->intr_handle);
309257251Skib	if (error != 0) {
310257512Skib		device_printf(dev, "cannot setup %s interrupt, %d\n",
311257512Skib		    dmd->name, error);
312257251Skib		goto err4;
313257251Skib	}
314306770Sjhb	bus_describe_intr(dev, dmd->irq_res, dmd->intr_handle, "%s", dmd->name);
315257512Skib	error = PCIB_MAP_MSI(pcib, dev, dmd->irq, &msi_addr, &msi_data);
316257251Skib	if (error != 0) {
317257512Skib		device_printf(dev, "cannot map %s interrupt, %d\n",
318257512Skib		    dmd->name, error);
319257251Skib		goto err5;
320257251Skib	}
321257512Skib	dmar_write4(unit, dmd->msi_data_reg, msi_data);
322257512Skib	dmar_write4(unit, dmd->msi_addr_reg, msi_addr);
323257251Skib	/* Only for xAPIC mode */
324257512Skib	dmar_write4(unit, dmd->msi_uaddr_reg, msi_addr >> 32);
325257251Skib	return (0);
326257251Skib
327257251Skiberr5:
328257512Skib	bus_teardown_intr(dev, dmd->irq_res, dmd->intr_handle);
329257251Skiberr4:
330257512Skib	bus_release_resource(dev, SYS_RES_IRQ, dmd->irq_rid, dmd->irq_res);
331257251Skiberr3:
332257512Skib	bus_delete_resource(dev, SYS_RES_IRQ, dmd->irq_rid);
333257251Skiberr2:
334257512Skib	PCIB_RELEASE_MSIX(pcib, dev, dmd->irq);
335257512Skib	dmd->irq = -1;
336257251Skiberr1:
337257251Skib	return (error);
338257251Skib}
339257251Skib
340257251Skib#ifdef DEV_APIC
341257251Skibstatic int
342257251Skibdmar_remap_intr(device_t dev, device_t child, u_int irq)
343257251Skib{
344257251Skib	struct dmar_unit *unit;
345257512Skib	struct dmar_msi_data *dmd;
346257251Skib	uint64_t msi_addr;
347257251Skib	uint32_t msi_data;
348257512Skib	int i, error;
349257251Skib
350257251Skib	unit = device_get_softc(dev);
351257512Skib	for (i = 0; i < DMAR_INTR_TOTAL; i++) {
352257512Skib		dmd = &unit->intrs[i];
353257512Skib		if (irq == dmd->irq) {
354257512Skib			error = PCIB_MAP_MSI(device_get_parent(
355257512Skib			    device_get_parent(dev)),
356257512Skib			    dev, irq, &msi_addr, &msi_data);
357257512Skib			if (error != 0)
358257512Skib				return (error);
359257512Skib			DMAR_LOCK(unit);
360257512Skib			(dmd->disable_intr)(unit);
361257512Skib			dmar_write4(unit, dmd->msi_data_reg, msi_data);
362257512Skib			dmar_write4(unit, dmd->msi_addr_reg, msi_addr);
363257512Skib			dmar_write4(unit, dmd->msi_uaddr_reg, msi_addr >> 32);
364257512Skib			(dmd->enable_intr)(unit);
365257512Skib			DMAR_UNLOCK(unit);
366257512Skib			return (0);
367257512Skib		}
368257512Skib	}
369257512Skib	return (ENOENT);
370257251Skib}
371257251Skib#endif
372257251Skib
373257251Skibstatic void
374257251Skibdmar_print_caps(device_t dev, struct dmar_unit *unit,
375257251Skib    ACPI_DMAR_HARDWARE_UNIT *dmaru)
376257251Skib{
377257251Skib	uint32_t caphi, ecaphi;
378257251Skib
379257251Skib	device_printf(dev, "regs@0x%08jx, ver=%d.%d, seg=%d, flags=<%b>\n",
380257251Skib	    (uintmax_t)dmaru->Address, DMAR_MAJOR_VER(unit->hw_ver),
381257251Skib	    DMAR_MINOR_VER(unit->hw_ver), dmaru->Segment,
382257251Skib	    dmaru->Flags, "\020\001INCLUDE_ALL_PCI");
383257251Skib	caphi = unit->hw_cap >> 32;
384257251Skib	device_printf(dev, "cap=%b,", (u_int)unit->hw_cap,
385257251Skib	    "\020\004AFL\005WBF\006PLMR\007PHMR\010CM\027ZLR\030ISOCH");
386278606Skib	printf("%b, ", caphi, "\020\010PSI\027DWD\030DRD\031FL1GP\034PSI");
387257251Skib	printf("ndoms=%d, sagaw=%d, mgaw=%d, fro=%d, nfr=%d, superp=%d",
388257251Skib	    DMAR_CAP_ND(unit->hw_cap), DMAR_CAP_SAGAW(unit->hw_cap),
389257251Skib	    DMAR_CAP_MGAW(unit->hw_cap), DMAR_CAP_FRO(unit->hw_cap),
390257251Skib	    DMAR_CAP_NFR(unit->hw_cap), DMAR_CAP_SPS(unit->hw_cap));
391257251Skib	if ((unit->hw_cap & DMAR_CAP_PSI) != 0)
392257251Skib		printf(", mamv=%d", DMAR_CAP_MAMV(unit->hw_cap));
393257251Skib	printf("\n");
394257251Skib	ecaphi = unit->hw_ecap >> 32;
395257251Skib	device_printf(dev, "ecap=%b,", (u_int)unit->hw_ecap,
396278606Skib	    "\020\001C\002QI\003DI\004IR\005EIM\007PT\010SC\031ECS\032MTS"
397278606Skib	    "\033NEST\034DIS\035PASID\036PRS\037ERS\040SRS");
398278606Skib	printf("%b, ", ecaphi, "\020\002NWFS\003EAFS");
399257251Skib	printf("mhmw=%d, iro=%d\n", DMAR_ECAP_MHMV(unit->hw_ecap),
400257251Skib	    DMAR_ECAP_IRO(unit->hw_ecap));
401257251Skib}
402257251Skib
403257251Skibstatic int
404257251Skibdmar_attach(device_t dev)
405257251Skib{
406257251Skib	struct dmar_unit *unit;
407257251Skib	ACPI_DMAR_HARDWARE_UNIT *dmaru;
408316449Skib	uint64_t timeout;
409257512Skib	int i, error;
410257251Skib
411257251Skib	unit = device_get_softc(dev);
412257251Skib	unit->dev = dev;
413257251Skib	unit->unit = device_get_unit(dev);
414257251Skib	dmaru = dmar_find_by_index(unit->unit);
415257251Skib	if (dmaru == NULL)
416257251Skib		return (EINVAL);
417257251Skib	unit->segment = dmaru->Segment;
418257251Skib	unit->base = dmaru->Address;
419257251Skib	unit->reg_rid = DMAR_REG_RID;
420257251Skib	unit->regs = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
421257251Skib	    &unit->reg_rid, RF_ACTIVE);
422257251Skib	if (unit->regs == NULL) {
423257251Skib		device_printf(dev, "cannot allocate register window\n");
424257251Skib		return (ENOMEM);
425257251Skib	}
426257251Skib	unit->hw_ver = dmar_read4(unit, DMAR_VER_REG);
427257251Skib	unit->hw_cap = dmar_read8(unit, DMAR_CAP_REG);
428257251Skib	unit->hw_ecap = dmar_read8(unit, DMAR_ECAP_REG);
429257251Skib	if (bootverbose)
430257251Skib		dmar_print_caps(dev, unit, dmaru);
431257251Skib	dmar_quirks_post_ident(unit);
432257251Skib
433316449Skib	timeout = dmar_get_timeout();
434316449Skib	TUNABLE_UINT64_FETCH("hw.dmar.timeout", &timeout);
435316449Skib	dmar_update_timeout(timeout);
436316449Skib
437257512Skib	for (i = 0; i < DMAR_INTR_TOTAL; i++)
438257512Skib		unit->intrs[i].irq = -1;
439257512Skib
440257512Skib	unit->intrs[DMAR_INTR_FAULT].name = "fault";
441257512Skib	unit->intrs[DMAR_INTR_FAULT].irq_rid = DMAR_FAULT_IRQ_RID;
442257512Skib	unit->intrs[DMAR_INTR_FAULT].handler = dmar_fault_intr;
443257512Skib	unit->intrs[DMAR_INTR_FAULT].msi_data_reg = DMAR_FEDATA_REG;
444257512Skib	unit->intrs[DMAR_INTR_FAULT].msi_addr_reg = DMAR_FEADDR_REG;
445257512Skib	unit->intrs[DMAR_INTR_FAULT].msi_uaddr_reg = DMAR_FEUADDR_REG;
446257512Skib	unit->intrs[DMAR_INTR_FAULT].enable_intr = dmar_enable_fault_intr;
447257512Skib	unit->intrs[DMAR_INTR_FAULT].disable_intr = dmar_disable_fault_intr;
448257512Skib	error = dmar_alloc_irq(dev, unit, DMAR_INTR_FAULT);
449257251Skib	if (error != 0) {
450257251Skib		dmar_release_resources(dev, unit);
451257251Skib		return (error);
452257251Skib	}
453257512Skib	if (DMAR_HAS_QI(unit)) {
454257512Skib		unit->intrs[DMAR_INTR_QI].name = "qi";
455257512Skib		unit->intrs[DMAR_INTR_QI].irq_rid = DMAR_QI_IRQ_RID;
456257512Skib		unit->intrs[DMAR_INTR_QI].handler = dmar_qi_intr;
457257512Skib		unit->intrs[DMAR_INTR_QI].msi_data_reg = DMAR_IEDATA_REG;
458257512Skib		unit->intrs[DMAR_INTR_QI].msi_addr_reg = DMAR_IEADDR_REG;
459257512Skib		unit->intrs[DMAR_INTR_QI].msi_uaddr_reg = DMAR_IEUADDR_REG;
460257512Skib		unit->intrs[DMAR_INTR_QI].enable_intr = dmar_enable_qi_intr;
461257512Skib		unit->intrs[DMAR_INTR_QI].disable_intr = dmar_disable_qi_intr;
462257512Skib		error = dmar_alloc_irq(dev, unit, DMAR_INTR_QI);
463257512Skib		if (error != 0) {
464257512Skib			dmar_release_resources(dev, unit);
465257512Skib			return (error);
466257512Skib		}
467257512Skib	}
468257512Skib
469257251Skib	mtx_init(&unit->lock, "dmarhw", NULL, MTX_DEF);
470257251Skib	unit->domids = new_unrhdr(0, dmar_nd2mask(DMAR_CAP_ND(unit->hw_cap)),
471257251Skib	    &unit->lock);
472284869Skib	LIST_INIT(&unit->domains);
473257251Skib
474257251Skib	/*
475257251Skib	 * 9.2 "Context Entry":
476257251Skib	 * When Caching Mode (CM) field is reported as Set, the
477257251Skib	 * domain-id value of zero is architecturally reserved.
478257251Skib	 * Software must not use domain-id value of zero
479257251Skib	 * when CM is Set.
480257251Skib	 */
481257251Skib	if ((unit->hw_cap & DMAR_CAP_CM) != 0)
482257251Skib		alloc_unr_specific(unit->domids, 0);
483257251Skib
484257251Skib	unit->ctx_obj = vm_pager_allocate(OBJT_PHYS, NULL, IDX_TO_OFF(1 +
485257251Skib	    DMAR_CTX_CNT), 0, 0, NULL);
486257251Skib
487257251Skib	/*
488257251Skib	 * Allocate and load the root entry table pointer.  Enable the
489257251Skib	 * address translation after the required invalidations are
490257251Skib	 * done.
491257251Skib	 */
492257251Skib	dmar_pgalloc(unit->ctx_obj, 0, DMAR_PGF_WAITOK | DMAR_PGF_ZERO);
493257251Skib	DMAR_LOCK(unit);
494257251Skib	error = dmar_load_root_entry_ptr(unit);
495257251Skib	if (error != 0) {
496257251Skib		DMAR_UNLOCK(unit);
497257251Skib		dmar_release_resources(dev, unit);
498257251Skib		return (error);
499257251Skib	}
500257251Skib	error = dmar_inv_ctx_glob(unit);
501257251Skib	if (error != 0) {
502257251Skib		DMAR_UNLOCK(unit);
503257251Skib		dmar_release_resources(dev, unit);
504257251Skib		return (error);
505257251Skib	}
506257251Skib	if ((unit->hw_ecap & DMAR_ECAP_DI) != 0) {
507257251Skib		error = dmar_inv_iotlb_glob(unit);
508257251Skib		if (error != 0) {
509257251Skib			DMAR_UNLOCK(unit);
510257251Skib			dmar_release_resources(dev, unit);
511257251Skib			return (error);
512257251Skib		}
513257251Skib	}
514257251Skib
515257251Skib	DMAR_UNLOCK(unit);
516257251Skib	error = dmar_init_fault_log(unit);
517257251Skib	if (error != 0) {
518257251Skib		dmar_release_resources(dev, unit);
519257251Skib		return (error);
520257251Skib	}
521257512Skib	error = dmar_init_qi(unit);
522257512Skib	if (error != 0) {
523257512Skib		dmar_release_resources(dev, unit);
524257512Skib		return (error);
525257512Skib	}
526280260Skib	error = dmar_init_irt(unit);
527280260Skib	if (error != 0) {
528280260Skib		dmar_release_resources(dev, unit);
529280260Skib		return (error);
530280260Skib	}
531257251Skib	error = dmar_init_busdma(unit);
532257251Skib	if (error != 0) {
533257251Skib		dmar_release_resources(dev, unit);
534257251Skib		return (error);
535257251Skib	}
536257251Skib
537257251Skib#ifdef NOTYET
538257251Skib	DMAR_LOCK(unit);
539257251Skib	error = dmar_enable_translation(unit);
540257251Skib	if (error != 0) {
541257251Skib		DMAR_UNLOCK(unit);
542257251Skib		dmar_release_resources(dev, unit);
543257251Skib		return (error);
544257251Skib	}
545257251Skib	DMAR_UNLOCK(unit);
546257251Skib#endif
547257251Skib
548257251Skib	return (0);
549257251Skib}
550257251Skib
551257251Skibstatic int
552257251Skibdmar_detach(device_t dev)
553257251Skib{
554257251Skib
555257251Skib	return (EBUSY);
556257251Skib}
557257251Skib
558257251Skibstatic int
559257251Skibdmar_suspend(device_t dev)
560257251Skib{
561257251Skib
562257251Skib	return (0);
563257251Skib}
564257251Skib
565257251Skibstatic int
566257251Skibdmar_resume(device_t dev)
567257251Skib{
568257251Skib
569257251Skib	/* XXXKIB */
570257251Skib	return (0);
571257251Skib}
572257251Skib
573257251Skibstatic device_method_t dmar_methods[] = {
574257251Skib	DEVMETHOD(device_identify, dmar_identify),
575257251Skib	DEVMETHOD(device_probe, dmar_probe),
576257251Skib	DEVMETHOD(device_attach, dmar_attach),
577257251Skib	DEVMETHOD(device_detach, dmar_detach),
578257251Skib	DEVMETHOD(device_suspend, dmar_suspend),
579257251Skib	DEVMETHOD(device_resume, dmar_resume),
580257251Skib#ifdef DEV_APIC
581257251Skib	DEVMETHOD(bus_remap_intr, dmar_remap_intr),
582257251Skib#endif
583257251Skib	DEVMETHOD_END
584257251Skib};
585257251Skib
586257251Skibstatic driver_t	dmar_driver = {
587257251Skib	"dmar",
588257251Skib	dmar_methods,
589257251Skib	sizeof(struct dmar_unit),
590257251Skib};
591257251Skib
592257251SkibDRIVER_MODULE(dmar, acpi, dmar_driver, dmar_devclass, 0, 0);
593257251SkibMODULE_DEPEND(dmar, acpi, 1, 1, 1);
594257251Skib
595257251Skibstatic void
596257251Skibdmar_print_path(device_t dev, const char *banner, int busno, int depth,
597257251Skib    const ACPI_DMAR_PCI_PATH *path)
598257251Skib{
599257251Skib	int i;
600257251Skib
601257251Skib	device_printf(dev, "%s [%d, ", banner, busno);
602257251Skib	for (i = 0; i < depth; i++) {
603257251Skib		if (i != 0)
604257251Skib			printf(", ");
605257251Skib		printf("(%d, %d)", path[i].Device, path[i].Function);
606257251Skib	}
607257251Skib	printf("]\n");
608257251Skib}
609257251Skib
610257251Skibstatic int
611257251Skibdmar_dev_depth(device_t child)
612257251Skib{
613257251Skib	devclass_t pci_class;
614257251Skib	device_t bus, pcib;
615257251Skib	int depth;
616257251Skib
617257251Skib	pci_class = devclass_find("pci");
618257251Skib	for (depth = 1; ; depth++) {
619257251Skib		bus = device_get_parent(child);
620257251Skib		pcib = device_get_parent(bus);
621257251Skib		if (device_get_devclass(device_get_parent(pcib)) !=
622257251Skib		    pci_class)
623257251Skib			return (depth);
624257251Skib		child = pcib;
625257251Skib	}
626257251Skib}
627257251Skib
628257251Skibstatic void
629257251Skibdmar_dev_path(device_t child, int *busno, ACPI_DMAR_PCI_PATH *path, int depth)
630257251Skib{
631257251Skib	devclass_t pci_class;
632257251Skib	device_t bus, pcib;
633257251Skib
634257251Skib	pci_class = devclass_find("pci");
635257251Skib	for (depth--; depth != -1; depth--) {
636257251Skib		path[depth].Device = pci_get_slot(child);
637257251Skib		path[depth].Function = pci_get_function(child);
638257251Skib		bus = device_get_parent(child);
639257251Skib		pcib = device_get_parent(bus);
640257251Skib		if (device_get_devclass(device_get_parent(pcib)) !=
641257251Skib		    pci_class) {
642257251Skib			/* reached a host bridge */
643257251Skib			*busno = pcib_get_bus(bus);
644257251Skib			return;
645257251Skib		}
646257251Skib		child = pcib;
647257251Skib	}
648257251Skib	panic("wrong depth");
649257251Skib}
650257251Skib
651257251Skibstatic int
652257251Skibdmar_match_pathes(int busno1, const ACPI_DMAR_PCI_PATH *path1, int depth1,
653257251Skib    int busno2, const ACPI_DMAR_PCI_PATH *path2, int depth2,
654257251Skib    enum AcpiDmarScopeType scope_type)
655257251Skib{
656257251Skib	int i, depth;
657257251Skib
658257251Skib	if (busno1 != busno2)
659257251Skib		return (0);
660257251Skib	if (scope_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT && depth1 != depth2)
661257251Skib		return (0);
662257251Skib	depth = depth1;
663257251Skib	if (depth2 < depth)
664257251Skib		depth = depth2;
665257251Skib	for (i = 0; i < depth; i++) {
666257251Skib		if (path1[i].Device != path2[i].Device ||
667257251Skib		    path1[i].Function != path2[i].Function)
668257251Skib			return (0);
669257251Skib	}
670257251Skib	return (1);
671257251Skib}
672257251Skib
673257251Skibstatic int
674257251Skibdmar_match_devscope(ACPI_DMAR_DEVICE_SCOPE *devscope, device_t dev,
675257251Skib    int dev_busno, const ACPI_DMAR_PCI_PATH *dev_path, int dev_path_len)
676257251Skib{
677257251Skib	ACPI_DMAR_PCI_PATH *path;
678257251Skib	int path_len;
679257251Skib
680257251Skib	if (devscope->Length < sizeof(*devscope)) {
681257251Skib		printf("dmar_find: corrupted DMAR table, dl %d\n",
682257251Skib		    devscope->Length);
683257251Skib		return (-1);
684257251Skib	}
685257251Skib	if (devscope->EntryType != ACPI_DMAR_SCOPE_TYPE_ENDPOINT &&
686257251Skib	    devscope->EntryType != ACPI_DMAR_SCOPE_TYPE_BRIDGE)
687257251Skib		return (0);
688257251Skib	path_len = devscope->Length - sizeof(*devscope);
689257251Skib	if (path_len % 2 != 0) {
690257251Skib		printf("dmar_find_bsf: corrupted DMAR table, dl %d\n",
691257251Skib		    devscope->Length);
692257251Skib		return (-1);
693257251Skib	}
694257251Skib	path_len /= 2;
695257251Skib	path = (ACPI_DMAR_PCI_PATH *)(devscope + 1);
696257251Skib	if (path_len == 0) {
697257251Skib		printf("dmar_find: corrupted DMAR table, dl %d\n",
698257251Skib		    devscope->Length);
699257251Skib		return (-1);
700257251Skib	}
701257251Skib	if (dmar_match_verbose)
702257251Skib		dmar_print_path(dev, "DMAR", devscope->Bus, path_len, path);
703257251Skib
704257251Skib	return (dmar_match_pathes(devscope->Bus, path, path_len, dev_busno,
705257251Skib	    dev_path, dev_path_len, devscope->EntryType));
706257251Skib}
707257251Skib
708257251Skibstruct dmar_unit *
709257251Skibdmar_find(device_t dev)
710257251Skib{
711257251Skib	device_t dmar_dev;
712257251Skib	ACPI_DMAR_HARDWARE_UNIT *dmarh;
713257251Skib	ACPI_DMAR_DEVICE_SCOPE *devscope;
714257251Skib	char *ptr, *ptrend;
715257251Skib	int i, match, dev_domain, dev_busno, dev_path_len;
716257251Skib
717257251Skib	dmar_dev = NULL;
718257251Skib	dev_domain = pci_get_domain(dev);
719257251Skib	dev_path_len = dmar_dev_depth(dev);
720257251Skib	ACPI_DMAR_PCI_PATH dev_path[dev_path_len];
721257251Skib	dmar_dev_path(dev, &dev_busno, dev_path, dev_path_len);
722257251Skib	if (dmar_match_verbose)
723257251Skib		dmar_print_path(dev, "PCI", dev_busno, dev_path_len, dev_path);
724257251Skib
725257251Skib	for (i = 0; i < dmar_devcnt; i++) {
726257251Skib		if (dmar_devs[i] == NULL)
727257251Skib			continue;
728257251Skib		dmarh = dmar_find_by_index(i);
729257251Skib		if (dmarh == NULL)
730257251Skib			continue;
731257251Skib		if (dmarh->Segment != dev_domain)
732257251Skib			continue;
733257251Skib		if ((dmarh->Flags & ACPI_DMAR_INCLUDE_ALL) != 0) {
734257251Skib			dmar_dev = dmar_devs[i];
735257251Skib			if (dmar_match_verbose) {
736257251Skib				device_printf(dev,
737257251Skib				    "pci%d:%d:%d:%d matched dmar%d INCLUDE_ALL\n",
738257251Skib				    dev_domain, pci_get_bus(dev),
739257251Skib				    pci_get_slot(dev),
740257251Skib				    pci_get_function(dev),
741257251Skib				    ((struct dmar_unit *)device_get_softc(
742257251Skib				    dmar_dev))->unit);
743257251Skib			}
744257251Skib			goto found;
745257251Skib		}
746257251Skib		ptr = (char *)dmarh + sizeof(*dmarh);
747257251Skib		ptrend = (char *)dmarh + dmarh->Header.Length;
748257251Skib		for (;;) {
749257251Skib			if (ptr >= ptrend)
750257251Skib				break;
751257251Skib			devscope = (ACPI_DMAR_DEVICE_SCOPE *)ptr;
752257251Skib			ptr += devscope->Length;
753257251Skib			if (dmar_match_verbose) {
754257251Skib				device_printf(dev,
755257251Skib				    "pci%d:%d:%d:%d matching dmar%d\n",
756257251Skib				    dev_domain, pci_get_bus(dev),
757257251Skib				    pci_get_slot(dev),
758257251Skib				    pci_get_function(dev),
759257251Skib				    ((struct dmar_unit *)device_get_softc(
760257251Skib				    dmar_devs[i]))->unit);
761257251Skib			}
762257251Skib			match = dmar_match_devscope(devscope, dev, dev_busno,
763257251Skib			    dev_path, dev_path_len);
764257251Skib			if (dmar_match_verbose) {
765257251Skib				if (match == -1)
766257251Skib					printf("table error\n");
767257251Skib				else if (match == 0)
768257251Skib					printf("not matched\n");
769257251Skib				else
770257251Skib					printf("matched\n");
771257251Skib			}
772257251Skib			if (match == -1)
773257251Skib				return (NULL);
774257251Skib			else if (match == 1) {
775257251Skib				dmar_dev = dmar_devs[i];
776257251Skib				goto found;
777257251Skib			}
778257251Skib		}
779257251Skib	}
780257251Skib	return (NULL);
781257251Skibfound:
782257251Skib	return (device_get_softc(dmar_dev));
783257251Skib}
784257251Skib
785280260Skibstatic struct dmar_unit *
786280260Skibdmar_find_nonpci(u_int id, u_int entry_type, uint16_t *rid)
787280260Skib{
788280260Skib	device_t dmar_dev;
789280260Skib	struct dmar_unit *unit;
790280260Skib	ACPI_DMAR_HARDWARE_UNIT *dmarh;
791280260Skib	ACPI_DMAR_DEVICE_SCOPE *devscope;
792280260Skib	ACPI_DMAR_PCI_PATH *path;
793280260Skib	char *ptr, *ptrend;
794323921Skib#ifdef DEV_APIC
795323921Skib	int error;
796323921Skib#endif
797280260Skib	int i;
798280260Skib
799280260Skib	for (i = 0; i < dmar_devcnt; i++) {
800280260Skib		dmar_dev = dmar_devs[i];
801280260Skib		if (dmar_dev == NULL)
802280260Skib			continue;
803280260Skib		unit = (struct dmar_unit *)device_get_softc(dmar_dev);
804280260Skib		dmarh = dmar_find_by_index(i);
805280260Skib		if (dmarh == NULL)
806280260Skib			continue;
807280260Skib		ptr = (char *)dmarh + sizeof(*dmarh);
808280260Skib		ptrend = (char *)dmarh + dmarh->Header.Length;
809280260Skib		for (;;) {
810280260Skib			if (ptr >= ptrend)
811280260Skib				break;
812280260Skib			devscope = (ACPI_DMAR_DEVICE_SCOPE *)ptr;
813280260Skib			ptr += devscope->Length;
814280260Skib			if (devscope->EntryType != entry_type)
815280260Skib				continue;
816280260Skib			if (devscope->EnumerationId != id)
817280260Skib				continue;
818323921Skib#ifdef DEV_APIC
819323921Skib			if (entry_type == ACPI_DMAR_SCOPE_TYPE_IOAPIC) {
820323921Skib				error = ioapic_get_rid(id, rid);
821323921Skib				/*
822323921Skib				 * If our IOAPIC has PCI bindings then
823323921Skib				 * use the PCI device rid.
824323921Skib				 */
825323921Skib				if (error == 0)
826323921Skib					return (unit);
827323921Skib			}
828323921Skib#endif
829280260Skib			if (devscope->Length - sizeof(ACPI_DMAR_DEVICE_SCOPE)
830280260Skib			    == 2) {
831280260Skib				if (rid != NULL) {
832280260Skib					path = (ACPI_DMAR_PCI_PATH *)
833280260Skib					    (devscope + 1);
834280260Skib					*rid = PCI_RID(devscope->Bus,
835280260Skib					    path->Device, path->Function);
836280260Skib				}
837280260Skib				return (unit);
838280260Skib			}
839323921Skib			printf(
840323921Skib		           "dmar_find_nonpci: id %d type %d path length != 2\n",
841323921Skib			    id, entry_type);
842323921Skib			break;
843280260Skib		}
844280260Skib	}
845280260Skib	return (NULL);
846280260Skib}
847280260Skib
848280260Skib
849280260Skibstruct dmar_unit *
850280260Skibdmar_find_hpet(device_t dev, uint16_t *rid)
851280260Skib{
852280260Skib
853295841Skib	return (dmar_find_nonpci(hpet_get_uid(dev), ACPI_DMAR_SCOPE_TYPE_HPET,
854295841Skib	    rid));
855280260Skib}
856280260Skib
857280260Skibstruct dmar_unit *
858280260Skibdmar_find_ioapic(u_int apic_id, uint16_t *rid)
859280260Skib{
860280260Skib
861280260Skib	return (dmar_find_nonpci(apic_id, ACPI_DMAR_SCOPE_TYPE_IOAPIC, rid));
862280260Skib}
863280260Skib
864257251Skibstruct rmrr_iter_args {
865284869Skib	struct dmar_domain *domain;
866257251Skib	device_t dev;
867257251Skib	int dev_domain;
868257251Skib	int dev_busno;
869257251Skib	ACPI_DMAR_PCI_PATH *dev_path;
870257251Skib	int dev_path_len;
871257251Skib	struct dmar_map_entries_tailq *rmrr_entries;
872257251Skib};
873257251Skib
874257251Skibstatic int
875257251Skibdmar_rmrr_iter(ACPI_DMAR_HEADER *dmarh, void *arg)
876257251Skib{
877257251Skib	struct rmrr_iter_args *ria;
878257251Skib	ACPI_DMAR_RESERVED_MEMORY *resmem;
879257251Skib	ACPI_DMAR_DEVICE_SCOPE *devscope;
880257251Skib	struct dmar_map_entry *entry;
881257251Skib	char *ptr, *ptrend;
882257251Skib	int match;
883257251Skib
884257251Skib	if (dmarh->Type != ACPI_DMAR_TYPE_RESERVED_MEMORY)
885257251Skib		return (1);
886257251Skib
887257251Skib	ria = arg;
888257251Skib	resmem = (ACPI_DMAR_RESERVED_MEMORY *)dmarh;
889257251Skib	if (dmar_match_verbose) {
890257251Skib		printf("RMRR [%jx,%jx] segment %d\n",
891257251Skib		    (uintmax_t)resmem->BaseAddress,
892257251Skib		    (uintmax_t)resmem->EndAddress,
893257251Skib		    resmem->Segment);
894257251Skib	}
895257251Skib	if (resmem->Segment != ria->dev_domain)
896257251Skib		return (1);
897257251Skib
898257251Skib	ptr = (char *)resmem + sizeof(*resmem);
899257251Skib	ptrend = (char *)resmem + resmem->Header.Length;
900257251Skib	for (;;) {
901257251Skib		if (ptr >= ptrend)
902257251Skib			break;
903257251Skib		devscope = (ACPI_DMAR_DEVICE_SCOPE *)ptr;
904257251Skib		ptr += devscope->Length;
905257251Skib		match = dmar_match_devscope(devscope, ria->dev, ria->dev_busno,
906257251Skib		    ria->dev_path, ria->dev_path_len);
907257251Skib		if (match == 1) {
908257251Skib			if (dmar_match_verbose)
909257251Skib				printf("matched\n");
910284869Skib			entry = dmar_gas_alloc_entry(ria->domain,
911284869Skib			    DMAR_PGF_WAITOK);
912257251Skib			entry->start = resmem->BaseAddress;
913257251Skib			/* The RMRR entry end address is inclusive. */
914257251Skib			entry->end = resmem->EndAddress;
915257251Skib			TAILQ_INSERT_TAIL(ria->rmrr_entries, entry,
916257251Skib			    unroll_link);
917257251Skib		} else if (dmar_match_verbose) {
918257251Skib			printf("not matched, err %d\n", match);
919257251Skib		}
920257251Skib	}
921257251Skib
922257251Skib	return (1);
923257251Skib}
924257251Skib
925257251Skibvoid
926284869Skibdmar_dev_parse_rmrr(struct dmar_domain *domain, device_t dev,
927257251Skib    struct dmar_map_entries_tailq *rmrr_entries)
928257251Skib{
929257251Skib	struct rmrr_iter_args ria;
930257251Skib
931257251Skib	ria.dev_domain = pci_get_domain(dev);
932257251Skib	ria.dev_path_len = dmar_dev_depth(dev);
933257251Skib	ACPI_DMAR_PCI_PATH dev_path[ria.dev_path_len];
934257251Skib	dmar_dev_path(dev, &ria.dev_busno, dev_path, ria.dev_path_len);
935257251Skib
936257251Skib	if (dmar_match_verbose) {
937257251Skib		device_printf(dev, "parsing RMRR entries for ");
938257251Skib		dmar_print_path(dev, "PCI", ria.dev_busno, ria.dev_path_len,
939257251Skib		    dev_path);
940257251Skib	}
941257251Skib
942284869Skib	ria.domain = domain;
943257251Skib	ria.dev = dev;
944257251Skib	ria.dev_path = dev_path;
945257251Skib	ria.rmrr_entries = rmrr_entries;
946257251Skib	dmar_iterate_tbl(dmar_rmrr_iter, &ria);
947257251Skib}
948257251Skib
949257251Skibstruct inst_rmrr_iter_args {
950257251Skib	struct dmar_unit *dmar;
951257251Skib};
952257251Skib
953257251Skibstatic device_t
954257251Skibdmar_path_dev(int segment, int path_len, int busno,
955257251Skib    const ACPI_DMAR_PCI_PATH *path)
956257251Skib{
957257251Skib	devclass_t pci_class;
958257251Skib	device_t bus, pcib, dev;
959257251Skib	int i;
960257251Skib
961257251Skib	pci_class = devclass_find("pci");
962257251Skib	dev = NULL;
963257251Skib	for (i = 0; i < path_len; i++, path++) {
964257251Skib		dev = pci_find_dbsf(segment, busno, path->Device,
965257251Skib		    path->Function);
966257251Skib		if (dev == NULL)
967257251Skib			break;
968257251Skib		if (i != path_len - 1) {
969257251Skib			bus = device_get_parent(dev);
970257251Skib			pcib = device_get_parent(bus);
971257251Skib			if (device_get_devclass(device_get_parent(pcib)) !=
972257251Skib			    pci_class)
973257251Skib				return (NULL);
974257251Skib		}
975257251Skib		busno = pcib_get_bus(dev);
976257251Skib	}
977257251Skib	return (dev);
978257251Skib}
979257251Skib
980257251Skibstatic int
981257251Skibdmar_inst_rmrr_iter(ACPI_DMAR_HEADER *dmarh, void *arg)
982257251Skib{
983257251Skib	const ACPI_DMAR_RESERVED_MEMORY *resmem;
984257251Skib	const ACPI_DMAR_DEVICE_SCOPE *devscope;
985257251Skib	struct inst_rmrr_iter_args *iria;
986257251Skib	const char *ptr, *ptrend;
987257251Skib	struct dmar_unit *dev_dmar;
988257251Skib	device_t dev;
989257251Skib
990257251Skib	if (dmarh->Type != ACPI_DMAR_TYPE_RESERVED_MEMORY)
991257251Skib		return (1);
992257251Skib
993257251Skib	iria = arg;
994257251Skib	resmem = (ACPI_DMAR_RESERVED_MEMORY *)dmarh;
995257251Skib	if (resmem->Segment != iria->dmar->segment)
996257251Skib		return (1);
997257251Skib	if (dmar_match_verbose) {
998257251Skib		printf("dmar%d: RMRR [%jx,%jx]\n", iria->dmar->unit,
999257251Skib		    (uintmax_t)resmem->BaseAddress,
1000257251Skib		    (uintmax_t)resmem->EndAddress);
1001257251Skib	}
1002257251Skib
1003257896Sdim	ptr = (const char *)resmem + sizeof(*resmem);
1004257896Sdim	ptrend = (const char *)resmem + resmem->Header.Length;
1005257251Skib	for (;;) {
1006257251Skib		if (ptr >= ptrend)
1007257251Skib			break;
1008257896Sdim		devscope = (const ACPI_DMAR_DEVICE_SCOPE *)ptr;
1009257251Skib		ptr += devscope->Length;
1010257251Skib		/* XXXKIB bridge */
1011257251Skib		if (devscope->EntryType != ACPI_DMAR_SCOPE_TYPE_ENDPOINT)
1012257251Skib			continue;
1013257251Skib		if (dmar_match_verbose) {
1014257251Skib			dmar_print_path(iria->dmar->dev, "RMRR scope",
1015257251Skib			    devscope->Bus, (devscope->Length -
1016257251Skib			    sizeof(ACPI_DMAR_DEVICE_SCOPE)) / 2,
1017257896Sdim			    (const ACPI_DMAR_PCI_PATH *)(devscope + 1));
1018257251Skib		}
1019257251Skib		dev = dmar_path_dev(resmem->Segment, (devscope->Length -
1020257251Skib		    sizeof(ACPI_DMAR_DEVICE_SCOPE)) / 2, devscope->Bus,
1021257896Sdim		    (const ACPI_DMAR_PCI_PATH *)(devscope + 1));
1022257251Skib		if (dev == NULL) {
1023257251Skib			if (dmar_match_verbose)
1024257251Skib				printf("null dev\n");
1025257251Skib			continue;
1026257251Skib		}
1027257251Skib		dev_dmar = dmar_find(dev);
1028257251Skib		if (dev_dmar != iria->dmar) {
1029257251Skib			if (dmar_match_verbose) {
1030257251Skib				printf("dmar%d matched, skipping\n",
1031257251Skib				    dev_dmar->unit);
1032257251Skib			}
1033257251Skib			continue;
1034257251Skib		}
1035257251Skib		if (dmar_match_verbose)
1036257251Skib			printf("matched, instantiating RMRR context\n");
1037257251Skib		dmar_instantiate_ctx(iria->dmar, dev, true);
1038257251Skib	}
1039257251Skib
1040257251Skib	return (1);
1041257251Skib
1042257251Skib}
1043257251Skib
1044257251Skib/*
1045257251Skib * Pre-create all contexts for the DMAR which have RMRR entries.
1046257251Skib */
1047257251Skibint
1048257251Skibdmar_instantiate_rmrr_ctxs(struct dmar_unit *dmar)
1049257251Skib{
1050257251Skib	struct inst_rmrr_iter_args iria;
1051257251Skib	int error;
1052257251Skib
1053257251Skib	if (!dmar_barrier_enter(dmar, DMAR_BARRIER_RMRR))
1054257251Skib		return (0);
1055257251Skib
1056257251Skib	error = 0;
1057257251Skib	iria.dmar = dmar;
1058257251Skib	if (dmar_match_verbose)
1059257251Skib		printf("dmar%d: instantiating RMRR contexts\n", dmar->unit);
1060257251Skib	dmar_iterate_tbl(dmar_inst_rmrr_iter, &iria);
1061257251Skib	DMAR_LOCK(dmar);
1062284869Skib	if (!LIST_EMPTY(&dmar->domains)) {
1063257251Skib		KASSERT((dmar->hw_gcmd & DMAR_GCMD_TE) == 0,
1064257251Skib	    ("dmar%d: RMRR not handled but translation is already enabled",
1065257251Skib		    dmar->unit));
1066257251Skib		error = dmar_enable_translation(dmar);
1067257251Skib	}
1068257251Skib	dmar_barrier_exit(dmar, DMAR_BARRIER_RMRR);
1069257251Skib	return (error);
1070257251Skib}
1071257251Skib
1072257251Skib#ifdef DDB
1073257251Skib#include <ddb/ddb.h>
1074257251Skib#include <ddb/db_lex.h>
1075257251Skib
1076257251Skibstatic void
1077284869Skibdmar_print_domain_entry(const struct dmar_map_entry *entry)
1078257251Skib{
1079257251Skib	struct dmar_map_entry *l, *r;
1080257251Skib
1081257251Skib	db_printf(
1082257251Skib	    "    start %jx end %jx free_after %jx free_down %jx flags %x ",
1083257251Skib	    entry->start, entry->end, entry->free_after, entry->free_down,
1084257251Skib	    entry->flags);
1085257251Skib	db_printf("left ");
1086257251Skib	l = RB_LEFT(entry, rb_entry);
1087257251Skib	if (l == NULL)
1088257251Skib		db_printf("NULL ");
1089257251Skib	else
1090257251Skib		db_printf("%jx ", l->start);
1091257251Skib	db_printf("right ");
1092257251Skib	r = RB_RIGHT(entry, rb_entry);
1093257251Skib	if (r == NULL)
1094257251Skib		db_printf("NULL");
1095257251Skib	else
1096257251Skib		db_printf("%jx", r->start);
1097257251Skib	db_printf("\n");
1098257251Skib}
1099257251Skib
1100257251Skibstatic void
1101284869Skibdmar_print_ctx(struct dmar_ctx *ctx)
1102257251Skib{
1103257251Skib
1104257251Skib	db_printf(
1105284869Skib	    "    @%p pci%d:%d:%d refs %d flags %x loads %lu unloads %lu\n",
1106264008Srstone	    ctx, pci_get_bus(ctx->ctx_tag.owner),
1107264008Srstone	    pci_get_slot(ctx->ctx_tag.owner),
1108284869Skib	    pci_get_function(ctx->ctx_tag.owner), ctx->refs, ctx->flags,
1109284869Skib	    ctx->loads, ctx->unloads);
1110284869Skib}
1111284869Skib
1112284869Skibstatic void
1113284869Skibdmar_print_domain(struct dmar_domain *domain, bool show_mappings)
1114284869Skib{
1115284869Skib	struct dmar_map_entry *entry;
1116284869Skib	struct dmar_ctx *ctx;
1117284869Skib
1118284869Skib	db_printf(
1119284869Skib	    "  @%p dom %d mgaw %d agaw %d pglvl %d end %jx refs %d\n"
1120284869Skib	    "   ctx_cnt %d flags %x pgobj %p map_ents %u\n",
1121284869Skib	    domain, domain->domain, domain->mgaw, domain->agaw, domain->pglvl,
1122284869Skib	    (uintmax_t)domain->end, domain->refs, domain->ctx_cnt,
1123284869Skib	    domain->flags, domain->pgtbl_obj, domain->entries_cnt);
1124284869Skib	if (!LIST_EMPTY(&domain->contexts)) {
1125284869Skib		db_printf("  Contexts:\n");
1126284869Skib		LIST_FOREACH(ctx, &domain->contexts, link)
1127284869Skib			dmar_print_ctx(ctx);
1128284869Skib	}
1129257251Skib	if (!show_mappings)
1130257251Skib		return;
1131257251Skib	db_printf("    mapped:\n");
1132284869Skib	RB_FOREACH(entry, dmar_gas_entries_tree, &domain->rb_root) {
1133284869Skib		dmar_print_domain_entry(entry);
1134257251Skib		if (db_pager_quit)
1135257251Skib			break;
1136257251Skib	}
1137257251Skib	if (db_pager_quit)
1138257251Skib		return;
1139257251Skib	db_printf("    unloading:\n");
1140284869Skib	TAILQ_FOREACH(entry, &domain->unload_entries, dmamap_link) {
1141284869Skib		dmar_print_domain_entry(entry);
1142257251Skib		if (db_pager_quit)
1143257251Skib			break;
1144257251Skib	}
1145257251Skib}
1146257251Skib
1147284869SkibDB_FUNC(dmar_domain, db_dmar_print_domain, db_show_table, CS_OWN, NULL)
1148257251Skib{
1149257251Skib	struct dmar_unit *unit;
1150284869Skib	struct dmar_domain *domain;
1151257251Skib	struct dmar_ctx *ctx;
1152257251Skib	bool show_mappings, valid;
1153284869Skib	int pci_domain, bus, device, function, i, t;
1154257251Skib	db_expr_t radix;
1155257251Skib
1156257251Skib	valid = false;
1157257251Skib	radix = db_radix;
1158257251Skib	db_radix = 10;
1159257251Skib	t = db_read_token();
1160257251Skib	if (t == tSLASH) {
1161257251Skib		t = db_read_token();
1162257251Skib		if (t != tIDENT) {
1163257251Skib			db_printf("Bad modifier\n");
1164257251Skib			db_radix = radix;
1165257251Skib			db_skip_to_eol();
1166257251Skib			return;
1167257251Skib		}
1168257251Skib		show_mappings = strchr(db_tok_string, 'm') != NULL;
1169257251Skib		t = db_read_token();
1170257903Sdim	} else {
1171257903Sdim		show_mappings = false;
1172257251Skib	}
1173257251Skib	if (t == tNUMBER) {
1174284869Skib		pci_domain = db_tok_number;
1175257251Skib		t = db_read_token();
1176257251Skib		if (t == tNUMBER) {
1177257251Skib			bus = db_tok_number;
1178257251Skib			t = db_read_token();
1179257251Skib			if (t == tNUMBER) {
1180257251Skib				device = db_tok_number;
1181257251Skib				t = db_read_token();
1182257251Skib				if (t == tNUMBER) {
1183257251Skib					function = db_tok_number;
1184257251Skib					valid = true;
1185257251Skib				}
1186257251Skib			}
1187257251Skib		}
1188257251Skib	}
1189257251Skib			db_radix = radix;
1190257251Skib	db_skip_to_eol();
1191257251Skib	if (!valid) {
1192284869Skib		db_printf("usage: show dmar_domain [/m] "
1193257251Skib		    "<domain> <bus> <device> <func>\n");
1194257251Skib		return;
1195257251Skib	}
1196257251Skib	for (i = 0; i < dmar_devcnt; i++) {
1197257251Skib		unit = device_get_softc(dmar_devs[i]);
1198284869Skib		LIST_FOREACH(domain, &unit->domains, link) {
1199284869Skib			LIST_FOREACH(ctx, &domain->contexts, link) {
1200284869Skib				if (pci_domain == unit->segment &&
1201284869Skib				    bus == pci_get_bus(ctx->ctx_tag.owner) &&
1202284869Skib				    device ==
1203284869Skib				    pci_get_slot(ctx->ctx_tag.owner) &&
1204284869Skib				    function ==
1205284869Skib				    pci_get_function(ctx->ctx_tag.owner)) {
1206284869Skib					dmar_print_domain(domain,
1207284869Skib					    show_mappings);
1208284869Skib					goto out;
1209284869Skib				}
1210257251Skib			}
1211257251Skib		}
1212257251Skib	}
1213257251Skibout:;
1214257251Skib}
1215257251Skib
1216257251Skibstatic void
1217284869Skibdmar_print_one(int idx, bool show_domains, bool show_mappings)
1218257251Skib{
1219257251Skib	struct dmar_unit *unit;
1220284869Skib	struct dmar_domain *domain;
1221257251Skib	int i, frir;
1222257251Skib
1223257251Skib	unit = device_get_softc(dmar_devs[idx]);
1224257251Skib	db_printf("dmar%d at %p, root at 0x%jx, ver 0x%x\n", unit->unit, unit,
1225257251Skib	    dmar_read8(unit, DMAR_RTADDR_REG), dmar_read4(unit, DMAR_VER_REG));
1226257251Skib	db_printf("cap 0x%jx ecap 0x%jx gsts 0x%x fsts 0x%x fectl 0x%x\n",
1227257251Skib	    (uintmax_t)dmar_read8(unit, DMAR_CAP_REG),
1228257251Skib	    (uintmax_t)dmar_read8(unit, DMAR_ECAP_REG),
1229257251Skib	    dmar_read4(unit, DMAR_GSTS_REG),
1230257251Skib	    dmar_read4(unit, DMAR_FSTS_REG),
1231257251Skib	    dmar_read4(unit, DMAR_FECTL_REG));
1232284869Skib	if (unit->ir_enabled) {
1233284869Skib		db_printf("ir is enabled; IRT @%p phys 0x%jx maxcnt %d\n",
1234284869Skib		    unit->irt, (uintmax_t)unit->irt_phys, unit->irte_cnt);
1235284869Skib	}
1236257251Skib	db_printf("fed 0x%x fea 0x%x feua 0x%x\n",
1237257251Skib	    dmar_read4(unit, DMAR_FEDATA_REG),
1238257251Skib	    dmar_read4(unit, DMAR_FEADDR_REG),
1239257251Skib	    dmar_read4(unit, DMAR_FEUADDR_REG));
1240257251Skib	db_printf("primary fault log:\n");
1241257251Skib	for (i = 0; i < DMAR_CAP_NFR(unit->hw_cap); i++) {
1242257251Skib		frir = (DMAR_CAP_FRO(unit->hw_cap) + i) * 16;
1243257251Skib		db_printf("  %d at 0x%x: %jx %jx\n", i, frir,
1244257251Skib		    (uintmax_t)dmar_read8(unit, frir),
1245257251Skib		    (uintmax_t)dmar_read8(unit, frir + 8));
1246257251Skib	}
1247257512Skib	if (DMAR_HAS_QI(unit)) {
1248257512Skib		db_printf("ied 0x%x iea 0x%x ieua 0x%x\n",
1249257512Skib		    dmar_read4(unit, DMAR_IEDATA_REG),
1250257512Skib		    dmar_read4(unit, DMAR_IEADDR_REG),
1251257512Skib		    dmar_read4(unit, DMAR_IEUADDR_REG));
1252257512Skib		if (unit->qi_enabled) {
1253257512Skib			db_printf("qi is enabled: queue @0x%jx (IQA 0x%jx) "
1254257512Skib			    "size 0x%jx\n"
1255257512Skib		    "  head 0x%x tail 0x%x avail 0x%x status 0x%x ctrl 0x%x\n"
1256257512Skib		    "  hw compl 0x%x@%p/phys@%jx next seq 0x%x gen 0x%x\n",
1257257512Skib			    (uintmax_t)unit->inv_queue,
1258257512Skib			    (uintmax_t)dmar_read8(unit, DMAR_IQA_REG),
1259257512Skib			    (uintmax_t)unit->inv_queue_size,
1260257512Skib			    dmar_read4(unit, DMAR_IQH_REG),
1261257512Skib			    dmar_read4(unit, DMAR_IQT_REG),
1262257512Skib			    unit->inv_queue_avail,
1263257512Skib			    dmar_read4(unit, DMAR_ICS_REG),
1264257512Skib			    dmar_read4(unit, DMAR_IECTL_REG),
1265257512Skib			    unit->inv_waitd_seq_hw,
1266257512Skib			    &unit->inv_waitd_seq_hw,
1267257512Skib			    (uintmax_t)unit->inv_waitd_seq_hw_phys,
1268257512Skib			    unit->inv_waitd_seq,
1269257512Skib			    unit->inv_waitd_gen);
1270257512Skib		} else {
1271257512Skib			db_printf("qi is disabled\n");
1272257512Skib		}
1273257512Skib	}
1274284869Skib	if (show_domains) {
1275284869Skib		db_printf("domains:\n");
1276284869Skib		LIST_FOREACH(domain, &unit->domains, link) {
1277284869Skib			dmar_print_domain(domain, show_mappings);
1278257251Skib			if (db_pager_quit)
1279257251Skib				break;
1280257251Skib		}
1281257251Skib	}
1282257251Skib}
1283257251Skib
1284257251SkibDB_SHOW_COMMAND(dmar, db_dmar_print)
1285257251Skib{
1286284869Skib	bool show_domains, show_mappings;
1287257251Skib
1288284869Skib	show_domains = strchr(modif, 'd') != NULL;
1289257251Skib	show_mappings = strchr(modif, 'm') != NULL;
1290257251Skib	if (!have_addr) {
1291284869Skib		db_printf("usage: show dmar [/d] [/m] index\n");
1292257251Skib		return;
1293257251Skib	}
1294284869Skib	dmar_print_one((int)addr, show_domains, show_mappings);
1295257251Skib}
1296257251Skib
1297257251SkibDB_SHOW_ALL_COMMAND(dmars, db_show_all_dmars)
1298257251Skib{
1299257251Skib	int i;
1300284869Skib	bool show_domains, show_mappings;
1301257251Skib
1302284869Skib	show_domains = strchr(modif, 'd') != NULL;
1303257251Skib	show_mappings = strchr(modif, 'm') != NULL;
1304257251Skib
1305257251Skib	for (i = 0; i < dmar_devcnt; i++) {
1306284869Skib		dmar_print_one(i, show_domains, show_mappings);
1307257251Skib		if (db_pager_quit)
1308257251Skib			break;
1309257251Skib	}
1310257251Skib}
1311257251Skib#endif
1312