lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Wed, 31 Aug 2016 12:45:37 +0100
From:   Lee Jones <lee.jones@...aro.org>
To:     Nicolas Boichat <drinkcat@...omium.org>
Cc:     gwendal@...omium.org, ejcaruso@...omium.org,
        Olof Johansson <olof@...om.net>, linux-kernel@...r.kernel.org,
        groeck@...omium.org
Subject: Re: [PATCH 2/3] mfd: cros_ec: add debugfs, console log file

On Tue, 23 Aug 2016, Nicolas Boichat wrote:

> From: Eric Caruso <ejcaruso@...omium.org>
> 
> If the EC supports the new CONSOLE_READ command type, then we
> place a console_log file in debugfs for that EC device which allows
> us to grab EC logs. The kernel will poll every 10 seconds for the
> log and keep its own buffer, but userspace should grab this and
> write it out to some logs which actually get rotated.
> 
> Signed-off-by: Eric Caruso <ejcaruso@...omium.org>
> Signed-off-by: Nicolas Boichat <drinkcat@...omium.org>
> ---
>  drivers/platform/chrome/Makefile          |   3 +-
>  drivers/platform/chrome/cros_ec_debugfs.c | 347 ++++++++++++++++++++++++++++++
>  drivers/platform/chrome/cros_ec_debugfs.h |  27 +++
>  drivers/platform/chrome/cros_ec_dev.c     |   7 +
>  include/linux/mfd/cros_ec.h               |   4 +

Acked-by: Lee Jones <lee.jones@...aro.org>

I guess this will be taken through Olof's tree?

>  5 files changed, 387 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/platform/chrome/cros_ec_debugfs.c
>  create mode 100644 drivers/platform/chrome/cros_ec_debugfs.h
> 
> diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile
> index 4f34627..3870afe 100644
> --- a/drivers/platform/chrome/Makefile
> +++ b/drivers/platform/chrome/Makefile
> @@ -2,7 +2,8 @@
>  obj-$(CONFIG_CHROMEOS_LAPTOP)		+= chromeos_laptop.o
>  obj-$(CONFIG_CHROMEOS_PSTORE)		+= chromeos_pstore.o
>  cros_ec_devs-objs			:= cros_ec_dev.o cros_ec_sysfs.o \
> -					   cros_ec_lightbar.o cros_ec_vbc.o
> +					   cros_ec_lightbar.o cros_ec_vbc.o \
> +					   cros_ec_debugfs.o
>  obj-$(CONFIG_CROS_EC_CHARDEV)		+= cros_ec_devs.o
>  obj-$(CONFIG_CROS_EC_LPC)		+= cros_ec_lpc.o
>  obj-$(CONFIG_CROS_EC_PROTO)		+= cros_ec_proto.o
> diff --git a/drivers/platform/chrome/cros_ec_debugfs.c b/drivers/platform/chrome/cros_ec_debugfs.c
> new file mode 100644
> index 0000000..225f936
> --- /dev/null
> +++ b/drivers/platform/chrome/cros_ec_debugfs.c
> @@ -0,0 +1,347 @@
> +/*
> + * cros_ec_debugfs - debug logs for Chrome OS EC
> + *
> + * Copyright 2015 Google, Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/circ_buf.h>
> +#include <linux/debugfs.h>
> +#include <linux/delay.h>
> +#include <linux/fs.h>
> +#include <linux/mfd/cros_ec.h>
> +#include <linux/mfd/cros_ec_commands.h>
> +#include <linux/mutex.h>
> +#include <linux/poll.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +#include <linux/wait.h>
> +
> +#include "cros_ec_dev.h"
> +#include "cros_ec_debugfs.h"
> +
> +#define LOG_SHIFT		14
> +#define LOG_SIZE		(1 << LOG_SHIFT)
> +#define LOG_POLL_SEC		10
> +
> +#define CIRC_ADD(idx, size, value)	(((idx) + (value)) & ((size) - 1))
> +
> +/* struct cros_ec_debugfs - ChromeOS EC debugging information
> + *
> + * @ec: EC device this debugfs information belongs to
> + * @dir: dentry for debugfs files
> + * @log_buffer: circular buffer for console log information
> + * @read_msg: preallocated EC command and buffer to read console log
> + * @log_mutex: mutex to protect circular buffer
> + * @log_wq: waitqueue for log readers
> + * @log_poll_work: recurring task to poll EC for new console log data
> + */
> +struct cros_ec_debugfs {
> +	struct cros_ec_dev *ec;
> +	struct dentry *dir;
> +	struct circ_buf log_buffer;
> +	struct cros_ec_command *read_msg;
> +	struct mutex log_mutex;
> +	wait_queue_head_t log_wq;
> +	struct delayed_work log_poll_work;
> +};
> +
> +/*
> + * We need to make sure that the EC log buffer on the UART is large enough,
> + * so that it is unlikely enough to overlow within LOG_POLL_SEC.
> + */
> +static void cros_ec_console_log_work(struct work_struct *__work)
> +{
> +	struct cros_ec_debugfs *debug_info =
> +		container_of(to_delayed_work(__work),
> +			     struct cros_ec_debugfs,
> +			     log_poll_work);
> +	struct cros_ec_dev *ec = debug_info->ec;
> +	struct circ_buf *cb = &debug_info->log_buffer;
> +	struct cros_ec_command snapshot_msg = {
> +		.command = EC_CMD_CONSOLE_SNAPSHOT + ec->cmd_offset,
> +	};
> +
> +	struct ec_params_console_read_v1 *read_params =
> +		(struct ec_params_console_read_v1 *)debug_info->read_msg->data;
> +	uint8_t *ec_buffer = (uint8_t *)debug_info->read_msg->data;
> +	int idx;
> +	int buf_space;
> +	int ret;
> +
> +	ret = cros_ec_cmd_xfer(ec->ec_dev, &snapshot_msg);
> +	if (ret < 0) {
> +		dev_err(ec->dev, "EC communication failed\n");
> +		goto resched;
> +	}
> +	if (snapshot_msg.result != EC_RES_SUCCESS) {
> +		dev_err(ec->dev, "EC failed to snapshot the console log\n");
> +		goto resched;
> +	}
> +
> +	/* Loop until we have read everything, or there's an error. */
> +	mutex_lock(&debug_info->log_mutex);
> +	buf_space = CIRC_SPACE(cb->head, cb->tail, LOG_SIZE);
> +
> +	while (1) {
> +		if (!buf_space) {
> +			dev_info_once(ec->dev,
> +				      "Some logs may have been dropped...\n");
> +			break;
> +		}
> +
> +		memset(read_params, '\0', sizeof(*read_params));
> +		read_params->subcmd = CONSOLE_READ_RECENT;
> +		ret = cros_ec_cmd_xfer(ec->ec_dev, debug_info->read_msg);
> +		if (ret < 0) {
> +			dev_err(ec->dev, "EC communication failed\n");
> +			break;
> +		}
> +		if (debug_info->read_msg->result != EC_RES_SUCCESS) {
> +			dev_err(ec->dev,
> +				"EC failed to read the console log\n");
> +			break;
> +		}
> +
> +		/* If the buffer is empty, we're done here. */
> +		if (ret == 0 || ec_buffer[0] == '\0')
> +			break;
> +
> +		idx = 0;
> +		while (idx < ret && ec_buffer[idx] != '\0' && buf_space > 0) {
> +			cb->buf[cb->head] = ec_buffer[idx];
> +			cb->head = CIRC_ADD(cb->head, LOG_SIZE, 1);
> +			idx++;
> +			buf_space--;
> +		}
> +
> +		wake_up(&debug_info->log_wq);
> +	}
> +
> +	mutex_unlock(&debug_info->log_mutex);
> +
> +resched:
> +	schedule_delayed_work(&debug_info->log_poll_work,
> +			      msecs_to_jiffies(LOG_POLL_SEC * 1000));
> +}
> +
> +static int cros_ec_console_log_open(struct inode *inode, struct file *file)
> +{
> +	file->private_data = inode->i_private;
> +
> +	return nonseekable_open(inode, file);
> +}
> +
> +static ssize_t cros_ec_console_log_read(struct file *file, char __user *buf,
> +					size_t count, loff_t *ppos)
> +{
> +	struct cros_ec_debugfs *debug_info = file->private_data;
> +	struct circ_buf *cb = &debug_info->log_buffer;
> +	ssize_t ret;
> +
> +	mutex_lock(&debug_info->log_mutex);
> +
> +	while (!CIRC_CNT(cb->head, cb->tail, LOG_SIZE)) {
> +		if (file->f_flags & O_NONBLOCK) {
> +			ret = -EAGAIN;
> +			goto error;
> +		}
> +
> +		mutex_unlock(&debug_info->log_mutex);
> +
> +		ret = wait_event_interruptible(debug_info->log_wq,
> +					CIRC_CNT(cb->head, cb->tail, LOG_SIZE));
> +		if (ret < 0)
> +			return ret;
> +
> +		mutex_lock(&debug_info->log_mutex);
> +	}
> +
> +	/* Only copy until the end of the circular buffer, and let userspace
> +	 * retry to get the rest of the data.
> +	 */
> +	ret = min_t(size_t, CIRC_CNT_TO_END(cb->head, cb->tail, LOG_SIZE),
> +		    count);
> +
> +	if (copy_to_user(buf, cb->buf + cb->tail, ret)) {
> +		ret = -EFAULT;
> +		goto error;
> +	}
> +
> +	cb->tail = CIRC_ADD(cb->tail, LOG_SIZE, ret);
> +
> +error:
> +	mutex_unlock(&debug_info->log_mutex);
> +	return ret;
> +}
> +
> +static unsigned int cros_ec_console_log_poll(struct file *file,
> +					     poll_table *wait)
> +{
> +	struct cros_ec_debugfs *debug_info = file->private_data;
> +	unsigned int mask = 0;
> +
> +	poll_wait(file, &debug_info->log_wq, wait);
> +
> +	mutex_lock(&debug_info->log_mutex);
> +	if (CIRC_CNT(debug_info->log_buffer.head,
> +		     debug_info->log_buffer.tail,
> +		     LOG_SIZE))
> +		mask |= POLLIN | POLLRDNORM;
> +	mutex_unlock(&debug_info->log_mutex);
> +
> +	return mask;
> +}
> +
> +static int cros_ec_console_log_release(struct inode *inode, struct file *file)
> +{
> +	return 0;
> +}
> +
> +const struct file_operations cros_ec_console_log_fops = {
> +	.owner = THIS_MODULE,
> +	.open = cros_ec_console_log_open,
> +	.read = cros_ec_console_log_read,
> +	.llseek = no_llseek,
> +	.poll = cros_ec_console_log_poll,
> +	.release = cros_ec_console_log_release,
> +};
> +
> +static int ec_read_version_supported(struct cros_ec_dev *ec)
> +{
> +	struct ec_params_get_cmd_versions_v1 *params;
> +	struct ec_response_get_cmd_versions *response;
> +	int ret;
> +
> +	struct cros_ec_command *msg;
> +
> +	msg = kzalloc(sizeof(*msg) + max(sizeof(params), sizeof(response)),
> +		GFP_KERNEL);
> +	if (!msg)
> +		return 0;
> +
> +	msg->command = EC_CMD_GET_CMD_VERSIONS + ec->cmd_offset;
> +	msg->outsize = sizeof(params);
> +	msg->insize = sizeof(response);
> +
> +	params = (struct ec_params_get_cmd_versions_v1 *)msg->data;
> +	params->cmd = EC_CMD_CONSOLE_READ;
> +	response = (struct ec_response_get_cmd_versions *)msg->data;
> +
> +	ret = cros_ec_cmd_xfer(ec->ec_dev, msg) >= 0 &&
> +		msg->result == EC_RES_SUCCESS &&
> +		(response->version_mask & EC_VER_MASK(1));
> +
> +	kfree(msg);
> +
> +	return ret;
> +}
> +
> +static int cros_ec_create_console_log(struct cros_ec_debugfs *debug_info)
> +{
> +	struct cros_ec_dev *ec = debug_info->ec;
> +	char *buf;
> +	int read_params_size;
> +	int read_response_size;
> +
> +	if (!ec_read_version_supported(ec)) {
> +		dev_warn(ec->dev,
> +			"device does not support reading the console log\n");
> +		return 0;
> +	}
> +
> +	buf = devm_kzalloc(ec->dev, LOG_SIZE, GFP_KERNEL);
> +	if (!buf)
> +		return -ENOMEM;
> +
> +	read_params_size = sizeof(struct ec_params_console_read_v1);
> +	read_response_size = ec->ec_dev->max_response;
> +	debug_info->read_msg = devm_kzalloc(ec->dev,
> +		sizeof(*debug_info->read_msg) +
> +			max(read_params_size, read_response_size), GFP_KERNEL);
> +	if (!debug_info->read_msg)
> +		return -ENOMEM;
> +
> +	debug_info->read_msg->version = 1;
> +	debug_info->read_msg->command = EC_CMD_CONSOLE_READ + ec->cmd_offset;
> +	debug_info->read_msg->outsize = read_params_size;
> +	debug_info->read_msg->insize = read_response_size;
> +
> +	debug_info->log_buffer.buf = buf;
> +	debug_info->log_buffer.head = 0;
> +	debug_info->log_buffer.tail = 0;
> +
> +	mutex_init(&debug_info->log_mutex);
> +	init_waitqueue_head(&debug_info->log_wq);
> +
> +	if (!debugfs_create_file("console_log",
> +				 S_IFREG | S_IRUGO,
> +				 debug_info->dir,
> +				 debug_info,
> +				 &cros_ec_console_log_fops))
> +		return -ENOMEM;
> +
> +	INIT_DELAYED_WORK(&debug_info->log_poll_work,
> +			  cros_ec_console_log_work);
> +	schedule_delayed_work(&debug_info->log_poll_work, 0);
> +
> +	return 0;
> +}
> +
> +static void cros_ec_cleanup_console_log(struct cros_ec_debugfs *debug_info)
> +{
> +	if (debug_info->log_buffer.buf) {
> +		cancel_delayed_work_sync(&debug_info->log_poll_work);
> +		mutex_destroy(&debug_info->log_mutex);
> +	}
> +}
> +
> +int cros_ec_debugfs_init(struct cros_ec_dev *ec)
> +{
> +	struct cros_ec_platform *ec_platform = dev_get_platdata(ec->dev);
> +	const char *name = ec_platform->ec_name;
> +	struct cros_ec_debugfs *debug_info;
> +	int ret;
> +
> +	debug_info = devm_kzalloc(ec->dev, sizeof(*debug_info), GFP_KERNEL);
> +	if (!debug_info)
> +		return -ENOMEM;
> +
> +	debug_info->ec = ec;
> +	debug_info->dir = debugfs_create_dir(name, NULL);
> +	if (!debug_info->dir)
> +		return -ENOMEM;
> +
> +	ret = cros_ec_create_console_log(debug_info);
> +	if (ret)
> +		goto remove_debugfs;
> +
> +	ec->debug_info = debug_info;
> +
> +	return 0;
> +
> +remove_debugfs:
> +	debugfs_remove_recursive(debug_info->dir);
> +	return ret;
> +}
> +
> +void cros_ec_debugfs_remove(struct cros_ec_dev *ec)
> +{
> +	if (!ec->debug_info)
> +		return;
> +
> +	debugfs_remove_recursive(ec->debug_info->dir);
> +	cros_ec_cleanup_console_log(ec->debug_info);
> +}
> diff --git a/drivers/platform/chrome/cros_ec_debugfs.h b/drivers/platform/chrome/cros_ec_debugfs.h
> new file mode 100644
> index 0000000..1ff3a50
> --- /dev/null
> +++ b/drivers/platform/chrome/cros_ec_debugfs.h
> @@ -0,0 +1,27 @@
> +/*
> + * Copyright 2015 Google, Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef _DRV_CROS_EC_DEBUGFS_H_
> +#define _DRV_CROS_EC_DEBUGFS_H_
> +
> +#include "cros_ec_dev.h"
> +
> +/* debugfs stuff */
> +int cros_ec_debugfs_init(struct cros_ec_dev *ec);
> +void cros_ec_debugfs_remove(struct cros_ec_dev *ec);
> +
> +#endif  /* _DRV_CROS_EC_DEBUGFS_H_ */
> diff --git a/drivers/platform/chrome/cros_ec_dev.c b/drivers/platform/chrome/cros_ec_dev.c
> index 8abd80d..7d51f69 100644
> --- a/drivers/platform/chrome/cros_ec_dev.c
> +++ b/drivers/platform/chrome/cros_ec_dev.c
> @@ -23,6 +23,7 @@
>  #include <linux/slab.h>
>  #include <linux/uaccess.h>
>  
> +#include "cros_ec_debugfs.h"
>  #include "cros_ec_dev.h"
>  
>  /* Device variables */
> @@ -282,6 +283,9 @@ static int ec_device_probe(struct platform_device *pdev)
>  		goto dev_reg_failed;
>  	}
>  
> +	if (cros_ec_debugfs_init(ec))
> +		dev_warn(dev, "failed to create debugfs directory\n");
> +
>  	return 0;
>  
>  dev_reg_failed:
> @@ -296,6 +300,9 @@ cdev_add_failed:
>  static int ec_device_remove(struct platform_device *pdev)
>  {
>  	struct cros_ec_dev *ec = dev_get_drvdata(&pdev->dev);
> +
> +	cros_ec_debugfs_remove(ec);
> +
>  	cdev_del(&ec->cdev);
>  	device_unregister(&ec->class_dev);
>  	return 0;
> diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h
> index d641a18..e7001a7 100644
> --- a/include/linux/mfd/cros_ec.h
> +++ b/include/linux/mfd/cros_ec.h
> @@ -151,6 +151,8 @@ struct cros_ec_platform {
>  	u16 cmd_offset;
>  };
>  
> +struct cros_ec_debugfs;
> +
>  /*
>   * struct cros_ec_dev - ChromeOS EC device entry point
>   *
> @@ -158,6 +160,7 @@ struct cros_ec_platform {
>   * @cdev: Character device structure in /dev
>   * @ec_dev: cros_ec_device structure to talk to the physical device
>   * @dev: pointer to the platform device
> + * @debug_info: cros_ec_debugfs structure for debugging information
>   * @cmd_offset: offset to apply for each command.
>   */
>  struct cros_ec_dev {
> @@ -165,6 +168,7 @@ struct cros_ec_dev {
>  	struct cdev cdev;
>  	struct cros_ec_device *ec_dev;
>  	struct device *dev;
> +	struct cros_ec_debugfs *debug_info;
>  	u16 cmd_offset;
>  };
>  

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ