linux/drivers/bus/fsl-mc/fsl-mc-uapi.c
<<
>>
Prefs
   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
  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 0x10
 373
 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 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
 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        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
 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