----- Original Message -----
Hello Dave,
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.
Just a thought -- might this be more applicable to "help -r", which
is where per-cpu register dumps are normally done?
Dave
This patch also adds a new -A option to the "bt" command. The
new vector registers are only shown when this option is specified.
This is done because for normal degugging 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 -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(a)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, 126 insertions(+), 1 deletion(-)
--- 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 displays all available CPU registers.",
" -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,12 +2003,15 @@ 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 'A':
+ bt->flags |= BT_SHOW_ALL_REGS;
+ break;
case 'F':
if (bt->flags & BT_FULL_SYM_SLAB)
--- 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",