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