[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 26/26] pc q35 based chipset emulator
From: |
Isaku Yamahata |
Subject: |
[Qemu-devel] [PATCH 26/26] pc q35 based chipset emulator |
Date: |
Wed, 16 Mar 2011 18:29:37 +0900 |
pc q35 based chipset emulator to support pci express natively.
Signed-off-by: Isaku Yamahata <address@hidden>
---
Makefile.target | 1 +
hw/acpi_ich9.c | 314 ++++++++++++++++++++
hw/acpi_ich9.h | 53 ++++
hw/pc_q35.c | 359 +++++++++++++++++++++++
hw/pci_ids.h | 13 +
hw/q35.c | 877 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
hw/q35.h | 271 +++++++++++++++++
hw/q35_smbus.c | 154 ++++++++++
8 files changed, 2042 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 d57d250..28da59f 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -219,6 +219,7 @@ obj-i386-y += vmport.o
obj-i386-y += device-hotplug.o pci-hotplug.o smbios.o wdt_ib700.o
obj-i386-y += debugcon.o multiboot.o
obj-i386-y += pc_piix.o kvmclock.o
+obj-i386-y += pc_q35.o q35.o q35_smbus.o acpi_ich9.o
obj-i386-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o
# shared objects
diff --git a/hw/acpi_ich9.c b/hw/acpi_ich9.c
new file mode 100644
index 0000000..28db57b
--- /dev/null
+++ b/hw/acpi_ich9.c
@@ -0,0 +1,314 @@
+/*
+ * 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..84dc221
--- /dev/null
+++ b/hw/pc_q35.c
@@ -0,0 +1,359 @@
+/*
+ * 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;
+
+ pc_cpus_init(cpu_model);
+
+ /* 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);
+
+ /* 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);
+
+ 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 a597a7b..21cac58 100644
--- a/hw/pci_ids.h
+++ b/hw/pci_ids.h
@@ -35,6 +35,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
@@ -111,4 +112,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_ICH9_UCHI1 0x2934
+
+#define PCI_DEVICE_ID_INTEL_Q35_MCH 0x29c0
diff --git a/hw/q35.c b/hw/q35.c
new file mode 100644
index 0000000..0636aa4
--- /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..c0f5213
--- /dev/null
+++ b/hw/q35.h
@@ -0,0 +1,271 @@
+/*
+ * 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.1
- [Qemu-devel] [PATCH 23/26] acpi, acpi_piix: factor out PM1_CNT logic, (continued)
[Qemu-devel] [PATCH 19/26] pc/piix_pci: factor out smram/pam logic, Isaku Yamahata, 2011/03/16
[Qemu-devel] [PATCH 12/26] usb/uhci: generalize initialization, Isaku Yamahata, 2011/03/16
[Qemu-devel] [PATCH 26/26] pc q35 based chipset emulator,
Isaku Yamahata <=
[Qemu-devel] [PATCH 05/26] piix_pci: eliminate PIIX3State::pci_irq_levels, Isaku Yamahata, 2011/03/16
[Qemu-devel] ACPI table loading [was: q35 chipset support for native pci express support], Michael Tokarev, 2011/03/16