]> git.proxmox.com Git - mirror_ubuntu-focal-kernel.git/blame - drivers/firmware/tegra/bpmp-debugfs.c
treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 288
[mirror_ubuntu-focal-kernel.git] / drivers / firmware / tegra / bpmp-debugfs.c
CommitLineData
2025cf9e 1// SPDX-License-Identifier: GPL-2.0-only
f2381f65
TA
2/*
3 * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved.
f2381f65
TA
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
f2381f65
TA
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
43dc7485 382 if (!tegra_bpmp_mrq_is_supported(bpmp, MRQ_DEBUGFS))
f2381f65
TA
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}