summaryrefslogtreecommitdiffstats
path: root/sys/amd64/vmm/io/iommu.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/amd64/vmm/io/iommu.c')
-rw-r--r--sys/amd64/vmm/io/iommu.c66
1 files changed, 55 insertions, 11 deletions
diff --git a/sys/amd64/vmm/io/iommu.c b/sys/amd64/vmm/io/iommu.c
index 9cfc4c2..75cf1ec 100644
--- a/sys/amd64/vmm/io/iommu.c
+++ b/sys/amd64/vmm/io/iommu.c
@@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$");
#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h>
+#include <machine/cpu.h>
#include <machine/md_var.h>
#include "vmm_util.h"
@@ -51,8 +52,13 @@ static int iommu_avail;
SYSCTL_INT(_hw_vmm_iommu, OID_AUTO, initialized, CTLFLAG_RD, &iommu_avail,
0, "bhyve iommu initialized?");
+static int iommu_enable = 1;
+SYSCTL_INT(_hw_vmm_iommu, OID_AUTO, enable, CTLFLAG_RDTUN, &iommu_enable, 0,
+ "Enable use of I/O MMU (required for PCI passthrough).");
+
static struct iommu_ops *ops;
static void *host_domain;
+static eventhandler_tag add_tag, delete_tag;
static __inline int
IOMMU_INIT(void)
@@ -148,14 +154,31 @@ IOMMU_DISABLE(void)
(*ops->disable)();
}
-void
+static void
+iommu_pci_add(void *arg, device_t dev)
+{
+
+ /* Add new devices to the host domain. */
+ iommu_add_device(host_domain, pci_get_rid(dev));
+}
+
+static void
+iommu_pci_delete(void *arg, device_t dev)
+{
+
+ iommu_remove_device(host_domain, pci_get_rid(dev));
+}
+
+static void
iommu_init(void)
{
int error, bus, slot, func;
vm_paddr_t maxaddr;
- const char *name;
device_t dev;
+ if (!iommu_enable)
+ return;
+
if (vmm_is_intel())
ops = &iommu_ops_intel;
else if (vmm_is_amd())
@@ -174,8 +197,13 @@ iommu_init(void)
*/
maxaddr = vmm_mem_maxaddr();
host_domain = IOMMU_CREATE_DOMAIN(maxaddr);
- if (host_domain == NULL)
- panic("iommu_init: unable to create a host domain");
+ if (host_domain == NULL) {
+ printf("iommu_init: unable to create a host domain");
+ IOMMU_CLEANUP();
+ ops = NULL;
+ iommu_avail = 0;
+ return;
+ }
/*
* Create 1:1 mappings from '0' to 'maxaddr' for devices assigned to
@@ -183,6 +211,9 @@ iommu_init(void)
*/
iommu_create_mapping(host_domain, 0, 0, maxaddr);
+ add_tag = EVENTHANDLER_REGISTER(pci_add_device, iommu_pci_add, NULL, 0);
+ delete_tag = EVENTHANDLER_REGISTER(pci_delete_device, iommu_pci_delete,
+ NULL, 0);
for (bus = 0; bus <= PCI_BUSMAX; bus++) {
for (slot = 0; slot <= PCI_SLOTMAX; slot++) {
for (func = 0; func <= PCI_FUNCMAX; func++) {
@@ -190,12 +221,7 @@ iommu_init(void)
if (dev == NULL)
continue;
- /* skip passthrough devices */
- name = device_get_name(dev);
- if (name != NULL && strcmp(name, "ppt") == 0)
- continue;
-
- /* everything else belongs to the host domain */
+ /* Everything belongs to the host domain. */
iommu_add_device(host_domain,
pci_get_rid(dev));
}
@@ -208,6 +234,15 @@ iommu_init(void)
void
iommu_cleanup(void)
{
+
+ if (add_tag != NULL) {
+ EVENTHANDLER_DEREGISTER(pci_add_device, add_tag);
+ add_tag = NULL;
+ }
+ if (delete_tag != NULL) {
+ EVENTHANDLER_DEREGISTER(pci_delete_device, delete_tag);
+ delete_tag = NULL;
+ }
IOMMU_DISABLE();
IOMMU_DESTROY_DOMAIN(host_domain);
IOMMU_CLEANUP();
@@ -216,7 +251,16 @@ iommu_cleanup(void)
void *
iommu_create_domain(vm_paddr_t maxaddr)
{
-
+ static volatile int iommu_initted;
+
+ if (iommu_initted < 2) {
+ if (atomic_cmpset_int(&iommu_initted, 0, 1)) {
+ iommu_init();
+ atomic_store_rel_int(&iommu_initted, 2);
+ } else
+ while (iommu_initted == 1)
+ cpu_spinwait();
+ }
return (IOMMU_CREATE_DOMAIN(maxaddr));
}
OpenPOWER on IntegriCloud