[PATCH v2 5/6] crash/kdump: add ARM support

Mika Westerberg ext-mika.1.westerberg at nokia.com
Thu Aug 26 08:02:31 EDT 2010


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 at sonyericsson.com>
Signed-off-by: Thomas Fänge <thomas.fange at sonyericsson.com>
Signed-off-by: Mika Westerberg <ext-mika.1.westerberg at nokia.com>
---
 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




More information about the linux-arm-kernel mailing list