Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Management Complex (MC) userspace support
0004  *
0005  * Copyright 2021 NXP
0006  *
0007  */
0008 
0009 #include <linux/slab.h>
0010 #include <linux/fs.h>
0011 #include <linux/uaccess.h>
0012 #include <linux/miscdevice.h>
0013 
0014 #include "fsl-mc-private.h"
0015 
0016 struct uapi_priv_data {
0017     struct fsl_mc_uapi *uapi;
0018     struct fsl_mc_io *mc_io;
0019 };
0020 
0021 struct fsl_mc_cmd_desc {
0022     u16 cmdid_value;
0023     u16 cmdid_mask;
0024     int size;
0025     bool token;
0026     int flags;
0027 };
0028 
0029 #define FSL_MC_CHECK_MODULE_ID      BIT(0)
0030 #define FSL_MC_CAP_NET_ADMIN_NEEDED BIT(1)
0031 
0032 enum fsl_mc_cmd_index {
0033     DPDBG_DUMP = 0,
0034     DPDBG_SET,
0035     DPRC_GET_CONTAINER_ID,
0036     DPRC_CREATE_CONT,
0037     DPRC_DESTROY_CONT,
0038     DPRC_ASSIGN,
0039     DPRC_UNASSIGN,
0040     DPRC_GET_OBJ_COUNT,
0041     DPRC_GET_OBJ,
0042     DPRC_GET_RES_COUNT,
0043     DPRC_GET_RES_IDS,
0044     DPRC_SET_OBJ_LABEL,
0045     DPRC_SET_LOCKED,
0046     DPRC_CONNECT,
0047     DPRC_DISCONNECT,
0048     DPRC_GET_POOL,
0049     DPRC_GET_POOL_COUNT,
0050     DPRC_GET_CONNECTION,
0051     DPCI_GET_LINK_STATE,
0052     DPCI_GET_PEER_ATTR,
0053     DPAIOP_GET_SL_VERSION,
0054     DPAIOP_GET_STATE,
0055     DPMNG_GET_VERSION,
0056     DPSECI_GET_TX_QUEUE,
0057     DPMAC_GET_COUNTER,
0058     DPMAC_GET_MAC_ADDR,
0059     DPNI_SET_PRIM_MAC,
0060     DPNI_GET_PRIM_MAC,
0061     DPNI_GET_STATISTICS,
0062     DPNI_GET_LINK_STATE,
0063     DPNI_GET_MAX_FRAME_LENGTH,
0064     DPSW_GET_TAILDROP,
0065     DPSW_SET_TAILDROP,
0066     DPSW_IF_GET_COUNTER,
0067     DPSW_IF_GET_MAX_FRAME_LENGTH,
0068     DPDMUX_GET_COUNTER,
0069     DPDMUX_IF_GET_MAX_FRAME_LENGTH,
0070     GET_ATTR,
0071     GET_IRQ_MASK,
0072     GET_IRQ_STATUS,
0073     CLOSE,
0074     OPEN,
0075     GET_API_VERSION,
0076     DESTROY,
0077     CREATE,
0078 };
0079 
0080 static struct fsl_mc_cmd_desc fsl_mc_accepted_cmds[] = {
0081     [DPDBG_DUMP] = {
0082         .cmdid_value = 0x1300,
0083         .cmdid_mask = 0xFFF0,
0084         .token = true,
0085         .size = 28,
0086     },
0087     [DPDBG_SET] = {
0088         .cmdid_value = 0x1400,
0089         .cmdid_mask = 0xFFF0,
0090         .token = true,
0091         .size = 28,
0092     },
0093     [DPRC_GET_CONTAINER_ID] = {
0094         .cmdid_value = 0x8300,
0095         .cmdid_mask = 0xFFF0,
0096         .token = false,
0097         .size = 8,
0098     },
0099     [DPRC_CREATE_CONT] = {
0100         .cmdid_value = 0x1510,
0101         .cmdid_mask = 0xFFF0,
0102         .token = true,
0103         .size = 40,
0104         .flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
0105     },
0106     [DPRC_DESTROY_CONT] = {
0107         .cmdid_value = 0x1520,
0108         .cmdid_mask = 0xFFF0,
0109         .token = true,
0110         .size = 12,
0111         .flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
0112     },
0113     [DPRC_ASSIGN] = {
0114         .cmdid_value = 0x1570,
0115         .cmdid_mask = 0xFFF0,
0116         .token = true,
0117         .size = 40,
0118         .flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
0119     },
0120     [DPRC_UNASSIGN] = {
0121         .cmdid_value = 0x1580,
0122         .cmdid_mask = 0xFFF0,
0123         .token = true,
0124         .size = 40,
0125         .flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
0126     },
0127     [DPRC_GET_OBJ_COUNT] = {
0128         .cmdid_value = 0x1590,
0129         .cmdid_mask = 0xFFF0,
0130         .token = true,
0131         .size = 16,
0132     },
0133     [DPRC_GET_OBJ] = {
0134         .cmdid_value = 0x15A0,
0135         .cmdid_mask = 0xFFF0,
0136         .token = true,
0137         .size = 12,
0138     },
0139     [DPRC_GET_RES_COUNT] = {
0140         .cmdid_value = 0x15B0,
0141         .cmdid_mask = 0xFFF0,
0142         .token = true,
0143         .size = 32,
0144     },
0145     [DPRC_GET_RES_IDS] = {
0146         .cmdid_value = 0x15C0,
0147         .cmdid_mask = 0xFFF0,
0148         .token = true,
0149         .size = 40,
0150     },
0151     [DPRC_SET_OBJ_LABEL] = {
0152         .cmdid_value = 0x1610,
0153         .cmdid_mask = 0xFFF0,
0154         .token = true,
0155         .size = 48,
0156         .flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
0157     },
0158     [DPRC_SET_LOCKED] = {
0159         .cmdid_value = 0x16B0,
0160         .cmdid_mask = 0xFFF0,
0161         .token = true,
0162         .size = 16,
0163         .flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
0164     },
0165     [DPRC_CONNECT] = {
0166         .cmdid_value = 0x1670,
0167         .cmdid_mask = 0xFFF0,
0168         .token = true,
0169         .size = 56,
0170         .flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
0171     },
0172     [DPRC_DISCONNECT] = {
0173         .cmdid_value = 0x1680,
0174         .cmdid_mask = 0xFFF0,
0175         .token = true,
0176         .size = 32,
0177         .flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
0178     },
0179     [DPRC_GET_POOL] = {
0180         .cmdid_value = 0x1690,
0181         .cmdid_mask = 0xFFF0,
0182         .token = true,
0183         .size = 12,
0184     },
0185     [DPRC_GET_POOL_COUNT] = {
0186         .cmdid_value = 0x16A0,
0187         .cmdid_mask = 0xFFF0,
0188         .token = true,
0189         .size = 8,
0190     },
0191     [DPRC_GET_CONNECTION] = {
0192         .cmdid_value = 0x16C0,
0193         .cmdid_mask = 0xFFF0,
0194         .token = true,
0195         .size = 32,
0196     },
0197 
0198     [DPCI_GET_LINK_STATE] = {
0199         .cmdid_value = 0x0E10,
0200         .cmdid_mask = 0xFFF0,
0201         .token = true,
0202         .size = 8,
0203     },
0204     [DPCI_GET_PEER_ATTR] = {
0205         .cmdid_value = 0x0E20,
0206         .cmdid_mask = 0xFFF0,
0207         .token = true,
0208         .size = 8,
0209     },
0210     [DPAIOP_GET_SL_VERSION] = {
0211         .cmdid_value = 0x2820,
0212         .cmdid_mask = 0xFFF0,
0213         .token = true,
0214         .size = 8,
0215     },
0216     [DPAIOP_GET_STATE] = {
0217         .cmdid_value = 0x2830,
0218         .cmdid_mask = 0xFFF0,
0219         .token = true,
0220         .size = 8,
0221     },
0222     [DPMNG_GET_VERSION] = {
0223         .cmdid_value = 0x8310,
0224         .cmdid_mask = 0xFFF0,
0225         .token = false,
0226         .size = 8,
0227     },
0228     [DPSECI_GET_TX_QUEUE] = {
0229         .cmdid_value = 0x1970,
0230         .cmdid_mask = 0xFFF0,
0231         .token = true,
0232         .size = 14,
0233     },
0234     [DPMAC_GET_COUNTER] = {
0235         .cmdid_value = 0x0c40,
0236         .cmdid_mask = 0xFFF0,
0237         .token = true,
0238         .size = 9,
0239     },
0240     [DPMAC_GET_MAC_ADDR] = {
0241         .cmdid_value = 0x0c50,
0242         .cmdid_mask = 0xFFF0,
0243         .token = true,
0244         .size = 8,
0245     },
0246     [DPNI_SET_PRIM_MAC] = {
0247         .cmdid_value = 0x2240,
0248         .cmdid_mask = 0xFFF0,
0249         .token = true,
0250         .size = 16,
0251         .flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
0252     },
0253     [DPNI_GET_PRIM_MAC] = {
0254         .cmdid_value = 0x2250,
0255         .cmdid_mask = 0xFFF0,
0256         .token = true,
0257         .size = 8,
0258     },
0259     [DPNI_GET_STATISTICS] = {
0260         .cmdid_value = 0x25D0,
0261         .cmdid_mask = 0xFFF0,
0262         .token = true,
0263         .size = 10,
0264     },
0265     [DPNI_GET_LINK_STATE] = {
0266         .cmdid_value = 0x2150,
0267         .cmdid_mask = 0xFFF0,
0268         .token = true,
0269         .size = 8,
0270     },
0271     [DPNI_GET_MAX_FRAME_LENGTH] = {
0272         .cmdid_value = 0x2170,
0273         .cmdid_mask = 0xFFF0,
0274         .token = true,
0275         .size = 8,
0276     },
0277     [DPSW_GET_TAILDROP] = {
0278         .cmdid_value = 0x0A80,
0279         .cmdid_mask = 0xFFF0,
0280         .token = true,
0281         .size = 14,
0282     },
0283     [DPSW_SET_TAILDROP] = {
0284         .cmdid_value = 0x0A90,
0285         .cmdid_mask = 0xFFF0,
0286         .token = true,
0287         .size = 24,
0288         .flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
0289     },
0290     [DPSW_IF_GET_COUNTER] = {
0291         .cmdid_value = 0x0340,
0292         .cmdid_mask = 0xFFF0,
0293         .token = true,
0294         .size = 11,
0295     },
0296     [DPSW_IF_GET_MAX_FRAME_LENGTH] = {
0297         .cmdid_value = 0x0450,
0298         .cmdid_mask = 0xFFF0,
0299         .token = true,
0300         .size = 10,
0301     },
0302     [DPDMUX_GET_COUNTER] = {
0303         .cmdid_value = 0x0b20,
0304         .cmdid_mask = 0xFFF0,
0305         .token = true,
0306         .size = 11,
0307     },
0308     [DPDMUX_IF_GET_MAX_FRAME_LENGTH] = {
0309         .cmdid_value = 0x0a20,
0310         .cmdid_mask = 0xFFF0,
0311         .token = true,
0312         .size = 10,
0313     },
0314     [GET_ATTR] = {
0315         .cmdid_value = 0x0040,
0316         .cmdid_mask = 0xFFF0,
0317         .token = true,
0318         .size = 8,
0319     },
0320     [GET_IRQ_MASK] = {
0321         .cmdid_value = 0x0150,
0322         .cmdid_mask = 0xFFF0,
0323         .token = true,
0324         .size = 13,
0325     },
0326     [GET_IRQ_STATUS] = {
0327         .cmdid_value = 0x0160,
0328         .cmdid_mask = 0xFFF0,
0329         .token = true,
0330         .size = 13,
0331     },
0332     [CLOSE] = {
0333         .cmdid_value = 0x8000,
0334         .cmdid_mask = 0xFFF0,
0335         .token = true,
0336         .size = 8,
0337     },
0338 
0339     /* Common commands amongst all types of objects. Must be checked last. */
0340     [OPEN] = {
0341         .cmdid_value = 0x8000,
0342         .cmdid_mask = 0xFC00,
0343         .token = false,
0344         .size = 12,
0345         .flags = FSL_MC_CHECK_MODULE_ID,
0346     },
0347     [GET_API_VERSION] = {
0348         .cmdid_value = 0xA000,
0349         .cmdid_mask = 0xFC00,
0350         .token = false,
0351         .size = 8,
0352         .flags = FSL_MC_CHECK_MODULE_ID,
0353     },
0354     [DESTROY] = {
0355         .cmdid_value = 0x9800,
0356         .cmdid_mask = 0xFC00,
0357         .token = true,
0358         .size = 12,
0359         .flags = FSL_MC_CHECK_MODULE_ID | FSL_MC_CAP_NET_ADMIN_NEEDED,
0360     },
0361     [CREATE] = {
0362         .cmdid_value = 0x9000,
0363         .cmdid_mask = 0xFC00,
0364         .token = true,
0365         .size = 64,
0366         .flags = FSL_MC_CHECK_MODULE_ID | FSL_MC_CAP_NET_ADMIN_NEEDED,
0367     },
0368 };
0369 
0370 #define FSL_MC_NUM_ACCEPTED_CMDS ARRAY_SIZE(fsl_mc_accepted_cmds)
0371 
0372 #define FSL_MC_MAX_MODULE_ID 0x10
0373 
0374 static int fsl_mc_command_check(struct fsl_mc_device *mc_dev,
0375                 struct fsl_mc_command *mc_cmd)
0376 {
0377     struct fsl_mc_cmd_desc *desc = NULL;
0378     int mc_cmd_max_size, i;
0379     bool token_provided;
0380     u16 cmdid, module_id;
0381     char *mc_cmd_end;
0382     char sum = 0;
0383 
0384     /* Check if this is an accepted MC command */
0385     cmdid = mc_cmd_hdr_read_cmdid(mc_cmd);
0386     for (i = 0; i < FSL_MC_NUM_ACCEPTED_CMDS; i++) {
0387         desc = &fsl_mc_accepted_cmds[i];
0388         if ((cmdid & desc->cmdid_mask) == desc->cmdid_value)
0389             break;
0390     }
0391     if (i == FSL_MC_NUM_ACCEPTED_CMDS) {
0392         dev_err(&mc_dev->dev, "MC command 0x%04x: cmdid not accepted\n", cmdid);
0393         return -EACCES;
0394     }
0395 
0396     /* Check if the size of the command is honored. Anything beyond the
0397      * last valid byte of the command should be zeroed.
0398      */
0399     mc_cmd_max_size = sizeof(*mc_cmd);
0400     mc_cmd_end = ((char *)mc_cmd) + desc->size;
0401     for (i = desc->size; i < mc_cmd_max_size; i++)
0402         sum |= *mc_cmd_end++;
0403     if (sum) {
0404         dev_err(&mc_dev->dev, "MC command 0x%04x: garbage beyond max size of %d bytes!\n",
0405             cmdid, desc->size);
0406         return -EACCES;
0407     }
0408 
0409     /* Some MC commands request a token to be passed so that object
0410      * identification is possible. Check if the token passed in the command
0411      * is as expected.
0412      */
0413     token_provided = mc_cmd_hdr_read_token(mc_cmd) ? true : false;
0414     if (token_provided != desc->token) {
0415         dev_err(&mc_dev->dev, "MC command 0x%04x: token 0x%04x is invalid!\n",
0416             cmdid, mc_cmd_hdr_read_token(mc_cmd));
0417         return -EACCES;
0418     }
0419 
0420     /* If needed, check if the module ID passed is valid */
0421     if (desc->flags & FSL_MC_CHECK_MODULE_ID) {
0422         /* The module ID is represented by bits [4:9] from the cmdid */
0423         module_id = (cmdid & GENMASK(9, 4)) >> 4;
0424         if (module_id == 0 || module_id > FSL_MC_MAX_MODULE_ID) {
0425             dev_err(&mc_dev->dev, "MC command 0x%04x: unknown module ID 0x%x\n",
0426                 cmdid, module_id);
0427             return -EACCES;
0428         }
0429     }
0430 
0431     /* Some commands alter how hardware resources are managed. For these
0432      * commands, check for CAP_NET_ADMIN.
0433      */
0434     if (desc->flags & FSL_MC_CAP_NET_ADMIN_NEEDED) {
0435         if (!capable(CAP_NET_ADMIN)) {
0436             dev_err(&mc_dev->dev, "MC command 0x%04x: needs CAP_NET_ADMIN!\n",
0437                 cmdid);
0438             return -EPERM;
0439         }
0440     }
0441 
0442     return 0;
0443 }
0444 
0445 static int fsl_mc_uapi_send_command(struct fsl_mc_device *mc_dev, unsigned long arg,
0446                     struct fsl_mc_io *mc_io)
0447 {
0448     struct fsl_mc_command mc_cmd;
0449     int error;
0450 
0451     error = copy_from_user(&mc_cmd, (void __user *)arg, sizeof(mc_cmd));
0452     if (error)
0453         return -EFAULT;
0454 
0455     error = fsl_mc_command_check(mc_dev, &mc_cmd);
0456     if (error)
0457         return error;
0458 
0459     error = mc_send_command(mc_io, &mc_cmd);
0460     if (error)
0461         return error;
0462 
0463     error = copy_to_user((void __user *)arg, &mc_cmd, sizeof(mc_cmd));
0464     if (error)
0465         return -EFAULT;
0466 
0467     return 0;
0468 }
0469 
0470 static int fsl_mc_uapi_dev_open(struct inode *inode, struct file *filep)
0471 {
0472     struct fsl_mc_device *root_mc_device;
0473     struct uapi_priv_data *priv_data;
0474     struct fsl_mc_io *dynamic_mc_io;
0475     struct fsl_mc_uapi *mc_uapi;
0476     struct fsl_mc_bus *mc_bus;
0477     int error;
0478 
0479     priv_data = kzalloc(sizeof(*priv_data), GFP_KERNEL);
0480     if (!priv_data)
0481         return -ENOMEM;
0482 
0483     mc_uapi = container_of(filep->private_data, struct fsl_mc_uapi, misc);
0484     mc_bus = container_of(mc_uapi, struct fsl_mc_bus, uapi_misc);
0485     root_mc_device = &mc_bus->mc_dev;
0486 
0487     mutex_lock(&mc_uapi->mutex);
0488 
0489     if (!mc_uapi->local_instance_in_use) {
0490         priv_data->mc_io = mc_uapi->static_mc_io;
0491         mc_uapi->local_instance_in_use = 1;
0492     } else {
0493         error = fsl_mc_portal_allocate(root_mc_device, 0,
0494                            &dynamic_mc_io);
0495         if (error) {
0496             dev_dbg(&root_mc_device->dev,
0497                 "Could not allocate MC portal\n");
0498             goto error_portal_allocate;
0499         }
0500 
0501         priv_data->mc_io = dynamic_mc_io;
0502     }
0503     priv_data->uapi = mc_uapi;
0504     filep->private_data = priv_data;
0505 
0506     mutex_unlock(&mc_uapi->mutex);
0507 
0508     return 0;
0509 
0510 error_portal_allocate:
0511     mutex_unlock(&mc_uapi->mutex);
0512     kfree(priv_data);
0513 
0514     return error;
0515 }
0516 
0517 static int fsl_mc_uapi_dev_release(struct inode *inode, struct file *filep)
0518 {
0519     struct uapi_priv_data *priv_data;
0520     struct fsl_mc_uapi *mc_uapi;
0521     struct fsl_mc_io *mc_io;
0522 
0523     priv_data = filep->private_data;
0524     mc_uapi = priv_data->uapi;
0525     mc_io = priv_data->mc_io;
0526 
0527     mutex_lock(&mc_uapi->mutex);
0528 
0529     if (mc_io == mc_uapi->static_mc_io)
0530         mc_uapi->local_instance_in_use = 0;
0531     else
0532         fsl_mc_portal_free(mc_io);
0533 
0534     kfree(filep->private_data);
0535     filep->private_data =  NULL;
0536 
0537     mutex_unlock(&mc_uapi->mutex);
0538 
0539     return 0;
0540 }
0541 
0542 static long fsl_mc_uapi_dev_ioctl(struct file *file,
0543                   unsigned int cmd,
0544                   unsigned long arg)
0545 {
0546     struct uapi_priv_data *priv_data = file->private_data;
0547     struct fsl_mc_device *root_mc_device;
0548     struct fsl_mc_bus *mc_bus;
0549     int error;
0550 
0551     mc_bus = container_of(priv_data->uapi, struct fsl_mc_bus, uapi_misc);
0552     root_mc_device = &mc_bus->mc_dev;
0553 
0554     switch (cmd) {
0555     case FSL_MC_SEND_MC_COMMAND:
0556         error = fsl_mc_uapi_send_command(root_mc_device, arg, priv_data->mc_io);
0557         break;
0558     default:
0559         dev_dbg(&root_mc_device->dev, "unexpected ioctl call number\n");
0560         error = -EINVAL;
0561     }
0562 
0563     return error;
0564 }
0565 
0566 static const struct file_operations fsl_mc_uapi_dev_fops = {
0567     .owner = THIS_MODULE,
0568     .open = fsl_mc_uapi_dev_open,
0569     .release = fsl_mc_uapi_dev_release,
0570     .unlocked_ioctl = fsl_mc_uapi_dev_ioctl,
0571 };
0572 
0573 int fsl_mc_uapi_create_device_file(struct fsl_mc_bus *mc_bus)
0574 {
0575     struct fsl_mc_device *mc_dev = &mc_bus->mc_dev;
0576     struct fsl_mc_uapi *mc_uapi = &mc_bus->uapi_misc;
0577     int error;
0578 
0579     mc_uapi->misc.minor = MISC_DYNAMIC_MINOR;
0580     mc_uapi->misc.name = dev_name(&mc_dev->dev);
0581     mc_uapi->misc.fops = &fsl_mc_uapi_dev_fops;
0582 
0583     error = misc_register(&mc_uapi->misc);
0584     if (error)
0585         return error;
0586 
0587     mc_uapi->static_mc_io = mc_bus->mc_dev.mc_io;
0588 
0589     mutex_init(&mc_uapi->mutex);
0590 
0591     return 0;
0592 }
0593 
0594 void fsl_mc_uapi_remove_device_file(struct fsl_mc_bus *mc_bus)
0595 {
0596     misc_deregister(&mc_bus->uapi_misc.misc);
0597 }