cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

cros_ec_chardev.c (10085B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Miscellaneous character driver for ChromeOS Embedded Controller
      4 *
      5 * Copyright 2014 Google, Inc.
      6 * Copyright 2019 Google LLC
      7 *
      8 * This file is a rework and part of the code is ported from
      9 * drivers/mfd/cros_ec_dev.c that was originally written by
     10 * Bill Richardson.
     11 */
     12
     13#include <linux/init.h>
     14#include <linux/device.h>
     15#include <linux/fs.h>
     16#include <linux/miscdevice.h>
     17#include <linux/module.h>
     18#include <linux/notifier.h>
     19#include <linux/platform_data/cros_ec_chardev.h>
     20#include <linux/platform_data/cros_ec_commands.h>
     21#include <linux/platform_data/cros_ec_proto.h>
     22#include <linux/platform_device.h>
     23#include <linux/poll.h>
     24#include <linux/slab.h>
     25#include <linux/types.h>
     26#include <linux/uaccess.h>
     27
     28#define DRV_NAME		"cros-ec-chardev"
     29
     30/* Arbitrary bounded size for the event queue */
     31#define CROS_MAX_EVENT_LEN	PAGE_SIZE
     32
     33struct chardev_data {
     34	struct cros_ec_dev *ec_dev;
     35	struct miscdevice misc;
     36};
     37
     38struct chardev_priv {
     39	struct cros_ec_dev *ec_dev;
     40	struct notifier_block notifier;
     41	wait_queue_head_t wait_event;
     42	unsigned long event_mask;
     43	struct list_head events;
     44	size_t event_len;
     45};
     46
     47struct ec_event {
     48	struct list_head node;
     49	size_t size;
     50	u8 event_type;
     51	u8 data[];
     52};
     53
     54static int ec_get_version(struct cros_ec_dev *ec, char *str, int maxlen)
     55{
     56	static const char * const current_image_name[] = {
     57		"unknown", "read-only", "read-write", "invalid",
     58	};
     59	struct ec_response_get_version *resp;
     60	struct cros_ec_command *msg;
     61	int ret;
     62
     63	msg = kzalloc(sizeof(*msg) + sizeof(*resp), GFP_KERNEL);
     64	if (!msg)
     65		return -ENOMEM;
     66
     67	msg->command = EC_CMD_GET_VERSION + ec->cmd_offset;
     68	msg->insize = sizeof(*resp);
     69
     70	ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
     71	if (ret < 0) {
     72		snprintf(str, maxlen,
     73			 "Unknown EC version, returned error: %d\n",
     74			 msg->result);
     75		goto exit;
     76	}
     77
     78	resp = (struct ec_response_get_version *)msg->data;
     79	if (resp->current_image >= ARRAY_SIZE(current_image_name))
     80		resp->current_image = 3; /* invalid */
     81
     82	snprintf(str, maxlen, "%s\n%s\n%s\n%s\n", CROS_EC_DEV_VERSION,
     83		 resp->version_string_ro, resp->version_string_rw,
     84		 current_image_name[resp->current_image]);
     85
     86	ret = 0;
     87exit:
     88	kfree(msg);
     89	return ret;
     90}
     91
     92static int cros_ec_chardev_mkbp_event(struct notifier_block *nb,
     93				      unsigned long queued_during_suspend,
     94				      void *_notify)
     95{
     96	struct chardev_priv *priv = container_of(nb, struct chardev_priv,
     97						 notifier);
     98	struct cros_ec_device *ec_dev = priv->ec_dev->ec_dev;
     99	struct ec_event *event;
    100	unsigned long event_bit = 1 << ec_dev->event_data.event_type;
    101	int total_size = sizeof(*event) + ec_dev->event_size;
    102
    103	if (!(event_bit & priv->event_mask) ||
    104	    (priv->event_len + total_size) > CROS_MAX_EVENT_LEN)
    105		return NOTIFY_DONE;
    106
    107	event = kzalloc(total_size, GFP_KERNEL);
    108	if (!event)
    109		return NOTIFY_DONE;
    110
    111	event->size = ec_dev->event_size;
    112	event->event_type = ec_dev->event_data.event_type;
    113	memcpy(event->data, &ec_dev->event_data.data, ec_dev->event_size);
    114
    115	spin_lock(&priv->wait_event.lock);
    116	list_add_tail(&event->node, &priv->events);
    117	priv->event_len += total_size;
    118	wake_up_locked(&priv->wait_event);
    119	spin_unlock(&priv->wait_event.lock);
    120
    121	return NOTIFY_OK;
    122}
    123
    124static struct ec_event *cros_ec_chardev_fetch_event(struct chardev_priv *priv,
    125						    bool fetch, bool block)
    126{
    127	struct ec_event *event;
    128	int err;
    129
    130	spin_lock(&priv->wait_event.lock);
    131	if (!block && list_empty(&priv->events)) {
    132		event = ERR_PTR(-EWOULDBLOCK);
    133		goto out;
    134	}
    135
    136	if (!fetch) {
    137		event = NULL;
    138		goto out;
    139	}
    140
    141	err = wait_event_interruptible_locked(priv->wait_event,
    142					      !list_empty(&priv->events));
    143	if (err) {
    144		event = ERR_PTR(err);
    145		goto out;
    146	}
    147
    148	event = list_first_entry(&priv->events, struct ec_event, node);
    149	list_del(&event->node);
    150	priv->event_len -= sizeof(*event) + event->size;
    151
    152out:
    153	spin_unlock(&priv->wait_event.lock);
    154	return event;
    155}
    156
    157/*
    158 * Device file ops
    159 */
    160static int cros_ec_chardev_open(struct inode *inode, struct file *filp)
    161{
    162	struct miscdevice *mdev = filp->private_data;
    163	struct cros_ec_dev *ec_dev = dev_get_drvdata(mdev->parent);
    164	struct chardev_priv *priv;
    165	int ret;
    166
    167	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
    168	if (!priv)
    169		return -ENOMEM;
    170
    171	priv->ec_dev = ec_dev;
    172	filp->private_data = priv;
    173	INIT_LIST_HEAD(&priv->events);
    174	init_waitqueue_head(&priv->wait_event);
    175	nonseekable_open(inode, filp);
    176
    177	priv->notifier.notifier_call = cros_ec_chardev_mkbp_event;
    178	ret = blocking_notifier_chain_register(&ec_dev->ec_dev->event_notifier,
    179					       &priv->notifier);
    180	if (ret) {
    181		dev_err(ec_dev->dev, "failed to register event notifier\n");
    182		kfree(priv);
    183	}
    184
    185	return ret;
    186}
    187
    188static __poll_t cros_ec_chardev_poll(struct file *filp, poll_table *wait)
    189{
    190	struct chardev_priv *priv = filp->private_data;
    191
    192	poll_wait(filp, &priv->wait_event, wait);
    193
    194	if (list_empty(&priv->events))
    195		return 0;
    196
    197	return EPOLLIN | EPOLLRDNORM;
    198}
    199
    200static ssize_t cros_ec_chardev_read(struct file *filp, char __user *buffer,
    201				     size_t length, loff_t *offset)
    202{
    203	char msg[sizeof(struct ec_response_get_version) +
    204		 sizeof(CROS_EC_DEV_VERSION)];
    205	struct chardev_priv *priv = filp->private_data;
    206	struct cros_ec_dev *ec_dev = priv->ec_dev;
    207	size_t count;
    208	int ret;
    209
    210	if (priv->event_mask) { /* queued MKBP event */
    211		struct ec_event *event;
    212
    213		event = cros_ec_chardev_fetch_event(priv, length != 0,
    214						!(filp->f_flags & O_NONBLOCK));
    215		if (IS_ERR(event))
    216			return PTR_ERR(event);
    217		/*
    218		 * length == 0 is special - no IO is done but we check
    219		 * for error conditions.
    220		 */
    221		if (length == 0)
    222			return 0;
    223
    224		/* The event is 1 byte of type plus the payload */
    225		count = min(length, event->size + 1);
    226		ret = copy_to_user(buffer, &event->event_type, count);
    227		kfree(event);
    228		if (ret) /* the copy failed */
    229			return -EFAULT;
    230		*offset = count;
    231		return count;
    232	}
    233
    234	/*
    235	 * Legacy behavior if no event mask is defined
    236	 */
    237	if (*offset != 0)
    238		return 0;
    239
    240	ret = ec_get_version(ec_dev, msg, sizeof(msg));
    241	if (ret)
    242		return ret;
    243
    244	count = min(length, strlen(msg));
    245
    246	if (copy_to_user(buffer, msg, count))
    247		return -EFAULT;
    248
    249	*offset = count;
    250	return count;
    251}
    252
    253static int cros_ec_chardev_release(struct inode *inode, struct file *filp)
    254{
    255	struct chardev_priv *priv = filp->private_data;
    256	struct cros_ec_dev *ec_dev = priv->ec_dev;
    257	struct ec_event *event, *e;
    258
    259	blocking_notifier_chain_unregister(&ec_dev->ec_dev->event_notifier,
    260					   &priv->notifier);
    261
    262	list_for_each_entry_safe(event, e, &priv->events, node) {
    263		list_del(&event->node);
    264		kfree(event);
    265	}
    266	kfree(priv);
    267
    268	return 0;
    269}
    270
    271/*
    272 * Ioctls
    273 */
    274static long cros_ec_chardev_ioctl_xcmd(struct cros_ec_dev *ec, void __user *arg)
    275{
    276	struct cros_ec_command *s_cmd;
    277	struct cros_ec_command u_cmd;
    278	long ret;
    279
    280	if (copy_from_user(&u_cmd, arg, sizeof(u_cmd)))
    281		return -EFAULT;
    282
    283	if (u_cmd.outsize > EC_MAX_MSG_BYTES ||
    284	    u_cmd.insize > EC_MAX_MSG_BYTES)
    285		return -EINVAL;
    286
    287	s_cmd = kmalloc(sizeof(*s_cmd) + max(u_cmd.outsize, u_cmd.insize),
    288			GFP_KERNEL);
    289	if (!s_cmd)
    290		return -ENOMEM;
    291
    292	if (copy_from_user(s_cmd, arg, sizeof(*s_cmd) + u_cmd.outsize)) {
    293		ret = -EFAULT;
    294		goto exit;
    295	}
    296
    297	if (u_cmd.outsize != s_cmd->outsize ||
    298	    u_cmd.insize != s_cmd->insize) {
    299		ret = -EINVAL;
    300		goto exit;
    301	}
    302
    303	s_cmd->command += ec->cmd_offset;
    304	ret = cros_ec_cmd_xfer(ec->ec_dev, s_cmd);
    305	/* Only copy data to userland if data was received. */
    306	if (ret < 0)
    307		goto exit;
    308
    309	if (copy_to_user(arg, s_cmd, sizeof(*s_cmd) + s_cmd->insize))
    310		ret = -EFAULT;
    311exit:
    312	kfree(s_cmd);
    313	return ret;
    314}
    315
    316static long cros_ec_chardev_ioctl_readmem(struct cros_ec_dev *ec,
    317					   void __user *arg)
    318{
    319	struct cros_ec_device *ec_dev = ec->ec_dev;
    320	struct cros_ec_readmem s_mem = { };
    321	long num;
    322
    323	/* Not every platform supports direct reads */
    324	if (!ec_dev->cmd_readmem)
    325		return -ENOTTY;
    326
    327	if (copy_from_user(&s_mem, arg, sizeof(s_mem)))
    328		return -EFAULT;
    329
    330	num = ec_dev->cmd_readmem(ec_dev, s_mem.offset, s_mem.bytes,
    331				  s_mem.buffer);
    332	if (num <= 0)
    333		return num;
    334
    335	if (copy_to_user((void __user *)arg, &s_mem, sizeof(s_mem)))
    336		return -EFAULT;
    337
    338	return num;
    339}
    340
    341static long cros_ec_chardev_ioctl(struct file *filp, unsigned int cmd,
    342				   unsigned long arg)
    343{
    344	struct chardev_priv *priv = filp->private_data;
    345	struct cros_ec_dev *ec = priv->ec_dev;
    346
    347	if (_IOC_TYPE(cmd) != CROS_EC_DEV_IOC)
    348		return -ENOTTY;
    349
    350	switch (cmd) {
    351	case CROS_EC_DEV_IOCXCMD:
    352		return cros_ec_chardev_ioctl_xcmd(ec, (void __user *)arg);
    353	case CROS_EC_DEV_IOCRDMEM:
    354		return cros_ec_chardev_ioctl_readmem(ec, (void __user *)arg);
    355	case CROS_EC_DEV_IOCEVENTMASK:
    356		priv->event_mask = arg;
    357		return 0;
    358	}
    359
    360	return -ENOTTY;
    361}
    362
    363static const struct file_operations chardev_fops = {
    364	.open		= cros_ec_chardev_open,
    365	.poll		= cros_ec_chardev_poll,
    366	.read		= cros_ec_chardev_read,
    367	.release	= cros_ec_chardev_release,
    368	.unlocked_ioctl	= cros_ec_chardev_ioctl,
    369#ifdef CONFIG_COMPAT
    370	.compat_ioctl	= cros_ec_chardev_ioctl,
    371#endif
    372};
    373
    374static int cros_ec_chardev_probe(struct platform_device *pdev)
    375{
    376	struct cros_ec_dev *ec_dev = dev_get_drvdata(pdev->dev.parent);
    377	struct cros_ec_platform *ec_platform = dev_get_platdata(ec_dev->dev);
    378	struct chardev_data *data;
    379
    380	/* Create a char device: we want to create it anew */
    381	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
    382	if (!data)
    383		return -ENOMEM;
    384
    385	data->ec_dev = ec_dev;
    386	data->misc.minor = MISC_DYNAMIC_MINOR;
    387	data->misc.fops = &chardev_fops;
    388	data->misc.name = ec_platform->ec_name;
    389	data->misc.parent = pdev->dev.parent;
    390
    391	dev_set_drvdata(&pdev->dev, data);
    392
    393	return misc_register(&data->misc);
    394}
    395
    396static int cros_ec_chardev_remove(struct platform_device *pdev)
    397{
    398	struct chardev_data *data = dev_get_drvdata(&pdev->dev);
    399
    400	misc_deregister(&data->misc);
    401
    402	return 0;
    403}
    404
    405static struct platform_driver cros_ec_chardev_driver = {
    406	.driver = {
    407		.name = DRV_NAME,
    408	},
    409	.probe = cros_ec_chardev_probe,
    410	.remove = cros_ec_chardev_remove,
    411};
    412
    413module_platform_driver(cros_ec_chardev_driver);
    414
    415MODULE_ALIAS("platform:" DRV_NAME);
    416MODULE_AUTHOR("Enric Balletbo i Serra <enric.balletbo@collabora.com>");
    417MODULE_DESCRIPTION("ChromeOS EC Miscellaneous Character Driver");
    418MODULE_LICENSE("GPL");