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