0001
0002
0003
0004
0005
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
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
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
0397
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
0410
0411
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
0421 if (desc->flags & FSL_MC_CHECK_MODULE_ID) {
0422
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
0432
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 }