598 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Management Complex (MC) userspace support4 *5 * Copyright 2021 NXP6 *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 16struct uapi_priv_data {17 struct fsl_mc_uapi *uapi;18 struct fsl_mc_io *mc_io;19};20 21struct 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 32enum 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 80static 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 0x10373 374static 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 the397 * 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 object410 * identification is possible. Check if the token passed in the command411 * 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 these432 * 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 445static 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 470static 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 510error_portal_allocate:511 mutex_unlock(&mc_uapi->mutex);512 kfree(priv_data);513 514 return error;515}516 517static 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 else532 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 542static 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 566static 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 573int 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 594void fsl_mc_uapi_remove_device_file(struct fsl_mc_bus *mc_bus)595{596 misc_deregister(&mc_bus->uapi_misc.misc);597}598