123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Management Complex (MC) userspace support
- *
- * Copyright 2021 NXP
- *
- */
- #include <linux/slab.h>
- #include <linux/fs.h>
- #include <linux/uaccess.h>
- #include <linux/miscdevice.h>
- #include "fsl-mc-private.h"
- struct uapi_priv_data {
- struct fsl_mc_uapi *uapi;
- struct fsl_mc_io *mc_io;
- };
- struct fsl_mc_cmd_desc {
- u16 cmdid_value;
- u16 cmdid_mask;
- int size;
- bool token;
- int flags;
- };
- #define FSL_MC_CHECK_MODULE_ID BIT(0)
- #define FSL_MC_CAP_NET_ADMIN_NEEDED BIT(1)
- enum fsl_mc_cmd_index {
- DPDBG_DUMP = 0,
- DPDBG_SET,
- DPRC_GET_CONTAINER_ID,
- DPRC_CREATE_CONT,
- DPRC_DESTROY_CONT,
- DPRC_ASSIGN,
- DPRC_UNASSIGN,
- DPRC_GET_OBJ_COUNT,
- DPRC_GET_OBJ,
- DPRC_GET_RES_COUNT,
- DPRC_GET_RES_IDS,
- DPRC_SET_OBJ_LABEL,
- DPRC_SET_LOCKED,
- DPRC_CONNECT,
- DPRC_DISCONNECT,
- DPRC_GET_POOL,
- DPRC_GET_POOL_COUNT,
- DPRC_GET_CONNECTION,
- DPCI_GET_LINK_STATE,
- DPCI_GET_PEER_ATTR,
- DPAIOP_GET_SL_VERSION,
- DPAIOP_GET_STATE,
- DPMNG_GET_VERSION,
- DPSECI_GET_TX_QUEUE,
- DPMAC_GET_COUNTER,
- DPMAC_GET_MAC_ADDR,
- DPNI_SET_PRIM_MAC,
- DPNI_GET_PRIM_MAC,
- DPNI_GET_STATISTICS,
- DPNI_GET_LINK_STATE,
- DPNI_GET_MAX_FRAME_LENGTH,
- DPSW_GET_TAILDROP,
- DPSW_SET_TAILDROP,
- DPSW_IF_GET_COUNTER,
- DPSW_IF_GET_MAX_FRAME_LENGTH,
- DPDMUX_GET_COUNTER,
- DPDMUX_IF_GET_MAX_FRAME_LENGTH,
- GET_ATTR,
- GET_IRQ_MASK,
- GET_IRQ_STATUS,
- CLOSE,
- OPEN,
- GET_API_VERSION,
- DESTROY,
- CREATE,
- };
- static struct fsl_mc_cmd_desc fsl_mc_accepted_cmds[] = {
- [DPDBG_DUMP] = {
- .cmdid_value = 0x1300,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 28,
- },
- [DPDBG_SET] = {
- .cmdid_value = 0x1400,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 28,
- },
- [DPRC_GET_CONTAINER_ID] = {
- .cmdid_value = 0x8300,
- .cmdid_mask = 0xFFF0,
- .token = false,
- .size = 8,
- },
- [DPRC_CREATE_CONT] = {
- .cmdid_value = 0x1510,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 40,
- .flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
- },
- [DPRC_DESTROY_CONT] = {
- .cmdid_value = 0x1520,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 12,
- .flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
- },
- [DPRC_ASSIGN] = {
- .cmdid_value = 0x1570,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 40,
- .flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
- },
- [DPRC_UNASSIGN] = {
- .cmdid_value = 0x1580,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 40,
- .flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
- },
- [DPRC_GET_OBJ_COUNT] = {
- .cmdid_value = 0x1590,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 16,
- },
- [DPRC_GET_OBJ] = {
- .cmdid_value = 0x15A0,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 12,
- },
- [DPRC_GET_RES_COUNT] = {
- .cmdid_value = 0x15B0,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 32,
- },
- [DPRC_GET_RES_IDS] = {
- .cmdid_value = 0x15C0,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 40,
- },
- [DPRC_SET_OBJ_LABEL] = {
- .cmdid_value = 0x1610,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 48,
- .flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
- },
- [DPRC_SET_LOCKED] = {
- .cmdid_value = 0x16B0,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 16,
- .flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
- },
- [DPRC_CONNECT] = {
- .cmdid_value = 0x1670,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 56,
- .flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
- },
- [DPRC_DISCONNECT] = {
- .cmdid_value = 0x1680,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 32,
- .flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
- },
- [DPRC_GET_POOL] = {
- .cmdid_value = 0x1690,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 12,
- },
- [DPRC_GET_POOL_COUNT] = {
- .cmdid_value = 0x16A0,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 8,
- },
- [DPRC_GET_CONNECTION] = {
- .cmdid_value = 0x16C0,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 32,
- },
- [DPCI_GET_LINK_STATE] = {
- .cmdid_value = 0x0E10,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 8,
- },
- [DPCI_GET_PEER_ATTR] = {
- .cmdid_value = 0x0E20,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 8,
- },
- [DPAIOP_GET_SL_VERSION] = {
- .cmdid_value = 0x2820,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 8,
- },
- [DPAIOP_GET_STATE] = {
- .cmdid_value = 0x2830,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 8,
- },
- [DPMNG_GET_VERSION] = {
- .cmdid_value = 0x8310,
- .cmdid_mask = 0xFFF0,
- .token = false,
- .size = 8,
- },
- [DPSECI_GET_TX_QUEUE] = {
- .cmdid_value = 0x1970,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 14,
- },
- [DPMAC_GET_COUNTER] = {
- .cmdid_value = 0x0c40,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 9,
- },
- [DPMAC_GET_MAC_ADDR] = {
- .cmdid_value = 0x0c50,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 8,
- },
- [DPNI_SET_PRIM_MAC] = {
- .cmdid_value = 0x2240,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 16,
- .flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
- },
- [DPNI_GET_PRIM_MAC] = {
- .cmdid_value = 0x2250,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 8,
- },
- [DPNI_GET_STATISTICS] = {
- .cmdid_value = 0x25D0,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 10,
- },
- [DPNI_GET_LINK_STATE] = {
- .cmdid_value = 0x2150,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 8,
- },
- [DPNI_GET_MAX_FRAME_LENGTH] = {
- .cmdid_value = 0x2170,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 8,
- },
- [DPSW_GET_TAILDROP] = {
- .cmdid_value = 0x0A80,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 14,
- },
- [DPSW_SET_TAILDROP] = {
- .cmdid_value = 0x0A90,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 24,
- .flags = FSL_MC_CAP_NET_ADMIN_NEEDED,
- },
- [DPSW_IF_GET_COUNTER] = {
- .cmdid_value = 0x0340,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 11,
- },
- [DPSW_IF_GET_MAX_FRAME_LENGTH] = {
- .cmdid_value = 0x0450,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 10,
- },
- [DPDMUX_GET_COUNTER] = {
- .cmdid_value = 0x0b20,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 11,
- },
- [DPDMUX_IF_GET_MAX_FRAME_LENGTH] = {
- .cmdid_value = 0x0a20,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 10,
- },
- [GET_ATTR] = {
- .cmdid_value = 0x0040,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 8,
- },
- [GET_IRQ_MASK] = {
- .cmdid_value = 0x0150,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 13,
- },
- [GET_IRQ_STATUS] = {
- .cmdid_value = 0x0160,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 13,
- },
- [CLOSE] = {
- .cmdid_value = 0x8000,
- .cmdid_mask = 0xFFF0,
- .token = true,
- .size = 8,
- },
- /* Common commands amongst all types of objects. Must be checked last. */
- [OPEN] = {
- .cmdid_value = 0x8000,
- .cmdid_mask = 0xFC00,
- .token = false,
- .size = 12,
- .flags = FSL_MC_CHECK_MODULE_ID,
- },
- [GET_API_VERSION] = {
- .cmdid_value = 0xA000,
- .cmdid_mask = 0xFC00,
- .token = false,
- .size = 8,
- .flags = FSL_MC_CHECK_MODULE_ID,
- },
- [DESTROY] = {
- .cmdid_value = 0x9800,
- .cmdid_mask = 0xFC00,
- .token = true,
- .size = 12,
- .flags = FSL_MC_CHECK_MODULE_ID | FSL_MC_CAP_NET_ADMIN_NEEDED,
- },
- [CREATE] = {
- .cmdid_value = 0x9000,
- .cmdid_mask = 0xFC00,
- .token = true,
- .size = 64,
- .flags = FSL_MC_CHECK_MODULE_ID | FSL_MC_CAP_NET_ADMIN_NEEDED,
- },
- };
- #define FSL_MC_NUM_ACCEPTED_CMDS ARRAY_SIZE(fsl_mc_accepted_cmds)
- #define FSL_MC_MAX_MODULE_ID 0x10
- static int fsl_mc_command_check(struct fsl_mc_device *mc_dev,
- struct fsl_mc_command *mc_cmd)
- {
- struct fsl_mc_cmd_desc *desc = NULL;
- int mc_cmd_max_size, i;
- bool token_provided;
- u16 cmdid, module_id;
- char *mc_cmd_end;
- char sum = 0;
- /* Check if this is an accepted MC command */
- cmdid = mc_cmd_hdr_read_cmdid(mc_cmd);
- for (i = 0; i < FSL_MC_NUM_ACCEPTED_CMDS; i++) {
- desc = &fsl_mc_accepted_cmds[i];
- if ((cmdid & desc->cmdid_mask) == desc->cmdid_value)
- break;
- }
- if (i == FSL_MC_NUM_ACCEPTED_CMDS) {
- dev_err(&mc_dev->dev, "MC command 0x%04x: cmdid not accepted\n", cmdid);
- return -EACCES;
- }
- /* Check if the size of the command is honored. Anything beyond the
- * last valid byte of the command should be zeroed.
- */
- mc_cmd_max_size = sizeof(*mc_cmd);
- mc_cmd_end = ((char *)mc_cmd) + desc->size;
- for (i = desc->size; i < mc_cmd_max_size; i++)
- sum |= *mc_cmd_end++;
- if (sum) {
- dev_err(&mc_dev->dev, "MC command 0x%04x: garbage beyond max size of %d bytes!\n",
- cmdid, desc->size);
- return -EACCES;
- }
- /* Some MC commands request a token to be passed so that object
- * identification is possible. Check if the token passed in the command
- * is as expected.
- */
- token_provided = mc_cmd_hdr_read_token(mc_cmd) ? true : false;
- if (token_provided != desc->token) {
- dev_err(&mc_dev->dev, "MC command 0x%04x: token 0x%04x is invalid!\n",
- cmdid, mc_cmd_hdr_read_token(mc_cmd));
- return -EACCES;
- }
- /* If needed, check if the module ID passed is valid */
- if (desc->flags & FSL_MC_CHECK_MODULE_ID) {
- /* The module ID is represented by bits [4:9] from the cmdid */
- module_id = (cmdid & GENMASK(9, 4)) >> 4;
- if (module_id == 0 || module_id > FSL_MC_MAX_MODULE_ID) {
- dev_err(&mc_dev->dev, "MC command 0x%04x: unknown module ID 0x%x\n",
- cmdid, module_id);
- return -EACCES;
- }
- }
- /* Some commands alter how hardware resources are managed. For these
- * commands, check for CAP_NET_ADMIN.
- */
- if (desc->flags & FSL_MC_CAP_NET_ADMIN_NEEDED) {
- if (!capable(CAP_NET_ADMIN)) {
- dev_err(&mc_dev->dev, "MC command 0x%04x: needs CAP_NET_ADMIN!\n",
- cmdid);
- return -EPERM;
- }
- }
- return 0;
- }
- static int fsl_mc_uapi_send_command(struct fsl_mc_device *mc_dev, unsigned long arg,
- struct fsl_mc_io *mc_io)
- {
- struct fsl_mc_command mc_cmd;
- int error;
- error = copy_from_user(&mc_cmd, (void __user *)arg, sizeof(mc_cmd));
- if (error)
- return -EFAULT;
- error = fsl_mc_command_check(mc_dev, &mc_cmd);
- if (error)
- return error;
- error = mc_send_command(mc_io, &mc_cmd);
- if (error)
- return error;
- error = copy_to_user((void __user *)arg, &mc_cmd, sizeof(mc_cmd));
- if (error)
- return -EFAULT;
- return 0;
- }
- static int fsl_mc_uapi_dev_open(struct inode *inode, struct file *filep)
- {
- struct fsl_mc_device *root_mc_device;
- struct uapi_priv_data *priv_data;
- struct fsl_mc_io *dynamic_mc_io;
- struct fsl_mc_uapi *mc_uapi;
- struct fsl_mc_bus *mc_bus;
- int error;
- priv_data = kzalloc(sizeof(*priv_data), GFP_KERNEL);
- if (!priv_data)
- return -ENOMEM;
- mc_uapi = container_of(filep->private_data, struct fsl_mc_uapi, misc);
- mc_bus = container_of(mc_uapi, struct fsl_mc_bus, uapi_misc);
- root_mc_device = &mc_bus->mc_dev;
- mutex_lock(&mc_uapi->mutex);
- if (!mc_uapi->local_instance_in_use) {
- priv_data->mc_io = mc_uapi->static_mc_io;
- mc_uapi->local_instance_in_use = 1;
- } else {
- error = fsl_mc_portal_allocate(root_mc_device, 0,
- &dynamic_mc_io);
- if (error) {
- dev_dbg(&root_mc_device->dev,
- "Could not allocate MC portal\n");
- goto error_portal_allocate;
- }
- priv_data->mc_io = dynamic_mc_io;
- }
- priv_data->uapi = mc_uapi;
- filep->private_data = priv_data;
- mutex_unlock(&mc_uapi->mutex);
- return 0;
- error_portal_allocate:
- mutex_unlock(&mc_uapi->mutex);
- kfree(priv_data);
- return error;
- }
- static int fsl_mc_uapi_dev_release(struct inode *inode, struct file *filep)
- {
- struct uapi_priv_data *priv_data;
- struct fsl_mc_uapi *mc_uapi;
- struct fsl_mc_io *mc_io;
- priv_data = filep->private_data;
- mc_uapi = priv_data->uapi;
- mc_io = priv_data->mc_io;
- mutex_lock(&mc_uapi->mutex);
- if (mc_io == mc_uapi->static_mc_io)
- mc_uapi->local_instance_in_use = 0;
- else
- fsl_mc_portal_free(mc_io);
- kfree(filep->private_data);
- filep->private_data = NULL;
- mutex_unlock(&mc_uapi->mutex);
- return 0;
- }
- static long fsl_mc_uapi_dev_ioctl(struct file *file,
- unsigned int cmd,
- unsigned long arg)
- {
- struct uapi_priv_data *priv_data = file->private_data;
- struct fsl_mc_device *root_mc_device;
- struct fsl_mc_bus *mc_bus;
- int error;
- mc_bus = container_of(priv_data->uapi, struct fsl_mc_bus, uapi_misc);
- root_mc_device = &mc_bus->mc_dev;
- switch (cmd) {
- case FSL_MC_SEND_MC_COMMAND:
- error = fsl_mc_uapi_send_command(root_mc_device, arg, priv_data->mc_io);
- break;
- default:
- dev_dbg(&root_mc_device->dev, "unexpected ioctl call number\n");
- error = -EINVAL;
- }
- return error;
- }
- static const struct file_operations fsl_mc_uapi_dev_fops = {
- .owner = THIS_MODULE,
- .open = fsl_mc_uapi_dev_open,
- .release = fsl_mc_uapi_dev_release,
- .unlocked_ioctl = fsl_mc_uapi_dev_ioctl,
- };
- int fsl_mc_uapi_create_device_file(struct fsl_mc_bus *mc_bus)
- {
- struct fsl_mc_device *mc_dev = &mc_bus->mc_dev;
- struct fsl_mc_uapi *mc_uapi = &mc_bus->uapi_misc;
- int error;
- mc_uapi->misc.minor = MISC_DYNAMIC_MINOR;
- mc_uapi->misc.name = dev_name(&mc_dev->dev);
- mc_uapi->misc.fops = &fsl_mc_uapi_dev_fops;
- error = misc_register(&mc_uapi->misc);
- if (error)
- return error;
- mc_uapi->static_mc_io = mc_bus->mc_dev.mc_io;
- mutex_init(&mc_uapi->mutex);
- return 0;
- }
- void fsl_mc_uapi_remove_device_file(struct fsl_mc_bus *mc_bus)
- {
- misc_deregister(&mc_bus->uapi_misc.misc);
- }
|