@@ -126,6 +126,8 @@ void pci_bridge_dev_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
Error **errp);
void pci_bridge_dev_unplug_request_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp);
+HotplugInfo *pci_bridge_dev_get_hotplug_state(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp);
/*
* before qdev initialization(qdev_init()), this function sets bus_name and
@@ -51,6 +51,8 @@ void shpc_device_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
Error **errp);
void shpc_device_unplug_request_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp);
+HotplugInfo *shpc_get_hotplug_state(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp);
extern VMStateInfo shpc_vmstate_info;
#define SHPC_VMSTATE(_field, _type, _test) \
@@ -41,4 +41,6 @@ const char *qdev_set_id(DeviceState *dev, char *id, Error **errp);
void qdev_hotplug_state_event(DeviceState *bus, const char *addr,
DeviceState *child, HotplugState *changed_state);
+DeviceAndPath *qdev_new_device_and_path(DeviceState *dev);
+
#endif
@@ -241,6 +241,19 @@ void pci_bridge_dev_unplug_request_cb(HotplugHandler *hotplug_dev,
shpc_device_unplug_request_cb(hotplug_dev, dev, errp);
}
+HotplugInfo *pci_bridge_dev_get_hotplug_state(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", object_get_typename(OBJECT(hotplug_dev)));
+ return NULL;
+ }
+ return shpc_get_hotplug_state(hotplug_dev, dev, errp);
+}
+
static void pci_bridge_dev_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@@ -261,6 +274,7 @@ static void pci_bridge_dev_class_init(ObjectClass *klass, void *data)
hc->plug = pci_bridge_dev_plug_cb;
hc->unplug = pci_bridge_dev_unplug_cb;
hc->unplug_request = pci_bridge_dev_unplug_request_cb;
+ hc->get_hotplug_state = pci_bridge_dev_get_hotplug_state;
}
static const TypeInfo pci_bridge_dev_info = {
@@ -157,6 +157,7 @@ static void pcie_pci_bridge_class_init(ObjectClass *klass, void *data)
hc->plug = pci_bridge_dev_plug_cb;
hc->unplug = pci_bridge_dev_unplug_cb;
hc->unplug_request = pci_bridge_dev_unplug_request_cb;
+ hc->get_hotplug_state = pci_bridge_dev_get_hotplug_state;
}
static const TypeInfo pcie_pci_bridge_info = {
@@ -8,6 +8,9 @@
#include "hw/pci/pci.h"
#include "hw/pci/pci_bus.h"
#include "hw/pci/msi.h"
+#include "qapi/qapi-types-qdev.h"
+#include "qapi/qapi-events-qdev.h"
+#include "monitor/qdev.h"
/* TODO: model power only and disabled slot states. */
/* TODO: handle SERR and wakeups */
@@ -123,6 +126,39 @@
#define SHPC_PCI_TO_IDX(pci_slot) ((pci_slot) - 1)
#define SHPC_IDX_TO_PHYSICAL(slot) ((slot) + 1)
+static char *shpc_idx_to_pci_addr(int slot)
+{
+ return g_strdup_printf("%d", SHPC_IDX_TO_PCI(slot));
+}
+
+static LedActivity shpc_led_state_to_qapi(uint8_t value)
+{
+ switch (value) {
+ case SHPC_LED_ON:
+ return LED_ACTIVITY_ON;
+ case SHPC_LED_BLINK:
+ return LED_ACTIVITY_BLINK;
+ case SHPC_LED_OFF:
+ return LED_ACTIVITY_OFF;
+ default:
+ abort();
+ }
+}
+
+static HotplugSHPCSlotState shpc_slot_state_to_qapi(uint8_t value)
+{
+ switch (value) {
+ case SHPC_STATE_PWRONLY:
+ return HOTPLUGSHPC_SLOT_STATE_POWER_ONLY;
+ case SHPC_STATE_ENABLED:
+ return HOTPLUGSHPC_SLOT_STATE_ENABLED;
+ case SHPC_STATE_DISABLED:
+ return HOTPLUGSHPC_SLOT_STATE_DISABLED;
+ default:
+ abort();
+ }
+}
+
static uint8_t shpc_get_status(SHPCDevice *shpc, int slot, uint16_t msk)
{
uint8_t *status = shpc->config + SHPC_SLOT_STATUS(slot);
@@ -268,9 +304,16 @@ static void shpc_slot_command(PCIDevice *d, uint8_t target,
{
SHPCDevice *shpc = d->shpc;
int slot = SHPC_LOGICAL_TO_IDX(target);
+ int pci_slot = SHPC_IDX_TO_PCI(slot);
uint8_t old_state = shpc_get_status(shpc, slot, SHPC_SLOT_STATE_MASK);
uint8_t old_power = shpc_get_status(shpc, slot, SHPC_SLOT_PWR_LED_MASK);
uint8_t old_attn = shpc_get_status(shpc, slot, SHPC_SLOT_ATTN_LED_MASK);
+ HotplugState changed_state = {
+ .type = HOTPLUG_TYPE_SHPC,
+ };
+ HotplugSHPCState *cs = &changed_state.u.shpc;
+ DeviceState *child_dev =
+ DEVICE(shpc->sec_bus->devices[PCI_DEVFN(pci_slot, 0)]);
if (target < SHPC_CMD_TRGT_MIN || slot >= shpc->nslots) {
shpc_invalid_command(shpc);
@@ -284,24 +327,34 @@ static void shpc_slot_command(PCIDevice *d, uint8_t target,
if (power == SHPC_LED_NO) {
power = old_power;
- } else {
- /* TODO: send event to monitor */
+ } else if (power != old_power) {
+ cs->has_power_led = true;
+ cs->power_led = shpc_led_state_to_qapi(power);
shpc_set_status(shpc, slot, power, SHPC_SLOT_PWR_LED_MASK);
}
if (attn == SHPC_LED_NO) {
attn = old_attn;
- } else {
- /* TODO: send event to monitor */
+ } else if (attn != old_attn) {
+ cs->has_attention_led = true;
+ cs->attention_led = shpc_led_state_to_qapi(attn);
shpc_set_status(shpc, slot, attn, SHPC_SLOT_ATTN_LED_MASK);
}
if (state == SHPC_STATE_NO) {
state = old_state;
- } else {
+ } else if (state != old_state) {
+ cs->has_slot_state = true;
+ cs->slot_state = shpc_slot_state_to_qapi(state);
shpc_set_status(shpc, slot, state, SHPC_SLOT_STATE_MASK);
}
+ if (cs->has_power_led || cs->has_attention_led || cs->has_slot_state) {
+ g_autofree char *addr = shpc_idx_to_pci_addr(slot);
+
+ qdev_hotplug_state_event(DEVICE(d), addr, child_dev, &changed_state);
+ }
+
if (!shpc_slot_is_off(old_state, old_power, old_attn) &&
shpc_slot_is_off(state, power, attn))
{
@@ -713,6 +766,44 @@ void shpc_cap_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l)
shpc_cap_update_dword(d);
}
+HotplugInfo *shpc_get_hotplug_state(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev);
+ SHPCDevice *shpc = pci_hotplug_dev->shpc;
+ int slot;
+ uint8_t state, power, attn;
+ HotplugInfo *res;
+
+ if (!shpc_device_get_slot(PCI_DEVICE(dev), &slot, shpc, errp)) {
+ return NULL;
+ }
+
+ state = shpc_get_status(shpc, slot, SHPC_SLOT_STATE_MASK);
+ power = shpc_get_status(shpc, slot, SHPC_SLOT_PWR_LED_MASK);
+ attn = shpc_get_status(shpc, slot, SHPC_SLOT_ATTN_LED_MASK);
+
+ res = g_new(HotplugInfo, 1);
+ *res = (HotplugInfo) {
+ .bus = qdev_new_device_and_path(DEVICE(pci_hotplug_dev)),
+ .addr = shpc_idx_to_pci_addr(slot),
+ .child = qdev_new_device_and_path(dev),
+ .state = g_new(HotplugState, 1),
+ };
+
+ *res->state = (HotplugState) {
+ .type = HOTPLUG_TYPE_SHPC,
+ .u.shpc.has_power_led = true,
+ .u.shpc.power_led = shpc_led_state_to_qapi(power),
+ .u.shpc.has_attention_led = true,
+ .u.shpc.attention_led = shpc_led_state_to_qapi(attn),
+ .u.shpc.has_slot_state = true,
+ .u.shpc.slot_state = shpc_slot_state_to_qapi(state),
+ };
+
+ return res;
+}
+
static int shpc_save(QEMUFile *f, void *pv, size_t size,
const VMStateField *field, JSONWriter *vmdesc)
{
@@ -1178,6 +1178,23 @@ bool qmp_command_available(const QmpCommand *cmd, Error **errp)
return true;
}
+DeviceAndPath *qdev_new_device_and_path(DeviceState *dev)
+{
+ DeviceAndPath *res;
+
+ if (!dev) {
+ return NULL;
+ }
+
+ res = g_new(DeviceAndPath, 1);
+ *res = (DeviceAndPath) {
+ .device = g_strdup(dev->id),
+ .path = g_strdup(dev->canonical_path),
+ };
+
+ return res;
+}
+
void qdev_hotplug_state_event(DeviceState *bus, const char *addr,
DeviceState *child, HotplugState *changed_state)
{
For PCIe and SHPC hotplug it's important to track led indicators, especially the power led. At this step, implement the prepared infrastructure in SHPC. Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> --- include/hw/pci/pci_bridge.h | 2 + include/hw/pci/shpc.h | 2 + include/monitor/qdev.h | 2 + hw/pci-bridge/pci_bridge_dev.c | 14 +++++ hw/pci-bridge/pcie_pci_bridge.c | 1 + hw/pci/shpc.c | 101 ++++++++++++++++++++++++++++++-- softmmu/qdev-monitor.c | 17 ++++++ 7 files changed, 134 insertions(+), 5 deletions(-)