summaryrefslogtreecommitdiffstats
path: root/drivers/misc/mei/main.c
diff options
context:
space:
mode:
authorAlexander Usyskin <alexander.usyskin@intel.com>2020-08-18 13:51:45 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2020-08-18 15:44:44 +0200
commitaa207a05f95abc3530b7415232f0f73278336bd3 (patch)
tree89887b2fb40849e19b457fb9fb99d34088416620 /drivers/misc/mei/main.c
parentmei: bus: unconditionally enable clients with vtag support (diff)
downloadlinux-aa207a05f95abc3530b7415232f0f73278336bd3.tar.xz
linux-aa207a05f95abc3530b7415232f0f73278336bd3.zip
mei: add connect with vtag ioctl
This IOCTL is used to associate the current file descriptor with a FW Client (given by UUID), and virtual tag (vtag). The IOCTL opens a communication channel between a host client and a FW client on a tagged channel. From this point on, every reader and write will communicate with the associated FW client on the tagged channel. Upon close() the communication is terminated. The IOCTL argument is a struct with a union that contains the input parameter and the output parameter for this IOCTL. The input parameter is UUID of the FW Client, a vtag [0,255] The output parameter is the properties of the FW client Clients that do not support tagged connection will respond with -EOPNOTSUPP Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com> Signed-off-by: Tomas Winkler <tomas.winkler@intel.com> Link: https://lore.kernel.org/r/20200818115147.2567012-12-tomas.winkler@intel.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/misc/mei/main.c')
-rw-r--r--drivers/misc/mei/main.c210
1 files changed, 194 insertions, 16 deletions
diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c
index 401bf8743689..9f6682033ed7 100644
--- a/drivers/misc/mei/main.c
+++ b/drivers/misc/mei/main.c
@@ -395,17 +395,18 @@ out:
* mei_ioctl_connect_client - the connect to fw client IOCTL function
*
* @file: private data of the file object
- * @data: IOCTL connect data, input and output parameters
+ * @in_client_uuid: requested UUID for connection
+ * @client: IOCTL connect data, output parameters
*
* Locking: called under "dev->device_lock" lock
*
* Return: 0 on success, <0 on failure.
*/
static int mei_ioctl_connect_client(struct file *file,
- struct mei_connect_client_data *data)
+ const uuid_le *in_client_uuid,
+ struct mei_client *client)
{
struct mei_device *dev;
- struct mei_client *client;
struct mei_me_client *me_cl;
struct mei_cl *cl;
int rets;
@@ -413,18 +414,15 @@ static int mei_ioctl_connect_client(struct file *file,
cl = file->private_data;
dev = cl->dev;
- if (dev->dev_state != MEI_DEV_ENABLED)
- return -ENODEV;
-
if (cl->state != MEI_FILE_INITIALIZING &&
cl->state != MEI_FILE_DISCONNECTED)
return -EBUSY;
/* find ME client we're trying to connect to */
- me_cl = mei_me_cl_by_uuid(dev, &data->in_client_uuid);
+ me_cl = mei_me_cl_by_uuid(dev, in_client_uuid);
if (!me_cl) {
dev_dbg(dev->dev, "Cannot connect to FW Client UUID = %pUl\n",
- &data->in_client_uuid);
+ in_client_uuid);
rets = -ENOTTY;
goto end;
}
@@ -434,7 +432,7 @@ static int mei_ioctl_connect_client(struct file *file,
!dev->allow_fixed_address : !dev->hbm_f_fa_supported;
if (forbidden) {
dev_dbg(dev->dev, "Connection forbidden to FW Client UUID = %pUl\n",
- &data->in_client_uuid);
+ in_client_uuid);
rets = -ENOTTY;
goto end;
}
@@ -448,7 +446,6 @@ static int mei_ioctl_connect_client(struct file *file,
me_cl->props.max_msg_length);
/* prepare the output buffer */
- client = &data->out_client_properties;
client->max_msg_length = me_cl->props.max_msg_length;
client->protocol_version = me_cl->props.protocol_version;
dev_dbg(dev->dev, "Can connect?\n");
@@ -461,6 +458,135 @@ end:
}
/**
+ * mei_vt_support_check - check if client support vtags
+ *
+ * Locking: called under "dev->device_lock" lock
+ *
+ * @dev: mei_device
+ * @uuid: client UUID
+ *
+ * Return:
+ * 0 - supported
+ * -ENOTTY - no such client
+ * -EOPNOTSUPP - vtags are not supported by client
+ */
+static int mei_vt_support_check(struct mei_device *dev, const uuid_le *uuid)
+{
+ struct mei_me_client *me_cl;
+ int ret;
+
+ if (!dev->hbm_f_vt_supported)
+ return -EOPNOTSUPP;
+
+ me_cl = mei_me_cl_by_uuid(dev, uuid);
+ if (!me_cl) {
+ dev_dbg(dev->dev, "Cannot connect to FW Client UUID = %pUl\n",
+ uuid);
+ return -ENOTTY;
+ }
+ ret = me_cl->props.vt_supported ? 0 : -EOPNOTSUPP;
+ mei_me_cl_put(me_cl);
+
+ return ret;
+}
+
+/**
+ * mei_ioctl_connect_vtag - connect to fw client with vtag IOCTL function
+ *
+ * @file: private data of the file object
+ * @in_client_uuid: requested UUID for connection
+ * @client: IOCTL connect data, output parameters
+ * @vtag: vm tag
+ *
+ * Locking: called under "dev->device_lock" lock
+ *
+ * Return: 0 on success, <0 on failure.
+ */
+static int mei_ioctl_connect_vtag(struct file *file,
+ const uuid_le *in_client_uuid,
+ struct mei_client *client,
+ u8 vtag)
+{
+ struct mei_device *dev;
+ struct mei_cl *cl;
+ struct mei_cl *pos;
+ struct mei_cl_vtag *cl_vtag;
+
+ cl = file->private_data;
+ dev = cl->dev;
+
+ dev_dbg(dev->dev, "FW Client %pUl vtag %d\n", in_client_uuid, vtag);
+
+ switch (cl->state) {
+ case MEI_FILE_DISCONNECTED:
+ if (mei_cl_vtag_by_fp(cl, file) != vtag) {
+ dev_err(dev->dev, "reconnect with different vtag\n");
+ return -EINVAL;
+ }
+ break;
+ case MEI_FILE_INITIALIZING:
+ /* malicious connect from another thread may push vtag */
+ if (!IS_ERR(mei_cl_fp_by_vtag(cl, vtag))) {
+ dev_err(dev->dev, "vtag already filled\n");
+ return -EINVAL;
+ }
+
+ list_for_each_entry(pos, &dev->file_list, link) {
+ if (pos == cl)
+ continue;
+ if (!pos->me_cl)
+ continue;
+
+ /* only search for same UUID */
+ if (uuid_le_cmp(*mei_cl_uuid(pos), *in_client_uuid))
+ continue;
+
+ /* if tag already exist try another fp */
+ if (!IS_ERR(mei_cl_fp_by_vtag(pos, vtag)))
+ continue;
+
+ /* replace cl with acquired one */
+ dev_dbg(dev->dev, "replacing with existing cl\n");
+ mei_cl_unlink(cl);
+ kfree(cl);
+ file->private_data = pos;
+ cl = pos;
+ break;
+ }
+
+ cl_vtag = mei_cl_vtag_alloc(file, vtag);
+ if (IS_ERR(cl_vtag))
+ return -ENOMEM;
+
+ list_add_tail(&cl_vtag->list, &cl->vtag_map);
+ break;
+ default:
+ return -EBUSY;
+ }
+
+ while (cl->state != MEI_FILE_INITIALIZING &&
+ cl->state != MEI_FILE_DISCONNECTED &&
+ cl->state != MEI_FILE_CONNECTED) {
+ mutex_unlock(&dev->device_lock);
+ wait_event_timeout(cl->wait,
+ (cl->state == MEI_FILE_CONNECTED ||
+ cl->state == MEI_FILE_DISCONNECTED ||
+ cl->state == MEI_FILE_DISCONNECT_REQUIRED ||
+ cl->state == MEI_FILE_DISCONNECT_REPLY),
+ mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
+ mutex_lock(&dev->device_lock);
+ }
+
+ if (!mei_cl_is_connected(cl))
+ return mei_ioctl_connect_client(file, in_client_uuid, client);
+
+ client->max_msg_length = cl->me_cl->props.max_msg_length;
+ client->protocol_version = cl->me_cl->props.protocol_version;
+
+ return 0;
+}
+
+/**
* mei_ioctl_client_notify_request -
* propagate event notification request to client
*
@@ -516,7 +642,11 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data)
{
struct mei_device *dev;
struct mei_cl *cl = file->private_data;
- struct mei_connect_client_data connect_data;
+ struct mei_connect_client_data conn;
+ struct mei_connect_client_data_vtag conn_vtag;
+ const uuid_le *cl_uuid;
+ struct mei_client *props;
+ u8 vtag;
u32 notify_get, notify_req;
int rets;
@@ -537,20 +667,68 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data)
switch (cmd) {
case IOCTL_MEI_CONNECT_CLIENT:
dev_dbg(dev->dev, ": IOCTL_MEI_CONNECT_CLIENT.\n");
- if (copy_from_user(&connect_data, (char __user *)data,
- sizeof(connect_data))) {
+ if (copy_from_user(&conn, (char __user *)data, sizeof(conn))) {
+ dev_dbg(dev->dev, "failed to copy data from userland\n");
+ rets = -EFAULT;
+ goto out;
+ }
+ cl_uuid = &conn.in_client_uuid;
+ props = &conn.out_client_properties;
+ vtag = 0;
+
+ rets = mei_vt_support_check(dev, cl_uuid);
+ if (rets == -ENOTTY)
+ goto out;
+ if (!rets)
+ rets = mei_ioctl_connect_vtag(file, cl_uuid, props,
+ vtag);
+ else
+ rets = mei_ioctl_connect_client(file, cl_uuid, props);
+ if (rets)
+ goto out;
+
+ /* if all is ok, copying the data back to user. */
+ if (copy_to_user((char __user *)data, &conn, sizeof(conn))) {
+ dev_dbg(dev->dev, "failed to copy data to userland\n");
+ rets = -EFAULT;
+ goto out;
+ }
+
+ break;
+
+ case IOCTL_MEI_CONNECT_CLIENT_VTAG:
+ dev_dbg(dev->dev, "IOCTL_MEI_CONNECT_CLIENT_VTAG\n");
+ if (copy_from_user(&conn_vtag, (char __user *)data,
+ sizeof(conn_vtag))) {
dev_dbg(dev->dev, "failed to copy data from userland\n");
rets = -EFAULT;
goto out;
}
- rets = mei_ioctl_connect_client(file, &connect_data);
+ cl_uuid = &conn_vtag.connect.in_client_uuid;
+ props = &conn_vtag.out_client_properties;
+ vtag = conn_vtag.connect.vtag;
+
+ rets = mei_vt_support_check(dev, cl_uuid);
+ if (rets == -EOPNOTSUPP)
+ dev_dbg(dev->dev, "FW Client %pUl does not support vtags\n",
+ cl_uuid);
+ if (rets)
+ goto out;
+
+ if (!vtag) {
+ dev_dbg(dev->dev, "vtag can't be zero\n");
+ rets = -EINVAL;
+ goto out;
+ }
+
+ rets = mei_ioctl_connect_vtag(file, cl_uuid, props, vtag);
if (rets)
goto out;
/* if all is ok, copying the data back to user. */
- if (copy_to_user((char __user *)data, &connect_data,
- sizeof(connect_data))) {
+ if (copy_to_user((char __user *)data, &conn_vtag,
+ sizeof(conn_vtag))) {
dev_dbg(dev->dev, "failed to copy data to userland\n");
rets = -EFAULT;
goto out;