linux/drivers/platform/chrome/cros_ec_debugfs.c
<<
>>
Prefs
   1/*
   2 * cros_ec_debugfs - debug logs for Chrome OS EC
   3 *
   4 * Copyright 2015 Google, Inc.
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; either version 2 of the License, or
   9 * (at your option) any later version.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14 * GNU General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU General Public License
  17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18 */
  19
  20#include <linux/circ_buf.h>
  21#include <linux/debugfs.h>
  22#include <linux/delay.h>
  23#include <linux/fs.h>
  24#include <linux/mfd/cros_ec.h>
  25#include <linux/mfd/cros_ec_commands.h>
  26#include <linux/mutex.h>
  27#include <linux/poll.h>
  28#include <linux/sched.h>
  29#include <linux/slab.h>
  30#include <linux/wait.h>
  31
  32#include "cros_ec_dev.h"
  33#include "cros_ec_debugfs.h"
  34
  35#define LOG_SHIFT               14
  36#define LOG_SIZE                (1 << LOG_SHIFT)
  37#define LOG_POLL_SEC            10
  38
  39#define CIRC_ADD(idx, size, value)      (((idx) + (value)) & ((size) - 1))
  40
  41/* struct cros_ec_debugfs - ChromeOS EC debugging information
  42 *
  43 * @ec: EC device this debugfs information belongs to
  44 * @dir: dentry for debugfs files
  45 * @log_buffer: circular buffer for console log information
  46 * @read_msg: preallocated EC command and buffer to read console log
  47 * @log_mutex: mutex to protect circular buffer
  48 * @log_wq: waitqueue for log readers
  49 * @log_poll_work: recurring task to poll EC for new console log data
  50 * @panicinfo_blob: panicinfo debugfs blob
  51 */
  52struct cros_ec_debugfs {
  53        struct cros_ec_dev *ec;
  54        struct dentry *dir;
  55        /* EC log */
  56        struct circ_buf log_buffer;
  57        struct cros_ec_command *read_msg;
  58        struct mutex log_mutex;
  59        wait_queue_head_t log_wq;
  60        struct delayed_work log_poll_work;
  61        /* EC panicinfo */
  62        struct debugfs_blob_wrapper panicinfo_blob;
  63};
  64
  65/*
  66 * We need to make sure that the EC log buffer on the UART is large enough,
  67 * so that it is unlikely enough to overlow within LOG_POLL_SEC.
  68 */
  69static void cros_ec_console_log_work(struct work_struct *__work)
  70{
  71        struct cros_ec_debugfs *debug_info =
  72                container_of(to_delayed_work(__work),
  73                             struct cros_ec_debugfs,
  74                             log_poll_work);
  75        struct cros_ec_dev *ec = debug_info->ec;
  76        struct circ_buf *cb = &debug_info->log_buffer;
  77        struct cros_ec_command snapshot_msg = {
  78                .command = EC_CMD_CONSOLE_SNAPSHOT + ec->cmd_offset,
  79        };
  80
  81        struct ec_params_console_read_v1 *read_params =
  82                (struct ec_params_console_read_v1 *)debug_info->read_msg->data;
  83        uint8_t *ec_buffer = (uint8_t *)debug_info->read_msg->data;
  84        int idx;
  85        int buf_space;
  86        int ret;
  87
  88        ret = cros_ec_cmd_xfer(ec->ec_dev, &snapshot_msg);
  89        if (ret < 0) {
  90                dev_err(ec->dev, "EC communication failed\n");
  91                goto resched;
  92        }
  93        if (snapshot_msg.result != EC_RES_SUCCESS) {
  94                dev_err(ec->dev, "EC failed to snapshot the console log\n");
  95                goto resched;
  96        }
  97
  98        /* Loop until we have read everything, or there's an error. */
  99        mutex_lock(&debug_info->log_mutex);
 100        buf_space = CIRC_SPACE(cb->head, cb->tail, LOG_SIZE);
 101
 102        while (1) {
 103                if (!buf_space) {
 104                        dev_info_once(ec->dev,
 105                                      "Some logs may have been dropped...\n");
 106                        break;
 107                }
 108
 109                memset(read_params, '\0', sizeof(*read_params));
 110                read_params->subcmd = CONSOLE_READ_RECENT;
 111                ret = cros_ec_cmd_xfer(ec->ec_dev, debug_info->read_msg);
 112                if (ret < 0) {
 113                        dev_err(ec->dev, "EC communication failed\n");
 114                        break;
 115                }
 116                if (debug_info->read_msg->result != EC_RES_SUCCESS) {
 117                        dev_err(ec->dev,
 118                                "EC failed to read the console log\n");
 119                        break;
 120                }
 121
 122                /* If the buffer is empty, we're done here. */
 123                if (ret == 0 || ec_buffer[0] == '\0')
 124                        break;
 125
 126                idx = 0;
 127                while (idx < ret && ec_buffer[idx] != '\0' && buf_space > 0) {
 128                        cb->buf[cb->head] = ec_buffer[idx];
 129                        cb->head = CIRC_ADD(cb->head, LOG_SIZE, 1);
 130                        idx++;
 131                        buf_space--;
 132                }
 133
 134                wake_up(&debug_info->log_wq);
 135        }
 136
 137        mutex_unlock(&debug_info->log_mutex);
 138
 139resched:
 140        schedule_delayed_work(&debug_info->log_poll_work,
 141                              msecs_to_jiffies(LOG_POLL_SEC * 1000));
 142}
 143
 144static int cros_ec_console_log_open(struct inode *inode, struct file *file)
 145{
 146        file->private_data = inode->i_private;
 147
 148        return nonseekable_open(inode, file);
 149}
 150
 151static ssize_t cros_ec_console_log_read(struct file *file, char __user *buf,
 152                                        size_t count, loff_t *ppos)
 153{
 154        struct cros_ec_debugfs *debug_info = file->private_data;
 155        struct circ_buf *cb = &debug_info->log_buffer;
 156        ssize_t ret;
 157
 158        mutex_lock(&debug_info->log_mutex);
 159
 160        while (!CIRC_CNT(cb->head, cb->tail, LOG_SIZE)) {
 161                if (file->f_flags & O_NONBLOCK) {
 162                        ret = -EAGAIN;
 163                        goto error;
 164                }
 165
 166                mutex_unlock(&debug_info->log_mutex);
 167
 168                ret = wait_event_interruptible(debug_info->log_wq,
 169                                        CIRC_CNT(cb->head, cb->tail, LOG_SIZE));
 170                if (ret < 0)
 171                        return ret;
 172
 173                mutex_lock(&debug_info->log_mutex);
 174        }
 175
 176        /* Only copy until the end of the circular buffer, and let userspace
 177         * retry to get the rest of the data.
 178         */
 179        ret = min_t(size_t, CIRC_CNT_TO_END(cb->head, cb->tail, LOG_SIZE),
 180                    count);
 181
 182        if (copy_to_user(buf, cb->buf + cb->tail, ret)) {
 183                ret = -EFAULT;
 184                goto error;
 185        }
 186
 187        cb->tail = CIRC_ADD(cb->tail, LOG_SIZE, ret);
 188
 189error:
 190        mutex_unlock(&debug_info->log_mutex);
 191        return ret;
 192}
 193
 194static unsigned int cros_ec_console_log_poll(struct file *file,
 195                                             poll_table *wait)
 196{
 197        struct cros_ec_debugfs *debug_info = file->private_data;
 198        unsigned int mask = 0;
 199
 200        poll_wait(file, &debug_info->log_wq, wait);
 201
 202        mutex_lock(&debug_info->log_mutex);
 203        if (CIRC_CNT(debug_info->log_buffer.head,
 204                     debug_info->log_buffer.tail,
 205                     LOG_SIZE))
 206                mask |= POLLIN | POLLRDNORM;
 207        mutex_unlock(&debug_info->log_mutex);
 208
 209        return mask;
 210}
 211
 212static int cros_ec_console_log_release(struct inode *inode, struct file *file)
 213{
 214        return 0;
 215}
 216
 217const struct file_operations cros_ec_console_log_fops = {
 218        .owner = THIS_MODULE,
 219        .open = cros_ec_console_log_open,
 220        .read = cros_ec_console_log_read,
 221        .llseek = no_llseek,
 222        .poll = cros_ec_console_log_poll,
 223        .release = cros_ec_console_log_release,
 224};
 225
 226static int ec_read_version_supported(struct cros_ec_dev *ec)
 227{
 228        struct ec_params_get_cmd_versions_v1 *params;
 229        struct ec_response_get_cmd_versions *response;
 230        int ret;
 231
 232        struct cros_ec_command *msg;
 233
 234        msg = kzalloc(sizeof(*msg) + max(sizeof(*params), sizeof(*response)),
 235                GFP_KERNEL);
 236        if (!msg)
 237                return 0;
 238
 239        msg->command = EC_CMD_GET_CMD_VERSIONS + ec->cmd_offset;
 240        msg->outsize = sizeof(*params);
 241        msg->insize = sizeof(*response);
 242
 243        params = (struct ec_params_get_cmd_versions_v1 *)msg->data;
 244        params->cmd = EC_CMD_CONSOLE_READ;
 245        response = (struct ec_response_get_cmd_versions *)msg->data;
 246
 247        ret = cros_ec_cmd_xfer(ec->ec_dev, msg) >= 0 &&
 248                msg->result == EC_RES_SUCCESS &&
 249                (response->version_mask & EC_VER_MASK(1));
 250
 251        kfree(msg);
 252
 253        return ret;
 254}
 255
 256static int cros_ec_create_console_log(struct cros_ec_debugfs *debug_info)
 257{
 258        struct cros_ec_dev *ec = debug_info->ec;
 259        char *buf;
 260        int read_params_size;
 261        int read_response_size;
 262
 263        if (!ec_read_version_supported(ec)) {
 264                dev_warn(ec->dev,
 265                        "device does not support reading the console log\n");
 266                return 0;
 267        }
 268
 269        buf = devm_kzalloc(ec->dev, LOG_SIZE, GFP_KERNEL);
 270        if (!buf)
 271                return -ENOMEM;
 272
 273        read_params_size = sizeof(struct ec_params_console_read_v1);
 274        read_response_size = ec->ec_dev->max_response;
 275        debug_info->read_msg = devm_kzalloc(ec->dev,
 276                sizeof(*debug_info->read_msg) +
 277                        max(read_params_size, read_response_size), GFP_KERNEL);
 278        if (!debug_info->read_msg)
 279                return -ENOMEM;
 280
 281        debug_info->read_msg->version = 1;
 282        debug_info->read_msg->command = EC_CMD_CONSOLE_READ + ec->cmd_offset;
 283        debug_info->read_msg->outsize = read_params_size;
 284        debug_info->read_msg->insize = read_response_size;
 285
 286        debug_info->log_buffer.buf = buf;
 287        debug_info->log_buffer.head = 0;
 288        debug_info->log_buffer.tail = 0;
 289
 290        mutex_init(&debug_info->log_mutex);
 291        init_waitqueue_head(&debug_info->log_wq);
 292
 293        if (!debugfs_create_file("console_log",
 294                                 S_IFREG | S_IRUGO,
 295                                 debug_info->dir,
 296                                 debug_info,
 297                                 &cros_ec_console_log_fops))
 298                return -ENOMEM;
 299
 300        INIT_DELAYED_WORK(&debug_info->log_poll_work,
 301                          cros_ec_console_log_work);
 302        schedule_delayed_work(&debug_info->log_poll_work, 0);
 303
 304        return 0;
 305}
 306
 307static void cros_ec_cleanup_console_log(struct cros_ec_debugfs *debug_info)
 308{
 309        if (debug_info->log_buffer.buf) {
 310                cancel_delayed_work_sync(&debug_info->log_poll_work);
 311                mutex_destroy(&debug_info->log_mutex);
 312        }
 313}
 314
 315static int cros_ec_create_panicinfo(struct cros_ec_debugfs *debug_info)
 316{
 317        struct cros_ec_device *ec_dev = debug_info->ec->ec_dev;
 318        int ret;
 319        struct cros_ec_command *msg;
 320        int insize;
 321
 322        insize = ec_dev->max_response;
 323
 324        msg = devm_kzalloc(debug_info->ec->dev,
 325                        sizeof(*msg) + insize, GFP_KERNEL);
 326        if (!msg)
 327                return -ENOMEM;
 328
 329        msg->command = EC_CMD_GET_PANIC_INFO;
 330        msg->insize = insize;
 331
 332        ret = cros_ec_cmd_xfer(ec_dev, msg);
 333        if (ret < 0) {
 334                dev_warn(debug_info->ec->dev, "Cannot read panicinfo.\n");
 335                ret = 0;
 336                goto free;
 337        }
 338
 339        /* No panic data */
 340        if (ret == 0)
 341                goto free;
 342
 343        debug_info->panicinfo_blob.data = msg->data;
 344        debug_info->panicinfo_blob.size = ret;
 345
 346        if (!debugfs_create_blob("panicinfo",
 347                                 S_IFREG | S_IRUGO,
 348                                 debug_info->dir,
 349                                 &debug_info->panicinfo_blob)) {
 350                ret = -ENOMEM;
 351                goto free;
 352        }
 353
 354        return 0;
 355
 356free:
 357        devm_kfree(debug_info->ec->dev, msg);
 358        return ret;
 359}
 360
 361int cros_ec_debugfs_init(struct cros_ec_dev *ec)
 362{
 363        struct cros_ec_platform *ec_platform = dev_get_platdata(ec->dev);
 364        const char *name = ec_platform->ec_name;
 365        struct cros_ec_debugfs *debug_info;
 366        int ret;
 367
 368        debug_info = devm_kzalloc(ec->dev, sizeof(*debug_info), GFP_KERNEL);
 369        if (!debug_info)
 370                return -ENOMEM;
 371
 372        debug_info->ec = ec;
 373        debug_info->dir = debugfs_create_dir(name, NULL);
 374        if (!debug_info->dir)
 375                return -ENOMEM;
 376
 377        ret = cros_ec_create_panicinfo(debug_info);
 378        if (ret)
 379                goto remove_debugfs;
 380
 381        ret = cros_ec_create_console_log(debug_info);
 382        if (ret)
 383                goto remove_debugfs;
 384
 385        ec->debug_info = debug_info;
 386
 387        return 0;
 388
 389remove_debugfs:
 390        debugfs_remove_recursive(debug_info->dir);
 391        return ret;
 392}
 393
 394void cros_ec_debugfs_remove(struct cros_ec_dev *ec)
 395{
 396        if (!ec->debug_info)
 397                return;
 398
 399        debugfs_remove_recursive(ec->debug_info->dir);
 400        cros_ec_cleanup_console_log(ec->debug_info);
 401}
 402