From: Jan Kiszka <jan.kis...@siemens.com>

Introduce a DMA default target address space for PCI devices. Catch all
interrupt requests to the front-side bus via an MSI memory region that
is part of that address space. Provide separate address spaces for
IOAPIC and HPET if the IOMMU is active to prepare for adding remapping
logic later on. Deliver the EDID from the IOAPIC for the same reason.

Allows to remove the MSI hack from the APIC MMIO write handler.

Signed-off-by: Rita Sinha <rita.sinh...@gmail.com>
---
 hw/i386/intel_iommu.c             | 22 ++++++++++------
 hw/i386/kvm/apic.c                | 24 +++++++++++-------
 hw/i386/pc.c                      | 17 +++++++++++++
 hw/i386/pc_piix.c                 |  1 -
 hw/i386/pc_q35.c                  | 11 +++++---
 hw/i386/xen/xen_apic.c            | 24 +++++++++++-------
 hw/intc/apic.c                    | 53 +++++++++++++++++++++++----------------
 hw/intc/apic_common.c             |  2 ++
 hw/intc/ioapic.c                  | 32 ++++++++---------------
 hw/pci-host/q35.c                 |  7 ++++++
 hw/pci/pci.c                      |  8 +++++-
 hw/timer/hpet.c                   |  6 ++---
 include/hw/i386/apic-msidef.h     |  4 +++
 include/hw/i386/apic_internal.h   |  1 +
 include/hw/i386/ioapic_internal.h |  1 +
 include/hw/i386/pc.h              |  6 +++++
 include/hw/pci-host/q35.h         |  4 +++
 include/hw/pci/pci.h              |  2 ++
 target-i386/cpu.c                 |  7 ++++++
 target-i386/cpu.h                 |  2 +-
 20 files changed, 156 insertions(+), 78 deletions(-)

diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c
index 347718f..c371588 100644
--- a/hw/i386/intel_iommu.c
+++ b/hw/i386/intel_iommu.c
@@ -24,6 +24,7 @@
 #include "exec/address-spaces.h"
 #include "intel_iommu_internal.h"
 #include "hw/pci/pci.h"
+#include "hw/i386/pc.h"
 
 /*#define DEBUG_INTEL_IOMMU*/
 #ifdef DEBUG_INTEL_IOMMU
@@ -266,6 +267,11 @@ static void vtd_update_iotlb(IntelIOMMUState *s, uint16_t 
source_id,
     g_hash_table_replace(s->iotlb, key, entry);
 }
 
+static AddressSpace *get_dma_address_space(void)
+{
+    return &PC_MACHINE(qdev_get_machine())->dma_address_space;
+}
+
 /* Given the reg addr of both the message data and address, generate an
  * interrupt via MSI.
  */
@@ -282,7 +288,7 @@ static void vtd_generate_interrupt(IntelIOMMUState *s, 
hwaddr mesg_addr_reg,
     data = vtd_get_long_raw(s, mesg_data_reg);
 
     VTD_DPRINTF(FLOG, "msi: addr 0x%"PRIx64 " data 0x%"PRIx32, addr, data);
-    address_space_stl_le(&address_space_memory, addr, data,
+    address_space_stl_le(get_dma_address_space(), addr, data,
                          MEMTXATTRS_UNSPECIFIED, NULL);
 }
 
@@ -496,7 +502,7 @@ static int vtd_get_root_entry(IntelIOMMUState *s, uint8_t 
index,
     dma_addr_t addr;
 
     addr = s->root + index * sizeof(*re);
-    if (dma_memory_read(&address_space_memory, addr, re, sizeof(*re))) {
+    if (dma_memory_read(get_dma_address_space(), addr, re, sizeof(*re))) {
         VTD_DPRINTF(GENERAL, "error: fail to access root-entry at 0x%"PRIx64
                     " + %"PRIu8, s->root, index);
         re->val = 0;
@@ -521,7 +527,7 @@ static int vtd_get_context_entry_from_root(VTDRootEntry 
*root, uint8_t index,
         return -VTD_FR_ROOT_ENTRY_P;
     }
     addr = (root->val & VTD_ROOT_ENTRY_CTP) + index * sizeof(*ce);
-    if (dma_memory_read(&address_space_memory, addr, ce, sizeof(*ce))) {
+    if (dma_memory_read(get_dma_address_space(), addr, ce, sizeof(*ce))) {
         VTD_DPRINTF(GENERAL, "error: fail to access context-entry at 0x%"PRIx64
                     " + %"PRIu8,
                     (uint64_t)(root->val & VTD_ROOT_ENTRY_CTP), index);
@@ -555,7 +561,7 @@ static uint64_t vtd_get_slpte(dma_addr_t base_addr, 
uint32_t index)
 
     assert(index < VTD_SL_PT_ENTRY_NR);
 
-    if (dma_memory_read(&address_space_memory,
+    if (dma_memory_read(get_dma_address_space(),
                         base_addr + index * sizeof(slpte), &slpte,
                         sizeof(slpte))) {
         slpte = (uint64_t)-1;
@@ -1227,7 +1233,7 @@ static bool vtd_get_inv_desc(dma_addr_t base_addr, 
uint32_t offset,
                              VTDInvDesc *inv_desc)
 {
     dma_addr_t addr = base_addr + offset * sizeof(*inv_desc);
-    if (dma_memory_read(&address_space_memory, addr, inv_desc,
+    if (dma_memory_read(get_dma_address_space(), addr, inv_desc,
         sizeof(*inv_desc))) {
         VTD_DPRINTF(GENERAL, "error: fail to fetch Invalidation Descriptor "
                     "base_addr 0x%"PRIx64 " offset %"PRIu32, base_addr, 
offset);
@@ -1262,8 +1268,8 @@ static bool vtd_process_wait_desc(IntelIOMMUState *s, 
VTDInvDesc *inv_desc)
         VTD_DPRINTF(INV, "status data 0x%x, status addr 0x%"PRIx64,
                     status_data, status_addr);
         status_data = cpu_to_le32(status_data);
-        if (dma_memory_write(&address_space_memory, status_addr, &status_data,
-                             sizeof(status_data))) {
+        if (dma_memory_write(get_dma_address_space(), status_addr,
+                             &status_data, sizeof(status_data))) {
             VTD_DPRINTF(GENERAL, "error: fail to perform a coherent write");
             return false;
         }
@@ -1845,7 +1851,7 @@ static IOMMUTLBEntry vtd_iommu_translate(MemoryRegion 
*iommu, hwaddr addr,
     VTDAddressSpace *vtd_as = container_of(iommu, VTDAddressSpace, iommu);
     IntelIOMMUState *s = vtd_as->iommu_state;
     IOMMUTLBEntry ret = {
-        .target_as = &address_space_memory,
+        .target_as = get_dma_address_space(),
         .iova = addr,
         .translated_addr = 0,
         .addr_mask = ~(hwaddr)0,
diff --git a/hw/i386/kvm/apic.c b/hw/i386/kvm/apic.c
index 694d398..5f55cf1 100644
--- a/hw/i386/kvm/apic.c
+++ b/hw/i386/kvm/apic.c
@@ -10,6 +10,7 @@
  * See the COPYING file in the top-level directory.
  */
 #include "qemu/osdep.h"
+#include "hw/i386/apic-msidef.h"
 #include "hw/i386/apic_internal.h"
 #include "hw/pci/msi.h"
 #include "sysemu/kvm.h"
@@ -147,14 +148,13 @@ static void kvm_apic_external_nmi(APICCommonState *s)
     run_on_cpu(CPU(s->cpu), do_inject_external_nmi, s);
 }
 
-static uint64_t kvm_apic_mem_read(void *opaque, hwaddr addr,
-                                  unsigned size)
+static uint64_t kvm_msi_region_read(void *opaque, hwaddr addr, unsigned size)
 {
     return ~(uint64_t)0;
 }
 
-static void kvm_apic_mem_write(void *opaque, hwaddr addr,
-                               uint64_t data, unsigned size)
+static void kvm_msi_region_write(void *opaque, hwaddr addr, uint64_t data,
+                                 unsigned size)
 {
     MSIMessage msg = { .address = addr, .data = data };
     int ret;
@@ -166,10 +166,14 @@ static void kvm_apic_mem_write(void *opaque, hwaddr addr,
     }
 }
 
-static const MemoryRegionOps kvm_apic_io_ops = {
-    .read = kvm_apic_mem_read,
-    .write = kvm_apic_mem_write,
+static const MemoryRegionOps kvm_msi_region_ops = {
+    .read = kvm_msi_region_read,
+    .write = kvm_msi_region_write,
     .endianness = DEVICE_NATIVE_ENDIAN,
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
 };
 
 static void kvm_apic_reset(APICCommonState *s)
@@ -182,8 +186,10 @@ static void kvm_apic_realize(DeviceState *dev, Error 
**errp)
 {
     APICCommonState *s = APIC_COMMON(dev);
 
-    memory_region_init_io(&s->io_memory, NULL, &kvm_apic_io_ops, s, 
"kvm-apic-msi",
-                          APIC_SPACE_SIZE);
+    memory_region_init(&s->io_memory, NULL, "kvm-apic", APIC_SPACE_SIZE);
+
+    memory_region_init_io(&s->msi_region, NULL, &kvm_msi_region_ops, NULL,
+                          "kvm-msi", MSI_REGION_SIZE);
 
     if (kvm_has_gsi_routing()) {
         msi_supported = true;
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index 0aeefd2..608d903 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -26,6 +26,7 @@
 #include "hw/i386/pc.h"
 #include "hw/char/serial.h"
 #include "hw/i386/apic.h"
+#include "hw/i386/apic-msidef.h"
 #include "hw/i386/topology.h"
 #include "sysemu/cpus.h"
 #include "hw/block/fdc.h"
@@ -1281,6 +1282,7 @@ void pc_memory_init(PCMachineState *pcms,
     FWCfgState *fw_cfg;
     MachineState *machine = MACHINE(pcms);
     PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
+    MemoryRegion *dma, *mem_alias;
 
     assert(machine->ram_size == pcms->below_4g_mem_size +
                                 pcms->above_4g_mem_size);
@@ -1360,6 +1362,21 @@ void pc_memory_init(PCMachineState *pcms,
                                     &pcms->hotplug_memory.mr);
     }
 
+    /* Initialized DMA address space */
+    dma = g_malloc(sizeof(*dma));
+    memory_region_init(dma, NULL, "dma-container",
+                       memory_region_size(system_memory));
+    address_space_init(&pcms->dma_address_space, dma, "pc-dma");
+
+    mem_alias = g_malloc(sizeof(*mem_alias));
+    memory_region_init_alias(mem_alias, NULL, "system-memory", system_memory,
+                             0, memory_region_size(system_memory));
+    memory_region_add_subregion_overlap(dma, 0, mem_alias, 0);
+
+    pci_set_dma_address_space(&pcms->dma_address_space);
+    pcms->ioapic_msi_target = &pcms->dma_address_space;
+    pcms->hpet_msi_target = &pcms->dma_address_space;
+
     /* Initialize PC system firmware */
     pc_system_firmware_init(rom_memory, !pcmc->pci_enabled);
 
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index 6f8c2cd..21ac546 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -126,7 +126,6 @@ static void pc_init1(MachineState *machine,
     }
 
     pc_cpus_init(pcms);
-
     if (kvm_enabled() && pcmc->kvmclock_enabled) {
         kvmclock_create();
     }
diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
index 46522c9..13fc647 100644
--- a/hw/i386/pc_q35.c
+++ b/hw/i386/pc_q35.c
@@ -113,10 +113,6 @@ static void pc_q35_init(MachineState *machine)
         xen_hvm_init(pcms, &ram_memory);
     }
 
-    pc_cpus_init(pcms);
-
-    kvmclock_create();
-
     /* pci enabled */
     if (pcmc->pci_enabled) {
         pci_memory = g_new(MemoryRegion, 1);
@@ -143,6 +139,13 @@ static void pc_q35_init(MachineState *machine)
                        rom_memory, &ram_memory);
     }
 
+    pc_cpus_init(pcms);
+    if (!pcmc->has_acpi_build) {
+        /* only machine types 1.7 & older need this */
+        pc_acpi_init("q35-acpi-dsdt.aml");
+    }
+
+    kvmclock_create();
     /* irq lines */
     gsi_state = g_malloc0(sizeof(*gsi_state));
     if (kvm_irqchip_in_kernel()) {
diff --git a/hw/i386/xen/xen_apic.c b/hw/i386/xen/xen_apic.c
index 2b8d709..71d52f4 100644
--- a/hw/i386/xen/xen_apic.c
+++ b/hw/i386/xen/xen_apic.c
@@ -10,18 +10,18 @@
  * later. See the COPYING file in the top-level directory.
  */
 #include "qemu/osdep.h"
+#include "hw/i386/apic-msidef.h"
 #include "hw/i386/apic_internal.h"
 #include "hw/pci/msi.h"
 #include "hw/xen/xen.h"
 
-static uint64_t xen_apic_mem_read(void *opaque, hwaddr addr,
-                                  unsigned size)
+static uint64_t xen_msi_region_read(void *opaque, hwaddr addr, unsigned size)
 {
     return ~(uint64_t)0;
 }
 
-static void xen_apic_mem_write(void *opaque, hwaddr addr,
-                               uint64_t data, unsigned size)
+static void xen_msi_region_write(void *opaque, hwaddr addr, uint64_t data,
+                                 unsigned size)
 {
     if (size != sizeof(uint32_t)) {
         fprintf(stderr, "Xen: APIC write data size = %d, invalid\n", size);
@@ -31,10 +31,14 @@ static void xen_apic_mem_write(void *opaque, hwaddr addr,
     xen_hvm_inject_msi(addr, data);
 }
 
-static const MemoryRegionOps xen_apic_io_ops = {
-    .read = xen_apic_mem_read,
-    .write = xen_apic_mem_write,
+static const MemoryRegionOps xen_msi_region_ops = {
+    .read = xen_msi_region_read,
+    .write = xen_msi_region_write,
     .endianness = DEVICE_NATIVE_ENDIAN,
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
 };
 
 static void xen_apic_realize(DeviceState *dev, Error **errp)
@@ -42,8 +46,10 @@ static void xen_apic_realize(DeviceState *dev, Error **errp)
     APICCommonState *s = APIC_COMMON(dev);
 
     s->vapic_control = 0;
-    memory_region_init_io(&s->io_memory, OBJECT(s), &xen_apic_io_ops, s,
-                          "xen-apic-msi", APIC_SPACE_SIZE);
+    memory_region_init(&s->io_memory, NULL, "xen-apic", APIC_SPACE_SIZE);
+
+    memory_region_init_io(&s->msi_region, NULL, &xen_msi_region_ops, NULL,
+                          "xen-msi", MSI_REGION_SIZE);
     msi_supported = true;
 }
 
diff --git a/hw/intc/apic.c b/hw/intc/apic.c
index a299462..2e99f75 100644
--- a/hw/intc/apic.c
+++ b/hw/intc/apic.c
@@ -732,31 +732,11 @@ static uint32_t apic_mem_readl(void *opaque, hwaddr addr)
     return val;
 }
 
-static void apic_send_msi(hwaddr addr, uint32_t data)
-{
-    uint8_t dest = (addr & MSI_ADDR_DEST_ID_MASK) >> MSI_ADDR_DEST_ID_SHIFT;
-    uint8_t vector = (data & MSI_DATA_VECTOR_MASK) >> MSI_DATA_VECTOR_SHIFT;
-    uint8_t dest_mode = (addr >> MSI_ADDR_DEST_MODE_SHIFT) & 0x1;
-    uint8_t trigger_mode = (data >> MSI_DATA_TRIGGER_SHIFT) & 0x1;
-    uint8_t delivery = (data >> MSI_DATA_DELIVERY_MODE_SHIFT) & 0x7;
-    /* XXX: Ignore redirection hint. */
-    apic_deliver_irq(dest, dest_mode, delivery, vector, trigger_mode);
-}
-
 static void apic_mem_writel(void *opaque, hwaddr addr, uint32_t val)
 {
     DeviceState *dev;
     APICCommonState *s;
     int index = (addr >> 4) & 0xff;
-    if (addr > 0xfff || !index) {
-        /* MSI and MMIO APIC are at the same memory location,
-         * but actually not on the global bus: MSI is on PCI bus
-         * APIC is connected directly to the CPU.
-         * Mapping them on the global bus happens to work because
-         * MSI registers are reserved in APIC MMIO and vice versa. */
-        apic_send_msi(addr, val);
-        return;
-    }
 
     dev = cpu_get_current_apic();
     if (!dev) {
@@ -856,6 +836,34 @@ static void apic_post_load(APICCommonState *s)
     }
 }
 
+static void msi_region_write(void *opaque, hwaddr addr, uint64_t data,
+                             unsigned size)
+{
+    uint8_t dest = (addr & MSI_ADDR_DEST_ID_MASK) >> MSI_ADDR_DEST_ID_SHIFT;
+    uint8_t vector = (data & MSI_DATA_VECTOR_MASK) >> MSI_DATA_VECTOR_SHIFT;
+    uint8_t dest_mode = (addr >> MSI_ADDR_DEST_MODE_SHIFT) & 0x1;
+    uint8_t trigger_mode = (data >> MSI_DATA_TRIGGER_SHIFT) & 0x1;
+    uint8_t delivery = (data >> MSI_DATA_DELIVERY_MODE_SHIFT) & 0x7;
+    /* FIXME: Ignoring redirection hint. */
+
+    apic_deliver_irq(dest, dest_mode, delivery, vector, trigger_mode);
+}
+
+static uint64_t msi_region_read(void *opaque, hwaddr addr, unsigned size)
+{
+    return ~(uint64_t)0;
+}
+
+static const MemoryRegionOps msi_region_ops = {
+    .write = msi_region_write,
+    .read = msi_region_read,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+};
+
 static const MemoryRegionOps apic_io_ops = {
     .old_mmio = {
         .read = { apic_mem_readb, apic_mem_readw, apic_mem_readl, },
@@ -868,9 +876,12 @@ static void apic_realize(DeviceState *dev, Error **errp)
 {
     APICCommonState *s = APIC_COMMON(dev);
 
-    memory_region_init_io(&s->io_memory, OBJECT(s), &apic_io_ops, s, 
"apic-msi",
+    memory_region_init_io(&s->io_memory, OBJECT(s), &apic_io_ops, s, "apic",
                           APIC_SPACE_SIZE);
 
+    memory_region_init_io(&s->msi_region, NULL, &msi_region_ops, NULL, "msi",
+                          MSI_REGION_SIZE);
+
     s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, apic_timer, s);
     local_apics[s->idx] = s;
 
diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c
index 659f377..b54eb7c 100644
--- a/hw/intc/apic_common.c
+++ b/hw/intc/apic_common.c
@@ -19,7 +19,9 @@
  */
 #include "qemu/osdep.h"
 #include "hw/i386/apic.h"
+#include "hw/i386/apic-msidef.h"
 #include "hw/i386/apic_internal.h"
+#include "hw/i386/pc.h"
 #include "trace.h"
 #include "sysemu/kvm.h"
 #include "hw/qdev.h"
diff --git a/hw/intc/ioapic.c b/hw/intc/ioapic.c
index 378e663..7bc7ee4 100644
--- a/hw/intc/ioapic.c
+++ b/hw/intc/ioapic.c
@@ -23,6 +23,7 @@
 #include "qemu/osdep.h"
 #include "monitor/monitor.h"
 #include "hw/hw.h"
+#include "hw/i386/apic-msidef.h"
 #include "hw/i386/pc.h"
 #include "hw/i386/ioapic.h"
 #include "hw/i386/ioapic_internal.h"
@@ -49,31 +50,31 @@ extern int ioapic_no;
 
 static void ioapic_service(IOAPICCommonState *s)
 {
+    AddressSpace *msi_as = PC_MACHINE(qdev_get_machine())->ioapic_msi_target;
+    uint32_t addr, data;
     uint8_t i;
     uint8_t trig_mode;
     uint8_t vector;
     uint8_t delivery_mode;
     uint32_t mask;
     uint64_t entry;
-    uint8_t dest;
+    uint16_t dest_idx;
     uint8_t dest_mode;
 
     for (i = 0; i < IOAPIC_NUM_PINS; i++) {
         mask = 1 << i;
         if (s->irr & mask) {
-            int coalesce = 0;
 
             entry = s->ioredtbl[i];
             if (!(entry & IOAPIC_LVT_MASKED)) {
                 trig_mode = ((entry >> IOAPIC_LVT_TRIGGER_MODE_SHIFT) & 1);
-                dest = entry >> IOAPIC_LVT_DEST_SHIFT;
+                dest_idx = entry >> IOAPIC_LVT_DEST_IDX_SHIFT;
                 dest_mode = (entry >> IOAPIC_LVT_DEST_MODE_SHIFT) & 1;
                 delivery_mode =
                     (entry >> IOAPIC_LVT_DELIV_MODE_SHIFT) & IOAPIC_DM_MASK;
                 if (trig_mode == IOAPIC_TRIGGER_EDGE) {
                     s->irr &= ~mask;
                 } else {
-                    coalesce = s->ioredtbl[i] & IOAPIC_LVT_REMOTE_IRR;
                     s->ioredtbl[i] |= IOAPIC_LVT_REMOTE_IRR;
                 }
                 if (delivery_mode == IOAPIC_DM_EXTINT) {
@@ -81,23 +82,12 @@ static void ioapic_service(IOAPICCommonState *s)
                 } else {
                     vector = entry & IOAPIC_VECTOR_MASK;
                 }
-#ifdef CONFIG_KVM
-                if (kvm_irqchip_is_split()) {
-                    if (trig_mode == IOAPIC_TRIGGER_EDGE) {
-                        kvm_set_irq(kvm_state, i, 1);
-                        kvm_set_irq(kvm_state, i, 0);
-                    } else {
-                        if (!coalesce) {
-                            kvm_set_irq(kvm_state, i, 1);
-                        }
-                    }
-                    continue;
-                }
-#else
-                (void)coalesce;
-#endif
-                apic_deliver_irq(dest, dest_mode, delivery_mode, vector,
-                                 trig_mode);
+                addr = MSI_ADDR_BASE | (dest_idx << MSI_ADDR_DEST_IDX_SHIFT) |
+                    (dest_mode << MSI_ADDR_DEST_MODE_SHIFT);
+                data = (vector << MSI_DATA_VECTOR_SHIFT) |
+                    (trig_mode << MSI_DATA_TRIGGER_SHIFT) |
+                    (delivery_mode << MSI_DATA_DELIVERY_MODE_SHIFT);
+                stl_le_phys(msi_as, addr, data);
             }
         }
     }
diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c
index 115fb8c..566e3d8 100644
--- a/hw/pci-host/q35.c
+++ b/hw/pci-host/q35.c
@@ -436,6 +436,7 @@ static AddressSpace *q35_host_dma_iommu(PCIBus *bus, void 
*opaque, int devfn)
 
 static void mch_init_dmar(MCHPCIState *mch)
 {
+    PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
     PCIBus *pci_bus = PCI_BUS(qdev_get_parent_bus(DEVICE(mch)));
 
     mch->iommu = INTEL_IOMMU_DEVICE(qdev_create(NULL, 
TYPE_INTEL_IOMMU_DEVICE));
@@ -445,6 +446,12 @@ static void mch_init_dmar(MCHPCIState *mch)
     sysbus_mmio_map(SYS_BUS_DEVICE(mch->iommu), 0, Q35_HOST_BRIDGE_IOMMU_ADDR);
 
     pci_setup_iommu(pci_bus, q35_host_dma_iommu, mch->iommu);
+
+    pcms->ioapic_msi_target = q35_host_dma_iommu(pci_bus, mch->iommu,
+                              Q35_PSEUDO_DEVFN_IOAPIC);
+
+    pcms->hpet_msi_target = q35_host_dma_iommu(pci_bus, mch->iommu,
+                              Q35_PSEUDO_DEVFN_HPET);
 }
 
 static void mch_realize(PCIDevice *d, Error **errp)
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index e67664d..bad2055 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -52,6 +52,7 @@ static void pcibus_dev_print(Monitor *mon, DeviceState *dev, 
int indent);
 static char *pcibus_get_dev_path(DeviceState *dev);
 static char *pcibus_get_fw_dev_path(DeviceState *dev);
 static void pcibus_reset(BusState *qbus);
+static AddressSpace *pci_dma_address_space = &address_space_memory;
 
 static Property pci_props[] = {
     DEFINE_PROP_PCI_DEVFN("addr", PCIDevice, devfn, -1),
@@ -2396,6 +2397,11 @@ static void pci_device_class_init(ObjectClass *klass, 
void *data)
     pc->realize = pci_default_realize;
 }
 
+void pci_set_dma_address_space(AddressSpace *dma_address_space)
+{
+    pci_dma_address_space = dma_address_space;
+}
+
 AddressSpace *pci_device_iommu_address_space(PCIDevice *dev)
 {
     PCIBus *bus = PCI_BUS(dev->bus);
@@ -2407,7 +2413,7 @@ AddressSpace *pci_device_iommu_address_space(PCIDevice 
*dev)
     if (iommu_bus && iommu_bus->iommu_fn) {
         return iommu_bus->iommu_fn(bus, iommu_bus->iommu_opaque, dev->devfn);
     }
-    return &address_space_memory;
+    return pci_dma_address_space;
 }
 
 void pci_setup_iommu(PCIBus *bus, PCIIOMMUFunc fn, void *opaque)
diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c
index 0ad5420..f36ae68 100644
--- a/hw/timer/hpet.c
+++ b/hw/timer/hpet.c
@@ -208,9 +208,9 @@ static void update_irq(struct HPETTimer *timer, int set)
             }
         }
     } else if (timer_fsb_route(timer)) {
-        address_space_stl_le(&address_space_memory, timer->fsb >> 32,
-                             timer->fsb & 0xffffffff, MEMTXATTRS_UNSPECIFIED,
-                             NULL);
+        address_space_stl_le(PC_MACHINE(qdev_get_machine())->hpet_msi_target,
+                             timer->fsb >> 32, timer->fsb & 0xffffffff,
+                             MEMTXATTRS_UNSPECIFIED, NULL);
     } else if (timer->config & HPET_TN_TYPE_LEVEL) {
         s->isr |= mask;
         /* fold the ICH PIRQ# pin's internal inversion logic into hpet */
diff --git a/include/hw/i386/apic-msidef.h b/include/hw/i386/apic-msidef.h
index 6e2eb71..5052f23 100644
--- a/include/hw/i386/apic-msidef.h
+++ b/include/hw/i386/apic-msidef.h
@@ -5,6 +5,9 @@
  * Intel APIC constants: from include/asm/msidef.h
  */
 
+#define MSI_ADDR_BASE                   0xfee00000
+#define MSI_REGION_SIZE                 0x00100000
+
 /*
  * Shifts for MSI data
  */
@@ -25,6 +28,7 @@
 #define MSI_ADDR_REDIRECTION_SHIFT      3
 
 #define MSI_ADDR_DEST_ID_SHIFT          12
+#define MSI_ADDR_DEST_IDX_SHIFT         4
 #define  MSI_ADDR_DEST_ID_MASK          0x00ffff0
 
 #endif /* HW_APIC_MSIDEF_H */
diff --git a/include/hw/i386/apic_internal.h b/include/hw/i386/apic_internal.h
index 74fe935..ad5ffe5 100644
--- a/include/hw/i386/apic_internal.h
+++ b/include/hw/i386/apic_internal.h
@@ -154,6 +154,7 @@ struct APICCommonState {
     /*< public >*/
 
     MemoryRegion io_memory;
+    MemoryRegion msi_region;
     X86CPU *cpu;
     uint32_t apicbase;
     uint8_t id;
diff --git a/include/hw/i386/ioapic_internal.h 
b/include/hw/i386/ioapic_internal.h
index 797ed47..d279f2d 100644
--- a/include/hw/i386/ioapic_internal.h
+++ b/include/hw/i386/ioapic_internal.h
@@ -31,6 +31,7 @@
 #define IOAPIC_VERSION                  0x11
 
 #define IOAPIC_LVT_DEST_SHIFT           56
+#define IOAPIC_LVT_DEST_IDX_SHIFT       48
 #define IOAPIC_LVT_MASKED_SHIFT         16
 #define IOAPIC_LVT_TRIGGER_MODE_SHIFT   15
 #define IOAPIC_LVT_REMOTE_IRR_SHIFT     14
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index 8b3546e..1bdabf6 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -36,6 +36,9 @@
 /**
  * PCMachineState:
  * @acpi_dev: link to ACPI PM device that performs ACPI hotplug handling
+ * @dma_address_space: target address space for DMA from I/O devices
+ * @ioapic_msi_target: target address space for IOAPIC interrupt messages
+ * @hpet_msi_target: target address space for HPET interrupt messages
  */
 struct PCMachineState {
     /*< private >*/
@@ -65,6 +68,9 @@ struct PCMachineState {
     /* CPU and apic information: */
     bool apic_xrupt_override;
     unsigned apic_id_limit;
+    AddressSpace dma_address_space;
+    AddressSpace *ioapic_msi_target;
+    AddressSpace *hpet_msi_target;
 
     /* NUMA information: */
     uint64_t numa_nodes;
diff --git a/include/hw/pci-host/q35.h b/include/hw/pci-host/q35.h
index c5c073d..fc87013 100644
--- a/include/hw/pci-host/q35.h
+++ b/include/hw/pci-host/q35.h
@@ -74,6 +74,10 @@ typedef struct Q35PCIHost {
 #define Q35_MASK(bit, ms_bit, ls_bit) \
 ((uint##bit##_t)(((1ULL << ((ms_bit) + 1)) - 1) & ~((1ULL << ls_bit) - 1)))
 
+#define Q35_PSEUDO_BUS_PLATFORM         0xff
+#define Q35_PSEUDO_DEVFN_IOAPIC         0x01
+#define Q35_PSEUDO_DEVFN_HPET           0x02
+
 /*
  * gmch part
  */
diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h
index 0be07c8..d37b546 100644
--- a/include/hw/pci/pci.h
+++ b/include/hw/pci/pci.h
@@ -417,6 +417,8 @@ void pci_bus_get_w64_range(PCIBus *bus, Range *range);
 
 void pci_device_deassert_intx(PCIDevice *dev);
 
+void pci_set_dma_address_space(AddressSpace *dma_address_space);
+
 typedef AddressSpace *(*PCIIOMMUFunc)(PCIBus *, void *, int);
 
 AddressSpace *pci_device_iommu_address_space(PCIDevice *dev);
diff --git a/target-i386/cpu.c b/target-i386/cpu.c
index 0f38d1e..6ac1382 100644
--- a/target-i386/cpu.c
+++ b/target-i386/cpu.c
@@ -23,6 +23,8 @@
 #include "sysemu/cpus.h"
 #include "kvm_i386.h"
 
+#include "hw/i386/pc.h"
+#include "hw/i386/apic-msidef.h"
 #include "qemu/error-report.h"
 #include "qemu/option.h"
 #include "qemu/config-file.h"
@@ -2821,6 +2823,7 @@ static void x86_cpu_apic_realize(X86CPU *cpu, Error 
**errp)
 {
     APICCommonState *apic;
     static bool apic_mmio_map_once;
+    AddressSpace *dma_as;
 
     if (cpu->apic_state == NULL) {
         return;
@@ -2836,6 +2839,10 @@ static void x86_cpu_apic_realize(X86CPU *cpu, Error 
**errp)
                                             MSR_IA32_APICBASE_BASE,
                                             &apic->io_memory,
                                             0x1000);
+
+        dma_as = &PC_MACHINE(qdev_get_machine())->dma_address_space;
+        memory_region_add_subregion_overlap(dma_as->root, MSI_ADDR_BASE,
+                                            &apic->msi_region, 1);
         apic_mmio_map_once = true;
      }
 }
diff --git a/target-i386/cpu.h b/target-i386/cpu.h
index 5148c82..fddca76 100644
--- a/target-i386/cpu.h
+++ b/target-i386/cpu.h
@@ -1376,7 +1376,7 @@ const char *get_register_name_32(unsigned int reg);
 void enable_compat_apic_id_mode(void);
 
 #define APIC_DEFAULT_ADDRESS 0xfee00000
-#define APIC_SPACE_SIZE      0x100000
+#define APIC_SPACE_SIZE      0x1000
 
 void x86_cpu_dump_local_apic_state(CPUState *cs, FILE *f,
                                    fprintf_function cpu_fprintf, int flags);
-- 
2.7.2


Reply via email to