Merge remote-tracking branch 'qemu-kvm/uq/master' into staging
diff --git a/Makefile.target b/Makefile.target
index db2fa6e..03d3646 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -216,6 +216,8 @@
 obj-i386-$(CONFIG_XEN_MAPCACHE) += xen-mapcache.o
 obj-$(CONFIG_NO_XEN_MAPCACHE) += xen-mapcache-stub.o
 
+obj-i386-$(CONFIG_XEN) += xen_platform.o
+
 # Inter-VM PCI shared memory
 CONFIG_IVSHMEM =
 ifeq ($(CONFIG_KVM), y)
diff --git a/cmd.c b/cmd.c
index db2c9c4..ecca167 100644
--- a/cmd.c
+++ b/cmd.c
@@ -486,7 +486,7 @@
 			snprintf(ts, size, "%u:%02u.%02u",
 				(unsigned int) MINUTES(tv->tv_sec),
 				(unsigned int) SECONDS(tv->tv_sec),
-				(unsigned int) usec * 100);
+				(unsigned int) (usec * 100));
 			return;
 		}
 		format |= VERBOSE_FIXED_TIME;	/* fallback if hours needed */
@@ -497,9 +497,9 @@
 			(unsigned int) HOURS(tv->tv_sec),
 			(unsigned int) MINUTES(tv->tv_sec),
 			(unsigned int) SECONDS(tv->tv_sec),
-			(unsigned int) usec * 100);
+			(unsigned int) (usec * 100));
 	} else {
-		snprintf(ts, size, "0.%04u sec", (unsigned int) usec * 10000);
+		snprintf(ts, size, "0.%04u sec", (unsigned int) (usec * 10000));
 	}
 }
 
diff --git a/configure b/configure
index 3286e33..856b41e 100755
--- a/configure
+++ b/configure
@@ -1205,6 +1205,7 @@
   xc = xc_interface_open(0, 0, 0);
   xc_hvm_set_mem_type(0, 0, HVMMEM_ram_ro, 0, 0);
   xc_gnttab_open(NULL, 0);
+  xc_domain_add_to_physmap(0, 0, XENMAPSPACE_gmfn, 0, 0);
   return 0;
 }
 EOF
@@ -1223,10 +1224,14 @@
 # error HVM_MAX_VCPUS not defined
 #endif
 int main(void) {
+  struct xen_add_to_physmap xatp = {
+    .domid = 0, .space = XENMAPSPACE_gmfn, .idx = 0, .gpfn = 0,
+  };
   xs_daemon_open();
   xc_interface_open();
   xc_gnttab_open();
   xc_hvm_set_mem_type(0, 0, HVMMEM_ram_ro, 0, 0);
+  xc_memory_op(0, XENMEM_add_to_physmap, &xatp);
   return 0;
 }
 EOF
@@ -1235,7 +1240,29 @@
     xen_ctrl_version=400
     xen=yes
 
-  # Xen 3.3.0, 3.4.0
+  # Xen 3.4.0
+  elif (
+      cat > $TMPC <<EOF
+#include <xenctrl.h>
+#include <xs.h>
+int main(void) {
+  struct xen_add_to_physmap xatp = {
+    .domid = 0, .space = XENMAPSPACE_gmfn, .idx = 0, .gpfn = 0,
+  };
+  xs_daemon_open();
+  xc_interface_open();
+  xc_gnttab_open();
+  xc_hvm_set_mem_type(0, 0, HVMMEM_ram_ro, 0, 0);
+  xc_memory_op(0, XENMEM_add_to_physmap, &xatp);
+  return 0;
+}
+EOF
+      compile_prog "" "$xen_libs"
+    ) ; then
+    xen_ctrl_version=340
+    xen=yes
+
+  # Xen 3.3.0
   elif (
       cat > $TMPC <<EOF
 #include <xenctrl.h>
diff --git a/cpu-common.h b/cpu-common.h
index 9f59172..b027e43 100644
--- a/cpu-common.h
+++ b/cpu-common.h
@@ -65,6 +65,7 @@
 void qemu_ram_remap(ram_addr_t addr, ram_addr_t length);
 /* This should only be used for ram local to a device.  */
 void *qemu_get_ram_ptr(ram_addr_t addr);
+void *qemu_ram_ptr_length(target_phys_addr_t addr, target_phys_addr_t *size);
 /* Same but slower, to use for migration, where the order of
  * RAMBlocks must not change. */
 void *qemu_safe_ram_ptr(ram_addr_t addr);
diff --git a/cpus.c b/cpus.c
index 17e96b5..0699f37 100644
--- a/cpus.c
+++ b/cpus.c
@@ -297,7 +297,7 @@
 
     /* EAGAIN is fine, a read must be pending.  */
     if (ret < 0 && errno != EAGAIN) {
-        fprintf(stderr, "qemu_event_increment: write() filed: %s\n",
+        fprintf(stderr, "qemu_event_increment: write() failed: %s\n",
                 strerror(errno));
         exit (1);
     }
diff --git a/exec.c b/exec.c
index 09928a3..b03b5be 100644
--- a/exec.c
+++ b/exec.c
@@ -53,6 +53,7 @@
 #endif
 #else /* !CONFIG_USER_ONLY */
 #include "xen-mapcache.h"
+#include "trace.h"
 #endif
 
 //#define DEBUG_TB_INVALIDATE
@@ -1752,14 +1753,21 @@
     return 0;
 }
 
+struct last_map {
+    target_phys_addr_t start_addr;
+    ram_addr_t size;
+    ram_addr_t phys_offset;
+};
+
 /* The l1_phys_map provides the upper P_L1_BITs of the guest physical
  * address.  Each intermediate table provides the next L2_BITs of guest
  * physical address space.  The number of levels vary based on host and
  * guest configuration, making it efficient to build the final guest
  * physical address by seeding the L1 offset and shifting and adding in
  * each L2 offset as we recurse through them. */
-static void phys_page_for_each_1(CPUPhysMemoryClient *client,
-                                 int level, void **lp, target_phys_addr_t addr)
+static void phys_page_for_each_1(CPUPhysMemoryClient *client, int level,
+                                 void **lp, target_phys_addr_t addr,
+                                 struct last_map *map)
 {
     int i;
 
@@ -1771,15 +1779,29 @@
         addr <<= L2_BITS + TARGET_PAGE_BITS;
         for (i = 0; i < L2_SIZE; ++i) {
             if (pd[i].phys_offset != IO_MEM_UNASSIGNED) {
-                client->set_memory(client, addr | i << TARGET_PAGE_BITS,
-                                   TARGET_PAGE_SIZE, pd[i].phys_offset, false);
+                target_phys_addr_t start_addr = addr | i << TARGET_PAGE_BITS;
+
+                if (map->size &&
+                    start_addr == map->start_addr + map->size &&
+                    pd[i].phys_offset == map->phys_offset + map->size) {
+
+                    map->size += TARGET_PAGE_SIZE;
+                    continue;
+                } else if (map->size) {
+                    client->set_memory(client, map->start_addr,
+                                       map->size, map->phys_offset, false);
+                }
+
+                map->start_addr = start_addr;
+                map->size = TARGET_PAGE_SIZE;
+                map->phys_offset = pd[i].phys_offset;
             }
         }
     } else {
         void **pp = *lp;
         for (i = 0; i < L2_SIZE; ++i) {
             phys_page_for_each_1(client, level - 1, pp + i,
-                                 (addr << L2_BITS) | i);
+                                 (addr << L2_BITS) | i, map);
         }
     }
 }
@@ -1787,9 +1809,15 @@
 static void phys_page_for_each(CPUPhysMemoryClient *client)
 {
     int i;
+    struct last_map map = { };
+
     for (i = 0; i < P_L1_SIZE; ++i) {
         phys_page_for_each_1(client, P_L1_SHIFT / L2_BITS - 1,
-                             l1_phys_map + i, i);
+                             l1_phys_map + i, i, &map);
+    }
+    if (map.size) {
+        client->set_memory(client, map.start_addr, map.size, map.phys_offset,
+                           false);
     }
 }
 
@@ -3084,11 +3112,12 @@
             if (xen_mapcache_enabled()) {
                 /* We need to check if the requested address is in the RAM
                  * because we don't want to map the entire memory in QEMU.
+                 * In that case just map until the end of the page.
                  */
                 if (block->offset == 0) {
-                    return qemu_map_cache(addr, 0, 1);
+                    return qemu_map_cache(addr, 0, 0);
                 } else if (block->host == NULL) {
-                    block->host = xen_map_block(block->offset, block->length);
+                    block->host = qemu_map_cache(block->offset, block->length, 1);
                 }
             }
             return block->host + (addr - block->offset);
@@ -3113,11 +3142,12 @@
             if (xen_mapcache_enabled()) {
                 /* We need to check if the requested address is in the RAM
                  * because we don't want to map the entire memory in QEMU.
+                 * In that case just map until the end of the page.
                  */
                 if (block->offset == 0) {
-                    return qemu_map_cache(addr, 0, 1);
+                    return qemu_map_cache(addr, 0, 0);
                 } else if (block->host == NULL) {
-                    block->host = xen_map_block(block->offset, block->length);
+                    block->host = qemu_map_cache(block->offset, block->length, 1);
                 }
             }
             return block->host + (addr - block->offset);
@@ -3130,32 +3160,46 @@
     return NULL;
 }
 
-void qemu_put_ram_ptr(void *addr)
+/* Return a host pointer to guest's ram. Similar to qemu_get_ram_ptr
+ * but takes a size argument */
+void *qemu_ram_ptr_length(target_phys_addr_t addr, target_phys_addr_t *size)
 {
-    trace_qemu_put_ram_ptr(addr);
-
-    if (xen_mapcache_enabled()) {
+    if (xen_mapcache_enabled())
+        return qemu_map_cache(addr, *size, 1);
+    else {
         RAMBlock *block;
 
         QLIST_FOREACH(block, &ram_list.blocks, next) {
-            if (addr == block->host) {
-                break;
+            if (addr - block->offset < block->length) {
+                if (addr - block->offset + *size > block->length)
+                    *size = block->length - addr + block->offset;
+                return block->host + (addr - block->offset);
             }
         }
-        if (block && block->host) {
-            xen_unmap_block(block->host, block->length);
-            block->host = NULL;
-        } else {
-            qemu_map_cache_unlock(addr);
-        }
+
+        fprintf(stderr, "Bad ram offset %" PRIx64 "\n", (uint64_t)addr);
+        abort();
+
+        *size = 0;
+        return NULL;
     }
 }
 
+void qemu_put_ram_ptr(void *addr)
+{
+    trace_qemu_put_ram_ptr(addr);
+}
+
 int qemu_ram_addr_from_host(void *ptr, ram_addr_t *ram_addr)
 {
     RAMBlock *block;
     uint8_t *host = ptr;
 
+    if (xen_mapcache_enabled()) {
+        *ram_addr = qemu_ram_addr_from_mapcache(ptr);
+        return 0;
+    }
+
     QLIST_FOREACH(block, &ram_list.blocks, next) {
         /* This case append when the block is not mapped. */
         if (block->host == NULL) {
@@ -3167,11 +3211,6 @@
         }
     }
 
-    if (xen_mapcache_enabled()) {
-        *ram_addr = qemu_ram_addr_from_mapcache(ptr);
-        return 0;
-    }
-
     return -1;
 }
 
@@ -4003,14 +4042,12 @@
                               int is_write)
 {
     target_phys_addr_t len = *plen;
-    target_phys_addr_t done = 0;
+    target_phys_addr_t todo = 0;
     int l;
-    uint8_t *ret = NULL;
-    uint8_t *ptr;
     target_phys_addr_t page;
     unsigned long pd;
     PhysPageDesc *p;
-    unsigned long addr1;
+    target_phys_addr_t addr1 = addr;
 
     while (len > 0) {
         page = addr & TARGET_PAGE_MASK;
@@ -4025,7 +4062,7 @@
         }
 
         if ((pd & ~TARGET_PAGE_MASK) != IO_MEM_RAM) {
-            if (done || bounce.buffer) {
+            if (todo || bounce.buffer) {
                 break;
             }
             bounce.buffer = qemu_memalign(TARGET_PAGE_SIZE, TARGET_PAGE_SIZE);
@@ -4034,23 +4071,17 @@
             if (!is_write) {
                 cpu_physical_memory_read(addr, bounce.buffer, l);
             }
-            ptr = bounce.buffer;
-        } else {
-            addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK);
-            ptr = qemu_get_ram_ptr(addr1);
-        }
-        if (!done) {
-            ret = ptr;
-        } else if (ret + done != ptr) {
-            break;
+
+            *plen = l;
+            return bounce.buffer;
         }
 
         len -= l;
         addr += l;
-        done += l;
+        todo += l;
     }
-    *plen = done;
-    return ret;
+    *plen = todo;
+    return qemu_ram_ptr_length(addr1, plen);
 }
 
 /* Unmaps a memory region previously mapped by cpu_physical_memory_map().
@@ -4080,13 +4111,7 @@
             }
         }
         if (xen_mapcache_enabled()) {
-            uint8_t *buffer1 = buffer;
-            uint8_t *end_buffer = buffer + len;
-
-            while (buffer1 < end_buffer) {
-                qemu_put_ram_ptr(buffer1);
-                buffer1 += TARGET_PAGE_SIZE;
-            }
+            qemu_invalidate_entry(buffer);
         }
         return;
     }
diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c
index a2b6acc..f235236 100644
--- a/hw/9pfs/virtio-9p-device.c
+++ b/hw/9pfs/virtio-9p-device.c
@@ -142,11 +142,7 @@
 
     vdev = virtio_9p_init(&pci_dev->qdev, &proxy->fsconf);
     vdev->nvectors = proxy->nvectors;
-    virtio_init_pci(proxy, vdev,
-                    PCI_VENDOR_ID_REDHAT_QUMRANET,
-                    0x1009,
-                    0x2,
-                    0x00);
+    virtio_init_pci(proxy, vdev);
     /* make the actual value visible */
     proxy->nvectors = vdev->nvectors;
     return 0;
@@ -156,6 +152,10 @@
     .qdev.name = "virtio-9p-pci",
     .qdev.size = sizeof(VirtIOPCIProxy),
     .init      = virtio_9p_init_pci,
+    .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET,
+    .device_id = 0x1009,
+    .revision  = VIRTIO_PCI_ABI_VERSION,
+    .class_id  = 0x2,
     .qdev.props = (Property[]) {
         DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
         DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features),
diff --git a/hw/ac97.c b/hw/ac97.c
index a946c1a..0b59896 100644
--- a/hw/ac97.c
+++ b/hw/ac97.c
@@ -1277,9 +1277,6 @@
     AC97LinkState *s = DO_UPCAST (AC97LinkState, dev, dev);
     uint8_t *c = s->dev.config;
 
-    pci_config_set_vendor_id (c, PCI_VENDOR_ID_INTEL); /* ro */
-    pci_config_set_device_id (c, PCI_DEVICE_ID_INTEL_82801AA_5); /* ro */
-
     /* TODO: no need to override */
     c[PCI_COMMAND] = 0x00;      /* pcicmd pci command rw, ro */
     c[PCI_COMMAND + 1] = 0x00;
@@ -1288,9 +1285,7 @@
     c[PCI_STATUS] = PCI_STATUS_FAST_BACK;      /* pcists pci status rwc, ro */
     c[PCI_STATUS + 1] = PCI_STATUS_DEVSEL_MEDIUM >> 8;
 
-    c[PCI_REVISION_ID] = 0x01;      /* rid revision ro */
     c[PCI_CLASS_PROG] = 0x00;      /* pi programming interface ro */
-    pci_config_set_class (c, PCI_CLASS_MULTIMEDIA_AUDIO); /* ro */
 
     /* TODO set when bar is registered. no need to override. */
     /* nabmar native audio mixer base address rw */
@@ -1337,6 +1332,10 @@
     .qdev.size    = sizeof (AC97LinkState),
     .qdev.vmsd    = &vmstate_ac97,
     .init         = ac97_initfn,
+    .vendor_id    = PCI_VENDOR_ID_INTEL,
+    .device_id    = PCI_DEVICE_ID_INTEL_82801AA_5,
+    .revision     = 0x01,
+    .class_id     = PCI_CLASS_MULTIMEDIA_AUDIO,
 };
 
 static void ac97_register (void)
diff --git a/hw/acpi_piix4.c b/hw/acpi_piix4.c
index 6c908ff..350558b 100644
--- a/hw/acpi_piix4.c
+++ b/hw/acpi_piix4.c
@@ -317,13 +317,9 @@
     uint8_t *pci_conf;
 
     pci_conf = s->dev.config;
-    pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL);
-    pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82371AB_3);
     pci_conf[0x06] = 0x80;
     pci_conf[0x07] = 0x02;
-    pci_conf[0x08] = 0x03; // revision number
     pci_conf[0x09] = 0x00;
-    pci_config_set_class(pci_conf, PCI_CLASS_BRIDGE_OTHER);
     pci_conf[0x3d] = 0x01; // interrupt pin 1
 
     pci_conf[0x40] = 0x01; /* PM io base read only bit */
@@ -394,6 +390,10 @@
     .no_hotplug         = 1,
     .init               = piix4_pm_initfn,
     .config_write       = pm_write_config,
+    .vendor_id          = PCI_VENDOR_ID_INTEL,
+    .device_id          = PCI_DEVICE_ID_INTEL_82371AB_3,
+    .revision           = 0x03,
+    .class_id           = PCI_CLASS_BRIDGE_OTHER,
     .qdev.props         = (Property[]) {
         DEFINE_PROP_UINT32("smb_io_base", PIIX4PMState, smb_io_base, 0),
         DEFINE_PROP_END_OF_LIST(),
diff --git a/hw/apb_pci.c b/hw/apb_pci.c
index 84e9af7..974c87a 100644
--- a/hw/apb_pci.c
+++ b/hw/apb_pci.c
@@ -304,9 +304,6 @@
         return rc;
     }
 
-    pci_config_set_vendor_id(dev->config, PCI_VENDOR_ID_SUN);
-    pci_config_set_device_id(dev->config, PCI_DEVICE_ID_SUN_SIMBA);
-
     /*
      * command register:
      * According to PCI bridge spec, after reset
@@ -321,7 +318,6 @@
     pci_set_word(dev->config + PCI_STATUS,
                  PCI_STATUS_FAST_BACK | PCI_STATUS_66MHZ |
                  PCI_STATUS_DEVSEL_MEDIUM);
-    pci_set_byte(dev->config + PCI_REVISION_ID, 0x11);
     return 0;
 }
 
@@ -436,14 +432,11 @@
 
 static int pbm_pci_host_init(PCIDevice *d)
 {
-    pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_SUN);
-    pci_config_set_device_id(d->config, PCI_DEVICE_ID_SUN_SABRE);
     pci_set_word(d->config + PCI_COMMAND,
                  PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
     pci_set_word(d->config + PCI_STATUS,
                  PCI_STATUS_FAST_BACK | PCI_STATUS_66MHZ |
                  PCI_STATUS_DEVSEL_MEDIUM);
-    pci_config_set_class(d->config, PCI_CLASS_BRIDGE_HOST);
     return 0;
 }
 
@@ -451,6 +444,9 @@
     .qdev.name = "pbm",
     .qdev.size = sizeof(PCIDevice),
     .init      = pbm_pci_host_init,
+    .vendor_id = PCI_VENDOR_ID_SUN,
+    .device_id = PCI_DEVICE_ID_SUN_SABRE,
+    .class_id  = PCI_CLASS_BRIDGE_HOST,
     .is_bridge = 1,
 };
 
@@ -468,6 +464,9 @@
     .qdev.reset = pci_bridge_reset,
     .init = apb_pci_bridge_initfn,
     .exit = pci_bridge_exitfn,
+    .vendor_id = PCI_VENDOR_ID_SUN,
+    .device_id = PCI_DEVICE_ID_SUN_SIMBA,
+    .revision = 0x11,
     .config_write = pci_bridge_write_config,
     .is_bridge = 1,
 };
diff --git a/hw/bonito.c b/hw/bonito.c
index 65a4a63..e8c57a3 100644
--- a/hw/bonito.c
+++ b/hw/bonito.c
@@ -691,11 +691,7 @@
     PCIBonitoState *s = DO_UPCAST(PCIBonitoState, dev, dev);
 
     /* Bonito North Bridge, built on FPGA, VENDOR_ID/DEVICE_ID are "undefined" */
-    pci_config_set_vendor_id(dev->config, 0xdf53);
-    pci_config_set_device_id(dev->config, 0x00d5);
-    pci_config_set_class(dev->config, PCI_CLASS_BRIDGE_HOST);
     pci_config_set_prog_interface(dev->config, 0x00);
-    pci_config_set_revision(dev->config, 0x01);
 
     /* set the north bridge register mapping */
     s->bonito_reg_handle = cpu_register_io_memory(bonito_read, bonito_write, s,
@@ -796,6 +792,11 @@
     .qdev.vmsd    = &vmstate_bonito,
     .qdev.no_user = 1,
     .init         = bonito_initfn,
+    /*Bonito North Bridge, built on FPGA, VENDOR_ID/DEVICE_ID are "undefined"*/
+    .vendor_id    = 0xdf53,
+    .device_id    = 0x00d5,
+    .revision     = 0x01,
+    .class_id     = PCI_CLASS_BRIDGE_HOST,
 };
 
 static SysBusDeviceInfo bonito_pcihost_info = {
diff --git a/hw/cirrus_vga.c b/hw/cirrus_vga.c
index 722cac7..f39d1f8 100644
--- a/hw/cirrus_vga.c
+++ b/hw/cirrus_vga.c
@@ -3088,8 +3088,11 @@
     CirrusVGAState *s = &pvs->cirrus_vga;
 
     pci_default_write_config(d, address, val, len);
-    if (s->vga.map_addr && d->io_regions[0].addr == PCI_BAR_UNMAPPED)
+    if (s->vga.map_addr && d->io_regions[0].addr == PCI_BAR_UNMAPPED) {
         s->vga.map_addr = 0;
+        s->vga.lfb_addr = 0;
+        s->vga.lfb_end = 0;
+    }
     cirrus_update_memory_access(s);
 }
 
@@ -3097,8 +3100,8 @@
 {
      PCICirrusVGAState *d = DO_UPCAST(PCICirrusVGAState, dev, dev);
      CirrusVGAState *s = &d->cirrus_vga;
-     uint8_t *pci_conf = d->dev.config;
-     int device_id = CIRRUS_ID_CLGD5446;
+     PCIDeviceInfo *info = DO_UPCAST(PCIDeviceInfo, qdev, dev->qdev.info);
+     int16_t device_id = info->device_id;
 
      /* setup VGA */
      vga_common_init(&s->vga, VGA_RAM_SIZE);
@@ -3108,9 +3111,6 @@
                                       &s->vga);
 
      /* setup PCI */
-     pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_CIRRUS);
-     pci_config_set_device_id(pci_conf, device_id);
-     pci_config_set_class(pci_conf, PCI_CLASS_DISPLAY_VGA);
 
      /* setup memory space */
      /* memory #0 LFB */
@@ -3139,6 +3139,9 @@
     .init         = pci_cirrus_vga_initfn,
     .romfile      = VGABIOS_CIRRUS_FILENAME,
     .config_write = pci_cirrus_write_config,
+    .vendor_id    = PCI_VENDOR_ID_CIRRUS,
+    .device_id    = CIRRUS_ID_CLGD5446,
+    .class_id     = PCI_CLASS_DISPLAY_VGA,
 };
 
 static void cirrus_vga_register(void)
diff --git a/hw/dec_pci.c b/hw/dec_pci.c
index bf88f2a..a35f382 100644
--- a/hw/dec_pci.c
+++ b/hw/dec_pci.c
@@ -50,28 +50,16 @@
     return irq_num;
 }
 
-static int dec_21154_initfn(PCIDevice *dev)
-{
-    int rc;
-
-    rc = pci_bridge_initfn(dev);
-    if (rc < 0) {
-        return rc;
-    }
-
-    pci_config_set_vendor_id(dev->config, PCI_VENDOR_ID_DEC);
-    pci_config_set_device_id(dev->config, PCI_DEVICE_ID_DEC_21154);
-    return 0;
-}
-
 static PCIDeviceInfo dec_21154_pci_bridge_info = {
     .qdev.name = "dec-21154-p2p-bridge",
     .qdev.desc = "DEC 21154 PCI-PCI bridge",
     .qdev.size = sizeof(PCIBridge),
     .qdev.vmsd = &vmstate_pci_device,
     .qdev.reset = pci_bridge_reset,
-    .init = dec_21154_initfn,
+    .init = pci_bridge_initfn,
     .exit = pci_bridge_exitfn,
+    .vendor_id = PCI_VENDOR_ID_DEC,
+    .device_id = PCI_DEVICE_ID_DEC_21154,
     .config_write = pci_bridge_write_config,
     .is_bridge = 1,
 };
@@ -108,10 +96,6 @@
 static int dec_21154_pci_host_init(PCIDevice *d)
 {
     /* PCI2PCI bridge same values as PearPC - check this */
-    pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_DEC);
-    pci_config_set_device_id(d->config, PCI_DEVICE_ID_DEC_21154);
-    pci_set_byte(d->config + PCI_REVISION_ID, 0x02);
-    pci_config_set_class(d->config, PCI_CLASS_BRIDGE_PCI);
     return 0;
 }
 
@@ -119,6 +103,10 @@
     .qdev.name = "dec-21154",
     .qdev.size = sizeof(PCIDevice),
     .init      = dec_21154_pci_host_init,
+    .vendor_id = PCI_VENDOR_ID_DEC,
+    .device_id = PCI_DEVICE_ID_DEC_21154,
+    .revision = 0x02,
+    .class_id = PCI_CLASS_BRIDGE_PCI,
     .is_bridge  = 1,
 };
 
diff --git a/hw/e1000.c b/hw/e1000.c
index f160bfc..96d84f9 100644
--- a/hw/e1000.c
+++ b/hw/e1000.c
@@ -1164,12 +1164,8 @@
 
     pci_conf = d->dev.config;
 
-    pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL);
-    pci_config_set_device_id(pci_conf, E1000_DEVID);
     /* TODO: we have no capabilities, so why is this bit set? */
     pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_CAP_LIST);
-    pci_conf[PCI_REVISION_ID] = 0x03;
-    pci_config_set_class(pci_conf, PCI_CLASS_NETWORK_ETHERNET);
     /* TODO: RST# value should be 0, PCI spec 6.2.4 */
     pci_conf[PCI_CACHE_LINE_SIZE] = 0x10;
 
@@ -1221,6 +1217,10 @@
     .init       = pci_e1000_init,
     .exit       = pci_e1000_uninit,
     .romfile    = "pxe-e1000.rom",
+    .vendor_id  = PCI_VENDOR_ID_INTEL,
+    .device_id  = E1000_DEVID,
+    .revision   = 0x03,
+    .class_id   = PCI_CLASS_NETWORK_ETHERNET,
     .qdev.props = (Property[]) {
         DEFINE_NIC_PROPERTIES(E1000State, conf),
         DEFINE_PROP_END_OF_LIST(),
diff --git a/hw/eepro100.c b/hw/eepro100.c
index 9f16efd..9b6f4a5 100644
--- a/hw/eepro100.c
+++ b/hw/eepro100.c
@@ -129,8 +129,6 @@
 typedef struct {
     PCIDeviceInfo pci;
     uint32_t device;
-    uint16_t device_id;
-    uint8_t revision;
     uint8_t stats_size;
     bool has_extended_tcb_support;
     bool power_management;
@@ -526,16 +524,9 @@
 
     TRACE(OTHER, logout("%p\n", s));
 
-    /* PCI Vendor ID */
-    pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL);
-    /* PCI Device ID */
-    pci_config_set_device_id(pci_conf, e100_device->device_id);
     /* PCI Status */
     pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_DEVSEL_MEDIUM |
                                         PCI_STATUS_FAST_BACK);
-    /* PCI Revision ID */
-    pci_config_set_revision(pci_conf, e100_device->revision);
-    pci_config_set_class(pci_conf, PCI_CLASS_NETWORK_ETHERNET);
     /* PCI Latency Timer */
     pci_set_byte(pci_conf + PCI_LATENCY_TIMER, 0x20);   /* latency timer = 32 clocks */
     /* Capability Pointer is set by PCI framework. */
@@ -563,12 +554,7 @@
     case i82559ER:
     case i82562:
     case i82801:
-        break;
     case i82559C:
-#if EEPROM_SIZE > 0
-        pci_set_word(pci_conf + PCI_SUBSYSTEM_VENDOR_ID, PCI_VENDOR_ID_INTEL);
-        pci_set_word(pci_conf + PCI_SUBSYSTEM_ID, 0x0040);
-#endif
         break;
     default:
         logout("Device %X is undefined!\n", device);
@@ -2040,9 +2026,9 @@
         .pci.qdev.desc = "Intel i82550 Ethernet",
         .device = i82550,
         /* TODO: check device id. */
-        .device_id = PCI_DEVICE_ID_INTEL_82551IT,
+        .pci.device_id = PCI_DEVICE_ID_INTEL_82551IT,
         /* Revision ID: 0x0c, 0x0d, 0x0e. */
-        .revision = 0x0e,
+        .pci.revision = 0x0e,
         /* TODO: check size of statistical counters. */
         .stats_size = 80,
         /* TODO: check extended tcb support. */
@@ -2052,9 +2038,9 @@
         .pci.qdev.name = "i82551",
         .pci.qdev.desc = "Intel i82551 Ethernet",
         .device = i82551,
-        .device_id = PCI_DEVICE_ID_INTEL_82551IT,
+        .pci.device_id = PCI_DEVICE_ID_INTEL_82551IT,
         /* Revision ID: 0x0f, 0x10. */
-        .revision = 0x0f,
+        .pci.revision = 0x0f,
         /* TODO: check size of statistical counters. */
         .stats_size = 80,
         .has_extended_tcb_support = true,
@@ -2063,29 +2049,29 @@
         .pci.qdev.name = "i82557a",
         .pci.qdev.desc = "Intel i82557A Ethernet",
         .device = i82557A,
-        .device_id = PCI_DEVICE_ID_INTEL_82557,
-        .revision = 0x01,
+        .pci.device_id = PCI_DEVICE_ID_INTEL_82557,
+        .pci.revision = 0x01,
         .power_management = false,
     },{
         .pci.qdev.name = "i82557b",
         .pci.qdev.desc = "Intel i82557B Ethernet",
         .device = i82557B,
-        .device_id = PCI_DEVICE_ID_INTEL_82557,
-        .revision = 0x02,
+        .pci.device_id = PCI_DEVICE_ID_INTEL_82557,
+        .pci.revision = 0x02,
         .power_management = false,
     },{
         .pci.qdev.name = "i82557c",
         .pci.qdev.desc = "Intel i82557C Ethernet",
         .device = i82557C,
-        .device_id = PCI_DEVICE_ID_INTEL_82557,
-        .revision = 0x03,
+        .pci.device_id = PCI_DEVICE_ID_INTEL_82557,
+        .pci.revision = 0x03,
         .power_management = false,
     },{
         .pci.qdev.name = "i82558a",
         .pci.qdev.desc = "Intel i82558A Ethernet",
         .device = i82558A,
-        .device_id = PCI_DEVICE_ID_INTEL_82557,
-        .revision = 0x04,
+        .pci.device_id = PCI_DEVICE_ID_INTEL_82557,
+        .pci.revision = 0x04,
         .stats_size = 76,
         .has_extended_tcb_support = true,
         .power_management = true,
@@ -2093,8 +2079,8 @@
         .pci.qdev.name = "i82558b",
         .pci.qdev.desc = "Intel i82558B Ethernet",
         .device = i82558B,
-        .device_id = PCI_DEVICE_ID_INTEL_82557,
-        .revision = 0x05,
+        .pci.device_id = PCI_DEVICE_ID_INTEL_82557,
+        .pci.revision = 0x05,
         .stats_size = 76,
         .has_extended_tcb_support = true,
         .power_management = true,
@@ -2102,8 +2088,8 @@
         .pci.qdev.name = "i82559a",
         .pci.qdev.desc = "Intel i82559A Ethernet",
         .device = i82559A,
-        .device_id = PCI_DEVICE_ID_INTEL_82557,
-        .revision = 0x06,
+        .pci.device_id = PCI_DEVICE_ID_INTEL_82557,
+        .pci.revision = 0x06,
         .stats_size = 80,
         .has_extended_tcb_support = true,
         .power_management = true,
@@ -2111,8 +2097,8 @@
         .pci.qdev.name = "i82559b",
         .pci.qdev.desc = "Intel i82559B Ethernet",
         .device = i82559B,
-        .device_id = PCI_DEVICE_ID_INTEL_82557,
-        .revision = 0x07,
+        .pci.device_id = PCI_DEVICE_ID_INTEL_82557,
+        .pci.revision = 0x07,
         .stats_size = 80,
         .has_extended_tcb_support = true,
         .power_management = true,
@@ -2120,12 +2106,16 @@
         .pci.qdev.name = "i82559c",
         .pci.qdev.desc = "Intel i82559C Ethernet",
         .device = i82559C,
-        .device_id = PCI_DEVICE_ID_INTEL_82557,
+        .pci.device_id = PCI_DEVICE_ID_INTEL_82557,
 #if 0
-        .revision = 0x08,
+        .pci.revision = 0x08,
 #endif
         /* TODO: Windows wants revision id 0x0c. */
-        .revision = 0x0c,
+        .pci.revision = 0x0c,
+#if EEPROM_SIZE > 0
+        .pci.subsystem_vendor_id = PCI_VENDOR_ID_INTEL,
+        .pci.subsystem_id = 0x0040,
+#endif
         .stats_size = 80,
         .has_extended_tcb_support = true,
         .power_management = true,
@@ -2133,8 +2123,8 @@
         .pci.qdev.name = "i82559er",
         .pci.qdev.desc = "Intel i82559ER Ethernet",
         .device = i82559ER,
-        .device_id = PCI_DEVICE_ID_INTEL_82551IT,
-        .revision = 0x09,
+        .pci.device_id = PCI_DEVICE_ID_INTEL_82551IT,
+        .pci.revision = 0x09,
         .stats_size = 80,
         .has_extended_tcb_support = true,
         .power_management = true,
@@ -2143,9 +2133,9 @@
         .pci.qdev.desc = "Intel i82562 Ethernet",
         .device = i82562,
         /* TODO: check device id. */
-        .device_id = PCI_DEVICE_ID_INTEL_82551IT,
+        .pci.device_id = PCI_DEVICE_ID_INTEL_82551IT,
         /* TODO: wrong revision id. */
-        .revision = 0x0e,
+        .pci.revision = 0x0e,
         .stats_size = 80,
         .has_extended_tcb_support = true,
         .power_management = true,
@@ -2154,8 +2144,8 @@
         .pci.qdev.name = "i82801",
         .pci.qdev.desc = "Intel i82801 Ethernet",
         .device = i82801,
-        .device_id = 0x2449,
-        .revision = 0x03,
+        .pci.device_id = 0x2449,
+        .pci.revision = 0x03,
         .stats_size = 80,
         .has_extended_tcb_support = true,
         .power_management = true,
@@ -2174,6 +2164,8 @@
         PCIDeviceInfo *pci_dev = &e100_devices[i].pci;
         /* We use the same rom file for all device ids.
            QEMU fixes the device id during rom load. */
+        pci_dev->vendor_id = PCI_VENDOR_ID_INTEL;
+        pci_dev->class_id = PCI_CLASS_NETWORK_ETHERNET;
         pci_dev->romfile = "pxe-eepro100.rom";
         pci_dev->init = e100_nic_init;
         pci_dev->exit = pci_nic_uninit;
diff --git a/hw/es1370.c b/hw/es1370.c
index 40cb48c..1ed62b7 100644
--- a/hw/es1370.c
+++ b/hw/es1370.c
@@ -998,21 +998,9 @@
     ES1370State *s = DO_UPCAST (ES1370State, dev, dev);
     uint8_t *c = s->dev.config;
 
-    pci_config_set_vendor_id (c, PCI_VENDOR_ID_ENSONIQ);
-    pci_config_set_device_id (c, PCI_DEVICE_ID_ENSONIQ_ES1370);
     c[PCI_STATUS + 1] = PCI_STATUS_DEVSEL_SLOW >> 8;
-    pci_config_set_class (c, PCI_CLASS_MULTIMEDIA_AUDIO);
 
-#if 1
-    c[PCI_SUBSYSTEM_VENDOR_ID] = 0x42;
-    c[PCI_SUBSYSTEM_VENDOR_ID + 1] = 0x49;
-    c[PCI_SUBSYSTEM_ID] = 0x4c;
-    c[PCI_SUBSYSTEM_ID + 1] = 0x4c;
-#else
-    c[PCI_SUBSYSTEM_VENDOR_ID] = 0x74;
-    c[PCI_SUBSYSTEM_VENDOR_ID + 1] = 0x12;
-    c[PCI_SUBSYSTEM_ID] = 0x71;
-    c[PCI_SUBSYSTEM_ID + 1] = 0x13;
+#if 0
     c[PCI_CAPABILITY_LIST] = 0xdc;
     c[PCI_INTERRUPT_LINE] = 10;
     c[0xdc] = 0x00;
@@ -1043,6 +1031,16 @@
     .qdev.size    = sizeof (ES1370State),
     .qdev.vmsd    = &vmstate_es1370,
     .init         = es1370_initfn,
+    .vendor_id    = PCI_VENDOR_ID_ENSONIQ,
+    .device_id    = PCI_DEVICE_ID_ENSONIQ_ES1370,
+    .class_id     = PCI_CLASS_MULTIMEDIA_AUDIO,
+#if 1
+    .subsystem_vendor_id = 0x4942,
+    .subsystem_id = 0x4c4c,
+#else
+    .subsystem_vendor_id = 0x1274,
+    .subsystem_id = 0x1371,
+#endif
 };
 
 static void es1370_register (void)
diff --git a/hw/grackle_pci.c b/hw/grackle_pci.c
index d35701f..cee07e0 100644
--- a/hw/grackle_pci.c
+++ b/hw/grackle_pci.c
@@ -104,11 +104,7 @@
 
 static int grackle_pci_host_init(PCIDevice *d)
 {
-    pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_MOTOROLA);
-    pci_config_set_device_id(d->config, PCI_DEVICE_ID_MOTOROLA_MPC106);
-    d->config[0x08] = 0x00; // revision
     d->config[0x09] = 0x01;
-    pci_config_set_class(d->config, PCI_CLASS_BRIDGE_HOST);
     return 0;
 }
 
@@ -116,6 +112,10 @@
     .qdev.name = "grackle",
     .qdev.size = sizeof(PCIDevice),
     .init      = grackle_pci_host_init,
+    .vendor_id = PCI_VENDOR_ID_MOTOROLA,
+    .device_id = PCI_DEVICE_ID_MOTOROLA_MPC106,
+    .revision  = 0x00,
+    .class_id  = PCI_CLASS_BRIDGE_HOST,
 };
 
 static void grackle_register_devices(void)
diff --git a/hw/gt64xxx.c b/hw/gt64xxx.c
index c66188f..8e1f6a0 100644
--- a/hw/gt64xxx.c
+++ b/hw/gt64xxx.c
@@ -1118,14 +1118,10 @@
 static int gt64120_pci_init(PCIDevice *d)
 {
     /* FIXME: Malta specific hw assumptions ahead */
-    pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_MARVELL);
-    pci_config_set_device_id(d->config, PCI_DEVICE_ID_MARVELL_GT6412X);
     pci_set_word(d->config + PCI_COMMAND, 0);
     pci_set_word(d->config + PCI_STATUS,
                  PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MEDIUM);
-    pci_set_byte(d->config + PCI_CLASS_REVISION, 0x10);
     pci_config_set_prog_interface(d->config, 0);
-    pci_config_set_class(d->config, PCI_CLASS_BRIDGE_HOST);
     pci_set_long(d->config + PCI_BASE_ADDRESS_0, 0x00000008);
     pci_set_long(d->config + PCI_BASE_ADDRESS_1, 0x01000008);
     pci_set_long(d->config + PCI_BASE_ADDRESS_2, 0x1c000000);
@@ -1141,6 +1137,10 @@
     .qdev.name = "gt64120_pci",
     .qdev.size = sizeof(PCIDevice),
     .init      = gt64120_pci_init,
+    .vendor_id = PCI_VENDOR_ID_MARVELL,
+    .device_id = PCI_DEVICE_ID_MARVELL_GT6412X,
+    .revision  = 0x10,
+    .class_id  = PCI_CLASS_BRIDGE_HOST,
 };
 
 static void gt64120_pci_register_devices(void)
diff --git a/hw/hw.h b/hw/hw.h
index 56447a7..9dd7096 100644
--- a/hw/hw.h
+++ b/hw/hw.h
@@ -780,6 +780,9 @@
 #define VMSTATE_INT32_LE(_f, _s)                                   \
     VMSTATE_SINGLE(_f, _s, 0, vmstate_info_int32_le, int32_t)
 
+#define VMSTATE_UINT8_TEST(_f, _s, _t)                               \
+    VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_info_uint8, uint8_t)
+
 #define VMSTATE_UINT16_TEST(_f, _s, _t)                               \
     VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_info_uint16, uint16_t)
 
diff --git a/hw/ide/cmd646.c b/hw/ide/cmd646.c
index 5d5464a..56302b5 100644
--- a/hw/ide/cmd646.c
+++ b/hw/ide/cmd646.c
@@ -226,14 +226,8 @@
     qemu_irq *irq;
     int i;
 
-    pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_CMD);
-    pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_CMD_646);
-
-    pci_conf[PCI_REVISION_ID] = 0x07; // IDE controller revision
     pci_conf[PCI_CLASS_PROG] = 0x8f;
 
-    pci_config_set_class(pci_conf, PCI_CLASS_STORAGE_IDE);
-
     pci_conf[0x51] = 0x04; // enable IDE0
     if (d->secondary) {
         /* XXX: if not enabled, really disable the seconday IDE controller */
@@ -282,6 +276,10 @@
         .qdev.name    = "cmd646-ide",
         .qdev.size    = sizeof(PCIIDEState),
         .init         = pci_cmd646_ide_initfn,
+        .vendor_id    = PCI_VENDOR_ID_CMD,
+        .device_id    = PCI_DEVICE_ID_CMD_646,
+        .revision     = 0x07, // IDE controller revision
+        .class_id     = PCI_CLASS_STORAGE_IDE,
         .qdev.props   = (Property[]) {
             DEFINE_PROP_UINT32("secondary", PCIIDEState, secondary, 0),
             DEFINE_PROP_END_OF_LIST(),
diff --git a/hw/ide/ich.c b/hw/ide/ich.c
index 6150ce3..054e073 100644
--- a/hw/ide/ich.c
+++ b/hw/ide/ich.c
@@ -77,11 +77,8 @@
     struct AHCIPCIState *d;
     d = DO_UPCAST(struct AHCIPCIState, card, dev);
 
-    pci_config_set_vendor_id(d->card.config, PCI_VENDOR_ID_INTEL);
-    pci_config_set_device_id(d->card.config, PCI_DEVICE_ID_INTEL_82801IR);
+    ahci_init(&d->ahci, &dev->qdev, 6);
 
-    pci_config_set_class(d->card.config, PCI_CLASS_STORAGE_SATA);
-    pci_config_set_revision(d->card.config, 0x02);
     pci_config_set_prog_interface(d->card.config, AHCI_PROGMODE_MAJOR_REV_1);
 
     d->card.config[PCI_CACHE_LINE_SIZE] = 0x08;  /* Cache line size */
@@ -94,8 +91,6 @@
     qemu_register_reset(ahci_reset, d);
 
     msi_init(dev, 0x50, 1, true, false);
-
-    ahci_init(&d->ahci, &dev->qdev, 6);
     d->ahci.irq = d->card.irq[0];
 
     /* XXX BAR size should be 1k, but that breaks, so bump it to 4k for now */
@@ -131,6 +126,10 @@
         .init         = pci_ich9_ahci_init,
         .exit         = pci_ich9_uninit,
         .config_write = pci_ich9_write_config,
+        .vendor_id    = PCI_VENDOR_ID_INTEL,
+        .device_id    = PCI_DEVICE_ID_INTEL_82801IR,
+        .revision     = 0x02,
+        .class_id     = PCI_CLASS_STORAGE_SATA,
     },{
         /* end of list */
     }
diff --git a/hw/ide/piix.c b/hw/ide/piix.c
index c349644..84f72b0 100644
--- a/hw/ide/piix.c
+++ b/hw/ide/piix.c
@@ -131,12 +131,12 @@
     }
 }
 
-static int pci_piix_ide_initfn(PCIIDEState *d)
+static int pci_piix_ide_initfn(PCIDevice *dev)
 {
+    PCIIDEState *d = DO_UPCAST(PCIIDEState, dev, dev);
     uint8_t *pci_conf = d->dev.config;
 
     pci_conf[PCI_CLASS_PROG] = 0x80; // legacy ATA mode
-    pci_config_set_class(pci_conf, PCI_CLASS_STORAGE_IDE);
 
     qemu_register_reset(piix3_reset, d);
 
@@ -149,24 +149,6 @@
     return 0;
 }
 
-static int pci_piix3_ide_initfn(PCIDevice *dev)
-{
-    PCIIDEState *d = DO_UPCAST(PCIIDEState, dev, dev);
-
-    pci_config_set_vendor_id(d->dev.config, PCI_VENDOR_ID_INTEL);
-    pci_config_set_device_id(d->dev.config, PCI_DEVICE_ID_INTEL_82371SB_1);
-    return pci_piix_ide_initfn(d);
-}
-
-static int pci_piix4_ide_initfn(PCIDevice *dev)
-{
-    PCIIDEState *d = DO_UPCAST(PCIIDEState, dev, dev);
-
-    pci_config_set_vendor_id(d->dev.config, PCI_VENDOR_ID_INTEL);
-    pci_config_set_device_id(d->dev.config, PCI_DEVICE_ID_INTEL_82371AB);
-    return pci_piix_ide_initfn(d);
-}
-
 /* hd_table must contain 4 block drivers */
 /* NOTE: for the PIIX3, the IRQs and IOports are hardcoded */
 PCIDevice *pci_piix3_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn)
@@ -195,13 +177,19 @@
         .qdev.size    = sizeof(PCIIDEState),
         .qdev.no_user = 1,
         .no_hotplug   = 1,
-        .init         = pci_piix3_ide_initfn,
+        .init         = pci_piix_ide_initfn,
+        .vendor_id    = PCI_VENDOR_ID_INTEL,
+        .device_id    = PCI_DEVICE_ID_INTEL_82371SB_1,
+        .class_id     = PCI_CLASS_STORAGE_IDE,
     },{
         .qdev.name    = "piix4-ide",
         .qdev.size    = sizeof(PCIIDEState),
         .qdev.no_user = 1,
         .no_hotplug   = 1,
-        .init         = pci_piix4_ide_initfn,
+        .init         = pci_piix_ide_initfn,
+        .vendor_id    = PCI_VENDOR_ID_INTEL,
+        .device_id    = PCI_DEVICE_ID_INTEL_82371AB,
+        .class_id     = PCI_CLASS_STORAGE_IDE,
     },{
         /* end of list */
     }
diff --git a/hw/ide/via.c b/hw/ide/via.c
index 04f3290..3474c37 100644
--- a/hw/ide/via.c
+++ b/hw/ide/via.c
@@ -160,11 +160,7 @@
     PCIIDEState *d = DO_UPCAST(PCIIDEState, dev, dev);;
     uint8_t *pci_conf = d->dev.config;
 
-    pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_VIA);
-    pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_VIA_IDE);
-    pci_config_set_class(pci_conf, PCI_CLASS_STORAGE_IDE);
     pci_config_set_prog_interface(pci_conf, 0x8a); /* legacy ATA mode */
-    pci_config_set_revision(pci_conf,0x06); /* Revision 0.6 */
     pci_set_long(pci_conf + PCI_CAPABILITY_LIST, 0x000000c0);
 
     qemu_register_reset(via_reset, d);
@@ -191,6 +187,10 @@
     .qdev.size    = sizeof(PCIIDEState),
     .qdev.no_user = 1,
     .init         = vt82c686b_ide_initfn,
+    .vendor_id    = PCI_VENDOR_ID_VIA,
+    .device_id    = PCI_DEVICE_ID_VIA_IDE,
+    .revision     = 0x06,
+    .class_id     = PCI_CLASS_STORAGE_IDE,
 };
 
 static void via_ide_register(void)
diff --git a/hw/intel-hda.c b/hw/intel-hda.c
index 5485745..0ffffce 100644
--- a/hw/intel-hda.c
+++ b/hw/intel-hda.c
@@ -1138,10 +1138,6 @@
 
     d->name = d->pci.qdev.info->name;
 
-    pci_config_set_vendor_id(conf, PCI_VENDOR_ID_INTEL);
-    pci_config_set_device_id(conf, 0x2668);
-    pci_config_set_revision(conf, 1);
-    pci_config_set_class(conf, PCI_CLASS_MULTIMEDIA_HD_AUDIO);
     pci_config_set_interrupt_pin(conf, 1);
 
     /* HDCTL off 0x40 bit 0 selects signaling mode (1-HDA, 0 - Ac97) 18.1.19 */
@@ -1265,6 +1261,10 @@
     .init         = intel_hda_init,
     .exit         = intel_hda_exit,
     .config_write = intel_hda_write_config,
+    .vendor_id    = PCI_VENDOR_ID_INTEL,
+    .device_id    = 0x2668,
+    .revision     = 1,
+    .class_id     = PCI_CLASS_MULTIMEDIA_HD_AUDIO,
     .qdev.props   = (Property[]) {
         DEFINE_PROP_UINT32("debug", IntelHDAState, debug, 0),
         DEFINE_PROP_UINT32("msi", IntelHDAState, msi, 1),
diff --git a/hw/ioh3420.c b/hw/ioh3420.c
index 95adf09..a6bfbb9 100644
--- a/hw/ioh3420.c
+++ b/hw/ioh3420.c
@@ -104,12 +104,8 @@
         return rc;
     }
 
-    d->config[PCI_REVISION_ID] = PCI_DEVICE_ID_IOH_REV;
     pcie_port_init_reg(d);
 
-    pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_INTEL);
-    pci_config_set_device_id(d->config, PCI_DEVICE_ID_IOH_EPORT);
-
     rc = pci_bridge_ssvid_init(d, IOH_EP_SSVID_OFFSET,
                                IOH_EP_SSVID_SVID, IOH_EP_SSVID_SSID);
     if (rc < 0) {
@@ -217,6 +213,9 @@
     .config_write = ioh3420_write_config,
     .init = ioh3420_initfn,
     .exit = ioh3420_exitfn,
+    .vendor_id = PCI_VENDOR_ID_INTEL,
+    .device_id = PCI_DEVICE_ID_IOH_EPORT,
+    .revision = PCI_DEVICE_ID_IOH_REV,
 
     .qdev.props = (Property[]) {
         DEFINE_PROP_UINT8("port", PCIESlot, port.port, 0),
diff --git a/hw/ivshmem.c b/hw/ivshmem.c
index 7b19a81..3055dd2 100644
--- a/hw/ivshmem.c
+++ b/hw/ivshmem.c
@@ -706,12 +706,7 @@
     }
 
     pci_conf = s->dev.config;
-    pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_REDHAT_QUMRANET);
-    pci_conf[0x02] = 0x10;
-    pci_conf[0x03] = 0x11;
     pci_conf[PCI_COMMAND] = PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
-    pci_config_set_class(pci_conf, PCI_CLASS_MEMORY_RAM);
-    pci_conf[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL;
 
     pci_config_set_interrupt_pin(pci_conf, 1);
 
@@ -809,6 +804,9 @@
     .qdev.reset = ivshmem_reset,
     .init       = pci_ivshmem_init,
     .exit       = pci_ivshmem_uninit,
+    .vendor_id  = PCI_VENDOR_ID_REDHAT_QUMRANET,
+    .device_id  = 0x1110,
+    .class_id   = PCI_CLASS_MEMORY_RAM,
     .qdev.props = (Property[]) {
         DEFINE_PROP_CHR("chardev", IVShmemState, server_chr),
         DEFINE_PROP_STRING("size", IVShmemState, sizearg),
diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c
index 83084b6..940b43a 100644
--- a/hw/lsi53c895a.c
+++ b/hw/lsi53c895a.c
@@ -889,7 +889,6 @@
     uint8_t msg;
     int len;
     uint32_t current_tag;
-    SCSIDevice *current_dev;
     lsi_request *current_req, *p, *p_next;
     int id;
 
@@ -901,7 +900,6 @@
         current_req = lsi_find_by_tag(s, current_tag);
     }
     id = (current_tag >> 8) & 0xf;
-    current_dev = s->bus.devs[id];
 
     DPRINTF("MSG out len=%d\n", s->dbc);
     while (s->dbc) {
@@ -2258,15 +2256,6 @@
 
     pci_conf = s->dev.config;
 
-    /* PCI Vendor ID (word) */
-    pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_LSI_LOGIC);
-    /* PCI device ID (word) */
-    pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_LSI_53C895A);
-    /* PCI base class code */
-    pci_config_set_class(pci_conf, PCI_CLASS_STORAGE_SCSI);
-    /* PCI subsystem ID */
-    pci_conf[PCI_SUBSYSTEM_ID] = 0x00;
-    pci_conf[PCI_SUBSYSTEM_ID + 1] = 0x10;
     /* PCI latency timer = 255 */
     pci_conf[PCI_LATENCY_TIMER] = 0xff;
     /* TODO: RST# value should be 0 */
@@ -2302,6 +2291,10 @@
     .qdev.vmsd  = &vmstate_lsi_scsi,
     .init       = lsi_scsi_init,
     .exit       = lsi_scsi_uninit,
+    .vendor_id  = PCI_VENDOR_ID_LSI_LOGIC,
+    .device_id  = PCI_DEVICE_ID_LSI_53C895A,
+    .class_id   = PCI_CLASS_STORAGE_SCSI,
+    .subsystem_id = 0x1000,
 };
 
 static void lsi53c895a_register_devices(void)
diff --git a/hw/msi.c b/hw/msi.c
index b087fe5..e8c5607 100644
--- a/hw/msi.c
+++ b/hw/msi.c
@@ -172,7 +172,7 @@
     }
     flags = pci_get_word(dev->config + msi_flags_off(dev));
     cap_size = msi_cap_sizeof(flags);
-    pci_del_capability(dev, PCI_CAP_ID_MSIX, cap_size);
+    pci_del_capability(dev, PCI_CAP_ID_MSI, cap_size);
     dev->cap_present &= ~QEMU_PCI_CAP_MSI;
 
     MSI_DEV_PRINTF(dev, "uninit\n");
diff --git a/hw/msix.c b/hw/msix.c
index af40e26..03d7bec 100644
--- a/hw/msix.c
+++ b/hw/msix.c
@@ -16,9 +16,6 @@
 #include "pci.h"
 #include "range.h"
 
-/* MSI-X capability structure */
-#define MSIX_TABLE_OFFSET 4
-#define MSIX_PBA_OFFSET 8
 #define MSIX_CAP_LENGTH 12
 
 /* MSI enable bit and maskall bit are in byte 1 in FLAGS register */
@@ -26,14 +23,6 @@
 #define MSIX_ENABLE_MASK (PCI_MSIX_FLAGS_ENABLE >> 8)
 #define MSIX_MASKALL_MASK (PCI_MSIX_FLAGS_MASKALL >> 8)
 
-/* MSI-X table format */
-#define MSIX_MSG_ADDR 0
-#define MSIX_MSG_UPPER_ADDR 4
-#define MSIX_MSG_DATA 8
-#define MSIX_VECTOR_CTRL 12
-#define MSIX_ENTRY_SIZE 16
-#define MSIX_VECTOR_MASK 0x1
-
 /* How much space does an MSIX table need. */
 /* The spec requires giving the table structure
  * a 4K aligned region all by itself. */
@@ -82,9 +71,9 @@
 
     pci_set_word(config + PCI_MSIX_FLAGS, nentries - 1);
     /* Table on top of BAR */
-    pci_set_long(config + MSIX_TABLE_OFFSET, bar_size | bar_nr);
+    pci_set_long(config + PCI_MSIX_TABLE, bar_size | bar_nr);
     /* Pending bits on top of that */
-    pci_set_long(config + MSIX_PBA_OFFSET, (bar_size + MSIX_PAGE_PENDING) |
+    pci_set_long(config + PCI_MSIX_PBA, (bar_size + MSIX_PAGE_PENDING) |
                  bar_nr);
     pdev->msix_cap = config_offset;
     /* Make flags bit writable. */
@@ -140,9 +129,10 @@
 
 static int msix_is_masked(PCIDevice *dev, int vector)
 {
-    unsigned offset = vector * MSIX_ENTRY_SIZE + MSIX_VECTOR_CTRL;
+    unsigned offset =
+        vector * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL;
     return msix_function_masked(dev) ||
-	   dev->msix_table_page[offset] & MSIX_VECTOR_MASK;
+	   dev->msix_table_page[offset] & PCI_MSIX_ENTRY_CTRL_MASKBIT;
 }
 
 static void msix_handle_mask_update(PCIDevice *dev, int vector)
@@ -184,7 +174,7 @@
 {
     PCIDevice *dev = opaque;
     unsigned int offset = addr & (MSIX_PAGE_SIZE - 1) & ~0x3;
-    int vector = offset / MSIX_ENTRY_SIZE;
+    int vector = offset / PCI_MSIX_ENTRY_SIZE;
     pci_set_long(dev->msix_table_page + offset, val);
     msix_handle_mask_update(dev, vector);
 }
@@ -208,7 +198,7 @@
                    pcibus_t addr, pcibus_t size, int type)
 {
     uint8_t *config = d->config + d->msix_cap;
-    uint32_t table = pci_get_long(config + MSIX_TABLE_OFFSET);
+    uint32_t table = pci_get_long(config + PCI_MSIX_TABLE);
     uint32_t offset = table & ~(MSIX_PAGE_SIZE - 1);
     /* TODO: for assigned devices, we'll want to make it possible to map
      * pending bits separately in case they are in a separate bar. */
@@ -226,8 +216,9 @@
 {
     int vector;
     for (vector = 0; vector < nentries; ++vector) {
-        unsigned offset = vector * MSIX_ENTRY_SIZE + MSIX_VECTOR_CTRL;
-        dev->msix_table_page[offset] |= MSIX_VECTOR_MASK;
+        unsigned offset =
+            vector * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL;
+        dev->msix_table_page[offset] |= PCI_MSIX_ENTRY_CTRL_MASKBIT;
     }
 }
 
@@ -313,7 +304,7 @@
         return;
     }
 
-    qemu_put_buffer(f, dev->msix_table_page, n * MSIX_ENTRY_SIZE);
+    qemu_put_buffer(f, dev->msix_table_page, n * PCI_MSIX_ENTRY_SIZE);
     qemu_put_buffer(f, dev->msix_table_page + MSIX_PAGE_PENDING, (n + 7) / 8);
 }
 
@@ -327,7 +318,7 @@
     }
 
     msix_free_irq_entries(dev);
-    qemu_get_buffer(f, dev->msix_table_page, n * MSIX_ENTRY_SIZE);
+    qemu_get_buffer(f, dev->msix_table_page, n * PCI_MSIX_ENTRY_SIZE);
     qemu_get_buffer(f, dev->msix_table_page + MSIX_PAGE_PENDING, (n + 7) / 8);
 }
 
@@ -355,7 +346,7 @@
 /* Send an MSI-X message */
 void msix_notify(PCIDevice *dev, unsigned vector)
 {
-    uint8_t *table_entry = dev->msix_table_page + vector * MSIX_ENTRY_SIZE;
+    uint8_t *table_entry = dev->msix_table_page + vector * PCI_MSIX_ENTRY_SIZE;
     uint64_t address;
     uint32_t data;
 
@@ -366,9 +357,8 @@
         return;
     }
 
-    address = pci_get_long(table_entry + MSIX_MSG_UPPER_ADDR);
-    address = (address << 32) | pci_get_long(table_entry + MSIX_MSG_ADDR);
-    data = pci_get_long(table_entry + MSIX_MSG_DATA);
+    address = pci_get_quad(table_entry + PCI_MSIX_ENTRY_LOWER_ADDR);
+    data = pci_get_long(table_entry + PCI_MSIX_ENTRY_DATA);
     stl_phys(address, data);
 }
 
diff --git a/hw/ne2000.c b/hw/ne2000.c
index b668ad1..f8acaae 100644
--- a/hw/ne2000.c
+++ b/hw/ne2000.c
@@ -721,9 +721,6 @@
     uint8_t *pci_conf;
 
     pci_conf = d->dev.config;
-    pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_REALTEK);
-    pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_REALTEK_8029);
-    pci_config_set_class(pci_conf, PCI_CLASS_NETWORK_ETHERNET);
     /* TODO: RST# value should be 0. PCI spec 6.2.4 */
     pci_conf[PCI_INTERRUPT_PIN] = 1; // interrupt pin 0
 
@@ -767,6 +764,9 @@
     .qdev.vmsd  = &vmstate_pci_ne2000,
     .init       = pci_ne2000_init,
     .exit       = pci_ne2000_exit,
+    .vendor_id  = PCI_VENDOR_ID_REALTEK,
+    .device_id  = PCI_DEVICE_ID_REALTEK_8029,
+    .class_id   = PCI_CLASS_NETWORK_ETHERNET,
     .qdev.props = (Property[]) {
         DEFINE_NIC_PROPERTIES(PCINE2000State, ne2000.c),
         DEFINE_PROP_END_OF_LIST(),
diff --git a/hw/pc.h b/hw/pc.h
index 0dcbee7..6d5730b 100644
--- a/hw/pc.h
+++ b/hw/pc.h
@@ -176,7 +176,6 @@
 typedef struct PCII440FXState PCII440FXState;
 
 PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix_devfn, qemu_irq *pic, ram_addr_t ram_size);
-PCIBus *i440fx_xen_init(PCII440FXState **pi440fx_state, int *piix3_devfn, qemu_irq *pic, ram_addr_t ram_size);
 void i440fx_init_memory_mappings(PCII440FXState *d);
 
 /* piix4.c */
diff --git a/hw/pc_piix.c b/hw/pc_piix.c
index 9a22a8a..c5c16b4 100644
--- a/hw/pc_piix.c
+++ b/hw/pc_piix.c
@@ -124,11 +124,7 @@
     isa_irq = qemu_allocate_irqs(isa_irq_handler, isa_irq_state, 24);
 
     if (pci_enabled) {
-        if (!xen_enabled()) {
-            pci_bus = i440fx_init(&i440fx_state, &piix3_devfn, isa_irq, ram_size);
-        } else {
-            pci_bus = i440fx_xen_init(&i440fx_state, &piix3_devfn, isa_irq, ram_size);
-        }
+        pci_bus = i440fx_init(&i440fx_state, &piix3_devfn, isa_irq, ram_size);
     } else {
         pci_bus = NULL;
         i440fx_state = NULL;
@@ -140,6 +136,10 @@
 
     pc_vga_init(pci_enabled? pci_bus: NULL);
 
+    if (xen_enabled()) {
+        pci_create_simple(pci_bus, -1, "xen-platform");
+    }
+
     /* init basic PC hardware */
     pc_basic_device_init(isa_irq, &rtc_state, xen_enabled());
 
@@ -288,6 +288,18 @@
             .driver   = "PCI",
             .property = "command_serr_enable",
             .value    = "off",
+        },{
+            .driver   = "virtio-blk-pci",
+            .property = "event_idx",
+            .value    = "off",
+        },{
+            .driver   = "virtio-serial-pci",
+            .property = "event_idx",
+            .value    = "off",
+        },{
+            .driver   = "virtio-net-pci",
+            .property = "event_idx",
+            .value    = "off",
         },
         { /* end of list */ }
     },
@@ -319,6 +331,18 @@
             .driver   = "PCI",
             .property = "command_serr_enable",
             .value    = "off",
+        },{
+            .driver   = "virtio-blk-pci",
+            .property = "event_idx",
+            .value    = "off",
+        },{
+            .driver   = "virtio-serial-pci",
+            .property = "event_idx",
+            .value    = "off",
+        },{
+            .driver   = "virtio-net-pci",
+            .property = "event_idx",
+            .value    = "off",
         },
         { /* end of list */ }
     }
@@ -358,6 +382,18 @@
             .driver   = "PCI",
             .property = "command_serr_enable",
             .value    = "off",
+        },{
+            .driver   = "virtio-blk-pci",
+            .property = "event_idx",
+            .value    = "off",
+        },{
+            .driver   = "virtio-serial-pci",
+            .property = "event_idx",
+            .value    = "off",
+        },{
+            .driver   = "virtio-net-pci",
+            .property = "event_idx",
+            .value    = "off",
         },
         { /* end of list */ }
     }
@@ -409,6 +445,18 @@
             .driver   = "PCI",
             .property = "command_serr_enable",
             .value    = "off",
+        },{
+            .driver   = "virtio-blk-pci",
+            .property = "event_idx",
+            .value    = "off",
+        },{
+            .driver   = "virtio-serial-pci",
+            .property = "event_idx",
+            .value    = "off",
+        },{
+            .driver   = "virtio-net-pci",
+            .property = "event_idx",
+            .value    = "off",
         },
         { /* end of list */ }
     },
diff --git a/hw/pci.c b/hw/pci.c
index 1d297d6..b904a4e 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -726,10 +726,11 @@
 /* -1 for devfn means auto assign */
 static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus,
                                          const char *name, int devfn,
-                                         PCIConfigReadFunc *config_read,
-                                         PCIConfigWriteFunc *config_write,
-                                         bool is_bridge)
+                                         const PCIDeviceInfo *info)
 {
+    PCIConfigReadFunc *config_read = info->config_read;
+    PCIConfigWriteFunc *config_write = info->config_write;
+
     if (devfn < 0) {
         for(devfn = bus->devfn_min ; devfn < ARRAY_SIZE(bus->devices);
             devfn += PCI_FUNC_MAX) {
@@ -750,13 +751,29 @@
     pci_dev->irq_state = 0;
     pci_config_alloc(pci_dev);
 
-    if (!is_bridge) {
-        pci_set_default_subsystem_id(pci_dev);
+    pci_config_set_vendor_id(pci_dev->config, info->vendor_id);
+    pci_config_set_device_id(pci_dev->config, info->device_id);
+    pci_config_set_revision(pci_dev->config, info->revision);
+    pci_config_set_class(pci_dev->config, info->class_id);
+
+    if (!info->is_bridge) {
+        if (info->subsystem_vendor_id || info->subsystem_id) {
+            pci_set_word(pci_dev->config + PCI_SUBSYSTEM_VENDOR_ID,
+                         info->subsystem_vendor_id);
+            pci_set_word(pci_dev->config + PCI_SUBSYSTEM_ID,
+                         info->subsystem_id);
+        } else {
+            pci_set_default_subsystem_id(pci_dev);
+        }
+    } else {
+        /* subsystem_vendor_id/subsystem_id are only for header type 0 */
+        assert(!info->subsystem_vendor_id);
+        assert(!info->subsystem_id);
     }
     pci_init_cmask(pci_dev);
     pci_init_wmask(pci_dev);
     pci_init_w1cmask(pci_dev);
-    if (is_bridge) {
+    if (info->is_bridge) {
         pci_init_wmask_bridge(pci_dev);
     }
     if (pci_init_multifunction(bus, pci_dev)) {
@@ -783,17 +800,20 @@
     pci_config_free(pci_dev);
 }
 
+/* TODO: obsolete. eliminate this once all pci devices are qdevifed. */
 PCIDevice *pci_register_device(PCIBus *bus, const char *name,
                                int instance_size, int devfn,
                                PCIConfigReadFunc *config_read,
                                PCIConfigWriteFunc *config_write)
 {
     PCIDevice *pci_dev;
+    PCIDeviceInfo info = {
+        .config_read = config_read,
+        .config_write = config_write,
+    };
 
     pci_dev = qemu_mallocz(instance_size);
-    pci_dev = do_pci_register_device(pci_dev, bus, name, devfn,
-                                     config_read, config_write,
-                                     PCI_HEADER_TYPE_NORMAL);
+    pci_dev = do_pci_register_device(pci_dev, bus, name, devfn, &info);
     if (pci_dev == NULL) {
         hw_error("PCI: can't register device\n");
     }
@@ -1643,7 +1663,7 @@
     PCIDevice *pci_dev = (PCIDevice *)qdev;
     PCIDeviceInfo *info = container_of(base, PCIDeviceInfo, qdev);
     PCIBus *bus;
-    int devfn, rc;
+    int rc;
     bool is_default_rom;
 
     /* initialize cap_present for pci_is_express() and pci_config_size() */
@@ -1652,10 +1672,8 @@
     }
 
     bus = FROM_QBUS(PCIBus, qdev_get_parent_bus(qdev));
-    devfn = pci_dev->devfn;
-    pci_dev = do_pci_register_device(pci_dev, bus, base->name, devfn,
-                                     info->config_read, info->config_write,
-                                     info->is_bridge);
+    pci_dev = do_pci_register_device(pci_dev, bus, base->name,
+                                     pci_dev->devfn, info);
     if (pci_dev == NULL)
         return -1;
     if (qdev->hotplugged && info->no_hotplug) {
@@ -1663,10 +1681,12 @@
         do_pci_unregister_device(pci_dev);
         return -1;
     }
-    rc = info->init(pci_dev);
-    if (rc != 0) {
-        do_pci_unregister_device(pci_dev);
-        return rc;
+    if (info->init) {
+        rc = info->init(pci_dev);
+        if (rc != 0) {
+            do_pci_unregister_device(pci_dev);
+            return rc;
+        }
     }
 
     /* rom loading */
diff --git a/hw/pci.h b/hw/pci.h
index 0d288ce..c220745 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -433,6 +433,13 @@
     PCIConfigReadFunc *config_read;
     PCIConfigWriteFunc *config_write;
 
+    uint16_t vendor_id;
+    uint16_t device_id;
+    uint8_t revision;
+    uint16_t class_id;
+    uint16_t subsystem_vendor_id;       /* only for header type = 0 */
+    uint16_t subsystem_id;              /* only for header type = 0 */
+
     /*
      * pci-to-pci bridge or normal device.
      * This doesn't mean pci host switch.
diff --git a/hw/pci_ids.h b/hw/pci_ids.h
index d9457ed..d94578c 100644
--- a/hw/pci_ids.h
+++ b/hw/pci_ids.h
@@ -109,3 +109,5 @@
 #define PCI_DEVICE_ID_INTEL_82371AB      0x7111
 #define PCI_DEVICE_ID_INTEL_82371AB_2    0x7112
 #define PCI_DEVICE_ID_INTEL_82371AB_3    0x7113
+
+#define PCI_VENDOR_ID_XENSOURCE          0x5853
diff --git a/hw/pci_regs.h b/hw/pci_regs.h
index 5a5ab89..e884096 100644
--- a/hw/pci_regs.h
+++ b/hw/pci_regs.h
@@ -300,12 +300,22 @@
 #define PCI_MSI_DATA_64		12	/* 16 bits of data for 64-bit devices */
 #define PCI_MSI_MASK_64		16	/* Mask bits register for 64-bit devices */
 
-/* MSI-X registers (these are at offset PCI_MSIX_FLAGS) */
+/* MSI-X registers */
 #define PCI_MSIX_FLAGS		2
 #define  PCI_MSIX_FLAGS_QSIZE	0x7FF
 #define  PCI_MSIX_FLAGS_ENABLE	(1 << 15)
 #define  PCI_MSIX_FLAGS_MASKALL	(1 << 14)
-#define PCI_MSIX_FLAGS_BIRMASK	(7 << 0)
+#define PCI_MSIX_TABLE		4
+#define PCI_MSIX_PBA		8
+#define  PCI_MSIX_FLAGS_BIRMASK	(7 << 0)
+
+/* MSI-X entry's format */
+#define PCI_MSIX_ENTRY_SIZE		16
+#define  PCI_MSIX_ENTRY_LOWER_ADDR	0
+#define  PCI_MSIX_ENTRY_UPPER_ADDR	4
+#define  PCI_MSIX_ENTRY_DATA		8
+#define  PCI_MSIX_ENTRY_VECTOR_CTRL	12
+#define   PCI_MSIX_ENTRY_CTRL_MASKBIT	1
 
 /* CompactPCI Hotswap Register */
 
@@ -365,6 +375,11 @@
 #define  PCI_X_STATUS_266MHZ	0x40000000	/* 266 MHz capable */
 #define  PCI_X_STATUS_533MHZ	0x80000000	/* 533 MHz capable */
 
+/* PCI Bridge Subsystem ID registers */
+
+#define PCI_SSVID_VENDOR_ID     4	/* PCI-Bridge subsystem vendor id register */
+#define PCI_SSVID_DEVICE_ID     6	/* PCI-Bridge subsystem device id register */
+
 /* PCI Express capability registers */
 
 #define PCI_EXP_FLAGS		2	/* Capabilities register */
@@ -420,7 +435,7 @@
 #define  PCI_EXP_LNKCAP_L0SEL	0x00007000 /* L0s Exit Latency */
 #define  PCI_EXP_LNKCAP_L1EL	0x00038000 /* L1 Exit Latency */
 #define  PCI_EXP_LNKCAP_CLKPM	0x00040000 /* L1 Clock Power Management */
-#define  PCI_EXP_LNKCAP_SDERC	0x00080000 /* Suprise Down Error Reporting Capable */
+#define  PCI_EXP_LNKCAP_SDERC	0x00080000 /* Surprise Down Error Reporting Capable */
 #define  PCI_EXP_LNKCAP_DLLLARC	0x00100000 /* Data Link Layer Link Active Reporting Capable */
 #define  PCI_EXP_LNKCAP_LBNC	0x00200000 /* Link Bandwidth Notification Capability */
 #define  PCI_EXP_LNKCAP_PN	0xff000000 /* Port Number */
@@ -437,7 +452,10 @@
 #define  PCI_EXP_LNKCTL_LABIE	0x0800	/* Lnk Autonomous Bandwidth Interrupt Enable */
 #define PCI_EXP_LNKSTA		18	/* Link Status */
 #define  PCI_EXP_LNKSTA_CLS	0x000f	/* Current Link Speed */
+#define  PCI_EXP_LNKSTA_CLS_2_5GB 0x01	/* Current Link Speed 2.5GT/s */
+#define  PCI_EXP_LNKSTA_CLS_5_0GB 0x02	/* Current Link Speed 5.0GT/s */
 #define  PCI_EXP_LNKSTA_NLW	0x03f0	/* Nogotiated Link Width */
+#define  PCI_EXP_LNKSTA_NLW_SHIFT 4	/* start of NLW mask in link status */
 #define  PCI_EXP_LNKSTA_LT	0x0800	/* Link Training */
 #define  PCI_EXP_LNKSTA_SLC	0x1000	/* Slot Clock Configuration */
 #define  PCI_EXP_LNKSTA_DLLLA	0x2000	/* Data Link Layer Link Active */
@@ -486,10 +504,22 @@
 #define  PCI_EXP_RTCTL_CRSSVE	0x10	/* CRS Software Visibility Enable */
 #define PCI_EXP_RTCAP		30	/* Root Capabilities */
 #define PCI_EXP_RTSTA		32	/* Root Status */
+#define PCI_EXP_RTSTA_PME	0x10000 /* PME status */
+#define PCI_EXP_RTSTA_PENDING	0x20000 /* PME pending */
 #define PCI_EXP_DEVCAP2		36	/* Device Capabilities 2 */
 #define  PCI_EXP_DEVCAP2_ARI	0x20	/* Alternative Routing-ID */
+#define  PCI_EXP_DEVCAP2_LTR	0x800	/* Latency tolerance reporting */
+#define  PCI_EXP_OBFF_MASK	0xc0000 /* OBFF support mechanism */
+#define  PCI_EXP_OBFF_MSG	0x40000 /* New message signaling */
+#define  PCI_EXP_OBFF_WAKE	0x80000 /* Re-use WAKE# for OBFF */
 #define PCI_EXP_DEVCTL2		40	/* Device Control 2 */
 #define  PCI_EXP_DEVCTL2_ARI	0x20	/* Alternative Routing-ID */
+#define  PCI_EXP_IDO_REQ_EN	0x100	/* ID-based ordering request enable */
+#define  PCI_EXP_IDO_CMP_EN	0x200	/* ID-based ordering completion enable */
+#define  PCI_EXP_LTR_EN		0x400	/* Latency tolerance reporting */
+#define  PCI_EXP_OBFF_MSGA_EN	0x2000	/* OBFF enable with Message type A */
+#define  PCI_EXP_OBFF_MSGB_EN	0x4000	/* OBFF enable with Message type B */
+#define  PCI_EXP_OBFF_WAKE_EN	0x6000	/* OBFF using WAKE# signaling */
 #define PCI_EXP_LNKCTL2		48	/* Link Control 2 */
 #define PCI_EXP_SLTCTL2		56	/* Slot Control 2 */
 
@@ -502,9 +532,12 @@
 #define PCI_EXT_CAP_ID_VC	2
 #define PCI_EXT_CAP_ID_DSN	3
 #define PCI_EXT_CAP_ID_PWR	4
+#define PCI_EXT_CAP_ID_VNDR	11
+#define PCI_EXT_CAP_ID_ACS	13
 #define PCI_EXT_CAP_ID_ARI	14
 #define PCI_EXT_CAP_ID_ATS	15
 #define PCI_EXT_CAP_ID_SRIOV	16
+#define PCI_EXT_CAP_ID_LTR	24
 
 /* Advanced Error Reporting */
 #define PCI_ERR_UNCOR_STATUS	4	/* Uncorrectable Error Status */
@@ -556,8 +589,7 @@
 #define PCI_ERR_ROOT_FIRST_FATAL	0x00000010	/* First Fatal */
 #define PCI_ERR_ROOT_NONFATAL_RCV	0x00000020	/* Non-Fatal Received */
 #define PCI_ERR_ROOT_FATAL_RCV		0x00000040	/* Fatal Received */
-#define PCI_ERR_ROOT_COR_SRC	52
-#define PCI_ERR_ROOT_SRC	54
+#define PCI_ERR_ROOT_ERR_SRC	52	/* Error Source Identification */
 
 /* Virtual Channel */
 #define PCI_VC_PORT_REG1	4
@@ -662,4 +694,22 @@
 #define  PCI_SRIOV_VFM_MO	0x2	/* Active.MigrateOut */
 #define  PCI_SRIOV_VFM_AV	0x3	/* Active.Available */
 
+#define PCI_LTR_MAX_SNOOP_LAT	0x4
+#define PCI_LTR_MAX_NOSNOOP_LAT	0x6
+#define  PCI_LTR_VALUE_MASK	0x000003ff
+#define  PCI_LTR_SCALE_MASK	0x00001c00
+#define  PCI_LTR_SCALE_SHIFT	10
+
+/* Access Control Service */
+#define PCI_ACS_CAP		0x04	/* ACS Capability Register */
+#define  PCI_ACS_SV		0x01	/* Source Validation */
+#define  PCI_ACS_TB		0x02	/* Translation Blocking */
+#define  PCI_ACS_RR		0x04	/* P2P Request Redirect */
+#define  PCI_ACS_CR		0x08	/* P2P Completion Redirect */
+#define  PCI_ACS_UF		0x10	/* Upstream Forwarding */
+#define  PCI_ACS_EC		0x20	/* P2P Egress Control */
+#define  PCI_ACS_DT		0x40	/* Direct Translated P2P */
+#define PCI_ACS_CTRL		0x06	/* ACS Control Register */
+#define PCI_ACS_EGRESS_CTL_V	0x08	/* ACS Egress Control Vector */
+
 #endif /* LINUX_PCI_REGS_H */
diff --git a/hw/pcie_aer.c b/hw/pcie_aer.c
index f08d3c7..be019c7 100644
--- a/hw/pcie_aer.c
+++ b/hw/pcie_aer.c
@@ -38,6 +38,9 @@
 #define PCIE_DEV_PRINTF(dev, fmt, ...)                                  \
     PCIE_DPRINTF("%s:%x "fmt, (dev)->name, (dev)->devfn, ## __VA_ARGS__)
 
+#define PCI_ERR_SRC_COR_OFFS    0
+#define PCI_ERR_SRC_UNCOR_OFFS  2
+
 /* From 6.2.7 Error Listing and Rules. Table 6-2, 6-3 and 6-4 */
 static uint32_t pcie_aer_uncor_default_severity(uint32_t status)
 {
@@ -320,7 +323,8 @@
         if (root_status & PCI_ERR_ROOT_COR_RCV) {
             root_status |= PCI_ERR_ROOT_MULTI_COR_RCV;
         } else {
-            pci_set_word(aer_cap + PCI_ERR_ROOT_COR_SRC, msg->source_id);
+            pci_set_word(aer_cap + PCI_ERR_ROOT_ERR_SRC + PCI_ERR_SRC_COR_OFFS,
+                         msg->source_id);
         }
         root_status |= PCI_ERR_ROOT_COR_RCV;
         break;
@@ -341,7 +345,8 @@
         if (root_status & PCI_ERR_ROOT_UNCOR_RCV) {
             root_status |= PCI_ERR_ROOT_MULTI_UNCOR_RCV;
         } else {
-            pci_set_word(aer_cap + PCI_ERR_ROOT_SRC, msg->source_id);
+            pci_set_word(aer_cap + PCI_ERR_ROOT_ERR_SRC +
+                         PCI_ERR_SRC_UNCOR_OFFS, msg->source_id);
         }
         root_status |= PCI_ERR_ROOT_UNCOR_RCV;
     }
diff --git a/hw/pcnet-pci.c b/hw/pcnet-pci.c
index 9415a1e..216cf81 100644
--- a/hw/pcnet-pci.c
+++ b/hw/pcnet-pci.c
@@ -265,12 +265,8 @@
 
     pci_conf = pci_dev->config;
 
-    pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_AMD);
-    pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_AMD_LANCE);
     pci_set_word(pci_conf + PCI_STATUS,
                  PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MEDIUM);
-    pci_conf[PCI_REVISION_ID] = 0x10;
-    pci_config_set_class(pci_conf, PCI_CLASS_NETWORK_ETHERNET);
 
     pci_set_word(pci_conf + PCI_SUBSYSTEM_VENDOR_ID, 0x0);
     pci_set_word(pci_conf + PCI_SUBSYSTEM_ID, 0x0);
@@ -318,6 +314,10 @@
     .qdev.vmsd  = &vmstate_pci_pcnet,
     .init       = pci_pcnet_init,
     .exit       = pci_pcnet_uninit,
+    .vendor_id  = PCI_VENDOR_ID_AMD,
+    .device_id  = PCI_DEVICE_ID_AMD_LANCE,
+    .revision   = 0x10,
+    .class_id   = PCI_CLASS_NETWORK_ETHERNET,
     .qdev.props = (Property[]) {
         DEFINE_NIC_PROPERTIES(PCIPCNetState, state.conf),
         DEFINE_PROP_END_OF_LIST(),
diff --git a/hw/piix4.c b/hw/piix4.c
index 71f1f84..9590e7b 100644
--- a/hw/piix4.c
+++ b/hw/piix4.c
@@ -86,15 +86,8 @@
 static int piix4_initfn(PCIDevice *dev)
 {
     PIIX4State *d = DO_UPCAST(PIIX4State, dev, dev);
-    uint8_t *pci_conf;
 
     isa_bus_new(&d->dev.qdev);
-
-    pci_conf = d->dev.config;
-    pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL);
-    pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82371AB_0); // 82371AB/EB/MB PIIX4 PCI-to-ISA bridge
-    pci_config_set_class(pci_conf, PCI_CLASS_BRIDGE_ISA);
-
     piix4_dev = &d->dev;
     qemu_register_reset(piix4_reset, d);
     return 0;
@@ -117,6 +110,9 @@
         .qdev.no_user = 1,
         .no_hotplug   = 1,
         .init         = piix4_initfn,
+        .vendor_id    = PCI_VENDOR_ID_INTEL,
+        .device_id    = PCI_DEVICE_ID_INTEL_82371AB_0, // 82371AB/EB/MB PIIX4 PCI-to-ISA bridge
+        .class_id     = PCI_CLASS_BRIDGE_ISA,
     },{
         /* end of list */
     }
diff --git a/hw/piix_pci.c b/hw/piix_pci.c
index 85a320e..26ce904 100644
--- a/hw/piix_pci.c
+++ b/hw/piix_pci.c
@@ -40,6 +40,7 @@
 
 #define PIIX_NUM_PIC_IRQS       16      /* i8259 * 2 */
 #define PIIX_NUM_PIRQS          4ULL    /* PIRQ[A-D] */
+#define XEN_PIIX_NUM_PIRQS      128ULL
 #define PIIX_PIRQC              0x60
 
 typedef struct PIIX3State {
@@ -78,6 +79,8 @@
 #define I440FX_SMRAM    0x72
 
 static void piix3_set_irq(void *opaque, int pirq, int level);
+static void piix3_write_config_xen(PCIDevice *dev,
+                               uint32_t address, uint32_t val, int len);
 
 /* return the global irq number corresponding to a given device irq
    pin. We could also use the bus number to have a more precise
@@ -173,13 +176,6 @@
     }
 }
 
-static void i440fx_write_config_xen(PCIDevice *dev,
-                                    uint32_t address, uint32_t val, int len)
-{
-    xen_piix_pci_write_config_client(address, val, len);
-    i440fx_write_config(dev, address, val, len);
-}
-
 static int i440fx_load_old(QEMUFile* f, void *opaque, int version_id)
 {
     PCII440FXState *d = opaque;
@@ -236,11 +232,6 @@
 {
     PCII440FXState *d = DO_UPCAST(PCII440FXState, dev, dev);
 
-    pci_config_set_vendor_id(d->dev.config, PCI_VENDOR_ID_INTEL);
-    pci_config_set_device_id(d->dev.config, PCI_DEVICE_ID_INTEL_82441);
-    d->dev.config[0x08] = 0x02; // revision
-    pci_config_set_class(d->dev.config, PCI_CLASS_BRIDGE_HOST);
-
     d->dev.config[I440FX_SMRAM] = 0x02;
 
     cpu_smm_register(&i440fx_set_smm, d);
@@ -267,8 +258,21 @@
     d = pci_create_simple(b, 0, device_name);
     *pi440fx_state = DO_UPCAST(PCII440FXState, dev, d);
 
-    piix3 = DO_UPCAST(PIIX3State, dev,
-                      pci_create_simple_multifunction(b, -1, true, "PIIX3"));
+    /* Xen supports additional interrupt routes from the PCI devices to
+     * the IOAPIC: the four pins of each PCI device on the bus are also
+     * connected to the IOAPIC directly.
+     * These additional routes can be discovered through ACPI. */
+    if (xen_enabled()) {
+        piix3 = DO_UPCAST(PIIX3State, dev,
+                pci_create_simple_multifunction(b, -1, true, "PIIX3-xen"));
+        pci_bus_irqs(b, xen_piix3_set_irq, xen_pci_slot_get_pirq,
+                piix3, XEN_PIIX_NUM_PIRQS);
+    } else {
+        piix3 = DO_UPCAST(PIIX3State, dev,
+                pci_create_simple_multifunction(b, -1, true, "PIIX3"));
+        pci_bus_irqs(b, piix3_set_irq, pci_slot_get_pirq, piix3,
+                PIIX_NUM_PIRQS);
+    }
     piix3->pic = pic;
 
     (*pi440fx_state)->piix3 = piix3;
@@ -289,21 +293,6 @@
     PCIBus *b;
 
     b = i440fx_common_init("i440FX", pi440fx_state, piix3_devfn, pic, ram_size);
-    pci_bus_irqs(b, piix3_set_irq, pci_slot_get_pirq, (*pi440fx_state)->piix3,
-                 PIIX_NUM_PIRQS);
-
-    return b;
-}
-
-PCIBus *i440fx_xen_init(PCII440FXState **pi440fx_state, int *piix3_devfn,
-                        qemu_irq *pic, ram_addr_t ram_size)
-{
-    PCIBus *b;
-
-    b = i440fx_common_init("i440FX-xen", pi440fx_state, piix3_devfn, pic, ram_size);
-    pci_bus_irqs(b, xen_piix3_set_irq, xen_pci_slot_get_pirq,
-                 (*pi440fx_state)->piix3, PIIX_NUM_PIRQS);
-
     return b;
 }
 
@@ -365,6 +354,13 @@
     }
 }
 
+static void piix3_write_config_xen(PCIDevice *dev,
+                               uint32_t address, uint32_t val, int len)
+{
+    xen_piix_pci_write_config_client(address, val, len);
+    piix3_write_config(dev, address, val, len);
+}
+
 static void piix3_reset(void *opaque)
 {
     PIIX3State *d = opaque;
@@ -441,15 +437,8 @@
 static int piix3_initfn(PCIDevice *dev)
 {
     PIIX3State *d = DO_UPCAST(PIIX3State, dev, dev);
-    uint8_t *pci_conf;
 
     isa_bus_new(&d->dev.qdev);
-
-    pci_conf = d->dev.config;
-    pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL);
-    pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82371SB_0); // 82371SB PIIX3 PCI-to-ISA bridge (Step A1)
-    pci_config_set_class(pci_conf, PCI_CLASS_BRIDGE_ISA);
-
     qemu_register_reset(piix3_reset, d);
     return 0;
 }
@@ -464,14 +453,10 @@
         .no_hotplug   = 1,
         .init         = i440fx_initfn,
         .config_write = i440fx_write_config,
-    },{
-        .qdev.name    = "i440FX-xen",
-        .qdev.desc    = "Host bridge",
-        .qdev.size    = sizeof(PCII440FXState),
-        .qdev.vmsd    = &vmstate_i440fx,
-        .qdev.no_user = 1,
-        .init         = i440fx_initfn,
-        .config_write = i440fx_write_config_xen,
+        .vendor_id    = PCI_VENDOR_ID_INTEL,
+        .device_id    = PCI_DEVICE_ID_INTEL_82441,
+        .revision     = 0x02,
+        .class_id     = PCI_CLASS_BRIDGE_HOST,
     },{
         .qdev.name    = "PIIX3",
         .qdev.desc    = "ISA bridge",
@@ -481,6 +466,18 @@
         .no_hotplug   = 1,
         .init         = piix3_initfn,
         .config_write = piix3_write_config,
+        .vendor_id    = PCI_VENDOR_ID_INTEL,
+        .device_id    = PCI_DEVICE_ID_INTEL_82371SB_0, // 82371SB PIIX3 PCI-to-ISA bridge (Step A1)
+        .class_id     = PCI_CLASS_BRIDGE_ISA,
+    },{
+        .qdev.name    = "PIIX3-xen",
+        .qdev.desc    = "ISA bridge",
+        .qdev.size    = sizeof(PIIX3State),
+        .qdev.vmsd    = &vmstate_piix3,
+        .qdev.no_user = 1,
+        .no_hotplug   = 1,
+        .init         = piix3_initfn,
+        .config_write = piix3_write_config_xen,
     },{
         /* end of list */
     }
diff --git a/hw/ppce500_pci.c b/hw/ppce500_pci.c
index 069af96..fc11af4 100644
--- a/hw/ppce500_pci.c
+++ b/hw/ppce500_pci.c
@@ -304,20 +304,13 @@
     return 0;
 }
 
-static int e500_host_bridge_initfn(PCIDevice *dev)
-{
-    pci_config_set_vendor_id(dev->config, PCI_VENDOR_ID_FREESCALE);
-    pci_config_set_device_id(dev->config, PCI_DEVICE_ID_MPC8533E);
-    pci_config_set_class(dev->config, PCI_CLASS_PROCESSOR_POWERPC);
-
-    return 0;
-}
-
 static PCIDeviceInfo e500_host_bridge_info = {
     .qdev.name    = "e500-host-bridge",
     .qdev.desc    = "Host bridge",
     .qdev.size    = sizeof(PCIDevice),
-    .init         = e500_host_bridge_initfn,
+    .vendor_id    = PCI_VENDOR_ID_FREESCALE,
+    .device_id    = PCI_DEVICE_ID_MPC8533E,
+    .class_id     = PCI_CLASS_PROCESSOR_POWERPC,
 };
 
 static SysBusDeviceInfo e500_pcihost_info = {
diff --git a/hw/qxl.c b/hw/qxl.c
index 1906e84..16316f2 100644
--- a/hw/qxl.c
+++ b/hw/qxl.c
@@ -1231,7 +1231,6 @@
         break;
     }
 
-    pci_config_set_vendor_id(config, REDHAT_PCI_VENDOR_ID);
     pci_config_set_device_id(config, pci_device_id);
     pci_set_byte(&config[PCI_REVISION_ID], pci_device_rev);
     pci_set_byte(&config[PCI_INTERRUPT_PIN], 1);
@@ -1311,7 +1310,6 @@
     qxl0 = qxl;
     register_displaychangelistener(vga->ds, &display_listener);
 
-    pci_config_set_class(dev->config, PCI_CLASS_DISPLAY_VGA);
     return qxl_init_common(qxl);
 }
 
@@ -1331,7 +1329,6 @@
                                           qxl->vga.vram_size);
     qxl->vga.vram_ptr = qemu_get_ram_ptr(qxl->vga.vram_offset);
 
-    pci_config_set_class(dev->config, PCI_CLASS_DISPLAY_OTHER);
     return qxl_init_common(qxl);
 }
 
@@ -1494,6 +1491,8 @@
     .init         = qxl_init_primary,
     .config_write = qxl_write_config,
     .romfile      = "vgabios-qxl.bin",
+    .vendor_id    = REDHAT_PCI_VENDOR_ID,
+    .class_id     = PCI_CLASS_DISPLAY_VGA,
     .qdev.props = (Property[]) {
         DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size, 64 * 1024 * 1024),
         DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram_size, 64 * 1024 * 1024),
@@ -1512,6 +1511,8 @@
     .qdev.reset   = qxl_reset_handler,
     .qdev.vmsd    = &qxl_vmstate,
     .init         = qxl_init_secondary,
+    .vendor_id    = REDHAT_PCI_VENDOR_ID,
+    .class_id     = PCI_CLASS_DISPLAY_OTHER,
     .qdev.props = (Property[]) {
         DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size, 64 * 1024 * 1024),
         DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram_size, 64 * 1024 * 1024),
diff --git a/hw/rtl8139.c b/hw/rtl8139.c
index 2f8db58..5214b8c 100644
--- a/hw/rtl8139.c
+++ b/hw/rtl8139.c
@@ -3457,10 +3457,6 @@
     uint8_t *pci_conf;
 
     pci_conf = s->dev.config;
-    pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_REALTEK);
-    pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_REALTEK_8139);
-    pci_conf[PCI_REVISION_ID] = RTL8139_PCI_REVID; /* >=0x20 is for 8139C+ */
-    pci_config_set_class(pci_conf, PCI_CLASS_NETWORK_ETHERNET);
     pci_conf[PCI_INTERRUPT_PIN] = 1;    /* interrupt pin 0 */
     /* TODO: start of capability list, but no capability
      * list bit in status register, and offset 0xdc seems unused. */
@@ -3514,6 +3510,10 @@
     .init       = pci_rtl8139_init,
     .exit       = pci_rtl8139_uninit,
     .romfile    = "pxe-rtl8139.rom",
+    .vendor_id  = PCI_VENDOR_ID_REALTEK,
+    .device_id  = PCI_DEVICE_ID_REALTEK_8139,
+    .revision   = RTL8139_PCI_REVID, /* >=0x20 is for 8139C+ */
+    .class_id   = PCI_CLASS_NETWORK_ETHERNET,
     .qdev.props = (Property[]) {
         DEFINE_NIC_PROPERTIES(RTL8139State, conf),
         DEFINE_PROP_END_OF_LIST(),
diff --git a/hw/sh_pci.c b/hw/sh_pci.c
index e99d8db..a076cf2 100644
--- a/hw/sh_pci.c
+++ b/hw/sh_pci.c
@@ -137,8 +137,6 @@
 
 static int sh_pci_host_init(PCIDevice *d)
 {
-    pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_HITACHI);
-    pci_config_set_device_id(d->config, PCI_DEVICE_ID_HITACHI_SH7751R);
     pci_set_word(d->config + PCI_COMMAND, PCI_COMMAND_WAIT);
     pci_set_word(d->config + PCI_STATUS, PCI_STATUS_CAP_LIST |
                  PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MEDIUM);
@@ -149,6 +147,8 @@
     .qdev.name = "sh_pci_host",
     .qdev.size = sizeof(PCIDevice),
     .init      = sh_pci_host_init,
+    .vendor_id = PCI_VENDOR_ID_HITACHI,
+    .device_id = PCI_DEVICE_ID_HITACHI_SH7751R,
 };
 
 static void sh_pci_register_devices(void)
diff --git a/hw/sun4u.c b/hw/sun4u.c
index 5eb38cf..d7dcaf0 100644
--- a/hw/sun4u.c
+++ b/hw/sun4u.c
@@ -553,15 +553,11 @@
 {
     isa_bus_new(&s->qdev);
 
-    pci_config_set_vendor_id(s->config, PCI_VENDOR_ID_SUN);
-    pci_config_set_device_id(s->config, PCI_DEVICE_ID_SUN_EBUS);
     s->config[0x04] = 0x06; // command = bus master, pci mem
     s->config[0x05] = 0x00;
     s->config[0x06] = 0xa0; // status = fast back-to-back, 66MHz, no error
     s->config[0x07] = 0x03; // status = medium devsel
-    s->config[0x08] = 0x01; // revision
     s->config[0x09] = 0x00; // programming i/f
-    pci_config_set_class(s->config, PCI_CLASS_BRIDGE_OTHER);
     s->config[0x0D] = 0x0a; // latency_timer
 
     pci_register_bar(s, 0, 0x1000000, PCI_BASE_ADDRESS_SPACE_MEMORY,
@@ -575,6 +571,10 @@
     .qdev.name = "ebus",
     .qdev.size = sizeof(PCIDevice),
     .init = pci_ebus_init1,
+    .vendor_id = PCI_VENDOR_ID_SUN,
+    .device_id = PCI_DEVICE_ID_SUN_EBUS,
+    .revision = 0x01,
+    .class_id = PCI_CLASS_BRIDGE_OTHER,
 };
 
 static void pci_ebus_register(void)
diff --git a/hw/syborg_virtio.c b/hw/syborg_virtio.c
index 2f3e6da..00c7be8 100644
--- a/hw/syborg_virtio.c
+++ b/hw/syborg_virtio.c
@@ -146,7 +146,9 @@
             vdev->queue_sel = value;
         break;
     case SYBORG_VIRTIO_QUEUE_NOTIFY:
-        virtio_queue_notify(vdev, value);
+        if (value < VIRTIO_PCI_QUEUE_MAX) {
+            virtio_queue_notify(vdev, value);
+        }
         break;
     case SYBORG_VIRTIO_STATUS:
         virtio_set_status(vdev, value & 0xFF);
diff --git a/hw/unin_pci.c b/hw/unin_pci.c
index c57c0a1..d364daa 100644
--- a/hw/unin_pci.c
+++ b/hw/unin_pci.c
@@ -279,10 +279,6 @@
 
 static int unin_main_pci_host_init(PCIDevice *d)
 {
-    pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_APPLE);
-    pci_config_set_device_id(d->config, PCI_DEVICE_ID_APPLE_UNI_N_PCI);
-    d->config[0x08] = 0x00; // revision
-    pci_config_set_class(d->config, PCI_CLASS_BRIDGE_HOST);
     d->config[0x0C] = 0x08; // cache_line_size
     d->config[0x0D] = 0x10; // latency_timer
     d->config[0x34] = 0x00; // capabilities_pointer
@@ -291,10 +287,6 @@
 
 static int unin_agp_pci_host_init(PCIDevice *d)
 {
-    pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_APPLE);
-    pci_config_set_device_id(d->config, PCI_DEVICE_ID_APPLE_UNI_N_AGP);
-    d->config[0x08] = 0x00; // revision
-    pci_config_set_class(d->config, PCI_CLASS_BRIDGE_HOST);
     d->config[0x0C] = 0x08; // cache_line_size
     d->config[0x0D] = 0x10; // latency_timer
     //    d->config[0x34] = 0x80; // capabilities_pointer
@@ -303,11 +295,6 @@
 
 static int u3_agp_pci_host_init(PCIDevice *d)
 {
-    pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_APPLE);
-    pci_config_set_device_id(d->config, PCI_DEVICE_ID_APPLE_U3_AGP);
-    /* revision */
-    d->config[0x08] = 0x00;
-    pci_config_set_class(d->config, PCI_CLASS_BRIDGE_HOST);
     /* cache line size */
     d->config[0x0C] = 0x08;
     /* latency timer */
@@ -317,10 +304,6 @@
 
 static int unin_internal_pci_host_init(PCIDevice *d)
 {
-    pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_APPLE);
-    pci_config_set_device_id(d->config, PCI_DEVICE_ID_APPLE_UNI_N_I_PCI);
-    d->config[0x08] = 0x00; // revision
-    pci_config_set_class(d->config, PCI_CLASS_BRIDGE_HOST);
     d->config[0x0C] = 0x08; // cache_line_size
     d->config[0x0D] = 0x10; // latency_timer
     d->config[0x34] = 0x00; // capabilities_pointer
@@ -331,24 +314,40 @@
     .qdev.name = "uni-north",
     .qdev.size = sizeof(PCIDevice),
     .init      = unin_main_pci_host_init,
+    .vendor_id = PCI_VENDOR_ID_APPLE,
+    .device_id = PCI_DEVICE_ID_APPLE_UNI_N_PCI,
+    .revision  = 0x00,
+    .class_id  = PCI_CLASS_BRIDGE_HOST,
 };
 
 static PCIDeviceInfo u3_agp_pci_host_info = {
     .qdev.name = "u3-agp",
     .qdev.size = sizeof(PCIDevice),
     .init      = u3_agp_pci_host_init,
+    .vendor_id = PCI_VENDOR_ID_APPLE,
+    .device_id = PCI_DEVICE_ID_APPLE_U3_AGP,
+    .revision  = 0x00,
+    .class_id  = PCI_CLASS_BRIDGE_HOST,
 };
 
 static PCIDeviceInfo unin_agp_pci_host_info = {
     .qdev.name = "uni-north-agp",
     .qdev.size = sizeof(PCIDevice),
     .init      = unin_agp_pci_host_init,
+    .vendor_id = PCI_VENDOR_ID_APPLE,
+    .device_id = PCI_DEVICE_ID_APPLE_UNI_N_AGP,
+    .revision  = 0x00,
+    .class_id  = PCI_CLASS_BRIDGE_HOST,
 };
 
 static PCIDeviceInfo unin_internal_pci_host_info = {
     .qdev.name = "uni-north-pci",
     .qdev.size = sizeof(PCIDevice),
     .init      = unin_internal_pci_host_init,
+    .vendor_id = PCI_VENDOR_ID_APPLE,
+    .device_id = PCI_DEVICE_ID_APPLE_UNI_N_I_PCI,
+    .revision  = 0x00,
+    .class_id  = PCI_CLASS_BRIDGE_HOST,
 };
 
 static void unin_register_devices(void)
diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c
index c909127..e33e546 100644
--- a/hw/usb-ehci.c
+++ b/hw/usb-ehci.c
@@ -2142,6 +2142,10 @@
     .qdev.name    = "usb-ehci",
     .qdev.size    = sizeof(EHCIState),
     .init         = usb_ehci_initfn,
+    .vendor_id    = PCI_VENDOR_ID_INTEL,
+    .device_id    = PCI_DEVICE_ID_INTEL_82801D,
+    .revision     = 0x10,
+    .class_id     = PCI_CLASS_SERIAL_USB,
 };
 
 static int usb_ehci_initfn(PCIDevice *dev)
@@ -2150,12 +2154,7 @@
     uint8_t *pci_conf = s->dev.config;
     int i;
 
-    pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL);
-    pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82801D);
-    pci_set_byte(&pci_conf[PCI_REVISION_ID], 0x10);
     pci_set_byte(&pci_conf[PCI_CLASS_PROG], 0x20);
-    pci_config_set_class(pci_conf, PCI_CLASS_SERIAL_USB);
-    pci_set_byte(&pci_conf[PCI_HEADER_TYPE], PCI_HEADER_TYPE_NORMAL);
 
     /* capabilities pointer */
     pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x00);
diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c
index 832dcd6..5d2ae01 100644
--- a/hw/usb-ohci.c
+++ b/hw/usb-ohci.c
@@ -1748,11 +1748,7 @@
     OHCIPCIState *ohci = DO_UPCAST(OHCIPCIState, pci_dev, dev);
     int num_ports = 3;
 
-    pci_config_set_vendor_id(ohci->pci_dev.config, PCI_VENDOR_ID_APPLE);
-    pci_config_set_device_id(ohci->pci_dev.config,
-                             PCI_DEVICE_ID_APPLE_IPID_USB);
     ohci->pci_dev.config[PCI_CLASS_PROG] = 0x10; /* OHCI */
-    pci_config_set_class(ohci->pci_dev.config, PCI_CLASS_SERIAL_USB);
     /* TODO: RST# value should be 0. */
     ohci->pci_dev.config[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin 1 */
 
@@ -1792,6 +1788,9 @@
     .qdev.desc    = "Apple USB Controller",
     .qdev.size    = sizeof(OHCIPCIState),
     .init         = usb_ohci_initfn_pci,
+    .vendor_id    = PCI_VENDOR_ID_APPLE,
+    .device_id    = PCI_DEVICE_ID_APPLE_IPID_USB,
+    .class_id     = PCI_CLASS_SERIAL_USB,
 };
 
 static SysBusDeviceInfo ohci_sysbus_info = {
diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c
index 75cd231..405fa7b 100644
--- a/hw/usb-uhci.c
+++ b/hw/usb-uhci.c
@@ -1114,14 +1114,13 @@
     .device_destroy = uhci_device_destroy,
 };
 
-static int usb_uhci_common_initfn(UHCIState *s)
+static int usb_uhci_common_initfn(PCIDevice *dev)
 {
+    UHCIState *s = DO_UPCAST(UHCIState, dev, dev);
     uint8_t *pci_conf = s->dev.config;
     int i;
 
-    pci_conf[PCI_REVISION_ID] = 0x01; // revision number
     pci_conf[PCI_CLASS_PROG] = 0x00;
-    pci_config_set_class(pci_conf, PCI_CLASS_SERIAL_USB);
     /* TODO: reset value should be 0. */
     pci_conf[PCI_INTERRUPT_PIN] = 4; // interrupt pin 3
     pci_conf[USB_SBRN] = USB_RELEASE_1; // release number
@@ -1146,34 +1145,11 @@
     return 0;
 }
 
-static int usb_uhci_piix3_initfn(PCIDevice *dev)
-{
-    UHCIState *s = DO_UPCAST(UHCIState, dev, dev);
-    uint8_t *pci_conf = s->dev.config;
-
-    pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL);
-    pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82371SB_2);
-    return usb_uhci_common_initfn(s);
-}
-
-static int usb_uhci_piix4_initfn(PCIDevice *dev)
-{
-    UHCIState *s = DO_UPCAST(UHCIState, dev, dev);
-    uint8_t *pci_conf = s->dev.config;
-
-    pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL);
-    pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82371AB_2);
-    return usb_uhci_common_initfn(s);
-}
-
 static int usb_uhci_vt82c686b_initfn(PCIDevice *dev)
 {
     UHCIState *s = DO_UPCAST(UHCIState, dev, dev);
     uint8_t *pci_conf = s->dev.config;
 
-    pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_VIA);
-    pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_VIA_UHCI);
-
     /* USB misc control 1/2 */
     pci_set_long(pci_conf + 0x40,0x00001000);
     /* PM capability */
@@ -1181,7 +1157,7 @@
     /* USB legacy support  */
     pci_set_long(pci_conf + 0xc0,0x00002000);
 
-    return usb_uhci_common_initfn(s);
+    return usb_uhci_common_initfn(dev);
 }
 
 static PCIDeviceInfo uhci_info[] = {
@@ -1189,17 +1165,29 @@
         .qdev.name    = "piix3-usb-uhci",
         .qdev.size    = sizeof(UHCIState),
         .qdev.vmsd    = &vmstate_uhci,
-        .init         = usb_uhci_piix3_initfn,
+        .init         = usb_uhci_common_initfn,
+        .vendor_id    = PCI_VENDOR_ID_INTEL,
+        .device_id    = PCI_DEVICE_ID_INTEL_82371SB_2,
+        .revision     = 0x01,
+        .class_id     = PCI_CLASS_SERIAL_USB,
     },{
         .qdev.name    = "piix4-usb-uhci",
         .qdev.size    = sizeof(UHCIState),
         .qdev.vmsd    = &vmstate_uhci,
-        .init         = usb_uhci_piix4_initfn,
+        .init         = usb_uhci_common_initfn,
+        .vendor_id    = PCI_VENDOR_ID_INTEL,
+        .device_id    = PCI_DEVICE_ID_INTEL_82371AB_2,
+        .revision     = 0x01,
+        .class_id     = PCI_CLASS_SERIAL_USB,
     },{
         .qdev.name    = "vt82c686b-usb-uhci",
         .qdev.size    = sizeof(UHCIState),
         .qdev.vmsd    = &vmstate_uhci,
         .init         = usb_uhci_vt82c686b_initfn,
+        .vendor_id    = PCI_VENDOR_ID_VIA,
+        .device_id    = PCI_DEVICE_ID_VIA_UHCI,
+        .revision     = 0x01,
+        .class_id     = PCI_CLASS_SERIAL_USB,
     },{
         /* end of list */
     }
diff --git a/hw/versatile_pci.c b/hw/versatile_pci.c
index 2fed8a0..8e75ffc 100644
--- a/hw/versatile_pci.c
+++ b/hw/versatile_pci.c
@@ -133,12 +133,8 @@
 
 static int versatile_pci_host_init(PCIDevice *d)
 {
-    pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_XILINX);
-    /* Both boards have the same device ID.  Oh well.  */
-    pci_config_set_device_id(d->config, PCI_DEVICE_ID_XILINX_XC2VP30);
     pci_set_word(d->config + PCI_STATUS,
 		 PCI_STATUS_66MHZ | PCI_STATUS_DEVSEL_MEDIUM);
-    pci_config_set_class(d->config, PCI_CLASS_PROCESSOR_CO);
     pci_set_byte(d->config + PCI_LATENCY_TIMER, 0x10);
     return 0;
 }
@@ -147,6 +143,10 @@
     .qdev.name = "versatile_pci_host",
     .qdev.size = sizeof(PCIDevice),
     .init      = versatile_pci_host_init,
+    .vendor_id = PCI_VENDOR_ID_XILINX,
+    /* Both boards have the same device ID.  Oh well.  */
+    .device_id = PCI_DEVICE_ID_XILINX_XC2VP30,
+    .class_id  = PCI_CLASS_PROCESSOR_CO,
 };
 
 static void versatile_pci_register_devices(void)
diff --git a/hw/vga-pci.c b/hw/vga-pci.c
index ce9ec45..481f448 100644
--- a/hw/vga-pci.c
+++ b/hw/vga-pci.c
@@ -74,7 +74,6 @@
 {
      PCIVGAState *d = DO_UPCAST(PCIVGAState, dev, dev);
      VGACommonState *s = &d->vga;
-     uint8_t *pci_conf = d->dev.config;
 
      // vga + console init
      vga_common_init(s, VGA_RAM_SIZE);
@@ -83,11 +82,6 @@
      s->ds = graphic_console_init(s->update, s->invalidate,
                                   s->screen_dump, s->text_update, s);
 
-     // dummy VGA (same as Bochs ID)
-     pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_QEMU);
-     pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_QEMU_VGA);
-     pci_config_set_class(pci_conf, PCI_CLASS_DISPLAY_VGA);
-
      /* XXX: VGA_RAM_SIZE must be a power of two */
      pci_register_bar(&d->dev, 0, VGA_RAM_SIZE,
                       PCI_BASE_ADDRESS_MEM_PREFETCH, vga_map);
@@ -114,6 +108,11 @@
     .init         = pci_vga_initfn,
     .config_write = pci_vga_write_config,
     .romfile      = "vgabios-stdvga.bin",
+
+    /* dummy VGA (same as Bochs ID) */
+    .vendor_id    = PCI_VENDOR_ID_QEMU,
+    .device_id    = PCI_DEVICE_ID_QEMU_VGA,
+    .class_id     = PCI_CLASS_DISPLAY_VGA,
 };
 
 static void vga_register(void)
diff --git a/hw/vhost_net.c b/hw/vhost_net.c
index 420e05f..b6dc592 100644
--- a/hw/vhost_net.c
+++ b/hw/vhost_net.c
@@ -15,6 +15,7 @@
 
 #include "virtio-net.h"
 #include "vhost_net.h"
+#include "qemu-error.h"
 
 #include "config.h"
 
@@ -50,6 +51,9 @@
     if (!(net->dev.features & (1 << VIRTIO_RING_F_INDIRECT_DESC))) {
         features &= ~(1 << VIRTIO_RING_F_INDIRECT_DESC);
     }
+    if (!(net->dev.features & (1 << VIRTIO_RING_F_EVENT_IDX))) {
+        features &= ~(1 << VIRTIO_RING_F_EVENT_IDX);
+    }
     if (!(net->dev.features & (1 << VIRTIO_NET_F_MRG_RXBUF))) {
         features &= ~(1 << VIRTIO_NET_F_MRG_RXBUF);
     }
@@ -65,6 +69,9 @@
     if (features & (1 << VIRTIO_RING_F_INDIRECT_DESC)) {
         net->dev.acked_features |= (1 << VIRTIO_RING_F_INDIRECT_DESC);
     }
+    if (features & (1 << VIRTIO_RING_F_EVENT_IDX)) {
+        net->dev.acked_features |= (1 << VIRTIO_RING_F_EVENT_IDX);
+    }
     if (features & (1 << VIRTIO_NET_F_MRG_RXBUF)) {
         net->dev.acked_features |= (1 << VIRTIO_NET_F_MRG_RXBUF);
     }
@@ -197,6 +204,7 @@
 struct vhost_net *vhost_net_init(VLANClientState *backend, int devfd,
                                  bool force)
 {
+    error_report("vhost-net support is not compiled in");
     return NULL;
 }
 
diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c
index c018351..b3e7ba5 100644
--- a/hw/virtio-pci.c
+++ b/hw/virtio-pci.c
@@ -75,9 +75,6 @@
                                          VIRTIO_PCI_CONFIG_MSI : \
                                          VIRTIO_PCI_CONFIG_NOMSI)
 
-/* Virtio ABI version, if we increment this, we break the guest driver. */
-#define VIRTIO_PCI_ABI_VERSION          0
-
 /* How many bits to shift physical queue address written to QUEUE_PFN.
  * 12 is historical, and due to x86 page size. */
 #define VIRTIO_PCI_QUEUE_ADDR_SHIFT    12
@@ -328,7 +325,9 @@
             vdev->queue_sel = val;
         break;
     case VIRTIO_PCI_QUEUE_NOTIFY:
-        virtio_queue_notify(vdev, val);
+        if (val < VIRTIO_PCI_QUEUE_MAX) {
+            virtio_queue_notify(vdev, val);
+        }
         break;
     case VIRTIO_PCI_STATUS:
         if (!(val & VIRTIO_CONFIG_S_DRIVER_OK)) {
@@ -649,9 +648,7 @@
     .vmstate_change = virtio_pci_vmstate_change,
 };
 
-void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev,
-                            uint16_t vendor, uint16_t device,
-                            uint16_t class_code, uint8_t pif)
+void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev)
 {
     uint8_t *config;
     uint32_t size;
@@ -659,19 +656,12 @@
     proxy->vdev = vdev;
 
     config = proxy->pci_dev.config;
-    pci_config_set_vendor_id(config, vendor);
-    pci_config_set_device_id(config, device);
 
-    config[0x08] = VIRTIO_PCI_ABI_VERSION;
-
-    config[0x09] = pif;
-    pci_config_set_class(config, class_code);
-
-    config[0x2c] = vendor & 0xFF;
-    config[0x2d] = (vendor >> 8) & 0xFF;
-    config[0x2e] = vdev->device_id & 0xFF;
-    config[0x2f] = (vdev->device_id >> 8) & 0xFF;
-
+    if (proxy->class_code) {
+        pci_config_set_class(config, proxy->class_code);
+    }
+    pci_set_word(config + 0x2c, pci_get_word(config + PCI_VENDOR_ID));
+    pci_set_word(config + 0x2e, vdev->device_id);
     config[0x3d] = 1;
 
     if (vdev->nvectors && !msix_init(&proxy->pci_dev, vdev->nvectors, 1, 0)) {
@@ -715,10 +705,7 @@
         return -1;
     }
     vdev->nvectors = proxy->nvectors;
-    virtio_init_pci(proxy, vdev,
-                    PCI_VENDOR_ID_REDHAT_QUMRANET,
-                    PCI_DEVICE_ID_VIRTIO_BLOCK,
-                    proxy->class_code, 0x00);
+    virtio_init_pci(proxy, vdev);
     /* make the actual value visible */
     proxy->nvectors = vdev->nvectors;
     return 0;
@@ -756,10 +743,7 @@
     vdev->nvectors = proxy->nvectors == DEV_NVECTORS_UNSPECIFIED
                                         ? proxy->serial.max_virtserial_ports + 1
                                         : proxy->nvectors;
-    virtio_init_pci(proxy, vdev,
-                    PCI_VENDOR_ID_REDHAT_QUMRANET,
-                    PCI_DEVICE_ID_VIRTIO_CONSOLE,
-                    proxy->class_code, 0x00);
+    virtio_init_pci(proxy, vdev);
     proxy->nvectors = vdev->nvectors;
     return 0;
 }
@@ -781,11 +765,7 @@
     vdev = virtio_net_init(&pci_dev->qdev, &proxy->nic, &proxy->net);
 
     vdev->nvectors = proxy->nvectors;
-    virtio_init_pci(proxy, vdev,
-                    PCI_VENDOR_ID_REDHAT_QUMRANET,
-                    PCI_DEVICE_ID_VIRTIO_NET,
-                    PCI_CLASS_NETWORK_ETHERNET,
-                    0x00);
+    virtio_init_pci(proxy, vdev);
 
     /* make the actual value visible */
     proxy->nvectors = vdev->nvectors;
@@ -807,11 +787,7 @@
     VirtIODevice *vdev;
 
     vdev = virtio_balloon_init(&pci_dev->qdev);
-    virtio_init_pci(proxy, vdev,
-                    PCI_VENDOR_ID_REDHAT_QUMRANET,
-                    PCI_DEVICE_ID_VIRTIO_BALLOON,
-                    PCI_CLASS_MEMORY_RAM,
-                    0x00);
+    virtio_init_pci(proxy, vdev);
     return 0;
 }
 
@@ -822,6 +798,10 @@
         .qdev.size = sizeof(VirtIOPCIProxy),
         .init      = virtio_blk_init_pci,
         .exit      = virtio_blk_exit_pci,
+        .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET,
+        .device_id = PCI_DEVICE_ID_VIRTIO_BLOCK,
+        .revision  = VIRTIO_PCI_ABI_VERSION,
+        .class_id  = PCI_CLASS_STORAGE_SCSI,
         .qdev.props = (Property[]) {
             DEFINE_PROP_HEX32("class", VirtIOPCIProxy, class_code, 0),
             DEFINE_BLOCK_PROPERTIES(VirtIOPCIProxy, block),
@@ -839,6 +819,10 @@
         .init       = virtio_net_init_pci,
         .exit       = virtio_net_exit_pci,
         .romfile    = "pxe-virtio.rom",
+        .vendor_id  = PCI_VENDOR_ID_REDHAT_QUMRANET,
+        .device_id  = PCI_DEVICE_ID_VIRTIO_NET,
+        .revision   = VIRTIO_PCI_ABI_VERSION,
+        .class_id   = PCI_CLASS_NETWORK_ETHERNET,
         .qdev.props = (Property[]) {
             DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags,
                             VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, false),
@@ -859,6 +843,10 @@
         .qdev.size = sizeof(VirtIOPCIProxy),
         .init      = virtio_serial_init_pci,
         .exit      = virtio_serial_exit_pci,
+        .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET,
+        .device_id = PCI_DEVICE_ID_VIRTIO_CONSOLE,
+        .revision  = VIRTIO_PCI_ABI_VERSION,
+        .class_id  = PCI_CLASS_COMMUNICATION_OTHER,
         .qdev.props = (Property[]) {
             DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags,
                             VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
@@ -877,6 +865,10 @@
         .qdev.size = sizeof(VirtIOPCIProxy),
         .init      = virtio_balloon_init_pci,
         .exit      = virtio_exit_pci,
+        .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET,
+        .device_id = PCI_DEVICE_ID_VIRTIO_BALLOON,
+        .revision  = VIRTIO_PCI_ABI_VERSION,
+        .class_id  = PCI_CLASS_MEMORY_RAM,
         .qdev.props = (Property[]) {
             DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features),
             DEFINE_PROP_END_OF_LIST(),
diff --git a/hw/virtio-pci.h b/hw/virtio-pci.h
index a4b5fd3..b518917 100644
--- a/hw/virtio-pci.h
+++ b/hw/virtio-pci.h
@@ -37,7 +37,9 @@
     bool ioeventfd_started;
 } VirtIOPCIProxy;
 
-extern void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev,
-                            uint16_t vendor, uint16_t device,
-                            uint16_t class_code, uint8_t pif);
+void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev);
+
+/* Virtio ABI version, if we increment this, we break the guest driver. */
+#define VIRTIO_PCI_ABI_VERSION          0
+
 #endif
diff --git a/hw/virtio.c b/hw/virtio.c
index 6e8814c..e6043de 100644
--- a/hw/virtio.c
+++ b/hw/virtio.c
@@ -71,7 +71,17 @@
     VRing vring;
     target_phys_addr_t pa;
     uint16_t last_avail_idx;
+    /* Last used index value we have signalled on */
+    uint16_t signalled_used;
+
+    /* Last used index value we have signalled on */
+    bool signalled_used_valid;
+
+    /* Notification enabled? */
+    bool notification;
+
     int inuse;
+
     uint16_t vector;
     void (*handle_output)(VirtIODevice *vdev, VirtQueue *vq);
     VirtIODevice *vdev;
@@ -140,6 +150,11 @@
     return lduw_phys(pa);
 }
 
+static inline uint16_t vring_used_event(VirtQueue *vq)
+{
+    return vring_avail_ring(vq, vq->vring.num);
+}
+
 static inline void vring_used_ring_id(VirtQueue *vq, int i, uint32_t val)
 {
     target_phys_addr_t pa;
@@ -161,11 +176,11 @@
     return lduw_phys(pa);
 }
 
-static inline void vring_used_idx_increment(VirtQueue *vq, uint16_t val)
+static inline void vring_used_idx_set(VirtQueue *vq, uint16_t val)
 {
     target_phys_addr_t pa;
     pa = vq->vring.used + offsetof(VRingUsed, idx);
-    stw_phys(pa, vring_used_idx(vq) + val);
+    stw_phys(pa, val);
 }
 
 static inline void vring_used_flags_set_bit(VirtQueue *vq, int mask)
@@ -182,12 +197,26 @@
     stw_phys(pa, lduw_phys(pa) & ~mask);
 }
 
+static inline void vring_avail_event(VirtQueue *vq, uint16_t val)
+{
+    target_phys_addr_t pa;
+    if (!vq->notification) {
+        return;
+    }
+    pa = vq->vring.used + offsetof(VRingUsed, ring[vq->vring.num]);
+    stw_phys(pa, val);
+}
+
 void virtio_queue_set_notification(VirtQueue *vq, int enable)
 {
-    if (enable)
+    vq->notification = enable;
+    if (vq->vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX)) {
+        vring_avail_event(vq, vring_avail_idx(vq));
+    } else if (enable) {
         vring_used_flags_unset_bit(vq, VRING_USED_F_NO_NOTIFY);
-    else
+    } else {
         vring_used_flags_set_bit(vq, VRING_USED_F_NO_NOTIFY);
+    }
 }
 
 int virtio_queue_ready(VirtQueue *vq)
@@ -233,11 +262,16 @@
 
 void virtqueue_flush(VirtQueue *vq, unsigned int count)
 {
+    uint16_t old, new;
     /* Make sure buffer is written before we update index. */
     wmb();
     trace_virtqueue_flush(vq, count);
-    vring_used_idx_increment(vq, count);
+    old = vring_used_idx(vq);
+    new = old + count;
+    vring_used_idx_set(vq, new);
     vq->inuse -= count;
+    if (unlikely((int16_t)(new - vq->signalled_used) < (uint16_t)(new - old)))
+        vq->signalled_used_valid = false;
 }
 
 void virtqueue_push(VirtQueue *vq, const VirtQueueElement *elem,
@@ -394,6 +428,9 @@
     max = vq->vring.num;
 
     i = head = virtqueue_get_head(vq, vq->last_avail_idx++);
+    if (vq->vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX)) {
+        vring_avail_event(vq, vring_avail_idx(vq));
+    }
 
     if (vring_desc_flags(desc_pa, i) & VRING_DESC_F_INDIRECT) {
         if (vring_desc_len(desc_pa, i) % sizeof(VRingDesc)) {
@@ -477,6 +514,9 @@
         vdev->vq[i].last_avail_idx = 0;
         vdev->vq[i].pa = 0;
         vdev->vq[i].vector = VIRTIO_NO_VECTOR;
+        vdev->vq[i].signalled_used = 0;
+        vdev->vq[i].signalled_used_valid = false;
+        vdev->vq[i].notification = true;
     }
 }
 
@@ -585,9 +625,7 @@
 
 void virtio_queue_notify(VirtIODevice *vdev, int n)
 {
-    if (n < VIRTIO_PCI_QUEUE_MAX) {
-        virtio_queue_notify_vq(&vdev->vq[n]);
-    }
+    virtio_queue_notify_vq(&vdev->vq[n]);
 }
 
 uint16_t virtio_queue_vector(VirtIODevice *vdev, int n)
@@ -628,13 +666,45 @@
     virtio_notify_vector(vq->vdev, vq->vector);
 }
 
+/* Assuming a given event_idx value from the other size, if
+ * we have just incremented index from old to new_idx,
+ * should we trigger an event? */
+static inline int vring_need_event(uint16_t event, uint16_t new, uint16_t old)
+{
+	/* Note: Xen has similar logic for notification hold-off
+	 * in include/xen/interface/io/ring.h with req_event and req_prod
+	 * corresponding to event_idx + 1 and new respectively.
+	 * Note also that req_event and req_prod in Xen start at 1,
+	 * event indexes in virtio start at 0. */
+	return (uint16_t)(new - event - 1) < (uint16_t)(new - old);
+}
+
+static bool vring_notify(VirtIODevice *vdev, VirtQueue *vq)
+{
+    uint16_t old, new;
+    bool v;
+    /* Always notify when queue is empty (when feature acknowledge) */
+    if (((vdev->guest_features & (1 << VIRTIO_F_NOTIFY_ON_EMPTY)) &&
+         !vq->inuse && vring_avail_idx(vq) == vq->last_avail_idx)) {
+        return true;
+    }
+
+    if (!(vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX))) {
+        return !(vring_avail_flags(vq) & VRING_AVAIL_F_NO_INTERRUPT);
+    }
+
+    v = vq->signalled_used_valid;
+    vq->signalled_used_valid = true;
+    old = vq->signalled_used;
+    new = vq->signalled_used = vring_used_idx(vq);
+    return !v || vring_need_event(vring_used_event(vq), new, old);
+}
+
 void virtio_notify(VirtIODevice *vdev, VirtQueue *vq)
 {
-    /* Always notify when queue is empty (when feature acknowledge) */
-    if ((vring_avail_flags(vq) & VRING_AVAIL_F_NO_INTERRUPT) &&
-        (!(vdev->guest_features & (1 << VIRTIO_F_NOTIFY_ON_EMPTY)) ||
-         (vq->inuse || vring_avail_idx(vq) != vq->last_avail_idx)))
+    if (!vring_notify(vdev, vq)) {
         return;
+    }
 
     trace_virtio_notify(vdev, vq);
     vdev->isr |= 0x01;
@@ -717,6 +787,8 @@
         vdev->vq[i].vring.num = qemu_get_be32(f);
         vdev->vq[i].pa = qemu_get_be64(f);
         qemu_get_be16s(f, &vdev->vq[i].last_avail_idx);
+        vdev->vq[i].signalled_used_valid = false;
+        vdev->vq[i].notification = true;
 
         if (vdev->vq[i].pa) {
             uint16_t nheads;
@@ -789,6 +861,7 @@
     vdev->queue_sel = 0;
     vdev->config_vector = VIRTIO_NO_VECTOR;
     vdev->vq = qemu_mallocz(sizeof(VirtQueue) * VIRTIO_PCI_QUEUE_MAX);
+    vdev->vm_running = vm_running;
     for(i = 0; i < VIRTIO_PCI_QUEUE_MAX; i++) {
         vdev->vq[i].vector = VIRTIO_NO_VECTOR;
         vdev->vq[i].vdev = vdev;
diff --git a/hw/virtio.h b/hw/virtio.h
index bc72289..69e6bb1 100644
--- a/hw/virtio.h
+++ b/hw/virtio.h
@@ -46,6 +46,11 @@
 #define VIRTIO_F_NOTIFY_ON_EMPTY        24
 /* We support indirect buffer descriptors */
 #define VIRTIO_RING_F_INDIRECT_DESC     28
+/* The Guest publishes the used index for which it expects an interrupt
+ * at the end of the avail ring. Host should ignore the avail->flags field. */
+/* The Host publishes the avail index for which it expects a kick
+ * at the end of the used ring. Guest should ignore the used->flags field. */
+#define VIRTIO_RING_F_EVENT_IDX         29
 /* A guest should never accept this.  It implies negotiation is broken. */
 #define VIRTIO_F_BAD_FEATURE		30
 
@@ -210,7 +215,9 @@
 
 #define DEFINE_VIRTIO_COMMON_FEATURES(_state, _field) \
 	DEFINE_PROP_BIT("indirect_desc", _state, _field, \
-			VIRTIO_RING_F_INDIRECT_DESC, true)
+			VIRTIO_RING_F_INDIRECT_DESC, true), \
+	DEFINE_PROP_BIT("event_idx", _state, _field, \
+			VIRTIO_RING_F_EVENT_IDX, true)
 
 target_phys_addr_t virtio_queue_get_desc_addr(VirtIODevice *vdev, int n);
 target_phys_addr_t virtio_queue_get_avail_addr(VirtIODevice *vdev, int n);
diff --git a/hw/vmware_vga.c b/hw/vmware_vga.c
index 4656767..354c221 100644
--- a/hw/vmware_vga.c
+++ b/hw/vmware_vga.c
@@ -1280,15 +1280,8 @@
     struct pci_vmsvga_state_s *s =
         DO_UPCAST(struct pci_vmsvga_state_s, card, dev);
 
-    pci_config_set_vendor_id(s->card.config, PCI_VENDOR_ID_VMWARE);
-    pci_config_set_device_id(s->card.config, SVGA_PCI_DEVICE_ID);
-    pci_config_set_class(s->card.config, PCI_CLASS_DISPLAY_VGA);
     s->card.config[PCI_CACHE_LINE_SIZE]	= 0x08;		/* Cache line size */
     s->card.config[PCI_LATENCY_TIMER] = 0x40;		/* Latency timer */
-    s->card.config[PCI_SUBSYSTEM_VENDOR_ID] = PCI_VENDOR_ID_VMWARE & 0xff;
-    s->card.config[PCI_SUBSYSTEM_VENDOR_ID + 1]	= PCI_VENDOR_ID_VMWARE >> 8;
-    s->card.config[PCI_SUBSYSTEM_ID] = SVGA_PCI_DEVICE_ID & 0xff;
-    s->card.config[PCI_SUBSYSTEM_ID + 1] = SVGA_PCI_DEVICE_ID >> 8;
     s->card.config[PCI_INTERRUPT_LINE] = 0xff;		/* End */
 
     pci_register_bar(&s->card, 0, 0x10,
@@ -1316,6 +1309,12 @@
     .no_hotplug   = 1,
     .init         = pci_vmsvga_initfn,
     .romfile      = "vgabios-vmware.bin",
+
+    .vendor_id    =  PCI_VENDOR_ID_VMWARE,
+    .device_id    = SVGA_PCI_DEVICE_ID,
+    .class_id     = PCI_CLASS_DISPLAY_VGA,
+    .subsystem_vendor_id = PCI_VENDOR_ID_VMWARE,
+    .subsystem_id = SVGA_PCI_DEVICE_ID,
 };
 
 static void vmsvga_register(void)
diff --git a/hw/vt82c686.c b/hw/vt82c686.c
index ca8f826..5c973ed 100644
--- a/hw/vt82c686.c
+++ b/hw/vt82c686.c
@@ -326,11 +326,6 @@
     VT686AC97State *s = DO_UPCAST(VT686AC97State, dev, dev);
     uint8_t *pci_conf = s->dev.config;
 
-    pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_VIA);
-    pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_VIA_AC97);
-    pci_config_set_class(pci_conf, PCI_CLASS_MULTIMEDIA_AUDIO);
-    pci_config_set_revision(pci_conf, 0x50);
-
     pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_INVALIDATE |
                  PCI_COMMAND_PARITY);
     pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_CAP_LIST |
@@ -353,6 +348,10 @@
     .qdev.desc          = "AC97",
     .qdev.size          = sizeof(VT686AC97State),
     .init               = vt82c686b_ac97_initfn,
+    .vendor_id          = PCI_VENDOR_ID_VIA,
+    .device_id          = PCI_DEVICE_ID_VIA_AC97,
+    .revision           = 0x50,
+    .class_id           = PCI_CLASS_MULTIMEDIA_AUDIO,
 };
 
 static void vt82c686b_ac97_register(void)
@@ -367,11 +366,6 @@
     VT686MC97State *s = DO_UPCAST(VT686MC97State, dev, dev);
     uint8_t *pci_conf = s->dev.config;
 
-    pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_VIA);
-    pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_VIA_MC97);
-    pci_config_set_class(pci_conf, PCI_CLASS_COMMUNICATION_OTHER);
-    pci_config_set_revision(pci_conf, 0x30);
-
     pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_INVALIDATE |
                  PCI_COMMAND_VGA_PALETTE);
     pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_DEVSEL_MEDIUM);
@@ -393,6 +387,10 @@
     .qdev.desc          = "MC97",
     .qdev.size          = sizeof(VT686MC97State),
     .init               = vt82c686b_mc97_initfn,
+    .vendor_id          = PCI_VENDOR_ID_VIA,
+    .device_id          = PCI_DEVICE_ID_VIA_MC97,
+    .class_id           = PCI_CLASS_COMMUNICATION_OTHER,
+    .revision           = 0x30,
 };
 
 static void vt82c686b_mc97_register(void)
@@ -409,11 +407,6 @@
     uint8_t *pci_conf;
 
     pci_conf = s->dev.config;
-    pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_VIA);
-    pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_VIA_ACPI);
-    pci_config_set_class(pci_conf, PCI_CLASS_BRIDGE_OTHER);
-    pci_config_set_revision(pci_conf, 0x40);
-
     pci_set_word(pci_conf + PCI_COMMAND, 0);
     pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_FAST_BACK |
                  PCI_STATUS_DEVSEL_MEDIUM);
@@ -462,6 +455,10 @@
     .qdev.vmsd          = &vmstate_acpi,
     .init               = vt82c686b_pm_initfn,
     .config_write       = pm_write_config,
+    .vendor_id          = PCI_VENDOR_ID_VIA,
+    .device_id          = PCI_DEVICE_ID_VIA_ACPI,
+    .class_id           = PCI_CLASS_BRIDGE_OTHER,
+    .revision           = 0x40,
     .qdev.props         = (Property[]) {
         DEFINE_PROP_UINT32("smb_io_base", VT686PMState, smb_io_base, 0),
         DEFINE_PROP_END_OF_LIST(),
@@ -496,11 +493,7 @@
     isa_bus_new(&d->qdev);
 
     pci_conf = d->config;
-    pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_VIA);
-    pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_VIA_ISA_BRIDGE);
-    pci_config_set_class(pci_conf, PCI_CLASS_BRIDGE_ISA);
     pci_config_set_prog_interface(pci_conf, 0x0);
-    pci_config_set_revision(pci_conf,0x40); /* Revision 4.0 */
 
     wmask = d->wmask;
     for (i = 0x00; i < 0xff; i++) {
@@ -531,6 +524,10 @@
     .qdev.no_user = 1,
     .init         = vt82c686b_initfn,
     .config_write = vt82c686b_write_config,
+    .vendor_id    = PCI_VENDOR_ID_VIA,
+    .device_id    = PCI_DEVICE_ID_VIA_ISA_BRIDGE,
+    .class_id     = PCI_CLASS_BRIDGE_ISA,
+    .revision     = 0x40,
 };
 
 static void vt82c686b_register(void)
diff --git a/hw/wdt_i6300esb.c b/hw/wdt_i6300esb.c
index 0791721..53786ce 100644
--- a/hw/wdt_i6300esb.c
+++ b/hw/wdt_i6300esb.c
@@ -381,7 +381,6 @@
 static int i6300esb_init(PCIDevice *dev)
 {
     I6300State *d = DO_UPCAST(I6300State, dev, dev);
-    uint8_t *pci_conf;
     int io_mem;
     static CPUReadMemoryFunc * const mem_read[3] = {
         i6300esb_mem_readb,
@@ -399,11 +398,6 @@
     d->timer = qemu_new_timer_ns(vm_clock, i6300esb_timer_expired, d);
     d->previous_reboot_flag = 0;
 
-    pci_conf = d->dev.config;
-    pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL);
-    pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_ESB_9);
-    pci_config_set_class(pci_conf, PCI_CLASS_SYSTEM_OTHER);
-
     io_mem = cpu_register_io_memory(mem_read, mem_write, d,
                                     DEVICE_NATIVE_ENDIAN);
     pci_register_bar_simple(&d->dev, 0, 0x10, 0, io_mem);
@@ -425,6 +419,9 @@
     .config_read  = i6300esb_config_read,
     .config_write = i6300esb_config_write,
     .init         = i6300esb_init,
+    .vendor_id    = PCI_VENDOR_ID_INTEL,
+    .device_id    = PCI_DEVICE_ID_INTEL_ESB_9,
+    .class_id     = PCI_CLASS_SYSTEM_OTHER,
 };
 
 static void i6300esb_register_devices(void)
diff --git a/hw/xen_common.h b/hw/xen_common.h
index a1958a0..2c79af6 100644
--- a/hw/xen_common.h
+++ b/hw/xen_common.h
@@ -71,6 +71,20 @@
         (xc_handle, domid, nr_extents, extent_order, mem_flags, extent_start);
 }
 
+static inline int xc_domain_add_to_physmap(int xc_handle, uint32_t domid,
+                                           unsigned int space, unsigned long idx,
+                                           xen_pfn_t gpfn)
+{
+    struct xen_add_to_physmap xatp = {
+        .domid = domid,
+        .space = space,
+        .idx = idx,
+        .gpfn = gpfn,
+    };
+
+    return xc_memory_op(xc_handle, XENMEM_add_to_physmap, &xatp);
+}
+
 
 /* Xen 4.1 */
 #else
diff --git a/hw/xen_platform.c b/hw/xen_platform.c
new file mode 100644
index 0000000..b167eee
--- /dev/null
+++ b/hw/xen_platform.c
@@ -0,0 +1,340 @@
+/*
+ * XEN platform pci device, formerly known as the event channel device
+ *
+ * Copyright (c) 2003-2004 Intel Corp.
+ * Copyright (c) 2006 XenSource
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <assert.h>
+
+#include "hw.h"
+#include "pc.h"
+#include "pci.h"
+#include "irq.h"
+#include "xen_common.h"
+#include "net.h"
+#include "xen_backend.h"
+#include "rwhandler.h"
+#include "trace.h"
+
+#include <xenguest.h>
+
+//#define DEBUG_PLATFORM
+
+#ifdef DEBUG_PLATFORM
+#define DPRINTF(fmt, ...) do { \
+    fprintf(stderr, "xen_platform: " fmt, ## __VA_ARGS__); \
+} while (0)
+#else
+#define DPRINTF(fmt, ...) do { } while (0)
+#endif
+
+#define PFFLAG_ROM_LOCK 1 /* Sets whether ROM memory area is RW or RO */
+
+typedef struct PCIXenPlatformState {
+    PCIDevice  pci_dev;
+    uint8_t flags; /* used only for version_id == 2 */
+    int drivers_blacklisted;
+    uint16_t driver_product_version;
+
+    /* Log from guest drivers */
+    char log_buffer[4096];
+    int log_buffer_off;
+} PCIXenPlatformState;
+
+#define XEN_PLATFORM_IOPORT 0x10
+
+/* Send bytes to syslog */
+static void log_writeb(PCIXenPlatformState *s, char val)
+{
+    if (val == '\n' || s->log_buffer_off == sizeof(s->log_buffer) - 1) {
+        /* Flush buffer */
+        s->log_buffer[s->log_buffer_off] = 0;
+        trace_xen_platform_log(s->log_buffer);
+        s->log_buffer_off = 0;
+    } else {
+        s->log_buffer[s->log_buffer_off++] = val;
+    }
+}
+
+/* Xen Platform, Fixed IOPort */
+
+static void platform_fixed_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
+{
+    PCIXenPlatformState *s = opaque;
+
+    switch (addr - XEN_PLATFORM_IOPORT) {
+    case 0:
+        /* TODO: */
+        /* Unplug devices.  Value is a bitmask of which devices to
+           unplug, with bit 0 the IDE devices, bit 1 the network
+           devices, and bit 2 the non-primary-master IDE devices. */
+        break;
+    case 2:
+        switch (val) {
+        case 1:
+            DPRINTF("Citrix Windows PV drivers loaded in guest\n");
+            break;
+        case 0:
+            DPRINTF("Guest claimed to be running PV product 0?\n");
+            break;
+        default:
+            DPRINTF("Unknown PV product %d loaded in guest\n", val);
+            break;
+        }
+        s->driver_product_version = val;
+        break;
+    }
+}
+
+static void platform_fixed_ioport_writel(void *opaque, uint32_t addr,
+                                         uint32_t val)
+{
+    switch (addr - XEN_PLATFORM_IOPORT) {
+    case 0:
+        /* PV driver version */
+        break;
+    }
+}
+
+static void platform_fixed_ioport_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+    PCIXenPlatformState *s = opaque;
+
+    switch (addr - XEN_PLATFORM_IOPORT) {
+    case 0: /* Platform flags */ {
+        hvmmem_type_t mem_type = (val & PFFLAG_ROM_LOCK) ?
+            HVMMEM_ram_ro : HVMMEM_ram_rw;
+        if (xc_hvm_set_mem_type(xen_xc, xen_domid, mem_type, 0xc0, 0x40)) {
+            DPRINTF("unable to change ro/rw state of ROM memory area!\n");
+        } else {
+            s->flags = val & PFFLAG_ROM_LOCK;
+            DPRINTF("changed ro/rw state of ROM memory area. now is %s state.\n",
+                    (mem_type == HVMMEM_ram_ro ? "ro":"rw"));
+        }
+        break;
+    }
+    case 2:
+        log_writeb(s, val);
+        break;
+    }
+}
+
+static uint32_t platform_fixed_ioport_readw(void *opaque, uint32_t addr)
+{
+    PCIXenPlatformState *s = opaque;
+
+    switch (addr - XEN_PLATFORM_IOPORT) {
+    case 0:
+        if (s->drivers_blacklisted) {
+            /* The drivers will recognise this magic number and refuse
+             * to do anything. */
+            return 0xd249;
+        } else {
+            /* Magic value so that you can identify the interface. */
+            return 0x49d2;
+        }
+    default:
+        return 0xffff;
+    }
+}
+
+static uint32_t platform_fixed_ioport_readb(void *opaque, uint32_t addr)
+{
+    PCIXenPlatformState *s = opaque;
+
+    switch (addr - XEN_PLATFORM_IOPORT) {
+    case 0:
+        /* Platform flags */
+        return s->flags;
+    case 2:
+        /* Version number */
+        return 1;
+    default:
+        return 0xff;
+    }
+}
+
+static void platform_fixed_ioport_reset(void *opaque)
+{
+    PCIXenPlatformState *s = opaque;
+
+    platform_fixed_ioport_writeb(s, XEN_PLATFORM_IOPORT, 0);
+}
+
+static void platform_fixed_ioport_init(PCIXenPlatformState* s)
+{
+    register_ioport_write(XEN_PLATFORM_IOPORT, 16, 4, platform_fixed_ioport_writel, s);
+    register_ioport_write(XEN_PLATFORM_IOPORT, 16, 2, platform_fixed_ioport_writew, s);
+    register_ioport_write(XEN_PLATFORM_IOPORT, 16, 1, platform_fixed_ioport_writeb, s);
+    register_ioport_read(XEN_PLATFORM_IOPORT, 16, 2, platform_fixed_ioport_readw, s);
+    register_ioport_read(XEN_PLATFORM_IOPORT, 16, 1, platform_fixed_ioport_readb, s);
+}
+
+/* Xen Platform PCI Device */
+
+static uint32_t xen_platform_ioport_readb(void *opaque, uint32_t addr)
+{
+    addr &= 0xff;
+
+    if (addr == 0) {
+        return platform_fixed_ioport_readb(opaque, XEN_PLATFORM_IOPORT);
+    } else {
+        return ~0u;
+    }
+}
+
+static void xen_platform_ioport_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+    PCIXenPlatformState *s = opaque;
+
+    addr &= 0xff;
+    val  &= 0xff;
+
+    switch (addr) {
+    case 0: /* Platform flags */
+        platform_fixed_ioport_writeb(opaque, XEN_PLATFORM_IOPORT, val);
+        break;
+    case 8:
+        log_writeb(s, val);
+        break;
+    default:
+        break;
+    }
+}
+
+static void platform_ioport_map(PCIDevice *pci_dev, int region_num, pcibus_t addr, pcibus_t size, int type)
+{
+    PCIXenPlatformState *d = DO_UPCAST(PCIXenPlatformState, pci_dev, pci_dev);
+
+    register_ioport_write(addr, size, 1, xen_platform_ioport_writeb, d);
+    register_ioport_read(addr, size, 1, xen_platform_ioport_readb, d);
+}
+
+static uint32_t platform_mmio_read(ReadWriteHandler *handler, pcibus_t addr, int len)
+{
+    DPRINTF("Warning: attempted read from physical address "
+            "0x" TARGET_FMT_plx " in xen platform mmio space\n", addr);
+
+    return 0;
+}
+
+static void platform_mmio_write(ReadWriteHandler *handler, pcibus_t addr,
+                                uint32_t val, int len)
+{
+    DPRINTF("Warning: attempted write of 0x%x to physical "
+            "address 0x" TARGET_FMT_plx " in xen platform mmio space\n",
+            val, addr);
+}
+
+static ReadWriteHandler platform_mmio_handler = {
+    .read = &platform_mmio_read,
+    .write = &platform_mmio_write,
+};
+
+static void platform_mmio_map(PCIDevice *d, int region_num,
+                              pcibus_t addr, pcibus_t size, int type)
+{
+    int mmio_io_addr;
+
+    mmio_io_addr = cpu_register_io_memory_simple(&platform_mmio_handler,
+                                                 DEVICE_NATIVE_ENDIAN);
+
+    cpu_register_physical_memory(addr, size, mmio_io_addr);
+}
+
+static int xen_platform_post_load(void *opaque, int version_id)
+{
+    PCIXenPlatformState *s = opaque;
+
+    platform_fixed_ioport_writeb(s, XEN_PLATFORM_IOPORT, s->flags);
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_xen_platform = {
+    .name = "platform",
+    .version_id = 4,
+    .minimum_version_id = 4,
+    .minimum_version_id_old = 4,
+    .post_load = xen_platform_post_load,
+    .fields = (VMStateField []) {
+        VMSTATE_PCI_DEVICE(pci_dev, PCIXenPlatformState),
+        VMSTATE_UINT8(flags, PCIXenPlatformState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static int xen_platform_initfn(PCIDevice *dev)
+{
+    PCIXenPlatformState *d = DO_UPCAST(PCIXenPlatformState, pci_dev, dev);
+    uint8_t *pci_conf;
+
+    pci_conf = d->pci_dev.config;
+
+    pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_XENSOURCE);
+    pci_config_set_device_id(pci_conf, 0x0001);
+    pci_set_word(pci_conf + PCI_SUBSYSTEM_VENDOR_ID, PCI_VENDOR_ID_XENSOURCE);
+    pci_set_word(pci_conf + PCI_SUBSYSTEM_ID, 0x0001);
+
+    pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
+
+    pci_config_set_revision(pci_conf, 1);
+    pci_config_set_prog_interface(pci_conf, 0);
+
+    pci_config_set_class(pci_conf, PCI_CLASS_OTHERS << 8 | 0x80);
+
+    pci_conf[PCI_INTERRUPT_PIN] = 1;
+
+    pci_register_bar(&d->pci_dev, 0, 0x100,
+            PCI_BASE_ADDRESS_SPACE_IO, platform_ioport_map);
+
+    /* reserve 16MB mmio address for share memory*/
+    pci_register_bar(&d->pci_dev, 1, 0x1000000,
+            PCI_BASE_ADDRESS_MEM_PREFETCH, platform_mmio_map);
+
+    platform_fixed_ioport_init(d);
+
+    return 0;
+}
+
+static void platform_reset(DeviceState *dev)
+{
+    PCIXenPlatformState *s = DO_UPCAST(PCIXenPlatformState, pci_dev.qdev, dev);
+
+    platform_fixed_ioport_reset(s);
+}
+
+static PCIDeviceInfo xen_platform_info = {
+    .init = xen_platform_initfn,
+    .qdev.name = "xen-platform",
+    .qdev.desc = "XEN platform pci device",
+    .qdev.size = sizeof(PCIXenPlatformState),
+    .qdev.vmsd = &vmstate_xen_platform,
+    .qdev.reset = platform_reset,
+};
+
+static void xen_platform_register(void)
+{
+    pci_qdev_register(&xen_platform_info);
+}
+
+device_init(xen_platform_register);
diff --git a/hw/xio3130_downstream.c b/hw/xio3130_downstream.c
index 5aa6a6b..d3c387d 100644
--- a/hw/xio3130_downstream.c
+++ b/hw/xio3130_downstream.c
@@ -69,9 +69,6 @@
     }
 
     pcie_port_init_reg(d);
-    pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_TI);
-    pci_config_set_device_id(d->config, PCI_DEVICE_ID_TI_XIO3130D);
-    d->config[PCI_REVISION_ID] = XIO3130_REVISION;
 
     rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR,
                   XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
@@ -182,6 +179,9 @@
     .config_write = xio3130_downstream_write_config,
     .init = xio3130_downstream_initfn,
     .exit = xio3130_downstream_exitfn,
+    .vendor_id = PCI_VENDOR_ID_TI,
+    .device_id = PCI_DEVICE_ID_TI_XIO3130D,
+    .revision = XIO3130_REVISION,
 
     .qdev.props = (Property[]) {
         DEFINE_PROP_UINT8("port", PCIESlot, port.port, 0),
diff --git a/hw/xio3130_upstream.c b/hw/xio3130_upstream.c
index a7640f5..8283695 100644
--- a/hw/xio3130_upstream.c
+++ b/hw/xio3130_upstream.c
@@ -65,9 +65,6 @@
     }
 
     pcie_port_init_reg(d);
-    pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_TI);
-    pci_config_set_device_id(d->config, PCI_DEVICE_ID_TI_XIO3130U);
-    d->config[PCI_REVISION_ID] = XIO3130_REVISION;
 
     rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR,
                   XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
@@ -159,6 +156,9 @@
     .config_write = xio3130_upstream_write_config,
     .init = xio3130_upstream_initfn,
     .exit = xio3130_upstream_exitfn,
+    .vendor_id = PCI_VENDOR_ID_TI,
+    .device_id = PCI_DEVICE_ID_TI_XIO3130U,
+    .revision = XIO3130_REVISION,
 
     .qdev.props = (Property[]) {
         DEFINE_PROP_UINT8("port", PCIEPort, port, 0),
diff --git a/scripts/get_maintainer.pl b/scripts/get_maintainer.pl
new file mode 100755
index 0000000..d9c48e0
--- /dev/null
+++ b/scripts/get_maintainer.pl
@@ -0,0 +1,2149 @@
+#!/usr/bin/perl -w
+# (c) 2007, Joe Perches <joe@perches.com>
+#           created from checkpatch.pl
+#
+# Print selected MAINTAINERS information for
+# the files modified in a patch or for a file
+#
+# usage: perl scripts/get_maintainer.pl [OPTIONS] <patch>
+#        perl scripts/get_maintainer.pl [OPTIONS] -f <file>
+#
+# Licensed under the terms of the GNU GPL License version 2
+
+use strict;
+
+my $P = $0;
+my $V = '0.26';
+
+use Getopt::Long qw(:config no_auto_abbrev);
+
+my $lk_path = "./";
+my $email = 1;
+my $email_usename = 1;
+my $email_maintainer = 1;
+my $email_list = 1;
+my $email_subscriber_list = 0;
+my $email_git_penguin_chiefs = 0;
+my $email_git = 0;
+my $email_git_all_signature_types = 0;
+my $email_git_blame = 0;
+my $email_git_blame_signatures = 1;
+my $email_git_fallback = 1;
+my $email_git_min_signatures = 1;
+my $email_git_max_maintainers = 5;
+my $email_git_min_percent = 5;
+my $email_git_since = "1-year-ago";
+my $email_hg_since = "-365";
+my $interactive = 0;
+my $email_remove_duplicates = 1;
+my $email_use_mailmap = 1;
+my $output_multiline = 1;
+my $output_separator = ", ";
+my $output_roles = 0;
+my $output_rolestats = 1;
+my $scm = 0;
+my $web = 0;
+my $subsystem = 0;
+my $status = 0;
+my $keywords = 1;
+my $sections = 0;
+my $file_emails = 0;
+my $from_filename = 0;
+my $pattern_depth = 0;
+my $version = 0;
+my $help = 0;
+
+my $vcs_used = 0;
+
+my $exit = 0;
+
+my %commit_author_hash;
+my %commit_signer_hash;
+
+my @penguin_chief = ();
+push(@penguin_chief, "Linus Torvalds:torvalds\@linux-foundation.org");
+#Andrew wants in on most everything - 2009/01/14
+#push(@penguin_chief, "Andrew Morton:akpm\@linux-foundation.org");
+
+my @penguin_chief_names = ();
+foreach my $chief (@penguin_chief) {
+    if ($chief =~ m/^(.*):(.*)/) {
+	my $chief_name = $1;
+	my $chief_addr = $2;
+	push(@penguin_chief_names, $chief_name);
+    }
+}
+my $penguin_chiefs = "\(" . join("|", @penguin_chief_names) . "\)";
+
+# Signature types of people who are either
+# 	a) responsible for the code in question, or
+# 	b) familiar enough with it to give relevant feedback
+my @signature_tags = ();
+push(@signature_tags, "Signed-off-by:");
+push(@signature_tags, "Reviewed-by:");
+push(@signature_tags, "Acked-by:");
+
+# rfc822 email address - preloaded methods go here.
+my $rfc822_lwsp = "(?:(?:\\r\\n)?[ \\t])";
+my $rfc822_char = '[\\000-\\377]';
+
+# VCS command support: class-like functions and strings
+
+my %VCS_cmds;
+
+my %VCS_cmds_git = (
+    "execute_cmd" => \&git_execute_cmd,
+    "available" => '(which("git") ne "") && (-d ".git")',
+    "find_signers_cmd" =>
+	"git log --no-color --since=\$email_git_since " .
+	    '--format="GitCommit: %H%n' .
+		      'GitAuthor: %an <%ae>%n' .
+		      'GitDate: %aD%n' .
+		      'GitSubject: %s%n' .
+		      '%b%n"' .
+	    " -- \$file",
+    "find_commit_signers_cmd" =>
+	"git log --no-color " .
+	    '--format="GitCommit: %H%n' .
+		      'GitAuthor: %an <%ae>%n' .
+		      'GitDate: %aD%n' .
+		      'GitSubject: %s%n' .
+		      '%b%n"' .
+	    " -1 \$commit",
+    "find_commit_author_cmd" =>
+	"git log --no-color " .
+	    '--format="GitCommit: %H%n' .
+		      'GitAuthor: %an <%ae>%n' .
+		      'GitDate: %aD%n' .
+		      'GitSubject: %s%n"' .
+	    " -1 \$commit",
+    "blame_range_cmd" => "git blame -l -L \$diff_start,+\$diff_length \$file",
+    "blame_file_cmd" => "git blame -l \$file",
+    "commit_pattern" => "^GitCommit: ([0-9a-f]{40,40})",
+    "blame_commit_pattern" => "^([0-9a-f]+) ",
+    "author_pattern" => "^GitAuthor: (.*)",
+    "subject_pattern" => "^GitSubject: (.*)",
+);
+
+my %VCS_cmds_hg = (
+    "execute_cmd" => \&hg_execute_cmd,
+    "available" => '(which("hg") ne "") && (-d ".hg")',
+    "find_signers_cmd" =>
+	"hg log --date=\$email_hg_since " .
+	    "--template='HgCommit: {node}\\n" .
+	                "HgAuthor: {author}\\n" .
+			"HgSubject: {desc}\\n'" .
+	    " -- \$file",
+    "find_commit_signers_cmd" =>
+	"hg log " .
+	    "--template='HgSubject: {desc}\\n'" .
+	    " -r \$commit",
+    "find_commit_author_cmd" =>
+	"hg log " .
+	    "--template='HgCommit: {node}\\n" .
+		        "HgAuthor: {author}\\n" .
+			"HgSubject: {desc|firstline}\\n'" .
+	    " -r \$commit",
+    "blame_range_cmd" => "",		# not supported
+    "blame_file_cmd" => "hg blame -n \$file",
+    "commit_pattern" => "^HgCommit: ([0-9a-f]{40,40})",
+    "blame_commit_pattern" => "^([ 0-9a-f]+):",
+    "author_pattern" => "^HgAuthor: (.*)",
+    "subject_pattern" => "^HgSubject: (.*)",
+);
+
+my $conf = which_conf(".get_maintainer.conf");
+if (-f $conf) {
+    my @conf_args;
+    open(my $conffile, '<', "$conf")
+	or warn "$P: Can't find a readable .get_maintainer.conf file $!\n";
+
+    while (<$conffile>) {
+	my $line = $_;
+
+	$line =~ s/\s*\n?$//g;
+	$line =~ s/^\s*//g;
+	$line =~ s/\s+/ /g;
+
+	next if ($line =~ m/^\s*#/);
+	next if ($line =~ m/^\s*$/);
+
+	my @words = split(" ", $line);
+	foreach my $word (@words) {
+	    last if ($word =~ m/^#/);
+	    push (@conf_args, $word);
+	}
+    }
+    close($conffile);
+    unshift(@ARGV, @conf_args) if @conf_args;
+}
+
+if (!GetOptions(
+		'email!' => \$email,
+		'git!' => \$email_git,
+		'git-all-signature-types!' => \$email_git_all_signature_types,
+		'git-blame!' => \$email_git_blame,
+		'git-blame-signatures!' => \$email_git_blame_signatures,
+		'git-fallback!' => \$email_git_fallback,
+		'git-chief-penguins!' => \$email_git_penguin_chiefs,
+		'git-min-signatures=i' => \$email_git_min_signatures,
+		'git-max-maintainers=i' => \$email_git_max_maintainers,
+		'git-min-percent=i' => \$email_git_min_percent,
+		'git-since=s' => \$email_git_since,
+		'hg-since=s' => \$email_hg_since,
+		'i|interactive!' => \$interactive,
+		'remove-duplicates!' => \$email_remove_duplicates,
+		'mailmap!' => \$email_use_mailmap,
+		'm!' => \$email_maintainer,
+		'n!' => \$email_usename,
+		'l!' => \$email_list,
+		's!' => \$email_subscriber_list,
+		'multiline!' => \$output_multiline,
+		'roles!' => \$output_roles,
+		'rolestats!' => \$output_rolestats,
+		'separator=s' => \$output_separator,
+		'subsystem!' => \$subsystem,
+		'status!' => \$status,
+		'scm!' => \$scm,
+		'web!' => \$web,
+		'pattern-depth=i' => \$pattern_depth,
+		'k|keywords!' => \$keywords,
+		'sections!' => \$sections,
+		'fe|file-emails!' => \$file_emails,
+		'f|file' => \$from_filename,
+		'v|version' => \$version,
+		'h|help|usage' => \$help,
+		)) {
+    die "$P: invalid argument - use --help if necessary\n";
+}
+
+if ($help != 0) {
+    usage();
+    exit 0;
+}
+
+if ($version != 0) {
+    print("${P} ${V}\n");
+    exit 0;
+}
+
+if (-t STDIN && !@ARGV) {
+    # We're talking to a terminal, but have no command line arguments.
+    die "$P: missing patchfile or -f file - use --help if necessary\n";
+}
+
+$output_multiline = 0 if ($output_separator ne ", ");
+$output_rolestats = 1 if ($interactive);
+$output_roles = 1 if ($output_rolestats);
+
+if ($sections) {
+    $email = 0;
+    $email_list = 0;
+    $scm = 0;
+    $status = 0;
+    $subsystem = 0;
+    $web = 0;
+    $keywords = 0;
+    $interactive = 0;
+} else {
+    my $selections = $email + $scm + $status + $subsystem + $web;
+    if ($selections == 0) {
+	die "$P:  Missing required option: email, scm, status, subsystem or web\n";
+    }
+}
+
+if ($email &&
+    ($email_maintainer + $email_list + $email_subscriber_list +
+     $email_git + $email_git_penguin_chiefs + $email_git_blame) == 0) {
+    die "$P: Please select at least 1 email option\n";
+}
+
+if (!top_of_tree($lk_path)) {
+    die "$P: The current directory does not appear to be "
+	. "a QEMU source tree.\n";
+}
+
+## Read MAINTAINERS for type/value pairs
+
+my @typevalue = ();
+my %keyword_hash;
+
+open (my $maint, '<', "${lk_path}MAINTAINERS")
+    or die "$P: Can't open MAINTAINERS: $!\n";
+while (<$maint>) {
+    my $line = $_;
+
+    if ($line =~ m/^(\C):\s*(.*)/) {
+	my $type = $1;
+	my $value = $2;
+
+	##Filename pattern matching
+	if ($type eq "F" || $type eq "X") {
+	    $value =~ s@\.@\\\.@g;       ##Convert . to \.
+	    $value =~ s/\*/\.\*/g;       ##Convert * to .*
+	    $value =~ s/\?/\./g;         ##Convert ? to .
+	    ##if pattern is a directory and it lacks a trailing slash, add one
+	    if ((-d $value)) {
+		$value =~ s@([^/])$@$1/@;
+	    }
+	} elsif ($type eq "K") {
+	    $keyword_hash{@typevalue} = $value;
+	}
+	push(@typevalue, "$type:$value");
+    } elsif (!/^(\s)*$/) {
+	$line =~ s/\n$//g;
+	push(@typevalue, $line);
+    }
+}
+close($maint);
+
+
+#
+# Read mail address map
+#
+
+my $mailmap;
+
+read_mailmap();
+
+sub read_mailmap {
+    $mailmap = {
+	names => {},
+	addresses => {}
+    };
+
+    return if (!$email_use_mailmap || !(-f "${lk_path}.mailmap"));
+
+    open(my $mailmap_file, '<', "${lk_path}.mailmap")
+	or warn "$P: Can't open .mailmap: $!\n";
+
+    while (<$mailmap_file>) {
+	s/#.*$//; #strip comments
+	s/^\s+|\s+$//g; #trim
+
+	next if (/^\s*$/); #skip empty lines
+	#entries have one of the following formats:
+	# name1 <mail1>
+	# <mail1> <mail2>
+	# name1 <mail1> <mail2>
+	# name1 <mail1> name2 <mail2>
+	# (see man git-shortlog)
+	if (/^(.+)<(.+)>$/) {
+	    my $real_name = $1;
+	    my $address = $2;
+
+	    $real_name =~ s/\s+$//;
+	    ($real_name, $address) = parse_email("$real_name <$address>");
+	    $mailmap->{names}->{$address} = $real_name;
+
+	} elsif (/^<([^\s]+)>\s*<([^\s]+)>$/) {
+	    my $real_address = $1;
+	    my $wrong_address = $2;
+
+	    $mailmap->{addresses}->{$wrong_address} = $real_address;
+
+	} elsif (/^(.+)<([^\s]+)>\s*<([^\s]+)>$/) {
+	    my $real_name = $1;
+	    my $real_address = $2;
+	    my $wrong_address = $3;
+
+	    $real_name =~ s/\s+$//;
+	    ($real_name, $real_address) =
+		parse_email("$real_name <$real_address>");
+	    $mailmap->{names}->{$wrong_address} = $real_name;
+	    $mailmap->{addresses}->{$wrong_address} = $real_address;
+
+	} elsif (/^(.+)<([^\s]+)>\s*([^\s].*)<([^\s]+)>$/) {
+	    my $real_name = $1;
+	    my $real_address = $2;
+	    my $wrong_name = $3;
+	    my $wrong_address = $4;
+
+	    $real_name =~ s/\s+$//;
+	    ($real_name, $real_address) =
+		parse_email("$real_name <$real_address>");
+
+	    $wrong_name =~ s/\s+$//;
+	    ($wrong_name, $wrong_address) =
+		parse_email("$wrong_name <$wrong_address>");
+
+	    my $wrong_email = format_email($wrong_name, $wrong_address, 1);
+	    $mailmap->{names}->{$wrong_email} = $real_name;
+	    $mailmap->{addresses}->{$wrong_email} = $real_address;
+	}
+    }
+    close($mailmap_file);
+}
+
+## use the filenames on the command line or find the filenames in the patchfiles
+
+my @files = ();
+my @range = ();
+my @keyword_tvi = ();
+my @file_emails = ();
+
+if (!@ARGV) {
+    push(@ARGV, "&STDIN");
+}
+
+foreach my $file (@ARGV) {
+    if ($file ne "&STDIN") {
+	##if $file is a directory and it lacks a trailing slash, add one
+	if ((-d $file)) {
+	    $file =~ s@([^/])$@$1/@;
+	} elsif (!(-f $file)) {
+	    die "$P: file '${file}' not found\n";
+	}
+    }
+    if ($from_filename) {
+	push(@files, $file);
+	if ($file ne "MAINTAINERS" && -f $file && ($keywords || $file_emails)) {
+	    open(my $f, '<', $file)
+		or die "$P: Can't open $file: $!\n";
+	    my $text = do { local($/) ; <$f> };
+	    close($f);
+	    if ($keywords) {
+		foreach my $line (keys %keyword_hash) {
+		    if ($text =~ m/$keyword_hash{$line}/x) {
+			push(@keyword_tvi, $line);
+		    }
+		}
+	    }
+	    if ($file_emails) {
+		my @poss_addr = $text =~ m$[A-Za-zÀ-ÿ\"\' \,\.\+-]*\s*[\,]*\s*[\(\<\{]{0,1}[A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+\.[A-Za-z0-9]+[\)\>\}]{0,1}$g;
+		push(@file_emails, clean_file_emails(@poss_addr));
+	    }
+	}
+    } else {
+	my $file_cnt = @files;
+	my $lastfile;
+
+	open(my $patch, "< $file")
+	    or die "$P: Can't open $file: $!\n";
+
+	# We can check arbitrary information before the patch
+	# like the commit message, mail headers, etc...
+	# This allows us to match arbitrary keywords against any part
+	# of a git format-patch generated file (subject tags, etc...)
+
+	my $patch_prefix = "";			#Parsing the intro
+
+	while (<$patch>) {
+	    my $patch_line = $_;
+	    if (m/^\+\+\+\s+(\S+)/) {
+		my $filename = $1;
+		$filename =~ s@^[^/]*/@@;
+		$filename =~ s@\n@@;
+		$lastfile = $filename;
+		push(@files, $filename);
+		$patch_prefix = "^[+-].*";	#Now parsing the actual patch
+	    } elsif (m/^\@\@ -(\d+),(\d+)/) {
+		if ($email_git_blame) {
+		    push(@range, "$lastfile:$1:$2");
+		}
+	    } elsif ($keywords) {
+		foreach my $line (keys %keyword_hash) {
+		    if ($patch_line =~ m/${patch_prefix}$keyword_hash{$line}/x) {
+			push(@keyword_tvi, $line);
+		    }
+		}
+	    }
+	}
+	close($patch);
+
+	if ($file_cnt == @files) {
+	    warn "$P: file '${file}' doesn't appear to be a patch.  "
+		. "Add -f to options?\n";
+	}
+	@files = sort_and_uniq(@files);
+    }
+}
+
+@file_emails = uniq(@file_emails);
+
+my %email_hash_name;
+my %email_hash_address;
+my @email_to = ();
+my %hash_list_to;
+my @list_to = ();
+my @scm = ();
+my @web = ();
+my @subsystem = ();
+my @status = ();
+my %deduplicate_name_hash = ();
+my %deduplicate_address_hash = ();
+my $signature_pattern;
+
+my @maintainers = get_maintainers();
+
+if (@maintainers) {
+    @maintainers = merge_email(@maintainers);
+    output(@maintainers);
+}
+
+if ($scm) {
+    @scm = uniq(@scm);
+    output(@scm);
+}
+
+if ($status) {
+    @status = uniq(@status);
+    output(@status);
+}
+
+if ($subsystem) {
+    @subsystem = uniq(@subsystem);
+    output(@subsystem);
+}
+
+if ($web) {
+    @web = uniq(@web);
+    output(@web);
+}
+
+exit($exit);
+
+sub range_is_maintained {
+    my ($start, $end) = @_;
+
+    for (my $i = $start; $i < $end; $i++) {
+	my $line = $typevalue[$i];
+	if ($line =~ m/^(\C):\s*(.*)/) {
+	    my $type = $1;
+	    my $value = $2;
+	    if ($type eq 'S') {
+		if ($value =~ /(maintain|support)/i) {
+		    return 1;
+		}
+	    }
+	}
+    }
+    return 0;
+}
+
+sub range_has_maintainer {
+    my ($start, $end) = @_;
+
+    for (my $i = $start; $i < $end; $i++) {
+	my $line = $typevalue[$i];
+	if ($line =~ m/^(\C):\s*(.*)/) {
+	    my $type = $1;
+	    my $value = $2;
+	    if ($type eq 'M') {
+		return 1;
+	    }
+	}
+    }
+    return 0;
+}
+
+sub get_maintainers {
+    %email_hash_name = ();
+    %email_hash_address = ();
+    %commit_author_hash = ();
+    %commit_signer_hash = ();
+    @email_to = ();
+    %hash_list_to = ();
+    @list_to = ();
+    @scm = ();
+    @web = ();
+    @subsystem = ();
+    @status = ();
+    %deduplicate_name_hash = ();
+    %deduplicate_address_hash = ();
+    if ($email_git_all_signature_types) {
+	$signature_pattern = "(.+?)[Bb][Yy]:";
+    } else {
+	$signature_pattern = "\(" . join("|", @signature_tags) . "\)";
+    }
+
+    # Find responsible parties
+
+    my %exact_pattern_match_hash = ();
+
+    foreach my $file (@files) {
+
+	my %hash;
+	my $tvi = find_first_section();
+	while ($tvi < @typevalue) {
+	    my $start = find_starting_index($tvi);
+	    my $end = find_ending_index($tvi);
+	    my $exclude = 0;
+	    my $i;
+
+	    #Do not match excluded file patterns
+
+	    for ($i = $start; $i < $end; $i++) {
+		my $line = $typevalue[$i];
+		if ($line =~ m/^(\C):\s*(.*)/) {
+		    my $type = $1;
+		    my $value = $2;
+		    if ($type eq 'X') {
+			if (file_match_pattern($file, $value)) {
+			    $exclude = 1;
+			    last;
+			}
+		    }
+		}
+	    }
+
+	    if (!$exclude) {
+		for ($i = $start; $i < $end; $i++) {
+		    my $line = $typevalue[$i];
+		    if ($line =~ m/^(\C):\s*(.*)/) {
+			my $type = $1;
+			my $value = $2;
+			if ($type eq 'F') {
+			    if (file_match_pattern($file, $value)) {
+				my $value_pd = ($value =~ tr@/@@);
+				my $file_pd = ($file  =~ tr@/@@);
+				$value_pd++ if (substr($value,-1,1) ne "/");
+				$value_pd = -1 if ($value =~ /^\.\*/);
+				if ($value_pd >= $file_pd &&
+				    range_is_maintained($start, $end) &&
+				    range_has_maintainer($start, $end)) {
+				    $exact_pattern_match_hash{$file} = 1;
+				}
+				if ($pattern_depth == 0 ||
+				    (($file_pd - $value_pd) < $pattern_depth)) {
+				    $hash{$tvi} = $value_pd;
+				}
+			    }
+			}
+		    }
+		}
+	    }
+	    $tvi = $end + 1;
+	}
+
+	foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
+	    add_categories($line);
+	    if ($sections) {
+		my $i;
+		my $start = find_starting_index($line);
+		my $end = find_ending_index($line);
+		for ($i = $start; $i < $end; $i++) {
+		    my $line = $typevalue[$i];
+		    if ($line =~ /^[FX]:/) {		##Restore file patterns
+			$line =~ s/([^\\])\.([^\*])/$1\?$2/g;
+			$line =~ s/([^\\])\.$/$1\?/g;	##Convert . back to ?
+			$line =~ s/\\\./\./g;       	##Convert \. to .
+			$line =~ s/\.\*/\*/g;       	##Convert .* to *
+		    }
+		    $line =~ s/^([A-Z]):/$1:\t/g;
+		    print("$line\n");
+		}
+		print("\n");
+	    }
+	}
+    }
+
+    if ($keywords) {
+	@keyword_tvi = sort_and_uniq(@keyword_tvi);
+	foreach my $line (@keyword_tvi) {
+	    add_categories($line);
+	}
+    }
+
+    foreach my $email (@email_to, @list_to) {
+	$email->[0] = deduplicate_email($email->[0]);
+    }
+
+    foreach my $file (@files) {
+	if ($email &&
+	    ($email_git || ($email_git_fallback &&
+			    !$exact_pattern_match_hash{$file}))) {
+	    vcs_file_signoffs($file);
+	}
+	if ($email && $email_git_blame) {
+	    vcs_file_blame($file);
+	}
+    }
+
+    if ($email) {
+	foreach my $chief (@penguin_chief) {
+	    if ($chief =~ m/^(.*):(.*)/) {
+		my $email_address;
+
+		$email_address = format_email($1, $2, $email_usename);
+		if ($email_git_penguin_chiefs) {
+		    push(@email_to, [$email_address, 'chief penguin']);
+		} else {
+		    @email_to = grep($_->[0] !~ /${email_address}/, @email_to);
+		}
+	    }
+	}
+
+	foreach my $email (@file_emails) {
+	    my ($name, $address) = parse_email($email);
+
+	    my $tmp_email = format_email($name, $address, $email_usename);
+	    push_email_address($tmp_email, '');
+	    add_role($tmp_email, 'in file');
+	}
+    }
+
+    my @to = ();
+    if ($email || $email_list) {
+	if ($email) {
+	    @to = (@to, @email_to);
+	}
+	if ($email_list) {
+	    @to = (@to, @list_to);
+	}
+    }
+
+    if ($interactive) {
+	@to = interactive_get_maintainers(\@to);
+    }
+
+    return @to;
+}
+
+sub file_match_pattern {
+    my ($file, $pattern) = @_;
+    if (substr($pattern, -1) eq "/") {
+	if ($file =~ m@^$pattern@) {
+	    return 1;
+	}
+    } else {
+	if ($file =~ m@^$pattern@) {
+	    my $s1 = ($file =~ tr@/@@);
+	    my $s2 = ($pattern =~ tr@/@@);
+	    if ($s1 == $s2) {
+		return 1;
+	    }
+	}
+    }
+    return 0;
+}
+
+sub usage {
+    print <<EOT;
+usage: $P [options] patchfile
+       $P [options] -f file|directory
+version: $V
+
+MAINTAINER field selection options:
+  --email => print email address(es) if any
+    --git => include recent git \*-by: signers
+    --git-all-signature-types => include signers regardless of signature type
+        or use only ${signature_pattern} signers (default: $email_git_all_signature_types)
+    --git-fallback => use git when no exact MAINTAINERS pattern (default: $email_git_fallback)
+    --git-chief-penguins => include ${penguin_chiefs}
+    --git-min-signatures => number of signatures required (default: $email_git_min_signatures)
+    --git-max-maintainers => maximum maintainers to add (default: $email_git_max_maintainers)
+    --git-min-percent => minimum percentage of commits required (default: $email_git_min_percent)
+    --git-blame => use git blame to find modified commits for patch or file
+    --git-since => git history to use (default: $email_git_since)
+    --hg-since => hg history to use (default: $email_hg_since)
+    --interactive => display a menu (mostly useful if used with the --git option)
+    --m => include maintainer(s) if any
+    --n => include name 'Full Name <addr\@domain.tld>'
+    --l => include list(s) if any
+    --s => include subscriber only list(s) if any
+    --remove-duplicates => minimize duplicate email names/addresses
+    --roles => show roles (status:subsystem, git-signer, list, etc...)
+    --rolestats => show roles and statistics (commits/total_commits, %)
+    --file-emails => add email addresses found in -f file (default: 0 (off))
+  --scm => print SCM tree(s) if any
+  --status => print status if any
+  --subsystem => print subsystem name if any
+  --web => print website(s) if any
+
+Output type options:
+  --separator [, ] => separator for multiple entries on 1 line
+    using --separator also sets --nomultiline if --separator is not [, ]
+  --multiline => print 1 entry per line
+
+Other options:
+  --pattern-depth => Number of pattern directory traversals (default: 0 (all))
+  --keywords => scan patch for keywords (default: $keywords)
+  --sections => print all of the subsystem sections with pattern matches
+  --mailmap => use .mailmap file (default: $email_use_mailmap)
+  --version => show version
+  --help => show this help information
+
+Default options:
+  [--email --nogit --git-fallback --m --n --l --multiline -pattern-depth=0
+   --remove-duplicates --rolestats]
+
+Notes:
+  Using "-f directory" may give unexpected results:
+      Used with "--git", git signators for _all_ files in and below
+          directory are examined as git recurses directories.
+          Any specified X: (exclude) pattern matches are _not_ ignored.
+      Used with "--nogit", directory is used as a pattern match,
+          no individual file within the directory or subdirectory
+          is matched.
+      Used with "--git-blame", does not iterate all files in directory
+  Using "--git-blame" is slow and may add old committers and authors
+      that are no longer active maintainers to the output.
+  Using "--roles" or "--rolestats" with git send-email --cc-cmd or any
+      other automated tools that expect only ["name"] <email address>
+      may not work because of additional output after <email address>.
+  Using "--rolestats" and "--git-blame" shows the #/total=% commits,
+      not the percentage of the entire file authored.  # of commits is
+      not a good measure of amount of code authored.  1 major commit may
+      contain a thousand lines, 5 trivial commits may modify a single line.
+  If git is not installed, but mercurial (hg) is installed and an .hg
+      repository exists, the following options apply to mercurial:
+          --git,
+          --git-min-signatures, --git-max-maintainers, --git-min-percent, and
+          --git-blame
+      Use --hg-since not --git-since to control date selection
+  File ".get_maintainer.conf", if it exists in the QEMU source root
+      directory, can change whatever get_maintainer defaults are desired.
+      Entries in this file can be any command line argument.
+      This file is prepended to any additional command line arguments.
+      Multiple lines and # comments are allowed.
+EOT
+}
+
+sub top_of_tree {
+    my ($lk_path) = @_;
+
+    if ($lk_path ne "" && substr($lk_path,length($lk_path)-1,1) ne "/") {
+	$lk_path .= "/";
+    }
+    if (    (-f "${lk_path}COPYING")
+        && (-f "${lk_path}MAINTAINERS")
+        && (-f "${lk_path}Makefile")
+        && (-d "${lk_path}docs")
+        && (-f "${lk_path}VERSION")
+        && (-f "${lk_path}vl.c")) {
+	return 1;
+    }
+    return 0;
+}
+
+sub parse_email {
+    my ($formatted_email) = @_;
+
+    my $name = "";
+    my $address = "";
+
+    if ($formatted_email =~ /^([^<]+)<(.+\@.*)>.*$/) {
+	$name = $1;
+	$address = $2;
+    } elsif ($formatted_email =~ /^\s*<(.+\@\S*)>.*$/) {
+	$address = $1;
+    } elsif ($formatted_email =~ /^(.+\@\S*).*$/) {
+	$address = $1;
+    }
+
+    $name =~ s/^\s+|\s+$//g;
+    $name =~ s/^\"|\"$//g;
+    $address =~ s/^\s+|\s+$//g;
+
+    if ($name =~ /[^\w \-]/i) {  	 ##has "must quote" chars
+	$name =~ s/(?<!\\)"/\\"/g;       ##escape quotes
+	$name = "\"$name\"";
+    }
+
+    return ($name, $address);
+}
+
+sub format_email {
+    my ($name, $address, $usename) = @_;
+
+    my $formatted_email;
+
+    $name =~ s/^\s+|\s+$//g;
+    $name =~ s/^\"|\"$//g;
+    $address =~ s/^\s+|\s+$//g;
+
+    if ($name =~ /[^\w \-]/i) {          ##has "must quote" chars
+	$name =~ s/(?<!\\)"/\\"/g;       ##escape quotes
+	$name = "\"$name\"";
+    }
+
+    if ($usename) {
+	if ("$name" eq "") {
+	    $formatted_email = "$address";
+	} else {
+	    $formatted_email = "$name <$address>";
+	}
+    } else {
+	$formatted_email = $address;
+    }
+
+    return $formatted_email;
+}
+
+sub find_first_section {
+    my $index = 0;
+
+    while ($index < @typevalue) {
+	my $tv = $typevalue[$index];
+	if (($tv =~ m/^(\C):\s*(.*)/)) {
+	    last;
+	}
+	$index++;
+    }
+
+    return $index;
+}
+
+sub find_starting_index {
+    my ($index) = @_;
+
+    while ($index > 0) {
+	my $tv = $typevalue[$index];
+	if (!($tv =~ m/^(\C):\s*(.*)/)) {
+	    last;
+	}
+	$index--;
+    }
+
+    return $index;
+}
+
+sub find_ending_index {
+    my ($index) = @_;
+
+    while ($index < @typevalue) {
+	my $tv = $typevalue[$index];
+	if (!($tv =~ m/^(\C):\s*(.*)/)) {
+	    last;
+	}
+	$index++;
+    }
+
+    return $index;
+}
+
+sub get_maintainer_role {
+    my ($index) = @_;
+
+    my $i;
+    my $start = find_starting_index($index);
+    my $end = find_ending_index($index);
+
+    my $role;
+    my $subsystem = $typevalue[$start];
+    if (length($subsystem) > 20) {
+	$subsystem = substr($subsystem, 0, 17);
+	$subsystem =~ s/\s*$//;
+	$subsystem = $subsystem . "...";
+    }
+
+    for ($i = $start + 1; $i < $end; $i++) {
+	my $tv = $typevalue[$i];
+	if ($tv =~ m/^(\C):\s*(.*)/) {
+	    my $ptype = $1;
+	    my $pvalue = $2;
+	    if ($ptype eq "S") {
+		$role = $pvalue;
+	    }
+	}
+    }
+
+    $role = lc($role);
+    if      ($role eq "supported") {
+	$role = "supporter";
+    } elsif ($role eq "maintained") {
+	$role = "maintainer";
+    } elsif ($role eq "odd fixes") {
+	$role = "odd fixer";
+    } elsif ($role eq "orphan") {
+	$role = "orphan minder";
+    } elsif ($role eq "obsolete") {
+	$role = "obsolete minder";
+    } elsif ($role eq "buried alive in reporters") {
+	$role = "chief penguin";
+    }
+
+    return $role . ":" . $subsystem;
+}
+
+sub get_list_role {
+    my ($index) = @_;
+
+    my $i;
+    my $start = find_starting_index($index);
+    my $end = find_ending_index($index);
+
+    my $subsystem = $typevalue[$start];
+    if (length($subsystem) > 20) {
+	$subsystem = substr($subsystem, 0, 17);
+	$subsystem =~ s/\s*$//;
+	$subsystem = $subsystem . "...";
+    }
+
+    if ($subsystem eq "THE REST") {
+	$subsystem = "";
+    }
+
+    return $subsystem;
+}
+
+sub add_categories {
+    my ($index) = @_;
+
+    my $i;
+    my $start = find_starting_index($index);
+    my $end = find_ending_index($index);
+
+    push(@subsystem, $typevalue[$start]);
+
+    for ($i = $start + 1; $i < $end; $i++) {
+	my $tv = $typevalue[$i];
+	if ($tv =~ m/^(\C):\s*(.*)/) {
+	    my $ptype = $1;
+	    my $pvalue = $2;
+	    if ($ptype eq "L") {
+		my $list_address = $pvalue;
+		my $list_additional = "";
+		my $list_role = get_list_role($i);
+
+		if ($list_role ne "") {
+		    $list_role = ":" . $list_role;
+		}
+		if ($list_address =~ m/([^\s]+)\s+(.*)$/) {
+		    $list_address = $1;
+		    $list_additional = $2;
+		}
+		if ($list_additional =~ m/subscribers-only/) {
+		    if ($email_subscriber_list) {
+			if (!$hash_list_to{lc($list_address)}) {
+			    $hash_list_to{lc($list_address)} = 1;
+			    push(@list_to, [$list_address,
+					    "subscriber list${list_role}"]);
+			}
+		    }
+		} else {
+		    if ($email_list) {
+			if (!$hash_list_to{lc($list_address)}) {
+			    $hash_list_to{lc($list_address)} = 1;
+			    push(@list_to, [$list_address,
+					    "open list${list_role}"]);
+			}
+		    }
+		}
+	    } elsif ($ptype eq "M") {
+		my ($name, $address) = parse_email($pvalue);
+		if ($name eq "") {
+		    if ($i > 0) {
+			my $tv = $typevalue[$i - 1];
+			if ($tv =~ m/^(\C):\s*(.*)/) {
+			    if ($1 eq "P") {
+				$name = $2;
+				$pvalue = format_email($name, $address, $email_usename);
+			    }
+			}
+		    }
+		}
+		if ($email_maintainer) {
+		    my $role = get_maintainer_role($i);
+		    push_email_addresses($pvalue, $role);
+		}
+	    } elsif ($ptype eq "T") {
+		push(@scm, $pvalue);
+	    } elsif ($ptype eq "W") {
+		push(@web, $pvalue);
+	    } elsif ($ptype eq "S") {
+		push(@status, $pvalue);
+	    }
+	}
+    }
+}
+
+sub email_inuse {
+    my ($name, $address) = @_;
+
+    return 1 if (($name eq "") && ($address eq ""));
+    return 1 if (($name ne "") && exists($email_hash_name{lc($name)}));
+    return 1 if (($address ne "") && exists($email_hash_address{lc($address)}));
+
+    return 0;
+}
+
+sub push_email_address {
+    my ($line, $role) = @_;
+
+    my ($name, $address) = parse_email($line);
+
+    if ($address eq "") {
+	return 0;
+    }
+
+    if (!$email_remove_duplicates) {
+	push(@email_to, [format_email($name, $address, $email_usename), $role]);
+    } elsif (!email_inuse($name, $address)) {
+	push(@email_to, [format_email($name, $address, $email_usename), $role]);
+	$email_hash_name{lc($name)}++ if ($name ne "");
+	$email_hash_address{lc($address)}++;
+    }
+
+    return 1;
+}
+
+sub push_email_addresses {
+    my ($address, $role) = @_;
+
+    my @address_list = ();
+
+    if (rfc822_valid($address)) {
+	push_email_address($address, $role);
+    } elsif (@address_list = rfc822_validlist($address)) {
+	my $array_count = shift(@address_list);
+	while (my $entry = shift(@address_list)) {
+	    push_email_address($entry, $role);
+	}
+    } else {
+	if (!push_email_address($address, $role)) {
+	    warn("Invalid MAINTAINERS address: '" . $address . "'\n");
+	}
+    }
+}
+
+sub add_role {
+    my ($line, $role) = @_;
+
+    my ($name, $address) = parse_email($line);
+    my $email = format_email($name, $address, $email_usename);
+
+    foreach my $entry (@email_to) {
+	if ($email_remove_duplicates) {
+	    my ($entry_name, $entry_address) = parse_email($entry->[0]);
+	    if (($name eq $entry_name || $address eq $entry_address)
+		&& ($role eq "" || !($entry->[1] =~ m/$role/))
+	    ) {
+		if ($entry->[1] eq "") {
+		    $entry->[1] = "$role";
+		} else {
+		    $entry->[1] = "$entry->[1],$role";
+		}
+	    }
+	} else {
+	    if ($email eq $entry->[0]
+		&& ($role eq "" || !($entry->[1] =~ m/$role/))
+	    ) {
+		if ($entry->[1] eq "") {
+		    $entry->[1] = "$role";
+		} else {
+		    $entry->[1] = "$entry->[1],$role";
+		}
+	    }
+	}
+    }
+}
+
+sub which {
+    my ($bin) = @_;
+
+    foreach my $path (split(/:/, $ENV{PATH})) {
+	if (-e "$path/$bin") {
+	    return "$path/$bin";
+	}
+    }
+
+    return "";
+}
+
+sub which_conf {
+    my ($conf) = @_;
+
+    foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) {
+	if (-e "$path/$conf") {
+	    return "$path/$conf";
+	}
+    }
+
+    return "";
+}
+
+sub mailmap_email {
+    my ($line) = @_;
+
+    my ($name, $address) = parse_email($line);
+    my $email = format_email($name, $address, 1);
+    my $real_name = $name;
+    my $real_address = $address;
+
+    if (exists $mailmap->{names}->{$email} ||
+	exists $mailmap->{addresses}->{$email}) {
+	if (exists $mailmap->{names}->{$email}) {
+	    $real_name = $mailmap->{names}->{$email};
+	}
+	if (exists $mailmap->{addresses}->{$email}) {
+	    $real_address = $mailmap->{addresses}->{$email};
+	}
+    } else {
+	if (exists $mailmap->{names}->{$address}) {
+	    $real_name = $mailmap->{names}->{$address};
+	}
+	if (exists $mailmap->{addresses}->{$address}) {
+	    $real_address = $mailmap->{addresses}->{$address};
+	}
+    }
+    return format_email($real_name, $real_address, 1);
+}
+
+sub mailmap {
+    my (@addresses) = @_;
+
+    my @mapped_emails = ();
+    foreach my $line (@addresses) {
+	push(@mapped_emails, mailmap_email($line));
+    }
+    merge_by_realname(@mapped_emails) if ($email_use_mailmap);
+    return @mapped_emails;
+}
+
+sub merge_by_realname {
+    my %address_map;
+    my (@emails) = @_;
+
+    foreach my $email (@emails) {
+	my ($name, $address) = parse_email($email);
+	if (exists $address_map{$name}) {
+	    $address = $address_map{$name};
+	    $email = format_email($name, $address, 1);
+	} else {
+	    $address_map{$name} = $address;
+	}
+    }
+}
+
+sub git_execute_cmd {
+    my ($cmd) = @_;
+    my @lines = ();
+
+    my $output = `$cmd`;
+    $output =~ s/^\s*//gm;
+    @lines = split("\n", $output);
+
+    return @lines;
+}
+
+sub hg_execute_cmd {
+    my ($cmd) = @_;
+    my @lines = ();
+
+    my $output = `$cmd`;
+    @lines = split("\n", $output);
+
+    return @lines;
+}
+
+sub extract_formatted_signatures {
+    my (@signature_lines) = @_;
+
+    my @type = @signature_lines;
+
+    s/\s*(.*):.*/$1/ for (@type);
+
+    # cut -f2- -d":"
+    s/\s*.*:\s*(.+)\s*/$1/ for (@signature_lines);
+
+## Reformat email addresses (with names) to avoid badly written signatures
+
+    foreach my $signer (@signature_lines) {
+	$signer = deduplicate_email($signer);
+    }
+
+    return (\@type, \@signature_lines);
+}
+
+sub vcs_find_signers {
+    my ($cmd) = @_;
+    my $commits;
+    my @lines = ();
+    my @signatures = ();
+
+    @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
+
+    my $pattern = $VCS_cmds{"commit_pattern"};
+
+    $commits = grep(/$pattern/, @lines);	# of commits
+
+    @signatures = grep(/^[ \t]*${signature_pattern}.*\@.*$/, @lines);
+
+    return (0, @signatures) if !@signatures;
+
+    save_commits_by_author(@lines) if ($interactive);
+    save_commits_by_signer(@lines) if ($interactive);
+
+    if (!$email_git_penguin_chiefs) {
+	@signatures = grep(!/${penguin_chiefs}/i, @signatures);
+    }
+
+    my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
+
+    return ($commits, @$signers_ref);
+}
+
+sub vcs_find_author {
+    my ($cmd) = @_;
+    my @lines = ();
+
+    @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
+
+    if (!$email_git_penguin_chiefs) {
+	@lines = grep(!/${penguin_chiefs}/i, @lines);
+    }
+
+    return @lines if !@lines;
+
+    my @authors = ();
+    foreach my $line (@lines) {
+	if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
+	    my $author = $1;
+	    my ($name, $address) = parse_email($author);
+	    $author = format_email($name, $address, 1);
+	    push(@authors, $author);
+	}
+    }
+
+    save_commits_by_author(@lines) if ($interactive);
+    save_commits_by_signer(@lines) if ($interactive);
+
+    return @authors;
+}
+
+sub vcs_save_commits {
+    my ($cmd) = @_;
+    my @lines = ();
+    my @commits = ();
+
+    @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
+
+    foreach my $line (@lines) {
+	if ($line =~ m/$VCS_cmds{"blame_commit_pattern"}/) {
+	    push(@commits, $1);
+	}
+    }
+
+    return @commits;
+}
+
+sub vcs_blame {
+    my ($file) = @_;
+    my $cmd;
+    my @commits = ();
+
+    return @commits if (!(-f $file));
+
+    if (@range && $VCS_cmds{"blame_range_cmd"} eq "") {
+	my @all_commits = ();
+
+	$cmd = $VCS_cmds{"blame_file_cmd"};
+	$cmd =~ s/(\$\w+)/$1/eeg;		#interpolate $cmd
+	@all_commits = vcs_save_commits($cmd);
+
+	foreach my $file_range_diff (@range) {
+	    next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
+	    my $diff_file = $1;
+	    my $diff_start = $2;
+	    my $diff_length = $3;
+	    next if ("$file" ne "$diff_file");
+	    for (my $i = $diff_start; $i < $diff_start + $diff_length; $i++) {
+		push(@commits, $all_commits[$i]);
+	    }
+	}
+    } elsif (@range) {
+	foreach my $file_range_diff (@range) {
+	    next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
+	    my $diff_file = $1;
+	    my $diff_start = $2;
+	    my $diff_length = $3;
+	    next if ("$file" ne "$diff_file");
+	    $cmd = $VCS_cmds{"blame_range_cmd"};
+	    $cmd =~ s/(\$\w+)/$1/eeg;		#interpolate $cmd
+	    push(@commits, vcs_save_commits($cmd));
+	}
+    } else {
+	$cmd = $VCS_cmds{"blame_file_cmd"};
+	$cmd =~ s/(\$\w+)/$1/eeg;		#interpolate $cmd
+	@commits = vcs_save_commits($cmd);
+    }
+
+    foreach my $commit (@commits) {
+	$commit =~ s/^\^//g;
+    }
+
+    return @commits;
+}
+
+my $printed_novcs = 0;
+sub vcs_exists {
+    %VCS_cmds = %VCS_cmds_git;
+    return 1 if eval $VCS_cmds{"available"};
+    %VCS_cmds = %VCS_cmds_hg;
+    return 2 if eval $VCS_cmds{"available"};
+    %VCS_cmds = ();
+    if (!$printed_novcs) {
+	warn("$P: No supported VCS found.  Add --nogit to options?\n");
+	warn("Using a git repository produces better results.\n");
+	warn("Try latest git repository using:\n");
+	warn("git clone git://git.qemu.org/qemu.git\n");
+	$printed_novcs = 1;
+    }
+    return 0;
+}
+
+sub vcs_is_git {
+    vcs_exists();
+    return $vcs_used == 1;
+}
+
+sub vcs_is_hg {
+    return $vcs_used == 2;
+}
+
+sub interactive_get_maintainers {
+    my ($list_ref) = @_;
+    my @list = @$list_ref;
+
+    vcs_exists();
+
+    my %selected;
+    my %authored;
+    my %signed;
+    my $count = 0;
+    my $maintained = 0;
+    foreach my $entry (@list) {
+	$maintained = 1 if ($entry->[1] =~ /^(maintainer|supporter)/i);
+	$selected{$count} = 1;
+	$authored{$count} = 0;
+	$signed{$count} = 0;
+	$count++;
+    }
+
+    #menu loop
+    my $done = 0;
+    my $print_options = 0;
+    my $redraw = 1;
+    while (!$done) {
+	$count = 0;
+	if ($redraw) {
+	    printf STDERR "\n%1s %2s %-65s",
+			  "*", "#", "email/list and role:stats";
+	    if ($email_git ||
+		($email_git_fallback && !$maintained) ||
+		$email_git_blame) {
+		print STDERR "auth sign";
+	    }
+	    print STDERR "\n";
+	    foreach my $entry (@list) {
+		my $email = $entry->[0];
+		my $role = $entry->[1];
+		my $sel = "";
+		$sel = "*" if ($selected{$count});
+		my $commit_author = $commit_author_hash{$email};
+		my $commit_signer = $commit_signer_hash{$email};
+		my $authored = 0;
+		my $signed = 0;
+		$authored++ for (@{$commit_author});
+		$signed++ for (@{$commit_signer});
+		printf STDERR "%1s %2d %-65s", $sel, $count + 1, $email;
+		printf STDERR "%4d %4d", $authored, $signed
+		    if ($authored > 0 || $signed > 0);
+		printf STDERR "\n     %s\n", $role;
+		if ($authored{$count}) {
+		    my $commit_author = $commit_author_hash{$email};
+		    foreach my $ref (@{$commit_author}) {
+			print STDERR "     Author: @{$ref}[1]\n";
+		    }
+		}
+		if ($signed{$count}) {
+		    my $commit_signer = $commit_signer_hash{$email};
+		    foreach my $ref (@{$commit_signer}) {
+			print STDERR "     @{$ref}[2]: @{$ref}[1]\n";
+		    }
+		}
+
+		$count++;
+	    }
+	}
+	my $date_ref = \$email_git_since;
+	$date_ref = \$email_hg_since if (vcs_is_hg());
+	if ($print_options) {
+	    $print_options = 0;
+	    if (vcs_exists()) {
+		print STDERR <<EOT
+
+Version Control options:
+g  use git history      [$email_git]
+gf use git-fallback     [$email_git_fallback]
+b  use git blame        [$email_git_blame]
+bs use blame signatures [$email_git_blame_signatures]
+c# minimum commits      [$email_git_min_signatures]
+%# min percent          [$email_git_min_percent]
+d# history to use       [$$date_ref]
+x# max maintainers      [$email_git_max_maintainers]
+t  all signature types  [$email_git_all_signature_types]
+m  use .mailmap         [$email_use_mailmap]
+EOT
+	    }
+	    print STDERR <<EOT
+
+Additional options:
+0  toggle all
+tm toggle maintainers
+tg toggle git entries
+tl toggle open list entries
+ts toggle subscriber list entries
+f  emails in file       [$file_emails]
+k  keywords in file     [$keywords]
+r  remove duplicates    [$email_remove_duplicates]
+p# pattern match depth  [$pattern_depth]
+EOT
+	}
+	print STDERR
+"\n#(toggle), A#(author), S#(signed) *(all), ^(none), O(options), Y(approve): ";
+
+	my $input = <STDIN>;
+	chomp($input);
+
+	$redraw = 1;
+	my $rerun = 0;
+	my @wish = split(/[, ]+/, $input);
+	foreach my $nr (@wish) {
+	    $nr = lc($nr);
+	    my $sel = substr($nr, 0, 1);
+	    my $str = substr($nr, 1);
+	    my $val = 0;
+	    $val = $1 if $str =~ /^(\d+)$/;
+
+	    if ($sel eq "y") {
+		$interactive = 0;
+		$done = 1;
+		$output_rolestats = 0;
+		$output_roles = 0;
+		last;
+	    } elsif ($nr =~ /^\d+$/ && $nr > 0 && $nr <= $count) {
+		$selected{$nr - 1} = !$selected{$nr - 1};
+	    } elsif ($sel eq "*" || $sel eq '^') {
+		my $toggle = 0;
+		$toggle = 1 if ($sel eq '*');
+		for (my $i = 0; $i < $count; $i++) {
+		    $selected{$i} = $toggle;
+		}
+	    } elsif ($sel eq "0") {
+		for (my $i = 0; $i < $count; $i++) {
+		    $selected{$i} = !$selected{$i};
+		}
+	    } elsif ($sel eq "t") {
+		if (lc($str) eq "m") {
+		    for (my $i = 0; $i < $count; $i++) {
+			$selected{$i} = !$selected{$i}
+			    if ($list[$i]->[1] =~ /^(maintainer|supporter)/i);
+		    }
+		} elsif (lc($str) eq "g") {
+		    for (my $i = 0; $i < $count; $i++) {
+			$selected{$i} = !$selected{$i}
+			    if ($list[$i]->[1] =~ /^(author|commit|signer)/i);
+		    }
+		} elsif (lc($str) eq "l") {
+		    for (my $i = 0; $i < $count; $i++) {
+			$selected{$i} = !$selected{$i}
+			    if ($list[$i]->[1] =~ /^(open list)/i);
+		    }
+		} elsif (lc($str) eq "s") {
+		    for (my $i = 0; $i < $count; $i++) {
+			$selected{$i} = !$selected{$i}
+			    if ($list[$i]->[1] =~ /^(subscriber list)/i);
+		    }
+		}
+	    } elsif ($sel eq "a") {
+		if ($val > 0 && $val <= $count) {
+		    $authored{$val - 1} = !$authored{$val - 1};
+		} elsif ($str eq '*' || $str eq '^') {
+		    my $toggle = 0;
+		    $toggle = 1 if ($str eq '*');
+		    for (my $i = 0; $i < $count; $i++) {
+			$authored{$i} = $toggle;
+		    }
+		}
+	    } elsif ($sel eq "s") {
+		if ($val > 0 && $val <= $count) {
+		    $signed{$val - 1} = !$signed{$val - 1};
+		} elsif ($str eq '*' || $str eq '^') {
+		    my $toggle = 0;
+		    $toggle = 1 if ($str eq '*');
+		    for (my $i = 0; $i < $count; $i++) {
+			$signed{$i} = $toggle;
+		    }
+		}
+	    } elsif ($sel eq "o") {
+		$print_options = 1;
+		$redraw = 1;
+	    } elsif ($sel eq "g") {
+		if ($str eq "f") {
+		    bool_invert(\$email_git_fallback);
+		} else {
+		    bool_invert(\$email_git);
+		}
+		$rerun = 1;
+	    } elsif ($sel eq "b") {
+		if ($str eq "s") {
+		    bool_invert(\$email_git_blame_signatures);
+		} else {
+		    bool_invert(\$email_git_blame);
+		}
+		$rerun = 1;
+	    } elsif ($sel eq "c") {
+		if ($val > 0) {
+		    $email_git_min_signatures = $val;
+		    $rerun = 1;
+		}
+	    } elsif ($sel eq "x") {
+		if ($val > 0) {
+		    $email_git_max_maintainers = $val;
+		    $rerun = 1;
+		}
+	    } elsif ($sel eq "%") {
+		if ($str ne "" && $val >= 0) {
+		    $email_git_min_percent = $val;
+		    $rerun = 1;
+		}
+	    } elsif ($sel eq "d") {
+		if (vcs_is_git()) {
+		    $email_git_since = $str;
+		} elsif (vcs_is_hg()) {
+		    $email_hg_since = $str;
+		}
+		$rerun = 1;
+	    } elsif ($sel eq "t") {
+		bool_invert(\$email_git_all_signature_types);
+		$rerun = 1;
+	    } elsif ($sel eq "f") {
+		bool_invert(\$file_emails);
+		$rerun = 1;
+	    } elsif ($sel eq "r") {
+		bool_invert(\$email_remove_duplicates);
+		$rerun = 1;
+	    } elsif ($sel eq "m") {
+		bool_invert(\$email_use_mailmap);
+		read_mailmap();
+		$rerun = 1;
+	    } elsif ($sel eq "k") {
+		bool_invert(\$keywords);
+		$rerun = 1;
+	    } elsif ($sel eq "p") {
+		if ($str ne "" && $val >= 0) {
+		    $pattern_depth = $val;
+		    $rerun = 1;
+		}
+	    } elsif ($sel eq "h" || $sel eq "?") {
+		print STDERR <<EOT
+
+Interactive mode allows you to select the various maintainers, submitters,
+commit signers and mailing lists that could be CC'd on a patch.
+
+Any *'d entry is selected.
+
+If you have git or hg installed, you can choose to summarize the commit
+history of files in the patch.  Also, each line of the current file can
+be matched to its commit author and that commits signers with blame.
+
+Various knobs exist to control the length of time for active commit
+tracking, the maximum number of commit authors and signers to add,
+and such.
+
+Enter selections at the prompt until you are satisfied that the selected
+maintainers are appropriate.  You may enter multiple selections separated
+by either commas or spaces.
+
+EOT
+	    } else {
+		print STDERR "invalid option: '$nr'\n";
+		$redraw = 0;
+	    }
+	}
+	if ($rerun) {
+	    print STDERR "git-blame can be very slow, please have patience..."
+		if ($email_git_blame);
+	    goto &get_maintainers;
+	}
+    }
+
+    #drop not selected entries
+    $count = 0;
+    my @new_emailto = ();
+    foreach my $entry (@list) {
+	if ($selected{$count}) {
+	    push(@new_emailto, $list[$count]);
+	}
+	$count++;
+    }
+    return @new_emailto;
+}
+
+sub bool_invert {
+    my ($bool_ref) = @_;
+
+    if ($$bool_ref) {
+	$$bool_ref = 0;
+    } else {
+	$$bool_ref = 1;
+    }
+}
+
+sub deduplicate_email {
+    my ($email) = @_;
+
+    my $matched = 0;
+    my ($name, $address) = parse_email($email);
+    $email = format_email($name, $address, 1);
+    $email = mailmap_email($email);
+
+    return $email if (!$email_remove_duplicates);
+
+    ($name, $address) = parse_email($email);
+
+    if ($name ne "" && $deduplicate_name_hash{lc($name)}) {
+	$name = $deduplicate_name_hash{lc($name)}->[0];
+	$address = $deduplicate_name_hash{lc($name)}->[1];
+	$matched = 1;
+    } elsif ($deduplicate_address_hash{lc($address)}) {
+	$name = $deduplicate_address_hash{lc($address)}->[0];
+	$address = $deduplicate_address_hash{lc($address)}->[1];
+	$matched = 1;
+    }
+    if (!$matched) {
+	$deduplicate_name_hash{lc($name)} = [ $name, $address ];
+	$deduplicate_address_hash{lc($address)} = [ $name, $address ];
+    }
+    $email = format_email($name, $address, 1);
+    $email = mailmap_email($email);
+    return $email;
+}
+
+sub save_commits_by_author {
+    my (@lines) = @_;
+
+    my @authors = ();
+    my @commits = ();
+    my @subjects = ();
+
+    foreach my $line (@lines) {
+	if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
+	    my $author = $1;
+	    $author = deduplicate_email($author);
+	    push(@authors, $author);
+	}
+	push(@commits, $1) if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
+	push(@subjects, $1) if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
+    }
+
+    for (my $i = 0; $i < @authors; $i++) {
+	my $exists = 0;
+	foreach my $ref(@{$commit_author_hash{$authors[$i]}}) {
+	    if (@{$ref}[0] eq $commits[$i] &&
+		@{$ref}[1] eq $subjects[$i]) {
+		$exists = 1;
+		last;
+	    }
+	}
+	if (!$exists) {
+	    push(@{$commit_author_hash{$authors[$i]}},
+		 [ ($commits[$i], $subjects[$i]) ]);
+	}
+    }
+}
+
+sub save_commits_by_signer {
+    my (@lines) = @_;
+
+    my $commit = "";
+    my $subject = "";
+
+    foreach my $line (@lines) {
+	$commit = $1 if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
+	$subject = $1 if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
+	if ($line =~ /^[ \t]*${signature_pattern}.*\@.*$/) {
+	    my @signatures = ($line);
+	    my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
+	    my @types = @$types_ref;
+	    my @signers = @$signers_ref;
+
+	    my $type = $types[0];
+	    my $signer = $signers[0];
+
+	    $signer = deduplicate_email($signer);
+
+	    my $exists = 0;
+	    foreach my $ref(@{$commit_signer_hash{$signer}}) {
+		if (@{$ref}[0] eq $commit &&
+		    @{$ref}[1] eq $subject &&
+		    @{$ref}[2] eq $type) {
+		    $exists = 1;
+		    last;
+		}
+	    }
+	    if (!$exists) {
+		push(@{$commit_signer_hash{$signer}},
+		     [ ($commit, $subject, $type) ]);
+	    }
+	}
+    }
+}
+
+sub vcs_assign {
+    my ($role, $divisor, @lines) = @_;
+
+    my %hash;
+    my $count = 0;
+
+    return if (@lines <= 0);
+
+    if ($divisor <= 0) {
+	warn("Bad divisor in " . (caller(0))[3] . ": $divisor\n");
+	$divisor = 1;
+    }
+
+    @lines = mailmap(@lines);
+
+    return if (@lines <= 0);
+
+    @lines = sort(@lines);
+
+    # uniq -c
+    $hash{$_}++ for @lines;
+
+    # sort -rn
+    foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
+	my $sign_offs = $hash{$line};
+	my $percent = $sign_offs * 100 / $divisor;
+
+	$percent = 100 if ($percent > 100);
+	$count++;
+	last if ($sign_offs < $email_git_min_signatures ||
+		 $count > $email_git_max_maintainers ||
+		 $percent < $email_git_min_percent);
+	push_email_address($line, '');
+	if ($output_rolestats) {
+	    my $fmt_percent = sprintf("%.0f", $percent);
+	    add_role($line, "$role:$sign_offs/$divisor=$fmt_percent%");
+	} else {
+	    add_role($line, $role);
+	}
+    }
+}
+
+sub vcs_file_signoffs {
+    my ($file) = @_;
+
+    my @signers = ();
+    my $commits;
+
+    $vcs_used = vcs_exists();
+    return if (!$vcs_used);
+
+    my $cmd = $VCS_cmds{"find_signers_cmd"};
+    $cmd =~ s/(\$\w+)/$1/eeg;		# interpolate $cmd
+
+    ($commits, @signers) = vcs_find_signers($cmd);
+
+    foreach my $signer (@signers) {
+	$signer = deduplicate_email($signer);
+    }
+
+    vcs_assign("commit_signer", $commits, @signers);
+}
+
+sub vcs_file_blame {
+    my ($file) = @_;
+
+    my @signers = ();
+    my @all_commits = ();
+    my @commits = ();
+    my $total_commits;
+    my $total_lines;
+
+    $vcs_used = vcs_exists();
+    return if (!$vcs_used);
+
+    @all_commits = vcs_blame($file);
+    @commits = uniq(@all_commits);
+    $total_commits = @commits;
+    $total_lines = @all_commits;
+
+    if ($email_git_blame_signatures) {
+	if (vcs_is_hg()) {
+	    my $commit_count;
+	    my @commit_signers = ();
+	    my $commit = join(" -r ", @commits);
+	    my $cmd;
+
+	    $cmd = $VCS_cmds{"find_commit_signers_cmd"};
+	    $cmd =~ s/(\$\w+)/$1/eeg;	#substitute variables in $cmd
+
+	    ($commit_count, @commit_signers) = vcs_find_signers($cmd);
+
+	    push(@signers, @commit_signers);
+	} else {
+	    foreach my $commit (@commits) {
+		my $commit_count;
+		my @commit_signers = ();
+		my $cmd;
+
+		$cmd = $VCS_cmds{"find_commit_signers_cmd"};
+		$cmd =~ s/(\$\w+)/$1/eeg;	#substitute variables in $cmd
+
+		($commit_count, @commit_signers) = vcs_find_signers($cmd);
+
+		push(@signers, @commit_signers);
+	    }
+	}
+    }
+
+    if ($from_filename) {
+	if ($output_rolestats) {
+	    my @blame_signers;
+	    if (vcs_is_hg()) {{		# Double brace for last exit
+		my $commit_count;
+		my @commit_signers = ();
+		@commits = uniq(@commits);
+		@commits = sort(@commits);
+		my $commit = join(" -r ", @commits);
+		my $cmd;
+
+		$cmd = $VCS_cmds{"find_commit_author_cmd"};
+		$cmd =~ s/(\$\w+)/$1/eeg;	#substitute variables in $cmd
+
+		my @lines = ();
+
+		@lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
+
+		if (!$email_git_penguin_chiefs) {
+		    @lines = grep(!/${penguin_chiefs}/i, @lines);
+		}
+
+		last if !@lines;
+
+		my @authors = ();
+		foreach my $line (@lines) {
+		    if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
+			my $author = $1;
+			$author = deduplicate_email($author);
+			push(@authors, $author);
+		    }
+		}
+
+		save_commits_by_author(@lines) if ($interactive);
+		save_commits_by_signer(@lines) if ($interactive);
+
+		push(@signers, @authors);
+	    }}
+	    else {
+		foreach my $commit (@commits) {
+		    my $i;
+		    my $cmd = $VCS_cmds{"find_commit_author_cmd"};
+		    $cmd =~ s/(\$\w+)/$1/eeg;	#interpolate $cmd
+		    my @author = vcs_find_author($cmd);
+		    next if !@author;
+
+		    my $formatted_author = deduplicate_email($author[0]);
+
+		    my $count = grep(/$commit/, @all_commits);
+		    for ($i = 0; $i < $count ; $i++) {
+			push(@blame_signers, $formatted_author);
+		    }
+		}
+	    }
+	    if (@blame_signers) {
+		vcs_assign("authored lines", $total_lines, @blame_signers);
+	    }
+	}
+	foreach my $signer (@signers) {
+	    $signer = deduplicate_email($signer);
+	}
+	vcs_assign("commits", $total_commits, @signers);
+    } else {
+	foreach my $signer (@signers) {
+	    $signer = deduplicate_email($signer);
+	}
+	vcs_assign("modified commits", $total_commits, @signers);
+    }
+}
+
+sub uniq {
+    my (@parms) = @_;
+
+    my %saw;
+    @parms = grep(!$saw{$_}++, @parms);
+    return @parms;
+}
+
+sub sort_and_uniq {
+    my (@parms) = @_;
+
+    my %saw;
+    @parms = sort @parms;
+    @parms = grep(!$saw{$_}++, @parms);
+    return @parms;
+}
+
+sub clean_file_emails {
+    my (@file_emails) = @_;
+    my @fmt_emails = ();
+
+    foreach my $email (@file_emails) {
+	$email =~ s/[\(\<\{]{0,1}([A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+)[\)\>\}]{0,1}/\<$1\>/g;
+	my ($name, $address) = parse_email($email);
+	if ($name eq '"[,\.]"') {
+	    $name = "";
+	}
+
+	my @nw = split(/[^A-Za-zÀ-ÿ\'\,\.\+-]/, $name);
+	if (@nw > 2) {
+	    my $first = $nw[@nw - 3];
+	    my $middle = $nw[@nw - 2];
+	    my $last = $nw[@nw - 1];
+
+	    if (((length($first) == 1 && $first =~ m/[A-Za-z]/) ||
+		 (length($first) == 2 && substr($first, -1) eq ".")) ||
+		(length($middle) == 1 ||
+		 (length($middle) == 2 && substr($middle, -1) eq "."))) {
+		$name = "$first $middle $last";
+	    } else {
+		$name = "$middle $last";
+	    }
+	}
+
+	if (substr($name, -1) =~ /[,\.]/) {
+	    $name = substr($name, 0, length($name) - 1);
+	} elsif (substr($name, -2) =~ /[,\.]"/) {
+	    $name = substr($name, 0, length($name) - 2) . '"';
+	}
+
+	if (substr($name, 0, 1) =~ /[,\.]/) {
+	    $name = substr($name, 1, length($name) - 1);
+	} elsif (substr($name, 0, 2) =~ /"[,\.]/) {
+	    $name = '"' . substr($name, 2, length($name) - 2);
+	}
+
+	my $fmt_email = format_email($name, $address, $email_usename);
+	push(@fmt_emails, $fmt_email);
+    }
+    return @fmt_emails;
+}
+
+sub merge_email {
+    my @lines;
+    my %saw;
+
+    for (@_) {
+	my ($address, $role) = @$_;
+	if (!$saw{$address}) {
+	    if ($output_roles) {
+		push(@lines, "$address ($role)");
+	    } else {
+		push(@lines, $address);
+	    }
+	    $saw{$address} = 1;
+	}
+    }
+
+    return @lines;
+}
+
+sub output {
+    my (@parms) = @_;
+
+    if ($output_multiline) {
+	foreach my $line (@parms) {
+	    print("${line}\n");
+	}
+    } else {
+	print(join($output_separator, @parms));
+	print("\n");
+    }
+}
+
+my $rfc822re;
+
+sub make_rfc822re {
+#   Basic lexical tokens are specials, domain_literal, quoted_string, atom, and
+#   comment.  We must allow for rfc822_lwsp (or comments) after each of these.
+#   This regexp will only work on addresses which have had comments stripped
+#   and replaced with rfc822_lwsp.
+
+    my $specials = '()<>@,;:\\\\".\\[\\]';
+    my $controls = '\\000-\\037\\177';
+
+    my $dtext = "[^\\[\\]\\r\\\\]";
+    my $domain_literal = "\\[(?:$dtext|\\\\.)*\\]$rfc822_lwsp*";
+
+    my $quoted_string = "\"(?:[^\\\"\\r\\\\]|\\\\.|$rfc822_lwsp)*\"$rfc822_lwsp*";
+
+#   Use zero-width assertion to spot the limit of an atom.  A simple
+#   $rfc822_lwsp* causes the regexp engine to hang occasionally.
+    my $atom = "[^$specials $controls]+(?:$rfc822_lwsp+|\\Z|(?=[\\[\"$specials]))";
+    my $word = "(?:$atom|$quoted_string)";
+    my $localpart = "$word(?:\\.$rfc822_lwsp*$word)*";
+
+    my $sub_domain = "(?:$atom|$domain_literal)";
+    my $domain = "$sub_domain(?:\\.$rfc822_lwsp*$sub_domain)*";
+
+    my $addr_spec = "$localpart\@$rfc822_lwsp*$domain";
+
+    my $phrase = "$word*";
+    my $route = "(?:\@$domain(?:,\@$rfc822_lwsp*$domain)*:$rfc822_lwsp*)";
+    my $route_addr = "\\<$rfc822_lwsp*$route?$addr_spec\\>$rfc822_lwsp*";
+    my $mailbox = "(?:$addr_spec|$phrase$route_addr)";
+
+    my $group = "$phrase:$rfc822_lwsp*(?:$mailbox(?:,\\s*$mailbox)*)?;\\s*";
+    my $address = "(?:$mailbox|$group)";
+
+    return "$rfc822_lwsp*$address";
+}
+
+sub rfc822_strip_comments {
+    my $s = shift;
+#   Recursively remove comments, and replace with a single space.  The simpler
+#   regexps in the Email Addressing FAQ are imperfect - they will miss escaped
+#   chars in atoms, for example.
+
+    while ($s =~ s/^((?:[^"\\]|\\.)*
+                    (?:"(?:[^"\\]|\\.)*"(?:[^"\\]|\\.)*)*)
+                    \((?:[^()\\]|\\.)*\)/$1 /osx) {}
+    return $s;
+}
+
+#   valid: returns true if the parameter is an RFC822 valid address
+#
+sub rfc822_valid {
+    my $s = rfc822_strip_comments(shift);
+
+    if (!$rfc822re) {
+        $rfc822re = make_rfc822re();
+    }
+
+    return $s =~ m/^$rfc822re$/so && $s =~ m/^$rfc822_char*$/;
+}
+
+#   validlist: In scalar context, returns true if the parameter is an RFC822
+#              valid list of addresses.
+#
+#              In list context, returns an empty list on failure (an invalid
+#              address was found); otherwise a list whose first element is the
+#              number of addresses found and whose remaining elements are the
+#              addresses.  This is needed to disambiguate failure (invalid)
+#              from success with no addresses found, because an empty string is
+#              a valid list.
+
+sub rfc822_validlist {
+    my $s = rfc822_strip_comments(shift);
+
+    if (!$rfc822re) {
+        $rfc822re = make_rfc822re();
+    }
+    # * null list items are valid according to the RFC
+    # * the '1' business is to aid in distinguishing failure from no results
+
+    my @r;
+    if ($s =~ m/^(?:$rfc822re)?(?:,(?:$rfc822re)?)*$/so &&
+	$s =~ m/^$rfc822_char*$/) {
+        while ($s =~ m/(?:^|,$rfc822_lwsp*)($rfc822re)/gos) {
+            push(@r, $1);
+        }
+        return wantarray ? (scalar(@r), @r) : 1;
+    }
+    return wantarray ? () : 0;
+}
diff --git a/target-alpha/translate.c b/target-alpha/translate.c
index ad6c2ca..936760c 100644
--- a/target-alpha/translate.c
+++ b/target-alpha/translate.c
@@ -1661,9 +1661,12 @@
 static ExitStatus translate_one(DisasContext *ctx, uint32_t insn)
 {
     uint32_t palcode;
-    int32_t disp21, disp16, disp12;
+    int32_t disp21, disp16;
+#ifndef CONFIG_USER_ONLY
+    int32_t disp12;
+#endif
     uint16_t fn11;
-    uint8_t opc, ra, rb, rc, fpfn, fn7, fn2, islit, real_islit;
+    uint8_t opc, ra, rb, rc, fpfn, fn7, islit, real_islit;
     uint8_t lit;
     ExitStatus ret;
 
@@ -1681,11 +1684,12 @@
     palcode = insn & 0x03FFFFFF;
     disp21 = ((int32_t)((insn & 0x001FFFFF) << 11)) >> 11;
     disp16 = (int16_t)(insn & 0x0000FFFF);
+#ifndef CONFIG_USER_ONLY
     disp12 = (int32_t)((insn & 0x00000FFF) << 20) >> 20;
+#endif
     fn11 = (insn >> 5) & 0x000007FF;
     fpfn = fn11 & 0x3F;
     fn7 = (insn >> 5) & 0x0000007F;
-    fn2 = (insn >> 5) & 0x00000003;
     LOG_DISAS("opc %02x ra %2d rb %2d rc %2d disp16 %6d\n",
               opc, ra, rb, rc, disp16);
 
diff --git a/trace-events b/trace-events
index f1230f1..bebf612f 100644
--- a/trace-events
+++ b/trace-events
@@ -396,6 +396,7 @@
 
 # xen-all.c
 disable xen_ram_alloc(unsigned long ram_addr, unsigned long size) "requested: %#lx, size %#lx"
+disable xen_client_set_memory(uint64_t start_addr, unsigned long size, unsigned long phys_offset, bool log_dirty) "%#"PRIx64" size %#lx, offset %#lx, log_dirty %i"
 
 # xen-mapcache.c
 disable qemu_map_cache(uint64_t phys_addr) "want %#"PRIx64""
@@ -406,3 +407,6 @@
 
 # exec.c
 disable qemu_put_ram_ptr(void* addr) "%p"
+
+# hw/xen_platform.c
+disable xen_platform_log(char *s) "xen platform: %s"
diff --git a/xen-all.c b/xen-all.c
index 0eac202..fe75ddd 100644
--- a/xen-all.c
+++ b/xen-all.c
@@ -13,6 +13,7 @@
 #include "hw/xen_common.h"
 #include "hw/xen_backend.h"
 
+#include "range.h"
 #include "xen-mapcache.h"
 #include "trace.h"
 
@@ -54,6 +55,14 @@
 
 #define BUFFER_IO_MAX_DELAY  100
 
+typedef struct XenPhysmap {
+    target_phys_addr_t start_addr;
+    ram_addr_t size;
+    target_phys_addr_t phys_offset;
+
+    QLIST_ENTRY(XenPhysmap) list;
+} XenPhysmap;
+
 typedef struct XenIOState {
     shared_iopage_t *shared_page;
     buffered_iopage_t *buffered_io_page;
@@ -66,6 +75,9 @@
     int send_vcpu;
 
     struct xs_handle *xenstore;
+    CPUPhysMemoryClient client;
+    QLIST_HEAD(, XenPhysmap) physmap;
+    const XenPhysmap *log_for_dirtybit;
 
     Notifier exit;
 } XenIOState;
@@ -178,6 +190,270 @@
     qemu_free(pfn_list);
 }
 
+static XenPhysmap *get_physmapping(XenIOState *state,
+                                   target_phys_addr_t start_addr, ram_addr_t size)
+{
+    XenPhysmap *physmap = NULL;
+
+    start_addr &= TARGET_PAGE_MASK;
+
+    QLIST_FOREACH(physmap, &state->physmap, list) {
+        if (range_covers_byte(physmap->start_addr, physmap->size, start_addr)) {
+            return physmap;
+        }
+    }
+    return NULL;
+}
+
+#if CONFIG_XEN_CTRL_INTERFACE_VERSION >= 340
+static int xen_add_to_physmap(XenIOState *state,
+                              target_phys_addr_t start_addr,
+                              ram_addr_t size,
+                              target_phys_addr_t phys_offset)
+{
+    unsigned long i = 0;
+    int rc = 0;
+    XenPhysmap *physmap = NULL;
+    target_phys_addr_t pfn, start_gpfn;
+    RAMBlock *block;
+
+    if (get_physmapping(state, start_addr, size)) {
+        return 0;
+    }
+    if (size <= 0) {
+        return -1;
+    }
+
+    /* Xen can only handle a single dirty log region for now and we want
+     * the linear framebuffer to be that region.
+     * Avoid tracking any regions that is not videoram and avoid tracking
+     * the legacy vga region. */
+    QLIST_FOREACH(block, &ram_list.blocks, next) {
+        if (!strcmp(block->idstr, "vga.vram") && block->offset == phys_offset
+                && start_addr > 0xbffff) {
+            goto go_physmap;
+        }
+    }
+    return -1;
+
+go_physmap:
+    DPRINTF("mapping vram to %llx - %llx, from %llx\n",
+            start_addr, start_addr + size, phys_offset);
+
+    pfn = phys_offset >> TARGET_PAGE_BITS;
+    start_gpfn = start_addr >> TARGET_PAGE_BITS;
+    for (i = 0; i < size >> TARGET_PAGE_BITS; i++) {
+        unsigned long idx = pfn + i;
+        xen_pfn_t gpfn = start_gpfn + i;
+
+        rc = xc_domain_add_to_physmap(xen_xc, xen_domid, XENMAPSPACE_gmfn, idx, gpfn);
+        if (rc) {
+            DPRINTF("add_to_physmap MFN %"PRI_xen_pfn" to PFN %"
+                    PRI_xen_pfn" failed: %d\n", idx, gpfn, rc);
+            return -rc;
+        }
+    }
+
+    physmap = qemu_malloc(sizeof (XenPhysmap));
+
+    physmap->start_addr = start_addr;
+    physmap->size = size;
+    physmap->phys_offset = phys_offset;
+
+    QLIST_INSERT_HEAD(&state->physmap, physmap, list);
+
+    xc_domain_pin_memory_cacheattr(xen_xc, xen_domid,
+                                   start_addr >> TARGET_PAGE_BITS,
+                                   (start_addr + size) >> TARGET_PAGE_BITS,
+                                   XEN_DOMCTL_MEM_CACHEATTR_WB);
+    return 0;
+}
+
+static int xen_remove_from_physmap(XenIOState *state,
+                                   target_phys_addr_t start_addr,
+                                   ram_addr_t size)
+{
+    unsigned long i = 0;
+    int rc = 0;
+    XenPhysmap *physmap = NULL;
+    target_phys_addr_t phys_offset = 0;
+
+    physmap = get_physmapping(state, start_addr, size);
+    if (physmap == NULL) {
+        return -1;
+    }
+
+    phys_offset = physmap->phys_offset;
+    size = physmap->size;
+
+    DPRINTF("unmapping vram to %llx - %llx, from %llx\n",
+            phys_offset, phys_offset + size, start_addr);
+
+    size >>= TARGET_PAGE_BITS;
+    start_addr >>= TARGET_PAGE_BITS;
+    phys_offset >>= TARGET_PAGE_BITS;
+    for (i = 0; i < size; i++) {
+        unsigned long idx = start_addr + i;
+        xen_pfn_t gpfn = phys_offset + i;
+
+        rc = xc_domain_add_to_physmap(xen_xc, xen_domid, XENMAPSPACE_gmfn, idx, gpfn);
+        if (rc) {
+            fprintf(stderr, "add_to_physmap MFN %"PRI_xen_pfn" to PFN %"
+                    PRI_xen_pfn" failed: %d\n", idx, gpfn, rc);
+            return -rc;
+        }
+    }
+
+    QLIST_REMOVE(physmap, list);
+    if (state->log_for_dirtybit == physmap) {
+        state->log_for_dirtybit = NULL;
+    }
+    free(physmap);
+
+    return 0;
+}
+
+#else
+static int xen_add_to_physmap(XenIOState *state,
+                              target_phys_addr_t start_addr,
+                              ram_addr_t size,
+                              target_phys_addr_t phys_offset)
+{
+    return -ENOSYS;
+}
+
+static int xen_remove_from_physmap(XenIOState *state,
+                                   target_phys_addr_t start_addr,
+                                   ram_addr_t size)
+{
+    return -ENOSYS;
+}
+#endif
+
+static void xen_client_set_memory(struct CPUPhysMemoryClient *client,
+                                  target_phys_addr_t start_addr,
+                                  ram_addr_t size,
+                                  ram_addr_t phys_offset,
+                                  bool log_dirty)
+{
+    XenIOState *state = container_of(client, XenIOState, client);
+    ram_addr_t flags = phys_offset & ~TARGET_PAGE_MASK;
+    hvmmem_type_t mem_type;
+
+    if (!(start_addr != phys_offset
+          && ( (log_dirty && flags < IO_MEM_UNASSIGNED)
+               || (!log_dirty && flags == IO_MEM_UNASSIGNED)))) {
+        return;
+    }
+
+    trace_xen_client_set_memory(start_addr, size, phys_offset, log_dirty);
+
+    start_addr &= TARGET_PAGE_MASK;
+    size = TARGET_PAGE_ALIGN(size);
+    phys_offset &= TARGET_PAGE_MASK;
+
+    switch (flags) {
+    case IO_MEM_RAM:
+        xen_add_to_physmap(state, start_addr, size, phys_offset);
+        break;
+    case IO_MEM_ROM:
+        mem_type = HVMMEM_ram_ro;
+        if (xc_hvm_set_mem_type(xen_xc, xen_domid, mem_type,
+                                start_addr >> TARGET_PAGE_BITS,
+                                size >> TARGET_PAGE_BITS)) {
+            DPRINTF("xc_hvm_set_mem_type error, addr: "TARGET_FMT_plx"\n",
+                    start_addr);
+        }
+        break;
+    case IO_MEM_UNASSIGNED:
+        if (xen_remove_from_physmap(state, start_addr, size) < 0) {
+            DPRINTF("physmapping does not exist at "TARGET_FMT_plx"\n", start_addr);
+        }
+        break;
+    }
+}
+
+static int xen_sync_dirty_bitmap(XenIOState *state,
+                                 target_phys_addr_t start_addr,
+                                 ram_addr_t size)
+{
+    target_phys_addr_t npages = size >> TARGET_PAGE_BITS;
+    target_phys_addr_t vram_offset = 0;
+    const int width = sizeof(unsigned long) * 8;
+    unsigned long bitmap[(npages + width - 1) / width];
+    int rc, i, j;
+    const XenPhysmap *physmap = NULL;
+
+    physmap = get_physmapping(state, start_addr, size);
+    if (physmap == NULL) {
+        /* not handled */
+        return -1;
+    }
+
+    if (state->log_for_dirtybit == NULL) {
+        state->log_for_dirtybit = physmap;
+    } else if (state->log_for_dirtybit != physmap) {
+        return -1;
+    }
+    vram_offset = physmap->phys_offset;
+
+    rc = xc_hvm_track_dirty_vram(xen_xc, xen_domid,
+                                 start_addr >> TARGET_PAGE_BITS, npages,
+                                 bitmap);
+    if (rc) {
+        return rc;
+    }
+
+    for (i = 0; i < ARRAY_SIZE(bitmap); i++) {
+        unsigned long map = bitmap[i];
+        while (map != 0) {
+            j = ffsl(map) - 1;
+            map &= ~(1ul << j);
+            cpu_physical_memory_set_dirty(vram_offset + (i * width + j) * TARGET_PAGE_SIZE);
+        };
+    }
+
+    return 0;
+}
+
+static int xen_log_start(CPUPhysMemoryClient *client, target_phys_addr_t phys_addr, ram_addr_t size)
+{
+    XenIOState *state = container_of(client, XenIOState, client);
+
+    return xen_sync_dirty_bitmap(state, phys_addr, size);
+}
+
+static int xen_log_stop(CPUPhysMemoryClient *client, target_phys_addr_t phys_addr, ram_addr_t size)
+{
+    XenIOState *state = container_of(client, XenIOState, client);
+
+    state->log_for_dirtybit = NULL;
+    /* Disable dirty bit tracking */
+    return xc_hvm_track_dirty_vram(xen_xc, xen_domid, 0, 0, NULL);
+}
+
+static int xen_client_sync_dirty_bitmap(struct CPUPhysMemoryClient *client,
+                                        target_phys_addr_t start_addr,
+                                        target_phys_addr_t end_addr)
+{
+    XenIOState *state = container_of(client, XenIOState, client);
+
+    return xen_sync_dirty_bitmap(state, start_addr, end_addr - start_addr);
+}
+
+static int xen_client_migration_log(struct CPUPhysMemoryClient *client,
+                                    int enable)
+{
+    return 0;
+}
+
+static CPUPhysMemoryClient xen_cpu_phys_memory_client = {
+    .set_memory = xen_client_set_memory,
+    .sync_dirty_bitmap = xen_client_sync_dirty_bitmap,
+    .migration_log = xen_client_migration_log,
+    .log_start = xen_log_start,
+    .log_stop = xen_log_stop,
+};
 
 /* VCPU Operations, MMIO, IO ring ... */
 
@@ -581,6 +857,11 @@
 
     qemu_add_vm_change_state_handler(xen_vm_change_state_handler, state);
 
+    state->client = xen_cpu_phys_memory_client;
+    QLIST_INIT(&state->physmap);
+    cpu_register_phys_memory_client(&state->client);
+    state->log_for_dirtybit = NULL;
+
     return 0;
 }
 
diff --git a/xen-mapcache-stub.c b/xen-mapcache-stub.c
index 7c14b3d..8a2380a 100644
--- a/xen-mapcache-stub.c
+++ b/xen-mapcache-stub.c
@@ -22,10 +22,6 @@
     return qemu_get_ram_ptr(phys_addr);
 }
 
-void qemu_map_cache_unlock(void *buffer)
-{
-}
-
 ram_addr_t qemu_ram_addr_from_mapcache(void *ptr)
 {
     return -1;
@@ -38,7 +34,3 @@
 void qemu_invalidate_entry(uint8_t *buffer)
 {
 }
-uint8_t *xen_map_block(target_phys_addr_t phys_addr, target_phys_addr_t size)
-{
-    return NULL;
-}
diff --git a/xen-mapcache.c b/xen-mapcache.c
index 349cc62..fac47cd 100644
--- a/xen-mapcache.c
+++ b/xen-mapcache.c
@@ -43,14 +43,16 @@
 typedef struct MapCacheEntry {
     target_phys_addr_t paddr_index;
     uint8_t *vaddr_base;
-    DECLARE_BITMAP(valid_mapping, MCACHE_BUCKET_SIZE >> XC_PAGE_SHIFT);
+    unsigned long *valid_mapping;
     uint8_t lock;
+    target_phys_addr_t size;
     struct MapCacheEntry *next;
 } MapCacheEntry;
 
 typedef struct MapCacheRev {
     uint8_t *vaddr_req;
     target_phys_addr_t paddr_index;
+    target_phys_addr_t size;
     QTAILQ_ENTRY(MapCacheRev) next;
 } MapCacheRev;
 
@@ -68,6 +70,15 @@
 
 static MapCache *mapcache;
 
+static inline int test_bits(int nr, int size, const unsigned long *addr)
+{
+    unsigned long res = find_next_zero_bit(addr, size + nr, nr);
+    if (res >= nr + size)
+        return 1;
+    else
+        return 0;
+}
+
 void qemu_map_cache_init(void)
 {
     unsigned long size;
@@ -115,11 +126,15 @@
     err = qemu_mallocz(nb_pfn * sizeof (int));
 
     if (entry->vaddr_base != NULL) {
-        if (munmap(entry->vaddr_base, size) != 0) {
+        if (munmap(entry->vaddr_base, entry->size) != 0) {
             perror("unmap fails");
             exit(-1);
         }
     }
+    if (entry->valid_mapping != NULL) {
+        qemu_free(entry->valid_mapping);
+        entry->valid_mapping = NULL;
+    }
 
     for (i = 0; i < nb_pfn; i++) {
         pfns[i] = (address_index << (MCACHE_BUCKET_SHIFT-XC_PAGE_SHIFT)) + i;
@@ -134,6 +149,9 @@
 
     entry->vaddr_base = vaddr_base;
     entry->paddr_index = address_index;
+    entry->size = size;
+    entry->valid_mapping = (unsigned long *) qemu_mallocz(sizeof(unsigned long) *
+            BITS_TO_LONGS(size >> XC_PAGE_SHIFT));
 
     bitmap_zero(entry->valid_mapping, nb_pfn);
     for (i = 0; i < nb_pfn; i++) {
@@ -151,32 +169,47 @@
     MapCacheEntry *entry, *pentry = NULL;
     target_phys_addr_t address_index  = phys_addr >> MCACHE_BUCKET_SHIFT;
     target_phys_addr_t address_offset = phys_addr & (MCACHE_BUCKET_SIZE - 1);
+    target_phys_addr_t __size = size;
 
     trace_qemu_map_cache(phys_addr);
 
-    if (address_index == mapcache->last_address_index && !lock) {
+    if (address_index == mapcache->last_address_index && !lock && !__size) {
         trace_qemu_map_cache_return(mapcache->last_address_vaddr + address_offset);
         return mapcache->last_address_vaddr + address_offset;
     }
 
+    /* size is always a multiple of MCACHE_BUCKET_SIZE */
+    if ((address_offset + (__size % MCACHE_BUCKET_SIZE)) > MCACHE_BUCKET_SIZE)
+        __size += MCACHE_BUCKET_SIZE;
+    if (__size % MCACHE_BUCKET_SIZE)
+        __size += MCACHE_BUCKET_SIZE - (__size % MCACHE_BUCKET_SIZE);
+    if (!__size)
+        __size = MCACHE_BUCKET_SIZE;
+
     entry = &mapcache->entry[address_index % mapcache->nr_buckets];
 
-    while (entry && entry->lock && entry->paddr_index != address_index && entry->vaddr_base) {
+    while (entry && entry->lock && entry->vaddr_base &&
+            (entry->paddr_index != address_index || entry->size != __size ||
+             !test_bits(address_offset >> XC_PAGE_SHIFT, size >> XC_PAGE_SHIFT,
+                 entry->valid_mapping))) {
         pentry = entry;
         entry = entry->next;
     }
     if (!entry) {
         entry = qemu_mallocz(sizeof (MapCacheEntry));
         pentry->next = entry;
-        qemu_remap_bucket(entry, size ? : MCACHE_BUCKET_SIZE, address_index);
+        qemu_remap_bucket(entry, __size, address_index);
     } else if (!entry->lock) {
         if (!entry->vaddr_base || entry->paddr_index != address_index ||
-            !test_bit(address_offset >> XC_PAGE_SHIFT, entry->valid_mapping)) {
-            qemu_remap_bucket(entry, size ? : MCACHE_BUCKET_SIZE, address_index);
+                entry->size != __size ||
+                !test_bits(address_offset >> XC_PAGE_SHIFT, size >> XC_PAGE_SHIFT,
+                    entry->valid_mapping)) {
+            qemu_remap_bucket(entry, __size, address_index);
         }
     }
 
-    if (!test_bit(address_offset >> XC_PAGE_SHIFT, entry->valid_mapping)) {
+    if(!test_bits(address_offset >> XC_PAGE_SHIFT, size >> XC_PAGE_SHIFT,
+                entry->valid_mapping)) {
         mapcache->last_address_index = -1;
         trace_qemu_map_cache_return(NULL);
         return NULL;
@@ -189,6 +222,7 @@
         entry->lock++;
         reventry->vaddr_req = mapcache->last_address_vaddr + address_offset;
         reventry->paddr_index = mapcache->last_address_index;
+        reventry->size = entry->size;
         QTAILQ_INSERT_HEAD(&mapcache->locked_entries, reventry, next);
     }
 
@@ -196,48 +230,18 @@
     return mapcache->last_address_vaddr + address_offset;
 }
 
-void qemu_map_cache_unlock(void *buffer)
+ram_addr_t qemu_ram_addr_from_mapcache(void *ptr)
 {
     MapCacheEntry *entry = NULL, *pentry = NULL;
     MapCacheRev *reventry;
     target_phys_addr_t paddr_index;
-    int found = 0;
-
-    QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) {
-        if (reventry->vaddr_req == buffer) {
-            paddr_index = reventry->paddr_index;
-            found = 1;
-            break;
-        }
-    }
-    if (!found) {
-        return;
-    }
-    QTAILQ_REMOVE(&mapcache->locked_entries, reventry, next);
-    qemu_free(reventry);
-
-    entry = &mapcache->entry[paddr_index % mapcache->nr_buckets];
-    while (entry && entry->paddr_index != paddr_index) {
-        pentry = entry;
-        entry = entry->next;
-    }
-    if (!entry) {
-        return;
-    }
-    if (entry->lock > 0) {
-        entry->lock--;
-    }
-}
-
-ram_addr_t qemu_ram_addr_from_mapcache(void *ptr)
-{
-    MapCacheRev *reventry;
-    target_phys_addr_t paddr_index;
+    target_phys_addr_t size;
     int found = 0;
 
     QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) {
         if (reventry->vaddr_req == ptr) {
             paddr_index = reventry->paddr_index;
+            size = reventry->size;
             found = 1;
             break;
         }
@@ -252,7 +256,17 @@
         return 0;
     }
 
-    return paddr_index << MCACHE_BUCKET_SHIFT;
+    entry = &mapcache->entry[paddr_index % mapcache->nr_buckets];
+    while (entry && (entry->paddr_index != paddr_index || entry->size != size)) {
+        pentry = entry;
+        entry = entry->next;
+    }
+    if (!entry) {
+        DPRINTF("Trying to find address %p that is not in the mapcache!\n", ptr);
+        return 0;
+    }
+    return (reventry->paddr_index << MCACHE_BUCKET_SHIFT) +
+        ((unsigned long) ptr - (unsigned long) entry->vaddr_base);
 }
 
 void qemu_invalidate_entry(uint8_t *buffer)
@@ -260,6 +274,7 @@
     MapCacheEntry *entry = NULL, *pentry = NULL;
     MapCacheRev *reventry;
     target_phys_addr_t paddr_index;
+    target_phys_addr_t size;
     int found = 0;
 
     if (mapcache->last_address_vaddr == buffer) {
@@ -269,6 +284,7 @@
     QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) {
         if (reventry->vaddr_req == buffer) {
             paddr_index = reventry->paddr_index;
+            size = reventry->size;
             found = 1;
             break;
         }
@@ -284,7 +300,7 @@
     qemu_free(reventry);
 
     entry = &mapcache->entry[paddr_index % mapcache->nr_buckets];
-    while (entry && entry->paddr_index != paddr_index) {
+    while (entry && (entry->paddr_index != paddr_index || entry->size != size)) {
         pentry = entry;
         entry = entry->next;
     }
@@ -298,10 +314,11 @@
     }
 
     pentry->next = entry->next;
-    if (munmap(entry->vaddr_base, MCACHE_BUCKET_SIZE) != 0) {
+    if (munmap(entry->vaddr_base, entry->size) != 0) {
         perror("unmap fails");
         exit(-1);
     }
+    qemu_free(entry->valid_mapping);
     qemu_free(entry);
 }
 
@@ -328,13 +345,16 @@
             continue;
         }
 
-        if (munmap(entry->vaddr_base, MCACHE_BUCKET_SIZE) != 0) {
+        if (munmap(entry->vaddr_base, entry->size) != 0) {
             perror("unmap fails");
             exit(-1);
         }
 
         entry->paddr_index = 0;
         entry->vaddr_base = NULL;
+        entry->size = 0;
+        qemu_free(entry->valid_mapping);
+        entry->valid_mapping = NULL;
     }
 
     mapcache->last_address_index = -1;
@@ -342,34 +362,3 @@
 
     mapcache_unlock();
 }
-
-uint8_t *xen_map_block(target_phys_addr_t phys_addr, target_phys_addr_t size)
-{
-    uint8_t *vaddr_base;
-    xen_pfn_t *pfns;
-    int *err;
-    unsigned int i;
-    target_phys_addr_t nb_pfn = size >> XC_PAGE_SHIFT;
-
-    trace_xen_map_block(phys_addr, size);
-    phys_addr >>= XC_PAGE_SHIFT;
-
-    pfns = qemu_mallocz(nb_pfn * sizeof (xen_pfn_t));
-    err = qemu_mallocz(nb_pfn * sizeof (int));
-
-    for (i = 0; i < nb_pfn; i++) {
-        pfns[i] = phys_addr + i;
-    }
-
-    vaddr_base = xc_map_foreign_bulk(xen_xc, xen_domid, PROT_READ|PROT_WRITE,
-                                     pfns, err, nb_pfn);
-    if (vaddr_base == NULL) {
-        perror("xc_map_foreign_bulk");
-        exit(-1);
-    }
-
-    qemu_free(pfns);
-    qemu_free(err);
-
-    return vaddr_base;
-}
diff --git a/xen-mapcache.h b/xen-mapcache.h
index 339444c..6216cc3 100644
--- a/xen-mapcache.h
+++ b/xen-mapcache.h
@@ -9,28 +9,12 @@
 #ifndef XEN_MAPCACHE_H
 #define XEN_MAPCACHE_H
 
-#include <sys/mman.h>
-#include "trace.h"
-
 void     qemu_map_cache_init(void);
 uint8_t  *qemu_map_cache(target_phys_addr_t phys_addr, target_phys_addr_t size, uint8_t lock);
-void     qemu_map_cache_unlock(void *phys_addr);
 ram_addr_t qemu_ram_addr_from_mapcache(void *ptr);
 void     qemu_invalidate_entry(uint8_t *buffer);
 void     qemu_invalidate_map_cache(void);
 
-uint8_t *xen_map_block(target_phys_addr_t phys_addr, target_phys_addr_t size);
-
-static inline void xen_unmap_block(void *addr, ram_addr_t size)
-{
-    trace_xen_unmap_block(addr, size);
-
-    if (munmap(addr, size) != 0) {
-        hw_error("xen_unmap_block: %s", strerror(errno));
-    }
-}
-
-
 #define mapcache_lock()   ((void)0)
 #define mapcache_unlock() ((void)0)