Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Coredump functionality for Remoteproc framework.
0004  *
0005  * Copyright (c) 2020, The Linux Foundation. All rights reserved.
0006  */
0007 
0008 #include <linux/completion.h>
0009 #include <linux/devcoredump.h>
0010 #include <linux/device.h>
0011 #include <linux/kernel.h>
0012 #include <linux/remoteproc.h>
0013 #include "remoteproc_internal.h"
0014 #include "remoteproc_elf_helpers.h"
0015 
0016 struct rproc_coredump_state {
0017     struct rproc *rproc;
0018     void *header;
0019     struct completion dump_done;
0020 };
0021 
0022 /**
0023  * rproc_coredump_cleanup() - clean up dump_segments list
0024  * @rproc: the remote processor handle
0025  */
0026 void rproc_coredump_cleanup(struct rproc *rproc)
0027 {
0028     struct rproc_dump_segment *entry, *tmp;
0029 
0030     list_for_each_entry_safe(entry, tmp, &rproc->dump_segments, node) {
0031         list_del(&entry->node);
0032         kfree(entry);
0033     }
0034 }
0035 
0036 /**
0037  * rproc_coredump_add_segment() - add segment of device memory to coredump
0038  * @rproc:  handle of a remote processor
0039  * @da:     device address
0040  * @size:   size of segment
0041  *
0042  * Add device memory to the list of segments to be included in a coredump for
0043  * the remoteproc.
0044  *
0045  * Return: 0 on success, negative errno on error.
0046  */
0047 int rproc_coredump_add_segment(struct rproc *rproc, dma_addr_t da, size_t size)
0048 {
0049     struct rproc_dump_segment *segment;
0050 
0051     segment = kzalloc(sizeof(*segment), GFP_KERNEL);
0052     if (!segment)
0053         return -ENOMEM;
0054 
0055     segment->da = da;
0056     segment->size = size;
0057 
0058     list_add_tail(&segment->node, &rproc->dump_segments);
0059 
0060     return 0;
0061 }
0062 EXPORT_SYMBOL(rproc_coredump_add_segment);
0063 
0064 /**
0065  * rproc_coredump_add_custom_segment() - add custom coredump segment
0066  * @rproc:  handle of a remote processor
0067  * @da:     device address
0068  * @size:   size of segment
0069  * @dumpfn: custom dump function called for each segment during coredump
0070  * @priv:   private data
0071  *
0072  * Add device memory to the list of segments to be included in the coredump
0073  * and associate the segment with the given custom dump function and private
0074  * data.
0075  *
0076  * Return: 0 on success, negative errno on error.
0077  */
0078 int rproc_coredump_add_custom_segment(struct rproc *rproc,
0079                       dma_addr_t da, size_t size,
0080                       void (*dumpfn)(struct rproc *rproc,
0081                              struct rproc_dump_segment *segment,
0082                              void *dest, size_t offset,
0083                              size_t size),
0084                       void *priv)
0085 {
0086     struct rproc_dump_segment *segment;
0087 
0088     segment = kzalloc(sizeof(*segment), GFP_KERNEL);
0089     if (!segment)
0090         return -ENOMEM;
0091 
0092     segment->da = da;
0093     segment->size = size;
0094     segment->priv = priv;
0095     segment->dump = dumpfn;
0096 
0097     list_add_tail(&segment->node, &rproc->dump_segments);
0098 
0099     return 0;
0100 }
0101 EXPORT_SYMBOL(rproc_coredump_add_custom_segment);
0102 
0103 /**
0104  * rproc_coredump_set_elf_info() - set coredump elf information
0105  * @rproc:  handle of a remote processor
0106  * @class:  elf class for coredump elf file
0107  * @machine:    elf machine for coredump elf file
0108  *
0109  * Set elf information which will be used for coredump elf file.
0110  *
0111  * Return: 0 on success, negative errno on error.
0112  */
0113 int rproc_coredump_set_elf_info(struct rproc *rproc, u8 class, u16 machine)
0114 {
0115     if (class != ELFCLASS64 && class != ELFCLASS32)
0116         return -EINVAL;
0117 
0118     rproc->elf_class = class;
0119     rproc->elf_machine = machine;
0120 
0121     return 0;
0122 }
0123 EXPORT_SYMBOL(rproc_coredump_set_elf_info);
0124 
0125 static void rproc_coredump_free(void *data)
0126 {
0127     struct rproc_coredump_state *dump_state = data;
0128 
0129     vfree(dump_state->header);
0130     complete(&dump_state->dump_done);
0131 }
0132 
0133 static void *rproc_coredump_find_segment(loff_t user_offset,
0134                      struct list_head *segments,
0135                      size_t *data_left)
0136 {
0137     struct rproc_dump_segment *segment;
0138 
0139     list_for_each_entry(segment, segments, node) {
0140         if (user_offset < segment->size) {
0141             *data_left = segment->size - user_offset;
0142             return segment;
0143         }
0144         user_offset -= segment->size;
0145     }
0146 
0147     *data_left = 0;
0148     return NULL;
0149 }
0150 
0151 static void rproc_copy_segment(struct rproc *rproc, void *dest,
0152                    struct rproc_dump_segment *segment,
0153                    size_t offset, size_t size)
0154 {
0155     bool is_iomem = false;
0156     void *ptr;
0157 
0158     if (segment->dump) {
0159         segment->dump(rproc, segment, dest, offset, size);
0160     } else {
0161         ptr = rproc_da_to_va(rproc, segment->da + offset, size, &is_iomem);
0162         if (!ptr) {
0163             dev_err(&rproc->dev,
0164                 "invalid copy request for segment %pad with offset %zu and size %zu)\n",
0165                 &segment->da, offset, size);
0166             memset(dest, 0xff, size);
0167         } else {
0168             if (is_iomem)
0169                 memcpy_fromio(dest, (void const __iomem *)ptr, size);
0170             else
0171                 memcpy(dest, ptr, size);
0172         }
0173     }
0174 }
0175 
0176 static ssize_t rproc_coredump_read(char *buffer, loff_t offset, size_t count,
0177                    void *data, size_t header_sz)
0178 {
0179     size_t seg_data, bytes_left = count;
0180     ssize_t copy_sz;
0181     struct rproc_dump_segment *seg;
0182     struct rproc_coredump_state *dump_state = data;
0183     struct rproc *rproc = dump_state->rproc;
0184     void *elfcore = dump_state->header;
0185 
0186     /* Copy the vmalloc'ed header first. */
0187     if (offset < header_sz) {
0188         copy_sz = memory_read_from_buffer(buffer, count, &offset,
0189                           elfcore, header_sz);
0190 
0191         return copy_sz;
0192     }
0193 
0194     /*
0195      * Find out the segment memory chunk to be copied based on offset.
0196      * Keep copying data until count bytes are read.
0197      */
0198     while (bytes_left) {
0199         seg = rproc_coredump_find_segment(offset - header_sz,
0200                           &rproc->dump_segments,
0201                           &seg_data);
0202         /* EOF check */
0203         if (!seg) {
0204             dev_info(&rproc->dev, "Ramdump done, %lld bytes read",
0205                  offset);
0206             break;
0207         }
0208 
0209         copy_sz = min_t(size_t, bytes_left, seg_data);
0210 
0211         rproc_copy_segment(rproc, buffer, seg, seg->size - seg_data,
0212                    copy_sz);
0213 
0214         offset += copy_sz;
0215         buffer += copy_sz;
0216         bytes_left -= copy_sz;
0217     }
0218 
0219     return count - bytes_left;
0220 }
0221 
0222 /**
0223  * rproc_coredump() - perform coredump
0224  * @rproc:  rproc handle
0225  *
0226  * This function will generate an ELF header for the registered segments
0227  * and create a devcoredump device associated with rproc. Based on the
0228  * coredump configuration this function will directly copy the segments
0229  * from device memory to userspace or copy segments from device memory to
0230  * a separate buffer, which can then be read by userspace.
0231  * The first approach avoids using extra vmalloc memory. But it will stall
0232  * recovery flow until dump is read by userspace.
0233  */
0234 void rproc_coredump(struct rproc *rproc)
0235 {
0236     struct rproc_dump_segment *segment;
0237     void *phdr;
0238     void *ehdr;
0239     size_t data_size;
0240     size_t offset;
0241     void *data;
0242     u8 class = rproc->elf_class;
0243     int phnum = 0;
0244     struct rproc_coredump_state dump_state;
0245     enum rproc_dump_mechanism dump_conf = rproc->dump_conf;
0246 
0247     if (list_empty(&rproc->dump_segments) ||
0248         dump_conf == RPROC_COREDUMP_DISABLED)
0249         return;
0250 
0251     if (class == ELFCLASSNONE) {
0252         dev_err(&rproc->dev, "Elf class is not set\n");
0253         return;
0254     }
0255 
0256     data_size = elf_size_of_hdr(class);
0257     list_for_each_entry(segment, &rproc->dump_segments, node) {
0258         /*
0259          * For default configuration buffer includes headers & segments.
0260          * For inline dump buffer just includes headers as segments are
0261          * directly read from device memory.
0262          */
0263         data_size += elf_size_of_phdr(class);
0264         if (dump_conf == RPROC_COREDUMP_ENABLED)
0265             data_size += segment->size;
0266 
0267         phnum++;
0268     }
0269 
0270     data = vmalloc(data_size);
0271     if (!data)
0272         return;
0273 
0274     ehdr = data;
0275 
0276     memset(ehdr, 0, elf_size_of_hdr(class));
0277     /* e_ident field is common for both elf32 and elf64 */
0278     elf_hdr_init_ident(ehdr, class);
0279 
0280     elf_hdr_set_e_type(class, ehdr, ET_CORE);
0281     elf_hdr_set_e_machine(class, ehdr, rproc->elf_machine);
0282     elf_hdr_set_e_version(class, ehdr, EV_CURRENT);
0283     elf_hdr_set_e_entry(class, ehdr, rproc->bootaddr);
0284     elf_hdr_set_e_phoff(class, ehdr, elf_size_of_hdr(class));
0285     elf_hdr_set_e_ehsize(class, ehdr, elf_size_of_hdr(class));
0286     elf_hdr_set_e_phentsize(class, ehdr, elf_size_of_phdr(class));
0287     elf_hdr_set_e_phnum(class, ehdr, phnum);
0288 
0289     phdr = data + elf_hdr_get_e_phoff(class, ehdr);
0290     offset = elf_hdr_get_e_phoff(class, ehdr);
0291     offset += elf_size_of_phdr(class) * elf_hdr_get_e_phnum(class, ehdr);
0292 
0293     list_for_each_entry(segment, &rproc->dump_segments, node) {
0294         memset(phdr, 0, elf_size_of_phdr(class));
0295         elf_phdr_set_p_type(class, phdr, PT_LOAD);
0296         elf_phdr_set_p_offset(class, phdr, offset);
0297         elf_phdr_set_p_vaddr(class, phdr, segment->da);
0298         elf_phdr_set_p_paddr(class, phdr, segment->da);
0299         elf_phdr_set_p_filesz(class, phdr, segment->size);
0300         elf_phdr_set_p_memsz(class, phdr, segment->size);
0301         elf_phdr_set_p_flags(class, phdr, PF_R | PF_W | PF_X);
0302         elf_phdr_set_p_align(class, phdr, 0);
0303 
0304         if (dump_conf == RPROC_COREDUMP_ENABLED)
0305             rproc_copy_segment(rproc, data + offset, segment, 0,
0306                        segment->size);
0307 
0308         offset += elf_phdr_get_p_filesz(class, phdr);
0309         phdr += elf_size_of_phdr(class);
0310     }
0311     if (dump_conf == RPROC_COREDUMP_ENABLED) {
0312         dev_coredumpv(&rproc->dev, data, data_size, GFP_KERNEL);
0313         return;
0314     }
0315 
0316     /* Initialize the dump state struct to be used by rproc_coredump_read */
0317     dump_state.rproc = rproc;
0318     dump_state.header = data;
0319     init_completion(&dump_state.dump_done);
0320 
0321     dev_coredumpm(&rproc->dev, NULL, &dump_state, data_size, GFP_KERNEL,
0322               rproc_coredump_read, rproc_coredump_free);
0323 
0324     /*
0325      * Wait until the dump is read and free is called. Data is freed
0326      * by devcoredump framework automatically after 5 minutes.
0327      */
0328     wait_for_completion(&dump_state.dump_done);
0329 }
0330 
0331 /**
0332  * rproc_coredump_using_sections() - perform coredump using section headers
0333  * @rproc:  rproc handle
0334  *
0335  * This function will generate an ELF header for the registered sections of
0336  * segments and create a devcoredump device associated with rproc. Based on
0337  * the coredump configuration this function will directly copy the segments
0338  * from device memory to userspace or copy segments from device memory to
0339  * a separate buffer, which can then be read by userspace.
0340  * The first approach avoids using extra vmalloc memory. But it will stall
0341  * recovery flow until dump is read by userspace.
0342  */
0343 void rproc_coredump_using_sections(struct rproc *rproc)
0344 {
0345     struct rproc_dump_segment *segment;
0346     void *shdr;
0347     void *ehdr;
0348     size_t data_size;
0349     size_t strtbl_size = 0;
0350     size_t strtbl_index = 1;
0351     size_t offset;
0352     void *data;
0353     u8 class = rproc->elf_class;
0354     int shnum;
0355     struct rproc_coredump_state dump_state;
0356     unsigned int dump_conf = rproc->dump_conf;
0357     char *str_tbl = "STR_TBL";
0358 
0359     if (list_empty(&rproc->dump_segments) ||
0360         dump_conf == RPROC_COREDUMP_DISABLED)
0361         return;
0362 
0363     if (class == ELFCLASSNONE) {
0364         dev_err(&rproc->dev, "Elf class is not set\n");
0365         return;
0366     }
0367 
0368     /*
0369      * We allocate two extra section headers. The first one is null.
0370      * Second section header is for the string table. Also space is
0371      * allocated for string table.
0372      */
0373     data_size = elf_size_of_hdr(class) + 2 * elf_size_of_shdr(class);
0374     shnum = 2;
0375 
0376     /* the extra byte is for the null character at index 0 */
0377     strtbl_size += strlen(str_tbl) + 2;
0378 
0379     list_for_each_entry(segment, &rproc->dump_segments, node) {
0380         data_size += elf_size_of_shdr(class);
0381         strtbl_size += strlen(segment->priv) + 1;
0382         if (dump_conf == RPROC_COREDUMP_ENABLED)
0383             data_size += segment->size;
0384         shnum++;
0385     }
0386 
0387     data_size += strtbl_size;
0388 
0389     data = vmalloc(data_size);
0390     if (!data)
0391         return;
0392 
0393     ehdr = data;
0394     memset(ehdr, 0, elf_size_of_hdr(class));
0395     /* e_ident field is common for both elf32 and elf64 */
0396     elf_hdr_init_ident(ehdr, class);
0397 
0398     elf_hdr_set_e_type(class, ehdr, ET_CORE);
0399     elf_hdr_set_e_machine(class, ehdr, rproc->elf_machine);
0400     elf_hdr_set_e_version(class, ehdr, EV_CURRENT);
0401     elf_hdr_set_e_entry(class, ehdr, rproc->bootaddr);
0402     elf_hdr_set_e_shoff(class, ehdr, elf_size_of_hdr(class));
0403     elf_hdr_set_e_ehsize(class, ehdr, elf_size_of_hdr(class));
0404     elf_hdr_set_e_shentsize(class, ehdr, elf_size_of_shdr(class));
0405     elf_hdr_set_e_shnum(class, ehdr, shnum);
0406     elf_hdr_set_e_shstrndx(class, ehdr, 1);
0407 
0408     /*
0409      * The zeroth index of the section header is reserved and is rarely used.
0410      * Set the section header as null (SHN_UNDEF) and move to the next one.
0411      */
0412     shdr = data + elf_hdr_get_e_shoff(class, ehdr);
0413     memset(shdr, 0, elf_size_of_shdr(class));
0414     shdr += elf_size_of_shdr(class);
0415 
0416     /* Initialize the string table. */
0417     offset = elf_hdr_get_e_shoff(class, ehdr) +
0418          elf_size_of_shdr(class) * elf_hdr_get_e_shnum(class, ehdr);
0419     memset(data + offset, 0, strtbl_size);
0420 
0421     /* Fill in the string table section header. */
0422     memset(shdr, 0, elf_size_of_shdr(class));
0423     elf_shdr_set_sh_type(class, shdr, SHT_STRTAB);
0424     elf_shdr_set_sh_offset(class, shdr, offset);
0425     elf_shdr_set_sh_size(class, shdr, strtbl_size);
0426     elf_shdr_set_sh_entsize(class, shdr, 0);
0427     elf_shdr_set_sh_flags(class, shdr, 0);
0428     elf_shdr_set_sh_name(class, shdr, elf_strtbl_add(str_tbl, ehdr, class, &strtbl_index));
0429     offset += elf_shdr_get_sh_size(class, shdr);
0430     shdr += elf_size_of_shdr(class);
0431 
0432     list_for_each_entry(segment, &rproc->dump_segments, node) {
0433         memset(shdr, 0, elf_size_of_shdr(class));
0434         elf_shdr_set_sh_type(class, shdr, SHT_PROGBITS);
0435         elf_shdr_set_sh_offset(class, shdr, offset);
0436         elf_shdr_set_sh_addr(class, shdr, segment->da);
0437         elf_shdr_set_sh_size(class, shdr, segment->size);
0438         elf_shdr_set_sh_entsize(class, shdr, 0);
0439         elf_shdr_set_sh_flags(class, shdr, SHF_WRITE);
0440         elf_shdr_set_sh_name(class, shdr,
0441                      elf_strtbl_add(segment->priv, ehdr, class, &strtbl_index));
0442 
0443         /* No need to copy segments for inline dumps */
0444         if (dump_conf == RPROC_COREDUMP_ENABLED)
0445             rproc_copy_segment(rproc, data + offset, segment, 0,
0446                        segment->size);
0447         offset += elf_shdr_get_sh_size(class, shdr);
0448         shdr += elf_size_of_shdr(class);
0449     }
0450 
0451     if (dump_conf == RPROC_COREDUMP_ENABLED) {
0452         dev_coredumpv(&rproc->dev, data, data_size, GFP_KERNEL);
0453         return;
0454     }
0455 
0456     /* Initialize the dump state struct to be used by rproc_coredump_read */
0457     dump_state.rproc = rproc;
0458     dump_state.header = data;
0459     init_completion(&dump_state.dump_done);
0460 
0461     dev_coredumpm(&rproc->dev, NULL, &dump_state, data_size, GFP_KERNEL,
0462               rproc_coredump_read, rproc_coredump_free);
0463 
0464     /* Wait until the dump is read and free is called. Data is freed
0465      * by devcoredump framework automatically after 5 minutes.
0466      */
0467     wait_for_completion(&dump_state.dump_done);
0468 }
0469 EXPORT_SYMBOL(rproc_coredump_using_sections);