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]
Message-ID: <CABXOdTfsyxtNfP1S_oZYCvHBtRk_=OtiUY1xx1y4U-fHUN_i7g@mail.gmail.com>
Date:   Tue, 23 Aug 2016 14:17:52 -0700
From:   Guenter Roeck <groeck@...gle.com>
To:     Nicolas Boichat <drinkcat@...omium.org>
Cc:     Lee Jones <lee.jones@...aro.org>,
        Gwendal Grignou <gwendal@...omium.org>, ejcaruso@...omium.org,
        Olof Johansson <olof@...om.net>,
        linux-kernel <linux-kernel@...r.kernel.org>,
        Guenter Roeck <groeck@...omium.org>
Subject: Re: [PATCH 2/3] mfd: cros_ec: add debugfs, console log file

On Mon, Aug 22, 2016 at 9:34 PM, Nicolas Boichat <drinkcat@...omium.org> 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>

Reviewed-by: Guenter Roeck <groeck@...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 +
>  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;
>  };
>
> --
> 2.8.0.rc3.226.g39d4020
>

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ