xref: /freebsd-src/sys/x86/iommu/iommu_utils.c (revision d97838b7c2a605932b6edf36f87abe7ccce74314)
140d951bcSKonstantin Belousov /*-
240d951bcSKonstantin Belousov  * SPDX-License-Identifier: BSD-2-Clause
340d951bcSKonstantin Belousov  *
465b133e5SKonstantin Belousov  * Copyright (c) 2013, 2014, 2024 The FreeBSD Foundation
540d951bcSKonstantin Belousov  *
640d951bcSKonstantin Belousov  * This software was developed by Konstantin Belousov <kib@FreeBSD.org>
740d951bcSKonstantin Belousov  * under sponsorship from the FreeBSD Foundation.
840d951bcSKonstantin Belousov  *
940d951bcSKonstantin Belousov  * Redistribution and use in source and binary forms, with or without
1040d951bcSKonstantin Belousov  * modification, are permitted provided that the following conditions
1140d951bcSKonstantin Belousov  * are met:
1240d951bcSKonstantin Belousov  * 1. Redistributions of source code must retain the above copyright
1340d951bcSKonstantin Belousov  *    notice, this list of conditions and the following disclaimer.
1440d951bcSKonstantin Belousov  * 2. Redistributions in binary form must reproduce the above copyright
1540d951bcSKonstantin Belousov  *    notice, this list of conditions and the following disclaimer in the
1640d951bcSKonstantin Belousov  *    documentation and/or other materials provided with the distribution.
1740d951bcSKonstantin Belousov  *
1840d951bcSKonstantin Belousov  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1940d951bcSKonstantin Belousov  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2040d951bcSKonstantin Belousov  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2140d951bcSKonstantin Belousov  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2240d951bcSKonstantin Belousov  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2340d951bcSKonstantin Belousov  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2440d951bcSKonstantin Belousov  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2540d951bcSKonstantin Belousov  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2640d951bcSKonstantin Belousov  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2740d951bcSKonstantin Belousov  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2840d951bcSKonstantin Belousov  * SUCH DAMAGE.
2940d951bcSKonstantin Belousov  */
3040d951bcSKonstantin Belousov 
315967352aSKonstantin Belousov #include "opt_acpi.h"
325967352aSKonstantin Belousov #if defined(__amd64__)
335967352aSKonstantin Belousov #define	DEV_APIC
345967352aSKonstantin Belousov #else
355967352aSKonstantin Belousov #include "opt_apic.h"
365967352aSKonstantin Belousov #endif
37e9d948cfSKonstantin Belousov #include "opt_ddb.h"
385967352aSKonstantin Belousov 
3940d951bcSKonstantin Belousov #include <sys/systm.h>
405967352aSKonstantin Belousov #include <sys/bus.h>
41ad794e6dSKonstantin Belousov #include <sys/kernel.h>
4240d951bcSKonstantin Belousov #include <sys/lock.h>
43ad794e6dSKonstantin Belousov #include <sys/malloc.h>
4440d951bcSKonstantin Belousov #include <sys/memdesc.h>
4540d951bcSKonstantin Belousov #include <sys/mutex.h>
4640d951bcSKonstantin Belousov #include <sys/sf_buf.h>
4740d951bcSKonstantin Belousov #include <sys/sysctl.h>
4840d951bcSKonstantin Belousov #include <sys/proc.h>
4940d951bcSKonstantin Belousov #include <sys/sched.h>
505967352aSKonstantin Belousov #include <sys/rman.h>
5140d951bcSKonstantin Belousov #include <sys/rwlock.h>
5240d951bcSKonstantin Belousov #include <sys/taskqueue.h>
5340d951bcSKonstantin Belousov #include <sys/tree.h>
5440d951bcSKonstantin Belousov #include <vm/vm.h>
55ad794e6dSKonstantin Belousov #include <vm/vm_extern.h>
56ad794e6dSKonstantin Belousov #include <vm/vm_kern.h>
57ad794e6dSKonstantin Belousov #include <vm/vm_map.h>
5840d951bcSKonstantin Belousov #include <vm/vm_object.h>
59ad794e6dSKonstantin Belousov #include <vm/vm_page.h>
6040d951bcSKonstantin Belousov #include <dev/pci/pcireg.h>
615967352aSKonstantin Belousov #include <dev/pci/pcivar.h>
6240d951bcSKonstantin Belousov #include <machine/atomic.h>
6340d951bcSKonstantin Belousov #include <machine/bus.h>
6440d951bcSKonstantin Belousov #include <machine/cpu.h>
6540d951bcSKonstantin Belousov #include <x86/include/busdma_impl.h>
6640d951bcSKonstantin Belousov #include <dev/iommu/busdma_iommu.h>
6740d951bcSKonstantin Belousov #include <dev/iommu/iommu.h>
6840d951bcSKonstantin Belousov #include <x86/iommu/x86_iommu.h>
6965b133e5SKonstantin Belousov #include <x86/iommu/iommu_intrmap.h>
705967352aSKonstantin Belousov #ifdef DEV_APIC
715967352aSKonstantin Belousov #include "pcib_if.h"
725967352aSKonstantin Belousov #include <machine/intr_machdep.h>
735967352aSKonstantin Belousov #include <x86/apicreg.h>
745967352aSKonstantin Belousov #include <x86/apicvar.h>
755967352aSKonstantin Belousov #endif
7640d951bcSKonstantin Belousov 
7740d951bcSKonstantin Belousov vm_page_t
7840d951bcSKonstantin Belousov iommu_pgalloc(vm_object_t obj, vm_pindex_t idx, int flags)
7940d951bcSKonstantin Belousov {
8040d951bcSKonstantin Belousov 	vm_page_t m;
8140d951bcSKonstantin Belousov 	int zeroed, aflags;
8240d951bcSKonstantin Belousov 
8340d951bcSKonstantin Belousov 	zeroed = (flags & IOMMU_PGF_ZERO) != 0 ? VM_ALLOC_ZERO : 0;
8440d951bcSKonstantin Belousov 	aflags = zeroed | VM_ALLOC_NOBUSY | VM_ALLOC_SYSTEM | VM_ALLOC_NODUMP |
8540d951bcSKonstantin Belousov 	    ((flags & IOMMU_PGF_WAITOK) != 0 ? VM_ALLOC_WAITFAIL :
8640d951bcSKonstantin Belousov 	    VM_ALLOC_NOWAIT);
8740d951bcSKonstantin Belousov 	for (;;) {
8840d951bcSKonstantin Belousov 		if ((flags & IOMMU_PGF_OBJL) == 0)
8940d951bcSKonstantin Belousov 			VM_OBJECT_WLOCK(obj);
9040d951bcSKonstantin Belousov 		m = vm_page_lookup(obj, idx);
9140d951bcSKonstantin Belousov 		if ((flags & IOMMU_PGF_NOALLOC) != 0 || m != NULL) {
9240d951bcSKonstantin Belousov 			if ((flags & IOMMU_PGF_OBJL) == 0)
9340d951bcSKonstantin Belousov 				VM_OBJECT_WUNLOCK(obj);
9440d951bcSKonstantin Belousov 			break;
9540d951bcSKonstantin Belousov 		}
9640d951bcSKonstantin Belousov 		m = vm_page_alloc_contig(obj, idx, aflags, 1, 0,
9740d951bcSKonstantin Belousov 		    iommu_high, PAGE_SIZE, 0, VM_MEMATTR_DEFAULT);
9840d951bcSKonstantin Belousov 		if ((flags & IOMMU_PGF_OBJL) == 0)
9940d951bcSKonstantin Belousov 			VM_OBJECT_WUNLOCK(obj);
10040d951bcSKonstantin Belousov 		if (m != NULL) {
10140d951bcSKonstantin Belousov 			if (zeroed && (m->flags & PG_ZERO) == 0)
10240d951bcSKonstantin Belousov 				pmap_zero_page(m);
10340d951bcSKonstantin Belousov 			atomic_add_int(&iommu_tbl_pagecnt, 1);
10440d951bcSKonstantin Belousov 			break;
10540d951bcSKonstantin Belousov 		}
10640d951bcSKonstantin Belousov 		if ((flags & IOMMU_PGF_WAITOK) == 0)
10740d951bcSKonstantin Belousov 			break;
10840d951bcSKonstantin Belousov 	}
10940d951bcSKonstantin Belousov 	return (m);
11040d951bcSKonstantin Belousov }
11140d951bcSKonstantin Belousov 
11240d951bcSKonstantin Belousov void
113f713ed66SKonstantin Belousov iommu_pgfree(vm_object_t obj, vm_pindex_t idx, int flags,
114f713ed66SKonstantin Belousov     struct iommu_map_entry *entry)
11540d951bcSKonstantin Belousov {
11640d951bcSKonstantin Belousov 	vm_page_t m;
11740d951bcSKonstantin Belousov 
11840d951bcSKonstantin Belousov 	if ((flags & IOMMU_PGF_OBJL) == 0)
11940d951bcSKonstantin Belousov 		VM_OBJECT_WLOCK(obj);
12040d951bcSKonstantin Belousov 	m = vm_page_grab(obj, idx, VM_ALLOC_NOCREAT);
12140d951bcSKonstantin Belousov 	if (m != NULL) {
122f713ed66SKonstantin Belousov 		if (entry == NULL) {
12340d951bcSKonstantin Belousov 			vm_page_free(m);
12440d951bcSKonstantin Belousov 			atomic_subtract_int(&iommu_tbl_pagecnt, 1);
125f713ed66SKonstantin Belousov 		} else {
126f713ed66SKonstantin Belousov 			vm_page_remove_xbusy(m);	/* keep page busy */
127f713ed66SKonstantin Belousov 			SLIST_INSERT_HEAD(&entry->pgtbl_free, m, plinks.s.ss);
128f713ed66SKonstantin Belousov 		}
12940d951bcSKonstantin Belousov 	}
13040d951bcSKonstantin Belousov 	if ((flags & IOMMU_PGF_OBJL) == 0)
13140d951bcSKonstantin Belousov 		VM_OBJECT_WUNLOCK(obj);
13240d951bcSKonstantin Belousov }
13340d951bcSKonstantin Belousov 
13440d951bcSKonstantin Belousov void *
13540d951bcSKonstantin Belousov iommu_map_pgtbl(vm_object_t obj, vm_pindex_t idx, int flags,
13640d951bcSKonstantin Belousov     struct sf_buf **sf)
13740d951bcSKonstantin Belousov {
13840d951bcSKonstantin Belousov 	vm_page_t m;
13940d951bcSKonstantin Belousov 	bool allocated;
14040d951bcSKonstantin Belousov 
14140d951bcSKonstantin Belousov 	if ((flags & IOMMU_PGF_OBJL) == 0)
14240d951bcSKonstantin Belousov 		VM_OBJECT_WLOCK(obj);
14340d951bcSKonstantin Belousov 	m = vm_page_lookup(obj, idx);
14440d951bcSKonstantin Belousov 	if (m == NULL && (flags & IOMMU_PGF_ALLOC) != 0) {
14540d951bcSKonstantin Belousov 		m = iommu_pgalloc(obj, idx, flags | IOMMU_PGF_OBJL);
14640d951bcSKonstantin Belousov 		allocated = true;
14740d951bcSKonstantin Belousov 	} else
14840d951bcSKonstantin Belousov 		allocated = false;
14940d951bcSKonstantin Belousov 	if (m == NULL) {
15040d951bcSKonstantin Belousov 		if ((flags & IOMMU_PGF_OBJL) == 0)
15140d951bcSKonstantin Belousov 			VM_OBJECT_WUNLOCK(obj);
15240d951bcSKonstantin Belousov 		return (NULL);
15340d951bcSKonstantin Belousov 	}
15440d951bcSKonstantin Belousov 	/* Sleepable allocations cannot fail. */
15540d951bcSKonstantin Belousov 	if ((flags & IOMMU_PGF_WAITOK) != 0)
15640d951bcSKonstantin Belousov 		VM_OBJECT_WUNLOCK(obj);
15740d951bcSKonstantin Belousov 	sched_pin();
15840d951bcSKonstantin Belousov 	*sf = sf_buf_alloc(m, SFB_CPUPRIVATE | ((flags & IOMMU_PGF_WAITOK)
15940d951bcSKonstantin Belousov 	    == 0 ? SFB_NOWAIT : 0));
16040d951bcSKonstantin Belousov 	if (*sf == NULL) {
16140d951bcSKonstantin Belousov 		sched_unpin();
16240d951bcSKonstantin Belousov 		if (allocated) {
16340d951bcSKonstantin Belousov 			VM_OBJECT_ASSERT_WLOCKED(obj);
164f713ed66SKonstantin Belousov 			iommu_pgfree(obj, m->pindex, flags | IOMMU_PGF_OBJL,
165f713ed66SKonstantin Belousov 			    NULL);
16640d951bcSKonstantin Belousov 		}
16740d951bcSKonstantin Belousov 		if ((flags & IOMMU_PGF_OBJL) == 0)
16840d951bcSKonstantin Belousov 			VM_OBJECT_WUNLOCK(obj);
16940d951bcSKonstantin Belousov 		return (NULL);
17040d951bcSKonstantin Belousov 	}
17140d951bcSKonstantin Belousov 	if ((flags & (IOMMU_PGF_WAITOK | IOMMU_PGF_OBJL)) ==
17240d951bcSKonstantin Belousov 	    (IOMMU_PGF_WAITOK | IOMMU_PGF_OBJL))
17340d951bcSKonstantin Belousov 		VM_OBJECT_WLOCK(obj);
17440d951bcSKonstantin Belousov 	else if ((flags & (IOMMU_PGF_WAITOK | IOMMU_PGF_OBJL)) == 0)
17540d951bcSKonstantin Belousov 		VM_OBJECT_WUNLOCK(obj);
17640d951bcSKonstantin Belousov 	return ((void *)sf_buf_kva(*sf));
17740d951bcSKonstantin Belousov }
17840d951bcSKonstantin Belousov 
17940d951bcSKonstantin Belousov void
18040d951bcSKonstantin Belousov iommu_unmap_pgtbl(struct sf_buf *sf)
18140d951bcSKonstantin Belousov {
18240d951bcSKonstantin Belousov 
18340d951bcSKonstantin Belousov 	sf_buf_free(sf);
18440d951bcSKonstantin Belousov 	sched_unpin();
18540d951bcSKonstantin Belousov }
18640d951bcSKonstantin Belousov 
18740d951bcSKonstantin Belousov iommu_haddr_t iommu_high;
18840d951bcSKonstantin Belousov int iommu_tbl_pagecnt;
18940d951bcSKonstantin Belousov 
19040d951bcSKonstantin Belousov SYSCTL_NODE(_hw_iommu, OID_AUTO, dmar, CTLFLAG_RD | CTLFLAG_MPSAFE,
19140d951bcSKonstantin Belousov     NULL, "");
1920386b245SKonstantin Belousov SYSCTL_INT(_hw_iommu, OID_AUTO, tbl_pagecnt, CTLFLAG_RD,
19340d951bcSKonstantin Belousov     &iommu_tbl_pagecnt, 0,
1940386b245SKonstantin Belousov     "Count of pages used for IOMMU pagetables");
1950386b245SKonstantin Belousov 
1960386b245SKonstantin Belousov int iommu_qi_batch_coalesce = 100;
1970386b245SKonstantin Belousov SYSCTL_INT(_hw_iommu, OID_AUTO, batch_coalesce, CTLFLAG_RWTUN,
1980386b245SKonstantin Belousov     &iommu_qi_batch_coalesce, 0,
1990386b245SKonstantin Belousov     "Number of qi batches between interrupt");
20065b133e5SKonstantin Belousov 
20199e3d96fSKonstantin Belousov static struct iommu_unit *
20299e3d96fSKonstantin Belousov x86_no_iommu_find(device_t dev, bool verbose)
20399e3d96fSKonstantin Belousov {
20499e3d96fSKonstantin Belousov 	return (NULL);
20599e3d96fSKonstantin Belousov }
20699e3d96fSKonstantin Belousov 
20799e3d96fSKonstantin Belousov static int
20899e3d96fSKonstantin Belousov x86_no_iommu_alloc_msi_intr(device_t src, u_int *cookies, u_int count)
20999e3d96fSKonstantin Belousov {
21099e3d96fSKonstantin Belousov 	return (EOPNOTSUPP);
21199e3d96fSKonstantin Belousov }
21299e3d96fSKonstantin Belousov 
21399e3d96fSKonstantin Belousov static int
21499e3d96fSKonstantin Belousov x86_no_iommu_map_msi_intr(device_t src, u_int cpu, u_int vector,
21599e3d96fSKonstantin Belousov     u_int cookie, uint64_t *addr, uint32_t *data)
21699e3d96fSKonstantin Belousov {
21799e3d96fSKonstantin Belousov 	return (EOPNOTSUPP);
21899e3d96fSKonstantin Belousov }
21999e3d96fSKonstantin Belousov 
22099e3d96fSKonstantin Belousov static int
22199e3d96fSKonstantin Belousov x86_no_iommu_unmap_msi_intr(device_t src, u_int cookie)
22299e3d96fSKonstantin Belousov {
22399e3d96fSKonstantin Belousov 	return (0);
22499e3d96fSKonstantin Belousov }
22599e3d96fSKonstantin Belousov 
22699e3d96fSKonstantin Belousov static int
22799e3d96fSKonstantin Belousov x86_no_iommu_map_ioapic_intr(u_int ioapic_id, u_int cpu, u_int vector,
22899e3d96fSKonstantin Belousov     bool edge, bool activehi, int irq, u_int *cookie, uint32_t *hi,
22999e3d96fSKonstantin Belousov     uint32_t *lo)
23099e3d96fSKonstantin Belousov {
23199e3d96fSKonstantin Belousov 	return (EOPNOTSUPP);
23299e3d96fSKonstantin Belousov }
23399e3d96fSKonstantin Belousov 
23499e3d96fSKonstantin Belousov static int
23599e3d96fSKonstantin Belousov x86_no_iommu_unmap_ioapic_intr(u_int ioapic_id, u_int *cookie)
23699e3d96fSKonstantin Belousov {
23799e3d96fSKonstantin Belousov 	return (0);
23899e3d96fSKonstantin Belousov }
23999e3d96fSKonstantin Belousov 
24099e3d96fSKonstantin Belousov static struct x86_iommu x86_no_iommu = {
24199e3d96fSKonstantin Belousov 	.find = x86_no_iommu_find,
24299e3d96fSKonstantin Belousov 	.alloc_msi_intr = x86_no_iommu_alloc_msi_intr,
24399e3d96fSKonstantin Belousov 	.map_msi_intr = x86_no_iommu_map_msi_intr,
24499e3d96fSKonstantin Belousov 	.unmap_msi_intr = x86_no_iommu_unmap_msi_intr,
24599e3d96fSKonstantin Belousov 	.map_ioapic_intr = x86_no_iommu_map_ioapic_intr,
24699e3d96fSKonstantin Belousov 	.unmap_ioapic_intr = x86_no_iommu_unmap_ioapic_intr,
24799e3d96fSKonstantin Belousov };
24899e3d96fSKonstantin Belousov 
24999e3d96fSKonstantin Belousov static struct x86_iommu *x86_iommu = &x86_no_iommu;
25065b133e5SKonstantin Belousov 
25165b133e5SKonstantin Belousov void
25265b133e5SKonstantin Belousov set_x86_iommu(struct x86_iommu *x)
25365b133e5SKonstantin Belousov {
25499e3d96fSKonstantin Belousov 	MPASS(x86_iommu == &x86_no_iommu);
25565b133e5SKonstantin Belousov 	x86_iommu = x;
25665b133e5SKonstantin Belousov }
25765b133e5SKonstantin Belousov 
25865b133e5SKonstantin Belousov struct x86_iommu *
25965b133e5SKonstantin Belousov get_x86_iommu(void)
26065b133e5SKonstantin Belousov {
26165b133e5SKonstantin Belousov 	return (x86_iommu);
26265b133e5SKonstantin Belousov }
26365b133e5SKonstantin Belousov 
26465b133e5SKonstantin Belousov void
26565b133e5SKonstantin Belousov iommu_domain_unload_entry(struct iommu_map_entry *entry, bool free,
26665b133e5SKonstantin Belousov     bool cansleep)
26765b133e5SKonstantin Belousov {
26865b133e5SKonstantin Belousov 	x86_iommu->domain_unload_entry(entry, free, cansleep);
26965b133e5SKonstantin Belousov }
27065b133e5SKonstantin Belousov 
27165b133e5SKonstantin Belousov void
27265b133e5SKonstantin Belousov iommu_domain_unload(struct iommu_domain *iodom,
27365b133e5SKonstantin Belousov     struct iommu_map_entries_tailq *entries, bool cansleep)
27465b133e5SKonstantin Belousov {
27565b133e5SKonstantin Belousov 	x86_iommu->domain_unload(iodom, entries, cansleep);
27665b133e5SKonstantin Belousov }
27765b133e5SKonstantin Belousov 
27865b133e5SKonstantin Belousov struct iommu_ctx *
27965b133e5SKonstantin Belousov iommu_get_ctx(struct iommu_unit *iommu, device_t dev, uint16_t rid,
28065b133e5SKonstantin Belousov     bool id_mapped, bool rmrr_init)
28165b133e5SKonstantin Belousov {
28265b133e5SKonstantin Belousov 	return (x86_iommu->get_ctx(iommu, dev, rid, id_mapped, rmrr_init));
28365b133e5SKonstantin Belousov }
28465b133e5SKonstantin Belousov 
28565b133e5SKonstantin Belousov void
28665b133e5SKonstantin Belousov iommu_free_ctx_locked(struct iommu_unit *iommu, struct iommu_ctx *context)
28765b133e5SKonstantin Belousov {
28865b133e5SKonstantin Belousov 	x86_iommu->free_ctx_locked(iommu, context);
28965b133e5SKonstantin Belousov }
29065b133e5SKonstantin Belousov 
29165b133e5SKonstantin Belousov struct iommu_unit *
29265b133e5SKonstantin Belousov iommu_find(device_t dev, bool verbose)
29365b133e5SKonstantin Belousov {
29465b133e5SKonstantin Belousov 	return (x86_iommu->find(dev, verbose));
29565b133e5SKonstantin Belousov }
29665b133e5SKonstantin Belousov 
29765b133e5SKonstantin Belousov int
29865b133e5SKonstantin Belousov iommu_alloc_msi_intr(device_t src, u_int *cookies, u_int count)
29965b133e5SKonstantin Belousov {
30065b133e5SKonstantin Belousov 	return (x86_iommu->alloc_msi_intr(src, cookies, count));
30165b133e5SKonstantin Belousov }
30265b133e5SKonstantin Belousov 
30365b133e5SKonstantin Belousov int
30465b133e5SKonstantin Belousov iommu_map_msi_intr(device_t src, u_int cpu, u_int vector, u_int cookie,
30565b133e5SKonstantin Belousov     uint64_t *addr, uint32_t *data)
30665b133e5SKonstantin Belousov {
30765b133e5SKonstantin Belousov 	return (x86_iommu->map_msi_intr(src, cpu, vector, cookie,
30865b133e5SKonstantin Belousov 	    addr, data));
30965b133e5SKonstantin Belousov }
31065b133e5SKonstantin Belousov 
31165b133e5SKonstantin Belousov int
31265b133e5SKonstantin Belousov iommu_unmap_msi_intr(device_t src, u_int cookie)
31365b133e5SKonstantin Belousov {
31465b133e5SKonstantin Belousov 	return (x86_iommu->unmap_msi_intr(src, cookie));
31565b133e5SKonstantin Belousov }
31665b133e5SKonstantin Belousov 
31765b133e5SKonstantin Belousov int
31865b133e5SKonstantin Belousov iommu_map_ioapic_intr(u_int ioapic_id, u_int cpu, u_int vector, bool edge,
31965b133e5SKonstantin Belousov     bool activehi, int irq, u_int *cookie, uint32_t *hi, uint32_t *lo)
32065b133e5SKonstantin Belousov {
32165b133e5SKonstantin Belousov 	return (x86_iommu->map_ioapic_intr(ioapic_id, cpu, vector, edge,
32265b133e5SKonstantin Belousov 	    activehi, irq, cookie, hi, lo));
32365b133e5SKonstantin Belousov }
32465b133e5SKonstantin Belousov 
32565b133e5SKonstantin Belousov int
32665b133e5SKonstantin Belousov iommu_unmap_ioapic_intr(u_int ioapic_id, u_int *cookie)
32765b133e5SKonstantin Belousov {
32865b133e5SKonstantin Belousov 	return (x86_iommu->unmap_ioapic_intr(ioapic_id, cookie));
32965b133e5SKonstantin Belousov }
330ad794e6dSKonstantin Belousov 
331ba33e74cSKonstantin Belousov void
332ba33e74cSKonstantin Belousov iommu_unit_pre_instantiate_ctx(struct iommu_unit *unit)
333ba33e74cSKonstantin Belousov {
334ba33e74cSKonstantin Belousov 	x86_iommu->unit_pre_instantiate_ctx(unit);
335ba33e74cSKonstantin Belousov }
336ba33e74cSKonstantin Belousov 
337ad794e6dSKonstantin Belousov #define	IOMMU2X86C(iommu)	(x86_iommu->get_x86_common(iommu))
338ad794e6dSKonstantin Belousov 
339ad794e6dSKonstantin Belousov static bool
340ad794e6dSKonstantin Belousov iommu_qi_seq_processed(struct iommu_unit *unit,
341ad794e6dSKonstantin Belousov     const struct iommu_qi_genseq *pseq)
342ad794e6dSKonstantin Belousov {
343ad794e6dSKonstantin Belousov 	struct x86_unit_common *x86c;
344ad794e6dSKonstantin Belousov 	u_int gen;
345ad794e6dSKonstantin Belousov 
346ad794e6dSKonstantin Belousov 	x86c = IOMMU2X86C(unit);
347ad794e6dSKonstantin Belousov 	gen = x86c->inv_waitd_gen;
348eb832642SKonstantin Belousov 	return (pseq->gen < gen || (pseq->gen == gen && pseq->seq <=
349eb832642SKonstantin Belousov 	    atomic_load_64(&x86c->inv_waitd_seq_hw)));
350ad794e6dSKonstantin Belousov }
351ad794e6dSKonstantin Belousov 
352ad794e6dSKonstantin Belousov void
353ad794e6dSKonstantin Belousov iommu_qi_emit_wait_seq(struct iommu_unit *unit, struct iommu_qi_genseq *pseq,
354ad794e6dSKonstantin Belousov     bool emit_wait)
355ad794e6dSKonstantin Belousov {
356ad794e6dSKonstantin Belousov 	struct x86_unit_common *x86c;
357ad794e6dSKonstantin Belousov 	struct iommu_qi_genseq gsec;
358ad794e6dSKonstantin Belousov 	uint32_t seq;
359ad794e6dSKonstantin Belousov 
360ad794e6dSKonstantin Belousov 	KASSERT(pseq != NULL, ("wait descriptor with no place for seq"));
361ad794e6dSKonstantin Belousov 	IOMMU_ASSERT_LOCKED(unit);
362ad794e6dSKonstantin Belousov 	x86c = IOMMU2X86C(unit);
363ad794e6dSKonstantin Belousov 
364ad794e6dSKonstantin Belousov 	if (x86c->inv_waitd_seq == 0xffffffff) {
365ad794e6dSKonstantin Belousov 		gsec.gen = x86c->inv_waitd_gen;
366ad794e6dSKonstantin Belousov 		gsec.seq = x86c->inv_waitd_seq;
367ad794e6dSKonstantin Belousov 		x86_iommu->qi_ensure(unit, 1);
368ad794e6dSKonstantin Belousov 		x86_iommu->qi_emit_wait_descr(unit, gsec.seq, false,
369ad794e6dSKonstantin Belousov 		    true, false);
370ad794e6dSKonstantin Belousov 		x86_iommu->qi_advance_tail(unit);
371ad794e6dSKonstantin Belousov 		while (!iommu_qi_seq_processed(unit, &gsec))
372ad794e6dSKonstantin Belousov 			cpu_spinwait();
373ad794e6dSKonstantin Belousov 		x86c->inv_waitd_gen++;
374ad794e6dSKonstantin Belousov 		x86c->inv_waitd_seq = 1;
375ad794e6dSKonstantin Belousov 	}
376ad794e6dSKonstantin Belousov 	seq = x86c->inv_waitd_seq++;
377ad794e6dSKonstantin Belousov 	pseq->gen = x86c->inv_waitd_gen;
378ad794e6dSKonstantin Belousov 	pseq->seq = seq;
379ad794e6dSKonstantin Belousov 	if (emit_wait) {
380ad794e6dSKonstantin Belousov 		x86_iommu->qi_ensure(unit, 1);
381ad794e6dSKonstantin Belousov 		x86_iommu->qi_emit_wait_descr(unit, seq, true, true, false);
382ad794e6dSKonstantin Belousov 	}
383ad794e6dSKonstantin Belousov }
384ad794e6dSKonstantin Belousov 
385ad794e6dSKonstantin Belousov /*
386ad794e6dSKonstantin Belousov  * To avoid missed wakeups, callers must increment the unit's waiters count
387ad794e6dSKonstantin Belousov  * before advancing the tail past the wait descriptor.
388ad794e6dSKonstantin Belousov  */
389ad794e6dSKonstantin Belousov void
390ad794e6dSKonstantin Belousov iommu_qi_wait_for_seq(struct iommu_unit *unit, const struct iommu_qi_genseq *
391ad794e6dSKonstantin Belousov     gseq, bool nowait)
392ad794e6dSKonstantin Belousov {
393ad794e6dSKonstantin Belousov 	struct x86_unit_common *x86c;
394ad794e6dSKonstantin Belousov 
395ad794e6dSKonstantin Belousov 	IOMMU_ASSERT_LOCKED(unit);
396ad794e6dSKonstantin Belousov 	x86c = IOMMU2X86C(unit);
397ad794e6dSKonstantin Belousov 
398ad794e6dSKonstantin Belousov 	KASSERT(x86c->inv_seq_waiters > 0, ("%s: no waiters", __func__));
399ad794e6dSKonstantin Belousov 	while (!iommu_qi_seq_processed(unit, gseq)) {
400ad794e6dSKonstantin Belousov 		if (cold || nowait) {
401ad794e6dSKonstantin Belousov 			cpu_spinwait();
402ad794e6dSKonstantin Belousov 		} else {
403ad794e6dSKonstantin Belousov 			msleep(&x86c->inv_seq_waiters, &unit->lock, 0,
404ad794e6dSKonstantin Belousov 			    "dmarse", hz);
405ad794e6dSKonstantin Belousov 		}
406ad794e6dSKonstantin Belousov 	}
407ad794e6dSKonstantin Belousov 	x86c->inv_seq_waiters--;
408ad794e6dSKonstantin Belousov }
409ad794e6dSKonstantin Belousov 
410ad794e6dSKonstantin Belousov /*
411ad794e6dSKonstantin Belousov  * The caller must not be using the entry's dmamap_link field.
412ad794e6dSKonstantin Belousov  */
413ad794e6dSKonstantin Belousov void
414ad794e6dSKonstantin Belousov iommu_qi_invalidate_locked(struct iommu_domain *domain,
415ad794e6dSKonstantin Belousov     struct iommu_map_entry *entry, bool emit_wait)
416ad794e6dSKonstantin Belousov {
417ad794e6dSKonstantin Belousov 	struct iommu_unit *unit;
418ad794e6dSKonstantin Belousov 	struct x86_unit_common *x86c;
419ad794e6dSKonstantin Belousov 
420ad794e6dSKonstantin Belousov 	unit = domain->iommu;
421ad794e6dSKonstantin Belousov 	x86c = IOMMU2X86C(unit);
422ad794e6dSKonstantin Belousov 	IOMMU_ASSERT_LOCKED(unit);
423ad794e6dSKonstantin Belousov 
424ad794e6dSKonstantin Belousov 	x86_iommu->qi_invalidate_emit(domain, entry->start, entry->end -
425ad794e6dSKonstantin Belousov 	    entry->start, &entry->gseq, emit_wait);
426ad794e6dSKonstantin Belousov 
427ad794e6dSKonstantin Belousov 	/*
428ad794e6dSKonstantin Belousov 	 * To avoid a data race in dmar_qi_task(), the entry's gseq must be
429ad794e6dSKonstantin Belousov 	 * initialized before the entry is added to the TLB flush list, and the
430ad794e6dSKonstantin Belousov 	 * entry must be added to that list before the tail is advanced.  More
431ad794e6dSKonstantin Belousov 	 * precisely, the tail must not be advanced past the wait descriptor
432ad794e6dSKonstantin Belousov 	 * that will generate the interrupt that schedules dmar_qi_task() for
433ad794e6dSKonstantin Belousov 	 * execution before the entry is added to the list.  While an earlier
434ad794e6dSKonstantin Belousov 	 * call to dmar_qi_ensure() might have advanced the tail, it will not
435ad794e6dSKonstantin Belousov 	 * advance it past the wait descriptor.
436ad794e6dSKonstantin Belousov 	 *
437ad794e6dSKonstantin Belousov 	 * See the definition of struct dmar_unit for more information on
438ad794e6dSKonstantin Belousov 	 * synchronization.
439ad794e6dSKonstantin Belousov 	 */
440ad794e6dSKonstantin Belousov 	entry->tlb_flush_next = NULL;
441ad794e6dSKonstantin Belousov 	atomic_store_rel_ptr((uintptr_t *)&x86c->tlb_flush_tail->
442ad794e6dSKonstantin Belousov 	    tlb_flush_next, (uintptr_t)entry);
443ad794e6dSKonstantin Belousov 	x86c->tlb_flush_tail = entry;
444ad794e6dSKonstantin Belousov 
445ad794e6dSKonstantin Belousov 	x86_iommu->qi_advance_tail(unit);
446ad794e6dSKonstantin Belousov }
447ad794e6dSKonstantin Belousov 
448ad794e6dSKonstantin Belousov void
449ad794e6dSKonstantin Belousov iommu_qi_invalidate_sync(struct iommu_domain *domain, iommu_gaddr_t base,
450ad794e6dSKonstantin Belousov     iommu_gaddr_t size, bool cansleep)
451ad794e6dSKonstantin Belousov {
452ad794e6dSKonstantin Belousov 	struct iommu_unit *unit;
453ad794e6dSKonstantin Belousov 	struct iommu_qi_genseq gseq;
454ad794e6dSKonstantin Belousov 
455ad794e6dSKonstantin Belousov 	unit = domain->iommu;
456ad794e6dSKonstantin Belousov 	IOMMU_LOCK(unit);
457ad794e6dSKonstantin Belousov 	x86_iommu->qi_invalidate_emit(domain, base, size, &gseq, true);
458ad794e6dSKonstantin Belousov 
459ad794e6dSKonstantin Belousov 	/*
460ad794e6dSKonstantin Belousov 	 * To avoid a missed wakeup in iommu_qi_task(), the unit's
461ad794e6dSKonstantin Belousov 	 * waiters count must be incremented before the tail is
462ad794e6dSKonstantin Belousov 	 * advanced.
463ad794e6dSKonstantin Belousov 	 */
464ad794e6dSKonstantin Belousov 	IOMMU2X86C(unit)->inv_seq_waiters++;
465ad794e6dSKonstantin Belousov 
466ad794e6dSKonstantin Belousov 	x86_iommu->qi_advance_tail(unit);
467ad794e6dSKonstantin Belousov 	iommu_qi_wait_for_seq(unit, &gseq, !cansleep);
468ad794e6dSKonstantin Belousov 	IOMMU_UNLOCK(unit);
469ad794e6dSKonstantin Belousov }
470ad794e6dSKonstantin Belousov 
471ad794e6dSKonstantin Belousov void
472ad794e6dSKonstantin Belousov iommu_qi_drain_tlb_flush(struct iommu_unit *unit)
473ad794e6dSKonstantin Belousov {
474ad794e6dSKonstantin Belousov 	struct x86_unit_common *x86c;
475ad794e6dSKonstantin Belousov 	struct iommu_map_entry *entry, *head;
476ad794e6dSKonstantin Belousov 
477ad794e6dSKonstantin Belousov 	x86c = IOMMU2X86C(unit);
478ad794e6dSKonstantin Belousov 	for (head = x86c->tlb_flush_head;; head = entry) {
479ad794e6dSKonstantin Belousov 		entry = (struct iommu_map_entry *)
480ad794e6dSKonstantin Belousov 		    atomic_load_acq_ptr((uintptr_t *)&head->tlb_flush_next);
481ad794e6dSKonstantin Belousov 		if (entry == NULL ||
482ad794e6dSKonstantin Belousov 		    !iommu_qi_seq_processed(unit, &entry->gseq))
483ad794e6dSKonstantin Belousov 			break;
484ad794e6dSKonstantin Belousov 		x86c->tlb_flush_head = entry;
485ad794e6dSKonstantin Belousov 		iommu_gas_free_entry(head);
486ad794e6dSKonstantin Belousov 		if ((entry->flags & IOMMU_MAP_ENTRY_RMRR) != 0)
487ad794e6dSKonstantin Belousov 			iommu_gas_free_region(entry);
488ad794e6dSKonstantin Belousov 		else
489ad794e6dSKonstantin Belousov 			iommu_gas_free_space(entry);
490ad794e6dSKonstantin Belousov 	}
491ad794e6dSKonstantin Belousov }
492ad794e6dSKonstantin Belousov 
493ad794e6dSKonstantin Belousov void
494ad794e6dSKonstantin Belousov iommu_qi_common_init(struct iommu_unit *unit, task_fn_t qi_task)
495ad794e6dSKonstantin Belousov {
496ad794e6dSKonstantin Belousov 	struct x86_unit_common *x86c;
497ad794e6dSKonstantin Belousov 	u_int qi_sz;
498ad794e6dSKonstantin Belousov 
499ad794e6dSKonstantin Belousov 	x86c = IOMMU2X86C(unit);
500ad794e6dSKonstantin Belousov 
501ad794e6dSKonstantin Belousov 	x86c->tlb_flush_head = x86c->tlb_flush_tail =
502ad794e6dSKonstantin Belousov             iommu_gas_alloc_entry(NULL, 0);
503ad794e6dSKonstantin Belousov 	TASK_INIT(&x86c->qi_task, 0, qi_task, unit);
504ad794e6dSKonstantin Belousov 	x86c->qi_taskqueue = taskqueue_create_fast("iommuqf", M_WAITOK,
505ad794e6dSKonstantin Belousov 	    taskqueue_thread_enqueue, &x86c->qi_taskqueue);
506ad794e6dSKonstantin Belousov 	taskqueue_start_threads(&x86c->qi_taskqueue, 1, PI_AV,
507ad794e6dSKonstantin Belousov 	    "iommu%d qi taskq", unit->unit);
508ad794e6dSKonstantin Belousov 
509ad794e6dSKonstantin Belousov 	x86c->inv_waitd_gen = 0;
510ad794e6dSKonstantin Belousov 	x86c->inv_waitd_seq = 1;
511ad794e6dSKonstantin Belousov 
512ad794e6dSKonstantin Belousov 	qi_sz = 3;
513ad794e6dSKonstantin Belousov 	TUNABLE_INT_FETCH("hw.iommu.qi_size", &qi_sz);
514ad794e6dSKonstantin Belousov 	if (qi_sz > x86c->qi_buf_maxsz)
515ad794e6dSKonstantin Belousov 		qi_sz = x86c->qi_buf_maxsz;
516ad794e6dSKonstantin Belousov 	x86c->inv_queue_size = (1ULL << qi_sz) * PAGE_SIZE;
517ad794e6dSKonstantin Belousov 	/* Reserve one descriptor to prevent wraparound. */
518ad794e6dSKonstantin Belousov 	x86c->inv_queue_avail = x86c->inv_queue_size -
519ad794e6dSKonstantin Belousov 	    x86c->qi_cmd_sz;
520ad794e6dSKonstantin Belousov 
521ad794e6dSKonstantin Belousov 	/*
522ad794e6dSKonstantin Belousov 	 * The invalidation queue reads by DMARs/AMDIOMMUs are always
523ad794e6dSKonstantin Belousov 	 * coherent.
524ad794e6dSKonstantin Belousov 	 */
525ad794e6dSKonstantin Belousov 	x86c->inv_queue = kmem_alloc_contig(x86c->inv_queue_size,
526ad794e6dSKonstantin Belousov 	    M_WAITOK | M_ZERO, 0, iommu_high, PAGE_SIZE, 0,
527ad794e6dSKonstantin Belousov 	    VM_MEMATTR_DEFAULT);
528ad794e6dSKonstantin Belousov 	x86c->inv_waitd_seq_hw_phys = pmap_kextract(
529ad794e6dSKonstantin Belousov 	    (vm_offset_t)&x86c->inv_waitd_seq_hw);
530ad794e6dSKonstantin Belousov }
531ad794e6dSKonstantin Belousov 
532ad794e6dSKonstantin Belousov void
533ad794e6dSKonstantin Belousov iommu_qi_common_fini(struct iommu_unit *unit, void (*disable_qi)(
534ad794e6dSKonstantin Belousov     struct iommu_unit *))
535ad794e6dSKonstantin Belousov {
536ad794e6dSKonstantin Belousov 	struct x86_unit_common *x86c;
537ad794e6dSKonstantin Belousov 	struct iommu_qi_genseq gseq;
538ad794e6dSKonstantin Belousov 
539ad794e6dSKonstantin Belousov 	x86c = IOMMU2X86C(unit);
540ad794e6dSKonstantin Belousov 
541ad794e6dSKonstantin Belousov 	taskqueue_drain(x86c->qi_taskqueue, &x86c->qi_task);
542ad794e6dSKonstantin Belousov 	taskqueue_free(x86c->qi_taskqueue);
543ad794e6dSKonstantin Belousov 	x86c->qi_taskqueue = NULL;
544ad794e6dSKonstantin Belousov 
545ad794e6dSKonstantin Belousov 	IOMMU_LOCK(unit);
546ad794e6dSKonstantin Belousov 	/* quisce */
547ad794e6dSKonstantin Belousov 	x86_iommu->qi_ensure(unit, 1);
548ad794e6dSKonstantin Belousov 	iommu_qi_emit_wait_seq(unit, &gseq, true);
549ad794e6dSKonstantin Belousov 	/* See iommu_qi_invalidate_locked(). */
550ad794e6dSKonstantin Belousov 	x86c->inv_seq_waiters++;
551ad794e6dSKonstantin Belousov 	x86_iommu->qi_advance_tail(unit);
552ad794e6dSKonstantin Belousov 	iommu_qi_wait_for_seq(unit, &gseq, false);
553ad794e6dSKonstantin Belousov 	/* only after the quisce, disable queue */
554ad794e6dSKonstantin Belousov 	disable_qi(unit);
555ad794e6dSKonstantin Belousov 	KASSERT(x86c->inv_seq_waiters == 0,
556ad794e6dSKonstantin Belousov 	    ("iommu%d: waiters on disabled queue", unit->unit));
557ad794e6dSKonstantin Belousov 	IOMMU_UNLOCK(unit);
558ad794e6dSKonstantin Belousov 
559ad794e6dSKonstantin Belousov 	kmem_free(x86c->inv_queue, x86c->inv_queue_size);
560ad794e6dSKonstantin Belousov 	x86c->inv_queue = NULL;
561ad794e6dSKonstantin Belousov 	x86c->inv_queue_size = 0;
562ad794e6dSKonstantin Belousov }
5635967352aSKonstantin Belousov 
5645967352aSKonstantin Belousov int
5655967352aSKonstantin Belousov iommu_alloc_irq(struct iommu_unit *unit, int idx)
5665967352aSKonstantin Belousov {
5675967352aSKonstantin Belousov 	device_t dev, pcib;
5685967352aSKonstantin Belousov 	struct iommu_msi_data *dmd;
5695967352aSKonstantin Belousov 	uint64_t msi_addr;
5705967352aSKonstantin Belousov 	uint32_t msi_data;
5715967352aSKonstantin Belousov 	int error;
5725967352aSKonstantin Belousov 
5735967352aSKonstantin Belousov 	MPASS(idx >= 0 || idx < IOMMU_MAX_MSI);
5745967352aSKonstantin Belousov 
5755967352aSKonstantin Belousov 	dev = unit->dev;
5765967352aSKonstantin Belousov 	dmd = &IOMMU2X86C(unit)->intrs[idx];
5775967352aSKonstantin Belousov 	pcib = device_get_parent(device_get_parent(dev)); /* Really not pcib */
5785967352aSKonstantin Belousov 	error = PCIB_ALLOC_MSIX(pcib, dev, &dmd->irq);
5795967352aSKonstantin Belousov 	if (error != 0) {
5805967352aSKonstantin Belousov 		device_printf(dev, "cannot allocate %s interrupt, %d\n",
5815967352aSKonstantin Belousov 		    dmd->name, error);
5825967352aSKonstantin Belousov 		goto err1;
5835967352aSKonstantin Belousov 	}
5845967352aSKonstantin Belousov 	error = bus_set_resource(dev, SYS_RES_IRQ, dmd->irq_rid,
5855967352aSKonstantin Belousov 	    dmd->irq, 1);
5865967352aSKonstantin Belousov 	if (error != 0) {
5875967352aSKonstantin Belousov 		device_printf(dev, "cannot set %s interrupt resource, %d\n",
5885967352aSKonstantin Belousov 		    dmd->name, error);
5895967352aSKonstantin Belousov 		goto err2;
5905967352aSKonstantin Belousov 	}
5915967352aSKonstantin Belousov 	dmd->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
5925967352aSKonstantin Belousov 	    &dmd->irq_rid, RF_ACTIVE);
5935967352aSKonstantin Belousov 	if (dmd->irq_res == NULL) {
5945967352aSKonstantin Belousov 		device_printf(dev,
5955967352aSKonstantin Belousov 		    "cannot allocate resource for %s interrupt\n", dmd->name);
5965967352aSKonstantin Belousov 		error = ENXIO;
5975967352aSKonstantin Belousov 		goto err3;
5985967352aSKonstantin Belousov 	}
5995967352aSKonstantin Belousov 	error = bus_setup_intr(dev, dmd->irq_res, INTR_TYPE_MISC,
6005967352aSKonstantin Belousov 	    dmd->handler, NULL, unit, &dmd->intr_handle);
6015967352aSKonstantin Belousov 	if (error != 0) {
6025967352aSKonstantin Belousov 		device_printf(dev, "cannot setup %s interrupt, %d\n",
6035967352aSKonstantin Belousov 		    dmd->name, error);
6045967352aSKonstantin Belousov 		goto err4;
6055967352aSKonstantin Belousov 	}
6065967352aSKonstantin Belousov 	bus_describe_intr(dev, dmd->irq_res, dmd->intr_handle, "%s", dmd->name);
6075967352aSKonstantin Belousov 	error = PCIB_MAP_MSI(pcib, dev, dmd->irq, &msi_addr, &msi_data);
6085967352aSKonstantin Belousov 	if (error != 0) {
6095967352aSKonstantin Belousov 		device_printf(dev, "cannot map %s interrupt, %d\n",
6105967352aSKonstantin Belousov 		    dmd->name, error);
6115967352aSKonstantin Belousov 		goto err5;
6125967352aSKonstantin Belousov 	}
6135967352aSKonstantin Belousov 
6145967352aSKonstantin Belousov 	dmd->msi_data = msi_data;
6155967352aSKonstantin Belousov 	dmd->msi_addr = msi_addr;
6165967352aSKonstantin Belousov 
6175967352aSKonstantin Belousov 	return (0);
6185967352aSKonstantin Belousov 
6195967352aSKonstantin Belousov err5:
6205967352aSKonstantin Belousov 	bus_teardown_intr(dev, dmd->irq_res, dmd->intr_handle);
6215967352aSKonstantin Belousov err4:
6225967352aSKonstantin Belousov 	bus_release_resource(dev, SYS_RES_IRQ, dmd->irq_rid, dmd->irq_res);
6235967352aSKonstantin Belousov err3:
6245967352aSKonstantin Belousov 	bus_delete_resource(dev, SYS_RES_IRQ, dmd->irq_rid);
6255967352aSKonstantin Belousov err2:
6265967352aSKonstantin Belousov 	PCIB_RELEASE_MSIX(pcib, dev, dmd->irq);
6275967352aSKonstantin Belousov 	dmd->irq = -1;
6285967352aSKonstantin Belousov err1:
6295967352aSKonstantin Belousov 	return (error);
6305967352aSKonstantin Belousov }
6315967352aSKonstantin Belousov 
6325967352aSKonstantin Belousov void
6335967352aSKonstantin Belousov iommu_release_intr(struct iommu_unit *unit, int idx)
6345967352aSKonstantin Belousov {
6355967352aSKonstantin Belousov 	device_t dev;
6365967352aSKonstantin Belousov 	struct iommu_msi_data *dmd;
6375967352aSKonstantin Belousov 
6385967352aSKonstantin Belousov 	MPASS(idx >= 0 || idx < IOMMU_MAX_MSI);
6395967352aSKonstantin Belousov 
6405967352aSKonstantin Belousov 	dmd = &IOMMU2X86C(unit)->intrs[idx];
6415967352aSKonstantin Belousov 	if (dmd->handler == NULL || dmd->irq == -1)
6425967352aSKonstantin Belousov 		return;
6435967352aSKonstantin Belousov 	dev = unit->dev;
6445967352aSKonstantin Belousov 
6455967352aSKonstantin Belousov 	bus_teardown_intr(dev, dmd->irq_res, dmd->intr_handle);
6465967352aSKonstantin Belousov 	bus_release_resource(dev, SYS_RES_IRQ, dmd->irq_rid, dmd->irq_res);
6475967352aSKonstantin Belousov 	bus_delete_resource(dev, SYS_RES_IRQ, dmd->irq_rid);
6485967352aSKonstantin Belousov 	PCIB_RELEASE_MSIX(device_get_parent(device_get_parent(dev)),
6495967352aSKonstantin Belousov 	    dev, dmd->irq);
6505967352aSKonstantin Belousov 	dmd->irq = -1;
6515967352aSKonstantin Belousov }
652ff54674bSKonstantin Belousov 
653ff54674bSKonstantin Belousov void
654ff54674bSKonstantin Belousov iommu_device_tag_init(struct iommu_ctx *ctx, device_t dev)
655ff54674bSKonstantin Belousov {
656ff54674bSKonstantin Belousov 	bus_addr_t maxaddr;
657ff54674bSKonstantin Belousov 
658ff54674bSKonstantin Belousov 	maxaddr = MIN(ctx->domain->end, BUS_SPACE_MAXADDR);
659ff54674bSKonstantin Belousov 	ctx->tag->common.impl = &bus_dma_iommu_impl;
660ff54674bSKonstantin Belousov 	ctx->tag->common.boundary = 0;
661ff54674bSKonstantin Belousov 	ctx->tag->common.lowaddr = maxaddr;
662ff54674bSKonstantin Belousov 	ctx->tag->common.highaddr = maxaddr;
663ff54674bSKonstantin Belousov 	ctx->tag->common.maxsize = maxaddr;
664ff54674bSKonstantin Belousov 	ctx->tag->common.nsegments = BUS_SPACE_UNRESTRICTED;
665ff54674bSKonstantin Belousov 	ctx->tag->common.maxsegsz = maxaddr;
666ff54674bSKonstantin Belousov 	ctx->tag->ctx = ctx;
667ff54674bSKonstantin Belousov 	ctx->tag->owner = dev;
668ff54674bSKonstantin Belousov }
669b3042e3aSKonstantin Belousov 
670b3042e3aSKonstantin Belousov void
671b3042e3aSKonstantin Belousov iommu_domain_free_entry(struct iommu_map_entry *entry, bool free)
672b3042e3aSKonstantin Belousov {
673b3042e3aSKonstantin Belousov 	if ((entry->flags & IOMMU_MAP_ENTRY_RMRR) != 0)
674b3042e3aSKonstantin Belousov 		iommu_gas_free_region(entry);
675b3042e3aSKonstantin Belousov 	else
676b3042e3aSKonstantin Belousov 		iommu_gas_free_space(entry);
677b3042e3aSKonstantin Belousov 	if (free)
678b3042e3aSKonstantin Belousov 		iommu_gas_free_entry(entry);
679b3042e3aSKonstantin Belousov 	else
680b3042e3aSKonstantin Belousov 		entry->flags = 0;
681b3042e3aSKonstantin Belousov }
682b3042e3aSKonstantin Belousov 
68329e22704SKonstantin Belousov /*
68429e22704SKonstantin Belousov  * Index of the pte for the guest address base in the page table at
68529e22704SKonstantin Belousov  * the level lvl.
68629e22704SKonstantin Belousov  */
68729e22704SKonstantin Belousov int
68829e22704SKonstantin Belousov pglvl_pgtbl_pte_off(int pglvl, iommu_gaddr_t base, int lvl)
68929e22704SKonstantin Belousov {
69029e22704SKonstantin Belousov 
69129e22704SKonstantin Belousov 	base >>= IOMMU_PAGE_SHIFT + (pglvl - lvl - 1) *
69229e22704SKonstantin Belousov 	    IOMMU_NPTEPGSHIFT;
69329e22704SKonstantin Belousov 	return (base & IOMMU_PTEMASK);
69429e22704SKonstantin Belousov }
69529e22704SKonstantin Belousov 
69629e22704SKonstantin Belousov /*
69729e22704SKonstantin Belousov  * Returns the page index of the page table page in the page table
69829e22704SKonstantin Belousov  * object, which maps the given address base at the page table level
69929e22704SKonstantin Belousov  * lvl.
70029e22704SKonstantin Belousov  */
70129e22704SKonstantin Belousov vm_pindex_t
70229e22704SKonstantin Belousov pglvl_pgtbl_get_pindex(int pglvl, iommu_gaddr_t base, int lvl)
70329e22704SKonstantin Belousov {
70429e22704SKonstantin Belousov 	vm_pindex_t idx, pidx;
70529e22704SKonstantin Belousov 	int i;
70629e22704SKonstantin Belousov 
70729e22704SKonstantin Belousov 	KASSERT(lvl >= 0 && lvl < pglvl,
70829e22704SKonstantin Belousov 	    ("wrong lvl %d %d", pglvl, lvl));
70929e22704SKonstantin Belousov 
71029e22704SKonstantin Belousov 	for (pidx = idx = 0, i = 0; i < lvl; i++, pidx = idx) {
71129e22704SKonstantin Belousov 		idx = pglvl_pgtbl_pte_off(pglvl, base, i) +
71229e22704SKonstantin Belousov 		    pidx * IOMMU_NPTEPG + 1;
71329e22704SKonstantin Belousov 	}
71429e22704SKonstantin Belousov 	return (idx);
71529e22704SKonstantin Belousov }
71629e22704SKonstantin Belousov 
71729e22704SKonstantin Belousov /*
71829e22704SKonstantin Belousov  * Calculate the total amount of page table pages needed to map the
71929e22704SKonstantin Belousov  * whole bus address space on the context with the selected agaw.
72029e22704SKonstantin Belousov  */
72129e22704SKonstantin Belousov vm_pindex_t
72229e22704SKonstantin Belousov pglvl_max_pages(int pglvl)
72329e22704SKonstantin Belousov {
72429e22704SKonstantin Belousov 	vm_pindex_t res;
72529e22704SKonstantin Belousov 	int i;
72629e22704SKonstantin Belousov 
72729e22704SKonstantin Belousov 	for (res = 0, i = pglvl; i > 0; i--) {
72829e22704SKonstantin Belousov 		res *= IOMMU_NPTEPG;
72929e22704SKonstantin Belousov 		res++;
73029e22704SKonstantin Belousov 	}
73129e22704SKonstantin Belousov 	return (res);
73229e22704SKonstantin Belousov }
73329e22704SKonstantin Belousov 
73429e22704SKonstantin Belousov iommu_gaddr_t
73529e22704SKonstantin Belousov pglvl_page_size(int total_pglvl, int lvl)
73629e22704SKonstantin Belousov {
73729e22704SKonstantin Belousov 	int rlvl;
73829e22704SKonstantin Belousov 	static const iommu_gaddr_t pg_sz[] = {
73929e22704SKonstantin Belousov 		(iommu_gaddr_t)IOMMU_PAGE_SIZE,
74029e22704SKonstantin Belousov 		(iommu_gaddr_t)IOMMU_PAGE_SIZE << IOMMU_NPTEPGSHIFT,
74129e22704SKonstantin Belousov 		(iommu_gaddr_t)IOMMU_PAGE_SIZE << (2 * IOMMU_NPTEPGSHIFT),
74229e22704SKonstantin Belousov 		(iommu_gaddr_t)IOMMU_PAGE_SIZE << (3 * IOMMU_NPTEPGSHIFT),
74329e22704SKonstantin Belousov 		(iommu_gaddr_t)IOMMU_PAGE_SIZE << (4 * IOMMU_NPTEPGSHIFT),
74429e22704SKonstantin Belousov 		(iommu_gaddr_t)IOMMU_PAGE_SIZE << (5 * IOMMU_NPTEPGSHIFT),
74529e22704SKonstantin Belousov 		(iommu_gaddr_t)IOMMU_PAGE_SIZE << (6 * IOMMU_NPTEPGSHIFT),
74629e22704SKonstantin Belousov 	};
74729e22704SKonstantin Belousov 
74829e22704SKonstantin Belousov 	KASSERT(lvl >= 0 && lvl < total_pglvl,
74929e22704SKonstantin Belousov 	    ("total %d lvl %d", total_pglvl, lvl));
75029e22704SKonstantin Belousov 	rlvl = total_pglvl - lvl - 1;
75129e22704SKonstantin Belousov 	KASSERT(rlvl < nitems(pg_sz), ("sizeof pg_sz lvl %d", lvl));
75229e22704SKonstantin Belousov 	return (pg_sz[rlvl]);
75329e22704SKonstantin Belousov }
754e9d948cfSKonstantin Belousov 
755*b08d332dSKonstantin Belousov void
756*b08d332dSKonstantin Belousov iommu_device_set_iommu_prop(device_t dev, device_t iommu)
757*b08d332dSKonstantin Belousov {
758*b08d332dSKonstantin Belousov 	device_t iommu_dev;
759*b08d332dSKonstantin Belousov 	int error;
760*b08d332dSKonstantin Belousov 
761*b08d332dSKonstantin Belousov 	bus_topo_lock();
762*b08d332dSKonstantin Belousov 	error = device_get_prop(dev, DEV_PROP_NAME_IOMMU, (void **)&iommu_dev);
763*b08d332dSKonstantin Belousov 	if (error == ENOENT)
764*b08d332dSKonstantin Belousov 		device_set_prop(dev, DEV_PROP_NAME_IOMMU, iommu, NULL, NULL);
765*b08d332dSKonstantin Belousov 	bus_topo_unlock();
766*b08d332dSKonstantin Belousov }
767*b08d332dSKonstantin Belousov 
768e9d948cfSKonstantin Belousov #ifdef DDB
769e9d948cfSKonstantin Belousov #include <ddb/ddb.h>
770e9d948cfSKonstantin Belousov #include <ddb/db_lex.h>
771e9d948cfSKonstantin Belousov 
772e9d948cfSKonstantin Belousov void
773e9d948cfSKonstantin Belousov iommu_db_print_domain_entry(const struct iommu_map_entry *entry)
774e9d948cfSKonstantin Belousov {
775e9d948cfSKonstantin Belousov 	struct iommu_map_entry *l, *r;
776e9d948cfSKonstantin Belousov 
777e9d948cfSKonstantin Belousov 	db_printf(
778e9d948cfSKonstantin Belousov 	    "    start %jx end %jx first %jx last %jx free_down %jx flags %x ",
779e9d948cfSKonstantin Belousov 	    entry->start, entry->end, entry->first, entry->last,
780e9d948cfSKonstantin Belousov 	    entry->free_down, entry->flags);
781e9d948cfSKonstantin Belousov 	db_printf("left ");
782e9d948cfSKonstantin Belousov 	l = RB_LEFT(entry, rb_entry);
783e9d948cfSKonstantin Belousov 	if (l == NULL)
784e9d948cfSKonstantin Belousov 		db_printf("NULL ");
785e9d948cfSKonstantin Belousov 	else
786e9d948cfSKonstantin Belousov 		db_printf("%jx ", l->start);
787e9d948cfSKonstantin Belousov 	db_printf("right ");
788e9d948cfSKonstantin Belousov 	r = RB_RIGHT(entry, rb_entry);
789e9d948cfSKonstantin Belousov 	if (r == NULL)
790e9d948cfSKonstantin Belousov 		db_printf("NULL");
791e9d948cfSKonstantin Belousov 	else
792e9d948cfSKonstantin Belousov 		db_printf("%jx", r->start);
793e9d948cfSKonstantin Belousov 	db_printf("\n");
794e9d948cfSKonstantin Belousov }
795e9d948cfSKonstantin Belousov 
796e9d948cfSKonstantin Belousov void
797e9d948cfSKonstantin Belousov iommu_db_print_ctx(struct iommu_ctx *ctx)
798e9d948cfSKonstantin Belousov {
799e9d948cfSKonstantin Belousov 	db_printf(
800e9d948cfSKonstantin Belousov 	    "    @%p pci%d:%d:%d refs %d flags %#x loads %lu unloads %lu\n",
801e9d948cfSKonstantin Belousov 	    ctx, pci_get_bus(ctx->tag->owner),
802e9d948cfSKonstantin Belousov 	    pci_get_slot(ctx->tag->owner),
803e9d948cfSKonstantin Belousov 	    pci_get_function(ctx->tag->owner), ctx->refs,
804e9d948cfSKonstantin Belousov 	    ctx->flags, ctx->loads, ctx->unloads);
805e9d948cfSKonstantin Belousov }
806c9e22c74SKonstantin Belousov 
807c9e22c74SKonstantin Belousov void
808c9e22c74SKonstantin Belousov iommu_db_domain_print_contexts(struct iommu_domain *iodom)
809c9e22c74SKonstantin Belousov {
810c9e22c74SKonstantin Belousov 	struct iommu_ctx *ctx;
811c9e22c74SKonstantin Belousov 
812c9e22c74SKonstantin Belousov 	if (LIST_EMPTY(&iodom->contexts))
813c9e22c74SKonstantin Belousov 		return;
814c9e22c74SKonstantin Belousov 
815c9e22c74SKonstantin Belousov 	db_printf("  Contexts:\n");
816c9e22c74SKonstantin Belousov 	LIST_FOREACH(ctx, &iodom->contexts, link)
817c9e22c74SKonstantin Belousov 		iommu_db_print_ctx(ctx);
818c9e22c74SKonstantin Belousov }
819c9e22c74SKonstantin Belousov 
820c9e22c74SKonstantin Belousov void
821c9e22c74SKonstantin Belousov iommu_db_domain_print_mappings(struct iommu_domain *iodom)
822c9e22c74SKonstantin Belousov {
823c9e22c74SKonstantin Belousov 	struct iommu_map_entry *entry;
824c9e22c74SKonstantin Belousov 
825c9e22c74SKonstantin Belousov 	db_printf("    mapped:\n");
826c9e22c74SKonstantin Belousov 	RB_FOREACH(entry, iommu_gas_entries_tree, &iodom->rb_root) {
827c9e22c74SKonstantin Belousov 		iommu_db_print_domain_entry(entry);
828c9e22c74SKonstantin Belousov 		if (db_pager_quit)
829c9e22c74SKonstantin Belousov 			break;
830c9e22c74SKonstantin Belousov 	}
831c9e22c74SKonstantin Belousov 	if (db_pager_quit)
832c9e22c74SKonstantin Belousov 		return;
833c9e22c74SKonstantin Belousov 	db_printf("    unloading:\n");
834c9e22c74SKonstantin Belousov 	TAILQ_FOREACH(entry, &iodom->unload_entries, dmamap_link) {
835c9e22c74SKonstantin Belousov 		iommu_db_print_domain_entry(entry);
836c9e22c74SKonstantin Belousov 		if (db_pager_quit)
837c9e22c74SKonstantin Belousov 			break;
838c9e22c74SKonstantin Belousov 	}
839c9e22c74SKonstantin Belousov }
840c9e22c74SKonstantin Belousov 
841e9d948cfSKonstantin Belousov #endif
842