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 16 struct uapi_priv_data { 17 struct fsl_mc_uapi *uapi; 18 struct fsl_mc_io *mc_io; 19 }; 20 21 struct 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 32 enum 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 DPRC_GET_MEM, 52 DPCI_GET_LINK_STATE, 53 DPCI_GET_PEER_ATTR, 54 DPAIOP_GET_SL_VERSION, 55 DPAIOP_GET_STATE, 56 DPMNG_GET_VERSION, 57 DPSECI_GET_TX_QUEUE, 58 DPMAC_GET_COUNTER, 59 DPMAC_GET_MAC_ADDR, 60 DPNI_SET_PRIM_MAC, 61 DPNI_GET_PRIM_MAC, 62 DPNI_GET_STATISTICS, 63 DPNI_GET_LINK_STATE, 64 DPNI_GET_MAX_FRAME_LENGTH, 65 DPSW_GET_TAILDROP, 66 DPSW_SET_TAILDROP, 67 DPSW_IF_GET_COUNTER, 68 DPSW_IF_GET_MAX_FRAME_LENGTH, 69 DPDMUX_GET_COUNTER, 70 DPDMUX_IF_GET_MAX_FRAME_LENGTH, 71 GET_ATTR, 72 GET_IRQ_MASK, 73 GET_IRQ_STATUS, 74 CLOSE, 75 OPEN, 76 GET_API_VERSION, 77 DESTROY, 78 CREATE, 79 }; 80 81 static struct fsl_mc_cmd_desc fsl_mc_accepted_cmds[] = { 82 [DPDBG_DUMP] = { 83 .cmdid_value = 0x1300, 84 .cmdid_mask = 0xFFF0, 85 .token = true, 86 .size = 28, 87 }, 88 [DPDBG_SET] = { 89 .cmdid_value = 0x1400, 90 .cmdid_mask = 0xFFF0, 91 .token = true, 92 .size = 28, 93 }, 94 [DPRC_GET_CONTAINER_ID] = { 95 .cmdid_value = 0x8300, 96 .cmdid_mask = 0xFFF0, 97 .token = false, 98 .size = 8, 99 }, 100 [DPRC_CREATE_CONT] = { 101 .cmdid_value = 0x1510, 102 .cmdid_mask = 0xFFF0, 103 .token = true, 104 .size = 40, 105 .flags = FSL_MC_CAP_NET_ADMIN_NEEDED, 106 }, 107 [DPRC_DESTROY_CONT] = { 108 .cmdid_value = 0x1520, 109 .cmdid_mask = 0xFFF0, 110 .token = true, 111 .size = 12, 112 .flags = FSL_MC_CAP_NET_ADMIN_NEEDED, 113 }, 114 [DPRC_ASSIGN] = { 115 .cmdid_value = 0x1570, 116 .cmdid_mask = 0xFFF0, 117 .token = true, 118 .size = 40, 119 .flags = FSL_MC_CAP_NET_ADMIN_NEEDED, 120 }, 121 [DPRC_UNASSIGN] = { 122 .cmdid_value = 0x1580, 123 .cmdid_mask = 0xFFF0, 124 .token = true, 125 .size = 40, 126 .flags = FSL_MC_CAP_NET_ADMIN_NEEDED, 127 }, 128 [DPRC_GET_OBJ_COUNT] = { 129 .cmdid_value = 0x1590, 130 .cmdid_mask = 0xFFF0, 131 .token = true, 132 .size = 16, 133 }, 134 [DPRC_GET_OBJ] = { 135 .cmdid_value = 0x15A0, 136 .cmdid_mask = 0xFFF0, 137 .token = true, 138 .size = 12, 139 }, 140 [DPRC_GET_RES_COUNT] = { 141 .cmdid_value = 0x15B0, 142 .cmdid_mask = 0xFFF0, 143 .token = true, 144 .size = 32, 145 }, 146 [DPRC_GET_RES_IDS] = { 147 .cmdid_value = 0x15C0, 148 .cmdid_mask = 0xFFF0, 149 .token = true, 150 .size = 40, 151 }, 152 [DPRC_SET_OBJ_LABEL] = { 153 .cmdid_value = 0x1610, 154 .cmdid_mask = 0xFFF0, 155 .token = true, 156 .size = 48, 157 .flags = FSL_MC_CAP_NET_ADMIN_NEEDED, 158 }, 159 [DPRC_SET_LOCKED] = { 160 .cmdid_value = 0x16B0, 161 .cmdid_mask = 0xFFF0, 162 .token = true, 163 .size = 16, 164 .flags = FSL_MC_CAP_NET_ADMIN_NEEDED, 165 }, 166 [DPRC_CONNECT] = { 167 .cmdid_value = 0x1670, 168 .cmdid_mask = 0xFFF0, 169 .token = true, 170 .size = 56, 171 .flags = FSL_MC_CAP_NET_ADMIN_NEEDED, 172 }, 173 [DPRC_DISCONNECT] = { 174 .cmdid_value = 0x1680, 175 .cmdid_mask = 0xFFF0, 176 .token = true, 177 .size = 32, 178 .flags = FSL_MC_CAP_NET_ADMIN_NEEDED, 179 }, 180 [DPRC_GET_POOL] = { 181 .cmdid_value = 0x1690, 182 .cmdid_mask = 0xFFF0, 183 .token = true, 184 .size = 12, 185 }, 186 [DPRC_GET_POOL_COUNT] = { 187 .cmdid_value = 0x16A0, 188 .cmdid_mask = 0xFFF0, 189 .token = true, 190 .size = 8, 191 }, 192 [DPRC_GET_CONNECTION] = { 193 .cmdid_value = 0x16C0, 194 .cmdid_mask = 0xFFF0, 195 .token = true, 196 .size = 32, 197 }, 198 [DPRC_GET_MEM] = { 199 .cmdid_value = 0x16D0, 200 .cmdid_mask = 0xFFF0, 201 .token = true, 202 .size = 12, 203 }, 204 205 [DPCI_GET_LINK_STATE] = { 206 .cmdid_value = 0x0E10, 207 .cmdid_mask = 0xFFF0, 208 .token = true, 209 .size = 8, 210 }, 211 [DPCI_GET_PEER_ATTR] = { 212 .cmdid_value = 0x0E20, 213 .cmdid_mask = 0xFFF0, 214 .token = true, 215 .size = 8, 216 }, 217 [DPAIOP_GET_SL_VERSION] = { 218 .cmdid_value = 0x2820, 219 .cmdid_mask = 0xFFF0, 220 .token = true, 221 .size = 8, 222 }, 223 [DPAIOP_GET_STATE] = { 224 .cmdid_value = 0x2830, 225 .cmdid_mask = 0xFFF0, 226 .token = true, 227 .size = 8, 228 }, 229 [DPMNG_GET_VERSION] = { 230 .cmdid_value = 0x8310, 231 .cmdid_mask = 0xFFF0, 232 .token = false, 233 .size = 8, 234 }, 235 [DPSECI_GET_TX_QUEUE] = { 236 .cmdid_value = 0x1970, 237 .cmdid_mask = 0xFFF0, 238 .token = true, 239 .size = 14, 240 }, 241 [DPMAC_GET_COUNTER] = { 242 .cmdid_value = 0x0c40, 243 .cmdid_mask = 0xFFF0, 244 .token = true, 245 .size = 9, 246 }, 247 [DPMAC_GET_MAC_ADDR] = { 248 .cmdid_value = 0x0c50, 249 .cmdid_mask = 0xFFF0, 250 .token = true, 251 .size = 8, 252 }, 253 [DPNI_SET_PRIM_MAC] = { 254 .cmdid_value = 0x2240, 255 .cmdid_mask = 0xFFF0, 256 .token = true, 257 .size = 16, 258 .flags = FSL_MC_CAP_NET_ADMIN_NEEDED, 259 }, 260 [DPNI_GET_PRIM_MAC] = { 261 .cmdid_value = 0x2250, 262 .cmdid_mask = 0xFFF0, 263 .token = true, 264 .size = 8, 265 }, 266 [DPNI_GET_STATISTICS] = { 267 .cmdid_value = 0x25D0, 268 .cmdid_mask = 0xFFF0, 269 .token = true, 270 .size = 10, 271 }, 272 [DPNI_GET_LINK_STATE] = { 273 .cmdid_value = 0x2150, 274 .cmdid_mask = 0xFFF0, 275 .token = true, 276 .size = 8, 277 }, 278 [DPNI_GET_MAX_FRAME_LENGTH] = { 279 .cmdid_value = 0x2170, 280 .cmdid_mask = 0xFFF0, 281 .token = true, 282 .size = 8, 283 }, 284 [DPSW_GET_TAILDROP] = { 285 .cmdid_value = 0x0A90, 286 .cmdid_mask = 0xFFF0, 287 .token = true, 288 .size = 14, 289 }, 290 [DPSW_SET_TAILDROP] = { 291 .cmdid_value = 0x0A80, 292 .cmdid_mask = 0xFFF0, 293 .token = true, 294 .size = 24, 295 .flags = FSL_MC_CAP_NET_ADMIN_NEEDED, 296 }, 297 [DPSW_IF_GET_COUNTER] = { 298 .cmdid_value = 0x0340, 299 .cmdid_mask = 0xFFF0, 300 .token = true, 301 .size = 11, 302 }, 303 [DPSW_IF_GET_MAX_FRAME_LENGTH] = { 304 .cmdid_value = 0x0450, 305 .cmdid_mask = 0xFFF0, 306 .token = true, 307 .size = 10, 308 }, 309 [DPDMUX_GET_COUNTER] = { 310 .cmdid_value = 0x0b20, 311 .cmdid_mask = 0xFFF0, 312 .token = true, 313 .size = 11, 314 }, 315 [DPDMUX_IF_GET_MAX_FRAME_LENGTH] = { 316 .cmdid_value = 0x0a20, 317 .cmdid_mask = 0xFFF0, 318 .token = true, 319 .size = 10, 320 }, 321 [GET_ATTR] = { 322 .cmdid_value = 0x0040, 323 .cmdid_mask = 0xFFF0, 324 .token = true, 325 .size = 8, 326 }, 327 [GET_IRQ_MASK] = { 328 .cmdid_value = 0x0150, 329 .cmdid_mask = 0xFFF0, 330 .token = true, 331 .size = 13, 332 }, 333 [GET_IRQ_STATUS] = { 334 .cmdid_value = 0x0160, 335 .cmdid_mask = 0xFFF0, 336 .token = true, 337 .size = 13, 338 }, 339 [CLOSE] = { 340 .cmdid_value = 0x8000, 341 .cmdid_mask = 0xFFF0, 342 .token = true, 343 .size = 8, 344 }, 345 346 /* Common commands amongst all types of objects. Must be checked last. */ 347 [OPEN] = { 348 .cmdid_value = 0x8000, 349 .cmdid_mask = 0xFC00, 350 .token = false, 351 .size = 12, 352 .flags = FSL_MC_CHECK_MODULE_ID, 353 }, 354 [GET_API_VERSION] = { 355 .cmdid_value = 0xA000, 356 .cmdid_mask = 0xFC00, 357 .token = false, 358 .size = 8, 359 .flags = FSL_MC_CHECK_MODULE_ID, 360 }, 361 [DESTROY] = { 362 .cmdid_value = 0x9800, 363 .cmdid_mask = 0xFC00, 364 .token = true, 365 .size = 12, 366 .flags = FSL_MC_CHECK_MODULE_ID | FSL_MC_CAP_NET_ADMIN_NEEDED, 367 }, 368 [CREATE] = { 369 .cmdid_value = 0x9000, 370 .cmdid_mask = 0xFC00, 371 .token = true, 372 .size = 64, 373 .flags = FSL_MC_CHECK_MODULE_ID | FSL_MC_CAP_NET_ADMIN_NEEDED, 374 }, 375 }; 376 377 #define FSL_MC_NUM_ACCEPTED_CMDS ARRAY_SIZE(fsl_mc_accepted_cmds) 378 379 #define FSL_MC_MAX_MODULE_ID 0x10 380 381 static int fsl_mc_command_check(struct fsl_mc_device *mc_dev, 382 struct fsl_mc_command *mc_cmd) 383 { 384 struct fsl_mc_cmd_desc *desc = NULL; 385 int mc_cmd_max_size, i; 386 bool token_provided; 387 u16 cmdid, module_id; 388 char *mc_cmd_end; 389 char sum = 0; 390 391 /* Check if this is an accepted MC command */ 392 cmdid = mc_cmd_hdr_read_cmdid(mc_cmd); 393 for (i = 0; i < FSL_MC_NUM_ACCEPTED_CMDS; i++) { 394 desc = &fsl_mc_accepted_cmds[i]; 395 if ((cmdid & desc->cmdid_mask) == desc->cmdid_value) 396 break; 397 } 398 if (i == FSL_MC_NUM_ACCEPTED_CMDS) { 399 dev_err(&mc_dev->dev, "MC command 0x%04x: cmdid not accepted\n", cmdid); 400 return -EACCES; 401 } 402 403 /* Check if the size of the command is honored. Anything beyond the 404 * last valid byte of the command should be zeroed. 405 */ 406 mc_cmd_max_size = sizeof(*mc_cmd); 407 mc_cmd_end = ((char *)mc_cmd) + desc->size; 408 for (i = desc->size; i < mc_cmd_max_size; i++) 409 sum |= *mc_cmd_end++; 410 if (sum) { 411 dev_err(&mc_dev->dev, "MC command 0x%04x: garbage beyond max size of %d bytes!\n", 412 cmdid, desc->size); 413 return -EACCES; 414 } 415 416 /* Some MC commands request a token to be passed so that object 417 * identification is possible. Check if the token passed in the command 418 * is as expected. 419 */ 420 token_provided = mc_cmd_hdr_read_token(mc_cmd) ? true : false; 421 if (token_provided != desc->token) { 422 dev_err(&mc_dev->dev, "MC command 0x%04x: token 0x%04x is invalid!\n", 423 cmdid, mc_cmd_hdr_read_token(mc_cmd)); 424 return -EACCES; 425 } 426 427 /* If needed, check if the module ID passed is valid */ 428 if (desc->flags & FSL_MC_CHECK_MODULE_ID) { 429 /* The module ID is represented by bits [4:9] from the cmdid */ 430 module_id = (cmdid & GENMASK(9, 4)) >> 4; 431 if (module_id == 0 || module_id > FSL_MC_MAX_MODULE_ID) { 432 dev_err(&mc_dev->dev, "MC command 0x%04x: unknown module ID 0x%x\n", 433 cmdid, module_id); 434 return -EACCES; 435 } 436 } 437 438 /* Some commands alter how hardware resources are managed. For these 439 * commands, check for CAP_NET_ADMIN. 440 */ 441 if (desc->flags & FSL_MC_CAP_NET_ADMIN_NEEDED) { 442 if (!capable(CAP_NET_ADMIN)) { 443 dev_err(&mc_dev->dev, "MC command 0x%04x: needs CAP_NET_ADMIN!\n", 444 cmdid); 445 return -EPERM; 446 } 447 } 448 449 return 0; 450 } 451 452 static int fsl_mc_uapi_send_command(struct fsl_mc_device *mc_dev, unsigned long arg, 453 struct fsl_mc_io *mc_io) 454 { 455 struct fsl_mc_command mc_cmd; 456 int error; 457 458 error = copy_from_user(&mc_cmd, (void __user *)arg, sizeof(mc_cmd)); 459 if (error) 460 return -EFAULT; 461 462 error = fsl_mc_command_check(mc_dev, &mc_cmd); 463 if (error) 464 return error; 465 466 error = mc_send_command(mc_io, &mc_cmd); 467 if (error) 468 return error; 469 470 error = copy_to_user((void __user *)arg, &mc_cmd, sizeof(mc_cmd)); 471 if (error) 472 return -EFAULT; 473 474 return 0; 475 } 476 477 static int fsl_mc_uapi_dev_open(struct inode *inode, struct file *filep) 478 { 479 struct fsl_mc_device *root_mc_device; 480 struct uapi_priv_data *priv_data; 481 struct fsl_mc_io *dynamic_mc_io; 482 struct fsl_mc_uapi *mc_uapi; 483 struct fsl_mc_bus *mc_bus; 484 int error; 485 486 priv_data = kzalloc(sizeof(*priv_data), GFP_KERNEL); 487 if (!priv_data) 488 return -ENOMEM; 489 490 mc_uapi = container_of(filep->private_data, struct fsl_mc_uapi, misc); 491 mc_bus = container_of(mc_uapi, struct fsl_mc_bus, uapi_misc); 492 root_mc_device = &mc_bus->mc_dev; 493 494 mutex_lock(&mc_uapi->mutex); 495 496 if (!mc_uapi->local_instance_in_use) { 497 priv_data->mc_io = mc_uapi->static_mc_io; 498 mc_uapi->local_instance_in_use = 1; 499 } else { 500 error = fsl_mc_portal_allocate(root_mc_device, 0, 501 &dynamic_mc_io); 502 if (error) { 503 dev_dbg(&root_mc_device->dev, 504 "Could not allocate MC portal\n"); 505 goto error_portal_allocate; 506 } 507 508 priv_data->mc_io = dynamic_mc_io; 509 } 510 priv_data->uapi = mc_uapi; 511 filep->private_data = priv_data; 512 513 mutex_unlock(&mc_uapi->mutex); 514 515 return 0; 516 517 error_portal_allocate: 518 mutex_unlock(&mc_uapi->mutex); 519 kfree(priv_data); 520 521 return error; 522 } 523 524 static int fsl_mc_uapi_dev_release(struct inode *inode, struct file *filep) 525 { 526 struct uapi_priv_data *priv_data; 527 struct fsl_mc_uapi *mc_uapi; 528 struct fsl_mc_io *mc_io; 529 530 priv_data = filep->private_data; 531 mc_uapi = priv_data->uapi; 532 mc_io = priv_data->mc_io; 533 534 mutex_lock(&mc_uapi->mutex); 535 536 if (mc_io == mc_uapi->static_mc_io) 537 mc_uapi->local_instance_in_use = 0; 538 else 539 fsl_mc_portal_free(mc_io); 540 541 kfree(filep->private_data); 542 filep->private_data = NULL; 543 544 mutex_unlock(&mc_uapi->mutex); 545 546 return 0; 547 } 548 549 static long fsl_mc_uapi_dev_ioctl(struct file *file, 550 unsigned int cmd, 551 unsigned long arg) 552 { 553 struct uapi_priv_data *priv_data = file->private_data; 554 struct fsl_mc_device *root_mc_device; 555 struct fsl_mc_bus *mc_bus; 556 int error; 557 558 mc_bus = container_of(priv_data->uapi, struct fsl_mc_bus, uapi_misc); 559 root_mc_device = &mc_bus->mc_dev; 560 561 switch (cmd) { 562 case FSL_MC_SEND_MC_COMMAND: 563 error = fsl_mc_uapi_send_command(root_mc_device, arg, priv_data->mc_io); 564 break; 565 default: 566 dev_dbg(&root_mc_device->dev, "unexpected ioctl call number\n"); 567 error = -EINVAL; 568 } 569 570 return error; 571 } 572 573 static const struct file_operations fsl_mc_uapi_dev_fops = { 574 .owner = THIS_MODULE, 575 .open = fsl_mc_uapi_dev_open, 576 .release = fsl_mc_uapi_dev_release, 577 .unlocked_ioctl = fsl_mc_uapi_dev_ioctl, 578 }; 579 580 int fsl_mc_uapi_create_device_file(struct fsl_mc_bus *mc_bus) 581 { 582 struct fsl_mc_device *mc_dev = &mc_bus->mc_dev; 583 struct fsl_mc_uapi *mc_uapi = &mc_bus->uapi_misc; 584 int error; 585 586 mc_uapi->misc.minor = MISC_DYNAMIC_MINOR; 587 mc_uapi->misc.name = dev_name(&mc_dev->dev); 588 mc_uapi->misc.fops = &fsl_mc_uapi_dev_fops; 589 590 error = misc_register(&mc_uapi->misc); 591 if (error) 592 return error; 593 594 mc_uapi->static_mc_io = mc_bus->mc_dev.mc_io; 595 596 mutex_init(&mc_uapi->mutex); 597 598 return 0; 599 } 600 601 void fsl_mc_uapi_remove_device_file(struct fsl_mc_bus *mc_bus) 602 { 603 misc_deregister(&mc_bus->uapi_misc.misc); 604 } 605