diff -Nru crash-7.1.5/arm64.c crash-7.1.7/arm64.c --- crash-7.1.5/arm64.c 2016-04-27 20:46:50.000000000 +0200 +++ crash-7.1.7/arm64.c 2016-11-30 19:56:29.000000000 +0100 @@ -20,6 +20,7 @@ #include "defs.h" #include #include +#include #define NOT_IMPLEMENTED(X) error((X), "%s: function not implemented\n", __func__) @@ -36,23 +37,30 @@ static int arm64_vtop_2level_64k(ulong, ulong, physaddr_t *, int); static int arm64_vtop_3level_64k(ulong, ulong, physaddr_t *, int); static int arm64_vtop_3level_4k(ulong, ulong, physaddr_t *, int); +static int arm64_vtop_4level_4k(ulong, ulong, physaddr_t *, int); static ulong arm64_get_task_pgd(ulong); static void arm64_irq_stack_init(void); static void arm64_stackframe_init(void); static int arm64_eframe_search(struct bt_info *); static int arm64_is_kernel_exception_frame(struct bt_info *, ulong); static int arm64_in_exception_text(ulong); +static int arm64_in_exp_entry(ulong); static void arm64_back_trace_cmd(struct bt_info *); +static void arm64_back_trace_cmd_v2(struct bt_info *); static void arm64_print_text_symbols(struct bt_info *, struct arm64_stackframe *, FILE *); static int arm64_print_stackframe_entry(struct bt_info *, int, struct arm64_stackframe *, FILE *); +static int arm64_print_stackframe_entry_v2(struct bt_info *, int, struct arm64_stackframe *, FILE *); static void arm64_display_full_frame(struct bt_info *, ulong); +static void arm64_display_full_frame_v2(struct bt_info *, struct arm64_stackframe *, struct arm64_stackframe *); static int arm64_unwind_frame(struct bt_info *, struct arm64_stackframe *); +static int arm64_unwind_frame_v2(struct bt_info *, struct arm64_stackframe *, FILE *); static int arm64_get_dumpfile_stackframe(struct bt_info *, struct arm64_stackframe *); static int arm64_in_kdump_text(struct bt_info *, struct arm64_stackframe *); static int arm64_in_kdump_text_on_irq_stack(struct bt_info *); -static void arm64_switch_stack(struct bt_info *, struct arm64_stackframe *, FILE *); +static int arm64_switch_stack(struct bt_info *, struct arm64_stackframe *, FILE *); static int arm64_get_stackframe(struct bt_info *, struct arm64_stackframe *); static void arm64_get_stack_frame(struct bt_info *, ulong *, ulong *); +static void arm64_gen_hidden_frame(struct bt_info *bt, ulong, struct arm64_stackframe *); static void arm64_print_exception_frame(struct bt_info *, ulong, int, FILE *); static void arm64_do_bt_reference_check(struct bt_info *, ulong, char *); static int arm64_translate_pte(ulong, void *, ulonglong); @@ -81,6 +89,7 @@ arm64_init(int when) { ulong value; + char *string; struct machine_specific *ms; #if defined(__x86_64__) @@ -102,9 +111,36 @@ if (machdep->cmdline_args[0]) arm64_parse_cmdline_args(); machdep->flags |= MACHDEP_BT_TEXT; + + ms = machdep->machspec; + + if (!ms->kimage_voffset && STREQ(pc->live_memsrc, "/dev/crash")) + ioctl(pc->mfd, DEV_CRASH_ARCH_DATA, &ms->kimage_voffset); + + if (!ms->kimage_voffset && + (string = pc->read_vmcoreinfo("NUMBER(kimage_voffset)"))) { + ms->kimage_voffset = htol(string, QUIET, NULL); + free(string); + } + + if (ms->kimage_voffset) { + machdep->flags |= NEW_VMEMMAP; + + /* + * Even if CONFIG_RANDOMIZE_BASE is not configured, + * derive_kaslr_offset() should work and set + * kt->relocate to 0 + */ + if (!kt->relocate && !(kt->flags2 & (RELOC_AUTO|KASLR))) + kt->flags2 |= (RELOC_AUTO|KASLR); + } + break; case PRE_GDB: + if (kernel_symbol_exists("kimage_voffset")) + machdep->flags |= NEW_VMEMMAP; + if (!machdep->pagesize) { /* * Kerneldoc Documentation/arm64/booting.txt describes @@ -160,32 +196,58 @@ machdep->pagemask = ~((ulonglong)machdep->pageoffset); arm64_calc_VA_BITS(); - machdep->machspec->page_offset = ARM64_PAGE_OFFSET; + ms = machdep->machspec; + ms->page_offset = ARM64_PAGE_OFFSET; machdep->identity_map_base = ARM64_PAGE_OFFSET; - machdep->machspec->userspace_top = ARM64_USERSPACE_TOP; - machdep->machspec->modules_vaddr = ARM64_MODULES_VADDR; - machdep->machspec->modules_end = ARM64_MODULES_END; - machdep->machspec->vmalloc_start_addr = ARM64_VMALLOC_START; - machdep->machspec->vmalloc_end = ARM64_VMALLOC_END; - machdep->kvbase = ARM64_VMALLOC_START; - machdep->machspec->vmemmap_vaddr = ARM64_VMEMMAP_VADDR; - machdep->machspec->vmemmap_end = ARM64_VMEMMAP_END; + machdep->kvbase = ARM64_VA_START; + ms->userspace_top = ARM64_USERSPACE_TOP; + if (machdep->flags & NEW_VMEMMAP) { + struct syment *sp; + + sp = kernel_symbol_search("_text"); + ms->kimage_text = (sp ? sp->value : 0); + sp = kernel_symbol_search("_end"); + ms->kimage_end = (sp ? sp->value : 0); + + ms->modules_vaddr = ARM64_VA_START; + if (kernel_symbol_exists("kasan_init")) + ms->modules_vaddr += ARM64_KASAN_SHADOW_SIZE; + ms->modules_end = ms->modules_vaddr + + ARM64_MODULES_VSIZE -1; + + ms->vmalloc_start_addr = ms->modules_end + 1; + } else { + ms->modules_vaddr = ARM64_PAGE_OFFSET - MEGABYTES(64); + ms->modules_end = ARM64_PAGE_OFFSET - 1; + ms->vmalloc_start_addr = ARM64_VA_START; + } + ms->vmalloc_end = ARM64_VMALLOC_END; + ms->vmemmap_vaddr = ARM64_VMEMMAP_VADDR; + ms->vmemmap_end = ARM64_VMEMMAP_END; switch (machdep->pagesize) { case 4096: - machdep->flags |= VM_L3_4K; machdep->ptrs_per_pgd = PTRS_PER_PGD_L3_4K; if ((machdep->pgd = (char *)malloc(PTRS_PER_PGD_L3_4K * 8)) == NULL) error(FATAL, "cannot malloc pgd space."); + if (machdep->machspec->VA_BITS > PGDIR_SHIFT_L4_4K) { + machdep->flags |= VM_L4_4K; + if ((machdep->pud = + (char *)malloc(PTRS_PER_PUD_L4_4K * 8)) + == NULL) + error(FATAL, "cannot malloc pud space."); + } else { + machdep->flags |= VM_L3_4K; + machdep->pud = NULL; /* not used */ + } if ((machdep->pmd = (char *)malloc(PTRS_PER_PMD_L3_4K * 8)) == NULL) error(FATAL, "cannot malloc pmd space."); if ((machdep->ptbl = (char *)malloc(PTRS_PER_PTE_L3_4K * 8)) == NULL) error(FATAL, "cannot malloc ptbl space."); - machdep->pud = NULL; /* not used */ break; case 65536: @@ -223,8 +285,8 @@ error(FATAL, "cannot determine page size\n"); } - machdep->last_pud_read = 0; /* not used */ machdep->last_pgd_read = 0; + machdep->last_pud_read = 0; machdep->last_pmd_read = 0; machdep->last_ptbl_read = 0; machdep->clear_machdep_cache = arm64_clear_machdep_cache; @@ -232,8 +294,6 @@ machdep->stacksize = ARM64_STACK_SIZE; machdep->flags |= VMEMMAP; - arm64_calc_phys_offset(); - machdep->uvtop = arm64_uvtop; machdep->kvtop = arm64_kvtop; machdep->is_kvaddr = generic_is_kvaddr; @@ -262,6 +322,18 @@ machdep->dumpfile_init = NULL; machdep->verify_line_number = NULL; machdep->init_kernel_pgd = arm64_init_kernel_pgd; + + /* use machdep parameters */ + arm64_calc_phys_offset(); + + if (CRASHDEBUG(1)) { + if (machdep->flags & NEW_VMEMMAP) + fprintf(fp, "kimage_voffset: %lx\n", + machdep->machspec->kimage_voffset); + fprintf(fp, "phys_offset: %lx\n", + machdep->machspec->phys_offset); + } + break; case POST_GDB: @@ -401,6 +473,8 @@ fprintf(fp, "%sVM_L3_64K", others++ ? "|" : ""); if (machdep->flags & VM_L3_4K) fprintf(fp, "%sVM_L3_4K", others++ ? "|" : ""); + if (machdep->flags & VM_L4_4K) + fprintf(fp, "%sVM_L4_4K", others++ ? "|" : ""); if (machdep->flags & VMEMMAP) fprintf(fp, "%sVMEMMAP", others++ ? "|" : ""); if (machdep->flags & KDUMP_ENABLED) @@ -409,6 +483,8 @@ fprintf(fp, "%sIRQ_STACKS", others++ ? "|" : ""); if (machdep->flags & MACHDEP_BT_TEXT) fprintf(fp, "%sMACHDEP_BT_TEXT", others++ ? "|" : ""); + if (machdep->flags & NEW_VMEMMAP) + fprintf(fp, "%sNEW_VMEMMAP", others++ ? "|" : ""); fprintf(fp, ")\n"); fprintf(fp, " kvbase: %lx\n", machdep->kvbase); @@ -425,17 +501,22 @@ fprintf(fp, " bits: %d\n", machdep->bits); fprintf(fp, " nr_irqs: %d\n", machdep->nr_irqs); fprintf(fp, " eframe_search: arm64_eframe_search()\n"); - fprintf(fp, " back_trace: arm64_back_trace_cmd()\n"); + fprintf(fp, " back_trace: arm64_back_trace_cmd() (default: %s method)\n", + kt->flags & USE_OPT_BT ? "optional" : "original"); fprintf(fp, " in_alternate_stack: arm64_in_alternate_stack()\n"); fprintf(fp, " processor_speed: arm64_processor_speed()\n"); fprintf(fp, " uvtop: arm64_uvtop()->%s()\n", machdep->flags & VM_L3_4K ? "arm64_vtop_3level_4k" : + machdep->flags & VM_L4_4K ? + "arm64_vtop_4level_4k" : machdep->flags & VM_L3_64K ? "arm64_vtop_3level_64k" : "arm64_vtop_2level_64k"); fprintf(fp, " kvtop: arm64_kvtop()->%s()\n", machdep->flags & VM_L3_4K ? "arm64_vtop_3level_4k" : + machdep->flags & VM_L4_4K ? + "arm64_vtop_4level_4k" : machdep->flags & VM_L3_64K ? "arm64_vtop_3level_64k" : "arm64_vtop_2level_64k"); fprintf(fp, " get_task_pgd: arm64_get_task_pgd()\n"); @@ -468,8 +549,13 @@ fprintf(fp, " xendump_panic_task: (n/a)\n"); fprintf(fp, " get_xendump_regs: (n/a)\n"); fprintf(fp, " line_number_hooks: (not used)\n"); - fprintf(fp, " last_pud_read: (not used)\n"); fprintf(fp, " last_pgd_read: %lx\n", machdep->last_pgd_read); + fprintf(fp, " last_pud_read: "); + if ((PAGESIZE() == 65536) || + ((PAGESIZE() == 4096) && !(machdep->flags & VM_L4_4K))) + fprintf(fp, "(not used)\n"); + else + fprintf(fp, "%lx\n", machdep->last_pud_read); fprintf(fp, " last_pmd_read: "); if (PAGESIZE() == 65536) fprintf(fp, "(not used)\n"); @@ -478,6 +564,7 @@ fprintf(fp, " last_ptbl_read: %lx\n", machdep->last_ptbl_read); fprintf(fp, " clear_machdep_cache: arm64_clear_machdep_cache()\n"); fprintf(fp, " pgd: %lx\n", (ulong)machdep->pgd); + fprintf(fp, " pud: %lx\n", (ulong)machdep->pud); fprintf(fp, " pmd: %lx\n", (ulong)machdep->pmd); fprintf(fp, " ptbl: %lx\n", (ulong)machdep->ptbl); fprintf(fp, " ptrs_per_pgd: %d\n", machdep->ptrs_per_pgd); @@ -503,9 +590,20 @@ fprintf(fp, " modules_end: %016lx\n", ms->modules_end); fprintf(fp, " vmemmap_vaddr: %016lx\n", ms->vmemmap_vaddr); fprintf(fp, " vmemmap_end: %016lx\n", ms->vmemmap_end); + if (machdep->flags & NEW_VMEMMAP) { + fprintf(fp, " kimage_text: %016lx\n", ms->kimage_text); + fprintf(fp, " kimage_end: %016lx\n", ms->kimage_end); + fprintf(fp, " kimage_voffset: %016lx\n", ms->kimage_voffset); + } fprintf(fp, " phys_offset: %lx\n", ms->phys_offset); fprintf(fp, "__exception_text_start: %lx\n", ms->__exception_text_start); fprintf(fp, " __exception_text_end: %lx\n", ms->__exception_text_end); + fprintf(fp, " __irqentry_text_start: %lx\n", ms->__irqentry_text_start); + fprintf(fp, " __irqentry_text_end: %lx\n", ms->__irqentry_text_end); + fprintf(fp, " exp_entry1_start: %lx\n", ms->exp_entry1_start); + fprintf(fp, " exp_entry1_end: %lx\n", ms->exp_entry1_end); + fprintf(fp, " exp_entry2_start: %lx\n", ms->exp_entry2_start); + fprintf(fp, " exp_entry2_end: %lx\n", ms->exp_entry2_end); fprintf(fp, " panic_task_regs: %lx\n", (ulong)ms->panic_task_regs); fprintf(fp, " PTE_PROT_NONE: %lx\n", ms->PTE_PROT_NONE); fprintf(fp, " PTE_FILE: "); @@ -532,6 +630,7 @@ fprintf(fp, " crash_save_cpu_start: %lx\n", ms->crash_save_cpu_start); fprintf(fp, " crash_save_cpu_end: %lx\n", ms->crash_save_cpu_end); fprintf(fp, " kernel_flags: %lx\n", ms->kernel_flags); + fprintf(fp, " irq_stackbuf: %lx\n", (ulong)ms->irq_stackbuf); if (machdep->flags & IRQ_STACKS) { fprintf(fp, " irq_stack_size: %ld\n", ms->irq_stack_size); for (i = 0; i < kt->cpus; i++) @@ -543,6 +642,42 @@ } } +static int +arm64_parse_machdep_arg_l(char *argstring, char *param, ulong *value) +{ + int len; + int megabytes = FALSE; + char *p; + + len = strlen(param); + if (!STRNEQ(argstring, param) || (argstring[len] != '=')) + return FALSE; + + if ((LASTCHAR(argstring) == 'm') || + (LASTCHAR(argstring) == 'M')) { + LASTCHAR(argstring) = NULLCHAR; + megabytes = TRUE; + } + + p = argstring + len + 1; + if (strlen(p)) { + int flags = RETURN_ON_ERROR | QUIET; + int err = 0; + + if (megabytes) { + *value = dtol(p, flags, &err); + if (!err) + *value = MEGABYTES(*value); + } else { + *value = htol(p, flags, &err); + } + + if (!err) + return TRUE; + } + + return FALSE; +} /* * Parse machine dependent command line arguments. @@ -554,11 +689,10 @@ static void arm64_parse_cmdline_args(void) { - int index, i, c, err; + int index, i, c; char *arglist[MAXARGS]; char buf[BUFSIZE]; char *p; - ulong value = 0; for (index = 0; index < MAX_MACHDEP_ARGS; index++) { if (!machdep->cmdline_args[index]) @@ -580,39 +714,19 @@ c = parse_line(buf, arglist); for (i = 0; i < c; i++) { - err = 0; - - if (STRNEQ(arglist[i], "phys_offset=")) { - int megabytes = FALSE; - int flags = RETURN_ON_ERROR | QUIET; - - if ((LASTCHAR(arglist[i]) == 'm') || - (LASTCHAR(arglist[i]) == 'M')) { - LASTCHAR(arglist[i]) = NULLCHAR; - megabytes = TRUE; - } - - p = arglist[i] + strlen("phys_offset="); - if (strlen(p)) { - if (megabytes) - value = dtol(p, flags, &err); - else - value = htol(p, flags, &err); - } - - if (!err) { - if (megabytes) - value = MEGABYTES(value); - - machdep->machspec->phys_offset = value; - - error(NOTE, - "setting phys_offset to: 0x%lx\n\n", - machdep->machspec->phys_offset); - - machdep->flags |= PHYS_OFFSET; - continue; - } + if (arm64_parse_machdep_arg_l(arglist[i], "phys_offset", + &machdep->machspec->phys_offset)) { + error(NOTE, + "setting phys_offset to: 0x%lx\n\n", + machdep->machspec->phys_offset); + machdep->flags |= PHYS_OFFSET; + continue; + } else if (arm64_parse_machdep_arg_l(arglist[i], "kimage_voffset", + &machdep->machspec->kimage_voffset)) { + error(NOTE, + "setting kimage_voffset to: 0x%lx\n\n", + machdep->machspec->kimage_voffset); + continue; } error(WARNING, "ignoring --machdep option: %s\n", @@ -641,9 +755,21 @@ char buf[BUFSIZE]; char *p1; int errflag; - FILE *fp; + FILE *iomem; + physaddr_t paddr; + struct syment *sp; + + if ((machdep->flags & NEW_VMEMMAP) && + ms->kimage_voffset && (sp = kernel_symbol_search("memstart_addr"))) { + paddr = sp->value - machdep->machspec->kimage_voffset; + if (READMEM(pc->mfd, &phys_offset, sizeof(phys_offset), + sp->value, paddr) > 0) { + ms->phys_offset = phys_offset; + return; + } + } - if ((fp = fopen("/proc/iomem", "r")) == NULL) + if ((iomem = fopen("/proc/iomem", "r")) == NULL) return; /* @@ -651,14 +777,14 @@ * first region which should be correct for most uses. */ errflag = 1; - while (fgets(buf, BUFSIZE, fp)) { + while (fgets(buf, BUFSIZE, iomem)) { if (strstr(buf, ": System RAM")) { clean_line(buf); errflag = 0; break; } } - fclose(fp); + fclose(iomem); if (errflag) return; @@ -692,11 +818,31 @@ /* - * Borrow the 32-bit ARM functionality. + * Determine PHYS_OFFSET either by reading VMCOREINFO or the kernel + * symbol, otherwise borrow the 32-bit ARM functionality. */ static int arm64_kdump_phys_base(ulong *phys_offset) { + char *string; + struct syment *sp; + physaddr_t paddr; + + if ((string = pc->read_vmcoreinfo("NUMBER(PHYS_OFFSET)"))) { + *phys_offset = htol(string, QUIET, NULL); + free(string); + return TRUE; + } + + if ((machdep->flags & NEW_VMEMMAP) && + machdep->machspec->kimage_voffset && + (sp = kernel_symbol_search("memstart_addr"))) { + paddr = sp->value - machdep->machspec->kimage_voffset; + if (READMEM(-1, phys_offset, sizeof(*phys_offset), + sp->value, paddr) > 0) + return TRUE; + } + return arm_kdump_phys_base(phys_offset); } @@ -721,6 +867,24 @@ vt->kernel_pgd[i] = value; } +ulong +arm64_VTOP(ulong addr) +{ + if (machdep->flags & NEW_VMEMMAP) { + if (addr >= machdep->machspec->page_offset) + return machdep->machspec->phys_offset + + (addr - machdep->machspec->page_offset); + else if (machdep->machspec->kimage_voffset) + return addr - machdep->machspec->kimage_voffset; + else /* no randomness */ + return machdep->machspec->phys_offset + + (addr - machdep->machspec->vmalloc_start_addr); + } else { + return machdep->machspec->phys_offset + + (addr - machdep->machspec->page_offset); + } +} + static int arm64_kvtop(struct task_context *tc, ulong kvaddr, physaddr_t *paddr, int verbose) { @@ -743,7 +907,7 @@ kernel_pgd = vt->kernel_pgd[0]; *paddr = 0; - switch (machdep->flags & (VM_L2_64K|VM_L3_64K|VM_L3_4K)) + switch (machdep->flags & (VM_L2_64K|VM_L3_64K|VM_L3_4K|VM_L4_4K)) { case VM_L2_64K: return arm64_vtop_2level_64k(kernel_pgd, kvaddr, paddr, verbose); @@ -751,6 +915,8 @@ return arm64_vtop_3level_64k(kernel_pgd, kvaddr, paddr, verbose); case VM_L3_4K: return arm64_vtop_3level_4k(kernel_pgd, kvaddr, paddr, verbose); + case VM_L4_4K: + return arm64_vtop_4level_4k(kernel_pgd, kvaddr, paddr, verbose); default: return FALSE; } @@ -766,7 +932,7 @@ *paddr = 0; - switch (machdep->flags & (VM_L2_64K|VM_L3_64K|VM_L3_4K)) + switch (machdep->flags & (VM_L2_64K|VM_L3_64K|VM_L3_4K|VM_L4_4K)) { case VM_L2_64K: return arm64_vtop_2level_64k(user_pgd, uvaddr, paddr, verbose); @@ -774,6 +940,8 @@ return arm64_vtop_3level_64k(user_pgd, uvaddr, paddr, verbose); case VM_L3_4K: return arm64_vtop_3level_4k(user_pgd, uvaddr, paddr, verbose); + case VM_L4_4K: + return arm64_vtop_4level_4k(user_pgd, uvaddr, paddr, verbose); default: return FALSE; } @@ -861,7 +1029,7 @@ pgd_base = (ulong *)pgd; FILL_PGD(pgd_base, KVADDR, PTRS_PER_PGD_L3_64K * sizeof(ulong)); pgd_ptr = pgd_base + (((vaddr) >> PGDIR_SHIFT_L3_64K) & (PTRS_PER_PGD_L3_64K - 1)); - pgd_val = ULONG(machdep->pgd + PAGEOFFSET(pgd_ptr)); + pgd_val = ULONG(machdep->pgd + PGDIR_OFFSET_L3_64K(pgd_ptr)); if (verbose) fprintf(fp, " PGD: %lx => %lx\n", (ulong)pgd_ptr, pgd_val); if (!pgd_val) @@ -992,6 +1160,85 @@ return FALSE; } +static int +arm64_vtop_4level_4k(ulong pgd, ulong vaddr, physaddr_t *paddr, int verbose) +{ + ulong *pgd_base, *pgd_ptr, pgd_val; + ulong *pud_base, *pud_ptr, pud_val; + ulong *pmd_base, *pmd_ptr, pmd_val; + ulong *pte_base, *pte_ptr, pte_val; + + if (verbose) + fprintf(fp, "PAGE DIRECTORY: %lx\n", pgd); + + pgd_base = (ulong *)pgd; + FILL_PGD(pgd_base, KVADDR, PTRS_PER_PGD_L4_4K * sizeof(ulong)); + pgd_ptr = pgd_base + (((vaddr) >> PGDIR_SHIFT_L4_4K) & (PTRS_PER_PGD_L4_4K - 1)); + pgd_val = ULONG(machdep->pgd + PGDIR_OFFSET_48VA(pgd_ptr)); + if (verbose) + fprintf(fp, " PGD: %lx => %lx\n", (ulong)pgd_ptr, pgd_val); + if (!pgd_val) + goto no_page; + + pud_base = (ulong *)PTOV(pgd_val & PHYS_MASK & PGDIR_MASK_48VA); + + FILL_PUD(pud_base, KVADDR, PTRS_PER_PUD_L4_4K * sizeof(ulong)); + pud_ptr = pud_base + (((vaddr) >> PUD_SHIFT_L4_4K) & (PTRS_PER_PUD_L4_4K - 1)); + pud_val = ULONG(machdep->pud + PAGEOFFSET(pud_ptr)); + if (verbose) + fprintf(fp, " PUD: %lx => %lx\n", (ulong)pud_ptr, pud_val); + if (!pud_val) + goto no_page; + + pmd_base = (ulong *)PTOV(pud_val & PHYS_MASK & (s32)machdep->pagemask); + FILL_PMD(pmd_base, KVADDR, PTRS_PER_PMD_L4_4K * sizeof(ulong)); + pmd_ptr = pmd_base + (((vaddr) >> PMD_SHIFT_L4_4K) & (PTRS_PER_PMD_L4_4K - 1)); + pmd_val = ULONG(machdep->pmd + PAGEOFFSET(pmd_ptr)); + if (verbose) + fprintf(fp, " PMD: %lx => %lx\n", (ulong)pmd_ptr, pmd_val); + if (!pmd_val) + goto no_page; + + if ((pmd_val & PMD_TYPE_MASK) == PMD_TYPE_SECT) { + ulong sectionbase = (pmd_val & SECTION_PAGE_MASK_2MB) & PHYS_MASK; + if (verbose) { + fprintf(fp, " PAGE: %lx (2MB)\n\n", sectionbase); + arm64_translate_pte(pmd_val, 0, 0); + } + *paddr = sectionbase + (vaddr & ~SECTION_PAGE_MASK_2MB); + return TRUE; + } + + pte_base = (ulong *)PTOV(pmd_val & PHYS_MASK & (s32)machdep->pagemask); + FILL_PTBL(pte_base, KVADDR, PTRS_PER_PTE_L4_4K * sizeof(ulong)); + pte_ptr = pte_base + (((vaddr) >> machdep->pageshift) & (PTRS_PER_PTE_L4_4K - 1)); + pte_val = ULONG(machdep->ptbl + PAGEOFFSET(pte_ptr)); + if (verbose) + fprintf(fp, " PTE: %lx => %lx\n", (ulong)pte_ptr, pte_val); + if (!pte_val) + goto no_page; + + if (pte_val & PTE_VALID) { + *paddr = (PAGEBASE(pte_val) & PHYS_MASK) + PAGEOFFSET(vaddr); + if (verbose) { + fprintf(fp, " PAGE: %lx\n\n", PAGEBASE(*paddr)); + arm64_translate_pte(pte_val, 0, 0); + } + } else { + if (IS_UVADDR(vaddr, NULL)) + *paddr = pte_val; + if (verbose) { + fprintf(fp, "\n"); + arm64_translate_pte(pte_val, 0, 0); + } + goto no_page; + } + + return TRUE; +no_page: + return FALSE; +} + static ulong arm64_get_task_pgd(ulong task) { @@ -1071,6 +1318,20 @@ symbol_value("__exception_text_start"); machdep->machspec->__exception_text_end = symbol_value("__exception_text_end"); + if ((sp1 = kernel_symbol_search("__irqentry_text_start")) && + (sp2 = kernel_symbol_search("__irqentry_text_end"))) { + machdep->machspec->__irqentry_text_start = sp1->value; + machdep->machspec->__irqentry_text_end = sp2->value; + } + if ((sp1 = kernel_symbol_search("vectors")) && + (sp1n = kernel_symbol_search("cpu_switch_to")) && + (sp2 = kernel_symbol_search("ret_fast_syscall")) && + (sp2n = kernel_symbol_search("sys_rt_sigreturn_wrapper"))) { + machdep->machspec->exp_entry1_start = sp1->value; + machdep->machspec->exp_entry1_end = sp1n->value; + machdep->machspec->exp_entry2_start = sp2->value; + machdep->machspec->exp_entry2_end = sp2n->value; + } if ((sp1 = kernel_symbol_search("crash_kexec")) && (sp1n = next_symbol(NULL, sp1)) && @@ -1186,6 +1447,8 @@ { case PSR_MODE_EL1t: case PSR_MODE_EL1h: + case PSR_MODE_EL2t: + case PSR_MODE_EL2h: return TRUE; } } @@ -1261,8 +1524,29 @@ { struct machine_specific *ms = machdep->machspec; - return((ptr >= ms->__exception_text_start) && - (ptr < ms->__exception_text_end)); + if ((ptr >= ms->__exception_text_start) && + (ptr < ms->__exception_text_end)) + return TRUE; + + if (ms->__irqentry_text_start && ms->__irqentry_text_end && + ((ptr >= ms->__irqentry_text_start) && + (ptr < ms->__irqentry_text_end))) + return TRUE; + + return FALSE; +} + +static int +arm64_in_exp_entry(ulong addr) +{ + struct machine_specific *ms; + + ms = machdep->machspec; + if ((ms->exp_entry1_start <= addr) && (addr < ms->exp_entry1_end)) + return TRUE; + if ((ms->exp_entry2_start <= addr) && (addr < ms->exp_entry2_end)) + return TRUE; + return FALSE; } #define BACKTRACE_CONTINUE (1) @@ -1273,39 +1557,52 @@ arm64_print_stackframe_entry(struct bt_info *bt, int level, struct arm64_stackframe *frame, FILE *ofp) { char *name, *name_plus_offset; - ulong symbol_offset; + ulong branch_pc, symbol_offset; struct syment *sp; struct load_module *lm; char buf[BUFSIZE]; - name = closest_symbol(frame->pc); + /* + * if pc comes from a saved lr, it actually points to an instruction + * after branch. To avoid any confusion, decrement pc by 4. + * See, for example, "bl schedule" before ret_to_user(). + */ + branch_pc = frame->pc - 4; + name = closest_symbol(branch_pc); name_plus_offset = NULL; if (bt->flags & BT_SYMBOL_OFFSET) { - sp = value_search(frame->pc, &symbol_offset); - if (sp && symbol_offset) - name_plus_offset = - value_to_symstr(frame->pc, buf, bt->radix); + sp = value_search(branch_pc, &symbol_offset); + if (sp && symbol_offset) + name_plus_offset = + value_to_symstr(branch_pc, buf, bt->radix); } + if (!INSTACK(frame->fp, bt) && IN_TASK_VMA(bt->task, frame->fp)) + frame->fp = 0; + if (bt->flags & BT_FULL) { - arm64_display_full_frame(bt, frame->sp); - bt->frameptr = frame->sp; + if (level) + arm64_display_full_frame(bt, frame->fp); + bt->frameptr = frame->fp; } fprintf(ofp, "%s#%d [%8lx] %s at %lx", level < 10 ? " " : "", level, - frame->sp, name_plus_offset ? name_plus_offset : name, frame->pc); + frame->fp ? frame->fp : bt->stacktop - USER_EFRAME_OFFSET, + name_plus_offset ? name_plus_offset : name, branch_pc); - if (BT_REFERENCE_CHECK(bt)) - arm64_do_bt_reference_check(bt, frame->pc, name); + if (BT_REFERENCE_CHECK(bt)) { + arm64_do_bt_reference_check(bt, frame->pc, closest_symbol(frame->pc)); + arm64_do_bt_reference_check(bt, branch_pc, name); + } - if (module_symbol(frame->pc, NULL, &lm, NULL, 0)) + if (module_symbol(branch_pc, NULL, &lm, NULL, 0)) fprintf(ofp, " [%s]", lm->mod_name); fprintf(ofp, "\n"); if (bt->flags & BT_LINE_NUMBERS) { - get_line_number(frame->pc, buf, FALSE); + get_line_number(branch_pc, buf, FALSE); if (strlen(buf)) fprintf(ofp, " %s\n", buf); } @@ -1317,6 +1614,59 @@ return BACKTRACE_CONTINUE; } +static int +arm64_print_stackframe_entry_v2(struct bt_info *bt, int level, struct arm64_stackframe *frame, FILE *ofp) +{ + char *name, *name_plus_offset; + ulong pc, symbol_offset; + struct syment *sp; + struct load_module *lm; + char buf[BUFSIZE]; + + /* + * if pc comes from a saved lr, it actually points to an instruction + * after branch. To avoid any confusion, decrement pc by 4. + * See, for example, "bl schedule" before ret_to_user(). + */ + pc = frame->pc - 0x4; + name = closest_symbol(pc); + name_plus_offset = NULL; + + if (bt->flags & BT_SYMBOL_OFFSET) { + sp = value_search(pc, &symbol_offset); + if (sp && symbol_offset) + name_plus_offset = value_to_symstr(pc, buf, bt->radix); + } + + if (bt->flags & BT_USER_EFRAME) + frame->fp = 0; + + fprintf(ofp, "%s#%d [%8lx] %s at %lx", level < 10 ? " " : "", level, + frame->fp ? frame->fp : bt->stacktop - USER_EFRAME_OFFSET, + name_plus_offset ? name_plus_offset : name, pc); + + if (BT_REFERENCE_CHECK(bt)) + arm64_do_bt_reference_check(bt, pc, name); + + if (module_symbol(pc, NULL, &lm, NULL, 0)) + fprintf(ofp, " [%s]", lm->mod_name); + + fprintf(ofp, "\n"); + + if (bt->flags & BT_LINE_NUMBERS) { + get_line_number(pc, buf, FALSE); + if (strlen(buf)) + fprintf(ofp, " %s\n", buf); + } + + if (STREQ(name, "start_kernel") || + STREQ(name, "secondary_start_kernel") || + STREQ(name, "kthread") || STREQ(name, "kthreadd")) + return BACKTRACE_COMPLETE_KERNEL; + + return BACKTRACE_CONTINUE; +} + static void arm64_display_full_frame(struct bt_info *bt, ulong sp) { @@ -1328,8 +1678,12 @@ if (bt->frameptr == sp) return; - if (!INSTACK(sp, bt) || !INSTACK(bt->frameptr, bt)) - return; + if (!INSTACK(sp, bt) || !INSTACK(bt->frameptr, bt)) { + if (sp == 0) + sp = bt->stacktop - USER_EFRAME_OFFSET; + else + return; + } words = (sp - bt->frameptr) / sizeof(ulong); @@ -1347,17 +1701,77 @@ fprintf(fp, "\n"); } +static void +arm64_display_full_frame_v2(struct bt_info *bt, struct arm64_stackframe *cur, + struct arm64_stackframe *next) +{ + struct machine_specific *ms; + ulong next_fp, stackbase; + char *stackbuf; + int i, u_idx; + ulong *up; + ulong words, addr; + char buf[BUFSIZE]; + + stackbase = bt->stackbase; + stackbuf = bt->stackbuf; + ms = machdep->machspec; + + /* Calc next fp for dump */ + if (next->fp == 0) + /* last stackframe on kernel tack */ + next_fp = bt->stacktop - 0x10; + else if (!INSTACK(cur->sp, bt)) { + /* We have just switched over stacks */ + next_fp = ms->irq_stacks[bt->tc->processor] + + ms->irq_stack_size - 0x10; + + /* + * We are already buffering a process stack. + * So use an old buffer for IRQ stack. + */ + stackbase = ms->irq_stacks[bt->tc->processor]; + stackbuf = ms->irq_stackbuf; + } else + next_fp = next->fp; + + if (CRASHDEBUG(1)) + fprintf(fp, " frame <%016lx:%016lx>\n", cur->fp, next_fp); + + /* Check here because we want to see a debug message above. */ + if (!(bt->flags & BT_FULL)) + return; + if (next_fp <= cur->fp) + return; + + /* Dump */ + words = (next_fp - cur->fp) / sizeof(ulong); + addr = cur->fp; + u_idx = (cur->fp - stackbase)/sizeof(ulong); + for (i = 0; i < words; i++, u_idx++) { + if (!(i & 1)) + fprintf(fp, "%s %lx: ", i ? "\n" : "", addr); + + up = (ulong *)(&stackbuf[u_idx*sizeof(ulong)]); + fprintf(fp, "%s ", format_stack_entry(bt, buf, *up, 0)); + + addr += sizeof(ulong); + } + fprintf(fp, "\n"); + + if (stackbuf == ms->irq_stackbuf) + FREEBUF(stackbuf); +} + static int arm64_unwind_frame(struct bt_info *bt, struct arm64_stackframe *frame) { unsigned long high, low, fp; unsigned long stack_mask; - unsigned long irq_stack_ptr, orig_sp, sp_in; + unsigned long irq_stack_ptr, orig_sp; struct arm64_pt_regs *ptregs; struct machine_specific *ms; - sp_in = frame->sp; - stack_mask = (unsigned long)(ARM64_STACK_SIZE) - 1; fp = frame->fp; @@ -1390,11 +1804,11 @@ if (frame->sp == irq_stack_ptr) { orig_sp = GET_STACK_ULONG(irq_stack_ptr - 8); arm64_set_process_stack(bt); - if (INSTACK(orig_sp, bt) && INSTACK(frame->fp, bt)) { + if (INSTACK(orig_sp, bt) && (INSTACK(frame->fp, bt) || (frame->fp == 0))) { ptregs = (struct arm64_pt_regs *)&bt->stackbuf[(ulong)(STACK_OFFSET_TYPE(orig_sp))]; frame->sp = orig_sp; frame->pc = ptregs->pc; - bt->bptr = sp_in; + bt->bptr = fp; if (CRASHDEBUG(1)) error(INFO, "arm64_unwind_frame: switch stacks: fp: %lx sp: %lx pc: %lx\n", @@ -1412,6 +1826,307 @@ return TRUE; } +/* + * The following figure shows how unwinding can be done. + * Here we assume that the callstack order is: + * #(X-1) ppc (previous PC) + * #X cpc (current PC) + * < #(X+ 1) epc (Exception entry) > + * #(X+1/2) npc (Next PC) + * #(X+2/3) Npc (One before Next) + * #(X+3/4) NNpc (One before 'Npc') + * and unwind frames from #X to #(X+1). + * When we add a faked frame for exception entry (exception frame) + * as #(X+1), the next frame for npc will be recognized as #(x+2). + * + * (1)Normal stackframe: + * +------+ + * | pfp | + * | cpc | + * psp + + + * | | + * | | + * pfp +------+ <--- :prev stackframe = + * | cfp | + * | npc | + * csp + + + * | | + * | | + * cfp +------+ <--- :curr stackframe = + * | nfp | cfp = *pfp + * | Npc | csp = pfp + 0x10 + * nsp + + + * | | + * | | + * nfp +------+ <--- :next stackframe = + * | | + * + * (2)Exception on the same (IRQ or process) stack: + * +------+ + * | pfp | + * | cpc | + * psp + + + * | | + * | | + * pfp +------+ <--- :prev stackframe = + * | cfp | + * | npc | + * csp + + + * | | + * | | + * cfp +------+ <--- :curr stackframe = + * | nfp | + * | epc | + * + + + * | | + * | | faked(*) + * esp +------+ <--- :excp stackframe = <---, esp, epc + * | | esp = nsp - sizeof(pt_regs) + * | | + * | Npc | (*) If we didn't add this frame, the next frame + * | nfp | would be + * | nsp | + * | npc | and the frame below for npc would be lost. + * nsp + + + * | | + * nfp +------+ <--- :task stackframe = + * | Nfp | + * | NNpc | + * Nsp + + + * | | + * Nfp +------+ <--- :task stackframe = + * | NNfp | + * + * (3)Interrupt: + * +------+ + * | cfp | + * | ipc | + * csp + + + * | | + * | | + * cfp +------+ <--- :curr stackframe = + * | ifp | + * | epc | + * isp + + + * | | + * | | (*) + * ifp +------+ <--- :irq stackframe = + * | nfp | ifp == IRQ_STACK_PTR + * | esp | (*) Before the kernel enters an irq handler, frame + * top +------+ pointer moves to the top of IRQ stack. + * IRQ stack So we have to skip this frame in unwinding. + * + * faked + * esp +------+ <--- :excp stackframe = <---, esp, epc> + * | | esp = nsp - sizeof(pt_regs) + * | | + * | Npc | + * | nfp | + * | nsp | + * | npc | + * nsp + + + * | | + * nfp +------+ <--- :task stackframe = + * | Nfp | + * | NNpc | + * Nsp + + + * | | + * Nfp +------+ <--- :task stackframe = + * | NNfp | + */ + +static struct arm64_stackframe ext_frame; + +static int +arm64_unwind_frame_v2(struct bt_info *bt, struct arm64_stackframe *frame, + FILE *ofp) +{ + unsigned long high, low, fp; + unsigned long stack_mask; + unsigned long irq_stack_ptr; + struct machine_specific *ms; + + stack_mask = (unsigned long)(ARM64_STACK_SIZE) - 1; + fp = frame->fp; + + low = frame->sp; + high = (low + stack_mask) & ~(stack_mask); + + if (fp < low || fp > high || fp & 0xf) + return FALSE; + + if (CRASHDEBUG(1)) + fprintf(ofp, " cur fp:%016lx sp:%016lx pc:%016lx\n", + frame->fp, frame->sp, frame->pc); + + if (ext_frame.pc) { + /* + * The previous frame was a dummy for exception entry. + * So complement a missing (task) stackframe now. + */ + frame->fp = ext_frame.fp; + frame->sp = ext_frame.sp; + frame->pc = ext_frame.pc; + + ext_frame.pc = 0; /* back to normal unwinding */ + + goto unwind_done; + } + + frame->pc = GET_STACK_ULONG(fp + 8); + if (!arm64_in_exp_entry(frame->pc)) { + /* (1) Normal stack frame */ + + frame->sp = fp + 0x10; + frame->fp = GET_STACK_ULONG(fp); + } else { + /* + * We are in exception entry code, and so + * - add a faked frame for exception entry, and + * - prepare for a stackframe hidden by exception + */ + + ext_frame.fp = GET_STACK_ULONG(fp); + /* + * Note: + * In the following code, we determine a stack pointer for + * exception entry based on ext_frame.fp because we have + * no way to know a ext_frame.sp. + * Fortunately, this will work fine for most functions + * in the kernel. + */ + if (ext_frame.fp == 0) { + /* + * (2) + * Either on process stack or on IRQ stack, + * the next frame is the last one on process stack. + */ + + frame->sp = bt->stacktop + - sizeof(struct arm64_pt_regs) - 0x10; + frame->fp = frame->sp; + } else if (!arm64_on_irq_stack(bt->tc->processor, frame->sp)) { + /* + * (2) + * We are on process stack. Just add a faked frame + */ + + if (!arm64_on_irq_stack(bt->tc->processor, ext_frame.fp)) + frame->sp = ext_frame.fp + - sizeof(struct arm64_pt_regs); + else { + /* + * FIXME: very exceptional case + * We are already back on process stack, but + * a saved frame pointer indicates that we are + * on IRQ stack. Unfortunately this can happen + * when some functions are called after + * an irq handler is done because irq_exit() + * doesn't restore a frame pointer (x29). + * Those functions include + * - do_notify_resume() + * - trace_hardirqs_off() + * - schedule() + * + * We have no perfect way to determine a true + * stack pointer value here. + * 0x20 is a stackframe size of schedule(). + * Really ugly + */ + frame->sp = frame->fp + 0x20; + fprintf(ofp, " (Next exception frame might be wrong)\n"); + } + + frame->fp = frame->sp; + } else { + /* We are on IRQ stack */ + + ms = machdep->machspec; + irq_stack_ptr = ms->irq_stacks[bt->tc->processor] + + ms->irq_stack_size - 0x20; + if (ext_frame.fp != irq_stack_ptr) { + /* (2) Just add a faked frame */ + + frame->sp = ext_frame.fp + - sizeof(struct arm64_pt_regs); + frame->fp = frame->sp; + } else { + /* + * (3) + * Switch from IRQ stack to process stack + */ + + frame->sp = GET_STACK_ULONG(irq_stack_ptr + 8); + frame->fp = frame->sp; + + /* + * Keep a buffer for a while until + * displaying the last frame on IRQ stack + * at next arm64_print_stackframe_entry_v2() + */ + if (bt->flags & BT_FULL) + ms->irq_stackbuf = bt->stackbuf; + + arm64_set_process_stack(bt); + } + } + + /* prepare for a stackframe hidden by exception */ + arm64_gen_hidden_frame(bt, frame->sp, &ext_frame); + } + +unwind_done: + if (CRASHDEBUG(1)) + fprintf(ofp, " nxt fp:%016lx sp:%016lx pc:%016lx\n", + frame->fp, frame->sp, frame->pc); + + return TRUE; +} + +/* + * A layout of a stack frame in a function looks like: + * + * stack grows to lower addresses. + * /|\ + * | + * | | + * new sp +------+ <--- + * |dyn | | + * | vars | | + * new fp +- - - + | + * |old fp| | a function's stack frame + * |old lr| | + * |static| | + * | vars| | + * old sp +------+ <--- + * |dyn | + * | vars | + * old fp +------+ + * | | + * + * - On function entry, sp is decremented down to new fp. + * + * - and old fp and sp are saved into this stack frame. + * "Static" local variables are allocated at the same time. + * + * - Later on, "dynamic" local variables may be allocated on a stack. + * But those dynamic variables are rarely used in the kernel image, + * and, as a matter of fact, sp is equal to fp in almost all functions. + * (not 100% though.) + * + * - Currently, sp is determined in arm64_unwind_frame() by + * sp = a callee's fp + 0x10 + * where 0x10 stands for a saved area for fp and sp + * + * - As you can see, however, this calculated sp still points to the top of + * callee's static local variables and doesn't match with a *real* sp. + * + * - So, generally, dumping a stack from this calculated sp to the next frame's + * sp shows "callee's static local variables", old fp and sp. + * + * Diagram and explanation courtesy of Takahiro Akashi + */ + static void arm64_back_trace_cmd(struct bt_info *bt) { @@ -1420,6 +2135,11 @@ ulong exception_frame; FILE *ofp; + if (bt->flags & BT_OPT_BACK_TRACE) { + arm64_back_trace_cmd_v2(bt); + return; + } + ofp = BT_REFERENCE_CHECK(bt) ? pc->nullfp : fp; /* @@ -1502,13 +2222,17 @@ if (!arm64_unwind_frame(bt, &stackframe)) break; - if (arm64_in_exception_text(bt->instptr) && INSTACK(stackframe.fp, bt)) - exception_frame = stackframe.fp - SIZE(pt_regs); + if (arm64_in_exception_text(bt->instptr) && INSTACK(stackframe.fp, bt)) { + if (!(bt->flags & BT_IRQSTACK) || + (((stackframe.sp + SIZE(pt_regs)) < bt->stacktop))) + exception_frame = stackframe.fp - SIZE(pt_regs); + } if ((bt->flags & BT_IRQSTACK) && !arm64_on_irq_stack(bt->tc->processor, stackframe.sp)) { - arm64_switch_stack(bt, &stackframe, ofp); bt->flags &= ~BT_IRQSTACK; + if (arm64_switch_stack(bt, &stackframe, ofp) == USER_MODE) + break; } @@ -1525,6 +2249,133 @@ fprintf(ofp, " #0 [user space]\n"); } +static void +arm64_back_trace_cmd_v2(struct bt_info *bt) +{ + struct arm64_stackframe stackframe, cur_frame; + int level, mode; + ulong exception_frame; + FILE *ofp; + + ofp = BT_REFERENCE_CHECK(bt) ? pc->nullfp : fp; + + /* + * stackframes are created from 3 contiguous stack addresses: + * + * x: contains stackframe.fp -- points to next triplet + * x+8: contains stackframe.pc -- text return address + * x+16: is the stackframe.sp address + */ + + if (bt->flags & BT_KDUMP_ADJUST) { + if (arm64_on_irq_stack(bt->tc->processor, bt->bptr)) { + arm64_set_irq_stack(bt); + bt->flags |= BT_IRQSTACK; + } + stackframe.fp = GET_STACK_ULONG(bt->bptr); + stackframe.pc = GET_STACK_ULONG(bt->bptr + 8); + stackframe.sp = bt->bptr + 16; + bt->frameptr = stackframe.fp; + } else { + stackframe.sp = bt->stkptr; + stackframe.pc = bt->instptr; + stackframe.fp = bt->frameptr; + } + + if (bt->flags & BT_TEXT_SYMBOLS) { + arm64_print_text_symbols(bt, &stackframe, ofp); + if (BT_REFERENCE_FOUND(bt)) { + print_task_header(fp, task_to_context(bt->task), 0); + arm64_print_text_symbols(bt, &stackframe, fp); + fprintf(fp, "\n"); + } + return; + } + + if (bt->flags & BT_REGS_NOT_FOUND) + return; + + if (!(bt->flags & BT_KDUMP_ADJUST)) { + if (bt->flags & BT_USER_SPACE) { +user_space: + exception_frame = bt->stacktop - USER_EFRAME_OFFSET; + arm64_print_exception_frame(bt, exception_frame, + USER_MODE, ofp); +// fprintf(ofp, " #0 [user space]\n"); + + return; + } + + if (DUMPFILE() && is_task_active(bt->task)) { + exception_frame = stackframe.fp - SIZE(pt_regs); + if (arm64_is_kernel_exception_frame(bt, exception_frame)) + arm64_print_exception_frame(bt, exception_frame, + KERNEL_MODE, ofp); + } + } + + for (level = 0;; level++) { + bt->instptr = stackframe.pc; + + /* + * Show one-line stackframe info + */ + if (arm64_print_stackframe_entry_v2(bt, level, &stackframe, ofp) + == BACKTRACE_COMPLETE_KERNEL) + break; + + cur_frame = stackframe; + if (!arm64_unwind_frame_v2(bt, &stackframe, ofp)) + break; + + /* + * Dump the contents of the current stackframe. + * We need to know the next stackframe to determine + * the dump range: + * + */ + arm64_display_full_frame_v2(bt, &cur_frame, &stackframe); + + /* + * If we are in a normal stackframe, just continue, + * otherwise show an exception frame. + * Since exception entry code doesn't have a real + * stackframe, we fake a dummy frame here. + */ + if (!arm64_in_exp_entry(stackframe.pc)) + continue; + + if (!INSTACK(cur_frame.sp, bt)) + fprintf(ofp, "--- ---\n"); + + arm64_print_stackframe_entry_v2(bt, ++level, &stackframe, ofp); + if (bt->flags & BT_USER_EFRAME) + goto user_space; + cur_frame = stackframe; + arm64_unwind_frame_v2(bt, &stackframe, ofp); + + /* + * and don't show the contenxts. Instead, + * show an exception frame below + */ + + if (!INSTACK(cur_frame.sp, bt)) { + /* This check is a safeguard. See unwind_frame(). */ + error(WARNING, + "stack pointer for exception frame is wrong\n"); + return; + } + mode = (stackframe.pc < machdep->machspec->userspace_top) ? + USER_MODE : KERNEL_MODE; +// fprintf(ofp, "--- ---\n", +// mode == KERNEL_MODE ? "kernel" : "user"); + arm64_print_exception_frame(bt, cur_frame.sp, mode, ofp); + + if (mode == USER_MODE) + break; + } +} + static void arm64_print_text_symbols(struct bt_info *bt, struct arm64_stackframe *frame, FILE *ofp) { @@ -1591,17 +2442,38 @@ ms = machdep->machspec; for (ptr = start - 8; ptr >= base; ptr--) { - if ((*ptr >= ms->crash_kexec_start) && (*ptr < ms->crash_kexec_end)) { - bt->bptr = ((ulong)ptr - (ulong)base) + bt->tc->thread_info; - if (CRASHDEBUG(1)) - fprintf(fp, "%lx: %lx (crash_kexec)\n", bt->bptr, *ptr); - return TRUE; - } - if ((*ptr >= ms->crash_save_cpu_start) && (*ptr < ms->crash_save_cpu_end)) { - bt->bptr = ((ulong)ptr - (ulong)base) + bt->tc->thread_info; - if (CRASHDEBUG(1)) - fprintf(fp, "%lx: %lx (crash_save_cpu)\n", bt->bptr, *ptr); - return TRUE; + if (bt->flags & BT_OPT_BACK_TRACE) { + if ((*ptr >= ms->crash_kexec_start) && + (*ptr < ms->crash_kexec_end) && + INSTACK(*(ptr - 1), bt)) { + bt->bptr = ((ulong)(ptr - 1) - (ulong)base) + + bt->tc->thread_info; + if (CRASHDEBUG(1)) + fprintf(fp, "%lx: %lx (crash_kexec)\n", bt->bptr, *ptr); + return TRUE; + } + if ((*ptr >= ms->crash_save_cpu_start) && + (*ptr < ms->crash_save_cpu_end) && + INSTACK(*(ptr - 1), bt)) { + bt->bptr = ((ulong)(ptr - 1) - (ulong)base) + + bt->tc->thread_info; + if (CRASHDEBUG(1)) + fprintf(fp, "%lx: %lx (crash_save_cpu)\n", bt->bptr, *ptr); + return TRUE; + } + } else { + if ((*ptr >= ms->crash_kexec_start) && (*ptr < ms->crash_kexec_end)) { + bt->bptr = ((ulong)ptr - (ulong)base) + bt->tc->thread_info; + if (CRASHDEBUG(1)) + fprintf(fp, "%lx: %lx (crash_kexec)\n", bt->bptr, *ptr); + return TRUE; + } + if ((*ptr >= ms->crash_save_cpu_start) && (*ptr < ms->crash_save_cpu_end)) { + bt->bptr = ((ulong)ptr - (ulong)base) + bt->tc->thread_info; + if (CRASHDEBUG(1)) + fprintf(fp, "%lx: %lx (crash_save_cpu)\n", bt->bptr, *ptr); + return TRUE; + } } } @@ -1636,21 +2508,44 @@ start = (ulong *)(stackbuf + ms->irq_stack_size); for (ptr = start - 8; ptr >= base; ptr--) { - if ((*ptr >= ms->crash_kexec_start) && (*ptr < ms->crash_kexec_end)) { - bt->bptr = ((ulong)ptr - (ulong)base) + stackbase; - if (CRASHDEBUG(1)) - fprintf(fp, "%lx: %lx (crash_kexec on IRQ stack)\n", - bt->bptr, *ptr); - FREEBUF(stackbuf); - return TRUE; - } - if ((*ptr >= ms->crash_save_cpu_start) && (*ptr < ms->crash_save_cpu_end)) { - bt->bptr = ((ulong)ptr - (ulong)base) + stackbase; - if (CRASHDEBUG(1)) - fprintf(fp, "%lx: %lx (crash_save_cpu on IRQ stack)\n", - bt->bptr, *ptr); - FREEBUF(stackbuf); - return TRUE; + if (bt->flags & BT_OPT_BACK_TRACE) { + if ((*ptr >= ms->crash_kexec_start) && + (*ptr < ms->crash_kexec_end) && + INSTACK(*(ptr - 1), bt)) { + bt->bptr = ((ulong)(ptr - 1) - (ulong)base) + stackbase; + if (CRASHDEBUG(1)) + fprintf(fp, "%lx: %lx (crash_kexec on IRQ stack)\n", + bt->bptr, *ptr); + FREEBUF(stackbuf); + return TRUE; + } + if ((*ptr >= ms->crash_save_cpu_start) && + (*ptr < ms->crash_save_cpu_end) && + INSTACK(*(ptr - 1), bt)) { + bt->bptr = ((ulong)(ptr - 1) - (ulong)base) + stackbase; + if (CRASHDEBUG(1)) + fprintf(fp, "%lx: %lx (crash_save_cpu on IRQ stack)\n", + bt->bptr, *ptr); + FREEBUF(stackbuf); + return TRUE; + } + } else { + if ((*ptr >= ms->crash_kexec_start) && (*ptr < ms->crash_kexec_end)) { + bt->bptr = ((ulong)ptr - (ulong)base) + stackbase; + if (CRASHDEBUG(1)) + fprintf(fp, "%lx: %lx (crash_kexec on IRQ stack)\n", + bt->bptr, *ptr); + FREEBUF(stackbuf); + return TRUE; + } + if ((*ptr >= ms->crash_save_cpu_start) && (*ptr < ms->crash_save_cpu_end)) { + bt->bptr = ((ulong)ptr - (ulong)base) + stackbase; + if (CRASHDEBUG(1)) + fprintf(fp, "%lx: %lx (crash_save_cpu on IRQ stack)\n", + bt->bptr, *ptr); + FREEBUF(stackbuf); + return TRUE; + } } } @@ -1658,7 +2553,7 @@ return FALSE; } -static void +static int arm64_switch_stack(struct bt_info *bt, struct arm64_stackframe *frame, FILE *ofp) { int i; @@ -1685,7 +2580,12 @@ FREEBUF(stackbuf); } fprintf(ofp, "--- ---\n"); + + if (frame->fp == 0) + return USER_MODE; + arm64_print_exception_frame(bt, frame->sp, KERNEL_MODE, ofp); + return KERNEL_MODE; } static int @@ -1709,13 +2609,17 @@ if (is_kernel_text(frame->pc) || !in_user_stack(bt->tc->task, frame->sp)) { error(WARNING, - "corrupt prstatus? pstate=0x%lx, but no user frame found\n", + "corrupt NT_PRSTATUS? pstate: 0x%lx, but no user frame found\n", ptregs->pstate); + if (is_kernel_text(frame->pc) && + INSTACK(frame->sp, bt) && INSTACK(frame->fp, bt)) + goto try_kernel; bt->flags |= BT_REGS_NOT_FOUND; return FALSE; } bt->flags |= BT_USER_SPACE; } else { +try_kernel: frame->sp = ptregs->sp; frame->fp = ptregs->regs[29]; } @@ -1764,6 +2668,25 @@ } static void +arm64_gen_hidden_frame(struct bt_info *bt, ulong sp, + struct arm64_stackframe *frame) +{ + struct arm64_pt_regs *ptregs; + + if (IN_TASK_VMA(bt->task, sp)) { + bt->flags |= BT_USER_EFRAME; + return; + } + + ptregs = (struct arm64_pt_regs *) + &bt->stackbuf[(ulong)(STACK_OFFSET_TYPE(sp))]; + + frame->pc = ptregs->pc; + frame->fp = ptregs->regs[29]; + frame->sp = ptregs->sp; +} + +static void arm64_print_exception_frame(struct bt_info *bt, ulong pt_regs, int mode, FILE *ofp) { int i, r, rows, top_reg, is_64_bit; @@ -1772,13 +2695,11 @@ ulong LR, SP, offset; char buf[BUFSIZE]; - if (bt->flags & BT_FULL) - arm64_display_full_frame(bt, pt_regs); - if (CRASHDEBUG(1)) fprintf(ofp, "pt_regs: %lx\n", pt_regs); - regs = (struct arm64_pt_regs *)&bt->stackbuf[(ulong)(STACK_OFFSET_TYPE(pt_regs))]; + regs = (struct arm64_pt_regs *) + &bt->stackbuf[(ulong)(STACK_OFFSET_TYPE(pt_regs))]; if ((mode == USER_MODE) && (regs->pstate & PSR_MODE32_BIT)) { LR = regs->regs[14]; @@ -1844,17 +2765,18 @@ if ((i == 0) && !is_64_bit) fprintf(ofp, "\n"); else if ((i == 0) || ((r % rows) == 0)) - fprintf(ofp, "\n "); + fprintf(ofp, "\n%s", + (i == 0) && (mode == KERNEL_MODE) ? "" : " "); else fprintf(ofp, "%s", is_64_bit ? " " : " "); } if (is_64_bit) { - fprintf(ofp, "ORIG_X0: %016lx SYSCALLNO: %lx", - (ulong)regs->orig_x0, (ulong)regs->syscallno); - if (mode == USER_MODE) - fprintf(ofp, " PSTATE: %08lx", (ulong)regs->pstate); - fprintf(ofp, "\n"); + if (mode == USER_MODE) { + fprintf(ofp, "ORIG_X0: %016lx SYSCALLNO: %lx", + (ulong)regs->orig_x0, (ulong)regs->syscallno); + fprintf(ofp, " PSTATE: %08lx\n", (ulong)regs->pstate); + } } if (is_kernel_text(regs->pc) && (bt->flags & BT_LINE_NUMBERS)) { @@ -1865,6 +2787,8 @@ if (BT_REFERENCE_CHECK(bt)) { arm64_do_bt_reference_check(bt, regs->pc, NULL); + if ((sp = value_search(regs->pc, &offset))) + arm64_do_bt_reference_check(bt, 0, sp->name); arm64_do_bt_reference_check(bt, LR, NULL); arm64_do_bt_reference_check(bt, SP, NULL); arm64_do_bt_reference_check(bt, regs->pstate, NULL); @@ -2029,6 +2953,60 @@ return (IS_KVADDR(task) && (ALIGNED_STACK_OFFSET(task) == 0)); } +static ulong +PLT_veneer_to_kvaddr(ulong value) +{ + uint32_t insn; + ulong addr = 0; + int i; + + /* + * PLT veneer always looks: + * movn x16, #0x.... + * movk x16, #0x...., lsl #16 + * movk x16, #0x...., lsl #32 + * br x16 + */ + for (i = 0; i < 4; i++) { + if (!readmem(value + i * sizeof(insn), KVADDR, &insn, + sizeof(insn), "PLT veneer", RETURN_ON_ERROR)) { + error(WARNING, "cannot read PLT veneer instruction at %lx\n", + value + i * sizeof(insn)); + return value; + } + switch (i) { + case 0: + if ((insn & 0xffe0001f) != 0x92800010) + goto not_plt; + addr = ~((ulong)(insn & 0x1fffe0) >> 5); + break; + case 1: + if ((insn & 0xffe0001f) != 0xf2a00010) + goto not_plt; + addr &= 0xffffffff0000ffff; + addr |= (ulong)(insn & 0x1fffe0) << (16 - 5); + break; + case 2: + if ((insn & 0xffe0001f) != 0xf2c00010) + goto not_plt; + addr &= 0xffff0000ffffffff; + addr |= (ulong)(insn & 0x1fffe0) << (32 - 5); + break; + case 3: + if (insn != 0xd61f0200) + goto not_plt; + break; + default: + return value; /* to avoid any warnings */ + } + } + + return addr; + +not_plt: + return value; +} + /* * Filter dissassembly output if the output radix is not gdb's default 10 */ @@ -2078,6 +3056,22 @@ sprintf(p1, "%s", buf1); } + if (IS_MODULE_VADDR(vaddr)) { + ulong orig_value; + + p1 = &inbuf[strlen(inbuf)-1]; + strcpy(buf1, inbuf); + argc = parse_line(buf1, argv); + + if ((STREQ(argv[argc-2], "b") || STREQ(argv[argc-2], "bl")) && + extract_hex(argv[argc-1], &orig_value, NULLCHAR, TRUE)) { + value = PLT_veneer_to_kvaddr(orig_value); + sprintf(p1, " <%s%s>\n", + value == orig_value ? "" : "plt:", + value_to_symstr(value, buf2, output_radix)); + } + } + console(" %s", inbuf); return TRUE; @@ -2377,6 +3371,11 @@ { struct machine_specific *ms = machdep->machspec; + if ((machdep->flags & NEW_VMEMMAP) && + (vaddr >= machdep->machspec->kimage_text) && + (vaddr <= machdep->machspec->kimage_end)) + return FALSE; + return ((vaddr >= ms->vmalloc_start_addr && vaddr <= ms->vmalloc_end) || ((machdep->flags & VMEMMAP) && (vaddr >= ms->vmemmap_vaddr && vaddr <= ms->vmemmap_end)) || @@ -2407,7 +3406,10 @@ for (bitval = highest_bit_long(value); bitval; bitval--) { if ((value & (1UL << bitval)) == 0) { - machdep->machspec->VA_BITS = bitval + 2; + if (machdep->flags & NEW_VMEMMAP) + machdep->machspec->VA_BITS = bitval + 1; + else + machdep->machspec->VA_BITS = bitval + 2; break; } } @@ -2448,7 +3450,7 @@ STRUCT_SIZE_INIT(page, "page"); - switch (machdep->flags & (VM_L2_64K|VM_L3_64K|VM_L3_4K)) + switch (machdep->flags & (VM_L2_64K|VM_L3_64K|VM_L3_4K|VM_L4_4K)) { case VM_L2_64K: case VM_L3_64K: @@ -2456,13 +3458,27 @@ break; case VM_L3_4K: PUD_SIZE = PGDIR_SIZE_L3_4K; + case VM_L4_4K: + PUD_SIZE = PUD_SIZE_L4_4K; break; } - vmemmap_size = ALIGN((1UL << (ms->VA_BITS - machdep->pageshift)) * SIZE(page), PUD_SIZE); + if (machdep->flags & NEW_VMEMMAP) +#define STRUCT_PAGE_MAX_SHIFT 6 + vmemmap_size = 1UL << (ms->VA_BITS - machdep->pageshift - 1 + + STRUCT_PAGE_MAX_SHIFT); + else + vmemmap_size = ALIGN((1UL << (ms->VA_BITS - machdep->pageshift)) * SIZE(page), PUD_SIZE); + vmalloc_end = (ms->page_offset - PUD_SIZE - vmemmap_size - SZ_64K); - vmemmap_start = vmalloc_end + SZ_64K; - vmemmap_end = vmemmap_start + vmemmap_size; + + if (machdep->flags & NEW_VMEMMAP) { + vmemmap_start = ms->page_offset - vmemmap_size; + vmemmap_end = ms->page_offset; + } else { + vmemmap_start = vmalloc_end + SZ_64K; + vmemmap_end = vmemmap_start + vmemmap_size; + } ms->vmalloc_end = vmalloc_end - 1; ms->vmemmap_vaddr = vmemmap_start; diff -Nru crash-7.1.5/crash.8 crash-7.1.7/crash.8 --- crash-7.1.5/crash.8 2016-04-27 20:46:50.000000000 +0200 +++ crash-7.1.7/crash.8 2016-11-30 19:56:29.000000000 +0100 @@ -140,7 +140,10 @@ with each dumpfile containing a contiguous block of RAM, where the ADDRESS value is the physical start address of the block expressed in hexadecimal. The physical address value(s) will be used to create a temporary ELF header -in /var/tmp, which will only exist during the crash session. +in /var/tmp, which will only exist during the crash session. If a raw RAM +dumpile represents a live memory source, such as that specified by the QEMU +mem-path argument of a memory-backend-file object, then "live:" must be +prepended to the MEMORY-IMAGE name. .TP .BI mapfile If the NAMELIST file is not the same kernel that is @@ -271,6 +274,7 @@ phys_base= ARM64: phys_offset= + kimage_voffset= .fi .RE .TP diff -Nru crash-7.1.5/debian/changelog crash-7.1.7/debian/changelog --- crash-7.1.5/debian/changelog 2017-01-18 17:31:01.000000000 +0100 +++ crash-7.1.7/debian/changelog 2017-01-18 17:31:06.000000000 +0100 @@ -1,8 +1,442 @@ -crash (7.1.5-1ubuntu2) yakkety; urgency=medium +crash (7.1.7-1ubuntu1) zesty; urgency=low - * Crash cannot parse Linux 4.8 vmcore files on ppc64el. (LP: #1628173) + * Merge from Debian unstable. (LP: #1657488) Remaining changes: + - Build for armhf, ppc64el. + - Minor fixes for live autopkgtest. + - debian/tests/live: Redirect gpg stderr to stdout, as that's the only + known and expected stderr source. + - Simplify ddeb archive requirements. - -- Chris J Arges Tue, 27 Sep 2016 14:58:43 -0500 + -- Louis Bouchard Wed, 18 Jan 2017 16:23:44 +0100 + +crash (7.1.7-1) unstable; urgency=medium + + * New upstream 7.1.7 (Closes: #843731): + + * Remove -pie compile option (Closes: #844831) + + * Set the default 32-bit MIPS HZ value to 100 if the in-kernel config data + is unavailable, and have the "mach" command display the value. + + * Enable SPARSEMEM support on 32-bit MIPS by setting SECTION_SIZE_BITS and + MAX_PHYSMEM_BITS. + + * Fix for Linux 4.9-rc1 commits 15f4eae70d365bba26854c90b6002aaabb18c8aa and + c65eacbe290b8141554c71b2c94489e73ade8c8d, which have introduced a new + CONFIG_THREAD_INFO_IN_TASK configuration. This configuration moves each + task's thread_info structure from the base of its kernel stack into its + task_struct. Without the patch, the crash session fails during + initialization with the error "crash: invalid structure member offset: + thread_info_cpu". + + * Fixes for the gathering of the active task registers from 32-bit MIPS + dumpfiles: (1) If ELF notes are not available, read them from the kernel's + crash_notes. (2) If an online CPUs did not save its ELF notes, then + adjust the mapping of each ELF note to its CPU accordingly. + + * Add support for "help -r" on 32-bit MIPS to display the registers for each + CPU from a dumpfile. + + * Fix for Linux 4.9-rc1 commit 0100301bfdf56a2a370c7157b5ab0fbf9313e1cd, + which rewrote the x86_64 switch_to() code by embedding the call to + __switch_to() inside a new __switch_to_asm() assembly code ENTRY() + function. Without the patch, the message "crash: cannot determine thread + return address" gets displayed during initialization, and the "bt" command + shows frame #0 starting at "schedule" instead of "__schedule". + + * When each x86_64 per-cpu cpu_tss.x86_tss.ist[] array member (or in older + kernels, each per-cpu init_tss.x86_hw_tss.ist[] array member), is compared + with its associated per-cpu orig_ist.ist[] array member, ensure that both + exception stack pointers have been initialized (non-NULL) before printing + a WARNING message if they don't match. + + * Fix for a possible segmentation violation when analyzing Linux 4.7 x86_64 + kernels that are configured with CONFIG_RANDOMIZE_BASE. Depending upon + the randomized starting address of the kernel text and static data, a + segmentation violation may occur during session initialization, just after + the patching of the gdb minimal_symbol values message. + + * Restore the x86_64 "dis" command's symbolic translation of jump or call + target addresses if the kernel was configured with CONFIG_RANDOMIZE_BASE. + + * Fix for the 32-bit MIPS "bt" command to prevent an empty display (task + header only) for an active task if the epc register in its exception frame + contains 00000000. + + * Fix for support of Linux 4.7 and later x86_64 ELF kdump vmcores from + kernels configured with CONFIG_RANDOMIZE_BASE. Without the patch, the + crash session may fail during initialization with the message "crash: + vmlinux and vmcore do not match!". + + * Fix for the x86_64 "mach" command display of the vmemmap base address in + Linux 4.9 and later kernels configured with CONFIG_RANDOMIZE_BASE. + Without the patch, the command shows a value of ffffea0000000000 next to + "KERNEL VMEMMAP BASE". + + * Since the Linux 3.10 release, the kernel has offered the ability to create + multiple independent ftrace buffers. At present, however, the "trace.c" + extension module is only able to extract the primary buffer. This patch + refactors the trace.c extension module so that the global instance is + passed around as a parameter rather than accessing it directly, and then + locates all of the available instances and extracts the data from each of + them. + + * Fix for the s390x "bt" command for active tasks. Since the commit above + in this crash-7.1.7 release that added support for the new + CONFIG_THREAD_INFO_IN_TASK configuration, the backtrace of active tasks + can be incomplete. + + * In collaboration with an update to the /dev/crash kernel driver, fix for + Linux 4.6 commit a7f8de168ace487fa7b88cb154e413cf40e87fc6, which allows + the ARM64 kernel image to be loaded anywhere in physical memory. Without + the patch, attempting to run live on an ARM64 Linux 4.6 and later kernel + may display the warning message "WARNING: cannot read linux_banner + string", and then fails with the message "crash: vmlinux and /dev/crash do + not match!". Version 1.3 of the crash driver is required, which + introduces a new ioctl command that retrieves the ARM64-only + "kimage_voffset" value that is required for virtual-to-physical address + translation. + + * Update of the sample memory_driver/crash.c /dev/crash kernel driver to + version 1.3, which adds support for Linux 4.6 and later ARM64 kernels, + kernels configured with CONFIG_HARDENED_USERCOPY, and S390X kernels use + xlate_dev_mem_ptr() and unxlate_dev_mem_ptr() instead of kmap() and + kunmap(). + + + -- Troy Heber Fri, 02 Dec 2016 11:57:29 -0700 + +crash (7.1.6-1) unstable; urgency=medium + + * New upstream 7.1.5 (Closes: #843731): + + * Introduction of support for "live" ramdump files, such as those that are + specified by the QEMU mem-path argument of a memory-backend-file object. + This allows the running of a live crash session against a QEMU guest from + the host machine. In this example, the /tmp/MEM file on a QEMU host + represents the guest's physical memory: + + $ qemu-kvm ...other-options... \ + -object memory-backend-file,id=MEM,size=128m,mem-path=/tmp/MEM,share=on \ + -numa node,memdev=MEM -m 128 + + and a live session run can be run against the guest kernel like so: + + $ crash live:/tmp/MEM@0 + + By prepending the ramdump image name with "live:", the crash session will + act as if it were running a normal live session. + + * Fix for the support of ELF vmcores created by the KVM "virsh dump + --memory-only" facility if the guest kernel was not configured with + CONFIG_KEXEC, or CONFIG_KEXEC_CORE in Linux 4.3 and later kernels. + Without the patch, the crash session fails during initialization with the + message "crash: cannot resolve kexec_crash_image". + + * Added support for x86_64 ramdump files. Without the patch, the crash + session fails immediately with the message "ramdump: unsupported machine + type: X86_64". + + * Fix for a "[-Werror=misleading-indentation]" compiler warning that is + generated by gdb-7.6/bfd/elf64-s390.c when building S390X in a Fedora + Rawhide environment with gcc-6.0.0 + + * Recognize and parse the new QEMU_VM_CONFIGURATION and QEMU_VM_FOOTER + sections used for live migration of KVM guests, which are seen in the + "kvmdump" format generated if "virsh dump" is used without the + "--memory-only" option. + + * Fix for Linux commit edf14cdbf9a0e5ab52698ca66d07a76ade0d5c46, which has + appended a NULL entry as the final member of the pageflag_names[] array. + Without the patch, a message that indicates "crash: failed to read + pageflag_names entry" is displayed during session initialization in Linux + 4.6 kernels. + + * Fix for Linux commit 0139aa7b7fa12ceef095d99dc36606a5b10ab83a, which + renamed the page._count member to page._refcount. Without the patch, + certain "kmem" commands fail with the "kmem: invalid structure member + offset: page_count". + + * Fix for an ARM64 crash-7.1.5 "bt" regression for a task that has called + panic(). Without the patch, the backtrace may fail with a message such as + "bt: WARNING: corrupt prstatus? pstate=0x20000000, but no user frame + found" followed by "bt: WARNING: cannot determine starting stack frame for + task
". The pstate register warning will still be displayed (as + it is essentially a kdump bug), but the backtrace will proceed normally. + + * Fix for the ARM64 "bt" command in Linux 4.5 and later kernels which use + per-cpu IRQ stacks. Without the patch, if an active non-crashing task was + running in user space when it received the shutdown IPI from the crashing + task, the "-- ---" transition marker from the IRQ stack to the + process stack is not displayed, and a message indicating "bt: WARNING: + arm64_unwind_frame: on IRQ stack: oriq_sp:
fp: 0 (?)" gets + displayed. + + * Fix for the ARM64 "bt" command in Linux 4.5 and later kernels which are + not configured with CONFIG_FUNCTION_GRAPH_TRACER. Without the patch, + backtraces that originate from a per-cpu IRQ stack will dump an invalid + exception frame before transitioning to the process stack. + + * Introduction of ARM64 support for 4K pages with 4-level page tables and 48 + VA bits. + + * Implemented support for the redesigned ARM64 kernel virtual memory layout + and associated KASLR support that was introduced in Linux 4.6. The kernel + text and static data has been moved from unity-mapped memory into the + vmalloc region, and its start address can be randomized if + CONFIG_RANDOMIZE_BASE is configured. Related support is being put into + the kernel's kdump code, the kexec-tools package, and makedumpfile(8); + with that in place, the analysis of Linux 4.6 ARM64 dumpfiles with or + without KASLR enabled should work normally by entering "crash vmlinux + vmcore". On live systems, Linux 4.6 ARM64 kernels will only work + automatically if CONFIG_RANDOMIZE_BASE is not configured. Unfortunately, + if CONFIG_RANDOMIZE_BASE is configured on a live system, two --machdep + command line arguments are required, at least for the time being. The + arguments are: + + --machdep phys_offset= + --machdep kimage_voffset= + + Without the patch, any attempt to analyze a Linux 4.6 ARM64 kernel fails + during initialization with a stream of "read error" messages followed by + "crash: vmlinux and vmcore do not match!". + + * Linux 3.15 and later kernels configured with CONFIG_RANDOMIZE_BASE could + be identified because of the "randomize_modules" kernel symbol, and if it + existed, the "--kaslr=" and/or "--kaslr=auto" options were + unnecessary. Since the "randomize_modules" symbol was removed in Linux + 4.1, this patch has replaced the KASLR identifier with the + "module_load_offset" symbol, which was also introduced in Linux 3.15, but + still remains. + + * Improvement of the ARM64 "bt -f" display such that in most cases, each + stack frame level delimiter will be set to the stack address location + containing the old FP and old LR pair. + + * Fix for the introduction of ARM64 support for 64K pages with 3-level page + tables in crash-7.1.5, which fails to translate user space virtual + addresses. Without the patch, "vtop " fails to + translate all user-space addresses, and any command that needs to either + translate or read user-space memory, such as "vm -p", "ps -a", and "rd -u" + will fail. + + * Enhancement of the error message generated by the "tree -t radix" option + when a duplicate entry is encountered. Without the patch, the error + message shows the address of the radix_tree_node that contains the + duplicate entry, for example, "tree: duplicate tree entry: + ". It has been changed to also display the + radix_tree_node.slots[] array index and the duplicate entry value, for + example, "tree: duplicate tree entry: radix_tree_node: + slots[]: ". + + * Introduction of a new "bt -v" option that checks the kernel stack of all + tasks for evidence of stack overflows. It does so by verifying the + thread_info.task address, ensuring the thread_info.cpu value is a valid + cpu number, and checking the end of the stack for the STACK_END_MAGIC + value. + + * Fix to recognize a kernel thread that has user space virtual memory + attached to it. While kernel threads typically do not have an mm_struct + referencing a user-space virtual address space, they can either + temporarily reference one for a user-space copy operation, or in the case + of KVM "vhost" kernel threads, keep a reference to the user space of the + "quem-kvm" task that created them. Without the patch, they will be + mistaken for user tasks; the "bt" command will display an invalid + kernel-entry exception frame that indicates "[exception RIP: unknown or + invalid address]", the "ps" command will not enclose the command name with + brackets, and the "ps -[uk]" and "foreach [user|kernel]" options will show + the kernel thread as a user task. + + * Fix for the "bt -[eE]" options on ARM64 to recognize kernel exception + frames in VHE enabled systems, in which the kernel runs in EL2. + + * Fix for the extensions/trace.c extension module to account for the Linux + 4.7 kernel commit dcb0b5575d24 that changed the bit index for the + TRACE_EVENT_FL_TRACEPOINT flag. Without the patch, the "extend" command + fails to load the trace.so module, with the error message "extend: + /path/to/crash/extensions/trace.so: no commands registered: shared object + unloaded". The patch reads the flag's enum value dynamically instead of + using a hard-coded value. + + * Incorporated Takahiro Akashi's alternative backtrace method as a "bt" + option, which can be accessed using "bt -o", and where "bt -O" will toggle + the original and optional methods as the default. The original backtrace + method has adopted two changes/features from the optional method: + (1) ORIG_X0 and SYSCALLNO registers are not displayed in kernel + exception frames. + (2) stackframe entry text locations are modified to be the PC + address of the branch instruction instead of the subsequent + "return" PC address contained in the stackframe link register. + Accordingly, these are the essential differences between the original and + optional methods: + (1) optional: the backtrace will start with the IPI exception frame + located on the process stack. + (2) original: the starting point of backtraces for the active, + non-crashing, tasks, will continue to have crash_save_cpu() + on the IRQ stack as the starting point. + (3) optional: the exception entry stackframe adjusted to be located + farther down in the IRQ stack. + (4) optional: bt -f does not display IRQ stack memory above the + adjusted exception entry stackframe. + (5) optional: may display "(Next exception frame might be wrong)". + + * Fix for the failure of the "sym " option in the extremely unlikely + case where the symbol's name string is composed entirely of hexadecimal + characters. For example, without the patch, "sym e820" fails with the + error message "sym: invalid address: e820". + + * Fix for the failure of the "dis " option in the extremely unlikely + case where the symbol's name string is composed entirely of hexadecimal + characters. For example, without the patch, "dis f" fails with the error + message "dis: WARNING: f: no associated kernel symbol found" followed by + "0xf: Cannot access memory at address 0xf". + + * Fix for the X86_64 "bt -R " option if the only reference to the + kernel text symbol in a backtrace is contained within the "[exception RIP: + ]" line of an exception frame dump. Without the patch, the + reference will only be picked up if the exception RIP's hexadecimal + address value is used. + + * Fix for the ARM64 "bt -R " option if the only reference to the + kernel text symbol in a backtrace is contained within the "[PC:
+ []" line of an exception frame dump. Without the patch, + the reference will only be picked up if the PC's hexadecimal address value + is used. + + * Fix for the gathering of module symbol name strings during session + initialization. In the unlikely case where the ordering of module symbol + name strings does not match the order of the kernel_symbol structures, a + faulty module symbol list entry may be created that contains a bogus name + string. + + * Fix the PERCENTAGE of total output of the "kmem -i" SWAP USED line when + the system has no swap pages at all. Without the patch, both the PAGES + and TOTAL columns show values of zero, but it confusingly shows "100% of + TOTAL SWAP", which upon first glance may seem to indicate potential memory + pressure. + + * Enhancement to determine structure member data if the member is contained + within an anonymous structure or union. Without the patch, it is + necessary to parse the output of a discrete gdb "printf" command to + determine the offset of such a structure member. + + * Speed up session initialization by attempting MEMBER_OFFSET_INIT() before + falling back to ANON_MEMBER_OFFSET_INIT() in several known cases of + structure members that are contained within anonymous structures. + + * Implemented new "list -S" and "tree -S" options that are similar to each + command's -s option, but instead of parsing gdb output, member values are + read directly from memory, so the command is much faster for 1-, 2-, 4-, + and 8-byte members. + + * Fix to recognize and support x86_64 Linux 4.8-rc1 and later kernels that + are configured with CONFIG_RANDOMIZE_MEMORY, which randomizes the base + addresses of the kernel's unity-map address (PAGE_OFFSET), and the vmalloc + region. Without the patch, the crash utility fails with a segmentation + violation during session initialization on a live system, or will generate + a number of WARNING messages followed by the fatal error message "crash: + vmlinux and do not match!" with dumpfiles. + + * Fix for Linux 4.1 commit d0a0de21f82bbc1737ea3c831f018d0c2bc6b9c2, which + renamed the x86_64 "init_tss" per-cpu variable to "cpu_tss". Without the + patch, the addresses of the 4 per-cpu exception stacks cannot be + determined, which causes backtraces that originate on any of the per-cpu + DOUBLEFAULT, NMI, DEBUG, or MCE stacks to be truncated. + + * With the introduction of radix MMU in Power ISA 3.0, there are changes in + kernel page table management accommodating it. This patch series makes + appropriate changes here to work for such kernels. Also, this series + fixes a few bugs along the way: + + ppc64: fix vtop page translation for 4K pages + ppc64: Use kernel terminology for each level in 4-level page table + ppc64/book3s: address changes in kernel v4.5 + ppc64/book3s: address change in page flags for PowerISA v3.0 + ppc64: use physical addresses and unfold pud for 64K page size + ppc64/book3s: support big endian Linux page tables + + The patches are needed for Linux v4.5 and later kernels on all ppc64 + hardware. + + * Fix for Linux 4.8-rc1 commit 500462a9de657f86edaa102f8ab6bff7f7e43fc2, in + which Thomas Gleixner redesigned the kernel timer mechanism to switch to a + non-cascading wheel. Without the patch, the "timer" command fails with + the message "timer: zero-size memory allocation! (called from
)" + + * Support for PPC64/BOOK3S virtual address translation for radix MMU. As + both radix and hash MMU are supported in a single kernel on Power ISA 3.0 + based server processors, identify the current MMU type and set page table + index values accordingly. Also, in Linux 4.7 and later kernels, + PPC64/BOOK3S uses the same masked bit values in page table entries for 4K + and 64K page sizes. + + * Change the RESIZEBUF() macro so that it will accept buffer pointers that + are not declared as "char *" types. Change two prior direct callers of + resizebuf() to use RESIZEBUF(), and fix two prior users of RESIZEBUF() to + correctly calculate the need to resize their buffers. + + * Fix for the "trace.so" extension module to properly recognize Linux 3.15 + and later kernels. In crash-7.1.6, the MEMBER_OFFSET() macro has been + improved so that it is able to recognize members of embedded anonymous + structures. However, the module's manner of recognizing Linux 3.15 and + later kernels depended upon MEMBER_OFFSET() failing to handle anonymous + members, and therefore the improvement prevented the module from + successfully loading. + + * If a "struct" command address argument is expressed using the per-cpu + "symbol:cpuspec" format, and the symbol is a pointer type, i.e., not the + address of the structure, display a WARNING message. + + * Exclude ARM64 kernel module linker mapping symbols like "$d" and "$x" as + is done with 32-bit ARM. Without the patch, a crash session may fail + during the "gathering module symbol data" stage with a message similar to + "crash: store_module_symbols_v2: total: 15 mcnt: 16". + + * Enhancement to the ARM64 "dis" command when the kernel has enabled KASLR. + When KASLR is enabled on ARM64, a function call between a module and the + base kernel code will be done via a veneer (PLT) if the displacement is + more than +/-128MB. As a result, disassembled code will show a branch to + the in-module veneer location instead of the in-kernel target location. + To avoid confusion, the output of the "dis" command will translate the + veneer location to the target location preceded by "plt:", for example, + "". + + * Improvement of the "dev -d" option to display I/O statics for disks whose + device driver uses the blk-mq interface. Currently "dev -d" always + displays 0 in all fields for the blk-mq disk because blk-mq does not + increment/decrement request_list.count[2] on I/O creation and I/O + completion. The following values are used in blk-mq in such situations: + + - I/O creation: blk_mq_ctx.rq_dispatched[2] + - I/O completion: blk_mq_ctx.rq_completed[2] + + So, we can get the counter of in-progress I/Os as follows: in progress + I/Os == rq_dispatched - rq_completed This patch displays the result of + above calculation for the disk. It determines whether the device driver + uses blk-mq if the request_queue.mq_ops is not NULL. The "DRV" field is + displayed as "N/A(MQ)" if the value for in-flight in the device driver + does not exist for blk-mq. + + + -- Troy Heber Wed, 16 Nov 2016 12:10:40 -0700 + +crash (7.1.5-4) unstable; urgency=medium + + * Fix FTBFS due compiler warnings in elf64-s390.c + + -- Troy Heber Wed, 05 Oct 2016 07:47:33 -0600 + +crash (7.1.5-3) unstable; urgency=medium + + * Update to packages-arch-specific to enable arm64 + + -- Troy Heber Mon, 03 Oct 2016 13:23:51 -0600 + +crash (7.1.5-2) unstable; urgency=medium + + * Forgot to close bug in the last upload (Closes: #811604) + + -- Troy Heber Tue, 06 Sep 2016 15:20:09 -0600 crash (7.1.5-1ubuntu1) yakkety; urgency=low diff -Nru crash-7.1.5/debian/Makefile.ori crash-7.1.7/debian/Makefile.ori --- crash-7.1.5/debian/Makefile.ori 2017-01-18 17:31:01.000000000 +0100 +++ crash-7.1.7/debian/Makefile.ori 2017-01-18 17:31:06.000000000 +0100 @@ -554,7 +554,7 @@ tar cvzf ${PROGRAM}.tar.gz ${TAR_FILES} ${GDB_FILES} ${GDB_PATCH_FILES} @echo; ls -l ${PROGRAM}.tar.gz -VERSION=7.1.5 +VERSION=7.1.7 RELEASE=0 release: make_configure diff -Nru crash-7.1.5/debian/patches/Fix-for-Linux-commit-0139aa7b7fa12ceef095d99dc36606a.patch crash-7.1.7/debian/patches/Fix-for-Linux-commit-0139aa7b7fa12ceef095d99dc36606a.patch --- crash-7.1.5/debian/patches/Fix-for-Linux-commit-0139aa7b7fa12ceef095d99dc36606a.patch 2017-01-18 17:31:01.000000000 +0100 +++ crash-7.1.7/debian/patches/Fix-for-Linux-commit-0139aa7b7fa12ceef095d99dc36606a.patch 1970-01-01 01:00:00.000000000 +0100 @@ -1,26 +0,0 @@ -From 8ceb1ac628bf6a0a7f0bbfff030ec93081bca4cd Mon Sep 17 00:00:00 2001 -From: Dave Anderson -Date: Mon, 23 May 2016 11:23:01 -0400 -Subject: [PATCH] Fix for Linux commit - 0139aa7b7fa12ceef095d99dc36606a5b10ab83a, which renamed the page._count - member to page._refcount. Without the patch, certain "kmem" commands fail - with the "kmem: invalid structure member offset: page_count". - (anderson@redhat.com) - ---- - memory.c | 4 ++++ - 1 file changed, 4 insertions(+) - ---- a/memory.c -+++ b/memory.c -@@ -434,6 +434,10 @@ - MEMBER_OFFSET_INIT(page_count, "page", "_count"); - if (INVALID_MEMBER(page_count)) - ANON_MEMBER_OFFSET_INIT(page_count, "page", "_count"); -+ if (INVALID_MEMBER(page_count)) -+ MEMBER_OFFSET_INIT(page_count, "page", "_refcount"); -+ if (INVALID_MEMBER(page_count)) -+ ANON_MEMBER_OFFSET_INIT(page_count, "page", "_refcount"); - } - MEMBER_OFFSET_INIT(page_flags, "page", "flags"); - MEMBER_SIZE_INIT(page_flags, "page", "flags"); diff -Nru crash-7.1.5/debian/patches/series crash-7.1.7/debian/patches/series --- crash-7.1.5/debian/patches/series 2017-01-18 17:31:01.000000000 +0100 +++ crash-7.1.7/debian/patches/series 1970-01-01 01:00:00.000000000 +0100 @@ -1,2 +0,0 @@ -Fix-for-Linux-commit-0139aa7b7fa12ceef095d99dc36606a.patch -With-the-introduction-of-radix-MMU-in-Power-ISA-3.0-.patch diff -Nru crash-7.1.5/debian/patches/With-the-introduction-of-radix-MMU-in-Power-ISA-3.0-.patch crash-7.1.7/debian/patches/With-the-introduction-of-radix-MMU-in-Power-ISA-3.0-.patch --- crash-7.1.5/debian/patches/With-the-introduction-of-radix-MMU-in-Power-ISA-3.0-.patch 2017-01-18 17:31:01.000000000 +0100 +++ crash-7.1.7/debian/patches/With-the-introduction-of-radix-MMU-in-Power-ISA-3.0-.patch 1970-01-01 01:00:00.000000000 +0100 @@ -1,806 +0,0 @@ -From 182914debbb9a2671ef644027fedd339aa9c80e0 Mon Sep 17 00:00:00 2001 -From: Dave Anderson -Date: Fri, 23 Sep 2016 09:09:15 -0400 -Subject: [PATCH] With the introduction of radix MMU in Power ISA 3.0, there - are changes in kernel page table management accommodating it. This patch - series makes appropriate changes here to work for such kernels. Also, this - series fixes a few bugs along the way: - - ppc64: fix vtop page translation for 4K pages - ppc64: Use kernel terminology for each level in 4-level page table - ppc64/book3s: address changes in kernel v4.5 - ppc64/book3s: address change in page flags for PowerISA v3.0 - ppc64: use physical addresses and unfold pud for 64K page size - ppc64/book3s: support big endian Linux page tables - -The patches are needed for Linux v4.5 and later kernels on all -ppc64 hardware. -(hbathini@linux.vnet.ibm.com) ---- - defs.h | 74 +++++++++----- - ppc64.c | 340 ++++++++++++++++++++++++++++++++++++++++++++++++++-------------- - tools.c | 16 +++ - 3 files changed, 332 insertions(+), 98 deletions(-) - ---- a/defs.h -+++ b/defs.h -@@ -3737,7 +3737,7 @@ - #define PMD_MASK (~((1UL << PMD_SHIFT) - 1)) - - /* shift to put page number into pte */ --#define PTE_SHIFT 16 -+#define PTE_RPN_SHIFT_DEFAULT 16 - #define PMD_TO_PTEPAGE_SHIFT 2 /* Used for 2.6 or later */ - - #define PTE_INDEX_SIZE 9 -@@ -3759,7 +3759,11 @@ - #define PMD_INDEX_SIZE_L4_4K 7 - #define PUD_INDEX_SIZE_L4_4K 7 - #define PGD_INDEX_SIZE_L4_4K 9 --#define PTE_SHIFT_L4_4K 17 -+#define PUD_INDEX_SIZE_L4_4K_3_7 9 -+#define PTE_RPN_SHIFT_L4_4K 17 -+#define PTE_RPN_SHIFT_L4_4K_4_5 18 -+#define PGD_MASKED_BITS_4K 0 -+#define PUD_MASKED_BITS_4K 0 - #define PMD_MASKED_BITS_4K 0 - - /* 64K pagesize */ -@@ -3770,24 +3774,44 @@ - #define PTE_INDEX_SIZE_L4_64K_3_10 8 - #define PMD_INDEX_SIZE_L4_64K_3_10 10 - #define PGD_INDEX_SIZE_L4_64K_3_10 12 --#define PTE_SHIFT_L4_64K_V1 32 --#define PTE_SHIFT_L4_64K_V2 30 --#define PTE_SHIFT_L4_BOOK3E_64K 28 --#define PTE_SHIFT_L4_BOOK3E_4K 24 -+#define PMD_INDEX_SIZE_L4_64K_4_6 5 -+#define PUD_INDEX_SIZE_L4_64K_4_6 5 -+#define PTE_RPN_SHIFT_L4_64K_V1 32 -+#define PTE_RPN_SHIFT_L4_64K_V2 30 -+#define PTE_RPN_SHIFT_L4_BOOK3E_64K 28 -+#define PTE_RPN_SHIFT_L4_BOOK3E_4K 24 -+#define PGD_MASKED_BITS_64K 0 -+#define PUD_MASKED_BITS_64K 0x1ff - #define PMD_MASKED_BITS_64K 0x1ff -+#define PMD_MASKED_BITS_64K_3_11 0xfff -+#define PMD_MASKED_BITS_BOOK3E_64K_4_5 0x7ff -+#define PGD_MASKED_BITS_64K_4_6 0xc0000000000000ffUL -+#define PUD_MASKED_BITS_64K_4_6 0xc0000000000000ffUL -+#define PMD_MASKED_BITS_64K_4_6 0xc0000000000000ffUL -+ -+#define PTE_RPN_MASK_DEFAULT 0xffffffffffffffffUL -+#define PTE_RPN_SIZE_L4_4_6 (PAGESIZE() == PPC64_64K_PAGE_SIZE ? 41 : 45) -+#define PTE_RPN_MASK_L4_4_6 (((1UL << PTE_RPN_SIZE_L4_4_6) - 1) << PAGESHIFT()) -+#define PTE_RPN_SHIFT_L4_4_6 PAGESHIFT() - - #define PD_HUGE 0x8000000000000000 - #define HUGE_PTE_MASK 0x03 - #define HUGEPD_SHIFT_MASK 0x3f --#define L4_MASK (THIS_KERNEL_VERSION >= LINUX(3,10,0) ? 0xfff : 0x1ff) --#define L4_OFFSET(vaddr) ((vaddr >> (machdep->machspec->l4_shift)) & L4_MASK) -+#define HUGEPD_ADDR_MASK (0x0fffffffffffffffUL & ~HUGEPD_SHIFT_MASK) -+ -+#define PGD_MASK_L4 \ -+ (THIS_KERNEL_VERSION >= LINUX(3,10,0) ? (machdep->ptrs_per_pgd - 1) : 0x1ff) - - #define PGD_OFFSET_L4(vaddr) \ -+ ((vaddr >> (machdep->machspec->l4_shift)) & PGD_MASK_L4) -+ -+#define PUD_OFFSET_L4(vaddr) \ - ((vaddr >> (machdep->machspec->l3_shift)) & (machdep->machspec->ptrs_per_l3 - 1)) - - #define PMD_OFFSET_L4(vaddr) \ - ((vaddr >> (machdep->machspec->l2_shift)) & (machdep->machspec->ptrs_per_l2 - 1)) - -+#define _PAGE_PTE (machdep->machspec->_page_pte) /* distinguishes PTEs from pointers */ - #define _PAGE_PRESENT (machdep->machspec->_page_present) /* software: pte contains a translation */ - #define _PAGE_USER (machdep->machspec->_page_user) /* matches one of the PP bits */ - #define _PAGE_RW (machdep->machspec->_page_rw) /* software: user write access allowed */ -@@ -3798,6 +3822,9 @@ - #define _PAGE_DIRTY (machdep->machspec->_page_dirty) /* C: page changed */ - #define _PAGE_ACCESSED (machdep->machspec->_page_accessed) /* R: page referenced */ - -+#define PTE_RPN_MASK (machdep->machspec->pte_rpn_mask) -+#define PTE_RPN_SHIFT (machdep->machspec->pte_rpn_shift) -+ - #define TIF_SIGPENDING (2) - - #define SWP_TYPE(entry) (((entry) >> 1) & 0x7f) -@@ -4740,6 +4767,7 @@ - int endian_mismatch(char *, char, ulong); - uint16_t swap16(uint16_t, int); - uint32_t swap32(uint32_t, int); -+uint64_t swap64(uint64_t, int); - ulong *get_cpumask_buf(void); - int make_cpumask(char *, ulong *, int, int *); - size_t strlcpy(char *, char *, size_t); -@@ -5608,14 +5636,13 @@ - ulong hwintrstack[NR_CPUS]; - char *hwstackbuf; - uint hwstacksize; -- char *level4; -- ulong last_level4_read; - - uint l4_index_size; - uint l3_index_size; - uint l2_index_size; - uint l1_index_size; - -+ uint ptrs_per_l4; - uint ptrs_per_l3; - uint ptrs_per_l2; - uint ptrs_per_l1; -@@ -5625,13 +5652,17 @@ - uint l2_shift; - uint l1_shift; - -- uint pte_shift; -- uint l2_masked_bits; -+ uint pte_rpn_shift; -+ ulong pte_rpn_mask; -+ ulong pgd_masked_bits; -+ ulong pud_masked_bits; -+ ulong pmd_masked_bits; - - int vmemmap_cnt; - int vmemmap_psize; - ulong vmemmap_base; - struct ppc64_vmemmap *vmemmap_list; -+ ulong _page_pte; - ulong _page_present; - ulong _page_user; - ulong _page_rw; -@@ -5645,23 +5676,16 @@ - int (*is_vmaddr)(ulong); - }; - --#define IS_LAST_L4_READ(l4) ((ulong)(l4) == machdep->machspec->last_level4_read) -- --#define FILL_L4(L4, TYPE, SIZE) \ -- if (!IS_LAST_L4_READ(L4)) { \ -- readmem((ulonglong)((ulong)(L4)), TYPE, machdep->machspec->level4, \ -- SIZE, "level4 page", FAULT_ON_ERROR); \ -- machdep->machspec->last_level4_read = (ulong)(L4); \ -- } -- - void ppc64_init(int); - void ppc64_dump_machdep_table(ulong); - #define display_idt_table() \ - error(FATAL, "-d option is not applicable to PowerPC architecture\n") --#define KSYMS_START (0x1) --#define VM_ORIG (0x2) --#define VMEMMAP_AWARE (0x4) --#define BOOK3E (0x8) -+#define KSYMS_START (0x1) -+#define VM_ORIG (0x2) -+#define VMEMMAP_AWARE (0x4) -+#define BOOK3E (0x8) -+#define PHYS_ENTRY_L4 (0x10) -+#define SWAP_ENTRY_L4 (0x20) - - #define REGION_SHIFT (60UL) - #define REGION_ID(addr) (((unsigned long)(addr)) >> REGION_SHIFT) ---- a/ppc64.c -+++ b/ppc64.c -@@ -15,7 +15,9 @@ - * GNU General Public License for more details. - */ - #ifdef PPC64 -+ - #include "defs.h" -+#include - - static int ppc64_kvtop(struct task_context *, ulong, physaddr_t *, int); - static int ppc64_uvtop(struct task_context *, ulong, physaddr_t *, int); -@@ -59,6 +61,9 @@ - static int is_hugepage(ulong pte); - static int is_hugepd(ulong pte); - static ulong hugepage_dir(ulong pte); -+static ulong pgd_page_vaddr_l4(ulong pgd); -+static ulong pud_page_vaddr_l4(ulong pud); -+static ulong pmd_page_vaddr_l4(ulong pmd); - - static inline uint get_ptetype(ulong pte) - { -@@ -72,33 +77,110 @@ - return pte_type; - } - --static int is_hugepage(ulong pte) -+static inline int is_hugepage(ulong pte) - { -- /* -- * leaf pte for huge page, bottom two bits != 00 -- */ -- return ((pte & HUGE_PTE_MASK) != 0x0); -+ if ((machdep->flags & BOOK3E) || -+ (THIS_KERNEL_VERSION < LINUX(3,10,0))) { -+ /* -+ * hugepage support via hugepd for book3e and -+ * also kernel v3.9 & below. -+ */ -+ return 0; -+ -+ } else if (THIS_KERNEL_VERSION >= LINUX(4,5,0)) { -+ /* -+ * leaf pte for huge page, if _PAGE_PTE is set. -+ */ -+ return !!(pte & _PAGE_PTE); -+ -+ } else { /* BOOK3S, kernel v3.10 - v4.4 */ -+ -+ /* -+ * leaf pte for huge page, bottom two bits != 00 -+ */ -+ return ((pte & HUGE_PTE_MASK) != 0x0); -+ } - } - - static inline int is_hugepd(ulong pte) - { -- if (THIS_KERNEL_VERSION >= LINUX(3,10,0)) { -+ if ((machdep->flags & BOOK3E) || -+ (THIS_KERNEL_VERSION < LINUX(3,10,0))) -+ return ((pte & PD_HUGE) == 0x0); -+ -+ else if (THIS_KERNEL_VERSION >= LINUX(4,5,0)) { -+ /* -+ * hugepd pointer, if _PAGE_PTE is not set and -+ * hugepd shift mask is set. -+ */ -+ return (!(pte & _PAGE_PTE) && -+ ((pte & HUGEPD_SHIFT_MASK) != 0)); -+ -+ } else { /* BOOK3S, kernel v3.10 - v4.4 */ -+ - /* - * hugepd pointer, bottom two bits == 00 and next 4 bits - * indicate size of table -- */ -+ */ - return (((pte & HUGE_PTE_MASK) == 0x0) && - ((pte & HUGEPD_SHIFT_MASK) != 0)); -- } else -- return ((pte & PD_HUGE) == 0x0); -+ } - } - - static inline ulong hugepage_dir(ulong pte) - { -- if (THIS_KERNEL_VERSION >= LINUX(3,10,0)) -- return (ulong)(pte & ~HUGEPD_SHIFT_MASK); -- else -+ if ((machdep->flags & BOOK3E) || -+ (THIS_KERNEL_VERSION < LINUX(3,10,0))) - return (ulong)((pte & ~HUGEPD_SHIFT_MASK) | PD_HUGE); -+ else if (machdep->flags & PHYS_ENTRY_L4) -+ return PTOV(pte & ~HUGEPD_ADDR_MASK); -+ else /* BOOK3S, kernel v3.10 - v4.4 */ -+ return (ulong)(pte & ~HUGEPD_SHIFT_MASK); -+} -+ -+static inline ulong pgd_page_vaddr_l4(ulong pgd) -+{ -+ ulong pgd_val; -+ -+ pgd_val = (pgd & ~machdep->machspec->pgd_masked_bits); -+ if (machdep->flags & PHYS_ENTRY_L4) { -+ /* -+ * physical address is stored starting from kernel v4.6 -+ */ -+ pgd_val = PTOV(pgd_val); -+ } -+ -+ return pgd_val; -+} -+ -+static inline ulong pud_page_vaddr_l4(ulong pud) -+{ -+ ulong pud_val; -+ -+ pud_val = (pud & ~machdep->machspec->pud_masked_bits); -+ if (machdep->flags & PHYS_ENTRY_L4) { -+ /* -+ * physical address is stored starting from kernel v4.6 -+ */ -+ pud_val = PTOV(pud_val); -+ } -+ -+ return pud_val; -+} -+ -+static inline ulong pmd_page_vaddr_l4(ulong pmd) -+{ -+ ulong pmd_val; -+ -+ pmd_val = (pmd & ~machdep->machspec->pmd_masked_bits); -+ if (machdep->flags & PHYS_ENTRY_L4) { -+ /* -+ * physical address is stored starting from kernel v4.6 -+ */ -+ pmd_val = PTOV(pmd_val); -+ } -+ -+ return pmd_val; - } - - static int book3e_is_kvaddr(ulong addr) -@@ -122,7 +204,8 @@ - .hwintrstack = { 0 }, - .hwstackbuf = 0, - .hwstacksize = 0, -- .pte_shift = PTE_SHIFT, -+ .pte_rpn_shift = PTE_RPN_SHIFT_DEFAULT, -+ ._page_pte = 0x0UL, - ._page_present = 0x1UL, - ._page_user = 0x2UL, - ._page_rw = 0x4UL, -@@ -140,7 +223,8 @@ - .hwintrstack = { 0 }, - .hwstackbuf = 0, - .hwstacksize = 0, -- .pte_shift = PTE_SHIFT_L4_BOOK3E_64K, -+ .pte_rpn_shift = PTE_RPN_SHIFT_L4_BOOK3E_64K, -+ ._page_pte = 0x0UL, - ._page_present = 0x1UL, - ._page_user = 0xCUL, - ._page_rw = 0x30UL, -@@ -179,9 +263,9 @@ - return; - machdep->stacksize = PPC64_STACK_SIZE; - machdep->last_pgd_read = 0; -- machdep->last_pmd_read = 0; -- machdep->last_ptbl_read = 0; -- machdep->machspec->last_level4_read = 0; -+ machdep->last_pud_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; - machdep->flags |= MACHDEP_BT_TEXT; -@@ -223,12 +307,12 @@ - machdep->pagemask = ~((ulonglong)machdep->pageoffset); - if ((machdep->pgd = (char *)malloc(PAGESIZE())) == NULL) - error(FATAL, "cannot malloc pgd space."); -+ if ((machdep->pud = (char *)malloc(PAGESIZE())) == NULL) -+ error(FATAL, "cannot malloc pud space."); - if ((machdep->pmd = (char *)malloc(PAGESIZE())) == NULL) - error(FATAL, "cannot malloc pmd space."); - if ((machdep->ptbl = (char *)malloc(PAGESIZE())) == NULL) - error(FATAL, "cannot malloc ptbl space."); -- if ((machdep->machspec->level4 = (char *)malloc(PAGESIZE())) == NULL) -- error(FATAL, "cannot malloc level4 space."); - - machdep->identity_map_base = symbol_value("_stext"); - machdep->is_kvaddr = machdep->machspec->is_kvaddr; -@@ -262,6 +346,53 @@ - break; - - case POST_GDB: -+ if (!(machdep->flags & BOOK3E)) { -+ struct machine_specific *m = machdep->machspec; -+ -+ /* -+ * Starting with v3.14 we no longer use _PAGE_COHERENT -+ * bit as it is always set on hash64 and on platforms -+ * that cannot always set it, _PAGE_NO_CACHE and -+ * _PAGE_WRITETHRU can be used to infer it. -+ */ -+ if (THIS_KERNEL_VERSION >= LINUX(3,14,0)) -+ m->_page_coherent = 0x0UL; -+ -+ /* -+ * In kernel v4.5, _PAGE_PTE bit is introduced to -+ * distinguish PTEs from pointers. -+ */ -+ if (THIS_KERNEL_VERSION >= LINUX(4,5,0)) { -+ m->_page_pte = 0x1UL; -+ m->_page_present = 0x2UL; -+ m->_page_user = 0x4UL; -+ m->_page_rw = 0x8UL; -+ m->_page_guarded = 0x10UL; -+ } -+ -+ /* -+ * Starting with kernel v4.6, to accommodate both -+ * radix and hash MMU modes in a single kernel, -+ * _PAGE_PTE & _PAGE_PRESENT page flags are changed. -+ * Also, page table entries store physical addresses. -+ */ -+ if (THIS_KERNEL_VERSION >= LINUX(4,6,0)) { -+ m->_page_pte = 0x1UL << 62; -+ m->_page_present = 0x1UL << 63; -+ machdep->flags |= PHYS_ENTRY_L4; -+ } -+ -+ if (THIS_KERNEL_VERSION >= LINUX(4,7,0)) { -+ /* -+ * Starting with kernel v4.7 page table entries -+ * are always big endian on BOOK3S. Set this -+ * flag if kernel is not big endian. -+ */ -+ if (__BYTE_ORDER == __LITTLE_ENDIAN) -+ machdep->flags |= SWAP_ENTRY_L4; -+ } -+ } -+ - if (!(machdep->flags & (VM_ORIG|VM_4_LEVEL))) { - if (THIS_KERNEL_VERSION >= LINUX(2,6,14)) { - machdep->flags |= VM_4_LEVEL; -@@ -271,15 +402,22 @@ - } - if (machdep->flags & VM_ORIG) { - /* pre-2.6.14 layout */ -- free(machdep->machspec->level4); -- machdep->machspec->level4 = NULL; -+ free(machdep->pud); -+ machdep->pud = NULL; - machdep->ptrs_per_pgd = PTRS_PER_PGD; - } else { - /* 2.6.14 layout */ - struct machine_specific *m = machdep->machspec; - if (machdep->pagesize == 65536) { - /* 64K pagesize */ -- if (THIS_KERNEL_VERSION >= LINUX(3,10,0)) { -+ if (!(machdep->flags & BOOK3E) && -+ (THIS_KERNEL_VERSION >= LINUX(4,6,0))) { -+ m->l1_index_size = PTE_INDEX_SIZE_L4_64K_3_10; -+ m->l2_index_size = PMD_INDEX_SIZE_L4_64K_4_6; -+ m->l3_index_size = PUD_INDEX_SIZE_L4_64K_4_6; -+ m->l4_index_size = PGD_INDEX_SIZE_L4_64K_3_10; -+ -+ } else if (THIS_KERNEL_VERSION >= LINUX(3,10,0)) { - m->l1_index_size = PTE_INDEX_SIZE_L4_64K_3_10; - m->l2_index_size = PMD_INDEX_SIZE_L4_64K_3_10; - m->l3_index_size = PUD_INDEX_SIZE_L4_64K; -@@ -291,19 +429,52 @@ - m->l3_index_size = PUD_INDEX_SIZE_L4_64K; - m->l4_index_size = PGD_INDEX_SIZE_L4_64K; - } -+ - if (!(machdep->flags & BOOK3E)) -- m->pte_shift = symbol_exists("demote_segment_4k") ? -- PTE_SHIFT_L4_64K_V2 : PTE_SHIFT_L4_64K_V1; -- m->l2_masked_bits = PMD_MASKED_BITS_64K; -+ m->pte_rpn_shift = symbol_exists("demote_segment_4k") ? -+ PTE_RPN_SHIFT_L4_64K_V2 : PTE_RPN_SHIFT_L4_64K_V1; -+ -+ if (!(machdep->flags & BOOK3E) && -+ (THIS_KERNEL_VERSION >= LINUX(4,6,0))) { -+ m->pgd_masked_bits = PGD_MASKED_BITS_64K_4_6; -+ m->pud_masked_bits = PUD_MASKED_BITS_64K_4_6; -+ m->pmd_masked_bits = PMD_MASKED_BITS_64K_4_6; -+ } else { -+ m->pgd_masked_bits = PGD_MASKED_BITS_64K; -+ m->pud_masked_bits = PUD_MASKED_BITS_64K; -+ if ((machdep->flags & BOOK3E) && -+ (THIS_KERNEL_VERSION >= LINUX(4,5,0))) -+ m->pmd_masked_bits = PMD_MASKED_BITS_BOOK3E_64K_4_5; -+ else if (THIS_KERNEL_VERSION >= LINUX(3,11,0)) -+ m->pmd_masked_bits = PMD_MASKED_BITS_64K_3_11; -+ else -+ m->pmd_masked_bits = PMD_MASKED_BITS_64K; -+ } - } else { - /* 4K pagesize */ - m->l1_index_size = PTE_INDEX_SIZE_L4_4K; - m->l2_index_size = PMD_INDEX_SIZE_L4_4K; -- m->l3_index_size = PUD_INDEX_SIZE_L4_4K; -+ if (THIS_KERNEL_VERSION >= LINUX(3,7,0)) -+ m->l3_index_size = PUD_INDEX_SIZE_L4_4K_3_7; -+ else -+ m->l3_index_size = PUD_INDEX_SIZE_L4_4K; - m->l4_index_size = PGD_INDEX_SIZE_L4_4K; -- m->pte_shift = (machdep->flags & BOOK3E) ? -- PTE_SHIFT_L4_BOOK3E_4K : PTE_SHIFT_L4_4K; -- m->l2_masked_bits = PMD_MASKED_BITS_4K; -+ -+ if (machdep->flags & BOOK3E) -+ m->pte_rpn_shift = PTE_RPN_SHIFT_L4_BOOK3E_4K; -+ else -+ m->pte_rpn_shift = THIS_KERNEL_VERSION >= LINUX(4,5,0) ? -+ PTE_RPN_SHIFT_L4_4K_4_5 : PTE_RPN_SHIFT_L4_4K; -+ m->pgd_masked_bits = PGD_MASKED_BITS_4K; -+ m->pud_masked_bits = PUD_MASKED_BITS_4K; -+ m->pmd_masked_bits = PMD_MASKED_BITS_4K; -+ } -+ -+ m->pte_rpn_mask = PTE_RPN_MASK_DEFAULT; -+ if (!(machdep->flags & BOOK3E) && -+ (THIS_KERNEL_VERSION >= LINUX(4,6,0))) { -+ m->pte_rpn_mask = PTE_RPN_MASK_L4_4_6; -+ m->pte_rpn_shift = PTE_RPN_SHIFT_L4_4_6; - } - - /* Compute ptrs per each level */ -@@ -311,8 +482,8 @@ - m->ptrs_per_l1 = (1 << m->l1_index_size); - m->ptrs_per_l2 = (1 << m->l2_index_size); - m->ptrs_per_l3 = (1 << m->l3_index_size); -- -- machdep->ptrs_per_pgd = m->ptrs_per_l3; -+ m->ptrs_per_l4 = (1 << m->l4_index_size); -+ machdep->ptrs_per_pgd = m->ptrs_per_l4; - - /* Compute shifts */ - m->l2_shift = m->l1_shift + m->l1_index_size; -@@ -516,10 +687,12 @@ - fprintf(fp, "xen_kdump_p2m_create: NULL\n"); - fprintf(fp, " line_number_hooks: ppc64_line_number_hooks\n"); - fprintf(fp, " last_pgd_read: %lx\n", machdep->last_pgd_read); -+ fprintf(fp, " last_pud_read: %lx\n", machdep->last_pud_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, "clear_machdep_cache: ppc64_clear_machdep_cache()\n"); - fprintf(fp, " pgd: %lx\n", (ulong)machdep->pgd); -+ fprintf(fp, " pud: %lx\n", (ulong)machdep->pud); - fprintf(fp, " pmd: %lx\n", (ulong)machdep->pmd); - fprintf(fp, " ptbl: %lx\n", (ulong)machdep->ptbl); - fprintf(fp, " ptrs_per_pgd: %d\n", machdep->ptrs_per_pgd); -@@ -558,12 +731,11 @@ - fprintf(fp, "\n"); - fprintf(fp, " hwstackbuf: %lx\n", (ulong)machdep->machspec->hwstackbuf); - fprintf(fp, " hwstacksize: %d\n", machdep->machspec->hwstacksize); -- fprintf(fp, " level4: %lx\n", (ulong)machdep->machspec->level4); -- fprintf(fp, " last_level4_read: %lx\n", (ulong)machdep->machspec->last_level4_read); - fprintf(fp, " l4_index_size: %d\n", machdep->machspec->l4_index_size); - fprintf(fp, " l3_index_size: %d\n", machdep->machspec->l3_index_size); - fprintf(fp, " l2_index_size: %d\n", machdep->machspec->l2_index_size); - fprintf(fp, " l1_index_size: %d\n", machdep->machspec->l1_index_size); -+ fprintf(fp, " ptrs_per_l4: %d\n", machdep->machspec->ptrs_per_l4); - fprintf(fp, " ptrs_per_l3: %d\n", machdep->machspec->ptrs_per_l3); - fprintf(fp, " ptrs_per_l2: %d\n", machdep->machspec->ptrs_per_l2); - fprintf(fp, " ptrs_per_l1: %d\n", machdep->machspec->ptrs_per_l1); -@@ -571,8 +743,11 @@ - fprintf(fp, " l3_shift: %d\n", machdep->machspec->l3_shift); - fprintf(fp, " l2_shift: %d\n", machdep->machspec->l2_shift); - fprintf(fp, " l1_shift: %d\n", machdep->machspec->l1_shift); -- fprintf(fp, " pte_shift: %d\n", machdep->machspec->pte_shift); -- fprintf(fp, " l2_masked_bits: %x\n", machdep->machspec->l2_masked_bits); -+ fprintf(fp, " pte_rpn_mask: %lx\n", machdep->machspec->pte_rpn_mask); -+ fprintf(fp, " pte_rpn_shift: %d\n", machdep->machspec->pte_rpn_shift); -+ fprintf(fp, " pgd_masked_bits: %lx\n", machdep->machspec->pgd_masked_bits); -+ fprintf(fp, " pud_masked_bits: %lx\n", machdep->machspec->pud_masked_bits); -+ fprintf(fp, " pmd_masked_bits: %lx\n", machdep->machspec->pmd_masked_bits); - fprintf(fp, " vmemmap_base: "); - if (machdep->machspec->vmemmap_base) - fprintf(fp, "%lx\n", machdep->machspec->vmemmap_base); -@@ -658,7 +833,7 @@ - if (!(pte & _PAGE_PRESENT)) { - if (pte && verbose) { - fprintf(fp, "\n"); -- ppc64_translate_pte(pte, 0, PTE_SHIFT); -+ ppc64_translate_pte(pte, 0, PTE_RPN_SHIFT_DEFAULT); - } - return FALSE; - } -@@ -666,11 +841,11 @@ - if (!pte) - return FALSE; - -- *paddr = PAGEBASE(PTOB(pte >> PTE_SHIFT)) + PAGEOFFSET(vaddr); -+ *paddr = PAGEBASE(PTOB(pte >> PTE_RPN_SHIFT_DEFAULT)) + PAGEOFFSET(vaddr); - - if (verbose) { - fprintf(fp, " PAGE: %lx\n\n", PAGEBASE(*paddr)); -- ppc64_translate_pte(pte, 0, PTE_SHIFT); -+ ppc64_translate_pte(pte, 0, PTE_RPN_SHIFT_DEFAULT); - } - - return TRUE; -@@ -683,54 +858,60 @@ - static int - ppc64_vtop_level4(ulong vaddr, ulong *level4, physaddr_t *paddr, int verbose) - { -- ulong *level4_dir; -- ulong *page_dir; -+ ulong *pgdir; -+ ulong *page_upper; - ulong *page_middle; - ulong *page_table; -- ulong level4_pte, pgd_pte, pmd_pte; -+ ulong pgd_pte, pud_pte, pmd_pte; - ulong pte; -+ uint pdshift; - uint hugepage_type = 0; /* 0: regular entry; 1: huge pte; 2: huge pd */ -+ uint swap = !!(machdep->flags & SWAP_ENTRY_L4); - - if (verbose) - fprintf(fp, "PAGE DIRECTORY: %lx\n", (ulong)level4); - -- level4_dir = (ulong *)((ulong *)level4 + L4_OFFSET(vaddr)); -- FILL_L4(PAGEBASE(level4), KVADDR, PAGESIZE()); -- level4_pte = ULONG(machdep->machspec->level4 + PAGEOFFSET(level4_dir)); -+ pgdir = (ulong *)((ulong *)level4 + PGD_OFFSET_L4(vaddr)); -+ FILL_PGD(PAGEBASE(level4), KVADDR, PAGESIZE()); -+ pgd_pte = swap64(ULONG(machdep->pgd + PAGEOFFSET(pgdir)), swap); - if (verbose) -- fprintf(fp, " L4: %lx => %lx\n", (ulong)level4_dir, level4_pte); -- if (!level4_pte) -+ fprintf(fp, " PGD: %lx => %lx\n", (ulong)pgdir, pgd_pte); -+ if (!pgd_pte) - return FALSE; - -- hugepage_type = get_ptetype(level4_pte); -+ hugepage_type = get_ptetype(pgd_pte); - if (hugepage_type) { -- pte = level4_pte; -+ pte = pgd_pte; -+ pdshift = machdep->machspec->l4_shift; - goto out; - } - - /* Sometimes we don't have level3 pagetable entries */ - if (machdep->machspec->l3_index_size != 0) { -- page_dir = (ulong *)((ulong *)level4_pte + PGD_OFFSET_L4(vaddr)); -- FILL_PGD(PAGEBASE(level4_pte), KVADDR, PAGESIZE()); -- pgd_pte = ULONG(machdep->pgd + PAGEOFFSET(page_dir)); -+ pgd_pte = pgd_page_vaddr_l4(pgd_pte); -+ page_upper = (ulong *)((ulong *)pgd_pte + PUD_OFFSET_L4(vaddr)); -+ FILL_PUD(PAGEBASE(pgd_pte), KVADDR, PAGESIZE()); -+ pud_pte = swap64(ULONG(machdep->pud + PAGEOFFSET(page_upper)), swap); - - if (verbose) -- fprintf(fp, " PGD: %lx => %lx\n", (ulong)page_dir, pgd_pte); -- if (!pgd_pte) -+ fprintf(fp, " PUD: %lx => %lx\n", (ulong)page_upper, pud_pte); -+ if (!pud_pte) - return FALSE; - -- hugepage_type = get_ptetype(pgd_pte); -+ hugepage_type = get_ptetype(pud_pte); - if (hugepage_type) { -- pte = pgd_pte; -+ pte = pud_pte; -+ pdshift = machdep->machspec->l3_shift; - goto out; - } - } else { -- pgd_pte = level4_pte; -+ pud_pte = pgd_pte; - } - -- page_middle = (ulong *)((ulong *)pgd_pte + PMD_OFFSET_L4(vaddr)); -- FILL_PMD(PAGEBASE(pgd_pte), KVADDR, PAGESIZE()); -- pmd_pte = ULONG(machdep->pmd + PAGEOFFSET(page_middle)); -+ pud_pte = pud_page_vaddr_l4(pud_pte); -+ page_middle = (ulong *)((ulong *)pud_pte + PMD_OFFSET_L4(vaddr)); -+ FILL_PMD(PAGEBASE(pud_pte), KVADDR, PAGESIZE()); -+ pmd_pte = swap64(ULONG(machdep->pmd + PAGEOFFSET(page_middle)), swap); - - if (verbose) - fprintf(fp, " PMD: %lx => %lx\n", (ulong)page_middle, pmd_pte); -@@ -741,17 +922,19 @@ - hugepage_type = get_ptetype(pmd_pte); - if (hugepage_type) { - pte = pmd_pte; -+ pdshift = machdep->machspec->l2_shift; - goto out; - } - -- page_table = (ulong *)(pmd_pte & ~(machdep->machspec->l2_masked_bits)) -+ pmd_pte = pmd_page_vaddr_l4(pmd_pte); -+ page_table = (ulong *)(pmd_pte) - + (BTOP(vaddr) & (machdep->machspec->ptrs_per_l1 - 1)); - if (verbose) - fprintf(fp, " PMD: %lx => %lx\n",(ulong)page_middle, - (ulong)page_table); - - FILL_PTBL(PAGEBASE(pmd_pte), KVADDR, PAGESIZE()); -- pte = ULONG(machdep->ptbl + PAGEOFFSET(page_table)); -+ pte = swap64(ULONG(machdep->ptbl + PAGEOFFSET(page_table)), swap); - - if (verbose) - fprintf(fp, " PTE: %lx => %lx\n", (ulong)page_table, pte); -@@ -759,7 +942,7 @@ - if (!(pte & _PAGE_PRESENT)) { - if (pte && verbose) { - fprintf(fp, "\n"); -- ppc64_translate_pte(pte, 0, machdep->machspec->pte_shift); -+ ppc64_translate_pte(pte, 0, machdep->machspec->pte_rpn_shift); - } - return FALSE; - } -@@ -777,13 +960,22 @@ - * in this directory for all the huge pages - * in this huge page directory. - */ -- readmem(hugepage_dir(pte), KVADDR, &pte, sizeof(pte), -- "hugepd_entry", RETURN_ON_ERROR); -+ ulong hugepd = hugepage_dir(pte); -+ -+ readmem(hugepd, KVADDR, &pte, sizeof(pte), -+ "hugepd_entry", RETURN_ON_ERROR); -+ -+ if (verbose) -+ fprintf(fp, " HUGE PD: %lx => %lx\n", hugepd, pte); -+ -+ if (!pte) -+ return FALSE; - } -- /* TODO: get page offset for huge pages based on page size */ -- *paddr = PAGEBASE(PTOB(pte >> machdep->machspec->pte_shift)); -+ -+ *paddr = PAGEBASE(PTOB((pte & PTE_RPN_MASK) >> PTE_RPN_SHIFT)) -+ + (vaddr & ((1UL << pdshift) - 1)); - } else { -- *paddr = PAGEBASE(PTOB(pte >> machdep->machspec->pte_shift)) -+ *paddr = PAGEBASE(PTOB((pte & PTE_RPN_MASK) >> PTE_RPN_SHIFT)) - + PAGEOFFSET(vaddr); - } - -@@ -792,7 +984,7 @@ - fprintf(fp, " HUGE PAGE: %lx\n\n", PAGEBASE(*paddr)); - else - fprintf(fp, " PAGE: %lx\n\n", PAGEBASE(*paddr)); -- ppc64_translate_pte(pte, 0, machdep->machspec->pte_shift); -+ ppc64_translate_pte(pte, 0, machdep->machspec->pte_rpn_shift); - } - - return TRUE; -@@ -1247,7 +1439,7 @@ - * If a physaddr pointer is passed in, don't print anything. - */ - static int --ppc64_translate_pte(ulong pte, void *physaddr, ulonglong pte_shift) -+ppc64_translate_pte(ulong pte, void *physaddr, ulonglong pte_rpn_shift) - { - int c, len1, len2, len3, others, page_present; - char buf[BUFSIZE]; -@@ -1258,8 +1450,8 @@ - char *arglist[MAXARGS]; - ulong paddr; - -- paddr = PTOB(pte >> pte_shift); -- page_present = (pte & _PAGE_PRESENT); -+ paddr = PTOB(pte >> pte_rpn_shift); -+ page_present = !!(pte & _PAGE_PRESENT); - - if (physaddr) { - *((ulong *)physaddr) = paddr; -@@ -1305,6 +1497,8 @@ - others = 0; - - if (pte) { -+ if (pte & _PAGE_PTE) -+ fprintf(fp, "%sPTE", others++ ? "|" : ""); - if (pte & _PAGE_PRESENT) - fprintf(fp, "%sPRESENT", others++ ? "|" : ""); - if (pte & _PAGE_USER) -@@ -2963,8 +3157,8 @@ - void - ppc64_clear_machdep_cache(void) - { -- if (machdep->machspec->last_level4_read != vt->kernel_pgd[0]) -- machdep->machspec->last_level4_read = 0; -+ if (machdep->last_pgd_read != vt->kernel_pgd[0]) -+ machdep->last_pgd_read = 0; - } - - static int ---- a/tools.c -+++ b/tools.c -@@ -5652,6 +5652,22 @@ - return val; - } - -+uint64_t -+swap64(uint64_t val, int swap) -+{ -+ if (swap) -+ return (((val & 0x00000000000000ffULL) << 56) | -+ ((val & 0x000000000000ff00ULL) << 40) | -+ ((val & 0x0000000000ff0000ULL) << 24) | -+ ((val & 0x00000000ff000000ULL) << 8) | -+ ((val & 0x000000ff00000000ULL) >> 8) | -+ ((val & 0x0000ff0000000000ULL) >> 24) | -+ ((val & 0x00ff000000000000ULL) >> 40) | -+ ((val & 0xff00000000000000ULL) >> 56)); -+ else -+ return val; -+} -+ - /* - * Get a sufficiently large buffer for cpumask. - * You should call FREEBUF() on the result when you no longer need it. diff -Nru crash-7.1.5/debian/rules crash-7.1.7/debian/rules --- crash-7.1.5/debian/rules 2017-01-18 17:31:01.000000000 +0100 +++ crash-7.1.7/debian/rules 2017-01-18 17:31:06.000000000 +0100 @@ -3,7 +3,7 @@ # Uncomment this to turn on verbose mode. export DH_VERBOSE=1 export DEB_BUILD_HARDENING=1 -export DEB_BUILD_MAINT_OPTIONS=hardening=+all,-pie +export DEB_BUILD_MAINT_OPTIONS=hardening=+all export EXTDIR=/usr/lib/crash/extensions DPKG_EXPORT_BUILDFLAGS=1 include /usr/share/dpkg/buildflags.mk diff -Nru crash-7.1.5/defs.h crash-7.1.7/defs.h --- crash-7.1.5/defs.h 2016-04-27 20:46:50.000000000 +0200 +++ crash-7.1.7/defs.h 2016-11-30 19:56:29.000000000 +0100 @@ -223,7 +223,7 @@ #define DEVMEM (0x2000000ULL) #define REM_LIVE_SYSTEM (0x4000000ULL) #define NAMELIST_LOCAL (0x8000000ULL) -#define MEMSRC_LOCAL (0x10000000ULL) +#define LIVE_RAMDUMP (0x10000000ULL) #define NAMELIST_SAVED (0x20000000ULL) #define DUMPFILE_SAVED (0x40000000ULL) #define UNLINK_NAMELIST (0x80000000ULL) @@ -262,10 +262,11 @@ #define PROC_KCORE (0x8000000000000000ULL) #define ACTIVE() (pc->flags & LIVE_SYSTEM) +#define LOCAL_ACTIVE() ((pc->flags & (LIVE_SYSTEM|LIVE_RAMDUMP)) == LIVE_SYSTEM) #define DUMPFILE() (!(pc->flags & LIVE_SYSTEM)) #define LIVE() (pc->flags2 & LIVE_DUMP || pc->flags & LIVE_SYSTEM) -#define MEMORY_SOURCES (NETDUMP|KDUMP|MCLXCD|LKCD|DEVMEM|S390D|MEMMOD|DISKDUMP|XENDUMP|CRASHBUILTIN|KVMDUMP|PROC_KCORE|SADUMP|VMWARE_VMSS) -#define DUMPFILE_TYPES (DISKDUMP|NETDUMP|KDUMP|MCLXCD|LKCD|S390D|XENDUMP|KVMDUMP|SADUMP|VMWARE_VMSS) +#define MEMORY_SOURCES (NETDUMP|KDUMP|MCLXCD|LKCD|DEVMEM|S390D|MEMMOD|DISKDUMP|XENDUMP|CRASHBUILTIN|KVMDUMP|PROC_KCORE|SADUMP|VMWARE_VMSS|LIVE_RAMDUMP) +#define DUMPFILE_TYPES (DISKDUMP|NETDUMP|KDUMP|MCLXCD|LKCD|S390D|XENDUMP|KVMDUMP|SADUMP|VMWARE_VMSS|LIVE_RAMDUMP) #define REMOTE() (pc->flags2 & REMOTE_DAEMON) #define REMOTE_ACTIVE() (pc->flags & REM_LIVE_SYSTEM) #define REMOTE_DUMPFILE() \ @@ -534,6 +535,7 @@ #define SNAP (0x20000ULL) #define EXCLUDED_VMEMMAP (0x40000ULL) #define is_excluded_vmemmap() (pc->flags2 & EXCLUDED_VMEMMAP) +#define MEMSRC_LOCAL (0x80000ULL) char *cleanup; char *namelist_orig; char *namelist_debug_orig; @@ -614,6 +616,7 @@ #define TVEC_BASES_V2 (0x4000) #define GCC_3_3_3 (0x8000) #define USE_OLD_BT (0x10000) +#define USE_OPT_BT (0x10000) #define ARCH_XEN (0x20000) #define NO_IKCONFIG (0x40000) #define DWARF_UNWIND (0x80000) @@ -639,6 +642,7 @@ #define KASLR_CHECK (0x4ULL) #define GET_TIMESTAMP (0x8ULL) #define TVEC_BASES_V3 (0x10ULL) +#define TIMER_BASES (0x20ULL) #define XEN() (kt->flags & ARCH_XEN) #define OPENVZ() (kt->flags & ARCH_OPENVZ) @@ -838,6 +842,8 @@ ulong tgid_cache_hits; long filepages; long anonpages; + ulong stack_end_magic; + ulong pf_kthread; }; #define TASK_INIT_DONE (0x1) @@ -856,6 +862,7 @@ #define NO_TIMESPEC (0x2000) #define ACTIVE_ONLY (0x4000) #define START_TIME_NSECS (0x8000) +#define THREAD_INFO_IN_TASK (0x10000) #define TASK_SLUSH (20) @@ -1052,7 +1059,7 @@ readmem((ulonglong)((ulong)(PGD)), TYPE, machdep->pgd, \ SIZE, "pgd page", FAULT_ON_ERROR); \ machdep->last_pgd_read = (ulong)(PGD); \ - } + } #define FILL_PUD(PUD, TYPE, SIZE) \ if (!IS_LAST_PUD_READ(PUD)) { \ @@ -1066,7 +1073,7 @@ readmem((ulonglong)(PMD), TYPE, machdep->pmd, \ SIZE, "pmd page", FAULT_ON_ERROR); \ machdep->last_pmd_read = (ulong)(PMD); \ - } + } #define FILL_PTBL(PTBL, TYPE, SIZE) \ if (!IS_LAST_PTBL_READ(PTBL)) { \ @@ -1082,6 +1089,7 @@ #define POST_INIT (4) #define POST_VM (5) #define LOG_ONLY (6) +#define POST_RELOC (7) #define FOREACH_BT (1) #define FOREACH_VM (2) @@ -1963,6 +1971,12 @@ long page_compound_head; long irq_desc_irq_data; long kmem_cache_node_total_objects; + long timer_base_vectors; + long request_queue_mq_ops; + long request_queue_queue_ctx; + long blk_mq_ctx_rq_dispatched; + long blk_mq_ctx_rq_completed; + long task_struct_stack; }; struct size_table { /* stash of commonly-used sizes */ @@ -2105,6 +2119,7 @@ long tnt; long trace_print_flags; long task_struct_flags; + long timer_base; }; struct array_table { @@ -2403,6 +2418,8 @@ #define LIST_ALLOCATE (VERBOSE << 10) #define LIST_CALLBACK (VERBOSE << 11) #define CALLBACK_RETURN (VERBOSE << 12) +#define LIST_PARSE_MEMBER (VERBOSE << 13) +#define LIST_READ_MEMBER (VERBOSE << 14) struct tree_data { ulong flags; @@ -2419,6 +2436,8 @@ #define TREE_POSITION_DISPLAY (VERBOSE << 4) #define TREE_STRUCT_RADIX_10 (VERBOSE << 5) #define TREE_STRUCT_RADIX_16 (VERBOSE << 6) +#define TREE_PARSE_MEMBER (VERBOSE << 7) +#define TREE_READ_MEMBER (VERBOSE << 8) #define ALIAS_RUNTIME (1) #define ALIAS_RCLOCAL (2) @@ -2844,8 +2863,8 @@ #define PTOV(X) \ ((unsigned long)(X)-(machdep->machspec->phys_offset)+(machdep->machspec->page_offset)) -#define VTOP(X) \ - ((unsigned long)(X)-(machdep->machspec->page_offset)+(machdep->machspec->phys_offset)) + +#define VTOP(X) arm64_VTOP((ulong)(X)) #define USERSPACE_TOP (machdep->machspec->userspace_top) #define PAGE_OFFSET (machdep->machspec->page_offset) @@ -2882,6 +2901,28 @@ #define PMD_MASK_L3_4K (~(PMD_SIZE_L3_4K-1)) /* + * 4-levels / 4K pages + * 48-bit VA + */ +#define PTRS_PER_PGD_L4_4K ((1UL) << (48 - 39)) +#define PTRS_PER_PUD_L4_4K (512) +#define PTRS_PER_PMD_L4_4K (512) +#define PTRS_PER_PTE_L4_4K (512) +#define PGDIR_SHIFT_L4_4K (39) +#define PGDIR_SIZE_L4_4K ((1UL) << PGDIR_SHIFT_L4_4K) +#define PGDIR_MASK_L4_4K (~(PGDIR_SIZE_L4_4K-1)) +#define PUD_SHIFT_L4_4K (30) +#define PUD_SIZE_L4_4K ((1UL) << PUD_SHIFT_L4_4K) +#define PUD_MASK_L4_4K (~(PUD_SIZE_L4_4K-1)) +#define PMD_SHIFT_L4_4K (21) +#define PMD_SIZE_L4_4K (1UL << PMD_SHIFT_L4_4K) +#define PMD_MASK_L4_4K (~(PMD_SIZE_L4_4K-1)) + +#define PGDIR_SIZE_48VA (1UL << ((48 - 39) + 3)) +#define PGDIR_MASK_48VA (~(PGDIR_SIZE_48VA - 1)) +#define PGDIR_OFFSET_48VA(X) (((ulong)(X)) & (PGDIR_SIZE_48VA - 1)) + +/* * 3-levels / 64K pages */ #define PTRS_PER_PGD_L3_64K (64) @@ -2893,6 +2934,7 @@ #define PMD_SHIFT_L3_64K (29) #define PMD_SIZE_L3_64K (1UL << PMD_SHIFT_L3_64K) #define PMD_MASK_L3_64K (~(PMD_SIZE_L3_64K-1)) +#define PGDIR_OFFSET_L3_64K(X) (((ulong)(X)) & ((PTRS_PER_PGD_L3_64K * 8) - 1)) /* * 2-levels / 64K pages @@ -2938,18 +2980,29 @@ #define VM_L3_4K (0x10) #define KDUMP_ENABLED (0x20) #define IRQ_STACKS (0x40) +#define NEW_VMEMMAP (0x80) +#define VM_L4_4K (0x100) + +/* + * Get kimage_voffset from /dev/crash + */ +#define DEV_CRASH_ARCH_DATA _IOR('c', 1, unsigned long) /* * sources: Documentation/arm64/memory.txt * arch/arm64/include/asm/memory.h * arch/arm64/include/asm/pgtable.h */ - -#define ARM64_PAGE_OFFSET ((0xffffffffffffffffUL) << (machdep->machspec->VA_BITS - 1)) +#define ARM64_VA_START ((0xffffffffffffffffUL) \ + << machdep->machspec->VA_BITS) +#define ARM64_PAGE_OFFSET ((0xffffffffffffffffUL) \ + << (machdep->machspec->VA_BITS - 1)) #define ARM64_USERSPACE_TOP ((1UL) << machdep->machspec->VA_BITS) -#define ARM64_MODULES_VADDR (ARM64_PAGE_OFFSET - MEGABYTES(64)) -#define ARM64_MODULES_END (ARM64_PAGE_OFFSET - 1) -#define ARM64_VMALLOC_START ((0xffffffffffffffffUL) << machdep->machspec->VA_BITS) + +/* only used for v4.6 or later */ +#define ARM64_MODULES_VSIZE MEGABYTES(128) +#define ARM64_KASAN_SHADOW_SIZE (1UL << (machdep->machspec->VA_BITS - 3)) + /* * The following 3 definitions are the original values, but are obsolete * for 3.17 and later kernels because they are now build-time calculations. @@ -3028,6 +3081,18 @@ ulong kernel_flags; ulong irq_stack_size; ulong *irq_stacks; + char *irq_stackbuf; + ulong __irqentry_text_start; + ulong __irqentry_text_end; + /* for exception vector code */ + ulong exp_entry1_start; + ulong exp_entry1_end; + ulong exp_entry2_start; + ulong exp_entry2_end; + /* only needed for v4.6 or later kernel */ + ulong kimage_voffset; + ulong kimage_text; + ulong kimage_end; }; struct arm64_stackframe { @@ -3067,6 +3132,9 @@ #define __swp_offset(entry) SWP_OFFSET(entry) #define TIF_SIGPENDING (2) + +#define _SECTION_SIZE_BITS 26 +#define _MAX_PHYSMEM_BITS 32 #endif /* MIPS */ #ifdef X86 @@ -3737,7 +3805,7 @@ #define PMD_MASK (~((1UL << PMD_SHIFT) - 1)) /* shift to put page number into pte */ -#define PTE_SHIFT 16 +#define PTE_RPN_SHIFT_DEFAULT 16 #define PMD_TO_PTEPAGE_SHIFT 2 /* Used for 2.6 or later */ #define PTE_INDEX_SIZE 9 @@ -3759,7 +3827,15 @@ #define PMD_INDEX_SIZE_L4_4K 7 #define PUD_INDEX_SIZE_L4_4K 7 #define PGD_INDEX_SIZE_L4_4K 9 -#define PTE_SHIFT_L4_4K 17 +#define PUD_INDEX_SIZE_L4_4K_3_7 9 +#define PTE_INDEX_SIZE_RADIX_4K 9 +#define PMD_INDEX_SIZE_RADIX_4K 9 +#define PUD_INDEX_SIZE_RADIX_4K 9 +#define PGD_INDEX_SIZE_RADIX_4K 13 +#define PTE_RPN_SHIFT_L4_4K 17 +#define PTE_RPN_SHIFT_L4_4K_4_5 18 +#define PGD_MASKED_BITS_4K 0 +#define PUD_MASKED_BITS_4K 0 #define PMD_MASKED_BITS_4K 0 /* 64K pagesize */ @@ -3770,24 +3846,52 @@ #define PTE_INDEX_SIZE_L4_64K_3_10 8 #define PMD_INDEX_SIZE_L4_64K_3_10 10 #define PGD_INDEX_SIZE_L4_64K_3_10 12 -#define PTE_SHIFT_L4_64K_V1 32 -#define PTE_SHIFT_L4_64K_V2 30 -#define PTE_SHIFT_L4_BOOK3E_64K 28 -#define PTE_SHIFT_L4_BOOK3E_4K 24 +#define PMD_INDEX_SIZE_L4_64K_4_6 5 +#define PUD_INDEX_SIZE_L4_64K_4_6 5 +#define PTE_INDEX_SIZE_RADIX_64K 5 +#define PMD_INDEX_SIZE_RADIX_64K 9 +#define PUD_INDEX_SIZE_RADIX_64K 9 +#define PGD_INDEX_SIZE_RADIX_64K 13 +#define PTE_RPN_SHIFT_L4_64K_V1 32 +#define PTE_RPN_SHIFT_L4_64K_V2 30 +#define PTE_RPN_SHIFT_L4_BOOK3E_64K 28 +#define PTE_RPN_SHIFT_L4_BOOK3E_4K 24 +#define PGD_MASKED_BITS_64K 0 +#define PUD_MASKED_BITS_64K 0x1ff #define PMD_MASKED_BITS_64K 0x1ff +#define PMD_MASKED_BITS_64K_3_11 0xfff +#define PMD_MASKED_BITS_BOOK3E_64K_4_5 0x7ff +#define PGD_MASKED_BITS_64K_4_6 0xc0000000000000ffUL +#define PUD_MASKED_BITS_64K_4_6 0xc0000000000000ffUL +#define PMD_MASKED_BITS_64K_4_6 0xc0000000000000ffUL + +#define PTE_RPN_MASK_DEFAULT 0xffffffffffffffffUL +#define PTE_RPN_SIZE_L4_4_6 (PAGESIZE() == PPC64_64K_PAGE_SIZE ? 41 : 45) +#define PTE_RPN_MASK_L4_4_6 (((1UL << PTE_RPN_SIZE_L4_4_6) - 1) << PAGESHIFT()) +#define PTE_RPN_SHIFT_L4_4_6 PAGESHIFT() + +#define PGD_MASKED_BITS_4_7 0xc0000000000000ffUL +#define PUD_MASKED_BITS_4_7 0xc0000000000000ffUL +#define PMD_MASKED_BITS_4_7 0xc0000000000000ffUL #define PD_HUGE 0x8000000000000000 #define HUGE_PTE_MASK 0x03 #define HUGEPD_SHIFT_MASK 0x3f -#define L4_MASK (THIS_KERNEL_VERSION >= LINUX(3,10,0) ? 0xfff : 0x1ff) -#define L4_OFFSET(vaddr) ((vaddr >> (machdep->machspec->l4_shift)) & L4_MASK) +#define HUGEPD_ADDR_MASK (0x0fffffffffffffffUL & ~HUGEPD_SHIFT_MASK) + +#define PGD_MASK_L4 \ + (THIS_KERNEL_VERSION >= LINUX(3,10,0) ? (machdep->ptrs_per_pgd - 1) : 0x1ff) #define PGD_OFFSET_L4(vaddr) \ + ((vaddr >> (machdep->machspec->l4_shift)) & PGD_MASK_L4) + +#define PUD_OFFSET_L4(vaddr) \ ((vaddr >> (machdep->machspec->l3_shift)) & (machdep->machspec->ptrs_per_l3 - 1)) #define PMD_OFFSET_L4(vaddr) \ ((vaddr >> (machdep->machspec->l2_shift)) & (machdep->machspec->ptrs_per_l2 - 1)) +#define _PAGE_PTE (machdep->machspec->_page_pte) /* distinguishes PTEs from pointers */ #define _PAGE_PRESENT (machdep->machspec->_page_present) /* software: pte contains a translation */ #define _PAGE_USER (machdep->machspec->_page_user) /* matches one of the PP bits */ #define _PAGE_RW (machdep->machspec->_page_rw) /* software: user write access allowed */ @@ -3798,6 +3902,9 @@ #define _PAGE_DIRTY (machdep->machspec->_page_dirty) /* C: page changed */ #define _PAGE_ACCESSED (machdep->machspec->_page_accessed) /* R: page referenced */ +#define PTE_RPN_MASK (machdep->machspec->pte_rpn_mask) +#define PTE_RPN_SHIFT (machdep->machspec->pte_rpn_shift) + #define TIF_SIGPENDING (2) #define SWP_TYPE(entry) (((entry) >> 1) & 0x7f) @@ -3985,6 +4092,8 @@ #define KILOBYTES(x) ((x) * (1024)) #define MEGABYTES(x) ((x) * (1048576)) #define GIGABYTES(x) ((x) * (1073741824)) +#define TB_SHIFT (40) +#define TERABYTES(x) ((x) * (1UL << TB_SHIFT)) #define MEGABYTE_MASK (MEGABYTES(1)-1) @@ -4400,7 +4509,11 @@ #endif }; +/* + * include/linux/sched.h + */ #define PF_EXITING 0x00000004 /* getting shut down */ +#define PF_KTHREAD 0x00200000 /* I am a kernel thread */ extern long _ZOMBIE_; #define IS_ZOMBIE(task) (task_state(task) & _ZOMBIE_) @@ -4720,7 +4833,7 @@ char *strdupbuf(char *); #define GETBUF(X) getbuf((long)(X)) #define FREEBUF(X) freebuf((char *)(X)) -#define RESIZEBUF(X,Y,Z) (X) = resizebuf((char *)(X), (long)(Y), (long)(Z)); +#define RESIZEBUF(X,Y,Z) (X) = (typeof(X))resizebuf((char *)(X), (long)(Y), (long)(Z)); #define STRDUPBUF(X) strdupbuf((char *)(X)) void sigsetup(int, void *, struct sigaction *, struct sigaction *); #define SIGACTION(s, h, a, o) sigsetup(s, h, a, o) @@ -4740,6 +4853,7 @@ int endian_mismatch(char *, char, ulong); uint16_t swap16(uint16_t, int); uint32_t swap32(uint32_t, int); +uint64_t swap64(uint64_t, int); ulong *get_cpumask_buf(void); int make_cpumask(char *, ulong *, int, int *); size_t strlcpy(char *, char *, size_t); @@ -4870,6 +4984,7 @@ void parse_for_member_extended(struct datatype_member *, ulong); void add_to_downsized(char *); int is_downsized(char *); +int is_string(char *, char *); /* * memory.c @@ -5144,6 +5259,7 @@ void sort_tgid_array(void); int sort_by_tgid(const void *, const void *); int in_irq_ctx(ulonglong, int, ulong); +void check_stack_overflow(void); /* * extensions.c @@ -5236,6 +5352,7 @@ #define BT_ERROR_MASK (BT_LOOP_TRAP|BT_WRAP_TRAP|BT_KERNEL_THREAD|BT_CPU_IDLE) #define BT_UNWIND_ERROR (0x2000000ULL) #define BT_OLD_BACK_TRACE (0x4000000ULL) +#define BT_OPT_BACK_TRACE (0x4000000ULL) #define BT_FRAMESIZE_DEBUG (0x8000000ULL) #define BT_CONTEXT_SWITCH (0x10000000ULL) #define BT_HARDIRQ (0x20000000ULL) @@ -5385,6 +5502,7 @@ #ifdef ARM64 void arm64_init(int); void arm64_dump_machdep_table(ulong); +ulong arm64_VTOP(ulong); int arm64_IS_VMALLOC_ADDR(ulong); ulong arm64_swp_type(ulong); ulong arm64_swp_offset(ulong); @@ -5512,6 +5630,7 @@ ulong page_protnone; ulong GART_start; ulong GART_end; + ulong kernel_image_size; }; #define KSYMS_START (0x1) @@ -5526,6 +5645,7 @@ #define FRAMEPOINTER (0x200) #define GART_REGION (0x400) #define NESTED_NMI (0x800) +#define RANDOMIZED (0x1000) #define VM_FLAGS (VM_ORIG|VM_2_6_11|VM_XEN|VM_XEN_RHEL4) @@ -5608,14 +5728,13 @@ ulong hwintrstack[NR_CPUS]; char *hwstackbuf; uint hwstacksize; - char *level4; - ulong last_level4_read; uint l4_index_size; uint l3_index_size; uint l2_index_size; uint l1_index_size; + uint ptrs_per_l4; uint ptrs_per_l3; uint ptrs_per_l2; uint ptrs_per_l1; @@ -5625,13 +5744,17 @@ uint l2_shift; uint l1_shift; - uint pte_shift; - uint l2_masked_bits; + uint pte_rpn_shift; + ulong pte_rpn_mask; + ulong pgd_masked_bits; + ulong pud_masked_bits; + ulong pmd_masked_bits; int vmemmap_cnt; int vmemmap_psize; ulong vmemmap_base; struct ppc64_vmemmap *vmemmap_list; + ulong _page_pte; ulong _page_present; ulong _page_user; ulong _page_rw; @@ -5645,23 +5768,21 @@ int (*is_vmaddr)(ulong); }; -#define IS_LAST_L4_READ(l4) ((ulong)(l4) == machdep->machspec->last_level4_read) - -#define FILL_L4(L4, TYPE, SIZE) \ - if (!IS_LAST_L4_READ(L4)) { \ - readmem((ulonglong)((ulong)(L4)), TYPE, machdep->machspec->level4, \ - SIZE, "level4 page", FAULT_ON_ERROR); \ - machdep->machspec->last_level4_read = (ulong)(L4); \ - } - void ppc64_init(int); void ppc64_dump_machdep_table(ulong); #define display_idt_table() \ error(FATAL, "-d option is not applicable to PowerPC architecture\n") -#define KSYMS_START (0x1) -#define VM_ORIG (0x2) -#define VMEMMAP_AWARE (0x4) -#define BOOK3E (0x8) +#define KSYMS_START (0x1) +#define VM_ORIG (0x2) +#define VMEMMAP_AWARE (0x4) +#define BOOK3E (0x8) +#define PHYS_ENTRY_L4 (0x10) +#define SWAP_ENTRY_L4 (0x20) +/* + * The flag bit for radix MMU in cpu_spec.mmu_features + * in the kernel is also 0x40. + */ +#define RADIX_MMU (0x40) #define REGION_SHIFT (60UL) #define REGION_ID(addr) (((unsigned long)(addr)) >> REGION_SHIFT) @@ -5820,6 +5941,11 @@ #define KSYMS_START (0x1) #endif +/* + * mips.c + */ +void mips_display_regs_from_elf_notes(int, FILE *); + #ifdef MIPS void mips_init(int); void mips_dump_machdep_table(ulong); @@ -5879,6 +6005,8 @@ #define _PAGE_NO_EXEC (machdep->machspec->_page_no_exec) #define _PAGE_DIRTY (machdep->machspec->_page_dirty) #define _PFN_SHIFT (machdep->machspec->_pfn_shift) + + struct mips_regset *crash_task_regs; }; #endif /* MIPS */ diff -Nru crash-7.1.5/dev.c crash-7.1.7/dev.c --- crash-7.1.5/dev.c 2016-04-27 20:46:50.000000000 +0200 +++ crash-7.1.7/dev.c 2016-11-30 19:56:29.000000000 +0100 @@ -3800,18 +3800,84 @@ return i->get_gendisk(klist_node_address); } +static int +use_mq_interface(unsigned long q) +{ + unsigned long mq_ops; + + if (!VALID_MEMBER(request_queue_mq_ops)) + return 0; + + readmem(q + OFFSET(request_queue_mq_ops), KVADDR, &mq_ops, + sizeof(ulong), "request_queue.mq_ops", FAULT_ON_ERROR); + + if (mq_ops == 0) + return 0; + else + return 1; +} + +static void +get_one_mctx_diskio(unsigned long mctx, struct diskio *io) +{ + unsigned long dispatch[2]; + unsigned long comp[2]; + + readmem(mctx + OFFSET(blk_mq_ctx_rq_dispatched), + KVADDR, dispatch, sizeof(ulong) * 2, "blk_mq_ctx.rq_dispatched", + FAULT_ON_ERROR); + + readmem(mctx + OFFSET(blk_mq_ctx_rq_completed), + KVADDR, comp, sizeof(ulong) * 2, "blk_mq_ctx.rq_completed", + FAULT_ON_ERROR); + + io->read = (dispatch[0] - comp[0]); + io->write = (dispatch[1] - comp[1]); +} + +static void +get_mq_diskio(unsigned long q, unsigned long *mq_count) +{ + int cpu; + unsigned long queue_ctx; + unsigned long mctx_addr; + struct diskio tmp; + + memset(&tmp, 0x00, sizeof(struct diskio)); + + readmem(q + OFFSET(request_queue_queue_ctx), KVADDR, &queue_ctx, + sizeof(ulong), "request_queue.queue_ctx", + FAULT_ON_ERROR); + + for (cpu = 0; cpu < kt->cpus; cpu++) { + if ((kt->flags & SMP) && (kt->flags & PER_CPU_OFF)) { + mctx_addr = queue_ctx + kt->__per_cpu_offset[cpu]; + get_one_mctx_diskio(mctx_addr, &tmp); + mq_count[0] += tmp.read; + mq_count[1] += tmp.write; + } + } +} + /* read request_queue.rq.count[2] */ static void get_diskio_1(unsigned long rq, struct diskio *io) { int count[2]; + unsigned long mq_count[2] = { 0 }; - readmem(rq + OFFSET(request_queue_rq) + OFFSET(request_list_count), - KVADDR, count, sizeof(int) * 2, "request_list.count", - FAULT_ON_ERROR); + if (!use_mq_interface(rq)) { + readmem(rq + OFFSET(request_queue_rq) + + OFFSET(request_list_count), KVADDR, count, + sizeof(int) * 2, "request_list.count", FAULT_ON_ERROR); - io->read = count[0]; - io->write = count[1]; + io->read = count[0]; + io->write = count[1]; + } else { + get_mq_diskio(rq, mq_count); + io->read = mq_count[0]; + io->write = mq_count[1]; + } } /* request_queue.in_flight contains total requests */ @@ -3961,9 +4027,8 @@ readmem(gendisk + OFFSET(gendisk_major), KVADDR, &major, sizeof(int), "gen_disk.major", FAULT_ON_ERROR); i->get_diskio(queue_addr, &io); - in_flight = i->get_in_flight(queue_addr); - fprintf(fp, "%s%s%s %s%s%s%s %s%5d%s%s%s%s%s%5u\n", + fprintf(fp, "%s%s%s %s%s%s%s %s%5d%s%s%s%s%s", mkstring(buf0, 5, RJUST|INT_DEC, (char *)(unsigned long)major), space(MINSPACE), mkstring(buf1, VADDR_PRLEN, LJUST|LONG_HEX, (char *)gendisk), @@ -3980,8 +4045,13 @@ space(MINSPACE), mkstring(buf5, 5, RJUST|INT_DEC, (char *)(unsigned long)io.write), - space(MINSPACE), - in_flight); + space(MINSPACE)); + + if (!use_mq_interface(queue_addr)) { + in_flight = i->get_in_flight(queue_addr); + fprintf(fp, "%5u\n", in_flight); + } else + fprintf(fp, "%s\n", "N/A(MQ)"); } static void @@ -4056,6 +4126,16 @@ MEMBER_OFFSET_INIT(request_queue_rq, "request_queue", "rq"); else MEMBER_OFFSET_INIT(request_queue_rq, "request_queue", "root_rl"); + if (MEMBER_EXISTS("request_queue", "mq_ops")) { + MEMBER_OFFSET_INIT(request_queue_mq_ops, "request_queue", + "mq_ops"); + ANON_MEMBER_OFFSET_INIT(request_queue_queue_ctx, + "request_queue", "queue_ctx"); + MEMBER_OFFSET_INIT(blk_mq_ctx_rq_dispatched, "blk_mq_ctx", + "rq_dispatched"); + MEMBER_OFFSET_INIT(blk_mq_ctx_rq_completed, "blk_mq_ctx", + "rq_completed"); + } MEMBER_OFFSET_INIT(subsys_private_klist_devices, "subsys_private", "klist_devices"); MEMBER_OFFSET_INIT(subsystem_kset, "subsystem", "kset"); diff -Nru crash-7.1.5/extensions/trace.c crash-7.1.7/extensions/trace.c --- crash-7.1.5/extensions/trace.c 2016-04-27 20:46:50.000000000 +0200 +++ crash-7.1.7/extensions/trace.c 2016-11-30 19:56:29.000000000 +0100 @@ -38,6 +38,10 @@ * max_buffer is supported */ static int max_buffer_available; +/* + * multiple trace instances are supported + */ +static int multiple_instances_available; #define koffset(struct, member) struct##_##member##_offset @@ -78,6 +82,8 @@ static int koffset(ftrace_event_field, size); static int koffset(ftrace_event_field, is_signed); +static int koffset(trace_array, name); + static int koffset(POINTER_SYM, POINTER) = 0; struct ring_buffer_per_cpu { @@ -101,16 +107,25 @@ }; static ulong global_trace; -static ulong global_trace_buffer; -static ulong global_max_buffer; -static ulong global_ring_buffer; -static unsigned global_pages; -static struct ring_buffer_per_cpu *global_buffers; - static ulong max_tr_trace; -static ulong max_tr_ring_buffer; -static unsigned max_tr_pages; -static struct ring_buffer_per_cpu *max_tr_buffers; + +struct trace_instance { + char name[NAME_MAX + 1]; + ulong trace_buffer; + ulong max_buffer; + ulong ring_buffer; + unsigned pages; + struct ring_buffer_per_cpu *buffers; + + ulong max_tr_ring_buffer; + unsigned max_tr_pages; + struct ring_buffer_per_cpu *max_tr_buffers; +}; + +static ulong ftrace_trace_arrays; +static struct trace_instance global_trace_instance; +static struct trace_instance *trace_instances = NULL; +static int instance_count; static ulong ftrace_events; static ulong current_trace; @@ -229,6 +244,9 @@ init_offset(ftrace_event_field, size); init_offset(ftrace_event_field, is_signed); + if (MEMBER_EXISTS("trace_array", "name")) + init_offset(trace_array, name); + return 0; #undef init_offset } @@ -435,61 +453,137 @@ return -1; } -static int ftrace_int_global_trace(void) +static int ftrace_init_trace(struct trace_instance *ti, ulong instance_addr) { if (trace_buffer_available) { - global_trace_buffer = global_trace + koffset(trace_array, trace_buffer); - read_value(global_ring_buffer, global_trace_buffer, trace_buffer, buffer); + ti->trace_buffer = instance_addr + + koffset(trace_array, trace_buffer); + read_value(ti->ring_buffer, ti->trace_buffer, + trace_buffer, buffer); + + if (max_buffer_available) { + ti->max_buffer = instance_addr + + koffset(trace_array, max_buffer); + read_value(ti->max_tr_ring_buffer, ti->max_buffer, + trace_buffer, buffer); + } } else { - read_value(global_ring_buffer, global_trace, trace_array, buffer); - read_value(global_pages, global_ring_buffer, ring_buffer, pages); + read_value(ti->ring_buffer, instance_addr, trace_array, buffer); + read_value(ti->pages, ti->ring_buffer, ring_buffer, pages); + + read_value(ti->max_tr_ring_buffer, max_tr_trace, trace_array, buffer); + if (ti->max_tr_ring_buffer) + read_value(ti->max_tr_pages, ti->max_tr_ring_buffer, ring_buffer, pages); } - global_buffers = calloc(sizeof(*global_buffers), nr_cpu_ids); - if (global_buffers == NULL) + ti->buffers = calloc(sizeof(*ti->buffers), nr_cpu_ids); + if (ti->buffers == NULL) + goto out_fail; + + if (ftrace_init_buffers(ti->buffers, ti->ring_buffer, + ti->pages) < 0) + goto out_fail; + + ti->max_tr_buffers = calloc(sizeof(*ti->max_tr_buffers), nr_cpu_ids); + if (ti->max_tr_buffers == NULL) goto out_fail; - if (ftrace_init_buffers(global_buffers, global_ring_buffer, - global_pages) < 0) + if (ftrace_init_buffers(ti->max_tr_buffers, ti->max_tr_ring_buffer, + ti->max_tr_pages) < 0) goto out_fail; return 0; out_fail: - free(global_buffers); + free(ti->max_tr_buffers); + free(ti->buffers); return -1; } -static int ftrace_int_max_tr_trace(void) +static void ftrace_destroy_all_instance_buffers() { - if (trace_buffer_available) { - if (!max_buffer_available) - return 0; + int i; - global_max_buffer = global_trace + koffset(trace_array, max_buffer); - read_value(max_tr_ring_buffer, global_max_buffer, trace_buffer, buffer); - } else { - read_value(max_tr_ring_buffer, max_tr_trace, trace_array, buffer); + for (i = 0; i < instance_count; i++) + { + struct trace_instance *ti = &trace_instances[i]; - if (!max_tr_ring_buffer) - return 0; + if (ti->max_tr_ring_buffer) { + ftrace_destroy_buffers(ti->max_tr_buffers); + free(ti->max_tr_buffers); + } - read_value(max_tr_pages, max_tr_ring_buffer, ring_buffer, pages); + ftrace_destroy_buffers(ti->buffers); + free(ti->buffers); } +} - max_tr_buffers = calloc(sizeof(*max_tr_buffers), nr_cpu_ids); - if (max_tr_buffers == NULL) - goto out_fail; +static void ftrace_destroy_instances() +{ + ftrace_destroy_all_instance_buffers(); + free(trace_instances); +} - if (ftrace_init_buffers(max_tr_buffers, max_tr_ring_buffer, - max_tr_pages) < 0) - goto out_fail; +static int ftrace_init_instances() +{ + int i; + struct trace_instance *ti; + struct list_data list_data; + struct list_data *ld = &list_data; + + if (!multiple_instances_available) + return 0; + + BZERO(ld, sizeof(struct list_data)); + ld->start = ftrace_trace_arrays; + ld->end = global_trace; + ld->flags = LIST_ALLOCATE; + instance_count = do_list(ld); + + /* The do_list count includes the list_head, which is not a + * proper instance */ + instance_count--; + if (instance_count <= 0) + return 0; + + trace_instances = calloc(sizeof(struct trace_instance), instance_count); + + /* We start i at 1 to skip over the list_head and continue to the last + * instance, which lies at index instance_count */ + for (i = 1; i <= instance_count; i++) + { + ulong instance_ptr; + ulong name_addr; + int ret; + + ti = &trace_instances[i-1]; + instance_ptr = ld->list_ptr[i]; + read_value(name_addr, instance_ptr, trace_array, name); + if (!name_addr) + { + console("Instance name is NULL\n"); + } + else if (!read_string(name_addr, ti->name, sizeof(ti->name))) + { + console("Failed to read instance name at address %p\n", (void*)name_addr); + goto out_fail; + } + + ret = ftrace_init_trace(ti, instance_ptr); + if (ret < 0) + goto out_fail; + } + FREEBUF(ld->list_ptr); return 0; out_fail: - free(max_tr_buffers); - max_tr_ring_buffer = 0; + /* We've already freed the current instance's trace buffer info, so + * we'll clear that out to avoid double freeing in + * ftrace_destroy_instances() */ + BZERO(ti, sizeof(struct trace_instance)); + ftrace_destroy_instances(); + return -1; } @@ -504,7 +598,7 @@ } else { read_value(addr, current_trace, POINTER_SYM, POINTER); } - + read_value(addr, addr, tracer, name); read_string(addr, tmp, 128); @@ -524,9 +618,11 @@ struct syment *sym_max_tr_trace; struct syment *sym_ftrace_events; struct syment *sym_current_trace; + struct syment *sym_ftrace_trace_arrays; sym_global_trace = symbol_search("global_trace"); sym_ftrace_events = symbol_search("ftrace_events"); + sym_ftrace_trace_arrays = symbol_search("ftrace_trace_arrays"); if (sym_global_trace == NULL || sym_ftrace_events == NULL) return -1; @@ -534,6 +630,13 @@ global_trace = sym_global_trace->value; ftrace_events = sym_ftrace_events->value; + if (sym_ftrace_trace_arrays) + { + multiple_instances_available = 1; + ftrace_trace_arrays = sym_ftrace_trace_arrays->value; + } + + if (MEMBER_EXISTS("trace_array", "current_trace")) { encapsulated_current_trace = 1; } else { @@ -564,28 +667,31 @@ return -1; print_offsets(); - if (ftrace_int_global_trace() < 0) + if (ftrace_init_trace(&global_trace_instance, global_trace) < 0) goto out_0; - ftrace_int_max_tr_trace(); + if (ftrace_init_instances() < 0) + goto out_1; if (ftrace_init_event_types() < 0) - goto out_1; + goto out_2; if (ftrace_init_current_tracer() < 0) - goto out_2; + goto out_3; return 0; -out_2: +out_3: ftrace_destroy_event_types(); +out_2: + ftrace_destroy_instances(); out_1: - if (max_tr_ring_buffer) { - ftrace_destroy_buffers(max_tr_buffers); - free(max_tr_buffers); + if (global_trace_instance.max_tr_ring_buffer) { + ftrace_destroy_buffers(global_trace_instance.max_tr_buffers); + free(global_trace_instance.max_tr_buffers); } - ftrace_destroy_buffers(global_buffers); - free(global_buffers); + ftrace_destroy_buffers(global_trace_instance.buffers); + free(global_trace_instance.buffers); out_0: return -1; } @@ -595,13 +701,15 @@ free(current_tracer_name); ftrace_destroy_event_types(); - if (max_tr_ring_buffer) { - ftrace_destroy_buffers(max_tr_buffers); - free(max_tr_buffers); + ftrace_destroy_instances(); + + if (global_trace_instance.max_tr_ring_buffer) { + ftrace_destroy_buffers(global_trace_instance.max_tr_buffers); + free(global_trace_instance.max_tr_buffers); } - ftrace_destroy_buffers(global_buffers); - free(global_buffers); + ftrace_destroy_buffers(global_trace_instance.buffers); + free(global_trace_instance.buffers); } static int ftrace_dump_page(int fd, ulong page, void *page_tmp) @@ -652,7 +760,8 @@ return 0; } -static int ftrace_dump_buffers(const char *per_cpu_path) +static int ftrace_dump_buffers(const char *per_cpu_path, + struct trace_instance *ti) { int i; void *page_tmp; @@ -664,7 +773,7 @@ return -1; for (i = 0; i < nr_cpu_ids; i++) { - struct ring_buffer_per_cpu *cpu_buffer = &global_buffers[i]; + struct ring_buffer_per_cpu *cpu_buffer = &ti->buffers[i]; if (!cpu_buffer->kaddr) continue; @@ -679,7 +788,7 @@ if (fd < 0) goto out_fail; - ftrace_dump_buffer(fd, cpu_buffer, global_pages, page_tmp); + ftrace_dump_buffer(fd, cpu_buffer, ti->pages, page_tmp); close(fd); } @@ -1015,8 +1124,6 @@ free(ftrace_common_fields); } -#define TRACE_EVENT_FL_TRACEPOINT 0x40 - static int ftrace_get_event_type_name(ulong call, char *name, int len) { @@ -1024,34 +1131,35 @@ static int name_offset; static int flags_offset; static int tp_name_offset; - uint flags; + static long tracepoint_flag; + uint flags; ulong name_addr; if (inited) goto work; - inited = 1; - name_offset = MAX(MEMBER_OFFSET("ftrace_event_call", "name"), - MEMBER_OFFSET("trace_event_call", "name")); - if (name_offset >= 0) - goto work; - - name_offset = MAX(ANON_MEMBER_OFFSET("ftrace_event_call", "name"), - ANON_MEMBER_OFFSET("trace_event_call", "name")); - if (name_offset < 0) - return -1; + name_offset = MAX(MEMBER_OFFSET("ftrace_event_call", "tp"), + MEMBER_OFFSET("trace_event_call", "tp")); + if (name_offset >= 0) { + flags_offset = MAX(MEMBER_OFFSET("ftrace_event_call", "flags"), + MEMBER_OFFSET("trace_event_call", "flags")); + if (flags_offset < 0) + return -1; - flags_offset = MAX(MEMBER_OFFSET("ftrace_event_call", "flags"), - MEMBER_OFFSET("trace_event_call", "flags")); - if (flags_offset < 0) - return -1; + tp_name_offset = MEMBER_OFFSET("tracepoint", "name"); + if (tp_name_offset < 0) + return -1; - tp_name_offset = MEMBER_OFFSET("tracepoint", "name"); - if (tp_name_offset < 0) - return -1; + if (!enumerator_value("TRACE_EVENT_FL_TRACEPOINT", &tracepoint_flag)) + return -1; - inited = 2; + inited = 2; + } else { + name_offset = MAX(MEMBER_OFFSET("ftrace_event_call", "name"), + MEMBER_OFFSET("trace_event_call", "name")); + inited = 1; + } work: if (name_offset < 0) @@ -1067,7 +1175,7 @@ RETURN_ON_ERROR)) return -1; - if (flags & TRACE_EVENT_FL_TRACEPOINT) { + if (flags & (uint)tracepoint_flag) { if (!readmem(name_addr + tp_name_offset, KVADDR, &name_addr, sizeof(name_addr), "read tracepoint name", RETURN_ON_ERROR)) @@ -1476,26 +1584,72 @@ static int trace_cmd_data_output(int fd); +#define FTRACE_DUMP_SYMBOLS (1 << 0) +#define FTRACE_DUMP_META_DATA (1 << 1) + +static int populate_ftrace_dir_tree(struct trace_instance *ti, + char *root, uint flags) +{ + char path[PATH_MAX]; + int ret; + + ret = mkdir(root, 0755); + if (ret < 0) { + if (errno == EEXIST) + error(INFO, "mkdir: %s exists\n", root); + return FALSE; + } + + snprintf(path, sizeof(path), "%s/per_cpu", root); + if (try_mkdir(path, 0755) < 0) + return FALSE; + + if (ftrace_dump_buffers(path, ti) < 0) + return FALSE; + + if (flags & FTRACE_DUMP_META_DATA) { + /* Dump event types */ + snprintf(path, sizeof(path), "%s/events", root); + if (try_mkdir(path, 0755) < 0) + return FALSE; + + if (ftrace_dump_event_types(path) < 0) + return FALSE; + + /* Dump pids with corresponding cmdlines */ + if (dump_saved_cmdlines(root) < 0) + return FALSE; + } + + if (flags & FTRACE_DUMP_SYMBOLS) { + /* Dump all symbols of the kernel */ + dump_kallsyms(root); + } + + return TRUE; +} + static void ftrace_dump(int argc, char *argv[]) { int c; - int dump_meta_data = 0; - int dump_symbols = 0; + int i; + uint flags = 0; char *dump_tracing_dir; - char path[PATH_MAX]; - int ret; + char instance_path[PATH_MAX]; while ((c = getopt(argc, argv, "smt")) != EOF) { switch(c) { case 's': - dump_symbols = 1; + flags |= FTRACE_DUMP_SYMBOLS; break; case 'm': - dump_meta_data = 1; + flags |= FTRACE_DUMP_META_DATA; break; case 't': - if (dump_symbols || dump_meta_data || argc - optind > 1) + if (flags & FTRACE_DUMP_SYMBOLS || + flags & FTRACE_DUMP_META_DATA || + argc - optind > 1) cmd_usage(pc->curcmd, SYNOPSIS); else { char *trace_dat = "trace.dat"; @@ -1526,38 +1680,34 @@ return; } - ret = mkdir(dump_tracing_dir, 0755); - if (ret < 0) { - if (errno == EEXIST) - error(INFO, "mkdir: %s exists\n", dump_tracing_dir); + if (!populate_ftrace_dir_tree(&global_trace_instance, dump_tracing_dir, flags)) return; - } - snprintf(path, sizeof(path), "%s/per_cpu", dump_tracing_dir); - if (try_mkdir(path, 0755) < 0) + if (!multiple_instances_available || instance_count == 0) return; - if (ftrace_dump_buffers(path) < 0) + /* Create an instances directory, and dump instance data in there */ + snprintf(instance_path, sizeof(instance_path), + "%s/instances", dump_tracing_dir); + if (try_mkdir(instance_path, 0755) < 0) return; - if (dump_meta_data) { - /* Dump event types */ - snprintf(path, sizeof(path), "%s/events", dump_tracing_dir); - if (try_mkdir(path, 0755) < 0) - return; + /* Don't care about the flags anymore */ + flags = 0; - if (ftrace_dump_event_types(path) < 0) - return; + for (i = 0; i < instance_count; i++) + { + struct trace_instance *ti = &trace_instances[i]; + + snprintf(instance_path, sizeof(instance_path), + "%s/instances/%s", dump_tracing_dir, + ti->name); - /* Dump pids with corresponding cmdlines */ - if (dump_saved_cmdlines(dump_tracing_dir) < 0) - return; + if (populate_ftrace_dir_tree(ti, instance_path, flags) < 0) + break; } - if (dump_symbols) { - /* Dump all symbols of the kernel */ - dump_kallsyms(dump_tracing_dir); - } + return; } static void ftrace_show(int argc, char *argv[]) @@ -2161,26 +2311,69 @@ return tmp_file_flush(fd); } -static int save_res_data(int fd, int nr_cpu_buffers) +/* From trace-cmd.h */ +enum { + TRACECMD_OPTION_DONE, /* 0 */ + TRACECMD_OPTION_DATE, /* 1 */ + TRACECMD_OPTION_CPUSTAT, /* 2 */ + TRACECMD_OPTION_BUFFER, /* 3 */ + TRACECMD_OPTION_TRACECLOCK, /* 4 */ + TRACECMD_OPTION_UNAME, /* 5 */ + TRACECMD_OPTION_HOOK, /* 6 */ +}; + +static int write_options(int fd, unsigned long long *buffer_offsets) { - unsigned short option = 0; + int i; + unsigned short option; - if (write_and_check(fd, &nr_cpu_buffers, 4)) - return -1; + if (!multiple_instances_available) + return 0; if (write_and_check(fd, "options ", 10)) return -1; + option = TRACECMD_OPTION_BUFFER; + for (i = 0; i < instance_count; i++) + { + char *name = trace_instances[i].name; + size_t name_size = strlen(name) + 1; /* Name length + '\0' */ + unsigned long long option_size = 8 + name_size; + unsigned long long offset; + + offset = buffer_offsets ? buffer_offsets[i] : 0; + if (write_and_check(fd, &option, 2)) + return -1; + if (write_and_check(fd, &option_size, 4)) + return -1; + if (write_and_check(fd, &offset, 8)) + return -1; + if (write_and_check(fd, name, name_size)) + return -1; + } + + option = TRACECMD_OPTION_DONE; if (write_and_check(fd, &option, 2)) return -1; + return 0; +} + +static int save_res_data(int fd, int nr_cpu_buffers, unsigned long long *buffer_offsets) +{ + if (write_and_check(fd, &nr_cpu_buffers, 4)) + return -1; + + if (write_options(fd, buffer_offsets)) + return -1; + if (write_and_check(fd, "flyrecord", 10)) return -1; return 0; } -static int save_record_data(int fd, int nr_cpu_buffers) +static int save_record_data(int fd, int nr_cpu_buffers, struct trace_instance *ti) { int i, j; unsigned long long offset, buffer_offset; @@ -2192,7 +2385,7 @@ buffer_offset = offset; for (i = 0; i < nr_cpu_ids; i++) { - struct ring_buffer_per_cpu *cpu_buffer = &global_buffers[i]; + struct ring_buffer_per_cpu *cpu_buffer = &ti->buffers[i]; unsigned long long buffer_size; if (!cpu_buffer->kaddr) @@ -2212,7 +2405,7 @@ lseek(fd, offset, SEEK_SET); for (i = 0; i < nr_cpu_ids; i++) { - struct ring_buffer_per_cpu *cpu_buffer = &global_buffers[i]; + struct ring_buffer_per_cpu *cpu_buffer = &ti->buffers[i]; if (!cpu_buffer->kaddr) continue; @@ -2231,13 +2424,13 @@ return 0; } -static int __trace_cmd_data_output(int fd) +static int get_nr_cpu_buffers(struct trace_instance *ti) { int i; int nr_cpu_buffers = 0; for (i = 0; i < nr_cpu_ids; i++) { - struct ring_buffer_per_cpu *cpu_buffer = &global_buffers[i]; + struct ring_buffer_per_cpu *cpu_buffer = &ti->buffers[i]; if (!cpu_buffer->kaddr) continue; @@ -2245,6 +2438,19 @@ nr_cpu_buffers++; } + return nr_cpu_buffers; +} + +static int __trace_cmd_data_output(int fd) +{ + int nr_cpu_buffers; + unsigned long long global_res_data_offset; + unsigned long long *instance_offsets; + + instance_offsets = calloc(sizeof(unsigned long long), instance_count); + + nr_cpu_buffers = get_nr_cpu_buffers(&global_trace_instance); + if (save_initial_data(fd)) return -1; if (save_header_files(fd)) @@ -2257,9 +2463,38 @@ return -1; if (save_ftrace_cmdlines(fd)) return -1; - if (save_res_data(fd, nr_cpu_buffers)) + + /* We don't have the instance buffer offsets yet, so we'll write in 0s + * for now, and fix it up after we have that information available */ + global_res_data_offset = lseek(fd, 0, SEEK_CUR); + if (save_res_data(fd, nr_cpu_buffers, NULL)) return -1; - if (save_record_data(fd, nr_cpu_buffers)) + if (save_record_data(fd, nr_cpu_buffers, &global_trace_instance)) + return -1; + + if (multiple_instances_available) + { + int i; + + for (i = 0; i < instance_count; i++) + { + struct trace_instance *ti = &trace_instances[i]; + nr_cpu_buffers = get_nr_cpu_buffers(ti); + + /* Save off the instance offset for fixup later */ + instance_offsets[i] = lseek(fd, 0, SEEK_CUR); + + if (write_and_check(fd, "flyrecord", 10)) + return -1; + if (save_record_data(fd, nr_cpu_buffers, ti)) + return -1; + } + } + + /* Fix up the global trace's options header with the instance offsets */ + lseek(fd, global_res_data_offset, SEEK_SET); + nr_cpu_buffers = get_nr_cpu_buffers(&global_trace_instance); + if (save_res_data(fd, nr_cpu_buffers, instance_offsets)) return -1; return 0; diff -Nru crash-7.1.5/filesys.c crash-7.1.7/filesys.c --- crash-7.1.5/filesys.c 2016-04-27 20:46:50.000000000 +0200 +++ crash-7.1.7/filesys.c 2016-11-30 19:56:29.000000000 +0100 @@ -1,8 +1,8 @@ /* filesys.c - core analysis suite * * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. - * Copyright (C) 2002-2015 David Anderson - * Copyright (C) 2002-2015 Red Hat, Inc. All rights reserved. + * Copyright (C) 2002-2016 David Anderson + * Copyright (C) 2002-2016 Red Hat, Inc. All rights reserved. * * 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 @@ -134,7 +134,7 @@ pc->nfd = -1; } - if (ACTIVE() && !(pc->namelist_debug || pc->system_map)) { + if (LOCAL_ACTIVE() && !(pc->namelist_debug || pc->system_map)) { memory_source_init(); match_proc_version(); } @@ -162,13 +162,13 @@ static void memory_source_init(void) { - if (REMOTE() && !(pc->flags & MEMSRC_LOCAL)) + if (REMOTE() && !(pc->flags2 & MEMSRC_LOCAL)) return; if (pc->flags & KERNEL_DEBUG_QUERY) return; - if (ACTIVE()) { + if (LOCAL_ACTIVE()) { if (pc->mfd != -1) /* already been here */ return; diff -Nru crash-7.1.5/gdb-7.6.patch crash-7.1.7/gdb-7.6.patch --- crash-7.1.5/gdb-7.6.patch 2016-04-27 20:46:50.000000000 +0200 +++ crash-7.1.7/gdb-7.6.patch 2016-11-30 19:56:29.000000000 +0100 @@ -2206,3 +2206,96 @@ + gi->finished = 1; +} #endif +--- gdb-7.6/bfd/elf64-s390.c.orig ++++ gdb-7.6/bfd/elf64-s390.c +@@ -323,10 +323,10 @@ elf_s390_reloc_name_lookup (bfd *abfd AT + && strcasecmp (elf_howto_table[i].name, r_name) == 0) + return &elf_howto_table[i]; + +- if (strcasecmp (elf64_s390_vtinherit_howto.name, r_name) == 0) +- return &elf64_s390_vtinherit_howto; +- if (strcasecmp (elf64_s390_vtentry_howto.name, r_name) == 0) +- return &elf64_s390_vtentry_howto; ++ if (strcasecmp (elf64_s390_vtinherit_howto.name, r_name) == 0) ++ return &elf64_s390_vtinherit_howto; ++ if (strcasecmp (elf64_s390_vtentry_howto.name, r_name) == 0) ++ return &elf64_s390_vtentry_howto; + + return NULL; + } +--- gdb-7.6/gdb/symtab.c.orig ++++ gdb-7.6/gdb/symtab.c +@@ -5122,7 +5122,7 @@ When enabled, debugging messages are pri + #define GDB_COMMON + #include "../../defs.h" + +-static void get_member_data(struct gnu_request *, struct type *); ++static void get_member_data(struct gnu_request *, struct type *, long, int); + static void dump_enum(struct type *, struct gnu_request *); + static void eval_enum(struct type *, struct gnu_request *); + static void gdb_get_line_number(struct gnu_request *); +@@ -5327,7 +5327,7 @@ gdb_get_datatype(struct gnu_request *req + req->typecode = TYPE_CODE(sym->type); + req->length = TYPE_LENGTH(sym->type); + if (req->member) +- get_member_data(req, sym->type); ++ get_member_data(req, sym->type, 0, 1); + + if (TYPE_CODE(sym->type) == TYPE_CODE_ENUM) { + if (req->flags & GNU_PRINT_ENUMERATORS) +@@ -5397,7 +5397,7 @@ gdb_get_datatype(struct gnu_request *req + } + + if (req->member) +- get_member_data(req, type); ++ get_member_data(req, type, 0, 1); + + break; + +@@ -5480,7 +5480,7 @@ eval_enum(struct type *type, struct gnu_ + * member field, and when found, return its relevant data. + */ + static void +-get_member_data(struct gnu_request *req, struct type *type) ++get_member_data(struct gnu_request *req, struct type *type, long offset, int is_first) + { + register short i; + struct field *nextfield; +@@ -5492,7 +5492,7 @@ get_member_data(struct gnu_request *req, + nfields = TYPE_MAIN_TYPE(type)->nfields; + nextfield = TYPE_MAIN_TYPE(type)->flds_bnds.fields; + +- if (nfields == 0) { ++ if (nfields == 0 && is_first /* The first call */) { + struct type *newtype; + newtype = lookup_transparent_type(req->name); + if (newtype) { +@@ -5505,13 +5505,18 @@ get_member_data(struct gnu_request *req, + + for (i = 0; i < nfields; i++) { + if (STREQ(req->member, nextfield->name)) { +- req->member_offset = nextfield->loc.bitpos; ++ req->member_offset = offset + nextfield->loc.bitpos; + req->member_length = TYPE_LENGTH(nextfield->type); + req->member_typecode = TYPE_CODE(nextfield->type); + if ((req->member_typecode == TYPE_CODE_TYPEDEF) && + (typedef_type = check_typedef(nextfield->type))) + req->member_length = TYPE_LENGTH(typedef_type); + return; ++ } else if (*nextfield->name == 0) { /* Anonymous struct/union */ ++ get_member_data(req, nextfield->type, ++ offset + nextfield->loc.bitpos, 0); ++ if (req->member_offset != -1) ++ return; + } + nextfield++; + } +@@ -5706,7 +5711,7 @@ gdb_get_symbol_type(struct gnu_request * + } + + if (req->member) +- get_member_data(req, type); ++ get_member_data(req, type, 0, 1); + + do_cleanups (old_chain); + } diff -Nru crash-7.1.5/help.c crash-7.1.7/help.c --- crash-7.1.5/help.c 2016-04-27 20:46:50.000000000 +0200 +++ crash-7.1.7/help.c 2016-11-30 19:56:29.000000000 +0100 @@ -76,7 +76,10 @@ " with each dumpfile containing a contiguous block of RAM, where the ADDRESS", " value is the physical start address of the block expressed in hexadecimal.", " The physical address value(s) will be used to create a temporary ELF header", - " in /var/tmp, which will only exist during the crash session.", + " in /var/tmp, which will only exist during the crash session. If a raw RAM", + " dumpile represents a live memory source, such as that specified by the QEMU", + " mem-path argument of a memory-backend-file object, then \"live:\" must be", + " prepended to the MEMORY-IMAGE name.", "", " mapfile", " If the NAMELIST file is not the same kernel that is running", @@ -172,6 +175,7 @@ " phys_base=", " ARM64:", " phys_offset=", + " kimage_voffset=", "", " -x ", " Automatically load extension modules from a particular directory.", @@ -1745,7 +1749,7 @@ char *help_bt[] = { "bt", "backtrace", -"[-a|-c cpu(s)|-g|-r|-t|-T|-l|-e|-E|-f|-F|-o|-O] [-R ref] [-s [-x|d]]" +"[-a|-c cpu(s)|-g|-r|-t|-T|-l|-e|-E|-f|-F|-o|-O|-v] [-R ref] [-s [-x|d]]" "\n [-I ip] [-S sp] [pid | task]", " Display a kernel stack backtrace. If no arguments are given, the stack", " trace of the current context will be displayed.\n", @@ -1784,11 +1788,18 @@ " is entered twice, and the stack data references a slab cache object,", " both the address and the name of the slab cache will be displayed", " in brackets.", -" -o x86: use old backtrace method, permissible only on kernels that were", +" -v check the kernel stack of all tasks for evidence of stack overflows.", +" It does so by verifying the thread_info.task pointer, ensuring that", +" the thread_info.cpu is a valid cpu number, and checking the end of ", +" the stack for the STACK_END_MAGIC value.", +" -o arm64: use optional backtrace method.", +" x86: use old backtrace method, permissible only on kernels that were", " compiled without the -fomit-frame_pointer.", " x86_64: use old backtrace method, which dumps potentially stale", " kernel text return addresses found on the stack.", -" -O x86: use old backtrace method by default, permissible only on kernels", +" -O arm64: use optional backtrace method by default; subsequent usage", +" of this option toggles the backtrace method.", +" x86: use old backtrace method by default, permissible only on kernels", " that were compiled without the -fomit-frame_pointer; subsequent usage", " of this option toggles the backtrace method.", " x86_64: use old backtrace method by default; subsequent usage of this", @@ -2068,6 +2079,12 @@ " ffff810072b47f38: 00002b141825d000 sys_write+69 ", " #5 [ffff810072b47f40] sys_write at ffffffff80078f75", " ...", +"", +" Check the kernel stack of all tasks for evidence of a stack overflow:\n", +" %s> bt -v", +" PID: 5823 TASK: ffff88102aae0040 CPU: 1 COMMAND: \"flush-253:0\"", +" possible stack overflow: thread_info.task: 102efb5adc0 != ffff88102aae0040", +" possible stack overflow: 40ffffffff != STACK_END_MAGIC", NULL }; @@ -2684,7 +2701,9 @@ " ASYNC: I/O requests that are asynchronous", " READ: I/O requests that are reads (older kernels)", " WRITE: I/O requests that are writes (older kernels)", -" DRV: I/O requests that are in-flight in the device driver", +" DRV: I/O requests that are in-flight in the device driver.", +" If the device driver uses blk-mq interface, this field", +" shows N/A(MQ).", "\nEXAMPLES", " Display character and block device data:\n", " %s> dev", @@ -5155,7 +5174,7 @@ char *help__list[] = { "list", "linked list", -"[[-o] offset][-e end][-s struct[.member[,member] [-l offset]] -[xd]] " +"[[-o] offset][-e end][-[s|S] struct[.member[,member] [-l offset]] -[x|d]]" "\n [-r|-h|-H] start", " ", " This command dumps the contents of a linked list. The entries in a linked", @@ -5237,6 +5256,9 @@ " \"struct.member.member\" or \"struct.member[index]\"; embedded", " member specifications may extend beyond one level deep by ", " expressing the argument as \"struct.member.member.member...\".", +" -S struct Similar to -s, but instead of parsing gdb output, member values", +" are read directly from memory, so the command works much faster", +" for 1-, 2-, 4-, and 8-byte members.", " -l offset Only used in conjunction with -s, if the start address argument", " is a pointer to an embedded list head (or any other similar list", " linkage structure whose first member points to the next linkage", @@ -5553,7 +5575,7 @@ char *help_tree[] = { "tree", "display radix tree or red-black tree", -"-t [radix|rbtree] [-r offset] [-s struct[.member[,member]] -[x|d]]\n [-o offset] [-p] [-N] start", +"-t [radix|rbtree] [-r offset] [-[s|S] struct[.member[,member]] -[x|d]]\n [-o offset] [-p] [-N] start", " This command dumps the contents of a radix tree or a red-black tree.", " The arguments are as follows:\n", " -t type The type of tree to dump; the type string can be either ", @@ -5583,6 +5605,9 @@ " or \"struct.member[index]\"; embedded member specifications may", " extend beyond one level deep by expressing the struct argument as", " \"struct.member.member.member...\".", +" -S struct Similar to -s, but instead of parsing gdb output, member values", +" are read directly from memory, so the command works much faster", +" for 1-, 2-, 4-, and 8-byte members.", " -x Override default output format with hexadecimal format.", " -d Override default output format with decimal format.", " -p Display the node's position information, showing the relationship", diff -Nru crash-7.1.5/kernel.c crash-7.1.7/kernel.c --- crash-7.1.5/kernel.c 2016-04-27 20:46:50.000000000 +0200 +++ crash-7.1.7/kernel.c 2016-11-30 19:56:29.000000000 +0100 @@ -47,10 +47,13 @@ static void dump_timer_data_tvec_bases_v1(void); static void dump_timer_data_tvec_bases_v2(void); static void dump_timer_data_tvec_bases_v3(void); +static void dump_timer_data_timer_bases(void); struct tv_range; static void init_tv_ranges(struct tv_range *, int, int, int); static int do_timer_list(ulong,int, ulong *, void *,ulong *,struct tv_range *); -static int do_timer_list_v3(ulong,int, ulong *, void *,ulong *); +static int do_timer_list_v3(ulong, int, ulong *, void *,ulong *); +struct timer_bases_data; +static int do_timer_list_v4(struct timer_bases_data *); static int compare_timer_data(const void *, const void *); static void panic_this_kernel(void); static void dump_waitq(ulong, char *); @@ -568,7 +571,11 @@ } } - if (per_cpu_symbol_search("per_cpu__tvec_bases")) { + if (per_cpu_symbol_search("timer_bases")) { + kt->flags2 |= TIMER_BASES; + MEMBER_OFFSET_INIT(timer_base_vectors, "timer_base", "vectors"); + STRUCT_SIZE_INIT(timer_base, "timer_base"); + } else if (per_cpu_symbol_search("per_cpu__tvec_bases")) { if (MEMBER_EXISTS("tvec_base", "migration_enabled")) kt->flags2 |= TVEC_BASES_V3; else @@ -1817,7 +1824,7 @@ FREEBUF(req); return; } - } else if (hexadecimal(args[optind], 0)) { + } else if (hexadecimal(args[optind], 0) && !symbol_exists(args[optind])) { req->buf = args[optind]; req->addr = htol(args[optind], FAULT_ON_ERROR, NULL); sp = value_search(req->addr, &offset); @@ -2332,10 +2339,10 @@ bt = &bt_info; BZERO(bt, sizeof(struct bt_info)); - if (kt->flags & USE_OLD_BT) - bt->flags |= BT_OLD_BACK_TRACE; + if (kt->flags & USE_OPT_BT) + bt->flags |= BT_OPT_BACK_TRACE; - while ((c = getopt(argcnt, args, "D:fFI:S:c:aAloreEgstTdxR:O")) != EOF) { + while ((c = getopt(argcnt, args, "D:fFI:S:c:aAloreEgstTdxR:Ov")) != EOF) { switch (c) { case 'f': @@ -2350,16 +2357,17 @@ break; case 'o': - if (XEN_HYPER_MODE()) + if (!(machine_type("X86") || machine_type("X86_64") || machine_type("ARM64")) || + XEN_HYPER_MODE()) option_not_supported(c); - bt->flags |= BT_OLD_BACK_TRACE; + bt->flags |= BT_OPT_BACK_TRACE; break; case 'O': - if (!(machine_type("X86") || machine_type("X86_64")) || + if (!(machine_type("X86") || machine_type("X86_64") || machine_type("ARM64")) || XEN_HYPER_MODE()) option_not_supported(c); - else if (kt->flags & USE_OLD_BT) { + else if (kt->flags & USE_OPT_BT) { /* * Make this setting idempotent across the use of * $HOME/.crashrc, ./.crashrc, and "-i input" files. @@ -2367,14 +2375,17 @@ * leave it alone. */ if (pc->flags & INIT_IFILE) { - error(INFO, "use old bt method by default (already set)\n"); + error(INFO, "use %s bt method by default (already set)\n", + machine_type("ARM64") ? "optional" : "old"); return; } - kt->flags &= ~USE_OLD_BT; - error(INFO, "use new bt method by default\n"); + kt->flags &= ~USE_OPT_BT; + error(INFO, "use %s bt method by default\n", + machine_type("ARM64") ? "original" : "new"); } else { - kt->flags |= USE_OLD_BT; - error(INFO, "use old bt method by default\n"); + kt->flags |= USE_OPT_BT; + error(INFO, "use %s bt method by default\n", + machine_type("ARM64") ? "optional" : "old"); } return; @@ -2512,6 +2523,12 @@ bt->flags |= BT_TEXT_SYMBOLS; break; + case 'v': + if (XEN_HYPER_MODE()) + option_not_supported(c); + check_stack_overflow(); + return; + default: argerrs++; if (optopt == 'D') { @@ -2800,8 +2817,7 @@ return; } - if (LIVE() && !(bt->flags & BT_EFRAME_SEARCH) && - ((bt->task == tt->this_task) || is_task_active(bt->task))) { + if (LIVE() && !(bt->flags & BT_EFRAME_SEARCH) && is_task_active(bt->task)) { if (BT_REFERENCE_CHECK(bt) || bt->flags & (BT_TEXT_SYMBOLS_PRINT|BT_TEXT_SYMBOLS_NOPRINT)) @@ -2901,6 +2917,10 @@ } if (ACTIVE() && !INSTACK(esp, bt)) { + if (!LOCAL_ACTIVE()) { + error(INFO, "task no longer exists\n"); + return; + } sprintf(buf, "/proc/%ld", bt->tc->pid); if (!file_exists(buf, NULL)) error(INFO, "task no longer exists\n"); @@ -5600,8 +5620,8 @@ fprintf(fp, "%sKMOD_V2", others++ ? "|" : ""); if (kt->flags & KALLSYMS_V2) fprintf(fp, "%sKALLSYMS_V2", others++ ? "|" : ""); - if (kt->flags & USE_OLD_BT) - fprintf(fp, "%sUSE_OLD_BT", others++ ? "|" : ""); + if (kt->flags & USE_OPT_BT) + fprintf(fp, "%sUSE_OPT_BT", others++ ? "|" : ""); if (kt->flags & ARCH_XEN) fprintf(fp, "%sARCH_XEN", others++ ? "|" : ""); if (kt->flags & ARCH_PVOPS_XEN) @@ -5645,6 +5665,8 @@ fprintf(fp, "%sKASLR_CHECK", others++ ? "|" : ""); if (kt->flags2 & TVEC_BASES_V3) fprintf(fp, "%sTVEC_BASES_V3", others++ ? "|" : ""); + if (kt->flags2 & TIMER_BASES) + fprintf(fp, "%sTIMER_BASES", others++ ? "|" : ""); fprintf(fp, ")\n"); fprintf(fp, " stext: %lx\n", kt->stext); @@ -7548,7 +7570,10 @@ int flen, tdx, old_timers_exist; struct tv_range tv[TVN]; - if (kt->flags2 & TVEC_BASES_V3) { + if (kt->flags2 & TIMER_BASES) { + dump_timer_data_timer_bases(); + return; + } else if (kt->flags2 & TVEC_BASES_V3) { dump_timer_data_tvec_bases_v3(); return; } else if (kt->flags & TVEC_BASES_V2) { @@ -7854,6 +7879,7 @@ fprintf(fp, "TVEC_BASES[%d]: [OFFLINE]\n", cpu); if (++cpu < kt->cpus) goto next_cpu; + return; } @@ -7995,6 +8021,7 @@ fprintf(fp, "TVEC_BASES[%d]: [OFFLINE]\n", cpu); if (++cpu < kt->cpus) goto next_cpu; + return; } count = 0; @@ -8435,6 +8462,217 @@ return(td ? tdx : count); } +#define TIMERS_CHUNK (100) + +struct timer_bases_data { + int total, cnt, num_vectors; + ulong *vectors; + ulong timer_base; + struct timer_data *timers; +}; + +static int +do_timer_list_v4(struct timer_bases_data *data) +{ + int i, t, timer_cnt, found; + struct list_data list_data, *ld; + ulong *timer_list; + ulong expires, function; + long oldsize; + char *timer_list_buf; + + timer_list_buf = GETBUF(SIZE(timer_list)); + ld = &list_data; + + for (i = found = 0; i < data->num_vectors; i++) { + if (data->vectors[i] == 0) + continue; + + if (CRASHDEBUG(1)) + fprintf(fp, "%lx vectors[%d]: %lx\n", + data->timer_base + OFFSET(timer_base_vectors) + (i * sizeof(void *)), + i, data->vectors[i]); + + BZERO(ld, sizeof(struct list_data)); + ld->start = data->vectors[i]; + ld->list_head_offset = OFFSET(timer_list_entry); + ld->end = 0; + ld->flags = RETURN_ON_LIST_ERROR; + + hq_open(); + if ((timer_cnt = do_list(ld)) == -1) { + /* Ignore chains with errors */ + if (CRASHDEBUG(1)) + error(INFO, + "ignoring faulty timer_list in timer_base.vector[%d] list\n", + i); + hq_close(); + continue; + } + if (!timer_cnt) { + hq_close(); + continue; + } + + timer_list = (ulong *)GETBUF(timer_cnt * sizeof(ulong)); + timer_cnt = retrieve_list(timer_list, timer_cnt); + hq_close(); + + for (t = 0; t < timer_cnt; t++) { + if (CRASHDEBUG(1)) + fprintf(fp, " %lx\n", timer_list[t]); + + if (!readmem(timer_list[t], KVADDR, timer_list_buf, + SIZE(timer_list), "timer_list buffer", QUIET|RETURN_ON_ERROR)) + continue; + + expires = ULONG(timer_list_buf + OFFSET(timer_list_expires)); + function = ULONG(timer_list_buf + OFFSET(timer_list_function)); + + data->timers[data->cnt].address = timer_list[t]; + data->timers[data->cnt].expires = expires; + data->timers[data->cnt].function = function; + data->cnt++; + + if (data->cnt == data->total) { + oldsize = data->total * sizeof(struct timer_data); + RESIZEBUF(data->timers, oldsize, oldsize * 2); + data->total *= 2; + } + + found++; + } + + FREEBUF(timer_list); + + } + + FREEBUF(timer_list_buf); + + return found; +} + +/* + * Linux 4.8 timers use new timer_bases[][] + */ +static void +dump_timer_data_timer_bases(void) +{ + int i, cpu, flen, base, nr_bases, found, display; + struct syment *sp; + ulong timer_base, jiffies, function; + struct timer_bases_data data; + char buf1[BUFSIZE]; + char buf2[BUFSIZE]; + + if (!(data.num_vectors = get_array_length("timer_base.vectors", NULL, 0))) + error(FATAL, "cannot determine timer_base.vectors[] array size\n"); + data.vectors = (ulong *)GETBUF(data.num_vectors * sizeof(void *)); + data.timers = (struct timer_data *)GETBUF(sizeof(struct timer_data) * TIMERS_CHUNK); + data.total = TIMERS_CHUNK; + data.cnt = 0; + + nr_bases = kernel_symbol_exists("sysctl_timer_migration") ? 2 : 1; + cpu = 0; + + get_symbol_data("jiffies", sizeof(ulong), &jiffies); + sprintf(buf1, "%ld", jiffies); + flen = MAX(strlen(buf1), strlen("JIFFIES")); + fprintf(fp, "%s\n", mkstring(buf1, flen, LJUST, "JIFFIES")); + fprintf(fp, "%s\n\n", mkstring(buf1, flen, + RJUST|LONG_DEC,MKSTR(jiffies))); + +next_cpu: + /* + * hide data of offline cpu and goto next cpu + */ + if (hide_offline_cpu(cpu)) { + fprintf(fp, "TIMER_BASES[%d]: [OFFLINE]\n", cpu); + if (++cpu < kt->cpus) + goto next_cpu; + goto done; + } + + base = 0; + + sp = per_cpu_symbol_search("per_cpu__timer_bases"); + if ((kt->flags & SMP) && (kt->flags & PER_CPU_OFF)) + timer_base = sp->value + kt->__per_cpu_offset[cpu]; + else + timer_base = sp->value; + + if (cpu) + fprintf(fp, "\n"); +next_base: + + fprintf(fp, "TIMER_BASES[%d][%s]: %lx\n", cpu, + base == 0 ? "BASE_STD" : "BASE_DEF", timer_base); + + readmem(timer_base + OFFSET(timer_base_vectors), KVADDR, data.vectors, + data.num_vectors * sizeof(void *), "timer_base.vectors[]", FAULT_ON_ERROR); + data.cnt = 0; + data.timer_base = timer_base; + + found = do_timer_list_v4(&data); + + qsort(data.timers, found, sizeof(struct timer_data), compare_timer_data); + + fprintf(fp, " %s TIMER_LIST FUNCTION\n", + mkstring(buf1, flen, LJUST, "EXPIRES")); + + for (i = 0; i < found; i++) { + display = FALSE; + + if (is_kernel_text(data.timers[i].function)) { + display = TRUE; + function = data.timers[i].function; + } else { + if (readmem(data.timers[i].function, KVADDR, &function, + sizeof(ulong), "timer function", + RETURN_ON_ERROR|QUIET) && is_kernel_text(function)) + display = TRUE; + else { + if (LIVE()) { + if (CRASHDEBUG(1)) + fprintf(fp, "(invalid/stale entry at %lx)\n", + data.timers[i].address); + display = FALSE; + } else { + function = data.timers[i].function; + display = TRUE; + } + } + } + + if (display) { + fprintf(fp, " %s", + mkstring(buf1, flen, RJUST|LONG_DEC, MKSTR(data.timers[i].expires))); + mkstring(buf1, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR(data.timers[i].address)); + fprintf(fp, " %s ", mkstring(buf2, 16, CENTER, buf1)); + fprintf(fp, "%s <%s>\n", + mkstring(buf1, VADDR_PRLEN, RJUST|LONG_HEX, + MKSTR(data.timers[i].function)), + value_to_symstr(function, buf2, 0)); + } + } + + if (!found) + fprintf(fp, " (none)\n"); + + if ((nr_bases == 2) && (base == 0)) { + base++; + timer_base += SIZE(timer_base); + goto next_base; + } + + if (++cpu < kt->cpus) + goto next_cpu; +done: + FREEBUF(data.vectors); + FREEBUF(data.timers); +} + + /* * Panic a live system by exploiting this code in do_exit(): * @@ -8449,7 +8687,7 @@ { pid_t zero_pid = 0; - if (DUMPFILE()) + if (!LOCAL_ACTIVE()) error(FATAL, "cannot panic a dumpfile!\n"); if (!(pc->flags & MFD_RDWR) || (pc->flags & MEMMOD)) diff -Nru crash-7.1.5/main.c crash-7.1.7/main.c --- crash-7.1.5/main.c 2016-04-27 20:46:50.000000000 +0200 +++ crash-7.1.7/main.c 2016-11-30 19:56:29.000000000 +0100 @@ -1,8 +1,8 @@ /* main.c - core analysis suite * * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. - * Copyright (C) 2002-2015 David Anderson - * Copyright (C) 2002-2015 Red Hat, Inc. All rights reserved. + * Copyright (C) 2002-2016 David Anderson + * Copyright (C) 2002-2016 Red Hat, Inc. All rights reserved. * * 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 @@ -227,9 +227,10 @@ optarg); } } else if (STREQ(long_options[option_index].name, "kaslr")) { - if (!machine_type("X86_64")) - error(INFO, "--kaslr only valid " - "with X86_64 machine type.\n"); + if (!machine_type("X86_64") && + !machine_type("ARM64")) + error(INFO, "--kaslr not valid " + "with this machine type.\n"); else if (STREQ(optarg, "auto")) kt->flags2 |= (RELOC_AUTO|KASLR); else { @@ -428,6 +429,15 @@ "too many dumpfile arguments\n"); program_usage(SHORT_FORM); } + + if (ACTIVE()) { + pc->flags |= LIVE_RAMDUMP; + pc->readmem = read_ramdump; + pc->writemem = NULL; + optind++; + continue; + } + pc->dumpfile = ramdump_to_elf(); if (is_kdump(pc->dumpfile, KDUMP_LOCAL)) { pc->flags |= KDUMP; @@ -742,6 +752,7 @@ if (!(pc->flags & GDB_INIT)) { gdb_session_init(); + machdep_init(POST_RELOC); show_untrusted_files(); kdump_backup_region_init(); if (XEN_HYPER_MODE()) { @@ -1294,7 +1305,7 @@ if (pc->flags & REM_LIVE_SYSTEM) sprintf(&buf[strlen(buf)], "%sREM_LIVE_SYSTEM", others++ ? "|" : ""); - if (pc->flags & MEMSRC_LOCAL) + if (pc->flags2 & MEMSRC_LOCAL) sprintf(&buf[strlen(buf)], "%sMEMSRC_LOCAL", others++ ? "|" : ""); if (pc->flags & NAMELIST_LOCAL) diff -Nru crash-7.1.5/Makefile crash-7.1.7/Makefile --- crash-7.1.5/Makefile 2016-04-27 20:46:50.000000000 +0200 +++ crash-7.1.7/Makefile 2016-11-30 19:56:29.000000000 +0100 @@ -554,7 +554,7 @@ tar cvzf ${PROGRAM}.tar.gz ${TAR_FILES} ${GDB_FILES} ${GDB_PATCH_FILES} @echo; ls -l ${PROGRAM}.tar.gz -VERSION=7.1.5 +VERSION=7.1.7 RELEASE=0 release: make_configure diff -Nru crash-7.1.5/memory.c crash-7.1.7/memory.c --- crash-7.1.5/memory.c 2016-04-27 20:46:50.000000000 +0200 +++ crash-7.1.7/memory.c 2016-11-30 19:56:29.000000000 +0100 @@ -434,6 +434,10 @@ MEMBER_OFFSET_INIT(page_count, "page", "_count"); if (INVALID_MEMBER(page_count)) ANON_MEMBER_OFFSET_INIT(page_count, "page", "_count"); + if (INVALID_MEMBER(page_count)) + MEMBER_OFFSET_INIT(page_count, "page", "_refcount"); + if (INVALID_MEMBER(page_count)) + ANON_MEMBER_OFFSET_INIT(page_count, "page", "_refcount"); } MEMBER_OFFSET_INIT(page_flags, "page", "flags"); MEMBER_SIZE_INIT(page_flags, "page", "flags"); @@ -690,9 +694,15 @@ /* * Common to slab/slub */ - ANON_MEMBER_OFFSET_INIT(page_slab, "page", "slab_cache"); - ANON_MEMBER_OFFSET_INIT(page_slab_page, "page", "slab_page"); - ANON_MEMBER_OFFSET_INIT(page_first_page, "page", "first_page"); + MEMBER_OFFSET_INIT(page_slab, "page", "slab_cache"); + if (INVALID_MEMBER(page_slab)) + ANON_MEMBER_OFFSET_INIT(page_slab, "page", "slab_cache"); + MEMBER_OFFSET_INIT(page_slab_page, "page", "slab_page"); + if (INVALID_MEMBER(page_slab_page)) + ANON_MEMBER_OFFSET_INIT(page_slab_page, "page", "slab_page"); + MEMBER_OFFSET_INIT(page_first_page, "page", "first_page"); + if (INVALID_MEMBER(page_first_page)) + ANON_MEMBER_OFFSET_INIT(page_first_page, "page", "first_page"); } else if (MEMBER_EXISTS("kmem_cache", "cpu_slab") && STRUCT_EXISTS("kmem_cache_node")) { @@ -719,16 +729,32 @@ MEMBER_OFFSET_INIT(kmem_cache_cpu_page, "kmem_cache_cpu", "page"); MEMBER_OFFSET_INIT(kmem_cache_cpu_node, "kmem_cache_cpu", "node"); MEMBER_OFFSET_INIT(kmem_cache_cpu_partial, "kmem_cache_cpu", "partial"); - ANON_MEMBER_OFFSET_INIT(page_inuse, "page", "inuse"); - ANON_MEMBER_OFFSET_INIT(page_offset, "page", "offset"); - ANON_MEMBER_OFFSET_INIT(page_slab, "page", "slab"); + MEMBER_OFFSET_INIT(page_inuse, "page", "inuse"); + if (INVALID_MEMBER(page_inuse)) + ANON_MEMBER_OFFSET_INIT(page_inuse, "page", "inuse"); + MEMBER_OFFSET_INIT(page_offset, "page", "offset"); + if (INVALID_MEMBER(page_offset)) + ANON_MEMBER_OFFSET_INIT(page_offset, "page", "offset"); + MEMBER_OFFSET_INIT(page_slab, "page", "slab"); if (INVALID_MEMBER(page_slab)) - ANON_MEMBER_OFFSET_INIT(page_slab, "page", "slab_cache"); - ANON_MEMBER_OFFSET_INIT(page_slab_page, "page", "slab_page"); - ANON_MEMBER_OFFSET_INIT(page_first_page, "page", "first_page"); - ANON_MEMBER_OFFSET_INIT(page_freelist, "page", "freelist"); + ANON_MEMBER_OFFSET_INIT(page_slab, "page", "slab"); + if (INVALID_MEMBER(page_slab)) { + MEMBER_OFFSET_INIT(page_slab, "page", "slab_cache"); + if (INVALID_MEMBER(page_slab)) + ANON_MEMBER_OFFSET_INIT(page_slab, "page", "slab_cache"); + } + MEMBER_OFFSET_INIT(page_slab_page, "page", "slab_page"); + if (INVALID_MEMBER(page_slab_page)) + ANON_MEMBER_OFFSET_INIT(page_slab_page, "page", "slab_page"); + MEMBER_OFFSET_INIT(page_first_page, "page", "first_page"); + if (INVALID_MEMBER(page_first_page)) + ANON_MEMBER_OFFSET_INIT(page_first_page, "page", "first_page"); + MEMBER_OFFSET_INIT(page_freelist, "page", "freelist"); + if (INVALID_MEMBER(page_freelist)) + ANON_MEMBER_OFFSET_INIT(page_freelist, "page", "freelist"); if (INVALID_MEMBER(kmem_cache_objects)) { MEMBER_OFFSET_INIT(kmem_cache_oo, "kmem_cache", "oo"); + /* NOTE: returns offset of containing bitfield */ ANON_MEMBER_OFFSET_INIT(page_objects, "page", "objects"); } if (VALID_MEMBER(kmem_cache_node)) { @@ -4617,7 +4643,7 @@ tm->total_vm = ULONG(tt->mm_struct + OFFSET(mm_struct_total_vm)); tm->pgd_addr = ULONG(tt->mm_struct + OFFSET(mm_struct_pgd)); - if (is_kernel_thread(task)) + if (is_kernel_thread(task) && !tm->rss) return; tm->pct_physmem = ((double)(tm->rss*100)) / @@ -6352,8 +6378,13 @@ break; } + if ((mask == 0UL) && !name) { /* Linux 4.6 and later */ + len--; + break; + } + if (!read_string((ulong)name, namebuf, BUFSIZE-1)) { - error(INFO, "failed to read pageflag_names entry\n", + error(INFO, "failed to read pageflag_names entry (i: %d name: \"%s\" mask: %ld)\n", i, name, mask); goto pageflags_fail; } @@ -8293,12 +8324,12 @@ fprintf(fp, "%13s %7ld %11s ----\n", "TOTAL SWAP", totalswap_pages, pages_to_size(totalswap_pages, buf)); - pct = totalswap_pages ? (totalused_pages * 100) / - totalswap_pages : 100; + pct = totalswap_pages ? (totalused_pages * 100) / + totalswap_pages : 0; fprintf(fp, "%13s %7ld %11s %3ld%% of TOTAL SWAP\n", "SWAP USED", totalused_pages, pages_to_size(totalused_pages, buf), pct); - pct = totalswap_pages ? + pct = totalswap_pages ? ((totalswap_pages - totalused_pages) * 100) / totalswap_pages : 0; fprintf(fp, "%13s %7ld %11s %3ld%% of TOTAL SWAP\n", @@ -16470,6 +16501,7 @@ case CRASHBUILTIN: case KVMDUMP: case PROC_KCORE: + case LIVE_RAMDUMP: psz = (uint)getpagesize(); break; diff -Nru crash-7.1.5/memory_driver/crash.c crash-7.1.7/memory_driver/crash.c --- crash-7.1.5/memory_driver/crash.c 2016-04-27 20:46:50.000000000 +0200 +++ crash-7.1.7/memory_driver/crash.c 2016-11-30 19:56:29.000000000 +0100 @@ -1,8 +1,8 @@ /* * linux/drivers/char/crash.c * - * Copyright (C) 2004, 2011 Dave Anderson - * Copyright (C) 2004, 2011 Red Hat, Inc. + * Copyright (C) 2004, 2011, 2016 Dave Anderson + * Copyright (C) 2004, 2011, 2016 Red Hat, Inc. */ /****************************************************************************** @@ -36,6 +36,58 @@ extern int page_is_ram(unsigned long); +#ifdef CONFIG_S390 +/* + * For swapped prefix pages get bounce buffer using xlate_dev_mem_ptr() + */ +static inline void *map_virtual(u64 offset, struct page **pp) +{ + struct page *page; + unsigned long pfn; + void *vaddr; + + vaddr = xlate_dev_mem_ptr(offset); + pfn = ((unsigned long) vaddr) >> PAGE_SHIFT; + if ((unsigned long) vaddr != offset) + page = pfn_to_page(pfn); + else + page = NULL; + + if (!page_is_ram(pfn)) { + printk(KERN_INFO + "crash memory driver: !page_is_ram(pfn: %lx)\n", pfn); + return NULL; + } + + if (!pfn_valid(pfn)) { + printk(KERN_INFO + "crash memory driver: invalid pfn: %lx )\n", pfn); + return NULL; + } + + *pp = page; + return vaddr; +} + +/* + * Free bounce buffer if necessary + */ +static inline void unmap_virtual(struct page *page) +{ + void *vaddr; + + if (page) { + /* + * Because for bounce buffers vaddr will never be 0 + * unxlate_dev_mem_ptr() will always free the bounce buffer. + */ + vaddr = (void *)(page_to_pfn(page) << PAGE_SHIFT); + unxlate_dev_mem_ptr(0, vaddr); + } +} + +#else /* all architectures except s390x */ + static inline void * map_virtual(u64 offset, struct page **pp) { @@ -82,16 +134,17 @@ { kunmap(page); } +#endif -#define CRASH_VERSION "1.1" +#define CRASH_VERSION "1.3" /* * These are the file operation functions that allow crash utility * access to physical memory. */ -static loff_t +static loff_t crash_llseek(struct file * file, loff_t offset, int orig) { switch (orig) { @@ -107,7 +160,7 @@ } /* - * Determine the page address for an address offset value, + * Determine the page address for an address offset value, * get a virtual address for it, and copy it out. * Accesses must fit within a page. */ @@ -118,16 +171,21 @@ struct page *page; u64 offset; ssize_t read; + char *buffer = file->private_data; offset = *poff; - if (offset >> PAGE_SHIFT != (offset+count-1) >> PAGE_SHIFT) + if (offset >> PAGE_SHIFT != (offset+count-1) >> PAGE_SHIFT) return -EINVAL; vaddr = map_virtual(offset, &page); if (!vaddr) return -EFAULT; - - if (copy_to_user(buf, vaddr, count)) { + /* + * Use bounce buffer to bypass the CONFIG_HARDENED_USERCOPY + * kernel text restriction. + */ + memcpy(buffer, (char *)vaddr, count); + if (copy_to_user(buf, buffer, count)) { unmap_virtual(page); return -EFAULT; } @@ -138,10 +196,66 @@ return read; } +static int +crash_open(struct inode * inode, struct file * filp) +{ + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + + filp->private_data = (void *)__get_free_page(GFP_KERNEL); + if (!filp->private_data) + return -ENOMEM; + + return 0; +} + +static int +crash_release(struct inode *inode, struct file *filp) +{ + free_pages((unsigned long)filp->private_data, 0); + return 0; +} + +/* + * Note: This function is required for Linux 4.6 and later ARM64 kernels. + * For earler kernel versions, remove this CONFIG_ARM64 section. + */ +#ifdef CONFIG_ARM64 + +#define DEV_CRASH_ARCH_DATA _IOR('c', 1, long) + +static long +crash_arch_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + extern u64 kimage_voffset; + + switch (cmd) + { + case DEV_CRASH_ARCH_DATA: + return put_user(kimage_voffset, (unsigned long __user *)arg); + default: + return -EINVAL; + } +} +#endif + +static long +crash_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ +#ifdef DEV_CRASH_ARCH_DATA + return crash_arch_ioctl(file, cmd, arg); +#else + return -EINVAL; +#endif +} + static struct file_operations crash_fops = { .owner = THIS_MODULE, .llseek = crash_llseek, .read = crash_read, + .unlocked_ioctl = crash_ioctl, + .open = crash_open, + .release = crash_release, }; static struct miscdevice crash_dev = { @@ -157,11 +271,11 @@ ret = misc_register(&crash_dev); if (ret) { - printk(KERN_ERR + printk(KERN_ERR "crash memory driver: cannot misc_register (MISC_DYNAMIC_MINOR)\n"); goto out; } - + ret = 0; printk(KERN_INFO "crash memory driver: version %s\n", CRASH_VERSION); out: diff -Nru crash-7.1.5/memory_driver/README crash-7.1.7/memory_driver/README --- crash-7.1.5/memory_driver/README 2016-04-27 20:46:50.000000000 +0200 +++ crash-7.1.7/memory_driver/README 2016-11-30 19:56:29.000000000 +0100 @@ -5,8 +5,8 @@ /proc/kcore /dev/crash -If the live system kernel was configured with CONFIG_STRICT_DEVMEM, -then /dev/mem cannot be used. +If the live system kernel was configured with CONFIG_STRICT_DEVMEM +or CONFIG_HARDENED_USERCOPY, then /dev/mem cannot be used. If the live system kernel was configured without CONFIG_PROC_KCORE, or if /proc/kcore is non-functional, then /proc/kcore cannot be used. diff -Nru crash-7.1.5/mips.c crash-7.1.7/mips.c --- crash-7.1.5/mips.c 2016-04-27 20:46:50.000000000 +0200 +++ crash-7.1.7/mips.c 2016-11-30 19:56:29.000000000 +0100 @@ -47,16 +47,27 @@ #define MIPS_CPU_RIXI 0x00800000llu -#define MIPS32_EF_R0 6 -#define MIPS32_EF_R29 35 -#define MIPS32_EF_R31 37 -#define MIPS32_EF_CPU0_EPC 40 +#define MIPS32_EF_R0 6 +#define MIPS32_EF_R29 35 +#define MIPS32_EF_R31 37 +#define MIPS32_EF_LO 38 +#define MIPS32_EF_HI 39 +#define MIPS32_EF_CP0_EPC 40 +#define MIPS32_EF_CP0_BADVADDR 41 +#define MIPS32_EF_CP0_STATUS 42 +#define MIPS32_EF_CP0_CAUSE 43 static struct machine_specific mips_machine_specific = { 0 }; +/* + * Holds registers during the crash. + */ +static struct mips_regset *panic_task_regs; + static void mips_display_machine_stats(void) { + fprintf(fp, " HZ: %d\n", machdep->hz); fprintf(fp, " PAGE SIZE: %d\n", PAGESIZE()); fprintf(fp, "\n"); @@ -411,7 +422,7 @@ struct mips_unwind_frame *current, struct mips_unwind_frame *previous, int level) { - const char *name = sym->name; + const char *name = sym ? sym->name : "(invalid)"; struct load_module *lm; char *name_plus_offset; char buf[BUFSIZE]; @@ -445,7 +456,7 @@ fprintf(fp, " %s\n", buf); } - if (mips_is_exception_entry(sym)) { + if (sym && mips_is_exception_entry(sym)) { char pt_regs[SIZE(pt_regs)]; GET_STACK_DATA(current->sp, &pt_regs, SIZE(pt_regs)); @@ -531,6 +542,10 @@ { struct mips_unwind_frame current, previous; int level = 0; + int invalid_ok = 1; + + if (bt->flags & BT_REGS_NOT_FOUND) + return; previous.sp = previous.pc = previous.ra = 0; @@ -544,22 +559,24 @@ } while (INSTACK(current.sp, bt)) { - struct syment *symbol; + struct syment *symbol = NULL; 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)) + if (!IS_KVADDR(current.pc) && !invalid_ok) return; symbol = value_search(current.pc, &offset); - if (!symbol) { + if (!symbol && !invalid_ok) { error(FATAL, "PC is unknown symbol (%lx)", current.pc); return; } + invalid_ok = 0; + /* * If we get an address which points to the start of a * function, then it could one of the following: @@ -581,7 +598,7 @@ * * ret_from_fork * * ret_from_kernel_thread */ - if (!current.ra && !offset && !STRNEQ(symbol->name, "ret_from")) { + if (!current.ra && !offset && symbol && !STRNEQ(symbol->name, "ret_from")) { if (CRASHDEBUG(8)) fprintf(fp, "zero offset at %s, try previous symbol\n", symbol->name); @@ -593,7 +610,7 @@ } } - if (mips_is_exception_entry(symbol)) { + if (symbol && mips_is_exception_entry(symbol)) { struct mips_pt_regs_main *mains; struct mips_pt_regs_cp0 *cp0; char pt_regs[SIZE(pt_regs)]; @@ -612,38 +629,63 @@ if (CRASHDEBUG(8)) fprintf(fp, "exception pc %#lx ra %#lx sp %lx\n", previous.pc, previous.ra, previous.sp); - } else { + + /* The PC causing the exception may have been invalid */ + invalid_ok = 1; + } else if (symbol) { mips_analyze_function(symbol->value, offset, ¤t, &previous); + } else { + /* + * The current PC is invalid. Assume that the code + * jumped through a invalid pointer and that the SP has + * not been adjusted. + */ + previous.sp = current.sp; } 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; + if (CRASHDEBUG(8)) + fprintf(fp, "next %d pc %#lx ra %#lx sp %lx\n", + level, current.pc, current.ra, current.sp); + previous.sp = previous.pc = previous.ra = 0; } } -static void +static int mips_dumpfile_stack_frame(struct bt_info *bt, ulong *nip, ulong *ksp) { + const struct machine_specific *ms = machdep->machspec; struct mips_regset *regs; + ulong epc, r29; - regs = bt->machdep; - if (!regs) { - fprintf(fp, "0%lx: Register values not available\n", - bt->task); - return; + if (!ms->crash_task_regs) { + bt->flags |= BT_REGS_NOT_FOUND; + return FALSE; + } + + regs = &ms->crash_task_regs[bt->tc->processor]; + epc = regs->regs[MIPS32_EF_CP0_EPC]; + r29 = regs->regs[MIPS32_EF_R29]; + + if (!epc && !r29) { + bt->flags |= BT_REGS_NOT_FOUND; + return FALSE; } if (nip) - *nip = regs->regs[MIPS32_EF_CPU0_EPC]; + *nip = epc; if (ksp) - *ksp = regs->regs[MIPS32_EF_R29]; + *ksp = r29; + + bt->machdep = regs; + + return TRUE; } static int @@ -697,14 +739,20 @@ static void mips_get_stack_frame(struct bt_info *bt, ulong *pcp, ulong *spp) { + int ret; + *pcp = 0; *spp = 0; + bt->machdep = NULL; if (DUMPFILE() && is_task_active(bt->task)) - mips_dumpfile_stack_frame(bt, pcp, spp); + ret = mips_dumpfile_stack_frame(bt, pcp, spp); else - mips_get_frame(bt, pcp, spp); + ret = mips_get_frame(bt, pcp, spp); + if (!ret) + error(WARNING, "cannot determine starting stack frame for task %lx\n", + bt->task); } static int @@ -746,6 +794,178 @@ return first_vmalloc_address(); } +/* + * Retrieve task registers for the time of the crash. + */ +static int +mips_get_crash_notes(void) +{ + struct machine_specific *ms = machdep->machspec; + ulong crash_notes; + Elf32_Nhdr *note; + ulong offset; + char *buf, *p; + ulong *notes_ptrs; + ulong i; + + if (!symbol_exists("crash_notes")) + return FALSE; + + crash_notes = symbol_value("crash_notes"); + + notes_ptrs = (ulong *)GETBUF(kt->cpus*sizeof(notes_ptrs[0])); + + /* + * Read crash_notes for the first CPU. crash_notes are in standard ELF + * note format. + */ + if (!readmem(crash_notes, KVADDR, ¬es_ptrs[kt->cpus-1], + sizeof(notes_ptrs[kt->cpus-1]), "crash_notes", + RETURN_ON_ERROR)) { + error(WARNING, "cannot read crash_notes\n"); + FREEBUF(notes_ptrs); + return FALSE; + } + + if (symbol_exists("__per_cpu_offset")) { + + /* Add __per_cpu_offset for each cpu to form the pointer to the notes */ + for (i = 0; icpus; i++) + notes_ptrs[i] = notes_ptrs[kt->cpus-1] + kt->__per_cpu_offset[i]; + } + + buf = GETBUF(SIZE(note_buf)); + + if (!(panic_task_regs = calloc((size_t)kt->cpus, sizeof(*panic_task_regs)))) + error(FATAL, "cannot calloc panic_task_regs space\n"); + + for (i=0;icpus;i++) { + + if (!readmem(notes_ptrs[i], KVADDR, buf, SIZE(note_buf), "note_buf_t", + RETURN_ON_ERROR)) { + error(WARNING, "failed to read note_buf_t\n"); + goto fail; + } + + /* + * Do some sanity checks for this note before reading registers from it. + */ + note = (Elf32_Nhdr *)buf; + p = buf + sizeof(Elf32_Nhdr); + + /* + * dumpfiles created with qemu won't have crash_notes, but there will + * be elf notes; dumpfiles created by kdump do not create notes for + * offline cpus. + */ + if (note->n_namesz == 0 && (DISKDUMP_DUMPFILE() || KDUMP_DUMPFILE())) { + if (DISKDUMP_DUMPFILE()) + note = diskdump_get_prstatus_percpu(i); + else if (KDUMP_DUMPFILE()) + note = netdump_get_prstatus_percpu(i); + if (note) { + /* + * SIZE(note_buf) accounts for a "final note", which is a + * trailing empty elf note header. + */ + long notesz = SIZE(note_buf) - sizeof(Elf32_Nhdr); + + if (sizeof(Elf32_Nhdr) + roundup(note->n_namesz, 4) + + note->n_descsz == notesz) + BCOPY((char *)note, buf, notesz); + } else { + error(WARNING, + "cannot find NT_PRSTATUS note for cpu: %d\n", i); + continue; + } + } + + if (note->n_type != NT_PRSTATUS) { + error(WARNING, "invalid note (n_type != NT_PRSTATUS)\n"); + goto fail; + } + if (p[0] != 'C' || p[1] != 'O' || p[2] != 'R' || p[3] != 'E') { + error(WARNING, "invalid note (name != \"CORE\"\n"); + goto fail; + } + + /* + * Find correct location of note data. This contains elf_prstatus + * structure which has registers etc. for the crashed task. + */ + offset = sizeof(Elf32_Nhdr); + offset = roundup(offset + note->n_namesz, 4); + p = buf + offset; /* start of elf_prstatus */ + + BCOPY(p + OFFSET(elf_prstatus_pr_reg), &panic_task_regs[i], + sizeof(panic_task_regs[i])); + } + + /* + * And finally we have the registers for the crashed task. This is + * used later on when dumping backtrace. + */ + ms->crash_task_regs = panic_task_regs; + + FREEBUF(buf); + FREEBUF(notes_ptrs); + return TRUE; + +fail: + FREEBUF(buf); + FREEBUF(notes_ptrs); + free(panic_task_regs); + return FALSE; +} + +static int mips_get_elf_notes(void) +{ + struct machine_specific *ms = machdep->machspec; + int i; + + if (!DISKDUMP_DUMPFILE() && !KDUMP_DUMPFILE()) + return FALSE; + + panic_task_regs = calloc(kt->cpus, sizeof(*panic_task_regs)); + if (!panic_task_regs) + error(FATAL, "cannot calloc panic_task_regs space\n"); + + for (i = 0; i < kt->cpus; i++) { + Elf32_Nhdr *note = NULL; + size_t len; + + if (DISKDUMP_DUMPFILE()) + note = diskdump_get_prstatus_percpu(i); + else if (KDUMP_DUMPFILE()) + note = netdump_get_prstatus_percpu(i); + + if (!note) + error(WARNING, + "cannot find NT_PRSTATUS note for cpu: %d\n", i); + + len = sizeof(Elf32_Nhdr); + len = roundup(len + note->n_namesz, 4); + + BCOPY((char *)note + len + OFFSET(elf_prstatus_pr_reg), + &panic_task_regs[i], sizeof(panic_task_regs[i])); + } + + ms->crash_task_regs = panic_task_regs; + + return TRUE; +} + +static int mips_init_active_task_regs(void) +{ + int retval; + + retval = mips_get_crash_notes(); + if (retval == TRUE) + return retval; + + return mips_get_elf_notes(); +} + static int mips_verify_symbol(const char *name, ulong value, char type) { @@ -776,6 +996,7 @@ 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, " hz: %d\n", machdep->hz); fprintf(fp, " memsize: %lld (0x%llx)\n", machdep->memsize, machdep->memsize); fprintf(fp, " bits: %d\n", machdep->bits); @@ -901,11 +1122,115 @@ machdep->dump_irq = generic_dump_irq; machdep->show_interrupts = generic_show_interrupts; machdep->get_irq_affinity = generic_get_irq_affinity; + machdep->section_size_bits = _SECTION_SIZE_BITS; + machdep->max_physmem_bits = _MAX_PHYSMEM_BITS; ARRAY_LENGTH_INIT(machdep->nr_irqs, irq_desc, "irq_desc", NULL, 0); mips_stackframe_init(); + + if (!machdep->hz) + machdep->hz = 100; + + MEMBER_OFFSET_INIT(elf_prstatus_pr_reg, "elf_prstatus", + "pr_reg"); + + STRUCT_SIZE_INIT(note_buf, "note_buf_t"); break; + case POST_VM: + /* + * crash_notes contains machine specific information about the + * crash. In particular, it contains CPU registers at the time + * of the crash. We need this information to extract correct + * backtraces from the panic task. + */ + if (!ACTIVE() && !mips_init_active_task_regs()) + error(WARNING, + "cannot retrieve registers for active task%s\n\n", + kt->cpus > 1 ? "s" : ""); } } -#endif /* MIPS */ +void +mips_display_regs_from_elf_notes(int cpu, FILE *ofp) +{ + const struct machine_specific *ms = machdep->machspec; + struct mips_regset *regs; + + if (!ms->crash_task_regs) { + error(INFO, "registers not collected for cpu %d\n", cpu); + return; + } + + regs = &ms->crash_task_regs[cpu]; + if (!regs->regs[MIPS32_EF_R29] && !regs->regs[MIPS32_EF_CP0_EPC]) { + error(INFO, "registers not collected for cpu %d\n", cpu); + return; + } + + fprintf(ofp, + " R0: %08lx R1: %08lx R2: %08lx\n" + " R3: %08lx R4: %08lx R5: %08lx\n" + " R6: %08lx R7: %08lx R8: %08lx\n" + " R9: %08lx R10: %08lx R11: %08lx\n" + " R12: %08lx R13: %08lx R14: %08lx\n" + " R15: %08lx R16: %08lx R17: %08lx\n" + " R18: %08lx R19: %08lx R20: %08lx\n" + " R21: %08lx R22: %08lx R23: %08lx\n" + " R24: %08lx R25: %08lx R26: %08lx\n" + " R27: %08lx R28: %08lx R29: %08lx\n" + " R30: %08lx R31: %08lx\n" + " LO: %08lx HI: %08lx\n" + " EPC: %08lx BADVADDR: %08lx\n" + " STATUS: %08lx CAUSE: %08lx\n", + regs->regs[MIPS32_EF_R0], + regs->regs[MIPS32_EF_R0 + 1], + regs->regs[MIPS32_EF_R0 + 2], + regs->regs[MIPS32_EF_R0 + 3], + regs->regs[MIPS32_EF_R0 + 4], + regs->regs[MIPS32_EF_R0 + 5], + regs->regs[MIPS32_EF_R0 + 6], + regs->regs[MIPS32_EF_R0 + 7], + regs->regs[MIPS32_EF_R0 + 8], + regs->regs[MIPS32_EF_R0 + 9], + regs->regs[MIPS32_EF_R0 + 10], + regs->regs[MIPS32_EF_R0 + 11], + regs->regs[MIPS32_EF_R0 + 12], + regs->regs[MIPS32_EF_R0 + 13], + regs->regs[MIPS32_EF_R0 + 14], + regs->regs[MIPS32_EF_R0 + 15], + regs->regs[MIPS32_EF_R0 + 16], + regs->regs[MIPS32_EF_R0 + 17], + regs->regs[MIPS32_EF_R0 + 18], + regs->regs[MIPS32_EF_R0 + 19], + regs->regs[MIPS32_EF_R0 + 20], + regs->regs[MIPS32_EF_R0 + 21], + regs->regs[MIPS32_EF_R0 + 22], + regs->regs[MIPS32_EF_R0 + 23], + regs->regs[MIPS32_EF_R0 + 24], + regs->regs[MIPS32_EF_R0 + 25], + regs->regs[MIPS32_EF_R0 + 26], + regs->regs[MIPS32_EF_R0 + 27], + regs->regs[MIPS32_EF_R0 + 28], + regs->regs[MIPS32_EF_R0 + 29], + regs->regs[MIPS32_EF_R0 + 30], + regs->regs[MIPS32_EF_R0 + 31], + regs->regs[MIPS32_EF_LO], + regs->regs[MIPS32_EF_HI], + regs->regs[MIPS32_EF_CP0_EPC], + regs->regs[MIPS32_EF_CP0_BADVADDR], + regs->regs[MIPS32_EF_CP0_STATUS], + regs->regs[MIPS32_EF_CP0_CAUSE]); +} +#else + +#include "defs.h" + +void +mips_display_regs_from_elf_notes(int cpu, FILE *ofp) +{ + return; +} + +#endif /* !MIPS */ + + diff -Nru crash-7.1.5/net.c crash-7.1.7/net.c --- crash-7.1.5/net.c 2016-04-27 20:46:50.000000000 +0200 +++ crash-7.1.7/net.c 2016-11-30 19:56:29.000000000 +0100 @@ -1,8 +1,8 @@ /* net.c - core analysis suite * * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. - * Copyright (C) 2002-2015 David Anderson - * Copyright (C) 2002-2015 Red Hat, Inc. All rights reserved. + * Copyright (C) 2002-2016 David Anderson + * Copyright (C) 2002-2016 Red Hat, Inc. All rights reserved. * * 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 @@ -241,14 +241,21 @@ ANON_MEMBER_OFFSET_INIT(inet_opt_rcv_saddr, "sock_common", "skc_rcv_saddr"); MEMBER_OFFSET_INIT(inet_opt_dport, "inet_sock", "inet_dport"); - if (INVALID_MEMBER(inet_opt_dport)) - ANON_MEMBER_OFFSET_INIT(inet_opt_dport, "sock_common", + if (INVALID_MEMBER(inet_opt_dport)) { + MEMBER_OFFSET_INIT(inet_opt_dport, "sock_common", "skc_dport"); + if (INVALID_MEMBER(inet_opt_dport)) + ANON_MEMBER_OFFSET_INIT(inet_opt_dport, "sock_common", + "skc_dport"); + } MEMBER_OFFSET_INIT(inet_opt_sport, "inet_sock", "inet_sport"); MEMBER_OFFSET_INIT(inet_opt_num, "inet_sock", "inet_num"); - if (INVALID_MEMBER(inet_opt_num)) - ANON_MEMBER_OFFSET_INIT(inet_opt_num, "sock_common", + if (INVALID_MEMBER(inet_opt_num)) { + MEMBER_OFFSET_INIT(inet_opt_num, "sock_common", "skc_num"); + if (INVALID_MEMBER(inet_opt_num)) + ANON_MEMBER_OFFSET_INIT(inet_opt_num, "sock_common", "skc_num"); + } } } diff -Nru crash-7.1.5/netdump.c crash-7.1.7/netdump.c --- crash-7.1.5/netdump.c 2016-04-27 20:46:50.000000000 +0200 +++ crash-7.1.7/netdump.c 2016-11-30 19:56:29.000000000 +0100 @@ -40,6 +40,7 @@ static void get_netdump_regs_ppc64(struct bt_info *, ulong *, ulong *); static void get_netdump_regs_arm(struct bt_info *, ulong *, ulong *); static void get_netdump_regs_arm64(struct bt_info *, ulong *, ulong *); +static void get_netdump_regs_mips(struct bt_info *, ulong *, ulong *); static void check_dumpfile_size(char *); static int proc_kcore_init_32(FILE *fp); static int proc_kcore_init_64(FILE *fp); @@ -2436,7 +2437,7 @@ break; case EM_MIPS: - return get_netdump_regs_32(bt, eip, esp); + return get_netdump_regs_mips(bt, eip, esp); break; default: @@ -2504,7 +2505,8 @@ } } - if ((cpu - skipped_count) >= nd->num_prstatus_notes) { + if ((cpu - skipped_count) >= nd->num_prstatus_notes && + !machine_type("MIPS")) { error(INFO, "registers not collected for cpu %d\n", cpu); return; } @@ -2690,6 +2692,8 @@ ULONG(user_regs + sizeof(ulong) * 32), ULONG(user_regs + sizeof(ulong) * 33), UINT(user_regs + sizeof(ulong) * 34)); + } else if (machine_type("MIPS")) { + mips_display_regs_from_elf_notes(cpu, ofp); } } @@ -2699,7 +2703,8 @@ int c; if (!(machine_type("X86") || machine_type("X86_64") || - machine_type("ARM64") || machine_type("PPC64"))) + machine_type("ARM64") || machine_type("PPC64") || + machine_type("MIPS"))) error(FATAL, "-r option not supported for this dumpfile\n"); if (NETDUMP_DUMPFILE()) { @@ -3628,6 +3633,12 @@ machdep->get_stack_frame(bt, eip, esp); } +static void +get_netdump_regs_mips(struct bt_info *bt, ulong *eip, ulong *esp) +{ + machdep->get_stack_frame(bt, eip, esp); +} + int is_partial_netdump(void) { @@ -4458,11 +4469,14 @@ } else return; - if (!readmem(symbol_value("kexec_crash_image"), KVADDR, - &kexec_crash_image_p, sizeof(ulong), - "kexec backup region: kexec_crash_image", - QUIET|RETURN_ON_ERROR)) - goto error; + if (symbol_exists("kexec_crash_image")) { + if (!readmem(symbol_value("kexec_crash_image"), KVADDR, + &kexec_crash_image_p, sizeof(ulong), + "kexec backup region: kexec_crash_image", + QUIET|RETURN_ON_ERROR)) + goto error; + } else + kexec_crash_image_p = 0; if (!kexec_crash_image_p) { if (CRASHDEBUG(1)) diff -Nru crash-7.1.5/ppc64.c crash-7.1.7/ppc64.c --- crash-7.1.5/ppc64.c 2016-04-27 20:46:50.000000000 +0200 +++ crash-7.1.7/ppc64.c 2016-11-30 19:56:29.000000000 +0100 @@ -15,7 +15,9 @@ * GNU General Public License for more details. */ #ifdef PPC64 + #include "defs.h" +#include static int ppc64_kvtop(struct task_context *, ulong, physaddr_t *, int); static int ppc64_uvtop(struct task_context *, ulong, physaddr_t *, int); @@ -59,6 +61,9 @@ static int is_hugepage(ulong pte); static int is_hugepd(ulong pte); static ulong hugepage_dir(ulong pte); +static ulong pgd_page_vaddr_l4(ulong pgd); +static ulong pud_page_vaddr_l4(ulong pud); +static ulong pmd_page_vaddr_l4(ulong pmd); static inline uint get_ptetype(ulong pte) { @@ -66,39 +71,117 @@ if (is_hugepage(pte)) pte_type = 1; - else if (is_hugepd(pte)) + else if (!(machdep->flags & RADIX_MMU) && + (PAGESIZE() != PPC64_64K_PAGE_SIZE) && is_hugepd(pte)) pte_type = 2; return pte_type; } -static int is_hugepage(ulong pte) +static inline int is_hugepage(ulong pte) { - /* - * leaf pte for huge page, bottom two bits != 00 - */ - return ((pte & HUGE_PTE_MASK) != 0x0); + if ((machdep->flags & BOOK3E) || + (THIS_KERNEL_VERSION < LINUX(3,10,0))) { + /* + * hugepage support via hugepd for book3e and + * also kernel v3.9 & below. + */ + return 0; + + } else if (THIS_KERNEL_VERSION >= LINUX(4,5,0)) { + /* + * leaf pte for huge page, if _PAGE_PTE is set. + */ + return !!(pte & _PAGE_PTE); + + } else { /* BOOK3S, kernel v3.10 - v4.4 */ + + /* + * leaf pte for huge page, bottom two bits != 00 + */ + return ((pte & HUGE_PTE_MASK) != 0x0); + } } static inline int is_hugepd(ulong pte) { - if (THIS_KERNEL_VERSION >= LINUX(3,10,0)) { + if ((machdep->flags & BOOK3E) || + (THIS_KERNEL_VERSION < LINUX(3,10,0))) + return ((pte & PD_HUGE) == 0x0); + + else if (THIS_KERNEL_VERSION >= LINUX(4,5,0)) { + /* + * hugepd pointer, if _PAGE_PTE is not set and + * hugepd shift mask is set. + */ + return (!(pte & _PAGE_PTE) && + ((pte & HUGEPD_SHIFT_MASK) != 0)); + + } else { /* BOOK3S, kernel v3.10 - v4.4 */ + /* * hugepd pointer, bottom two bits == 00 and next 4 bits * indicate size of table - */ + */ return (((pte & HUGE_PTE_MASK) == 0x0) && ((pte & HUGEPD_SHIFT_MASK) != 0)); - } else - return ((pte & PD_HUGE) == 0x0); + } } static inline ulong hugepage_dir(ulong pte) { - if (THIS_KERNEL_VERSION >= LINUX(3,10,0)) - return (ulong)(pte & ~HUGEPD_SHIFT_MASK); - else + if ((machdep->flags & BOOK3E) || + (THIS_KERNEL_VERSION < LINUX(3,10,0))) return (ulong)((pte & ~HUGEPD_SHIFT_MASK) | PD_HUGE); + else if (machdep->flags & PHYS_ENTRY_L4) + return PTOV(pte & ~HUGEPD_ADDR_MASK); + else /* BOOK3S, kernel v3.10 - v4.4 */ + return (ulong)(pte & ~HUGEPD_SHIFT_MASK); +} + +static inline ulong pgd_page_vaddr_l4(ulong pgd) +{ + ulong pgd_val; + + pgd_val = (pgd & ~machdep->machspec->pgd_masked_bits); + if (machdep->flags & PHYS_ENTRY_L4) { + /* + * physical address is stored starting from kernel v4.6 + */ + pgd_val = PTOV(pgd_val); + } + + return pgd_val; +} + +static inline ulong pud_page_vaddr_l4(ulong pud) +{ + ulong pud_val; + + pud_val = (pud & ~machdep->machspec->pud_masked_bits); + if (machdep->flags & PHYS_ENTRY_L4) { + /* + * physical address is stored starting from kernel v4.6 + */ + pud_val = PTOV(pud_val); + } + + return pud_val; +} + +static inline ulong pmd_page_vaddr_l4(ulong pmd) +{ + ulong pmd_val; + + pmd_val = (pmd & ~machdep->machspec->pmd_masked_bits); + if (machdep->flags & PHYS_ENTRY_L4) { + /* + * physical address is stored starting from kernel v4.6 + */ + pmd_val = PTOV(pmd_val); + } + + return pmd_val; } static int book3e_is_kvaddr(ulong addr) @@ -122,7 +205,8 @@ .hwintrstack = { 0 }, .hwstackbuf = 0, .hwstacksize = 0, - .pte_shift = PTE_SHIFT, + .pte_rpn_shift = PTE_RPN_SHIFT_DEFAULT, + ._page_pte = 0x0UL, ._page_present = 0x1UL, ._page_user = 0x2UL, ._page_rw = 0x4UL, @@ -140,7 +224,8 @@ .hwintrstack = { 0 }, .hwstackbuf = 0, .hwstacksize = 0, - .pte_shift = PTE_SHIFT_L4_BOOK3E_64K, + .pte_rpn_shift = PTE_RPN_SHIFT_L4_BOOK3E_64K, + ._page_pte = 0x0UL, ._page_present = 0x1UL, ._page_user = 0xCUL, ._page_rw = 0x30UL, @@ -179,9 +264,9 @@ return; machdep->stacksize = PPC64_STACK_SIZE; machdep->last_pgd_read = 0; - machdep->last_pmd_read = 0; - machdep->last_ptbl_read = 0; - machdep->machspec->last_level4_read = 0; + machdep->last_pud_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; machdep->flags |= MACHDEP_BT_TEXT; @@ -214,6 +299,7 @@ machdep->kvbase = BOOK3E_VMBASE; } else machdep->kvbase = symbol_value("_stext"); + if (symbol_exists("__hash_page_64K")) machdep->pagesize = PPC64_64K_PAGE_SIZE; else @@ -223,12 +309,12 @@ machdep->pagemask = ~((ulonglong)machdep->pageoffset); if ((machdep->pgd = (char *)malloc(PAGESIZE())) == NULL) error(FATAL, "cannot malloc pgd space."); + if ((machdep->pud = (char *)malloc(PAGESIZE())) == NULL) + error(FATAL, "cannot malloc pud space."); if ((machdep->pmd = (char *)malloc(PAGESIZE())) == NULL) error(FATAL, "cannot malloc pmd space."); if ((machdep->ptbl = (char *)malloc(PAGESIZE())) == NULL) error(FATAL, "cannot malloc ptbl space."); - if ((machdep->machspec->level4 = (char *)malloc(PAGESIZE())) == NULL) - error(FATAL, "cannot malloc level4 space."); machdep->identity_map_base = symbol_value("_stext"); machdep->is_kvaddr = machdep->machspec->is_kvaddr; @@ -252,16 +338,87 @@ machdep->value_to_symbol = generic_machdep_value_to_symbol; machdep->get_kvaddr_ranges = ppc64_get_kvaddr_ranges; machdep->init_kernel_pgd = NULL; + if (symbol_exists("vmemmap_populate")) { + if (symbol_exists("vmemmap")) { + get_symbol_data("vmemmap", sizeof(void *), + &machdep->machspec->vmemmap_base); + } else + machdep->machspec->vmemmap_base = + VMEMMAP_REGION_ID << REGION_SHIFT; + machdep->flags |= VMEMMAP; - machdep->machspec->vmemmap_base = - VMEMMAP_REGION_ID << REGION_SHIFT; } + machdep->get_irq_affinity = generic_get_irq_affinity; machdep->show_interrupts = generic_show_interrupts; break; case POST_GDB: + if (!(machdep->flags & BOOK3E)) { + struct machine_specific *m = machdep->machspec; + + /* + * On Power ISA 3.0 based server processors, a kernel can + * run with radix MMU or standard MMU. Set the flag, + * if it is radix MMU. + */ + if (symbol_exists("cur_cpu_spec") && + MEMBER_EXISTS("cpu_spec", "mmu_features")) { + ulong cur_cpu_spec; + uint mmu_features, offset; + + get_symbol_data("cur_cpu_spec", sizeof(void *), &cur_cpu_spec); + offset = MEMBER_OFFSET("cpu_spec", "mmu_features"); + readmem(cur_cpu_spec + offset, KVADDR, &mmu_features, + sizeof(uint), "cpu mmu features", FAULT_ON_ERROR); + machdep->flags |= (mmu_features & RADIX_MMU); + } + + /* + * Starting with v3.14 we no longer use _PAGE_COHERENT + * bit as it is always set on hash64 and on platforms + * that cannot always set it, _PAGE_NO_CACHE and + * _PAGE_WRITETHRU can be used to infer it. + */ + if (THIS_KERNEL_VERSION >= LINUX(3,14,0)) + m->_page_coherent = 0x0UL; + + /* + * In kernel v4.5, _PAGE_PTE bit is introduced to + * distinguish PTEs from pointers. + */ + if (THIS_KERNEL_VERSION >= LINUX(4,5,0)) { + m->_page_pte = 0x1UL; + m->_page_present = 0x2UL; + m->_page_user = 0x4UL; + m->_page_rw = 0x8UL; + m->_page_guarded = 0x10UL; + } + + /* + * Starting with kernel v4.6, to accommodate both + * radix and hash MMU modes in a single kernel, + * _PAGE_PTE & _PAGE_PRESENT page flags are changed. + * Also, page table entries store physical addresses. + */ + if (THIS_KERNEL_VERSION >= LINUX(4,6,0)) { + m->_page_pte = 0x1UL << 62; + m->_page_present = 0x1UL << 63; + machdep->flags |= PHYS_ENTRY_L4; + } + + if (THIS_KERNEL_VERSION >= LINUX(4,7,0)) { + /* + * Starting with kernel v4.7 page table entries + * are always big endian on BOOK3S. Set this + * flag if kernel is not big endian. + */ + if (__BYTE_ORDER == __LITTLE_ENDIAN) + machdep->flags |= SWAP_ENTRY_L4; + } + } + if (!(machdep->flags & (VM_ORIG|VM_4_LEVEL))) { if (THIS_KERNEL_VERSION >= LINUX(2,6,14)) { machdep->flags |= VM_4_LEVEL; @@ -271,15 +428,28 @@ } if (machdep->flags & VM_ORIG) { /* pre-2.6.14 layout */ - free(machdep->machspec->level4); - machdep->machspec->level4 = NULL; + free(machdep->pud); + machdep->pud = NULL; machdep->ptrs_per_pgd = PTRS_PER_PGD; } else { /* 2.6.14 layout */ struct machine_specific *m = machdep->machspec; if (machdep->pagesize == 65536) { /* 64K pagesize */ - if (THIS_KERNEL_VERSION >= LINUX(3,10,0)) { + if (machdep->flags & RADIX_MMU) { + m->l1_index_size = PTE_INDEX_SIZE_RADIX_64K; + m->l2_index_size = PMD_INDEX_SIZE_RADIX_64K; + m->l3_index_size = PUD_INDEX_SIZE_RADIX_64K; + m->l4_index_size = PGD_INDEX_SIZE_RADIX_64K; + + } else if (!(machdep->flags & BOOK3E) && + (THIS_KERNEL_VERSION >= LINUX(4,6,0))) { + m->l1_index_size = PTE_INDEX_SIZE_L4_64K_3_10; + m->l2_index_size = PMD_INDEX_SIZE_L4_64K_4_6; + m->l3_index_size = PUD_INDEX_SIZE_L4_64K_4_6; + m->l4_index_size = PGD_INDEX_SIZE_L4_64K_3_10; + + } else if (THIS_KERNEL_VERSION >= LINUX(3,10,0)) { m->l1_index_size = PTE_INDEX_SIZE_L4_64K_3_10; m->l2_index_size = PMD_INDEX_SIZE_L4_64K_3_10; m->l3_index_size = PUD_INDEX_SIZE_L4_64K; @@ -291,19 +461,67 @@ m->l3_index_size = PUD_INDEX_SIZE_L4_64K; m->l4_index_size = PGD_INDEX_SIZE_L4_64K; } + if (!(machdep->flags & BOOK3E)) - m->pte_shift = symbol_exists("demote_segment_4k") ? - PTE_SHIFT_L4_64K_V2 : PTE_SHIFT_L4_64K_V1; - m->l2_masked_bits = PMD_MASKED_BITS_64K; + m->pte_rpn_shift = symbol_exists("demote_segment_4k") ? + PTE_RPN_SHIFT_L4_64K_V2 : PTE_RPN_SHIFT_L4_64K_V1; + + if (!(machdep->flags & BOOK3E) && + (THIS_KERNEL_VERSION >= LINUX(4,6,0))) { + m->pgd_masked_bits = PGD_MASKED_BITS_64K_4_6; + m->pud_masked_bits = PUD_MASKED_BITS_64K_4_6; + m->pmd_masked_bits = PMD_MASKED_BITS_64K_4_6; + } else { + m->pgd_masked_bits = PGD_MASKED_BITS_64K; + m->pud_masked_bits = PUD_MASKED_BITS_64K; + if ((machdep->flags & BOOK3E) && + (THIS_KERNEL_VERSION >= LINUX(4,5,0))) + m->pmd_masked_bits = PMD_MASKED_BITS_BOOK3E_64K_4_5; + else if (THIS_KERNEL_VERSION >= LINUX(3,11,0)) + m->pmd_masked_bits = PMD_MASKED_BITS_64K_3_11; + else + m->pmd_masked_bits = PMD_MASKED_BITS_64K; + } } else { /* 4K pagesize */ - m->l1_index_size = PTE_INDEX_SIZE_L4_4K; - m->l2_index_size = PMD_INDEX_SIZE_L4_4K; - m->l3_index_size = PUD_INDEX_SIZE_L4_4K; - m->l4_index_size = PGD_INDEX_SIZE_L4_4K; - m->pte_shift = (machdep->flags & BOOK3E) ? - PTE_SHIFT_L4_BOOK3E_4K : PTE_SHIFT_L4_4K; - m->l2_masked_bits = PMD_MASKED_BITS_4K; + if (machdep->flags & RADIX_MMU) { + m->l1_index_size = PTE_INDEX_SIZE_RADIX_4K; + m->l2_index_size = PMD_INDEX_SIZE_RADIX_4K; + m->l3_index_size = PUD_INDEX_SIZE_RADIX_4K; + m->l4_index_size = PGD_INDEX_SIZE_RADIX_4K; + + } else { + m->l1_index_size = PTE_INDEX_SIZE_L4_4K; + m->l2_index_size = PMD_INDEX_SIZE_L4_4K; + if (THIS_KERNEL_VERSION >= LINUX(3,7,0)) + m->l3_index_size = PUD_INDEX_SIZE_L4_4K_3_7; + else + m->l3_index_size = PUD_INDEX_SIZE_L4_4K; + m->l4_index_size = PGD_INDEX_SIZE_L4_4K; + + if (machdep->flags & BOOK3E) + m->pte_rpn_shift = PTE_RPN_SHIFT_L4_BOOK3E_4K; + else + m->pte_rpn_shift = THIS_KERNEL_VERSION >= LINUX(4,5,0) ? + PTE_RPN_SHIFT_L4_4K_4_5 : PTE_RPN_SHIFT_L4_4K; + } + + m->pgd_masked_bits = PGD_MASKED_BITS_4K; + m->pud_masked_bits = PUD_MASKED_BITS_4K; + m->pmd_masked_bits = PMD_MASKED_BITS_4K; + } + + m->pte_rpn_mask = PTE_RPN_MASK_DEFAULT; + if (!(machdep->flags & BOOK3E)) { + if (THIS_KERNEL_VERSION >= LINUX(4,6,0)) { + m->pte_rpn_mask = PTE_RPN_MASK_L4_4_6; + m->pte_rpn_shift = PTE_RPN_SHIFT_L4_4_6; + } + if (THIS_KERNEL_VERSION >= LINUX(4,7,0)) { + m->pgd_masked_bits = PGD_MASKED_BITS_4_7; + m->pud_masked_bits = PUD_MASKED_BITS_4_7; + m->pmd_masked_bits = PMD_MASKED_BITS_4_7; + } } /* Compute ptrs per each level */ @@ -311,8 +529,8 @@ m->ptrs_per_l1 = (1 << m->l1_index_size); m->ptrs_per_l2 = (1 << m->l2_index_size); m->ptrs_per_l3 = (1 << m->l3_index_size); - - machdep->ptrs_per_pgd = m->ptrs_per_l3; + m->ptrs_per_l4 = (1 << m->l4_index_size); + machdep->ptrs_per_pgd = m->ptrs_per_l4; /* Compute shifts */ m->l2_shift = m->l1_shift + m->l1_index_size; @@ -471,6 +689,14 @@ fprintf(fp, "%sVMEMMAP", others++ ? "|" : ""); if (machdep->flags & VMEMMAP_AWARE) fprintf(fp, "%sVMEMMAP_AWARE", others++ ? "|" : ""); + if (machdep->flags & BOOK3E) + fprintf(fp, "%sBOOK3E", others++ ? "|" : ""); + if (machdep->flags & PHYS_ENTRY_L4) + fprintf(fp, "%sPHYS_ENTRY_L4", others++ ? "|" : ""); + if (machdep->flags & SWAP_ENTRY_L4) + fprintf(fp, "%sSWAP_ENTRY_L4", others++ ? "|" : ""); + if (machdep->flags & RADIX_MMU) + fprintf(fp, "%sRADIX_MMU", others++ ? "|" : ""); fprintf(fp, ")\n"); fprintf(fp, " kvbase: %lx\n", machdep->kvbase); @@ -516,10 +742,12 @@ fprintf(fp, "xen_kdump_p2m_create: NULL\n"); fprintf(fp, " line_number_hooks: ppc64_line_number_hooks\n"); fprintf(fp, " last_pgd_read: %lx\n", machdep->last_pgd_read); + fprintf(fp, " last_pud_read: %lx\n", machdep->last_pud_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, "clear_machdep_cache: ppc64_clear_machdep_cache()\n"); fprintf(fp, " pgd: %lx\n", (ulong)machdep->pgd); + fprintf(fp, " pud: %lx\n", (ulong)machdep->pud); fprintf(fp, " pmd: %lx\n", (ulong)machdep->pmd); fprintf(fp, " ptbl: %lx\n", (ulong)machdep->ptbl); fprintf(fp, " ptrs_per_pgd: %d\n", machdep->ptrs_per_pgd); @@ -558,12 +786,11 @@ fprintf(fp, "\n"); fprintf(fp, " hwstackbuf: %lx\n", (ulong)machdep->machspec->hwstackbuf); fprintf(fp, " hwstacksize: %d\n", machdep->machspec->hwstacksize); - fprintf(fp, " level4: %lx\n", (ulong)machdep->machspec->level4); - fprintf(fp, " last_level4_read: %lx\n", (ulong)machdep->machspec->last_level4_read); fprintf(fp, " l4_index_size: %d\n", machdep->machspec->l4_index_size); fprintf(fp, " l3_index_size: %d\n", machdep->machspec->l3_index_size); fprintf(fp, " l2_index_size: %d\n", machdep->machspec->l2_index_size); fprintf(fp, " l1_index_size: %d\n", machdep->machspec->l1_index_size); + fprintf(fp, " ptrs_per_l4: %d\n", machdep->machspec->ptrs_per_l4); fprintf(fp, " ptrs_per_l3: %d\n", machdep->machspec->ptrs_per_l3); fprintf(fp, " ptrs_per_l2: %d\n", machdep->machspec->ptrs_per_l2); fprintf(fp, " ptrs_per_l1: %d\n", machdep->machspec->ptrs_per_l1); @@ -571,8 +798,11 @@ fprintf(fp, " l3_shift: %d\n", machdep->machspec->l3_shift); fprintf(fp, " l2_shift: %d\n", machdep->machspec->l2_shift); fprintf(fp, " l1_shift: %d\n", machdep->machspec->l1_shift); - fprintf(fp, " pte_shift: %d\n", machdep->machspec->pte_shift); - fprintf(fp, " l2_masked_bits: %x\n", machdep->machspec->l2_masked_bits); + fprintf(fp, " pte_rpn_mask: %lx\n", machdep->machspec->pte_rpn_mask); + fprintf(fp, " pte_rpn_shift: %d\n", machdep->machspec->pte_rpn_shift); + fprintf(fp, " pgd_masked_bits: %lx\n", machdep->machspec->pgd_masked_bits); + fprintf(fp, " pud_masked_bits: %lx\n", machdep->machspec->pud_masked_bits); + fprintf(fp, " pmd_masked_bits: %lx\n", machdep->machspec->pmd_masked_bits); fprintf(fp, " vmemmap_base: "); if (machdep->machspec->vmemmap_base) fprintf(fp, "%lx\n", machdep->machspec->vmemmap_base); @@ -658,7 +888,7 @@ if (!(pte & _PAGE_PRESENT)) { if (pte && verbose) { fprintf(fp, "\n"); - ppc64_translate_pte(pte, 0, PTE_SHIFT); + ppc64_translate_pte(pte, 0, PTE_RPN_SHIFT_DEFAULT); } return FALSE; } @@ -666,11 +896,11 @@ if (!pte) return FALSE; - *paddr = PAGEBASE(PTOB(pte >> PTE_SHIFT)) + PAGEOFFSET(vaddr); + *paddr = PAGEBASE(PTOB(pte >> PTE_RPN_SHIFT_DEFAULT)) + PAGEOFFSET(vaddr); if (verbose) { fprintf(fp, " PAGE: %lx\n\n", PAGEBASE(*paddr)); - ppc64_translate_pte(pte, 0, PTE_SHIFT); + ppc64_translate_pte(pte, 0, PTE_RPN_SHIFT_DEFAULT); } return TRUE; @@ -683,54 +913,60 @@ static int ppc64_vtop_level4(ulong vaddr, ulong *level4, physaddr_t *paddr, int verbose) { - ulong *level4_dir; - ulong *page_dir; + ulong *pgdir; + ulong *page_upper; ulong *page_middle; ulong *page_table; - ulong level4_pte, pgd_pte, pmd_pte; + ulong pgd_pte, pud_pte, pmd_pte; ulong pte; + uint pdshift; uint hugepage_type = 0; /* 0: regular entry; 1: huge pte; 2: huge pd */ + uint swap = !!(machdep->flags & SWAP_ENTRY_L4); if (verbose) fprintf(fp, "PAGE DIRECTORY: %lx\n", (ulong)level4); - level4_dir = (ulong *)((ulong *)level4 + L4_OFFSET(vaddr)); - FILL_L4(PAGEBASE(level4), KVADDR, PAGESIZE()); - level4_pte = ULONG(machdep->machspec->level4 + PAGEOFFSET(level4_dir)); + pgdir = (ulong *)((ulong *)level4 + PGD_OFFSET_L4(vaddr)); + FILL_PGD(PAGEBASE(level4), KVADDR, PAGESIZE()); + pgd_pte = swap64(ULONG(machdep->pgd + PAGEOFFSET(pgdir)), swap); if (verbose) - fprintf(fp, " L4: %lx => %lx\n", (ulong)level4_dir, level4_pte); - if (!level4_pte) + fprintf(fp, " PGD: %lx => %lx\n", (ulong)pgdir, pgd_pte); + if (!pgd_pte) return FALSE; - hugepage_type = get_ptetype(level4_pte); + hugepage_type = get_ptetype(pgd_pte); if (hugepage_type) { - pte = level4_pte; + pte = pgd_pte; + pdshift = machdep->machspec->l4_shift; goto out; } /* Sometimes we don't have level3 pagetable entries */ if (machdep->machspec->l3_index_size != 0) { - page_dir = (ulong *)((ulong *)level4_pte + PGD_OFFSET_L4(vaddr)); - FILL_PGD(PAGEBASE(level4_pte), KVADDR, PAGESIZE()); - pgd_pte = ULONG(machdep->pgd + PAGEOFFSET(page_dir)); + pgd_pte = pgd_page_vaddr_l4(pgd_pte); + page_upper = (ulong *)((ulong *)pgd_pte + PUD_OFFSET_L4(vaddr)); + FILL_PUD(PAGEBASE(pgd_pte), KVADDR, PAGESIZE()); + pud_pte = swap64(ULONG(machdep->pud + PAGEOFFSET(page_upper)), swap); if (verbose) - fprintf(fp, " PGD: %lx => %lx\n", (ulong)page_dir, pgd_pte); - if (!pgd_pte) + fprintf(fp, " PUD: %lx => %lx\n", (ulong)page_upper, pud_pte); + if (!pud_pte) return FALSE; - hugepage_type = get_ptetype(pgd_pte); + hugepage_type = get_ptetype(pud_pte); if (hugepage_type) { - pte = pgd_pte; + pte = pud_pte; + pdshift = machdep->machspec->l3_shift; goto out; } } else { - pgd_pte = level4_pte; + pud_pte = pgd_pte; } - page_middle = (ulong *)((ulong *)pgd_pte + PMD_OFFSET_L4(vaddr)); - FILL_PMD(PAGEBASE(pgd_pte), KVADDR, PAGESIZE()); - pmd_pte = ULONG(machdep->pmd + PAGEOFFSET(page_middle)); + pud_pte = pud_page_vaddr_l4(pud_pte); + page_middle = (ulong *)((ulong *)pud_pte + PMD_OFFSET_L4(vaddr)); + FILL_PMD(PAGEBASE(pud_pte), KVADDR, PAGESIZE()); + pmd_pte = swap64(ULONG(machdep->pmd + PAGEOFFSET(page_middle)), swap); if (verbose) fprintf(fp, " PMD: %lx => %lx\n", (ulong)page_middle, pmd_pte); @@ -741,17 +977,19 @@ hugepage_type = get_ptetype(pmd_pte); if (hugepage_type) { pte = pmd_pte; + pdshift = machdep->machspec->l2_shift; goto out; } - page_table = (ulong *)(pmd_pte & ~(machdep->machspec->l2_masked_bits)) + pmd_pte = pmd_page_vaddr_l4(pmd_pte); + page_table = (ulong *)(pmd_pte) + (BTOP(vaddr) & (machdep->machspec->ptrs_per_l1 - 1)); if (verbose) fprintf(fp, " PMD: %lx => %lx\n",(ulong)page_middle, (ulong)page_table); FILL_PTBL(PAGEBASE(pmd_pte), KVADDR, PAGESIZE()); - pte = ULONG(machdep->ptbl + PAGEOFFSET(page_table)); + pte = swap64(ULONG(machdep->ptbl + PAGEOFFSET(page_table)), swap); if (verbose) fprintf(fp, " PTE: %lx => %lx\n", (ulong)page_table, pte); @@ -759,7 +997,7 @@ if (!(pte & _PAGE_PRESENT)) { if (pte && verbose) { fprintf(fp, "\n"); - ppc64_translate_pte(pte, 0, machdep->machspec->pte_shift); + ppc64_translate_pte(pte, 0, machdep->machspec->pte_rpn_shift); } return FALSE; } @@ -777,13 +1015,22 @@ * in this directory for all the huge pages * in this huge page directory. */ - readmem(hugepage_dir(pte), KVADDR, &pte, sizeof(pte), - "hugepd_entry", RETURN_ON_ERROR); + ulong hugepd = hugepage_dir(pte); + + readmem(hugepd, KVADDR, &pte, sizeof(pte), + "hugepd_entry", RETURN_ON_ERROR); + + if (verbose) + fprintf(fp, " HUGE PD: %lx => %lx\n", hugepd, pte); + + if (!pte) + return FALSE; } - /* TODO: get page offset for huge pages based on page size */ - *paddr = PAGEBASE(PTOB(pte >> machdep->machspec->pte_shift)); + + *paddr = PAGEBASE(PTOB((pte & PTE_RPN_MASK) >> PTE_RPN_SHIFT)) + + (vaddr & ((1UL << pdshift) - 1)); } else { - *paddr = PAGEBASE(PTOB(pte >> machdep->machspec->pte_shift)) + *paddr = PAGEBASE(PTOB((pte & PTE_RPN_MASK) >> PTE_RPN_SHIFT)) + PAGEOFFSET(vaddr); } @@ -792,7 +1039,7 @@ fprintf(fp, " HUGE PAGE: %lx\n\n", PAGEBASE(*paddr)); else fprintf(fp, " PAGE: %lx\n\n", PAGEBASE(*paddr)); - ppc64_translate_pte(pte, 0, machdep->machspec->pte_shift); + ppc64_translate_pte(pte, 0, machdep->machspec->pte_rpn_shift); } return TRUE; @@ -1247,7 +1494,7 @@ * If a physaddr pointer is passed in, don't print anything. */ static int -ppc64_translate_pte(ulong pte, void *physaddr, ulonglong pte_shift) +ppc64_translate_pte(ulong pte, void *physaddr, ulonglong pte_rpn_shift) { int c, len1, len2, len3, others, page_present; char buf[BUFSIZE]; @@ -1258,8 +1505,8 @@ char *arglist[MAXARGS]; ulong paddr; - paddr = PTOB(pte >> pte_shift); - page_present = (pte & _PAGE_PRESENT); + paddr = PTOB(pte >> pte_rpn_shift); + page_present = !!(pte & _PAGE_PRESENT); if (physaddr) { *((ulong *)physaddr) = paddr; @@ -1305,6 +1552,8 @@ others = 0; if (pte) { + if (pte & _PAGE_PTE) + fprintf(fp, "%sPTE", others++ ? "|" : ""); if (pte & _PAGE_PRESENT) fprintf(fp, "%sPRESENT", others++ ? "|" : ""); if (pte & _PAGE_USER) @@ -2963,8 +3212,8 @@ void ppc64_clear_machdep_cache(void) { - if (machdep->machspec->last_level4_read != vt->kernel_pgd[0]) - machdep->machspec->last_level4_read = 0; + if (machdep->last_pgd_read != vt->kernel_pgd[0]) + machdep->last_pgd_read = 0; } static int diff -Nru crash-7.1.5/qemu-load.c crash-7.1.7/qemu-load.c --- crash-7.1.5/qemu-load.c 2016-04-27 20:46:50.000000000 +0200 +++ crash-7.1.7/qemu-load.c 2016-11-30 19:56:29.000000000 +0100 @@ -208,6 +208,16 @@ name[sz] = 0; return sz; } +static int +get_string_len (FILE *fp, char *name, uint32_t sz) +{ + size_t items ATTRIBUTE_UNUSED; + if (sz == EOF) + return -1; + items = fread (name, sz, 1, fp); + name[sz] = 0; + return sz; +} static void ram_read_blocks (FILE *fp, uint64_t size) @@ -924,6 +934,8 @@ struct qemu_device_list *result = NULL; struct qemu_device *last = NULL;; size_t items ATTRIBUTE_UNUSED; + uint32_t footerSecId ATTRIBUTE_UNUSED; + char name[257]; switch (get_be32 (fp)) { case QEMU_VM_FILE_MAGIC: @@ -961,6 +973,15 @@ break; if (sec == QEMU_VM_EOF) break; + if (sec == QEMU_VM_SECTION_FOOTER) { + footerSecId = get_be32 (fp); + continue; + } + if (sec == QEMU_VM_CONFIGURATION) { + uint32_t len = get_be32 (fp); + get_string_len (fp, name, len); + continue; + } d = device_get (devices, result, sec, fp); if (!d) diff -Nru crash-7.1.5/qemu-load.h crash-7.1.7/qemu-load.h --- crash-7.1.5/qemu-load.h 2016-04-27 20:46:50.000000000 +0200 +++ crash-7.1.7/qemu-load.h 2016-11-30 19:56:29.000000000 +0100 @@ -29,7 +29,9 @@ QEMU_VM_SECTION_PART, QEMU_VM_SECTION_END, QEMU_VM_SECTION_FULL, - QEMU_VM_SUBSECTION + QEMU_VM_SUBSECTION, + QEMU_VM_CONFIGURATION = 0x07, + QEMU_VM_SECTION_FOOTER = 0x7e }; enum qemu_features { diff -Nru crash-7.1.5/ramdump.c crash-7.1.7/ramdump.c --- crash-7.1.5/ramdump.c 2016-04-27 20:46:50.000000000 +0200 +++ crash-7.1.7/ramdump.c 2016-11-30 19:56:29.000000000 +0100 @@ -59,29 +59,19 @@ ehdr->e_shstrndx = 0; } -static int alloc_program_headers(Elf64_Phdr *phdr) +static void alloc_program_headers(Elf64_Phdr *phdr) { unsigned int i; - struct stat64 st; for (i = 0; i < nodes; i++) { phdr[i].p_type = PT_LOAD; - - if (0 > stat64(ramdump[i].path, &st)) { - error(INFO, "ramdump stat failed\n"); - return -1; - } - - phdr[i].p_filesz = st.st_size; + phdr[i].p_filesz = ramdump[i].end_paddr + 1 - ramdump[i].start_paddr; phdr[i].p_memsz = phdr[i].p_filesz; phdr[i].p_vaddr = 0; phdr[i].p_paddr = ramdump[i].start_paddr; - ramdump[i].end_paddr = ramdump[i].start_paddr + st.st_size - 1; phdr[i].p_flags = PF_R | PF_W | PF_X; phdr[i].p_align = 0; } - - return 0; } static char *write_elf(Elf64_Phdr *load, Elf64_Ehdr *e_head, size_t data_offset) @@ -196,6 +186,8 @@ e_machine = EM_AARCH64; else if (machine_type("MIPS")) e_machine = EM_MIPS; + else if (machine_type("X86_64")) + e_machine = EM_X86_64; else error(FATAL, "ramdump: unsupported machine type: %s\n", MACHINE_TYPE); @@ -219,8 +211,7 @@ load = (Elf64_Phdr *)ptr; - if (alloc_program_headers(load)) - goto end; + alloc_program_headers(load); offset += sizeof(Elf64_Phdr) * nodes; ptr += sizeof(Elf64_Phdr) * nodes; @@ -238,18 +229,26 @@ } e_file = write_elf(load, e_head, data_offset); -end: + free(e_head); return e_file; } +#define PREFIX(ptr, pat) \ + (strncmp((ptr), (pat), sizeof(pat)-1) ? 0 : \ + ((ptr) += sizeof(pat)-1, 1)) + int is_ramdump(char *p) { char *x = NULL, *y = NULL, *pat; size_t len; char *pattern; + struct stat64 st; + int is_live; int err = 0; + is_live = PREFIX(p, "live:"); + if (nodes || !strchr(p, '@')) return 0; @@ -277,11 +276,20 @@ "ramdump %s open failed:%s\n", ramdump[nodes - 1].path, strerror(errno)); + if (fstat64(ramdump[nodes - 1].rfd, &st) < 0) + error(FATAL, "ramdump stat failed\n"); + ramdump[nodes - 1].end_paddr = + ramdump[nodes - 1].start_paddr + st.st_size - 1; } pat = NULL; } + if (nodes && is_live) { + pc->flags |= LIVE_SYSTEM; + pc->dumpfile = ramdump[0].path; + pc->live_memsrc = pc->dumpfile; + } return nodes; } diff -Nru crash-7.1.5/README crash-7.1.7/README --- crash-7.1.5/README 2016-04-27 20:46:50.000000000 +0200 +++ crash-7.1.7/README 2016-11-30 19:56:29.000000000 +0100 @@ -74,8 +74,8 @@ To build the crash utility: - $ tar -xf crash-7.1.5.tar.gz - $ cd crash-7.1.5 + $ tar -xf crash-7.1.7.tar.gz + $ cd crash-7.1.7 $ make The initial build will take several minutes because the embedded gdb module @@ -116,7 +116,7 @@ If neither /dev/mem or /dev/crash are available, then /proc/kcore will be be used as the live memory source. If /proc/kcore is also restricted, then the Red Hat /dev/crash driver may be compiled and installed; its source - is included in the crash-7.1.5/memory_driver subdirectory. + is included in the crash-7.1.7/memory_driver subdirectory. If the kernel file is stored in /boot, /, /boot/efi, or in any /usr/src or /usr/lib/debug/lib/modules subdirectory, then no command line arguments @@ -127,7 +127,7 @@ $ crash - crash 7.1.5 + crash 7.1.7 Copyright (C) 2002-2016 Red Hat, Inc. Copyright (C) 2004, 2005, 2006, 2010 IBM Corporation Copyright (C) 1999-2006 Hewlett-Packard Co @@ -152,7 +152,7 @@ KERNEL: /boot/vmlinux DUMPFILE: /dev/mem CPUS: 1 - DATE: Wed Apr 27 14:46:50 2016 + DATE: Wed Nov 30 13:56:29 2016 UPTIME: 10 days, 22:55:18 LOAD AVERAGE: 0.08, 0.03, 0.01 TASKS: 42 @@ -180,7 +180,7 @@ exit list ptov sys q extend log rd task - crash version: 7.1.5 gdb version: 7.6 + crash version: 7.1.7 gdb version: 7.6 For help on any command above, enter "help ". For help on input options, enter "help input". For help on output options, enter "help output". @@ -193,7 +193,7 @@ $ crash vmlinux vmcore - crash 7.1.5 + crash 7.1.7 Copyright (C) 2002-2016 Red Hat, Inc. Copyright (C) 2004, 2005, 2006, 2010 IBM Corporation Copyright (C) 1999-2006 Hewlett-Packard Co diff -Nru crash-7.1.5/remote.c crash-7.1.7/remote.c --- crash-7.1.5/remote.c 2016-04-27 20:46:50.000000000 +0200 +++ crash-7.1.7/remote.c 2016-11-30 19:56:29.000000000 +0100 @@ -2537,7 +2537,7 @@ error(INFO, "too many dumpfile/memory arguments\n"); program_usage(SHORT_FORM); } - pc->flags |= MEMSRC_LOCAL; + pc->flags2 |= MEMSRC_LOCAL; if (pc->flags & (DEVMEM|MEMMOD)) { if (!get_proc_version()) error(INFO, "/proc/version: %s\n", @@ -3025,7 +3025,7 @@ return FALSE; } - if (pc->flags & MEMSRC_LOCAL) { + if (pc->flags2 & MEMSRC_LOCAL) { error(INFO, "%s is a local file\n", pc->dumpfile); return FALSE; } diff -Nru crash-7.1.5/.rh_rpm_package crash-7.1.7/.rh_rpm_package --- crash-7.1.5/.rh_rpm_package 2016-04-27 20:46:50.000000000 +0200 +++ crash-7.1.7/.rh_rpm_package 2016-11-30 19:56:29.000000000 +0100 @@ -1 +1 @@ -7.1.5 +7.1.7 diff -Nru crash-7.1.5/s390x.c crash-7.1.7/s390x.c --- crash-7.1.5/s390x.c 2016-04-27 20:46:50.000000000 +0200 +++ crash-7.1.7/s390x.c 2016-11-30 19:56:29.000000000 +0100 @@ -1304,8 +1304,7 @@ * Print task stack */ if (THIS_KERNEL_VERSION >= LINUX(2, 6, 0)) { - readmem(bt->task + OFFSET(task_struct_thread_info), KVADDR, - &low, sizeof(long), "thread info", FAULT_ON_ERROR); + low = task_to_stackbase(bt->task); } else { low = bt->task; } diff -Nru crash-7.1.5/symbols.c crash-7.1.7/symbols.c --- crash-7.1.5/symbols.c 2016-04-27 20:46:50.000000000 +0200 +++ crash-7.1.7/symbols.c 2016-11-30 19:56:29.000000000 +0100 @@ -81,6 +81,7 @@ static void do_datatype_addr(struct datatype_member *, ulong, int, ulong, char **, int); static void process_gdb_output(char *, unsigned, const char *, int); +static char *expr_type_name(const char *); static int display_per_cpu_info(struct syment *, int, char *); static struct load_module *get_module_percpu_sym_owner(struct syment *); static int is_percpu_symbol(struct syment *); @@ -583,7 +584,7 @@ * relocated "_stext" value found in either a dumpfile's vmcoreinfo * or in /proc/kallsyms on a live system. * - * Setting KASLR_CHECK will trigger a search for "randomize_modules" + * Setting KASLR_CHECK will trigger a search for "module_load_offset" * during the initial symbol sort operation, and if found, will * set (RELOC_AUTO|KASLR). On live systems, the search is done * here by checking /proc/kallsyms. @@ -593,7 +594,8 @@ { char *string; - if (!machine_type("X86_64") || (kt->flags & RELOC_SET)) + if ((!machine_type("X86_64") && !machine_type("ARM64")) || + (kt->flags & RELOC_SET)) return; /* @@ -603,7 +605,7 @@ st->_stext_vmlinux = UNINITIALIZED; if (ACTIVE() && /* Linux 3.15 */ - (symbol_value_from_proc_kallsyms("randomize_modules") != BADVAL)) { + (symbol_value_from_proc_kallsyms("module_load_offset") != BADVAL)) { kt->flags2 |= (RELOC_AUTO|KASLR); st->_stext_vmlinux = UNINITIALIZED; } @@ -712,7 +714,7 @@ if (machine_type("X86")) { if (!(kt->flags & RELOC_SET)) kt->flags |= RELOC_FORCE; - } else if (machine_type("X86_64")) { + } else if (machine_type("X86_64") || machine_type("ARM64")) { if ((kt->flags2 & RELOC_AUTO) && !(kt->flags & RELOC_SET)) derive_kaslr_offset(abfd, dynamic, from, fromend, size, store); @@ -783,7 +785,8 @@ error(FATAL, "symbol table namespace malloc: %s\n", strerror(errno)); - if (!machine_type("X86") && !machine_type("X86_64")) + if (!machine_type("X86") && !machine_type("X86_64") && + !machine_type("ARM64")) kt->flags &= ~RELOC_SET; first = 0; @@ -833,7 +836,7 @@ } /* - * Handle x86 kernels configured such that the vmlinux symbols + * Handle x86/arm64 kernels configured such that the vmlinux symbols * are not as loaded into the kernel (not unity-mapped). */ static ulong @@ -1418,9 +1421,11 @@ for (i = first = last = 0; i < nsyms; i++) { modsym = (struct module_symbol *) (modsymbuf + (i * sizeof(struct module_symbol))); - if (!first) + if (!first + || first > (ulong)modsym->name) first = (ulong)modsym->name; - last = (ulong)modsym->name; + if ((ulong)modsym->name > last) + last = (ulong)modsym->name; } if (last > first) { @@ -1441,15 +1446,11 @@ } else strbuf = NULL; - for (i = first = last = 0; i < nsyms; i++) { + for (i = 0; i < nsyms; i++) { modsym = (struct module_symbol *) (modsymbuf + (i * sizeof(struct module_symbol))); - if (!first) - first = (ulong)modsym->name; - last = (ulong)modsym->name; - BZERO(buf1, BUFSIZE); if (strbuf) @@ -1722,9 +1723,11 @@ for (i = first = last = 0; i < nsyms; i++) { modsym = (struct kernel_symbol *) (modsymbuf + (i * sizeof(struct kernel_symbol))); - if (!first) + if (!first + || first > (ulong)modsym->name) first = (ulong)modsym->name; - last = (ulong)modsym->name; + if ((ulong)modsym->name > last) + last = (ulong)modsym->name; } if (last > first) { @@ -1745,15 +1748,12 @@ } else strbuf = NULL; - for (i = first = last = 0; i < nsyms; i++) { + + for (i = 0; i < nsyms; i++) { modsym = (struct kernel_symbol *) (modsymbuf + (i * sizeof(struct kernel_symbol))); - if (!first) - first = (ulong)modsym->name; - last = (ulong)modsym->name; - BZERO(buf1, BUFSIZE); if (strbuf) @@ -1797,9 +1797,11 @@ for (i = first = last = 0; i < ngplsyms; i++) { modsym = (struct kernel_symbol *) (modsymbuf + (i * sizeof(struct kernel_symbol))); - if (!first) + if (!first + || first > (ulong)modsym->name) first = (ulong)modsym->name; - last = (ulong)modsym->name; + if ((ulong)modsym->name > last) + last = (ulong)modsym->name; } if (last > first) { @@ -1820,15 +1822,11 @@ } else strbuf = NULL; - for (i = first = last = 0; i < ngplsyms; i++) { + for (i = 0; i < ngplsyms; i++) { modsym = (struct kernel_symbol *) (modsymbuf + (i * sizeof(struct kernel_symbol))); - if (!first) - first = (ulong)modsym->name; - last = (ulong)modsym->name; - BZERO(buf1, BUFSIZE); if (strbuf) @@ -2249,10 +2247,11 @@ continue; /* - * On ARM we have linker mapping symbols like '$a' and '$d'. + * On ARM/ARM64 we have linker mapping symbols like '$a' + * or '$x' for ARM64, and '$d'. * Make sure that these don't end up into our symbol list. */ - if (machine_type("ARM") && + if ((machine_type("ARM") || machine_type("ARM64")) && !machdep->verify_symbol(nameptr, ec->st_value, ec->st_info)) continue; @@ -3925,7 +3924,8 @@ sp = NULL; show_flags &= ~SHOW_MODULE; - if (clean_arg() && hexadecimal(args[optind], 0)) { + if (clean_arg() && + (!symbol_exists(args[optind]) && hexadecimal(args[optind], 0))) { errflag = 0; value = htol(args[optind], RETURN_ON_ERROR, &errflag); @@ -4681,7 +4681,7 @@ if ((sp = machdep->value_to_symbol(value, offset))) return sp; - if (IS_VMALLOC_ADDR(value)) + if (IS_VMALLOC_ADDR(value)) goto check_modules; if ((sp = symval_hash_search(value)) == NULL) @@ -6114,6 +6114,7 @@ char *separator; char *structname, *members; char *memberlist[MAXARGS]; + char *typename; dm = &datatype_member; count = 0xdeadbeef; @@ -6247,6 +6248,15 @@ sp->name); cpuspec = NULL; } + if (cpuspec) { + if ((typename = expr_type_name(sp->name))) { + if (LASTCHAR(typename) == '*') + error(WARNING, + "percpu symbol \"%s\" is of type pointer\n", + sp->name); + FREEBUF(typename); + } + } addr = sp->value; aflag++; } else { @@ -6438,6 +6448,37 @@ } } +int +is_string(char *structure, char *member) +{ + int retval; + char *t; + char buf[BUFSIZE]; + + retval = FALSE; + open_tmpfile(); + whatis_datatype(structure, STRUCT_REQUEST, pc->tmpfile); + rewind(pc->tmpfile); + while (fgets(buf, BUFSIZE, pc->tmpfile)) { + if (!(t = strstr(buf, "char "))) + continue; + t += 5; + if (*t == '*') + t++; + if (t != strstr(t, member)) + continue; + t += strlen(member); + if (*t == ';' || *t == '[') { + retval = TRUE; + break; + } + } + + close_tmpfile(); + + return retval; +} + /* * Generic function for dumping data structure declarations, with a small @@ -6687,7 +6728,7 @@ if (treq->idx == treq->cnt) { s = sizeof(struct type_info) * treq->cnt; - treq->types = (void *)resizebuf((void *)treq->types, s, s * 3); + RESIZEBUF(treq->types, s, s * 3); treq->cnt *= 3; } @@ -8499,6 +8540,8 @@ OFFSET(sched_info_last_arrival)); fprintf(fp, " task_struct_thread_info: %ld\n", OFFSET(task_struct_thread_info)); + fprintf(fp, " task_struct_stack: %ld\n", + OFFSET(task_struct_stack)); fprintf(fp, " task_struct_nsproxy: %ld\n", OFFSET(task_struct_nsproxy)); fprintf(fp, " task_struct_rlim: %ld\n", @@ -9401,6 +9444,8 @@ OFFSET(tvec_s_vec)); fprintf(fp, " tvec_t_base_s_tv1: %ld\n", OFFSET(tvec_t_base_s_tv1)); + fprintf(fp, " timer_base_vectors: %ld\n", + OFFSET(timer_base_vectors)); fprintf(fp, " wait_queue_task: %ld\n", OFFSET(wait_queue_task)); @@ -9881,6 +9926,14 @@ OFFSET(request_queue_in_flight)); fprintf(fp, " request_queue_rq: %ld\n", OFFSET(request_queue_rq)); + fprintf(fp, " request_queue_mq_ops: %ld\n", + OFFSET(request_queue_mq_ops)); + fprintf(fp, " request_queue_queue_ctx: %ld\n", + OFFSET(request_queue_queue_ctx)); + fprintf(fp, " blk_mq_ctx_rq_dispatched: %ld\n", + OFFSET(blk_mq_ctx_rq_dispatched)); + fprintf(fp, " blk_mq_ctx_rq_completed: %ld\n", + OFFSET(blk_mq_ctx_rq_completed)); fprintf(fp, " subsys_private_klist_devices: %ld\n", OFFSET(subsys_private_klist_devices)); fprintf(fp, " subsystem_kset: %ld\n", @@ -10291,6 +10344,8 @@ SIZE(hrtimer_clock_base)); fprintf(fp, " hrtimer_base: %ld\n", SIZE(hrtimer_base)); + fprintf(fp, " timer_base: %ld\n", + SIZE(timer_base)); fprintf(fp, " tnt: %ld\n", SIZE(tnt)); @@ -12132,8 +12187,8 @@ st->_stext_vmlinux = valueof(y); } if (kt->flags2 & KASLR_CHECK) { - if (STREQ(x->name, "randomize_modules") || - STREQ(y->name, "randomize_modules")) { + if (STREQ(x->name, "module_load_offset") || + STREQ(y->name, "module_load_offset")) { kt->flags2 &= ~KASLR_CHECK; kt->flags2 |= (RELOC_AUTO|KASLR); } diff -Nru crash-7.1.5/task.c crash-7.1.7/task.c --- crash-7.1.5/task.c 2016-04-27 20:46:50.000000000 +0200 +++ crash-7.1.7/task.c 2016-11-30 19:56:29.000000000 +0100 @@ -108,6 +108,7 @@ static void show_ps_summary(ulong); static void irqstacks_init(void); static void parse_task_thread(int argcnt, char *arglist[], struct task_context *); +static void stack_overflow_check_init(void); /* * Figure out how much space will be required to hold the task context @@ -212,21 +213,43 @@ get_idle_threads(&tt->idle_threads[0], kt->cpus); } - if (MEMBER_EXISTS("task_struct", "thread_info")) - MEMBER_OFFSET_INIT(task_struct_thread_info, "task_struct", - "thread_info"); - else if (MEMBER_EXISTS("task_struct", "stack")) - MEMBER_OFFSET_INIT(task_struct_thread_info, "task_struct", - "stack"); - else - ASSIGN_OFFSET(task_struct_thread_info) = INVALID_OFFSET; + /* + * Handle CONFIG_THREAD_INFO_IN_TASK changes + */ + MEMBER_OFFSET_INIT(task_struct_stack, "task_struct", "stack"); + MEMBER_OFFSET_INIT(task_struct_thread_info, "task_struct", "thread_info"); if (VALID_MEMBER(task_struct_thread_info)) { - MEMBER_OFFSET_INIT(thread_info_task, "thread_info", "task"); - MEMBER_OFFSET_INIT(thread_info_cpu, "thread_info", "cpu"); - MEMBER_OFFSET_INIT(thread_info_flags, "thread_info", "flags"); - MEMBER_OFFSET_INIT(thread_info_previous_esp, "thread_info", - "previous_esp"); + switch (MEMBER_TYPE("task_struct", "thread_info")) + { + case TYPE_CODE_PTR: + break; + case TYPE_CODE_STRUCT: + tt->flags |= THREAD_INFO_IN_TASK; + break; + default: + error(FATAL, + "unexpected type code for task_struct.thread_info: %ld\n", + MEMBER_TYPE("task_struct", "thread_info")); + break; + } + } else if (VALID_MEMBER(task_struct_stack)) + MEMBER_OFFSET_INIT(task_struct_thread_info, "task_struct", "stack"); + + if (VALID_MEMBER(task_struct_thread_info)) { + if (tt->flags & THREAD_INFO_IN_TASK) { + MEMBER_OFFSET_INIT(thread_info_flags, "thread_info", "flags"); + /* (unnecessary) reminders */ + ASSIGN_OFFSET(thread_info_task) = INVALID_OFFSET; + ASSIGN_OFFSET(thread_info_cpu) = INVALID_OFFSET; + ASSIGN_OFFSET(thread_info_previous_esp) = INVALID_OFFSET; + } else { + MEMBER_OFFSET_INIT(thread_info_task, "thread_info", "task"); + MEMBER_OFFSET_INIT(thread_info_cpu, "thread_info", "cpu"); + MEMBER_OFFSET_INIT(thread_info_flags, "thread_info", "flags"); + MEMBER_OFFSET_INIT(thread_info_previous_esp, "thread_info", + "previous_esp"); + } STRUCT_SIZE_INIT(thread_info, "thread_info"); tt->flags |= THREAD_INFO; } @@ -477,6 +500,8 @@ tt->flags |= PIDHASH; } + tt->pf_kthread = UNINITIALIZED; + /* * Get the IRQ stacks info if it's configured. */ @@ -494,7 +519,8 @@ tt->flags &= ~(TASK_REFRESH|TASK_REFRESH_OFF); if (ACTIVE()) { - active_pid = REMOTE() ? pc->server_pid : pc->program_pid; + active_pid = REMOTE() ? pc->server_pid : + LOCAL_ACTIVE() ? pc->program_pid : 1; set_context(NO_TASK, active_pid); tt->this_task = pid_to_task(active_pid); } @@ -514,6 +540,8 @@ if (pc->flags & SILENT) initialize_task_state(); + stack_overflow_check_init(); + tt->flags |= TASK_INIT_DONE; } @@ -2352,10 +2380,16 @@ tgid_addr = (pid_t *)(tp + OFFSET(task_struct_tgid)); comm_addr = (char *)(tp + OFFSET(task_struct_comm)); if (tt->flags & THREAD_INFO) { - tc->thread_info = ULONG(tp + OFFSET(task_struct_thread_info)); + if (tt->flags & THREAD_INFO_IN_TASK) + tc->thread_info = task + OFFSET(task_struct_thread_info); + else + tc->thread_info = ULONG(tp + OFFSET(task_struct_thread_info)); fill_thread_info(tc->thread_info); - processor_addr = (int *) (tt->thread_info + - OFFSET(thread_info_cpu)); + if (tt->flags & THREAD_INFO_IN_TASK) + processor_addr = (int *) (tp + OFFSET(task_struct_cpu)); + else + processor_addr = (int *) (tt->thread_info + + OFFSET(thread_info_cpu)); } else if (VALID_MEMBER(task_struct_processor)) processor_addr = (int *) (tp + OFFSET(task_struct_processor)); else if (VALID_MEMBER(task_struct_cpu)) @@ -4161,7 +4195,7 @@ while (child != parent) { child = task_list[cnt++] = parent; parent = parent_of(child); - if (cnt == reserved) { + if ((cnt * sizeof(ulong)) == reserved) { RESIZEBUF(buffer, reserved, reserved * 2); reserved *= 2; task_list = (ulong *)buffer; @@ -4474,7 +4508,13 @@ ulong task_to_stackbase(ulong task) { - if (tt->flags & THREAD_INFO) + ulong stackbase; + + if (tt->flags & THREAD_INFO_IN_TASK) { + readmem(task + OFFSET(task_struct_stack), KVADDR, &stackbase, + sizeof(void *), "task_struct.stack", FAULT_ON_ERROR); + return stackbase; + } else if (tt->flags & THREAD_INFO) return task_to_thread_info(task); else return (task & ~(STACKSIZE()-1)); @@ -5369,18 +5409,16 @@ { int has_cpu; + if (LOCAL_ACTIVE() && (task == tt->this_task)) + return TRUE; if (DUMPFILE() && is_panic_thread(task)) return TRUE; fill_task_struct(task); - has_cpu = tt->last_task_read ? + has_cpu = tt->last_task_read ? task_has_cpu(task, tt->task_struct) : 0; - if (!(kt->flags & SMP) && !has_cpu && ACTIVE() && - (task == tt->this_task)) - has_cpu = TRUE; - return(has_cpu); } @@ -6381,8 +6419,8 @@ bt->flags |= BT_TEXT_SYMBOLS_ALL; } if ((fd->flags & FOREACH_o_FLAG) || - (kt->flags & USE_OLD_BT)) - bt->flags |= BT_OLD_BACK_TRACE; + (kt->flags & USE_OPT_BT)) + bt->flags |= BT_OPT_BACK_TRACE; if (fd->flags & FOREACH_e_FLAG) bt->flags |= BT_EFRAME_SEARCH; #ifdef GDB_5_3 @@ -6953,6 +6991,9 @@ if (tt->flags & THREAD_INFO) sprintf(&buf[strlen(buf)], "%sTHREAD_INFO", others++ ? "|" : ""); + if (tt->flags & THREAD_INFO_IN_TASK) + sprintf(&buf[strlen(buf)], + "%sTHREAD_INFO_IN_TASK", others++ ? "|" : ""); if (tt->flags & IRQSTACKS) sprintf(&buf[strlen(buf)], "%sIRQSTACKS", others++ ? "|" : ""); @@ -6996,7 +7037,20 @@ fprintf(fp, " init_pid_ns: %lx\n", tt->init_pid_ns); fprintf(fp, " filepages: %ld\n", tt->filepages); fprintf(fp, " anonpages: %ld\n", tt->anonpages); - + fprintf(fp, " stack_end_magic: %lx\n", tt->stack_end_magic); + fprintf(fp, " pf_kthread: %lx ", tt->pf_kthread); + switch (tt->pf_kthread) + { + case UNINITIALIZED: + fprintf(fp, "(UNINITIALIZED)\n"); + break; + case 0: + fprintf(fp, "(n/a)\n"); + break; + default: + fprintf(fp, "(PF_KTHREAD)\n"); + break; + } wrap = sizeof(void *) == SIZEOF_32BIT ? 8 : 4; flen = sizeof(void *) == SIZEOF_32BIT ? 8 : 16; @@ -7230,6 +7284,28 @@ struct task_context *tc; ulong mm; + if (tt->pf_kthread == UNINITIALIZED) { + if (THIS_KERNEL_VERSION >= LINUX(2,6,27)) { + tt->pf_kthread = PF_KTHREAD; + + if ((tc = pid_to_context(0)) && + !(task_flags(tc->task) & PF_KTHREAD)) { + error(WARNING, "pid 0: PF_KTHREAD not set?\n"); + tt->pf_kthread = 0; + } + if ((tc = pid_to_context(1)) && + task_mm(tc->task, FALSE) && + (task_flags(tc->task) & PF_KTHREAD)) { + error(WARNING, "pid 1: PF_KTHREAD set?\n"); + tt->pf_kthread = 0; + } + } else + tt->pf_kthread = 0; + } + + if (tt->pf_kthread) + return (task_flags(task) & tt->pf_kthread ? TRUE : FALSE); + tc = task_to_context(task); if ((tc->pid == 0) && !STREQ(tc->comm, pc->program_name)) @@ -10070,4 +10146,141 @@ return task_to_stackbase(task) + STACKSIZE(); } +#define STACK_END_MAGIC 0x57AC6E9D +static void +stack_overflow_check_init(void) +{ + int pid; + struct task_context *tc; + ulong location, magic; + + if (!(tt->flags & THREAD_INFO)) + return; + + for (pid = 1; pid < 10; pid++) { + if (!(tc = pid_to_context(pid))) + continue; + + if (tt->flags & THREAD_INFO_IN_TASK) + location = task_to_stackbase(tc->task); + else + location = tc->thread_info + SIZE(thread_info); + + if (!readmem(location, KVADDR, &magic, sizeof(long), + "stack magic", RETURN_ON_ERROR|QUIET)) + continue; + + if (magic == STACK_END_MAGIC) { + tt->stack_end_magic = STACK_END_MAGIC; + break; + } + } +} + +/* + * Check thread_info.task and thread_info.cpu members, + * and the STACK_END_MAGIC location. + */ +void +check_stack_overflow(void) +{ + int i, overflow, cpu_size, cpu, total; + char buf[BUFSIZE]; + ulong magic, task, stackbase; + struct task_context *tc; + + if (!tt->stack_end_magic && + INVALID_MEMBER(thread_info_task) && + INVALID_MEMBER(thread_info_cpu)) + option_not_supported('v'); + + cpu_size = VALID_MEMBER(thread_info_cpu) ? + MEMBER_SIZE("thread_info", "cpu") : 0; + + tc = FIRST_CONTEXT(); + for (i = total = 0; i < RUNNING_TASKS(); i++, tc++) { + overflow = 0; + + if (tt->flags & THREAD_INFO_IN_TASK) { + if (!readmem(task_to_stackbase(tc->task), KVADDR, &stackbase, + sizeof(ulong), "stack overflow check", RETURN_ON_ERROR)) + continue; + goto check_stack_end_magic; + } else { + if (!readmem(tc->thread_info, KVADDR, buf, + SIZE(thread_info) + sizeof(ulong), + "stack overflow check", RETURN_ON_ERROR)) + continue; + } + + if (VALID_MEMBER(thread_info_task)) { + task = ULONG(buf + OFFSET(thread_info_task)); + if (task != tc->task) { + print_task_header(fp, tc, 0); + fprintf(fp, + " possible stack overflow: thread_info.task: %lx != %lx\n", + task, tc->task); + overflow++; total++; + } + } + + if (VALID_MEMBER(thread_info_cpu)) { + switch (cpu_size) + { + case 1: + cpu = UCHAR(buf + OFFSET(thread_info_cpu)); + break; + case 2: + cpu = USHORT(buf + OFFSET(thread_info_cpu)); + break; + case 4: + cpu = UINT(buf + OFFSET(thread_info_cpu)); + break; + default: + cpu = 0; + break; + } + if (cpu >= kt->cpus) { + if (!overflow) + print_task_header(fp, tc, 0); + fprintf(fp, + " possible stack overflow: thread_info.cpu: %d >= %d\n", + cpu, kt->cpus); + overflow++; total++; + } + } + +check_stack_end_magic: + if (!tt->stack_end_magic) + continue; + + if (tt->flags & THREAD_INFO_IN_TASK) + magic = stackbase; + else + magic = ULONG(buf + SIZE(thread_info)); + + if (tc->pid == 0) { + if (kernel_symbol_exists("init_task")) { + if (tc->task == symbol_value("init_task")) + continue; + } else + continue; + } + + if (magic != STACK_END_MAGIC) { + if (!overflow) + print_task_header(fp, tc, 0); + fprintf(fp, + " possible stack overflow: %lx: %lx != STACK_END_MAGIC\n", + tc->thread_info + SIZE(thread_info), magic); + overflow++, total++; + } + + if (overflow) + fprintf(fp, "\n"); + } + + if (!total) + fprintf(fp, "No stack overflows detected\n"); +} diff -Nru crash-7.1.5/tools.c crash-7.1.7/tools.c --- crash-7.1.5/tools.c 2016-04-27 20:46:50.000000000 +0200 +++ crash-7.1.7/tools.c 2016-11-30 19:56:29.000000000 +0100 @@ -1,8 +1,8 @@ /* tools.c - core analysis suite * * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. - * Copyright (C) 2002-2015 David Anderson - * Copyright (C) 2002-2015 Red Hat, Inc. All rights reserved. + * Copyright (C) 2002-2016 David Anderson + * Copyright (C) 2002-2016 Red Hat, Inc. All rights reserved. * * 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 @@ -28,6 +28,17 @@ static void rdtree_iteration(ulong, struct tree_data *, char *, ulong, uint); static void dump_struct_members_for_tree(struct tree_data *, int, ulong); +struct req_entry { + char *arg, *name, **member; + int *is_str, *is_ptr; + ulong *width, *offset; + int count; +}; + +static void print_value(struct req_entry *, unsigned int, ulong, unsigned int); +static struct req_entry *fill_member_offsets(char *); +static void dump_struct_members_fast(struct req_entry *, int, ulong); + /* * General purpose error reporting routine. Type INFO prints the message * and returns. Type FATAL aborts the command in progress, and longjmps @@ -1787,7 +1798,7 @@ return; if (ACTIVE()) { - set_context(NO_TASK, pc->program_pid); + set_context(tt->this_task, NO_PID); show_context(CURRENT_CONTEXT()); return; } @@ -3229,7 +3240,7 @@ BZERO(ld, sizeof(struct list_data)); struct_list_offset = 0; - while ((c = getopt(argcnt, args, "Hhrs:e:o:xdl:")) != EOF) { + while ((c = getopt(argcnt, args, "Hhrs:S:e:o:xdl:")) != EOF) { switch(c) { case 'H': @@ -3246,9 +3257,13 @@ break; case 's': - if (ld->structname_args++ == 0) + case 'S': + if (ld->structname_args++ == 0) hq_open(); hq_enter((ulong)optarg); + ld->flags |= (c == 's') ? LIST_PARSE_MEMBER : LIST_READ_MEMBER; + if (count_bits_long(ld->flags & (LIST_PARSE_MEMBER|LIST_READ_MEMBER)) > 1) + error(FATAL, "-S and -s options are mutually exclusive\n"); break; case 'l': @@ -3319,7 +3334,7 @@ hq_close(); ld->struct_list_offset = struct_list_offset; } else if (struct_list_offset) { - error(INFO, "-l option can only be used with -s option\n"); + error(INFO, "-l option can only be used with -s or -S option\n"); cmd_usage(pc->curcmd, SYNOPSIS); } @@ -3483,6 +3498,128 @@ FREEBUF(ld->structname); } +void +dump_struct_members_fast(struct req_entry *e, int radix, ulong p) +{ + unsigned int i; + char b[BUFSIZE]; + + if (!(e && IS_KVADDR(p))) + return; + + if (!radix) + radix = *gdb_output_radix; + + for (i = 0; i < e->count; i++) { + if (0 < e->width[i] && (e->width[i] <= 8 || e->is_str[i])) { + print_value(e, i, p, e->is_ptr[i] ? 16 : radix); + } else if (e->width[i] == 0 || e->width[i] > 8) { + snprintf(b, BUFSIZE, "%s.%s", e->name, e->member[i]); + dump_struct_member(b, p, radix); + } + } +} + +static struct req_entry * +fill_member_offsets(char *arg) +{ + int j; + char *p, m; + struct req_entry *e; + char buf[BUFSIZE]; + + if (!(arg && *arg)) + return NULL; + + j = count_chars(arg, ',') + 1; + e = (struct req_entry *)GETBUF(sizeof(*e)); + + e->arg = GETBUF(strlen(arg + 1)); + strcpy(e->arg, arg); + + m = ((p = strchr(e->arg, '.')) != NULL); + if (!p++) + p = e->arg + strlen(e->arg) + 1; + + e->name = GETBUF(p - e->arg); + strncpy(e->name, e->arg, p - e->arg - 1); + + if (!m) + return e; + + e->count = count_chars(p, ',') + 1; + e->width = (ulong *)GETBUF(e->count * sizeof(ulong)); + e->is_ptr = (int *)GETBUF(e->count * sizeof(int)); + e->is_str = (int *)GETBUF(e->count * sizeof(int)); + e->member = (char **)GETBUF(e->count * sizeof(char *)); + e->offset = (ulong *)GETBUF(e->count * sizeof(ulong)); + + replace_string(p, ",", ' '); + parse_line(p, e->member); + + for (j = 0; j < e->count; j++) { + e->offset[j] = MEMBER_OFFSET(e->name, e->member[j]); + if (e->offset[j] == INVALID_OFFSET) + e->offset[j] = ANON_MEMBER_OFFSET(e->name, e->member[j]); + if (e->offset[j] == INVALID_OFFSET) + error(FATAL, "Can't get offset of '%s.%s'\n", + e->name, e->member[j]); + + e->is_ptr[j] = MEMBER_TYPE(e->name, e->member[j]) == TYPE_CODE_PTR; + e->is_str[j] = is_string(e->name, e->member[j]); + + /* Dirty hack for obtaining size of particular field */ + snprintf(buf, BUFSIZE, "%s + 1", e->member[j]); + e->width[j] = ANON_MEMBER_OFFSET(e->name, buf) - e->offset[j]; + } + + return e; +} + +static void +print_value(struct req_entry *e, unsigned int i, ulong addr, unsigned int radix) +{ + union { uint64_t v64; uint32_t v32; + uint16_t v16; uint8_t v8; + } v; + char buf[BUFSIZE]; + struct syment *sym; + + addr += e->offset[i]; + + /* Read up to 8 bytes, counters, pointers, etc. */ + if (e->width[i] <= 8 && !readmem(addr, KVADDR, &v, e->width[i], + "structure value", RETURN_ON_ERROR | QUIET)) { + error(INFO, "cannot access member: %s at %lx\n", e->member[i], addr); + return; + } + snprintf(buf, BUFSIZE, " %%s = %s%%%s%s", + (radix == 16 ? "0x" : ""), + (e->width[i] == 8 ? "l" : ""), + (radix == 16 ? "x" : "u" ) + ); + + switch (e->width[i]) { + case 1: fprintf(fp, buf, e->member[i], v.v8); break; + case 2: fprintf(fp, buf, e->member[i], v.v16); break; + case 4: fprintf(fp, buf, e->member[i], v.v32); break; + case 8: fprintf(fp, buf, e->member[i], v.v64); break; + } + + + if (e->is_str[i]) { + if (e->is_ptr[i]) { + read_string(v.v64, buf, BUFSIZE); + fprintf(fp, " \"%s\"", buf); + } else { + read_string(addr, buf, e->width[i]); + fprintf(fp, " %s = \"%s\"", e->member[i], buf); + } + } else if ((sym = value_search(v.v64, 0)) && is_symbol_text(sym)) + fprintf(fp, " <%s>", sym->name); + + fprintf(fp, "\n"); +} /* * Does the work for cmd_list() and any other function that requires the @@ -3491,10 +3628,11 @@ int do_list(struct list_data *ld) { - ulong next, last, first; + ulong next, last, first, offset; ulong searchfor, readflag; int i, count, others, close_hq_on_return; unsigned int radix; + struct req_entry **e = NULL; if (CRASHDEBUG(1)) { others = 0; @@ -3580,6 +3718,14 @@ if (ld->header) fprintf(fp, "%s", ld->header); + offset = ld->list_head_offset + ld->struct_list_offset; + + if (ld->structname && (ld->flags & LIST_READ_MEMBER)) { + e = (struct req_entry **)GETBUF(sizeof(*e) * ld->structname_args); + for (i = 0; i < ld->structname_args; i++) + e[i] = fill_member_offsets(ld->structname[i]); + } + while (1) { if (ld->flags & VERBOSE) { fprintf(fp, "%lx\n", next - ld->list_head_offset); @@ -3589,12 +3735,15 @@ switch (count_chars(ld->structname[i], '.')) { case 0: - dump_struct(ld->structname[i], - next - ld->list_head_offset - ld->struct_list_offset, - radix); + dump_struct(ld->structname[i], + next - offset, radix); break; default: - dump_struct_members(ld, i, next); + if (ld->flags & LIST_PARSE_MEMBER) + dump_struct_members(ld, i, next); + else if (ld->flags & LIST_READ_MEMBER) + dump_struct_members_fast(e[i], + radix, next - offset); break; } } @@ -3745,7 +3894,7 @@ td = &tree_data; BZERO(td, sizeof(struct tree_data)); - while ((c = getopt(argcnt, args, "xdt:r:o:s:pN")) != EOF) { + while ((c = getopt(argcnt, args, "xdt:r:o:s:S:pN")) != EOF) { switch (c) { case 't': @@ -3799,9 +3948,13 @@ break; case 's': + case 'S': if (td->structname_args++ == 0) hq_open(); hq_enter((ulong)optarg); + td->flags |= (c == 's') ? TREE_PARSE_MEMBER : TREE_READ_MEMBER; + if (count_bits_long(td->flags & (TREE_PARSE_MEMBER|TREE_READ_MEMBER)) > 1) + error(FATAL, "-S and -s options are mutually exclusive\n"); break; case 'p': @@ -4025,6 +4178,7 @@ int i, index; uint print_radix; char pos[BUFSIZE]; + static struct req_entry **e = NULL; if (indexnum != -1) sprintf(pos, "%s/%ld", ppos, indexnum); @@ -4038,10 +4192,23 @@ if (!slot) continue; if (height == 1) { + if (!td->count && td->structname_args) { + /* + * Retrieve all members' info only once (count == 0) + * After last iteration all memory will be freed up + */ + e = (struct req_entry **)GETBUF(sizeof(*e) * + td->structname_args); + for (i = 0; i < td->structname_args; i++) + e[i] = fill_member_offsets(td->structname[i]); + } + if (hq_enter(slot)) td->count++; else - error(FATAL, "\nduplicate tree entry: %lx\n", node_p); + error(FATAL, + "\nduplicate tree entry: radix_tree_node: %lx slots[%d]: %lx\n", + node_p, index, slot); if (td->flags & VERBOSE) fprintf(fp, "%lx\n",slot); @@ -4064,9 +4231,12 @@ dump_struct(td->structname[i], slot, print_radix); break; - default: - dump_struct_members_for_tree(td, i, - slot); + default: + if (td->flags & TREE_PARSE_MEMBER) + dump_struct_members_for_tree(td, i, + slot); + else if (td->flags & TREE_READ_MEMBER) + dump_struct_members_fast(e[i], print_radix, slot); break; } } @@ -4107,10 +4277,22 @@ uint print_radix; ulong struct_p, left_p, right_p; char left_pos[BUFSIZE], right_pos[BUFSIZE]; + static struct req_entry **e; if (!node_p) return; + if (!td->count && td->structname_args) { + /* + * Retrieve all members' info only once (count == 0) + * After last iteration all memory will be freed up + */ + e = (struct req_entry **)GETBUF(sizeof(*e) * + td->structname_args); + for (i = 0; i < td->structname_args; i++) + e[i] = fill_member_offsets(td->structname[i]); + } + if (hq_enter(node_p)) td->count++; else @@ -4138,8 +4320,12 @@ case 0: dump_struct(td->structname[i], struct_p, print_radix); break; - default: - dump_struct_members_for_tree(td, i, struct_p); + default: + if (td->flags & TREE_PARSE_MEMBER) + dump_struct_members_for_tree(td, i, struct_p); + else if (td->flags & TREE_READ_MEMBER) + dump_struct_members_fast(e[i], print_radix, + struct_p); break; } } @@ -5651,6 +5837,22 @@ else return val; } + +uint64_t +swap64(uint64_t val, int swap) +{ + if (swap) + return (((val & 0x00000000000000ffULL) << 56) | + ((val & 0x000000000000ff00ULL) << 40) | + ((val & 0x0000000000ff0000ULL) << 24) | + ((val & 0x00000000ff000000ULL) << 8) | + ((val & 0x000000ff00000000ULL) >> 8) | + ((val & 0x0000ff0000000000ULL) >> 24) | + ((val & 0x00ff000000000000ULL) >> 40) | + ((val & 0xff00000000000000ULL) >> 56)); + else + return val; +} /* * Get a sufficiently large buffer for cpumask. diff -Nru crash-7.1.5/x86_64.c crash-7.1.7/x86_64.c --- crash-7.1.5/x86_64.c 2016-04-27 20:46:50.000000000 +0200 +++ crash-7.1.7/x86_64.c 2016-11-30 19:56:29.000000000 +0100 @@ -126,6 +126,7 @@ x86_64_init(int when) { int len, dim; + char *string; if (XEN_HYPER_MODE()) { x86_64_init_hyper(when); @@ -172,6 +173,10 @@ machdep->get_kvaddr_ranges = x86_64_get_kvaddr_ranges; if (machdep->cmdline_args[0]) parse_cmdline_args(); + if ((string = pc->read_vmcoreinfo("NUMBER(KERNEL_IMAGE_SIZE)"))) { + machdep->machspec->kernel_image_size = dtol(string, QUIET, NULL); + free(string); + } break; case PRE_GDB: @@ -282,6 +287,23 @@ x86_64_calc_phys_base(); break; + case POST_RELOC: + /* + * Check for CONFIG_RANDOMIZE_MEMORY, and set page_offset here. + * The remainder of the virtual address range setups will get + * done below in POST_GDB. + */ + if (kernel_symbol_exists("page_offset_base") && + kernel_symbol_exists("vmalloc_base")) { + machdep->flags |= RANDOMIZED; + readmem(symbol_value("page_offset_base"), KVADDR, + &machdep->machspec->page_offset, sizeof(ulong), + "page_offset_base", FAULT_ON_ERROR); + machdep->kvbase = machdep->machspec->page_offset; + machdep->identity_map_base = machdep->machspec->page_offset; + } + break; + case POST_GDB: if (THIS_KERNEL_VERSION >= LINUX(2,6,26) && THIS_KERNEL_VERSION < LINUX(2,6,31)) { @@ -292,12 +314,40 @@ machdep->machspec->modules_end = MODULES_END_2_6_27; } if (THIS_KERNEL_VERSION >= LINUX(2,6,31)) { - machdep->machspec->vmalloc_start_addr = VMALLOC_START_ADDR_2_6_31; - machdep->machspec->vmalloc_end = VMALLOC_END_2_6_31; - machdep->machspec->vmemmap_vaddr = VMEMMAP_VADDR_2_6_31; - machdep->machspec->vmemmap_end = VMEMMAP_END_2_6_31; - machdep->machspec->modules_vaddr = MODULES_VADDR_2_6_31; - machdep->machspec->modules_end = MODULES_END_2_6_31; + if (machdep->flags & RANDOMIZED) { + readmem(symbol_value("vmalloc_base"), KVADDR, + &machdep->machspec->vmalloc_start_addr, + sizeof(ulong), "vmalloc_base", FAULT_ON_ERROR); + machdep->machspec->vmalloc_end = + machdep->machspec->vmalloc_start_addr + TERABYTES(32) - 1; + if (kernel_symbol_exists("vmemmap_base")) { + readmem(symbol_value("vmemmap_base"), KVADDR, + &machdep->machspec->vmemmap_vaddr, sizeof(ulong), + "vmemmap_base", FAULT_ON_ERROR); + machdep->machspec->vmemmap_end = + machdep->machspec->vmemmap_vaddr + + TERABYTES(1) - 1; + } else { + machdep->machspec->vmemmap_vaddr = VMEMMAP_VADDR_2_6_31; + machdep->machspec->vmemmap_end = VMEMMAP_END_2_6_31; + } + machdep->machspec->modules_vaddr = __START_KERNEL_map + + (machdep->machspec->kernel_image_size ? + machdep->machspec->kernel_image_size : GIGABYTES(1)); + machdep->machspec->modules_end = MODULES_END_2_6_31; + } else { + machdep->machspec->vmalloc_start_addr = VMALLOC_START_ADDR_2_6_31; + machdep->machspec->vmalloc_end = VMALLOC_END_2_6_31; + machdep->machspec->vmemmap_vaddr = VMEMMAP_VADDR_2_6_31; + machdep->machspec->vmemmap_end = VMEMMAP_END_2_6_31; + if ((kt->flags2 & KASLR) && (THIS_KERNEL_VERSION >= LINUX(4,7,0))) + machdep->machspec->modules_vaddr = __START_KERNEL_map + + (machdep->machspec->kernel_image_size ? + machdep->machspec->kernel_image_size : GIGABYTES(1)); + else + machdep->machspec->modules_vaddr = MODULES_VADDR_2_6_31; + machdep->machspec->modules_end = MODULES_END_2_6_31; + } } STRUCT_SIZE_INIT(cpuinfo_x86, "cpuinfo_x86"); /* @@ -614,6 +664,8 @@ fprintf(fp, "%sGART_REGION", others++ ? "|" : ""); if (machdep->flags & NESTED_NMI) fprintf(fp, "%sNESTED_NMI", others++ ? "|" : ""); + if (machdep->flags & RANDOMIZED) + fprintf(fp, "%sRANDOMIZED", others++ ? "|" : ""); fprintf(fp, ")\n"); fprintf(fp, " kvbase: %lx\n", machdep->kvbase); @@ -720,6 +772,7 @@ fprintf(fp, " vmemmap_end: %016lx %s\n", (ulong)ms->vmemmap_end, machdep->flags & VMEMMAP ? "" : "(unused)"); fprintf(fp, " phys_base: %lx\n", (ulong)ms->phys_base); + fprintf(fp, " kernel_image_size: %ldMB\n", ms->kernel_image_size/MEGABYTES(1)); fprintf(fp, " GART_start: %lx\n", ms->GART_start); fprintf(fp, " GART_end: %lx\n", ms->GART_end); fprintf(fp, " pml4: %lx\n", (ulong)ms->pml4); @@ -1081,7 +1134,8 @@ struct syment *boot_sp, *tss_sp, *ist_sp; ms = machdep->machspec; - tss_sp = per_cpu_symbol_search("per_cpu__init_tss"); + if (!(tss_sp = per_cpu_symbol_search("per_cpu__init_tss"))) + tss_sp = per_cpu_symbol_search("per_cpu__cpu_tss"); ist_sp = per_cpu_symbol_search("per_cpu__orig_ist"); x86_64_exception_stacks_init(); @@ -1132,7 +1186,8 @@ "orig_ist array", FAULT_ON_ERROR); for (i = 0; i < MAX_EXCEPTION_STACKS; i++) { - if (ms->stkinfo.ebase[c][i] != estacks[i]) + if (ms->stkinfo.ebase[c][i] && estacks[i] && + (ms->stkinfo.ebase[c][i] != estacks[i])) error(WARNING, "cpu %d %s stack: init_tss: %lx orig_ist: %lx\n", c, ms->stkinfo.exception_stacks[i], @@ -4073,6 +4128,8 @@ if ((flags & EFRAME_PRINT) && BT_REFERENCE_CHECK(bt)) { x86_64_do_bt_reference_check(bt, rip, NULL); + if ((sp = value_search(rip, &offset))) + x86_64_do_bt_reference_check(bt, 0, sp->name); x86_64_do_bt_reference_check(bt, rsp, NULL); x86_64_do_bt_reference_check(bt, cs, NULL); x86_64_do_bt_reference_check(bt, ss, NULL); @@ -4925,24 +4982,25 @@ sprintf(p1, "%s", buf1); - } else if (STREQ(argv[argc-2], "callq") && - hexadecimal(argv[argc-1], 0)) { - /* - * Update module code of the form: - * - * callq 0xffffffffa0017aa0 + } else if ((STREQ(argv[argc-2], "callq") || (argv[argc-2][0] == 'j')) && + hexadecimal(argv[argc-1], 0)) { + /* + * Update code of the form: + * + * callq + * jmp + * jCC * - * to show a bracketed direct call target. - */ - p1 = &LASTCHAR(inbuf); - - if (extract_hex(argv[argc-1], &value, NULLCHAR, TRUE)) { - sprintf(buf1, " <%s>\n", - value_to_symstr(value, buf2, output_radix)); - if (IS_MODULE_VADDR(value) && - !strstr(buf2, "+")) - sprintf(p1, "%s", buf1); - } + * to show a translated, bracketed, target. + */ + p1 = &LASTCHAR(inbuf); + + if (extract_hex(argv[argc-1], &value, NULLCHAR, TRUE)) { + sprintf(buf1, " <%s>\n", + value_to_symstr(value, buf2, output_radix)); + if (!strstr(buf1, "<>")) + sprintf(p1, "%s", buf1); + } } if (value_symbol(vaddr) && @@ -5578,19 +5636,23 @@ char buf1[BUFSIZE]; char search_string1[BUFSIZE]; char search_string2[BUFSIZE]; + char search_string3[BUFSIZE]; int found; max_instructions = end - start; found = FALSE; + search_string1[0] = search_string2[0] = search_string3[0] = NULLCHAR; sprintf(buf1, "x/%ldi 0x%lx", max_instructions, start); + if (symbol_exists("__switch_to")) { sprintf(search_string1, "callq 0x%lx", symbol_value("__switch_to")); sprintf(search_string2, "call 0x%lx", symbol_value("__switch_to")); - } else { - search_string1[0] = NULLCHAR; - search_string2[0] = NULLCHAR; + } + if (symbol_exists("__switch_to_asm")) { + sprintf(search_string3, + "callq 0x%lx", symbol_value("__switch_to_asm")); } open_tmpfile(); @@ -5608,6 +5670,8 @@ found = TRUE; if (strlen(search_string2) && strstr(buf1, search_string2)) found = TRUE; + if (strlen(search_string3) && strstr(buf1, search_string3)) + found = TRUE; } close_tmpfile(); @@ -6054,7 +6118,7 @@ } } - if (ACTIVE()) { + if (LOCAL_ACTIVE()) { if ((iomem = fopen("/proc/iomem", "r")) == NULL) return; @@ -6148,11 +6212,16 @@ return; } +#define IN_KERNEL_REGION(vaddr) \ + (((vaddr) >= __START_KERNEL_map) && \ + ((vaddr) < (__START_KERNEL_map + machdep->machspec->kernel_image_size))) + if ((vd = get_kdump_vmcore_data())) { for (i = 0; i < vd->num_pt_load_segments; i++) { phdr = vd->load64 + i; if ((phdr->p_vaddr >= __START_KERNEL_map) && - !(IS_VMALLOC_ADDR(phdr->p_vaddr))) { + (IN_KERNEL_REGION(phdr->p_vaddr) || + !(IS_VMALLOC_ADDR(phdr->p_vaddr)))) { machdep->machspec->phys_base = phdr->p_paddr - (phdr->p_vaddr & ~(__START_KERNEL_map));