brintos

brintos / linux-shallow public Read only

0
0
Text · 12.5 KiB · 9c4c139 Raw
598 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Management Complex (MC) userspace support4 *5 * Copyright 2021 NXP6 *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 0x10373 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(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 the397	 * 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 object410	 * identification is possible. Check if the token passed in the command411	 * 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 these432	 * 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(&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 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(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 510error_portal_allocate:511	mutex_unlock(&mc_uapi->mutex);512	kfree(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	else532		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 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(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 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(&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 594void fsl_mc_uapi_remove_device_file(struct fsl_mc_bus *mc_bus)595{596	misc_deregister(&mc_bus->uapi_misc.misc);597}598