1 // SPDX-License-Identifier: GPL-2.0-only 2 // Copyright(c) 2021 Intel Corporation. All rights reserved. 3 4 #include <linux/platform_device.h> 5 #include <linux/mod_devicetable.h> 6 #include <linux/module.h> 7 #include <linux/sizes.h> 8 #include <linux/bits.h> 9 #include <cxlmem.h> 10 11 #define LSA_SIZE SZ_128K 12 #define EFFECT(x) (1U << x) 13 14 static struct cxl_cel_entry mock_cel[] = { 15 { 16 .opcode = cpu_to_le16(CXL_MBOX_OP_GET_SUPPORTED_LOGS), 17 .effect = cpu_to_le16(0), 18 }, 19 { 20 .opcode = cpu_to_le16(CXL_MBOX_OP_IDENTIFY), 21 .effect = cpu_to_le16(0), 22 }, 23 { 24 .opcode = cpu_to_le16(CXL_MBOX_OP_GET_LSA), 25 .effect = cpu_to_le16(0), 26 }, 27 { 28 .opcode = cpu_to_le16(CXL_MBOX_OP_SET_LSA), 29 .effect = cpu_to_le16(EFFECT(1) | EFFECT(2)), 30 }, 31 { 32 .opcode = cpu_to_le16(CXL_MBOX_OP_GET_HEALTH_INFO), 33 .effect = cpu_to_le16(0), 34 }, 35 }; 36 37 /* See CXL 2.0 Table 181 Get Health Info Output Payload */ 38 struct cxl_mbox_health_info { 39 u8 health_status; 40 u8 media_status; 41 u8 ext_status; 42 u8 life_used; 43 __le16 temperature; 44 __le32 dirty_shutdowns; 45 __le32 volatile_errors; 46 __le32 pmem_errors; 47 } __packed; 48 49 static struct { 50 struct cxl_mbox_get_supported_logs gsl; 51 struct cxl_gsl_entry entry; 52 } mock_gsl_payload = { 53 .gsl = { 54 .entries = cpu_to_le16(1), 55 }, 56 .entry = { 57 .uuid = DEFINE_CXL_CEL_UUID, 58 .size = cpu_to_le32(sizeof(mock_cel)), 59 }, 60 }; 61 62 static int mock_gsl(struct cxl_mbox_cmd *cmd) 63 { 64 if (cmd->size_out < sizeof(mock_gsl_payload)) 65 return -EINVAL; 66 67 memcpy(cmd->payload_out, &mock_gsl_payload, sizeof(mock_gsl_payload)); 68 cmd->size_out = sizeof(mock_gsl_payload); 69 70 return 0; 71 } 72 73 static int mock_get_log(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd) 74 { 75 struct cxl_mbox_get_log *gl = cmd->payload_in; 76 u32 offset = le32_to_cpu(gl->offset); 77 u32 length = le32_to_cpu(gl->length); 78 uuid_t uuid = DEFINE_CXL_CEL_UUID; 79 void *data = &mock_cel; 80 81 if (cmd->size_in < sizeof(*gl)) 82 return -EINVAL; 83 if (length > cxlds->payload_size) 84 return -EINVAL; 85 if (offset + length > sizeof(mock_cel)) 86 return -EINVAL; 87 if (!uuid_equal(&gl->uuid, &uuid)) 88 return -EINVAL; 89 if (length > cmd->size_out) 90 return -EINVAL; 91 92 memcpy(cmd->payload_out, data + offset, length); 93 94 return 0; 95 } 96 97 static int mock_id(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd) 98 { 99 struct platform_device *pdev = to_platform_device(cxlds->dev); 100 struct cxl_mbox_identify id = { 101 .fw_revision = { "mock fw v1 " }, 102 .lsa_size = cpu_to_le32(LSA_SIZE), 103 /* FIXME: Add partition support */ 104 .partition_align = cpu_to_le64(0), 105 }; 106 u64 capacity = 0; 107 int i; 108 109 if (cmd->size_out < sizeof(id)) 110 return -EINVAL; 111 112 for (i = 0; i < 2; i++) { 113 struct resource *res; 114 115 res = platform_get_resource(pdev, IORESOURCE_MEM, i); 116 if (!res) 117 break; 118 119 capacity += resource_size(res) / CXL_CAPACITY_MULTIPLIER; 120 121 if (le64_to_cpu(id.partition_align)) 122 continue; 123 124 if (res->desc == IORES_DESC_PERSISTENT_MEMORY) 125 id.persistent_capacity = cpu_to_le64( 126 resource_size(res) / CXL_CAPACITY_MULTIPLIER); 127 else 128 id.volatile_capacity = cpu_to_le64( 129 resource_size(res) / CXL_CAPACITY_MULTIPLIER); 130 } 131 132 id.total_capacity = cpu_to_le64(capacity); 133 134 memcpy(cmd->payload_out, &id, sizeof(id)); 135 136 return 0; 137 } 138 139 static int mock_get_lsa(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd) 140 { 141 struct cxl_mbox_get_lsa *get_lsa = cmd->payload_in; 142 void *lsa = dev_get_drvdata(cxlds->dev); 143 u32 offset, length; 144 145 if (sizeof(*get_lsa) > cmd->size_in) 146 return -EINVAL; 147 offset = le32_to_cpu(get_lsa->offset); 148 length = le32_to_cpu(get_lsa->length); 149 if (offset + length > LSA_SIZE) 150 return -EINVAL; 151 if (length > cmd->size_out) 152 return -EINVAL; 153 154 memcpy(cmd->payload_out, lsa + offset, length); 155 return 0; 156 } 157 158 static int mock_set_lsa(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd) 159 { 160 struct cxl_mbox_set_lsa *set_lsa = cmd->payload_in; 161 void *lsa = dev_get_drvdata(cxlds->dev); 162 u32 offset, length; 163 164 if (sizeof(*set_lsa) > cmd->size_in) 165 return -EINVAL; 166 offset = le32_to_cpu(set_lsa->offset); 167 length = cmd->size_in - sizeof(*set_lsa); 168 if (offset + length > LSA_SIZE) 169 return -EINVAL; 170 171 memcpy(lsa + offset, &set_lsa->data[0], length); 172 return 0; 173 } 174 175 static int mock_health_info(struct cxl_dev_state *cxlds, 176 struct cxl_mbox_cmd *cmd) 177 { 178 struct cxl_mbox_health_info health_info = { 179 /* set flags for maint needed, perf degraded, hw replacement */ 180 .health_status = 0x7, 181 /* set media status to "All Data Lost" */ 182 .media_status = 0x3, 183 /* 184 * set ext_status flags for: 185 * ext_life_used: normal, 186 * ext_temperature: critical, 187 * ext_corrected_volatile: warning, 188 * ext_corrected_persistent: normal, 189 */ 190 .ext_status = 0x18, 191 .life_used = 15, 192 .temperature = cpu_to_le16(25), 193 .dirty_shutdowns = cpu_to_le32(10), 194 .volatile_errors = cpu_to_le32(20), 195 .pmem_errors = cpu_to_le32(30), 196 }; 197 198 if (cmd->size_out < sizeof(health_info)) 199 return -EINVAL; 200 201 memcpy(cmd->payload_out, &health_info, sizeof(health_info)); 202 return 0; 203 } 204 205 static int cxl_mock_mbox_send(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd) 206 { 207 struct device *dev = cxlds->dev; 208 int rc = -EIO; 209 210 switch (cmd->opcode) { 211 case CXL_MBOX_OP_GET_SUPPORTED_LOGS: 212 rc = mock_gsl(cmd); 213 break; 214 case CXL_MBOX_OP_GET_LOG: 215 rc = mock_get_log(cxlds, cmd); 216 break; 217 case CXL_MBOX_OP_IDENTIFY: 218 rc = mock_id(cxlds, cmd); 219 break; 220 case CXL_MBOX_OP_GET_LSA: 221 rc = mock_get_lsa(cxlds, cmd); 222 break; 223 case CXL_MBOX_OP_SET_LSA: 224 rc = mock_set_lsa(cxlds, cmd); 225 break; 226 case CXL_MBOX_OP_GET_HEALTH_INFO: 227 rc = mock_health_info(cxlds, cmd); 228 break; 229 default: 230 break; 231 } 232 233 dev_dbg(dev, "opcode: %#x sz_in: %zd sz_out: %zd rc: %d\n", cmd->opcode, 234 cmd->size_in, cmd->size_out, rc); 235 236 return rc; 237 } 238 239 static void label_area_release(void *lsa) 240 { 241 vfree(lsa); 242 } 243 244 static int cxl_mock_mem_probe(struct platform_device *pdev) 245 { 246 struct device *dev = &pdev->dev; 247 struct cxl_memdev *cxlmd; 248 struct cxl_dev_state *cxlds; 249 void *lsa; 250 int rc; 251 252 lsa = vmalloc(LSA_SIZE); 253 if (!lsa) 254 return -ENOMEM; 255 rc = devm_add_action_or_reset(dev, label_area_release, lsa); 256 if (rc) 257 return rc; 258 dev_set_drvdata(dev, lsa); 259 260 cxlds = cxl_dev_state_create(dev); 261 if (IS_ERR(cxlds)) 262 return PTR_ERR(cxlds); 263 264 cxlds->mbox_send = cxl_mock_mbox_send; 265 cxlds->payload_size = SZ_4K; 266 267 rc = cxl_enumerate_cmds(cxlds); 268 if (rc) 269 return rc; 270 271 rc = cxl_dev_state_identify(cxlds); 272 if (rc) 273 return rc; 274 275 rc = cxl_mem_create_range_info(cxlds); 276 if (rc) 277 return rc; 278 279 cxlmd = devm_cxl_add_memdev(cxlds); 280 if (IS_ERR(cxlmd)) 281 return PTR_ERR(cxlmd); 282 283 if (range_len(&cxlds->pmem_range) && IS_ENABLED(CONFIG_CXL_PMEM)) 284 rc = devm_cxl_add_nvdimm(dev, cxlmd); 285 286 return 0; 287 } 288 289 static const struct platform_device_id cxl_mock_mem_ids[] = { 290 { .name = "cxl_mem", }, 291 { }, 292 }; 293 MODULE_DEVICE_TABLE(platform, cxl_mock_mem_ids); 294 295 static struct platform_driver cxl_mock_mem_driver = { 296 .probe = cxl_mock_mem_probe, 297 .id_table = cxl_mock_mem_ids, 298 .driver = { 299 .name = KBUILD_MODNAME, 300 }, 301 }; 302 303 module_platform_driver(cxl_mock_mem_driver); 304 MODULE_LICENSE("GPL v2"); 305 MODULE_IMPORT_NS(CXL); 306