xref: /linux/drivers/remoteproc/remoteproc_debugfs.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1  // SPDX-License-Identifier: GPL-2.0-only
2  /*
3   * Remote Processor Framework
4   *
5   * Copyright (C) 2011 Texas Instruments, Inc.
6   * Copyright (C) 2011 Google, Inc.
7   *
8   * Ohad Ben-Cohen <ohad@wizery.com>
9   * Mark Grosen <mgrosen@ti.com>
10   * Brian Swetland <swetland@google.com>
11   * Fernando Guzman Lugo <fernando.lugo@ti.com>
12   * Suman Anna <s-anna@ti.com>
13   * Robert Tivy <rtivy@ti.com>
14   * Armando Uribe De Leon <x0095078@ti.com>
15   */
16  
17  #define pr_fmt(fmt)    "%s: " fmt, __func__
18  
19  #include <linux/kernel.h>
20  #include <linux/debugfs.h>
21  #include <linux/remoteproc.h>
22  #include <linux/device.h>
23  #include <linux/uaccess.h>
24  
25  #include "remoteproc_internal.h"
26  
27  /* remoteproc debugfs parent dir */
28  static struct dentry *rproc_dbg;
29  
30  /*
31   * A coredump-configuration-to-string lookup table, for exposing a
32   * human readable configuration via debugfs. Always keep in sync with
33   * enum rproc_coredump_mechanism
34   */
35  static const char * const rproc_coredump_str[] = {
36  	[RPROC_COREDUMP_DISABLED]	= "disabled",
37  	[RPROC_COREDUMP_ENABLED]	= "enabled",
38  	[RPROC_COREDUMP_INLINE]		= "inline",
39  };
40  
41  /* Expose the current coredump configuration via debugfs */
rproc_coredump_read(struct file * filp,char __user * userbuf,size_t count,loff_t * ppos)42  static ssize_t rproc_coredump_read(struct file *filp, char __user *userbuf,
43  				   size_t count, loff_t *ppos)
44  {
45  	struct rproc *rproc = filp->private_data;
46  	char buf[20];
47  	int len;
48  
49  	len = scnprintf(buf, sizeof(buf), "%s\n",
50  			rproc_coredump_str[rproc->dump_conf]);
51  
52  	return simple_read_from_buffer(userbuf, count, ppos, buf, len);
53  }
54  
55  /*
56   * By writing to the 'coredump' debugfs entry, we control the behavior of the
57   * coredump mechanism dynamically. The default value of this entry is "disabled".
58   *
59   * The 'coredump' debugfs entry supports these commands:
60   *
61   * disabled:	By default coredump collection is disabled. Recovery will
62   *		proceed without collecting any dump.
63   *
64   * enabled:	When the remoteproc crashes the entire coredump will be copied
65   *		to a separate buffer and exposed to userspace.
66   *
67   * inline:	The coredump will not be copied to a separate buffer and the
68   *		recovery process will have to wait until data is read by
69   *		userspace. But this avoid usage of extra memory.
70   */
rproc_coredump_write(struct file * filp,const char __user * user_buf,size_t count,loff_t * ppos)71  static ssize_t rproc_coredump_write(struct file *filp,
72  				    const char __user *user_buf, size_t count,
73  				    loff_t *ppos)
74  {
75  	struct rproc *rproc = filp->private_data;
76  	int ret, err = 0;
77  	char buf[20];
78  
79  	if (count < 1 || count > sizeof(buf))
80  		return -EINVAL;
81  
82  	ret = copy_from_user(buf, user_buf, count);
83  	if (ret)
84  		return -EFAULT;
85  
86  	/* remove end of line */
87  	if (buf[count - 1] == '\n')
88  		buf[count - 1] = '\0';
89  
90  	if (rproc->state == RPROC_CRASHED) {
91  		dev_err(&rproc->dev, "can't change coredump configuration\n");
92  		err = -EBUSY;
93  		goto out;
94  	}
95  
96  	if (!strncmp(buf, "disabled", count)) {
97  		rproc->dump_conf = RPROC_COREDUMP_DISABLED;
98  	} else if (!strncmp(buf, "enabled", count)) {
99  		rproc->dump_conf = RPROC_COREDUMP_ENABLED;
100  	} else if (!strncmp(buf, "inline", count)) {
101  		rproc->dump_conf = RPROC_COREDUMP_INLINE;
102  	} else {
103  		dev_err(&rproc->dev, "Invalid coredump configuration\n");
104  		err = -EINVAL;
105  	}
106  out:
107  	return err ? err : count;
108  }
109  
110  static const struct file_operations rproc_coredump_fops = {
111  	.read = rproc_coredump_read,
112  	.write = rproc_coredump_write,
113  	.open = simple_open,
114  	.llseek = generic_file_llseek,
115  };
116  
117  /*
118   * Some remote processors may support dumping trace logs into a shared
119   * memory buffer. We expose this trace buffer using debugfs, so users
120   * can easily tell what's going on remotely.
121   *
122   * We will most probably improve the rproc tracing facilities later on,
123   * but this kind of lightweight and simple mechanism is always good to have,
124   * as it provides very early tracing with little to no dependencies at all.
125   */
rproc_trace_read(struct file * filp,char __user * userbuf,size_t count,loff_t * ppos)126  static ssize_t rproc_trace_read(struct file *filp, char __user *userbuf,
127  				size_t count, loff_t *ppos)
128  {
129  	struct rproc_debug_trace *data = filp->private_data;
130  	struct rproc_mem_entry *trace = &data->trace_mem;
131  	void *va;
132  	char buf[100];
133  	int len;
134  
135  	va = rproc_da_to_va(data->rproc, trace->da, trace->len, NULL);
136  
137  	if (!va) {
138  		len = scnprintf(buf, sizeof(buf), "Trace %s not available\n",
139  				trace->name);
140  		va = buf;
141  	} else {
142  		len = strnlen(va, trace->len);
143  	}
144  
145  	return simple_read_from_buffer(userbuf, count, ppos, va, len);
146  }
147  
148  static const struct file_operations trace_rproc_ops = {
149  	.read = rproc_trace_read,
150  	.open = simple_open,
151  	.llseek	= generic_file_llseek,
152  };
153  
154  /* expose the name of the remote processor via debugfs */
rproc_name_read(struct file * filp,char __user * userbuf,size_t count,loff_t * ppos)155  static ssize_t rproc_name_read(struct file *filp, char __user *userbuf,
156  			       size_t count, loff_t *ppos)
157  {
158  	struct rproc *rproc = filp->private_data;
159  	/* need room for the name, a newline and a terminating null */
160  	char buf[100];
161  	int i;
162  
163  	i = scnprintf(buf, sizeof(buf), "%.98s\n", rproc->name);
164  
165  	return simple_read_from_buffer(userbuf, count, ppos, buf, i);
166  }
167  
168  static const struct file_operations rproc_name_ops = {
169  	.read = rproc_name_read,
170  	.open = simple_open,
171  	.llseek	= generic_file_llseek,
172  };
173  
174  /* expose recovery flag via debugfs */
rproc_recovery_read(struct file * filp,char __user * userbuf,size_t count,loff_t * ppos)175  static ssize_t rproc_recovery_read(struct file *filp, char __user *userbuf,
176  				   size_t count, loff_t *ppos)
177  {
178  	struct rproc *rproc = filp->private_data;
179  	char *buf = rproc->recovery_disabled ? "disabled\n" : "enabled\n";
180  
181  	return simple_read_from_buffer(userbuf, count, ppos, buf, strlen(buf));
182  }
183  
184  /*
185   * By writing to the 'recovery' debugfs entry, we control the behavior of the
186   * recovery mechanism dynamically. The default value of this entry is "enabled".
187   *
188   * The 'recovery' debugfs entry supports these commands:
189   *
190   * enabled:	When enabled, the remote processor will be automatically
191   *		recovered whenever it crashes. Moreover, if the remote
192   *		processor crashes while recovery is disabled, it will
193   *		be automatically recovered too as soon as recovery is enabled.
194   *
195   * disabled:	When disabled, a remote processor will remain in a crashed
196   *		state if it crashes. This is useful for debugging purposes;
197   *		without it, debugging a crash is substantially harder.
198   *
199   * recover:	This function will trigger an immediate recovery if the
200   *		remote processor is in a crashed state, without changing
201   *		or checking the recovery state (enabled/disabled).
202   *		This is useful during debugging sessions, when one expects
203   *		additional crashes to happen after enabling recovery. In this
204   *		case, enabling recovery will make it hard to debug subsequent
205   *		crashes, so it's recommended to keep recovery disabled, and
206   *		instead use the "recover" command as needed.
207   */
208  static ssize_t
rproc_recovery_write(struct file * filp,const char __user * user_buf,size_t count,loff_t * ppos)209  rproc_recovery_write(struct file *filp, const char __user *user_buf,
210  		     size_t count, loff_t *ppos)
211  {
212  	struct rproc *rproc = filp->private_data;
213  	char buf[10];
214  	int ret;
215  
216  	if (count < 1 || count > sizeof(buf))
217  		return -EINVAL;
218  
219  	ret = copy_from_user(buf, user_buf, count);
220  	if (ret)
221  		return -EFAULT;
222  
223  	/* remove end of line */
224  	if (buf[count - 1] == '\n')
225  		buf[count - 1] = '\0';
226  
227  	if (!strncmp(buf, "enabled", count)) {
228  		/* change the flag and begin the recovery process if needed */
229  		rproc->recovery_disabled = false;
230  		rproc_trigger_recovery(rproc);
231  	} else if (!strncmp(buf, "disabled", count)) {
232  		rproc->recovery_disabled = true;
233  	} else if (!strncmp(buf, "recover", count)) {
234  		/* begin the recovery process without changing the flag */
235  		rproc_trigger_recovery(rproc);
236  	} else {
237  		return -EINVAL;
238  	}
239  
240  	return count;
241  }
242  
243  static const struct file_operations rproc_recovery_ops = {
244  	.read = rproc_recovery_read,
245  	.write = rproc_recovery_write,
246  	.open = simple_open,
247  	.llseek = generic_file_llseek,
248  };
249  
250  /* expose the crash trigger via debugfs */
251  static ssize_t
rproc_crash_write(struct file * filp,const char __user * user_buf,size_t count,loff_t * ppos)252  rproc_crash_write(struct file *filp, const char __user *user_buf,
253  		  size_t count, loff_t *ppos)
254  {
255  	struct rproc *rproc = filp->private_data;
256  	unsigned int type;
257  	int ret;
258  
259  	ret = kstrtouint_from_user(user_buf, count, 0, &type);
260  	if (ret < 0)
261  		return ret;
262  
263  	rproc_report_crash(rproc, type);
264  
265  	return count;
266  }
267  
268  static const struct file_operations rproc_crash_ops = {
269  	.write = rproc_crash_write,
270  	.open = simple_open,
271  	.llseek = generic_file_llseek,
272  };
273  
274  /* Expose resource table content via debugfs */
rproc_rsc_table_show(struct seq_file * seq,void * p)275  static int rproc_rsc_table_show(struct seq_file *seq, void *p)
276  {
277  	static const char * const types[] = {"carveout", "devmem", "trace", "vdev"};
278  	struct rproc *rproc = seq->private;
279  	struct resource_table *table = rproc->table_ptr;
280  	struct fw_rsc_carveout *c;
281  	struct fw_rsc_devmem *d;
282  	struct fw_rsc_trace *t;
283  	struct fw_rsc_vdev *v;
284  	int i, j;
285  
286  	if (!table) {
287  		seq_puts(seq, "No resource table found\n");
288  		return 0;
289  	}
290  
291  	for (i = 0; i < table->num; i++) {
292  		int offset = table->offset[i];
293  		struct fw_rsc_hdr *hdr = (void *)table + offset;
294  		void *rsc = (void *)hdr + sizeof(*hdr);
295  
296  		switch (hdr->type) {
297  		case RSC_CARVEOUT:
298  			c = rsc;
299  			seq_printf(seq, "Entry %d is of type %s\n", i, types[hdr->type]);
300  			seq_printf(seq, "  Device Address 0x%x\n", c->da);
301  			seq_printf(seq, "  Physical Address 0x%x\n", c->pa);
302  			seq_printf(seq, "  Length 0x%x Bytes\n", c->len);
303  			seq_printf(seq, "  Flags 0x%x\n", c->flags);
304  			seq_printf(seq, "  Reserved (should be zero) [%d]\n", c->reserved);
305  			seq_printf(seq, "  Name %s\n\n", c->name);
306  			break;
307  		case RSC_DEVMEM:
308  			d = rsc;
309  			seq_printf(seq, "Entry %d is of type %s\n", i, types[hdr->type]);
310  			seq_printf(seq, "  Device Address 0x%x\n", d->da);
311  			seq_printf(seq, "  Physical Address 0x%x\n", d->pa);
312  			seq_printf(seq, "  Length 0x%x Bytes\n", d->len);
313  			seq_printf(seq, "  Flags 0x%x\n", d->flags);
314  			seq_printf(seq, "  Reserved (should be zero) [%d]\n", d->reserved);
315  			seq_printf(seq, "  Name %s\n\n", d->name);
316  			break;
317  		case RSC_TRACE:
318  			t = rsc;
319  			seq_printf(seq, "Entry %d is of type %s\n", i, types[hdr->type]);
320  			seq_printf(seq, "  Device Address 0x%x\n", t->da);
321  			seq_printf(seq, "  Length 0x%x Bytes\n", t->len);
322  			seq_printf(seq, "  Reserved (should be zero) [%d]\n", t->reserved);
323  			seq_printf(seq, "  Name %s\n\n", t->name);
324  			break;
325  		case RSC_VDEV:
326  			v = rsc;
327  			seq_printf(seq, "Entry %d is of type %s\n", i, types[hdr->type]);
328  
329  			seq_printf(seq, "  ID %d\n", v->id);
330  			seq_printf(seq, "  Notify ID %d\n", v->notifyid);
331  			seq_printf(seq, "  Device features 0x%x\n", v->dfeatures);
332  			seq_printf(seq, "  Guest features 0x%x\n", v->gfeatures);
333  			seq_printf(seq, "  Config length 0x%x\n", v->config_len);
334  			seq_printf(seq, "  Status 0x%x\n", v->status);
335  			seq_printf(seq, "  Number of vrings %d\n", v->num_of_vrings);
336  			seq_printf(seq, "  Reserved (should be zero) [%d][%d]\n\n",
337  				   v->reserved[0], v->reserved[1]);
338  
339  			for (j = 0; j < v->num_of_vrings; j++) {
340  				seq_printf(seq, "  Vring %d\n", j);
341  				seq_printf(seq, "    Device Address 0x%x\n", v->vring[j].da);
342  				seq_printf(seq, "    Alignment %d\n", v->vring[j].align);
343  				seq_printf(seq, "    Number of buffers %d\n", v->vring[j].num);
344  				seq_printf(seq, "    Notify ID %d\n", v->vring[j].notifyid);
345  				seq_printf(seq, "    Physical Address 0x%x\n\n",
346  					   v->vring[j].pa);
347  			}
348  			break;
349  		default:
350  			seq_printf(seq, "Unknown resource type found: %d [hdr: %pK]\n",
351  				   hdr->type, hdr);
352  			break;
353  		}
354  	}
355  
356  	return 0;
357  }
358  
359  DEFINE_SHOW_ATTRIBUTE(rproc_rsc_table);
360  
361  /* Expose carveout content via debugfs */
rproc_carveouts_show(struct seq_file * seq,void * p)362  static int rproc_carveouts_show(struct seq_file *seq, void *p)
363  {
364  	struct rproc *rproc = seq->private;
365  	struct rproc_mem_entry *carveout;
366  
367  	list_for_each_entry(carveout, &rproc->carveouts, node) {
368  		seq_puts(seq, "Carveout memory entry:\n");
369  		seq_printf(seq, "\tName: %s\n", carveout->name);
370  		seq_printf(seq, "\tVirtual address: %pK\n", carveout->va);
371  		seq_printf(seq, "\tDMA address: %pad\n", &carveout->dma);
372  		seq_printf(seq, "\tDevice address: 0x%x\n", carveout->da);
373  		seq_printf(seq, "\tLength: 0x%zx Bytes\n\n", carveout->len);
374  	}
375  
376  	return 0;
377  }
378  
379  DEFINE_SHOW_ATTRIBUTE(rproc_carveouts);
380  
rproc_remove_trace_file(struct dentry * tfile)381  void rproc_remove_trace_file(struct dentry *tfile)
382  {
383  	debugfs_remove(tfile);
384  }
385  
rproc_create_trace_file(const char * name,struct rproc * rproc,struct rproc_debug_trace * trace)386  struct dentry *rproc_create_trace_file(const char *name, struct rproc *rproc,
387  				       struct rproc_debug_trace *trace)
388  {
389  	return debugfs_create_file(name, 0400, rproc->dbg_dir, trace,
390  				    &trace_rproc_ops);
391  }
392  
rproc_delete_debug_dir(struct rproc * rproc)393  void rproc_delete_debug_dir(struct rproc *rproc)
394  {
395  	debugfs_remove_recursive(rproc->dbg_dir);
396  }
397  
rproc_create_debug_dir(struct rproc * rproc)398  void rproc_create_debug_dir(struct rproc *rproc)
399  {
400  	struct device *dev = &rproc->dev;
401  
402  	if (!rproc_dbg)
403  		return;
404  
405  	rproc->dbg_dir = debugfs_create_dir(dev_name(dev), rproc_dbg);
406  
407  	debugfs_create_file("name", 0400, rproc->dbg_dir,
408  			    rproc, &rproc_name_ops);
409  	debugfs_create_file("recovery", 0600, rproc->dbg_dir,
410  			    rproc, &rproc_recovery_ops);
411  	debugfs_create_file("crash", 0200, rproc->dbg_dir,
412  			    rproc, &rproc_crash_ops);
413  	debugfs_create_file("resource_table", 0400, rproc->dbg_dir,
414  			    rproc, &rproc_rsc_table_fops);
415  	debugfs_create_file("carveout_memories", 0400, rproc->dbg_dir,
416  			    rproc, &rproc_carveouts_fops);
417  	debugfs_create_file("coredump", 0600, rproc->dbg_dir,
418  			    rproc, &rproc_coredump_fops);
419  }
420  
rproc_init_debugfs(void)421  void __init rproc_init_debugfs(void)
422  {
423  	if (debugfs_initialized())
424  		rproc_dbg = debugfs_create_dir(KBUILD_MODNAME, NULL);
425  }
426  
rproc_exit_debugfs(void)427  void __exit rproc_exit_debugfs(void)
428  {
429  	debugfs_remove(rproc_dbg);
430  }
431