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:
Ryan Lattrel 2018-03-06 17:06:50 -06:00
parent 008033c7f2
commit 2dec90bc68
5 changed files with 890 additions and 0 deletions

View 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

View 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

View 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

View 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");

View 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 */