| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386 |
- #include <linux/kobject.h>
- #include <linux/string.h>
- #include <linux/sysfs.h>
- #include <linux/init.h>
- #include <linux/sched.h>
- #include <linux/module.h>
- #include <linux/proc_fs.h>
- #include <linux/seq_file.h>
- #include <linux/delay.h>
- #include <linux/mm.h>
- #include <linux/sched.h>
- #include <asm/stacktrace.h>
- #include <asm/memory.h>
- #include <asm/traps.h>
- #include <linux/elf.h>
- #include <mach/wd_api.h>
- #include <smp.h>
- #include <mt-plat/aee.h>
- #include <mrdump.h>
- #include "aee-common.h"
- #define RR_PROC_NAME "reboot-reason"
- static struct proc_dir_entry *aee_rr_file;
- #define WDT_NORMAL_BOOT 0
- #define WDT_HW_REBOOT 1
- #define WDT_SW_REBOOT 2
- enum boot_reason_t {
- BR_POWER_KEY = 0,
- BR_USB,
- BR_RTC,
- BR_WDT,
- BR_WDT_BY_PASS_PWK,
- BR_TOOL_BY_PASS_PWK,
- BR_2SEC_REBOOT,
- BR_UNKNOWN,
- BR_KE_REBOOT
- };
- char boot_reason[][16] = { "XXXXXX", "XXXXXXX", "XXX", "XXX",
- "XXXXXX", "XXXXXXXXXXX", "XXXX", "XXXXXX", "XXXXXX" };
- int __weak aee_rr_reboot_reason_show(struct seq_file *m, void *v)
- {
- seq_puts(m, "mtk_ram_console not enabled.");
- return 0;
- }
- static int aee_rr_reboot_reason_proc_open(struct inode *inode, struct file *file)
- {
- return single_open(file, aee_rr_reboot_reason_show, NULL);
- }
- static const struct file_operations aee_rr_reboot_reason_proc_fops = {
- .open = aee_rr_reboot_reason_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- };
- void aee_rr_proc_init(struct proc_dir_entry *aed_proc_dir)
- {
- aee_rr_file = proc_create(RR_PROC_NAME,
- 0444, aed_proc_dir, &aee_rr_reboot_reason_proc_fops);
- if (aee_rr_file == NULL)
- LOGE("%s: Can't create rr proc entry\n", __func__);
- }
- EXPORT_SYMBOL(aee_rr_proc_init);
- void aee_rr_proc_done(struct proc_dir_entry *aed_proc_dir)
- {
- remove_proc_entry(RR_PROC_NAME, aed_proc_dir);
- }
- EXPORT_SYMBOL(aee_rr_proc_done);
- /* define /sys/bootinfo/powerup_reason */
- static ssize_t powerup_reason_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
- {
- int g_boot_reason = 0;
- char *br_ptr;
- br_ptr = strstr(saved_command_line, "boot_reason=");
- if (br_ptr != 0) {
- /* get boot reason */
- g_boot_reason = br_ptr[12] - '0';
- LOGE("g_boot_reason=%d\n", g_boot_reason);
- #ifdef CONFIG_MTK_RAM_CONSOLE
- if (aee_rr_last_fiq_step() != 0)
- g_boot_reason = BR_KE_REBOOT;
- #endif
- return sprintf(buf, "%s\n", boot_reason[g_boot_reason]);
- } else
- return 0;
- }
- static struct kobj_attribute powerup_reason_attr = __ATTR_RO(powerup_reason);
- struct kobject *bootinfo_kobj;
- EXPORT_SYMBOL(bootinfo_kobj);
- static struct attribute *bootinfo_attrs[] = {
- &powerup_reason_attr.attr,
- NULL
- };
- static struct attribute_group bootinfo_attr_group = {
- .attrs = bootinfo_attrs,
- };
- int ksysfs_bootinfo_init(void)
- {
- int error;
- bootinfo_kobj = kobject_create_and_add("bootinfo", NULL);
- if (!bootinfo_kobj)
- return -ENOMEM;
- error = sysfs_create_group(bootinfo_kobj, &bootinfo_attr_group);
- if (error)
- kobject_put(bootinfo_kobj);
- return error;
- }
- void ksysfs_bootinfo_exit(void)
- {
- kobject_put(bootinfo_kobj);
- }
- /* end sysfs bootinfo */
- static inline unsigned int get_linear_memory_size(void)
- {
- return (unsigned long)high_memory - PAGE_OFFSET;
- }
- 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 void print_error_msg(int len)
- {
- static char error_msg[][50] = { "Bottom unaligned", "Bottom out of kernel addr",
- "Top out of kernel addr", "Buf len not enough"
- };
- int tmp = (-len) - 1;
- aee_sram_fiq_log(error_msg[tmp]);
- }
- /*save stack as binary into buf,
- *return value
- -1: bottom unaligned
- -2: bottom out of kernel addr space
- -3 top out of kernel addr addr
- -4: buff len not enough
- >0: used length of the buf
- */
- int aee_dump_stack_top_binary(char *buf, int buf_len, unsigned long bottom, unsigned long top)
- {
- /*should check stack address in kernel range */
- if (bottom & 3)
- return -1;
- if (!((bottom >= (PAGE_OFFSET + THREAD_SIZE)) &&
- (bottom <= (PAGE_OFFSET + get_linear_memory_size())))) {
- return -2;
- }
- if (!((top >= (PAGE_OFFSET + THREAD_SIZE)) &&
- (top <= (PAGE_OFFSET + get_linear_memory_size())))) {
- return -3;
- }
- if (buf_len < top - bottom)
- return -4;
- memcpy((void *)buf, (void *)bottom, top - bottom);
- return top - bottom;
- }
- /* extern void mt_fiq_printf(const char *fmt, ...); */
- void *aee_excp_regs;
- static atomic_t nested_panic_time = ATOMIC_INIT(0);
- #ifdef __aarch64__
- #define FORMAT_LONG "%016lx "
- #else
- #define FORMAT_LONG "%08lx "
- #endif
- inline void aee_print_regs(struct pt_regs *regs)
- {
- int i;
- aee_nested_printf("[pt_regs]");
- for (i = 0; i < ELF_NGREG; i++)
- aee_nested_printf(FORMAT_LONG, ((unsigned long *)regs)[i]);
- aee_nested_printf("\n");
- }
- #define AEE_MAX_EXCP_FRAME 32
- inline void aee_print_bt(struct pt_regs *regs)
- {
- int i;
- unsigned long high, bottom, fp;
- struct stackframe cur_frame;
- struct pt_regs *excp_regs;
- bottom = regs->reg_sp;
- if (!virt_addr_valid(bottom)) {
- aee_nested_printf("invalid sp[%lx]\n", regs);
- return;
- }
- high = ALIGN(bottom, THREAD_SIZE);
- cur_frame.fp = regs->reg_fp;
- cur_frame.pc = regs->reg_pc;
- cur_frame.sp = regs->reg_sp;
- for (i = 0; i < AEE_MAX_EXCP_FRAME; i++) {
- fp = cur_frame.fp;
- if ((fp < bottom) || (fp >= (high + THREAD_SIZE))) {
- if (fp != 0)
- aee_nested_printf("fp(%lx)", fp);
- break;
- }
- unwind_frame(&cur_frame);
- if (!((cur_frame.pc >= (PAGE_OFFSET + THREAD_SIZE))
- && virt_addr_valid(cur_frame.pc)))
- break;
- if (in_exception_text(cur_frame.pc)) {
- #ifdef __aarch64__
- /* work around for unknown reason do_mem_abort stack abnormal */
- excp_regs = (void *)(cur_frame.fp + 0x10 + 0xa0);
- unwind_frame(&cur_frame); /* skip do_mem_abort & el1_da */
- #else
- excp_regs = (void *)(cur_frame.fp + 4);
- #endif
- cur_frame.pc = excp_regs->reg_pc;
- }
- aee_nested_printf("%p, ", (void *)cur_frame.pc);
- }
- aee_nested_printf("\n");
- }
- inline int aee_nested_save_stack(struct pt_regs *regs)
- {
- int len = 0;
- if (!virt_addr_valid(regs->reg_sp))
- return -1;
- aee_nested_printf("[%lx %lx]\n", regs->reg_sp, regs->reg_sp + 256);
- len = aee_dump_stack_top_binary(nested_panic_buf, sizeof(nested_panic_buf),
- regs->reg_sp, regs->reg_sp + 256);
- if (len > 0)
- aee_sram_fiq_save_bin(nested_panic_buf, len);
- else
- print_error_msg(len);
- return len;
- }
- int aee_in_nested_panic(void)
- {
- return (atomic_read(&nested_panic_time) &&
- ((aee_rr_curr_fiq_step() & ~(AEE_FIQ_STEP_KE_NESTED_PANIC - 1)) ==
- AEE_FIQ_STEP_KE_NESTED_PANIC));
- }
- static inline void aee_rec_step_nested_panic(int step)
- {
- if (step < 64)
- aee_rr_rec_fiq_step(AEE_FIQ_STEP_KE_NESTED_PANIC + step);
- }
- asmlinkage void aee_stop_nested_panic(struct pt_regs *regs)
- {
- struct thread_info *thread = current_thread_info();
- int len = 0;
- int timeout = 1000000;
- int res = 0, cpu = 0;
- struct wd_api *wd_api = NULL;
- struct pt_regs *excp_regs = NULL;
- int prev_fiq_step = aee_rr_curr_fiq_step();
- /* everytime enter nested_panic flow, add 8 */
- static int step_base = -8;
- step_base = step_base < 48 ? step_base + 8 : 56;
- aee_rec_step_nested_panic(step_base);
- local_irq_disable();
- aee_rec_step_nested_panic(step_base + 1);
- cpu = get_HW_cpuid();
- aee_rec_step_nested_panic(step_base + 2);
- /*nested panic may happens more than once on many/single cpus */
- if (atomic_read(&nested_panic_time) < 3)
- aee_nested_printf("\nCPU%dpanic%d@%d\n", cpu, nested_panic_time, prev_fiq_step);
- atomic_inc(&nested_panic_time);
- switch (atomic_read(&nested_panic_time)) {
- case 2:
- aee_print_regs(regs);
- aee_nested_printf("backtrace:");
- aee_print_bt(regs);
- break;
- /* must guarantee Only one cpu can run here */
- /* first check if thread valid */
- case 1:
- if (virt_addr_valid(thread) && virt_addr_valid(thread->regs_on_excp)) {
- excp_regs = thread->regs_on_excp;
- } else {
- /* if thread invalid, which means wrong sp or thread_info corrupted,
- check global aee_excp_regs instead */
- aee_nested_printf("invalid thread [%lx], excp_regs [%lx]\n", thread,
- aee_excp_regs);
- excp_regs = aee_excp_regs;
- }
- aee_nested_printf("Nested panic\n");
- if (excp_regs) {
- aee_nested_printf("Previous\n");
- aee_print_regs(excp_regs);
- }
- aee_nested_printf("Current\n");
- aee_print_regs(regs);
- /*should not print stack info. this may overwhelms ram console used by fiq */
- if (0 != in_fiq_handler()) {
- aee_nested_printf("in fiq handler\n");
- } else {
- /*Dump first panic stack */
- aee_nested_printf("Previous\n");
- if (excp_regs) {
- len = aee_nested_save_stack(excp_regs);
- aee_nested_printf("\nbacktrace:");
- aee_print_bt(excp_regs);
- }
- /*Dump second panic stack */
- aee_nested_printf("Current\n");
- if (virt_addr_valid(regs)) {
- len = aee_nested_save_stack(regs);
- aee_nested_printf("\nbacktrace:");
- aee_print_bt(regs);
- }
- }
- aee_rec_step_nested_panic(step_base + 5);
- ipanic_recursive_ke(regs, excp_regs, cpu);
- aee_rec_step_nested_panic(step_base + 6);
- /* we donot want a FIQ after this, so disable hwt */
- res = get_wd_api(&wd_api);
- if (res)
- aee_nested_printf("get_wd_api error\n");
- else
- wd_api->wd_aee_confirm_hwreboot();
- aee_rec_step_nested_panic(step_base + 7);
- break;
- default:
- break;
- }
- /* waiting for the WDT timeout */
- while (1) {
- /* output to UART directly to avoid printk nested panic */
- /* mt_fiq_printf("%s hang here%d\t", __func__, i++); */
- while (timeout--)
- udelay(1);
- timeout = 1000000;
- }
- }
|