inline void aee_print_regs(struct pt_regs *regs) { aee_nested_printf("[r0-r15,cpsr]%08lx %08lx %08lx %08lx %08lx %08lx %08lx " "%08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", regs->ARM_r0, regs->ARM_r1, regs->ARM_r2, regs->ARM_r3, regs->ARM_r4, regs->ARM_r5, regs->ARM_r6, regs->ARM_r7, regs->ARM_r8, regs->ARM_r9, regs->ARM_r10, regs->ARM_fp, regs->ARM_ip, regs->ARM_sp, regs->ARM_lr, regs->ARM_pc, regs->ARM_cpsr); }
inline void aee_print_bt(struct pt_regs *regs) { int i; unsigned long high, bottom, fp; struct stackframe cur_frame; struct pt_regs *exp_regs; bottom = regs->ARM_sp; if (!virt_addr_valid(bottom)) { aee_nested_printf("invalid sp[%x]\n", regs); return; } high = ALIGN(bottom, THREAD_SIZE); cur_frame.lr = regs->ARM_lr; cur_frame.fp = regs->ARM_fp; cur_frame.pc = regs->ARM_pc; for (i = 0; i < AEE_MAX_EXCP_FRAME; i++) { fp = cur_frame.fp; if ((fp < (bottom + 12)) || ((fp + 4) >= (high + 8192))) { if (fp != 0) aee_nested_printf("fp(%x)", fp); break; } cur_frame.fp = *(unsigned long *)(fp - 12); cur_frame.lr = *(unsigned long *)(fp - 4); cur_frame.pc = *(unsigned long *)fp; if (! ((cur_frame.lr >= (PAGE_OFFSET + THREAD_SIZE)) && virt_addr_valid(cur_frame.lr))) break; if (in_exception_text(cur_frame.pc)) { exp_regs = (struct pt_regs *)(fp + 4); cur_frame.lr = exp_regs->ARM_pc; } aee_nested_printf("%08lx, ", cur_frame.lr); } aee_nested_printf("\n"); return; }
struct ipanic_header *ipanic_header(void) { int i; struct ipanic_data_header *dheader; int next_offset; if (iheader) return iheader; iheader = &ipanic_hdr; iheader->magic = AEE_IPANIC_MAGIC; iheader->version = AEE_IPANIC_PHDR_VERSION; if (ipanic_msdc_info(iheader)) { LOGE("ipanic initialize msdc fail."); aee_nested_printf("$"); return NULL; } iheader->size = sizeof(struct ipanic_header); iheader->datas = 0; #if 1 iheader->dhblk = ALIGN(sizeof(struct ipanic_data_header), iheader->blksize); #else iheader->dhblk = 0; #endif next_offset = ALIGN(sizeof(struct ipanic_header), iheader->blksize); for (i = IPANIC_DT_HEADER + 1; i < IPANIC_DT_RESERVED31; i++) { dheader = &iheader->data_hdr[i]; dheader->type = i; dheader->valid = 0; dheader->used = 0; strncpy(dheader->name, ipanic_dt_ops[i].string, 32); if (ipanic_dt_active(i) && ipanic_dt_ops[i].size) { dheader->encrypt = ipanic_dt_encrypt(i); dheader->offset = next_offset + iheader->dhblk; dheader->total = ALIGN(ipanic_dt_ops[i].size, iheader->blksize); if (iheader->partsize < (dheader->offset + dheader->total)) { LOGW("skip %s[%x@%x>%x]\n", dheader->name, dheader->total, dheader->offset, iheader->partsize); dheader->offset = INT_MAX; dheader->total = 0; continue; } next_offset += dheader->total + iheader->dhblk; } else { dheader->offset = INT_MAX; dheader->total = 0; } } ipanic_header_to_sd(0); return iheader; }
inline int aee_nested_save_stack(struct pt_regs *regs) { int len = 0; if (!virt_addr_valid(regs->ARM_sp)) return -1; aee_nested_printf("[%08lx %08lx]\n", regs->ARM_sp, regs->ARM_sp + 256); len = aee_dump_stack_top_binary(nested_panic_buf, sizeof(nested_panic_buf), regs->ARM_sp, regs->ARM_sp + 256); if (len > 0) aee_sram_fiq_save_bin(nested_panic_buf, len); else print_error_msg(len); return len; }
struct ipanic_header *ipanic_header_from_sd(unsigned int offset, unsigned int magic) { struct ipanic_data_header *dheader; int dt; char str[256]; size_t size = 0; struct ipanic_header *header; struct ipanic_data_header dheader_header = { .type = IPANIC_DT_HEADER, .offset = offset, .used = sizeof(struct ipanic_header), }; header = (struct ipanic_header *)ipanic_data_from_sd(&dheader_header, 0); if (IS_ERR_OR_NULL((void *)header)) { LOGD("read header failed[%ld]\n", PTR_ERR((void *)header)); header = NULL; } else if (header->magic != magic) { LOGD("no ipanic data[%x]\n", header->magic); kfree(header); header = NULL; ipanic_erase(); } else { for (dt = IPANIC_DT_HEADER + 1; dt < IPANIC_DT_RESERVED31; dt++) { dheader = &header->data_hdr[dt]; if (dheader->valid) { size += snprintf(str + size, 256 - size, "%s[%x@%x],", dheader->name, dheader->used, dheader->offset); } } LOGD("ipanic data available^v^%s^v^\n", str); } return header; } struct aee_oops *ipanic_oops_from_sd(void) { struct aee_oops *oops = NULL; struct ipanic_header *hdr = NULL; struct ipanic_data_header *dheader; char *data; int i; hdr = ipanic_header_from_sd(0, AEE_IPANIC_MAGIC); if (hdr == NULL) { return NULL; } oops = aee_oops_create(AE_DEFECT_FATAL, AE_KE, IPANIC_MODULE_TAG); if (oops == NULL) { LOGE("%s: can not allocate buffer\n", __func__); return NULL; } for (i = IPANIC_DT_HEADER + 1; i < IPANIC_DT_RESERVED31; i++) { dheader = &hdr->data_hdr[i]; if (dheader->valid == 0) { continue; } data = ipanic_data_from_sd(dheader, 1); if (data) { switch (i) { case IPANIC_DT_KERNEL_LOG: oops->console = data; oops->console_len = dheader->used; break; case IPANIC_DT_MINI_RDUMP: oops->mini_rdump = data; oops->mini_rdump_len = dheader->used; break; case IPANIC_DT_MAIN_LOG: oops->android_main = data; oops->android_main_len = dheader->used; break; case IPANIC_DT_SYSTEM_LOG: oops->android_system = data; oops->android_system_len = dheader->used; break; case IPANIC_DT_EVENTS_LOG: /* Todo .. */ break; case IPANIC_DT_RADIO_LOG: oops->android_radio = data; oops->android_radio_len = dheader->used; break; case IPANIC_DT_CURRENT_TSK: memcpy(oops->process_path, data, sizeof(struct aee_process_info)); break; case IPANIC_DT_MMPROFILE: oops->mmprofile = data; oops->mmprofile_len = dheader->used; break; default: LOGI("%s: [%d] NOT USED.\n", __func__, i); } } else { LOGW("%s: read %s failed, %x@%x\n", __func__, dheader->name, dheader->used, dheader->offset); } } return oops; } int ipanic(struct notifier_block *this, unsigned long event, void *ptr) { struct ipanic_data_header *dheader; struct kmsg_dumper dumper; ipanic_atf_log_rec_t atf_log = {ATF_LOG_SIZE, 0, 0}; int dt; int errno; struct ipanic_header *ipanic_hdr; aee_rr_rec_fiq_step(AEE_FIQ_STEP_KE_IPANIC_START); aee_rr_rec_exp_type(2); bust_spinlocks(1); spin_lock_irq(&ipanic_lock); aee_disable_api(); mrdump_mini_ke_cpu_regs(NULL); ipanic_mrdump_mini(AEE_REBOOT_MODE_KERNEL_PANIC, "kernel PANIC"); if (!ipanic_data_is_valid(IPANIC_DT_KERNEL_LOG)) { ipanic_klog_region(&dumper); errno = ipanic_data_to_sd(IPANIC_DT_KERNEL_LOG, &dumper); if (errno == -1) aee_nested_printf("$"); } ipanic_klog_region(&dumper); errno = ipanic_data_to_sd(IPANIC_DT_OOPS_LOG, &dumper); if (errno == -1) aee_nested_printf("$"); ipanic_data_to_sd(IPANIC_DT_CURRENT_TSK, 0); /* kick wdt after save the most critical infos */ ipanic_kick_wdt(); ipanic_data_to_sd(IPANIC_DT_MAIN_LOG, (void *)1); ipanic_data_to_sd(IPANIC_DT_SYSTEM_LOG, (void *)4); ipanic_data_to_sd(IPANIC_DT_EVENTS_LOG, (void *)2); ipanic_data_to_sd(IPANIC_DT_RADIO_LOG, (void *)3); aee_wdt_dump_info(); ipanic_klog_region(&dumper); ipanic_data_to_sd(IPANIC_DT_WDT_LOG, &dumper); #ifdef CONFIG_MTK_WQ_DEBUG mt_dump_wq_debugger(); #endif ipanic_klog_region(&dumper); ipanic_data_to_sd(IPANIC_DT_WQ_LOG, &dumper); ipanic_data_to_sd(IPANIC_DT_MMPROFILE, 0); ipanic_data_to_sd(IPANIC_DT_ATF_LOG, &atf_log); errno = ipanic_header_to_sd(0); if (!IS_ERR(ERR_PTR(errno))) mrdump_mini_ipanic_done(); ipanic_klog_region(&dumper); ipanic_data_to_sd(IPANIC_DT_LAST_LOG, &dumper); LOGD("ipanic done^_^"); ipanic_hdr = ipanic_header(); for (dt = IPANIC_DT_HEADER + 1; dt < IPANIC_DT_RESERVED31; dt++) { dheader = &ipanic_hdr->data_hdr[dt]; if (dheader->valid) { LOGD("%s[%x@%x],", dheader->name, dheader->used, dheader->offset); } } LOGD("^_^\n"); aee_rr_rec_fiq_step(AEE_FIQ_STEP_KE_IPANIC_DONE); return NOTIFY_DONE; } void ipanic_recursive_ke(struct pt_regs *regs, struct pt_regs *excp_regs, int cpu) { int errno; struct kmsg_dumper dumper; aee_nested_printf("minidump\n"); aee_rr_rec_exp_type(3); bust_spinlocks(1); flush_cache_all(); #ifdef __aarch64__ cpu_cache_off(); #else cpu_proc_fin(); #endif mrdump_mini_ke_cpu_regs(excp_regs); mrdump_mini_per_cpu_regs(cpu, regs); flush_cache_all(); ipanic_mrdump_mini(AEE_REBOOT_MODE_NESTED_EXCEPTION, "Nested Panic"); ipanic_data_to_sd(IPANIC_DT_CURRENT_TSK, 0); ipanic_kick_wdt(); ipanic_klog_region(&dumper); ipanic_data_to_sd(IPANIC_DT_KERNEL_LOG, &dumper); errno = ipanic_header_to_sd(0); if (!IS_ERR(ERR_PTR(errno))) mrdump_mini_ipanic_done(); if (ipanic_dt_active(IPANIC_DT_RAM_DUMP)) { aee_nested_printf("RAMDUMP.\n"); __mrdump_create_oops_dump(AEE_REBOOT_MODE_NESTED_EXCEPTION, excp_regs, "Nested Panic"); } bust_spinlocks(0); }
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(); preempt_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 [%x], excp_regs [%x]\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 hander\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; } }
void aee_stop_nested_panic(struct pt_regs *regs) { struct thread_info *thread = current_thread_info(); int i = 0; int len = 0; int timeout = 1000000; local_irq_disable(); preempt_disable(); /*Data abort handler data abort again on many cpu. Since ram console api is lockless, this should be prevented*/ if(atomic_xchg(&nested_panic_time, 1) != 0) { aee_nested_printf("multicore enters nested panic\n"); goto out; } mtk_wdt_restart(WK_WDT_LOC_TYPE_NOLOCK); hw_reboot_mode(); /*must guarantee Only one cpu can run here*/ aee_nested_printf("Nested panic\n"); aee_nested_printf("Log for the previous panic:\n"); aee_nested_printf("pc: %08lx lr: %08lx psr: %08lx\n", ((struct pt_regs *)thread->regs_on_excp)->ARM_pc, ((struct pt_regs *)thread->regs_on_excp)->ARM_lr, ((struct pt_regs *)thread->regs_on_excp)->ARM_cpsr); aee_nested_printf("sp: %08lx ip: %08lx fp: %08lx\n", ((struct pt_regs *)thread->regs_on_excp)->ARM_sp, ((struct pt_regs *)thread->regs_on_excp)->ARM_ip, ((struct pt_regs *)thread->regs_on_excp)->ARM_fp); aee_nested_printf("r10: %08lx r9: %08lx r8: %08lx\n", ((struct pt_regs *)thread->regs_on_excp)->ARM_r10, ((struct pt_regs *)thread->regs_on_excp)->ARM_r9, ((struct pt_regs *)thread->regs_on_excp)->ARM_r8); aee_nested_printf("r7: %08lx r6: %08lx r5: %08lx r4: %08lx\n", ((struct pt_regs *)thread->regs_on_excp)->ARM_r7, ((struct pt_regs *)thread->regs_on_excp)->ARM_r6, ((struct pt_regs *)thread->regs_on_excp)->ARM_r5, ((struct pt_regs *)thread->regs_on_excp)->ARM_r4); aee_nested_printf("r3: %08lx r2: %08lx r1: %08lx r0: %08lx\n", ((struct pt_regs *)thread->regs_on_excp)->ARM_r3, ((struct pt_regs *)thread->regs_on_excp)->ARM_r2, ((struct pt_regs *)thread->regs_on_excp)->ARM_r1, ((struct pt_regs *)thread->regs_on_excp)->ARM_r0); aee_nested_printf("Log for the current panic:\n"); aee_nested_printf("pc: %08lx lr: %08lx psr: %08lx\n", regs->ARM_pc, regs->ARM_lr, regs->ARM_cpsr); aee_nested_printf("sp: %08lx ip: %08lx fp: %08lx\n", regs->ARM_sp, regs->ARM_ip, regs->ARM_fp); aee_nested_printf("r10: %08lx r9: %08lx r8: %08lx\n", regs->ARM_r10, regs->ARM_r9, regs->ARM_r8); aee_nested_printf("r7: %08lx r6: %08lx r5: %08lx r4: %08lx\n", regs->ARM_r7, regs->ARM_r6, regs->ARM_r5, regs->ARM_r4); len = aee_nested_printf("r3: %08lx r2: %08lx r1: %08lx r0: %08lx\n", regs->ARM_r3, regs->ARM_r2, regs->ARM_r1, regs->ARM_r0); /*should not print stack info. this may overwhelms ram console used by fiq*/ if(0!= in_fiq_handler()) { goto out; } aee_nested_printf("stack [%08lx %08lx]", ((struct pt_regs *)thread->regs_on_excp)->ARM_sp, ((struct pt_regs *)thread->regs_on_excp)->ARM_sp + 512); /*Dump first panic stack*/ len = aee_dump_stack_top_binary(nested_panic_buf, sizeof(nested_panic_buf), ((struct pt_regs *)thread->regs_on_excp)->ARM_sp, ((struct pt_regs *)thread->regs_on_excp)->ARM_sp + 512 ); if(len > 0) { aee_sram_fiq_save_bin(nested_panic_buf, len); }else{ print_error_msg(len); } aee_nested_printf("stack [%08lx %08lx]", regs->ARM_sp, regs->ARM_sp + 256); /*Dump second panic stack*/ len = aee_dump_stack_top_binary(nested_panic_buf, sizeof(nested_panic_buf), regs->ARM_sp, regs->ARM_sp + 256); if(len > 0) { aee_sram_fiq_save_bin(nested_panic_buf, len); }else { print_error_msg(len); } out: /* waiting for the WDT timeout */ while (1) { printk("%s hang here%d\t", __func__, i++); while(timeout--) { udelay(1); } timeout = 1000000; } }