xref: /linux/tools/testing/cxl/test/mem.c (revision c6fbb759)
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 
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 
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 
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 
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 
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 
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 
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 
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 
243 static void label_area_release(void *lsa)
244 {
245 	vfree(lsa);
246 }
247 
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