linux/drivers/firmware/tegra/bpmp-debugfs.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2017, NVIDIA CORPORATION.  All rights reserved.
   4 */
   5#include <linux/debugfs.h>
   6#include <linux/dma-mapping.h>
   7#include <linux/uaccess.h>
   8
   9#include <soc/tegra/bpmp.h>
  10#include <soc/tegra/bpmp-abi.h>
  11
  12struct seqbuf {
  13        char *buf;
  14        size_t pos;
  15        size_t size;
  16};
  17
  18static void seqbuf_init(struct seqbuf *seqbuf, void *buf, size_t size)
  19{
  20        seqbuf->buf = buf;
  21        seqbuf->size = size;
  22        seqbuf->pos = 0;
  23}
  24
  25static size_t seqbuf_avail(struct seqbuf *seqbuf)
  26{
  27        return seqbuf->pos < seqbuf->size ? seqbuf->size - seqbuf->pos : 0;
  28}
  29
  30static size_t seqbuf_status(struct seqbuf *seqbuf)
  31{
  32        return seqbuf->pos <= seqbuf->size ? 0 : -EOVERFLOW;
  33}
  34
  35static int seqbuf_eof(struct seqbuf *seqbuf)
  36{
  37        return seqbuf->pos >= seqbuf->size;
  38}
  39
  40static int seqbuf_read(struct seqbuf *seqbuf, void *buf, size_t nbyte)
  41{
  42        nbyte = min(nbyte, seqbuf_avail(seqbuf));
  43        memcpy(buf, seqbuf->buf + seqbuf->pos, nbyte);
  44        seqbuf->pos += nbyte;
  45        return seqbuf_status(seqbuf);
  46}
  47
  48static int seqbuf_read_u32(struct seqbuf *seqbuf, uint32_t *v)
  49{
  50        int err;
  51
  52        err = seqbuf_read(seqbuf, v, 4);
  53        *v = le32_to_cpu(*v);
  54        return err;
  55}
  56
  57static int seqbuf_read_str(struct seqbuf *seqbuf, const char **str)
  58{
  59        *str = seqbuf->buf + seqbuf->pos;
  60        seqbuf->pos += strnlen(*str, seqbuf_avail(seqbuf));
  61        seqbuf->pos++;
  62        return seqbuf_status(seqbuf);
  63}
  64
  65static void seqbuf_seek(struct seqbuf *seqbuf, ssize_t offset)
  66{
  67        seqbuf->pos += offset;
  68}
  69
  70/* map filename in Linux debugfs to corresponding entry in BPMP */
  71static const char *get_filename(struct tegra_bpmp *bpmp,
  72                                const struct file *file, char *buf, int size)
  73{
  74        char root_path_buf[512];
  75        const char *root_path;
  76        const char *filename;
  77        size_t root_len;
  78
  79        root_path = dentry_path(bpmp->debugfs_mirror, root_path_buf,
  80                                sizeof(root_path_buf));
  81        if (IS_ERR(root_path))
  82                return NULL;
  83
  84        root_len = strlen(root_path);
  85
  86        filename = dentry_path(file->f_path.dentry, buf, size);
  87        if (IS_ERR(filename))
  88                return NULL;
  89
  90        if (strlen(filename) < root_len ||
  91                        strncmp(filename, root_path, root_len))
  92                return NULL;
  93
  94        filename += root_len;
  95
  96        return filename;
  97}
  98
  99static int mrq_debugfs_read(struct tegra_bpmp *bpmp,
 100                            dma_addr_t name, size_t sz_name,
 101                            dma_addr_t data, size_t sz_data,
 102                            size_t *nbytes)
 103{
 104        struct mrq_debugfs_request req = {
 105                .cmd = cpu_to_le32(CMD_DEBUGFS_READ),
 106                .fop = {
 107                        .fnameaddr = cpu_to_le32((uint32_t)name),
 108                        .fnamelen = cpu_to_le32((uint32_t)sz_name),
 109                        .dataaddr = cpu_to_le32((uint32_t)data),
 110                        .datalen = cpu_to_le32((uint32_t)sz_data),
 111                },
 112        };
 113        struct mrq_debugfs_response resp;
 114        struct tegra_bpmp_message msg = {
 115                .mrq = MRQ_DEBUGFS,
 116                .tx = {
 117                        .data = &req,
 118                        .size = sizeof(req),
 119                },
 120                .rx = {
 121                        .data = &resp,
 122                        .size = sizeof(resp),
 123                },
 124        };
 125        int err;
 126
 127        err = tegra_bpmp_transfer(bpmp, &msg);
 128        if (err < 0)
 129                return err;
 130
 131        *nbytes = (size_t)resp.fop.nbytes;
 132
 133        return 0;
 134}
 135
 136static int mrq_debugfs_write(struct tegra_bpmp *bpmp,
 137                             dma_addr_t name, size_t sz_name,
 138                             dma_addr_t data, size_t sz_data)
 139{
 140        const struct mrq_debugfs_request req = {
 141                .cmd = cpu_to_le32(CMD_DEBUGFS_WRITE),
 142                .fop = {
 143                        .fnameaddr = cpu_to_le32((uint32_t)name),
 144                        .fnamelen = cpu_to_le32((uint32_t)sz_name),
 145                        .dataaddr = cpu_to_le32((uint32_t)data),
 146                        .datalen = cpu_to_le32((uint32_t)sz_data),
 147                },
 148        };
 149        struct tegra_bpmp_message msg = {
 150                .mrq = MRQ_DEBUGFS,
 151                .tx = {
 152                        .data = &req,
 153                        .size = sizeof(req),
 154                },
 155        };
 156
 157        return tegra_bpmp_transfer(bpmp, &msg);
 158}
 159
 160static int mrq_debugfs_dumpdir(struct tegra_bpmp *bpmp, dma_addr_t addr,
 161                               size_t size, size_t *nbytes)
 162{
 163        const struct mrq_debugfs_request req = {
 164                .cmd = cpu_to_le32(CMD_DEBUGFS_DUMPDIR),
 165                .dumpdir = {
 166                        .dataaddr = cpu_to_le32((uint32_t)addr),
 167                        .datalen = cpu_to_le32((uint32_t)size),
 168                },
 169        };
 170        struct mrq_debugfs_response resp;
 171        struct tegra_bpmp_message msg = {
 172                .mrq = MRQ_DEBUGFS,
 173                .tx = {
 174                        .data = &req,
 175                        .size = sizeof(req),
 176                },
 177                .rx = {
 178                        .data = &resp,
 179                        .size = sizeof(resp),
 180                },
 181        };
 182        int err;
 183
 184        err = tegra_bpmp_transfer(bpmp, &msg);
 185        if (err < 0)
 186                return err;
 187
 188        *nbytes = (size_t)resp.dumpdir.nbytes;
 189
 190        return 0;
 191}
 192
 193static int debugfs_show(struct seq_file *m, void *p)
 194{
 195        struct file *file = m->private;
 196        struct inode *inode = file_inode(file);
 197        struct tegra_bpmp *bpmp = inode->i_private;
 198        const size_t datasize = m->size;
 199        const size_t namesize = SZ_256;
 200        void *datavirt, *namevirt;
 201        dma_addr_t dataphys, namephys;
 202        char buf[256];
 203        const char *filename;
 204        size_t len, nbytes;
 205        int ret;
 206
 207        filename = get_filename(bpmp, file, buf, sizeof(buf));
 208        if (!filename)
 209                return -ENOENT;
 210
 211        namevirt = dma_alloc_coherent(bpmp->dev, namesize, &namephys,
 212                                      GFP_KERNEL | GFP_DMA32);
 213        if (!namevirt)
 214                return -ENOMEM;
 215
 216        datavirt = dma_alloc_coherent(bpmp->dev, datasize, &dataphys,
 217                                      GFP_KERNEL | GFP_DMA32);
 218        if (!datavirt) {
 219                ret = -ENOMEM;
 220                goto free_namebuf;
 221        }
 222
 223        len = strlen(filename);
 224        strncpy(namevirt, filename, namesize);
 225
 226        ret = mrq_debugfs_read(bpmp, namephys, len, dataphys, datasize,
 227                               &nbytes);
 228
 229        if (!ret)
 230                seq_write(m, datavirt, nbytes);
 231
 232        dma_free_coherent(bpmp->dev, datasize, datavirt, dataphys);
 233free_namebuf:
 234        dma_free_coherent(bpmp->dev, namesize, namevirt, namephys);
 235
 236        return ret;
 237}
 238
 239static int debugfs_open(struct inode *inode, struct file *file)
 240{
 241        return single_open_size(file, debugfs_show, file, SZ_128K);
 242}
 243
 244static ssize_t debugfs_store(struct file *file, const char __user *buf,
 245                size_t count, loff_t *f_pos)
 246{
 247        struct inode *inode = file_inode(file);
 248        struct tegra_bpmp *bpmp = inode->i_private;
 249        const size_t datasize = count;
 250        const size_t namesize = SZ_256;
 251        void *datavirt, *namevirt;
 252        dma_addr_t dataphys, namephys;
 253        char fnamebuf[256];
 254        const char *filename;
 255        size_t len;
 256        int ret;
 257
 258        filename = get_filename(bpmp, file, fnamebuf, sizeof(fnamebuf));
 259        if (!filename)
 260                return -ENOENT;
 261
 262        namevirt = dma_alloc_coherent(bpmp->dev, namesize, &namephys,
 263                                      GFP_KERNEL | GFP_DMA32);
 264        if (!namevirt)
 265                return -ENOMEM;
 266
 267        datavirt = dma_alloc_coherent(bpmp->dev, datasize, &dataphys,
 268                                      GFP_KERNEL | GFP_DMA32);
 269        if (!datavirt) {
 270                ret = -ENOMEM;
 271                goto free_namebuf;
 272        }
 273
 274        len = strlen(filename);
 275        strncpy(namevirt, filename, namesize);
 276
 277        if (copy_from_user(datavirt, buf, count)) {
 278                ret = -EFAULT;
 279                goto free_databuf;
 280        }
 281
 282        ret = mrq_debugfs_write(bpmp, namephys, len, dataphys,
 283                                count);
 284
 285free_databuf:
 286        dma_free_coherent(bpmp->dev, datasize, datavirt, dataphys);
 287free_namebuf:
 288        dma_free_coherent(bpmp->dev, namesize, namevirt, namephys);
 289
 290        return ret ?: count;
 291}
 292
 293static const struct file_operations debugfs_fops = {
 294        .open           = debugfs_open,
 295        .read           = seq_read,
 296        .llseek         = seq_lseek,
 297        .write          = debugfs_store,
 298        .release        = single_release,
 299};
 300
 301static int bpmp_populate_dir(struct tegra_bpmp *bpmp, struct seqbuf *seqbuf,
 302                             struct dentry *parent, uint32_t depth)
 303{
 304        int err;
 305        uint32_t d, t;
 306        const char *name;
 307        struct dentry *dentry;
 308
 309        while (!seqbuf_eof(seqbuf)) {
 310                err = seqbuf_read_u32(seqbuf, &d);
 311                if (err < 0)
 312                        return err;
 313
 314                if (d < depth) {
 315                        seqbuf_seek(seqbuf, -4);
 316                        /* go up a level */
 317                        return 0;
 318                } else if (d != depth) {
 319                        /* malformed data received from BPMP */
 320                        return -EIO;
 321                }
 322
 323                err = seqbuf_read_u32(seqbuf, &t);
 324                if (err < 0)
 325                        return err;
 326                err = seqbuf_read_str(seqbuf, &name);
 327                if (err < 0)
 328                        return err;
 329
 330                if (t & DEBUGFS_S_ISDIR) {
 331                        dentry = debugfs_create_dir(name, parent);
 332                        if (!dentry)
 333                                return -ENOMEM;
 334                        err = bpmp_populate_dir(bpmp, seqbuf, dentry, depth+1);
 335                        if (err < 0)
 336                                return err;
 337                } else {
 338                        umode_t mode;
 339
 340                        mode = t & DEBUGFS_S_IRUSR ? S_IRUSR : 0;
 341                        mode |= t & DEBUGFS_S_IWUSR ? S_IWUSR : 0;
 342                        dentry = debugfs_create_file(name, mode,
 343                                                     parent, bpmp,
 344                                                     &debugfs_fops);
 345                        if (!dentry)
 346                                return -ENOMEM;
 347                }
 348        }
 349
 350        return 0;
 351}
 352
 353static int create_debugfs_mirror(struct tegra_bpmp *bpmp, void *buf,
 354                                 size_t bufsize, struct dentry *root)
 355{
 356        struct seqbuf seqbuf;
 357        int err;
 358
 359        bpmp->debugfs_mirror = debugfs_create_dir("debug", root);
 360        if (!bpmp->debugfs_mirror)
 361                return -ENOMEM;
 362
 363        seqbuf_init(&seqbuf, buf, bufsize);
 364        err = bpmp_populate_dir(bpmp, &seqbuf, bpmp->debugfs_mirror, 0);
 365        if (err < 0) {
 366                debugfs_remove_recursive(bpmp->debugfs_mirror);
 367                bpmp->debugfs_mirror = NULL;
 368        }
 369
 370        return err;
 371}
 372
 373int tegra_bpmp_init_debugfs(struct tegra_bpmp *bpmp)
 374{
 375        dma_addr_t phys;
 376        void *virt;
 377        const size_t sz = SZ_256K;
 378        size_t nbytes;
 379        int ret;
 380        struct dentry *root;
 381
 382        if (!tegra_bpmp_mrq_is_supported(bpmp, MRQ_DEBUGFS))
 383                return 0;
 384
 385        root = debugfs_create_dir("bpmp", NULL);
 386        if (!root)
 387                return -ENOMEM;
 388
 389        virt = dma_alloc_coherent(bpmp->dev, sz, &phys,
 390                                  GFP_KERNEL | GFP_DMA32);
 391        if (!virt) {
 392                ret = -ENOMEM;
 393                goto out;
 394        }
 395
 396        ret = mrq_debugfs_dumpdir(bpmp, phys, sz, &nbytes);
 397        if (ret < 0)
 398                goto free;
 399
 400        ret = create_debugfs_mirror(bpmp, virt, nbytes, root);
 401free:
 402        dma_free_coherent(bpmp->dev, sz, virt, phys);
 403out:
 404        if (ret < 0)
 405                debugfs_remove(root);
 406
 407        return ret;
 408}
 409