This patch adds support for PCIe Root Complex Event Collector (RCEC) emulation. Further, if a RCiEP supports AER capability then a mapping is created for that RCiEP in the RCEC's endpoint association capability.
Signed-off-by: Mayuresh Chitale <mchit...@ventanamicro.com> --- hw/pci/meson.build | 2 +- hw/pci/pcie.c | 18 ++++++ hw/pci/pcie_aer.c | 9 +++ hw/pci/pcie_rcec.c | 119 +++++++++++++++++++++++++++++++++++++ include/hw/pci/pci.h | 1 + include/hw/pci/pci_ids.h | 1 + include/hw/pci/pcie.h | 1 + include/hw/pci/pcie_regs.h | 3 + 8 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 hw/pci/pcie_rcec.c diff --git a/hw/pci/meson.build b/hw/pci/meson.build index 5c4bbac817..34ae7d4a44 100644 --- a/hw/pci/meson.build +++ b/hw/pci/meson.build @@ -11,7 +11,7 @@ pci_ss.add(files( # The functions in these modules can be used by devices too. Since we # allow plugging PCIe devices into PCI buses, include them even if # CONFIG_PCI_EXPRESS=n. -pci_ss.add(files('pcie.c', 'pcie_aer.c')) +pci_ss.add(files('pcie.c', 'pcie_aer.c', 'pcie_rcec.c')) softmmu_ss.add(when: 'CONFIG_PCI_EXPRESS', if_true: files('pcie_port.c', 'pcie_host.c')) softmmu_ss.add_all(when: 'CONFIG_PCI', if_true: pci_ss) diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index 017d5075ae..55938a1798 100644 --- a/hw/pci/pcie.c +++ b/hw/pci/pcie.c @@ -747,6 +747,24 @@ void pcie_cap_slot_push_attention_button(PCIDevice *dev) pcie_cap_slot_event(dev, PCI_EXP_HP_EV_ABP); } +void pcie_rcec_ep_map(PCIDevice *dev) +{ + int devnum = PCI_SLOT(dev->devfn); + uint32_t ep_bitmap; + PCIDevice *rcec; + uint16_t cap; + + /* RCEC is always expected to be at 00:01.0 */ + rcec = pci_find_device(pci_get_bus(dev), 0, PCI_DEVFN(1,0)); + if (!rcec) + return; + + pcie_cap_deverr_init(dev); + cap = pcie_find_capability(rcec, PCI_EXT_CAP_ID_RCEC); + ep_bitmap = pci_get_long(rcec->config + cap + 0x4); + pci_set_long(rcec->config + cap + 0x4, ep_bitmap | 1 << devnum); +} + /* root control/capabilities/status. PME isn't emulated for now */ void pcie_cap_root_init(PCIDevice *dev) { diff --git a/hw/pci/pcie_aer.c b/hw/pci/pcie_aer.c index 27f9cc56af..ba4a1e6d78 100644 --- a/hw/pci/pcie_aer.c +++ b/hw/pci/pcie_aer.c @@ -166,6 +166,15 @@ int pcie_aer_init(PCIDevice *dev, uint8_t cap_ver, uint16_t offset, /* nothing */ break; } + + /* + * If this is a RCiEP, map it into the RCEC's endpoint association bitmap + * capability + */ + if (pci_bus_is_express(pci_get_bus(dev)) + && pci_bus_is_root(pci_get_bus(dev))) + pcie_rcec_ep_map(dev); + return 0; } diff --git a/hw/pci/pcie_rcec.c b/hw/pci/pcie_rcec.c new file mode 100644 index 0000000000..9a51d7b9b4 --- /dev/null +++ b/hw/pci/pcie_rcec.c @@ -0,0 +1,119 @@ +/* + * pcie_rcec.c + * PCIe Root Complex Event Collector emulation + * + * Copyright (c) 2021 Mayuresh Chitale <mchit...@ventanamicro.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qemu/module.h" +#include "qemu/range.h" +#include "sysemu/sysemu.h" +#include "hw/hw.h" +#include "hw/pci/pci.h" +#include "hw/qdev-properties.h" +#include "migration/vmstate.h" + +#define TYPE_RCEC_DEVICE "pcie-rcec" +#define PCIE_RCEC_EXP_CAP_OFF 0x40 +#define PCIE_RCEC_EP_ECAP_OFF 0x100 +#define PCIE_RCEC_AER_ECAP_OFF 0x120 + +struct RcecState { + /*< private >*/ + PCIDevice parent_obj; + /*< public >*/ +}; + + +static int pcie_rcec_cap_init(PCIDevice *dev, uint8_t offset) +{ + int rc; + + dev->config[PCI_INTERRUPT_PIN] = 1; + rc = pcie_endpoint_cap_common_init(dev, offset, + PCI_EXP_VER2_SIZEOF, PCI_EXP_TYPE_RC_EC); + pcie_cap_root_init(dev); + pcie_cap_deverr_init(dev); + + return rc; +} + +static void pcie_rcec_ep_cap_init(PCIDevice *dev, uint8_t cap_ver, uint16_t offset, + uint16_t size, Error **errp) +{ + pcie_add_capability(dev, PCI_EXT_CAP_ID_RCEC, cap_ver, offset, size); + /* Map device (bit) 1 which is RCEC by default */ + pci_set_long(dev->config + offset + 0x4, 0x2); +} + +static void pcie_rcec_realize(PCIDevice *pci_dev, Error **errp) +{ + if (pcie_rcec_cap_init(pci_dev, PCIE_RCEC_EXP_CAP_OFF) < 0) + hw_error("Failed to initialize RCEC express capability"); + + pcie_rcec_ep_cap_init(pci_dev, PCI_RCEC_EP_VER, PCIE_RCEC_EP_ECAP_OFF, + PCI_RCEC_EP_SIZEOF, errp); + + if (pcie_aer_init(pci_dev, PCI_ERR_VER, PCIE_RCEC_AER_ECAP_OFF, + PCI_ERR_SIZEOF, errp) < 0) + hw_error("Failed to initialize RCEC AER capability"); +} + +static const VMStateDescription vmstate_rcec = { + .name = "rcec", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(parent_obj, struct RcecState), + VMSTATE_END_OF_LIST() + } +}; + +static void rcec_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 = "QEMU generic RCEC"; + dc->vmsd = &vmstate_rcec; + k->vendor_id = PCI_VENDOR_ID_REDHAT; + k->device_id = PCI_DEVICE_ID_REDHAT_RCEC; + k->revision = 0; + k->class_id = PCI_CLASS_SYSTEM_RCEC; + k->realize = pcie_rcec_realize; +} + +static const TypeInfo pcie_rcec_info = { + .name = TYPE_RCEC_DEVICE, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(struct RcecState), + .class_init = rcec_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_PCIE_DEVICE }, + { }, + }, +}; + + +static void pcie_rcec_register_types(void) +{ + type_register_static(&pcie_rcec_info); +} + +type_init(pcie_rcec_register_types) diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index d0f4266e37..d580c9e6cf 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -109,6 +109,7 @@ extern bool pci_available; #define PCI_DEVICE_ID_REDHAT_NVME 0x0010 #define PCI_DEVICE_ID_REDHAT_PVPANIC 0x0011 #define PCI_DEVICE_ID_REDHAT_QXL 0x0100 +#define PCI_DEVICE_ID_REDHAT_RCEC 0x0101 #define FMT_PCIBUS PRIx64 diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h index 11abe22d46..d5214d2764 100644 --- a/include/hw/pci/pci_ids.h +++ b/include/hw/pci/pci_ids.h @@ -88,6 +88,7 @@ #define PCI_CLASS_SYSTEM_RTC 0x0803 #define PCI_CLASS_SYSTEM_PCI_HOTPLUG 0x0804 #define PCI_CLASS_SYSTEM_SDHCI 0x0805 +#define PCI_CLASS_SYSTEM_RCEC 0x0807 #define PCI_CLASS_SYSTEM_OTHER 0x0880 #define PCI_BASE_CLASS_INPUT 0x09 diff --git a/include/hw/pci/pcie.h b/include/hw/pci/pcie.h index b40b088604..482fefe704 100644 --- a/include/hw/pci/pcie.h +++ b/include/hw/pci/pcie.h @@ -149,4 +149,5 @@ void pcie_cap_slot_unplug_request_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); int pcie_endpoint_cap_common_init(PCIDevice *dev, uint8_t offset, uint8_t cap_size, uint8_t type); +void pcie_rcec_ep_map(PCIDevice *dev); #endif /* QEMU_PCIE_H */ diff --git a/include/hw/pci/pcie_regs.h b/include/hw/pci/pcie_regs.h index 1db86b0ec4..7b8f2616e9 100644 --- a/include/hw/pci/pcie_regs.h +++ b/include/hw/pci/pcie_regs.h @@ -179,4 +179,7 @@ typedef enum PCIExpLinkWidth { #define PCI_ACS_VER 0x1 #define PCI_ACS_SIZEOF 8 +#define PCI_RCEC_EP_VER 1 +#define PCI_RCEC_EP_SIZEOF 0x8 + #endif /* QEMU_PCIE_REGS_H */ -- 2.17.1