1ee3a71e3SShivaprasad G Bhat /* 2ee3a71e3SShivaprasad G Bhat * QEMU PAPR Storage Class Memory Interfaces 3ee3a71e3SShivaprasad G Bhat * 4ee3a71e3SShivaprasad G Bhat * Copyright (c) 2019-2020, IBM Corporation. 5ee3a71e3SShivaprasad G Bhat * 6ee3a71e3SShivaprasad G Bhat * Permission is hereby granted, free of charge, to any person obtaining a copy 7ee3a71e3SShivaprasad G Bhat * of this software and associated documentation files (the "Software"), to deal 8ee3a71e3SShivaprasad G Bhat * in the Software without restriction, including without limitation the rights 9ee3a71e3SShivaprasad G Bhat * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10ee3a71e3SShivaprasad G Bhat * copies of the Software, and to permit persons to whom the Software is 11ee3a71e3SShivaprasad G Bhat * furnished to do so, subject to the following conditions: 12ee3a71e3SShivaprasad G Bhat * 13ee3a71e3SShivaprasad G Bhat * The above copyright notice and this permission notice shall be included in 14ee3a71e3SShivaprasad G Bhat * all copies or substantial portions of the Software. 15ee3a71e3SShivaprasad G Bhat * 16ee3a71e3SShivaprasad G Bhat * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17ee3a71e3SShivaprasad G Bhat * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18ee3a71e3SShivaprasad G Bhat * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19ee3a71e3SShivaprasad G Bhat * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20ee3a71e3SShivaprasad G Bhat * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21ee3a71e3SShivaprasad G Bhat * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22ee3a71e3SShivaprasad G Bhat * THE SOFTWARE. 23ee3a71e3SShivaprasad G Bhat */ 24ee3a71e3SShivaprasad G Bhat #include "qemu/osdep.h" 25b5513584SShivaprasad G Bhat #include "qemu/cutils.h" 26ee3a71e3SShivaprasad G Bhat #include "qapi/error.h" 27ee3a71e3SShivaprasad G Bhat #include "hw/ppc/spapr_drc.h" 28ee3a71e3SShivaprasad G Bhat #include "hw/ppc/spapr_nvdimm.h" 29ee3a71e3SShivaprasad G Bhat #include "hw/mem/nvdimm.h" 30ee3a71e3SShivaprasad G Bhat #include "qemu/nvdimm-utils.h" 31ee3a71e3SShivaprasad G Bhat #include "hw/ppc/fdt.h" 32b5fca656SShivaprasad G Bhat #include "qemu/range.h" 33f1aa45ffSDaniel Henrique Barboza #include "hw/ppc/spapr_numa.h" 34b5513584SShivaprasad G Bhat #include "block/thread-pool.h" 35b5513584SShivaprasad G Bhat #include "migration/vmstate.h" 36b5513584SShivaprasad G Bhat #include "qemu/pmem.h" 378601b4f1SShivaprasad G Bhat #include "hw/qdev-properties.h" 38ee3a71e3SShivaprasad G Bhat 3953d7d7e2SVaibhav Jain /* DIMM health bitmap bitmap indicators. Taken from kernel's papr_scm.c */ 4053d7d7e2SVaibhav Jain /* SCM device is unable to persist memory contents */ 4153d7d7e2SVaibhav Jain #define PAPR_PMEM_UNARMED PPC_BIT(0) 4253d7d7e2SVaibhav Jain 43f93c8f14SShivaprasad G Bhat /* 44f93c8f14SShivaprasad G Bhat * The nvdimm size should be aligned to SCM block size. 45f93c8f14SShivaprasad G Bhat * The SCM block size should be aligned to SPAPR_MEMORY_BLOCK_SIZE 46f93c8f14SShivaprasad G Bhat * in order to have SCM regions not to overlap with dimm memory regions. 47f93c8f14SShivaprasad G Bhat * The SCM devices can have variable block sizes. For now, fixing the 48f93c8f14SShivaprasad G Bhat * block size to the minimum value. 49f93c8f14SShivaprasad G Bhat */ 50f93c8f14SShivaprasad G Bhat #define SPAPR_MINIMUM_SCM_BLOCK_SIZE SPAPR_MEMORY_BLOCK_SIZE 51f93c8f14SShivaprasad G Bhat 52f93c8f14SShivaprasad G Bhat /* Have an explicit check for alignment */ 53f93c8f14SShivaprasad G Bhat QEMU_BUILD_BUG_ON(SPAPR_MINIMUM_SCM_BLOCK_SIZE % SPAPR_MEMORY_BLOCK_SIZE); 54f93c8f14SShivaprasad G Bhat 55b5513584SShivaprasad G Bhat #define TYPE_SPAPR_NVDIMM "spapr-nvdimm" 56b5513584SShivaprasad G Bhat OBJECT_DECLARE_TYPE(SpaprNVDIMMDevice, SPAPRNVDIMMClass, SPAPR_NVDIMM) 57b5513584SShivaprasad G Bhat 58b5513584SShivaprasad G Bhat struct SPAPRNVDIMMClass { 59b5513584SShivaprasad G Bhat /* private */ 60b5513584SShivaprasad G Bhat NVDIMMClass parent_class; 618601b4f1SShivaprasad G Bhat 628601b4f1SShivaprasad G Bhat /* public */ 638601b4f1SShivaprasad G Bhat void (*realize)(NVDIMMDevice *dimm, Error **errp); 648601b4f1SShivaprasad G Bhat void (*unrealize)(NVDIMMDevice *dimm, Error **errp); 65b5513584SShivaprasad G Bhat }; 66b5513584SShivaprasad G Bhat 67451c6905SGreg Kurz bool spapr_nvdimm_validate(HotplugHandler *hotplug_dev, NVDIMMDevice *nvdimm, 68beb6073fSDaniel Henrique Barboza uint64_t size, Error **errp) 69ee3a71e3SShivaprasad G Bhat { 70beb6073fSDaniel Henrique Barboza const MachineClass *mc = MACHINE_GET_CLASS(hotplug_dev); 7128f5a716SDaniel Henrique Barboza const MachineState *ms = MACHINE(hotplug_dev); 728601b4f1SShivaprasad G Bhat PCDIMMDevice *dimm = PC_DIMM(nvdimm); 738601b4f1SShivaprasad G Bhat MemoryRegion *mr = host_memory_backend_get_memory(dimm->hostmem); 7490d282d0SDaniel Henrique Barboza g_autofree char *uuidstr = NULL; 75ee3a71e3SShivaprasad G Bhat QemuUUID uuid; 76af7084e7SShivaprasad G Bhat int ret; 77ee3a71e3SShivaprasad G Bhat 78beb6073fSDaniel Henrique Barboza if (!mc->nvdimm_supported) { 79beb6073fSDaniel Henrique Barboza error_setg(errp, "NVDIMM hotplug not supported for this machine"); 80451c6905SGreg Kurz return false; 81beb6073fSDaniel Henrique Barboza } 82beb6073fSDaniel Henrique Barboza 8355810e90SIgor Mammedov if (!ms->nvdimms_state->is_enabled) { 8428f5a716SDaniel Henrique Barboza error_setg(errp, "nvdimm device found but 'nvdimm=off' was set"); 85451c6905SGreg Kurz return false; 8628f5a716SDaniel Henrique Barboza } 8728f5a716SDaniel Henrique Barboza 8870fc9cb0SDaniel Henrique Barboza if (object_property_get_int(OBJECT(nvdimm), NVDIMM_LABEL_SIZE_PROP, 8970fc9cb0SDaniel Henrique Barboza &error_abort) == 0) { 906c0f0cb3SDavid Gibson error_setg(errp, "PAPR requires NVDIMM devices to have label-size set"); 91451c6905SGreg Kurz return false; 9270fc9cb0SDaniel Henrique Barboza } 9370fc9cb0SDaniel Henrique Barboza 94ee3a71e3SShivaprasad G Bhat if (size % SPAPR_MINIMUM_SCM_BLOCK_SIZE) { 956c0f0cb3SDavid Gibson error_setg(errp, "PAPR requires NVDIMM memory size (excluding label)" 966c0f0cb3SDavid Gibson " to be a multiple of %" PRIu64 "MB", 97ee3a71e3SShivaprasad G Bhat SPAPR_MINIMUM_SCM_BLOCK_SIZE / MiB); 98451c6905SGreg Kurz return false; 99ee3a71e3SShivaprasad G Bhat } 100ee3a71e3SShivaprasad G Bhat 101af7084e7SShivaprasad G Bhat uuidstr = object_property_get_str(OBJECT(nvdimm), NVDIMM_UUID_PROP, 102af7084e7SShivaprasad G Bhat &error_abort); 103af7084e7SShivaprasad G Bhat ret = qemu_uuid_parse(uuidstr, &uuid); 104af7084e7SShivaprasad G Bhat g_assert(!ret); 105ee3a71e3SShivaprasad G Bhat 106ee3a71e3SShivaprasad G Bhat if (qemu_uuid_is_null(&uuid)) { 107ee3a71e3SShivaprasad G Bhat error_setg(errp, "NVDIMM device requires the uuid to be set"); 108451c6905SGreg Kurz return false; 109ee3a71e3SShivaprasad G Bhat } 110451c6905SGreg Kurz 1118601b4f1SShivaprasad G Bhat if (object_dynamic_cast(OBJECT(nvdimm), TYPE_SPAPR_NVDIMM) && 1128601b4f1SShivaprasad G Bhat (memory_region_get_fd(mr) < 0)) { 1138601b4f1SShivaprasad G Bhat error_setg(errp, "spapr-nvdimm device requires the " 1148601b4f1SShivaprasad G Bhat "memdev %s to be of memory-backend-file type", 1158601b4f1SShivaprasad G Bhat object_get_canonical_path_component(OBJECT(dimm->hostmem))); 1168601b4f1SShivaprasad G Bhat return false; 1178601b4f1SShivaprasad G Bhat } 1188601b4f1SShivaprasad G Bhat 119451c6905SGreg Kurz return true; 120ee3a71e3SShivaprasad G Bhat } 121ee3a71e3SShivaprasad G Bhat 122ee3a71e3SShivaprasad G Bhat 123ea042c53SGreg Kurz void spapr_add_nvdimm(DeviceState *dev, uint64_t slot) 124ee3a71e3SShivaprasad G Bhat { 125ee3a71e3SShivaprasad G Bhat SpaprDrc *drc; 126ee3a71e3SShivaprasad G Bhat bool hotplugged = spapr_drc_hotplugged(dev); 127ee3a71e3SShivaprasad G Bhat 128ee3a71e3SShivaprasad G Bhat drc = spapr_drc_by_id(TYPE_SPAPR_DRC_PMEM, slot); 129ee3a71e3SShivaprasad G Bhat g_assert(drc); 130ee3a71e3SShivaprasad G Bhat 131ea042c53SGreg Kurz /* 132ea042c53SGreg Kurz * pc_dimm_get_free_slot() provided a free slot at pre-plug. The 133ea042c53SGreg Kurz * corresponding DRC is thus assumed to be attachable. 134ea042c53SGreg Kurz */ 135bc370a65SGreg Kurz spapr_drc_attach(drc, dev); 136ee3a71e3SShivaprasad G Bhat 137ee3a71e3SShivaprasad G Bhat if (hotplugged) { 138ee3a71e3SShivaprasad G Bhat spapr_hotplug_req_add_by_index(drc); 139ee3a71e3SShivaprasad G Bhat } 140ee3a71e3SShivaprasad G Bhat } 141ee3a71e3SShivaprasad G Bhat 142f1aa45ffSDaniel Henrique Barboza static int spapr_dt_nvdimm(SpaprMachineState *spapr, void *fdt, 143f1aa45ffSDaniel Henrique Barboza int parent_offset, NVDIMMDevice *nvdimm) 144ee3a71e3SShivaprasad G Bhat { 145ee3a71e3SShivaprasad G Bhat int child_offset; 146ee3a71e3SShivaprasad G Bhat char *buf; 147ee3a71e3SShivaprasad G Bhat SpaprDrc *drc; 148ee3a71e3SShivaprasad G Bhat uint32_t drc_idx; 149ee3a71e3SShivaprasad G Bhat uint32_t node = object_property_get_uint(OBJECT(nvdimm), PC_DIMM_NODE_PROP, 150ee3a71e3SShivaprasad G Bhat &error_abort); 151ee3a71e3SShivaprasad G Bhat uint64_t slot = object_property_get_uint(OBJECT(nvdimm), PC_DIMM_SLOT_PROP, 152ee3a71e3SShivaprasad G Bhat &error_abort); 153ee3a71e3SShivaprasad G Bhat uint64_t lsize = nvdimm->label_size; 154ee3a71e3SShivaprasad G Bhat uint64_t size = object_property_get_int(OBJECT(nvdimm), PC_DIMM_SIZE_PROP, 155ee3a71e3SShivaprasad G Bhat NULL); 156ee3a71e3SShivaprasad G Bhat 157ee3a71e3SShivaprasad G Bhat drc = spapr_drc_by_id(TYPE_SPAPR_DRC_PMEM, slot); 158ee3a71e3SShivaprasad G Bhat g_assert(drc); 159ee3a71e3SShivaprasad G Bhat 160ee3a71e3SShivaprasad G Bhat drc_idx = spapr_drc_index(drc); 161ee3a71e3SShivaprasad G Bhat 162ee3a71e3SShivaprasad G Bhat buf = g_strdup_printf("ibm,pmemory@%x", drc_idx); 163ee3a71e3SShivaprasad G Bhat child_offset = fdt_add_subnode(fdt, parent_offset, buf); 164ee3a71e3SShivaprasad G Bhat g_free(buf); 165ee3a71e3SShivaprasad G Bhat 166ee3a71e3SShivaprasad G Bhat _FDT(child_offset); 167ee3a71e3SShivaprasad G Bhat 168ee3a71e3SShivaprasad G Bhat _FDT((fdt_setprop_cell(fdt, child_offset, "reg", drc_idx))); 169ee3a71e3SShivaprasad G Bhat _FDT((fdt_setprop_string(fdt, child_offset, "compatible", "ibm,pmemory"))); 170ee3a71e3SShivaprasad G Bhat _FDT((fdt_setprop_string(fdt, child_offset, "device_type", "ibm,pmemory"))); 171ee3a71e3SShivaprasad G Bhat 172f1aa45ffSDaniel Henrique Barboza spapr_numa_write_associativity_dt(spapr, fdt, child_offset, node); 173ee3a71e3SShivaprasad G Bhat 174ee3a71e3SShivaprasad G Bhat buf = qemu_uuid_unparse_strdup(&nvdimm->uuid); 175ee3a71e3SShivaprasad G Bhat _FDT((fdt_setprop_string(fdt, child_offset, "ibm,unit-guid", buf))); 176ee3a71e3SShivaprasad G Bhat g_free(buf); 177ee3a71e3SShivaprasad G Bhat 178ee3a71e3SShivaprasad G Bhat _FDT((fdt_setprop_cell(fdt, child_offset, "ibm,my-drc-index", drc_idx))); 179ee3a71e3SShivaprasad G Bhat 180ee3a71e3SShivaprasad G Bhat _FDT((fdt_setprop_u64(fdt, child_offset, "ibm,block-size", 181ee3a71e3SShivaprasad G Bhat SPAPR_MINIMUM_SCM_BLOCK_SIZE))); 182ee3a71e3SShivaprasad G Bhat _FDT((fdt_setprop_u64(fdt, child_offset, "ibm,number-of-blocks", 183ee3a71e3SShivaprasad G Bhat size / SPAPR_MINIMUM_SCM_BLOCK_SIZE))); 184ee3a71e3SShivaprasad G Bhat _FDT((fdt_setprop_cell(fdt, child_offset, "ibm,metadata-size", lsize))); 185ee3a71e3SShivaprasad G Bhat 186ee3a71e3SShivaprasad G Bhat _FDT((fdt_setprop_string(fdt, child_offset, "ibm,pmem-application", 187ee3a71e3SShivaprasad G Bhat "operating-system"))); 188ee3a71e3SShivaprasad G Bhat _FDT(fdt_setprop(fdt, child_offset, "ibm,cache-flush-required", NULL, 0)); 189ee3a71e3SShivaprasad G Bhat 1908601b4f1SShivaprasad G Bhat if (object_dynamic_cast(OBJECT(nvdimm), TYPE_SPAPR_NVDIMM)) { 1918601b4f1SShivaprasad G Bhat bool is_pmem = false, pmem_override = false; 1928601b4f1SShivaprasad G Bhat PCDIMMDevice *dimm = PC_DIMM(nvdimm); 1938601b4f1SShivaprasad G Bhat HostMemoryBackend *hostmem = dimm->hostmem; 1948601b4f1SShivaprasad G Bhat 1958601b4f1SShivaprasad G Bhat is_pmem = object_property_get_bool(OBJECT(hostmem), "pmem", NULL); 1968601b4f1SShivaprasad G Bhat pmem_override = object_property_get_bool(OBJECT(nvdimm), 1978601b4f1SShivaprasad G Bhat "pmem-override", NULL); 1988601b4f1SShivaprasad G Bhat if (!is_pmem || pmem_override) { 1998601b4f1SShivaprasad G Bhat _FDT(fdt_setprop(fdt, child_offset, "ibm,hcall-flush-required", 2008601b4f1SShivaprasad G Bhat NULL, 0)); 2018601b4f1SShivaprasad G Bhat } 2028601b4f1SShivaprasad G Bhat } 2038601b4f1SShivaprasad G Bhat 204ee3a71e3SShivaprasad G Bhat return child_offset; 205ee3a71e3SShivaprasad G Bhat } 206ee3a71e3SShivaprasad G Bhat 2076ee1d62eSDaniel Henrique Barboza int spapr_pmem_dt_populate(SpaprDrc *drc, SpaprMachineState *spapr, 2086ee1d62eSDaniel Henrique Barboza void *fdt, int *fdt_start_offset, Error **errp) 2096ee1d62eSDaniel Henrique Barboza { 2106ee1d62eSDaniel Henrique Barboza NVDIMMDevice *nvdimm = NVDIMM(drc->dev); 2116ee1d62eSDaniel Henrique Barboza 212f1aa45ffSDaniel Henrique Barboza *fdt_start_offset = spapr_dt_nvdimm(spapr, fdt, 0, nvdimm); 2136ee1d62eSDaniel Henrique Barboza 2146ee1d62eSDaniel Henrique Barboza return 0; 2156ee1d62eSDaniel Henrique Barboza } 2166ee1d62eSDaniel Henrique Barboza 217f1aa45ffSDaniel Henrique Barboza void spapr_dt_persistent_memory(SpaprMachineState *spapr, void *fdt) 218ee3a71e3SShivaprasad G Bhat { 2199f9f82daSShivaprasad G Bhat int offset = fdt_subnode_offset(fdt, 0, "ibm,persistent-memory"); 220ee3a71e3SShivaprasad G Bhat GSList *iter, *nvdimms = nvdimm_get_device_list(); 221ee3a71e3SShivaprasad G Bhat 222ee3a71e3SShivaprasad G Bhat if (offset < 0) { 2239f9f82daSShivaprasad G Bhat offset = fdt_add_subnode(fdt, 0, "ibm,persistent-memory"); 224ee3a71e3SShivaprasad G Bhat _FDT(offset); 225ee3a71e3SShivaprasad G Bhat _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 0x1))); 226ee3a71e3SShivaprasad G Bhat _FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 0x0))); 227ee3a71e3SShivaprasad G Bhat _FDT((fdt_setprop_string(fdt, offset, "device_type", 228ee3a71e3SShivaprasad G Bhat "ibm,persistent-memory"))); 229ee3a71e3SShivaprasad G Bhat } 230ee3a71e3SShivaprasad G Bhat 231ee3a71e3SShivaprasad G Bhat /* Create DT entries for cold plugged NVDIMM devices */ 232ee3a71e3SShivaprasad G Bhat for (iter = nvdimms; iter; iter = iter->next) { 233ee3a71e3SShivaprasad G Bhat NVDIMMDevice *nvdimm = iter->data; 234ee3a71e3SShivaprasad G Bhat 235f1aa45ffSDaniel Henrique Barboza spapr_dt_nvdimm(spapr, fdt, offset, nvdimm); 236ee3a71e3SShivaprasad G Bhat } 237ee3a71e3SShivaprasad G Bhat g_slist_free(nvdimms); 238ee3a71e3SShivaprasad G Bhat } 239b5fca656SShivaprasad G Bhat 240b5fca656SShivaprasad G Bhat static target_ulong h_scm_read_metadata(PowerPCCPU *cpu, 241b5fca656SShivaprasad G Bhat SpaprMachineState *spapr, 242b5fca656SShivaprasad G Bhat target_ulong opcode, 243b5fca656SShivaprasad G Bhat target_ulong *args) 244b5fca656SShivaprasad G Bhat { 245b5fca656SShivaprasad G Bhat uint32_t drc_index = args[0]; 246b5fca656SShivaprasad G Bhat uint64_t offset = args[1]; 247b5fca656SShivaprasad G Bhat uint64_t len = args[2]; 248b5fca656SShivaprasad G Bhat SpaprDrc *drc = spapr_drc_by_index(drc_index); 249b5fca656SShivaprasad G Bhat NVDIMMDevice *nvdimm; 250b5fca656SShivaprasad G Bhat NVDIMMClass *ddc; 251b5fca656SShivaprasad G Bhat uint64_t data = 0; 252b5fca656SShivaprasad G Bhat uint8_t buf[8] = { 0 }; 253b5fca656SShivaprasad G Bhat 254b5fca656SShivaprasad G Bhat if (!drc || !drc->dev || 255b5fca656SShivaprasad G Bhat spapr_drc_type(drc) != SPAPR_DR_CONNECTOR_TYPE_PMEM) { 256b5fca656SShivaprasad G Bhat return H_PARAMETER; 257b5fca656SShivaprasad G Bhat } 258b5fca656SShivaprasad G Bhat 259b5fca656SShivaprasad G Bhat if (len != 1 && len != 2 && 260b5fca656SShivaprasad G Bhat len != 4 && len != 8) { 261b5fca656SShivaprasad G Bhat return H_P3; 262b5fca656SShivaprasad G Bhat } 263b5fca656SShivaprasad G Bhat 264b5fca656SShivaprasad G Bhat nvdimm = NVDIMM(drc->dev); 265b5fca656SShivaprasad G Bhat if ((offset + len < offset) || 266b5fca656SShivaprasad G Bhat (nvdimm->label_size < len + offset)) { 267b5fca656SShivaprasad G Bhat return H_P2; 268b5fca656SShivaprasad G Bhat } 269b5fca656SShivaprasad G Bhat 270b5fca656SShivaprasad G Bhat ddc = NVDIMM_GET_CLASS(nvdimm); 271b5fca656SShivaprasad G Bhat ddc->read_label_data(nvdimm, buf, len, offset); 272b5fca656SShivaprasad G Bhat 273b5fca656SShivaprasad G Bhat switch (len) { 274b5fca656SShivaprasad G Bhat case 1: 275b5fca656SShivaprasad G Bhat data = ldub_p(buf); 276b5fca656SShivaprasad G Bhat break; 277b5fca656SShivaprasad G Bhat case 2: 278b5fca656SShivaprasad G Bhat data = lduw_be_p(buf); 279b5fca656SShivaprasad G Bhat break; 280b5fca656SShivaprasad G Bhat case 4: 281b5fca656SShivaprasad G Bhat data = ldl_be_p(buf); 282b5fca656SShivaprasad G Bhat break; 283b5fca656SShivaprasad G Bhat case 8: 284b5fca656SShivaprasad G Bhat data = ldq_be_p(buf); 285b5fca656SShivaprasad G Bhat break; 286b5fca656SShivaprasad G Bhat default: 287b5fca656SShivaprasad G Bhat g_assert_not_reached(); 288b5fca656SShivaprasad G Bhat } 289b5fca656SShivaprasad G Bhat 290b5fca656SShivaprasad G Bhat args[0] = data; 291b5fca656SShivaprasad G Bhat 292b5fca656SShivaprasad G Bhat return H_SUCCESS; 293b5fca656SShivaprasad G Bhat } 294b5fca656SShivaprasad G Bhat 295b5fca656SShivaprasad G Bhat static target_ulong h_scm_write_metadata(PowerPCCPU *cpu, 296b5fca656SShivaprasad G Bhat SpaprMachineState *spapr, 297b5fca656SShivaprasad G Bhat target_ulong opcode, 298b5fca656SShivaprasad G Bhat target_ulong *args) 299b5fca656SShivaprasad G Bhat { 300b5fca656SShivaprasad G Bhat uint32_t drc_index = args[0]; 301b5fca656SShivaprasad G Bhat uint64_t offset = args[1]; 302b5fca656SShivaprasad G Bhat uint64_t data = args[2]; 303b5fca656SShivaprasad G Bhat uint64_t len = args[3]; 304b5fca656SShivaprasad G Bhat SpaprDrc *drc = spapr_drc_by_index(drc_index); 305b5fca656SShivaprasad G Bhat NVDIMMDevice *nvdimm; 306b5fca656SShivaprasad G Bhat NVDIMMClass *ddc; 307b5fca656SShivaprasad G Bhat uint8_t buf[8] = { 0 }; 308b5fca656SShivaprasad G Bhat 309b5fca656SShivaprasad G Bhat if (!drc || !drc->dev || 310b5fca656SShivaprasad G Bhat spapr_drc_type(drc) != SPAPR_DR_CONNECTOR_TYPE_PMEM) { 311b5fca656SShivaprasad G Bhat return H_PARAMETER; 312b5fca656SShivaprasad G Bhat } 313b5fca656SShivaprasad G Bhat 314b5fca656SShivaprasad G Bhat if (len != 1 && len != 2 && 315b5fca656SShivaprasad G Bhat len != 4 && len != 8) { 316b5fca656SShivaprasad G Bhat return H_P4; 317b5fca656SShivaprasad G Bhat } 318b5fca656SShivaprasad G Bhat 319b5fca656SShivaprasad G Bhat nvdimm = NVDIMM(drc->dev); 320b5fca656SShivaprasad G Bhat if ((offset + len < offset) || 3213a125839SDavid Hildenbrand (nvdimm->label_size < len + offset) || 3223a125839SDavid Hildenbrand nvdimm->readonly) { 323b5fca656SShivaprasad G Bhat return H_P2; 324b5fca656SShivaprasad G Bhat } 325b5fca656SShivaprasad G Bhat 326b5fca656SShivaprasad G Bhat switch (len) { 327b5fca656SShivaprasad G Bhat case 1: 328b5fca656SShivaprasad G Bhat if (data & 0xffffffffffffff00) { 329b5fca656SShivaprasad G Bhat return H_P2; 330b5fca656SShivaprasad G Bhat } 331b5fca656SShivaprasad G Bhat stb_p(buf, data); 332b5fca656SShivaprasad G Bhat break; 333b5fca656SShivaprasad G Bhat case 2: 334b5fca656SShivaprasad G Bhat if (data & 0xffffffffffff0000) { 335b5fca656SShivaprasad G Bhat return H_P2; 336b5fca656SShivaprasad G Bhat } 337b5fca656SShivaprasad G Bhat stw_be_p(buf, data); 338b5fca656SShivaprasad G Bhat break; 339b5fca656SShivaprasad G Bhat case 4: 340b5fca656SShivaprasad G Bhat if (data & 0xffffffff00000000) { 341b5fca656SShivaprasad G Bhat return H_P2; 342b5fca656SShivaprasad G Bhat } 343b5fca656SShivaprasad G Bhat stl_be_p(buf, data); 344b5fca656SShivaprasad G Bhat break; 345b5fca656SShivaprasad G Bhat case 8: 346b5fca656SShivaprasad G Bhat stq_be_p(buf, data); 347b5fca656SShivaprasad G Bhat break; 348b5fca656SShivaprasad G Bhat default: 349b5fca656SShivaprasad G Bhat g_assert_not_reached(); 350b5fca656SShivaprasad G Bhat } 351b5fca656SShivaprasad G Bhat 352b5fca656SShivaprasad G Bhat ddc = NVDIMM_GET_CLASS(nvdimm); 353b5fca656SShivaprasad G Bhat ddc->write_label_data(nvdimm, buf, len, offset); 354b5fca656SShivaprasad G Bhat 355b5fca656SShivaprasad G Bhat return H_SUCCESS; 356b5fca656SShivaprasad G Bhat } 357b5fca656SShivaprasad G Bhat 358b5fca656SShivaprasad G Bhat static target_ulong h_scm_bind_mem(PowerPCCPU *cpu, SpaprMachineState *spapr, 359b5fca656SShivaprasad G Bhat target_ulong opcode, target_ulong *args) 360b5fca656SShivaprasad G Bhat { 361b5fca656SShivaprasad G Bhat uint32_t drc_index = args[0]; 362b5fca656SShivaprasad G Bhat uint64_t starting_idx = args[1]; 363b5fca656SShivaprasad G Bhat uint64_t no_of_scm_blocks_to_bind = args[2]; 364b5fca656SShivaprasad G Bhat uint64_t target_logical_mem_addr = args[3]; 365b5fca656SShivaprasad G Bhat uint64_t continue_token = args[4]; 366b5fca656SShivaprasad G Bhat uint64_t size; 367b5fca656SShivaprasad G Bhat uint64_t total_no_of_scm_blocks; 368b5fca656SShivaprasad G Bhat SpaprDrc *drc = spapr_drc_by_index(drc_index); 369b5fca656SShivaprasad G Bhat hwaddr addr; 370b5fca656SShivaprasad G Bhat NVDIMMDevice *nvdimm; 371b5fca656SShivaprasad G Bhat 372b5fca656SShivaprasad G Bhat if (!drc || !drc->dev || 373b5fca656SShivaprasad G Bhat spapr_drc_type(drc) != SPAPR_DR_CONNECTOR_TYPE_PMEM) { 374b5fca656SShivaprasad G Bhat return H_PARAMETER; 375b5fca656SShivaprasad G Bhat } 376b5fca656SShivaprasad G Bhat 377b5fca656SShivaprasad G Bhat /* 378b5fca656SShivaprasad G Bhat * Currently continue token should be zero qemu has already bound 379e6a19a64SMichael Tokarev * everything and this hcall doesn't return H_BUSY. 380b5fca656SShivaprasad G Bhat */ 381b5fca656SShivaprasad G Bhat if (continue_token > 0) { 382b5fca656SShivaprasad G Bhat return H_P5; 383b5fca656SShivaprasad G Bhat } 384b5fca656SShivaprasad G Bhat 385b5fca656SShivaprasad G Bhat /* Currently qemu assigns the address. */ 386b5fca656SShivaprasad G Bhat if (target_logical_mem_addr != 0xffffffffffffffff) { 387b5fca656SShivaprasad G Bhat return H_OVERLAP; 388b5fca656SShivaprasad G Bhat } 389b5fca656SShivaprasad G Bhat 390b5fca656SShivaprasad G Bhat nvdimm = NVDIMM(drc->dev); 391b5fca656SShivaprasad G Bhat 392b5fca656SShivaprasad G Bhat size = object_property_get_uint(OBJECT(nvdimm), 393b5fca656SShivaprasad G Bhat PC_DIMM_SIZE_PROP, &error_abort); 394b5fca656SShivaprasad G Bhat 395b5fca656SShivaprasad G Bhat total_no_of_scm_blocks = size / SPAPR_MINIMUM_SCM_BLOCK_SIZE; 396b5fca656SShivaprasad G Bhat 397b5fca656SShivaprasad G Bhat if (starting_idx > total_no_of_scm_blocks) { 398b5fca656SShivaprasad G Bhat return H_P2; 399b5fca656SShivaprasad G Bhat } 400b5fca656SShivaprasad G Bhat 401b5fca656SShivaprasad G Bhat if (((starting_idx + no_of_scm_blocks_to_bind) < starting_idx) || 402b5fca656SShivaprasad G Bhat ((starting_idx + no_of_scm_blocks_to_bind) > total_no_of_scm_blocks)) { 403b5fca656SShivaprasad G Bhat return H_P3; 404b5fca656SShivaprasad G Bhat } 405b5fca656SShivaprasad G Bhat 406b5fca656SShivaprasad G Bhat addr = object_property_get_uint(OBJECT(nvdimm), 407b5fca656SShivaprasad G Bhat PC_DIMM_ADDR_PROP, &error_abort); 408b5fca656SShivaprasad G Bhat 409b5fca656SShivaprasad G Bhat addr += starting_idx * SPAPR_MINIMUM_SCM_BLOCK_SIZE; 410b5fca656SShivaprasad G Bhat 411b5fca656SShivaprasad G Bhat /* Already bound, Return target logical address in R5 */ 412b5fca656SShivaprasad G Bhat args[1] = addr; 413b5fca656SShivaprasad G Bhat args[2] = no_of_scm_blocks_to_bind; 414b5fca656SShivaprasad G Bhat 415b5fca656SShivaprasad G Bhat return H_SUCCESS; 416b5fca656SShivaprasad G Bhat } 417b5fca656SShivaprasad G Bhat 418b5513584SShivaprasad G Bhat typedef struct SpaprNVDIMMDeviceFlushState { 419b5513584SShivaprasad G Bhat uint64_t continue_token; 420b5513584SShivaprasad G Bhat int64_t hcall_ret; 421b5513584SShivaprasad G Bhat uint32_t drcidx; 422b5513584SShivaprasad G Bhat 423b5513584SShivaprasad G Bhat QLIST_ENTRY(SpaprNVDIMMDeviceFlushState) node; 424b5513584SShivaprasad G Bhat } SpaprNVDIMMDeviceFlushState; 425b5513584SShivaprasad G Bhat 426b5513584SShivaprasad G Bhat typedef struct SpaprNVDIMMDevice SpaprNVDIMMDevice; 427b5513584SShivaprasad G Bhat struct SpaprNVDIMMDevice { 4288601b4f1SShivaprasad G Bhat /* private */ 429b5513584SShivaprasad G Bhat NVDIMMDevice parent_obj; 430b5513584SShivaprasad G Bhat 4318601b4f1SShivaprasad G Bhat bool hcall_flush_required; 432b5513584SShivaprasad G Bhat uint64_t nvdimm_flush_token; 433b5513584SShivaprasad G Bhat QLIST_HEAD(, SpaprNVDIMMDeviceFlushState) pending_nvdimm_flush_states; 434b5513584SShivaprasad G Bhat QLIST_HEAD(, SpaprNVDIMMDeviceFlushState) completed_nvdimm_flush_states; 4358601b4f1SShivaprasad G Bhat 4368601b4f1SShivaprasad G Bhat /* public */ 4378601b4f1SShivaprasad G Bhat 4388601b4f1SShivaprasad G Bhat /* 4398601b4f1SShivaprasad G Bhat * The 'on' value for this property forced the qemu to enable the hcall 4408601b4f1SShivaprasad G Bhat * flush for the nvdimm device even if the backend is a pmem 4418601b4f1SShivaprasad G Bhat */ 4428601b4f1SShivaprasad G Bhat bool pmem_override; 443b5513584SShivaprasad G Bhat }; 444b5513584SShivaprasad G Bhat 445b5513584SShivaprasad G Bhat static int flush_worker_cb(void *opaque) 446b5513584SShivaprasad G Bhat { 447b5513584SShivaprasad G Bhat SpaprNVDIMMDeviceFlushState *state = opaque; 448b5513584SShivaprasad G Bhat SpaprDrc *drc = spapr_drc_by_index(state->drcidx); 449edccf661SDaniel Henrique Barboza PCDIMMDevice *dimm; 450edccf661SDaniel Henrique Barboza HostMemoryBackend *backend; 451edccf661SDaniel Henrique Barboza int backend_fd; 452edccf661SDaniel Henrique Barboza 453edccf661SDaniel Henrique Barboza g_assert(drc != NULL); 454edccf661SDaniel Henrique Barboza 455edccf661SDaniel Henrique Barboza dimm = PC_DIMM(drc->dev); 456edccf661SDaniel Henrique Barboza backend = MEMORY_BACKEND(dimm->hostmem); 457edccf661SDaniel Henrique Barboza backend_fd = memory_region_get_fd(&backend->mr); 458b5513584SShivaprasad G Bhat 459b5513584SShivaprasad G Bhat if (object_property_get_bool(OBJECT(backend), "pmem", NULL)) { 460b5513584SShivaprasad G Bhat MemoryRegion *mr = host_memory_backend_get_memory(dimm->hostmem); 461b5513584SShivaprasad G Bhat void *ptr = memory_region_get_ram_ptr(mr); 462b5513584SShivaprasad G Bhat size_t size = object_property_get_uint(OBJECT(dimm), PC_DIMM_SIZE_PROP, 463b5513584SShivaprasad G Bhat NULL); 464b5513584SShivaprasad G Bhat 465b5513584SShivaprasad G Bhat /* flush pmem backend */ 466b5513584SShivaprasad G Bhat pmem_persist(ptr, size); 467b5513584SShivaprasad G Bhat } else { 468b5513584SShivaprasad G Bhat /* flush raw backing image */ 469b5513584SShivaprasad G Bhat if (qemu_fdatasync(backend_fd) < 0) { 470b5513584SShivaprasad G Bhat error_report("papr_scm: Could not sync nvdimm to backend file: %s", 471b5513584SShivaprasad G Bhat strerror(errno)); 472b5513584SShivaprasad G Bhat return H_HARDWARE; 473b5513584SShivaprasad G Bhat } 474b5513584SShivaprasad G Bhat } 475b5513584SShivaprasad G Bhat 476b5513584SShivaprasad G Bhat return H_SUCCESS; 477b5513584SShivaprasad G Bhat } 478b5513584SShivaprasad G Bhat 479b5513584SShivaprasad G Bhat static void spapr_nvdimm_flush_completion_cb(void *opaque, int hcall_ret) 480b5513584SShivaprasad G Bhat { 481b5513584SShivaprasad G Bhat SpaprNVDIMMDeviceFlushState *state = opaque; 482b5513584SShivaprasad G Bhat SpaprDrc *drc = spapr_drc_by_index(state->drcidx); 483edccf661SDaniel Henrique Barboza SpaprNVDIMMDevice *s_nvdimm; 484edccf661SDaniel Henrique Barboza 485edccf661SDaniel Henrique Barboza g_assert(drc != NULL); 486edccf661SDaniel Henrique Barboza 487edccf661SDaniel Henrique Barboza s_nvdimm = SPAPR_NVDIMM(drc->dev); 488b5513584SShivaprasad G Bhat 489b5513584SShivaprasad G Bhat state->hcall_ret = hcall_ret; 490b5513584SShivaprasad G Bhat QLIST_REMOVE(state, node); 491b5513584SShivaprasad G Bhat QLIST_INSERT_HEAD(&s_nvdimm->completed_nvdimm_flush_states, state, node); 492b5513584SShivaprasad G Bhat } 493b5513584SShivaprasad G Bhat 494b5513584SShivaprasad G Bhat static int spapr_nvdimm_flush_post_load(void *opaque, int version_id) 495b5513584SShivaprasad G Bhat { 496b5513584SShivaprasad G Bhat SpaprNVDIMMDevice *s_nvdimm = (SpaprNVDIMMDevice *)opaque; 497b5513584SShivaprasad G Bhat SpaprNVDIMMDeviceFlushState *state; 4988601b4f1SShivaprasad G Bhat HostMemoryBackend *backend = MEMORY_BACKEND(PC_DIMM(s_nvdimm)->hostmem); 4998601b4f1SShivaprasad G Bhat bool is_pmem = object_property_get_bool(OBJECT(backend), "pmem", NULL); 5008601b4f1SShivaprasad G Bhat bool pmem_override = object_property_get_bool(OBJECT(s_nvdimm), 5018601b4f1SShivaprasad G Bhat "pmem-override", NULL); 5028601b4f1SShivaprasad G Bhat bool dest_hcall_flush_required = pmem_override || !is_pmem; 5038601b4f1SShivaprasad G Bhat 5048601b4f1SShivaprasad G Bhat if (!s_nvdimm->hcall_flush_required && dest_hcall_flush_required) { 5058601b4f1SShivaprasad G Bhat error_report("The file backend for the spapr-nvdimm device %s at " 5068601b4f1SShivaprasad G Bhat "source is a pmem, use pmem=on and pmem-override=off to " 5078601b4f1SShivaprasad G Bhat "continue.", DEVICE(s_nvdimm)->id); 5088601b4f1SShivaprasad G Bhat return -EINVAL; 5098601b4f1SShivaprasad G Bhat } 5108601b4f1SShivaprasad G Bhat if (s_nvdimm->hcall_flush_required && !dest_hcall_flush_required) { 5118601b4f1SShivaprasad G Bhat error_report("The guest expects hcall-flush support for the " 5128601b4f1SShivaprasad G Bhat "spapr-nvdimm device %s, use pmem_override=on to " 5138601b4f1SShivaprasad G Bhat "continue.", DEVICE(s_nvdimm)->id); 5148601b4f1SShivaprasad G Bhat return -EINVAL; 5158601b4f1SShivaprasad G Bhat } 516b5513584SShivaprasad G Bhat 517b5513584SShivaprasad G Bhat QLIST_FOREACH(state, &s_nvdimm->pending_nvdimm_flush_states, node) { 518aef04fc7SEmanuele Giuseppe Esposito thread_pool_submit_aio(flush_worker_cb, state, 519b5513584SShivaprasad G Bhat spapr_nvdimm_flush_completion_cb, state); 520b5513584SShivaprasad G Bhat } 521b5513584SShivaprasad G Bhat 522b5513584SShivaprasad G Bhat return 0; 523b5513584SShivaprasad G Bhat } 524b5513584SShivaprasad G Bhat 525b5513584SShivaprasad G Bhat static const VMStateDescription vmstate_spapr_nvdimm_flush_state = { 526b5513584SShivaprasad G Bhat .name = "spapr_nvdimm_flush_state", 527b5513584SShivaprasad G Bhat .version_id = 1, 528b5513584SShivaprasad G Bhat .minimum_version_id = 1, 529078ddbc9SRichard Henderson .fields = (const VMStateField[]) { 530b5513584SShivaprasad G Bhat VMSTATE_UINT64(continue_token, SpaprNVDIMMDeviceFlushState), 531b5513584SShivaprasad G Bhat VMSTATE_INT64(hcall_ret, SpaprNVDIMMDeviceFlushState), 532b5513584SShivaprasad G Bhat VMSTATE_UINT32(drcidx, SpaprNVDIMMDeviceFlushState), 533b5513584SShivaprasad G Bhat VMSTATE_END_OF_LIST() 534b5513584SShivaprasad G Bhat }, 535b5513584SShivaprasad G Bhat }; 536b5513584SShivaprasad G Bhat 537b5513584SShivaprasad G Bhat const VMStateDescription vmstate_spapr_nvdimm_states = { 538b5513584SShivaprasad G Bhat .name = "spapr_nvdimm_states", 539b5513584SShivaprasad G Bhat .version_id = 1, 540b5513584SShivaprasad G Bhat .minimum_version_id = 1, 541b5513584SShivaprasad G Bhat .post_load = spapr_nvdimm_flush_post_load, 542078ddbc9SRichard Henderson .fields = (const VMStateField[]) { 5438601b4f1SShivaprasad G Bhat VMSTATE_BOOL(hcall_flush_required, SpaprNVDIMMDevice), 544b5513584SShivaprasad G Bhat VMSTATE_UINT64(nvdimm_flush_token, SpaprNVDIMMDevice), 545b5513584SShivaprasad G Bhat VMSTATE_QLIST_V(completed_nvdimm_flush_states, SpaprNVDIMMDevice, 1, 546b5513584SShivaprasad G Bhat vmstate_spapr_nvdimm_flush_state, 547b5513584SShivaprasad G Bhat SpaprNVDIMMDeviceFlushState, node), 548b5513584SShivaprasad G Bhat VMSTATE_QLIST_V(pending_nvdimm_flush_states, SpaprNVDIMMDevice, 1, 549b5513584SShivaprasad G Bhat vmstate_spapr_nvdimm_flush_state, 550b5513584SShivaprasad G Bhat SpaprNVDIMMDeviceFlushState, node), 551b5513584SShivaprasad G Bhat VMSTATE_END_OF_LIST() 552b5513584SShivaprasad G Bhat }, 553b5513584SShivaprasad G Bhat }; 554b5513584SShivaprasad G Bhat 555b5513584SShivaprasad G Bhat /* 556b5513584SShivaprasad G Bhat * Assign a token and reserve it for the new flush state. 557b5513584SShivaprasad G Bhat */ 558b5513584SShivaprasad G Bhat static SpaprNVDIMMDeviceFlushState *spapr_nvdimm_init_new_flush_state( 559b5513584SShivaprasad G Bhat SpaprNVDIMMDevice *spapr_nvdimm) 560b5513584SShivaprasad G Bhat { 561b5513584SShivaprasad G Bhat SpaprNVDIMMDeviceFlushState *state; 562b5513584SShivaprasad G Bhat 563b5513584SShivaprasad G Bhat state = g_malloc0(sizeof(*state)); 564b5513584SShivaprasad G Bhat 565b5513584SShivaprasad G Bhat spapr_nvdimm->nvdimm_flush_token++; 566b5513584SShivaprasad G Bhat /* Token zero is presumed as no job pending. Assert on overflow to zero */ 567b5513584SShivaprasad G Bhat g_assert(spapr_nvdimm->nvdimm_flush_token != 0); 568b5513584SShivaprasad G Bhat 569b5513584SShivaprasad G Bhat state->continue_token = spapr_nvdimm->nvdimm_flush_token; 570b5513584SShivaprasad G Bhat 571b5513584SShivaprasad G Bhat QLIST_INSERT_HEAD(&spapr_nvdimm->pending_nvdimm_flush_states, state, node); 572b5513584SShivaprasad G Bhat 573b5513584SShivaprasad G Bhat return state; 574b5513584SShivaprasad G Bhat } 575b5513584SShivaprasad G Bhat 576b5513584SShivaprasad G Bhat /* 577b5513584SShivaprasad G Bhat * spapr_nvdimm_finish_flushes 578b5513584SShivaprasad G Bhat * Waits for all pending flush requests to complete 579b5513584SShivaprasad G Bhat * their execution and free the states 580b5513584SShivaprasad G Bhat */ 581b5513584SShivaprasad G Bhat void spapr_nvdimm_finish_flushes(void) 582b5513584SShivaprasad G Bhat { 583b5513584SShivaprasad G Bhat SpaprNVDIMMDeviceFlushState *state, *next; 584b5513584SShivaprasad G Bhat GSList *list, *nvdimms; 585b5513584SShivaprasad G Bhat 586b5513584SShivaprasad G Bhat /* 587b5513584SShivaprasad G Bhat * Called on reset path, the main loop thread which calls 588b5513584SShivaprasad G Bhat * the pending BHs has gotten out running in the reset path, 589b5513584SShivaprasad G Bhat * finally reaching here. Other code path being guest 590e6a19a64SMichael Tokarev * h_client_architecture_support, that's early boot up. 591b5513584SShivaprasad G Bhat */ 592b5513584SShivaprasad G Bhat nvdimms = nvdimm_get_device_list(); 593b5513584SShivaprasad G Bhat for (list = nvdimms; list; list = list->next) { 594b5513584SShivaprasad G Bhat NVDIMMDevice *nvdimm = list->data; 595b5513584SShivaprasad G Bhat if (object_dynamic_cast(OBJECT(nvdimm), TYPE_SPAPR_NVDIMM)) { 596b5513584SShivaprasad G Bhat SpaprNVDIMMDevice *s_nvdimm = SPAPR_NVDIMM(nvdimm); 597b5513584SShivaprasad G Bhat while (!QLIST_EMPTY(&s_nvdimm->pending_nvdimm_flush_states)) { 598b5513584SShivaprasad G Bhat aio_poll(qemu_get_aio_context(), true); 599b5513584SShivaprasad G Bhat } 600b5513584SShivaprasad G Bhat 601b5513584SShivaprasad G Bhat QLIST_FOREACH_SAFE(state, &s_nvdimm->completed_nvdimm_flush_states, 602b5513584SShivaprasad G Bhat node, next) { 603b5513584SShivaprasad G Bhat QLIST_REMOVE(state, node); 604b5513584SShivaprasad G Bhat g_free(state); 605b5513584SShivaprasad G Bhat } 606b5513584SShivaprasad G Bhat } 607b5513584SShivaprasad G Bhat } 608b5513584SShivaprasad G Bhat g_slist_free(nvdimms); 609b5513584SShivaprasad G Bhat } 610b5513584SShivaprasad G Bhat 611b5513584SShivaprasad G Bhat /* 612b5513584SShivaprasad G Bhat * spapr_nvdimm_get_flush_status 613b5513584SShivaprasad G Bhat * Fetches the status of the hcall worker and returns 614b5513584SShivaprasad G Bhat * H_LONG_BUSY_ORDER_10_MSEC if the worker is still running. 615b5513584SShivaprasad G Bhat */ 616b5513584SShivaprasad G Bhat static int spapr_nvdimm_get_flush_status(SpaprNVDIMMDevice *s_nvdimm, 617b5513584SShivaprasad G Bhat uint64_t token) 618b5513584SShivaprasad G Bhat { 619b5513584SShivaprasad G Bhat SpaprNVDIMMDeviceFlushState *state, *node; 620b5513584SShivaprasad G Bhat 621b5513584SShivaprasad G Bhat QLIST_FOREACH(state, &s_nvdimm->pending_nvdimm_flush_states, node) { 622b5513584SShivaprasad G Bhat if (state->continue_token == token) { 623b5513584SShivaprasad G Bhat return H_LONG_BUSY_ORDER_10_MSEC; 624b5513584SShivaprasad G Bhat } 625b5513584SShivaprasad G Bhat } 626b5513584SShivaprasad G Bhat 627b5513584SShivaprasad G Bhat QLIST_FOREACH_SAFE(state, &s_nvdimm->completed_nvdimm_flush_states, 628b5513584SShivaprasad G Bhat node, node) { 629b5513584SShivaprasad G Bhat if (state->continue_token == token) { 630b5513584SShivaprasad G Bhat int ret = state->hcall_ret; 631b5513584SShivaprasad G Bhat QLIST_REMOVE(state, node); 632b5513584SShivaprasad G Bhat g_free(state); 633b5513584SShivaprasad G Bhat return ret; 634b5513584SShivaprasad G Bhat } 635b5513584SShivaprasad G Bhat } 636b5513584SShivaprasad G Bhat 637b5513584SShivaprasad G Bhat /* If not found in complete list too, invalid token */ 638b5513584SShivaprasad G Bhat return H_P2; 639b5513584SShivaprasad G Bhat } 640b5513584SShivaprasad G Bhat 641b5513584SShivaprasad G Bhat /* 642b5513584SShivaprasad G Bhat * H_SCM_FLUSH 643b5513584SShivaprasad G Bhat * Input: drc_index, continue-token 644b5513584SShivaprasad G Bhat * Out: continue-token 645b5513584SShivaprasad G Bhat * Return Value: H_SUCCESS, H_Parameter, H_P2, H_LONG_BUSY_ORDER_10_MSEC, 646b5513584SShivaprasad G Bhat * H_UNSUPPORTED 647b5513584SShivaprasad G Bhat * 648b5513584SShivaprasad G Bhat * Given a DRC Index Flush the data to backend NVDIMM device. The hcall returns 649b5513584SShivaprasad G Bhat * H_LONG_BUSY_ORDER_10_MSEC when the flush takes longer time and the hcall 650b5513584SShivaprasad G Bhat * needs to be issued multiple times in order to be completely serviced. The 651b5513584SShivaprasad G Bhat * continue-token from the output to be passed in the argument list of 652b5513584SShivaprasad G Bhat * subsequent hcalls until the hcall is completely serviced at which point 653b5513584SShivaprasad G Bhat * H_SUCCESS or other error is returned. 654b5513584SShivaprasad G Bhat */ 655b5513584SShivaprasad G Bhat static target_ulong h_scm_flush(PowerPCCPU *cpu, SpaprMachineState *spapr, 656b5513584SShivaprasad G Bhat target_ulong opcode, target_ulong *args) 657b5513584SShivaprasad G Bhat { 658b5513584SShivaprasad G Bhat int ret; 659b5513584SShivaprasad G Bhat uint32_t drc_index = args[0]; 660b5513584SShivaprasad G Bhat uint64_t continue_token = args[1]; 661b5513584SShivaprasad G Bhat SpaprDrc *drc = spapr_drc_by_index(drc_index); 662b5513584SShivaprasad G Bhat PCDIMMDevice *dimm; 663b5513584SShivaprasad G Bhat HostMemoryBackend *backend = NULL; 664b5513584SShivaprasad G Bhat SpaprNVDIMMDeviceFlushState *state; 665b5513584SShivaprasad G Bhat int fd; 666b5513584SShivaprasad G Bhat 667b5513584SShivaprasad G Bhat if (!drc || !drc->dev || 668b5513584SShivaprasad G Bhat spapr_drc_type(drc) != SPAPR_DR_CONNECTOR_TYPE_PMEM) { 669b5513584SShivaprasad G Bhat return H_PARAMETER; 670b5513584SShivaprasad G Bhat } 671b5513584SShivaprasad G Bhat 672b5513584SShivaprasad G Bhat dimm = PC_DIMM(drc->dev); 6738601b4f1SShivaprasad G Bhat if (!object_dynamic_cast(OBJECT(dimm), TYPE_SPAPR_NVDIMM)) { 6748601b4f1SShivaprasad G Bhat return H_PARAMETER; 6758601b4f1SShivaprasad G Bhat } 676b5513584SShivaprasad G Bhat if (continue_token == 0) { 6778601b4f1SShivaprasad G Bhat bool is_pmem = false, pmem_override = false; 678b5513584SShivaprasad G Bhat backend = MEMORY_BACKEND(dimm->hostmem); 679b5513584SShivaprasad G Bhat fd = memory_region_get_fd(&backend->mr); 680b5513584SShivaprasad G Bhat 681b5513584SShivaprasad G Bhat if (fd < 0) { 682b5513584SShivaprasad G Bhat return H_UNSUPPORTED; 683b5513584SShivaprasad G Bhat } 684b5513584SShivaprasad G Bhat 6858601b4f1SShivaprasad G Bhat is_pmem = object_property_get_bool(OBJECT(backend), "pmem", NULL); 6868601b4f1SShivaprasad G Bhat pmem_override = object_property_get_bool(OBJECT(dimm), 6878601b4f1SShivaprasad G Bhat "pmem-override", NULL); 6888601b4f1SShivaprasad G Bhat if (is_pmem && !pmem_override) { 6898601b4f1SShivaprasad G Bhat return H_UNSUPPORTED; 6908601b4f1SShivaprasad G Bhat } 6918601b4f1SShivaprasad G Bhat 692b5513584SShivaprasad G Bhat state = spapr_nvdimm_init_new_flush_state(SPAPR_NVDIMM(dimm)); 693b5513584SShivaprasad G Bhat if (!state) { 694b5513584SShivaprasad G Bhat return H_HARDWARE; 695b5513584SShivaprasad G Bhat } 696b5513584SShivaprasad G Bhat 697b5513584SShivaprasad G Bhat state->drcidx = drc_index; 698b5513584SShivaprasad G Bhat 699aef04fc7SEmanuele Giuseppe Esposito thread_pool_submit_aio(flush_worker_cb, state, 700b5513584SShivaprasad G Bhat spapr_nvdimm_flush_completion_cb, state); 701b5513584SShivaprasad G Bhat 702b5513584SShivaprasad G Bhat continue_token = state->continue_token; 703b5513584SShivaprasad G Bhat } 704b5513584SShivaprasad G Bhat 705b5513584SShivaprasad G Bhat ret = spapr_nvdimm_get_flush_status(SPAPR_NVDIMM(dimm), continue_token); 706b5513584SShivaprasad G Bhat if (H_IS_LONG_BUSY(ret)) { 707b5513584SShivaprasad G Bhat args[0] = continue_token; 708b5513584SShivaprasad G Bhat } 709b5513584SShivaprasad G Bhat 710b5513584SShivaprasad G Bhat return ret; 711b5513584SShivaprasad G Bhat } 712b5513584SShivaprasad G Bhat 713b5fca656SShivaprasad G Bhat static target_ulong h_scm_unbind_mem(PowerPCCPU *cpu, SpaprMachineState *spapr, 714b5fca656SShivaprasad G Bhat target_ulong opcode, target_ulong *args) 715b5fca656SShivaprasad G Bhat { 716b5fca656SShivaprasad G Bhat uint32_t drc_index = args[0]; 717b5fca656SShivaprasad G Bhat uint64_t starting_scm_logical_addr = args[1]; 718b5fca656SShivaprasad G Bhat uint64_t no_of_scm_blocks_to_unbind = args[2]; 719b5fca656SShivaprasad G Bhat uint64_t continue_token = args[3]; 720b5fca656SShivaprasad G Bhat uint64_t size_to_unbind; 721b5fca656SShivaprasad G Bhat Range blockrange = range_empty; 722b5fca656SShivaprasad G Bhat Range nvdimmrange = range_empty; 723b5fca656SShivaprasad G Bhat SpaprDrc *drc = spapr_drc_by_index(drc_index); 724b5fca656SShivaprasad G Bhat NVDIMMDevice *nvdimm; 725b5fca656SShivaprasad G Bhat uint64_t size, addr; 726b5fca656SShivaprasad G Bhat 727b5fca656SShivaprasad G Bhat if (!drc || !drc->dev || 728b5fca656SShivaprasad G Bhat spapr_drc_type(drc) != SPAPR_DR_CONNECTOR_TYPE_PMEM) { 729b5fca656SShivaprasad G Bhat return H_PARAMETER; 730b5fca656SShivaprasad G Bhat } 731b5fca656SShivaprasad G Bhat 732b5fca656SShivaprasad G Bhat /* continue_token should be zero as this hcall doesn't return H_BUSY. */ 733b5fca656SShivaprasad G Bhat if (continue_token > 0) { 734b5fca656SShivaprasad G Bhat return H_P4; 735b5fca656SShivaprasad G Bhat } 736b5fca656SShivaprasad G Bhat 737b5fca656SShivaprasad G Bhat /* Check if starting_scm_logical_addr is block aligned */ 738b5fca656SShivaprasad G Bhat if (!QEMU_IS_ALIGNED(starting_scm_logical_addr, 739b5fca656SShivaprasad G Bhat SPAPR_MINIMUM_SCM_BLOCK_SIZE)) { 740b5fca656SShivaprasad G Bhat return H_P2; 741b5fca656SShivaprasad G Bhat } 742b5fca656SShivaprasad G Bhat 743b5fca656SShivaprasad G Bhat size_to_unbind = no_of_scm_blocks_to_unbind * SPAPR_MINIMUM_SCM_BLOCK_SIZE; 744b5fca656SShivaprasad G Bhat if (no_of_scm_blocks_to_unbind == 0 || no_of_scm_blocks_to_unbind != 745b5fca656SShivaprasad G Bhat size_to_unbind / SPAPR_MINIMUM_SCM_BLOCK_SIZE) { 746b5fca656SShivaprasad G Bhat return H_P3; 747b5fca656SShivaprasad G Bhat } 748b5fca656SShivaprasad G Bhat 749b5fca656SShivaprasad G Bhat nvdimm = NVDIMM(drc->dev); 750b5fca656SShivaprasad G Bhat size = object_property_get_int(OBJECT(nvdimm), PC_DIMM_SIZE_PROP, 751b5fca656SShivaprasad G Bhat &error_abort); 752b5fca656SShivaprasad G Bhat addr = object_property_get_int(OBJECT(nvdimm), PC_DIMM_ADDR_PROP, 753b5fca656SShivaprasad G Bhat &error_abort); 754b5fca656SShivaprasad G Bhat 755b5fca656SShivaprasad G Bhat range_init_nofail(&nvdimmrange, addr, size); 756b5fca656SShivaprasad G Bhat range_init_nofail(&blockrange, starting_scm_logical_addr, size_to_unbind); 757b5fca656SShivaprasad G Bhat 758b5fca656SShivaprasad G Bhat if (!range_contains_range(&nvdimmrange, &blockrange)) { 759b5fca656SShivaprasad G Bhat return H_P3; 760b5fca656SShivaprasad G Bhat } 761b5fca656SShivaprasad G Bhat 762b5fca656SShivaprasad G Bhat args[1] = no_of_scm_blocks_to_unbind; 763b5fca656SShivaprasad G Bhat 764b5fca656SShivaprasad G Bhat /* let unplug take care of actual unbind */ 765b5fca656SShivaprasad G Bhat return H_SUCCESS; 766b5fca656SShivaprasad G Bhat } 767b5fca656SShivaprasad G Bhat 768b5fca656SShivaprasad G Bhat #define H_UNBIND_SCOPE_ALL 0x1 769b5fca656SShivaprasad G Bhat #define H_UNBIND_SCOPE_DRC 0x2 770b5fca656SShivaprasad G Bhat 771b5fca656SShivaprasad G Bhat static target_ulong h_scm_unbind_all(PowerPCCPU *cpu, SpaprMachineState *spapr, 772b5fca656SShivaprasad G Bhat target_ulong opcode, target_ulong *args) 773b5fca656SShivaprasad G Bhat { 774b5fca656SShivaprasad G Bhat uint64_t target_scope = args[0]; 775b5fca656SShivaprasad G Bhat uint32_t drc_index = args[1]; 776b5fca656SShivaprasad G Bhat uint64_t continue_token = args[2]; 777b5fca656SShivaprasad G Bhat NVDIMMDevice *nvdimm; 778b5fca656SShivaprasad G Bhat uint64_t size; 779b5fca656SShivaprasad G Bhat uint64_t no_of_scm_blocks_unbound = 0; 780b5fca656SShivaprasad G Bhat 781b5fca656SShivaprasad G Bhat /* continue_token should be zero as this hcall doesn't return H_BUSY. */ 782b5fca656SShivaprasad G Bhat if (continue_token > 0) { 783b5fca656SShivaprasad G Bhat return H_P4; 784b5fca656SShivaprasad G Bhat } 785b5fca656SShivaprasad G Bhat 786b5fca656SShivaprasad G Bhat if (target_scope == H_UNBIND_SCOPE_DRC) { 787b5fca656SShivaprasad G Bhat SpaprDrc *drc = spapr_drc_by_index(drc_index); 788b5fca656SShivaprasad G Bhat 789b5fca656SShivaprasad G Bhat if (!drc || !drc->dev || 790b5fca656SShivaprasad G Bhat spapr_drc_type(drc) != SPAPR_DR_CONNECTOR_TYPE_PMEM) { 791b5fca656SShivaprasad G Bhat return H_P2; 792b5fca656SShivaprasad G Bhat } 793b5fca656SShivaprasad G Bhat 794b5fca656SShivaprasad G Bhat nvdimm = NVDIMM(drc->dev); 795b5fca656SShivaprasad G Bhat size = object_property_get_int(OBJECT(nvdimm), PC_DIMM_SIZE_PROP, 796b5fca656SShivaprasad G Bhat &error_abort); 797b5fca656SShivaprasad G Bhat 798b5fca656SShivaprasad G Bhat no_of_scm_blocks_unbound = size / SPAPR_MINIMUM_SCM_BLOCK_SIZE; 799b5fca656SShivaprasad G Bhat } else if (target_scope == H_UNBIND_SCOPE_ALL) { 800b5fca656SShivaprasad G Bhat GSList *list, *nvdimms; 801b5fca656SShivaprasad G Bhat 802b5fca656SShivaprasad G Bhat nvdimms = nvdimm_get_device_list(); 803b5fca656SShivaprasad G Bhat for (list = nvdimms; list; list = list->next) { 804b5fca656SShivaprasad G Bhat nvdimm = list->data; 805b5fca656SShivaprasad G Bhat size = object_property_get_int(OBJECT(nvdimm), PC_DIMM_SIZE_PROP, 806b5fca656SShivaprasad G Bhat &error_abort); 807b5fca656SShivaprasad G Bhat 808b5fca656SShivaprasad G Bhat no_of_scm_blocks_unbound += size / SPAPR_MINIMUM_SCM_BLOCK_SIZE; 809b5fca656SShivaprasad G Bhat } 810b5fca656SShivaprasad G Bhat g_slist_free(nvdimms); 811b5fca656SShivaprasad G Bhat } else { 812b5fca656SShivaprasad G Bhat return H_PARAMETER; 813b5fca656SShivaprasad G Bhat } 814b5fca656SShivaprasad G Bhat 815b5fca656SShivaprasad G Bhat args[1] = no_of_scm_blocks_unbound; 816b5fca656SShivaprasad G Bhat 817b5fca656SShivaprasad G Bhat /* let unplug take care of actual unbind */ 818b5fca656SShivaprasad G Bhat return H_SUCCESS; 819b5fca656SShivaprasad G Bhat } 820b5fca656SShivaprasad G Bhat 82153d7d7e2SVaibhav Jain static target_ulong h_scm_health(PowerPCCPU *cpu, SpaprMachineState *spapr, 82253d7d7e2SVaibhav Jain target_ulong opcode, target_ulong *args) 82353d7d7e2SVaibhav Jain { 82453d7d7e2SVaibhav Jain 82553d7d7e2SVaibhav Jain NVDIMMDevice *nvdimm; 82653d7d7e2SVaibhav Jain uint64_t hbitmap = 0; 82753d7d7e2SVaibhav Jain uint32_t drc_index = args[0]; 82853d7d7e2SVaibhav Jain SpaprDrc *drc = spapr_drc_by_index(drc_index); 82953d7d7e2SVaibhav Jain const uint64_t hbitmap_mask = PAPR_PMEM_UNARMED; 83053d7d7e2SVaibhav Jain 83153d7d7e2SVaibhav Jain 83253d7d7e2SVaibhav Jain /* Ensure that the drc is valid & is valid PMEM dimm and is plugged in */ 83353d7d7e2SVaibhav Jain if (!drc || !drc->dev || 83453d7d7e2SVaibhav Jain spapr_drc_type(drc) != SPAPR_DR_CONNECTOR_TYPE_PMEM) { 83553d7d7e2SVaibhav Jain return H_PARAMETER; 83653d7d7e2SVaibhav Jain } 83753d7d7e2SVaibhav Jain 83853d7d7e2SVaibhav Jain nvdimm = NVDIMM(drc->dev); 83953d7d7e2SVaibhav Jain 84053d7d7e2SVaibhav Jain /* Update if the nvdimm is unarmed and send its status via health bitmaps */ 84153d7d7e2SVaibhav Jain if (object_property_get_bool(OBJECT(nvdimm), NVDIMM_UNARMED_PROP, NULL)) { 84253d7d7e2SVaibhav Jain hbitmap |= PAPR_PMEM_UNARMED; 84353d7d7e2SVaibhav Jain } 84453d7d7e2SVaibhav Jain 84553d7d7e2SVaibhav Jain /* Update the out args with health bitmap/mask */ 84653d7d7e2SVaibhav Jain args[0] = hbitmap; 84753d7d7e2SVaibhav Jain args[1] = hbitmap_mask; 84853d7d7e2SVaibhav Jain 84953d7d7e2SVaibhav Jain return H_SUCCESS; 85053d7d7e2SVaibhav Jain } 85153d7d7e2SVaibhav Jain 852b5fca656SShivaprasad G Bhat static void spapr_scm_register_types(void) 853b5fca656SShivaprasad G Bhat { 854b5fca656SShivaprasad G Bhat /* qemu/scm specific hcalls */ 855b5fca656SShivaprasad G Bhat spapr_register_hypercall(H_SCM_READ_METADATA, h_scm_read_metadata); 856b5fca656SShivaprasad G Bhat spapr_register_hypercall(H_SCM_WRITE_METADATA, h_scm_write_metadata); 857b5fca656SShivaprasad G Bhat spapr_register_hypercall(H_SCM_BIND_MEM, h_scm_bind_mem); 858b5fca656SShivaprasad G Bhat spapr_register_hypercall(H_SCM_UNBIND_MEM, h_scm_unbind_mem); 859b5fca656SShivaprasad G Bhat spapr_register_hypercall(H_SCM_UNBIND_ALL, h_scm_unbind_all); 86053d7d7e2SVaibhav Jain spapr_register_hypercall(H_SCM_HEALTH, h_scm_health); 861b5513584SShivaprasad G Bhat spapr_register_hypercall(H_SCM_FLUSH, h_scm_flush); 862b5fca656SShivaprasad G Bhat } 863b5fca656SShivaprasad G Bhat 864b5fca656SShivaprasad G Bhat type_init(spapr_scm_register_types) 8658601b4f1SShivaprasad G Bhat 8668601b4f1SShivaprasad G Bhat static void spapr_nvdimm_realize(NVDIMMDevice *dimm, Error **errp) 8678601b4f1SShivaprasad G Bhat { 8688601b4f1SShivaprasad G Bhat SpaprNVDIMMDevice *s_nvdimm = SPAPR_NVDIMM(dimm); 8698601b4f1SShivaprasad G Bhat HostMemoryBackend *backend = MEMORY_BACKEND(PC_DIMM(dimm)->hostmem); 8708601b4f1SShivaprasad G Bhat bool is_pmem = object_property_get_bool(OBJECT(backend), "pmem", NULL); 8718601b4f1SShivaprasad G Bhat bool pmem_override = object_property_get_bool(OBJECT(dimm), "pmem-override", 8728601b4f1SShivaprasad G Bhat NULL); 8738601b4f1SShivaprasad G Bhat if (!is_pmem || pmem_override) { 8748601b4f1SShivaprasad G Bhat s_nvdimm->hcall_flush_required = true; 8758601b4f1SShivaprasad G Bhat } 8768601b4f1SShivaprasad G Bhat 87799b16e8eSJuan Quintela vmstate_register_any(NULL, &vmstate_spapr_nvdimm_states, dimm); 8788601b4f1SShivaprasad G Bhat } 8798601b4f1SShivaprasad G Bhat 8808601b4f1SShivaprasad G Bhat static void spapr_nvdimm_unrealize(NVDIMMDevice *dimm) 8818601b4f1SShivaprasad G Bhat { 8828601b4f1SShivaprasad G Bhat vmstate_unregister(NULL, &vmstate_spapr_nvdimm_states, dimm); 8838601b4f1SShivaprasad G Bhat } 8848601b4f1SShivaprasad G Bhat 8858601b4f1SShivaprasad G Bhat #ifdef CONFIG_LIBPMEM 8864ee77e89SRichard Henderson static const Property spapr_nvdimm_properties[] = { 8878601b4f1SShivaprasad G Bhat DEFINE_PROP_BOOL("pmem-override", SpaprNVDIMMDevice, pmem_override, false), 8888601b4f1SShivaprasad G Bhat }; 8894ee77e89SRichard Henderson #endif 8908601b4f1SShivaprasad G Bhat 891*12d1a768SPhilippe Mathieu-Daudé static void spapr_nvdimm_class_init(ObjectClass *oc, const void *data) 8928601b4f1SShivaprasad G Bhat { 8938601b4f1SShivaprasad G Bhat NVDIMMClass *nvc = NVDIMM_CLASS(oc); 8948601b4f1SShivaprasad G Bhat 8958601b4f1SShivaprasad G Bhat nvc->realize = spapr_nvdimm_realize; 8968601b4f1SShivaprasad G Bhat nvc->unrealize = spapr_nvdimm_unrealize; 8978601b4f1SShivaprasad G Bhat 8984ee77e89SRichard Henderson #ifdef CONFIG_LIBPMEM 8994ee77e89SRichard Henderson device_class_set_props(DEVICE_CLASS(oc), spapr_nvdimm_properties); 9004ee77e89SRichard Henderson #endif 9018601b4f1SShivaprasad G Bhat } 9028601b4f1SShivaprasad G Bhat 9038601b4f1SShivaprasad G Bhat static void spapr_nvdimm_init(Object *obj) 9048601b4f1SShivaprasad G Bhat { 9058601b4f1SShivaprasad G Bhat SpaprNVDIMMDevice *s_nvdimm = SPAPR_NVDIMM(obj); 9068601b4f1SShivaprasad G Bhat 9078601b4f1SShivaprasad G Bhat s_nvdimm->hcall_flush_required = false; 9088601b4f1SShivaprasad G Bhat QLIST_INIT(&s_nvdimm->pending_nvdimm_flush_states); 9098601b4f1SShivaprasad G Bhat QLIST_INIT(&s_nvdimm->completed_nvdimm_flush_states); 9108601b4f1SShivaprasad G Bhat } 9118601b4f1SShivaprasad G Bhat 9128601b4f1SShivaprasad G Bhat static TypeInfo spapr_nvdimm_info = { 9138601b4f1SShivaprasad G Bhat .name = TYPE_SPAPR_NVDIMM, 9148601b4f1SShivaprasad G Bhat .parent = TYPE_NVDIMM, 9158601b4f1SShivaprasad G Bhat .class_init = spapr_nvdimm_class_init, 9168601b4f1SShivaprasad G Bhat .class_size = sizeof(SPAPRNVDIMMClass), 9178601b4f1SShivaprasad G Bhat .instance_size = sizeof(SpaprNVDIMMDevice), 9188601b4f1SShivaprasad G Bhat .instance_init = spapr_nvdimm_init, 9198601b4f1SShivaprasad G Bhat }; 9208601b4f1SShivaprasad G Bhat 9218601b4f1SShivaprasad G Bhat static void spapr_nvdimm_register_types(void) 9228601b4f1SShivaprasad G Bhat { 9238601b4f1SShivaprasad G Bhat type_register_static(&spapr_nvdimm_info); 9248601b4f1SShivaprasad G Bhat } 9258601b4f1SShivaprasad G Bhat 9268601b4f1SShivaprasad G Bhat type_init(spapr_nvdimm_register_types) 927