1 /* 2 * QEMU PAPR Storage Class Memory Interfaces 3 * 4 * Copyright (c) 2019-2020, IBM Corporation. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to deal 8 * in the Software without restriction, including without limitation the rights 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 * copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 * THE SOFTWARE. 23 */ 24 #include "qemu/osdep.h" 25 #include "qapi/error.h" 26 #include "hw/ppc/spapr_drc.h" 27 #include "hw/ppc/spapr_nvdimm.h" 28 #include "hw/mem/nvdimm.h" 29 #include "qemu/nvdimm-utils.h" 30 #include "hw/ppc/fdt.h" 31 32 void spapr_nvdimm_validate_opts(NVDIMMDevice *nvdimm, uint64_t size, 33 Error **errp) 34 { 35 char *uuidstr = NULL; 36 QemuUUID uuid; 37 38 if (size % SPAPR_MINIMUM_SCM_BLOCK_SIZE) { 39 error_setg(errp, "NVDIMM memory size excluding the label area" 40 " must be a multiple of %" PRIu64 "MB", 41 SPAPR_MINIMUM_SCM_BLOCK_SIZE / MiB); 42 return; 43 } 44 45 uuidstr = object_property_get_str(OBJECT(nvdimm), NVDIMM_UUID_PROP, NULL); 46 qemu_uuid_parse(uuidstr, &uuid); 47 g_free(uuidstr); 48 49 if (qemu_uuid_is_null(&uuid)) { 50 error_setg(errp, "NVDIMM device requires the uuid to be set"); 51 return; 52 } 53 } 54 55 56 void spapr_add_nvdimm(DeviceState *dev, uint64_t slot, Error **errp) 57 { 58 SpaprDrc *drc; 59 bool hotplugged = spapr_drc_hotplugged(dev); 60 Error *local_err = NULL; 61 62 drc = spapr_drc_by_id(TYPE_SPAPR_DRC_PMEM, slot); 63 g_assert(drc); 64 65 spapr_drc_attach(drc, dev, &local_err); 66 if (local_err) { 67 error_propagate(errp, local_err); 68 return; 69 } 70 71 if (hotplugged) { 72 spapr_hotplug_req_add_by_index(drc); 73 } 74 } 75 76 int spapr_pmem_dt_populate(SpaprDrc *drc, SpaprMachineState *spapr, 77 void *fdt, int *fdt_start_offset, Error **errp) 78 { 79 NVDIMMDevice *nvdimm = NVDIMM(drc->dev); 80 81 *fdt_start_offset = spapr_dt_nvdimm(fdt, 0, nvdimm); 82 83 return 0; 84 } 85 86 void spapr_create_nvdimm_dr_connectors(SpaprMachineState *spapr) 87 { 88 MachineState *machine = MACHINE(spapr); 89 int i; 90 91 for (i = 0; i < machine->ram_slots; i++) { 92 spapr_dr_connector_new(OBJECT(spapr), TYPE_SPAPR_DRC_PMEM, i); 93 } 94 } 95 96 97 int spapr_dt_nvdimm(void *fdt, int parent_offset, 98 NVDIMMDevice *nvdimm) 99 { 100 int child_offset; 101 char *buf; 102 SpaprDrc *drc; 103 uint32_t drc_idx; 104 uint32_t node = object_property_get_uint(OBJECT(nvdimm), PC_DIMM_NODE_PROP, 105 &error_abort); 106 uint64_t slot = object_property_get_uint(OBJECT(nvdimm), PC_DIMM_SLOT_PROP, 107 &error_abort); 108 uint32_t associativity[] = { 109 cpu_to_be32(0x4), /* length */ 110 cpu_to_be32(0x0), cpu_to_be32(0x0), 111 cpu_to_be32(0x0), cpu_to_be32(node) 112 }; 113 uint64_t lsize = nvdimm->label_size; 114 uint64_t size = object_property_get_int(OBJECT(nvdimm), PC_DIMM_SIZE_PROP, 115 NULL); 116 117 drc = spapr_drc_by_id(TYPE_SPAPR_DRC_PMEM, slot); 118 g_assert(drc); 119 120 drc_idx = spapr_drc_index(drc); 121 122 buf = g_strdup_printf("ibm,pmemory@%x", drc_idx); 123 child_offset = fdt_add_subnode(fdt, parent_offset, buf); 124 g_free(buf); 125 126 _FDT(child_offset); 127 128 _FDT((fdt_setprop_cell(fdt, child_offset, "reg", drc_idx))); 129 _FDT((fdt_setprop_string(fdt, child_offset, "compatible", "ibm,pmemory"))); 130 _FDT((fdt_setprop_string(fdt, child_offset, "device_type", "ibm,pmemory"))); 131 132 _FDT((fdt_setprop(fdt, child_offset, "ibm,associativity", associativity, 133 sizeof(associativity)))); 134 135 buf = qemu_uuid_unparse_strdup(&nvdimm->uuid); 136 _FDT((fdt_setprop_string(fdt, child_offset, "ibm,unit-guid", buf))); 137 g_free(buf); 138 139 _FDT((fdt_setprop_cell(fdt, child_offset, "ibm,my-drc-index", drc_idx))); 140 141 _FDT((fdt_setprop_u64(fdt, child_offset, "ibm,block-size", 142 SPAPR_MINIMUM_SCM_BLOCK_SIZE))); 143 _FDT((fdt_setprop_u64(fdt, child_offset, "ibm,number-of-blocks", 144 size / SPAPR_MINIMUM_SCM_BLOCK_SIZE))); 145 _FDT((fdt_setprop_cell(fdt, child_offset, "ibm,metadata-size", lsize))); 146 147 _FDT((fdt_setprop_string(fdt, child_offset, "ibm,pmem-application", 148 "operating-system"))); 149 _FDT(fdt_setprop(fdt, child_offset, "ibm,cache-flush-required", NULL, 0)); 150 151 return child_offset; 152 } 153 154 void spapr_dt_persistent_memory(void *fdt) 155 { 156 int offset = fdt_subnode_offset(fdt, 0, "persistent-memory"); 157 GSList *iter, *nvdimms = nvdimm_get_device_list(); 158 159 if (offset < 0) { 160 offset = fdt_add_subnode(fdt, 0, "persistent-memory"); 161 _FDT(offset); 162 _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 0x1))); 163 _FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 0x0))); 164 _FDT((fdt_setprop_string(fdt, offset, "device_type", 165 "ibm,persistent-memory"))); 166 } 167 168 /* Create DT entries for cold plugged NVDIMM devices */ 169 for (iter = nvdimms; iter; iter = iter->next) { 170 NVDIMMDevice *nvdimm = iter->data; 171 172 spapr_dt_nvdimm(fdt, offset, nvdimm); 173 } 174 g_slist_free(nvdimms); 175 176 return; 177 } 178