6ae9ed
From aa8079012cc5655cc0e2c55532f8e6b55f1d0a51 Mon Sep 17 00:00:00 2001
6ae9ed
Message-Id: <aa8079012cc5655cc0e2c55532f8e6b55f1d0a51@dist-git>
6ae9ed
From: =?UTF-8?q?J=C3=A1n=20Tomko?= <jtomko@redhat.com>
6ae9ed
Date: Mon, 25 Jul 2016 10:24:56 +0200
6ae9ed
Subject: [PATCH] Introduce <iommu> device
6ae9ed
MIME-Version: 1.0
6ae9ed
Content-Type: text/plain; charset=UTF-8
6ae9ed
Content-Transfer-Encoding: 8bit
6ae9ed
6ae9ed
A device with an attribute 'model', with just one model
6ae9ed
so far:
6ae9ed
6ae9ed
<devices>
6ae9ed
  ...
6ae9ed
  <iommu model='intel'/>
6ae9ed
</devices>
6ae9ed
6ae9ed
https://bugzilla.redhat.com/show_bug.cgi?id=1235581
6ae9ed
(cherry picked from commit ea0ed35d6efcba9e79c76d7b57959f553b76224a)
6ae9ed
Signed-off-by: Ján Tomko <jtomko@redhat.com>
6ae9ed
---
6ae9ed
 docs/formatdomain.html.in                          | 26 +++++++
6ae9ed
 docs/schemas/domaincommon.rng                      | 11 +++
6ae9ed
 src/conf/domain_conf.c                             | 90 +++++++++++++++++++++-
6ae9ed
 src/conf/domain_conf.h                             | 16 ++++
6ae9ed
 src/libvirt_private.syms                           |  2 +
6ae9ed
 src/qemu/qemu_driver.c                             |  6 ++
6ae9ed
 src/qemu/qemu_hotplug.c                            |  1 +
6ae9ed
 .../qemuxml2argvdata/qemuxml2argv-intel-iommu.xml  | 37 +++++++++
6ae9ed
 .../qemuxml2xmlout-intel-iommu.xml                 | 37 +++++++++
6ae9ed
 tests/qemuxml2xmltest.c                            |  4 +
6ae9ed
 10 files changed, 229 insertions(+), 1 deletion(-)
6ae9ed
 create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-intel-iommu.xml
6ae9ed
 create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-intel-iommu.xml
6ae9ed
6ae9ed
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
6ae9ed
index b0b2f82..d206f6a 100644
6ae9ed
--- a/docs/formatdomain.html.in
6ae9ed
+++ b/docs/formatdomain.html.in
6ae9ed
@@ -6716,6 +6716,32 @@ qemu-kvm -net nic,model=? /dev/null
6ae9ed
       
6ae9ed
     
6ae9ed
 
6ae9ed
+    

IOMMU devices

6ae9ed
+
6ae9ed
+    

6ae9ed
+      The iommu element can be used to add an IOMMU device.
6ae9ed
+      Since 2.1.0
6ae9ed
+    

6ae9ed
+
6ae9ed
+    

6ae9ed
+      Example:
6ae9ed
+    

6ae9ed
+
6ae9ed
+  ...
6ae9ed
+  <devices>
6ae9ed
+    <iommu model='intel'/>
6ae9ed
+  </devices>
6ae9ed
+  ...
6ae9ed
+
6ae9ed
+    
6ae9ed
+      
model
6ae9ed
+      
6ae9ed
+        

6ae9ed
+          Currently only the intel model is supported.
6ae9ed
+        

6ae9ed
+      
6ae9ed
+    
6ae9ed
+
6ae9ed
     

Security label

6ae9ed
 
6ae9ed
     

6ae9ed
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
6ae9ed
index 0876daa..f17104b 100644
6ae9ed
--- a/docs/schemas/domaincommon.rng
6ae9ed
+++ b/docs/schemas/domaincommon.rng
6ae9ed
@@ -3713,6 +3713,14 @@
6ae9ed
    </optional>
6ae9ed
   </define>
6ae9ed
 
6ae9ed
+  <define name="iommu">
6ae9ed
+    <element name="iommu">
6ae9ed
+      <attribute name="model">
6ae9ed
+        <value>intel</value>
6ae9ed
+      </attribute>
6ae9ed
+    </element>
6ae9ed
+  </define>
6ae9ed
+
6ae9ed
   <define name="input">
6ae9ed
     <element name="input">
6ae9ed
       <choice>
6ae9ed
@@ -4184,6 +4192,9 @@
6ae9ed
         <zeroOrMore>
6ae9ed
           <ref name="panic"/>
6ae9ed
         </zeroOrMore>
6ae9ed
+        <optional>
6ae9ed
+          <ref name="iommu"/>
6ae9ed
+        </optional>
6ae9ed
       </interleave>
6ae9ed
     </element>
6ae9ed
   </define>
6ae9ed
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
6ae9ed
index bc01633..ddeca0d 100644
6ae9ed
--- a/src/conf/domain_conf.c
6ae9ed
+++ b/src/conf/domain_conf.c
6ae9ed
@@ -241,7 +241,8 @@ VIR_ENUM_IMPL(virDomainDevice, VIR_DOMAIN_DEVICE_LAST,
6ae9ed
               "shmem",
6ae9ed
               "tpm",
6ae9ed
               "panic",
6ae9ed
-              "memory")
6ae9ed
+              "memory",
6ae9ed
+              "iommu")
6ae9ed
 
6ae9ed
 VIR_ENUM_IMPL(virDomainDeviceAddress, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_LAST,
6ae9ed
               "none",
6ae9ed
@@ -804,6 +805,9 @@ VIR_ENUM_IMPL(virDomainTPMModel, VIR_DOMAIN_TPM_MODEL_LAST,
6ae9ed
 VIR_ENUM_IMPL(virDomainTPMBackend, VIR_DOMAIN_TPM_TYPE_LAST,
6ae9ed
               "passthrough")
6ae9ed
 
6ae9ed
+VIR_ENUM_IMPL(virDomainIOMMUModel, VIR_DOMAIN_IOMMU_MODEL_LAST,
6ae9ed
+              "intel")
6ae9ed
+
6ae9ed
 VIR_ENUM_IMPL(virDomainDiskDiscard, VIR_DOMAIN_DISK_DISCARD_LAST,
6ae9ed
               "default",
6ae9ed
               "unmap",
6ae9ed
@@ -2364,6 +2368,9 @@ void virDomainDeviceDefFree(virDomainDeviceDefPtr def)
6ae9ed
     case VIR_DOMAIN_DEVICE_MEMORY:
6ae9ed
         virDomainMemoryDefFree(def->data.memory);
6ae9ed
         break;
6ae9ed
+    case VIR_DOMAIN_DEVICE_IOMMU:
6ae9ed
+        VIR_FREE(def->data.iommu);
6ae9ed
+        break;
6ae9ed
     case VIR_DOMAIN_DEVICE_LAST:
6ae9ed
     case VIR_DOMAIN_DEVICE_NONE:
6ae9ed
         break;
6ae9ed
@@ -2610,6 +2617,8 @@ void virDomainDefFree(virDomainDefPtr def)
6ae9ed
         virDomainPanicDefFree(def->panics[i]);
6ae9ed
     VIR_FREE(def->panics);
6ae9ed
 
6ae9ed
+    VIR_FREE(def->iommu);
6ae9ed
+
6ae9ed
     VIR_FREE(def->idmap.uidmap);
6ae9ed
     VIR_FREE(def->idmap.gidmap);
6ae9ed
 
6ae9ed
@@ -3179,6 +3188,7 @@ virDomainDeviceGetInfo(virDomainDeviceDefPtr device)
6ae9ed
     /* The following devices do not contain virDomainDeviceInfo */
6ae9ed
     case VIR_DOMAIN_DEVICE_LEASE:
6ae9ed
     case VIR_DOMAIN_DEVICE_GRAPHICS:
6ae9ed
+    case VIR_DOMAIN_DEVICE_IOMMU:
6ae9ed
     case VIR_DOMAIN_DEVICE_LAST:
6ae9ed
     case VIR_DOMAIN_DEVICE_NONE:
6ae9ed
         break;
6ae9ed
@@ -3540,6 +3550,7 @@ virDomainDeviceInfoIterateInternal(virDomainDefPtr def,
6ae9ed
     case VIR_DOMAIN_DEVICE_LAST:
6ae9ed
     case VIR_DOMAIN_DEVICE_RNG:
6ae9ed
     case VIR_DOMAIN_DEVICE_MEMORY:
6ae9ed
+    case VIR_DOMAIN_DEVICE_IOMMU:
6ae9ed
         break;
6ae9ed
     }
6ae9ed
 #endif
6ae9ed
@@ -4635,6 +4646,7 @@ virDomainDeviceDefValidateInternal(const virDomainDeviceDef *dev,
6ae9ed
     case VIR_DOMAIN_DEVICE_TPM:
6ae9ed
     case VIR_DOMAIN_DEVICE_PANIC:
6ae9ed
     case VIR_DOMAIN_DEVICE_MEMORY:
6ae9ed
+    case VIR_DOMAIN_DEVICE_IOMMU:
6ae9ed
     case VIR_DOMAIN_DEVICE_NONE:
6ae9ed
     case VIR_DOMAIN_DEVICE_LAST:
6ae9ed
         break;
6ae9ed
@@ -13269,6 +13281,39 @@ virDomainMemoryDefParseXML(xmlNodePtr memdevNode,
6ae9ed
 }
6ae9ed
 
6ae9ed
 
6ae9ed
+static virDomainIOMMUDefPtr
6ae9ed
+virDomainIOMMUDefParseXML(xmlNodePtr node)
6ae9ed
+{
6ae9ed
+    virDomainIOMMUDefPtr iommu = NULL, ret = NULL;
6ae9ed
+    char *tmp = NULL;
6ae9ed
+    int val;
6ae9ed
+
6ae9ed
+    if (VIR_ALLOC(iommu) < 0)
6ae9ed
+        goto cleanup;
6ae9ed
+
6ae9ed
+    if (!(tmp = virXMLPropString(node, "model"))) {
6ae9ed
+        virReportError(VIR_ERR_XML_ERROR, "%s",
6ae9ed
+                       _("missing model for IOMMU device"));
6ae9ed
+        goto cleanup;
6ae9ed
+    }
6ae9ed
+
6ae9ed
+    if ((val = virDomainIOMMUModelTypeFromString(tmp)) < 0) {
6ae9ed
+        virReportError(VIR_ERR_XML_ERROR, _("unknown IOMMU model: %s"), tmp);
6ae9ed
+        goto cleanup;
6ae9ed
+    }
6ae9ed
+
6ae9ed
+    iommu->model = val;
6ae9ed
+
6ae9ed
+    ret = iommu;
6ae9ed
+    iommu = NULL;
6ae9ed
+
6ae9ed
+ cleanup:
6ae9ed
+    VIR_FREE(iommu);
6ae9ed
+    VIR_FREE(tmp);
6ae9ed
+    return ret;
6ae9ed
+}
6ae9ed
+
6ae9ed
+
6ae9ed
 virDomainDeviceDefPtr
6ae9ed
 virDomainDeviceDefParse(const char *xmlStr,
6ae9ed
                         const virDomainDef *def,
6ae9ed
@@ -13410,6 +13455,10 @@ virDomainDeviceDefParse(const char *xmlStr,
6ae9ed
         if (!(dev->data.memory = virDomainMemoryDefParseXML(node, ctxt, flags)))
6ae9ed
             goto error;
6ae9ed
         break;
6ae9ed
+    case VIR_DOMAIN_DEVICE_IOMMU:
6ae9ed
+        if (!(dev->data.iommu = virDomainIOMMUDefParseXML(node)))
6ae9ed
+            goto error;
6ae9ed
+        break;
6ae9ed
     case VIR_DOMAIN_DEVICE_NONE:
6ae9ed
     case VIR_DOMAIN_DEVICE_LAST:
6ae9ed
         break;
6ae9ed
@@ -17217,6 +17266,21 @@ virDomainDefParseXML(xmlDocPtr xml,
6ae9ed
     }
6ae9ed
     VIR_FREE(nodes);
6ae9ed
 
6ae9ed
+    if ((n = virXPathNodeSet("./devices/iommu", ctxt, &nodes)) < 0)
6ae9ed
+        goto error;
6ae9ed
+
6ae9ed
+    if (n > 1) {
6ae9ed
+        virReportError(VIR_ERR_XML_ERROR, "%s",
6ae9ed
+                       _("only a single IOMMU device is supported"));
6ae9ed
+        goto error;
6ae9ed
+    }
6ae9ed
+
6ae9ed
+    if (n > 0) {
6ae9ed
+        if (!(def->iommu = virDomainIOMMUDefParseXML(nodes[0])))
6ae9ed
+            goto error;
6ae9ed
+    }
6ae9ed
+    VIR_FREE(nodes);
6ae9ed
+
6ae9ed
     /* analysis of the user namespace mapping */
6ae9ed
     if ((n = virXPathNodeSet("./idmap/uid", ctxt, &nodes)) < 0)
6ae9ed
         goto error;
6ae9ed
@@ -18983,6 +19047,23 @@ virDomainDefCheckABIStability(virDomainDefPtr src,
6ae9ed
             goto error;
6ae9ed
     }
6ae9ed
 
6ae9ed
+    if (!!src->iommu != !!dst->iommu) {
6ae9ed
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
6ae9ed
+                       _("Target domain IOMMU device count "
6ae9ed
+                         "does not match source"));
6ae9ed
+        goto error;
6ae9ed
+    }
6ae9ed
+
6ae9ed
+    if (src->iommu &&
6ae9ed
+        src->iommu->model != dst->iommu->model) {
6ae9ed
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
6ae9ed
+                       _("Target domain IOMMU device model '%s' "
6ae9ed
+                         "does not match source '%s'"),
6ae9ed
+                       virDomainIOMMUModelTypeToString(dst->iommu->model),
6ae9ed
+                       virDomainIOMMUModelTypeToString(src->iommu->model));
6ae9ed
+        goto error;
6ae9ed
+    }
6ae9ed
+
6ae9ed
     /* Coverity is not very happy with this - all dead_error_condition */
6ae9ed
 #if !STATIC_ANALYSIS
6ae9ed
     /* This switch statement is here to trigger compiler warning when adding
6ae9ed
@@ -19015,6 +19096,7 @@ virDomainDefCheckABIStability(virDomainDefPtr src,
6ae9ed
     case VIR_DOMAIN_DEVICE_PANIC:
6ae9ed
     case VIR_DOMAIN_DEVICE_SHMEM:
6ae9ed
     case VIR_DOMAIN_DEVICE_MEMORY:
6ae9ed
+    case VIR_DOMAIN_DEVICE_IOMMU:
6ae9ed
         break;
6ae9ed
     }
6ae9ed
 #endif
6ae9ed
@@ -23525,6 +23607,11 @@ virDomainDefFormatInternal(virDomainDefPtr def,
6ae9ed
             goto error;
6ae9ed
     }
6ae9ed
 
6ae9ed
+    if (def->iommu) {
6ae9ed
+        virBufferAsprintf(buf, "<iommu model='%s'/>\n",
6ae9ed
+                          virDomainIOMMUModelTypeToString(def->iommu->model));
6ae9ed
+    }
6ae9ed
+
6ae9ed
     virBufferAdjustIndent(buf, -2);
6ae9ed
     virBufferAddLit(buf, "</devices>\n");
6ae9ed
 
6ae9ed
@@ -24642,6 +24729,7 @@ virDomainDeviceDefCopy(virDomainDeviceDefPtr src,
6ae9ed
     case VIR_DOMAIN_DEVICE_MEMBALLOON:
6ae9ed
     case VIR_DOMAIN_DEVICE_NVRAM:
6ae9ed
     case VIR_DOMAIN_DEVICE_SHMEM:
6ae9ed
+    case VIR_DOMAIN_DEVICE_IOMMU:
6ae9ed
     case VIR_DOMAIN_DEVICE_LAST:
6ae9ed
         virReportError(VIR_ERR_INTERNAL_ERROR,
6ae9ed
                        _("Copying definition of '%d' type "
6ae9ed
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
6ae9ed
index f29c4bd..1f75be1 100644
6ae9ed
--- a/src/conf/domain_conf.h
6ae9ed
+++ b/src/conf/domain_conf.h
6ae9ed
@@ -151,6 +151,9 @@ typedef virDomainShmemDef *virDomainShmemDefPtr;
6ae9ed
 typedef struct _virDomainTPMDef virDomainTPMDef;
6ae9ed
 typedef virDomainTPMDef *virDomainTPMDefPtr;
6ae9ed
 
6ae9ed
+typedef struct _virDomainIOMMUDef virDomainIOMMUDef;
6ae9ed
+typedef virDomainIOMMUDef *virDomainIOMMUDefPtr;
6ae9ed
+
6ae9ed
 /* Flags for the 'type' field in virDomainDeviceDef */
6ae9ed
 typedef enum {
6ae9ed
     VIR_DOMAIN_DEVICE_NONE = 0,
6ae9ed
@@ -176,6 +179,7 @@ typedef enum {
6ae9ed
     VIR_DOMAIN_DEVICE_TPM,
6ae9ed
     VIR_DOMAIN_DEVICE_PANIC,
6ae9ed
     VIR_DOMAIN_DEVICE_MEMORY,
6ae9ed
+    VIR_DOMAIN_DEVICE_IOMMU,
6ae9ed
 
6ae9ed
     VIR_DOMAIN_DEVICE_LAST
6ae9ed
 } virDomainDeviceType;
6ae9ed
@@ -207,6 +211,7 @@ struct _virDomainDeviceDef {
6ae9ed
         virDomainTPMDefPtr tpm;
6ae9ed
         virDomainPanicDefPtr panic;
6ae9ed
         virDomainMemoryDefPtr memory;
6ae9ed
+        virDomainIOMMUDefPtr iommu;
6ae9ed
     } data;
6ae9ed
 };
6ae9ed
 
6ae9ed
@@ -2097,6 +2102,15 @@ struct _virDomainKeyWrapDef {
6ae9ed
     int dea; /* enum virTristateSwitch */
6ae9ed
 };
6ae9ed
 
6ae9ed
+typedef enum {
6ae9ed
+    VIR_DOMAIN_IOMMU_MODEL_INTEL,
6ae9ed
+
6ae9ed
+    VIR_DOMAIN_IOMMU_MODEL_LAST
6ae9ed
+} virDomainIOMMUModel;
6ae9ed
+
6ae9ed
+struct _virDomainIOMMUDef {
6ae9ed
+    virDomainIOMMUModel model;
6ae9ed
+};
6ae9ed
 /*
6ae9ed
  * Guest VM main configuration
6ae9ed
  *
6ae9ed
@@ -2234,6 +2248,7 @@ struct _virDomainDef {
6ae9ed
     virCPUDefPtr cpu;
6ae9ed
     virSysinfoDefPtr sysinfo;
6ae9ed
     virDomainRedirFilterDefPtr redirfilter;
6ae9ed
+    virDomainIOMMUDefPtr iommu;
6ae9ed
 
6ae9ed
     void *namespaceData;
6ae9ed
     virDomainXMLNamespace ns;
6ae9ed
@@ -2999,6 +3014,7 @@ VIR_ENUM_DECL(virDomainTPMModel)
6ae9ed
 VIR_ENUM_DECL(virDomainTPMBackend)
6ae9ed
 VIR_ENUM_DECL(virDomainMemoryModel)
6ae9ed
 VIR_ENUM_DECL(virDomainMemoryBackingModel)
6ae9ed
+VIR_ENUM_DECL(virDomainIOMMUModel)
6ae9ed
 /* from libvirt.h */
6ae9ed
 VIR_ENUM_DECL(virDomainState)
6ae9ed
 VIR_ENUM_DECL(virDomainNostateReason)
6ae9ed
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
6ae9ed
index 8cd1ba3..4dd93d7 100644
6ae9ed
--- a/src/libvirt_private.syms
6ae9ed
+++ b/src/libvirt_private.syms
6ae9ed
@@ -353,6 +353,8 @@ virDomainHubTypeToString;
6ae9ed
 virDomainHypervTypeFromString;
6ae9ed
 virDomainHypervTypeToString;
6ae9ed
 virDomainInputDefFree;
6ae9ed
+virDomainIOMMUModelTypeFromString;
6ae9ed
+virDomainIOMMUModelTypeToString;
6ae9ed
 virDomainIOThreadIDAdd;
6ae9ed
 virDomainIOThreadIDDefFree;
6ae9ed
 virDomainIOThreadIDDel;
6ae9ed
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
6ae9ed
index f1425b7..2d6e5d2 100644
6ae9ed
--- a/src/qemu/qemu_driver.c
6ae9ed
+++ b/src/qemu/qemu_driver.c
6ae9ed
@@ -7440,6 +7440,7 @@ qemuDomainAttachDeviceLive(virDomainObjPtr vm,
6ae9ed
     case VIR_DOMAIN_DEVICE_SHMEM:
6ae9ed
     case VIR_DOMAIN_DEVICE_TPM:
6ae9ed
     case VIR_DOMAIN_DEVICE_PANIC:
6ae9ed
+    case VIR_DOMAIN_DEVICE_IOMMU:
6ae9ed
     case VIR_DOMAIN_DEVICE_LAST:
6ae9ed
         virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
6ae9ed
                        _("live attach of device '%s' is not supported"),
6ae9ed
@@ -7531,6 +7532,7 @@ qemuDomainDetachDeviceLive(virDomainObjPtr vm,
6ae9ed
     case VIR_DOMAIN_DEVICE_NONE:
6ae9ed
     case VIR_DOMAIN_DEVICE_TPM:
6ae9ed
     case VIR_DOMAIN_DEVICE_PANIC:
6ae9ed
+    case VIR_DOMAIN_DEVICE_IOMMU:
6ae9ed
     case VIR_DOMAIN_DEVICE_LAST:
6ae9ed
         virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
6ae9ed
                        _("live detach of device '%s' is not supported"),
6ae9ed
@@ -7646,6 +7648,7 @@ qemuDomainUpdateDeviceLive(virConnectPtr conn,
6ae9ed
     case VIR_DOMAIN_DEVICE_NONE:
6ae9ed
     case VIR_DOMAIN_DEVICE_TPM:
6ae9ed
     case VIR_DOMAIN_DEVICE_PANIC:
6ae9ed
+    case VIR_DOMAIN_DEVICE_IOMMU:
6ae9ed
     case VIR_DOMAIN_DEVICE_LAST:
6ae9ed
         virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
6ae9ed
                        _("live update of device '%s' is not supported"),
6ae9ed
@@ -7809,6 +7812,7 @@ qemuDomainAttachDeviceConfig(virDomainDefPtr vmdef,
6ae9ed
     case VIR_DOMAIN_DEVICE_NONE:
6ae9ed
     case VIR_DOMAIN_DEVICE_TPM:
6ae9ed
     case VIR_DOMAIN_DEVICE_PANIC:
6ae9ed
+    case VIR_DOMAIN_DEVICE_IOMMU:
6ae9ed
     case VIR_DOMAIN_DEVICE_LAST:
6ae9ed
          virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
6ae9ed
                         _("persistent attach of device '%s' is not supported"),
6ae9ed
@@ -7963,6 +7967,7 @@ qemuDomainDetachDeviceConfig(virDomainDefPtr vmdef,
6ae9ed
     case VIR_DOMAIN_DEVICE_NONE:
6ae9ed
     case VIR_DOMAIN_DEVICE_TPM:
6ae9ed
     case VIR_DOMAIN_DEVICE_PANIC:
6ae9ed
+    case VIR_DOMAIN_DEVICE_IOMMU:
6ae9ed
     case VIR_DOMAIN_DEVICE_LAST:
6ae9ed
         virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
6ae9ed
                        _("persistent detach of device '%s' is not supported"),
6ae9ed
@@ -8061,6 +8066,7 @@ qemuDomainUpdateDeviceConfig(virDomainDefPtr vmdef,
6ae9ed
     case VIR_DOMAIN_DEVICE_NONE:
6ae9ed
     case VIR_DOMAIN_DEVICE_TPM:
6ae9ed
     case VIR_DOMAIN_DEVICE_PANIC:
6ae9ed
+    case VIR_DOMAIN_DEVICE_IOMMU:
6ae9ed
     case VIR_DOMAIN_DEVICE_LAST:
6ae9ed
         virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
6ae9ed
                        _("persistent update of device '%s' is not supported"),
6ae9ed
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
6ae9ed
index 4a56cad..7a71333 100644
6ae9ed
--- a/src/qemu/qemu_hotplug.c
6ae9ed
+++ b/src/qemu/qemu_hotplug.c
6ae9ed
@@ -3328,6 +3328,7 @@ qemuDomainRemoveDevice(virQEMUDriverPtr driver,
6ae9ed
     case VIR_DOMAIN_DEVICE_SHMEM:
6ae9ed
     case VIR_DOMAIN_DEVICE_TPM:
6ae9ed
     case VIR_DOMAIN_DEVICE_PANIC:
6ae9ed
+    case VIR_DOMAIN_DEVICE_IOMMU:
6ae9ed
     case VIR_DOMAIN_DEVICE_LAST:
6ae9ed
         virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
6ae9ed
                        _("don't know how to remove a %s device"),
6ae9ed
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-intel-iommu.xml b/tests/qemuxml2argvdata/qemuxml2argv-intel-iommu.xml
6ae9ed
new file mode 100644
6ae9ed
index 0000000..b5b2b51
6ae9ed
--- /dev/null
6ae9ed
+++ b/tests/qemuxml2argvdata/qemuxml2argv-intel-iommu.xml
6ae9ed
@@ -0,0 +1,37 @@
6ae9ed
+<domain type='qemu'>
6ae9ed
+  <name>QEMUGuest1</name>
6ae9ed
+  <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
6ae9ed
+  <memory unit='KiB'>219100</memory>
6ae9ed
+  <currentMemory unit='KiB'>219100</currentMemory>
6ae9ed
+  <vcpu placement='static'>1</vcpu>
6ae9ed
+  <os>
6ae9ed
+    <type arch='x86_64' machine='q35'>hvm</type>
6ae9ed
+    <boot dev='hd'/>
6ae9ed
+  </os>
6ae9ed
+  <clock offset='utc'/>
6ae9ed
+  <on_poweroff>destroy</on_poweroff>
6ae9ed
+  <on_reboot>restart</on_reboot>
6ae9ed
+  <on_crash>destroy</on_crash>
6ae9ed
+  <devices>
6ae9ed
+    <emulator>/usr/bin/qemu</emulator>
6ae9ed
+    <controller type='pci' index='0' model='pcie-root'/>
6ae9ed
+    <controller type='pci' index='1' model='dmi-to-pci-bridge'>
6ae9ed
+      <model name='i82801b11-bridge'/>
6ae9ed
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x1e' function='0x0'/>
6ae9ed
+    </controller>
6ae9ed
+    <controller type='pci' index='2' model='pci-bridge'>
6ae9ed
+      <model name='pci-bridge'/>
6ae9ed
+      <target chassisNr='2'/>
6ae9ed
+      <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
6ae9ed
+    </controller>
6ae9ed
+    <controller type='sata' index='0'>
6ae9ed
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x1f' function='0x2'/>
6ae9ed
+    </controller>
6ae9ed
+    <input type='mouse' bus='ps2'/>
6ae9ed
+    <input type='keyboard' bus='ps2'/>
6ae9ed
+    <memballoon model='virtio'>
6ae9ed
+      <address type='pci' domain='0x0000' bus='0x02' slot='0x01' function='0x0'/>
6ae9ed
+    </memballoon>
6ae9ed
+    <iommu model='intel'/>
6ae9ed
+  </devices>
6ae9ed
+</domain>
6ae9ed
diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-intel-iommu.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-intel-iommu.xml
6ae9ed
new file mode 100644
6ae9ed
index 0000000..b5b2b51
6ae9ed
--- /dev/null
6ae9ed
+++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-intel-iommu.xml
6ae9ed
@@ -0,0 +1,37 @@
6ae9ed
+<domain type='qemu'>
6ae9ed
+  <name>QEMUGuest1</name>
6ae9ed
+  <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
6ae9ed
+  <memory unit='KiB'>219100</memory>
6ae9ed
+  <currentMemory unit='KiB'>219100</currentMemory>
6ae9ed
+  <vcpu placement='static'>1</vcpu>
6ae9ed
+  <os>
6ae9ed
+    <type arch='x86_64' machine='q35'>hvm</type>
6ae9ed
+    <boot dev='hd'/>
6ae9ed
+  </os>
6ae9ed
+  <clock offset='utc'/>
6ae9ed
+  <on_poweroff>destroy</on_poweroff>
6ae9ed
+  <on_reboot>restart</on_reboot>
6ae9ed
+  <on_crash>destroy</on_crash>
6ae9ed
+  <devices>
6ae9ed
+    <emulator>/usr/bin/qemu</emulator>
6ae9ed
+    <controller type='pci' index='0' model='pcie-root'/>
6ae9ed
+    <controller type='pci' index='1' model='dmi-to-pci-bridge'>
6ae9ed
+      <model name='i82801b11-bridge'/>
6ae9ed
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x1e' function='0x0'/>
6ae9ed
+    </controller>
6ae9ed
+    <controller type='pci' index='2' model='pci-bridge'>
6ae9ed
+      <model name='pci-bridge'/>
6ae9ed
+      <target chassisNr='2'/>
6ae9ed
+      <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
6ae9ed
+    </controller>
6ae9ed
+    <controller type='sata' index='0'>
6ae9ed
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x1f' function='0x2'/>
6ae9ed
+    </controller>
6ae9ed
+    <input type='mouse' bus='ps2'/>
6ae9ed
+    <input type='keyboard' bus='ps2'/>
6ae9ed
+    <memballoon model='virtio'>
6ae9ed
+      <address type='pci' domain='0x0000' bus='0x02' slot='0x01' function='0x0'/>
6ae9ed
+    </memballoon>
6ae9ed
+    <iommu model='intel'/>
6ae9ed
+  </devices>
6ae9ed
+</domain>
6ae9ed
diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c
6ae9ed
index c6ef28c..2112d39 100644
6ae9ed
--- a/tests/qemuxml2xmltest.c
6ae9ed
+++ b/tests/qemuxml2xmltest.c
6ae9ed
@@ -830,6 +830,10 @@ mymain(void)
6ae9ed
     DO_TEST("video-qxl-heads");
6ae9ed
     DO_TEST("video-qxl-noheads");
6ae9ed
 
6ae9ed
+    DO_TEST_FULL("intel-iommu", WHEN_ACTIVE, GIC_NONE,
6ae9ed
+                 QEMU_CAPS_DEVICE_PCI_BRIDGE,
6ae9ed
+                 QEMU_CAPS_DEVICE_DMI_TO_PCI_BRIDGE);
6ae9ed
+
6ae9ed
     qemuTestDriverFree(&driver);
6ae9ed
 
6ae9ed
     return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
6ae9ed
-- 
6ae9ed
2.9.2
6ae9ed