summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--Makefile.target3
-rw-r--r--VERSION2
-rw-r--r--aio-win32.c5
-rwxr-xr-xconfigure16
-rw-r--r--hw/Makefile.objs4
-rw-r--r--hw/acpi_ich9.c322
-rw-r--r--hw/acpi_ich9.h47
-rw-r--r--hw/i386/Makefile.objs1
-rw-r--r--hw/i8259_internal.h2
-rw-r--r--hw/i82801b11.c125
-rw-r--r--hw/ich9.h207
-rw-r--r--hw/kvm/ioapic.c40
-rw-r--r--hw/lpc_ich9.c525
-rw-r--r--hw/mc146818rtc.c6
-rw-r--r--hw/pam.c87
-rw-r--r--hw/pam.h97
-rw-r--r--hw/pc.c39
-rw-r--r--hw/pc.h3
-rw-r--r--hw/pc_piix.c79
-rw-r--r--hw/pc_q35.c223
-rw-r--r--hw/pci-hotplug.c8
-rw-r--r--hw/pci.c2
-rw-r--r--hw/pci_ids.h14
-rw-r--r--hw/piix_pci.c68
-rw-r--r--hw/q35.c309
-rw-r--r--hw/q35.h150
-rw-r--r--hw/qdev-core.h5
-rw-r--r--hw/qdev.c26
-rw-r--r--hw/smbus_ich9.c159
-rw-r--r--hw/sysbus.c2
-rw-r--r--hw/virtio-rng.c95
-rw-r--r--include/qemu/object.h29
-rw-r--r--include/qemu/rng.h6
-rw-r--r--kvm.h2
-rw-r--r--qapi/qapi-dealloc-visitor.c6
-rw-r--r--qom/object.c16
-rw-r--r--tests/Makefile4
-rw-r--r--tests/rtc-test.c40
-rw-r--r--tests/test-aio.c667
-rw-r--r--tests/test-thread-pool.c216
-rw-r--r--vl.c12
41 files changed, 3414 insertions, 255 deletions
diff --git a/Makefile.target b/Makefile.target
index 8b658c0d13..927347bac2 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -143,6 +143,9 @@ GENERATED_HEADERS += hmp-commands.h qmp-commands-old.h
 
 endif # CONFIG_SOFTMMU
 
+# Workaround for http://gcc.gnu.org/PR55489, see configure.
+%/translate.o: QEMU_CFLAGS += $(TRANSLATE_OPT_CFLAGS)
+
 nested-vars += obj-y
 
 # This resolves all nested paths, so it must come last
diff --git a/VERSION b/VERSION
index 2d04904622..1ec5aef844 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.2.90
+1.2.91
diff --git a/aio-win32.c b/aio-win32.c
index a84eb71246..cec4646635 100644
--- a/aio-win32.c
+++ b/aio-win32.c
@@ -173,7 +173,7 @@ bool aio_poll(AioContext *ctx, bool blocking)
     }
 
     /* wait until next event */
-    for (;;) {
+    while (count > 0) {
         int timeout = blocking ? INFINITE : 0;
         int ret = WaitForMultipleObjects(count, events, FALSE, timeout);
 
@@ -209,6 +209,9 @@ bool aio_poll(AioContext *ctx, bool blocking)
                 g_free(tmp);
             }
         }
+
+        /* Try again, but only call each handler once.  */
+        events[ret - WAIT_OBJECT_0] = events[--count];
     }
 
     return progress;
diff --git a/configure b/configure
index 780b19afd6..994f7310b8 100755
--- a/configure
+++ b/configure
@@ -1183,6 +1183,21 @@ for flag in $gcc_flags; do
     fi
 done
 
+# Workaround for http://gcc.gnu.org/PR55489.  Happens with -fPIE/-fPIC and
+# large functions that use global variables.  The bug is in all releases of
+# GCC, but it became particularly acute in 4.6.x and 4.7.x.  It is fixed in
+# 4.7.3 and 4.8.0.  We should be able to delete this at the end of 2013.
+cat > $TMPC << EOF
+#if __GNUC__ == 4 && (__GNUC_MINOR__ == 6 || (__GNUC_MINOR__ == 7 && __GNUC_PATCHLEVEL__ <= 2))
+int main(void) { return 0; }
+#else
+#error No bug in this compiler.
+#endif
+EOF
+if compile_prog "-Werror -fno-gcse" "" ; then
+  TRANSLATE_OPT_CFLAGS=-fno-gcse
+fi
+
 if test "$static" = "yes" ; then
   if test "$pie" = "yes" ; then
     echo "static and pie are mutually incompatible"
@@ -3662,6 +3677,7 @@ echo "LIBS_TOOLS+=$libs_tools" >> $config_host_mak
 echo "EXESUF=$EXESUF" >> $config_host_mak
 echo "LIBS_QGA+=$libs_qga" >> $config_host_mak
 echo "POD2MAN=$POD2MAN" >> $config_host_mak
+echo "TRANSLATE_OPT_CFLAGS=$TRANSLATE_OPT_CFLAGS" >> $config_host_mak
 
 # generate list of library paths for linker script
 
diff --git a/hw/Makefile.objs b/hw/Makefile.objs
index ea46f8128e..d581d8d6d6 100644
--- a/hw/Makefile.objs
+++ b/hw/Makefile.objs
@@ -10,6 +10,7 @@ common-obj-$(CONFIG_PCI) += shpc.o
 common-obj-$(CONFIG_PCI) += slotid_cap.o
 common-obj-$(CONFIG_PCI) += pci_host.o pcie_host.o
 common-obj-$(CONFIG_PCI) += ioh3420.o xio3130_upstream.o xio3130_downstream.o
+common-obj-$(CONFIG_PCI) += i82801b11.o
 common-obj-y += watchdog.o
 common-obj-$(CONFIG_ISA_MMIO) += isa_mmio.o
 common-obj-$(CONFIG_ECC) += ecc.o
@@ -28,7 +29,7 @@ common-obj-$(CONFIG_I8254) += i8254_common.o i8254.o
 common-obj-$(CONFIG_PCSPK) += pcspk.o
 common-obj-$(CONFIG_PCKBD) += pckbd.o
 common-obj-$(CONFIG_FDC) += fdc.o
-common-obj-$(CONFIG_ACPI) += acpi.o acpi_piix4.o
+common-obj-$(CONFIG_ACPI) += acpi.o acpi_piix4.o acpi_ich9.o smbus_ich9.o
 common-obj-$(CONFIG_APM) += pm_smbus.o apm.o
 common-obj-$(CONFIG_DMA) += dma.o
 common-obj-$(CONFIG_I82374) += i82374.o
@@ -38,6 +39,7 @@ common-obj-$(CONFIG_SMARTCARD) += ccid-card-passthru.o
 common-obj-$(CONFIG_SMARTCARD_NSS) += ccid-card-emulated.o
 common-obj-$(CONFIG_I8259) += i8259_common.o i8259.o
 common-obj-y += fifo.o
+common-obj-y += pam.o
 
 # PPC devices
 common-obj-$(CONFIG_PREP_PCI) += prep_pci.o
diff --git a/hw/acpi_ich9.c b/hw/acpi_ich9.c
new file mode 100644
index 0000000000..61034d3bd7
--- /dev/null
+++ b/hw/acpi_ich9.c
@@ -0,0 +1,322 @@
+/*
+ * ACPI implementation
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+/*
+ *  Copyright (c) 2009 Isaku Yamahata <yamahata at valinux co jp>
+ *                     VA Linux Systems Japan K.K.
+ *  Copyright (C) 2012 Jason Baron <jbaron@redhat.com>
+ *
+ *  This is based on acpi.c.
+ */
+#include "hw.h"
+#include "pc.h"
+#include "pci.h"
+#include "qemu-timer.h"
+#include "sysemu.h"
+#include "acpi.h"
+#include "kvm.h"
+
+#include "ich9.h"
+
+//#define DEBUG
+
+#ifdef DEBUG
+#define ICH9_DEBUG(fmt, ...) \
+do { printf("%s "fmt, __func__, ## __VA_ARGS__); } while (0)
+#else
+#define ICH9_DEBUG(fmt, ...)    do { } while (0)
+#endif
+
+static void pm_ioport_write_fallback(void *opaque, uint32_t addr, int len,
+                                     uint32_t val);
+static uint32_t pm_ioport_read_fallback(void *opaque, uint32_t addr, int len);
+
+static void pm_update_sci(ICH9LPCPMRegs *pm)
+{
+    int sci_level, pm1a_sts;
+
+    pm1a_sts = acpi_pm1_evt_get_sts(&pm->acpi_regs);
+
+    sci_level = (((pm1a_sts & pm->acpi_regs.pm1.evt.en) &
+                  (ACPI_BITMASK_RT_CLOCK_ENABLE |
+                   ACPI_BITMASK_POWER_BUTTON_ENABLE |
+                   ACPI_BITMASK_GLOBAL_LOCK_ENABLE |
+                   ACPI_BITMASK_TIMER_ENABLE)) != 0);
+    qemu_set_irq(pm->irq, sci_level);
+
+    /* schedule a timer interruption if needed */
+    acpi_pm_tmr_update(&pm->acpi_regs,
+                       (pm->acpi_regs.pm1.evt.en & ACPI_BITMASK_TIMER_ENABLE) &&
+                       !(pm1a_sts & ACPI_BITMASK_TIMER_STATUS));
+}
+
+static void ich9_pm_update_sci_fn(ACPIREGS *regs)
+{
+    ICH9LPCPMRegs *pm = container_of(regs, ICH9LPCPMRegs, acpi_regs);
+    pm_update_sci(pm);
+}
+
+static void pm_ioport_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+    ICH9LPCPMRegs *pm = opaque;
+
+    switch (addr & ICH9_PMIO_MASK) {
+    case ICH9_PMIO_GPE0_STS ... (ICH9_PMIO_GPE0_STS + ICH9_PMIO_GPE0_LEN - 1):
+        acpi_gpe_ioport_writeb(&pm->acpi_regs, addr, val);
+        break;
+    default:
+        break;
+    }
+
+    ICH9_DEBUG("port=0x%04x val=0x%04x\n", addr, val);
+}
+
+static uint32_t pm_ioport_readb(void *opaque, uint32_t addr)
+{
+    ICH9LPCPMRegs *pm = opaque;
+    uint32_t val = 0;
+
+    switch (addr & ICH9_PMIO_MASK) {
+    case ICH9_PMIO_GPE0_STS ... (ICH9_PMIO_GPE0_STS + ICH9_PMIO_GPE0_LEN - 1):
+        val = acpi_gpe_ioport_readb(&pm->acpi_regs, addr);
+        break;
+    default:
+        val = 0;
+        break;
+    }
+    ICH9_DEBUG("port=0x%04x val=0x%04x\n", addr, val);
+    return val;
+}
+
+static void pm_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
+{
+    ICH9LPCPMRegs *pm = opaque;
+
+    switch (addr & ICH9_PMIO_MASK) {
+    case ICH9_PMIO_PM1_STS:
+        acpi_pm1_evt_write_sts(&pm->acpi_regs, val);
+        pm_update_sci(pm);
+        break;
+    case ICH9_PMIO_PM1_EN:
+        pm->acpi_regs.pm1.evt.en = val;
+        pm_update_sci(pm);
+        break;
+    case ICH9_PMIO_PM1_CNT:
+        acpi_pm1_cnt_write(&pm->acpi_regs, val, 0);
+        break;
+    default:
+        pm_ioport_write_fallback(opaque, addr, 2, val);
+        break;
+    }
+    ICH9_DEBUG("port=0x%04x val=0x%04x\n", addr, val);
+}
+
+static uint32_t pm_ioport_readw(void *opaque, uint32_t addr)
+{
+    ICH9LPCPMRegs *pm = opaque;
+    uint32_t val;
+
+    switch (addr & ICH9_PMIO_MASK) {
+    case ICH9_PMIO_PM1_STS:
+        val = acpi_pm1_evt_get_sts(&pm->acpi_regs);
+        break;
+    case ICH9_PMIO_PM1_EN:
+        val = pm->acpi_regs.pm1.evt.en;
+        break;
+    case ICH9_PMIO_PM1_CNT:
+        val = pm->acpi_regs.pm1.cnt.cnt;
+        break;
+    default:
+        val = pm_ioport_read_fallback(opaque, addr, 2);
+        break;
+    }
+    ICH9_DEBUG("port=0x%04x val=0x%04x\n", addr, val);
+    return val;
+}
+
+static void pm_ioport_writel(void *opaque, uint32_t addr, uint32_t val)
+{
+    ICH9LPCPMRegs *pm = opaque;
+
+    switch (addr & ICH9_PMIO_MASK) {
+    case ICH9_PMIO_SMI_EN:
+        pm->smi_en = val;
+        break;
+    default:
+        pm_ioport_write_fallback(opaque, addr, 4, val);
+        break;
+    }
+    ICH9_DEBUG("port=0x%04x val=0x%08x\n", addr, val);
+}
+
+static uint32_t pm_ioport_readl(void *opaque, uint32_t addr)
+{
+    ICH9LPCPMRegs *pm = opaque;
+    uint32_t val;
+
+    switch (addr & ICH9_PMIO_MASK) {
+    case ICH9_PMIO_PM1_TMR:
+        val = acpi_pm_tmr_get(&pm->acpi_regs);
+        break;
+    case ICH9_PMIO_SMI_EN:
+        val = pm->smi_en;
+        break;
+
+    default:
+        val = pm_ioport_read_fallback(opaque, addr, 4);
+        break;
+    }
+    ICH9_DEBUG("port=0x%04x val=0x%08x\n", addr, val);
+    return val;
+}
+
+static void pm_ioport_write_fallback(void *opaque, uint32_t addr, int len,
+                                     uint32_t val)
+ {
+    int subsize = (len == 4) ? 2 : 1;
+    IOPortWriteFunc *ioport_write =
+        (subsize == 2) ? pm_ioport_writew : pm_ioport_writeb;
+
+    int i;
+
+    for (i = 0; i < len; i += subsize) {
+        ioport_write(opaque, addr, val);
+        val >>= 8 * subsize;
+    }
+}
+
+static uint32_t pm_ioport_read_fallback(void *opaque, uint32_t addr, int len)
+{
+    int subsize = (len == 4) ? 2 : 1;
+    IOPortReadFunc *ioport_read =
+        (subsize == 2) ? pm_ioport_readw : pm_ioport_readb;
+
+    uint32_t val;
+    int i;
+
+    val = 0;
+    for (i = 0; i < len; i += subsize) {
+        val <<= 8 * subsize;
+        val |= ioport_read(opaque, addr);
+    }
+
+    return val;
+}
+
+void ich9_pm_iospace_update(ICH9LPCPMRegs *pm, uint32_t pm_io_base)
+{
+    ICH9_DEBUG("to 0x%x\n", pm_io_base);
+
+    assert((pm_io_base & ICH9_PMIO_MASK) == 0);
+
+    if (pm->pm_io_base != 0) {
+        isa_unassign_ioport(pm->pm_io_base, ICH9_PMIO_SIZE);
+    }
+
+    /* don't map at 0 */
+    if (pm_io_base == 0) {
+        return;
+    }
+
+    register_ioport_write(pm_io_base, ICH9_PMIO_SIZE, 1, pm_ioport_writeb, pm);
+    register_ioport_read(pm_io_base, ICH9_PMIO_SIZE, 1, pm_ioport_readb, pm);
+    register_ioport_write(pm_io_base, ICH9_PMIO_SIZE, 2, pm_ioport_writew, pm);
+    register_ioport_read(pm_io_base, ICH9_PMIO_SIZE, 2, pm_ioport_readw, pm);
+    register_ioport_write(pm_io_base, ICH9_PMIO_SIZE, 4, pm_ioport_writel, pm);
+    register_ioport_read(pm_io_base, ICH9_PMIO_SIZE, 4, pm_ioport_readl, pm);
+
+    pm->pm_io_base = pm_io_base;
+    acpi_gpe_blk(&pm->acpi_regs, pm_io_base + ICH9_PMIO_GPE0_STS);
+}
+
+static int ich9_pm_post_load(void *opaque, int version_id)
+{
+    ICH9LPCPMRegs *pm = opaque;
+    uint32_t pm_io_base = pm->pm_io_base;
+    pm->pm_io_base = 0;
+    ich9_pm_iospace_update(pm, pm_io_base);
+    return 0;
+}
+
+#define VMSTATE_GPE_ARRAY(_field, _state)                            \
+ {                                                                   \
+     .name       = (stringify(_field)),                              \
+     .version_id = 0,                                                \
+     .num        = ICH9_PMIO_GPE0_LEN,                               \
+     .info       = &vmstate_info_uint8,                              \
+     .size       = sizeof(uint8_t),                                  \
+     .flags      = VMS_ARRAY | VMS_POINTER,                          \
+     .offset     = vmstate_offset_pointer(_state, _field, uint8_t),  \
+ }
+
+const VMStateDescription vmstate_ich9_pm = {
+    .name = "ich9_pm",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .post_load = ich9_pm_post_load,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT16(acpi_regs.pm1.evt.sts, ICH9LPCPMRegs),
+        VMSTATE_UINT16(acpi_regs.pm1.evt.en, ICH9LPCPMRegs),
+        VMSTATE_UINT16(acpi_regs.pm1.cnt.cnt, ICH9LPCPMRegs),
+        VMSTATE_TIMER(acpi_regs.tmr.timer, ICH9LPCPMRegs),
+        VMSTATE_INT64(acpi_regs.tmr.overflow_time, ICH9LPCPMRegs),
+        VMSTATE_GPE_ARRAY(acpi_regs.gpe.sts, ICH9LPCPMRegs),
+        VMSTATE_GPE_ARRAY(acpi_regs.gpe.en, ICH9LPCPMRegs),
+        VMSTATE_UINT32(smi_en, ICH9LPCPMRegs),
+        VMSTATE_UINT32(smi_sts, ICH9LPCPMRegs),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void pm_reset(void *opaque)
+{
+    ICH9LPCPMRegs *pm = opaque;
+    ich9_pm_iospace_update(pm, 0);
+
+    acpi_pm1_evt_reset(&pm->acpi_regs);
+    acpi_pm1_cnt_reset(&pm->acpi_regs);
+    acpi_pm_tmr_reset(&pm->acpi_regs);
+    acpi_gpe_reset(&pm->acpi_regs);
+
+    if (kvm_enabled()) {
+        /* Mark SMM as already inited to prevent SMM from running. KVM does not
+         * support SMM mode. */
+        pm->smi_en |= ICH9_PMIO_SMI_EN_APMC_EN;
+    }
+
+    pm_update_sci(pm);
+}
+
+static void pm_powerdown_req(Notifier *n, void *opaque)
+{
+    ICH9LPCPMRegs *pm = container_of(n, ICH9LPCPMRegs, powerdown_notifier);
+
+    acpi_pm1_evt_power_down(&pm->acpi_regs);
+}
+
+void ich9_pm_init(ICH9LPCPMRegs *pm, qemu_irq sci_irq, qemu_irq cmos_s3)
+{
+    acpi_pm_tmr_init(&pm->acpi_regs, ich9_pm_update_sci_fn);
+    acpi_pm1_cnt_init(&pm->acpi_regs);
+    acpi_gpe_init(&pm->acpi_regs, ICH9_PMIO_GPE0_LEN);
+
+    pm->irq = sci_irq;
+    qemu_register_reset(pm_reset, pm);
+    pm->powerdown_notifier.notify = pm_powerdown_req;
+    qemu_register_powerdown_notifier(&pm->powerdown_notifier);
+}
diff --git a/hw/acpi_ich9.h b/hw/acpi_ich9.h
new file mode 100644
index 0000000000..180c40673b
--- /dev/null
+++ b/hw/acpi_ich9.h
@@ -0,0 +1,47 @@
+/*
+ * QEMU GMCH/ICH9 LPC PM Emulation
+ *
+ *  Copyright (c) 2009 Isaku Yamahata <yamahata at valinux co jp>
+ *                     VA Linux Systems Japan K.K.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#ifndef HW_ACPI_ICH9_H
+#define HW_ACPI_ICH9_H
+
+#include "acpi.h"
+
+typedef struct ICH9LPCPMRegs {
+    /*
+     * In ich9 spec says that pm1_cnt register is 32bit width and
+     * that the upper 16bits are reserved and unused.
+     * PM1a_CNT_BLK = 2 in FADT so it is defined as uint16_t.
+     */
+    ACPIREGS acpi_regs;
+    uint32_t smi_en;
+    uint32_t smi_sts;
+
+    qemu_irq irq;      /* SCI */
+
+    uint32_t pm_io_base;
+    Notifier powerdown_notifier;
+} ICH9LPCPMRegs;
+
+void ich9_pm_init(ICH9LPCPMRegs *pm,
+                  qemu_irq sci_irq, qemu_irq cmos_s3_resume);
+void ich9_pm_iospace_update(ICH9LPCPMRegs *pm, uint32_t pm_io_base);
+extern const VMStateDescription vmstate_ich9_pm;
+
+#endif /* HW_ACPI_ICH9_H */
diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs
index 8c764bbfef..0d3f6a8e84 100644
--- a/hw/i386/Makefile.objs
+++ b/hw/i386/Makefile.objs
@@ -6,6 +6,7 @@ obj-y += pci-hotplug.o smbios.o wdt_ib700.o
 obj-y += debugcon.o multiboot.o
 obj-y += pc_piix.o
 obj-y += pc_sysfw.o
+obj-y += lpc_ich9.o q35.o pc_q35.o
 obj-$(CONFIG_XEN) += xen_platform.o xen_apic.o
 obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen-host-pci-device.o
 obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_msi.o
diff --git a/hw/i8259_internal.h b/hw/i8259_internal.h
index 4137b61703..8785b1da3f 100644
--- a/hw/i8259_internal.h
+++ b/hw/i8259_internal.h
@@ -33,7 +33,7 @@ typedef struct PICCommonState PICCommonState;
 
 #define TYPE_PIC_COMMON "pic-common"
 #define PIC_COMMON(obj) \
-     OBJECT_CHECK(PICCommon, (obj), TYPE_PIC_COMMON)
+     OBJECT_CHECK(PICCommonState, (obj), TYPE_PIC_COMMON)
 #define PIC_COMMON_CLASS(klass) \
      OBJECT_CLASS_CHECK(PICCommonClass, (klass), TYPE_PIC_COMMON)
 #define PIC_COMMON_GET_CLASS(obj) \
diff --git a/hw/i82801b11.c b/hw/i82801b11.c
new file mode 100644
index 0000000000..3d1f996b2f
--- /dev/null
+++ b/hw/i82801b11.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * 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.
+ */
+/*
+ * QEMU i82801b11 dmi-to-pci Bridge Emulation
+ *
+ *  Copyright (c) 2009, 2010, 2011
+ *                Isaku Yamahata <yamahata at valinux co jp>
+ *                VA Linux Systems Japan K.K.
+ *  Copyright (C) 2012 Jason Baron <jbaron@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#include "pci.h"
+#include "ich9.h"
+
+
+/*****************************************************************************/
+/* ICH9 DMI-to-PCI bridge */
+#define I82801ba_SSVID_OFFSET   0x50
+#define I82801ba_SSVID_SVID     0
+#define I82801ba_SSVID_SSID     0
+
+typedef struct I82801b11Bridge {
+    PCIBridge br;
+} I82801b11Bridge;
+
+static int i82801b11_bridge_initfn(PCIDevice *d)
+{
+    int rc;
+
+    rc = pci_bridge_initfn(d);
+    if (rc < 0) {
+        return rc;
+    }
+
+    rc = pci_bridge_ssvid_init(d, I82801ba_SSVID_OFFSET,
+                               I82801ba_SSVID_SVID, I82801ba_SSVID_SSID);
+    if (rc < 0) {
+        goto err_bridge;
+    }
+    pci_config_set_prog_interface(d->config, PCI_CLASS_BRDIGE_PCI_INF_SUB);
+    return 0;
+
+err_bridge:
+    pci_bridge_exitfn(d);
+
+    return rc;
+}
+
+static void i82801b11_bridge_class_init(ObjectClass *klass, void *data)
+{
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->is_bridge = 1;
+    k->vendor_id = PCI_VENDOR_ID_INTEL;
+    k->device_id = PCI_DEVICE_ID_INTEL_82801BA_11;
+    k->revision = ICH9_D2P_A2_REVISION;
+    k->init = i82801b11_bridge_initfn;
+}
+
+static const TypeInfo i82801b11_bridge_info = {
+    .name          = "i82801b11-bridge",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(I82801b11Bridge),
+    .class_init    = i82801b11_bridge_class_init,
+};
+
+PCIBus *ich9_d2pbr_init(PCIBus *bus, int devfn, int sec_bus)
+{
+    PCIDevice *d;
+    PCIBridge *br;
+    char buf[16];
+    DeviceState *qdev;
+
+    d = pci_create_multifunction(bus, devfn, true, "i82801b11-bridge");
+    if (!d) {
+        return NULL;
+    }
+    br = DO_UPCAST(PCIBridge, dev, d);
+    qdev = &br->dev.qdev;
+
+    snprintf(buf, sizeof(buf), "pci.%d", sec_bus);
+    pci_bridge_map_irq(br, buf, pci_swizzle_map_irq_fn);
+    qdev_init_nofail(qdev);
+
+    return pci_bridge_get_sec_bus(br);
+}
+
+static void d2pbr_register(void)
+{
+    type_register_static(&i82801b11_bridge_info);
+}
+
+type_init(d2pbr_register);
diff --git a/hw/ich9.h b/hw/ich9.h
new file mode 100644
index 0000000000..de491350c3
--- /dev/null
+++ b/hw/ich9.h
@@ -0,0 +1,207 @@
+#ifndef HW_ICH9_H
+#define HW_ICH9_H
+
+#include "hw.h"
+#include "range.h"
+#include "isa.h"
+#include "sysbus.h"
+#include "pc.h"
+#include "apm.h"
+#include "ioapic.h"
+#include "pci.h"
+#include "pcie_host.h"
+#include "pci_bridge.h"
+#include "acpi.h"
+#include "acpi_ich9.h"
+#include "pam.h"
+#include "pci_internals.h"
+
+void ich9_lpc_set_irq(void *opaque, int irq_num, int level);
+int ich9_lpc_map_irq(PCIDevice *pci_dev, int intx);
+void ich9_lpc_pm_init(PCIDevice *pci_lpc, qemu_irq cmos_s3);
+PCIBus *ich9_d2pbr_init(PCIBus *bus, int devfn, int sec_bus);
+i2c_bus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base);
+
+#define ICH9_CC_SIZE                            (16 * 1024)     /* 16KB */
+
+#define TYPE_ICH9_LPC_DEVICE "ICH9 LPC"
+#define ICH9_LPC_DEVICE(obj) \
+     OBJECT_CHECK(ICH9LPCState, (obj), TYPE_ICH9_LPC_DEVICE)
+
+typedef struct ICH9LPCState {
+    /* ICH9 LPC PCI to ISA bridge */
+    PCIDevice d;
+
+    /* (pci device, intx) -> pirq
+     * In real chipset case, the unused slots are never used
+     * as ICH9 supports only D25-D32 irq routing.
+     * On the other hand in qemu case, any slot/function can be populated
+     * via command line option.
+     * So fallback interrupt routing for any devices in any slots is necessary.
+    */
+    uint8_t irr[PCI_SLOT_MAX][PCI_NUM_PINS];
+
+    APMState apm;
+    ICH9LPCPMRegs pm;
+    uint32_t sci_level; /* track sci level */
+
+    /* 10.1 Chipset Configuration registers(Memory Space)
+     which is pointed by RCBA */
+    uint8_t chip_config[ICH9_CC_SIZE];
+    /* isa bus */
+    ISABus *isa_bus;
+    MemoryRegion rbca_mem;
+
+    qemu_irq *pic;
+    qemu_irq *ioapic;
+} ICH9LPCState;
+
+#define Q35_MASK(bit, ms_bit, ls_bit) \
+((uint##bit##_t)(((1ULL << ((ms_bit) + 1)) - 1) & ~((1ULL << ls_bit) - 1)))
+
+/* ICH9: Chipset Configuration Registers */
+#define ICH9_CC_ADDR_MASK                       (ICH9_CC_SIZE - 1)
+
+#define ICH9_CC
+#define ICH9_CC_D28IP                           0x310C
+#define ICH9_CC_D28IP_SHIFT                     4
+#define ICH9_CC_D28IP_MASK                      0xf
+#define ICH9_CC_D28IP_DEFAULT                   0x00214321
+#define ICH9_CC_D31IR                           0x3140
+#define ICH9_CC_D30IR                           0x3142
+#define ICH9_CC_D29IR                           0x3144
+#define ICH9_CC_D28IR                           0x3146
+#define ICH9_CC_D27IR                           0x3148
+#define ICH9_CC_D26IR                           0x314C
+#define ICH9_CC_D25IR                           0x3150
+#define ICH9_CC_DIR_DEFAULT                     0x3210
+#define ICH9_CC_D30IR_DEFAULT                   0x0
+#define ICH9_CC_DIR_SHIFT                       4
+#define ICH9_CC_DIR_MASK                        0x7
+#define ICH9_CC_OIC                             0x31FF
+#define ICH9_CC_OIC_AEN                         0x1
+
+/* D28:F[0-5] */
+#define ICH9_PCIE_DEV                           28
+#define ICH9_PCIE_FUNC_MAX                      6
+
+
+/* D29:F0 USB UHCI Controller #1 */
+#define ICH9_USB_UHCI1_DEV                      29
+#define ICH9_USB_UHCI1_FUNC                     0
+
+/* D30:F0 DMI-to-PCI brdige */
+#define ICH9_D2P_BRIDGE                         "ICH9 D2P BRIDGE"
+#define ICH9_D2P_BRIDGE_SAVEVM_VERSION          0
+
+#define ICH9_D2P_BRIDGE_DEV                     30
+#define ICH9_D2P_BRIDGE_FUNC                    0
+
+#define ICH9_D2P_SECONDARY_DEFAULT              (256 - 8)
+
+#define ICH9_D2P_A2_REVISION                    0x92
+
+
+/* D31:F1 LPC controller */
+#define ICH9_A2_LPC                             "ICH9 A2 LPC"
+#define ICH9_A2_LPC_SAVEVM_VERSION              0
+
+#define ICH9_LPC_DEV                            31
+#define ICH9_LPC_FUNC                           0
+
+#define ICH9_A2_LPC_REVISION                    0x2
+#define ICH9_LPC_NB_PIRQS                       8       /* PCI A-H */
+
+#define ICH9_LPC_PMBASE                         0x40
+#define ICH9_LPC_PMBASE_BASE_ADDRESS_MASK       Q35_MASK(32, 15, 7)
+#define ICH9_LPC_PMBASE_RTE                     0x1
+#define ICH9_LPC_PMBASE_DEFAULT                 0x1
+#define ICH9_LPC_ACPI_CTRL                      0x44
+#define ICH9_LPC_ACPI_CTRL_ACPI_EN              0x80
+#define ICH9_LPC_ACPI_CTRL_SCI_IRQ_SEL_MASK     Q35_MASK(8, 2, 0)
+#define ICH9_LPC_ACPI_CTRL_9                    0x0
+#define ICH9_LPC_ACPI_CTRL_10                   0x1
+#define ICH9_LPC_ACPI_CTRL_11                   0x2
+#define ICH9_LPC_ACPI_CTRL_20                   0x4
+#define ICH9_LPC_ACPI_CTRL_21                   0x5
+#define ICH9_LPC_ACPI_CTRL_DEFAULT              0x0
+
+#define ICH9_LPC_PIRQA_ROUT                     0x60
+#define ICH9_LPC_PIRQB_ROUT                     0x61
+#define ICH9_LPC_PIRQC_ROUT                     0x62
+#define ICH9_LPC_PIRQD_ROUT                     0x63
+
+#define ICH9_LPC_PIRQE_ROUT                     0x68
+#define ICH9_LPC_PIRQF_ROUT                     0x69
+#define ICH9_LPC_PIRQG_ROUT                     0x6a
+#define ICH9_LPC_PIRQH_ROUT                     0x6b
+
+#define ICH9_LPC_PIRQ_ROUT_IRQEN                0x80
+#define ICH9_LPC_PIRQ_ROUT_MASK                 Q35_MASK(8, 3, 0)
+#define ICH9_LPC_PIRQ_ROUT_DEFAULT              0x80
+
+#define ICH9_LPC_RCBA                           0xf0
+#define ICH9_LPC_RCBA_BA_MASK                   Q35_MASK(32, 31, 14)
+#define ICH9_LPC_RCBA_EN                        0x1
+#define ICH9_LPC_RCBA_DEFAULT                   0x0
+
+#define ICH9_LPC_PIC_NUM_PINS                   16
+#define ICH9_LPC_IOAPIC_NUM_PINS                24
+
+/* D31:F2 SATA Controller #1 */
+#define ICH9_SATA1_DEV                          31
+#define ICH9_SATA1_FUNC                         2
+
+/* D30:F1 power management I/O registers
+   offset from the address ICH9_LPC_PMBASE */
+
+/* ICH9 LPC PM I/O registers are 128 ports and 128-aligned */
+#define ICH9_PMIO_SIZE                          128
+#define ICH9_PMIO_MASK                          (ICH9_PMIO_SIZE - 1)
+
+#define ICH9_PMIO_PM1_STS                       0x00
+#define ICH9_PMIO_PM1_EN                        0x02
+#define ICH9_PMIO_PM1_CNT                       0x04
+#define ICH9_PMIO_PM1_TMR                       0x08
+#define ICH9_PMIO_GPE0_STS                      0x20
+#define ICH9_PMIO_GPE0_EN                       0x28
+#define ICH9_PMIO_GPE0_LEN                      16
+#define ICH9_PMIO_SMI_EN                        0x30
+#define ICH9_PMIO_SMI_EN_APMC_EN                (1 << 5)
+#define ICH9_PMIO_SMI_STS                       0x34
+
+/* FADT ACPI_ENABLE/ACPI_DISABLE */
+#define ICH9_APM_ACPI_ENABLE                    0x2
+#define ICH9_APM_ACPI_DISABLE                   0x3
+
+
+/* D31:F3 SMBus controller */
+#define ICH9_A2_SMB_REVISION                    0x02
+#define ICH9_SMB_PI                             0x00
+
+#define ICH9_SMB_SMBMBAR0                       0x10
+#define ICH9_SMB_SMBMBAR1                       0x14
+#define ICH9_SMB_SMBM_BAR                       0
+#define ICH9_SMB_SMBM_SIZE                      (1 << 8)
+#define ICH9_SMB_SMB_BASE                       0x20
+#define ICH9_SMB_SMB_BASE_BAR                   4
+#define ICH9_SMB_SMB_BASE_SIZE                  (1 << 5)
+#define ICH9_SMB_HOSTC                          0x40
+#define ICH9_SMB_HOSTC_SSRESET                  ((uint8_t)(1 << 3))
+#define ICH9_SMB_HOSTC_I2C_EN                   ((uint8_t)(1 << 2))
+#define ICH9_SMB_HOSTC_SMB_SMI_EN               ((uint8_t)(1 << 1))
+#define ICH9_SMB_HOSTC_HST_EN                   ((uint8_t)(1 << 0))
+
+/* D31:F3 SMBus I/O and memory mapped I/O registers */
+#define ICH9_SMB_DEV                            31
+#define ICH9_SMB_FUNC                           3
+
+#define ICH9_SMB_HST_STS                        0x00
+#define ICH9_SMB_HST_CNT                        0x02
+#define ICH9_SMB_HST_CMD                        0x03
+#define ICH9_SMB_XMIT_SLVA                      0x04
+#define ICH9_SMB_HST_D0                         0x05
+#define ICH9_SMB_HST_D1                         0x06
+#define ICH9_SMB_HOST_BLOCK_DB                  0x07
+
+#endif /* HW_ICH9_H */
diff --git a/hw/kvm/ioapic.c b/hw/kvm/ioapic.c
index 6c3b8fe39a..f95c157591 100644
--- a/hw/kvm/ioapic.c
+++ b/hw/kvm/ioapic.c
@@ -15,6 +15,46 @@
 #include "hw/apic_internal.h"
 #include "kvm.h"
 
+/* PC Utility function */
+void kvm_pc_setup_irq_routing(bool pci_enabled)
+{
+    KVMState *s = kvm_state;
+    int i;
+
+    if (kvm_check_extension(s, KVM_CAP_IRQ_ROUTING)) {
+        for (i = 0; i < 8; ++i) {
+            if (i == 2) {
+                continue;
+            }
+            kvm_irqchip_add_irq_route(s, i, KVM_IRQCHIP_PIC_MASTER, i);
+        }
+        for (i = 8; i < 16; ++i) {
+            kvm_irqchip_add_irq_route(s, i, KVM_IRQCHIP_PIC_SLAVE, i - 8);
+        }
+        if (pci_enabled) {
+            for (i = 0; i < 24; ++i) {
+                if (i == 0) {
+                    kvm_irqchip_add_irq_route(s, i, KVM_IRQCHIP_IOAPIC, 2);
+                } else if (i != 2) {
+                    kvm_irqchip_add_irq_route(s, i, KVM_IRQCHIP_IOAPIC, i);
+                }
+            }
+        }
+    }
+}
+
+void kvm_pc_gsi_handler(void *opaque, int n, int level)
+{
+    GSIState *s = opaque;
+
+    if (n < ISA_NUM_IRQS) {
+        /* Kernel will forward to both PIC and IOAPIC */
+        qemu_set_irq(s->i8259_irq[n], level);
+    } else {
+        qemu_set_irq(s->ioapic_irq[n], level);
+    }
+}
+
 typedef struct KVMIOAPICState KVMIOAPICState;
 
 struct KVMIOAPICState {
diff --git a/hw/lpc_ich9.c b/hw/lpc_ich9.c
new file mode 100644
index 0000000000..2fc83a496f
--- /dev/null
+++ b/hw/lpc_ich9.c
@@ -0,0 +1,525 @@
+/*
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * 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.
+ */
+/*
+ * QEMU ICH9 Emulation
+ *
+ *  Copyright (c) 2009, 2010, 2011
+ *                Isaku Yamahata <yamahata at valinux co jp>
+ *                VA Linux Systems Japan K.K.
+ *  Copyright (C) 2012 Jason Baron <jbaron@redhat.com>
+ *
+ *  This is based on piix_pci.c, but heavily modified.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#include "qemu-common.h"
+#include "hw.h"
+#include "range.h"
+#include "isa.h"
+#include "sysbus.h"
+#include "pc.h"
+#include "apm.h"
+#include "ioapic.h"
+#include "pci.h"
+#include "pcie_host.h"
+#include "pci_bridge.h"
+#include "ich9.h"
+#include "acpi.h"
+#include "acpi_ich9.h"
+#include "pam.h"
+#include "pci_internals.h"
+#include "exec-memory.h"
+
+static int ich9_lpc_sci_irq(ICH9LPCState *lpc);
+
+/*****************************************************************************/
+/* ICH9 LPC PCI to ISA bridge */
+
+static void ich9_lpc_reset(DeviceState *qdev);
+
+/* chipset configuration register
+ * to access chipset configuration registers, pci_[sg]et_{byte, word, long}
+ * are used.
+ * Although it's not pci configuration space, it's little endian as Intel.
+ */
+
+static void ich9_cc_update_ir(uint8_t irr[PCI_NUM_PINS], uint16_t ir)
+{
+    int intx;
+    for (intx = 0; intx < PCI_NUM_PINS; intx++) {
+        irr[intx] = (ir >> (intx * ICH9_CC_DIR_SHIFT)) & ICH9_CC_DIR_MASK;
+    }
+}
+
+static void ich9_cc_update(ICH9LPCState *lpc)
+{
+    int slot;
+    int pci_intx;
+
+    const int reg_offsets[] = {
+        ICH9_CC_D25IR,
+        ICH9_CC_D26IR,
+        ICH9_CC_D27IR,
+        ICH9_CC_D28IR,
+        ICH9_CC_D29IR,
+        ICH9_CC_D30IR,
+        ICH9_CC_D31IR,
+    };
+    const int *offset;
+
+    /* D{25 - 31}IR, but D30IR is read only to 0. */
+    for (slot = 25, offset = reg_offsets; slot < 32; slot++, offset++) {
+        if (slot == 30) {
+            continue;
+        }
+        ich9_cc_update_ir(lpc->irr[slot],
+                          pci_get_word(lpc->chip_config + *offset));
+    }
+
+    /*
+     * D30: DMI2PCI bridge
+     * It is arbitrarily decided how INTx lines of PCI devicesbehind the bridge
+     * are connected to pirq lines. Our choice is PIRQ[E-H].
+     * INT[A-D] are connected to PIRQ[E-H]
+     */
+    for (pci_intx = 0; pci_intx < PCI_NUM_PINS; pci_intx++) {
+        lpc->irr[30][pci_intx] = pci_intx + 4;
+    }
+}
+
+static void ich9_cc_init(ICH9LPCState *lpc)
+{
+    int slot;
+    int intx;
+
+    /* the default irq routing is arbitrary as long as it matches with
+     * acpi irq routing table.
+     * The one that is incompatible with piix_pci(= bochs) one is
+     * intentionally chosen to let the users know that the different
+     * board is used.
+     *
+     * int[A-D] -> pirq[E-F]
+     * avoid pirq A-D because they are used for pci express port
+     */
+    for (slot = 0; slot < PCI_SLOT_MAX; slot++) {
+        for (intx = 0; intx < PCI_NUM_PINS; intx++) {
+            lpc->irr[slot][intx] = (slot + intx) % 4 + 4;
+        }
+    }
+    ich9_cc_update(lpc);
+}
+
+static void ich9_cc_reset(ICH9LPCState *lpc)
+{
+    uint8_t *c = lpc->chip_config;
+
+    memset(lpc->chip_config, 0, sizeof(lpc->chip_config));
+
+    pci_set_long(c + ICH9_CC_D31IR, ICH9_CC_DIR_DEFAULT);
+    pci_set_long(c + ICH9_CC_D30IR, ICH9_CC_D30IR_DEFAULT);
+    pci_set_long(c + ICH9_CC_D29IR, ICH9_CC_DIR_DEFAULT);
+    pci_set_long(c + ICH9_CC_D28IR, ICH9_CC_DIR_DEFAULT);
+    pci_set_long(c + ICH9_CC_D27IR, ICH9_CC_DIR_DEFAULT);
+    pci_set_long(c + ICH9_CC_D26IR, ICH9_CC_DIR_DEFAULT);
+    pci_set_long(c + ICH9_CC_D25IR, ICH9_CC_DIR_DEFAULT);
+
+    ich9_cc_update(lpc);
+}
+
+static void ich9_cc_addr_len(uint64_t *addr, unsigned *len)
+{
+    *addr &= ICH9_CC_ADDR_MASK;
+    if (*addr + *len >= ICH9_CC_SIZE) {
+        *len = ICH9_CC_SIZE - *addr;
+    }
+}
+
+/* val: little endian */
+static void ich9_cc_write(void *opaque, hwaddr addr,
+                          uint64_t val, unsigned len)
+{
+    ICH9LPCState *lpc = (ICH9LPCState *)opaque;
+
+    ich9_cc_addr_len(&addr, &len);
+    memcpy(lpc->chip_config + addr, &val, len);
+    ich9_cc_update(lpc);
+}
+
+/* return value: little endian */
+static uint64_t ich9_cc_read(void *opaque, hwaddr addr,
+                              unsigned len)
+{
+    ICH9LPCState *lpc = (ICH9LPCState *)opaque;
+
+    uint32_t val = 0;
+    ich9_cc_addr_len(&addr, &len);
+    memcpy(&val, lpc->chip_config + addr, len);
+    return val;
+}
+
+/* IRQ routing */
+/* */
+static void ich9_lpc_rout(uint8_t pirq_rout, int *pic_irq, int *pic_dis)
+{
+    *pic_irq = pirq_rout & ICH9_LPC_PIRQ_ROUT_MASK;
+    *pic_dis = pirq_rout & ICH9_LPC_PIRQ_ROUT_IRQEN;
+}
+
+static void ich9_lpc_pic_irq(ICH9LPCState *lpc, int pirq_num,
+                             int *pic_irq, int *pic_dis)
+{
+    switch (pirq_num) {
+    case 0 ... 3: /* A-D */
+        ich9_lpc_rout(lpc->d.config[ICH9_LPC_PIRQA_ROUT + pirq_num],
+                      pic_irq, pic_dis);
+        return;
+    case 4 ... 7: /* E-H */
+        ich9_lpc_rout(lpc->d.config[ICH9_LPC_PIRQE_ROUT + (pirq_num - 4)],
+                      pic_irq, pic_dis);
+        return;
+    default:
+        break;
+    }
+    abort();
+}
+
+/* pic_irq: i8254 irq 0-15 */
+static void ich9_lpc_update_pic(ICH9LPCState *lpc, int pic_irq)
+{
+    int i, pic_level;
+
+    /* The pic level is the logical OR of all the PCI irqs mapped to it */
+    pic_level = 0;
+    for (i = 0; i < ICH9_LPC_NB_PIRQS; i++) {
+        int tmp_irq;
+        int tmp_dis;
+        ich9_lpc_pic_irq(lpc, i, &tmp_irq, &tmp_dis);
+        if (!tmp_dis && pic_irq == tmp_irq) {
+            pic_level |= pci_bus_get_irq_level(lpc->d.bus, i);
+        }
+    }
+    if (pic_irq == ich9_lpc_sci_irq(lpc)) {
+        pic_level |= lpc->sci_level;
+    }
+
+    qemu_set_irq(lpc->pic[pic_irq], pic_level);
+}
+
+/* pirq: pirq[A-H] 0-7*/
+static void ich9_lpc_update_by_pirq(ICH9LPCState *lpc, int pirq)
+{
+    int pic_irq;
+    int pic_dis;
+
+    ich9_lpc_pic_irq(lpc, pirq, &pic_irq, &pic_dis);
+    assert(pic_irq < ICH9_LPC_PIC_NUM_PINS);
+    if (pic_dis) {
+        return;
+    }
+
+    ich9_lpc_update_pic(lpc, pic_irq);
+}
+
+/* APIC mode: GSIx: PIRQ[A-H] -> GSI 16, ... no pirq shares same APIC pins. */
+static int ich9_pirq_to_gsi(int pirq)
+{
+    return pirq + ICH9_LPC_PIC_NUM_PINS;
+}
+
+static int ich9_gsi_to_pirq(int gsi)
+{
+    return gsi - ICH9_LPC_PIC_NUM_PINS;
+}
+
+static void ich9_lpc_update_apic(ICH9LPCState *lpc, int gsi)
+{
+    int level = 0;
+
+    if (gsi >= ICH9_LPC_PIC_NUM_PINS) {
+        level |= pci_bus_get_irq_level(lpc->d.bus, ich9_gsi_to_pirq(gsi));
+    }
+    if (gsi == ich9_lpc_sci_irq(lpc)) {
+        level |= lpc->sci_level;
+    }
+
+    qemu_set_irq(lpc->ioapic[gsi], level);
+}
+
+void ich9_lpc_set_irq(void *opaque, int pirq, int level)
+{
+    ICH9LPCState *lpc = opaque;
+
+    assert(0 <= pirq);
+    assert(pirq < ICH9_LPC_NB_PIRQS);
+
+    ich9_lpc_update_apic(lpc, ich9_pirq_to_gsi(pirq));
+    ich9_lpc_update_by_pirq(lpc, pirq);
+}
+
+/* return the pirq number (PIRQ[A-H]:0-7) corresponding to
+ * a given device irq pin.
+ */
+int ich9_lpc_map_irq(PCIDevice *pci_dev, int intx)
+{
+    BusState *bus = qdev_get_parent_bus(&pci_dev->qdev);
+    PCIBus *pci_bus = PCI_BUS(bus);
+    PCIDevice *lpc_pdev =
+            pci_bus->devices[PCI_DEVFN(ICH9_LPC_DEV, ICH9_LPC_FUNC)];
+    ICH9LPCState *lpc = ICH9_LPC_DEVICE(lpc_pdev);
+
+    return lpc->irr[PCI_SLOT(pci_dev->devfn)][intx];
+}
+
+static int ich9_lpc_sci_irq(ICH9LPCState *lpc)
+{
+    switch (lpc->d.config[ICH9_LPC_ACPI_CTRL] &
+            ICH9_LPC_ACPI_CTRL_SCI_IRQ_SEL_MASK) {
+    case ICH9_LPC_ACPI_CTRL_9:
+        return 9;
+    case ICH9_LPC_ACPI_CTRL_10:
+        return 10;
+    case ICH9_LPC_ACPI_CTRL_11:
+        return 11;
+    case ICH9_LPC_ACPI_CTRL_20:
+        return 20;
+    case ICH9_LPC_ACPI_CTRL_21:
+        return 21;
+    default:
+        /* reserved */
+        break;
+    }
+    return -1;
+}
+
+static void ich9_set_sci(void *opaque, int irq_num, int level)
+{
+    ICH9LPCState *lpc = opaque;
+    int irq;
+
+    assert(irq_num == 0);
+    level = !!level;
+    if (level == lpc->sci_level) {
+        return;
+    }
+    lpc->sci_level = level;
+
+    irq = ich9_lpc_sci_irq(lpc);
+    if (irq < 0) {
+        return;
+    }
+
+    ich9_lpc_update_apic(lpc, irq);
+    if (irq < ICH9_LPC_PIC_NUM_PINS) {
+        ich9_lpc_update_pic(lpc, irq);
+    }
+}
+
+void ich9_lpc_pm_init(PCIDevice *lpc_pci, qemu_irq cmos_s3)
+{
+    ICH9LPCState *lpc = ICH9_LPC_DEVICE(lpc_pci);
+    qemu_irq *sci_irq;
+
+    sci_irq = qemu_allocate_irqs(ich9_set_sci, lpc, 1);
+    ich9_pm_init(&lpc->pm, sci_irq[0], cmos_s3);
+
+    ich9_lpc_reset(&lpc->d.qdev);
+}
+
+/* APM */
+
+static void ich9_apm_ctrl_changed(uint32_t val, void *arg)
+{
+    ICH9LPCState *lpc = arg;
+
+    /* ACPI specs 3.0, 4.7.2.5 */
+    acpi_pm1_cnt_update(&lpc->pm.acpi_regs,
+                        val == ICH9_APM_ACPI_ENABLE,
+                        val == ICH9_APM_ACPI_DISABLE);
+
+    /* SMI_EN = PMBASE + 30. SMI control and enable register */
+    if (lpc->pm.smi_en & ICH9_PMIO_SMI_EN_APMC_EN) {
+        cpu_interrupt(first_cpu, CPU_INTERRUPT_SMI);
+    }
+}
+
+/* config:PMBASE */
+static void
+ich9_lpc_pmbase_update(ICH9LPCState *lpc)
+{
+    uint32_t pm_io_base = pci_get_long(lpc->d.config + ICH9_LPC_PMBASE);
+    pm_io_base &= ICH9_LPC_PMBASE_BASE_ADDRESS_MASK;
+
+    ich9_pm_iospace_update(&lpc->pm, pm_io_base);
+}
+
+/* config:RBCA */
+static void ich9_lpc_rcba_update(ICH9LPCState *lpc, uint32_t rbca_old)
+{
+    uint32_t rbca = pci_get_long(lpc->d.config + ICH9_LPC_RCBA);
+
+    if (rbca_old & ICH9_LPC_RCBA_EN) {
+            memory_region_del_subregion(get_system_memory(), &lpc->rbca_mem);
+    }
+    if (rbca & ICH9_LPC_RCBA_EN) {
+            memory_region_add_subregion_overlap(get_system_memory(),
+                                                rbca & ICH9_LPC_RCBA_BA_MASK,
+                                                &lpc->rbca_mem, 1);
+    }
+}
+
+static int ich9_lpc_post_load(void *opaque, int version_id)
+{
+    ICH9LPCState *lpc = opaque;
+
+    ich9_lpc_pmbase_update(lpc);
+    ich9_lpc_rcba_update(lpc, 0 /* disabled ICH9_LPC_RBCA_EN */);
+    return 0;
+}
+
+static void ich9_lpc_config_write(PCIDevice *d,
+                                  uint32_t addr, uint32_t val, int len)
+{
+    ICH9LPCState *lpc = ICH9_LPC_DEVICE(d);
+    uint32_t rbca_old = pci_get_long(d->config + ICH9_LPC_RCBA);
+
+    pci_default_write_config(d, addr, val, len);
+    if (ranges_overlap(addr, len, ICH9_LPC_PMBASE, 4)) {
+        ich9_lpc_pmbase_update(lpc);
+    }
+    if (ranges_overlap(addr, len, ICH9_LPC_RCBA, 4)) {
+        ich9_lpc_rcba_update(lpc, rbca_old);
+    }
+}
+
+static void ich9_lpc_reset(DeviceState *qdev)
+{
+    PCIDevice *d = PCI_DEVICE(qdev);
+    ICH9LPCState *lpc = ICH9_LPC_DEVICE(d);
+    uint32_t rbca_old = pci_get_long(d->config + ICH9_LPC_RCBA);
+    int i;
+
+    for (i = 0; i < 4; i++) {
+        pci_set_byte(d->config + ICH9_LPC_PIRQA_ROUT + i,
+                     ICH9_LPC_PIRQ_ROUT_DEFAULT);
+    }
+    for (i = 0; i < 4; i++) {
+        pci_set_byte(d->config + ICH9_LPC_PIRQE_ROUT + i,
+                     ICH9_LPC_PIRQ_ROUT_DEFAULT);
+    }
+    pci_set_byte(d->config + ICH9_LPC_ACPI_CTRL, ICH9_LPC_ACPI_CTRL_DEFAULT);
+
+    pci_set_long(d->config + ICH9_LPC_PMBASE, ICH9_LPC_PMBASE_DEFAULT);
+    pci_set_long(d->config + ICH9_LPC_RCBA, ICH9_LPC_RCBA_DEFAULT);
+
+    ich9_cc_reset(lpc);
+
+    ich9_lpc_pmbase_update(lpc);
+    ich9_lpc_rcba_update(lpc, rbca_old);
+
+    lpc->sci_level = 0;
+}
+
+static const MemoryRegionOps rbca_mmio_ops = {
+    .read = ich9_cc_read,
+    .write = ich9_cc_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static int ich9_lpc_initfn(PCIDevice *d)
+{
+    ICH9LPCState *lpc = ICH9_LPC_DEVICE(d);
+    ISABus *isa_bus;
+
+    isa_bus = isa_bus_new(&d->qdev, get_system_io());
+
+    pci_set_long(d->wmask + ICH9_LPC_PMBASE,
+                 ICH9_LPC_PMBASE_BASE_ADDRESS_MASK);
+
+    memory_region_init_io(&lpc->rbca_mem, &rbca_mmio_ops, lpc,
+                            "lpc-rbca-mmio", ICH9_CC_SIZE);
+
+    lpc->isa_bus = isa_bus;
+
+    ich9_cc_init(lpc);
+    apm_init(&lpc->apm, ich9_apm_ctrl_changed, lpc);
+    return 0;
+}
+
+static const VMStateDescription vmstate_ich9_lpc = {
+    .name = "ICH9LPC",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .post_load = ich9_lpc_post_load,
+    .fields = (VMStateField[]) {
+        VMSTATE_PCI_DEVICE(d, ICH9LPCState),
+        VMSTATE_STRUCT(apm, ICH9LPCState, 0, vmstate_apm, APMState),
+        VMSTATE_STRUCT(pm, ICH9LPCState, 0, vmstate_ich9_pm, ICH9LPCPMRegs),
+        VMSTATE_UINT8_ARRAY(chip_config, ICH9LPCState, ICH9_CC_SIZE),
+        VMSTATE_UINT32(sci_level, ICH9LPCState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void ich9_lpc_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    dc->reset = ich9_lpc_reset;
+    k->init = ich9_lpc_initfn;
+    dc->vmsd = &vmstate_ich9_lpc;
+    dc->no_user = 1;
+    k->config_write = ich9_lpc_config_write;
+    dc->desc = "ICH9 LPC bridge";
+    k->vendor_id = PCI_VENDOR_ID_INTEL;
+    k->device_id = PCI_DEVICE_ID_INTEL_ICH9_8;
+    k->revision = ICH9_A2_LPC_REVISION;
+    k->class_id = PCI_CLASS_BRIDGE_ISA;
+
+}
+
+static const TypeInfo ich9_lpc_info = {
+    .name       = TYPE_ICH9_LPC_DEVICE,
+    .parent     = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(struct ICH9LPCState),
+    .class_init  = ich9_lpc_class_init,
+};
+
+static void ich9_lpc_register(void)
+{
+    type_register_static(&ich9_lpc_info);
+}
+
+type_init(ich9_lpc_register);
diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c
index 7d84ce3d74..c79fca7d68 100644
--- a/hw/mc146818rtc.c
+++ b/hw/mc146818rtc.c
@@ -570,7 +570,11 @@ static void rtc_update_time(RTCState *s)
     guest_nsec = get_guest_rtc_ns(s);
     guest_sec = guest_nsec / NSEC_PER_SEC;
     gmtime_r(&guest_sec, &ret);
-    rtc_set_cmos(s, &ret);
+
+    /* Is SET flag of Register B disabled? */
+    if ((s->cmos_data[RTC_REG_B] & REG_B_SET) == 0) {
+        rtc_set_cmos(s, &ret);
+    }
 }
 
 static int update_in_progress(RTCState *s)
diff --git a/hw/pam.c b/hw/pam.c
new file mode 100644
index 0000000000..a95e2cfb07
--- /dev/null
+++ b/hw/pam.c
@@ -0,0 +1,87 @@
+/*
+ * QEMU i440FX/PIIX3 PCI Bridge Emulation
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ * Copyright (c) 2011 Isaku Yamahata <yamahata at valinux co jp>
+ *                    VA Linux Systems Japan K.K.
+ * Copyright (c) 2012 Jason Baron <jbaron@redhat.com>
+ *
+ * Split out from piix_pci.c
+ *
+ * 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 "sysemu.h"
+#include "pam.h"
+
+void smram_update(MemoryRegion *smram_region, uint8_t smram,
+                  uint8_t smm_enabled)
+{
+    bool smram_enabled;
+
+    smram_enabled = ((smm_enabled && (smram & SMRAM_G_SMRAME)) ||
+                        (smram & SMRAM_D_OPEN));
+    memory_region_set_enabled(smram_region, !smram_enabled);
+}
+
+void smram_set_smm(uint8_t *host_smm_enabled, int smm, uint8_t smram,
+                   MemoryRegion *smram_region)
+{
+    uint8_t smm_enabled = (smm != 0);
+    if (*host_smm_enabled != smm_enabled) {
+        *host_smm_enabled = smm_enabled;
+        smram_update(smram_region, smram, *host_smm_enabled);
+    }
+}
+
+void init_pam(MemoryRegion *ram_memory, MemoryRegion *system_memory,
+              MemoryRegion *pci_address_space, PAMMemoryRegion *mem,
+              uint32_t start, uint32_t size)
+{
+    int i;
+
+    /* RAM */
+    memory_region_init_alias(&mem->alias[3], "pam-ram", ram_memory,
+                             start, size);
+    /* ROM (XXX: not quite correct) */
+    memory_region_init_alias(&mem->alias[1], "pam-rom", ram_memory,
+                             start, size);
+    memory_region_set_readonly(&mem->alias[1], true);
+
+    /* XXX: should distinguish read/write cases */
+    memory_region_init_alias(&mem->alias[0], "pam-pci", pci_address_space,
+                             start, size);
+    memory_region_init_alias(&mem->alias[2], "pam-pci", pci_address_space,
+                             start, size);
+
+    for (i = 0; i < 4; ++i) {
+        memory_region_set_enabled(&mem->alias[i], false);
+        memory_region_add_subregion_overlap(system_memory, start,
+                                            &mem->alias[i], 1);
+    }
+    mem->current = 0;
+}
+
+void pam_update(PAMMemoryRegion *pam, int idx, uint8_t val)
+{
+    assert(0 <= idx && idx <= 12);
+
+    memory_region_set_enabled(&pam->alias[pam->current], false);
+    pam->current = (val >> ((!(idx & 1)) * 4)) & PAM_ATTR_MASK;
+    memory_region_set_enabled(&pam->alias[pam->current], true);
+}
diff --git a/hw/pam.h b/hw/pam.h
new file mode 100644
index 0000000000..2d77ebe0d9
--- /dev/null
+++ b/hw/pam.h
@@ -0,0 +1,97 @@
+#ifndef QEMU_PAM_H
+#define QEMU_PAM_H
+
+/*
+ * Copyright (c) 2006 Fabrice Bellard
+ * Copyright (c) 2011 Isaku Yamahata <yamahata at valinux co jp>
+ *               VA Linux Systems Japan K.K.
+ * Copyright (c) 2012 Jason Baron <jbaron@redhat.com>
+ *
+ * Split out from piix_pci.c
+ *
+ * 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.
+ */
+
+/*
+ * SMRAM memory area and PAM memory area in Legacy address range for PC.
+ * PAM: Programmable Attribute Map registers
+ *
+ * 0xa0000 - 0xbffff compatible SMRAM
+ *
+ * 0xc0000 - 0xc3fff Expansion area memory segments
+ * 0xc4000 - 0xc7fff
+ * 0xc8000 - 0xcbfff
+ * 0xcc000 - 0xcffff
+ * 0xd0000 - 0xd3fff
+ * 0xd4000 - 0xd7fff
+ * 0xd8000 - 0xdbfff
+ * 0xdc000 - 0xdffff
+ * 0xe0000 - 0xe3fff Extended System BIOS Area Memory Segments
+ * 0xe4000 - 0xe7fff
+ * 0xe8000 - 0xebfff
+ * 0xec000 - 0xeffff
+ *
+ * 0xf0000 - 0xfffff System BIOS Area Memory Segments
+ */
+
+#include "qemu-common.h"
+#include "memory.h"
+
+#define SMRAM_C_BASE    0xa0000
+#define SMRAM_C_END     0xc0000
+#define SMRAM_C_SIZE    0x20000
+
+#define PAM_EXPAN_BASE  0xc0000
+#define PAM_EXPAN_SIZE  0x04000
+
+#define PAM_EXBIOS_BASE 0xe0000
+#define PAM_EXBIOS_SIZE 0x04000
+
+#define PAM_BIOS_BASE   0xf0000
+#define PAM_BIOS_END    0xfffff
+/* 64KB: Intel 3 series express chipset family p. 58*/
+#define PAM_BIOS_SIZE   0x10000
+
+/* PAM registers: log nibble and high nibble*/
+#define PAM_ATTR_WE     ((uint8_t)2)
+#define PAM_ATTR_RE     ((uint8_t)1)
+#define PAM_ATTR_MASK   ((uint8_t)3)
+
+/* SMRAM register */
+#define SMRAM_D_OPEN           ((uint8_t)(1 << 6))
+#define SMRAM_D_CLS            ((uint8_t)(1 << 5))
+#define SMRAM_D_LCK            ((uint8_t)(1 << 4))
+#define SMRAM_G_SMRAME         ((uint8_t)(1 << 3))
+#define SMRAM_C_BASE_SEG_MASK  ((uint8_t)0x7)
+#define SMRAM_C_BASE_SEG       ((uint8_t)0x2)  /* hardwired to b010 */
+
+typedef struct PAMMemoryRegion {
+    MemoryRegion alias[4];  /* index = PAM value */
+    unsigned current;
+} PAMMemoryRegion;
+
+void smram_update(MemoryRegion *smram_region, uint8_t smram,
+                  uint8_t smm_enabled);
+void smram_set_smm(uint8_t *host_smm_enabled, int smm, uint8_t smram,
+                   MemoryRegion *smram_region);
+void init_pam(MemoryRegion *ram, MemoryRegion *system, MemoryRegion *pci,
+              PAMMemoryRegion *mem, uint32_t start, uint32_t size);
+void pam_update(PAMMemoryRegion *mem, int idx, uint8_t val);
+
+#endif /* QEMU_PAM_H */
diff --git a/hw/pc.c b/hw/pc.c
index 4aca4986dd..2b5bbbfb30 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -1058,6 +1058,21 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
     *floppy = fdctrl_init_isa(isa_bus, fd);
 }
 
+void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus)
+{
+    int i;
+
+    for (i = 0; i < nb_nics; i++) {
+        NICInfo *nd = &nd_table[i];
+
+        if (!pci_bus || (nd->model && strcmp(nd->model, "ne2k_isa") == 0)) {
+            pc_init_ne2k_isa(isa_bus, nd);
+        } else {
+            pci_nic_init_nofail(nd, "e1000", NULL);
+        }
+    }
+}
+
 void pc_pci_device_init(PCIBus *pci_bus)
 {
     int max_bus;
@@ -1068,3 +1083,27 @@ void pc_pci_device_init(PCIBus *pci_bus)
         pci_create_simple(pci_bus, -1, "lsi53c895a");
     }
 }
+
+void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name)
+{
+    DeviceState *dev;
+    SysBusDevice *d;
+    unsigned int i;
+
+    if (kvm_irqchip_in_kernel()) {
+        dev = qdev_create(NULL, "kvm-ioapic");
+    } else {
+        dev = qdev_create(NULL, "ioapic");
+    }
+    if (parent_name) {
+        object_property_add_child(object_resolve_path(parent_name, NULL),
+                                  "ioapic", OBJECT(dev), NULL);
+    }
+    qdev_init_nofail(dev);
+    d = sysbus_from_qdev(dev);
+    sysbus_mmio_map(d, 0, 0xfec00000);
+
+    for (i = 0; i < IOAPIC_NUM_PINS; i++) {
+        gsi_state->ioapic_irq[i] = qdev_get_gpio_in(dev, i);
+    }
+}
diff --git a/hw/pc.h b/hw/pc.h
index e7993ca5d9..2237e86446 100644
--- a/hw/pc.h
+++ b/hw/pc.h
@@ -98,11 +98,14 @@ void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
                   const char *boot_device,
                   ISADevice *floppy, BusState *ide0, BusState *ide1,
                   ISADevice *s);
+void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus);
 void pc_pci_device_init(PCIBus *pci_bus);
 
 typedef void (*cpu_set_smm_t)(int smm, void *arg);
 void cpu_smm_register(cpu_set_smm_t callback, void *arg);
 
+void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name);
+
 /* acpi.c */
 extern int acpi_enabled;
 extern char *acpi_tables;
diff --git a/hw/pc_piix.c b/hw/pc_piix.c
index cfa839c8b4..aa3e7f40dc 100644
--- a/hw/pc_piix.c
+++ b/hw/pc_piix.c
@@ -54,70 +54,6 @@ static const int ide_iobase[MAX_IDE_BUS] = { 0x1f0, 0x170 };
 static const int ide_iobase2[MAX_IDE_BUS] = { 0x3f6, 0x376 };
 static const int ide_irq[MAX_IDE_BUS] = { 14, 15 };
 
-static void kvm_piix3_setup_irq_routing(bool pci_enabled)
-{
-#ifdef CONFIG_KVM
-    KVMState *s = kvm_state;
-    int i;
-
-    if (kvm_check_extension(s, KVM_CAP_IRQ_ROUTING)) {
-        for (i = 0; i < 8; ++i) {
-            if (i == 2) {
-                continue;
-            }
-            kvm_irqchip_add_irq_route(s, i, KVM_IRQCHIP_PIC_MASTER, i);
-        }
-        for (i = 8; i < 16; ++i) {
-            kvm_irqchip_add_irq_route(s, i, KVM_IRQCHIP_PIC_SLAVE, i - 8);
-        }
-        if (pci_enabled) {
-            for (i = 0; i < 24; ++i) {
-                if (i == 0) {
-                    kvm_irqchip_add_irq_route(s, i, KVM_IRQCHIP_IOAPIC, 2);
-                } else if (i != 2) {
-                    kvm_irqchip_add_irq_route(s, i, KVM_IRQCHIP_IOAPIC, i);
-                }
-            }
-        }
-    }
-#endif /* CONFIG_KVM */
-}
-
-static void kvm_piix3_gsi_handler(void *opaque, int n, int level)
-{
-    GSIState *s = opaque;
-
-    if (n < ISA_NUM_IRQS) {
-        /* Kernel will forward to both PIC and IOAPIC */
-        qemu_set_irq(s->i8259_irq[n], level);
-    } else {
-        qemu_set_irq(s->ioapic_irq[n], level);
-    }
-}
-
-static void ioapic_init(GSIState *gsi_state)
-{
-    DeviceState *dev;
-    SysBusDevice *d;
-    unsigned int i;
-
-    if (kvm_irqchip_in_kernel()) {
-        dev = qdev_create(NULL, "kvm-ioapic");
-    } else {
-        dev = qdev_create(NULL, "ioapic");
-    }
-    /* FIXME: this should be under the piix3.  */
-    object_property_add_child(object_resolve_path("i440fx", NULL),
-                              "ioapic", OBJECT(dev), NULL);
-    qdev_init_nofail(dev);
-    d = sysbus_from_qdev(dev);
-    sysbus_mmio_map(d, 0, 0xfec00000);
-
-    for (i = 0; i < IOAPIC_NUM_PINS; i++) {
-        gsi_state->ioapic_irq[i] = qdev_get_gpio_in(dev, i);
-    }
-}
-
 /* PC hardware initialisation */
 static void pc_init1(MemoryRegion *system_memory,
                      MemoryRegion *system_io,
@@ -183,8 +119,8 @@ static void pc_init1(MemoryRegion *system_memory,
 
     gsi_state = g_malloc0(sizeof(*gsi_state));
     if (kvm_irqchip_in_kernel()) {
-        kvm_piix3_setup_irq_routing(pci_enabled);
-        gsi = qemu_allocate_irqs(kvm_piix3_gsi_handler, gsi_state,
+        kvm_pc_setup_irq_routing(pci_enabled);
+        gsi = qemu_allocate_irqs(kvm_pc_gsi_handler, gsi_state,
                                  GSI_NUM_PINS);
     } else {
         gsi = qemu_allocate_irqs(gsi_handler, gsi_state, GSI_NUM_PINS);
@@ -221,7 +157,7 @@ static void pc_init1(MemoryRegion *system_memory,
         gsi_state->i8259_irq[i] = i8259[i];
     }
     if (pci_enabled) {
-        ioapic_init(gsi_state);
+        ioapic_init_gsi(gsi_state, "i440fx");
     }
 
     pc_register_ferr_irq(gsi[13]);
@@ -234,14 +170,7 @@ static void pc_init1(MemoryRegion *system_memory,
     /* init basic PC hardware */
     pc_basic_device_init(isa_bus, gsi, &rtc_state, &floppy, xen_enabled());
 
-    for(i = 0; i < nb_nics; i++) {
-        NICInfo *nd = &nd_table[i];
-
-        if (!pci_enabled || (nd->model && strcmp(nd->model, "ne2k_isa") == 0))
-            pc_init_ne2k_isa(isa_bus, nd);
-        else
-            pci_nic_init_nofail(nd, "e1000", NULL);
-    }
+    pc_nic_init(isa_bus, pci_bus);
 
     ide_drive_get(hd, MAX_IDE_BUS);
     if (pci_enabled) {
diff --git a/hw/pc_q35.c b/hw/pc_q35.c
new file mode 100644
index 0000000000..3429a9ae8f
--- /dev/null
+++ b/hw/pc_q35.c
@@ -0,0 +1,223 @@
+/*
+ * Q35 chipset based pc system emulator
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ * Copyright (c) 2009, 2010
+ *               Isaku Yamahata <yamahata at valinux co jp>
+ *               VA Linux Systems Japan K.K.
+ * Copyright (C) 2012 Jason Baron <jbaron@redhat.com>
+ *
+ * This is based on pc.c, but heavily modified.
+ *
+ * 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 "hw.h"
+#include "arch_init.h"
+#include "smbus.h"
+#include "boards.h"
+#include "mc146818rtc.h"
+#include "xen.h"
+#include "kvm.h"
+#include "kvm/clock.h"
+#include "q35.h"
+#include "exec-memory.h"
+#include "ich9.h"
+#include "hw/ide/pci.h"
+#include "hw/ide/ahci.h"
+#include "hw/usb.h"
+
+/* ICH9 AHCI has 6 ports */
+#define MAX_SATA_PORTS     6
+
+/* set CMOS shutdown status register (index 0xF) as S3_resume(0xFE)
+ *    BIOS will read it and start S3 resume at POST Entry */
+static void pc_cmos_set_s3_resume(void *opaque, int irq, int level)
+{
+    ISADevice *s = opaque;
+
+    if (level) {
+        rtc_set_memory(s, 0xF, 0xFE);
+    }
+}
+
+/* PC hardware initialisation */
+static void pc_q35_init(QEMUMachineInitArgs *args)
+{
+    ram_addr_t ram_size = args->ram_size;
+    const char *cpu_model = args->cpu_model;
+    const char *kernel_filename = args->kernel_filename;
+    const char *kernel_cmdline = args->kernel_cmdline;
+    const char *initrd_filename = args->initrd_filename;
+    const char *boot_device = args->boot_device;
+    ram_addr_t below_4g_mem_size, above_4g_mem_size;
+    Q35PCIHost *q35_host;
+    PCIBus *host_bus;
+    PCIDevice *lpc;
+    BusState *idebus[MAX_SATA_PORTS];
+    ISADevice *rtc_state;
+    ISADevice *floppy;
+    MemoryRegion *pci_memory;
+    MemoryRegion *rom_memory;
+    MemoryRegion *ram_memory;
+    GSIState *gsi_state;
+    ISABus *isa_bus;
+    int pci_enabled = 1;
+    qemu_irq *cpu_irq;
+    qemu_irq *gsi;
+    qemu_irq *i8259;
+    int i;
+    ICH9LPCState *ich9_lpc;
+    PCIDevice *ahci;
+    qemu_irq *cmos_s3;
+
+    pc_cpus_init(cpu_model);
+
+    kvmclock_create();
+
+    if (ram_size >= 0xb0000000) {
+        above_4g_mem_size = ram_size - 0xb0000000;
+        below_4g_mem_size = 0xb0000000;
+    } else {
+        above_4g_mem_size = 0;
+        below_4g_mem_size = ram_size;
+    }
+
+    /* pci enabled */
+    if (pci_enabled) {
+        pci_memory = g_new(MemoryRegion, 1);
+        memory_region_init(pci_memory, "pci", INT64_MAX);
+        rom_memory = pci_memory;
+    } else {
+        pci_memory = NULL;
+        rom_memory = get_system_memory();
+    }
+
+    /* allocate ram and load rom/bios */
+    if (!xen_enabled()) {
+        pc_memory_init(get_system_memory(), kernel_filename, kernel_cmdline,
+                       initrd_filename, below_4g_mem_size, above_4g_mem_size,
+                       rom_memory, &ram_memory);
+    }
+
+    /* irq lines */
+    gsi_state = g_malloc0(sizeof(*gsi_state));
+    if (kvm_irqchip_in_kernel()) {
+        kvm_pc_setup_irq_routing(pci_enabled);
+        gsi = qemu_allocate_irqs(kvm_pc_gsi_handler, gsi_state,
+                                 GSI_NUM_PINS);
+    } else {
+        gsi = qemu_allocate_irqs(gsi_handler, gsi_state, GSI_NUM_PINS);
+    }
+
+    /* create pci host bus */
+    q35_host = Q35_HOST_DEVICE(qdev_create(NULL, TYPE_Q35_HOST_DEVICE));
+
+    q35_host->mch.ram_memory = ram_memory;
+    q35_host->mch.pci_address_space = pci_memory;
+    q35_host->mch.system_memory = get_system_memory();
+    q35_host->mch.address_space_io = get_system_io();;
+    q35_host->mch.below_4g_mem_size = below_4g_mem_size;
+    q35_host->mch.above_4g_mem_size = above_4g_mem_size;
+    /* pci */
+    qdev_init_nofail(DEVICE(q35_host));
+    host_bus = q35_host->host.pci.bus;
+    /* create ISA bus */
+    lpc = pci_create_simple_multifunction(host_bus, PCI_DEVFN(ICH9_LPC_DEV,
+                                          ICH9_LPC_FUNC), true,
+                                          TYPE_ICH9_LPC_DEVICE);
+    ich9_lpc = ICH9_LPC_DEVICE(lpc);
+    ich9_lpc->pic = gsi;
+    ich9_lpc->ioapic = gsi_state->ioapic_irq;
+    pci_bus_irqs(host_bus, ich9_lpc_set_irq, ich9_lpc_map_irq, ich9_lpc,
+                 ICH9_LPC_NB_PIRQS);
+    isa_bus = ich9_lpc->isa_bus;
+
+    /*end early*/
+    isa_bus_irqs(isa_bus, gsi);
+
+    if (kvm_irqchip_in_kernel()) {
+        i8259 = kvm_i8259_init(isa_bus);
+    } else if (xen_enabled()) {
+        i8259 = xen_interrupt_controller_init();
+    } else {
+        cpu_irq = pc_allocate_cpu_irq();
+        i8259 = i8259_init(isa_bus, cpu_irq[0]);
+    }
+
+    for (i = 0; i < ISA_NUM_IRQS; i++) {
+        gsi_state->i8259_irq[i] = i8259[i];
+    }
+    if (pci_enabled) {
+        ioapic_init_gsi(gsi_state, NULL);
+    }
+
+    pc_register_ferr_irq(gsi[13]);
+
+    /* init basic PC hardware */
+    pc_basic_device_init(isa_bus, gsi, &rtc_state, &floppy, false);
+
+    /* connect pm stuff to lpc */
+    cmos_s3 = qemu_allocate_irqs(pc_cmos_set_s3_resume, rtc_state, 1);
+    ich9_lpc_pm_init(lpc, *cmos_s3);
+
+    /* ahci and SATA device, for q35 1 ahci controller is built-in */
+    ahci = pci_create_simple_multifunction(host_bus,
+                                           PCI_DEVFN(ICH9_SATA1_DEV,
+                                                     ICH9_SATA1_FUNC),
+                                           true, "ich9-ahci");
+    idebus[0] = qdev_get_child_bus(&ahci->qdev, "ide.0");
+    idebus[1] = qdev_get_child_bus(&ahci->qdev, "ide.1");
+
+    if (usb_enabled(false)) {
+        /* Should we create 6 UHCI according to ich9 spec? */
+        ehci_create_ich9_with_companions(host_bus, 0x1d);
+    }
+
+    /* TODO: Populate SPD eeprom data.  */
+    smbus_eeprom_init(ich9_smb_init(host_bus,
+                                    PCI_DEVFN(ICH9_SMB_DEV, ICH9_SMB_FUNC),
+                                    0xb100),
+                      8, NULL, 0);
+
+    pc_cmos_init(below_4g_mem_size, above_4g_mem_size, boot_device,
+                 floppy, idebus[0], idebus[1], rtc_state);
+
+    /* the rest devices to which pci devfn is automatically assigned */
+    pc_vga_init(isa_bus, host_bus);
+    audio_init(isa_bus, host_bus);
+    pc_nic_init(isa_bus, host_bus);
+    if (pci_enabled) {
+        pc_pci_device_init(host_bus);
+    }
+}
+
+static QEMUMachine pc_q35_machine = {
+    .name = "q35-next",
+    .alias = "q35",
+    .desc = "Q35 chipset PC",
+    .init = pc_q35_init,
+    .max_cpus = 255,
+};
+
+static void pc_q35_machine_init(void)
+{
+    qemu_register_machine(&pc_q35_machine);
+}
+
+machine_init(pc_q35_machine_init);
diff --git a/hw/pci-hotplug.c b/hw/pci-hotplug.c
index e7fb780a08..0ca5546fc6 100644
--- a/hw/pci-hotplug.c
+++ b/hw/pci-hotplug.c
@@ -80,7 +80,13 @@ static int scsi_hot_add(Monitor *mon, DeviceState *adapter,
     SCSIBus *scsibus;
     SCSIDevice *scsidev;
 
-    scsibus = SCSI_BUS(QLIST_FIRST(&adapter->child_bus));
+    scsibus = (SCSIBus *)
+        object_dynamic_cast(OBJECT(QLIST_FIRST(&adapter->child_bus)),
+                            TYPE_SCSI_BUS);
+    if (!scsibus) {
+	error_report("Device is not a SCSI adapter");
+	return -1;
+    }
 
     /*
      * drive_init() tries to find a default for dinfo->unit.  Doesn't
diff --git a/hw/pci.c b/hw/pci.c
index 9841e398a6..97a0cd77c1 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -301,9 +301,9 @@ PCIBus *pci_bus_new(DeviceState *parent, const char *name,
     PCIBus *bus;
 
     bus = g_malloc0(sizeof(*bus));
-    bus->qbus.glib_allocated = true;
     pci_bus_new_inplace(bus, parent, name, address_space_mem,
                         address_space_io, devfn_min);
+    OBJECT(bus)->free = g_free;
     return bus;
 }
 
diff --git a/hw/pci_ids.h b/hw/pci_ids.h
index 41f3570fb9..5df7245349 100644
--- a/hw/pci_ids.h
+++ b/hw/pci_ids.h
@@ -36,6 +36,7 @@
 #define PCI_CLASS_BRIDGE_HOST            0x0600
 #define PCI_CLASS_BRIDGE_ISA             0x0601
 #define PCI_CLASS_BRIDGE_PCI             0x0604
+#define PCI_CLASS_BRDIGE_PCI_INF_SUB     0x01
 #define PCI_CLASS_BRIDGE_OTHER           0x0680
 
 #define PCI_CLASS_COMMUNICATION_SERIAL   0x0700
@@ -116,6 +117,17 @@
 #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_DEVICE_ID_INTEL_ICH9_0       0x2910
+#define PCI_DEVICE_ID_INTEL_ICH9_1       0x2917
+#define PCI_DEVICE_ID_INTEL_ICH9_2       0x2912
+#define PCI_DEVICE_ID_INTEL_ICH9_3       0x2913
+#define PCI_DEVICE_ID_INTEL_ICH9_4       0x2914
+#define PCI_DEVICE_ID_INTEL_ICH9_5       0x2919
+#define PCI_DEVICE_ID_INTEL_ICH9_6       0x2930
+#define PCI_DEVICE_ID_INTEL_ICH9_7       0x2916
+#define PCI_DEVICE_ID_INTEL_ICH9_8       0x2918
+
 #define PCI_DEVICE_ID_INTEL_82801I_UHCI1 0x2934
 #define PCI_DEVICE_ID_INTEL_82801I_UHCI2 0x2935
 #define PCI_DEVICE_ID_INTEL_82801I_UHCI3 0x2936
@@ -126,6 +138,8 @@
 #define PCI_DEVICE_ID_INTEL_82801I_EHCI2 0x293c
 #define PCI_DEVICE_ID_INTEL_82599_SFP_VF 0x10ed
 
+#define PCI_DEVICE_ID_INTEL_Q35_MCH      0x29c0
+
 #define PCI_VENDOR_ID_XEN               0x5853
 #define PCI_DEVICE_ID_XEN_PLATFORM      0x0001
 
diff --git a/hw/piix_pci.c b/hw/piix_pci.c
index 9af5847690..ba1b3de749 100644
--- a/hw/piix_pci.c
+++ b/hw/piix_pci.c
@@ -30,6 +30,7 @@
 #include "sysbus.h"
 #include "range.h"
 #include "xen.h"
+#include "pam.h"
 
 /*
  * I440FX chipset data sheet.
@@ -68,11 +69,6 @@ typedef struct PIIX3State {
     int32_t pci_irq_levels_vmstate[PIIX_NUM_PIRQS];
 } PIIX3State;
 
-typedef struct PAMMemoryRegion {
-    MemoryRegion alias[4];  /* index = PAM value */
-    unsigned current;
-} PAMMemoryRegion;
-
 struct PCII440FXState {
     PCIDevice dev;
     MemoryRegion *system_memory;
@@ -105,52 +101,16 @@ static int pci_slot_get_pirq(PCIDevice *pci_dev, int pci_intx)
     return (pci_intx + slot_addend) & 3;
 }
 
-static void init_pam(PCII440FXState *d, PAMMemoryRegion *mem,
-                     uint32_t start, uint32_t size)
-{
-    int i;
-
-    /* RAM */
-    memory_region_init_alias(&mem->alias[3], "pam-ram", d->ram_memory, start, size);
-    /* ROM (XXX: not quite correct) */
-    memory_region_init_alias(&mem->alias[1], "pam-rom", d->ram_memory, start, size);
-    memory_region_set_readonly(&mem->alias[1], true);
-
-    /* XXX: should distinguish read/write cases */
-    memory_region_init_alias(&mem->alias[0], "pam-pci", d->pci_address_space,
-                             start, size);
-    memory_region_init_alias(&mem->alias[2], "pam-pci", d->pci_address_space,
-                             start, size);
-
-    for (i = 0; i < 4; ++i) {
-        memory_region_set_enabled(&mem->alias[i], false);
-        memory_region_add_subregion_overlap(d->system_memory, start, &mem->alias[i], 1);
-    }
-    mem->current = 0;
-}
-
-static void update_pam(PAMMemoryRegion *pam, unsigned r)
-{
-    memory_region_set_enabled(&pam->alias[pam->current], false);
-    pam->current = r;
-    memory_region_set_enabled(&pam->alias[pam->current], true);
-}
-
 static void i440fx_update_memory_mappings(PCII440FXState *d)
 {
-    int i, r;
-    uint32_t smram;
-    bool smram_enabled;
+    int i;
 
     memory_region_transaction_begin();
-    update_pam(&d->pam_regions[0], (d->dev.config[I440FX_PAM] >> 4) & 3);
-    for(i = 0; i < 12; i++) {
-        r = (d->dev.config[(i >> 1) + (I440FX_PAM + 1)] >> ((i & 1) * 4)) & 3;
-        update_pam(&d->pam_regions[i+1], r);
+    for (i = 0; i < 13; i++) {
+        pam_update(&d->pam_regions[i], i,
+                   d->dev.config[I440FX_PAM + ((i + 1) / 2)]);
     }
-    smram = d->dev.config[I440FX_SMRAM];
-    smram_enabled = (d->smm_enabled && (smram & 0x08)) || (smram & 0x40);
-    memory_region_set_enabled(&d->smram_region, !smram_enabled);
+    smram_update(&d->smram_region, d->dev.config[I440FX_SMRAM], d->smm_enabled);
     memory_region_transaction_commit();
 }
 
@@ -158,11 +118,10 @@ static void i440fx_set_smm(int val, void *arg)
 {
     PCII440FXState *d = arg;
 
-    val = (val != 0);
-    if (d->smm_enabled != val) {
-        d->smm_enabled = val;
-        i440fx_update_memory_mappings(d);
-    }
+    memory_region_transaction_begin();
+    smram_set_smm(&d->smm_enabled, val, d->dev.config[I440FX_SMRAM],
+                  &d->smram_region);
+    memory_region_transaction_commit();
 }
 
 
@@ -300,9 +259,12 @@ static PCIBus *i440fx_common_init(const char *device_name,
     memory_region_add_subregion_overlap(f->system_memory, 0xa0000,
                                         &f->smram_region, 1);
     memory_region_set_enabled(&f->smram_region, false);
-    init_pam(f, &f->pam_regions[0], 0xf0000, 0x10000);
+    init_pam(f->ram_memory, f->system_memory, f->pci_address_space,
+             &f->pam_regions[0], PAM_BIOS_BASE, PAM_BIOS_SIZE);
     for (i = 0; i < 12; ++i) {
-        init_pam(f, &f->pam_regions[i+1], 0xc0000 + i * 0x4000, 0x4000);
+        init_pam(f->ram_memory, f->system_memory, f->pci_address_space,
+                 &f->pam_regions[i+1], PAM_EXPAN_BASE + i * PAM_EXPAN_SIZE,
+                 PAM_EXPAN_SIZE);
     }
 
     /* Xen supports additional interrupt routes from the PCI devices to
diff --git a/hw/q35.c b/hw/q35.c
new file mode 100644
index 0000000000..efebc2786a
--- /dev/null
+++ b/hw/q35.c
@@ -0,0 +1,309 @@
+/*
+ * QEMU MCH/ICH9 PCI Bridge Emulation
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ * Copyright (c) 2009, 2010, 2011
+ *               Isaku Yamahata <yamahata at valinux co jp>
+ *               VA Linux Systems Japan K.K.
+ * Copyright (C) 2012 Jason Baron <jbaron@redhat.com>
+ *
+ * This is based on piix_pci.c, but heavily modified.
+ *
+ * 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 "hw.h"
+#include "q35.h"
+
+/****************************************************************************
+ * Q35 host
+ */
+
+static int q35_host_init(SysBusDevice *dev)
+{
+    PCIBus *b;
+    PCIHostState *pci = FROM_SYSBUS(PCIHostState, dev);
+    Q35PCIHost *s = Q35_HOST_DEVICE(&dev->qdev);
+
+    memory_region_init_io(&pci->conf_mem, &pci_host_conf_le_ops, pci,
+                          "pci-conf-idx", 4);
+    sysbus_add_io(dev, MCH_HOST_BRIDGE_CONFIG_ADDR, &pci->conf_mem);
+    sysbus_init_ioports(&pci->busdev, MCH_HOST_BRIDGE_CONFIG_ADDR, 4);
+
+    memory_region_init_io(&pci->data_mem, &pci_host_data_le_ops, pci,
+                          "pci-conf-data", 4);
+    sysbus_add_io(dev, MCH_HOST_BRIDGE_CONFIG_DATA, &pci->data_mem);
+    sysbus_init_ioports(&pci->busdev, MCH_HOST_BRIDGE_CONFIG_DATA, 4);
+
+    if (pcie_host_init(&s->host) < 0) {
+        return -1;
+    }
+    b = pci_bus_new(&s->host.pci.busdev.qdev, "pcie.0",
+                    s->mch.pci_address_space, s->mch.address_space_io, 0);
+    s->host.pci.bus = b;
+    qdev_set_parent_bus(DEVICE(&s->mch), BUS(b));
+    qdev_init_nofail(DEVICE(&s->mch));
+
+    return 0;
+}
+
+static Property mch_props[] = {
+    DEFINE_PROP_UINT64("MCFG", Q35PCIHost, host.base_addr,
+                        MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void q35_host_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = q35_host_init;
+    dc->props = mch_props;
+}
+
+static void q35_host_initfn(Object *obj)
+{
+    Q35PCIHost *s = Q35_HOST_DEVICE(obj);
+
+    object_initialize(&s->mch, TYPE_MCH_PCI_DEVICE);
+    object_property_add_child(OBJECT(s), "mch", OBJECT(&s->mch), NULL);
+    qdev_prop_set_uint32(DEVICE(&s->mch), "addr", PCI_DEVFN(0, 0));
+    qdev_prop_set_bit(DEVICE(&s->mch), "multifunction", false);
+}
+
+static const TypeInfo q35_host_info = {
+    .name       = TYPE_Q35_HOST_DEVICE,
+    .parent     = TYPE_PCIE_HOST_BRIDGE,
+    .instance_size = sizeof(Q35PCIHost),
+    .instance_init = q35_host_initfn,
+    .class_init = q35_host_class_init,
+};
+
+/****************************************************************************
+ * MCH D0:F0
+ */
+
+/* PCIe MMCFG */
+static void mch_update_pciexbar(MCHPCIState *mch)
+{
+    PCIDevice *pci_dev = &mch->d;
+    BusState *bus = qdev_get_parent_bus(&pci_dev->qdev);
+    DeviceState *qdev = bus->parent;
+    Q35PCIHost *s = Q35_HOST_DEVICE(qdev);
+
+    uint64_t pciexbar;
+    int enable;
+    uint64_t addr;
+    uint64_t addr_mask;
+    uint32_t length;
+
+    pciexbar = pci_get_quad(pci_dev->config + MCH_HOST_BRIDGE_PCIEXBAR);
+    enable = pciexbar & MCH_HOST_BRIDGE_PCIEXBAREN;
+    addr_mask = MCH_HOST_BRIDGE_PCIEXBAR_ADMSK;
+    switch (pciexbar & MCH_HOST_BRIDGE_PCIEXBAR_LENGTH_MASK) {
+    case MCH_HOST_BRIDGE_PCIEXBAR_LENGTH_256M:
+        length = 256 * 1024 * 1024;
+        break;
+    case MCH_HOST_BRIDGE_PCIEXBAR_LENGTH_128M:
+        length = 128 * 1024 * 1024;
+        addr_mask |= MCH_HOST_BRIDGE_PCIEXBAR_128ADMSK |
+            MCH_HOST_BRIDGE_PCIEXBAR_64ADMSK;
+        break;
+    case MCH_HOST_BRIDGE_PCIEXBAR_LENGTH_64M:
+        length = 64 * 1024 * 1024;
+        addr_mask |= MCH_HOST_BRIDGE_PCIEXBAR_64ADMSK;
+        break;
+    case MCH_HOST_BRIDGE_PCIEXBAR_LENGTH_RVD:
+    default:
+        enable = 0;
+        length = 0;
+        abort();
+        break;
+    }
+    addr = pciexbar & addr_mask;
+    pcie_host_mmcfg_update(&s->host, enable, addr, length);
+}
+
+/* PAM */
+static void mch_update_pam(MCHPCIState *mch)
+{
+    int i;
+
+    memory_region_transaction_begin();
+    for (i = 0; i < 13; i++) {
+        pam_update(&mch->pam_regions[i], i,
+                   mch->d.config[MCH_HOST_BRIDGE_PAM0 + ((i + 1) / 2)]);
+    }
+    memory_region_transaction_commit();
+}
+
+/* SMRAM */
+static void mch_update_smram(MCHPCIState *mch)
+{
+    memory_region_transaction_begin();
+    smram_update(&mch->smram_region, mch->d.config[MCH_HOST_BRDIGE_SMRAM],
+                    mch->smm_enabled);
+    memory_region_transaction_commit();
+}
+
+static void mch_set_smm(int smm, void *arg)
+{
+    MCHPCIState *mch = arg;
+
+    memory_region_transaction_begin();
+    smram_set_smm(&mch->smm_enabled, smm, mch->d.config[MCH_HOST_BRDIGE_SMRAM],
+                    &mch->smram_region);
+    memory_region_transaction_commit();
+}
+
+static void mch_write_config(PCIDevice *d,
+                              uint32_t address, uint32_t val, int len)
+{
+    MCHPCIState *mch = MCH_PCI_DEVICE(d);
+
+    /* XXX: implement SMRAM.D_LOCK */
+    pci_default_write_config(d, address, val, len);
+
+    if (ranges_overlap(address, len, MCH_HOST_BRIDGE_PAM0,
+                       MCH_HOST_BRIDGE_PAM_SIZE)) {
+        mch_update_pam(mch);
+    }
+
+    if (ranges_overlap(address, len, MCH_HOST_BRIDGE_PCIEXBAR,
+                       MCH_HOST_BRIDGE_PCIEXBAR_SIZE)) {
+        mch_update_pciexbar(mch);
+    }
+
+    if (ranges_overlap(address, len, MCH_HOST_BRDIGE_SMRAM,
+                       MCH_HOST_BRDIGE_SMRAM_SIZE)) {
+        mch_update_smram(mch);
+    }
+}
+
+static void mch_update(MCHPCIState *mch)
+{
+    mch_update_pciexbar(mch);
+    mch_update_pam(mch);
+    mch_update_smram(mch);
+}
+
+static int mch_post_load(void *opaque, int version_id)
+{
+    MCHPCIState *mch = opaque;
+    mch_update(mch);
+    return 0;
+}
+
+static const VMStateDescription vmstate_mch = {
+    .name = "mch",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .post_load = mch_post_load,
+    .fields = (VMStateField []) {
+        VMSTATE_PCI_DEVICE(d, MCHPCIState),
+        VMSTATE_UINT8(smm_enabled, MCHPCIState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void mch_reset(DeviceState *qdev)
+{
+    PCIDevice *d = PCI_DEVICE(qdev);
+    MCHPCIState *mch = MCH_PCI_DEVICE(d);
+
+    pci_set_quad(d->config + MCH_HOST_BRIDGE_PCIEXBAR,
+                 MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT);
+
+    d->config[MCH_HOST_BRDIGE_SMRAM] = MCH_HOST_BRIDGE_SMRAM_DEFAULT;
+
+    mch_update(mch);
+}
+
+static int mch_init(PCIDevice *d)
+{
+    int i;
+    hwaddr pci_hole64_size;
+    MCHPCIState *mch = MCH_PCI_DEVICE(d);
+
+    /* setup pci memory regions */
+    memory_region_init_alias(&mch->pci_hole, "pci-hole",
+                             mch->pci_address_space,
+                             mch->below_4g_mem_size,
+                             0x100000000ULL - mch->below_4g_mem_size);
+    memory_region_add_subregion(mch->system_memory, mch->below_4g_mem_size,
+                                &mch->pci_hole);
+    pci_hole64_size = (sizeof(hwaddr) == 4 ? 0 :
+                       ((uint64_t)1 << 62));
+    memory_region_init_alias(&mch->pci_hole_64bit, "pci-hole64",
+                             mch->pci_address_space,
+                             0x100000000ULL + mch->above_4g_mem_size,
+                             pci_hole64_size);
+    if (pci_hole64_size) {
+        memory_region_add_subregion(mch->system_memory,
+                                    0x100000000ULL + mch->above_4g_mem_size,
+                                    &mch->pci_hole_64bit);
+    }
+    /* smram */
+    cpu_smm_register(&mch_set_smm, mch);
+    memory_region_init_alias(&mch->smram_region, "smram-region",
+                             mch->pci_address_space, 0xa0000, 0x20000);
+    memory_region_add_subregion_overlap(mch->system_memory, 0xa0000,
+                                        &mch->smram_region, 1);
+    memory_region_set_enabled(&mch->smram_region, false);
+    init_pam(mch->ram_memory, mch->system_memory, mch->pci_address_space,
+             &mch->pam_regions[0], PAM_BIOS_BASE, PAM_BIOS_SIZE);
+    for (i = 0; i < 12; ++i) {
+        init_pam(mch->ram_memory, mch->system_memory, mch->pci_address_space,
+                 &mch->pam_regions[i+1], PAM_EXPAN_BASE + i * PAM_EXPAN_SIZE,
+                 PAM_EXPAN_SIZE);
+    }
+    return 0;
+}
+
+static void mch_class_init(ObjectClass *klass, void *data)
+{
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    k->init = mch_init;
+    k->config_write = mch_write_config;
+    dc->reset = mch_reset;
+    dc->desc = "Host bridge";
+    dc->vmsd = &vmstate_mch;
+    k->vendor_id = PCI_VENDOR_ID_INTEL;
+    k->device_id = PCI_DEVICE_ID_INTEL_Q35_MCH;
+    k->revision = MCH_HOST_BRIDGE_REVISION_DEFUALT;
+    k->class_id = PCI_CLASS_BRIDGE_HOST;
+}
+
+static const TypeInfo mch_info = {
+    .name = TYPE_MCH_PCI_DEVICE,
+    .parent = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(MCHPCIState),
+    .class_init = mch_class_init,
+};
+
+static void q35_register(void)
+{
+    type_register_static(&mch_info);
+    type_register_static(&q35_host_info);
+}
+
+type_init(q35_register);
diff --git a/hw/q35.h b/hw/q35.h
new file mode 100644
index 0000000000..e34f7c165f
--- /dev/null
+++ b/hw/q35.h
@@ -0,0 +1,150 @@
+/*
+ * q35.h
+ *
+ * Copyright (c) 2009 Isaku Yamahata <yamahata at valinux co jp>
+ *                    VA Linux Systems Japan K.K.
+ * Copyright (C) 2012 Jason Baron <jbaron@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#ifndef HW_Q35_H
+#define HW_Q35_H
+
+#include "hw.h"
+#include "range.h"
+#include "isa.h"
+#include "sysbus.h"
+#include "pc.h"
+#include "apm.h"
+#include "apic.h"
+#include "pci.h"
+#include "pcie_host.h"
+#include "acpi.h"
+#include "acpi_ich9.h"
+#include "pam.h"
+
+#define TYPE_Q35_HOST_DEVICE "q35-pcihost"
+#define Q35_HOST_DEVICE(obj) \
+     OBJECT_CHECK(Q35PCIHost, (obj), TYPE_Q35_HOST_DEVICE)
+
+#define TYPE_MCH_PCI_DEVICE "mch"
+#define MCH_PCI_DEVICE(obj) \
+     OBJECT_CHECK(MCHPCIState, (obj), TYPE_MCH_PCI_DEVICE)
+
+typedef struct MCHPCIState {
+    PCIDevice d;
+    MemoryRegion *ram_memory;
+    MemoryRegion *pci_address_space;
+    MemoryRegion *system_memory;
+    MemoryRegion *address_space_io;
+    PAMMemoryRegion pam_regions[13];
+    MemoryRegion smram_region;
+    MemoryRegion pci_hole;
+    MemoryRegion pci_hole_64bit;
+    uint8_t smm_enabled;
+    ram_addr_t below_4g_mem_size;
+    ram_addr_t above_4g_mem_size;
+} MCHPCIState;
+
+typedef struct Q35PCIHost {
+    PCIExpressHost host;
+    MCHPCIState mch;
+} Q35PCIHost;
+
+#define Q35_MASK(bit, ms_bit, ls_bit) \
+((uint##bit##_t)(((1ULL << ((ms_bit) + 1)) - 1) & ~((1ULL << ls_bit) - 1)))
+
+/*
+ * gmch part
+ */
+
+/* PCI configuration */
+#define MCH_HOST_BRIDGE                        "MCH"
+
+#define MCH_HOST_BRIDGE_CONFIG_ADDR            0xcf8
+#define MCH_HOST_BRIDGE_CONFIG_DATA            0xcfc
+
+/* D0:F0 configuration space */
+#define MCH_HOST_BRIDGE_REVISION_DEFUALT       0x0
+
+#define MCH_HOST_BRIDGE_PCIEXBAR               0x60    /* 64bit register */
+#define MCH_HOST_BRIDGE_PCIEXBAR_SIZE          8       /* 64bit register */
+#define MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT       0xb0000000
+#define MCH_HOST_BRIDGE_PCIEXBAR_ADMSK         Q35_MASK(64, 35, 28)
+#define MCH_HOST_BRIDGE_PCIEXBAR_128ADMSK      ((uint64_t)(1 << 26))
+#define MCH_HOST_BRIDGE_PCIEXBAR_64ADMSK       ((uint64_t)(1 << 25))
+#define MCH_HOST_BRIDGE_PCIEXBAR_LENGTH_MASK   ((uint64_t)(0x3 << 1))
+#define MCH_HOST_BRIDGE_PCIEXBAR_LENGTH_256M   ((uint64_t)(0x0 << 1))
+#define MCH_HOST_BRIDGE_PCIEXBAR_LENGTH_128M   ((uint64_t)(0x1 << 1))
+#define MCH_HOST_BRIDGE_PCIEXBAR_LENGTH_64M    ((uint64_t)(0x2 << 1))
+#define MCH_HOST_BRIDGE_PCIEXBAR_LENGTH_RVD    ((uint64_t)(0x3 << 1))
+#define MCH_HOST_BRIDGE_PCIEXBAREN             ((uint64_t)1)
+
+#define MCH_HOST_BRIDGE_PAM_NB                 7
+#define MCH_HOST_BRIDGE_PAM_SIZE               7
+#define MCH_HOST_BRIDGE_PAM0                   0x90
+#define MCH_HOST_BRIDGE_PAM_BIOS_AREA          0xf0000
+#define MCH_HOST_BRIDGE_PAM_AREA_SIZE          0x10000 /* 16KB */
+#define MCH_HOST_BRIDGE_PAM1                   0x91
+#define MCH_HOST_BRIDGE_PAM_EXPAN_AREA         0xc0000
+#define MCH_HOST_BRIDGE_PAM_EXPAN_SIZE         0x04000
+#define MCH_HOST_BRIDGE_PAM2                   0x92
+#define MCH_HOST_BRIDGE_PAM3                   0x93
+#define MCH_HOST_BRIDGE_PAM4                   0x94
+#define MCH_HOST_BRIDGE_PAM_EXBIOS_AREA        0xe0000
+#define MCH_HOST_BRIDGE_PAM_EXBIOS_SIZE        0x04000
+#define MCH_HOST_BRIDGE_PAM5                   0x95
+#define MCH_HOST_BRIDGE_PAM6                   0x96
+#define MCH_HOST_BRIDGE_PAM_WE_HI              ((uint8_t)(0x2 << 4))
+#define MCH_HOST_BRIDGE_PAM_RE_HI              ((uint8_t)(0x1 << 4))
+#define MCH_HOST_BRIDGE_PAM_HI_MASK            ((uint8_t)(0x3 << 4))
+#define MCH_HOST_BRIDGE_PAM_WE_LO              ((uint8_t)0x2)
+#define MCH_HOST_BRIDGE_PAM_RE_LO              ((uint8_t)0x1)
+#define MCH_HOST_BRIDGE_PAM_LO_MASK            ((uint8_t)0x3)
+#define MCH_HOST_BRIDGE_PAM_WE                 ((uint8_t)0x2)
+#define MCH_HOST_BRIDGE_PAM_RE                 ((uint8_t)0x1)
+#define MCH_HOST_BRIDGE_PAM_MASK               ((uint8_t)0x3)
+
+#define MCH_HOST_BRDIGE_SMRAM                  0x9d
+#define MCH_HOST_BRDIGE_SMRAM_SIZE             1
+#define MCH_HOST_BRIDGE_SMRAM_DEFAULT          ((uint8_t)0x2)
+#define MCH_HOST_BRIDGE_SMRAM_D_OPEN           ((uint8_t)(1 << 6))
+#define MCH_HOST_BRIDGE_SMRAM_D_CLS            ((uint8_t)(1 << 5))
+#define MCH_HOST_BRIDGE_SMRAM_D_LCK            ((uint8_t)(1 << 4))
+#define MCH_HOST_BRIDGE_SMRAM_G_SMRAME         ((uint8_t)(1 << 3))
+#define MCH_HOST_BRIDGE_SMRAM_C_BASE_SEG_MASK  ((uint8_t)0x7)
+#define MCH_HOST_BRIDGE_SMRAM_C_BASE_SEG       ((uint8_t)0x2)  /* hardwired to b010 */
+#define MCH_HOST_BRIDGE_SMRAM_C_BASE           0xa0000
+#define MCH_HOST_BRIDGE_SMRAM_C_END            0xc0000
+#define MCH_HOST_BRIDGE_SMRAM_C_SIZE           0x20000
+#define MCH_HOST_BRIDGE_UPPER_SYSTEM_BIOS_END  0x100000
+
+#define MCH_HOST_BRIDGE_ESMRAMC                0x9e
+#define MCH_HOST_BRDIGE_ESMRAMC_H_SMRAME       ((uint8_t)(1 << 6))
+#define MCH_HOST_BRDIGE_ESMRAMC_E_SMERR        ((uint8_t)(1 << 5))
+#define MCH_HOST_BRDIGE_ESMRAMC_SM_CACHE       ((uint8_t)(1 << 4))
+#define MCH_HOST_BRDIGE_ESMRAMC_SM_L1          ((uint8_t)(1 << 3))
+#define MCH_HOST_BRDIGE_ESMRAMC_SM_L2          ((uint8_t)(1 << 2))
+#define MCH_HOST_BRDIGE_ESMRAMC_TSEG_SZ_MASK   ((uint8_t)(0x3 << 1))
+#define MCH_HOST_BRDIGE_ESMRAMC_TSEG_SZ_1MB    ((uint8_t)(0x0 << 1))
+#define MCH_HOST_BRDIGE_ESMRAMC_TSEG_SZ_2MB    ((uint8_t)(0x1 << 1))
+#define MCH_HOST_BRDIGE_ESMRAMC_TSEG_SZ_8MB    ((uint8_t)(0x2 << 1))
+#define MCH_HOST_BRDIGE_ESMRAMC_T_EN           ((uint8_t)1)
+
+/* D1:F0 PCIE* port*/
+#define MCH_PCIE_DEV                           1
+#define MCH_PCIE_FUNC                          0
+
+#endif /* HW_Q35_H */
diff --git a/hw/qdev-core.h b/hw/qdev-core.h
index fce9e2249c..fff7f0f5ab 100644
--- a/hw/qdev-core.h
+++ b/hw/qdev-core.h
@@ -106,17 +106,12 @@ typedef struct BusChild {
 
 /**
  * BusState:
- * @qom_allocated: Indicates whether the object was allocated by QOM.
- * @glib_allocated: Indicates whether the object was initialized in-place
- * yet is expected to be freed with g_free().
  */
 struct BusState {
     Object obj;
     DeviceState *parent;
     const char *name;
     int allow_hotplug;
-    bool qom_allocated;
-    bool glib_allocated;
     int max_index;
     QTAILQ_HEAD(ChildrenHead, BusChild) children;
     QLIST_ENTRY(BusState) sibling;
diff --git a/hw/qdev.c b/hw/qdev.c
index 7ddcd24299..788b4da55c 100644
--- a/hw/qdev.c
+++ b/hw/qdev.c
@@ -454,7 +454,6 @@ BusState *qbus_create(const char *typename, DeviceState *parent, const char *nam
     BusState *bus;
 
     bus = BUS(object_new(typename));
-    bus->qom_allocated = true;
 
     bus->parent = parent;
     bus->name = name ? g_strdup(name) : NULL;
@@ -465,14 +464,7 @@ BusState *qbus_create(const char *typename, DeviceState *parent, const char *nam
 
 void qbus_free(BusState *bus)
 {
-    if (bus->qom_allocated) {
-        object_delete(OBJECT(bus));
-    } else {
-        object_finalize(OBJECT(bus));
-        if (bus->glib_allocated) {
-            g_free(bus);
-        }
-    }
+    object_delete(OBJECT(bus));
 }
 
 static char *bus_get_fw_dev_path(BusState *bus, DeviceState *dev)
@@ -705,9 +697,6 @@ static void device_finalize(Object *obj)
             qemu_opts_del(dev->opts);
         }
     }
-    if (dev->parent_bus) {
-        bus_remove_child(dev->parent_bus, dev);
-    }
 }
 
 static void device_class_base_init(ObjectClass *class, void *data)
@@ -720,6 +709,18 @@ static void device_class_base_init(ObjectClass *class, void *data)
     klass->props = NULL;
 }
 
+static void qdev_remove_from_bus(Object *obj)
+{
+    DeviceState *dev = DEVICE(obj);
+
+    bus_remove_child(dev->parent_bus, dev);
+}
+
+static void device_class_init(ObjectClass *class, void *data)
+{
+    class->unparent = qdev_remove_from_bus;
+}
+
 void device_reset(DeviceState *dev)
 {
     DeviceClass *klass = DEVICE_GET_CLASS(dev);
@@ -747,6 +748,7 @@ static TypeInfo device_type_info = {
     .instance_init = device_initfn,
     .instance_finalize = device_finalize,
     .class_base_init = device_class_base_init,
+    .class_init = device_class_init,
     .abstract = true,
     .class_size = sizeof(DeviceClass),
 };
diff --git a/hw/smbus_ich9.c b/hw/smbus_ich9.c
new file mode 100644
index 0000000000..6940583bb6
--- /dev/null
+++ b/hw/smbus_ich9.c
@@ -0,0 +1,159 @@
+/*
+ * ACPI implementation
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+/*
+ *  Copyright (c) 2009 Isaku Yamahata <yamahata at valinux co jp>
+ *                     VA Linux Systems Japan K.K.
+ *  Copyright (C) 2012 Jason Baron <jbaron@redhat.com>
+ *
+ *  This is based on acpi.c, but heavily rewritten.
+ */
+#include "hw.h"
+#include "pc.h"
+#include "pm_smbus.h"
+#include "pci.h"
+#include "sysemu.h"
+#include "i2c.h"
+#include "smbus.h"
+
+#include "ich9.h"
+
+#define TYPE_ICH9_SMB_DEVICE "ICH9 SMB"
+#define ICH9_SMB_DEVICE(obj) \
+     OBJECT_CHECK(ICH9SMBState, (obj), TYPE_ICH9_SMB_DEVICE)
+
+typedef struct ICH9SMBState {
+    PCIDevice dev;
+
+    PMSMBus smb;
+    MemoryRegion mem_bar;
+} ICH9SMBState;
+
+static const VMStateDescription vmstate_ich9_smbus = {
+    .name = "ich9_smb",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_PCI_DEVICE(dev, struct ICH9SMBState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void ich9_smb_ioport_writeb(void *opaque, hwaddr addr,
+                                   uint64_t val, unsigned size)
+{
+    ICH9SMBState *s = opaque;
+    uint8_t hostc = s->dev.config[ICH9_SMB_HOSTC];
+
+    if ((hostc & ICH9_SMB_HOSTC_HST_EN) && !(hostc & ICH9_SMB_HOSTC_I2C_EN)) {
+        uint64_t offset = addr - s->dev.io_regions[ICH9_SMB_SMB_BASE_BAR].addr;
+        smb_ioport_writeb(&s->smb, offset, val);
+    }
+}
+
+static uint64_t ich9_smb_ioport_readb(void *opaque, hwaddr addr,
+                                      unsigned size)
+{
+    ICH9SMBState *s = opaque;
+    uint8_t hostc = s->dev.config[ICH9_SMB_HOSTC];
+
+    if ((hostc & ICH9_SMB_HOSTC_HST_EN) && !(hostc & ICH9_SMB_HOSTC_I2C_EN)) {
+        uint64_t offset = addr - s->dev.io_regions[ICH9_SMB_SMB_BASE_BAR].addr;
+        return smb_ioport_readb(&s->smb, offset);
+    }
+
+    return 0xff;
+}
+
+static const MemoryRegionOps lpc_smb_mmio_ops = {
+    .read = ich9_smb_ioport_readb,
+    .write = ich9_smb_ioport_writeb,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 1,
+    },
+};
+
+static int ich9_smbus_initfn(PCIDevice *d)
+{
+    ICH9SMBState *s = ICH9_SMB_DEVICE(d);
+
+    /* TODO? D31IP.SMIP in chipset configuration space */
+    pci_config_set_interrupt_pin(d->config, 0x01); /* interrupt pin 1 */
+
+    pci_set_byte(d->config + ICH9_SMB_HOSTC, 0);
+
+    /*
+     * update parameters based on
+     * paralell_hds[0]
+     * serial_hds[0]
+     * serial_hds[0]
+     * fdc
+     *
+     * Is there any OS that depends on them?
+     */
+
+    /* TODO smb_io_base */
+    pci_set_byte(d->config + ICH9_SMB_HOSTC, 0);
+    /* TODO bar0, bar1: 64bit BAR support*/
+
+    memory_region_init_io(&s->mem_bar, &lpc_smb_mmio_ops, s, "ich9-smbus-bar",
+                            ICH9_SMB_SMB_BASE_SIZE);
+    pci_register_bar(d, ICH9_SMB_SMB_BASE_BAR, PCI_BASE_ADDRESS_SPACE_IO,
+                        &s->mem_bar);
+    pm_smbus_init(&d->qdev, &s->smb);
+    return 0;
+}
+
+static void ich9_smb_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->vendor_id = PCI_VENDOR_ID_INTEL;
+    k->device_id = PCI_DEVICE_ID_INTEL_ICH9_6;
+    k->revision = ICH9_A2_SMB_REVISION;
+    k->class_id = PCI_CLASS_SERIAL_SMBUS;
+    dc->no_user = 1;
+    dc->vmsd = &vmstate_ich9_smbus;
+    dc->desc = "ICH9 SMBUS Bridge";
+    k->init = ich9_smbus_initfn;
+}
+
+i2c_bus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base)
+{
+    PCIDevice *d =
+        pci_create_simple_multifunction(bus, devfn, true, TYPE_ICH9_SMB_DEVICE);
+    ICH9SMBState *s = ICH9_SMB_DEVICE(d);
+    return s->smb.smbus;
+}
+
+static const TypeInfo ich9_smb_info = {
+    .name   = TYPE_ICH9_SMB_DEVICE,
+    .parent = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(ICH9SMBState),
+    .class_init = ich9_smb_class_init,
+};
+
+static void ich9_smb_register(void)
+{
+    type_register_static(&ich9_smb_info);
+}
+
+type_init(ich9_smb_register);
diff --git a/hw/sysbus.c b/hw/sysbus.c
index 4969f06a66..ef8ffb6603 100644
--- a/hw/sysbus.c
+++ b/hw/sysbus.c
@@ -274,7 +274,7 @@ static void main_system_bus_create(void)
     main_system_bus = g_malloc0(system_bus_info.instance_size);
     qbus_create_inplace(main_system_bus, TYPE_SYSTEM_BUS, NULL,
                         "main-system-bus");
-    main_system_bus->glib_allocated = true;
+    OBJECT(main_system_bus)->free = g_free;
     object_property_add_child(container_get(qdev_get_machine(),
                                             "/unattached"),
                               "sysbus", OBJECT(main_system_bus), NULL);
diff --git a/hw/virtio-rng.c b/hw/virtio-rng.c
index 3ca96c855f..df329f25e1 100644
--- a/hw/virtio-rng.c
+++ b/hw/virtio-rng.c
@@ -22,14 +22,9 @@ typedef struct VirtIORNG {
 
     /* Only one vq - guest puts buffer(s) on it when it needs entropy */
     VirtQueue *vq;
-    VirtQueueElement elem;
 
-    /* Config data for the device -- currently only chardev */
     VirtIORNGConf *conf;
 
-    /* Whether we've popped a vq element into 'elem' above */
-    bool popped;
-
     RngBackend *rng;
 
     /* We purposefully don't migrate this state.  The quota will reset on the
@@ -48,17 +43,12 @@ static bool is_guest_ready(VirtIORNG *vrng)
     return false;
 }
 
-static size_t pop_an_elem(VirtIORNG *vrng)
+static size_t get_request_size(VirtQueue *vq)
 {
-    size_t size;
-
-    if (!vrng->popped && !virtqueue_pop(vrng->vq, &vrng->elem)) {
-        return 0;
-    }
-    vrng->popped = true;
+    unsigned int in, out;
 
-    size = iov_size(vrng->elem.in_sg, vrng->elem.in_num);
-    return size;
+    virtqueue_get_avail_bytes(vq, &in, &out);
+    return in;
 }
 
 static void virtio_rng_process(VirtIORNG *vrng);
@@ -67,6 +57,7 @@ static void virtio_rng_process(VirtIORNG *vrng);
 static void chr_read(void *opaque, const void *buf, size_t size)
 {
     VirtIORNG *vrng = opaque;
+    VirtQueueElement elem;
     size_t len;
     int offset;
 
@@ -78,43 +69,33 @@ static void chr_read(void *opaque, const void *buf, size_t size)
 
     offset = 0;
     while (offset < size) {
-        if (!pop_an_elem(vrng)) {
+        if (!virtqueue_pop(vrng->vq, &elem)) {
             break;
         }
-        len = iov_from_buf(vrng->elem.in_sg, vrng->elem.in_num,
+        len = iov_from_buf(elem.in_sg, elem.in_num,
                            0, buf + offset, size - offset);
         offset += len;
 
-        virtqueue_push(vrng->vq, &vrng->elem, len);
-        vrng->popped = false;
+        virtqueue_push(vrng->vq, &elem, len);
     }
     virtio_notify(&vrng->vdev, vrng->vq);
-
-    /*
-     * Lastly, if we had multiple elems queued by the guest, and we
-     * didn't have enough data to fill them all, indicate we want more
-     * data.
-     */
-    virtio_rng_process(vrng);
 }
 
 static void virtio_rng_process(VirtIORNG *vrng)
 {
-    ssize_t size;
+    size_t size;
 
     if (!is_guest_ready(vrng)) {
         return;
     }
 
-    size = pop_an_elem(vrng);
+    size = get_request_size(vrng->vq);
     size = MIN(vrng->quota_remaining, size);
-
-    if (size > 0) {
+    if (size) {
         rng_backend_request_entropy(vrng->rng, size, chr_read, vrng);
     }
 }
 
-
 static void handle_input(VirtIODevice *vdev, VirtQueue *vq)
 {
     VirtIORNG *vrng = DO_UPCAST(VirtIORNG, vdev, vdev);
@@ -131,23 +112,6 @@ static void virtio_rng_save(QEMUFile *f, void *opaque)
     VirtIORNG *vrng = opaque;
 
     virtio_save(&vrng->vdev, f);
-
-    qemu_put_byte(f, vrng->popped);
-    if (vrng->popped) {
-        int i;
-
-        qemu_put_be32(f, vrng->elem.index);
-
-        qemu_put_be32(f, vrng->elem.in_num);
-        for (i = 0; i < vrng->elem.in_num; i++) {
-            qemu_put_be64(f, vrng->elem.in_addr[i]);
-        }
-
-        qemu_put_be32(f, vrng->elem.out_num);
-        for (i = 0; i < vrng->elem.out_num; i++) {
-            qemu_put_be64(f, vrng->elem.out_addr[i]);
-        }
-    }
 }
 
 static int virtio_rng_load(QEMUFile *f, void *opaque, int version_id)
@@ -159,34 +123,10 @@ static int virtio_rng_load(QEMUFile *f, void *opaque, int version_id)
     }
     virtio_load(&vrng->vdev, f);
 
-    vrng->popped = qemu_get_byte(f);
-    if (vrng->popped) {
-        int i;
-
-        vrng->elem.index = qemu_get_be32(f);
-
-        vrng->elem.in_num = qemu_get_be32(f);
-        g_assert(vrng->elem.in_num < VIRTQUEUE_MAX_SIZE);
-        for (i = 0; i < vrng->elem.in_num; i++) {
-            vrng->elem.in_addr[i] = qemu_get_be64(f);
-        }
-
-        vrng->elem.out_num = qemu_get_be32(f);
-        g_assert(vrng->elem.out_num < VIRTQUEUE_MAX_SIZE);
-        for (i = 0; i < vrng->elem.out_num; i++) {
-            vrng->elem.out_addr[i] = qemu_get_be64(f);
-        }
-
-        virtqueue_map_sg(vrng->elem.in_sg, vrng->elem.in_addr,
-                         vrng->elem.in_num, 1);
-        virtqueue_map_sg(vrng->elem.out_sg, vrng->elem.out_addr,
-                         vrng->elem.out_num, 0);
-    }
-
     /* We may have an element ready but couldn't process it due to a quota
-       limit.  Make sure to try again after live migration when the quota may
-       have been reset.
-    */
+     * limit.  Make sure to try again after live migration when the quota may
+     * have been reset.
+     */
     virtio_rng_process(vrng);
 
     return 0;
@@ -232,10 +172,9 @@ VirtIODevice *virtio_rng_init(DeviceState *dev, VirtIORNGConf *conf)
 
     vrng->qdev = dev;
     vrng->conf = conf;
-    vrng->popped = false;
-    vrng->quota_remaining = vrng->conf->max_bytes;
 
-    g_assert_cmpint(vrng->conf->max_bytes, <=, INT64_MAX);
+    assert(vrng->conf->max_bytes <= INT64_MAX);
+    vrng->quota_remaining = vrng->conf->max_bytes;
 
     vrng->rate_limit_timer = qemu_new_timer_ms(vm_clock,
                                                check_rate_limit, vrng);
@@ -253,6 +192,8 @@ void virtio_rng_exit(VirtIODevice *vdev)
 {
     VirtIORNG *vrng = DO_UPCAST(VirtIORNG, vdev, vdev);
 
+    qemu_del_timer(vrng->rate_limit_timer);
+    qemu_free_timer(vrng->rate_limit_timer);
     unregister_savevm(vrng->qdev, "virtio-rng", vrng);
     virtio_cleanup(vdev);
 }
diff --git a/include/qemu/object.h b/include/qemu/object.h
index be707f1a36..ed1f47f050 100644
--- a/include/qemu/object.h
+++ b/include/qemu/object.h
@@ -230,6 +230,23 @@ typedef struct ObjectProperty
 } ObjectProperty;
 
 /**
+ * ObjectUnparent:
+ * @obj: the object that is being removed from the composition tree
+ *
+ * Called when an object is being removed from the QOM composition tree.
+ * The function should remove any backlinks from children objects to @obj.
+ */
+typedef void (ObjectUnparent)(Object *obj);
+
+/**
+ * ObjectFree:
+ * @obj: the object being freed
+ *
+ * Called when an object's last reference is removed.
+ */
+typedef void (ObjectFree)(void *obj);
+
+/**
  * ObjectClass:
  *
  * The base for all classes.  The only thing that #ObjectClass contains is an
@@ -240,6 +257,8 @@ struct ObjectClass
     /*< private >*/
     Type type;
     GSList *interfaces;
+
+    ObjectUnparent *unparent;
 };
 
 /**
@@ -261,6 +280,7 @@ struct Object
 {
     /*< private >*/
     ObjectClass *class;
+    ObjectFree *free;
     QTAILQ_HEAD(, ObjectProperty) properties;
     uint32_t ref;
     Object *parent;
@@ -485,15 +505,6 @@ void object_initialize_with_type(void *data, Type type);
 void object_initialize(void *obj, const char *typename);
 
 /**
- * object_finalize:
- * @obj: The object to finalize.
- *
- * This function destroys and object without freeing the memory associated with
- * it.
- */
-void object_finalize(void *obj);
-
-/**
  * object_dynamic_cast:
  * @obj: The object to cast.
  * @typename: The @typename to cast to.
diff --git a/include/qemu/rng.h b/include/qemu/rng.h
index 7e9d6723ff..d094bf8d4c 100644
--- a/include/qemu/rng.h
+++ b/include/qemu/rng.h
@@ -61,10 +61,10 @@ struct RngBackend
  * This function is used by the front-end to request entropy from an entropy
  * source.  This function can be called multiple times before @receive_entropy
  * is invoked with different values of @receive_entropy and @opaque.  The
- * backend will queue each request and handle appropriate.
+ * backend will queue each request and handle appropriately.
  *
  * The backend does not need to pass the full amount of data to @receive_entropy
- * but will pass at a value greater than 0.
+ * but will pass a value greater than 0.
  */
 void rng_backend_request_entropy(RngBackend *s, size_t size,
                                  EntropyReceiveFunc *receive_entropy,
@@ -87,7 +87,7 @@ void rng_backend_cancel_requests(RngBackend *s);
  *
  * This function will open the backend if it is not already open.  Calling this
  * function on an already opened backend will not result in an error.
- */ 
+ */
 void rng_backend_open(RngBackend *s, Error **errp);
 
 #endif
diff --git a/kvm.h b/kvm.h
index 1e7f244561..72d866a966 100644
--- a/kvm.h
+++ b/kvm.h
@@ -275,4 +275,6 @@ void kvm_irqchip_release_virq(KVMState *s, int virq);
 
 int kvm_irqchip_add_irqfd_notifier(KVMState *s, EventNotifier *n, int virq);
 int kvm_irqchip_remove_irqfd_notifier(KVMState *s, EventNotifier *n, int virq);
+void kvm_pc_gsi_handler(void *opaque, int n, int level);
+void kvm_pc_setup_irq_routing(bool pci_enabled);
 #endif
diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c
index a154523731..a07b171b86 100644
--- a/qapi/qapi-dealloc-visitor.c
+++ b/qapi/qapi-dealloc-visitor.c
@@ -132,6 +132,11 @@ static void qapi_dealloc_type_number(Visitor *v, double *obj, const char *name,
 {
 }
 
+static void qapi_dealloc_type_size(Visitor *v, size_t *obj, const char *name,
+                                   Error **errp)
+{
+}
+
 static void qapi_dealloc_type_enum(Visitor *v, int *obj, const char *strings[],
                                    const char *kind, const char *name,
                                    Error **errp)
@@ -164,6 +169,7 @@ QapiDeallocVisitor *qapi_dealloc_visitor_new(void)
     v->visitor.type_bool = qapi_dealloc_type_bool;
     v->visitor.type_str = qapi_dealloc_type_str;
     v->visitor.type_number = qapi_dealloc_type_number;
+    v->visitor.type_size = qapi_dealloc_type_size;
 
     QTAILQ_INIT(&v->stack);
 
diff --git a/qom/object.c b/qom/object.c
index d7092b09d8..0739aa2943 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -307,6 +307,7 @@ void object_initialize_with_type(void *data, TypeImpl *type)
 
     memset(obj, 0, type->instance_size);
     obj->class = type->class;
+    object_ref(obj);
     QTAILQ_INIT(&obj->properties);
     object_init_with_type(obj, type);
 }
@@ -362,6 +363,9 @@ void object_unparent(Object *obj)
     if (obj->parent) {
         object_property_del_child(obj->parent, obj, NULL);
     }
+    if (obj->class->unparent) {
+        (obj->class->unparent)(obj);
+    }
 }
 
 static void object_deinit(Object *obj, TypeImpl *type)
@@ -375,7 +379,7 @@ static void object_deinit(Object *obj, TypeImpl *type)
     }
 }
 
-void object_finalize(void *data)
+static void object_finalize(void *data)
 {
     Object *obj = data;
     TypeImpl *ti = obj->class->type;
@@ -384,6 +388,9 @@ void object_finalize(void *data)
     object_property_del_all(obj);
 
     g_assert(obj->ref == 0);
+    if (obj->free) {
+        obj->free(obj);
+    }
 }
 
 Object *object_new_with_type(Type type)
@@ -395,7 +402,7 @@ Object *object_new_with_type(Type type)
 
     obj = g_malloc(type->instance_size);
     object_initialize_with_type(obj, type);
-    object_ref(obj);
+    obj->free = g_free;
 
     return obj;
 }
@@ -412,12 +419,11 @@ void object_delete(Object *obj)
     object_unparent(obj);
     g_assert(obj->ref == 1);
     object_unref(obj);
-    g_free(obj);
 }
 
 Object *object_dynamic_cast(Object *obj, const char *typename)
 {
-    if (object_class_dynamic_cast(object_get_class(obj), typename)) {
+    if (obj && object_class_dynamic_cast(object_get_class(obj), typename)) {
         return obj;
     }
 
@@ -430,7 +436,7 @@ Object *object_dynamic_cast_assert(Object *obj, const char *typename)
 
     inst = object_dynamic_cast(obj, typename);
 
-    if (!inst) {
+    if (!inst && obj) {
         fprintf(stderr, "Object %p is not an instance of type %s\n",
                 obj, typename);
         abort();
diff --git a/tests/Makefile b/tests/Makefile
index ca680e5644..b60f0fb8f0 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -15,6 +15,8 @@ check-unit-y += tests/test-string-output-visitor$(EXESUF)
 check-unit-y += tests/test-coroutine$(EXESUF)
 check-unit-y += tests/test-visitor-serialization$(EXESUF)
 check-unit-y += tests/test-iov$(EXESUF)
+check-unit-y += tests/test-aio$(EXESUF)
+check-unit-y += tests/test-thread-pool$(EXESUF)
 
 check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
 
@@ -49,6 +51,8 @@ tests/check-qlist$(EXESUF): tests/check-qlist.o qlist.o qint.o
 tests/check-qfloat$(EXESUF): tests/check-qfloat.o qfloat.o
 tests/check-qjson$(EXESUF): tests/check-qjson.o $(qobject-obj-y) qemu-tool.o
 tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(coroutine-obj-y) $(tools-obj-y) $(block-obj-y) iov.o libqemustub.a
+tests/test-aio$(EXESUF): tests/test-aio.o $(coroutine-obj-y) $(tools-obj-y) $(block-obj-y) libqemustub.a
+tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(coroutine-obj-y) $(tools-obj-y) $(block-obj-y) libqemustub.a
 tests/test-iov$(EXESUF): tests/test-iov.o iov.o
 
 tests/test-qapi-types.c tests/test-qapi-types.h :\
diff --git a/tests/rtc-test.c b/tests/rtc-test.c
index 7fdc94a3de..02edbf5727 100644
--- a/tests/rtc-test.c
+++ b/tests/rtc-test.c
@@ -327,6 +327,45 @@ static void fuzz_registers(void)
     }
 }
 
+static void register_b_set_flag(void)
+{
+    /* Enable binary-coded decimal (BCD) mode and SET flag in Register B*/
+    cmos_write(RTC_REG_B, (cmos_read(RTC_REG_B) & ~REG_B_DM) | REG_B_SET);
+
+    cmos_write(RTC_REG_A, 0x76);
+    cmos_write(RTC_YEAR, 0x11);
+    cmos_write(RTC_CENTURY, 0x20);
+    cmos_write(RTC_MONTH, 0x02);
+    cmos_write(RTC_DAY_OF_MONTH, 0x02);
+    cmos_write(RTC_HOURS, 0x02);
+    cmos_write(RTC_MINUTES, 0x04);
+    cmos_write(RTC_SECONDS, 0x58);
+    cmos_write(RTC_REG_A, 0x26);
+
+    /* Since SET flag is still enabled, these are equality checks. */
+    g_assert_cmpint(cmos_read(RTC_HOURS), ==, 0x02);
+    g_assert_cmpint(cmos_read(RTC_MINUTES), ==, 0x04);
+    g_assert_cmpint(cmos_read(RTC_SECONDS), ==, 0x58);
+    g_assert_cmpint(cmos_read(RTC_DAY_OF_MONTH), ==, 0x02);
+    g_assert_cmpint(cmos_read(RTC_MONTH), ==, 0x02);
+    g_assert_cmpint(cmos_read(RTC_YEAR), ==, 0x11);
+    g_assert_cmpint(cmos_read(RTC_CENTURY), ==, 0x20);
+
+    /* Disable SET flag in Register B */
+    cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) & ~REG_B_SET);
+
+    g_assert_cmpint(cmos_read(RTC_HOURS), ==, 0x02);
+    g_assert_cmpint(cmos_read(RTC_MINUTES), ==, 0x04);
+
+    /* Since SET flag is disabled, this is an inequality check.
+     * We (reasonably) assume that no (sexagesimal) overflow occurs. */
+    g_assert_cmpint(cmos_read(RTC_SECONDS), >=, 0x58);
+    g_assert_cmpint(cmos_read(RTC_DAY_OF_MONTH), ==, 0x02);
+    g_assert_cmpint(cmos_read(RTC_MONTH), ==, 0x02);
+    g_assert_cmpint(cmos_read(RTC_YEAR), ==, 0x11);
+    g_assert_cmpint(cmos_read(RTC_CENTURY), ==, 0x20);
+}
+
 int main(int argc, char **argv)
 {
     QTestState *s = NULL;
@@ -342,6 +381,7 @@ int main(int argc, char **argv)
     qtest_add_func("/rtc/alarm-time", alarm_time);
     qtest_add_func("/rtc/set-year/20xx", set_year_20xx);
     qtest_add_func("/rtc/set-year/1980", set_year_1980);
+    qtest_add_func("/rtc/register_b_set_flag", register_b_set_flag);
     qtest_add_func("/rtc/fuzz-registers", fuzz_registers);
     ret = g_test_run();
 
diff --git a/tests/test-aio.c b/tests/test-aio.c
new file mode 100644
index 0000000000..f53c908707
--- /dev/null
+++ b/tests/test-aio.c
@@ -0,0 +1,667 @@
+/*
+ * AioContext tests
+ *
+ * Copyright Red Hat, Inc. 2012
+ *
+ * Authors:
+ *  Paolo Bonzini    <pbonzini@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#include <glib.h>
+#include "qemu-aio.h"
+
+AioContext *ctx;
+
+/* Simple callbacks for testing.  */
+
+typedef struct {
+    QEMUBH *bh;
+    int n;
+    int max;
+} BHTestData;
+
+static void bh_test_cb(void *opaque)
+{
+    BHTestData *data = opaque;
+    if (++data->n < data->max) {
+        qemu_bh_schedule(data->bh);
+    }
+}
+
+static void bh_delete_cb(void *opaque)
+{
+    BHTestData *data = opaque;
+    if (++data->n < data->max) {
+        qemu_bh_schedule(data->bh);
+    } else {
+        qemu_bh_delete(data->bh);
+        data->bh = NULL;
+    }
+}
+
+typedef struct {
+    EventNotifier e;
+    int n;
+    int active;
+    bool auto_set;
+} EventNotifierTestData;
+
+static int event_active_cb(EventNotifier *e)
+{
+    EventNotifierTestData *data = container_of(e, EventNotifierTestData, e);
+    return data->active > 0;
+}
+
+static void event_ready_cb(EventNotifier *e)
+{
+    EventNotifierTestData *data = container_of(e, EventNotifierTestData, e);
+    g_assert(event_notifier_test_and_clear(e));
+    data->n++;
+    if (data->active > 0) {
+        data->active--;
+    }
+    if (data->auto_set && data->active) {
+        event_notifier_set(e);
+    }
+}
+
+/* Tests using aio_*.  */
+
+static void test_notify(void)
+{
+    g_assert(!aio_poll(ctx, false));
+    aio_notify(ctx);
+    g_assert(!aio_poll(ctx, true));
+    g_assert(!aio_poll(ctx, false));
+}
+
+static void test_flush(void)
+{
+    g_assert(!aio_poll(ctx, false));
+    aio_notify(ctx);
+    aio_flush(ctx);
+    g_assert(!aio_poll(ctx, false));
+}
+
+static void test_bh_schedule(void)
+{
+    BHTestData data = { .n = 0 };
+    data.bh = aio_bh_new(ctx, bh_test_cb, &data);
+
+    qemu_bh_schedule(data.bh);
+    g_assert_cmpint(data.n, ==, 0);
+
+    g_assert(aio_poll(ctx, true));
+    g_assert_cmpint(data.n, ==, 1);
+
+    g_assert(!aio_poll(ctx, false));
+    g_assert_cmpint(data.n, ==, 1);
+    qemu_bh_delete(data.bh);
+}
+
+static void test_bh_schedule10(void)
+{
+    BHTestData data = { .n = 0, .max = 10 };
+    data.bh = aio_bh_new(ctx, bh_test_cb, &data);
+
+    qemu_bh_schedule(data.bh);
+    g_assert_cmpint(data.n, ==, 0);
+
+    g_assert(aio_poll(ctx, false));
+    g_assert_cmpint(data.n, ==, 1);
+
+    g_assert(aio_poll(ctx, true));
+    g_assert_cmpint(data.n, ==, 2);
+
+    aio_flush(ctx);
+    g_assert_cmpint(data.n, ==, 10);
+
+    g_assert(!aio_poll(ctx, false));
+    g_assert_cmpint(data.n, ==, 10);
+    qemu_bh_delete(data.bh);
+}
+
+static void test_bh_cancel(void)
+{
+    BHTestData data = { .n = 0 };
+    data.bh = aio_bh_new(ctx, bh_test_cb, &data);
+
+    qemu_bh_schedule(data.bh);
+    g_assert_cmpint(data.n, ==, 0);
+
+    qemu_bh_cancel(data.bh);
+    g_assert_cmpint(data.n, ==, 0);
+
+    g_assert(!aio_poll(ctx, false));
+    g_assert_cmpint(data.n, ==, 0);
+    qemu_bh_delete(data.bh);
+}
+
+static void test_bh_delete(void)
+{
+    BHTestData data = { .n = 0 };
+    data.bh = aio_bh_new(ctx, bh_test_cb, &data);
+
+    qemu_bh_schedule(data.bh);
+    g_assert_cmpint(data.n, ==, 0);
+
+    qemu_bh_delete(data.bh);
+    g_assert_cmpint(data.n, ==, 0);
+
+    g_assert(!aio_poll(ctx, false));
+    g_assert_cmpint(data.n, ==, 0);
+}
+
+static void test_bh_delete_from_cb(void)
+{
+    BHTestData data1 = { .n = 0, .max = 1 };
+
+    data1.bh = aio_bh_new(ctx, bh_delete_cb, &data1);
+
+    qemu_bh_schedule(data1.bh);
+    g_assert_cmpint(data1.n, ==, 0);
+
+    aio_flush(ctx);
+    g_assert_cmpint(data1.n, ==, data1.max);
+    g_assert(data1.bh == NULL);
+
+    g_assert(!aio_poll(ctx, false));
+    g_assert(!aio_poll(ctx, true));
+}
+
+static void test_bh_delete_from_cb_many(void)
+{
+    BHTestData data1 = { .n = 0, .max = 1 };
+    BHTestData data2 = { .n = 0, .max = 3 };
+    BHTestData data3 = { .n = 0, .max = 2 };
+    BHTestData data4 = { .n = 0, .max = 4 };
+
+    data1.bh = aio_bh_new(ctx, bh_delete_cb, &data1);
+    data2.bh = aio_bh_new(ctx, bh_delete_cb, &data2);
+    data3.bh = aio_bh_new(ctx, bh_delete_cb, &data3);
+    data4.bh = aio_bh_new(ctx, bh_delete_cb, &data4);
+
+    qemu_bh_schedule(data1.bh);
+    qemu_bh_schedule(data2.bh);
+    qemu_bh_schedule(data3.bh);
+    qemu_bh_schedule(data4.bh);
+    g_assert_cmpint(data1.n, ==, 0);
+    g_assert_cmpint(data2.n, ==, 0);
+    g_assert_cmpint(data3.n, ==, 0);
+    g_assert_cmpint(data4.n, ==, 0);
+
+    g_assert(aio_poll(ctx, false));
+    g_assert_cmpint(data1.n, ==, 1);
+    g_assert_cmpint(data2.n, ==, 1);
+    g_assert_cmpint(data3.n, ==, 1);
+    g_assert_cmpint(data4.n, ==, 1);
+    g_assert(data1.bh == NULL);
+
+    aio_flush(ctx);
+    g_assert_cmpint(data1.n, ==, data1.max);
+    g_assert_cmpint(data2.n, ==, data2.max);
+    g_assert_cmpint(data3.n, ==, data3.max);
+    g_assert_cmpint(data4.n, ==, data4.max);
+    g_assert(data1.bh == NULL);
+    g_assert(data2.bh == NULL);
+    g_assert(data3.bh == NULL);
+    g_assert(data4.bh == NULL);
+}
+
+static void test_bh_flush(void)
+{
+    BHTestData data = { .n = 0 };
+    data.bh = aio_bh_new(ctx, bh_test_cb, &data);
+
+    qemu_bh_schedule(data.bh);
+    g_assert_cmpint(data.n, ==, 0);
+
+    aio_flush(ctx);
+    g_assert_cmpint(data.n, ==, 1);
+
+    g_assert(!aio_poll(ctx, false));
+    g_assert_cmpint(data.n, ==, 1);
+    qemu_bh_delete(data.bh);
+}
+
+static void test_set_event_notifier(void)
+{
+    EventNotifierTestData data = { .n = 0, .active = 0 };
+    event_notifier_init(&data.e, false);
+    aio_set_event_notifier(ctx, &data.e, event_ready_cb, event_active_cb);
+    g_assert(!aio_poll(ctx, false));
+    g_assert_cmpint(data.n, ==, 0);
+
+    aio_set_event_notifier(ctx, &data.e, NULL, NULL);
+    g_assert(!aio_poll(ctx, false));
+    g_assert_cmpint(data.n, ==, 0);
+    event_notifier_cleanup(&data.e);
+}
+
+static void test_wait_event_notifier(void)
+{
+    EventNotifierTestData data = { .n = 0, .active = 1 };
+    event_notifier_init(&data.e, false);
+    aio_set_event_notifier(ctx, &data.e, event_ready_cb, event_active_cb);
+    g_assert(aio_poll(ctx, false));
+    g_assert_cmpint(data.n, ==, 0);
+    g_assert_cmpint(data.active, ==, 1);
+
+    event_notifier_set(&data.e);
+    g_assert(aio_poll(ctx, false));
+    g_assert_cmpint(data.n, ==, 1);
+    g_assert_cmpint(data.active, ==, 0);
+
+    g_assert(!aio_poll(ctx, false));
+    g_assert_cmpint(data.n, ==, 1);
+    g_assert_cmpint(data.active, ==, 0);
+
+    aio_set_event_notifier(ctx, &data.e, NULL, NULL);
+    g_assert(!aio_poll(ctx, false));
+    g_assert_cmpint(data.n, ==, 1);
+
+    event_notifier_cleanup(&data.e);
+}
+
+static void test_flush_event_notifier(void)
+{
+    EventNotifierTestData data = { .n = 0, .active = 10, .auto_set = true };
+    event_notifier_init(&data.e, false);
+    aio_set_event_notifier(ctx, &data.e, event_ready_cb, event_active_cb);
+    g_assert(aio_poll(ctx, false));
+    g_assert_cmpint(data.n, ==, 0);
+    g_assert_cmpint(data.active, ==, 10);
+
+    event_notifier_set(&data.e);
+    g_assert(aio_poll(ctx, false));
+    g_assert_cmpint(data.n, ==, 1);
+    g_assert_cmpint(data.active, ==, 9);
+    g_assert(aio_poll(ctx, false));
+
+    aio_flush(ctx);
+    g_assert_cmpint(data.n, ==, 10);
+    g_assert_cmpint(data.active, ==, 0);
+    g_assert(!aio_poll(ctx, false));
+
+    aio_set_event_notifier(ctx, &data.e, NULL, NULL);
+    g_assert(!aio_poll(ctx, false));
+    event_notifier_cleanup(&data.e);
+}
+
+static void test_wait_event_notifier_noflush(void)
+{
+    EventNotifierTestData data = { .n = 0 };
+    EventNotifierTestData dummy = { .n = 0, .active = 1 };
+
+    event_notifier_init(&data.e, false);
+    aio_set_event_notifier(ctx, &data.e, event_ready_cb, NULL);
+
+    g_assert(!aio_poll(ctx, false));
+    g_assert_cmpint(data.n, ==, 0);
+
+    /* Until there is an active descriptor, aio_poll may or may not call
+     * event_ready_cb.  Still, it must not block.  */
+    event_notifier_set(&data.e);
+    g_assert(!aio_poll(ctx, true));
+    data.n = 0;
+
+    /* An active event notifier forces aio_poll to look at EventNotifiers.  */
+    event_notifier_init(&dummy.e, false);
+    aio_set_event_notifier(ctx, &dummy.e, event_ready_cb, event_active_cb);
+
+    event_notifier_set(&data.e);
+    g_assert(aio_poll(ctx, false));
+    g_assert_cmpint(data.n, ==, 1);
+    g_assert(!aio_poll(ctx, false));
+    g_assert_cmpint(data.n, ==, 1);
+
+    event_notifier_set(&data.e);
+    g_assert(aio_poll(ctx, false));
+    g_assert_cmpint(data.n, ==, 2);
+    g_assert(!aio_poll(ctx, false));
+    g_assert_cmpint(data.n, ==, 2);
+
+    event_notifier_set(&dummy.e);
+    aio_flush(ctx);
+    g_assert_cmpint(data.n, ==, 2);
+    g_assert_cmpint(dummy.n, ==, 1);
+    g_assert_cmpint(dummy.active, ==, 0);
+
+    aio_set_event_notifier(ctx, &dummy.e, NULL, NULL);
+    event_notifier_cleanup(&dummy.e);
+
+    aio_set_event_notifier(ctx, &data.e, NULL, NULL);
+    g_assert(!aio_poll(ctx, false));
+    g_assert_cmpint(data.n, ==, 2);
+
+    event_notifier_cleanup(&data.e);
+}
+
+/* Now the same tests, using the context as a GSource.  They are
+ * very similar to the ones above, with g_main_context_iteration
+ * replacing aio_poll.  However:
+ * - sometimes both the AioContext and the glib main loop wake
+ *   themselves up.  Hence, some "g_assert(!aio_poll(ctx, false));"
+ *   are replaced by "while (g_main_context_iteration(NULL, false));".
+ * - there is no exact replacement for aio_flush's blocking wait.
+ *   "while (g_main_context_iteration(NULL, true)" seems to work,
+ *   but it is not documented _why_ it works.  For these tests a
+ *   non-blocking loop like "while (g_main_context_iteration(NULL, false)"
+ *   works well, and that's what I am using.
+ */
+
+static void test_source_notify(void)
+{
+    while (g_main_context_iteration(NULL, false));
+    aio_notify(ctx);
+    g_assert(g_main_context_iteration(NULL, true));
+    g_assert(!g_main_context_iteration(NULL, false));
+}
+
+static void test_source_flush(void)
+{
+    g_assert(!g_main_context_iteration(NULL, false));
+    aio_notify(ctx);
+    while (g_main_context_iteration(NULL, false));
+    g_assert(!g_main_context_iteration(NULL, false));
+}
+
+static void test_source_bh_schedule(void)
+{
+    BHTestData data = { .n = 0 };
+    data.bh = aio_bh_new(ctx, bh_test_cb, &data);
+
+    qemu_bh_schedule(data.bh);
+    g_assert_cmpint(data.n, ==, 0);
+
+    g_assert(g_main_context_iteration(NULL, true));
+    g_assert_cmpint(data.n, ==, 1);
+
+    g_assert(!g_main_context_iteration(NULL, false));
+    g_assert_cmpint(data.n, ==, 1);
+    qemu_bh_delete(data.bh);
+}
+
+static void test_source_bh_schedule10(void)
+{
+    BHTestData data = { .n = 0, .max = 10 };
+    data.bh = aio_bh_new(ctx, bh_test_cb, &data);
+
+    qemu_bh_schedule(data.bh);
+    g_assert_cmpint(data.n, ==, 0);
+
+    g_assert(g_main_context_iteration(NULL, false));
+    g_assert_cmpint(data.n, ==, 1);
+
+    g_assert(g_main_context_iteration(NULL, true));
+    g_assert_cmpint(data.n, ==, 2);
+
+    while (g_main_context_iteration(NULL, false));
+    g_assert_cmpint(data.n, ==, 10);
+
+    g_assert(!g_main_context_iteration(NULL, false));
+    g_assert_cmpint(data.n, ==, 10);
+    qemu_bh_delete(data.bh);
+}
+
+static void test_source_bh_cancel(void)
+{
+    BHTestData data = { .n = 0 };
+    data.bh = aio_bh_new(ctx, bh_test_cb, &data);
+
+    qemu_bh_schedule(data.bh);
+    g_assert_cmpint(data.n, ==, 0);
+
+    qemu_bh_cancel(data.bh);
+    g_assert_cmpint(data.n, ==, 0);
+
+    while (g_main_context_iteration(NULL, false));
+    g_assert_cmpint(data.n, ==, 0);
+    qemu_bh_delete(data.bh);
+}
+
+static void test_source_bh_delete(void)
+{
+    BHTestData data = { .n = 0 };
+    data.bh = aio_bh_new(ctx, bh_test_cb, &data);
+
+    qemu_bh_schedule(data.bh);
+    g_assert_cmpint(data.n, ==, 0);
+
+    qemu_bh_delete(data.bh);
+    g_assert_cmpint(data.n, ==, 0);
+
+    while (g_main_context_iteration(NULL, false));
+    g_assert_cmpint(data.n, ==, 0);
+}
+
+static void test_source_bh_delete_from_cb(void)
+{
+    BHTestData data1 = { .n = 0, .max = 1 };
+
+    data1.bh = aio_bh_new(ctx, bh_delete_cb, &data1);
+
+    qemu_bh_schedule(data1.bh);
+    g_assert_cmpint(data1.n, ==, 0);
+
+    g_main_context_iteration(NULL, true);
+    g_assert_cmpint(data1.n, ==, data1.max);
+    g_assert(data1.bh == NULL);
+
+    g_assert(!g_main_context_iteration(NULL, false));
+}
+
+static void test_source_bh_delete_from_cb_many(void)
+{
+    BHTestData data1 = { .n = 0, .max = 1 };
+    BHTestData data2 = { .n = 0, .max = 3 };
+    BHTestData data3 = { .n = 0, .max = 2 };
+    BHTestData data4 = { .n = 0, .max = 4 };
+
+    data1.bh = aio_bh_new(ctx, bh_delete_cb, &data1);
+    data2.bh = aio_bh_new(ctx, bh_delete_cb, &data2);
+    data3.bh = aio_bh_new(ctx, bh_delete_cb, &data3);
+    data4.bh = aio_bh_new(ctx, bh_delete_cb, &data4);
+
+    qemu_bh_schedule(data1.bh);
+    qemu_bh_schedule(data2.bh);
+    qemu_bh_schedule(data3.bh);
+    qemu_bh_schedule(data4.bh);
+    g_assert_cmpint(data1.n, ==, 0);
+    g_assert_cmpint(data2.n, ==, 0);
+    g_assert_cmpint(data3.n, ==, 0);
+    g_assert_cmpint(data4.n, ==, 0);
+
+    g_assert(g_main_context_iteration(NULL, false));
+    g_assert_cmpint(data1.n, ==, 1);
+    g_assert_cmpint(data2.n, ==, 1);
+    g_assert_cmpint(data3.n, ==, 1);
+    g_assert_cmpint(data4.n, ==, 1);
+    g_assert(data1.bh == NULL);
+
+    while (g_main_context_iteration(NULL, false));
+    g_assert_cmpint(data1.n, ==, data1.max);
+    g_assert_cmpint(data2.n, ==, data2.max);
+    g_assert_cmpint(data3.n, ==, data3.max);
+    g_assert_cmpint(data4.n, ==, data4.max);
+    g_assert(data1.bh == NULL);
+    g_assert(data2.bh == NULL);
+    g_assert(data3.bh == NULL);
+    g_assert(data4.bh == NULL);
+}
+
+static void test_source_bh_flush(void)
+{
+    BHTestData data = { .n = 0 };
+    data.bh = aio_bh_new(ctx, bh_test_cb, &data);
+
+    qemu_bh_schedule(data.bh);
+    g_assert_cmpint(data.n, ==, 0);
+
+    g_assert(g_main_context_iteration(NULL, true));
+    g_assert_cmpint(data.n, ==, 1);
+
+    g_assert(!g_main_context_iteration(NULL, false));
+    g_assert_cmpint(data.n, ==, 1);
+    qemu_bh_delete(data.bh);
+}
+
+static void test_source_set_event_notifier(void)
+{
+    EventNotifierTestData data = { .n = 0, .active = 0 };
+    event_notifier_init(&data.e, false);
+    aio_set_event_notifier(ctx, &data.e, event_ready_cb, event_active_cb);
+    while (g_main_context_iteration(NULL, false));
+    g_assert_cmpint(data.n, ==, 0);
+
+    aio_set_event_notifier(ctx, &data.e, NULL, NULL);
+    while (g_main_context_iteration(NULL, false));
+    g_assert_cmpint(data.n, ==, 0);
+    event_notifier_cleanup(&data.e);
+}
+
+static void test_source_wait_event_notifier(void)
+{
+    EventNotifierTestData data = { .n = 0, .active = 1 };
+    event_notifier_init(&data.e, false);
+    aio_set_event_notifier(ctx, &data.e, event_ready_cb, event_active_cb);
+    g_assert(g_main_context_iteration(NULL, false));
+    g_assert_cmpint(data.n, ==, 0);
+    g_assert_cmpint(data.active, ==, 1);
+
+    event_notifier_set(&data.e);
+    g_assert(g_main_context_iteration(NULL, false));
+    g_assert_cmpint(data.n, ==, 1);
+    g_assert_cmpint(data.active, ==, 0);
+
+    while (g_main_context_iteration(NULL, false));
+    g_assert_cmpint(data.n, ==, 1);
+    g_assert_cmpint(data.active, ==, 0);
+
+    aio_set_event_notifier(ctx, &data.e, NULL, NULL);
+    while (g_main_context_iteration(NULL, false));
+    g_assert_cmpint(data.n, ==, 1);
+
+    event_notifier_cleanup(&data.e);
+}
+
+static void test_source_flush_event_notifier(void)
+{
+    EventNotifierTestData data = { .n = 0, .active = 10, .auto_set = true };
+    event_notifier_init(&data.e, false);
+    aio_set_event_notifier(ctx, &data.e, event_ready_cb, event_active_cb);
+    g_assert(g_main_context_iteration(NULL, false));
+    g_assert_cmpint(data.n, ==, 0);
+    g_assert_cmpint(data.active, ==, 10);
+
+    event_notifier_set(&data.e);
+    g_assert(g_main_context_iteration(NULL, false));
+    g_assert_cmpint(data.n, ==, 1);
+    g_assert_cmpint(data.active, ==, 9);
+    g_assert(g_main_context_iteration(NULL, false));
+
+    while (g_main_context_iteration(NULL, false));
+    g_assert_cmpint(data.n, ==, 10);
+    g_assert_cmpint(data.active, ==, 0);
+    g_assert(!g_main_context_iteration(NULL, false));
+
+    aio_set_event_notifier(ctx, &data.e, NULL, NULL);
+    while (g_main_context_iteration(NULL, false));
+    event_notifier_cleanup(&data.e);
+}
+
+static void test_source_wait_event_notifier_noflush(void)
+{
+    EventNotifierTestData data = { .n = 0 };
+    EventNotifierTestData dummy = { .n = 0, .active = 1 };
+
+    event_notifier_init(&data.e, false);
+    aio_set_event_notifier(ctx, &data.e, event_ready_cb, NULL);
+
+    while (g_main_context_iteration(NULL, false));
+    g_assert_cmpint(data.n, ==, 0);
+
+    /* Until there is an active descriptor, glib may or may not call
+     * event_ready_cb.  Still, it must not block.  */
+    event_notifier_set(&data.e);
+    g_main_context_iteration(NULL, true);
+    data.n = 0;
+
+    /* An active event notifier forces aio_poll to look at EventNotifiers.  */
+    event_notifier_init(&dummy.e, false);
+    aio_set_event_notifier(ctx, &dummy.e, event_ready_cb, event_active_cb);
+
+    event_notifier_set(&data.e);
+    g_assert(g_main_context_iteration(NULL, false));
+    g_assert_cmpint(data.n, ==, 1);
+    g_assert(!g_main_context_iteration(NULL, false));
+    g_assert_cmpint(data.n, ==, 1);
+
+    event_notifier_set(&data.e);
+    g_assert(g_main_context_iteration(NULL, false));
+    g_assert_cmpint(data.n, ==, 2);
+    g_assert(!g_main_context_iteration(NULL, false));
+    g_assert_cmpint(data.n, ==, 2);
+
+    event_notifier_set(&dummy.e);
+    while (g_main_context_iteration(NULL, false));
+    g_assert_cmpint(data.n, ==, 2);
+    g_assert_cmpint(dummy.n, ==, 1);
+    g_assert_cmpint(dummy.active, ==, 0);
+
+    aio_set_event_notifier(ctx, &dummy.e, NULL, NULL);
+    event_notifier_cleanup(&dummy.e);
+
+    aio_set_event_notifier(ctx, &data.e, NULL, NULL);
+    while (g_main_context_iteration(NULL, false));
+    g_assert_cmpint(data.n, ==, 2);
+
+    event_notifier_cleanup(&data.e);
+}
+
+/* End of tests.  */
+
+int main(int argc, char **argv)
+{
+    GSource *src;
+
+    ctx = aio_context_new();
+    src = aio_get_g_source(ctx);
+    g_source_attach(src, NULL);
+    g_source_unref(src);
+
+    while (g_main_context_iteration(NULL, false));
+
+    g_test_init(&argc, &argv, NULL);
+    g_test_add_func("/aio/notify",                  test_notify);
+    g_test_add_func("/aio/flush",                   test_flush);
+    g_test_add_func("/aio/bh/schedule",             test_bh_schedule);
+    g_test_add_func("/aio/bh/schedule10",           test_bh_schedule10);
+    g_test_add_func("/aio/bh/cancel",               test_bh_cancel);
+    g_test_add_func("/aio/bh/delete",               test_bh_delete);
+    g_test_add_func("/aio/bh/callback-delete/one",  test_bh_delete_from_cb);
+    g_test_add_func("/aio/bh/callback-delete/many", test_bh_delete_from_cb_many);
+    g_test_add_func("/aio/bh/flush",                test_bh_flush);
+    g_test_add_func("/aio/event/add-remove",        test_set_event_notifier);
+    g_test_add_func("/aio/event/wait",              test_wait_event_notifier);
+    g_test_add_func("/aio/event/wait/no-flush-cb",  test_wait_event_notifier_noflush);
+    g_test_add_func("/aio/event/flush",             test_flush_event_notifier);
+
+    g_test_add_func("/aio-gsource/notify",                  test_source_notify);
+    g_test_add_func("/aio-gsource/flush",                   test_source_flush);
+    g_test_add_func("/aio-gsource/bh/schedule",             test_source_bh_schedule);
+    g_test_add_func("/aio-gsource/bh/schedule10",           test_source_bh_schedule10);
+    g_test_add_func("/aio-gsource/bh/cancel",               test_source_bh_cancel);
+    g_test_add_func("/aio-gsource/bh/delete",               test_source_bh_delete);
+    g_test_add_func("/aio-gsource/bh/callback-delete/one",  test_source_bh_delete_from_cb);
+    g_test_add_func("/aio-gsource/bh/callback-delete/many", test_source_bh_delete_from_cb_many);
+    g_test_add_func("/aio-gsource/bh/flush",                test_source_bh_flush);
+    g_test_add_func("/aio-gsource/event/add-remove",        test_source_set_event_notifier);
+    g_test_add_func("/aio-gsource/event/wait",              test_source_wait_event_notifier);
+    g_test_add_func("/aio-gsource/event/wait/no-flush-cb",  test_source_wait_event_notifier_noflush);
+    g_test_add_func("/aio-gsource/event/flush",             test_source_flush_event_notifier);
+    return g_test_run();
+}
diff --git a/tests/test-thread-pool.c b/tests/test-thread-pool.c
new file mode 100644
index 0000000000..fea0445fb4
--- /dev/null
+++ b/tests/test-thread-pool.c
@@ -0,0 +1,216 @@
+#include <glib.h>
+#include "qemu-common.h"
+#include "qemu-aio.h"
+#include "thread-pool.h"
+#include "block.h"
+
+static int active;
+
+typedef struct {
+    BlockDriverAIOCB *aiocb;
+    int n;
+    int ret;
+} WorkerTestData;
+
+static int worker_cb(void *opaque)
+{
+    WorkerTestData *data = opaque;
+    return __sync_fetch_and_add(&data->n, 1);
+}
+
+static int long_cb(void *opaque)
+{
+    WorkerTestData *data = opaque;
+    __sync_fetch_and_add(&data->n, 1);
+    g_usleep(2000000);
+    __sync_fetch_and_add(&data->n, 1);
+    return 0;
+}
+
+static void done_cb(void *opaque, int ret)
+{
+    WorkerTestData *data = opaque;
+    g_assert_cmpint(data->ret, ==, -EINPROGRESS);
+    data->ret = ret;
+    data->aiocb = NULL;
+
+    /* Callbacks are serialized, so no need to use atomic ops.  */
+    active--;
+}
+
+/* A non-blocking poll of the main AIO context (we cannot use aio_poll
+ * because we do not know the AioContext).
+ */
+static void qemu_aio_wait_nonblocking(void)
+{
+    qemu_notify_event();
+    qemu_aio_wait();
+}
+
+static void test_submit(void)
+{
+    WorkerTestData data = { .n = 0 };
+    thread_pool_submit(worker_cb, &data);
+    qemu_aio_flush();
+    g_assert_cmpint(data.n, ==, 1);
+}
+
+static void test_submit_aio(void)
+{
+    WorkerTestData data = { .n = 0, .ret = -EINPROGRESS };
+    data.aiocb = thread_pool_submit_aio(worker_cb, &data, done_cb, &data);
+
+    /* The callbacks are not called until after the first wait.  */
+    active = 1;
+    g_assert_cmpint(data.ret, ==, -EINPROGRESS);
+    qemu_aio_flush();
+    g_assert_cmpint(active, ==, 0);
+    g_assert_cmpint(data.n, ==, 1);
+    g_assert_cmpint(data.ret, ==, 0);
+}
+
+static void co_test_cb(void *opaque)
+{
+    WorkerTestData *data = opaque;
+
+    active = 1;
+    data->n = 0;
+    data->ret = -EINPROGRESS;
+    thread_pool_submit_co(worker_cb, data);
+
+    /* The test continues in test_submit_co, after qemu_coroutine_enter... */
+
+    g_assert_cmpint(data->n, ==, 1);
+    data->ret = 0;
+    active--;
+
+    /* The test continues in test_submit_co, after qemu_aio_flush... */
+}
+
+static void test_submit_co(void)
+{
+    WorkerTestData data;
+    Coroutine *co = qemu_coroutine_create(co_test_cb);
+
+    qemu_coroutine_enter(co, &data);
+
+    /* Back here once the worker has started.  */
+
+    g_assert_cmpint(active, ==, 1);
+    g_assert_cmpint(data.ret, ==, -EINPROGRESS);
+
+    /* qemu_aio_flush will execute the rest of the coroutine.  */
+
+    qemu_aio_flush();
+
+    /* Back here after the coroutine has finished.  */
+
+    g_assert_cmpint(active, ==, 0);
+    g_assert_cmpint(data.ret, ==, 0);
+}
+
+static void test_submit_many(void)
+{
+    WorkerTestData data[100];
+    int i;
+
+    /* Start more work items than there will be threads.  */
+    for (i = 0; i < 100; i++) {
+        data[i].n = 0;
+        data[i].ret = -EINPROGRESS;
+        thread_pool_submit_aio(worker_cb, &data[i], done_cb, &data[i]);
+    }
+
+    active = 100;
+    while (active > 0) {
+        qemu_aio_wait();
+    }
+    for (i = 0; i < 100; i++) {
+        g_assert_cmpint(data[i].n, ==, 1);
+        g_assert_cmpint(data[i].ret, ==, 0);
+    }
+}
+
+static void test_cancel(void)
+{
+    WorkerTestData data[100];
+    int num_canceled;
+    int i;
+
+    /* Start more work items than there will be threads, to ensure
+     * the pool is full.
+     */
+    test_submit_many();
+
+    /* Start long running jobs, to ensure we can cancel some.  */
+    for (i = 0; i < 100; i++) {
+        data[i].n = 0;
+        data[i].ret = -EINPROGRESS;
+        data[i].aiocb = thread_pool_submit_aio(long_cb, &data[i],
+                                               done_cb, &data[i]);
+    }
+
+    /* Starting the threads may be left to a bottom half.  Let it
+     * run, but do not waste too much time...
+     */
+    active = 100;
+    qemu_aio_wait_nonblocking();
+
+    /* Wait some time for the threads to start, with some sanity
+     * testing on the behavior of the scheduler...
+     */
+    g_assert_cmpint(active, ==, 100);
+    g_usleep(1000000);
+    g_assert_cmpint(active, >, 50);
+
+    /* Cancel the jobs that haven't been started yet.  */
+    num_canceled = 0;
+    for (i = 0; i < 100; i++) {
+        if (__sync_val_compare_and_swap(&data[i].n, 0, 3) == 0) {
+            data[i].ret = -ECANCELED;
+            bdrv_aio_cancel(data[i].aiocb);
+            active--;
+            num_canceled++;
+        }
+    }
+    g_assert_cmpint(active, >, 0);
+    g_assert_cmpint(num_canceled, <, 100);
+
+    /* Canceling the others will be a blocking operation.  */
+    for (i = 0; i < 100; i++) {
+        if (data[i].n != 3) {
+            bdrv_aio_cancel(data[i].aiocb);
+        }
+    }
+
+    /* Finish execution and execute any remaining callbacks.  */
+    qemu_aio_flush();
+    g_assert_cmpint(active, ==, 0);
+    for (i = 0; i < 100; i++) {
+        if (data[i].n == 3) {
+            g_assert_cmpint(data[i].ret, ==, -ECANCELED);
+            g_assert(data[i].aiocb != NULL);
+        } else {
+            g_assert_cmpint(data[i].n, ==, 2);
+            g_assert_cmpint(data[i].ret, ==, 0);
+            g_assert(data[i].aiocb == NULL);
+        }
+    }
+}
+
+int main(int argc, char **argv)
+{
+    /* These should be removed once each AioContext has its thread pool.
+     * The test should create its own AioContext.
+     */
+    qemu_init_main_loop();
+    bdrv_init();
+
+    g_test_init(&argc, &argv, NULL);
+    g_test_add_func("/thread-pool/submit", test_submit);
+    g_test_add_func("/thread-pool/submit-aio", test_submit_aio);
+    g_test_add_func("/thread-pool/submit-co", test_submit_co);
+    g_test_add_func("/thread-pool/submit-many", test_submit_many);
+    g_test_add_func("/thread-pool/cancel", test_cancel);
+    return g_test_run();
+}
diff --git a/vl.c b/vl.c
index c8e9c782d6..a3ab3841a7 100644
--- a/vl.c
+++ b/vl.c
@@ -3273,16 +3273,12 @@ int main(int argc, char **argv, char **envp)
                 break;
             }
             case QEMU_OPTION_usb:
-                machine_opts = qemu_opts_find(qemu_find_opts("machine"), 0);
-                if (machine_opts) {
-                    qemu_opt_set_bool(machine_opts, "usb", true);
-                }
+                olist = qemu_find_opts("machine");
+                qemu_opts_parse(olist, "usb=on", 0);
                 break;
             case QEMU_OPTION_usbdevice:
-                machine_opts = qemu_opts_find(qemu_find_opts("machine"), 0);
-                if (machine_opts) {
-                    qemu_opt_set_bool(machine_opts, "usb", true);
-                }
+                olist = qemu_find_opts("machine");
+                qemu_opts_parse(olist, "usb=on", 0);
                 add_device_config(DEV_USB, optarg);
                 break;
             case QEMU_OPTION_device: