xref: /openbmc/linux/drivers/bus/fsl-mc/fsl-mc-uapi.c (revision d67cc29e)
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 	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 
80 static 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 
fsl_mc_command_check(struct fsl_mc_device * mc_dev,struct fsl_mc_command * mc_cmd)374 static 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 
fsl_mc_uapi_send_command(struct fsl_mc_device * mc_dev,unsigned long arg,struct fsl_mc_io * mc_io)445 static 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 
fsl_mc_uapi_dev_open(struct inode * inode,struct file * filep)470 static 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 
510 error_portal_allocate:
511 	mutex_unlock(&mc_uapi->mutex);
512 	kfree(priv_data);
513 
514 	return error;
515 }
516 
fsl_mc_uapi_dev_release(struct inode * inode,struct file * filep)517 static 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 
fsl_mc_uapi_dev_ioctl(struct file * file,unsigned int cmd,unsigned long arg)542 static 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 
566 static 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 
fsl_mc_uapi_create_device_file(struct fsl_mc_bus * mc_bus)573 int 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 
fsl_mc_uapi_remove_device_file(struct fsl_mc_bus * mc_bus)594 void fsl_mc_uapi_remove_device_file(struct fsl_mc_bus *mc_bus)
595 {
596 	misc_deregister(&mc_bus->uapi_misc.misc);
597 }
598