Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/testing/cxl/test/mem.c
49639 views
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/vmalloc.h>
7
#include <linux/module.h>
8
#include <linux/delay.h>
9
#include <linux/sizes.h>
10
#include <linux/bits.h>
11
#include <cxl/mailbox.h>
12
#include <linux/unaligned.h>
13
#include <crypto/sha2.h>
14
#include <cxlmem.h>
15
16
#include "trace.h"
17
18
#define LSA_SIZE SZ_128K
19
#define FW_SIZE SZ_64M
20
#define FW_SLOTS 3
21
#define DEV_SIZE SZ_2G
22
#define EFFECT(x) (1U << x)
23
24
#define MOCK_INJECT_DEV_MAX 8
25
#define MOCK_INJECT_TEST_MAX 128
26
27
static unsigned int poison_inject_dev_max = MOCK_INJECT_DEV_MAX;
28
29
enum cxl_command_effects {
30
CONF_CHANGE_COLD_RESET = 0,
31
CONF_CHANGE_IMMEDIATE,
32
DATA_CHANGE_IMMEDIATE,
33
POLICY_CHANGE_IMMEDIATE,
34
LOG_CHANGE_IMMEDIATE,
35
SECURITY_CHANGE_IMMEDIATE,
36
BACKGROUND_OP,
37
SECONDARY_MBOX_SUPPORTED,
38
};
39
40
#define CXL_CMD_EFFECT_NONE cpu_to_le16(0)
41
42
static struct cxl_cel_entry mock_cel[] = {
43
{
44
.opcode = cpu_to_le16(CXL_MBOX_OP_GET_SUPPORTED_LOGS),
45
.effect = CXL_CMD_EFFECT_NONE,
46
},
47
{
48
.opcode = cpu_to_le16(CXL_MBOX_OP_GET_SUPPORTED_FEATURES),
49
.effect = CXL_CMD_EFFECT_NONE,
50
},
51
{
52
.opcode = cpu_to_le16(CXL_MBOX_OP_GET_FEATURE),
53
.effect = CXL_CMD_EFFECT_NONE,
54
},
55
{
56
.opcode = cpu_to_le16(CXL_MBOX_OP_SET_FEATURE),
57
.effect = cpu_to_le16(EFFECT(CONF_CHANGE_IMMEDIATE)),
58
},
59
{
60
.opcode = cpu_to_le16(CXL_MBOX_OP_IDENTIFY),
61
.effect = CXL_CMD_EFFECT_NONE,
62
},
63
{
64
.opcode = cpu_to_le16(CXL_MBOX_OP_GET_LSA),
65
.effect = CXL_CMD_EFFECT_NONE,
66
},
67
{
68
.opcode = cpu_to_le16(CXL_MBOX_OP_GET_PARTITION_INFO),
69
.effect = CXL_CMD_EFFECT_NONE,
70
},
71
{
72
.opcode = cpu_to_le16(CXL_MBOX_OP_SET_LSA),
73
.effect = cpu_to_le16(EFFECT(CONF_CHANGE_IMMEDIATE) |
74
EFFECT(DATA_CHANGE_IMMEDIATE)),
75
},
76
{
77
.opcode = cpu_to_le16(CXL_MBOX_OP_GET_HEALTH_INFO),
78
.effect = CXL_CMD_EFFECT_NONE,
79
},
80
{
81
.opcode = cpu_to_le16(CXL_MBOX_OP_SET_SHUTDOWN_STATE),
82
.effect = POLICY_CHANGE_IMMEDIATE,
83
},
84
{
85
.opcode = cpu_to_le16(CXL_MBOX_OP_GET_POISON),
86
.effect = CXL_CMD_EFFECT_NONE,
87
},
88
{
89
.opcode = cpu_to_le16(CXL_MBOX_OP_INJECT_POISON),
90
.effect = cpu_to_le16(EFFECT(DATA_CHANGE_IMMEDIATE)),
91
},
92
{
93
.opcode = cpu_to_le16(CXL_MBOX_OP_CLEAR_POISON),
94
.effect = cpu_to_le16(EFFECT(DATA_CHANGE_IMMEDIATE)),
95
},
96
{
97
.opcode = cpu_to_le16(CXL_MBOX_OP_GET_FW_INFO),
98
.effect = CXL_CMD_EFFECT_NONE,
99
},
100
{
101
.opcode = cpu_to_le16(CXL_MBOX_OP_TRANSFER_FW),
102
.effect = cpu_to_le16(EFFECT(CONF_CHANGE_COLD_RESET) |
103
EFFECT(BACKGROUND_OP)),
104
},
105
{
106
.opcode = cpu_to_le16(CXL_MBOX_OP_ACTIVATE_FW),
107
.effect = cpu_to_le16(EFFECT(CONF_CHANGE_COLD_RESET) |
108
EFFECT(CONF_CHANGE_IMMEDIATE)),
109
},
110
{
111
.opcode = cpu_to_le16(CXL_MBOX_OP_SANITIZE),
112
.effect = cpu_to_le16(EFFECT(DATA_CHANGE_IMMEDIATE) |
113
EFFECT(SECURITY_CHANGE_IMMEDIATE) |
114
EFFECT(BACKGROUND_OP)),
115
},
116
};
117
118
/* See CXL 2.0 Table 181 Get Health Info Output Payload */
119
struct cxl_mbox_health_info {
120
u8 health_status;
121
u8 media_status;
122
u8 ext_status;
123
u8 life_used;
124
__le16 temperature;
125
__le32 dirty_shutdowns;
126
__le32 volatile_errors;
127
__le32 pmem_errors;
128
} __packed;
129
130
static struct {
131
struct cxl_mbox_get_supported_logs gsl;
132
struct cxl_gsl_entry entry;
133
} mock_gsl_payload = {
134
.gsl = {
135
.entries = cpu_to_le16(1),
136
},
137
.entry = {
138
.uuid = DEFINE_CXL_CEL_UUID,
139
.size = cpu_to_le32(sizeof(mock_cel)),
140
},
141
};
142
143
#define PASS_TRY_LIMIT 3
144
145
#define CXL_TEST_EVENT_CNT_MAX 15
146
147
/* Set a number of events to return at a time for simulation. */
148
#define CXL_TEST_EVENT_RET_MAX 4
149
150
struct mock_event_log {
151
u16 clear_idx;
152
u16 cur_idx;
153
u16 nr_events;
154
u16 nr_overflow;
155
u16 overflow_reset;
156
struct cxl_event_record_raw *events[CXL_TEST_EVENT_CNT_MAX];
157
};
158
159
struct mock_event_store {
160
struct mock_event_log mock_logs[CXL_EVENT_TYPE_MAX];
161
u32 ev_status;
162
};
163
164
struct vendor_test_feat {
165
__le32 data;
166
} __packed;
167
168
struct cxl_mockmem_data {
169
void *lsa;
170
void *fw;
171
int fw_slot;
172
int fw_staged;
173
size_t fw_size;
174
u32 security_state;
175
u8 user_pass[NVDIMM_PASSPHRASE_LEN];
176
u8 master_pass[NVDIMM_PASSPHRASE_LEN];
177
int user_limit;
178
int master_limit;
179
struct mock_event_store mes;
180
struct cxl_memdev_state *mds;
181
u8 event_buf[SZ_4K];
182
u64 timestamp;
183
unsigned long sanitize_timeout;
184
struct vendor_test_feat test_feat;
185
u8 shutdown_state;
186
};
187
188
static struct mock_event_log *event_find_log(struct device *dev, int log_type)
189
{
190
struct cxl_mockmem_data *mdata = dev_get_drvdata(dev);
191
192
if (log_type >= CXL_EVENT_TYPE_MAX)
193
return NULL;
194
return &mdata->mes.mock_logs[log_type];
195
}
196
197
static struct cxl_event_record_raw *event_get_current(struct mock_event_log *log)
198
{
199
return log->events[log->cur_idx];
200
}
201
202
static void event_reset_log(struct mock_event_log *log)
203
{
204
log->cur_idx = 0;
205
log->clear_idx = 0;
206
log->nr_overflow = log->overflow_reset;
207
}
208
209
/* Handle can never be 0 use 1 based indexing for handle */
210
static u16 event_get_clear_handle(struct mock_event_log *log)
211
{
212
return log->clear_idx + 1;
213
}
214
215
/* Handle can never be 0 use 1 based indexing for handle */
216
static __le16 event_get_cur_event_handle(struct mock_event_log *log)
217
{
218
u16 cur_handle = log->cur_idx + 1;
219
220
return cpu_to_le16(cur_handle);
221
}
222
223
static bool event_log_empty(struct mock_event_log *log)
224
{
225
return log->cur_idx == log->nr_events;
226
}
227
228
static void mes_add_event(struct mock_event_store *mes,
229
enum cxl_event_log_type log_type,
230
struct cxl_event_record_raw *event)
231
{
232
struct mock_event_log *log;
233
234
if (WARN_ON(log_type >= CXL_EVENT_TYPE_MAX))
235
return;
236
237
log = &mes->mock_logs[log_type];
238
239
if ((log->nr_events + 1) > CXL_TEST_EVENT_CNT_MAX) {
240
log->nr_overflow++;
241
log->overflow_reset = log->nr_overflow;
242
return;
243
}
244
245
log->events[log->nr_events] = event;
246
log->nr_events++;
247
}
248
249
/*
250
* Vary the number of events returned to simulate events occuring while the
251
* logs are being read.
252
*/
253
static atomic_t event_counter = ATOMIC_INIT(0);
254
255
static int mock_get_event(struct device *dev, struct cxl_mbox_cmd *cmd)
256
{
257
struct cxl_get_event_payload *pl;
258
struct mock_event_log *log;
259
int ret_limit;
260
u8 log_type;
261
int i;
262
263
if (cmd->size_in != sizeof(log_type))
264
return -EINVAL;
265
266
/* Vary return limit from 1 to CXL_TEST_EVENT_RET_MAX */
267
ret_limit = (atomic_inc_return(&event_counter) % CXL_TEST_EVENT_RET_MAX) + 1;
268
269
if (cmd->size_out < struct_size(pl, records, ret_limit))
270
return -EINVAL;
271
272
log_type = *((u8 *)cmd->payload_in);
273
if (log_type >= CXL_EVENT_TYPE_MAX)
274
return -EINVAL;
275
276
memset(cmd->payload_out, 0, struct_size(pl, records, 0));
277
278
log = event_find_log(dev, log_type);
279
if (!log || event_log_empty(log))
280
return 0;
281
282
pl = cmd->payload_out;
283
284
for (i = 0; i < ret_limit && !event_log_empty(log); i++) {
285
memcpy(&pl->records[i], event_get_current(log),
286
sizeof(pl->records[i]));
287
pl->records[i].event.generic.hdr.handle =
288
event_get_cur_event_handle(log);
289
log->cur_idx++;
290
}
291
292
cmd->size_out = struct_size(pl, records, i);
293
pl->record_count = cpu_to_le16(i);
294
if (!event_log_empty(log))
295
pl->flags |= CXL_GET_EVENT_FLAG_MORE_RECORDS;
296
297
if (log->nr_overflow) {
298
u64 ns;
299
300
pl->flags |= CXL_GET_EVENT_FLAG_OVERFLOW;
301
pl->overflow_err_count = cpu_to_le16(log->nr_overflow);
302
ns = ktime_get_real_ns();
303
ns -= 5000000000; /* 5s ago */
304
pl->first_overflow_timestamp = cpu_to_le64(ns);
305
ns = ktime_get_real_ns();
306
ns -= 1000000000; /* 1s ago */
307
pl->last_overflow_timestamp = cpu_to_le64(ns);
308
}
309
310
return 0;
311
}
312
313
static int mock_clear_event(struct device *dev, struct cxl_mbox_cmd *cmd)
314
{
315
struct cxl_mbox_clear_event_payload *pl = cmd->payload_in;
316
struct mock_event_log *log;
317
u8 log_type = pl->event_log;
318
u16 handle;
319
int nr;
320
321
if (log_type >= CXL_EVENT_TYPE_MAX)
322
return -EINVAL;
323
324
log = event_find_log(dev, log_type);
325
if (!log)
326
return 0; /* No mock data in this log */
327
328
/*
329
* This check is technically not invalid per the specification AFAICS.
330
* (The host could 'guess' handles and clear them in order).
331
* However, this is not good behavior for the host so test it.
332
*/
333
if (log->clear_idx + pl->nr_recs > log->cur_idx) {
334
dev_err(dev,
335
"Attempting to clear more events than returned!\n");
336
return -EINVAL;
337
}
338
339
/* Check handle order prior to clearing events */
340
for (nr = 0, handle = event_get_clear_handle(log);
341
nr < pl->nr_recs;
342
nr++, handle++) {
343
if (handle != le16_to_cpu(pl->handles[nr])) {
344
dev_err(dev, "Clearing events out of order\n");
345
return -EINVAL;
346
}
347
}
348
349
if (log->nr_overflow)
350
log->nr_overflow = 0;
351
352
/* Clear events */
353
log->clear_idx += pl->nr_recs;
354
return 0;
355
}
356
357
static void cxl_mock_event_trigger(struct device *dev)
358
{
359
struct cxl_mockmem_data *mdata = dev_get_drvdata(dev);
360
struct mock_event_store *mes = &mdata->mes;
361
int i;
362
363
for (i = CXL_EVENT_TYPE_INFO; i < CXL_EVENT_TYPE_MAX; i++) {
364
struct mock_event_log *log;
365
366
log = event_find_log(dev, i);
367
if (log)
368
event_reset_log(log);
369
}
370
371
cxl_mem_get_event_records(mdata->mds, mes->ev_status);
372
}
373
374
struct cxl_event_record_raw maint_needed = {
375
.id = UUID_INIT(0xBA5EBA11, 0xABCD, 0xEFEB,
376
0xa5, 0x5a, 0xa5, 0x5a, 0xa5, 0xa5, 0x5a, 0xa5),
377
.event.generic = {
378
.hdr = {
379
.length = sizeof(struct cxl_event_record_raw),
380
.flags[0] = CXL_EVENT_RECORD_FLAG_MAINT_NEEDED,
381
/* .handle = Set dynamically */
382
.related_handle = cpu_to_le16(0xa5b6),
383
},
384
.data = { 0xDE, 0xAD, 0xBE, 0xEF },
385
},
386
};
387
388
struct cxl_event_record_raw hardware_replace = {
389
.id = UUID_INIT(0xABCDEFEB, 0xBA11, 0xBA5E,
390
0xa5, 0x5a, 0xa5, 0x5a, 0xa5, 0xa5, 0x5a, 0xa5),
391
.event.generic = {
392
.hdr = {
393
.length = sizeof(struct cxl_event_record_raw),
394
.flags[0] = CXL_EVENT_RECORD_FLAG_HW_REPLACE,
395
/* .handle = Set dynamically */
396
.related_handle = cpu_to_le16(0xb6a5),
397
},
398
.data = { 0xDE, 0xAD, 0xBE, 0xEF },
399
},
400
};
401
402
struct cxl_test_gen_media {
403
uuid_t id;
404
struct cxl_event_gen_media rec;
405
} __packed;
406
407
struct cxl_test_gen_media gen_media = {
408
.id = CXL_EVENT_GEN_MEDIA_UUID,
409
.rec = {
410
.media_hdr = {
411
.hdr = {
412
.length = sizeof(struct cxl_test_gen_media),
413
.flags[0] = CXL_EVENT_RECORD_FLAG_PERMANENT,
414
/* .handle = Set dynamically */
415
.related_handle = cpu_to_le16(0),
416
},
417
.phys_addr = cpu_to_le64(0x2000),
418
.descriptor = CXL_GMER_EVT_DESC_UNCORECTABLE_EVENT,
419
.type = CXL_GMER_MEM_EVT_TYPE_DATA_PATH_ERROR,
420
.transaction_type = CXL_GMER_TRANS_HOST_WRITE,
421
/* .validity_flags = <set below> */
422
.channel = 1,
423
.rank = 30,
424
},
425
.component_id = { 0x3, 0x74, 0xc5, 0x8, 0x9a, 0x1a, 0xb, 0xfc, 0xd2, 0x7e, 0x2f, 0x31, 0x9b, 0x3c, 0x81, 0x4d },
426
.cme_threshold_ev_flags = 3,
427
.cme_count = { 33, 0, 0 },
428
.sub_type = 0x2,
429
},
430
};
431
432
struct cxl_test_dram {
433
uuid_t id;
434
struct cxl_event_dram rec;
435
} __packed;
436
437
struct cxl_test_dram dram = {
438
.id = CXL_EVENT_DRAM_UUID,
439
.rec = {
440
.media_hdr = {
441
.hdr = {
442
.length = sizeof(struct cxl_test_dram),
443
.flags[0] = CXL_EVENT_RECORD_FLAG_PERF_DEGRADED,
444
/* .handle = Set dynamically */
445
.related_handle = cpu_to_le16(0),
446
},
447
.phys_addr = cpu_to_le64(0x8000),
448
.descriptor = CXL_GMER_EVT_DESC_THRESHOLD_EVENT,
449
.type = CXL_GMER_MEM_EVT_TYPE_INV_ADDR,
450
.transaction_type = CXL_GMER_TRANS_INTERNAL_MEDIA_SCRUB,
451
/* .validity_flags = <set below> */
452
.channel = 1,
453
},
454
.bank_group = 5,
455
.bank = 2,
456
.column = {0xDE, 0xAD},
457
.component_id = { 0x1, 0x74, 0xc5, 0x8, 0x9a, 0x1a, 0xb, 0xfc, 0xd2, 0x7e, 0x2f, 0x31, 0x9b, 0x3c, 0x81, 0x4d },
458
.sub_channel = 8,
459
.cme_threshold_ev_flags = 2,
460
.cvme_count = { 14, 0, 0 },
461
.sub_type = 0x5,
462
},
463
};
464
465
struct cxl_test_mem_module {
466
uuid_t id;
467
struct cxl_event_mem_module rec;
468
} __packed;
469
470
struct cxl_test_mem_module mem_module = {
471
.id = CXL_EVENT_MEM_MODULE_UUID,
472
.rec = {
473
.hdr = {
474
.length = sizeof(struct cxl_test_mem_module),
475
/* .handle = Set dynamically */
476
.related_handle = cpu_to_le16(0),
477
},
478
.event_type = CXL_MMER_TEMP_CHANGE,
479
.info = {
480
.health_status = CXL_DHI_HS_PERFORMANCE_DEGRADED,
481
.media_status = CXL_DHI_MS_ALL_DATA_LOST,
482
.add_status = (CXL_DHI_AS_CRITICAL << 2) |
483
(CXL_DHI_AS_WARNING << 4) |
484
(CXL_DHI_AS_WARNING << 5),
485
.device_temp = { 0xDE, 0xAD},
486
.dirty_shutdown_cnt = { 0xde, 0xad, 0xbe, 0xef },
487
.cor_vol_err_cnt = { 0xde, 0xad, 0xbe, 0xef },
488
.cor_per_err_cnt = { 0xde, 0xad, 0xbe, 0xef },
489
},
490
/* .validity_flags = <set below> */
491
.component_id = { 0x2, 0x74, 0xc5, 0x8, 0x9a, 0x1a, 0xb, 0xfc, 0xd2, 0x7e, 0x2f, 0x31, 0x9b, 0x3c, 0x81, 0x4d },
492
.event_sub_type = 0x3,
493
},
494
};
495
496
static int mock_set_timestamp(struct cxl_dev_state *cxlds,
497
struct cxl_mbox_cmd *cmd)
498
{
499
struct cxl_mockmem_data *mdata = dev_get_drvdata(cxlds->dev);
500
struct cxl_mbox_set_timestamp_in *ts = cmd->payload_in;
501
502
if (cmd->size_in != sizeof(*ts))
503
return -EINVAL;
504
505
if (cmd->size_out != 0)
506
return -EINVAL;
507
508
mdata->timestamp = le64_to_cpu(ts->timestamp);
509
return 0;
510
}
511
512
static void cxl_mock_add_event_logs(struct mock_event_store *mes)
513
{
514
put_unaligned_le16(CXL_GMER_VALID_CHANNEL | CXL_GMER_VALID_RANK |
515
CXL_GMER_VALID_COMPONENT | CXL_GMER_VALID_COMPONENT_ID_FORMAT,
516
&gen_media.rec.media_hdr.validity_flags);
517
518
put_unaligned_le16(CXL_DER_VALID_CHANNEL | CXL_DER_VALID_BANK_GROUP |
519
CXL_DER_VALID_BANK | CXL_DER_VALID_COLUMN | CXL_DER_VALID_SUB_CHANNEL |
520
CXL_DER_VALID_COMPONENT | CXL_DER_VALID_COMPONENT_ID_FORMAT,
521
&dram.rec.media_hdr.validity_flags);
522
523
put_unaligned_le16(CXL_MMER_VALID_COMPONENT | CXL_MMER_VALID_COMPONENT_ID_FORMAT,
524
&mem_module.rec.validity_flags);
525
526
mes_add_event(mes, CXL_EVENT_TYPE_INFO, &maint_needed);
527
mes_add_event(mes, CXL_EVENT_TYPE_INFO,
528
(struct cxl_event_record_raw *)&gen_media);
529
mes_add_event(mes, CXL_EVENT_TYPE_INFO,
530
(struct cxl_event_record_raw *)&mem_module);
531
mes->ev_status |= CXLDEV_EVENT_STATUS_INFO;
532
533
mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &maint_needed);
534
mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace);
535
mes_add_event(mes, CXL_EVENT_TYPE_FAIL,
536
(struct cxl_event_record_raw *)&dram);
537
mes_add_event(mes, CXL_EVENT_TYPE_FAIL,
538
(struct cxl_event_record_raw *)&gen_media);
539
mes_add_event(mes, CXL_EVENT_TYPE_FAIL,
540
(struct cxl_event_record_raw *)&mem_module);
541
mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace);
542
mes_add_event(mes, CXL_EVENT_TYPE_FAIL,
543
(struct cxl_event_record_raw *)&dram);
544
/* Overflow this log */
545
mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace);
546
mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace);
547
mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace);
548
mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace);
549
mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace);
550
mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace);
551
mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace);
552
mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace);
553
mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace);
554
mes_add_event(mes, CXL_EVENT_TYPE_FAIL, &hardware_replace);
555
mes->ev_status |= CXLDEV_EVENT_STATUS_FAIL;
556
557
mes_add_event(mes, CXL_EVENT_TYPE_FATAL, &hardware_replace);
558
mes_add_event(mes, CXL_EVENT_TYPE_FATAL,
559
(struct cxl_event_record_raw *)&dram);
560
mes->ev_status |= CXLDEV_EVENT_STATUS_FATAL;
561
}
562
563
static int mock_gsl(struct cxl_mbox_cmd *cmd)
564
{
565
if (cmd->size_out < sizeof(mock_gsl_payload))
566
return -EINVAL;
567
568
memcpy(cmd->payload_out, &mock_gsl_payload, sizeof(mock_gsl_payload));
569
cmd->size_out = sizeof(mock_gsl_payload);
570
571
return 0;
572
}
573
574
static int mock_get_log(struct cxl_memdev_state *mds, struct cxl_mbox_cmd *cmd)
575
{
576
struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox;
577
struct cxl_mbox_get_log *gl = cmd->payload_in;
578
u32 offset = le32_to_cpu(gl->offset);
579
u32 length = le32_to_cpu(gl->length);
580
uuid_t uuid = DEFINE_CXL_CEL_UUID;
581
void *data = &mock_cel;
582
583
if (cmd->size_in < sizeof(*gl))
584
return -EINVAL;
585
if (length > cxl_mbox->payload_size)
586
return -EINVAL;
587
if (offset + length > sizeof(mock_cel))
588
return -EINVAL;
589
if (!uuid_equal(&gl->uuid, &uuid))
590
return -EINVAL;
591
if (length > cmd->size_out)
592
return -EINVAL;
593
594
memcpy(cmd->payload_out, data + offset, length);
595
596
return 0;
597
}
598
599
static int mock_rcd_id(struct cxl_mbox_cmd *cmd)
600
{
601
struct cxl_mbox_identify id = {
602
.fw_revision = { "mock fw v1 " },
603
.total_capacity =
604
cpu_to_le64(DEV_SIZE / CXL_CAPACITY_MULTIPLIER),
605
.volatile_capacity =
606
cpu_to_le64(DEV_SIZE / CXL_CAPACITY_MULTIPLIER),
607
};
608
609
if (cmd->size_out < sizeof(id))
610
return -EINVAL;
611
612
memcpy(cmd->payload_out, &id, sizeof(id));
613
614
return 0;
615
}
616
617
static int mock_id(struct cxl_mbox_cmd *cmd)
618
{
619
struct cxl_mbox_identify id = {
620
.fw_revision = { "mock fw v1 " },
621
.lsa_size = cpu_to_le32(LSA_SIZE),
622
.partition_align =
623
cpu_to_le64(SZ_256M / CXL_CAPACITY_MULTIPLIER),
624
.total_capacity =
625
cpu_to_le64(DEV_SIZE / CXL_CAPACITY_MULTIPLIER),
626
.inject_poison_limit = cpu_to_le16(MOCK_INJECT_TEST_MAX),
627
};
628
629
put_unaligned_le24(CXL_POISON_LIST_MAX, id.poison_list_max_mer);
630
631
if (cmd->size_out < sizeof(id))
632
return -EINVAL;
633
634
memcpy(cmd->payload_out, &id, sizeof(id));
635
636
return 0;
637
}
638
639
static int mock_partition_info(struct cxl_mbox_cmd *cmd)
640
{
641
struct cxl_mbox_get_partition_info pi = {
642
.active_volatile_cap =
643
cpu_to_le64(DEV_SIZE / 2 / CXL_CAPACITY_MULTIPLIER),
644
.active_persistent_cap =
645
cpu_to_le64(DEV_SIZE / 2 / CXL_CAPACITY_MULTIPLIER),
646
};
647
648
if (cmd->size_out < sizeof(pi))
649
return -EINVAL;
650
651
memcpy(cmd->payload_out, &pi, sizeof(pi));
652
653
return 0;
654
}
655
656
void cxl_mockmem_sanitize_work(struct work_struct *work)
657
{
658
struct cxl_memdev_state *mds =
659
container_of(work, typeof(*mds), security.poll_dwork.work);
660
struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox;
661
662
mutex_lock(&cxl_mbox->mbox_mutex);
663
if (mds->security.sanitize_node)
664
sysfs_notify_dirent(mds->security.sanitize_node);
665
mds->security.sanitize_active = false;
666
mutex_unlock(&cxl_mbox->mbox_mutex);
667
668
dev_dbg(mds->cxlds.dev, "sanitize complete\n");
669
}
670
671
static int mock_sanitize(struct cxl_mockmem_data *mdata,
672
struct cxl_mbox_cmd *cmd)
673
{
674
struct cxl_memdev_state *mds = mdata->mds;
675
struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox;
676
int rc = 0;
677
678
if (cmd->size_in != 0)
679
return -EINVAL;
680
681
if (cmd->size_out != 0)
682
return -EINVAL;
683
684
if (mdata->security_state & CXL_PMEM_SEC_STATE_USER_PASS_SET) {
685
cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
686
return -ENXIO;
687
}
688
if (mdata->security_state & CXL_PMEM_SEC_STATE_LOCKED) {
689
cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
690
return -ENXIO;
691
}
692
693
mutex_lock(&cxl_mbox->mbox_mutex);
694
if (schedule_delayed_work(&mds->security.poll_dwork,
695
msecs_to_jiffies(mdata->sanitize_timeout))) {
696
mds->security.sanitize_active = true;
697
dev_dbg(mds->cxlds.dev, "sanitize issued\n");
698
} else
699
rc = -EBUSY;
700
mutex_unlock(&cxl_mbox->mbox_mutex);
701
702
return rc;
703
}
704
705
static int mock_secure_erase(struct cxl_mockmem_data *mdata,
706
struct cxl_mbox_cmd *cmd)
707
{
708
if (cmd->size_in != 0)
709
return -EINVAL;
710
711
if (cmd->size_out != 0)
712
return -EINVAL;
713
714
if (mdata->security_state & CXL_PMEM_SEC_STATE_USER_PASS_SET) {
715
cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
716
return -ENXIO;
717
}
718
719
if (mdata->security_state & CXL_PMEM_SEC_STATE_LOCKED) {
720
cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
721
return -ENXIO;
722
}
723
724
return 0;
725
}
726
727
static int mock_get_security_state(struct cxl_mockmem_data *mdata,
728
struct cxl_mbox_cmd *cmd)
729
{
730
if (cmd->size_in)
731
return -EINVAL;
732
733
if (cmd->size_out != sizeof(u32))
734
return -EINVAL;
735
736
memcpy(cmd->payload_out, &mdata->security_state, sizeof(u32));
737
738
return 0;
739
}
740
741
static void master_plimit_check(struct cxl_mockmem_data *mdata)
742
{
743
if (mdata->master_limit == PASS_TRY_LIMIT)
744
return;
745
mdata->master_limit++;
746
if (mdata->master_limit == PASS_TRY_LIMIT)
747
mdata->security_state |= CXL_PMEM_SEC_STATE_MASTER_PLIMIT;
748
}
749
750
static void user_plimit_check(struct cxl_mockmem_data *mdata)
751
{
752
if (mdata->user_limit == PASS_TRY_LIMIT)
753
return;
754
mdata->user_limit++;
755
if (mdata->user_limit == PASS_TRY_LIMIT)
756
mdata->security_state |= CXL_PMEM_SEC_STATE_USER_PLIMIT;
757
}
758
759
static int mock_set_passphrase(struct cxl_mockmem_data *mdata,
760
struct cxl_mbox_cmd *cmd)
761
{
762
struct cxl_set_pass *set_pass;
763
764
if (cmd->size_in != sizeof(*set_pass))
765
return -EINVAL;
766
767
if (cmd->size_out != 0)
768
return -EINVAL;
769
770
if (mdata->security_state & CXL_PMEM_SEC_STATE_FROZEN) {
771
cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
772
return -ENXIO;
773
}
774
775
set_pass = cmd->payload_in;
776
switch (set_pass->type) {
777
case CXL_PMEM_SEC_PASS_MASTER:
778
if (mdata->security_state & CXL_PMEM_SEC_STATE_MASTER_PLIMIT) {
779
cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
780
return -ENXIO;
781
}
782
/*
783
* CXL spec rev3.0 8.2.9.8.6.2, The master pasphrase shall only be set in
784
* the security disabled state when the user passphrase is not set.
785
*/
786
if (mdata->security_state & CXL_PMEM_SEC_STATE_USER_PASS_SET) {
787
cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
788
return -ENXIO;
789
}
790
if (memcmp(mdata->master_pass, set_pass->old_pass, NVDIMM_PASSPHRASE_LEN)) {
791
master_plimit_check(mdata);
792
cmd->return_code = CXL_MBOX_CMD_RC_PASSPHRASE;
793
return -ENXIO;
794
}
795
memcpy(mdata->master_pass, set_pass->new_pass, NVDIMM_PASSPHRASE_LEN);
796
mdata->security_state |= CXL_PMEM_SEC_STATE_MASTER_PASS_SET;
797
return 0;
798
799
case CXL_PMEM_SEC_PASS_USER:
800
if (mdata->security_state & CXL_PMEM_SEC_STATE_USER_PLIMIT) {
801
cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
802
return -ENXIO;
803
}
804
if (memcmp(mdata->user_pass, set_pass->old_pass, NVDIMM_PASSPHRASE_LEN)) {
805
user_plimit_check(mdata);
806
cmd->return_code = CXL_MBOX_CMD_RC_PASSPHRASE;
807
return -ENXIO;
808
}
809
memcpy(mdata->user_pass, set_pass->new_pass, NVDIMM_PASSPHRASE_LEN);
810
mdata->security_state |= CXL_PMEM_SEC_STATE_USER_PASS_SET;
811
return 0;
812
813
default:
814
cmd->return_code = CXL_MBOX_CMD_RC_INPUT;
815
}
816
return -EINVAL;
817
}
818
819
static int mock_disable_passphrase(struct cxl_mockmem_data *mdata,
820
struct cxl_mbox_cmd *cmd)
821
{
822
struct cxl_disable_pass *dis_pass;
823
824
if (cmd->size_in != sizeof(*dis_pass))
825
return -EINVAL;
826
827
if (cmd->size_out != 0)
828
return -EINVAL;
829
830
if (mdata->security_state & CXL_PMEM_SEC_STATE_FROZEN) {
831
cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
832
return -ENXIO;
833
}
834
835
dis_pass = cmd->payload_in;
836
switch (dis_pass->type) {
837
case CXL_PMEM_SEC_PASS_MASTER:
838
if (mdata->security_state & CXL_PMEM_SEC_STATE_MASTER_PLIMIT) {
839
cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
840
return -ENXIO;
841
}
842
843
if (!(mdata->security_state & CXL_PMEM_SEC_STATE_MASTER_PASS_SET)) {
844
cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
845
return -ENXIO;
846
}
847
848
if (memcmp(dis_pass->pass, mdata->master_pass, NVDIMM_PASSPHRASE_LEN)) {
849
master_plimit_check(mdata);
850
cmd->return_code = CXL_MBOX_CMD_RC_PASSPHRASE;
851
return -ENXIO;
852
}
853
854
mdata->master_limit = 0;
855
memset(mdata->master_pass, 0, NVDIMM_PASSPHRASE_LEN);
856
mdata->security_state &= ~CXL_PMEM_SEC_STATE_MASTER_PASS_SET;
857
return 0;
858
859
case CXL_PMEM_SEC_PASS_USER:
860
if (mdata->security_state & CXL_PMEM_SEC_STATE_USER_PLIMIT) {
861
cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
862
return -ENXIO;
863
}
864
865
if (!(mdata->security_state & CXL_PMEM_SEC_STATE_USER_PASS_SET)) {
866
cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
867
return -ENXIO;
868
}
869
870
if (memcmp(dis_pass->pass, mdata->user_pass, NVDIMM_PASSPHRASE_LEN)) {
871
user_plimit_check(mdata);
872
cmd->return_code = CXL_MBOX_CMD_RC_PASSPHRASE;
873
return -ENXIO;
874
}
875
876
mdata->user_limit = 0;
877
memset(mdata->user_pass, 0, NVDIMM_PASSPHRASE_LEN);
878
mdata->security_state &= ~(CXL_PMEM_SEC_STATE_USER_PASS_SET |
879
CXL_PMEM_SEC_STATE_LOCKED);
880
return 0;
881
882
default:
883
cmd->return_code = CXL_MBOX_CMD_RC_INPUT;
884
return -EINVAL;
885
}
886
887
return 0;
888
}
889
890
static int mock_freeze_security(struct cxl_mockmem_data *mdata,
891
struct cxl_mbox_cmd *cmd)
892
{
893
if (cmd->size_in != 0)
894
return -EINVAL;
895
896
if (cmd->size_out != 0)
897
return -EINVAL;
898
899
if (mdata->security_state & CXL_PMEM_SEC_STATE_FROZEN)
900
return 0;
901
902
mdata->security_state |= CXL_PMEM_SEC_STATE_FROZEN;
903
return 0;
904
}
905
906
static int mock_unlock_security(struct cxl_mockmem_data *mdata,
907
struct cxl_mbox_cmd *cmd)
908
{
909
if (cmd->size_in != NVDIMM_PASSPHRASE_LEN)
910
return -EINVAL;
911
912
if (cmd->size_out != 0)
913
return -EINVAL;
914
915
if (mdata->security_state & CXL_PMEM_SEC_STATE_FROZEN) {
916
cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
917
return -ENXIO;
918
}
919
920
if (!(mdata->security_state & CXL_PMEM_SEC_STATE_USER_PASS_SET)) {
921
cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
922
return -ENXIO;
923
}
924
925
if (mdata->security_state & CXL_PMEM_SEC_STATE_USER_PLIMIT) {
926
cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
927
return -ENXIO;
928
}
929
930
if (!(mdata->security_state & CXL_PMEM_SEC_STATE_LOCKED)) {
931
cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
932
return -ENXIO;
933
}
934
935
if (memcmp(cmd->payload_in, mdata->user_pass, NVDIMM_PASSPHRASE_LEN)) {
936
if (++mdata->user_limit == PASS_TRY_LIMIT)
937
mdata->security_state |= CXL_PMEM_SEC_STATE_USER_PLIMIT;
938
cmd->return_code = CXL_MBOX_CMD_RC_PASSPHRASE;
939
return -ENXIO;
940
}
941
942
mdata->user_limit = 0;
943
mdata->security_state &= ~CXL_PMEM_SEC_STATE_LOCKED;
944
return 0;
945
}
946
947
static int mock_passphrase_secure_erase(struct cxl_mockmem_data *mdata,
948
struct cxl_mbox_cmd *cmd)
949
{
950
struct cxl_pass_erase *erase;
951
952
if (cmd->size_in != sizeof(*erase))
953
return -EINVAL;
954
955
if (cmd->size_out != 0)
956
return -EINVAL;
957
958
erase = cmd->payload_in;
959
if (mdata->security_state & CXL_PMEM_SEC_STATE_FROZEN) {
960
cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
961
return -ENXIO;
962
}
963
964
if (mdata->security_state & CXL_PMEM_SEC_STATE_USER_PLIMIT &&
965
erase->type == CXL_PMEM_SEC_PASS_USER) {
966
cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
967
return -ENXIO;
968
}
969
970
if (mdata->security_state & CXL_PMEM_SEC_STATE_MASTER_PLIMIT &&
971
erase->type == CXL_PMEM_SEC_PASS_MASTER) {
972
cmd->return_code = CXL_MBOX_CMD_RC_SECURITY;
973
return -ENXIO;
974
}
975
976
switch (erase->type) {
977
case CXL_PMEM_SEC_PASS_MASTER:
978
/*
979
* The spec does not clearly define the behavior of the scenario
980
* where a master passphrase is passed in while the master
981
* passphrase is not set and user passphrase is not set. The
982
* code will take the assumption that it will behave the same
983
* as a CXL secure erase command without passphrase (0x4401).
984
*/
985
if (mdata->security_state & CXL_PMEM_SEC_STATE_MASTER_PASS_SET) {
986
if (memcmp(mdata->master_pass, erase->pass,
987
NVDIMM_PASSPHRASE_LEN)) {
988
master_plimit_check(mdata);
989
cmd->return_code = CXL_MBOX_CMD_RC_PASSPHRASE;
990
return -ENXIO;
991
}
992
mdata->master_limit = 0;
993
mdata->user_limit = 0;
994
mdata->security_state &= ~CXL_PMEM_SEC_STATE_USER_PASS_SET;
995
memset(mdata->user_pass, 0, NVDIMM_PASSPHRASE_LEN);
996
mdata->security_state &= ~CXL_PMEM_SEC_STATE_LOCKED;
997
} else {
998
/*
999
* CXL rev3 8.2.9.8.6.3 Disable Passphrase
1000
* When master passphrase is disabled, the device shall
1001
* return Invalid Input for the Passphrase Secure Erase
1002
* command with master passphrase.
1003
*/
1004
return -EINVAL;
1005
}
1006
/* Scramble encryption keys so that data is effectively erased */
1007
break;
1008
case CXL_PMEM_SEC_PASS_USER:
1009
/*
1010
* The spec does not clearly define the behavior of the scenario
1011
* where a user passphrase is passed in while the user
1012
* passphrase is not set. The code will take the assumption that
1013
* it will behave the same as a CXL secure erase command without
1014
* passphrase (0x4401).
1015
*/
1016
if (mdata->security_state & CXL_PMEM_SEC_STATE_USER_PASS_SET) {
1017
if (memcmp(mdata->user_pass, erase->pass,
1018
NVDIMM_PASSPHRASE_LEN)) {
1019
user_plimit_check(mdata);
1020
cmd->return_code = CXL_MBOX_CMD_RC_PASSPHRASE;
1021
return -ENXIO;
1022
}
1023
mdata->user_limit = 0;
1024
mdata->security_state &= ~CXL_PMEM_SEC_STATE_USER_PASS_SET;
1025
memset(mdata->user_pass, 0, NVDIMM_PASSPHRASE_LEN);
1026
}
1027
1028
/*
1029
* CXL rev3 Table 8-118
1030
* If user passphrase is not set or supported by device, current
1031
* passphrase value is ignored. Will make the assumption that
1032
* the operation will proceed as secure erase w/o passphrase
1033
* since spec is not explicit.
1034
*/
1035
1036
/* Scramble encryption keys so that data is effectively erased */
1037
break;
1038
default:
1039
return -EINVAL;
1040
}
1041
1042
return 0;
1043
}
1044
1045
static int mock_get_lsa(struct cxl_mockmem_data *mdata,
1046
struct cxl_mbox_cmd *cmd)
1047
{
1048
struct cxl_mbox_get_lsa *get_lsa = cmd->payload_in;
1049
void *lsa = mdata->lsa;
1050
u32 offset, length;
1051
1052
if (sizeof(*get_lsa) > cmd->size_in)
1053
return -EINVAL;
1054
offset = le32_to_cpu(get_lsa->offset);
1055
length = le32_to_cpu(get_lsa->length);
1056
if (offset + length > LSA_SIZE)
1057
return -EINVAL;
1058
if (length > cmd->size_out)
1059
return -EINVAL;
1060
1061
memcpy(cmd->payload_out, lsa + offset, length);
1062
return 0;
1063
}
1064
1065
static int mock_set_lsa(struct cxl_mockmem_data *mdata,
1066
struct cxl_mbox_cmd *cmd)
1067
{
1068
struct cxl_mbox_set_lsa *set_lsa = cmd->payload_in;
1069
void *lsa = mdata->lsa;
1070
u32 offset, length;
1071
1072
if (sizeof(*set_lsa) > cmd->size_in)
1073
return -EINVAL;
1074
offset = le32_to_cpu(set_lsa->offset);
1075
length = cmd->size_in - sizeof(*set_lsa);
1076
if (offset + length > LSA_SIZE)
1077
return -EINVAL;
1078
1079
memcpy(lsa + offset, &set_lsa->data[0], length);
1080
return 0;
1081
}
1082
1083
static int mock_health_info(struct cxl_mbox_cmd *cmd)
1084
{
1085
struct cxl_mbox_health_info health_info = {
1086
/* set flags for maint needed, perf degraded, hw replacement */
1087
.health_status = 0x7,
1088
/* set media status to "All Data Lost" */
1089
.media_status = 0x3,
1090
/*
1091
* set ext_status flags for:
1092
* ext_life_used: normal,
1093
* ext_temperature: critical,
1094
* ext_corrected_volatile: warning,
1095
* ext_corrected_persistent: normal,
1096
*/
1097
.ext_status = 0x18,
1098
.life_used = 15,
1099
.temperature = cpu_to_le16(25),
1100
.dirty_shutdowns = cpu_to_le32(10),
1101
.volatile_errors = cpu_to_le32(20),
1102
.pmem_errors = cpu_to_le32(30),
1103
};
1104
1105
if (cmd->size_out < sizeof(health_info))
1106
return -EINVAL;
1107
1108
memcpy(cmd->payload_out, &health_info, sizeof(health_info));
1109
return 0;
1110
}
1111
1112
static int mock_set_shutdown_state(struct cxl_mockmem_data *mdata,
1113
struct cxl_mbox_cmd *cmd)
1114
{
1115
struct cxl_mbox_set_shutdown_state_in *ss = cmd->payload_in;
1116
1117
if (cmd->size_in != sizeof(*ss))
1118
return -EINVAL;
1119
1120
if (cmd->size_out != 0)
1121
return -EINVAL;
1122
1123
mdata->shutdown_state = ss->state;
1124
return 0;
1125
}
1126
1127
static struct mock_poison {
1128
struct cxl_dev_state *cxlds;
1129
u64 dpa;
1130
} mock_poison_list[MOCK_INJECT_TEST_MAX];
1131
1132
static struct cxl_mbox_poison_out *
1133
cxl_get_injected_po(struct cxl_dev_state *cxlds, u64 offset, u64 length)
1134
{
1135
struct cxl_mbox_poison_out *po;
1136
int nr_records = 0;
1137
u64 dpa;
1138
1139
po = kzalloc(struct_size(po, record, poison_inject_dev_max), GFP_KERNEL);
1140
if (!po)
1141
return NULL;
1142
1143
for (int i = 0; i < MOCK_INJECT_TEST_MAX; i++) {
1144
if (mock_poison_list[i].cxlds != cxlds)
1145
continue;
1146
if (mock_poison_list[i].dpa < offset ||
1147
mock_poison_list[i].dpa > offset + length - 1)
1148
continue;
1149
1150
dpa = mock_poison_list[i].dpa + CXL_POISON_SOURCE_INJECTED;
1151
po->record[nr_records].address = cpu_to_le64(dpa);
1152
po->record[nr_records].length = cpu_to_le32(1);
1153
nr_records++;
1154
if (nr_records == poison_inject_dev_max)
1155
break;
1156
}
1157
1158
/* Always return count, even when zero */
1159
po->count = cpu_to_le16(nr_records);
1160
1161
return po;
1162
}
1163
1164
static int mock_get_poison(struct cxl_dev_state *cxlds,
1165
struct cxl_mbox_cmd *cmd)
1166
{
1167
struct cxl_mbox_poison_in *pi = cmd->payload_in;
1168
struct cxl_mbox_poison_out *po;
1169
u64 offset = le64_to_cpu(pi->offset);
1170
u64 length = le64_to_cpu(pi->length);
1171
int nr_records;
1172
1173
po = cxl_get_injected_po(cxlds, offset, length);
1174
if (!po)
1175
return -ENOMEM;
1176
nr_records = le16_to_cpu(po->count);
1177
memcpy(cmd->payload_out, po, struct_size(po, record, nr_records));
1178
cmd->size_out = struct_size(po, record, nr_records);
1179
kfree(po);
1180
1181
return 0;
1182
}
1183
1184
static bool mock_poison_dev_max_injected(struct cxl_dev_state *cxlds)
1185
{
1186
int count = 0;
1187
1188
for (int i = 0; i < MOCK_INJECT_TEST_MAX; i++) {
1189
if (mock_poison_list[i].cxlds == cxlds)
1190
count++;
1191
}
1192
return (count >= poison_inject_dev_max);
1193
}
1194
1195
static int mock_poison_add(struct cxl_dev_state *cxlds, u64 dpa)
1196
{
1197
/* Return EBUSY to match the CXL driver handling */
1198
if (mock_poison_dev_max_injected(cxlds)) {
1199
dev_dbg(cxlds->dev,
1200
"Device poison injection limit has been reached: %d\n",
1201
poison_inject_dev_max);
1202
return -EBUSY;
1203
}
1204
1205
for (int i = 0; i < MOCK_INJECT_TEST_MAX; i++) {
1206
if (!mock_poison_list[i].cxlds) {
1207
mock_poison_list[i].cxlds = cxlds;
1208
mock_poison_list[i].dpa = dpa;
1209
return 0;
1210
}
1211
}
1212
dev_dbg(cxlds->dev,
1213
"Mock test poison injection limit has been reached: %d\n",
1214
MOCK_INJECT_TEST_MAX);
1215
1216
return -ENXIO;
1217
}
1218
1219
static bool mock_poison_found(struct cxl_dev_state *cxlds, u64 dpa)
1220
{
1221
for (int i = 0; i < MOCK_INJECT_TEST_MAX; i++) {
1222
if (mock_poison_list[i].cxlds == cxlds &&
1223
mock_poison_list[i].dpa == dpa)
1224
return true;
1225
}
1226
return false;
1227
}
1228
1229
static int mock_inject_poison(struct cxl_dev_state *cxlds,
1230
struct cxl_mbox_cmd *cmd)
1231
{
1232
struct cxl_mbox_inject_poison *pi = cmd->payload_in;
1233
u64 dpa = le64_to_cpu(pi->address);
1234
1235
if (mock_poison_found(cxlds, dpa)) {
1236
/* Not an error to inject poison if already poisoned */
1237
dev_dbg(cxlds->dev, "DPA: 0x%llx already poisoned\n", dpa);
1238
return 0;
1239
}
1240
1241
return mock_poison_add(cxlds, dpa);
1242
}
1243
1244
static bool mock_poison_del(struct cxl_dev_state *cxlds, u64 dpa)
1245
{
1246
for (int i = 0; i < MOCK_INJECT_TEST_MAX; i++) {
1247
if (mock_poison_list[i].cxlds == cxlds &&
1248
mock_poison_list[i].dpa == dpa) {
1249
mock_poison_list[i].cxlds = NULL;
1250
return true;
1251
}
1252
}
1253
return false;
1254
}
1255
1256
static int mock_clear_poison(struct cxl_dev_state *cxlds,
1257
struct cxl_mbox_cmd *cmd)
1258
{
1259
struct cxl_mbox_clear_poison *pi = cmd->payload_in;
1260
u64 dpa = le64_to_cpu(pi->address);
1261
1262
/*
1263
* A real CXL device will write pi->write_data to the address
1264
* being cleared. In this mock, just delete this address from
1265
* the mock poison list.
1266
*/
1267
if (!mock_poison_del(cxlds, dpa))
1268
dev_dbg(cxlds->dev, "DPA: 0x%llx not in poison list\n", dpa);
1269
1270
return 0;
1271
}
1272
1273
static bool mock_poison_list_empty(void)
1274
{
1275
for (int i = 0; i < MOCK_INJECT_TEST_MAX; i++) {
1276
if (mock_poison_list[i].cxlds)
1277
return false;
1278
}
1279
return true;
1280
}
1281
1282
static ssize_t poison_inject_max_show(struct device_driver *drv, char *buf)
1283
{
1284
return sysfs_emit(buf, "%u\n", poison_inject_dev_max);
1285
}
1286
1287
static ssize_t poison_inject_max_store(struct device_driver *drv,
1288
const char *buf, size_t len)
1289
{
1290
int val;
1291
1292
if (kstrtoint(buf, 0, &val) < 0)
1293
return -EINVAL;
1294
1295
if (!mock_poison_list_empty())
1296
return -EBUSY;
1297
1298
if (val <= MOCK_INJECT_TEST_MAX)
1299
poison_inject_dev_max = val;
1300
else
1301
return -EINVAL;
1302
1303
return len;
1304
}
1305
1306
static DRIVER_ATTR_RW(poison_inject_max);
1307
1308
static struct attribute *cxl_mock_mem_core_attrs[] = {
1309
&driver_attr_poison_inject_max.attr,
1310
NULL
1311
};
1312
ATTRIBUTE_GROUPS(cxl_mock_mem_core);
1313
1314
static int mock_fw_info(struct cxl_mockmem_data *mdata,
1315
struct cxl_mbox_cmd *cmd)
1316
{
1317
struct cxl_mbox_get_fw_info fw_info = {
1318
.num_slots = FW_SLOTS,
1319
.slot_info = (mdata->fw_slot & 0x7) |
1320
((mdata->fw_staged & 0x7) << 3),
1321
.activation_cap = 0,
1322
};
1323
1324
strcpy(fw_info.slot_1_revision, "cxl_test_fw_001");
1325
strcpy(fw_info.slot_2_revision, "cxl_test_fw_002");
1326
strcpy(fw_info.slot_3_revision, "cxl_test_fw_003");
1327
strcpy(fw_info.slot_4_revision, "");
1328
1329
if (cmd->size_out < sizeof(fw_info))
1330
return -EINVAL;
1331
1332
memcpy(cmd->payload_out, &fw_info, sizeof(fw_info));
1333
return 0;
1334
}
1335
1336
static int mock_transfer_fw(struct cxl_mockmem_data *mdata,
1337
struct cxl_mbox_cmd *cmd)
1338
{
1339
struct cxl_mbox_transfer_fw *transfer = cmd->payload_in;
1340
void *fw = mdata->fw;
1341
size_t offset, length;
1342
1343
offset = le32_to_cpu(transfer->offset) * CXL_FW_TRANSFER_ALIGNMENT;
1344
length = cmd->size_in - sizeof(*transfer);
1345
if (offset + length > FW_SIZE)
1346
return -EINVAL;
1347
1348
switch (transfer->action) {
1349
case CXL_FW_TRANSFER_ACTION_FULL:
1350
if (offset != 0)
1351
return -EINVAL;
1352
fallthrough;
1353
case CXL_FW_TRANSFER_ACTION_END:
1354
if (transfer->slot == 0 || transfer->slot > FW_SLOTS)
1355
return -EINVAL;
1356
mdata->fw_size = offset + length;
1357
break;
1358
case CXL_FW_TRANSFER_ACTION_INITIATE:
1359
case CXL_FW_TRANSFER_ACTION_CONTINUE:
1360
break;
1361
case CXL_FW_TRANSFER_ACTION_ABORT:
1362
return 0;
1363
default:
1364
return -EINVAL;
1365
}
1366
1367
memcpy(fw + offset, transfer->data, length);
1368
usleep_range(1500, 2000);
1369
return 0;
1370
}
1371
1372
static int mock_activate_fw(struct cxl_mockmem_data *mdata,
1373
struct cxl_mbox_cmd *cmd)
1374
{
1375
struct cxl_mbox_activate_fw *activate = cmd->payload_in;
1376
1377
if (activate->slot == 0 || activate->slot > FW_SLOTS)
1378
return -EINVAL;
1379
1380
switch (activate->action) {
1381
case CXL_FW_ACTIVATE_ONLINE:
1382
mdata->fw_slot = activate->slot;
1383
mdata->fw_staged = 0;
1384
return 0;
1385
case CXL_FW_ACTIVATE_OFFLINE:
1386
mdata->fw_staged = activate->slot;
1387
return 0;
1388
}
1389
1390
return -EINVAL;
1391
}
1392
1393
#define CXL_VENDOR_FEATURE_TEST \
1394
UUID_INIT(0xffffffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, 0xff, 0xff, \
1395
0xff, 0xff, 0xff)
1396
1397
static void fill_feature_vendor_test(struct cxl_feat_entry *feat)
1398
{
1399
feat->uuid = CXL_VENDOR_FEATURE_TEST;
1400
feat->id = 0;
1401
feat->get_feat_size = cpu_to_le16(0x4);
1402
feat->set_feat_size = cpu_to_le16(0x4);
1403
feat->flags = cpu_to_le32(CXL_FEATURE_F_CHANGEABLE |
1404
CXL_FEATURE_F_DEFAULT_SEL |
1405
CXL_FEATURE_F_SAVED_SEL);
1406
feat->get_feat_ver = 1;
1407
feat->set_feat_ver = 1;
1408
feat->effects = cpu_to_le16(CXL_CMD_CONFIG_CHANGE_COLD_RESET |
1409
CXL_CMD_EFFECTS_VALID);
1410
}
1411
1412
#define MAX_CXL_TEST_FEATS 1
1413
1414
static int mock_get_test_feature(struct cxl_mockmem_data *mdata,
1415
struct cxl_mbox_cmd *cmd)
1416
{
1417
struct vendor_test_feat *output = cmd->payload_out;
1418
struct cxl_mbox_get_feat_in *input = cmd->payload_in;
1419
u16 offset = le16_to_cpu(input->offset);
1420
u16 count = le16_to_cpu(input->count);
1421
u8 *ptr;
1422
1423
if (offset > sizeof(*output)) {
1424
cmd->return_code = CXL_MBOX_CMD_RC_INPUT;
1425
return -EINVAL;
1426
}
1427
1428
if (offset + count > sizeof(*output)) {
1429
cmd->return_code = CXL_MBOX_CMD_RC_INPUT;
1430
return -EINVAL;
1431
}
1432
1433
ptr = (u8 *)&mdata->test_feat + offset;
1434
memcpy((u8 *)output + offset, ptr, count);
1435
1436
return 0;
1437
}
1438
1439
static int mock_get_feature(struct cxl_mockmem_data *mdata,
1440
struct cxl_mbox_cmd *cmd)
1441
{
1442
struct cxl_mbox_get_feat_in *input = cmd->payload_in;
1443
1444
if (uuid_equal(&input->uuid, &CXL_VENDOR_FEATURE_TEST))
1445
return mock_get_test_feature(mdata, cmd);
1446
1447
cmd->return_code = CXL_MBOX_CMD_RC_UNSUPPORTED;
1448
1449
return -EOPNOTSUPP;
1450
}
1451
1452
static int mock_set_test_feature(struct cxl_mockmem_data *mdata,
1453
struct cxl_mbox_cmd *cmd)
1454
{
1455
struct cxl_mbox_set_feat_in *input = cmd->payload_in;
1456
struct vendor_test_feat *test =
1457
(struct vendor_test_feat *)input->feat_data;
1458
u32 action;
1459
1460
action = FIELD_GET(CXL_SET_FEAT_FLAG_DATA_TRANSFER_MASK,
1461
le32_to_cpu(input->hdr.flags));
1462
/*
1463
* While it is spec compliant to support other set actions, it is not
1464
* necessary to add the complication in the emulation currently. Reject
1465
* anything besides full xfer.
1466
*/
1467
if (action != CXL_SET_FEAT_FLAG_FULL_DATA_TRANSFER) {
1468
cmd->return_code = CXL_MBOX_CMD_RC_INPUT;
1469
return -EINVAL;
1470
}
1471
1472
/* Offset should be reserved when doing full transfer */
1473
if (input->hdr.offset) {
1474
cmd->return_code = CXL_MBOX_CMD_RC_INPUT;
1475
return -EINVAL;
1476
}
1477
1478
memcpy(&mdata->test_feat.data, &test->data, sizeof(u32));
1479
1480
return 0;
1481
}
1482
1483
static int mock_set_feature(struct cxl_mockmem_data *mdata,
1484
struct cxl_mbox_cmd *cmd)
1485
{
1486
struct cxl_mbox_set_feat_in *input = cmd->payload_in;
1487
1488
if (uuid_equal(&input->hdr.uuid, &CXL_VENDOR_FEATURE_TEST))
1489
return mock_set_test_feature(mdata, cmd);
1490
1491
cmd->return_code = CXL_MBOX_CMD_RC_UNSUPPORTED;
1492
1493
return -EOPNOTSUPP;
1494
}
1495
1496
static int mock_get_supported_features(struct cxl_mockmem_data *mdata,
1497
struct cxl_mbox_cmd *cmd)
1498
{
1499
struct cxl_mbox_get_sup_feats_in *in = cmd->payload_in;
1500
struct cxl_mbox_get_sup_feats_out *out = cmd->payload_out;
1501
struct cxl_feat_entry *feat;
1502
u16 start_idx, count;
1503
1504
if (cmd->size_out < sizeof(*out)) {
1505
cmd->return_code = CXL_MBOX_CMD_RC_PAYLOADLEN;
1506
return -EINVAL;
1507
}
1508
1509
/*
1510
* Current emulation only supports 1 feature
1511
*/
1512
start_idx = le16_to_cpu(in->start_idx);
1513
if (start_idx != 0) {
1514
cmd->return_code = CXL_MBOX_CMD_RC_INPUT;
1515
return -EINVAL;
1516
}
1517
1518
count = le16_to_cpu(in->count);
1519
if (count < struct_size(out, ents, 0)) {
1520
cmd->return_code = CXL_MBOX_CMD_RC_PAYLOADLEN;
1521
return -EINVAL;
1522
}
1523
1524
out->supported_feats = cpu_to_le16(MAX_CXL_TEST_FEATS);
1525
cmd->return_code = 0;
1526
if (count < struct_size(out, ents, MAX_CXL_TEST_FEATS)) {
1527
out->num_entries = 0;
1528
return 0;
1529
}
1530
1531
out->num_entries = cpu_to_le16(MAX_CXL_TEST_FEATS);
1532
feat = out->ents;
1533
fill_feature_vendor_test(feat);
1534
1535
return 0;
1536
}
1537
1538
static int cxl_mock_mbox_send(struct cxl_mailbox *cxl_mbox,
1539
struct cxl_mbox_cmd *cmd)
1540
{
1541
struct device *dev = cxl_mbox->host;
1542
struct cxl_mockmem_data *mdata = dev_get_drvdata(dev);
1543
struct cxl_memdev_state *mds = mdata->mds;
1544
struct cxl_dev_state *cxlds = &mds->cxlds;
1545
int rc = -EIO;
1546
1547
switch (cmd->opcode) {
1548
case CXL_MBOX_OP_SET_TIMESTAMP:
1549
rc = mock_set_timestamp(cxlds, cmd);
1550
break;
1551
case CXL_MBOX_OP_GET_SUPPORTED_LOGS:
1552
rc = mock_gsl(cmd);
1553
break;
1554
case CXL_MBOX_OP_GET_LOG:
1555
rc = mock_get_log(mds, cmd);
1556
break;
1557
case CXL_MBOX_OP_IDENTIFY:
1558
if (cxlds->rcd)
1559
rc = mock_rcd_id(cmd);
1560
else
1561
rc = mock_id(cmd);
1562
break;
1563
case CXL_MBOX_OP_GET_LSA:
1564
rc = mock_get_lsa(mdata, cmd);
1565
break;
1566
case CXL_MBOX_OP_GET_PARTITION_INFO:
1567
rc = mock_partition_info(cmd);
1568
break;
1569
case CXL_MBOX_OP_GET_EVENT_RECORD:
1570
rc = mock_get_event(dev, cmd);
1571
break;
1572
case CXL_MBOX_OP_CLEAR_EVENT_RECORD:
1573
rc = mock_clear_event(dev, cmd);
1574
break;
1575
case CXL_MBOX_OP_SET_LSA:
1576
rc = mock_set_lsa(mdata, cmd);
1577
break;
1578
case CXL_MBOX_OP_GET_HEALTH_INFO:
1579
rc = mock_health_info(cmd);
1580
break;
1581
case CXL_MBOX_OP_SANITIZE:
1582
rc = mock_sanitize(mdata, cmd);
1583
break;
1584
case CXL_MBOX_OP_SECURE_ERASE:
1585
rc = mock_secure_erase(mdata, cmd);
1586
break;
1587
case CXL_MBOX_OP_GET_SECURITY_STATE:
1588
rc = mock_get_security_state(mdata, cmd);
1589
break;
1590
case CXL_MBOX_OP_SET_PASSPHRASE:
1591
rc = mock_set_passphrase(mdata, cmd);
1592
break;
1593
case CXL_MBOX_OP_DISABLE_PASSPHRASE:
1594
rc = mock_disable_passphrase(mdata, cmd);
1595
break;
1596
case CXL_MBOX_OP_FREEZE_SECURITY:
1597
rc = mock_freeze_security(mdata, cmd);
1598
break;
1599
case CXL_MBOX_OP_UNLOCK:
1600
rc = mock_unlock_security(mdata, cmd);
1601
break;
1602
case CXL_MBOX_OP_PASSPHRASE_SECURE_ERASE:
1603
rc = mock_passphrase_secure_erase(mdata, cmd);
1604
break;
1605
case CXL_MBOX_OP_SET_SHUTDOWN_STATE:
1606
rc = mock_set_shutdown_state(mdata, cmd);
1607
break;
1608
case CXL_MBOX_OP_GET_POISON:
1609
rc = mock_get_poison(cxlds, cmd);
1610
break;
1611
case CXL_MBOX_OP_INJECT_POISON:
1612
rc = mock_inject_poison(cxlds, cmd);
1613
break;
1614
case CXL_MBOX_OP_CLEAR_POISON:
1615
rc = mock_clear_poison(cxlds, cmd);
1616
break;
1617
case CXL_MBOX_OP_GET_FW_INFO:
1618
rc = mock_fw_info(mdata, cmd);
1619
break;
1620
case CXL_MBOX_OP_TRANSFER_FW:
1621
rc = mock_transfer_fw(mdata, cmd);
1622
break;
1623
case CXL_MBOX_OP_ACTIVATE_FW:
1624
rc = mock_activate_fw(mdata, cmd);
1625
break;
1626
case CXL_MBOX_OP_GET_SUPPORTED_FEATURES:
1627
rc = mock_get_supported_features(mdata, cmd);
1628
break;
1629
case CXL_MBOX_OP_GET_FEATURE:
1630
rc = mock_get_feature(mdata, cmd);
1631
break;
1632
case CXL_MBOX_OP_SET_FEATURE:
1633
rc = mock_set_feature(mdata, cmd);
1634
break;
1635
default:
1636
break;
1637
}
1638
1639
dev_dbg(dev, "opcode: %#x sz_in: %zd sz_out: %zd rc: %d\n", cmd->opcode,
1640
cmd->size_in, cmd->size_out, rc);
1641
1642
return rc;
1643
}
1644
1645
static void label_area_release(void *lsa)
1646
{
1647
vfree(lsa);
1648
}
1649
1650
static void fw_buf_release(void *buf)
1651
{
1652
vfree(buf);
1653
}
1654
1655
static bool is_rcd(struct platform_device *pdev)
1656
{
1657
const struct platform_device_id *id = platform_get_device_id(pdev);
1658
1659
return !!id->driver_data;
1660
}
1661
1662
static ssize_t event_trigger_store(struct device *dev,
1663
struct device_attribute *attr,
1664
const char *buf, size_t count)
1665
{
1666
cxl_mock_event_trigger(dev);
1667
return count;
1668
}
1669
static DEVICE_ATTR_WO(event_trigger);
1670
1671
static int cxl_mock_mailbox_create(struct cxl_dev_state *cxlds)
1672
{
1673
int rc;
1674
1675
rc = cxl_mailbox_init(&cxlds->cxl_mbox, cxlds->dev);
1676
if (rc)
1677
return rc;
1678
1679
return 0;
1680
}
1681
1682
static void cxl_mock_test_feat_init(struct cxl_mockmem_data *mdata)
1683
{
1684
mdata->test_feat.data = cpu_to_le32(0xdeadbeef);
1685
}
1686
1687
static int cxl_mock_mem_probe(struct platform_device *pdev)
1688
{
1689
struct device *dev = &pdev->dev;
1690
struct cxl_memdev *cxlmd;
1691
struct cxl_memdev_state *mds;
1692
struct cxl_dev_state *cxlds;
1693
struct cxl_mockmem_data *mdata;
1694
struct cxl_mailbox *cxl_mbox;
1695
struct cxl_dpa_info range_info = { 0 };
1696
int rc;
1697
1698
mdata = devm_kzalloc(dev, sizeof(*mdata), GFP_KERNEL);
1699
if (!mdata)
1700
return -ENOMEM;
1701
dev_set_drvdata(dev, mdata);
1702
1703
mdata->lsa = vmalloc(LSA_SIZE);
1704
if (!mdata->lsa)
1705
return -ENOMEM;
1706
mdata->fw = vmalloc(FW_SIZE);
1707
if (!mdata->fw)
1708
return -ENOMEM;
1709
mdata->fw_slot = 2;
1710
1711
rc = devm_add_action_or_reset(dev, label_area_release, mdata->lsa);
1712
if (rc)
1713
return rc;
1714
1715
rc = devm_add_action_or_reset(dev, fw_buf_release, mdata->fw);
1716
if (rc)
1717
return rc;
1718
1719
mds = cxl_memdev_state_create(dev);
1720
if (IS_ERR(mds))
1721
return PTR_ERR(mds);
1722
1723
cxlds = &mds->cxlds;
1724
rc = cxl_mock_mailbox_create(cxlds);
1725
if (rc)
1726
return rc;
1727
1728
cxl_mbox = &mds->cxlds.cxl_mbox;
1729
mdata->mds = mds;
1730
cxl_mbox->mbox_send = cxl_mock_mbox_send;
1731
cxl_mbox->payload_size = SZ_4K;
1732
mds->event.buf = (struct cxl_get_event_payload *) mdata->event_buf;
1733
INIT_DELAYED_WORK(&mds->security.poll_dwork, cxl_mockmem_sanitize_work);
1734
1735
cxlds->serial = pdev->id + 1;
1736
if (is_rcd(pdev))
1737
cxlds->rcd = true;
1738
1739
rc = cxl_enumerate_cmds(mds);
1740
if (rc)
1741
return rc;
1742
1743
rc = cxl_poison_state_init(mds);
1744
if (rc)
1745
return rc;
1746
1747
rc = cxl_set_timestamp(mds);
1748
if (rc)
1749
return rc;
1750
1751
cxlds->media_ready = true;
1752
rc = cxl_dev_state_identify(mds);
1753
if (rc)
1754
return rc;
1755
1756
rc = cxl_mem_dpa_fetch(mds, &range_info);
1757
if (rc)
1758
return rc;
1759
1760
rc = cxl_dpa_setup(cxlds, &range_info);
1761
if (rc)
1762
return rc;
1763
1764
rc = devm_cxl_setup_features(cxlds);
1765
if (rc)
1766
dev_dbg(dev, "No CXL Features discovered\n");
1767
1768
cxl_mock_add_event_logs(&mdata->mes);
1769
1770
cxlmd = devm_cxl_add_memdev(&pdev->dev, cxlds);
1771
if (IS_ERR(cxlmd))
1772
return PTR_ERR(cxlmd);
1773
1774
rc = devm_cxl_setup_fw_upload(&pdev->dev, mds);
1775
if (rc)
1776
return rc;
1777
1778
rc = devm_cxl_sanitize_setup_notifier(&pdev->dev, cxlmd);
1779
if (rc)
1780
return rc;
1781
1782
rc = devm_cxl_setup_fwctl(&pdev->dev, cxlmd);
1783
if (rc)
1784
dev_dbg(dev, "No CXL FWCTL setup\n");
1785
1786
cxl_mem_get_event_records(mds, CXLDEV_EVENT_STATUS_ALL);
1787
cxl_mock_test_feat_init(mdata);
1788
1789
return 0;
1790
}
1791
1792
static ssize_t security_lock_show(struct device *dev,
1793
struct device_attribute *attr, char *buf)
1794
{
1795
struct cxl_mockmem_data *mdata = dev_get_drvdata(dev);
1796
1797
return sysfs_emit(buf, "%u\n",
1798
!!(mdata->security_state & CXL_PMEM_SEC_STATE_LOCKED));
1799
}
1800
1801
static ssize_t security_lock_store(struct device *dev, struct device_attribute *attr,
1802
const char *buf, size_t count)
1803
{
1804
struct cxl_mockmem_data *mdata = dev_get_drvdata(dev);
1805
u32 mask = CXL_PMEM_SEC_STATE_FROZEN | CXL_PMEM_SEC_STATE_USER_PLIMIT |
1806
CXL_PMEM_SEC_STATE_MASTER_PLIMIT;
1807
int val;
1808
1809
if (kstrtoint(buf, 0, &val) < 0)
1810
return -EINVAL;
1811
1812
if (val == 1) {
1813
if (!(mdata->security_state & CXL_PMEM_SEC_STATE_USER_PASS_SET))
1814
return -ENXIO;
1815
mdata->security_state |= CXL_PMEM_SEC_STATE_LOCKED;
1816
mdata->security_state &= ~mask;
1817
} else {
1818
return -EINVAL;
1819
}
1820
return count;
1821
}
1822
1823
static DEVICE_ATTR_RW(security_lock);
1824
1825
static ssize_t fw_buf_checksum_show(struct device *dev,
1826
struct device_attribute *attr, char *buf)
1827
{
1828
struct cxl_mockmem_data *mdata = dev_get_drvdata(dev);
1829
u8 hash[SHA256_DIGEST_SIZE];
1830
1831
sha256(mdata->fw, mdata->fw_size, hash);
1832
1833
return sysfs_emit(buf, "%*phN\n", SHA256_DIGEST_SIZE, hash);
1834
}
1835
1836
static DEVICE_ATTR_RO(fw_buf_checksum);
1837
1838
static ssize_t sanitize_timeout_show(struct device *dev,
1839
struct device_attribute *attr, char *buf)
1840
{
1841
struct cxl_mockmem_data *mdata = dev_get_drvdata(dev);
1842
1843
return sysfs_emit(buf, "%lu\n", mdata->sanitize_timeout);
1844
}
1845
1846
static ssize_t sanitize_timeout_store(struct device *dev,
1847
struct device_attribute *attr,
1848
const char *buf, size_t count)
1849
{
1850
struct cxl_mockmem_data *mdata = dev_get_drvdata(dev);
1851
unsigned long val;
1852
int rc;
1853
1854
rc = kstrtoul(buf, 0, &val);
1855
if (rc)
1856
return rc;
1857
1858
mdata->sanitize_timeout = val;
1859
1860
return count;
1861
}
1862
1863
static DEVICE_ATTR_RW(sanitize_timeout);
1864
1865
static struct attribute *cxl_mock_mem_attrs[] = {
1866
&dev_attr_security_lock.attr,
1867
&dev_attr_event_trigger.attr,
1868
&dev_attr_fw_buf_checksum.attr,
1869
&dev_attr_sanitize_timeout.attr,
1870
NULL
1871
};
1872
ATTRIBUTE_GROUPS(cxl_mock_mem);
1873
1874
static const struct platform_device_id cxl_mock_mem_ids[] = {
1875
{ .name = "cxl_mem", 0 },
1876
{ .name = "cxl_rcd", 1 },
1877
{ },
1878
};
1879
MODULE_DEVICE_TABLE(platform, cxl_mock_mem_ids);
1880
1881
static struct platform_driver cxl_mock_mem_driver = {
1882
.probe = cxl_mock_mem_probe,
1883
.id_table = cxl_mock_mem_ids,
1884
.driver = {
1885
.name = KBUILD_MODNAME,
1886
.dev_groups = cxl_mock_mem_groups,
1887
.groups = cxl_mock_mem_core_groups,
1888
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
1889
},
1890
};
1891
1892
module_platform_driver(cxl_mock_mem_driver);
1893
MODULE_LICENSE("GPL v2");
1894
MODULE_DESCRIPTION("cxl_test: mem device mock module");
1895
MODULE_IMPORT_NS("CXL");
1896
1897