* See the COPYING file in the top-level directory.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "cpu.h"
+#include "qemu/cutils.h"
#include "hw/ppc/spapr_drc.h"
#include "qom/object.h"
#include "hw/qdev.h"
#include "qapi/visitor.h"
#include "qemu/error-report.h"
+#include "hw/ppc/spapr.h" /* for RTAS return codes */
/* #define DEBUG_SPAPR_DRC */
#define DRC_CONTAINER_PATH "/dr-connector"
#define DRC_INDEX_TYPE_SHIFT 28
-#define DRC_INDEX_ID_MASK (~(~0 << DRC_INDEX_TYPE_SHIFT))
+#define DRC_INDEX_ID_MASK ((1ULL << DRC_INDEX_TYPE_SHIFT) - 1)
static sPAPRDRConnectorTypeShift get_type_shift(sPAPRDRConnectorType type)
{
(drc->id & DRC_INDEX_ID_MASK);
}
-static int set_isolation_state(sPAPRDRConnector *drc,
- sPAPRDRIsolationState state)
+static uint32_t set_isolation_state(sPAPRDRConnector *drc,
+ sPAPRDRIsolationState state)
{
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
DPRINTFN("drc: %x, set_isolation_state: %x", get_index(drc), state);
+ if (state == SPAPR_DR_ISOLATION_STATE_UNISOLATED) {
+ /* cannot unisolate a non-existant resource, and, or resources
+ * which are in an 'UNUSABLE' allocation state. (PAPR 2.7, 13.5.3.5)
+ */
+ if (!drc->dev ||
+ drc->allocation_state == SPAPR_DR_ALLOCATION_STATE_UNUSABLE) {
+ return RTAS_OUT_NO_SUCH_INDICATOR;
+ }
+ }
+
drc->isolation_state = state;
if (drc->isolation_state == SPAPR_DR_ISOLATION_STATE_ISOLATED) {
drc->configured = false;
}
- return 0;
+ return RTAS_OUT_SUCCESS;
}
-static int set_indicator_state(sPAPRDRConnector *drc,
- sPAPRDRIndicatorState state)
+static uint32_t set_indicator_state(sPAPRDRConnector *drc,
+ sPAPRDRIndicatorState state)
{
DPRINTFN("drc: %x, set_indicator_state: %x", get_index(drc), state);
drc->indicator_state = state;
- return 0;
+ return RTAS_OUT_SUCCESS;
}
-static int set_allocation_state(sPAPRDRConnector *drc,
- sPAPRDRAllocationState state)
+static uint32_t set_allocation_state(sPAPRDRConnector *drc,
+ sPAPRDRAllocationState state)
{
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
DPRINTFN("drc: %x, set_allocation_state: %x", get_index(drc), state);
+ if (state == SPAPR_DR_ALLOCATION_STATE_USABLE) {
+ /* if there's no resource/device associated with the DRC, there's
+ * no way for us to put it in an allocation state consistent with
+ * being 'USABLE'. PAPR 2.7, 13.5.3.4 documents that this should
+ * result in an RTAS return code of -3 / "no such indicator"
+ */
+ if (!drc->dev) {
+ return RTAS_OUT_NO_SUCH_INDICATOR;
+ }
+ }
+
if (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI) {
drc->allocation_state = state;
if (drc->awaiting_release &&
drc->detach_cb_opaque, NULL);
}
}
- return 0;
+ return RTAS_OUT_SUCCESS;
}
static uint32_t get_type(sPAPRDRConnector *drc)
drc->configured = true;
}
+/* has the guest been notified of device attachment? */
+static void set_signalled(sPAPRDRConnector *drc)
+{
+ drc->signalled = true;
+}
+
/*
* dr-entity-sense sensor value
* returned via get-sensor-state RTAS calls
* based on the current allocation/indicator/power states
* for the DR connector.
*/
-static sPAPRDREntitySense entity_sense(sPAPRDRConnector *drc)
+static uint32_t entity_sense(sPAPRDRConnector *drc, sPAPRDREntitySense *state)
{
- sPAPRDREntitySense state;
-
if (drc->dev) {
if (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI &&
drc->allocation_state == SPAPR_DR_ALLOCATION_STATE_UNUSABLE) {
* Otherwise, report the state as USABLE/PRESENT,
* as we would for PCI.
*/
- state = SPAPR_DR_ENTITY_SENSE_UNUSABLE;
+ *state = SPAPR_DR_ENTITY_SENSE_UNUSABLE;
} else {
/* this assumes all PCI devices are assigned to
* a 'live insertion' power domain, where QEMU
* to the guest. present, non-PCI resources are
* unaffected by power state.
*/
- state = SPAPR_DR_ENTITY_SENSE_PRESENT;
+ *state = SPAPR_DR_ENTITY_SENSE_PRESENT;
}
} else {
if (drc->type == SPAPR_DR_CONNECTOR_TYPE_PCI) {
/* PCI devices, and only PCI devices, use EMPTY
* in cases where we'd otherwise use UNUSABLE
*/
- state = SPAPR_DR_ENTITY_SENSE_EMPTY;
+ *state = SPAPR_DR_ENTITY_SENSE_EMPTY;
} else {
- state = SPAPR_DR_ENTITY_SENSE_UNUSABLE;
+ *state = SPAPR_DR_ENTITY_SENSE_UNUSABLE;
}
}
DPRINTFN("drc: %x, entity_sense: %x", get_index(drc), state);
- return state;
+ return RTAS_OUT_SUCCESS;
}
-static void prop_get_index(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void prop_get_index(Object *obj, Visitor *v, const char *name,
+ void *opaque, 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);
+ visit_type_uint32(v, name, &value, errp);
}
-static void prop_get_type(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void prop_get_type(Object *obj, Visitor *v, const char *name,
+ void *opaque, 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);
+ visit_type_uint32(v, name, &value, errp);
}
static char *prop_get_name(Object *obj, Error **errp)
return g_strdup(drck->get_name(drc));
}
-static void prop_get_entity_sense(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void prop_get_entity_sense(Object *obj, Visitor *v, const char *name,
+ void *opaque, 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);
+ uint32_t value;
+
+ drck->entity_sense(drc, &value);
+ visit_type_uint32(v, name, &value, errp);
}
-static void prop_get_fdt(Object *obj, Visitor *v, void *opaque,
- const char *name, Error **errp)
+static void prop_get_fdt(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj);
+ Error *err = NULL;
int fdt_offset_next, fdt_offset, fdt_depth;
void *fdt;
if (!drc->fdt) {
+ visit_start_struct(v, name, NULL, 0, &err);
+ if (!err) {
+ visit_end_struct(v, &err);
+ }
+ error_propagate(errp, err);
return;
}
case FDT_BEGIN_NODE:
fdt_depth++;
name = fdt_get_name(fdt, fdt_offset, &name_len);
- visit_start_struct(v, NULL, NULL, name, 0, NULL);
+ visit_start_struct(v, name, NULL, 0, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
break;
case FDT_END_NODE:
/* shouldn't ever see an FDT_END_NODE before FDT_BEGIN_NODE */
g_assert(fdt_depth > 0);
- visit_end_struct(v, NULL);
+ visit_end_struct(v, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
fdt_depth--;
break;
case FDT_PROP: {
int i;
prop = fdt_get_property_by_offset(fdt, fdt_offset, &prop_len);
name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
- visit_start_list(v, name, NULL);
+ visit_start_list(v, name, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
for (i = 0; i < prop_len; i++) {
- visit_type_uint8(v, (uint8_t *)&prop->data[i], NULL, NULL);
-
+ visit_type_uint8(v, NULL, (uint8_t *)&prop->data[i], &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
}
- visit_end_list(v, NULL);
+ visit_end_list(v);
break;
}
default:
drc->dev = d;
drc->fdt = fdt;
drc->fdt_start_offset = fdt_start_offset;
- drc->configured = false;
+ drc->configured = coldplug;
+ /* 'logical' DR resources such as memory/cpus are in some cases treated
+ * as a pool of resources from which the guest is free to choose from
+ * based on only a count. for resources that can be assigned in this
+ * fashion, we must assume the resource is signalled immediately
+ * since a single hotplug request might make an arbitrary number of
+ * such attached resources available to the guest, as opposed to
+ * 'physical' DR resources such as PCI where each device/resource is
+ * signalled individually.
+ */
+ drc->signalled = (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI)
+ ? true : coldplug;
object_property_add_link(OBJECT(drc), "device",
object_get_typename(OBJECT(drc->dev)),
drc->detach_cb = detach_cb;
drc->detach_cb_opaque = detach_cb_opaque;
+ /* if we've signalled device presence to the guest, or if the guest
+ * has gone ahead and configured the device (via manually-executed
+ * device add via drmgr in guest, namely), we need to wait
+ * for the guest to quiesce the device before completing detach.
+ * Otherwise, we can assume the guest hasn't seen it and complete the
+ * detach immediately. Note that there is a small race window
+ * just before, or during, configuration, which is this context
+ * refers mainly to fetching the device tree via RTAS.
+ * During this window the device access will be arbitrated by
+ * associated DRC, which will simply fail the RTAS calls as invalid.
+ * This is recoverable within guest and current implementations of
+ * drmgr should be able to cope.
+ */
+ if (!drc->signalled && !drc->configured) {
+ /* if the guest hasn't seen the device we can't rely on it to
+ * set it back to an isolated state via RTAS, so do it here manually
+ */
+ drc->isolation_state = SPAPR_DR_ISOLATION_STATE_ISOLATED;
+ }
+
if (drc->isolation_state != SPAPR_DR_ISOLATION_STATE_ISOLATED) {
DPRINTFN("awaiting transition to isolated state before removal");
drc->awaiting_release = true;
{
sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(d);
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+ sPAPRDREntitySense state;
DPRINTFN("drc reset: %x", drck->get_index(drc));
/* immediately upon reset we can safely assume DRCs whose devices
drck->set_allocation_state(drc, SPAPR_DR_ALLOCATION_STATE_UNUSABLE);
}
}
+
+ drck->entity_sense(drc, &state);
+ if (state == SPAPR_DR_ENTITY_SENSE_PRESENT) {
+ drck->set_signalled(drc);
+ }
}
static void realize(DeviceState *d, Error **errp)
object_property_add_alias(root_container, link_name,
drc->owner, child_name, &err);
if (err) {
- error_report("%s", error_get_pretty(err));
- error_free(err);
+ error_report_err(err);
object_unref(OBJECT(drc));
}
g_free(child_name);
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);
+ error_report_err(err);
object_unref(OBJECT(drc));
}
}
{
sPAPRDRConnector *drc =
SPAPR_DR_CONNECTOR(object_new(TYPE_SPAPR_DR_CONNECTOR));
+ char *prop_name;
g_assert(type);
drc->type = type;
drc->id = id;
drc->owner = owner;
- object_property_add_child(owner, "dr-connector[*]", OBJECT(drc), NULL);
+ prop_name = g_strdup_printf("dr-connector[%"PRIu32"]", get_index(drc));
+ object_property_add_child(owner, prop_name, OBJECT(drc), NULL);
object_property_set_bool(OBJECT(drc), true, "realized", NULL);
+ g_free(prop_name);
/* human-readable name for a DRC to encode into the DT
* description. this is mainly only used within a guest in place
drck->attach = attach;
drck->detach = detach;
drck->release_pending = release_pending;
+ drck->set_signalled = set_signalled;
+ /*
+ * Reason: it crashes FIXME find and document the real reason
+ */
+ dk->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo spapr_dr_connector_info = {
{
Object *root_container;
ObjectProperty *prop;
+ ObjectPropertyIterator iter;
uint32_t drc_count = 0;
GArray *drc_indexes, *drc_power_domains;
GString *drc_names, *drc_types;
*/
root_container = container_get(object_get_root(), DRC_CONTAINER_PATH);
- QTAILQ_FOREACH(prop, &root_container->properties, node) {
+ object_property_iter_init(&iter, root_container);
+ while ((prop = object_property_iter_next(&iter))) {
Object *obj;
sPAPRDRConnector *drc;
sPAPRDRConnectorClass *drck;