xref: /linux/tools/testing/cxl/test/mem.c (revision 2455f0e124d317dd08d337a7550a78a224d4ba41)
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 #define PASS_TRY_LIMIT 3
69 
70 struct cxl_mockmem_data {
71 	void *lsa;
72 	u32 security_state;
73 	u8 user_pass[NVDIMM_PASSPHRASE_LEN];
74 	u8 master_pass[NVDIMM_PASSPHRASE_LEN];
75 	int user_limit;
76 	int master_limit;
77 
78 };
79 
80 static int mock_gsl(struct cxl_mbox_cmd *cmd)
81 {
82 	if (cmd->size_out < sizeof(mock_gsl_payload))
83 		return -EINVAL;
84 
85 	memcpy(cmd->payload_out, &mock_gsl_payload, sizeof(mock_gsl_payload));
86 	cmd->size_out = sizeof(mock_gsl_payload);
87 
88 	return 0;
89 }
90 
91 static int mock_get_log(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
92 {
93 	struct cxl_mbox_get_log *gl = cmd->payload_in;
94 	u32 offset = le32_to_cpu(gl->offset);
95 	u32 length = le32_to_cpu(gl->length);
96 	uuid_t uuid = DEFINE_CXL_CEL_UUID;
97 	void *data = &mock_cel;
98 
99 	if (cmd->size_in < sizeof(*gl))
100 		return -EINVAL;
101 	if (length > cxlds->payload_size)
102 		return -EINVAL;
103 	if (offset + length > sizeof(mock_cel))
104 		return -EINVAL;
105 	if (!uuid_equal(&gl->uuid, &uuid))
106 		return -EINVAL;
107 	if (length > cmd->size_out)
108 		return -EINVAL;
109 
110 	memcpy(cmd->payload_out, data + offset, length);
111 
112 	return 0;
113 }
114 
115 static int mock_rcd_id(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
116 {
117 	struct cxl_mbox_identify id = {
118 		.fw_revision = { "mock fw v1 " },
119 		.total_capacity =
120 			cpu_to_le64(DEV_SIZE / CXL_CAPACITY_MULTIPLIER),
121 		.volatile_capacity =
122 			cpu_to_le64(DEV_SIZE / CXL_CAPACITY_MULTIPLIER),
123 	};
124 
125 	if (cmd->size_out < sizeof(id))
126 		return -EINVAL;
127 
128 	memcpy(cmd->payload_out, &id, sizeof(id));
129 
130 	return 0;
131 }
132 
133 static int mock_id(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
134 {
135 	struct cxl_mbox_identify id = {
136 		.fw_revision = { "mock fw v1 " },
137 		.lsa_size = cpu_to_le32(LSA_SIZE),
138 		.partition_align =
139 			cpu_to_le64(SZ_256M / CXL_CAPACITY_MULTIPLIER),
140 		.total_capacity =
141 			cpu_to_le64(DEV_SIZE / CXL_CAPACITY_MULTIPLIER),
142 	};
143 
144 	if (cmd->size_out < sizeof(id))
145 		return -EINVAL;
146 
147 	memcpy(cmd->payload_out, &id, sizeof(id));
148 
149 	return 0;
150 }
151 
152 static int mock_partition_info(struct cxl_dev_state *cxlds,
153 			       struct cxl_mbox_cmd *cmd)
154 {
155 	struct cxl_mbox_get_partition_info pi = {
156 		.active_volatile_cap =
157 			cpu_to_le64(DEV_SIZE / 2 / CXL_CAPACITY_MULTIPLIER),
158 		.active_persistent_cap =
159 			cpu_to_le64(DEV_SIZE / 2 / CXL_CAPACITY_MULTIPLIER),
160 	};
161 
162 	if (cmd->size_out < sizeof(pi))
163 		return -EINVAL;
164 
165 	memcpy(cmd->payload_out, &pi, sizeof(pi));
166 
167 	return 0;
168 }
169 
170 static int mock_get_security_state(struct cxl_dev_state *cxlds,
171 				   struct cxl_mbox_cmd *cmd)
172 {
173 	struct cxl_mockmem_data *mdata = dev_get_drvdata(cxlds->dev);
174 
175 	if (cmd->size_in)
176 		return -EINVAL;
177 
178 	if (cmd->size_out != sizeof(u32))
179 		return -EINVAL;
180 
181 	memcpy(cmd->payload_out, &mdata->security_state, sizeof(u32));
182 
183 	return 0;
184 }
185 
186 static void master_plimit_check(struct cxl_mockmem_data *mdata)
187 {
188 	if (mdata->master_limit == PASS_TRY_LIMIT)
189 		return;
190 	mdata->master_limit++;
191 	if (mdata->master_limit == PASS_TRY_LIMIT)
192 		mdata->security_state |= CXL_PMEM_SEC_STATE_MASTER_PLIMIT;
193 }
194 
195 static void user_plimit_check(struct cxl_mockmem_data *mdata)
196 {
197 	if (mdata->user_limit == PASS_TRY_LIMIT)
198 		return;
199 	mdata->user_limit++;
200 	if (mdata->user_limit == PASS_TRY_LIMIT)
201 		mdata->security_state |= CXL_PMEM_SEC_STATE_USER_PLIMIT;
202 }
203 
204 static int mock_set_passphrase(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
205 {
206 	struct cxl_mockmem_data *mdata = dev_get_drvdata(cxlds->dev);
207 	struct cxl_set_pass *set_pass;
208 
209 	if (cmd->size_in != sizeof(*set_pass))
210 		return -EINVAL;
211 
212 	if (cmd->size_out != 0)
213 		return -EINVAL;
214 
215 	if (mdata->security_state & CXL_PMEM_SEC_STATE_FROZEN) {
216 		cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
217 		return -ENXIO;
218 	}
219 
220 	set_pass = cmd->payload_in;
221 	switch (set_pass->type) {
222 	case CXL_PMEM_SEC_PASS_MASTER:
223 		if (mdata->security_state & CXL_PMEM_SEC_STATE_MASTER_PLIMIT) {
224 			cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
225 			return -ENXIO;
226 		}
227 		/*
228 		 * CXL spec rev3.0 8.2.9.8.6.2, The master pasphrase shall only be set in
229 		 * the security disabled state when the user passphrase is not set.
230 		 */
231 		if (mdata->security_state & CXL_PMEM_SEC_STATE_USER_PASS_SET) {
232 			cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
233 			return -ENXIO;
234 		}
235 		if (memcmp(mdata->master_pass, set_pass->old_pass, NVDIMM_PASSPHRASE_LEN)) {
236 			master_plimit_check(mdata);
237 			cmd->return_code = CXL_MBOX_CMD_RC_PASSPHRASE;
238 			return -ENXIO;
239 		}
240 		memcpy(mdata->master_pass, set_pass->new_pass, NVDIMM_PASSPHRASE_LEN);
241 		mdata->security_state |= CXL_PMEM_SEC_STATE_MASTER_PASS_SET;
242 		return 0;
243 
244 	case CXL_PMEM_SEC_PASS_USER:
245 		if (mdata->security_state & CXL_PMEM_SEC_STATE_USER_PLIMIT) {
246 			cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
247 			return -ENXIO;
248 		}
249 		if (memcmp(mdata->user_pass, set_pass->old_pass, NVDIMM_PASSPHRASE_LEN)) {
250 			user_plimit_check(mdata);
251 			cmd->return_code = CXL_MBOX_CMD_RC_PASSPHRASE;
252 			return -ENXIO;
253 		}
254 		memcpy(mdata->user_pass, set_pass->new_pass, NVDIMM_PASSPHRASE_LEN);
255 		mdata->security_state |= CXL_PMEM_SEC_STATE_USER_PASS_SET;
256 		return 0;
257 
258 	default:
259 		cmd->return_code = CXL_MBOX_CMD_RC_INPUT;
260 	}
261 	return -EINVAL;
262 }
263 
264 static int mock_disable_passphrase(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
265 {
266 	struct cxl_mockmem_data *mdata = dev_get_drvdata(cxlds->dev);
267 	struct cxl_disable_pass *dis_pass;
268 
269 	if (cmd->size_in != sizeof(*dis_pass))
270 		return -EINVAL;
271 
272 	if (cmd->size_out != 0)
273 		return -EINVAL;
274 
275 	if (mdata->security_state & CXL_PMEM_SEC_STATE_FROZEN) {
276 		cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
277 		return -ENXIO;
278 	}
279 
280 	dis_pass = cmd->payload_in;
281 	switch (dis_pass->type) {
282 	case CXL_PMEM_SEC_PASS_MASTER:
283 		if (mdata->security_state & CXL_PMEM_SEC_STATE_MASTER_PLIMIT) {
284 			cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
285 			return -ENXIO;
286 		}
287 
288 		if (!(mdata->security_state & CXL_PMEM_SEC_STATE_MASTER_PASS_SET)) {
289 			cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
290 			return -ENXIO;
291 		}
292 
293 		if (memcmp(dis_pass->pass, mdata->master_pass, NVDIMM_PASSPHRASE_LEN)) {
294 			master_plimit_check(mdata);
295 			cmd->return_code = CXL_MBOX_CMD_RC_PASSPHRASE;
296 			return -ENXIO;
297 		}
298 
299 		mdata->master_limit = 0;
300 		memset(mdata->master_pass, 0, NVDIMM_PASSPHRASE_LEN);
301 		mdata->security_state &= ~CXL_PMEM_SEC_STATE_MASTER_PASS_SET;
302 		return 0;
303 
304 	case CXL_PMEM_SEC_PASS_USER:
305 		if (mdata->security_state & CXL_PMEM_SEC_STATE_USER_PLIMIT) {
306 			cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
307 			return -ENXIO;
308 		}
309 
310 		if (!(mdata->security_state & CXL_PMEM_SEC_STATE_USER_PASS_SET)) {
311 			cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
312 			return -ENXIO;
313 		}
314 
315 		if (memcmp(dis_pass->pass, mdata->user_pass, NVDIMM_PASSPHRASE_LEN)) {
316 			user_plimit_check(mdata);
317 			cmd->return_code = CXL_MBOX_CMD_RC_PASSPHRASE;
318 			return -ENXIO;
319 		}
320 
321 		mdata->user_limit = 0;
322 		memset(mdata->user_pass, 0, NVDIMM_PASSPHRASE_LEN);
323 		mdata->security_state &= ~(CXL_PMEM_SEC_STATE_USER_PASS_SET |
324 					   CXL_PMEM_SEC_STATE_LOCKED);
325 		return 0;
326 
327 	default:
328 		cmd->return_code = CXL_MBOX_CMD_RC_INPUT;
329 		return -EINVAL;
330 	}
331 
332 	return 0;
333 }
334 
335 static int mock_freeze_security(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
336 {
337 	struct cxl_mockmem_data *mdata = dev_get_drvdata(cxlds->dev);
338 
339 	if (cmd->size_in != 0)
340 		return -EINVAL;
341 
342 	if (cmd->size_out != 0)
343 		return -EINVAL;
344 
345 	if (mdata->security_state & CXL_PMEM_SEC_STATE_FROZEN)
346 		return 0;
347 
348 	mdata->security_state |= CXL_PMEM_SEC_STATE_FROZEN;
349 	return 0;
350 }
351 
352 static int mock_unlock_security(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
353 {
354 	struct cxl_mockmem_data *mdata = dev_get_drvdata(cxlds->dev);
355 
356 	if (cmd->size_in != NVDIMM_PASSPHRASE_LEN)
357 		return -EINVAL;
358 
359 	if (cmd->size_out != 0)
360 		return -EINVAL;
361 
362 	if (mdata->security_state & CXL_PMEM_SEC_STATE_FROZEN) {
363 		cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
364 		return -ENXIO;
365 	}
366 
367 	if (!(mdata->security_state & CXL_PMEM_SEC_STATE_USER_PASS_SET)) {
368 		cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
369 		return -ENXIO;
370 	}
371 
372 	if (mdata->security_state & CXL_PMEM_SEC_STATE_USER_PLIMIT) {
373 		cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
374 		return -ENXIO;
375 	}
376 
377 	if (!(mdata->security_state & CXL_PMEM_SEC_STATE_LOCKED)) {
378 		cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
379 		return -ENXIO;
380 	}
381 
382 	if (memcmp(cmd->payload_in, mdata->user_pass, NVDIMM_PASSPHRASE_LEN)) {
383 		if (++mdata->user_limit == PASS_TRY_LIMIT)
384 			mdata->security_state |= CXL_PMEM_SEC_STATE_USER_PLIMIT;
385 		cmd->return_code = CXL_MBOX_CMD_RC_PASSPHRASE;
386 		return -ENXIO;
387 	}
388 
389 	mdata->user_limit = 0;
390 	mdata->security_state &= ~CXL_PMEM_SEC_STATE_LOCKED;
391 	return 0;
392 }
393 
394 static int mock_passphrase_secure_erase(struct cxl_dev_state *cxlds,
395 					struct cxl_mbox_cmd *cmd)
396 {
397 	struct cxl_mockmem_data *mdata = dev_get_drvdata(cxlds->dev);
398 	struct cxl_pass_erase *erase;
399 
400 	if (cmd->size_in != sizeof(*erase))
401 		return -EINVAL;
402 
403 	if (cmd->size_out != 0)
404 		return -EINVAL;
405 
406 	erase = cmd->payload_in;
407 	if (mdata->security_state & CXL_PMEM_SEC_STATE_FROZEN) {
408 		cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
409 		return -ENXIO;
410 	}
411 
412 	if (mdata->security_state & CXL_PMEM_SEC_STATE_USER_PLIMIT &&
413 	    erase->type == CXL_PMEM_SEC_PASS_USER) {
414 		cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
415 		return -ENXIO;
416 	}
417 
418 	if (mdata->security_state & CXL_PMEM_SEC_STATE_MASTER_PLIMIT &&
419 	    erase->type == CXL_PMEM_SEC_PASS_MASTER) {
420 		cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
421 		return -ENXIO;
422 	}
423 
424 	switch (erase->type) {
425 	case CXL_PMEM_SEC_PASS_MASTER:
426 		/*
427 		 * The spec does not clearly define the behavior of the scenario
428 		 * where a master passphrase is passed in while the master
429 		 * passphrase is not set and user passphrase is not set. The
430 		 * code will take the assumption that it will behave the same
431 		 * as a CXL secure erase command without passphrase (0x4401).
432 		 */
433 		if (mdata->security_state & CXL_PMEM_SEC_STATE_MASTER_PASS_SET) {
434 			if (memcmp(mdata->master_pass, erase->pass,
435 				   NVDIMM_PASSPHRASE_LEN)) {
436 				master_plimit_check(mdata);
437 				cmd->return_code = CXL_MBOX_CMD_RC_PASSPHRASE;
438 				return -ENXIO;
439 			}
440 			mdata->master_limit = 0;
441 			mdata->user_limit = 0;
442 			mdata->security_state &= ~CXL_PMEM_SEC_STATE_USER_PASS_SET;
443 			memset(mdata->user_pass, 0, NVDIMM_PASSPHRASE_LEN);
444 			mdata->security_state &= ~CXL_PMEM_SEC_STATE_LOCKED;
445 		} else {
446 			/*
447 			 * CXL rev3 8.2.9.8.6.3 Disable Passphrase
448 			 * When master passphrase is disabled, the device shall
449 			 * return Invalid Input for the Passphrase Secure Erase
450 			 * command with master passphrase.
451 			 */
452 			return -EINVAL;
453 		}
454 		/* Scramble encryption keys so that data is effectively erased */
455 		break;
456 	case CXL_PMEM_SEC_PASS_USER:
457 		/*
458 		 * The spec does not clearly define the behavior of the scenario
459 		 * where a user passphrase is passed in while the user
460 		 * passphrase is not set. The code will take the assumption that
461 		 * it will behave the same as a CXL secure erase command without
462 		 * passphrase (0x4401).
463 		 */
464 		if (mdata->security_state & CXL_PMEM_SEC_STATE_USER_PASS_SET) {
465 			if (memcmp(mdata->user_pass, erase->pass,
466 				   NVDIMM_PASSPHRASE_LEN)) {
467 				user_plimit_check(mdata);
468 				cmd->return_code = CXL_MBOX_CMD_RC_PASSPHRASE;
469 				return -ENXIO;
470 			}
471 			mdata->user_limit = 0;
472 			mdata->security_state &= ~CXL_PMEM_SEC_STATE_USER_PASS_SET;
473 			memset(mdata->user_pass, 0, NVDIMM_PASSPHRASE_LEN);
474 		}
475 
476 		/*
477 		 * CXL rev3 Table 8-118
478 		 * If user passphrase is not set or supported by device, current
479 		 * passphrase value is ignored. Will make the assumption that
480 		 * the operation will proceed as secure erase w/o passphrase
481 		 * since spec is not explicit.
482 		 */
483 
484 		/* Scramble encryption keys so that data is effectively erased */
485 		break;
486 	default:
487 		return -EINVAL;
488 	}
489 
490 	return 0;
491 }
492 
493 static int mock_get_lsa(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
494 {
495 	struct cxl_mbox_get_lsa *get_lsa = cmd->payload_in;
496 	struct cxl_mockmem_data *mdata = dev_get_drvdata(cxlds->dev);
497 	void *lsa = mdata->lsa;
498 	u32 offset, length;
499 
500 	if (sizeof(*get_lsa) > cmd->size_in)
501 		return -EINVAL;
502 	offset = le32_to_cpu(get_lsa->offset);
503 	length = le32_to_cpu(get_lsa->length);
504 	if (offset + length > LSA_SIZE)
505 		return -EINVAL;
506 	if (length > cmd->size_out)
507 		return -EINVAL;
508 
509 	memcpy(cmd->payload_out, lsa + offset, length);
510 	return 0;
511 }
512 
513 static int mock_set_lsa(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
514 {
515 	struct cxl_mbox_set_lsa *set_lsa = cmd->payload_in;
516 	struct cxl_mockmem_data *mdata = dev_get_drvdata(cxlds->dev);
517 	void *lsa = mdata->lsa;
518 	u32 offset, length;
519 
520 	if (sizeof(*set_lsa) > cmd->size_in)
521 		return -EINVAL;
522 	offset = le32_to_cpu(set_lsa->offset);
523 	length = cmd->size_in - sizeof(*set_lsa);
524 	if (offset + length > LSA_SIZE)
525 		return -EINVAL;
526 
527 	memcpy(lsa + offset, &set_lsa->data[0], length);
528 	return 0;
529 }
530 
531 static int mock_health_info(struct cxl_dev_state *cxlds,
532 			    struct cxl_mbox_cmd *cmd)
533 {
534 	struct cxl_mbox_health_info health_info = {
535 		/* set flags for maint needed, perf degraded, hw replacement */
536 		.health_status = 0x7,
537 		/* set media status to "All Data Lost" */
538 		.media_status = 0x3,
539 		/*
540 		 * set ext_status flags for:
541 		 *  ext_life_used: normal,
542 		 *  ext_temperature: critical,
543 		 *  ext_corrected_volatile: warning,
544 		 *  ext_corrected_persistent: normal,
545 		 */
546 		.ext_status = 0x18,
547 		.life_used = 15,
548 		.temperature = cpu_to_le16(25),
549 		.dirty_shutdowns = cpu_to_le32(10),
550 		.volatile_errors = cpu_to_le32(20),
551 		.pmem_errors = cpu_to_le32(30),
552 	};
553 
554 	if (cmd->size_out < sizeof(health_info))
555 		return -EINVAL;
556 
557 	memcpy(cmd->payload_out, &health_info, sizeof(health_info));
558 	return 0;
559 }
560 
561 static int cxl_mock_mbox_send(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
562 {
563 	struct device *dev = cxlds->dev;
564 	int rc = -EIO;
565 
566 	switch (cmd->opcode) {
567 	case CXL_MBOX_OP_GET_SUPPORTED_LOGS:
568 		rc = mock_gsl(cmd);
569 		break;
570 	case CXL_MBOX_OP_GET_LOG:
571 		rc = mock_get_log(cxlds, cmd);
572 		break;
573 	case CXL_MBOX_OP_IDENTIFY:
574 		if (cxlds->rcd)
575 			rc = mock_rcd_id(cxlds, cmd);
576 		else
577 			rc = mock_id(cxlds, cmd);
578 		break;
579 	case CXL_MBOX_OP_GET_LSA:
580 		rc = mock_get_lsa(cxlds, cmd);
581 		break;
582 	case CXL_MBOX_OP_GET_PARTITION_INFO:
583 		rc = mock_partition_info(cxlds, cmd);
584 		break;
585 	case CXL_MBOX_OP_SET_LSA:
586 		rc = mock_set_lsa(cxlds, cmd);
587 		break;
588 	case CXL_MBOX_OP_GET_HEALTH_INFO:
589 		rc = mock_health_info(cxlds, cmd);
590 		break;
591 	case CXL_MBOX_OP_GET_SECURITY_STATE:
592 		rc = mock_get_security_state(cxlds, cmd);
593 		break;
594 	case CXL_MBOX_OP_SET_PASSPHRASE:
595 		rc = mock_set_passphrase(cxlds, cmd);
596 		break;
597 	case CXL_MBOX_OP_DISABLE_PASSPHRASE:
598 		rc = mock_disable_passphrase(cxlds, cmd);
599 		break;
600 	case CXL_MBOX_OP_FREEZE_SECURITY:
601 		rc = mock_freeze_security(cxlds, cmd);
602 		break;
603 	case CXL_MBOX_OP_UNLOCK:
604 		rc = mock_unlock_security(cxlds, cmd);
605 		break;
606 	case CXL_MBOX_OP_PASSPHRASE_SECURE_ERASE:
607 		rc = mock_passphrase_secure_erase(cxlds, cmd);
608 		break;
609 	default:
610 		break;
611 	}
612 
613 	dev_dbg(dev, "opcode: %#x sz_in: %zd sz_out: %zd rc: %d\n", cmd->opcode,
614 		cmd->size_in, cmd->size_out, rc);
615 
616 	return rc;
617 }
618 
619 static void label_area_release(void *lsa)
620 {
621 	vfree(lsa);
622 }
623 
624 static bool is_rcd(struct platform_device *pdev)
625 {
626 	const struct platform_device_id *id = platform_get_device_id(pdev);
627 
628 	return !!id->driver_data;
629 }
630 
631 static int cxl_mock_mem_probe(struct platform_device *pdev)
632 {
633 	struct device *dev = &pdev->dev;
634 	struct cxl_memdev *cxlmd;
635 	struct cxl_dev_state *cxlds;
636 	struct cxl_mockmem_data *mdata;
637 	int rc;
638 
639 	mdata = devm_kzalloc(dev, sizeof(*mdata), GFP_KERNEL);
640 	if (!mdata)
641 		return -ENOMEM;
642 	dev_set_drvdata(dev, mdata);
643 
644 	mdata->lsa = vmalloc(LSA_SIZE);
645 	if (!mdata->lsa)
646 		return -ENOMEM;
647 	rc = devm_add_action_or_reset(dev, label_area_release, mdata->lsa);
648 	if (rc)
649 		return rc;
650 
651 	cxlds = cxl_dev_state_create(dev);
652 	if (IS_ERR(cxlds))
653 		return PTR_ERR(cxlds);
654 
655 	cxlds->serial = pdev->id;
656 	cxlds->mbox_send = cxl_mock_mbox_send;
657 	cxlds->payload_size = SZ_4K;
658 	if (is_rcd(pdev)) {
659 		cxlds->rcd = true;
660 		cxlds->component_reg_phys = CXL_RESOURCE_NONE;
661 	}
662 
663 	rc = cxl_enumerate_cmds(cxlds);
664 	if (rc)
665 		return rc;
666 
667 	rc = cxl_dev_state_identify(cxlds);
668 	if (rc)
669 		return rc;
670 
671 	rc = cxl_mem_create_range_info(cxlds);
672 	if (rc)
673 		return rc;
674 
675 	cxlmd = devm_cxl_add_memdev(cxlds);
676 	if (IS_ERR(cxlmd))
677 		return PTR_ERR(cxlmd);
678 
679 	return 0;
680 }
681 
682 static ssize_t security_lock_show(struct device *dev,
683 				  struct device_attribute *attr, char *buf)
684 {
685 	struct cxl_mockmem_data *mdata = dev_get_drvdata(dev);
686 
687 	return sysfs_emit(buf, "%u\n",
688 			  !!(mdata->security_state & CXL_PMEM_SEC_STATE_LOCKED));
689 }
690 
691 static ssize_t security_lock_store(struct device *dev, struct device_attribute *attr,
692 				   const char *buf, size_t count)
693 {
694 	struct cxl_mockmem_data *mdata = dev_get_drvdata(dev);
695 	u32 mask = CXL_PMEM_SEC_STATE_FROZEN | CXL_PMEM_SEC_STATE_USER_PLIMIT |
696 		   CXL_PMEM_SEC_STATE_MASTER_PLIMIT;
697 	int val;
698 
699 	if (kstrtoint(buf, 0, &val) < 0)
700 		return -EINVAL;
701 
702 	if (val == 1) {
703 		if (!(mdata->security_state & CXL_PMEM_SEC_STATE_USER_PASS_SET))
704 			return -ENXIO;
705 		mdata->security_state |= CXL_PMEM_SEC_STATE_LOCKED;
706 		mdata->security_state &= ~mask;
707 	} else {
708 		return -EINVAL;
709 	}
710 	return count;
711 }
712 
713 static DEVICE_ATTR_RW(security_lock);
714 
715 static struct attribute *cxl_mock_mem_attrs[] = {
716 	&dev_attr_security_lock.attr,
717 	NULL
718 };
719 ATTRIBUTE_GROUPS(cxl_mock_mem);
720 
721 static const struct platform_device_id cxl_mock_mem_ids[] = {
722 	{ .name = "cxl_mem", 0 },
723 	{ .name = "cxl_rcd", 1 },
724 	{ },
725 };
726 MODULE_DEVICE_TABLE(platform, cxl_mock_mem_ids);
727 
728 static struct platform_driver cxl_mock_mem_driver = {
729 	.probe = cxl_mock_mem_probe,
730 	.id_table = cxl_mock_mem_ids,
731 	.driver = {
732 		.name = KBUILD_MODNAME,
733 		.dev_groups = cxl_mock_mem_groups,
734 	},
735 };
736 
737 module_platform_driver(cxl_mock_mem_driver);
738 MODULE_LICENSE("GPL v2");
739 MODULE_IMPORT_NS(CXL);
740