blob: b70bfd7478dca65fa4f6326d95dd5c480b734d3c [file] [log] [blame] [edit]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/compat.h>
#include <linux/efi.h>
#include <linux/elf.h>
#include <linux/export.h>
#include <linux/sched.h>
#include <linux/sched/debug.h>
#include <linux/sched/task.h>
#include <linux/sched/task_stack.h>
#include <linux/kernel.h>
#include <linux/mman.h>
#include <linux/mm.h>
#include <linux/nospec.h>
#include <linux/stddef.h>
#include <linux/sysctl.h>
#include <linux/unistd.h>
#include <linux/user.h>
#include <linux/delay.h>
#include <linux/reboot.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/cpu.h>
#include <linux/elfcore.h>
#include <linux/pm.h>
#include <linux/tick.h>
#include <linux/utsname.h>
#include <linux/uaccess.h>
#include <linux/random.h>
#include <linux/hw_breakpoint.h>
#include <linux/personality.h>
#include <linux/notifier.h>
#include <trace/events/power.h>
#include <linux/percpu.h>
#include <linux/thread_info.h>
#include <linux/prctl.h>
#include <trace/hooks/fpsimd.h>
#include <trace/hooks/mpam.h>
#include <linux/mmap_lock.h>
#include <asm/compat.h>
#ifndef CONFIG_RISCV
#include <asm/cpufeature.h>
#endif
#include <asm/cacheflush.h>
#include <asm/exec.h>
#include <asm/mmu_context.h>
#include <asm/processor.h>
#include <asm/stacktrace.h>
#include <asm/switch_to.h>
#ifndef CONFIG_RISCV
#include <asm/system_misc.h>
#endif
#include <linux/mm_inline.h>
#include <linux/amlogic/user_fault.h>
#if IS_ENABLED(CONFIG_AMLOGIC_SECMON)
#include <linux/amlogic/secmon.h>
#endif
#ifndef CONFIG_RISCV
#ifndef CONFIG_ARM64
#include <asm/ptrace.h>
#include <asm/vdso/cp15.h>
#include <linux/ratelimit.h>
#define USR_FAULT_DBG_RATELIMIT_INTERVAL (5 * HZ)
#define USR_FAULT_DBG_RATELIMIT_BURST 3
#define user_fault_debug_ratelimited() \
({ \
static DEFINE_RATELIMIT_STATE(usr_fault_dgb_rs, \
USR_FAULT_DBG_RATELIMIT_INTERVAL, \
USR_FAULT_DBG_RATELIMIT_BURST); \
bool __show_ratelimited = false; \
if (__ratelimit(&usr_fault_dgb_rs)) \
__show_ratelimited = true; \
__show_ratelimited; \
})
#endif
#endif
static int show_kernel_data_valid(unsigned long reg)
{
struct page *page;
if (reg < (unsigned long)PAGE_OFFSET)
return 0;
else if (reg <= (unsigned long)high_memory)
return 1;
page = vmalloc_to_page((const void *)reg);
if (page && pfn_valid(page_to_pfn(page)))
return 1;
return 0;
}
static void show_data(unsigned long addr, int nbytes, const char *name)
{
int i, j;
int nlines;
u32 *p;
char buf[128] = {0};
int len = 0;
/*
* don't attempt to dump non-kernel addresses or
* values that are probably just small negative numbers
*/
#ifdef CONFIG_RISCV
if (addr < VMALLOC_START || addr > -256UL)
#else
if (addr < PAGE_OFFSET || addr > -256UL)
#endif
return;
/*
* Treating data in general purpose register as an address
* and dereferencing it is quite a dangerous behaviour,
* especially when it belongs to secure monotor region or
* ioremap region(for arm64 vmalloc region is already filtered
* out), which can lead to external abort on non-linefetch and
* can not be protected by probe_kernel_address.
* We need more strict filtering rules
*/
if (!show_kernel_data_valid((unsigned long)(addr + nbytes / 2)))
return;
#if IS_ENABLED(CONFIG_AMLOGIC_SECMON)
/*
* filter out secure monitor region
*/
if (within_secmon_region(addr + nbytes / 2)) {
pr_emerg("\n%s: %#lx S\n", name, addr + nbytes / 2);
return;
}
#endif
printk("\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
*/
len = 0;
len += snprintf(buf + len, sizeof(buf) - len, "%04lx ", (unsigned long)p & 0xffff);
for (j = 0; j < 8; j++) {
u32 data = 0;
if (get_kernel_nofault(data, p))
len += snprintf(buf + len, sizeof(buf) - len, " ********");
else
len += snprintf(buf + len, sizeof(buf) - len, " %08x", data);
++p;
}
pr_info("%s\n", buf);
}
}
/*
* 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;
char buf[128] = {0};
int len = 0;
if (!access_ok((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
*/
len = 0;
len += snprintf(buf + len, sizeof(buf) - len,
"%04lx ", (unsigned long)p & 0xffff);
for (j = 0; j < 8; j++) {
int bad;
unsigned char data[4];
bad = __copy_from_user_inatomic(data, (const void *)p, sizeof(u32));
if (bad)
len += snprintf(buf + len, sizeof(buf) - len, " ********");
else
len += snprintf(buf + len, sizeof(buf) - len,
" %08x", *(u32 *)data);
++p;
}
pr_info("%s\n", buf);
}
}
#ifdef CONFIG_ARM64
static void show_pfn(unsigned long reg, char *s)
{
struct page *page;
if (reg < (unsigned long)PAGE_OFFSET) {
pr_info("%s : %016lx U\n", s, reg);
} else if (reg <= (unsigned long)high_memory) {
pr_info("%s : %016lx, PFN:%5lx L\n", s, reg, virt_to_pfn(reg));
} else {
page = vmalloc_to_page((const void *)reg);
if (page)
pr_info("%s : %016lx, PFN:%5lx V\n", s, reg, page_to_pfn(page));
else
pr_info("%s : %016lx, PFN:***** V\n", s, reg);
}
}
static void show_regs_pfn(struct pt_regs *regs)
{
int i;
struct page *page;
for (i = 0; i < 31; i++) {
if (regs->regs[i] < (unsigned long)PAGE_OFFSET) {
continue;
} else if (regs->regs[i] <= (unsigned long)high_memory) {
pr_info("R%-2d : %016llx, PFN:%5lx L\n",
i, regs->regs[i], virt_to_pfn((void *)regs->regs[i]));
} else {
page = vmalloc_to_page((void *)regs->regs[i]);
if (page)
pr_info("R%-2d : %016llx, PFN:%5lx V\n",
i, regs->regs[i], page_to_pfn(page));
else
pr_info("R%-2d : %016llx, PFN:***** V\n", i, regs->regs[i]);
}
}
}
#elif defined CONFIG_RISCV
static void show_pfn(unsigned long reg, const char *s)
{
struct page *page;
if (reg < (unsigned long)VMALLOC_START) {
pr_info("%s : %016lx U\n", s, reg);
} else if (reg <= (unsigned long)PAGE_OFFSET) {
page = vmalloc_to_page((const void *)reg);
if (page)
pr_info("%s : %016lx, PFN:%5lx V\n", s, reg, page_to_pfn(page));
else
pr_info("%s : %016lx, PFN:***** V\n", s, reg);
} else if (reg <= (unsigned long)high_memory) {
pr_info("%s : %016lx, PFN:%5lx L\n", s, reg, virt_to_pfn(reg));
} else if (reg <= KERNEL_LINK_ADDR) {
} else if (reg <= kernel_map.virt_addr + kernel_map.size) {
pr_info("%s : %016lx, PFN:%5lx L\n", s, reg, virt_to_pfn(reg));
}
}
static const char * const regs_name[] = {
"epc", "ra", "sp", "gp", "tp", "t0", "t1", "t2", "s0", "s1",
"a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "s2", "s3",
"s4", "s5", "s6", "s7", "s8", "s9", "s10", "s11", "t3", "t4",
"t5", "t6"
};
static void show_regs_pfn(struct pt_regs *regs)
{
int i;
struct page *page;
unsigned long *reg_array = (unsigned long *)regs;
for (i = 0; i < 32; i++) {
if (reg_array[i] < (unsigned long)VMALLOC_START) {
continue;
} else if (reg_array[i] <= (unsigned long)PAGE_OFFSET) {
page = vmalloc_to_page((void *)reg_array[i]);
if (page)
pr_info("%-3s : %016lx, PFN:%5lx V\n",
regs_name[i], reg_array[i], page_to_pfn(page));
else
pr_info("%-3s : %016lx, PFN:***** V\n",
regs_name[i], reg_array[i]);
} else if (reg_array[i] <= (unsigned long)high_memory) {
pr_info("%-3s : %016lx, PFN:%5lx L\n",
regs_name[i], reg_array[i], virt_to_pfn((void *)reg_array[i]));
} else if (reg_array[i] <= KERNEL_LINK_ADDR) {
} else if (reg_array[i] <= kernel_map.virt_addr + kernel_map.size) {
pr_info("%-3s : %016lx, PFN:%5lx L\n",
regs_name[i], reg_array[i], virt_to_pfn((void *)reg_array[i]));
}
}
}
#endif /* CONFIG_ARM64 */
#ifdef CONFIG_ARM64
static void show_extra_register_data(struct pt_regs *regs, int nbytes)
{
unsigned int i;
show_data(regs->pc - nbytes, nbytes * 2, "PC");
show_data(regs->regs[30] - nbytes, nbytes * 2, "LR");
show_data(regs->sp - nbytes, nbytes * 2, "SP");
show_data(read_sysreg(far_el1), nbytes * 2, "FAR");
for (i = 0; i < 30; i++) {
char name[4];
snprintf(name, sizeof(name), "X%u", i);
show_data(regs->regs[i] - nbytes, nbytes * 2, name);
}
}
static void show_user_extra_register_data(struct pt_regs *regs, int nbytes)
{
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;
}
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");
show_user_data(read_sysreg(far_el1), nbytes * 2, "FAR");
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);
}
}
#elif defined CONFIG_ARM
static void show_extra_register_data(struct pt_regs *regs, int nbytes)
{
show_data(regs->ARM_pc - nbytes, nbytes * 2, "PC");
show_data(regs->ARM_lr - nbytes, nbytes * 2, "LR");
show_data(regs->ARM_sp - nbytes, nbytes * 2, "SP");
show_data(regs->ARM_ip - nbytes, nbytes * 2, "IP");
show_data(regs->ARM_fp - nbytes, nbytes * 2, "FP");
show_data(regs->ARM_r0 - nbytes, nbytes * 2, "R0");
show_data(regs->ARM_r1 - nbytes, nbytes * 2, "R1");
show_data(regs->ARM_r2 - nbytes, nbytes * 2, "R2");
show_data(regs->ARM_r3 - nbytes, nbytes * 2, "R3");
show_data(regs->ARM_r4 - nbytes, nbytes * 2, "R4");
show_data(regs->ARM_r5 - nbytes, nbytes * 2, "R5");
show_data(regs->ARM_r6 - nbytes, nbytes * 2, "R6");
show_data(regs->ARM_r7 - nbytes, nbytes * 2, "R7");
show_data(regs->ARM_r8 - nbytes, nbytes * 2, "R8");
show_data(regs->ARM_r9 - nbytes, nbytes * 2, "R9");
show_data(regs->ARM_r10 - nbytes, nbytes * 2, "R10");
}
static void show_user_extra_register_data(struct pt_regs *regs, int nbytes)
{
show_user_data(regs->ARM_pc - nbytes, nbytes * 2, "PC");
show_user_data(regs->ARM_lr - nbytes, nbytes * 2, "LR");
show_user_data(regs->ARM_sp - nbytes, nbytes * 2, "SP");
show_user_data(regs->ARM_ip - nbytes, nbytes * 2, "IP");
show_user_data(regs->ARM_fp - nbytes, nbytes * 2, "FP");
show_user_data(regs->ARM_r0 - nbytes, nbytes * 2, "R0");
show_user_data(regs->ARM_r1 - nbytes, nbytes * 2, "R1");
show_user_data(regs->ARM_r2 - nbytes, nbytes * 2, "R2");
show_user_data(regs->ARM_r3 - nbytes, nbytes * 2, "R3");
show_user_data(regs->ARM_r4 - nbytes, nbytes * 2, "R4");
show_user_data(regs->ARM_r5 - nbytes, nbytes * 2, "R5");
show_user_data(regs->ARM_r6 - nbytes, nbytes * 2, "R6");
show_user_data(regs->ARM_r7 - nbytes, nbytes * 2, "R7");
show_user_data(regs->ARM_r8 - nbytes, nbytes * 2, "R8");
show_user_data(regs->ARM_r9 - nbytes, nbytes * 2, "R9");
show_user_data(regs->ARM_r10 - nbytes, nbytes * 2, "R10");
}
void show_vmalloc_pfn(struct pt_regs *regs)
{
int i;
struct page *page;
for (i = 0; i < 16; i++) {
if (is_vmalloc_or_module_addr((void *)regs->uregs[i])) {
page = vmalloc_to_page((void *)regs->uregs[i]);
if (!page)
continue;
pr_info("R%-2d : %08lx, PFN:%5lx\n",
i, regs->uregs[i], page_to_pfn(page));
}
}
}
#elif defined CONFIG_RISCV
static void show_extra_register_data(struct pt_regs *regs, int nbytes)
{
int i;
unsigned long *reg_array = (unsigned long *)regs;
show_data(regs->badaddr - nbytes, nbytes * 2, "badaddr");
for (i = 0; i < 32; i++)
show_data(reg_array[i] - nbytes, nbytes * 2, regs_name[i]);
}
static void show_user_extra_register_data(struct pt_regs *regs, int nbytes)
{
int i;
unsigned long *reg_array = (unsigned long *)regs;
show_user_data(regs->badaddr - nbytes, nbytes * 2, "badaddr");
for (i = 0; i < 32; i++)
show_user_data(reg_array[i] - nbytes, nbytes * 2, regs_name[i]);
}
#endif
#if IS_MODULE(CONFIG_AMLOGIC_USER_FAULT)
__weak const char *arch_vma_name(struct vm_area_struct *vma)
{
return NULL;
}
#endif
/* Check if the vma is being used as a stack by this task */
static int aml_vma_is_stack_for_current(struct vm_area_struct *vma)
{
struct task_struct * __maybe_unused t = current;
return (vma->vm_start <= KSTK_ESP(t) && vma->vm_end >= KSTK_ESP(t));
}
static struct anon_vma_name *aml_anon_vma_name(struct vm_area_struct *vma)
{
mmap_assert_locked(vma->vm_mm);
if (vma->vm_file)
return NULL;
return vma->anon_name;
}
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 = aml_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 (aml_anon_vma_name(vma))
pr_info("[anon]");
}
done:
if (name)
pr_info("%s", name);
pr_info("\n");
}
#if IS_MODULE(CONFIG_AMLOGIC_USER_FAULT) && IS_ENABLED(CONFIG_KALLSYMS_ALL)
struct mm_struct *aml_init_mm;
unsigned long (*aml_syms_lookup)(const char *name);
/* For each probe you need to allocate a kprobe structure */
static struct kprobe kp_lookup_name = {
.symbol_name = "kallsyms_lookup_name",
};
#endif
static long __nocfi get_user_pfn(struct mm_struct *mm, unsigned long addr)
{
long pfn = -1;
pgd_t *pgd;
#if IS_MODULE(CONFIG_AMLOGIC_USER_FAULT) && IS_ENABLED(CONFIG_KALLSYMS_ALL)
if (!mm || addr >= VMALLOC_START)
mm = aml_init_mm;
if (!mm)
return pfn;
#elif IS_MODULE(CONFIG_AMLOGIC_USER_FAULT) && !IS_ENABLED(CONFIG_KALLSYMS_ALL)
return pfn;
#else
if (!mm || addr >= VMALLOC_START)
mm = &init_mm;
#endif
pgd = pgd_offset(mm, addr);
do {
p4d_t *p4d;
pud_t *pud;
pmd_t *pmd;
pte_t *pte;
if (pgd_none(*pgd) || pgd_bad(*pgd))
break;
p4d = p4d_offset(pgd, addr);
if (p4d_none(*p4d) || p4d_bad(*p4d))
break;
pud = pud_offset(p4d, 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;
}
#ifdef CONFIG_ARM64
void show_all_pfn(struct task_struct *task, struct pt_regs *regs)
{
int i;
long pfn1, far;
char s1[10];
int top;
char buf[128] = {0};
int len = 0;
if (compat_user_mode(regs))
top = 15;
else
top = 31;
len += snprintf(buf + len, sizeof(buf) - len, "reg value pfn ");
len += snprintf(buf + len, sizeof(buf) - len, "reg value pfn");
pr_info("%s\n", buf);
len = 0;
memset(buf, 0, sizeof(buf));
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, "--------");
if (i % 2 == 1) {
len += snprintf(buf + len, sizeof(buf) - len,
"r%-2d: %016llx %s", i, regs->regs[i], s1);
pr_info("%s\n", buf);
len = 0;
} else {
len += snprintf(buf + len, sizeof(buf) - len,
"r%-2d: %016llx %s ", i, regs->regs[i], s1);
}
}
if (len > 0)
pr_info("%s\n", buf);
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);
far = read_sysreg(far_el1);
pfn1 = get_user_pfn(task->mm, far);
if (pfn1 >= 0)
sprintf(s1, "%8lx", pfn1);
else
sprintf(s1, "--------");
pr_info("unused : %016lx %s\n", far, s1);
#if IS_BUILTIN(CONFIG_AMLOGIC_USER_FAULT)
pr_info("offset : %016lx\n", kaslr_offset());
#endif
}
#elif defined CONFIG_ARM
static unsigned char *regidx_to_name[] = {
"r0 ", "r1 ", "r2 ", "r3 ",
"r4 ", "r5 ", "r6 ", "r7 ",
"r8 ", "r9 ", "r10",
"fp ", "ip ", "sp ", "lr ",
"pc "
};
void show_all_pfn(struct task_struct *task, struct pt_regs *regs)
{
int i;
long pfn1;
char s1[10];
int top;
char buf[128] = {0};
int len = 0;
top = 16;
len += snprintf(buf + len, sizeof(buf) - len, "reg value pfn ");
len += snprintf(buf + len, sizeof(buf) - len, "reg value pfn");
pr_info("%s\n", buf);
len = 0;
memset(buf, 0, sizeof(buf));
for (i = 0; i < top; i++) {
pfn1 = get_user_pfn(task->mm, regs->uregs[i]);
if (pfn1 >= 0)
sprintf(s1, "%8lx", pfn1);
else
sprintf(s1, "--------");
if (i % 2 == 0) {
len += snprintf(buf + len, sizeof(buf) - len, "%s: %08lx %s ",
regidx_to_name[i], regs->uregs[i], s1);
} else {
len += snprintf(buf + len, sizeof(buf) - len, "%s: %08lx %s",
regidx_to_name[i], regs->uregs[i], s1);
pr_info("%s\n", buf);
len = 0;
}
}
if (len > 0)
pr_info("%s\n", buf);
}
void show_debug_ratelimited(struct pt_regs *regs, unsigned int reg_en)
{
if (user_fault_debug_ratelimited()) {
show_all_pfn(current, regs);
if (reg_en)
show_regs(regs);
}
}
#elif defined CONFIG_RISCV
void show_all_pfn(struct task_struct *task, struct pt_regs *regs)
{
int i;
long pfn1;
char s1[10];
int top = 32;
unsigned long *reg_array = (unsigned long *)regs;
pr_info("reg value pfn ");
pr_cont("reg value pfn\n");
for (i = 0; i < top; i++) {
pfn1 = get_user_pfn(task->mm, reg_array[i]);
if (pfn1 >= 0)
sprintf(s1, "%8lx", pfn1);
else
sprintf(s1, "--------");
if (i % 2 == 1)
pr_cont("%-3s: %016lx %s\n", regs_name[i], reg_array[i], s1);
else
pr_info("%-3s: %016lx %s ", regs_name[i], reg_array[i], s1);
}
pr_cont("\n");
pfn1 = get_user_pfn(task->mm, regs->epc);
if (pfn1 >= 0)
sprintf(s1, "%8lx", pfn1);
else
sprintf(s1, "--------");
pr_info("pc : %016lx %s\n", regs->epc, s1);
pfn1 = get_user_pfn(task->mm, regs->sp);
if (pfn1 >= 0)
sprintf(s1, "%8lx", pfn1);
else
sprintf(s1, "--------");
pr_info("sp : %016lx %s\n", regs->sp, s1);
pfn1 = get_user_pfn(task->mm, regs->badaddr);
if (pfn1 >= 0)
sprintf(s1, "%8lx", pfn1);
else
sprintf(s1, "--------");
pr_info("unused : %016lx %s\n", regs->badaddr, s1);
// pr_info("offset : %016lx\n", kaslr_offset());
}
#endif
static int (*dmc_cb)(char *);
void set_dump_dmc_func(void *f)
{
dmc_cb = (void *)f;
}
EXPORT_SYMBOL(set_dump_dmc_func);
void _dump_dmc_reg(void)
{
char buf[1024] = {0};
if (!dmc_cb)
return;
dmc_cb(buf);
pr_crit("%s\n", buf);
}
void show_user_fault_info(struct pt_regs *regs, u64 lr, u64 sp)
{
#ifdef CONFIG_ARM64
if (user_mode(regs)) {
show_vma(current->mm, instruction_pointer(regs));
show_vma(current->mm, lr);
show_vma(current->mm, read_sysreg(far_el1));
}
show_pfn(instruction_pointer(regs), "PC");
show_pfn(sp, "SP");
show_pfn(read_sysreg(far_el1), "FAR");
show_regs_pfn(regs);
#elif defined CONFIG_ARM
if (user_mode(regs)) {
show_vma(current->mm, instruction_pointer(regs));
show_vma(current->mm, regs->ARM_lr);
}
#elif defined CONFIG_RISCV
if (user_mode(regs)) {
show_vma(current->mm, instruction_pointer(regs));
show_vma(current->mm, lr);
show_vma(current->mm, regs->badaddr);
}
show_pfn(regs->badaddr, "badaddr");
show_regs_pfn(regs);
#endif
}
void show_extra_reg_data(struct pt_regs *regs)
{
if (!user_mode(regs))
show_extra_register_data(regs, 128);
else
show_user_extra_register_data(regs, 128);
printk("\n");
}
#if IS_MODULE(CONFIG_AMLOGIC_USER_FAULT) && IS_ENABLED(CONFIG_KALLSYMS_ALL)
static struct kprobe kp_show_regs = {
.symbol_name = "__show_regs",
};
static struct kprobe kp_bad_el0_sync = {
.symbol_name = "bad_el0_sync",
};
static void __kprobes show_regs_handler_post(struct kprobe *p,
struct pt_regs *param_regs, unsigned long flags)
{
struct pt_regs *regs = (struct pt_regs *)param_regs->regs[0];
u64 lr, sp;
if (compat_user_mode(regs)) {
lr = regs->compat_lr;
sp = regs->compat_sp;
} else {
lr = regs->regs[30];
sp = regs->sp;
}
show_user_fault_info(regs, lr, sp);
show_extra_reg_data(regs);
}
static void __kprobes bad_el0_sync_handler_post(struct kprobe *p,
struct pt_regs *param_regs, unsigned long flags)
{
struct pt_regs *regs = (struct pt_regs *)param_regs->regs[0];
show_all_pfn(current, regs);
}
static int __nocfi user_fault_register_kprobe(void *data)
{
int ret;
ret = register_kprobe(&kp_lookup_name);
if (ret < 0) {
pr_err("register_kprobe failed, returned %d\n", ret);
return -1;
}
pr_debug("kprobe lookup offset at %px\n", kp_lookup_name.addr);
aml_syms_lookup = (unsigned long (*)(const char *name))kp_lookup_name.addr;
aml_init_mm = (struct mm_struct *)aml_syms_lookup("init_mm");
pr_debug("aml_init_mm: %px\n", aml_init_mm);
kp_show_regs.post_handler = show_regs_handler_post;
ret = register_kprobe(&kp_show_regs);
if (ret < 0) {
pr_err("register_kprobe %s failed, returned %d\n",
kp_show_regs.symbol_name, ret);
return -1;
}
kp_bad_el0_sync.post_handler = bad_el0_sync_handler_post;
ret = register_kprobe(&kp_bad_el0_sync);
if (ret < 0) {
pr_err("register_kprobe %s failed, returned %d\n",
kp_bad_el0_sync.symbol_name, ret);
return -1;
}
return 0;
}
static int __init user_fault_module_init(void)
{
kthread_run(user_fault_register_kprobe, NULL, "AML_USER_FAULT");
return 0;
}
static void __exit user_fault_module_exit(void)
{
unregister_kprobe(&kp_lookup_name);
unregister_kprobe(&kp_show_regs);
unregister_kprobe(&kp_bad_el0_sync);
}
module_init(user_fault_module_init);
module_exit(user_fault_module_exit);
#endif
MODULE_LICENSE("GPL v2");