--- Makefile | 7 +- configure.c | 48 +++ defs.h | 111 ++++++- help.c | 6 +- lkcd_vmdump_v2_v3.h | 3 +- mips.c | 879 ++++++++++++++++++++++++++++++++++++++++++++++++++++ netdump.c | 10 + symbols.c | 9 + 8 files changed, 1067 insertions(+), 6 deletions(-) create mode 100644 mips.c diff --git a/Makefile b/Makefile index c1a3efd..184ef66 100644 --- a/Makefile +++ b/Makefile @@ -61,7 +61,7 @@ UNWIND_HFILES=unwind.h unwind_i.h rse.h unwind_x86.h unwind_x86_64.h CFILES=main.c tools.c global_data.c memory.c filesys.c help.c task.c \ kernel.c test.c gdb_interface.c configure.c net.c dev.c \ alpha.c x86.c ppc.c ia64.c s390.c s390x.c s390dbf.c ppc64.c x86_64.c \ - arm.c arm64.c \ + arm.c arm64.c mips.o \ extensions.c remote.c va_server.c va_server_v1.c symbols.c cmdline.c \ lkcd_common.c lkcd_v1.c lkcd_v2_v3.c lkcd_v5.c lkcd_v7.c lkcd_v8.c\ lkcd_fix_mem.c s390_dump.c lkcd_x86_trace.c \ @@ -79,7 +79,7 @@ SOURCE_FILES=${CFILES} ${GENERIC_HFILES} ${MCORE_HFILES} \ OBJECT_FILES=main.o tools.o global_data.o memory.o filesys.o help.o task.o \ build_data.o kernel.o test.o gdb_interface.o net.o dev.o \ alpha.o x86.o ppc.o ia64.o s390.o s390x.o s390dbf.o ppc64.o x86_64.o \ - arm.o arm64.o \ + arm.o arm64.o mips.o \ extensions.o remote.o va_server.o va_server_v1.o symbols.o cmdline.o \ lkcd_common.o lkcd_v1.o lkcd_v2_v3.o lkcd_v5.o lkcd_v7.o lkcd_v8.o \ lkcd_fix_mem.o s390_dump.o netdump.o diskdump.o makedumpfile.o xendump.o \ @@ -416,6 +416,9 @@ arm.o: ${GENERIC_HFILES} ${REDHAT_HFILES} arm.c arm64.o: ${GENERIC_HFILES} ${REDHAT_HFILES} arm64.c ${CC} -c ${CRASH_CFLAGS} arm64.c ${WARNING_OPTIONS} ${WARNING_ERROR} +mips.o: ${GENERIC_HFILES} ${REDHAT_HFILES} mips.c + ${CC} -c ${CRASH_CFLAGS} mips.c ${WARNING_OPTIONS} ${WARNING_ERROR} + s390.o: ${GENERIC_HFILES} ${IBM_HFILES} s390.c ${CC} -c ${CRASH_CFLAGS} s390.c ${WARNING_OPTIONS} ${WARNING_ERROR} diff --git a/configure.c b/configure.c index 7b91e04..800173a 100644 --- a/configure.c +++ b/configure.c @@ -116,6 +116,7 @@ void add_extra_lib(char *); #define X86_64 8 #define ARM 9 #define ARM64 10 +#define MIPS 11 #define TARGET_X86 "TARGET=X86" #define TARGET_ALPHA "TARGET=ALPHA" @@ -127,6 +128,7 @@ void add_extra_lib(char *); #define TARGET_X86_64 "TARGET=X86_64" #define TARGET_ARM "TARGET=ARM" #define TARGET_ARM64 "TARGET=ARM64" +#define TARGET_MIPS "TARGET=MIPS" #define TARGET_CFLAGS_X86 "TARGET_CFLAGS=-D_FILE_OFFSET_BITS=64" #define TARGET_CFLAGS_ALPHA "TARGET_CFLAGS=" @@ -144,6 +146,9 @@ void add_extra_lib(char *); #define TARGET_CFLAGS_ARM64 "TARGET_CFLAGS=" #define TARGET_CFLAGS_ARM64_ON_X86_64 "TARGET_CFLAGS=" #define TARGET_CFLAGS_PPC64_ON_X86_64 "TARGET_CFLAGS=" +#define TARGET_CFLAGS_MIPS "TARGET_CFLAGS=-D_FILE_OFFSET_BITS=64" +#define TARGET_CFLAGS_MIPS_ON_X86 "TARGET_CFLAGS=-D_FILE_OFFSET_BITS=64" +#define TARGET_CFLAGS_MIPS_ON_X86_64 "TARGET_CFLAGS=-m32 -D_FILE_OFFSET_BITS=64" #define GDB_TARGET_DEFAULT "GDB_CONF_FLAGS=" #define GDB_TARGET_ARM_ON_X86 "GDB_CONF_FLAGS=--target=arm-elf-linux" @@ -152,6 +157,8 @@ void add_extra_lib(char *); #define GDB_TARGET_PPC_ON_PPC64 "GDB_CONF_FLAGS=--target=ppc-elf-linux CFLAGS=-m32" #define GDB_TARGET_ARM64_ON_X86_64 "GDB_CONF_FLAGS=--target=aarch64-elf-linux" /* TBD */ #define GDB_TARGET_PPC64_ON_X86_64 "GDB_CONF_FLAGS=--target=powerpc64le-unknown-linux-gnu" +#define GDB_TARGET_MIPS_ON_X86 "GDB_CONF_FLAGS=--target=mipsel-elf-linux" +#define GDB_TARGET_MIPS_ON_X86_64 "GDB_CONF_FLAGS=--target=mipsel-elf-linux CFLAGS=-m32" /* * The original plan was to allow the use of a particular version @@ -368,6 +375,9 @@ get_current_configuration(struct supported_gdb_version *sp) #ifdef __aarch64__ target_data.target = ARM64; #endif +#ifdef __mipsel___ + target_data.target = MIPS; +#endif set_initial_target(sp); @@ -384,6 +394,14 @@ get_current_configuration(struct supported_gdb_version *sp) * X86_64 when built as a 32-bit executable. */ target_data.target = ARM; + } else if ((target_data.target == X86 || target_data.target == X86_64) && + (name_to_target((char *)target_data.target_as_param) == MIPS)) { + /* + * Debugging of MIPS little-endian core files + * supported on X86, and on X86_64 when built as a + * 32-bit executable. + */ + target_data.target = MIPS; } else if ((target_data.target == X86_64) && (name_to_target((char *)target_data.target_as_param) == X86)) { /* @@ -440,6 +458,15 @@ get_current_configuration(struct supported_gdb_version *sp) (target_data.initial_gdb_target != ARM)) arch_mismatch(sp); + if ((target_data.initial_gdb_target == MIPS) && + (target_data.target != MIPS)) { + if ((target_data.target == X86) || + (target_data.target == X86_64)) + target_data.target = MIPS; + else + arch_mismatch(sp); + } + if ((target_data.initial_gdb_target == X86) && (target_data.target != X86)) { if (target_data.target == X86_64) @@ -590,6 +617,9 @@ show_configuration(void) case ARM64: printf("TARGET: ARM64\n"); break; + case MIPS: + printf("TARGET: MIPS\n"); + break; } if (strlen(target_data.program)) { @@ -688,6 +718,17 @@ build_configure(struct supported_gdb_version *sp) } else target_CFLAGS = TARGET_CFLAGS_ARM64; break; + case MIPS: + target = TARGET_MIPS; + if (target_data.host == X86) { + target_CFLAGS = TARGET_CFLAGS_MIPS_ON_X86; + gdb_conf_flags = GDB_TARGET_MIPS_ON_X86; + } else if (target_data.host == X86_64) { + target_CFLAGS = TARGET_CFLAGS_MIPS_ON_X86_64; + gdb_conf_flags = GDB_TARGET_MIPS_ON_X86_64; + } else + target_CFLAGS = TARGET_CFLAGS_MIPS; + break; } ldflags = get_extra_flags("LDFLAGS.extra", NULL); @@ -1511,6 +1552,8 @@ set_initial_target(struct supported_gdb_version *sp) target_data.initial_gdb_target = ARM64; else if (strncmp(buf, "ARM", strlen("ARM")) == 0) target_data.initial_gdb_target = ARM; + else if (strncmp(buf, "MIPS", strlen("MIPS")) == 0) + target_data.initial_gdb_target = MIPS; } char * @@ -1528,6 +1571,7 @@ target_to_name(int target) case X86_64: return("X86_64"); case ARM: return("ARM"); case ARM64: return("ARM64"); + case MIPS: return("MIPS"); } return "UNKNOWN"; @@ -1582,6 +1626,10 @@ name_to_target(char *name) return ARM; else if (strncmp(name, "arm", strlen("arm")) == 0) return ARM; + else if (strncmp(name, "mips", strlen("mips")) == 0) + return MIPS; + else if (strncmp(name, "MIPS", strlen("MIPS")) == 0) + return MIPS; return UNKNOWN; } diff --git a/defs.h b/defs.h index 9d44fbc..c1dfbbc 100644 --- a/defs.h +++ b/defs.h @@ -71,7 +71,7 @@ #if !defined(X86) && !defined(X86_64) && !defined(ALPHA) && !defined(PPC) && \ !defined(IA64) && !defined(PPC64) && !defined(S390) && !defined(S390X) && \ - !defined(ARM) && !defined(ARM64) + !defined(ARM) && !defined(ARM64) && !defined(MIPS) #ifdef __alpha__ #define ALPHA #endif @@ -103,6 +103,9 @@ #ifdef __aarch64__ #define ARM64 #endif +#ifdef __mipsel__ +#define MIPS +#endif #endif #ifdef X86 @@ -135,6 +138,9 @@ #ifdef ARM64 #define NR_CPUS (4096) /* TBD */ #endif +#ifdef MIPS +#define NR_CPUS (32) +#endif #define BUFSIZE (1500) #define NULLCHAR ('\0') @@ -1928,6 +1934,8 @@ struct offset_table { /* stash of commonly-used offsets */ long atomic_t_counter; long percpu_counter_count; long mm_struct_mm_count; + long task_struct_thread_reg29; + long task_struct_thread_reg31; }; struct size_table { /* stash of commonly-used sizes */ @@ -2945,6 +2953,37 @@ struct arm64_stackframe { #endif /* ARM64 */ +#ifdef MIPS +#define _32BIT_ +#define MACHINE_TYPE "MIPS" + +#define PAGEBASE(X) (((ulong)(X)) & (ulong)machdep->pagemask) + +#define PTOV(X) ((unsigned long)(X) + 0x80000000lu) +#define VTOP(X) ((unsigned long)(X) & 0x1ffffffflu) + +#define IS_VMALLOC_ADDR(X) (vt->vmalloc_start && (ulong)(X) >= vt->vmalloc_start) + +#define DEFAULT_MODULES_VADDR (machdep->kvbase - 16 * 1024 * 1024) +#define MODULES_VADDR (machdep->machspec->modules_vaddr) +#define MODULES_END (machdep->machspec->modules_end) +#define VMALLOC_START (machdep->machspec->vmalloc_start_addr) +#define VMALLOC_END (machdep->machspec->vmalloc_end) + +#define __SWP_TYPE_SHIFT 3 +#define __SWP_TYPE_BITS 6 +#define __SWP_TYPE_MASK ((1 << __SWP_TYPE_BITS) - 1) +#define __SWP_OFFSET_SHIFT (__SWP_TYPE_BITS + __SWP_TYPE_SHIFT) + +#define SWP_TYPE(entry) (((entry) >> __SWP_TYPE_SHIFT) & __SWP_TYPE_MASK) +#define SWP_OFFSET(entry) ((entry) >> __SWP_OFFSET_SHIFT) + +#define __swp_type(entry) SWP_TYPE(entry) +#define __swp_offset(entry) SWP_OFFSET(entry) + +#define TIF_SIGPENDING (2) +#endif /* MIPS */ + #ifdef X86 #define _32BIT_ #define MACHINE_TYPE "X86" @@ -3802,6 +3841,10 @@ struct efi_memory_desc_t { #define MAX_HEXADDR_STRLEN (16) #define UVADDR_PRLEN (10) #endif +#ifdef MIPS +#define MAX_HEXADDR_STRLEN (8) +#define UVADDR_PRLEN (8) +#endif #define BADADDR ((ulong)(-1)) #define BADVAL ((ulong)(-1)) @@ -4338,6 +4381,9 @@ void dump_build_data(void); #ifdef PPC64 #define machdep_init(X) ppc64_init(X) #endif +#ifdef MIPS +#define machdep_init(X) mips_init(X) +#endif int clean_exit(int); int untrusted_file(FILE *, char *); char *readmem_function_name(void); @@ -4745,6 +4791,9 @@ void display_help_screen(char *); #ifdef PPC64 #define dump_machdep_table(X) ppc64_dump_machdep_table(X) #endif +#ifdef MIPS +#define dump_machdep_table(X) mips_dump_machdep_table(X) +#endif extern char *help_pointer[]; extern char *help_alias[]; extern char *help_ascii[]; @@ -5505,6 +5554,66 @@ void s390x_dump_machdep_table(ulong); #define KSYMS_START (0x1) #endif +#ifdef MIPS +void mips_init(int); +void mips_dump_machdep_table(ulong); + +#define display_idt_table() \ + error(FATAL, "-d option is not applicable to MIPS architecture\n") + +struct mips_regset { + ulong regs[45]; +}; + +struct mips_pt_regs { + ulong pad0[8]; + ulong regs[32]; + ulong cp0_status; + ulong hi; + ulong lo; + ulong cp0_badvaddr; + ulong cp0_cause; + ulong cp0_epc; +}; + +#define KSYMS_START (0x1) +#define PHYS_BASE (0x2) + +#define KVBASE_MASK (0x1ffffff) + +struct machine_specific { + ulong phys_base; + ulong vmalloc_start_addr; + ulong modules_vaddr; + ulong modules_end; + + ulong _page_present; + ulong _page_read; + ulong _page_write; + ulong _page_accessed; + ulong _page_modified; + ulong _page_global; + ulong _page_valid; + ulong _page_no_read; + ulong _page_no_exec; + ulong _page_dirty; + + ulong _pfn_shift; + +#define _PAGE_PRESENT (machdep->machspec->_page_present) +#define _PAGE_READ (machdep->machspec->_page_read) +#define _PAGE_WRITE (machdep->machspec->_page_write) +#define _PAGE_ACCESSED (machdep->machspec->_page_accessed) +#define _PAGE_MODIFIED (machdep->machspec->_page_modified) +#define _PAGE_GLOBAL (machdep->machspec->_page_global) +#define _PAGE_VALID (machdep->machspec->_page_valid) +#define _PAGE_NO_READ (machdep->machspec->_page_no_read) +#define _PAGE_NO_EXEC (machdep->machspec->_page_no_exec) +#define _PAGE_DIRTY (machdep->machspec->_page_dirty) +#define _PFN_SHIFT (machdep->machspec->_pfn_shift) +}; +#endif /* MIPS */ + /* * netdump.c */ diff --git a/help.c b/help.c index bc0dcde..217b73d 100644 --- a/help.c +++ b/help.c @@ -8000,8 +8000,8 @@ char *README[] = { " ", " These are the current prerequisites: ", "", -" o At this point, x86, ia64, x86_64, ppc64, ppc, arm, arm64, alpha, s390", -" and s390x-based kernels are supported. Other architectures may be", +" o At this point, x86, ia64, x86_64, ppc64, ppc, arm, arm64, alpha, mips," +" s390 and s390x-based kernels are supported. Other architectures may be", " addressed in the future.", "", " o One size fits all -- the utility can be run on any Linux kernel version", @@ -8053,6 +8053,8 @@ README_ENTER_DIRECTORY, " 32-bit x86 dumpfiles may be built by typing \"make target=X86\".", " o On an x86 or x86_64 host, a 32-bit x86 binary that can be used to analyze", " 32-bit arm dumpfiles may be built by typing \"make target=ARM\".", +" o On an x86 or x86_64 host, a 32-bit x86 binary that can be used to analyze", +" 32-bit mips dumpfiles may be built by typing \"make target=MIPS\".", " o On an ppc64 host, a 32-bit ppc binary that can be used to analyze", " 32-bit ppc dumpfiles may be built by typing \"make target=PPC\".", " o On an x86_64 host, an x86_64 binary that can be used to analyze", diff --git a/lkcd_vmdump_v2_v3.h b/lkcd_vmdump_v2_v3.h index efcdaf2..7b014ec 100644 --- a/lkcd_vmdump_v2_v3.h +++ b/lkcd_vmdump_v2_v3.h @@ -35,7 +35,8 @@ #include <asm/vmdump.h> /* for architecture-specific header */ #endif -#if defined(ARM) || defined(X86) || defined(PPC) || defined(S390) || defined(S390X) || defined(ARM64) +#if defined(ARM) || defined(X86) || defined(PPC) || defined(S390) || \ + defined(S390X) || defined(ARM64) || defined(MIPS) /* * Kernel header file for Linux crash dumps. diff --git a/mips.c b/mips.c new file mode 100644 index 0000000..863c182 --- /dev/null +++ b/mips.c @@ -0,0 +1,879 @@ +/* + * mips.c - core analysis suite + * + * Copyright (C) 2015 Rabin Vincent <rabin@xxxxxx> + * + * 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. + */ + +#ifdef MIPS + +#include <elf.h> +#include "defs.h" + +/* From arch/mips/asm/include/pgtable{,-32}.h */ +typedef ulong pgd_t; +typedef ulong pte_t; + +#define PTE_ORDER 0 + +#define PGD_T_LOG2 (__builtin_ffs(sizeof(pgd_t)) - 1) +#define PTE_T_LOG2 (__builtin_ffs(sizeof(pte_t)) - 1) + +#define __PGD_ORDER (32 - 3 * PAGESHIFT() + PGD_T_LOG2 + PTE_T_LOG2) +#define PGD_ORDER (__PGD_ORDER >= 0 ? __PGD_ORDER : 0) +#define PGD_SIZE (PAGESIZE() << PGD_ORDER) + +#define PGDIR_SHIFT (2 * PAGESHIFT() + PTE_ORDER - PTE_T_LOG2) +#define PGDIR_SIZE (1UL << PGDIR_SHIFT) +#define PGDIR_MASK (~(PGDIR_SIZE-1)) + +#define USER_PTRS_PER_PGD (0x80000000UL/PGDIR_SIZE) + +#define PTRS_PER_PGD (USER_PTRS_PER_PGD * 2) +#define PTRS_PER_PTE ((PAGESIZE() << PTE_ORDER) / sizeof(pte_t)) + +#define pgd_index(address) (((address) >> PGDIR_SHIFT) & (PTRS_PER_PGD-1)) +#define pte_offset(address) \ + (((address) >> PAGESHIFT()) & (PTRS_PER_PTE - 1)) + +#define MIPS_CPU_RIXI 0x00800000llu + +static struct machine_specific mips_machine_specific = { 0 }; + +static void +mips_display_machine_stats(void) +{ + fprintf(fp, " PAGE SIZE: %d\n", PAGESIZE()); + fprintf(fp, "\n"); + +#define PRINT_PAGE_FLAG(flag) \ + if (flag) \ + fprintf(fp, " %14s: %08lx\n", #flag, flag) + + PRINT_PAGE_FLAG(_PAGE_PRESENT); + PRINT_PAGE_FLAG(_PAGE_READ); + PRINT_PAGE_FLAG(_PAGE_WRITE); + PRINT_PAGE_FLAG(_PAGE_ACCESSED); + PRINT_PAGE_FLAG(_PAGE_MODIFIED); + PRINT_PAGE_FLAG(_PAGE_GLOBAL); + PRINT_PAGE_FLAG(_PAGE_VALID); + PRINT_PAGE_FLAG(_PAGE_NO_READ); + PRINT_PAGE_FLAG(_PAGE_NO_EXEC); + PRINT_PAGE_FLAG(_PAGE_DIRTY); +} + +static void +mips_cmd_mach(void) +{ + int c; + + while ((c = getopt(argcnt, args, "")) != EOF) { + switch(c) { + default: + argerrs++; + break; + } + } + + if (argerrs) + cmd_usage(pc->curcmd, SYNOPSIS); + + mips_display_machine_stats(); +} + +#define PGDIR_OFFSET(X) (((ulong)(X)) & (PGD_SIZE - 1)) + +static void +mips_init_page_flags(void) +{ + ulonglong cpu_options; + int rixi; + ulong shift = 0; + ulong addr; + + addr = symbol_value("cpu_data") + + MEMBER_OFFSET("cpuinfo_mips", "options"); + readmem(addr, KVADDR, &cpu_options, sizeof(cpu_options), + "cpu_data[0].options", FAULT_ON_ERROR); + + rixi = cpu_options & MIPS_CPU_RIXI; + + _PAGE_PRESENT = 1UL << shift++; + + if (!rixi) + _PAGE_READ = 1UL << shift++; + + _PAGE_WRITE = 1UL << shift++; + _PAGE_ACCESSED = 1UL << shift++; + _PAGE_MODIFIED = 1UL << shift++; + + if (rixi) { + _PAGE_NO_EXEC = 1UL << shift++; + _PAGE_NO_READ = 1UL << shift++; + } + + _PAGE_GLOBAL = 1UL << shift++; + _PAGE_VALID = 1UL << shift++; + _PAGE_DIRTY = 1UL << shift++; + + _PFN_SHIFT = PAGESHIFT() - 12 + shift + 3; +} + +static int +mips_translate_pte(ulong pte, void *physaddr, ulonglong pte64) +{ + char ptebuf[BUFSIZE]; + char physbuf[BUFSIZE]; + char buf[BUFSIZE]; + int present; + ulong paddr; + int len1, len2, others; + + present = pte & _PAGE_PRESENT; + paddr = (pte >> _PFN_SHIFT) << PAGESHIFT(); + + if (physaddr) { + *(ulong *)physaddr = PAGEBASE(pte); + return !!present; + } + + sprintf(ptebuf, "%lx", pte); + len1 = MAX(strlen(ptebuf), strlen("PTE")); + fprintf(fp, "%s ", mkstring(buf, len1, CENTER | LJUST, "PTE")); + + if (!present) + return !!present; + + sprintf(physbuf, "%lx", paddr); + len2 = MAX(strlen(physbuf), strlen("PHYSICAL")); + fprintf(fp, "%s ", mkstring(buf, len2, CENTER | LJUST, "PHYSICAL")); + + fprintf(fp, "FLAGS\n"); + fprintf(fp, "%s %s ", + mkstring(ptebuf, len1, CENTER | RJUST, NULL), + mkstring(physbuf, len2, CENTER | RJUST, NULL)); + + fprintf(fp, "("); + others = 0; + +#define CHECK_PAGE_FLAG(flag) \ + if ((_PAGE_##flag) && (pte & _PAGE_##flag)) \ + fprintf(fp, "%s" #flag, others++ ? "|" : "") + + if (pte) { + CHECK_PAGE_FLAG(PRESENT); + CHECK_PAGE_FLAG(READ); + CHECK_PAGE_FLAG(WRITE); + CHECK_PAGE_FLAG(ACCESSED); + CHECK_PAGE_FLAG(MODIFIED); + CHECK_PAGE_FLAG(GLOBAL); + CHECK_PAGE_FLAG(VALID); + CHECK_PAGE_FLAG(NO_READ); + CHECK_PAGE_FLAG(NO_EXEC); + CHECK_PAGE_FLAG(DIRTY); + } else { + fprintf(fp, "no mapping"); + } + + fprintf(fp, ")\n"); + + return !!present; +} + +static int +mips_pgd_vtop(ulong *pgd, ulong vaddr, physaddr_t *paddr, int verbose) +{ + ulong invalid_pte_table = symbol_value("invalid_pte_table"); + ulong *page_dir; + ulong pgd_pte, page_table; + ulong pte; + ulong pbase; + + if (verbose) { + const char *segment; + + if (vaddr < 0x80000000lu) + segment = "useg"; + else if (vaddr < 0xa0000000lu) + segment = "kseg0"; + else if (vaddr < 0xc0000000lu) + segment = "kseg1"; + else if (vaddr < 0xe0000000lu) + segment = "ksseg"; + else + segment = "kseg3"; + + fprintf(fp, "SEGMENT: %s\n", segment); + } + + if (vaddr >= 0x80000000lu && vaddr < 0xc0000000lu) { + *paddr = VTOP(vaddr); + return TRUE; + } + + if (verbose) + fprintf(fp, "PAGE DIRECTORY: %lx\n", (ulong)pgd); + + page_dir = pgd + pgd_index(vaddr); + + FILL_PGD(PAGEBASE(pgd), KVADDR, PGD_SIZE); + pgd_pte = ULONG(machdep->pgd + PGDIR_OFFSET(page_dir)); + + if (verbose) + fprintf(fp, " PGD: %08lx => %lx\n", (ulong)page_dir, pgd_pte); + + if (pgd_pte == invalid_pte_table) { + fprintf(fp, "invalid\n"); + return FALSE; + } + + page_table = VTOP(pgd_pte) + sizeof(pte_t) * pte_offset(vaddr); + + FILL_PTBL(PAGEBASE(page_table), PHYSADDR, PAGESIZE()); + pte = ULONG(machdep->ptbl + PAGEOFFSET(page_table)); + if (verbose) + fprintf(fp, " PTE: %08lx => %08lx\n", page_table, pte); + + if (!(pte & _PAGE_PRESENT)) { + if (verbose) { + fprintf(fp, "\n"); + mips_translate_pte((ulong)pte, 0, pte); + } + return FALSE; + } + + pbase = (pte >> _PFN_SHIFT) << PAGESHIFT(); + *paddr = pbase + PAGEOFFSET(vaddr); + + if (verbose) { + fprintf(fp, " PAGE: %08lx\n\n", pbase); + mips_translate_pte(pte, 0, 0); + } + + return TRUE; +} + +static int +mips_uvtop(struct task_context *tc, ulong vaddr, physaddr_t *paddr, int verbose) +{ + ulong *pgd; + + if (!tc) + error(FATAL, "current context invalid\n"); + + if (is_kernel_thread(tc->task) && IS_KVADDR(vaddr)) { + ulong active_mm; + + readmem(tc->task + OFFSET(task_struct_active_mm), + KVADDR, &active_mm, sizeof(void *), + "task active_mm contents", FAULT_ON_ERROR); + + if (!active_mm) + error(FATAL, + "no active_mm for this kernel thread\n"); + + readmem(active_mm + OFFSET(mm_struct_pgd), + KVADDR, &pgd, sizeof(long), + "mm_struct pgd", FAULT_ON_ERROR); + } else { + ulong mm; + + mm = task_mm(tc->task, TRUE); + if (mm) + pgd = ULONG_PTR(tt->mm_struct + OFFSET(mm_struct_pgd)); + else + readmem(tc->mm_struct + OFFSET(mm_struct_pgd), + KVADDR, &pgd, sizeof(long), "mm_struct pgd", + FAULT_ON_ERROR); + } + + return mips_pgd_vtop(pgd, vaddr, paddr, verbose); +} + +static int +mips_kvtop(struct task_context *tc, ulong kvaddr, physaddr_t *paddr, int verbose) +{ + if (!IS_KVADDR(kvaddr)) + return FALSE; + + if (!verbose && !IS_VMALLOC_ADDR(kvaddr)) { + *paddr = VTOP(kvaddr); + return TRUE; + } + + return mips_pgd_vtop((ulong *)vt->kernel_pgd[0], kvaddr, paddr, + verbose); +} + +static void +mips_dump_exception_stack(struct bt_info *bt, struct mips_pt_regs *regs) +{ + int i; + char buf[BUFSIZE]; + + for (i = 0; i < 32; i += 4) { + fprintf(fp, " $%2d : %08lx %08lx %08lx %08lx\n", + i, regs->regs[i], regs->regs[i+1], + regs->regs[i+2], regs->regs[i+3]); + } + fprintf(fp, " Hi : %08lx\n", regs->hi); + fprintf(fp, " Lo : %08lx\n", regs->lo); + + value_to_symstr(regs->cp0_epc, buf, 16); + fprintf(fp, " epc : %08lx %s\n", regs->cp0_epc, buf); + + value_to_symstr(regs->regs[31], buf, 16); + fprintf(fp, " ra : %08lx %s\n", regs->regs[31], buf); + + fprintf(fp, " Status: %08lx\n", regs->cp0_status); + fprintf(fp, " Cause : %08lx\n", regs->cp0_cause); + fprintf(fp, " BadVA : %08lx\n", regs->cp0_badvaddr); +} + +struct mips_unwind_frame { + ulong sp; + ulong pc; + ulong ra; +}; + +static void +mips_display_full_frame(struct bt_info *bt, struct mips_unwind_frame *current, + struct mips_unwind_frame *previous) +{ + ulong words, addr; + ulong *up; + char buf[BUFSIZE]; + int i, u_idx; + + if (!INSTACK(previous->sp, bt) || !INSTACK(current->sp, bt)) + return; + + words = (previous->sp - current->sp) / sizeof(ulong); + + if (words == 0) { + fprintf(fp, " (no frame)\n"); + return; + } + + addr = current->sp; + u_idx = (current->sp - bt->stackbase) / sizeof(ulong); + for (i = 0; i < words; i++, u_idx++) { + if ((i % 4) == 0) + fprintf(fp, "%s %lx: ", i ? "\n" : "", addr); + + up = (ulong *)(&bt->stackbuf[u_idx * sizeof(ulong)]); + fprintf(fp, "%s ", format_stack_entry(bt, buf, *up, 0)); + addr += sizeof(ulong); + } + fprintf(fp, "\n"); +} + +static int +mips_is_exception_entry(struct syment *sym) +{ + return STREQ(sym->name, "ret_from_exception") || + STREQ(sym->name, "ret_from_irq") || + STREQ(sym->name, "work_resched") || + STREQ(sym->name, "handle_sys"); +} + +static void +mips_dump_backtrace_entry(struct bt_info *bt, struct syment *sym, + struct mips_unwind_frame *current, + struct mips_unwind_frame *previous, int level) +{ + const char *name = sym->name; + struct load_module *lm; + char *name_plus_offset; + char buf[BUFSIZE]; + + name_plus_offset = NULL; + if (bt->flags & BT_SYMBOL_OFFSET) { + struct syment *symp; + ulong symbol_offset; + + symp = value_search(current->pc, &symbol_offset); + + if (symp && symbol_offset) + name_plus_offset = + value_to_symstr(current->pc, buf, bt->radix); + } + + fprintf(fp, "%s#%d [%8lx] %s at %lx", level < 10 ? " " : "", level, + current->sp, name_plus_offset ? name_plus_offset : name, + current->pc); + + if (module_symbol(current->pc, NULL, &lm, NULL, 0)) + fprintf(fp, " [%s]", lm->mod_name); + + fprintf(fp, "\n"); + + if (bt->flags & BT_LINE_NUMBERS) { + char buf[BUFSIZE]; + + get_line_number(current->pc, buf, FALSE); + if (strlen(buf)) + fprintf(fp, " %s\n", buf); + } + + if (mips_is_exception_entry(sym)) { + struct mips_pt_regs ptregs; + + GET_STACK_DATA(current->sp, &ptregs, sizeof(ptregs)); + mips_dump_exception_stack(bt, &ptregs); + } + + if (bt->flags & BT_FULL) { + fprintf(fp, " " + "[PC: %08lx RA: %08lx SP: %08lx SIZE: %ld]\n", + current->pc, current->ra, current->sp, + previous->sp - current->sp); + mips_display_full_frame(bt, current, previous); + } +} + + +static void +mips_analyze_function(ulong start, ulong offset, + struct mips_unwind_frame *current, + struct mips_unwind_frame *previous) +{ + ulong rapos = 0; + ulong spadjust = 0; + ulong *funcbuf, *ip; + ulong i; + + if (CRASHDEBUG(8)) + fprintf(fp, "%s: start %#lx offset %#lx\n", + __func__, start, offset); + + if (!offset) { + previous->sp = current->sp; + return; + } + + ip = funcbuf = (ulong *)GETBUF(offset); + if (!readmem(start, KVADDR, funcbuf, offset, + "mips_analyze_function", RETURN_ON_ERROR)) { + FREEBUF(funcbuf); + error(FATAL, "Cannot read function at %8x", start); + return; + } + + for (i = 0; i < offset; i += 4) { + ulong insn = *ip; + ulong high = (insn >> 16) & 0xffff; + ulong low = insn & 0xffff; + + if (CRASHDEBUG(8)) + fprintf(fp, "insn @ %#lx = %#lx\n", start + i, insn); + + if (high == 0x27bd) { /* ADDIU sp, sp, imm */ + if (!(low & 0x8000)) + break; + + spadjust += 0x10000 - low; + if (CRASHDEBUG(8)) + fprintf(fp, "spadjust = %lu\n", spadjust); + } else if (high == 0xafbf) { /* SW RA, imm(SP) */ + rapos = current->sp + low; + if (CRASHDEBUG(8)) + fprintf(fp, "rapos %lx\n", rapos); + break; + } + + ip++; + } + + FREEBUF(funcbuf); + + previous->sp = current->sp + spadjust; + + if (rapos && !readmem(rapos, KVADDR, ¤t->ra, + sizeof(current->ra), "RA from stack", + RETURN_ON_ERROR)) { + error(FATAL, "Cannot read RA from stack %lx", rapos); + return; + } +} + +static void +mips_back_trace_cmd(struct bt_info *bt) +{ + struct mips_unwind_frame current, previous; + int level = 0; + + previous.sp = previous.pc = previous.ra = 0; + + current.pc = bt->instptr; + current.sp = bt->stkptr; + current.ra = 0; + + if (bt->machdep) { + struct mips_regset *regs = bt->machdep; + previous.pc = current.ra = regs->regs[31]; + } + + while (INSTACK(current.sp, bt)) { + struct syment *symbol; + ulong offset; + + if (CRASHDEBUG(8)) + fprintf(fp, "level %d pc %#lx ra %#lx sp %lx\n", + level, current.pc, current.ra, current.sp); + + if (!IS_KVADDR(current.pc)) + return; + + symbol = value_search(current.pc, &offset); + if (!symbol) { + error(FATAL, "PC is unknown symbol (%lx)", current.pc); + return; + } + + /* + * If we get an address which points to the start of a + * function, then it could one of the following: + * + * - we are dealing with a noreturn function. The last call + * from a noreturn function has an an ra which points to the + * start of the function after it. This is common in the + * oops callchain because of die() which is annotated as + * noreturn. + * + * - we have taken an exception at the start of this function. + * In this case we already have the RA in current.ra. + * + * - we are in one of these routines which appear with zero + * offset in manually-constructed stack frames: + * + * * ret_from_exception + * * ret_from_irq + * * ret_from_fork + * * ret_from_kernel_thread + */ + if (!current.ra && !offset && !STRNEQ(symbol->name, "ret_from")) { + if (CRASHDEBUG(8)) + fprintf(fp, "zero offset at %s, try previous symbol\n", + symbol->name); + + symbol = value_search(current.pc - 4, &offset); + if (!symbol) { + error(FATAL, "PC is unknown symbol (%lx)", current.pc); + return; + } + } + + if (mips_is_exception_entry(symbol)) { + struct mips_pt_regs ptregs; + + GET_STACK_DATA(current.sp, &ptregs, sizeof(ptregs)); + + previous.ra = ptregs.regs[31]; + previous.sp = ptregs.regs[29]; + current.ra = ptregs.cp0_epc; + + if (CRASHDEBUG(8)) + fprintf(fp, "exception pc %#lx ra %#lx sp %lx\n", + previous.pc, previous.ra, previous.sp); + } else { + mips_analyze_function(symbol->value, offset, ¤t, &previous); + } + + mips_dump_backtrace_entry(bt, symbol, ¤t, &previous, level++); + if (!current.ra) + break; + + current.pc = current.ra; + current.sp = previous.sp; + current.ra = previous.ra; + + previous.sp = previous.pc = previous.ra = 0; + } +} + +static void +mips_dumpfile_stack_frame(struct bt_info *bt, ulong *nip, ulong *ksp) +{ + struct mips_regset *regs; + + regs = bt->machdep; + if (!regs) { + fprintf(fp, "0%lx: Register values not available\n", + bt->task); + return; + } + + if (nip) + *nip = regs->regs[40]; + if (ksp) + *ksp = regs->regs[29]; +} + +static int +mips_get_frame(struct bt_info *bt, ulong *pcp, ulong *spp) +{ + if (!bt->tc || !(tt->flags & THREAD_INFO)) + return FALSE; + + if (!readmem(bt->task + OFFSET(task_struct_thread_reg31), + KVADDR, pcp, sizeof(*pcp), + "thread_struct.regs31", + RETURN_ON_ERROR)) { + return FALSE; + } + + if (!readmem(bt->task + OFFSET(task_struct_thread_reg29), + KVADDR, spp, sizeof(*spp), + "thread_struct.regs29", + RETURN_ON_ERROR)) { + return FALSE; + } + + return TRUE; +} + +static void +mips_stackframe_init(void) +{ + long task_struct_thread = MEMBER_OFFSET("task_struct", "thread"); + long thread_reg29 = MEMBER_OFFSET("thread_struct", "reg29"); + long thread_reg31 = MEMBER_OFFSET("thread_struct", "reg31"); + + if ((task_struct_thread == INVALID_OFFSET) || + (thread_reg29 == INVALID_OFFSET) || + (thread_reg31 == INVALID_OFFSET)) { + error(FATAL, + "cannot determine thread_struct offsets\n"); + return; + } + + ASSIGN_OFFSET(task_struct_thread_reg29) = + task_struct_thread + thread_reg29; + ASSIGN_OFFSET(task_struct_thread_reg31) = + task_struct_thread + thread_reg31; +} + +static void +mips_get_stack_frame(struct bt_info *bt, ulong *pcp, ulong *spp) +{ + *pcp = 0; + *spp = 0; + + if (DUMPFILE() && is_task_active(bt->task)) + mips_dumpfile_stack_frame(bt, pcp, spp); + else + mips_get_frame(bt, pcp, spp); + +} + +static int +mips_eframe_search(struct bt_info *bt) +{ + return error(FATAL, "%s: not implemented\n", __func__); +} + +static ulong +mips_get_task_pgd(ulong task) +{ + return error(FATAL, "%s: not implemented\n", __func__); +} + +static int +mips_is_task_addr(ulong task) +{ + if (tt->flags & THREAD_INFO) + return IS_KVADDR(task); + + return (IS_KVADDR(task) && ALIGNED_STACK_OFFSET(task) == 0); +} + +static ulong +mips_processor_speed(void) +{ + return 0; +} + +static int +mips_get_smp_cpus(void) +{ + return (get_cpus_online() > 0) ? get_cpus_online() : kt->cpus; +} + +static ulong +mips_vmalloc_start(void) +{ + return first_vmalloc_address(); +} + +static int +mips_verify_symbol(const char *name, ulong value, char type) +{ + if (CRASHDEBUG(8) && name && strlen(name)) + fprintf(fp, "%08lx %s\n", value, name); + + if (STREQ(name, "_text")) + machdep->flags |= KSYMS_START; + + return (name && strlen(name) && (machdep->flags & KSYMS_START) && + !STRNEQ(name, "__func__.") && !STRNEQ(name, "__crc_")); +} + +void +mips_dump_machdep_table(ulong arg) +{ + int others = 0; + + fprintf(fp, " flags: %lx (", machdep->flags); + if (machdep->flags & KSYMS_START) + fprintf(fp, "%sKSYMS_START", others++ ? "|" : ""); + fprintf(fp, ")\n"); + + fprintf(fp, " kvbase: %lx\n", machdep->kvbase); + fprintf(fp, " identity_map_base: %lx\n", machdep->identity_map_base); + fprintf(fp, " pagesize: %d\n", machdep->pagesize); + fprintf(fp, " pageshift: %d\n", machdep->pageshift); + fprintf(fp, " pagemask: %llx\n", machdep->pagemask); + fprintf(fp, " pageoffset: %lx\n", machdep->pageoffset); + fprintf(fp, " pgdir_shift: %d\n", PGDIR_SHIFT); + fprintf(fp, " ptrs_per_pgd: %lu\n", PTRS_PER_PGD); + fprintf(fp, " ptrs_per_pte: %d\n", PTRS_PER_PTE); + fprintf(fp, " stacksize: %ld\n", machdep->stacksize); + fprintf(fp, " memsize: %lld (0x%llx)\n", + machdep->memsize, machdep->memsize); + fprintf(fp, " bits: %d\n", machdep->bits); + fprintf(fp, " nr_irqs: %d\n", machdep->nr_irqs); + fprintf(fp, " eframe_search: mips_eframe_search()\n"); + fprintf(fp, " back_trace: mips_back_trace_cmd()\n"); + fprintf(fp, " processor_speed: mips_processor_speed()\n"); + fprintf(fp, " uvtop: mips_uvtop()\n"); + fprintf(fp, " kvtop: mips_kvtop()\n"); + fprintf(fp, " get_task_pgd: mips_get_task_pgd()\n"); + fprintf(fp, " dump_irq: generic_dump_irq()\n"); + fprintf(fp, " show_interrupts: generic_show_interrupts()\n"); + fprintf(fp, " get_irq_affinity: generic_get_irq_affinity()\n"); + fprintf(fp, " get_stack_frame: mips_get_stack_frame()\n"); + fprintf(fp, " get_stackbase: generic_get_stackbase()\n"); + fprintf(fp, " get_stacktop: generic_get_stacktop()\n"); + fprintf(fp, " translate_pte: mips_translate_pte()\n"); + fprintf(fp, " memory_size: generic_memory_size()\n"); + fprintf(fp, " vmalloc_start: mips_vmalloc_start()\n"); + fprintf(fp, " is_task_addr: mips_is_task_addr()\n"); + fprintf(fp, " verify_symbol: mips_verify_symbol()\n"); + fprintf(fp, " dis_filter: generic_dis_filter()\n"); + fprintf(fp, " cmd_mach: mips_cmd_mach()\n"); + fprintf(fp, " get_smp_cpus: mips_get_smp_cpus()\n"); + fprintf(fp, " is_kvaddr: generic_is_kvaddr()\n"); + fprintf(fp, " is_uvaddr: generic_is_uvaddr()\n"); + fprintf(fp, " verify_paddr: generic_verify_paddr()\n"); + fprintf(fp, " init_kernel_pgd: NULL\n"); + fprintf(fp, " value_to_symbol: generic_machdep_value_to_symbol()\n"); + fprintf(fp, " line_number_hooks: NULL\n"); + fprintf(fp, " last_pgd_read: %lx\n", machdep->last_pgd_read); + fprintf(fp, " last_pmd_read: %lx\n", machdep->last_pmd_read); + fprintf(fp, " last_ptbl_read: %lx\n", machdep->last_ptbl_read); + fprintf(fp, " pgd: %lx\n", (ulong)machdep->pgd); + fprintf(fp, " pmd: %lx\n", (ulong)machdep->pmd); + fprintf(fp, " ptbl: %lx\n", (ulong)machdep->ptbl); + fprintf(fp, " section_size_bits: %ld\n", machdep->section_size_bits); + fprintf(fp, " max_physmem_bits: %ld\n", machdep->max_physmem_bits); + fprintf(fp, " sections_per_root: %ld\n", machdep->sections_per_root); + fprintf(fp, " machspec: %lx\n", (ulong)machdep->machspec); +} + +static ulong +mips_get_page_size(void) +{ + struct syment *spd, *next = NULL; + + spd = symbol_search("swapper_pg_dir"); + if (spd) + next = next_symbol(NULL, spd); + + if (!spd || !next) + return memory_page_size(); + + return next->value - spd->value; +} + +void +mips_init(int when) +{ +#if defined(__i386__) || defined(__x86_64__) + if (ACTIVE()) + error(FATAL, "compiled for the MIPS architecture\n"); +#endif + + switch (when) { + case PRE_SYMTAB: + machdep->verify_symbol = mips_verify_symbol; + machdep->machspec = &mips_machine_specific; + if (pc->flags & KERNEL_DEBUG_QUERY) + return; + machdep->last_pgd_read = 0; + machdep->last_pmd_read = 0; + machdep->last_ptbl_read = 0; + machdep->verify_paddr = generic_verify_paddr; + machdep->ptrs_per_pgd = PTRS_PER_PGD; + break; + + case PRE_GDB: + machdep->pagesize = mips_get_page_size(); + machdep->pageshift = ffs(machdep->pagesize) - 1; + machdep->pageoffset = machdep->pagesize - 1; + machdep->pagemask = ~((ulonglong)machdep->pageoffset); + if (machdep->pagesize >= 16384) + machdep->stacksize = machdep->pagesize; + else + machdep->stacksize = machdep->pagesize * 2; + + if ((machdep->pgd = malloc(PGD_SIZE)) == NULL) + error(FATAL, "cannot malloc pgd space."); + if ((machdep->ptbl = malloc(PAGESIZE())) == NULL) + error(FATAL, "cannot malloc ptbl space."); + + machdep->kvbase = 0x80000000; + machdep->identity_map_base = machdep->kvbase; + machdep->is_kvaddr = generic_is_kvaddr; + machdep->is_uvaddr = generic_is_uvaddr; + machdep->uvtop = mips_uvtop; + machdep->kvtop = mips_kvtop; + machdep->vmalloc_start = mips_vmalloc_start; + machdep->eframe_search = mips_eframe_search; + machdep->back_trace = mips_back_trace_cmd; + machdep->processor_speed = mips_processor_speed; + machdep->get_task_pgd = mips_get_task_pgd; + machdep->get_stack_frame = mips_get_stack_frame; + machdep->get_stackbase = generic_get_stackbase; + machdep->get_stacktop = generic_get_stacktop; + machdep->translate_pte = mips_translate_pte; + machdep->memory_size = generic_memory_size; + machdep->is_task_addr = mips_is_task_addr; + machdep->dis_filter = generic_dis_filter; + machdep->cmd_mach = mips_cmd_mach; + machdep->get_smp_cpus = mips_get_smp_cpus; + machdep->value_to_symbol = generic_machdep_value_to_symbol; + machdep->init_kernel_pgd = NULL; + break; + case POST_GDB: + mips_init_page_flags(); + machdep->dump_irq = generic_dump_irq; + machdep->show_interrupts = generic_show_interrupts; + machdep->get_irq_affinity = generic_get_irq_affinity; + ARRAY_LENGTH_INIT(machdep->nr_irqs, irq_desc, + "irq_desc", NULL, 0); + mips_stackframe_init(); + break; + } +} + +#endif /* MIPS */ diff --git a/netdump.c b/netdump.c index ce69310..410455b 100644 --- a/netdump.c +++ b/netdump.c @@ -198,6 +198,12 @@ is_netdump(char *file, ulong source_query) goto bailout; break; + case EM_MIPS: + if (machine_type_mismatch(file, "MIPS", NULL, + source_query)) + goto bailout; + break; + default: if (machine_type_mismatch(file, "(unknown)", NULL, source_query)) @@ -2390,6 +2396,10 @@ get_netdump_regs(struct bt_info *bt, ulong *eip, ulong *esp) return get_netdump_regs_arm64(bt, eip, esp); break; + case EM_MIPS: + return get_netdump_regs_32(bt, eip, esp); + break; + default: error(FATAL, "support for ELF machine type %d not available\n", diff --git a/symbols.c b/symbols.c index 6dfd769..59312e6 100644 --- a/symbols.c +++ b/symbols.c @@ -3236,6 +3236,11 @@ is_kernel(char *file) goto bailout; break; + case EM_MIPS: + if (machine_type_mismatch(file, "MIPS", NULL, 0)) + goto bailout; + break; + default: if (machine_type_mismatch(file, "(unknown)", NULL, 0)) goto bailout; @@ -7813,6 +7818,10 @@ dump_offset_table(char *spec, ulong makestruct) OFFSET(task_struct_thread_esp)); fprintf(fp, " task_struct_thread_ksp: %ld\n", OFFSET(task_struct_thread_ksp)); + fprintf(fp, " task_struct_thread_reg29: %ld\n", + OFFSET(task_struct_thread_reg29)); + fprintf(fp, " task_struct_thread_reg31: %ld\n", + OFFSET(task_struct_thread_reg31)); fprintf(fp, " task_struct_thread_context_fp: %ld\n", OFFSET(task_struct_thread_context_fp)); fprintf(fp, " task_struct_thread_context_sp: %ld\n", -- 2.1.4 -- Crash-utility mailing list Crash-utility@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/crash-utility