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