modules: create watchdog_cpu_ctx modules
Port watchdog_cpu_ctx functions to SDM845 and also make it a loadable module. Change-Id: I35f53a75b4e58ae0a59fe14491e9c70bc69a19a0 Signed-off-by: Ryan Lattrel <ryanl@motorola.com> Reviewed-on: https://gerrit.mot.com/1144236 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Ling Jin <lingjin@motorola.com> Reviewed-by: Kenneth Kessler <kennykessler@motorola.com> Submit-Approved: Jira Key
This commit is contained in:
parent
008033c7f2
commit
2dec90bc68
5 changed files with 890 additions and 0 deletions
8
drivers/watchdog_cpu_ctx/Android.mk
Normal file
8
drivers/watchdog_cpu_ctx/Android.mk
Normal file
|
@ -0,0 +1,8 @@
|
|||
DLKM_DIR := motorola/kernel/modules
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := watchdog_cpu_ctx.ko
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
|
||||
include $(DLKM_DIR)/AndroidKernelModule.mk
|
5
drivers/watchdog_cpu_ctx/Kbuild
Normal file
5
drivers/watchdog_cpu_ctx/Kbuild
Normal file
|
@ -0,0 +1,5 @@
|
|||
# add -Wall to try to catch everything we can.
|
||||
EXTRA_CFLAGS += -Wall
|
||||
EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include
|
||||
|
||||
obj-m += watchdog_cpu_ctx.o
|
10
drivers/watchdog_cpu_ctx/Makefile
Normal file
10
drivers/watchdog_cpu_ctx/Makefile
Normal file
|
@ -0,0 +1,10 @@
|
|||
KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build
|
||||
|
||||
all:
|
||||
$(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS)
|
||||
|
||||
modules_install:
|
||||
$(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install
|
||||
|
||||
clean:
|
||||
$(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean
|
667
drivers/watchdog_cpu_ctx/watchdog_cpu_ctx.c
Normal file
667
drivers/watchdog_cpu_ctx/watchdog_cpu_ctx.c
Normal file
|
@ -0,0 +1,667 @@
|
|||
/*
|
||||
* Copyright (C) 2018 Motorola Mobility LLC
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/dma-contiguous.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/version.h>
|
||||
#include <asm/stacktrace.h>
|
||||
#include <asm/memory.h>
|
||||
#include <asm/sections.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mm_types.h>
|
||||
#include <linux/mmi_annotate.h>
|
||||
#include <soc/qcom/mmi_boot_info.h>
|
||||
#include "watchdog_cpu_ctx.h"
|
||||
|
||||
#define MSMDBG(fmt, args...) mmi_annotate(fmt, ##args)
|
||||
|
||||
#define MSMWDT_ERR(fmt, args...) do { \
|
||||
pr_err("WdogCtx: "fmt, ##args); \
|
||||
MSMWDTD("WdogCtx: "fmt, ##args); \
|
||||
} while (0)
|
||||
|
||||
#define MSMWDTD_IFWDOG(fmt, args...) do { \
|
||||
if (bi_powerup_reason() == PU_REASON_WDOG_AP_RESET) \
|
||||
MSMDBG(fmt, ##args); \
|
||||
} while (0)
|
||||
|
||||
#define MSMWDTD(fmt, args...) MSMWDTD_IFWDOG(fmt, ##args)
|
||||
|
||||
/* Check memory_dump.h to verify this is not going over the max or
|
||||
* conflicting with another entry. Also must match BL
|
||||
*/
|
||||
#define MSM_DUMP_DATA_WDOG_CPU_CTX 0x200
|
||||
|
||||
struct platform_data {
|
||||
phys_addr_t mem_address;
|
||||
size_t mem_size;
|
||||
};
|
||||
|
||||
const struct msm_wdog_cpuctx_header mwc_header[] = {
|
||||
{
|
||||
.type = CPUCTX_LNX_INFO,
|
||||
.magic = CPUCTX_LNX_INFO_MAGIC,
|
||||
.offset = offsetof(struct msm_wdog_cpuctx, lnx),
|
||||
.length = sizeof(struct msm_wdog_lnx_info),
|
||||
},
|
||||
{
|
||||
.type = CPUCTX_STAT,
|
||||
.magic = CPUCTX_STAT_MAGIC,
|
||||
.offset = offsetof(struct msm_wdog_cpuctx, stat),
|
||||
.length = sizeof(struct msm_wdog_cpuctx_stat),
|
||||
},
|
||||
{
|
||||
.type = CPUCTX_DUMP_DATA,
|
||||
.magic = CPUCTX_DUMP_DATA_MAGIC,
|
||||
.offset = offsetof(struct msm_wdog_cpuctx, cpu_data),
|
||||
.length = sizeof(struct msm_dump_data),
|
||||
},
|
||||
{
|
||||
.type = CPUCTX_TASK_STRUCT,
|
||||
.magic = CPUCTX_TASK_STRUCT_MAGIC,
|
||||
.offset = offsetof(struct msm_wdog_cpuctx, task),
|
||||
.length = sizeof(struct task_struct),
|
||||
},
|
||||
{
|
||||
.type = CPUCTX_THREAD,
|
||||
.magic = CPUCTX_THREAD_MAGIC,
|
||||
.offset = offsetof(struct msm_wdog_cpuctx, stack),
|
||||
.length = THREAD_SIZE,
|
||||
},
|
||||
};
|
||||
|
||||
static int aa64_pa_max(void)
|
||||
{
|
||||
#if LINUX_VERSION_CODE <= KERNEL_VERSION(4,9,0)
|
||||
int pa_range = read_cpuid(SYS_ID_AA64MMFR0_EL1) & 0xf;
|
||||
#else
|
||||
int pa_range = read_cpuid(ID_AA64MMFR0_EL1) & 0xf;
|
||||
#endif
|
||||
switch (pa_range) {
|
||||
case 0x0:
|
||||
return 32;
|
||||
case 0x1:
|
||||
return 36;
|
||||
case 0x2:
|
||||
return 40;
|
||||
case 0x3:
|
||||
return 42;
|
||||
case 0x4:
|
||||
return 44;
|
||||
case 0x5:
|
||||
return 48;
|
||||
default:
|
||||
pr_err("PAMax: Reserved PARange: %#x\n", pa_range);
|
||||
break;
|
||||
}
|
||||
return 32;
|
||||
}
|
||||
|
||||
static unsigned long get_kaslr_offset(void)
|
||||
{
|
||||
void __iomem *kaslr_addr;
|
||||
unsigned kaslr_magic_num;
|
||||
unsigned kaslr_offset_low = 0;
|
||||
unsigned kaslr_offset_high = 0;
|
||||
|
||||
kaslr_addr = ioremap(KASLR_PHYS_ADDR, KASLR_REGION_LEN);
|
||||
if(!kaslr_addr)
|
||||
goto out;
|
||||
|
||||
kaslr_magic_num = ioread32(kaslr_addr);
|
||||
if(kaslr_magic_num != KASLR_MAGIC_NUM)
|
||||
goto unmap;
|
||||
|
||||
kaslr_offset_low = ioread32(kaslr_addr + 4);
|
||||
kaslr_offset_high = ioread32(kaslr_addr + 8);
|
||||
unmap:
|
||||
iounmap(kaslr_addr);
|
||||
out:
|
||||
return ((unsigned long)kaslr_offset_high << 32 |
|
||||
(unsigned long)kaslr_offset_low);
|
||||
}
|
||||
|
||||
static void msm_wdog_ctx_lnx(struct msm_wdog_lnx_info *lnx)
|
||||
{
|
||||
/* Since this is a module we cannot link to these symbols
|
||||
* directly, so use kallsyms. kallsyms is needed to find the
|
||||
* names of the stacktrace symbols anyways.
|
||||
*/
|
||||
unsigned long _text_addr = kallsyms_lookup_name("_text");
|
||||
/* This is a way to get the address of swapper_pg_dir which
|
||||
* is not available via kallsyms_lookup_name directly
|
||||
*/
|
||||
unsigned long init_mm_addr = kallsyms_lookup_name("init_mm");
|
||||
struct mm_struct *init_mm = NULL;
|
||||
if(init_mm_addr) {
|
||||
init_mm = (struct mm_struct *)init_mm_addr;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE <= KERNEL_VERSION(4,9,0)
|
||||
lnx->ti_tsk_offset = offsetof(struct thread_info, task);
|
||||
#endif
|
||||
lnx->tsk_size = sizeof(struct task_struct);
|
||||
lnx->aa64 = 1;
|
||||
lnx->lpae = 0;
|
||||
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();
|
||||
}
|
||||
|
||||
static void msm_wdog_ctx_header_init(struct msm_wdog_cpuctx *ctxi)
|
||||
{
|
||||
struct msm_wdog_cpuctx_header *header = &ctxi->header[0];
|
||||
|
||||
memset_io(header, 0, sizeof(*header));
|
||||
memcpy(header, mwc_header, sizeof(mwc_header));
|
||||
}
|
||||
|
||||
static void msm_wdog_ctx_reset(struct msm_wdog_cpuctx *ctx, size_t ctx_size)
|
||||
{
|
||||
struct msm_wdog_cpuctx *ctxi;
|
||||
int cpu;
|
||||
|
||||
memset_io(ctx, 0, ctx_size);
|
||||
for_each_cpu(cpu, cpu_present_mask) {
|
||||
ctxi = &ctx[cpu];
|
||||
ctxi->info.sig = MSM_WDOG_CTX_SIG;
|
||||
ctxi->info.rev = MSM_WDOG_CTX_REV;
|
||||
ctxi->info.size = WDOG_CPUCTX_SIZE_PERCPU;
|
||||
#if IS_ENABLED(CONFIG_KALLSYMS)
|
||||
ctxi->info.syms_avail = 1;
|
||||
#else
|
||||
ctxi->info.syms_avail = 0;
|
||||
#endif
|
||||
msm_wdog_ctx_header_init(ctxi);
|
||||
msm_wdog_ctx_lnx(&ctxi->lnx);
|
||||
|
||||
if(!ctxi->lnx.text_paddr ||
|
||||
!ctxi->lnx.pgd_paddr ||
|
||||
!ctxi->lnx.kaslr_offset)
|
||||
ctxi->info.syms_avail = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int virt_is_valid(unsigned long addr)
|
||||
{
|
||||
return (addr >= VA_START);
|
||||
}
|
||||
|
||||
static int msm_wdog_cpu_regs_version_unknown(uint32_t version)
|
||||
{
|
||||
if (version != CPU_FORMAT_VERSION4)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_wdog_ctx_header_check(struct msm_wdog_cpuctx *ctxi)
|
||||
{
|
||||
struct msm_wdog_cpuctx_header *header = &ctxi->header[0];
|
||||
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 high, low;
|
||||
unsigned long fp = frame->fp;
|
||||
|
||||
low = frame->sp;
|
||||
high = ALIGN(low, THREAD_SIZE) - 0x20;
|
||||
|
||||
if (fp < low || fp > high || fp & 0xf)
|
||||
return -EINVAL;
|
||||
|
||||
frame->sp = 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)
|
||||
{
|
||||
int i, j;
|
||||
int nlines;
|
||||
unsigned long *p;
|
||||
|
||||
MSMWDTD("%s: %#lx: ", label, old_addr);
|
||||
if (!virt_is_valid(old_addr)) {
|
||||
MSMWDTD("is not valid kernel address.\n");
|
||||
return;
|
||||
}
|
||||
MSMWDTD("\n");
|
||||
|
||||
/*
|
||||
* round address down to unsigned long aligned boundary
|
||||
* and always dump a multiple of 32 bytes
|
||||
*/
|
||||
p = (unsigned long *)(addr & ~(sizeof(unsigned long) - 1));
|
||||
nbytes += (addr & (sizeof(unsigned long) - 1));
|
||||
nlines = (nbytes + 31) / 32;
|
||||
|
||||
for (i = 0; i < nlines; i++) {
|
||||
/*
|
||||
* just display low 16 bits of address to keep
|
||||
* each line of the dump < 80 characters
|
||||
*/
|
||||
MSMWDTD("%04lx ", (unsigned long)old_addr & 0xffff);
|
||||
for (j = 0; j < (32 / sizeof(unsigned long)); j++) {
|
||||
if (sizeof(unsigned long) == 4)
|
||||
MSMWDTD(" %08lx", *p);
|
||||
else
|
||||
MSMWDTD(" %016lx", *p);
|
||||
++p;
|
||||
old_addr += sizeof(*p);
|
||||
}
|
||||
MSMWDTD("\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void msm_wdt_show_regs(struct sysdbgCPUCtxtType *sysdbg_ctx)
|
||||
{
|
||||
struct sysdbg_cpu_ctxt_regs *regs = &sysdbg_ctx->cpu_regs;
|
||||
uint64_t *gp_reg;
|
||||
int i;
|
||||
|
||||
MSMWDTD("PC is at %016llx\n", regs->pc);
|
||||
MSMWDTD("sp : %016llx ", regs->sp_el1);
|
||||
MSMWDTD("x30: %016llx\n", regs->x30);
|
||||
gp_reg = (uint64_t *)regs;
|
||||
for (i = 29; i >= 0; i--) {
|
||||
MSMWDTD("x%-2d: %016llx ", i, gp_reg[i]);
|
||||
if (i % 2 == 0)
|
||||
MSMWDTD("\n");
|
||||
}
|
||||
MSMWDTD("currentEL : %016llx\n", regs->currentEL);
|
||||
MSMWDTD("EL3: elr : %016llx spsr : %016llx sp : %016llx\n",
|
||||
regs->elr_el3, regs->spsr_el3, regs->sp_el3);
|
||||
MSMWDTD("EL2: elr : %016llx spsr : %016llx sp : %016llx\n",
|
||||
regs->elr_el2, regs->spsr_el2, regs->sp_el2);
|
||||
MSMWDTD("EL1: elr : %016llx spsr : %016llx sp : %016llx\n",
|
||||
regs->elr_el1, regs->spsr_el1, regs->sp_el1);
|
||||
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)
|
||||
{
|
||||
struct stackframe frame;
|
||||
int offset;
|
||||
char sym_buf[KSYM_NAME_LEN];
|
||||
struct sysdbg_cpu_ctxt_regs *regs = &sysdbg_ctx->cpu_regs;
|
||||
unsigned long cur_kaslr_offset = get_kaslr_offset();
|
||||
|
||||
if (!virt_is_valid(addr)) {
|
||||
MSMWDTD("%016lx is not valid kernel address.\n", addr);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((regs->sp_el1 & ~(THREAD_SIZE - 1)) == addr) {
|
||||
frame.fp = (regs->x29 & (THREAD_SIZE - 1)) + stack;
|
||||
frame.sp = (regs->sp_el1 & (THREAD_SIZE - 1)) + stack;
|
||||
frame.pc = regs->pc;
|
||||
}
|
||||
else {
|
||||
MSMWDTD("Unexpected stack address\n");
|
||||
return;
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
sprint_symbol(sym_buf,
|
||||
regs->pc - kaslr_offset + cur_kaslr_offset);
|
||||
MSMWDTD("PC: %s <%016llx>\n", sym_buf, regs->pc);
|
||||
sprint_symbol(sym_buf,
|
||||
regs->x30 - kaslr_offset + cur_kaslr_offset);
|
||||
MSMWDTD("LR: %s <%016llx>\n", sym_buf, regs->x30);
|
||||
|
||||
while (1) {
|
||||
int urc;
|
||||
unsigned long where = frame.pc;
|
||||
|
||||
/* Symbols are located at a random offset...subtract
|
||||
* the old one and add the current one
|
||||
*/
|
||||
sprint_symbol(sym_buf,
|
||||
frame.pc - kaslr_offset + cur_kaslr_offset);
|
||||
MSMWDTD("[<%016lx>] %s\n", where, sym_buf);
|
||||
|
||||
urc = msm_wdt_unwind_frame_aa64(&frame, stack);
|
||||
if (urc < 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void msm_wdog_ctx_print(struct msm_wdog_cpuctx *ctx,
|
||||
phys_addr_t paddr, size_t ctx_size)
|
||||
{
|
||||
struct msm_wdog_cpuctx *ctxi;
|
||||
struct msm_dump_data *cpu_data;
|
||||
struct msm_wdog_cpuctx_info *info;
|
||||
struct msm_wdog_cpuctx_stat *stat;
|
||||
cpumask_t cpus, cpus_nodump, cpus_regs, cpus_dd;
|
||||
unsigned long stack_tmp = 0;
|
||||
int cpu;
|
||||
|
||||
cpumask_clear(&cpus);
|
||||
for_each_cpu(cpu, cpu_present_mask) {
|
||||
ctxi = &ctx[cpu];
|
||||
cpu_data = &ctxi->cpu_data;
|
||||
if (msm_wdog_ctx_header_check(ctxi)) {
|
||||
MSMWDTD_IFWDOG("CPU%d: ctx header invalid\n", cpu);
|
||||
continue;
|
||||
}
|
||||
|
||||
info = &ctxi->info;
|
||||
if ((info->sig != MSM_WDOG_CTX_SIG) ||
|
||||
(info->rev2 != MSM_WDOG_CTX_REV) ||
|
||||
(info->rev != MSM_WDOG_CTX_REV) ||
|
||||
(info->size != WDOG_CPUCTX_SIZE_PERCPU) ||
|
||||
(info->ret != ERR_NONE)) {
|
||||
MSMWDTD_IFWDOG("CPU%d: sig %x rev %x/%x sz %x ret %x\n",
|
||||
cpu, info->sig, (unsigned)info->rev,
|
||||
info->rev2, info->size, info->ret);
|
||||
continue;
|
||||
}
|
||||
cpumask_set_cpu(cpu, &cpus);
|
||||
}
|
||||
|
||||
if (cpumask_empty(&cpus))
|
||||
return;
|
||||
|
||||
cpumask_clear(&cpus_nodump);
|
||||
cpumask_clear(&cpus_regs);
|
||||
cpumask_clear(&cpus_dd);
|
||||
|
||||
for_each_cpu(cpu, &cpus) {
|
||||
uint32_t *status;
|
||||
ctxi = &ctx[cpu];
|
||||
cpu_data = &ctxi->cpu_data;
|
||||
stat = &ctxi->stat;
|
||||
|
||||
if (!cpu_data->magic && !cpu_data->version &&
|
||||
!ctxi->sysdbg.data.status[0]) {
|
||||
MSMWDTD_IFWDOG("CPU%d: No Dump!\n", cpu);
|
||||
cpumask_set_cpu(cpu, &cpus_nodump);
|
||||
continue;
|
||||
}
|
||||
if (cpu_data->magic != DUMP_MAGIC_NUMBER) {
|
||||
MSMWDTD_IFWDOG("CPU%d: dump magic mismatch %x/%x\n",
|
||||
cpu, cpu_data->magic, DUMP_MAGIC_NUMBER);
|
||||
continue;
|
||||
}
|
||||
if (msm_wdog_cpu_regs_version_unknown(cpu_data->version)) {
|
||||
MSMWDTD_IFWDOG("CPU%d: unknown version %d\n",
|
||||
cpu, cpu_data->version);
|
||||
continue;
|
||||
}
|
||||
cpumask_set_cpu(cpu, &cpus_regs);
|
||||
status = &ctxi->sysdbg.data.status[0];
|
||||
MSMWDTD("CPU%d: %x %x ", cpu, status[0], status[1]);
|
||||
msm_wdog_show_sc_status(status[1]);
|
||||
if (stat->ret == ERR_NONE || stat->ret == ERR_TASK_INVAL)
|
||||
cpumask_set_cpu(cpu, &cpus_dd);
|
||||
}
|
||||
|
||||
if (cpumask_equal(&cpus_nodump, cpu_present_mask)) {
|
||||
MSMWDTD_IFWDOG("Might be Secure Watchdog Bite!\n");
|
||||
return;
|
||||
}
|
||||
if (cpumask_empty(&cpus_regs))
|
||||
return;
|
||||
MSMWDTD("\n");
|
||||
for_each_cpu(cpu, &cpus_regs) {
|
||||
struct msm_wdog_copy *job;
|
||||
|
||||
ctxi = &ctx[cpu];
|
||||
stat = &ctxi->stat;
|
||||
MSMWDTD("CPU%d: ret %x", cpu, stat->ret);
|
||||
if (stat->stack_va) {
|
||||
MSMWDTD(" stack %lx ", (unsigned long)stat->stack_va);
|
||||
job = &stat->jobs[LNX_STACK];
|
||||
MSMWDTD("%lx -> %lx (%lx) ", (unsigned long)job->from,
|
||||
(unsigned long)job->to,
|
||||
(unsigned long)job->size);
|
||||
job = &stat->jobs[LNX_TASK];
|
||||
MSMWDTD("%lx -> %lx (%lx)", (unsigned long)job->from,
|
||||
(unsigned long)job->to,
|
||||
(unsigned long)job->size);
|
||||
}
|
||||
MSMWDTD("\n");
|
||||
}
|
||||
for_each_cpu(cpu, &cpus_regs) {
|
||||
ctxi = &ctx[cpu];
|
||||
MSMWDTD("\nCPU%d\n", cpu);
|
||||
msm_wdt_show_regs(&ctxi->sysdbg.data);
|
||||
}
|
||||
for_each_cpu(cpu, &cpus_dd) {
|
||||
unsigned long data;
|
||||
|
||||
ctxi = &ctx[cpu];
|
||||
stat = &ctxi->stat;
|
||||
if (!IS_ALIGNED((unsigned long)ctxi->stack, THREAD_SIZE)
|
||||
&& !stack_tmp) {
|
||||
stack_tmp = __get_free_pages(GFP_KERNEL,
|
||||
THREAD_SIZE_ORDER);
|
||||
if (!stack_tmp)
|
||||
MSMWDT_ERR("Alloc temp stack failed.\n");
|
||||
}
|
||||
if (stack_tmp) {
|
||||
memcpy_fromio((void *)stack_tmp, ctxi->stack,
|
||||
THREAD_SIZE);
|
||||
data = stack_tmp;
|
||||
} else {
|
||||
data = (unsigned long)ctxi->stack;
|
||||
}
|
||||
|
||||
MSMWDTD("\nCPU%d\n", cpu);
|
||||
if(stat->ret != ERR_TASK_INVAL) {
|
||||
#if LINUX_VERSION_CODE <= KERNEL_VERSION(4,9,0)
|
||||
msm_wdt_show_task(&ctxi->task,
|
||||
(struct thread_info *)ctxi->stack);
|
||||
#else
|
||||
msm_wdt_show_task(&ctxi->task,
|
||||
&ctxi->task.thread_info);
|
||||
#endif
|
||||
}
|
||||
msm_wdt_unwind(&ctxi->sysdbg.data, ctxi->stat.stack_va,
|
||||
ctx->lnx.kaslr_offset, data);
|
||||
}
|
||||
MSMWDTD("\n");
|
||||
if (stack_tmp)
|
||||
free_pages(stack_tmp, THREAD_SIZE_ORDER);
|
||||
}
|
||||
|
||||
static void watchdog_cpu_ctx_table_register(struct device *dev,
|
||||
struct platform_data *pdata)
|
||||
{
|
||||
int err = 0;
|
||||
struct msm_dump_entry dump_entry;
|
||||
struct msm_dump_data *ctx_dump_data;
|
||||
|
||||
ctx_dump_data = kzalloc(sizeof(struct msm_dump_data),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!ctx_dump_data) {
|
||||
dev_err(dev, "Cannot alloc dump data structure.\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
ctx_dump_data->addr = pdata->mem_address;
|
||||
ctx_dump_data->len = pdata->mem_size;
|
||||
dump_entry.id = MSM_DUMP_DATA_WDOG_CPU_CTX;
|
||||
dump_entry.addr = virt_to_phys(ctx_dump_data);
|
||||
err = msm_dump_data_register(MSM_DUMP_TABLE_APPS, &dump_entry);
|
||||
if (err) {
|
||||
dev_err(dev, "Registering dump data failed.\n");
|
||||
kfree(ctx_dump_data);
|
||||
}
|
||||
err:
|
||||
return;
|
||||
}
|
||||
|
||||
static int watchdog_cpu_ctx_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct platform_data *pdata;
|
||||
struct resource res;
|
||||
struct device_node *node;
|
||||
struct msm_wdog_cpuctx *ctx_vaddr;
|
||||
int err = 0;
|
||||
|
||||
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata) {
|
||||
err = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Get reserved memory region from Device-tree */
|
||||
node = of_parse_phandle(dev->of_node, "memory-region", 0);
|
||||
if (!node) {
|
||||
dev_err(dev, "No %s specified\n", "memory-region");
|
||||
goto err;
|
||||
}
|
||||
|
||||
err = of_address_to_resource(node, 0, &res);
|
||||
of_node_put(node);
|
||||
if (err) {
|
||||
dev_err(dev, "No memory address assigned to the region\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
pdata->mem_size = resource_size(&res);
|
||||
pdata->mem_address = res.start;
|
||||
|
||||
dev_info(dev, "size %lx", 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);
|
||||
|
||||
if (pdata->mem_size < WDOG_CPUCTX_SIZE) {
|
||||
dev_err(dev, "Mem size too small %zx/%lu\n",
|
||||
pdata->mem_size, WDOG_CPUCTX_SIZE);
|
||||
err = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
watchdog_cpu_ctx_table_register(dev, pdata);
|
||||
|
||||
ctx_vaddr = dma_remap(dev, NULL, pdata->mem_address,
|
||||
pdata->mem_size, 0);
|
||||
if (!ctx_vaddr) {
|
||||
dev_err(dev, "Cannot remap buffer %pa size %zx\n",
|
||||
&pdata->mem_address, pdata->mem_size);
|
||||
err = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
msm_wdog_ctx_print(ctx_vaddr, pdata->mem_address, pdata->mem_size);
|
||||
msm_wdog_ctx_reset(ctx_vaddr, pdata->mem_size);
|
||||
|
||||
err:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int watchdog_cpu_ctx_remove(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id watchdog_cpu_ctx_match[] = {
|
||||
{ .compatible = "mmi,watchdog_cpu_ctx" },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver watchdog_cpu_ctx_driver = {
|
||||
.probe = watchdog_cpu_ctx_probe,
|
||||
.remove = watchdog_cpu_ctx_remove,
|
||||
.driver = {
|
||||
.name = "watchdog_cpu_ctx",
|
||||
.of_match_table = watchdog_cpu_ctx_match,
|
||||
},
|
||||
};
|
||||
|
||||
static int watchdog_cpu_ctx_init(void)
|
||||
{
|
||||
return platform_driver_register(&watchdog_cpu_ctx_driver);
|
||||
}
|
||||
|
||||
static void watchdog_cpu_ctx_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&watchdog_cpu_ctx_driver);
|
||||
}
|
||||
|
||||
module_init(watchdog_cpu_ctx_init);
|
||||
module_exit(watchdog_cpu_ctx_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("Watchdog cpu ctx");
|
200
drivers/watchdog_cpu_ctx/watchdog_cpu_ctx.h
Normal file
200
drivers/watchdog_cpu_ctx/watchdog_cpu_ctx.h
Normal file
|
@ -0,0 +1,200 @@
|
|||
/*
|
||||
* Copyright (C) 2018 Motorola Mobility LLC
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
#ifndef __WATCHDOG_CPU_CTX_H
|
||||
#define __WATCHDOG_CPU_CTX_H
|
||||
|
||||
#include <linux/thread_info.h>
|
||||
#include <soc/qcom/memory_dump.h>
|
||||
|
||||
#define MAX_CPU_CTX_SIZE 2048
|
||||
#define CPU_FORMAT_VERSION4 0x14
|
||||
|
||||
#define DUMP_MAGIC_NUMBER 0x42445953
|
||||
#define CPUCTX_LNX_INFO_MAGIC 0x4c4e5831
|
||||
#define CPUCTX_STAT_MAGIC 0x4c4e5832
|
||||
#define CPUCTX_DUMP_DATA_MAGIC 0x4c4e5833
|
||||
#define CPUCTX_TASK_STRUCT_MAGIC 0x4c4e5834
|
||||
#define CPUCTX_THREAD_MAGIC 0x4c4e5835
|
||||
|
||||
#define MSM_WDOG_CTX_SIG 0x77647473
|
||||
#define MSM_WDOG_CTX_REV 0x00020000
|
||||
|
||||
#define RET_STAGE_SHIFT (28)
|
||||
#define RET_SECTION_SHIFT (24)
|
||||
#define RET_ERR_SHIFT (16)
|
||||
#define RET_MISC_SHIFT (12)
|
||||
|
||||
#define RET_TZ (0xfu << RET_STAGE_SHIFT)
|
||||
#define ERR_NONE (RET_TZ | (0xffu << RET_ERR_SHIFT))
|
||||
#define ERR_NO_SYMS (RET_TZ | (0xe9u << RET_ERR_SHIFT))
|
||||
#define ERR_TASK_INVAL (RET_TZ | (0xeau << RET_ERR_SHIFT))
|
||||
|
||||
#define SC_STATUS_NS 0x01
|
||||
#define SC_STATUS_WDT 0x02
|
||||
#define SC_STATUS_SGI 0x04
|
||||
#define SC_STATUS_WARM_BOOT 0x08
|
||||
#define SC_STATUS_DBI 0x10
|
||||
#define SC_STATUS_CTX_BY_TZ 0x20
|
||||
|
||||
#ifndef THREAD_SIZE_ORDER
|
||||
#define THREAD_SIZE_ORDER (get_order(THREAD_SIZE))
|
||||
#endif
|
||||
|
||||
enum lnx_jobs {
|
||||
LNX_STACK,
|
||||
LNX_TASK,
|
||||
LNX_CTX_AREAS,
|
||||
};
|
||||
|
||||
enum cpuctx_type {
|
||||
CPUCTX_LNX_INFO,
|
||||
CPUCTX_STAT,
|
||||
CPUCTX_DUMP_DATA,
|
||||
CPUCTX_TASK_STRUCT,
|
||||
CPUCTX_THREAD,
|
||||
CPUCTX_TYPES,
|
||||
CPUCTX_MAX = 20,
|
||||
};
|
||||
|
||||
struct msm_wdog_copy {
|
||||
uint64_t from;
|
||||
uint64_t to;
|
||||
uint64_t size;
|
||||
} __packed __aligned(4);
|
||||
|
||||
struct sysdbg_cpu_ctxt_regs {
|
||||
uint64_t x0;
|
||||
uint64_t x1;
|
||||
uint64_t x2;
|
||||
uint64_t x3;
|
||||
uint64_t x4;
|
||||
uint64_t x5;
|
||||
uint64_t x6;
|
||||
uint64_t x7;
|
||||
uint64_t x8;
|
||||
uint64_t x9;
|
||||
uint64_t x10;
|
||||
uint64_t x11;
|
||||
uint64_t x12;
|
||||
uint64_t x13;
|
||||
uint64_t x14;
|
||||
uint64_t x15;
|
||||
uint64_t x16;
|
||||
uint64_t x17;
|
||||
uint64_t x18;
|
||||
uint64_t x19;
|
||||
uint64_t x20;
|
||||
uint64_t x21;
|
||||
uint64_t x22;
|
||||
uint64_t x23;
|
||||
uint64_t x24;
|
||||
uint64_t x25;
|
||||
uint64_t x26;
|
||||
uint64_t x27;
|
||||
uint64_t x28;
|
||||
uint64_t x29;
|
||||
uint64_t x30;
|
||||
uint64_t pc;
|
||||
uint64_t currentEL;
|
||||
uint64_t sp_el3;
|
||||
uint64_t elr_el3;
|
||||
uint64_t spsr_el3;
|
||||
uint64_t sp_el2;
|
||||
uint64_t elr_el2;
|
||||
uint64_t spsr_el2;
|
||||
uint64_t sp_el1;
|
||||
uint64_t elr_el1;
|
||||
uint64_t spsr_el1;
|
||||
uint64_t sp_el0;
|
||||
uint64_t __reserved1;
|
||||
uint64_t __reserved2;
|
||||
uint64_t __reserved3;
|
||||
uint64_t __reserved4;
|
||||
};
|
||||
|
||||
struct sysdbgCPUCtxtType {
|
||||
uint32_t status[4];
|
||||
struct sysdbg_cpu_ctxt_regs cpu_regs;
|
||||
struct sysdbg_cpu_ctxt_regs __reserved3; /* Secure - Not used */
|
||||
};
|
||||
|
||||
union sysdbg_cpuctx {
|
||||
struct sysdbgCPUCtxtType data;
|
||||
uint8_t space[MAX_CPU_CTX_SIZE];
|
||||
};
|
||||
|
||||
struct msm_wdog_cpuctx_info {
|
||||
uint32_t sig;
|
||||
uint32_t rev;
|
||||
uint32_t ret;
|
||||
uint32_t size;
|
||||
uint32_t rev2;
|
||||
uint32_t syms_avail;
|
||||
uint32_t reserve2;
|
||||
uint32_t reserve3;
|
||||
} __packed __aligned(4);
|
||||
|
||||
struct msm_wdog_cpuctx_header {
|
||||
uint32_t type; /* cpuctx_type */
|
||||
uint32_t magic;
|
||||
uint64_t offset;
|
||||
uint64_t length;
|
||||
} __packed __aligned(4);
|
||||
|
||||
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;
|
||||
uint64_t pgd_paddr;
|
||||
uint32_t thread_size;
|
||||
uint32_t va_bits;
|
||||
uint32_t pa_bits;
|
||||
uint32_t page_shift;
|
||||
uint32_t aa32_pxn;
|
||||
uint32_t pad1;
|
||||
uint64_t kaslr_offset;
|
||||
} __packed __aligned(4);
|
||||
|
||||
struct msm_wdog_cpuctx_stat {
|
||||
uint32_t ret;
|
||||
uint32_t reserved;
|
||||
uint64_t stack_va;
|
||||
struct msm_wdog_copy jobs[LNX_CTX_AREAS];
|
||||
} __packed __aligned(4);
|
||||
|
||||
struct msm_wdog_cpuctx {
|
||||
union sysdbg_cpuctx sysdbg;
|
||||
struct msm_wdog_cpuctx_info info;
|
||||
struct msm_wdog_cpuctx_header header[CPUCTX_MAX];
|
||||
struct msm_wdog_lnx_info lnx;
|
||||
struct msm_wdog_cpuctx_stat stat;
|
||||
struct msm_dump_data cpu_data;
|
||||
struct task_struct task;
|
||||
u8 stack[THREAD_SIZE] __aligned(1024);
|
||||
} __packed __aligned(4);
|
||||
|
||||
#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_PHYS_ADDR 0x146bf6d0
|
||||
#define KASLR_MAGIC_NUM 0xdead4ead
|
||||
#define KASLR_REGION_LEN 32
|
||||
|
||||
#endif /* __WATCHDOG_CPU_CTX_H */
|
Loading…
Reference in a new issue