mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 19:30:30 +09:00
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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user