kernel_samsung_a34x-permissive/drivers/misc/mediatek/aee/mrdump/mrdump_panic.c

329 lines
8 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2015 MediaTek Inc.
*/
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/kdebug.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/reboot.h>
#include <linux/sched/clock.h>
#include <linux/slab.h>
#include <asm/cacheflush.h>
#include <asm/kexec.h>
#include <asm/memory.h>
#include <asm/stacktrace.h>
#include <asm/system_misc.h>
#include <mrdump.h>
#include <mt-plat/mboot_params.h>
#include "mrdump_mini.h"
#include "mrdump_private.h"
/* for arm_smccc_smc */
#include <linux/arm-smccc.h>
#include <uapi/linux/psci.h>
#ifdef CONFIG_SEC_DEBUG
#include <linux/sec_debug.h>
#endif
static inline unsigned long get_linear_memory_size(void)
{
return (unsigned long)high_memory - PAGE_OFFSET;
}
/* no export symbol to aee_exception_reboot, only used in exception flow */
/* PSCI v1.1 extended power state encoding for SYSTEM_RESET2 function */
#define PSCI_1_1_FN_SYSTEM_RESET2 0x84000012
#define PSCI_1_1_RESET2_TYPE_VENDOR_SHIFT 31
#define PSCI_1_1_RESET2_TYPE_VENDOR \
(1 << PSCI_1_1_RESET2_TYPE_VENDOR_SHIFT)
static void aee_exception_reboot(void)
{
struct arm_smccc_res res;
int opt1 = 1, opt2 = 0;
arm_smccc_smc(PSCI_1_1_FN_SYSTEM_RESET2,
PSCI_1_1_RESET2_TYPE_VENDOR | opt1,
opt2, 0, 0, 0, 0, 0, &res);
}
#if defined(CONFIG_RANDOMIZE_BASE) && defined(CONFIG_ARM64)
static inline void show_kaslr(void)
{
u64 const kaslr_offset = aee_get_kimage_vaddr() - KIMAGE_VADDR;
pr_notice("Kernel Offset: 0x%llx from 0x%lx\n",
kaslr_offset, KIMAGE_VADDR);
pr_notice("PHYS_OFFSET: 0x%llx\n", PHYS_OFFSET);
aee_rr_rec_kaslr_offset(kaslr_offset);
}
#else
static inline void show_kaslr(void)
{
pr_notice("Kernel Offset: disabled\n");
aee_rr_rec_kaslr_offset(0xd15ab1e);
}
#endif
static char nested_panic_buf[1024];
int aee_nested_printf(const char *fmt, ...)
{
va_list args;
static int total_len;
va_start(args, fmt);
total_len += vsnprintf(nested_panic_buf, sizeof(nested_panic_buf),
fmt, args);
va_end(args);
aee_sram_fiq_log(nested_panic_buf);
return total_len;
}
static int num_die;
int mrdump_common_die(u8 fiq_step, int reboot_reason, const char *msg,
struct pt_regs *regs)
{
int last_step;
int next_step;
num_die++;
last_step = aee_rr_curr_fiq_step();
if (num_die > 1) {
/* NESTED KE */
#if IS_ENABLED(CONFIG_ARM64)
aee_reinit_die_lock();
#endif
}
aee_nested_printf("num_die-%d, fiq_step-%d last_step-%d\n",
num_die, fiq_step, last_step);
/* if we were in nested ke now, then the if condition would be false */
if (last_step < AEE_FIQ_STEP_COMMON_DIE_START)
last_step = AEE_FIQ_STEP_COMMON_DIE_START - 1;
/* skip the works of last_step */
next_step = last_step + 1;
switch (next_step) {
case AEE_FIQ_STEP_COMMON_DIE_START:
aee_rr_rec_fiq_step(AEE_FIQ_STEP_COMMON_DIE_START);
__mrdump_create_oops_dump(reboot_reason, regs, msg);
mrdump_mini_ke_cpu_regs(regs);
/* FALLTHRU */
case AEE_FIQ_STEP_COMMON_DIE_LOCK:
aee_rr_rec_fiq_step(AEE_FIQ_STEP_COMMON_DIE_LOCK);
#if IS_ENABLED(CONFIG_ARM64)
aee_reinit_die_lock();
#endif
aee_zap_locks();
/* FALLTHRU */
case AEE_FIQ_STEP_COMMON_DIE_KASLR:
aee_rr_rec_fiq_step(AEE_FIQ_STEP_COMMON_DIE_KASLR);
show_kaslr();
aee_print_modules();
/* FALLTHRU */
case AEE_FIQ_STEP_COMMON_DIE_SCP:
aee_rr_rec_fiq_step(AEE_FIQ_STEP_COMMON_DIE_SCP);
aee_rr_rec_scp();
/* FALLTHRU */
case AEE_FIQ_STEP_COMMON_DIE_EMISC:
aee_rr_rec_fiq_step(AEE_FIQ_STEP_COMMON_DIE_EMISC);
mrdump_mini_add_extra_misc();
/* FALLTHRU */
case AEE_FIQ_STEP_COMMON_DIE_TRACE:
aee_rr_rec_fiq_step(AEE_FIQ_STEP_COMMON_DIE_TRACE);
switch (reboot_reason) {
case AEE_REBOOT_MODE_KERNEL_OOPS:
aee_show_regs(regs);
dump_stack();
break;
case AEE_REBOOT_MODE_KERNEL_PANIC:
#ifndef CONFIG_DEBUG_BUGVERBOSE
dump_stack();
#endif
break;
case AEE_REBOOT_MODE_HANG_DETECT:
aee_rr_rec_exp_type(AEE_EXP_TYPE_HANG_DETECT);
break;
default:
/* Don't print anything */
break;
}
/* FALLTHRU */
case AEE_FIQ_STEP_COMMON_DIE_CS:
aee_rr_rec_fiq_step(AEE_FIQ_STEP_COMMON_DIE_CS);
console_unlock();
/* FALLTHRU */
case AEE_FIQ_STEP_COMMON_DIE_DONE:
aee_rr_rec_fiq_step(AEE_FIQ_STEP_COMMON_DIE_DONE);
/* FALLTHRU */
default:
#ifdef CONFIG_SEC_DEBUG
sec_save_context(_THIS_CPU, regs);
sec_debug_dump_info();
#endif
#ifdef CONFIG_SEC_DEBUG_AUTO_COMMENT
dump_backtrace_auto_comment(regs, NULL);
#endif
aee_nested_printf("num_die-%d, fiq_step-%d, last_step-%d, next_step-%d\n",
num_die, fiq_step,
last_step, next_step);
aee_exception_reboot();
break;
}
return NOTIFY_DONE;
}
EXPORT_SYMBOL(mrdump_common_die);
int ipanic(struct notifier_block *this, unsigned long event, void *ptr)
{
struct pt_regs saved_regs;
aee_rr_rec_exp_type(AEE_EXP_TYPE_KE);
crash_setup_regs(&saved_regs, NULL);
#ifdef CONFIG_SEC_DEBUG
sec_upload_cause(ptr);
#endif
return mrdump_common_die(AEE_FIQ_STEP_KE_IPANIC_START,
AEE_REBOOT_MODE_KERNEL_PANIC,
"Kernel Panic", &saved_regs);
}
static int ipanic_die(struct notifier_block *self, unsigned long cmd, void *ptr)
{
struct die_args *dargs = (struct die_args *)ptr;
aee_rr_rec_exp_type(AEE_EXP_TYPE_KE);
#ifdef CONFIG_SEC_DEBUG
sec_upload_cause((void *)(dargs->str));
#endif
return mrdump_common_die(AEE_FIQ_STEP_KE_IPANIC_DIE,
AEE_REBOOT_MODE_KERNEL_OOPS,
"Kernel Oops", dargs->regs);
}
static struct notifier_block panic_blk = {
.notifier_call = ipanic,
};
static struct notifier_block die_blk = {
.notifier_call = ipanic_die,
};
static __init int mrdump_parse_chosen(struct mrdump_params *mparams)
{
struct device_node *node;
u32 reg[2];
const char *lkver, *ddr_rsv;
memset(mparams, 0, sizeof(struct mrdump_params));
node = of_find_node_by_path("/chosen");
if (node) {
if (of_property_read_u32_array(node, "mrdump,cblock",
reg, ARRAY_SIZE(reg)) == 0) {
mparams->cb_addr = reg[0];
mparams->cb_size = reg[1];
pr_notice("%s: mrdump_cbaddr=%x, mrdump_cbsize=%x\n",
__func__, mparams->cb_addr, mparams->cb_size);
}
if (of_property_read_string(node, "mrdump,lk", &lkver) == 0) {
strlcpy(mparams->lk_version, lkver,
sizeof(mparams->lk_version));
pr_notice("%s: lk version %s\n", __func__, lkver);
}
if (of_property_read_string(node, "mrdump,ddr_rsv",
&ddr_rsv) == 0) {
if (strcmp(ddr_rsv, "yes") == 0)
mparams->drm_ready = true;
pr_notice("%s: ddr reserve mode %s\n", __func__,
ddr_rsv);
}
return 0;
}
of_node_put(node);
pr_notice("%s: Can't find chosen node\n", __func__);
return -1;
}
#ifdef CONFIG_MODULES
/* Module notifier call back, update module info list */
static int mrdump_module_callback(struct notifier_block *nb,
unsigned long val, void *data)
{
if (val == MODULE_STATE_LIVE)
mrdump_modules_info(NULL, -1);
return NOTIFY_DONE;
}
static struct notifier_block mrdump_module_nb = {
.notifier_call = mrdump_module_callback,
};
#endif
static int __init mrdump_panic_init(void)
{
struct mrdump_params mparams = {};
if (!aee_is_enable()) {
pr_notice("%s: ipanic: mrdump is disable\n", __func__);
return 0;
}
mrdump_parse_chosen(&mparams);
#ifdef MODULE
mrdump_module_init_mboot_params();
#endif
mrdump_hw_init(mparams.drm_ready);
mrdump_cblock_init(mparams.cb_addr, mparams.cb_size);
if (mrdump_cblock == NULL) {
pr_notice("%s: MT-RAMDUMP no control block\n", __func__);
return -EINVAL;
}
mrdump_mini_init(&mparams);
if (strcmp(mparams.lk_version, MRDUMP_GO_DUMP) == 0) {
mrdump_full_init();
} else {
pr_notice("%s: Full ramdump disabled, version %s not matched.\n",
__func__, mparams.lk_version);
}
atomic_notifier_chain_register(&panic_notifier_list, &panic_blk);
register_die_notifier(&die_blk);
#ifdef CONFIG_MODULES
register_module_notifier(&mrdump_module_nb);
#endif
pr_debug("ipanic: startup\n");
return 0;
}
arch_initcall(mrdump_panic_init);
#ifdef MODULE
static void __exit mrdump_panic_exit(void)
{
atomic_notifier_chain_unregister(&panic_notifier_list, &panic_blk);
unregister_die_notifier(&die_blk);
unregister_module_notifier(&mrdump_module_nb);
pr_debug("ipanic: exit\n");
}
module_exit(mrdump_panic_exit);
#endif