summaryrefslogtreecommitdiffstats
path: root/hw/ppc/spapr_nvdimm.c
diff options
context:
space:
mode:
authorShivaprasad G Bhat2020-02-10 05:56:31 +0100
committerDavid Gibson2020-02-20 23:15:04 +0100
commitee3a71e36654317b14ede0290e87628f8b79f850 (patch)
treec42f6057545d006d23cae4b25aa66c9f16eb6a4c /hw/ppc/spapr_nvdimm.c
parentnvdimm: add uuid property to nvdimm (diff)
downloadqemu-ee3a71e36654317b14ede0290e87628f8b79f850.tar.gz
qemu-ee3a71e36654317b14ede0290e87628f8b79f850.tar.xz
qemu-ee3a71e36654317b14ede0290e87628f8b79f850.zip
spapr: Add NVDIMM device support
Add support for NVDIMM devices for sPAPR. Piggyback on existing nvdimm device interface in QEMU to support virtual NVDIMM devices for Power. Create the required DT entries for the device (some entries have dummy values right now). The patch creates the required DT node and sends a hotplug interrupt to the guest. Guest is expected to undertake the normal DR resource add path in response and start issuing PAPR SCM hcalls. The device support is verified based on the machine version unlike x86. This is how it can be used .. Ex : For coldplug, the device to be added in qemu command line as shown below -object memory-backend-file,id=memnvdimm0,prealloc=yes,mem-path=/tmp/nvdimm0,share=yes,size=1073872896 -device nvdimm,label-size=128k,uuid=75a3cdd7-6a2f-4791-8d15-fe0a920e8e9e,memdev=memnvdimm0,id=nvdimm0,slot=0 For hotplug, the device to be added from monitor as below object_add memory-backend-file,id=memnvdimm0,prealloc=yes,mem-path=/tmp/nvdimm0,share=yes,size=1073872896 device_add nvdimm,label-size=128k,uuid=75a3cdd7-6a2f-4791-8d15-fe0a920e8e9e,memdev=memnvdimm0,id=nvdimm0,slot=0 Signed-off-by: Shivaprasad G Bhat <sbhat@linux.ibm.com> Signed-off-by: Bharata B Rao <bharata@linux.ibm.com> [Early implementation] Message-Id: <158131058078.2897.12767731856697459923.stgit@lep8c.aus.stglabs.ibm.com> Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Diffstat (limited to 'hw/ppc/spapr_nvdimm.c')
-rw-r--r--hw/ppc/spapr_nvdimm.c177
1 files changed, 177 insertions, 0 deletions
diff --git a/hw/ppc/spapr_nvdimm.c b/hw/ppc/spapr_nvdimm.c
new file mode 100644
index 0000000000..d03c8d3a5c
--- /dev/null
+++ b/hw/ppc/spapr_nvdimm.c
@@ -0,0 +1,177 @@
+/*
+ * QEMU PAPR Storage Class Memory Interfaces
+ *
+ * Copyright (c) 2019-2020, IBM Corporation.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/ppc/spapr_drc.h"
+#include "hw/ppc/spapr_nvdimm.h"
+#include "hw/mem/nvdimm.h"
+#include "qemu/nvdimm-utils.h"
+#include "hw/ppc/fdt.h"
+
+void spapr_nvdimm_validate_opts(NVDIMMDevice *nvdimm, uint64_t size,
+ Error **errp)
+{
+ char *uuidstr = NULL;
+ QemuUUID uuid;
+
+ if (size % SPAPR_MINIMUM_SCM_BLOCK_SIZE) {
+ error_setg(errp, "NVDIMM memory size excluding the label area"
+ " must be a multiple of %" PRIu64 "MB",
+ SPAPR_MINIMUM_SCM_BLOCK_SIZE / MiB);
+ return;
+ }
+
+ uuidstr = object_property_get_str(OBJECT(nvdimm), NVDIMM_UUID_PROP, NULL);
+ qemu_uuid_parse(uuidstr, &uuid);
+ g_free(uuidstr);
+
+ if (qemu_uuid_is_null(&uuid)) {
+ error_setg(errp, "NVDIMM device requires the uuid to be set");
+ return;
+ }
+}
+
+
+void spapr_add_nvdimm(DeviceState *dev, uint64_t slot, Error **errp)
+{
+ SpaprDrc *drc;
+ bool hotplugged = spapr_drc_hotplugged(dev);
+ Error *local_err = NULL;
+
+ drc = spapr_drc_by_id(TYPE_SPAPR_DRC_PMEM, slot);
+ g_assert(drc);
+
+ spapr_drc_attach(drc, dev, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ if (hotplugged) {
+ spapr_hotplug_req_add_by_index(drc);
+ }
+}
+
+int spapr_pmem_dt_populate(SpaprDrc *drc, SpaprMachineState *spapr,
+ void *fdt, int *fdt_start_offset, Error **errp)
+{
+ NVDIMMDevice *nvdimm = NVDIMM(drc->dev);
+
+ *fdt_start_offset = spapr_dt_nvdimm(fdt, 0, nvdimm);
+
+ return 0;
+}
+
+void spapr_create_nvdimm_dr_connectors(SpaprMachineState *spapr)
+{
+ MachineState *machine = MACHINE(spapr);
+ int i;
+
+ for (i = 0; i < machine->ram_slots; i++) {
+ spapr_dr_connector_new(OBJECT(spapr), TYPE_SPAPR_DRC_PMEM, i);
+ }
+}
+
+
+int spapr_dt_nvdimm(void *fdt, int parent_offset,
+ NVDIMMDevice *nvdimm)
+{
+ int child_offset;
+ char *buf;
+ SpaprDrc *drc;
+ uint32_t drc_idx;
+ uint32_t node = object_property_get_uint(OBJECT(nvdimm), PC_DIMM_NODE_PROP,
+ &error_abort);
+ uint64_t slot = object_property_get_uint(OBJECT(nvdimm), PC_DIMM_SLOT_PROP,
+ &error_abort);
+ uint32_t associativity[] = {
+ cpu_to_be32(0x4), /* length */
+ cpu_to_be32(0x0), cpu_to_be32(0x0),
+ cpu_to_be32(0x0), cpu_to_be32(node)
+ };
+ uint64_t lsize = nvdimm->label_size;
+ uint64_t size = object_property_get_int(OBJECT(nvdimm), PC_DIMM_SIZE_PROP,
+ NULL);
+
+ drc = spapr_drc_by_id(TYPE_SPAPR_DRC_PMEM, slot);
+ g_assert(drc);
+
+ drc_idx = spapr_drc_index(drc);
+
+ buf = g_strdup_printf("ibm,pmemory@%x", drc_idx);
+ child_offset = fdt_add_subnode(fdt, parent_offset, buf);
+ g_free(buf);
+
+ _FDT(child_offset);
+
+ _FDT((fdt_setprop_cell(fdt, child_offset, "reg", drc_idx)));
+ _FDT((fdt_setprop_string(fdt, child_offset, "compatible", "ibm,pmemory")));
+ _FDT((fdt_setprop_string(fdt, child_offset, "device_type", "ibm,pmemory")));
+
+ _FDT((fdt_setprop(fdt, child_offset, "ibm,associativity", associativity,
+ sizeof(associativity))));
+
+ buf = qemu_uuid_unparse_strdup(&nvdimm->uuid);
+ _FDT((fdt_setprop_string(fdt, child_offset, "ibm,unit-guid", buf)));
+ g_free(buf);
+
+ _FDT((fdt_setprop_cell(fdt, child_offset, "ibm,my-drc-index", drc_idx)));
+
+ _FDT((fdt_setprop_u64(fdt, child_offset, "ibm,block-size",
+ SPAPR_MINIMUM_SCM_BLOCK_SIZE)));
+ _FDT((fdt_setprop_u64(fdt, child_offset, "ibm,number-of-blocks",
+ size / SPAPR_MINIMUM_SCM_BLOCK_SIZE)));
+ _FDT((fdt_setprop_cell(fdt, child_offset, "ibm,metadata-size", lsize)));
+
+ _FDT((fdt_setprop_string(fdt, child_offset, "ibm,pmem-application",
+ "operating-system")));
+ _FDT(fdt_setprop(fdt, child_offset, "ibm,cache-flush-required", NULL, 0));
+
+ return child_offset;
+}
+
+void spapr_dt_persistent_memory(void *fdt)
+{
+ int offset = fdt_subnode_offset(fdt, 0, "persistent-memory");
+ GSList *iter, *nvdimms = nvdimm_get_device_list();
+
+ if (offset < 0) {
+ offset = fdt_add_subnode(fdt, 0, "persistent-memory");
+ _FDT(offset);
+ _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 0x1)));
+ _FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 0x0)));
+ _FDT((fdt_setprop_string(fdt, offset, "device_type",
+ "ibm,persistent-memory")));
+ }
+
+ /* Create DT entries for cold plugged NVDIMM devices */
+ for (iter = nvdimms; iter; iter = iter->next) {
+ NVDIMMDevice *nvdimm = iter->data;
+
+ spapr_dt_nvdimm(fdt, offset, nvdimm);
+ }
+ g_slist_free(nvdimms);
+
+ return;
+}