fsl-mc-uapi.c (12766B)
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}