Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a543b4c8 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "drivers: soc: Add devcoredump based ramdump APIs"

parents 888edf7b 11af5827
Loading
Loading
Loading
Loading
+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>
@@ -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);
+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;


@@ -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);