cregit-Linux how code gets into the kernel

Release 4.16 drivers/platform/chrome/cros_ec_debugfs.c

/*
 * 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>


#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
 * @panicinfo_blob: panicinfo debugfs blob
 */

struct cros_ec_debugfs {
	
struct cros_ec_dev *ec;
	
struct dentry *dir;
	/* EC log */
	
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;
	/* EC panicinfo */
	
struct debugfs_blob_wrapper panicinfo_blob;
};

/*
 * 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)); }

Contributors

PersonTokensPropCommitsCommitProp
Eric Caruso381100.00%1100.00%
Total381100.00%1100.00%


static int cros_ec_console_log_open(struct inode *inode, struct file *file) { file->private_data = inode->i_private; return nonseekable_open(inode, file); }

Contributors

PersonTokensPropCommitsCommitProp
Eric Caruso32100.00%1100.00%
Total32100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Eric Caruso214100.00%1100.00%
Total214100.00%1100.00%


static __poll_t cros_ec_console_log_poll(struct file *file, poll_table *wait) { struct cros_ec_debugfs *debug_info = file->private_data; __poll_t 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 |= EPOLLIN | EPOLLRDNORM; mutex_unlock(&debug_info->log_mutex); return mask; }

Contributors

PersonTokensPropCommitsCommitProp
Eric Caruso8195.29%133.33%
Al Viro22.35%133.33%
Linus Torvalds22.35%133.33%
Total85100.00%3100.00%


static int cros_ec_console_log_release(struct inode *inode, struct file *file) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Eric Caruso19100.00%1100.00%
Total19100.00%1100.00%

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; }

Contributors

PersonTokensPropCommitsCommitProp
Eric Caruso15797.52%150.00%
Shawn Nematbakhsh42.48%150.00%
Total161100.00%2100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Eric Caruso253100.00%1100.00%
Total253100.00%1100.00%


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); } }

Contributors

PersonTokensPropCommitsCommitProp
Eric Caruso37100.00%1100.00%
Total37100.00%1100.00%


static int cros_ec_create_panicinfo(struct cros_ec_debugfs *debug_info) { struct cros_ec_device *ec_dev = debug_info->ec->ec_dev; int ret; struct cros_ec_command *msg; int insize; insize = ec_dev->max_response; msg = devm_kzalloc(debug_info->ec->dev, sizeof(*msg) + insize, GFP_KERNEL); if (!msg) return -ENOMEM; msg->command = EC_CMD_GET_PANIC_INFO; msg->insize = insize; ret = cros_ec_cmd_xfer(ec_dev, msg); if (ret < 0) { dev_warn(debug_info->ec->dev, "Cannot read panicinfo.\n"); ret = 0; goto free; } /* No panic data */ if (ret == 0) goto free; debug_info->panicinfo_blob.data = msg->data; debug_info->panicinfo_blob.size = ret; if (!debugfs_create_blob("panicinfo", S_IFREG | S_IRUGO, debug_info->dir, &debug_info->panicinfo_blob)) { ret = -ENOMEM; goto free; } return 0; free: devm_kfree(debug_info->ec->dev, msg); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Nicolas Boichat194100.00%1100.00%
Total194100.00%1100.00%


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_panicinfo(debug_info); if (ret) goto remove_debugfs; 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; }

Contributors

PersonTokensPropCommitsCommitProp
Eric Caruso12890.14%150.00%
Nicolas Boichat149.86%150.00%
Total142100.00%2100.00%

EXPORT_SYMBOL(cros_ec_debugfs_init);
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); }

Contributors

PersonTokensPropCommitsCommitProp
Eric Caruso34100.00%1100.00%
Total34100.00%1100.00%

EXPORT_SYMBOL(cros_ec_debugfs_remove);

Overall Contributors

PersonTokensPropCommitsCommitProp
Eric Caruso146686.29%116.67%
Nicolas Boichat21512.65%116.67%
Thierry Escande100.59%116.67%
Shawn Nematbakhsh40.24%116.67%
Al Viro20.12%116.67%
Linus Torvalds20.12%116.67%
Total1699100.00%6100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.