// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2015 MediaTek Inc. */ #include #include #include #include #include #include #include "../../../../kernel/sched/sched.h" #include #include #include #include #include #include #include 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