root/drivers/firmware/tegra/bpmp-debugfs.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. seqbuf_init
  2. seqbuf_avail
  3. seqbuf_status
  4. seqbuf_eof
  5. seqbuf_read
  6. seqbuf_read_u32
  7. seqbuf_read_str
  8. seqbuf_seek
  9. get_filename
  10. mrq_debugfs_read
  11. mrq_debugfs_write
  12. mrq_debugfs_dumpdir
  13. debugfs_show
  14. debugfs_open
  15. debugfs_store
  16. bpmp_populate_dir
  17. create_debugfs_mirror
  18. tegra_bpmp_init_debugfs

   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 
  12 struct seqbuf {
  13         char *buf;
  14         size_t pos;
  15         size_t size;
  16 };
  17 
  18 static 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 
  25 static size_t seqbuf_avail(struct seqbuf *seqbuf)
  26 {
  27         return seqbuf->pos < seqbuf->size ? seqbuf->size - seqbuf->pos : 0;
  28 }
  29 
  30 static size_t seqbuf_status(struct seqbuf *seqbuf)
  31 {
  32         return seqbuf->pos <= seqbuf->size ? 0 : -EOVERFLOW;
  33 }
  34 
  35 static int seqbuf_eof(struct seqbuf *seqbuf)
  36 {
  37         return seqbuf->pos >= seqbuf->size;
  38 }
  39 
  40 static 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 
  48 static 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 
  57 static 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 
  65 static 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 */
  71 static 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 
  99 static 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 
 136 static 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 
 160 static 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 
 193 static 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);
 233 free_namebuf:
 234         dma_free_coherent(bpmp->dev, namesize, namevirt, namephys);
 235 
 236         return ret;
 237 }
 238 
 239 static int debugfs_open(struct inode *inode, struct file *file)
 240 {
 241         return single_open_size(file, debugfs_show, file, SZ_128K);
 242 }
 243 
 244 static 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 
 285 free_databuf:
 286         dma_free_coherent(bpmp->dev, datasize, datavirt, dataphys);
 287 free_namebuf:
 288         dma_free_coherent(bpmp->dev, namesize, namevirt, namephys);
 289 
 290         return ret ?: count;
 291 }
 292 
 293 static 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 
 301 static 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 
 353 static 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 
 373 int 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);
 401 free:
 402         dma_free_coherent(bpmp->dev, sz, virt, phys);
 403 out:
 404         if (ret < 0)
 405                 debugfs_remove(root);
 406 
 407         return ret;
 408 }

/* [<][>][^][v][top][bottom][index][help] */