This patch adds ARM support for kdump files (ELF files readable from /proc/vmcore). It also provides a function which tries to determine correct phys_base based on the program headers in the ELF file. Signed-off-by: Jan Karlsson <jan.karlsson@xxxxxxxxxxxxxxxx> Signed-off-by: Thomas Fänge <thomas.fange@xxxxxxxxxxxxxxxx> Signed-off-by: Mika Westerberg <ext-mika.1.westerberg@xxxxxxxxx> --- defs.h | 3 ++ netdump.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+), 0 deletions(-) diff --git a/defs.h b/defs.h index 3d34b8e..c8bae00 100755 --- a/defs.h +++ b/defs.h @@ -4353,6 +4353,9 @@ int xen_minor_version(void); int get_netdump_arch(void); void *get_regs_from_elf_notes(struct task_context *); void map_cpus_to_prstatus(void); +#ifdef ARM +int kdump_phys_base(ulong *); +#endif /* * diskdump.c diff --git a/netdump.c b/netdump.c index 3a5db13..3bf6e3b 100644 --- a/netdump.c +++ b/netdump.c @@ -32,6 +32,7 @@ static void dump_Elf64_Ehdr(Elf64_Ehdr *); static void dump_Elf64_Phdr(Elf64_Phdr *, int); static size_t dump_Elf64_Nhdr(Elf64_Off offset, int); static void get_netdump_regs_ppc64(struct bt_info *, ulong *, ulong *); +static void get_netdump_regs_arm(struct bt_info *, ulong *, ulong *); static physaddr_t xen_kdump_p2m(physaddr_t); static void check_dumpfile_size(char *); @@ -170,6 +171,12 @@ is_netdump(char *file, ulong source_query) goto bailout; break; + case EM_ARM: + if (machine_type_mismatch(file, "ARM", NULL, + source_query)) + goto bailout; + break; + default: if (machine_type_mismatch(file, "(unknown)", NULL, source_query)) @@ -2094,6 +2101,10 @@ get_netdump_regs(struct bt_info *bt, ulong *eip, ulong *esp) case EM_S390: machdep->get_stack_frame(bt, eip, esp); break; + + case EM_ARM: + return get_netdump_regs_arm(bt, eip, esp); + break; default: error(FATAL, "support for ELF machine type %d not available\n", @@ -2393,6 +2404,40 @@ get_netdump_regs_ppc64(struct bt_info *bt, ulong *eip, ulong *esp) machdep->get_stack_frame(bt, eip, esp); } +static void +get_netdump_regs_arm(struct bt_info *bt, ulong *eip, ulong *esp) +{ + Elf64_Nhdr *note; + size_t len; + + if ((bt->task == tt->panic_task) || + (is_task_active(bt->task) && nd->num_prstatus_notes > 1)) { + /* + * Registers are saved during the dump process for the + * panic task. Whereas in kdump, regs are captured for all + * CPUs if they responded to an IPI. + */ + if (nd->num_prstatus_notes > 1) { + if (!nd->nt_prstatus_percpu[bt->tc->processor]) + error(FATAL, + "cannot determine NT_PRSTATUS ELF note " + "for %s task: %lx\n", + (bt->task == tt->panic_task) ? + "panic" : "active", bt->task); + note = (Elf64_Nhdr *) + nd->nt_prstatus_percpu[bt->tc->processor]; + } else + note = (Elf64_Nhdr *)nd->nt_prstatus; + + len = sizeof(Elf64_Nhdr); + len = roundup(len + note->n_namesz, 4); + bt->machdep = (void *)((char *)note + len + + MEMBER_OFFSET("elf_prstatus", "pr_reg")); + } + + machdep->get_stack_frame(bt, eip, esp); +} + int is_partial_netdump(void) { @@ -2644,6 +2689,7 @@ xen_minor_version(void) static void *get_ppc64_regs_from_elf_notes(struct task_context *); static void *get_x86_regs_from_elf_notes(struct task_context *); static void *get_x86_64_regs_from_elf_notes(struct task_context *); +static void *get_arm_regs_from_elf_notes(struct task_context *); int get_netdump_arch(void) { @@ -2670,6 +2716,8 @@ get_regs_from_elf_notes(struct task_context *tc) return get_ppc64_regs_from_elf_notes(tc); case EM_X86_64: return get_x86_64_regs_from_elf_notes(tc); + case EM_ARM: + return get_arm_regs_from_elf_notes(tc); default: error(FATAL, "support for ELF machine type %d not available\n", @@ -2786,3 +2834,70 @@ get_ppc64_regs_from_elf_notes(struct task_context *tc) return pt_regs; } + +#ifdef ARM +/* + * In case of ARM we need to determine correct PHYS_OFFSET from the kdump file. + * This is done by taking lowest physical address (LMA) from given load + * segments. Normally this is the right one. + * + * Alternative would be to store phys_base in VMCOREINFO but current kernel + * kdump doesn't do that yet. + */ +int kdump_phys_base(ulong *phys_base) +{ + struct pt_load_segment *pls; + ulong paddr = ULONG_MAX; + int i; + + for (i = 0; i < nd->num_pt_load_segments; i++) { + pls = &nd->pt_load_segments[i]; + if (pls->phys_start < paddr) + paddr = pls->phys_start; + } + + if (paddr != ULONG_MAX) { + *phys_base = paddr; + return TRUE; + } + return FALSE; +} +#endif /* ARM */ + +static void * +get_arm_regs_from_elf_notes(struct task_context *tc) +{ + Elf32_Nhdr *note_32; + Elf64_Nhdr *note_64; + void *note; + size_t len; + void *pt_regs; + + len = 0; + pt_regs = NULL; + + if ((tc->task == tt->panic_task) || + (is_task_active(tc->task) && (nd->num_prstatus_notes > 1))) { + if (nd->num_prstatus_notes > 1) + note = (void *) + nd->nt_prstatus_percpu[tc->processor]; + else + note = (void *)nd->nt_prstatus; + if (nd->elf32) { + note_32 = (Elf32_Nhdr *)note; + len = sizeof(Elf32_Nhdr); + len = roundup(len + note_32->n_namesz, 4); + } else if (nd->elf64) { + note_64 = (Elf64_Nhdr *)note; + len = sizeof(Elf64_Nhdr); + len = roundup(len + note_64->n_namesz, 4); + } + + pt_regs = (void *)((char *)note + len + + MEMBER_OFFSET("elf_prstatus", "pr_reg")); + } else + error(FATAL, + "cannot determine arm register set for task \"%s\"\n", + tc->comm); + return pt_regs; +} -- 1.5.6.5 -- Crash-utility mailing list Crash-utility@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/crash-utility