diff --git a/hw/pci.c b/hw/pci.c
index a98d6f3..49f03fb 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -744,19 +744,28 @@ static target_phys_addr_t pci_to_cpu_addr(PCIBus *bus,
 static void pci_unregister_io_regions(PCIDevice *pci_dev)
 {
     PCIIORegion *r;
-    int i;
+    PCIIOSubRegion *s;
+    int i, j;

     for(i = 0; i < PCI_NUM_REGIONS; i++) {
         r = &pci_dev->io_regions[i];
         if (!r->size || r->addr == PCI_BAR_UNMAPPED)
             continue;
-        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);
+
+        for (j = 0; j < PCI_NUM_SUBREGIONS; j++) {
+            s = &r->subregions[j];
+
+            if (!s->size) {
+                continue;
+            }
+            if (r->type == PCI_BASE_ADDRESS_SPACE_IO) {
+                isa_unassign_ioport(r->addr + s->offset, s->filtered_size);
+            } else {
+                cpu_register_physical_memory(pci_to_cpu_addr(pci_dev->bus,
+                                                         r->addr + s->offset),
+                                             s->filtered_size,
+                                             IO_MEM_UNASSIGNED);
+            }
         }
     }
 }
@@ -798,7 +807,6 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num,
     r = &pci_dev->io_regions[region_num];
     r->addr = PCI_BAR_UNMAPPED;
     r->size = size;
-    r->filtered_size = size;
     r->type = type;
     r->map_func = map_func;

@@ -817,6 +825,25 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num,
         pci_set_long(pci_dev->wmask + addr, wmask & 0xffffffff);
         pci_set_long(pci_dev->cmask + addr, 0xffffffff);
     }
+    pci_bar_map(pci_dev, region_num, 0, 0, size, -1);
+}
+
+void pci_bar_map(PCIDevice *pci_dev, int region_num, int subregion_num,
+                 pcibus_t offset, pcibus_t size, int ix)
+{
+    PCIIOSubRegion *s;
+
+    if ((unsigned int)region_num >= PCI_NUM_REGIONS ||
+        (unsigned int)subregion_num >= PCI_NUM_SUBREGIONS) {
+        return;
+    }
+
+    s = &pci_dev->io_regions[region_num].subregions[subregion_num];
+
+    s->offset = offset;
+    s->size = size;
+    s->filtered_size = size;
+    s->ix = ix;
 }

 static uint32_t pci_config_get_io_base(PCIDevice *d,
@@ -991,8 +1018,9 @@ static pcibus_t pci_bar_address(PCIDevice *d,
 static void pci_update_mappings(PCIDevice *d)
 {
     PCIIORegion *r;
-    int i;
-    pcibus_t new_addr, filtered_size;
+    PCIIOSubRegion *s;
+    int i, j;
+    pcibus_t bar_addr, new_addr, filtered_size;

     for(i = 0; i < PCI_NUM_REGIONS; i++) {
         r = &d->io_regions[i];
@@ -1001,54 +1029,82 @@ static void pci_update_mappings(PCIDevice *d)
         if (!r->size)
             continue;

-        new_addr = pci_bar_address(d, i, r->type, r->size);
+        bar_addr = pci_bar_address(d, i, r->type, r->size);

-        /* bridge filtering */
-        filtered_size = r->size;
-        if (new_addr != PCI_BAR_UNMAPPED) {
-            pci_bridge_filter(d, &new_addr, &filtered_size, r->type);
-        }
+        for (j = 0; j < PCI_NUM_SUBREGIONS; j++) {
+            s = &r->subregions[j];

-        /* This bar isn't changed */
-        if (new_addr == r->addr && filtered_size == r->filtered_size)
-            continue;
+            /* this subregion isn't registered */
+            if (!s->size) {
+                continue;
+            }
+
+            new_addr = bar_addr + s->offset;

-        /* now do the real mapping */
-        if (r->addr != PCI_BAR_UNMAPPED) {
-            if (r->type & PCI_BASE_ADDRESS_SPACE_IO) {
-                int class;
-                /* NOTE: specific hack for IDE in PC case:
-                   only one byte must be mapped. */
-                class = pci_get_word(d->config + PCI_CLASS_DEVICE);
-                if (class == 0x0101 && r->size == 4) {
-                    isa_unassign_ioport(r->addr + 2, 1);
+            /* bridge filtering */
+            filtered_size = s->size;
+            if (bar_addr != PCI_BAR_UNMAPPED) {
+                pci_bridge_filter(d, &new_addr, &filtered_size, r->type);
+            }
+
+            /* this subregion hasn't changed */
+            if (bar_addr == r->addr && new_addr == bar_addr + s->offset &&
+                filtered_size == s->filtered_size) {
+                continue;
+            }
+            /* now do the real mapping */
+            if (r->addr != PCI_BAR_UNMAPPED) {
+                if (r->type & PCI_BASE_ADDRESS_SPACE_IO) {
+                    int class;
+                    /* NOTE: specific hack for IDE in PC case:
+                       only one byte must be mapped. */
+                    class = pci_get_word(d->config + PCI_CLASS_DEVICE);
+                    if (class == 0x0101 && r->size == 4) {
+                        isa_unassign_ioport(r->addr + s->offset + 2, 1);
+                    } else {
+                        isa_unassign_ioport(r->addr + s->offset,
+                                            s->filtered_size);
+                    }
                 } else {
-                    isa_unassign_ioport(r->addr, r->filtered_size);
+                    cpu_register_physical_memory(pci_to_cpu_addr(d->bus,
+                                                                 r->addr +
+                                                                 s->offset),
+                                                 s->filtered_size,
+                                                 IO_MEM_UNASSIGNED);
+                    qemu_unregister_coalesced_mmio(r->addr + s->offset,
+                                                   s->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);
             }
-        }
-        r->addr = new_addr;
-        r->filtered_size = filtered_size;
-        if (r->addr != PCI_BAR_UNMAPPED) {
-            /*
-             * TODO: currently almost all the map funcions assumes
-             * filtered_size == size and addr & ~(size - 1) == addr.
-             * However with bridge filtering, they aren't always true.
-             * Teach them such cases, such that filtered_size < size and
-             * addr & (size - 1) != 0.
-             */
-            if (r->type & PCI_BASE_ADDRESS_SPACE_IO) {
-                r->map_func(d, i, r->addr, r->filtered_size, r->type);
-            } else {
-                r->map_func(d, i, pci_to_cpu_addr(d->bus, r->addr),
-                            r->filtered_size, r->type);
+            s->filtered_size = filtered_size;
+            if (bar_addr != PCI_BAR_UNMAPPED && new_addr != PCI_BAR_UNMAPPED &&
+                s->ix >= 0) {
+                /*
+                 * TODO: currently almost all the map funcions assumes
+                 * filtered_size == size and addr & ~(size - 1) == addr.
+                 * However with bridge filtering, they aren't always true.
+                 * Teach them such cases, such that filtered_size < size and
+                 * addr & (size - 1) != 0.
+                 */
+                if (r->type & PCI_BASE_ADDRESS_SPACE_IO) {
+                    if (r->map_func) {
+                        r->map_func(d, i, new_addr, s->filtered_size, r->type);
+                    } else {
+                        cpu_map_io(new_addr, s->ix);
+                    }
+                } else {
+                    if (r->map_func) {
+                        r->map_func(d, i, pci_to_cpu_addr(d->bus, new_addr),
+                                    s->filtered_size, r->type);
+                    } else {
+                        cpu_register_physical_memory(pci_to_cpu_addr(d->bus,
+                                                                     new_addr),
+                                                     s->filtered_size,
+                                                     s->ix);
+                    }
+                }
             }
         }
+        r->addr = bar_addr;
     }
 }

@@ -1791,11 +1847,6 @@ static uint8_t
pci_find_capability_list(PCIDevice *pdev, uint8_t cap_id,
     return next;
 }

-static void pci_map_option_rom(PCIDevice *pdev, int region_num,
pcibus_t addr, pcibus_t size, int type)
-{
-    cpu_register_physical_memory(addr, size, pdev->rom_offset);
-}
-
 /* Add an option rom for the device */
 static int pci_add_option_rom(PCIDevice *pdev)
 {
@@ -1848,8 +1899,8 @@ static int pci_add_option_rom(PCIDevice *pdev)
     load_image(path, ptr);
     qemu_free(path);

-    pci_register_bar(pdev, PCI_ROM_SLOT, size,
-                     0, pci_map_option_rom);
+    pci_register_bar(pdev, PCI_ROM_SLOT, size, 0, NULL);
+    pci_bar_map(pdev, PCI_ROM_SLOT, 0, 0, size, pdev->rom_offset);

     return 0;
 }
diff --git a/hw/pci.h b/hw/pci.h
index 1eab7e7..b518b3f 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -81,13 +81,22 @@ typedef void PCIMapIORegionFunc(PCIDevice
*pci_dev, int region_num,
                                 pcibus_t addr, pcibus_t size, int type);
 typedef int PCIUnregisterFunc(PCIDevice *pci_dev);

+typedef struct PCIIOSubRegion {
+    pcibus_t offset; /* offset to BAR start. -1 means not mapped */
+    pcibus_t size;
+    pcibus_t filtered_size;
+    int ix;
+} PCIIOSubRegion;
+
+#define PCI_NUM_SUBREGIONS 16
+
 typedef struct PCIIORegion {
     pcibus_t addr; /* current PCI mapping address. -1 means not mapped */
 #define PCI_BAR_UNMAPPED (~(pcibus_t)0)
     pcibus_t size;
-    pcibus_t filtered_size;
     uint8_t type;
     PCIMapIORegionFunc *map_func;
+    PCIIOSubRegion subregions[PCI_NUM_SUBREGIONS];
 } PCIIORegion;

