qemu-devel
[Top][All Lists]
Advanced

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

[Qemu-devel] [PATCH 53/61] pc q35 based chipset emulator


From: Isaku Yamahata
Subject: [Qemu-devel] [PATCH 53/61] pc q35 based chipset emulator
Date: Wed, 30 Sep 2009 19:18:29 +0900

pc q35 based chipset emulator

Signed-off-by: Isaku Yamahata <address@hidden>
---
 Makefile.target |    1 +
 hw/acpi_ich9.c  |  565 ++++++++++++++++++++++++++++++++++++++++++
 hw/acpi_ich9.h  |   57 +++++
 hw/pc_q35.c     |  220 +++++++++++++++++
 hw/pci_ids.h    |   13 +
 hw/q35.c        |  731 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/q35.h        |  228 +++++++++++++++++
 hw/q35_smbus.c  |  150 ++++++++++++
 8 files changed, 1965 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/Makefile.target b/Makefile.target
index ab5c098..9990942 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -188,6 +188,7 @@ obj-i386-y += usb-uhci.o vmmouse.o vmport.o vmware_vga.o 
hpet.o
 obj-i386-y += device-hotplug.o pci-hotplug.o smbios.o wdt_ib700.o
 obj-i386-y += ne2000-isa.o
 obj-i386-y += pc_smbus.o pc_apm.o acpi_piix4.o pc_piix.o
+obj-i386-y += pc_q35.o q35.o q35_smbus.o acpi_ich9.o
 
 # shared objects
 obj-ppc-y = ppc.o ide/core.o ide/qdev.o ide/isa.o ide/pci.o ide/macio.o
diff --git a/hw/acpi_ich9.c b/hw/acpi_ich9.c
new file mode 100644
index 0000000..82e4d5c
--- /dev/null
+++ b/hw/acpi_ich9.c
@@ -0,0 +1,565 @@
+/*
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA  02110-1301 USA
+ */
+/*
+ *  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
+
+/* XXX where does the constant, ACPI_DBG_IO_ADDR, come from */
+/* 0xb044 is used on ich9. 0xb080 or something. */
+//#define ACPI_DBG_IO_ADDR  0xb044
+// 0xb044 is already used.
+#define ACPI_DBG_IO_ADDR  0xb080
+static void ich9_acpi_dbg_writel(void *opaqe, uint32_t addr, uint32_t val)
+{
+    ICH9_DEBUG("0x%08x\n", val);
+}
+
+void ich9_acpi_dbg_init(void);
+void ich9_acpi_dbg_init(void)
+{
+    register_ioport_write(ACPI_DBG_IO_ADDR, 4, 4, ich9_acpi_dbg_writel, NULL);
+}
+
+
+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 gpe_ioport_writeb(struct ich9_lpc_pm_regs *pm,
+                              uint32_t addr, uint32_t val);
+static uint32_t gpe_ioport_readb(struct ich9_lpc_pm_regs *pm,
+                                 uint32_t addr);
+
+static uint32_t get_pmtmr(struct ich9_lpc_pm_regs *pm)
+{
+    uint32_t d;
+    d = muldiv64(qemu_get_clock(vm_clock), PM_TIMER_FREQUENCY,
+                 get_ticks_per_sec());
+    return d & 0xffffff;
+}
+
+static int get_pm1_sts(struct ich9_lpc_pm_regs *pm)
+{
+    int64_t d;
+    d = muldiv64(qemu_get_clock(vm_clock), PM_TIMER_FREQUENCY,
+                 get_ticks_per_sec());
+    if (d >= pm->tmr_overflow_time)
+        pm->pm1_sts |= ACPI_BITMASK_TIMER_STATUS;
+    return pm->pm1_sts;
+}
+
+static void pm_update_sci(struct ich9_lpc_pm_regs *pm)
+{
+    int sci_level, pm1_sts;
+    int64_t expire_time;
+
+    pm1_sts = get_pm1_sts(pm);
+    sci_level = (((pm1_sts & pm->pm1_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 */
+    if ((pm->pm1_en & ACPI_BITMASK_TIMER_ENABLE) &&
+        !(pm1_sts & ACPI_BITMASK_TIMER_STATUS)) {
+        expire_time = muldiv64(pm->tmr_overflow_time, get_ticks_per_sec(),
+                               PM_TIMER_FREQUENCY);
+        qemu_mod_timer(pm->tmr_timer, expire_time);
+    } else {
+        qemu_del_timer(pm->tmr_timer);
+    }
+}
+
+static void pm_tmr_timer(void *opaque)
+{
+    struct ich9_lpc_pm_regs *pm = opaque;
+    pm_update_sci(pm);
+}
+
+static void pm_ioport_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+    struct ich9_lpc_pm_regs *pm = opaque;
+
+    switch (addr & ICH9_PMIO_MASK) {
+    case ICH9_PMIO_GPE0_STS ... (ICH9_PMIO_GPE0_STS + 7):
+    case ICH9_PMIO_GPE0_EN ... (ICH9_PMIO_GPE0_EN + 7):
+        gpe_ioport_writeb(pm, 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)
+{
+    struct ich9_lpc_pm_regs *pm = opaque;
+    uint32_t val = 0;
+
+    switch(addr & ICH9_PMIO_MASK) {
+    case ICH9_PMIO_GPE0_STS ... (ICH9_PMIO_GPE0_STS + 7):
+    case ICH9_PMIO_GPE0_EN ... (ICH9_PMIO_GPE0_EN + 7):
+        val = gpe_ioport_readb(pm, 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)
+{
+    struct ich9_lpc_pm_regs *pm = opaque;
+
+    switch(addr & ICH9_PMIO_MASK) {
+    case ICH9_PMIO_PM1_STS:
+        {
+            int64_t d;
+            int pm1_sts;
+            pm1_sts = get_pm1_sts(pm);
+            if (pm1_sts & val & ACPI_BITMASK_TIMER_STATUS) {
+                /* if TMRSTS is reset, then compute the new overflow time */
+                d = muldiv64(qemu_get_clock(vm_clock), PM_TIMER_FREQUENCY,
+                             get_ticks_per_sec());
+                pm->tmr_overflow_time = (d + 0x800000LL) & ~0x7fffffLL;
+            }
+            pm->pm1_sts &= ~val;
+            pm_update_sci(pm);
+        }
+        break;
+    case ICH9_PMIO_PM1_EN:
+        pm->pm1_en = val;
+        pm_update_sci(pm);
+        break;
+    case ICH9_PMIO_PM1_CNT:
+        {
+            int sus_typ;
+            pm->pm1_cnt = val & ~(ACPI_BITMASK_SLEEP_ENABLE);
+            if (val & ACPI_BITMASK_SLEEP_ENABLE) {
+                /* change suspend type */
+                sus_typ = (val >> 10) & 7;
+                switch(sus_typ) {
+                case 0: /* soft power off */
+                    qemu_system_shutdown_request();
+                    break;
+                case 1:
+                    /* ACPI_BITMASK_WAKE_STATUS should be set on resume.
+                       Pretend that resume was caused by power button */
+                    pm->pm1_sts |= (ACPI_BITMASK_WAKE_STATUS |
+                                 ACPI_BITMASK_POWER_BUTTON_STATUS);
+                    qemu_system_reset_request();
+#if defined(TARGET_I386)
+                    cmos_set_s3_resume();
+#endif
+                default:
+                    break;
+                }
+            }
+        }
+        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)
+{
+    struct ich9_lpc_pm_regs *pm = opaque;
+    uint32_t val;
+
+    switch(addr & ICH9_PMIO_MASK) {
+    case ICH9_PMIO_PM1_STS:
+        val = get_pm1_sts(pm);
+        break;
+    case ICH9_PMIO_PM1_EN:
+        val = pm->pm1_en;
+        break;
+    case ICH9_PMIO_PM1_CNT:
+        val = pm->pm1_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)
+{
+    struct ich9_lpc_pm_regs *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)
+{
+    struct ich9_lpc_pm_regs *pm = opaque;
+    uint32_t val;
+
+    switch(addr & ICH9_PMIO_MASK) {
+    case ICH9_PMIO_PM1_TMR:
+        val = get_pmtmr(pm);
+        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(struct ich9_lpc_pm_regs *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);
+
+    /* XXX: tmp hack */
+    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;
+}
+
+#define PCI_BUS_MAX     256
+struct ich9_pci_status {
+    uint32_t up[PCI_BUS_MAX];
+    uint32_t down[PCI_BUS_MAX];
+    uint8_t bus;
+};
+
+const VMStateDescription vmstate_ich9_pci_status = {
+    .name = "ich9_pci_status",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(up, struct ich9_pci_status, PCI_BUS_MAX),
+        VMSTATE_UINT32_ARRAY(down, struct ich9_pci_status, PCI_BUS_MAX),
+        VMSTATE_UINT8(bus, struct ich9_pci_status),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+const VMStateDescription vmstate_ich9_pm = {
+    .name = "ich9_pm",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT16(pm1_sts, struct ich9_lpc_pm_regs),
+        VMSTATE_UINT16(pm1_en, struct ich9_lpc_pm_regs),
+        VMSTATE_UINT16(pm1_cnt, struct ich9_lpc_pm_regs),
+        VMSTATE_TIMER(tmr_timer, struct ich9_lpc_pm_regs),
+        VMSTATE_INT64(tmr_overflow_time, struct ich9_lpc_pm_regs),
+        VMSTATE_UINT64(gpe0_sts, struct ich9_lpc_pm_regs),
+        VMSTATE_UINT64(gpe0_en, struct ich9_lpc_pm_regs),
+        VMSTATE_UINT32(smi_en, struct ich9_lpc_pm_regs),
+        VMSTATE_UINT32(smi_sts, struct ich9_lpc_pm_regs),
+        VMSTATE_STRUCT_POINTER(pci_status, struct ich9_lpc_pm_regs, 0,
+                               vmstate_ich9_pci_status,
+                               struct ich9_pci_status*),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void pm_reset(void *opaque)
+{
+    //struct ich9_lpc_pm_regs *pm = opaque;
+    /* XXX:TODO */
+}
+
+static void pm_powerdown(void *opaque, int irq, int power_failing)
+{
+#if defined(TARGET_I386)
+    struct ich9_lpc_pm_regs *pm = (struct ich9_lpc_pm_regs*) opaque;
+
+    if (!pm) {
+        qemu_system_shutdown_request();
+    } else if (pm->pm1_en & ACPI_BITMASK_POWER_BUTTON_ENABLE) {
+        pm->pm1_sts |= ACPI_BITMASK_POWER_BUTTON_STATUS;
+        pm_update_sci(pm);
+    }
+#endif
+}
+
+void ich9_pm_init(struct ich9_lpc_pm_regs *pm, qemu_irq sci_irq)
+{
+    pm->tmr_timer = qemu_new_timer(vm_clock, pm_tmr_timer, pm);
+
+    pm->irq = sci_irq;
+    qemu_register_reset(pm_reset, pm);
+    qemu_system_powerdown = *qemu_allocate_irqs(pm_powerdown, pm, 1);
+}
+
+/* GPE */
+#define GPE_ADDR_MASK   0xff
+
+static uint8_t *gpe_ioport_get_ptr(struct ich9_lpc_pm_regs *pm, uint32_t addr)
+{
+    uint8_t *cur = NULL;
+
+    addr &= GPE_ADDR_MASK;
+    switch (addr) {
+    case ICH9_PMIO_GPE0_STS ... (ICH9_PMIO_GPE0_STS + 7):
+        cur = (uint8_t*)&pm->gpe0_sts;
+        cur += addr - ICH9_PMIO_GPE0_STS;
+        break;
+    case ICH9_PMIO_GPE0_EN ... (ICH9_PMIO_GPE0_EN + 7):
+        cur = (uint8_t*)&pm->gpe0_en;
+        cur += addr - ICH9_PMIO_GPE0_EN;
+        break;
+    default:
+        abort();
+        break;
+    }
+    return cur;
+}
+
+static void gpe_ioport_writeb(struct ich9_lpc_pm_regs *pm,
+                              uint32_t addr, uint32_t val)
+{
+    uint8_t *cur = gpe_ioport_get_ptr(pm, addr);
+
+    switch (addr & GPE_ADDR_MASK) {
+    case ICH9_PMIO_GPE0_STS ... (ICH9_PMIO_GPE0_STS + 7):
+        *cur = (*cur) & ~val;
+        break;
+    case ICH9_PMIO_GPE0_EN ... (ICH9_PMIO_GPE0_EN + 7):
+        *cur = val;
+        break;
+    default:
+        abort();
+        break;
+    }
+    ICH9_DEBUG("%x <== %d\n", addr, val);
+}
+
+static uint32_t gpe_ioport_readb(struct ich9_lpc_pm_regs *pm,
+                                 uint32_t addr)
+{
+    uint8_t *cur = gpe_ioport_get_ptr(pm, addr);
+    uint32_t val = 0;
+
+    if (cur != NULL)
+        val = *cur;
+
+    ICH9_DEBUG("%x == %x\n", addr, val);
+    return val;
+}
+
+#define PCI_HP_BASE     0xae00
+#define PCI_EJ_BASE     0xae08
+#define PCI_HP_BUS      0xae0c
+
+static uint32_t pcihotplug_read(void *opaque, uint32_t addr)
+{
+    uint32_t val = 0;
+    struct ich9_pci_status *p = opaque;
+    switch (addr) {
+        case PCI_HP_BASE:
+            val = p->up[p->bus];
+            break;
+        case PCI_HP_BASE + 4:
+            val = p->down[p->bus];
+            break;
+        default:
+            break;
+    }
+
+    ICH9_DEBUG("%x == %x\n", addr, val);
+    return val;
+}
+
+static void pcihotplug_write(void *opaque, uint32_t addr, uint32_t val)
+{
+    struct ich9_pci_status *p = opaque;
+    switch (addr) {
+        case PCI_HP_BASE:
+            p->up[p->bus] = val;
+            break;
+        case PCI_HP_BASE + 4:
+            p->down[p->bus] = val;
+            break;
+   }
+
+    ICH9_DEBUG("%x <== %d\n", addr, val);
+}
+
+static uint32_t pciej_read(void *opaque, uint32_t addr)
+{
+    ICH9_DEBUG("%x\n", addr);
+    return 0;
+}
+
+static void pciej_write(void *opaque, uint32_t addr, uint32_t val)
+{
+#if defined (TARGET_I386)
+    struct ich9_pci_status *p = opaque;
+    int slot = ffs(val) - 1;
+
+    pci_device_hot_remove_success(p->bus, slot);
+#endif
+
+    ICH9_DEBUG("%x <== %d\n", addr, val);
+}
+
+static uint32_t pci_hp_bus_read(void *opaque, uint32_t addr)
+{
+    struct ich9_pci_status *p = opaque;
+    ICH9_DEBUG("%x <== %d\n", addr, val);
+    return p->bus;
+}
+
+static void pci_hp_bus_write(void *opaque, uint32_t addr, uint32_t val)
+{
+    struct ich9_pci_status *p = opaque;
+    p->bus = val;
+    ICH9_DEBUG("%x <== %d\n", addr, val);
+}
+
+static void enable_device(struct ich9_pci_status *p, int bus, int slot)
+{
+    p->up[bus] |= (1 << slot);
+}
+
+static void disable_device(struct ich9_pci_status *p, int bus, int slot)
+{
+    p->down[bus] |= (1 << slot);
+}
+
+#define ACPI_SCI_L1 0x2
+
+static void ich9_device_hot_add(int bus, int slot, int state, void *opaque)
+{
+    struct ich9_lpc_pm_regs *pm = opaque;
+    struct ich9_pci_status *pci_status = pm->pci_status;
+
+    pci_status->up[bus] = 0;
+    pci_status->down[bus] = 0;
+
+    if (state)
+        enable_device(pci_status, bus, slot);
+    else
+        disable_device(pci_status, bus, slot);
+
+    pm->gpe0_sts |= ACPI_SCI_L1;
+    if (pm->gpe0_en & ACPI_SCI_L1) {
+        qemu_set_irq(pm->irq, 1);
+        qemu_set_irq(pm->irq, 0);
+    }
+}
+
+void ich9_hot_add_init(struct ich9_lpc_pm_regs *pm)
+{
+    struct ich9_pci_status *pci_status;
+    pci_status = qemu_mallocz(sizeof(*pci_status));
+
+    register_ioport_write(PCI_HP_BASE, 8, 4, pcihotplug_write, pci_status);
+    register_ioport_read(PCI_HP_BASE, 8, 4,  pcihotplug_read, pci_status);
+
+    register_ioport_write(PCI_EJ_BASE, 4, 4, pciej_write, pci_status);
+    register_ioport_read(PCI_EJ_BASE, 4, 4,  pciej_read, pci_status);
+
+    register_ioport_write(PCI_HP_BUS, 1, 1, pci_hp_bus_write, pci_status);
+    register_ioport_read(PCI_HP_BUS, 1, 1,  pci_hp_bus_read, pci_status);
+
+    pm->pci_status = pci_status;
+    qemu_system_device_hot_add_register(ich9_device_hot_add, pm);
+}
diff --git a/hw/acpi_ich9.h b/hw/acpi_ich9.h
new file mode 100644
index 0000000..7ebb710
--- /dev/null
+++ b/hw/acpi_ich9.h
@@ -0,0 +1,57 @@
+/*
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA  02110-1301 USA
+ */
+
+#ifndef HW_ACPI_ICH9_H
+#define HW_ACPI_ICH9_H
+
+#include <stdint.h>
+
+struct ich9_pci_status;
+
+struct ich9_lpc_pm_regs {
+    uint16_t pm1_sts;
+    uint16_t pm1_en;
+
+    /*
+     * 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.
+     */
+    uint16_t pm1_cnt;
+
+    /* uint32_t pm1_tmr; */
+    QEMUTimer *tmr_timer;
+    int64_t tmr_overflow_time;
+
+    uint64_t gpe0_sts;
+    uint64_t gpe0_en;
+
+    uint32_t smi_en;
+    uint32_t smi_sts;
+
+    qemu_irq irq;      /* SCI */
+
+    uint32_t pm_io_base;
+
+    struct ich9_pci_status *pci_status;
+};
+
+#endif /* HW_ACPI_ICH9_H */
diff --git a/hw/pc_q35.c b/hw/pc_q35.c
new file mode 100644
index 0000000..9980a39
--- /dev/null
+++ b/hw/pc_q35.c
@@ -0,0 +1,220 @@
+/*
+ *  Q35 chipset based pc system emulator
+ *
+ *  Copyright (c) 2009 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA  02110-1301 USA
+ */
+/*
+ * 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.
+ */
+#include "hw.h"
+#include "pc.h"
+#include "fdc.h"
+#include "pci.h"
+#include "pci_bridge.h"
+#include "block.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 "q35.h"
+
+#define MAX_IDE_BUS 2
+
+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 pc_q35_bridge_init(PCIBus *host_bus, PCIBus *pci_bus)
+{
+    int dev;
+
+    /* PCI to PCI bridge b6:d[29 - 31]:f0, 6:[1d - 1f].0 with subordinate bus
+       of 7 - 9 on b0:d30:f0, 0.1e.0 = pci_bus */
+#define Q35_P2P_BRDIGE_DEV_BASE         29
+#define Q35_P2P_BRDIGE_DEV_MAX          32
+#define Q35_P2P_BRDIGE_SUBBUS_BASE      7
+    for (dev = Q35_P2P_BRDIGE_DEV_BASE; dev < Q35_P2P_BRDIGE_DEV_MAX; dev++) {
+        int sec_bus =
+            Q35_P2P_BRDIGE_SUBBUS_BASE + dev - Q35_P2P_BRDIGE_DEV_BASE;
+        int sub_bus = sec_bus;
+
+        i82801ba11_init(pci_bus, PCI_DEVFN(dev, 0), sec_bus, sub_bus);
+    }
+
+    /* PCIe root port b0:d1:f0, 0:01.0 with subordinate bus of 63 */
+
+    /* PCIe root port b0:d23:f[0-5], 0.17.[0-5] with subordinate bus of 64 */
+
+    /* PCIe upstream port b0:d24:f0, 0.18.0 with subordinate bus of 126 */
+
+    /* PCIe downstream port b126:d[0-15]:f[0-7], 7e:[0-1f].[0-7] with
+       subordinate bus of 127 - 127 + 128 on bus 126 */
+}
+
+/* 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)
+{
+    int i;
+    ram_addr_t below_4g_mem_size, above_4g_mem_size;
+    PCIBus *host_bus;
+    PCIBus *pci_bus;
+    ISADevice *isa_dev;
+    static PCIDevice *gmch_state;
+    int ich9_lpc_devfn = -1;
+    qemu_irq *cpu_irq;
+    qemu_irq *isa_irq;
+    qemu_irq *i8259;
+    IsaIrqState *isa_irq_state;
+    DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
+    fdctrl_t *floppy_controller;
+    RTCState *rtc_state;
+
+    pc_cpus_init(cpu_model);
+
+    vmport_init();
+
+    /* allocate ram and load rom/bios */
+    pc_memory_init(ram_size, kernel_filename, kernel_cmdline, initrd_filename,
+                   &below_4g_mem_size, &above_4g_mem_size);
+
+
+    cpu_irq = pc_allocate_cpu_irq();
+    i8259 = i8259_init(cpu_irq[0]);
+    isa_irq_state = qemu_mallocz(sizeof(*isa_irq_state));
+    isa_irq_state->i8259 = i8259;
+    isa_irq_state->ioapic = ioapic_init();
+    isa_irq = qemu_allocate_irqs(isa_irq_handler, isa_irq_state, 24);
+
+    host_bus = gmch_init(&gmch_state, isa_irq, &ich9_lpc_devfn);
+
+    isa_bus_irqs(isa_irq);
+    pc_register_ferr_irq(isa_reserve_irq(13));
+
+    pci_bus = ich9_d2pbr_init(host_bus, PCI_DEVFN(ICH9_D2P_BRIDGE_DEV,
+                                                  ICH9_D2P_BRIDGE_FUNC),
+                              ICH9_D2P_SECONDARY_DEFAULT);
+    pci_set_default_bus(pci_bus);
+
+    pc_vga_init(host_bus);
+
+    /* init basic PC hardware */
+    pc_basic_device_init(isa_irq, &isa_dev, &floppy_controller, &rtc_state);
+
+    pc_q35_bridge_init(host_bus, pci_bus);
+
+    for(i = 0; i < nb_nics; i++) {
+        NICInfo *nd = &nd_table[i];
+
+        pci_nic_init(nd, "e1000", NULL);
+    }
+
+    if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) {
+        fprintf(stderr, "qemu: too many IDE bus\n");
+        exit(1);
+    }
+
+    for(i = 0; i < MAX_IDE_BUS * MAX_IDE_DEVS; i++) {
+        hd[i] = drive_get(IF_IDE, i / MAX_IDE_DEVS, i % MAX_IDE_DEVS);
+    }
+
+    /* XXX ich9 supports only SATA */
+    pci_piix3_ide_init(pci_bus, hd, -1);
+
+#ifdef HAS_AUDIO
+    pc_audio_init(pci_bus, isa_irq);
+#endif
+
+    pc_cmos_init(below_4g_mem_size, above_4g_mem_size, boot_device, hd,
+                 floppy_controller, rtc_state);
+
+    if (usb_enabled) {
+        /* XXX: make ich9 specific add usb_uchi_ich9_init() */
+        usb_uhci_piix4_init(pci_bus, -1);
+    }
+
+    if (acpi_enabled) {
+        uint8_t *eeprom_buf = qemu_mallocz(8 * 256); /* XXX: make this 
persistent */
+        i2c_bus *smbus;
+
+        /* TODO: Populate SPD eeprom data.  */
+        /* XXX determine proper smb_io_base */
+        smbus = ich9_smb_init(host_bus, ich9_lpc_devfn + 3, 0xb100);
+        for (i = 0; i < 8; i++) {
+            DeviceState *eeprom;
+            eeprom = qdev_create((BusState *)smbus, "smbus-eeprom");
+            qdev_prop_set_uint32(eeprom, "address", 0x50 + i);
+            qdev_prop_set_ptr(eeprom, "data", eeprom_buf + (i * 256));
+            qdev_init(eeprom);
+        }
+    }
+
+    if (gmch_state) {
+        gmch_init_memory_mappings(gmch_state);
+    }
+
+    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 7eef2cd..4eb6d07 100644
--- a/hw/pci_ids.h
+++ b/hw/pci_ids.h
@@ -33,6 +33,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
@@ -109,4 +110,16 @@
 #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_Q35_MCH      0x29c0
+
 #define PCI_VENDOR_ID_INVALID            0xffff
diff --git a/hw/q35.c b/hw/q35.c
new file mode 100644
index 0000000..8e07048
--- /dev/null
+++ b/hw/q35.c
@@ -0,0 +1,731 @@
+/*
+ * QEMU GMCH/ICH9 PCI Bridge Emulation
+ *
+ *  Copyright (c) 2009 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA  02110-1301 USA
+ */
+/*
+ * 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.
+ */
+
+#include "hw.h"
+#include "isa.h"
+#include "sysbus.h"
+#include "pc.h"
+#include "pc_apm.h"
+#include "pci.h"
+#include "pci_bridge.h"
+#include "q35.h"
+#include "acpi.h"
+
+typedef uint32_t pci_addr_t;
+#include "pci_host_c.h"
+
+struct ICH9_LPCState {
+    /* ICH9 LPC PCI to ISA bridge */
+    PCIDevice d;
+
+    int pci_irq_levels[ICH9_LPC_NB_PIRQS];
+
+    APMState apm;
+    struct ich9_lpc_pm_regs pm;
+};
+
+struct ICH9_LPCIrqState {
+    struct ICH9_LPCState *lpc;
+    qemu_irq *pic;
+};
+
+typedef struct GMCHState {
+    PCIExpressHost      host;
+
+    struct PCIDevice    *dev;
+} GMCHState;
+
+struct GMCH_PCIState {
+    PCIDevice   d;
+    //GMCHState   *gmch;
+    // PCIDevice -> qdev -> parent_bus -upcast-> GMCHState
+
+    target_phys_addr_t  isa_page_descs[384 / 4];
+    uint8_t             smm_enabled;
+
+    struct ICH9_LPCIrqState *irq_state;
+};
+
+static GMCHState *gmchstate_from_sysbus(SysBusDevice *dev)
+{
+    PCIHostState *pci = FROM_SYSBUS(PCIHostState, dev);
+    PCIExpressHost *pcie = DO_UPCAST(PCIExpressHost, pci, pci);
+    return DO_UPCAST(GMCHState, host, pcie);
+}
+
+/* pci */
+static void gmch_addr_writel(void* opaque, uint32_t addr, uint32_t val)
+{
+    GMCHState *s = opaque;
+    s->host.pci.config_reg = val;
+}
+
+static uint32_t gmch_addr_readl(void* opaque, uint32_t addr)
+{
+    GMCHState *s = opaque;
+    return s->host.pci.config_reg;
+}
+
+/* PCIE MMCFG */
+static void gmch_update_pcixbar(struct GMCH_PCIState *gs)
+{
+    PCIDevice* pci_dev = &gs->d;
+    BusState *bus = qdev_get_parent_bus(&pci_dev->qdev);
+    DeviceState *qdev = bus->parent;
+    GMCHState *s = gmchstate_from_sysbus(sysbus_from_qdev(qdev));
+
+    uint64_t pciexbar;
+    int enable;
+    uint64_t addr;
+    uint64_t addr_mask;
+    uint32_t length;
+
+    pciexbar = pci_config_get_quad(pci_dev, Q35_HOST_BRIDGE_PCIEXBAR);
+    enable = pciexbar & Q35_HOST_BRIDGE_PCIEXBAREN;
+
+    addr_mask = Q35_HOST_BRIDGE_PCIEXBAR_ADMSK;
+    switch (pciexbar & Q35_HOST_BRIDGE_PCIEXBAR_LENGTH_MASK) {
+    case Q35_HOST_BRIDGE_PCIEXBAR_LENGTH_256M:
+        length = 256 * 1024 * 1024;
+        break;
+    case Q35_HOST_BRIDGE_PCIEXBAR_LENGTH_128M:
+        length = 128 * 1024 * 1024;
+        addr_mask |= Q35_HOST_BRIDGE_PCIEXBAR_128ADMSK |
+            Q35_HOST_BRIDGE_PCIEXBAR_64ADMSK;
+        break;
+    case Q35_HOST_BRIDGE_PCIEXBAR_LENGTH_64M:
+        length = 64 * 1024 * 1024;
+        addr_mask |= Q35_HOST_BRIDGE_PCIEXBAR_64ADMSK;
+        break;
+    case Q35_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);
+}
+
+/* ich9 irq */
+static struct ICH9_LPCState *ich9_lpc_init(PCIBus *bus, int devfn, struct 
ICH9_LPCIrqState *irq_state);
+static void ich9_lpc_set_irq(void *opaque, int irq_num, int level);
+
+/* return the global irq number corresponding to a given device irq
+   pin. We could also use the bus number to have a more precise
+   mapping. */
+static int pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num)
+{
+    return pci_swizzle_map_irq_fn(pci_dev, irq_num);
+}
+
+/* PAM */
+
+/* XXX: suppress when better memory API. We make the assumption that
+   no device (in particular the VGA) changes the memory mappings in
+   the 0xa0000-0x100000 =
+   [Q35_HOST_BRIDGE_SMARM_C_BASE, Q35_HOST_BRIDGE_UPPER_SYSTEM_BIOS_END)
+   range */
+void gmch_init_memory_mappings(PCIDevice *d)
+{
+    struct GMCH_PCIState *gs = DO_UPCAST(struct GMCH_PCIState, d, d);
+    int i;
+    for(i = 0; i < 96; i++) {
+        gs->isa_page_descs[i] = cpu_get_physical_page_desc(
+            Q35_HOST_BRIDGE_SMARM_C_BASE + i * 0x1000);
+    }
+}
+
+static target_phys_addr_t isa_page_descs_get(struct GMCH_PCIState *gs,
+                                             uint32_t addr)
+{
+    return gs->isa_page_descs[
+        (addr - Q35_HOST_BRIDGE_SMARM_C_BASE) >> TARGET_PAGE_BITS];
+}
+
+static void update_pam(struct GMCH_PCIState *gs,
+                       uint32_t start, uint32_t size, uint8_t r)
+{
+    uint32_t addr;
+
+#if 0
+    printf("ISA mapping %08"PRIx32"-0x%08"PRIx32": %"PRId32"\n",
+           start, start + size, r);
+#endif
+    switch(r) {
+    case Q35_HOST_BRIDGE_PAM_WE | Q35_HOST_BRIDGE_PAM_RE:
+        /* RAM */
+        cpu_register_physical_memory(start, size, start);
+        break;
+
+    case Q35_HOST_BRIDGE_PAM_RE:
+        /* ROM (XXX: not quite correct) */
+        cpu_register_physical_memory(start, size, start | IO_MEM_ROM);
+        break;
+
+    case Q35_HOST_BRIDGE_PAM_WE:
+    case 0:
+        /* XXX: should distinguish read/write cases */
+        for(addr = start; addr < start + size; addr += TARGET_PAGE_SIZE) {
+            cpu_register_physical_memory(addr, TARGET_PAGE_SIZE,
+                                         isa_page_descs_get(gs, addr));
+        }
+        break;
+
+    default:
+        abort();
+        break;
+    }
+}
+
+static uint8_t gmch_get_pam_attr(PCIDevice *d, uint32_t addr, int hi)
+{
+    return (d->config[addr] >> ((!!hi) * 4)) & Q35_HOST_BRIDGE_PAM_MASK;
+}
+
+static void gmch_update_pam(struct GMCH_PCIState *gs, int pam)
+{
+    PCIDevice *d = &gs->d;
+    uint32_t conf_addr = Q35_HOST_BRIDGE_PAM0 + pam;
+    uint32_t phys_addr;
+
+    assert(0 <= pam && pam <= 6);
+
+    if (pam == 0) {
+        update_pam(gs, Q35_HOST_BRIDGE_PAM_BIOS_AREA,
+                   Q35_HOST_BRIDGE_PAM_AREA_SIZE,
+                   gmch_get_pam_attr(d, conf_addr, 1));
+        return;
+    }
+
+    phys_addr = Q35_HOST_BRIDGE_PAM_EXPAN_AREA +
+        Q35_HOST_BRIDGE_PAM_EXPAN_SIZE * (pam - 1) * 2;
+    update_pam(gs, phys_addr, Q35_HOST_BRIDGE_PAM_EXPAN_SIZE,
+               gmch_get_pam_attr(d, conf_addr, 0));
+
+    phys_addr += Q35_HOST_BRIDGE_PAM_AREA_SIZE;
+    update_pam(gs, phys_addr, Q35_HOST_BRIDGE_PAM_EXPAN_SIZE,
+               gmch_get_pam_attr(d, conf_addr, 1));
+}
+
+static void gmch_update_memory_mappings(struct GMCH_PCIState *gs)
+{
+    int i;
+
+    for (i = 0; i < Q35_HOST_BRIDGE_PAM_NB; i++)
+        gmch_update_pam(gs, i);
+}
+
+/* SMRAM */
+static void gmch_update_smram(struct GMCH_PCIState *gs)
+{
+    uint32_t smram;
+    uint32_t addr;
+
+    smram = gs->d.config[Q35_HOST_BRDIGE_SMRAM];
+    if ((gs->smm_enabled && (smram & Q35_HOST_BRIDGE_SMRAM_G_SMRAME)) ||
+        (smram & Q35_HOST_BRIDGE_SMRAM_D_OPEN)) {
+        cpu_register_physical_memory(Q35_HOST_BRIDGE_SMARM_C_BASE,
+                                     Q35_HOST_BRIDGE_SMRAM_C_SIZE,
+                                     Q35_HOST_BRIDGE_SMARM_C_BASE);
+    } else {
+        for(addr = Q35_HOST_BRIDGE_SMARM_C_BASE;
+            addr < Q35_HOST_BRIDGE_SMRAM_C_END;
+            addr += TARGET_PAGE_SIZE) {
+            cpu_register_physical_memory(addr, TARGET_PAGE_SIZE,
+                                         isa_page_descs_get(gs, addr));
+        }
+    }
+}
+
+static void gmch_set_smm(int smm, void *arg)
+{
+    uint8_t val = (smm != 0);
+    struct GMCH_PCIState *gs = (struct GMCH_PCIState*)arg;
+
+    if (gs->smm_enabled != val) {
+        gs->smm_enabled = val;
+        gmch_update_smram(gs);
+    }
+}
+
+static void gmch_write_config(PCIDevice *d,
+                                uint32_t address, uint32_t val, int len)
+{
+    struct GMCH_PCIState *gs = DO_UPCAST(struct GMCH_PCIState, d, d);
+
+    /* XXX: implement SMRAM.D_LOCK */
+    pci_default_write_config(d, address, val, len);
+
+    if (pci_config_changed(address, len,
+                           Q35_HOST_BRIDGE_PAM0, Q35_HOST_BRIDGE_PAM6))
+        gmch_update_memory_mappings(gs);
+
+    if (pci_config_changed_with_size(address, len, Q35_HOST_BRIDGE_PCIEXBAR,
+                                     Q35_HOST_BRIDGE_PCIEXBAR_SIZE))
+        gmch_update_pcixbar(gs);
+
+    if (pci_config_changed_with_size(address, len, Q35_HOST_BRDIGE_SMRAM,
+                                     Q35_HOST_BRDIGE_SMRAM_SIZE))
+        gmch_update_smram(gs);
+}
+
+static int gmch_post_load(void *opaque)
+{
+    struct GMCH_PCIState *gs = opaque;
+    gmch_update_memory_mappings(gs);
+    gmch_update_smram(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, struct GMCH_PCIState),
+        VMSTATE_UINT8(smm_enabled, struct GMCH_PCIState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static int gmch_pcihost_initfn(SysBusDevice *dev)
+{
+    GMCHState *s = gmchstate_from_sysbus(dev);
+
+    register_ioport_write(Q35_HOST_BRIDGE_CONFIG_ADDR,
+                          4, 4, gmch_addr_writel, s);
+    register_ioport_read(Q35_HOST_BRIDGE_CONFIG_ADDR,
+                         4, 4, gmch_addr_readl, s);
+
+    register_ioport_write(Q35_HOST_BRIDGE_CONFIG_DATA,
+                          4, 1, pci_host_data_writeb, s);
+    register_ioport_write(Q35_HOST_BRIDGE_CONFIG_DATA,
+                          4, 2, pci_host_data_writew, s);
+    register_ioport_write(Q35_HOST_BRIDGE_CONFIG_DATA,
+                          4, 4, pci_host_data_writel, s);
+    register_ioport_read(Q35_HOST_BRIDGE_CONFIG_DATA,
+                         4, 1, pci_host_data_readb, s);
+    register_ioport_read(Q35_HOST_BRIDGE_CONFIG_DATA,
+                         4, 2, pci_host_data_readw, s);
+    register_ioport_read(Q35_HOST_BRIDGE_CONFIG_DATA,
+                         4, 4, pci_host_data_readl, s);
+
+    if (pcie_host_init(&s->host, NULL, NULL) < 0)
+        abort();
+
+    return 0;
+}
+
+static int gmch_initfn(PCIDevice *d)
+{
+    struct GMCH_PCIState *gs = DO_UPCAST(struct GMCH_PCIState, d, 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);
+    d->config[PCI_REVISION_ID] = Q35_HOST_BRIDGE_REVISION_DEFUALT;
+    pci_config_set_class(d->config, PCI_CLASS_BRIDGE_HOST);
+    d->config[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL;
+
+    pci_config_set_quad(d, Q35_HOST_BRIDGE_PCIEXBAR,
+                        Q35_HOST_BRIDGE_PCIEXBAR_DEFAULT);
+    gmch_update_pcixbar(gs);
+
+    d->config[Q35_HOST_BRDIGE_SMRAM] = Q35_HOST_BRIDGE_SMRAM_DEFAULT;
+
+    vmstate_register(0, &vmstate_gmch, gs);
+    cpu_smm_register(&gmch_set_smm, gs);
+
+    return 0;
+}
+
+/* host bridge */
+PCIBus *gmch_init(PCIDevice **pgmch_state, qemu_irq *pic, int *ich9_lpc_devfn)
+{
+    DeviceState *dev;
+    GMCHState *s;
+    PCIBus *b;
+    PCIDevice *d;
+    struct GMCH_PCIState *gs;
+    struct ICH9_LPCIrqState *irq_state = qemu_malloc(sizeof(*irq_state));
+
+    irq_state->pic = pic;
+    dev = qdev_create(NULL, "gmch-pcihost");
+    s = gmchstate_from_sysbus(sysbus_from_qdev(dev));
+    b = pci_register_bus(dev, "pci.0",
+                         ich9_lpc_set_irq, pci_slot_get_pirq, irq_state, 0,
+                         24 /* 24 pin IO APIC */);
+    s->host.pci.bus = b;
+    qdev_init(dev);
+
+    d = pci_create_simple(b, 0, "gmch");
+    s->dev = d;
+    gs = DO_UPCAST(struct GMCH_PCIState, d, d);
+    gs->irq_state = irq_state;
+    *pgmch_state = d;
+
+    irq_state->lpc = ich9_lpc_init(b, PCI_DEVFN(ICH9_LPC_DEV, ICH9_LPC_FUNC),
+                                   irq_state);
+    *ich9_lpc_devfn = irq_state->lpc->d.devfn;
+    return b;
+}
+
+static SysBusDeviceInfo gmch_pcihost_info = {
+    .init         = gmch_pcihost_initfn,
+    .qdev.name    = "gmch-pcihost",
+    .qdev.size    = sizeof(GMCHState),
+    .qdev.no_user = 1,
+    .qdev.props = (Property[]) {
+        {
+            .name = "MCFG",
+            .info = &qdev_prop_uint64,
+            .offset = offsetof(GMCHState, host.base_addr),
+            .defval = (uint64_t[]){ Q35_HOST_BRIDGE_PCIEXBAR_DEFAULT },
+        },
+        {/* end of list */}
+    },
+};
+
+static PCIDeviceInfo gmch_info = {
+    .qdev.name    = "gmch",
+    .qdev.desc    = "Host bridge",
+    .qdev.size    = sizeof(struct GMCH_PCIState),
+    .qdev.no_user = 1,
+    .init         = gmch_initfn,
+    .config_write = gmch_write_config,
+};
+
+static void gmch_register(void)
+{
+    sysbus_register_withprop(&gmch_pcihost_info);
+    pci_qdev_register(&gmch_info);
+}
+device_init(gmch_register);
+
+/*****************************************************************************/
+/* ICH9 DMI-to-PCI bridge */
+static void ich9_d2pbr_reset(PCIDevice *d)
+{
+    pci_config_set_word(d, PCI_COMMAND,
+                        PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
+                        PCI_COMMAND_MASTER);
+    pci_config_set_word(d, PCI_STATUS, PCI_STATUS_DEVSEL_MEDIUM);
+}
+
+PCIBus *ich9_d2pbr_init(PCIBus *bus, int devfn, int secondary_bus_num)
+{
+    PCIBus *b;
+    PCIDevice *d;
+
+    b = pci_bridge_create_simple(bus, devfn,
+                                 PCI_VENDOR_ID_INTEL,
+                                 PCI_DEVICE_ID_INTEL_82801BA_11,
+                                 secondary_bus_num,
+                                 secondary_bus_num,
+                                 pci_swizzle_map_irq_fn,
+                                 "Intel ich9 dmi to pci bridge",
+                                 PCI_BRIDGE_INTEL_82801BA_11);
+    if (b == NULL)
+        return NULL;
+
+    d = pci_bus_to_dev(b);
+    vmstate_register(0, &vmstate_pci_device, d); /* current s2pbr doesn't have
+                                                    specfic bridge status */
+
+
+    /* some command bits are hotwired to 0 for pcie */
+    pci_conf_initw(d, PCI_COMMAND,
+                   PCI_COMMAND_IO |
+                   PCI_COMMAND_MEMORY |
+                   PCI_COMMAND_MASTER |
+                   PCI_COMMAND_PARITY |
+                   PCI_COMMAND_SERR);
+
+    pci_config_set_byte(d, PCI_REVISION_ID, ICH9_D2P_A2_REVISION);
+
+    // XXX
+    pci_config_set_byte(d, PCI_CLASS_PROG, PCI_CLASS_BRDIGE_PCI_INF_SUB);
+    pci_config_set_byte(d, PCI_HEADER_TYPE, PCI_HEADER_TYPE_BRIDGE);
+
+    ich9_d2pbr_reset(d);
+    return b;
+}
+
+
+/*****************************************************************************/
+/* ICH9 LPC PCI to ISA bridge */
+
+static struct ICH9_LPCState *ich9_pci_to_lpc(PCIDevice *d)
+{
+    return DO_UPCAST(struct ICH9_LPCState, d, d);
+}
+
+static void ich9_lpc_reset(struct ICH9_LPCState *lpc)
+{
+    PCIDevice *d = &lpc->d;
+    int i;
+
+    pci_config_set_word(d, PCI_COMMAND,
+                        PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
+                        PCI_COMMAND_MASTER);
+
+    /* XXX capability isn't supported yet */
+    pci_config_set_word(d, PCI_STATUS,
+                        PCI_STATUS_DEVSEL_MEDIUM /* | PCI_STATUS_CAP_LIST */);
+
+    for (i = 0; i < 4; i++) {
+        pci_config_set_byte(d, ICH9_LPC_PIRQA_ROUT + i,
+                            ICH9_LPC_PIRQ_ROUT_DEFAULT);
+    }
+    for (i = 0; i < 4; i++) {
+        pci_config_set_byte(d, ICH9_LPC_PIRQE_ROUT + i,
+                            ICH9_LPC_PIRQ_ROUT_DEFAULT);
+    }
+    pci_config_set_long(d, ICH9_LPC_PMBASE, ICH9_LPC_PMBASE_DEFAULT);
+    pci_config_set_long(d, ICH9_LPC_RCBA, ICH9_LPC_RCBA_DEFAULT);
+}
+
+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(struct 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();
+}
+
+static void ich9_lpc_set_irq(void *opaque, int irq_num, int level)
+{
+    struct ICH9_LPCIrqState *irq_state = opaque;
+    struct ICH9_LPCState *ich9_lpc = irq_state->lpc;
+    qemu_irq *pic = irq_state->pic;
+    int i, pic_level;
+    int pic_irq;
+    int pic_dis;
+
+    assert(0 <= irq_num && irq_num < 8);
+    ich9_lpc->pci_irq_levels[irq_num] = level;
+
+    /* now we change the pic irq level according to
+       the ich9 lpc irq mappings */
+    ich9_lpc_pic_irq(ich9_lpc, irq_num, &pic_irq, &pic_dis);
+    if (pic_dis) {
+        return;
+    }
+
+    if (pic_irq > 24) {
+        fprintf(stderr, "%s: error irq_num %d pic_irq %d\n",
+                __func__, irq_num, pic_irq);
+        return;
+    }
+
+    /* 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(ich9_lpc, i, &tmp_irq, &tmp_dis);
+        if (!tmp_dis && pic_irq == tmp_irq) {
+            pic_level |= ich9_lpc->pci_irq_levels[i];
+        }
+    }
+    qemu_set_irq(pic[pic_irq], pic_level);
+}
+
+/* APM */
+static void ich9_apm_ctrl_changed(uint32_t val, void *arg)
+{
+    struct ICH9_LPCState *lpc = arg;
+
+    /* ACPI specs 3.0, 4.7.2.5 */
+    if (val == ICH9_APM_ACPI_ENABLE) {
+        lpc->pm.pm1_cnt |= ACPI_BITMASK_SCI_ENABLE;
+    } else if (val == ICH9_APM_ACPI_DISABLE) {
+        lpc->pm.pm1_cnt &= ~ACPI_BITMASK_SCI_ENABLE;
+    }
+
+    /* 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(PCIDevice *d)
+{
+    uint32_t pm_io_base = pci_config_get_long(d, ICH9_LPC_PMBASE);
+    pm_io_base &= ICH9_LPC_PMBASE_BASE_ADDRESS_MASK;
+
+    ich9_pm_iospace_update(&ich9_pci_to_lpc(d)->pm, pm_io_base);
+}
+
+static int ich9_lpc_post_load(void *opaque)
+{
+    struct ICH9_LPCState *lpc = opaque;
+
+    ich9_lpc_pmbase_update(&lpc->d);
+    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, struct ICH9_LPCState),
+        VMSTATE_INT32_ARRAY(pci_irq_levels, struct ICH9_LPCState,
+                            ICH9_LPC_NB_PIRQS),
+        VMSTATE_STRUCT(apm, struct ICH9_LPCState, 0,
+                       vmstate_pc_apm, APMState),
+        VMSTATE_STRUCT(pm, struct ICH9_LPCState, 0,
+                       vmstate_ich9_pm, struct ich9_lpc_pm_regs),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void ich9_set_sci(void *opaque, int irq_num, int level)
+{
+    /* opaque = pic */
+    assert(irq_num == 0);
+    ich9_lpc_set_irq(opaque, 1/* SCI = PIRQB# */, level);
+}
+
+static qemu_irq *ich9_lpc_allocate_sci_irq(struct ICH9_LPCIrqState *irq_state)
+{
+    return qemu_allocate_irqs(ich9_set_sci, irq_state, 1);
+}
+
+static void ich9_lpc_config_write( PCIDevice *d,
+                                   uint32_t addr, uint32_t val, int len)
+{
+    pci_default_write_config(d, addr, val, len);
+    if (pci_config_changed_with_size(addr, len, ICH9_LPC_PMBASE, 4)) {
+        ich9_lpc_pmbase_update(d);
+    }
+}
+
+static int ich9_lpc_initfn(PCIDevice *d)
+{
+    struct ICH9_LPCState *lpc = ich9_pci_to_lpc(d);
+    //ich9_lpc_set_irq_register(lpc); /* for ich9_lpc_set_irq XXX */
+
+    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_byte(d, PCI_REVISION_ID, ICH9_A2_LPC_REVISION);
+    pci_config_set_class(d->config, PCI_CLASS_BRIDGE_ISA);
+
+    /* header_type = PCI_multifunction, generic */
+    pci_config_set_byte(d, PCI_HEADER_TYPE,
+                        PCI_HEADER_TYPE_NORMAL |
+                        PCI_HEADER_TYPE_MULTI_FUNCTION);
+
+    apm_init(&lpc->apm, ich9_apm_ctrl_changed, lpc);
+
+
+    pci_conf_initl(d, ICH9_LPC_PMBASE, ICH9_LPC_PMBASE_BASE_ADDRESS_MASK);
+    pci_config_set_long(d, ICH9_LPC_PMBASE, ICH9_LPC_PMBASE_DEFAULT);
+
+    vmstate_register(0, &vmstate_ich9_lpc, lpc);
+
+    return 0;
+}
+
+static struct ICH9_LPCState *
+ich9_lpc_init(PCIBus *bus, int devfn, struct ICH9_LPCIrqState *irq_state)
+{
+    PCIDevice *d;
+    struct ICH9_LPCState *lpc;
+    qemu_irq *sci_irq;
+
+    assert(devfn == PCI_DEVFN(ICH9_LPC_DEV, ICH9_LPC_FUNC));
+    d = pci_create_simple(bus, devfn, "ICH9 LPC");
+    lpc = ich9_pci_to_lpc(d);
+
+    sci_irq = ich9_lpc_allocate_sci_irq(irq_state);
+    ich9_pm_init(&lpc->pm, sci_irq[0]);
+    ich9_lpc_reset(lpc);
+    ich9_hot_add_init(&lpc->pm);
+
+    return lpc;
+}
+
+static PCIDeviceInfo ich9_lpc_info = {
+    .qdev.name    = "ICH9 LPC",
+    .qdev.desc    = "ICH9 LPC bridge",
+    .qdev.size    = sizeof(struct ICH9_LPCState),
+    .qdev.no_user = 1,
+    .init         = ich9_lpc_initfn,
+    .config_write = ich9_lpc_config_write,
+};
+
+
+static void ich9_lpc_register(void)
+{
+    pci_qdev_register(&ich9_lpc_info);
+}
+
+device_init(ich9_lpc_register);
diff --git a/hw/q35.h b/hw/q35.h
new file mode 100644
index 0000000..a3a97b5
--- /dev/null
+++ b/hw/q35.h
@@ -0,0 +1,228 @@
+/*
+ * 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 General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef HW_Q35_H
+#define HW_Q35_H
+
+#include "acpi_ich9.h"
+
+void gmch_init_memory_mappings(PCIDevice *d);
+PCIBus *gmch_init(PCIDevice **pgmch_state, qemu_irq *pic, int *ich9_lpc_devfn);
+
+PCIBus *ich9_d2pbr_init(PCIBus *bus, int devfn, int secondary_bus_num);
+
+i2c_bus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base);
+
+void ich9_pm_init(struct ich9_lpc_pm_regs *pm, qemu_irq sci_irq);
+void ich9_pm_iospace_update(struct ich9_lpc_pm_regs *pm, uint32_t pm_io_base);
+extern const VMStateDescription vmstate_ich9_pm;
+
+void ich9_hot_add_init(struct ich9_lpc_pm_regs *pm);
+
+#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 Q35_HOST_BRIDGE                         "GMCH"
+
+#define Q35_HOST_BRIDGE_CONFIG_ADDR             0xcf8
+#define Q35_HOST_BRIDGE_CONFIG_DATA             0xcfc
+
+/* D0:F0 configuration space */
+#define  Q35_HOST_BRIDGE_REVISION_DEFUALT       0x0
+
+#define Q35_HOST_BRIDGE_PCIEXBAR                0x60    /* 64bit register */
+#define  Q35_HOST_BRIDGE_PCIEXBAR_SIZE          8       /* 64bit register */
+#define  Q35_HOST_BRIDGE_PCIEXBAR_DEFAULT       0xe0000000
+//#define  Q35_HOST_BRIDGE_PCIEXBAR_DEFAULT       (0xe0000000 | 
Q35_HOST_BRIDGE_PCIEXBAREN)
+//#define  Q35_HOST_BRIDGE_PCIEXBAR_DEFAULT       (0xf0000000 | 
Q35_HOST_BRIDGE_PCIEXBAREN)
+//#define  Q35_HOST_BRIDGE_PCIEXBAR_DEFAULT       (0xf4000000 | 
Q35_HOST_BRIDGE_PCIEXBAREN)
+#define  Q35_HOST_BRIDGE_PCIEXBAR_ADMSK         Q35_MASK(64, 35, 25)    /* bit 
35:28 */
+#define  Q35_HOST_BRIDGE_PCIEXBAR_128ADMSK      ((uint64_t)(1 << 26))
+#define  Q35_HOST_BRIDGE_PCIEXBAR_64ADMSK       ((uint64_t)(1 << 25))
+#define  Q35_HOST_BRIDGE_PCIEXBAR_LENGTH_MASK   ((uint64_t)(0x3 << 1))
+#define  Q35_HOST_BRIDGE_PCIEXBAR_LENGTH_256M   ((uint64_t)(0x0 << 1))
+#define  Q35_HOST_BRIDGE_PCIEXBAR_LENGTH_128M   ((uint64_t)(0x1 << 1))
+#define  Q35_HOST_BRIDGE_PCIEXBAR_LENGTH_64M    ((uint64_t)(0x2 << 1))
+#define  Q35_HOST_BRIDGE_PCIEXBAR_LENGTH_RVD    ((uint64_t)(0x3 << 1))
+#define  Q35_HOST_BRIDGE_PCIEXBAREN             ((uint64_t)1)
+
+#define Q35_HOST_BRIDGE_PAM_NB                  7
+#define Q35_HOST_BRIDGE_PAM0                    0x90
+#define  Q35_HOST_BRIDGE_PAM_BIOS_AREA          0xf0000
+#define  Q35_HOST_BRIDGE_PAM_AREA_SIZE          0x10000 /* 16KB */
+#define Q35_HOST_BRIDGE_PAM1                    0x91
+#define  Q35_HOST_BRIDGE_PAM_EXPAN_AREA         0xc0000
+#define  Q35_HOST_BRIDGE_PAM_EXPAN_SIZE         0x04000
+#define Q35_HOST_BRIDGE_PAM2                    0x92
+#define Q35_HOST_BRIDGE_PAM3                    0x93
+#define Q35_HOST_BRIDGE_PAM4                    0x94
+#define  Q35_HOST_BRIDGE_PAM_EXBIOS_AREA        0xe0000
+#define  Q35_HOST_BRIDGE_PAM_EXBIOS_SIZE        0x04000
+#define Q35_HOST_BRIDGE_PAM5                    0x95
+#define Q35_HOST_BRIDGE_PAM6                    0x96
+#define  Q35_HOST_BRIDGE_PAM_WE_HI              ((uint8_t)(0x2 << 4))
+#define  Q35_HOST_BRIDGE_PAM_RE_HI              ((uint8_t)(0x1 << 4))
+#define  Q35_HOST_BRIDGE_PAM_HI_MASK            ((uint8_t)(0x3 << 4))
+#define  Q35_HOST_BRIDGE_PAM_WE_LO              ((uint8_t)0x2)
+#define  Q35_HOST_BRIDGE_PAM_RE_LO              ((uint8_t)0x1)
+#define  Q35_HOST_BRIDGE_PAM_LO_MASK            ((uint8_t)0x3)
+#define  Q35_HOST_BRIDGE_PAM_WE                 ((uint8_t)0x2)
+#define  Q35_HOST_BRIDGE_PAM_RE                 ((uint8_t)0x1)
+#define  Q35_HOST_BRIDGE_PAM_MASK               ((uint8_t)0x3)
+
+
+#define Q35_HOST_BRDIGE_SMRAM                   0x9d
+#define Q35_HOST_BRDIGE_SMRAM_SIZE              1
+#define  Q35_HOST_BRIDGE_SMRAM_DEFAULT          ((uint8_t)0x2)
+#define  Q35_HOST_BRIDGE_SMRAM_D_OPEN           ((uint8_t)(1 << 6))
+#define  Q35_HOST_BRIDGE_SMRAM_D_CLS            ((uint8_t)(1 << 5))
+#define  Q35_HOST_BRIDGE_SMRAM_D_LCK            ((uint8_t)(1 << 4))
+#define  Q35_HOST_BRIDGE_SMRAM_G_SMRAME         ((uint8_t)(1 << 3))
+#define  Q35_HOST_BRIDGE_SMRAM_C_BASE_SEG_MASK  ((uint8_t)0x7)
+#define  Q35_HOST_BRIDGE_SMRAM_C_BASE_SEG       ((uint8_t)0x2)  /* hardwired 
to b010 */
+#define   Q35_HOST_BRIDGE_SMARM_C_BASE          0xa0000
+#define   Q35_HOST_BRIDGE_SMRAM_C_END           0xc0000
+#define   Q35_HOST_BRIDGE_SMRAM_C_SIZE          0x20000
+#define Q35_HOST_BRIDGE_UPPER_SYSTEM_BIOS_END   0x100000
+
+#define Q35_HOST_BRIDGE_ESMRAMC                 0x9e
+#define  Q35_HOST_BRDIGE_ESMRAMC_H_SMRAME       ((uint8_t)(1 << 6))
+#define  Q35_HOST_BRDIGE_ESMRAMC_E_SMERR        ((uint8_t)(1 << 5))
+#define  Q35_HOST_BRDIGE_ESMRAMC_SM_CACHE       ((uint8_t)(1 << 4))
+#define  Q35_HOST_BRDIGE_ESMRAMC_SM_L1          ((uint8_t)(1 << 3))
+#define  Q35_HOST_BRDIGE_ESMRAMC_SM_L2          ((uint8_t)(1 << 2))
+#define  Q35_HOST_BRDIGE_ESMRAMC_TSEG_SZ_MASK   ((uint8_t)(0x3 << 1))
+#define   Q35_HOST_BRDIGE_ESMRAMC_TSEG_SZ_1MB   ((uint8_t)(0x0 << 1))
+#define   Q35_HOST_BRDIGE_ESMRAMC_TSEG_SZ_2MB   ((uint8_t)(0x1 << 1))
+#define   Q35_HOST_BRDIGE_ESMRAMC_TSEG_SZ_8MB   ((uint8_t)(0x2 << 1))
+#define  Q35_HOST_BRDIGE_ESMRAMC_T_EN           ((uint8_t)1)
+
+
+/*
+ * ich9 part
+ */
+
+/* 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
+
+/* 1 pcie port on gmch and 6 pcie port on ich9,
+   the next bus # is 7 */
+#define ICH9_D2P_SECONDARY_DEFAULT              6
+
+#define ICH9_D2P_A2_REVISION                    0x92
+
+
+/* D30: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_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
+
+/* 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_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
+
+
+/* D30: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))
+
+/* D30:F3 SMBus I/O and memory mapped I/O registers */
+#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..63ad8d3
--- /dev/null
+++ b/hw/q35_smbus.c
@@ -0,0 +1,150 @@
+/*
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA  02110-1301 USA
+ */
+/*
+ *  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 "pc_smbus.h"
+#include "pci.h"
+#include "sysemu.h"
+#include "i2c.h"
+#include "smbus.h"
+
+#include "q35.h"
+
+typedef struct ICH9_SMBState {
+    PCIDevice dev;
+
+    PCSMBus 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_reset(void *opaque)
+{
+    //ICH9_SMBState *s = opaque;
+    //uint8_t *pci_conf = s->dev.config;
+
+    /* XXX not yet */
+}
+
+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_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);
+}
+
+i2c_bus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base)
+{
+    ICH9_SMBState *s;
+    PCIDevice *d;
+
+    s = (ICH9_SMBState *)pci_register_device(bus,
+                                             "ICH9 SMB", sizeof(ICH9_SMBState),
+                                             devfn, NULL, NULL);
+    d = &s->dev;
+    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_config_set_word(d, PCI_STATUS,
+                        PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MEDIUM);
+    pci_conf_initw(d, PCI_STATUS,
+                   PCI_STATUS_SIG_SYSTEM_ERROR | PCI_STATUS_DETECTED_PARITY);
+
+    pci_config_set_byte(d, PCI_REVISION_ID, ICH9_A2_SMB_REVISION);
+    pci_config_set_byte(d, PCI_CLASS_PROG, ICH9_SMB_PI);
+
+    pci_config_set_class(d->config, PCI_CLASS_SERIAL_SMBUS);
+    pci_config_set_byte(d, PCI_HEADER_TYPE, PCI_HEADER_TYPE_NORMAL); // 
header_type
+
+    /* XXX: D31IP.SMIP in chipset configuration space */
+    pci_config_set_byte(d, PCI_INTERRUPT_PIN, 0x01); // interrupt pin 1
+
+    pci_config_set_byte(d, ICH9_SMB_HOSTC, 0);
+
+
+    /* XXX:
+     * paralell_hds[0]
+     * serial_hds[0]
+     * serial_hds[0]
+     * fdc
+     */
+
+    // XXX smb_io_base
+    pci_config_set_byte(d, ICH9_SMB_HOSTC, 0);
+    /* XXX bar0, bar1: 64bit BAR support*/
+    pci_register_bar(d, ICH9_SMB_SMB_BASE_BAR,
+                     ICH9_SMB_SMB_BASE_SIZE, PCI_ADDRESS_SPACE_IO,
+                     &ich9_smb_map_ioport);
+
+    vmstate_register(0, &vmstate_ich9_smbus, s);
+
+    pc_smbus_init(&s->smb);
+    qemu_register_reset(ich9_smb_reset, s);
+
+    return s->smb.smbus;
+}
-- 
1.6.0.2





reply via email to

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