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