]>
Commit | Line | Data |
---|---|---|
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 | ||
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 | ||
f2381f65 TA |
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 | ||
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); | |
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 | } |