qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH v2 12/21] q35: Introduce q35 pc based chipset emulat


From: Jason Baron
Subject: [Qemu-devel] [PATCH v2 12/21] q35: Introduce q35 pc based chipset emulator
Date: Mon, 8 Oct 2012 23:30:32 -0400

From: Isaku Yamahata <address@hidden>

pc q35 based chipset emulator to support pci express natively.

Signed-off-by: Isaku Yamahata <address@hidden>
Signed-off-by: Jason Baron <address@hidden>
---
 hw/acpi_ich9.c |  315 ++++++++++++++++++++
 hw/acpi_ich9.h |   53 ++++
 hw/pc_q35.c    |  378 ++++++++++++++++++++++++
 hw/pci_ids.h   |   14 +
 hw/q35.c       |  877 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/q35.h       |  272 ++++++++++++++++++
 hw/q35_smbus.c |  154 ++++++++++
 7 files changed, 2063 insertions(+), 0 deletions(-)
 create mode 100644 hw/acpi_ich9.c
 create mode 100644 hw/acpi_ich9.h
 create mode 100644 hw/pc_q35.c
 create mode 100644 hw/q35.c
 create mode 100644 hw/q35.h
 create mode 100644 hw/q35_smbus.c

diff --git a/hw/acpi_ich9.c b/hw/acpi_ich9.c
new file mode 100644
index 0000000..59c0807
--- /dev/null
+++ b/hw/acpi_ich9.c
@@ -0,0 +1,315 @@
+/*
+ * 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.
+ *
+ *  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 "q35.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(ICH9_LPCPmRegs *pm)
+{
+    int sci_level, pm1a_sts;
+
+    pm1a_sts = acpi_pm1_evt_get_sts(&pm->pm1a, pm->tmr.overflow_time);
+
+    sci_level = (((pm1a_sts & pm->pm1a.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->tmr,
+                       (pm->pm1a.en & ACPI_BITMASK_TIMER_ENABLE) &&
+                       !(pm1a_sts & ACPI_BITMASK_TIMER_STATUS));
+}
+
+static void ich9_pm_update_sci_fn(ACPIPMTimer *tmr)
+{
+    ICH9_LPCPmRegs *pm = container_of(tmr, ICH9_LPCPmRegs, tmr);
+    pm_update_sci(pm);
+}
+
+static void pm_ioport_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+    ICH9_LPCPmRegs *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->gpe0, 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)
+{
+    ICH9_LPCPmRegs *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->gpe0, 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)
+{
+    ICH9_LPCPmRegs *pm = opaque;
+
+    switch (addr & ICH9_PMIO_MASK) {
+    case ICH9_PMIO_PM1_STS:
+        acpi_pm1_evt_write_sts(&pm->pm1a, &pm->tmr, val);
+        pm_update_sci(pm);
+        break;
+    case ICH9_PMIO_PM1_EN:
+        pm->pm1a.en = val;
+        pm_update_sci(pm);
+        break;
+    case ICH9_PMIO_PM1_CNT:
+        acpi_pm1_cnt_write(&pm->pm1a, &pm->pm1_cnt, val);
+        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)
+{
+    ICH9_LPCPmRegs *pm = opaque;
+    uint32_t val;
+
+    switch (addr & ICH9_PMIO_MASK) {
+    case ICH9_PMIO_PM1_STS:
+        val = acpi_pm1_evt_get_sts(&pm->pm1a, pm->tmr.overflow_time);
+        break;
+    case ICH9_PMIO_PM1_EN:
+        val = pm->pm1a.en;
+        break;
+    case ICH9_PMIO_PM1_CNT:
+        val = pm->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)
+{
+    ICH9_LPCPmRegs *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)
+{
+    ICH9_LPCPmRegs *pm = opaque;
+    uint32_t val;
+
+    switch (addr & ICH9_PMIO_MASK) {
+    case ICH9_PMIO_PM1_TMR:
+        val = acpi_pm_tmr_get(&pm->tmr);
+        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(ICH9_LPCPmRegs *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->gpe0, pm_io_base + ICH9_PMIO_GPE0_STS);
+}
+
+static int ich9_pm_post_load(void *opaque, int version_id)
+{
+    ICH9_LPCPmRegs *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(pm1a.sts, ICH9_LPCPmRegs),
+        VMSTATE_UINT16(pm1a.en, ICH9_LPCPmRegs),
+        VMSTATE_UINT16(pm1_cnt.cnt, ICH9_LPCPmRegs),
+        VMSTATE_TIMER(tmr.timer, ICH9_LPCPmRegs),
+        VMSTATE_INT64(tmr.overflow_time, ICH9_LPCPmRegs),
+        VMSTATE_GPE_ARRAY(gpe0.sts, ICH9_LPCPmRegs),
+        VMSTATE_GPE_ARRAY(gpe0.en, ICH9_LPCPmRegs),
+        VMSTATE_UINT32(smi_en, ICH9_LPCPmRegs),
+        VMSTATE_UINT32(smi_sts, ICH9_LPCPmRegs),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void pm_reset(void *opaque)
+{
+    ICH9_LPCPmRegs *pm = opaque;
+    ich9_pm_iospace_update(pm, 0);
+
+    acpi_pm1_evt_reset(&pm->pm1a);
+    acpi_pm1_cnt_reset(&pm->pm1_cnt);
+    acpi_pm_tmr_reset(&pm->tmr);
+    acpi_gpe_reset(&pm->gpe0);
+
+    pm_update_sci(pm);
+}
+
+static void pm_powerdown(void *opaque, int irq, int power_failing)
+{
+    ICH9_LPCPmRegs *pm = opaque;
+    ACPIPM1EVT *pm1a = pm ? &pm->pm1a : NULL;
+    ACPIPMTimer *tmr = pm ? &pm->tmr : NULL;
+
+    acpi_pm1_evt_power_down(pm1a, tmr);
+}
+
+void ich9_pm_init(ICH9_LPCPmRegs *pm, qemu_irq sci_irq, qemu_irq cmos_s3)
+{
+    acpi_pm_tmr_init(&pm->tmr, ich9_pm_update_sci_fn);
+    acpi_pm1_cnt_init(&pm->pm1_cnt, cmos_s3);
+    acpi_gpe_init(&pm->gpe0, ICH9_PMIO_GPE0_LEN);
+
+    pm->irq = sci_irq;
+    qemu_register_reset(pm_reset, pm);
+    qemu_system_powerdown = *qemu_allocate_irqs(pm_powerdown, pm, 1);
+}
diff --git a/hw/acpi_ich9.h b/hw/acpi_ich9.h
new file mode 100644
index 0000000..f55c0e9
--- /dev/null
+++ b/hw/acpi_ich9.h
@@ -0,0 +1,53 @@
+/*
+ * 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 ICH9_LPCPmRegs {
+    ACPIPM1EVT pm1a;
+
+    /*
+     * 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.
+     */
+    ACPIPM1CNT pm1_cnt;
+
+    ACPIPMTimer tmr;
+
+    ACPIGPE gpe0;
+
+    uint32_t smi_en;
+    uint32_t smi_sts;
+
+    qemu_irq irq;      /* SCI */
+
+    uint32_t pm_io_base;
+} ICH9_LPCPmRegs;
+
+void ich9_pm_init(ICH9_LPCPmRegs *pm,
+                  qemu_irq sci_irq, qemu_irq cmos_s3_resume);
+void ich9_pm_iospace_update(ICH9_LPCPmRegs *pm, uint32_t pm_io_base);
+extern const VMStateDescription vmstate_ich9_pm;
+
+#endif /* HW_ACPI_ICH9_H */
diff --git a/hw/pc_q35.c b/hw/pc_q35.c
new file mode 100644
index 0000000..4f75d97
--- /dev/null
+++ b/hw/pc_q35.c
@@ -0,0 +1,378 @@
+/*
+ * QEMU PC System Emulator
+ *
+ * Copyright (c) 2003-2004 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.
+ */
+/*
+ *  Q35 chipset based pc system emulator
+ *
+ *  Copyright (c) 2009, 2010
+ *                     Isaku Yamahata <yamahata at valinux co jp>
+ *                     VA Linux Systems Japan K.K.
+ *
+ *  This is based on pc.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 "hw.h"
+#include "arch_init.h"
+#include "pc.h"
+#include "fdc.h"
+#include "pci.h"
+#include "pci_bridge.h"
+#include "pci_p2pbr.h"
+#include "ioh3420.h"
+#include "xio3130_upstream.h"
+#include "xio3130_downstream.h"
+#include "block.h"
+#include "blockdev.h"
+#include "sysemu.h"
+#include "audio/audio.h"
+#include "net.h"
+#include "smbus.h"
+#include "boards.h"
+#include "monitor.h"
+#include "fw_cfg.h"
+#include "hpet_emul.h"
+#include "watchdog.h"
+#include "smbios.h"
+#include "ide.h"
+#include "usb-uhci.h"
+
+#include "q35.h"
+
+/* ICH9 AHCI has 6 ports */
+#define MAX_SATA_PORTS     6
+
+#define I21154_REV            0x05
+#define I21154_PI             0x00
+
+static PCIBridge *i21154_init(PCIBus *bus, int devfn, const char *bus_name,
+                              bool multifunction)
+{
+    const PCIP2PBridgeInit init = {
+        .bus = bus,
+        .devfn = devfn,
+        .multifunction = multifunction,
+
+        .bus_name = bus_name,
+        .map_irq = pci_swizzle_map_irq_fn,
+    };
+    const PCIP2PBridgeProp prop = {
+        .vendor_id = PCI_VENDOR_ID_DEC,
+        .device_id = PCI_DEVICE_ID_DEC_21154,
+        .revision_id = I21154_REV,
+        .prog_interface = I21154_PI,
+    };
+    return pci_p2pbr_create_simple(&init, &prop);
+}
+
+static void pc_q35_bridge_init(PCIBus *host_bus, PCIBus *pci_bus)
+{
+    uint8_t dev;
+    uint8_t sec_bus;
+    uint8_t port = 0;
+    uint8_t chassis = 0;
+    uint16_t slot = 0;
+    uint8_t upstream_port;
+    PCIESlot *s;
+    uint8_t fn;
+    PCIESlot *root_port;
+    PCIBus *root_port_bus;
+    char buf[16];
+
+    /* PCI to PCI bridge b6:d[29 - 31]:f0, 6:[1c - 1f].0 with subordinate bus
+       of 7 - 9 on b0:d30:f0, 0.1e.0 = bus */
+#define Q35_P2P_BRDIGE_DEV_BASE         28
+#define Q35_P2P_BRDIGE_DEV_MAX          32
+#define Q35_P2P_BRDIGE_SUBBUS_BASE      (ICH9_D2P_SECONDARY_DEFAULT + 1)
+    for (dev = Q35_P2P_BRDIGE_DEV_BASE; dev < Q35_P2P_BRDIGE_DEV_MAX; dev++) {
+        PCIBridge *br;
+        sec_bus = Q35_P2P_BRDIGE_SUBBUS_BASE + dev - Q35_P2P_BRDIGE_DEV_BASE;
+
+        snprintf(buf, sizeof(buf), "pci.%d", sec_bus);
+        br = i21154_init(pci_bus, PCI_DEVFN(dev, 0), buf, true);
+    }
+
+    /* PCIe root port b0:d1:f0 in GMCH.
+     * Actually it's vid/did = 0x8086:0x29c1, but we substitute ioh for it.
+     */
+    sec_bus = 32;
+    snprintf(buf, sizeof(buf), "pcie.%d", sec_bus);
+    s = ioh3420_init(host_bus, PCI_DEVFN(GMCH_PCIE_DEV, GMCH_PCIE_FUNC), true,
+                     buf, pci_swizzle_map_irq_fn, port, chassis, slot);
+
+
+    /* more slots. ICH9 doesn't have those, but many slots are wanted. */
+//#define Q35_MANY_SLOTS
+#undef Q35_MANY_SLOTS
+
+#ifdef Q35_MANY_SLOTS
+#define Q35_NR_ROOTPORT         6
+#define Q35_NR_UPSTREAM         8
+#define Q35_NR_DOWNSTREAM       16
+#else
+#define Q35_NR_ROOTPORT         1
+#define Q35_NR_UPSTREAM         1
+#define Q35_NR_DOWNSTREAM       1
+#endif
+
+    /* PCIe root port b0:d23:f[0-5], 0.17.[0-5] */
+    for (fn = 0; fn < Q35_NR_ROOTPORT; fn++) {
+        sec_bus++;
+        port++;
+        slot++;
+
+        snprintf(buf, sizeof(buf), "pcie.%d", sec_bus);
+        s = ioh3420_init(host_bus, PCI_DEVFN(23, fn), true,
+                         buf, pci_swizzle_map_irq_fn, port, chassis, slot);
+    }
+
+    /* PCIe root port b0:d24:f0 */
+    sec_bus++;
+    port++;
+    slot++;
+    snprintf(buf, sizeof(buf), "pcie.%d", sec_bus);
+    root_port = ioh3420_init(host_bus, PCI_DEVFN(24, 0), true,
+                             buf, pci_swizzle_map_irq_fn, port, chassis, slot);
+    root_port_bus = pci_bridge_get_sec_bus(&root_port->port.br);
+
+    /* 8 * 16 = 128 slots */
+    upstream_port = 0;
+    for (fn = 0; fn < Q35_NR_UPSTREAM; fn++) {
+        PCIEPort *upstream;
+        PCIBus *upstream_bus;
+        uint16_t downstream_port;
+
+        uint8_t ds_dev_max;
+        uint8_t ds_dev;
+        uint8_t ds_fn_max;
+        uint8_t ds_fn;
+
+        /* PCIe upstream port d0:f[0-7] */
+        sec_bus++;
+        snprintf(buf, sizeof(buf), "pcie.%d", sec_bus);
+        upstream = xio3130_upstream_init(root_port_bus, PCI_DEVFN(0, fn),
+                                         true, buf, pci_swizzle_map_irq_fn,
+                                         upstream_port);
+
+        upstream_bus = pci_bridge_get_sec_bus(&upstream->br);
+        upstream_port++;
+
+        /* PCIe downstream port */
+        downstream_port = 0;
+        ds_fn_max = MIN(Q35_NR_DOWNSTREAM / PCI_SLOT_MAX, PCI_FUNC_MAX);
+        ds_dev_max = MIN(Q35_NR_DOWNSTREAM / (ds_fn_max + 1), PCI_SLOT_MAX);
+
+        for (ds_dev = 0; ds_dev <= ds_dev_max &&
+                 downstream_port < Q35_NR_DOWNSTREAM; ds_dev++) {
+            for (ds_fn = 0; ds_fn <= ds_fn_max &&
+                     downstream_port < Q35_NR_DOWNSTREAM; ds_fn++) {
+                sec_bus++;
+                slot++;
+                snprintf(buf, sizeof(buf), "pcie.%d", sec_bus);
+
+                xio3130_downstream_init(upstream_bus, PCI_DEVFN(ds_dev, ds_fn),
+                                        true, buf, pci_swizzle_map_irq_fn,
+                                        downstream_port, chassis, slot);
+                downstream_port++;
+            }
+        }
+    }
+
+    /* PCIe root port b0:d28:f[0-6] in ICH9.
+     * Actually it's vid/did = 0x8086:0x294[02468A], but we substitute ioh
+     * for them.
+     */
+    for (fn = 0; fn < ICH9_PCIE_FUNC_MAX; fn++) {
+        sec_bus++;
+        port++;
+        slot++;
+
+        snprintf(buf, sizeof(buf), "pcie.%d", sec_bus);
+        s = ioh3420_init(host_bus, PCI_DEVFN(ICH9_PCIE_DEV, fn), true,
+                         buf, pci_swizzle_map_irq_fn,
+                         port, chassis, slot);
+    }
+}
+
+static void pc_q35_init_early(qemu_irq *isa_irq, IsaIrqState *isa_irq_state,
+                              DeviceState **gmch_host_p,
+                              PCIBus **host_bus_p, PCIBus **pci_bus_p,
+                              PCIDevice **lpc_p)
+{
+    DeviceState *gmch_host;
+    PCIBus *host_bus;
+    PCIBus *pci_bus;
+
+    PCIDevice *gmch_state;
+    PCIDevice *lpc;
+
+    /* create pci host bus */
+    host_bus = gmch_host_init(&gmch_host, isa_irq, isa_irq_state->ioapic);
+    gmch_state = gmch_init(gmch_host, host_bus);
+
+    /* create conventional pci bus: pcie2pci bridge */
+    pci_bus = ich9_d2pbr_init(host_bus, PCI_DEVFN(ICH9_D2P_BRIDGE_DEV,
+                                                  ICH9_D2P_BRIDGE_FUNC),
+                              ICH9_D2P_SECONDARY_DEFAULT);
+
+    /* create child pci/pcie buses */
+    pc_q35_bridge_init(host_bus, pci_bus);
+
+    /* create ISA bus */
+    lpc = gmch_lpc_init(gmch_host, host_bus);
+
+    *gmch_host_p = gmch_host;
+    *host_bus_p = host_bus;
+    *pci_bus_p = pci_bus;
+    *lpc_p = lpc;
+}
+
+static void pc_q35_init_late(BusState **idebus, ISADevice *rtc_state,
+                             DeviceState *gmch_host,
+                             PCIBus *host_bus, PCIBus *pci_bus,
+                             PCIDevice *lpc)
+{
+    qemu_irq *cmos_s3;
+    PCIDevice *ahci;
+    DriveInfo *hd[MAX_SATA_PORTS * MAX_IDE_DEVS];
+
+    /* connect pm stuff to lpc */
+    cmos_s3 = qemu_allocate_irqs(pc_cmos_set_s3_resume, rtc_state, 1);
+    ich9_lpc_pm_init(gmch_host, lpc, *cmos_s3);
+
+    /* ahci and SATA device */
+    ide_drive_get(hd, MAX_SATA_PORTS);
+    ahci = pci_create_simple_multifunction(host_bus,
+                                           PCI_DEVFN(ICH9_SATA1_DEV,
+                                                     ICH9_SATA1_FUNC),
+                                           true, "ich9-ahci");
+    pci_ahci_ide_create_devs(ahci, hd);
+    idebus[0] = qdev_get_child_bus(&ahci->qdev, "ide.0");
+    idebus[1] = qdev_get_child_bus(&ahci->qdev, "ide.1");
+
+    if (usb_enabled) {
+        /* Should we create 6 UHCI according to ich9 spec? */
+        pci_create_simple_multifunction(
+            host_bus, PCI_DEVFN(ICH9_USB_UHCI1_DEV, ICH9_USB_UHCI1_FUNC),
+            true, "ich9-usb-uhci1");
+        /* XXX: EHCI */
+    }
+
+    /* 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 hardware initialisation */
+static void pc_q35_init(ram_addr_t ram_size,
+                        const char *boot_device,
+                        const char *kernel_filename,
+                        const char *kernel_cmdline,
+                        const char *initrd_filename,
+                        const char *cpu_model)
+{
+    ram_addr_t below_4g_mem_size, above_4g_mem_size;
+    DeviceState *gmch_host;
+    PCIBus *host_bus;
+    PCIBus *pci_bus;
+    PCIDevice *lpc;
+    qemu_irq *isa_irq;
+    IsaIrqState *isa_irq_state;
+    BusState *idebus[MAX_SATA_PORTS];
+    ISADevice *rtc_state;
+    MemoryRegion *pci_memory;
+    MemoryRegion *rom_memory;
+    MemoryRegion *ram_memory;
+
+    pc_cpus_init(cpu_model);
+
+    /* FIXME: add kvm clock ? */
+
+    if (ram_size >= 0xe0000000) {
+        above_4g_mem_size = ram_size - 0xe0000000;
+        below_4g_mem_size = 0xe0000000;
+    } else {
+        above_4g_mem_size = 0;
+        below_4g_mem_size = ram_size;
+    }
+
+    /* pci enabled */
+    pci_memory = g_new(MemoryRegion, 1);
+    memory_region_init(pci_memory, "pci", INT64_MAX);
+    rom_memory = pci_memory;
+
+    /* allocate ram and load rom/bios */
+    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 */
+    isa_irq = pc_isa_irq(&isa_irq_state);
+    ioapic_init(isa_irq_state);
+
+    pc_q35_init_early(isa_irq, isa_irq_state,
+                      &gmch_host, &host_bus, &pci_bus, &lpc);
+    isa_bus_irqs(isa_irq);
+    pc_register_ferr_irq(isa_get_irq(13));
+
+    /* init basic PC hardware */
+    pc_basic_device_init(isa_irq, &rtc_state, false);
+
+    pc_q35_init_late(idebus, rtc_state, gmch_host, host_bus, pci_bus, lpc);
+
+    pc_cmos_init(below_4g_mem_size, above_4g_mem_size, boot_device,
+                 idebus[0], idebus[1], rtc_state);
+
+    /* the rest devices to which pci devfn is automatically assigned */
+    pc_vga_init(host_bus);
+    audio_init(isa_irq, pci_bus);
+    pc_nic_init(pci_bus);
+    pc_pci_device_init(pci_bus);
+}
+
+static QEMUMachine pc_q35_machine = {
+    .name = "pc_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_ids.h b/hw/pci_ids.h
index 6deeac0..50744dd 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_OTHER    0x0780
@@ -115,6 +116,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
@@ -125,6 +137,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/q35.c b/hw/q35.c
new file mode 100644
index 0000000..1776ac3
--- /dev/null
+++ b/hw/q35.c
@@ -0,0 +1,877 @@
+/*
+ * 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 GMCH/ICH9 PCI Bridge Emulation
+ *
+ *  Copyright (c) 2009, 2010, 2011
+ *                Isaku Yamahata <yamahata at valinux co jp>
+ *                VA Linux Systems Japan K.K.
+ *
+ *  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 "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 "pci_bridge.h"
+#include "pci_p2pbr.h"
+#include "q35.h"
+#include "acpi.h"
+#include "acpi_ich9.h"
+#include "pam.h"
+
+
+struct ICH9_LPCState;
+
+typedef struct ICH9_LPCIrqState {
+    struct ICH9_LPCState *lpc;
+    qemu_irq *pic;
+    qemu_irq *ioapic;
+} ICH9_LPCIrqState;
+
+typedef struct GMCH_PCIHost {
+    PCIExpressHost      host;
+
+    PCIDevice    *dev;
+    ICH9_LPCIrqState irq_state;
+} GMCH_PCIHost;
+
+typedef struct GMCH_PCIState {
+    PCIDevice   d;
+    /*
+     * GMCH_PCIHost   *gmch_host;
+     * In order to get GMCH_PCIHost
+     *  PCIDevice -> qdev -> parent_bus -> qdev -upcast-> GMCH_PCIHost
+     */
+
+    PAM pam;
+} GMCH_PCIState;
+
+typedef struct ICH9_LPCState {
+    /* 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;
+    ICH9_LPCPmRegs 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];
+    int rbca_index;
+} ICH9_LPCState;
+
+
+/****************************************************************************
+ * GMCH PCI host
+ */
+/* ich9 irq */
+static int ich9_lpc_map_irq(void *opaque, PCIDevice *pci_dev, int intx);
+static void ich9_lpc_set_irq(void *opaque, int irq_num, int level);
+static int ich9_lpc_sci_irq(ICH9_LPCState *lpc);
+
+static GMCH_PCIHost *gmch_pcihost_from_qdev(DeviceState *gmch_host_qdev)
+{
+    SysBusDevice *sysdev = sysbus_from_qdev(gmch_host_qdev);
+    PCIHostState *pci = FROM_SYSBUS(PCIHostState, sysdev);
+    PCIExpressHost *pcie = DO_UPCAST(PCIExpressHost, pci, pci);
+    return DO_UPCAST(GMCH_PCIHost, host, pcie);
+}
+
+static int gmch_pcihost_initfn(SysBusDevice *dev)
+{
+    GMCH_PCIHost *s = gmch_pcihost_from_qdev(&dev->qdev);
+
+    pci_host_conf_register_ioport(GMCH_HOST_BRIDGE_CONFIG_ADDR, &s->host.pci);
+    pci_host_data_register_ioport(GMCH_HOST_BRIDGE_CONFIG_DATA, &s->host.pci);
+
+    if (pcie_host_init(&s->host) < 0) {
+        abort();
+    }
+
+    return 0;
+}
+
+static SysBusDeviceInfo gmch_pcihost_info = {
+    .init         = gmch_pcihost_initfn,
+    .qdev.name    = "gmch-pcihost",
+    .qdev.size    = sizeof(GMCH_PCIHost),
+    .qdev.no_user = 1,
+    .qdev.props = (Property[]) {
+        {
+            .name = "MCFG",
+            .info = &qdev_prop_uint64,
+            .offset = offsetof(GMCH_PCIHost, host.base_addr),
+            .defval = (uint64_t[]){ GMCH_HOST_BRIDGE_PCIEXBAR_DEFAULT },
+        },
+        DEFINE_PROP_END_OF_LIST(),
+    },
+};
+
+/* host bridge */
+PCIBus *gmch_host_init(DeviceState **gmch_hostp,
+                       qemu_irq *pic, qemu_irq *ioapic)
+{
+    DeviceState *dev;
+    GMCH_PCIHost *s;
+    PCIBus *b;
+
+    dev = qdev_create(NULL, "gmch-pcihost");
+    s = gmch_pcihost_from_qdev(dev);
+    s->irq_state.pic = pic;
+    s->irq_state.ioapic = ioapic;
+
+    b = pci_bus_new(dev, "pcie.0", 0);
+    pci_bus_irqs(b, ich9_lpc_set_irq, ich9_lpc_map_irq, &s->irq_state,
+                 ICH9_LPC_NB_PIRQS);
+    s->host.pci.bus = b;
+    qdev_init_nofail(dev);
+
+    *gmch_hostp = dev;
+    return b;
+}
+
+
+/****************************************************************************
+ * GMCH
+ */
+static GMCH_PCIState *gmch_from_pci(PCIDevice *gmch_pci)
+{
+    return DO_UPCAST(GMCH_PCIState, d, gmch_pci);
+}
+
+/* PCIE MMCFG */
+static void gmch_update_pciexbar(GMCH_PCIState *gs)
+{
+    PCIDevice *pci_dev = &gs->d;
+    BusState *bus = qdev_get_parent_bus(&pci_dev->qdev);
+    DeviceState *qdev = bus->parent;
+    GMCH_PCIHost *s = gmch_pcihost_from_qdev(qdev);
+
+    uint64_t pciexbar;
+    int enable;
+    uint64_t addr;
+    uint64_t addr_mask;
+    uint32_t length;
+
+    pciexbar = pci_get_quad(pci_dev->config + GMCH_HOST_BRIDGE_PCIEXBAR);
+    enable = pciexbar & GMCH_HOST_BRIDGE_PCIEXBAREN;
+
+    addr_mask = GMCH_HOST_BRIDGE_PCIEXBAR_ADMSK;
+    switch (pciexbar & GMCH_HOST_BRIDGE_PCIEXBAR_LENGTH_MASK) {
+    case GMCH_HOST_BRIDGE_PCIEXBAR_LENGTH_256M:
+        length = 256 * 1024 * 1024;
+        break;
+    case GMCH_HOST_BRIDGE_PCIEXBAR_LENGTH_128M:
+        length = 128 * 1024 * 1024;
+        addr_mask |= GMCH_HOST_BRIDGE_PCIEXBAR_128ADMSK |
+            GMCH_HOST_BRIDGE_PCIEXBAR_64ADMSK;
+        break;
+    case GMCH_HOST_BRIDGE_PCIEXBAR_LENGTH_64M:
+        length = 64 * 1024 * 1024;
+        addr_mask |= GMCH_HOST_BRIDGE_PCIEXBAR_64ADMSK;
+        break;
+    case GMCH_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 gmch_update_pam(GMCH_PCIState *gs)
+{
+    int i;
+    for (i = 0; i <= PAM_IDX_MAX; i++) {
+        pam_update(&gs->pam, i, gs->d.config[GMCH_HOST_BRIDGE_PAM0 + i]);
+    }
+}
+
+/* SMRAM */
+static void gmch_update_smram(GMCH_PCIState *gs)
+{
+    smram_update(&gs->pam, gs->d.config[GMCH_HOST_BRDIGE_SMRAM]);
+}
+
+static void gmch_set_smm(int smm, void *arg)
+{
+    GMCH_PCIState *gs = arg;
+    smram_set_smm(&gs->pam, smm, gs->d.config[GMCH_HOST_BRDIGE_SMRAM]);
+}
+
+static void gmch_write_config(PCIDevice *d,
+                              uint32_t address, uint32_t val, int len)
+{
+    GMCH_PCIState *gs = gmch_from_pci(d);
+
+    /* XXX: implement SMRAM.D_LOCK */
+    pci_default_write_config(d, address, val, len);
+
+    if (ranges_overlap(address, len, GMCH_HOST_BRIDGE_PAM0,
+                       GMCH_HOST_BRIDGE_PAM_SIZE)) {
+        gmch_update_pam(gs);
+    }
+
+    if (ranges_overlap(address, len, GMCH_HOST_BRIDGE_PCIEXBAR,
+                       GMCH_HOST_BRIDGE_PCIEXBAR_SIZE)) {
+        gmch_update_pciexbar(gs);
+    }
+
+    if (ranges_overlap(address, len, GMCH_HOST_BRDIGE_SMRAM,
+                       GMCH_HOST_BRDIGE_SMRAM_SIZE)) {
+        gmch_update_smram(gs);
+    }
+}
+
+static void gmch_update(GMCH_PCIState *gs)
+{
+    gmch_update_pciexbar(gs);
+    gmch_update_pam(gs);
+    gmch_update_smram(gs);
+}
+
+static int gmch_post_load(void *opaque, int version_id)
+{
+    GMCH_PCIState *gs = opaque;
+    gmch_update(gs);
+    return 0;
+}
+
+static const VMStateDescription vmstate_gmch = {
+    .name = "gmch",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .post_load = gmch_post_load,
+    .fields = (VMStateField []) {
+        VMSTATE_PCI_DEVICE(d, GMCH_PCIState),
+        VMSTATE_UINT8(pam.smm_enabled, GMCH_PCIState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void gmch_reset(DeviceState *qdev)
+{
+    PCIDevice *d = DO_UPCAST(PCIDevice, qdev, qdev);
+    GMCH_PCIState *gs = gmch_from_pci(d);
+
+    pci_set_quad(d->config + GMCH_HOST_BRIDGE_PCIEXBAR,
+                 GMCH_HOST_BRIDGE_PCIEXBAR_DEFAULT);
+
+    d->config[GMCH_HOST_BRDIGE_SMRAM] = GMCH_HOST_BRIDGE_SMRAM_DEFAULT;
+
+    gmch_update(gs);
+}
+
+static int gmch_initfn(PCIDevice *d)
+{
+    GMCH_PCIState *gs = gmch_from_pci(d);
+
+    pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_INTEL);
+    pci_config_set_device_id(d->config, PCI_DEVICE_ID_INTEL_Q35_MCH);
+    pci_config_set_revision(d->config, GMCH_HOST_BRIDGE_REVISION_DEFUALT);
+    pci_config_set_class(d->config, PCI_CLASS_BRIDGE_HOST);
+
+    cpu_smm_register(&gmch_set_smm, gs);
+    pam_init_memory_mappings(&gs->pam);
+
+    return 0;
+}
+
+static PCIDeviceInfo gmch_info = {
+    .qdev.name    = "gmch",
+    .qdev.desc    = "Host bridge",
+    .qdev.size    = sizeof(GMCH_PCIState),
+    .qdev.vmsd    = &vmstate_gmch,
+    .qdev.no_user = 1,
+    .init         = gmch_initfn,
+    .config_write = gmch_write_config,
+    .qdev.reset   = gmch_reset,
+};
+
+/* host bridge */
+PCIDevice *gmch_init(DeviceState *gmch_host, PCIBus *b)
+{
+    GMCH_PCIHost *s = gmch_pcihost_from_qdev(gmch_host);
+    PCIDevice *d;
+
+    d = pci_create_simple_multifunction(b, 0, false, "gmch");
+    s->dev = d;
+
+    return d;
+}
+
+/*****************************************************************************/
+/* ICH9 DMI-to-PCI bridge */
+#define I82801ba_SSVID_OFFSET   0x50
+#define I82801ba_SSVID_SVID     0
+#define I82801ba_SSVID_SSID     0
+
+static PCIBridge *i82801ba11_init(PCIBus *bus, int devfn, const char *bus_name,
+                                  bool multifunction)
+{
+    const PCIP2PBridgeInit init = {
+        .bus = bus,
+        .devfn = devfn,
+        .multifunction = multifunction,
+
+        .bus_name = bus_name,
+        .map_irq = pci_swizzle_map_irq_fn,
+    };
+    const PCIP2PBridgeProp prop = {
+        .vendor_id = PCI_VENDOR_ID_INTEL,
+        .device_id = PCI_DEVICE_ID_INTEL_82801BA_11,
+        .revision_id = ICH9_D2P_A2_REVISION,
+        .prog_interface = PCI_CLASS_BRDIGE_PCI_INF_SUB,
+
+        .ssvid_cap = I82801ba_SSVID_OFFSET,
+        .svid = I82801ba_SSVID_SVID,
+        .ssid = I82801ba_SSVID_SSID,
+    };
+    return pci_p2pbr_create_simple(&init, &prop);
+}
+
+PCIBus *ich9_d2pbr_init(PCIBus *bus, int devfn, int sec_bus)
+{
+    PCIBridge *br;
+    char buf[16];
+
+    snprintf(buf, sizeof(buf), "pci.%d", sec_bus);
+    br = i82801ba11_init(bus, devfn, buf, true);
+    if (br == NULL) {
+        return NULL;
+    }
+    return pci_bridge_get_sec_bus(br);
+}
+
+
+/*****************************************************************************/
+/* ICH9 LPC PCI to ISA bridge */
+
+static void ich9_lpc_reset(DeviceState *qdev);
+
+static ICH9_LPCState *ich9_lpc_from_pci(PCIDevice *lpc_pci)
+{
+    return DO_UPCAST(ICH9_LPCState, d, lpc_pci);
+}
+
+/* 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], uint32_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(ICH9_LPCState *lpc)
+{
+    int slot;
+    int reg_offset;
+    int intx;
+
+    /* D{25 - 31}IR, but D30IR is read only to 0. */
+    for (slot = 25, reg_offset = 0; slot < 32; slot++, reg_offset++) {
+        if (slot != 30) {
+            ich9_cc_update_ir(lpc->irr[slot],
+                              lpc->chip_config[ICH9_CC_D31IR + reg_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 (intx = 0; intx < PCI_NUM_PINS; intx++) {
+        lpc->irr[30][intx] = intx + 4;
+    }
+}
+
+static void ich9_cc_init(ICH9_LPCState *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(ICH9_LPCState *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(uint32_t *addr, int *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(ICH9_LPCState *lpc, uint32_t addr,
+                          uint32_t val, int len)
+{
+    ich9_cc_addr_len(&addr, &len);
+    memcpy(lpc->chip_config + addr, &val, len);
+}
+
+/* return value: little endian */
+static uint32_t ich9_cc_read(ICH9_LPCState *lpc, uint32_t addr, int len)
+{
+    uint32_t val = 0;
+    ich9_cc_addr_len(&addr, &len);
+    memcpy(&val, lpc->chip_config + addr, len);
+    return val;
+}
+
+#define ICH9_CC_MMIO_WRITE(type, len)                           \
+    static void ich9_cc_mmio_write ## type                      \
+    (void *opaque, target_phys_addr_t addr, uint32_t val)       \
+    {                                                           \
+        ich9_cc_write(opaque, addr, val, len);                  \
+    }
+
+#define ICH9_CC_MMIO_READ(type, len)            \
+    static uint32_t ich9_cc_mmio_read ## type   \
+    (void *opaque, target_phys_addr_t addr)     \
+    {                                           \
+        return ich9_cc_read(opaque, addr, len); \
+    }
+
+ICH9_CC_MMIO_WRITE(b, 1)
+ICH9_CC_MMIO_WRITE(w, 2)
+ICH9_CC_MMIO_WRITE(l, 4)
+
+ICH9_CC_MMIO_READ(b, 1)
+ICH9_CC_MMIO_READ(w, 2)
+ICH9_CC_MMIO_READ(l, 4)
+
+static CPUWriteMemoryFunc * const ich9_cc_mmio_write[] = {
+    ich9_cc_mmio_writeb,
+    ich9_cc_mmio_writew,
+    ich9_cc_mmio_writel,
+};
+
+static CPUReadMemoryFunc * const ich9_cc_mmio_read[] = {
+    ich9_cc_mmio_readb,
+    ich9_cc_mmio_readw,
+    ich9_cc_mmio_readl,
+};
+
+/* 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(ICH9_LPCState *lpc, int irq_num,
+                             int *pic_irq, int *pic_dis)
+{
+    switch (irq_num) {
+    case 0 ... 3: /* A-D */
+        ich9_lpc_rout(lpc->d.config[ICH9_LPC_PIRQA_ROUT + irq_num],
+                      pic_irq, pic_dis);
+        return;
+    case 4 ... 7: /* E-H */
+        ich9_lpc_rout(lpc->d.config[ICH9_LPC_PIRQE_ROUT + (irq_num - 4)],
+                      pic_irq, pic_dis);
+        return;
+    default:
+        break;
+    }
+    abort();
+}
+
+/* pic_irq: i8254 irq 0-15 */
+static void ich9_lpc_update_pic(ICH9_LPCIrqState *irq_state, int pic_irq)
+{
+    GMCH_PCIHost *s = container_of(irq_state, GMCH_PCIHost, irq_state);
+    ICH9_LPCState *lpc = irq_state->lpc;
+    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(s->host.pci.bus, i);
+        }
+    }
+    if (pic_irq == ich9_lpc_sci_irq(lpc)) {
+        pic_level |= lpc->sci_level;
+    }
+
+    qemu_set_irq(irq_state->pic[pic_irq], pic_level);
+}
+
+/* pirq: pirq[A-H] 0-7*/
+static void ich9_lpc_update_by_pirq(ICH9_LPCIrqState *irq_state, int pirq)
+{
+    ICH9_LPCState *lpc = irq_state->lpc;
+    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(irq_state, 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(ICH9_LPCIrqState *irq_state, int gsi)
+{
+    GMCH_PCIHost *s = container_of(irq_state, GMCH_PCIHost, irq_state);
+    ICH9_LPCState *lpc = irq_state->lpc;
+    int level;
+
+    level = pci_bus_get_irq_level(s->host.pci.bus, ich9_gsi_to_pirq(gsi));
+    if (gsi == ich9_lpc_sci_irq(lpc)) {
+        level |= lpc->sci_level;
+    }
+
+    qemu_set_irq(irq_state->ioapic[gsi], level);
+}
+
+/* return the pirq number (PIRQ[A-H]:0-7) corresponding to
+   a given device irq pin. */
+static int ich9_lpc_map_irq(void *opaque, PCIDevice *pci_dev, int intx)
+{
+    ICH9_LPCIrqState *irq_state = opaque;
+    return irq_state->lpc->irr[PCI_SLOT(pci_dev->devfn)][intx];
+}
+
+static void ich9_lpc_set_irq(void *opaque, int pirq, int level)
+{
+    ICH9_LPCIrqState *irq_state = opaque;
+
+    assert(0 <= pirq);
+    assert(pirq < ICH9_LPC_NB_PIRQS);
+
+    ich9_lpc_update_apic(irq_state, ich9_pirq_to_gsi(pirq));
+    ich9_lpc_update_by_pirq(irq_state, pirq);
+}
+
+static int ich9_lpc_sci_irq(ICH9_LPCState *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)
+{
+    ICH9_LPCIrqState *irq_state = opaque;
+    ICH9_LPCState *lpc = irq_state->lpc;
+    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(irq_state, irq);
+    if (irq < ICH9_LPC_PIC_NUM_PINS) {
+        ich9_lpc_update_pic(irq_state, irq);
+    }
+}
+
+void ich9_lpc_pm_init(DeviceState *gmch_host, PCIDevice *lpc_pci,
+                      qemu_irq cmos_s3)
+{
+    GMCH_PCIHost *s = gmch_pcihost_from_qdev(gmch_host);
+    ICH9_LPCState *lpc = ich9_lpc_from_pci(lpc_pci);
+    qemu_irq *sci_irq;
+
+    sci_irq = qemu_allocate_irqs(ich9_set_sci, &s->irq_state, 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)
+{
+    ICH9_LPCState *lpc = arg;
+
+    /* ACPI specs 3.0, 4.7.2.5 */
+    acpi_pm1_cnt_update(&lpc->pm.pm1_cnt,
+                        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(ICH9_LPCState *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(ICH9_LPCState *lpc, uint32_t rbca_old)
+{
+    uint32_t rbca = pci_get_long(lpc->d.config + ICH9_LPC_RCBA);
+
+    if (rbca_old & ICH9_LPC_RCBA_EN) {
+        cpu_register_physical_memory(rbca_old & ICH9_LPC_RCBA_BA_MASK,
+                                     ICH9_CC_SIZE, IO_MEM_UNASSIGNED);
+    }
+    if (rbca & ICH9_LPC_RCBA_EN) {
+        cpu_register_physical_memory(rbca & ICH9_LPC_RCBA_BA_MASK,
+                                     ICH9_CC_SIZE, lpc->rbca_index);
+    }
+}
+
+static int ich9_lpc_post_load(void *opaque, int version_id)
+{
+    ICH9_LPCState *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)
+{
+    ICH9_LPCState *lpc = ich9_lpc_from_pci(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 = DO_UPCAST(PCIDevice, qdev, qdev);
+    ICH9_LPCState *lpc = ich9_lpc_from_pci(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 int ich9_lpc_initfn(PCIDevice *d)
+{
+    ICH9_LPCState *lpc = ich9_lpc_from_pci(d);
+
+    isa_bus_new(&d->qdev);
+    pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_INTEL);
+    pci_config_set_device_id(d->config, PCI_DEVICE_ID_INTEL_ICH9_8); /* ICH9 
LPC */
+    pci_config_set_revision(d->config, ICH9_A2_LPC_REVISION);
+    pci_config_set_class(d->config, PCI_CLASS_BRIDGE_ISA);
+
+    pci_set_long(d->wmask + ICH9_LPC_PMBASE,
+                 ICH9_LPC_PMBASE_BASE_ADDRESS_MASK);
+
+    lpc->rbca_index = cpu_register_io_memory(ich9_cc_mmio_read,
+                                             ich9_cc_mmio_write,
+                                             lpc, DEVICE_LITTLE_ENDIAN);
+
+    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, ICH9_LPCState),
+        VMSTATE_STRUCT(apm, ICH9_LPCState, 0, vmstate_apm, APMState),
+        VMSTATE_STRUCT(pm, ICH9_LPCState, 0, vmstate_ich9_pm, ICH9_LPCPmRegs),
+        VMSTATE_UINT8_ARRAY(chip_config, ICH9_LPCState, ICH9_CC_SIZE),
+        VMSTATE_UINT32(sci_level, ICH9_LPCState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+PCIDevice *gmch_lpc_init(DeviceState *gmch_host, PCIBus *bus)
+{
+    GMCH_PCIHost *s = gmch_pcihost_from_qdev(gmch_host);
+    PCIDevice *d;
+    ICH9_LPCState *lpc;
+
+    d = pci_create_simple_multifunction(bus, PCI_DEVFN(ICH9_LPC_DEV,
+                                                       ICH9_LPC_FUNC),
+                                        true, "ICH9 LPC");
+    lpc = ich9_lpc_from_pci(d);
+    s->irq_state.lpc = lpc;
+    return &lpc->d;
+}
+
+static PCIDeviceInfo ich9_lpc_info = {
+    .qdev.name    = "ICH9 LPC",
+    .qdev.desc    = "ICH9 LPC bridge",
+    .qdev.size    = sizeof(ICH9_LPCState),
+    .qdev.vmsd    = &vmstate_ich9_lpc,
+    .qdev.no_user = 1,
+    .init         = ich9_lpc_initfn,
+    .config_write = ich9_lpc_config_write,
+    .qdev.reset   = ich9_lpc_reset,
+};
+
+static void q35_register(void)
+{
+    sysbus_register_withprop(&gmch_pcihost_info);
+    pci_qdev_register(&gmch_info);
+    pci_qdev_register(&ich9_lpc_info);
+}
+device_init(q35_register);
diff --git a/hw/q35.h b/hw/q35.h
new file mode 100644
index 0000000..be2e96b
--- /dev/null
+++ b/hw/q35.h
@@ -0,0 +1,272 @@
+/*
+ * q35.h
+ *
+ * Copyright (c) 2009 Isaku Yamahata <yamahata at valinux co jp>
+ *                    VA Linux Systems Japan K.K.
+ *
+ * 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 "sysbus.h"
+#include "acpi_ich9.h"
+
+PCIBus *gmch_host_init(DeviceState **gmch_hostp,
+                       qemu_irq *pic, qemu_irq *ioapic);
+
+PCIDevice *gmch_init(DeviceState *gmch_host, PCIBus *b);
+PCIBus *ich9_d2pbr_init(PCIBus *bus, int devfn, int sec_bus);
+PCIDevice *gmch_lpc_init(DeviceState *gmch_host, PCIBus *bus);
+void ich9_lpc_pm_init(DeviceState *gmch_host, PCIDevice *pci_lpc,
+                      qemu_irq cmos_s3);
+
+i2c_bus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base);
+
+#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 GMCH_HOST_BRIDGE                        "GMCH"
+
+#define GMCH_HOST_BRIDGE_CONFIG_ADDR            0xcf8
+#define GMCH_HOST_BRIDGE_CONFIG_DATA            0xcfc
+
+/* D0:F0 configuration space */
+#define  GMCH_HOST_BRIDGE_REVISION_DEFUALT      0x0
+
+#define GMCH_HOST_BRIDGE_PCIEXBAR               0x60    /* 64bit register */
+#define  GMCH_HOST_BRIDGE_PCIEXBAR_SIZE         8       /* 64bit register */
+#define  GMCH_HOST_BRIDGE_PCIEXBAR_DEFAULT      0xe0000000
+#define  GMCH_HOST_BRIDGE_PCIEXBAR_ADMSK        Q35_MASK(64, 35, 25) /* bit 
35:28 */
+#define  GMCH_HOST_BRIDGE_PCIEXBAR_128ADMSK     ((uint64_t)(1 << 26))
+#define  GMCH_HOST_BRIDGE_PCIEXBAR_64ADMSK      ((uint64_t)(1 << 25))
+#define  GMCH_HOST_BRIDGE_PCIEXBAR_LENGTH_MASK  ((uint64_t)(0x3 << 1))
+#define  GMCH_HOST_BRIDGE_PCIEXBAR_LENGTH_256M  ((uint64_t)(0x0 << 1))
+#define  GMCH_HOST_BRIDGE_PCIEXBAR_LENGTH_128M  ((uint64_t)(0x1 << 1))
+#define  GMCH_HOST_BRIDGE_PCIEXBAR_LENGTH_64M   ((uint64_t)(0x2 << 1))
+#define  GMCH_HOST_BRIDGE_PCIEXBAR_LENGTH_RVD   ((uint64_t)(0x3 << 1))
+#define  GMCH_HOST_BRIDGE_PCIEXBAREN            ((uint64_t)1)
+
+#define GMCH_HOST_BRIDGE_PAM_NB                 7
+#define GMCH_HOST_BRIDGE_PAM_SIZE               7
+#define GMCH_HOST_BRIDGE_PAM0                   0x90
+#define  GMCH_HOST_BRIDGE_PAM_BIOS_AREA         0xf0000
+#define  GMCH_HOST_BRIDGE_PAM_AREA_SIZE         0x10000 /* 16KB */
+#define GMCH_HOST_BRIDGE_PAM1                   0x91
+#define  GMCH_HOST_BRIDGE_PAM_EXPAN_AREA        0xc0000
+#define  GMCH_HOST_BRIDGE_PAM_EXPAN_SIZE        0x04000
+#define GMCH_HOST_BRIDGE_PAM2                   0x92
+#define GMCH_HOST_BRIDGE_PAM3                   0x93
+#define GMCH_HOST_BRIDGE_PAM4                   0x94
+#define  GMCH_HOST_BRIDGE_PAM_EXBIOS_AREA       0xe0000
+#define  GMCH_HOST_BRIDGE_PAM_EXBIOS_SIZE       0x04000
+#define GMCH_HOST_BRIDGE_PAM5                   0x95
+#define GMCH_HOST_BRIDGE_PAM6                   0x96
+#define  GMCH_HOST_BRIDGE_PAM_WE_HI             ((uint8_t)(0x2 << 4))
+#define  GMCH_HOST_BRIDGE_PAM_RE_HI             ((uint8_t)(0x1 << 4))
+#define  GMCH_HOST_BRIDGE_PAM_HI_MASK           ((uint8_t)(0x3 << 4))
+#define  GMCH_HOST_BRIDGE_PAM_WE_LO             ((uint8_t)0x2)
+#define  GMCH_HOST_BRIDGE_PAM_RE_LO             ((uint8_t)0x1)
+#define  GMCH_HOST_BRIDGE_PAM_LO_MASK           ((uint8_t)0x3)
+#define  GMCH_HOST_BRIDGE_PAM_WE                ((uint8_t)0x2)
+#define  GMCH_HOST_BRIDGE_PAM_RE                ((uint8_t)0x1)
+#define  GMCH_HOST_BRIDGE_PAM_MASK              ((uint8_t)0x3)
+
+#define GMCH_HOST_BRDIGE_SMRAM                  0x9d
+#define GMCH_HOST_BRDIGE_SMRAM_SIZE             1
+#define  GMCH_HOST_BRIDGE_SMRAM_DEFAULT         ((uint8_t)0x2)
+#define  GMCH_HOST_BRIDGE_SMRAM_D_OPEN          ((uint8_t)(1 << 6))
+#define  GMCH_HOST_BRIDGE_SMRAM_D_CLS           ((uint8_t)(1 << 5))
+#define  GMCH_HOST_BRIDGE_SMRAM_D_LCK           ((uint8_t)(1 << 4))
+#define  GMCH_HOST_BRIDGE_SMRAM_G_SMRAME        ((uint8_t)(1 << 3))
+#define  GMCH_HOST_BRIDGE_SMRAM_C_BASE_SEG_MASK ((uint8_t)0x7)
+#define  GMCH_HOST_BRIDGE_SMRAM_C_BASE_SEG      ((uint8_t)0x2)  /* hardwired 
to b010 */
+#define   GMCH_HOST_BRIDGE_SMRAM_C_BASE         0xa0000
+#define   GMCH_HOST_BRIDGE_SMRAM_C_END          0xc0000
+#define   GMCH_HOST_BRIDGE_SMRAM_C_SIZE         0x20000
+#define GMCH_HOST_BRIDGE_UPPER_SYSTEM_BIOS_END  0x100000
+
+#define GMCH_HOST_BRIDGE_ESMRAMC                0x9e
+#define  GMCH_HOST_BRDIGE_ESMRAMC_H_SMRAME      ((uint8_t)(1 << 6))
+#define  GMCH_HOST_BRDIGE_ESMRAMC_E_SMERR       ((uint8_t)(1 << 5))
+#define  GMCH_HOST_BRDIGE_ESMRAMC_SM_CACHE      ((uint8_t)(1 << 4))
+#define  GMCH_HOST_BRDIGE_ESMRAMC_SM_L1         ((uint8_t)(1 << 3))
+#define  GMCH_HOST_BRDIGE_ESMRAMC_SM_L2         ((uint8_t)(1 << 2))
+#define  GMCH_HOST_BRDIGE_ESMRAMC_TSEG_SZ_MASK  ((uint8_t)(0x3 << 1))
+#define   GMCH_HOST_BRDIGE_ESMRAMC_TSEG_SZ_1MB  ((uint8_t)(0x0 << 1))
+#define   GMCH_HOST_BRDIGE_ESMRAMC_TSEG_SZ_2MB  ((uint8_t)(0x1 << 1))
+#define   GMCH_HOST_BRDIGE_ESMRAMC_TSEG_SZ_8MB  ((uint8_t)(0x2 << 1))
+#define  GMCH_HOST_BRDIGE_ESMRAMC_T_EN          ((uint8_t)1)
+
+/* D1:F0 PCIE* port*/
+#define GMCH_PCIE_DEV                           1
+#define GMCH_PCIE_FUNC                          0
+
+/*
+ * ich9 part
+ */
+
+/* ICH9: Chipset Configuration Registers */
+#define ICH9_CC_SIZE                            (16 * 1024)     /* 16KB */
+#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_Q35_H */
diff --git a/hw/q35_smbus.c b/hw/q35_smbus.c
new file mode 100644
index 0000000..fe445ac
--- /dev/null
+++ b/hw/q35_smbus.c
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ *
+ *  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 "q35.h"
+
+typedef struct ICH9_SMBState {
+    PCIDevice dev;
+
+    PMSMBus smb;
+} ICH9_SMBState;
+
+static ICH9_SMBState *ich9_pci_to_smb(PCIDevice* pci_dev)
+{
+    return DO_UPCAST(ICH9_SMBState, dev, pci_dev);
+}
+
+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 ICH9_SMBState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void ich9_smb_ioport_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+    ICH9_SMBState *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 uint32_t ich9_smb_ioport_readb(void *opaque, uint32_t addr)
+{
+    ICH9_SMBState *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 void ich9_smb_map_ioport(PCIDevice *dev, int region_num,
+                                uint64_t addr, uint64_t size, int type)
+{
+    ICH9_SMBState *s = ich9_pci_to_smb(dev);
+
+    assert(size == ICH9_SMB_SMB_BASE_SIZE);
+    assert(type == PCI_BASE_ADDRESS_SPACE_IO);
+
+    register_ioport_write(addr, 64, 1, ich9_smb_ioport_writeb, s);
+    register_ioport_read(addr, 64, 1, ich9_smb_ioport_readb, s);
+}
+
+static int ich9_smb_initfn(PCIDevice *d)
+{
+    ICH9_SMBState *s = ich9_pci_to_smb(d);
+
+    pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_INTEL);
+    pci_config_set_device_id(d->config, PCI_DEVICE_ID_INTEL_ICH9_6);
+
+    pci_set_word(d->wmask + PCI_STATUS,
+                 PCI_STATUS_SIG_SYSTEM_ERROR | PCI_STATUS_DETECTED_PARITY);
+
+    pci_config_set_revision(d->config, ICH9_A2_SMB_REVISION);
+    pci_config_set_prog_interface(d->config, ICH9_SMB_PI);
+    pci_config_set_class(d->config, PCI_CLASS_SERIAL_SMBUS);
+
+    /* 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*/
+    pci_register_bar(d, ICH9_SMB_SMB_BASE_BAR,
+                     ICH9_SMB_SMB_BASE_SIZE, PCI_BASE_ADDRESS_SPACE_IO,
+                     &ich9_smb_map_ioport);
+
+    pm_smbus_init(&d->qdev, &s->smb);
+    return 0;
+}
+
+i2c_bus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base)
+{
+    PCIDevice *d =
+        pci_create_simple_multifunction(bus, devfn, true, "ICH9 SMB");
+    ICH9_SMBState *s = ich9_pci_to_smb(d);
+    return s->smb.smbus;
+}
+
+static PCIDeviceInfo ich9_smb_info = {
+    .qdev.name = "ICH9 SMB",
+    .qdev.desc = "ICH9 SMBUS Bridge",
+    .qdev.size = sizeof(ICH9_SMBState),
+    .qdev.vmsd = &vmstate_ich9_smbus,
+    .qdev.no_user = 1,
+    .init = ich9_smb_initfn,
+};
+
+static void ich9_smb_register(void)
+{
+    pci_qdev_register(&ich9_smb_info);
+}
+
+device_init(ich9_smb_register);
-- 
1.7.1




reply via email to

[Prev in Thread] Current Thread [Next in Thread]