xref: /freebsd-src/sys/x86/iommu/amd_drv.c (revision 58d1fdfaa90a5365a5f951e2ac80e0fce02ca9bb)
10f5116d7SKonstantin Belousov /*-
20f5116d7SKonstantin Belousov  * SPDX-License-Identifier: BSD-2-Clause
30f5116d7SKonstantin Belousov  *
40f5116d7SKonstantin Belousov  * Copyright (c) 2024 The FreeBSD Foundation
50f5116d7SKonstantin Belousov  *
60f5116d7SKonstantin Belousov  * This software was developed by Konstantin Belousov <kib@FreeBSD.org>
70f5116d7SKonstantin Belousov  * under sponsorship from the FreeBSD Foundation.
80f5116d7SKonstantin Belousov  *
90f5116d7SKonstantin Belousov  * Redistribution and use in source and binary forms, with or without
100f5116d7SKonstantin Belousov  * modification, are permitted provided that the following conditions
110f5116d7SKonstantin Belousov  * are met:
120f5116d7SKonstantin Belousov  * 1. Redistributions of source code must retain the above copyright
130f5116d7SKonstantin Belousov  *    notice, this list of conditions and the following disclaimer.
140f5116d7SKonstantin Belousov  * 2. Redistributions in binary form must reproduce the above copyright
150f5116d7SKonstantin Belousov  *    notice, this list of conditions and the following disclaimer in the
160f5116d7SKonstantin Belousov  *    documentation and/or other materials provided with the distribution.
170f5116d7SKonstantin Belousov  *
180f5116d7SKonstantin Belousov  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
190f5116d7SKonstantin Belousov  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
200f5116d7SKonstantin Belousov  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
210f5116d7SKonstantin Belousov  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
220f5116d7SKonstantin Belousov  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
230f5116d7SKonstantin Belousov  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
240f5116d7SKonstantin Belousov  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
250f5116d7SKonstantin Belousov  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
260f5116d7SKonstantin Belousov  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
270f5116d7SKonstantin Belousov  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
280f5116d7SKonstantin Belousov  * SUCH DAMAGE.
290f5116d7SKonstantin Belousov  */
300f5116d7SKonstantin Belousov 
310f5116d7SKonstantin Belousov #include "opt_acpi.h"
320f5116d7SKonstantin Belousov #include "opt_ddb.h"
330f5116d7SKonstantin Belousov 
340f5116d7SKonstantin Belousov #include <sys/param.h>
350f5116d7SKonstantin Belousov #include <sys/bus.h>
360f5116d7SKonstantin Belousov #include <sys/domainset.h>
370f5116d7SKonstantin Belousov #include <sys/kernel.h>
380f5116d7SKonstantin Belousov #include <sys/lock.h>
390f5116d7SKonstantin Belousov #include <sys/malloc.h>
400f5116d7SKonstantin Belousov #include <sys/memdesc.h>
410f5116d7SKonstantin Belousov #include <sys/module.h>
420f5116d7SKonstantin Belousov #include <sys/mutex.h>
430f5116d7SKonstantin Belousov #include <sys/rman.h>
440f5116d7SKonstantin Belousov #include <sys/rwlock.h>
450f5116d7SKonstantin Belousov #include <sys/smp.h>
460f5116d7SKonstantin Belousov #include <sys/taskqueue.h>
470f5116d7SKonstantin Belousov #include <sys/tree.h>
480f5116d7SKonstantin Belousov #include <sys/vmem.h>
490f5116d7SKonstantin Belousov #include <vm/vm.h>
500f5116d7SKonstantin Belousov #include <vm/vm_extern.h>
510f5116d7SKonstantin Belousov #include <vm/vm_object.h>
520f5116d7SKonstantin Belousov #include <vm/vm_page.h>
530f5116d7SKonstantin Belousov #include <vm/vm_pageout.h>
540f5116d7SKonstantin Belousov #include <vm/vm_pager.h>
550f5116d7SKonstantin Belousov #include <contrib/dev/acpica/include/acpi.h>
560f5116d7SKonstantin Belousov #include <contrib/dev/acpica/include/accommon.h>
570f5116d7SKonstantin Belousov #include <dev/acpica/acpivar.h>
580f5116d7SKonstantin Belousov #include <dev/pci/pcireg.h>
590f5116d7SKonstantin Belousov #include <dev/pci/pcivar.h>
600f5116d7SKonstantin Belousov #include <machine/bus.h>
610f5116d7SKonstantin Belousov #include <machine/pci_cfgreg.h>
620f5116d7SKonstantin Belousov #include "pcib_if.h"
630f5116d7SKonstantin Belousov #include <machine/intr_machdep.h>
640f5116d7SKonstantin Belousov #include <machine/md_var.h>
650f5116d7SKonstantin Belousov #include <machine/cputypes.h>
660f5116d7SKonstantin Belousov #include <x86/apicreg.h>
670f5116d7SKonstantin Belousov #include <x86/apicvar.h>
680f5116d7SKonstantin Belousov #include <dev/iommu/iommu.h>
690f5116d7SKonstantin Belousov #include <x86/iommu/amd_reg.h>
700f5116d7SKonstantin Belousov #include <x86/iommu/x86_iommu.h>
710f5116d7SKonstantin Belousov #include <x86/iommu/amd_iommu.h>
720f5116d7SKonstantin Belousov 
730f5116d7SKonstantin Belousov static int amdiommu_enable = 0;
740f5116d7SKonstantin Belousov 
750f5116d7SKonstantin Belousov /*
760f5116d7SKonstantin Belousov  * All enumerated AMD IOMMU units.
770f5116d7SKonstantin Belousov  * Access is unlocked, the list is not modified after early
780f5116d7SKonstantin Belousov  * single-threaded startup.
790f5116d7SKonstantin Belousov  */
800f5116d7SKonstantin Belousov static TAILQ_HEAD(, amdiommu_unit) amdiommu_units =
810f5116d7SKonstantin Belousov     TAILQ_HEAD_INITIALIZER(amdiommu_units);
820f5116d7SKonstantin Belousov 
830f5116d7SKonstantin Belousov typedef bool (*amdiommu_itercc_t)(void *, void *);
840f5116d7SKonstantin Belousov typedef bool (*amdiommu_iter40_t)(ACPI_IVRS_HARDWARE2 *, void *);
850f5116d7SKonstantin Belousov typedef bool (*amdiommu_iter11_t)(ACPI_IVRS_HARDWARE2 *, void *);
860f5116d7SKonstantin Belousov typedef bool (*amdiommu_iter10_t)(ACPI_IVRS_HARDWARE1 *, void *);
870f5116d7SKonstantin Belousov 
880f5116d7SKonstantin Belousov static bool
890f5116d7SKonstantin Belousov amdiommu_ivrs_iterate_tbl_typed(amdiommu_itercc_t iter, void *arg,
900f5116d7SKonstantin Belousov     int type, ACPI_TABLE_IVRS *ivrs_tbl)
910f5116d7SKonstantin Belousov {
920f5116d7SKonstantin Belousov 	char *ptr, *ptrend;
930f5116d7SKonstantin Belousov 	bool done;
940f5116d7SKonstantin Belousov 
950f5116d7SKonstantin Belousov 	done = false;
960f5116d7SKonstantin Belousov 	ptr = (char *)ivrs_tbl + sizeof(*ivrs_tbl);
970f5116d7SKonstantin Belousov 	ptrend = (char *)ivrs_tbl + ivrs_tbl->Header.Length;
980f5116d7SKonstantin Belousov 	for (;;) {
990f5116d7SKonstantin Belousov 		ACPI_IVRS_HEADER *ivrsh;
1000f5116d7SKonstantin Belousov 
1010f5116d7SKonstantin Belousov 		if (ptr >= ptrend)
1020f5116d7SKonstantin Belousov 			break;
1030f5116d7SKonstantin Belousov 		ivrsh = (ACPI_IVRS_HEADER *)ptr;
1040f5116d7SKonstantin Belousov 		if (ivrsh->Length <= 0) {
1050f5116d7SKonstantin Belousov 			printf("amdiommu_iterate_tbl: corrupted IVRS table, "
1060f5116d7SKonstantin Belousov 			    "length %d\n", ivrsh->Length);
1070f5116d7SKonstantin Belousov 			break;
1080f5116d7SKonstantin Belousov 		}
1090f5116d7SKonstantin Belousov 		ptr += ivrsh->Length;
1100f5116d7SKonstantin Belousov 		if (ivrsh->Type ==  type) {
1110f5116d7SKonstantin Belousov 			done = iter((void *)ivrsh, arg);
1120f5116d7SKonstantin Belousov 			if (done)
1130f5116d7SKonstantin Belousov 				break;
1140f5116d7SKonstantin Belousov 		}
1150f5116d7SKonstantin Belousov 	}
1160f5116d7SKonstantin Belousov 	return (done);
1170f5116d7SKonstantin Belousov }
1180f5116d7SKonstantin Belousov 
1190f5116d7SKonstantin Belousov /*
1200f5116d7SKonstantin Belousov  * Walk over IVRS, calling callback iterators following priority:
1210f5116d7SKonstantin Belousov  * 0x40, then 0x11, then 0x10 subtable.  First iterator returning true
1220f5116d7SKonstantin Belousov  * ends the walk.
1230f5116d7SKonstantin Belousov  * Returns true if any iterator returned true, otherwise false.
1240f5116d7SKonstantin Belousov  */
1250f5116d7SKonstantin Belousov static bool
1260f5116d7SKonstantin Belousov amdiommu_ivrs_iterate_tbl(amdiommu_iter40_t iter40, amdiommu_iter11_t iter11,
1270f5116d7SKonstantin Belousov     amdiommu_iter10_t iter10, void *arg)
1280f5116d7SKonstantin Belousov {
1290f5116d7SKonstantin Belousov 	ACPI_TABLE_IVRS *ivrs_tbl;
1300f5116d7SKonstantin Belousov 	ACPI_STATUS status;
1310f5116d7SKonstantin Belousov 	bool done;
1320f5116d7SKonstantin Belousov 
1330f5116d7SKonstantin Belousov 	status = AcpiGetTable(ACPI_SIG_IVRS, 1,
1340f5116d7SKonstantin Belousov 	    (ACPI_TABLE_HEADER **)&ivrs_tbl);
1350f5116d7SKonstantin Belousov 	if (ACPI_FAILURE(status))
1360f5116d7SKonstantin Belousov 		return (false);
1370f5116d7SKonstantin Belousov 	done = false;
1380f5116d7SKonstantin Belousov 	if (iter40 != NULL)
1390f5116d7SKonstantin Belousov 		done = amdiommu_ivrs_iterate_tbl_typed(
1400f5116d7SKonstantin Belousov 		    (amdiommu_itercc_t)iter40, arg,
1410f5116d7SKonstantin Belousov 		    ACPI_IVRS_TYPE_HARDWARE3, ivrs_tbl);
1420f5116d7SKonstantin Belousov 	if (!done && iter11 != NULL)
1430f5116d7SKonstantin Belousov 		done = amdiommu_ivrs_iterate_tbl_typed(
1440f5116d7SKonstantin Belousov 		    (amdiommu_itercc_t)iter11, arg, ACPI_IVRS_TYPE_HARDWARE2,
1450f5116d7SKonstantin Belousov 		    ivrs_tbl);
1460f5116d7SKonstantin Belousov 	if (!done && iter10 != NULL)
1470f5116d7SKonstantin Belousov 		done = amdiommu_ivrs_iterate_tbl_typed(
1480f5116d7SKonstantin Belousov 		    (amdiommu_itercc_t)iter10, arg, ACPI_IVRS_TYPE_HARDWARE1,
1490f5116d7SKonstantin Belousov 		    ivrs_tbl);
1500f5116d7SKonstantin Belousov 	AcpiPutTable((ACPI_TABLE_HEADER *)ivrs_tbl);
1510f5116d7SKonstantin Belousov 	return (done);
1520f5116d7SKonstantin Belousov }
1530f5116d7SKonstantin Belousov 
1540f5116d7SKonstantin Belousov struct ivhd_lookup_data  {
1550f5116d7SKonstantin Belousov 	struct amdiommu_unit *sc;
1560f5116d7SKonstantin Belousov 	uint16_t devid;
1570f5116d7SKonstantin Belousov };
1580f5116d7SKonstantin Belousov 
1590f5116d7SKonstantin Belousov static bool
1600f5116d7SKonstantin Belousov ivrs_lookup_ivhd_0x40(ACPI_IVRS_HARDWARE2 *h2, void *arg)
1610f5116d7SKonstantin Belousov {
1620f5116d7SKonstantin Belousov 	struct ivhd_lookup_data *ildp;
1630f5116d7SKonstantin Belousov 
1640f5116d7SKonstantin Belousov 	KASSERT(h2->Header.Type == ACPI_IVRS_TYPE_HARDWARE2 ||
1650f5116d7SKonstantin Belousov 	    h2->Header.Type == ACPI_IVRS_TYPE_HARDWARE3,
1660f5116d7SKonstantin Belousov 	    ("Misparsed IVHD, h2 type %#x", h2->Header.Type));
1670f5116d7SKonstantin Belousov 
1680f5116d7SKonstantin Belousov 	ildp = arg;
1690f5116d7SKonstantin Belousov 	if (h2->Header.DeviceId != ildp->devid)
1700f5116d7SKonstantin Belousov 		return (false);
1710f5116d7SKonstantin Belousov 
1720f5116d7SKonstantin Belousov 	ildp->sc->unit_dom = h2->PciSegmentGroup;
1730f5116d7SKonstantin Belousov 	ildp->sc->efr = h2->EfrRegisterImage;
1740f5116d7SKonstantin Belousov 	return (true);
1750f5116d7SKonstantin Belousov }
1760f5116d7SKonstantin Belousov 
1770f5116d7SKonstantin Belousov static bool
1780f5116d7SKonstantin Belousov ivrs_lookup_ivhd_0x10(ACPI_IVRS_HARDWARE1 *h1, void *arg)
1790f5116d7SKonstantin Belousov {
1800f5116d7SKonstantin Belousov 	struct ivhd_lookup_data *ildp;
1810f5116d7SKonstantin Belousov 
1820f5116d7SKonstantin Belousov 	KASSERT(h1->Header.Type == ACPI_IVRS_TYPE_HARDWARE1,
1830f5116d7SKonstantin Belousov 	    ("Misparsed IVHD, h1 type %#x", h1->Header.Type));
1840f5116d7SKonstantin Belousov 
1850f5116d7SKonstantin Belousov 	ildp = arg;
1860f5116d7SKonstantin Belousov 	if (h1->Header.DeviceId != ildp->devid)
1870f5116d7SKonstantin Belousov 		return (false);
1880f5116d7SKonstantin Belousov 
1890f5116d7SKonstantin Belousov 	ildp->sc->unit_dom = h1->PciSegmentGroup;
1900f5116d7SKonstantin Belousov 	return (true);
1910f5116d7SKonstantin Belousov }
1920f5116d7SKonstantin Belousov 
1930f5116d7SKonstantin Belousov static u_int
1940f5116d7SKonstantin Belousov amdiommu_devtbl_sz(struct amdiommu_unit *sc __unused)
1950f5116d7SKonstantin Belousov {
1960f5116d7SKonstantin Belousov 	return (sizeof(struct amdiommu_dte) * (1 << 16));
1970f5116d7SKonstantin Belousov }
1980f5116d7SKonstantin Belousov 
1990f5116d7SKonstantin Belousov static void
2000f5116d7SKonstantin Belousov amdiommu_free_dev_tbl(struct amdiommu_unit *sc)
2010f5116d7SKonstantin Belousov {
2020f5116d7SKonstantin Belousov 	u_int devtbl_sz;
2030f5116d7SKonstantin Belousov 
2040f5116d7SKonstantin Belousov 	devtbl_sz = amdiommu_devtbl_sz(sc);
2050f5116d7SKonstantin Belousov 	pmap_qremove((vm_offset_t)sc->dev_tbl, atop(devtbl_sz));
2060f5116d7SKonstantin Belousov 	kva_free((vm_offset_t)sc->dev_tbl, devtbl_sz);
2070f5116d7SKonstantin Belousov 	sc->dev_tbl = NULL;
2080f5116d7SKonstantin Belousov 	vm_object_deallocate(sc->devtbl_obj);
2090f5116d7SKonstantin Belousov 	sc->devtbl_obj = NULL;
2100f5116d7SKonstantin Belousov }
2110f5116d7SKonstantin Belousov 
2120f5116d7SKonstantin Belousov static int
2130f5116d7SKonstantin Belousov amdiommu_create_dev_tbl(struct amdiommu_unit *sc)
2140f5116d7SKonstantin Belousov {
2150f5116d7SKonstantin Belousov 	vm_offset_t seg_vaddr;
2160f5116d7SKonstantin Belousov 	u_int devtbl_sz, dom, i, reclaimno, segnum_log, segnum, seg_sz;
2170f5116d7SKonstantin Belousov 	int error;
2180f5116d7SKonstantin Belousov 
21931e796c8SJason A. Harmening 	static const int devtab_base_regs[] = {
22031e796c8SJason A. Harmening 		AMDIOMMU_DEVTAB_BASE,
22131e796c8SJason A. Harmening 		AMDIOMMU_DEVTAB_S1_BASE,
22231e796c8SJason A. Harmening 		AMDIOMMU_DEVTAB_S2_BASE,
22331e796c8SJason A. Harmening 		AMDIOMMU_DEVTAB_S3_BASE,
22431e796c8SJason A. Harmening 		AMDIOMMU_DEVTAB_S4_BASE,
22531e796c8SJason A. Harmening 		AMDIOMMU_DEVTAB_S5_BASE,
22631e796c8SJason A. Harmening 		AMDIOMMU_DEVTAB_S6_BASE,
22731e796c8SJason A. Harmening 		AMDIOMMU_DEVTAB_S7_BASE
22831e796c8SJason A. Harmening 	};
22931e796c8SJason A. Harmening 
2300f5116d7SKonstantin Belousov 	segnum_log = (sc->efr & AMDIOMMU_EFR_DEVTBLSEG_MASK) >>
2310f5116d7SKonstantin Belousov 	    AMDIOMMU_EFR_DEVTBLSEG_SHIFT;
2320f5116d7SKonstantin Belousov 	segnum = 1 << segnum_log;
2330f5116d7SKonstantin Belousov 
23431e796c8SJason A. Harmening 	KASSERT(segnum <= nitems(devtab_base_regs),
23531e796c8SJason A. Harmening 	    ("%s: unsupported devtab segment count %u", __func__, segnum));
23631e796c8SJason A. Harmening 
2370f5116d7SKonstantin Belousov 	devtbl_sz = amdiommu_devtbl_sz(sc);
2380f5116d7SKonstantin Belousov 	seg_sz = devtbl_sz / segnum;
2390f5116d7SKonstantin Belousov 	sc->devtbl_obj = vm_pager_allocate(OBJT_PHYS, NULL, atop(devtbl_sz),
2400f5116d7SKonstantin Belousov 	    VM_PROT_ALL, 0, NULL);
2410f5116d7SKonstantin Belousov 	if (bus_get_domain(sc->iommu.dev, &dom) == 0)
2420f5116d7SKonstantin Belousov 		sc->devtbl_obj->domain.dr_policy = DOMAINSET_PREF(dom);
2430f5116d7SKonstantin Belousov 
2440f5116d7SKonstantin Belousov 	sc->hw_ctrl &= ~AMDIOMMU_CTRL_DEVTABSEG_MASK;
2450f5116d7SKonstantin Belousov 	sc->hw_ctrl |= (uint64_t)segnum_log << ilog2(AMDIOMMU_CTRL_DEVTABSEG_2);
2460f5116d7SKonstantin Belousov 	sc->hw_ctrl |= AMDIOMMU_CTRL_COHERENT;
2470f5116d7SKonstantin Belousov 	amdiommu_write8(sc, AMDIOMMU_CTRL, sc->hw_ctrl);
2480f5116d7SKonstantin Belousov 
2490f5116d7SKonstantin Belousov 	seg_vaddr = kva_alloc(devtbl_sz);
2500f5116d7SKonstantin Belousov 	if (seg_vaddr == 0)
2510f5116d7SKonstantin Belousov 		return (ENOMEM);
2520f5116d7SKonstantin Belousov 	sc->dev_tbl = (void *)seg_vaddr;
2530f5116d7SKonstantin Belousov 
2540f5116d7SKonstantin Belousov 	for (i = 0; i < segnum; i++) {
2550f5116d7SKonstantin Belousov 		vm_page_t m;
2560f5116d7SKonstantin Belousov 		uint64_t rval;
2570f5116d7SKonstantin Belousov 
2580f5116d7SKonstantin Belousov 		for (reclaimno = 0; reclaimno < 3; reclaimno++) {
2590f5116d7SKonstantin Belousov 			VM_OBJECT_WLOCK(sc->devtbl_obj);
2600f5116d7SKonstantin Belousov 			m = vm_page_alloc_contig(sc->devtbl_obj,
2610f5116d7SKonstantin Belousov 			    i * atop(seg_sz),
2620f5116d7SKonstantin Belousov 			    VM_ALLOC_NORMAL | VM_ALLOC_NOBUSY,
2630f5116d7SKonstantin Belousov 			    atop(seg_sz), 0, ~0ul, IOMMU_PAGE_SIZE, 0,
2640f5116d7SKonstantin Belousov 			    VM_MEMATTR_DEFAULT);
2650f5116d7SKonstantin Belousov 			VM_OBJECT_WUNLOCK(sc->devtbl_obj);
2660f5116d7SKonstantin Belousov 			if (m != NULL)
2670f5116d7SKonstantin Belousov 				break;
2680f5116d7SKonstantin Belousov 			error = vm_page_reclaim_contig(VM_ALLOC_NORMAL,
2690f5116d7SKonstantin Belousov 			    atop(seg_sz), 0, ~0ul, IOMMU_PAGE_SIZE, 0);
2700f5116d7SKonstantin Belousov 			if (error != 0)
2710f5116d7SKonstantin Belousov 				vm_wait(sc->devtbl_obj);
2720f5116d7SKonstantin Belousov 		}
2730f5116d7SKonstantin Belousov 		if (m == NULL) {
2740f5116d7SKonstantin Belousov 			amdiommu_free_dev_tbl(sc);
2750f5116d7SKonstantin Belousov 			return (ENOMEM);
2760f5116d7SKonstantin Belousov 		}
2770f5116d7SKonstantin Belousov 
2780f5116d7SKonstantin Belousov 		rval = VM_PAGE_TO_PHYS(m) | (atop(seg_sz) - 1);
2790f5116d7SKonstantin Belousov 		for (u_int j = 0; j < atop(seg_sz);
2800f5116d7SKonstantin Belousov 		     j++, seg_vaddr += PAGE_SIZE, m++) {
2810f5116d7SKonstantin Belousov 			pmap_zero_page(m);
2820f5116d7SKonstantin Belousov 			pmap_qenter(seg_vaddr, &m, 1);
2830f5116d7SKonstantin Belousov 		}
28431e796c8SJason A. Harmening 		amdiommu_write8(sc, devtab_base_regs[i], rval);
2850f5116d7SKonstantin Belousov 	}
2860f5116d7SKonstantin Belousov 
2870f5116d7SKonstantin Belousov 	return (0);
2880f5116d7SKonstantin Belousov }
2890f5116d7SKonstantin Belousov 
2900f5116d7SKonstantin Belousov static int
2910f5116d7SKonstantin Belousov amdiommu_cmd_event_intr(void *arg)
2920f5116d7SKonstantin Belousov {
2930f5116d7SKonstantin Belousov 	struct amdiommu_unit *unit;
2940f5116d7SKonstantin Belousov 	uint64_t status;
2950f5116d7SKonstantin Belousov 
2960f5116d7SKonstantin Belousov 	unit = arg;
2970f5116d7SKonstantin Belousov 	status = amdiommu_read8(unit, AMDIOMMU_CMDEV_STATUS);
2980f5116d7SKonstantin Belousov 	if ((status & AMDIOMMU_CMDEVS_COMWAITINT) != 0) {
2990f5116d7SKonstantin Belousov 		amdiommu_write8(unit, AMDIOMMU_CMDEV_STATUS,
3000f5116d7SKonstantin Belousov 		    AMDIOMMU_CMDEVS_COMWAITINT);
3010f5116d7SKonstantin Belousov 		taskqueue_enqueue(unit->x86c.qi_taskqueue,
3020f5116d7SKonstantin Belousov 		    &unit->x86c.qi_task);
3030f5116d7SKonstantin Belousov 	}
3040f5116d7SKonstantin Belousov 	if ((status & (AMDIOMMU_CMDEVS_EVLOGINT |
3050f5116d7SKonstantin Belousov 	    AMDIOMMU_CMDEVS_EVOVRFLW)) != 0)
3060f5116d7SKonstantin Belousov 		amdiommu_event_intr(unit, status);
3070f5116d7SKonstantin Belousov 	return (FILTER_HANDLED);
3080f5116d7SKonstantin Belousov }
3090f5116d7SKonstantin Belousov 
3100f5116d7SKonstantin Belousov static int
3110f5116d7SKonstantin Belousov amdiommu_setup_intr(struct amdiommu_unit *sc)
3120f5116d7SKonstantin Belousov {
3130f5116d7SKonstantin Belousov 	int error, msi_count, msix_count;
3140f5116d7SKonstantin Belousov 
3150f5116d7SKonstantin Belousov 	msi_count = pci_msi_count(sc->iommu.dev);
3160f5116d7SKonstantin Belousov 	msix_count = pci_msix_count(sc->iommu.dev);
3170f5116d7SKonstantin Belousov 	if (msi_count == 0 && msix_count == 0) {
3180f5116d7SKonstantin Belousov 		device_printf(sc->iommu.dev, "needs MSI-class intr\n");
3190f5116d7SKonstantin Belousov 		return (ENXIO);
3200f5116d7SKonstantin Belousov 	}
3210f5116d7SKonstantin Belousov 
3220f5116d7SKonstantin Belousov #if 0
3230f5116d7SKonstantin Belousov 	/*
3240f5116d7SKonstantin Belousov 	 * XXXKIB how MSI-X is supposed to be organized for BAR-less
3250f5116d7SKonstantin Belousov 	 * function?  Practically available hardware implements only
3260f5116d7SKonstantin Belousov 	 * one IOMMU unit per function, and uses MSI.
3270f5116d7SKonstantin Belousov 	 */
3280f5116d7SKonstantin Belousov 	if (msix_count > 0) {
3290f5116d7SKonstantin Belousov 		sc->msix_table = bus_alloc_resource_any(sc->iommu.dev,
3300f5116d7SKonstantin Belousov 		    SYS_RES_MEMORY, &sc->msix_tab_rid, RF_ACTIVE);
3310f5116d7SKonstantin Belousov 		if (sc->msix_table == NULL)
3320f5116d7SKonstantin Belousov 			return (ENXIO);
3330f5116d7SKonstantin Belousov 
3340f5116d7SKonstantin Belousov 		if (sc->msix_pba_rid != sc->msix_tab_rid) {
3350f5116d7SKonstantin Belousov 			/* Separate BAR for PBA */
3360f5116d7SKonstantin Belousov 			sc->msix_pba = bus_alloc_resource_any(sc->iommu.dev,
3370f5116d7SKonstantin Belousov 			    SYS_RES_MEMORY,
3380f5116d7SKonstantin Belousov 			    &sc->msix_pba_rid, RF_ACTIVE);
3390f5116d7SKonstantin Belousov 			if (sc->msix_pba == NULL) {
3400f5116d7SKonstantin Belousov 				bus_release_resource(sc->iommu.dev,
3410f5116d7SKonstantin Belousov 				    SYS_RES_MEMORY, &sc->msix_tab_rid,
3420f5116d7SKonstantin Belousov 				    sc->msix_table);
3430f5116d7SKonstantin Belousov 				return (ENXIO);
3440f5116d7SKonstantin Belousov 			}
3450f5116d7SKonstantin Belousov 		}
3460f5116d7SKonstantin Belousov 	}
3470f5116d7SKonstantin Belousov #endif
3480f5116d7SKonstantin Belousov 
3490f5116d7SKonstantin Belousov 	error = ENXIO;
3500f5116d7SKonstantin Belousov 	if (msix_count > 0) {
3510f5116d7SKonstantin Belousov 		error = pci_alloc_msix(sc->iommu.dev, &msix_count);
3520f5116d7SKonstantin Belousov 		if (error == 0)
3530f5116d7SKonstantin Belousov 			sc->numirqs = msix_count;
3540f5116d7SKonstantin Belousov 	}
3550f5116d7SKonstantin Belousov 	if (error != 0 && msi_count > 0) {
3560f5116d7SKonstantin Belousov 		error = pci_alloc_msi(sc->iommu.dev, &msi_count);
3570f5116d7SKonstantin Belousov 		if (error == 0)
3580f5116d7SKonstantin Belousov 			sc->numirqs = msi_count;
3590f5116d7SKonstantin Belousov 	}
3600f5116d7SKonstantin Belousov 	if (error != 0) {
3610f5116d7SKonstantin Belousov 		device_printf(sc->iommu.dev,
3620f5116d7SKonstantin Belousov 		    "Failed to allocate MSI/MSI-x (%d)\n", error);
3630f5116d7SKonstantin Belousov 		return (ENXIO);
3640f5116d7SKonstantin Belousov 	}
3650f5116d7SKonstantin Belousov 
3660f5116d7SKonstantin Belousov 	/*
3670f5116d7SKonstantin Belousov 	 * XXXKIB Spec states that MISC0.MsiNum must be zero for IOMMU
3680f5116d7SKonstantin Belousov 	 * using MSI interrupts.  But at least one BIOS programmed '2'
3690f5116d7SKonstantin Belousov 	 * there, making driver use wrong rid and causing
3700f5116d7SKonstantin Belousov 	 * command/event interrupt ignored as stray.  Try to fix it
3710f5116d7SKonstantin Belousov 	 * with dirty force by assuming MsiNum is zero for MSI.
3720f5116d7SKonstantin Belousov 	 */
3730f5116d7SKonstantin Belousov 	sc->irq_cmdev_rid = 1;
3740f5116d7SKonstantin Belousov 	if (msix_count > 0) {
3750f5116d7SKonstantin Belousov 		sc->irq_cmdev_rid += pci_read_config(sc->iommu.dev,
3760f5116d7SKonstantin Belousov 		    sc->seccap_reg + PCIR_AMDIOMMU_MISC0, 4) &
3770f5116d7SKonstantin Belousov 		    PCIM_AMDIOMMU_MISC0_MSINUM_MASK;
3780f5116d7SKonstantin Belousov 	}
3790f5116d7SKonstantin Belousov 
3800f5116d7SKonstantin Belousov 	sc->irq_cmdev = bus_alloc_resource_any(sc->iommu.dev, SYS_RES_IRQ,
3810f5116d7SKonstantin Belousov 	    &sc->irq_cmdev_rid, RF_SHAREABLE | RF_ACTIVE);
3820f5116d7SKonstantin Belousov 	if (sc->irq_cmdev == NULL) {
3830f5116d7SKonstantin Belousov 		device_printf(sc->iommu.dev,
3840f5116d7SKonstantin Belousov 		    "unable to map CMD/EV interrupt\n");
3850f5116d7SKonstantin Belousov 		return (ENXIO);
3860f5116d7SKonstantin Belousov 	}
3870f5116d7SKonstantin Belousov 	error = bus_setup_intr(sc->iommu.dev, sc->irq_cmdev,
3880f5116d7SKonstantin Belousov 	    INTR_TYPE_MISC, amdiommu_cmd_event_intr, NULL, sc,
3890f5116d7SKonstantin Belousov 	    &sc->irq_cmdev_cookie);
3900f5116d7SKonstantin Belousov 	if (error != 0) {
3910f5116d7SKonstantin Belousov 		device_printf(sc->iommu.dev,
3920f5116d7SKonstantin Belousov 		    "unable to setup interrupt (%d)\n", error);
3930f5116d7SKonstantin Belousov 		return (ENXIO);
3940f5116d7SKonstantin Belousov 	}
3950f5116d7SKonstantin Belousov 	bus_describe_intr(sc->iommu.dev, sc->irq_cmdev, sc->irq_cmdev_cookie,
3960f5116d7SKonstantin Belousov 	    "cmdev");
3970f5116d7SKonstantin Belousov 
3980f5116d7SKonstantin Belousov 	if (x2apic_mode) {
3990f5116d7SKonstantin Belousov 		AMDIOMMU_LOCK(sc);
4000f5116d7SKonstantin Belousov 		sc->hw_ctrl |= AMDIOMMU_CTRL_GA_EN | AMDIOMMU_CTRL_XT_EN;
4010f5116d7SKonstantin Belousov 		amdiommu_write8(sc, AMDIOMMU_CTRL, sc->hw_ctrl);
4020f5116d7SKonstantin Belousov 		// XXXKIB AMDIOMMU_CTRL_INTCAPXT_EN and program x2APIC_CTRL
4030f5116d7SKonstantin Belousov 		AMDIOMMU_UNLOCK(sc);
4040f5116d7SKonstantin Belousov 	}
4050f5116d7SKonstantin Belousov 
4060f5116d7SKonstantin Belousov 	return (0);
4070f5116d7SKonstantin Belousov }
4080f5116d7SKonstantin Belousov 
4090f5116d7SKonstantin Belousov static int
4100f5116d7SKonstantin Belousov amdiommu_probe(device_t dev)
4110f5116d7SKonstantin Belousov {
4120f5116d7SKonstantin Belousov 	int seccap_reg;
4130f5116d7SKonstantin Belousov 	int error;
4140f5116d7SKonstantin Belousov 	uint32_t cap_h, cap_type, cap_rev;
4150f5116d7SKonstantin Belousov 
4160f5116d7SKonstantin Belousov 	if (acpi_disabled("amdiommu"))
4170f5116d7SKonstantin Belousov 		return (ENXIO);
4180f5116d7SKonstantin Belousov 	TUNABLE_INT_FETCH("hw.amdiommu.enable", &amdiommu_enable);
4190f5116d7SKonstantin Belousov 	if (!amdiommu_enable)
4200f5116d7SKonstantin Belousov 		return (ENXIO);
4210f5116d7SKonstantin Belousov 	if (pci_get_class(dev) != PCIC_BASEPERIPH ||
4220f5116d7SKonstantin Belousov 	    pci_get_subclass(dev) != PCIS_BASEPERIPH_IOMMU)
4230f5116d7SKonstantin Belousov 		return (ENXIO);
4240f5116d7SKonstantin Belousov 
4250f5116d7SKonstantin Belousov 	error = pci_find_cap(dev, PCIY_SECDEV, &seccap_reg);
4260f5116d7SKonstantin Belousov 	if (error != 0 || seccap_reg == 0)
4270f5116d7SKonstantin Belousov 		return (ENXIO);
4280f5116d7SKonstantin Belousov 
4290f5116d7SKonstantin Belousov 	cap_h = pci_read_config(dev, seccap_reg + PCIR_AMDIOMMU_CAP_HEADER,
4300f5116d7SKonstantin Belousov 	    4);
4310f5116d7SKonstantin Belousov 	cap_type = cap_h & PCIM_AMDIOMMU_CAP_TYPE_MASK;
4320f5116d7SKonstantin Belousov 	cap_rev = cap_h & PCIM_AMDIOMMU_CAP_REV_MASK;
4330f5116d7SKonstantin Belousov 	if (cap_type != PCIM_AMDIOMMU_CAP_TYPE_VAL &&
4340f5116d7SKonstantin Belousov 	    cap_rev != PCIM_AMDIOMMU_CAP_REV_VAL)
4350f5116d7SKonstantin Belousov 		return (ENXIO);
4360f5116d7SKonstantin Belousov 
4370f5116d7SKonstantin Belousov 	device_set_desc(dev, "DMA remap");
4380f5116d7SKonstantin Belousov 	return (BUS_PROBE_SPECIFIC);
4390f5116d7SKonstantin Belousov }
4400f5116d7SKonstantin Belousov 
4410f5116d7SKonstantin Belousov static int
4420f5116d7SKonstantin Belousov amdiommu_attach(device_t dev)
4430f5116d7SKonstantin Belousov {
4440f5116d7SKonstantin Belousov 	struct amdiommu_unit *sc;
4450f5116d7SKonstantin Belousov 	struct ivhd_lookup_data ild;
4460f5116d7SKonstantin Belousov 	int error;
4470f5116d7SKonstantin Belousov 	uint32_t base_low, base_high;
4480f5116d7SKonstantin Belousov 	bool res;
4490f5116d7SKonstantin Belousov 
4500f5116d7SKonstantin Belousov 	sc = device_get_softc(dev);
451*53adc0b8SKonstantin Belousov 	sc->iommu.unit = device_get_unit(dev);
4520f5116d7SKonstantin Belousov 	sc->iommu.dev = dev;
4530f5116d7SKonstantin Belousov 
4540f5116d7SKonstantin Belousov 	error = pci_find_cap(dev, PCIY_SECDEV, &sc->seccap_reg);
4550f5116d7SKonstantin Belousov 	if (error != 0 || sc->seccap_reg == 0)
4560f5116d7SKonstantin Belousov 		return (ENXIO);
4570f5116d7SKonstantin Belousov 
4580f5116d7SKonstantin Belousov 	base_low = pci_read_config(dev, sc->seccap_reg +
4590f5116d7SKonstantin Belousov 	    PCIR_AMDIOMMU_BASE_LOW, 4);
4600f5116d7SKonstantin Belousov 	base_high = pci_read_config(dev, sc->seccap_reg +
4610f5116d7SKonstantin Belousov 	    PCIR_AMDIOMMU_BASE_HIGH, 4);
4620f5116d7SKonstantin Belousov 	sc->mmio_base = (base_low & PCIM_AMDIOMMU_BASE_LOW_ADDRM) |
4630f5116d7SKonstantin Belousov 	    ((uint64_t)base_high << 32);
4640f5116d7SKonstantin Belousov 
4650f5116d7SKonstantin Belousov 	sc->device_id = pci_get_rid(dev);
4660f5116d7SKonstantin Belousov 	ild.sc = sc;
4670f5116d7SKonstantin Belousov 	ild.devid = sc->device_id;
4680f5116d7SKonstantin Belousov 	res = amdiommu_ivrs_iterate_tbl(ivrs_lookup_ivhd_0x40,
4690f5116d7SKonstantin Belousov 	    ivrs_lookup_ivhd_0x40, ivrs_lookup_ivhd_0x10, &ild);
4700f5116d7SKonstantin Belousov 	if (!res) {
4710f5116d7SKonstantin Belousov 		device_printf(dev, "Cannot find IVHD\n");
4720f5116d7SKonstantin Belousov 		return (ENXIO);
4730f5116d7SKonstantin Belousov 	}
4740f5116d7SKonstantin Belousov 
4750f5116d7SKonstantin Belousov 	mtx_init(&sc->iommu.lock, "amdihw", NULL, MTX_DEF);
4760f5116d7SKonstantin Belousov 	sc->domids = new_unrhdr(0, 0xffff, &sc->iommu.lock);
4770f5116d7SKonstantin Belousov 	LIST_INIT(&sc->domains);
4780f5116d7SKonstantin Belousov 	sysctl_ctx_init(&sc->iommu.sysctl_ctx);
4790f5116d7SKonstantin Belousov 
4800f5116d7SKonstantin Belousov 	sc->mmio_sz = ((sc->efr & AMDIOMMU_EFR_PC_SUP) != 0 ? 512 : 16) *
4810f5116d7SKonstantin Belousov 	    1024;
4820f5116d7SKonstantin Belousov 
4830f5116d7SKonstantin Belousov 	sc->mmio_rid = AMDIOMMU_RID;
4840f5116d7SKonstantin Belousov 	error = bus_set_resource(dev, SYS_RES_MEMORY, AMDIOMMU_RID,
4850f5116d7SKonstantin Belousov 	    sc->mmio_base, sc->mmio_sz);
4860f5116d7SKonstantin Belousov 	if (error != 0) {
4870f5116d7SKonstantin Belousov 		device_printf(dev,
4880f5116d7SKonstantin Belousov 		    "bus_set_resource %#jx-%#jx failed, error %d\n",
4890f5116d7SKonstantin Belousov 		    (uintmax_t)sc->mmio_base, (uintmax_t)sc->mmio_base +
4900f5116d7SKonstantin Belousov 		    sc->mmio_sz, error);
4910f5116d7SKonstantin Belousov 		error = ENXIO;
4920f5116d7SKonstantin Belousov 		goto errout1;
4930f5116d7SKonstantin Belousov 	}
4940f5116d7SKonstantin Belousov 	sc->mmio_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->mmio_rid,
4950f5116d7SKonstantin Belousov 	    sc->mmio_base, sc->mmio_base + sc->mmio_sz - 1, sc->mmio_sz,
4960f5116d7SKonstantin Belousov 	    RF_ALLOCATED | RF_ACTIVE | RF_SHAREABLE);
4970f5116d7SKonstantin Belousov 	if (sc->mmio_res == NULL) {
4980f5116d7SKonstantin Belousov 		device_printf(dev,
4990f5116d7SKonstantin Belousov 		    "bus_alloc_resource %#jx-%#jx failed\n",
5000f5116d7SKonstantin Belousov 		    (uintmax_t)sc->mmio_base, (uintmax_t)sc->mmio_base +
5010f5116d7SKonstantin Belousov 		    sc->mmio_sz);
5020f5116d7SKonstantin Belousov 		error = ENXIO;
5030f5116d7SKonstantin Belousov 		goto errout2;
5040f5116d7SKonstantin Belousov 	}
5050f5116d7SKonstantin Belousov 
5060f5116d7SKonstantin Belousov 	sc->hw_ctrl = amdiommu_read8(sc, AMDIOMMU_CTRL);
5070f5116d7SKonstantin Belousov 	if (bootverbose)
5080f5116d7SKonstantin Belousov 		device_printf(dev, "ctrl reg %#jx\n", (uintmax_t)sc->hw_ctrl);
5090f5116d7SKonstantin Belousov 	if ((sc->hw_ctrl & AMDIOMMU_CTRL_EN) != 0) {
5100f5116d7SKonstantin Belousov 		device_printf(dev, "CTRL_EN is set, bailing out\n");
5110f5116d7SKonstantin Belousov 		error = EBUSY;
5120f5116d7SKonstantin Belousov 		goto errout2;
5130f5116d7SKonstantin Belousov 	}
5140f5116d7SKonstantin Belousov 
5150f5116d7SKonstantin Belousov 	iommu_high = BUS_SPACE_MAXADDR;
5160f5116d7SKonstantin Belousov 
5170f5116d7SKonstantin Belousov 	error = amdiommu_create_dev_tbl(sc);
5180f5116d7SKonstantin Belousov 	if (error != 0)
5190f5116d7SKonstantin Belousov 		goto errout3;
5200f5116d7SKonstantin Belousov 
5210f5116d7SKonstantin Belousov 	error = amdiommu_init_cmd(sc);
5220f5116d7SKonstantin Belousov 	if (error != 0)
5230f5116d7SKonstantin Belousov 		goto errout4;
5240f5116d7SKonstantin Belousov 
5250f5116d7SKonstantin Belousov 	error = amdiommu_init_event(sc);
5260f5116d7SKonstantin Belousov 	if (error != 0)
5270f5116d7SKonstantin Belousov 		goto errout5;
5280f5116d7SKonstantin Belousov 
5290f5116d7SKonstantin Belousov 	error = amdiommu_setup_intr(sc);
5300f5116d7SKonstantin Belousov 	if (error != 0)
5310f5116d7SKonstantin Belousov 		goto errout6;
5320f5116d7SKonstantin Belousov 
5330f5116d7SKonstantin Belousov 	error = iommu_init_busdma(AMD2IOMMU(sc));
5340f5116d7SKonstantin Belousov 	if (error != 0)
5350f5116d7SKonstantin Belousov 		goto errout7;
5360f5116d7SKonstantin Belousov 
5370f5116d7SKonstantin Belousov 	error = amdiommu_init_irt(sc);
5380f5116d7SKonstantin Belousov 	if (error != 0)
5390f5116d7SKonstantin Belousov 		goto errout8;
5400f5116d7SKonstantin Belousov 
5410f5116d7SKonstantin Belousov 	/*
5420f5116d7SKonstantin Belousov 	 * Unlike DMAR, AMD IOMMU does not process command queue
5430f5116d7SKonstantin Belousov 	 * unless IOMMU is enabled.  But since non-present devtab
5440f5116d7SKonstantin Belousov 	 * entry makes IOMMU ignore transactions from corresponding
5450f5116d7SKonstantin Belousov 	 * initiator, de-facto IOMMU operations are disabled for the
5460f5116d7SKonstantin Belousov 	 * DMA and intr remapping.
5470f5116d7SKonstantin Belousov 	 */
5480f5116d7SKonstantin Belousov 	AMDIOMMU_LOCK(sc);
5490f5116d7SKonstantin Belousov 	sc->hw_ctrl |= AMDIOMMU_CTRL_EN;
5500f5116d7SKonstantin Belousov 	amdiommu_write8(sc, AMDIOMMU_CTRL, sc->hw_ctrl);
5510f5116d7SKonstantin Belousov 	if (bootverbose) {
5520f5116d7SKonstantin Belousov 		printf("amdiommu%d: enabled translation\n",
5530f5116d7SKonstantin Belousov 		    AMD2IOMMU(sc)->unit);
5540f5116d7SKonstantin Belousov 	}
5550f5116d7SKonstantin Belousov 	AMDIOMMU_UNLOCK(sc);
5560f5116d7SKonstantin Belousov 
5570f5116d7SKonstantin Belousov 	TAILQ_INSERT_TAIL(&amdiommu_units, sc, unit_next);
5580f5116d7SKonstantin Belousov 	return (0);
5590f5116d7SKonstantin Belousov 
5600f5116d7SKonstantin Belousov errout8:
5610f5116d7SKonstantin Belousov 	iommu_fini_busdma(&sc->iommu);
5620f5116d7SKonstantin Belousov errout7:
5630f5116d7SKonstantin Belousov 	pci_release_msi(dev);
5640f5116d7SKonstantin Belousov errout6:
5650f5116d7SKonstantin Belousov 	amdiommu_fini_event(sc);
5660f5116d7SKonstantin Belousov errout5:
5670f5116d7SKonstantin Belousov 	amdiommu_fini_cmd(sc);
5680f5116d7SKonstantin Belousov errout4:
5690f5116d7SKonstantin Belousov 	amdiommu_free_dev_tbl(sc);
5700f5116d7SKonstantin Belousov errout3:
5710f5116d7SKonstantin Belousov 	bus_release_resource(dev, SYS_RES_MEMORY, sc->mmio_rid, sc->mmio_res);
5720f5116d7SKonstantin Belousov errout2:
5730f5116d7SKonstantin Belousov 	bus_delete_resource(dev, SYS_RES_MEMORY, sc->mmio_rid);
5740f5116d7SKonstantin Belousov errout1:
5750f5116d7SKonstantin Belousov 	sysctl_ctx_free(&sc->iommu.sysctl_ctx);
5760f5116d7SKonstantin Belousov 	delete_unrhdr(sc->domids);
5770f5116d7SKonstantin Belousov 	mtx_destroy(&sc->iommu.lock);
5780f5116d7SKonstantin Belousov 
5790f5116d7SKonstantin Belousov 	return (error);
5800f5116d7SKonstantin Belousov }
5810f5116d7SKonstantin Belousov 
5820f5116d7SKonstantin Belousov static int
5830f5116d7SKonstantin Belousov amdiommu_detach(device_t dev)
5840f5116d7SKonstantin Belousov {
5850f5116d7SKonstantin Belousov 	return (EBUSY);
5860f5116d7SKonstantin Belousov }
5870f5116d7SKonstantin Belousov 
5880f5116d7SKonstantin Belousov static int
5890f5116d7SKonstantin Belousov amdiommu_suspend(device_t dev)
5900f5116d7SKonstantin Belousov {
5910f5116d7SKonstantin Belousov 	/* XXXKIB */
5920f5116d7SKonstantin Belousov 	return (0);
5930f5116d7SKonstantin Belousov }
5940f5116d7SKonstantin Belousov 
5950f5116d7SKonstantin Belousov static int
5960f5116d7SKonstantin Belousov amdiommu_resume(device_t dev)
5970f5116d7SKonstantin Belousov {
5980f5116d7SKonstantin Belousov 	/* XXXKIB */
5990f5116d7SKonstantin Belousov 	return (0);
6000f5116d7SKonstantin Belousov }
6010f5116d7SKonstantin Belousov 
6020f5116d7SKonstantin Belousov static device_method_t amdiommu_methods[] = {
6030f5116d7SKonstantin Belousov 	DEVMETHOD(device_probe, amdiommu_probe),
6040f5116d7SKonstantin Belousov 	DEVMETHOD(device_attach, amdiommu_attach),
6050f5116d7SKonstantin Belousov 	DEVMETHOD(device_detach, amdiommu_detach),
6060f5116d7SKonstantin Belousov 	DEVMETHOD(device_suspend, amdiommu_suspend),
6070f5116d7SKonstantin Belousov 	DEVMETHOD(device_resume, amdiommu_resume),
6080f5116d7SKonstantin Belousov 	DEVMETHOD_END
6090f5116d7SKonstantin Belousov };
6100f5116d7SKonstantin Belousov 
6110f5116d7SKonstantin Belousov static driver_t	amdiommu_driver = {
6120f5116d7SKonstantin Belousov 	"amdiommu",
6130f5116d7SKonstantin Belousov 	amdiommu_methods,
6140f5116d7SKonstantin Belousov 	sizeof(struct amdiommu_unit),
6150f5116d7SKonstantin Belousov };
6160f5116d7SKonstantin Belousov 
6170f5116d7SKonstantin Belousov EARLY_DRIVER_MODULE(amdiommu, pci, amdiommu_driver, 0, 0, BUS_PASS_SUPPORTDEV);
6180f5116d7SKonstantin Belousov MODULE_DEPEND(amdiommu, pci, 1, 1, 1);
6190f5116d7SKonstantin Belousov 
6200f5116d7SKonstantin Belousov static struct amdiommu_unit *
6210f5116d7SKonstantin Belousov amdiommu_unit_by_device_id(u_int pci_seg, u_int device_id)
6220f5116d7SKonstantin Belousov {
6230f5116d7SKonstantin Belousov 	struct amdiommu_unit *unit;
6240f5116d7SKonstantin Belousov 
6250f5116d7SKonstantin Belousov 	TAILQ_FOREACH(unit, &amdiommu_units, unit_next) {
6260f5116d7SKonstantin Belousov 		if (unit->unit_dom == pci_seg && unit->device_id == device_id)
6270f5116d7SKonstantin Belousov 			return (unit);
6280f5116d7SKonstantin Belousov 	}
6290f5116d7SKonstantin Belousov 	return (NULL);
6300f5116d7SKonstantin Belousov }
6310f5116d7SKonstantin Belousov 
6320f5116d7SKonstantin Belousov struct ivhd_find_unit {
6330f5116d7SKonstantin Belousov 	u_int		domain;
6340f5116d7SKonstantin Belousov 	uintptr_t	rid;
6350f5116d7SKonstantin Belousov 	int		devno;
6360f5116d7SKonstantin Belousov 	enum {
6370f5116d7SKonstantin Belousov 		IFU_DEV_PCI,
6380f5116d7SKonstantin Belousov 		IFU_DEV_IOAPIC,
6390f5116d7SKonstantin Belousov 		IFU_DEV_HPET,
6400f5116d7SKonstantin Belousov 	} type;
6410f5116d7SKonstantin Belousov 	u_int		device_id;
6420f5116d7SKonstantin Belousov 	uint16_t	rid_real;
6430f5116d7SKonstantin Belousov 	uint8_t		dte;
6440f5116d7SKonstantin Belousov 	uint32_t	edte;
6450f5116d7SKonstantin Belousov };
6460f5116d7SKonstantin Belousov 
6470f5116d7SKonstantin Belousov static bool
6480f5116d7SKonstantin Belousov amdiommu_find_unit_scan_ivrs(ACPI_IVRS_DE_HEADER *d, size_t tlen,
6490f5116d7SKonstantin Belousov     struct ivhd_find_unit *ifu)
6500f5116d7SKonstantin Belousov {
6510f5116d7SKonstantin Belousov 	char *db, *de;
6520f5116d7SKonstantin Belousov 	size_t len;
6530f5116d7SKonstantin Belousov 
6540f5116d7SKonstantin Belousov 	for (de = (char *)d + tlen; (char *)d < de;
6550f5116d7SKonstantin Belousov 	     d = (ACPI_IVRS_DE_HEADER *)(db + len)) {
6560f5116d7SKonstantin Belousov 		db = (char *)d;
6570f5116d7SKonstantin Belousov 		if (d->Type == ACPI_IVRS_TYPE_PAD4) {
6580f5116d7SKonstantin Belousov 			len = sizeof(ACPI_IVRS_DEVICE4);
6590f5116d7SKonstantin Belousov 		} else if (d->Type == ACPI_IVRS_TYPE_ALL) {
6600f5116d7SKonstantin Belousov 			ACPI_IVRS_DEVICE4 *d4;
6610f5116d7SKonstantin Belousov 
6620f5116d7SKonstantin Belousov 			d4 = (ACPI_IVRS_DEVICE4 *)db;
6630f5116d7SKonstantin Belousov 			len = sizeof(*d4);
6640f5116d7SKonstantin Belousov 			ifu->dte = d4->Header.DataSetting;
6650f5116d7SKonstantin Belousov 		} else if (d->Type == ACPI_IVRS_TYPE_SELECT) {
6660f5116d7SKonstantin Belousov 			ACPI_IVRS_DEVICE4 *d4;
6670f5116d7SKonstantin Belousov 
6680f5116d7SKonstantin Belousov 			d4 = (ACPI_IVRS_DEVICE4 *)db;
6690f5116d7SKonstantin Belousov 			if (d4->Header.Id == ifu->rid) {
6700f5116d7SKonstantin Belousov 				ifu->dte = d4->Header.DataSetting;
6710f5116d7SKonstantin Belousov 				ifu->rid_real = ifu->rid;
6720f5116d7SKonstantin Belousov 				return (true);
6730f5116d7SKonstantin Belousov 			}
6740f5116d7SKonstantin Belousov 			len = sizeof(*d4);
6750f5116d7SKonstantin Belousov 		} else if (d->Type == ACPI_IVRS_TYPE_START) {
6760f5116d7SKonstantin Belousov 			ACPI_IVRS_DEVICE4 *d4, *d4n;
6770f5116d7SKonstantin Belousov 
6780f5116d7SKonstantin Belousov 			d4 = (ACPI_IVRS_DEVICE4 *)db;
6790f5116d7SKonstantin Belousov 			d4n = d4 + 1;
6800f5116d7SKonstantin Belousov 			if (d4n->Header.Type != ACPI_IVRS_TYPE_END) {
6810f5116d7SKonstantin Belousov 				printf("IVRS dev4 start not followed by END "
6820f5116d7SKonstantin Belousov 				    "(%#x)\n", d4n->Header.Type);
6830f5116d7SKonstantin Belousov 				return (false);
6840f5116d7SKonstantin Belousov 			}
6850f5116d7SKonstantin Belousov 			if (d4->Header.Id <= ifu->rid &&
6860f5116d7SKonstantin Belousov 			    ifu->rid <= d4n->Header.Id) {
6870f5116d7SKonstantin Belousov 				ifu->dte = d4->Header.DataSetting;
6880f5116d7SKonstantin Belousov 				ifu->rid_real = ifu->rid;
6890f5116d7SKonstantin Belousov 				return (true);
6900f5116d7SKonstantin Belousov 			}
6910f5116d7SKonstantin Belousov 			len = 2 * sizeof(*d4);
6920f5116d7SKonstantin Belousov 		} else if (d->Type == ACPI_IVRS_TYPE_PAD8) {
6930f5116d7SKonstantin Belousov 			len = sizeof(ACPI_IVRS_DEVICE8A);
6940f5116d7SKonstantin Belousov 		} else if (d->Type == ACPI_IVRS_TYPE_ALIAS_SELECT) {
6950f5116d7SKonstantin Belousov 			ACPI_IVRS_DEVICE8A *d8a;
6960f5116d7SKonstantin Belousov 
6970f5116d7SKonstantin Belousov 			d8a = (ACPI_IVRS_DEVICE8A *)db;
6980f5116d7SKonstantin Belousov 			if (d8a->Header.Id == ifu->rid) {
6990f5116d7SKonstantin Belousov 				ifu->dte = d8a->Header.DataSetting;
7000f5116d7SKonstantin Belousov 				ifu->rid_real = d8a->UsedId;
7010f5116d7SKonstantin Belousov 				return (true);
7020f5116d7SKonstantin Belousov 			}
7030f5116d7SKonstantin Belousov 			len = sizeof(*d8a);
7040f5116d7SKonstantin Belousov 		} else if (d->Type == ACPI_IVRS_TYPE_ALIAS_START) {
7050f5116d7SKonstantin Belousov 			ACPI_IVRS_DEVICE8A *d8a;
7060f5116d7SKonstantin Belousov 			ACPI_IVRS_DEVICE4 *d4;
7070f5116d7SKonstantin Belousov 
7080f5116d7SKonstantin Belousov 			d8a = (ACPI_IVRS_DEVICE8A *)db;
7090f5116d7SKonstantin Belousov 			d4 = (ACPI_IVRS_DEVICE4 *)(d8a + 1);
7100f5116d7SKonstantin Belousov 			if (d4->Header.Type != ACPI_IVRS_TYPE_END) {
7110f5116d7SKonstantin Belousov 				printf("IVRS alias start not followed by END "
7120f5116d7SKonstantin Belousov 				    "(%#x)\n", d4->Header.Type);
7130f5116d7SKonstantin Belousov 				return (false);
7140f5116d7SKonstantin Belousov 			}
7150f5116d7SKonstantin Belousov 			if (d8a->Header.Id <= ifu->rid &&
7160f5116d7SKonstantin Belousov 			    ifu->rid <= d4->Header.Id) {
7170f5116d7SKonstantin Belousov 				ifu->dte = d8a->Header.DataSetting;
7180f5116d7SKonstantin Belousov 				ifu->rid_real = d8a->UsedId;
7190f5116d7SKonstantin Belousov 				return (true);
7200f5116d7SKonstantin Belousov 			}
7210f5116d7SKonstantin Belousov 			len = sizeof(*d8a) + sizeof(*d4);
7220f5116d7SKonstantin Belousov 		} else if (d->Type == ACPI_IVRS_TYPE_EXT_SELECT) {
7230f5116d7SKonstantin Belousov 			ACPI_IVRS_DEVICE8B *d8b;
7240f5116d7SKonstantin Belousov 
7250f5116d7SKonstantin Belousov 			d8b = (ACPI_IVRS_DEVICE8B *)db;
7260f5116d7SKonstantin Belousov 			if (d8b->Header.Id == ifu->rid) {
7270f5116d7SKonstantin Belousov 				ifu->dte = d8b->Header.DataSetting;
7280f5116d7SKonstantin Belousov 				ifu->rid_real = ifu->rid;
7290f5116d7SKonstantin Belousov 				ifu->edte = d8b->ExtendedData;
7300f5116d7SKonstantin Belousov 				return (true);
7310f5116d7SKonstantin Belousov 			}
7320f5116d7SKonstantin Belousov 			len = sizeof(*d8b);
7330f5116d7SKonstantin Belousov 		} else if (d->Type == ACPI_IVRS_TYPE_EXT_START) {
7340f5116d7SKonstantin Belousov 			ACPI_IVRS_DEVICE8B *d8b;
7350f5116d7SKonstantin Belousov 			ACPI_IVRS_DEVICE4 *d4;
7360f5116d7SKonstantin Belousov 
7370f5116d7SKonstantin Belousov 			d8b = (ACPI_IVRS_DEVICE8B *)db;
7380f5116d7SKonstantin Belousov 			d4 = (ACPI_IVRS_DEVICE4 *)(db + sizeof(*d8b));
7390f5116d7SKonstantin Belousov 			if (d4->Header.Type != ACPI_IVRS_TYPE_END) {
7400f5116d7SKonstantin Belousov 				printf("IVRS ext start not followed by END "
7410f5116d7SKonstantin Belousov 				    "(%#x)\n", d4->Header.Type);
7420f5116d7SKonstantin Belousov 				return (false);
7430f5116d7SKonstantin Belousov 			}
7440f5116d7SKonstantin Belousov 			if (d8b->Header.Id >= ifu->rid &&
7450f5116d7SKonstantin Belousov 			    ifu->rid <= d4->Header.Id) {
7460f5116d7SKonstantin Belousov 				ifu->dte = d8b->Header.DataSetting;
7470f5116d7SKonstantin Belousov 				ifu->rid_real = ifu->rid;
7480f5116d7SKonstantin Belousov 				ifu->edte = d8b->ExtendedData;
7490f5116d7SKonstantin Belousov 				return (true);
7500f5116d7SKonstantin Belousov 			}
7510f5116d7SKonstantin Belousov 			len = sizeof(*d8b) + sizeof(*d4);
7520f5116d7SKonstantin Belousov 		} else if (d->Type == ACPI_IVRS_TYPE_SPECIAL) {
7530f5116d7SKonstantin Belousov 			ACPI_IVRS_DEVICE8C *d8c;
7540f5116d7SKonstantin Belousov 
7550f5116d7SKonstantin Belousov 			d8c = (ACPI_IVRS_DEVICE8C *)db;
7560f5116d7SKonstantin Belousov 			if (((ifu->type == IFU_DEV_IOAPIC &&
7570f5116d7SKonstantin Belousov 			    d8c->Variety == ACPI_IVHD_IOAPIC) ||
7580f5116d7SKonstantin Belousov 			    (ifu->type == IFU_DEV_HPET &&
7590f5116d7SKonstantin Belousov 			    d8c->Variety == ACPI_IVHD_HPET)) &&
7600f5116d7SKonstantin Belousov 			    ifu->devno == d8c->Handle) {
7610f5116d7SKonstantin Belousov 				ifu->dte = d8c->Header.DataSetting;
7620f5116d7SKonstantin Belousov 				ifu->rid_real = d8c->UsedId;
7630f5116d7SKonstantin Belousov 				return (true);
7640f5116d7SKonstantin Belousov 			}
7650f5116d7SKonstantin Belousov 			len = sizeof(*d8c);
7660f5116d7SKonstantin Belousov 		} else if (d->Type == ACPI_IVRS_TYPE_HID) {
7670f5116d7SKonstantin Belousov 			ACPI_IVRS_DEVICE_HID *dh;
7680f5116d7SKonstantin Belousov 
7690f5116d7SKonstantin Belousov 			dh = (ACPI_IVRS_DEVICE_HID *)db;
7700f5116d7SKonstantin Belousov 			len = sizeof(*dh) + dh->UidLength;
7710f5116d7SKonstantin Belousov 			/* XXXKIB */
7720f5116d7SKonstantin Belousov 		} else {
7730f5116d7SKonstantin Belousov #if 0
7740f5116d7SKonstantin Belousov 			printf("amdiommu: unknown IVRS device entry type %#x\n",
7750f5116d7SKonstantin Belousov 			    d->Type);
7760f5116d7SKonstantin Belousov #endif
7770f5116d7SKonstantin Belousov 			if (d->Type <= 63)
7780f5116d7SKonstantin Belousov 				len = sizeof(ACPI_IVRS_DEVICE4);
7790f5116d7SKonstantin Belousov 			else if (d->Type <= 127)
7800f5116d7SKonstantin Belousov 				len = sizeof(ACPI_IVRS_DEVICE8A);
7810f5116d7SKonstantin Belousov 			else {
7820f5116d7SKonstantin Belousov 				printf("amdiommu: abort, cannot "
7830f5116d7SKonstantin Belousov 				    "advance iterator, item type %#x\n",
7840f5116d7SKonstantin Belousov 				    d->Type);
7850f5116d7SKonstantin Belousov 				return (false);
7860f5116d7SKonstantin Belousov 			}
7870f5116d7SKonstantin Belousov 		}
7880f5116d7SKonstantin Belousov 	}
7890f5116d7SKonstantin Belousov 	return (false);
7900f5116d7SKonstantin Belousov }
7910f5116d7SKonstantin Belousov 
7920f5116d7SKonstantin Belousov static bool
7930f5116d7SKonstantin Belousov amdiommu_find_unit_scan_0x11(ACPI_IVRS_HARDWARE2 *ivrs, void *arg)
7940f5116d7SKonstantin Belousov {
7950f5116d7SKonstantin Belousov 	struct ivhd_find_unit *ifu = arg;
7960f5116d7SKonstantin Belousov 	ACPI_IVRS_DE_HEADER *d;
7970f5116d7SKonstantin Belousov 	bool res;
7980f5116d7SKonstantin Belousov 
7990f5116d7SKonstantin Belousov 	KASSERT(ivrs->Header.Type == ACPI_IVRS_TYPE_HARDWARE2 ||
8000f5116d7SKonstantin Belousov 	    ivrs->Header.Type == ACPI_IVRS_TYPE_HARDWARE3,
8010f5116d7SKonstantin Belousov 	    ("Misparsed IVHD h2, ivrs type %#x", ivrs->Header.Type));
8020f5116d7SKonstantin Belousov 
8030f5116d7SKonstantin Belousov 	if (ifu->domain != ivrs->PciSegmentGroup)
8040f5116d7SKonstantin Belousov 		return (false);
8050f5116d7SKonstantin Belousov 	d = (ACPI_IVRS_DE_HEADER *)(ivrs + 1);
8060f5116d7SKonstantin Belousov 	res = amdiommu_find_unit_scan_ivrs(d, ivrs->Header.Length, ifu);
8070f5116d7SKonstantin Belousov 	if (res)
8080f5116d7SKonstantin Belousov 		ifu->device_id = ivrs->Header.DeviceId;
8090f5116d7SKonstantin Belousov 	return (res);
8100f5116d7SKonstantin Belousov }
8110f5116d7SKonstantin Belousov 
8120f5116d7SKonstantin Belousov static bool
8130f5116d7SKonstantin Belousov amdiommu_find_unit_scan_0x10(ACPI_IVRS_HARDWARE1 *ivrs, void *arg)
8140f5116d7SKonstantin Belousov {
8150f5116d7SKonstantin Belousov 	struct ivhd_find_unit *ifu = arg;
8160f5116d7SKonstantin Belousov 	ACPI_IVRS_DE_HEADER *d;
8170f5116d7SKonstantin Belousov 	bool res;
8180f5116d7SKonstantin Belousov 
8190f5116d7SKonstantin Belousov 	KASSERT(ivrs->Header.Type == ACPI_IVRS_TYPE_HARDWARE1,
8200f5116d7SKonstantin Belousov 	    ("Misparsed IVHD h1, ivrs type %#x", ivrs->Header.Type));
8210f5116d7SKonstantin Belousov 
8220f5116d7SKonstantin Belousov 	if (ifu->domain != ivrs->PciSegmentGroup)
8230f5116d7SKonstantin Belousov 		return (false);
8240f5116d7SKonstantin Belousov 	d = (ACPI_IVRS_DE_HEADER *)(ivrs + 1);
8250f5116d7SKonstantin Belousov 	res = amdiommu_find_unit_scan_ivrs(d, ivrs->Header.Length, ifu);
8260f5116d7SKonstantin Belousov 	if (res)
8270f5116d7SKonstantin Belousov 		ifu->device_id = ivrs->Header.DeviceId;
8280f5116d7SKonstantin Belousov 	return (res);
8290f5116d7SKonstantin Belousov }
8300f5116d7SKonstantin Belousov 
8310f5116d7SKonstantin Belousov static void
8320f5116d7SKonstantin Belousov amdiommu_dev_prop_dtr(device_t dev, const char *name, void *val, void *dtr_ctx)
8330f5116d7SKonstantin Belousov {
8340f5116d7SKonstantin Belousov 	free(val, M_DEVBUF);
8350f5116d7SKonstantin Belousov }
8360f5116d7SKonstantin Belousov 
8370f5116d7SKonstantin Belousov static int *
8380f5116d7SKonstantin Belousov amdiommu_dev_fetch_flagsp(struct amdiommu_unit *unit, device_t dev)
8390f5116d7SKonstantin Belousov {
8400f5116d7SKonstantin Belousov 	int *flagsp, error;
8410f5116d7SKonstantin Belousov 
8420f5116d7SKonstantin Belousov 	bus_topo_assert();
8430f5116d7SKonstantin Belousov 	error = device_get_prop(dev, device_get_nameunit(unit->iommu.dev),
8440f5116d7SKonstantin Belousov 	    (void **)&flagsp);
8450f5116d7SKonstantin Belousov 	if (error == ENOENT) {
8460f5116d7SKonstantin Belousov 		flagsp = malloc(sizeof(int), M_DEVBUF, M_WAITOK | M_ZERO);
8470f5116d7SKonstantin Belousov 		device_set_prop(dev, device_get_nameunit(unit->iommu.dev),
8480f5116d7SKonstantin Belousov 		    flagsp, amdiommu_dev_prop_dtr, unit);
8490f5116d7SKonstantin Belousov 	}
8500f5116d7SKonstantin Belousov 	return (flagsp);
8510f5116d7SKonstantin Belousov }
8520f5116d7SKonstantin Belousov 
8530f5116d7SKonstantin Belousov static int
8540f5116d7SKonstantin Belousov amdiommu_get_dev_prop_flags(struct amdiommu_unit *unit, device_t dev)
8550f5116d7SKonstantin Belousov {
8560f5116d7SKonstantin Belousov 	int *flagsp, flags;
8570f5116d7SKonstantin Belousov 
8580f5116d7SKonstantin Belousov 	bus_topo_lock();
8590f5116d7SKonstantin Belousov 	flagsp = amdiommu_dev_fetch_flagsp(unit, dev);
8600f5116d7SKonstantin Belousov 	flags = *flagsp;
8610f5116d7SKonstantin Belousov 	bus_topo_unlock();
8620f5116d7SKonstantin Belousov 	return (flags);
8630f5116d7SKonstantin Belousov }
8640f5116d7SKonstantin Belousov 
8650f5116d7SKonstantin Belousov static void
8660f5116d7SKonstantin Belousov amdiommu_set_dev_prop_flags(struct amdiommu_unit *unit, device_t dev,
8670f5116d7SKonstantin Belousov     int flag)
8680f5116d7SKonstantin Belousov {
8690f5116d7SKonstantin Belousov 	int *flagsp;
8700f5116d7SKonstantin Belousov 
8710f5116d7SKonstantin Belousov 	bus_topo_lock();
8720f5116d7SKonstantin Belousov 	flagsp = amdiommu_dev_fetch_flagsp(unit, dev);
8730f5116d7SKonstantin Belousov 	*flagsp |= flag;
8740f5116d7SKonstantin Belousov 	bus_topo_unlock();
8750f5116d7SKonstantin Belousov }
8760f5116d7SKonstantin Belousov 
8770f5116d7SKonstantin Belousov int
8780f5116d7SKonstantin Belousov amdiommu_find_unit(device_t dev, struct amdiommu_unit **unitp, uint16_t *ridp,
8790f5116d7SKonstantin Belousov     uint8_t *dtep, uint32_t *edtep, bool verbose)
8800f5116d7SKonstantin Belousov {
8810f5116d7SKonstantin Belousov 	struct ivhd_find_unit ifu;
8820f5116d7SKonstantin Belousov 	struct amdiommu_unit *unit;
8830f5116d7SKonstantin Belousov 	int error, flags;
8840f5116d7SKonstantin Belousov 	bool res;
8850f5116d7SKonstantin Belousov 
8869805e5b0SKonstantin Belousov 	if (!amdiommu_enable)
8879805e5b0SKonstantin Belousov 		return (ENXIO);
8889805e5b0SKonstantin Belousov 
8890f5116d7SKonstantin Belousov 	if (device_get_devclass(device_get_parent(dev)) !=
8900f5116d7SKonstantin Belousov 	    devclass_find("pci"))
8910f5116d7SKonstantin Belousov 		return (ENXIO);
8920f5116d7SKonstantin Belousov 
8930f5116d7SKonstantin Belousov 	bzero(&ifu, sizeof(ifu));
8940f5116d7SKonstantin Belousov 	ifu.type = IFU_DEV_PCI;
8950f5116d7SKonstantin Belousov 
8960f5116d7SKonstantin Belousov 	error = pci_get_id(dev, PCI_ID_RID, &ifu.rid);
8970f5116d7SKonstantin Belousov 	if (error != 0) {
8980f5116d7SKonstantin Belousov 		if (verbose)
8990f5116d7SKonstantin Belousov 			device_printf(dev,
9000f5116d7SKonstantin Belousov 			    "amdiommu cannot get rid, error %d\n", error);
9010f5116d7SKonstantin Belousov 		return (ENXIO);
9020f5116d7SKonstantin Belousov 	}
9030f5116d7SKonstantin Belousov 
9040f5116d7SKonstantin Belousov 	ifu.domain = pci_get_domain(dev);
9050f5116d7SKonstantin Belousov 	res = amdiommu_ivrs_iterate_tbl(amdiommu_find_unit_scan_0x11,
9060f5116d7SKonstantin Belousov 	    amdiommu_find_unit_scan_0x11, amdiommu_find_unit_scan_0x10, &ifu);
9070f5116d7SKonstantin Belousov 	if (!res) {
9080f5116d7SKonstantin Belousov 		if (verbose)
9090f5116d7SKonstantin Belousov 			device_printf(dev,
9100f5116d7SKonstantin Belousov 			    "(%#06x:%#06x) amdiommu cannot match rid in IVHD\n",
9110f5116d7SKonstantin Belousov 			    ifu.domain, (unsigned)ifu.rid);
9120f5116d7SKonstantin Belousov 		return (ENXIO);
9130f5116d7SKonstantin Belousov 	}
9140f5116d7SKonstantin Belousov 
9150f5116d7SKonstantin Belousov 	unit = amdiommu_unit_by_device_id(ifu.domain, ifu.device_id);
9160f5116d7SKonstantin Belousov 	if (unit == NULL) {
9170f5116d7SKonstantin Belousov 		if (verbose)
9180f5116d7SKonstantin Belousov 			device_printf(dev,
9190f5116d7SKonstantin Belousov 			    "(%#06x:%#06x) amdiommu cannot find unit\n",
9200f5116d7SKonstantin Belousov 			    ifu.domain, (unsigned)ifu.rid);
9210f5116d7SKonstantin Belousov 		return (ENXIO);
9220f5116d7SKonstantin Belousov 	}
9230f5116d7SKonstantin Belousov 	*unitp = unit;
9240f5116d7SKonstantin Belousov 	iommu_device_set_iommu_prop(dev, unit->iommu.dev);
9250f5116d7SKonstantin Belousov 	if (ridp != NULL)
9260f5116d7SKonstantin Belousov 		*ridp = ifu.rid_real;
9270f5116d7SKonstantin Belousov 	if (dtep != NULL)
9280f5116d7SKonstantin Belousov 		*dtep = ifu.dte;
9290f5116d7SKonstantin Belousov 	if (edtep != NULL)
9300f5116d7SKonstantin Belousov 		*edtep = ifu.edte;
9310f5116d7SKonstantin Belousov 	if (verbose) {
9320f5116d7SKonstantin Belousov 		flags = amdiommu_get_dev_prop_flags(unit, dev);
9330f5116d7SKonstantin Belousov 		if ((flags & AMDIOMMU_DEV_REPORTED) == 0) {
9340f5116d7SKonstantin Belousov 			amdiommu_set_dev_prop_flags(unit, dev,
9350f5116d7SKonstantin Belousov 			    AMDIOMMU_DEV_REPORTED);
9360f5116d7SKonstantin Belousov 			device_printf(dev, "amdiommu%d "
9370f5116d7SKonstantin Belousov 			    "initiator rid %#06x dte %#x edte %#x\n",
9380f5116d7SKonstantin Belousov 			    unit->iommu.unit, ifu.rid_real, ifu.dte, ifu.edte);
9390f5116d7SKonstantin Belousov 		}
9400f5116d7SKonstantin Belousov 	}
9410f5116d7SKonstantin Belousov 	return (0);
9420f5116d7SKonstantin Belousov }
9430f5116d7SKonstantin Belousov 
9440f5116d7SKonstantin Belousov int
9450f5116d7SKonstantin Belousov amdiommu_find_unit_for_ioapic(int apic_id, struct amdiommu_unit **unitp,
9460f5116d7SKonstantin Belousov     uint16_t *ridp, uint8_t *dtep, uint32_t *edtep, bool verbose)
9470f5116d7SKonstantin Belousov {
9480f5116d7SKonstantin Belousov 	struct ivhd_find_unit ifu;
9490f5116d7SKonstantin Belousov 	struct amdiommu_unit *unit;
9500f5116d7SKonstantin Belousov 	device_t apic_dev;
9510f5116d7SKonstantin Belousov 	bool res;
9520f5116d7SKonstantin Belousov 
9539805e5b0SKonstantin Belousov 	if (!amdiommu_enable)
9549805e5b0SKonstantin Belousov 		return (ENXIO);
9559805e5b0SKonstantin Belousov 
9560f5116d7SKonstantin Belousov 	bzero(&ifu, sizeof(ifu));
9570f5116d7SKonstantin Belousov 	ifu.type = IFU_DEV_IOAPIC;
9580f5116d7SKonstantin Belousov 	ifu.devno = apic_id;
9590f5116d7SKonstantin Belousov 	ifu.rid = -1;
9600f5116d7SKonstantin Belousov 
9610f5116d7SKonstantin Belousov 	res = amdiommu_ivrs_iterate_tbl(amdiommu_find_unit_scan_0x11,
9620f5116d7SKonstantin Belousov 	    amdiommu_find_unit_scan_0x11, amdiommu_find_unit_scan_0x10, &ifu);
9630f5116d7SKonstantin Belousov 	if (!res) {
9640f5116d7SKonstantin Belousov 		if (verbose)
9650f5116d7SKonstantin Belousov 			printf("amdiommu cannot match ioapic no %d in IVHD\n",
9660f5116d7SKonstantin Belousov 			    apic_id);
9670f5116d7SKonstantin Belousov 		return (ENXIO);
9680f5116d7SKonstantin Belousov 	}
9690f5116d7SKonstantin Belousov 
9700f5116d7SKonstantin Belousov 	unit = amdiommu_unit_by_device_id(0, ifu.device_id);
9710f5116d7SKonstantin Belousov 	apic_dev = ioapic_get_dev(apic_id);
9720f5116d7SKonstantin Belousov 	if (apic_dev != NULL)
9730f5116d7SKonstantin Belousov 		iommu_device_set_iommu_prop(apic_dev, unit->iommu.dev);
9740f5116d7SKonstantin Belousov 	if (unit == NULL) {
9750f5116d7SKonstantin Belousov 		if (verbose)
9760f5116d7SKonstantin Belousov 			printf("amdiommu cannot find unit by dev id %#x\n",
9770f5116d7SKonstantin Belousov 			    ifu.device_id);
9780f5116d7SKonstantin Belousov 		return (ENXIO);
9790f5116d7SKonstantin Belousov 	}
9800f5116d7SKonstantin Belousov 	*unitp = unit;
9810f5116d7SKonstantin Belousov 	if (ridp != NULL)
9820f5116d7SKonstantin Belousov 		*ridp = ifu.rid_real;
9830f5116d7SKonstantin Belousov 	if (dtep != NULL)
9840f5116d7SKonstantin Belousov 		*dtep = ifu.dte;
9850f5116d7SKonstantin Belousov 	if (edtep != NULL)
9860f5116d7SKonstantin Belousov 		*edtep = ifu.edte;
9870f5116d7SKonstantin Belousov 	if (verbose) {
9880f5116d7SKonstantin Belousov 		printf("amdiommu%d IOAPIC %d "
9890f5116d7SKonstantin Belousov 		    "initiator rid %#06x dte %#x edte %#x\n",
9900f5116d7SKonstantin Belousov 		    unit->iommu.unit, apic_id, ifu.rid_real, ifu.dte,
9910f5116d7SKonstantin Belousov 		    ifu.edte);
9920f5116d7SKonstantin Belousov 	}
9930f5116d7SKonstantin Belousov 	return (0);
9940f5116d7SKonstantin Belousov }
9950f5116d7SKonstantin Belousov 
9960f5116d7SKonstantin Belousov int
9970f5116d7SKonstantin Belousov amdiommu_find_unit_for_hpet(device_t hpet, struct amdiommu_unit **unitp,
9980f5116d7SKonstantin Belousov     uint16_t *ridp, uint8_t *dtep, uint32_t *edtep, bool verbose)
9990f5116d7SKonstantin Belousov {
10000f5116d7SKonstantin Belousov 	struct ivhd_find_unit ifu;
10010f5116d7SKonstantin Belousov 	struct amdiommu_unit *unit;
10020f5116d7SKonstantin Belousov 	int hpet_no;
10030f5116d7SKonstantin Belousov 	bool res;
10040f5116d7SKonstantin Belousov 
10059805e5b0SKonstantin Belousov 	if (!amdiommu_enable)
10069805e5b0SKonstantin Belousov 		return (ENXIO);
10079805e5b0SKonstantin Belousov 
10080f5116d7SKonstantin Belousov 	hpet_no = hpet_get_uid(hpet);
10090f5116d7SKonstantin Belousov 	bzero(&ifu, sizeof(ifu));
10100f5116d7SKonstantin Belousov 	ifu.type = IFU_DEV_HPET;
10110f5116d7SKonstantin Belousov 	ifu.devno = hpet_no;
10120f5116d7SKonstantin Belousov 	ifu.rid = -1;
10130f5116d7SKonstantin Belousov 
10140f5116d7SKonstantin Belousov 	res = amdiommu_ivrs_iterate_tbl(amdiommu_find_unit_scan_0x11,
10150f5116d7SKonstantin Belousov 	    amdiommu_find_unit_scan_0x11, amdiommu_find_unit_scan_0x10, &ifu);
10160f5116d7SKonstantin Belousov 	if (!res) {
10171bca58a1SKonstantin Belousov 		if (verbose)
10180f5116d7SKonstantin Belousov 			printf("amdiommu cannot match hpet no %d in IVHD\n",
10190f5116d7SKonstantin Belousov 			    hpet_no);
10200f5116d7SKonstantin Belousov 		return (ENXIO);
10210f5116d7SKonstantin Belousov 	}
10220f5116d7SKonstantin Belousov 
10230f5116d7SKonstantin Belousov 	unit = amdiommu_unit_by_device_id(0, ifu.device_id);
10240f5116d7SKonstantin Belousov 	if (unit == NULL) {
10250f5116d7SKonstantin Belousov 		if (verbose)
10260f5116d7SKonstantin Belousov 			printf("amdiommu cannot find unit id %d\n",
10270f5116d7SKonstantin Belousov 			    hpet_no);
10280f5116d7SKonstantin Belousov 		return (ENXIO);
10290f5116d7SKonstantin Belousov 	}
10300f5116d7SKonstantin Belousov 	*unitp = unit;
10310f5116d7SKonstantin Belousov 	iommu_device_set_iommu_prop(hpet, unit->iommu.dev);
10320f5116d7SKonstantin Belousov 	if (ridp != NULL)
10330f5116d7SKonstantin Belousov 		*ridp = ifu.rid_real;
10340f5116d7SKonstantin Belousov 	if (dtep != NULL)
10350f5116d7SKonstantin Belousov 		*dtep = ifu.dte;
10360f5116d7SKonstantin Belousov 	if (edtep != NULL)
10370f5116d7SKonstantin Belousov 		*edtep = ifu.edte;
10380f5116d7SKonstantin Belousov 	if (verbose) {
10390f5116d7SKonstantin Belousov 		printf("amdiommu%d HPET no %d "
10400f5116d7SKonstantin Belousov 		    "initiator rid %#06x dte %#x edte %#x\n",
10410f5116d7SKonstantin Belousov 		    unit->iommu.unit, hpet_no, ifu.rid_real, ifu.dte,
10420f5116d7SKonstantin Belousov 		    ifu.edte);
10430f5116d7SKonstantin Belousov 	}
10440f5116d7SKonstantin Belousov 	return (0);
10450f5116d7SKonstantin Belousov }
10460f5116d7SKonstantin Belousov 
10470f5116d7SKonstantin Belousov static struct iommu_unit *
10480f5116d7SKonstantin Belousov amdiommu_find_method(device_t dev, bool verbose)
10490f5116d7SKonstantin Belousov {
10500f5116d7SKonstantin Belousov 	struct amdiommu_unit *unit;
10510f5116d7SKonstantin Belousov 	int error;
10520f5116d7SKonstantin Belousov 	uint32_t edte;
10530f5116d7SKonstantin Belousov 	uint16_t rid;
10540f5116d7SKonstantin Belousov 	uint8_t dte;
10550f5116d7SKonstantin Belousov 
10560f5116d7SKonstantin Belousov 	error = amdiommu_find_unit(dev, &unit, &rid, &dte, &edte, verbose);
10570f5116d7SKonstantin Belousov 	if (error != 0) {
10589805e5b0SKonstantin Belousov 		if (verbose && amdiommu_enable)
10590f5116d7SKonstantin Belousov 			device_printf(dev,
10600f5116d7SKonstantin Belousov 			    "cannot find amdiommu unit, error %d\n",
10610f5116d7SKonstantin Belousov 			    error);
10620f5116d7SKonstantin Belousov 		return (NULL);
10630f5116d7SKonstantin Belousov 	}
10640f5116d7SKonstantin Belousov 	return (&unit->iommu);
10650f5116d7SKonstantin Belousov }
10660f5116d7SKonstantin Belousov 
10670f5116d7SKonstantin Belousov static struct x86_unit_common *
10680f5116d7SKonstantin Belousov amdiommu_get_x86_common(struct iommu_unit *unit)
10690f5116d7SKonstantin Belousov {
10700f5116d7SKonstantin Belousov 	struct amdiommu_unit *iommu;
10710f5116d7SKonstantin Belousov 
10720f5116d7SKonstantin Belousov 	iommu = IOMMU2AMD(unit);
10730f5116d7SKonstantin Belousov 	return (&iommu->x86c);
10740f5116d7SKonstantin Belousov }
10750f5116d7SKonstantin Belousov 
10760f5116d7SKonstantin Belousov static void
10770f5116d7SKonstantin Belousov amdiommu_unit_pre_instantiate_ctx(struct iommu_unit *unit)
10780f5116d7SKonstantin Belousov {
10790f5116d7SKonstantin Belousov }
10800f5116d7SKonstantin Belousov 
10810f5116d7SKonstantin Belousov static struct x86_iommu amd_x86_iommu = {
10820f5116d7SKonstantin Belousov 	.get_x86_common = amdiommu_get_x86_common,
10830f5116d7SKonstantin Belousov 	.unit_pre_instantiate_ctx = amdiommu_unit_pre_instantiate_ctx,
10840f5116d7SKonstantin Belousov 	.find = amdiommu_find_method,
10850f5116d7SKonstantin Belousov 	.domain_unload_entry = amdiommu_domain_unload_entry,
10860f5116d7SKonstantin Belousov 	.domain_unload = amdiommu_domain_unload,
10870f5116d7SKonstantin Belousov 	.get_ctx = amdiommu_get_ctx,
10880f5116d7SKonstantin Belousov 	.free_ctx_locked = amdiommu_free_ctx_locked_method,
10890f5116d7SKonstantin Belousov 	.alloc_msi_intr = amdiommu_alloc_msi_intr,
10900f5116d7SKonstantin Belousov 	.map_msi_intr = amdiommu_map_msi_intr,
10910f5116d7SKonstantin Belousov 	.unmap_msi_intr = amdiommu_unmap_msi_intr,
10920f5116d7SKonstantin Belousov 	.map_ioapic_intr = amdiommu_map_ioapic_intr,
10930f5116d7SKonstantin Belousov 	.unmap_ioapic_intr = amdiommu_unmap_ioapic_intr,
10940f5116d7SKonstantin Belousov };
10950f5116d7SKonstantin Belousov 
10960f5116d7SKonstantin Belousov static void
10970f5116d7SKonstantin Belousov x86_iommu_set_amd(void *arg __unused)
10980f5116d7SKonstantin Belousov {
10990f5116d7SKonstantin Belousov 	if (cpu_vendor_id == CPU_VENDOR_AMD)
11000f5116d7SKonstantin Belousov 		set_x86_iommu(&amd_x86_iommu);
11010f5116d7SKonstantin Belousov }
11020f5116d7SKonstantin Belousov 
11030f5116d7SKonstantin Belousov SYSINIT(x86_iommu, SI_SUB_TUNABLES, SI_ORDER_ANY, x86_iommu_set_amd, NULL);
11040f5116d7SKonstantin Belousov 
11050f5116d7SKonstantin Belousov #ifdef DDB
11060f5116d7SKonstantin Belousov #include <ddb/ddb.h>
11070f5116d7SKonstantin Belousov #include <ddb/db_lex.h>
11080f5116d7SKonstantin Belousov 
11090f5116d7SKonstantin Belousov static void
11100f5116d7SKonstantin Belousov amdiommu_print_domain(struct amdiommu_domain *domain, bool show_mappings)
11110f5116d7SKonstantin Belousov {
11120f5116d7SKonstantin Belousov 	struct iommu_domain *iodom;
11130f5116d7SKonstantin Belousov 
11140f5116d7SKonstantin Belousov 	iodom = DOM2IODOM(domain);
11150f5116d7SKonstantin Belousov 
11160f5116d7SKonstantin Belousov 	db_printf(
111743bfb4e7SKonstantin Belousov 	    "  @%p dom %d pglvl %d end %jx refs %d\n"
11180f5116d7SKonstantin Belousov 	    "   ctx_cnt %d flags %x pgobj %p map_ents %u\n",
111943bfb4e7SKonstantin Belousov 	    domain, domain->domain, domain->pglvl,
11200f5116d7SKonstantin Belousov 	    (uintmax_t)domain->iodom.end, domain->refs, domain->ctx_cnt,
11210f5116d7SKonstantin Belousov 	    domain->iodom.flags, domain->pgtbl_obj, domain->iodom.entries_cnt);
11220f5116d7SKonstantin Belousov 
11230f5116d7SKonstantin Belousov 	iommu_db_domain_print_contexts(iodom);
11240f5116d7SKonstantin Belousov 
11250f5116d7SKonstantin Belousov 	if (show_mappings)
11260f5116d7SKonstantin Belousov 		iommu_db_domain_print_mappings(iodom);
11270f5116d7SKonstantin Belousov }
11280f5116d7SKonstantin Belousov 
11290f5116d7SKonstantin Belousov static void
11300f5116d7SKonstantin Belousov amdiommu_print_one(struct amdiommu_unit *unit, bool show_domains,
11310f5116d7SKonstantin Belousov     bool show_mappings, bool show_cmdq)
11320f5116d7SKonstantin Belousov {
11330f5116d7SKonstantin Belousov 	struct amdiommu_domain *domain;
11340f5116d7SKonstantin Belousov 	struct amdiommu_cmd_generic *cp;
11350f5116d7SKonstantin Belousov 	u_int cmd_head, cmd_tail, ci;
11360f5116d7SKonstantin Belousov 
11370f5116d7SKonstantin Belousov 	cmd_head = amdiommu_read4(unit, AMDIOMMU_CMDBUF_HEAD);
11380f5116d7SKonstantin Belousov 	cmd_tail = amdiommu_read4(unit, AMDIOMMU_CMDBUF_TAIL);
11390f5116d7SKonstantin Belousov 	db_printf("amdiommu%d at %p, mmio at %#jx/sz %#jx\n",
11400f5116d7SKonstantin Belousov 	    unit->iommu.unit, unit, (uintmax_t)unit->mmio_base,
11410f5116d7SKonstantin Belousov 	    (uintmax_t)unit->mmio_sz);
11420f5116d7SKonstantin Belousov 	db_printf("  hw ctrl %#018jx cmdevst %#018jx\n",
11430f5116d7SKonstantin Belousov 	    (uintmax_t)amdiommu_read8(unit, AMDIOMMU_CTRL),
11440f5116d7SKonstantin Belousov 	    (uintmax_t)amdiommu_read8(unit, AMDIOMMU_CMDEV_STATUS));
11450f5116d7SKonstantin Belousov 	db_printf("  devtbl at %p\n", unit->dev_tbl);
11460f5116d7SKonstantin Belousov 	db_printf("  hwseq at %p phys %#jx val %#jx\n",
11470f5116d7SKonstantin Belousov 	    &unit->x86c.inv_waitd_seq_hw,
11480f5116d7SKonstantin Belousov 	    pmap_kextract((vm_offset_t)&unit->x86c.inv_waitd_seq_hw),
11490f5116d7SKonstantin Belousov 	    unit->x86c.inv_waitd_seq_hw);
11500f5116d7SKonstantin Belousov 	db_printf("  invq at %p base %#jx hw head/tail %#x/%#x\n",
11510f5116d7SKonstantin Belousov 	    unit->x86c.inv_queue,
11520f5116d7SKonstantin Belousov 	    (uintmax_t)amdiommu_read8(unit, AMDIOMMU_CMDBUF_BASE),
11530f5116d7SKonstantin Belousov 	    cmd_head, cmd_tail);
11540f5116d7SKonstantin Belousov 
11550f5116d7SKonstantin Belousov 	if (show_cmdq) {
11560f5116d7SKonstantin Belousov 		db_printf("  cmd q:\n");
11570f5116d7SKonstantin Belousov 		for (ci = cmd_head; ci != cmd_tail;) {
11580f5116d7SKonstantin Belousov 			cp = (struct amdiommu_cmd_generic *)(unit->
11590f5116d7SKonstantin Belousov 			    x86c.inv_queue + ci);
11600f5116d7SKonstantin Belousov 			db_printf(
11610f5116d7SKonstantin Belousov 		    "    idx %#x op %#x %#010x %#010x %#010x %#010x\n",
11620f5116d7SKonstantin Belousov 			    ci >> AMDIOMMU_CMD_SZ_SHIFT, cp->op,
11630f5116d7SKonstantin Belousov 		    	    cp->w0, cp->ww1, cp->w2, cp->w3);
11640f5116d7SKonstantin Belousov 
11650f5116d7SKonstantin Belousov 			ci += AMDIOMMU_CMD_SZ;
11660f5116d7SKonstantin Belousov 			if (ci == unit->x86c.inv_queue_size)
11670f5116d7SKonstantin Belousov 				ci = 0;
11680f5116d7SKonstantin Belousov 		}
11690f5116d7SKonstantin Belousov 	}
11700f5116d7SKonstantin Belousov 
11710f5116d7SKonstantin Belousov 	if (show_domains) {
11720f5116d7SKonstantin Belousov 		db_printf("  domains:\n");
11730f5116d7SKonstantin Belousov 		LIST_FOREACH(domain, &unit->domains, link) {
11740f5116d7SKonstantin Belousov 			amdiommu_print_domain(domain, show_mappings);
11750f5116d7SKonstantin Belousov 			if (db_pager_quit)
11760f5116d7SKonstantin Belousov 				break;
11770f5116d7SKonstantin Belousov 		}
11780f5116d7SKonstantin Belousov 	}
11790f5116d7SKonstantin Belousov }
11800f5116d7SKonstantin Belousov 
11810f5116d7SKonstantin Belousov DB_SHOW_COMMAND(amdiommu, db_amdiommu_print)
11820f5116d7SKonstantin Belousov {
11830f5116d7SKonstantin Belousov 	struct amdiommu_unit *unit;
11840f5116d7SKonstantin Belousov 	bool show_domains, show_mappings, show_cmdq;
11850f5116d7SKonstantin Belousov 
11860f5116d7SKonstantin Belousov 	show_domains = strchr(modif, 'd') != NULL;
11870f5116d7SKonstantin Belousov 	show_mappings = strchr(modif, 'm') != NULL;
11880f5116d7SKonstantin Belousov 	show_cmdq = strchr(modif, 'q') != NULL;
11890f5116d7SKonstantin Belousov 	if (!have_addr) {
11900f5116d7SKonstantin Belousov 		db_printf("usage: show amdiommu [/d] [/m] [/q] index\n");
11910f5116d7SKonstantin Belousov 		return;
11920f5116d7SKonstantin Belousov 	}
11930f5116d7SKonstantin Belousov 	if ((vm_offset_t)addr < 0x10000)
11940f5116d7SKonstantin Belousov 		unit = amdiommu_unit_by_device_id(0, (u_int)addr);
11950f5116d7SKonstantin Belousov 	else
11960f5116d7SKonstantin Belousov 		unit = (struct amdiommu_unit *)addr;
11970f5116d7SKonstantin Belousov 	amdiommu_print_one(unit, show_domains, show_mappings, show_cmdq);
11980f5116d7SKonstantin Belousov }
11990f5116d7SKonstantin Belousov 
12000f5116d7SKonstantin Belousov DB_SHOW_ALL_COMMAND(amdiommus, db_show_all_amdiommus)
12010f5116d7SKonstantin Belousov {
12020f5116d7SKonstantin Belousov 	struct amdiommu_unit *unit;
12030f5116d7SKonstantin Belousov 	bool show_domains, show_mappings, show_cmdq;
12040f5116d7SKonstantin Belousov 
12050f5116d7SKonstantin Belousov 	show_domains = strchr(modif, 'd') != NULL;
12060f5116d7SKonstantin Belousov 	show_mappings = strchr(modif, 'm') != NULL;
12070f5116d7SKonstantin Belousov 	show_cmdq = strchr(modif, 'q') != NULL;
12080f5116d7SKonstantin Belousov 
12090f5116d7SKonstantin Belousov 	TAILQ_FOREACH(unit, &amdiommu_units, unit_next) {
12100f5116d7SKonstantin Belousov 		amdiommu_print_one(unit, show_domains, show_mappings,
12110f5116d7SKonstantin Belousov 		    show_cmdq);
12120f5116d7SKonstantin Belousov 		if (db_pager_quit)
12130f5116d7SKonstantin Belousov 			break;
12140f5116d7SKonstantin Belousov 	}
12150f5116d7SKonstantin Belousov }
12160f5116d7SKonstantin Belousov #endif
1217