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(cmd: 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(cmd: 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(to: &mc_cmd, from: (void __user *)arg, n: sizeof(mc_cmd));
452 if (error)
453 return -EFAULT;
454
455 error = fsl_mc_command_check(mc_dev, mc_cmd: &mc_cmd);
456 if (error)
457 return error;
458
459 error = mc_send_command(mc_io, cmd: &mc_cmd);
460 if (error)
461 return error;
462
463 error = copy_to_user(to: (void __user *)arg, from: &mc_cmd, n: 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(size: 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(mc_dev: root_mc_device, mc_io_flags: 0,
494 new_mc_io: &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(lock: &mc_uapi->mutex);
507
508 return 0;
509
510error_portal_allocate:
511 mutex_unlock(lock: &mc_uapi->mutex);
512 kfree(objp: 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(objp: filep->private_data);
535 filep->private_data = NULL;
536
537 mutex_unlock(lock: &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(mc_dev: root_mc_device, arg, mc_io: 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(dev: &mc_dev->dev);
581 mc_uapi->misc.fops = &fsl_mc_uapi_dev_fops;
582
583 error = misc_register(misc: &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(misc: &mc_bus->uapi_misc.misc);
597}
598

source code of linux/drivers/bus/fsl-mc/fsl-mc-uapi.c