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

Unverified Commit 070bf821 authored by derfelot's avatar derfelot
Browse files

misc: Add ramdump driver from Sony kernel

Taken from Sony 47.2.A.10.107 stock kernel
parent f4d7d13e
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
Ramdump Debug Memory

Debug memory contains necessary information for ramdump to dump
the memory.This memory is used as a bridge between Android and
ramdump.

This memory is used by the modules mentioned below:
a) Rdtags
b) Ramdump Memory Descriptors


Required Properties:
-compatible: "removed-dma-pool", "qcom,debug_memory"
-reg: Specifies the physical address of the debug memory region and its size

Example:
	debug_region: debug_region@57e00000 {
		compatible = "removed-dma-pool", "qcom,debug_memory";
		no-map;
		reg = <0 0x57e00000 0 0x100000>;
		label = "debug_mem";
	};
+22 −0
Original line number Diff line number Diff line
@@ -583,6 +583,28 @@ config MEMORY_STATE_TIME
	help
	  Memory time statistics exported to /sys/kernel/memory_state_time

config RAMDUMP_TAGS
	bool "Ramdump tags to communicate between kernel and ramdump"
	default n
	depends on !RAMDUMP_CRASH_LOGS
	help
	  This option enables a driver which can be interacted with to
	  supply the ramdumper with more information about the current
	  state of the crashing kernel such as build info, HWWD registers,
	  additional CPU registers not stored in crash_notes, crashing
	  application info etc. A small amount of memory will be set
	  aside to hold the information which will later be used by
	  the ramdump application.

config RAMDUMP_MEMDESC
	bool "Ramdump memory descriptors"
	default n
	help
	  This is a ramdump_mem_desc driver which adds the
	  memory descriptors in Normal mode into a piece of debug memory
	  and split the contents and exports it as /proc/mem_desc
	  in Ramdump mode to support ramdump functionality

config NFC_PN553_DEVICES
	tristate "Nxp pn553 NCI protocol driver (I2C) devices"
	default y
+2 −0
Original line number Diff line number Diff line
@@ -70,4 +70,6 @@ obj-y += qcom/
obj-$(CONFIG_QPNP_MISC) 	+= qpnp-misc.o
obj-$(CONFIG_MEMORY_STATE_TIME) += memory_state_time.o
obj-$(CONFIG_LDO_VIBRATOR) += ldo_vibrator.o
obj-$(CONFIG_RAMDUMP_TAGS) += rdtags.o
obj-$(CONFIG_RAMDUMP_MEMDESC) += ramdump_mem_desc.o
obj-$(CONFIG_MMTUNER_MN8855x) += mm_tuner/
+515 −0
Original line number Diff line number Diff line
/*
 * Author: Nandhakumar Rangasamy<nandhakumar.x.rangasamy@sonymobile.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2, as
 * published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 */
/*
 * Copyright (C) 2015 Sony Mobile Communications Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2, as
 * published by the Free Software Foundation.
 */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/of_fdt.h>
#include <linux/string.h>
#include <asm/setup.h>
#include <linux/delay.h>
#include <linux/ramdump_mem_desc.h>
#include <linux/sort.h>
#include <linux/genhd.h>
#include <linux/device.h>
#include <linux/vmalloc.h>

#ifdef CONFIG_HAVE_MEMBLOCK
#include <linux/memblock.h>
#endif

static struct device *dev;
static void *mem_desc_base;
static size_t mem_desc_size;
static char *mem_desc_buf;
static unsigned int mem_desc_data_size;

static DEFINE_MUTEX(mem_desc_lock);

/*Random number*/
#define RDTAGS_MEM_DESC_SIG 0x42972468
#define DUMP_TABLE_OFFSET 0x5014
#define MEM_DESC_MAX 64
#define MEM_DESC_MASK 0xf

struct mem_desc_hdr {
	u32 sig;
	u32 version;
	u32 num_desc;
	u32 reserved;
};

struct ramdump_mem_desc {
	struct mem_desc_hdr hdr;
	struct mem_desc desc[MEM_DESC_MAX];
};

#define MEM_DESC_FORMAT_SIZE 114
#define MEM_DESC_FORMAT "0x%016llx:0x%016llx:%s:%s:0x%08x\n"

static int match_dev_by_volname(struct device *pdev, const void *data)
{
	char *volname = "cache";
	struct hd_struct *part = dev_to_part(pdev);

	if (!part->info)
		goto no_match;

	if (strncmp(volname, part->info->volname,
			sizeof(part->info->volname)))
		goto no_match;

	return 1;
no_match:
	return 0;
}

static int get_ramdump_partition_index(void)
{
	struct device *pdev = NULL;
	u8 uuid[16] = {0};
	int ret = -1;

	pdev = class_find_device(&block_class, NULL, &uuid,
					match_dev_by_volname);
	if (!pdev) {
		dev_info(dev, "Partition device not found");
		return ret;
	}

	return dev_to_part(pdev)->partno;
}

static u32 mem_desc_update_flags(u64 start, u64 end,
			struct ramdump_mem_desc *m_desc)
{
	int i, ret;
	u32 flags;

	ret = get_ramdump_partition_index();
	if (ret < 0)
		return 0;

	flags = ret << 9;
	for (i = 0; i < m_desc->hdr.num_desc; i++) {
		if (start >= m_desc->desc[i].phys_addr &&
			(end <= (m_desc->desc[i].phys_addr
				+ m_desc->desc[i].size)))
				flags |= m_desc->desc[i].flags;
	}

	return flags;
}

static void mem_desc_split_sect_format(
				u64 sort_buffer[],
				unsigned int sort_count,
				struct ramdump_mem_desc *m_desc)
{
	unsigned int i, j;

	for (j = 0; j < sort_count; j++) {
		u32 flags = 0; u64 size = 0;
		char *p_name = NULL, *c_name = NULL;

		if ((j+1) < sort_count) {
			if (sort_buffer[j] == sort_buffer[j+1])
				continue;
		}

		for (i = 0; i < m_desc->hdr.num_desc; i++) {
			if (sort_buffer[j] >= m_desc->desc[i].phys_addr &&
				(sort_buffer[j] < (m_desc->desc[i].phys_addr
					+ m_desc->desc[i].size))) {

				size = sort_buffer[j + 1] - sort_buffer[j];
				flags = mem_desc_update_flags(sort_buffer[j],
						sort_buffer[j+1],
						m_desc);

				switch (m_desc->desc[i].flags & MEM_DESC_MASK) {
				case MEM_DESC_PLATFORM:
					if (p_name == NULL)
						p_name = m_desc->desc[i].name;
					else if ((!strncmp(p_name,
						m_desc->desc[i].name,
						sizeof(m_desc->desc[i].name)))
						&&
						(m_desc->desc[i].flags >> 31))
						continue;
					else {
						mem_desc_data_size += snprintf(
							(mem_desc_buf +
							mem_desc_data_size),
							MEM_DESC_FORMAT_SIZE,
							MEM_DESC_FORMAT,
							sort_buffer[j],
							size,
							m_desc->desc[i].name,
							"NULL",
							flags);
					}
					break;
				case MEM_DESC_CORE:
					c_name = m_desc->desc[i].name;
					break;
				}
			}
		}
		if (p_name != NULL) {
			mem_desc_data_size += snprintf(
					(mem_desc_buf +
					mem_desc_data_size),
					MEM_DESC_FORMAT_SIZE,
					MEM_DESC_FORMAT,
					sort_buffer[j],
					size,
					p_name,
					c_name ? c_name : "NULL",
					flags);
		}
	}
}

void ramdump_add_mem_desc(struct mem_desc *desc)
{
	struct ramdump_mem_desc *m_desc = NULL;
	unsigned int offset;

	if (!mem_desc_base) {
		dev_info(dev, "Adding mem_desc failed\n");
		return;
	}

	mutex_lock(&mem_desc_lock);
	m_desc = (struct ramdump_mem_desc *)mem_desc_base;
	offset = sizeof(struct mem_desc_hdr) +
		m_desc->hdr.num_desc * sizeof(struct mem_desc);

	if (m_desc->hdr.num_desc >= MEM_DESC_MAX)
		goto exit;

	if ((offset + sizeof(struct mem_desc)) > mem_desc_size)
		goto exit;

	memcpy_toio(
		((struct ramdump_mem_desc *)(((unsigned long)m_desc) + offset)),
		desc, sizeof(struct mem_desc));

	m_desc->hdr.num_desc++;
exit:
	mutex_unlock(&mem_desc_lock);
}
EXPORT_SYMBOL(ramdump_add_mem_desc);

void ramdump_remove_mem_desc(struct mem_desc *desc)
{
	struct ramdump_mem_desc *m_desc = NULL;
	struct ramdump_mem_desc *m_desc_buf = NULL;
	unsigned int i, offset, m_desc_size;

	if (!mem_desc_base) {
		dev_info(dev, "Removing mem_desc failed\n");
		return;
	}

	mutex_lock(&mem_desc_lock);
	m_desc = (struct ramdump_mem_desc *)mem_desc_base;
	m_desc_size = sizeof(struct mem_desc_hdr) +
	m_desc->hdr.num_desc * sizeof(struct mem_desc);

	m_desc_buf = vmalloc(m_desc_size);
	if (!m_desc_buf) {
		dev_err(dev, "Failed to allocate mem_desc buffer\n");
		goto exit;
	}

	memcpy_fromio(m_desc_buf, mem_desc_base, m_desc_size);
	offset = sizeof(struct mem_desc_hdr) +
	m_desc_buf->hdr.num_desc * sizeof(struct mem_desc);

	for (i = 0; i < m_desc_buf->hdr.num_desc; i++) {
		if ((desc->phys_addr == m_desc_buf->desc[i].phys_addr) &&
				!strncmp(desc->name, m_desc_buf->desc[i].name,
				sizeof(m_desc_buf->desc[i].name))) {
			memset(&m_desc_buf->desc[i], 0,
					sizeof(struct mem_desc));
			memmove(&m_desc_buf->desc[i],
				&m_desc_buf->desc[i + 1],
				(m_desc_buf->hdr.num_desc - i - 1)
				* sizeof(struct mem_desc));
			m_desc_buf->hdr.num_desc--;
		}
	}

	memset_io(m_desc, 0, m_desc_size);
	memcpy_toio(m_desc, m_desc_buf, m_desc_size);
	vfree(m_desc_buf);
exit:
	mutex_unlock(&mem_desc_lock);
}
EXPORT_SYMBOL(ramdump_remove_mem_desc);

static int cmp_sort_addr(const void *a, const void *b)
{
	u64 x = *(u64 *)a;
	u64 y = *(u64 *)b;
	int ret = 0;

	if (x < y)
		ret = -1;
	if (x > y)
		ret = 1;

	return ret;
}

static void get_mem_desc(void)
{
	unsigned int count = 0;
	struct ramdump_mem_desc *m_desc =
		(struct ramdump_mem_desc *)mem_desc_base;
	unsigned int i, num_desc;

	num_desc = (m_desc->hdr.num_desc < MEM_DESC_MAX) ?
			m_desc->hdr.num_desc : MEM_DESC_MAX;

	if (m_desc->hdr.sig == RDTAGS_MEM_DESC_SIG) {
		u64 sort_buffer[MEM_DESC_MAX << 1];
		for (i = 0; i < num_desc; i++) {
			u64 start, end;
			start = m_desc->desc[i].phys_addr;
			end = m_desc->desc[i].phys_addr + m_desc->desc[i].size;

			memcpy(&sort_buffer[count++], &start,
					sizeof(u64));
			memcpy(&sort_buffer[count++], &end,
					sizeof(u64));
		}

		sort(sort_buffer, count, sizeof(u64),
						cmp_sort_addr, NULL);
		mem_desc_split_sect_format(sort_buffer, count, m_desc);
	}
}

#ifdef CONFIG_HAVE_MEMBLOCK
static void add_memblock_info(void)
{
	int i;
	struct memblock_type *block = &memblock.memory;

	for (i = 0; i < block->cnt; i++) {
		struct mem_desc desc;

		desc.phys_addr = (u64)block->regions[i].base;
		desc.size = (u64)block->regions[i].size;
		desc.flags = MEM_DESC_CORE;
		strlcpy(desc.name, "vmcore", sizeof(desc.name));
		ramdump_add_mem_desc(&desc);
	}
}
#else
static void add_ioresource(struct resource *res,
				char *iores_name,
				char *core_name)
{
	while (res != NULL) {
		if (res->child != NULL)
			add_ioresource(res->child, iores_name, core_name);

		if ((res->flags & IORESOURCE_MEM) &&
			strcmp(res->name, iores_name) == 0) {
			if (core_name != NULL) {
				struct mem_desc desc;

				desc.phys_addr = (u64)res->start;
				desc.size =
					((u64)res->end - (u64)res->start) + 1;
				desc.flags = MEM_DESC_CORE;
				strlcpy(desc.name, core_name,
						sizeof(desc.name));
				ramdump_add_mem_desc(&desc);
			}
		}
		res = res->sibling;
	}
}
#endif

static void mem_desc_add_linux_meminfo(void)
{
#ifdef CONFIG_HAVE_MEMBLOCK
	add_memblock_info();
#else
	add_ioresource(&iomem_resource, "System RAM", "vmcore");
#endif
}

static ssize_t mem_desc_read(struct file *file, char __user *buf,
				size_t len, loff_t *offset)
{
	loff_t pos = *offset;
	ssize_t count;

	if (pos == 0)
		get_mem_desc();

	if (pos >= mem_desc_data_size) {
		memset_io(mem_desc_base, 0x0, mem_desc_data_size);
		return 0;
	}

	count = min(len, (size_t)(mem_desc_data_size - pos));
	if (copy_to_user(buf, mem_desc_buf + pos, count))
		return -EFAULT;

	*offset += count;
	return count;
}

static const struct file_operations mem_desc_file_ops = {
	.owner = THIS_MODULE,
	.read = mem_desc_read,
};

#define MAX_DESC_PER_TYPE 32
static int mem_desc_init(void)
{
	int ret = 0;
	struct proc_dir_entry *subentry_mem_desc;

	mem_desc_buf = kmalloc((MEM_DESC_FORMAT_SIZE * 3 * MAX_DESC_PER_TYPE),
			GFP_KERNEL);
	if (mem_desc_buf == NULL) {
		dev_err(dev, "Failed to allocte memory regs_buf\n");
		ret = -ENOMEM;
		goto exit;
	}

	subentry_mem_desc = proc_create_data("mem_desc",
				S_IFREG | S_IRUGO, NULL,
				&mem_desc_file_ops, NULL);
	if (!subentry_mem_desc) {
		dev_err(dev, "Failed to create proc subentry mem_desc\n");
		ret = -1;
		goto exit1;
	}

	return ret;

exit1:
	kfree(mem_desc_buf);
	mem_desc_buf = NULL;
exit:
	return ret;
}

static int is_ramdump_mode(void)
{
	return *((int *)(dev->platform_data));
}

static int mem_desc_driver_probe(struct platform_device *pdev)
{
	struct resource *res_mem_desc;
	struct ramdump_mem_desc *m_desc;
	void *mem_desc_end;
	int ret = 0;

	dev = &pdev->dev;

	res_mem_desc = platform_get_resource_byname(pdev, IORESOURCE_MEM,
					   "ramdump_memdesc");
	if (!res_mem_desc || !res_mem_desc->start) {
		dev_err(dev,
			"Ramdump tags driver mem desc resource"
				" invalid/absent\n");
		ret = -ENODEV;
		goto exit;
	}

	mem_desc_size = res_mem_desc->end
				- res_mem_desc->start + 1;
	mem_desc_base = (void *)ioremap(res_mem_desc->start,
						mem_desc_size);
	if (!mem_desc_base) {
		dev_err(dev, "Failed to mem desc map %ld bytes at 0x%08llx\n",
				mem_desc_size, res_mem_desc->start);
		ret = -EINVAL;
		goto exit;
	}

	mem_desc_end = mem_desc_base + mem_desc_size;
	dev_info(dev, "res_mem_desc_base = 0x%08llx mem_desc size = %ld\n",
			res_mem_desc->start, mem_desc_size);
	m_desc = (struct ramdump_mem_desc *)mem_desc_base;

	dev_info(dev, "ramdump_mode = %s\n",
		is_ramdump_mode() ? "ramdump" : "normal");

	if (!is_ramdump_mode()) {
		memset_io(mem_desc_base, 0x0, mem_desc_size);
		m_desc->hdr.sig = RDTAGS_MEM_DESC_SIG;
		mem_desc_add_linux_meminfo();
	} else {
		if (m_desc->hdr.sig == RDTAGS_MEM_DESC_SIG) {
			dev_info(dev, "Found memory descriptors\n");
			if (mem_desc_init() != 0)
				goto exit;
		} else {
			dev_info(dev, "NO valid memory descriptors found!!\n");
			ret = -EINVAL;
			goto exit;
		}
	}

	return 0;
exit:
	if (mem_desc_base) {
		iounmap(mem_desc_base);
		mem_desc_base = NULL;
	}

	return ret;
}

static struct platform_driver mem_desc_driver = {
	.probe = mem_desc_driver_probe,
	.driver	= {
		.name = "ramdump_memdesc",
	},
};

static int __init mem_desc_core_init(void)
{
	return platform_driver_register(&mem_desc_driver);
}

static void __exit mem_desc_module_exit(void)
{
	if (mem_desc_base) {
		iounmap(mem_desc_base);
		mem_desc_base = NULL;
	}

	platform_driver_unregister(&mem_desc_driver);
}

core_initcall(mem_desc_core_init);
module_exit(mem_desc_module_exit);

drivers/misc/rdtags.c

0 → 100644
+1176 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading