On Tue, Dec 23, 2014 at 06:30:16AM -0600, Michael Roth wrote: > This device emulates a firmware abstraction used by pSeries guests to > manage hotplug/dynamic-reconfiguration of host-bridges, PCI devices, > memory, and CPUs. It is conceptually similar to an SHPC device, > complete with LED indicators to identify individual slots to physical > physical users and indicate when it is safe to remove a device. In > some cases it is also used to manage virtualized resources, such a > memory, CPUs, and physical-host bridges, which in the case of pSeries > guests are virtualized resources where the physical components are > managed by the host. > > Guests communicate with these DR Connectors using RTAS calls, > generally by addressing the unique DRC index associated with a > particular connector for a particular resource. For introspection > purposes we expose this state initially as QOM properties, and > in subsequent patches will introduce the RTAS calls that make use of > it. This constitutes to the 'guest' interface. > > On the QEMU side we provide an attach/detach interface to associate > or cleanup a DeviceState with a particular sPAPRDRConnector in > response to hotplug/unplug, respectively. This constitutes the > 'physical' interface to the DR Connector. > > Signed-off-by: Michael Roth <mdr...@linux.vnet.ibm.com> > --- > hw/ppc/Makefile.objs | 2 +- > hw/ppc/spapr_drc.c | 503 > +++++++++++++++++++++++++++++++++++++++++++++ > include/hw/ppc/spapr_drc.h | 201 ++++++++++++++++++ > 3 files changed, 705 insertions(+), 1 deletion(-) > create mode 100644 hw/ppc/spapr_drc.c > create mode 100644 include/hw/ppc/spapr_drc.h > > diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs > index 19d9920..ea010fd 100644 > --- a/hw/ppc/Makefile.objs > +++ b/hw/ppc/Makefile.objs > @@ -3,7 +3,7 @@ obj-y += ppc.o ppc_booke.o > # IBM pSeries (sPAPR) > obj-$(CONFIG_PSERIES) += spapr.o spapr_vio.o spapr_events.o > obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o > -obj-$(CONFIG_PSERIES) += spapr_pci.o > +obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_drc.o > ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy) > obj-y += spapr_pci_vfio.o > endif > diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c > new file mode 100644 > index 0000000..f81c6d1 > --- /dev/null > +++ b/hw/ppc/spapr_drc.c > @@ -0,0 +1,503 @@ > +/* > + * QEMU SPAPR Dynamic Reconfiguration Connector Implementation > + * > + * Copyright IBM Corp. 2014 > + * > + * Authors: > + * Michael Roth <mdr...@linux.vnet.ibm.com> > + * > + * This work is licensed under the terms of the GNU GPL, version 2 or later. > + * See the COPYING file in the top-level directory. > + */ > + > +#include "hw/ppc/spapr_drc.h" > +#include "qom/object.h" > +#include "hw/qdev.h" > +#include "qapi/visitor.h" > +#include "qemu/error-report.h" > + > +/* #define DEBUG_SPAPR_DRC */ > + > +#ifdef DEBUG_SPAPR_DRC > +#define DPRINTF(fmt, ...) \ > + do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) > +#define DPRINTFN(fmt, ...) \ > + do { DPRINTF(fmt, ## __VA_ARGS__); fprintf(stderr, "\n"); } while (0) > +#else > +#define DPRINTF(fmt, ...) \ > + do { } while (0) > +#define DPRINTFN(fmt, ...) \ > + do { } while (0) > +#endif > + > +#define DRC_CONTAINER_PATH "/dr-connector" > +#define DRC_INDEX_TYPE_SHIFT 28 > +#define DRC_INDEX_ID_MASK ~(~0 << DRC_INDEX_TYPE_SHIFT)
I'm not sure if there are actually any situations where it can break, but safest to put parens around the whole macro body, just in case of macro vs. precedence weirdness. > +static int set_isolation_state(sPAPRDRConnector *drc, > + sPAPRDRIsolationState state) > +{ > + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); > + > + DPRINTFN("set_isolation_state: %x", state); > + drc->isolation_state = state; > + if (drc->awaiting_release && > + drc->isolation_state == SPAPR_DR_ISOLATION_STATE_ISOLATED) { > + drck->detach(drc, DEVICE(drc->dev), drc->detach_cb, > + drc->detach_cb_opaque); > + } > + return 0; > +} > + > +static int set_indicator_state(sPAPRDRConnector *drc, > + sPAPRDRIndicatorState state) > +{ > + DPRINTFN("set_indicator_state: %x", state); > + drc->indicator_state = state; > + return 0; > +} > + > +static int set_allocation_state(sPAPRDRConnector *drc, > + sPAPRDRAllocationState state) > +{ > + DPRINTFN("set_allocation_state: %x", state); > + drc->indicator_state = state; This should be drc->allocation_state, surely. > + return 0; > +} > + > +static uint32_t get_id(sPAPRDRConnector *drc) > +{ > + /* this value is unique for DRCs of a particular type, but may > + * overlap with the id's of other DRCs. the value is used both > + * to calculate a unique (across all DRC types) index, as well > + * as generating the ibm,drc-names OFDT property that describes > + * DRCs > + */ > + return drc->id; > +} Since this is static anyway, why not just reference drc->id directly? > +static sPAPRDRConnectorTypeShift get_type_shift(sPAPRDRConnectorType type) > +{ > + uint32_t shift = 0; > + > + g_assert(type != SPAPR_DR_CONNECTOR_TYPE_ANY); > + while (type != (1 << shift)) { > + shift++; > + } > + return shift; > +} > + > +static uint32_t get_index(sPAPRDRConnector *drc) > +{ > + /* no set format for a drc index: it only needs to be globally > + * unique. this is how we encode the DRC type on bare-metal > + * however, so might as well do that here > + */ > + return (get_type_shift(drc->type) << DRC_INDEX_TYPE_SHIFT) | > + (drc->id & DRC_INDEX_ID_MASK); > +} > + > +static uint32_t get_type(sPAPRDRConnector *drc) > +{ > + return drc->type; > +} > + > +/* > + * dr-entity-sense sensor value > + * returned via get-sensor-state RTAS calls > + * as expected by state diagram in PAPR+ 2.7, 13.4 > + * based on the current allocation/indicator/power states > + * for the DR connector. > + */ > +static sPAPRDREntitySense entity_sense(sPAPRDRConnector *drc) > +{ > + if (drc->dev) { > + /* this assumes all PCI devices are assigned to > + * a 'live insertion' power domain, where QEMU > + * manages power state automatically as opposed > + * to the guest. present, non-PCI resources are > + * unaffected by power state. > + */ Is it possible to make an assert() to check that? > + return SPAPR_DR_ENTITY_SENSE_PRESENT; > + } > + > + if (drc->type == SPAPR_DR_CONNECTOR_TYPE_PCI) { > + /* PCI devices, and only PCI devices, use PRESENT > + * in cases where we'd otherwise use UNUSABLE > + */ > + return SPAPR_DR_ENTITY_SENSE_EMPTY; > + } > + return SPAPR_DR_ENTITY_SENSE_UNUSABLE; > +} > + > +static sPAPRDRCCResponse configure_connector_common(sPAPRDRCCState *ccs, > + char **prop_name, const struct fdt_property > **prop, Maybe rename prop_name to name, since it's also used for node names. > + int *prop_len) > +{ > + sPAPRDRCCResponse resp = SPAPR_DR_CC_RESPONSE_CONTINUE; > + int fdt_offset_next; > + > + *prop_name = NULL; > + *prop = NULL; > + *prop_len = 0; > + > + if (!ccs->fdt) { > + return SPAPR_DR_CC_RESPONSE_ERROR; > + } > + > + while (resp == SPAPR_DR_CC_RESPONSE_CONTINUE) { > + const char *name_cur; > + uint32_t tag; > + int name_cur_len; > + > + tag = fdt_next_tag(ccs->fdt, ccs->fdt_offset, &fdt_offset_next); > + switch (tag) { > + case FDT_BEGIN_NODE: > + ccs->fdt_depth++; > + name_cur = fdt_get_name(ccs->fdt, ccs->fdt_offset, > &name_cur_len); > + *prop_name = g_strndup(name_cur, name_cur_len); > + resp = SPAPR_DR_CC_RESPONSE_NEXT_CHILD; > + break; > + case FDT_END_NODE: > + ccs->fdt_depth--; > + if (ccs->fdt_depth == 0) { > + resp = SPAPR_DR_CC_RESPONSE_SUCCESS; > + } else { > + resp = SPAPR_DR_CC_RESPONSE_PREV_PARENT; > + } > + break; > + case FDT_PROP: > + *prop = fdt_get_property_by_offset(ccs->fdt, ccs->fdt_offset, > + prop_len); > + name_cur = fdt_string(ccs->fdt, fdt32_to_cpu((*prop)->nameoff)); > + *prop_name = g_strdup(name_cur); > + resp = SPAPR_DR_CC_RESPONSE_NEXT_PROPERTY; > + break; > + case FDT_END: > + resp = SPAPR_DR_CC_RESPONSE_ERROR; > + break; IIUC, the fdt fragment you're stepping through here is generated within qemu. In which case shouldn't this be an assert, rather than reporting an error to the guest? > + default: > + ccs->fdt_offset = fdt_offset_next; > + } > + } > + > + ccs->fdt_offset = fdt_offset_next; > + return resp; > +} > + > +static sPAPRDRCCResponse configure_connector(sPAPRDRConnector *drc, > + char **prop_name, > + const struct fdt_property > **prop, > + int *prop_len) > +{ > + return configure_connector_common(&drc->ccs, prop_name, prop, prop_len); > +} > + > +static void prop_get_id(Object *obj, Visitor *v, void *opaque, > + const char *name, Error **errp) > +{ > + sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj); > + uint32_t value = get_id(drc); > + visit_type_uint32(v, &value, name, errp); > +} > + > +static void prop_get_index(Object *obj, Visitor *v, void *opaque, > + const char *name, Error **errp) > +{ > + sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj); > + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); > + uint32_t value = (uint32_t)drck->get_index(drc); > + visit_type_uint32(v, &value, name, errp); > +} > + > +static void prop_get_type(Object *obj, Visitor *v, void *opaque, > + const char *name, Error **errp) > +{ > + sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj); > + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); > + uint32_t value = (uint32_t)drck->get_type(drc); > + visit_type_uint32(v, &value, name, errp); > +} > + > +static void prop_get_entity_sense(Object *obj, Visitor *v, void *opaque, > + const char *name, Error **errp) > +{ > + sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj); > + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); > + uint32_t value = (uint32_t)drck->entity_sense(drc); > + visit_type_uint32(v, &value, name, errp); > +} > + > +static void prop_get_fdt(Object *obj, Visitor *v, void *opaque, > + const char *name, Error **errp) > +{ > + sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj); > + sPAPRDRCCState ccs = { 0 }; > + sPAPRDRCCResponse resp; > + > + ccs.fdt = drc->ccs.fdt; > + ccs.fdt_offset = ccs.fdt_start_offset = drc->ccs.fdt_start_offset; > + > + do { > + char *prop_name = NULL; > + const struct fdt_property *prop = NULL; > + int prop_len; > + > + resp = configure_connector_common(&ccs, &prop_name, &prop, > &prop_len); > + > + switch (resp) { > + case SPAPR_DR_CC_RESPONSE_NEXT_CHILD: > + visit_start_struct(v, NULL, NULL, prop_name, 0, NULL); > + break; > + case SPAPR_DR_CC_RESPONSE_PREV_PARENT: > + visit_end_struct(v, NULL); > + break; > + case SPAPR_DR_CC_RESPONSE_NEXT_PROPERTY: { > + int i; > + visit_start_list(v, prop_name, NULL); > + for (i = 0; i < prop_len; i++) { > + visit_type_uint8(v, (uint8_t *)&prop->data[i], NULL, NULL); > + } > + visit_end_list(v, NULL); > + break; > + } > + default: > + resp = SPAPR_DR_CC_RESPONSE_SUCCESS; > + break; > + } > + > + g_free(prop_name); > + } while (resp != SPAPR_DR_CC_RESPONSE_SUCCESS && > + resp != SPAPR_DR_CC_RESPONSE_ERROR); > +} > + > +static void attach(sPAPRDRConnector *drc, DeviceState *d, void *fdt, > + int fdt_start_offset, bool coldplug) > +{ > + DPRINTFN("attach"); > + > + g_assert(drc->isolation_state == SPAPR_DR_ISOLATION_STATE_ISOLATED); > + g_assert(drc->allocation_state == SPAPR_DR_ALLOCATION_STATE_UNUSABLE); > + g_assert(drc->indicator_state == SPAPR_DR_INDICATOR_STATE_INACTIVE); > + g_assert(fdt || coldplug); > + > + /* NOTE: setting initial isolation state to UNISOLATED means we can't > + * detach unless guest has a userspace/kernel that moves this state > + * back to ISOLATED in response to an unplug event, or this is done > + * manually by the admin prior. if we force things while the guest > + * may be accessing the device, we can easily crash the guest, so we > + * we defer completion of removal in such cases to the reset() hook. > + */ Given that, would it make more sense to start in ISOLATED state? Or is the initial state specified by PAPR? > + drc->isolation_state = SPAPR_DR_ISOLATION_STATE_UNISOLATED; > + drc->allocation_state = SPAPR_DR_ALLOCATION_STATE_USABLE; > + drc->indicator_state = SPAPR_DR_INDICATOR_STATE_ACTIVE; > + > + drc->dev = d; > + drc->ccs.fdt = fdt; > + drc->ccs.fdt_offset = drc->ccs.fdt_start_offset = fdt_start_offset; > + drc->ccs.fdt_depth = 0; > + > + object_property_add_link(OBJECT(drc), "device", > + object_get_typename(OBJECT(drc->dev)), > + (Object **)(&drc->dev), > + NULL, 0, NULL); > +} > + > +static void detach(sPAPRDRConnector *drc, DeviceState *d, > + spapr_drc_detach_cb *detach_cb, > + void *detach_cb_opaque) > +{ > + DPRINTFN("detach"); > + > + drc->detach_cb = detach_cb; > + drc->detach_cb_opaque = detach_cb_opaque; > + > + if (drc->isolation_state != SPAPR_DR_ISOLATION_STATE_ISOLATED) { > + DPRINTFN("awaiting transition to isolated state before removal"); > + drc->awaiting_release = true; > + return; > + } > + > + drc->allocation_state = SPAPR_DR_ALLOCATION_STATE_UNUSABLE; > + drc->indicator_state = SPAPR_DR_INDICATOR_STATE_INACTIVE; > + > + if (drc->detach_cb) { > + drc->detach_cb(drc->dev, drc->detach_cb_opaque); > + } > + > + drc->awaiting_release = false; > + g_free(drc->ccs.fdt); > + drc->ccs.fdt = NULL; > + drc->ccs.fdt_offset = drc->ccs.fdt_start_offset = drc->ccs.fdt_depth = 0; > + object_property_del(OBJECT(drc), "device", NULL); > + drc->dev = NULL; > + drc->detach_cb = NULL; > + drc->detach_cb_opaque = NULL; Shouldn't all this code after the detach_cb call also be called from set_isolation_state in the case of a deferred detach? In which case you probably want a helper. > +} > + > +static void reset(DeviceState *d) > +{ > + sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(d); > + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); > + > + DPRINTFN("drc reset: %x", drck->get_index(drc)); > + /* immediately upon reset we can safely assume DRCs whose devices are > pending > + * removal can be safely removed, and that they will subsequently be > left in > + * an ISOLATED state. move the DRC to this state in these cases (which > will in > + * turn complete any pending device removals) > + */ > + if (drc->awaiting_release) { > + drck->set_isolation_state(drc, SPAPR_DR_ISOLATION_STATE_ISOLATED); > + } > +} > + > +static void realize(DeviceState *d, Error **errp) > +{ > + sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(d); > + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); > + Object *root_container; > + char link_name[256]; > + gchar *child_name; > + Error *err = NULL; > + > + DPRINTFN("drc realize: %x", drck->get_index(drc)); > + /* NOTE: we do this as part of realize/unrealize due to the fact > + * that the guest will communicate with the DRC via RTAS calls > + * referencing the global DRC index. By unlinking the DRC > + * from DRC_CONTAINER_PATH/<drc_index> we effectively make it > + * inaccessible by the guest, since lookups rely on this path > + * existing in the composition tree > + */ > + root_container = container_get(object_get_root(), DRC_CONTAINER_PATH); > + snprintf(link_name, sizeof(link_name), "%x", drck->get_index(drc)); > + child_name = object_get_canonical_path_component(OBJECT(drc)); > + DPRINTFN("drc child name: %s", child_name); > + object_property_add_alias(root_container, link_name, > + drc->owner, child_name, &err); > + /* > + object_property_add_link(root_container, name, TYPE_SPAPR_DR_CONNECTOR, > + (Object **)&drc, NULL, > + OBJ_PROP_LINK_UNREF_ON_RELEASE, &err); > + */ > + if (err) { > + error_report("%s", error_get_pretty(err)); > + error_free(err); > + object_unref(OBJECT(drc)); > + } > + DPRINTFN("drc realize complete"); > +} > + > +static void unrealize(DeviceState *d, Error **errp) > +{ > + sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(d); > + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); > + Object *root_container; > + char name[256]; > + Error *err = NULL; > + > + DPRINTFN("drc unrealize: %x", drck->get_index(drc)); > + root_container = container_get(object_get_root(), DRC_CONTAINER_PATH); > + snprintf(name, sizeof(name), "%x", drck->get_index(drc)); > + object_property_del(root_container, name, &err); > + if (err) { > + error_report("%s", error_get_pretty(err)); > + error_free(err); > + object_unref(OBJECT(drc)); > + } > +} > + > +sPAPRDRConnector *spapr_dr_connector_new(Object *owner, > + sPAPRDRConnectorType type, > + uint32_t id) > +{ > + sPAPRDRConnector *drc = > + SPAPR_DR_CONNECTOR(object_new(TYPE_SPAPR_DR_CONNECTOR)); > + > + g_assert(type); > + > + drc->type = type; > + drc->id = id; > + drc->owner = owner; > + object_property_add_child(owner, "dr-connector[*]", OBJECT(drc), NULL); > + object_property_set_bool(OBJECT(drc), true, "realized", NULL); > + > + return drc; > +} > + > +static void spapr_dr_connector_instance_init(Object *obj) > +{ > + sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj); > + > + object_property_add_uint32_ptr(obj, "isolation-state", > + &drc->isolation_state, NULL); > + object_property_add_uint32_ptr(obj, "indicator-state", > + &drc->indicator_state, NULL); > + object_property_add_uint32_ptr(obj, "allocation-state", > + &drc->allocation_state, NULL); Don't these QOM properties need to be bound to set_isolation_state etc. for the write side? Or does add_uint32_ptr only allow reads? > + object_property_add(obj, "id", "uint32", prop_get_id, > + NULL, NULL, NULL, NULL); > + object_property_add(obj, "index", "uint32", prop_get_index, > + NULL, NULL, NULL, NULL); > + object_property_add(obj, "index", "uint32", prop_get_type, > + NULL, NULL, NULL, NULL); > + object_property_add(obj, "entity-sense", "uint32", prop_get_entity_sense, > + NULL, NULL, NULL, NULL); > + object_property_add(obj, "fdt", "struct", prop_get_fdt, > + NULL, NULL, NULL, NULL); > +} > + > +static void spapr_dr_connector_class_init(ObjectClass *k, void *data) > +{ > + DeviceClass *dk = DEVICE_CLASS(k); > + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); > + > + dk->reset = reset; > + dk->realize = realize; > + dk->unrealize = unrealize; > + drck->set_isolation_state = set_isolation_state; > + drck->set_indicator_state = set_indicator_state; > + drck->set_allocation_state = set_allocation_state; > + drck->get_index = get_index; > + drck->get_type = get_type; > + drck->entity_sense = entity_sense; > + drck->configure_connector = configure_connector; > + drck->attach = attach; > + drck->detach = detach; > +} > + > +static const TypeInfo spapr_dr_connector_info = { > + .name = TYPE_SPAPR_DR_CONNECTOR, > + .parent = TYPE_DEVICE, > + .instance_size = sizeof(sPAPRDRConnector), > + .instance_init = spapr_dr_connector_instance_init, > + .class_size = sizeof(sPAPRDRConnectorClass), > + .class_init = spapr_dr_connector_class_init, > +}; > + > +static void spapr_drc_register_types(void) > +{ > + type_register_static(&spapr_dr_connector_info); > +} > + > +type_init(spapr_drc_register_types) > + > +/* helper functions for external users */ > + > +sPAPRDRConnector *spapr_dr_connector_by_index(uint32_t index) > +{ > + Object *obj; > + char name[256]; > + > + snprintf(name, sizeof(name), "%s/%x", DRC_CONTAINER_PATH, index); > + obj = object_resolve_path(name, NULL); > + > + return !obj ? NULL : SPAPR_DR_CONNECTOR(obj); > +} > + > +sPAPRDRConnector *spapr_dr_connector_by_id(sPAPRDRConnectorType type, > + uint32_t id) > +{ > + return spapr_dr_connector_by_index( > + (get_type_shift(type) << DRC_INDEX_TYPE_SHIFT) | > + (id & DRC_INDEX_ID_MASK)); > +} > diff --git a/include/hw/ppc/spapr_drc.h b/include/hw/ppc/spapr_drc.h > new file mode 100644 > index 0000000..63ec687 > --- /dev/null > +++ b/include/hw/ppc/spapr_drc.h > @@ -0,0 +1,201 @@ > +/* > + * QEMU SPAPR Dynamic Reconfiguration Connector Implementation > + * > + * Copyright IBM Corp. 2014 > + * > + * Authors: > + * Michael Roth <mdr...@linux.vnet.ibm.com> > + * > + * This work is licensed under the terms of the GNU GPL, version 2 or later. > + * See the COPYING file in the top-level directory. > + */ > +#if !defined(__HW_SPAPR_DRC_H__) > +#define __HW_SPAPR_DRC_H__ > + > +#include "qom/object.h" > +#include "hw/qdev.h" > +#include "libfdt.h" > + > +#define TYPE_SPAPR_DR_CONNECTOR "spapr-dr-connector" > +#define SPAPR_DR_CONNECTOR_GET_CLASS(obj) \ > + OBJECT_GET_CLASS(sPAPRDRConnectorClass, obj, TYPE_SPAPR_DR_CONNECTOR) > +#define SPAPR_DR_CONNECTOR_CLASS(klass) \ > + OBJECT_CLASS_CHECK(sPAPRDRConnectorClass, klass, \ > + TYPE_SPAPR_DR_CONNECTOR) > +#define SPAPR_DR_CONNECTOR(obj) OBJECT_CHECK(sPAPRDRConnector, (obj), \ > + TYPE_SPAPR_DR_CONNECTOR) > + > +/* > + * Various hotplug types managed by sPAPRDRConnector > + * > + * these are somewhat arbitrary, but to make things easier > + * when generating DRC indexes later we've aligned the bit > + * positions with the values used to assign DRC indexes on > + * pSeries. we use those values as bit shifts to allow for > + * the OR'ing of these values in various QEMU routines, but > + * for values exposed to the guest (via DRC indexes for > + * instance) we will use the shift amounts. > + */ > +typedef enum { > + SPAPR_DR_CONNECTOR_TYPE_SHIFT_CPU = 1, > + SPAPR_DR_CONNECTOR_TYPE_SHIFT_PHB = 2, > + SPAPR_DR_CONNECTOR_TYPE_SHIFT_VIO = 3, > + SPAPR_DR_CONNECTOR_TYPE_SHIFT_PCI = 4, > + SPAPR_DR_CONNECTOR_TYPE_SHIFT_LMB = 8, > +} sPAPRDRConnectorTypeShift; > + > +typedef enum { > + SPAPR_DR_CONNECTOR_TYPE_ANY = ~0, > + SPAPR_DR_CONNECTOR_TYPE_CPU = 1 << SPAPR_DR_CONNECTOR_TYPE_SHIFT_CPU, > + SPAPR_DR_CONNECTOR_TYPE_PHB = 1 << SPAPR_DR_CONNECTOR_TYPE_SHIFT_PHB, > + SPAPR_DR_CONNECTOR_TYPE_VIO = 1 << SPAPR_DR_CONNECTOR_TYPE_SHIFT_VIO, > + SPAPR_DR_CONNECTOR_TYPE_PCI = 1 << SPAPR_DR_CONNECTOR_TYPE_SHIFT_PCI, > + SPAPR_DR_CONNECTOR_TYPE_LMB = 1 << SPAPR_DR_CONNECTOR_TYPE_SHIFT_LMB, > +} sPAPRDRConnectorType; > + > +/* > + * set via set-indicator RTAS calls > + * as documented by PAPR+ 2.7 13.5.3.4, Table 177 > + * > + * isolated: put device under firmware control > + * unisolated: claim OS control of device (may or may not be in use) > + */ > +typedef enum { > + SPAPR_DR_ISOLATION_STATE_ISOLATED = 0, > + SPAPR_DR_ISOLATION_STATE_UNISOLATED = 1 > +} sPAPRDRIsolationState; > + > +/* > + * set via set-indicator RTAS calls > + * as documented by PAPR+ 2.7 13.5.3.4, Table 177 > + * > + * unusable: mark device as unavailable to OS > + * usable: mark device as available to OS > + * exchange: (currently unused) > + * recover: (currently unused) > + */ > +typedef enum { > + SPAPR_DR_ALLOCATION_STATE_UNUSABLE = 0, > + SPAPR_DR_ALLOCATION_STATE_USABLE = 1, > + SPAPR_DR_ALLOCATION_STATE_EXCHANGE = 2, > + SPAPR_DR_ALLOCATION_STATE_RECOVER = 3 > +} sPAPRDRAllocationState; > + > +/* > + * LED/visual indicator state > + * > + * set via set-indicator RTAS calls > + * as documented by PAPR+ 2.7 13.5.3.4, Table 177, > + * and PAPR+ 2.7 13.5.4.1, Table 180 > + * > + * inactive: hotpluggable entity inactive and safely removable > + * active: hotpluggable entity in use and not safely removable > + * identify: (currently unused) > + * action: (currently unused) > + */ > +typedef enum { > + SPAPR_DR_INDICATOR_STATE_INACTIVE = 0, > + SPAPR_DR_INDICATOR_STATE_ACTIVE = 1, > + SPAPR_DR_INDICATOR_STATE_IDENTIFY = 2, > + SPAPR_DR_INDICATOR_STATE_ACTION = 3, > +} sPAPRDRIndicatorState; > + > +/* > + * returned via get-sensor-state RTAS calls > + * as documented by PAPR+ 2.7 13.5.3.3, Table 175: > + * > + * empty: connector slot empty (e.g. empty hotpluggable PCI slot) > + * present: connector slot populated and device available to OS > + * unusable: device not currently available to OS > + * exchange: (currently unused) > + * recover: (currently unused) > + */ > +typedef enum { > + SPAPR_DR_ENTITY_SENSE_EMPTY = 0, > + SPAPR_DR_ENTITY_SENSE_PRESENT = 1, > + SPAPR_DR_ENTITY_SENSE_UNUSABLE = 2, > + SPAPR_DR_ENTITY_SENSE_EXCHANGE = 3, > + SPAPR_DR_ENTITY_SENSE_RECOVER = 4, > +} sPAPRDREntitySense; > + > +typedef enum { > + SPAPR_DR_CC_RESPONSE_NEXT_SIB = 1, /* currently unused */ > + SPAPR_DR_CC_RESPONSE_NEXT_CHILD = 2, > + SPAPR_DR_CC_RESPONSE_NEXT_PROPERTY = 3, > + SPAPR_DR_CC_RESPONSE_PREV_PARENT = 4, > + SPAPR_DR_CC_RESPONSE_SUCCESS = 0, > + SPAPR_DR_CC_RESPONSE_ERROR = -1, > + SPAPR_DR_CC_RESPONSE_CONTINUE = -2, > +} sPAPRDRCCResponse; > + > +typedef struct sPAPRDRCCState { > + void *fdt; > + int fdt_start_offset; > + int fdt_offset; > + int fdt_depth; > +} sPAPRDRCCState; > + > +typedef void (spapr_drc_detach_cb)(DeviceState *d, void *opaque); > + > +typedef struct sPAPRDRConnector { > + /*< private >*/ > + DeviceState parent; > + > + sPAPRDRConnectorType type; > + uint32_t id; > + Object *owner; > + > + /* sensor/indicator states */ > + uint32_t isolation_state; > + uint32_t allocation_state; > + uint32_t indicator_state; > + > + /* configure-connector state */ > + sPAPRDRCCState ccs; > + > + bool awaiting_release; > + > + /* device pointer, via link property */ > + DeviceState *dev; > + spapr_drc_detach_cb *detach_cb; > + void *detach_cb_opaque; > +} sPAPRDRConnector; > + > +typedef struct sPAPRDRConnectorClass { > + /*< private >*/ > + DeviceClass parent; > + > + /*< public >*/ > + > + /* accessors for guest-visible (generally via RTAS) DR state */ > + int (*set_isolation_state)(sPAPRDRConnector *drc, > + sPAPRDRIsolationState state); > + int (*set_indicator_state)(sPAPRDRConnector *drc, > + sPAPRDRIndicatorState state); > + int (*set_allocation_state)(sPAPRDRConnector *drc, > + sPAPRDRAllocationState state); > + uint32_t (*get_index)(sPAPRDRConnector *drc); > + uint32_t (*get_type)(sPAPRDRConnector *drc); > + > + sPAPRDREntitySense (*entity_sense)(sPAPRDRConnector *drc); > + sPAPRDRCCResponse (*configure_connector)(sPAPRDRConnector *drc, > + char **prop_name, > + const struct fdt_property > **prop, > + int *prop_len); > + > + /* QEMU interfaces for managing hotplug operations */ > + void (*attach)(sPAPRDRConnector *drc, DeviceState *d, void *fdt, > + int fdt_start_offset, bool coldplug); > + void (*detach)(sPAPRDRConnector *drc, DeviceState *d, > + spapr_drc_detach_cb *detach_cb, > + void *detach_cb_opaque); > +} sPAPRDRConnectorClass; > + > +sPAPRDRConnector *spapr_dr_connector_new(Object *owner, > + sPAPRDRConnectorType type, > + uint32_t token); > +sPAPRDRConnector *spapr_dr_connector_by_index(uint32_t index); > +sPAPRDRConnector *spapr_dr_connector_by_id(sPAPRDRConnectorType type, > + uint32_t id); > + > +#endif /* __HW_SPAPR_DRC_H__ */ -- David Gibson | I'll have my music baroque, and my code david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_ | _way_ _around_! http://www.ozlabs.org/~dgibson
pgpYkraF8Rt2Q.pgp
Description: PGP signature