diff --git a/tools/libxl/libxl_create.c b/tools/libxl/libxl_create.c
index 03ce166f4f..feb9f1ce0c 100644
--- a/tools/libxl/libxl_create.c
+++ b/tools/libxl/libxl_create.c
@@ -555,6 +555,7 @@ int libxl__domain_make(libxl__gc *gc, libxl_domain_config *d_config,
             .max_grant_frames = b_info->max_grant_frames,
             .max_maptrack_frames = b_info->max_maptrack_frames,
         };
+        libxl_physinfo physinfo;
 
         if (info->type != LIBXL_DOMAIN_TYPE_PV) {
             create.flags |= XEN_DOMCTL_CDF_hvm_guest;
@@ -564,6 +565,13 @@ int libxl__domain_make(libxl__gc *gc, libxl_domain_config *d_config,
                 libxl_defbool_val(info->oos) ? 0 : XEN_DOMCTL_CDF_oos_off;
         }
 
+        rc = libxl_get_physinfo(ctx, &physinfo);
+        if (rc < 0)
+            goto out;
+
+        if ( physinfo.cap_hvm_directio )
+            create.flags |= XEN_DOMCTL_CDF_iommu;
+
         /* Ultimately, handle is an array of 16 uint8_t, same as uuid */
         libxl_uuid_copy(ctx, (libxl_uuid *)&create.handle, &info->uuid);
 
diff --git a/xen/arch/arm/domain.c b/xen/arch/arm/domain.c
index 941bbff4fe..e06bd27dad 100644
--- a/xen/arch/arm/domain.c
+++ b/xen/arch/arm/domain.c
@@ -673,8 +673,7 @@ int arch_domain_create(struct domain *d,
 
     ASSERT(config != NULL);
 
-    /* p2m_init relies on some value initialized by the IOMMU subsystem */
-    if ( (rc = iommu_domain_init(d)) != 0 )
+    if ( is_iommu_enabled(d) && (rc = iommu_domain_init(d)) != 0 )
         goto fail;
 
     if ( (rc = p2m_init(d)) != 0 )
diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c
index 215746a5c3..fca1e62901 100644
--- a/xen/arch/arm/setup.c
+++ b/xen/arch/arm/setup.c
@@ -914,6 +914,9 @@ void __init start_xen(unsigned long boot_phys_offset,
     dom0_cfg.arch.tee_type = tee_get_type();
     dom0_cfg.max_vcpus = dom0_max_vcpus();
 
+    if ( iommu_enabled )
+        dom0_cfg.flags |= XEN_DOMCTL_CDF_iommu;
+
     dom0 = domain_create(0, &dom0_cfg, true);
     if ( IS_ERR(dom0) || (alloc_dom0_vcpu0(dom0) == NULL) )
         panic("Error creating domain 0\n");
diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c
index fbc70b9f94..42778099da 100644
--- a/xen/arch/x86/domain.c
+++ b/xen/arch/x86/domain.c
@@ -604,7 +604,7 @@ int arch_domain_create(struct domain *d,
     if ( (rc = init_domain_irq_mapping(d)) != 0 )
         goto fail;
 
-    if ( (rc = iommu_domain_init(d)) != 0 )
+    if ( is_iommu_enabled(d) && (rc = iommu_domain_init(d)) != 0 )
         goto fail;
 
     psr_domain_init(d);
diff --git a/xen/arch/x86/setup.c b/xen/arch/x86/setup.c
index 277170f386..e048f70eef 100644
--- a/xen/arch/x86/setup.c
+++ b/xen/arch/x86/setup.c
@@ -1726,6 +1726,9 @@ void __init noreturn __start_xen(unsigned long mbi_p)
     }
     dom0_cfg.max_vcpus = dom0_max_vcpus();
 
+    if ( iommu_enabled )
+        dom0_cfg.flags |= XEN_DOMCTL_CDF_iommu;
+
     /* Create initial domain 0. */
     dom0 = domain_create(get_initial_domain_id(), &dom0_cfg, !pv_shim);
     if ( IS_ERR(dom0) || (alloc_dom0_vcpu0(dom0) == NULL) )
diff --git a/xen/common/domain.c b/xen/common/domain.c
index 9b70626753..0df4b47352 100644
--- a/xen/common/domain.c
+++ b/xen/common/domain.c
@@ -301,7 +301,8 @@ static int sanitise_domain_config(struct xen_domctl_createdomain *config)
                            XEN_DOMCTL_CDF_hap |
                            XEN_DOMCTL_CDF_s3_integrity |
                            XEN_DOMCTL_CDF_oos_off |
-                           XEN_DOMCTL_CDF_xs_domain) )
+                           XEN_DOMCTL_CDF_xs_domain |
+                           XEN_DOMCTL_CDF_iommu) )
     {
         dprintk(XENLOG_INFO, "Unknown CDF flags %#x\n", config->flags);
         return -EINVAL;
diff --git a/xen/drivers/passthrough/device_tree.c b/xen/drivers/passthrough/device_tree.c
index b6eaae7283..d32b172664 100644
--- a/xen/drivers/passthrough/device_tree.c
+++ b/xen/drivers/passthrough/device_tree.c
@@ -119,6 +119,9 @@ int iommu_release_dt_devices(struct domain *d)
     struct dt_device_node *dev, *_dev;
     int rc;
 
+    if ( !is_iommu_enabled(d) )
+        return 0;
+
     list_for_each_entry_safe(dev, _dev, &hd->dt_devices, domain_list)
     {
         rc = iommu_deassign_dt_device(d, dev);
diff --git a/xen/drivers/passthrough/iommu.c b/xen/drivers/passthrough/iommu.c
index 37eb0f7d01..0a00279067 100644
--- a/xen/drivers/passthrough/iommu.c
+++ b/xen/drivers/passthrough/iommu.c
@@ -151,13 +151,13 @@ int iommu_domain_init(struct domain *d)
     struct domain_iommu *hd = dom_iommu(d);
     int ret = 0;
 
+    if ( !iommu_enabled )
+        return -EOPNOTSUPP;
+
     ret = arch_iommu_domain_init(d);
     if ( ret )
         return ret;
 
-    if ( !iommu_enabled )
-        return 0;
-
     hd->platform_ops = iommu_get_ops();
     return hd->platform_ops->init(d);
 }
diff --git a/xen/include/public/domctl.h b/xen/include/public/domctl.h
index 19486d5e32..3f82c78870 100644
--- a/xen/include/public/domctl.h
+++ b/xen/include/public/domctl.h
@@ -64,6 +64,10 @@ struct xen_domctl_createdomain {
  /* Is this a xenstore domain? */
 #define _XEN_DOMCTL_CDF_xs_domain     4
 #define XEN_DOMCTL_CDF_xs_domain      (1U<<_XEN_DOMCTL_CDF_xs_domain)
+ /* Should this domain be permitted to use the IOMMU? */
+#define _XEN_DOMCTL_CDF_iommu         5
+#define XEN_DOMCTL_CDF_iommu          (1U<<_XEN_DOMCTL_CDF_iommu)
+
     uint32_t flags;
 
     /*
diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h
index a62161cc54..bad9734626 100644
--- a/xen/include/xen/sched.h
+++ b/xen/include/xen/sched.h
@@ -981,6 +981,11 @@ static inline bool is_xenstore_domain(const struct domain *d)
     return d->options & XEN_DOMCTL_CDF_xs_domain;
 }
 
+static inline bool is_iommu_enabled(const struct domain *d)
+{
+    return d->options & XEN_DOMCTL_CDF_iommu;
+}
+
 extern bool sched_smt_power_savings;
 
 extern enum cpufreq_controller {
