Created
November 18, 2018 04:32
-
-
Save Aerocatia/411acfbb8d5eb8ceb337924f1c04d1ef to your computer and use it in GitHub Desktop.
Patch for enhanced link speed and width support for Qemu 3.0.0
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- | |
hw/pci/pcie.c | 7 ++++--- | |
hw/vfio/pci.c | 3 ++- | |
include/hw/pci/pcie_regs.h | 23 +++++++++++++++++++++-- | |
3 files changed, 27 insertions(+), 6 deletions(-) | |
diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c | |
index 6c91bd44a0a5..914a5261a79b 100644 | |
--- a/hw/pci/pcie.c | |
+++ b/hw/pci/pcie.c | |
@@ -68,11 +68,12 @@ pcie_cap_v1_fill(PCIDevice *dev, uint8_t port, uint8_t type, uint8_t version) | |
pci_set_long(exp_cap + PCI_EXP_LNKCAP, | |
(port << PCI_EXP_LNKCAP_PN_SHIFT) | | |
PCI_EXP_LNKCAP_ASPMS_0S | | |
- PCI_EXP_LNK_MLW_1 | | |
- PCI_EXP_LNK_LS_25); | |
+ QEMU_PCI_EXP_LNKCAP_MLW(QEMU_PCI_EXP_LNK_X1) | | |
+ QEMU_PCI_EXP_LNKCAP_MLS(QEMU_PCI_EXP_LNK_2_5GT)); | |
pci_set_word(exp_cap + PCI_EXP_LNKSTA, | |
- PCI_EXP_LNK_MLW_1 | PCI_EXP_LNK_LS_25); | |
+ QEMU_PCI_EXP_LNKSTA_NLW(QEMU_PCI_EXP_LNK_X1) | | |
+ QEMU_PCI_EXP_LNKSTA_CLS(QEMU_PCI_EXP_LNK_2_5GT)); | |
if (dev->cap_present & QEMU_PCIE_LNKSTA_DLLLA) { | |
pci_word_test_and_set_mask(exp_cap + PCI_EXP_LNKSTA, | |
diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c | |
index 866f0deeb7eb..08aab328b442 100644 | |
--- a/hw/vfio/pci.c | |
+++ b/hw/vfio/pci.c | |
@@ -1895,7 +1895,8 @@ static int vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size, | |
PCI_EXP_TYPE_ENDPOINT << 4, | |
PCI_EXP_FLAGS_TYPE); | |
vfio_add_emulated_long(vdev, pos + PCI_EXP_LNKCAP, | |
- PCI_EXP_LNK_MLW_1 | PCI_EXP_LNK_LS_25, ~0); | |
+ QEMU_PCI_EXP_LNKCAP_MLW(QEMU_PCI_EXP_LNK_X1) | | |
+ QEMU_PCI_EXP_LNKCAP_MLS(QEMU_PCI_EXP_LNK_2_5GT), ~0); | |
vfio_add_emulated_word(vdev, pos + PCI_EXP_LNKCTL, 0, ~0); | |
} | |
diff --git a/include/hw/pci/pcie_regs.h b/include/hw/pci/pcie_regs.h | |
index a95522a13b04..ad4e7808b8ac 100644 | |
--- a/include/hw/pci/pcie_regs.h | |
+++ b/include/hw/pci/pcie_regs.h | |
@@ -34,10 +34,29 @@ | |
/* PCI_EXP_LINK{CAP, STA} */ | |
/* link speed */ | |
-#define PCI_EXP_LNK_LS_25 1 | |
+typedef enum PCIExpLinkSpeed { | |
+ QEMU_PCI_EXP_LNK_2_5GT = 1, | |
+ QEMU_PCI_EXP_LNK_5GT, | |
+ QEMU_PCI_EXP_LNK_8GT, | |
+ QEMU_PCI_EXP_LNK_16GT, | |
+} PCIExpLinkSpeed; | |
+ | |
+#define QEMU_PCI_EXP_LNKCAP_MLS(speed) (speed) | |
+#define QEMU_PCI_EXP_LNKSTA_CLS QEMU_PCI_EXP_LNKCAP_MLS | |
+ | |
+typedef enum PCIExpLinkWidth { | |
+ QEMU_PCI_EXP_LNK_X1 = 1, | |
+ QEMU_PCI_EXP_LNK_X2 = 2, | |
+ QEMU_PCI_EXP_LNK_X4 = 4, | |
+ QEMU_PCI_EXP_LNK_X8 = 8, | |
+ QEMU_PCI_EXP_LNK_X12 = 12, | |
+ QEMU_PCI_EXP_LNK_X16 = 16, | |
+ QEMU_PCI_EXP_LNK_X32 = 32, | |
+} PCIExpLinkWidth; | |
#define PCI_EXP_LNK_MLW_SHIFT ctz32(PCI_EXP_LNKCAP_MLW) | |
-#define PCI_EXP_LNK_MLW_1 (1 << PCI_EXP_LNK_MLW_SHIFT) | |
+#define QEMU_PCI_EXP_LNKCAP_MLW(width) (width << PCI_EXP_LNK_MLW_SHIFT) | |
+#define QEMU_PCI_EXP_LNKSTA_NLW QEMU_PCI_EXP_LNKCAP_MLW | |
/* PCI_EXP_LINKCAP */ | |
#define PCI_EXP_LNKCAP_ASPMS_SHIFT ctz32(PCI_EXP_LNKCAP_ASPMS) | |
--- | |
hw/pci/pci.c | 4 ++++ | |
hw/pci/pcie.c | 39 +++++++++++++++++++++++++++++++++++++++ | |
include/hw/pci/pci.h | 13 +++++++++++++ | |
include/hw/pci/pcie.h | 1 + | |
4 files changed, 57 insertions(+) | |
diff --git a/hw/pci/pci.c b/hw/pci/pci.c | |
index b937f0dc0af4..576c85f376b6 100644 | |
--- a/hw/pci/pci.c | |
+++ b/hw/pci/pci.c | |
@@ -1353,6 +1353,10 @@ uint32_t pci_default_read_config(PCIDevice *d, | |
{ | |
uint32_t val = 0; | |
+ if (pci_is_express_downstream_port(d) && | |
+ ranges_overlap(address, len, d->exp.exp_cap + PCI_EXP_LNKSTA, 2)) { | |
+ pcie_sync_bridge_lnk(d); | |
+ } | |
memcpy(&val, d->config + address, len); | |
return le32_to_cpu(val); | |
} | |
diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c | |
index 914a5261a79b..61b7b96c52cd 100644 | |
--- a/hw/pci/pcie.c | |
+++ b/hw/pci/pcie.c | |
@@ -729,6 +729,45 @@ void pcie_add_capability(PCIDevice *dev, | |
memset(dev->cmask + offset, 0xFF, size); | |
} | |
+/* | |
+ * Sync the PCIe Link Status negotiated speed and width of a bridge with the | |
+ * downstream device. If downstream device is not present, re-write with the | |
+ * Link Capability fields. Limit width and speed to bridge capabilities for | |
+ * compatibility. Use config_read to access the downstream device since it | |
+ * could be an assigned device with volatile link information. | |
+ */ | |
+void pcie_sync_bridge_lnk(PCIDevice *bridge_dev) | |
+{ | |
+ PCIBridge *br = PCI_BRIDGE(bridge_dev); | |
+ PCIBus *bus = pci_bridge_get_sec_bus(br); | |
+ PCIDevice *target = bus->devices[0]; | |
+ uint8_t *exp_cap = bridge_dev->config + bridge_dev->exp.exp_cap; | |
+ uint16_t lnksta, lnkcap = pci_get_word(exp_cap + PCI_EXP_LNKCAP); | |
+ | |
+ if (!target || !target->exp.exp_cap) { | |
+ lnksta = lnkcap; | |
+ } else { | |
+ lnksta = target->config_read(target, | |
+ target->exp.exp_cap + PCI_EXP_LNKSTA, | |
+ sizeof(lnksta)); | |
+ | |
+ if ((lnksta & PCI_EXP_LNKSTA_NLW) > (lnkcap & PCI_EXP_LNKCAP_MLW)) { | |
+ lnksta &= ~PCI_EXP_LNKSTA_NLW; | |
+ lnksta |= lnkcap & PCI_EXP_LNKCAP_MLW; | |
+ } | |
+ | |
+ if ((lnksta & PCI_EXP_LNKSTA_CLS) > (lnkcap & PCI_EXP_LNKCAP_SLS)) { | |
+ lnksta &= ~PCI_EXP_LNKSTA_CLS; | |
+ lnksta |= lnkcap & PCI_EXP_LNKCAP_SLS; | |
+ } | |
+ } | |
+ | |
+ pci_word_test_and_clear_mask(exp_cap + PCI_EXP_LNKSTA, | |
+ PCI_EXP_LNKSTA_CLS | PCI_EXP_LNKSTA_NLW); | |
+ pci_word_test_and_set_mask(exp_cap + PCI_EXP_LNKSTA, lnksta & | |
+ (PCI_EXP_LNKSTA_CLS | PCI_EXP_LNKSTA_NLW)); | |
+} | |
+ | |
/************************************************************************** | |
* pci express extended capability helper functions | |
*/ | |
diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h | |
index e6514bba23aa..eb12fa112ed2 100644 | |
--- a/include/hw/pci/pci.h | |
+++ b/include/hw/pci/pci.h | |
@@ -737,6 +737,19 @@ static inline int pci_is_express(const PCIDevice *d) | |
return d->cap_present & QEMU_PCI_CAP_EXPRESS; | |
} | |
+static inline int pci_is_express_downstream_port(const PCIDevice *d) | |
+{ | |
+ uint8_t type; | |
+ | |
+ if (!pci_is_express(d) || !d->exp.exp_cap) { | |
+ return 0; | |
+ } | |
+ | |
+ type = pcie_cap_get_type(d); | |
+ | |
+ return type == PCI_EXP_TYPE_DOWNSTREAM || type == PCI_EXP_TYPE_ROOT_PORT; | |
+} | |
+ | |
static inline uint32_t pci_config_size(const PCIDevice *d) | |
{ | |
return pci_is_express(d) ? PCIE_CONFIG_SPACE_SIZE : PCI_CONFIG_SPACE_SIZE; | |
diff --git a/include/hw/pci/pcie.h b/include/hw/pci/pcie.h | |
index b71e36970345..1976909ab4c8 100644 | |
--- a/include/hw/pci/pcie.h | |
+++ b/include/hw/pci/pcie.h | |
@@ -126,6 +126,7 @@ uint16_t pcie_find_capability(PCIDevice *dev, uint16_t cap_id); | |
void pcie_add_capability(PCIDevice *dev, | |
uint16_t cap_id, uint8_t cap_ver, | |
uint16_t offset, uint16_t size); | |
+void pcie_sync_bridge_lnk(PCIDevice *dev); | |
void pcie_ari_init(PCIDevice *dev, uint16_t offset, uint16_t nextfn); | |
void pcie_dev_ser_num_init(PCIDevice *dev, uint16_t offset, uint64_t ser_num); | |
--- | |
hw/core/qdev-properties.c | 178 ++++++++++++++++++++++++++++++++++++++++++ | |
include/hw/qdev-properties.h | 8 ++ | |
qapi/common.json | 42 ++++++++++ | |
3 files changed, 228 insertions(+) | |
diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c | |
index 35072dec1ecf..f5ca5b821a79 100644 | |
--- a/hw/core/qdev-properties.c | |
+++ b/hw/core/qdev-properties.c | |
@@ -1327,3 +1327,181 @@ const PropertyInfo qdev_prop_off_auto_pcibar = { | |
.set = set_enum, | |
.set_default_value = set_default_value_enum, | |
}; | |
+ | |
+/* --- PCIELinkSpeed 2_5/5/8/16 -- */ | |
+ | |
+static void get_prop_pcielinkspeed(Object *obj, Visitor *v, const char *name, | |
+ void *opaque, Error **errp) | |
+{ | |
+ DeviceState *dev = DEVICE(obj); | |
+ Property *prop = opaque; | |
+ PCIExpLinkSpeed *p = qdev_get_prop_ptr(dev, prop); | |
+ PCIELinkSpeed speed; | |
+ | |
+ switch (*p) { | |
+ case QEMU_PCI_EXP_LNK_2_5GT: | |
+ speed = PCIE_LINK_SPEED_2_5; | |
+ break; | |
+ case QEMU_PCI_EXP_LNK_5GT: | |
+ speed = PCIE_LINK_SPEED_5; | |
+ break; | |
+ case QEMU_PCI_EXP_LNK_8GT: | |
+ speed = PCIE_LINK_SPEED_8; | |
+ break; | |
+ case QEMU_PCI_EXP_LNK_16GT: | |
+ speed = PCIE_LINK_SPEED_16; | |
+ break; | |
+ default: | |
+ /* Unreachable */ | |
+ abort(); | |
+ } | |
+ | |
+ visit_type_enum(v, prop->name, (int *)&speed, prop->info->enum_table, errp); | |
+} | |
+ | |
+static void set_prop_pcielinkspeed(Object *obj, Visitor *v, const char *name, | |
+ void *opaque, Error **errp) | |
+{ | |
+ DeviceState *dev = DEVICE(obj); | |
+ Property *prop = opaque; | |
+ PCIExpLinkSpeed *p = qdev_get_prop_ptr(dev, prop); | |
+ PCIELinkSpeed speed; | |
+ Error *local_err = NULL; | |
+ | |
+ if (dev->realized) { | |
+ qdev_prop_set_after_realize(dev, name, errp); | |
+ return; | |
+ } | |
+ | |
+ visit_type_enum(v, prop->name, (int *)&speed, | |
+ prop->info->enum_table, &local_err); | |
+ if (local_err) { | |
+ error_propagate(errp, local_err); | |
+ return; | |
+ } | |
+ | |
+ switch (speed) { | |
+ case PCIE_LINK_SPEED_2_5: | |
+ *p = QEMU_PCI_EXP_LNK_2_5GT; | |
+ break; | |
+ case PCIE_LINK_SPEED_5: | |
+ *p = QEMU_PCI_EXP_LNK_5GT; | |
+ break; | |
+ case PCIE_LINK_SPEED_8: | |
+ *p = QEMU_PCI_EXP_LNK_8GT; | |
+ break; | |
+ case PCIE_LINK_SPEED_16: | |
+ *p = QEMU_PCI_EXP_LNK_16GT; | |
+ break; | |
+ default: | |
+ /* Unreachable */ | |
+ abort(); | |
+ } | |
+} | |
+ | |
+const PropertyInfo qdev_prop_pcie_link_speed = { | |
+ .name = "PCIELinkSpeed", | |
+ .description = "2_5/5/8/16", | |
+ .enum_table = &PCIELinkSpeed_lookup, | |
+ .get = get_prop_pcielinkspeed, | |
+ .set = set_prop_pcielinkspeed, | |
+ .set_default_value = set_default_value_enum, | |
+}; | |
+ | |
+/* --- PCIELinkWidth 1/2/4/8/12/16/32 -- */ | |
+ | |
+static void get_prop_pcielinkwidth(Object *obj, Visitor *v, const char *name, | |
+ void *opaque, Error **errp) | |
+{ | |
+ DeviceState *dev = DEVICE(obj); | |
+ Property *prop = opaque; | |
+ PCIExpLinkWidth *p = qdev_get_prop_ptr(dev, prop); | |
+ PCIELinkWidth width; | |
+ | |
+ switch (*p) { | |
+ case QEMU_PCI_EXP_LNK_X1: | |
+ width = PCIE_LINK_WIDTH_1; | |
+ break; | |
+ case QEMU_PCI_EXP_LNK_X2: | |
+ width = PCIE_LINK_WIDTH_2; | |
+ break; | |
+ case QEMU_PCI_EXP_LNK_X4: | |
+ width = PCIE_LINK_WIDTH_4; | |
+ break; | |
+ case QEMU_PCI_EXP_LNK_X8: | |
+ width = PCIE_LINK_WIDTH_8; | |
+ break; | |
+ case QEMU_PCI_EXP_LNK_X12: | |
+ width = PCIE_LINK_WIDTH_12; | |
+ break; | |
+ case QEMU_PCI_EXP_LNK_X16: | |
+ width = PCIE_LINK_WIDTH_16; | |
+ break; | |
+ case QEMU_PCI_EXP_LNK_X32: | |
+ width = PCIE_LINK_WIDTH_32; | |
+ break; | |
+ default: | |
+ /* Unreachable */ | |
+ abort(); | |
+ } | |
+ | |
+ visit_type_enum(v, prop->name, (int *)&width, prop->info->enum_table, errp); | |
+} | |
+ | |
+static void set_prop_pcielinkwidth(Object *obj, Visitor *v, const char *name, | |
+ void *opaque, Error **errp) | |
+{ | |
+ DeviceState *dev = DEVICE(obj); | |
+ Property *prop = opaque; | |
+ PCIExpLinkWidth *p = qdev_get_prop_ptr(dev, prop); | |
+ PCIELinkWidth width; | |
+ Error *local_err = NULL; | |
+ | |
+ if (dev->realized) { | |
+ qdev_prop_set_after_realize(dev, name, errp); | |
+ return; | |
+ } | |
+ | |
+ visit_type_enum(v, prop->name, (int *)&width, | |
+ prop->info->enum_table, &local_err); | |
+ if (local_err) { | |
+ error_propagate(errp, local_err); | |
+ return; | |
+ } | |
+ | |
+ switch (width) { | |
+ case PCIE_LINK_WIDTH_1: | |
+ *p = QEMU_PCI_EXP_LNK_X1; | |
+ break; | |
+ case PCIE_LINK_WIDTH_2: | |
+ *p = QEMU_PCI_EXP_LNK_X2; | |
+ break; | |
+ case PCIE_LINK_WIDTH_4: | |
+ *p = QEMU_PCI_EXP_LNK_X4; | |
+ break; | |
+ case PCIE_LINK_WIDTH_8: | |
+ *p = QEMU_PCI_EXP_LNK_X8; | |
+ break; | |
+ case PCIE_LINK_WIDTH_12: | |
+ *p = QEMU_PCI_EXP_LNK_X12; | |
+ break; | |
+ case PCIE_LINK_WIDTH_16: | |
+ *p = QEMU_PCI_EXP_LNK_X16; | |
+ break; | |
+ case PCIE_LINK_WIDTH_32: | |
+ *p = QEMU_PCI_EXP_LNK_X32; | |
+ break; | |
+ default: | |
+ /* Unreachable */ | |
+ abort(); | |
+ } | |
+} | |
+ | |
+const PropertyInfo qdev_prop_pcie_link_width = { | |
+ .name = "PCIELinkWidth", | |
+ .description = "1/2/4/8/12/16/32", | |
+ .enum_table = &PCIELinkWidth_lookup, | |
+ .get = get_prop_pcielinkwidth, | |
+ .set = set_prop_pcielinkwidth, | |
+ .set_default_value = set_default_value_enum, | |
+}; | |
diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h | |
index 4f60cc88f325..6a13a284c48c 100644 | |
--- a/include/hw/qdev-properties.h | |
+++ b/include/hw/qdev-properties.h | |
@@ -36,6 +36,8 @@ extern const PropertyInfo qdev_prop_uuid; | |
extern const PropertyInfo qdev_prop_arraylen; | |
extern const PropertyInfo qdev_prop_link; | |
extern const PropertyInfo qdev_prop_off_auto_pcibar; | |
+extern const PropertyInfo qdev_prop_pcie_link_speed; | |
+extern const PropertyInfo qdev_prop_pcie_link_width; | |
#define DEFINE_PROP(_name, _state, _field, _prop, _type) { \ | |
.name = (_name), \ | |
@@ -217,6 +219,12 @@ extern const PropertyInfo qdev_prop_off_auto_pcibar; | |
#define DEFINE_PROP_OFF_AUTO_PCIBAR(_n, _s, _f, _d) \ | |
DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_off_auto_pcibar, \ | |
OffAutoPCIBAR) | |
+#define DEFINE_PROP_PCIE_LINK_SPEED(_n, _s, _f, _d) \ | |
+ DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_pcie_link_speed, \ | |
+ PCIExpLinkSpeed) | |
+#define DEFINE_PROP_PCIE_LINK_WIDTH(_n, _s, _f, _d) \ | |
+ DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_pcie_link_width, \ | |
+ PCIExpLinkWidth) | |
#define DEFINE_PROP_UUID(_name, _state, _field) { \ | |
.name = (_name), \ | |
diff --git a/qapi/common.json b/qapi/common.json | |
index 021174f04ea4..b6f3cca35c7e 100644 | |
--- a/qapi/common.json | |
+++ b/qapi/common.json | |
@@ -127,6 +127,48 @@ | |
{ 'enum': 'OffAutoPCIBAR', | |
'data': [ 'off', 'auto', 'bar0', 'bar1', 'bar2', 'bar3', 'bar4', 'bar5' ] } | |
+## | |
+# @PCIELinkSpeed: | |
+# | |
+# An enumeration of PCIe link speeds in units of GT/s | |
+# | |
+# @2_5: 2.5GT/s | |
+# | |
+# @5: 5.0GT/s | |
+# | |
+# @8: 8.0GT/s | |
+# | |
+# @16: 16.0GT/s | |
+# | |
+# Since: 3.2 | |
+## | |
+{ 'enum': 'PCIELinkSpeed', | |
+ 'data': [ '2_5', '5', '8', '16' ] } | |
+ | |
+## | |
+# @PCIELinkWidth: | |
+# | |
+# An enumeration of PCIe link width | |
+# | |
+# @1: x1 | |
+# | |
+# @2: x2 | |
+# | |
+# @4: x4 | |
+# | |
+# @8: x8 | |
+# | |
+# @12: x12 | |
+# | |
+# @16: x16 | |
+# | |
+# @32: x32 | |
+# | |
+# Since: 3.2 | |
+## | |
+{ 'enum': 'PCIELinkWidth', | |
+ 'data': [ '1', '2', '4', '8', '12', '16', '32' ] } | |
+ | |
## | |
# @SysEmuTarget: | |
# | |
--- | |
hw/pci-bridge/pcie_root_port.c | 14 ++++++++++++++ | |
include/hw/pci/pcie_port.h | 4 ++++ | |
2 files changed, 18 insertions(+) | |
diff --git a/hw/pci-bridge/pcie_root_port.c b/hw/pci-bridge/pcie_root_port.c | |
index 45f9e8cd4a36..34ad76743c44 100644 | |
--- a/hw/pci-bridge/pcie_root_port.c | |
+++ b/hw/pci-bridge/pcie_root_port.c | |
@@ -140,6 +140,19 @@ static Property rp_props[] = { | |
DEFINE_PROP_END_OF_LIST() | |
}; | |
+static void rp_instance_post_init(Object *obj) | |
+{ | |
+ PCIESlot *s = PCIE_SLOT(obj); | |
+ | |
+ if (!s->speed) { | |
+ s->speed = QEMU_PCI_EXP_LNK_2_5GT; | |
+ } | |
+ | |
+ if (!s->width) { | |
+ s->width = QEMU_PCI_EXP_LNK_X1; | |
+ } | |
+} | |
+ | |
static void rp_class_init(ObjectClass *klass, void *data) | |
{ | |
DeviceClass *dc = DEVICE_CLASS(klass); | |
@@ -157,6 +170,7 @@ static void rp_class_init(ObjectClass *klass, void *data) | |
static const TypeInfo rp_info = { | |
.name = TYPE_PCIE_ROOT_PORT, | |
.parent = TYPE_PCIE_SLOT, | |
+ .instance_post_init = rp_instance_post_init, | |
.class_init = rp_class_init, | |
.abstract = true, | |
.class_size = sizeof(PCIERootPortClass), | |
diff --git a/include/hw/pci/pcie_port.h b/include/hw/pci/pcie_port.h | |
index 0736014bfdb4..df242a0cafff 100644 | |
--- a/include/hw/pci/pcie_port.h | |
+++ b/include/hw/pci/pcie_port.h | |
@@ -49,6 +49,10 @@ struct PCIESlot { | |
/* pci express switch port with slot */ | |
uint8_t chassis; | |
uint16_t slot; | |
+ | |
+ PCIExpLinkSpeed speed; | |
+ PCIExpLinkWidth width; | |
+ | |
QLIST_ENTRY(PCIESlot) next; | |
}; | |
--- | |
hw/pci/pcie.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | |
1 file changed, 72 insertions(+) | |
diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c | |
index 61b7b96c52cd..556ec19925b9 100644 | |
--- a/hw/pci/pcie.c | |
+++ b/hw/pci/pcie.c | |
@@ -27,6 +27,7 @@ | |
#include "hw/pci/msi.h" | |
#include "hw/pci/pci_bus.h" | |
#include "hw/pci/pcie_regs.h" | |
+#include "hw/pci/pcie_port.h" | |
#include "qemu/range.h" | |
//#define DEBUG_PCIE | |
@@ -87,6 +88,74 @@ pcie_cap_v1_fill(PCIDevice *dev, uint8_t port, uint8_t type, uint8_t version) | |
pci_set_word(cmask + PCI_EXP_LNKSTA, 0); | |
} | |
+static void pcie_cap_fill_slot_lnk(PCIDevice *dev) | |
+{ | |
+ PCIESlot *s = (PCIESlot *)object_dynamic_cast(OBJECT(dev), TYPE_PCIE_SLOT); | |
+ uint8_t *exp_cap = dev->config + dev->exp.exp_cap; | |
+ | |
+ /* Skip anything that isn't a PCIESlot */ | |
+ if (!s) { | |
+ return; | |
+ } | |
+ | |
+ /* Clear and fill LNKCAP from what was configured above */ | |
+ pci_long_test_and_clear_mask(exp_cap + PCI_EXP_LNKCAP, | |
+ PCI_EXP_LNKCAP_MLW | PCI_EXP_LNKCAP_SLS); | |
+ pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKCAP, | |
+ QEMU_PCI_EXP_LNKCAP_MLW(s->width) | | |
+ QEMU_PCI_EXP_LNKCAP_MLS(s->speed)); | |
+ | |
+ /* | |
+ * Link bandwidth notification is required for all root ports and | |
+ * downstream ports supporting links wider than x1. | |
+ */ | |
+ if (s->width > QEMU_PCI_EXP_LNK_X1) { | |
+ pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKCAP, | |
+ PCI_EXP_LNKCAP_LBNC); | |
+ } | |
+ | |
+ if (s->speed > QEMU_PCI_EXP_LNK_2_5GT) { | |
+ /* | |
+ * Hot-plug capable downstream ports and downstream ports supporting | |
+ * link speeds greater than 5GT/s must hardwire PCI_EXP_LNKCAP_DLLLARC | |
+ * to 1b. PCI_EXP_LNKCAP_DLLLARC implies PCI_EXP_LNKSTA_DLLLA, which | |
+ * we also hardwire to 1b here. 2.5GT/s hot-plug slots should also | |
+ * technically implement this, but it's not done here for compatibility. | |
+ */ | |
+ pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKCAP, | |
+ PCI_EXP_LNKCAP_DLLLARC); | |
+ pci_word_test_and_set_mask(exp_cap + PCI_EXP_LNKSTA, | |
+ PCI_EXP_LNKSTA_DLLLA); | |
+ | |
+ /* | |
+ * Target Link Speed defaults to the highest link speed supported by | |
+ * the component. 2.5GT/s devices are permitted to hardwire to zero. | |
+ */ | |
+ pci_word_test_and_clear_mask(exp_cap + PCI_EXP_LNKCTL2, | |
+ PCI_EXP_LNKCTL2_TLS); | |
+ pci_word_test_and_set_mask(exp_cap + PCI_EXP_LNKCTL2, | |
+ QEMU_PCI_EXP_LNKCAP_MLS(s->speed) & | |
+ PCI_EXP_LNKCTL2_TLS); | |
+ } | |
+ | |
+ /* | |
+ * 2.5 & 5.0GT/s can be fully described by LNKCAP, but 8.0GT/s is | |
+ * actually a reference to the highest bit supported in this register. | |
+ * We assume the device supports all link speeds. | |
+ */ | |
+ if (s->speed > QEMU_PCI_EXP_LNK_5GT) { | |
+ pci_long_test_and_clear_mask(exp_cap + PCI_EXP_LNKCAP2, ~0U); | |
+ pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKCAP2, | |
+ PCI_EXP_LNKCAP2_SLS_2_5GB | | |
+ PCI_EXP_LNKCAP2_SLS_5_0GB | | |
+ PCI_EXP_LNKCAP2_SLS_8_0GB); | |
+ if (s->speed > QEMU_PCI_EXP_LNK_8GT) { | |
+ pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKCAP2, | |
+ PCI_EXP_LNKCAP2_SLS_16_0GB); | |
+ } | |
+ } | |
+} | |
+ | |
int pcie_cap_init(PCIDevice *dev, uint8_t offset, | |
uint8_t type, uint8_t port, | |
Error **errp) | |
@@ -108,6 +177,9 @@ int pcie_cap_init(PCIDevice *dev, uint8_t offset, | |
/* Filling values common with v1 */ | |
pcie_cap_v1_fill(dev, port, type, PCI_EXP_FLAGS_VER2); | |
+ /* Fill link speed and width options */ | |
+ pcie_cap_fill_slot_lnk(dev); | |
+ | |
/* Filling v2 specific values */ | |
pci_set_long(exp_cap + PCI_EXP_DEVCAP2, | |
PCI_EXP_DEVCAP2_EFF | PCI_EXP_DEVCAP2_EETLPP); | |
--- | |
hw/pci-bridge/gen_pcie_root_port.c | 2 ++ | |
1 file changed, 2 insertions(+) | |
diff --git a/hw/pci-bridge/gen_pcie_root_port.c b/hw/pci-bridge/gen_pcie_root_port.c | |
index 299de429ec1e..e3bba2ab68ef 100644 | |
--- a/hw/pci-bridge/gen_pcie_root_port.c | |
+++ b/hw/pci-bridge/gen_pcie_root_port.c | |
@@ -123,6 +123,8 @@ static Property gen_rp_props[] = { | |
DEFINE_PROP_SIZE("mem-reserve", GenPCIERootPort, mem_reserve, -1), | |
DEFINE_PROP_SIZE("pref32-reserve", GenPCIERootPort, pref32_reserve, -1), | |
DEFINE_PROP_SIZE("pref64-reserve", GenPCIERootPort, pref64_reserve, -1), | |
+ DEFINE_PROP_PCIE_LINK_SPEED("speed", PCIESlot, speed, PCIE_LINK_SPEED_2_5), | |
+ DEFINE_PROP_PCIE_LINK_WIDTH("width", PCIESlot, width, PCIE_LINK_WIDTH_1), | |
DEFINE_PROP_END_OF_LIST() | |
}; | |
--- | |
hw/vfio/pci.c | 6 ------ | |
1 file changed, 6 deletions(-) | |
diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c | |
index 08aab328b442..0d1dd4666fd1 100644 | |
--- a/hw/vfio/pci.c | |
+++ b/hw/vfio/pci.c | |
@@ -1899,12 +1899,6 @@ static int vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size, | |
QEMU_PCI_EXP_LNKCAP_MLS(QEMU_PCI_EXP_LNK_2_5GT), ~0); | |
vfio_add_emulated_word(vdev, pos + PCI_EXP_LNKCTL, 0, ~0); | |
} | |
- | |
- /* Mark the Link Status bits as emulated to allow virtual negotiation */ | |
- vfio_add_emulated_word(vdev, pos + PCI_EXP_LNKSTA, | |
- pci_get_word(vdev->pdev.config + pos + | |
- PCI_EXP_LNKSTA), | |
- PCI_EXP_LNKCAP_MLW | PCI_EXP_LNKCAP_SLS); | |
} | |
/* |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment