From 16024815e40aaec552408ff2023f08decaee26f4 Mon Sep 17 00:00:00 2001 From: jixj Date: Wed, 8 Aug 2018 10:35:47 +0800 Subject: [PATCH] watchdog_cpu_ctx: Add 32bit support Change-Id: I9d1c9f561bd64ac9ec120b747b50993b6cc90804 Reviewed-on: https://gerrit.mot.com/1216713 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Konstantin Makariev Reviewed-by: Huosheng Liao Reviewed-by: Ryan Lattrel Submit-Approved: Jira Key --- drivers/watchdog_cpu_ctx/watchdog_cpu_ctx.c | 318 +++++++++++++++----- drivers/watchdog_cpu_ctx/watchdog_cpu_ctx.h | 181 ++++++++++- 2 files changed, 422 insertions(+), 77 deletions(-) diff --git a/drivers/watchdog_cpu_ctx/watchdog_cpu_ctx.c b/drivers/watchdog_cpu_ctx/watchdog_cpu_ctx.c index b767db6a3404..b12cafbd16be 100644 --- a/drivers/watchdog_cpu_ctx/watchdog_cpu_ctx.c +++ b/drivers/watchdog_cpu_ctx/watchdog_cpu_ctx.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -99,6 +100,7 @@ const struct msm_wdog_cpuctx_header mwc_header[] = { }, }; +#if defined(CONFIG_ARM64) static int aa64_pa_max(void) { #if LINUX_VERSION_CODE <= KERNEL_VERSION(4,9,0) @@ -158,6 +160,59 @@ out: (unsigned long)kaslr_offset_low); } +static void msm_wdog_ctx_lnx_arch(struct msm_wdog_lnx_info *lnx) +{ + lnx->va_bits = VA_BITS; + lnx->pa_bits = aa64_pa_max(); + lnx->kaslr_offset = get_kaslr_offset(); +} + +#elif defined(CONFIG_ARM) + +struct wdog_frame_struct { + unsigned long addr; + unsigned long stack; +}; + +#if defined(CONFIG_ARM_LPAE) +static int armv7_pa_max(void) +{ + int cached_mem_size = (read_cpuid_ext(CPUID_EXT_MMFR3) >> 24) & 0xf; + + switch (cached_mem_size) { + case 0x0: + return 32; + case 0x1: + return 36; + case 0x2: + return 40; + default: + pr_err("PAMax: Unknown mem size: %#x\n", cached_mem_size); + break; + } + return 32; +} +#else +static int armv7_pa_max(void) +{ + int supersection = (read_cpuid_ext(CPUID_EXT_MMFR3) >> 28) & 0xf; + + if ((supersection == 0x0) && IS_ENABLED(CONFIG_PHYS_ADDR_T_64BIT)) + return 40; + else + return 32; +} +#endif /* CONFIG_ARM_LPAE */ + +static void msm_wdog_ctx_lnx_arch(struct msm_wdog_lnx_info *lnx) +{ + lnx->va_bits = 32; + lnx->pa_bits = armv7_pa_max(); + lnx->aa32_pxn = ((read_cpuid_ext(CPUID_EXT_MMFR0) & 0xf) >= 0x4); + lnx->kaslr_offset = 0; +} +#endif + static void msm_wdog_ctx_lnx(struct msm_wdog_lnx_info *lnx) { /* Since this is a module we cannot link to these symbols @@ -174,19 +229,19 @@ static void msm_wdog_ctx_lnx(struct msm_wdog_lnx_info *lnx) init_mm = (struct mm_struct *)init_mm_addr; } -#if LINUX_VERSION_CODE <= KERNEL_VERSION(4,9,0) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4,9,0) || IS_ENABLED(CONFIG_ARM) lnx->ti_tsk_offset = offsetof(struct thread_info, task); +#else + lnx->ti_tsk_offset = 0; #endif lnx->tsk_size = sizeof(struct task_struct); - lnx->aa64 = 1; - lnx->lpae = 0; + lnx->aa64 = IS_ENABLED(CONFIG_ARM64); + lnx->lpae = IS_ENABLED(CONFIG_ARM_LPAE); lnx->text_paddr = _text_addr ? virt_to_phys((void *)_text_addr) : 0; lnx->pgd_paddr = init_mm ? virt_to_phys(init_mm->pgd) : 0; lnx->page_shift = PAGE_SHIFT; lnx->thread_size = THREAD_SIZE; - lnx->va_bits = VA_BITS; - lnx->pa_bits = aa64_pa_max(); - lnx->kaslr_offset = get_kaslr_offset(); + msm_wdog_ctx_lnx_arch(lnx); } static void msm_wdog_ctx_header_init(struct msm_wdog_cpuctx *ctxi) @@ -218,15 +273,22 @@ static void msm_wdog_ctx_reset(struct msm_wdog_cpuctx *ctx, size_t ctx_size) if(!ctxi->lnx.text_paddr || !ctxi->lnx.pgd_paddr || - !ctxi->lnx.kaslr_offset) + (IS_ENABLED(CONFIG_ARM64) && !ctxi->lnx.kaslr_offset)) ctxi->info.syms_avail = 0; } } +#if defined(CONFIG_ARM64) static inline int virt_is_valid(unsigned long addr) { return (addr >= VA_START); } +#else +static inline int virt_is_valid(unsigned long addr) +{ + return virt_addr_valid(addr); +} +#endif static int msm_wdog_cpu_regs_version_unknown(uint32_t version) { @@ -241,47 +303,6 @@ static int msm_wdog_ctx_header_check(struct msm_wdog_cpuctx *ctxi) return memcmp(header, mwc_header, sizeof(mwc_header)); } -static void msm_wdog_show_sc_status(uint32_t sc_status) -{ - if (sc_status & SC_STATUS_DBI) - MSMWDTD("SDI: Secure watchdog bite. "); - if (sc_status & SC_STATUS_CTX_BY_TZ) - MSMWDTD("TZ: Non-secure watchdog bite. "); - MSMWDTD("%sS ", (sc_status & SC_STATUS_NS) ? "N" : " "); - if (sc_status & SC_STATUS_WDT) - MSMWDTD("WDT "); - if (sc_status & SC_STATUS_SGI) - MSMWDTD("SGI "); - if (sc_status & SC_STATUS_WARM_BOOT) - MSMWDTD("WARM_BOOT "); - MSMWDTD("\n"); -} - -static void msm_wdt_show_thread_saved_pc(struct task_struct *p) -{ - MSMWDTD(" %016lx ", (unsigned long)p->thread.cpu_context.pc); -} - -static int msm_wdt_unwind_frame_aa64(struct stackframe *frame, - unsigned long stack, - unsigned long *sp) -{ - unsigned long high, low; - unsigned long fp = frame->fp; - - low = *sp; - high = ALIGN(low, THREAD_SIZE) - 0x20; - - if (fp < low || fp > high || fp & 0xf) - return -EINVAL; - - *sp = (unsigned long)(fp + 0x10); - frame->fp = (*(unsigned long *)(fp) & (THREAD_SIZE - 1)) + stack; - frame->pc = *(unsigned long *)(fp + 8); - - return 0; -} - static void msm_wdt_show_raw_mem(unsigned long addr, int nbytes, unsigned long old_addr, const char *label) { @@ -322,9 +343,70 @@ static void msm_wdt_show_raw_mem(unsigned long addr, int nbytes, } } +static void msm_wdog_show_sc_status(uint32_t sc_status) +{ + if (sc_status & SC_STATUS_DBI) + MSMWDTD("SDI: Secure watchdog bite. "); + if (sc_status & SC_STATUS_CTX_BY_TZ) + MSMWDTD("TZ: Non-secure watchdog bite. "); + MSMWDTD("%sS ", (sc_status & SC_STATUS_NS) ? "N" : " "); + if (sc_status & SC_STATUS_WDT) + MSMWDTD("WDT "); + if (sc_status & SC_STATUS_SGI) + MSMWDTD("SGI "); + if (sc_status & SC_STATUS_WARM_BOOT) + MSMWDTD("WARM_BOOT "); + MSMWDTD("\n"); +} + +static void msm_wdt_show_thread_saved_pc(struct task_struct *p, + struct thread_info *ti); +static void msm_wdt_show_task(struct task_struct *p, struct thread_info *ti) +{ + unsigned state; + const char *stat_nam = TASK_STATE_TO_CHAR_STR; + + state = p->state ? __ffs(p->state) + 1 : 0; + MSMWDTD("%-15.15s %c", p->comm, + state < strlen(stat_nam) - 1 ? stat_nam[state] : '?'); + if (state == TASK_RUNNING) + MSMWDTD(" running "); + else + msm_wdt_show_thread_saved_pc(p, ti); + MSMWDTD("pid %6d tgid %6d 0x%08lx\n", task_pid_nr(p), task_tgid_nr(p), + (unsigned long)ti->flags); +} + +#if defined(CONFIG_ARM64) +static void msm_wdt_show_thread_saved_pc(struct task_struct *p, + struct thread_info *ti) +{ + MSMWDTD(" %016lx ", (unsigned long)p->thread.cpu_context.pc); +} + +static int msm_wdt_unwind_frame_aa64(struct stackframe *frame, + unsigned long stack, + unsigned long *sp) +{ + unsigned long high, low; + unsigned long fp = frame->fp; + + low = *sp; + high = ALIGN(low, THREAD_SIZE) - 0x20; + + if (fp < low || fp > high || fp & 0xf) + return -EINVAL; + + *sp = (unsigned long)(fp + 0x10); + frame->fp = (*(unsigned long *)(fp) & (THREAD_SIZE - 1)) + stack; + frame->pc = *(unsigned long *)(fp + 8); + + return 0; +} + static void msm_wdt_show_regs(struct sysdbgCPUCtxtType *sysdbg_ctx) { - struct sysdbg_cpu_ctxt_regs *regs = &sysdbg_ctx->cpu_regs; + struct sysdbg_cpu64_ctxt_regs *regs = &sysdbg_ctx->cpu_regs.cpu64_ctxt; uint64_t *gp_reg; int i; @@ -347,22 +429,6 @@ static void msm_wdt_show_regs(struct sysdbgCPUCtxtType *sysdbg_ctx) MSMWDTD("EL0: sp : %016llx\n", regs->sp_el0); } -static void msm_wdt_show_task(struct task_struct *p, struct thread_info *ti) -{ - unsigned state; - const char *stat_nam = TASK_STATE_TO_CHAR_STR; - - state = p->state ? __ffs(p->state) + 1 : 0; - MSMWDTD("%-15.15s %c", p->comm, - state < strlen(stat_nam) - 1 ? stat_nam[state] : '?'); - if (state == TASK_RUNNING) - MSMWDTD(" running "); - else - msm_wdt_show_thread_saved_pc(p); - MSMWDTD("pid %6d tgid %6d 0x%08lx\n", task_pid_nr(p), task_tgid_nr(p), - (unsigned long)ti->flags); -} - static void msm_wdt_unwind(struct sysdbgCPUCtxtType *sysdbg_ctx, unsigned long addr, unsigned long kaslr_offset, unsigned long stack) @@ -371,7 +437,7 @@ static void msm_wdt_unwind(struct sysdbgCPUCtxtType *sysdbg_ctx, unsigned long sp; int offset; char sym_buf[KSYM_NAME_LEN]; - struct sysdbg_cpu_ctxt_regs *regs = &sysdbg_ctx->cpu_regs; + struct sysdbg_cpu64_ctxt_regs *regs = &sysdbg_ctx->cpu_regs.cpu64_ctxt; unsigned long cur_kaslr_offset = get_kaslr_offset(); if (!virt_is_valid(addr)) { @@ -418,6 +484,120 @@ static void msm_wdt_unwind(struct sysdbgCPUCtxtType *sysdbg_ctx, } } +#elif defined(CONFIG_ARM) + +static int __in_irqentry_text(unsigned long ptr) +{ + unsigned long _irqentry_text_start_addr = kallsyms_lookup_name("__irqentry_text_start"); + unsigned long __irqentry_text_end_addr = kallsyms_lookup_name("__irqentry_text_end"); + return ptr >= (unsigned long)&_irqentry_text_start_addr && + ptr < (unsigned long)&__irqentry_text_end_addr; +} + +static int in_exception_text(unsigned long ptr) +{ + unsigned long _exception_text_start_addr = kallsyms_lookup_name("__exception_text_start"); + unsigned long _exception_text_end_addr = kallsyms_lookup_name("__exception_text_end"); + int in; + in = ptr >= (unsigned long)&_exception_text_start_addr && + ptr < (unsigned long)&_exception_text_end_addr; + + return in ? : __in_irqentry_text(ptr); +} + +static int msm_wdt_stackframe(struct stackframe *frame, void *data) +{ + unsigned long where = frame->pc; + struct wdog_frame_struct * frame_data = (struct wdog_frame_struct *)data; + + MSMWDTD("[<%08lx>] (%pS) from [<%08lx>] (%pS)\n", where, + (void *)where, frame->pc, (void *)frame->pc); + if (in_exception_text(where)) { + struct pt_regs *excep_regs = + (struct pt_regs *)(frame->sp); + if ((excep_regs->ARM_sp & ~(THREAD_SIZE - 1)) == frame_data->addr) { + excep_regs->ARM_sp -= frame_data->addr; + excep_regs->ARM_sp += frame_data->stack; + } + } + return 0; +} + +static void msm_wdt_show_thread_saved_pc(struct task_struct *p, + struct thread_info *ti) +{ + MSMWDTD(" %08lx ", (unsigned long)ti->cpu_context.pc); +} + +static void msm_wdt_show_regs(struct sysdbgCPUCtxtType *sysdbg_ctx) +{ + struct sysdbg_cpu32_ctxt_regs *regs = &sysdbg_ctx->cpu_regs.cpu32_ctxt; + unsigned long _stext_addr = kallsyms_lookup_name("_stext"); + unsigned long _etext_addr = kallsyms_lookup_name("_etext"); + + if ((regs->pc >= _stext_addr) && + (regs->pc <= _etext_addr)) + MSMWDTD("PC is at %pS <%08x>\n", + (void *)(unsigned long)regs->pc, (uint32_t)regs->pc); + else + MSMWDTD("PC is at %08x\n", (uint32_t)regs->pc); + MSMWDTD("\tr12: %08x r11: %08x r10: %08x r9 : %08x r8 : %08x\n", + regs->r12, regs->r11, regs->r10, regs->r9, regs->r8); + MSMWDTD("\tr7 : %08x r6 : %08x r5 : %08x r4 : %08x\n", + regs->r7, regs->r6, regs->r5, regs->r4); + MSMWDTD("\tr3 : %08x r2 : %08x r1 : %08x r0 : %08x\n", + regs->r3, regs->r2, regs->r1, regs->r0); + MSMWDTD("USR:\tr13: %08x r14: %08x\n", regs->r13_usr, regs->r14_usr); + MSMWDTD("HYP:\tr13: %08x r14: %08x\n", regs->r13_hyp, regs->r14_hyp); + MSMWDTD("IRQ:\tr13: %08x r14: %08x\n", regs->r13_irq, regs->r14_irq); + MSMWDTD("SVC:\tr13: %08x r14: %08x\n", regs->r13_svc, regs->r14_svc); + MSMWDTD("ABT:\tr13: %08x r14: %08x\n", regs->r13_abt, regs->r14_abt); + MSMWDTD("UND:\tr13: %08x r14: %08x\n", regs->r13_und, regs->r14_und); + MSMWDTD("MON:\tr13: %08x r14: %08x\n", regs->r13_mon, regs->r14_mon); + MSMWDTD("FIQ:\tr13: %08x r14: %08x\n", regs->r13_fiq, regs->r14_fiq); + MSMWDTD("\tr12: %08x r11: %08x r10: %08x r9 : %08x r8 : %08x\n", + regs->r12_fiq, regs->r11_fiq, regs->r10_fiq, + regs->r9_fiq, regs->r8_fiq); +} + +static void msm_wdt_unwind(struct sysdbgCPUCtxtType *sysdbg_ctx, + unsigned long addr, unsigned long kaslr_offset, + unsigned long stack) +{ + struct stackframe frame; + int offset; + struct wdog_frame_struct frame_data; + struct sysdbg_cpu32_ctxt_regs *regs = &sysdbg_ctx->cpu_regs.cpu32_ctxt; + + if (!virt_is_valid(addr)) { + MSMWDTD("%08lx is not valid kernel address.\n", addr); + return; + } + + frame_data.addr = addr; + frame_data.stack = stack; + + if ((regs->r13_svc & ~(THREAD_SIZE - 1)) == addr) { + frame.fp = (regs->r11 & (THREAD_SIZE - 1)) + stack; + frame.sp = (regs->r13_svc & (THREAD_SIZE - 1)) + stack; + frame.lr = regs->r14_svc; + frame.pc = regs->pc; + } else { + struct thread_info *ti = (struct thread_info *)stack; + + frame.fp = ti->cpu_context.fp - addr + stack; + frame.sp = ti->cpu_context.sp - addr + stack; + frame.lr = 0; + frame.pc = ti->cpu_context.pc; + } + offset = (frame.sp - stack - 128) & ~(128 - 1); + msm_wdt_show_raw_mem(stack, 96, addr, "thread_info"); + msm_wdt_show_raw_mem(stack + offset, THREAD_SIZE - offset, + addr + offset, "stack"); + walk_stackframe(&frame, &msm_wdt_stackframe, &frame_data); +} +#endif + static void msm_wdog_ctx_print(struct msm_wdog_cpuctx *ctx, phys_addr_t paddr, size_t ctx_size) { @@ -542,7 +722,7 @@ static void msm_wdog_ctx_print(struct msm_wdog_cpuctx *ctx, MSMWDTD("\nCPU%d\n", cpu); if(stat->ret != ERR_TASK_INVAL) { -#if LINUX_VERSION_CODE <= KERNEL_VERSION(4,9,0) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4,9,0) || !IS_ENABLED(CONFIG_THREAD_INFO_IN_TASK) msm_wdt_show_task(&ctxi->task, (struct thread_info *)ctxi->stack); #else @@ -620,11 +800,11 @@ static int watchdog_cpu_ctx_probe(struct platform_device *pdev) dev_info(dev, "size %zx", pdata->mem_size); dev_info(dev, "addr %lx", (unsigned long)pdata->mem_address); - dev_info(dev, "size per cpu is %lx", WDOG_CPUCTX_SIZE_PERCPU); + dev_info(dev, "size per cpu is %lx", (unsigned long)WDOG_CPUCTX_SIZE_PERCPU); if (pdata->mem_size < WDOG_CPUCTX_SIZE) { dev_err(dev, "Mem size too small %zx/%lu\n", - pdata->mem_size, WDOG_CPUCTX_SIZE); + pdata->mem_size, (unsigned long)WDOG_CPUCTX_SIZE); err = -ENOMEM; goto err; } diff --git a/drivers/watchdog_cpu_ctx/watchdog_cpu_ctx.h b/drivers/watchdog_cpu_ctx/watchdog_cpu_ctx.h index eff9c1c081a7..e50e5bfb4b59 100644 --- a/drivers/watchdog_cpu_ctx/watchdog_cpu_ctx.h +++ b/drivers/watchdog_cpu_ctx/watchdog_cpu_ctx.h @@ -73,7 +73,7 @@ struct msm_wdog_copy { uint64_t size; } __packed __aligned(4); -struct sysdbg_cpu_ctxt_regs { +struct sysdbg_cpu64_ctxt_regs { uint64_t x0; uint64_t x1; uint64_t x2; @@ -123,10 +123,179 @@ struct sysdbg_cpu_ctxt_regs { uint64_t __reserved4; }; +struct sysdbg_cpu32_ctxt_regs { + union { + uint32_t r0; + uint64_t x0; + }; + union { + uint32_t r1; + uint64_t x1; + }; + union { + uint32_t r2; + uint64_t x2; + }; + union { + uint32_t r3; + uint64_t x3; + }; + union { + uint32_t r4; + uint64_t x4; + }; + union { + uint32_t r5; + uint64_t x5; + }; + union { + uint32_t r6; + uint64_t x6; + }; + union { + uint32_t r7; + uint64_t x7; + }; + union { + uint32_t r8; + uint64_t x8; + }; + union { + uint32_t r9; + uint64_t x9; + }; + union { + uint32_t r10; + uint64_t x10; + }; + union { + uint32_t r11; + uint64_t x11; + }; + union { + uint32_t r12; + uint64_t x12; + }; + union { + uint32_t r13_usr; + uint64_t x13; + }; + union { + uint32_t r14_usr; + uint64_t x14; + }; + union { + uint32_t r13_hyp; + uint64_t x15; + }; + union { + uint32_t r14_irq; + uint64_t x16; + }; + union { + uint32_t r13_irq; + uint64_t x17; + }; + union { + uint32_t r14_svc; + uint64_t x18; + }; + union { + uint32_t r13_svc; + uint64_t x19; + }; + union { + uint32_t r14_abt; + uint64_t x20; + }; + union { + uint32_t r13_abt; + uint64_t x21; + }; + union { + uint32_t r14_und; + uint64_t x22; + }; + union { + uint32_t r13_und; + uint64_t x23; + }; + union { + uint32_t r8_fiq; + uint64_t x24; + }; + union { + uint32_t r9_fiq; + uint64_t x25; + }; + union { + uint32_t r10_fiq; + uint64_t x26; + }; + union { + uint32_t r11_fiq; + uint64_t x27; + }; + union { + uint32_t r12_fiq; + uint64_t x28; + }; + union { + uint32_t r13_fiq; + uint64_t x29; + }; + union { + uint32_t r14_fiq; + uint64_t x30; + }; + union { + uint32_t pc; + uint64_t x31; + }; + union { + uint32_t r13_mon; + uint64_t x32; + }; + union { + uint32_t r14_mon; + uint64_t x33; + }; + union { + uint32_t r14_hyp; + uint64_t x34; + }; + union { + uint32_t _reserved; + uint64_t x35; + }; + union { + uint32_t __reserved1; + uint64_t x36; + }; + union { + uint32_t __reserved2; + uint64_t x37; + }; + union { + uint32_t __reserved3; + uint64_t x38; + }; + union { + uint32_t __reserved4; + uint64_t x39; + }; +}; + + +union sysdbg_cpu_ctxt_regs_type { + struct sysdbg_cpu32_ctxt_regs cpu32_ctxt; + struct sysdbg_cpu64_ctxt_regs cpu64_ctxt; +}; + struct sysdbgCPUCtxtType { uint32_t status[4]; - struct sysdbg_cpu_ctxt_regs cpu_regs; - struct sysdbg_cpu_ctxt_regs __reserved3; /* Secure - Not used */ + union sysdbg_cpu_ctxt_regs_type cpu_regs; + union sysdbg_cpu_ctxt_regs_type __reserved3; /* Secure - Not used */ }; union sysdbg_cpuctx { @@ -154,9 +323,7 @@ struct msm_wdog_cpuctx_header { struct msm_wdog_lnx_info { uint32_t tsk_size; -#if LINUX_VERSION_CODE <= KERNEL_VERSION(4,9,0) uint32_t ti_tsk_offset; -#endif uint32_t aa64; uint32_t lpae; uint64_t text_paddr; @@ -191,9 +358,7 @@ struct msm_wdog_cpuctx { #define WDOG_CPUCTX_SIZE_PERCPU (sizeof(struct msm_wdog_cpuctx)) #define WDOG_CPUCTX_SIZE (num_present_cpus() * WDOG_CPUCTX_SIZE_PERCPU) -#define PHYS_PFN_OFFSET (PHYS_OFFSET >> PAGE_SHIFT) - #define KASLR_MAGIC_NUM 0xdead4ead #define KASLR_REGION_LEN 32 -#endif /* __WATCHDOG_CPU_CTX_H */ \ No newline at end of file +#endif /* __WATCHDOG_CPU_CTX_H */