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