Loading drivers/soc/qcom/ramdump.c +161 −1 Original line number Original line Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only // SPDX-License-Identifier: GPL-2.0-only /* /* * Copyright (c) 2011-2019, The Linux Foundation. All rights reserved. * Copyright (c) 2011-2020, The Linux Foundation. All rights reserved. */ */ #include <linux/kernel.h> #include <linux/kernel.h> Loading Loading @@ -644,3 +644,163 @@ do_elf_ramdump(void *handle, struct ramdump_segment *segments, int nsegments) return _do_ramdump(handle, segments, nsegments, true); return _do_ramdump(handle, segments, nsegments, true); } } EXPORT_SYMBOL(do_elf_ramdump); EXPORT_SYMBOL(do_elf_ramdump); int do_dump(struct list_head *segs, struct device *dev) { struct qcom_dump_segment *segment; void *data; void __iomem *ptr; size_t data_size = 0; size_t offset = 0; if (!segs || list_empty(segs)) return -EINVAL; list_for_each_entry(segment, segs, node) { pr_info("Got segment size %d\n", segment->size); data_size += segment->size; } data = vmalloc(data_size); if (!data) return -ENOMEM; list_for_each_entry(segment, segs, node) { if (segment->va) memcpy(data + offset, segment->va, segment->size); else { ptr = devm_ioremap(dev, segment->da, segment->size); if (!ptr) { dev_err(dev, "invalid coredump segment (%pad, %zu)\n", &segment->da, segment->size); memset(data + offset, 0xff, segment->size); } else memcpy_fromio(data + offset, ptr, segment->size); } offset += segment->size; } dev_coredumpv(dev, data, data_size, GFP_KERNEL); return 0; } EXPORT_SYMBOL(do_dump); int do_elf_dump(struct list_head *segs, struct device *dev) { struct qcom_dump_segment *segment; struct elf32_phdr *phdr; struct elf32_hdr *ehdr; size_t data_size; size_t offset; int phnum = 0; void *data; void __iomem *ptr; if (!segs || list_empty(segs)) return -EINVAL; data_size = sizeof(*ehdr); list_for_each_entry(segment, segs, node) { data_size += sizeof(*phdr) + segment->size; phnum++; } data = vmalloc(data_size); if (!data) return -ENOMEM; pr_debug("Creating elf with size %d\n", data_size); ehdr = data; memset(ehdr, 0, sizeof(*ehdr)); memcpy(ehdr->e_ident, ELFMAG, SELFMAG); ehdr->e_ident[EI_CLASS] = ELFCLASS32; ehdr->e_ident[EI_DATA] = ELFDATA2LSB; ehdr->e_ident[EI_VERSION] = EV_CURRENT; ehdr->e_ident[EI_OSABI] = ELFOSABI_NONE; ehdr->e_type = ET_CORE; ehdr->e_machine = EM_NONE; ehdr->e_version = EV_CURRENT; ehdr->e_entry = 0; ehdr->e_phoff = sizeof(*ehdr); ehdr->e_ehsize = sizeof(*ehdr); ehdr->e_phentsize = sizeof(*phdr); ehdr->e_phnum = phnum; phdr = data + ehdr->e_phoff; offset = ehdr->e_phoff + sizeof(*phdr) * ehdr->e_phnum; list_for_each_entry(segment, segs, node) { memset(phdr, 0, sizeof(*phdr)); phdr->p_type = PT_LOAD; phdr->p_offset = offset; phdr->p_vaddr = segment->da; phdr->p_paddr = segment->da; phdr->p_filesz = segment->size; phdr->p_memsz = segment->size; phdr->p_flags = PF_R | PF_W | PF_X; phdr->p_align = 0; if (segment->va) memcpy(data + offset, segment->va, segment->size); else { ptr = devm_ioremap(dev, segment->da, segment->size); if (!ptr) { dev_err(dev, "invalid coredump segment (%pad, %zu)\n", &segment->da, segment->size); memset(data + offset, 0xff, segment->size); } else memcpy_fromio(data + offset, ptr, segment->size); } offset += phdr->p_filesz; phdr++; } dev_coredumpv(dev, data, data_size, GFP_KERNEL); return 0; } EXPORT_SYMBOL(do_elf_dump); int do_fw_elf_dump(struct firmware *fw, struct device *dev) { const struct elf32_phdr *phdrs, *phdr; const struct elf32_hdr *ehdr; struct qcom_dump_segment *segment; struct list_head head; int i; ehdr = (struct elf32_hdr *)fw->data; phdrs = (struct elf32_phdr *)(ehdr + 1); INIT_LIST_HEAD(&head); for (i = 0; i < ehdr->e_phnum; i++) { phdr = &phdrs[i]; if (phdr->p_type != PT_LOAD) continue; if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH) continue; if (!phdr->p_memsz) continue; segment = kzalloc(sizeof(*segment), GFP_KERNEL); if (!segment) return -ENOMEM; segment->da = phdr->p_paddr; segment->size = phdr->p_memsz; list_add_tail(&segment->node, &head); } do_elf_dump(&head, dev); return 0; } EXPORT_SYMBOL(do_fw_elf_dump); include/soc/qcom/ramdump.h +16 −1 Original line number Original line Diff line number Diff line /* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */ /* /* * Copyright (c) 2011-2014, 2017-2019, The Linux Foundation. All rights reserved. * Copyright (c) 2011-2014, 2017-2020, The Linux Foundation. All rights reserved. */ */ #ifndef _RAMDUMP_HEADER #ifndef _RAMDUMP_HEADER #define _RAMDUMP_HEADER #define _RAMDUMP_HEADER #include <linux/kernel.h> #include <linux/firmware.h> #include <linux/devcoredump.h> #include <linux/soc/qcom/mdt_loader.h> struct device; struct device; Loading @@ -15,6 +19,17 @@ struct ramdump_segment { unsigned long size; unsigned long size; }; }; struct qcom_dump_segment { struct list_head node; dma_addr_t da; void *va; size_t size; }; extern int do_elf_dump(struct list_head *segs, struct device *dev); extern int do_dump(struct list_head *head, struct device *dev); extern int do_fw_elf_dump(struct firmware *fw, struct device *dev); #if IS_ENABLED(CONFIG_MSM_SUBSYSTEM_RESTART) #if IS_ENABLED(CONFIG_MSM_SUBSYSTEM_RESTART) extern void *create_ramdump_device(const char *dev_name, struct device *parent); extern void *create_ramdump_device(const char *dev_name, struct device *parent); extern void destroy_ramdump_device(void *dev); extern void destroy_ramdump_device(void *dev); Loading Loading
drivers/soc/qcom/ramdump.c +161 −1 Original line number Original line Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only // SPDX-License-Identifier: GPL-2.0-only /* /* * Copyright (c) 2011-2019, The Linux Foundation. All rights reserved. * Copyright (c) 2011-2020, The Linux Foundation. All rights reserved. */ */ #include <linux/kernel.h> #include <linux/kernel.h> Loading Loading @@ -644,3 +644,163 @@ do_elf_ramdump(void *handle, struct ramdump_segment *segments, int nsegments) return _do_ramdump(handle, segments, nsegments, true); return _do_ramdump(handle, segments, nsegments, true); } } EXPORT_SYMBOL(do_elf_ramdump); EXPORT_SYMBOL(do_elf_ramdump); int do_dump(struct list_head *segs, struct device *dev) { struct qcom_dump_segment *segment; void *data; void __iomem *ptr; size_t data_size = 0; size_t offset = 0; if (!segs || list_empty(segs)) return -EINVAL; list_for_each_entry(segment, segs, node) { pr_info("Got segment size %d\n", segment->size); data_size += segment->size; } data = vmalloc(data_size); if (!data) return -ENOMEM; list_for_each_entry(segment, segs, node) { if (segment->va) memcpy(data + offset, segment->va, segment->size); else { ptr = devm_ioremap(dev, segment->da, segment->size); if (!ptr) { dev_err(dev, "invalid coredump segment (%pad, %zu)\n", &segment->da, segment->size); memset(data + offset, 0xff, segment->size); } else memcpy_fromio(data + offset, ptr, segment->size); } offset += segment->size; } dev_coredumpv(dev, data, data_size, GFP_KERNEL); return 0; } EXPORT_SYMBOL(do_dump); int do_elf_dump(struct list_head *segs, struct device *dev) { struct qcom_dump_segment *segment; struct elf32_phdr *phdr; struct elf32_hdr *ehdr; size_t data_size; size_t offset; int phnum = 0; void *data; void __iomem *ptr; if (!segs || list_empty(segs)) return -EINVAL; data_size = sizeof(*ehdr); list_for_each_entry(segment, segs, node) { data_size += sizeof(*phdr) + segment->size; phnum++; } data = vmalloc(data_size); if (!data) return -ENOMEM; pr_debug("Creating elf with size %d\n", data_size); ehdr = data; memset(ehdr, 0, sizeof(*ehdr)); memcpy(ehdr->e_ident, ELFMAG, SELFMAG); ehdr->e_ident[EI_CLASS] = ELFCLASS32; ehdr->e_ident[EI_DATA] = ELFDATA2LSB; ehdr->e_ident[EI_VERSION] = EV_CURRENT; ehdr->e_ident[EI_OSABI] = ELFOSABI_NONE; ehdr->e_type = ET_CORE; ehdr->e_machine = EM_NONE; ehdr->e_version = EV_CURRENT; ehdr->e_entry = 0; ehdr->e_phoff = sizeof(*ehdr); ehdr->e_ehsize = sizeof(*ehdr); ehdr->e_phentsize = sizeof(*phdr); ehdr->e_phnum = phnum; phdr = data + ehdr->e_phoff; offset = ehdr->e_phoff + sizeof(*phdr) * ehdr->e_phnum; list_for_each_entry(segment, segs, node) { memset(phdr, 0, sizeof(*phdr)); phdr->p_type = PT_LOAD; phdr->p_offset = offset; phdr->p_vaddr = segment->da; phdr->p_paddr = segment->da; phdr->p_filesz = segment->size; phdr->p_memsz = segment->size; phdr->p_flags = PF_R | PF_W | PF_X; phdr->p_align = 0; if (segment->va) memcpy(data + offset, segment->va, segment->size); else { ptr = devm_ioremap(dev, segment->da, segment->size); if (!ptr) { dev_err(dev, "invalid coredump segment (%pad, %zu)\n", &segment->da, segment->size); memset(data + offset, 0xff, segment->size); } else memcpy_fromio(data + offset, ptr, segment->size); } offset += phdr->p_filesz; phdr++; } dev_coredumpv(dev, data, data_size, GFP_KERNEL); return 0; } EXPORT_SYMBOL(do_elf_dump); int do_fw_elf_dump(struct firmware *fw, struct device *dev) { const struct elf32_phdr *phdrs, *phdr; const struct elf32_hdr *ehdr; struct qcom_dump_segment *segment; struct list_head head; int i; ehdr = (struct elf32_hdr *)fw->data; phdrs = (struct elf32_phdr *)(ehdr + 1); INIT_LIST_HEAD(&head); for (i = 0; i < ehdr->e_phnum; i++) { phdr = &phdrs[i]; if (phdr->p_type != PT_LOAD) continue; if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH) continue; if (!phdr->p_memsz) continue; segment = kzalloc(sizeof(*segment), GFP_KERNEL); if (!segment) return -ENOMEM; segment->da = phdr->p_paddr; segment->size = phdr->p_memsz; list_add_tail(&segment->node, &head); } do_elf_dump(&head, dev); return 0; } EXPORT_SYMBOL(do_fw_elf_dump);
include/soc/qcom/ramdump.h +16 −1 Original line number Original line Diff line number Diff line /* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */ /* /* * Copyright (c) 2011-2014, 2017-2019, The Linux Foundation. All rights reserved. * Copyright (c) 2011-2014, 2017-2020, The Linux Foundation. All rights reserved. */ */ #ifndef _RAMDUMP_HEADER #ifndef _RAMDUMP_HEADER #define _RAMDUMP_HEADER #define _RAMDUMP_HEADER #include <linux/kernel.h> #include <linux/firmware.h> #include <linux/devcoredump.h> #include <linux/soc/qcom/mdt_loader.h> struct device; struct device; Loading @@ -15,6 +19,17 @@ struct ramdump_segment { unsigned long size; unsigned long size; }; }; struct qcom_dump_segment { struct list_head node; dma_addr_t da; void *va; size_t size; }; extern int do_elf_dump(struct list_head *segs, struct device *dev); extern int do_dump(struct list_head *head, struct device *dev); extern int do_fw_elf_dump(struct firmware *fw, struct device *dev); #if IS_ENABLED(CONFIG_MSM_SUBSYSTEM_RESTART) #if IS_ENABLED(CONFIG_MSM_SUBSYSTEM_RESTART) extern void *create_ramdump_device(const char *dev_name, struct device *parent); extern void *create_ramdump_device(const char *dev_name, struct device *parent); extern void destroy_ramdump_device(void *dev); extern void destroy_ramdump_device(void *dev); Loading