Rishabh Bhatnagar | 2c010cc | 2020-07-16 15:20:31 -0700 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0-only |
| 2 | /* |
| 3 | * Coredump functionality for Remoteproc framework. |
| 4 | * |
| 5 | * Copyright (c) 2020, The Linux Foundation. All rights reserved. |
| 6 | */ |
| 7 | |
Rishabh Bhatnagar | c973198 | 2020-07-16 15:20:34 -0700 | [diff] [blame] | 8 | #include <linux/completion.h> |
Rishabh Bhatnagar | 2c010cc | 2020-07-16 15:20:31 -0700 | [diff] [blame] | 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" |
| 15 | |
Rishabh Bhatnagar | c973198 | 2020-07-16 15:20:34 -0700 | [diff] [blame] | 16 | struct rproc_coredump_state { |
| 17 | struct rproc *rproc; |
| 18 | void *header; |
| 19 | struct completion dump_done; |
| 20 | }; |
| 21 | |
Rishabh Bhatnagar | 2c010cc | 2020-07-16 15:20:31 -0700 | [diff] [blame] | 22 | /** |
| 23 | * rproc_coredump_cleanup() - clean up dump_segments list |
| 24 | * @rproc: the remote processor handle |
| 25 | */ |
| 26 | void rproc_coredump_cleanup(struct rproc *rproc) |
| 27 | { |
| 28 | struct rproc_dump_segment *entry, *tmp; |
| 29 | |
| 30 | list_for_each_entry_safe(entry, tmp, &rproc->dump_segments, node) { |
| 31 | list_del(&entry->node); |
| 32 | kfree(entry); |
| 33 | } |
| 34 | } |
Siddharth Gupta | f247f08 | 2023-02-24 13:17:06 -0800 | [diff] [blame] | 35 | EXPORT_SYMBOL_GPL(rproc_coredump_cleanup); |
Rishabh Bhatnagar | 2c010cc | 2020-07-16 15:20:31 -0700 | [diff] [blame] | 36 | |
| 37 | /** |
| 38 | * rproc_coredump_add_segment() - add segment of device memory to coredump |
| 39 | * @rproc: handle of a remote processor |
| 40 | * @da: device address |
| 41 | * @size: size of segment |
| 42 | * |
| 43 | * Add device memory to the list of segments to be included in a coredump for |
| 44 | * the remoteproc. |
| 45 | * |
| 46 | * Return: 0 on success, negative errno on error. |
| 47 | */ |
| 48 | int rproc_coredump_add_segment(struct rproc *rproc, dma_addr_t da, size_t size) |
| 49 | { |
| 50 | struct rproc_dump_segment *segment; |
| 51 | |
| 52 | segment = kzalloc(sizeof(*segment), GFP_KERNEL); |
| 53 | if (!segment) |
| 54 | return -ENOMEM; |
| 55 | |
| 56 | segment->da = da; |
| 57 | segment->size = size; |
| 58 | |
| 59 | list_add_tail(&segment->node, &rproc->dump_segments); |
| 60 | |
| 61 | return 0; |
| 62 | } |
| 63 | EXPORT_SYMBOL(rproc_coredump_add_segment); |
| 64 | |
| 65 | /** |
| 66 | * rproc_coredump_add_custom_segment() - add custom coredump segment |
| 67 | * @rproc: handle of a remote processor |
| 68 | * @da: device address |
| 69 | * @size: size of segment |
| 70 | * @dumpfn: custom dump function called for each segment during coredump |
| 71 | * @priv: private data |
| 72 | * |
| 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 |
| 75 | * data. |
| 76 | * |
| 77 | * Return: 0 on success, negative errno on error. |
| 78 | */ |
| 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, |
Rishabh Bhatnagar | 76abf9c | 2020-07-16 15:20:33 -0700 | [diff] [blame] | 83 | void *dest, size_t offset, |
| 84 | size_t size), |
Rishabh Bhatnagar | 2c010cc | 2020-07-16 15:20:31 -0700 | [diff] [blame] | 85 | void *priv) |
| 86 | { |
| 87 | struct rproc_dump_segment *segment; |
| 88 | |
| 89 | segment = kzalloc(sizeof(*segment), GFP_KERNEL); |
| 90 | if (!segment) |
| 91 | return -ENOMEM; |
| 92 | |
| 93 | segment->da = da; |
| 94 | segment->size = size; |
| 95 | segment->priv = priv; |
| 96 | segment->dump = dumpfn; |
| 97 | |
| 98 | list_add_tail(&segment->node, &rproc->dump_segments); |
| 99 | |
| 100 | return 0; |
| 101 | } |
| 102 | EXPORT_SYMBOL(rproc_coredump_add_custom_segment); |
| 103 | |
| 104 | /** |
| 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 |
| 109 | * |
| 110 | * Set elf information which will be used for coredump elf file. |
| 111 | * |
| 112 | * Return: 0 on success, negative errno on error. |
| 113 | */ |
| 114 | int rproc_coredump_set_elf_info(struct rproc *rproc, u8 class, u16 machine) |
| 115 | { |
| 116 | if (class != ELFCLASS64 && class != ELFCLASS32) |
| 117 | return -EINVAL; |
| 118 | |
| 119 | rproc->elf_class = class; |
| 120 | rproc->elf_machine = machine; |
| 121 | |
| 122 | return 0; |
| 123 | } |
| 124 | EXPORT_SYMBOL(rproc_coredump_set_elf_info); |
| 125 | |
Rishabh Bhatnagar | c973198 | 2020-07-16 15:20:34 -0700 | [diff] [blame] | 126 | static void rproc_coredump_free(void *data) |
| 127 | { |
| 128 | struct rproc_coredump_state *dump_state = data; |
| 129 | |
| 130 | vfree(dump_state->header); |
| 131 | complete(&dump_state->dump_done); |
| 132 | } |
| 133 | |
| 134 | static void *rproc_coredump_find_segment(loff_t user_offset, |
| 135 | struct list_head *segments, |
| 136 | size_t *data_left) |
| 137 | { |
| 138 | struct rproc_dump_segment *segment; |
| 139 | |
| 140 | list_for_each_entry(segment, segments, node) { |
| 141 | if (user_offset < segment->size) { |
| 142 | *data_left = segment->size - user_offset; |
| 143 | return segment; |
| 144 | } |
| 145 | user_offset -= segment->size; |
| 146 | } |
| 147 | |
| 148 | *data_left = 0; |
| 149 | return NULL; |
| 150 | } |
| 151 | |
| 152 | static void rproc_copy_segment(struct rproc *rproc, void *dest, |
| 153 | struct rproc_dump_segment *segment, |
| 154 | size_t offset, size_t size) |
| 155 | { |
Dong Aisheng | 970675f | 2021-09-10 17:06:17 +0800 | [diff] [blame] | 156 | bool is_iomem = false; |
Rishabh Bhatnagar | c973198 | 2020-07-16 15:20:34 -0700 | [diff] [blame] | 157 | void *ptr; |
| 158 | |
| 159 | if (segment->dump) { |
| 160 | segment->dump(rproc, segment, dest, offset, size); |
| 161 | } else { |
Peng Fan | 40df0a9 | 2021-03-06 19:24:19 +0800 | [diff] [blame] | 162 | ptr = rproc_da_to_va(rproc, segment->da + offset, size, &is_iomem); |
Rishabh Bhatnagar | c973198 | 2020-07-16 15:20:34 -0700 | [diff] [blame] | 163 | if (!ptr) { |
| 164 | dev_err(&rproc->dev, |
| 165 | "invalid copy request for segment %pad with offset %zu and size %zu)\n", |
| 166 | &segment->da, offset, size); |
| 167 | memset(dest, 0xff, size); |
| 168 | } else { |
Peng Fan | 40df0a9 | 2021-03-06 19:24:19 +0800 | [diff] [blame] | 169 | if (is_iomem) |
Peng Fan | 876e0b2 | 2021-11-10 11:21:01 +0800 | [diff] [blame] | 170 | memcpy_fromio(dest, (void const __iomem *)ptr, size); |
Peng Fan | 40df0a9 | 2021-03-06 19:24:19 +0800 | [diff] [blame] | 171 | else |
| 172 | memcpy(dest, ptr, size); |
Rishabh Bhatnagar | c973198 | 2020-07-16 15:20:34 -0700 | [diff] [blame] | 173 | } |
| 174 | } |
| 175 | } |
| 176 | |
| 177 | static ssize_t rproc_coredump_read(char *buffer, loff_t offset, size_t count, |
| 178 | void *data, size_t header_sz) |
| 179 | { |
| 180 | size_t seg_data, bytes_left = count; |
| 181 | ssize_t copy_sz; |
| 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; |
| 186 | |
| 187 | /* Copy the vmalloc'ed header first. */ |
| 188 | if (offset < header_sz) { |
| 189 | copy_sz = memory_read_from_buffer(buffer, count, &offset, |
| 190 | elfcore, header_sz); |
| 191 | |
| 192 | return copy_sz; |
| 193 | } |
| 194 | |
| 195 | /* |
| 196 | * Find out the segment memory chunk to be copied based on offset. |
| 197 | * Keep copying data until count bytes are read. |
| 198 | */ |
| 199 | while (bytes_left) { |
| 200 | seg = rproc_coredump_find_segment(offset - header_sz, |
| 201 | &rproc->dump_segments, |
| 202 | &seg_data); |
| 203 | /* EOF check */ |
| 204 | if (!seg) { |
| 205 | dev_info(&rproc->dev, "Ramdump done, %lld bytes read", |
| 206 | offset); |
| 207 | break; |
| 208 | } |
| 209 | |
| 210 | copy_sz = min_t(size_t, bytes_left, seg_data); |
| 211 | |
| 212 | rproc_copy_segment(rproc, buffer, seg, seg->size - seg_data, |
| 213 | copy_sz); |
| 214 | |
| 215 | offset += copy_sz; |
| 216 | buffer += copy_sz; |
| 217 | bytes_left -= copy_sz; |
| 218 | } |
| 219 | |
| 220 | return count - bytes_left; |
| 221 | } |
| 222 | |
Rishabh Bhatnagar | 2c010cc | 2020-07-16 15:20:31 -0700 | [diff] [blame] | 223 | /** |
| 224 | * rproc_coredump() - perform coredump |
| 225 | * @rproc: rproc handle |
| 226 | * |
| 227 | * This function will generate an ELF header for the registered segments |
Rishabh Bhatnagar | c973198 | 2020-07-16 15:20:34 -0700 | [diff] [blame] | 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. |
Rishabh Bhatnagar | 2c010cc | 2020-07-16 15:20:31 -0700 | [diff] [blame] | 234 | */ |
| 235 | void rproc_coredump(struct rproc *rproc) |
| 236 | { |
| 237 | struct rproc_dump_segment *segment; |
| 238 | void *phdr; |
| 239 | void *ehdr; |
| 240 | size_t data_size; |
| 241 | size_t offset; |
| 242 | void *data; |
Rishabh Bhatnagar | 2c010cc | 2020-07-16 15:20:31 -0700 | [diff] [blame] | 243 | u8 class = rproc->elf_class; |
| 244 | int phnum = 0; |
Rishabh Bhatnagar | c973198 | 2020-07-16 15:20:34 -0700 | [diff] [blame] | 245 | struct rproc_coredump_state dump_state; |
| 246 | enum rproc_dump_mechanism dump_conf = rproc->dump_conf; |
Rishabh Bhatnagar | 2c010cc | 2020-07-16 15:20:31 -0700 | [diff] [blame] | 247 | |
Rishabh Bhatnagar | c973198 | 2020-07-16 15:20:34 -0700 | [diff] [blame] | 248 | if (list_empty(&rproc->dump_segments) || |
| 249 | dump_conf == RPROC_COREDUMP_DISABLED) |
Rishabh Bhatnagar | 2c010cc | 2020-07-16 15:20:31 -0700 | [diff] [blame] | 250 | return; |
| 251 | |
| 252 | if (class == ELFCLASSNONE) { |
Alexey Dobriyan | 70e7986 | 2023-02-28 15:14:17 +0300 | [diff] [blame] | 253 | dev_err(&rproc->dev, "ELF class is not set\n"); |
Rishabh Bhatnagar | 2c010cc | 2020-07-16 15:20:31 -0700 | [diff] [blame] | 254 | return; |
| 255 | } |
| 256 | |
| 257 | data_size = elf_size_of_hdr(class); |
| 258 | list_for_each_entry(segment, &rproc->dump_segments, node) { |
Rishabh Bhatnagar | c973198 | 2020-07-16 15:20:34 -0700 | [diff] [blame] | 259 | /* |
| 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. |
| 263 | */ |
| 264 | data_size += elf_size_of_phdr(class); |
Rishabh Bhatnagar | bf41a09 | 2020-10-02 11:09:02 -0700 | [diff] [blame] | 265 | if (dump_conf == RPROC_COREDUMP_ENABLED) |
Rishabh Bhatnagar | c973198 | 2020-07-16 15:20:34 -0700 | [diff] [blame] | 266 | data_size += segment->size; |
Rishabh Bhatnagar | 2c010cc | 2020-07-16 15:20:31 -0700 | [diff] [blame] | 267 | |
| 268 | phnum++; |
| 269 | } |
| 270 | |
| 271 | data = vmalloc(data_size); |
| 272 | if (!data) |
| 273 | return; |
| 274 | |
| 275 | ehdr = data; |
| 276 | |
| 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); |
| 280 | |
| 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); |
| 289 | |
| 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); |
| 293 | |
| 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); |
| 304 | |
Rishabh Bhatnagar | bf41a09 | 2020-10-02 11:09:02 -0700 | [diff] [blame] | 305 | if (dump_conf == RPROC_COREDUMP_ENABLED) |
Rishabh Bhatnagar | c973198 | 2020-07-16 15:20:34 -0700 | [diff] [blame] | 306 | rproc_copy_segment(rproc, data + offset, segment, 0, |
| 307 | segment->size); |
Rishabh Bhatnagar | 2c010cc | 2020-07-16 15:20:31 -0700 | [diff] [blame] | 308 | |
| 309 | offset += elf_phdr_get_p_filesz(class, phdr); |
| 310 | phdr += elf_size_of_phdr(class); |
| 311 | } |
Rishabh Bhatnagar | bf41a09 | 2020-10-02 11:09:02 -0700 | [diff] [blame] | 312 | if (dump_conf == RPROC_COREDUMP_ENABLED) { |
Rishabh Bhatnagar | c973198 | 2020-07-16 15:20:34 -0700 | [diff] [blame] | 313 | dev_coredumpv(&rproc->dev, data, data_size, GFP_KERNEL); |
| 314 | return; |
| 315 | } |
Rishabh Bhatnagar | 2c010cc | 2020-07-16 15:20:31 -0700 | [diff] [blame] | 316 | |
Rishabh Bhatnagar | c973198 | 2020-07-16 15:20:34 -0700 | [diff] [blame] | 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); |
| 321 | |
| 322 | dev_coredumpm(&rproc->dev, NULL, &dump_state, data_size, GFP_KERNEL, |
| 323 | rproc_coredump_read, rproc_coredump_free); |
| 324 | |
| 325 | /* |
| 326 | * Wait until the dump is read and free is called. Data is freed |
| 327 | * by devcoredump framework automatically after 5 minutes. |
| 328 | */ |
| 329 | wait_for_completion(&dump_state.dump_done); |
Rishabh Bhatnagar | 2c010cc | 2020-07-16 15:20:31 -0700 | [diff] [blame] | 330 | } |
Siddharth Gupta | f247f08 | 2023-02-24 13:17:06 -0800 | [diff] [blame] | 331 | EXPORT_SYMBOL_GPL(rproc_coredump); |
Siddharth Gupta | abc72b6 | 2020-11-19 13:05:33 -0800 | [diff] [blame] | 332 | |
| 333 | /** |
| 334 | * rproc_coredump_using_sections() - perform coredump using section headers |
| 335 | * @rproc: rproc handle |
| 336 | * |
| 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. |
| 344 | */ |
| 345 | void rproc_coredump_using_sections(struct rproc *rproc) |
| 346 | { |
| 347 | struct rproc_dump_segment *segment; |
| 348 | void *shdr; |
| 349 | void *ehdr; |
| 350 | size_t data_size; |
| 351 | size_t strtbl_size = 0; |
| 352 | size_t strtbl_index = 1; |
| 353 | size_t offset; |
| 354 | void *data; |
| 355 | u8 class = rproc->elf_class; |
| 356 | int shnum; |
| 357 | struct rproc_coredump_state dump_state; |
| 358 | unsigned int dump_conf = rproc->dump_conf; |
| 359 | char *str_tbl = "STR_TBL"; |
| 360 | |
| 361 | if (list_empty(&rproc->dump_segments) || |
| 362 | dump_conf == RPROC_COREDUMP_DISABLED) |
| 363 | return; |
| 364 | |
| 365 | if (class == ELFCLASSNONE) { |
Alexey Dobriyan | 70e7986 | 2023-02-28 15:14:17 +0300 | [diff] [blame] | 366 | dev_err(&rproc->dev, "ELF class is not set\n"); |
Siddharth Gupta | abc72b6 | 2020-11-19 13:05:33 -0800 | [diff] [blame] | 367 | return; |
| 368 | } |
| 369 | |
| 370 | /* |
| 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. |
| 374 | */ |
| 375 | data_size = elf_size_of_hdr(class) + 2 * elf_size_of_shdr(class); |
| 376 | shnum = 2; |
| 377 | |
| 378 | /* the extra byte is for the null character at index 0 */ |
| 379 | strtbl_size += strlen(str_tbl) + 2; |
| 380 | |
| 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; |
| 386 | shnum++; |
| 387 | } |
| 388 | |
| 389 | data_size += strtbl_size; |
| 390 | |
| 391 | data = vmalloc(data_size); |
| 392 | if (!data) |
| 393 | return; |
| 394 | |
| 395 | ehdr = data; |
| 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); |
| 399 | |
| 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); |
| 409 | |
| 410 | /* |
| 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. |
| 413 | */ |
| 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); |
| 417 | |
| 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); |
| 422 | |
| 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); |
| 433 | |
| 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)); |
| 444 | |
| 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, |
| 448 | segment->size); |
| 449 | offset += elf_shdr_get_sh_size(class, shdr); |
| 450 | shdr += elf_size_of_shdr(class); |
| 451 | } |
| 452 | |
| 453 | if (dump_conf == RPROC_COREDUMP_ENABLED) { |
| 454 | dev_coredumpv(&rproc->dev, data, data_size, GFP_KERNEL); |
| 455 | return; |
| 456 | } |
| 457 | |
| 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); |
| 462 | |
| 463 | dev_coredumpm(&rproc->dev, NULL, &dump_state, data_size, GFP_KERNEL, |
| 464 | rproc_coredump_read, rproc_coredump_free); |
| 465 | |
| 466 | /* Wait until the dump is read and free is called. Data is freed |
| 467 | * by devcoredump framework automatically after 5 minutes. |
| 468 | */ |
| 469 | wait_for_completion(&dump_state.dump_done); |
| 470 | } |
| 471 | EXPORT_SYMBOL(rproc_coredump_using_sections); |