162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Coredump functionality for Remoteproc framework. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2020, The Linux Foundation. All rights reserved. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/completion.h> 962306a36Sopenharmony_ci#include <linux/devcoredump.h> 1062306a36Sopenharmony_ci#include <linux/device.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/remoteproc.h> 1362306a36Sopenharmony_ci#include "remoteproc_internal.h" 1462306a36Sopenharmony_ci#include "remoteproc_elf_helpers.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistruct rproc_coredump_state { 1762306a36Sopenharmony_ci struct rproc *rproc; 1862306a36Sopenharmony_ci void *header; 1962306a36Sopenharmony_ci struct completion dump_done; 2062306a36Sopenharmony_ci}; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/** 2362306a36Sopenharmony_ci * rproc_coredump_cleanup() - clean up dump_segments list 2462306a36Sopenharmony_ci * @rproc: the remote processor handle 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_civoid rproc_coredump_cleanup(struct rproc *rproc) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci struct rproc_dump_segment *entry, *tmp; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci list_for_each_entry_safe(entry, tmp, &rproc->dump_segments, node) { 3162306a36Sopenharmony_ci list_del(&entry->node); 3262306a36Sopenharmony_ci kfree(entry); 3362306a36Sopenharmony_ci } 3462306a36Sopenharmony_ci} 3562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rproc_coredump_cleanup); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/** 3862306a36Sopenharmony_ci * rproc_coredump_add_segment() - add segment of device memory to coredump 3962306a36Sopenharmony_ci * @rproc: handle of a remote processor 4062306a36Sopenharmony_ci * @da: device address 4162306a36Sopenharmony_ci * @size: size of segment 4262306a36Sopenharmony_ci * 4362306a36Sopenharmony_ci * Add device memory to the list of segments to be included in a coredump for 4462306a36Sopenharmony_ci * the remoteproc. 4562306a36Sopenharmony_ci * 4662306a36Sopenharmony_ci * Return: 0 on success, negative errno on error. 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_ciint rproc_coredump_add_segment(struct rproc *rproc, dma_addr_t da, size_t size) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci struct rproc_dump_segment *segment; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci segment = kzalloc(sizeof(*segment), GFP_KERNEL); 5362306a36Sopenharmony_ci if (!segment) 5462306a36Sopenharmony_ci return -ENOMEM; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci segment->da = da; 5762306a36Sopenharmony_ci segment->size = size; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci list_add_tail(&segment->node, &rproc->dump_segments); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci return 0; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ciEXPORT_SYMBOL(rproc_coredump_add_segment); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/** 6662306a36Sopenharmony_ci * rproc_coredump_add_custom_segment() - add custom coredump segment 6762306a36Sopenharmony_ci * @rproc: handle of a remote processor 6862306a36Sopenharmony_ci * @da: device address 6962306a36Sopenharmony_ci * @size: size of segment 7062306a36Sopenharmony_ci * @dumpfn: custom dump function called for each segment during coredump 7162306a36Sopenharmony_ci * @priv: private data 7262306a36Sopenharmony_ci * 7362306a36Sopenharmony_ci * Add device memory to the list of segments to be included in the coredump 7462306a36Sopenharmony_ci * and associate the segment with the given custom dump function and private 7562306a36Sopenharmony_ci * data. 7662306a36Sopenharmony_ci * 7762306a36Sopenharmony_ci * Return: 0 on success, negative errno on error. 7862306a36Sopenharmony_ci */ 7962306a36Sopenharmony_ciint rproc_coredump_add_custom_segment(struct rproc *rproc, 8062306a36Sopenharmony_ci dma_addr_t da, size_t size, 8162306a36Sopenharmony_ci void (*dumpfn)(struct rproc *rproc, 8262306a36Sopenharmony_ci struct rproc_dump_segment *segment, 8362306a36Sopenharmony_ci void *dest, size_t offset, 8462306a36Sopenharmony_ci size_t size), 8562306a36Sopenharmony_ci void *priv) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci struct rproc_dump_segment *segment; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci segment = kzalloc(sizeof(*segment), GFP_KERNEL); 9062306a36Sopenharmony_ci if (!segment) 9162306a36Sopenharmony_ci return -ENOMEM; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci segment->da = da; 9462306a36Sopenharmony_ci segment->size = size; 9562306a36Sopenharmony_ci segment->priv = priv; 9662306a36Sopenharmony_ci segment->dump = dumpfn; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci list_add_tail(&segment->node, &rproc->dump_segments); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci return 0; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ciEXPORT_SYMBOL(rproc_coredump_add_custom_segment); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci/** 10562306a36Sopenharmony_ci * rproc_coredump_set_elf_info() - set coredump elf information 10662306a36Sopenharmony_ci * @rproc: handle of a remote processor 10762306a36Sopenharmony_ci * @class: elf class for coredump elf file 10862306a36Sopenharmony_ci * @machine: elf machine for coredump elf file 10962306a36Sopenharmony_ci * 11062306a36Sopenharmony_ci * Set elf information which will be used for coredump elf file. 11162306a36Sopenharmony_ci * 11262306a36Sopenharmony_ci * Return: 0 on success, negative errno on error. 11362306a36Sopenharmony_ci */ 11462306a36Sopenharmony_ciint rproc_coredump_set_elf_info(struct rproc *rproc, u8 class, u16 machine) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci if (class != ELFCLASS64 && class != ELFCLASS32) 11762306a36Sopenharmony_ci return -EINVAL; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci rproc->elf_class = class; 12062306a36Sopenharmony_ci rproc->elf_machine = machine; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci return 0; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ciEXPORT_SYMBOL(rproc_coredump_set_elf_info); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic void rproc_coredump_free(void *data) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci struct rproc_coredump_state *dump_state = data; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci vfree(dump_state->header); 13162306a36Sopenharmony_ci complete(&dump_state->dump_done); 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic void *rproc_coredump_find_segment(loff_t user_offset, 13562306a36Sopenharmony_ci struct list_head *segments, 13662306a36Sopenharmony_ci size_t *data_left) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci struct rproc_dump_segment *segment; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci list_for_each_entry(segment, segments, node) { 14162306a36Sopenharmony_ci if (user_offset < segment->size) { 14262306a36Sopenharmony_ci *data_left = segment->size - user_offset; 14362306a36Sopenharmony_ci return segment; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci user_offset -= segment->size; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci *data_left = 0; 14962306a36Sopenharmony_ci return NULL; 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic void rproc_copy_segment(struct rproc *rproc, void *dest, 15362306a36Sopenharmony_ci struct rproc_dump_segment *segment, 15462306a36Sopenharmony_ci size_t offset, size_t size) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci bool is_iomem = false; 15762306a36Sopenharmony_ci void *ptr; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (segment->dump) { 16062306a36Sopenharmony_ci segment->dump(rproc, segment, dest, offset, size); 16162306a36Sopenharmony_ci } else { 16262306a36Sopenharmony_ci ptr = rproc_da_to_va(rproc, segment->da + offset, size, &is_iomem); 16362306a36Sopenharmony_ci if (!ptr) { 16462306a36Sopenharmony_ci dev_err(&rproc->dev, 16562306a36Sopenharmony_ci "invalid copy request for segment %pad with offset %zu and size %zu)\n", 16662306a36Sopenharmony_ci &segment->da, offset, size); 16762306a36Sopenharmony_ci memset(dest, 0xff, size); 16862306a36Sopenharmony_ci } else { 16962306a36Sopenharmony_ci if (is_iomem) 17062306a36Sopenharmony_ci memcpy_fromio(dest, (void const __iomem *)ptr, size); 17162306a36Sopenharmony_ci else 17262306a36Sopenharmony_ci memcpy(dest, ptr, size); 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic ssize_t rproc_coredump_read(char *buffer, loff_t offset, size_t count, 17862306a36Sopenharmony_ci void *data, size_t header_sz) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci size_t seg_data, bytes_left = count; 18162306a36Sopenharmony_ci ssize_t copy_sz; 18262306a36Sopenharmony_ci struct rproc_dump_segment *seg; 18362306a36Sopenharmony_ci struct rproc_coredump_state *dump_state = data; 18462306a36Sopenharmony_ci struct rproc *rproc = dump_state->rproc; 18562306a36Sopenharmony_ci void *elfcore = dump_state->header; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci /* Copy the vmalloc'ed header first. */ 18862306a36Sopenharmony_ci if (offset < header_sz) { 18962306a36Sopenharmony_ci copy_sz = memory_read_from_buffer(buffer, count, &offset, 19062306a36Sopenharmony_ci elfcore, header_sz); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci return copy_sz; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* 19662306a36Sopenharmony_ci * Find out the segment memory chunk to be copied based on offset. 19762306a36Sopenharmony_ci * Keep copying data until count bytes are read. 19862306a36Sopenharmony_ci */ 19962306a36Sopenharmony_ci while (bytes_left) { 20062306a36Sopenharmony_ci seg = rproc_coredump_find_segment(offset - header_sz, 20162306a36Sopenharmony_ci &rproc->dump_segments, 20262306a36Sopenharmony_ci &seg_data); 20362306a36Sopenharmony_ci /* EOF check */ 20462306a36Sopenharmony_ci if (!seg) { 20562306a36Sopenharmony_ci dev_info(&rproc->dev, "Ramdump done, %lld bytes read", 20662306a36Sopenharmony_ci offset); 20762306a36Sopenharmony_ci break; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci copy_sz = min_t(size_t, bytes_left, seg_data); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci rproc_copy_segment(rproc, buffer, seg, seg->size - seg_data, 21362306a36Sopenharmony_ci copy_sz); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci offset += copy_sz; 21662306a36Sopenharmony_ci buffer += copy_sz; 21762306a36Sopenharmony_ci bytes_left -= copy_sz; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci return count - bytes_left; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci/** 22462306a36Sopenharmony_ci * rproc_coredump() - perform coredump 22562306a36Sopenharmony_ci * @rproc: rproc handle 22662306a36Sopenharmony_ci * 22762306a36Sopenharmony_ci * This function will generate an ELF header for the registered segments 22862306a36Sopenharmony_ci * and create a devcoredump device associated with rproc. Based on the 22962306a36Sopenharmony_ci * coredump configuration this function will directly copy the segments 23062306a36Sopenharmony_ci * from device memory to userspace or copy segments from device memory to 23162306a36Sopenharmony_ci * a separate buffer, which can then be read by userspace. 23262306a36Sopenharmony_ci * The first approach avoids using extra vmalloc memory. But it will stall 23362306a36Sopenharmony_ci * recovery flow until dump is read by userspace. 23462306a36Sopenharmony_ci */ 23562306a36Sopenharmony_civoid rproc_coredump(struct rproc *rproc) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci struct rproc_dump_segment *segment; 23862306a36Sopenharmony_ci void *phdr; 23962306a36Sopenharmony_ci void *ehdr; 24062306a36Sopenharmony_ci size_t data_size; 24162306a36Sopenharmony_ci size_t offset; 24262306a36Sopenharmony_ci void *data; 24362306a36Sopenharmony_ci u8 class = rproc->elf_class; 24462306a36Sopenharmony_ci int phnum = 0; 24562306a36Sopenharmony_ci struct rproc_coredump_state dump_state; 24662306a36Sopenharmony_ci enum rproc_dump_mechanism dump_conf = rproc->dump_conf; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if (list_empty(&rproc->dump_segments) || 24962306a36Sopenharmony_ci dump_conf == RPROC_COREDUMP_DISABLED) 25062306a36Sopenharmony_ci return; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (class == ELFCLASSNONE) { 25362306a36Sopenharmony_ci dev_err(&rproc->dev, "ELF class is not set\n"); 25462306a36Sopenharmony_ci return; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci data_size = elf_size_of_hdr(class); 25862306a36Sopenharmony_ci list_for_each_entry(segment, &rproc->dump_segments, node) { 25962306a36Sopenharmony_ci /* 26062306a36Sopenharmony_ci * For default configuration buffer includes headers & segments. 26162306a36Sopenharmony_ci * For inline dump buffer just includes headers as segments are 26262306a36Sopenharmony_ci * directly read from device memory. 26362306a36Sopenharmony_ci */ 26462306a36Sopenharmony_ci data_size += elf_size_of_phdr(class); 26562306a36Sopenharmony_ci if (dump_conf == RPROC_COREDUMP_ENABLED) 26662306a36Sopenharmony_ci data_size += segment->size; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci phnum++; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci data = vmalloc(data_size); 27262306a36Sopenharmony_ci if (!data) 27362306a36Sopenharmony_ci return; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci ehdr = data; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci memset(ehdr, 0, elf_size_of_hdr(class)); 27862306a36Sopenharmony_ci /* e_ident field is common for both elf32 and elf64 */ 27962306a36Sopenharmony_ci elf_hdr_init_ident(ehdr, class); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci elf_hdr_set_e_type(class, ehdr, ET_CORE); 28262306a36Sopenharmony_ci elf_hdr_set_e_machine(class, ehdr, rproc->elf_machine); 28362306a36Sopenharmony_ci elf_hdr_set_e_version(class, ehdr, EV_CURRENT); 28462306a36Sopenharmony_ci elf_hdr_set_e_entry(class, ehdr, rproc->bootaddr); 28562306a36Sopenharmony_ci elf_hdr_set_e_phoff(class, ehdr, elf_size_of_hdr(class)); 28662306a36Sopenharmony_ci elf_hdr_set_e_ehsize(class, ehdr, elf_size_of_hdr(class)); 28762306a36Sopenharmony_ci elf_hdr_set_e_phentsize(class, ehdr, elf_size_of_phdr(class)); 28862306a36Sopenharmony_ci elf_hdr_set_e_phnum(class, ehdr, phnum); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci phdr = data + elf_hdr_get_e_phoff(class, ehdr); 29162306a36Sopenharmony_ci offset = elf_hdr_get_e_phoff(class, ehdr); 29262306a36Sopenharmony_ci offset += elf_size_of_phdr(class) * elf_hdr_get_e_phnum(class, ehdr); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci list_for_each_entry(segment, &rproc->dump_segments, node) { 29562306a36Sopenharmony_ci memset(phdr, 0, elf_size_of_phdr(class)); 29662306a36Sopenharmony_ci elf_phdr_set_p_type(class, phdr, PT_LOAD); 29762306a36Sopenharmony_ci elf_phdr_set_p_offset(class, phdr, offset); 29862306a36Sopenharmony_ci elf_phdr_set_p_vaddr(class, phdr, segment->da); 29962306a36Sopenharmony_ci elf_phdr_set_p_paddr(class, phdr, segment->da); 30062306a36Sopenharmony_ci elf_phdr_set_p_filesz(class, phdr, segment->size); 30162306a36Sopenharmony_ci elf_phdr_set_p_memsz(class, phdr, segment->size); 30262306a36Sopenharmony_ci elf_phdr_set_p_flags(class, phdr, PF_R | PF_W | PF_X); 30362306a36Sopenharmony_ci elf_phdr_set_p_align(class, phdr, 0); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (dump_conf == RPROC_COREDUMP_ENABLED) 30662306a36Sopenharmony_ci rproc_copy_segment(rproc, data + offset, segment, 0, 30762306a36Sopenharmony_ci segment->size); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci offset += elf_phdr_get_p_filesz(class, phdr); 31062306a36Sopenharmony_ci phdr += elf_size_of_phdr(class); 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci if (dump_conf == RPROC_COREDUMP_ENABLED) { 31362306a36Sopenharmony_ci dev_coredumpv(&rproc->dev, data, data_size, GFP_KERNEL); 31462306a36Sopenharmony_ci return; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci /* Initialize the dump state struct to be used by rproc_coredump_read */ 31862306a36Sopenharmony_ci dump_state.rproc = rproc; 31962306a36Sopenharmony_ci dump_state.header = data; 32062306a36Sopenharmony_ci init_completion(&dump_state.dump_done); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci dev_coredumpm(&rproc->dev, NULL, &dump_state, data_size, GFP_KERNEL, 32362306a36Sopenharmony_ci rproc_coredump_read, rproc_coredump_free); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* 32662306a36Sopenharmony_ci * Wait until the dump is read and free is called. Data is freed 32762306a36Sopenharmony_ci * by devcoredump framework automatically after 5 minutes. 32862306a36Sopenharmony_ci */ 32962306a36Sopenharmony_ci wait_for_completion(&dump_state.dump_done); 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rproc_coredump); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci/** 33462306a36Sopenharmony_ci * rproc_coredump_using_sections() - perform coredump using section headers 33562306a36Sopenharmony_ci * @rproc: rproc handle 33662306a36Sopenharmony_ci * 33762306a36Sopenharmony_ci * This function will generate an ELF header for the registered sections of 33862306a36Sopenharmony_ci * segments and create a devcoredump device associated with rproc. Based on 33962306a36Sopenharmony_ci * the coredump configuration this function will directly copy the segments 34062306a36Sopenharmony_ci * from device memory to userspace or copy segments from device memory to 34162306a36Sopenharmony_ci * a separate buffer, which can then be read by userspace. 34262306a36Sopenharmony_ci * The first approach avoids using extra vmalloc memory. But it will stall 34362306a36Sopenharmony_ci * recovery flow until dump is read by userspace. 34462306a36Sopenharmony_ci */ 34562306a36Sopenharmony_civoid rproc_coredump_using_sections(struct rproc *rproc) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci struct rproc_dump_segment *segment; 34862306a36Sopenharmony_ci void *shdr; 34962306a36Sopenharmony_ci void *ehdr; 35062306a36Sopenharmony_ci size_t data_size; 35162306a36Sopenharmony_ci size_t strtbl_size = 0; 35262306a36Sopenharmony_ci size_t strtbl_index = 1; 35362306a36Sopenharmony_ci size_t offset; 35462306a36Sopenharmony_ci void *data; 35562306a36Sopenharmony_ci u8 class = rproc->elf_class; 35662306a36Sopenharmony_ci int shnum; 35762306a36Sopenharmony_ci struct rproc_coredump_state dump_state; 35862306a36Sopenharmony_ci unsigned int dump_conf = rproc->dump_conf; 35962306a36Sopenharmony_ci char *str_tbl = "STR_TBL"; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (list_empty(&rproc->dump_segments) || 36262306a36Sopenharmony_ci dump_conf == RPROC_COREDUMP_DISABLED) 36362306a36Sopenharmony_ci return; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (class == ELFCLASSNONE) { 36662306a36Sopenharmony_ci dev_err(&rproc->dev, "ELF class is not set\n"); 36762306a36Sopenharmony_ci return; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci /* 37162306a36Sopenharmony_ci * We allocate two extra section headers. The first one is null. 37262306a36Sopenharmony_ci * Second section header is for the string table. Also space is 37362306a36Sopenharmony_ci * allocated for string table. 37462306a36Sopenharmony_ci */ 37562306a36Sopenharmony_ci data_size = elf_size_of_hdr(class) + 2 * elf_size_of_shdr(class); 37662306a36Sopenharmony_ci shnum = 2; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci /* the extra byte is for the null character at index 0 */ 37962306a36Sopenharmony_ci strtbl_size += strlen(str_tbl) + 2; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci list_for_each_entry(segment, &rproc->dump_segments, node) { 38262306a36Sopenharmony_ci data_size += elf_size_of_shdr(class); 38362306a36Sopenharmony_ci strtbl_size += strlen(segment->priv) + 1; 38462306a36Sopenharmony_ci if (dump_conf == RPROC_COREDUMP_ENABLED) 38562306a36Sopenharmony_ci data_size += segment->size; 38662306a36Sopenharmony_ci shnum++; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci data_size += strtbl_size; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci data = vmalloc(data_size); 39262306a36Sopenharmony_ci if (!data) 39362306a36Sopenharmony_ci return; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci ehdr = data; 39662306a36Sopenharmony_ci memset(ehdr, 0, elf_size_of_hdr(class)); 39762306a36Sopenharmony_ci /* e_ident field is common for both elf32 and elf64 */ 39862306a36Sopenharmony_ci elf_hdr_init_ident(ehdr, class); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci elf_hdr_set_e_type(class, ehdr, ET_CORE); 40162306a36Sopenharmony_ci elf_hdr_set_e_machine(class, ehdr, rproc->elf_machine); 40262306a36Sopenharmony_ci elf_hdr_set_e_version(class, ehdr, EV_CURRENT); 40362306a36Sopenharmony_ci elf_hdr_set_e_entry(class, ehdr, rproc->bootaddr); 40462306a36Sopenharmony_ci elf_hdr_set_e_shoff(class, ehdr, elf_size_of_hdr(class)); 40562306a36Sopenharmony_ci elf_hdr_set_e_ehsize(class, ehdr, elf_size_of_hdr(class)); 40662306a36Sopenharmony_ci elf_hdr_set_e_shentsize(class, ehdr, elf_size_of_shdr(class)); 40762306a36Sopenharmony_ci elf_hdr_set_e_shnum(class, ehdr, shnum); 40862306a36Sopenharmony_ci elf_hdr_set_e_shstrndx(class, ehdr, 1); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci /* 41162306a36Sopenharmony_ci * The zeroth index of the section header is reserved and is rarely used. 41262306a36Sopenharmony_ci * Set the section header as null (SHN_UNDEF) and move to the next one. 41362306a36Sopenharmony_ci */ 41462306a36Sopenharmony_ci shdr = data + elf_hdr_get_e_shoff(class, ehdr); 41562306a36Sopenharmony_ci memset(shdr, 0, elf_size_of_shdr(class)); 41662306a36Sopenharmony_ci shdr += elf_size_of_shdr(class); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci /* Initialize the string table. */ 41962306a36Sopenharmony_ci offset = elf_hdr_get_e_shoff(class, ehdr) + 42062306a36Sopenharmony_ci elf_size_of_shdr(class) * elf_hdr_get_e_shnum(class, ehdr); 42162306a36Sopenharmony_ci memset(data + offset, 0, strtbl_size); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci /* Fill in the string table section header. */ 42462306a36Sopenharmony_ci memset(shdr, 0, elf_size_of_shdr(class)); 42562306a36Sopenharmony_ci elf_shdr_set_sh_type(class, shdr, SHT_STRTAB); 42662306a36Sopenharmony_ci elf_shdr_set_sh_offset(class, shdr, offset); 42762306a36Sopenharmony_ci elf_shdr_set_sh_size(class, shdr, strtbl_size); 42862306a36Sopenharmony_ci elf_shdr_set_sh_entsize(class, shdr, 0); 42962306a36Sopenharmony_ci elf_shdr_set_sh_flags(class, shdr, 0); 43062306a36Sopenharmony_ci elf_shdr_set_sh_name(class, shdr, elf_strtbl_add(str_tbl, ehdr, class, &strtbl_index)); 43162306a36Sopenharmony_ci offset += elf_shdr_get_sh_size(class, shdr); 43262306a36Sopenharmony_ci shdr += elf_size_of_shdr(class); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci list_for_each_entry(segment, &rproc->dump_segments, node) { 43562306a36Sopenharmony_ci memset(shdr, 0, elf_size_of_shdr(class)); 43662306a36Sopenharmony_ci elf_shdr_set_sh_type(class, shdr, SHT_PROGBITS); 43762306a36Sopenharmony_ci elf_shdr_set_sh_offset(class, shdr, offset); 43862306a36Sopenharmony_ci elf_shdr_set_sh_addr(class, shdr, segment->da); 43962306a36Sopenharmony_ci elf_shdr_set_sh_size(class, shdr, segment->size); 44062306a36Sopenharmony_ci elf_shdr_set_sh_entsize(class, shdr, 0); 44162306a36Sopenharmony_ci elf_shdr_set_sh_flags(class, shdr, SHF_WRITE); 44262306a36Sopenharmony_ci elf_shdr_set_sh_name(class, shdr, 44362306a36Sopenharmony_ci elf_strtbl_add(segment->priv, ehdr, class, &strtbl_index)); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci /* No need to copy segments for inline dumps */ 44662306a36Sopenharmony_ci if (dump_conf == RPROC_COREDUMP_ENABLED) 44762306a36Sopenharmony_ci rproc_copy_segment(rproc, data + offset, segment, 0, 44862306a36Sopenharmony_ci segment->size); 44962306a36Sopenharmony_ci offset += elf_shdr_get_sh_size(class, shdr); 45062306a36Sopenharmony_ci shdr += elf_size_of_shdr(class); 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (dump_conf == RPROC_COREDUMP_ENABLED) { 45462306a36Sopenharmony_ci dev_coredumpv(&rproc->dev, data, data_size, GFP_KERNEL); 45562306a36Sopenharmony_ci return; 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci /* Initialize the dump state struct to be used by rproc_coredump_read */ 45962306a36Sopenharmony_ci dump_state.rproc = rproc; 46062306a36Sopenharmony_ci dump_state.header = data; 46162306a36Sopenharmony_ci init_completion(&dump_state.dump_done); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci dev_coredumpm(&rproc->dev, NULL, &dump_state, data_size, GFP_KERNEL, 46462306a36Sopenharmony_ci rproc_coredump_read, rproc_coredump_free); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci /* Wait until the dump is read and free is called. Data is freed 46762306a36Sopenharmony_ci * by devcoredump framework automatically after 5 minutes. 46862306a36Sopenharmony_ci */ 46962306a36Sopenharmony_ci wait_for_completion(&dump_state.dump_done); 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ciEXPORT_SYMBOL(rproc_coredump_using_sections); 472