.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Remote Processor Framework |
---|
3 | 4 | * |
---|
.. | .. |
---|
11 | 12 | * Suman Anna <s-anna@ti.com> |
---|
12 | 13 | * Robert Tivy <rtivy@ti.com> |
---|
13 | 14 | * Armando Uribe De Leon <x0095078@ti.com> |
---|
14 | | - * |
---|
15 | | - * This program is free software; you can redistribute it and/or |
---|
16 | | - * modify it under the terms of the GNU General Public License |
---|
17 | | - * version 2 as published by the Free Software Foundation. |
---|
18 | | - * |
---|
19 | | - * This program is distributed in the hope that it will be useful, |
---|
20 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
21 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
22 | | - * GNU General Public License for more details. |
---|
23 | 15 | */ |
---|
24 | 16 | |
---|
25 | 17 | #define pr_fmt(fmt) "%s: " fmt, __func__ |
---|
.. | .. |
---|
36 | 28 | static struct dentry *rproc_dbg; |
---|
37 | 29 | |
---|
38 | 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 */ |
---|
| 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 | + */ |
---|
| 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 | +/* |
---|
39 | 118 | * Some remote processors may support dumping trace logs into a shared |
---|
40 | 119 | * memory buffer. We expose this trace buffer using debugfs, so users |
---|
41 | 120 | * can easily tell what's going on remotely. |
---|
.. | .. |
---|
47 | 126 | static ssize_t rproc_trace_read(struct file *filp, char __user *userbuf, |
---|
48 | 127 | size_t count, loff_t *ppos) |
---|
49 | 128 | { |
---|
50 | | - struct rproc_mem_entry *trace = filp->private_data; |
---|
51 | | - int len = strnlen(trace->va, trace->len); |
---|
| 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; |
---|
52 | 134 | |
---|
53 | | - return simple_read_from_buffer(userbuf, count, ppos, trace->va, len); |
---|
| 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); |
---|
54 | 146 | } |
---|
55 | 147 | |
---|
56 | 148 | static const struct file_operations trace_rproc_ops = { |
---|
.. | .. |
---|
133 | 225 | buf[count - 1] = '\0'; |
---|
134 | 226 | |
---|
135 | 227 | if (!strncmp(buf, "enabled", count)) { |
---|
| 228 | + /* change the flag and begin the recovery process if needed */ |
---|
136 | 229 | rproc->recovery_disabled = false; |
---|
137 | | - /* if rproc has crashed, trigger recovery */ |
---|
138 | | - if (rproc->state == RPROC_CRASHED) |
---|
139 | | - rproc_trigger_recovery(rproc); |
---|
| 230 | + rproc_trigger_recovery(rproc); |
---|
140 | 231 | } else if (!strncmp(buf, "disabled", count)) { |
---|
141 | 232 | rproc->recovery_disabled = true; |
---|
142 | 233 | } else if (!strncmp(buf, "recover", count)) { |
---|
143 | | - /* if rproc has crashed, trigger recovery */ |
---|
144 | | - if (rproc->state == RPROC_CRASHED) |
---|
145 | | - rproc_trigger_recovery(rproc); |
---|
| 234 | + /* begin the recovery process without changing the flag */ |
---|
| 235 | + rproc_trigger_recovery(rproc); |
---|
| 236 | + } else { |
---|
| 237 | + return -EINVAL; |
---|
146 | 238 | } |
---|
147 | 239 | |
---|
148 | 240 | return count; |
---|
.. | .. |
---|
151 | 243 | static const struct file_operations rproc_recovery_ops = { |
---|
152 | 244 | .read = rproc_recovery_read, |
---|
153 | 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 |
---|
| 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, |
---|
154 | 270 | .open = simple_open, |
---|
155 | 271 | .llseek = generic_file_llseek, |
---|
156 | 272 | }; |
---|
.. | .. |
---|
240 | 356 | return 0; |
---|
241 | 357 | } |
---|
242 | 358 | |
---|
243 | | -static int rproc_rsc_table_open(struct inode *inode, struct file *file) |
---|
244 | | -{ |
---|
245 | | - return single_open(file, rproc_rsc_table_show, inode->i_private); |
---|
246 | | -} |
---|
247 | | - |
---|
248 | | -static const struct file_operations rproc_rsc_table_ops = { |
---|
249 | | - .open = rproc_rsc_table_open, |
---|
250 | | - .read = seq_read, |
---|
251 | | - .llseek = seq_lseek, |
---|
252 | | - .release = single_release, |
---|
253 | | -}; |
---|
| 359 | +DEFINE_SHOW_ATTRIBUTE(rproc_rsc_table); |
---|
254 | 360 | |
---|
255 | 361 | /* Expose carveout content via debugfs */ |
---|
256 | 362 | static int rproc_carveouts_show(struct seq_file *seq, void *p) |
---|
.. | .. |
---|
260 | 366 | |
---|
261 | 367 | list_for_each_entry(carveout, &rproc->carveouts, node) { |
---|
262 | 368 | seq_puts(seq, "Carveout memory entry:\n"); |
---|
| 369 | + seq_printf(seq, "\tName: %s\n", carveout->name); |
---|
263 | 370 | seq_printf(seq, "\tVirtual address: %pK\n", carveout->va); |
---|
264 | 371 | seq_printf(seq, "\tDMA address: %pad\n", &carveout->dma); |
---|
265 | 372 | seq_printf(seq, "\tDevice address: 0x%x\n", carveout->da); |
---|
266 | | - seq_printf(seq, "\tLength: 0x%x Bytes\n\n", carveout->len); |
---|
| 373 | + seq_printf(seq, "\tLength: 0x%zx Bytes\n\n", carveout->len); |
---|
267 | 374 | } |
---|
268 | 375 | |
---|
269 | 376 | return 0; |
---|
270 | 377 | } |
---|
271 | 378 | |
---|
272 | | -static int rproc_carveouts_open(struct inode *inode, struct file *file) |
---|
273 | | -{ |
---|
274 | | - return single_open(file, rproc_carveouts_show, inode->i_private); |
---|
275 | | -} |
---|
276 | | - |
---|
277 | | -static const struct file_operations rproc_carveouts_ops = { |
---|
278 | | - .open = rproc_carveouts_open, |
---|
279 | | - .read = seq_read, |
---|
280 | | - .llseek = seq_lseek, |
---|
281 | | - .release = single_release, |
---|
282 | | -}; |
---|
| 379 | +DEFINE_SHOW_ATTRIBUTE(rproc_carveouts); |
---|
283 | 380 | |
---|
284 | 381 | void rproc_remove_trace_file(struct dentry *tfile) |
---|
285 | 382 | { |
---|
.. | .. |
---|
287 | 384 | } |
---|
288 | 385 | |
---|
289 | 386 | struct dentry *rproc_create_trace_file(const char *name, struct rproc *rproc, |
---|
290 | | - struct rproc_mem_entry *trace) |
---|
| 387 | + struct rproc_debug_trace *trace) |
---|
291 | 388 | { |
---|
292 | 389 | struct dentry *tfile; |
---|
293 | 390 | |
---|
.. | .. |
---|
303 | 400 | |
---|
304 | 401 | void rproc_delete_debug_dir(struct rproc *rproc) |
---|
305 | 402 | { |
---|
306 | | - if (!rproc->dbg_dir) |
---|
307 | | - return; |
---|
308 | | - |
---|
309 | 403 | debugfs_remove_recursive(rproc->dbg_dir); |
---|
310 | 404 | } |
---|
311 | 405 | |
---|
.. | .. |
---|
322 | 416 | |
---|
323 | 417 | debugfs_create_file("name", 0400, rproc->dbg_dir, |
---|
324 | 418 | rproc, &rproc_name_ops); |
---|
325 | | - debugfs_create_file("recovery", 0400, rproc->dbg_dir, |
---|
| 419 | + debugfs_create_file("recovery", 0600, rproc->dbg_dir, |
---|
326 | 420 | rproc, &rproc_recovery_ops); |
---|
| 421 | + debugfs_create_file("crash", 0200, rproc->dbg_dir, |
---|
| 422 | + rproc, &rproc_crash_ops); |
---|
327 | 423 | debugfs_create_file("resource_table", 0400, rproc->dbg_dir, |
---|
328 | | - rproc, &rproc_rsc_table_ops); |
---|
| 424 | + rproc, &rproc_rsc_table_fops); |
---|
329 | 425 | debugfs_create_file("carveout_memories", 0400, rproc->dbg_dir, |
---|
330 | | - rproc, &rproc_carveouts_ops); |
---|
| 426 | + rproc, &rproc_carveouts_fops); |
---|
| 427 | + debugfs_create_file("coredump", 0600, rproc->dbg_dir, |
---|
| 428 | + rproc, &rproc_coredump_fops); |
---|
331 | 429 | } |
---|
332 | 430 | |
---|
333 | 431 | void __init rproc_init_debugfs(void) |
---|