Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 // Debug logs for the ChromeOS EC
0003 //
0004 // Copyright (C) 2015 Google, Inc.
0005 
0006 #include <linux/circ_buf.h>
0007 #include <linux/debugfs.h>
0008 #include <linux/delay.h>
0009 #include <linux/fs.h>
0010 #include <linux/module.h>
0011 #include <linux/mutex.h>
0012 #include <linux/platform_data/cros_ec_commands.h>
0013 #include <linux/platform_data/cros_ec_proto.h>
0014 #include <linux/platform_device.h>
0015 #include <linux/poll.h>
0016 #include <linux/sched.h>
0017 #include <linux/slab.h>
0018 #include <linux/wait.h>
0019 
0020 #define DRV_NAME "cros-ec-debugfs"
0021 
0022 #define LOG_SHIFT       14
0023 #define LOG_SIZE        (1 << LOG_SHIFT)
0024 #define LOG_POLL_SEC        10
0025 
0026 #define CIRC_ADD(idx, size, value)  (((idx) + (value)) & ((size) - 1))
0027 
0028 /* waitqueue for log readers */
0029 static DECLARE_WAIT_QUEUE_HEAD(cros_ec_debugfs_log_wq);
0030 
0031 /**
0032  * struct cros_ec_debugfs - EC debugging information.
0033  *
0034  * @ec: EC device this debugfs information belongs to
0035  * @dir: dentry for debugfs files
0036  * @log_buffer: circular buffer for console log information
0037  * @read_msg: preallocated EC command and buffer to read console log
0038  * @log_mutex: mutex to protect circular buffer
0039  * @log_poll_work: recurring task to poll EC for new console log data
0040  * @panicinfo_blob: panicinfo debugfs blob
0041  */
0042 struct cros_ec_debugfs {
0043     struct cros_ec_dev *ec;
0044     struct dentry *dir;
0045     /* EC log */
0046     struct circ_buf log_buffer;
0047     struct cros_ec_command *read_msg;
0048     struct mutex log_mutex;
0049     struct delayed_work log_poll_work;
0050     /* EC panicinfo */
0051     struct debugfs_blob_wrapper panicinfo_blob;
0052 };
0053 
0054 /*
0055  * We need to make sure that the EC log buffer on the UART is large enough,
0056  * so that it is unlikely enough to overlow within LOG_POLL_SEC.
0057  */
0058 static void cros_ec_console_log_work(struct work_struct *__work)
0059 {
0060     struct cros_ec_debugfs *debug_info =
0061         container_of(to_delayed_work(__work),
0062                  struct cros_ec_debugfs,
0063                  log_poll_work);
0064     struct cros_ec_dev *ec = debug_info->ec;
0065     struct circ_buf *cb = &debug_info->log_buffer;
0066     struct cros_ec_command snapshot_msg = {
0067         .command = EC_CMD_CONSOLE_SNAPSHOT + ec->cmd_offset,
0068     };
0069 
0070     struct ec_params_console_read_v1 *read_params =
0071         (struct ec_params_console_read_v1 *)debug_info->read_msg->data;
0072     uint8_t *ec_buffer = (uint8_t *)debug_info->read_msg->data;
0073     int idx;
0074     int buf_space;
0075     int ret;
0076 
0077     ret = cros_ec_cmd_xfer_status(ec->ec_dev, &snapshot_msg);
0078     if (ret < 0)
0079         goto resched;
0080 
0081     /* Loop until we have read everything, or there's an error. */
0082     mutex_lock(&debug_info->log_mutex);
0083     buf_space = CIRC_SPACE(cb->head, cb->tail, LOG_SIZE);
0084 
0085     while (1) {
0086         if (!buf_space) {
0087             dev_info_once(ec->dev,
0088                       "Some logs may have been dropped...\n");
0089             break;
0090         }
0091 
0092         memset(read_params, '\0', sizeof(*read_params));
0093         read_params->subcmd = CONSOLE_READ_RECENT;
0094         ret = cros_ec_cmd_xfer_status(ec->ec_dev,
0095                           debug_info->read_msg);
0096         if (ret < 0)
0097             break;
0098 
0099         /* If the buffer is empty, we're done here. */
0100         if (ret == 0 || ec_buffer[0] == '\0')
0101             break;
0102 
0103         idx = 0;
0104         while (idx < ret && ec_buffer[idx] != '\0' && buf_space > 0) {
0105             cb->buf[cb->head] = ec_buffer[idx];
0106             cb->head = CIRC_ADD(cb->head, LOG_SIZE, 1);
0107             idx++;
0108             buf_space--;
0109         }
0110 
0111         wake_up(&cros_ec_debugfs_log_wq);
0112     }
0113 
0114     mutex_unlock(&debug_info->log_mutex);
0115 
0116 resched:
0117     schedule_delayed_work(&debug_info->log_poll_work,
0118                   msecs_to_jiffies(LOG_POLL_SEC * 1000));
0119 }
0120 
0121 static int cros_ec_console_log_open(struct inode *inode, struct file *file)
0122 {
0123     file->private_data = inode->i_private;
0124 
0125     return stream_open(inode, file);
0126 }
0127 
0128 static ssize_t cros_ec_console_log_read(struct file *file, char __user *buf,
0129                     size_t count, loff_t *ppos)
0130 {
0131     struct cros_ec_debugfs *debug_info = file->private_data;
0132     struct circ_buf *cb = &debug_info->log_buffer;
0133     ssize_t ret;
0134 
0135     mutex_lock(&debug_info->log_mutex);
0136 
0137     while (!CIRC_CNT(cb->head, cb->tail, LOG_SIZE)) {
0138         if (file->f_flags & O_NONBLOCK) {
0139             ret = -EAGAIN;
0140             goto error;
0141         }
0142 
0143         mutex_unlock(&debug_info->log_mutex);
0144 
0145         ret = wait_event_interruptible(cros_ec_debugfs_log_wq,
0146                     CIRC_CNT(cb->head, cb->tail, LOG_SIZE));
0147         if (ret < 0)
0148             return ret;
0149 
0150         mutex_lock(&debug_info->log_mutex);
0151     }
0152 
0153     /* Only copy until the end of the circular buffer, and let userspace
0154      * retry to get the rest of the data.
0155      */
0156     ret = min_t(size_t, CIRC_CNT_TO_END(cb->head, cb->tail, LOG_SIZE),
0157             count);
0158 
0159     if (copy_to_user(buf, cb->buf + cb->tail, ret)) {
0160         ret = -EFAULT;
0161         goto error;
0162     }
0163 
0164     cb->tail = CIRC_ADD(cb->tail, LOG_SIZE, ret);
0165 
0166 error:
0167     mutex_unlock(&debug_info->log_mutex);
0168     return ret;
0169 }
0170 
0171 static __poll_t cros_ec_console_log_poll(struct file *file,
0172                          poll_table *wait)
0173 {
0174     struct cros_ec_debugfs *debug_info = file->private_data;
0175     __poll_t mask = 0;
0176 
0177     poll_wait(file, &cros_ec_debugfs_log_wq, wait);
0178 
0179     mutex_lock(&debug_info->log_mutex);
0180     if (CIRC_CNT(debug_info->log_buffer.head,
0181              debug_info->log_buffer.tail,
0182              LOG_SIZE))
0183         mask |= EPOLLIN | EPOLLRDNORM;
0184     mutex_unlock(&debug_info->log_mutex);
0185 
0186     return mask;
0187 }
0188 
0189 static int cros_ec_console_log_release(struct inode *inode, struct file *file)
0190 {
0191     return 0;
0192 }
0193 
0194 static ssize_t cros_ec_pdinfo_read(struct file *file,
0195                    char __user *user_buf,
0196                    size_t count,
0197                    loff_t *ppos)
0198 {
0199     char read_buf[EC_USB_PD_MAX_PORTS * 40], *p = read_buf;
0200     struct cros_ec_debugfs *debug_info = file->private_data;
0201     struct cros_ec_device *ec_dev = debug_info->ec->ec_dev;
0202     struct {
0203         struct cros_ec_command msg;
0204         union {
0205             struct ec_response_usb_pd_control_v1 resp;
0206             struct ec_params_usb_pd_control params;
0207         };
0208     } __packed ec_buf;
0209     struct cros_ec_command *msg;
0210     struct ec_response_usb_pd_control_v1 *resp;
0211     struct ec_params_usb_pd_control *params;
0212     int i;
0213 
0214     msg = &ec_buf.msg;
0215     params = (struct ec_params_usb_pd_control *)msg->data;
0216     resp = (struct ec_response_usb_pd_control_v1 *)msg->data;
0217 
0218     msg->command = EC_CMD_USB_PD_CONTROL;
0219     msg->version = 1;
0220     msg->insize = sizeof(*resp);
0221     msg->outsize = sizeof(*params);
0222 
0223     /*
0224      * Read status from all PD ports until failure, typically caused
0225      * by attempting to read status on a port that doesn't exist.
0226      */
0227     for (i = 0; i < EC_USB_PD_MAX_PORTS; ++i) {
0228         params->port = i;
0229         params->role = 0;
0230         params->mux = 0;
0231         params->swap = 0;
0232 
0233         if (cros_ec_cmd_xfer_status(ec_dev, msg) < 0)
0234             break;
0235 
0236         p += scnprintf(p, sizeof(read_buf) + read_buf - p,
0237                    "p%d: %s en:%.2x role:%.2x pol:%.2x\n", i,
0238                    resp->state, resp->enabled, resp->role,
0239                    resp->polarity);
0240     }
0241 
0242     return simple_read_from_buffer(user_buf, count, ppos,
0243                        read_buf, p - read_buf);
0244 }
0245 
0246 static bool cros_ec_uptime_is_supported(struct cros_ec_device *ec_dev)
0247 {
0248     struct {
0249         struct cros_ec_command cmd;
0250         struct ec_response_uptime_info resp;
0251     } __packed msg = {};
0252     int ret;
0253 
0254     msg.cmd.command = EC_CMD_GET_UPTIME_INFO;
0255     msg.cmd.insize = sizeof(msg.resp);
0256 
0257     ret = cros_ec_cmd_xfer_status(ec_dev, &msg.cmd);
0258     if (ret == -EPROTO && msg.cmd.result == EC_RES_INVALID_COMMAND)
0259         return false;
0260 
0261     /* Other errors maybe a transient error, do not rule about support. */
0262     return true;
0263 }
0264 
0265 static ssize_t cros_ec_uptime_read(struct file *file, char __user *user_buf,
0266                    size_t count, loff_t *ppos)
0267 {
0268     struct cros_ec_debugfs *debug_info = file->private_data;
0269     struct cros_ec_device *ec_dev = debug_info->ec->ec_dev;
0270     struct {
0271         struct cros_ec_command cmd;
0272         struct ec_response_uptime_info resp;
0273     } __packed msg = {};
0274     struct ec_response_uptime_info *resp;
0275     char read_buf[32];
0276     int ret;
0277 
0278     resp = (struct ec_response_uptime_info *)&msg.resp;
0279 
0280     msg.cmd.command = EC_CMD_GET_UPTIME_INFO;
0281     msg.cmd.insize = sizeof(*resp);
0282 
0283     ret = cros_ec_cmd_xfer_status(ec_dev, &msg.cmd);
0284     if (ret < 0)
0285         return ret;
0286 
0287     ret = scnprintf(read_buf, sizeof(read_buf), "%u\n",
0288             resp->time_since_ec_boot_ms);
0289 
0290     return simple_read_from_buffer(user_buf, count, ppos, read_buf, ret);
0291 }
0292 
0293 static const struct file_operations cros_ec_console_log_fops = {
0294     .owner = THIS_MODULE,
0295     .open = cros_ec_console_log_open,
0296     .read = cros_ec_console_log_read,
0297     .llseek = no_llseek,
0298     .poll = cros_ec_console_log_poll,
0299     .release = cros_ec_console_log_release,
0300 };
0301 
0302 static const struct file_operations cros_ec_pdinfo_fops = {
0303     .owner = THIS_MODULE,
0304     .open = simple_open,
0305     .read = cros_ec_pdinfo_read,
0306     .llseek = default_llseek,
0307 };
0308 
0309 static const struct file_operations cros_ec_uptime_fops = {
0310     .owner = THIS_MODULE,
0311     .open = simple_open,
0312     .read = cros_ec_uptime_read,
0313     .llseek = default_llseek,
0314 };
0315 
0316 static int ec_read_version_supported(struct cros_ec_dev *ec)
0317 {
0318     struct ec_params_get_cmd_versions_v1 *params;
0319     struct ec_response_get_cmd_versions *response;
0320     int ret;
0321 
0322     struct cros_ec_command *msg;
0323 
0324     msg = kzalloc(sizeof(*msg) + max(sizeof(*params), sizeof(*response)),
0325         GFP_KERNEL);
0326     if (!msg)
0327         return 0;
0328 
0329     msg->command = EC_CMD_GET_CMD_VERSIONS + ec->cmd_offset;
0330     msg->outsize = sizeof(*params);
0331     msg->insize = sizeof(*response);
0332 
0333     params = (struct ec_params_get_cmd_versions_v1 *)msg->data;
0334     params->cmd = EC_CMD_CONSOLE_READ;
0335     response = (struct ec_response_get_cmd_versions *)msg->data;
0336 
0337     ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg) >= 0 &&
0338           response->version_mask & EC_VER_MASK(1);
0339 
0340     kfree(msg);
0341 
0342     return ret;
0343 }
0344 
0345 static int cros_ec_create_console_log(struct cros_ec_debugfs *debug_info)
0346 {
0347     struct cros_ec_dev *ec = debug_info->ec;
0348     char *buf;
0349     int read_params_size;
0350     int read_response_size;
0351 
0352     /*
0353      * If the console log feature is not supported return silently and
0354      * don't create the console_log entry.
0355      */
0356     if (!ec_read_version_supported(ec))
0357         return 0;
0358 
0359     buf = devm_kzalloc(ec->dev, LOG_SIZE, GFP_KERNEL);
0360     if (!buf)
0361         return -ENOMEM;
0362 
0363     read_params_size = sizeof(struct ec_params_console_read_v1);
0364     read_response_size = ec->ec_dev->max_response;
0365     debug_info->read_msg = devm_kzalloc(ec->dev,
0366         sizeof(*debug_info->read_msg) +
0367             max(read_params_size, read_response_size), GFP_KERNEL);
0368     if (!debug_info->read_msg)
0369         return -ENOMEM;
0370 
0371     debug_info->read_msg->version = 1;
0372     debug_info->read_msg->command = EC_CMD_CONSOLE_READ + ec->cmd_offset;
0373     debug_info->read_msg->outsize = read_params_size;
0374     debug_info->read_msg->insize = read_response_size;
0375 
0376     debug_info->log_buffer.buf = buf;
0377     debug_info->log_buffer.head = 0;
0378     debug_info->log_buffer.tail = 0;
0379 
0380     mutex_init(&debug_info->log_mutex);
0381 
0382     debugfs_create_file("console_log", S_IFREG | 0444, debug_info->dir,
0383                 debug_info, &cros_ec_console_log_fops);
0384 
0385     INIT_DELAYED_WORK(&debug_info->log_poll_work,
0386               cros_ec_console_log_work);
0387     schedule_delayed_work(&debug_info->log_poll_work, 0);
0388 
0389     return 0;
0390 }
0391 
0392 static void cros_ec_cleanup_console_log(struct cros_ec_debugfs *debug_info)
0393 {
0394     if (debug_info->log_buffer.buf) {
0395         cancel_delayed_work_sync(&debug_info->log_poll_work);
0396         mutex_destroy(&debug_info->log_mutex);
0397     }
0398 }
0399 
0400 static int cros_ec_create_panicinfo(struct cros_ec_debugfs *debug_info)
0401 {
0402     struct cros_ec_device *ec_dev = debug_info->ec->ec_dev;
0403     int ret;
0404     struct cros_ec_command *msg;
0405     int insize;
0406 
0407     insize = ec_dev->max_response;
0408 
0409     msg = devm_kzalloc(debug_info->ec->dev,
0410             sizeof(*msg) + insize, GFP_KERNEL);
0411     if (!msg)
0412         return -ENOMEM;
0413 
0414     msg->command = EC_CMD_GET_PANIC_INFO;
0415     msg->insize = insize;
0416 
0417     ret = cros_ec_cmd_xfer_status(ec_dev, msg);
0418     if (ret < 0) {
0419         ret = 0;
0420         goto free;
0421     }
0422 
0423     /* No panic data */
0424     if (ret == 0)
0425         goto free;
0426 
0427     debug_info->panicinfo_blob.data = msg->data;
0428     debug_info->panicinfo_blob.size = ret;
0429 
0430     debugfs_create_blob("panicinfo", S_IFREG | 0444, debug_info->dir,
0431                 &debug_info->panicinfo_blob);
0432 
0433     return 0;
0434 
0435 free:
0436     devm_kfree(debug_info->ec->dev, msg);
0437     return ret;
0438 }
0439 
0440 static int cros_ec_debugfs_probe(struct platform_device *pd)
0441 {
0442     struct cros_ec_dev *ec = dev_get_drvdata(pd->dev.parent);
0443     struct cros_ec_platform *ec_platform = dev_get_platdata(ec->dev);
0444     const char *name = ec_platform->ec_name;
0445     struct cros_ec_debugfs *debug_info;
0446     int ret;
0447 
0448     debug_info = devm_kzalloc(ec->dev, sizeof(*debug_info), GFP_KERNEL);
0449     if (!debug_info)
0450         return -ENOMEM;
0451 
0452     debug_info->ec = ec;
0453     debug_info->dir = debugfs_create_dir(name, NULL);
0454 
0455     ret = cros_ec_create_panicinfo(debug_info);
0456     if (ret)
0457         goto remove_debugfs;
0458 
0459     ret = cros_ec_create_console_log(debug_info);
0460     if (ret)
0461         goto remove_debugfs;
0462 
0463     debugfs_create_file("pdinfo", 0444, debug_info->dir, debug_info,
0464                 &cros_ec_pdinfo_fops);
0465 
0466     if (cros_ec_uptime_is_supported(ec->ec_dev))
0467         debugfs_create_file("uptime", 0444, debug_info->dir, debug_info,
0468                     &cros_ec_uptime_fops);
0469 
0470     debugfs_create_x32("last_resume_result", 0444, debug_info->dir,
0471                &ec->ec_dev->last_resume_result);
0472 
0473     ec->debug_info = debug_info;
0474 
0475     dev_set_drvdata(&pd->dev, ec);
0476 
0477     return 0;
0478 
0479 remove_debugfs:
0480     debugfs_remove_recursive(debug_info->dir);
0481     return ret;
0482 }
0483 
0484 static int cros_ec_debugfs_remove(struct platform_device *pd)
0485 {
0486     struct cros_ec_dev *ec = dev_get_drvdata(pd->dev.parent);
0487 
0488     debugfs_remove_recursive(ec->debug_info->dir);
0489     cros_ec_cleanup_console_log(ec->debug_info);
0490 
0491     return 0;
0492 }
0493 
0494 static int __maybe_unused cros_ec_debugfs_suspend(struct device *dev)
0495 {
0496     struct cros_ec_dev *ec = dev_get_drvdata(dev);
0497 
0498     if (ec->debug_info->log_buffer.buf)
0499         cancel_delayed_work_sync(&ec->debug_info->log_poll_work);
0500 
0501     return 0;
0502 }
0503 
0504 static int __maybe_unused cros_ec_debugfs_resume(struct device *dev)
0505 {
0506     struct cros_ec_dev *ec = dev_get_drvdata(dev);
0507 
0508     if (ec->debug_info->log_buffer.buf)
0509         schedule_delayed_work(&ec->debug_info->log_poll_work, 0);
0510 
0511     return 0;
0512 }
0513 
0514 static SIMPLE_DEV_PM_OPS(cros_ec_debugfs_pm_ops,
0515              cros_ec_debugfs_suspend, cros_ec_debugfs_resume);
0516 
0517 static struct platform_driver cros_ec_debugfs_driver = {
0518     .driver = {
0519         .name = DRV_NAME,
0520         .pm = &cros_ec_debugfs_pm_ops,
0521     },
0522     .probe = cros_ec_debugfs_probe,
0523     .remove = cros_ec_debugfs_remove,
0524 };
0525 
0526 module_platform_driver(cros_ec_debugfs_driver);
0527 
0528 MODULE_LICENSE("GPL");
0529 MODULE_DESCRIPTION("Debug logs for ChromeOS EC");
0530 MODULE_ALIAS("platform:" DRV_NAME);