mirror of
https://github.com/hardkernel/kernel_common_drivers.git
synced 2026-06-25 12:03:48 +09:00
88ea1e31f2
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>
472 lines
11 KiB
C
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, ®_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;
|
|
}
|