On i386/x86_64, crash uses the ESP register to start its backtrace when analysing LKCD dumps. It does this by guessing in get_lkcd_regs(). This patch uses another approach vor v8 dumps: It uses the registers saved by LKCD in the arch-specific dump header. It fixed a problem here where I head a crash dump from a customer using LKCD that showed a totally wrong backtrace in crash but lcrash worked fine. To implement this, the patch fetches the headers for all architectures and not only to IA64. We need the arch-specific header, this is done by copying the declaraction from the kernel patch (2.6.17, the latest) and using a separate header file for each architecture. Signed-off-by: Bernhard Walle <bwalle@xxxxxxx> --- Makefile | 31 +++++++++- defs.h | 2 kernel.c | 63 +++++++++++--------- lkcd_dump_i386.c | 32 ++++++++++ lkcd_dump_i386.h | 71 +++++++++++++++++++++++ lkcd_dump_ia64.c | 35 +++++++++++ lkcd_dump_ia64.h | 81 ++++++++++++++++++++++++++ lkcd_dump_ppc.h | 67 +++++++++++++++++++++ lkcd_dump_v7.h | 163 ++--------------------------------------------------- lkcd_dump_v8.h | 32 ++++++++++ lkcd_dump_x86_64.c | 32 ++++++++++ lkcd_dump_x86_64.h | 71 +++++++++++++++++++++++ lkcd_fix_mem.c | 18 ----- lkcd_v8.c | 140 ++++++++++++++++++++++++++++++++++++++++++++- 14 files changed, 633 insertions(+), 205 deletions(-) --- a/Makefile +++ b/Makefile @@ -97,6 +97,24 @@ OBJECT_FILES=main.o tools.o global_data. xen_hyper.o xen_hyper_command.o xen_hyper_global_data.o \ xen_hyper_dump_tables.o + +# architecture specific LKCD files + +ifeq ($(ARCH), x86_64) +CFILES += lkcd_dump_x86_64.c +OBJECT_FILES += lkcd_dump_x86_64.o +endif + +ifeq ($(ARCH), i386) +CFILES += lkcd_dump_i386.c +OBJECT_FILES += lkcd_dump_i386.o +endif + +ifeq ($(ARCH), ia64) +CFILES += lkcd_dump_ia64.c +OBJECT_FILES += lkcd_dump_ia64.o +endif + # These are the current set of crash extensions sources. They are not built # by default unless the third command line of the "all:" stanza is uncommented. # Alternatively, they can be built by entering "make extensions" from this @@ -334,6 +352,15 @@ lkcd_v7.o: ${GENERIC_HFILES} ${LKCD_DUMP lkcd_v8.o: ${GENERIC_HFILES} ${LKCD_DUMP_HFILES} lkcd_v8.c cc -c ${CFLAGS} -DMCLX lkcd_v8.c ${WARNING_OPTIONS} ${WARNING_ERROR} +lkcd_dump_x86_64.o: ${GENERIC_HFILES} ${LKCD_DUMP_HFILES} lkcd_dump_x86_64.c + cc -c ${CFLAGS} -DMCLX lkcd_dump_x86_64.c ${WARNING_OPTIONS} ${WARNING_ERROR} + +lkcd_dump_i386.o: ${GENERIC_HFILES} ${LKCD_DUMP_HFILES} lkcd_dump_i386.c + cc -c ${CFLAGS} -DMCLX lkcd_dump_i386.c ${WARNING_OPTIONS} ${WARNING_ERROR} + +lkcd_dump_ia64.o: ${GENERIC_HFILES} ${LKCD_DUMP_HFILES} lkcd_dump_ia64.c + cc -c ${CFLAGS} -DMCLX lkcd_dump_ia64.c ${WARNING_OPTIONS} ${WARNING_ERROR} + net.o: ${GENERIC_HFILES} net.c cc -c ${CFLAGS} net.c ${WARNING_OPTIONS} ${WARNING_ERROR} --- a/defs.h +++ b/defs.h @@ -3790,7 +3790,7 @@ void ppc_dump_machdep_table(ulong); */ ulong get_lkcd_switch_stack(ulong); -int fix_addr_v8(int); +int fix_addr_v8(); int fix_addr_v7(int); /* --- a/kernel.c +++ b/kernel.c @@ -2089,6 +2089,11 @@ get_lkcd_regs(struct bt_info *bt, ulong return; } + /* try to get it from the header */ + if (get_lkcd_regs_for_cpu(bt, eip, esp) == 0) + return; + + /* if that fails: do guessing */ sysrq_eip = sysrq_esp = 0; for (i = 0, up = (ulong *)bt->stackbuf; i < LONGS_PER_STACK; i++, up++){ @@ -2098,48 +2103,48 @@ get_lkcd_regs(struct bt_info *bt, ulong *esp = *(up-1); return; } - /* Begin 3PAR change -- required for our panic path */ + /* Begin 3PAR change -- required for our panic path */ if (STREQ(sym, "dump_ipi") && INSTACK(*(up-1), bt)) { *eip = *up; *esp = *(up-1); return; } /* End 3PAR change */ - if (STREQ(sym, "panic") && INSTACK(*(up-1), bt)) { - *eip = *up; - *esp = *(up-1); - return; - } + if (STREQ(sym, "panic") && INSTACK(*(up-1), bt)) { + *eip = *up; + *esp = *(up-1); + return; + } /* Egenera */ - if (STREQ(sym, "netdump_ipi")) { - *eip = *up; - *esp = bt->task + + if (STREQ(sym, "netdump_ipi")) { + *eip = *up; + *esp = bt->task + ((char *)(up-1) - bt->stackbuf); - return; - } - if (STREQ(sym, "smp_stop_cpu_interrupt")) { - *eip = *up; - *esp = bt->task + + return; + } + if (STREQ(sym, "smp_stop_cpu_interrupt")) { + *eip = *up; + *esp = bt->task + ((char *)(up-1) - bt->stackbuf); - return; - } - if (STREQ(sym, "stop_this_cpu")) { - *eip = *up; - *esp = bt->task + + return; + } + if (STREQ(sym, "stop_this_cpu")) { + *eip = *up; + *esp = bt->task + ((char *)(up-1) - bt->stackbuf); - return; - } - if (SYSRQ_TASK(bt->task) && - STREQ(sym, "smp_call_function_interrupt")) { - sysrq_eip = *up; - sysrq_esp = bt->task + - ((char *)(up-1) - bt->stackbuf); - } + return; + } + if (SYSRQ_TASK(bt->task) && + STREQ(sym, "smp_call_function_interrupt")) { + sysrq_eip = *up; + sysrq_esp = bt->task + + ((char *)(up-1) - bt->stackbuf); + } } if (sysrq_eip) { - *eip = sysrq_eip; - *esp = sysrq_esp; + *eip = sysrq_eip; + *esp = sysrq_esp; return; } --- /dev/null +++ b/lkcd_dump_i386.c @@ -0,0 +1,32 @@ +/* lkcd_dump_i386.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include <stdint.h> +#include <stddef.h> +#include <stdio.h> + +#define LKCD_COMMON +#include "defs.h" +#include "lkcd_dump_v8.h" + + +int +get_lkcd_regs_for_cpu_arch_i386(dump_header_asm_t *dha, int cpu, ulong *eip, ulong *esp) +{ + if (eip) + *eip = dha->dha_smp_regs[cpu].eip; + if (esp) + *esp = dha->dha_smp_regs[cpu].esp; + + return 0; +} + --- /dev/null +++ b/lkcd_dump_i386.h @@ -0,0 +1,71 @@ +/* lkcd_dump_i386.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef LKCD_DUMP_I386_H +#define LKCD_DUMP_I386_H + +#include <linux/ptrace.h> + +/* from kernel include/asm-i386/dump.h */ +#define DUMP_ASM_MAGIC_NUMBER 0xdeaddeadULL /* magic number */ +#define DUMP_ASM_VERSION_NUMBER 0x5 /* version number */ + +/* + * Structure: __dump_header_asm + * Function: This is the header for architecture-specific stuff. It + * follows right after the dump header. + */ +typedef struct __dump_header_asm_i386 { + /* the dump magic number -- unique to verify dump is valid */ + uint64_t dha_magic_number; + + /* the version number of this dump */ + uint32_t dha_version; + + /* the size of this header (in case we can't read it) */ + uint32_t dha_header_size; + + /* the esp for i386 systems */ + uint32_t dha_esp; + + /* the eip for i386 systems */ + uint32_t dha_eip; + + /* the dump registers */ + struct pt_regs dha_regs; + + /* smp specific */ + uint32_t dha_smp_num_cpus; + uint32_t dha_dumping_cpu; + struct pt_regs dha_smp_regs[NR_CPUS]; + uint32_t dha_smp_current_task[NR_CPUS]; + uint32_t dha_stack[NR_CPUS]; + uint32_t dha_stack_ptr[NR_CPUS]; +} __attribute__((packed)) dump_header_asm_i386_t; + +/* + * CPU specific part of dump_header_asm_t + */ +typedef struct dump_CPU_info_i386 { + struct pt_regs dha_smp_regs; + uint64_t dha_smp_current_task; + uint64_t dha_stack; + uint64_t dha_stack_ptr; +} __attribute__ ((packed)) dump_CPU_info_i386_t; + + +typedef struct __dump_header_asm_i386 dump_header_asm_t; +typedef struct dump_CPU_info_i386 dump_CPU_info_t; + + +#endif /* LKCD_DUMP_I386_H */ + --- /dev/null +++ b/lkcd_dump_ia64.c @@ -0,0 +1,35 @@ +/* lkcd_dump_x86_64.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include <stdint.h> +#include <stddef.h> +#include <stdio.h> + +#define LKCD_COMMON +#include "defs.h" +#include "lkcd_dump_v8.h" +#include "lkcd_dump_ia64.h" + + +/* reads the load address value for IA64 */ + +int lkcd_dump_init_v8_ia64(int fd, dump_header_asm_t *dha) +{ + int ret; + + ret = read(fd, &dha->dha_kernel_addr, sizeof(dha->dha_kernel_addr)); + if (ret != sizeof(dha->dha_kernel_addr)) + return -1; + else + return 0; +} + --- /dev/null +++ b/lkcd_dump_ia64.h @@ -0,0 +1,81 @@ +/* lkcd_dump_ia64.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef LKCD_DUMP_IA64_H +#define LKCD_DUMP_IA64_H + +#define _ASM_IA64_FPU_H +#include <linux/ptrace.h> + +#define DUMP_ASM_MAGIC_NUMBER 0xdeaddeadULL /* magic number */ +#define DUMP_ASM_VERSION_NUMBER 0x5 /* version number */ + +/* + * Structure: dump_header_asm_t + * Function: This is the header for architecture-specific stuff. It + * follows right after the dump header. + */ +/*typedef struct _dump_header_asm {*/ + +typedef struct __dump_header_asm_ia64 { + + /* the dump magic number -- unique to verify dump is valid */ + uint64_t dha_magic_number; + + /* the version number of this dump */ + uint32_t dha_version; + + /* the size of this header (in case we can't read it) */ + uint32_t dha_header_size; + + /* pointer to pt_regs, (OLD: (struct pt_regs *, NEW: (uint64_t)) */ + uint64_t dha_pt_regs; + + /* the dump registers */ + struct pt_regs dha_regs; + + /* the rnat register saved after flushrs */ + uint64_t dha_rnat; + + /* the pfs register saved after flushrs */ + uint64_t dha_pfs; + + /* the bspstore register saved after flushrs */ + uint64_t dha_bspstore; + + /* smp specific */ + uint32_t dha_smp_num_cpus; + uint32_t dha_dumping_cpu; + struct pt_regs dha_smp_regs[NR_CPUS]; + uint64_t dha_smp_current_task[NR_CPUS]; + uint64_t dha_stack[NR_CPUS]; + uint64_t dha_stack_ptr[NR_CPUS]; + + /* load address of kernel */ + uint64_t dha_kernel_addr; + +} __attribute__((packed)) dump_header_asm_ia64_t; + + +struct dump_CPU_info_ia64 { + struct pt_regs dha_smp_regs; + uint64_t dha_smp_current_task; + uint64_t dha_stack; + uint64_t dha_stack_ptr; +} __attribute__((packed)) dump_CPU_info_ia64_t; + +typedef struct dump_CPU_info_ia64 dump_CPU_info_t; +typedef struct __dump_header_asm_ia64 dump_header_asm_t; + +int lkcd_dump_init_v8_ia64(int fd, dump_header_asm_t *dha); + +#endif /* LKCD_DUMP_IA64_H */ --- /dev/null +++ b/lkcd_dump_ppc.h @@ -0,0 +1,67 @@ +/* lkcd_dump_ppc.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef LKCD_DUMP_PPC_H +#define LKCD_DUMP_PPC_H + +#include <linux/ptrace.h> + +/* from kernel include/asm-i386/dump.h */ +#define DUMP_ASM_MAGIC_NUMBER 0xdeaddeadULL /* magic number */ +#define DUMP_ASM_VERSION_NUMBER 0x5 /* version number */ + +/* + * Structure: __dump_header_asm + * Function: This is the header for architecture-specific stuff. It + * follows right after the dump header. + */ +struct __dump_header_asm_ppc { + + /* the dump magic number -- unique to verify dump is valid */ + uint64_t dha_magic_number; + + /* the version number of this dump */ + uint32_t dha_version; + + /* the size of this header (in case we can't read it) */ + uint32_t dha_header_size; + + /* the dump registers */ + struct pt_regs dha_regs; + + /* smp specific */ + uint32_t dha_smp_num_cpus; + int dha_dumping_cpu; + struct pt_regs dha_smp_regs[NR_CPUS]; + uint64_t dha_smp_current_task[NR_CPUS]; + uint64_t dha_stack[NR_CPUS]; + uint64_t dha_stack_ptr[NR_CPUS]; +} __attribute__((packed)) dump_header_asm_ppc_t; + + +/* + * CPU specific part of dump_header_asm_t + */ +typedef struct dump_CPU_info_ppc { + struct pt_regs dha_smp_regs; + uint64_t dha_smp_current_task; + uint64_t dha_stack; + uint64_t dha_stack_ptr; +} __attribute__ ((packed)) dump_CPU_info_ppc_t; + + +typedef struct __dump_header_asm_ppc dump_header_asm_t; +typedef struct dump_CPU_info_ppc dump_CPU_info_t; + + +#endif /* LKCD_DUMP_PPC_H */ + --- a/lkcd_dump_v7.h +++ b/lkcd_dump_v7.h @@ -228,165 +228,20 @@ typedef struct lkcdinfo_s { } lkcdinfo_t; -#ifdef IA64 - -#define DUMP_ASM_MAGIC_NUMBER 0xdeaddeadULL /* magic number */ - -struct pt_regs { - /* The following registers are saved by SAVE_MIN: */ - unsigned long b6; /* scratch */ - unsigned long b7; /* scratch */ - - unsigned long ar_csd; /* used by cmp8xchg16 (scratch) */ - unsigned long ar_ssd; /* reserved for future use (scratch) */ - - unsigned long r8; /* scratch (return value register 0) */ - unsigned long r9; /* scratch (return value register 1) */ - unsigned long r10; /* scratch (return value register 2) */ - unsigned long r11; /* scratch (return value register 3) */ - - unsigned long cr_ipsr; /* interrupted task's psr */ - unsigned long cr_iip; /* interrupted task's instruction pointer */ - unsigned long cr_ifs; /* interrupted task's function state */ - - unsigned long ar_unat; /* interrupted task's NaT register (preserved) */ - unsigned long ar_pfs; /* prev function state */ - unsigned long ar_rsc; /* RSE configuration */ - /* The following two are valid only if cr_ipsr.cpl > 0: */ - unsigned long ar_rnat; /* RSE NaT */ - unsigned long ar_bspstore; /* RSE bspstore */ - - unsigned long pr; /* 64 predicate registers (1 bit each) */ - unsigned long b0; /* return pointer (bp) */ - unsigned long loadrs; /* size of dirty partition << 16 */ - - unsigned long r1; /* the gp pointer */ - unsigned long r12; /* interrupted task's memory stack pointer */ - unsigned long r13; /* thread pointer */ - - unsigned long ar_fpsr; /* floating point status (preserved) */ - unsigned long r15; /* scratch */ - - /* The remaining registers are NOT saved for system calls. */ - - unsigned long r14; /* scratch */ - unsigned long r2; /* scratch */ - unsigned long r3; /* scratch */ - - /* The following registers are saved by SAVE_REST: */ - unsigned long r16; /* scratch */ - unsigned long r17; /* scratch */ - unsigned long r18; /* scratch */ - unsigned long r19; /* scratch */ - unsigned long r20; /* scratch */ - unsigned long r21; /* scratch */ - unsigned long r22; /* scratch */ - unsigned long r23; /* scratch */ - unsigned long r24; /* scratch */ - unsigned long r25; /* scratch */ - unsigned long r26; /* scratch */ - unsigned long r27; /* scratch */ - unsigned long r28; /* scratch */ - unsigned long r29; /* scratch */ - unsigned long r30; /* scratch */ - unsigned long r31; /* scratch */ - - unsigned long ar_ccv; /* compare/exchange value (scratch) */ - - /* - * Floating point registers that the kernel considers scratch: - */ - struct ia64_fpreg f6; /* scratch */ - struct ia64_fpreg f7; /* scratch */ - struct ia64_fpreg f8; /* scratch */ - struct ia64_fpreg f9; /* scratch */ - struct ia64_fpreg f10; /* scratch */ - struct ia64_fpreg f11; /* scratch */ -}; - /* - * Structure: dump_header_asm_t - * Function: This is the header for architecture-specific stuff. It - * follows right after the dump header. + * architecture specific data structures */ -typedef struct _dump_header_asm_s { - - /* the dump magic number -- unique to verify dump is valid */ - uint64_t dha_magic_number; - - /* the version number of this dump */ - uint32_t dha_version; - - /* the size of this header (in case we can't read it) */ - uint32_t dha_header_size; - - /* pointer to pt_regs */ - struct pt_regs *dha_pt_regs; - - /* the dump registers */ - struct pt_regs dha_regs; - - /* the rnat register saved after flushrs */ - uint64_t dha_rnat; - - /* the pfs register saved after flushrs */ - uint64_t dha_pfs; - - /* the bspstore register saved after flushrs */ - uint64_t dha_bspstore; - - /* smp specific */ - uint32_t dha_smp_num_cpus; - int dha_dumping_cpu; - struct pt_regs dha_smp_regs[NR_CPUS]; - void * dha_smp_current_task[NR_CPUS]; - void * dha_stack[NR_CPUS]; - void * dha_switch_stack[NR_CPUS]; - -} dump_header_asm_t; - -#define NR_CPUS 32 - -typedef struct _dump_header_asm_smp_s { - - /* the dump magic number -- unique to verify dump is valid */ - uint64_t dha_magic_number; - - /* the version number of this dump */ - uint32_t dha_version; - - /* the size of this header (in case we can't read it) */ - uint32_t dha_header_size; - - /* pointer to pt_regs */ - struct pt_regs *dha_pt_regs; - - /* the dump registers */ - struct pt_regs dha_regs; - - /* the rnat register saved after flushrs */ - uint64_t dha_rnat; - - /* the pfs register saved after flushrs */ - uint64_t dha_pfs; - - /* the bspstore register saved after flushrs */ - uint64_t dha_bspstore; - - /* smp specific */ - uint32_t dha_smp_num_cpus; - int dha_dumping_cpu; - struct pt_regs dha_smp_regs[NR_CPUS]; - void * dha_smp_current_task[NR_CPUS]; - void * dha_stack[NR_CPUS]; - void * dha_switch_stack[NR_CPUS]; - -} dump_header_asm_smp_t; - +#if defined(IA64) +# include "lkcd_dump_ia64.h" +#elif defined(X86_64) +# include "lkcd_dump_x86_64.h" +#elif defined(X86) +# include "lkcd_dump_i386.h" +#else +# error "Add support for your architecture here" #endif - #ifdef __KERNEL__ /* --- a/lkcd_dump_v8.h +++ b/lkcd_dump_v8.h @@ -235,4 +235,36 @@ typedef struct lkcdinfo_s { int stack_offset; } lkcdinfo_t; + +#if defined(IA64) + +# include "lkcd_dump_ia64.h" +# define get_lkcd_regs_for_cpu_arch(dha, cpu, eip, esp) \ + -1 + +#elif defined(X86_64) + +# include "lkcd_dump_x86_64.h" +# define get_lkcd_regs_for_cpu_arch(dha, cpu, eip, esp) \ + get_lkcd_regs_for_cpu_arch_x86_64(dha, cpu, eip, esp) + +#elif defined(X86) + +# include "lkcd_dump_i386.h" +# define get_lkcd_regs_for_cpu_arch(dha, cpu, eip, esp) \ + get_lkcd_regs_for_cpu_arch_i386(dha, cpu, eip, esp) + +#elif defined(PPC) || defined(PPC64) + +# include "lkcd_dump_ppc.h" +# define get_lkcd_regs_for_cpu_arch(dha, cpu, eip, esp) \ + -1 + +#else + +# define NO_LKCD_DUMP_ARCH 1 + +#endif + + #endif /* _DUMP_H */ --- /dev/null +++ b/lkcd_dump_x86_64.c @@ -0,0 +1,32 @@ +/* lkcd_dump_x86_64.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include <stdint.h> +#include <stddef.h> +#include <stdio.h> + +#define LKCD_COMMON +#include "defs.h" +#include "lkcd_dump_v8.h" + + +int +get_lkcd_regs_for_cpu_arch_x86_64(dump_header_asm_t *dha, int cpu, ulong *eip, ulong *esp) +{ + if (eip) + *eip = dha->dha_smp_regs[cpu].rip; + if (esp) + *esp = dha->dha_smp_regs[cpu].rsp; + + return 0; +} + --- /dev/null +++ b/lkcd_dump_x86_64.h @@ -0,0 +1,71 @@ +/* lkcd_dump_x86_64.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef LKCD_DUMP_X86_64_H +#define LKCD_DUMP_X86_64_H + +#include <linux/ptrace.h> + +#define DUMP_HEADER_SIZE 0x100000 + + +/* from kernel include/asm-x86_64/dump.h */ + +/* definitions */ +#define DUMP_ASM_MAGIC_NUMBER 0xdeaddeadULL /* magic number */ +#define DUMP_ASM_VERSION_NUMBER 0x2 /* version number */ + + +/* + * Structure: dump_header_asm_t + * Function: This is the header for architecture-specific stuff. It + * follows right after the dump header. + */ +typedef struct __dump_header_asm_x86_64 { + + /* the dump magic number -- unique to verify dump is valid */ + uint64_t dha_magic_number; + + /* the version number of this dump */ + uint32_t dha_version; + + /* the size of this header (in case we can't read it) */ + uint32_t dha_header_size; + + /* the dump registers */ + struct pt_regs dha_regs; + + /* smp specific */ + uint32_t dha_smp_num_cpus; + int dha_dumping_cpu; + struct pt_regs dha_smp_regs[NR_CPUS]; + uint64_t dha_smp_current_task[NR_CPUS]; + uint64_t dha_stack[NR_CPUS]; + uint64_t dha_stack_ptr[NR_CPUS]; +} __attribute__((packed)) dump_header_asm_t_x86_64; + + +/* + * CPU specific part of dump_header_asm_t + */ +typedef struct dump_CPU_info_x86_64 { + struct pt_regs dha_smp_regs; + uint64_t dha_smp_current_task; + uint64_t dha_stack; + uint64_t dha_stack_ptr; +} __attribute__ ((packed)) dump_CPU_info_x86_64_t; + +typedef struct dump_CPU_info_x86_64 dump_CPU_info_t; +typedef struct __dump_header_asm_x86_64 dump_header_asm_t; + +#endif /* LKCD_DUMP_X86_64_H */ + --- a/lkcd_fix_mem.c +++ b/lkcd_fix_mem.c @@ -25,16 +25,8 @@ static int fix_addr(dump_header_asm_t *); int -fix_addr_v8(int fd) +fix_addr_v8(dump_header_asm_t *dha) { - static dump_header_asm_t dump_header_asm_v8 = { 0 }; - dump_header_asm_t *dha; - dha = &dump_header_asm_v8; - - if (read(lkcd->fd, dha, sizeof(dump_header_asm_t)) != - sizeof(dump_header_asm_t)) - return -1; - fix_addr(dha); return 0; @@ -59,14 +51,6 @@ fix_addr_v7(int fd) static int fix_addr(dump_header_asm_t *dha) { - - - if (dha->dha_header_size != sizeof(dump_header_asm_t)) { - error(INFO, "LKCD machine specific dump header doesn't match crash version\n"); - error(INFO, "traceback of currently executing threads may not work\n\n"); - } - - lkcd->dump_header_asm = dha; --- a/lkcd_v8.c +++ b/lkcd_v8.c @@ -19,15 +19,120 @@ */ #define LKCD_COMMON +#include <stddef.h> #include "defs.h" #include "lkcd_dump_v8.h" /* REMIND */ static dump_header_t dump_header_v8 = { 0 }; -// static dump_header_asm_t dump_header_asm_v8 = { 0 }; +#ifndef NO_LKCD_DUMP_ARCH +static dump_header_asm_t dump_header_asm_v8 = { 0 }; +#endif static dump_page_t dump_page = { 0 }; static void mclx_cache_page_headers_v8(void); static off_t lkcd_offset_to_first_page = LKCD_OFFSET_TO_FIRST_PAGE; +#define STR(x) #x + +#ifndef NO_LKCD_DUMP_ARCH +int +lkcd_dump_init_v8_arch(dump_header_t *dh, dump_header_asm_t *dha) +{ + off_t ret_of; + ssize_t ret_sz; + uint32_t hdr_size, offset, nr_cpus; + dump_header_asm_t arch_hdr; + char *hdr_buf = NULL; + + ret_of = lseek(lkcd->fd, dh->dh_header_size + + offsetof(dump_header_asm_t, dha_header_size), + SEEK_SET); + if (ret_of < 0) { + perror("lseek failed in " __FILE__ ":" STR(__LINE__)); + goto err; + } + + ret_sz = read(lkcd->fd, (char *)&hdr_size, sizeof(hdr_size)); + if (ret_sz != sizeof(hdr_size)) { + perror("Reading hdr_size failed in " __FILE__ ":" STR(__LINE__)); + goto err; + } + + ret_of = lseek(lkcd->fd, dh->dh_header_size, SEEK_SET); + if (ret_of < 0) { + perror("lseek failed in " __FILE__ ":" STR(__LINE__)); + goto err; + } + + hdr_buf = (char *)malloc(hdr_size); + if (!hdr_buf) { + perror("Could not allocate memory for dump header\n"); + goto err; + } + + ret_sz = read(lkcd->fd, (char *)hdr_buf, hdr_size); + if (ret_sz != hdr_size) { + perror("Could not read header " __FILE__ ":" STR(__LINE__)); + goto err; + } + + + /* + * Though we have KL_NR_CPUS is 128, the header size is different + * CONFIG_NR_CPUS might be different in the kernel. Hence, need + * to find out how many CPUs are configured. + */ + offset = offsetof(dump_header_asm_t, dha_smp_regs[0]); + nr_cpus = (hdr_size - offset) / sizeof(dump_CPU_info_t); + + /* check for CPU overflow */ + if (nr_cpus > NR_CPUS) { + fprintf(stderr, "CPU number too high %d (%s:%s)\n", + nr_cpus, __FILE__, __LINE__); + goto err; + } + + /* parts that don't depend on the number of CPUs */ + memcpy(&arch_hdr, (void *)hdr_buf, offset); + + /* registers */ + memcpy(&arch_hdr.dha_smp_regs, (void *)&hdr_buf[offset], + nr_cpus * sizeof(struct pt_regs)); + offset += nr_cpus * sizeof(struct pt_regs); + + /* current task */ + memcpy(&arch_hdr.dha_smp_current_task, (void *)&hdr_buf[offset], + nr_cpus * sizeof(&arch_hdr.dha_smp_current_task[0])); + offset += nr_cpus * sizeof(&arch_hdr.dha_smp_current_task[0]); + + /* stack */ + memcpy(&arch_hdr.dha_stack, (void *)&hdr_buf[offset], + nr_cpus * sizeof(&arch_hdr.dha_stack[0])); + offset += nr_cpus * sizeof(&arch_hdr.dha_stack[0]); + + /* stack_ptr */ + memcpy(&arch_hdr.dha_stack_ptr, (void *)&hdr_buf[offset], + nr_cpus * sizeof(&arch_hdr.dha_stack_ptr[0])); + offset += nr_cpus * sizeof(&arch_hdr.dha_stack_ptr[0]); + + if (arch_hdr.dha_magic_number != DUMP_ASM_MAGIC_NUMBER) { + fprintf(stderr, "Invalid magic number for x86_64\n"); + goto err; + } + +#ifdef IA64 + lkcd_dump_init_v8_ia64(lkcd->fd, &arch_hdr); +#endif + + memcpy(dha, &arch_hdr, sizeof(dump_header_asm_t)); + + return 0; + +err: + free(hdr_buf); + return -1; +} +#endif + /* * Verify and initialize the LKCD environment, storing the common data * in the global lkcd_environment structure. @@ -69,8 +174,16 @@ lkcd_dump_init_v8(FILE *fp, int fd, char lkcd->dump_header = dh; if (lkcd->debug) dump_lkcd_environment(LKCD_DUMP_HEADER_ONLY); + +#ifndef NO_LKCD_DUMP_ARCH + if (lkcd_dump_init_v8_arch(dh, &dump_header_asm_v8) != 0) { + fprintf(stderr, "Warning: Failed to initialise " + "arch specific dump code\n"); + } +#endif + #ifdef IA64 - if ( (fix_addr_v8(fd) == -1) ) + if ( (fix_addr_v8(&dump_header_asm_v8) == -1) ) return FALSE; #endif @@ -233,6 +346,29 @@ lkcd_dump_init_v8(FILE *fp, int fd, char return TRUE; } +int +get_lkcd_regs_for_cpu(struct bt_info *bt, ulong *eip, ulong *esp) +{ + int cpu; + + if (!bt || !bt->tc) { + fprintf(stderr, "get_lkcd_regs_for_cpu: invalid tc\n", cpu); + return -EINVAL; + } + + cpu = bt->tc->processor; + + if (cpu >= NR_CPUS) { + fprintf(stderr, "get_lkcd_regs_for_cpu, cpu (%d) too high\n", cpu); + return -EINVAL; + } + +#ifndef NO_LKCD_DUMP_ARCH + return get_lkcd_regs_for_cpu_arch(&dump_header_asm_v8, cpu, eip, esp); +#else + return -ENOSYS; +#endif +} /* * Return the current page's dp_size. -- Crash-utility mailing list Crash-utility@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/crash-utility