diff mbox

[v3,2/5] hw/pci: introduce pcie-pci-bridge device

Message ID 1501285073-2215-3-git-send-email-zuban32s@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Aleksandr Bezzubikov July 28, 2017, 11:37 p.m. UTC
Introduce a new PCIExpress-to-PCI Bridge device,
which is a hot-pluggable PCI Express device and
supports devices hot-plug with SHPC.

This device is intended to replace the DMI-to-PCI
Bridge in an overwhelming majority of use-cases.

Signed-off-by: Aleksandr Bezzubikov <zuban32s@gmail.com>
---
 hw/pci-bridge/Makefile.objs     |   2 +-
 hw/pci-bridge/pcie_pci_bridge.c | 220 ++++++++++++++++++++++++++++++++++++++++
 include/hw/pci/pci.h            |   1 +
 3 files changed, 222 insertions(+), 1 deletion(-)
 create mode 100644 hw/pci-bridge/pcie_pci_bridge.c

Comments

Marcel Apfelbaum July 31, 2017, 11:23 a.m. UTC | #1
On 29/07/2017 2:37, Aleksandr Bezzubikov wrote:
> Introduce a new PCIExpress-to-PCI Bridge device,
> which is a hot-pluggable PCI Express device and
> supports devices hot-plug with SHPC.
> 
> This device is intended to replace the DMI-to-PCI
> Bridge in an overwhelming majority of use-cases.
> 
> Signed-off-by: Aleksandr Bezzubikov <zuban32s@gmail.com>
> ---
>   hw/pci-bridge/Makefile.objs     |   2 +-
>   hw/pci-bridge/pcie_pci_bridge.c | 220 ++++++++++++++++++++++++++++++++++++++++
>   include/hw/pci/pci.h            |   1 +
>   3 files changed, 222 insertions(+), 1 deletion(-)
>   create mode 100644 hw/pci-bridge/pcie_pci_bridge.c
> 
> diff --git a/hw/pci-bridge/Makefile.objs b/hw/pci-bridge/Makefile.objs
> index c4683cf..666db37 100644
> --- a/hw/pci-bridge/Makefile.objs
> +++ b/hw/pci-bridge/Makefile.objs
> @@ -1,4 +1,4 @@
> -common-obj-y += pci_bridge_dev.o
> +common-obj-y += pci_bridge_dev.o pcie_pci_bridge.o
>   common-obj-$(CONFIG_PCIE_PORT) += pcie_root_port.o gen_pcie_root_port.o
>   common-obj-$(CONFIG_PXB) += pci_expander_bridge.o
>   common-obj-$(CONFIG_XIO3130) += xio3130_upstream.o xio3130_downstream.o
> diff --git a/hw/pci-bridge/pcie_pci_bridge.c b/hw/pci-bridge/pcie_pci_bridge.c
> new file mode 100644
> index 0000000..c28f820
> --- /dev/null
> +++ b/hw/pci-bridge/pcie_pci_bridge.c
> @@ -0,0 +1,220 @@
> +/*
> + * QEMU Generic PCIE-PCI Bridge
> + *
> + * Copyright (c) 2017 Aleksandr Bezzubikov
> + *
> + * 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 "qapi/error.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"
> +

Hi Aleksander,

> +typedef struct PCIEPCIBridge {
> +    /*< private >*/
> +    PCIBridge parent_obj;
> +
> +    bool msi_enable;

Please rename the msi_enable property to "msi" in order
to be aligned with the existent PCIBridgeDev and
consider making it OnOffAuto for the same reason.
(I am not sure about the last part though, we have
  no meaning for "auto" here)

> +    MemoryRegion bar;

I suggest renaming it to shpc_bar, it will make the code
more readable.

> +    /*< public >*/
> +} PCIEPCIBridge;
> +
> +#define TYPE_PCIE_PCI_BRIDGE_DEV      "pcie-pci-bridge"
> +#define PCIE_PCI_BRIDGE_DEV(obj) \
> +        OBJECT_CHECK(PCIEPCIBridge, (obj), TYPE_PCIE_PCI_BRIDGE_DEV)
> +
> +static void pciepci_bridge_realize(PCIDevice *d, Error **errp)
> +{
> +    PCIBridge *br = PCI_BRIDGE(d);
> +    PCIEPCIBridge *pcie_br = PCIE_PCI_BRIDGE_DEV(d);
> +    int rc, pos;
> +    Error *local_err = NULL;

Since you don't "create" errors in this function, you
could simply pass errp to the functions and not use
local_err, it will save some code lines.

> +
> +    pci_bridge_initfn(d, TYPE_PCI_BUS);
> +
> +    d->config[PCI_INTERRUPT_PIN] = 0x1;
> +    memory_region_init(&pcie_br->bar, OBJECT(d), "shpc-bar",
> +                       shpc_bar_size(d));
> +    rc = shpc_init(d, &br->sec_bus, &pcie_br->bar, 0, &local_err);
> +    if (rc) {
> +        error_propagate(errp, local_err);
> +        goto error;
> +    }
> +
> +    rc = pcie_cap_init(d, 0, PCI_EXP_TYPE_PCI_BRIDGE, 0, &local_err);
> +    if (rc < 0) {
> +        error_propagate(errp, local_err);
> +        goto cap_error;
> +    }
> +
> +    pos = pci_add_capability(d, PCI_CAP_ID_PM, 0, PCI_PM_SIZEOF, &local_err);
> +    if (pos < 0) {
> +        error_propagate(errp, local_err);
> +        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, &local_err);
> +    if (rc < 0) {
> +        error_propagate(errp, local_err);
> +        goto aer_error;
> +    }
> +
> +    if (pcie_br->msi_enable) {
> +        rc = msi_init(d, 0, 1, true, true, &local_err);
> +        if (rc < 0) {
> +            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->bar);
> +    return;
> +
> +msi_error:
> +    pcie_aer_exit(d);
> +aer_error:
> +pm_error:
> +    pcie_cap_exit(d);
> +cap_error:
> +    shpc_free(d);
> +error:
> +    pci_bridge_exitfn(d);
> +}
> +
> +static void pciepci_bridge_exit(PCIDevice *d)
> +{
> +    PCIEPCIBridge *bridge_dev = PCIE_PCI_BRIDGE_DEV(d);
> +    pcie_cap_exit(d);
> +    shpc_cleanup(d, &bridge_dev->bar);
> +    pci_bridge_exitfn(d);
> +}
> +
> +static void pciepci_bridge_reset(DeviceState *qdev)
> +{
> +    PCIDevice *d = PCI_DEVICE(qdev);
> +    pci_bridge_reset(qdev);
> +    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);
> +    msi_write_config(d, address, val, len);
> +    shpc_cap_write_config(d, address, val, len);
> +}
> +
> +static bool pci_device_shpc_present(void *opaque, int version_id)
> +{
> +    PCIDevice *dev = opaque;
> +
> +    return shpc_present(dev);
> +}
> +
> +static Property pcie_pci_bridge_dev_properties[] = {
> +        DEFINE_PROP_BOOL("msi_enable", PCIEPCIBridge, msi_enable, true),
> +        DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static const VMStateDescription pciepci_bridge_dev_vmstate = {
> +        .name = TYPE_PCIE_PCI_BRIDGE_DEV,
> +        .fields = (VMStateField[]) {
> +            VMSTATE_PCI_DEVICE(parent_obj, PCIBridge),
> +            SHPC_VMSTATE(shpc, PCIDevice, pci_device_shpc_present),

SHPC is always present for this device, right?
You don't need tfor presence, just add it to the vmsate.

> +            VMSTATE_END_OF_LIST()
> +        }
> +};
> +
> +static void pcie_pci_bridge_hotplug_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", TYPE_PCIE_PCI_BRIDGE_DEV);
> +        return;
> +    }
> +    shpc_device_hotplug_cb(hotplug_dev, dev, errp);
> +}
> +
> +static void pcie_pci_bridge_hot_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", TYPE_PCIE_PCI_BRIDGE_DEV);
> +        return;
> +    }
> +    shpc_device_hot_unplug_request_cb(hotplug_dev, dev, errp);
> +}
> +
> +static void pciepci_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_express = 1;
> +    k->is_bridge = 1;
> +    k->vendor_id = PCI_VENDOR_ID_REDHAT;
> +    k->device_id = PCI_DEVICE_ID_REDHAT_PCIE_BRIDGE;
> +    k->realize = pciepci_bridge_realize;
> +    k->exit = pciepci_bridge_exit;
> +    k->config_write = pcie_pci_bridge_write_config;
> +    dc->vmsd = &pciepci_bridge_dev_vmstate;
> +    dc->props = pcie_pci_bridge_dev_properties;
> +    dc->vmsd = &pciepci_bridge_dev_vmstate;
> +    dc->reset = &pciepci_bridge_reset;
> +    set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
> +    hc->plug = pcie_pci_bridge_hotplug_cb;
> +    hc->unplug_request = pcie_pci_bridge_hot_unplug_request_cb;
> +}
> +
> +static const TypeInfo pciepci_bridge_info = {
> +        .name = TYPE_PCIE_PCI_BRIDGE_DEV,
> +        .parent = TYPE_PCI_BRIDGE,
> +        .instance_size = sizeof(PCIEPCIBridge),
> +        .class_init = pciepci_bridge_class_init,
> +        .interfaces = (InterfaceInfo[]) {
> +            { TYPE_HOTPLUG_HANDLER },
> +            { },
> +        }
> +};
> +
> +static void pciepci_register(void)
> +{
> +    type_register_static(&pciepci_bridge_info);
> +}
> +
> +type_init(pciepci_register);
> diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h
> index e598b09..b33a34f 100644
> --- a/include/hw/pci/pci.h
> +++ b/include/hw/pci/pci.h
> @@ -98,6 +98,7 @@
>   #define PCI_DEVICE_ID_REDHAT_PXB_PCIE    0x000b
>   #define PCI_DEVICE_ID_REDHAT_PCIE_RP     0x000c
>   #define PCI_DEVICE_ID_REDHAT_XHCI        0x000d
> +#define PCI_DEVICE_ID_REDHAT_PCIE_BRIDGE 0x000e
>   #define PCI_DEVICE_ID_REDHAT_QXL         0x0100
>   
>   #define FMT_PCIBUS                      PRIx64
> 

Other than the comments above the implementation
looks good to me.

Thanks,
Marcel
Aleksandr Bezzubikov July 31, 2017, 6:40 p.m. UTC | #2
2017-07-31 14:23 GMT+03:00 Marcel Apfelbaum <marcel@redhat.com>:
> On 29/07/2017 2:37, Aleksandr Bezzubikov wrote:
>>
>> Introduce a new PCIExpress-to-PCI Bridge device,
>> which is a hot-pluggable PCI Express device and
>> supports devices hot-plug with SHPC.
>>
>> This device is intended to replace the DMI-to-PCI
>> Bridge in an overwhelming majority of use-cases.
>>
>> Signed-off-by: Aleksandr Bezzubikov <zuban32s@gmail.com>
>> ---
>>   hw/pci-bridge/Makefile.objs     |   2 +-
>>   hw/pci-bridge/pcie_pci_bridge.c | 220
>> ++++++++++++++++++++++++++++++++++++++++
>>   include/hw/pci/pci.h            |   1 +
>>   3 files changed, 222 insertions(+), 1 deletion(-)
>>   create mode 100644 hw/pci-bridge/pcie_pci_bridge.c
>>
>> diff --git a/hw/pci-bridge/Makefile.objs b/hw/pci-bridge/Makefile.objs
>> index c4683cf..666db37 100644
>> --- a/hw/pci-bridge/Makefile.objs
>> +++ b/hw/pci-bridge/Makefile.objs
>> @@ -1,4 +1,4 @@
>> -common-obj-y += pci_bridge_dev.o
>> +common-obj-y += pci_bridge_dev.o pcie_pci_bridge.o
>>   common-obj-$(CONFIG_PCIE_PORT) += pcie_root_port.o gen_pcie_root_port.o
>>   common-obj-$(CONFIG_PXB) += pci_expander_bridge.o
>>   common-obj-$(CONFIG_XIO3130) += xio3130_upstream.o xio3130_downstream.o
>> diff --git a/hw/pci-bridge/pcie_pci_bridge.c
>> b/hw/pci-bridge/pcie_pci_bridge.c
>> new file mode 100644
>> index 0000000..c28f820
>> --- /dev/null
>> +++ b/hw/pci-bridge/pcie_pci_bridge.c
>> @@ -0,0 +1,220 @@
>> +/*
>> + * QEMU Generic PCIE-PCI Bridge
>> + *
>> + * Copyright (c) 2017 Aleksandr Bezzubikov
>> + *
>> + * 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 "qapi/error.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"
>> +
>
>
> Hi Aleksander,

Hi Marcel,
thanks for the review

>
>> +typedef struct PCIEPCIBridge {
>> +    /*< private >*/
>> +    PCIBridge parent_obj;
>> +
>> +    bool msi_enable;
>
>
> Please rename the msi_enable property to "msi" in order
> to be aligned with the existent PCIBridgeDev and
> consider making it OnOffAuto for the same reason.
> (I am not sure about the last part though, we have
>  no meaning for "auto" here)
>

Agreed about "msi", but OnOffAuto looks weird to me
as we always want MSI to be enabled.

>> +    MemoryRegion bar;
>
>
> I suggest renaming it to shpc_bar, it will make the code
> more readable.
>

OK.

>> +    /*< public >*/
>> +} PCIEPCIBridge;
>> +
>> +#define TYPE_PCIE_PCI_BRIDGE_DEV      "pcie-pci-bridge"
>> +#define PCIE_PCI_BRIDGE_DEV(obj) \
>> +        OBJECT_CHECK(PCIEPCIBridge, (obj), TYPE_PCIE_PCI_BRIDGE_DEV)
>> +
>> +static void pciepci_bridge_realize(PCIDevice *d, Error **errp)
>> +{
>> +    PCIBridge *br = PCI_BRIDGE(d);
>> +    PCIEPCIBridge *pcie_br = PCIE_PCI_BRIDGE_DEV(d);
>> +    int rc, pos;
>> +    Error *local_err = NULL;
>
>
> Since you don't "create" errors in this function, you
> could simply pass errp to the functions and not use
> local_err, it will save some code lines.
>
>
>> +
>> +    pci_bridge_initfn(d, TYPE_PCI_BUS);
>> +
>> +    d->config[PCI_INTERRUPT_PIN] = 0x1;
>> +    memory_region_init(&pcie_br->bar, OBJECT(d), "shpc-bar",
>> +                       shpc_bar_size(d));
>> +    rc = shpc_init(d, &br->sec_bus, &pcie_br->bar, 0, &local_err);
>> +    if (rc) {
>> +        error_propagate(errp, local_err);
>> +        goto error;
>> +    }
>> +
>> +    rc = pcie_cap_init(d, 0, PCI_EXP_TYPE_PCI_BRIDGE, 0, &local_err);
>> +    if (rc < 0) {
>> +        error_propagate(errp, local_err);
>> +        goto cap_error;
>> +    }
>> +
>> +    pos = pci_add_capability(d, PCI_CAP_ID_PM, 0, PCI_PM_SIZEOF,
>> &local_err);
>> +    if (pos < 0) {
>> +        error_propagate(errp, local_err);
>> +        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,
>> &local_err);
>> +    if (rc < 0) {
>> +        error_propagate(errp, local_err);
>> +        goto aer_error;
>> +    }
>> +
>> +    if (pcie_br->msi_enable) {
>> +        rc = msi_init(d, 0, 1, true, true, &local_err);
>> +        if (rc < 0) {
>> +            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->bar);
>> +    return;
>> +
>> +msi_error:
>> +    pcie_aer_exit(d);
>> +aer_error:
>> +pm_error:
>> +    pcie_cap_exit(d);
>> +cap_error:
>> +    shpc_free(d);
>> +error:
>> +    pci_bridge_exitfn(d);
>> +}
>> +
>> +static void pciepci_bridge_exit(PCIDevice *d)
>> +{
>> +    PCIEPCIBridge *bridge_dev = PCIE_PCI_BRIDGE_DEV(d);
>> +    pcie_cap_exit(d);
>> +    shpc_cleanup(d, &bridge_dev->bar);
>> +    pci_bridge_exitfn(d);
>> +}
>> +
>> +static void pciepci_bridge_reset(DeviceState *qdev)
>> +{
>> +    PCIDevice *d = PCI_DEVICE(qdev);
>> +    pci_bridge_reset(qdev);
>> +    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);
>> +    msi_write_config(d, address, val, len);
>> +    shpc_cap_write_config(d, address, val, len);
>> +}
>> +
>> +static bool pci_device_shpc_present(void *opaque, int version_id)
>> +{
>> +    PCIDevice *dev = opaque;
>> +
>> +    return shpc_present(dev);
>> +}
>> +
>> +static Property pcie_pci_bridge_dev_properties[] = {
>> +        DEFINE_PROP_BOOL("msi_enable", PCIEPCIBridge, msi_enable, true),
>> +        DEFINE_PROP_END_OF_LIST(),
>> +};
>> +
>> +static const VMStateDescription pciepci_bridge_dev_vmstate = {
>> +        .name = TYPE_PCIE_PCI_BRIDGE_DEV,
>> +        .fields = (VMStateField[]) {
>> +            VMSTATE_PCI_DEVICE(parent_obj, PCIBridge),
>> +            SHPC_VMSTATE(shpc, PCIDevice, pci_device_shpc_present),
>
>
> SHPC is always present for this device, right?
> You don't need tfor presence, just add it to the vmsate.
>
>
>> +            VMSTATE_END_OF_LIST()
>> +        }
>> +};
>> +
>> +static void pcie_pci_bridge_hotplug_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", TYPE_PCIE_PCI_BRIDGE_DEV);
>> +        return;
>> +    }
>> +    shpc_device_hotplug_cb(hotplug_dev, dev, errp);
>> +}
>> +
>> +static void pcie_pci_bridge_hot_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", TYPE_PCIE_PCI_BRIDGE_DEV);
>> +        return;
>> +    }
>> +    shpc_device_hot_unplug_request_cb(hotplug_dev, dev, errp);
>> +}
>> +
>> +static void pciepci_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_express = 1;
>> +    k->is_bridge = 1;
>> +    k->vendor_id = PCI_VENDOR_ID_REDHAT;
>> +    k->device_id = PCI_DEVICE_ID_REDHAT_PCIE_BRIDGE;
>> +    k->realize = pciepci_bridge_realize;
>> +    k->exit = pciepci_bridge_exit;
>> +    k->config_write = pcie_pci_bridge_write_config;
>> +    dc->vmsd = &pciepci_bridge_dev_vmstate;
>> +    dc->props = pcie_pci_bridge_dev_properties;
>> +    dc->vmsd = &pciepci_bridge_dev_vmstate;
>> +    dc->reset = &pciepci_bridge_reset;
>> +    set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
>> +    hc->plug = pcie_pci_bridge_hotplug_cb;
>> +    hc->unplug_request = pcie_pci_bridge_hot_unplug_request_cb;
>> +}
>> +
>> +static const TypeInfo pciepci_bridge_info = {
>> +        .name = TYPE_PCIE_PCI_BRIDGE_DEV,
>> +        .parent = TYPE_PCI_BRIDGE,
>> +        .instance_size = sizeof(PCIEPCIBridge),
>> +        .class_init = pciepci_bridge_class_init,
>> +        .interfaces = (InterfaceInfo[]) {
>> +            { TYPE_HOTPLUG_HANDLER },
>> +            { },
>> +        }
>> +};
>> +
>> +static void pciepci_register(void)
>> +{
>> +    type_register_static(&pciepci_bridge_info);
>> +}
>> +
>> +type_init(pciepci_register);
>> diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h
>> index e598b09..b33a34f 100644
>> --- a/include/hw/pci/pci.h
>> +++ b/include/hw/pci/pci.h
>> @@ -98,6 +98,7 @@
>>   #define PCI_DEVICE_ID_REDHAT_PXB_PCIE    0x000b
>>   #define PCI_DEVICE_ID_REDHAT_PCIE_RP     0x000c
>>   #define PCI_DEVICE_ID_REDHAT_XHCI        0x000d
>> +#define PCI_DEVICE_ID_REDHAT_PCIE_BRIDGE 0x000e
>>   #define PCI_DEVICE_ID_REDHAT_QXL         0x0100
>>     #define FMT_PCIBUS                      PRIx64
>>
>
> Other than the comments above the implementation
> looks good to me.
>
> Thanks,
> Marcel
>
Michael S. Tsirkin Aug. 1, 2017, 3:32 p.m. UTC | #3
On Mon, Jul 31, 2017 at 09:40:41PM +0300, Alexander Bezzubikov wrote:
> >> +typedef struct PCIEPCIBridge {
> >> +    /*< private >*/
> >> +    PCIBridge parent_obj;
> >> +
> >> +    bool msi_enable;
> >
> >
> > Please rename the msi_enable property to "msi" in order
> > to be aligned with the existent PCIBridgeDev and
> > consider making it OnOffAuto for the same reason.
> > (I am not sure about the last part though, we have
> >  no meaning for "auto" here)
> >
> 
> Agreed about "msi", but OnOffAuto looks weird to me
> as we always want MSI to be enabled.

Why even have a property then? Can't you enable it unconditionally?
Marcel Apfelbaum Aug. 1, 2017, 3:45 p.m. UTC | #4
On 01/08/2017 18:32, Michael S. Tsirkin wrote:
> On Mon, Jul 31, 2017 at 09:40:41PM +0300, Alexander Bezzubikov wrote:
>>>> +typedef struct PCIEPCIBridge {
>>>> +    /*< private >*/
>>>> +    PCIBridge parent_obj;
>>>> +
>>>> +    bool msi_enable;
>>>
>>>
>>> Please rename the msi_enable property to "msi" in order
>>> to be aligned with the existent PCIBridgeDev and
>>> consider making it OnOffAuto for the same reason.
>>> (I am not sure about the last part though, we have
>>>   no meaning for "auto" here)
>>>
>>
>> Agreed about "msi", but OnOffAuto looks weird to me
>> as we always want MSI to be enabled.
> 

Hi Michael,

> Why even have a property then? Can't you enable it unconditionally?
> 

Because of a current bug in Linux kernel:
    https://www.spinics.net/lists/linux-pci/msg63052.html
msi will not work until the patch is merged. Even when
it will be merged, not all linux kernels will contain the patch.

Disabling msi is a workaround for the above case.

Thanks,
Marcel
Michael S. Tsirkin Aug. 1, 2017, 3:51 p.m. UTC | #5
On Tue, Aug 01, 2017 at 06:45:13PM +0300, Marcel Apfelbaum wrote:
> On 01/08/2017 18:32, Michael S. Tsirkin wrote:
> > On Mon, Jul 31, 2017 at 09:40:41PM +0300, Alexander Bezzubikov wrote:
> > > > > +typedef struct PCIEPCIBridge {
> > > > > +    /*< private >*/
> > > > > +    PCIBridge parent_obj;
> > > > > +
> > > > > +    bool msi_enable;
> > > > 
> > > > 
> > > > Please rename the msi_enable property to "msi" in order
> > > > to be aligned with the existent PCIBridgeDev and
> > > > consider making it OnOffAuto for the same reason.
> > > > (I am not sure about the last part though, we have
> > > >   no meaning for "auto" here)
> > > > 
> > > 
> > > Agreed about "msi", but OnOffAuto looks weird to me
> > > as we always want MSI to be enabled.
> > 
> 
> Hi Michael,
> 
> > Why even have a property then? Can't you enable it unconditionally?
> > 
> 
> Because of a current bug in Linux kernel:
>    https://www.spinics.net/lists/linux-pci/msg63052.html
> msi will not work until the patch is merged. Even when
> it will be merged, not all linux kernels will contain the patch.

You should Cc stable to make sure they all gain it eventually.

> Disabling msi is a workaround for the above case.
> 
> Thanks,
> Marcel

Really enabling MSI without bus master is a bug that I'm not 100% sure
it even worth working around. But I guess it's not too bad to have a
work-around given it's this simple.
Marcel Apfelbaum Aug. 1, 2017, 3:59 p.m. UTC | #6
On 01/08/2017 18:51, Michael S. Tsirkin wrote:
> On Tue, Aug 01, 2017 at 06:45:13PM +0300, Marcel Apfelbaum wrote:
>> On 01/08/2017 18:32, Michael S. Tsirkin wrote:
>>> On Mon, Jul 31, 2017 at 09:40:41PM +0300, Alexander Bezzubikov wrote:
>>>>>> +typedef struct PCIEPCIBridge {
>>>>>> +    /*< private >*/
>>>>>> +    PCIBridge parent_obj;
>>>>>> +
>>>>>> +    bool msi_enable;
>>>>>
>>>>>
>>>>> Please rename the msi_enable property to "msi" in order
>>>>> to be aligned with the existent PCIBridgeDev and
>>>>> consider making it OnOffAuto for the same reason.
>>>>> (I am not sure about the last part though, we have
>>>>>    no meaning for "auto" here)
>>>>>
>>>>
>>>> Agreed about "msi", but OnOffAuto looks weird to me
>>>> as we always want MSI to be enabled.
>>>
>>
>> Hi Michael,
>>
>>> Why even have a property then? Can't you enable it unconditionally?
>>>
>>
>> Because of a current bug in Linux kernel:
>>     https://www.spinics.net/lists/linux-pci/msg63052.html
>> msi will not work until the patch is merged. Even when
>> it will be merged, not all linux kernels will contain the patch.
> 
> You should Cc stable to make sure they all gain it eventually.
> 

Right! thanks, we missed cc-ing stable.
Added stable to the mail thread.
Marcel


>> Disabling msi is a workaround for the above case.
>>
>> Thanks,
>> Marcel
> 
> Really enabling MSI without bus master is a bug that I'm not 100% sure
> it even worth working around. But I guess it's not too bad to have a
> work-around given it's this simple.
>
diff mbox

Patch

diff --git a/hw/pci-bridge/Makefile.objs b/hw/pci-bridge/Makefile.objs
index c4683cf..666db37 100644
--- a/hw/pci-bridge/Makefile.objs
+++ b/hw/pci-bridge/Makefile.objs
@@ -1,4 +1,4 @@ 
-common-obj-y += pci_bridge_dev.o
+common-obj-y += pci_bridge_dev.o pcie_pci_bridge.o
 common-obj-$(CONFIG_PCIE_PORT) += pcie_root_port.o gen_pcie_root_port.o
 common-obj-$(CONFIG_PXB) += pci_expander_bridge.o
 common-obj-$(CONFIG_XIO3130) += xio3130_upstream.o xio3130_downstream.o
diff --git a/hw/pci-bridge/pcie_pci_bridge.c b/hw/pci-bridge/pcie_pci_bridge.c
new file mode 100644
index 0000000..c28f820
--- /dev/null
+++ b/hw/pci-bridge/pcie_pci_bridge.c
@@ -0,0 +1,220 @@ 
+/*
+ * QEMU Generic PCIE-PCI Bridge
+ *
+ * Copyright (c) 2017 Aleksandr Bezzubikov
+ *
+ * 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 "qapi/error.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"
+
+typedef struct PCIEPCIBridge {
+    /*< private >*/
+    PCIBridge parent_obj;
+
+    bool msi_enable;
+    MemoryRegion bar;
+    /*< public >*/
+} PCIEPCIBridge;
+
+#define TYPE_PCIE_PCI_BRIDGE_DEV      "pcie-pci-bridge"
+#define PCIE_PCI_BRIDGE_DEV(obj) \
+        OBJECT_CHECK(PCIEPCIBridge, (obj), TYPE_PCIE_PCI_BRIDGE_DEV)
+
+static void pciepci_bridge_realize(PCIDevice *d, Error **errp)
+{
+    PCIBridge *br = PCI_BRIDGE(d);
+    PCIEPCIBridge *pcie_br = PCIE_PCI_BRIDGE_DEV(d);
+    int rc, pos;
+    Error *local_err = NULL;
+
+    pci_bridge_initfn(d, TYPE_PCI_BUS);
+
+    d->config[PCI_INTERRUPT_PIN] = 0x1;
+    memory_region_init(&pcie_br->bar, OBJECT(d), "shpc-bar",
+                       shpc_bar_size(d));
+    rc = shpc_init(d, &br->sec_bus, &pcie_br->bar, 0, &local_err);
+    if (rc) {
+        error_propagate(errp, local_err);
+        goto error;
+    }
+
+    rc = pcie_cap_init(d, 0, PCI_EXP_TYPE_PCI_BRIDGE, 0, &local_err);
+    if (rc < 0) {
+        error_propagate(errp, local_err);
+        goto cap_error;
+    }
+
+    pos = pci_add_capability(d, PCI_CAP_ID_PM, 0, PCI_PM_SIZEOF, &local_err);
+    if (pos < 0) {
+        error_propagate(errp, local_err);
+        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, &local_err);
+    if (rc < 0) {
+        error_propagate(errp, local_err);
+        goto aer_error;
+    }
+
+    if (pcie_br->msi_enable) {
+        rc = msi_init(d, 0, 1, true, true, &local_err);
+        if (rc < 0) {
+            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->bar);
+    return;
+
+msi_error:
+    pcie_aer_exit(d);
+aer_error:
+pm_error:
+    pcie_cap_exit(d);
+cap_error:
+    shpc_free(d);
+error:
+    pci_bridge_exitfn(d);
+}
+
+static void pciepci_bridge_exit(PCIDevice *d)
+{
+    PCIEPCIBridge *bridge_dev = PCIE_PCI_BRIDGE_DEV(d);
+    pcie_cap_exit(d);
+    shpc_cleanup(d, &bridge_dev->bar);
+    pci_bridge_exitfn(d);
+}
+
+static void pciepci_bridge_reset(DeviceState *qdev)
+{
+    PCIDevice *d = PCI_DEVICE(qdev);
+    pci_bridge_reset(qdev);
+    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);
+    msi_write_config(d, address, val, len);
+    shpc_cap_write_config(d, address, val, len);
+}
+
+static bool pci_device_shpc_present(void *opaque, int version_id)
+{
+    PCIDevice *dev = opaque;
+
+    return shpc_present(dev);
+}
+
+static Property pcie_pci_bridge_dev_properties[] = {
+        DEFINE_PROP_BOOL("msi_enable", PCIEPCIBridge, msi_enable, true),
+        DEFINE_PROP_END_OF_LIST(),
+};
+
+static const VMStateDescription pciepci_bridge_dev_vmstate = {
+        .name = TYPE_PCIE_PCI_BRIDGE_DEV,
+        .fields = (VMStateField[]) {
+            VMSTATE_PCI_DEVICE(parent_obj, PCIBridge),
+            SHPC_VMSTATE(shpc, PCIDevice, pci_device_shpc_present),
+            VMSTATE_END_OF_LIST()
+        }
+};
+
+static void pcie_pci_bridge_hotplug_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", TYPE_PCIE_PCI_BRIDGE_DEV);
+        return;
+    }
+    shpc_device_hotplug_cb(hotplug_dev, dev, errp);
+}
+
+static void pcie_pci_bridge_hot_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", TYPE_PCIE_PCI_BRIDGE_DEV);
+        return;
+    }
+    shpc_device_hot_unplug_request_cb(hotplug_dev, dev, errp);
+}
+
+static void pciepci_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_express = 1;
+    k->is_bridge = 1;
+    k->vendor_id = PCI_VENDOR_ID_REDHAT;
+    k->device_id = PCI_DEVICE_ID_REDHAT_PCIE_BRIDGE;
+    k->realize = pciepci_bridge_realize;
+    k->exit = pciepci_bridge_exit;
+    k->config_write = pcie_pci_bridge_write_config;
+    dc->vmsd = &pciepci_bridge_dev_vmstate;
+    dc->props = pcie_pci_bridge_dev_properties;
+    dc->vmsd = &pciepci_bridge_dev_vmstate;
+    dc->reset = &pciepci_bridge_reset;
+    set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
+    hc->plug = pcie_pci_bridge_hotplug_cb;
+    hc->unplug_request = pcie_pci_bridge_hot_unplug_request_cb;
+}
+
+static const TypeInfo pciepci_bridge_info = {
+        .name = TYPE_PCIE_PCI_BRIDGE_DEV,
+        .parent = TYPE_PCI_BRIDGE,
+        .instance_size = sizeof(PCIEPCIBridge),
+        .class_init = pciepci_bridge_class_init,
+        .interfaces = (InterfaceInfo[]) {
+            { TYPE_HOTPLUG_HANDLER },
+            { },
+        }
+};
+
+static void pciepci_register(void)
+{
+    type_register_static(&pciepci_bridge_info);
+}
+
+type_init(pciepci_register);
diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h
index e598b09..b33a34f 100644
--- a/include/hw/pci/pci.h
+++ b/include/hw/pci/pci.h
@@ -98,6 +98,7 @@ 
 #define PCI_DEVICE_ID_REDHAT_PXB_PCIE    0x000b
 #define PCI_DEVICE_ID_REDHAT_PCIE_RP     0x000c
 #define PCI_DEVICE_ID_REDHAT_XHCI        0x000d
+#define PCI_DEVICE_ID_REDHAT_PCIE_BRIDGE 0x000e
 #define PCI_DEVICE_ID_REDHAT_QXL         0x0100
 
 #define FMT_PCIBUS                      PRIx64