aboutsummaryrefslogtreecommitdiff
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--target-i386/cpu.c7
-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
41 files changed, 3414 insertions, 250 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/target-i386/cpu.c b/target-i386/cpu.c
index 64c34910a0..c6c2ca03a1 100644
--- a/target-i386/cpu.c
+++ b/target-i386/cpu.c
@@ -315,7 +315,7 @@ typedef struct x86_def_t {
/* missing:
CPUID_VME, CPUID_DTS, CPUID_SS, CPUID_HT, CPUID_TM, CPUID_PBE */
#define TCG_EXT_FEATURES (CPUID_EXT_SSE3 | CPUID_EXT_MONITOR | \
- CPUID_EXT_CX16 | CPUID_EXT_POPCNT | \
+ CPUID_EXT_SSSE3 | CPUID_EXT_CX16 | CPUID_EXT_POPCNT | \
CPUID_EXT_HYPERVISOR)
/* missing:
CPUID_EXT_DTES64, CPUID_EXT_DSCPL, CPUID_EXT_VMX, CPUID_EXT_EST,
@@ -657,7 +657,7 @@ static x86_def_t builtin_x86_defs[] = {
.stepping = 1,
.features = CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX |
CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA |
- CPUID_PGE | CPUID_MTRR | CPUID_APIC | CPUID_CX8 |
+ CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 |
CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE |
CPUID_DE | CPUID_FP87,
.ext_features = CPUID_EXT_AVX | CPUID_EXT_XSAVE | CPUID_EXT_AES |
@@ -666,7 +666,8 @@ static x86_def_t builtin_x86_defs[] = {
CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3 |
CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_FMA | CPUID_EXT_MOVBE |
CPUID_EXT_PCID,
- .ext2_features = CPUID_EXT2_LM | CPUID_EXT2_NX | CPUID_EXT2_SYSCALL,
+ .ext2_features = CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_NX |
+ CPUID_EXT2_SYSCALL,
.ext3_features = CPUID_EXT3_LAHF_LM,
.cpuid_7_0_ebx_features = CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 |
CPUID_7_0_EBX_HLE | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP |
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();
+}