xref: /openbmc/qemu/hw/ppc/spapr_nvdimm.c (revision 8601b4f1)
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