[for-5.0,v11,19/20] pc: Add support for virtio-iommu-pci
diff mbox series

Message ID 20191122182943.4656-20-eric.auger@redhat.com
State New
Headers show
Series
  • VIRTIO-IOMMU device
Related show

Commit Message

Auger Eric Nov. 22, 2019, 6:29 p.m. UTC
The virtio-iommu-pci is instantiated through the -device QEMU
option. However if instantiated it also requires an IORT ACPI table
to describe the ID mappings between the root complex and the iommu.

This patch adds the generation of the IORT table if the
virtio-iommu-pci device is instantiated.

We also declare the [0xfee00000 - 0xfeefffff] MSI reserved region
so that it gets bypassed by the IOMMU.

Signed-off-by: Eric Auger <eric.auger@redhat.com>
---
 hw/i386/acpi-build.c | 72 ++++++++++++++++++++++++++++++++++++++++++++
 hw/i386/pc.c         | 15 ++++++++-
 include/hw/i386/pc.h |  2 ++
 3 files changed, 88 insertions(+), 1 deletion(-)

Comments

Jean-Philippe Brucker Dec. 10, 2019, 4:50 p.m. UTC | #1
On Fri, Nov 22, 2019 at 07:29:42PM +0100, Eric Auger wrote:
> The virtio-iommu-pci is instantiated through the -device QEMU
> option. However if instantiated it also requires an IORT ACPI table
> to describe the ID mappings between the root complex and the iommu.
> 
> This patch adds the generation of the IORT table if the
> virtio-iommu-pci device is instantiated.
> 
> We also declare the [0xfee00000 - 0xfeefffff] MSI reserved region
> so that it gets bypassed by the IOMMU.
> 
> Signed-off-by: Eric Auger <eric.auger@redhat.com>

It would be nice to factor the IORT code with arm, but this looks OK.

Reviewed-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Auger Eric Dec. 24, 2019, 7:39 a.m. UTC | #2
Hi Jean,

On 12/10/19 5:50 PM, Jean-Philippe Brucker wrote:
> On Fri, Nov 22, 2019 at 07:29:42PM +0100, Eric Auger wrote:
>> The virtio-iommu-pci is instantiated through the -device QEMU
>> option. However if instantiated it also requires an IORT ACPI table
>> to describe the ID mappings between the root complex and the iommu.
>>
>> This patch adds the generation of the IORT table if the
>> virtio-iommu-pci device is instantiated.
>>
>> We also declare the [0xfee00000 - 0xfeefffff] MSI reserved region
>> so that it gets bypassed by the IOMMU.
>>
>> Signed-off-by: Eric Auger <eric.auger@redhat.com>
> 
> It would be nice to factor the IORT code with arm, but this looks OK.
I factorized the iort table code generation. Not sure this will be used
eventually but well.

Thanks

Eric
> 
> Reviewed-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
>
Michael S. Tsirkin Jan. 9, 2020, 12:02 p.m. UTC | #3
On Fri, Nov 22, 2019 at 07:29:42PM +0100, Eric Auger wrote:
> The virtio-iommu-pci is instantiated through the -device QEMU
> option. However if instantiated it also requires an IORT ACPI table
> to describe the ID mappings between the root complex and the iommu.
> 
> This patch adds the generation of the IORT table if the
> virtio-iommu-pci device is instantiated.
> 
> We also declare the [0xfee00000 - 0xfeefffff] MSI reserved region
> so that it gets bypassed by the IOMMU.
> 
> Signed-off-by: Eric Auger <eric.auger@redhat.com>

So I'd like us to use virtio config space in preference to ACPI.

Guest bits for that are not ready yet, but I think it's
better to wait than maintaining both ACPI and config space forever
later.

Could you send a smaller patchset without pci/acpi bits for now?

> ---
>  hw/i386/acpi-build.c | 72 ++++++++++++++++++++++++++++++++++++++++++++
>  hw/i386/pc.c         | 15 ++++++++-
>  include/hw/i386/pc.h |  2 ++
>  3 files changed, 88 insertions(+), 1 deletion(-)
> 
> diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
> index 12ff55fcfb..f09cabdcae 100644
> --- a/hw/i386/acpi-build.c
> +++ b/hw/i386/acpi-build.c
> @@ -2744,6 +2744,72 @@ static bool acpi_get_mcfg(AcpiMcfgInfo *mcfg)
>      return true;
>  }
>  
> +static inline void
> +fill_iort_idmap(AcpiIortIdMapping *idmap, int i,
> +                uint32_t input_base, uint32_t id_count,
> +                uint32_t output_base, uint32_t output_reference)
> +{
> +    idmap[i].input_base = cpu_to_le32(input_base);
> +    idmap[i].id_count = cpu_to_le32(id_count);
> +    idmap[i].output_base = cpu_to_le32(output_base);
> +    idmap[i].output_reference = cpu_to_le32(output_reference);
> +}
> +
> +static void
> +build_iort(GArray *table_data, BIOSLinker *linker, PCMachineState *pcms)
> +{
> +    size_t iommu_node_size, rc_node_size, iommu_node_offset;
> +    int iort_start = table_data->len;
> +    AcpiIortPVIommuPCI *iommu;
> +    AcpiIortIdMapping *idmap;
> +    AcpiIortTable *iort;
> +    size_t iort_length;
> +    AcpiIortRC *rc;
> +
> +    iort = acpi_data_push(table_data, sizeof(*iort));
> +    iort_length = sizeof(*iort);
> +    iort->node_count = cpu_to_le32(2);
> +
> +    /* virtio-iommu node */
> +
> +    iommu_node_offset = sizeof(*iort);
> +    iort->node_offset = cpu_to_le32(iommu_node_offset);
> +    iommu_node_size = sizeof(*iommu);
> +    iort_length += iommu_node_offset;
> +    iommu = acpi_data_push(table_data, iommu_node_size);
> +    iommu->type = ACPI_IORT_NODE_PARAVIRT;
> +    iommu->length = cpu_to_le16(iommu_node_size);
> +    iommu->mapping_count = 0;
> +    iommu->devid = cpu_to_le32(pcms->virtio_iommu_bdf);
> +    iommu->model = cpu_to_le32(ACPI_IORT_NODE_PV_VIRTIO_IOMMU_PCI);
> +
> +    /* Root Complex Node */
> +    rc_node_size = sizeof(*rc) + 2 * sizeof(*idmap);
> +    iort_length += rc_node_size;
> +    rc = acpi_data_push(table_data, rc_node_size);
> +
> +    rc->type = ACPI_IORT_NODE_PCI_ROOT_COMPLEX;
> +    rc->length = cpu_to_le16(rc_node_size);
> +    rc->mapping_count = cpu_to_le32(2);
> +    rc->mapping_offset = cpu_to_le32(sizeof(*rc));
> +
> +    /* fully coherent device */
> +    rc->memory_properties.cache_coherency = cpu_to_le32(1);
> +    rc->memory_properties.memory_flags = 0x3; /* CCA = CPM = DCAS = 1 */
> +    rc->pci_segment_number = 0; /* MCFG pci_segment */
> +    fill_iort_idmap(rc->id_mapping_array, 0, 0, pcms->virtio_iommu_bdf, 0,
> +                    iommu_node_offset);
> +    fill_iort_idmap(rc->id_mapping_array, 1, pcms->virtio_iommu_bdf + 1,
> +                    0xFFFF - pcms->virtio_iommu_bdf,
> +                    pcms->virtio_iommu_bdf + 1, iommu_node_offset);
> +
> +    iort = (AcpiIortTable *)(table_data->data + iort_start);
> +    iort->length = cpu_to_le32(iort_length);
> +
> +    build_header(linker, table_data, (void *)(table_data->data + iort_start),
> +                 "IORT", table_data->len - iort_start, 0, NULL, NULL);
> +}
> +
>  static
>  void acpi_build(AcpiBuildTables *tables, MachineState *machine)
>  {
> @@ -2835,6 +2901,12 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine)
>              build_slit(tables_blob, tables->linker, machine);
>          }
>      }
> +
> +    if (pcms->virtio_iommu) {
> +        acpi_add_table(table_offsets, tables_blob);
> +        build_iort(tables_blob, tables->linker, pcms);
> +    }
> +
>      if (acpi_get_mcfg(&mcfg)) {
>          acpi_add_table(table_offsets, tables_blob);
>          build_mcfg(tables_blob, tables->linker, &mcfg);
> diff --git a/hw/i386/pc.c b/hw/i386/pc.c
> index ac08e63604..af984ee041 100644
> --- a/hw/i386/pc.c
> +++ b/hw/i386/pc.c
> @@ -84,6 +84,7 @@
>  #include "hw/net/ne2000-isa.h"
>  #include "standard-headers/asm-x86/bootparam.h"
>  #include "hw/virtio/virtio-pmem-pci.h"
> +#include "hw/virtio/virtio-iommu.h"
>  #include "hw/mem/memory-device.h"
>  #include "sysemu/replay.h"
>  #include "qapi/qmp/qerror.h"
> @@ -1940,6 +1941,11 @@ static void pc_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
>          pc_cpu_pre_plug(hotplug_dev, dev, errp);
>      } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI)) {
>          pc_virtio_pmem_pci_pre_plug(hotplug_dev, dev, errp);
> +    } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) {
> +        /* we declare a VIRTIO_IOMMU_RESV_MEM_T_MSI region */
> +        qdev_prop_set_uint32(dev, "len-reserved-regions", 1);
> +        qdev_prop_set_string(dev, "reserved-regions[0]",
> +                             "0xfee00000, 0xfeefffff, 1");
>      }
>  }
>  
> @@ -1952,6 +1958,12 @@ static void pc_machine_device_plug_cb(HotplugHandler *hotplug_dev,
>          pc_cpu_plug(hotplug_dev, dev, errp);
>      } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI)) {
>          pc_virtio_pmem_pci_plug(hotplug_dev, dev, errp);
> +    } else if (object_dynamic_cast(OBJECT(dev), "virtio-iommu-pci")) {
> +        PCMachineState *pcms = PC_MACHINE(hotplug_dev);
> +        PCIDevice *pdev = PCI_DEVICE(dev);
> +
> +        pcms->virtio_iommu = true;
> +        pcms->virtio_iommu_bdf = pci_get_bdf(pdev);
>      }
>  }
>  
> @@ -1990,7 +2002,8 @@ static HotplugHandler *pc_get_hotplug_handler(MachineState *machine,
>  {
>      if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) ||
>          object_dynamic_cast(OBJECT(dev), TYPE_CPU) ||
> -        object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI)) {
> +        object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI) ||
> +        object_dynamic_cast(OBJECT(dev), "virtio-iommu-pci")) {
>          return HOTPLUG_HANDLER(machine);
>      }
>  
> diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
> index 1f86eba3f9..221b4c6ef9 100644
> --- a/include/hw/i386/pc.h
> +++ b/include/hw/i386/pc.h
> @@ -49,6 +49,8 @@ struct PCMachineState {
>      bool smbus_enabled;
>      bool sata_enabled;
>      bool pit_enabled;
> +    bool virtio_iommu;
> +    uint16_t virtio_iommu_bdf;
>  
>      /* NUMA information: */
>      uint64_t numa_nodes;
> -- 
> 2.20.1
Auger Eric Jan. 9, 2020, 1:34 p.m. UTC | #4
Hi Michael,

On 1/9/20 1:02 PM, Michael S. Tsirkin wrote:
> On Fri, Nov 22, 2019 at 07:29:42PM +0100, Eric Auger wrote:
>> The virtio-iommu-pci is instantiated through the -device QEMU
>> option. However if instantiated it also requires an IORT ACPI table
>> to describe the ID mappings between the root complex and the iommu.
>>
>> This patch adds the generation of the IORT table if the
>> virtio-iommu-pci device is instantiated.
>>
>> We also declare the [0xfee00000 - 0xfeefffff] MSI reserved region
>> so that it gets bypassed by the IOMMU.
>>
>> Signed-off-by: Eric Auger <eric.auger@redhat.com>
> 
> So I'd like us to use virtio config space in preference to ACPI.
> 
> Guest bits for that are not ready yet, but I think it's
> better to wait than maintaining both ACPI and config space forever
> later.
> 
> Could you send a smaller patchset without pci/acpi bits for now?

Yes I am about to send v12.

Indeed I hope the DT integration in ARM virt can land in 5.0. All the
kernel dependencies are resolved and it complies with the voted spec.

Then I will push the non DT integration when Jean's series get
stabilized and spec updated.

Thanks

Eric
> 
>> ---
>>  hw/i386/acpi-build.c | 72 ++++++++++++++++++++++++++++++++++++++++++++
>>  hw/i386/pc.c         | 15 ++++++++-
>>  include/hw/i386/pc.h |  2 ++
>>  3 files changed, 88 insertions(+), 1 deletion(-)
>>
>> diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
>> index 12ff55fcfb..f09cabdcae 100644
>> --- a/hw/i386/acpi-build.c
>> +++ b/hw/i386/acpi-build.c
>> @@ -2744,6 +2744,72 @@ static bool acpi_get_mcfg(AcpiMcfgInfo *mcfg)
>>      return true;
>>  }
>>  
>> +static inline void
>> +fill_iort_idmap(AcpiIortIdMapping *idmap, int i,
>> +                uint32_t input_base, uint32_t id_count,
>> +                uint32_t output_base, uint32_t output_reference)
>> +{
>> +    idmap[i].input_base = cpu_to_le32(input_base);
>> +    idmap[i].id_count = cpu_to_le32(id_count);
>> +    idmap[i].output_base = cpu_to_le32(output_base);
>> +    idmap[i].output_reference = cpu_to_le32(output_reference);
>> +}
>> +
>> +static void
>> +build_iort(GArray *table_data, BIOSLinker *linker, PCMachineState *pcms)
>> +{
>> +    size_t iommu_node_size, rc_node_size, iommu_node_offset;
>> +    int iort_start = table_data->len;
>> +    AcpiIortPVIommuPCI *iommu;
>> +    AcpiIortIdMapping *idmap;
>> +    AcpiIortTable *iort;
>> +    size_t iort_length;
>> +    AcpiIortRC *rc;
>> +
>> +    iort = acpi_data_push(table_data, sizeof(*iort));
>> +    iort_length = sizeof(*iort);
>> +    iort->node_count = cpu_to_le32(2);
>> +
>> +    /* virtio-iommu node */
>> +
>> +    iommu_node_offset = sizeof(*iort);
>> +    iort->node_offset = cpu_to_le32(iommu_node_offset);
>> +    iommu_node_size = sizeof(*iommu);
>> +    iort_length += iommu_node_offset;
>> +    iommu = acpi_data_push(table_data, iommu_node_size);
>> +    iommu->type = ACPI_IORT_NODE_PARAVIRT;
>> +    iommu->length = cpu_to_le16(iommu_node_size);
>> +    iommu->mapping_count = 0;
>> +    iommu->devid = cpu_to_le32(pcms->virtio_iommu_bdf);
>> +    iommu->model = cpu_to_le32(ACPI_IORT_NODE_PV_VIRTIO_IOMMU_PCI);
>> +
>> +    /* Root Complex Node */
>> +    rc_node_size = sizeof(*rc) + 2 * sizeof(*idmap);
>> +    iort_length += rc_node_size;
>> +    rc = acpi_data_push(table_data, rc_node_size);
>> +
>> +    rc->type = ACPI_IORT_NODE_PCI_ROOT_COMPLEX;
>> +    rc->length = cpu_to_le16(rc_node_size);
>> +    rc->mapping_count = cpu_to_le32(2);
>> +    rc->mapping_offset = cpu_to_le32(sizeof(*rc));
>> +
>> +    /* fully coherent device */
>> +    rc->memory_properties.cache_coherency = cpu_to_le32(1);
>> +    rc->memory_properties.memory_flags = 0x3; /* CCA = CPM = DCAS = 1 */
>> +    rc->pci_segment_number = 0; /* MCFG pci_segment */
>> +    fill_iort_idmap(rc->id_mapping_array, 0, 0, pcms->virtio_iommu_bdf, 0,
>> +                    iommu_node_offset);
>> +    fill_iort_idmap(rc->id_mapping_array, 1, pcms->virtio_iommu_bdf + 1,
>> +                    0xFFFF - pcms->virtio_iommu_bdf,
>> +                    pcms->virtio_iommu_bdf + 1, iommu_node_offset);
>> +
>> +    iort = (AcpiIortTable *)(table_data->data + iort_start);
>> +    iort->length = cpu_to_le32(iort_length);
>> +
>> +    build_header(linker, table_data, (void *)(table_data->data + iort_start),
>> +                 "IORT", table_data->len - iort_start, 0, NULL, NULL);
>> +}
>> +
>>  static
>>  void acpi_build(AcpiBuildTables *tables, MachineState *machine)
>>  {
>> @@ -2835,6 +2901,12 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine)
>>              build_slit(tables_blob, tables->linker, machine);
>>          }
>>      }
>> +
>> +    if (pcms->virtio_iommu) {
>> +        acpi_add_table(table_offsets, tables_blob);
>> +        build_iort(tables_blob, tables->linker, pcms);
>> +    }
>> +
>>      if (acpi_get_mcfg(&mcfg)) {
>>          acpi_add_table(table_offsets, tables_blob);
>>          build_mcfg(tables_blob, tables->linker, &mcfg);
>> diff --git a/hw/i386/pc.c b/hw/i386/pc.c
>> index ac08e63604..af984ee041 100644
>> --- a/hw/i386/pc.c
>> +++ b/hw/i386/pc.c
>> @@ -84,6 +84,7 @@
>>  #include "hw/net/ne2000-isa.h"
>>  #include "standard-headers/asm-x86/bootparam.h"
>>  #include "hw/virtio/virtio-pmem-pci.h"
>> +#include "hw/virtio/virtio-iommu.h"
>>  #include "hw/mem/memory-device.h"
>>  #include "sysemu/replay.h"
>>  #include "qapi/qmp/qerror.h"
>> @@ -1940,6 +1941,11 @@ static void pc_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
>>          pc_cpu_pre_plug(hotplug_dev, dev, errp);
>>      } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI)) {
>>          pc_virtio_pmem_pci_pre_plug(hotplug_dev, dev, errp);
>> +    } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) {
>> +        /* we declare a VIRTIO_IOMMU_RESV_MEM_T_MSI region */
>> +        qdev_prop_set_uint32(dev, "len-reserved-regions", 1);
>> +        qdev_prop_set_string(dev, "reserved-regions[0]",
>> +                             "0xfee00000, 0xfeefffff, 1");
>>      }
>>  }
>>  
>> @@ -1952,6 +1958,12 @@ static void pc_machine_device_plug_cb(HotplugHandler *hotplug_dev,
>>          pc_cpu_plug(hotplug_dev, dev, errp);
>>      } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI)) {
>>          pc_virtio_pmem_pci_plug(hotplug_dev, dev, errp);
>> +    } else if (object_dynamic_cast(OBJECT(dev), "virtio-iommu-pci")) {
>> +        PCMachineState *pcms = PC_MACHINE(hotplug_dev);
>> +        PCIDevice *pdev = PCI_DEVICE(dev);
>> +
>> +        pcms->virtio_iommu = true;
>> +        pcms->virtio_iommu_bdf = pci_get_bdf(pdev);
>>      }
>>  }
>>  
>> @@ -1990,7 +2002,8 @@ static HotplugHandler *pc_get_hotplug_handler(MachineState *machine,
>>  {
>>      if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) ||
>>          object_dynamic_cast(OBJECT(dev), TYPE_CPU) ||
>> -        object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI)) {
>> +        object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI) ||
>> +        object_dynamic_cast(OBJECT(dev), "virtio-iommu-pci")) {
>>          return HOTPLUG_HANDLER(machine);
>>      }
>>  
>> diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
>> index 1f86eba3f9..221b4c6ef9 100644
>> --- a/include/hw/i386/pc.h
>> +++ b/include/hw/i386/pc.h
>> @@ -49,6 +49,8 @@ struct PCMachineState {
>>      bool smbus_enabled;
>>      bool sata_enabled;
>>      bool pit_enabled;
>> +    bool virtio_iommu;
>> +    uint16_t virtio_iommu_bdf;
>>  
>>      /* NUMA information: */
>>      uint64_t numa_nodes;
>> -- 
>> 2.20.1
>

Patch
diff mbox series

diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index 12ff55fcfb..f09cabdcae 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -2744,6 +2744,72 @@  static bool acpi_get_mcfg(AcpiMcfgInfo *mcfg)
     return true;
 }
 
+static inline void
+fill_iort_idmap(AcpiIortIdMapping *idmap, int i,
+                uint32_t input_base, uint32_t id_count,
+                uint32_t output_base, uint32_t output_reference)
+{
+    idmap[i].input_base = cpu_to_le32(input_base);
+    idmap[i].id_count = cpu_to_le32(id_count);
+    idmap[i].output_base = cpu_to_le32(output_base);
+    idmap[i].output_reference = cpu_to_le32(output_reference);
+}
+
+static void
+build_iort(GArray *table_data, BIOSLinker *linker, PCMachineState *pcms)
+{
+    size_t iommu_node_size, rc_node_size, iommu_node_offset;
+    int iort_start = table_data->len;
+    AcpiIortPVIommuPCI *iommu;
+    AcpiIortIdMapping *idmap;
+    AcpiIortTable *iort;
+    size_t iort_length;
+    AcpiIortRC *rc;
+
+    iort = acpi_data_push(table_data, sizeof(*iort));
+    iort_length = sizeof(*iort);
+    iort->node_count = cpu_to_le32(2);
+
+    /* virtio-iommu node */
+
+    iommu_node_offset = sizeof(*iort);
+    iort->node_offset = cpu_to_le32(iommu_node_offset);
+    iommu_node_size = sizeof(*iommu);
+    iort_length += iommu_node_offset;
+    iommu = acpi_data_push(table_data, iommu_node_size);
+    iommu->type = ACPI_IORT_NODE_PARAVIRT;
+    iommu->length = cpu_to_le16(iommu_node_size);
+    iommu->mapping_count = 0;
+    iommu->devid = cpu_to_le32(pcms->virtio_iommu_bdf);
+    iommu->model = cpu_to_le32(ACPI_IORT_NODE_PV_VIRTIO_IOMMU_PCI);
+
+    /* Root Complex Node */
+    rc_node_size = sizeof(*rc) + 2 * sizeof(*idmap);
+    iort_length += rc_node_size;
+    rc = acpi_data_push(table_data, rc_node_size);
+
+    rc->type = ACPI_IORT_NODE_PCI_ROOT_COMPLEX;
+    rc->length = cpu_to_le16(rc_node_size);
+    rc->mapping_count = cpu_to_le32(2);
+    rc->mapping_offset = cpu_to_le32(sizeof(*rc));
+
+    /* fully coherent device */
+    rc->memory_properties.cache_coherency = cpu_to_le32(1);
+    rc->memory_properties.memory_flags = 0x3; /* CCA = CPM = DCAS = 1 */
+    rc->pci_segment_number = 0; /* MCFG pci_segment */
+    fill_iort_idmap(rc->id_mapping_array, 0, 0, pcms->virtio_iommu_bdf, 0,
+                    iommu_node_offset);
+    fill_iort_idmap(rc->id_mapping_array, 1, pcms->virtio_iommu_bdf + 1,
+                    0xFFFF - pcms->virtio_iommu_bdf,
+                    pcms->virtio_iommu_bdf + 1, iommu_node_offset);
+
+    iort = (AcpiIortTable *)(table_data->data + iort_start);
+    iort->length = cpu_to_le32(iort_length);
+
+    build_header(linker, table_data, (void *)(table_data->data + iort_start),
+                 "IORT", table_data->len - iort_start, 0, NULL, NULL);
+}
+
 static
 void acpi_build(AcpiBuildTables *tables, MachineState *machine)
 {
@@ -2835,6 +2901,12 @@  void acpi_build(AcpiBuildTables *tables, MachineState *machine)
             build_slit(tables_blob, tables->linker, machine);
         }
     }
+
+    if (pcms->virtio_iommu) {
+        acpi_add_table(table_offsets, tables_blob);
+        build_iort(tables_blob, tables->linker, pcms);
+    }
+
     if (acpi_get_mcfg(&mcfg)) {
         acpi_add_table(table_offsets, tables_blob);
         build_mcfg(tables_blob, tables->linker, &mcfg);
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index ac08e63604..af984ee041 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -84,6 +84,7 @@ 
 #include "hw/net/ne2000-isa.h"
 #include "standard-headers/asm-x86/bootparam.h"
 #include "hw/virtio/virtio-pmem-pci.h"
+#include "hw/virtio/virtio-iommu.h"
 #include "hw/mem/memory-device.h"
 #include "sysemu/replay.h"
 #include "qapi/qmp/qerror.h"
@@ -1940,6 +1941,11 @@  static void pc_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
         pc_cpu_pre_plug(hotplug_dev, dev, errp);
     } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI)) {
         pc_virtio_pmem_pci_pre_plug(hotplug_dev, dev, errp);
+    } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) {
+        /* we declare a VIRTIO_IOMMU_RESV_MEM_T_MSI region */
+        qdev_prop_set_uint32(dev, "len-reserved-regions", 1);
+        qdev_prop_set_string(dev, "reserved-regions[0]",
+                             "0xfee00000, 0xfeefffff, 1");
     }
 }
 
@@ -1952,6 +1958,12 @@  static void pc_machine_device_plug_cb(HotplugHandler *hotplug_dev,
         pc_cpu_plug(hotplug_dev, dev, errp);
     } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI)) {
         pc_virtio_pmem_pci_plug(hotplug_dev, dev, errp);
+    } else if (object_dynamic_cast(OBJECT(dev), "virtio-iommu-pci")) {
+        PCMachineState *pcms = PC_MACHINE(hotplug_dev);
+        PCIDevice *pdev = PCI_DEVICE(dev);
+
+        pcms->virtio_iommu = true;
+        pcms->virtio_iommu_bdf = pci_get_bdf(pdev);
     }
 }
 
@@ -1990,7 +2002,8 @@  static HotplugHandler *pc_get_hotplug_handler(MachineState *machine,
 {
     if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) ||
         object_dynamic_cast(OBJECT(dev), TYPE_CPU) ||
-        object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI)) {
+        object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI) ||
+        object_dynamic_cast(OBJECT(dev), "virtio-iommu-pci")) {
         return HOTPLUG_HANDLER(machine);
     }
 
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index 1f86eba3f9..221b4c6ef9 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -49,6 +49,8 @@  struct PCMachineState {
     bool smbus_enabled;
     bool sata_enabled;
     bool pit_enabled;
+    bool virtio_iommu;
+    uint16_t virtio_iommu_bdf;
 
     /* NUMA information: */
     uint64_t numa_nodes;