Add ELF core dump support for s390x

This patch enables crash for reading s390x (64 bit) ELF core dumps. The
following new ELF note sections are added by this patch:

* NT_FPREGSET:     Floating point registers - all architectures
* NT_S390_TIMER:   S390 CPU timer
* NT_S390_TODCMP:  S390 TOD clock comparator
* NT_S390_TODPREG: S390 TOD programmable register
* NT_S390_CTRS:    S390 control registers
* NT_S390_PREFIX:  S390 prefix register

The mapping of a s390 CPU to the correct note number is done via the prefix
register of the CPUs. This is implemented in s390x_get_note_nr(). The
function gets the prefix register for the logical CPU number from the lowcore
pointer array (global variable lowcore_ptr) and searches the matching prefix
register note.

If CPU information is found in the dump for a running task, it is converted
into a generic s390x_cpu structure which is then attached to "bt->machdep".
If "bt->machdep" is set, the s390x "machdep->get_stack_frame()" function uses
that register information.
---
 defs.h    |   66 ++++++++++++++++++++++++
 netdump.c |  164 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 netdump.h |    6 ++
 s390x.c   |   34 ++++++++++--
 symbols.c |    2 
 5 files changed, 264 insertions(+), 8 deletions(-)

--- a/defs.h
+++ b/defs.h
@@ -2747,6 +2747,71 @@ struct efi_memory_desc_t {
 #define _SECTION_SIZE_BITS	28
 #define _MAX_PHYSMEM_BITS	42
 
+/*
+ * S390 CPU timer ELF note
+ */
+#ifndef NT_S390_TIMER
+#define NT_S390_TIMER 0x301
+#endif
+
+/*
+ * S390 TOD clock comparator ELF note
+ */
+#ifndef NT_S390_TODCMP
+#define NT_S390_TODCMP 0x302
+#endif
+
+/*
+ * S390 TOD programmable register ELF note
+ */
+#ifndef NT_S390_TODPREG
+#define NT_S390_TODPREG 0x303
+#endif
+
+/*
+ * S390 control registers ELF note
+ */
+#ifndef NT_S390_CTRS
+#define NT_S390_CTRS 0x304
+#endif
+
+/*
+ * S390 prefix ELF note
+ */
+#ifndef NT_S390_PREFIX
+#define NT_S390_PREFIX 0x305
+#endif
+
+/*
+ * S390 floating point register ELF Note
+ */
+#ifndef NT_FPREGSET
+#define NT_FPREGSET 0x2
+#endif
+
+struct nt_s390_fpregset_64 {
+	uint32_t	fpc;
+	uint32_t	pad;
+	uint64_t	fprs[16];
+} __attribute__ ((packed));
+
+/*
+ * s390x CPU info
+ */
+struct s390x_cpu
+{
+	uint64_t	gprs[16];
+	uint64_t	ctrs[16];
+	uint32_t	acrs[16];
+	uint64_t	fprs[16];
+	uint32_t	fpc;
+	uint64_t	psw[2];
+	uint32_t	prefix;
+	uint64_t	timer;
+	uint64_t	todcmp;
+	uint32_t	todpreg;
+};
+
 #endif  /* S390X */
 
 #ifdef PLATFORM
@@ -4196,6 +4261,7 @@ void get_netdump_regs(struct bt_info *, 
 int is_partial_netdump(void);
 void get_netdump_regs_x86(struct bt_info *, ulong *, ulong *);
 void get_netdump_regs_x86_64(struct bt_info *, ulong *, ulong *);
+void get_netdump_regs_s390x(struct bt_info *, ulong *, ulong *);
 struct vmcore_data;
 struct vmcore_data *get_kdump_vmcore_data(void);
 int read_kdump(int, void *, int, ulong, physaddr_t);
--- a/netdump.c
+++ b/netdump.c
@@ -213,6 +213,11 @@ is_netdump(char *file, ulong source_quer
 				goto bailout;
 			break;
 
+		case EM_S390:
+			if (machine_type_mismatch(file, "S390X", NULL,
+			    source_query))
+				goto bailout;
+			break;
 		case EM_386:
 			if (machine_type_mismatch(file, "X86", NULL,
 			    source_query))
@@ -1858,6 +1863,72 @@ dump_Elf64_Nhdr(Elf64_Off offset, int st
 			}
 		}
 		break;
+	case NT_FPREGSET:
+		netdump_print("(NT_FPREGSET)");
+		if (store) {
+			for (i = 0; i < NR_CPUS; i++) {
+				if (nd->nt_fpregset_percpu[i])
+					continue;
+				nd->nt_fpregset_percpu[i] = (void *)note;
+				break;
+			}
+		}
+		break;
+	case NT_S390_TIMER:
+		netdump_print("(NT_S390_TIMER)\n");
+		if (store) {
+			for (i = 0; i < NR_CPUS; i++) {
+				if (nd->nt_s390_timer_percpu[i])
+					continue;
+				nd->nt_s390_timer_percpu[i] = (void *)note;
+				break;
+			}
+		}
+		break;
+	case NT_S390_TODCMP:
+		netdump_print("(NT_S390_TODCMP)\n");
+		if (store) {
+			for (i = 0; i < NR_CPUS; i++) {
+				if (nd->nt_s390_todcmp_percpu[i])
+					continue;
+				nd->nt_s390_todcmp_percpu[i] = (void *)note;
+				break;
+			}
+		}
+		break;
+	case NT_S390_TODPREG:
+		netdump_print("(NT_S390_TODPREG)\n");
+		if (store) {
+			for (i = 0; i < NR_CPUS; i++) {
+				if (nd->nt_s390_todpreg_percpu[i])
+					continue;
+				nd->nt_s390_todpreg_percpu[i] = (void *)note;
+				break;
+			}
+		}
+		break;
+	case NT_S390_CTRS:
+		netdump_print("(NT_S390_CTRS)\n");
+		if (store) {
+			for (i = 0; i < NR_CPUS; i++) {
+				if (nd->nt_s390_ctrs_percpu[i])
+					continue;
+				nd->nt_s390_ctrs_percpu[i] = (void *)note;
+				break;
+			}
+		}
+		break;
+	case NT_S390_PREFIX:
+		netdump_print("(NT_S390_PREFIX)\n");
+		if (store) {
+			for (i = 0; i < NR_CPUS; i++) {
+				if (nd->nt_s390_prefix_percpu[i])
+					continue;
+				nd->nt_s390_prefix_percpu[i] = (void *)note;
+				break;
+			}
+		}
+		break;
 	case NT_PRPSINFO:
 		netdump_print("(NT_PRPSINFO)\n");
 		if (store)
@@ -2082,6 +2153,9 @@ get_netdump_regs(struct bt_info *bt, ulo
 		return get_netdump_regs_x86_64(bt, eip, esp);
 		break;
 
+	case EM_S390:
+		get_netdump_regs_s390x(bt, eip, esp);
+		break;
 	default:
 		error(FATAL, 
 		   "support for ELF machine type %d not available\n",
@@ -2381,6 +2455,96 @@ get_netdump_regs_ppc64(struct bt_info *b
 	machdep->get_stack_frame(bt, eip, esp);
 }
 
+static void *
+get_note_desc(Elf64_Nhdr *note)
+{
+	void *ptr = note;
+
+	return ptr + roundup(sizeof(*note) + note->n_namesz, 4);
+}
+
+static int
+s390x_get_note_nr(struct bt_info *bt)
+{
+	unsigned int cpu = bt->tc->processor;
+	unsigned long lowcore_ptr, prefix;
+	uint32_t *nt_prefix;
+	unsigned int i;
+
+	lowcore_ptr = symbol_value("lowcore_ptr");
+	readmem(lowcore_ptr + cpu * sizeof(long), KVADDR,
+		&prefix, sizeof(long), "lowcore_ptr", FAULT_ON_ERROR);
+	for (i = 0; i < nd->num_prstatus_notes; i++) {
+		nt_prefix = get_note_desc(nd->nt_s390_prefix_percpu[i]);
+		if (*nt_prefix == prefix)
+			return i;
+	}
+	error(FATAL, "cannot determine NT_PRSTATUS ELF note for task: %lx\n",
+	      bt->task);
+}
+
+static void
+s390x_note_to_s390x_cpu(struct s390x_cpu *s390x_cpu, int note_nr)
+{
+	struct nt_s390_fpregset_64 *nt_s390_fpregset;
+	Elf64_Nhdr *note;
+	char *ptr;
+
+	/* Copy prstatus note */
+	note = (Elf64_Nhdr *) nd->nt_prstatus_percpu[note_nr];
+	ptr = get_note_desc(note) + MEMBER_OFFSET("elf_prstatus", "pr_reg");
+	memcpy(&s390x_cpu->psw, ptr, sizeof(s390x_cpu->psw));
+	memcpy(&s390x_cpu->gprs, ptr + 16, sizeof(s390x_cpu->gprs));
+	memcpy(&s390x_cpu->acrs, ptr + 16 + 16 * 8, sizeof(s390x_cpu->acrs));
+
+	/* Copy s390 floating point register note */
+	nt_s390_fpregset = get_note_desc(nd->nt_fpregset_percpu[note_nr]);
+	memcpy(&s390x_cpu->fpc, &nt_s390_fpregset->fpc,
+	       sizeof(s390x_cpu->fpc));
+	memcpy(&s390x_cpu->fprs, &nt_s390_fpregset->fprs,
+	       sizeof(s390x_cpu->fprs));
+
+	/* Copy s390 additional notes */
+	memcpy(&s390x_cpu->timer,
+	       get_note_desc(nd->nt_s390_timer_percpu[note_nr]),
+	       sizeof(s390x_cpu->timer));
+	memcpy(&s390x_cpu->todcmp,
+	       get_note_desc(nd->nt_s390_todcmp_percpu[note_nr]),
+	       sizeof(s390x_cpu->todcmp));
+	memcpy(&s390x_cpu->todpreg,
+	       get_note_desc(nd->nt_s390_todpreg_percpu[note_nr]),
+	       sizeof(s390x_cpu->todpreg));
+	memcpy(&s390x_cpu->ctrs,
+	       get_note_desc(nd->nt_s390_ctrs_percpu[note_nr]),
+	       sizeof(s390x_cpu->ctrs));
+	memcpy(&s390x_cpu->prefix,
+	       get_note_desc(nd->nt_s390_prefix_percpu[note_nr]),
+	       sizeof(s390x_cpu->prefix));
+}
+
+void
+get_netdump_regs_s390x(struct bt_info *bt, ulong *eip, ulong *esp)
+{
+	static struct s390x_cpu s390x_cpu;
+	unsigned int note_nr;
+	Elf64_Nhdr *note;
+
+	bt->machdep = NULL;
+	if (nd->num_prstatus_notes == 0)
+		goto out;
+	if (!(is_task_active(bt->task) &&
+	      (kt->cpu_flags[bt->tc->processor] & ONLINE)))
+		goto out;
+	note_nr = s390x_get_note_nr(bt);
+	if (!nd->nt_prstatus_percpu[note_nr])
+		error(FATAL, "cannot determine NT_PRSTATUS ELF note for "
+		      "task: %lx\n", bt->task);
+	s390x_note_to_s390x_cpu(&s390x_cpu, note_nr);
+	bt->machdep = &s390x_cpu;
+out:
+	machdep->get_stack_frame(bt, eip, esp);
+}
+
 int 
 is_partial_netdump(void)
 {
--- a/netdump.h
+++ b/netdump.h
@@ -68,6 +68,12 @@ struct vmcore_data {
 	ulong switch_stack;
 	uint num_prstatus_notes;
 	void *nt_prstatus_percpu[NR_CPUS];
+	void *nt_fpregset_percpu[NR_CPUS];
+	void *nt_s390_timer_percpu[NR_CPUS];
+	void *nt_s390_todcmp_percpu[NR_CPUS];
+	void *nt_s390_todpreg_percpu[NR_CPUS];
+	void *nt_s390_ctrs_percpu[NR_CPUS];
+	void *nt_s390_prefix_percpu[NR_CPUS];
 	struct xen_kdump_data *xen_kdump_data;
 	void *vmcoreinfo;
 	uint size_vmcoreinfo;
--- a/s390x.c
+++ b/s390x.c
@@ -56,7 +56,6 @@ static ulong s390x_processor_speed(void)
 static int s390x_eframe_search(struct bt_info *);
 static void s390x_back_trace_cmd(struct bt_info *);
 static void s390x_dump_irq(int);
-static void s390x_get_stack_frame(struct bt_info *, ulong *, ulong *);
 static int s390x_dis_filter(ulong, char *);
 static void s390x_cmd_mach(void);
 static int s390x_get_smp_cpus(void);
@@ -64,6 +63,7 @@ static void s390x_display_machine_stats(
 static void s390x_dump_line_number(ulong);
 static struct line_number_hook s390x_line_number_hooks[];
 static int s390x_is_uvaddr(ulong, struct task_context *);
+static void s390x_get_stack_frame(struct bt_info *, ulong *, ulong *);
 
 
 /*
@@ -571,15 +571,36 @@ s390x_has_cpu(struct bt_info *bt)
  * read lowcore for cpu
  */
 static void
-s390x_get_lowcore(int cpu, char* lowcore)
+s390x_get_lowcore(struct bt_info *bt, char* lowcore)
 {
 	unsigned long lowcore_array,lowcore_ptr;
+	struct s390x_cpu *s390x_cpu;
+	int cpu = bt->tc->processor;
 
 	lowcore_array = symbol_value("lowcore_ptr");
 	readmem(lowcore_array + cpu * S390X_WORD_SIZE,KVADDR,
-		&lowcore_ptr, sizeof(long), "lowcore_ptr", FAULT_ON_ERROR);
-	readmem(lowcore_ptr, KVADDR, lowcore, LOWCORE_SIZE, "lowcore", 
+		&lowcore_ptr, sizeof(long), "lowcore_ptr",
 		FAULT_ON_ERROR);
+	readmem(lowcore_ptr, KVADDR, lowcore, LOWCORE_SIZE, "lowcore", FAULT_ON_ERROR);
+
+	if (!bt->machdep)
+		return;
+
+	s390x_cpu = bt->machdep;
+	/* Copy ELF register information to defined places in lowcore */
+	memcpy(lowcore + 4864, &s390x_cpu->psw, sizeof(s390x_cpu->psw));
+	memcpy(lowcore + 4736, &s390x_cpu->gprs, sizeof(s390x_cpu->gprs));
+	memcpy(lowcore + 4928, &s390x_cpu->acrs, sizeof(s390x_cpu->acrs));
+
+	memcpy(lowcore + 4892, &s390x_cpu->fpc, sizeof(s390x_cpu->fpc));
+	memcpy(lowcore + 4608, &s390x_cpu->fprs, sizeof(s390x_cpu->fprs));
+
+	memcpy(lowcore + 4888, &s390x_cpu->prefix, sizeof(s390x_cpu->prefix));
+	memcpy(lowcore + 4992, &s390x_cpu->ctrs, sizeof(s390x_cpu->ctrs));
+
+	memcpy(lowcore + 4900, &s390x_cpu->todpreg, sizeof(s390x_cpu->todpreg));
+	memcpy(lowcore + 4904, &s390x_cpu->timer, sizeof(s390x_cpu->timer));
+	memcpy(lowcore + 4912, &s390x_cpu->todcmp, sizeof(s390x_cpu->todcmp));
 }
 
 /*
@@ -627,7 +648,7 @@ s390x_back_trace_cmd(struct bt_info *bt)
 			fprintf(fp,"(active)\n");
 			return;
 		}
-		s390x_get_lowcore(cpu,lowcore);
+		s390x_get_lowcore(bt, lowcore);
 		psw_flags = ULONG(lowcore + OFFSET(s390_lowcore_psw_save_area));
 
 		if(psw_flags & 0x1000000000000ULL){
@@ -908,7 +929,7 @@ s390x_get_stack_frame(struct bt_info *bt
 	char lowcore[LOWCORE_SIZE];
 
 	if(s390x_has_cpu(bt))
-		s390x_get_lowcore(s390x_cpu_of_task(bt->task),lowcore);
+		s390x_get_lowcore(bt, lowcore);
 
 	/* get the stack pointer */
 	if(esp){
@@ -1139,5 +1160,4 @@ try_closest:
 		}
 	}
 }
-
 #endif 
--- a/symbols.c
+++ b/symbols.c
@@ -2710,7 +2710,7 @@ is_shared_object(char *file)
 			break;
 
 		case EM_S390:
-			if (machine_type("S390"))
+			if (machine_type("S390X"))
 				return TRUE;
 			break;
 		}
