1
0
mirror of https://github.com/physwizz/a155-U-u1.git synced 2025-10-28 16:15:46 +00:00
Files
a155-U-u1/kernel-5.10/drivers/samsung/debug/sec_debug_base.c
physwizz 99537be4e2 first
2024-03-11 06:53:12 +11:00

506 lines
13 KiB
C

/*
* Copyright (c) 2017 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <asm/cacheflush.h>
#include <asm/io.h>
#include <asm/stacktrace.h>
#include <linux/fdtable.h>
#include <linux/file.h>
#include <linux/input.h>
#include <linux/irq.h>
#include <linux/kallsyms.h>
#include <linux/kernel.h>
#include <linux/kernel_stat.h>
#include <linux/kexec.h>
#include <linux/kmsg_dump.h>
#include <linux/memblock.h>
#include <linux/module.h>
#include <linux/mount.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/ptrace.h>
#include <linux/reboot.h>
#include <linux/tick.h>
#include <linux/uaccess.h>
#include <linux/sched/mm.h>
#include <linux/sched/debug.h>
#include <linux/nmi.h>
#include <linux/sec_debug.h>
#include <linux/platform_device.h>
#include <linux/of_reserved_mem.h>
/* get time function to log */
#ifndef arch_irq_stat_cpu
#define arch_irq_stat_cpu(cpu) 0
#endif
#ifndef arch_irq_stat
#define arch_irq_stat() 0
#endif
static unsigned int __read_mostly debug_level;
module_param(debug_level, uint, 0440);
unsigned int __read_mostly reset_reason;
module_param(reset_reason, uint, 0440);
DEFINE_PER_CPU(unsigned char, coreregs_stored);
DEFINE_PER_CPU(struct pt_regs, sec_aarch64_core_reg);
DEFINE_PER_CPU(sec_debug_mmu_reg_t, sec_aarch64_mmu_reg);
void sec_debug_save_core_reg(void *v_regs)
{
struct pt_regs *regs = (struct pt_regs *)v_regs;
struct pt_regs *core_reg =
&per_cpu(sec_aarch64_core_reg, smp_processor_id());
if (!regs) {
asm volatile (
"stp x0, x1, [%0, #8 * 0]\n\t"
"stp x2, x3, [%0, #8 * 2]\n\t"
"stp x4, x5, [%0, #8 * 4]\n\t"
"stp x6, x7, [%0, #8 * 6]\n\t"
"stp x8, x9, [%0, #8 * 8]\n\t"
"stp x10, x11, [%0, #8 * 10]\n\t"
"stp x12, x13, [%0, #8 * 12]\n\t"
"stp x14, x15, [%0, #8 * 14]\n\t"
"stp x16, x17, [%0, #8 * 16]\n\t"
"stp x18, x19, [%0, #8 * 18]\n\t"
"stp x20, x21, [%0, #8 * 20]\n\t"
"stp x22, x23, [%0, #8 * 22]\n\t"
"stp x24, x25, [%0, #8 * 24]\n\t"
"stp x26, x27, [%0, #8 * 26]\n\t"
"stp x28, x29, [%0, #8 * 28]\n\t"
"stp x5, x6, [sp,#-32]!\n\t"
"str x7, [sp,#16]\n\t"
"mov x5, sp\n\t"
"stp x30, x5, [%0, #8 * 30]\n\t"
"adr x6, 1f\n\t"
"mrs x7, spsr_el1\n\t"
"stp x6, x7, [%0, #8 * 32]\n\t"
"ldr x7, [sp,#16]\n\t"
"ldp x5, x6, [sp],#32\n\t"
"1:"
:
: "r" (&core_reg->regs[0])
: "memory"
);
} else {
memcpy(core_reg, regs, sizeof(struct pt_regs));
}
return;
}
void sec_debug_save_mmu_reg(sec_debug_mmu_reg_t *mmu_reg)
{
asm("mrs x1, SCTLR_EL1\n\t" /* SCTLR_EL1 */
"str x1, [%0]\n\t"
"mrs x1, TTBR0_EL1\n\t" /* TTBR0_EL1 */
"str x1, [%0,#8]\n\t"
"mrs x1, TTBR1_EL1\n\t" /* TTBR1_EL1 */
"str x1, [%0,#16]\n\t"
"mrs x1, TCR_EL1\n\t" /* TCR_EL1 */
"str x1, [%0,#24]\n\t"
"mrs x1, ESR_EL1\n\t" /* ESR_EL1 */
"str x1, [%0,#32]\n\t"
"mrs x1, FAR_EL1\n\t" /* FAR_EL1 */
"str x1, [%0,#40]\n\t"
/* Don't populate AFSR0_EL1 and AFSR1_EL1 */
"mrs x1, CONTEXTIDR_EL1\n\t" /* CONTEXTIDR_EL1 */
"str x1, [%0,#48]\n\t"
"mrs x1, TPIDR_EL0\n\t" /* TPIDR_EL0 */
"str x1, [%0,#56]\n\t"
"mrs x1, TPIDRRO_EL0\n\t" /* TPIDRRO_EL0 */
"str x1, [%0,#64]\n\t"
"mrs x1, TPIDR_EL1\n\t" /* TPIDR_EL1 */
"str x1, [%0,#72]\n\t"
"mrs x1, MAIR_EL1\n\t" /* MAIR_EL1 */
"str x1, [%0,#80]\n\t" : /* output */
: "r"(mmu_reg) /* input */
: "%x1", "memory" /* clobbered register */
);
}
void sec_debug_init_mmu(void *unused)
{
sec_debug_save_mmu_reg(&per_cpu(sec_aarch64_mmu_reg, raw_smp_processor_id()));
}
int sec_save_context(int cpu, void *v_regs)
{
unsigned long flags;
struct pt_regs *regs = (struct pt_regs *)v_regs;
//struct pt_regs tmp;
if (cpu == _THIS_CPU) {
if (__this_cpu_read(coreregs_stored)) {
pr_emerg("skip context saved(CPU:%d)\n", smp_processor_id());
return 0;
}
} else {
if (per_cpu(coreregs_stored, cpu)) {
pr_emerg("skip context saved(CPU:%d)\n", cpu);
return 0;
}
}
local_irq_save(flags);
sec_debug_save_core_reg(regs);
if (cpu == _THIS_CPU) {
sec_debug_save_mmu_reg(&per_cpu(sec_aarch64_mmu_reg, smp_processor_id()));
} else {
sec_debug_save_mmu_reg(&per_cpu(sec_aarch64_mmu_reg, cpu));
}
//crash_setup_regs(&tmp, regs);
//crash_save_vmcoreinfo();
if (cpu == _THIS_CPU) {
//crash_save_cpu(&tmp, smp_processor_id());
__this_cpu_inc(coreregs_stored);
pr_emerg("context saved(CPU:%d)\n", smp_processor_id());
} else {
//crash_save_cpu(&tmp, cpu);
++per_cpu(coreregs_stored, cpu);
pr_emerg("context saved(CPU:%d)\n", cpu);
}
/* TO_DO : check cache flush api */
//__inner_flush_dcache_all();
local_irq_restore(flags);
return 0;
}
static void secdbg_show_debug_level(void)
{
pr_info("%s: debug_level=%d\n", __func__, debug_level);
}
bool is_debug_level_low(void)
{
if(debug_level == 0) {
pr_info("%s: YES\n", __func__);
return true;
}
pr_info("%s: NO, debug_level=%d\n", __func__, debug_level);
return false;
}
EXPORT_SYMBOL(is_debug_level_low);
#define task_contributes_to_load(task) ((task->state & TASK_UNINTERRUPTIBLE) != 0 && \
(task->flags & PF_FROZEN) == 0 && \
(task->state & TASK_NOLOAD) == 0)
static unsigned long secdbg_get_wchan(struct task_struct *p)
{
unsigned long entry = 0;
unsigned int skip = 0;
if (p != current)
skip = 1; /* to skip __switch_to */
stack_trace_save_tsk(p, &entry, 1, skip);
return entry;
}
static void sec_dump_one_task_info(struct task_struct *tsk, bool is_main)
{
char state_array[] = {'R', 'S', 'D', 'T', 't', 'X', 'Z', 'P', 'x', 'K', 'W', 'I', 'N'};
unsigned char idx = 0;
unsigned long state;
unsigned long wchan;
unsigned long pc = 0;
char symname[KSYM_NAME_LEN];
if ((tsk == NULL) || !try_get_task_stack(tsk))
return;
state = tsk->state | tsk->exit_state;
pc = KSTK_EIP(tsk);
while (state) {
idx++;
state >>= 1;
}
wchan = secdbg_get_wchan(tsk);
snprintf(symname, KSYM_NAME_LEN, "%ps", wchan);
touch_softlockup_watchdog();
pr_info("%8d %8d %8d %16lld %c(%d) %3d %16lx %16lx %16lx %c %16s [%s]\n",
tsk->pid, (int)(tsk->utime), (int)(tsk->stime),
tsk->se.exec_start, state_array[idx], (int)(tsk->state),
task_cpu(tsk), wchan, pc, (unsigned long)tsk,
is_main ? '*' : ' ', tsk->comm, symname);
if (tsk->on_cpu && tsk->on_rq && tsk->cpu != smp_processor_id())
return;
if (tsk->state == TASK_RUNNING || tsk->state == TASK_WAKING ||
task_contributes_to_load(tsk)) {
dump_backtrace(NULL, tsk, KERN_DEFAULT);
}
}
static inline struct task_struct *get_next_thread(struct task_struct *tsk)
{
return container_of(tsk->thread_group.next,
struct task_struct,
thread_group);
}
static void sec_dump_task_info(void)
{
struct task_struct *frst_tsk;
struct task_struct *curr_tsk;
struct task_struct *frst_thr;
struct task_struct *curr_thr;
pr_info("\n");
pr_info(" current proc : %d %s\n", current->pid, current->comm);
pr_info(" ----------------------------------------------------------------------------------------------------------------------------\n");
pr_info(" pid uTime(ms) sTime(ms) exec(ns) stat cpu wchan user_pc task_struct comm sym_wchan\n");
pr_info(" ----------------------------------------------------------------------------------------------------------------------------\n");
/* processes */
frst_tsk = &init_task;
curr_tsk = frst_tsk;
while (curr_tsk != NULL) {
sec_dump_one_task_info(curr_tsk, true);
/* threads */
if (curr_tsk->thread_group.next != NULL) {
frst_thr = get_next_thread(curr_tsk);
curr_thr = frst_thr;
if (frst_thr != curr_tsk) {
while (curr_thr != NULL) {
sec_dump_one_task_info(curr_thr, false);
curr_thr = get_next_thread(curr_thr);
if (curr_thr == curr_tsk)
break;
}
}
}
curr_tsk = container_of(curr_tsk->tasks.next,
struct task_struct, tasks);
if (curr_tsk == frst_tsk)
break;
}
pr_info(" ----------------------------------------------------------------------------------------------------------------------------\n");
}
static void sec_dump_irq_info(void)
{
int i, cpu;
u64 sum = 0;
for_each_possible_cpu(cpu)
sum += kstat_cpu_irqs_sum(cpu);
sum += arch_irq_stat();
pr_info("<irq info>\n");
pr_info("----------------------------------------------------------\n");
pr_info("sum irq : %llu", sum);
pr_info("----------------------------------------------------------\n");
for_each_irq_nr(i) {
struct irq_desc *desc = irq_to_desc(i);
unsigned int irq_stat = 0;
const char *name;
if (!desc)
continue;
for_each_possible_cpu(cpu)
irq_stat += *per_cpu_ptr(desc->kstat_irqs, cpu);
if (!irq_stat)
continue;
if (desc->action && desc->action->name)
name = desc->action->name;
else
name = "???";
pr_info("irq-%-4d(hwirq-%-3d) : %8u %s\n",
i, (int)desc->irq_data.hwirq, irq_stat, name);
}
}
void sec_debug_dump_info(struct pt_regs *regs)
{
sec_save_context(_THIS_CPU, regs);
sec_dump_task_info();
sec_dump_irq_info();
}
EXPORT_SYMBOL(sec_debug_dump_info);
static int set_reset_reason_proc_show(struct seq_file *m, void *v)
{
if (reset_reason == RR_S)
seq_puts(m, "SPON\n");
else if (reset_reason == RR_W)
seq_puts(m, "WPON\n");
else if (reset_reason == RR_D)
seq_puts(m, "DPON\n");
else if (reset_reason == RR_K)
seq_puts(m, "KPON\n");
else if (reset_reason == RR_M)
seq_puts(m, "MPON\n");
else if (reset_reason == RR_P)
seq_puts(m, "PPON\n");
else if (reset_reason == RR_R)
seq_puts(m, "RPON\n");
else if (reset_reason == RR_B)
seq_puts(m, "BPON\n");
else if (reset_reason == RR_T)
seq_puts(m, "TPON\n");
else if (reset_reason == RR_C)
seq_puts(m, "CPON\n");
else
seq_puts(m, "NPON\n");
return 0;
}
static int sec_reset_reason_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, set_reset_reason_proc_show, NULL);
}
static const struct proc_ops sec_reset_reason_proc_fops = {
.proc_open = sec_reset_reason_proc_open,
.proc_read = seq_read,
.proc_lseek = seq_lseek,
.proc_release = single_release,
};
static int sec_debug_reset_reason_store_lastkmsg_proc_show(struct seq_file *m, void *v)
{
if (reset_reason == RR_K || reset_reason == RR_D || reset_reason == RR_P)
seq_puts(m, "1\n");
else
seq_puts(m, "0\n");
return 0;
}
static int sec_debug_reset_reason_store_lastkmsg_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, sec_debug_reset_reason_store_lastkmsg_proc_show, NULL);
}
static const struct proc_ops sec_debug_reset_reason_store_lastkmsg_proc_fops = {
.proc_open = sec_debug_reset_reason_store_lastkmsg_proc_open,
.proc_read = seq_read,
.proc_lseek = seq_lseek,
.proc_release = single_release,
};
static int sec_debug_reset_reason_init(void)
{
struct proc_dir_entry *entry;
entry = proc_create("reset_reason", 0400, NULL,
&sec_reset_reason_proc_fops);
if (!entry)
return -ENOMEM;
entry = proc_create("store_lastkmsg", 0400, NULL,
&sec_debug_reset_reason_store_lastkmsg_proc_fops);
if (!entry)
return -ENOMEM;
return 0;
}
struct platform_device *secdbg_pdev;
EXPORT_SYMBOL(secdbg_pdev);
extern int sec_debug_log_init(void);
extern int secdbg_init_log_init(void);
extern int secdbg_pmsg_log_init(void);
#if IS_ENABLED(CONFIG_SEC_DEBUG_AUTO_COMMENT)
extern int sec_debug_auto_comment_init(void);
#endif
#if IS_ENABLED(CONFIG_SEC_DEBUG_HISTORY_LOG)
extern int sec_debug_history_init(void);
#endif
#if IS_ENABLED(CONFIG_SEC_DEBUG_EXTRA_INFO)
//extern int secdbg_extra_info_init(void);
//extern int secdbg_hw_param_init(void);
#endif
static int sec_debug_probe(struct platform_device *pdev)
{
pr_info("%s: start\n", __func__);
secdbg_pdev = pdev;
sec_debug_log_init();
secdbg_init_log_init();
secdbg_pmsg_log_init();
sec_debug_reset_reason_init();
#if IS_ENABLED(CONFIG_SEC_DEBUG_AUTO_COMMENT)
sec_debug_auto_comment_init();
#endif
#if IS_ENABLED(CONFIG_SEC_DEBUG_HISTORY_LOG)
sec_debug_history_init();
#endif
#if IS_ENABLED(CONFIG_SEC_DEBUG_EXTRA_INFO)
//secdbg_extra_info_init();
//secdbg_hw_param_init();
#endif
smp_call_function(sec_debug_init_mmu, NULL, 1);
sec_debug_init_mmu(NULL);
secdbg_show_debug_level();
return 0;
}
static const struct of_device_id sec_debug_of_match[] = {
{ .compatible = "samsung,sec_debug" },
{},
};
MODULE_DEVICE_TABLE(of, sec_debug_of_match);
static struct platform_driver sec_debug_driver = {
.probe = sec_debug_probe,
.driver = {
.name = "sec_debug_base",
.of_match_table = of_match_ptr(sec_debug_of_match),
},
};
static int __init sec_debug_init(void)
{
return platform_driver_register(&sec_debug_driver);
}
core_initcall(sec_debug_init);
static void __exit sec_debug_exit(void)
{
platform_driver_unregister(&sec_debug_driver);
}
module_exit(sec_debug_exit);
MODULE_DESCRIPTION("Samsung Debug driver");
MODULE_LICENSE("GPL v2");