xref: /linux/tools/testing/cxl/test/mem.c (revision 24bce201d79807b668bf9d9e0aca801c5c0d5f78)
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 EFFECT(x) (1U << x)
14 
15 static struct cxl_cel_entry mock_cel[] = {
16 	{
17 		.opcode = cpu_to_le16(CXL_MBOX_OP_GET_SUPPORTED_LOGS),
18 		.effect = cpu_to_le16(0),
19 	},
20 	{
21 		.opcode = cpu_to_le16(CXL_MBOX_OP_IDENTIFY),
22 		.effect = cpu_to_le16(0),
23 	},
24 	{
25 		.opcode = cpu_to_le16(CXL_MBOX_OP_GET_LSA),
26 		.effect = cpu_to_le16(0),
27 	},
28 	{
29 		.opcode = cpu_to_le16(CXL_MBOX_OP_SET_LSA),
30 		.effect = cpu_to_le16(EFFECT(1) | EFFECT(2)),
31 	},
32 	{
33 		.opcode = cpu_to_le16(CXL_MBOX_OP_GET_HEALTH_INFO),
34 		.effect = cpu_to_le16(0),
35 	},
36 };
37 
38 /* See CXL 2.0 Table 181 Get Health Info Output Payload */
39 struct cxl_mbox_health_info {
40 	u8 health_status;
41 	u8 media_status;
42 	u8 ext_status;
43 	u8 life_used;
44 	__le16 temperature;
45 	__le32 dirty_shutdowns;
46 	__le32 volatile_errors;
47 	__le32 pmem_errors;
48 } __packed;
49 
50 static struct {
51 	struct cxl_mbox_get_supported_logs gsl;
52 	struct cxl_gsl_entry entry;
53 } mock_gsl_payload = {
54 	.gsl = {
55 		.entries = cpu_to_le16(1),
56 	},
57 	.entry = {
58 		.uuid = DEFINE_CXL_CEL_UUID,
59 		.size = cpu_to_le32(sizeof(mock_cel)),
60 	},
61 };
62 
63 static int mock_gsl(struct cxl_mbox_cmd *cmd)
64 {
65 	if (cmd->size_out < sizeof(mock_gsl_payload))
66 		return -EINVAL;
67 
68 	memcpy(cmd->payload_out, &mock_gsl_payload, sizeof(mock_gsl_payload));
69 	cmd->size_out = sizeof(mock_gsl_payload);
70 
71 	return 0;
72 }
73 
74 static int mock_get_log(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
75 {
76 	struct cxl_mbox_get_log *gl = cmd->payload_in;
77 	u32 offset = le32_to_cpu(gl->offset);
78 	u32 length = le32_to_cpu(gl->length);
79 	uuid_t uuid = DEFINE_CXL_CEL_UUID;
80 	void *data = &mock_cel;
81 
82 	if (cmd->size_in < sizeof(*gl))
83 		return -EINVAL;
84 	if (length > cxlds->payload_size)
85 		return -EINVAL;
86 	if (offset + length > sizeof(mock_cel))
87 		return -EINVAL;
88 	if (!uuid_equal(&gl->uuid, &uuid))
89 		return -EINVAL;
90 	if (length > cmd->size_out)
91 		return -EINVAL;
92 
93 	memcpy(cmd->payload_out, data + offset, length);
94 
95 	return 0;
96 }
97 
98 static int mock_id(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
99 {
100 	struct platform_device *pdev = to_platform_device(cxlds->dev);
101 	struct cxl_mbox_identify id = {
102 		.fw_revision = { "mock fw v1 " },
103 		.lsa_size = cpu_to_le32(LSA_SIZE),
104 		/* FIXME: Add partition support */
105 		.partition_align = cpu_to_le64(0),
106 	};
107 	u64 capacity = 0;
108 	int i;
109 
110 	if (cmd->size_out < sizeof(id))
111 		return -EINVAL;
112 
113 	for (i = 0; i < 2; i++) {
114 		struct resource *res;
115 
116 		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
117 		if (!res)
118 			break;
119 
120 		capacity += resource_size(res) / CXL_CAPACITY_MULTIPLIER;
121 
122 		if (le64_to_cpu(id.partition_align))
123 			continue;
124 
125 		if (res->desc == IORES_DESC_PERSISTENT_MEMORY)
126 			id.persistent_capacity = cpu_to_le64(
127 				resource_size(res) / CXL_CAPACITY_MULTIPLIER);
128 		else
129 			id.volatile_capacity = cpu_to_le64(
130 				resource_size(res) / CXL_CAPACITY_MULTIPLIER);
131 	}
132 
133 	id.total_capacity = cpu_to_le64(capacity);
134 
135 	memcpy(cmd->payload_out, &id, sizeof(id));
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_SET_LSA:
225 		rc = mock_set_lsa(cxlds, cmd);
226 		break;
227 	case CXL_MBOX_OP_GET_HEALTH_INFO:
228 		rc = mock_health_info(cxlds, cmd);
229 		break;
230 	default:
231 		break;
232 	}
233 
234 	dev_dbg(dev, "opcode: %#x sz_in: %zd sz_out: %zd rc: %d\n", cmd->opcode,
235 		cmd->size_in, cmd->size_out, rc);
236 
237 	return rc;
238 }
239 
240 static void label_area_release(void *lsa)
241 {
242 	vfree(lsa);
243 }
244 
245 static int cxl_mock_mem_probe(struct platform_device *pdev)
246 {
247 	struct device *dev = &pdev->dev;
248 	struct cxl_memdev *cxlmd;
249 	struct cxl_dev_state *cxlds;
250 	void *lsa;
251 	int rc;
252 
253 	lsa = vmalloc(LSA_SIZE);
254 	if (!lsa)
255 		return -ENOMEM;
256 	rc = devm_add_action_or_reset(dev, label_area_release, lsa);
257 	if (rc)
258 		return rc;
259 	dev_set_drvdata(dev, lsa);
260 
261 	cxlds = cxl_dev_state_create(dev);
262 	if (IS_ERR(cxlds))
263 		return PTR_ERR(cxlds);
264 
265 	cxlds->serial = pdev->id;
266 	cxlds->mbox_send = cxl_mock_mbox_send;
267 	cxlds->payload_size = SZ_4K;
268 
269 	rc = cxl_enumerate_cmds(cxlds);
270 	if (rc)
271 		return rc;
272 
273 	rc = cxl_dev_state_identify(cxlds);
274 	if (rc)
275 		return rc;
276 
277 	rc = cxl_mem_create_range_info(cxlds);
278 	if (rc)
279 		return rc;
280 
281 	cxlmd = devm_cxl_add_memdev(cxlds);
282 	if (IS_ERR(cxlmd))
283 		return PTR_ERR(cxlmd);
284 
285 	if (range_len(&cxlds->pmem_range) && IS_ENABLED(CONFIG_CXL_PMEM))
286 		rc = devm_cxl_add_nvdimm(dev, cxlmd);
287 
288 	return 0;
289 }
290 
291 static const struct platform_device_id cxl_mock_mem_ids[] = {
292 	{ .name = "cxl_mem", },
293 	{ },
294 };
295 MODULE_DEVICE_TABLE(platform, cxl_mock_mem_ids);
296 
297 static struct platform_driver cxl_mock_mem_driver = {
298 	.probe = cxl_mock_mem_probe,
299 	.id_table = cxl_mock_mem_ids,
300 	.driver = {
301 		.name = KBUILD_MODNAME,
302 	},
303 };
304 
305 module_platform_driver(cxl_mock_mem_driver);
306 MODULE_LICENSE("GPL v2");
307 MODULE_IMPORT_NS(CXL);
308