FAQ Search Today's Posts Mark Forums Read
» Video Reviews

» Linux Archive

Linux-archive is a website aiming to archive linux email lists and to make them easily accessible for linux users/developers.


» Sponsor

» Partners

» Sponsor

Go Back   Linux Archive > Redhat > Crash Utility

 
 
LinkBack Thread Tools
 
Old 06-30-2010, 10:09 AM
Mika Westerberg
 
Default crash: add ARM crashdump support

This patch adds minimal support for reading ARM crashdumps (currently only
diskdump format is supported). It contains necessary virtual to physical
translations and stack unwinding support.

Signed-off-by: Mika Westerberg <ext-mika.1.westerberg@nokia.com>
---
Makefile | 12 +-
arm.c | 1188 ++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++
defs.h | 131 +++++++
unwind_arm.c | 702 ++++++++++++++++++++++++++++++++++
4 files changed, 2031 insertions(+), 2 deletions(-)
create mode 100644 arm.c
create mode 100644 unwind_arm.c

diff --git a/Makefile b/Makefile
index 3ea6539..61d88c0 100644
--- a/Makefile
+++ b/Makefile
@@ -74,11 +74,12 @@ UNWIND_HFILES=unwind.h unwind_i.h rse.h unwind_x86.h unwind_x86_64.h
CFILES=main.c tools.c global_data.c memory.c filesys.c help.c task.c
kernel.c test.c gdb_interface.c configure.c net.c dev.c
alpha.c x86.c ppc.c ia64.c s390.c s390x.c s390dbf.c ppc64.c x86_64.c
+ arm.c
extensions.c remote.c va_server.c va_server_v1.c symbols.c cmdline.c
lkcd_common.c lkcd_v1.c lkcd_v2_v3.c lkcd_v5.c lkcd_v7.c lkcd_v8.c
lkcd_fix_mem.c s390_dump.c lkcd_x86_trace.c
netdump.c diskdump.c xendump.c unwind.c unwind_decoder.c
- unwind_x86_32_64.c
+ unwind_x86_32_64.c unwind_arm.c
xen_hyper.c xen_hyper_command.c xen_hyper_global_data.c
xen_hyper_dump_tables.c kvmdump.c qemu.c qemu-load.c

@@ -90,11 +91,12 @@ SOURCE_FILES=${CFILES} ${GENERIC_HFILES} ${MCORE_HFILES}
OBJECT_FILES=main.o tools.o global_data.o memory.o filesys.o help.o task.o
build_data.o kernel.o test.o gdb_interface.o net.o dev.o
alpha.o x86.o ppc.o ia64.o s390.o s390x.o s390dbf.o ppc64.o x86_64.o
+ arm.o
extensions.o remote.o va_server.o va_server_v1.o symbols.o cmdline.o
lkcd_common.o lkcd_v1.o lkcd_v2_v3.o lkcd_v5.o lkcd_v7.o lkcd_v8.o
lkcd_fix_mem.o s390_dump.o netdump.o diskdump.o xendump.o
lkcd_x86_trace.o unwind_v1.o unwind_v2.o unwind_v3.o
- unwind_x86_32_64.o
+ unwind_x86_32_64.o unwind_arm.o
xen_hyper.o xen_hyper_command.o xen_hyper_global_data.o
xen_hyper_dump_tables.o kvmdump.o qemu.o qemu-load.o

@@ -407,6 +409,9 @@ ppc64.o: ${GENERIC_HFILES} ppc64.c
x86_64.o: ${GENERIC_HFILES} ${REDHAT_HFILES} x86_64.c
cc -c ${CRASH_CFLAGS} x86_64.c ${WARNING_OPTIONS} ${WARNING_ERROR}

+arm.o: ${GENERIC_HFILES} ${REDHAT_HFILES} arm.c
+ cc -c ${CRASH_CFLAGS} arm.c ${WARNING_OPTIONS} ${WARNING_ERROR}
+
s390.o: ${GENERIC_HFILES} ${IBM_HFILES} s390.c
cc -c ${CRASH_CFLAGS} s390.c ${WARNING_OPTIONS} ${WARNING_ERROR}

@@ -448,6 +453,9 @@ lkcd_x86_trace.o: ${GENERIC_HFILES} ${LKCD_TRACE_HFILES} lkcd_x86_trace.c
unwind_x86_32_64.o: ${GENERIC_HFILES} ${UNWIND_HFILES} unwind_x86_32_64.c
cc -c ${CRASH_CFLAGS} unwind_x86_32_64.c -o unwind_x86_32_64.o ${WARNING_OPTIONS} ${WARNING_ERROR}

+unwind_arm.o: ${GENERIC_HFILES} ${UNWIND_HFILES} unwind_arm.c
+ cc -c ${CRASH_CFLAGS} unwind_arm.c -o unwind_arm.o ${WARNING_OPTIONS} ${WARNING_ERROR}
+
unwind_v1.o: ${GENERIC_HFILES} ${UNWIND_HFILES} unwind.c unwind_decoder.c
cc -c ${CRASH_CFLAGS} unwind.c -DREDHAT -DUNWIND_V1 -o unwind_v1.o ${WARNING_OPTIONS} ${WARNING_ERROR}

diff --git a/arm.c b/arm.c
new file mode 100644
index 0000000..40c860a
--- /dev/null
+++ b/arm.c
@@ -0,0 +1,1188 @@
+/*
+ * arm.c - core analysis suite
+ *
+ * Created by: Mika Westerberg <ext-mika.1.westerberg@nokia.com>
+ * Copyright (C) 2010 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ */
+
+#ifdef ARM
+#include <elf.h>
+
+#include "defs.h"
+
+static int arm_get_crash_notes(void);
+static int arm_verify_symbol(const char *, ulong, char);
+static int arm_is_module_addr(ulong);
+static int arm_is_kvaddr(ulong);
+static int arm_in_exception_text(ulong);
+static void arm_back_trace_cmd(struct bt_info *);
+static ulong arm_processor_speed(void);
+static int arm_translate_pte(ulong, void *, ulonglong);
+static int arm_vtop(ulong, ulong *, physaddr_t *, int);
+static int arm_kvtop(struct task_context *, ulong, physaddr_t *, int);
+static int arm_uvtop(struct task_context *, ulong, physaddr_t *, int);
+static int arm_get_frame(struct bt_info *, ulong *, ulong *);
+static int arm_get_dumpfile_stack_frame(struct bt_info *, ulong *, ulong *);
+static void arm_get_stack_frame(struct bt_info *, ulong *, ulong *);
+static void arm_dump_exception_stack(ulong, ulong);
+static ulong arm_vmalloc_start(void);
+static int arm_is_task_addr(ulong);
+static int arm_dis_filter(ulong, char *);
+static int arm_eframe_search(struct bt_info *);
+static ulong arm_get_task_pgd(ulong);
+static void arm_cmd_mach(void);
+static void arm_display_machine_stats(void);
+static int arm_get_smp_cpus(void);
+static void arm_init_machspec(void);
+
+static struct line_number_hook arm_line_number_hooks[];
+static struct machine_specific arm_machine_specific;
+
+/**
+ * struct arm_cpu_context_save - idle task registers
+ *
+ * This structure holds idle task registers. Only FP, SP, and PC are needed for
+ * unwinding the stack.
+ */
+struct arm_cpu_context_save {
+ ulong fp;
+ ulong sp;
+ ulong pc;
+};
+
+/*
+ * Holds registers during the crash.
+ */
+static struct arm_pt_regs panic_task_regs;
+
+#define PGDIR_SIZE() (4 * PAGESIZE())
+#define PGDIR_OFFSET(X) (((ulong)(X)) & (PGDIR_SIZE() - 1))
+
+#define _SECTION_PAGE_MASK (~((MEGABYTES(1))-1))
+
+#define PMD_TYPE_MASK 3
+#define PMD_TYPE_SECT 2
+#define PMD_TYPE_TABLE 1
+
+static inline ulong *
+pmd_page_addr(ulong pmd)
+{
+ ulong ptr;
+
+ ptr = pmd & ~(PTRS_PER_PTE * sizeof(void *) - 1);
+ ptr += PTRS_PER_PTE * sizeof(void *);
+
+ return (ulong *)ptr;
+}
+
+/*
+ * "Linux" PTE definitions.
+ */
+#define L_PTE_PRESENT (1 << 0)
+#define L_PTE_YOUNG (1 << 1)
+#define L_PTE_FILE (1 << 2)
+#define L_PTE_DIRTY (1 << 6)
+#define L_PTE_WRITE (1 << 7)
+#define L_PTE_USER (1 << 8)
+#define L_PTE_EXEC (1 << 9)
+#define L_PTE_SHARED (1 << 10)
+
+#define pte_val(pte) (pte)
+
+#define pte_present(pte) (pte_val(pte) & L_PTE_PRESENT)
+#define pte_write(pte) (pte_val(pte) & L_PTE_WRITE)
+#define pte_dirty(pte) (pte_val(pte) & L_PTE_DIRTY)
+#define pte_young(pte) (pte_val(pte) & L_PTE_YOUNG)
+
+/*
+ * Following stuff is taken directly from the kernel sources. These are used in
+ * dump_exception_stack() to format an exception stack entry.
+ */
+#define USR26_MODE 0x00000000
+#define FIQ26_MODE 0x00000001
+#define IRQ26_MODE 0x00000002
+#define SVC26_MODE 0x00000003
+#define USR_MODE 0x00000010
+#define FIQ_MODE 0x00000011
+#define IRQ_MODE 0x00000012
+#define SVC_MODE 0x00000013
+#define ABT_MODE 0x00000017
+#define UND_MODE 0x0000001b
+#define SYSTEM_MODE 0x0000001f
+#define MODE32_BIT 0x00000010
+#define MODE_MASK 0x0000001f
+#define PSR_T_BIT 0x00000020
+#define PSR_F_BIT 0x00000040
+#define PSR_I_BIT 0x00000080
+#define PSR_A_BIT 0x00000100
+#define PSR_E_BIT 0x00000200
+#define PSR_J_BIT 0x01000000
+#define PSR_Q_BIT 0x08000000
+#define PSR_V_BIT 0x10000000
+#define PSR_C_BIT 0x20000000
+#define PSR_Z_BIT 0x40000000
+#define PSR_N_BIT 0x80000000
+
+#define isa_mode(regs)
+ ((((regs)->ARM_cpsr & PSR_J_BIT) >> 23) |
+ (((regs)->ARM_cpsr & PSR_T_BIT) >> 5))
+
+#define processor_mode(regs)
+ ((regs)->ARM_cpsr & MODE_MASK)
+
+#define interrupts_enabled(regs)
+ (!((regs)->ARM_cpsr & PSR_I_BIT))
+
+#define fast_interrupts_enabled(regs)
+ (!((regs)->ARM_cpsr & PSR_F_BIT))
+
+static const char *processor_modes[] = {
+ "USER_26", "FIQ_26", "IRQ_26", "SVC_26", "UK4_26", "UK5_26",
+ "UK6_26", "UK7_26" , "UK8_26", "UK9_26", "UK10_26", "UK11_26",
+ "UK12_26", "UK13_26", "UK14_26", "UK15_26", "USER_32", "FIQ_32",
+ "IRQ_32", "SVC_32", "UK4_32", "UK5_32", "UK6_32", "ABT_32",
+ "UK8_32", "UK9_32", "UK10_32", "UND_32", "UK12_32", "UK13_32",
+ "UK14_32", "SYS_32",
+};
+
+static const char *isa_modes[] = {
+ "ARM" , "Thumb" , "Jazelle", "ThumbEE",
+};
+
+#define NOT_IMPLEMENTED()
+ error(FATAL, "%s: N/A
", __func__)
+
+/*
+ * Do all necessary machine-specific setup here. This is called several times
+ * during initialization.
+ */
+void
+arm_init(int when)
+{
+ switch (when) {
+ case PRE_SYMTAB:
+ machdep->verify_symbol = arm_verify_symbol;
+ machdep->machspec = &arm_machine_specific;
+ if (pc->flags & KERNEL_DEBUG_QUERY)
+ return;
+ machdep->pagesize = memory_page_size();
+ machdep->pageshift = ffs(machdep->pagesize) - 1;
+ machdep->pageoffset = machdep->pagesize - 1;
+ machdep->pagemask = ~((ulonglong)machdep->pageoffset);
+ machdep->stacksize = machdep->pagesize * 2;
+ machdep->last_pgd_read = 0;
+ machdep->last_pmd_read = 0;
+ machdep->last_ptbl_read = 0;
+ machdep->verify_paddr = generic_verify_paddr;
+ machdep->ptrs_per_pgd = PTRS_PER_PGD;
+ break;
+
+ case PRE_GDB:
+ if ((machdep->pgd = (char *)malloc(PGDIR_SIZE())) == NULL)
+ error(FATAL, "cannot malloc pgd space.");
+ if ((machdep->ptbl = (char *)malloc(PAGESIZE())) == NULL)
+ error(FATAL, "cannot malloc ptbl space.");
+
+ /*
+ * Kernel text starts 16k after PAGE_OFFSET.
+ */
+ machdep->kvbase = symbol_value("_stext") & 0xffff0000UL;
+ machdep->identity_map_base = machdep->kvbase;
+ machdep->is_kvaddr = arm_is_kvaddr;
+ machdep->is_uvaddr = generic_is_uvaddr;
+ machdep->eframe_search = arm_eframe_search;
+ machdep->back_trace = arm_back_trace_cmd;
+ machdep->processor_speed = arm_processor_speed;
+ machdep->uvtop = arm_uvtop;
+ machdep->kvtop = arm_kvtop;
+ machdep->get_task_pgd = arm_get_task_pgd;
+ machdep->get_stack_frame = arm_get_stack_frame;
+ machdep->get_stackbase = generic_get_stackbase;
+ machdep->get_stacktop = generic_get_stacktop;
+ machdep->translate_pte = arm_translate_pte;
+ machdep->memory_size = generic_memory_size;
+ machdep->vmalloc_start = arm_vmalloc_start;
+ machdep->is_task_addr = arm_is_task_addr;
+ machdep->dis_filter = arm_dis_filter;
+ machdep->cmd_mach = arm_cmd_mach;
+ machdep->get_smp_cpus = arm_get_smp_cpus;
+ machdep->line_number_hooks = arm_line_number_hooks;
+ machdep->value_to_symbol = generic_machdep_value_to_symbol;
+ machdep->init_kernel_pgd = NULL;
+
+ if (symbol_exists("irq_desc"))
+ machdep->dump_irq = generic_dump_irq;
+
+ arm_init_machspec();
+ break;
+
+ case POST_GDB:
+ if (symbol_exists("irq_desc"))
+ ARRAY_LENGTH_INIT(machdep->nr_irqs, irq_desc,
+ "irq_desc", NULL, 0);
+ /*
+ * Registers for idle threads are saved in
+ * thread_info.cpu_context.
+ */
+ STRUCT_SIZE_INIT(cpu_context_save, "cpu_context_save");
+ MEMBER_OFFSET_INIT(cpu_context_save_fp,
+ "cpu_context_save", "fp");
+ MEMBER_OFFSET_INIT(cpu_context_save_sp,
+ "cpu_context_save", "sp");
+ MEMBER_OFFSET_INIT(cpu_context_save_pc,
+ "cpu_context_save", "pc");
+ MEMBER_OFFSET_INIT(thread_info_cpu_context,
+ "thread_info", "cpu_context");
+
+ /*
+ * We need to have information about note_buf_t which is used to
+ * hold ELF note containing registers and status of the thread
+ * that panic'd.
+ */
+ STRUCT_SIZE_INIT(note_buf, "note_buf_t");
+
+ STRUCT_SIZE_INIT(elf_prstatus, "elf_prstatus");
+ MEMBER_OFFSET_INIT(elf_prstatus_pr_pid, "elf_prstatus",
+ "pr_pid");
+ MEMBER_OFFSET_INIT(elf_prstatus_pr_reg, "elf_prstatus",
+ "pr_reg");
+
+ /*
+ * crash_notes contains machine specific information about the
+ * crash. In particular, it contains CPU registers at the time
+ * of the crash. We need this information to extract correct
+ * backtraces from the panic task.
+ */
+ if (!arm_get_crash_notes())
+ error(WARNING, "Couldn't retrieve crash_notes
");
+ break;
+
+ case POST_VM:
+ machdep->machspec->vmalloc_start_addr = vt->high_memory;
+ /*
+ * Modules are placed in first vmalloc'd area. This is 16MB
+ * below PAGE_OFFSET.
+ */
+ machdep->machspec->modules_vaddr = first_vmalloc_address();
+ machdep->machspec->modules_end = machdep->kvbase - 1;
+
+ if (!init_unwind_tables()) {
+ error(WARNING, "Couldn't initialize unwind tables.
"
+ "This means that stack unwinding is not
"
+ "available for this core file.
");
+ }
+ break;
+ }
+}
+
+void
+arm_dump_machdep_table(ulong arg)
+{
+ const struct machine_specific *ms;
+ int others, i;
+
+ others = 0;
+ fprintf(fp, " flags: %lx (", machdep->flags);
+ if (machdep->flags & KSYMS_START)
+ fprintf(fp, "%sKSYMS_START", others++ ? "|" : "");
+ fprintf(fp, ")
");
+
+ fprintf(fp, " kvbase: %lx
", machdep->kvbase);
+ fprintf(fp, " identity_map_base: %lx
", machdep->kvbase);
+ fprintf(fp, " pagesize: %d
", machdep->pagesize);
+ fprintf(fp, " pageshift: %d
", machdep->pageshift);
+ fprintf(fp, " pagemask: %lx
", (ulong)machdep->pagemask);
+ fprintf(fp, " pageoffset: %lx
", machdep->pageoffset);
+ fprintf(fp, " stacksize: %ld
", machdep->stacksize);
+ fprintf(fp, " hz: %d
", machdep->hz);
+ fprintf(fp, " mhz: %ld
", machdep->mhz);
+ fprintf(fp, " memsize: %lld (0x%llx)
",
+ machdep->memsize, machdep->memsize);
+ fprintf(fp, " bits: %d
", machdep->bits);
+ fprintf(fp, " nr_irqs: %d
", machdep->nr_irqs);
+ fprintf(fp, " eframe_search: arm_eframe_search()
");
+ fprintf(fp, " back_trace: arm_back_trace_cmd()
");
+ fprintf(fp, " processor_speed: arm_processor_speed()
");
+ fprintf(fp, " uvtop: arm_uvtop()
");
+ fprintf(fp, " kvtop: arm_kvtop()
");
+ fprintf(fp, " get_task_pgd: arm_get_task_pgd()
");
+ fprintf(fp, " dump_irq: generic_dump_irq()
");
+ fprintf(fp, " get_stack_frame: arm_get_stack_frame()
");
+ fprintf(fp, " get_stackbase: generic_get_stackbase()
");
+ fprintf(fp, " get_stacktop: generic_get_stacktop()
");
+ fprintf(fp, " translate_pte: arm_translate_pte()
");
+ fprintf(fp, " memory_size: generic_memory_size()
");
+ fprintf(fp, " vmalloc_start: arm_vmalloc_start()
");
+ fprintf(fp, " is_task_addr: arm_is_task_addr()
");
+ fprintf(fp, " verify_symbol: arm_verify_symbol()
");
+ fprintf(fp, " dis_filter: arm_dis_filter()
");
+ fprintf(fp, " cmd_mach: arm_cmd_mach()
");
+ fprintf(fp, " get_smp_cpus: arm_get_smp_cpus()
");
+ fprintf(fp, " is_kvaddr: generic_is_kvaddr()
");
+ fprintf(fp, " is_uvaddr: generic_is_uvaddr()
");
+ fprintf(fp, " verify_paddr: generic_verify_paddr()
");
+ fprintf(fp, " xendump_p2m_create: NULL
");
+ fprintf(fp, "xen_kdump_p2m_create: NULL
");
+ fprintf(fp, " line_number_hooks: arm_line_number_hooks
");
+ fprintf(fp, " last_pgd_read: %lx
", machdep->last_pgd_read);
+ fprintf(fp, " last_pmd_read: %lx
", machdep->last_pmd_read);
+ fprintf(fp, " last_ptbl_read: %lx
", machdep->last_ptbl_read);
+ fprintf(fp, "clear_machdep_cache: NULL
");
+ fprintf(fp, " pgd: %lx
", (ulong)machdep->pgd);
+ fprintf(fp, " pmd: %lx
", (ulong)machdep->pmd);
+ fprintf(fp, " ptbl: %lx
", (ulong)machdep->ptbl);
+ fprintf(fp, " ptrs_per_pgd: %d
", machdep->ptrs_per_pgd);
+ fprintf(fp, " section_size_bits: %ld
", machdep->section_size_bits);
+ fprintf(fp, " max_physmem_bits: %ld
", machdep->max_physmem_bits);
+ fprintf(fp, " sections_per_root: %ld
", machdep->sections_per_root);
+
+ for (i = 0; i < MAX_MACHDEP_ARGS; i++) {
+ fprintf(fp, " cmdline_args[%d]: %s
",
+ i, machdep->cmdline_args[i] ?
+ machdep->cmdline_args[i] : "(unused)");
+ }
+
+ ms = machdep->machspec;
+
+ fprintf(fp, " machspec: %lx
", (ulong)ms);
+ fprintf(fp, " phys_base: %lx
", ms->phys_base);
+ fprintf(fp, " vmalloc_start_addr: %lx
", ms->vmalloc_start_addr);
+ fprintf(fp, " modules_vaddr: %lx
", ms->modules_vaddr);
+ fprintf(fp, " modules_end: %lx
", ms->modules_end);
+ fprintf(fp, " kernel_text_start: %lx
", ms->kernel_text_start);
+ fprintf(fp, " kernel_text_end: %lx
", ms->kernel_text_end);
+ fprintf(fp, "exception_text_start: %lx
", ms->exception_text_start);
+ fprintf(fp, " exception_text_end: %lx
", ms->exception_text_end);
+ fprintf(fp, " crash_task_pid: %ld
", ms->crash_task_pid);
+ fprintf(fp, " crash_task_regs: %lx
", (ulong)ms->crash_task_regs);
+}
+
+/*
+ * Retrieve task registers for the time of the crash.
+ */
+static int
+arm_get_crash_notes(void)
+{
+ struct machine_specific *ms = machdep->machspec;
+ ulong crash_notes;
+ Elf32_Nhdr *note;
+ ulong ptr, offset;
+ char *buf, *p;
+
+ if (!symbol_exists("crash_notes"))
+ return FALSE;
+
+ crash_notes = symbol_value("crash_notes");
+
+ if (kt->cpus > 1)
+ error(WARNING, "only one CPU is currently supported
");
+
+ /*
+ * Read crash_notes for the first CPU. crash_notes are in standard ELF
+ * note format.
+ */
+ if (!readmem(crash_notes, KVADDR, &ptr, sizeof(ptr), "crash_notes",
+ RETURN_ON_ERROR)) {
+ error(WARNING, "cannot read crash_notes
");
+ return FALSE;
+ }
+
+ buf = GETBUF(SIZE(note_buf));
+
+ if (!readmem(ptr, KVADDR, buf, SIZE(note_buf), "note_buf_t",
+ RETURN_ON_ERROR)) {
+ error(WARNING, "failed to read note_buf_t
");
+ goto fail;
+ }
+
+ /*
+ * Do some sanity checks for this note before reading registers from it.
+ */
+ note = (Elf32_Nhdr *)buf;
+ p = buf + sizeof(Elf32_Nhdr);
+
+ if (note->n_type != NT_PRSTATUS) {
+ error(WARNING, "invalid note (n_type != NT_PRSTATUS)
");
+ goto fail;
+ }
+ if (p[0] != 'C' || p[1] != 'O' || p[2] != 'R' || p[3] != 'E') {
+ error(WARNING, "invalid note (name != "CORE"
");
+ goto fail;
+ }
+
+ /*
+ * Find correct location of note data. This contains elf_prstatus
+ * structure which has registers etc. for the crashed task.
+ */
+ offset = sizeof(Elf32_Nhdr);
+ offset = roundup(offset + note->n_namesz, 4);
+ p = buf + offset; /* start of elf_prstatus */
+
+ BCOPY(p + OFFSET(elf_prstatus_pr_reg), &panic_task_regs,
+ sizeof(panic_task_regs));
+
+ /*
+ * And finally we have pid and registers for the crashed task. This is
+ * used later on when dumping backtrace.
+ */
+ ms->crash_task_pid = *(ulong *)(p + OFFSET(elf_prstatus_pr_pid));
+ ms->crash_task_regs = &panic_task_regs;
+
+ FREEBUF(buf);
+ return TRUE;
+
+fail:
+ FREEBUF(buf);
+ return FALSE;
+}
+
+/*
+ * Accept or reject a symbol from the kernel namelist.
+ */
+static int
+arm_verify_symbol(const char *name, ulong value, char type)
+{
+ if (STREQ(name, "swapper_pg_dir"))
+ machdep->flags |= KSYMS_START;
+
+ if (!name || !strlen(name) || !(machdep->flags & KSYMS_START))
+ return FALSE;
+
+ if (STREQ(name, "$a") || STREQ(name, "$n") || STREQ(name, "$d"))
+ return FALSE;
+
+ if (CRASHDEBUG(8) && name && strlen(name))
+ fprintf(fp, "%08lx %s
", value, name);
+
+ return TRUE;
+}
+
+static int
+arm_is_module_addr(ulong vaddr)
+{
+ ulong modules_start;
+ ulong modules_end = machdep->kvbase - 1;
+
+ if (!MODULES_VADDR) {
+ /*
+ * In case we are still initializing, and vm_init() has not been
+ * called, we use defaults here which is 16MB below kernel start
+ * address.
+ */
+ modules_start = machdep->kvbase - 16 * 1024 * 1024;
+ } else {
+ modules_start = MODULES_VADDR;
+ }
+
+ return (vaddr >= modules_start && vaddr <= modules_end);
+}
+
+int
+arm_is_vmalloc_addr(ulong vaddr)
+{
+ if (arm_is_module_addr(vaddr))
+ return TRUE;
+
+ if (!VMALLOC_START)
+ return FALSE;
+
+ return (vaddr >= VMALLOC_START);
+}
+
+/*
+ * Check whether given address falls inside kernel address space (including
+ * modules).
+ */
+static int
+arm_is_kvaddr(ulong vaddr)
+{
+ if (arm_is_module_addr(vaddr))
+ return TRUE;
+
+ return (vaddr >= machdep->kvbase);
+}
+
+/*
+ * Returns TRUE if given pc is in exception area.
+ */
+static int
+arm_in_exception_text(ulong pc)
+{
+ ulong exception_start = machdep->machspec->exception_text_start;
+ ulong exception_end = machdep->machspec->exception_text_end;
+
+ if (exception_start && exception_end)
+ return (pc >= exception_start && pc < exception_end);
+
+ return FALSE;
+}
+
+/*
+ * Unroll a kernel stack.
+ */
+static void
+arm_back_trace_cmd(struct bt_info *bt)
+{
+ if (!(kt->flags & DWARF_UNWIND)) {
+ error(WARNING, "unwinding without unwind tables is not "
+ "supported yet!
");
+ return;
+ }
+
+ if (!INSTACK(bt->stkptr, bt)) {
+ error(WARNING, "unwinding exception stack is not supported
");
+ return;
+ }
+
+ unwind_backtrace(bt);
+}
+
+/*
+ * Calculate and return the speed of the processor.
+ */
+static ulong
+arm_processor_speed(void)
+{
+ /*
+ * For now, we don't support reading CPU speed.
+ */
+ return 0;
+}
+
+/*
+ * Translate a PTE, returning TRUE if the page is present. If a physaddr pointer
+ * is passed in, don't print anything.
+ */
+static int
+arm_translate_pte(ulong pte, void *physaddr, ulonglong pae_pte)
+{
+ char ptebuf[BUFSIZE];
+ char physbuf[BUFSIZE];
+ char buf[BUFSIZE];
+ int page_present;
+ ulong paddr;
+ int len1, len2, others;
+
+ page_present = pte_present(pte);
+ paddr = PAGEBASE(pte);
+
+ if (physaddr) {
+ *((ulong *)physaddr) = paddr;
+ return page_present;
+ }
+
+ sprintf(ptebuf, "%lx", pte);
+ len1 = MAX(strlen(ptebuf), strlen("PTE"));
+ fprintf(fp, "%s ", mkstring(buf, len1, CENTER | LJUST, "PTE"));
+
+ if (!page_present && pte) {
+ /* swap page, not handled yet */
+ return page_present;
+ }
+
+ sprintf(physbuf, "%lx", paddr);
+ len2 = MAX(strlen(physbuf), strlen("PHYSICAL"));
+ fprintf(fp, "%s ", mkstring(buf, len2, CENTER | LJUST, "PHYSICAL"));
+
+ fprintf(fp, "FLAGS
");
+ fprintf(fp, "%s %s ",
+ mkstring(ptebuf, len1, CENTER | RJUST, NULL),
+ mkstring(physbuf, len2, CENTER | RJUST, NULL));
+
+ fprintf(fp, "(");
+ others = 0;
+
+ if (pte) {
+ if (pte_present(pte))
+ fprintf(fp, "%sPRESENT", others++ ? "|" : "");
+ if (pte_write(pte))
+ fprintf(fp, "%sWRITE", others++ ? "|" : "");
+ if (pte_dirty(pte))
+ fprintf(fp, "%sDIRTY", others++ ? "|" : "");
+ if (pte_young(pte))
+ fprintf(fp, "%sYOUNG", others++ ? "|" : "");
+ } else {
+ fprintf(fp, "no mapping");
+ }
+
+ fprintf(fp, ")
");
+
+ return 0;
+}
+
+/*
+ * Virtual to physical memory translation. This function will be called by both
+ * arm_kvtop() and arm_uvtop().
+ */
+static int
+arm_vtop(ulong vaddr, ulong *pgd, physaddr_t *paddr, int verbose)
+{
+ char buf[BUFSIZE];
+ ulong *page_dir;
+ ulong *page_middle;
+ ulong *page_table;
+ ulong pgd_pte;
+ ulong pmd_pte;
+ ulong pte;
+
+ /*
+ * Page tables in ARM Linux
+ *
+ * In hardware PGD is 16k (having 4096 pointers to PTE) and PTE is 1k
+ * (containing 256 translations).
+ *
+ * Linux, however, wants to have PTEs as page sized entities. This means
+ * that in ARM Linux we have following setup (see also
+ * arch/arm/include/asm/pgtable.h)
+ *
+ * PGD PTE
+ * +---------+
+ * | | 0 ----> +------------+
+ * +- - - - -+ | h/w pt 0 |
+ * | | 4 ----> +------------+ +1024
+ * +- - - - -+ | h/w pt 1 |
+ * . . +------------+ +2048
+ * . . | Linux pt 0 |
+ * . . +------------+ +3072
+ * | | 4095 | Linux pt 1 |
+ * +---------+ +------------+ +4096
+ *
+ * So in Linux implementation we have two hardware pointers to second
+ * level page tables. After these come "Linux" versions of the page
+ * tables.
+ *
+ * Linux PT entries contain bits that are not supported on hardware, for
+ * example "young" and "dirty" flags.
+ *
+ * Our translation scheme only uses Linux PTEs here. Hardware entries
+ * are 1024 bytes below Linux versions.
+ */
+
+ if (verbose)
+ fprintf(fp, "PAGE DIRECTORY: %lx
", (ulong)pgd);
+
+ /*
+ * pgd_offset(pgd, vaddr)
+ */
+ page_dir = pgd + PGD_OFFSET(vaddr) * 2;
+
+ FILL_PGD(PAGEBASE(pgd), KVADDR, PGDIR_SIZE());
+ pgd_pte = ULONG(machdep->pgd + PGDIR_OFFSET(page_dir));
+
+ if (verbose)
+ fprintf(fp, " PGD: %s => %lx
",
+ mkstring(buf, VADDR_PRLEN, RJUST | LONG_HEX,
+ MKSTR((ulong)page_dir)), pgd_pte);
+
+ if (!pgd_pte)
+ return FALSE;
+
+ /*
+ * pmd_offset(pgd, vaddr)
+ *
+ * Here PMD is folded into a PGD.
+ */
+ pmd_pte = pgd_pte;
+ page_middle = page_dir;
+
+ if (verbose)
+ fprintf(fp, " PMD: %s => %lx
",
+ mkstring(buf, VADDR_PRLEN, RJUST | LONG_HEX,
+ MKSTR((ulong)page_middle)), pmd_pte);
+
+ if ((pmd_pte & PMD_TYPE_MASK) == PMD_TYPE_SECT) {
+ if (verbose) {
+ fprintf(fp, " PAGE: %s (1MB)

",
+ mkstring(buf, VADDR_PRLEN, RJUST | LONG_HEX,
+ MKSTR(PAGEBASE(pmd_pte))));
+ }
+ *paddr = PAGEBASE(pmd_pte) + (vaddr & ~_SECTION_PAGE_MASK);
+ return TRUE;
+ }
+
+ /*
+ * pte_offset_map(pmd, vaddr)
+ */
+ page_table = (ulong *)PTOV(pmd_page_addr(pmd_pte)) + PTE_OFFSET(vaddr);
+
+ FILL_PTBL(PAGEBASE(page_table), KVADDR, PAGESIZE());
+ pte = ULONG(machdep->ptbl + PAGEOFFSET(page_table));
+
+ if (verbose) {
+ fprintf(fp, " PTE: %s => %lx

",
+ mkstring(buf, VADDR_PRLEN, RJUST | LONG_HEX,
+ MKSTR((ulong)page_table)), pte);
+ }
+
+ if (!pte_present(pte)) {
+ if (pte && verbose) {
+ fprintf(fp, "
");
+ arm_translate_pte(pte, 0, 0);
+ }
+ return FALSE;
+ }
+
+ *paddr = PAGEBASE(pte) + PAGEOFFSET(vaddr);
+
+ if (verbose) {
+ fprintf(fp, " PAGE: %s

",
+ mkstring(buf, VADDR_PRLEN, RJUST | LONG_HEX,
+ MKSTR(PAGEBASE(pte))));
+ arm_translate_pte(pte, 0, 0);
+ }
+
+ return TRUE;
+}
+
+/*
+ * Translates a user virtual address to its physical address. cmd_vtop() sets
+ * the verbose flag so that the pte translation gets displayed; all other
+ * callers quietly accept the translation.
+ */
+static int
+arm_uvtop(struct task_context *tc, ulong uvaddr, physaddr_t *paddr, int verbose)
+{
+ ulong *pgd;
+ ulong mm;
+
+ if (!tc)
+ error(FATAL, "current context invalid
");
+
+ *paddr = 0;
+
+ if (IS_KVADDR(uvaddr))
+ return arm_kvtop(tc, uvaddr, paddr, verbose);
+
+ mm = task_mm(tc->task, TRUE);
+ if (mm)
+ pgd = ULONG_PTR(tt->mm_struct + OFFSET(mm_struct_pgd));
+ else
+ readmem(tc->mm_struct + OFFSET(mm_struct_pgd), KVADDR, &pgd,
+ sizeof(long), "mm_struct pgd", FAULT_ON_ERROR);
+
+ return arm_vtop(uvaddr, pgd, paddr, verbose);
+}
+
+/*
+ * Translates a kernel virtual address to its physical address. cmd_vtop() sets
+ * the verbose flag so that the pte translation gets displayed; all other
+ * callers quietly accept the translation.
+ */
+static int
+arm_kvtop(struct task_context *tc, ulong kvaddr, physaddr_t *paddr, int verbose)
+{
+ if (!IS_KVADDR(kvaddr))
+ return FALSE;
+
+ if (!vt->vmalloc_start) {
+ *paddr = VTOP(kvaddr);
+ return TRUE;
+ }
+
+ if (!IS_VMALLOC_ADDR(kvaddr)) {
+ *paddr = VTOP(kvaddr);
+ if (!verbose)
+ return TRUE;
+ }
+
+ return arm_vtop(kvaddr, (ulong *)vt->kernel_pgd[0], paddr, verbose);
+}
+
+/*
+ * Get SP and PC values for idle tasks.
+ */
+static int
+arm_get_frame(struct bt_info *bt, ulong *pcp, ulong *spp)
+{
+ const char *cpu_context;
+
+ if (!bt->tc || !(tt->flags & THREAD_INFO))
+ return FALSE;
+
+ /*
+ * Update thread_info in tt.
+ */
+ if (!fill_thread_info(bt->tc->thread_info))
+ return FALSE;
+
+ cpu_context = tt->thread_info + OFFSET(thread_info_cpu_context);
+
+#define GET_REG(ptr, cp, off) ((*ptr) = (*((ulong *)((cp) + OFFSET(off)))))
+ /*
+ * Unwinding code needs FP value also so we pass it with bt.
+ */
+ GET_REG(&bt->frameptr, cpu_context, cpu_context_save_fp);
+ GET_REG(spp, cpu_context, cpu_context_save_sp);
+ GET_REG(pcp, cpu_context, cpu_context_save_pc);
+
+ return TRUE;
+}
+
+/*
+ * Get the starting point for the active cpu in a diskdump.
+ *
+ * Note that we currently support only UP machines. In future we might want to
+ * support SMP machines as well.
+ */
+static int
+arm_get_dumpfile_stack_frame(struct bt_info *bt, ulong *nip, ulong *ksp)
+{
+ const struct machine_specific *ms = machdep->machspec;
+
+ if (!ms->crash_task_regs)
+ return FALSE;
+
+ if (tt->panic_task != bt->task || bt->tc->pid != ms->crash_task_pid)
+ return FALSE;
+
+ /*
+ * We got registers for panic task from crash_notes. Just return them.
+ */
+ *nip = ms->crash_task_regs->ARM_pc;
+ *ksp = ms->crash_task_regs->ARM_sp;
+
+ /*
+ * Also store pointer to all registers in case unwinding code needs
+ * to access LR.
+ */
+ bt->machdep = ms->crash_task_regs;
+
+ return TRUE;
+}
+
+/*
+ * Get a stack frame combination of PC and SP from the most relevant spot.
+ */
+static void
+arm_get_stack_frame(struct bt_info *bt, ulong *pcp, ulong *spp)
+{
+ ulong ip, sp;
+ int ret;
+
+ ip = sp = 0;
+ bt->machdep = NULL;
+
+ if (DUMPFILE() && is_task_active(bt->task))
+ ret = arm_get_dumpfile_stack_frame(bt, &ip, &sp);
+ else
+ ret = arm_get_frame(bt, &ip, &sp);
+
+ if (!ret) {
+ error(WARNING, "cannot get stackframe for task
");
+ return;
+ }
+
+ if (pcp)
+ *pcp = ip;
+ if (spp)
+ *spp = sp;
+}
+
+/*
+ * Prints out exception stack starting from start.
+ */
+static void
+arm_dump_exception_stack(ulong start, ulong end)
+{
+ struct arm_pt_regs regs;
+ ulong flags;
+ char buf[64];
+
+ if (!readmem(start, KVADDR, &regs, sizeof(regs),
+ "exception regs", RETURN_ON_ERROR)) {
+ error(WARNING, "failed to read exception registers
");
+ return;
+ }
+
+ fprintf(fp, " pc : [<%08lx>] lr : [<%08lx>] psr: %08lx
"
+ " sp : %08lx ip : %08lx fp : %08lx
",
+ regs.ARM_pc, regs.ARM_lr, regs.ARM_cpsr,
+ regs.ARM_sp, regs.ARM_ip, regs.ARM_fp);
+ fprintf(fp, " r10: %08lx r9 : %08lx r8 : %08lx
",
+ regs.ARM_r10, regs.ARM_r9, regs.ARM_r8);
+ fprintf(fp, " r7 : %08lx r6 : %08lx r5 : %08lx r4 : %08lx
",
+ regs.ARM_r7, regs.ARM_r6,
+ regs.ARM_r5, regs.ARM_r4);
+ fprintf(fp, " r3 : %08lx r2 : %08lx r1 : %08lx r0 : %08lx
",
+ regs.ARM_r3, regs.ARM_r2,
+ regs.ARM_r1, regs.ARM_r0);
+
+ flags = regs.ARM_cpsr;
+ buf[0] = flags & PSR_N_BIT ? 'N' : 'n';
+ buf[1] = flags & PSR_Z_BIT ? 'Z' : 'z';
+ buf[2] = flags & PSR_C_BIT ? 'C' : 'c';
+ buf[3] = flags & PSR_V_BIT ? 'V' : 'v';
+ buf[4] = '';
+
+ fprintf(fp, " Flags: %s IRQs o%s FIQs o%s Mode %s ISA %s
",
+ buf, interrupts_enabled(&regs) ? "n" : "ff",
+ fast_interrupts_enabled(&regs) ? "n" : "ff",
+ processor_modes[processor_mode(&regs)],
+ isa_modes[isa_mode(&regs)]);
+}
+
+/*
+ * Pretty prints a single stack frame.
+ */
+void
+arm_dump_backtrace_entry(struct bt_info *bt, int level, ulong where,
+ ulong from, ulong frame)
+{
+ struct load_module *lm;
+ const char *name;
+
+ name = closest_symbol(where);
+
+ if (module_symbol(where, NULL, &lm, NULL, 0)) {
+ fprintf(fp, "%s#%d [<%08lx>] (%s [%s]) from [<%08lx>]
",
+ level < 10 ? " " : "",
+ level, where, name, lm->mod_name, from);
+ } else {
+ fprintf(fp, "%s#%d [<%08lx>] (%s) from [<%08lx>]
",
+ level < 10 ? " " : "",
+ level, where, name, from);
+ }
+
+ if (bt->flags & BT_LINE_NUMBERS) {
+ char buf[BUFSIZE];
+
+ get_line_number(where, buf, FALSE);
+ if (strlen(buf))
+ fprintf(fp, " %s
", buf);
+ }
+
+ if (arm_in_exception_text(where)) {
+ ulong frame_start = frame + 4;
+ ulong frame_end = frame_start + sizeof(struct arm_pt_regs);
+
+ arm_dump_exception_stack(frame_start, frame_end);
+ }
+}
+
+/*
+ * Determine where vmalloc'd memory starts.
+ */
+static ulong
+arm_vmalloc_start(void)
+{
+ return vt->high_memory;
+}
+
+/*
+ * Checks whether given task is valid task address.
+ */
+static int
+arm_is_task_addr(ulong task)
+{
+ if (tt->flags & THREAD_INFO)
+ return IS_KVADDR(task);
+
+ return (IS_KVADDR(task) && ALIGNED_STACK_OFFSET(task) == 0);
+}
+
+/*
+ * Filter dissassembly output if the output radix is not gdb's default 10
+ */
+static int
+arm_dis_filter(ulong vaddr, char *inbuf)
+{
+ char buf1[BUFSIZE];
+ char buf2[BUFSIZE];
+ char *colon, *p1;
+ int argc;
+ char *argv[MAXARGS];
+ ulong value;
+
+ if (!inbuf)
+ return TRUE;
+/*
+ * For some reason gdb can go off into the weeds translating text addresses,
+ * (on alpha -- not necessarily seen on arm) so this routine both fixes the
+ * references as well as imposing the current output radix on the translations.
+ */
+ console("IN: %s", inbuf);
+
+ colon = strstr(inbuf, ":");
+
+ if (colon) {
+ sprintf(buf1, "0x%lx <%s>", vaddr,
+ value_to_symstr(vaddr, buf2, pc->output_radix));
+ sprintf(buf2, "%s%s", buf1, colon);
+ strcpy(inbuf, buf2);
+ }
+
+ strcpy(buf1, inbuf);
+ argc = parse_line(buf1, argv);
+
+ if ((FIRSTCHAR(argv[argc-1]) == '<') &&
+ (LASTCHAR(argv[argc-1]) == '>')) {
+ p1 = rindex(inbuf, '<');
+ while ((p1 > inbuf) && !STRNEQ(p1, " 0x"))
+ p1--;
+
+ if (!STRNEQ(p1, " 0x"))
+ return FALSE;
+ p1++;
+
+ if (!extract_hex(p1, &value, NULLCHAR, TRUE))
+ return FALSE;
+
+ sprintf(buf1, "0x%lx <%s>
", value,
+ value_to_symstr(value, buf2, pc->output_radix));
+
+ sprintf(p1, buf1);
+ }
+
+ console(" %s", inbuf);
+
+ return TRUE;
+}
+
+/*
+ * Look for likely exception frames in a stack.
+ */
+static int
+arm_eframe_search(struct bt_info *bt)
+{
+ return (NOT_IMPLEMENTED());
+}
+
+/*
+ * Get the relevant page directory pointer from a task structure.
+ */
+static ulong
+arm_get_task_pgd(ulong task)
+{
+ return (NOT_IMPLEMENTED());
+}
+
+/*
+ * Machine dependent command.
+ */
+static void
+arm_cmd_mach(void)
+{
+ int c;
+
+ while ((c = getopt(argcnt, args, "cm")) != -1) {
+ switch (c) {
+ case 'c':
+ case 'm':
+ fprintf(fp, "ARM: '-%c' option is not supported
", c);
+ break;
+
+ default:
+ argerrs++;
+ break;
+ }
+ }
+
+ if (argerrs)
+ cmd_usage(pc->curcmd, SYNOPSIS);
+
+ arm_display_machine_stats();
+}
+
+static void
+arm_display_machine_stats(void)
+{
+ struct new_utsname *uts;
+ char buf[BUFSIZE];
+ ulong mhz;
+
+ uts = &kt->utsname;
+
+ fprintf(fp, " MACHINE TYPE: %s
", uts->machine);
+ fprintf(fp, " MEMORY SIZE: %s
", get_memory_size(buf));
+ fprintf(fp, " CPUS: %d
", get_cpus_to_display());
+ fprintf(fp, " PROCESSOR SPEED: ");
+ if ((mhz = machdep->processor_speed()))
+ fprintf(fp, "%ld Mhz
", mhz);
+ else
+ fprintf(fp, "(unknown)
");
+ fprintf(fp, " HZ: %d
", machdep->hz);
+ fprintf(fp, " PAGE SIZE: %d
", PAGESIZE());
+ fprintf(fp, "KERNEL VIRTUAL BASE: %lx
", machdep->kvbase);
+ fprintf(fp, "KERNEL VMALLOC BASE: %lx
", vt->vmalloc_start);
+ fprintf(fp, " KERNEL STACK SIZE: %ld
", STACKSIZE());
+}
+
+static int
+arm_get_smp_cpus(void)
+{
+ return get_cpus_online();
+}
+
+/*
+ * Initialize ARM specific stuff.
+ */
+static void
+arm_init_machspec(void)
+{
+ struct machine_specific *ms = machdep->machspec;
+ ulong phys_base;
+
+ if (!DISKDUMP_DUMPFILE())
+ error(FATAL, "Only diskdump format is currently supported!
");
+
+ /*
+ * First determine actual phys_base. This was set in place by
+ * makedumpfile so we can just read what diskdump gives us.
+ */
+ if (!diskdump_phys_base(&phys_base))
+ error(FATAL, "Cannot determine phys_base
");
+
+ ms->phys_base = phys_base;
+
+ if (symbol_exists("__exception_text_start") &&
+ symbol_exists("__exception_text_end")) {
+ ms->exception_text_start = symbol_value("__exception_text_start");
+ ms->exception_text_end = symbol_value("__exception_text_end");
+ }
+
+ if (symbol_exists("_stext") && symbol_exists("_etext")) {
+ ms->kernel_text_start = symbol_value("_stext");
+ ms->kernel_text_end = symbol_value("_etext");
+ }
+
+ if (CRASHDEBUG(1)) {
+ fprintf(fp, "compressed kdump: phys_base: %lx
",
+ phys_base);
+ fprintf(fp, "kernel text: [%lx - %lx]
",
+ ms->kernel_text_start, ms->kernel_text_end);
+ fprintf(fp, "exception text: [%lx - %lx]
",
+ ms->exception_text_start, ms->exception_text_end);
+ }
+}
+
+static const char *hook_files[] = {
+ "arch/arm/kernel/entry-armv.S",
+ "arch/arm/kernel/entry-common.S",
+};
+
+#define ENTRY_ARMV_S ((char **)&hook_files[0])
+#define ENTRY_COMMON_S ((char **)&hook_files[1])
+
+static struct line_number_hook arm_line_number_hooks[] = {
+ { "__dabt_svc", ENTRY_ARMV_S },
+ { "__irq_svc", ENTRY_ARMV_S },
+ { "__und_svc", ENTRY_ARMV_S },
+ { "__pabt_svc", ENTRY_ARMV_S },
+ { "__switch_to", ENTRY_ARMV_S },
+
+ { "ret_fast_syscall", ENTRY_COMMON_S },
+ { "ret_slow_syscall", ENTRY_COMMON_S },
+ { "ret_from_fork", ENTRY_COMMON_S },
+ { NULL, NULL },
+};
+#endif /* ARM */
diff --git a/defs.h b/defs.h
index bd8d492..0f63539 100644
--- a/defs.h
+++ b/defs.h
@@ -84,6 +84,9 @@
#ifdef S390X
#define NR_CPUS (64)
#endif
+#ifdef ARM
+#define NR_CPUS (1)
+#endif

#define BUFSIZE (1500)
#define NULLCHAR ('')
@@ -976,6 +979,7 @@ struct offset_table { /* stash of commonly-used offsets */
long thread_info_cpu;
long thread_info_previous_esp;
long thread_info_flags;
+ long thread_info_cpu_context;
long nsproxy_mnt_ns;
long mnt_namespace_root;
long mnt_namespace_list;
@@ -1427,6 +1431,13 @@ struct offset_table { /* stash of commonly-used offsets */
long unwind_table_size;
long unwind_table_link;
long unwind_table_name;
+ long unwind_table_list;
+ long unwind_table_start;
+ long unwind_table_stop;
+ long unwind_table_begin_addr;
+ long unwind_table_end_addr;
+ long unwind_idx_addr;
+ long unwind_idx_insn;
long rq_cfs;
long rq_rt;
long rq_nr_running;
@@ -1504,6 +1515,11 @@ struct offset_table { /* stash of commonly-used offsets */
long mm_rss_stat_count;
long module_module_init;
long module_init_text_size;
+ long cpu_context_save_fp;
+ long cpu_context_save_sp;
+ long cpu_context_save_pc;
+ long elf_prstatus_pr_pid;
+ long elf_prstatus_pr_reg;
};

struct size_table { /* stash of commonly-used sizes */
@@ -1601,6 +1617,7 @@ struct size_table { /* stash of commonly-used sizes */
long mem_section;
long pid_link;
long unwind_table;
+ long unwind_idx;
long rlimit;
long kmem_cache;
long kmem_cache_node;
@@ -1616,6 +1633,9 @@ struct size_table { /* stash of commonly-used sizes */
long module_sect_attr;
long task_struct_utime;
long task_struct_stime;
+ long cpu_context_save;
+ long note_buf;
+ long elf_prstatus;
};

struct array_table {
@@ -2090,6 +2110,49 @@ struct load_module {
* Machine specific stuff
*/

+#ifdef ARM
+#define _32BIT_
+#define MACHINE_TYPE "ARM"
+
+#define PAGEBASE(X) (((ulong)(X)) & (ulong)machdep->pagemask)
+
+#define PTOV(X)
+ ((unsigned long)(X)-(machdep->machspec->phys_base)+(machdep->kvbase))
+#define VTOP(X)
+ ((unsigned long)(X)-(machdep->kvbase)+(machdep->machspec->phys_base))
+
+#define IS_VMALLOC_ADDR(X) arm_is_vmalloc_addr((ulong)(X))
+
+#define MODULES_VADDR (machdep->machspec->modules_vaddr)
+#define MODULES_END (machdep->machspec->modules_end)
+#define VMALLOC_START (machdep->machspec->vmalloc_start_addr)
+#define VMALLOC_END (machdep->machspec->vmalloc_end)
+
+#define PGDIR_SHIFT (21)
+#define PTRS_PER_PTE (512)
+#define PTRS_PER_PGD (2048)
+
+#define PGD_OFFSET(vaddr) ((vaddr) >> PGDIR_SHIFT)
+#define PTE_OFFSET(vaddr) (((vaddr) >> PAGESHIFT()) & (PTRS_PER_PTE - 1))
+
+#define __SWP_TYPE_SHIFT 3
+#define __SWP_TYPE_BITS 6
+#define __SWP_TYPE_MASK ((1 << __SWP_TYPE_BITS) - 1)
+#define __SWP_OFFSET_SHIFT (__SWP_TYPE_BITS + __SWP_TYPE_SHIFT)
+
+#define SWP_TYPE(entry) (((entry) >> __SWP_TYPE_SHIFT) & __SWP_TYPE_MASK)
+#define SWP_OFFSET(entry) ((entry) >> __SWP_OFFSET_SHIFT)
+
+#define __swp_type(entry) SWP_TYPE(entry)
+#define __swp_offset(entry) SWP_OFFSET(entry)
+
+#define TIF_SIGPENDING (2)
+
+#define _SECTION_SIZE_BITS 28
+#define _MAX_PHYSMEM_BITS 32
+
+#endif /* ARM */
+
#ifdef X86
#define _32BIT_
#define MACHINE_TYPE "X86"
@@ -2786,6 +2849,10 @@ struct efi_memory_desc_t {
#define SIZEOF_16BIT (2)
#define SIZEOF_8BIT (1)

+#ifdef ARM
+#define MAX_HEXADDR_STRLEN (8)
+#define UVADDR_PRLEN (8)
+#endif
#ifdef X86
#define MAX_HEXADDR_STRLEN (8)
#define UVADDR_PRLEN (8)
@@ -2873,6 +2940,13 @@ struct efi_memory_desc_t {
#define IRQ_LEVEL 64 /* IRQ level triggered */
#define IRQ_MASKED 128 /* IRQ masked - shouldn't be seen again */

+#ifdef ARM
+#define SA_PROBE SA_ONESHOT
+#define SA_SAMPLE_RANDOM SA_RESTART
+#define SA_SHIRQ 0x04000000
+#define SA_RESTORER 0x04000000
+#endif
+
#ifdef X86
#define SA_PROBE SA_ONESHOT
#define SA_SAMPLE_RANDOM SA_RESTART
@@ -3203,6 +3277,9 @@ void program_usage(int);
#define SHORT_FORM (0)
void dump_program_context(void);
void dump_build_data(void);
+#ifdef ARM
+#define machdep_init(X) arm_init(X)
+#endif
#ifdef X86
#define machdep_init(X) x86_init(X)
#endif
@@ -3561,6 +3638,9 @@ void help_init(void);
void cmd_usage(char *, int);
void display_version(void);
void display_help_screen(char *);
+#ifdef ARM
+#define dump_machdep_table(X) arm_dump_machdep_table(X)
+#endif
#ifdef X86
#define dump_machdep_table(X) x86_dump_machdep_table(X)
#endif
@@ -3839,6 +3919,57 @@ void read_in_kernel_config(int);
void dev_init(void);
void dump_dev_table(void);

+#ifdef ARM
+void arm_init(int);
+void arm_dump_machdep_table(ulong);
+void arm_display_idt_table(void);
+int arm_is_vmalloc_addr(ulong);
+void arm_dump_backtrace_entry(struct bt_info *, int, ulong, ulong, ulong);
+#define display_idt_table()
+ error(FATAL, "-d option is not applicable to ARM architecture
")
+
+struct arm_pt_regs {
+ ulong uregs[18];
+};
+
+#define ARM_cpsr uregs[16]
+#define ARM_pc uregs[15]
+#define ARM_lr uregs[14]
+#define ARM_sp uregs[13]
+#define ARM_ip uregs[12]
+#define ARM_fp uregs[11]
+#define ARM_r10 uregs[10]
+#define ARM_r9 uregs[9]
+#define ARM_r8 uregs[8]
+#define ARM_r7 uregs[7]
+#define ARM_r6 uregs[6]
+#define ARM_r5 uregs[5]
+#define ARM_r4 uregs[4]
+#define ARM_r3 uregs[3]
+#define ARM_r2 uregs[2]
+#define ARM_r1 uregs[1]
+#define ARM_r0 uregs[0]
+#define ARM_ORIG_r0 uregs[17]
+
+#define KSYMS_START (0x1)
+
+struct machine_specific {
+ ulong phys_base;
+ ulong vmalloc_start_addr;
+ ulong modules_vaddr;
+ ulong modules_end;
+ ulong kernel_text_start;
+ ulong kernel_text_end;
+ ulong exception_text_start;
+ ulong exception_text_end;
+ ulong crash_task_pid;
+ struct arm_pt_regs *crash_task_regs;
+};
+
+int init_unwind_tables(void);
+void unwind_backtrace(struct bt_info *);
+#endif /* ARM */
+
/*
* alpha.c
*/
diff --git a/unwind_arm.c b/unwind_arm.c
new file mode 100644
index 0000000..f3497a8
--- /dev/null
+++ b/unwind_arm.c
@@ -0,0 +1,702 @@
+/*
+ * Stack unwinding support for ARM
+ *
+ * This code is derived from the kernel source:
+ * arch/arm/kernel/unwind.c
+ * Copyright (C) 2008 ARM Limited
+ *
+ * Created by: Mika Westerberg <ext-mika.1.westerberg@nokia.com>
+ * Copyright (C) 2010 Nokia Corporation
+ *
+ * For more information about ARM unwind tables see "Exception handling ABI for
+ * the ARM architecture" document at:
+ *
+ * http://infocenter.arm.com/help/topic/com.arm.doc.subset.swdev.abi/index.html
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ */
+
+#ifdef ARM
+
+#include "defs.h"
+
+/**
+ * struct unwind_idx - index table entry
+ * @addr: prel31 offset to the start of the function
+ * @insn: index table entry.
+ *
+ * @insn can be encoded as follows:
+ * 1. if bit31 is clear this points to the start of the EHT entry
+ * (prel31 offset)
+ * 2. if bit31 is set, this contains the EHT entry itself
+ * 3. if 0x1, cannot unwind.
+ *
+ * In case 1. @insn points to the EH table that comes directly after index
+ * table. This offset is relative to address of @insn which implies that we must
+ * allocate both index table and EH table in single chunk.
+ */
+struct unwind_idx {
+ ulong addr;
+ ulong insn;
+};
+
+/**
+ * struct unwind_table - per-module unwind table
+ * @idx: pointer to the star of the unwind table
+ * @start: pointer to the start of the index table
+ * @end: pointer to the last element +1 of the index table
+ * @begin_addr: start address which this table covers
+ * @end_addr: end address which this table covers
+ *
+ * Kernel stores per-module unwind tables in this format. There can be more than
+ * one table per module as we have different ELF sections in the module.
+ */
+struct unwind_table {
+ struct unwind_idx *idx;
+ struct unwind_idx *start;
+ struct unwind_idx *end;
+ ulong begin_addr;
+ ulong end_addr;
+};
+
+/*
+ * Unwind table pointers to master kernel table and for modules.
+ */
+static struct unwind_table *kernel_unwind_table;
+static struct unwind_table *module_unwind_tables;
+
+struct unwind_ctrl_block {
+ ulong vrs[16];
+ ulong *insn;
+ int entries;
+ int byte;
+};
+
+enum regs {
+ FP = 11,
+ SP = 13,
+ LR = 14,
+ PC = 15,
+};
+
+struct stackframe {
+ ulong fp;
+ ulong sp;
+ ulong lr;
+ ulong pc;
+};
+
+static int init_kernel_unwind_table(void);
+static void free_kernel_unwind_table(void);
+static int read_module_unwind_table(struct unwind_table *, ulong);
+static int init_module_unwind_tables(void);
+static ulong unwind_get_byte(struct unwind_ctrl_block *);
+static ulong get_value_from_stack(ulong *);
+static int unwind_exec_insn(struct unwind_ctrl_block *);
+static int is_core_kernel_text(ulong);
+static struct unwind_idx *search_index(ulong);
+static ulong *prel31_to_addr(ulong *);
+static int unwind_frame(struct stackframe *, ulong);
+
+/*
+ * Function reads in-memory kernel and module unwind tables and makes
+ * local copy of them for unwinding. If unwinding tables cannot be found, this
+ * function returns FALSE, otherwise TRUE.
+ */
+int
+init_unwind_tables(void)
+{
+ if (!symbol_exists("__start_unwind_idx") ||
+ !symbol_exists("__stop_unwind_idx") ||
+ !symbol_exists("__start_unwind_tab") ||
+ !symbol_exists("__stop_unwind_tab") ||
+ !symbol_exists("unwind_tables")) {
+ return FALSE;
+ }
+
+ if (!init_kernel_unwind_table()) {
+ error(WARNING,
+ "UNWIND: failed to initialize kernel unwind table
");
+ return FALSE;
+ }
+
+ /*
+ * Initialize symbols for per-module unwind tables. Actually there are
+ * several tables per module (one per code section).
+ */
+ STRUCT_SIZE_INIT(unwind_table, "unwind_table");
+ MEMBER_OFFSET_INIT(unwind_table_list, "unwind_table", "list");
+ MEMBER_OFFSET_INIT(unwind_table_start, "unwind_table", "start");
+ MEMBER_OFFSET_INIT(unwind_table_stop, "unwind_table", "stop");
+ MEMBER_OFFSET_INIT(unwind_table_begin_addr, "unwind_table",
+ "begin_addr");
+ MEMBER_OFFSET_INIT(unwind_table_end_addr, "unwind_table", "end_addr");
+
+ STRUCT_SIZE_INIT(unwind_idx, "unwind_idx");
+ MEMBER_OFFSET_INIT(unwind_idx_addr, "unwind_idx", "addr");
+ MEMBER_OFFSET_INIT(unwind_idx_insn, "unwind_idx", "insn");
+
+ if (!init_module_unwind_tables()) {
+ error(WARNING,
+ "UNWIND: failed to initialize module unwind tables
");
+ free_kernel_unwind_table();
+ return FALSE;
+ }
+
+ /*
+ * We abuse DWARF_UNWIND flag a little here as ARM unwinding tables are
+ * not in DWARF format but we can use the flags to indicate that we have
+ * unwind tables support ready.
+ */
+ kt->flags |= DWARF_UNWIND_CAPABLE;
+ kt->flags |= DWARF_UNWIND;
+
+ return TRUE;
+}
+
+/*
+ * Allocate and fill master kernel unwind table.
+ */
+static int
+init_kernel_unwind_table(void)
+{
+ ulong idx_start, idx_end, idx_size;
+ ulong tab_end, tab_size;
+
+ kernel_unwind_table = calloc(sizeof(*kernel_unwind_table), 1);
+ if (!kernel_unwind_table)
+ return FALSE;
+
+ idx_start = symbol_value("__start_unwind_idx");
+ idx_end = symbol_value("__stop_unwind_idx");
+ tab_end = symbol_value("__stop_unwind_tab");
+
+ /*
+ * Calculate sizes of the idx table and the EH table.
+ */
+ idx_size = idx_end - idx_start;
+ tab_size = tab_end - idx_start;
+
+ kernel_unwind_table->idx = calloc(tab_size, 1);
+ if (!kernel_unwind_table->idx)
+ goto fail;
+
+ /*
+ * Now read in both the index table and the EH table. We need to read in
+ * both because prel31 offsets in the index table are relative to the
+ * index address.
+ */
+ if (!readmem(idx_start, KVADDR, kernel_unwind_table->idx, tab_size,
+ "master kernel unwind table", RETURN_ON_ERROR))
+ goto fail;
+
+ kernel_unwind_table->start = kernel_unwind_table->idx;
+ kernel_unwind_table->end = (struct unwind_idx *)
+ ((char *)kernel_unwind_table->idx + idx_size);
+ kernel_unwind_table->begin_addr = kernel_unwind_table->start->addr;
+ kernel_unwind_table->end_addr = (kernel_unwind_table->end - 1)->addr;
+
+ if (CRASHDEBUG(1)) {
+ fprintf(fp, "UNWIND: master kernel table start
");
+ fprintf(fp, "UNWIND: size : %ld
", tab_size);
+ fprintf(fp, "UNWIND: start : %p
", kernel_unwind_table->start);
+ fprintf(fp, "UNWIND: end : %p
", kernel_unwind_table->end);
+ fprintf(fp, "UNWIND: begin_addr: 0x%lx
",
+ kernel_unwind_table->begin_addr);
+ fprintf(fp, "UNWIND: begin_addr: 0x%lx
",
+ kernel_unwind_table->end_addr);
+ fprintf(fp, "UNWIND: master kernel table end
");
+ }
+
+ return TRUE;
+
+fail:
+ free(kernel_unwind_table->idx);
+ free(kernel_unwind_table);
+ return FALSE;
+}
+
+static void
+free_kernel_unwind_table(void)
+{
+ free(kernel_unwind_table->idx);
+ free(kernel_unwind_table);
+}
+
+/*
+ * Read single module unwind table from addr.
+ */
+static int
+read_module_unwind_table(struct unwind_table *tbl, ulong addr)
+{
+ ulong idx_start, idx_stop, idx_size;
+ char *buf;
+
+ buf = GETBUF(SIZE(unwind_table));
+
+ /*
+ * First read in the unwind table for this module. It then contains
+ * pointers to the index table which we will read later.
+ */
+ if (!readmem(addr, KVADDR, buf, SIZE(unwind_table),
+ "module unwind table", RETURN_ON_ERROR)) {
+ error(WARNING, "UNWIND: cannot read unwind table
");
+ goto fail;
+ }
+
+#define TABLE_VALUE(b, offs) (*((ulong *)((b) + OFFSET(offs))))
+
+ idx_start = TABLE_VALUE(buf, unwind_table_start);
+ idx_stop = TABLE_VALUE(buf, unwind_table_stop);
+ idx_size = idx_stop - idx_start;
+
+ /*
+ * We know the size of the index table. Allocate memory for the table
+ * (including the EH table) and read the contents from the kernel
+ * memory.
+ */
+ tbl->idx = calloc(idx_size, 1);
+ if (!tbl->idx)
+ goto fail;
+
+ if (!readmem(idx_start, KVADDR, tbl->idx, idx_size,
+ "module unwind index table", RETURN_ON_ERROR)) {
+ free(tbl->idx);
+ goto fail;
+ }
+
+ tbl->start = &tbl->idx[0];
+ tbl->end = (struct unwind_idx *)((char *)tbl->start + idx_size);
+ tbl->begin_addr = TABLE_VALUE(buf, unwind_table_begin_addr);
+ tbl->end_addr = TABLE_VALUE(buf, unwind_table_end_addr);
+
+ if (CRASHDEBUG(1)) {
+ fprintf(fp, "UNWIND: module table start
");
+ fprintf(fp, "UNWIND: start : %p
", tbl->start);
+ fprintf(fp, "UNWIND: end : %p
", tbl->end);
+ fprintf(fp, "UNWIND: begin_addr: 0x%lx
", tbl->begin_addr);
+ fprintf(fp, "UNWIND: begin_addr: 0x%lx
", tbl->end_addr);
+ fprintf(fp, "UNWIND: module table end
");
+ }
+
+ FREEBUF(buf);
+ return TRUE;
+
+fail:
+ FREEBUF(buf);
+ free(tbl->idx);
+ return FALSE;
+}
+
+/*
+ * Allocate and fill per-module unwind tables.
+ */
+static int
+init_module_unwind_tables(void)
+{
+ ulong head = symbol_value("unwind_tables");
+ struct unwind_table *tbl;
+ struct list_data ld;
+ ulong *table_list;
+ int cnt, i, n;
+
+ BZERO(&ld, sizeof(ld));
+ ld.start = head;
+ ld.member_offset = OFFSET(unwind_table_list);
+
+ if (CRASHDEBUG(1))
+ ld.flags |= VERBOSE;
+
+ /*
+ * Iterate through unwind table list and store start address of each
+ * table in table_list.
+ */
+ hq_open();
+ cnt = do_list(&ld);
+ table_list = (ulong *)GETBUF(cnt * sizeof(ulong));
+ cnt = retrieve_list(table_list, cnt);
+ hq_close();
+
+ module_unwind_tables = calloc(sizeof(struct unwind_table), cnt);
+ if (!module_unwind_tables) {
+ error(WARNING,
+ "UNWIND: failed to allocate memory for (%d tables)
",
+ cnt);
+ FREEBUF(table_list);
+ return FALSE;
+ }
+
+ /* we skip the first address as it is just head pointer */
+ for (i = 1, n = 0; i < cnt; i++, n++) {
+ tbl = &module_unwind_tables[n];
+ if (!read_module_unwind_table(tbl, table_list[i]))
+ goto fail;
+ }
+
+ /* just in case, zero the last entry (again) */
+ BZERO(&module_unwind_tables[n], sizeof(module_unwind_tables[n]));
+
+ FREEBUF(table_list);
+ return TRUE;
+
+fail:
+ FREEBUF(table_list);
+
+ while (--n >= 0) {
+ tbl = &module_unwind_tables[n];
+ free(tbl->idx);
+ }
+
+ free(module_unwind_tables);
+ return FALSE;
+}
+
+/*
+ * Return next insn byte from ctl or 0 in case of failure. As a side-effect,
+ * changes ctrl according the next byte.
+ */
+static ulong
+unwind_get_byte(struct unwind_ctrl_block *ctrl)
+{
+ ulong ret;
+
+ if (ctrl->entries <= 0) {
+ error(WARNING, "UNWIND: corrupt unwind entry
");
+ return 0;
+ }
+
+ ret = (*ctrl->insn >> (ctrl->byte * 8)) & 0xff;
+
+ if (!ctrl->byte) {
+ ctrl->insn++;
+ ctrl->entries--;
+ ctrl->byte = 3;
+ } else {
+ ctrl->byte--;
+ }
+
+ return ret;
+}
+
+/*
+ * Gets one value from stack pointed by vsp.
+ */
+static ulong
+get_value_from_stack(ulong *vsp)
+{
+ ulong val;
+
+ /*
+ * We just read the value from kernel memory instead of peeking it from
+ * the bt->stack.
+ */
+ if (!readmem((ulong)vsp, KVADDR, &val, sizeof(val),
+ "unwind stack value", RETURN_ON_ERROR)) {
+ error(FATAL, "unwind: failed to read value from stack
");
+ }
+
+ return val;
+}
+
+/*
+ * Execute the next unwind instruction.
+ */
+static int
+unwind_exec_insn(struct unwind_ctrl_block *ctrl)
+{
+ ulong insn = unwind_get_byte(ctrl);
+
+ if ((insn & 0xc0) == 0) {
+ /*
+ * 00xx xxxx: vsp = vsp + (xx xxx << 2) + 4
+ *
+ * Note that it seems that there is a typo in the spec and this
+ * is corrected in kernel.
+ */
+ ctrl->vrs[SP] += ((insn & 0x3f) << 2) + 4;
+ } else if ((insn & 0xc0) == 0x40) {
+ /* 00xx xxxx: vsp = vsp + (xx xxx << 2) + 4 */
+ ctrl->vrs[SP] -= ((insn & 0x3f) << 2) + 4;
+ } else if ((insn & 0xf0) == 0x80) {
+ /*
+ * Pop up to 12 integer registers under masks
+ * {r15-r12}, {r11-r4}.
+ */
+ ulong mask;
+ ulong *vsp = (ulong *)ctrl->vrs[SP];
+ int load_sp, reg = 4;
+
+ insn = (insn << 8) | unwind_get_byte(ctrl);
+ mask = insn & 0x0fff;
+ if (mask == 0) {
+ error(WARNING, "UNWIND: refuse to unwind
");
+ return FALSE;
+ }
+
+ /* pop {r4-r15} according to mask */
+ load_sp = mask & (1 << (13 - 4));
+ while (mask) {
+ if (mask & 1)
+ ctrl->vrs[reg] = get_value_from_stack(vsp++);
+ mask >>= 1;
+ reg++;
+ }
+ if (!load_sp)
+ ctrl->vrs[SP] = (ulong)vsp;
+ } else if ((insn & 0xf0) == 0x90 &&
+ (insn & 0x0d) != 0x0d) {
+ /* 1001 nnnn: set vsp = r[nnnn] */
+ ctrl->vrs[SP] = ctrl->vrs[insn & 0x0f];
+ } else if ((insn & 0xf0) == 0xa0) {
+ /*
+ * 1010 0nnn: pop r4-r[4+nnn]
+ * 1010 1nnn: pop r4-r[4+nnn], r14
+ */
+ ulong *vsp = (ulong *)ctrl->vrs[SP];
+ int reg;
+
+ for (reg = 4; reg <= 4 + (insn & 7); reg++)
+ ctrl->vrs[reg] = get_value_from_stack(vsp++);
+
+ if (insn & 0x80)
+ ctrl->vrs[14] = get_value_from_stack(vsp++);
+
+ ctrl->vrs[SP] = (ulong)vsp;
+ } else if (insn == 0xb0) {
+ /* 1011 0000: finish */
+ if (ctrl->vrs[PC] == 0)
+ ctrl->vrs[PC] = ctrl->vrs[LR];
+ /* no further processing */
+ ctrl->entries = 0;
+ } else if (insn == 0xb1) {
+ /* 1011 0001 xxxx yyyy: spare */
+ ulong mask = unwind_get_byte(ctrl);
+ ulong *vsp = (ulong *)ctrl->vrs[SP];
+ int reg = 0;
+
+ if (mask == 0 || mask & 0xf0) {
+ error(WARNING, "UNWIND: spare error
");
+ return FALSE;
+ }
+
+ /* pop r0-r3 according to mask */
+ while (mask) {
+ if (mask & 1)
+ ctrl->vrs[reg] = get_value_from_stack(vsp++);
+ mask >>= 1;
+ reg++;
+ }
+ ctrl->vrs[SP] = (ulong)vsp;
+ } else if (insn == 0xb2) {
+ /* 1011 0010 uleb128: vsp = vsp + 0x204 (uleb128 << 2) */
+ ulong uleb128 = unwind_get_byte(ctrl);
+
+ ctrl->vrs[SP] += 0x204 + (uleb128 << 2);
+ } else {
+ error(WARNING, "UNWIND: unhandled instruction: %02lx
", insn);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static int
+is_core_kernel_text(ulong pc)
+{
+ ulong text_start = machdep->machspec->kernel_text_start;
+ ulong text_end = machdep->machspec->kernel_text_end;
+
+ if (text_start && text_end)
+ return (pc >= text_start && pc <= text_end);
+
+ return FALSE;
+}
+
+static struct unwind_idx *
+search_index(ulong ip)
+{
+ struct unwind_idx *start = NULL;
+ struct unwind_idx *end = NULL;
+
+ /*
+ * First check if this address is in the master kernel unwind table or
+ * some of the module unwind tables.
+ */
+ if (is_core_kernel_text(ip)) {
+ start = kernel_unwind_table->start;
+ end = kernel_unwind_table->end;
+ } else {
+ struct unwind_table *tbl;
+
+ for (tbl = &module_unwind_tables[0]; tbl->idx; tbl++) {
+ if (ip >= tbl->begin_addr && ip < tbl->end_addr) {
+ start = tbl->start;
+ end = tbl->end;
+ break;
+ }
+ }
+ }
+
+ if (start && end) {
+ /*
+ * Do a binary search for the addresses in the index table.
+ * Addresses are guaranteed to be sorted in ascending order.
+ */
+ while (start < end - 1) {
+ struct unwind_idx *mid = start + ((end - start + 1) >> 1);
+
+ if (ip < mid->addr)
+ end = mid;
+ else
+ start = mid;
+ }
+
+ return start;
+ }
+
+ return NULL;
+}
+
+/*
+ * Convert a prel31 symbol to an absolute address.
+ */
+static ulong *
+prel31_to_addr(ulong *ptr)
+{
+ /* sign extend to 32 bits */
+ long offset = (((long)*ptr) << 1) >> 1;
+ return (ulong *)((ulong)ptr + offset);
+}
+
+static int
+unwind_frame(struct stackframe *frame, ulong stacktop)
+{
+ struct unwind_ctrl_block ctrl;
+ struct unwind_idx *idx;
+ ulong low, high;
+
+ low = frame->sp;
+ high = stacktop;
+
+ idx = search_index(frame->pc);
+ if (!idx) {
+ error(WARNING, "UNWIND: cannot find index for %lx
",
+ frame->pc);
+ return FALSE;
+ }
+
+ ctrl.vrs[FP] = frame->fp;
+ ctrl.vrs[SP] = frame->sp;
+ ctrl.vrs[LR] = frame->lr;
+ ctrl.vrs[PC] = 0;
+
+ if (CRASHDEBUG(5)) {
+ fprintf(fp, "UNWIND: >frame: FP=%lx
", ctrl.vrs[FP]);
+ fprintf(fp, "UNWIND: >frame: SP=%lx
", ctrl.vrs[SP]);
+ fprintf(fp, "UNWIND: >frame: LR=%lx
", ctrl.vrs[LR]);
+ fprintf(fp, "UNWIND: >frame: PC=%lx
", ctrl.vrs[PC]);
+ }
+
+ if (idx->insn == 1) {
+ /* can't unwind */
+ return FALSE;
+ } else if ((idx->insn & 0x80000000) == 0) {
+ /* insn contains offset to eht entry */
+ ctrl.insn = prel31_to_addr(&idx->insn);
+ } else if ((idx->insn & 0xff000000) == 0x80000000) {
+ /* eht entry is in insn itself */
+ ctrl.insn = &idx->insn;
+ } else {
+ error(WARNING, "UNWIND: unsupported instruction %lx
",
+ idx->insn);
+ return FALSE;
+ }
+
+ /* check the personality routine */
+ if ((*ctrl.insn & 0xff000000) == 0x80000000) {
+ ctrl.byte = 2;
+ ctrl.entries = 1;
+ } else if ((*ctrl.insn & 0xff000000) == 0x81000000) {
+ ctrl.byte = 1;
+ ctrl.entries = 1 + ((*ctrl.insn & 0x00ff0000) >> 16);
+ } else {
+ error(WARNING, "UNWIND: unsupported personality routine
");
+ return FALSE;
+ }
+
+ /* now, execute the instructions */
+ while (ctrl.entries > 0) {
+ if (!unwind_exec_insn(&ctrl)) {
+ error(WARNING, "UNWIND: failed to exec instruction
");
+ return FALSE;
+ }
+
+ if (ctrl.vrs[SP] < low || ctrl.vrs[SP] >= high)
+ return FALSE;
+ }
+
+ if (ctrl.vrs[PC] == 0)
+ ctrl.vrs[PC] = ctrl.vrs[LR];
+
+ if (frame->pc == ctrl.vrs[PC])
+ return FALSE;
+
+ frame->fp = ctrl.vrs[FP];
+ frame->sp = ctrl.vrs[SP];
+ frame->lr = ctrl.vrs[LR];
+ frame->pc = ctrl.vrs[PC];
+
+ if (CRASHDEBUG(5)) {
+ fprintf(fp, "UNWIND: <frame: FP=%lx
", ctrl.vrs[FP]);
+ fprintf(fp, "UNWIND: <frame: SP=%lx
", ctrl.vrs[SP]);
+ fprintf(fp, "UNWIND: <frame: LR=%lx
", ctrl.vrs[LR]);
+ fprintf(fp, "UNWIND: <frame: PC=%lx
", ctrl.vrs[PC]);
+ }
+
+ return TRUE;
+}
+
+void
+unwind_backtrace(struct bt_info *bt)
+{
+ struct stackframe frame;
+ int n = 0;
+
+ BZERO(&frame, sizeof(frame));
+ frame.fp = bt->frameptr;
+ frame.sp = bt->stkptr;
+ frame.pc = bt->instptr;
+
+ /*
+ * In case bt->machdep contains pointer to a full register set, we take
+ * LR from there.
+ */
+ if (bt->machdep) {
+ const struct arm_pt_regs *regs = bt->machdep;
+
+ frame.lr = regs->ARM_lr;
+ }
+
+ while (1) {
+ ulong where = frame.pc;
+
+ if (!IS_KVADDR(where))
+ break;
+
+ if (!unwind_frame(&frame, bt->stacktop))
+ break;
+
+ /* call back to ARM code to actually print this frame */
+ arm_dump_backtrace_entry(bt, n++, where, frame.pc,
+ frame.sp - 4);
+ }
+}
+#endif /* ARM */
--
1.5.6.5

--
Crash-utility mailing list
Crash-utility@redhat.com
https://www.redhat.com/mailman/listinfo/crash-utility
 
Old 08-26-2010, 12:02 PM
Mika Westerberg
 
Default crash: add ARM crashdump support

This patch adds minimal support for reading ARM crashdumps. Currently supported
dumpfiles are /dev/mem, diskdump and vmcore. Stack unwinding can be done using
framepointers or unwinding tables depending on kernel configuration.

Signed-off-by: Jan Karlsson <jan.karlsson@sonyericsson.com>
Signed-off-by: Thomas Fänge <thomas.fange@sonyericsson.com>
Signed-off-by: Mika Westerberg <ext-mika.1.westerberg@nokia.com>
---
Makefile | 12 +-
arm.c | 1741 ++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++
defs.h | 137 +++++
diskdump.c | 8 +
unwind_arm.c | 697 +++++++++++++++++++++++
5 files changed, 2593 insertions(+), 2 deletions(-)
create mode 100644 arm.c
create mode 100644 unwind_arm.c

diff --git a/Makefile b/Makefile
index 038c887..f79f6a9 100644
--- a/Makefile
+++ b/Makefile
@@ -80,11 +80,12 @@ UNWIND_HFILES=unwind.h unwind_i.h rse.h unwind_x86.h unwind_x86_64.h
CFILES=main.c tools.c global_data.c memory.c filesys.c help.c task.c
kernel.c test.c gdb_interface.c configure.c net.c dev.c
alpha.c x86.c ppc.c ia64.c s390.c s390x.c s390dbf.c ppc64.c x86_64.c
+ arm.c
extensions.c remote.c va_server.c va_server_v1.c symbols.c cmdline.c
lkcd_common.c lkcd_v1.c lkcd_v2_v3.c lkcd_v5.c lkcd_v7.c lkcd_v8.c
lkcd_fix_mem.c s390_dump.c lkcd_x86_trace.c
netdump.c diskdump.c xendump.c unwind.c unwind_decoder.c
- unwind_x86_32_64.c
+ unwind_x86_32_64.c unwind_arm.c
xen_hyper.c xen_hyper_command.c xen_hyper_global_data.c
xen_hyper_dump_tables.c kvmdump.c qemu.c qemu-load.c

@@ -96,11 +97,12 @@ SOURCE_FILES=${CFILES} ${GENERIC_HFILES} ${MCORE_HFILES}
OBJECT_FILES=main.o tools.o global_data.o memory.o filesys.o help.o task.o
build_data.o kernel.o test.o gdb_interface.o net.o dev.o
alpha.o x86.o ppc.o ia64.o s390.o s390x.o s390dbf.o ppc64.o x86_64.o
+ arm.o
extensions.o remote.o va_server.o va_server_v1.o symbols.o cmdline.o
lkcd_common.o lkcd_v1.o lkcd_v2_v3.o lkcd_v5.o lkcd_v7.o lkcd_v8.o
lkcd_fix_mem.o s390_dump.o netdump.o diskdump.o xendump.o
lkcd_x86_trace.o unwind_v1.o unwind_v2.o unwind_v3.o
- unwind_x86_32_64.o
+ unwind_x86_32_64.o unwind_arm.o
xen_hyper.o xen_hyper_command.o xen_hyper_global_data.o
xen_hyper_dump_tables.o kvmdump.o qemu.o qemu-load.o

@@ -419,6 +421,9 @@ ppc64.o: ${GENERIC_HFILES} ppc64.c
x86_64.o: ${GENERIC_HFILES} ${REDHAT_HFILES} x86_64.c
cc -c ${CRASH_CFLAGS} x86_64.c ${WARNING_OPTIONS} ${WARNING_ERROR}

+arm.o: ${GENERIC_HFILES} ${REDHAT_HFILES} arm.c
+ cc -c ${CRASH_CFLAGS} arm.c ${WARNING_OPTIONS} ${WARNING_ERROR}
+
s390.o: ${GENERIC_HFILES} ${IBM_HFILES} s390.c
cc -c ${CRASH_CFLAGS} s390.c ${WARNING_OPTIONS} ${WARNING_ERROR}

@@ -460,6 +465,9 @@ lkcd_x86_trace.o: ${GENERIC_HFILES} ${LKCD_TRACE_HFILES} lkcd_x86_trace.c
unwind_x86_32_64.o: ${GENERIC_HFILES} ${UNWIND_HFILES} unwind_x86_32_64.c
cc -c ${CRASH_CFLAGS} unwind_x86_32_64.c -o unwind_x86_32_64.o ${WARNING_OPTIONS} ${WARNING_ERROR}

+unwind_arm.o: ${GENERIC_HFILES} ${UNWIND_HFILES} unwind_arm.c
+ cc -c ${CRASH_CFLAGS} unwind_arm.c -o unwind_arm.o ${WARNING_OPTIONS} ${WARNING_ERROR}
+
unwind_v1.o: ${GENERIC_HFILES} ${UNWIND_HFILES} unwind.c unwind_decoder.c
cc -c ${CRASH_CFLAGS} unwind.c -DREDHAT -DUNWIND_V1 -o unwind_v1.o ${WARNING_OPTIONS} ${WARNING_ERROR}

diff --git a/arm.c b/arm.c
new file mode 100644
index 0000000..f6d1b27
--- /dev/null
+++ b/arm.c
@@ -0,0 +1,1741 @@
+/*
+ * arm.c - core analysis suite
+ *
+ * Authors:
+ * Thomas Fänge <thomas.fange@sonyericsson.com>
+ * Jan Karlsson <jan.karlsson@sonyericsson.com>
+ * Mika Westerberg <ext-mika.1.westerberg@nokia.com>
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2010 Sony Ericsson. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ */
+
+#ifdef ARM
+#include <elf.h>
+
+#include "defs.h"
+
+static void arm_parse_cmdline_args(void);
+static int arm_get_crash_notes(void);
+static int arm_verify_symbol(const char *, ulong, char);
+static int arm_is_module_addr(ulong);
+static int arm_is_kvaddr(ulong);
+static int arm_in_exception_text(ulong);
+static void arm_back_trace(struct bt_info *);
+static void arm_back_trace_cmd(struct bt_info *);
+static ulong arm_processor_speed(void);
+static int arm_translate_pte(ulong, void *, ulonglong);
+static int arm_vtop(ulong, ulong *, physaddr_t *, int);
+static int arm_kvtop(struct task_context *, ulong, physaddr_t *, int);
+static int arm_uvtop(struct task_context *, ulong, physaddr_t *, int);
+static int arm_get_frame(struct bt_info *, ulong *, ulong *);
+static int arm_get_dumpfile_stack_frame(struct bt_info *, ulong *, ulong *);
+static void arm_get_stack_frame(struct bt_info *, ulong *, ulong *);
+static void arm_dump_exception_stack(ulong, ulong);
+static void arm_display_full_frame(struct bt_info *, ulong);
+static ulong arm_vmalloc_start(void);
+static int arm_is_task_addr(ulong);
+static int arm_dis_filter(ulong, char *);
+static int arm_eframe_search(struct bt_info *);
+static ulong arm_get_task_pgd(ulong);
+static void arm_cmd_mach(void);
+static void arm_display_machine_stats(void);
+static int arm_get_smp_cpus(void);
+static void print_irq_member(char *, ulong, char *);
+static void arm_dump_irq(int);
+static void arm_init_machspec(void);
+
+static struct line_number_hook arm_line_number_hooks[];
+static struct machine_specific arm_machine_specific;
+
+/**
+ * struct arm_cpu_context_save - idle task registers
+ *
+ * This structure holds idle task registers. Only FP, SP, and PC are needed for
+ * unwinding the stack.
+ */
+struct arm_cpu_context_save {
+ ulong fp;
+ ulong sp;
+ ulong pc;
+};
+
+/*
+ * Holds registers during the crash.
+ */
+static struct arm_pt_regs panic_task_regs;
+
+#define PGDIR_SIZE() (4 * PAGESIZE())
+#define PGDIR_OFFSET(X) (((ulong)(X)) & (PGDIR_SIZE() - 1))
+
+#define _SECTION_PAGE_MASK (~((MEGABYTES(1))-1))
+
+#define PMD_TYPE_MASK 3
+#define PMD_TYPE_SECT 2
+#define PMD_TYPE_TABLE 1
+
+static inline ulong *
+pmd_page_addr(ulong pmd)
+{
+ ulong ptr;
+
+ ptr = pmd & ~(PTRS_PER_PTE * sizeof(void *) - 1);
+ ptr += PTRS_PER_PTE * sizeof(void *);
+
+ return (ulong *)ptr;
+}
+
+/*
+ * "Linux" PTE definitions.
+ */
+#define L_PTE_PRESENT (1 << 0)
+#define L_PTE_YOUNG (1 << 1)
+#define L_PTE_FILE (1 << 2)
+#define L_PTE_DIRTY (1 << 6)
+#define L_PTE_WRITE (1 << 7)
+#define L_PTE_USER (1 << 8)
+#define L_PTE_EXEC (1 << 9)
+#define L_PTE_SHARED (1 << 10)
+
+#define pte_val(pte) (pte)
+
+#define pte_present(pte) (pte_val(pte) & L_PTE_PRESENT)
+#define pte_write(pte) (pte_val(pte) & L_PTE_WRITE)
+#define pte_dirty(pte) (pte_val(pte) & L_PTE_DIRTY)
+#define pte_young(pte) (pte_val(pte) & L_PTE_YOUNG)
+
+/*
+ * Following stuff is taken directly from the kernel sources. These are used in
+ * dump_exception_stack() to format an exception stack entry.
+ */
+#define USR26_MODE 0x00000000
+#define FIQ26_MODE 0x00000001
+#define IRQ26_MODE 0x00000002
+#define SVC26_MODE 0x00000003
+#define USR_MODE 0x00000010
+#define FIQ_MODE 0x00000011
+#define IRQ_MODE 0x00000012
+#define SVC_MODE 0x00000013
+#define ABT_MODE 0x00000017
+#define UND_MODE 0x0000001b
+#define SYSTEM_MODE 0x0000001f
+#define MODE32_BIT 0x00000010
+#define MODE_MASK 0x0000001f
+#define PSR_T_BIT 0x00000020
+#define PSR_F_BIT 0x00000040
+#define PSR_I_BIT 0x00000080
+#define PSR_A_BIT 0x00000100
+#define PSR_E_BIT 0x00000200
+#define PSR_J_BIT 0x01000000
+#define PSR_Q_BIT 0x08000000
+#define PSR_V_BIT 0x10000000
+#define PSR_C_BIT 0x20000000
+#define PSR_Z_BIT 0x40000000
+#define PSR_N_BIT 0x80000000
+
+#define isa_mode(regs)
+ ((((regs)->ARM_cpsr & PSR_J_BIT) >> 23) |
+ (((regs)->ARM_cpsr & PSR_T_BIT) >> 5))
+
+#define processor_mode(regs)
+ ((regs)->ARM_cpsr & MODE_MASK)
+
+#define interrupts_enabled(regs)
+ (!((regs)->ARM_cpsr & PSR_I_BIT))
+
+#define fast_interrupts_enabled(regs)
+ (!((regs)->ARM_cpsr & PSR_F_BIT))
+
+static const char *processor_modes[] = {
+ "USER_26", "FIQ_26", "IRQ_26", "SVC_26", "UK4_26", "UK5_26",
+ "UK6_26", "UK7_26" , "UK8_26", "UK9_26", "UK10_26", "UK11_26",
+ "UK12_26", "UK13_26", "UK14_26", "UK15_26", "USER_32", "FIQ_32",
+ "IRQ_32", "SVC_32", "UK4_32", "UK5_32", "UK6_32", "ABT_32",
+ "UK8_32", "UK9_32", "UK10_32", "UND_32", "UK12_32", "UK13_32",
+ "UK14_32", "SYS_32",
+};
+
+static const char *isa_modes[] = {
+ "ARM" , "Thumb" , "Jazelle", "ThumbEE",
+};
+
+#define NOT_IMPLEMENTED()
+ error(FATAL, "%s: N/A
", __func__)
+
+/*
+ * Do all necessary machine-specific setup here. This is called several times
+ * during initialization.
+ */
+void
+arm_init(int when)
+{
+ switch (when) {
+ case PRE_SYMTAB:
+ machdep->verify_symbol = arm_verify_symbol;
+ machdep->machspec = &arm_machine_specific;
+ if (pc->flags & KERNEL_DEBUG_QUERY)
+ return;
+ machdep->pagesize = memory_page_size();
+ machdep->pageshift = ffs(machdep->pagesize) - 1;
+ machdep->pageoffset = machdep->pagesize - 1;
+ machdep->pagemask = ~((ulonglong)machdep->pageoffset);
+ machdep->stacksize = machdep->pagesize * 2;
+ machdep->last_pgd_read = 0;
+ machdep->last_pmd_read = 0;
+ machdep->last_ptbl_read = 0;
+ machdep->verify_paddr = generic_verify_paddr;
+ machdep->ptrs_per_pgd = PTRS_PER_PGD;
+
+ if (machdep->cmdline_args[0])
+ arm_parse_cmdline_args();
+ break;
+
+ case PRE_GDB:
+ if ((machdep->pgd = (char *)malloc(PGDIR_SIZE())) == NULL)
+ error(FATAL, "cannot malloc pgd space.");
+ if ((machdep->ptbl = (char *)malloc(PAGESIZE())) == NULL)
+ error(FATAL, "cannot malloc ptbl space.");
+
+ /*
+ * Kernel text starts 16k after PAGE_OFFSET.
+ */
+ machdep->kvbase = symbol_value("_stext") & 0xffff0000UL;
+ machdep->identity_map_base = machdep->kvbase;
+ machdep->is_kvaddr = arm_is_kvaddr;
+ machdep->is_uvaddr = generic_is_uvaddr;
+ machdep->eframe_search = arm_eframe_search;
+ machdep->back_trace = arm_back_trace_cmd;
+ machdep->processor_speed = arm_processor_speed;
+ machdep->uvtop = arm_uvtop;
+ machdep->kvtop = arm_kvtop;
+ machdep->get_task_pgd = arm_get_task_pgd;
+ machdep->get_stack_frame = arm_get_stack_frame;
+ machdep->get_stackbase = generic_get_stackbase;
+ machdep->get_stacktop = generic_get_stacktop;
+ machdep->translate_pte = arm_translate_pte;
+ machdep->memory_size = generic_memory_size;
+ machdep->vmalloc_start = arm_vmalloc_start;
+ machdep->is_task_addr = arm_is_task_addr;
+ machdep->dis_filter = arm_dis_filter;
+ machdep->cmd_mach = arm_cmd_mach;
+ machdep->get_smp_cpus = arm_get_smp_cpus;
+ machdep->line_number_hooks = arm_line_number_hooks;
+ machdep->value_to_symbol = generic_machdep_value_to_symbol;
+ machdep->init_kernel_pgd = NULL;
+
+ if (symbol_exists("irq_desc"))
+ machdep->dump_irq = arm_dump_irq;
+
+ arm_init_machspec();
+ break;
+
+ case POST_GDB:
+ if (symbol_exists("irq_desc")) {
+ ARRAY_LENGTH_INIT(machdep->nr_irqs, irq_desc,
+ "irq_desc", NULL, 0);
+ if (MEMBER_EXISTS("irq_desc", "name"))
+ MEMBER_OFFSET_INIT(irq_desc_t_name, "irq_desc",
+ "name");
+ }
+
+ /*
+ * Registers for idle threads are saved in
+ * thread_info.cpu_context.
+ */
+ STRUCT_SIZE_INIT(cpu_context_save, "cpu_context_save");
+ MEMBER_OFFSET_INIT(cpu_context_save_fp,
+ "cpu_context_save", "fp");
+ MEMBER_OFFSET_INIT(cpu_context_save_sp,
+ "cpu_context_save", "sp");
+ MEMBER_OFFSET_INIT(cpu_context_save_pc,
+ "cpu_context_save", "pc");
+ MEMBER_OFFSET_INIT(thread_info_cpu_context,
+ "thread_info", "cpu_context");
+
+ /*
+ * We need to have information about note_buf_t which is used to
+ * hold ELF note containing registers and status of the thread
+ * that panic'd.
+ */
+ STRUCT_SIZE_INIT(note_buf, "note_buf_t");
+
+ STRUCT_SIZE_INIT(elf_prstatus, "elf_prstatus");
+ MEMBER_OFFSET_INIT(elf_prstatus_pr_pid, "elf_prstatus",
+ "pr_pid");
+ MEMBER_OFFSET_INIT(elf_prstatus_pr_reg, "elf_prstatus",
+ "pr_reg");
+
+ /*
+ * crash_notes contains machine specific information about the
+ * crash. In particular, it contains CPU registers at the time
+ * of the crash. We need this information to extract correct
+ * backtraces from the panic task.
+ */
+ if (!ACTIVE() && !arm_get_crash_notes())
+ error(WARNING, "Couldn't retrieve crash_notes
");
+ break;
+
+ case POST_VM:
+ machdep->machspec->vmalloc_start_addr = vt->high_memory;
+ /*
+ * Modules are placed in first vmalloc'd area. This is 16MB
+ * below PAGE_OFFSET.
+ */
+ machdep->machspec->modules_vaddr = first_vmalloc_address();
+ machdep->machspec->modules_end = machdep->kvbase - 1;
+
+ if (init_unwind_tables()) {
+ if (CRASHDEBUG(1))
+ fprintf(fp, "using unwind tables
");
+ } else {
+ if (CRASHDEBUG(1))
+ fprintf(fp, "using framepointers
");
+ }
+ break;
+ }
+}
+
+void
+arm_dump_machdep_table(ulong arg)
+{
+ const struct machine_specific *ms;
+ int others, i;
+
+ others = 0;
+ fprintf(fp, " flags: %lx (", machdep->flags);
+ if (machdep->flags & KSYMS_START)
+ fprintf(fp, "%sKSYMS_START", others++ ? "|" : "");
+ if (machdep->flags & PHYS_BASE)
+ fprintf(fp, "%sPHYS_BASE", others++ ? "|" : "");
+ fprintf(fp, ")
");
+
+ fprintf(fp, " kvbase: %lx
", machdep->kvbase);
+ fprintf(fp, " identity_map_base: %lx
", machdep->kvbase);
+ fprintf(fp, " pagesize: %d
", machdep->pagesize);
+ fprintf(fp, " pageshift: %d
", machdep->pageshift);
+ fprintf(fp, " pagemask: %lx
", (ulong)machdep->pagemask);
+ fprintf(fp, " pageoffset: %lx
", machdep->pageoffset);
+ fprintf(fp, " stacksize: %ld
", machdep->stacksize);
+ fprintf(fp, " hz: %d
", machdep->hz);
+ fprintf(fp, " mhz: %ld
", machdep->mhz);
+ fprintf(fp, " memsize: %lld (0x%llx)
",
+ machdep->memsize, machdep->memsize);
+ fprintf(fp, " bits: %d
", machdep->bits);
+ fprintf(fp, " nr_irqs: %d
", machdep->nr_irqs);
+ fprintf(fp, " eframe_search: arm_eframe_search()
");
+ fprintf(fp, " back_trace: arm_back_trace_cmd()
");
+ fprintf(fp, " processor_speed: arm_processor_speed()
");
+ fprintf(fp, " uvtop: arm_uvtop()
");
+ fprintf(fp, " kvtop: arm_kvtop()
");
+ fprintf(fp, " get_task_pgd: arm_get_task_pgd()
");
+ if (symbol_exists("irq_desc"))
+ fprintf(fp, " dump_irq: arm_dump_irq()
");
+ else
+ fprintf(fp, " dump_irq: NULL
");
+ fprintf(fp, " get_stack_frame: arm_get_stack_frame()
");
+ fprintf(fp, " get_stackbase: generic_get_stackbase()
");
+ fprintf(fp, " get_stacktop: generic_get_stacktop()
");
+ fprintf(fp, " translate_pte: arm_translate_pte()
");
+ fprintf(fp, " memory_size: generic_memory_size()
");
+ fprintf(fp, " vmalloc_start: arm_vmalloc_start()
");
+ fprintf(fp, " is_task_addr: arm_is_task_addr()
");
+ fprintf(fp, " verify_symbol: arm_verify_symbol()
");
+ fprintf(fp, " dis_filter: arm_dis_filter()
");
+ fprintf(fp, " cmd_mach: arm_cmd_mach()
");
+ fprintf(fp, " get_smp_cpus: arm_get_smp_cpus()
");
+ fprintf(fp, " is_kvaddr: generic_is_kvaddr()
");
+ fprintf(fp, " is_uvaddr: generic_is_uvaddr()
");
+ fprintf(fp, " verify_paddr: generic_verify_paddr()
");
+ fprintf(fp, " xendump_p2m_create: NULL
");
+ fprintf(fp, "xen_kdump_p2m_create: NULL
");
+ fprintf(fp, " line_number_hooks: arm_line_number_hooks
");
+ fprintf(fp, " last_pgd_read: %lx
", machdep->last_pgd_read);
+ fprintf(fp, " last_pmd_read: %lx
", machdep->last_pmd_read);
+ fprintf(fp, " last_ptbl_read: %lx
", machdep->last_ptbl_read);
+ fprintf(fp, "clear_machdep_cache: NULL
");
+ fprintf(fp, " pgd: %lx
", (ulong)machdep->pgd);
+ fprintf(fp, " pmd: %lx
", (ulong)machdep->pmd);
+ fprintf(fp, " ptbl: %lx
", (ulong)machdep->ptbl);
+ fprintf(fp, " ptrs_per_pgd: %d
", machdep->ptrs_per_pgd);
+ fprintf(fp, " section_size_bits: %ld
", machdep->section_size_bits);
+ fprintf(fp, " max_physmem_bits: %ld
", machdep->max_physmem_bits);
+ fprintf(fp, " sections_per_root: %ld
", machdep->sections_per_root);
+
+ for (i = 0; i < MAX_MACHDEP_ARGS; i++) {
+ fprintf(fp, " cmdline_args[%d]: %s
",
+ i, machdep->cmdline_args[i] ?
+ machdep->cmdline_args[i] : "(unused)");
+ }
+
+ ms = machdep->machspec;
+
+ fprintf(fp, " machspec: %lx
", (ulong)ms);
+ fprintf(fp, " phys_base: %lx
", ms->phys_base);
+ fprintf(fp, " vmalloc_start_addr: %lx
", ms->vmalloc_start_addr);
+ fprintf(fp, " modules_vaddr: %lx
", ms->modules_vaddr);
+ fprintf(fp, " modules_end: %lx
", ms->modules_end);
+ fprintf(fp, " kernel_text_start: %lx
", ms->kernel_text_start);
+ fprintf(fp, " kernel_text_end: %lx
", ms->kernel_text_end);
+ fprintf(fp, "exception_text_start: %lx
", ms->exception_text_start);
+ fprintf(fp, " exception_text_end: %lx
", ms->exception_text_end);
+ fprintf(fp, " crash_task_pid: %ld
", ms->crash_task_pid);
+ fprintf(fp, " crash_task_regs: %lx
", (ulong)ms->crash_task_regs);
+}
+
+/*
+ * Parse machine dependent command line arguments.
+ *
+ * Force the phys_base address via:
+ *
+ * --machdep phys_base=<address>
+ */
+static void
+arm_parse_cmdline_args(void)
+{
+ int index, i, c, err;
+ char *arglist[MAXARGS];
+ char buf[BUFSIZE];
+ char *p;
+ ulong value;
+
+ for (index = 0; index < MAX_MACHDEP_ARGS; index++) {
+ if (!machdep->cmdline_args[index])
+ break;
+
+ if (!strstr(machdep->cmdline_args[index], "=")) {
+ error(WARNING, "ignoring --machdep option: %x
",
+ machdep->cmdline_args[index]);
+ continue;
+ }
+
+ strcpy(buf, machdep->cmdline_args[index]);
+
+ for (p = buf; *p; p++) {
+ if (*p == ',')
+ *p = ' ';
+ }
+
+ c = parse_line(buf, arglist);
+
+ for (i = 0; i < c; i++) {
+ err = 0;
+
+ if (STRNEQ(arglist[i], "phys_base=")) {
+ int megabytes = FALSE;
+ int flags = RETURN_ON_ERROR | QUIET;
+
+ if ((LASTCHAR(arglist[i]) == 'm') ||
+ (LASTCHAR(arglist[i]) == 'M')) {
+ LASTCHAR(arglist[i]) = NULLCHAR;
+ megabytes = TRUE;
+ }
+
+ p = arglist[i] + strlen("phys_base=");
+ if (strlen(p)) {
+ if (megabytes)
+ value = dtol(p, flags, &err);
+ else
+ value = htol(p, flags, &err);
+ }
+
+ if (!err) {
+ if (megabytes)
+ value = MEGABYTES(value);
+
+ machdep->machspec->phys_base = value;
+
+ error(NOTE,
+ "setting phys_base to: 0x%lx
",
+ machdep->machspec->phys_base);
+
+ machdep->flags |= PHYS_BASE;
+ continue;
+ }
+ }
+
+ error(WARNING, "ignoring --machdep option: %s
",
+ arglist[i]);
+ }
+ }
+}
+
+/*
+ * Retrieve task registers for the time of the crash.
+ */
+static int
+arm_get_crash_notes(void)
+{
+ struct machine_specific *ms = machdep->machspec;
+ ulong crash_notes;
+ Elf32_Nhdr *note;
+ ulong ptr, offset;
+ char *buf, *p;
+
+ if (!symbol_exists("crash_notes"))
+ return FALSE;
+
+ crash_notes = symbol_value("crash_notes");
+
+ if (kt->cpus > 1)
+ error(WARNING, "only one CPU is currently supported
");
+
+ /*
+ * Read crash_notes for the first CPU. crash_notes are in standard ELF
+ * note format.
+ */
+ if (!readmem(crash_notes, KVADDR, &ptr, sizeof(ptr), "crash_notes",
+ RETURN_ON_ERROR)) {
+ error(WARNING, "cannot read crash_notes
");
+ return FALSE;
+ }
+
+ buf = GETBUF(SIZE(note_buf));
+
+ if (!readmem(ptr, KVADDR, buf, SIZE(note_buf), "note_buf_t",
+ RETURN_ON_ERROR)) {
+ error(WARNING, "failed to read note_buf_t
");
+ goto fail;
+ }
+
+ /*
+ * Do some sanity checks for this note before reading registers from it.
+ */
+ note = (Elf32_Nhdr *)buf;
+ p = buf + sizeof(Elf32_Nhdr);
+
+ if (note->n_type != NT_PRSTATUS) {
+ error(WARNING, "invalid note (n_type != NT_PRSTATUS)
");
+ goto fail;
+ }
+ if (p[0] != 'C' || p[1] != 'O' || p[2] != 'R' || p[3] != 'E') {
+ error(WARNING, "invalid note (name != "CORE"
");
+ goto fail;
+ }
+
+ /*
+ * Find correct location of note data. This contains elf_prstatus
+ * structure which has registers etc. for the crashed task.
+ */
+ offset = sizeof(Elf32_Nhdr);
+ offset = roundup(offset + note->n_namesz, 4);
+ p = buf + offset; /* start of elf_prstatus */
+
+ BCOPY(p + OFFSET(elf_prstatus_pr_reg), &panic_task_regs,
+ sizeof(panic_task_regs));
+
+ /*
+ * And finally we have pid and registers for the crashed task. This is
+ * used later on when dumping backtrace.
+ */
+ ms->crash_task_pid = *(ulong *)(p + OFFSET(elf_prstatus_pr_pid));
+ ms->crash_task_regs = &panic_task_regs;
+
+ FREEBUF(buf);
+ return TRUE;
+
+fail:
+ FREEBUF(buf);
+ return FALSE;
+}
+
+/*
+ * Accept or reject a symbol from the kernel namelist.
+ */
+static int
+arm_verify_symbol(const char *name, ulong value, char type)
+{
+ if (STREQ(name, "swapper_pg_dir"))
+ machdep->flags |= KSYMS_START;
+
+ if (!name || !strlen(name) || !(machdep->flags & KSYMS_START))
+ return FALSE;
+
+ if (STREQ(name, "$a") || STREQ(name, "$n") || STREQ(name, "$d"))
+ return FALSE;
+
+ if (CRASHDEBUG(8) && name && strlen(name))
+ fprintf(fp, "%08lx %s
", value, name);
+
+ return TRUE;
+}
+
+static int
+arm_is_module_addr(ulong vaddr)
+{
+ ulong modules_start;
+ ulong modules_end = machdep->kvbase - 1;
+
+ if (!MODULES_VADDR) {
+ /*
+ * In case we are still initializing, and vm_init() has not been
+ * called, we use defaults here which is 16MB below kernel start
+ * address.
+ */
+ modules_start = machdep->kvbase - 16 * 1024 * 1024;
+ } else {
+ modules_start = MODULES_VADDR;
+ }
+
+ return (vaddr >= modules_start && vaddr <= modules_end);
+}
+
+int
+arm_is_vmalloc_addr(ulong vaddr)
+{
+ if (arm_is_module_addr(vaddr))
+ return TRUE;
+
+ if (!VMALLOC_START)
+ return FALSE;
+
+ return (vaddr >= VMALLOC_START);
+}
+
+/*
+ * Check whether given address falls inside kernel address space (including
+ * modules).
+ */
+static int
+arm_is_kvaddr(ulong vaddr)
+{
+ if (arm_is_module_addr(vaddr))
+ return TRUE;
+
+ return (vaddr >= machdep->kvbase);
+}
+
+/*
+ * Returns TRUE if given pc is in exception area.
+ */
+static int
+arm_in_exception_text(ulong pc)
+{
+ ulong exception_start = machdep->machspec->exception_text_start;
+ ulong exception_end = machdep->machspec->exception_text_end;
+
+ if (exception_start && exception_end)
+ return (pc >= exception_start && pc < exception_end);
+
+ return FALSE;
+}
+
+/*
+ * Unroll the kernel stack using a minimal amount of gdb services.
+ */
+static void
+arm_back_trace(struct bt_info *bt)
+{
+ int n = 0;
+
+ /*
+ * In case bt->machdep contains pointer to a full register set, we take
+ * FP from there.
+ */
+ if (bt->machdep) {
+ const struct arm_pt_regs *regs = bt->machdep;
+ bt->frameptr = regs->ARM_fp;
+ }
+
+ /*
+ * Stack frame layout:
+ * optionally saved caller registers (r4 - r10)
+ * saved fp
+ * saved sp
+ * saved lr
+ * frame => saved pc
+ * optionally saved arguments (r0 - r3)
+ * saved sp => <next word>
+ *
+ * Functions start with the following code sequence:
+ * mov ip, sp
+ * stmfd sp!, {r0 - r3} (optional)
+ * corrected pc => stmfd sp!, {..., fp, ip, lr, pc}
+ */
+ while (bt->frameptr && INSTACK(bt->frameptr, bt)) {
+ ulong from;
+ ulong sp;
+
+ /*
+ * We correct the PC to point to the actual instruction (current
+ * value is PC + 8).
+ */
+ bt->instptr = GET_STACK_ULONG(bt->frameptr - 0);
+ bt->instptr -= 8;
+
+ /*
+ * Now get LR, saved SP and FP from the frame as well.
+ */
+ from = GET_STACK_ULONG(bt->frameptr - 4);
+ sp = GET_STACK_ULONG(bt->frameptr - 8);
+ bt->frameptr = GET_STACK_ULONG(bt->frameptr - 12);
+
+ arm_dump_backtrace_entry(bt, n++, from, sp);
+
+ bt->stkptr = sp;
+ }
+}
+
+/*
+ * Unroll a kernel stack.
+ */
+static void
+arm_back_trace_cmd(struct bt_info *bt)
+{
+ if (kt->flags & DWARF_UNWIND)
+ unwind_backtrace(bt);
+ else
+ arm_back_trace(bt);
+}
+
+/*
+ * Calculate and return the speed of the processor.
+ */
+static ulong
+arm_processor_speed(void)
+{
+ /*
+ * For now, we don't support reading CPU speed.
+ */
+ return 0;
+}
+
+/*
+ * Translate a PTE, returning TRUE if the page is present. If a physaddr pointer
+ * is passed in, don't print anything.
+ */
+static int
+arm_translate_pte(ulong pte, void *physaddr, ulonglong pae_pte)
+{
+ char ptebuf[BUFSIZE];
+ char physbuf[BUFSIZE];
+ char buf[BUFSIZE];
+ int page_present;
+ ulong paddr;
+ int len1, len2, others;
+
+ page_present = pte_present(pte);
+ paddr = PAGEBASE(pte);
+
+ if (physaddr) {
+ *((ulong *)physaddr) = paddr;
+ return page_present;
+ }
+
+ sprintf(ptebuf, "%lx", pte);
+ len1 = MAX(strlen(ptebuf), strlen("PTE"));
+ fprintf(fp, "%s ", mkstring(buf, len1, CENTER | LJUST, "PTE"));
+
+ if (!page_present && pte) {
+ /* swap page, not handled yet */
+ return page_present;
+ }
+
+ sprintf(physbuf, "%lx", paddr);
+ len2 = MAX(strlen(physbuf), strlen("PHYSICAL"));
+ fprintf(fp, "%s ", mkstring(buf, len2, CENTER | LJUST, "PHYSICAL"));
+
+ fprintf(fp, "FLAGS
");
+ fprintf(fp, "%s %s ",
+ mkstring(ptebuf, len1, CENTER | RJUST, NULL),
+ mkstring(physbuf, len2, CENTER | RJUST, NULL));
+
+ fprintf(fp, "(");
+ others = 0;
+
+ if (pte) {
+ if (pte_present(pte))
+ fprintf(fp, "%sPRESENT", others++ ? "|" : "");
+ if (pte_write(pte))
+ fprintf(fp, "%sWRITE", others++ ? "|" : "");
+ if (pte_dirty(pte))
+ fprintf(fp, "%sDIRTY", others++ ? "|" : "");
+ if (pte_young(pte))
+ fprintf(fp, "%sYOUNG", others++ ? "|" : "");
+ } else {
+ fprintf(fp, "no mapping");
+ }
+
+ fprintf(fp, ")
");
+
+ return 0;
+}
+
+/*
+ * Virtual to physical memory translation. This function will be called by both
+ * arm_kvtop() and arm_uvtop().
+ */
+static int
+arm_vtop(ulong vaddr, ulong *pgd, physaddr_t *paddr, int verbose)
+{
+ char buf[BUFSIZE];
+ ulong *page_dir;
+ ulong *page_middle;
+ ulong *page_table;
+ ulong pgd_pte;
+ ulong pmd_pte;
+ ulong pte;
+
+ /*
+ * Page tables in ARM Linux
+ *
+ * In hardware PGD is 16k (having 4096 pointers to PTE) and PTE is 1k
+ * (containing 256 translations).
+ *
+ * Linux, however, wants to have PTEs as page sized entities. This means
+ * that in ARM Linux we have following setup (see also
+ * arch/arm/include/asm/pgtable.h)
+ *
+ * PGD PTE
+ * +---------+
+ * | | 0 ----> +------------+
+ * +- - - - -+ | h/w pt 0 |
+ * | | 4 ----> +------------+ +1024
+ * +- - - - -+ | h/w pt 1 |
+ * . . +------------+ +2048
+ * . . | Linux pt 0 |
+ * . . +------------+ +3072
+ * | | 4095 | Linux pt 1 |
+ * +---------+ +------------+ +4096
+ *
+ * So in Linux implementation we have two hardware pointers to second
+ * level page tables. After these come "Linux" versions of the page
+ * tables.
+ *
+ * Linux PT entries contain bits that are not supported on hardware, for
+ * example "young" and "dirty" flags.
+ *
+ * Our translation scheme only uses Linux PTEs here. Hardware entries
+ * are 1024 bytes below Linux versions.
+ */
+
+ if (verbose)
+ fprintf(fp, "PAGE DIRECTORY: %lx
", (ulong)pgd);
+
+ /*
+ * pgd_offset(pgd, vaddr)
+ */
+ page_dir = pgd + PGD_OFFSET(vaddr) * 2;
+
+ FILL_PGD(PAGEBASE(pgd), KVADDR, PGDIR_SIZE());
+ pgd_pte = ULONG(machdep->pgd + PGDIR_OFFSET(page_dir));
+
+ if (verbose)
+ fprintf(fp, " PGD: %s => %lx
",
+ mkstring(buf, VADDR_PRLEN, RJUST | LONG_HEX,
+ MKSTR((ulong)page_dir)), pgd_pte);
+
+ if (!pgd_pte)
+ return FALSE;
+
+ /*
+ * pmd_offset(pgd, vaddr)
+ *
+ * Here PMD is folded into a PGD.
+ */
+ pmd_pte = pgd_pte;
+ page_middle = page_dir;
+
+ if (verbose)
+ fprintf(fp, " PMD: %s => %lx
",
+ mkstring(buf, VADDR_PRLEN, RJUST | LONG_HEX,
+ MKSTR((ulong)page_middle)), pmd_pte);
+
+ if ((pmd_pte & PMD_TYPE_MASK) == PMD_TYPE_SECT) {
+ if (verbose) {
+ fprintf(fp, " PAGE: %s (1MB)

",
+ mkstring(buf, VADDR_PRLEN, RJUST | LONG_HEX,
+ MKSTR(PAGEBASE(pmd_pte))));
+ }
+ *paddr = PAGEBASE(pmd_pte) + (vaddr & ~_SECTION_PAGE_MASK);
+ return TRUE;
+ }
+
+ /*
+ * pte_offset_map(pmd, vaddr)
+ */
+ page_table = (ulong *)PTOV(pmd_page_addr(pmd_pte)) + PTE_OFFSET(vaddr);
+
+ FILL_PTBL(PAGEBASE(page_table), KVADDR, PAGESIZE());
+ pte = ULONG(machdep->ptbl + PAGEOFFSET(page_table));
+
+ if (verbose) {
+ fprintf(fp, " PTE: %s => %lx

",
+ mkstring(buf, VADDR_PRLEN, RJUST | LONG_HEX,
+ MKSTR((ulong)page_table)), pte);
+ }
+
+ if (!pte_present(pte)) {
+ if (pte && verbose) {
+ fprintf(fp, "
");
+ arm_translate_pte(pte, 0, 0);
+ }
+ return FALSE;
+ }
+
+ *paddr = PAGEBASE(pte) + PAGEOFFSET(vaddr);
+
+ if (verbose) {
+ fprintf(fp, " PAGE: %s

",
+ mkstring(buf, VADDR_PRLEN, RJUST | LONG_HEX,
+ MKSTR(PAGEBASE(pte))));
+ arm_translate_pte(pte, 0, 0);
+ }
+
+ return TRUE;
+}
+
+/*
+ * Translates a user virtual address to its physical address. cmd_vtop() sets
+ * the verbose flag so that the pte translation gets displayed; all other
+ * callers quietly accept the translation.
+ */
+static int
+arm_uvtop(struct task_context *tc, ulong uvaddr, physaddr_t *paddr, int verbose)
+{
+ ulong *pgd;
+ ulong mm;
+
+ if (!tc)
+ error(FATAL, "current context invalid
");
+
+ *paddr = 0;
+
+ if (IS_KVADDR(uvaddr))
+ return arm_kvtop(tc, uvaddr, paddr, verbose);
+
+ mm = task_mm(tc->task, TRUE);
+ if (mm)
+ pgd = ULONG_PTR(tt->mm_struct + OFFSET(mm_struct_pgd));
+ else
+ readmem(tc->mm_struct + OFFSET(mm_struct_pgd), KVADDR, &pgd,
+ sizeof(long), "mm_struct pgd", FAULT_ON_ERROR);
+
+ return arm_vtop(uvaddr, pgd, paddr, verbose);
+}
+
+/*
+ * Translates a kernel virtual address to its physical address. cmd_vtop() sets
+ * the verbose flag so that the pte translation gets displayed; all other
+ * callers quietly accept the translation.
+ */
+static int
+arm_kvtop(struct task_context *tc, ulong kvaddr, physaddr_t *paddr, int verbose)
+{
+ if (!IS_KVADDR(kvaddr))
+ return FALSE;
+
+ if (!vt->vmalloc_start) {
+ *paddr = VTOP(kvaddr);
+ return TRUE;
+ }
+
+ if (!IS_VMALLOC_ADDR(kvaddr)) {
+ *paddr = VTOP(kvaddr);
+ if (!verbose)
+ return TRUE;
+ }
+
+ return arm_vtop(kvaddr, (ulong *)vt->kernel_pgd[0], paddr, verbose);
+}
+
+/*
+ * Get SP and PC values for idle tasks.
+ */
+static int
+arm_get_frame(struct bt_info *bt, ulong *pcp, ulong *spp)
+{
+ const char *cpu_context;
+
+ if (!bt->tc || !(tt->flags & THREAD_INFO))
+ return FALSE;
+
+ /*
+ * Update thread_info in tt.
+ */
+ if (!fill_thread_info(bt->tc->thread_info))
+ return FALSE;
+
+ cpu_context = tt->thread_info + OFFSET(thread_info_cpu_context);
+
+#define GET_REG(ptr, cp, off) ((*ptr) = (*((ulong *)((cp) + OFFSET(off)))))
+ /*
+ * Unwinding code needs FP value also so we pass it with bt.
+ */
+ GET_REG(&bt->frameptr, cpu_context, cpu_context_save_fp);
+ GET_REG(spp, cpu_context, cpu_context_save_sp);
+ GET_REG(pcp, cpu_context, cpu_context_save_pc);
+
+ return TRUE;
+}
+
+/*
+ * Get the starting point for the active cpu in a diskdump.
+ *
+ * Note that we currently support only UP machines. In future we might want to
+ * support SMP machines as well.
+ */
+static int
+arm_get_dumpfile_stack_frame(struct bt_info *bt, ulong *nip, ulong *ksp)
+{
+ const struct machine_specific *ms = machdep->machspec;
+
+ if (!ms->crash_task_regs)
+ return FALSE;
+
+ if (tt->panic_task != bt->task || bt->tc->pid != ms->crash_task_pid)
+ return FALSE;
+
+ /*
+ * We got registers for panic task from crash_notes. Just return them.
+ */
+ *nip = ms->crash_task_regs->ARM_pc;
+ *ksp = ms->crash_task_regs->ARM_sp;
+
+ /*
+ * Also store pointer to all registers in case unwinding code needs
+ * to access LR.
+ */
+ bt->machdep = ms->crash_task_regs;
+
+ return TRUE;
+}
+
+/*
+ * Get a stack frame combination of PC and SP from the most relevant spot.
+ */
+static void
+arm_get_stack_frame(struct bt_info *bt, ulong *pcp, ulong *spp)
+{
+ ulong ip, sp;
+ int ret;
+
+ ip = sp = 0;
+ bt->machdep = NULL;
+
+ if (DUMPFILE() && is_task_active(bt->task))
+ ret = arm_get_dumpfile_stack_frame(bt, &ip, &sp);
+ else
+ ret = arm_get_frame(bt, &ip, &sp);
+
+ if (!ret) {
+ error(WARNING, "cannot get stackframe for task
");
+ return;
+ }
+
+ if (pcp)
+ *pcp = ip;
+ if (spp)
+ *spp = sp;
+}
+
+/*
+ * Prints out exception stack starting from start.
+ */
+void
+arm_dump_exception_stack(ulong start, ulong end)
+{
+ struct arm_pt_regs regs;
+ ulong flags;
+ char buf[64];
+
+ if (!readmem(start, KVADDR, &regs, sizeof(regs),
+ "exception regs", RETURN_ON_ERROR)) {
+ error(WARNING, "failed to read exception registers
");
+ return;
+ }
+
+ fprintf(fp, " pc : [<%08lx>] lr : [<%08lx>] psr: %08lx
"
+ " sp : %08lx ip : %08lx fp : %08lx
",
+ regs.ARM_pc, regs.ARM_lr, regs.ARM_cpsr,
+ regs.ARM_sp, regs.ARM_ip, regs.ARM_fp);
+ fprintf(fp, " r10: %08lx r9 : %08lx r8 : %08lx
",
+ regs.ARM_r10, regs.ARM_r9, regs.ARM_r8);
+ fprintf(fp, " r7 : %08lx r6 : %08lx r5 : %08lx r4 : %08lx
",
+ regs.ARM_r7, regs.ARM_r6,
+ regs.ARM_r5, regs.ARM_r4);
+ fprintf(fp, " r3 : %08lx r2 : %08lx r1 : %08lx r0 : %08lx
",
+ regs.ARM_r3, regs.ARM_r2,
+ regs.ARM_r1, regs.ARM_r0);
+
+ flags = regs.ARM_cpsr;
+ buf[0] = flags & PSR_N_BIT ? 'N' : 'n';
+ buf[1] = flags & PSR_Z_BIT ? 'Z' : 'z';
+ buf[2] = flags & PSR_C_BIT ? 'C' : 'c';
+ buf[3] = flags & PSR_V_BIT ? 'V' : 'v';
+ buf[4] = '';
+
+ fprintf(fp, " Flags: %s IRQs o%s FIQs o%s Mode %s ISA %s
",
+ buf, interrupts_enabled(&regs) ? "n" : "ff",
+ fast_interrupts_enabled(&regs) ? "n" : "ff",
+ processor_modes[processor_mode(&regs)],
+ isa_modes[isa_mode(&regs)]);
+}
+
+static void
+arm_display_full_frame(struct bt_info *bt, ulong sp)
+{
+ ulong words, addr;
+ ulong *up;
+ char buf[BUFSIZE];
+ int i, u_idx;
+
+ if (!INSTACK(sp, bt) || !INSTACK(bt->stkptr, bt))
+ return;
+
+ words = (sp - bt->stkptr) / sizeof(ulong);
+
+ if (words == 0) {
+ fprintf(fp, " (no frame)
");
+ return;
+ }
+
+ addr = bt->stkptr;
+ u_idx = (bt->stkptr - bt->stackbase) / sizeof(ulong);
+ for (i = 0; i < words; i++, u_idx++) {
+ if ((i % 4) == 0)
+ fprintf(fp, "%s %lx: ", i ? "
" : "", addr);
+
+ up = (ulong *)(&bt->stackbuf[u_idx * sizeof(ulong)]);
+ fprintf(fp, "%s ", format_stack_entry(bt, buf, *up, 0));
+ addr += sizeof(ulong);
+ }
+ fprintf(fp, "
");
+}
+
+/*
+ * Prints out a single stack frame. What is printed depends on flags passed in
+ * with bt.
+ *
+ * What is expected when calling this function:
+ * bt->frameptr = current FP (or 0 if there is no such)
+ * bt->stkptr = current SP
+ * bt->instptr = current PC
+ *
+ * from = LR
+ * sp = previous/saved SP
+ */
+void
+arm_dump_backtrace_entry(struct bt_info *bt, int level, ulong from, ulong sp)
+{
+ struct load_module *lm;
+ const char *name;
+
+ name = closest_symbol(bt->instptr);
+
+ if (module_symbol(bt->instptr, NULL, &lm, NULL, 0)) {
+ fprintf(fp, "%s#%d [<%08lx>] (%s [%s]) from [<%08lx>]
",
+ level < 10 ? " " : "",
+ level, bt->instptr, name, lm->mod_name, from);
+ } else {
+ fprintf(fp, "%s#%d [<%08lx>] (%s) from [<%08lx>]
",
+ level < 10 ? " " : "",
+ level, bt->instptr, name, from);
+ }
+
+ if (bt->flags & BT_LINE_NUMBERS) {
+ char buf[BUFSIZE];
+
+ get_line_number(bt->instptr, buf, FALSE);
+ if (strlen(buf))
+ fprintf(fp, " %s
", buf);
+ }
+
+ if (arm_in_exception_text(bt->instptr))
+ arm_dump_exception_stack(sp, sp + sizeof(struct arm_pt_regs));
+
+ if (bt->flags & BT_FULL) {
+ if (kt->flags & DWARF_UNWIND) {
+ fprintf(fp, " "
+ "[PC: %08lx LR: %08lx SP: %08lx SIZE: %d]
",
+ bt->instptr, from, bt->stkptr, sp - bt->stkptr);
+ } else {
+ fprintf(fp, " "
+ "[PC: %08lx LR: %08lx SP: %08lx FP: %08lx "
+ "SIZE: %d]
",
+ bt->instptr, from, bt->stkptr, bt->frameptr,
+ sp - bt->stkptr);
+ }
+ arm_display_full_frame(bt, sp);
+ }
+}
+
+/*
+ * Determine where vmalloc'd memory starts.
+ */
+static ulong
+arm_vmalloc_start(void)
+{
+ return vt->high_memory;
+}
+
+/*
+ * Checks whether given task is valid task address.
+ */
+static int
+arm_is_task_addr(ulong task)
+{
+ if (tt->flags & THREAD_INFO)
+ return IS_KVADDR(task);
+
+ return (IS_KVADDR(task) && ALIGNED_STACK_OFFSET(task) == 0);
+}
+
+/*
+ * Filter dissassembly output if the output radix is not gdb's default 10
+ */
+static int
+arm_dis_filter(ulong vaddr, char *inbuf)
+{
+ char buf1[BUFSIZE];
+ char buf2[BUFSIZE];
+ char *colon, *p1;
+ int argc;
+ char *argv[MAXARGS];
+ ulong value;
+
+ if (!inbuf)
+ return TRUE;
+/*
+ * For some reason gdb can go off into the weeds translating text addresses,
+ * (on alpha -- not necessarily seen on arm) so this routine both fixes the
+ * references as well as imposing the current output radix on the translations.
+ */
+ console("IN: %s", inbuf);
+
+ colon = strstr(inbuf, ":");
+
+ if (colon) {
+ sprintf(buf1, "0x%lx <%s>", vaddr,
+ value_to_symstr(vaddr, buf2, pc->output_radix));
+ sprintf(buf2, "%s%s", buf1, colon);
+ strcpy(inbuf, buf2);
+ }
+
+ strcpy(buf1, inbuf);
+ argc = parse_line(buf1, argv);
+
+ if ((FIRSTCHAR(argv[argc-1]) == '<') &&
+ (LASTCHAR(argv[argc-1]) == '>')) {
+ p1 = rindex(inbuf, '<');
+ while ((p1 > inbuf) && !STRNEQ(p1, " 0x"))
+ p1--;
+
+ if (!STRNEQ(p1, " 0x"))
+ return FALSE;
+ p1++;
+
+ if (!extract_hex(p1, &value, NULLCHAR, TRUE))
+ return FALSE;
+
+ sprintf(buf1, "0x%lx <%s>
", value,
+ value_to_symstr(value, buf2, pc->output_radix));
+
+ sprintf(p1, buf1);
+ }
+
+ console(" %s", inbuf);
+
+ return TRUE;
+}
+
+/*
+ * Look for likely exception frames in a stack.
+ */
+static int
+arm_eframe_search(struct bt_info *bt)
+{
+ return (NOT_IMPLEMENTED());
+}
+
+/*
+ * Get the relevant page directory pointer from a task structure.
+ */
+static ulong
+arm_get_task_pgd(ulong task)
+{
+ return (NOT_IMPLEMENTED());
+}
+
+/*
+ * Machine dependent command.
+ */
+static void
+arm_cmd_mach(void)
+{
+ int c;
+
+ while ((c = getopt(argcnt, args, "cm")) != -1) {
+ switch (c) {
+ case 'c':
+ case 'm':
+ fprintf(fp, "ARM: '-%c' option is not supported
", c);
+ break;
+
+ default:
+ argerrs++;
+ break;
+ }
+ }
+
+ if (argerrs)
+ cmd_usage(pc->curcmd, SYNOPSIS);
+
+ arm_display_machine_stats();
+}
+
+static void
+arm_display_machine_stats(void)
+{
+ struct new_utsname *uts;
+ char buf[BUFSIZE];
+ ulong mhz;
+
+ uts = &kt->utsname;
+
+ fprintf(fp, " MACHINE TYPE: %s
", uts->machine);
+ fprintf(fp, " MEMORY SIZE: %s
", get_memory_size(buf));
+ fprintf(fp, " CPUS: %d
", get_cpus_to_display());
+ fprintf(fp, " PROCESSOR SPEED: ");
+ if ((mhz = machdep->processor_speed()))
+ fprintf(fp, "%ld Mhz
", mhz);
+ else
+ fprintf(fp, "(unknown)
");
+ fprintf(fp, " HZ: %d
", machdep->hz);
+ fprintf(fp, " PAGE SIZE: %d
", PAGESIZE());
+ fprintf(fp, "KERNEL VIRTUAL BASE: %lx
", machdep->kvbase);
+ fprintf(fp, "KERNEL VMALLOC BASE: %lx
", vt->vmalloc_start);
+ fprintf(fp, " KERNEL STACK SIZE: %ld
", STACKSIZE());
+}
+
+static int
+arm_get_smp_cpus(void)
+{
+ return get_cpus_online();
+}
+
+static void
+print_irq_member(char *irq, ulong val, char *ind)
+{
+ char buf[BUFSIZE];
+ ulong val2;
+
+ fprintf(fp, "%17s: %8lx ", irq, val);
+ if (val) {
+ if (is_kernel_text(val))
+ fprintf(fp, "<%s>", value_to_symstr(val, buf, 0));
+ else if (readmem(val, KVADDR, &val2,
+ sizeof(ulong), ind, RETURN_ON_ERROR|QUIET) &&
+ is_kernel_text(val2))
+ fprintf(fp, "<%s>", value_to_symstr(val2, buf, 0));
+ }
+ fprintf(fp, "
");
+}
+
+/*
+ * Do the work for cmd_irq().
+ */
+static void
+arm_dump_irq(int irq)
+{
+ struct datatype_member datatype_member, *dm;
+ ulong irq_desc_addr;
+ ulong irq_desc_ptr;
+ long len;
+ char buf[BUFSIZE];
+ int type, status, depth, others;
+ ulong handler, action, value;
+ ulong tmp1, tmp2;
+
+ dm = &datatype_member;
+
+ if (!VALID_STRUCT(irq_desc_t))
+ error(FATAL, "cannot determine size of irq_desc
");
+ len = SIZE(irq_desc_t);
+
+ if (symbol_exists("irq_desc"))
+ irq_desc_addr = symbol_value("irq_desc") + (len * irq);
+ else if (symbol_exists("_irq_desc"))
+ irq_desc_addr = symbol_value("_irq_desc") + (len * irq);
+ else if (symbol_exists("irq_desc_ptrs")) {
+ get_symbol_data("irq_desc_ptrs", sizeof(void *), &irq_desc_ptr);
+ irq_desc_ptr += (irq * sizeof(void *));
+ readmem(irq_desc_ptr, KVADDR, &irq_desc_addr,
+ sizeof(void *), "irq_desc_ptrs entry",
+ FAULT_ON_ERROR);
+ if (!irq_desc_addr) {
+ fprintf(fp, " IRQ: %d (unused)

", irq);
+ return;
+ }
+ } else {
+ irq_desc_addr = 0;
+ error(FATAL,
+ "neither irq_desc, _irq_desc, nor irq_desc_ptrs "
+ "symbols exist
");
+ }
+
+ readmem(irq_desc_addr + OFFSET(irq_desc_t_status), KVADDR, &status,
+ sizeof(int), "irq_desc entry", FAULT_ON_ERROR);
+ if (VALID_MEMBER(irq_desc_t_handler))
+ readmem(irq_desc_addr + OFFSET(irq_desc_t_handler), KVADDR,
+ &handler, sizeof(long), "irq_desc entry",
+ FAULT_ON_ERROR);
+ else if (VALID_MEMBER(irq_desc_t_chip))
+ readmem(irq_desc_addr + OFFSET(irq_desc_t_chip), KVADDR,
+ &handler, sizeof(long), "irq_desc entry",
+ FAULT_ON_ERROR);
+ readmem(irq_desc_addr + OFFSET(irq_desc_t_action), KVADDR, &action,
+ sizeof(long), "irq_desc entry", FAULT_ON_ERROR);
+ readmem(irq_desc_addr + OFFSET(irq_desc_t_depth), KVADDR, &depth,
+ sizeof(int), "irq_desc entry", FAULT_ON_ERROR);
+
+ if (!action && (handler == (ulong)pc->curcmd_private))
+ return;
+
+ fprintf(fp, " IRQ: %d
", irq);
+ fprintf(fp, "ADDRESS: %08lx
", irq_desc_addr);
+
+ if (VALID_MEMBER(irq_desc_t_name)) {
+ readmem(irq_desc_addr+OFFSET(irq_desc_t_name),
+ KVADDR, &tmp1, sizeof(void *),
+ "irq_desc name", FAULT_ON_ERROR);
+ if (tmp1) {
+ fprintf(fp, " NAME: %lx", tmp1);
+ BZERO(buf, BUFSIZE);
+ if (read_string(tmp1, buf, BUFSIZE-1))
+ fprintf(fp, " "%s"", buf);
+ fprintf(fp, "
");
+ }
+ }
+
+ type = status & IRQ_TYPE_SENSE_MASK;
+ fprintf(fp, " TYPE: %x %s", type, type ? "(" : "");
+ others = 0;
+
+ if (type & IRQ_TYPE_EDGE_BOTH == IRQ_TYPE_EDGE_BOTH)
+ fprintf(fp, "%sEDGE_BOTH", others++ ? "|" : "");
+ else {
+ if (type & IRQ_TYPE_EDGE_RISING)
+ fprintf(fp, "%sEDGE_RISING", others++ ? "|" : "");
+ if (type & IRQ_TYPE_EDGE_FALLING)
+ fprintf(fp, "%sEDGE_FALLING", others++ ? "|" : "");
+ }
+ if (type & IRQ_TYPE_LEVEL_HIGH)
+ fprintf(fp, "%sLEVEL_HIGH", others++ ? "|" : "");
+ if (type & IRQ_TYPE_LEVEL_LOW)
+ fprintf(fp, "%sLEVEL_LOW", others++ ? "|" : "");
+ fprintf(fp, "%s
", type ? ")" : "");
+
+ fprintf(fp, " PROBE: %s
", status & IRQ_TYPE_PROBE ? "true" : "false");
+
+ fprintf(fp, " STATUS: %x %s", status, status ? "(" : "");
+ others = 0;
+ if (status & IRQ_INPROGRESS) {
+ fprintf(fp, "IRQ_INPROGRESS");
+ others++;
+ }
+ if (status & IRQ_DISABLED)
+ fprintf(fp, "%sIRQ_DISABLED", others++ ? "|" : "");
+ if (status & IRQ_PENDING)
+ fprintf(fp, "%sIRQ_PENDING", others++ ? "|" : "");
+ if (status & IRQ_REPLAY)
+ fprintf(fp, "%sIRQ_REPLAY", others++ ? "|" : "");
+ if (status & IRQ_AUTODETECT)
+ fprintf(fp, "%sIRQ_AUTODETECT", others++ ? "|" : "");
+ if (status & IRQ_WAITING)
+ fprintf(fp, "%sIRQ_WAITING", others++ ? "|" : "");
+ if (status & IRQ_LEVEL)
+ fprintf(fp, "%sIRQ_LEVEL", others++ ? "|" : "");
+ if (status & IRQ_MASKED)
+ fprintf(fp, "%sIRQ_MASKED", others++ ? "|" : "");
+
+ if (status & IRQ_PER_CPU)
+ fprintf(fp, "%sIRQ_PER_CPU", others++ ? "|" : "");
+ if (status & IRQ_NOPROBE)
+ fprintf(fp, "%sIRQ_NOPROBE", others++ ? "|" : "");
+ if (status & IRQ_NOREQUEST)
+ fprintf(fp, "%sIRQ_NOREQUEST", others++ ? "|" : "");
+ if (status & IRQ_NOAUTOEN)
+ fprintf(fp, "%sIRQ_NOAUTOEN", others++ ? "|" : "");
+ if (status & IRQ_WAKEUP)
+ fprintf(fp, "%sIRQ_WAKEUP", others++ ? "|" : "");
+ if (status & IRQ_MOVE_PENDING)
+ fprintf(fp, "%sIRQ_MOVE_PENDING", others++ ? "|" : "");
+ if (status & IRQ_NO_BALANCING)
+ fprintf(fp, "%sIRQ_NO_BALANCING", others++ ? "|" : "");
+ if (status & IRQ_SPURIOUS_DISABLED)
+ fprintf(fp, "%sIRQ_SPURIOUS_DISABLED", others++ ? "|" : "");
+ if (status & IRQ_MOVE_PCNTXT)
+ fprintf(fp, "%sIRQ_MOVE_PCNTXT", others++ ? "|" : "");
+ if (status & IRQ_AFFINITY_SET)
+ fprintf(fp, "%sIRQ_AFFINITY_SET", others++ ? "|" : "");
+
+
+ fprintf(fp, "%s
", status ? ")" : "");
+
+ fprintf(fp, "HANDLER: %8lx ", handler);
+ if (value_symbol(handler)) {
+ pad_line(fp, VADDR_PRLEN == 8 ? VADDR_PRLEN+2 : VADDR_PRLEN-6, ' ');
+ fprintf(fp, "<%s>", value_symbol(handler));
+ }
+ fprintf(fp, "
", handler);
+
+#define CHECK_HANDLER_OPT_MEMBER_PRT(x)
+ if (VALID_MEMBER(hw_interrupt_type_ ## x)) {
+ readmem(handler+OFFSET(hw_interrupt_type_ ## x), KVADDR,
+ &tmp1, sizeof(void *), "hw_interrupt_type " #x,
+ FAULT_ON_ERROR);
+ print_irq_member(#x, tmp1, #x " indirection");
+ } else if (VALID_MEMBER(irq_chip_ ## x)) {
+ readmem(handler+OFFSET(irq_chip_ ## x),KVADDR, &tmp1,
+ sizeof(void *), "irq_chip " #x, FAULT_ON_ERROR);
+ print_irq_member(#x, tmp1, #x " indirection");
+ }
+
+#define CHECK_HANDLER_OPT_MEMBER(x)
+ if (VALID_MEMBER(hw_interrupt_type_ ## x))
+ readmem(handler+OFFSET(hw_interrupt_type_ ## x), KVADDR,
+ &tmp1, sizeof(void *), "hw_interrupt_type " #x,
+ FAULT_ON_ERROR);
+ else if (VALID_MEMBER(irq_chip_ ## x))
+ readmem(handler+OFFSET(irq_chip_ ## x),KVADDR, &tmp1,
+ sizeof(void *), "irq_chip " #x, FAULT_ON_ERROR);
+
+#define CHECK_HANDLER_MEMBER_PRT(x,s1,s2)
+ if (VALID_MEMBER(x)) {
+ readmem(handler+OFFSET(x), KVADDR, &tmp1, sizeof(void *),
+ s1, FAULT_ON_ERROR);
+ print_irq_member(s2, tmp1, s2 " indirection");
+ }
+
+ if (handler) {
+ CHECK_HANDLER_OPT_MEMBER(typename);
+
+ fprintf(fp, " typename: %lx ", tmp1);
+ BZERO(buf, BUFSIZE);
+ if (read_string(tmp1, buf, BUFSIZE-1))
+ fprintf(fp, ""%s"", buf);
+ fprintf(fp, "
");
+
+ CHECK_HANDLER_OPT_MEMBER(startup);
+
+ print_irq_member("startup", tmp1, "startup indirection");
+
+ CHECK_HANDLER_OPT_MEMBER(shutdown);
+
+ print_irq_member("shutdown", tmp1, "shutdown indirection");
+
+ CHECK_HANDLER_MEMBER_PRT(hw_interrupt_type_handle,
+ "hw_interrupt_type handle",
+ "handle");
+
+ CHECK_HANDLER_OPT_MEMBER(enable);
+
+ print_irq_member("enable", tmp1, "enable indirection");
+
+ CHECK_HANDLER_OPT_MEMBER(disable);
+
+ print_irq_member("disable", tmp1, "disable indirection");
+
+ CHECK_HANDLER_OPT_MEMBER(ack);
+
+ print_irq_member("ack", tmp1, "ack indirection");
+
+ CHECK_HANDLER_MEMBER_PRT(irq_chip_mask, "irq_chip mask","mask");
+
+ CHECK_HANDLER_MEMBER_PRT(irq_chip_mask_ack, "irq_chip mask_ack",
+ "mask_ack");
+
+ CHECK_HANDLER_MEMBER_PRT(irq_chip_unmask, "irq_chip unmask",
+ "unmask");
+
+ CHECK_HANDLER_MEMBER_PRT(irq_chip_eoi,"irq_chip eoi","eoi");
+
+ CHECK_HANDLER_OPT_MEMBER_PRT(startup);
+
+ CHECK_HANDLER_OPT_MEMBER_PRT(set_affinity);
+
+ CHECK_HANDLER_MEMBER_PRT(irq_chip_retrigger,
+ "irq_chip retrigger", "retrigger");
+ CHECK_HANDLER_MEMBER_PRT(irq_chip_set_type,
+ "irq_chip set_type", "set_type");
+ CHECK_HANDLER_MEMBER_PRT(irq_chip_set_wake,
+ "irq_chip set_wake", "set_wake");
+ }
+
+do_linked_action:
+
+ fprintf(fp, " ACTION: ");
+ if (value_symbol(action)) {
+ fprintf(fp, "%lx ", action);
+ pad_line(fp, VADDR_PRLEN == 8 ?
+ VADDR_PRLEN+2 : VADDR_PRLEN - 6, ' ');
+ fprintf(fp, "<%s>
", value_symbol(action));
+ } else if (action) {
+ fprintf(fp, "%8lx
", action);
+ } else {
+ fprintf(fp, "(none)
");
+ }
+
+ if (action) {
+ readmem(action+OFFSET(irqaction_handler), KVADDR,
+ &tmp1, sizeof(void *),
+ "irqaction handler", FAULT_ON_ERROR);
+
+ print_irq_member("handler", tmp1, "handler indirection");
+
+ readmem(action+OFFSET(irqaction_flags), KVADDR,
+ &value, sizeof(void *),
+ "irqaction flags", FAULT_ON_ERROR);
+ fprintf(fp, " flags: %8lx
", value);
+
+ if (VALID_MEMBER(irqaction_mask)) {
+ readmem(action+OFFSET(irqaction_mask), KVADDR,
+ &tmp1, sizeof(void *),
+ "irqaction mask", FAULT_ON_ERROR);
+ fprintf(fp, " mask: %8lx
", tmp1);
+ }
+
+ readmem(action+OFFSET(irqaction_name), KVADDR,
+ &tmp1, sizeof(void *),
+ "irqaction name", FAULT_ON_ERROR);
+ fprintf(fp, " name: %8lx ", tmp1);
+ BZERO(buf, BUFSIZE);
+ if (read_string(tmp1, buf, BUFSIZE-1))
+ fprintf(fp, ""%s"", buf);
+ fprintf(fp, "
");
+
+ readmem(action+OFFSET(irqaction_dev_id), KVADDR,
+ &tmp1, sizeof(void *),
+ "irqaction dev_id", FAULT_ON_ERROR);
+ fprintf(fp, " dev_id: %8lx
", tmp1);
+
+ readmem(action+OFFSET(irqaction_next), KVADDR,
+ &action, sizeof(void *),
+ "irqaction dev_id", FAULT_ON_ERROR);
+ fprintf(fp, " next: %8lx
", action);
+ }
+
+ if (action)
+ goto do_linked_action;
+
+ fprintf(fp, " DEPTH: %d

", depth);
+}
+
+/*
+ * Initialize ARM specific stuff.
+ */
+static void
+arm_init_machspec(void)
+{
+ struct machine_specific *ms = machdep->machspec;
+ ulong phys_base;
+
+ if (symbol_exists("__exception_text_start") &&
+ symbol_exists("__exception_text_end")) {
+ ms->exception_text_start = symbol_value("__exception_text_start");
+ ms->exception_text_end = symbol_value("__exception_text_end");
+ }
+
+ if (symbol_exists("_stext") && symbol_exists("_etext")) {
+ ms->kernel_text_start = symbol_value("_stext");
+ ms->kernel_text_end = symbol_value("_etext");
+ }
+
+ if (CRASHDEBUG(1)) {
+ fprintf(fp, "kernel text: [%lx - %lx]
",
+ ms->kernel_text_start, ms->kernel_text_end);
+ fprintf(fp, "exception text: [%lx - %lx]
",
+ ms->exception_text_start, ms->exception_text_end);
+ }
+
+ if (machdep->flags & PHYS_BASE) /* --machdep override */
+ return;
+
+ /*
+ * Next determine suitable value for phys_base. User can override this
+ * by passing valid '--machdep phys_base=<addr>' option.
+ */
+ ms->phys_base = 0;
+
+ if (ACTIVE()) {
+ char buf[BUFSIZE];
+ char *p1;
+ int errflag;
+ FILE *fp;
+
+ if ((fp = fopen("/proc/iomem", "r")) == NULL)
+ return;
+
+ /*
+ * Memory regions are sorted in ascending order. We take the
+ * first region which should be correct for most uses.
+ */
+ errflag = 1;
+ while (fgets(buf, BUFSIZE, fp)) {
+ if (strstr(buf, ": System RAM")) {
+ clean_line(buf);
+ errflag = 0;
+ break;
+ }
+ }
+ fclose(fp);
+
+ if (errflag)
+ return;
+
+ if (!(p1 = strstr(buf, "-")))
+ return;
+
+ *p1 = NULLCHAR;
+
+ phys_base = htol(buf, RETURN_ON_ERROR | QUIET, &errflag);
+ if (errflag)
+ return;
+
+ ms->phys_base = phys_base;
+ } else if (DISKDUMP_DUMPFILE() && diskdump_phys_base(&phys_base)) {
+ ms->phys_base = phys_base;
+ } else if (KDUMP_DUMPFILE() && kdump_phys_base(&phys_base)) {
+ ms->phys_base = phys_base;
+ } else {
+ error(WARNING,
+ "phys_base cannot be determined from the dumpfile.
"
+ "Using default value of 0. If this is not correct,
"
+ "consider using '--machdep phys_base=<addr>'
");
+ }
+
+ if (CRASHDEBUG(1))
+ fprintf(fp, "using %lx as phys_base
", ms->phys_base);
+}
+
+static const char *hook_files[] = {
+ "arch/arm/kernel/entry-armv.S",
+ "arch/arm/kernel/entry-common.S",
+};
+
+#define ENTRY_ARMV_S ((char **)&hook_files[0])
+#define ENTRY_COMMON_S ((char **)&hook_files[1])
+
+static struct line_number_hook arm_line_number_hooks[] = {
+ { "__dabt_svc", ENTRY_ARMV_S },
+ { "__irq_svc", ENTRY_ARMV_S },
+ { "__und_svc", ENTRY_ARMV_S },
+ { "__pabt_svc", ENTRY_ARMV_S },
+ { "__switch_to", ENTRY_ARMV_S },
+
+ { "ret_fast_syscall", ENTRY_COMMON_S },
+ { "ret_slow_syscall", ENTRY_COMMON_S },
+ { "ret_from_fork", ENTRY_COMMON_S },
+ { NULL, NULL },
+};
+#endif /* ARM */
diff --git a/defs.h b/defs.h
index c8bae00..f9118f0 100755
--- a/defs.h
+++ b/defs.h
@@ -84,6 +84,9 @@
#ifdef S390X
#define NR_CPUS (64)
#endif
+#ifdef ARM
+#define NR_CPUS (1)
+#endif

#define BUFSIZE (1500)
#define NULLCHAR ('')
@@ -1513,6 +1516,22 @@ struct offset_table { /* stash of commonly-used offsets */
long mm_rss_stat_count;
long module_module_init;
long module_init_text_size;
+#ifdef ARM
+ long cpu_context_save_fp;
+ long cpu_context_save_sp;
+ long cpu_context_save_pc;
+ long elf_prstatus_pr_pid;
+ long elf_prstatus_pr_reg;
+ long irq_desc_t_name;
+ long thread_info_cpu_context;
+ long unwind_table_list;
+ long unwind_table_start;
+ long unwind_table_stop;
+ long unwind_table_begin_addr;
+ long unwind_table_end_addr;
+ long unwind_idx_addr;
+ long unwind_idx_insn;
+#endif /* ARM */
};

struct size_table { /* stash of commonly-used sizes */
@@ -1625,6 +1644,12 @@ struct size_table { /* stash of commonly-used sizes */
long module_sect_attr;
long task_struct_utime;
long task_struct_stime;
+#ifdef ARM
+ long cpu_context_save;
+ long elf_prstatus;
+ long note_buf;
+ long unwind_idx;
+#endif /* ARM */
};

struct array_table {
@@ -2099,6 +2124,49 @@ struct load_module {
* Machine specific stuff
*/

+#ifdef ARM
+#define _32BIT_
+#define MACHINE_TYPE "ARM"
+
+#define PAGEBASE(X) (((ulong)(X)) & (ulong)machdep->pagemask)
+
+#define PTOV(X)
+ ((unsigned long)(X)-(machdep->machspec->phys_base)+(machdep->kvbase))
+#define VTOP(X)
+ ((unsigned long)(X)-(machdep->kvbase)+(machdep->machspec->phys_base))
+
+#define IS_VMALLOC_ADDR(X) arm_is_vmalloc_addr((ulong)(X))
+
+#define MODULES_VADDR (machdep->machspec->modules_vaddr)
+#define MODULES_END (machdep->machspec->modules_end)
+#define VMALLOC_START (machdep->machspec->vmalloc_start_addr)
+#define VMALLOC_END (machdep->machspec->vmalloc_end)
+
+#define PGDIR_SHIFT (21)
+#define PTRS_PER_PTE (512)
+#define PTRS_PER_PGD (2048)
+
+#define PGD_OFFSET(vaddr) ((vaddr) >> PGDIR_SHIFT)
+#define PTE_OFFSET(vaddr) (((vaddr) >> PAGESHIFT()) & (PTRS_PER_PTE - 1))
+
+#define __SWP_TYPE_SHIFT 3
+#define __SWP_TYPE_BITS 6
+#define __SWP_TYPE_MASK ((1 << __SWP_TYPE_BITS) - 1)
+#define __SWP_OFFSET_SHIFT (__SWP_TYPE_BITS + __SWP_TYPE_SHIFT)
+
+#define SWP_TYPE(entry) (((entry) >> __SWP_TYPE_SHIFT) & __SWP_TYPE_MASK)
+#define SWP_OFFSET(entry) ((entry) >> __SWP_OFFSET_SHIFT)
+
+#define __swp_type(entry) SWP_TYPE(entry)
+#define __swp_offset(entry) SWP_OFFSET(entry)
+
+#define TIF_SIGPENDING (2)
+
+#define _SECTION_SIZE_BITS 28
+#define _MAX_PHYSMEM_BITS 32
+
+#endif /* ARM */
+
#ifdef X86
#define _32BIT_
#define MACHINE_TYPE "X86"
@@ -2795,6 +2863,10 @@ struct efi_memory_desc_t {
#define SIZEOF_16BIT (2)
#define SIZEOF_8BIT (1)

+#ifdef ARM
+#define MAX_HEXADDR_STRLEN (8)
+#define UVADDR_PRLEN (8)
+#endif
#ifdef X86
#define MAX_HEXADDR_STRLEN (8)
#define UVADDR_PRLEN (8)
@@ -2971,6 +3043,13 @@ struct efi_memory_desc_t {
#define IRQ_AFFINITY_SET
(THIS_KERNEL_VERSION >= LINUX(2,6,21) ? IRQ_AFFINITY_SET_2_6_21 : 0)

+#ifdef ARM
+#define SA_PROBE SA_ONESHOT
+#define SA_SAMPLE_RANDOM SA_RESTART
+#define SA_SHIRQ 0x04000000
+#define SA_RESTORER 0x04000000
+#endif
+
#ifdef X86
#define SA_PROBE SA_ONESHOT
#define SA_SAMPLE_RANDOM SA_RESTART
@@ -3301,6 +3380,9 @@ void program_usage(int);
#define SHORT_FORM (0)
void dump_program_context(void);
void dump_build_data(void);
+#ifdef ARM
+#define machdep_init(X) arm_init(X)
+#endif
#ifdef X86
#define machdep_init(X) x86_init(X)
#endif
@@ -3659,6 +3741,9 @@ void help_init(void);
void cmd_usage(char *, int);
void display_version(void);
void display_help_screen(char *);
+#ifdef ARM
+#define dump_machdep_table(X) arm_dump_machdep_table(X)
+#endif
#ifdef X86
#define dump_machdep_table(X) x86_dump_machdep_table(X)
#endif
@@ -3937,6 +4022,58 @@ void read_in_kernel_config(int);
void dev_init(void);
void dump_dev_table(void);

+#ifdef ARM
+void arm_init(int);
+void arm_dump_machdep_table(ulong);
+int arm_is_vmalloc_addr(ulong);
+void arm_dump_backtrace_entry(struct bt_info *, int, ulong, ulong);
+
+#define display_idt_table()
+ error(FATAL, "-d option is not applicable to ARM architecture
")
+
+struct arm_pt_regs {
+ ulong uregs[18];
+};
+
+#define ARM_cpsr uregs[16]
+#define ARM_pc uregs[15]
+#define ARM_lr uregs[14]
+#define ARM_sp uregs[13]
+#define ARM_ip uregs[12]
+#define ARM_fp uregs[11]
+#define ARM_r10 uregs[10]
+#define ARM_r9 uregs[9]
+#define ARM_r8 uregs[8]
+#define ARM_r7 uregs[7]
+#define ARM_r6 uregs[6]
+#define ARM_r5 uregs[5]
+#define ARM_r4 uregs[4]
+#define ARM_r3 uregs[3]
+#define ARM_r2 uregs[2]
+#define ARM_r1 uregs[1]
+#define ARM_r0 uregs[0]
+#define ARM_ORIG_r0 uregs[17]
+
+#define KSYMS_START (0x1)
+#define PHYS_BASE (0x2)
+
+struct machine_specific {
+ ulong phys_base;
+ ulong vmalloc_start_addr;
+ ulong modules_vaddr;
+ ulong modules_end;
+ ulong kernel_text_start;
+ ulong kernel_text_end;
+ ulong exception_text_start;
+ ulong exception_text_end;
+ ulong crash_task_pid;
+ struct arm_pt_regs *crash_task_regs;
+};
+
+int init_unwind_tables(void);
+void unwind_backtrace(struct bt_info *);
+#endif /* ARM */
+
/*
* alpha.c
*/
diff --git a/diskdump.c b/diskdump.c
index 3aab338..e52e603 100644
--- a/diskdump.c
+++ b/diskdump.c
@@ -528,7 +528,15 @@ page_is_cached(physaddr_t paddr)
static ulong
paddr_to_pfn(physaddr_t paddr)
{
+#ifdef ARM
+ /*
+ * In ARM, PFN 0 means first page in kernel direct-mapped view.
+ * This is also first page in mem_map as well.
+ */
+ return (paddr - machdep->machspec->phys_base) >> dd->block_shift;
+#else
return paddr >> dd->block_shift;
+#endif
}

/*
diff --git a/unwind_arm.c b/unwind_arm.c
new file mode 100644
index 0000000..8352e2d
--- /dev/null
+++ b/unwind_arm.c
@@ -0,0 +1,697 @@
+/*
+ * Stack unwinding support for ARM
+ *
+ * This code is derived from the kernel source:
+ * arch/arm/kernel/unwind.c
+ * Copyright (C) 2008 ARM Limited
+ *
+ * Created by: Mika Westerberg <ext-mika.1.westerberg@nokia.com>
+ * Copyright (C) 2010 Nokia Corporation
+ *
+ * For more information about ARM unwind tables see "Exception handling ABI for
+ * the ARM architecture" document at:
+ *
+ * http://infocenter.arm.com/help/topic/com.arm.doc.subset.swdev.abi/index.html
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ */
+
+#ifdef ARM
+
+#include "defs.h"
+
+/**
+ * struct unwind_idx - index table entry
+ * @addr: prel31 offset to the start of the function
+ * @insn: index table entry.
+ *
+ * @insn can be encoded as follows:
+ * 1. if bit31 is clear this points to the start of the EHT entry
+ * (prel31 offset)
+ * 2. if bit31 is set, this contains the EHT entry itself
+ * 3. if 0x1, cannot unwind.
+ *
+ * In case 1. @insn points to the EH table that comes directly after index
+ * table. This offset is relative to address of @insn which implies that we must
+ * allocate both index table and EH table in single chunk.
+ */
+struct unwind_idx {
+ ulong addr;
+ ulong insn;
+};
+
+/**
+ * struct unwind_table - per-module unwind table
+ * @idx: pointer to the star of the unwind table
+ * @start: pointer to the start of the index table
+ * @end: pointer to the last element +1 of the index table
+ * @begin_addr: start address which this table covers
+ * @end_addr: end address which this table covers
+ *
+ * Kernel stores per-module unwind tables in this format. There can be more than
+ * one table per module as we have different ELF sections in the module.
+ */
+struct unwind_table {
+ struct unwind_idx *idx;
+ struct unwind_idx *start;
+ struct unwind_idx *end;
+ ulong begin_addr;
+ ulong end_addr;
+};
+
+/*
+ * Unwind table pointers to master kernel table and for modules.
+ */
+static struct unwind_table *kernel_unwind_table;
+static struct unwind_table *module_unwind_tables;
+
+struct unwind_ctrl_block {
+ ulong vrs[16];
+ ulong *insn;
+ int entries;
+ int byte;
+};
+
+struct stackframe {
+ ulong fp;
+ ulong sp;
+ ulong lr;
+ ulong pc;
+};
+
+enum regs {
+ FP = 11,
+ SP = 13,
+ LR = 14,
+ PC = 15,
+};
+
+static int init_kernel_unwind_table(void);
+static void free_kernel_unwind_table(void);
+static int read_module_unwind_table(struct unwind_table *, ulong);
+static int init_module_unwind_tables(void);
+static ulong unwind_get_byte(struct unwind_ctrl_block *);
+static ulong get_value_from_stack(ulong *);
+static int unwind_exec_insn(struct unwind_ctrl_block *);
+static int is_core_kernel_text(ulong);
+static struct unwind_idx *search_index(ulong);
+static ulong *prel31_to_addr(ulong *);
+static int unwind_frame(struct stackframe *, ulong);
+
+/*
+ * Function reads in-memory kernel and module unwind tables and makes
+ * local copy of them for unwinding. If unwinding tables cannot be found, this
+ * function returns FALSE, otherwise TRUE.
+ */
+int
+init_unwind_tables(void)
+{
+ if (!symbol_exists("__start_unwind_idx") ||
+ !symbol_exists("__stop_unwind_idx") ||
+ !symbol_exists("__start_unwind_tab") ||
+ !symbol_exists("__stop_unwind_tab") ||
+ !symbol_exists("unwind_tables")) {
+ return FALSE;
+ }
+
+ if (!init_kernel_unwind_table()) {
+ error(WARNING,
+ "UNWIND: failed to initialize kernel unwind table
");
+ return FALSE;
+ }
+
+ /*
+ * Initialize symbols for per-module unwind tables. Actually there are
+ * several tables per module (one per code section).
+ */
+ STRUCT_SIZE_INIT(unwind_table, "unwind_table");
+ MEMBER_OFFSET_INIT(unwind_table_list, "unwind_table", "list");
+ MEMBER_OFFSET_INIT(unwind_table_start, "unwind_table", "start");
+ MEMBER_OFFSET_INIT(unwind_table_stop, "unwind_table", "stop");
+ MEMBER_OFFSET_INIT(unwind_table_begin_addr, "unwind_table",
+ "begin_addr");
+ MEMBER_OFFSET_INIT(unwind_table_end_addr, "unwind_table", "end_addr");
+
+ STRUCT_SIZE_INIT(unwind_idx, "unwind_idx");
+ MEMBER_OFFSET_INIT(unwind_idx_addr, "unwind_idx", "addr");
+ MEMBER_OFFSET_INIT(unwind_idx_insn, "unwind_idx", "insn");
+
+ if (!init_module_unwind_tables()) {
+ error(WARNING,
+ "UNWIND: failed to initialize module unwind tables
");
+ free_kernel_unwind_table();
+ return FALSE;
+ }
+
+ /*
+ * We abuse DWARF_UNWIND flag a little here as ARM unwinding tables are
+ * not in DWARF format but we can use the flags to indicate that we have
+ * unwind tables support ready.
+ */
+ kt->flags |= DWARF_UNWIND_CAPABLE;
+ kt->flags |= DWARF_UNWIND;
+
+ return TRUE;
+}
+
+/*
+ * Allocate and fill master kernel unwind table.
+ */
+static int
+init_kernel_unwind_table(void)
+{
+ ulong idx_start, idx_end, idx_size;
+ ulong tab_end, tab_size;
+
+ kernel_unwind_table = calloc(sizeof(*kernel_unwind_table), 1);
+ if (!kernel_unwind_table)
+ return FALSE;
+
+ idx_start = symbol_value("__start_unwind_idx");
+ idx_end = symbol_value("__stop_unwind_idx");
+ tab_end = symbol_value("__stop_unwind_tab");
+
+ /*
+ * Calculate sizes of the idx table and the EH table.
+ */
+ idx_size = idx_end - idx_start;
+ tab_size = tab_end - idx_start;
+
+ kernel_unwind_table->idx = calloc(tab_size, 1);
+ if (!kernel_unwind_table->idx)
+ goto fail;
+
+ /*
+ * Now read in both the index table and the EH table. We need to read in
+ * both because prel31 offsets in the index table are relative to the
+ * index address.
+ */
+ if (!readmem(idx_start, KVADDR, kernel_unwind_table->idx, tab_size,
+ "master kernel unwind table", RETURN_ON_ERROR))
+ goto fail;
+
+ kernel_unwind_table->start = kernel_unwind_table->idx;
+ kernel_unwind_table->end = (struct unwind_idx *)
+ ((char *)kernel_unwind_table->idx + idx_size);
+ kernel_unwind_table->begin_addr = kernel_unwind_table->start->addr;
+ kernel_unwind_table->end_addr = (kernel_unwind_table->end - 1)->addr;
+
+ if (CRASHDEBUG(1)) {
+ fprintf(fp, "UNWIND: master kernel table start
");
+ fprintf(fp, "UNWIND: size : %ld
", tab_size);
+ fprintf(fp, "UNWIND: start : %p
", kernel_unwind_table->start);
+ fprintf(fp, "UNWIND: end : %p
", kernel_unwind_table->end);
+ fprintf(fp, "UNWIND: begin_addr: 0x%lx
",
+ kernel_unwind_table->begin_a
 

Thread Tools




All times are GMT. The time now is 02:34 AM.

VBulletin, Copyright ©2000 - 2014, Jelsoft Enterprises Ltd.
Content Relevant URLs by vBSEO ©2007, Crawlability, Inc.
Copyright ©2007 - 2008, www.linux-archive.org