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" 37*8601b4f1SShivaprasad 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; 61*8601b4f1SShivaprasad G Bhat 62*8601b4f1SShivaprasad G Bhat /* public */ 63*8601b4f1SShivaprasad G Bhat void (*realize)(NVDIMMDevice *dimm, Error **errp); 64*8601b4f1SShivaprasad 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); 72*8601b4f1SShivaprasad G Bhat PCDIMMDevice *dimm = PC_DIMM(nvdimm); 73*8601b4f1SShivaprasad 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 111*8601b4f1SShivaprasad G Bhat if (object_dynamic_cast(OBJECT(nvdimm), TYPE_SPAPR_NVDIMM) && 112*8601b4f1SShivaprasad G Bhat (memory_region_get_fd(mr) < 0)) { 113*8601b4f1SShivaprasad G Bhat error_setg(errp, "spapr-nvdimm device requires the " 114*8601b4f1SShivaprasad G Bhat "memdev %s to be of memory-backend-file type", 115*8601b4f1SShivaprasad G Bhat object_get_canonical_path_component(OBJECT(dimm->hostmem))); 116*8601b4f1SShivaprasad G Bhat return false; 117*8601b4f1SShivaprasad G Bhat } 118*8601b4f1SShivaprasad 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 190*8601b4f1SShivaprasad G Bhat if (object_dynamic_cast(OBJECT(nvdimm), TYPE_SPAPR_NVDIMM)) { 191*8601b4f1SShivaprasad G Bhat bool is_pmem = false, pmem_override = false; 192*8601b4f1SShivaprasad G Bhat PCDIMMDevice *dimm = PC_DIMM(nvdimm); 193*8601b4f1SShivaprasad G Bhat HostMemoryBackend *hostmem = dimm->hostmem; 194*8601b4f1SShivaprasad G Bhat 195*8601b4f1SShivaprasad G Bhat is_pmem = object_property_get_bool(OBJECT(hostmem), "pmem", NULL); 196*8601b4f1SShivaprasad G Bhat pmem_override = object_property_get_bool(OBJECT(nvdimm), 197*8601b4f1SShivaprasad G Bhat "pmem-override", NULL); 198*8601b4f1SShivaprasad G Bhat if (!is_pmem || pmem_override) { 199*8601b4f1SShivaprasad G Bhat _FDT(fdt_setprop(fdt, child_offset, "ibm,hcall-flush-required", 200*8601b4f1SShivaprasad G Bhat NULL, 0)); 201*8601b4f1SShivaprasad G Bhat } 202*8601b4f1SShivaprasad G Bhat } 203*8601b4f1SShivaprasad 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 { 429*8601b4f1SShivaprasad G Bhat /* private */ 430b5513584SShivaprasad G Bhat NVDIMMDevice parent_obj; 431b5513584SShivaprasad G Bhat 432*8601b4f1SShivaprasad 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; 436*8601b4f1SShivaprasad G Bhat 437*8601b4f1SShivaprasad G Bhat /* public */ 438*8601b4f1SShivaprasad G Bhat 439*8601b4f1SShivaprasad G Bhat /* 440*8601b4f1SShivaprasad G Bhat * The 'on' value for this property forced the qemu to enable the hcall 441*8601b4f1SShivaprasad G Bhat * flush for the nvdimm device even if the backend is a pmem 442*8601b4f1SShivaprasad G Bhat */ 443*8601b4f1SShivaprasad 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); 450b5513584SShivaprasad G Bhat PCDIMMDevice *dimm = PC_DIMM(drc->dev); 451b5513584SShivaprasad G Bhat HostMemoryBackend *backend = MEMORY_BACKEND(dimm->hostmem); 452b5513584SShivaprasad G Bhat int backend_fd = memory_region_get_fd(&backend->mr); 453b5513584SShivaprasad G Bhat 454b5513584SShivaprasad G Bhat if (object_property_get_bool(OBJECT(backend), "pmem", NULL)) { 455b5513584SShivaprasad G Bhat MemoryRegion *mr = host_memory_backend_get_memory(dimm->hostmem); 456b5513584SShivaprasad G Bhat void *ptr = memory_region_get_ram_ptr(mr); 457b5513584SShivaprasad G Bhat size_t size = object_property_get_uint(OBJECT(dimm), PC_DIMM_SIZE_PROP, 458b5513584SShivaprasad G Bhat NULL); 459b5513584SShivaprasad G Bhat 460b5513584SShivaprasad G Bhat /* flush pmem backend */ 461b5513584SShivaprasad G Bhat pmem_persist(ptr, size); 462b5513584SShivaprasad G Bhat } else { 463b5513584SShivaprasad G Bhat /* flush raw backing image */ 464b5513584SShivaprasad G Bhat if (qemu_fdatasync(backend_fd) < 0) { 465b5513584SShivaprasad G Bhat error_report("papr_scm: Could not sync nvdimm to backend file: %s", 466b5513584SShivaprasad G Bhat strerror(errno)); 467b5513584SShivaprasad G Bhat return H_HARDWARE; 468b5513584SShivaprasad G Bhat } 469b5513584SShivaprasad G Bhat } 470b5513584SShivaprasad G Bhat 471b5513584SShivaprasad G Bhat return H_SUCCESS; 472b5513584SShivaprasad G Bhat } 473b5513584SShivaprasad G Bhat 474b5513584SShivaprasad G Bhat static void spapr_nvdimm_flush_completion_cb(void *opaque, int hcall_ret) 475b5513584SShivaprasad G Bhat { 476b5513584SShivaprasad G Bhat SpaprNVDIMMDeviceFlushState *state = opaque; 477b5513584SShivaprasad G Bhat SpaprDrc *drc = spapr_drc_by_index(state->drcidx); 478b5513584SShivaprasad G Bhat SpaprNVDIMMDevice *s_nvdimm = SPAPR_NVDIMM(drc->dev); 479b5513584SShivaprasad G Bhat 480b5513584SShivaprasad G Bhat state->hcall_ret = hcall_ret; 481b5513584SShivaprasad G Bhat QLIST_REMOVE(state, node); 482b5513584SShivaprasad G Bhat QLIST_INSERT_HEAD(&s_nvdimm->completed_nvdimm_flush_states, state, node); 483b5513584SShivaprasad G Bhat } 484b5513584SShivaprasad G Bhat 485b5513584SShivaprasad G Bhat static int spapr_nvdimm_flush_post_load(void *opaque, int version_id) 486b5513584SShivaprasad G Bhat { 487b5513584SShivaprasad G Bhat SpaprNVDIMMDevice *s_nvdimm = (SpaprNVDIMMDevice *)opaque; 488b5513584SShivaprasad G Bhat SpaprNVDIMMDeviceFlushState *state; 489b5513584SShivaprasad G Bhat ThreadPool *pool = aio_get_thread_pool(qemu_get_aio_context()); 490*8601b4f1SShivaprasad G Bhat HostMemoryBackend *backend = MEMORY_BACKEND(PC_DIMM(s_nvdimm)->hostmem); 491*8601b4f1SShivaprasad G Bhat bool is_pmem = object_property_get_bool(OBJECT(backend), "pmem", NULL); 492*8601b4f1SShivaprasad G Bhat bool pmem_override = object_property_get_bool(OBJECT(s_nvdimm), 493*8601b4f1SShivaprasad G Bhat "pmem-override", NULL); 494*8601b4f1SShivaprasad G Bhat bool dest_hcall_flush_required = pmem_override || !is_pmem; 495*8601b4f1SShivaprasad G Bhat 496*8601b4f1SShivaprasad G Bhat if (!s_nvdimm->hcall_flush_required && dest_hcall_flush_required) { 497*8601b4f1SShivaprasad G Bhat error_report("The file backend for the spapr-nvdimm device %s at " 498*8601b4f1SShivaprasad G Bhat "source is a pmem, use pmem=on and pmem-override=off to " 499*8601b4f1SShivaprasad G Bhat "continue.", DEVICE(s_nvdimm)->id); 500*8601b4f1SShivaprasad G Bhat return -EINVAL; 501*8601b4f1SShivaprasad G Bhat } 502*8601b4f1SShivaprasad G Bhat if (s_nvdimm->hcall_flush_required && !dest_hcall_flush_required) { 503*8601b4f1SShivaprasad G Bhat error_report("The guest expects hcall-flush support for the " 504*8601b4f1SShivaprasad G Bhat "spapr-nvdimm device %s, use pmem_override=on to " 505*8601b4f1SShivaprasad G Bhat "continue.", DEVICE(s_nvdimm)->id); 506*8601b4f1SShivaprasad G Bhat return -EINVAL; 507*8601b4f1SShivaprasad G Bhat } 508b5513584SShivaprasad G Bhat 509b5513584SShivaprasad G Bhat QLIST_FOREACH(state, &s_nvdimm->pending_nvdimm_flush_states, node) { 510b5513584SShivaprasad G Bhat thread_pool_submit_aio(pool, flush_worker_cb, state, 511b5513584SShivaprasad G Bhat spapr_nvdimm_flush_completion_cb, state); 512b5513584SShivaprasad G Bhat } 513b5513584SShivaprasad G Bhat 514b5513584SShivaprasad G Bhat return 0; 515b5513584SShivaprasad G Bhat } 516b5513584SShivaprasad G Bhat 517b5513584SShivaprasad G Bhat static const VMStateDescription vmstate_spapr_nvdimm_flush_state = { 518b5513584SShivaprasad G Bhat .name = "spapr_nvdimm_flush_state", 519b5513584SShivaprasad G Bhat .version_id = 1, 520b5513584SShivaprasad G Bhat .minimum_version_id = 1, 521b5513584SShivaprasad G Bhat .fields = (VMStateField[]) { 522b5513584SShivaprasad G Bhat VMSTATE_UINT64(continue_token, SpaprNVDIMMDeviceFlushState), 523b5513584SShivaprasad G Bhat VMSTATE_INT64(hcall_ret, SpaprNVDIMMDeviceFlushState), 524b5513584SShivaprasad G Bhat VMSTATE_UINT32(drcidx, SpaprNVDIMMDeviceFlushState), 525b5513584SShivaprasad G Bhat VMSTATE_END_OF_LIST() 526b5513584SShivaprasad G Bhat }, 527b5513584SShivaprasad G Bhat }; 528b5513584SShivaprasad G Bhat 529b5513584SShivaprasad G Bhat const VMStateDescription vmstate_spapr_nvdimm_states = { 530b5513584SShivaprasad G Bhat .name = "spapr_nvdimm_states", 531b5513584SShivaprasad G Bhat .version_id = 1, 532b5513584SShivaprasad G Bhat .minimum_version_id = 1, 533b5513584SShivaprasad G Bhat .post_load = spapr_nvdimm_flush_post_load, 534b5513584SShivaprasad G Bhat .fields = (VMStateField[]) { 535*8601b4f1SShivaprasad G Bhat VMSTATE_BOOL(hcall_flush_required, SpaprNVDIMMDevice), 536b5513584SShivaprasad G Bhat VMSTATE_UINT64(nvdimm_flush_token, SpaprNVDIMMDevice), 537b5513584SShivaprasad G Bhat VMSTATE_QLIST_V(completed_nvdimm_flush_states, SpaprNVDIMMDevice, 1, 538b5513584SShivaprasad G Bhat vmstate_spapr_nvdimm_flush_state, 539b5513584SShivaprasad G Bhat SpaprNVDIMMDeviceFlushState, node), 540b5513584SShivaprasad G Bhat VMSTATE_QLIST_V(pending_nvdimm_flush_states, SpaprNVDIMMDevice, 1, 541b5513584SShivaprasad G Bhat vmstate_spapr_nvdimm_flush_state, 542b5513584SShivaprasad G Bhat SpaprNVDIMMDeviceFlushState, node), 543b5513584SShivaprasad G Bhat VMSTATE_END_OF_LIST() 544b5513584SShivaprasad G Bhat }, 545b5513584SShivaprasad G Bhat }; 546b5513584SShivaprasad G Bhat 547b5513584SShivaprasad G Bhat /* 548b5513584SShivaprasad G Bhat * Assign a token and reserve it for the new flush state. 549b5513584SShivaprasad G Bhat */ 550b5513584SShivaprasad G Bhat static SpaprNVDIMMDeviceFlushState *spapr_nvdimm_init_new_flush_state( 551b5513584SShivaprasad G Bhat SpaprNVDIMMDevice *spapr_nvdimm) 552b5513584SShivaprasad G Bhat { 553b5513584SShivaprasad G Bhat SpaprNVDIMMDeviceFlushState *state; 554b5513584SShivaprasad G Bhat 555b5513584SShivaprasad G Bhat state = g_malloc0(sizeof(*state)); 556b5513584SShivaprasad G Bhat 557b5513584SShivaprasad G Bhat spapr_nvdimm->nvdimm_flush_token++; 558b5513584SShivaprasad G Bhat /* Token zero is presumed as no job pending. Assert on overflow to zero */ 559b5513584SShivaprasad G Bhat g_assert(spapr_nvdimm->nvdimm_flush_token != 0); 560b5513584SShivaprasad G Bhat 561b5513584SShivaprasad G Bhat state->continue_token = spapr_nvdimm->nvdimm_flush_token; 562b5513584SShivaprasad G Bhat 563b5513584SShivaprasad G Bhat QLIST_INSERT_HEAD(&spapr_nvdimm->pending_nvdimm_flush_states, state, node); 564b5513584SShivaprasad G Bhat 565b5513584SShivaprasad G Bhat return state; 566b5513584SShivaprasad G Bhat } 567b5513584SShivaprasad G Bhat 568b5513584SShivaprasad G Bhat /* 569b5513584SShivaprasad G Bhat * spapr_nvdimm_finish_flushes 570b5513584SShivaprasad G Bhat * Waits for all pending flush requests to complete 571b5513584SShivaprasad G Bhat * their execution and free the states 572b5513584SShivaprasad G Bhat */ 573b5513584SShivaprasad G Bhat void spapr_nvdimm_finish_flushes(void) 574b5513584SShivaprasad G Bhat { 575b5513584SShivaprasad G Bhat SpaprNVDIMMDeviceFlushState *state, *next; 576b5513584SShivaprasad G Bhat GSList *list, *nvdimms; 577b5513584SShivaprasad G Bhat 578b5513584SShivaprasad G Bhat /* 579b5513584SShivaprasad G Bhat * Called on reset path, the main loop thread which calls 580b5513584SShivaprasad G Bhat * the pending BHs has gotten out running in the reset path, 581b5513584SShivaprasad G Bhat * finally reaching here. Other code path being guest 582b5513584SShivaprasad G Bhat * h_client_architecture_support, thats early boot up. 583b5513584SShivaprasad G Bhat */ 584b5513584SShivaprasad G Bhat nvdimms = nvdimm_get_device_list(); 585b5513584SShivaprasad G Bhat for (list = nvdimms; list; list = list->next) { 586b5513584SShivaprasad G Bhat NVDIMMDevice *nvdimm = list->data; 587b5513584SShivaprasad G Bhat if (object_dynamic_cast(OBJECT(nvdimm), TYPE_SPAPR_NVDIMM)) { 588b5513584SShivaprasad G Bhat SpaprNVDIMMDevice *s_nvdimm = SPAPR_NVDIMM(nvdimm); 589b5513584SShivaprasad G Bhat while (!QLIST_EMPTY(&s_nvdimm->pending_nvdimm_flush_states)) { 590b5513584SShivaprasad G Bhat aio_poll(qemu_get_aio_context(), true); 591b5513584SShivaprasad G Bhat } 592b5513584SShivaprasad G Bhat 593b5513584SShivaprasad G Bhat QLIST_FOREACH_SAFE(state, &s_nvdimm->completed_nvdimm_flush_states, 594b5513584SShivaprasad G Bhat node, next) { 595b5513584SShivaprasad G Bhat QLIST_REMOVE(state, node); 596b5513584SShivaprasad G Bhat g_free(state); 597b5513584SShivaprasad G Bhat } 598b5513584SShivaprasad G Bhat } 599b5513584SShivaprasad G Bhat } 600b5513584SShivaprasad G Bhat g_slist_free(nvdimms); 601b5513584SShivaprasad G Bhat } 602b5513584SShivaprasad G Bhat 603b5513584SShivaprasad G Bhat /* 604b5513584SShivaprasad G Bhat * spapr_nvdimm_get_flush_status 605b5513584SShivaprasad G Bhat * Fetches the status of the hcall worker and returns 606b5513584SShivaprasad G Bhat * H_LONG_BUSY_ORDER_10_MSEC if the worker is still running. 607b5513584SShivaprasad G Bhat */ 608b5513584SShivaprasad G Bhat static int spapr_nvdimm_get_flush_status(SpaprNVDIMMDevice *s_nvdimm, 609b5513584SShivaprasad G Bhat uint64_t token) 610b5513584SShivaprasad G Bhat { 611b5513584SShivaprasad G Bhat SpaprNVDIMMDeviceFlushState *state, *node; 612b5513584SShivaprasad G Bhat 613b5513584SShivaprasad G Bhat QLIST_FOREACH(state, &s_nvdimm->pending_nvdimm_flush_states, node) { 614b5513584SShivaprasad G Bhat if (state->continue_token == token) { 615b5513584SShivaprasad G Bhat return H_LONG_BUSY_ORDER_10_MSEC; 616b5513584SShivaprasad G Bhat } 617b5513584SShivaprasad G Bhat } 618b5513584SShivaprasad G Bhat 619b5513584SShivaprasad G Bhat QLIST_FOREACH_SAFE(state, &s_nvdimm->completed_nvdimm_flush_states, 620b5513584SShivaprasad G Bhat node, node) { 621b5513584SShivaprasad G Bhat if (state->continue_token == token) { 622b5513584SShivaprasad G Bhat int ret = state->hcall_ret; 623b5513584SShivaprasad G Bhat QLIST_REMOVE(state, node); 624b5513584SShivaprasad G Bhat g_free(state); 625b5513584SShivaprasad G Bhat return ret; 626b5513584SShivaprasad G Bhat } 627b5513584SShivaprasad G Bhat } 628b5513584SShivaprasad G Bhat 629b5513584SShivaprasad G Bhat /* If not found in complete list too, invalid token */ 630b5513584SShivaprasad G Bhat return H_P2; 631b5513584SShivaprasad G Bhat } 632b5513584SShivaprasad G Bhat 633b5513584SShivaprasad G Bhat /* 634b5513584SShivaprasad G Bhat * H_SCM_FLUSH 635b5513584SShivaprasad G Bhat * Input: drc_index, continue-token 636b5513584SShivaprasad G Bhat * Out: continue-token 637b5513584SShivaprasad G Bhat * Return Value: H_SUCCESS, H_Parameter, H_P2, H_LONG_BUSY_ORDER_10_MSEC, 638b5513584SShivaprasad G Bhat * H_UNSUPPORTED 639b5513584SShivaprasad G Bhat * 640b5513584SShivaprasad G Bhat * Given a DRC Index Flush the data to backend NVDIMM device. The hcall returns 641b5513584SShivaprasad G Bhat * H_LONG_BUSY_ORDER_10_MSEC when the flush takes longer time and the hcall 642b5513584SShivaprasad G Bhat * needs to be issued multiple times in order to be completely serviced. The 643b5513584SShivaprasad G Bhat * continue-token from the output to be passed in the argument list of 644b5513584SShivaprasad G Bhat * subsequent hcalls until the hcall is completely serviced at which point 645b5513584SShivaprasad G Bhat * H_SUCCESS or other error is returned. 646b5513584SShivaprasad G Bhat */ 647b5513584SShivaprasad G Bhat static target_ulong h_scm_flush(PowerPCCPU *cpu, SpaprMachineState *spapr, 648b5513584SShivaprasad G Bhat target_ulong opcode, target_ulong *args) 649b5513584SShivaprasad G Bhat { 650b5513584SShivaprasad G Bhat int ret; 651b5513584SShivaprasad G Bhat uint32_t drc_index = args[0]; 652b5513584SShivaprasad G Bhat uint64_t continue_token = args[1]; 653b5513584SShivaprasad G Bhat SpaprDrc *drc = spapr_drc_by_index(drc_index); 654b5513584SShivaprasad G Bhat PCDIMMDevice *dimm; 655b5513584SShivaprasad G Bhat HostMemoryBackend *backend = NULL; 656b5513584SShivaprasad G Bhat SpaprNVDIMMDeviceFlushState *state; 657b5513584SShivaprasad G Bhat ThreadPool *pool = aio_get_thread_pool(qemu_get_aio_context()); 658b5513584SShivaprasad G Bhat int fd; 659b5513584SShivaprasad G Bhat 660b5513584SShivaprasad G Bhat if (!drc || !drc->dev || 661b5513584SShivaprasad G Bhat spapr_drc_type(drc) != SPAPR_DR_CONNECTOR_TYPE_PMEM) { 662b5513584SShivaprasad G Bhat return H_PARAMETER; 663b5513584SShivaprasad G Bhat } 664b5513584SShivaprasad G Bhat 665b5513584SShivaprasad G Bhat dimm = PC_DIMM(drc->dev); 666*8601b4f1SShivaprasad G Bhat if (!object_dynamic_cast(OBJECT(dimm), TYPE_SPAPR_NVDIMM)) { 667*8601b4f1SShivaprasad G Bhat return H_PARAMETER; 668*8601b4f1SShivaprasad G Bhat } 669b5513584SShivaprasad G Bhat if (continue_token == 0) { 670*8601b4f1SShivaprasad G Bhat bool is_pmem = false, pmem_override = false; 671b5513584SShivaprasad G Bhat backend = MEMORY_BACKEND(dimm->hostmem); 672b5513584SShivaprasad G Bhat fd = memory_region_get_fd(&backend->mr); 673b5513584SShivaprasad G Bhat 674b5513584SShivaprasad G Bhat if (fd < 0) { 675b5513584SShivaprasad G Bhat return H_UNSUPPORTED; 676b5513584SShivaprasad G Bhat } 677b5513584SShivaprasad G Bhat 678*8601b4f1SShivaprasad G Bhat is_pmem = object_property_get_bool(OBJECT(backend), "pmem", NULL); 679*8601b4f1SShivaprasad G Bhat pmem_override = object_property_get_bool(OBJECT(dimm), 680*8601b4f1SShivaprasad G Bhat "pmem-override", NULL); 681*8601b4f1SShivaprasad G Bhat if (is_pmem && !pmem_override) { 682*8601b4f1SShivaprasad G Bhat return H_UNSUPPORTED; 683*8601b4f1SShivaprasad G Bhat } 684*8601b4f1SShivaprasad G Bhat 685b5513584SShivaprasad G Bhat state = spapr_nvdimm_init_new_flush_state(SPAPR_NVDIMM(dimm)); 686b5513584SShivaprasad G Bhat if (!state) { 687b5513584SShivaprasad G Bhat return H_HARDWARE; 688b5513584SShivaprasad G Bhat } 689b5513584SShivaprasad G Bhat 690b5513584SShivaprasad G Bhat state->drcidx = drc_index; 691b5513584SShivaprasad G Bhat 692b5513584SShivaprasad G Bhat thread_pool_submit_aio(pool, flush_worker_cb, state, 693b5513584SShivaprasad G Bhat spapr_nvdimm_flush_completion_cb, state); 694b5513584SShivaprasad G Bhat 695b5513584SShivaprasad G Bhat continue_token = state->continue_token; 696b5513584SShivaprasad G Bhat } 697b5513584SShivaprasad G Bhat 698b5513584SShivaprasad G Bhat ret = spapr_nvdimm_get_flush_status(SPAPR_NVDIMM(dimm), continue_token); 699b5513584SShivaprasad G Bhat if (H_IS_LONG_BUSY(ret)) { 700b5513584SShivaprasad G Bhat args[0] = continue_token; 701b5513584SShivaprasad G Bhat } 702b5513584SShivaprasad G Bhat 703b5513584SShivaprasad G Bhat return ret; 704b5513584SShivaprasad G Bhat } 705b5513584SShivaprasad G Bhat 706b5fca656SShivaprasad G Bhat static target_ulong h_scm_unbind_mem(PowerPCCPU *cpu, SpaprMachineState *spapr, 707b5fca656SShivaprasad G Bhat target_ulong opcode, target_ulong *args) 708b5fca656SShivaprasad G Bhat { 709b5fca656SShivaprasad G Bhat uint32_t drc_index = args[0]; 710b5fca656SShivaprasad G Bhat uint64_t starting_scm_logical_addr = args[1]; 711b5fca656SShivaprasad G Bhat uint64_t no_of_scm_blocks_to_unbind = args[2]; 712b5fca656SShivaprasad G Bhat uint64_t continue_token = args[3]; 713b5fca656SShivaprasad G Bhat uint64_t size_to_unbind; 714b5fca656SShivaprasad G Bhat Range blockrange = range_empty; 715b5fca656SShivaprasad G Bhat Range nvdimmrange = range_empty; 716b5fca656SShivaprasad G Bhat SpaprDrc *drc = spapr_drc_by_index(drc_index); 717b5fca656SShivaprasad G Bhat NVDIMMDevice *nvdimm; 718b5fca656SShivaprasad G Bhat uint64_t size, addr; 719b5fca656SShivaprasad G Bhat 720b5fca656SShivaprasad G Bhat if (!drc || !drc->dev || 721b5fca656SShivaprasad G Bhat spapr_drc_type(drc) != SPAPR_DR_CONNECTOR_TYPE_PMEM) { 722b5fca656SShivaprasad G Bhat return H_PARAMETER; 723b5fca656SShivaprasad G Bhat } 724b5fca656SShivaprasad G Bhat 725b5fca656SShivaprasad G Bhat /* continue_token should be zero as this hcall doesn't return H_BUSY. */ 726b5fca656SShivaprasad G Bhat if (continue_token > 0) { 727b5fca656SShivaprasad G Bhat return H_P4; 728b5fca656SShivaprasad G Bhat } 729b5fca656SShivaprasad G Bhat 730b5fca656SShivaprasad G Bhat /* Check if starting_scm_logical_addr is block aligned */ 731b5fca656SShivaprasad G Bhat if (!QEMU_IS_ALIGNED(starting_scm_logical_addr, 732b5fca656SShivaprasad G Bhat SPAPR_MINIMUM_SCM_BLOCK_SIZE)) { 733b5fca656SShivaprasad G Bhat return H_P2; 734b5fca656SShivaprasad G Bhat } 735b5fca656SShivaprasad G Bhat 736b5fca656SShivaprasad G Bhat size_to_unbind = no_of_scm_blocks_to_unbind * SPAPR_MINIMUM_SCM_BLOCK_SIZE; 737b5fca656SShivaprasad G Bhat if (no_of_scm_blocks_to_unbind == 0 || no_of_scm_blocks_to_unbind != 738b5fca656SShivaprasad G Bhat size_to_unbind / SPAPR_MINIMUM_SCM_BLOCK_SIZE) { 739b5fca656SShivaprasad G Bhat return H_P3; 740b5fca656SShivaprasad G Bhat } 741b5fca656SShivaprasad G Bhat 742b5fca656SShivaprasad G Bhat nvdimm = NVDIMM(drc->dev); 743b5fca656SShivaprasad G Bhat size = object_property_get_int(OBJECT(nvdimm), PC_DIMM_SIZE_PROP, 744b5fca656SShivaprasad G Bhat &error_abort); 745b5fca656SShivaprasad G Bhat addr = object_property_get_int(OBJECT(nvdimm), PC_DIMM_ADDR_PROP, 746b5fca656SShivaprasad G Bhat &error_abort); 747b5fca656SShivaprasad G Bhat 748b5fca656SShivaprasad G Bhat range_init_nofail(&nvdimmrange, addr, size); 749b5fca656SShivaprasad G Bhat range_init_nofail(&blockrange, starting_scm_logical_addr, size_to_unbind); 750b5fca656SShivaprasad G Bhat 751b5fca656SShivaprasad G Bhat if (!range_contains_range(&nvdimmrange, &blockrange)) { 752b5fca656SShivaprasad G Bhat return H_P3; 753b5fca656SShivaprasad G Bhat } 754b5fca656SShivaprasad G Bhat 755b5fca656SShivaprasad G Bhat args[1] = no_of_scm_blocks_to_unbind; 756b5fca656SShivaprasad G Bhat 757b5fca656SShivaprasad G Bhat /* let unplug take care of actual unbind */ 758b5fca656SShivaprasad G Bhat return H_SUCCESS; 759b5fca656SShivaprasad G Bhat } 760b5fca656SShivaprasad G Bhat 761b5fca656SShivaprasad G Bhat #define H_UNBIND_SCOPE_ALL 0x1 762b5fca656SShivaprasad G Bhat #define H_UNBIND_SCOPE_DRC 0x2 763b5fca656SShivaprasad G Bhat 764b5fca656SShivaprasad G Bhat static target_ulong h_scm_unbind_all(PowerPCCPU *cpu, SpaprMachineState *spapr, 765b5fca656SShivaprasad G Bhat target_ulong opcode, target_ulong *args) 766b5fca656SShivaprasad G Bhat { 767b5fca656SShivaprasad G Bhat uint64_t target_scope = args[0]; 768b5fca656SShivaprasad G Bhat uint32_t drc_index = args[1]; 769b5fca656SShivaprasad G Bhat uint64_t continue_token = args[2]; 770b5fca656SShivaprasad G Bhat NVDIMMDevice *nvdimm; 771b5fca656SShivaprasad G Bhat uint64_t size; 772b5fca656SShivaprasad G Bhat uint64_t no_of_scm_blocks_unbound = 0; 773b5fca656SShivaprasad G Bhat 774b5fca656SShivaprasad G Bhat /* continue_token should be zero as this hcall doesn't return H_BUSY. */ 775b5fca656SShivaprasad G Bhat if (continue_token > 0) { 776b5fca656SShivaprasad G Bhat return H_P4; 777b5fca656SShivaprasad G Bhat } 778b5fca656SShivaprasad G Bhat 779b5fca656SShivaprasad G Bhat if (target_scope == H_UNBIND_SCOPE_DRC) { 780b5fca656SShivaprasad G Bhat SpaprDrc *drc = spapr_drc_by_index(drc_index); 781b5fca656SShivaprasad G Bhat 782b5fca656SShivaprasad G Bhat if (!drc || !drc->dev || 783b5fca656SShivaprasad G Bhat spapr_drc_type(drc) != SPAPR_DR_CONNECTOR_TYPE_PMEM) { 784b5fca656SShivaprasad G Bhat return H_P2; 785b5fca656SShivaprasad G Bhat } 786b5fca656SShivaprasad G Bhat 787b5fca656SShivaprasad G Bhat nvdimm = NVDIMM(drc->dev); 788b5fca656SShivaprasad G Bhat size = object_property_get_int(OBJECT(nvdimm), PC_DIMM_SIZE_PROP, 789b5fca656SShivaprasad G Bhat &error_abort); 790b5fca656SShivaprasad G Bhat 791b5fca656SShivaprasad G Bhat no_of_scm_blocks_unbound = size / SPAPR_MINIMUM_SCM_BLOCK_SIZE; 792b5fca656SShivaprasad G Bhat } else if (target_scope == H_UNBIND_SCOPE_ALL) { 793b5fca656SShivaprasad G Bhat GSList *list, *nvdimms; 794b5fca656SShivaprasad G Bhat 795b5fca656SShivaprasad G Bhat nvdimms = nvdimm_get_device_list(); 796b5fca656SShivaprasad G Bhat for (list = nvdimms; list; list = list->next) { 797b5fca656SShivaprasad G Bhat nvdimm = list->data; 798b5fca656SShivaprasad G Bhat size = object_property_get_int(OBJECT(nvdimm), PC_DIMM_SIZE_PROP, 799b5fca656SShivaprasad G Bhat &error_abort); 800b5fca656SShivaprasad G Bhat 801b5fca656SShivaprasad G Bhat no_of_scm_blocks_unbound += size / SPAPR_MINIMUM_SCM_BLOCK_SIZE; 802b5fca656SShivaprasad G Bhat } 803b5fca656SShivaprasad G Bhat g_slist_free(nvdimms); 804b5fca656SShivaprasad G Bhat } else { 805b5fca656SShivaprasad G Bhat return H_PARAMETER; 806b5fca656SShivaprasad G Bhat } 807b5fca656SShivaprasad G Bhat 808b5fca656SShivaprasad G Bhat args[1] = no_of_scm_blocks_unbound; 809b5fca656SShivaprasad G Bhat 810b5fca656SShivaprasad G Bhat /* let unplug take care of actual unbind */ 811b5fca656SShivaprasad G Bhat return H_SUCCESS; 812b5fca656SShivaprasad G Bhat } 813b5fca656SShivaprasad G Bhat 81453d7d7e2SVaibhav Jain static target_ulong h_scm_health(PowerPCCPU *cpu, SpaprMachineState *spapr, 81553d7d7e2SVaibhav Jain target_ulong opcode, target_ulong *args) 81653d7d7e2SVaibhav Jain { 81753d7d7e2SVaibhav Jain 81853d7d7e2SVaibhav Jain NVDIMMDevice *nvdimm; 81953d7d7e2SVaibhav Jain uint64_t hbitmap = 0; 82053d7d7e2SVaibhav Jain uint32_t drc_index = args[0]; 82153d7d7e2SVaibhav Jain SpaprDrc *drc = spapr_drc_by_index(drc_index); 82253d7d7e2SVaibhav Jain const uint64_t hbitmap_mask = PAPR_PMEM_UNARMED; 82353d7d7e2SVaibhav Jain 82453d7d7e2SVaibhav Jain 82553d7d7e2SVaibhav Jain /* Ensure that the drc is valid & is valid PMEM dimm and is plugged in */ 82653d7d7e2SVaibhav Jain if (!drc || !drc->dev || 82753d7d7e2SVaibhav Jain spapr_drc_type(drc) != SPAPR_DR_CONNECTOR_TYPE_PMEM) { 82853d7d7e2SVaibhav Jain return H_PARAMETER; 82953d7d7e2SVaibhav Jain } 83053d7d7e2SVaibhav Jain 83153d7d7e2SVaibhav Jain nvdimm = NVDIMM(drc->dev); 83253d7d7e2SVaibhav Jain 83353d7d7e2SVaibhav Jain /* Update if the nvdimm is unarmed and send its status via health bitmaps */ 83453d7d7e2SVaibhav Jain if (object_property_get_bool(OBJECT(nvdimm), NVDIMM_UNARMED_PROP, NULL)) { 83553d7d7e2SVaibhav Jain hbitmap |= PAPR_PMEM_UNARMED; 83653d7d7e2SVaibhav Jain } 83753d7d7e2SVaibhav Jain 83853d7d7e2SVaibhav Jain /* Update the out args with health bitmap/mask */ 83953d7d7e2SVaibhav Jain args[0] = hbitmap; 84053d7d7e2SVaibhav Jain args[1] = hbitmap_mask; 84153d7d7e2SVaibhav Jain 84253d7d7e2SVaibhav Jain return H_SUCCESS; 84353d7d7e2SVaibhav Jain } 84453d7d7e2SVaibhav Jain 845b5fca656SShivaprasad G Bhat static void spapr_scm_register_types(void) 846b5fca656SShivaprasad G Bhat { 847b5fca656SShivaprasad G Bhat /* qemu/scm specific hcalls */ 848b5fca656SShivaprasad G Bhat spapr_register_hypercall(H_SCM_READ_METADATA, h_scm_read_metadata); 849b5fca656SShivaprasad G Bhat spapr_register_hypercall(H_SCM_WRITE_METADATA, h_scm_write_metadata); 850b5fca656SShivaprasad G Bhat spapr_register_hypercall(H_SCM_BIND_MEM, h_scm_bind_mem); 851b5fca656SShivaprasad G Bhat spapr_register_hypercall(H_SCM_UNBIND_MEM, h_scm_unbind_mem); 852b5fca656SShivaprasad G Bhat spapr_register_hypercall(H_SCM_UNBIND_ALL, h_scm_unbind_all); 85353d7d7e2SVaibhav Jain spapr_register_hypercall(H_SCM_HEALTH, h_scm_health); 854b5513584SShivaprasad G Bhat spapr_register_hypercall(H_SCM_FLUSH, h_scm_flush); 855b5fca656SShivaprasad G Bhat } 856b5fca656SShivaprasad G Bhat 857b5fca656SShivaprasad G Bhat type_init(spapr_scm_register_types) 858*8601b4f1SShivaprasad G Bhat 859*8601b4f1SShivaprasad G Bhat static void spapr_nvdimm_realize(NVDIMMDevice *dimm, Error **errp) 860*8601b4f1SShivaprasad G Bhat { 861*8601b4f1SShivaprasad G Bhat SpaprNVDIMMDevice *s_nvdimm = SPAPR_NVDIMM(dimm); 862*8601b4f1SShivaprasad G Bhat HostMemoryBackend *backend = MEMORY_BACKEND(PC_DIMM(dimm)->hostmem); 863*8601b4f1SShivaprasad G Bhat bool is_pmem = object_property_get_bool(OBJECT(backend), "pmem", NULL); 864*8601b4f1SShivaprasad G Bhat bool pmem_override = object_property_get_bool(OBJECT(dimm), "pmem-override", 865*8601b4f1SShivaprasad G Bhat NULL); 866*8601b4f1SShivaprasad G Bhat if (!is_pmem || pmem_override) { 867*8601b4f1SShivaprasad G Bhat s_nvdimm->hcall_flush_required = true; 868*8601b4f1SShivaprasad G Bhat } 869*8601b4f1SShivaprasad G Bhat 870*8601b4f1SShivaprasad G Bhat vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, 871*8601b4f1SShivaprasad G Bhat &vmstate_spapr_nvdimm_states, dimm); 872*8601b4f1SShivaprasad G Bhat } 873*8601b4f1SShivaprasad G Bhat 874*8601b4f1SShivaprasad G Bhat static void spapr_nvdimm_unrealize(NVDIMMDevice *dimm) 875*8601b4f1SShivaprasad G Bhat { 876*8601b4f1SShivaprasad G Bhat vmstate_unregister(NULL, &vmstate_spapr_nvdimm_states, dimm); 877*8601b4f1SShivaprasad G Bhat } 878*8601b4f1SShivaprasad G Bhat 879*8601b4f1SShivaprasad G Bhat static Property spapr_nvdimm_properties[] = { 880*8601b4f1SShivaprasad G Bhat #ifdef CONFIG_LIBPMEM 881*8601b4f1SShivaprasad G Bhat DEFINE_PROP_BOOL("pmem-override", SpaprNVDIMMDevice, pmem_override, false), 882*8601b4f1SShivaprasad G Bhat #endif 883*8601b4f1SShivaprasad G Bhat DEFINE_PROP_END_OF_LIST(), 884*8601b4f1SShivaprasad G Bhat }; 885*8601b4f1SShivaprasad G Bhat 886*8601b4f1SShivaprasad G Bhat static void spapr_nvdimm_class_init(ObjectClass *oc, void *data) 887*8601b4f1SShivaprasad G Bhat { 888*8601b4f1SShivaprasad G Bhat DeviceClass *dc = DEVICE_CLASS(oc); 889*8601b4f1SShivaprasad G Bhat NVDIMMClass *nvc = NVDIMM_CLASS(oc); 890*8601b4f1SShivaprasad G Bhat 891*8601b4f1SShivaprasad G Bhat nvc->realize = spapr_nvdimm_realize; 892*8601b4f1SShivaprasad G Bhat nvc->unrealize = spapr_nvdimm_unrealize; 893*8601b4f1SShivaprasad G Bhat 894*8601b4f1SShivaprasad G Bhat device_class_set_props(dc, spapr_nvdimm_properties); 895*8601b4f1SShivaprasad G Bhat } 896*8601b4f1SShivaprasad G Bhat 897*8601b4f1SShivaprasad G Bhat static void spapr_nvdimm_init(Object *obj) 898*8601b4f1SShivaprasad G Bhat { 899*8601b4f1SShivaprasad G Bhat SpaprNVDIMMDevice *s_nvdimm = SPAPR_NVDIMM(obj); 900*8601b4f1SShivaprasad G Bhat 901*8601b4f1SShivaprasad G Bhat s_nvdimm->hcall_flush_required = false; 902*8601b4f1SShivaprasad G Bhat QLIST_INIT(&s_nvdimm->pending_nvdimm_flush_states); 903*8601b4f1SShivaprasad G Bhat QLIST_INIT(&s_nvdimm->completed_nvdimm_flush_states); 904*8601b4f1SShivaprasad G Bhat } 905*8601b4f1SShivaprasad G Bhat 906*8601b4f1SShivaprasad G Bhat static TypeInfo spapr_nvdimm_info = { 907*8601b4f1SShivaprasad G Bhat .name = TYPE_SPAPR_NVDIMM, 908*8601b4f1SShivaprasad G Bhat .parent = TYPE_NVDIMM, 909*8601b4f1SShivaprasad G Bhat .class_init = spapr_nvdimm_class_init, 910*8601b4f1SShivaprasad G Bhat .class_size = sizeof(SPAPRNVDIMMClass), 911*8601b4f1SShivaprasad G Bhat .instance_size = sizeof(SpaprNVDIMMDevice), 912*8601b4f1SShivaprasad G Bhat .instance_init = spapr_nvdimm_init, 913*8601b4f1SShivaprasad G Bhat }; 914*8601b4f1SShivaprasad G Bhat 915*8601b4f1SShivaprasad G Bhat static void spapr_nvdimm_register_types(void) 916*8601b4f1SShivaprasad G Bhat { 917*8601b4f1SShivaprasad G Bhat type_register_static(&spapr_nvdimm_info); 918*8601b4f1SShivaprasad G Bhat } 919*8601b4f1SShivaprasad G Bhat 920*8601b4f1SShivaprasad G Bhat type_init(spapr_nvdimm_register_types) 921