summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--hw/acpi/core.c18
-rw-r--r--hw/acpi/ich9.c24
-rw-r--r--hw/acpi/piix4.c27
-rw-r--r--hw/core/qdev.c47
-rw-r--r--hw/i386/acpi-dsdt-cpu-hotplug.dsl1
-rw-r--r--hw/i386/pc_piix.c19
-rw-r--r--hw/i386/pc_sysfw.c105
-rw-r--r--hw/i386/q35-acpi-dsdt.dsl4
-rw-r--r--hw/pci-host/piix.c11
-rw-r--r--hw/pci/pci.c42
-rw-r--r--hw/pci/pci_bridge.c2
-rw-r--r--hw/s390x/virtio-ccw.c5
-rw-r--r--hw/virtio/virtio.c2
-rw-r--r--include/hw/acpi/acpi.h8
-rw-r--r--include/hw/i386/pc.h1
-rw-r--r--include/hw/pci/pci.h1
-rw-r--r--include/hw/qdev-core.h15
-rw-r--r--tests/i440fx-test.c167
18 files changed, 359 insertions, 140 deletions
diff --git a/hw/acpi/core.c b/hw/acpi/core.c
index 58308a3406..79414b44c7 100644
--- a/hw/acpi/core.c
+++ b/hw/acpi/core.c
@@ -662,3 +662,21 @@ uint32_t acpi_gpe_ioport_readb(ACPIREGS *ar, uint32_t addr)
 
     return val;
 }
+
+void acpi_update_sci(ACPIREGS *regs, qemu_irq irq)
+{
+    int sci_level, pm1a_sts;
+
+    pm1a_sts = acpi_pm1_evt_get_sts(regs);
+
+    sci_level = ((pm1a_sts &
+                  regs->pm1.evt.en & ACPI_BITMASK_PM1_COMMON_ENABLED) != 0) ||
+                ((regs->gpe.sts[0] & regs->gpe.en[0]) != 0);
+
+    qemu_set_irq(irq, sci_level);
+
+    /* schedule a timer interruption if needed */
+    acpi_pm_tmr_update(regs,
+                       (regs->pm1.evt.en & ACPI_BITMASK_TIMER_ENABLE) &&
+                       !(pm1a_sts & ACPI_BITMASK_TIMER_STATUS));
+}
diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c
index 7e0429e0f9..30f0df8713 100644
--- a/hw/acpi/ich9.c
+++ b/hw/acpi/ich9.c
@@ -44,29 +44,10 @@ do { printf("%s "fmt, __func__, ## __VA_ARGS__); } while (0)
 #define ICH9_DEBUG(fmt, ...)    do { } while (0)
 #endif
 
-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);
+    acpi_update_sci(&pm->acpi_regs, pm->irq);
 }
 
 static uint64_t ich9_gpe_readb(void *opaque, hwaddr addr, unsigned width)
@@ -80,6 +61,7 @@ static void ich9_gpe_writeb(void *opaque, hwaddr addr, uint64_t val,
 {
     ICH9LPCPMRegs *pm = opaque;
     acpi_gpe_ioport_writeb(&pm->acpi_regs, addr, val);
+    acpi_update_sci(&pm->acpi_regs, pm->irq);
 }
 
 static const MemoryRegionOps ich9_gpe_ops = {
@@ -193,7 +175,7 @@ static void pm_reset(void *opaque)
         pm->smi_en |= ICH9_PMIO_SMI_EN_APMC_EN;
     }
 
-    pm_update_sci(pm);
+    acpi_update_sci(&pm->acpi_regs, pm->irq);
 }
 
 static void pm_powerdown_req(Notifier *n, void *opaque)
diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c
index 9e8a89c233..20353b983e 100644
--- a/hw/acpi/piix4.c
+++ b/hw/acpi/piix4.c
@@ -112,29 +112,10 @@ static void piix4_acpi_system_hot_add_init(MemoryRegion *parent,
 #define ACPI_ENABLE 0xf1
 #define ACPI_DISABLE 0xf0
 
-static void pm_update_sci(PIIX4PMState *s)
-{
-    int sci_level, pmsts;
-
-    pmsts = acpi_pm1_evt_get_sts(&s->ar);
-    sci_level = (((pmsts & s->ar.pm1.evt.en) &
-                  (ACPI_BITMASK_RT_CLOCK_ENABLE |
-                   ACPI_BITMASK_POWER_BUTTON_ENABLE |
-                   ACPI_BITMASK_GLOBAL_LOCK_ENABLE |
-                   ACPI_BITMASK_TIMER_ENABLE)) != 0) ||
-        (((s->ar.gpe.sts[0] & s->ar.gpe.en[0]) &
-          (PIIX4_PCI_HOTPLUG_STATUS | PIIX4_CPU_HOTPLUG_STATUS)) != 0);
-
-    qemu_set_irq(s->irq, sci_level);
-    /* schedule a timer interruption if needed */
-    acpi_pm_tmr_update(&s->ar, (s->ar.pm1.evt.en & ACPI_BITMASK_TIMER_ENABLE) &&
-                       !(pmsts & ACPI_BITMASK_TIMER_STATUS));
-}
-
 static void pm_tmr_timer(ACPIREGS *ar)
 {
     PIIX4PMState *s = container_of(ar, PIIX4PMState, ar);
-    pm_update_sci(s);
+    acpi_update_sci(&s->ar, s->irq);
 }
 
 static void apm_ctrl_changed(uint32_t val, void *arg)
@@ -582,7 +563,7 @@ static void gpe_writeb(void *opaque, hwaddr addr, uint64_t val,
     PIIX4PMState *s = opaque;
 
     acpi_gpe_ioport_writeb(&s->ar, addr, val);
-    pm_update_sci(s);
+    acpi_update_sci(&s->ar, s->irq);
 
     PIIX4_DPRINTF("gpe write %" HWADDR_PRIx " <== %" PRIu64 "\n", addr, val);
 }
@@ -698,7 +679,7 @@ static void piix4_cpu_hotplug_req(PIIX4PMState *s, CPUState *cpu,
     } else {
         g->sts[cpu_id / 8] &= ~(1 << (cpu_id % 8));
     }
-    pm_update_sci(s);
+    acpi_update_sci(&s->ar, s->irq);
 }
 
 static void piix4_cpu_added_req(Notifier *n, void *opaque)
@@ -772,7 +753,7 @@ static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev,
         disable_device(s, slot);
     }
 
-    pm_update_sci(s);
+    acpi_update_sci(&s->ar, s->irq);
 
     return 0;
 }
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index bbd780aad1..d6df8864dd 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -233,19 +233,19 @@ static int qbus_reset_one(BusState *bus, void *opaque)
 {
     BusClass *bc = BUS_GET_CLASS(bus);
     if (bc->reset) {
-        return bc->reset(bus);
+        bc->reset(bus);
     }
     return 0;
 }
 
 void qdev_reset_all(DeviceState *dev)
 {
-    qdev_walk_children(dev, qdev_reset_one, qbus_reset_one, NULL);
+    qdev_walk_children(dev, NULL, NULL, qdev_reset_one, qbus_reset_one, NULL);
 }
 
 void qbus_reset_all(BusState *bus)
 {
-    qbus_walk_children(bus, qdev_reset_one, qbus_reset_one, NULL);
+    qbus_walk_children(bus, NULL, NULL, qdev_reset_one, qbus_reset_one, NULL);
 }
 
 void qbus_reset_all_fn(void *opaque)
@@ -337,49 +337,70 @@ BusState *qdev_get_child_bus(DeviceState *dev, const char *name)
     return NULL;
 }
 
-int qbus_walk_children(BusState *bus, qdev_walkerfn *devfn,
-                       qbus_walkerfn *busfn, void *opaque)
+int qbus_walk_children(BusState *bus,
+                       qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn,
+                       qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn,
+                       void *opaque)
 {
     BusChild *kid;
     int err;
 
-    if (busfn) {
-        err = busfn(bus, opaque);
+    if (pre_busfn) {
+        err = pre_busfn(bus, opaque);
         if (err) {
             return err;
         }
     }
 
     QTAILQ_FOREACH(kid, &bus->children, sibling) {
-        err = qdev_walk_children(kid->child, devfn, busfn, opaque);
+        err = qdev_walk_children(kid->child,
+                                 pre_devfn, pre_busfn,
+                                 post_devfn, post_busfn, opaque);
         if (err < 0) {
             return err;
         }
     }
 
+    if (post_busfn) {
+        err = post_busfn(bus, opaque);
+        if (err) {
+            return err;
+        }
+    }
+
     return 0;
 }
 
-int qdev_walk_children(DeviceState *dev, qdev_walkerfn *devfn,
-                       qbus_walkerfn *busfn, void *opaque)
+int qdev_walk_children(DeviceState *dev,
+                       qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn,
+                       qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn,
+                       void *opaque)
 {
     BusState *bus;
     int err;
 
-    if (devfn) {
-        err = devfn(dev, opaque);
+    if (pre_devfn) {
+        err = pre_devfn(dev, opaque);
         if (err) {
             return err;
         }
     }
 
     QLIST_FOREACH(bus, &dev->child_bus, sibling) {
-        err = qbus_walk_children(bus, devfn, busfn, opaque);
+        err = qbus_walk_children(bus, pre_devfn, pre_busfn,
+                                 post_devfn, post_busfn, opaque);
         if (err < 0) {
             return err;
         }
     }
 
+    if (post_devfn) {
+        err = post_devfn(dev, opaque);
+        if (err) {
+            return err;
+        }
+    }
+
     return 0;
 }
 
diff --git a/hw/i386/acpi-dsdt-cpu-hotplug.dsl b/hw/i386/acpi-dsdt-cpu-hotplug.dsl
index c96ac42a31..995b415bae 100644
--- a/hw/i386/acpi-dsdt-cpu-hotplug.dsl
+++ b/hw/i386/acpi-dsdt-cpu-hotplug.dsl
@@ -52,7 +52,6 @@ Scope(\_SB) {
         Sleep(200)
     }
 
-    /* CPU hotplug notify method */
     OperationRegion(PRST, SystemIO, 0xaf00, 32)
     Field(PRST, ByteAcc, NoLock, Preserve) {
         PRS, 256
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index 4e0dae7981..276641436e 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -61,6 +61,11 @@ static const int ide_irq[MAX_IDE_BUS] = { 14, 15 };
 static bool has_pci_info;
 static bool has_acpi_build = true;
 static bool smbios_type1_defaults = true;
+/* Make sure that guest addresses aligned at 1Gbyte boundaries get mapped to
+ * host addresses aligned at 1Gbyte boundaries.  This way we can use 1GByte
+ * pages in the host.
+ */
+static bool gigabyte_align = true;
 
 /* PC hardware initialisation */
 static void pc_init1(QEMUMachineInitArgs *args,
@@ -106,9 +111,17 @@ static void pc_init1(QEMUMachineInitArgs *args,
         kvmclock_create();
     }
 
+    /* Check whether RAM fits below 4G (leaving 1/2 GByte for IO memory).
+     * If it doesn't, we need to split it in chunks below and above 4G.
+     * In any case, try to make sure that guest addresses aligned at
+     * 1G boundaries get mapped to host addresses aligned at 1G boundaries.
+     * For old machine types, use whatever split we used historically to avoid
+     * breaking migration.
+     */
     if (args->ram_size >= 0xe0000000) {
-        above_4g_mem_size = args->ram_size - 0xe0000000;
-        below_4g_mem_size = 0xe0000000;
+        ram_addr_t lowmem = gigabyte_align ? 0xc0000000 : 0xe0000000;
+        above_4g_mem_size = args->ram_size - lowmem;
+        below_4g_mem_size = lowmem;
     } else {
         above_4g_mem_size = 0;
         below_4g_mem_size = args->ram_size;
@@ -157,6 +170,7 @@ static void pc_init1(QEMUMachineInitArgs *args,
     if (pci_enabled) {
         pci_bus = i440fx_init(&i440fx_state, &piix3_devfn, &isa_bus, gsi,
                               system_memory, system_io, args->ram_size,
+                              below_4g_mem_size,
                               above_4g_mem_size,
                               pci_memory, ram_memory);
     } else {
@@ -245,6 +259,7 @@ static void pc_init_pci(QEMUMachineInitArgs *args)
 static void pc_compat_1_7(QEMUMachineInitArgs *args)
 {
     smbios_type1_defaults = false;
+    gigabyte_align = false;
 }
 
 static void pc_compat_1_6(QEMUMachineInitArgs *args)
diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c
index e917c83540..75a7ebbaa7 100644
--- a/hw/i386/pc_sysfw.c
+++ b/hw/i386/pc_sysfw.c
@@ -72,35 +72,102 @@ static void pc_isa_bios_init(MemoryRegion *rom_memory,
     memory_region_set_readonly(isa_bios, true);
 }
 
-static void pc_system_flash_init(MemoryRegion *rom_memory,
-                                 DriveInfo *pflash_drv)
+#define FLASH_MAP_UNIT_MAX 2
+
+/* We don't have a theoretically justifiable exact lower bound on the base
+ * address of any flash mapping. In practice, the IO-APIC MMIO range is
+ * [0xFEE00000..0xFEE01000[ -- see IO_APIC_DEFAULT_ADDRESS --, leaving free
+ * only 18MB-4KB below 4G. For now, restrict the cumulative mapping to 8MB in
+ * size.
+ */
+#define FLASH_MAP_BASE_MIN ((hwaddr)(0x100000000ULL - 8*1024*1024))
+
+/* This function maps flash drives from 4G downward, in order of their unit
+ * numbers. The mapping starts at unit#0, with unit number increments of 1, and
+ * stops before the first missing flash drive, or before
+ * unit#FLASH_MAP_UNIT_MAX, whichever is reached first.
+ *
+ * Addressing within one flash drive is of course not reversed.
+ *
+ * An error message is printed and the process exits if:
+ * - the size of the backing file for a flash drive is non-positive, or not a
+ *   multiple of the required sector size, or
+ * - the current mapping's base address would fall below FLASH_MAP_BASE_MIN.
+ *
+ * The drive with unit#0 (if available) is mapped at the highest address, and
+ * it is passed to pc_isa_bios_init(). Merging several drives for isa-bios is
+ * not supported.
+ */
+static void pc_system_flash_init(MemoryRegion *rom_memory)
 {
+    int unit;
+    DriveInfo *pflash_drv;
     BlockDriverState *bdrv;
     int64_t size;
-    hwaddr phys_addr;
+    char *fatal_errmsg = NULL;
+    hwaddr phys_addr = 0x100000000ULL;
     int sector_bits, sector_size;
     pflash_t *system_flash;
     MemoryRegion *flash_mem;
+    char name[64];
 
-    bdrv = pflash_drv->bdrv;
-    size = bdrv_getlength(pflash_drv->bdrv);
     sector_bits = 12;
     sector_size = 1 << sector_bits;
 
-    if ((size % sector_size) != 0) {
-        fprintf(stderr,
-                "qemu: PC system firmware (pflash) must be a multiple of 0x%x\n",
-                sector_size);
-        exit(1);
+    for (unit = 0;
+         (unit < FLASH_MAP_UNIT_MAX &&
+          (pflash_drv = drive_get(IF_PFLASH, 0, unit)) != NULL);
+         ++unit) {
+        bdrv = pflash_drv->bdrv;
+        size = bdrv_getlength(bdrv);
+        if (size < 0) {
+            fatal_errmsg = g_strdup_printf("failed to get backing file size");
+        } else if (size == 0) {
+            fatal_errmsg = g_strdup_printf("PC system firmware (pflash) "
+                               "cannot have zero size");
+        } else if ((size % sector_size) != 0) {
+            fatal_errmsg = g_strdup_printf("PC system firmware (pflash) "
+                               "must be a multiple of 0x%x", sector_size);
+        } else if (phys_addr < size || phys_addr - size < FLASH_MAP_BASE_MIN) {
+            fatal_errmsg = g_strdup_printf("oversized backing file, pflash "
+                               "segments cannot be mapped under "
+                               TARGET_FMT_plx, FLASH_MAP_BASE_MIN);
+        }
+        if (fatal_errmsg != NULL) {
+            Location loc;
+
+            /* push a new, "none" location on the location stack; overwrite its
+             * contents with the location saved in the option; print the error
+             * (includes location); pop the top
+             */
+            loc_push_none(&loc);
+            if (pflash_drv->opts != NULL) {
+                qemu_opts_loc_restore(pflash_drv->opts);
+            }
+            error_report("%s", fatal_errmsg);
+            loc_pop(&loc);
+            g_free(fatal_errmsg);
+            exit(1);
+        }
+
+        phys_addr -= size;
+
+        /* pflash_cfi01_register() creates a deep copy of the name */
+        snprintf(name, sizeof name, "system.flash%d", unit);
+        system_flash = pflash_cfi01_register(phys_addr, NULL /* qdev */, name,
+                                             size, bdrv, sector_size,
+                                             size >> sector_bits,
+                                             1      /* width */,
+                                             0x0000 /* id0 */,
+                                             0x0000 /* id1 */,
+                                             0x0000 /* id2 */,
+                                             0x0000 /* id3 */,
+                                             0      /* be */);
+        if (unit == 0) {
+            flash_mem = pflash_cfi01_get_memory(system_flash);
+            pc_isa_bios_init(rom_memory, flash_mem, size);
+        }
     }
-
-    phys_addr = 0x100000000ULL - size;
-    system_flash = pflash_cfi01_register(phys_addr, NULL, "system.flash", size,
-                                         bdrv, sector_size, size >> sector_bits,
-                                         1, 0x0000, 0x0000, 0x0000, 0x0000, 0);
-    flash_mem = pflash_cfi01_get_memory(system_flash);
-
-    pc_isa_bios_init(rom_memory, flash_mem, size);
 }
 
 static void old_pc_system_rom_init(MemoryRegion *rom_memory, bool isapc_ram_fw)
@@ -181,5 +248,5 @@ void pc_system_firmware_init(MemoryRegion *rom_memory, bool isapc_ram_fw)
         exit(1);
     }
 
-    pc_system_flash_init(rom_memory, pflash_drv);
+    pc_system_flash_init(rom_memory);
 }
diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl
index 575c5d7376..7934a9ddfb 100644
--- a/hw/i386/q35-acpi-dsdt.dsl
+++ b/hw/i386/q35-acpi-dsdt.dsl
@@ -417,11 +417,11 @@ DefinitionBlock (
         Method(_L00) {
         }
         Method(_L01) {
+        }
+        Method(_E02) {
             // CPU hotplug event
             \_SB.PRSC()
         }
-        Method(_L02) {
-        }
         Method(_L03) {
         }
         Method(_L04) {
diff --git a/hw/pci-host/piix.c b/hw/pci-host/piix.c
index cc97150a90..e89d5c1dfa 100644
--- a/hw/pci-host/piix.c
+++ b/hw/pci-host/piix.c
@@ -311,6 +311,7 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
                     MemoryRegion *address_space_mem,
                     MemoryRegion *address_space_io,
                     ram_addr_t ram_size,
+                    ram_addr_t below_4g_mem_size,
                     ram_addr_t above_4g_mem_size,
                     MemoryRegion *pci_address_space,
                     MemoryRegion *ram_memory)
@@ -340,15 +341,7 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
     f->ram_memory = ram_memory;
 
     i440fx = I440FX_PCI_HOST_BRIDGE(dev);
-    /* Set PCI window size the way seabios has always done it. */
-    /* Power of 2 so bios can cover it with a single MTRR */
-    if (ram_size <= 0x80000000) {
-        i440fx->pci_info.w32.begin = 0x80000000;
-    } else if (ram_size <= 0xc0000000) {
-        i440fx->pci_info.w32.begin = 0xc0000000;
-    } else {
-        i440fx->pci_info.w32.begin = 0xe0000000;
-    }
+    i440fx->pci_info.w32.begin = below_4g_mem_size;
 
     /* setup pci memory mapping */
     pc_pci_as_mapping_init(OBJECT(f), f->system_memory,
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index 82c11ecde4..aa2a395499 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -46,7 +46,7 @@
 static void pcibus_dev_print(Monitor *mon, DeviceState *dev, int indent);
 static char *pcibus_get_dev_path(DeviceState *dev);
 static char *pcibus_get_fw_dev_path(DeviceState *dev);
-static int pcibus_reset(BusState *qbus);
+static void pcibus_reset(BusState *qbus);
 static void pci_bus_finalize(Object *obj);
 
 static Property pci_props[] = {
@@ -167,16 +167,10 @@ void pci_device_deassert_intx(PCIDevice *dev)
     }
 }
 
-/*
- * This function is called on #RST and FLR.
- * FLR if PCI_EXP_DEVCTL_BCR_FLR is set
- */
-void pci_device_reset(PCIDevice *dev)
+static void pci_do_device_reset(PCIDevice *dev)
 {
     int r;
 
-    qdev_reset_all(&dev->qdev);
-
     dev->irq_state = 0;
     pci_update_irq_status(dev);
     pci_device_deassert_intx(dev);
@@ -209,30 +203,34 @@ void pci_device_reset(PCIDevice *dev)
 }
 
 /*
+ * This function is called on #RST and FLR.
+ * FLR if PCI_EXP_DEVCTL_BCR_FLR is set
+ */
+void pci_device_reset(PCIDevice *dev)
+{
+    qdev_reset_all(&dev->qdev);
+    pci_do_device_reset(dev);
+}
+
+/*
  * Trigger pci bus reset under a given bus.
- * To be called on RST# assert.
+ * Called via qbus_reset_all on RST# assert, after the devices
+ * have been reset qdev_reset_all-ed already.
  */
-void pci_bus_reset(PCIBus *bus)
+static void pcibus_reset(BusState *qbus)
 {
+    PCIBus *bus = DO_UPCAST(PCIBus, qbus, qbus);
     int i;
 
-    for (i = 0; i < bus->nirq; i++) {
-        bus->irq_count[i] = 0;
-    }
     for (i = 0; i < ARRAY_SIZE(bus->devices); ++i) {
         if (bus->devices[i]) {
-            pci_device_reset(bus->devices[i]);
+            pci_do_device_reset(bus->devices[i]);
         }
     }
-}
-
-static int pcibus_reset(BusState *qbus)
-{
-    pci_bus_reset(DO_UPCAST(PCIBus, qbus, qbus));
 
-    /* topology traverse is done by pci_bus_reset().
-       Tell qbus/qdev walker not to traverse the tree */
-    return 1;
+    for (i = 0; i < bus->nirq; i++) {
+        assert(bus->irq_count[i] == 0);
+    }
 }
 
 static void pci_host_bus_register(PCIBus *bus, DeviceState *parent)
diff --git a/hw/pci/pci_bridge.c b/hw/pci/pci_bridge.c
index 355c3e1b06..4becdc14b8 100644
--- a/hw/pci/pci_bridge.c
+++ b/hw/pci/pci_bridge.c
@@ -268,7 +268,7 @@ void pci_bridge_write_config(PCIDevice *d,
     newctl = pci_get_word(d->config + PCI_BRIDGE_CONTROL);
     if (~oldctl & newctl & PCI_BRIDGE_CTL_BUS_RESET) {
         /* Trigger hot reset on 0->1 transition. */
-        pci_bus_reset(&s->sec_bus);
+        qbus_reset_all(&s->sec_bus.qbus);
     }
 }
 
diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c
index 4b6250dda9..bc8871249d 100644
--- a/hw/s390x/virtio-ccw.c
+++ b/hw/s390x/virtio-ccw.c
@@ -30,13 +30,10 @@
 static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size,
                                VirtioCcwDevice *dev);
 
-static int virtual_css_bus_reset(BusState *qbus)
+static void virtual_css_bus_reset(BusState *qbus)
 {
     /* This should actually be modelled via the generic css */
     css_reset();
-
-    /* we dont traverse ourself, return 0 */
-    return 0;
 }
 
 
diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index 144b9ca2ef..a001e668c4 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -1172,6 +1172,8 @@ static void virtio_device_unrealize(DeviceState *dev, Error **errp)
     VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(dev);
     Error *err = NULL;
 
+    virtio_bus_device_unplugged(vdev);
+
     if (vdc->unrealize != NULL) {
         vdc->unrealize(dev, &err);
         if (err != NULL) {
diff --git a/include/hw/acpi/acpi.h b/include/hw/acpi/acpi.h
index 6bbcb1750d..3e53297a99 100644
--- a/include/hw/acpi/acpi.h
+++ b/include/hw/acpi/acpi.h
@@ -69,6 +69,12 @@
 #define ACPI_BITMASK_RT_CLOCK_ENABLE            0x0400
 #define ACPI_BITMASK_PCIEXP_WAKE_DISABLE        0x4000	/* ACPI 3.0 */
 
+#define ACPI_BITMASK_PM1_COMMON_ENABLED         ( \
+        ACPI_BITMASK_RT_CLOCK_ENABLE        | \
+        ACPI_BITMASK_POWER_BUTTON_ENABLE    | \
+        ACPI_BITMASK_GLOBAL_LOCK_ENABLE     | \
+        ACPI_BITMASK_TIMER_ENABLE)
+
 /* PM1x_CNT */
 #define ACPI_BITMASK_SCI_ENABLE                 0x0001
 #define ACPI_BITMASK_BUS_MASTER_RLD             0x0002
@@ -160,6 +166,8 @@ void acpi_gpe_reset(ACPIREGS *ar);
 void acpi_gpe_ioport_writeb(ACPIREGS *ar, uint32_t addr, uint32_t val);
 uint32_t acpi_gpe_ioport_readb(ACPIREGS *ar, uint32_t addr);
 
+void acpi_update_sci(ACPIREGS *acpi_regs, qemu_irq irq);
+
 /* acpi.c */
 extern int acpi_enabled;
 extern char unsigned *acpi_tables;
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index 24eb3de310..eb3da964f0 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -182,6 +182,7 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix_devfn,
                     MemoryRegion *address_space_mem,
                     MemoryRegion *address_space_io,
                     ram_addr_t ram_size,
+                    ram_addr_t below_4g_mem_size,
                     ram_addr_t above_4g_mem_size,
                     MemoryRegion *pci_memory,
                     MemoryRegion *ram_memory);
diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h
index b783e68d08..754b82de81 100644
--- a/include/hw/pci/pci.h
+++ b/include/hw/pci/pci.h
@@ -373,7 +373,6 @@ void pci_bus_fire_intx_routing_notifier(PCIBus *bus);
 void pci_device_set_intx_routing_notifier(PCIDevice *dev,
                                           PCIINTxRoutingNotifier notifier);
 void pci_device_reset(PCIDevice *dev);
-void pci_bus_reset(PCIBus *bus);
 
 PCIDevice *pci_nic_init(NICInfo *nd, PCIBus *rootbus,
                         const char *default_model,
diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
index 651c3e54ee..2c4f140b9c 100644
--- a/include/hw/qdev-core.h
+++ b/include/hw/qdev-core.h
@@ -169,7 +169,7 @@ struct BusClass {
      * bindings can be found at http://playground.sun.com/1275/bindings/.
      */
     char *(*get_fw_dev_path)(DeviceState *dev);
-    int (*reset)(BusState *bus);
+    void (*reset)(BusState *bus);
     /* maximum devices allowed on the bus, 0: no limit. */
     int max_dev;
 };
@@ -264,10 +264,15 @@ BusState *qbus_create(const char *typename, DeviceState *parent, const char *nam
 /* Returns > 0 if either devfn or busfn skip walk somewhere in cursion,
  *         < 0 if either devfn or busfn terminate walk somewhere in cursion,
  *           0 otherwise. */
-int qbus_walk_children(BusState *bus, qdev_walkerfn *devfn,
-                       qbus_walkerfn *busfn, void *opaque);
-int qdev_walk_children(DeviceState *dev, qdev_walkerfn *devfn,
-                       qbus_walkerfn *busfn, void *opaque);
+int qbus_walk_children(BusState *bus,
+                       qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn,
+                       qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn,
+                       void *opaque);
+int qdev_walk_children(DeviceState *dev,
+                       qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn,
+                       qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn,
+                       void *opaque);
+
 void qdev_reset_all(DeviceState *dev);
 
 /**
diff --git a/tests/i440fx-test.c b/tests/i440fx-test.c
index 65c786ca1e..fa3e3d6b87 100644
--- a/tests/i440fx-test.c
+++ b/tests/i440fx-test.c
@@ -2,9 +2,11 @@
  * qtest I440FX test case
  *
  * Copyright IBM, Corp. 2012-2013
+ * Copyright Red Hat, Inc. 2013
  *
  * Authors:
  *  Anthony Liguori   <aliguori@us.ibm.com>
+ *  Laszlo Ersek      <lersek@redhat.com>
  *
  * This work is licensed under the terms of the GNU GPL, version 2 or later.
  * See the COPYING file in the top-level directory.
@@ -18,6 +20,11 @@
 
 #include <glib.h>
 #include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <stdlib.h>
 
 #define BROKEN 1
 
@@ -26,16 +33,32 @@
 typedef struct TestData
 {
     int num_cpus;
-    QPCIBus *bus;
 } TestData;
 
+typedef struct FirmwareTestFixture {
+    /* decides whether we're testing -bios or -pflash */
+    bool is_bios;
+} FirmwareTestFixture;
+
+static QPCIBus *test_start_get_bus(const TestData *s)
+{
+    char *cmdline;
+
+    cmdline = g_strdup_printf("-smp %d", s->num_cpus);
+    qtest_start(cmdline);
+    g_free(cmdline);
+    return qpci_init_pc();
+}
+
 static void test_i440fx_defaults(gconstpointer opaque)
 {
     const TestData *s = opaque;
+    QPCIBus *bus;
     QPCIDevice *dev;
     uint32_t value;
 
-    dev = qpci_device_find(s->bus, QPCI_DEVFN(0, 0));
+    bus = test_start_get_bus(s);
+    dev = qpci_device_find(bus, QPCI_DEVFN(0, 0));
     g_assert(dev != NULL);
 
     /* 3.2.2 */
@@ -119,6 +142,8 @@ static void test_i440fx_defaults(gconstpointer opaque)
     g_assert_cmpint(qpci_config_readb(dev, 0x91), ==, 0x00); /* ERRSTS */
     /* 3.2.26 */
     g_assert_cmpint(qpci_config_readb(dev, 0x93), ==, 0x00); /* TRC */
+
+    qtest_end();
 }
 
 #define PAM_RE 1
@@ -177,6 +202,7 @@ static void write_area(uint32_t start, uint32_t end, uint8_t value)
 static void test_i440fx_pam(gconstpointer opaque)
 {
     const TestData *s = opaque;
+    QPCIBus *bus;
     QPCIDevice *dev;
     int i;
     static struct {
@@ -199,7 +225,8 @@ static void test_i440fx_pam(gconstpointer opaque)
         { 0xEC000, 0xEFFFF }, /* BIOS Extension */
     };
 
-    dev = qpci_device_find(s->bus, QPCI_DEVFN(0, 0));
+    bus = test_start_get_bus(s);
+    dev = qpci_device_find(bus, QPCI_DEVFN(0, 0));
     g_assert(dev != NULL);
 
     for (i = 0; i < ARRAY_SIZE(pam_area); i++) {
@@ -252,34 +279,140 @@ static void test_i440fx_pam(gconstpointer opaque)
         /* Verify the area is not our new mask */
         g_assert(!verify_area(pam_area[i].start, pam_area[i].end, 0x82));
     }
+    qtest_end();
+}
+
+#define BLOB_SIZE ((size_t)65536)
+#define ISA_BIOS_MAXSZ ((size_t)(128 * 1024))
+
+/* Create a blob file, and return its absolute pathname as a dynamically
+ * allocated string.
+ * The file is closed before the function returns.
+ * In case of error, NULL is returned. The function prints the error message.
+ */
+static char *create_blob_file(void)
+{
+    int ret, fd;
+    char *pathname;
+    GError *error = NULL;
+
+    ret = -1;
+    fd = g_file_open_tmp("blob_XXXXXX", &pathname, &error);
+    if (fd == -1) {
+        fprintf(stderr, "unable to create blob file: %s\n", error->message);
+        g_error_free(error);
+    } else {
+        if (ftruncate(fd, BLOB_SIZE) == -1) {
+            fprintf(stderr, "ftruncate(\"%s\", %zu): %s\n", pathname,
+                    BLOB_SIZE, strerror(errno));
+        } else {
+            void *buf;
+
+            buf = mmap(NULL, BLOB_SIZE, PROT_WRITE, MAP_SHARED, fd, 0);
+            if (buf == MAP_FAILED) {
+                fprintf(stderr, "mmap(\"%s\", %zu): %s\n", pathname, BLOB_SIZE,
+                        strerror(errno));
+            } else {
+                size_t i;
+
+                for (i = 0; i < BLOB_SIZE; ++i) {
+                    ((uint8_t *)buf)[i] = i;
+                }
+                munmap(buf, BLOB_SIZE);
+                ret = 0;
+            }
+        }
+        close(fd);
+        if (ret == -1) {
+            unlink(pathname);
+            g_free(pathname);
+        }
+    }
+
+    return ret == -1 ? NULL : pathname;
+}
+
+static void test_i440fx_firmware(FirmwareTestFixture *fixture,
+                                 gconstpointer user_data)
+{
+    char *fw_pathname, *cmdline;
+    uint8_t *buf;
+    size_t i, isa_bios_size;
+
+    fw_pathname = create_blob_file();
+    g_assert(fw_pathname != NULL);
+
+    /* Better hope the user didn't put metacharacters in TMPDIR and co. */
+    cmdline = g_strdup_printf("-S %s %s",
+                              fixture->is_bios ? "-bios" : "-pflash",
+                              fw_pathname);
+    g_test_message("qemu cmdline: %s", cmdline);
+    qtest_start(cmdline);
+    g_free(cmdline);
+
+    /* Qemu has loaded the firmware (because qtest_start() only returns after
+     * the QMP handshake completes). We must unlink the firmware blob right
+     * here, because any assertion firing below would leak it in the
+     * filesystem. This is also the reason why we recreate the blob every time
+     * this function is invoked.
+     */
+    unlink(fw_pathname);
+    g_free(fw_pathname);
+
+    /* check below 4G */
+    buf = g_malloc0(BLOB_SIZE);
+    memread(0x100000000ULL - BLOB_SIZE, buf, BLOB_SIZE);
+    for (i = 0; i < BLOB_SIZE; ++i) {
+        g_assert_cmphex(buf[i], ==, (uint8_t)i);
+    }
+
+    /* check in ISA space too */
+    memset(buf, 0, BLOB_SIZE);
+    isa_bios_size = ISA_BIOS_MAXSZ < BLOB_SIZE ? ISA_BIOS_MAXSZ : BLOB_SIZE;
+    memread(0x100000 - isa_bios_size, buf, isa_bios_size);
+    for (i = 0; i < isa_bios_size; ++i) {
+        g_assert_cmphex(buf[i], ==,
+                        (uint8_t)((BLOB_SIZE - isa_bios_size) + i));
+    }
+
+    g_free(buf);
+    qtest_end();
+}
+
+static void add_firmware_test(const char *testpath,
+                              void (*setup_fixture)(FirmwareTestFixture *f,
+                                                    gconstpointer test_data))
+{
+    g_test_add(testpath, FirmwareTestFixture, NULL, setup_fixture,
+               test_i440fx_firmware, NULL);
+}
+
+static void request_bios(FirmwareTestFixture *fixture,
+                         gconstpointer user_data)
+{
+    fixture->is_bios = true;
+}
+
+static void request_pflash(FirmwareTestFixture *fixture,
+                           gconstpointer user_data)
+{
+    fixture->is_bios = false;
 }
 
 int main(int argc, char **argv)
 {
-    QTestState *s;
     TestData data;
-    char *cmdline;
     int ret;
 
     g_test_init(&argc, &argv, NULL);
 
     data.num_cpus = 1;
 
-    cmdline = g_strdup_printf("-smp %d", data.num_cpus);
-    s = qtest_start(cmdline);
-    g_free(cmdline);
-
-    data.bus = qpci_init_pc();
-
     g_test_add_data_func("/i440fx/defaults", &data, test_i440fx_defaults);
     g_test_add_data_func("/i440fx/pam", &data, test_i440fx_pam);
-    
+    add_firmware_test("/i440fx/firmware/bios", request_bios);
+    add_firmware_test("/i440fx/firmware/pflash", request_pflash);
 
     ret = g_test_run();
-
-    if (s) {
-        qtest_quit(s);
-    }
-
     return ret;
 }