mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-13 16:16:15 -04:00
soc: qcom: Dump cpu registers content during panic
Add minidump entry and dump memory around addresses contained in registers in panic. Change-Id: Id4857e3b08838838d8aa3a51cb9d6961be37b579 Signed-off-by: Neeraj Upadhyay <neeraju@codeaurora.org>
This commit is contained in:
parent
f8a6a88e57
commit
831a64e11d
2 changed files with 233 additions and 21 deletions
|
|
@ -502,6 +502,13 @@ config QCOM_MINIDUMP_PANIC_DUMP
|
|||
This enables various dumps collection in minidump table,
|
||||
on panic.
|
||||
|
||||
config QCOM_MINIDUMP_PANIC_CPU_CONTEXT
|
||||
bool "QCOM Minidump Panic dumps Support"
|
||||
depends on ARM64 && QCOM_MINIDUMP_PANIC_DUMP
|
||||
help
|
||||
This enables cpu context collection in minidump table,
|
||||
on panic.
|
||||
|
||||
config MINIDUMP_MAX_ENTRIES
|
||||
int "Minidump Maximum num of entries"
|
||||
default 200
|
||||
|
|
|
|||
|
|
@ -36,6 +36,12 @@
|
|||
#include <asm/memory.h>
|
||||
|
||||
#include "../../../kernel/sched/sched.h"
|
||||
|
||||
#include <linux/kdebug.h>
|
||||
#include <linux/thread_info.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/percpu.h>
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_QCOM_DYN_MINIDUMP_STACK
|
||||
|
|
@ -78,11 +84,20 @@ static size_t md_ftrace_buf_current;
|
|||
#endif
|
||||
|
||||
#ifdef CONFIG_QCOM_MINIDUMP_PANIC_DUMP
|
||||
/* Rnqueue information */
|
||||
#define MD_RUNQUEUE_PAGES 8
|
||||
|
||||
static bool md_in_oops_handler;
|
||||
static struct seq_buf *md_runq_seq_buf;
|
||||
static md_align_offset;
|
||||
|
||||
/* CPU context information */
|
||||
#ifdef CONFIG_QCOM_MINIDUMP_PANIC_CPU_CONTEXT
|
||||
#define MD_CPU_CNTXT_PAGES 32
|
||||
|
||||
static int die_cpu = -1;
|
||||
static struct seq_buf *md_cntxt_seq_buf;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static void __init register_log_buf(void)
|
||||
|
|
@ -734,12 +749,175 @@ static void md_dump_runqueues(void)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_QCOM_MINIDUMP_PANIC_CPU_CONTEXT
|
||||
/*
|
||||
* dump a block of kernel memory from around the given address.
|
||||
* Bulk of the code is lifted from arch/arm64/kernel/proccess.c.
|
||||
*/
|
||||
static void md_dump_data(unsigned long addr, int nbytes, const char *name)
|
||||
{
|
||||
int i, j;
|
||||
int nlines;
|
||||
u32 *p;
|
||||
|
||||
/*
|
||||
* don't attempt to dump non-kernel addresses or
|
||||
* values that are probably just small negative numbers
|
||||
*/
|
||||
if (addr < PAGE_OFFSET || addr > -256UL)
|
||||
return;
|
||||
|
||||
seq_buf_printf(md_cntxt_seq_buf, "\n%s: %#lx:\n", name, addr);
|
||||
|
||||
/*
|
||||
* round address down to a 32 bit boundary
|
||||
* and always dump a multiple of 32 bytes
|
||||
*/
|
||||
p = (u32 *)(addr & ~(sizeof(u32) - 1));
|
||||
nbytes += (addr & (sizeof(u32) - 1));
|
||||
nlines = (nbytes + 31) / 32;
|
||||
|
||||
|
||||
for (i = 0; i < nlines; i++) {
|
||||
/*
|
||||
* just display low 16 bits of address to keep
|
||||
* each line of the dump < 80 characters
|
||||
*/
|
||||
seq_buf_printf(md_cntxt_seq_buf, "%04lx ",
|
||||
(unsigned long)p & 0xffff);
|
||||
for (j = 0; j < 8; j++) {
|
||||
u32 data;
|
||||
|
||||
if (probe_kernel_address(p, data))
|
||||
seq_buf_printf(md_cntxt_seq_buf, " ********");
|
||||
else
|
||||
seq_buf_printf(md_cntxt_seq_buf, " %08x", data);
|
||||
++p;
|
||||
}
|
||||
seq_buf_printf(md_cntxt_seq_buf, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void md_reg_context_data(struct pt_regs *regs)
|
||||
{
|
||||
mm_segment_t fs;
|
||||
unsigned int i;
|
||||
int nbytes = 128;
|
||||
|
||||
if (user_mode(regs) || !regs->pc)
|
||||
return;
|
||||
|
||||
fs = get_fs();
|
||||
set_fs(KERNEL_DS);
|
||||
md_dump_data(regs->pc - nbytes, nbytes * 2, "PC");
|
||||
md_dump_data(regs->regs[30] - nbytes, nbytes * 2, "LR");
|
||||
md_dump_data(regs->sp - nbytes, nbytes * 2, "SP");
|
||||
for (i = 0; i < 30; i++) {
|
||||
char name[4];
|
||||
|
||||
snprintf(name, sizeof(name), "X%u", i);
|
||||
md_dump_data(regs->regs[i] - nbytes, nbytes * 2, name);
|
||||
}
|
||||
set_fs(fs);
|
||||
}
|
||||
|
||||
static inline void md_dump_panic_regs(void)
|
||||
{
|
||||
struct pt_regs regs;
|
||||
u64 tmp1, tmp2;
|
||||
|
||||
/* Lifted from crash_setup_regs() */
|
||||
__asm__ __volatile__ (
|
||||
"stp x0, x1, [%2, #16 * 0]\n"
|
||||
"stp x2, x3, [%2, #16 * 1]\n"
|
||||
"stp x4, x5, [%2, #16 * 2]\n"
|
||||
"stp x6, x7, [%2, #16 * 3]\n"
|
||||
"stp x8, x9, [%2, #16 * 4]\n"
|
||||
"stp x10, x11, [%2, #16 * 5]\n"
|
||||
"stp x12, x13, [%2, #16 * 6]\n"
|
||||
"stp x14, x15, [%2, #16 * 7]\n"
|
||||
"stp x16, x17, [%2, #16 * 8]\n"
|
||||
"stp x18, x19, [%2, #16 * 9]\n"
|
||||
"stp x20, x21, [%2, #16 * 10]\n"
|
||||
"stp x22, x23, [%2, #16 * 11]\n"
|
||||
"stp x24, x25, [%2, #16 * 12]\n"
|
||||
"stp x26, x27, [%2, #16 * 13]\n"
|
||||
"stp x28, x29, [%2, #16 * 14]\n"
|
||||
"mov %0, sp\n"
|
||||
"stp x30, %0, [%2, #16 * 15]\n"
|
||||
|
||||
"/* faked current PSTATE */\n"
|
||||
"mrs %0, CurrentEL\n"
|
||||
"mrs %1, SPSEL\n"
|
||||
"orr %0, %0, %1\n"
|
||||
"mrs %1, DAIF\n"
|
||||
"orr %0, %0, %1\n"
|
||||
"mrs %1, NZCV\n"
|
||||
"orr %0, %0, %1\n"
|
||||
/* pc */
|
||||
"adr %1, 1f\n"
|
||||
"1:\n"
|
||||
"stp %1, %0, [%2, #16 * 16]\n"
|
||||
: "=&r" (tmp1), "=&r" (tmp2)
|
||||
: "r" (®s)
|
||||
: "memory"
|
||||
);
|
||||
|
||||
seq_buf_printf(md_cntxt_seq_buf, "PANIC CPU : %d\n",
|
||||
raw_smp_processor_id());
|
||||
md_reg_context_data(®s);
|
||||
}
|
||||
|
||||
static void md_dump_other_cpus_context(void)
|
||||
{
|
||||
unsigned long ipi_stop_addr = kallsyms_lookup_name("regs_before_stop");
|
||||
int cpu;
|
||||
struct pt_regs *regs;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
regs = (struct pt_regs *)(ipi_stop_addr + per_cpu_offset(cpu));
|
||||
seq_buf_printf(md_cntxt_seq_buf, "\nSTOPPED CPU : %d\n", cpu);
|
||||
md_reg_context_data(regs);
|
||||
}
|
||||
}
|
||||
|
||||
static int md_die_context_notify(struct notifier_block *self,
|
||||
unsigned long val, void *data)
|
||||
{
|
||||
struct die_args *args = (struct die_args *)data;
|
||||
|
||||
if (md_in_oops_handler)
|
||||
return NOTIFY_DONE;
|
||||
md_in_oops_handler = true;
|
||||
if (!md_cntxt_seq_buf)
|
||||
return NOTIFY_DONE;
|
||||
die_cpu = raw_smp_processor_id();
|
||||
seq_buf_printf(md_cntxt_seq_buf, "\nDIE CPU : %d\n", die_cpu);
|
||||
md_reg_context_data(args->regs);
|
||||
md_in_oops_handler = false;
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block md_die_context_nb = {
|
||||
.notifier_call = md_die_context_notify,
|
||||
.priority = INT_MAX
|
||||
};
|
||||
#endif
|
||||
|
||||
static int md_panic_handler(struct notifier_block *this,
|
||||
unsigned long event, void *ptr)
|
||||
{
|
||||
if (md_in_oops_handler)
|
||||
return NOTIFY_DONE;
|
||||
md_in_oops_handler = true;
|
||||
#ifdef CONFIG_QCOM_MINIDUMP_PANIC_CPU_CONTEXT
|
||||
if (!md_cntxt_seq_buf)
|
||||
goto dump_rq;
|
||||
if (raw_smp_processor_id() != die_cpu)
|
||||
md_dump_panic_regs();
|
||||
md_dump_other_cpus_context();
|
||||
dump_rq:
|
||||
#endif
|
||||
md_dump_runqueues();
|
||||
md_in_oops_handler = false;
|
||||
return NOTIFY_DONE;
|
||||
|
|
@ -750,40 +928,64 @@ static struct notifier_block md_panic_blk = {
|
|||
.priority = INT_MAX,
|
||||
};
|
||||
|
||||
void md_register_panic_entries(void)
|
||||
static int md_register_minidump_entry(char *name, u64 virt_addr,
|
||||
u64 phys_addr, u64 size)
|
||||
{
|
||||
char *runq_buf;
|
||||
struct md_region md_entry;
|
||||
struct seq_buf *runq_seq_buf;
|
||||
int ret;
|
||||
|
||||
runq_buf = kzalloc(MD_RUNQUEUE_PAGES * PAGE_SIZE, GFP_KERNEL);
|
||||
if (!runq_buf)
|
||||
strlcpy(md_entry.name, name, sizeof(md_entry.name));
|
||||
md_entry.virt_addr = virt_addr;
|
||||
md_entry.phys_addr = phys_addr;
|
||||
md_entry.size = size;
|
||||
ret = msm_minidump_add_region(&md_entry);
|
||||
if (ret < 0)
|
||||
pr_err("Failed to add %s entry in Minidump\n", name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void md_register_panic_entries(int num_pages, char *name,
|
||||
struct seq_buf **global_buf)
|
||||
{
|
||||
char *buf;
|
||||
struct seq_buf *seq_buf_p;
|
||||
int ret;
|
||||
|
||||
buf = kzalloc(num_pages * PAGE_SIZE, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return;
|
||||
|
||||
runq_seq_buf = kzalloc(sizeof(*runq_seq_buf), GFP_KERNEL);
|
||||
if (!runq_seq_buf)
|
||||
seq_buf_p = kzalloc(sizeof(*seq_buf_p), GFP_KERNEL);
|
||||
if (!seq_buf_p)
|
||||
goto err_seq_buf;
|
||||
|
||||
strlcpy(md_entry.name, "KRUNQUEUE", sizeof(md_entry.name));
|
||||
md_entry.virt_addr = (uintptr_t)runq_buf;
|
||||
md_entry.phys_addr = virt_to_phys(runq_buf);
|
||||
md_entry.size = MD_RUNQUEUE_PAGES * PAGE_SIZE;
|
||||
if (msm_minidump_add_region(&md_entry) < 0) {
|
||||
pr_err("Failed to add runq buffer entry in Minidump\n");
|
||||
goto err_runq_reg;
|
||||
}
|
||||
ret = md_register_minidump_entry(name, (uintptr_t)buf,
|
||||
virt_to_phys(buf),
|
||||
num_pages * PAGE_SIZE);
|
||||
if (ret < 0)
|
||||
goto err_entry_reg;
|
||||
|
||||
seq_buf_init(runq_seq_buf, runq_buf, MD_RUNQUEUE_PAGES * PAGE_SIZE);
|
||||
seq_buf_init(seq_buf_p, buf, num_pages * PAGE_SIZE);
|
||||
|
||||
/* Complete registration before populating data */
|
||||
smp_mb();
|
||||
WRITE_ONCE(md_runq_seq_buf, runq_seq_buf);
|
||||
WRITE_ONCE(*global_buf, seq_buf_p);
|
||||
return;
|
||||
|
||||
err_runq_reg:
|
||||
kfree(runq_seq_buf);
|
||||
err_entry_reg:
|
||||
kfree(seq_buf_p);
|
||||
err_seq_buf:
|
||||
kfree(runq_buf);
|
||||
kfree(buf);
|
||||
}
|
||||
|
||||
static void md_register_panic_data(void)
|
||||
{
|
||||
md_register_panic_entries(MD_RUNQUEUE_PAGES, "KRUNQUEUE",
|
||||
&md_runq_seq_buf);
|
||||
#ifdef CONFIG_QCOM_MINIDUMP_PANIC_CPU_CONTEXT
|
||||
md_register_panic_entries(MD_CPU_CNTXT_PAGES, "KCNTXT",
|
||||
&md_cntxt_seq_buf);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -802,8 +1004,11 @@ static int __init msm_minidump_log_init(void)
|
|||
md_register_trace_buf();
|
||||
#endif
|
||||
#ifdef CONFIG_QCOM_MINIDUMP_PANIC_DUMP
|
||||
md_register_panic_entries();
|
||||
md_register_panic_data();
|
||||
atomic_notifier_chain_register(&panic_notifier_list, &md_panic_blk);
|
||||
#ifdef CONFIG_QCOM_MINIDUMP_PANIC_CPU_CONTEXT
|
||||
register_die_notifier(&md_die_context_nb);
|
||||
#endif
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue