summaryrefslogtreecommitdiff
path: root/drivers/platform/sprd/sysdump64.c
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
committerYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
commit8e4bff4cab0ddac6060645b0715210484d02ff40 (patch)
tree3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/platform/sprd/sysdump64.c
Initial file dump from open source releaseHEADmaster
Diffstat (limited to 'drivers/platform/sprd/sysdump64.c')
-rw-r--r--drivers/platform/sprd/sysdump64.c741
1 files changed, 741 insertions, 0 deletions
diff --git a/drivers/platform/sprd/sysdump64.c b/drivers/platform/sprd/sysdump64.c
new file mode 100644
index 00000000..fdb207e7
--- /dev/null
+++ b/drivers/platform/sprd/sysdump64.c
@@ -0,0 +1,741 @@
+/*
+ * Copyright (C) 2013 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/miscdevice.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/list.h>
+#include <linux/proc_fs.h>
+#include <linux/elf.h>
+#include <linux/elfcore.h>
+#include <linux/init.h>
+#include <linux/highuid.h>
+#include <linux/sched.h>
+#include <linux/sysctl.h>
+#include <asm/memory.h>
+#include <asm/cacheflush.h>
+
+#include <soc/sprd/sci_glb_regs.h>
+#include "sysdump64.h"
+
+#define CORE_STR "CORE"
+#ifndef ELF_CORE_EFLAGS
+#define ELF_CORE_EFLAGS 0
+#endif
+
+#define SYSDUMP_MAGIC "SPRD_SYSDUMP_119"
+/* FIXME: come from u-boot */
+#define SPRD_SYSDUMP_MAGIC (0x80000000 + 0x20000000 - (1024<<10))
+
+#define SYSDUMP_NOTE_BYTES (ALIGN(sizeof(struct elf_note), 4) + \
+ ALIGN(sizeof(CORE_STR), 4) + \
+ ALIGN(sizeof(struct elf_prstatus), 4))
+
+typedef char note_buf_t[SYSDUMP_NOTE_BYTES];
+
+note_buf_t __percpu *crash_notes;
+
+/* An ELF note in memory */
+struct memelfnote
+{
+ const char *name;
+ int type;
+ unsigned int datasz;
+ void *data;
+};
+
+struct sysdump_info {
+ char magic[16];
+ char time[32];
+ char dump_path[128];
+ int elfhdr_size;
+ int mem_num;
+ unsigned long dump_mem_paddr;
+ int crash_key;
+};
+
+struct sysdump_extra {
+ int enter_id;
+ int enter_cpu;
+ char reason[256];
+ struct pt_regs cpu_context[CONFIG_NR_CPUS];
+};
+
+struct sysdump_config {
+ int enable;
+ int crashkey_only;
+ int dump_modem;
+ int reboot;
+ char dump_path[128];
+};
+
+static struct sysdump_info *sprd_sysdump_info = NULL;
+
+/* must be global to let gdb know */
+struct sysdump_extra sprd_sysdump_extra = {
+ .enter_id = -1,
+ .enter_cpu = -1,
+ .reason = {0},
+};
+
+static struct sysdump_config sysdump_conf = {
+ .enable = 1,
+ .crashkey_only = 0,
+ .dump_modem = 1,
+ .reboot = 1,
+ .dump_path = "",
+};
+
+#ifdef CONFIG_SPRD_SYSDUMP
+/*FIXME:
+ the file nodes related to resered memory from DT has not been created,
+ so we just use hard code here temporarily.
+*/
+struct sysdump_mem sprd_dump_mem[] = {
+ {
+ .paddr = 0x80000000,
+ .soff = 0xffffffff,
+ .size = 128*1024*1024,
+ .type = SYSDUMP_RAM,
+ },
+ { /* GGE modem */
+ .paddr = 0x80000000 + 128*1024*1024,
+ .soff = 0xffffffff,
+ .size = 22*1024*1024,
+ .type = SYSDUMP_RAM,
+ },
+ { /* LTE modem */
+ .paddr = 0x80000000 + 150*1024*1024,
+ .soff = 0xffffffff,
+ .size = 80*1024*1024,
+ .type = SYSDUMP_RAM,
+ },
+ { /* LEFT mem */
+ .paddr = 0x80000000 + 230*1024*1024,
+ .soff = 0xffffffff,
+ .size = 0,
+ .type = SYSDUMP_RAM,
+ },
+};
+int sprd_dump_mem_num = ARRAY_SIZE(sprd_dump_mem);
+#endif /* CONFIG_SPRD_SYSDUMP */
+
+static size_t get_elfhdr_size(int nphdr)
+{
+ size_t elfhdr_len;
+
+ elfhdr_len = sizeof(struct elfhdr) +
+ (nphdr + 1)*sizeof(struct elf_phdr) +
+ ((sizeof(struct elf_note)) * 3 +
+ roundup(sizeof(CORE_STR), 4)) +
+ roundup(sizeof(struct elf_prstatus), 4) +
+ roundup(sizeof(struct elf_prpsinfo), 4) +
+ roundup(sizeof(struct task_struct), 4);
+ elfhdr_len = PAGE_ALIGN(elfhdr_len);
+
+ return elfhdr_len;
+}
+
+static int notesize(struct memelfnote *en)
+{
+ int sz;
+
+ sz = sizeof(struct elf_note);
+ sz += roundup((strlen(en->name) + 1), 4);
+ sz += roundup(en->datasz, 4);
+
+ return sz;
+}
+
+static char *storenote(struct memelfnote *men, char *bufp)
+{
+ struct elf_note en;
+
+#define DUMP_WRITE(addr,nr) do { memcpy(bufp,addr,nr); bufp += nr; } while(0)
+
+ en.n_namesz = strlen(men->name) + 1;
+ en.n_descsz = men->datasz;
+ en.n_type = men->type;
+
+ DUMP_WRITE(&en, sizeof(en));
+ DUMP_WRITE(men->name, en.n_namesz);
+
+ /* XXX - cast from long long to long to avoid need for libgcc.a */
+ bufp = (char*) roundup((unsigned long)bufp,4);
+ DUMP_WRITE(men->data, men->datasz);
+ bufp = (char*) roundup((unsigned long)bufp,4);
+
+#undef DUMP_WRITE
+
+ return bufp;
+}
+
+/*
+ * fill up all the fields in prstatus from the given task struct, except
+ * registers which need to be filled up separately.
+ */
+static void fill_prstatus(struct elf_prstatus *prstatus,
+ struct task_struct *p, long signr)
+{
+ prstatus->pr_info.si_signo = prstatus->pr_cursig = signr;
+ prstatus->pr_sigpend = p->pending.signal.sig[0];
+ prstatus->pr_sighold = p->blocked.sig[0];
+ rcu_read_lock();
+ prstatus->pr_ppid = task_pid_vnr(rcu_dereference(p->real_parent));
+ rcu_read_unlock();
+ prstatus->pr_pid = task_pid_vnr(p);
+ prstatus->pr_pgrp = task_pgrp_vnr(p);
+ prstatus->pr_sid = task_session_vnr(p);
+ if ( 0 /* thread_group_leader(p) */) {
+ struct task_cputime cputime;
+
+ /*
+ * This is the record for the group leader. It shows the
+ * group-wide total, not its individual thread total.
+ */
+ /* thread_group_cputime(p, &cputime); */
+ cputime_to_timeval(cputime.utime, &prstatus->pr_utime);
+ cputime_to_timeval(cputime.stime, &prstatus->pr_stime);
+ } else {
+ cputime_to_timeval(p->utime, &prstatus->pr_utime);
+ cputime_to_timeval(p->stime, &prstatus->pr_stime);
+ }
+ cputime_to_timeval(p->signal->cutime, &prstatus->pr_cutime);
+ cputime_to_timeval(p->signal->cstime, &prstatus->pr_cstime);
+}
+
+static int fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p,
+ struct mm_struct *mm)
+{
+ const struct cred *cred;
+ unsigned int i, len;
+
+ /* first copy the parameters from user space */
+ memset(psinfo, 0, sizeof(struct elf_prpsinfo));
+
+ if (mm) {
+ len = mm->arg_end - mm->arg_start;
+ if (len >= ELF_PRARGSZ)
+ len = ELF_PRARGSZ-1;
+ if (copy_from_user(&psinfo->pr_psargs,
+ (const char __user *)mm->arg_start, len))
+ return -EFAULT;
+ for(i = 0; i < len; i++)
+ if (psinfo->pr_psargs[i] == 0)
+ psinfo->pr_psargs[i] = ' ';
+ psinfo->pr_psargs[len] = 0;
+ }
+
+ rcu_read_lock();
+ psinfo->pr_ppid = task_pid_vnr(rcu_dereference(p->real_parent));
+ rcu_read_unlock();
+ psinfo->pr_pid = task_pid_vnr(p);
+ psinfo->pr_pgrp = task_pgrp_vnr(p);
+ psinfo->pr_sid = task_session_vnr(p);
+
+ i = p->state ? ffz(~p->state) + 1 : 0;
+ psinfo->pr_state = i;
+ psinfo->pr_sname = (i > 5) ? '.' : "RSDTZW"[i];
+ psinfo->pr_zomb = psinfo->pr_sname == 'Z';
+ psinfo->pr_nice = task_nice(p);
+ psinfo->pr_flag = p->flags;
+
+ rcu_read_lock();
+ cred = __task_cred(p);
+ SET_UID(psinfo->pr_uid, cred->uid);
+ SET_GID(psinfo->pr_gid, cred->gid);
+ rcu_read_unlock();
+ strncpy(psinfo->pr_fname, p->comm, sizeof(psinfo->pr_fname));
+
+ return 0;
+}
+
+/**
+ * crash_setup_regs() - save registers for the panic kernel
+ * @newregs: registers are saved here
+ * @oldregs: registers to be saved (may be %NULL)
+ *
+ * Function copies machine registers from @oldregs to @newregs. If @oldregs is
+ * %NULL then current registers are stored there.
+ */
+static inline void crash_setup_regs(struct pt_regs *newregs,
+ struct pt_regs *oldregs)
+{
+ unsigned long tmp = 0;
+ if (oldregs) {
+ memcpy(newregs, oldregs, sizeof(*newregs));
+ } else {
+ __asm__ __volatile__ (
+ "stp x0, x1, [%[regs_base], #0x00]\n\t"
+ "stp x2, x3, [%[regs_base], #0x10]\n\t"
+ "stp x4, x5, [%[regs_base], #0x20]\n\t"
+ "stp x6, x7, [%[regs_base], #0x30]\n\t"
+ "stp x8, x9, [%[regs_base], #0x40]\n\t"
+ "stp x10, x11, [%[regs_base], #0x50]\n\t"
+ "stp x12, x13, [%[regs_base], #0x60]\n\t"
+ "stp x14, x15, [%[regs_base], #0x70]\n\t"
+ "stp x16, x17, [%[regs_base], #0x80]\n\t"
+ "stp x18, x19, [%[regs_base], #0x90]\n\t"
+ "stp x20, x21, [%[regs_base], #0x100]\n\t"
+ "stp x22, x23, [%[regs_base], #0x110]\n\t"
+ "stp x24, x25, [%[regs_base], #0x120]\n\t"
+ "stp x26, x27, [%[regs_base], #0x130]\n\t"
+ "stp x28, x29, [%[regs_base], #0x140]\n\t"
+ "mov %[tmp], sp\n\t"
+ "stp x30, %[tmp], [%[regs_base], #0x150]\n\t"
+ "adr %[pc], 1f\n\t"
+ "mrs %[cpsr], nzcv\n\t"
+ "mrs %[tmp], daif\n\t"
+ "orr %[cpsr], %[cpsr], %[tmp]\n\t"
+ "mrs %[tmp], CurrentEL\n\t"
+ "orr %[cpsr], %[cpsr], %[tmp]\n\t"
+ "mrs %[tmp], SPSel\n\t"
+ "orr %[cpsr], %[cpsr], %[tmp]\n\t"
+ "1:"
+ : [pc] "=r" (newregs->pc),
+ [cpsr] "=r" (newregs->pstate),
+ [tmp] "=r" (tmp)
+ : [regs_base] "r" (&newregs->regs)
+ : "memory"
+ );
+ }
+}
+
+void crash_note_save_cpu(struct pt_regs *regs, int cpu)
+{
+ struct elf_prstatus prstatus;
+ struct memelfnote notes;
+
+ notes.name = CORE_STR;
+ notes.type = NT_PRSTATUS;
+ notes.datasz = sizeof(struct elf_prstatus);
+ notes.data = &prstatus;
+
+ memset(&prstatus, 0, sizeof(struct elf_prstatus));
+ fill_prstatus(&prstatus, current, 0);
+ memcpy(&prstatus.pr_reg, regs, sizeof(struct pt_regs));
+ storenote(&notes, per_cpu_ptr(crash_notes, cpu));
+}
+
+static void sysdump_fill_core_hdr(struct pt_regs *regs,
+ struct sysdump_mem *sysmem, int mem_num,
+ char *bufp, int nphdr, int dataoff)
+{
+ struct elf_prstatus prstatus; /* NT_PRSTATUS */
+ struct elf_prpsinfo prpsinfo; /* NT_PRPSINFO */
+ struct elf_phdr *nhdr, *phdr;
+ struct elfhdr *elf;
+ struct memelfnote notes;
+ off_t offset = 0;
+ int i;
+
+ /* setup ELF header */
+ elf = (struct elfhdr *) bufp;
+ bufp += sizeof(struct elfhdr);
+ offset += sizeof(struct elfhdr);
+ memcpy(elf->e_ident, ELFMAG, SELFMAG);
+ elf->e_ident[EI_CLASS] = ELF_CLASS;
+ elf->e_ident[EI_DATA] = ELF_DATA;
+ elf->e_ident[EI_VERSION]= EV_CURRENT;
+ elf->e_ident[EI_OSABI] = ELF_OSABI;
+ memset(elf->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD);
+ elf->e_type = ET_CORE;
+ elf->e_machine = ELF_ARCH;
+ elf->e_version = EV_CURRENT;
+ elf->e_entry = 0;
+ elf->e_phoff = sizeof(struct elfhdr);
+ elf->e_shoff = 0;
+ elf->e_flags = ELF_CORE_EFLAGS;
+ elf->e_ehsize = sizeof(struct elfhdr);
+ elf->e_phentsize= sizeof(struct elf_phdr);
+ elf->e_phnum = nphdr;
+ elf->e_shentsize= 0;
+ elf->e_shnum = 0;
+ elf->e_shstrndx = 0;
+
+ /* setup ELF PT_NOTE program header */
+ nhdr = (struct elf_phdr *) bufp;
+ bufp += sizeof(struct elf_phdr);
+ offset += sizeof(struct elf_phdr);
+ nhdr->p_type = PT_NOTE;
+ nhdr->p_offset = 0;
+ nhdr->p_vaddr = 0;
+ nhdr->p_paddr = 0;
+ nhdr->p_filesz = 0;
+ nhdr->p_memsz = SYSDUMP_NOTE_BYTES*NR_CPUS;
+ nhdr->p_flags = 0;
+ nhdr->p_align = 0;
+
+ /* setup ELF PT_LOAD program header for every area */
+ for (i = 0; i < mem_num; i++) {
+ phdr = (struct elf_phdr *) bufp;
+ bufp += sizeof(struct elf_phdr);
+ offset += sizeof(struct elf_phdr);
+
+ phdr->p_type = PT_LOAD;
+ phdr->p_flags = PF_R|PF_W|PF_X;
+ phdr->p_offset = dataoff;
+ phdr->p_vaddr = sysmem[i].vaddr;
+ phdr->p_paddr = sysmem[i].paddr;
+ phdr->p_filesz = phdr->p_memsz = sysmem[i].size;
+ phdr->p_align = 0;//PAGE_SIZE;
+ dataoff += sysmem[i].size;
+ }
+
+ /*
+ * Set up the notes in similar form to SVR4 core dumps made
+ * with info from their /proc.
+ */
+ nhdr->p_offset = offset;
+ nhdr->p_filesz = 0;
+
+ return;
+} /* end elf_kcore_store_hdr() */
+
+static void sysdump_prepare_info(int enter_id, const char *reason,
+ struct pt_regs *regs)
+{
+ unsigned long long t;
+ unsigned long nanosec_rem;
+ int iocnt, i;
+ char *iomem;
+
+ strncpy(sprd_sysdump_extra.reason,
+ reason, sizeof(sprd_sysdump_extra.reason));
+ sprd_sysdump_extra.enter_id = enter_id;
+
+ sprd_sysdump_info = (struct sysdump_info *)phys_to_virt(SPRD_SYSDUMP_MAGIC);
+
+ printk("vaddr is %p,paddr is %p\n",sprd_sysdump_info, (void *)SPRD_SYSDUMP_MAGIC);
+ memcpy(sprd_sysdump_info->magic, SYSDUMP_MAGIC,
+ sizeof(sprd_sysdump_info->magic));
+
+ if (reason != NULL && !strcmp(reason, "Crash Key")) {
+ sprd_sysdump_info->crash_key = 1;
+ } else {
+ sprd_sysdump_info->crash_key = 0;
+ }
+
+ printk("reason: %s, sprd_sysdump_info->crash_key: %d\n", reason, sprd_sysdump_info->crash_key);
+
+ t = cpu_clock(smp_processor_id());
+ nanosec_rem = do_div(t, 1000000000);
+ sprintf(sprd_sysdump_info->time, "%lu.%06lu", (unsigned long)t,
+ nanosec_rem / 1000);
+
+ memcpy(sprd_sysdump_info->dump_path, sysdump_conf.dump_path,
+ sizeof(sprd_sysdump_info->dump_path));
+
+ sprd_sysdump_info->dump_mem_paddr = virt_to_phys(sprd_dump_mem);
+ sprd_sysdump_info->mem_num = sprd_dump_mem_num;
+ sprd_sysdump_info->elfhdr_size = get_elfhdr_size(sprd_sysdump_info->mem_num);
+
+ /* this must before sysdump_fill_core_hdr, it needs size */
+ for (i = 0; i < sprd_dump_mem_num; i++) {
+ if (SYSDUMP_RAM == sprd_dump_mem[i].type && 0 == sprd_dump_mem[i].size)
+ sprd_dump_mem[i].size = PHYS_OFFSET + (num_physpages << PAGE_SHIFT) - sprd_dump_mem[i].paddr;
+
+ if (!sysdump_conf.dump_modem && SYSDUMP_MODEM == sprd_dump_mem[i].type)
+ sprd_dump_mem[i].size = 0;
+ }
+
+ sysdump_fill_core_hdr(regs,
+ sprd_dump_mem,
+ sprd_dump_mem_num,
+ (char *)sprd_sysdump_info + sizeof(*sprd_sysdump_info),
+ sprd_sysdump_info->mem_num + 1,
+ sprd_sysdump_info->elfhdr_size);
+
+ iocnt = 0;
+ for (i = 0; i < sprd_dump_mem_num; i++) {
+ /* save iomem(regiters) to ram, cause they will change while rebooting */
+ if (0xffffffff != sprd_dump_mem[i].soff) {
+ sprd_dump_mem[i].soff = iocnt;
+ iomem = (char *)sprd_sysdump_info + sizeof(*sprd_sysdump_info) +
+ sprd_sysdump_info->elfhdr_size + iocnt;
+ memcpy(iomem,
+ (void const *)(sprd_dump_mem[i].vaddr),
+ sprd_dump_mem[i].size);
+ iocnt += sprd_dump_mem[i].size;
+ }
+ }
+
+ return;
+}
+
+
+struct sprd_debug_mmu_reg_t {
+ unsigned long sctlr_el1;
+ unsigned long ttbr0_el1;
+ unsigned long ttbr1_el1;
+ unsigned long tcr_el1;
+ unsigned long mair_el1;
+ unsigned long amair_el1;
+ unsigned long contextidr_el1;
+};
+
+/* ARM CORE regs mapping structure */
+struct sprd_debug_core_t {
+ /* COMMON */
+ unsigned long x0, x1, x2, x3, x4, x5, x6, x7, x8, x9;
+ unsigned long x10, x11, x12, x13, x14, x15, x16, x17, x18, x19;
+ unsigned long x20, x21, x22, x23, x24, x25, x26, x27, x28, x29;
+ unsigned long x30;
+
+ /* PC & CPSR */
+ unsigned long pc;
+ unsigned long pstate;
+};
+
+DEFINE_PER_CPU(struct sprd_debug_core_t, sprd_debug_core_reg);
+DEFINE_PER_CPU(struct sprd_debug_mmu_reg_t, sprd_debug_mmu_reg);
+
+
+/* core reg dump function*/
+static inline void sprd_debug_save_core_reg(struct sprd_debug_core_t *core_reg)
+{
+ unsigned long tmp;
+ __asm__ __volatile__ (
+ "stp x0, x1, [%[regs_base], #0x00]\n\t"
+ "stp x2, x3, [%[regs_base], #0x10]\n\t"
+ "stp x4, x5, [%[regs_base], #0x20]\n\t"
+ "stp x6, x7, [%[regs_base], #0x30]\n\t"
+ "stp x8, x9, [%[regs_base], #0x40]\n\t"
+ "stp x10, x11, [%[regs_base], #0x50]\n\t"
+ "stp x12, x13, [%[regs_base], #0x60]\n\t"
+ "stp x14, x15, [%[regs_base], #0x70]\n\t"
+ "stp x16, x17, [%[regs_base], #0x80]\n\t"
+ "stp x18, x19, [%[regs_base], #0x90]\n\t"
+ "stp x20, x21, [%[regs_base], #0x100]\n\t"
+ "stp x22, x23, [%[regs_base], #0x110]\n\t"
+ "stp x24, x25, [%[regs_base], #0x120]\n\t"
+ "stp x26, x27, [%[regs_base], #0x130]\n\t"
+ "stp x28, x29, [%[regs_base], #0x140]\n\t"
+ "mov %[tmp], sp\n\t"
+ "stp x30, %[tmp], [%[regs_base], #0x150]\n\t"
+ "adr %[pc], 1f\n\t"
+ "mrs %[cpsr], nzcv\n\t"
+ "mrs %[tmp], daif\n\t"
+ "orr %[cpsr], %[cpsr], %[tmp]\n\t"
+ "mrs %[tmp], CurrentEL\n\t"
+ "orr %[cpsr], %[cpsr], %[tmp]\n\t"
+ "mrs %[tmp], SPSel\n\t"
+ "orr %[cpsr], %[cpsr], %[tmp]\n\t"
+ "stp %[pc], %[cpsr], [%[regs_base], #0x160]\n\t"
+ "1:"
+ : [pc] "=r" (core_reg->pc),
+ [cpsr] "=r" (core_reg->pstate),
+ [tmp] "=r" (tmp)
+ : [regs_base] "r" (core_reg)
+ : "memory"
+ );
+
+ return;
+}
+
+static inline void sprd_debug_save_mmu_reg(struct sprd_debug_mmu_reg_t *mmu_reg)
+{
+ unsigned long tmp = 0;
+
+ asm volatile ("mrs %1, sctlr_el1\n\t"
+ "str %1, [%0]\n\t"
+ "mrs %1, ttbr0_el1\n\t"
+ "str %1, [%0, #8]\n\t"
+ "mrs %1, ttbr1_el1\n\t"
+ "str %1, [%0, #0x10]\n\t"
+ "mrs %1, tcr_el1\n\t"
+ "str %1, [%0, #0x18]\n\t"
+ "mrs %1, mair_el1\n\t"
+ "str %1, [%0, #0x20]\n\t"
+ "mrs %1, amair_el1\n\t"
+ "str %1, [%0, #0x28]\n\t"
+ "mrs %1, contextidr_el1\n\t"
+ "str %1, [%0, #0x30]\n\t"
+ :
+ : "r"(mmu_reg), "r"(tmp)
+ : "%0", "%1"
+ );
+}
+
+static inline void sprd_debug_save_context(void)
+{
+ unsigned long flags;
+ local_irq_save(flags);
+ sprd_debug_save_mmu_reg(&per_cpu(sprd_debug_mmu_reg, smp_processor_id()));
+ sprd_debug_save_core_reg(&per_cpu
+ (sprd_debug_core_reg, smp_processor_id()));
+
+ pr_emerg("(%s) context saved(CPU:%d)\n", __func__, smp_processor_id());
+ local_irq_restore(flags);
+
+ flush_cache_all();
+}
+
+
+extern void emergency_restart(void);
+void sysdump_enter(int enter_id, const char *reason, struct pt_regs *regs)
+{
+ struct pt_regs * pregs;
+ if (!sysdump_conf.enable)
+ return;
+
+ if ((sysdump_conf.crashkey_only) && !(reason != NULL && !strcmp(reason, "Crash Key")))
+ return;
+
+ /* this should before smp_send_stop() to make sysdump_ipi enable */
+ sprd_sysdump_extra.enter_cpu = smp_processor_id();
+
+ pregs = &sprd_sysdump_extra.cpu_context[sprd_sysdump_extra.enter_cpu];
+ if (regs)
+ memcpy(pregs, regs, sizeof(*regs));
+ else
+ crash_setup_regs((struct pt_regs *)pregs, NULL);
+
+ crash_note_save_cpu(pregs, sprd_sysdump_extra.enter_cpu);
+ sprd_debug_save_context();
+
+ smp_send_stop();
+ mdelay(1000);
+
+ printk("\n");
+ printk("*****************************************************\n");
+ printk("* *\n");
+ printk("* Sysdump enter, preparing debug info to dump ... *\n");
+ printk("* *\n");
+ printk("*****************************************************\n");
+ printk("\n");
+
+ flush_cache_all();
+ mdelay(1000);
+
+ sysdump_prepare_info(enter_id, reason, regs);
+
+ flush_cache_all();
+ mdelay(1000);
+
+ printk("\n");
+ printk("*****************************************************\n");
+ printk("* *\n");
+ printk("* Try to reboot system ... *\n");
+ printk("* *\n");
+ printk("*****************************************************\n");
+ printk("\n");
+
+ emergency_restart();
+
+
+ return;
+}
+
+void sysdump_ipi(struct pt_regs *regs)
+{
+ int cpu = smp_processor_id();
+
+ if (sprd_sysdump_extra.enter_cpu != -1)
+ {
+ memcpy((void *)&(sprd_sysdump_extra.cpu_context[cpu]),
+ (void *)regs, sizeof(struct pt_regs));
+ crash_note_save_cpu(regs, cpu);
+ sprd_debug_save_context();
+ }
+ return;
+}
+
+
+static ctl_table sysdump_sysctl_table[] = {
+ {
+ .procname = "sysdump_enable",
+ .data = &sysdump_conf.enable,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+ {
+ .procname = "sysdump_crashkey_only",
+ .data = &sysdump_conf.crashkey_only,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+ {
+ .procname = "sysdump_dump_modem",
+ .data = &sysdump_conf.dump_modem,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+ {
+ .procname = "sysdump_reboot",
+ .data = &sysdump_conf.reboot,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+ {
+ .procname = "sysdump_dump_path",
+ .data = sysdump_conf.dump_path,
+ .maxlen = sizeof(sysdump_conf.dump_path),
+ .mode = 0644,
+ .proc_handler = proc_dostring,
+ },
+ {}
+};
+
+static ctl_table sysdump_sysctl_root[] = {
+ {
+ .procname = "kernel",
+ .mode = 0555,
+ .child = sysdump_sysctl_table,
+ },
+ {}
+};
+
+
+static struct ctl_table_header *sysdump_sysctl_hdr = NULL;
+
+int sysdump_sysctl_init(void)
+{
+ char *phy_sprd_sysdump_magic;
+ int i;
+
+ for (i = 0; i < sprd_dump_mem_num; i++)
+ sprd_dump_mem[i].vaddr = __va(sprd_dump_mem[i].paddr);
+
+ sysdump_sysctl_hdr = register_sysctl_table(sysdump_sysctl_root);
+ if (!sysdump_sysctl_hdr)
+ return -ENOMEM;
+
+ crash_notes = alloc_percpu(note_buf_t);
+ if (crash_notes == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+
+void sysdump_sysctl_exit(void)
+{
+ if (sysdump_sysctl_hdr)
+ unregister_sysctl_table(sysdump_sysctl_hdr);
+}
+
+module_init(sysdump_sysctl_init);
+module_exit(sysdump_sysctl_exit);
+
+MODULE_AUTHOR("Jianjun.He <jianjun.he@spreadtrum.com>");
+MODULE_DESCRIPTION("kernel core dump for Spreadtrum");
+MODULE_LICENSE("GPL");