diff mbox

[2/4] pci: Add generic PCIe host bridge

Message ID 1420560191-48029-3-git-send-email-agraf@suse.de
State New
Headers show

Commit Message

Alexander Graf Jan. 6, 2015, 4:03 p.m. UTC
With simple exposure of MMFG, ioport window, mmio window and an IRQ line we
can successfully create a workable PCIe host bridge that can be mapped anywhere
and only needs to get described to the OS using whatever means it likes.

This patch implements such a "generic" host bridge. It only supports a single
legacy IRQ line so far. MSIs need to be handled external to the host bridge.

This device is particularly useful for the "pci-host-ecam-generic" driver in
Linux.

Signed-off-by: Alexander Graf <agraf@suse.de>
---
 hw/pci-host/Makefile.objs  |   1 +
 hw/pci-host/gpex.c         | 156 +++++++++++++++++++++++++++++++++++++++++++++
 include/hw/pci-host/gpex.h |  56 ++++++++++++++++
 3 files changed, 213 insertions(+)
 create mode 100644 hw/pci-host/gpex.c
 create mode 100644 include/hw/pci-host/gpex.h

Comments

Claudio Fontana Jan. 12, 2015, 4:29 p.m. UTC | #1
On 06.01.2015 17:03, Alexander Graf wrote:
> With simple exposure of MMFG, ioport window, mmio window and an IRQ line we
> can successfully create a workable PCIe host bridge that can be mapped anywhere
> and only needs to get described to the OS using whatever means it likes.
> 
> This patch implements such a "generic" host bridge. It only supports a single
> legacy IRQ line so far. MSIs need to be handled external to the host bridge.
> 
> This device is particularly useful for the "pci-host-ecam-generic" driver in
> Linux.
> 
> Signed-off-by: Alexander Graf <agraf@suse.de>
> ---
>  hw/pci-host/Makefile.objs  |   1 +
>  hw/pci-host/gpex.c         | 156 +++++++++++++++++++++++++++++++++++++++++++++
>  include/hw/pci-host/gpex.h |  56 ++++++++++++++++
>  3 files changed, 213 insertions(+)
>  create mode 100644 hw/pci-host/gpex.c
>  create mode 100644 include/hw/pci-host/gpex.h
> 
> diff --git a/hw/pci-host/Makefile.objs b/hw/pci-host/Makefile.objs
> index bb65f9c..45f1f0e 100644
> --- a/hw/pci-host/Makefile.objs
> +++ b/hw/pci-host/Makefile.objs
> @@ -15,3 +15,4 @@ common-obj-$(CONFIG_PCI_APB) += apb.o
>  common-obj-$(CONFIG_FULONG) += bonito.o
>  common-obj-$(CONFIG_PCI_PIIX) += piix.o
>  common-obj-$(CONFIG_PCI_Q35) += q35.o
> +common-obj-$(CONFIG_PCI_GENERIC) += gpex.o
> diff --git a/hw/pci-host/gpex.c b/hw/pci-host/gpex.c
> new file mode 100644
> index 0000000..bd62a3c
> --- /dev/null
> +++ b/hw/pci-host/gpex.c
> @@ -0,0 +1,156 @@
> +/*
> + * QEMU Generic PCI Express Bridge Emulation
> + *
> + * Copyright (C) 2015 Alexander Graf <agraf@suse.de>
> + *
> + * Code loosely based on q35.c.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +#include "hw/hw.h"
> +#include "hw/pci-host/gpex.h"
> +
> +/****************************************************************************
> + * GPEX host
> + */
> +
> +static void gpex_set_irq(void *opaque, int irq_num, int level)
> +{
> +    GPEXHost *s = opaque;
> +
> +    qemu_set_irq(s->irq, level);
> +}
> +
> +static int gpex_map_irq(PCIDevice *pci_dev, int irq_num)
> +{
> +    /* We only support one IRQ line so far */
> +    return 0;
> +}
> +
> +static void gpex_host_realize(DeviceState *dev, Error **errp)
> +{
> +    PCIHostState *pci = PCI_HOST_BRIDGE(dev);
> +    GPEXHost *s = GPEX_HOST(dev);
> +    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
> +    PCIExpressHost *pex = PCIE_HOST_BRIDGE(dev);
> +
> +    pcie_host_mmcfg_init(pex, PCIE_MMCFG_SIZE_MIN);
> +    memory_region_init(&s->io_mmio, OBJECT(s), "gpex_mmio", s->mmio_window_size);
> +    memory_region_init(&s->io_ioport, OBJECT(s), "gpex_ioport", 64 * 1024);
> +
> +    sysbus_init_mmio(sbd, &pex->mmio);
> +    sysbus_init_mmio(sbd, &s->io_mmio);
> +    sysbus_init_mmio(sbd, &s->io_ioport);
> +    sysbus_init_irq(sbd, &s->irq);
> +
> +    pci->bus = pci_register_bus(dev, "pcie.0", gpex_set_irq, gpex_map_irq, s,
> +                              &s->io_mmio, &s->io_ioport, 0, 1, TYPE_PCIE_BUS);
> +
> +    qdev_set_parent_bus(DEVICE(&s->gpex_root), BUS(pci->bus));
> +    qdev_init_nofail(DEVICE(&s->gpex_root));
> +}
> +
> +static const char *gpex_host_root_bus_path(PCIHostState *host_bridge,
> +                                          PCIBus *rootbus)
> +{
> +    return "0000:00";
> +}
> +
> +static Property gpex_root_props[] = {
> +    DEFINE_PROP_UINT64("mmio_window_size", GPEXHost, mmio_window_size, 1ULL << 32),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void gpex_host_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass);
> +
> +    hc->root_bus_path = gpex_host_root_bus_path;
> +    dc->realize = gpex_host_realize;
> +    dc->props = gpex_root_props;
> +    set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
> +    dc->fw_name = "pci";
> +}
> +
> +static void gpex_host_initfn(Object *obj)
> +{
> +    GPEXHost *s = GPEX_HOST(obj);
> +
> +    object_initialize(&s->gpex_root, sizeof(s->gpex_root), TYPE_GPEX_ROOT_DEVICE);
> +    object_property_add_child(OBJECT(s), "gpex_root", OBJECT(&s->gpex_root), NULL);
> +    qdev_prop_set_uint32(DEVICE(&s->gpex_root), "addr", PCI_DEVFN(0, 0));
> +    qdev_prop_set_bit(DEVICE(&s->gpex_root), "multifunction", false);
> +}
> +
> +static const TypeInfo gpex_host_info = {
> +    .name       = TYPE_GPEX_HOST,
> +    .parent     = TYPE_PCIE_HOST_BRIDGE,
> +    .instance_size = sizeof(GPEXHost),
> +    .instance_init = gpex_host_initfn,
> +    .class_init = gpex_host_class_init,
> +};
> +
> +/****************************************************************************
> + * GPEX Root D0:F0
> + */
> +
> +static const VMStateDescription vmstate_gpex_root = {
> +    .name = "gpex_root",
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_PCI_DEVICE(parent_obj, GPEXRootState),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static void gpex_root_class_init(ObjectClass *klass, void *data)
> +{
> +    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
> +    dc->desc = "Host bridge";
> +    dc->vmsd = &vmstate_gpex_root;
> +    k->vendor_id = PCI_VENDOR_ID_REDHAT;
> +    k->device_id = PCI_DEVICE_ID_REDHAT_BRIDGE;
> +    k->revision = 0;
> +    k->class_id = PCI_CLASS_BRIDGE_HOST;
> +    /*
> +     * PCI-facing part of the host bridge, not usable without the
> +     * host-facing part, which can't be device_add'ed, yet.
> +     */
> +    dc->cannot_instantiate_with_device_add_yet = true;
> +}
> +
> +static const TypeInfo gpex_root_info = {
> +    .name = TYPE_GPEX_ROOT_DEVICE,
> +    .parent = TYPE_PCI_DEVICE,
> +    .instance_size = sizeof(GPEXRootState),
> +    .class_init = gpex_root_class_init,
> +};
> +
> +static void gpex_register(void)
> +{
> +    type_register_static(&gpex_root_info);
> +    type_register_static(&gpex_host_info);
> +}
> +
> +type_init(gpex_register);
> diff --git a/include/hw/pci-host/gpex.h b/include/hw/pci-host/gpex.h
> new file mode 100644
> index 0000000..5cf2073
> --- /dev/null
> +++ b/include/hw/pci-host/gpex.h
> @@ -0,0 +1,56 @@
> +/*
> + * gpex.h
> + *
> + * Copyright (C) 2015 Alexander Graf <agraf@suse.de>
> + *
> + * 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_GPEX_H
> +#define HW_GPEX_H
> +
> +#include "hw/hw.h"
> +#include "hw/sysbus.h"
> +#include "hw/pci/pci.h"
> +#include "hw/pci/pcie_host.h"
> +
> +#define TYPE_GPEX_HOST "gpex-pcihost"
> +#define GPEX_HOST(obj) \
> +     OBJECT_CHECK(GPEXHost, (obj), TYPE_GPEX_HOST)
> +
> +#define TYPE_GPEX_ROOT_DEVICE "gpex-root"
> +#define MCH_PCI_DEVICE(obj) \
> +     OBJECT_CHECK(GPEXRootState, (obj), TYPE_GPEX_ROOT_DEVICE)
> +
> +typedef struct GPEXRootState {
> +    /*< private >*/
> +    PCIDevice parent_obj;
> +    /*< public >*/
> +} GPEXRootState;
> +
> +typedef struct GPEXHost {
> +    /*< private >*/
> +    PCIExpressHost parent_obj;
> +    /*< public >*/
> +
> +    GPEXRootState gpex_root;
> +
> +    MemoryRegion io_ioport;
> +    MemoryRegion io_mmio;
> +    qemu_irq irq;
> +
> +    uint64_t mmio_window_size;
> +} GPEXHost;
> +
> +#endif /* HW_GPEX_H */
> 

Reviewed-by: Claudio Fontana <claudio.fontana@huawei.com>
Alvise Rigo Jan. 12, 2015, 5:36 p.m. UTC | #2
Hi Alexander,

Just a comment below.

On Tue, Jan 6, 2015 at 5:03 PM, Alexander Graf <agraf@suse.de> wrote:
> With simple exposure of MMFG, ioport window, mmio window and an IRQ line we
> can successfully create a workable PCIe host bridge that can be mapped anywhere
> and only needs to get described to the OS using whatever means it likes.
>
> This patch implements such a "generic" host bridge. It only supports a single
> legacy IRQ line so far. MSIs need to be handled external to the host bridge.
>
> This device is particularly useful for the "pci-host-ecam-generic" driver in
> Linux.
>
> Signed-off-by: Alexander Graf <agraf@suse.de>
> ---
>  hw/pci-host/Makefile.objs  |   1 +
>  hw/pci-host/gpex.c         | 156 +++++++++++++++++++++++++++++++++++++++++++++
>  include/hw/pci-host/gpex.h |  56 ++++++++++++++++
>  3 files changed, 213 insertions(+)
>  create mode 100644 hw/pci-host/gpex.c
>  create mode 100644 include/hw/pci-host/gpex.h
>
> diff --git a/hw/pci-host/Makefile.objs b/hw/pci-host/Makefile.objs
> index bb65f9c..45f1f0e 100644
> --- a/hw/pci-host/Makefile.objs
> +++ b/hw/pci-host/Makefile.objs
> @@ -15,3 +15,4 @@ common-obj-$(CONFIG_PCI_APB) += apb.o
>  common-obj-$(CONFIG_FULONG) += bonito.o
>  common-obj-$(CONFIG_PCI_PIIX) += piix.o
>  common-obj-$(CONFIG_PCI_Q35) += q35.o
> +common-obj-$(CONFIG_PCI_GENERIC) += gpex.o
> diff --git a/hw/pci-host/gpex.c b/hw/pci-host/gpex.c
> new file mode 100644
> index 0000000..bd62a3c
> --- /dev/null
> +++ b/hw/pci-host/gpex.c
> @@ -0,0 +1,156 @@
> +/*
> + * QEMU Generic PCI Express Bridge Emulation
> + *
> + * Copyright (C) 2015 Alexander Graf <agraf@suse.de>
> + *
> + * Code loosely based on q35.c.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +#include "hw/hw.h"
> +#include "hw/pci-host/gpex.h"
> +
> +/****************************************************************************
> + * GPEX host
> + */
> +
> +static void gpex_set_irq(void *opaque, int irq_num, int level)
> +{
> +    GPEXHost *s = opaque;
> +
> +    qemu_set_irq(s->irq, level);
> +}
> +
> +static int gpex_map_irq(PCIDevice *pci_dev, int irq_num)
> +{
> +    /* We only support one IRQ line so far */
> +    return 0;
> +}

Regarding the request from Claudio to have one system interrupt for
each PCI device, we could address this by swizzling four (or more)
system interrupts for all the PCI devices. In this case a slightly
different interrupt-map and interrupt-map-mask is required as well as
a new map_irq callback (the legacy pci_swizzle_map_irq_fn is fine for
4 IRQs).
This of course would work as far as we have less PCI devices than the
number of swizzled IRQs.

Regards,
alvise

> +
> +static void gpex_host_realize(DeviceState *dev, Error **errp)
> +{
> +    PCIHostState *pci = PCI_HOST_BRIDGE(dev);
> +    GPEXHost *s = GPEX_HOST(dev);
> +    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
> +    PCIExpressHost *pex = PCIE_HOST_BRIDGE(dev);
> +
> +    pcie_host_mmcfg_init(pex, PCIE_MMCFG_SIZE_MIN);
> +    memory_region_init(&s->io_mmio, OBJECT(s), "gpex_mmio", s->mmio_window_size);
> +    memory_region_init(&s->io_ioport, OBJECT(s), "gpex_ioport", 64 * 1024);
> +
> +    sysbus_init_mmio(sbd, &pex->mmio);
> +    sysbus_init_mmio(sbd, &s->io_mmio);
> +    sysbus_init_mmio(sbd, &s->io_ioport);
> +    sysbus_init_irq(sbd, &s->irq);
> +
> +    pci->bus = pci_register_bus(dev, "pcie.0", gpex_set_irq, gpex_map_irq, s,
> +                              &s->io_mmio, &s->io_ioport, 0, 1, TYPE_PCIE_BUS);
> +
> +    qdev_set_parent_bus(DEVICE(&s->gpex_root), BUS(pci->bus));
> +    qdev_init_nofail(DEVICE(&s->gpex_root));
> +}
> +
> +static const char *gpex_host_root_bus_path(PCIHostState *host_bridge,
> +                                          PCIBus *rootbus)
> +{
> +    return "0000:00";
> +}
> +
> +static Property gpex_root_props[] = {
> +    DEFINE_PROP_UINT64("mmio_window_size", GPEXHost, mmio_window_size, 1ULL << 32),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void gpex_host_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass);
> +
> +    hc->root_bus_path = gpex_host_root_bus_path;
> +    dc->realize = gpex_host_realize;
> +    dc->props = gpex_root_props;
> +    set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
> +    dc->fw_name = "pci";
> +}
> +
> +static void gpex_host_initfn(Object *obj)
> +{
> +    GPEXHost *s = GPEX_HOST(obj);
> +
> +    object_initialize(&s->gpex_root, sizeof(s->gpex_root), TYPE_GPEX_ROOT_DEVICE);
> +    object_property_add_child(OBJECT(s), "gpex_root", OBJECT(&s->gpex_root), NULL);
> +    qdev_prop_set_uint32(DEVICE(&s->gpex_root), "addr", PCI_DEVFN(0, 0));
> +    qdev_prop_set_bit(DEVICE(&s->gpex_root), "multifunction", false);
> +}
> +
> +static const TypeInfo gpex_host_info = {
> +    .name       = TYPE_GPEX_HOST,
> +    .parent     = TYPE_PCIE_HOST_BRIDGE,
> +    .instance_size = sizeof(GPEXHost),
> +    .instance_init = gpex_host_initfn,
> +    .class_init = gpex_host_class_init,
> +};
> +
> +/****************************************************************************
> + * GPEX Root D0:F0
> + */
> +
> +static const VMStateDescription vmstate_gpex_root = {
> +    .name = "gpex_root",
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_PCI_DEVICE(parent_obj, GPEXRootState),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static void gpex_root_class_init(ObjectClass *klass, void *data)
> +{
> +    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
> +    dc->desc = "Host bridge";
> +    dc->vmsd = &vmstate_gpex_root;
> +    k->vendor_id = PCI_VENDOR_ID_REDHAT;
> +    k->device_id = PCI_DEVICE_ID_REDHAT_BRIDGE;
> +    k->revision = 0;
> +    k->class_id = PCI_CLASS_BRIDGE_HOST;
> +    /*
> +     * PCI-facing part of the host bridge, not usable without the
> +     * host-facing part, which can't be device_add'ed, yet.
> +     */
> +    dc->cannot_instantiate_with_device_add_yet = true;
> +}
> +
> +static const TypeInfo gpex_root_info = {
> +    .name = TYPE_GPEX_ROOT_DEVICE,
> +    .parent = TYPE_PCI_DEVICE,
> +    .instance_size = sizeof(GPEXRootState),
> +    .class_init = gpex_root_class_init,
> +};
> +
> +static void gpex_register(void)
> +{
> +    type_register_static(&gpex_root_info);
> +    type_register_static(&gpex_host_info);
> +}
> +
> +type_init(gpex_register);
> diff --git a/include/hw/pci-host/gpex.h b/include/hw/pci-host/gpex.h
> new file mode 100644
> index 0000000..5cf2073
> --- /dev/null
> +++ b/include/hw/pci-host/gpex.h
> @@ -0,0 +1,56 @@
> +/*
> + * gpex.h
> + *
> + * Copyright (C) 2015 Alexander Graf <agraf@suse.de>
> + *
> + * 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_GPEX_H
> +#define HW_GPEX_H
> +
> +#include "hw/hw.h"
> +#include "hw/sysbus.h"
> +#include "hw/pci/pci.h"
> +#include "hw/pci/pcie_host.h"
> +
> +#define TYPE_GPEX_HOST "gpex-pcihost"
> +#define GPEX_HOST(obj) \
> +     OBJECT_CHECK(GPEXHost, (obj), TYPE_GPEX_HOST)
> +
> +#define TYPE_GPEX_ROOT_DEVICE "gpex-root"
> +#define MCH_PCI_DEVICE(obj) \
> +     OBJECT_CHECK(GPEXRootState, (obj), TYPE_GPEX_ROOT_DEVICE)
> +
> +typedef struct GPEXRootState {
> +    /*< private >*/
> +    PCIDevice parent_obj;
> +    /*< public >*/
> +} GPEXRootState;
> +
> +typedef struct GPEXHost {
> +    /*< private >*/
> +    PCIExpressHost parent_obj;
> +    /*< public >*/
> +
> +    GPEXRootState gpex_root;
> +
> +    MemoryRegion io_ioport;
> +    MemoryRegion io_mmio;
> +    qemu_irq irq;
> +
> +    uint64_t mmio_window_size;
> +} GPEXHost;
> +
> +#endif /* HW_GPEX_H */
> --
> 1.7.12.4
>
Alexander Graf Jan. 12, 2015, 5:38 p.m. UTC | #3
On 12.01.15 18:36, alvise rigo wrote:
> Hi Alexander,
> 
> Just a comment below.
> 
> On Tue, Jan 6, 2015 at 5:03 PM, Alexander Graf <agraf@suse.de> wrote:
>> With simple exposure of MMFG, ioport window, mmio window and an IRQ line we
>> can successfully create a workable PCIe host bridge that can be mapped anywhere
>> and only needs to get described to the OS using whatever means it likes.
>>
>> This patch implements such a "generic" host bridge. It only supports a single
>> legacy IRQ line so far. MSIs need to be handled external to the host bridge.
>>
>> This device is particularly useful for the "pci-host-ecam-generic" driver in
>> Linux.
>>
>> Signed-off-by: Alexander Graf <agraf@suse.de>
>> ---
>>  hw/pci-host/Makefile.objs  |   1 +
>>  hw/pci-host/gpex.c         | 156 +++++++++++++++++++++++++++++++++++++++++++++
>>  include/hw/pci-host/gpex.h |  56 ++++++++++++++++
>>  3 files changed, 213 insertions(+)
>>  create mode 100644 hw/pci-host/gpex.c
>>  create mode 100644 include/hw/pci-host/gpex.h
>>
>> diff --git a/hw/pci-host/Makefile.objs b/hw/pci-host/Makefile.objs
>> index bb65f9c..45f1f0e 100644
>> --- a/hw/pci-host/Makefile.objs
>> +++ b/hw/pci-host/Makefile.objs
>> @@ -15,3 +15,4 @@ common-obj-$(CONFIG_PCI_APB) += apb.o
>>  common-obj-$(CONFIG_FULONG) += bonito.o
>>  common-obj-$(CONFIG_PCI_PIIX) += piix.o
>>  common-obj-$(CONFIG_PCI_Q35) += q35.o
>> +common-obj-$(CONFIG_PCI_GENERIC) += gpex.o
>> diff --git a/hw/pci-host/gpex.c b/hw/pci-host/gpex.c
>> new file mode 100644
>> index 0000000..bd62a3c
>> --- /dev/null
>> +++ b/hw/pci-host/gpex.c
>> @@ -0,0 +1,156 @@
>> +/*
>> + * QEMU Generic PCI Express Bridge Emulation
>> + *
>> + * Copyright (C) 2015 Alexander Graf <agraf@suse.de>
>> + *
>> + * Code loosely based on q35.c.
>> + *
>> + * Permission is hereby granted, free of charge, to any person obtaining a copy
>> + * of this software and associated documentation files (the "Software"), to deal
>> + * in the Software without restriction, including without limitation the rights
>> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
>> + * copies of the Software, and to permit persons to whom the Software is
>> + * furnished to do so, subject to the following conditions:
>> + *
>> + * The above copyright notice and this permission notice shall be included in
>> + * all copies or substantial portions of the Software.
>> + *
>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
>> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
>> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
>> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
>> + * THE SOFTWARE.
>> + */
>> +#include "hw/hw.h"
>> +#include "hw/pci-host/gpex.h"
>> +
>> +/****************************************************************************
>> + * GPEX host
>> + */
>> +
>> +static void gpex_set_irq(void *opaque, int irq_num, int level)
>> +{
>> +    GPEXHost *s = opaque;
>> +
>> +    qemu_set_irq(s->irq, level);
>> +}
>> +
>> +static int gpex_map_irq(PCIDevice *pci_dev, int irq_num)
>> +{
>> +    /* We only support one IRQ line so far */
>> +    return 0;
>> +}
> 
> Regarding the request from Claudio to have one system interrupt for
> each PCI device, we could address this by swizzling four (or more)
> system interrupts for all the PCI devices. In this case a slightly
> different interrupt-map and interrupt-map-mask is required as well as
> a new map_irq callback (the legacy pci_swizzle_map_irq_fn is fine for
> 4 IRQs).
> This of course would work as far as we have less PCI devices than the
> number of swizzled IRQs.

I'd prefer to keep things as easy as we humanly can for now. Then add
MSI. And if we then realize that we still need 4 rather than 1 shared
interrupt lines we can still change it :).


Alex
Peter Maydell Jan. 12, 2015, 8:08 p.m. UTC | #4
On 12 January 2015 at 17:38, Alexander Graf <agraf@suse.de> wrote:
> I'd prefer to keep things as easy as we humanly can for now. Then add
> MSI. And if we then realize that we still need 4 rather than 1 shared
> interrupt lines we can still change it :)

Except that that would be a breaking change, so I would prefer
to think ahead where possible; at some point there will come
a time when we really can't make breaking changes to this
board any more...

-- PMM
Alexander Graf Jan. 12, 2015, 9:06 p.m. UTC | #5
On 12.01.15 21:08, Peter Maydell wrote:
> On 12 January 2015 at 17:38, Alexander Graf <agraf@suse.de> wrote:
>> I'd prefer to keep things as easy as we humanly can for now. Then add
>> MSI. And if we then realize that we still need 4 rather than 1 shared
>> interrupt lines we can still change it :)
> 
> Except that that would be a breaking change, so I would prefer
> to think ahead where possible; at some point there will come
> a time when we really can't make breaking changes to this
> board any more...

Works for me, then we stay at a single interrupt line. The only reason
we have 4 in PCI is that back in the day you could have non-sharing PCI
devices that were essentially ISA ones.

If you actually care about interrupt latency and performance you will
want more than 4 IRQ lines, so you'll want MSI.


Alex
Peter Maydell Jan. 12, 2015, 9:20 p.m. UTC | #6
On 12 January 2015 at 21:06, Alexander Graf <agraf@suse.de> wrote:
>
>
> On 12.01.15 21:08, Peter Maydell wrote:
>> On 12 January 2015 at 17:38, Alexander Graf <agraf@suse.de> wrote:
>>> I'd prefer to keep things as easy as we humanly can for now. Then add
>>> MSI. And if we then realize that we still need 4 rather than 1 shared
>>> interrupt lines we can still change it :)
>>
>> Except that that would be a breaking change, so I would prefer
>> to think ahead where possible; at some point there will come
>> a time when we really can't make breaking changes to this
>> board any more...
>
> Works for me, then we stay at a single interrupt line. The only reason
> we have 4 in PCI is that back in the day you could have non-sharing PCI
> devices that were essentially ISA ones.

Well, also your typical small system probably doesn't have more
than 4 PCI slots and so 4 IRQs is enough to give them each one.
Most small VMs probably won't have more than four PCI devices
either...

-- PMM
Alexander Graf Jan. 13, 2015, 12:13 a.m. UTC | #7
On 12.01.15 22:20, Peter Maydell wrote:
> On 12 January 2015 at 21:06, Alexander Graf <agraf@suse.de> wrote:
>>
>>
>> On 12.01.15 21:08, Peter Maydell wrote:
>>> On 12 January 2015 at 17:38, Alexander Graf <agraf@suse.de> wrote:
>>>> I'd prefer to keep things as easy as we humanly can for now. Then add
>>>> MSI. And if we then realize that we still need 4 rather than 1 shared
>>>> interrupt lines we can still change it :)
>>>
>>> Except that that would be a breaking change, so I would prefer
>>> to think ahead where possible; at some point there will come
>>> a time when we really can't make breaking changes to this
>>> board any more...
>>
>> Works for me, then we stay at a single interrupt line. The only reason
>> we have 4 in PCI is that back in the day you could have non-sharing PCI
>> devices that were essentially ISA ones.
> 
> Well, also your typical small system probably doesn't have more
> than 4 PCI slots and so 4 IRQs is enough to give them each one.
> Most small VMs probably won't have more than four PCI devices
> either...

My main problem with multiple IRQs is that we'd have to describe the
mapping. I'd rather not have a fixed number of PCI slots hardcoded
anywhere, especially not in the map. So the only chance we have to keep
it dynamic would be to mask some field of the devfn to PCI IRQ lines.

How about we map the slots with a simple, pretty generic mask on the
lower bitsto 4 host IRQ lines? Would that make everyone happy?

I still don't think it's worth the hassle, but I'd be happy to do it if
people insist.


Alex
Claudio Fontana Jan. 13, 2015, 9:09 a.m. UTC | #8
On 12.01.2015 22:20, Peter Maydell wrote:
> On 12 January 2015 at 21:06, Alexander Graf <agraf@suse.de> wrote:
>>
>>
>> On 12.01.15 21:08, Peter Maydell wrote:
>>> On 12 January 2015 at 17:38, Alexander Graf <agraf@suse.de> wrote:
>>>> I'd prefer to keep things as easy as we humanly can for now. Then add
>>>> MSI. And if we then realize that we still need 4 rather than 1 shared
>>>> interrupt lines we can still change it :)
>>>
>>> Except that that would be a breaking change, so I would prefer
>>> to think ahead where possible; at some point there will come
>>> a time when we really can't make breaking changes to this
>>> board any more...
>>
>> Works for me, then we stay at a single interrupt line. The only reason
>> we have 4 in PCI is that back in the day you could have non-sharing PCI
>> devices that were essentially ISA ones.
> 
> Well, also your typical small system probably doesn't have more
> than 4 PCI slots and so 4 IRQs is enough to give them each one.
> Most small VMs probably won't have more than four PCI devices
> either...
> 
> -- PMM
> 

This solution with 4 slots, 4 irqs would be preferable to me..

Ciao,

Claudio
Peter Maydell Jan. 13, 2015, 10:07 a.m. UTC | #9
On 13 January 2015 at 00:13, Alexander Graf <agraf@suse.de> wrote:
> My main problem with multiple IRQs is that we'd have to describe the
> mapping.

This is true...

> I'd rather not have a fixed number of PCI slots hardcoded
> anywhere, especially not in the map.

...but this doesn't follow. What you do is the standard thing
of swizzling the IRQ lines across PCI slots, which is describable
with a device-tree mapping without having to hardcode the
number of slots. See my other email on the subject.

-- PMM
diff mbox

Patch

diff --git a/hw/pci-host/Makefile.objs b/hw/pci-host/Makefile.objs
index bb65f9c..45f1f0e 100644
--- a/hw/pci-host/Makefile.objs
+++ b/hw/pci-host/Makefile.objs
@@ -15,3 +15,4 @@  common-obj-$(CONFIG_PCI_APB) += apb.o
 common-obj-$(CONFIG_FULONG) += bonito.o
 common-obj-$(CONFIG_PCI_PIIX) += piix.o
 common-obj-$(CONFIG_PCI_Q35) += q35.o
+common-obj-$(CONFIG_PCI_GENERIC) += gpex.o
diff --git a/hw/pci-host/gpex.c b/hw/pci-host/gpex.c
new file mode 100644
index 0000000..bd62a3c
--- /dev/null
+++ b/hw/pci-host/gpex.c
@@ -0,0 +1,156 @@ 
+/*
+ * QEMU Generic PCI Express Bridge Emulation
+ *
+ * Copyright (C) 2015 Alexander Graf <agraf@suse.de>
+ *
+ * Code loosely based on q35.c.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "hw/pci-host/gpex.h"
+
+/****************************************************************************
+ * GPEX host
+ */
+
+static void gpex_set_irq(void *opaque, int irq_num, int level)
+{
+    GPEXHost *s = opaque;
+
+    qemu_set_irq(s->irq, level);
+}
+
+static int gpex_map_irq(PCIDevice *pci_dev, int irq_num)
+{
+    /* We only support one IRQ line so far */
+    return 0;
+}
+
+static void gpex_host_realize(DeviceState *dev, Error **errp)
+{
+    PCIHostState *pci = PCI_HOST_BRIDGE(dev);
+    GPEXHost *s = GPEX_HOST(dev);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+    PCIExpressHost *pex = PCIE_HOST_BRIDGE(dev);
+
+    pcie_host_mmcfg_init(pex, PCIE_MMCFG_SIZE_MIN);
+    memory_region_init(&s->io_mmio, OBJECT(s), "gpex_mmio", s->mmio_window_size);
+    memory_region_init(&s->io_ioport, OBJECT(s), "gpex_ioport", 64 * 1024);
+
+    sysbus_init_mmio(sbd, &pex->mmio);
+    sysbus_init_mmio(sbd, &s->io_mmio);
+    sysbus_init_mmio(sbd, &s->io_ioport);
+    sysbus_init_irq(sbd, &s->irq);
+
+    pci->bus = pci_register_bus(dev, "pcie.0", gpex_set_irq, gpex_map_irq, s,
+                              &s->io_mmio, &s->io_ioport, 0, 1, TYPE_PCIE_BUS);
+
+    qdev_set_parent_bus(DEVICE(&s->gpex_root), BUS(pci->bus));
+    qdev_init_nofail(DEVICE(&s->gpex_root));
+}
+
+static const char *gpex_host_root_bus_path(PCIHostState *host_bridge,
+                                          PCIBus *rootbus)
+{
+    return "0000:00";
+}
+
+static Property gpex_root_props[] = {
+    DEFINE_PROP_UINT64("mmio_window_size", GPEXHost, mmio_window_size, 1ULL << 32),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void gpex_host_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass);
+
+    hc->root_bus_path = gpex_host_root_bus_path;
+    dc->realize = gpex_host_realize;
+    dc->props = gpex_root_props;
+    set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
+    dc->fw_name = "pci";
+}
+
+static void gpex_host_initfn(Object *obj)
+{
+    GPEXHost *s = GPEX_HOST(obj);
+
+    object_initialize(&s->gpex_root, sizeof(s->gpex_root), TYPE_GPEX_ROOT_DEVICE);
+    object_property_add_child(OBJECT(s), "gpex_root", OBJECT(&s->gpex_root), NULL);
+    qdev_prop_set_uint32(DEVICE(&s->gpex_root), "addr", PCI_DEVFN(0, 0));
+    qdev_prop_set_bit(DEVICE(&s->gpex_root), "multifunction", false);
+}
+
+static const TypeInfo gpex_host_info = {
+    .name       = TYPE_GPEX_HOST,
+    .parent     = TYPE_PCIE_HOST_BRIDGE,
+    .instance_size = sizeof(GPEXHost),
+    .instance_init = gpex_host_initfn,
+    .class_init = gpex_host_class_init,
+};
+
+/****************************************************************************
+ * GPEX Root D0:F0
+ */
+
+static const VMStateDescription vmstate_gpex_root = {
+    .name = "gpex_root",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_PCI_DEVICE(parent_obj, GPEXRootState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void gpex_root_class_init(ObjectClass *klass, void *data)
+{
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
+    dc->desc = "Host bridge";
+    dc->vmsd = &vmstate_gpex_root;
+    k->vendor_id = PCI_VENDOR_ID_REDHAT;
+    k->device_id = PCI_DEVICE_ID_REDHAT_BRIDGE;
+    k->revision = 0;
+    k->class_id = PCI_CLASS_BRIDGE_HOST;
+    /*
+     * PCI-facing part of the host bridge, not usable without the
+     * host-facing part, which can't be device_add'ed, yet.
+     */
+    dc->cannot_instantiate_with_device_add_yet = true;
+}
+
+static const TypeInfo gpex_root_info = {
+    .name = TYPE_GPEX_ROOT_DEVICE,
+    .parent = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(GPEXRootState),
+    .class_init = gpex_root_class_init,
+};
+
+static void gpex_register(void)
+{
+    type_register_static(&gpex_root_info);
+    type_register_static(&gpex_host_info);
+}
+
+type_init(gpex_register);
diff --git a/include/hw/pci-host/gpex.h b/include/hw/pci-host/gpex.h
new file mode 100644
index 0000000..5cf2073
--- /dev/null
+++ b/include/hw/pci-host/gpex.h
@@ -0,0 +1,56 @@ 
+/*
+ * gpex.h
+ *
+ * Copyright (C) 2015 Alexander Graf <agraf@suse.de>
+ *
+ * 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_GPEX_H
+#define HW_GPEX_H
+
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pcie_host.h"
+
+#define TYPE_GPEX_HOST "gpex-pcihost"
+#define GPEX_HOST(obj) \
+     OBJECT_CHECK(GPEXHost, (obj), TYPE_GPEX_HOST)
+
+#define TYPE_GPEX_ROOT_DEVICE "gpex-root"
+#define MCH_PCI_DEVICE(obj) \
+     OBJECT_CHECK(GPEXRootState, (obj), TYPE_GPEX_ROOT_DEVICE)
+
+typedef struct GPEXRootState {
+    /*< private >*/
+    PCIDevice parent_obj;
+    /*< public >*/
+} GPEXRootState;
+
+typedef struct GPEXHost {
+    /*< private >*/
+    PCIExpressHost parent_obj;
+    /*< public >*/
+
+    GPEXRootState gpex_root;
+
+    MemoryRegion io_ioport;
+    MemoryRegion io_mmio;
+    qemu_irq irq;
+
+    uint64_t mmio_window_size;
+} GPEXHost;
+
+#endif /* HW_GPEX_H */