tools/testing/cxl: Add "Get Security State" opcode support
[platform/kernel/linux-rpi.git] / tools / testing / cxl / test / mem.c
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 struct cxl_mockmem_data {
69         void *lsa;
70         u32 security_state;
71 };
72
73 static int mock_gsl(struct cxl_mbox_cmd *cmd)
74 {
75         if (cmd->size_out < sizeof(mock_gsl_payload))
76                 return -EINVAL;
77
78         memcpy(cmd->payload_out, &mock_gsl_payload, sizeof(mock_gsl_payload));
79         cmd->size_out = sizeof(mock_gsl_payload);
80
81         return 0;
82 }
83
84 static int mock_get_log(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
85 {
86         struct cxl_mbox_get_log *gl = cmd->payload_in;
87         u32 offset = le32_to_cpu(gl->offset);
88         u32 length = le32_to_cpu(gl->length);
89         uuid_t uuid = DEFINE_CXL_CEL_UUID;
90         void *data = &mock_cel;
91
92         if (cmd->size_in < sizeof(*gl))
93                 return -EINVAL;
94         if (length > cxlds->payload_size)
95                 return -EINVAL;
96         if (offset + length > sizeof(mock_cel))
97                 return -EINVAL;
98         if (!uuid_equal(&gl->uuid, &uuid))
99                 return -EINVAL;
100         if (length > cmd->size_out)
101                 return -EINVAL;
102
103         memcpy(cmd->payload_out, data + offset, length);
104
105         return 0;
106 }
107
108 static int mock_id(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
109 {
110         struct cxl_mbox_identify id = {
111                 .fw_revision = { "mock fw v1 " },
112                 .lsa_size = cpu_to_le32(LSA_SIZE),
113                 .partition_align =
114                         cpu_to_le64(SZ_256M / CXL_CAPACITY_MULTIPLIER),
115                 .total_capacity =
116                         cpu_to_le64(DEV_SIZE / CXL_CAPACITY_MULTIPLIER),
117         };
118
119         if (cmd->size_out < sizeof(id))
120                 return -EINVAL;
121
122         memcpy(cmd->payload_out, &id, sizeof(id));
123
124         return 0;
125 }
126
127 static int mock_partition_info(struct cxl_dev_state *cxlds,
128                                struct cxl_mbox_cmd *cmd)
129 {
130         struct cxl_mbox_get_partition_info pi = {
131                 .active_volatile_cap =
132                         cpu_to_le64(DEV_SIZE / 2 / CXL_CAPACITY_MULTIPLIER),
133                 .active_persistent_cap =
134                         cpu_to_le64(DEV_SIZE / 2 / CXL_CAPACITY_MULTIPLIER),
135         };
136
137         if (cmd->size_out < sizeof(pi))
138                 return -EINVAL;
139
140         memcpy(cmd->payload_out, &pi, sizeof(pi));
141
142         return 0;
143 }
144
145 static int mock_get_security_state(struct cxl_dev_state *cxlds,
146                                    struct cxl_mbox_cmd *cmd)
147 {
148         struct cxl_mockmem_data *mdata = dev_get_drvdata(cxlds->dev);
149
150         if (cmd->size_in)
151                 return -EINVAL;
152
153         if (cmd->size_out != sizeof(u32))
154                 return -EINVAL;
155
156         memcpy(cmd->payload_out, &mdata->security_state, sizeof(u32));
157
158         return 0;
159 }
160
161 static int mock_get_lsa(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
162 {
163         struct cxl_mbox_get_lsa *get_lsa = cmd->payload_in;
164         struct cxl_mockmem_data *mdata = dev_get_drvdata(cxlds->dev);
165         void *lsa = mdata->lsa;
166         u32 offset, length;
167
168         if (sizeof(*get_lsa) > cmd->size_in)
169                 return -EINVAL;
170         offset = le32_to_cpu(get_lsa->offset);
171         length = le32_to_cpu(get_lsa->length);
172         if (offset + length > LSA_SIZE)
173                 return -EINVAL;
174         if (length > cmd->size_out)
175                 return -EINVAL;
176
177         memcpy(cmd->payload_out, lsa + offset, length);
178         return 0;
179 }
180
181 static int mock_set_lsa(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
182 {
183         struct cxl_mbox_set_lsa *set_lsa = cmd->payload_in;
184         struct cxl_mockmem_data *mdata = dev_get_drvdata(cxlds->dev);
185         void *lsa = mdata->lsa;
186         u32 offset, length;
187
188         if (sizeof(*set_lsa) > cmd->size_in)
189                 return -EINVAL;
190         offset = le32_to_cpu(set_lsa->offset);
191         length = cmd->size_in - sizeof(*set_lsa);
192         if (offset + length > LSA_SIZE)
193                 return -EINVAL;
194
195         memcpy(lsa + offset, &set_lsa->data[0], length);
196         return 0;
197 }
198
199 static int mock_health_info(struct cxl_dev_state *cxlds,
200                             struct cxl_mbox_cmd *cmd)
201 {
202         struct cxl_mbox_health_info health_info = {
203                 /* set flags for maint needed, perf degraded, hw replacement */
204                 .health_status = 0x7,
205                 /* set media status to "All Data Lost" */
206                 .media_status = 0x3,
207                 /*
208                  * set ext_status flags for:
209                  *  ext_life_used: normal,
210                  *  ext_temperature: critical,
211                  *  ext_corrected_volatile: warning,
212                  *  ext_corrected_persistent: normal,
213                  */
214                 .ext_status = 0x18,
215                 .life_used = 15,
216                 .temperature = cpu_to_le16(25),
217                 .dirty_shutdowns = cpu_to_le32(10),
218                 .volatile_errors = cpu_to_le32(20),
219                 .pmem_errors = cpu_to_le32(30),
220         };
221
222         if (cmd->size_out < sizeof(health_info))
223                 return -EINVAL;
224
225         memcpy(cmd->payload_out, &health_info, sizeof(health_info));
226         return 0;
227 }
228
229 static int cxl_mock_mbox_send(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
230 {
231         struct device *dev = cxlds->dev;
232         int rc = -EIO;
233
234         switch (cmd->opcode) {
235         case CXL_MBOX_OP_GET_SUPPORTED_LOGS:
236                 rc = mock_gsl(cmd);
237                 break;
238         case CXL_MBOX_OP_GET_LOG:
239                 rc = mock_get_log(cxlds, cmd);
240                 break;
241         case CXL_MBOX_OP_IDENTIFY:
242                 rc = mock_id(cxlds, cmd);
243                 break;
244         case CXL_MBOX_OP_GET_LSA:
245                 rc = mock_get_lsa(cxlds, cmd);
246                 break;
247         case CXL_MBOX_OP_GET_PARTITION_INFO:
248                 rc = mock_partition_info(cxlds, cmd);
249                 break;
250         case CXL_MBOX_OP_SET_LSA:
251                 rc = mock_set_lsa(cxlds, cmd);
252                 break;
253         case CXL_MBOX_OP_GET_HEALTH_INFO:
254                 rc = mock_health_info(cxlds, cmd);
255                 break;
256         case CXL_MBOX_OP_GET_SECURITY_STATE:
257                 rc = mock_get_security_state(cxlds, cmd);
258                 break;
259         default:
260                 break;
261         }
262
263         dev_dbg(dev, "opcode: %#x sz_in: %zd sz_out: %zd rc: %d\n", cmd->opcode,
264                 cmd->size_in, cmd->size_out, rc);
265
266         return rc;
267 }
268
269 static void label_area_release(void *lsa)
270 {
271         vfree(lsa);
272 }
273
274 static int cxl_mock_mem_probe(struct platform_device *pdev)
275 {
276         struct device *dev = &pdev->dev;
277         struct cxl_memdev *cxlmd;
278         struct cxl_dev_state *cxlds;
279         struct cxl_mockmem_data *mdata;
280         int rc;
281
282         mdata = devm_kzalloc(dev, sizeof(*mdata), GFP_KERNEL);
283         if (!mdata)
284                 return -ENOMEM;
285         dev_set_drvdata(dev, mdata);
286
287         mdata->lsa = vmalloc(LSA_SIZE);
288         if (!mdata->lsa)
289                 return -ENOMEM;
290         rc = devm_add_action_or_reset(dev, label_area_release, mdata->lsa);
291         if (rc)
292                 return rc;
293
294         cxlds = cxl_dev_state_create(dev);
295         if (IS_ERR(cxlds))
296                 return PTR_ERR(cxlds);
297
298         cxlds->serial = pdev->id;
299         cxlds->mbox_send = cxl_mock_mbox_send;
300         cxlds->payload_size = SZ_4K;
301
302         rc = cxl_enumerate_cmds(cxlds);
303         if (rc)
304                 return rc;
305
306         rc = cxl_dev_state_identify(cxlds);
307         if (rc)
308                 return rc;
309
310         rc = cxl_mem_create_range_info(cxlds);
311         if (rc)
312                 return rc;
313
314         cxlmd = devm_cxl_add_memdev(cxlds);
315         if (IS_ERR(cxlmd))
316                 return PTR_ERR(cxlmd);
317
318         if (resource_size(&cxlds->pmem_res) && IS_ENABLED(CONFIG_CXL_PMEM))
319                 rc = devm_cxl_add_nvdimm(dev, cxlmd);
320
321         return 0;
322 }
323
324 static const struct platform_device_id cxl_mock_mem_ids[] = {
325         { .name = "cxl_mem", },
326         { },
327 };
328 MODULE_DEVICE_TABLE(platform, cxl_mock_mem_ids);
329
330 static struct platform_driver cxl_mock_mem_driver = {
331         .probe = cxl_mock_mem_probe,
332         .id_table = cxl_mock_mem_ids,
333         .driver = {
334                 .name = KBUILD_MODNAME,
335         },
336 };
337
338 module_platform_driver(cxl_mock_mem_driver);
339 MODULE_LICENSE("GPL v2");
340 MODULE_IMPORT_NS(CXL);