1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Management Complex (MC) userspace support
4 *
5 * Copyright 2021 NXP
6 *
7 */
8
9 #include <linux/slab.h>
10 #include <linux/fs.h>
11 #include <linux/uaccess.h>
12 #include <linux/miscdevice.h>
13
14 #include "fsl-mc-private.h"
15
16 struct uapi_priv_data {
17 struct fsl_mc_uapi *uapi;
18 struct fsl_mc_io *mc_io;
19 };
20
21 struct fsl_mc_cmd_desc {
22 u16 cmdid_value;
23 u16 cmdid_mask;
24 int size;
25 bool token;
26 int flags;
27 };
28
29 #define FSL_MC_CHECK_MODULE_ID BIT(0)
30 #define FSL_MC_CAP_NET_ADMIN_NEEDED BIT(1)
31
32 enum fsl_mc_cmd_index {
33 DPDBG_DUMP = 0,
34 DPDBG_SET,
35 DPRC_GET_CONTAINER_ID,
36 DPRC_CREATE_CONT,
37 DPRC_DESTROY_CONT,
38 DPRC_ASSIGN,
39 DPRC_UNASSIGN,
40 DPRC_GET_OBJ_COUNT,
41 DPRC_GET_OBJ,
42 DPRC_GET_RES_COUNT,
43 DPRC_GET_RES_IDS,
44 DPRC_SET_OBJ_LABEL,
45 DPRC_SET_LOCKED,
46 DPRC_CONNECT,
47 DPRC_DISCONNECT,
48 DPRC_GET_POOL,
49 DPRC_GET_POOL_COUNT,
50 DPRC_GET_CONNECTION,
51 DPRC_GET_MEM,
52 DPCI_GET_LINK_STATE,
53 DPCI_GET_PEER_ATTR,
54 DPAIOP_GET_SL_VERSION,
55 DPAIOP_GET_STATE,
56 DPMNG_GET_VERSION,
57 DPSECI_GET_TX_QUEUE,
58 DPMAC_GET_COUNTER,
59 DPMAC_GET_MAC_ADDR,
60 DPNI_SET_PRIM_MAC,
61 DPNI_GET_PRIM_MAC,
62 DPNI_GET_STATISTICS,
63 DPNI_GET_LINK_STATE,
64 DPNI_GET_MAX_FRAME_LENGTH,
65 DPSW_GET_TAILDROP,
66 DPSW_SET_TAILDROP,
67 DPSW_IF_GET_COUNTER,
68 DPSW_IF_GET_MAX_FRAME_LENGTH,
69 DPDMUX_GET_COUNTER,
70 DPDMUX_IF_GET_MAX_FRAME_LENGTH,
71 GET_ATTR,
72 GET_IRQ_MASK,
73 GET_IRQ_STATUS,
74 CLOSE,
75 OPEN,
76 GET_API_VERSION,
77 DESTROY,
78 CREATE,
79 };
80
81 static struct fsl_mc_cmd_desc fsl_mc_accepted_cmds[] = {
82 [DPDBG_DUMP] = {
83 .cmdid_value = 0x1300,
84 .cmdid_mask = 0xFFF0,
85 .token = true,
86 .size = 28,
87 },
88 [DPDBG_SET] = {
89 .cmdid_value = 0x1400,
90 .cmdid_mask = 0xFFF0,
91 .token = true,
92 .size = 28,
93 },
94 [DPRC_GET_CONTAINER_ID] = {
95 .cmdid_value = 0x8300,
96 .cmdid_mask = 0xFFF0,
97 .token = false,
98 .size = 8,
99 },
100 [DPRC_CREATE_CONT] = {
101 .cmdid_value = 0x1510,
102 .cmdid_mask = 0xFFF0,
103 .token = true,
104 .size = 40,
105 .flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
106 },
107 [DPRC_DESTROY_CONT] = {
108 .cmdid_value = 0x1520,
109 .cmdid_mask = 0xFFF0,
110 .token = true,
111 .size = 12,
112 .flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
113 },
114 [DPRC_ASSIGN] = {
115 .cmdid_value = 0x1570,
116 .cmdid_mask = 0xFFF0,
117 .token = true,
118 .size = 40,
119 .flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
120 },
121 [DPRC_UNASSIGN] = {
122 .cmdid_value = 0x1580,
123 .cmdid_mask = 0xFFF0,
124 .token = true,
125 .size = 40,
126 .flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
127 },
128 [DPRC_GET_OBJ_COUNT] = {
129 .cmdid_value = 0x1590,
130 .cmdid_mask = 0xFFF0,
131 .token = true,
132 .size = 16,
133 },
134 [DPRC_GET_OBJ] = {
135 .cmdid_value = 0x15A0,
136 .cmdid_mask = 0xFFF0,
137 .token = true,
138 .size = 12,
139 },
140 [DPRC_GET_RES_COUNT] = {
141 .cmdid_value = 0x15B0,
142 .cmdid_mask = 0xFFF0,
143 .token = true,
144 .size = 32,
145 },
146 [DPRC_GET_RES_IDS] = {
147 .cmdid_value = 0x15C0,
148 .cmdid_mask = 0xFFF0,
149 .token = true,
150 .size = 40,
151 },
152 [DPRC_SET_OBJ_LABEL] = {
153 .cmdid_value = 0x1610,
154 .cmdid_mask = 0xFFF0,
155 .token = true,
156 .size = 48,
157 .flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
158 },
159 [DPRC_SET_LOCKED] = {
160 .cmdid_value = 0x16B0,
161 .cmdid_mask = 0xFFF0,
162 .token = true,
163 .size = 16,
164 .flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
165 },
166 [DPRC_CONNECT] = {
167 .cmdid_value = 0x1670,
168 .cmdid_mask = 0xFFF0,
169 .token = true,
170 .size = 56,
171 .flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
172 },
173 [DPRC_DISCONNECT] = {
174 .cmdid_value = 0x1680,
175 .cmdid_mask = 0xFFF0,
176 .token = true,
177 .size = 32,
178 .flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
179 },
180 [DPRC_GET_POOL] = {
181 .cmdid_value = 0x1690,
182 .cmdid_mask = 0xFFF0,
183 .token = true,
184 .size = 12,
185 },
186 [DPRC_GET_POOL_COUNT] = {
187 .cmdid_value = 0x16A0,
188 .cmdid_mask = 0xFFF0,
189 .token = true,
190 .size = 8,
191 },
192 [DPRC_GET_CONNECTION] = {
193 .cmdid_value = 0x16C0,
194 .cmdid_mask = 0xFFF0,
195 .token = true,
196 .size = 32,
197 },
198 [DPRC_GET_MEM] = {
199 .cmdid_value = 0x16D0,
200 .cmdid_mask = 0xFFF0,
201 .token = true,
202 .size = 12,
203 },
204
205 [DPCI_GET_LINK_STATE] = {
206 .cmdid_value = 0x0E10,
207 .cmdid_mask = 0xFFF0,
208 .token = true,
209 .size = 8,
210 },
211 [DPCI_GET_PEER_ATTR] = {
212 .cmdid_value = 0x0E20,
213 .cmdid_mask = 0xFFF0,
214 .token = true,
215 .size = 8,
216 },
217 [DPAIOP_GET_SL_VERSION] = {
218 .cmdid_value = 0x2820,
219 .cmdid_mask = 0xFFF0,
220 .token = true,
221 .size = 8,
222 },
223 [DPAIOP_GET_STATE] = {
224 .cmdid_value = 0x2830,
225 .cmdid_mask = 0xFFF0,
226 .token = true,
227 .size = 8,
228 },
229 [DPMNG_GET_VERSION] = {
230 .cmdid_value = 0x8310,
231 .cmdid_mask = 0xFFF0,
232 .token = false,
233 .size = 8,
234 },
235 [DPSECI_GET_TX_QUEUE] = {
236 .cmdid_value = 0x1970,
237 .cmdid_mask = 0xFFF0,
238 .token = true,
239 .size = 14,
240 },
241 [DPMAC_GET_COUNTER] = {
242 .cmdid_value = 0x0c40,
243 .cmdid_mask = 0xFFF0,
244 .token = true,
245 .size = 9,
246 },
247 [DPMAC_GET_MAC_ADDR] = {
248 .cmdid_value = 0x0c50,
249 .cmdid_mask = 0xFFF0,
250 .token = true,
251 .size = 8,
252 },
253 [DPNI_SET_PRIM_MAC] = {
254 .cmdid_value = 0x2240,
255 .cmdid_mask = 0xFFF0,
256 .token = true,
257 .size = 16,
258 .flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
259 },
260 [DPNI_GET_PRIM_MAC] = {
261 .cmdid_value = 0x2250,
262 .cmdid_mask = 0xFFF0,
263 .token = true,
264 .size = 8,
265 },
266 [DPNI_GET_STATISTICS] = {
267 .cmdid_value = 0x25D0,
268 .cmdid_mask = 0xFFF0,
269 .token = true,
270 .size = 10,
271 },
272 [DPNI_GET_LINK_STATE] = {
273 .cmdid_value = 0x2150,
274 .cmdid_mask = 0xFFF0,
275 .token = true,
276 .size = 8,
277 },
278 [DPNI_GET_MAX_FRAME_LENGTH] = {
279 .cmdid_value = 0x2170,
280 .cmdid_mask = 0xFFF0,
281 .token = true,
282 .size = 8,
283 },
284 [DPSW_GET_TAILDROP] = {
285 .cmdid_value = 0x0A90,
286 .cmdid_mask = 0xFFF0,
287 .token = true,
288 .size = 14,
289 },
290 [DPSW_SET_TAILDROP] = {
291 .cmdid_value = 0x0A80,
292 .cmdid_mask = 0xFFF0,
293 .token = true,
294 .size = 24,
295 .flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
296 },
297 [DPSW_IF_GET_COUNTER] = {
298 .cmdid_value = 0x0340,
299 .cmdid_mask = 0xFFF0,
300 .token = true,
301 .size = 11,
302 },
303 [DPSW_IF_GET_MAX_FRAME_LENGTH] = {
304 .cmdid_value = 0x0450,
305 .cmdid_mask = 0xFFF0,
306 .token = true,
307 .size = 10,
308 },
309 [DPDMUX_GET_COUNTER] = {
310 .cmdid_value = 0x0b20,
311 .cmdid_mask = 0xFFF0,
312 .token = true,
313 .size = 11,
314 },
315 [DPDMUX_IF_GET_MAX_FRAME_LENGTH] = {
316 .cmdid_value = 0x0a20,
317 .cmdid_mask = 0xFFF0,
318 .token = true,
319 .size = 10,
320 },
321 [GET_ATTR] = {
322 .cmdid_value = 0x0040,
323 .cmdid_mask = 0xFFF0,
324 .token = true,
325 .size = 8,
326 },
327 [GET_IRQ_MASK] = {
328 .cmdid_value = 0x0150,
329 .cmdid_mask = 0xFFF0,
330 .token = true,
331 .size = 13,
332 },
333 [GET_IRQ_STATUS] = {
334 .cmdid_value = 0x0160,
335 .cmdid_mask = 0xFFF0,
336 .token = true,
337 .size = 13,
338 },
339 [CLOSE] = {
340 .cmdid_value = 0x8000,
341 .cmdid_mask = 0xFFF0,
342 .token = true,
343 .size = 8,
344 },
345
346 /* Common commands amongst all types of objects. Must be checked last. */
347 [OPEN] = {
348 .cmdid_value = 0x8000,
349 .cmdid_mask = 0xFC00,
350 .token = false,
351 .size = 12,
352 .flags = FSL_MC_CHECK_MODULE_ID,
353 },
354 [GET_API_VERSION] = {
355 .cmdid_value = 0xA000,
356 .cmdid_mask = 0xFC00,
357 .token = false,
358 .size = 8,
359 .flags = FSL_MC_CHECK_MODULE_ID,
360 },
361 [DESTROY] = {
362 .cmdid_value = 0x9800,
363 .cmdid_mask = 0xFC00,
364 .token = true,
365 .size = 12,
366 .flags = FSL_MC_CHECK_MODULE_ID | FSL_MC_CAP_NET_ADMIN_NEEDED,
367 },
368 [CREATE] = {
369 .cmdid_value = 0x9000,
370 .cmdid_mask = 0xFC00,
371 .token = true,
372 .size = 64,
373 .flags = FSL_MC_CHECK_MODULE_ID | FSL_MC_CAP_NET_ADMIN_NEEDED,
374 },
375 };
376
377 #define FSL_MC_NUM_ACCEPTED_CMDS ARRAY_SIZE(fsl_mc_accepted_cmds)
378
379 #define FSL_MC_MAX_MODULE_ID 0x10
380
fsl_mc_command_check(struct fsl_mc_device * mc_dev,struct fsl_mc_command * mc_cmd)381 static int fsl_mc_command_check(struct fsl_mc_device *mc_dev,
382 struct fsl_mc_command *mc_cmd)
383 {
384 struct fsl_mc_cmd_desc *desc = NULL;
385 int mc_cmd_max_size, i;
386 bool token_provided;
387 u16 cmdid, module_id;
388 char *mc_cmd_end;
389 char sum = 0;
390
391 /* Check if this is an accepted MC command */
392 cmdid = mc_cmd_hdr_read_cmdid(mc_cmd);
393 for (i = 0; i < FSL_MC_NUM_ACCEPTED_CMDS; i++) {
394 desc = &fsl_mc_accepted_cmds[i];
395 if ((cmdid & desc->cmdid_mask) == desc->cmdid_value)
396 break;
397 }
398 if (i == FSL_MC_NUM_ACCEPTED_CMDS) {
399 dev_err(&mc_dev->dev, "MC command 0x%04x: cmdid not accepted\n", cmdid);
400 return -EACCES;
401 }
402
403 /* Check if the size of the command is honored. Anything beyond the
404 * last valid byte of the command should be zeroed.
405 */
406 mc_cmd_max_size = sizeof(*mc_cmd);
407 mc_cmd_end = ((char *)mc_cmd) + desc->size;
408 for (i = desc->size; i < mc_cmd_max_size; i++)
409 sum |= *mc_cmd_end++;
410 if (sum) {
411 dev_err(&mc_dev->dev, "MC command 0x%04x: garbage beyond max size of %d bytes!\n",
412 cmdid, desc->size);
413 return -EACCES;
414 }
415
416 /* Some MC commands request a token to be passed so that object
417 * identification is possible. Check if the token passed in the command
418 * is as expected.
419 */
420 token_provided = mc_cmd_hdr_read_token(mc_cmd) ? true : false;
421 if (token_provided != desc->token) {
422 dev_err(&mc_dev->dev, "MC command 0x%04x: token 0x%04x is invalid!\n",
423 cmdid, mc_cmd_hdr_read_token(mc_cmd));
424 return -EACCES;
425 }
426
427 /* If needed, check if the module ID passed is valid */
428 if (desc->flags & FSL_MC_CHECK_MODULE_ID) {
429 /* The module ID is represented by bits [4:9] from the cmdid */
430 module_id = (cmdid & GENMASK(9, 4)) >> 4;
431 if (module_id == 0 || module_id > FSL_MC_MAX_MODULE_ID) {
432 dev_err(&mc_dev->dev, "MC command 0x%04x: unknown module ID 0x%x\n",
433 cmdid, module_id);
434 return -EACCES;
435 }
436 }
437
438 /* Some commands alter how hardware resources are managed. For these
439 * commands, check for CAP_NET_ADMIN.
440 */
441 if (desc->flags & FSL_MC_CAP_NET_ADMIN_NEEDED) {
442 if (!capable(CAP_NET_ADMIN)) {
443 dev_err(&mc_dev->dev, "MC command 0x%04x: needs CAP_NET_ADMIN!\n",
444 cmdid);
445 return -EPERM;
446 }
447 }
448
449 return 0;
450 }
451
fsl_mc_uapi_send_command(struct fsl_mc_device * mc_dev,unsigned long arg,struct fsl_mc_io * mc_io)452 static int fsl_mc_uapi_send_command(struct fsl_mc_device *mc_dev, unsigned long arg,
453 struct fsl_mc_io *mc_io)
454 {
455 struct fsl_mc_command mc_cmd;
456 int error;
457
458 error = copy_from_user(&mc_cmd, (void __user *)arg, sizeof(mc_cmd));
459 if (error)
460 return -EFAULT;
461
462 error = fsl_mc_command_check(mc_dev, &mc_cmd);
463 if (error)
464 return error;
465
466 error = mc_send_command(mc_io, &mc_cmd);
467 if (error)
468 return error;
469
470 error = copy_to_user((void __user *)arg, &mc_cmd, sizeof(mc_cmd));
471 if (error)
472 return -EFAULT;
473
474 return 0;
475 }
476
fsl_mc_uapi_dev_open(struct inode * inode,struct file * filep)477 static int fsl_mc_uapi_dev_open(struct inode *inode, struct file *filep)
478 {
479 struct fsl_mc_device *root_mc_device;
480 struct uapi_priv_data *priv_data;
481 struct fsl_mc_io *dynamic_mc_io;
482 struct fsl_mc_uapi *mc_uapi;
483 struct fsl_mc_bus *mc_bus;
484 int error;
485
486 priv_data = kzalloc(sizeof(*priv_data), GFP_KERNEL);
487 if (!priv_data)
488 return -ENOMEM;
489
490 mc_uapi = container_of(filep->private_data, struct fsl_mc_uapi, misc);
491 mc_bus = container_of(mc_uapi, struct fsl_mc_bus, uapi_misc);
492 root_mc_device = &mc_bus->mc_dev;
493
494 mutex_lock(&mc_uapi->mutex);
495
496 if (!mc_uapi->local_instance_in_use) {
497 priv_data->mc_io = mc_uapi->static_mc_io;
498 mc_uapi->local_instance_in_use = 1;
499 } else {
500 error = fsl_mc_portal_allocate(root_mc_device, 0,
501 &dynamic_mc_io);
502 if (error) {
503 dev_dbg(&root_mc_device->dev,
504 "Could not allocate MC portal\n");
505 goto error_portal_allocate;
506 }
507
508 priv_data->mc_io = dynamic_mc_io;
509 }
510 priv_data->uapi = mc_uapi;
511 filep->private_data = priv_data;
512
513 mutex_unlock(&mc_uapi->mutex);
514
515 return 0;
516
517 error_portal_allocate:
518 mutex_unlock(&mc_uapi->mutex);
519 kfree(priv_data);
520
521 return error;
522 }
523
fsl_mc_uapi_dev_release(struct inode * inode,struct file * filep)524 static int fsl_mc_uapi_dev_release(struct inode *inode, struct file *filep)
525 {
526 struct uapi_priv_data *priv_data;
527 struct fsl_mc_uapi *mc_uapi;
528 struct fsl_mc_io *mc_io;
529
530 priv_data = filep->private_data;
531 mc_uapi = priv_data->uapi;
532 mc_io = priv_data->mc_io;
533
534 mutex_lock(&mc_uapi->mutex);
535
536 if (mc_io == mc_uapi->static_mc_io)
537 mc_uapi->local_instance_in_use = 0;
538 else
539 fsl_mc_portal_free(mc_io);
540
541 kfree(filep->private_data);
542 filep->private_data = NULL;
543
544 mutex_unlock(&mc_uapi->mutex);
545
546 return 0;
547 }
548
fsl_mc_uapi_dev_ioctl(struct file * file,unsigned int cmd,unsigned long arg)549 static long fsl_mc_uapi_dev_ioctl(struct file *file,
550 unsigned int cmd,
551 unsigned long arg)
552 {
553 struct uapi_priv_data *priv_data = file->private_data;
554 struct fsl_mc_device *root_mc_device;
555 struct fsl_mc_bus *mc_bus;
556 int error;
557
558 mc_bus = container_of(priv_data->uapi, struct fsl_mc_bus, uapi_misc);
559 root_mc_device = &mc_bus->mc_dev;
560
561 switch (cmd) {
562 case FSL_MC_SEND_MC_COMMAND:
563 error = fsl_mc_uapi_send_command(root_mc_device, arg, priv_data->mc_io);
564 break;
565 default:
566 dev_dbg(&root_mc_device->dev, "unexpected ioctl call number\n");
567 error = -EINVAL;
568 }
569
570 return error;
571 }
572
573 static const struct file_operations fsl_mc_uapi_dev_fops = {
574 .owner = THIS_MODULE,
575 .open = fsl_mc_uapi_dev_open,
576 .release = fsl_mc_uapi_dev_release,
577 .unlocked_ioctl = fsl_mc_uapi_dev_ioctl,
578 };
579
fsl_mc_uapi_create_device_file(struct fsl_mc_bus * mc_bus)580 int fsl_mc_uapi_create_device_file(struct fsl_mc_bus *mc_bus)
581 {
582 struct fsl_mc_device *mc_dev = &mc_bus->mc_dev;
583 struct fsl_mc_uapi *mc_uapi = &mc_bus->uapi_misc;
584 int error;
585
586 mc_uapi->misc.minor = MISC_DYNAMIC_MINOR;
587 mc_uapi->misc.name = dev_name(&mc_dev->dev);
588 mc_uapi->misc.fops = &fsl_mc_uapi_dev_fops;
589
590 error = misc_register(&mc_uapi->misc);
591 if (error)
592 return error;
593
594 mc_uapi->static_mc_io = mc_bus->mc_dev.mc_io;
595
596 mutex_init(&mc_uapi->mutex);
597
598 return 0;
599 }
600
fsl_mc_uapi_remove_device_file(struct fsl_mc_bus * mc_bus)601 void fsl_mc_uapi_remove_device_file(struct fsl_mc_bus *mc_bus)
602 {
603 misc_deregister(&mc_bus->uapi_misc.misc);
604 }
605