fsl-mc-uapi.c 12 KB

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