File Index
Files and directories
Sign in
FILE

fsl-mc-uapi.c

File preview with syntax highlighting.

linux-6.19.14/drivers/bus/fsl-mc/fsl-mc-uapi.c C
  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	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
 81static 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
381static 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
452static 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
477static 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
517error_portal_allocate:
518	mutex_unlock(&mc_uapi->mutex);
519	kfree(priv_data);
520
521	return error;
522}
523
524static 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
549static 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
573static 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
580int 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
601void fsl_mc_uapi_remove_device_file(struct fsl_mc_bus *mc_bus)
602{
603	misc_deregister(&mc_bus->uapi_misc.misc);
604}
~/ Read-only file preview.