[PATCH] crash: s390x: Add vector register support

This patch adds support for the new s390x vector registers.
For ELF dumps the registers are taken from the VX ELF notes, for
s390 dumps the registers are taken from memory. The kernel stores
a pointer the save area in the CPU lowcores at offset 0x11b0.

This patch also adds the new -A option to the "bt" command. This 
option produces almost the same output as the -a option, but in
addition also the new vector registers for all active tasks
are shown. This is done because for normal debugging using "bt -a"
we do not want to pollute the bt output with the large vector register
output (512 byte).

The following shows an output example:

crash> bt -A
PID: 2387   TASK: 1785a5e8          CPU: 0   COMMAND: "bash"
 LOWCORE INFO:
  -psw      : 0x0400d00180000000 0x0000000000112aa0
  -function : store_status at 112aa0
  -prefix   : 0x1fffc000
  -cpu timer: 0x7ffffff3 0x0066ef81
  -clock cmp: 0x0066ef81 0000000000
  -general registers:
     000000000000000000 0x0400c00180000000
   ....
  - vector registers:
     0x404b000000000000 0x0000000000000000 
     0x0000000000000000 0x0000000000000000 
     0x404b000000000000 0x0000000000000000 
     0x0000000000000000 0x0000000000000000 
     0x0000000000000000 0x0000000000000000 
     0x0000000000000000 0x0000000000000000 
     0x0000000000000000 0x0000000000000000 
     0x0000000000000000 0x0000000000000000 
     0x0000000000000000 0x0000000000000000 
     0x0000000000000000 0x0000000000000000 
     0x0000000000000000 0x0000000000000000 
     0x0000000000000000 0x0000000000000000 
     0x0000000000000000 0x0000000000000000 
     0x0000000000000000 0x0000000000000000 
     0x0000000000000000 0x0000000000000000 
     0x0000000000000000 0x0000000000000000 
     0x0000000000000000 0x0000000000000000 
     0x0000000000000000 0x0000000000000000 
     0x0000000000000000 0x0000000000000000 
     0x0000000000000000 0x0000000000000000 
     0x0000000000000000 0x0000000000000000 
     0x0000000000000000 0x0000000000000000 
     0x0000000000000000 0x0000000000000000 
     0x0000000000000000 0x0000000000000000 
     0x0000000000000000 0x0000000000000000 
     0x0000000000000000 0x0000000000000000 
     0x0000000000000000 0x0000000000000000 
     0x0000000000000000 0x0000000000000000 
     0x0000000000000000 0x0000000000000000 
     0x0000000000000000 0x0000000000000000 
     0x0000000000000000 0x0000000000000000 
     0x0000000000000000 0x0000000000000000

Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
---
 defs.h    |    1 
 help.c    |    1 
 kernel.c  |    5 +--
 netdump.c |    6 +++
 netdump.h |   14 ++++++++
 s390x.c   |  100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 125 insertions(+), 2 deletions(-)

--- a/defs.h
+++ b/defs.h
@@ -4966,6 +4966,7 @@ ulong cpu_map_addr(const char *type);
 #define BT_FULL_SYM_SLAB2  (0x400000000000ULL)
 #define BT_EFRAME_TARGET   (0x800000000000ULL)
 #define BT_CPUMASK        (0x1000000000000ULL)
+#define BT_SHOW_ALL_REGS  (0x2000000000000ULL)
 #define BT_SYMBOL_OFFSET   (BT_SYMBOLIC_ARGS)
 
 #define BT_REF_HEXVAL         (0x1)
--- a/help.c
+++ b/help.c
@@ -1742,6 +1742,7 @@ char *help_bt[] = {
 "  trace of the current context will be displayed.\n",
 "       -a  displays the stack traces of the active task on each CPU.",
 "           (only applicable to crash dumps)",
+"       -A  same as -a, but also displays vector registers (S390X only).",
 "   -c cpu  display the stack trace of the active task on one or more CPUs,",
 "           which can be specified using the format \"3\", \"1,8,9\", \"1-23\",",
 "           or \"1,8,9-14\". (only applicable to crash dumps)",
--- a/kernel.c
+++ b/kernel.c
@@ -2003,13 +2003,12 @@ cmd_bt(void)
 	if (kt->flags & USE_OLD_BT)
 		bt->flags |= BT_OLD_BACK_TRACE;
 
-        while ((c = getopt(argcnt, args, "D:fFI:S:c:aloreEgstTdxR:O")) != EOF) {
+	while ((c = getopt(argcnt, args, "D:fFI:S:c:aAloreEgstTdxR:O")) != EOF) {
                 switch (c)
 		{
 		case 'f':
 			bt->flags |= BT_FULL;
 			break;
-
 		case 'F':
 			if (bt->flags & BT_FULL_SYM_SLAB)
 				bt->flags |= BT_FULL_SYM_SLAB2;
@@ -2158,6 +2157,8 @@ cmd_bt(void)
 			}
 			break;
 
+		case 'A':
+			bt->flags |= BT_SHOW_ALL_REGS; /* FALLTHROUGH */
 		case 'a':
 			active++;
 			break;
--- a/netdump.c
+++ b/netdump.c
@@ -2086,6 +2086,12 @@ dump_Elf64_Nhdr(Elf64_Off offset, int st
 	case NT_S390_PREFIX:
 		netdump_print("(NT_S390_PREFIX)\n");
 		break;
+	case NT_S390_VXRS_LOW:
+		netdump_print("(NT_S390_VXRS_LOW)\n");
+		break;
+	case NT_S390_VXRS_HIGH:
+		netdump_print("(NT_S390_VXRS_HIGH)\n");
+		break;
 	case NT_TASKSTRUCT:
 		netdump_print("(NT_TASKSTRUCT)\n");
 		if (STRNEQ(buf, "SNAP"))
--- a/netdump.h
+++ b/netdump.h
@@ -169,6 +169,20 @@ struct xen_kdump_data {
 #define NT_S390_PREFIX 0x305
 #endif
 
+/*
+ * S390 vector registers 0-15 upper half note (16 * u64)
+ */
+#ifndef NT_S390_VXRS_LOW
+#define NT_S390_VXRS_LOW 0x309
+#endif
+
+/*
+ * S390 vector registers 16-31 note (16 * u128)
+ */
+#ifndef NT_S390_VXRS_HIGH
+#define NT_S390_VXRS_HIGH 0x30a
+#endif
+
 #define MAX_KCORE_ELF_HEADER_SIZE (32768)
 
 struct proc_kcore_data {
--- a/s390x.c
+++ b/s390x.c
@@ -41,6 +41,7 @@
 #define KERNEL_STACK_SIZE STACKSIZE() // can be 8192 or 16384
 
 #define LOWCORE_SIZE 8192
+#define VX_SA_SIZE (32 * 16)
 
 #define S390X_PSW_MASK_PSTATE	0x0001000000000000UL
 
@@ -72,6 +73,11 @@ struct s390x_nt_fpregset {
 	uint64_t	fprs[16];
 } __attribute__ ((packed));
 
+struct s390x_vxrs {
+	uint64_t	low;
+	uint64_t	high;
+} __attribute__ ((packed));
+
 /*
  * s390x CPU info
  */
@@ -87,6 +93,8 @@ struct s390x_cpu
 	uint64_t	timer;
 	uint64_t	todcmp;
 	uint32_t	todpreg;
+	uint64_t		vxrs_low[16];
+	struct s390x_vxrs	vxrs_high[16];
 };
 
 /*
@@ -133,6 +141,27 @@ static unsigned long readmem_ul(unsigned
 }
 
 /*
+ * Print hex data
+ */
+static void print_hex_buf(void *buf, int len, int cols, char *tag)
+{
+	int j, first = 1;
+
+	for (j = 0; j < len; j += 8) {
+		if (j % (cols * 8) == 0) {
+			if (first)
+				first = 0;
+			else
+				fprintf(fp, "\n");
+			fprintf(fp, "%s", tag);
+		}
+		fprintf(fp, "%#018lx ", *((unsigned long *)(buf + j)));
+	}
+	if (len)
+		fprintf(fp, "\n");
+}
+
+/*
  * Initialize member offsets
  */
 static void s390x_offsets_init(void)
@@ -271,6 +300,16 @@ static void s390x_elf_nt_prefix_add(stru
 	memcpy(&cpu->prefix, desc, sizeof(cpu->prefix));
 }
 
+static void s390x_elf_nt_vxrs_low_add(struct s390x_cpu *cpu, void *desc)
+{
+	memcpy(&cpu->vxrs_low, desc, sizeof(cpu->vxrs_low));
+}
+
+static void s390x_elf_nt_vxrs_high_add(struct s390x_cpu *cpu, void *desc)
+{
+	memcpy(&cpu->vxrs_high, desc, sizeof(cpu->vxrs_high));
+}
+
 static void *get_elf_note_desc(Elf64_Nhdr *note)
 {
 	void *ptr = note;
@@ -315,6 +354,12 @@ static void s390x_elf_note_add(int elf_c
 	case NT_S390_PREFIX:
 		s390x_elf_nt_prefix_add(cpu, desc);
 		break;
+	case NT_S390_VXRS_LOW:
+		s390x_elf_nt_vxrs_low_add(cpu, desc);
+		break;
+	case NT_S390_VXRS_HIGH:
+		s390x_elf_nt_vxrs_high_add(cpu, desc);
+		break;
 	}
 }
 
@@ -916,6 +961,59 @@ s390x_get_lowcore(struct bt_info *bt, ch
 }
 
 /*
+ * Copy VX registers out of s390x cpu
+ */
+static void vx_copy(void *buf, struct s390x_cpu *s390x_cpu)
+{
+	char *_buf = buf;
+	int i;
+
+	for (i = 0; i < 16; i++) {
+		memcpy(&_buf[i * 16], &s390x_cpu->fprs[i], 8);
+		memcpy(&_buf[i * 16 + 8], &s390x_cpu->vxrs_low[i], 8);
+	}
+	memcpy(&_buf[16 * 16], &s390x_cpu->vxrs_high[0], 16 * 16);
+}
+
+/*
+ * Check if VX registers are available
+ */
+static int has_vx_regs(char *lowcore)
+{
+	unsigned long addr = *((uint64_t *)(lowcore + 0x11b0));
+
+	if (addr == 0 || addr % 1024)
+		return 0;
+	return 1;
+}
+
+/*
+ * Print vector registers for cpu
+ */
+static void
+s390x_print_vx_sa(struct bt_info *bt, char *lc)
+{
+	char vx_sa[VX_SA_SIZE];
+	uint64_t addr;
+
+	if (!(bt->flags & BT_SHOW_ALL_REGS))
+		return;
+	if (!has_vx_regs(lc))
+		return;
+	if (!s390x_cpu_vec) {
+		/* Pointer to save area */
+		addr = *((uint64_t *)(lc + 0x11b0));
+		readmem(addr, KVADDR, vx_sa, sizeof(vx_sa), "vx_sa",
+			FAULT_ON_ERROR);
+	} else {
+		/* Get data from s390x cpu */
+		vx_copy(vx_sa, s390x_cpu_get(bt));
+	}
+	fprintf(fp, "  -vector registers:\n");
+	print_hex_buf(vx_sa, sizeof(vx_sa), 2, "     ");
+}
+
+/*
  * Get stack address for interrupt stack using the pcpu array
  */
 static unsigned long get_int_stack_pcpu(char *stack_name, int cpu)
@@ -1180,9 +1278,11 @@ static void s390x_back_trace_cmd(struct
 		if (psw_flags & S390X_PSW_MASK_PSTATE) {
 			fprintf(fp,"Task runs in userspace\n");
 			s390x_print_lowcore(lowcore,bt,0);
+			s390x_print_vx_sa(bt, lowcore);
 			return;
 		}
 		s390x_print_lowcore(lowcore,bt,1);
+		s390x_print_vx_sa(bt, lowcore);
 		fprintf(fp,"\n");
 		if (symbol_exists("restart_stack")) {
 			get_int_stack("restart_stack",
