kernel_samsung_a34x-permissive/drivers/misc/mediatek/aee/mrdump/mrdump_helper.c
2024-04-28 15:51:13 +02:00

721 lines
14 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2015 MediaTek Inc.
*/
#include <linux/init.h>
#include <linux/memblock.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/of_fdt.h>
#include <linux/percpu.h>
#include "../../../../kernel/sched/sched.h"
#include <asm/memory.h>
#include <asm/pgtable.h>
#include <asm/sections.h>
#include <asm/smp_plat.h>
#include <asm/stacktrace.h>
#include <asm/system_misc.h>
#include <mt-plat/aee.h>
struct module_sect_attr {
struct module_attribute mattr;
char *name;
unsigned long address;
};
struct module_sect_attrs {
struct attribute_group grp;
unsigned int nsections;
struct module_sect_attr attrs[0];
};
#ifdef MODULE
struct aee_sym {
char name[KSYM_NAME_LEN];
unsigned long addr;
};
static int (*aee_koes)(int (*fn)(void *, const char *,
struct module *,
unsigned long),
void *data);
static int addr_ok(void *data, const char *namebuf,
struct module *mod, unsigned long addr)
{
struct aee_sym *aee_sym = (struct aee_sym *)data;
if (strcmp(namebuf, aee_sym->name) == 0) {
aee_sym->addr = addr;
return 1;
}
return 0;
}
#define SYM_PT "kallsyms_on_each_symbol+0x%X/0x%X"
#define FIX_OFF 0x72C
static void find_koes(void)
{
char namebuf[KSYM_NAME_LEN] = {0};
unsigned long addr_tmp = (unsigned long)sprint_symbol;
int off;
int fsize;
int ret;
if (aee_koes)
return;
addr_tmp -= FIX_OFF;
sprint_symbol(namebuf, addr_tmp);
ret = sscanf(namebuf, SYM_PT, &off, &fsize);
if (ret == 2) {
if (off > 0 && off <= fsize)
addr_tmp -= off;
aee_koes = (void *)addr_tmp;
return;
}
}
static int need_init = 1;
unsigned long aee_addr_find(const char *name)
{
struct aee_sym sym;
if (need_init) {
need_init = 0;
find_koes();
}
if (!aee_koes)
return 0;
memset(&sym, 0x0, sizeof(sym));
if (snprintf(sym.name, sizeof(sym.name), "%s", name) < 0)
return 0;
if (aee_koes(addr_ok, (void *)(&sym)))
return sym.addr;
return 0;
}
EXPORT_SYMBOL(aee_addr_find);
/* for mrdump.ko */
static void (*p_show_regs)(struct pt_regs *);
void aee_show_regs(struct pt_regs *regs)
{
if (p_show_regs) {
p_show_regs(regs);
return;
}
p_show_regs = (void *)aee_addr_find("__show_regs");
if (!p_show_regs)
pr_info("%s failed", __func__);
else
p_show_regs(regs);
}
#ifdef CONFIG_ARM64
static int (*p_unwind_frame)(struct task_struct *tsk,
struct stackframe *frame);
int aee_unwind_frame(struct task_struct *tsk, struct stackframe *frame)
{
if (p_unwind_frame)
return p_unwind_frame(tsk, frame);
p_unwind_frame = (void *)aee_addr_find("unwind_frame");
if (p_unwind_frame)
return p_unwind_frame(tsk, frame);
pr_info("%s failed", __func__);
return -1;
}
#else
static int (*p_unwind_frame)(struct stackframe *frame);
int aee_unwind_frame(struct stackframe *frame)
{
if (p_unwind_frame)
return p_unwind_frame(frame);
p_unwind_frame = (void *)aee_addr_find("unwind_frame");
if (p_unwind_frame)
return p_unwind_frame(frame);
pr_info("%s failed", __func__);
return -1;
}
#endif
static unsigned long p_stext;
unsigned long aee_get_stext(void)
{
if (p_stext != 0)
return p_stext;
p_stext = aee_addr_find("_stext");
if (p_stext == 0)
pr_info("%s failed", __func__);
return p_stext;
}
static unsigned long p_etext;
unsigned long aee_get_etext(void)
{
if (p_etext != 0)
return p_etext;
p_etext = aee_addr_find("_etext");
if (p_etext == 0)
pr_info("%s failed", __func__);
return p_etext;
}
static unsigned long p_text;
unsigned long aee_get_text(void)
{
if (p_text != 0)
return p_text;
p_text = aee_addr_find("_text");
if (p_text == 0)
pr_info("%s failed", __func__);
return p_text;
}
#if defined(CONFIG_ARM64)
static unsigned long *p_kimage_vaddr;
unsigned long aee_get_kimage_vaddr(void)
{
if (p_kimage_vaddr)
return *p_kimage_vaddr;
p_kimage_vaddr = (void *)aee_addr_find("kimage_vaddr");
if (!p_kimage_vaddr) {
pr_info("%s failed", __func__);
return 0;
}
return *p_kimage_vaddr;
}
#endif
static phys_addr_t (*p_memblock_start_of_DRAM)(void);
phys_addr_t aee_memblock_start_of_DRAM(void)
{
if (p_memblock_start_of_DRAM)
return p_memblock_start_of_DRAM();
p_memblock_start_of_DRAM =
(void *)aee_addr_find("memblock_start_of_DRAM");
if (p_memblock_start_of_DRAM)
return p_memblock_start_of_DRAM();
pr_info("%s failed", __func__);
return 0;
}
static phys_addr_t (*p_memblock_end_of_DRAM)(void);
phys_addr_t aee_memblock_end_of_DRAM(void)
{
if (p_memblock_end_of_DRAM)
return p_memblock_end_of_DRAM();
p_memblock_end_of_DRAM = (void *)aee_addr_find("memblock_end_of_DRAM");
if (p_memblock_end_of_DRAM)
return p_memblock_end_of_DRAM();
pr_info("%s failed", __func__);
return 0;
}
static void (*p_print_modules)(void);
void aee_print_modules(void)
{
if (p_print_modules) {
p_print_modules();
return;
}
p_print_modules = (void *)aee_addr_find("print_modules");
if (!p_print_modules) {
pr_info("%s failed", __func__);
return;
}
p_print_modules();
}
static struct list_head *p_modules;
/* MUST ensure called when preempt disabled already */
int aee_save_modules(char *mbuf, int mbufsize)
{
struct module *mod;
int sz = 0;
unsigned long text_addr = 0;
unsigned long init_addr = 0;
int i, search_nm;
if (!mbuf || mbufsize <= 0) {
pr_info("mrdump: module info buffer wrong(sz:%d)\n", mbufsize);
return sz;
}
if (!p_modules)
p_modules = (void *)aee_addr_find("modules");
if (!p_modules) {
pr_info("%s failed", __func__);
return sz;
}
memset(mbuf, '\0', mbufsize);
sz += snprintf(mbuf + sz, mbufsize - sz, "Modules linked in:");
list_for_each_entry_rcu(mod, p_modules, list) {
if (mod->state == MODULE_STATE_UNFORMED)
continue;
if (sz >= mbufsize) {
pr_info("mrdump: module info buffer full(sz:%d)\n",
mbufsize);
break;
}
text_addr = (unsigned long)mod->core_layout.base;
init_addr = (unsigned long)mod->init_layout.base;
search_nm = 2;
for (i = 0; i < mod->sect_attrs->nsections; i++) {
if (!strcmp(mod->sect_attrs->attrs[i].name, ".text")) {
text_addr = mod->sect_attrs->attrs[i].address;
search_nm--;
} else if (!strcmp(mod->sect_attrs->attrs[i].name,
".init.text")) {
init_addr = mod->sect_attrs->attrs[i].address;
search_nm--;
}
if (!search_nm)
break;
}
sz += snprintf(mbuf + sz, mbufsize - sz,
" %s %lx %lx %d %d",
mod->name,
text_addr,
init_addr,
mod->core_layout.size,
mod->init_layout.size);
}
if (sz < mbufsize)
sz += snprintf(mbuf + sz, mbufsize - sz, "\n");
return sz;
}
#ifdef __aarch64__
static u64 *p__cpu_logical_map;
static u64 *aee_cpu_logical_map(void)
#else
static u32 *p__cpu_logical_map;
static u32 *aee_cpu_logical_map(void)
#endif
{
if (p__cpu_logical_map)
return p__cpu_logical_map;
p__cpu_logical_map = (void *)aee_addr_find("__cpu_logical_map");
if (p__cpu_logical_map)
return p__cpu_logical_map;
return NULL;
}
int get_HW_cpuid(void)
{
u64 mpidr;
int cpu;
if (!aee_cpu_logical_map())
return -EINVAL;
mpidr = read_cpuid_mpidr();
/*
* NOTICE: the logic of following code sould be the same with
* get_logical_index() of linux kenrel
*/
mpidr &= MPIDR_HWID_BITMASK;
for (cpu = 0; cpu < nr_cpu_ids; cpu++)
if (p__cpu_logical_map[cpu] == mpidr)
return cpu;
return -EINVAL;
}
EXPORT_SYMBOL(get_HW_cpuid);
u32 aee_log_buf_len_get(void)
{
u32 log_buf_len = 1 << CONFIG_LOG_BUF_SHIFT;
if (log_buf_len > 0 && log_buf_len <= (1 << 25))
return log_buf_len;
return 0;
}
static char *p__log_buf;
char *aee_log_buf_addr_get(void)
{
if (p__log_buf)
return p__log_buf;
p__log_buf = (void *)(aee_addr_find("__log_buf"));
if (p__log_buf)
return p__log_buf;
pr_info("%s failed", __func__);
return NULL;
}
EXPORT_SYMBOL(aee_log_buf_addr_get);
#ifdef __aarch64__
static unsigned long *p_irq_stack_ptr;
static unsigned long *aee_irq_stack_ptr(void)
{
if (p_irq_stack_ptr)
return p_irq_stack_ptr;
p_irq_stack_ptr = (void *)aee_addr_find("irq_stack_ptr");
if (!p_irq_stack_ptr) {
pr_info("%s failed", __func__);
return NULL;
}
return p_irq_stack_ptr;
}
/* NOTICE: this function should be the same with on_irq_stack() */
bool aee_on_irq_stack(unsigned long sp, struct stack_info *info)
{
unsigned long *isp = aee_irq_stack_ptr();
unsigned long low, high;
if (!isp)
return false;
low = (unsigned long)raw_cpu_read(*isp);
high = low + IRQ_STACK_SIZE;
if (!low)
return false;
if (sp < low || sp >= high)
return false;
if (info) {
info->low = low;
info->high = high;
info->type = STACK_TYPE_IRQ;
}
return true;
}
#endif
static struct mm_struct *p_init_mm;
static struct mm_struct *aee_init_mm(void)
{
if (p_init_mm)
return p_init_mm;
p_init_mm = (void *)aee_addr_find("init_mm");
if (!p_init_mm) {
pr_info("%s failed", __func__);
return NULL;
}
return p_init_mm;
}
pgd_t *aee_pgd_offset_k(unsigned long addr)
{
struct mm_struct *pim = aee_init_mm();
if (!pim)
return NULL;
return (pgd_t *)pgd_offset(pim, addr);
}
static struct rq *p_runqueues;
static struct rq *aee_runqueues(void)
{
if (p_runqueues)
return p_runqueues;
p_runqueues = (void *)aee_addr_find("runqueues");
if (!p_runqueues) {
pr_info("%s failed", __func__);
return NULL;
}
return p_runqueues;
}
unsigned long aee_cpu_rq(int cpu)
{
struct rq *p_runqueues = aee_runqueues();
if (p_runqueues)
return (unsigned long)(&per_cpu(*p_runqueues, (cpu)));
return 0;
}
struct task_struct *aee_cpu_curr(int cpu)
{
struct rq *rq = (struct rq *)aee_cpu_rq(cpu);
if (rq)
return (struct task_struct *)(rq->curr);
return NULL;
}
unsigned long aee_get_swapper_pg_dir(void)
{
/* FIX ME: symbol in *ABS* section */
return 0;
}
unsigned long aee_get_kallsyms_addresses(void)
{
/* FIX ME: symbol in *UND* section */
return 0;
}
static void (*p__flush_dcache_area)(void *addr, size_t len);
void aee__flush_dcache_area(void *addr, size_t len)
{
if (p__flush_dcache_area) {
p__flush_dcache_area(addr, len);
return;
}
p__flush_dcache_area = (void *)aee_addr_find("__flush_dcache_area");
if (!p__flush_dcache_area) {
pr_info("%s failed", __func__);
return;
}
p__flush_dcache_area(addr, len);
}
raw_spinlock_t *p_logbuf_lock;
struct semaphore *p_console_sem;
void aee_zap_locks(void)
{
if (!p_logbuf_lock) {
p_logbuf_lock = (void *)aee_addr_find("logbuf_lock");
if (!p_logbuf_lock) {
aee_sram_printk("%s failed to get logbuf lock\n",
__func__);
return;
}
}
if (!p_console_sem) {
p_console_sem = (void *)aee_addr_find("console_sem");
if (!p_console_sem) {
aee_sram_printk("%s failed to get console_sem\n",
__func__);
return;
}
}
debug_locks_off();
/* If a crash is occurring, make sure we can't deadlock */
raw_spin_lock_init(p_logbuf_lock);
/* And make sure that we print immediately */
sema_init(p_console_sem, 1);
}
static raw_spinlock_t *p_die_lock;
void aee_reinit_die_lock(void)
{
if (!p_die_lock) {
p_die_lock = (void *)aee_addr_find("die_lock");
if (!p_die_lock) {
aee_sram_printk("%s failed to get die_lock\n",
__func__);
return;
}
}
/* If a crash is occurring, make sure we not deadlock */
raw_spin_lock_init(p_die_lock);
}
/* for aee_aed.ko */
static const char *(*p_arch_vma_name)(struct vm_area_struct *vma);
const char *aee_arch_vma_name(struct vm_area_struct *vma)
{
if (p_arch_vma_name)
return p_arch_vma_name(vma);
p_arch_vma_name = (void *)aee_addr_find("arch_vma_name");
if (p_arch_vma_name)
return p_arch_vma_name(vma);
pr_info("%s failed", __func__);
return NULL;
}
EXPORT_SYMBOL(aee_arch_vma_name);
#else /* #ifdef MODULE*/
/* for mrdump.ko */
void aee_show_regs(struct pt_regs *regs)
{
__show_regs(regs);
}
#ifdef CONFIG_ARM64
int aee_unwind_frame(struct task_struct *tsk, struct stackframe *frame)
{
return unwind_frame(tsk, frame);
}
#else
int aee_unwind_frame(struct stackframe *frame)
{
return unwind_frame(frame);
}
#endif
unsigned long aee_get_stext(void)
{
return (unsigned long)_stext;
}
unsigned long aee_get_etext(void)
{
return (unsigned long)_etext;
}
unsigned long aee_get_text(void)
{
return (unsigned long)_text;
}
#if defined(CONFIG_ARM64)
unsigned long aee_get_kimage_vaddr(void)
{
return (unsigned long)kimage_vaddr;
}
#endif
phys_addr_t aee_memblock_start_of_DRAM(void)
{
return memblock_start_of_DRAM();
}
phys_addr_t aee_memblock_end_of_DRAM(void)
{
return memblock_end_of_DRAM();
}
void aee_print_modules(void)
{
print_modules();
}
extern int save_modules(char *mbuf, int mbufsize);
/* MUST ensure called when preempt disabled already */
int aee_save_modules(char *mbuf, int mbufsize)
{
return save_modules(mbuf, mbufsize);
}
int get_HW_cpuid(void)
{
u64 mpidr;
mpidr = read_cpuid_mpidr();
return get_logical_index(mpidr & MPIDR_HWID_BITMASK);
}
u32 aee_log_buf_len_get(void)
{
return log_buf_len_get();
}
char *aee_log_buf_addr_get(void)
{
return log_buf_addr_get();
}
#ifdef __aarch64__
bool aee_on_irq_stack(unsigned long sp, struct stack_info *info)
{
return on_irq_stack(sp, info);
}
#endif
pgd_t *aee_pgd_offset_k(unsigned long addr)
{
return (pgd_t *)pgd_offset_k(addr);
}
unsigned long aee_cpu_rq(int cpu)
{
return (unsigned long)cpu_rq(cpu);
}
struct task_struct *aee_cpu_curr(int cpu)
{
return (struct task_struct *)cpu_curr(cpu);
}
unsigned long aee_get_swapper_pg_dir(void)
{
return (unsigned long)(&swapper_pg_dir);
}
extern const unsigned long kallsyms_addresses[] __weak;
unsigned long aee_get_kallsyms_addresses(void)
{
return (unsigned long)&kallsyms_addresses;
}
/* workaround for 32bit kernel, waiting for cache implement */
__weak void __flush_dcache_area(void *addr, size_t len)
{
pr_info("%s weak function", __func__);
}
extern void __flush_dcache_area(void *addr, size_t len);
void aee__flush_dcache_area(void *addr, size_t len)
{
__flush_dcache_area(addr, len);
}
void aee_zap_locks(void)
{
aee_wdt_zap_locks();
}
static raw_spinlock_t *p_die_lock;
void aee_reinit_die_lock(void)
{
if (!p_die_lock) {
p_die_lock = (void *)kallsyms_lookup_name("die_lock");
if (!p_die_lock) {
aee_sram_printk("%s failed to get die_lock\n",
__func__);
return;
}
}
/* If a crash is occurring, make sure we not deadlock */
raw_spin_lock_init(p_die_lock);
}
/* for aee_aed.ko */
const char *aee_arch_vma_name(struct vm_area_struct *vma)
{
return arch_vma_name(vma);
}
EXPORT_SYMBOL(aee_arch_vma_name);
#endif