mm: show more informations when catch user fault

PD#154260: mm: show more informations when catch user fault

1. show pfn for all registers;
2. show memory around all registers;
3. show vma maps around pc/lr.

Change-Id: Ibc92f3aea2727c749c3e865495ca7a4920d4e7a0
Signed-off-by: tao zeng <tao.zeng@amlogic.com>
This commit is contained in:
tao zeng
2017-12-11 10:08:52 +08:00
committed by Jianxin Pan
parent 3e1038b0aa
commit b0c8f6e30f
6 changed files with 285 additions and 0 deletions

View File

@@ -15,6 +15,14 @@ config ARM64_PTDUMP
If in doubt, say N.
config AMLOGIC_USER_FAULT
bool "Amlogic user fault helper"
help
show more information when catch user fault. Including:
memory around all registers
maps with pc/lr
pfn for all virtual address of regirsters
config PID_IN_CONTEXTIDR
bool "Write the current PID to the CONTEXTIDR register"
help

View File

@@ -530,6 +530,7 @@ CONFIG_DEBUG_ATOMIC_SLEEP=y
CONFIG_FTRACE_SYSCALLS=y
CONFIG_STACK_TRACER=y
CONFIG_FUNCTION_PROFILER=y
CONFIG_AMLOGIC_USER_FAULT=y
CONFIG_SECURITY_DMESG_RESTRICT=y
CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y
CONFIG_SECURITY=y

View File

@@ -41,6 +41,13 @@ void hook_debug_fault_code(int nr, int (*fn)(unsigned long, unsigned int,
struct mm_struct;
extern void show_pte(struct mm_struct *mm, unsigned long addr);
#ifdef CONFIG_AMLOGIC_USER_FAULT
extern void show_all_pfn(struct task_struct *task, struct pt_regs *regs);
#else
static inline void show_all_pfn(struct task_struct *task, struct pt_regs *regs)
{
}
#endif /* CONFIG_AMLOGIC_USER_FAULT */
extern void __show_regs(struct pt_regs *);
extern void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd);

View File

@@ -212,6 +212,51 @@ static void show_data(unsigned long addr, int nbytes, const char *name)
}
}
#ifdef CONFIG_AMLOGIC_USER_FAULT
/*
* dump a block of user memory from around the given address
*/
static void show_user_data(unsigned long addr, int nbytes, const char *name)
{
int i, j;
int nlines;
u32 *p;
if (!access_ok(VERIFY_READ, (void *)addr, nbytes))
return;
pr_info("\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
*/
pr_info("%04lx ", (unsigned long)p & 0xffff);
for (j = 0; j < 8; j++) {
u32 data;
int bad;
bad = __get_user(data, p);
if (bad)
pr_info(" ********");
else
pr_info(" %08x", data);
++p;
}
pr_info("\n");
}
}
#endif /* CONFIG_AMLOGIC_USER_FAULT */
static void show_extra_register_data(struct pt_regs *regs, int nbytes)
{
mm_segment_t fs;
@@ -230,6 +275,138 @@ static void show_extra_register_data(struct pt_regs *regs, int nbytes)
set_fs(fs);
}
#ifdef CONFIG_AMLOGIC_USER_FAULT
static void show_user_extra_register_data(struct pt_regs *regs, int nbytes)
{
mm_segment_t fs;
unsigned int i, top_reg;
u64 sp, lr;
if (compat_user_mode(regs)) {
lr = regs->compat_lr;
sp = regs->compat_sp;
top_reg = 13;
} else {
lr = regs->regs[30];
sp = regs->sp;
top_reg = 29;
}
fs = get_fs();
set_fs(KERNEL_DS);
show_user_data(regs->pc - nbytes, nbytes * 2, "PC");
show_user_data(lr - nbytes, nbytes * 2, "LR");
show_user_data(sp - nbytes, nbytes * 2, "SP");
for (i = 0; i < top_reg; i++) {
char name[4];
snprintf(name, sizeof(name), "r%u", i);
show_user_data(regs->regs[i] - nbytes, nbytes * 2, name);
}
set_fs(fs);
}
static void show_vma(struct mm_struct *mm, unsigned long addr)
{
struct vm_area_struct *vma;
struct file *file;
vm_flags_t flags;
unsigned long ino = 0;
unsigned long long pgoff = 0;
unsigned long start, end;
dev_t dev = 0;
const char *name = NULL;
vma = find_vma(mm, addr);
if (!vma) {
pr_info("can't find vma for %lx\n", addr);
return;
}
file = vma->vm_file;
flags = vma->vm_flags;
if (file) {
struct inode *inode = file_inode(vma->vm_file);
dev = inode->i_sb->s_dev;
ino = inode->i_ino;
pgoff = ((loff_t)vma->vm_pgoff) << PAGE_SHIFT;
}
/* We don't show the stack guard page in /proc/maps */
start = vma->vm_start;
end = vma->vm_end;
pr_info("vma for %lx:\n", addr);
pr_info("%08lx-%08lx %c%c%c%c %08llx %02x:%02x %lu ",
start,
end,
flags & VM_READ ? 'r' : '-',
flags & VM_WRITE ? 'w' : '-',
flags & VM_EXEC ? 'x' : '-',
flags & VM_MAYSHARE ? 's' : 'p',
pgoff,
MAJOR(dev), MINOR(dev), ino);
/*
* Print the dentry name for named mappings, and a
* special [heap] marker for the heap:
*/
if (file) {
char file_name[256] = {};
char *p = d_path(&file->f_path, file_name, 256);
if (!IS_ERR(p)) {
mangle_path(file_name, p, "\n");
pr_info("%s", p);
} else
pr_info(" get file path failed\n");
goto done;
}
name = arch_vma_name(vma);
if (!name) {
pid_t tid;
if (!mm) {
name = "[vdso]";
goto done;
}
if (vma->vm_start <= mm->brk &&
vma->vm_end >= mm->start_brk) {
name = "[heap]";
goto done;
}
tid = vma_is_stack_for_current(vma);
if (tid != 0) {
/*
* Thread stack in /proc/PID/task/TID/maps or
* the main process stack.
*/
if ((vma->vm_start <= mm->start_stack &&
vma->vm_end >= mm->start_stack)) {
name = "[stack]";
} else {
/* Thread stack in /proc/PID/maps */
pr_info("[stack:%d]", tid);
}
goto done;
}
if (vma_get_anon_name(vma))
pr_info("[anon]");
}
done:
if (name)
pr_info("%s", name);
pr_info("\n");
}
#endif /* CONFIG_AMLOGIC_USER_FAULT */
void __show_regs(struct pt_regs *regs)
{
int i, top_reg;
@@ -248,6 +425,12 @@ void __show_regs(struct pt_regs *regs)
show_regs_print_info(KERN_DEFAULT);
print_symbol("PC is at %s\n", instruction_pointer(regs));
print_symbol("LR is at %s\n", lr);
#ifdef CONFIG_AMLOGIC_USER_FAULT
if (user_mode(regs)) {
show_vma(current->mm, instruction_pointer(regs));
show_vma(current->mm, lr);
}
#endif /* CONFIG_AMLOGIC_USER_FAULT */
printk("pc : [<%016llx>] lr : [<%016llx>] pstate: %08llx\n",
regs->pc, lr, regs->pstate);
printk("sp : %016llx\n", sp);
@@ -267,6 +450,10 @@ void __show_regs(struct pt_regs *regs)
}
if (!user_mode(regs))
show_extra_register_data(regs, 128);
#ifdef CONFIG_AMLOGIC_USER_FAULT
else
show_user_extra_register_data(regs, 128);
#endif /* CONFIG_AMLOGIC_USER_FAULT */
printk("\n");
}

View File

@@ -625,6 +625,9 @@ asmlinkage void bad_el0_sync(struct pt_regs *regs, int reason, unsigned int esr)
void __user *pc = (void __user *)instruction_pointer(regs);
console_verbose();
#ifdef CONFIG_AMLOGIC_USER_FAULT
show_all_pfn(current, regs);
#endif /* CONFIG_AMLOGIC_USER_FAULT */
pr_crit("Bad EL0 synchronous exception detected on CPU%d, code 0x%08x -- %s\n",
smp_processor_id(), esr, esr_get_class_string(esr));
__show_regs(regs);

View File

@@ -168,6 +168,43 @@ int ptep_set_access_flags(struct vm_area_struct *vma,
}
#endif
#ifdef CONFIG_AMLOGIC_USER_FAULT
static long get_user_pfn(struct mm_struct *mm, unsigned long addr)
{
long pfn = -1;
pgd_t *pgd;
if (!mm)
mm = &init_mm;
pgd = pgd_offset(mm, addr);
do {
pud_t *pud;
pmd_t *pmd;
pte_t *pte;
if (pgd_none(*pgd) || pgd_bad(*pgd))
break;
pud = pud_offset(pgd, addr);
if (pud_none(*pud) || pud_bad(*pud))
break;
pmd = pmd_offset(pud, addr);
if (pmd_none(*pmd) || pmd_bad(*pmd))
break;
pte = pte_offset_map(pmd, addr);
pfn = pte_pfn(*pte);
pte_unmap(pte);
} while (0);
return pfn;
}
#endif /* CONFIG_AMLOGIC_USER_FAULT */
static bool is_el1_instruction_abort(unsigned int esr)
{
return ESR_ELx_EC(esr) == ESR_ELx_EC_IABT_CUR;
@@ -200,6 +237,45 @@ static void __do_kernel_fault(struct mm_struct *mm, unsigned long addr,
do_exit(SIGKILL);
}
#ifdef CONFIG_AMLOGIC_USER_FAULT
void show_all_pfn(struct task_struct *task, struct pt_regs *regs)
{
int i;
long pfn1;
char s1[10];
int top;
if (compat_user_mode(regs))
top = 15;
else
top = 31;
pr_info("reg value pfn ");
pr_info("reg value pfn\n");
for (i = 0; i < top; i++) {
pfn1 = get_user_pfn(task->mm, regs->regs[i]);
if (pfn1 >= 0)
sprintf(s1, "%8lx", pfn1);
else
sprintf(s1, "--------");
pr_info("r%-2d: %016llx %s ", i, regs->regs[i], s1);
if (i % 2 == 1)
pr_info("\n");
}
pfn1 = get_user_pfn(task->mm, regs->pc);
if (pfn1 >= 0)
sprintf(s1, "%8lx", pfn1);
else
sprintf(s1, "--------");
pr_info("pc : %016llx %s\n", regs->pc, s1);
pfn1 = get_user_pfn(task->mm, regs->sp);
if (pfn1 >= 0)
sprintf(s1, "%8lx", pfn1);
else
sprintf(s1, "--------");
pr_info("sp : %016llx %s\n", regs->sp, s1);
}
#endif /* CONFIG_AMLOGIC_USER_FAULT */
/*
* Something tried to access memory that isn't in our memory map. User mode
* accesses just cause a SIGSEGV
@@ -217,6 +293,9 @@ static void __do_user_fault(struct task_struct *tsk, unsigned long addr,
tsk->comm, task_pid_nr(tsk), inf->name, sig,
addr, esr);
show_pte(tsk->mm, addr);
#ifdef CONFIG_AMLOGIC_USER_FAULT
show_all_pfn(tsk, regs);
#endif /* CONFIG_AMLOGIC_USER_FAULT */
show_regs(regs);
}