summaryrefslogtreecommitdiffstats
path: root/hw/pci
diff options
context:
space:
mode:
Diffstat (limited to 'hw/pci')
-rw-r--r--hw/pci/msix.c4
-rw-r--r--hw/pci/pci.c87
-rw-r--r--hw/pci/pci_bridge.c4
-rw-r--r--hw/pci/pci_host.c26
-rw-r--r--hw/pci/pcie.c178
-rw-r--r--hw/pci/pcie_port.c6
-rw-r--r--hw/pci/shpc.c25
7 files changed, 275 insertions, 55 deletions
diff --git a/hw/pci/msix.c b/hw/pci/msix.c
index 702dac4ec7..4e336416a7 100644
--- a/hw/pci/msix.c
+++ b/hw/pci/msix.c
@@ -345,7 +345,7 @@ int msix_init_exclusive_bar(PCIDevice *dev, unsigned short nentries,
char *name;
uint32_t bar_size = 4096;
uint32_t bar_pba_offset = bar_size / 2;
- uint32_t bar_pba_size = (nentries / 8 + 1) * 8;
+ uint32_t bar_pba_size = QEMU_ALIGN_UP(nentries, 64) / 8;
/*
* Migration compatibility dictates that this remains a 4k
@@ -501,7 +501,7 @@ void msix_reset(PCIDevice *dev)
}
msix_clear_all_vectors(dev);
dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] &=
- ~dev->wmask[dev->msix_cap + MSIX_CONTROL_OFFSET];
+ ~dev->wmask[dev->msix_cap + MSIX_CONTROL_OFFSET];
memset(dev->msix_table, 0, dev->msix_entries_nr * PCI_MSIX_ENTRY_SIZE);
memset(dev->msix_pba, 0, QEMU_ALIGN_UP(dev->msix_entries_nr, 64) / 8);
msix_mask_all(dev, dev->msix_entries_nr);
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index 56b13b3320..c9fc2fbe19 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -211,13 +211,13 @@ int pci_bar(PCIDevice *d, int reg)
static inline int pci_irq_state(PCIDevice *d, int irq_num)
{
- return (d->irq_state >> irq_num) & 0x1;
+ return (d->irq_state >> irq_num) & 0x1;
}
static inline void pci_set_irq_state(PCIDevice *d, int irq_num, int level)
{
- d->irq_state &= ~(0x1 << irq_num);
- d->irq_state |= level << irq_num;
+ d->irq_state &= ~(0x1 << irq_num);
+ d->irq_state |= level << irq_num;
}
static void pci_change_irq_level(PCIDevice *pci_dev, int irq_num, int change)
@@ -333,6 +333,13 @@ static void pci_host_bus_register(DeviceState *host)
QLIST_INSERT_HEAD(&pci_host_bridges, host_bridge, next);
}
+static void pci_host_bus_unregister(DeviceState *host)
+{
+ PCIHostState *host_bridge = PCI_HOST_BRIDGE(host);
+
+ QLIST_REMOVE(host_bridge, next);
+}
+
PCIBus *pci_device_root_bus(const PCIDevice *d)
{
PCIBus *bus = pci_get_bus(d);
@@ -379,6 +386,11 @@ static void pci_root_bus_init(PCIBus *bus, DeviceState *parent,
pci_host_bus_register(parent);
}
+static void pci_bus_uninit(PCIBus *bus)
+{
+ pci_host_bus_unregister(BUS(bus)->parent);
+}
+
bool pci_bus_is_express(PCIBus *bus)
{
return object_dynamic_cast(OBJECT(bus), TYPE_PCIE_BUS);
@@ -413,6 +425,12 @@ PCIBus *pci_root_bus_new(DeviceState *parent, const char *name,
return bus;
}
+void pci_root_bus_cleanup(PCIBus *bus)
+{
+ pci_bus_uninit(bus);
+ object_unparent(OBJECT(bus));
+}
+
void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq,
void *irq_opaque, int nirq)
{
@@ -423,6 +441,15 @@ void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq,
bus->irq_count = g_malloc0(nirq * sizeof(bus->irq_count[0]));
}
+void pci_bus_irqs_cleanup(PCIBus *bus)
+{
+ bus->set_irq = NULL;
+ bus->map_irq = NULL;
+ bus->irq_opaque = NULL;
+ bus->nirq = 0;
+ g_free(bus->irq_count);
+}
+
PCIBus *pci_register_root_bus(DeviceState *parent, const char *name,
pci_set_irq_fn set_irq, pci_map_irq_fn map_irq,
void *irq_opaque,
@@ -439,6 +466,12 @@ PCIBus *pci_register_root_bus(DeviceState *parent, const char *name,
return bus;
}
+void pci_unregister_root_bus(PCIBus *bus)
+{
+ pci_bus_irqs_cleanup(bus);
+ pci_root_bus_cleanup(bus);
+}
+
int pci_bus_num(PCIBus *s)
{
return PCI_BUS_GET_CLASS(s)->bus_num(s);
@@ -571,8 +604,8 @@ const VMStateDescription vmstate_pci_device = {
0, vmstate_info_pci_config,
PCIE_CONFIG_SPACE_SIZE),
VMSTATE_BUFFER_UNSAFE_INFO(irq_state, PCIDevice, 2,
- vmstate_info_pci_irq_state,
- PCI_NUM_PINS * sizeof(int32_t)),
+ vmstate_info_pci_irq_state,
+ PCI_NUM_PINS * sizeof(int32_t)),
VMSTATE_END_OF_LIST()
}
};
@@ -624,21 +657,21 @@ static int pci_parse_devaddr(const char *addr, int *domp, int *busp,
p = addr;
val = strtoul(p, &e, 16);
if (e == p)
- return -1;
+ return -1;
if (*e == ':') {
- bus = val;
- p = e + 1;
- val = strtoul(p, &e, 16);
- if (e == p)
- return -1;
- if (*e == ':') {
- dom = bus;
- bus = val;
- p = e + 1;
- val = strtoul(p, &e, 16);
- if (e == p)
- return -1;
- }
+ bus = val;
+ p = e + 1;
+ val = strtoul(p, &e, 16);
+ if (e == p)
+ return -1;
+ if (*e == ':') {
+ dom = bus;
+ bus = val;
+ p = e + 1;
+ val = strtoul(p, &e, 16);
+ if (e == p)
+ return -1;
+ }
}
slot = val;
@@ -657,10 +690,10 @@ static int pci_parse_devaddr(const char *addr, int *domp, int *busp,
/* if funcp == NULL func is 0 */
if (dom > 0xffff || bus > 0xff || slot > 0x1f || func > 7)
- return -1;
+ return -1;
if (*e)
- return -1;
+ return -1;
*domp = dom;
*busp = bus;
@@ -1217,7 +1250,7 @@ pcibus_t pci_get_bar_addr(PCIDevice *pci_dev, int region_num)
}
static pcibus_t pci_bar_address(PCIDevice *d,
- int reg, uint8_t type, pcibus_t size)
+ int reg, uint8_t type, pcibus_t size)
{
pcibus_t new_addr, last_addr;
int bar = pci_bar(d, reg);
@@ -1353,6 +1386,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);
}
@@ -2261,7 +2298,11 @@ static void pci_add_option_rom(PCIDevice *pdev, bool is_default_rom,
pdev->has_rom = true;
memory_region_init_rom(&pdev->rom, OBJECT(pdev), name, size, &error_fatal);
ptr = memory_region_get_ram_ptr(&pdev->rom);
- load_image(path, ptr);
+ if (load_image_size(path, ptr, size) < 0) {
+ error_setg(errp, "failed to load romfile \"%s\"", pdev->romfile);
+ g_free(path);
+ return;
+ }
g_free(path);
if (is_default_rom) {
diff --git a/hw/pci/pci_bridge.c b/hw/pci/pci_bridge.c
index ee9dff2d3a..c6d9ded320 100644
--- a/hw/pci/pci_bridge.c
+++ b/hw/pci/pci_bridge.c
@@ -241,9 +241,9 @@ void pci_bridge_update_mappings(PCIBridge *br)
* while another accesses an unaffected region. */
memory_region_transaction_begin();
pci_bridge_region_del(br, br->windows);
+ pci_bridge_region_cleanup(br, w);
br->windows = pci_bridge_region_init(br);
memory_region_transaction_commit();
- pci_bridge_region_cleanup(br, w);
}
/* default write_config function for PCI-to-PCI bridge */
@@ -369,7 +369,7 @@ void pci_bridge_initfn(PCIDevice *dev, const char *typename)
* let users address the bus using the device name.
*/
if (!br->bus_name && dev->qdev.id && *dev->qdev.id) {
- br->bus_name = dev->qdev.id;
+ br->bus_name = dev->qdev.id;
}
qbus_create_inplace(sec_bus, sizeof(br->sec_bus), typename, DEVICE(dev),
diff --git a/hw/pci/pci_host.c b/hw/pci/pci_host.c
index 5eaa935cb5..5f5345dbac 100644
--- a/hw/pci/pci_host.c
+++ b/hw/pci/pci_host.c
@@ -20,6 +20,7 @@
#include "qemu/osdep.h"
#include "hw/pci/pci.h"
+#include "hw/pci/pci_bridge.h"
#include "hw/pci/pci_host.h"
#include "hw/pci/pci_bus.h"
#include "trace.h"
@@ -50,9 +51,29 @@ static inline PCIDevice *pci_dev_find_by_addr(PCIBus *bus, uint32_t addr)
return pci_find_device(bus, bus_num, devfn);
}
+static void pci_adjust_config_limit(PCIBus *bus, uint32_t *limit)
+{
+ if (*limit > PCI_CONFIG_SPACE_SIZE) {
+ if (!pci_bus_is_express(bus)) {
+ *limit = PCI_CONFIG_SPACE_SIZE;
+ return;
+ }
+
+ if (!pci_bus_is_root(bus)) {
+ PCIDevice *bridge = pci_bridge_get_device(bus);
+ pci_adjust_config_limit(pci_get_bus(bridge), limit);
+ }
+ }
+}
+
void pci_host_config_write_common(PCIDevice *pci_dev, uint32_t addr,
uint32_t limit, uint32_t val, uint32_t len)
{
+ pci_adjust_config_limit(pci_get_bus(pci_dev), &limit);
+ if (limit <= addr) {
+ return;
+ }
+
assert(len <= 4);
/* non-zero functions are only exposed when function 0 is present,
* allowing direct removal of unexposed functions.
@@ -71,6 +92,11 @@ uint32_t pci_host_config_read_common(PCIDevice *pci_dev, uint32_t addr,
{
uint32_t ret;
+ pci_adjust_config_limit(pci_get_bus(pci_dev), &limit);
+ if (limit <= addr) {
+ return ~0x0;
+ }
+
assert(len <= 4);
/* non-zero functions are only exposed when function 0 is present,
* allowing direct removal of unexposed functions.
diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c
index 6c91bd44a0..230478faab 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
@@ -68,11 +69,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,
@@ -86,6 +88,76 @@ 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 or multiple link
+ * speeds.
+ */
+ if (s->width > QEMU_PCI_EXP_LNK_X1 ||
+ s->speed > QEMU_PCI_EXP_LNK_2_5GT) {
+ 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)
@@ -107,6 +179,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);
@@ -315,12 +390,11 @@ static void pcie_cap_slot_event(PCIDevice *dev, PCIExpressHotPlugEvent event)
hotplug_event_notify(dev);
}
-static void pcie_cap_slot_hotplug_common(PCIDevice *hotplug_dev,
- DeviceState *dev,
- uint8_t **exp_cap, Error **errp)
+static void pcie_cap_slot_plug_common(PCIDevice *hotplug_dev, DeviceState *dev,
+ Error **errp)
{
- *exp_cap = hotplug_dev->config + hotplug_dev->exp.exp_cap;
- uint16_t sltsta = pci_get_word(*exp_cap + PCI_EXP_SLTSTA);
+ uint8_t *exp_cap = hotplug_dev->config + hotplug_dev->exp.exp_cap;
+ uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA);
PCIE_DEV_PRINTF(PCI_DEVICE(dev), "hotplug state: 0x%x\n", sltsta);
if (sltsta & PCI_EXP_SLTSTA_EIS) {
@@ -331,13 +405,18 @@ static void pcie_cap_slot_hotplug_common(PCIDevice *hotplug_dev,
}
}
-void pcie_cap_slot_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
- Error **errp)
+void pcie_cap_slot_pre_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
+ Error **errp)
{
- uint8_t *exp_cap;
- PCIDevice *pci_dev = PCI_DEVICE(dev);
+ pcie_cap_slot_plug_common(PCI_DEVICE(hotplug_dev), dev, errp);
+}
- pcie_cap_slot_hotplug_common(PCI_DEVICE(hotplug_dev), dev, &exp_cap, errp);
+void pcie_cap_slot_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
+ Error **errp)
+{
+ PCIDevice *hotplug_pdev = PCI_DEVICE(hotplug_dev);
+ uint8_t *exp_cap = hotplug_pdev->config + hotplug_pdev->exp.exp_cap;
+ PCIDevice *pci_dev = PCI_DEVICE(dev);
/* Don't send event when device is enabled during qemu machine creation:
* it is present on boot, no hotplug event is necessary. We do send an
@@ -345,6 +424,10 @@ void pcie_cap_slot_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
if (!dev->hotplugged) {
pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA,
PCI_EXP_SLTSTA_PDS);
+ if (pci_dev->cap_present & QEMU_PCIE_LNKSTA_DLLLA) {
+ pci_word_test_and_set_mask(exp_cap + PCI_EXP_LNKSTA,
+ PCI_EXP_LNKSTA_DLLLA);
+ }
return;
}
@@ -355,24 +438,40 @@ void pcie_cap_slot_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
if (pci_get_function_0(pci_dev)) {
pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA,
PCI_EXP_SLTSTA_PDS);
+ if (pci_dev->cap_present & QEMU_PCIE_LNKSTA_DLLLA) {
+ pci_word_test_and_set_mask(exp_cap + PCI_EXP_LNKSTA,
+ PCI_EXP_LNKSTA_DLLLA);
+ }
pcie_cap_slot_event(PCI_DEVICE(hotplug_dev),
PCI_EXP_HP_EV_PDC | PCI_EXP_HP_EV_ABP);
}
}
-static void pcie_unplug_device(PCIBus *bus, PCIDevice *dev, void *opaque)
+void pcie_cap_slot_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
+ Error **errp)
{
object_unparent(OBJECT(dev));
}
-void pcie_cap_slot_hot_unplug_request_cb(HotplugHandler *hotplug_dev,
- DeviceState *dev, Error **errp)
+static void pcie_unplug_device(PCIBus *bus, PCIDevice *dev, void *opaque)
{
- uint8_t *exp_cap;
+ HotplugHandler *hotplug_ctrl = qdev_get_hotplug_handler(DEVICE(dev));
+
+ hotplug_handler_unplug(hotplug_ctrl, DEVICE(dev), &error_abort);
+}
+
+void pcie_cap_slot_unplug_request_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ Error *local_err = NULL;
PCIDevice *pci_dev = PCI_DEVICE(dev);
PCIBus *bus = pci_get_bus(pci_dev);
- pcie_cap_slot_hotplug_common(PCI_DEVICE(hotplug_dev), dev, &exp_cap, errp);
+ pcie_cap_slot_plug_common(PCI_DEVICE(hotplug_dev), dev, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
/* In case user cancel the operation of multi-function hot-add,
* remove the function that is unexposed to guest individually,
@@ -531,6 +630,10 @@ void pcie_cap_slot_write_config(PCIDevice *dev,
pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTSTA,
PCI_EXP_SLTSTA_PDS);
+ if (dev->cap_present & QEMU_PCIE_LNKSTA_DLLLA) {
+ pci_word_test_and_clear_mask(exp_cap + PCI_EXP_LNKSTA,
+ PCI_EXP_LNKSTA_DLLLA);
+ }
pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA,
PCI_EXP_SLTSTA_PDC);
}
@@ -728,6 +831,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/hw/pci/pcie_port.c b/hw/pci/pcie_port.c
index 6432b9ac1f..a30291ef54 100644
--- a/hw/pci/pcie_port.c
+++ b/hw/pci/pcie_port.c
@@ -154,8 +154,10 @@ static void pcie_slot_class_init(ObjectClass *oc, void *data)
HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc);
dc->props = pcie_slot_props;
- hc->plug = pcie_cap_slot_hotplug_cb;
- hc->unplug_request = pcie_cap_slot_hot_unplug_request_cb;
+ hc->pre_plug = pcie_cap_slot_pre_plug_cb;
+ hc->plug = pcie_cap_slot_plug_cb;
+ hc->unplug = pcie_cap_slot_unplug_cb;
+ hc->unplug_request = pcie_cap_slot_unplug_request_cb;
}
static const TypeInfo pcie_slot_type_info = {
diff --git a/hw/pci/shpc.c b/hw/pci/shpc.c
index 96a43d2f70..45053b39b9 100644
--- a/hw/pci/shpc.c
+++ b/hw/pci/shpc.c
@@ -238,6 +238,7 @@ static void shpc_invalid_command(SHPCDevice *shpc)
static void shpc_free_devices_in_slot(SHPCDevice *shpc, int slot)
{
+ HotplugHandler *hotplug_ctrl;
int devfn;
int pci_slot = SHPC_IDX_TO_PCI(slot);
for (devfn = PCI_DEVFN(pci_slot, 0);
@@ -245,7 +246,9 @@ static void shpc_free_devices_in_slot(SHPCDevice *shpc, int slot)
++devfn) {
PCIDevice *affected_dev = shpc->sec_bus->devices[devfn];
if (affected_dev) {
- object_unparent(OBJECT(affected_dev));
+ hotplug_ctrl = qdev_get_hotplug_handler(DEVICE(affected_dev));
+ hotplug_handler_unplug(hotplug_ctrl, DEVICE(affected_dev),
+ &error_abort);
}
}
}
@@ -482,8 +485,8 @@ static const MemoryRegionOps shpc_mmio_ops = {
.max_access_size = 4,
},
};
-static void shpc_device_hotplug_common(PCIDevice *affected_dev, int *slot,
- SHPCDevice *shpc, Error **errp)
+static void shpc_device_plug_common(PCIDevice *affected_dev, int *slot,
+ SHPCDevice *shpc, Error **errp)
{
int pci_slot = PCI_SLOT(affected_dev->devfn);
*slot = SHPC_PCI_TO_IDX(pci_slot);
@@ -497,7 +500,7 @@ static void shpc_device_hotplug_common(PCIDevice *affected_dev, int *slot,
}
}
-void shpc_device_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
+void shpc_device_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
Error **errp)
{
Error *local_err = NULL;
@@ -505,7 +508,7 @@ void shpc_device_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
SHPCDevice *shpc = pci_hotplug_dev->shpc;
int slot;
- shpc_device_hotplug_common(PCI_DEVICE(dev), &slot, shpc, &local_err);
+ shpc_device_plug_common(PCI_DEVICE(dev), &slot, shpc, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
@@ -540,8 +543,14 @@ void shpc_device_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
shpc_interrupt_update(pci_hotplug_dev);
}
-void shpc_device_hot_unplug_request_cb(HotplugHandler *hotplug_dev,
- DeviceState *dev, Error **errp)
+void shpc_device_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
+ Error **errp)
+{
+ object_unparent(OBJECT(dev));
+}
+
+void shpc_device_unplug_request_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
{
Error *local_err = NULL;
PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev);
@@ -550,7 +559,7 @@ void shpc_device_hot_unplug_request_cb(HotplugHandler *hotplug_dev,
uint8_t led;
int slot;
- shpc_device_hotplug_common(PCI_DEVICE(dev), &slot, shpc, &local_err);
+ shpc_device_plug_common(PCI_DEVICE(dev), &slot, shpc, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;