diff options
author | Michael Roth <mdroth@linux.vnet.ibm.com> | 2015-05-07 15:33:48 +1000 |
---|---|---|
committer | Alexander Graf <agraf@suse.de> | 2015-06-03 23:56:53 +0200 |
commit | 46503c2bc047bfe8c26440e17298fcbc59d7bbbe (patch) | |
tree | e574a90752e0548b854a740a56805e95636a807d /hw | |
parent | ab316865db8ee97c53cd70c91b1b160c474102f8 (diff) |
spapr_rtas: add ibm, configure-connector RTAS interface
This interface is used to fetch an OF device-tree nodes that describes a
newly-attached device to guest. It is called multiple times to walk the
device-tree node and fetch individual properties into a 'workarea'/buffer
provided by the guest.
The device-tree is generated by QEMU and passed to an sPAPRDRConnector during
the initial hotplug operation, and the state of these RTAS calls is tracked by
the sPAPRDRConnector. When the last of these properties is successfully
fetched, we report as special return value to the guest and transition
the device to a 'configured' state on the QEMU/DRC side.
See docs/specs/ppc-spapr-hotplug.txt for a complete description of
this interface.
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Alexander Graf <agraf@suse.de>
Diffstat (limited to 'hw')
-rw-r--r-- | hw/ppc/spapr.c | 4 | ||||
-rw-r--r-- | hw/ppc/spapr_rtas.c | 180 |
2 files changed, 184 insertions, 0 deletions
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 8cf1f2a547..7323efda4c 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -1663,6 +1663,10 @@ static void ppc_spapr_init(MachineState *machine) kernel_cmdline, spapr->epow_irq); assert(spapr->fdt_skel != NULL); + /* used by RTAS */ + QTAILQ_INIT(&spapr->ccs_list); + qemu_register_reset(spapr_ccs_reset_hook, spapr); + qemu_register_boot_set(spapr_boot_set, spapr); } diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index f80beb2d9b..fa28d43f81 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -47,6 +47,43 @@ do { } while (0) #endif +static sPAPRConfigureConnectorState *spapr_ccs_find(sPAPREnvironment *spapr, + uint32_t drc_index) +{ + sPAPRConfigureConnectorState *ccs = NULL; + + QTAILQ_FOREACH(ccs, &spapr->ccs_list, next) { + if (ccs->drc_index == drc_index) { + break; + } + } + + return ccs; +} + +static void spapr_ccs_add(sPAPREnvironment *spapr, + sPAPRConfigureConnectorState *ccs) +{ + g_assert(!spapr_ccs_find(spapr, ccs->drc_index)); + QTAILQ_INSERT_HEAD(&spapr->ccs_list, ccs, next); +} + +static void spapr_ccs_remove(sPAPREnvironment *spapr, + sPAPRConfigureConnectorState *ccs) +{ + QTAILQ_REMOVE(&spapr->ccs_list, ccs, next); + g_free(ccs); +} + +void spapr_ccs_reset_hook(void *opaque) +{ + sPAPREnvironment *spapr = opaque; + sPAPRConfigureConnectorState *ccs, *ccs_tmp; + + QTAILQ_FOREACH_SAFE(ccs, &spapr->ccs_list, next, ccs_tmp) { + spapr_ccs_remove(spapr, ccs); + } +} static void rtas_display_character(PowerPCCPU *cpu, sPAPREnvironment *spapr, uint32_t token, uint32_t nargs, @@ -355,6 +392,19 @@ static void rtas_set_indicator(PowerPCCPU *cpu, sPAPREnvironment *spapr, switch (sensor_type) { case RTAS_SENSOR_TYPE_ISOLATION_STATE: + /* if the guest is configuring a device attached to this + * DRC, we should reset the configuration state at this + * point since it may no longer be reliable (guest released + * device and needs to start over, or unplug occurred so + * the FDT is no longer valid) + */ + if (sensor_state == SPAPR_DR_ISOLATION_STATE_ISOLATED) { + sPAPRConfigureConnectorState *ccs = spapr_ccs_find(spapr, + sensor_index); + if (ccs) { + spapr_ccs_remove(spapr, ccs); + } + } drck->set_isolation_state(drc, sensor_state); break; case RTAS_SENSOR_TYPE_DR: @@ -418,6 +468,134 @@ static void rtas_get_sensor_state(PowerPCCPU *cpu, sPAPREnvironment *spapr, rtas_st(rets, 1, entity_sense); } +/* configure-connector work area offsets, int32_t units for field + * indexes, bytes for field offset/len values. + * + * as documented by PAPR+ v2.7, 13.5.3.5 + */ +#define CC_IDX_NODE_NAME_OFFSET 2 +#define CC_IDX_PROP_NAME_OFFSET 2 +#define CC_IDX_PROP_LEN 3 +#define CC_IDX_PROP_DATA_OFFSET 4 +#define CC_VAL_DATA_OFFSET ((CC_IDX_PROP_DATA_OFFSET + 1) * 4) +#define CC_WA_LEN 4096 + +static void rtas_ibm_configure_connector(PowerPCCPU *cpu, + sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, uint32_t nret, + target_ulong rets) +{ + uint64_t wa_addr; + uint64_t wa_offset; + uint32_t drc_index; + sPAPRDRConnector *drc; + sPAPRDRConnectorClass *drck; + sPAPRConfigureConnectorState *ccs; + sPAPRDRCCResponse resp = SPAPR_DR_CC_RESPONSE_CONTINUE; + int rc; + const void *fdt; + + if (nargs != 2 || nret != 1) { + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); + return; + } + + wa_addr = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 0); + + drc_index = rtas_ld(wa_addr, 0); + drc = spapr_dr_connector_by_index(drc_index); + if (!drc) { + DPRINTF("rtas_ibm_configure_connector: invalid DRC index: %xh\n", + drc_index); + rc = RTAS_OUT_PARAM_ERROR; + goto out; + } + + drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + fdt = drck->get_fdt(drc, NULL); + + ccs = spapr_ccs_find(spapr, drc_index); + if (!ccs) { + ccs = g_new0(sPAPRConfigureConnectorState, 1); + (void)drck->get_fdt(drc, &ccs->fdt_offset); + ccs->drc_index = drc_index; + spapr_ccs_add(spapr, ccs); + } + + do { + uint32_t tag; + const char *name; + const struct fdt_property *prop; + int fdt_offset_next, prop_len; + + tag = fdt_next_tag(fdt, ccs->fdt_offset, &fdt_offset_next); + + switch (tag) { + case FDT_BEGIN_NODE: + ccs->fdt_depth++; + name = fdt_get_name(fdt, ccs->fdt_offset, NULL); + + /* provide the name of the next OF node */ + wa_offset = CC_VAL_DATA_OFFSET; + rtas_st(wa_addr, CC_IDX_NODE_NAME_OFFSET, wa_offset); + rtas_st_buffer_direct(wa_addr + wa_offset, CC_WA_LEN - wa_offset, + (uint8_t *)name, strlen(name) + 1); + resp = SPAPR_DR_CC_RESPONSE_NEXT_CHILD; + break; + case FDT_END_NODE: + ccs->fdt_depth--; + if (ccs->fdt_depth == 0) { + /* done sending the device tree, don't need to track + * the state anymore + */ + drck->set_configured(drc); + spapr_ccs_remove(spapr, ccs); + ccs = NULL; + resp = SPAPR_DR_CC_RESPONSE_SUCCESS; + } else { + resp = SPAPR_DR_CC_RESPONSE_PREV_PARENT; + } + break; + case FDT_PROP: + prop = fdt_get_property_by_offset(fdt, ccs->fdt_offset, + &prop_len); + name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); + + /* provide the name of the next OF property */ + wa_offset = CC_VAL_DATA_OFFSET; + rtas_st(wa_addr, CC_IDX_PROP_NAME_OFFSET, wa_offset); + rtas_st_buffer_direct(wa_addr + wa_offset, CC_WA_LEN - wa_offset, + (uint8_t *)name, strlen(name) + 1); + + /* provide the length and value of the OF property. data gets + * placed immediately after NULL terminator of the OF property's + * name string + */ + wa_offset += strlen(name) + 1, + rtas_st(wa_addr, CC_IDX_PROP_LEN, prop_len); + rtas_st(wa_addr, CC_IDX_PROP_DATA_OFFSET, wa_offset); + rtas_st_buffer_direct(wa_addr + wa_offset, CC_WA_LEN - wa_offset, + (uint8_t *)((struct fdt_property *)prop)->data, + prop_len); + resp = SPAPR_DR_CC_RESPONSE_NEXT_PROPERTY; + break; + case FDT_END: + resp = SPAPR_DR_CC_RESPONSE_ERROR; + default: + /* keep seeking for an actionable tag */ + break; + } + if (ccs) { + ccs->fdt_offset = fdt_offset_next; + } + } while (resp == SPAPR_DR_CC_RESPONSE_CONTINUE); + + rc = resp; +out: + rtas_st(rets, 0, rc); +} + static struct rtas_call { const char *name; spapr_rtas_fn fn; @@ -551,6 +729,8 @@ static void core_rtas_register_types(void) rtas_set_indicator); spapr_rtas_register(RTAS_GET_SENSOR_STATE, "get-sensor-state", rtas_get_sensor_state); + spapr_rtas_register(RTAS_IBM_CONFIGURE_CONNECTOR, "ibm,configure-connector", + rtas_ibm_configure_connector); } type_init(core_rtas_register_types) |