summary refs log tree commit diff stats
path: root/hw/pci.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/pci.c')
-rw-r--r--hw/pci.c63
1 files changed, 50 insertions, 13 deletions
diff --git a/hw/pci.c b/hw/pci.c
index ef94739718..8621d3d2b1 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -263,11 +263,14 @@ int pci_find_domain(const PCIBus *bus)
 }
 
 void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent,
-                         const char *name, uint8_t devfn_min)
+                         const char *name,
+                         MemoryRegion *address_space,
+                         uint8_t devfn_min)
 {
     qbus_create_inplace(&bus->qbus, &pci_bus_info, parent, name);
     assert(PCI_FUNC(devfn_min) == 0);
     bus->devfn_min = devfn_min;
+    bus->address_space = address_space;
 
     /* host bridge */
     QLIST_INIT(&bus->child);
@@ -276,13 +279,14 @@ void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent,
     vmstate_register(NULL, -1, &vmstate_pcibus, bus);
 }
 
-PCIBus *pci_bus_new(DeviceState *parent, const char *name, uint8_t devfn_min)
+PCIBus *pci_bus_new(DeviceState *parent, const char *name,
+                    MemoryRegion *address_space, uint8_t devfn_min)
 {
     PCIBus *bus;
 
     bus = qemu_mallocz(sizeof(*bus));
     bus->qbus.qdev_allocated = 1;
-    pci_bus_new_inplace(bus, parent, name, devfn_min);
+    pci_bus_new_inplace(bus, parent, name, address_space, devfn_min);
     return bus;
 }
 
@@ -310,11 +314,13 @@ void pci_bus_set_mem_base(PCIBus *bus, target_phys_addr_t base)
 
 PCIBus *pci_register_bus(DeviceState *parent, const char *name,
                          pci_set_irq_fn set_irq, pci_map_irq_fn map_irq,
-                         void *irq_opaque, uint8_t devfn_min, int nirq)
+                         void *irq_opaque,
+                         MemoryRegion *address_space,
+                         uint8_t devfn_min, int nirq)
 {
     PCIBus *bus;
 
-    bus = pci_bus_new(parent, name, devfn_min);
+    bus = pci_bus_new(parent, name, address_space, devfn_min);
     pci_bus_irqs(bus, set_irq, map_irq, irq_opaque, nirq);
     return bus;
 }
@@ -838,10 +844,15 @@ static void pci_unregister_io_regions(PCIDevice *pci_dev)
         if (r->type == PCI_BASE_ADDRESS_SPACE_IO) {
             isa_unassign_ioport(r->addr, r->filtered_size);
         } else {
-            cpu_register_physical_memory(pci_to_cpu_addr(pci_dev->bus,
-                                                         r->addr),
-                                         r->filtered_size,
-                                         IO_MEM_UNASSIGNED);
+            if (r->memory) {
+                memory_region_del_subregion(pci_dev->bus->address_space,
+                                            r->memory);
+            } else {
+                cpu_register_physical_memory(pci_to_cpu_addr(pci_dev->bus,
+                                                             r->addr),
+                                             r->filtered_size,
+                                             IO_MEM_UNASSIGNED);
+            }
         }
     }
 }
@@ -887,6 +898,7 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num,
     r->type = type;
     r->map_func = map_func;
     r->ram_addr = IO_MEM_UNASSIGNED;
+    r->memory = NULL;
 
     wmask = ~(size - 1);
     addr = pci_bar(pci_dev, region_num);
@@ -912,6 +924,16 @@ static void pci_simple_bar_mapfunc(PCIDevice *pci_dev, int region_num,
                                  pci_dev->io_regions[region_num].ram_addr);
 }
 
+static void pci_simple_bar_mapfunc_region(PCIDevice *pci_dev, int region_num,
+                                          pcibus_t addr, pcibus_t size,
+                                          int type)
+{
+    memory_region_add_subregion_overlap(pci_dev->bus->address_space,
+                                        addr,
+                                        pci_dev->io_regions[region_num].memory,
+                                        1);
+}
+
 void pci_register_bar_simple(PCIDevice *pci_dev, int region_num,
                              pcibus_t size,  uint8_t attr, ram_addr_t ram_addr)
 {
@@ -921,6 +943,15 @@ void pci_register_bar_simple(PCIDevice *pci_dev, int region_num,
     pci_dev->io_regions[region_num].ram_addr = ram_addr;
 }
 
+void pci_register_bar_region(PCIDevice *pci_dev, int region_num,
+                             uint8_t attr, MemoryRegion *memory)
+{
+    pci_register_bar(pci_dev, region_num, memory_region_size(memory),
+                     PCI_BASE_ADDRESS_SPACE_MEMORY | attr,
+                     pci_simple_bar_mapfunc_region);
+    pci_dev->io_regions[region_num].memory = memory;
+}
+
 static void pci_bridge_filter(PCIDevice *d, pcibus_t *addr, pcibus_t *size,
                               uint8_t type)
 {
@@ -1059,10 +1090,16 @@ static void pci_update_mappings(PCIDevice *d)
                     isa_unassign_ioport(r->addr, r->filtered_size);
                 }
             } else {
-                cpu_register_physical_memory(pci_to_cpu_addr(d->bus, r->addr),
-                                             r->filtered_size,
-                                             IO_MEM_UNASSIGNED);
-                qemu_unregister_coalesced_mmio(r->addr, r->filtered_size);
+                if (r->memory) {
+                    memory_region_del_subregion(d->bus->address_space,
+                                                r->memory);
+                } else {
+                    cpu_register_physical_memory(pci_to_cpu_addr(d->bus,
+                                                                 r->addr),
+                                                 r->filtered_size,
+                                                 IO_MEM_UNASSIGNED);
+                    qemu_unregister_coalesced_mmio(r->addr, r->filtered_size);
+                }
             }
         }
         r->addr = new_addr;