diff options
Diffstat (limited to 'sys/dev/iommu/iommu_gas.c')
-rw-r--r-- | sys/dev/iommu/iommu_gas.c | 64 |
1 files changed, 64 insertions, 0 deletions
diff --git a/sys/dev/iommu/iommu_gas.c b/sys/dev/iommu/iommu_gas.c index fc5140035567..7ef1ceee6768 100644 --- a/sys/dev/iommu/iommu_gas.c +++ b/sys/dev/iommu/iommu_gas.c @@ -63,6 +63,7 @@ __FBSDID("$FreeBSD$"); #include <dev/pci/pcivar.h> #include <dev/iommu/iommu.h> #include <dev/iommu/iommu_gas.h> +#include <dev/iommu/iommu_msi.h> #include <machine/atomic.h> #include <machine/bus.h> #include <machine/md_var.h> @@ -725,6 +726,69 @@ iommu_map(struct iommu_domain *domain, } int +iommu_map_msi(struct iommu_ctx *ctx, iommu_gaddr_t size, int offset, + u_int eflags, u_int flags, vm_page_t *ma) +{ + struct iommu_domain *domain; + struct iommu_map_entry *entry; + int error; + + error = 0; + domain = ctx->domain; + + /* Check if there is already an MSI page allocated */ + IOMMU_DOMAIN_LOCK(domain); + entry = domain->msi_entry; + IOMMU_DOMAIN_UNLOCK(domain); + + if (entry == NULL) { + error = iommu_gas_map(domain, &ctx->tag->common, size, offset, + eflags, flags, ma, &entry); + IOMMU_DOMAIN_LOCK(domain); + if (error == 0) { + if (domain->msi_entry == NULL) { + MPASS(domain->msi_base == 0); + MPASS(domain->msi_phys == 0); + + domain->msi_entry = entry; + domain->msi_base = entry->start; + domain->msi_phys = VM_PAGE_TO_PHYS(ma[0]); + } else { + /* + * We lost the race and already have an + * MSI page allocated. Free the unneeded entry. + */ + iommu_gas_free_entry(domain, entry); + } + } else if (domain->msi_entry != NULL) { + /* + * The allocation failed, but another succeeded. + * Return success as there is a valid MSI page. + */ + error = 0; + } + IOMMU_DOMAIN_UNLOCK(domain); + } + + return (error); +} + +void +iommu_translate_msi(struct iommu_domain *domain, uint64_t *addr) +{ + + *addr = (*addr - domain->msi_phys) + domain->msi_base; + + KASSERT(*addr >= domain->msi_entry->start, + ("%s: Address is below the MSI entry start address (%jx < %jx)", + __func__, (uintmax_t)*addr, (uintmax_t)domain->msi_entry->start)); + + KASSERT(*addr + sizeof(*addr) <= domain->msi_entry->end, + ("%s: Address is above the MSI entry end address (%jx < %jx)", + __func__, (uintmax_t)*addr, (uintmax_t)domain->msi_entry->end)); +} + +int iommu_map_region(struct iommu_domain *domain, struct iommu_map_entry *entry, u_int eflags, u_int flags, vm_page_t *ma) { |