1 // SPDX-License-Identifier: GPL-2.0-only
3 * Coredump functionality for Remoteproc framework.
5 * Copyright (c) 2020, The Linux Foundation. All rights reserved.
8 #include <linux/completion.h>
9 #include <linux/devcoredump.h>
10 #include <linux/device.h>
11 #include <linux/kernel.h>
12 #include <linux/remoteproc.h>
13 #include "remoteproc_internal.h"
14 #include "remoteproc_elf_helpers.h"
16 struct rproc_coredump_state
{
19 struct completion dump_done
;
23 * rproc_coredump_cleanup() - clean up dump_segments list
24 * @rproc: the remote processor handle
26 void rproc_coredump_cleanup(struct rproc
*rproc
)
28 struct rproc_dump_segment
*entry
, *tmp
;
30 list_for_each_entry_safe(entry
, tmp
, &rproc
->dump_segments
, node
) {
31 list_del(&entry
->node
);
35 EXPORT_SYMBOL_GPL(rproc_coredump_cleanup
);
38 * rproc_coredump_add_segment() - add segment of device memory to coredump
39 * @rproc: handle of a remote processor
41 * @size: size of segment
43 * Add device memory to the list of segments to be included in a coredump for
46 * Return: 0 on success, negative errno on error.
48 int rproc_coredump_add_segment(struct rproc
*rproc
, dma_addr_t da
, size_t size
)
50 struct rproc_dump_segment
*segment
;
52 segment
= kzalloc(sizeof(*segment
), GFP_KERNEL
);
59 list_add_tail(&segment
->node
, &rproc
->dump_segments
);
63 EXPORT_SYMBOL(rproc_coredump_add_segment
);
66 * rproc_coredump_add_custom_segment() - add custom coredump segment
67 * @rproc: handle of a remote processor
69 * @size: size of segment
70 * @dumpfn: custom dump function called for each segment during coredump
73 * Add device memory to the list of segments to be included in the coredump
74 * and associate the segment with the given custom dump function and private
77 * Return: 0 on success, negative errno on error.
79 int rproc_coredump_add_custom_segment(struct rproc
*rproc
,
80 dma_addr_t da
, size_t size
,
81 void (*dumpfn
)(struct rproc
*rproc
,
82 struct rproc_dump_segment
*segment
,
83 void *dest
, size_t offset
,
87 struct rproc_dump_segment
*segment
;
89 segment
= kzalloc(sizeof(*segment
), GFP_KERNEL
);
96 segment
->dump
= dumpfn
;
98 list_add_tail(&segment
->node
, &rproc
->dump_segments
);
102 EXPORT_SYMBOL(rproc_coredump_add_custom_segment
);
105 * rproc_coredump_set_elf_info() - set coredump elf information
106 * @rproc: handle of a remote processor
107 * @class: elf class for coredump elf file
108 * @machine: elf machine for coredump elf file
110 * Set elf information which will be used for coredump elf file.
112 * Return: 0 on success, negative errno on error.
114 int rproc_coredump_set_elf_info(struct rproc
*rproc
, u8
class, u16 machine
)
116 if (class != ELFCLASS64
&& class != ELFCLASS32
)
119 rproc
->elf_class
= class;
120 rproc
->elf_machine
= machine
;
124 EXPORT_SYMBOL(rproc_coredump_set_elf_info
);
126 static void rproc_coredump_free(void *data
)
128 struct rproc_coredump_state
*dump_state
= data
;
130 vfree(dump_state
->header
);
131 complete(&dump_state
->dump_done
);
134 static void *rproc_coredump_find_segment(loff_t user_offset
,
135 struct list_head
*segments
,
138 struct rproc_dump_segment
*segment
;
140 list_for_each_entry(segment
, segments
, node
) {
141 if (user_offset
< segment
->size
) {
142 *data_left
= segment
->size
- user_offset
;
145 user_offset
-= segment
->size
;
152 static void rproc_copy_segment(struct rproc
*rproc
, void *dest
,
153 struct rproc_dump_segment
*segment
,
154 size_t offset
, size_t size
)
156 bool is_iomem
= false;
160 segment
->dump(rproc
, segment
, dest
, offset
, size
);
162 ptr
= rproc_da_to_va(rproc
, segment
->da
+ offset
, size
, &is_iomem
);
165 "invalid copy request for segment %pad with offset %zu and size %zu)\n",
166 &segment
->da
, offset
, size
);
167 memset(dest
, 0xff, size
);
170 memcpy_fromio(dest
, (void const __iomem
*)ptr
, size
);
172 memcpy(dest
, ptr
, size
);
177 static ssize_t
rproc_coredump_read(char *buffer
, loff_t offset
, size_t count
,
178 void *data
, size_t header_sz
)
180 size_t seg_data
, bytes_left
= count
;
182 struct rproc_dump_segment
*seg
;
183 struct rproc_coredump_state
*dump_state
= data
;
184 struct rproc
*rproc
= dump_state
->rproc
;
185 void *elfcore
= dump_state
->header
;
187 /* Copy the vmalloc'ed header first. */
188 if (offset
< header_sz
) {
189 copy_sz
= memory_read_from_buffer(buffer
, count
, &offset
,
196 * Find out the segment memory chunk to be copied based on offset.
197 * Keep copying data until count bytes are read.
200 seg
= rproc_coredump_find_segment(offset
- header_sz
,
201 &rproc
->dump_segments
,
205 dev_info(&rproc
->dev
, "Ramdump done, %lld bytes read",
210 copy_sz
= min_t(size_t, bytes_left
, seg_data
);
212 rproc_copy_segment(rproc
, buffer
, seg
, seg
->size
- seg_data
,
217 bytes_left
-= copy_sz
;
220 return count
- bytes_left
;
224 * rproc_coredump() - perform coredump
225 * @rproc: rproc handle
227 * This function will generate an ELF header for the registered segments
228 * and create a devcoredump device associated with rproc. Based on the
229 * coredump configuration this function will directly copy the segments
230 * from device memory to userspace or copy segments from device memory to
231 * a separate buffer, which can then be read by userspace.
232 * The first approach avoids using extra vmalloc memory. But it will stall
233 * recovery flow until dump is read by userspace.
235 void rproc_coredump(struct rproc
*rproc
)
237 struct rproc_dump_segment
*segment
;
243 u8
class = rproc
->elf_class
;
245 struct rproc_coredump_state dump_state
;
246 enum rproc_dump_mechanism dump_conf
= rproc
->dump_conf
;
248 if (list_empty(&rproc
->dump_segments
) ||
249 dump_conf
== RPROC_COREDUMP_DISABLED
)
252 if (class == ELFCLASSNONE
) {
253 dev_err(&rproc
->dev
, "ELF class is not set\n");
257 data_size
= elf_size_of_hdr(class);
258 list_for_each_entry(segment
, &rproc
->dump_segments
, node
) {
260 * For default configuration buffer includes headers & segments.
261 * For inline dump buffer just includes headers as segments are
262 * directly read from device memory.
264 data_size
+= elf_size_of_phdr(class);
265 if (dump_conf
== RPROC_COREDUMP_ENABLED
)
266 data_size
+= segment
->size
;
271 data
= vmalloc(data_size
);
277 memset(ehdr
, 0, elf_size_of_hdr(class));
278 /* e_ident field is common for both elf32 and elf64 */
279 elf_hdr_init_ident(ehdr
, class);
281 elf_hdr_set_e_type(class, ehdr
, ET_CORE
);
282 elf_hdr_set_e_machine(class, ehdr
, rproc
->elf_machine
);
283 elf_hdr_set_e_version(class, ehdr
, EV_CURRENT
);
284 elf_hdr_set_e_entry(class, ehdr
, rproc
->bootaddr
);
285 elf_hdr_set_e_phoff(class, ehdr
, elf_size_of_hdr(class));
286 elf_hdr_set_e_ehsize(class, ehdr
, elf_size_of_hdr(class));
287 elf_hdr_set_e_phentsize(class, ehdr
, elf_size_of_phdr(class));
288 elf_hdr_set_e_phnum(class, ehdr
, phnum
);
290 phdr
= data
+ elf_hdr_get_e_phoff(class, ehdr
);
291 offset
= elf_hdr_get_e_phoff(class, ehdr
);
292 offset
+= elf_size_of_phdr(class) * elf_hdr_get_e_phnum(class, ehdr
);
294 list_for_each_entry(segment
, &rproc
->dump_segments
, node
) {
295 memset(phdr
, 0, elf_size_of_phdr(class));
296 elf_phdr_set_p_type(class, phdr
, PT_LOAD
);
297 elf_phdr_set_p_offset(class, phdr
, offset
);
298 elf_phdr_set_p_vaddr(class, phdr
, segment
->da
);
299 elf_phdr_set_p_paddr(class, phdr
, segment
->da
);
300 elf_phdr_set_p_filesz(class, phdr
, segment
->size
);
301 elf_phdr_set_p_memsz(class, phdr
, segment
->size
);
302 elf_phdr_set_p_flags(class, phdr
, PF_R
| PF_W
| PF_X
);
303 elf_phdr_set_p_align(class, phdr
, 0);
305 if (dump_conf
== RPROC_COREDUMP_ENABLED
)
306 rproc_copy_segment(rproc
, data
+ offset
, segment
, 0,
309 offset
+= elf_phdr_get_p_filesz(class, phdr
);
310 phdr
+= elf_size_of_phdr(class);
312 if (dump_conf
== RPROC_COREDUMP_ENABLED
) {
313 dev_coredumpv(&rproc
->dev
, data
, data_size
, GFP_KERNEL
);
317 /* Initialize the dump state struct to be used by rproc_coredump_read */
318 dump_state
.rproc
= rproc
;
319 dump_state
.header
= data
;
320 init_completion(&dump_state
.dump_done
);
322 dev_coredumpm(&rproc
->dev
, NULL
, &dump_state
, data_size
, GFP_KERNEL
,
323 rproc_coredump_read
, rproc_coredump_free
);
326 * Wait until the dump is read and free is called. Data is freed
327 * by devcoredump framework automatically after 5 minutes.
329 wait_for_completion(&dump_state
.dump_done
);
331 EXPORT_SYMBOL_GPL(rproc_coredump
);
334 * rproc_coredump_using_sections() - perform coredump using section headers
335 * @rproc: rproc handle
337 * This function will generate an ELF header for the registered sections of
338 * segments and create a devcoredump device associated with rproc. Based on
339 * the coredump configuration this function will directly copy the segments
340 * from device memory to userspace or copy segments from device memory to
341 * a separate buffer, which can then be read by userspace.
342 * The first approach avoids using extra vmalloc memory. But it will stall
343 * recovery flow until dump is read by userspace.
345 void rproc_coredump_using_sections(struct rproc
*rproc
)
347 struct rproc_dump_segment
*segment
;
351 size_t strtbl_size
= 0;
352 size_t strtbl_index
= 1;
355 u8
class = rproc
->elf_class
;
357 struct rproc_coredump_state dump_state
;
358 unsigned int dump_conf
= rproc
->dump_conf
;
359 char *str_tbl
= "STR_TBL";
361 if (list_empty(&rproc
->dump_segments
) ||
362 dump_conf
== RPROC_COREDUMP_DISABLED
)
365 if (class == ELFCLASSNONE
) {
366 dev_err(&rproc
->dev
, "ELF class is not set\n");
371 * We allocate two extra section headers. The first one is null.
372 * Second section header is for the string table. Also space is
373 * allocated for string table.
375 data_size
= elf_size_of_hdr(class) + 2 * elf_size_of_shdr(class);
378 /* the extra byte is for the null character at index 0 */
379 strtbl_size
+= strlen(str_tbl
) + 2;
381 list_for_each_entry(segment
, &rproc
->dump_segments
, node
) {
382 data_size
+= elf_size_of_shdr(class);
383 strtbl_size
+= strlen(segment
->priv
) + 1;
384 if (dump_conf
== RPROC_COREDUMP_ENABLED
)
385 data_size
+= segment
->size
;
389 data_size
+= strtbl_size
;
391 data
= vmalloc(data_size
);
396 memset(ehdr
, 0, elf_size_of_hdr(class));
397 /* e_ident field is common for both elf32 and elf64 */
398 elf_hdr_init_ident(ehdr
, class);
400 elf_hdr_set_e_type(class, ehdr
, ET_CORE
);
401 elf_hdr_set_e_machine(class, ehdr
, rproc
->elf_machine
);
402 elf_hdr_set_e_version(class, ehdr
, EV_CURRENT
);
403 elf_hdr_set_e_entry(class, ehdr
, rproc
->bootaddr
);
404 elf_hdr_set_e_shoff(class, ehdr
, elf_size_of_hdr(class));
405 elf_hdr_set_e_ehsize(class, ehdr
, elf_size_of_hdr(class));
406 elf_hdr_set_e_shentsize(class, ehdr
, elf_size_of_shdr(class));
407 elf_hdr_set_e_shnum(class, ehdr
, shnum
);
408 elf_hdr_set_e_shstrndx(class, ehdr
, 1);
411 * The zeroth index of the section header is reserved and is rarely used.
412 * Set the section header as null (SHN_UNDEF) and move to the next one.
414 shdr
= data
+ elf_hdr_get_e_shoff(class, ehdr
);
415 memset(shdr
, 0, elf_size_of_shdr(class));
416 shdr
+= elf_size_of_shdr(class);
418 /* Initialize the string table. */
419 offset
= elf_hdr_get_e_shoff(class, ehdr
) +
420 elf_size_of_shdr(class) * elf_hdr_get_e_shnum(class, ehdr
);
421 memset(data
+ offset
, 0, strtbl_size
);
423 /* Fill in the string table section header. */
424 memset(shdr
, 0, elf_size_of_shdr(class));
425 elf_shdr_set_sh_type(class, shdr
, SHT_STRTAB
);
426 elf_shdr_set_sh_offset(class, shdr
, offset
);
427 elf_shdr_set_sh_size(class, shdr
, strtbl_size
);
428 elf_shdr_set_sh_entsize(class, shdr
, 0);
429 elf_shdr_set_sh_flags(class, shdr
, 0);
430 elf_shdr_set_sh_name(class, shdr
, elf_strtbl_add(str_tbl
, ehdr
, class, &strtbl_index
));
431 offset
+= elf_shdr_get_sh_size(class, shdr
);
432 shdr
+= elf_size_of_shdr(class);
434 list_for_each_entry(segment
, &rproc
->dump_segments
, node
) {
435 memset(shdr
, 0, elf_size_of_shdr(class));
436 elf_shdr_set_sh_type(class, shdr
, SHT_PROGBITS
);
437 elf_shdr_set_sh_offset(class, shdr
, offset
);
438 elf_shdr_set_sh_addr(class, shdr
, segment
->da
);
439 elf_shdr_set_sh_size(class, shdr
, segment
->size
);
440 elf_shdr_set_sh_entsize(class, shdr
, 0);
441 elf_shdr_set_sh_flags(class, shdr
, SHF_WRITE
);
442 elf_shdr_set_sh_name(class, shdr
,
443 elf_strtbl_add(segment
->priv
, ehdr
, class, &strtbl_index
));
445 /* No need to copy segments for inline dumps */
446 if (dump_conf
== RPROC_COREDUMP_ENABLED
)
447 rproc_copy_segment(rproc
, data
+ offset
, segment
, 0,
449 offset
+= elf_shdr_get_sh_size(class, shdr
);
450 shdr
+= elf_size_of_shdr(class);
453 if (dump_conf
== RPROC_COREDUMP_ENABLED
) {
454 dev_coredumpv(&rproc
->dev
, data
, data_size
, GFP_KERNEL
);
458 /* Initialize the dump state struct to be used by rproc_coredump_read */
459 dump_state
.rproc
= rproc
;
460 dump_state
.header
= data
;
461 init_completion(&dump_state
.dump_done
);
463 dev_coredumpm(&rproc
->dev
, NULL
, &dump_state
, data_size
, GFP_KERNEL
,
464 rproc_coredump_read
, rproc_coredump_free
);
466 /* Wait until the dump is read and free is called. Data is freed
467 * by devcoredump framework automatically after 5 minutes.
469 wait_for_completion(&dump_state
.dump_done
);
471 EXPORT_SYMBOL(rproc_coredump_using_sections
);