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