summaryrefslogtreecommitdiff
path: root/sys/dev/iommu/iommu_gas.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/iommu/iommu_gas.c')
-rw-r--r--sys/dev/iommu/iommu_gas.c64
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)
{