intel_drv.c revision 278947
1/*-
2 * Copyright (c) 2013 The FreeBSD Foundation
3 * All rights reserved.
4 *
5 * This software was developed by Konstantin Belousov <kib@FreeBSD.org>
6 * under sponsorship from the FreeBSD Foundation.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: stable/10/sys/x86/iommu/intel_drv.c 278947 2015-02-18 08:06:36Z kib $");
32
33#include "opt_acpi.h"
34#if defined(__amd64__) /* || defined(__ia64__) */
35#define	DEV_APIC
36#else
37#include "opt_apic.h"
38#endif
39#include "opt_ddb.h"
40
41#include <sys/param.h>
42#include <sys/bus.h>
43#include <sys/kernel.h>
44#include <sys/lock.h>
45#include <sys/malloc.h>
46#include <sys/memdesc.h>
47#include <sys/module.h>
48#include <sys/rman.h>
49#include <sys/rwlock.h>
50#include <sys/smp.h>
51#include <sys/taskqueue.h>
52#include <sys/tree.h>
53#include <machine/bus.h>
54#include <contrib/dev/acpica/include/acpi.h>
55#include <contrib/dev/acpica/include/accommon.h>
56#include <dev/acpica/acpivar.h>
57#include <vm/vm.h>
58#include <vm/vm_extern.h>
59#include <vm/vm_kern.h>
60#include <vm/vm_object.h>
61#include <vm/vm_page.h>
62#include <vm/vm_pager.h>
63#include <vm/vm_map.h>
64#include <x86/include/busdma_impl.h>
65#include <x86/iommu/intel_reg.h>
66#include <x86/iommu/busdma_dmar.h>
67#include <x86/iommu/intel_dmar.h>
68#include <dev/pci/pcivar.h>
69
70#ifdef DEV_APIC
71#include "pcib_if.h"
72#endif
73
74#define	DMAR_FAULT_IRQ_RID	0
75#define	DMAR_QI_IRQ_RID		1
76#define	DMAR_REG_RID		2
77
78static devclass_t dmar_devclass;
79static device_t *dmar_devs;
80static int dmar_devcnt;
81
82typedef int (*dmar_iter_t)(ACPI_DMAR_HEADER *, void *);
83
84static void
85dmar_iterate_tbl(dmar_iter_t iter, void *arg)
86{
87	ACPI_TABLE_DMAR *dmartbl;
88	ACPI_DMAR_HEADER *dmarh;
89	char *ptr, *ptrend;
90	ACPI_STATUS status;
91
92	status = AcpiGetTable(ACPI_SIG_DMAR, 1, (ACPI_TABLE_HEADER **)&dmartbl);
93	if (ACPI_FAILURE(status))
94		return;
95	ptr = (char *)dmartbl + sizeof(*dmartbl);
96	ptrend = (char *)dmartbl + dmartbl->Header.Length;
97	for (;;) {
98		if (ptr >= ptrend)
99			break;
100		dmarh = (ACPI_DMAR_HEADER *)ptr;
101		if (dmarh->Length <= 0) {
102			printf("dmar_identify: corrupted DMAR table, l %d\n",
103			    dmarh->Length);
104			break;
105		}
106		ptr += dmarh->Length;
107		if (!iter(dmarh, arg))
108			break;
109	}
110}
111
112struct find_iter_args {
113	int i;
114	ACPI_DMAR_HARDWARE_UNIT *res;
115};
116
117static int
118dmar_find_iter(ACPI_DMAR_HEADER *dmarh, void *arg)
119{
120	struct find_iter_args *fia;
121
122	if (dmarh->Type != ACPI_DMAR_TYPE_HARDWARE_UNIT)
123		return (1);
124
125	fia = arg;
126	if (fia->i == 0) {
127		fia->res = (ACPI_DMAR_HARDWARE_UNIT *)dmarh;
128		return (0);
129	}
130	fia->i--;
131	return (1);
132}
133
134static ACPI_DMAR_HARDWARE_UNIT *
135dmar_find_by_index(int idx)
136{
137	struct find_iter_args fia;
138
139	fia.i = idx;
140	fia.res = NULL;
141	dmar_iterate_tbl(dmar_find_iter, &fia);
142	return (fia.res);
143}
144
145static int
146dmar_count_iter(ACPI_DMAR_HEADER *dmarh, void *arg)
147{
148
149	if (dmarh->Type == ACPI_DMAR_TYPE_HARDWARE_UNIT)
150		dmar_devcnt++;
151	return (1);
152}
153
154static int dmar_enable = 0;
155static void
156dmar_identify(driver_t *driver, device_t parent)
157{
158	ACPI_TABLE_DMAR *dmartbl;
159	ACPI_DMAR_HARDWARE_UNIT *dmarh;
160	ACPI_STATUS status;
161	int i, error;
162
163	if (acpi_disabled("dmar"))
164		return;
165	TUNABLE_INT_FETCH("hw.dmar.enable", &dmar_enable);
166	if (!dmar_enable)
167		return;
168#ifdef INVARIANTS
169	TUNABLE_INT_FETCH("hw.dmar.check_free", &dmar_check_free);
170#endif
171	TUNABLE_INT_FETCH("hw.dmar.match_verbose", &dmar_match_verbose);
172	status = AcpiGetTable(ACPI_SIG_DMAR, 1, (ACPI_TABLE_HEADER **)&dmartbl);
173	if (ACPI_FAILURE(status))
174		return;
175	haw = dmartbl->Width + 1;
176	if ((1ULL << (haw + 1)) > BUS_SPACE_MAXADDR)
177		dmar_high = BUS_SPACE_MAXADDR;
178	else
179		dmar_high = 1ULL << (haw + 1);
180	if (bootverbose) {
181		printf("DMAR HAW=%d flags=<%b>\n", dmartbl->Width,
182		    (unsigned)dmartbl->Flags,
183		    "\020\001INTR_REMAP\002X2APIC_OPT_OUT");
184	}
185
186	dmar_iterate_tbl(dmar_count_iter, NULL);
187	if (dmar_devcnt == 0)
188		return;
189	dmar_devs = malloc(sizeof(device_t) * dmar_devcnt, M_DEVBUF,
190	    M_WAITOK | M_ZERO);
191	for (i = 0; i < dmar_devcnt; i++) {
192		dmarh = dmar_find_by_index(i);
193		if (dmarh == NULL) {
194			printf("dmar_identify: cannot find HWUNIT %d\n", i);
195			continue;
196		}
197		dmar_devs[i] = BUS_ADD_CHILD(parent, 1, "dmar", i);
198		if (dmar_devs[i] == NULL) {
199			printf("dmar_identify: cannot create instance %d\n", i);
200			continue;
201		}
202		error = bus_set_resource(dmar_devs[i], SYS_RES_MEMORY,
203		    DMAR_REG_RID, dmarh->Address, PAGE_SIZE);
204		if (error != 0) {
205			printf(
206	"dmar%d: unable to alloc register window at 0x%08jx: error %d\n",
207			    i, (uintmax_t)dmarh->Address, error);
208			device_delete_child(parent, dmar_devs[i]);
209			dmar_devs[i] = NULL;
210		}
211	}
212}
213
214static int
215dmar_probe(device_t dev)
216{
217
218	if (acpi_get_handle(dev) != NULL)
219		return (ENXIO);
220	device_set_desc(dev, "DMA remap");
221	return (BUS_PROBE_NOWILDCARD);
222}
223
224static void
225dmar_release_intr(device_t dev, struct dmar_unit *unit, int idx)
226{
227	struct dmar_msi_data *dmd;
228
229	dmd = &unit->intrs[idx];
230	if (dmd->irq == -1)
231		return;
232	bus_teardown_intr(dev, dmd->irq_res, dmd->intr_handle);
233	bus_release_resource(dev, SYS_RES_IRQ, dmd->irq_rid, dmd->irq_res);
234	bus_delete_resource(dev, SYS_RES_IRQ, dmd->irq_rid);
235	PCIB_RELEASE_MSIX(device_get_parent(device_get_parent(dev)),
236	    dev, dmd->irq);
237	dmd->irq = -1;
238}
239
240static void
241dmar_release_resources(device_t dev, struct dmar_unit *unit)
242{
243	int i;
244
245	dmar_fini_busdma(unit);
246	dmar_fini_qi(unit);
247	dmar_fini_fault_log(unit);
248	for (i = 0; i < DMAR_INTR_TOTAL; i++)
249		dmar_release_intr(dev, unit, i);
250	if (unit->regs != NULL) {
251		bus_deactivate_resource(dev, SYS_RES_MEMORY, unit->reg_rid,
252		    unit->regs);
253		bus_release_resource(dev, SYS_RES_MEMORY, unit->reg_rid,
254		    unit->regs);
255		unit->regs = NULL;
256	}
257	if (unit->domids != NULL) {
258		delete_unrhdr(unit->domids);
259		unit->domids = NULL;
260	}
261	if (unit->ctx_obj != NULL) {
262		vm_object_deallocate(unit->ctx_obj);
263		unit->ctx_obj = NULL;
264	}
265}
266
267static int
268dmar_alloc_irq(device_t dev, struct dmar_unit *unit, int idx)
269{
270	device_t pcib;
271	struct dmar_msi_data *dmd;
272	uint64_t msi_addr;
273	uint32_t msi_data;
274	int error;
275
276	dmd = &unit->intrs[idx];
277	pcib = device_get_parent(device_get_parent(dev)); /* Really not pcib */
278	error = PCIB_ALLOC_MSIX(pcib, dev, &dmd->irq);
279	if (error != 0) {
280		device_printf(dev, "cannot allocate %s interrupt, %d\n",
281		    dmd->name, error);
282		goto err1;
283	}
284	error = bus_set_resource(dev, SYS_RES_IRQ, dmd->irq_rid,
285	    dmd->irq, 1);
286	if (error != 0) {
287		device_printf(dev, "cannot set %s interrupt resource, %d\n",
288		    dmd->name, error);
289		goto err2;
290	}
291	dmd->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
292	    &dmd->irq_rid, RF_ACTIVE);
293	if (dmd->irq_res == NULL) {
294		device_printf(dev,
295		    "cannot allocate resource for %s interrupt\n", dmd->name);
296		error = ENXIO;
297		goto err3;
298	}
299	error = bus_setup_intr(dev, dmd->irq_res, INTR_TYPE_MISC,
300	    dmd->handler, NULL, unit, &dmd->intr_handle);
301	if (error != 0) {
302		device_printf(dev, "cannot setup %s interrupt, %d\n",
303		    dmd->name, error);
304		goto err4;
305	}
306	bus_describe_intr(dev, dmd->irq_res, dmd->intr_handle, dmd->name);
307	error = PCIB_MAP_MSI(pcib, dev, dmd->irq, &msi_addr, &msi_data);
308	if (error != 0) {
309		device_printf(dev, "cannot map %s interrupt, %d\n",
310		    dmd->name, error);
311		goto err5;
312	}
313	dmar_write4(unit, dmd->msi_data_reg, msi_data);
314	dmar_write4(unit, dmd->msi_addr_reg, msi_addr);
315	/* Only for xAPIC mode */
316	dmar_write4(unit, dmd->msi_uaddr_reg, msi_addr >> 32);
317	return (0);
318
319err5:
320	bus_teardown_intr(dev, dmd->irq_res, dmd->intr_handle);
321err4:
322	bus_release_resource(dev, SYS_RES_IRQ, dmd->irq_rid, dmd->irq_res);
323err3:
324	bus_delete_resource(dev, SYS_RES_IRQ, dmd->irq_rid);
325err2:
326	PCIB_RELEASE_MSIX(pcib, dev, dmd->irq);
327	dmd->irq = -1;
328err1:
329	return (error);
330}
331
332#ifdef DEV_APIC
333static int
334dmar_remap_intr(device_t dev, device_t child, u_int irq)
335{
336	struct dmar_unit *unit;
337	struct dmar_msi_data *dmd;
338	uint64_t msi_addr;
339	uint32_t msi_data;
340	int i, error;
341
342	unit = device_get_softc(dev);
343	for (i = 0; i < DMAR_INTR_TOTAL; i++) {
344		dmd = &unit->intrs[i];
345		if (irq == dmd->irq) {
346			error = PCIB_MAP_MSI(device_get_parent(
347			    device_get_parent(dev)),
348			    dev, irq, &msi_addr, &msi_data);
349			if (error != 0)
350				return (error);
351			DMAR_LOCK(unit);
352			(dmd->disable_intr)(unit);
353			dmar_write4(unit, dmd->msi_data_reg, msi_data);
354			dmar_write4(unit, dmd->msi_addr_reg, msi_addr);
355			dmar_write4(unit, dmd->msi_uaddr_reg, msi_addr >> 32);
356			(dmd->enable_intr)(unit);
357			DMAR_UNLOCK(unit);
358			return (0);
359		}
360	}
361	return (ENOENT);
362}
363#endif
364
365static void
366dmar_print_caps(device_t dev, struct dmar_unit *unit,
367    ACPI_DMAR_HARDWARE_UNIT *dmaru)
368{
369	uint32_t caphi, ecaphi;
370
371	device_printf(dev, "regs@0x%08jx, ver=%d.%d, seg=%d, flags=<%b>\n",
372	    (uintmax_t)dmaru->Address, DMAR_MAJOR_VER(unit->hw_ver),
373	    DMAR_MINOR_VER(unit->hw_ver), dmaru->Segment,
374	    dmaru->Flags, "\020\001INCLUDE_ALL_PCI");
375	caphi = unit->hw_cap >> 32;
376	device_printf(dev, "cap=%b,", (u_int)unit->hw_cap,
377	    "\020\004AFL\005WBF\006PLMR\007PHMR\010CM\027ZLR\030ISOCH");
378	printf("%b, ", caphi, "\020\010PSI\027DWD\030DRD\031FL1GP\034PSI");
379	printf("ndoms=%d, sagaw=%d, mgaw=%d, fro=%d, nfr=%d, superp=%d",
380	    DMAR_CAP_ND(unit->hw_cap), DMAR_CAP_SAGAW(unit->hw_cap),
381	    DMAR_CAP_MGAW(unit->hw_cap), DMAR_CAP_FRO(unit->hw_cap),
382	    DMAR_CAP_NFR(unit->hw_cap), DMAR_CAP_SPS(unit->hw_cap));
383	if ((unit->hw_cap & DMAR_CAP_PSI) != 0)
384		printf(", mamv=%d", DMAR_CAP_MAMV(unit->hw_cap));
385	printf("\n");
386	ecaphi = unit->hw_ecap >> 32;
387	device_printf(dev, "ecap=%b,", (u_int)unit->hw_ecap,
388	    "\020\001C\002QI\003DI\004IR\005EIM\007PT\010SC\031ECS\032MTS"
389	    "\033NEST\034DIS\035PASID\036PRS\037ERS\040SRS");
390	printf("%b, ", ecaphi, "\020\002NWFS\003EAFS");
391	printf("mhmw=%d, iro=%d\n", DMAR_ECAP_MHMV(unit->hw_ecap),
392	    DMAR_ECAP_IRO(unit->hw_ecap));
393}
394
395static int
396dmar_attach(device_t dev)
397{
398	struct dmar_unit *unit;
399	ACPI_DMAR_HARDWARE_UNIT *dmaru;
400	int i, error;
401
402	unit = device_get_softc(dev);
403	unit->dev = dev;
404	unit->unit = device_get_unit(dev);
405	dmaru = dmar_find_by_index(unit->unit);
406	if (dmaru == NULL)
407		return (EINVAL);
408	unit->segment = dmaru->Segment;
409	unit->base = dmaru->Address;
410	unit->reg_rid = DMAR_REG_RID;
411	unit->regs = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
412	    &unit->reg_rid, RF_ACTIVE);
413	if (unit->regs == NULL) {
414		device_printf(dev, "cannot allocate register window\n");
415		return (ENOMEM);
416	}
417	unit->hw_ver = dmar_read4(unit, DMAR_VER_REG);
418	unit->hw_cap = dmar_read8(unit, DMAR_CAP_REG);
419	unit->hw_ecap = dmar_read8(unit, DMAR_ECAP_REG);
420	if (bootverbose)
421		dmar_print_caps(dev, unit, dmaru);
422	dmar_quirks_post_ident(unit);
423
424	for (i = 0; i < DMAR_INTR_TOTAL; i++)
425		unit->intrs[i].irq = -1;
426
427	unit->intrs[DMAR_INTR_FAULT].name = "fault";
428	unit->intrs[DMAR_INTR_FAULT].irq_rid = DMAR_FAULT_IRQ_RID;
429	unit->intrs[DMAR_INTR_FAULT].handler = dmar_fault_intr;
430	unit->intrs[DMAR_INTR_FAULT].msi_data_reg = DMAR_FEDATA_REG;
431	unit->intrs[DMAR_INTR_FAULT].msi_addr_reg = DMAR_FEADDR_REG;
432	unit->intrs[DMAR_INTR_FAULT].msi_uaddr_reg = DMAR_FEUADDR_REG;
433	unit->intrs[DMAR_INTR_FAULT].enable_intr = dmar_enable_fault_intr;
434	unit->intrs[DMAR_INTR_FAULT].disable_intr = dmar_disable_fault_intr;
435	error = dmar_alloc_irq(dev, unit, DMAR_INTR_FAULT);
436	if (error != 0) {
437		dmar_release_resources(dev, unit);
438		return (error);
439	}
440	if (DMAR_HAS_QI(unit)) {
441		unit->intrs[DMAR_INTR_QI].name = "qi";
442		unit->intrs[DMAR_INTR_QI].irq_rid = DMAR_QI_IRQ_RID;
443		unit->intrs[DMAR_INTR_QI].handler = dmar_qi_intr;
444		unit->intrs[DMAR_INTR_QI].msi_data_reg = DMAR_IEDATA_REG;
445		unit->intrs[DMAR_INTR_QI].msi_addr_reg = DMAR_IEADDR_REG;
446		unit->intrs[DMAR_INTR_QI].msi_uaddr_reg = DMAR_IEUADDR_REG;
447		unit->intrs[DMAR_INTR_QI].enable_intr = dmar_enable_qi_intr;
448		unit->intrs[DMAR_INTR_QI].disable_intr = dmar_disable_qi_intr;
449		error = dmar_alloc_irq(dev, unit, DMAR_INTR_QI);
450		if (error != 0) {
451			dmar_release_resources(dev, unit);
452			return (error);
453		}
454	}
455
456	mtx_init(&unit->lock, "dmarhw", NULL, MTX_DEF);
457	unit->domids = new_unrhdr(0, dmar_nd2mask(DMAR_CAP_ND(unit->hw_cap)),
458	    &unit->lock);
459
460	/*
461	 * 9.2 "Context Entry":
462	 * When Caching Mode (CM) field is reported as Set, the
463	 * domain-id value of zero is architecturally reserved.
464	 * Software must not use domain-id value of zero
465	 * when CM is Set.
466	 */
467	if ((unit->hw_cap & DMAR_CAP_CM) != 0)
468		alloc_unr_specific(unit->domids, 0);
469
470	unit->ctx_obj = vm_pager_allocate(OBJT_PHYS, NULL, IDX_TO_OFF(1 +
471	    DMAR_CTX_CNT), 0, 0, NULL);
472
473	/*
474	 * Allocate and load the root entry table pointer.  Enable the
475	 * address translation after the required invalidations are
476	 * done.
477	 */
478	dmar_pgalloc(unit->ctx_obj, 0, DMAR_PGF_WAITOK | DMAR_PGF_ZERO);
479	DMAR_LOCK(unit);
480	error = dmar_load_root_entry_ptr(unit);
481	if (error != 0) {
482		DMAR_UNLOCK(unit);
483		dmar_release_resources(dev, unit);
484		return (error);
485	}
486	error = dmar_inv_ctx_glob(unit);
487	if (error != 0) {
488		DMAR_UNLOCK(unit);
489		dmar_release_resources(dev, unit);
490		return (error);
491	}
492	if ((unit->hw_ecap & DMAR_ECAP_DI) != 0) {
493		error = dmar_inv_iotlb_glob(unit);
494		if (error != 0) {
495			DMAR_UNLOCK(unit);
496			dmar_release_resources(dev, unit);
497			return (error);
498		}
499	}
500
501	DMAR_UNLOCK(unit);
502	error = dmar_init_fault_log(unit);
503	if (error != 0) {
504		dmar_release_resources(dev, unit);
505		return (error);
506	}
507	error = dmar_init_qi(unit);
508	if (error != 0) {
509		dmar_release_resources(dev, unit);
510		return (error);
511	}
512	error = dmar_init_busdma(unit);
513	if (error != 0) {
514		dmar_release_resources(dev, unit);
515		return (error);
516	}
517
518#ifdef NOTYET
519	DMAR_LOCK(unit);
520	error = dmar_enable_translation(unit);
521	if (error != 0) {
522		DMAR_UNLOCK(unit);
523		dmar_release_resources(dev, unit);
524		return (error);
525	}
526	DMAR_UNLOCK(unit);
527#endif
528
529	return (0);
530}
531
532static int
533dmar_detach(device_t dev)
534{
535
536	return (EBUSY);
537}
538
539static int
540dmar_suspend(device_t dev)
541{
542
543	return (0);
544}
545
546static int
547dmar_resume(device_t dev)
548{
549
550	/* XXXKIB */
551	return (0);
552}
553
554static device_method_t dmar_methods[] = {
555	DEVMETHOD(device_identify, dmar_identify),
556	DEVMETHOD(device_probe, dmar_probe),
557	DEVMETHOD(device_attach, dmar_attach),
558	DEVMETHOD(device_detach, dmar_detach),
559	DEVMETHOD(device_suspend, dmar_suspend),
560	DEVMETHOD(device_resume, dmar_resume),
561#ifdef DEV_APIC
562	DEVMETHOD(bus_remap_intr, dmar_remap_intr),
563#endif
564	DEVMETHOD_END
565};
566
567static driver_t	dmar_driver = {
568	"dmar",
569	dmar_methods,
570	sizeof(struct dmar_unit),
571};
572
573DRIVER_MODULE(dmar, acpi, dmar_driver, dmar_devclass, 0, 0);
574MODULE_DEPEND(dmar, acpi, 1, 1, 1);
575
576static void
577dmar_print_path(device_t dev, const char *banner, int busno, int depth,
578    const ACPI_DMAR_PCI_PATH *path)
579{
580	int i;
581
582	device_printf(dev, "%s [%d, ", banner, busno);
583	for (i = 0; i < depth; i++) {
584		if (i != 0)
585			printf(", ");
586		printf("(%d, %d)", path[i].Device, path[i].Function);
587	}
588	printf("]\n");
589}
590
591static int
592dmar_dev_depth(device_t child)
593{
594	devclass_t pci_class;
595	device_t bus, pcib;
596	int depth;
597
598	pci_class = devclass_find("pci");
599	for (depth = 1; ; depth++) {
600		bus = device_get_parent(child);
601		pcib = device_get_parent(bus);
602		if (device_get_devclass(device_get_parent(pcib)) !=
603		    pci_class)
604			return (depth);
605		child = pcib;
606	}
607}
608
609static void
610dmar_dev_path(device_t child, int *busno, ACPI_DMAR_PCI_PATH *path, int depth)
611{
612	devclass_t pci_class;
613	device_t bus, pcib;
614
615	pci_class = devclass_find("pci");
616	for (depth--; depth != -1; depth--) {
617		path[depth].Device = pci_get_slot(child);
618		path[depth].Function = pci_get_function(child);
619		bus = device_get_parent(child);
620		pcib = device_get_parent(bus);
621		if (device_get_devclass(device_get_parent(pcib)) !=
622		    pci_class) {
623			/* reached a host bridge */
624			*busno = pcib_get_bus(bus);
625			return;
626		}
627		child = pcib;
628	}
629	panic("wrong depth");
630}
631
632static int
633dmar_match_pathes(int busno1, const ACPI_DMAR_PCI_PATH *path1, int depth1,
634    int busno2, const ACPI_DMAR_PCI_PATH *path2, int depth2,
635    enum AcpiDmarScopeType scope_type)
636{
637	int i, depth;
638
639	if (busno1 != busno2)
640		return (0);
641	if (scope_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT && depth1 != depth2)
642		return (0);
643	depth = depth1;
644	if (depth2 < depth)
645		depth = depth2;
646	for (i = 0; i < depth; i++) {
647		if (path1[i].Device != path2[i].Device ||
648		    path1[i].Function != path2[i].Function)
649			return (0);
650	}
651	return (1);
652}
653
654static int
655dmar_match_devscope(ACPI_DMAR_DEVICE_SCOPE *devscope, device_t dev,
656    int dev_busno, const ACPI_DMAR_PCI_PATH *dev_path, int dev_path_len)
657{
658	ACPI_DMAR_PCI_PATH *path;
659	int path_len;
660
661	if (devscope->Length < sizeof(*devscope)) {
662		printf("dmar_find: corrupted DMAR table, dl %d\n",
663		    devscope->Length);
664		return (-1);
665	}
666	if (devscope->EntryType != ACPI_DMAR_SCOPE_TYPE_ENDPOINT &&
667	    devscope->EntryType != ACPI_DMAR_SCOPE_TYPE_BRIDGE)
668		return (0);
669	path_len = devscope->Length - sizeof(*devscope);
670	if (path_len % 2 != 0) {
671		printf("dmar_find_bsf: corrupted DMAR table, dl %d\n",
672		    devscope->Length);
673		return (-1);
674	}
675	path_len /= 2;
676	path = (ACPI_DMAR_PCI_PATH *)(devscope + 1);
677	if (path_len == 0) {
678		printf("dmar_find: corrupted DMAR table, dl %d\n",
679		    devscope->Length);
680		return (-1);
681	}
682	if (dmar_match_verbose)
683		dmar_print_path(dev, "DMAR", devscope->Bus, path_len, path);
684
685	return (dmar_match_pathes(devscope->Bus, path, path_len, dev_busno,
686	    dev_path, dev_path_len, devscope->EntryType));
687}
688
689struct dmar_unit *
690dmar_find(device_t dev)
691{
692	device_t dmar_dev;
693	ACPI_DMAR_HARDWARE_UNIT *dmarh;
694	ACPI_DMAR_DEVICE_SCOPE *devscope;
695	char *ptr, *ptrend;
696	int i, match, dev_domain, dev_busno, dev_path_len;
697
698	dmar_dev = NULL;
699	dev_domain = pci_get_domain(dev);
700	dev_path_len = dmar_dev_depth(dev);
701	ACPI_DMAR_PCI_PATH dev_path[dev_path_len];
702	dmar_dev_path(dev, &dev_busno, dev_path, dev_path_len);
703	if (dmar_match_verbose)
704		dmar_print_path(dev, "PCI", dev_busno, dev_path_len, dev_path);
705
706	for (i = 0; i < dmar_devcnt; i++) {
707		if (dmar_devs[i] == NULL)
708			continue;
709		dmarh = dmar_find_by_index(i);
710		if (dmarh == NULL)
711			continue;
712		if (dmarh->Segment != dev_domain)
713			continue;
714		if ((dmarh->Flags & ACPI_DMAR_INCLUDE_ALL) != 0) {
715			dmar_dev = dmar_devs[i];
716			if (dmar_match_verbose) {
717				device_printf(dev,
718				    "pci%d:%d:%d:%d matched dmar%d INCLUDE_ALL\n",
719				    dev_domain, pci_get_bus(dev),
720				    pci_get_slot(dev),
721				    pci_get_function(dev),
722				    ((struct dmar_unit *)device_get_softc(
723				    dmar_dev))->unit);
724			}
725			goto found;
726		}
727		ptr = (char *)dmarh + sizeof(*dmarh);
728		ptrend = (char *)dmarh + dmarh->Header.Length;
729		for (;;) {
730			if (ptr >= ptrend)
731				break;
732			devscope = (ACPI_DMAR_DEVICE_SCOPE *)ptr;
733			ptr += devscope->Length;
734			if (dmar_match_verbose) {
735				device_printf(dev,
736				    "pci%d:%d:%d:%d matching dmar%d\n",
737				    dev_domain, pci_get_bus(dev),
738				    pci_get_slot(dev),
739				    pci_get_function(dev),
740				    ((struct dmar_unit *)device_get_softc(
741				    dmar_devs[i]))->unit);
742			}
743			match = dmar_match_devscope(devscope, dev, dev_busno,
744			    dev_path, dev_path_len);
745			if (dmar_match_verbose) {
746				if (match == -1)
747					printf("table error\n");
748				else if (match == 0)
749					printf("not matched\n");
750				else
751					printf("matched\n");
752			}
753			if (match == -1)
754				return (NULL);
755			else if (match == 1) {
756				dmar_dev = dmar_devs[i];
757				goto found;
758			}
759		}
760	}
761	return (NULL);
762found:
763	return (device_get_softc(dmar_dev));
764}
765
766struct rmrr_iter_args {
767	struct dmar_ctx *ctx;
768	device_t dev;
769	int dev_domain;
770	int dev_busno;
771	ACPI_DMAR_PCI_PATH *dev_path;
772	int dev_path_len;
773	struct dmar_map_entries_tailq *rmrr_entries;
774};
775
776static int
777dmar_rmrr_iter(ACPI_DMAR_HEADER *dmarh, void *arg)
778{
779	struct rmrr_iter_args *ria;
780	ACPI_DMAR_RESERVED_MEMORY *resmem;
781	ACPI_DMAR_DEVICE_SCOPE *devscope;
782	struct dmar_map_entry *entry;
783	char *ptr, *ptrend;
784	int match;
785
786	if (dmarh->Type != ACPI_DMAR_TYPE_RESERVED_MEMORY)
787		return (1);
788
789	ria = arg;
790	resmem = (ACPI_DMAR_RESERVED_MEMORY *)dmarh;
791	if (dmar_match_verbose) {
792		printf("RMRR [%jx,%jx] segment %d\n",
793		    (uintmax_t)resmem->BaseAddress,
794		    (uintmax_t)resmem->EndAddress,
795		    resmem->Segment);
796	}
797	if (resmem->Segment != ria->dev_domain)
798		return (1);
799
800	ptr = (char *)resmem + sizeof(*resmem);
801	ptrend = (char *)resmem + resmem->Header.Length;
802	for (;;) {
803		if (ptr >= ptrend)
804			break;
805		devscope = (ACPI_DMAR_DEVICE_SCOPE *)ptr;
806		ptr += devscope->Length;
807		match = dmar_match_devscope(devscope, ria->dev, ria->dev_busno,
808		    ria->dev_path, ria->dev_path_len);
809		if (match == 1) {
810			if (dmar_match_verbose)
811				printf("matched\n");
812			entry = dmar_gas_alloc_entry(ria->ctx, DMAR_PGF_WAITOK);
813			entry->start = resmem->BaseAddress;
814			/* The RMRR entry end address is inclusive. */
815			entry->end = resmem->EndAddress;
816			TAILQ_INSERT_TAIL(ria->rmrr_entries, entry,
817			    unroll_link);
818		} else if (dmar_match_verbose) {
819			printf("not matched, err %d\n", match);
820		}
821	}
822
823	return (1);
824}
825
826void
827dmar_ctx_parse_rmrr(struct dmar_ctx *ctx, device_t dev,
828    struct dmar_map_entries_tailq *rmrr_entries)
829{
830	struct rmrr_iter_args ria;
831
832	ria.dev_domain = pci_get_domain(dev);
833	ria.dev_path_len = dmar_dev_depth(dev);
834	ACPI_DMAR_PCI_PATH dev_path[ria.dev_path_len];
835	dmar_dev_path(dev, &ria.dev_busno, dev_path, ria.dev_path_len);
836
837	if (dmar_match_verbose) {
838		device_printf(dev, "parsing RMRR entries for ");
839		dmar_print_path(dev, "PCI", ria.dev_busno, ria.dev_path_len,
840		    dev_path);
841	}
842
843	ria.ctx = ctx;
844	ria.dev = dev;
845	ria.dev_path = dev_path;
846	ria.rmrr_entries = rmrr_entries;
847	dmar_iterate_tbl(dmar_rmrr_iter, &ria);
848}
849
850struct inst_rmrr_iter_args {
851	struct dmar_unit *dmar;
852};
853
854static device_t
855dmar_path_dev(int segment, int path_len, int busno,
856    const ACPI_DMAR_PCI_PATH *path)
857{
858	devclass_t pci_class;
859	device_t bus, pcib, dev;
860	int i;
861
862	pci_class = devclass_find("pci");
863	dev = NULL;
864	for (i = 0; i < path_len; i++, path++) {
865		dev = pci_find_dbsf(segment, busno, path->Device,
866		    path->Function);
867		if (dev == NULL)
868			break;
869		if (i != path_len - 1) {
870			bus = device_get_parent(dev);
871			pcib = device_get_parent(bus);
872			if (device_get_devclass(device_get_parent(pcib)) !=
873			    pci_class)
874				return (NULL);
875		}
876		busno = pcib_get_bus(dev);
877	}
878	return (dev);
879}
880
881static int
882dmar_inst_rmrr_iter(ACPI_DMAR_HEADER *dmarh, void *arg)
883{
884	const ACPI_DMAR_RESERVED_MEMORY *resmem;
885	const ACPI_DMAR_DEVICE_SCOPE *devscope;
886	struct inst_rmrr_iter_args *iria;
887	const char *ptr, *ptrend;
888	struct dmar_unit *dev_dmar;
889	device_t dev;
890
891	if (dmarh->Type != ACPI_DMAR_TYPE_RESERVED_MEMORY)
892		return (1);
893
894	iria = arg;
895	resmem = (ACPI_DMAR_RESERVED_MEMORY *)dmarh;
896	if (resmem->Segment != iria->dmar->segment)
897		return (1);
898	if (dmar_match_verbose) {
899		printf("dmar%d: RMRR [%jx,%jx]\n", iria->dmar->unit,
900		    (uintmax_t)resmem->BaseAddress,
901		    (uintmax_t)resmem->EndAddress);
902	}
903
904	ptr = (const char *)resmem + sizeof(*resmem);
905	ptrend = (const char *)resmem + resmem->Header.Length;
906	for (;;) {
907		if (ptr >= ptrend)
908			break;
909		devscope = (const ACPI_DMAR_DEVICE_SCOPE *)ptr;
910		ptr += devscope->Length;
911		/* XXXKIB bridge */
912		if (devscope->EntryType != ACPI_DMAR_SCOPE_TYPE_ENDPOINT)
913			continue;
914		if (dmar_match_verbose) {
915			dmar_print_path(iria->dmar->dev, "RMRR scope",
916			    devscope->Bus, (devscope->Length -
917			    sizeof(ACPI_DMAR_DEVICE_SCOPE)) / 2,
918			    (const ACPI_DMAR_PCI_PATH *)(devscope + 1));
919		}
920		dev = dmar_path_dev(resmem->Segment, (devscope->Length -
921		    sizeof(ACPI_DMAR_DEVICE_SCOPE)) / 2, devscope->Bus,
922		    (const ACPI_DMAR_PCI_PATH *)(devscope + 1));
923		if (dev == NULL) {
924			if (dmar_match_verbose)
925				printf("null dev\n");
926			continue;
927		}
928		dev_dmar = dmar_find(dev);
929		if (dev_dmar != iria->dmar) {
930			if (dmar_match_verbose) {
931				printf("dmar%d matched, skipping\n",
932				    dev_dmar->unit);
933			}
934			continue;
935		}
936		if (dmar_match_verbose)
937			printf("matched, instantiating RMRR context\n");
938		dmar_instantiate_ctx(iria->dmar, dev, true);
939	}
940
941	return (1);
942
943}
944
945/*
946 * Pre-create all contexts for the DMAR which have RMRR entries.
947 */
948int
949dmar_instantiate_rmrr_ctxs(struct dmar_unit *dmar)
950{
951	struct inst_rmrr_iter_args iria;
952	int error;
953
954	if (!dmar_barrier_enter(dmar, DMAR_BARRIER_RMRR))
955		return (0);
956
957	error = 0;
958	iria.dmar = dmar;
959	if (dmar_match_verbose)
960		printf("dmar%d: instantiating RMRR contexts\n", dmar->unit);
961	dmar_iterate_tbl(dmar_inst_rmrr_iter, &iria);
962	DMAR_LOCK(dmar);
963	if (!LIST_EMPTY(&dmar->contexts)) {
964		KASSERT((dmar->hw_gcmd & DMAR_GCMD_TE) == 0,
965	    ("dmar%d: RMRR not handled but translation is already enabled",
966		    dmar->unit));
967		error = dmar_enable_translation(dmar);
968	}
969	dmar_barrier_exit(dmar, DMAR_BARRIER_RMRR);
970	return (error);
971}
972
973#ifdef DDB
974#include <ddb/ddb.h>
975#include <ddb/db_lex.h>
976
977static void
978dmar_print_ctx_entry(const struct dmar_map_entry *entry)
979{
980	struct dmar_map_entry *l, *r;
981
982	db_printf(
983	    "    start %jx end %jx free_after %jx free_down %jx flags %x ",
984	    entry->start, entry->end, entry->free_after, entry->free_down,
985	    entry->flags);
986	db_printf("left ");
987	l = RB_LEFT(entry, rb_entry);
988	if (l == NULL)
989		db_printf("NULL ");
990	else
991		db_printf("%jx ", l->start);
992	db_printf("right ");
993	r = RB_RIGHT(entry, rb_entry);
994	if (r == NULL)
995		db_printf("NULL");
996	else
997		db_printf("%jx", r->start);
998	db_printf("\n");
999}
1000
1001static void
1002dmar_print_ctx(struct dmar_ctx *ctx, bool show_mappings)
1003{
1004	struct dmar_map_entry *entry;
1005
1006	db_printf(
1007	    "  @%p pci%d:%d:%d dom %d mgaw %d agaw %d pglvl %d end %jx\n"
1008	    "    refs %d flags %x pgobj %p map_ents %u loads %lu unloads %lu\n",
1009	    ctx, ctx->bus, ctx->slot, ctx->func, ctx->domain, ctx->mgaw,
1010	    ctx->agaw, ctx->pglvl, (uintmax_t)ctx->end, ctx->refs,
1011	    ctx->flags, ctx->pgtbl_obj, ctx->entries_cnt, ctx->loads,
1012	    ctx->unloads);
1013	if (!show_mappings)
1014		return;
1015	db_printf("    mapped:\n");
1016	RB_FOREACH(entry, dmar_gas_entries_tree, &ctx->rb_root) {
1017		dmar_print_ctx_entry(entry);
1018		if (db_pager_quit)
1019			break;
1020	}
1021	if (db_pager_quit)
1022		return;
1023	db_printf("    unloading:\n");
1024	TAILQ_FOREACH(entry, &ctx->unload_entries, dmamap_link) {
1025		dmar_print_ctx_entry(entry);
1026		if (db_pager_quit)
1027			break;
1028	}
1029}
1030
1031DB_FUNC(dmar_ctx, db_dmar_print_ctx, db_show_table, CS_OWN, NULL)
1032{
1033	struct dmar_unit *unit;
1034	struct dmar_ctx *ctx;
1035	bool show_mappings, valid;
1036	int domain, bus, device, function, i, t;
1037	db_expr_t radix;
1038
1039	valid = false;
1040	radix = db_radix;
1041	db_radix = 10;
1042	t = db_read_token();
1043	if (t == tSLASH) {
1044		t = db_read_token();
1045		if (t != tIDENT) {
1046			db_printf("Bad modifier\n");
1047			db_radix = radix;
1048			db_skip_to_eol();
1049			return;
1050		}
1051		show_mappings = strchr(db_tok_string, 'm') != NULL;
1052		t = db_read_token();
1053	} else {
1054		show_mappings = false;
1055	}
1056	if (t == tNUMBER) {
1057		domain = db_tok_number;
1058		t = db_read_token();
1059		if (t == tNUMBER) {
1060			bus = db_tok_number;
1061			t = db_read_token();
1062			if (t == tNUMBER) {
1063				device = db_tok_number;
1064				t = db_read_token();
1065				if (t == tNUMBER) {
1066					function = db_tok_number;
1067					valid = true;
1068				}
1069			}
1070		}
1071	}
1072			db_radix = radix;
1073	db_skip_to_eol();
1074	if (!valid) {
1075		db_printf("usage: show dmar_ctx [/m] "
1076		    "<domain> <bus> <device> <func>\n");
1077		return;
1078	}
1079	for (i = 0; i < dmar_devcnt; i++) {
1080		unit = device_get_softc(dmar_devs[i]);
1081		LIST_FOREACH(ctx, &unit->contexts, link) {
1082			if (domain == unit->segment && bus == ctx->bus &&
1083			    device == ctx->slot && function == ctx->func) {
1084				dmar_print_ctx(ctx, show_mappings);
1085				goto out;
1086			}
1087		}
1088	}
1089out:;
1090}
1091
1092static void
1093dmar_print_one(int idx, bool show_ctxs, bool show_mappings)
1094{
1095	struct dmar_unit *unit;
1096	struct dmar_ctx *ctx;
1097	int i, frir;
1098
1099	unit = device_get_softc(dmar_devs[idx]);
1100	db_printf("dmar%d at %p, root at 0x%jx, ver 0x%x\n", unit->unit, unit,
1101	    dmar_read8(unit, DMAR_RTADDR_REG), dmar_read4(unit, DMAR_VER_REG));
1102	db_printf("cap 0x%jx ecap 0x%jx gsts 0x%x fsts 0x%x fectl 0x%x\n",
1103	    (uintmax_t)dmar_read8(unit, DMAR_CAP_REG),
1104	    (uintmax_t)dmar_read8(unit, DMAR_ECAP_REG),
1105	    dmar_read4(unit, DMAR_GSTS_REG),
1106	    dmar_read4(unit, DMAR_FSTS_REG),
1107	    dmar_read4(unit, DMAR_FECTL_REG));
1108	db_printf("fed 0x%x fea 0x%x feua 0x%x\n",
1109	    dmar_read4(unit, DMAR_FEDATA_REG),
1110	    dmar_read4(unit, DMAR_FEADDR_REG),
1111	    dmar_read4(unit, DMAR_FEUADDR_REG));
1112	db_printf("primary fault log:\n");
1113	for (i = 0; i < DMAR_CAP_NFR(unit->hw_cap); i++) {
1114		frir = (DMAR_CAP_FRO(unit->hw_cap) + i) * 16;
1115		db_printf("  %d at 0x%x: %jx %jx\n", i, frir,
1116		    (uintmax_t)dmar_read8(unit, frir),
1117		    (uintmax_t)dmar_read8(unit, frir + 8));
1118	}
1119	if (DMAR_HAS_QI(unit)) {
1120		db_printf("ied 0x%x iea 0x%x ieua 0x%x\n",
1121		    dmar_read4(unit, DMAR_IEDATA_REG),
1122		    dmar_read4(unit, DMAR_IEADDR_REG),
1123		    dmar_read4(unit, DMAR_IEUADDR_REG));
1124		if (unit->qi_enabled) {
1125			db_printf("qi is enabled: queue @0x%jx (IQA 0x%jx) "
1126			    "size 0x%jx\n"
1127		    "  head 0x%x tail 0x%x avail 0x%x status 0x%x ctrl 0x%x\n"
1128		    "  hw compl 0x%x@%p/phys@%jx next seq 0x%x gen 0x%x\n",
1129			    (uintmax_t)unit->inv_queue,
1130			    (uintmax_t)dmar_read8(unit, DMAR_IQA_REG),
1131			    (uintmax_t)unit->inv_queue_size,
1132			    dmar_read4(unit, DMAR_IQH_REG),
1133			    dmar_read4(unit, DMAR_IQT_REG),
1134			    unit->inv_queue_avail,
1135			    dmar_read4(unit, DMAR_ICS_REG),
1136			    dmar_read4(unit, DMAR_IECTL_REG),
1137			    unit->inv_waitd_seq_hw,
1138			    &unit->inv_waitd_seq_hw,
1139			    (uintmax_t)unit->inv_waitd_seq_hw_phys,
1140			    unit->inv_waitd_seq,
1141			    unit->inv_waitd_gen);
1142		} else {
1143			db_printf("qi is disabled\n");
1144		}
1145	}
1146	if (show_ctxs) {
1147		db_printf("contexts:\n");
1148		LIST_FOREACH(ctx, &unit->contexts, link) {
1149			dmar_print_ctx(ctx, show_mappings);
1150			if (db_pager_quit)
1151				break;
1152		}
1153	}
1154}
1155
1156DB_SHOW_COMMAND(dmar, db_dmar_print)
1157{
1158	bool show_ctxs, show_mappings;
1159
1160	show_ctxs = strchr(modif, 'c') != NULL;
1161	show_mappings = strchr(modif, 'm') != NULL;
1162	if (!have_addr) {
1163		db_printf("usage: show dmar [/c] [/m] index\n");
1164		return;
1165	}
1166	dmar_print_one((int)addr, show_ctxs, show_mappings);
1167}
1168
1169DB_SHOW_ALL_COMMAND(dmars, db_show_all_dmars)
1170{
1171	int i;
1172	bool show_ctxs, show_mappings;
1173
1174	show_ctxs = strchr(modif, 'c') != NULL;
1175	show_mappings = strchr(modif, 'm') != NULL;
1176
1177	for (i = 0; i < dmar_devcnt; i++) {
1178		dmar_print_one(i, show_ctxs, show_mappings);
1179		if (db_pager_quit)
1180			break;
1181	}
1182}
1183#endif
1184