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 239ee3a71e3SShivaprasad G Bhat return; 240ee3a71e3SShivaprasad G Bhat } 241b5fca656SShivaprasad G Bhat 242b5fca656SShivaprasad G Bhat static target_ulong h_scm_read_metadata(PowerPCCPU *cpu, 243b5fca656SShivaprasad G Bhat SpaprMachineState *spapr, 244b5fca656SShivaprasad G Bhat target_ulong opcode, 245b5fca656SShivaprasad G Bhat target_ulong *args) 246b5fca656SShivaprasad G Bhat { 247b5fca656SShivaprasad G Bhat uint32_t drc_index = args[0]; 248b5fca656SShivaprasad G Bhat uint64_t offset = args[1]; 249b5fca656SShivaprasad G Bhat uint64_t len = args[2]; 250b5fca656SShivaprasad G Bhat SpaprDrc *drc = spapr_drc_by_index(drc_index); 251b5fca656SShivaprasad G Bhat NVDIMMDevice *nvdimm; 252b5fca656SShivaprasad G Bhat NVDIMMClass *ddc; 253b5fca656SShivaprasad G Bhat uint64_t data = 0; 254b5fca656SShivaprasad G Bhat uint8_t buf[8] = { 0 }; 255b5fca656SShivaprasad G Bhat 256b5fca656SShivaprasad G Bhat if (!drc || !drc->dev || 257b5fca656SShivaprasad G Bhat spapr_drc_type(drc) != SPAPR_DR_CONNECTOR_TYPE_PMEM) { 258b5fca656SShivaprasad G Bhat return H_PARAMETER; 259b5fca656SShivaprasad G Bhat } 260b5fca656SShivaprasad G Bhat 261b5fca656SShivaprasad G Bhat if (len != 1 && len != 2 && 262b5fca656SShivaprasad G Bhat len != 4 && len != 8) { 263b5fca656SShivaprasad G Bhat return H_P3; 264b5fca656SShivaprasad G Bhat } 265b5fca656SShivaprasad G Bhat 266b5fca656SShivaprasad G Bhat nvdimm = NVDIMM(drc->dev); 267b5fca656SShivaprasad G Bhat if ((offset + len < offset) || 268b5fca656SShivaprasad G Bhat (nvdimm->label_size < len + offset)) { 269b5fca656SShivaprasad G Bhat return H_P2; 270b5fca656SShivaprasad G Bhat } 271b5fca656SShivaprasad G Bhat 272b5fca656SShivaprasad G Bhat ddc = NVDIMM_GET_CLASS(nvdimm); 273b5fca656SShivaprasad G Bhat ddc->read_label_data(nvdimm, buf, len, offset); 274b5fca656SShivaprasad G Bhat 275b5fca656SShivaprasad G Bhat switch (len) { 276b5fca656SShivaprasad G Bhat case 1: 277b5fca656SShivaprasad G Bhat data = ldub_p(buf); 278b5fca656SShivaprasad G Bhat break; 279b5fca656SShivaprasad G Bhat case 2: 280b5fca656SShivaprasad G Bhat data = lduw_be_p(buf); 281b5fca656SShivaprasad G Bhat break; 282b5fca656SShivaprasad G Bhat case 4: 283b5fca656SShivaprasad G Bhat data = ldl_be_p(buf); 284b5fca656SShivaprasad G Bhat break; 285b5fca656SShivaprasad G Bhat case 8: 286b5fca656SShivaprasad G Bhat data = ldq_be_p(buf); 287b5fca656SShivaprasad G Bhat break; 288b5fca656SShivaprasad G Bhat default: 289b5fca656SShivaprasad G Bhat g_assert_not_reached(); 290b5fca656SShivaprasad G Bhat } 291b5fca656SShivaprasad G Bhat 292b5fca656SShivaprasad G Bhat args[0] = data; 293b5fca656SShivaprasad G Bhat 294b5fca656SShivaprasad G Bhat return H_SUCCESS; 295b5fca656SShivaprasad G Bhat } 296b5fca656SShivaprasad G Bhat 297b5fca656SShivaprasad G Bhat static target_ulong h_scm_write_metadata(PowerPCCPU *cpu, 298b5fca656SShivaprasad G Bhat SpaprMachineState *spapr, 299b5fca656SShivaprasad G Bhat target_ulong opcode, 300b5fca656SShivaprasad G Bhat target_ulong *args) 301b5fca656SShivaprasad G Bhat { 302b5fca656SShivaprasad G Bhat uint32_t drc_index = args[0]; 303b5fca656SShivaprasad G Bhat uint64_t offset = args[1]; 304b5fca656SShivaprasad G Bhat uint64_t data = args[2]; 305b5fca656SShivaprasad G Bhat uint64_t len = args[3]; 306b5fca656SShivaprasad G Bhat SpaprDrc *drc = spapr_drc_by_index(drc_index); 307b5fca656SShivaprasad G Bhat NVDIMMDevice *nvdimm; 308b5fca656SShivaprasad G Bhat NVDIMMClass *ddc; 309b5fca656SShivaprasad G Bhat uint8_t buf[8] = { 0 }; 310b5fca656SShivaprasad G Bhat 311b5fca656SShivaprasad G Bhat if (!drc || !drc->dev || 312b5fca656SShivaprasad G Bhat spapr_drc_type(drc) != SPAPR_DR_CONNECTOR_TYPE_PMEM) { 313b5fca656SShivaprasad G Bhat return H_PARAMETER; 314b5fca656SShivaprasad G Bhat } 315b5fca656SShivaprasad G Bhat 316b5fca656SShivaprasad G Bhat if (len != 1 && len != 2 && 317b5fca656SShivaprasad G Bhat len != 4 && len != 8) { 318b5fca656SShivaprasad G Bhat return H_P4; 319b5fca656SShivaprasad G Bhat } 320b5fca656SShivaprasad G Bhat 321b5fca656SShivaprasad G Bhat nvdimm = NVDIMM(drc->dev); 322b5fca656SShivaprasad G Bhat if ((offset + len < offset) || 323b5fca656SShivaprasad G Bhat (nvdimm->label_size < len + offset)) { 324b5fca656SShivaprasad G Bhat return H_P2; 325b5fca656SShivaprasad G Bhat } 326b5fca656SShivaprasad G Bhat 327b5fca656SShivaprasad G Bhat switch (len) { 328b5fca656SShivaprasad G Bhat case 1: 329b5fca656SShivaprasad G Bhat if (data & 0xffffffffffffff00) { 330b5fca656SShivaprasad G Bhat return H_P2; 331b5fca656SShivaprasad G Bhat } 332b5fca656SShivaprasad G Bhat stb_p(buf, data); 333b5fca656SShivaprasad G Bhat break; 334b5fca656SShivaprasad G Bhat case 2: 335b5fca656SShivaprasad G Bhat if (data & 0xffffffffffff0000) { 336b5fca656SShivaprasad G Bhat return H_P2; 337b5fca656SShivaprasad G Bhat } 338b5fca656SShivaprasad G Bhat stw_be_p(buf, data); 339b5fca656SShivaprasad G Bhat break; 340b5fca656SShivaprasad G Bhat case 4: 341b5fca656SShivaprasad G Bhat if (data & 0xffffffff00000000) { 342b5fca656SShivaprasad G Bhat return H_P2; 343b5fca656SShivaprasad G Bhat } 344b5fca656SShivaprasad G Bhat stl_be_p(buf, data); 345b5fca656SShivaprasad G Bhat break; 346b5fca656SShivaprasad G Bhat case 8: 347b5fca656SShivaprasad G Bhat stq_be_p(buf, data); 348b5fca656SShivaprasad G Bhat break; 349b5fca656SShivaprasad G Bhat default: 350b5fca656SShivaprasad G Bhat g_assert_not_reached(); 351b5fca656SShivaprasad G Bhat } 352b5fca656SShivaprasad G Bhat 353b5fca656SShivaprasad G Bhat ddc = NVDIMM_GET_CLASS(nvdimm); 354b5fca656SShivaprasad G Bhat ddc->write_label_data(nvdimm, buf, len, offset); 355b5fca656SShivaprasad G Bhat 356b5fca656SShivaprasad G Bhat return H_SUCCESS; 357b5fca656SShivaprasad G Bhat } 358b5fca656SShivaprasad G Bhat 359b5fca656SShivaprasad G Bhat static target_ulong h_scm_bind_mem(PowerPCCPU *cpu, SpaprMachineState *spapr, 360b5fca656SShivaprasad G Bhat target_ulong opcode, target_ulong *args) 361b5fca656SShivaprasad G Bhat { 362b5fca656SShivaprasad G Bhat uint32_t drc_index = args[0]; 363b5fca656SShivaprasad G Bhat uint64_t starting_idx = args[1]; 364b5fca656SShivaprasad G Bhat uint64_t no_of_scm_blocks_to_bind = args[2]; 365b5fca656SShivaprasad G Bhat uint64_t target_logical_mem_addr = args[3]; 366b5fca656SShivaprasad G Bhat uint64_t continue_token = args[4]; 367b5fca656SShivaprasad G Bhat uint64_t size; 368b5fca656SShivaprasad G Bhat uint64_t total_no_of_scm_blocks; 369b5fca656SShivaprasad G Bhat SpaprDrc *drc = spapr_drc_by_index(drc_index); 370b5fca656SShivaprasad G Bhat hwaddr addr; 371b5fca656SShivaprasad G Bhat NVDIMMDevice *nvdimm; 372b5fca656SShivaprasad G Bhat 373b5fca656SShivaprasad G Bhat if (!drc || !drc->dev || 374b5fca656SShivaprasad G Bhat spapr_drc_type(drc) != SPAPR_DR_CONNECTOR_TYPE_PMEM) { 375b5fca656SShivaprasad G Bhat return H_PARAMETER; 376b5fca656SShivaprasad G Bhat } 377b5fca656SShivaprasad G Bhat 378b5fca656SShivaprasad G Bhat /* 379b5fca656SShivaprasad G Bhat * Currently continue token should be zero qemu has already bound 380b5fca656SShivaprasad G Bhat * everything and this hcall doesnt return H_BUSY. 381b5fca656SShivaprasad G Bhat */ 382b5fca656SShivaprasad G Bhat if (continue_token > 0) { 383b5fca656SShivaprasad G Bhat return H_P5; 384b5fca656SShivaprasad G Bhat } 385b5fca656SShivaprasad G Bhat 386b5fca656SShivaprasad G Bhat /* Currently qemu assigns the address. */ 387b5fca656SShivaprasad G Bhat if (target_logical_mem_addr != 0xffffffffffffffff) { 388b5fca656SShivaprasad G Bhat return H_OVERLAP; 389b5fca656SShivaprasad G Bhat } 390b5fca656SShivaprasad G Bhat 391b5fca656SShivaprasad G Bhat nvdimm = NVDIMM(drc->dev); 392b5fca656SShivaprasad G Bhat 393b5fca656SShivaprasad G Bhat size = object_property_get_uint(OBJECT(nvdimm), 394b5fca656SShivaprasad G Bhat PC_DIMM_SIZE_PROP, &error_abort); 395b5fca656SShivaprasad G Bhat 396b5fca656SShivaprasad G Bhat total_no_of_scm_blocks = size / SPAPR_MINIMUM_SCM_BLOCK_SIZE; 397b5fca656SShivaprasad G Bhat 398b5fca656SShivaprasad G Bhat if (starting_idx > total_no_of_scm_blocks) { 399b5fca656SShivaprasad G Bhat return H_P2; 400b5fca656SShivaprasad G Bhat } 401b5fca656SShivaprasad G Bhat 402b5fca656SShivaprasad G Bhat if (((starting_idx + no_of_scm_blocks_to_bind) < starting_idx) || 403b5fca656SShivaprasad G Bhat ((starting_idx + no_of_scm_blocks_to_bind) > total_no_of_scm_blocks)) { 404b5fca656SShivaprasad G Bhat return H_P3; 405b5fca656SShivaprasad G Bhat } 406b5fca656SShivaprasad G Bhat 407b5fca656SShivaprasad G Bhat addr = object_property_get_uint(OBJECT(nvdimm), 408b5fca656SShivaprasad G Bhat PC_DIMM_ADDR_PROP, &error_abort); 409b5fca656SShivaprasad G Bhat 410b5fca656SShivaprasad G Bhat addr += starting_idx * SPAPR_MINIMUM_SCM_BLOCK_SIZE; 411b5fca656SShivaprasad G Bhat 412b5fca656SShivaprasad G Bhat /* Already bound, Return target logical address in R5 */ 413b5fca656SShivaprasad G Bhat args[1] = addr; 414b5fca656SShivaprasad G Bhat args[2] = no_of_scm_blocks_to_bind; 415b5fca656SShivaprasad G Bhat 416b5fca656SShivaprasad G Bhat return H_SUCCESS; 417b5fca656SShivaprasad G Bhat } 418b5fca656SShivaprasad G Bhat 419b5513584SShivaprasad G Bhat typedef struct SpaprNVDIMMDeviceFlushState { 420b5513584SShivaprasad G Bhat uint64_t continue_token; 421b5513584SShivaprasad G Bhat int64_t hcall_ret; 422b5513584SShivaprasad G Bhat uint32_t drcidx; 423b5513584SShivaprasad G Bhat 424b5513584SShivaprasad G Bhat QLIST_ENTRY(SpaprNVDIMMDeviceFlushState) node; 425b5513584SShivaprasad G Bhat } SpaprNVDIMMDeviceFlushState; 426b5513584SShivaprasad G Bhat 427b5513584SShivaprasad G Bhat typedef struct SpaprNVDIMMDevice SpaprNVDIMMDevice; 428b5513584SShivaprasad G Bhat struct SpaprNVDIMMDevice { 4298601b4f1SShivaprasad G Bhat /* private */ 430b5513584SShivaprasad G Bhat NVDIMMDevice parent_obj; 431b5513584SShivaprasad G Bhat 4328601b4f1SShivaprasad G Bhat bool hcall_flush_required; 433b5513584SShivaprasad G Bhat uint64_t nvdimm_flush_token; 434b5513584SShivaprasad G Bhat QLIST_HEAD(, SpaprNVDIMMDeviceFlushState) pending_nvdimm_flush_states; 435b5513584SShivaprasad G Bhat QLIST_HEAD(, SpaprNVDIMMDeviceFlushState) completed_nvdimm_flush_states; 4368601b4f1SShivaprasad G Bhat 4378601b4f1SShivaprasad G Bhat /* public */ 4388601b4f1SShivaprasad G Bhat 4398601b4f1SShivaprasad G Bhat /* 4408601b4f1SShivaprasad G Bhat * The 'on' value for this property forced the qemu to enable the hcall 4418601b4f1SShivaprasad G Bhat * flush for the nvdimm device even if the backend is a pmem 4428601b4f1SShivaprasad G Bhat */ 4438601b4f1SShivaprasad G Bhat bool pmem_override; 444b5513584SShivaprasad G Bhat }; 445b5513584SShivaprasad G Bhat 446b5513584SShivaprasad G Bhat static int flush_worker_cb(void *opaque) 447b5513584SShivaprasad G Bhat { 448b5513584SShivaprasad G Bhat SpaprNVDIMMDeviceFlushState *state = opaque; 449b5513584SShivaprasad G Bhat SpaprDrc *drc = spapr_drc_by_index(state->drcidx); 450edccf661SDaniel Henrique Barboza PCDIMMDevice *dimm; 451edccf661SDaniel Henrique Barboza HostMemoryBackend *backend; 452edccf661SDaniel Henrique Barboza int backend_fd; 453edccf661SDaniel Henrique Barboza 454edccf661SDaniel Henrique Barboza g_assert(drc != NULL); 455edccf661SDaniel Henrique Barboza 456edccf661SDaniel Henrique Barboza dimm = PC_DIMM(drc->dev); 457edccf661SDaniel Henrique Barboza backend = MEMORY_BACKEND(dimm->hostmem); 458edccf661SDaniel Henrique Barboza backend_fd = memory_region_get_fd(&backend->mr); 459b5513584SShivaprasad G Bhat 460b5513584SShivaprasad G Bhat if (object_property_get_bool(OBJECT(backend), "pmem", NULL)) { 461b5513584SShivaprasad G Bhat MemoryRegion *mr = host_memory_backend_get_memory(dimm->hostmem); 462b5513584SShivaprasad G Bhat void *ptr = memory_region_get_ram_ptr(mr); 463b5513584SShivaprasad G Bhat size_t size = object_property_get_uint(OBJECT(dimm), PC_DIMM_SIZE_PROP, 464b5513584SShivaprasad G Bhat NULL); 465b5513584SShivaprasad G Bhat 466b5513584SShivaprasad G Bhat /* flush pmem backend */ 467b5513584SShivaprasad G Bhat pmem_persist(ptr, size); 468b5513584SShivaprasad G Bhat } else { 469b5513584SShivaprasad G Bhat /* flush raw backing image */ 470b5513584SShivaprasad G Bhat if (qemu_fdatasync(backend_fd) < 0) { 471b5513584SShivaprasad G Bhat error_report("papr_scm: Could not sync nvdimm to backend file: %s", 472b5513584SShivaprasad G Bhat strerror(errno)); 473b5513584SShivaprasad G Bhat return H_HARDWARE; 474b5513584SShivaprasad G Bhat } 475b5513584SShivaprasad G Bhat } 476b5513584SShivaprasad G Bhat 477b5513584SShivaprasad G Bhat return H_SUCCESS; 478b5513584SShivaprasad G Bhat } 479b5513584SShivaprasad G Bhat 480b5513584SShivaprasad G Bhat static void spapr_nvdimm_flush_completion_cb(void *opaque, int hcall_ret) 481b5513584SShivaprasad G Bhat { 482b5513584SShivaprasad G Bhat SpaprNVDIMMDeviceFlushState *state = opaque; 483b5513584SShivaprasad G Bhat SpaprDrc *drc = spapr_drc_by_index(state->drcidx); 484edccf661SDaniel Henrique Barboza SpaprNVDIMMDevice *s_nvdimm; 485edccf661SDaniel Henrique Barboza 486edccf661SDaniel Henrique Barboza g_assert(drc != NULL); 487edccf661SDaniel Henrique Barboza 488edccf661SDaniel Henrique Barboza s_nvdimm = SPAPR_NVDIMM(drc->dev); 489b5513584SShivaprasad G Bhat 490b5513584SShivaprasad G Bhat state->hcall_ret = hcall_ret; 491b5513584SShivaprasad G Bhat QLIST_REMOVE(state, node); 492b5513584SShivaprasad G Bhat QLIST_INSERT_HEAD(&s_nvdimm->completed_nvdimm_flush_states, state, node); 493b5513584SShivaprasad G Bhat } 494b5513584SShivaprasad G Bhat 495b5513584SShivaprasad G Bhat static int spapr_nvdimm_flush_post_load(void *opaque, int version_id) 496b5513584SShivaprasad G Bhat { 497b5513584SShivaprasad G Bhat SpaprNVDIMMDevice *s_nvdimm = (SpaprNVDIMMDevice *)opaque; 498b5513584SShivaprasad G Bhat SpaprNVDIMMDeviceFlushState *state; 4998601b4f1SShivaprasad G Bhat HostMemoryBackend *backend = MEMORY_BACKEND(PC_DIMM(s_nvdimm)->hostmem); 5008601b4f1SShivaprasad G Bhat bool is_pmem = object_property_get_bool(OBJECT(backend), "pmem", NULL); 5018601b4f1SShivaprasad G Bhat bool pmem_override = object_property_get_bool(OBJECT(s_nvdimm), 5028601b4f1SShivaprasad G Bhat "pmem-override", NULL); 5038601b4f1SShivaprasad G Bhat bool dest_hcall_flush_required = pmem_override || !is_pmem; 5048601b4f1SShivaprasad G Bhat 5058601b4f1SShivaprasad G Bhat if (!s_nvdimm->hcall_flush_required && dest_hcall_flush_required) { 5068601b4f1SShivaprasad G Bhat error_report("The file backend for the spapr-nvdimm device %s at " 5078601b4f1SShivaprasad G Bhat "source is a pmem, use pmem=on and pmem-override=off to " 5088601b4f1SShivaprasad G Bhat "continue.", DEVICE(s_nvdimm)->id); 5098601b4f1SShivaprasad G Bhat return -EINVAL; 5108601b4f1SShivaprasad G Bhat } 5118601b4f1SShivaprasad G Bhat if (s_nvdimm->hcall_flush_required && !dest_hcall_flush_required) { 5128601b4f1SShivaprasad G Bhat error_report("The guest expects hcall-flush support for the " 5138601b4f1SShivaprasad G Bhat "spapr-nvdimm device %s, use pmem_override=on to " 5148601b4f1SShivaprasad G Bhat "continue.", DEVICE(s_nvdimm)->id); 5158601b4f1SShivaprasad G Bhat return -EINVAL; 5168601b4f1SShivaprasad G Bhat } 517b5513584SShivaprasad G Bhat 518b5513584SShivaprasad G Bhat QLIST_FOREACH(state, &s_nvdimm->pending_nvdimm_flush_states, node) { 519*aef04fc7SEmanuele Giuseppe Esposito thread_pool_submit_aio(flush_worker_cb, state, 520b5513584SShivaprasad G Bhat spapr_nvdimm_flush_completion_cb, state); 521b5513584SShivaprasad G Bhat } 522b5513584SShivaprasad G Bhat 523b5513584SShivaprasad G Bhat return 0; 524b5513584SShivaprasad G Bhat } 525b5513584SShivaprasad G Bhat 526b5513584SShivaprasad G Bhat static const VMStateDescription vmstate_spapr_nvdimm_flush_state = { 527b5513584SShivaprasad G Bhat .name = "spapr_nvdimm_flush_state", 528b5513584SShivaprasad G Bhat .version_id = 1, 529b5513584SShivaprasad G Bhat .minimum_version_id = 1, 530b5513584SShivaprasad G Bhat .fields = (VMStateField[]) { 531b5513584SShivaprasad G Bhat VMSTATE_UINT64(continue_token, SpaprNVDIMMDeviceFlushState), 532b5513584SShivaprasad G Bhat VMSTATE_INT64(hcall_ret, SpaprNVDIMMDeviceFlushState), 533b5513584SShivaprasad G Bhat VMSTATE_UINT32(drcidx, SpaprNVDIMMDeviceFlushState), 534b5513584SShivaprasad G Bhat VMSTATE_END_OF_LIST() 535b5513584SShivaprasad G Bhat }, 536b5513584SShivaprasad G Bhat }; 537b5513584SShivaprasad G Bhat 538b5513584SShivaprasad G Bhat const VMStateDescription vmstate_spapr_nvdimm_states = { 539b5513584SShivaprasad G Bhat .name = "spapr_nvdimm_states", 540b5513584SShivaprasad G Bhat .version_id = 1, 541b5513584SShivaprasad G Bhat .minimum_version_id = 1, 542b5513584SShivaprasad G Bhat .post_load = spapr_nvdimm_flush_post_load, 543b5513584SShivaprasad G Bhat .fields = (VMStateField[]) { 5448601b4f1SShivaprasad G Bhat VMSTATE_BOOL(hcall_flush_required, SpaprNVDIMMDevice), 545b5513584SShivaprasad G Bhat VMSTATE_UINT64(nvdimm_flush_token, SpaprNVDIMMDevice), 546b5513584SShivaprasad G Bhat VMSTATE_QLIST_V(completed_nvdimm_flush_states, SpaprNVDIMMDevice, 1, 547b5513584SShivaprasad G Bhat vmstate_spapr_nvdimm_flush_state, 548b5513584SShivaprasad G Bhat SpaprNVDIMMDeviceFlushState, node), 549b5513584SShivaprasad G Bhat VMSTATE_QLIST_V(pending_nvdimm_flush_states, SpaprNVDIMMDevice, 1, 550b5513584SShivaprasad G Bhat vmstate_spapr_nvdimm_flush_state, 551b5513584SShivaprasad G Bhat SpaprNVDIMMDeviceFlushState, node), 552b5513584SShivaprasad G Bhat VMSTATE_END_OF_LIST() 553b5513584SShivaprasad G Bhat }, 554b5513584SShivaprasad G Bhat }; 555b5513584SShivaprasad G Bhat 556b5513584SShivaprasad G Bhat /* 557b5513584SShivaprasad G Bhat * Assign a token and reserve it for the new flush state. 558b5513584SShivaprasad G Bhat */ 559b5513584SShivaprasad G Bhat static SpaprNVDIMMDeviceFlushState *spapr_nvdimm_init_new_flush_state( 560b5513584SShivaprasad G Bhat SpaprNVDIMMDevice *spapr_nvdimm) 561b5513584SShivaprasad G Bhat { 562b5513584SShivaprasad G Bhat SpaprNVDIMMDeviceFlushState *state; 563b5513584SShivaprasad G Bhat 564b5513584SShivaprasad G Bhat state = g_malloc0(sizeof(*state)); 565b5513584SShivaprasad G Bhat 566b5513584SShivaprasad G Bhat spapr_nvdimm->nvdimm_flush_token++; 567b5513584SShivaprasad G Bhat /* Token zero is presumed as no job pending. Assert on overflow to zero */ 568b5513584SShivaprasad G Bhat g_assert(spapr_nvdimm->nvdimm_flush_token != 0); 569b5513584SShivaprasad G Bhat 570b5513584SShivaprasad G Bhat state->continue_token = spapr_nvdimm->nvdimm_flush_token; 571b5513584SShivaprasad G Bhat 572b5513584SShivaprasad G Bhat QLIST_INSERT_HEAD(&spapr_nvdimm->pending_nvdimm_flush_states, state, node); 573b5513584SShivaprasad G Bhat 574b5513584SShivaprasad G Bhat return state; 575b5513584SShivaprasad G Bhat } 576b5513584SShivaprasad G Bhat 577b5513584SShivaprasad G Bhat /* 578b5513584SShivaprasad G Bhat * spapr_nvdimm_finish_flushes 579b5513584SShivaprasad G Bhat * Waits for all pending flush requests to complete 580b5513584SShivaprasad G Bhat * their execution and free the states 581b5513584SShivaprasad G Bhat */ 582b5513584SShivaprasad G Bhat void spapr_nvdimm_finish_flushes(void) 583b5513584SShivaprasad G Bhat { 584b5513584SShivaprasad G Bhat SpaprNVDIMMDeviceFlushState *state, *next; 585b5513584SShivaprasad G Bhat GSList *list, *nvdimms; 586b5513584SShivaprasad G Bhat 587b5513584SShivaprasad G Bhat /* 588b5513584SShivaprasad G Bhat * Called on reset path, the main loop thread which calls 589b5513584SShivaprasad G Bhat * the pending BHs has gotten out running in the reset path, 590b5513584SShivaprasad G Bhat * finally reaching here. Other code path being guest 591b5513584SShivaprasad G Bhat * h_client_architecture_support, thats early boot up. 592b5513584SShivaprasad G Bhat */ 593b5513584SShivaprasad G Bhat nvdimms = nvdimm_get_device_list(); 594b5513584SShivaprasad G Bhat for (list = nvdimms; list; list = list->next) { 595b5513584SShivaprasad G Bhat NVDIMMDevice *nvdimm = list->data; 596b5513584SShivaprasad G Bhat if (object_dynamic_cast(OBJECT(nvdimm), TYPE_SPAPR_NVDIMM)) { 597b5513584SShivaprasad G Bhat SpaprNVDIMMDevice *s_nvdimm = SPAPR_NVDIMM(nvdimm); 598b5513584SShivaprasad G Bhat while (!QLIST_EMPTY(&s_nvdimm->pending_nvdimm_flush_states)) { 599b5513584SShivaprasad G Bhat aio_poll(qemu_get_aio_context(), true); 600b5513584SShivaprasad G Bhat } 601b5513584SShivaprasad G Bhat 602b5513584SShivaprasad G Bhat QLIST_FOREACH_SAFE(state, &s_nvdimm->completed_nvdimm_flush_states, 603b5513584SShivaprasad G Bhat node, next) { 604b5513584SShivaprasad G Bhat QLIST_REMOVE(state, node); 605b5513584SShivaprasad G Bhat g_free(state); 606b5513584SShivaprasad G Bhat } 607b5513584SShivaprasad G Bhat } 608b5513584SShivaprasad G Bhat } 609b5513584SShivaprasad G Bhat g_slist_free(nvdimms); 610b5513584SShivaprasad G Bhat } 611b5513584SShivaprasad G Bhat 612b5513584SShivaprasad G Bhat /* 613b5513584SShivaprasad G Bhat * spapr_nvdimm_get_flush_status 614b5513584SShivaprasad G Bhat * Fetches the status of the hcall worker and returns 615b5513584SShivaprasad G Bhat * H_LONG_BUSY_ORDER_10_MSEC if the worker is still running. 616b5513584SShivaprasad G Bhat */ 617b5513584SShivaprasad G Bhat static int spapr_nvdimm_get_flush_status(SpaprNVDIMMDevice *s_nvdimm, 618b5513584SShivaprasad G Bhat uint64_t token) 619b5513584SShivaprasad G Bhat { 620b5513584SShivaprasad G Bhat SpaprNVDIMMDeviceFlushState *state, *node; 621b5513584SShivaprasad G Bhat 622b5513584SShivaprasad G Bhat QLIST_FOREACH(state, &s_nvdimm->pending_nvdimm_flush_states, node) { 623b5513584SShivaprasad G Bhat if (state->continue_token == token) { 624b5513584SShivaprasad G Bhat return H_LONG_BUSY_ORDER_10_MSEC; 625b5513584SShivaprasad G Bhat } 626b5513584SShivaprasad G Bhat } 627b5513584SShivaprasad G Bhat 628b5513584SShivaprasad G Bhat QLIST_FOREACH_SAFE(state, &s_nvdimm->completed_nvdimm_flush_states, 629b5513584SShivaprasad G Bhat node, node) { 630b5513584SShivaprasad G Bhat if (state->continue_token == token) { 631b5513584SShivaprasad G Bhat int ret = state->hcall_ret; 632b5513584SShivaprasad G Bhat QLIST_REMOVE(state, node); 633b5513584SShivaprasad G Bhat g_free(state); 634b5513584SShivaprasad G Bhat return ret; 635b5513584SShivaprasad G Bhat } 636b5513584SShivaprasad G Bhat } 637b5513584SShivaprasad G Bhat 638b5513584SShivaprasad G Bhat /* If not found in complete list too, invalid token */ 639b5513584SShivaprasad G Bhat return H_P2; 640b5513584SShivaprasad G Bhat } 641b5513584SShivaprasad G Bhat 642b5513584SShivaprasad G Bhat /* 643b5513584SShivaprasad G Bhat * H_SCM_FLUSH 644b5513584SShivaprasad G Bhat * Input: drc_index, continue-token 645b5513584SShivaprasad G Bhat * Out: continue-token 646b5513584SShivaprasad G Bhat * Return Value: H_SUCCESS, H_Parameter, H_P2, H_LONG_BUSY_ORDER_10_MSEC, 647b5513584SShivaprasad G Bhat * H_UNSUPPORTED 648b5513584SShivaprasad G Bhat * 649b5513584SShivaprasad G Bhat * Given a DRC Index Flush the data to backend NVDIMM device. The hcall returns 650b5513584SShivaprasad G Bhat * H_LONG_BUSY_ORDER_10_MSEC when the flush takes longer time and the hcall 651b5513584SShivaprasad G Bhat * needs to be issued multiple times in order to be completely serviced. The 652b5513584SShivaprasad G Bhat * continue-token from the output to be passed in the argument list of 653b5513584SShivaprasad G Bhat * subsequent hcalls until the hcall is completely serviced at which point 654b5513584SShivaprasad G Bhat * H_SUCCESS or other error is returned. 655b5513584SShivaprasad G Bhat */ 656b5513584SShivaprasad G Bhat static target_ulong h_scm_flush(PowerPCCPU *cpu, SpaprMachineState *spapr, 657b5513584SShivaprasad G Bhat target_ulong opcode, target_ulong *args) 658b5513584SShivaprasad G Bhat { 659b5513584SShivaprasad G Bhat int ret; 660b5513584SShivaprasad G Bhat uint32_t drc_index = args[0]; 661b5513584SShivaprasad G Bhat uint64_t continue_token = args[1]; 662b5513584SShivaprasad G Bhat SpaprDrc *drc = spapr_drc_by_index(drc_index); 663b5513584SShivaprasad G Bhat PCDIMMDevice *dimm; 664b5513584SShivaprasad G Bhat HostMemoryBackend *backend = NULL; 665b5513584SShivaprasad G Bhat SpaprNVDIMMDeviceFlushState *state; 666b5513584SShivaprasad G Bhat int fd; 667b5513584SShivaprasad G Bhat 668b5513584SShivaprasad G Bhat if (!drc || !drc->dev || 669b5513584SShivaprasad G Bhat spapr_drc_type(drc) != SPAPR_DR_CONNECTOR_TYPE_PMEM) { 670b5513584SShivaprasad G Bhat return H_PARAMETER; 671b5513584SShivaprasad G Bhat } 672b5513584SShivaprasad G Bhat 673b5513584SShivaprasad G Bhat dimm = PC_DIMM(drc->dev); 6748601b4f1SShivaprasad G Bhat if (!object_dynamic_cast(OBJECT(dimm), TYPE_SPAPR_NVDIMM)) { 6758601b4f1SShivaprasad G Bhat return H_PARAMETER; 6768601b4f1SShivaprasad G Bhat } 677b5513584SShivaprasad G Bhat if (continue_token == 0) { 6788601b4f1SShivaprasad G Bhat bool is_pmem = false, pmem_override = false; 679b5513584SShivaprasad G Bhat backend = MEMORY_BACKEND(dimm->hostmem); 680b5513584SShivaprasad G Bhat fd = memory_region_get_fd(&backend->mr); 681b5513584SShivaprasad G Bhat 682b5513584SShivaprasad G Bhat if (fd < 0) { 683b5513584SShivaprasad G Bhat return H_UNSUPPORTED; 684b5513584SShivaprasad G Bhat } 685b5513584SShivaprasad G Bhat 6868601b4f1SShivaprasad G Bhat is_pmem = object_property_get_bool(OBJECT(backend), "pmem", NULL); 6878601b4f1SShivaprasad G Bhat pmem_override = object_property_get_bool(OBJECT(dimm), 6888601b4f1SShivaprasad G Bhat "pmem-override", NULL); 6898601b4f1SShivaprasad G Bhat if (is_pmem && !pmem_override) { 6908601b4f1SShivaprasad G Bhat return H_UNSUPPORTED; 6918601b4f1SShivaprasad G Bhat } 6928601b4f1SShivaprasad G Bhat 693b5513584SShivaprasad G Bhat state = spapr_nvdimm_init_new_flush_state(SPAPR_NVDIMM(dimm)); 694b5513584SShivaprasad G Bhat if (!state) { 695b5513584SShivaprasad G Bhat return H_HARDWARE; 696b5513584SShivaprasad G Bhat } 697b5513584SShivaprasad G Bhat 698b5513584SShivaprasad G Bhat state->drcidx = drc_index; 699b5513584SShivaprasad G Bhat 700*aef04fc7SEmanuele Giuseppe Esposito thread_pool_submit_aio(flush_worker_cb, state, 701b5513584SShivaprasad G Bhat spapr_nvdimm_flush_completion_cb, state); 702b5513584SShivaprasad G Bhat 703b5513584SShivaprasad G Bhat continue_token = state->continue_token; 704b5513584SShivaprasad G Bhat } 705b5513584SShivaprasad G Bhat 706b5513584SShivaprasad G Bhat ret = spapr_nvdimm_get_flush_status(SPAPR_NVDIMM(dimm), continue_token); 707b5513584SShivaprasad G Bhat if (H_IS_LONG_BUSY(ret)) { 708b5513584SShivaprasad G Bhat args[0] = continue_token; 709b5513584SShivaprasad G Bhat } 710b5513584SShivaprasad G Bhat 711b5513584SShivaprasad G Bhat return ret; 712b5513584SShivaprasad G Bhat } 713b5513584SShivaprasad G Bhat 714b5fca656SShivaprasad G Bhat static target_ulong h_scm_unbind_mem(PowerPCCPU *cpu, SpaprMachineState *spapr, 715b5fca656SShivaprasad G Bhat target_ulong opcode, target_ulong *args) 716b5fca656SShivaprasad G Bhat { 717b5fca656SShivaprasad G Bhat uint32_t drc_index = args[0]; 718b5fca656SShivaprasad G Bhat uint64_t starting_scm_logical_addr = args[1]; 719b5fca656SShivaprasad G Bhat uint64_t no_of_scm_blocks_to_unbind = args[2]; 720b5fca656SShivaprasad G Bhat uint64_t continue_token = args[3]; 721b5fca656SShivaprasad G Bhat uint64_t size_to_unbind; 722b5fca656SShivaprasad G Bhat Range blockrange = range_empty; 723b5fca656SShivaprasad G Bhat Range nvdimmrange = range_empty; 724b5fca656SShivaprasad G Bhat SpaprDrc *drc = spapr_drc_by_index(drc_index); 725b5fca656SShivaprasad G Bhat NVDIMMDevice *nvdimm; 726b5fca656SShivaprasad G Bhat uint64_t size, addr; 727b5fca656SShivaprasad G Bhat 728b5fca656SShivaprasad G Bhat if (!drc || !drc->dev || 729b5fca656SShivaprasad G Bhat spapr_drc_type(drc) != SPAPR_DR_CONNECTOR_TYPE_PMEM) { 730b5fca656SShivaprasad G Bhat return H_PARAMETER; 731b5fca656SShivaprasad G Bhat } 732b5fca656SShivaprasad G Bhat 733b5fca656SShivaprasad G Bhat /* continue_token should be zero as this hcall doesn't return H_BUSY. */ 734b5fca656SShivaprasad G Bhat if (continue_token > 0) { 735b5fca656SShivaprasad G Bhat return H_P4; 736b5fca656SShivaprasad G Bhat } 737b5fca656SShivaprasad G Bhat 738b5fca656SShivaprasad G Bhat /* Check if starting_scm_logical_addr is block aligned */ 739b5fca656SShivaprasad G Bhat if (!QEMU_IS_ALIGNED(starting_scm_logical_addr, 740b5fca656SShivaprasad G Bhat SPAPR_MINIMUM_SCM_BLOCK_SIZE)) { 741b5fca656SShivaprasad G Bhat return H_P2; 742b5fca656SShivaprasad G Bhat } 743b5fca656SShivaprasad G Bhat 744b5fca656SShivaprasad G Bhat size_to_unbind = no_of_scm_blocks_to_unbind * SPAPR_MINIMUM_SCM_BLOCK_SIZE; 745b5fca656SShivaprasad G Bhat if (no_of_scm_blocks_to_unbind == 0 || no_of_scm_blocks_to_unbind != 746b5fca656SShivaprasad G Bhat size_to_unbind / SPAPR_MINIMUM_SCM_BLOCK_SIZE) { 747b5fca656SShivaprasad G Bhat return H_P3; 748b5fca656SShivaprasad G Bhat } 749b5fca656SShivaprasad G Bhat 750b5fca656SShivaprasad G Bhat nvdimm = NVDIMM(drc->dev); 751b5fca656SShivaprasad G Bhat size = object_property_get_int(OBJECT(nvdimm), PC_DIMM_SIZE_PROP, 752b5fca656SShivaprasad G Bhat &error_abort); 753b5fca656SShivaprasad G Bhat addr = object_property_get_int(OBJECT(nvdimm), PC_DIMM_ADDR_PROP, 754b5fca656SShivaprasad G Bhat &error_abort); 755b5fca656SShivaprasad G Bhat 756b5fca656SShivaprasad G Bhat range_init_nofail(&nvdimmrange, addr, size); 757b5fca656SShivaprasad G Bhat range_init_nofail(&blockrange, starting_scm_logical_addr, size_to_unbind); 758b5fca656SShivaprasad G Bhat 759b5fca656SShivaprasad G Bhat if (!range_contains_range(&nvdimmrange, &blockrange)) { 760b5fca656SShivaprasad G Bhat return H_P3; 761b5fca656SShivaprasad G Bhat } 762b5fca656SShivaprasad G Bhat 763b5fca656SShivaprasad G Bhat args[1] = no_of_scm_blocks_to_unbind; 764b5fca656SShivaprasad G Bhat 765b5fca656SShivaprasad G Bhat /* let unplug take care of actual unbind */ 766b5fca656SShivaprasad G Bhat return H_SUCCESS; 767b5fca656SShivaprasad G Bhat } 768b5fca656SShivaprasad G Bhat 769b5fca656SShivaprasad G Bhat #define H_UNBIND_SCOPE_ALL 0x1 770b5fca656SShivaprasad G Bhat #define H_UNBIND_SCOPE_DRC 0x2 771b5fca656SShivaprasad G Bhat 772b5fca656SShivaprasad G Bhat static target_ulong h_scm_unbind_all(PowerPCCPU *cpu, SpaprMachineState *spapr, 773b5fca656SShivaprasad G Bhat target_ulong opcode, target_ulong *args) 774b5fca656SShivaprasad G Bhat { 775b5fca656SShivaprasad G Bhat uint64_t target_scope = args[0]; 776b5fca656SShivaprasad G Bhat uint32_t drc_index = args[1]; 777b5fca656SShivaprasad G Bhat uint64_t continue_token = args[2]; 778b5fca656SShivaprasad G Bhat NVDIMMDevice *nvdimm; 779b5fca656SShivaprasad G Bhat uint64_t size; 780b5fca656SShivaprasad G Bhat uint64_t no_of_scm_blocks_unbound = 0; 781b5fca656SShivaprasad G Bhat 782b5fca656SShivaprasad G Bhat /* continue_token should be zero as this hcall doesn't return H_BUSY. */ 783b5fca656SShivaprasad G Bhat if (continue_token > 0) { 784b5fca656SShivaprasad G Bhat return H_P4; 785b5fca656SShivaprasad G Bhat } 786b5fca656SShivaprasad G Bhat 787b5fca656SShivaprasad G Bhat if (target_scope == H_UNBIND_SCOPE_DRC) { 788b5fca656SShivaprasad G Bhat SpaprDrc *drc = spapr_drc_by_index(drc_index); 789b5fca656SShivaprasad G Bhat 790b5fca656SShivaprasad G Bhat if (!drc || !drc->dev || 791b5fca656SShivaprasad G Bhat spapr_drc_type(drc) != SPAPR_DR_CONNECTOR_TYPE_PMEM) { 792b5fca656SShivaprasad G Bhat return H_P2; 793b5fca656SShivaprasad G Bhat } 794b5fca656SShivaprasad G Bhat 795b5fca656SShivaprasad G Bhat nvdimm = NVDIMM(drc->dev); 796b5fca656SShivaprasad G Bhat size = object_property_get_int(OBJECT(nvdimm), PC_DIMM_SIZE_PROP, 797b5fca656SShivaprasad G Bhat &error_abort); 798b5fca656SShivaprasad G Bhat 799b5fca656SShivaprasad G Bhat no_of_scm_blocks_unbound = size / SPAPR_MINIMUM_SCM_BLOCK_SIZE; 800b5fca656SShivaprasad G Bhat } else if (target_scope == H_UNBIND_SCOPE_ALL) { 801b5fca656SShivaprasad G Bhat GSList *list, *nvdimms; 802b5fca656SShivaprasad G Bhat 803b5fca656SShivaprasad G Bhat nvdimms = nvdimm_get_device_list(); 804b5fca656SShivaprasad G Bhat for (list = nvdimms; list; list = list->next) { 805b5fca656SShivaprasad G Bhat nvdimm = list->data; 806b5fca656SShivaprasad G Bhat size = object_property_get_int(OBJECT(nvdimm), PC_DIMM_SIZE_PROP, 807b5fca656SShivaprasad G Bhat &error_abort); 808b5fca656SShivaprasad G Bhat 809b5fca656SShivaprasad G Bhat no_of_scm_blocks_unbound += size / SPAPR_MINIMUM_SCM_BLOCK_SIZE; 810b5fca656SShivaprasad G Bhat } 811b5fca656SShivaprasad G Bhat g_slist_free(nvdimms); 812b5fca656SShivaprasad G Bhat } else { 813b5fca656SShivaprasad G Bhat return H_PARAMETER; 814b5fca656SShivaprasad G Bhat } 815b5fca656SShivaprasad G Bhat 816b5fca656SShivaprasad G Bhat args[1] = no_of_scm_blocks_unbound; 817b5fca656SShivaprasad G Bhat 818b5fca656SShivaprasad G Bhat /* let unplug take care of actual unbind */ 819b5fca656SShivaprasad G Bhat return H_SUCCESS; 820b5fca656SShivaprasad G Bhat } 821b5fca656SShivaprasad G Bhat 82253d7d7e2SVaibhav Jain static target_ulong h_scm_health(PowerPCCPU *cpu, SpaprMachineState *spapr, 82353d7d7e2SVaibhav Jain target_ulong opcode, target_ulong *args) 82453d7d7e2SVaibhav Jain { 82553d7d7e2SVaibhav Jain 82653d7d7e2SVaibhav Jain NVDIMMDevice *nvdimm; 82753d7d7e2SVaibhav Jain uint64_t hbitmap = 0; 82853d7d7e2SVaibhav Jain uint32_t drc_index = args[0]; 82953d7d7e2SVaibhav Jain SpaprDrc *drc = spapr_drc_by_index(drc_index); 83053d7d7e2SVaibhav Jain const uint64_t hbitmap_mask = PAPR_PMEM_UNARMED; 83153d7d7e2SVaibhav Jain 83253d7d7e2SVaibhav Jain 83353d7d7e2SVaibhav Jain /* Ensure that the drc is valid & is valid PMEM dimm and is plugged in */ 83453d7d7e2SVaibhav Jain if (!drc || !drc->dev || 83553d7d7e2SVaibhav Jain spapr_drc_type(drc) != SPAPR_DR_CONNECTOR_TYPE_PMEM) { 83653d7d7e2SVaibhav Jain return H_PARAMETER; 83753d7d7e2SVaibhav Jain } 83853d7d7e2SVaibhav Jain 83953d7d7e2SVaibhav Jain nvdimm = NVDIMM(drc->dev); 84053d7d7e2SVaibhav Jain 84153d7d7e2SVaibhav Jain /* Update if the nvdimm is unarmed and send its status via health bitmaps */ 84253d7d7e2SVaibhav Jain if (object_property_get_bool(OBJECT(nvdimm), NVDIMM_UNARMED_PROP, NULL)) { 84353d7d7e2SVaibhav Jain hbitmap |= PAPR_PMEM_UNARMED; 84453d7d7e2SVaibhav Jain } 84553d7d7e2SVaibhav Jain 84653d7d7e2SVaibhav Jain /* Update the out args with health bitmap/mask */ 84753d7d7e2SVaibhav Jain args[0] = hbitmap; 84853d7d7e2SVaibhav Jain args[1] = hbitmap_mask; 84953d7d7e2SVaibhav Jain 85053d7d7e2SVaibhav Jain return H_SUCCESS; 85153d7d7e2SVaibhav Jain } 85253d7d7e2SVaibhav Jain 853b5fca656SShivaprasad G Bhat static void spapr_scm_register_types(void) 854b5fca656SShivaprasad G Bhat { 855b5fca656SShivaprasad G Bhat /* qemu/scm specific hcalls */ 856b5fca656SShivaprasad G Bhat spapr_register_hypercall(H_SCM_READ_METADATA, h_scm_read_metadata); 857b5fca656SShivaprasad G Bhat spapr_register_hypercall(H_SCM_WRITE_METADATA, h_scm_write_metadata); 858b5fca656SShivaprasad G Bhat spapr_register_hypercall(H_SCM_BIND_MEM, h_scm_bind_mem); 859b5fca656SShivaprasad G Bhat spapr_register_hypercall(H_SCM_UNBIND_MEM, h_scm_unbind_mem); 860b5fca656SShivaprasad G Bhat spapr_register_hypercall(H_SCM_UNBIND_ALL, h_scm_unbind_all); 86153d7d7e2SVaibhav Jain spapr_register_hypercall(H_SCM_HEALTH, h_scm_health); 862b5513584SShivaprasad G Bhat spapr_register_hypercall(H_SCM_FLUSH, h_scm_flush); 863b5fca656SShivaprasad G Bhat } 864b5fca656SShivaprasad G Bhat 865b5fca656SShivaprasad G Bhat type_init(spapr_scm_register_types) 8668601b4f1SShivaprasad G Bhat 8678601b4f1SShivaprasad G Bhat static void spapr_nvdimm_realize(NVDIMMDevice *dimm, Error **errp) 8688601b4f1SShivaprasad G Bhat { 8698601b4f1SShivaprasad G Bhat SpaprNVDIMMDevice *s_nvdimm = SPAPR_NVDIMM(dimm); 8708601b4f1SShivaprasad G Bhat HostMemoryBackend *backend = MEMORY_BACKEND(PC_DIMM(dimm)->hostmem); 8718601b4f1SShivaprasad G Bhat bool is_pmem = object_property_get_bool(OBJECT(backend), "pmem", NULL); 8728601b4f1SShivaprasad G Bhat bool pmem_override = object_property_get_bool(OBJECT(dimm), "pmem-override", 8738601b4f1SShivaprasad G Bhat NULL); 8748601b4f1SShivaprasad G Bhat if (!is_pmem || pmem_override) { 8758601b4f1SShivaprasad G Bhat s_nvdimm->hcall_flush_required = true; 8768601b4f1SShivaprasad G Bhat } 8778601b4f1SShivaprasad G Bhat 8788601b4f1SShivaprasad G Bhat vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, 8798601b4f1SShivaprasad G Bhat &vmstate_spapr_nvdimm_states, dimm); 8808601b4f1SShivaprasad G Bhat } 8818601b4f1SShivaprasad G Bhat 8828601b4f1SShivaprasad G Bhat static void spapr_nvdimm_unrealize(NVDIMMDevice *dimm) 8838601b4f1SShivaprasad G Bhat { 8848601b4f1SShivaprasad G Bhat vmstate_unregister(NULL, &vmstate_spapr_nvdimm_states, dimm); 8858601b4f1SShivaprasad G Bhat } 8868601b4f1SShivaprasad G Bhat 8878601b4f1SShivaprasad G Bhat static Property spapr_nvdimm_properties[] = { 8888601b4f1SShivaprasad G Bhat #ifdef CONFIG_LIBPMEM 8898601b4f1SShivaprasad G Bhat DEFINE_PROP_BOOL("pmem-override", SpaprNVDIMMDevice, pmem_override, false), 8908601b4f1SShivaprasad G Bhat #endif 8918601b4f1SShivaprasad G Bhat DEFINE_PROP_END_OF_LIST(), 8928601b4f1SShivaprasad G Bhat }; 8938601b4f1SShivaprasad G Bhat 8948601b4f1SShivaprasad G Bhat static void spapr_nvdimm_class_init(ObjectClass *oc, void *data) 8958601b4f1SShivaprasad G Bhat { 8968601b4f1SShivaprasad G Bhat DeviceClass *dc = DEVICE_CLASS(oc); 8978601b4f1SShivaprasad G Bhat NVDIMMClass *nvc = NVDIMM_CLASS(oc); 8988601b4f1SShivaprasad G Bhat 8998601b4f1SShivaprasad G Bhat nvc->realize = spapr_nvdimm_realize; 9008601b4f1SShivaprasad G Bhat nvc->unrealize = spapr_nvdimm_unrealize; 9018601b4f1SShivaprasad G Bhat 9028601b4f1SShivaprasad G Bhat device_class_set_props(dc, spapr_nvdimm_properties); 9038601b4f1SShivaprasad G Bhat } 9048601b4f1SShivaprasad G Bhat 9058601b4f1SShivaprasad G Bhat static void spapr_nvdimm_init(Object *obj) 9068601b4f1SShivaprasad G Bhat { 9078601b4f1SShivaprasad G Bhat SpaprNVDIMMDevice *s_nvdimm = SPAPR_NVDIMM(obj); 9088601b4f1SShivaprasad G Bhat 9098601b4f1SShivaprasad G Bhat s_nvdimm->hcall_flush_required = false; 9108601b4f1SShivaprasad G Bhat QLIST_INIT(&s_nvdimm->pending_nvdimm_flush_states); 9118601b4f1SShivaprasad G Bhat QLIST_INIT(&s_nvdimm->completed_nvdimm_flush_states); 9128601b4f1SShivaprasad G Bhat } 9138601b4f1SShivaprasad G Bhat 9148601b4f1SShivaprasad G Bhat static TypeInfo spapr_nvdimm_info = { 9158601b4f1SShivaprasad G Bhat .name = TYPE_SPAPR_NVDIMM, 9168601b4f1SShivaprasad G Bhat .parent = TYPE_NVDIMM, 9178601b4f1SShivaprasad G Bhat .class_init = spapr_nvdimm_class_init, 9188601b4f1SShivaprasad G Bhat .class_size = sizeof(SPAPRNVDIMMClass), 9198601b4f1SShivaprasad G Bhat .instance_size = sizeof(SpaprNVDIMMDevice), 9208601b4f1SShivaprasad G Bhat .instance_init = spapr_nvdimm_init, 9218601b4f1SShivaprasad G Bhat }; 9228601b4f1SShivaprasad G Bhat 9238601b4f1SShivaprasad G Bhat static void spapr_nvdimm_register_types(void) 9248601b4f1SShivaprasad G Bhat { 9258601b4f1SShivaprasad G Bhat type_register_static(&spapr_nvdimm_info); 9268601b4f1SShivaprasad G Bhat } 9278601b4f1SShivaprasad G Bhat 9288601b4f1SShivaprasad G Bhat type_init(spapr_nvdimm_register_types) 929