diff options
author | Javier Martinez Canillas <javier.martinez@collabora.co.uk> | 2015-06-09 13:04:42 +0200 |
---|---|---|
committer | Lee Jones <lee.jones@linaro.org> | 2015-06-15 13:18:19 +0100 |
commit | a841178445bb72a3d566b4e6ab9d19e9b002eb47 (patch) | |
tree | bdc857e82c9c9e8b49bd5471efa5a954673d58f0 /drivers/platform/chrome/cros_ec_dev.c | |
parent | bb03ffb96c72418d06e75c7b74ea62e04c78d322 (diff) |
mfd: cros_ec: Use a zero-length array for command data
Commit 1b84f2a4cd4a ("mfd: cros_ec: Use fixed size arrays to transfer
data with the EC") modified the struct cros_ec_command fields to not
use pointers for the input and output buffers and use fixed length
arrays instead.
This change was made because the cros_ec ioctl API uses that struct
cros_ec_command to allow user-space to send commands to the EC and
to get data from the EC. So using pointers made the API not 64-bit
safe. Unfortunately this approach was not flexible enough for all
the use-cases since there may be a need to send larger commands
on newer versions of the EC command protocol.
So to avoid to choose a constant length that it may be too big for
most commands and thus wasting memory and CPU cycles on copy from
and to user-space or having a size that is too small for some big
commands, use a zero-length array that is both 64-bit safe and
flexible. The same buffer is used for both output and input data
so the maximum of these values should be used to allocate it.
Suggested-by: Gwendal Grignou <gwendal@chromium.org>
Signed-off-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
Tested-by: Heiko Stuebner <heiko@sntech.de>
Acked-by: Lee Jones <lee.jones@linaro.org>
Acked-by: Olof Johansson <olof@lixom.net>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Diffstat (limited to 'drivers/platform/chrome/cros_ec_dev.c')
-rw-r--r-- | drivers/platform/chrome/cros_ec_dev.c | 65 |
1 files changed, 42 insertions, 23 deletions
diff --git a/drivers/platform/chrome/cros_ec_dev.c b/drivers/platform/chrome/cros_ec_dev.c index 6090d0b2826f..e91ced1cb8ce 100644 --- a/drivers/platform/chrome/cros_ec_dev.c +++ b/drivers/platform/chrome/cros_ec_dev.c @@ -20,6 +20,7 @@ #include <linux/fs.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/slab.h> #include <linux/uaccess.h> #include "cros_ec_dev.h" @@ -36,28 +37,31 @@ static int ec_get_version(struct cros_ec_device *ec, char *str, int maxlen) static const char * const current_image_name[] = { "unknown", "read-only", "read-write", "invalid", }; - struct cros_ec_command msg = { - .version = 0, - .command = EC_CMD_GET_VERSION, - .outdata = { 0 }, - .outsize = 0, - .indata = { 0 }, - .insize = sizeof(*resp), - }; + struct cros_ec_command *msg; int ret; - ret = cros_ec_cmd_xfer(ec, &msg); + msg = kmalloc(sizeof(*msg) + sizeof(*resp), GFP_KERNEL); + if (!msg) + return -ENOMEM; + + msg->version = 0; + msg->command = EC_CMD_GET_VERSION; + msg->insize = sizeof(*resp); + msg->outsize = 0; + + ret = cros_ec_cmd_xfer(ec, msg); if (ret < 0) - return ret; + goto exit; - if (msg.result != EC_RES_SUCCESS) { + if (msg->result != EC_RES_SUCCESS) { snprintf(str, maxlen, "%s\nUnknown EC version: EC returned %d\n", - CROS_EC_DEV_VERSION, msg.result); - return 0; + CROS_EC_DEV_VERSION, msg->result); + ret = -EINVAL; + goto exit; } - resp = (struct ec_response_get_version *)msg.indata; + resp = (struct ec_response_get_version *)msg->data; if (resp->current_image >= ARRAY_SIZE(current_image_name)) resp->current_image = 3; /* invalid */ @@ -65,7 +69,10 @@ static int ec_get_version(struct cros_ec_device *ec, char *str, int maxlen) resp->version_string_ro, resp->version_string_rw, current_image_name[resp->current_image]); - return 0; + ret = 0; +exit: + kfree(msg); + return ret; } /* Device file ops */ @@ -110,20 +117,32 @@ static ssize_t ec_device_read(struct file *filp, char __user *buffer, static long ec_device_ioctl_xcmd(struct cros_ec_device *ec, void __user *arg) { long ret; - struct cros_ec_command s_cmd = { }; + struct cros_ec_command u_cmd; + struct cros_ec_command *s_cmd; - if (copy_from_user(&s_cmd, arg, sizeof(s_cmd))) + if (copy_from_user(&u_cmd, arg, sizeof(u_cmd))) return -EFAULT; - ret = cros_ec_cmd_xfer(ec, &s_cmd); + s_cmd = kmalloc(sizeof(*s_cmd) + max(u_cmd.outsize, u_cmd.insize), + GFP_KERNEL); + if (!s_cmd) + return -ENOMEM; + + if (copy_from_user(s_cmd, arg, sizeof(*s_cmd) + u_cmd.outsize)) { + ret = -EFAULT; + goto exit; + } + + ret = cros_ec_cmd_xfer(ec, s_cmd); /* Only copy data to userland if data was received. */ if (ret < 0) - return ret; + goto exit; - if (copy_to_user(arg, &s_cmd, sizeof(s_cmd))) - return -EFAULT; - - return 0; + if (copy_to_user(arg, s_cmd, sizeof(*s_cmd) + u_cmd.insize)) + ret = -EFAULT; +exit: + kfree(s_cmd); + return ret; } static long ec_device_ioctl_readmem(struct cros_ec_device *ec, void __user *arg) |