805 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved.4 */5#include <linux/debugfs.h>6#include <linux/dma-mapping.h>7#include <linux/slab.h>8#include <linux/uaccess.h>9 10#include <soc/tegra/bpmp.h>11#include <soc/tegra/bpmp-abi.h>12 13static DEFINE_MUTEX(bpmp_debug_lock);14 15struct seqbuf {16 char *buf;17 size_t pos;18 size_t size;19};20 21static void seqbuf_init(struct seqbuf *seqbuf, void *buf, size_t size)22{23 seqbuf->buf = buf;24 seqbuf->size = size;25 seqbuf->pos = 0;26}27 28static size_t seqbuf_avail(struct seqbuf *seqbuf)29{30 return seqbuf->pos < seqbuf->size ? seqbuf->size - seqbuf->pos : 0;31}32 33static size_t seqbuf_status(struct seqbuf *seqbuf)34{35 return seqbuf->pos <= seqbuf->size ? 0 : -EOVERFLOW;36}37 38static int seqbuf_eof(struct seqbuf *seqbuf)39{40 return seqbuf->pos >= seqbuf->size;41}42 43static int seqbuf_read(struct seqbuf *seqbuf, void *buf, size_t nbyte)44{45 nbyte = min(nbyte, seqbuf_avail(seqbuf));46 memcpy(buf, seqbuf->buf + seqbuf->pos, nbyte);47 seqbuf->pos += nbyte;48 return seqbuf_status(seqbuf);49}50 51static int seqbuf_read_u32(struct seqbuf *seqbuf, u32 *v)52{53 return seqbuf_read(seqbuf, v, 4);54}55 56static int seqbuf_read_str(struct seqbuf *seqbuf, const char **str)57{58 *str = seqbuf->buf + seqbuf->pos;59 seqbuf->pos += strnlen(*str, seqbuf_avail(seqbuf));60 seqbuf->pos++;61 return seqbuf_status(seqbuf);62}63 64static void seqbuf_seek(struct seqbuf *seqbuf, ssize_t offset)65{66 seqbuf->pos += offset;67}68 69/* map filename in Linux debugfs to corresponding entry in BPMP */70static const char *get_filename(struct tegra_bpmp *bpmp,71 const struct file *file, char *buf, int size)72{73 const char *root_path, *filename = NULL;74 char *root_path_buf;75 size_t root_len;76 size_t root_path_buf_len = 512;77 78 root_path_buf = kzalloc(root_path_buf_len, GFP_KERNEL);79 if (!root_path_buf)80 return NULL;81 82 root_path = dentry_path(bpmp->debugfs_mirror, root_path_buf,83 root_path_buf_len);84 if (IS_ERR(root_path))85 goto out;86 87 root_len = strlen(root_path);88 89 filename = dentry_path(file->f_path.dentry, buf, size);90 if (IS_ERR(filename)) {91 filename = NULL;92 goto out;93 }94 95 if (strlen(filename) < root_len || strncmp(filename, root_path, root_len)) {96 filename = NULL;97 goto out;98 }99 100 filename += root_len;101 102out:103 kfree(root_path_buf);104 return filename;105}106 107static int mrq_debug_open(struct tegra_bpmp *bpmp, const char *name,108 u32 *fd, u32 *len, bool write)109{110 struct mrq_debug_request req = {111 .cmd = write ? CMD_DEBUG_OPEN_WO : CMD_DEBUG_OPEN_RO,112 };113 struct mrq_debug_response resp;114 struct tegra_bpmp_message msg = {115 .mrq = MRQ_DEBUG,116 .tx = {117 .data = &req,118 .size = sizeof(req),119 },120 .rx = {121 .data = &resp,122 .size = sizeof(resp),123 },124 };125 ssize_t sz_name;126 int err = 0;127 128 sz_name = strscpy(req.fop.name, name, sizeof(req.fop.name));129 if (sz_name < 0) {130 pr_err("File name too large: %s\n", name);131 return -EINVAL;132 }133 134 err = tegra_bpmp_transfer(bpmp, &msg);135 if (err < 0)136 return err;137 else if (msg.rx.ret < 0)138 return -EINVAL;139 140 *len = resp.fop.datalen;141 *fd = resp.fop.fd;142 143 return 0;144}145 146static int mrq_debug_close(struct tegra_bpmp *bpmp, u32 fd)147{148 struct mrq_debug_request req = {149 .cmd = CMD_DEBUG_CLOSE,150 .frd = {151 .fd = fd,152 },153 };154 struct mrq_debug_response resp;155 struct tegra_bpmp_message msg = {156 .mrq = MRQ_DEBUG,157 .tx = {158 .data = &req,159 .size = sizeof(req),160 },161 .rx = {162 .data = &resp,163 .size = sizeof(resp),164 },165 };166 int err = 0;167 168 err = tegra_bpmp_transfer(bpmp, &msg);169 if (err < 0)170 return err;171 else if (msg.rx.ret < 0)172 return -EINVAL;173 174 return 0;175}176 177static int mrq_debug_read(struct tegra_bpmp *bpmp, const char *name,178 char *data, size_t sz_data, u32 *nbytes)179{180 struct mrq_debug_request req = {181 .cmd = CMD_DEBUG_READ,182 };183 struct mrq_debug_response resp;184 struct tegra_bpmp_message msg = {185 .mrq = MRQ_DEBUG,186 .tx = {187 .data = &req,188 .size = sizeof(req),189 },190 .rx = {191 .data = &resp,192 .size = sizeof(resp),193 },194 };195 u32 fd = 0, len = 0;196 int remaining, err, close_err;197 198 mutex_lock(&bpmp_debug_lock);199 err = mrq_debug_open(bpmp, name, &fd, &len, 0);200 if (err)201 goto out;202 203 if (len > sz_data) {204 err = -EFBIG;205 goto close;206 }207 208 req.frd.fd = fd;209 remaining = len;210 211 while (remaining > 0) {212 err = tegra_bpmp_transfer(bpmp, &msg);213 if (err < 0) {214 goto close;215 } else if (msg.rx.ret < 0) {216 err = -EINVAL;217 goto close;218 }219 220 if (resp.frd.readlen > remaining) {221 pr_err("%s: read data length invalid\n", __func__);222 err = -EINVAL;223 goto close;224 }225 226 memcpy(data, resp.frd.data, resp.frd.readlen);227 data += resp.frd.readlen;228 remaining -= resp.frd.readlen;229 }230 231 *nbytes = len;232 233close:234 close_err = mrq_debug_close(bpmp, fd);235 if (!err)236 err = close_err;237out:238 mutex_unlock(&bpmp_debug_lock);239 return err;240}241 242static int mrq_debug_write(struct tegra_bpmp *bpmp, const char *name,243 uint8_t *data, size_t sz_data)244{245 struct mrq_debug_request req = {246 .cmd = CMD_DEBUG_WRITE247 };248 struct mrq_debug_response resp;249 struct tegra_bpmp_message msg = {250 .mrq = MRQ_DEBUG,251 .tx = {252 .data = &req,253 .size = sizeof(req),254 },255 .rx = {256 .data = &resp,257 .size = sizeof(resp),258 },259 };260 u32 fd = 0, len = 0;261 size_t remaining;262 int err;263 264 mutex_lock(&bpmp_debug_lock);265 err = mrq_debug_open(bpmp, name, &fd, &len, 1);266 if (err)267 goto out;268 269 if (sz_data > len) {270 err = -EINVAL;271 goto close;272 }273 274 req.fwr.fd = fd;275 remaining = sz_data;276 277 while (remaining > 0) {278 len = min(remaining, sizeof(req.fwr.data));279 memcpy(req.fwr.data, data, len);280 req.fwr.datalen = len;281 282 err = tegra_bpmp_transfer(bpmp, &msg);283 if (err < 0) {284 goto close;285 } else if (msg.rx.ret < 0) {286 err = -EINVAL;287 goto close;288 }289 290 data += req.fwr.datalen;291 remaining -= req.fwr.datalen;292 }293 294close:295 err = mrq_debug_close(bpmp, fd);296out:297 mutex_unlock(&bpmp_debug_lock);298 return err;299}300 301static int bpmp_debug_show(struct seq_file *m, void *p)302{303 struct file *file = m->private;304 struct inode *inode = file_inode(file);305 struct tegra_bpmp *bpmp = inode->i_private;306 char fnamebuf[256];307 const char *filename;308 struct mrq_debug_request req = {309 .cmd = CMD_DEBUG_READ,310 };311 struct mrq_debug_response resp;312 struct tegra_bpmp_message msg = {313 .mrq = MRQ_DEBUG,314 .tx = {315 .data = &req,316 .size = sizeof(req),317 },318 .rx = {319 .data = &resp,320 .size = sizeof(resp),321 },322 };323 u32 fd = 0, len = 0;324 int remaining, err, close_err;325 326 filename = get_filename(bpmp, file, fnamebuf, sizeof(fnamebuf));327 if (!filename)328 return -ENOENT;329 330 mutex_lock(&bpmp_debug_lock);331 err = mrq_debug_open(bpmp, filename, &fd, &len, 0);332 if (err)333 goto out;334 335 req.frd.fd = fd;336 remaining = len;337 338 while (remaining > 0) {339 err = tegra_bpmp_transfer(bpmp, &msg);340 if (err < 0) {341 goto close;342 } else if (msg.rx.ret < 0) {343 err = -EINVAL;344 goto close;345 }346 347 if (resp.frd.readlen > remaining) {348 pr_err("%s: read data length invalid\n", __func__);349 err = -EINVAL;350 goto close;351 }352 353 seq_write(m, resp.frd.data, resp.frd.readlen);354 remaining -= resp.frd.readlen;355 }356 357close:358 close_err = mrq_debug_close(bpmp, fd);359 if (!err)360 err = close_err;361out:362 mutex_unlock(&bpmp_debug_lock);363 return err;364}365 366static ssize_t bpmp_debug_store(struct file *file, const char __user *buf,367 size_t count, loff_t *f_pos)368{369 struct inode *inode = file_inode(file);370 struct tegra_bpmp *bpmp = inode->i_private;371 char *databuf = NULL;372 char fnamebuf[256];373 const char *filename;374 ssize_t err;375 376 filename = get_filename(bpmp, file, fnamebuf, sizeof(fnamebuf));377 if (!filename)378 return -ENOENT;379 380 databuf = memdup_user(buf, count);381 if (IS_ERR(databuf))382 return PTR_ERR(databuf);383 384 err = mrq_debug_write(bpmp, filename, databuf, count);385 kfree(databuf);386 387 return err ?: count;388}389 390static int bpmp_debug_open(struct inode *inode, struct file *file)391{392 return single_open_size(file, bpmp_debug_show, file, SZ_256K);393}394 395static const struct file_operations bpmp_debug_fops = {396 .open = bpmp_debug_open,397 .read = seq_read,398 .llseek = seq_lseek,399 .write = bpmp_debug_store,400 .release = single_release,401};402 403static int bpmp_populate_debugfs_inband(struct tegra_bpmp *bpmp,404 struct dentry *parent,405 char *ppath)406{407 const size_t pathlen = SZ_256;408 const size_t bufsize = SZ_16K;409 struct dentry *dentry;410 u32 dsize, attrs = 0;411 struct seqbuf seqbuf;412 char *buf, *pathbuf;413 const char *name;414 int err = 0;415 416 if (!bpmp || !parent || !ppath)417 return -EINVAL;418 419 buf = kmalloc(bufsize, GFP_KERNEL);420 if (!buf)421 return -ENOMEM;422 423 pathbuf = kzalloc(pathlen, GFP_KERNEL);424 if (!pathbuf) {425 kfree(buf);426 return -ENOMEM;427 }428 429 err = mrq_debug_read(bpmp, ppath, buf, bufsize, &dsize);430 if (err)431 goto out;432 433 seqbuf_init(&seqbuf, buf, dsize);434 435 while (!seqbuf_eof(&seqbuf)) {436 err = seqbuf_read_u32(&seqbuf, &attrs);437 if (err)438 goto out;439 440 err = seqbuf_read_str(&seqbuf, &name);441 if (err < 0)442 goto out;443 444 if (attrs & DEBUGFS_S_ISDIR) {445 size_t len;446 447 dentry = debugfs_create_dir(name, parent);448 if (IS_ERR(dentry)) {449 err = PTR_ERR(dentry);450 goto out;451 }452 453 len = snprintf(pathbuf, pathlen, "%s%s/", ppath, name);454 if (len >= pathlen) {455 err = -EINVAL;456 goto out;457 }458 459 err = bpmp_populate_debugfs_inband(bpmp, dentry,460 pathbuf);461 if (err < 0)462 goto out;463 } else {464 umode_t mode;465 466 mode = attrs & DEBUGFS_S_IRUSR ? 0400 : 0;467 mode |= attrs & DEBUGFS_S_IWUSR ? 0200 : 0;468 dentry = debugfs_create_file(name, mode, parent, bpmp,469 &bpmp_debug_fops);470 if (IS_ERR(dentry)) {471 err = -ENOMEM;472 goto out;473 }474 }475 }476 477out:478 kfree(pathbuf);479 kfree(buf);480 481 return err;482}483 484static int mrq_debugfs_read(struct tegra_bpmp *bpmp,485 dma_addr_t name, size_t sz_name,486 dma_addr_t data, size_t sz_data,487 size_t *nbytes)488{489 struct mrq_debugfs_request req = {490 .cmd = CMD_DEBUGFS_READ,491 .fop = {492 .fnameaddr = (u32)name,493 .fnamelen = (u32)sz_name,494 .dataaddr = (u32)data,495 .datalen = (u32)sz_data,496 },497 };498 struct mrq_debugfs_response resp;499 struct tegra_bpmp_message msg = {500 .mrq = MRQ_DEBUGFS,501 .tx = {502 .data = &req,503 .size = sizeof(req),504 },505 .rx = {506 .data = &resp,507 .size = sizeof(resp),508 },509 };510 int err;511 512 err = tegra_bpmp_transfer(bpmp, &msg);513 if (err < 0)514 return err;515 else if (msg.rx.ret < 0)516 return -EINVAL;517 518 *nbytes = (size_t)resp.fop.nbytes;519 520 return 0;521}522 523static int mrq_debugfs_write(struct tegra_bpmp *bpmp,524 dma_addr_t name, size_t sz_name,525 dma_addr_t data, size_t sz_data)526{527 const struct mrq_debugfs_request req = {528 .cmd = CMD_DEBUGFS_WRITE,529 .fop = {530 .fnameaddr = (u32)name,531 .fnamelen = (u32)sz_name,532 .dataaddr = (u32)data,533 .datalen = (u32)sz_data,534 },535 };536 struct tegra_bpmp_message msg = {537 .mrq = MRQ_DEBUGFS,538 .tx = {539 .data = &req,540 .size = sizeof(req),541 },542 };543 544 return tegra_bpmp_transfer(bpmp, &msg);545}546 547static int mrq_debugfs_dumpdir(struct tegra_bpmp *bpmp, dma_addr_t addr,548 size_t size, size_t *nbytes)549{550 const struct mrq_debugfs_request req = {551 .cmd = CMD_DEBUGFS_DUMPDIR,552 .dumpdir = {553 .dataaddr = (u32)addr,554 .datalen = (u32)size,555 },556 };557 struct mrq_debugfs_response resp;558 struct tegra_bpmp_message msg = {559 .mrq = MRQ_DEBUGFS,560 .tx = {561 .data = &req,562 .size = sizeof(req),563 },564 .rx = {565 .data = &resp,566 .size = sizeof(resp),567 },568 };569 int err;570 571 err = tegra_bpmp_transfer(bpmp, &msg);572 if (err < 0)573 return err;574 else if (msg.rx.ret < 0)575 return -EINVAL;576 577 *nbytes = (size_t)resp.dumpdir.nbytes;578 579 return 0;580}581 582static int debugfs_show(struct seq_file *m, void *p)583{584 struct file *file = m->private;585 struct inode *inode = file_inode(file);586 struct tegra_bpmp *bpmp = inode->i_private;587 const size_t datasize = m->size;588 const size_t namesize = SZ_256;589 void *datavirt, *namevirt;590 dma_addr_t dataphys, namephys;591 char buf[256];592 const char *filename;593 size_t len, nbytes;594 int err;595 596 filename = get_filename(bpmp, file, buf, sizeof(buf));597 if (!filename)598 return -ENOENT;599 600 namevirt = dma_alloc_coherent(bpmp->dev, namesize, &namephys,601 GFP_KERNEL | GFP_DMA32);602 if (!namevirt)603 return -ENOMEM;604 605 datavirt = dma_alloc_coherent(bpmp->dev, datasize, &dataphys,606 GFP_KERNEL | GFP_DMA32);607 if (!datavirt) {608 err = -ENOMEM;609 goto free_namebuf;610 }611 612 len = strlen(filename);613 strscpy_pad(namevirt, filename, namesize);614 615 err = mrq_debugfs_read(bpmp, namephys, len, dataphys, datasize,616 &nbytes);617 618 if (!err)619 seq_write(m, datavirt, nbytes);620 621 dma_free_coherent(bpmp->dev, datasize, datavirt, dataphys);622free_namebuf:623 dma_free_coherent(bpmp->dev, namesize, namevirt, namephys);624 625 return err;626}627 628static int debugfs_open(struct inode *inode, struct file *file)629{630 return single_open_size(file, debugfs_show, file, SZ_128K);631}632 633static ssize_t debugfs_store(struct file *file, const char __user *buf,634 size_t count, loff_t *f_pos)635{636 struct inode *inode = file_inode(file);637 struct tegra_bpmp *bpmp = inode->i_private;638 const size_t datasize = count;639 const size_t namesize = SZ_256;640 void *datavirt, *namevirt;641 dma_addr_t dataphys, namephys;642 char fnamebuf[256];643 const char *filename;644 size_t len;645 int err;646 647 filename = get_filename(bpmp, file, fnamebuf, sizeof(fnamebuf));648 if (!filename)649 return -ENOENT;650 651 namevirt = dma_alloc_coherent(bpmp->dev, namesize, &namephys,652 GFP_KERNEL | GFP_DMA32);653 if (!namevirt)654 return -ENOMEM;655 656 datavirt = dma_alloc_coherent(bpmp->dev, datasize, &dataphys,657 GFP_KERNEL | GFP_DMA32);658 if (!datavirt) {659 err = -ENOMEM;660 goto free_namebuf;661 }662 663 len = strlen(filename);664 strscpy_pad(namevirt, filename, namesize);665 666 if (copy_from_user(datavirt, buf, count)) {667 err = -EFAULT;668 goto free_databuf;669 }670 671 err = mrq_debugfs_write(bpmp, namephys, len, dataphys,672 count);673 674free_databuf:675 dma_free_coherent(bpmp->dev, datasize, datavirt, dataphys);676free_namebuf:677 dma_free_coherent(bpmp->dev, namesize, namevirt, namephys);678 679 return err ?: count;680}681 682static const struct file_operations debugfs_fops = {683 .open = debugfs_open,684 .read = seq_read,685 .llseek = seq_lseek,686 .write = debugfs_store,687 .release = single_release,688};689 690static int bpmp_populate_dir(struct tegra_bpmp *bpmp, struct seqbuf *seqbuf,691 struct dentry *parent, u32 depth)692{693 int err;694 u32 d, t;695 const char *name;696 struct dentry *dentry;697 698 while (!seqbuf_eof(seqbuf)) {699 err = seqbuf_read_u32(seqbuf, &d);700 if (err < 0)701 return err;702 703 if (d < depth) {704 seqbuf_seek(seqbuf, -4);705 /* go up a level */706 return 0;707 } else if (d != depth) {708 /* malformed data received from BPMP */709 return -EIO;710 }711 712 err = seqbuf_read_u32(seqbuf, &t);713 if (err < 0)714 return err;715 err = seqbuf_read_str(seqbuf, &name);716 if (err < 0)717 return err;718 719 if (t & DEBUGFS_S_ISDIR) {720 dentry = debugfs_create_dir(name, parent);721 if (IS_ERR(dentry))722 return -ENOMEM;723 err = bpmp_populate_dir(bpmp, seqbuf, dentry, depth+1);724 if (err < 0)725 return err;726 } else {727 umode_t mode;728 729 mode = t & DEBUGFS_S_IRUSR ? S_IRUSR : 0;730 mode |= t & DEBUGFS_S_IWUSR ? S_IWUSR : 0;731 dentry = debugfs_create_file(name, mode,732 parent, bpmp,733 &debugfs_fops);734 if (IS_ERR(dentry))735 return -ENOMEM;736 }737 }738 739 return 0;740}741 742static int bpmp_populate_debugfs_shmem(struct tegra_bpmp *bpmp)743{744 struct seqbuf seqbuf;745 const size_t sz = SZ_512K;746 dma_addr_t phys;747 size_t nbytes;748 void *virt;749 int err;750 751 virt = dma_alloc_coherent(bpmp->dev, sz, &phys,752 GFP_KERNEL | GFP_DMA32);753 if (!virt)754 return -ENOMEM;755 756 err = mrq_debugfs_dumpdir(bpmp, phys, sz, &nbytes);757 if (err < 0) {758 goto free;759 } else if (nbytes > sz) {760 err = -EINVAL;761 goto free;762 }763 764 seqbuf_init(&seqbuf, virt, nbytes);765 err = bpmp_populate_dir(bpmp, &seqbuf, bpmp->debugfs_mirror, 0);766free:767 dma_free_coherent(bpmp->dev, sz, virt, phys);768 769 return err;770}771 772int tegra_bpmp_init_debugfs(struct tegra_bpmp *bpmp)773{774 struct dentry *root;775 bool inband;776 int err;777 778 inband = tegra_bpmp_mrq_is_supported(bpmp, MRQ_DEBUG);779 780 if (!inband && !tegra_bpmp_mrq_is_supported(bpmp, MRQ_DEBUGFS))781 return 0;782 783 root = debugfs_create_dir("bpmp", NULL);784 if (IS_ERR(root))785 return -ENOMEM;786 787 bpmp->debugfs_mirror = debugfs_create_dir("debug", root);788 if (IS_ERR(bpmp->debugfs_mirror)) {789 err = -ENOMEM;790 goto out;791 }792 793 if (inband)794 err = bpmp_populate_debugfs_inband(bpmp, bpmp->debugfs_mirror,795 "/");796 else797 err = bpmp_populate_debugfs_shmem(bpmp);798 799out:800 if (err < 0)801 debugfs_remove_recursive(root);802 803 return err;804}805