diff mbox

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

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

Commit Message

Alexander Graf Jan. 21, 2015, 4:18 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>
Reviewed-by: Claudio Fontana <claudio.fontana@huawei.com>
Tested-by: Claudio Fontana <claudio.fontana@huawei.com>

---

v1 -> v2:

  - Add documentation links
  - Remove mmio_window_size
  - Expose 4 INTX IRQs
---
 hw/pci-host/Makefile.objs  |   1 +
 hw/pci-host/gpex.c         | 153 +++++++++++++++++++++++++++++++++++++++++++++
 include/hw/pci-host/gpex.h |  54 ++++++++++++++++
 3 files changed, 208 insertions(+)
 create mode 100644 hw/pci-host/gpex.c
 create mode 100644 include/hw/pci-host/gpex.h

Comments

Caraman Mihai Claudiu-B02008 Jan. 22, 2015, 4:32 p.m. UTC | #1
Looks that you forgot the "single legacy IRQ line" comment from v1.

-Mike

On 01/21/2015 06:18 PM, 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>
> Reviewed-by: Claudio Fontana <claudio.fontana@huawei.com>
> Tested-by: Claudio Fontana <claudio.fontana@huawei.com>
>
> ---
>
> v1 -> v2:
>
>    - Add documentation links
>    - Remove mmio_window_size
>    - Expose 4 INTX IRQs
> ---
>   hw/pci-host/Makefile.objs  |   1 +
>   hw/pci-host/gpex.c         | 153 +++++++++++++++++++++++++++++++++++++++++++++
>   include/hw/pci-host/gpex.h |  54 ++++++++++++++++
>   3 files changed, 208 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..11aaf5b
> --- /dev/null
> +++ b/hw/pci-host/gpex.c
> @@ -0,0 +1,153 @@
> +/*
> + * 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.
> + *
> + * Check out these documents for more information on the device:
> + *
> + * http://www.kernel.org/doc/Documentation/devicetree/bindings/pci/host-generic-pci.txt
> + * http://www.firmware.org/1275/practice/imap/imap0_9d.pdf
> + */
> +#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[irq_num], level);
> +}
> +
> +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);
> +    int i;
> +
> +    pcie_host_mmcfg_init(pex, PCIE_MMCFG_SIZE_MIN);
> +    memory_region_init(&s->io_mmio, OBJECT(s), "gpex_mmio", UINT64_MAX);
> +    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);
> +    for (i = 0; i < 4; i++) {
> +        sysbus_init_irq(sbd, &s->irq[i]);
> +    }
> +
> +    pci->bus = pci_register_bus(dev, "pcie.0", gpex_set_irq,
> +                                pci_swizzle_map_irq_fn, s, &s->io_mmio,
> +                                &s->io_ioport, 0, 4, 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 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;
> +    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..b465667
> --- /dev/null
> +++ b/include/hw/pci-host/gpex.h
> @@ -0,0 +1,54 @@
> +/*
> + * 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[4];
> +} GPEXHost;
> +
> +#endif /* HW_GPEX_H */
>
Peter Maydell Jan. 27, 2015, 3:31 p.m. UTC | #2
On 21 January 2015 at 16:18, Alexander Graf <agraf@suse.de> wrote:
> With simple exposure of MMFG, ioport window, mmio window and an IRQ line we

Four :-)

> 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>
> Reviewed-by: Claudio Fontana <claudio.fontana@huawei.com>
> Tested-by: Claudio Fontana <claudio.fontana@huawei.com>

Checkpatch complains about a few over-80-chars lines;
the URL is an unavoidable one but could you fold the others,
please?

> +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);
> +    int i;
> +
> +    pcie_host_mmcfg_init(pex, PCIE_MMCFG_SIZE_MIN);

So this gives us 1MB of ECAM (config) space. That means
we can't specify a target bus, so we're restricted to
the 31 devices directly attached to the root complex.
Among other things, this will mean we can't do PCIe
hotplug. I think we should make this at least a bit bigger,
even if we don't go up to the full 256MB.

Probably the best thing for the gpex device itself is
to make the config space be the spec maximum (PCIE_MMCFG_SIZE_MAX)
and let the user map only a portion of that into their
address space if they want.

Ideally we should just do that in the base class, and
get the q35 subclass to do the right thing with aliases
to handle the dynamic resizing it wants. Then we wouldn't
need to track "size of cfg region" in the baseclass either.

> +    memory_region_init(&s->io_mmio, OBJECT(s), "gpex_mmio", UINT64_MAX);
> +    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);
> +    for (i = 0; i < 4; i++) {

Can we at least have a constant rather than hardcoded 4s ?
(qdev property for number-of-irqs if you're really feeling
enthusiastic...)

> +
> +/****************************************************************************
> + * GPEX Root D0:F0
> + */

What does "D0:F0" mean here?

> +
> +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";

We can be a bit less terse: "QEMU generic PCIe host bridge".

> +    dc->vmsd = &vmstate_gpex_root;
> +    k->vendor_id = PCI_VENDOR_ID_REDHAT;
> +    k->device_id = PCI_DEVICE_ID_REDHAT_BRIDGE;

Pretty sure we shouldn't be reusing the PCI bridge IDs
for a host bridge. We should allocate ourselves another
device ID in the range...

> +    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);

We seem to prefer no trailing ';' on type_init by a ratio
of more than 10:1.

> diff --git a/include/hw/pci-host/gpex.h b/include/hw/pci-host/gpex.h
> new file mode 100644
> index 0000000..b465667
> --- /dev/null
> +++ b/include/hw/pci-host/gpex.h
> @@ -0,0 +1,54 @@
> +/*
> + * gpex.h

Would be nice to say "QEMU Generic PCI Express Bridge Emulation" here
(like the .c file's header does).

thanks
-- PMM
Alexander Graf Jan. 29, 2015, 1:59 p.m. UTC | #3
On 27.01.15 16:31, Peter Maydell wrote:
> On 21 January 2015 at 16:18, Alexander Graf <agraf@suse.de> wrote:
>> With simple exposure of MMFG, ioport window, mmio window and an IRQ line we
> 
> Four :-)
> 
>> 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>
>> Reviewed-by: Claudio Fontana <claudio.fontana@huawei.com>
>> Tested-by: Claudio Fontana <claudio.fontana@huawei.com>
> 
> Checkpatch complains about a few over-80-chars lines;
> the URL is an unavoidable one but could you fold the others,
> please?

Sure

> 
>> +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);
>> +    int i;
>> +
>> +    pcie_host_mmcfg_init(pex, PCIE_MMCFG_SIZE_MIN);
> 
> So this gives us 1MB of ECAM (config) space. That means
> we can't specify a target bus, so we're restricted to
> the 31 devices directly attached to the root complex.
> Among other things, this will mean we can't do PCIe
> hotplug. I think we should make this at least a bit bigger,
> even if we don't go up to the full 256MB.
> 
> Probably the best thing for the gpex device itself is
> to make the config space be the spec maximum (PCIE_MMCFG_SIZE_MAX)
> and let the user map only a portion of that into their
> address space if they want.

Sounds like a good plan. Will do.

> 
> Ideally we should just do that in the base class, and
> get the q35 subclass to do the right thing with aliases
> to handle the dynamic resizing it wants. Then we wouldn't
> need to track "size of cfg region" in the baseclass either.
> 
>> +    memory_region_init(&s->io_mmio, OBJECT(s), "gpex_mmio", UINT64_MAX);
>> +    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);
>> +    for (i = 0; i < 4; i++) {
> 
> Can we at least have a constant rather than hardcoded 4s ?
> (qdev property for number-of-irqs if you're really feeling
> enthusiastic...)

Sure.

> 
>> +
>> +/****************************************************************************
>> + * GPEX Root D0:F0
>> + */
> 
> What does "D0:F0" mean here?

Device 0 Function 0. It's pretty common PCI speech ;).

> 
>> +
>> +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";
> 
> We can be a bit less terse: "QEMU generic PCIe host bridge".
> 
>> +    dc->vmsd = &vmstate_gpex_root;
>> +    k->vendor_id = PCI_VENDOR_ID_REDHAT;
>> +    k->device_id = PCI_DEVICE_ID_REDHAT_BRIDGE;
> 
> Pretty sure we shouldn't be reusing the PCI bridge IDs
> for a host bridge. We should allocate ourselves another
> device ID in the range...

Ah, right, I forgot about that. Unfortunately I can't reserve PCI IDs in
the Red Hat PCI range.

Michael, what's the process here?

> 
>> +    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);
> 
> We seem to prefer no trailing ';' on type_init by a ratio
> of more than 10:1.

Ok

> 
>> diff --git a/include/hw/pci-host/gpex.h b/include/hw/pci-host/gpex.h
>> new file mode 100644
>> index 0000000..b465667
>> --- /dev/null
>> +++ b/include/hw/pci-host/gpex.h
>> @@ -0,0 +1,54 @@
>> +/*
>> + * gpex.h
> 
> Would be nice to say "QEMU Generic PCI Express Bridge Emulation" here
> (like the .c file's header does).

Ok


Alex
Peter Maydell Jan. 29, 2015, 2:25 p.m. UTC | #4
On 29 January 2015 at 13:59, Alexander Graf <agraf@suse.de> wrote:
> On 27.01.15 16:31, Peter Maydell wrote:
>> On 21 January 2015 at 16:18, Alexander Graf <agraf@suse.de> wrote:
>>> +    dc->vmsd = &vmstate_gpex_root;
>>> +    k->vendor_id = PCI_VENDOR_ID_REDHAT;
>>> +    k->device_id = PCI_DEVICE_ID_REDHAT_BRIDGE;
>>
>> Pretty sure we shouldn't be reusing the PCI bridge IDs
>> for a host bridge. We should allocate ourselves another
>> device ID in the range...
>
> Ah, right, I forgot about that. Unfortunately I can't reserve PCI IDs in
> the Red Hat PCI range.
>
> Michael, what's the process here?

Last time this came up I think Paolo said the official registration
was getting a patch into QEMU master that added a line to the
list in pci.h :-) [ie QEMU is the master source for these IDs]

-- PMM
Paolo Bonzini Jan. 30, 2015, 10:25 a.m. UTC | #5
On 29/01/2015 15:25, Peter Maydell wrote:
> > Ah, right, I forgot about that. Unfortunately I can't reserve PCI IDs in
> > the Red Hat PCI range.
> >
> > Michael, what's the process here?
>
> Last time this came up I think Paolo said the official registration
> was getting a patch into QEMU master that added a line to the
> list in pci.h :-) [ie QEMU is the master source for these IDs]

Yeah, rocker was the first case of someone not a QEMU developer needing
an id.  So let's make QEMU the master source.

Paolo
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..11aaf5b
--- /dev/null
+++ b/hw/pci-host/gpex.c
@@ -0,0 +1,153 @@ 
+/*
+ * 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.
+ *
+ * Check out these documents for more information on the device:
+ *
+ * http://www.kernel.org/doc/Documentation/devicetree/bindings/pci/host-generic-pci.txt
+ * http://www.firmware.org/1275/practice/imap/imap0_9d.pdf
+ */
+#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[irq_num], level);
+}
+
+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);
+    int i;
+
+    pcie_host_mmcfg_init(pex, PCIE_MMCFG_SIZE_MIN);
+    memory_region_init(&s->io_mmio, OBJECT(s), "gpex_mmio", UINT64_MAX);
+    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);
+    for (i = 0; i < 4; i++) {
+        sysbus_init_irq(sbd, &s->irq[i]);
+    }
+
+    pci->bus = pci_register_bus(dev, "pcie.0", gpex_set_irq,
+                                pci_swizzle_map_irq_fn, s, &s->io_mmio,
+                                &s->io_ioport, 0, 4, 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 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;
+    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..b465667
--- /dev/null
+++ b/include/hw/pci-host/gpex.h
@@ -0,0 +1,54 @@ 
+/*
+ * 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[4];
+} GPEXHost;
+
+#endif /* HW_GPEX_H */