Files
kernel_common_drivers/drivers/debug/ftrace_ramoops.c
T
yao zhang1 88ea1e31f2 license: Fix amlogic drivers license. [1/1]
PD#SWPL-217731

Problem:
Fix amlogic license.

Solution:
Fix amlogic license.

Verify:
all chip

Change-Id: I36cc711e44a201ec944c6b0b54c0d21ab671b068
Signed-off-by: yao zhang1 <yao.zhang1@amlogic.com>
2025-09-25 03:48:58 -07:00

472 lines
11 KiB
C

// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2025 Amlogic, Inc. All rights reserved.
*/
#define SKIP_LOCKUP_CHECK
#include <linux/init.h>
#include <linux/module.h>
#include <linux/irqflags.h>
#include <linux/percpu.h>
#include <linux/ftrace.h>
#include <linux/kprobes.h>
#include <linux/pm_domain.h>
#include <linux/workqueue.h>
#include <trace/hooks/sched.h>
#include <linux/amlogic/aml_iotrace.h>
#include <linux/amlogic/gki_module.h>
#include <trace/events/rwmmio.h>
#include <linux/of_address.h>
#include <linux/scs.h>
static DEFINE_PER_CPU(int, en);
#define IRQ_D 1
#define MAX_DETECT_REG 10
#define MAX_STACK_DEPTH 6
/*
* bit0: skip vdec-core,vdec irqthread read/write
* bit1: skip frc tasklet read/write
* bit2: skip vsync isr read/write
* bit3: skip amvecm isr read/write
* bit4: skip usb isr read/write
*/
#define IO_BLACKLIST_VDEC 0x1
#define IO_BLACKLIST_FRC 0x2
#define IO_BLACKLIST_VSYNC 0x4
#define IO_BLACKLIST_AMVECM 0x8
#define IO_BLACKLIST_USB 0x10
static int ramoops_io_blacklist = IO_BLACKLIST_AMVECM;
static int ramoops_io_blacklist_setup(char *buf)
{
if (!buf)
return -EINVAL;
if (kstrtoint(buf, 0, &ramoops_io_blacklist)) {
pr_err("ramoops_io_blacklist error: %s\n", buf);
return -EINVAL;
}
return 1;
}
__setup("ramoops_io_blacklist=", ramoops_io_blacklist_setup);
static unsigned int check_reg[MAX_DETECT_REG];
static unsigned int check_mask[MAX_DETECT_REG];
static unsigned int *virt_addr[MAX_DETECT_REG];
unsigned long old_val_reg[MAX_DETECT_REG];
DEFINE_PER_CPU(bool, frc_iotrace_cut);
EXPORT_PER_CPU_SYMBOL_GPL(frc_iotrace_cut);
DEFINE_PER_CPU(bool, vsync_iotrace_cut);
EXPORT_PER_CPU_SYMBOL_GPL(vsync_iotrace_cut);
DEFINE_PER_CPU(bool, amvecm_iotrace_cut);
EXPORT_PER_CPU_SYMBOL_GPL(amvecm_iotrace_cut);
DEFINE_PER_CPU(bool, usb_iotrace_cut);
EXPORT_PER_CPU_SYMBOL_GPL(usb_iotrace_cut);
#if 0
static struct delayed_work find_vdec_pid_work;
static pid_t vdec_core_pid, vdec_irqthread_pid0, vdec_irqthread_pid1;
static int retry_times;
static void find_vdec_pid(struct work_struct *work)
{
struct task_struct *task;
for_each_process(task) {
if (!task->mm) {
if (!vdec_core_pid && !strncmp(task->comm, "vdec-core", 16))
vdec_core_pid = task->pid;
if (!vdec_irqthread_pid0 && strstr(task->comm, "vdec-0"))
vdec_irqthread_pid0 = task->pid;
if (!vdec_irqthread_pid1 && strstr(task->comm, "vdec-1"))
vdec_irqthread_pid1 = task->pid;
}
}
if (!vdec_core_pid || !vdec_irqthread_pid0 || !vdec_irqthread_pid1) {
if (retry_times++ > 3)
//cancel_delayed_work(&find_vdec_pid_work);
else
queue_delayed_work(system_wq, &find_vdec_pid_work, retry_times * 10 * HZ);
} else {
//cancel_delayed_work(&find_vdec_pid_work);
}
}
static bool is_vdec_pid(void)
{
if (current->pid == vdec_core_pid || current->pid == vdec_irqthread_pid0 ||
current->pid == vdec_irqthread_pid1)
return true;
else
return false;
}
#endif
static bool is_in_frc_tasklet(void)
{
unsigned int cpu = raw_smp_processor_id();
return per_cpu(frc_iotrace_cut, cpu);
}
static bool is_in_vsync_isr(void)
{
unsigned int cpu = raw_smp_processor_id();
return per_cpu(vsync_iotrace_cut, cpu);
}
static bool is_in_amvecm_isr(void)
{
unsigned int cpu = raw_smp_processor_id();
return per_cpu(amvecm_iotrace_cut, cpu);
}
static bool is_in_usb_isr(void)
{
unsigned int cpu = raw_smp_processor_id();
return per_cpu(usb_iotrace_cut, cpu);
}
static int reg_check_panic;
static bool reg_check_flag;
static int reg_check_panic_setup(char *buf)
{
if (!buf)
return -EINVAL;
if (kstrtoint(buf, 0, &reg_check_panic)) {
pr_err("reg_check_panic error: %s\n", buf);
return -EINVAL;
}
return 1;
}
__setup("reg_check_panic=", reg_check_panic_setup);
void reg_check_init(void)
{
int i;
unsigned int *virt_tmp[MAX_DETECT_REG] = {NULL};
memcpy(virt_tmp, virt_addr, sizeof(virt_addr));
for (i = 0; i < MAX_DETECT_REG; i++)
rcu_assign_pointer(virt_addr[i], NULL);
synchronize_rcu();
for (i = 0; i < MAX_DETECT_REG; i++) {
if (virt_tmp[i])
iounmap(virt_tmp[i]);
else
break;
}
for (i = 0; i < MAX_DETECT_REG; i++) {
if (check_reg[i]) {
virt_addr[i] = (unsigned int *)ioremap(check_reg[i], sizeof(unsigned long));
if (!virt_addr[i]) {
pr_err("Unable to map reg 0x%x\n", check_reg[i]);
return;
}
pr_info("reg 0x%x has been mapped to 0x%px\n", check_reg[i], virt_addr[i]);
} else {
break;
}
}
}
void reg_check_func(void)
{
unsigned int val;
unsigned long tmp;
unsigned int i = 0;
unsigned int *tmp_addr;
rcu_read_lock();
while (i < MAX_DETECT_REG && virt_addr[i]) {
tmp_addr = rcu_dereference(virt_addr[i]);
if (old_val_reg[i] != -1) {
val = *tmp_addr;
if ((val & check_mask[i]) != (old_val_reg[i] & check_mask[i])) {
tmp = old_val_reg[i];
old_val_reg[i] = val;
pr_err("phys_addr:0x%x new_val=0x%x old_val=0x%lx\n",
check_reg[i], val, tmp);
if (!reg_check_panic)
dump_stack();
else
panic("reg_check_panic");
}
} else {
old_val_reg[i] = *tmp_addr;
}
i++;
}
rcu_read_unlock();
}
static int check_reg_setup(char *ptr)
{
char *str_entry;
char *str = (char *)ptr;
unsigned int tmp;
unsigned int i = 0, ret;
do {
str_entry = strsep(&str, ",");
if (str_entry) {
ret = kstrtou32(str_entry, 16, &tmp);
if (ret)
return -1;
pr_info("check_reg: 0x%x\n", tmp);
check_reg[i] = tmp;
old_val_reg[i++] = -1;
}
} while (str_entry && i < MAX_DETECT_REG);
reg_check_flag = true;
return 1;
}
__setup("check_reg=", check_reg_setup);
static int check_mask_setup(char *ptr)
{
char *str_entry;
char *str = (char *)ptr;
unsigned int tmp;
unsigned int i = 0, ret;
do {
str_entry = strsep(&str, ",");
if (str_entry) {
ret = kstrtou32(str_entry, 16, &tmp);
if (ret)
return -1;
pr_info("check_mask: 0x%x\n", tmp);
check_mask[i++] = tmp;
}
} while (str_entry && i < MAX_DETECT_REG);
return 1;
}
__setup("check_mask=", check_mask_setup);
#define REG_MAX_NUM 5
static struct resource gic_mem[REG_MAX_NUM];
static char *gic_compatible[] = {
"arm,gic-400",
"arm,arm11mp-gic",
"arm,arm1176jzf-devchip-gic",
"arm,cortex-a15-gic",
"arm,cortex-a9-gic",
"arm,cortex-a7-gic",
"arm,p1390"
};
static int gic_reg_num;
static void aml_ramoops_filter_dt(void)
{
struct device_node *node_gic = NULL;
int i, ret = 0;
for (i = 0; i < ARRAY_SIZE(gic_compatible); i++) {
node_gic = of_find_compatible_node(NULL, NULL, gic_compatible[i]);
if (node_gic)
break;
}
if (node_gic) {
for (i = 0; i < REG_MAX_NUM; i++) {
ret = of_address_to_resource(node_gic, i, &gic_mem[i]);
if (ret)
break;
}
gic_reg_num = i;
}
}
static int is_filter_reg(unsigned int reg)
{
int i;
/* filter the gic register. */
for (i = 0; i < gic_reg_num; i++)
if (reg >= gic_mem[i].start && reg <= gic_mem[i].end)
return 1;
return 0;
}
void __nocfi pstore_io_save(unsigned long reg, unsigned long val, unsigned int flag,
unsigned long *irq_flags)
{
struct iotrace_record rec = {
.type = flag,
};
int cpu = raw_smp_processor_id();
if (!ramoops_ftrace_en || !(ramoops_trace_mask & TRACE_MASK_IO))
return;
#if 0
if (ramoops_io_blacklist) {
if ((ramoops_io_blacklist & IO_BLACKLIST_VDEC && is_vdec_pid() && in_task()) ||
(ramoops_io_blacklist & IO_BLACKLIST_FRC && (is_in_frc_tasklet() &&
!in_irq())) ||
(ramoops_io_blacklist & IO_BLACKLIST_VSYNC && is_in_vsync_isr()) ||
(ramoops_io_blacklist & IO_BLACKLIST_AMVECM && is_in_amvecm_isr()) ||
(ramoops_io_blacklist & IO_BLACKLIST_USB && is_in_usb_isr()))
return;
}
#else
if (ramoops_io_blacklist) {
if ((ramoops_io_blacklist & IO_BLACKLIST_FRC && (is_in_frc_tasklet() &&
!in_irq())) ||
(ramoops_io_blacklist & IO_BLACKLIST_VSYNC && is_in_vsync_isr()) ||
(ramoops_io_blacklist & IO_BLACKLIST_AMVECM && is_in_amvecm_isr()) ||
(ramoops_io_blacklist & IO_BLACKLIST_USB && is_in_usb_isr()))
return;
}
#endif
if ((flag == RECORD_TYPE_IO_R || flag == RECORD_TYPE_IO_W) && IRQ_D)
local_irq_save(*irq_flags);
rec.reg = (unsigned int)page_to_phys(vmalloc_to_page((const void *)reg)) +
offset_in_page(reg);
rec.reg_val = (unsigned int)val;
if (reg_check_flag && flag == RECORD_TYPE_IO_W_END)
reg_check_func();
if (ramoops_io_stack) {
unsigned long stack_entries[MAX_STACK_DEPTH];
stack_trace_save(stack_entries, ARRAY_SIZE(stack_entries), 2 + ramoops_io_skip);
if (flag == RECORD_TYPE_IO_W || flag == RECORD_TYPE_IO_R) {
rec.ip = stack_entries[0];
rec.parent_ip = stack_entries[1];
} else {
rec.ip = stack_entries[4];
rec.parent_ip = stack_entries[5];
}
}
cpu = raw_smp_processor_id();
if (unlikely(oops_in_progress) || unlikely(per_cpu(en, cpu))) {
if ((flag == RECORD_TYPE_IO_R || flag == RECORD_TYPE_IO_W) && IRQ_D)
local_irq_restore(*irq_flags);
return;
}
per_cpu(en, cpu) = 1;
if (!is_filter_reg(rec.reg))
aml_pstore_write(AML_PSTORE_TYPE_IO, (void *)&rec, irqs_disabled_flags(*irq_flags),
flag);
per_cpu(en, cpu) = 0;
if ((flag == RECORD_TYPE_IO_R_END || flag == RECORD_TYPE_IO_W_END) &&
IRQ_D)
local_irq_restore(*irq_flags);
}
EXPORT_SYMBOL(pstore_io_save);
#ifdef CONFIG_ANDROID_VENDOR_HOOKS
static DEFINE_PER_CPU(unsigned long, irqflag);
static void __nocfi rwmmio_write_hook(void *data, unsigned long caller_addr,
unsigned long caller_addr0, u64 val, u8 width,
volatile void __iomem *addr)
{
if (!ramoops_ftrace_en)
return;
preempt_iotrace_enter();
pstore_ftrace_io_wr((unsigned long)addr, val, per_cpu(irqflag, raw_smp_processor_id()));
}
static void __nocfi rwmmio_post_write_hook(void *data, unsigned long caller_addr,
unsigned long caller_addr0, u64 val, u8 width,
volatile void __iomem *addr)
{
if (!ramoops_ftrace_en)
return;
if (unlikely(!iotrace_count()))
return;
pstore_ftrace_io_wr_end((unsigned long)addr, val, per_cpu(irqflag, raw_smp_processor_id()));
preempt_iotrace_exit();
}
static void __nocfi rwmmio_read_hook(void *data, unsigned long caller_addr,
unsigned long caller_addr0, u8 width,
const volatile void __iomem *addr)
{
if (!ramoops_ftrace_en)
return;
preempt_iotrace_enter();
pstore_ftrace_io_rd((unsigned long)addr, per_cpu(irqflag, raw_smp_processor_id()));
}
static void __nocfi rwmmio_post_read_hook(void *data, unsigned long caller_addr,
unsigned long caller_addr0, u64 val, u8 width,
const volatile void __iomem *addr)
{
if (!ramoops_ftrace_en)
return;
if (unlikely(!iotrace_count()))
return;
pstore_ftrace_io_rd_end((unsigned long)addr, val, per_cpu(irqflag, raw_smp_processor_id()));
preempt_iotrace_exit();
}
#endif
int ftrace_ramoops_init(void)
{
if (reg_check_flag)
reg_check_init();
aml_ramoops_filter_dt();
#ifdef CONFIG_ANDROID_VENDOR_HOOKS
register_trace_rwmmio_write(rwmmio_write_hook, NULL);
register_trace_rwmmio_post_write(rwmmio_post_write_hook, NULL);
register_trace_rwmmio_read(rwmmio_read_hook, NULL);
register_trace_rwmmio_post_read(rwmmio_post_read_hook, NULL);
#endif
#if 0
INIT_DELAYED_WORK(&find_vdec_pid_work, find_vdec_pid);
queue_delayed_work(system_wq, &find_vdec_pid_work, 10 * HZ);
#endif
return 0;
}