[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH v2 12/21] q35: Introduce q35 pc based chipset em
From: |
Michael S. Tsirkin |
Subject: |
Re: [Qemu-devel] [PATCH v2 12/21] q35: Introduce q35 pc based chipset emulator |
Date: |
Thu, 11 Oct 2012 16:47:43 +0200 |
On Mon, Oct 08, 2012 at 11:30:32PM -0400, Jason Baron wrote:
> From: Isaku Yamahata <address@hidden>
>
> pc q35 based chipset emulator to support pci express natively.
>
> Signed-off-by: Isaku Yamahata <address@hidden>
> Signed-off-by: Jason Baron <address@hidden>
Preferable to smash this with the next patch, this split
along historical rather than logical lines
complicates review.
> ---
> hw/acpi_ich9.c | 315 ++++++++++++++++++++
> hw/acpi_ich9.h | 53 ++++
> hw/pc_q35.c | 378 ++++++++++++++++++++++++
> hw/pci_ids.h | 14 +
> hw/q35.c | 877
> ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> hw/q35.h | 272 ++++++++++++++++++
> hw/q35_smbus.c | 154 ++++++++++
> 7 files changed, 2063 insertions(+), 0 deletions(-)
> create mode 100644 hw/acpi_ich9.c
> create mode 100644 hw/acpi_ich9.h
> create mode 100644 hw/pc_q35.c
> create mode 100644 hw/q35.c
> create mode 100644 hw/q35.h
> create mode 100644 hw/q35_smbus.c
>
> diff --git a/hw/acpi_ich9.c b/hw/acpi_ich9.c
> new file mode 100644
> index 0000000..59c0807
> --- /dev/null
> +++ b/hw/acpi_ich9.c
> @@ -0,0 +1,315 @@
> +/*
> + * ACPI implementation
> + *
> + * Copyright (c) 2006 Fabrice Bellard
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License version 2 as published by the Free Software Foundation.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> <http://www.gnu.org/licenses/>
> + */
> +/*
> + * Copyright (c) 2009 Isaku Yamahata <yamahata at valinux co jp>
> + * VA Linux Systems Japan K.K.
> + *
> + * This is based on acpi.c.
> + */
> +#include "hw.h"
> +#include "pc.h"
> +#include "pci.h"
> +#include "qemu-timer.h"
> +#include "sysemu.h"
> +#include "acpi.h"
> +
> +#include "q35.h"
> +
> +//#define DEBUG
> +
> +#ifdef DEBUG
> +#define ICH9_DEBUG(fmt, ...) \
> +do { printf("%s "fmt, __func__, ## __VA_ARGS__); } while (0)
> +#else
> +#define ICH9_DEBUG(fmt, ...) do { } while (0)
> +#endif
> +
> +static void pm_ioport_write_fallback(void *opaque, uint32_t addr, int len,
> + uint32_t val);
> +static uint32_t pm_ioport_read_fallback(void *opaque, uint32_t addr, int
> len);
> +
> +static void pm_update_sci(ICH9_LPCPmRegs *pm)
> +{
> + int sci_level, pm1a_sts;
> +
> + pm1a_sts = acpi_pm1_evt_get_sts(&pm->pm1a, pm->tmr.overflow_time);
> +
> + sci_level = (((pm1a_sts & pm->pm1a.en) &
> + (ACPI_BITMASK_RT_CLOCK_ENABLE |
> + ACPI_BITMASK_POWER_BUTTON_ENABLE |
> + ACPI_BITMASK_GLOBAL_LOCK_ENABLE |
> + ACPI_BITMASK_TIMER_ENABLE)) != 0);
> + qemu_set_irq(pm->irq, sci_level);
> +
> + /* schedule a timer interruption if needed */
> + acpi_pm_tmr_update(&pm->tmr,
> + (pm->pm1a.en & ACPI_BITMASK_TIMER_ENABLE) &&
> + !(pm1a_sts & ACPI_BITMASK_TIMER_STATUS));
> +}
> +
> +static void ich9_pm_update_sci_fn(ACPIPMTimer *tmr)
> +{
> + ICH9_LPCPmRegs *pm = container_of(tmr, ICH9_LPCPmRegs, tmr);
> + pm_update_sci(pm);
> +}
> +
> +static void pm_ioport_writeb(void *opaque, uint32_t addr, uint32_t val)
> +{
> + ICH9_LPCPmRegs *pm = opaque;
> +
> + switch (addr & ICH9_PMIO_MASK) {
> + case ICH9_PMIO_GPE0_STS ... (ICH9_PMIO_GPE0_STS + ICH9_PMIO_GPE0_LEN -
> 1):
> + acpi_gpe_ioport_writeb(&pm->gpe0, addr, val);
> + break;
> + default:
> + break;
> + }
> +
> + ICH9_DEBUG("port=0x%04x val=0x%04x\n", addr, val);
> +}
> +
> +static uint32_t pm_ioport_readb(void *opaque, uint32_t addr)
> +{
> + ICH9_LPCPmRegs *pm = opaque;
> + uint32_t val = 0;
> +
> + switch (addr & ICH9_PMIO_MASK) {
> + case ICH9_PMIO_GPE0_STS ... (ICH9_PMIO_GPE0_STS + ICH9_PMIO_GPE0_LEN -
> 1):
> + val = acpi_gpe_ioport_readb(&pm->gpe0, addr);
> + break;
> + default:
> + val = 0;
> + break;
> + }
> + ICH9_DEBUG("port=0x%04x val=0x%04x\n", addr, val);
> + return val;
> +}
> +
> +static void pm_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
> +{
> + ICH9_LPCPmRegs *pm = opaque;
> +
> + switch (addr & ICH9_PMIO_MASK) {
> + case ICH9_PMIO_PM1_STS:
> + acpi_pm1_evt_write_sts(&pm->pm1a, &pm->tmr, val);
> + pm_update_sci(pm);
> + break;
> + case ICH9_PMIO_PM1_EN:
> + pm->pm1a.en = val;
> + pm_update_sci(pm);
> + break;
> + case ICH9_PMIO_PM1_CNT:
> + acpi_pm1_cnt_write(&pm->pm1a, &pm->pm1_cnt, val);
> + break;
> + default:
> + pm_ioport_write_fallback(opaque, addr, 2, val);
> + break;
> + }
> + ICH9_DEBUG("port=0x%04x val=0x%04x\n", addr, val);
> +}
> +
> +static uint32_t pm_ioport_readw(void *opaque, uint32_t addr)
> +{
> + ICH9_LPCPmRegs *pm = opaque;
> + uint32_t val;
> +
> + switch (addr & ICH9_PMIO_MASK) {
> + case ICH9_PMIO_PM1_STS:
> + val = acpi_pm1_evt_get_sts(&pm->pm1a, pm->tmr.overflow_time);
> + break;
> + case ICH9_PMIO_PM1_EN:
> + val = pm->pm1a.en;
> + break;
> + case ICH9_PMIO_PM1_CNT:
> + val = pm->pm1_cnt.cnt;
> + break;
> + default:
> + val = pm_ioport_read_fallback(opaque, addr, 2);
> + break;
> + }
> + ICH9_DEBUG("port=0x%04x val=0x%04x\n", addr, val);
> + return val;
> +}
> +
> +static void pm_ioport_writel(void *opaque, uint32_t addr, uint32_t val)
> +{
> + ICH9_LPCPmRegs *pm = opaque;
> +
> + switch (addr & ICH9_PMIO_MASK) {
> + case ICH9_PMIO_SMI_EN:
> + pm->smi_en = val;
> + break;
> + default:
> + pm_ioport_write_fallback(opaque, addr, 4, val);
> + break;
> + }
> + ICH9_DEBUG("port=0x%04x val=0x%08x\n", addr, val);
> +}
> +
> +static uint32_t pm_ioport_readl(void *opaque, uint32_t addr)
> +{
> + ICH9_LPCPmRegs *pm = opaque;
> + uint32_t val;
> +
> + switch (addr & ICH9_PMIO_MASK) {
> + case ICH9_PMIO_PM1_TMR:
> + val = acpi_pm_tmr_get(&pm->tmr);
> + break;
> + case ICH9_PMIO_SMI_EN:
> + val = pm->smi_en;
> + break;
> +
> + default:
> + val = pm_ioport_read_fallback(opaque, addr, 4);
> + break;
> + }
> + ICH9_DEBUG("port=0x%04x val=0x%08x\n", addr, val);
> + return val;
> +}
> +
> +static void pm_ioport_write_fallback(void *opaque, uint32_t addr, int len,
> + uint32_t val)
> + {
> + int subsize = (len == 4) ? 2 : 1;
> + IOPortWriteFunc *ioport_write =
> + (subsize == 2) ? pm_ioport_writew : pm_ioport_writeb;
> +
> + int i;
> +
> + for (i = 0; i < len; i += subsize) {
> + ioport_write(opaque, addr, val);
> + val >>= 8 * subsize;
> + }
> +}
> +
> +static uint32_t pm_ioport_read_fallback(void *opaque, uint32_t addr, int len)
> +{
> + int subsize = (len == 4) ? 2 : 1;
> + IOPortReadFunc *ioport_read =
> + (subsize == 2) ? pm_ioport_readw : pm_ioport_readb;
> +
> + uint32_t val;
> + int i;
> +
> + val = 0;
> + for (i = 0; i < len; i += subsize) {
> + val <<= 8 * subsize;
> + val |= ioport_read(opaque, addr);
> + }
> +
> + return val;
> +}
> +
> +void ich9_pm_iospace_update(ICH9_LPCPmRegs *pm, uint32_t pm_io_base)
> +{
> + ICH9_DEBUG("to 0x%x\n", pm_io_base);
> +
> + assert((pm_io_base & ICH9_PMIO_MASK) == 0);
> +
> + if (pm->pm_io_base != 0) {
> + isa_unassign_ioport(pm->pm_io_base, ICH9_PMIO_SIZE);
> + }
> +
> + /* don't map at 0 */
> + if (pm_io_base == 0) {
> + return;
> + }
> +
> + register_ioport_write(pm_io_base, ICH9_PMIO_SIZE, 1, pm_ioport_writeb,
> pm);
> + register_ioport_read(pm_io_base, ICH9_PMIO_SIZE, 1, pm_ioport_readb, pm);
> + register_ioport_write(pm_io_base, ICH9_PMIO_SIZE, 2, pm_ioport_writew,
> pm);
> + register_ioport_read(pm_io_base, ICH9_PMIO_SIZE, 2, pm_ioport_readw, pm);
> + register_ioport_write(pm_io_base, ICH9_PMIO_SIZE, 4, pm_ioport_writel,
> pm);
> + register_ioport_read(pm_io_base, ICH9_PMIO_SIZE, 4, pm_ioport_readl, pm);
> +
> + pm->pm_io_base = pm_io_base;
> + acpi_gpe_blk(&pm->gpe0, pm_io_base + ICH9_PMIO_GPE0_STS);
> +}
> +
> +static int ich9_pm_post_load(void *opaque, int version_id)
> +{
> + ICH9_LPCPmRegs *pm = opaque;
> + uint32_t pm_io_base = pm->pm_io_base;
> + pm->pm_io_base = 0;
> + ich9_pm_iospace_update(pm, pm_io_base);
> + return 0;
> +}
> +
> +#define VMSTATE_GPE_ARRAY(_field, _state) \
> + { \
> + .name = (stringify(_field)), \
> + .version_id = 0, \
> + .num = ICH9_PMIO_GPE0_LEN, \
> + .info = &vmstate_info_uint8, \
> + .size = sizeof(uint8_t), \
> + .flags = VMS_ARRAY | VMS_POINTER, \
> + .offset = vmstate_offset_pointer(_state, _field, uint8_t), \
> + }
> +
> +const VMStateDescription vmstate_ich9_pm = {
> + .name = "ich9_pm",
> + .version_id = 1,
> + .minimum_version_id = 1,
> + .minimum_version_id_old = 1,
> + .post_load = ich9_pm_post_load,
> + .fields = (VMStateField[]) {
> + VMSTATE_UINT16(pm1a.sts, ICH9_LPCPmRegs),
> + VMSTATE_UINT16(pm1a.en, ICH9_LPCPmRegs),
> + VMSTATE_UINT16(pm1_cnt.cnt, ICH9_LPCPmRegs),
> + VMSTATE_TIMER(tmr.timer, ICH9_LPCPmRegs),
> + VMSTATE_INT64(tmr.overflow_time, ICH9_LPCPmRegs),
> + VMSTATE_GPE_ARRAY(gpe0.sts, ICH9_LPCPmRegs),
> + VMSTATE_GPE_ARRAY(gpe0.en, ICH9_LPCPmRegs),
> + VMSTATE_UINT32(smi_en, ICH9_LPCPmRegs),
> + VMSTATE_UINT32(smi_sts, ICH9_LPCPmRegs),
> + VMSTATE_END_OF_LIST()
> + }
> +};
> +
> +static void pm_reset(void *opaque)
> +{
> + ICH9_LPCPmRegs *pm = opaque;
> + ich9_pm_iospace_update(pm, 0);
> +
> + acpi_pm1_evt_reset(&pm->pm1a);
> + acpi_pm1_cnt_reset(&pm->pm1_cnt);
> + acpi_pm_tmr_reset(&pm->tmr);
> + acpi_gpe_reset(&pm->gpe0);
> +
> + pm_update_sci(pm);
> +}
> +
> +static void pm_powerdown(void *opaque, int irq, int power_failing)
> +{
> + ICH9_LPCPmRegs *pm = opaque;
> + ACPIPM1EVT *pm1a = pm ? &pm->pm1a : NULL;
> + ACPIPMTimer *tmr = pm ? &pm->tmr : NULL;
> +
> + acpi_pm1_evt_power_down(pm1a, tmr);
> +}
> +
> +void ich9_pm_init(ICH9_LPCPmRegs *pm, qemu_irq sci_irq, qemu_irq cmos_s3)
> +{
> + acpi_pm_tmr_init(&pm->tmr, ich9_pm_update_sci_fn);
> + acpi_pm1_cnt_init(&pm->pm1_cnt, cmos_s3);
> + acpi_gpe_init(&pm->gpe0, ICH9_PMIO_GPE0_LEN);
> +
> + pm->irq = sci_irq;
> + qemu_register_reset(pm_reset, pm);
> + qemu_system_powerdown = *qemu_allocate_irqs(pm_powerdown, pm, 1);
> +}
> diff --git a/hw/acpi_ich9.h b/hw/acpi_ich9.h
> new file mode 100644
> index 0000000..f55c0e9
> --- /dev/null
> +++ b/hw/acpi_ich9.h
> @@ -0,0 +1,53 @@
> +/*
> + * QEMU GMCH/ICH9 LPC PM Emulation
> + *
> + * Copyright (c) 2009 Isaku Yamahata <yamahata at valinux co jp>
> + * VA Linux Systems Japan K.K.
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> <http://www.gnu.org/licenses/>
> + */
> +
> +#ifndef HW_ACPI_ICH9_H
> +#define HW_ACPI_ICH9_H
> +
> +#include "acpi.h"
> +
> +typedef struct ICH9_LPCPmRegs {
> + ACPIPM1EVT pm1a;
> +
> + /*
> + * In ich9 spec says that pm1_cnt register is 32bit width and
> + * that the upper 16bits are reserved and unused.
> + * PM1a_CNT_BLK = 2 in FADT so it is defined as uint16_t.
> + */
> + ACPIPM1CNT pm1_cnt;
> +
> + ACPIPMTimer tmr;
> +
> + ACPIGPE gpe0;
> +
> + uint32_t smi_en;
> + uint32_t smi_sts;
> +
> + qemu_irq irq; /* SCI */
> +
> + uint32_t pm_io_base;
> +} ICH9_LPCPmRegs;
> +
> +void ich9_pm_init(ICH9_LPCPmRegs *pm,
> + qemu_irq sci_irq, qemu_irq cmos_s3_resume);
> +void ich9_pm_iospace_update(ICH9_LPCPmRegs *pm, uint32_t pm_io_base);
> +extern const VMStateDescription vmstate_ich9_pm;
> +
> +#endif /* HW_ACPI_ICH9_H */
> diff --git a/hw/pc_q35.c b/hw/pc_q35.c
> new file mode 100644
> index 0000000..4f75d97
> --- /dev/null
> +++ b/hw/pc_q35.c
> @@ -0,0 +1,378 @@
> +/*
> + * QEMU PC System Emulator
> + *
> + * Copyright (c) 2003-2004 Fabrice Bellard
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> copy
> + * of this software and associated documentation files (the "Software"), to
> deal
> + * in the Software without restriction, including without limitation the
> rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +/*
> + * Q35 chipset based pc system emulator
> + *
> + * Copyright (c) 2009, 2010
> + * Isaku Yamahata <yamahata at valinux co jp>
> + * VA Linux Systems Japan K.K.
> + *
> + * This is based on pc.c, but heavily modified.
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> <http://www.gnu.org/licenses/>
> + */
> +#include "hw.h"
> +#include "arch_init.h"
> +#include "pc.h"
> +#include "fdc.h"
> +#include "pci.h"
> +#include "pci_bridge.h"
> +#include "pci_p2pbr.h"
> +#include "ioh3420.h"
> +#include "xio3130_upstream.h"
> +#include "xio3130_downstream.h"
> +#include "block.h"
> +#include "blockdev.h"
> +#include "sysemu.h"
> +#include "audio/audio.h"
> +#include "net.h"
> +#include "smbus.h"
> +#include "boards.h"
> +#include "monitor.h"
> +#include "fw_cfg.h"
> +#include "hpet_emul.h"
> +#include "watchdog.h"
> +#include "smbios.h"
> +#include "ide.h"
> +#include "usb-uhci.h"
> +
> +#include "q35.h"
> +
> +/* ICH9 AHCI has 6 ports */
> +#define MAX_SATA_PORTS 6
> +
> +#define I21154_REV 0x05
> +#define I21154_PI 0x00
> +
> +static PCIBridge *i21154_init(PCIBus *bus, int devfn, const char *bus_name,
> + bool multifunction)
> +{
> + const PCIP2PBridgeInit init = {
> + .bus = bus,
> + .devfn = devfn,
> + .multifunction = multifunction,
> +
> + .bus_name = bus_name,
> + .map_irq = pci_swizzle_map_irq_fn,
> + };
> + const PCIP2PBridgeProp prop = {
> + .vendor_id = PCI_VENDOR_ID_DEC,
> + .device_id = PCI_DEVICE_ID_DEC_21154,
> + .revision_id = I21154_REV,
> + .prog_interface = I21154_PI,
> + };
> + return pci_p2pbr_create_simple(&init, &prop);
> +}
> +
> +static void pc_q35_bridge_init(PCIBus *host_bus, PCIBus *pci_bus)
> +{
> + uint8_t dev;
> + uint8_t sec_bus;
> + uint8_t port = 0;
> + uint8_t chassis = 0;
> + uint16_t slot = 0;
> + uint8_t upstream_port;
> + PCIESlot *s;
> + uint8_t fn;
> + PCIESlot *root_port;
> + PCIBus *root_port_bus;
> + char buf[16];
> +
> + /* PCI to PCI bridge b6:d[29 - 31]:f0, 6:[1c - 1f].0 with subordinate bus
> + of 7 - 9 on b0:d30:f0, 0.1e.0 = bus */
> +#define Q35_P2P_BRDIGE_DEV_BASE 28
> +#define Q35_P2P_BRDIGE_DEV_MAX 32
> +#define Q35_P2P_BRDIGE_SUBBUS_BASE (ICH9_D2P_SECONDARY_DEFAULT + 1)
> + for (dev = Q35_P2P_BRDIGE_DEV_BASE; dev < Q35_P2P_BRDIGE_DEV_MAX; dev++)
> {
> + PCIBridge *br;
> + sec_bus = Q35_P2P_BRDIGE_SUBBUS_BASE + dev - Q35_P2P_BRDIGE_DEV_BASE;
> +
> + snprintf(buf, sizeof(buf), "pci.%d", sec_bus);
> + br = i21154_init(pci_bus, PCI_DEVFN(dev, 0), buf, true);
> + }
> +
> + /* PCIe root port b0:d1:f0 in GMCH.
> + * Actually it's vid/did = 0x8086:0x29c1, but we substitute ioh for it.
> + */
> + sec_bus = 32;
> + snprintf(buf, sizeof(buf), "pcie.%d", sec_bus);
> + s = ioh3420_init(host_bus, PCI_DEVFN(GMCH_PCIE_DEV, GMCH_PCIE_FUNC),
> true,
> + buf, pci_swizzle_map_irq_fn, port, chassis, slot);
> +
> +
> + /* more slots. ICH9 doesn't have those, but many slots are wanted. */
> +//#define Q35_MANY_SLOTS
> +#undef Q35_MANY_SLOTS
> +
> +#ifdef Q35_MANY_SLOTS
> +#define Q35_NR_ROOTPORT 6
> +#define Q35_NR_UPSTREAM 8
> +#define Q35_NR_DOWNSTREAM 16
> +#else
> +#define Q35_NR_ROOTPORT 1
> +#define Q35_NR_UPSTREAM 1
> +#define Q35_NR_DOWNSTREAM 1
> +#endif
> +
> + /* PCIe root port b0:d23:f[0-5], 0.17.[0-5] */
> + for (fn = 0; fn < Q35_NR_ROOTPORT; fn++) {
> + sec_bus++;
> + port++;
> + slot++;
> +
> + snprintf(buf, sizeof(buf), "pcie.%d", sec_bus);
> + s = ioh3420_init(host_bus, PCI_DEVFN(23, fn), true,
> + buf, pci_swizzle_map_irq_fn, port, chassis, slot);
> + }
> +
> + /* PCIe root port b0:d24:f0 */
> + sec_bus++;
> + port++;
> + slot++;
> + snprintf(buf, sizeof(buf), "pcie.%d", sec_bus);
> + root_port = ioh3420_init(host_bus, PCI_DEVFN(24, 0), true,
> + buf, pci_swizzle_map_irq_fn, port, chassis,
> slot);
> + root_port_bus = pci_bridge_get_sec_bus(&root_port->port.br);
> +
> + /* 8 * 16 = 128 slots */
> + upstream_port = 0;
> + for (fn = 0; fn < Q35_NR_UPSTREAM; fn++) {
> + PCIEPort *upstream;
> + PCIBus *upstream_bus;
> + uint16_t downstream_port;
> +
> + uint8_t ds_dev_max;
> + uint8_t ds_dev;
> + uint8_t ds_fn_max;
> + uint8_t ds_fn;
> +
> + /* PCIe upstream port d0:f[0-7] */
> + sec_bus++;
> + snprintf(buf, sizeof(buf), "pcie.%d", sec_bus);
> + upstream = xio3130_upstream_init(root_port_bus, PCI_DEVFN(0, fn),
> + true, buf, pci_swizzle_map_irq_fn,
> + upstream_port);
> +
> + upstream_bus = pci_bridge_get_sec_bus(&upstream->br);
> + upstream_port++;
> +
> + /* PCIe downstream port */
> + downstream_port = 0;
> + ds_fn_max = MIN(Q35_NR_DOWNSTREAM / PCI_SLOT_MAX, PCI_FUNC_MAX);
> + ds_dev_max = MIN(Q35_NR_DOWNSTREAM / (ds_fn_max + 1), PCI_SLOT_MAX);
> +
> + for (ds_dev = 0; ds_dev <= ds_dev_max &&
> + downstream_port < Q35_NR_DOWNSTREAM; ds_dev++) {
> + for (ds_fn = 0; ds_fn <= ds_fn_max &&
> + downstream_port < Q35_NR_DOWNSTREAM; ds_fn++) {
> + sec_bus++;
> + slot++;
> + snprintf(buf, sizeof(buf), "pcie.%d", sec_bus);
> +
> + xio3130_downstream_init(upstream_bus, PCI_DEVFN(ds_dev,
> ds_fn),
> + true, buf, pci_swizzle_map_irq_fn,
> + downstream_port, chassis, slot);
> + downstream_port++;
> + }
> + }
> + }
> +
> + /* PCIe root port b0:d28:f[0-6] in ICH9.
> + * Actually it's vid/did = 0x8086:0x294[02468A], but we substitute ioh
> + * for them.
> + */
> + for (fn = 0; fn < ICH9_PCIE_FUNC_MAX; fn++) {
> + sec_bus++;
> + port++;
> + slot++;
> +
> + snprintf(buf, sizeof(buf), "pcie.%d", sec_bus);
> + s = ioh3420_init(host_bus, PCI_DEVFN(ICH9_PCIE_DEV, fn), true,
> + buf, pci_swizzle_map_irq_fn,
> + port, chassis, slot);
> + }
> +}
> +
> +static void pc_q35_init_early(qemu_irq *isa_irq, IsaIrqState *isa_irq_state,
> + DeviceState **gmch_host_p,
> + PCIBus **host_bus_p, PCIBus **pci_bus_p,
> + PCIDevice **lpc_p)
> +{
> + DeviceState *gmch_host;
> + PCIBus *host_bus;
> + PCIBus *pci_bus;
> +
> + PCIDevice *gmch_state;
> + PCIDevice *lpc;
> +
> + /* create pci host bus */
> + host_bus = gmch_host_init(&gmch_host, isa_irq, isa_irq_state->ioapic);
> + gmch_state = gmch_init(gmch_host, host_bus);
> +
> + /* create conventional pci bus: pcie2pci bridge */
> + pci_bus = ich9_d2pbr_init(host_bus, PCI_DEVFN(ICH9_D2P_BRIDGE_DEV,
> + ICH9_D2P_BRIDGE_FUNC),
> + ICH9_D2P_SECONDARY_DEFAULT);
> +
> + /* create child pci/pcie buses */
> + pc_q35_bridge_init(host_bus, pci_bus);
> +
> + /* create ISA bus */
> + lpc = gmch_lpc_init(gmch_host, host_bus);
> +
> + *gmch_host_p = gmch_host;
> + *host_bus_p = host_bus;
> + *pci_bus_p = pci_bus;
> + *lpc_p = lpc;
> +}
> +
> +static void pc_q35_init_late(BusState **idebus, ISADevice *rtc_state,
> + DeviceState *gmch_host,
> + PCIBus *host_bus, PCIBus *pci_bus,
> + PCIDevice *lpc)
> +{
> + qemu_irq *cmos_s3;
> + PCIDevice *ahci;
> + DriveInfo *hd[MAX_SATA_PORTS * MAX_IDE_DEVS];
> +
> + /* connect pm stuff to lpc */
> + cmos_s3 = qemu_allocate_irqs(pc_cmos_set_s3_resume, rtc_state, 1);
> + ich9_lpc_pm_init(gmch_host, lpc, *cmos_s3);
> +
> + /* ahci and SATA device */
> + ide_drive_get(hd, MAX_SATA_PORTS);
> + ahci = pci_create_simple_multifunction(host_bus,
> + PCI_DEVFN(ICH9_SATA1_DEV,
> + ICH9_SATA1_FUNC),
> + true, "ich9-ahci");
> + pci_ahci_ide_create_devs(ahci, hd);
> + idebus[0] = qdev_get_child_bus(&ahci->qdev, "ide.0");
> + idebus[1] = qdev_get_child_bus(&ahci->qdev, "ide.1");
> +
> + if (usb_enabled) {
> + /* Should we create 6 UHCI according to ich9 spec? */
> + pci_create_simple_multifunction(
> + host_bus, PCI_DEVFN(ICH9_USB_UHCI1_DEV, ICH9_USB_UHCI1_FUNC),
> + true, "ich9-usb-uhci1");
> + /* XXX: EHCI */
> + }
> +
> + /* TODO: Populate SPD eeprom data. */
> + smbus_eeprom_init(ich9_smb_init(host_bus,
> + PCI_DEVFN(ICH9_SMB_DEV, ICH9_SMB_FUNC),
> + 0xb100),
> + 8, NULL, 0);
> +}
> +
> +/* PC hardware initialisation */
> +static void pc_q35_init(ram_addr_t ram_size,
> + const char *boot_device,
> + const char *kernel_filename,
> + const char *kernel_cmdline,
> + const char *initrd_filename,
> + const char *cpu_model)
> +{
> + ram_addr_t below_4g_mem_size, above_4g_mem_size;
> + DeviceState *gmch_host;
> + PCIBus *host_bus;
> + PCIBus *pci_bus;
> + PCIDevice *lpc;
> + qemu_irq *isa_irq;
> + IsaIrqState *isa_irq_state;
> + BusState *idebus[MAX_SATA_PORTS];
> + ISADevice *rtc_state;
> + MemoryRegion *pci_memory;
> + MemoryRegion *rom_memory;
> + MemoryRegion *ram_memory;
> +
> + pc_cpus_init(cpu_model);
> +
> + /* FIXME: add kvm clock ? */
> +
> + if (ram_size >= 0xe0000000) {
> + above_4g_mem_size = ram_size - 0xe0000000;
> + below_4g_mem_size = 0xe0000000;
> + } else {
> + above_4g_mem_size = 0;
> + below_4g_mem_size = ram_size;
> + }
> +
> + /* pci enabled */
> + pci_memory = g_new(MemoryRegion, 1);
> + memory_region_init(pci_memory, "pci", INT64_MAX);
> + rom_memory = pci_memory;
> +
> + /* allocate ram and load rom/bios */
> + pc_memory_init(get_system_memory(), kernel_filename, kernel_cmdline,
> + initrd_filename, below_4g_mem_size, above_4g_mem_size,
> + rom_memory, &ram_memory);
> +
> + /* irq lines */
> + isa_irq = pc_isa_irq(&isa_irq_state);
> + ioapic_init(isa_irq_state);
> +
> + pc_q35_init_early(isa_irq, isa_irq_state,
> + &gmch_host, &host_bus, &pci_bus, &lpc);
> + isa_bus_irqs(isa_irq);
> + pc_register_ferr_irq(isa_get_irq(13));
> +
> + /* init basic PC hardware */
> + pc_basic_device_init(isa_irq, &rtc_state, false);
> +
> + pc_q35_init_late(idebus, rtc_state, gmch_host, host_bus, pci_bus, lpc);
> +
> + pc_cmos_init(below_4g_mem_size, above_4g_mem_size, boot_device,
> + idebus[0], idebus[1], rtc_state);
> +
> + /* the rest devices to which pci devfn is automatically assigned */
> + pc_vga_init(host_bus);
> + audio_init(isa_irq, pci_bus);
> + pc_nic_init(pci_bus);
> + pc_pci_device_init(pci_bus);
> +}
> +
> +static QEMUMachine pc_q35_machine = {
> + .name = "pc_q35",
> + .desc = "Q35 chipset PC",
> + .init = pc_q35_init,
> + .max_cpus = 255,
> +};
> +
> +static void pc_q35_machine_init(void)
> +{
> + qemu_register_machine(&pc_q35_machine);
> +}
> +
> +machine_init(pc_q35_machine_init);
> diff --git a/hw/pci_ids.h b/hw/pci_ids.h
> index 6deeac0..50744dd 100644
> --- a/hw/pci_ids.h
> +++ b/hw/pci_ids.h
> @@ -36,6 +36,7 @@
> #define PCI_CLASS_BRIDGE_HOST 0x0600
> #define PCI_CLASS_BRIDGE_ISA 0x0601
> #define PCI_CLASS_BRIDGE_PCI 0x0604
> +#define PCI_CLASS_BRDIGE_PCI_INF_SUB 0x01
> #define PCI_CLASS_BRIDGE_OTHER 0x0680
>
> #define PCI_CLASS_COMMUNICATION_OTHER 0x0780
> @@ -115,6 +116,17 @@
> #define PCI_DEVICE_ID_INTEL_82371AB 0x7111
> #define PCI_DEVICE_ID_INTEL_82371AB_2 0x7112
> #define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113
> +
> +#define PCI_DEVICE_ID_INTEL_ICH9_0 0x2910
> +#define PCI_DEVICE_ID_INTEL_ICH9_1 0x2917
> +#define PCI_DEVICE_ID_INTEL_ICH9_2 0x2912
> +#define PCI_DEVICE_ID_INTEL_ICH9_3 0x2913
> +#define PCI_DEVICE_ID_INTEL_ICH9_4 0x2914
> +#define PCI_DEVICE_ID_INTEL_ICH9_5 0x2919
> +#define PCI_DEVICE_ID_INTEL_ICH9_6 0x2930
> +#define PCI_DEVICE_ID_INTEL_ICH9_7 0x2916
> +#define PCI_DEVICE_ID_INTEL_ICH9_8 0x2918
> +
> #define PCI_DEVICE_ID_INTEL_82801I_UHCI1 0x2934
> #define PCI_DEVICE_ID_INTEL_82801I_UHCI2 0x2935
> #define PCI_DEVICE_ID_INTEL_82801I_UHCI3 0x2936
> @@ -125,6 +137,8 @@
> #define PCI_DEVICE_ID_INTEL_82801I_EHCI2 0x293c
> #define PCI_DEVICE_ID_INTEL_82599_SFP_VF 0x10ed
>
> +#define PCI_DEVICE_ID_INTEL_Q35_MCH 0x29c0
> +
> #define PCI_VENDOR_ID_XEN 0x5853
> #define PCI_DEVICE_ID_XEN_PLATFORM 0x0001
>
> diff --git a/hw/q35.c b/hw/q35.c
> new file mode 100644
> index 0000000..1776ac3
> --- /dev/null
> +++ b/hw/q35.c
> @@ -0,0 +1,877 @@
> +/*
> + * Copyright (c) 2006 Fabrice Bellard
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> copy
> + * of this software and associated documentation files (the "Software"), to
> deal
> + * in the Software without restriction, including without limitation the
> rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +/*
> + * QEMU GMCH/ICH9 PCI Bridge Emulation
> + *
> + * Copyright (c) 2009, 2010, 2011
> + * Isaku Yamahata <yamahata at valinux co jp>
> + * VA Linux Systems Japan K.K.
> + *
> + * This is based on piix_pci.c, but heavily modified.
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> <http://www.gnu.org/licenses/>
> + */
> +
> +#include "hw.h"
> +#include "range.h"
> +#include "isa.h"
> +#include "sysbus.h"
> +#include "pc.h"
> +#include "apm.h"
> +#include "apic.h"
> +#include "pci.h"
> +#include "pcie_host.h"
> +#include "pci_bridge.h"
> +#include "pci_p2pbr.h"
> +#include "q35.h"
> +#include "acpi.h"
> +#include "acpi_ich9.h"
> +#include "pam.h"
> +
> +
> +struct ICH9_LPCState;
> +
> +typedef struct ICH9_LPCIrqState {
> + struct ICH9_LPCState *lpc;
> + qemu_irq *pic;
> + qemu_irq *ioapic;
> +} ICH9_LPCIrqState;
> +
> +typedef struct GMCH_PCIHost {
> + PCIExpressHost host;
> +
> + PCIDevice *dev;
> + ICH9_LPCIrqState irq_state;
> +} GMCH_PCIHost;
> +
> +typedef struct GMCH_PCIState {
> + PCIDevice d;
> + /*
> + * GMCH_PCIHost *gmch_host;
> + * In order to get GMCH_PCIHost
> + * PCIDevice -> qdev -> parent_bus -> qdev -upcast-> GMCH_PCIHost
> + */
> +
> + PAM pam;
> +} GMCH_PCIState;
> +
> +typedef struct ICH9_LPCState {
> + /* ICH9 LPC PCI to ISA bridge */
> + PCIDevice d;
> +
> + /* (pci device, intx) -> pirq
> + * In real chipset case, the unused slots are never used
> + * as ICH9 supports only D25-D32 irq routing.
> + * On the other hand in qemu case, any slot/function can be populated
> + * via command line option.
> + * So fallback interrupt routing for any devices in any slots is
> necessary.
> + */
> + uint8_t irr[PCI_SLOT_MAX][PCI_NUM_PINS];
> +
> + APMState apm;
> + ICH9_LPCPmRegs pm;
> + uint32_t sci_level; /* track sci level */
> +
> + /* 10.1 Chipset Configuration registers(Memory Space)
> + which is pointed by RCBA */
> + uint8_t chip_config[ICH9_CC_SIZE];
> + int rbca_index;
> +} ICH9_LPCState;
> +
> +
> +/****************************************************************************
> + * GMCH PCI host
> + */
> +/* ich9 irq */
> +static int ich9_lpc_map_irq(void *opaque, PCIDevice *pci_dev, int intx);
> +static void ich9_lpc_set_irq(void *opaque, int irq_num, int level);
> +static int ich9_lpc_sci_irq(ICH9_LPCState *lpc);
> +
> +static GMCH_PCIHost *gmch_pcihost_from_qdev(DeviceState *gmch_host_qdev)
> +{
> + SysBusDevice *sysdev = sysbus_from_qdev(gmch_host_qdev);
> + PCIHostState *pci = FROM_SYSBUS(PCIHostState, sysdev);
> + PCIExpressHost *pcie = DO_UPCAST(PCIExpressHost, pci, pci);
> + return DO_UPCAST(GMCH_PCIHost, host, pcie);
> +}
> +
> +static int gmch_pcihost_initfn(SysBusDevice *dev)
> +{
> + GMCH_PCIHost *s = gmch_pcihost_from_qdev(&dev->qdev);
> +
> + pci_host_conf_register_ioport(GMCH_HOST_BRIDGE_CONFIG_ADDR,
> &s->host.pci);
> + pci_host_data_register_ioport(GMCH_HOST_BRIDGE_CONFIG_DATA,
> &s->host.pci);
> +
> + if (pcie_host_init(&s->host) < 0) {
> + abort();
> + }
> +
> + return 0;
> +}
> +
> +static SysBusDeviceInfo gmch_pcihost_info = {
> + .init = gmch_pcihost_initfn,
> + .qdev.name = "gmch-pcihost",
> + .qdev.size = sizeof(GMCH_PCIHost),
> + .qdev.no_user = 1,
> + .qdev.props = (Property[]) {
> + {
> + .name = "MCFG",
> + .info = &qdev_prop_uint64,
> + .offset = offsetof(GMCH_PCIHost, host.base_addr),
> + .defval = (uint64_t[]){ GMCH_HOST_BRIDGE_PCIEXBAR_DEFAULT },
> + },
> + DEFINE_PROP_END_OF_LIST(),
> + },
> +};
> +
> +/* host bridge */
> +PCIBus *gmch_host_init(DeviceState **gmch_hostp,
> + qemu_irq *pic, qemu_irq *ioapic)
> +{
> + DeviceState *dev;
> + GMCH_PCIHost *s;
> + PCIBus *b;
> +
> + dev = qdev_create(NULL, "gmch-pcihost");
> + s = gmch_pcihost_from_qdev(dev);
> + s->irq_state.pic = pic;
> + s->irq_state.ioapic = ioapic;
> +
> + b = pci_bus_new(dev, "pcie.0", 0);
> + pci_bus_irqs(b, ich9_lpc_set_irq, ich9_lpc_map_irq, &s->irq_state,
> + ICH9_LPC_NB_PIRQS);
> + s->host.pci.bus = b;
> + qdev_init_nofail(dev);
> +
> + *gmch_hostp = dev;
> + return b;
> +}
> +
> +
> +/****************************************************************************
> + * GMCH
> + */
> +static GMCH_PCIState *gmch_from_pci(PCIDevice *gmch_pci)
> +{
> + return DO_UPCAST(GMCH_PCIState, d, gmch_pci);
> +}
> +
> +/* PCIE MMCFG */
> +static void gmch_update_pciexbar(GMCH_PCIState *gs)
> +{
> + PCIDevice *pci_dev = &gs->d;
> + BusState *bus = qdev_get_parent_bus(&pci_dev->qdev);
> + DeviceState *qdev = bus->parent;
> + GMCH_PCIHost *s = gmch_pcihost_from_qdev(qdev);
> +
> + uint64_t pciexbar;
> + int enable;
> + uint64_t addr;
> + uint64_t addr_mask;
> + uint32_t length;
> +
> + pciexbar = pci_get_quad(pci_dev->config + GMCH_HOST_BRIDGE_PCIEXBAR);
> + enable = pciexbar & GMCH_HOST_BRIDGE_PCIEXBAREN;
> +
> + addr_mask = GMCH_HOST_BRIDGE_PCIEXBAR_ADMSK;
> + switch (pciexbar & GMCH_HOST_BRIDGE_PCIEXBAR_LENGTH_MASK) {
> + case GMCH_HOST_BRIDGE_PCIEXBAR_LENGTH_256M:
> + length = 256 * 1024 * 1024;
> + break;
> + case GMCH_HOST_BRIDGE_PCIEXBAR_LENGTH_128M:
> + length = 128 * 1024 * 1024;
> + addr_mask |= GMCH_HOST_BRIDGE_PCIEXBAR_128ADMSK |
> + GMCH_HOST_BRIDGE_PCIEXBAR_64ADMSK;
> + break;
> + case GMCH_HOST_BRIDGE_PCIEXBAR_LENGTH_64M:
> + length = 64 * 1024 * 1024;
> + addr_mask |= GMCH_HOST_BRIDGE_PCIEXBAR_64ADMSK;
> + break;
> + case GMCH_HOST_BRIDGE_PCIEXBAR_LENGTH_RVD:
> + default:
> + enable = 0;
> + length = 0;
> + abort();
> + break;
> + }
> + addr = pciexbar & addr_mask;
> +
> + pcie_host_mmcfg_update(&s->host, enable, addr, length);
> +}
> +
> +/* PAM */
> +static void gmch_update_pam(GMCH_PCIState *gs)
> +{
> + int i;
> + for (i = 0; i <= PAM_IDX_MAX; i++) {
> + pam_update(&gs->pam, i, gs->d.config[GMCH_HOST_BRIDGE_PAM0 + i]);
> + }
> +}
> +
> +/* SMRAM */
> +static void gmch_update_smram(GMCH_PCIState *gs)
> +{
> + smram_update(&gs->pam, gs->d.config[GMCH_HOST_BRDIGE_SMRAM]);
> +}
> +
> +static void gmch_set_smm(int smm, void *arg)
> +{
> + GMCH_PCIState *gs = arg;
> + smram_set_smm(&gs->pam, smm, gs->d.config[GMCH_HOST_BRDIGE_SMRAM]);
> +}
> +
> +static void gmch_write_config(PCIDevice *d,
> + uint32_t address, uint32_t val, int len)
> +{
> + GMCH_PCIState *gs = gmch_from_pci(d);
> +
> + /* XXX: implement SMRAM.D_LOCK */
> + pci_default_write_config(d, address, val, len);
> +
> + if (ranges_overlap(address, len, GMCH_HOST_BRIDGE_PAM0,
> + GMCH_HOST_BRIDGE_PAM_SIZE)) {
> + gmch_update_pam(gs);
> + }
> +
> + if (ranges_overlap(address, len, GMCH_HOST_BRIDGE_PCIEXBAR,
> + GMCH_HOST_BRIDGE_PCIEXBAR_SIZE)) {
> + gmch_update_pciexbar(gs);
> + }
> +
> + if (ranges_overlap(address, len, GMCH_HOST_BRDIGE_SMRAM,
> + GMCH_HOST_BRDIGE_SMRAM_SIZE)) {
> + gmch_update_smram(gs);
> + }
> +}
> +
> +static void gmch_update(GMCH_PCIState *gs)
> +{
> + gmch_update_pciexbar(gs);
> + gmch_update_pam(gs);
> + gmch_update_smram(gs);
> +}
> +
> +static int gmch_post_load(void *opaque, int version_id)
> +{
> + GMCH_PCIState *gs = opaque;
> + gmch_update(gs);
> + return 0;
> +}
> +
> +static const VMStateDescription vmstate_gmch = {
> + .name = "gmch",
> + .version_id = 1,
> + .minimum_version_id = 1,
> + .minimum_version_id_old = 1,
> + .post_load = gmch_post_load,
> + .fields = (VMStateField []) {
> + VMSTATE_PCI_DEVICE(d, GMCH_PCIState),
> + VMSTATE_UINT8(pam.smm_enabled, GMCH_PCIState),
> + VMSTATE_END_OF_LIST()
> + }
> +};
> +
> +static void gmch_reset(DeviceState *qdev)
> +{
> + PCIDevice *d = DO_UPCAST(PCIDevice, qdev, qdev);
> + GMCH_PCIState *gs = gmch_from_pci(d);
> +
> + pci_set_quad(d->config + GMCH_HOST_BRIDGE_PCIEXBAR,
> + GMCH_HOST_BRIDGE_PCIEXBAR_DEFAULT);
> +
> + d->config[GMCH_HOST_BRDIGE_SMRAM] = GMCH_HOST_BRIDGE_SMRAM_DEFAULT;
> +
> + gmch_update(gs);
> +}
> +
> +static int gmch_initfn(PCIDevice *d)
> +{
> + GMCH_PCIState *gs = gmch_from_pci(d);
> +
> + pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_INTEL);
> + pci_config_set_device_id(d->config, PCI_DEVICE_ID_INTEL_Q35_MCH);
> + pci_config_set_revision(d->config, GMCH_HOST_BRIDGE_REVISION_DEFUALT);
> + pci_config_set_class(d->config, PCI_CLASS_BRIDGE_HOST);
> +
> + cpu_smm_register(&gmch_set_smm, gs);
> + pam_init_memory_mappings(&gs->pam);
> +
> + return 0;
> +}
> +
> +static PCIDeviceInfo gmch_info = {
> + .qdev.name = "gmch",
> + .qdev.desc = "Host bridge",
> + .qdev.size = sizeof(GMCH_PCIState),
> + .qdev.vmsd = &vmstate_gmch,
> + .qdev.no_user = 1,
> + .init = gmch_initfn,
> + .config_write = gmch_write_config,
> + .qdev.reset = gmch_reset,
> +};
> +
> +/* host bridge */
> +PCIDevice *gmch_init(DeviceState *gmch_host, PCIBus *b)
> +{
> + GMCH_PCIHost *s = gmch_pcihost_from_qdev(gmch_host);
> + PCIDevice *d;
> +
> + d = pci_create_simple_multifunction(b, 0, false, "gmch");
> + s->dev = d;
> +
> + return d;
> +}
> +
> +/*****************************************************************************/
> +/* ICH9 DMI-to-PCI bridge */
> +#define I82801ba_SSVID_OFFSET 0x50
> +#define I82801ba_SSVID_SVID 0
> +#define I82801ba_SSVID_SSID 0
> +
> +static PCIBridge *i82801ba11_init(PCIBus *bus, int devfn, const char
> *bus_name,
> + bool multifunction)
> +{
> + const PCIP2PBridgeInit init = {
> + .bus = bus,
> + .devfn = devfn,
> + .multifunction = multifunction,
> +
> + .bus_name = bus_name,
> + .map_irq = pci_swizzle_map_irq_fn,
> + };
> + const PCIP2PBridgeProp prop = {
> + .vendor_id = PCI_VENDOR_ID_INTEL,
> + .device_id = PCI_DEVICE_ID_INTEL_82801BA_11,
> + .revision_id = ICH9_D2P_A2_REVISION,
> + .prog_interface = PCI_CLASS_BRDIGE_PCI_INF_SUB,
> +
> + .ssvid_cap = I82801ba_SSVID_OFFSET,
> + .svid = I82801ba_SSVID_SVID,
> + .ssid = I82801ba_SSVID_SSID,
> + };
> + return pci_p2pbr_create_simple(&init, &prop);
> +}
> +
> +PCIBus *ich9_d2pbr_init(PCIBus *bus, int devfn, int sec_bus)
> +{
> + PCIBridge *br;
> + char buf[16];
> +
> + snprintf(buf, sizeof(buf), "pci.%d", sec_bus);
> + br = i82801ba11_init(bus, devfn, buf, true);
> + if (br == NULL) {
> + return NULL;
> + }
> + return pci_bridge_get_sec_bus(br);
> +}
> +
> +
> +/*****************************************************************************/
> +/* ICH9 LPC PCI to ISA bridge */
> +
> +static void ich9_lpc_reset(DeviceState *qdev);
> +
> +static ICH9_LPCState *ich9_lpc_from_pci(PCIDevice *lpc_pci)
> +{
> + return DO_UPCAST(ICH9_LPCState, d, lpc_pci);
> +}
> +
> +/* chipset configuration register
> + * to access chipset configuration registers, pci_[sg]et_{byte, word, long}
> + * are used.
> + * Although it's not pci configuration space, it's little endian as Intel.
> + */
> +
> +static void ich9_cc_update_ir(uint8_t irr[PCI_NUM_PINS], uint32_t ir)
> +{
> + int intx;
> + for (intx = 0; intx < PCI_NUM_PINS; intx++) {
> + irr[intx] = (ir >> (intx * ICH9_CC_DIR_SHIFT)) & ICH9_CC_DIR_MASK;
> + }
> +}
> +
> +static void ich9_cc_update(ICH9_LPCState *lpc)
> +{
> + int slot;
> + int reg_offset;
> + int intx;
> +
> + /* D{25 - 31}IR, but D30IR is read only to 0. */
> + for (slot = 25, reg_offset = 0; slot < 32; slot++, reg_offset++) {
> + if (slot != 30) {
> + ich9_cc_update_ir(lpc->irr[slot],
> + lpc->chip_config[ICH9_CC_D31IR + reg_offset]);
> + }
> + }
> +
> + /*
> + * D30: DMI2PCI bridge
> + * It is arbitrarily decided how INTx lines of PCI devicesbehind the
> bridge
> + * are connected to pirq lines. Our choice is PIRQ[E-H].
> + * INT[A-D] are connected to PIRQ[E-H]
> + */
> + for (intx = 0; intx < PCI_NUM_PINS; intx++) {
> + lpc->irr[30][intx] = intx + 4;
> + }
> +}
> +
> +static void ich9_cc_init(ICH9_LPCState *lpc)
> +{
> + int slot;
> + int intx;
> +
> + /* the default irq routing is arbitrary as long as it matches with
> + * acpi irq routing table.
> + * The one that is incompatible with piix_pci(= bochs) one is
> + * intentionally chosen to let the users know that the different
> + * board is used.
> + *
> + * int[A-D] -> pirq[E-F]
> + * avoid pirq A-D because they are used for pci express port
> + */
> + for (slot = 0; slot < PCI_SLOT_MAX; slot++) {
> + for (intx = 0; intx < PCI_NUM_PINS; intx++) {
> + lpc->irr[slot][intx] = (slot + intx) % 4 + 4;
> + }
> + }
> + ich9_cc_update(lpc);
> +}
> +
> +static void ich9_cc_reset(ICH9_LPCState *lpc)
> +{
> + uint8_t *c = lpc->chip_config;
> +
> + memset(lpc->chip_config, 0, sizeof(lpc->chip_config));
> +
> + pci_set_long(c + ICH9_CC_D31IR, ICH9_CC_DIR_DEFAULT);
> + pci_set_long(c + ICH9_CC_D30IR, ICH9_CC_D30IR_DEFAULT);
> + pci_set_long(c + ICH9_CC_D29IR, ICH9_CC_DIR_DEFAULT);
> + pci_set_long(c + ICH9_CC_D28IR, ICH9_CC_DIR_DEFAULT);
> + pci_set_long(c + ICH9_CC_D27IR, ICH9_CC_DIR_DEFAULT);
> + pci_set_long(c + ICH9_CC_D26IR, ICH9_CC_DIR_DEFAULT);
> + pci_set_long(c + ICH9_CC_D25IR, ICH9_CC_DIR_DEFAULT);
> +
> + ich9_cc_update(lpc);
> +}
> +
> +static void ich9_cc_addr_len(uint32_t *addr, int *len)
> +{
> + *addr &= ICH9_CC_ADDR_MASK;
> + if (*addr + *len >= ICH9_CC_SIZE) {
> + *len = ICH9_CC_SIZE - *addr;
> + }
> +}
> +
> +/* val: little endian */
> +static void ich9_cc_write(ICH9_LPCState *lpc, uint32_t addr,
> + uint32_t val, int len)
> +{
> + ich9_cc_addr_len(&addr, &len);
> + memcpy(lpc->chip_config + addr, &val, len);
> +}
> +
> +/* return value: little endian */
> +static uint32_t ich9_cc_read(ICH9_LPCState *lpc, uint32_t addr, int len)
> +{
> + uint32_t val = 0;
> + ich9_cc_addr_len(&addr, &len);
> + memcpy(&val, lpc->chip_config + addr, len);
> + return val;
> +}
> +
> +#define ICH9_CC_MMIO_WRITE(type, len) \
> + static void ich9_cc_mmio_write ## type \
> + (void *opaque, target_phys_addr_t addr, uint32_t val) \
> + { \
> + ich9_cc_write(opaque, addr, val, len); \
> + }
> +
> +#define ICH9_CC_MMIO_READ(type, len) \
> + static uint32_t ich9_cc_mmio_read ## type \
> + (void *opaque, target_phys_addr_t addr) \
> + { \
> + return ich9_cc_read(opaque, addr, len); \
> + }
> +
> +ICH9_CC_MMIO_WRITE(b, 1)
> +ICH9_CC_MMIO_WRITE(w, 2)
> +ICH9_CC_MMIO_WRITE(l, 4)
> +
> +ICH9_CC_MMIO_READ(b, 1)
> +ICH9_CC_MMIO_READ(w, 2)
> +ICH9_CC_MMIO_READ(l, 4)
> +
> +static CPUWriteMemoryFunc * const ich9_cc_mmio_write[] = {
> + ich9_cc_mmio_writeb,
> + ich9_cc_mmio_writew,
> + ich9_cc_mmio_writel,
> +};
> +
> +static CPUReadMemoryFunc * const ich9_cc_mmio_read[] = {
> + ich9_cc_mmio_readb,
> + ich9_cc_mmio_readw,
> + ich9_cc_mmio_readl,
> +};
> +
> +/* IRQ routing */
> +/* */
> +static void ich9_lpc_rout(uint8_t pirq_rout, int *pic_irq, int *pic_dis)
> +{
> + *pic_irq = pirq_rout & ICH9_LPC_PIRQ_ROUT_MASK;
> + *pic_dis = pirq_rout & ICH9_LPC_PIRQ_ROUT_IRQEN;
> +}
> +
> +static void ich9_lpc_pic_irq(ICH9_LPCState *lpc, int irq_num,
> + int *pic_irq, int *pic_dis)
> +{
> + switch (irq_num) {
> + case 0 ... 3: /* A-D */
> + ich9_lpc_rout(lpc->d.config[ICH9_LPC_PIRQA_ROUT + irq_num],
> + pic_irq, pic_dis);
> + return;
> + case 4 ... 7: /* E-H */
> + ich9_lpc_rout(lpc->d.config[ICH9_LPC_PIRQE_ROUT + (irq_num - 4)],
> + pic_irq, pic_dis);
> + return;
> + default:
> + break;
> + }
> + abort();
> +}
> +
> +/* pic_irq: i8254 irq 0-15 */
> +static void ich9_lpc_update_pic(ICH9_LPCIrqState *irq_state, int pic_irq)
> +{
> + GMCH_PCIHost *s = container_of(irq_state, GMCH_PCIHost, irq_state);
> + ICH9_LPCState *lpc = irq_state->lpc;
> + int i, pic_level;
> +
> + /* The pic level is the logical OR of all the PCI irqs mapped to it */
> + pic_level = 0;
> + for (i = 0; i < ICH9_LPC_NB_PIRQS; i++) {
> + int tmp_irq;
> + int tmp_dis;
> + ich9_lpc_pic_irq(lpc, i, &tmp_irq, &tmp_dis);
> + if (!tmp_dis && pic_irq == tmp_irq) {
> + pic_level |= pci_bus_get_irq_level(s->host.pci.bus, i);
> + }
> + }
> + if (pic_irq == ich9_lpc_sci_irq(lpc)) {
> + pic_level |= lpc->sci_level;
> + }
> +
> + qemu_set_irq(irq_state->pic[pic_irq], pic_level);
> +}
> +
> +/* pirq: pirq[A-H] 0-7*/
> +static void ich9_lpc_update_by_pirq(ICH9_LPCIrqState *irq_state, int pirq)
> +{
> + ICH9_LPCState *lpc = irq_state->lpc;
> + int pic_irq;
> + int pic_dis;
> +
> + ich9_lpc_pic_irq(lpc, pirq, &pic_irq, &pic_dis);
> + assert(pic_irq < ICH9_LPC_PIC_NUM_PINS);
> + if (pic_dis) {
> + return;
> + }
> +
> + ich9_lpc_update_pic(irq_state, pic_irq);
> +}
> +
> +/* APIC mode: GSIx: PIRQ[A-H] -> GSI 16, ... no pirq shares same APIC pins.
> */
> +static int ich9_pirq_to_gsi(int pirq)
> +{
> + return pirq + ICH9_LPC_PIC_NUM_PINS;
> +}
> +
> +static int ich9_gsi_to_pirq(int gsi)
> +{
> + return gsi - ICH9_LPC_PIC_NUM_PINS;
> +}
> +
> +static void ich9_lpc_update_apic(ICH9_LPCIrqState *irq_state, int gsi)
> +{
> + GMCH_PCIHost *s = container_of(irq_state, GMCH_PCIHost, irq_state);
> + ICH9_LPCState *lpc = irq_state->lpc;
> + int level;
> +
> + level = pci_bus_get_irq_level(s->host.pci.bus, ich9_gsi_to_pirq(gsi));
> + if (gsi == ich9_lpc_sci_irq(lpc)) {
> + level |= lpc->sci_level;
> + }
> +
> + qemu_set_irq(irq_state->ioapic[gsi], level);
> +}
> +
> +/* return the pirq number (PIRQ[A-H]:0-7) corresponding to
> + a given device irq pin. */
> +static int ich9_lpc_map_irq(void *opaque, PCIDevice *pci_dev, int intx)
> +{
> + ICH9_LPCIrqState *irq_state = opaque;
> + return irq_state->lpc->irr[PCI_SLOT(pci_dev->devfn)][intx];
> +}
> +
> +static void ich9_lpc_set_irq(void *opaque, int pirq, int level)
> +{
> + ICH9_LPCIrqState *irq_state = opaque;
> +
> + assert(0 <= pirq);
> + assert(pirq < ICH9_LPC_NB_PIRQS);
> +
> + ich9_lpc_update_apic(irq_state, ich9_pirq_to_gsi(pirq));
> + ich9_lpc_update_by_pirq(irq_state, pirq);
> +}
> +
> +static int ich9_lpc_sci_irq(ICH9_LPCState *lpc)
> +{
> + switch (lpc->d.config[ICH9_LPC_ACPI_CTRL] &
> + ICH9_LPC_ACPI_CTRL_SCI_IRQ_SEL_MASK) {
> + case ICH9_LPC_ACPI_CTRL_9:
> + return 9;
> + case ICH9_LPC_ACPI_CTRL_10:
> + return 10;
> + case ICH9_LPC_ACPI_CTRL_11:
> + return 11;
> + case ICH9_LPC_ACPI_CTRL_20:
> + return 20;
> + case ICH9_LPC_ACPI_CTRL_21:
> + return 21;
> + default:
> + /* reserved */
> + break;
> + }
> + return -1;
> +}
> +
> +static void ich9_set_sci(void *opaque, int irq_num, int level)
> +{
> + ICH9_LPCIrqState *irq_state = opaque;
> + ICH9_LPCState *lpc = irq_state->lpc;
> + int irq;
> +
> + assert(irq_num == 0);
> + level = !!level;
> + if (level == lpc->sci_level) {
> + return;
> + }
> + lpc->sci_level = level;
> +
> + irq = ich9_lpc_sci_irq(lpc);
> + if (irq < 0) {
> + return;
> + }
> +
> + ich9_lpc_update_apic(irq_state, irq);
> + if (irq < ICH9_LPC_PIC_NUM_PINS) {
> + ich9_lpc_update_pic(irq_state, irq);
> + }
> +}
> +
> +void ich9_lpc_pm_init(DeviceState *gmch_host, PCIDevice *lpc_pci,
> + qemu_irq cmos_s3)
> +{
> + GMCH_PCIHost *s = gmch_pcihost_from_qdev(gmch_host);
> + ICH9_LPCState *lpc = ich9_lpc_from_pci(lpc_pci);
> + qemu_irq *sci_irq;
> +
> + sci_irq = qemu_allocate_irqs(ich9_set_sci, &s->irq_state, 1);
> + ich9_pm_init(&lpc->pm, sci_irq[0], cmos_s3);
> +
> + ich9_lpc_reset(&lpc->d.qdev);
> +}
> +
> +/* APM */
> +static void ich9_apm_ctrl_changed(uint32_t val, void *arg)
> +{
> + ICH9_LPCState *lpc = arg;
> +
> + /* ACPI specs 3.0, 4.7.2.5 */
> + acpi_pm1_cnt_update(&lpc->pm.pm1_cnt,
> + val == ICH9_APM_ACPI_ENABLE,
> + val == ICH9_APM_ACPI_DISABLE);
> +
> + /* SMI_EN = PMBASE + 30. SMI control and enable register */
> + if (lpc->pm.smi_en & ICH9_PMIO_SMI_EN_APMC_EN) {
> + cpu_interrupt(first_cpu, CPU_INTERRUPT_SMI);
> + }
> +}
> +
> +/* config:PMBASE */
> +static void
> +ich9_lpc_pmbase_update(ICH9_LPCState *lpc)
> +{
> + uint32_t pm_io_base = pci_get_long(lpc->d.config + ICH9_LPC_PMBASE);
> + pm_io_base &= ICH9_LPC_PMBASE_BASE_ADDRESS_MASK;
> +
> + ich9_pm_iospace_update(&lpc->pm, pm_io_base);
> +}
> +
> +/* config:RBCA */
> +static void ich9_lpc_rcba_update(ICH9_LPCState *lpc, uint32_t rbca_old)
> +{
> + uint32_t rbca = pci_get_long(lpc->d.config + ICH9_LPC_RCBA);
> +
> + if (rbca_old & ICH9_LPC_RCBA_EN) {
> + cpu_register_physical_memory(rbca_old & ICH9_LPC_RCBA_BA_MASK,
> + ICH9_CC_SIZE, IO_MEM_UNASSIGNED);
> + }
> + if (rbca & ICH9_LPC_RCBA_EN) {
> + cpu_register_physical_memory(rbca & ICH9_LPC_RCBA_BA_MASK,
> + ICH9_CC_SIZE, lpc->rbca_index);
> + }
> +}
> +
> +static int ich9_lpc_post_load(void *opaque, int version_id)
> +{
> + ICH9_LPCState *lpc = opaque;
> +
> + ich9_lpc_pmbase_update(lpc);
> + ich9_lpc_rcba_update(lpc, 0 /* disabled ICH9_LPC_RBCA_EN */);
> + return 0;
> +}
> +
> +static void ich9_lpc_config_write(PCIDevice *d,
> + uint32_t addr, uint32_t val, int len)
> +{
> + ICH9_LPCState *lpc = ich9_lpc_from_pci(d);
> + uint32_t rbca_old = pci_get_long(d->config + ICH9_LPC_RCBA);
> +
> + pci_default_write_config(d, addr, val, len);
> + if (ranges_overlap(addr, len, ICH9_LPC_PMBASE, 4)) {
> + ich9_lpc_pmbase_update(lpc);
> + }
> + if (ranges_overlap(addr, len, ICH9_LPC_RCBA, 4)) {
> + ich9_lpc_rcba_update(lpc, rbca_old);
> + }
> +}
> +
> +static void ich9_lpc_reset(DeviceState *qdev)
> +{
> + PCIDevice *d = DO_UPCAST(PCIDevice, qdev, qdev);
> + ICH9_LPCState *lpc = ich9_lpc_from_pci(d);
> + uint32_t rbca_old = pci_get_long(d->config + ICH9_LPC_RCBA);
> + int i;
> +
> + for (i = 0; i < 4; i++) {
> + pci_set_byte(d->config + ICH9_LPC_PIRQA_ROUT + i,
> + ICH9_LPC_PIRQ_ROUT_DEFAULT);
> + }
> + for (i = 0; i < 4; i++) {
> + pci_set_byte(d->config + ICH9_LPC_PIRQE_ROUT + i,
> + ICH9_LPC_PIRQ_ROUT_DEFAULT);
> + }
> + pci_set_byte(d->config + ICH9_LPC_ACPI_CTRL, ICH9_LPC_ACPI_CTRL_DEFAULT);
> +
> + pci_set_long(d->config + ICH9_LPC_PMBASE, ICH9_LPC_PMBASE_DEFAULT);
> + pci_set_long(d->config + ICH9_LPC_RCBA, ICH9_LPC_RCBA_DEFAULT);
> +
> + ich9_cc_reset(lpc);
> +
> + ich9_lpc_pmbase_update(lpc);
> + ich9_lpc_rcba_update(lpc, rbca_old);
> +
> + lpc->sci_level = 0;
> +}
> +
> +static int ich9_lpc_initfn(PCIDevice *d)
> +{
> + ICH9_LPCState *lpc = ich9_lpc_from_pci(d);
> +
> + isa_bus_new(&d->qdev);
> + pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_INTEL);
> + pci_config_set_device_id(d->config, PCI_DEVICE_ID_INTEL_ICH9_8); /* ICH9
> LPC */
> + pci_config_set_revision(d->config, ICH9_A2_LPC_REVISION);
> + pci_config_set_class(d->config, PCI_CLASS_BRIDGE_ISA);
> +
> + pci_set_long(d->wmask + ICH9_LPC_PMBASE,
> + ICH9_LPC_PMBASE_BASE_ADDRESS_MASK);
> +
> + lpc->rbca_index = cpu_register_io_memory(ich9_cc_mmio_read,
> + ich9_cc_mmio_write,
> + lpc, DEVICE_LITTLE_ENDIAN);
> +
> + ich9_cc_init(lpc);
> + apm_init(&lpc->apm, ich9_apm_ctrl_changed, lpc);
> + return 0;
> +}
> +
> +static const VMStateDescription vmstate_ich9_lpc = {
> + .name = "ICH9LPC",
> + .version_id = 1,
> + .minimum_version_id = 1,
> + .minimum_version_id_old = 1,
> + .post_load = ich9_lpc_post_load,
> + .fields = (VMStateField[]) {
> + VMSTATE_PCI_DEVICE(d, ICH9_LPCState),
> + VMSTATE_STRUCT(apm, ICH9_LPCState, 0, vmstate_apm, APMState),
> + VMSTATE_STRUCT(pm, ICH9_LPCState, 0, vmstate_ich9_pm,
> ICH9_LPCPmRegs),
> + VMSTATE_UINT8_ARRAY(chip_config, ICH9_LPCState, ICH9_CC_SIZE),
> + VMSTATE_UINT32(sci_level, ICH9_LPCState),
> + VMSTATE_END_OF_LIST()
> + }
> +};
> +
> +PCIDevice *gmch_lpc_init(DeviceState *gmch_host, PCIBus *bus)
> +{
> + GMCH_PCIHost *s = gmch_pcihost_from_qdev(gmch_host);
> + PCIDevice *d;
> + ICH9_LPCState *lpc;
> +
> + d = pci_create_simple_multifunction(bus, PCI_DEVFN(ICH9_LPC_DEV,
> + ICH9_LPC_FUNC),
> + true, "ICH9 LPC");
> + lpc = ich9_lpc_from_pci(d);
> + s->irq_state.lpc = lpc;
> + return &lpc->d;
> +}
> +
> +static PCIDeviceInfo ich9_lpc_info = {
> + .qdev.name = "ICH9 LPC",
> + .qdev.desc = "ICH9 LPC bridge",
> + .qdev.size = sizeof(ICH9_LPCState),
> + .qdev.vmsd = &vmstate_ich9_lpc,
> + .qdev.no_user = 1,
> + .init = ich9_lpc_initfn,
> + .config_write = ich9_lpc_config_write,
> + .qdev.reset = ich9_lpc_reset,
> +};
> +
> +static void q35_register(void)
> +{
> + sysbus_register_withprop(&gmch_pcihost_info);
> + pci_qdev_register(&gmch_info);
> + pci_qdev_register(&ich9_lpc_info);
> +}
> +device_init(q35_register);
> diff --git a/hw/q35.h b/hw/q35.h
> new file mode 100644
> index 0000000..be2e96b
> --- /dev/null
> +++ b/hw/q35.h
> @@ -0,0 +1,272 @@
> +/*
> + * q35.h
> + *
> + * Copyright (c) 2009 Isaku Yamahata <yamahata at valinux co jp>
> + * VA Linux Systems Japan K.K.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> <http://www.gnu.org/licenses/>
> + */
> +
> +#ifndef HW_Q35_H
> +#define HW_Q35_H
> +
> +#include "sysbus.h"
> +#include "acpi_ich9.h"
> +
> +PCIBus *gmch_host_init(DeviceState **gmch_hostp,
> + qemu_irq *pic, qemu_irq *ioapic);
> +
> +PCIDevice *gmch_init(DeviceState *gmch_host, PCIBus *b);
> +PCIBus *ich9_d2pbr_init(PCIBus *bus, int devfn, int sec_bus);
> +PCIDevice *gmch_lpc_init(DeviceState *gmch_host, PCIBus *bus);
> +void ich9_lpc_pm_init(DeviceState *gmch_host, PCIDevice *pci_lpc,
> + qemu_irq cmos_s3);
> +
> +i2c_bus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base);
> +
> +#define Q35_MASK(bit, ms_bit, ls_bit) \
> +((uint##bit##_t)(((1ULL << ((ms_bit) + 1)) - 1) & ~((1ULL << ls_bit) - 1)))
> +
> +/*
> + * gmch part
> + */
> +
> +/* PCI configuration */
> +#define GMCH_HOST_BRIDGE "GMCH"
> +
> +#define GMCH_HOST_BRIDGE_CONFIG_ADDR 0xcf8
> +#define GMCH_HOST_BRIDGE_CONFIG_DATA 0xcfc
> +
> +/* D0:F0 configuration space */
> +#define GMCH_HOST_BRIDGE_REVISION_DEFUALT 0x0
> +
> +#define GMCH_HOST_BRIDGE_PCIEXBAR 0x60 /* 64bit register */
> +#define GMCH_HOST_BRIDGE_PCIEXBAR_SIZE 8 /* 64bit register */
> +#define GMCH_HOST_BRIDGE_PCIEXBAR_DEFAULT 0xe0000000
> +#define GMCH_HOST_BRIDGE_PCIEXBAR_ADMSK Q35_MASK(64, 35, 25) /* bit
> 35:28 */
> +#define GMCH_HOST_BRIDGE_PCIEXBAR_128ADMSK ((uint64_t)(1 << 26))
> +#define GMCH_HOST_BRIDGE_PCIEXBAR_64ADMSK ((uint64_t)(1 << 25))
> +#define GMCH_HOST_BRIDGE_PCIEXBAR_LENGTH_MASK ((uint64_t)(0x3 << 1))
> +#define GMCH_HOST_BRIDGE_PCIEXBAR_LENGTH_256M ((uint64_t)(0x0 << 1))
> +#define GMCH_HOST_BRIDGE_PCIEXBAR_LENGTH_128M ((uint64_t)(0x1 << 1))
> +#define GMCH_HOST_BRIDGE_PCIEXBAR_LENGTH_64M ((uint64_t)(0x2 << 1))
> +#define GMCH_HOST_BRIDGE_PCIEXBAR_LENGTH_RVD ((uint64_t)(0x3 << 1))
> +#define GMCH_HOST_BRIDGE_PCIEXBAREN ((uint64_t)1)
> +
> +#define GMCH_HOST_BRIDGE_PAM_NB 7
> +#define GMCH_HOST_BRIDGE_PAM_SIZE 7
> +#define GMCH_HOST_BRIDGE_PAM0 0x90
> +#define GMCH_HOST_BRIDGE_PAM_BIOS_AREA 0xf0000
> +#define GMCH_HOST_BRIDGE_PAM_AREA_SIZE 0x10000 /* 16KB */
> +#define GMCH_HOST_BRIDGE_PAM1 0x91
> +#define GMCH_HOST_BRIDGE_PAM_EXPAN_AREA 0xc0000
> +#define GMCH_HOST_BRIDGE_PAM_EXPAN_SIZE 0x04000
> +#define GMCH_HOST_BRIDGE_PAM2 0x92
> +#define GMCH_HOST_BRIDGE_PAM3 0x93
> +#define GMCH_HOST_BRIDGE_PAM4 0x94
> +#define GMCH_HOST_BRIDGE_PAM_EXBIOS_AREA 0xe0000
> +#define GMCH_HOST_BRIDGE_PAM_EXBIOS_SIZE 0x04000
> +#define GMCH_HOST_BRIDGE_PAM5 0x95
> +#define GMCH_HOST_BRIDGE_PAM6 0x96
> +#define GMCH_HOST_BRIDGE_PAM_WE_HI ((uint8_t)(0x2 << 4))
> +#define GMCH_HOST_BRIDGE_PAM_RE_HI ((uint8_t)(0x1 << 4))
> +#define GMCH_HOST_BRIDGE_PAM_HI_MASK ((uint8_t)(0x3 << 4))
> +#define GMCH_HOST_BRIDGE_PAM_WE_LO ((uint8_t)0x2)
> +#define GMCH_HOST_BRIDGE_PAM_RE_LO ((uint8_t)0x1)
> +#define GMCH_HOST_BRIDGE_PAM_LO_MASK ((uint8_t)0x3)
> +#define GMCH_HOST_BRIDGE_PAM_WE ((uint8_t)0x2)
> +#define GMCH_HOST_BRIDGE_PAM_RE ((uint8_t)0x1)
> +#define GMCH_HOST_BRIDGE_PAM_MASK ((uint8_t)0x3)
> +
> +#define GMCH_HOST_BRDIGE_SMRAM 0x9d
> +#define GMCH_HOST_BRDIGE_SMRAM_SIZE 1
> +#define GMCH_HOST_BRIDGE_SMRAM_DEFAULT ((uint8_t)0x2)
> +#define GMCH_HOST_BRIDGE_SMRAM_D_OPEN ((uint8_t)(1 << 6))
> +#define GMCH_HOST_BRIDGE_SMRAM_D_CLS ((uint8_t)(1 << 5))
> +#define GMCH_HOST_BRIDGE_SMRAM_D_LCK ((uint8_t)(1 << 4))
> +#define GMCH_HOST_BRIDGE_SMRAM_G_SMRAME ((uint8_t)(1 << 3))
> +#define GMCH_HOST_BRIDGE_SMRAM_C_BASE_SEG_MASK ((uint8_t)0x7)
> +#define GMCH_HOST_BRIDGE_SMRAM_C_BASE_SEG ((uint8_t)0x2) /* hardwired
> to b010 */
> +#define GMCH_HOST_BRIDGE_SMRAM_C_BASE 0xa0000
> +#define GMCH_HOST_BRIDGE_SMRAM_C_END 0xc0000
> +#define GMCH_HOST_BRIDGE_SMRAM_C_SIZE 0x20000
> +#define GMCH_HOST_BRIDGE_UPPER_SYSTEM_BIOS_END 0x100000
> +
> +#define GMCH_HOST_BRIDGE_ESMRAMC 0x9e
> +#define GMCH_HOST_BRDIGE_ESMRAMC_H_SMRAME ((uint8_t)(1 << 6))
> +#define GMCH_HOST_BRDIGE_ESMRAMC_E_SMERR ((uint8_t)(1 << 5))
> +#define GMCH_HOST_BRDIGE_ESMRAMC_SM_CACHE ((uint8_t)(1 << 4))
> +#define GMCH_HOST_BRDIGE_ESMRAMC_SM_L1 ((uint8_t)(1 << 3))
> +#define GMCH_HOST_BRDIGE_ESMRAMC_SM_L2 ((uint8_t)(1 << 2))
> +#define GMCH_HOST_BRDIGE_ESMRAMC_TSEG_SZ_MASK ((uint8_t)(0x3 << 1))
> +#define GMCH_HOST_BRDIGE_ESMRAMC_TSEG_SZ_1MB ((uint8_t)(0x0 << 1))
> +#define GMCH_HOST_BRDIGE_ESMRAMC_TSEG_SZ_2MB ((uint8_t)(0x1 << 1))
> +#define GMCH_HOST_BRDIGE_ESMRAMC_TSEG_SZ_8MB ((uint8_t)(0x2 << 1))
> +#define GMCH_HOST_BRDIGE_ESMRAMC_T_EN ((uint8_t)1)
> +
> +/* D1:F0 PCIE* port*/
> +#define GMCH_PCIE_DEV 1
> +#define GMCH_PCIE_FUNC 0
> +
> +/*
> + * ich9 part
> + */
> +
> +/* ICH9: Chipset Configuration Registers */
> +#define ICH9_CC_SIZE (16 * 1024) /* 16KB */
> +#define ICH9_CC_ADDR_MASK (ICH9_CC_SIZE - 1)
> +
> +#define ICH9_CC
> +#define ICH9_CC_D28IP 0x310C
> +#define ICH9_CC_D28IP_SHIFT 4
> +#define ICH9_CC_D28IP_MASK 0xf
> +#define ICH9_CC_D28IP_DEFAULT 0x00214321
> +#define ICH9_CC_D31IR 0x3140
> +#define ICH9_CC_D30IR 0x3142
> +#define ICH9_CC_D29IR 0x3144
> +#define ICH9_CC_D28IR 0x3146
> +#define ICH9_CC_D27IR 0x3148
> +#define ICH9_CC_D26IR 0x314C
> +#define ICH9_CC_D25IR 0x3150
> +#define ICH9_CC_DIR_DEFAULT 0x3210
> +#define ICH9_CC_D30IR_DEFAULT 0x0
> +#define ICH9_CC_DIR_SHIFT 4
> +#define ICH9_CC_DIR_MASK 0x7
> +#define ICH9_CC_OIC 0x31FF
> +#define ICH9_CC_OIC_AEN 0x1
> +
> +/* D28:F[0-5] */
> +#define ICH9_PCIE_DEV 28
> +#define ICH9_PCIE_FUNC_MAX 6
> +
> +
> +/* D29:F0 USB UHCI Controller #1 */
> +#define ICH9_USB_UHCI1_DEV 29
> +#define ICH9_USB_UHCI1_FUNC 0
> +
> +/* D30:F0 DMI-to-PCI brdige */
> +#define ICH9_D2P_BRIDGE "ICH9 D2P BRIDGE"
> +#define ICH9_D2P_BRIDGE_SAVEVM_VERSION 0
> +
> +#define ICH9_D2P_BRIDGE_DEV 30
> +#define ICH9_D2P_BRIDGE_FUNC 0
> +
> +#define ICH9_D2P_SECONDARY_DEFAULT (256 - 8)
> +
> +#define ICH9_D2P_A2_REVISION 0x92
> +
> +
> +/* D31:F1 LPC controller */
> +#define ICH9_A2_LPC "ICH9 A2 LPC"
> +#define ICH9_A2_LPC_SAVEVM_VERSION 0
> +
> +#define ICH9_LPC_DEV 31
> +#define ICH9_LPC_FUNC 0
> +
> +#define ICH9_A2_LPC_REVISION 0x2
> +#define ICH9_LPC_NB_PIRQS 8 /* PCI A-H */
> +
> +#define ICH9_LPC_PMBASE 0x40
> +#define ICH9_LPC_PMBASE_BASE_ADDRESS_MASK Q35_MASK(32, 15, 7)
> +#define ICH9_LPC_PMBASE_RTE 0x1
> +#define ICH9_LPC_PMBASE_DEFAULT 0x1
> +#define ICH9_LPC_ACPI_CTRL 0x44
> +#define ICH9_LPC_ACPI_CTRL_ACPI_EN 0x80
> +#define ICH9_LPC_ACPI_CTRL_SCI_IRQ_SEL_MASK Q35_MASK(8, 2, 0)
> +#define ICH9_LPC_ACPI_CTRL_9 0x0
> +#define ICH9_LPC_ACPI_CTRL_10 0x1
> +#define ICH9_LPC_ACPI_CTRL_11 0x2
> +#define ICH9_LPC_ACPI_CTRL_20 0x4
> +#define ICH9_LPC_ACPI_CTRL_21 0x5
> +#define ICH9_LPC_ACPI_CTRL_DEFAULT 0x0
> +
> +#define ICH9_LPC_PIRQA_ROUT 0x60
> +#define ICH9_LPC_PIRQB_ROUT 0x61
> +#define ICH9_LPC_PIRQC_ROUT 0x62
> +#define ICH9_LPC_PIRQD_ROUT 0x63
> +
> +#define ICH9_LPC_PIRQE_ROUT 0x68
> +#define ICH9_LPC_PIRQF_ROUT 0x69
> +#define ICH9_LPC_PIRQG_ROUT 0x6a
> +#define ICH9_LPC_PIRQH_ROUT 0x6b
> +
> +#define ICH9_LPC_PIRQ_ROUT_IRQEN 0x80
> +#define ICH9_LPC_PIRQ_ROUT_MASK Q35_MASK(8, 3, 0)
> +#define ICH9_LPC_PIRQ_ROUT_DEFAULT 0x80
> +
> +#define ICH9_LPC_RCBA 0xf0
> +#define ICH9_LPC_RCBA_BA_MASK Q35_MASK(32, 31, 14)
> +#define ICH9_LPC_RCBA_EN 0x1
> +#define ICH9_LPC_RCBA_DEFAULT 0x0
> +
> +#define ICH9_LPC_PIC_NUM_PINS 16
> +#define ICH9_LPC_IOAPIC_NUM_PINS 24
> +
> +/* D31:F2 SATA Controller #1 */
> +#define ICH9_SATA1_DEV 31
> +#define ICH9_SATA1_FUNC 2
> +
> +/* D30:F1 power management I/O registers
> + offset from the address ICH9_LPC_PMBASE */
> +
> +/* ICH9 LPC PM I/O registers are 128 ports and 128-aligned */
> +#define ICH9_PMIO_SIZE 128
> +#define ICH9_PMIO_MASK (ICH9_PMIO_SIZE - 1)
> +
> +#define ICH9_PMIO_PM1_STS 0x00
> +#define ICH9_PMIO_PM1_EN 0x02
> +#define ICH9_PMIO_PM1_CNT 0x04
> +#define ICH9_PMIO_PM1_TMR 0x08
> +#define ICH9_PMIO_GPE0_STS 0x20
> +#define ICH9_PMIO_GPE0_EN 0x28
> +#define ICH9_PMIO_GPE0_LEN 16
> +#define ICH9_PMIO_SMI_EN 0x30
> +#define ICH9_PMIO_SMI_EN_APMC_EN (1 << 5)
> +#define ICH9_PMIO_SMI_STS 0x34
> +
> +/* FADT ACPI_ENABLE/ACPI_DISABLE */
> +#define ICH9_APM_ACPI_ENABLE 0x2
> +#define ICH9_APM_ACPI_DISABLE 0x3
> +
> +
> +/* D31:F3 SMBus controller */
> +#define ICH9_A2_SMB_REVISION 0x02
> +#define ICH9_SMB_PI 0x00
> +
> +#define ICH9_SMB_SMBMBAR0 0x10
> +#define ICH9_SMB_SMBMBAR1 0x14
> +#define ICH9_SMB_SMBM_BAR 0
> +#define ICH9_SMB_SMBM_SIZE (1 << 8)
> +#define ICH9_SMB_SMB_BASE 0x20
> +#define ICH9_SMB_SMB_BASE_BAR 4
> +#define ICH9_SMB_SMB_BASE_SIZE (1 << 5)
> +#define ICH9_SMB_HOSTC 0x40
> +#define ICH9_SMB_HOSTC_SSRESET ((uint8_t)(1 << 3))
> +#define ICH9_SMB_HOSTC_I2C_EN ((uint8_t)(1 << 2))
> +#define ICH9_SMB_HOSTC_SMB_SMI_EN ((uint8_t)(1 << 1))
> +#define ICH9_SMB_HOSTC_HST_EN ((uint8_t)(1 << 0))
> +
> +/* D31:F3 SMBus I/O and memory mapped I/O registers */
> +#define ICH9_SMB_DEV 31
> +#define ICH9_SMB_FUNC 3
> +
> +#define ICH9_SMB_HST_STS 0x00
> +#define ICH9_SMB_HST_CNT 0x02
> +#define ICH9_SMB_HST_CMD 0x03
> +#define ICH9_SMB_XMIT_SLVA 0x04
> +#define ICH9_SMB_HST_D0 0x05
> +#define ICH9_SMB_HST_D1 0x06
> +#define ICH9_SMB_HOST_BLOCK_DB 0x07
> +
> +#endif /* HW_Q35_H */
> diff --git a/hw/q35_smbus.c b/hw/q35_smbus.c
> new file mode 100644
> index 0000000..fe445ac
> --- /dev/null
> +++ b/hw/q35_smbus.c
> @@ -0,0 +1,154 @@
> +/*
> + * ACPI implementation
> + *
> + * Copyright (c) 2006 Fabrice Bellard
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License version 2 as published by the Free Software Foundation.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> <http://www.gnu.org/licenses/>
> + */
> +/*
> + * Copyright (c) 2009 Isaku Yamahata <yamahata at valinux co jp>
> + * VA Linux Systems Japan K.K.
> + *
> + * This is based on acpi.c, but heavily rewritten.
> + */
> +#include "hw.h"
> +#include "pc.h"
> +#include "pm_smbus.h"
> +#include "pci.h"
> +#include "sysemu.h"
> +#include "i2c.h"
> +#include "smbus.h"
> +
> +#include "q35.h"
> +
> +typedef struct ICH9_SMBState {
> + PCIDevice dev;
> +
> + PMSMBus smb;
> +} ICH9_SMBState;
> +
> +static ICH9_SMBState *ich9_pci_to_smb(PCIDevice* pci_dev)
> +{
> + return DO_UPCAST(ICH9_SMBState, dev, pci_dev);
> +}
> +
> +static const VMStateDescription vmstate_ich9_smbus = {
> + .name = "ich9_smb",
> + .version_id = 1,
> + .minimum_version_id = 1,
> + .minimum_version_id_old = 1,
> + .fields = (VMStateField[]) {
> + VMSTATE_PCI_DEVICE(dev, struct ICH9_SMBState),
> + VMSTATE_END_OF_LIST()
> + }
> +};
> +
> +static void ich9_smb_ioport_writeb(void *opaque, uint32_t addr, uint32_t val)
> +{
> + ICH9_SMBState *s = opaque;
> + uint8_t hostc = s->dev.config[ICH9_SMB_HOSTC];
> +
> + if ((hostc & ICH9_SMB_HOSTC_HST_EN) && !(hostc & ICH9_SMB_HOSTC_I2C_EN))
> {
> + uint64_t offset = addr -
> s->dev.io_regions[ICH9_SMB_SMB_BASE_BAR].addr;
> + smb_ioport_writeb(&s->smb, offset, val);
> + }
> +}
> +
> +static uint32_t ich9_smb_ioport_readb(void *opaque, uint32_t addr)
> +{
> + ICH9_SMBState *s = opaque;
> + uint8_t hostc = s->dev.config[ICH9_SMB_HOSTC];
> +
> + if ((hostc & ICH9_SMB_HOSTC_HST_EN) && !(hostc & ICH9_SMB_HOSTC_I2C_EN))
> {
> + uint64_t offset = addr -
> s->dev.io_regions[ICH9_SMB_SMB_BASE_BAR].addr;
> + return smb_ioport_readb(&s->smb, offset);
> + }
> +
> + return 0xff;
> +}
> +
> +static void ich9_smb_map_ioport(PCIDevice *dev, int region_num,
> + uint64_t addr, uint64_t size, int type)
> +{
> + ICH9_SMBState *s = ich9_pci_to_smb(dev);
> +
> + assert(size == ICH9_SMB_SMB_BASE_SIZE);
> + assert(type == PCI_BASE_ADDRESS_SPACE_IO);
> +
> + register_ioport_write(addr, 64, 1, ich9_smb_ioport_writeb, s);
> + register_ioport_read(addr, 64, 1, ich9_smb_ioport_readb, s);
> +}
> +
> +static int ich9_smb_initfn(PCIDevice *d)
> +{
> + ICH9_SMBState *s = ich9_pci_to_smb(d);
> +
> + pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_INTEL);
> + pci_config_set_device_id(d->config, PCI_DEVICE_ID_INTEL_ICH9_6);
> +
> + pci_set_word(d->wmask + PCI_STATUS,
> + PCI_STATUS_SIG_SYSTEM_ERROR | PCI_STATUS_DETECTED_PARITY);
> +
> + pci_config_set_revision(d->config, ICH9_A2_SMB_REVISION);
> + pci_config_set_prog_interface(d->config, ICH9_SMB_PI);
> + pci_config_set_class(d->config, PCI_CLASS_SERIAL_SMBUS);
> +
> + /* TODO? D31IP.SMIP in chipset configuration space */
> + pci_config_set_interrupt_pin(d->config, 0x01); /* interrupt pin 1 */
> +
> + pci_set_byte(d->config + ICH9_SMB_HOSTC, 0);
> +
> + /*
> + * update parameters based on
> + * paralell_hds[0]
> + * serial_hds[0]
> + * serial_hds[0]
> + * fdc
> + *
> + * Is there any OS that depends on them?
> + */
> +
> + /* TODO smb_io_base */
> + pci_set_byte(d->config + ICH9_SMB_HOSTC, 0);
> + /* TODO bar0, bar1: 64bit BAR support*/
> + pci_register_bar(d, ICH9_SMB_SMB_BASE_BAR,
> + ICH9_SMB_SMB_BASE_SIZE, PCI_BASE_ADDRESS_SPACE_IO,
> + &ich9_smb_map_ioport);
> +
> + pm_smbus_init(&d->qdev, &s->smb);
> + return 0;
> +}
> +
> +i2c_bus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base)
> +{
> + PCIDevice *d =
> + pci_create_simple_multifunction(bus, devfn, true, "ICH9 SMB");
> + ICH9_SMBState *s = ich9_pci_to_smb(d);
> + return s->smb.smbus;
> +}
> +
> +static PCIDeviceInfo ich9_smb_info = {
> + .qdev.name = "ICH9 SMB",
> + .qdev.desc = "ICH9 SMBUS Bridge",
> + .qdev.size = sizeof(ICH9_SMBState),
> + .qdev.vmsd = &vmstate_ich9_smbus,
> + .qdev.no_user = 1,
> + .init = ich9_smb_initfn,
> +};
> +
> +static void ich9_smb_register(void)
> +{
> + pci_qdev_register(&ich9_smb_info);
> +}
> +
> +device_init(ich9_smb_register);
> --
> 1.7.1
- Re: [Qemu-devel] [PATCH v2 15/21] q35: Fix non-PCI IRQ processing in ich9_lpc_update_apic, (continued)
- [Qemu-devel] [PATCH v2 19/21] Add a fallback bios file search, if -L fails., Jason Baron, 2012/10/08
- [Qemu-devel] [PATCH v2 16/21] q35: smbus: Remove PCI_STATUS_SIG_SYSTEM_ERROR and PCI_STATUS_DETECTED_PARITY from w1cmask, Jason Baron, 2012/10/08
- [Qemu-devel] [PATCH v2 18/21] q35: Fix irr initialization for slots 25..31, Jason Baron, 2012/10/08
- [Qemu-devel] [PATCH v2 12/21] q35: Introduce q35 pc based chipset emulator, Jason Baron, 2012/10/08
- Re: [Qemu-devel] [PATCH v2 12/21] q35: Introduce q35 pc based chipset emulator,
Michael S. Tsirkin <=
- [Qemu-devel] [PATCH v2 20/21] q35: automatically load the q35 dsdt table, Jason Baron, 2012/10/08
- [Qemu-devel] [PATCH v2 21/21] q35: add acpi-based pci hotplug., Jason Baron, 2012/10/08