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