aboutsummaryrefslogtreecommitdiffstats
path: root/hw/pci-bridge
diff options
context:
space:
mode:
Diffstat (limited to 'hw/pci-bridge')
-rw-r--r--hw/pci-bridge/Kconfig29
-rw-r--r--hw/pci-bridge/dec.c164
-rw-r--r--hw/pci-bridge/dec.h9
-rw-r--r--hw/pci-bridge/gen_pcie_root_port.c178
-rw-r--r--hw/pci-bridge/i82801b11.c122
-rw-r--r--hw/pci-bridge/ioh3420.c130
-rw-r--r--hw/pci-bridge/meson.build14
-rw-r--r--hw/pci-bridge/pci_bridge_dev.c308
-rw-r--r--hw/pci-bridge/pci_expander_bridge.c385
-rw-r--r--hw/pci-bridge/pcie_pci_bridge.c180
-rw-r--r--hw/pci-bridge/pcie_root_port.c198
-rw-r--r--hw/pci-bridge/simba.c102
-rw-r--r--hw/pci-bridge/xio3130_downstream.c190
-rw-r--r--hw/pci-bridge/xio3130_upstream.c159
14 files changed, 2168 insertions, 0 deletions
diff --git a/hw/pci-bridge/Kconfig b/hw/pci-bridge/Kconfig
new file mode 100644
index 000000000..f8df4315b
--- /dev/null
+++ b/hw/pci-bridge/Kconfig
@@ -0,0 +1,29 @@
+config PCIE_PORT
+ bool
+ default y if PCI_DEVICES
+ depends on PCI_EXPRESS && MSI_NONBROKEN
+
+config PXB
+ bool
+ default y if Q35 || ARM_VIRT
+
+config XIO3130
+ bool
+ default y if PCI_DEVICES
+ depends on PCI_EXPRESS && MSI_NONBROKEN
+
+config IOH3420
+ bool
+ default y if PCI_DEVICES
+ depends on PCI_EXPRESS && MSI_NONBROKEN
+
+config I82801B11
+ bool
+ default y if PCI_DEVICES
+ depends on PCI_EXPRESS
+
+config DEC_PCI
+ bool
+
+config SIMBA
+ bool
diff --git a/hw/pci-bridge/dec.c b/hw/pci-bridge/dec.c
new file mode 100644
index 000000000..4773d07e6
--- /dev/null
+++ b/hw/pci-bridge/dec.c
@@ -0,0 +1,164 @@
+/*
+ * QEMU DEC 21154 PCI bridge
+ *
+ * Copyright (c) 2006-2007 Fabrice Bellard
+ * Copyright (c) 2007 Jocelyn Mayer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "dec.h"
+#include "hw/sysbus.h"
+#include "qapi/error.h"
+#include "qemu/module.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pci_host.h"
+#include "hw/pci/pci_bridge.h"
+#include "hw/pci/pci_bus.h"
+#include "qom/object.h"
+
+OBJECT_DECLARE_SIMPLE_TYPE(DECState, DEC_21154)
+
+struct DECState {
+ PCIHostState parent_obj;
+};
+
+static int dec_map_irq(PCIDevice *pci_dev, int irq_num)
+{
+ return irq_num;
+}
+
+static void dec_pci_bridge_realize(PCIDevice *pci_dev, Error **errp)
+{
+ pci_bridge_initfn(pci_dev, TYPE_PCI_BUS);
+}
+
+static void dec_21154_pci_bridge_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
+ k->realize = dec_pci_bridge_realize;
+ k->exit = pci_bridge_exitfn;
+ k->vendor_id = PCI_VENDOR_ID_DEC;
+ k->device_id = PCI_DEVICE_ID_DEC_21154;
+ k->config_write = pci_bridge_write_config;
+ k->is_bridge = true;
+ dc->desc = "DEC 21154 PCI-PCI bridge";
+ dc->reset = pci_bridge_reset;
+ dc->vmsd = &vmstate_pci_device;
+}
+
+static const TypeInfo dec_21154_pci_bridge_info = {
+ .name = "dec-21154-p2p-bridge",
+ .parent = TYPE_PCI_BRIDGE,
+ .instance_size = sizeof(PCIBridge),
+ .class_init = dec_21154_pci_bridge_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { INTERFACE_CONVENTIONAL_PCI_DEVICE },
+ { },
+ },
+};
+
+PCIBus *pci_dec_21154_init(PCIBus *parent_bus, int devfn)
+{
+ PCIDevice *dev;
+ PCIBridge *br;
+
+ dev = pci_new_multifunction(devfn, false, "dec-21154-p2p-bridge");
+ br = PCI_BRIDGE(dev);
+ pci_bridge_map_irq(br, "DEC 21154 PCI-PCI bridge", dec_map_irq);
+ pci_realize_and_unref(dev, parent_bus, &error_fatal);
+ return pci_bridge_get_sec_bus(br);
+}
+
+static void pci_dec_21154_device_realize(DeviceState *dev, Error **errp)
+{
+ PCIHostState *phb;
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+ phb = PCI_HOST_BRIDGE(dev);
+
+ memory_region_init_io(&phb->conf_mem, OBJECT(dev), &pci_host_conf_le_ops,
+ dev, "pci-conf-idx", 0x1000);
+ memory_region_init_io(&phb->data_mem, OBJECT(dev), &pci_host_data_le_ops,
+ dev, "pci-data-idx", 0x1000);
+ sysbus_init_mmio(sbd, &phb->conf_mem);
+ sysbus_init_mmio(sbd, &phb->data_mem);
+}
+
+static void dec_21154_pci_host_realize(PCIDevice *d, Error **errp)
+{
+ /* PCI2PCI bridge same values as PearPC - check this */
+}
+
+static void dec_21154_pci_host_class_init(ObjectClass *klass, void *data)
+{
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
+ k->realize = dec_21154_pci_host_realize;
+ k->vendor_id = PCI_VENDOR_ID_DEC;
+ k->device_id = PCI_DEVICE_ID_DEC_21154;
+ k->revision = 0x02;
+ k->class_id = PCI_CLASS_BRIDGE_PCI;
+ k->is_bridge = true;
+ /*
+ * PCI-facing part of the host bridge, not usable without the
+ * host-facing part, which can't be device_add'ed, yet.
+ */
+ dc->user_creatable = false;
+}
+
+static const TypeInfo dec_21154_pci_host_info = {
+ .name = "dec-21154",
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(PCIDevice),
+ .class_init = dec_21154_pci_host_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { INTERFACE_CONVENTIONAL_PCI_DEVICE },
+ { },
+ },
+};
+
+static void pci_dec_21154_device_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = pci_dec_21154_device_realize;
+}
+
+static const TypeInfo pci_dec_21154_device_info = {
+ .name = TYPE_DEC_21154,
+ .parent = TYPE_PCI_HOST_BRIDGE,
+ .instance_size = sizeof(DECState),
+ .class_init = pci_dec_21154_device_class_init,
+};
+
+static void dec_register_types(void)
+{
+ type_register_static(&pci_dec_21154_device_info);
+ type_register_static(&dec_21154_pci_host_info);
+ type_register_static(&dec_21154_pci_bridge_info);
+}
+
+type_init(dec_register_types)
diff --git a/hw/pci-bridge/dec.h b/hw/pci-bridge/dec.h
new file mode 100644
index 000000000..869e90b13
--- /dev/null
+++ b/hw/pci-bridge/dec.h
@@ -0,0 +1,9 @@
+#ifndef HW_PCI_BRIDGE_DEC_H
+#define HW_PCI_BRIDGE_DEC_H
+
+
+#define TYPE_DEC_21154 "dec-21154-sysbus"
+
+PCIBus *pci_dec_21154_init(PCIBus *parent_bus, int devfn);
+
+#endif
diff --git a/hw/pci-bridge/gen_pcie_root_port.c b/hw/pci-bridge/gen_pcie_root_port.c
new file mode 100644
index 000000000..20099a8ae
--- /dev/null
+++ b/hw/pci-bridge/gen_pcie_root_port.c
@@ -0,0 +1,178 @@
+/*
+ * Generic PCI Express Root Port emulation
+ *
+ * Copyright (C) 2017 Red Hat Inc
+ *
+ * Authors:
+ * Marcel Apfelbaum <marcel@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/module.h"
+#include "hw/pci/msix.h"
+#include "hw/pci/pcie_port.h"
+#include "hw/qdev-properties.h"
+#include "hw/qdev-properties-system.h"
+#include "migration/vmstate.h"
+#include "qom/object.h"
+
+#define TYPE_GEN_PCIE_ROOT_PORT "pcie-root-port"
+OBJECT_DECLARE_SIMPLE_TYPE(GenPCIERootPort, GEN_PCIE_ROOT_PORT)
+
+#define GEN_PCIE_ROOT_PORT_AER_OFFSET 0x100
+#define GEN_PCIE_ROOT_PORT_ACS_OFFSET \
+ (GEN_PCIE_ROOT_PORT_AER_OFFSET + PCI_ERR_SIZEOF)
+
+#define GEN_PCIE_ROOT_PORT_MSIX_NR_VECTOR 1
+#define GEN_PCIE_ROOT_DEFAULT_IO_RANGE 4096
+
+struct GenPCIERootPort {
+ /*< private >*/
+ PCIESlot parent_obj;
+ /*< public >*/
+
+ bool migrate_msix;
+
+ /* additional resources to reserve */
+ PCIResReserve res_reserve;
+};
+
+static uint8_t gen_rp_aer_vector(const PCIDevice *d)
+{
+ return 0;
+}
+
+static int gen_rp_interrupts_init(PCIDevice *d, Error **errp)
+{
+ int rc;
+
+ rc = msix_init_exclusive_bar(d, GEN_PCIE_ROOT_PORT_MSIX_NR_VECTOR, 0, errp);
+
+ if (rc < 0) {
+ assert(rc == -ENOTSUP);
+ } else {
+ msix_vector_use(d, 0);
+ }
+
+ return rc;
+}
+
+static void gen_rp_interrupts_uninit(PCIDevice *d)
+{
+ msix_uninit_exclusive_bar(d);
+}
+
+static bool gen_rp_test_migrate_msix(void *opaque, int version_id)
+{
+ GenPCIERootPort *rp = opaque;
+
+ return rp->migrate_msix;
+}
+
+static void gen_rp_realize(DeviceState *dev, Error **errp)
+{
+ PCIDevice *d = PCI_DEVICE(dev);
+ PCIESlot *s = PCIE_SLOT(d);
+ GenPCIERootPort *grp = GEN_PCIE_ROOT_PORT(d);
+ PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(d);
+ Error *local_err = NULL;
+
+ rpc->parent_realize(dev, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ if (grp->res_reserve.io == -1 && s->hotplug && !s->native_hotplug) {
+ grp->res_reserve.io = GEN_PCIE_ROOT_DEFAULT_IO_RANGE;
+ }
+ int rc = pci_bridge_qemu_reserve_cap_init(d, 0,
+ grp->res_reserve, errp);
+
+ if (rc < 0) {
+ rpc->parent_class.exit(d);
+ return;
+ }
+
+ if (!grp->res_reserve.io) {
+ pci_word_test_and_clear_mask(d->wmask + PCI_COMMAND,
+ PCI_COMMAND_IO);
+ d->wmask[PCI_IO_BASE] = 0;
+ d->wmask[PCI_IO_LIMIT] = 0;
+ }
+}
+
+static const VMStateDescription vmstate_rp_dev = {
+ .name = "pcie-root-port",
+ .priority = MIG_PRI_PCI_BUS,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .post_load = pcie_cap_slot_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCI_DEVICE(parent_obj.parent_obj.parent_obj, PCIESlot),
+ VMSTATE_STRUCT(parent_obj.parent_obj.parent_obj.exp.aer_log,
+ PCIESlot, 0, vmstate_pcie_aer_log, PCIEAERLog),
+ VMSTATE_MSIX_TEST(parent_obj.parent_obj.parent_obj.parent_obj,
+ GenPCIERootPort,
+ gen_rp_test_migrate_msix),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property gen_rp_props[] = {
+ DEFINE_PROP_BOOL("x-migrate-msix", GenPCIERootPort,
+ migrate_msix, true),
+ DEFINE_PROP_UINT32("bus-reserve", GenPCIERootPort,
+ res_reserve.bus, -1),
+ DEFINE_PROP_SIZE("io-reserve", GenPCIERootPort,
+ res_reserve.io, -1),
+ DEFINE_PROP_SIZE("mem-reserve", GenPCIERootPort,
+ res_reserve.mem_non_pref, -1),
+ DEFINE_PROP_SIZE("pref32-reserve", GenPCIERootPort,
+ res_reserve.mem_pref_32, -1),
+ DEFINE_PROP_SIZE("pref64-reserve", GenPCIERootPort,
+ res_reserve.mem_pref_64, -1),
+ DEFINE_PROP_PCIE_LINK_SPEED("x-speed", PCIESlot,
+ speed, PCIE_LINK_SPEED_16),
+ DEFINE_PROP_PCIE_LINK_WIDTH("x-width", PCIESlot,
+ width, PCIE_LINK_WIDTH_32),
+ DEFINE_PROP_END_OF_LIST()
+};
+
+static void gen_rp_dev_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+ PCIERootPortClass *rpc = PCIE_ROOT_PORT_CLASS(klass);
+
+ k->vendor_id = PCI_VENDOR_ID_REDHAT;
+ k->device_id = PCI_DEVICE_ID_REDHAT_PCIE_RP;
+ dc->desc = "PCI Express Root Port";
+ dc->vmsd = &vmstate_rp_dev;
+ device_class_set_props(dc, gen_rp_props);
+
+ device_class_set_parent_realize(dc, gen_rp_realize, &rpc->parent_realize);
+
+ rpc->aer_vector = gen_rp_aer_vector;
+ rpc->interrupts_init = gen_rp_interrupts_init;
+ rpc->interrupts_uninit = gen_rp_interrupts_uninit;
+ rpc->aer_offset = GEN_PCIE_ROOT_PORT_AER_OFFSET;
+ rpc->acs_offset = GEN_PCIE_ROOT_PORT_ACS_OFFSET;
+}
+
+static const TypeInfo gen_rp_dev_info = {
+ .name = TYPE_GEN_PCIE_ROOT_PORT,
+ .parent = TYPE_PCIE_ROOT_PORT,
+ .instance_size = sizeof(GenPCIERootPort),
+ .class_init = gen_rp_dev_class_init,
+};
+
+ static void gen_rp_register_types(void)
+ {
+ type_register_static(&gen_rp_dev_info);
+ }
+ type_init(gen_rp_register_types)
diff --git a/hw/pci-bridge/i82801b11.c b/hw/pci-bridge/i82801b11.c
new file mode 100644
index 000000000..f28181e21
--- /dev/null
+++ b/hw/pci-bridge/i82801b11.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+/*
+ * QEMU i82801b11 dmi-to-pci Bridge Emulation
+ *
+ * Copyright (c) 2009, 2010, 2011
+ * Isaku Yamahata <yamahata at valinux co jp>
+ * VA Linux Systems Japan K.K.
+ * Copyright (C) 2012 Jason Baron <jbaron@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#include "qemu/osdep.h"
+#include "hw/pci/pci.h"
+#include "migration/vmstate.h"
+#include "qemu/module.h"
+#include "hw/i386/ich9.h"
+
+/*****************************************************************************/
+/* ICH9 DMI-to-PCI bridge */
+#define I82801ba_SSVID_OFFSET 0x50
+#define I82801ba_SSVID_SVID 0
+#define I82801ba_SSVID_SSID 0
+
+typedef struct I82801b11Bridge {
+ /*< private >*/
+ PCIBridge parent_obj;
+ /*< public >*/
+} I82801b11Bridge;
+
+static void i82801b11_bridge_realize(PCIDevice *d, Error **errp)
+{
+ int rc;
+
+ pci_bridge_initfn(d, TYPE_PCI_BUS);
+
+ rc = pci_bridge_ssvid_init(d, I82801ba_SSVID_OFFSET,
+ I82801ba_SSVID_SVID, I82801ba_SSVID_SSID,
+ errp);
+ if (rc < 0) {
+ goto err_bridge;
+ }
+ pci_config_set_prog_interface(d->config, PCI_CLASS_BRIDGE_PCI_INF_SUB);
+ return;
+
+err_bridge:
+ pci_bridge_exitfn(d);
+}
+
+static const VMStateDescription i82801b11_bridge_dev_vmstate = {
+ .name = "i82801b11_bridge",
+ .priority = MIG_PRI_PCI_BUS,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCI_DEVICE(parent_obj, PCIBridge),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void i82801b11_bridge_class_init(ObjectClass *klass, void *data)
+{
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ k->is_bridge = true;
+ k->vendor_id = PCI_VENDOR_ID_INTEL;
+ k->device_id = PCI_DEVICE_ID_INTEL_82801BA_11;
+ k->revision = ICH9_D2P_A2_REVISION;
+ k->realize = i82801b11_bridge_realize;
+ k->config_write = pci_bridge_write_config;
+ dc->vmsd = &i82801b11_bridge_dev_vmstate;
+ dc->reset = pci_bridge_reset;
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
+}
+
+static const TypeInfo i82801b11_bridge_info = {
+ .name = "i82801b11-bridge",
+ .parent = TYPE_PCI_BRIDGE,
+ .instance_size = sizeof(I82801b11Bridge),
+ .class_init = i82801b11_bridge_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { INTERFACE_CONVENTIONAL_PCI_DEVICE },
+ { },
+ },
+};
+
+static void d2pbr_register(void)
+{
+ type_register_static(&i82801b11_bridge_info);
+}
+
+type_init(d2pbr_register);
diff --git a/hw/pci-bridge/ioh3420.c b/hw/pci-bridge/ioh3420.c
new file mode 100644
index 000000000..f1e16135a
--- /dev/null
+++ b/hw/pci-bridge/ioh3420.c
@@ -0,0 +1,130 @@
+/*
+ * ioh3420.c
+ * Intel X58 north bridge IOH
+ * PCI Express root port device id 3420
+ *
+ * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
+ * VA Linux Systems Japan K.K.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/pci/pci_ids.h"
+#include "hw/pci/msi.h"
+#include "hw/pci/pcie.h"
+#include "hw/pci/pcie_port.h"
+#include "migration/vmstate.h"
+#include "qemu/module.h"
+
+#define PCI_DEVICE_ID_IOH_EPORT 0x3420 /* D0:F0 express mode */
+#define PCI_DEVICE_ID_IOH_REV 0x2
+#define IOH_EP_SSVID_OFFSET 0x40
+#define IOH_EP_SSVID_SVID PCI_VENDOR_ID_INTEL
+#define IOH_EP_SSVID_SSID 0
+#define IOH_EP_MSI_OFFSET 0x60
+#define IOH_EP_MSI_SUPPORTED_FLAGS PCI_MSI_FLAGS_MASKBIT
+#define IOH_EP_MSI_NR_VECTOR 2
+#define IOH_EP_EXP_OFFSET 0x90
+#define IOH_EP_AER_OFFSET 0x100
+
+/*
+ * If two MSI vector are allocated, Advanced Error Interrupt Message Number
+ * is 1. otherwise 0.
+ * 17.12.5.10 RPERRSTS, 32:27 bit Advanced Error Interrupt Message Number.
+ */
+static uint8_t ioh3420_aer_vector(const PCIDevice *d)
+{
+ switch (msi_nr_vectors_allocated(d)) {
+ case 1:
+ return 0;
+ case 2:
+ return 1;
+ case 4:
+ case 8:
+ case 16:
+ case 32:
+ default:
+ break;
+ }
+ abort();
+ return 0;
+}
+
+static int ioh3420_interrupts_init(PCIDevice *d, Error **errp)
+{
+ int rc;
+
+ rc = msi_init(d, IOH_EP_MSI_OFFSET, IOH_EP_MSI_NR_VECTOR,
+ IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
+ IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT,
+ errp);
+ if (rc < 0) {
+ assert(rc == -ENOTSUP);
+ }
+
+ return rc;
+}
+
+static void ioh3420_interrupts_uninit(PCIDevice *d)
+{
+ msi_uninit(d);
+}
+
+static const VMStateDescription vmstate_ioh3420 = {
+ .name = "ioh-3240-express-root-port",
+ .priority = MIG_PRI_PCI_BUS,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .post_load = pcie_cap_slot_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCI_DEVICE(parent_obj.parent_obj.parent_obj, PCIESlot),
+ VMSTATE_STRUCT(parent_obj.parent_obj.parent_obj.exp.aer_log,
+ PCIESlot, 0, vmstate_pcie_aer_log, PCIEAERLog),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void ioh3420_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+ PCIERootPortClass *rpc = PCIE_ROOT_PORT_CLASS(klass);
+
+ k->vendor_id = PCI_VENDOR_ID_INTEL;
+ k->device_id = PCI_DEVICE_ID_IOH_EPORT;
+ k->revision = PCI_DEVICE_ID_IOH_REV;
+ dc->desc = "Intel IOH device id 3420 PCIE Root Port";
+ dc->vmsd = &vmstate_ioh3420;
+ rpc->aer_vector = ioh3420_aer_vector;
+ rpc->interrupts_init = ioh3420_interrupts_init;
+ rpc->interrupts_uninit = ioh3420_interrupts_uninit;
+ rpc->exp_offset = IOH_EP_EXP_OFFSET;
+ rpc->aer_offset = IOH_EP_AER_OFFSET;
+ rpc->ssvid_offset = IOH_EP_SSVID_OFFSET;
+ rpc->ssid = IOH_EP_SSVID_SSID;
+}
+
+static const TypeInfo ioh3420_info = {
+ .name = "ioh3420",
+ .parent = TYPE_PCIE_ROOT_PORT,
+ .class_init = ioh3420_class_init,
+};
+
+static void ioh3420_register_types(void)
+{
+ type_register_static(&ioh3420_info);
+}
+
+type_init(ioh3420_register_types)
diff --git a/hw/pci-bridge/meson.build b/hw/pci-bridge/meson.build
new file mode 100644
index 000000000..daab8acf2
--- /dev/null
+++ b/hw/pci-bridge/meson.build
@@ -0,0 +1,14 @@
+pci_ss = ss.source_set()
+pci_ss.add(files('pci_bridge_dev.c'))
+pci_ss.add(when: 'CONFIG_I82801B11', if_true: files('i82801b11.c'))
+pci_ss.add(when: 'CONFIG_IOH3420', if_true: files('ioh3420.c'))
+pci_ss.add(when: 'CONFIG_PCIE_PORT', if_true: files('pcie_root_port.c', 'gen_pcie_root_port.c', 'pcie_pci_bridge.c'))
+pci_ss.add(when: 'CONFIG_PXB', if_true: files('pci_expander_bridge.c'))
+pci_ss.add(when: 'CONFIG_XIO3130', if_true: files('xio3130_upstream.c', 'xio3130_downstream.c'))
+
+# NewWorld PowerMac
+pci_ss.add(when: 'CONFIG_DEC_PCI', if_true: files('dec.c'))
+# Sun4u
+pci_ss.add(when: 'CONFIG_SIMBA', if_true: files('simba.c'))
+
+softmmu_ss.add_all(when: 'CONFIG_PCI', if_true: pci_ss)
diff --git a/hw/pci-bridge/pci_bridge_dev.c b/hw/pci-bridge/pci_bridge_dev.c
new file mode 100644
index 000000000..657a06ddb
--- /dev/null
+++ b/hw/pci-bridge/pci_bridge_dev.c
@@ -0,0 +1,308 @@
+/*
+ * Standard PCI Bridge Device
+ *
+ * Copyright (c) 2011 Red Hat Inc. Author: Michael S. Tsirkin <mst@redhat.com>
+ *
+ * http://www.pcisig.com/specifications/conventional/pci_to_pci_bridge_architecture/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/module.h"
+#include "hw/pci/pci_bridge.h"
+#include "hw/pci/pci_ids.h"
+#include "hw/pci/msi.h"
+#include "hw/pci/shpc.h"
+#include "hw/pci/slotid_cap.h"
+#include "hw/qdev-properties.h"
+#include "exec/memory.h"
+#include "hw/pci/pci_bus.h"
+#include "hw/hotplug.h"
+#include "qom/object.h"
+
+#define TYPE_PCI_BRIDGE_DEV "pci-bridge"
+#define TYPE_PCI_BRIDGE_SEAT_DEV "pci-bridge-seat"
+OBJECT_DECLARE_SIMPLE_TYPE(PCIBridgeDev, PCI_BRIDGE_DEV)
+
+struct PCIBridgeDev {
+ /*< private >*/
+ PCIBridge parent_obj;
+ /*< public >*/
+
+ MemoryRegion bar;
+ uint8_t chassis_nr;
+#define PCI_BRIDGE_DEV_F_SHPC_REQ 0
+ uint32_t flags;
+
+ OnOffAuto msi;
+
+ /* additional resources to reserve */
+ PCIResReserve res_reserve;
+};
+
+static void pci_bridge_dev_realize(PCIDevice *dev, Error **errp)
+{
+ PCIBridge *br = PCI_BRIDGE(dev);
+ PCIBridgeDev *bridge_dev = PCI_BRIDGE_DEV(dev);
+ int err;
+ Error *local_err = NULL;
+
+ pci_bridge_initfn(dev, TYPE_PCI_BUS);
+
+ if (bridge_dev->flags & (1 << PCI_BRIDGE_DEV_F_SHPC_REQ)) {
+ dev->config[PCI_INTERRUPT_PIN] = 0x1;
+ memory_region_init(&bridge_dev->bar, OBJECT(dev), "shpc-bar",
+ shpc_bar_size(dev));
+ err = shpc_init(dev, &br->sec_bus, &bridge_dev->bar, 0, errp);
+ if (err) {
+ goto shpc_error;
+ }
+ } else {
+ /* MSI is not applicable without SHPC */
+ bridge_dev->msi = ON_OFF_AUTO_OFF;
+ }
+
+ err = slotid_cap_init(dev, 0, bridge_dev->chassis_nr, 0, errp);
+ if (err) {
+ goto slotid_error;
+ }
+
+ if (bridge_dev->msi != ON_OFF_AUTO_OFF) {
+ /* it means SHPC exists, because MSI is needed by SHPC */
+
+ err = msi_init(dev, 0, 1, true, true, &local_err);
+ /* Any error other than -ENOTSUP(board's MSI support is broken)
+ * is a programming error */
+ assert(!err || err == -ENOTSUP);
+ if (err && bridge_dev->msi == ON_OFF_AUTO_ON) {
+ /* Can't satisfy user's explicit msi=on request, fail */
+ error_append_hint(&local_err, "You have to use msi=auto (default) "
+ "or msi=off with this machine type.\n");
+ error_propagate(errp, local_err);
+ goto msi_error;
+ }
+ assert(!local_err || bridge_dev->msi == ON_OFF_AUTO_AUTO);
+ /* With msi=auto, we fall back to MSI off silently */
+ error_free(local_err);
+ }
+
+ err = pci_bridge_qemu_reserve_cap_init(dev, 0,
+ bridge_dev->res_reserve, errp);
+ if (err) {
+ goto cap_error;
+ }
+
+ if (shpc_present(dev)) {
+ /* TODO: spec recommends using 64 bit prefetcheable BAR.
+ * Check whether that works well. */
+ pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY |
+ PCI_BASE_ADDRESS_MEM_TYPE_64, &bridge_dev->bar);
+ }
+ return;
+
+cap_error:
+ msi_uninit(dev);
+msi_error:
+ slotid_cap_cleanup(dev);
+slotid_error:
+ if (shpc_present(dev)) {
+ shpc_cleanup(dev, &bridge_dev->bar);
+ }
+shpc_error:
+ pci_bridge_exitfn(dev);
+}
+
+static void pci_bridge_dev_exitfn(PCIDevice *dev)
+{
+ PCIBridgeDev *bridge_dev = PCI_BRIDGE_DEV(dev);
+
+ pci_del_capability(dev, PCI_CAP_ID_VNDR, sizeof(PCIBridgeQemuCap));
+ if (msi_present(dev)) {
+ msi_uninit(dev);
+ }
+ slotid_cap_cleanup(dev);
+ if (shpc_present(dev)) {
+ shpc_cleanup(dev, &bridge_dev->bar);
+ }
+ pci_bridge_exitfn(dev);
+}
+
+static void pci_bridge_dev_instance_finalize(Object *obj)
+{
+ /* this function is idempotent and handles (PCIDevice.shpc == NULL) */
+ shpc_free(PCI_DEVICE(obj));
+}
+
+static void pci_bridge_dev_write_config(PCIDevice *d,
+ uint32_t address, uint32_t val, int len)
+{
+ pci_bridge_write_config(d, address, val, len);
+ if (msi_present(d)) {
+ msi_write_config(d, address, val, len);
+ }
+ if (shpc_present(d)) {
+ shpc_cap_write_config(d, address, val, len);
+ }
+}
+
+static void qdev_pci_bridge_dev_reset(DeviceState *qdev)
+{
+ PCIDevice *dev = PCI_DEVICE(qdev);
+
+ pci_bridge_reset(qdev);
+ if (shpc_present(dev)) {
+ shpc_reset(dev);
+ }
+}
+
+static Property pci_bridge_dev_properties[] = {
+ /* Note: 0 is not a legal chassis number. */
+ DEFINE_PROP_UINT8(PCI_BRIDGE_DEV_PROP_CHASSIS_NR, PCIBridgeDev, chassis_nr,
+ 0),
+ DEFINE_PROP_ON_OFF_AUTO(PCI_BRIDGE_DEV_PROP_MSI, PCIBridgeDev, msi,
+ ON_OFF_AUTO_AUTO),
+ DEFINE_PROP_BIT(PCI_BRIDGE_DEV_PROP_SHPC, PCIBridgeDev, flags,
+ PCI_BRIDGE_DEV_F_SHPC_REQ, true),
+ DEFINE_PROP_UINT32("bus-reserve", PCIBridgeDev,
+ res_reserve.bus, -1),
+ DEFINE_PROP_SIZE("io-reserve", PCIBridgeDev,
+ res_reserve.io, -1),
+ DEFINE_PROP_SIZE("mem-reserve", PCIBridgeDev,
+ res_reserve.mem_non_pref, -1),
+ DEFINE_PROP_SIZE("pref32-reserve", PCIBridgeDev,
+ res_reserve.mem_pref_32, -1),
+ DEFINE_PROP_SIZE("pref64-reserve", PCIBridgeDev,
+ res_reserve.mem_pref_64, -1),
+
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static bool pci_device_shpc_present(void *opaque, int version_id)
+{
+ PCIDevice *dev = opaque;
+
+ return shpc_present(dev);
+}
+
+static const VMStateDescription pci_bridge_dev_vmstate = {
+ .name = "pci_bridge",
+ .priority = MIG_PRI_PCI_BUS,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCI_DEVICE(parent_obj, PCIBridge),
+ SHPC_VMSTATE(shpc, PCIDevice, pci_device_shpc_present),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+void pci_bridge_dev_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
+ Error **errp)
+{
+ PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev);
+
+ if (!shpc_present(pci_hotplug_dev)) {
+ error_setg(errp, "standard hotplug controller has been disabled for "
+ "this %s", object_get_typename(OBJECT(hotplug_dev)));
+ return;
+ }
+ shpc_device_plug_cb(hotplug_dev, dev, errp);
+}
+
+void pci_bridge_dev_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
+ Error **errp)
+{
+ PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev);
+
+ g_assert(shpc_present(pci_hotplug_dev));
+ shpc_device_unplug_cb(hotplug_dev, dev, errp);
+}
+
+void pci_bridge_dev_unplug_request_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev);
+
+ if (!shpc_present(pci_hotplug_dev)) {
+ error_setg(errp, "standard hotplug controller has been disabled for "
+ "this %s", object_get_typename(OBJECT(hotplug_dev)));
+ return;
+ }
+ shpc_device_unplug_request_cb(hotplug_dev, dev, errp);
+}
+
+static void pci_bridge_dev_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+ HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
+
+ k->realize = pci_bridge_dev_realize;
+ k->exit = pci_bridge_dev_exitfn;
+ k->config_write = pci_bridge_dev_write_config;
+ k->vendor_id = PCI_VENDOR_ID_REDHAT;
+ k->device_id = PCI_DEVICE_ID_REDHAT_BRIDGE;
+ k->class_id = PCI_CLASS_BRIDGE_PCI;
+ k->is_bridge = true;
+ dc->desc = "Standard PCI Bridge";
+ dc->reset = qdev_pci_bridge_dev_reset;
+ device_class_set_props(dc, pci_bridge_dev_properties);
+ dc->vmsd = &pci_bridge_dev_vmstate;
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
+ hc->plug = pci_bridge_dev_plug_cb;
+ hc->unplug = pci_bridge_dev_unplug_cb;
+ hc->unplug_request = pci_bridge_dev_unplug_request_cb;
+}
+
+static const TypeInfo pci_bridge_dev_info = {
+ .name = TYPE_PCI_BRIDGE_DEV,
+ .parent = TYPE_PCI_BRIDGE,
+ .instance_size = sizeof(PCIBridgeDev),
+ .class_init = pci_bridge_dev_class_init,
+ .instance_finalize = pci_bridge_dev_instance_finalize,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_HOTPLUG_HANDLER },
+ { INTERFACE_CONVENTIONAL_PCI_DEVICE },
+ { }
+ }
+};
+
+/*
+ * Multiseat bridge. Same as the standard pci bridge, only with a
+ * different pci id, so we can match it easily in the guest for
+ * automagic multiseat configuration. See docs/multiseat.txt for more.
+ */
+static void pci_bridge_dev_seat_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->device_id = PCI_DEVICE_ID_REDHAT_BRIDGE_SEAT;
+ dc->desc = "Standard PCI Bridge (multiseat)";
+}
+
+static const TypeInfo pci_bridge_dev_seat_info = {
+ .name = TYPE_PCI_BRIDGE_SEAT_DEV,
+ .parent = TYPE_PCI_BRIDGE_DEV,
+ .instance_size = sizeof(PCIBridgeDev),
+ .class_init = pci_bridge_dev_seat_class_init,
+};
+
+static void pci_bridge_dev_register(void)
+{
+ type_register_static(&pci_bridge_dev_info);
+ type_register_static(&pci_bridge_dev_seat_info);
+}
+
+type_init(pci_bridge_dev_register);
diff --git a/hw/pci-bridge/pci_expander_bridge.c b/hw/pci-bridge/pci_expander_bridge.c
new file mode 100644
index 000000000..10e6e7c2a
--- /dev/null
+++ b/hw/pci-bridge/pci_expander_bridge.c
@@ -0,0 +1,385 @@
+/*
+ * PCI Expander Bridge Device Emulation
+ *
+ * Copyright (C) 2015 Red Hat Inc
+ *
+ * Authors:
+ * Marcel Apfelbaum <marcel@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pci_bus.h"
+#include "hw/pci/pci_host.h"
+#include "hw/qdev-properties.h"
+#include "hw/pci/pci_bridge.h"
+#include "qemu/range.h"
+#include "qemu/error-report.h"
+#include "qemu/module.h"
+#include "sysemu/numa.h"
+#include "hw/boards.h"
+#include "qom/object.h"
+
+#define TYPE_PXB_BUS "pxb-bus"
+typedef struct PXBBus PXBBus;
+DECLARE_INSTANCE_CHECKER(PXBBus, PXB_BUS,
+ TYPE_PXB_BUS)
+
+#define TYPE_PXB_PCIE_BUS "pxb-pcie-bus"
+DECLARE_INSTANCE_CHECKER(PXBBus, PXB_PCIE_BUS,
+ TYPE_PXB_PCIE_BUS)
+
+struct PXBBus {
+ /*< private >*/
+ PCIBus parent_obj;
+ /*< public >*/
+
+ char bus_path[8];
+};
+
+#define TYPE_PXB_DEVICE "pxb"
+typedef struct PXBDev PXBDev;
+DECLARE_INSTANCE_CHECKER(PXBDev, PXB_DEV,
+ TYPE_PXB_DEVICE)
+
+#define TYPE_PXB_PCIE_DEVICE "pxb-pcie"
+DECLARE_INSTANCE_CHECKER(PXBDev, PXB_PCIE_DEV,
+ TYPE_PXB_PCIE_DEVICE)
+
+struct PXBDev {
+ /*< private >*/
+ PCIDevice parent_obj;
+ /*< public >*/
+
+ uint8_t bus_nr;
+ uint16_t numa_node;
+ bool bypass_iommu;
+};
+
+static PXBDev *convert_to_pxb(PCIDevice *dev)
+{
+ return pci_bus_is_express(pci_get_bus(dev))
+ ? PXB_PCIE_DEV(dev) : PXB_DEV(dev);
+}
+
+static GList *pxb_dev_list;
+
+#define TYPE_PXB_HOST "pxb-host"
+
+static int pxb_bus_num(PCIBus *bus)
+{
+ PXBDev *pxb = convert_to_pxb(bus->parent_dev);
+
+ return pxb->bus_nr;
+}
+
+static uint16_t pxb_bus_numa_node(PCIBus *bus)
+{
+ PXBDev *pxb = convert_to_pxb(bus->parent_dev);
+
+ return pxb->numa_node;
+}
+
+static void pxb_bus_class_init(ObjectClass *class, void *data)
+{
+ PCIBusClass *pbc = PCI_BUS_CLASS(class);
+
+ pbc->bus_num = pxb_bus_num;
+ pbc->numa_node = pxb_bus_numa_node;
+}
+
+static const TypeInfo pxb_bus_info = {
+ .name = TYPE_PXB_BUS,
+ .parent = TYPE_PCI_BUS,
+ .instance_size = sizeof(PXBBus),
+ .class_init = pxb_bus_class_init,
+};
+
+static const TypeInfo pxb_pcie_bus_info = {
+ .name = TYPE_PXB_PCIE_BUS,
+ .parent = TYPE_PCIE_BUS,
+ .instance_size = sizeof(PXBBus),
+ .class_init = pxb_bus_class_init,
+};
+
+static const char *pxb_host_root_bus_path(PCIHostState *host_bridge,
+ PCIBus *rootbus)
+{
+ PXBBus *bus = pci_bus_is_express(rootbus) ?
+ PXB_PCIE_BUS(rootbus) : PXB_BUS(rootbus);
+
+ snprintf(bus->bus_path, 8, "0000:%02x", pxb_bus_num(rootbus));
+ return bus->bus_path;
+}
+
+static char *pxb_host_ofw_unit_address(const SysBusDevice *dev)
+{
+ const PCIHostState *pxb_host;
+ const PCIBus *pxb_bus;
+ const PXBDev *pxb_dev;
+ int position;
+ const DeviceState *pxb_dev_base;
+ const PCIHostState *main_host;
+ const SysBusDevice *main_host_sbd;
+
+ pxb_host = PCI_HOST_BRIDGE(dev);
+ pxb_bus = pxb_host->bus;
+ pxb_dev = convert_to_pxb(pxb_bus->parent_dev);
+ position = g_list_index(pxb_dev_list, pxb_dev);
+ assert(position >= 0);
+
+ pxb_dev_base = DEVICE(pxb_dev);
+ main_host = PCI_HOST_BRIDGE(pxb_dev_base->parent_bus->parent);
+ main_host_sbd = SYS_BUS_DEVICE(main_host);
+
+ if (main_host_sbd->num_mmio > 0) {
+ return g_strdup_printf(TARGET_FMT_plx ",%x",
+ main_host_sbd->mmio[0].addr, position + 1);
+ }
+ if (main_host_sbd->num_pio > 0) {
+ return g_strdup_printf("i%04x,%x",
+ main_host_sbd->pio[0], position + 1);
+ }
+ return NULL;
+}
+
+static void pxb_host_class_init(ObjectClass *class, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(class);
+ SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(class);
+ PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(class);
+
+ dc->fw_name = "pci";
+ /* Reason: Internal part of the pxb/pxb-pcie device, not usable by itself */
+ dc->user_creatable = false;
+ sbc->explicit_ofw_unit_address = pxb_host_ofw_unit_address;
+ hc->root_bus_path = pxb_host_root_bus_path;
+}
+
+static const TypeInfo pxb_host_info = {
+ .name = TYPE_PXB_HOST,
+ .parent = TYPE_PCI_HOST_BRIDGE,
+ .class_init = pxb_host_class_init,
+};
+
+/*
+ * Registers the PXB bus as a child of pci host root bus.
+ */
+static void pxb_register_bus(PCIDevice *dev, PCIBus *pxb_bus, Error **errp)
+{
+ PCIBus *bus = pci_get_bus(dev);
+ int pxb_bus_num = pci_bus_num(pxb_bus);
+
+ if (bus->parent_dev) {
+ error_setg(errp, "PXB devices can be attached only to root bus");
+ return;
+ }
+
+ QLIST_FOREACH(bus, &bus->child, sibling) {
+ if (pci_bus_num(bus) == pxb_bus_num) {
+ error_setg(errp, "Bus %d is already in use", pxb_bus_num);
+ return;
+ }
+ }
+ QLIST_INSERT_HEAD(&pci_get_bus(dev)->child, pxb_bus, sibling);
+}
+
+static int pxb_map_irq_fn(PCIDevice *pci_dev, int pin)
+{
+ PCIDevice *pxb = pci_get_bus(pci_dev)->parent_dev;
+
+ /*
+ * The bios does not index the pxb slot number when
+ * it computes the IRQ because it resides on bus 0
+ * and not on the current bus.
+ * However QEMU routes the irq through bus 0 and adds
+ * the pxb slot to the IRQ computation of the PXB
+ * device.
+ *
+ * Synchronize between bios and QEMU by canceling
+ * pxb's effect.
+ */
+ return pin - PCI_SLOT(pxb->devfn);
+}
+
+static gint pxb_compare(gconstpointer a, gconstpointer b)
+{
+ const PXBDev *pxb_a = a, *pxb_b = b;
+
+ return pxb_a->bus_nr < pxb_b->bus_nr ? -1 :
+ pxb_a->bus_nr > pxb_b->bus_nr ? 1 :
+ 0;
+}
+
+static void pxb_dev_realize_common(PCIDevice *dev, bool pcie, Error **errp)
+{
+ PXBDev *pxb = convert_to_pxb(dev);
+ DeviceState *ds, *bds = NULL;
+ PCIBus *bus;
+ const char *dev_name = NULL;
+ Error *local_err = NULL;
+ MachineState *ms = MACHINE(qdev_get_machine());
+
+ if (ms->numa_state == NULL) {
+ error_setg(errp, "NUMA is not supported by this machine-type");
+ return;
+ }
+
+ if (pxb->numa_node != NUMA_NODE_UNASSIGNED &&
+ pxb->numa_node >= ms->numa_state->num_nodes) {
+ error_setg(errp, "Illegal numa node %d", pxb->numa_node);
+ return;
+ }
+
+ if (dev->qdev.id && *dev->qdev.id) {
+ dev_name = dev->qdev.id;
+ }
+
+ ds = qdev_new(TYPE_PXB_HOST);
+ if (pcie) {
+ bus = pci_root_bus_new(ds, dev_name, NULL, NULL, 0, TYPE_PXB_PCIE_BUS);
+ } else {
+ bus = pci_root_bus_new(ds, "pxb-internal", NULL, NULL, 0, TYPE_PXB_BUS);
+ bds = qdev_new("pci-bridge");
+ bds->id = g_strdup(dev_name);
+ qdev_prop_set_uint8(bds, PCI_BRIDGE_DEV_PROP_CHASSIS_NR, pxb->bus_nr);
+ qdev_prop_set_bit(bds, PCI_BRIDGE_DEV_PROP_SHPC, false);
+ }
+
+ bus->parent_dev = dev;
+ bus->address_space_mem = pci_get_bus(dev)->address_space_mem;
+ bus->address_space_io = pci_get_bus(dev)->address_space_io;
+ bus->map_irq = pxb_map_irq_fn;
+
+ PCI_HOST_BRIDGE(ds)->bus = bus;
+ PCI_HOST_BRIDGE(ds)->bypass_iommu = pxb->bypass_iommu;
+
+ pxb_register_bus(dev, bus, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto err_register_bus;
+ }
+
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(ds), &error_fatal);
+ if (bds) {
+ qdev_realize_and_unref(bds, &bus->qbus, &error_fatal);
+ }
+
+ pci_word_test_and_set_mask(dev->config + PCI_STATUS,
+ PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK);
+ pci_config_set_class(dev->config, PCI_CLASS_BRIDGE_HOST);
+
+ pxb_dev_list = g_list_insert_sorted(pxb_dev_list, pxb, pxb_compare);
+ return;
+
+err_register_bus:
+ object_unref(OBJECT(bds));
+ object_unparent(OBJECT(bus));
+ object_unref(OBJECT(ds));
+}
+
+static void pxb_dev_realize(PCIDevice *dev, Error **errp)
+{
+ if (pci_bus_is_express(pci_get_bus(dev))) {
+ error_setg(errp, "pxb devices cannot reside on a PCIe bus");
+ return;
+ }
+
+ pxb_dev_realize_common(dev, false, errp);
+}
+
+static void pxb_dev_exitfn(PCIDevice *pci_dev)
+{
+ PXBDev *pxb = convert_to_pxb(pci_dev);
+
+ pxb_dev_list = g_list_remove(pxb_dev_list, pxb);
+}
+
+static Property pxb_dev_properties[] = {
+ /* Note: 0 is not a legal PXB bus number. */
+ DEFINE_PROP_UINT8("bus_nr", PXBDev, bus_nr, 0),
+ DEFINE_PROP_UINT16("numa_node", PXBDev, numa_node, NUMA_NODE_UNASSIGNED),
+ DEFINE_PROP_BOOL("bypass_iommu", PXBDev, bypass_iommu, false),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void pxb_dev_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->realize = pxb_dev_realize;
+ k->exit = pxb_dev_exitfn;
+ k->vendor_id = PCI_VENDOR_ID_REDHAT;
+ k->device_id = PCI_DEVICE_ID_REDHAT_PXB;
+ k->class_id = PCI_CLASS_BRIDGE_HOST;
+
+ dc->desc = "PCI Expander Bridge";
+ device_class_set_props(dc, pxb_dev_properties);
+ dc->hotpluggable = false;
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
+}
+
+static const TypeInfo pxb_dev_info = {
+ .name = TYPE_PXB_DEVICE,
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(PXBDev),
+ .class_init = pxb_dev_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { INTERFACE_CONVENTIONAL_PCI_DEVICE },
+ { },
+ },
+};
+
+static void pxb_pcie_dev_realize(PCIDevice *dev, Error **errp)
+{
+ if (!pci_bus_is_express(pci_get_bus(dev))) {
+ error_setg(errp, "pxb-pcie devices cannot reside on a PCI bus");
+ return;
+ }
+
+ pxb_dev_realize_common(dev, true, errp);
+}
+
+static void pxb_pcie_dev_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->realize = pxb_pcie_dev_realize;
+ k->exit = pxb_dev_exitfn;
+ k->vendor_id = PCI_VENDOR_ID_REDHAT;
+ k->device_id = PCI_DEVICE_ID_REDHAT_PXB_PCIE;
+ k->class_id = PCI_CLASS_BRIDGE_HOST;
+
+ dc->desc = "PCI Express Expander Bridge";
+ device_class_set_props(dc, pxb_dev_properties);
+ dc->hotpluggable = false;
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
+}
+
+static const TypeInfo pxb_pcie_dev_info = {
+ .name = TYPE_PXB_PCIE_DEVICE,
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(PXBDev),
+ .class_init = pxb_pcie_dev_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { INTERFACE_CONVENTIONAL_PCI_DEVICE },
+ { },
+ },
+};
+
+static void pxb_register_types(void)
+{
+ type_register_static(&pxb_bus_info);
+ type_register_static(&pxb_pcie_bus_info);
+ type_register_static(&pxb_host_info);
+ type_register_static(&pxb_dev_info);
+ type_register_static(&pxb_pcie_dev_info);
+}
+
+type_init(pxb_register_types)
diff --git a/hw/pci-bridge/pcie_pci_bridge.c b/hw/pci-bridge/pcie_pci_bridge.c
new file mode 100644
index 000000000..1cd917a45
--- /dev/null
+++ b/hw/pci-bridge/pcie_pci_bridge.c
@@ -0,0 +1,180 @@
+/*
+ * QEMU Generic PCIE-PCI Bridge
+ *
+ * Copyright (c) 2017 Aleksandr Bezzubikov
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/module.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pci_bus.h"
+#include "hw/pci/pci_bridge.h"
+#include "hw/pci/msi.h"
+#include "hw/pci/shpc.h"
+#include "hw/pci/slotid_cap.h"
+#include "hw/qdev-properties.h"
+#include "qom/object.h"
+
+struct PCIEPCIBridge {
+ /*< private >*/
+ PCIBridge parent_obj;
+
+ OnOffAuto msi;
+ MemoryRegion shpc_bar;
+ /*< public >*/
+};
+
+#define TYPE_PCIE_PCI_BRIDGE_DEV "pcie-pci-bridge"
+OBJECT_DECLARE_SIMPLE_TYPE(PCIEPCIBridge, PCIE_PCI_BRIDGE_DEV)
+
+static void pcie_pci_bridge_realize(PCIDevice *d, Error **errp)
+{
+ PCIBridge *br = PCI_BRIDGE(d);
+ PCIEPCIBridge *pcie_br = PCIE_PCI_BRIDGE_DEV(d);
+ int rc, pos;
+
+ pci_bridge_initfn(d, TYPE_PCI_BUS);
+
+ d->config[PCI_INTERRUPT_PIN] = 0x1;
+ memory_region_init(&pcie_br->shpc_bar, OBJECT(d), "shpc-bar",
+ shpc_bar_size(d));
+ rc = shpc_init(d, &br->sec_bus, &pcie_br->shpc_bar, 0, errp);
+ if (rc) {
+ goto error;
+ }
+
+ rc = pcie_cap_init(d, 0, PCI_EXP_TYPE_PCI_BRIDGE, 0, errp);
+ if (rc < 0) {
+ goto cap_error;
+ }
+
+ pos = pci_add_capability(d, PCI_CAP_ID_PM, 0, PCI_PM_SIZEOF, errp);
+ if (pos < 0) {
+ goto pm_error;
+ }
+ d->exp.pm_cap = pos;
+ pci_set_word(d->config + pos + PCI_PM_PMC, 0x3);
+
+ pcie_cap_arifwd_init(d);
+ pcie_cap_deverr_init(d);
+
+ rc = pcie_aer_init(d, PCI_ERR_VER, 0x100, PCI_ERR_SIZEOF, errp);
+ if (rc < 0) {
+ goto aer_error;
+ }
+
+ Error *local_err = NULL;
+ if (pcie_br->msi != ON_OFF_AUTO_OFF) {
+ rc = msi_init(d, 0, 1, true, true, &local_err);
+ if (rc < 0) {
+ assert(rc == -ENOTSUP);
+ if (pcie_br->msi != ON_OFF_AUTO_ON) {
+ error_free(local_err);
+ } else {
+ /* failed to satisfy user's explicit request for MSI */
+ error_propagate(errp, local_err);
+ goto msi_error;
+ }
+ }
+ }
+ pci_register_bar(d, 0, PCI_BASE_ADDRESS_SPACE_MEMORY |
+ PCI_BASE_ADDRESS_MEM_TYPE_64, &pcie_br->shpc_bar);
+ return;
+
+msi_error:
+ pcie_aer_exit(d);
+aer_error:
+pm_error:
+ pcie_cap_exit(d);
+cap_error:
+ shpc_cleanup(d, &pcie_br->shpc_bar);
+error:
+ pci_bridge_exitfn(d);
+}
+
+static void pcie_pci_bridge_exit(PCIDevice *d)
+{
+ PCIEPCIBridge *bridge_dev = PCIE_PCI_BRIDGE_DEV(d);
+ pcie_cap_exit(d);
+ shpc_cleanup(d, &bridge_dev->shpc_bar);
+ pci_bridge_exitfn(d);
+}
+
+static void pcie_pci_bridge_reset(DeviceState *qdev)
+{
+ PCIDevice *d = PCI_DEVICE(qdev);
+ pci_bridge_reset(qdev);
+ if (msi_present(d)) {
+ msi_reset(d);
+ }
+ shpc_reset(d);
+}
+
+static void pcie_pci_bridge_write_config(PCIDevice *d,
+ uint32_t address, uint32_t val, int len)
+{
+ pci_bridge_write_config(d, address, val, len);
+ if (msi_present(d)) {
+ msi_write_config(d, address, val, len);
+ }
+ shpc_cap_write_config(d, address, val, len);
+}
+
+static Property pcie_pci_bridge_dev_properties[] = {
+ DEFINE_PROP_ON_OFF_AUTO("msi", PCIEPCIBridge, msi, ON_OFF_AUTO_AUTO),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static const VMStateDescription pcie_pci_bridge_dev_vmstate = {
+ .name = TYPE_PCIE_PCI_BRIDGE_DEV,
+ .priority = MIG_PRI_PCI_BUS,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCI_DEVICE(parent_obj, PCIBridge),
+ SHPC_VMSTATE(shpc, PCIDevice, NULL),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void pcie_pci_bridge_class_init(ObjectClass *klass, void *data)
+{
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
+
+ k->is_bridge = true;
+ k->vendor_id = PCI_VENDOR_ID_REDHAT;
+ k->device_id = PCI_DEVICE_ID_REDHAT_PCIE_BRIDGE;
+ k->realize = pcie_pci_bridge_realize;
+ k->exit = pcie_pci_bridge_exit;
+ k->config_write = pcie_pci_bridge_write_config;
+ dc->vmsd = &pcie_pci_bridge_dev_vmstate;
+ device_class_set_props(dc, pcie_pci_bridge_dev_properties);
+ dc->reset = &pcie_pci_bridge_reset;
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
+ hc->plug = pci_bridge_dev_plug_cb;
+ hc->unplug = pci_bridge_dev_unplug_cb;
+ hc->unplug_request = pci_bridge_dev_unplug_request_cb;
+}
+
+static const TypeInfo pcie_pci_bridge_info = {
+ .name = TYPE_PCIE_PCI_BRIDGE_DEV,
+ .parent = TYPE_PCI_BRIDGE,
+ .instance_size = sizeof(PCIEPCIBridge),
+ .class_init = pcie_pci_bridge_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_HOTPLUG_HANDLER },
+ { INTERFACE_PCIE_DEVICE },
+ { },
+ }
+};
+
+static void pciepci_register(void)
+{
+ type_register_static(&pcie_pci_bridge_info);
+}
+
+type_init(pciepci_register);
diff --git a/hw/pci-bridge/pcie_root_port.c b/hw/pci-bridge/pcie_root_port.c
new file mode 100644
index 000000000..f1cfe9d14
--- /dev/null
+++ b/hw/pci-bridge/pcie_root_port.c
@@ -0,0 +1,198 @@
+/*
+ * Base class for PCI Express Root Ports
+ *
+ * Copyright (C) 2017 Red Hat Inc
+ *
+ * Authors:
+ * Marcel Apfelbaum <marcel@redhat.com>
+ *
+ * Most of the code was migrated from hw/pci-bridge/ioh3420.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/module.h"
+#include "hw/pci/pcie_port.h"
+#include "hw/qdev-properties.h"
+
+static void rp_aer_vector_update(PCIDevice *d)
+{
+ PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(d);
+
+ if (rpc->aer_vector) {
+ pcie_aer_root_set_vector(d, rpc->aer_vector(d));
+ }
+}
+
+static void rp_write_config(PCIDevice *d, uint32_t address,
+ uint32_t val, int len)
+{
+ uint32_t root_cmd =
+ pci_get_long(d->config + d->exp.aer_cap + PCI_ERR_ROOT_COMMAND);
+ uint16_t slt_ctl, slt_sta;
+
+ pcie_cap_slot_get(d, &slt_ctl, &slt_sta);
+
+ pci_bridge_write_config(d, address, val, len);
+ rp_aer_vector_update(d);
+ pcie_cap_slot_write_config(d, slt_ctl, slt_sta, address, val, len);
+ pcie_aer_write_config(d, address, val, len);
+ pcie_aer_root_write_config(d, address, val, len, root_cmd);
+}
+
+static void rp_reset(DeviceState *qdev)
+{
+ PCIDevice *d = PCI_DEVICE(qdev);
+
+ rp_aer_vector_update(d);
+ pcie_cap_root_reset(d);
+ pcie_cap_deverr_reset(d);
+ pcie_cap_slot_reset(d);
+ pcie_cap_arifwd_reset(d);
+ pcie_acs_reset(d);
+ pcie_aer_root_reset(d);
+ pci_bridge_reset(qdev);
+ pci_bridge_disable_base_limit(d);
+}
+
+static void rp_realize(PCIDevice *d, Error **errp)
+{
+ PCIEPort *p = PCIE_PORT(d);
+ PCIESlot *s = PCIE_SLOT(d);
+ PCIDeviceClass *dc = PCI_DEVICE_GET_CLASS(d);
+ PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(d);
+ int rc;
+
+ pci_config_set_interrupt_pin(d->config, 1);
+ pci_bridge_initfn(d, TYPE_PCIE_BUS);
+ pcie_port_init_reg(d);
+
+ rc = pci_bridge_ssvid_init(d, rpc->ssvid_offset, dc->vendor_id,
+ rpc->ssid, errp);
+ if (rc < 0) {
+ error_append_hint(errp, "Can't init SSV ID, error %d\n", rc);
+ goto err_bridge;
+ }
+
+ if (rpc->interrupts_init) {
+ rc = rpc->interrupts_init(d, errp);
+ if (rc < 0) {
+ goto err_bridge;
+ }
+ }
+
+ rc = pcie_cap_init(d, rpc->exp_offset, PCI_EXP_TYPE_ROOT_PORT,
+ p->port, errp);
+ if (rc < 0) {
+ error_append_hint(errp, "Can't add Root Port capability, "
+ "error %d\n", rc);
+ goto err_int;
+ }
+
+ pcie_cap_arifwd_init(d);
+ pcie_cap_deverr_init(d);
+ pcie_cap_slot_init(d, s);
+ pcie_cap_root_init(d);
+
+ pcie_chassis_create(s->chassis);
+ rc = pcie_chassis_add_slot(s);
+ if (rc < 0) {
+ error_setg(errp, "Can't add chassis slot, error %d", rc);
+ goto err_pcie_cap;
+ }
+
+ rc = pcie_aer_init(d, PCI_ERR_VER, rpc->aer_offset,
+ PCI_ERR_SIZEOF, errp);
+ if (rc < 0) {
+ goto err;
+ }
+ pcie_aer_root_init(d);
+ rp_aer_vector_update(d);
+
+ if (rpc->acs_offset && !s->disable_acs) {
+ pcie_acs_init(d, rpc->acs_offset);
+ }
+ return;
+
+err:
+ pcie_chassis_del_slot(s);
+err_pcie_cap:
+ pcie_cap_exit(d);
+err_int:
+ if (rpc->interrupts_uninit) {
+ rpc->interrupts_uninit(d);
+ }
+err_bridge:
+ pci_bridge_exitfn(d);
+}
+
+static void rp_exit(PCIDevice *d)
+{
+ PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(d);
+ PCIESlot *s = PCIE_SLOT(d);
+
+ pcie_aer_exit(d);
+ pcie_chassis_del_slot(s);
+ pcie_cap_exit(d);
+ if (rpc->interrupts_uninit) {
+ rpc->interrupts_uninit(d);
+ }
+ pci_bridge_exitfn(d);
+}
+
+static Property rp_props[] = {
+ DEFINE_PROP_BIT(COMPAT_PROP_PCP, PCIDevice, cap_present,
+ QEMU_PCIE_SLTCAP_PCP_BITNR, true),
+ DEFINE_PROP_BOOL("disable-acs", PCIESlot, disable_acs, false),
+ DEFINE_PROP_END_OF_LIST()
+};
+
+static void rp_instance_post_init(Object *obj)
+{
+ PCIESlot *s = PCIE_SLOT(obj);
+
+ if (!s->speed) {
+ s->speed = QEMU_PCI_EXP_LNK_2_5GT;
+ }
+
+ if (!s->width) {
+ s->width = QEMU_PCI_EXP_LNK_X1;
+ }
+}
+
+static void rp_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->is_bridge = true;
+ k->config_write = rp_write_config;
+ k->realize = rp_realize;
+ k->exit = rp_exit;
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
+ dc->reset = rp_reset;
+ device_class_set_props(dc, rp_props);
+}
+
+static const TypeInfo rp_info = {
+ .name = TYPE_PCIE_ROOT_PORT,
+ .parent = TYPE_PCIE_SLOT,
+ .instance_post_init = rp_instance_post_init,
+ .class_init = rp_class_init,
+ .abstract = true,
+ .class_size = sizeof(PCIERootPortClass),
+ .interfaces = (InterfaceInfo[]) {
+ { INTERFACE_PCIE_DEVICE },
+ { }
+ },
+};
+
+static void rp_register_types(void)
+{
+ type_register_static(&rp_info);
+}
+
+type_init(rp_register_types)
diff --git a/hw/pci-bridge/simba.c b/hw/pci-bridge/simba.c
new file mode 100644
index 000000000..ba55ab193
--- /dev/null
+++ b/hw/pci-bridge/simba.c
@@ -0,0 +1,102 @@
+/*
+ * QEMU Simba PCI bridge
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ * Copyright (c) 2012,2013 Artyom Tarasenko
+ * Copyright (c) 2018 Mark Cave-Ayland
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pci_bridge.h"
+#include "hw/pci/pci_bus.h"
+#include "qemu/module.h"
+#include "hw/pci-bridge/simba.h"
+
+/*
+ * Chipset docs:
+ * APB: "Advanced PCI Bridge (APB) User's Manual",
+ * http://www.sun.com/processors/manuals/805-1251.pdf
+ */
+
+static void simba_pci_bridge_realize(PCIDevice *dev, Error **errp)
+{
+ /*
+ * command register:
+ * According to PCI bridge spec, after reset
+ * bus master bit is off
+ * memory space enable bit is off
+ * According to manual (805-1251.pdf).
+ * the reset value should be zero unless the boot pin is tied high
+ * (which is true) and thus it should be PCI_COMMAND_MEMORY.
+ */
+ SimbaPCIBridge *br = SIMBA_PCI_BRIDGE(dev);
+
+ pci_bridge_initfn(dev, TYPE_PCI_BUS);
+
+ pci_set_word(dev->config + PCI_COMMAND, PCI_COMMAND_MEMORY);
+ pci_set_word(dev->config + PCI_STATUS,
+ PCI_STATUS_FAST_BACK | PCI_STATUS_66MHZ |
+ PCI_STATUS_DEVSEL_MEDIUM);
+
+ /* Allow 32-bit IO addresses */
+ pci_set_word(dev->config + PCI_IO_BASE, PCI_IO_RANGE_TYPE_32);
+ pci_set_word(dev->config + PCI_IO_LIMIT, PCI_IO_RANGE_TYPE_32);
+ pci_set_word(dev->wmask + PCI_IO_BASE_UPPER16, 0xffff);
+ pci_set_word(dev->wmask + PCI_IO_LIMIT_UPPER16, 0xffff);
+
+ pci_bridge_update_mappings(PCI_BRIDGE(br));
+}
+
+static void simba_pci_bridge_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->realize = simba_pci_bridge_realize;
+ k->exit = pci_bridge_exitfn;
+ k->vendor_id = PCI_VENDOR_ID_SUN;
+ k->device_id = PCI_DEVICE_ID_SUN_SIMBA;
+ k->revision = 0x11;
+ k->config_write = pci_bridge_write_config;
+ k->is_bridge = true;
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
+ dc->reset = pci_bridge_reset;
+ dc->vmsd = &vmstate_pci_device;
+}
+
+static const TypeInfo simba_pci_bridge_info = {
+ .name = TYPE_SIMBA_PCI_BRIDGE,
+ .parent = TYPE_PCI_BRIDGE,
+ .class_init = simba_pci_bridge_class_init,
+ .instance_size = sizeof(SimbaPCIBridge),
+ .interfaces = (InterfaceInfo[]) {
+ { INTERFACE_CONVENTIONAL_PCI_DEVICE },
+ { },
+ },
+};
+
+static void simba_register_types(void)
+{
+ type_register_static(&simba_pci_bridge_info);
+}
+
+type_init(simba_register_types)
diff --git a/hw/pci-bridge/xio3130_downstream.c b/hw/pci-bridge/xio3130_downstream.c
new file mode 100644
index 000000000..04aae72cd
--- /dev/null
+++ b/hw/pci-bridge/xio3130_downstream.c
@@ -0,0 +1,190 @@
+/*
+ * x3130_downstream.c
+ * TI X3130 pci express downstream port switch
+ *
+ * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
+ * VA Linux Systems Japan K.K.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/pci/pci_ids.h"
+#include "hw/pci/msi.h"
+#include "hw/pci/pcie.h"
+#include "hw/pci/pcie_port.h"
+#include "hw/qdev-properties.h"
+#include "migration/vmstate.h"
+#include "qapi/error.h"
+#include "qemu/module.h"
+
+#define PCI_DEVICE_ID_TI_XIO3130D 0x8233 /* downstream port */
+#define XIO3130_REVISION 0x1
+#define XIO3130_MSI_OFFSET 0x70
+#define XIO3130_MSI_SUPPORTED_FLAGS PCI_MSI_FLAGS_64BIT
+#define XIO3130_MSI_NR_VECTOR 1
+#define XIO3130_SSVID_OFFSET 0x80
+#define XIO3130_SSVID_SVID 0
+#define XIO3130_SSVID_SSID 0
+#define XIO3130_EXP_OFFSET 0x90
+#define XIO3130_AER_OFFSET 0x100
+
+static void xio3130_downstream_write_config(PCIDevice *d, uint32_t address,
+ uint32_t val, int len)
+{
+ uint16_t slt_ctl, slt_sta;
+
+ pcie_cap_slot_get(d, &slt_ctl, &slt_sta);
+ pci_bridge_write_config(d, address, val, len);
+ pcie_cap_flr_write_config(d, address, val, len);
+ pcie_cap_slot_write_config(d, slt_ctl, slt_sta, address, val, len);
+ pcie_aer_write_config(d, address, val, len);
+}
+
+static void xio3130_downstream_reset(DeviceState *qdev)
+{
+ PCIDevice *d = PCI_DEVICE(qdev);
+
+ pcie_cap_deverr_reset(d);
+ pcie_cap_slot_reset(d);
+ pcie_cap_arifwd_reset(d);
+ pci_bridge_reset(qdev);
+}
+
+static void xio3130_downstream_realize(PCIDevice *d, Error **errp)
+{
+ PCIEPort *p = PCIE_PORT(d);
+ PCIESlot *s = PCIE_SLOT(d);
+ int rc;
+
+ pci_bridge_initfn(d, TYPE_PCIE_BUS);
+ pcie_port_init_reg(d);
+
+ rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR,
+ XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
+ XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT,
+ errp);
+ if (rc < 0) {
+ assert(rc == -ENOTSUP);
+ goto err_bridge;
+ }
+
+ rc = pci_bridge_ssvid_init(d, XIO3130_SSVID_OFFSET,
+ XIO3130_SSVID_SVID, XIO3130_SSVID_SSID,
+ errp);
+ if (rc < 0) {
+ goto err_bridge;
+ }
+
+ rc = pcie_cap_init(d, XIO3130_EXP_OFFSET, PCI_EXP_TYPE_DOWNSTREAM,
+ p->port, errp);
+ if (rc < 0) {
+ goto err_msi;
+ }
+ pcie_cap_flr_init(d);
+ pcie_cap_deverr_init(d);
+ pcie_cap_slot_init(d, s);
+ pcie_cap_arifwd_init(d);
+
+ pcie_chassis_create(s->chassis);
+ rc = pcie_chassis_add_slot(s);
+ if (rc < 0) {
+ error_setg(errp, "Can't add chassis slot, error %d", rc);
+ goto err_pcie_cap;
+ }
+
+ rc = pcie_aer_init(d, PCI_ERR_VER, XIO3130_AER_OFFSET,
+ PCI_ERR_SIZEOF, errp);
+ if (rc < 0) {
+ goto err;
+ }
+
+ return;
+
+err:
+ pcie_chassis_del_slot(s);
+err_pcie_cap:
+ pcie_cap_exit(d);
+err_msi:
+ msi_uninit(d);
+err_bridge:
+ pci_bridge_exitfn(d);
+}
+
+static void xio3130_downstream_exitfn(PCIDevice *d)
+{
+ PCIESlot *s = PCIE_SLOT(d);
+
+ pcie_aer_exit(d);
+ pcie_chassis_del_slot(s);
+ pcie_cap_exit(d);
+ msi_uninit(d);
+ pci_bridge_exitfn(d);
+}
+
+static Property xio3130_downstream_props[] = {
+ DEFINE_PROP_BIT(COMPAT_PROP_PCP, PCIDevice, cap_present,
+ QEMU_PCIE_SLTCAP_PCP_BITNR, true),
+ DEFINE_PROP_END_OF_LIST()
+};
+
+static const VMStateDescription vmstate_xio3130_downstream = {
+ .name = "xio3130-express-downstream-port",
+ .priority = MIG_PRI_PCI_BUS,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .post_load = pcie_cap_slot_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCI_DEVICE(parent_obj.parent_obj.parent_obj, PCIESlot),
+ VMSTATE_STRUCT(parent_obj.parent_obj.parent_obj.exp.aer_log,
+ PCIESlot, 0, vmstate_pcie_aer_log, PCIEAERLog),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void xio3130_downstream_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->is_bridge = true;
+ k->config_write = xio3130_downstream_write_config;
+ k->realize = xio3130_downstream_realize;
+ k->exit = xio3130_downstream_exitfn;
+ k->vendor_id = PCI_VENDOR_ID_TI;
+ k->device_id = PCI_DEVICE_ID_TI_XIO3130D;
+ k->revision = XIO3130_REVISION;
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
+ dc->desc = "TI X3130 Downstream Port of PCI Express Switch";
+ dc->reset = xio3130_downstream_reset;
+ dc->vmsd = &vmstate_xio3130_downstream;
+ device_class_set_props(dc, xio3130_downstream_props);
+}
+
+static const TypeInfo xio3130_downstream_info = {
+ .name = "xio3130-downstream",
+ .parent = TYPE_PCIE_SLOT,
+ .class_init = xio3130_downstream_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { INTERFACE_PCIE_DEVICE },
+ { }
+ },
+};
+
+static void xio3130_downstream_register_types(void)
+{
+ type_register_static(&xio3130_downstream_info);
+}
+
+type_init(xio3130_downstream_register_types)
diff --git a/hw/pci-bridge/xio3130_upstream.c b/hw/pci-bridge/xio3130_upstream.c
new file mode 100644
index 000000000..5cd3af4fb
--- /dev/null
+++ b/hw/pci-bridge/xio3130_upstream.c
@@ -0,0 +1,159 @@
+/*
+ * xio3130_upstream.c
+ * TI X3130 pci express upstream port switch
+ *
+ * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
+ * VA Linux Systems Japan K.K.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/pci/pci_ids.h"
+#include "hw/pci/msi.h"
+#include "hw/pci/pcie.h"
+#include "hw/pci/pcie_port.h"
+#include "migration/vmstate.h"
+#include "qemu/module.h"
+
+#define PCI_DEVICE_ID_TI_XIO3130U 0x8232 /* upstream port */
+#define XIO3130_REVISION 0x2
+#define XIO3130_MSI_OFFSET 0x70
+#define XIO3130_MSI_SUPPORTED_FLAGS PCI_MSI_FLAGS_64BIT
+#define XIO3130_MSI_NR_VECTOR 1
+#define XIO3130_SSVID_OFFSET 0x80
+#define XIO3130_SSVID_SVID 0
+#define XIO3130_SSVID_SSID 0
+#define XIO3130_EXP_OFFSET 0x90
+#define XIO3130_AER_OFFSET 0x100
+
+static void xio3130_upstream_write_config(PCIDevice *d, uint32_t address,
+ uint32_t val, int len)
+{
+ pci_bridge_write_config(d, address, val, len);
+ pcie_cap_flr_write_config(d, address, val, len);
+ pcie_aer_write_config(d, address, val, len);
+}
+
+static void xio3130_upstream_reset(DeviceState *qdev)
+{
+ PCIDevice *d = PCI_DEVICE(qdev);
+
+ pci_bridge_reset(qdev);
+ pcie_cap_deverr_reset(d);
+}
+
+static void xio3130_upstream_realize(PCIDevice *d, Error **errp)
+{
+ PCIEPort *p = PCIE_PORT(d);
+ int rc;
+
+ pci_bridge_initfn(d, TYPE_PCIE_BUS);
+ pcie_port_init_reg(d);
+
+ rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR,
+ XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
+ XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT,
+ errp);
+ if (rc < 0) {
+ assert(rc == -ENOTSUP);
+ goto err_bridge;
+ }
+
+ rc = pci_bridge_ssvid_init(d, XIO3130_SSVID_OFFSET,
+ XIO3130_SSVID_SVID, XIO3130_SSVID_SSID,
+ errp);
+ if (rc < 0) {
+ goto err_bridge;
+ }
+
+ rc = pcie_cap_init(d, XIO3130_EXP_OFFSET, PCI_EXP_TYPE_UPSTREAM,
+ p->port, errp);
+ if (rc < 0) {
+ goto err_msi;
+ }
+ pcie_cap_flr_init(d);
+ pcie_cap_deverr_init(d);
+
+ rc = pcie_aer_init(d, PCI_ERR_VER, XIO3130_AER_OFFSET,
+ PCI_ERR_SIZEOF, errp);
+ if (rc < 0) {
+ goto err;
+ }
+
+ return;
+
+err:
+ pcie_cap_exit(d);
+err_msi:
+ msi_uninit(d);
+err_bridge:
+ pci_bridge_exitfn(d);
+}
+
+static void xio3130_upstream_exitfn(PCIDevice *d)
+{
+ pcie_aer_exit(d);
+ pcie_cap_exit(d);
+ msi_uninit(d);
+ pci_bridge_exitfn(d);
+}
+
+static const VMStateDescription vmstate_xio3130_upstream = {
+ .name = "xio3130-express-upstream-port",
+ .priority = MIG_PRI_PCI_BUS,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCI_DEVICE(parent_obj.parent_obj, PCIEPort),
+ VMSTATE_STRUCT(parent_obj.parent_obj.exp.aer_log, PCIEPort, 0,
+ vmstate_pcie_aer_log, PCIEAERLog),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void xio3130_upstream_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->is_bridge = true;
+ k->config_write = xio3130_upstream_write_config;
+ k->realize = xio3130_upstream_realize;
+ k->exit = xio3130_upstream_exitfn;
+ k->vendor_id = PCI_VENDOR_ID_TI;
+ k->device_id = PCI_DEVICE_ID_TI_XIO3130U;
+ k->revision = XIO3130_REVISION;
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
+ dc->desc = "TI X3130 Upstream Port of PCI Express Switch";
+ dc->reset = xio3130_upstream_reset;
+ dc->vmsd = &vmstate_xio3130_upstream;
+}
+
+static const TypeInfo xio3130_upstream_info = {
+ .name = "x3130-upstream",
+ .parent = TYPE_PCIE_PORT,
+ .class_init = xio3130_upstream_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { INTERFACE_PCIE_DEVICE },
+ { }
+ },
+};
+
+static void xio3130_upstream_register_types(void)
+{
+ type_register_static(&xio3130_upstream_info);
+}
+
+type_init(xio3130_upstream_register_types)