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 33 static struct { 34 struct cxl_mbox_get_supported_logs gsl; 35 struct cxl_gsl_entry entry; 36 } mock_gsl_payload = { 37 .gsl = { 38 .entries = cpu_to_le16(1), 39 }, 40 .entry = { 41 .uuid = DEFINE_CXL_CEL_UUID, 42 .size = cpu_to_le32(sizeof(mock_cel)), 43 }, 44 }; 45 46 static int mock_gsl(struct cxl_mbox_cmd *cmd) 47 { 48 if (cmd->size_out < sizeof(mock_gsl_payload)) 49 return -EINVAL; 50 51 memcpy(cmd->payload_out, &mock_gsl_payload, sizeof(mock_gsl_payload)); 52 cmd->size_out = sizeof(mock_gsl_payload); 53 54 return 0; 55 } 56 57 static int mock_get_log(struct cxl_mem *cxlm, struct cxl_mbox_cmd *cmd) 58 { 59 struct cxl_mbox_get_log *gl = cmd->payload_in; 60 u32 offset = le32_to_cpu(gl->offset); 61 u32 length = le32_to_cpu(gl->length); 62 uuid_t uuid = DEFINE_CXL_CEL_UUID; 63 void *data = &mock_cel; 64 65 if (cmd->size_in < sizeof(*gl)) 66 return -EINVAL; 67 if (length > cxlm->payload_size) 68 return -EINVAL; 69 if (offset + length > sizeof(mock_cel)) 70 return -EINVAL; 71 if (!uuid_equal(&gl->uuid, &uuid)) 72 return -EINVAL; 73 if (length > cmd->size_out) 74 return -EINVAL; 75 76 memcpy(cmd->payload_out, data + offset, length); 77 78 return 0; 79 } 80 81 static int mock_id(struct cxl_mem *cxlm, struct cxl_mbox_cmd *cmd) 82 { 83 struct platform_device *pdev = to_platform_device(cxlm->dev); 84 struct cxl_mbox_identify id = { 85 .fw_revision = { "mock fw v1 " }, 86 .lsa_size = cpu_to_le32(LSA_SIZE), 87 /* FIXME: Add partition support */ 88 .partition_align = cpu_to_le64(0), 89 }; 90 u64 capacity = 0; 91 int i; 92 93 if (cmd->size_out < sizeof(id)) 94 return -EINVAL; 95 96 for (i = 0; i < 2; i++) { 97 struct resource *res; 98 99 res = platform_get_resource(pdev, IORESOURCE_MEM, i); 100 if (!res) 101 break; 102 103 capacity += resource_size(res) / CXL_CAPACITY_MULTIPLIER; 104 105 if (le64_to_cpu(id.partition_align)) 106 continue; 107 108 if (res->desc == IORES_DESC_PERSISTENT_MEMORY) 109 id.persistent_capacity = cpu_to_le64( 110 resource_size(res) / CXL_CAPACITY_MULTIPLIER); 111 else 112 id.volatile_capacity = cpu_to_le64( 113 resource_size(res) / CXL_CAPACITY_MULTIPLIER); 114 } 115 116 id.total_capacity = cpu_to_le64(capacity); 117 118 memcpy(cmd->payload_out, &id, sizeof(id)); 119 120 return 0; 121 } 122 123 static int mock_get_lsa(struct cxl_mem *cxlm, struct cxl_mbox_cmd *cmd) 124 { 125 struct cxl_mbox_get_lsa *get_lsa = cmd->payload_in; 126 void *lsa = dev_get_drvdata(cxlm->dev); 127 u32 offset, length; 128 129 if (sizeof(*get_lsa) > cmd->size_in) 130 return -EINVAL; 131 offset = le32_to_cpu(get_lsa->offset); 132 length = le32_to_cpu(get_lsa->length); 133 if (offset + length > LSA_SIZE) 134 return -EINVAL; 135 if (length > cmd->size_out) 136 return -EINVAL; 137 138 memcpy(cmd->payload_out, lsa + offset, length); 139 return 0; 140 } 141 142 static int mock_set_lsa(struct cxl_mem *cxlm, struct cxl_mbox_cmd *cmd) 143 { 144 struct cxl_mbox_set_lsa *set_lsa = cmd->payload_in; 145 void *lsa = dev_get_drvdata(cxlm->dev); 146 u32 offset, length; 147 148 if (sizeof(*set_lsa) > cmd->size_in) 149 return -EINVAL; 150 offset = le32_to_cpu(set_lsa->offset); 151 length = cmd->size_in - sizeof(*set_lsa); 152 if (offset + length > LSA_SIZE) 153 return -EINVAL; 154 155 memcpy(lsa + offset, &set_lsa->data[0], length); 156 return 0; 157 } 158 159 static int cxl_mock_mbox_send(struct cxl_mem *cxlm, struct cxl_mbox_cmd *cmd) 160 { 161 struct device *dev = cxlm->dev; 162 int rc = -EIO; 163 164 switch (cmd->opcode) { 165 case CXL_MBOX_OP_GET_SUPPORTED_LOGS: 166 rc = mock_gsl(cmd); 167 break; 168 case CXL_MBOX_OP_GET_LOG: 169 rc = mock_get_log(cxlm, cmd); 170 break; 171 case CXL_MBOX_OP_IDENTIFY: 172 rc = mock_id(cxlm, cmd); 173 break; 174 case CXL_MBOX_OP_GET_LSA: 175 rc = mock_get_lsa(cxlm, cmd); 176 break; 177 case CXL_MBOX_OP_SET_LSA: 178 rc = mock_set_lsa(cxlm, cmd); 179 break; 180 default: 181 break; 182 } 183 184 dev_dbg(dev, "opcode: %#x sz_in: %zd sz_out: %zd rc: %d\n", cmd->opcode, 185 cmd->size_in, cmd->size_out, rc); 186 187 return rc; 188 } 189 190 static void label_area_release(void *lsa) 191 { 192 vfree(lsa); 193 } 194 195 static int cxl_mock_mem_probe(struct platform_device *pdev) 196 { 197 struct device *dev = &pdev->dev; 198 struct cxl_memdev *cxlmd; 199 struct cxl_mem *cxlm; 200 void *lsa; 201 int rc; 202 203 lsa = vmalloc(LSA_SIZE); 204 if (!lsa) 205 return -ENOMEM; 206 rc = devm_add_action_or_reset(dev, label_area_release, lsa); 207 if (rc) 208 return rc; 209 dev_set_drvdata(dev, lsa); 210 211 cxlm = cxl_mem_create(dev); 212 if (IS_ERR(cxlm)) 213 return PTR_ERR(cxlm); 214 215 cxlm->mbox_send = cxl_mock_mbox_send; 216 cxlm->payload_size = SZ_4K; 217 218 rc = cxl_mem_enumerate_cmds(cxlm); 219 if (rc) 220 return rc; 221 222 rc = cxl_mem_identify(cxlm); 223 if (rc) 224 return rc; 225 226 rc = cxl_mem_create_range_info(cxlm); 227 if (rc) 228 return rc; 229 230 cxlmd = devm_cxl_add_memdev(cxlm); 231 if (IS_ERR(cxlmd)) 232 return PTR_ERR(cxlmd); 233 234 if (range_len(&cxlm->pmem_range) && IS_ENABLED(CONFIG_CXL_PMEM)) 235 rc = devm_cxl_add_nvdimm(dev, cxlmd); 236 237 return 0; 238 } 239 240 static const struct platform_device_id cxl_mock_mem_ids[] = { 241 { .name = "cxl_mem", }, 242 { }, 243 }; 244 MODULE_DEVICE_TABLE(platform, cxl_mock_mem_ids); 245 246 static struct platform_driver cxl_mock_mem_driver = { 247 .probe = cxl_mock_mem_probe, 248 .id_table = cxl_mock_mem_ids, 249 .driver = { 250 .name = KBUILD_MODNAME, 251 }, 252 }; 253 254 module_platform_driver(cxl_mock_mem_driver); 255 MODULE_LICENSE("GPL v2"); 256 MODULE_IMPORT_NS(CXL); 257