xref: /qemu/hw/ppc/spapr_drc.c (revision 62d38c9bd3a217fa21f4fe73526839eee5f2f770)
1bbf5c878SMichael Roth /*
2bbf5c878SMichael Roth  * QEMU SPAPR Dynamic Reconfiguration Connector Implementation
3bbf5c878SMichael Roth  *
4bbf5c878SMichael Roth  * Copyright IBM Corp. 2014
5bbf5c878SMichael Roth  *
6bbf5c878SMichael Roth  * Authors:
7bbf5c878SMichael Roth  *  Michael Roth      <mdroth@linux.vnet.ibm.com>
8bbf5c878SMichael Roth  *
9bbf5c878SMichael Roth  * This work is licensed under the terms of the GNU GPL, version 2 or later.
10bbf5c878SMichael Roth  * See the COPYING file in the top-level directory.
11bbf5c878SMichael Roth  */
12bbf5c878SMichael Roth 
130d75590dSPeter Maydell #include "qemu/osdep.h"
14da34e65cSMarkus Armbruster #include "qapi/error.h"
1515280c36SMarkus Armbruster #include "qapi/qmp/qnull.h"
164771d756SPaolo Bonzini #include "cpu.h"
17f348b6d1SVeronia Bahaa #include "qemu/cutils.h"
18bbf5c878SMichael Roth #include "hw/ppc/spapr_drc.h"
19bbf5c878SMichael Roth #include "qom/object.h"
20bbf5c878SMichael Roth #include "hw/qdev.h"
21bbf5c878SMichael Roth #include "qapi/visitor.h"
22bbf5c878SMichael Roth #include "qemu/error-report.h"
230cb688d2SMichael Roth #include "hw/ppc/spapr.h" /* for RTAS return codes */
2431834723SDaniel Henrique Barboza #include "hw/pci-host/spapr.h" /* spapr_phb_remove_pci_device_cb callback */
25d9c95c71SGreg Kurz #include "sysemu/device_tree.h"
2624ac7755SLaurent Vivier #include "trace.h"
27bbf5c878SMichael Roth 
28bbf5c878SMichael Roth #define DRC_CONTAINER_PATH "/dr-connector"
29bbf5c878SMichael Roth #define DRC_INDEX_TYPE_SHIFT 28
30627c2ef7SDavid Gibson #define DRC_INDEX_ID_MASK ((1ULL << DRC_INDEX_TYPE_SHIFT) - 1)
31bbf5c878SMichael Roth 
322d335818SDavid Gibson sPAPRDRConnectorType spapr_drc_type(sPAPRDRConnector *drc)
332d335818SDavid Gibson {
342d335818SDavid Gibson     sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
352d335818SDavid Gibson 
362d335818SDavid Gibson     return 1 << drck->typeshift;
372d335818SDavid Gibson }
382d335818SDavid Gibson 
390b55aa91SDavid Gibson uint32_t spapr_drc_index(sPAPRDRConnector *drc)
40bbf5c878SMichael Roth {
412d335818SDavid Gibson     sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
422d335818SDavid Gibson 
43bbf5c878SMichael Roth     /* no set format for a drc index: it only needs to be globally
44bbf5c878SMichael Roth      * unique. this is how we encode the DRC type on bare-metal
45bbf5c878SMichael Roth      * however, so might as well do that here
46bbf5c878SMichael Roth      */
472d335818SDavid Gibson     return (drck->typeshift << DRC_INDEX_TYPE_SHIFT)
482d335818SDavid Gibson         | (drc->id & DRC_INDEX_ID_MASK);
49bbf5c878SMichael Roth }
50bbf5c878SMichael Roth 
510dfabd39SDavid Gibson static uint32_t drc_isolate_physical(sPAPRDRConnector *drc)
52bbf5c878SMichael Roth {
539d4c0f4fSDavid Gibson     switch (drc->state) {
549d4c0f4fSDavid Gibson     case SPAPR_DRC_STATE_PHYSICAL_POWERON:
559d4c0f4fSDavid Gibson         return RTAS_OUT_SUCCESS; /* Nothing to do */
569d4c0f4fSDavid Gibson     case SPAPR_DRC_STATE_PHYSICAL_CONFIGURED:
579d4c0f4fSDavid Gibson         break; /* see below */
589d4c0f4fSDavid Gibson     case SPAPR_DRC_STATE_PHYSICAL_UNISOLATE:
599d4c0f4fSDavid Gibson         return RTAS_OUT_PARAM_ERROR; /* not allowed */
609d4c0f4fSDavid Gibson     default:
619d4c0f4fSDavid Gibson         g_assert_not_reached();
629d4c0f4fSDavid Gibson     }
639d4c0f4fSDavid Gibson 
649d4c0f4fSDavid Gibson     drc->state = SPAPR_DRC_STATE_PHYSICAL_POWERON;
659d1852ceSMichael Roth 
66f1c52354SDavid Gibson     if (drc->unplug_requested) {
670b55aa91SDavid Gibson         uint32_t drc_index = spapr_drc_index(drc);
680b55aa91SDavid Gibson         trace_spapr_drc_set_isolation_state_finalizing(drc_index);
69a8dc47fdSDavid Gibson         spapr_drc_detach(drc);
70bbf5c878SMichael Roth     }
710dfabd39SDavid Gibson 
720dfabd39SDavid Gibson     return RTAS_OUT_SUCCESS;
73bbf5c878SMichael Roth }
74bbf5c878SMichael Roth 
750dfabd39SDavid Gibson static uint32_t drc_unisolate_physical(sPAPRDRConnector *drc)
760dfabd39SDavid Gibson {
779d4c0f4fSDavid Gibson     switch (drc->state) {
789d4c0f4fSDavid Gibson     case SPAPR_DRC_STATE_PHYSICAL_UNISOLATE:
799d4c0f4fSDavid Gibson     case SPAPR_DRC_STATE_PHYSICAL_CONFIGURED:
809d4c0f4fSDavid Gibson         return RTAS_OUT_SUCCESS; /* Nothing to do */
819d4c0f4fSDavid Gibson     case SPAPR_DRC_STATE_PHYSICAL_POWERON:
829d4c0f4fSDavid Gibson         break; /* see below */
839d4c0f4fSDavid Gibson     default:
849d4c0f4fSDavid Gibson         g_assert_not_reached();
859d4c0f4fSDavid Gibson     }
869d4c0f4fSDavid Gibson 
870dfabd39SDavid Gibson     /* cannot unisolate a non-existent resource, and, or resources
880dfabd39SDavid Gibson      * which are in an 'UNUSABLE' allocation state. (PAPR 2.7,
890dfabd39SDavid Gibson      * 13.5.3.5)
900dfabd39SDavid Gibson      */
910dfabd39SDavid Gibson     if (!drc->dev) {
920dfabd39SDavid Gibson         return RTAS_OUT_NO_SUCH_INDICATOR;
930dfabd39SDavid Gibson     }
940dfabd39SDavid Gibson 
959d4c0f4fSDavid Gibson     drc->state = SPAPR_DRC_STATE_PHYSICAL_UNISOLATE;
964445b1d2SDavid Gibson     drc->ccs_offset = drc->fdt_start_offset;
974445b1d2SDavid Gibson     drc->ccs_depth = 0;
980dfabd39SDavid Gibson 
990dfabd39SDavid Gibson     return RTAS_OUT_SUCCESS;
1000dfabd39SDavid Gibson }
1010dfabd39SDavid Gibson 
1020dfabd39SDavid Gibson static uint32_t drc_isolate_logical(sPAPRDRConnector *drc)
1030dfabd39SDavid Gibson {
1049d4c0f4fSDavid Gibson     switch (drc->state) {
1059d4c0f4fSDavid Gibson     case SPAPR_DRC_STATE_LOGICAL_AVAILABLE:
1069d4c0f4fSDavid Gibson     case SPAPR_DRC_STATE_LOGICAL_UNUSABLE:
1079d4c0f4fSDavid Gibson         return RTAS_OUT_SUCCESS; /* Nothing to do */
1089d4c0f4fSDavid Gibson     case SPAPR_DRC_STATE_LOGICAL_CONFIGURED:
1099d4c0f4fSDavid Gibson         break; /* see below */
1109d4c0f4fSDavid Gibson     case SPAPR_DRC_STATE_LOGICAL_UNISOLATE:
1119d4c0f4fSDavid Gibson         return RTAS_OUT_PARAM_ERROR; /* not allowed */
1129d4c0f4fSDavid Gibson     default:
1139d4c0f4fSDavid Gibson         g_assert_not_reached();
1149d4c0f4fSDavid Gibson     }
1159d4c0f4fSDavid Gibson 
1160dfabd39SDavid Gibson     /*
1170dfabd39SDavid Gibson      * Fail any requests to ISOLATE the LMB DRC if this LMB doesn't
1180dfabd39SDavid Gibson      * belong to a DIMM device that is marked for removal.
1190dfabd39SDavid Gibson      *
1200dfabd39SDavid Gibson      * Currently the guest userspace tool drmgr that drives the memory
1210dfabd39SDavid Gibson      * hotplug/unplug will just try to remove a set of 'removable' LMBs
1220dfabd39SDavid Gibson      * in response to a hot unplug request that is based on drc-count.
1230dfabd39SDavid Gibson      * If the LMB being removed doesn't belong to a DIMM device that is
1240dfabd39SDavid Gibson      * actually being unplugged, fail the isolation request here.
1250dfabd39SDavid Gibson      */
1260dfabd39SDavid Gibson     if (spapr_drc_type(drc) == SPAPR_DR_CONNECTOR_TYPE_LMB
127f1c52354SDavid Gibson         && !drc->unplug_requested) {
1280dfabd39SDavid Gibson         return RTAS_OUT_HW_ERROR;
1290dfabd39SDavid Gibson     }
1300dfabd39SDavid Gibson 
1319d4c0f4fSDavid Gibson     drc->state = SPAPR_DRC_STATE_LOGICAL_AVAILABLE;
1320dfabd39SDavid Gibson 
1330dfabd39SDavid Gibson     /* if we're awaiting release, but still in an unconfigured state,
1340dfabd39SDavid Gibson      * it's likely the guest is still in the process of configuring
1350dfabd39SDavid Gibson      * the device and is transitioning the devices to an ISOLATED
1360dfabd39SDavid Gibson      * state as a part of that process. so we only complete the
1370dfabd39SDavid Gibson      * removal when this transition happens for a device in a
1380dfabd39SDavid Gibson      * configured state, as suggested by the state diagram from PAPR+
1390dfabd39SDavid Gibson      * 2.7, 13.4
1400dfabd39SDavid Gibson      */
141f1c52354SDavid Gibson     if (drc->unplug_requested) {
1420dfabd39SDavid Gibson         uint32_t drc_index = spapr_drc_index(drc);
1430dfabd39SDavid Gibson         trace_spapr_drc_set_isolation_state_finalizing(drc_index);
144a8dc47fdSDavid Gibson         spapr_drc_detach(drc);
1450dfabd39SDavid Gibson     }
1460dfabd39SDavid Gibson     return RTAS_OUT_SUCCESS;
1470dfabd39SDavid Gibson }
1480dfabd39SDavid Gibson 
1490dfabd39SDavid Gibson static uint32_t drc_unisolate_logical(sPAPRDRConnector *drc)
1500dfabd39SDavid Gibson {
1519d4c0f4fSDavid Gibson     switch (drc->state) {
1529d4c0f4fSDavid Gibson     case SPAPR_DRC_STATE_LOGICAL_UNISOLATE:
1539d4c0f4fSDavid Gibson     case SPAPR_DRC_STATE_LOGICAL_CONFIGURED:
1549d4c0f4fSDavid Gibson         return RTAS_OUT_SUCCESS; /* Nothing to do */
1559d4c0f4fSDavid Gibson     case SPAPR_DRC_STATE_LOGICAL_AVAILABLE:
1569d4c0f4fSDavid Gibson         break; /* see below */
1579d4c0f4fSDavid Gibson     case SPAPR_DRC_STATE_LOGICAL_UNUSABLE:
1589d4c0f4fSDavid Gibson         return RTAS_OUT_NO_SUCH_INDICATOR; /* not allowed */
1599d4c0f4fSDavid Gibson     default:
1609d4c0f4fSDavid Gibson         g_assert_not_reached();
1610dfabd39SDavid Gibson     }
1620dfabd39SDavid Gibson 
1639d4c0f4fSDavid Gibson     /* Move to AVAILABLE state should have ensured device was present */
1649d4c0f4fSDavid Gibson     g_assert(drc->dev);
1650dfabd39SDavid Gibson 
1669d4c0f4fSDavid Gibson     drc->state = SPAPR_DRC_STATE_LOGICAL_UNISOLATE;
1674445b1d2SDavid Gibson     drc->ccs_offset = drc->fdt_start_offset;
1684445b1d2SDavid Gibson     drc->ccs_depth = 0;
1694445b1d2SDavid Gibson 
1700cb688d2SMichael Roth     return RTAS_OUT_SUCCESS;
171bbf5c878SMichael Roth }
172bbf5c878SMichael Roth 
17361736732SDavid Gibson static uint32_t drc_set_usable(sPAPRDRConnector *drc)
174bbf5c878SMichael Roth {
1759d4c0f4fSDavid Gibson     switch (drc->state) {
1769d4c0f4fSDavid Gibson     case SPAPR_DRC_STATE_LOGICAL_AVAILABLE:
1779d4c0f4fSDavid Gibson     case SPAPR_DRC_STATE_LOGICAL_UNISOLATE:
1789d4c0f4fSDavid Gibson     case SPAPR_DRC_STATE_LOGICAL_CONFIGURED:
1799d4c0f4fSDavid Gibson         return RTAS_OUT_SUCCESS; /* Nothing to do */
1809d4c0f4fSDavid Gibson     case SPAPR_DRC_STATE_LOGICAL_UNUSABLE:
1819d4c0f4fSDavid Gibson         break; /* see below */
1829d4c0f4fSDavid Gibson     default:
1839d4c0f4fSDavid Gibson         g_assert_not_reached();
1849d4c0f4fSDavid Gibson     }
1859d4c0f4fSDavid Gibson 
1869d1852ceSMichael Roth     /* if there's no resource/device associated with the DRC, there's
1879d1852ceSMichael Roth      * no way for us to put it in an allocation state consistent with
1889d1852ceSMichael Roth      * being 'USABLE'. PAPR 2.7, 13.5.3.4 documents that this should
1899d1852ceSMichael Roth      * result in an RTAS return code of -3 / "no such indicator"
1909d1852ceSMichael Roth      */
1919d1852ceSMichael Roth     if (!drc->dev) {
1929d1852ceSMichael Roth         return RTAS_OUT_NO_SUCH_INDICATOR;
1939d1852ceSMichael Roth     }
194f1c52354SDavid Gibson     if (drc->unplug_requested) {
19582a93a1dSDavid Gibson         /* Don't allow the guest to move a device away from UNUSABLE
19682a93a1dSDavid Gibson          * state when we want to unplug it */
19761736732SDavid Gibson         return RTAS_OUT_NO_SUCH_INDICATOR;
1989d1852ceSMichael Roth     }
1999d1852ceSMichael Roth 
2009d4c0f4fSDavid Gibson     drc->state = SPAPR_DRC_STATE_LOGICAL_AVAILABLE;
20161736732SDavid Gibson 
20261736732SDavid Gibson     return RTAS_OUT_SUCCESS;
20361736732SDavid Gibson }
20461736732SDavid Gibson 
20561736732SDavid Gibson static uint32_t drc_set_unusable(sPAPRDRConnector *drc)
20661736732SDavid Gibson {
2079d4c0f4fSDavid Gibson     switch (drc->state) {
2089d4c0f4fSDavid Gibson     case SPAPR_DRC_STATE_LOGICAL_UNUSABLE:
2099d4c0f4fSDavid Gibson         return RTAS_OUT_SUCCESS; /* Nothing to do */
2109d4c0f4fSDavid Gibson     case SPAPR_DRC_STATE_LOGICAL_AVAILABLE:
2119d4c0f4fSDavid Gibson         break; /* see below */
2129d4c0f4fSDavid Gibson     case SPAPR_DRC_STATE_LOGICAL_UNISOLATE:
2139d4c0f4fSDavid Gibson     case SPAPR_DRC_STATE_LOGICAL_CONFIGURED:
2149d4c0f4fSDavid Gibson         return RTAS_OUT_NO_SUCH_INDICATOR; /* not allowed */
2159d4c0f4fSDavid Gibson     default:
2169d4c0f4fSDavid Gibson         g_assert_not_reached();
2179d4c0f4fSDavid Gibson     }
2189d4c0f4fSDavid Gibson 
2199d4c0f4fSDavid Gibson     drc->state = SPAPR_DRC_STATE_LOGICAL_UNUSABLE;
220f1c52354SDavid Gibson     if (drc->unplug_requested) {
2210b55aa91SDavid Gibson         uint32_t drc_index = spapr_drc_index(drc);
2220b55aa91SDavid Gibson         trace_spapr_drc_set_allocation_state_finalizing(drc_index);
223a8dc47fdSDavid Gibson         spapr_drc_detach(drc);
224bbf5c878SMichael Roth     }
22561736732SDavid Gibson 
2260cb688d2SMichael Roth     return RTAS_OUT_SUCCESS;
227bbf5c878SMichael Roth }
228bbf5c878SMichael Roth 
22979808336SDavid Gibson static const char *spapr_drc_name(sPAPRDRConnector *drc)
230bbf5c878SMichael Roth {
23179808336SDavid Gibson     sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
23279808336SDavid Gibson 
23379808336SDavid Gibson     /* human-readable name for a DRC to encode into the DT
23479808336SDavid Gibson      * description. this is mainly only used within a guest in place
23579808336SDavid Gibson      * of the unique DRC index.
23679808336SDavid Gibson      *
23779808336SDavid Gibson      * in the case of VIO/PCI devices, it corresponds to a "location
23879808336SDavid Gibson      * code" that maps a logical device/function (DRC index) to a
23979808336SDavid Gibson      * physical (or virtual in the case of VIO) location in the system
24079808336SDavid Gibson      * by chaining together the "location label" for each
24179808336SDavid Gibson      * encapsulating component.
24279808336SDavid Gibson      *
24379808336SDavid Gibson      * since this is more to do with diagnosing physical hardware
24479808336SDavid Gibson      * issues than guest compatibility, we choose location codes/DRC
24579808336SDavid Gibson      * names that adhere to the documented format, but avoid encoding
24679808336SDavid Gibson      * the entire topology information into the label/code, instead
24779808336SDavid Gibson      * just using the location codes based on the labels for the
24879808336SDavid Gibson      * endpoints (VIO/PCI adaptor connectors), which is basically just
24979808336SDavid Gibson      * "C" followed by an integer ID.
25079808336SDavid Gibson      *
25179808336SDavid Gibson      * DRC names as documented by PAPR+ v2.7, 13.5.2.4
25279808336SDavid Gibson      * location codes as documented by PAPR+ v2.7, 12.3.1.5
25379808336SDavid Gibson      */
25479808336SDavid Gibson     return g_strdup_printf("%s%d", drck->drc_name_prefix, drc->id);
255bbf5c878SMichael Roth }
256bbf5c878SMichael Roth 
257bbf5c878SMichael Roth /*
258bbf5c878SMichael Roth  * dr-entity-sense sensor value
259bbf5c878SMichael Roth  * returned via get-sensor-state RTAS calls
260bbf5c878SMichael Roth  * as expected by state diagram in PAPR+ 2.7, 13.4
261bbf5c878SMichael Roth  * based on the current allocation/indicator/power states
262bbf5c878SMichael Roth  * for the DR connector.
263bbf5c878SMichael Roth  */
264f224d35bSDavid Gibson static sPAPRDREntitySense physical_entity_sense(sPAPRDRConnector *drc)
265bbf5c878SMichael Roth {
266f224d35bSDavid Gibson     /* this assumes all PCI devices are assigned to a 'live insertion'
267f224d35bSDavid Gibson      * power domain, where QEMU manages power state automatically as
268f224d35bSDavid Gibson      * opposed to the guest. present, non-PCI resources are unaffected
269f224d35bSDavid Gibson      * by power state.
270bbf5c878SMichael Roth      */
271f224d35bSDavid Gibson     return drc->dev ? SPAPR_DR_ENTITY_SENSE_PRESENT
272f224d35bSDavid Gibson         : SPAPR_DR_ENTITY_SENSE_EMPTY;
273bbf5c878SMichael Roth }
274bbf5c878SMichael Roth 
275f224d35bSDavid Gibson static sPAPRDREntitySense logical_entity_sense(sPAPRDRConnector *drc)
276f224d35bSDavid Gibson {
2779d4c0f4fSDavid Gibson     switch (drc->state) {
2789d4c0f4fSDavid Gibson     case SPAPR_DRC_STATE_LOGICAL_UNUSABLE:
279f224d35bSDavid Gibson         return SPAPR_DR_ENTITY_SENSE_UNUSABLE;
2809d4c0f4fSDavid Gibson     case SPAPR_DRC_STATE_LOGICAL_AVAILABLE:
2819d4c0f4fSDavid Gibson     case SPAPR_DRC_STATE_LOGICAL_UNISOLATE:
2829d4c0f4fSDavid Gibson     case SPAPR_DRC_STATE_LOGICAL_CONFIGURED:
2839d4c0f4fSDavid Gibson         g_assert(drc->dev);
2849d4c0f4fSDavid Gibson         return SPAPR_DR_ENTITY_SENSE_PRESENT;
2859d4c0f4fSDavid Gibson     default:
2869d4c0f4fSDavid Gibson         g_assert_not_reached();
287f224d35bSDavid Gibson     }
288bbf5c878SMichael Roth }
289bbf5c878SMichael Roth 
290d7bce999SEric Blake static void prop_get_index(Object *obj, Visitor *v, const char *name,
291d7bce999SEric Blake                            void *opaque, Error **errp)
292bbf5c878SMichael Roth {
293bbf5c878SMichael Roth     sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj);
2940b55aa91SDavid Gibson     uint32_t value = spapr_drc_index(drc);
29551e72bc1SEric Blake     visit_type_uint32(v, name, &value, errp);
296bbf5c878SMichael Roth }
297bbf5c878SMichael Roth 
298d7bce999SEric Blake static void prop_get_fdt(Object *obj, Visitor *v, const char *name,
299d7bce999SEric Blake                          void *opaque, Error **errp)
300bbf5c878SMichael Roth {
301bbf5c878SMichael Roth     sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj);
302d2f95f4dSMarkus Armbruster     QNull *null = NULL;
303c75304a1SMarkus Armbruster     Error *err = NULL;
304bbf5c878SMichael Roth     int fdt_offset_next, fdt_offset, fdt_depth;
305bbf5c878SMichael Roth     void *fdt;
306bbf5c878SMichael Roth 
307bbf5c878SMichael Roth     if (!drc->fdt) {
308d2f95f4dSMarkus Armbruster         visit_type_null(v, NULL, &null, errp);
309cb3e7f08SMarc-André Lureau         qobject_unref(null);
310bbf5c878SMichael Roth         return;
311bbf5c878SMichael Roth     }
312bbf5c878SMichael Roth 
313bbf5c878SMichael Roth     fdt = drc->fdt;
314bbf5c878SMichael Roth     fdt_offset = drc->fdt_start_offset;
315bbf5c878SMichael Roth     fdt_depth = 0;
316bbf5c878SMichael Roth 
317bbf5c878SMichael Roth     do {
318bbf5c878SMichael Roth         const char *name = NULL;
319bbf5c878SMichael Roth         const struct fdt_property *prop = NULL;
320bbf5c878SMichael Roth         int prop_len = 0, name_len = 0;
321bbf5c878SMichael Roth         uint32_t tag;
322bbf5c878SMichael Roth 
323bbf5c878SMichael Roth         tag = fdt_next_tag(fdt, fdt_offset, &fdt_offset_next);
324bbf5c878SMichael Roth         switch (tag) {
325bbf5c878SMichael Roth         case FDT_BEGIN_NODE:
326bbf5c878SMichael Roth             fdt_depth++;
327bbf5c878SMichael Roth             name = fdt_get_name(fdt, fdt_offset, &name_len);
328337283dfSEric Blake             visit_start_struct(v, name, NULL, 0, &err);
329c75304a1SMarkus Armbruster             if (err) {
330c75304a1SMarkus Armbruster                 error_propagate(errp, err);
331c75304a1SMarkus Armbruster                 return;
332c75304a1SMarkus Armbruster             }
333bbf5c878SMichael Roth             break;
334bbf5c878SMichael Roth         case FDT_END_NODE:
335bbf5c878SMichael Roth             /* shouldn't ever see an FDT_END_NODE before FDT_BEGIN_NODE */
336bbf5c878SMichael Roth             g_assert(fdt_depth > 0);
33715c2f669SEric Blake             visit_check_struct(v, &err);
3381158bb2aSEric Blake             visit_end_struct(v, NULL);
339c75304a1SMarkus Armbruster             if (err) {
340c75304a1SMarkus Armbruster                 error_propagate(errp, err);
341c75304a1SMarkus Armbruster                 return;
342c75304a1SMarkus Armbruster             }
343bbf5c878SMichael Roth             fdt_depth--;
344bbf5c878SMichael Roth             break;
345bbf5c878SMichael Roth         case FDT_PROP: {
346bbf5c878SMichael Roth             int i;
347bbf5c878SMichael Roth             prop = fdt_get_property_by_offset(fdt, fdt_offset, &prop_len);
348bbf5c878SMichael Roth             name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
349d9f62ddeSEric Blake             visit_start_list(v, name, NULL, 0, &err);
350c75304a1SMarkus Armbruster             if (err) {
351c75304a1SMarkus Armbruster                 error_propagate(errp, err);
352c75304a1SMarkus Armbruster                 return;
353bbf5c878SMichael Roth             }
354c75304a1SMarkus Armbruster             for (i = 0; i < prop_len; i++) {
35551e72bc1SEric Blake                 visit_type_uint8(v, NULL, (uint8_t *)&prop->data[i], &err);
356c75304a1SMarkus Armbruster                 if (err) {
357c75304a1SMarkus Armbruster                     error_propagate(errp, err);
358c75304a1SMarkus Armbruster                     return;
359c75304a1SMarkus Armbruster                 }
360c75304a1SMarkus Armbruster             }
361a4a1c70dSMarkus Armbruster             visit_check_list(v, &err);
3621158bb2aSEric Blake             visit_end_list(v, NULL);
363a4a1c70dSMarkus Armbruster             if (err) {
364a4a1c70dSMarkus Armbruster                 error_propagate(errp, err);
365a4a1c70dSMarkus Armbruster                 return;
366a4a1c70dSMarkus Armbruster             }
367bbf5c878SMichael Roth             break;
368bbf5c878SMichael Roth         }
369bbf5c878SMichael Roth         default:
370e20c6314SPhilippe Mathieu-Daudé             error_report("device FDT in unexpected state: %d", tag);
371e20c6314SPhilippe Mathieu-Daudé             abort();
372bbf5c878SMichael Roth         }
373bbf5c878SMichael Roth         fdt_offset = fdt_offset_next;
374bbf5c878SMichael Roth     } while (fdt_depth != 0);
375bbf5c878SMichael Roth }
376bbf5c878SMichael Roth 
3770be4e886SDavid Gibson void spapr_drc_attach(sPAPRDRConnector *drc, DeviceState *d, void *fdt,
3785c1da812SDavid Gibson                       int fdt_start_offset, Error **errp)
379bbf5c878SMichael Roth {
380d9c95c71SGreg Kurz     sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
381d9c95c71SGreg Kurz 
3820b55aa91SDavid Gibson     trace_spapr_drc_attach(spapr_drc_index(drc));
383bbf5c878SMichael Roth 
3849d4c0f4fSDavid Gibson     if (drc->dev) {
385bbf5c878SMichael Roth         error_setg(errp, "an attached device is still awaiting release");
386bbf5c878SMichael Roth         return;
387bbf5c878SMichael Roth     }
3889d4c0f4fSDavid Gibson     g_assert((drc->state == SPAPR_DRC_STATE_LOGICAL_UNUSABLE)
3899d4c0f4fSDavid Gibson              || (drc->state == SPAPR_DRC_STATE_PHYSICAL_POWERON));
390d9c95c71SGreg Kurz     g_assert(fdt || drck->dt_populate);
391bbf5c878SMichael Roth 
392bbf5c878SMichael Roth     drc->dev = d;
393d9c95c71SGreg Kurz 
394d9c95c71SGreg Kurz     if (fdt) {
395bbf5c878SMichael Roth         drc->fdt = fdt;
396bbf5c878SMichael Roth         drc->fdt_start_offset = fdt_start_offset;
397d9c95c71SGreg Kurz     }
398bbf5c878SMichael Roth 
399bbf5c878SMichael Roth     object_property_add_link(OBJECT(drc), "device",
400bbf5c878SMichael Roth                              object_get_typename(OBJECT(drc->dev)),
401bbf5c878SMichael Roth                              (Object **)(&drc->dev),
402bbf5c878SMichael Roth                              NULL, 0, NULL);
403bbf5c878SMichael Roth }
404bbf5c878SMichael Roth 
4059c914e53SDavid Gibson static void spapr_drc_release(sPAPRDRConnector *drc)
406bbf5c878SMichael Roth {
4076b762f29SDavid Gibson     sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
4086b762f29SDavid Gibson 
4096b762f29SDavid Gibson     drck->release(drc->dev);
410bbf5c878SMichael Roth 
411f1c52354SDavid Gibson     drc->unplug_requested = false;
412bbf5c878SMichael Roth     g_free(drc->fdt);
413bbf5c878SMichael Roth     drc->fdt = NULL;
414bbf5c878SMichael Roth     drc->fdt_start_offset = 0;
415ba50822fSDavid Gibson     object_property_del(OBJECT(drc), "device", &error_abort);
416bbf5c878SMichael Roth     drc->dev = NULL;
417bbf5c878SMichael Roth }
418bbf5c878SMichael Roth 
419a8dc47fdSDavid Gibson void spapr_drc_detach(sPAPRDRConnector *drc)
4209c914e53SDavid Gibson {
4219d4c0f4fSDavid Gibson     sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
4229d4c0f4fSDavid Gibson 
4239c914e53SDavid Gibson     trace_spapr_drc_detach(spapr_drc_index(drc));
4249c914e53SDavid Gibson 
4259d4c0f4fSDavid Gibson     g_assert(drc->dev);
4269d4c0f4fSDavid Gibson 
427f1c52354SDavid Gibson     drc->unplug_requested = true;
428a8dc47fdSDavid Gibson 
4299d4c0f4fSDavid Gibson     if (drc->state != drck->empty_state) {
4309d4c0f4fSDavid Gibson         trace_spapr_drc_awaiting_quiesce(spapr_drc_index(drc));
4319c914e53SDavid Gibson         return;
4329c914e53SDavid Gibson     }
4339c914e53SDavid Gibson 
4349c914e53SDavid Gibson     spapr_drc_release(drc);
4359c914e53SDavid Gibson }
4369c914e53SDavid Gibson 
43794fd9cbaSLaurent Vivier void spapr_drc_reset(sPAPRDRConnector *drc)
438bbf5c878SMichael Roth {
4399d4c0f4fSDavid Gibson     sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
4409d4c0f4fSDavid Gibson 
4410b55aa91SDavid Gibson     trace_spapr_drc_reset(spapr_drc_index(drc));
442b8fdd530SDavid Gibson 
443bbf5c878SMichael Roth     /* immediately upon reset we can safely assume DRCs whose devices
4444f9242fcSDavid Gibson      * are pending removal can be safely removed.
445bbf5c878SMichael Roth      */
446f1c52354SDavid Gibson     if (drc->unplug_requested) {
4474f9242fcSDavid Gibson         spapr_drc_release(drc);
448bbf5c878SMichael Roth     }
449bbf5c878SMichael Roth 
4504f9242fcSDavid Gibson     if (drc->dev) {
4519d4c0f4fSDavid Gibson         /* A device present at reset is ready to go, same as coldplugged */
4529d4c0f4fSDavid Gibson         drc->state = drck->ready_state;
453188bfe1bSBharata B Rao         /*
454188bfe1bSBharata B Rao          * Ensure that we are able to send the FDT fragment again
455188bfe1bSBharata B Rao          * via configure-connector call if the guest requests.
456188bfe1bSBharata B Rao          */
457188bfe1bSBharata B Rao         drc->ccs_offset = drc->fdt_start_offset;
458188bfe1bSBharata B Rao         drc->ccs_depth = 0;
4594f9242fcSDavid Gibson     } else {
4609d4c0f4fSDavid Gibson         drc->state = drck->empty_state;
4614445b1d2SDavid Gibson         drc->ccs_offset = -1;
4624445b1d2SDavid Gibson         drc->ccs_depth = -1;
463bbf5c878SMichael Roth     }
464188bfe1bSBharata B Rao }
465bbf5c878SMichael Roth 
46610f12e64SDaniel Henrique Barboza bool spapr_drc_needed(void *opaque)
467a50919ddSDaniel Henrique Barboza {
468a50919ddSDaniel Henrique Barboza     sPAPRDRConnector *drc = (sPAPRDRConnector *)opaque;
469a50919ddSDaniel Henrique Barboza     sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
470a50919ddSDaniel Henrique Barboza 
471a50919ddSDaniel Henrique Barboza     /* If no dev is plugged in there is no need to migrate the DRC state */
472c618e300SDaniel Henrique Barboza     if (!drc->dev) {
473a50919ddSDaniel Henrique Barboza         return false;
474a50919ddSDaniel Henrique Barboza     }
475a50919ddSDaniel Henrique Barboza 
476a50919ddSDaniel Henrique Barboza     /*
4779d4c0f4fSDavid Gibson      * We need to migrate the state if it's not equal to the expected
4789d4c0f4fSDavid Gibson      * long-term state, which is the same as the coldplugged initial
4799d4c0f4fSDavid Gibson      * state */
4809d4c0f4fSDavid Gibson     return (drc->state != drck->ready_state);
481a50919ddSDaniel Henrique Barboza }
482a50919ddSDaniel Henrique Barboza 
483a50919ddSDaniel Henrique Barboza static const VMStateDescription vmstate_spapr_drc = {
484a50919ddSDaniel Henrique Barboza     .name = "spapr_drc",
485a50919ddSDaniel Henrique Barboza     .version_id = 1,
486a50919ddSDaniel Henrique Barboza     .minimum_version_id = 1,
487a50919ddSDaniel Henrique Barboza     .needed = spapr_drc_needed,
488a50919ddSDaniel Henrique Barboza     .fields  = (VMStateField []) {
4899d4c0f4fSDavid Gibson         VMSTATE_UINT32(state, sPAPRDRConnector),
490a50919ddSDaniel Henrique Barboza         VMSTATE_END_OF_LIST()
491a50919ddSDaniel Henrique Barboza     }
492a50919ddSDaniel Henrique Barboza };
493a50919ddSDaniel Henrique Barboza 
494bbf5c878SMichael Roth static void realize(DeviceState *d, Error **errp)
495bbf5c878SMichael Roth {
496bbf5c878SMichael Roth     sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(d);
497bbf5c878SMichael Roth     Object *root_container;
498f5babeacSGreg Kurz     gchar *link_name;
499bbf5c878SMichael Roth     gchar *child_name;
500bbf5c878SMichael Roth     Error *err = NULL;
501bbf5c878SMichael Roth 
5020b55aa91SDavid Gibson     trace_spapr_drc_realize(spapr_drc_index(drc));
503bbf5c878SMichael Roth     /* NOTE: we do this as part of realize/unrealize due to the fact
504bbf5c878SMichael Roth      * that the guest will communicate with the DRC via RTAS calls
505bbf5c878SMichael Roth      * referencing the global DRC index. By unlinking the DRC
506bbf5c878SMichael Roth      * from DRC_CONTAINER_PATH/<drc_index> we effectively make it
507bbf5c878SMichael Roth      * inaccessible by the guest, since lookups rely on this path
508bbf5c878SMichael Roth      * existing in the composition tree
509bbf5c878SMichael Roth      */
510bbf5c878SMichael Roth     root_container = container_get(object_get_root(), DRC_CONTAINER_PATH);
511f5babeacSGreg Kurz     link_name = g_strdup_printf("%x", spapr_drc_index(drc));
512bbf5c878SMichael Roth     child_name = object_get_canonical_path_component(OBJECT(drc));
5130b55aa91SDavid Gibson     trace_spapr_drc_realize_child(spapr_drc_index(drc), child_name);
514bbf5c878SMichael Roth     object_property_add_alias(root_container, link_name,
515bbf5c878SMichael Roth                               drc->owner, child_name, &err);
516586d2142SGonglei     g_free(child_name);
517f5babeacSGreg Kurz     g_free(link_name);
518bf26ae32SGreg Kurz     if (err) {
519bf26ae32SGreg Kurz         error_propagate(errp, err);
520bf26ae32SGreg Kurz         return;
521bf26ae32SGreg Kurz     }
5220b55aa91SDavid Gibson     vmstate_register(DEVICE(drc), spapr_drc_index(drc), &vmstate_spapr_drc,
523a50919ddSDaniel Henrique Barboza                      drc);
5240b55aa91SDavid Gibson     trace_spapr_drc_realize_complete(spapr_drc_index(drc));
525bbf5c878SMichael Roth }
526bbf5c878SMichael Roth 
527bbf5c878SMichael Roth static void unrealize(DeviceState *d, Error **errp)
528bbf5c878SMichael Roth {
529bbf5c878SMichael Roth     sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(d);
530bbf5c878SMichael Roth     Object *root_container;
531f5babeacSGreg Kurz     gchar *name;
532bbf5c878SMichael Roth 
5330b55aa91SDavid Gibson     trace_spapr_drc_unrealize(spapr_drc_index(drc));
534bf26ae32SGreg Kurz     vmstate_unregister(DEVICE(drc), &vmstate_spapr_drc, drc);
535bbf5c878SMichael Roth     root_container = container_get(object_get_root(), DRC_CONTAINER_PATH);
536f5babeacSGreg Kurz     name = g_strdup_printf("%x", spapr_drc_index(drc));
537bf26ae32SGreg Kurz     object_property_del(root_container, name, errp);
538f5babeacSGreg Kurz     g_free(name);
539bbf5c878SMichael Roth }
540bbf5c878SMichael Roth 
5412d335818SDavid Gibson sPAPRDRConnector *spapr_dr_connector_new(Object *owner, const char *type,
542bbf5c878SMichael Roth                                          uint32_t id)
543bbf5c878SMichael Roth {
5442d335818SDavid Gibson     sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(object_new(type));
54594649d42SDavid Gibson     char *prop_name;
546bbf5c878SMichael Roth 
547bbf5c878SMichael Roth     drc->id = id;
548bbf5c878SMichael Roth     drc->owner = owner;
5490b55aa91SDavid Gibson     prop_name = g_strdup_printf("dr-connector[%"PRIu32"]",
5500b55aa91SDavid Gibson                                 spapr_drc_index(drc));
551325837caSGreg Kurz     object_property_add_child(owner, prop_name, OBJECT(drc), &error_abort);
552f3f41030SMichael Roth     object_unref(OBJECT(drc));
553bbf5c878SMichael Roth     object_property_set_bool(OBJECT(drc), true, "realized", NULL);
55494649d42SDavid Gibson     g_free(prop_name);
555bbf5c878SMichael Roth 
556bbf5c878SMichael Roth     return drc;
557bbf5c878SMichael Roth }
558bbf5c878SMichael Roth 
559bbf5c878SMichael Roth static void spapr_dr_connector_instance_init(Object *obj)
560bbf5c878SMichael Roth {
561bbf5c878SMichael Roth     sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj);
5629d4c0f4fSDavid Gibson     sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
563bbf5c878SMichael Roth 
564bbf5c878SMichael Roth     object_property_add_uint32_ptr(obj, "id", &drc->id, NULL);
565bbf5c878SMichael Roth     object_property_add(obj, "index", "uint32", prop_get_index,
566bbf5c878SMichael Roth                         NULL, NULL, NULL, NULL);
567bbf5c878SMichael Roth     object_property_add(obj, "fdt", "struct", prop_get_fdt,
568bbf5c878SMichael Roth                         NULL, NULL, NULL, NULL);
5699d4c0f4fSDavid Gibson     drc->state = drck->empty_state;
570bbf5c878SMichael Roth }
571bbf5c878SMichael Roth 
572bbf5c878SMichael Roth static void spapr_dr_connector_class_init(ObjectClass *k, void *data)
573bbf5c878SMichael Roth {
574bbf5c878SMichael Roth     DeviceClass *dk = DEVICE_CLASS(k);
575bbf5c878SMichael Roth 
576bbf5c878SMichael Roth     dk->realize = realize;
577bbf5c878SMichael Roth     dk->unrealize = unrealize;
578c401ae8cSMarkus Armbruster     /*
579c401ae8cSMarkus Armbruster      * Reason: it crashes FIXME find and document the real reason
580c401ae8cSMarkus Armbruster      */
581e90f2a8cSEduardo Habkost     dk->user_creatable = false;
582bbf5c878SMichael Roth }
583bbf5c878SMichael Roth 
58467fea71bSDavid Gibson static bool drc_physical_needed(void *opaque)
58567fea71bSDavid Gibson {
58667fea71bSDavid Gibson     sPAPRDRCPhysical *drcp = (sPAPRDRCPhysical *)opaque;
58767fea71bSDavid Gibson     sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(drcp);
58867fea71bSDavid Gibson 
58967fea71bSDavid Gibson     if ((drc->dev && (drcp->dr_indicator == SPAPR_DR_INDICATOR_ACTIVE))
59067fea71bSDavid Gibson         || (!drc->dev && (drcp->dr_indicator == SPAPR_DR_INDICATOR_INACTIVE))) {
59167fea71bSDavid Gibson         return false;
59267fea71bSDavid Gibson     }
59367fea71bSDavid Gibson     return true;
59467fea71bSDavid Gibson }
59567fea71bSDavid Gibson 
59667fea71bSDavid Gibson static const VMStateDescription vmstate_spapr_drc_physical = {
59767fea71bSDavid Gibson     .name = "spapr_drc/physical",
59867fea71bSDavid Gibson     .version_id = 1,
59967fea71bSDavid Gibson     .minimum_version_id = 1,
60067fea71bSDavid Gibson     .needed = drc_physical_needed,
60167fea71bSDavid Gibson     .fields  = (VMStateField []) {
60267fea71bSDavid Gibson         VMSTATE_UINT32(dr_indicator, sPAPRDRCPhysical),
60367fea71bSDavid Gibson         VMSTATE_END_OF_LIST()
60467fea71bSDavid Gibson     }
60567fea71bSDavid Gibson };
60667fea71bSDavid Gibson 
60767fea71bSDavid Gibson static void drc_physical_reset(void *opaque)
60867fea71bSDavid Gibson {
60967fea71bSDavid Gibson     sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(opaque);
61067fea71bSDavid Gibson     sPAPRDRCPhysical *drcp = SPAPR_DRC_PHYSICAL(drc);
61167fea71bSDavid Gibson 
61267fea71bSDavid Gibson     if (drc->dev) {
61367fea71bSDavid Gibson         drcp->dr_indicator = SPAPR_DR_INDICATOR_ACTIVE;
61467fea71bSDavid Gibson     } else {
61567fea71bSDavid Gibson         drcp->dr_indicator = SPAPR_DR_INDICATOR_INACTIVE;
61667fea71bSDavid Gibson     }
61767fea71bSDavid Gibson }
61867fea71bSDavid Gibson 
61967fea71bSDavid Gibson static void realize_physical(DeviceState *d, Error **errp)
62067fea71bSDavid Gibson {
62167fea71bSDavid Gibson     sPAPRDRCPhysical *drcp = SPAPR_DRC_PHYSICAL(d);
62267fea71bSDavid Gibson     Error *local_err = NULL;
62367fea71bSDavid Gibson 
62467fea71bSDavid Gibson     realize(d, &local_err);
62567fea71bSDavid Gibson     if (local_err) {
62667fea71bSDavid Gibson         error_propagate(errp, local_err);
62767fea71bSDavid Gibson         return;
62867fea71bSDavid Gibson     }
62967fea71bSDavid Gibson 
63067fea71bSDavid Gibson     vmstate_register(DEVICE(drcp), spapr_drc_index(SPAPR_DR_CONNECTOR(drcp)),
63167fea71bSDavid Gibson                      &vmstate_spapr_drc_physical, drcp);
63267fea71bSDavid Gibson     qemu_register_reset(drc_physical_reset, drcp);
63367fea71bSDavid Gibson }
63467fea71bSDavid Gibson 
635379ae096SGreg Kurz static void unrealize_physical(DeviceState *d, Error **errp)
636379ae096SGreg Kurz {
637379ae096SGreg Kurz     sPAPRDRCPhysical *drcp = SPAPR_DRC_PHYSICAL(d);
638379ae096SGreg Kurz     Error *local_err = NULL;
639379ae096SGreg Kurz 
640379ae096SGreg Kurz     unrealize(d, &local_err);
641379ae096SGreg Kurz     if (local_err) {
642379ae096SGreg Kurz         error_propagate(errp, local_err);
643379ae096SGreg Kurz         return;
644379ae096SGreg Kurz     }
645379ae096SGreg Kurz 
646379ae096SGreg Kurz     vmstate_unregister(DEVICE(drcp), &vmstate_spapr_drc_physical, drcp);
647379ae096SGreg Kurz     qemu_unregister_reset(drc_physical_reset, drcp);
648379ae096SGreg Kurz }
649379ae096SGreg Kurz 
650f224d35bSDavid Gibson static void spapr_drc_physical_class_init(ObjectClass *k, void *data)
651f224d35bSDavid Gibson {
65267fea71bSDavid Gibson     DeviceClass *dk = DEVICE_CLASS(k);
653f224d35bSDavid Gibson     sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_CLASS(k);
654f224d35bSDavid Gibson 
65567fea71bSDavid Gibson     dk->realize = realize_physical;
656379ae096SGreg Kurz     dk->unrealize = unrealize_physical;
657f224d35bSDavid Gibson     drck->dr_entity_sense = physical_entity_sense;
6580dfabd39SDavid Gibson     drck->isolate = drc_isolate_physical;
6590dfabd39SDavid Gibson     drck->unisolate = drc_unisolate_physical;
6609d4c0f4fSDavid Gibson     drck->ready_state = SPAPR_DRC_STATE_PHYSICAL_CONFIGURED;
6619d4c0f4fSDavid Gibson     drck->empty_state = SPAPR_DRC_STATE_PHYSICAL_POWERON;
662f224d35bSDavid Gibson }
663f224d35bSDavid Gibson 
664f224d35bSDavid Gibson static void spapr_drc_logical_class_init(ObjectClass *k, void *data)
665f224d35bSDavid Gibson {
666f224d35bSDavid Gibson     sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_CLASS(k);
667f224d35bSDavid Gibson 
668f224d35bSDavid Gibson     drck->dr_entity_sense = logical_entity_sense;
6690dfabd39SDavid Gibson     drck->isolate = drc_isolate_logical;
6700dfabd39SDavid Gibson     drck->unisolate = drc_unisolate_logical;
6719d4c0f4fSDavid Gibson     drck->ready_state = SPAPR_DRC_STATE_LOGICAL_CONFIGURED;
6729d4c0f4fSDavid Gibson     drck->empty_state = SPAPR_DRC_STATE_LOGICAL_UNUSABLE;
673f224d35bSDavid Gibson }
674f224d35bSDavid Gibson 
6752d335818SDavid Gibson static void spapr_drc_cpu_class_init(ObjectClass *k, void *data)
6762d335818SDavid Gibson {
6772d335818SDavid Gibson     sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_CLASS(k);
6782d335818SDavid Gibson 
6792d335818SDavid Gibson     drck->typeshift = SPAPR_DR_CONNECTOR_TYPE_SHIFT_CPU;
6801693ea16SDavid Gibson     drck->typename = "CPU";
68179808336SDavid Gibson     drck->drc_name_prefix = "CPU ";
6826b762f29SDavid Gibson     drck->release = spapr_core_release;
6832d335818SDavid Gibson }
6842d335818SDavid Gibson 
6852d335818SDavid Gibson static void spapr_drc_pci_class_init(ObjectClass *k, void *data)
6862d335818SDavid Gibson {
6872d335818SDavid Gibson     sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_CLASS(k);
6882d335818SDavid Gibson 
6892d335818SDavid Gibson     drck->typeshift = SPAPR_DR_CONNECTOR_TYPE_SHIFT_PCI;
6901693ea16SDavid Gibson     drck->typename = "28";
69179808336SDavid Gibson     drck->drc_name_prefix = "C";
6926b762f29SDavid Gibson     drck->release = spapr_phb_remove_pci_device_cb;
6932d335818SDavid Gibson }
6942d335818SDavid Gibson 
6952d335818SDavid Gibson static void spapr_drc_lmb_class_init(ObjectClass *k, void *data)
6962d335818SDavid Gibson {
6972d335818SDavid Gibson     sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_CLASS(k);
6982d335818SDavid Gibson 
6992d335818SDavid Gibson     drck->typeshift = SPAPR_DR_CONNECTOR_TYPE_SHIFT_LMB;
7001693ea16SDavid Gibson     drck->typename = "MEM";
70179808336SDavid Gibson     drck->drc_name_prefix = "LMB ";
7026b762f29SDavid Gibson     drck->release = spapr_lmb_release;
703*62d38c9bSGreg Kurz     drck->dt_populate = spapr_lmb_dt_populate;
7042d335818SDavid Gibson }
7052d335818SDavid Gibson 
706bbf5c878SMichael Roth static const TypeInfo spapr_dr_connector_info = {
707bbf5c878SMichael Roth     .name          = TYPE_SPAPR_DR_CONNECTOR,
708bbf5c878SMichael Roth     .parent        = TYPE_DEVICE,
709bbf5c878SMichael Roth     .instance_size = sizeof(sPAPRDRConnector),
710bbf5c878SMichael Roth     .instance_init = spapr_dr_connector_instance_init,
711bbf5c878SMichael Roth     .class_size    = sizeof(sPAPRDRConnectorClass),
712bbf5c878SMichael Roth     .class_init    = spapr_dr_connector_class_init,
7132d335818SDavid Gibson     .abstract      = true,
7142d335818SDavid Gibson };
7152d335818SDavid Gibson 
7162d335818SDavid Gibson static const TypeInfo spapr_drc_physical_info = {
7172d335818SDavid Gibson     .name          = TYPE_SPAPR_DRC_PHYSICAL,
7182d335818SDavid Gibson     .parent        = TYPE_SPAPR_DR_CONNECTOR,
71967fea71bSDavid Gibson     .instance_size = sizeof(sPAPRDRCPhysical),
720f224d35bSDavid Gibson     .class_init    = spapr_drc_physical_class_init,
7212d335818SDavid Gibson     .abstract      = true,
7222d335818SDavid Gibson };
7232d335818SDavid Gibson 
7242d335818SDavid Gibson static const TypeInfo spapr_drc_logical_info = {
7252d335818SDavid Gibson     .name          = TYPE_SPAPR_DRC_LOGICAL,
7262d335818SDavid Gibson     .parent        = TYPE_SPAPR_DR_CONNECTOR,
727f224d35bSDavid Gibson     .class_init    = spapr_drc_logical_class_init,
7282d335818SDavid Gibson     .abstract      = true,
7292d335818SDavid Gibson };
7302d335818SDavid Gibson 
7312d335818SDavid Gibson static const TypeInfo spapr_drc_cpu_info = {
7322d335818SDavid Gibson     .name          = TYPE_SPAPR_DRC_CPU,
7332d335818SDavid Gibson     .parent        = TYPE_SPAPR_DRC_LOGICAL,
7342d335818SDavid Gibson     .class_init    = spapr_drc_cpu_class_init,
7352d335818SDavid Gibson };
7362d335818SDavid Gibson 
7372d335818SDavid Gibson static const TypeInfo spapr_drc_pci_info = {
7382d335818SDavid Gibson     .name          = TYPE_SPAPR_DRC_PCI,
7392d335818SDavid Gibson     .parent        = TYPE_SPAPR_DRC_PHYSICAL,
7402d335818SDavid Gibson     .class_init    = spapr_drc_pci_class_init,
7412d335818SDavid Gibson };
7422d335818SDavid Gibson 
7432d335818SDavid Gibson static const TypeInfo spapr_drc_lmb_info = {
7442d335818SDavid Gibson     .name          = TYPE_SPAPR_DRC_LMB,
7452d335818SDavid Gibson     .parent        = TYPE_SPAPR_DRC_LOGICAL,
7462d335818SDavid Gibson     .class_init    = spapr_drc_lmb_class_init,
747bbf5c878SMichael Roth };
748bbf5c878SMichael Roth 
749bbf5c878SMichael Roth /* helper functions for external users */
750bbf5c878SMichael Roth 
751fbf55397SDavid Gibson sPAPRDRConnector *spapr_drc_by_index(uint32_t index)
752bbf5c878SMichael Roth {
753bbf5c878SMichael Roth     Object *obj;
754f5babeacSGreg Kurz     gchar *name;
755bbf5c878SMichael Roth 
756f5babeacSGreg Kurz     name = g_strdup_printf("%s/%x", DRC_CONTAINER_PATH, index);
757bbf5c878SMichael Roth     obj = object_resolve_path(name, NULL);
758f5babeacSGreg Kurz     g_free(name);
759bbf5c878SMichael Roth 
760bbf5c878SMichael Roth     return !obj ? NULL : SPAPR_DR_CONNECTOR(obj);
761bbf5c878SMichael Roth }
762bbf5c878SMichael Roth 
763fbf55397SDavid Gibson sPAPRDRConnector *spapr_drc_by_id(const char *type, uint32_t id)
764bbf5c878SMichael Roth {
765fbf55397SDavid Gibson     sPAPRDRConnectorClass *drck
766fbf55397SDavid Gibson         = SPAPR_DR_CONNECTOR_CLASS(object_class_by_name(type));
767fbf55397SDavid Gibson 
768fbf55397SDavid Gibson     return spapr_drc_by_index(drck->typeshift << DRC_INDEX_TYPE_SHIFT
769fbf55397SDavid Gibson                               | (id & DRC_INDEX_ID_MASK));
770bbf5c878SMichael Roth }
771e4b798bbSMichael Roth 
772e4b798bbSMichael Roth /**
773e4b798bbSMichael Roth  * spapr_drc_populate_dt
774e4b798bbSMichael Roth  *
775e4b798bbSMichael Roth  * @fdt: libfdt device tree
776e4b798bbSMichael Roth  * @path: path in the DT to generate properties
777e4b798bbSMichael Roth  * @owner: parent Object/DeviceState for which to generate DRC
778e4b798bbSMichael Roth  *         descriptions for
779e4b798bbSMichael Roth  * @drc_type_mask: mask of sPAPRDRConnectorType values corresponding
780e4b798bbSMichael Roth  *   to the types of DRCs to generate entries for
781e4b798bbSMichael Roth  *
782e4b798bbSMichael Roth  * generate OF properties to describe DRC topology/indices to guests
783e4b798bbSMichael Roth  *
784e4b798bbSMichael Roth  * as documented in PAPR+ v2.1, 13.5.2
785e4b798bbSMichael Roth  */
786e4b798bbSMichael Roth int spapr_drc_populate_dt(void *fdt, int fdt_offset, Object *owner,
787e4b798bbSMichael Roth                           uint32_t drc_type_mask)
788e4b798bbSMichael Roth {
789e4b798bbSMichael Roth     Object *root_container;
790e4b798bbSMichael Roth     ObjectProperty *prop;
7917746abd8SDaniel P. Berrange     ObjectPropertyIterator iter;
792e4b798bbSMichael Roth     uint32_t drc_count = 0;
793e4b798bbSMichael Roth     GArray *drc_indexes, *drc_power_domains;
794e4b798bbSMichael Roth     GString *drc_names, *drc_types;
795e4b798bbSMichael Roth     int ret;
796e4b798bbSMichael Roth 
797e4b798bbSMichael Roth     /* the first entry of each properties is a 32-bit integer encoding
798e4b798bbSMichael Roth      * the number of elements in the array. we won't know this until
799e4b798bbSMichael Roth      * we complete the iteration through all the matching DRCs, but
800e4b798bbSMichael Roth      * reserve the space now and set the offsets accordingly so we
801e4b798bbSMichael Roth      * can fill them in later.
802e4b798bbSMichael Roth      */
803e4b798bbSMichael Roth     drc_indexes = g_array_new(false, true, sizeof(uint32_t));
804e4b798bbSMichael Roth     drc_indexes = g_array_set_size(drc_indexes, 1);
805e4b798bbSMichael Roth     drc_power_domains = g_array_new(false, true, sizeof(uint32_t));
806e4b798bbSMichael Roth     drc_power_domains = g_array_set_size(drc_power_domains, 1);
807e4b798bbSMichael Roth     drc_names = g_string_set_size(g_string_new(NULL), sizeof(uint32_t));
808e4b798bbSMichael Roth     drc_types = g_string_set_size(g_string_new(NULL), sizeof(uint32_t));
809e4b798bbSMichael Roth 
810e4b798bbSMichael Roth     /* aliases for all DRConnector objects will be rooted in QOM
811e4b798bbSMichael Roth      * composition tree at DRC_CONTAINER_PATH
812e4b798bbSMichael Roth      */
813e4b798bbSMichael Roth     root_container = container_get(object_get_root(), DRC_CONTAINER_PATH);
814e4b798bbSMichael Roth 
8157746abd8SDaniel P. Berrange     object_property_iter_init(&iter, root_container);
8167746abd8SDaniel P. Berrange     while ((prop = object_property_iter_next(&iter))) {
817e4b798bbSMichael Roth         Object *obj;
818e4b798bbSMichael Roth         sPAPRDRConnector *drc;
819e4b798bbSMichael Roth         sPAPRDRConnectorClass *drck;
820e4b798bbSMichael Roth         uint32_t drc_index, drc_power_domain;
821e4b798bbSMichael Roth 
822e4b798bbSMichael Roth         if (!strstart(prop->type, "link<", NULL)) {
823e4b798bbSMichael Roth             continue;
824e4b798bbSMichael Roth         }
825e4b798bbSMichael Roth 
826e4b798bbSMichael Roth         obj = object_property_get_link(root_container, prop->name, NULL);
827e4b798bbSMichael Roth         drc = SPAPR_DR_CONNECTOR(obj);
828e4b798bbSMichael Roth         drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
829e4b798bbSMichael Roth 
830e4b798bbSMichael Roth         if (owner && (drc->owner != owner)) {
831e4b798bbSMichael Roth             continue;
832e4b798bbSMichael Roth         }
833e4b798bbSMichael Roth 
8342d335818SDavid Gibson         if ((spapr_drc_type(drc) & drc_type_mask) == 0) {
835e4b798bbSMichael Roth             continue;
836e4b798bbSMichael Roth         }
837e4b798bbSMichael Roth 
838e4b798bbSMichael Roth         drc_count++;
839e4b798bbSMichael Roth 
840e4b798bbSMichael Roth         /* ibm,drc-indexes */
8410b55aa91SDavid Gibson         drc_index = cpu_to_be32(spapr_drc_index(drc));
842e4b798bbSMichael Roth         g_array_append_val(drc_indexes, drc_index);
843e4b798bbSMichael Roth 
844e4b798bbSMichael Roth         /* ibm,drc-power-domains */
845e4b798bbSMichael Roth         drc_power_domain = cpu_to_be32(-1);
846e4b798bbSMichael Roth         g_array_append_val(drc_power_domains, drc_power_domain);
847e4b798bbSMichael Roth 
848e4b798bbSMichael Roth         /* ibm,drc-names */
84979808336SDavid Gibson         drc_names = g_string_append(drc_names, spapr_drc_name(drc));
850e4b798bbSMichael Roth         drc_names = g_string_insert_len(drc_names, -1, "\0", 1);
851e4b798bbSMichael Roth 
852e4b798bbSMichael Roth         /* ibm,drc-types */
8531693ea16SDavid Gibson         drc_types = g_string_append(drc_types, drck->typename);
854e4b798bbSMichael Roth         drc_types = g_string_insert_len(drc_types, -1, "\0", 1);
855e4b798bbSMichael Roth     }
856e4b798bbSMichael Roth 
857e4b798bbSMichael Roth     /* now write the drc count into the space we reserved at the
858e4b798bbSMichael Roth      * beginning of the arrays previously
859e4b798bbSMichael Roth      */
860e4b798bbSMichael Roth     *(uint32_t *)drc_indexes->data = cpu_to_be32(drc_count);
861e4b798bbSMichael Roth     *(uint32_t *)drc_power_domains->data = cpu_to_be32(drc_count);
862e4b798bbSMichael Roth     *(uint32_t *)drc_names->str = cpu_to_be32(drc_count);
863e4b798bbSMichael Roth     *(uint32_t *)drc_types->str = cpu_to_be32(drc_count);
864e4b798bbSMichael Roth 
865e4b798bbSMichael Roth     ret = fdt_setprop(fdt, fdt_offset, "ibm,drc-indexes",
866e4b798bbSMichael Roth                       drc_indexes->data,
867e4b798bbSMichael Roth                       drc_indexes->len * sizeof(uint32_t));
868e4b798bbSMichael Roth     if (ret) {
869ce9863b7SCédric Le Goater         error_report("Couldn't create ibm,drc-indexes property");
870e4b798bbSMichael Roth         goto out;
871e4b798bbSMichael Roth     }
872e4b798bbSMichael Roth 
873e4b798bbSMichael Roth     ret = fdt_setprop(fdt, fdt_offset, "ibm,drc-power-domains",
874e4b798bbSMichael Roth                       drc_power_domains->data,
875e4b798bbSMichael Roth                       drc_power_domains->len * sizeof(uint32_t));
876e4b798bbSMichael Roth     if (ret) {
877ce9863b7SCédric Le Goater         error_report("Couldn't finalize ibm,drc-power-domains property");
878e4b798bbSMichael Roth         goto out;
879e4b798bbSMichael Roth     }
880e4b798bbSMichael Roth 
881e4b798bbSMichael Roth     ret = fdt_setprop(fdt, fdt_offset, "ibm,drc-names",
882e4b798bbSMichael Roth                       drc_names->str, drc_names->len);
883e4b798bbSMichael Roth     if (ret) {
884ce9863b7SCédric Le Goater         error_report("Couldn't finalize ibm,drc-names property");
885e4b798bbSMichael Roth         goto out;
886e4b798bbSMichael Roth     }
887e4b798bbSMichael Roth 
888e4b798bbSMichael Roth     ret = fdt_setprop(fdt, fdt_offset, "ibm,drc-types",
889e4b798bbSMichael Roth                       drc_types->str, drc_types->len);
890e4b798bbSMichael Roth     if (ret) {
891ce9863b7SCédric Le Goater         error_report("Couldn't finalize ibm,drc-types property");
892e4b798bbSMichael Roth         goto out;
893e4b798bbSMichael Roth     }
894e4b798bbSMichael Roth 
895e4b798bbSMichael Roth out:
896e4b798bbSMichael Roth     g_array_free(drc_indexes, true);
897e4b798bbSMichael Roth     g_array_free(drc_power_domains, true);
898e4b798bbSMichael Roth     g_string_free(drc_names, true);
899e4b798bbSMichael Roth     g_string_free(drc_types, true);
900e4b798bbSMichael Roth 
901e4b798bbSMichael Roth     return ret;
902e4b798bbSMichael Roth }
903b89b3d39SDavid Gibson 
904b89b3d39SDavid Gibson /*
905b89b3d39SDavid Gibson  * RTAS calls
906b89b3d39SDavid Gibson  */
907b89b3d39SDavid Gibson 
9087b7258f8SDavid Gibson static uint32_t rtas_set_isolation_state(uint32_t idx, uint32_t state)
909b89b3d39SDavid Gibson {
9107b7258f8SDavid Gibson     sPAPRDRConnector *drc = spapr_drc_by_index(idx);
9117b7258f8SDavid Gibson     sPAPRDRConnectorClass *drck;
9127b7258f8SDavid Gibson 
9137b7258f8SDavid Gibson     if (!drc) {
9140dfabd39SDavid Gibson         return RTAS_OUT_NO_SUCH_INDICATOR;
915b89b3d39SDavid Gibson     }
916b89b3d39SDavid Gibson 
9170dfabd39SDavid Gibson     trace_spapr_drc_set_isolation_state(spapr_drc_index(drc), state);
9180dfabd39SDavid Gibson 
9197b7258f8SDavid Gibson     drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
9200dfabd39SDavid Gibson 
9210dfabd39SDavid Gibson     switch (state) {
9220dfabd39SDavid Gibson     case SPAPR_DR_ISOLATION_STATE_ISOLATED:
9230dfabd39SDavid Gibson         return drck->isolate(drc);
9240dfabd39SDavid Gibson 
9250dfabd39SDavid Gibson     case SPAPR_DR_ISOLATION_STATE_UNISOLATED:
9260dfabd39SDavid Gibson         return drck->unisolate(drc);
9270dfabd39SDavid Gibson 
9280dfabd39SDavid Gibson     default:
9290dfabd39SDavid Gibson         return RTAS_OUT_PARAM_ERROR;
9300dfabd39SDavid Gibson     }
9317b7258f8SDavid Gibson }
9327b7258f8SDavid Gibson 
9337b7258f8SDavid Gibson static uint32_t rtas_set_allocation_state(uint32_t idx, uint32_t state)
9347b7258f8SDavid Gibson {
9357b7258f8SDavid Gibson     sPAPRDRConnector *drc = spapr_drc_by_index(idx);
9367b7258f8SDavid Gibson 
93761736732SDavid Gibson     if (!drc || !object_dynamic_cast(OBJECT(drc), TYPE_SPAPR_DRC_LOGICAL)) {
93861736732SDavid Gibson         return RTAS_OUT_NO_SUCH_INDICATOR;
9397b7258f8SDavid Gibson     }
9407b7258f8SDavid Gibson 
94161736732SDavid Gibson     trace_spapr_drc_set_allocation_state(spapr_drc_index(drc), state);
94261736732SDavid Gibson 
94361736732SDavid Gibson     switch (state) {
94461736732SDavid Gibson     case SPAPR_DR_ALLOCATION_STATE_USABLE:
94561736732SDavid Gibson         return drc_set_usable(drc);
94661736732SDavid Gibson 
94761736732SDavid Gibson     case SPAPR_DR_ALLOCATION_STATE_UNUSABLE:
94861736732SDavid Gibson         return drc_set_unusable(drc);
94961736732SDavid Gibson 
95061736732SDavid Gibson     default:
95161736732SDavid Gibson         return RTAS_OUT_PARAM_ERROR;
95261736732SDavid Gibson     }
9537b7258f8SDavid Gibson }
9547b7258f8SDavid Gibson 
955cd74d27eSDavid Gibson static uint32_t rtas_set_dr_indicator(uint32_t idx, uint32_t state)
9567b7258f8SDavid Gibson {
9577b7258f8SDavid Gibson     sPAPRDRConnector *drc = spapr_drc_by_index(idx);
9587b7258f8SDavid Gibson 
95967fea71bSDavid Gibson     if (!drc || !object_dynamic_cast(OBJECT(drc), TYPE_SPAPR_DRC_PHYSICAL)) {
96067fea71bSDavid Gibson         return RTAS_OUT_NO_SUCH_INDICATOR;
96167fea71bSDavid Gibson     }
96267fea71bSDavid Gibson     if ((state != SPAPR_DR_INDICATOR_INACTIVE)
96367fea71bSDavid Gibson         && (state != SPAPR_DR_INDICATOR_ACTIVE)
96467fea71bSDavid Gibson         && (state != SPAPR_DR_INDICATOR_IDENTIFY)
96567fea71bSDavid Gibson         && (state != SPAPR_DR_INDICATOR_ACTION)) {
96667fea71bSDavid Gibson         return RTAS_OUT_PARAM_ERROR; /* bad state parameter */
9677b7258f8SDavid Gibson     }
9687b7258f8SDavid Gibson 
969cd74d27eSDavid Gibson     trace_spapr_drc_set_dr_indicator(idx, state);
97067fea71bSDavid Gibson     SPAPR_DRC_PHYSICAL(drc)->dr_indicator = state;
971cd74d27eSDavid Gibson     return RTAS_OUT_SUCCESS;
972b89b3d39SDavid Gibson }
973b89b3d39SDavid Gibson 
974b89b3d39SDavid Gibson static void rtas_set_indicator(PowerPCCPU *cpu, sPAPRMachineState *spapr,
9757b7258f8SDavid Gibson                                uint32_t token,
9767b7258f8SDavid Gibson                                uint32_t nargs, target_ulong args,
9777b7258f8SDavid Gibson                                uint32_t nret, target_ulong rets)
978b89b3d39SDavid Gibson {
9797b7258f8SDavid Gibson     uint32_t type, idx, state;
980b89b3d39SDavid Gibson     uint32_t ret = RTAS_OUT_SUCCESS;
981b89b3d39SDavid Gibson 
982b89b3d39SDavid Gibson     if (nargs != 3 || nret != 1) {
983b89b3d39SDavid Gibson         ret = RTAS_OUT_PARAM_ERROR;
984b89b3d39SDavid Gibson         goto out;
985b89b3d39SDavid Gibson     }
986b89b3d39SDavid Gibson 
9877b7258f8SDavid Gibson     type = rtas_ld(args, 0);
9887b7258f8SDavid Gibson     idx = rtas_ld(args, 1);
9897b7258f8SDavid Gibson     state = rtas_ld(args, 2);
990b89b3d39SDavid Gibson 
9917b7258f8SDavid Gibson     switch (type) {
992b89b3d39SDavid Gibson     case RTAS_SENSOR_TYPE_ISOLATION_STATE:
9937b7258f8SDavid Gibson         ret = rtas_set_isolation_state(idx, state);
994b89b3d39SDavid Gibson         break;
995b89b3d39SDavid Gibson     case RTAS_SENSOR_TYPE_DR:
996cd74d27eSDavid Gibson         ret = rtas_set_dr_indicator(idx, state);
997b89b3d39SDavid Gibson         break;
998b89b3d39SDavid Gibson     case RTAS_SENSOR_TYPE_ALLOCATION_STATE:
9997b7258f8SDavid Gibson         ret = rtas_set_allocation_state(idx, state);
1000b89b3d39SDavid Gibson         break;
1001b89b3d39SDavid Gibson     default:
10027b7258f8SDavid Gibson         ret = RTAS_OUT_NOT_SUPPORTED;
1003b89b3d39SDavid Gibson     }
1004b89b3d39SDavid Gibson 
1005b89b3d39SDavid Gibson out:
1006b89b3d39SDavid Gibson     rtas_st(rets, 0, ret);
1007b89b3d39SDavid Gibson }
1008b89b3d39SDavid Gibson 
1009b89b3d39SDavid Gibson static void rtas_get_sensor_state(PowerPCCPU *cpu, sPAPRMachineState *spapr,
1010b89b3d39SDavid Gibson                                   uint32_t token, uint32_t nargs,
1011b89b3d39SDavid Gibson                                   target_ulong args, uint32_t nret,
1012b89b3d39SDavid Gibson                                   target_ulong rets)
1013b89b3d39SDavid Gibson {
1014b89b3d39SDavid Gibson     uint32_t sensor_type;
1015b89b3d39SDavid Gibson     uint32_t sensor_index;
1016b89b3d39SDavid Gibson     uint32_t sensor_state = 0;
1017b89b3d39SDavid Gibson     sPAPRDRConnector *drc;
1018b89b3d39SDavid Gibson     sPAPRDRConnectorClass *drck;
1019b89b3d39SDavid Gibson     uint32_t ret = RTAS_OUT_SUCCESS;
1020b89b3d39SDavid Gibson 
1021b89b3d39SDavid Gibson     if (nargs != 2 || nret != 2) {
1022b89b3d39SDavid Gibson         ret = RTAS_OUT_PARAM_ERROR;
1023b89b3d39SDavid Gibson         goto out;
1024b89b3d39SDavid Gibson     }
1025b89b3d39SDavid Gibson 
1026b89b3d39SDavid Gibson     sensor_type = rtas_ld(args, 0);
1027b89b3d39SDavid Gibson     sensor_index = rtas_ld(args, 1);
1028b89b3d39SDavid Gibson 
1029b89b3d39SDavid Gibson     if (sensor_type != RTAS_SENSOR_TYPE_ENTITY_SENSE) {
1030b89b3d39SDavid Gibson         /* currently only DR-related sensors are implemented */
1031b89b3d39SDavid Gibson         trace_spapr_rtas_get_sensor_state_not_supported(sensor_index,
1032b89b3d39SDavid Gibson                                                         sensor_type);
1033b89b3d39SDavid Gibson         ret = RTAS_OUT_NOT_SUPPORTED;
1034b89b3d39SDavid Gibson         goto out;
1035b89b3d39SDavid Gibson     }
1036b89b3d39SDavid Gibson 
1037fbf55397SDavid Gibson     drc = spapr_drc_by_index(sensor_index);
1038b89b3d39SDavid Gibson     if (!drc) {
1039b89b3d39SDavid Gibson         trace_spapr_rtas_get_sensor_state_invalid(sensor_index);
1040b89b3d39SDavid Gibson         ret = RTAS_OUT_PARAM_ERROR;
1041b89b3d39SDavid Gibson         goto out;
1042b89b3d39SDavid Gibson     }
1043b89b3d39SDavid Gibson     drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
1044f224d35bSDavid Gibson     sensor_state = drck->dr_entity_sense(drc);
1045b89b3d39SDavid Gibson 
1046b89b3d39SDavid Gibson out:
1047b89b3d39SDavid Gibson     rtas_st(rets, 0, ret);
1048b89b3d39SDavid Gibson     rtas_st(rets, 1, sensor_state);
1049b89b3d39SDavid Gibson }
1050b89b3d39SDavid Gibson 
1051b89b3d39SDavid Gibson /* configure-connector work area offsets, int32_t units for field
1052b89b3d39SDavid Gibson  * indexes, bytes for field offset/len values.
1053b89b3d39SDavid Gibson  *
1054b89b3d39SDavid Gibson  * as documented by PAPR+ v2.7, 13.5.3.5
1055b89b3d39SDavid Gibson  */
1056b89b3d39SDavid Gibson #define CC_IDX_NODE_NAME_OFFSET 2
1057b89b3d39SDavid Gibson #define CC_IDX_PROP_NAME_OFFSET 2
1058b89b3d39SDavid Gibson #define CC_IDX_PROP_LEN 3
1059b89b3d39SDavid Gibson #define CC_IDX_PROP_DATA_OFFSET 4
1060b89b3d39SDavid Gibson #define CC_VAL_DATA_OFFSET ((CC_IDX_PROP_DATA_OFFSET + 1) * 4)
1061b89b3d39SDavid Gibson #define CC_WA_LEN 4096
1062b89b3d39SDavid Gibson 
1063b89b3d39SDavid Gibson static void configure_connector_st(target_ulong addr, target_ulong offset,
1064b89b3d39SDavid Gibson                                    const void *buf, size_t len)
1065b89b3d39SDavid Gibson {
1066b89b3d39SDavid Gibson     cpu_physical_memory_write(ppc64_phys_to_real(addr + offset),
1067b89b3d39SDavid Gibson                               buf, MIN(len, CC_WA_LEN - offset));
1068b89b3d39SDavid Gibson }
1069b89b3d39SDavid Gibson 
1070b89b3d39SDavid Gibson static void rtas_ibm_configure_connector(PowerPCCPU *cpu,
1071b89b3d39SDavid Gibson                                          sPAPRMachineState *spapr,
1072b89b3d39SDavid Gibson                                          uint32_t token, uint32_t nargs,
1073b89b3d39SDavid Gibson                                          target_ulong args, uint32_t nret,
1074b89b3d39SDavid Gibson                                          target_ulong rets)
1075b89b3d39SDavid Gibson {
1076b89b3d39SDavid Gibson     uint64_t wa_addr;
1077b89b3d39SDavid Gibson     uint64_t wa_offset;
1078b89b3d39SDavid Gibson     uint32_t drc_index;
1079b89b3d39SDavid Gibson     sPAPRDRConnector *drc;
10809d4c0f4fSDavid Gibson     sPAPRDRConnectorClass *drck;
1081b89b3d39SDavid Gibson     sPAPRDRCCResponse resp = SPAPR_DR_CC_RESPONSE_CONTINUE;
1082b89b3d39SDavid Gibson     int rc;
1083b89b3d39SDavid Gibson 
1084b89b3d39SDavid Gibson     if (nargs != 2 || nret != 1) {
1085b89b3d39SDavid Gibson         rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
1086b89b3d39SDavid Gibson         return;
1087b89b3d39SDavid Gibson     }
1088b89b3d39SDavid Gibson 
1089b89b3d39SDavid Gibson     wa_addr = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 0);
1090b89b3d39SDavid Gibson 
1091b89b3d39SDavid Gibson     drc_index = rtas_ld(wa_addr, 0);
1092fbf55397SDavid Gibson     drc = spapr_drc_by_index(drc_index);
1093b89b3d39SDavid Gibson     if (!drc) {
1094b89b3d39SDavid Gibson         trace_spapr_rtas_ibm_configure_connector_invalid(drc_index);
1095b89b3d39SDavid Gibson         rc = RTAS_OUT_PARAM_ERROR;
1096b89b3d39SDavid Gibson         goto out;
1097b89b3d39SDavid Gibson     }
1098b89b3d39SDavid Gibson 
10999d4c0f4fSDavid Gibson     if ((drc->state != SPAPR_DRC_STATE_LOGICAL_UNISOLATE)
1100188bfe1bSBharata B Rao         && (drc->state != SPAPR_DRC_STATE_PHYSICAL_UNISOLATE)
1101188bfe1bSBharata B Rao         && (drc->state != SPAPR_DRC_STATE_LOGICAL_CONFIGURED)
1102188bfe1bSBharata B Rao         && (drc->state != SPAPR_DRC_STATE_PHYSICAL_CONFIGURED)) {
1103188bfe1bSBharata B Rao         /*
1104188bfe1bSBharata B Rao          * Need to unisolate the device before configuring
1105188bfe1bSBharata B Rao          * or it should already be in configured state to
1106188bfe1bSBharata B Rao          * allow configure-connector be called repeatedly.
1107188bfe1bSBharata B Rao          */
1108b89b3d39SDavid Gibson         rc = SPAPR_DR_CC_RESPONSE_NOT_CONFIGURABLE;
1109b89b3d39SDavid Gibson         goto out;
1110b89b3d39SDavid Gibson     }
1111b89b3d39SDavid Gibson 
11129d4c0f4fSDavid Gibson     drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
11139d4c0f4fSDavid Gibson 
1114d9c95c71SGreg Kurz     g_assert(drc->fdt || drck->dt_populate);
1115d9c95c71SGreg Kurz 
1116d9c95c71SGreg Kurz     if (!drc->fdt) {
1117d9c95c71SGreg Kurz         Error *local_err = NULL;
1118d9c95c71SGreg Kurz         void *fdt;
1119d9c95c71SGreg Kurz         int fdt_size;
1120d9c95c71SGreg Kurz 
1121d9c95c71SGreg Kurz         fdt = create_device_tree(&fdt_size);
1122d9c95c71SGreg Kurz 
1123d9c95c71SGreg Kurz         if (drck->dt_populate(drc, spapr, fdt, &drc->fdt_start_offset,
1124d9c95c71SGreg Kurz                               &local_err)) {
1125d9c95c71SGreg Kurz             g_free(fdt);
1126d9c95c71SGreg Kurz             error_free(local_err);
1127d9c95c71SGreg Kurz             rc = SPAPR_DR_CC_RESPONSE_ERROR;
1128d9c95c71SGreg Kurz             goto out;
1129d9c95c71SGreg Kurz         }
1130d9c95c71SGreg Kurz 
1131d9c95c71SGreg Kurz         drc->fdt = fdt;
1132d9c95c71SGreg Kurz         drc->ccs_offset = drc->fdt_start_offset;
1133d9c95c71SGreg Kurz         drc->ccs_depth = 0;
1134d9c95c71SGreg Kurz     }
1135d9c95c71SGreg Kurz 
1136b89b3d39SDavid Gibson     do {
1137b89b3d39SDavid Gibson         uint32_t tag;
1138b89b3d39SDavid Gibson         const char *name;
1139b89b3d39SDavid Gibson         const struct fdt_property *prop;
1140b89b3d39SDavid Gibson         int fdt_offset_next, prop_len;
1141b89b3d39SDavid Gibson 
11424445b1d2SDavid Gibson         tag = fdt_next_tag(drc->fdt, drc->ccs_offset, &fdt_offset_next);
1143b89b3d39SDavid Gibson 
1144b89b3d39SDavid Gibson         switch (tag) {
1145b89b3d39SDavid Gibson         case FDT_BEGIN_NODE:
11464445b1d2SDavid Gibson             drc->ccs_depth++;
11474445b1d2SDavid Gibson             name = fdt_get_name(drc->fdt, drc->ccs_offset, NULL);
1148b89b3d39SDavid Gibson 
1149b89b3d39SDavid Gibson             /* provide the name of the next OF node */
1150b89b3d39SDavid Gibson             wa_offset = CC_VAL_DATA_OFFSET;
1151b89b3d39SDavid Gibson             rtas_st(wa_addr, CC_IDX_NODE_NAME_OFFSET, wa_offset);
1152b89b3d39SDavid Gibson             configure_connector_st(wa_addr, wa_offset, name, strlen(name) + 1);
1153b89b3d39SDavid Gibson             resp = SPAPR_DR_CC_RESPONSE_NEXT_CHILD;
1154b89b3d39SDavid Gibson             break;
1155b89b3d39SDavid Gibson         case FDT_END_NODE:
11564445b1d2SDavid Gibson             drc->ccs_depth--;
11574445b1d2SDavid Gibson             if (drc->ccs_depth == 0) {
11580b55aa91SDavid Gibson                 uint32_t drc_index = spapr_drc_index(drc);
11599d4c0f4fSDavid Gibson 
11609d4c0f4fSDavid Gibson                 /* done sending the device tree, move to configured state */
11610b55aa91SDavid Gibson                 trace_spapr_drc_set_configured(drc_index);
11629d4c0f4fSDavid Gibson                 drc->state = drck->ready_state;
1163188bfe1bSBharata B Rao                 /*
1164188bfe1bSBharata B Rao                  * Ensure that we are able to send the FDT fragment
1165188bfe1bSBharata B Rao                  * again via configure-connector call if the guest requests.
1166188bfe1bSBharata B Rao                  */
1167188bfe1bSBharata B Rao                 drc->ccs_offset = drc->fdt_start_offset;
1168188bfe1bSBharata B Rao                 drc->ccs_depth = 0;
1169188bfe1bSBharata B Rao                 fdt_offset_next = drc->fdt_start_offset;
1170b89b3d39SDavid Gibson                 resp = SPAPR_DR_CC_RESPONSE_SUCCESS;
1171b89b3d39SDavid Gibson             } else {
1172b89b3d39SDavid Gibson                 resp = SPAPR_DR_CC_RESPONSE_PREV_PARENT;
1173b89b3d39SDavid Gibson             }
1174b89b3d39SDavid Gibson             break;
1175b89b3d39SDavid Gibson         case FDT_PROP:
11764445b1d2SDavid Gibson             prop = fdt_get_property_by_offset(drc->fdt, drc->ccs_offset,
1177b89b3d39SDavid Gibson                                               &prop_len);
117888af6ea5SDavid Gibson             name = fdt_string(drc->fdt, fdt32_to_cpu(prop->nameoff));
1179b89b3d39SDavid Gibson 
1180b89b3d39SDavid Gibson             /* provide the name of the next OF property */
1181b89b3d39SDavid Gibson             wa_offset = CC_VAL_DATA_OFFSET;
1182b89b3d39SDavid Gibson             rtas_st(wa_addr, CC_IDX_PROP_NAME_OFFSET, wa_offset);
1183b89b3d39SDavid Gibson             configure_connector_st(wa_addr, wa_offset, name, strlen(name) + 1);
1184b89b3d39SDavid Gibson 
1185b89b3d39SDavid Gibson             /* provide the length and value of the OF property. data gets
1186b89b3d39SDavid Gibson              * placed immediately after NULL terminator of the OF property's
1187b89b3d39SDavid Gibson              * name string
1188b89b3d39SDavid Gibson              */
1189b89b3d39SDavid Gibson             wa_offset += strlen(name) + 1,
1190b89b3d39SDavid Gibson             rtas_st(wa_addr, CC_IDX_PROP_LEN, prop_len);
1191b89b3d39SDavid Gibson             rtas_st(wa_addr, CC_IDX_PROP_DATA_OFFSET, wa_offset);
1192b89b3d39SDavid Gibson             configure_connector_st(wa_addr, wa_offset, prop->data, prop_len);
1193b89b3d39SDavid Gibson             resp = SPAPR_DR_CC_RESPONSE_NEXT_PROPERTY;
1194b89b3d39SDavid Gibson             break;
1195b89b3d39SDavid Gibson         case FDT_END:
1196b89b3d39SDavid Gibson             resp = SPAPR_DR_CC_RESPONSE_ERROR;
1197b89b3d39SDavid Gibson         default:
1198b89b3d39SDavid Gibson             /* keep seeking for an actionable tag */
1199b89b3d39SDavid Gibson             break;
1200b89b3d39SDavid Gibson         }
12014445b1d2SDavid Gibson         if (drc->ccs_offset >= 0) {
12024445b1d2SDavid Gibson             drc->ccs_offset = fdt_offset_next;
1203b89b3d39SDavid Gibson         }
1204b89b3d39SDavid Gibson     } while (resp == SPAPR_DR_CC_RESPONSE_CONTINUE);
1205b89b3d39SDavid Gibson 
1206b89b3d39SDavid Gibson     rc = resp;
1207b89b3d39SDavid Gibson out:
1208b89b3d39SDavid Gibson     rtas_st(rets, 0, rc);
1209b89b3d39SDavid Gibson }
1210b89b3d39SDavid Gibson 
1211b89b3d39SDavid Gibson static void spapr_drc_register_types(void)
1212b89b3d39SDavid Gibson {
1213b89b3d39SDavid Gibson     type_register_static(&spapr_dr_connector_info);
12142d335818SDavid Gibson     type_register_static(&spapr_drc_physical_info);
12152d335818SDavid Gibson     type_register_static(&spapr_drc_logical_info);
12162d335818SDavid Gibson     type_register_static(&spapr_drc_cpu_info);
12172d335818SDavid Gibson     type_register_static(&spapr_drc_pci_info);
12182d335818SDavid Gibson     type_register_static(&spapr_drc_lmb_info);
1219b89b3d39SDavid Gibson 
1220b89b3d39SDavid Gibson     spapr_rtas_register(RTAS_SET_INDICATOR, "set-indicator",
1221b89b3d39SDavid Gibson                         rtas_set_indicator);
1222b89b3d39SDavid Gibson     spapr_rtas_register(RTAS_GET_SENSOR_STATE, "get-sensor-state",
1223b89b3d39SDavid Gibson                         rtas_get_sensor_state);
1224b89b3d39SDavid Gibson     spapr_rtas_register(RTAS_IBM_CONFIGURE_CONNECTOR, "ibm,configure-connector",
1225b89b3d39SDavid Gibson                         rtas_ibm_configure_connector);
1226b89b3d39SDavid Gibson }
1227b89b3d39SDavid Gibson type_init(spapr_drc_register_types)
1228