diff -Nru crash-7.1.4/arm64.c crash-7.2.3+real/arm64.c --- crash-7.1.4/arm64.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/arm64.c 2018-05-17 17:39:38.000000000 +0000 @@ -1,8 +1,8 @@ /* * arm64.c - core analysis suite * - * Copyright (C) 2012-2015 David Anderson - * Copyright (C) 2012-2015 Red Hat, Inc. All rights reserved. + * Copyright (C) 2012-2018 David Anderson + * Copyright (C) 2012-2018 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 @@ -20,12 +20,14 @@ #include "defs.h" #include #include +#include #define NOT_IMPLEMENTED(X) error((X), "%s: function not implemented\n", __func__) static struct machine_specific arm64_machine_specific = { 0 }; static int arm64_verify_symbol(const char *, ulong, char); static void arm64_parse_cmdline_args(void); +static void arm64_calc_kimage_voffset(void); static void arm64_calc_phys_offset(void); static void arm64_calc_virtual_memory_ranges(void); static int arm64_kdump_phys_base(ulong *); @@ -34,22 +36,33 @@ static int arm64_kvtop(struct task_context *, ulong, physaddr_t *, int); static int arm64_uvtop(struct task_context *, ulong, physaddr_t *, int); 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 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_print_exception_frame(struct bt_info *, ulong, int, FILE *ofp); +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); static ulong arm64_vmalloc_start(void); @@ -59,7 +72,11 @@ static void arm64_display_machine_stats(void); static int arm64_get_smp_cpus(void); static void arm64_clear_machdep_cache(void); +static int arm64_on_process_stack(struct bt_info *, ulong); static int arm64_in_alternate_stack(int, ulong); +static int arm64_on_irq_stack(int, ulong); +static void arm64_set_irq_stack(struct bt_info *); +static void arm64_set_process_stack(struct bt_info *); static int arm64_get_kvaddr_ranges(struct vaddr_range *); static int arm64_get_crash_notes(void); static void arm64_calc_VA_BITS(void); @@ -74,6 +91,7 @@ arm64_init(int when) { ulong value; + char *string; struct machine_specific *ms; #if defined(__x86_64__) @@ -95,9 +113,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 @@ -127,8 +172,14 @@ if (!machdep->pagesize && kernel_symbol_exists("swapper_pg_dir") && kernel_symbol_exists("idmap_pg_dir")) { - value = symbol_value("swapper_pg_dir") - - symbol_value("idmap_pg_dir"); + if (kernel_symbol_exists("tramp_pg_dir")) + value = symbol_value("tramp_pg_dir"); + else if (kernel_symbol_exists("reserved_ttbr0")) + value = symbol_value("reserved_ttbr0"); + else + value = symbol_value("swapper_pg_dir"); + + value -= symbol_value("idmap_pg_dir"); /* * idmap_pg_dir is 2 pages prior to 4.1, * and 3 pages thereafter. Only 4K and 64K @@ -153,44 +204,86 @@ 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; + + arm64_calc_kimage_voffset(); + } 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: - machdep->flags |= VM_L2_64K; - machdep->ptrs_per_pgd = PTRS_PER_PGD_L2_64K; - if ((machdep->pgd = - (char *)malloc(PTRS_PER_PGD_L2_64K * 8)) == NULL) - error(FATAL, "cannot malloc pgd space."); - if ((machdep->ptbl = - (char *)malloc(PTRS_PER_PTE_L2_64K * 8)) == NULL) - error(FATAL, "cannot malloc ptbl space."); - machdep->pmd = NULL; /* not used */ + if (machdep->machspec->VA_BITS > PGDIR_SHIFT_L3_64K) { + machdep->flags |= VM_L3_64K; + machdep->ptrs_per_pgd = PTRS_PER_PGD_L3_64K; + if ((machdep->pgd = + (char *)malloc(PTRS_PER_PGD_L3_64K * 8)) == NULL) + error(FATAL, "cannot malloc pgd space."); + if ((machdep->pmd = + (char *)malloc(PTRS_PER_PMD_L3_64K * 8)) == NULL) + error(FATAL, "cannot malloc pmd space."); + if ((machdep->ptbl = + (char *)malloc(PTRS_PER_PTE_L3_64K * 8)) == NULL) + error(FATAL, "cannot malloc ptbl space."); + } else { + machdep->flags |= VM_L2_64K; + machdep->ptrs_per_pgd = PTRS_PER_PGD_L2_64K; + if ((machdep->pgd = + (char *)malloc(PTRS_PER_PGD_L2_64K * 8)) == NULL) + error(FATAL, "cannot malloc pgd space."); + if ((machdep->ptbl = + (char *)malloc(PTRS_PER_PTE_L2_64K * 8)) == NULL) + error(FATAL, "cannot malloc ptbl space."); + machdep->pmd = NULL; /* not used */ + } machdep->pud = NULL; /* not used */ break; @@ -202,8 +295,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; @@ -211,8 +304,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; @@ -241,12 +332,27 @@ 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: arm64_calc_virtual_memory_ranges(); machdep->section_size_bits = _SECTION_SIZE_BITS; - machdep->max_physmem_bits = _MAX_PHYSMEM_BITS; + if (THIS_KERNEL_VERSION >= LINUX(3,17,0)) + machdep->max_physmem_bits = _MAX_PHYSMEM_BITS_3_17; + else + machdep->max_physmem_bits = _MAX_PHYSMEM_BITS; ms = machdep->machspec; if (THIS_KERNEL_VERSION >= LINUX(4,0,0)) { @@ -297,6 +403,7 @@ if (!machdep->hz) machdep->hz = 100; + arm64_irq_stack_init(); arm64_stackframe_init(); break; @@ -335,6 +442,12 @@ if ((type == 'A') && STREQ(name, "_kernel_flags_le")) machdep->machspec->kernel_flags = le64toh(value); + if ((type == 'A') && STREQ(name, "_kernel_flags_le_hi32")) + machdep->machspec->kernel_flags |= ((ulong)le32toh(value) << 32); + + if ((type == 'A') && STREQ(name, "_kernel_flags_le_lo32")) + machdep->machspec->kernel_flags |= le32toh(value); + if (((type == 'A') || (type == 'a')) && (highest_bit_long(value) != 63)) return FALSE; @@ -348,6 +461,9 @@ if ((type == 'A') && STRNEQ(name, "__crc_")) return FALSE; + if ((type == 'N') && strstr(name, "$d")) + return FALSE; + if (!(machdep->flags & KSYMS_START) && STREQ(name, "idmap_pg_dir")) machdep->flags |= KSYMS_START; @@ -369,14 +485,24 @@ fprintf(fp, "%sPHYS_OFFSET", others++ ? "|" : ""); if (machdep->flags & VM_L2_64K) fprintf(fp, "%sVM_L2_64K", others++ ? "|" : ""); + if (machdep->flags & VM_L3_64K) + 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) fprintf(fp, "%sKDUMP_ENABLED", others++ ? "|" : ""); + if (machdep->flags & IRQ_STACKS) + fprintf(fp, "%sIRQ_STACKS", others++ ? "|" : ""); + if (machdep->flags & UNW_4_14) + fprintf(fp, "%sUNW_4_14", 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); @@ -393,15 +519,24 @@ 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" : "arm64_vtop_2level_64k"); + "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" : "arm64_vtop_2level_64k"); + "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"); fprintf(fp, " dump_irq: generic_dump_irq()\n"); fprintf(fp, " get_stack_frame: arm64_get_stack_frame()\n"); @@ -432,8 +567,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"); @@ -442,6 +582,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); @@ -467,10 +608,23 @@ 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, " user_eframe_offset: %ld\n", ms->user_eframe_offset); + fprintf(fp, " kern_eframe_offset: %ld\n", ms->kern_eframe_offset); fprintf(fp, " PTE_PROT_NONE: %lx\n", ms->PTE_PROT_NONE); fprintf(fp, " PTE_FILE: "); if (ms->PTE_FILE) @@ -491,13 +645,61 @@ fprintf(fp, "%lx\n", ms->__SWP_OFFSET_MASK); else fprintf(fp, "(unused)\n"); + fprintf(fp, " machine_kexec_start: %lx\n", ms->machine_kexec_start); + fprintf(fp, " machine_kexec_end: %lx\n", ms->machine_kexec_end); fprintf(fp, " crash_kexec_start: %lx\n", ms->crash_kexec_start); fprintf(fp, " crash_kexec_end: %lx\n", ms->crash_kexec_end); 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, " 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++) + fprintf(fp, " irq_stacks[%d]: %lx\n", + i, ms->irq_stacks[i]); + } else { + fprintf(fp, " irq_stack_size: (unused)\n"); + fprintf(fp, " irq_stacks: (unused)\n"); + } } +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. @@ -509,11 +711,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]) @@ -535,47 +736,86 @@ c = parse_line(buf, arglist); for (i = 0; i < c; i++) { - err = 0; + 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; + } - 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; - } + error(WARNING, "ignoring --machdep option: %s\n", + arglist[i]); + } + } +} - p = arglist[i] + strlen("phys_offset="); - if (strlen(p)) { - if (megabytes) - value = dtol(p, flags, &err); - else - value = htol(p, flags, &err); - } +static void +arm64_calc_kimage_voffset(void) +{ + struct machine_specific *ms = machdep->machspec; + ulong phys_addr; - if (!err) { - if (megabytes) - value = MEGABYTES(value); + if (ms->kimage_voffset) /* vmcoreinfo, ioctl, or --machdep override */ + return; - machdep->machspec->phys_offset = value; + if (ACTIVE()) { + char buf[BUFSIZE]; + char *p1; + int errflag; + FILE *iomem; - error(NOTE, - "setting phys_offset to: 0x%lx\n\n", - machdep->machspec->phys_offset); + if ((iomem = fopen("/proc/iomem", "r")) == NULL) + return; - machdep->flags |= PHYS_OFFSET; - continue; - } + errflag = 1; + while (fgets(buf, BUFSIZE, iomem)) { + if(strstr(buf, ": Kernel code")) { + errflag = 0; + break; } + if (strstr(buf, ": System RAM")) { + clean_line(buf); - error(WARNING, "ignoring --machdep option: %s\n", - arglist[i]); + if (!(p1 = strstr(buf, "-"))) + continue; + + *p1 = NULLCHAR; + + phys_addr = htol(buf, RETURN_ON_ERROR | QUIET, NULL); + if (phys_addr == BADADDR) + continue; + } } + fclose(iomem); + + if (errflag) + return; + + } else if (KDUMP_DUMPFILE()) + arm_kdump_phys_base(&phys_addr); /* Get start address of first memory block */ + else { + error(WARNING, + "kimage_voffset cannot be determined from the dumpfile.\n"); + error(CONT, + "Using default value of 0. If this is not correct, then try\n"); + error(CONT, + "using the command line option: --machdep kimage_voffset=\n"); + return; } -} + ms->kimage_voffset = ms->vmalloc_start_addr - phys_addr; + + if ((kt->flags2 & KASLR) && (kt->flags & RELOC_SET)) + ms->kimage_voffset += (kt->relocate * -1); +} static void arm64_calc_phys_offset(void) @@ -596,9 +836,24 @@ 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"))) { + if (pc->flags & PROC_KCORE) + paddr = KCORE_USE_VADDR; + else + 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; /* @@ -606,14 +861,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; @@ -647,11 +902,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); } @@ -676,6 +951,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) { @@ -698,12 +991,16 @@ kernel_pgd = vt->kernel_pgd[0]; *paddr = 0; - switch (machdep->flags & (VM_L2_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); + case VM_L3_64K: + 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; } @@ -719,12 +1016,16 @@ *paddr = 0; - switch (machdep->flags & (VM_L2_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); + case VM_L3_64K: + 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; } @@ -799,6 +1100,78 @@ return FALSE; } +static int +arm64_vtop_3level_64k(ulong pgd, ulong vaddr, physaddr_t *paddr, int verbose) +{ + ulong *pgd_base, *pgd_ptr, pgd_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_L3_64K * sizeof(ulong)); + pgd_ptr = pgd_base + (((vaddr) >> PGDIR_SHIFT_L3_64K) & (PTRS_PER_PGD_L3_64K - 1)); + 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) + goto no_page; + + /* + * #define __PAGETABLE_PUD_FOLDED + */ + + pmd_base = (ulong *)PTOV(pgd_val & PHYS_MASK & (s32)machdep->pagemask); + FILL_PMD(pmd_base, KVADDR, PTRS_PER_PMD_L3_64K * sizeof(ulong)); + pmd_ptr = pmd_base + (((vaddr) >> PMD_SHIFT_L3_64K) & (PTRS_PER_PMD_L3_64K - 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_512MB) & PHYS_MASK; + if (verbose) { + fprintf(fp, " PAGE: %lx (512MB)\n\n", sectionbase); + arm64_translate_pte(pmd_val, 0, 0); + } + *paddr = sectionbase + (vaddr & ~SECTION_PAGE_MASK_512MB); + return TRUE; + } + + pte_base = (ulong *)PTOV(pmd_val & PHYS_MASK & (s32)machdep->pagemask); + FILL_PTBL(pte_base, KVADDR, PTRS_PER_PTE_L3_64K * sizeof(ulong)); + pte_ptr = pte_base + (((vaddr) >> machdep->pageshift) & (PTRS_PER_PTE_L3_64K - 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 int arm64_vtop_3level_4k(ulong pgd, ulong vaddr, physaddr_t *paddr, int verbose) { @@ -871,6 +1244,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) { @@ -891,6 +1343,93 @@ return 0; }; +/* + * Gather IRQ stack values. + */ +static void +arm64_irq_stack_init(void) +{ + int i; + struct syment *sp; + struct gnu_request request, *req; + struct machine_specific *ms = machdep->machspec; + ulong p, sz; + req = &request; + + if (symbol_exists("irq_stack") && + (sp = per_cpu_symbol_search("irq_stack")) && + get_symbol_type("irq_stack", NULL, req)) { + /* before v4.14 or CONFIG_VMAP_STACK disabled */ + if (CRASHDEBUG(1)) { + fprintf(fp, "irq_stack: \n"); + fprintf(fp, " type: %s\n", + (req->typecode == TYPE_CODE_ARRAY) ? + "TYPE_CODE_ARRAY" : "other"); + fprintf(fp, " target_typecode: %s\n", + req->target_typecode == TYPE_CODE_INT ? + "TYPE_CODE_INT" : "other"); + fprintf(fp, " target_length: %ld\n", + req->target_length); + fprintf(fp, " length: %ld\n", req->length); + } + + if (!(ms->irq_stacks = (ulong *)malloc((size_t)(kt->cpus * sizeof(ulong))))) + error(FATAL, "cannot malloc irq_stack addresses\n"); + ms->irq_stack_size = req->length; + machdep->flags |= IRQ_STACKS; + + for (i = 0; i < kt->cpus; i++) + ms->irq_stacks[i] = kt->__per_cpu_offset[i] + sp->value; + } else if (symbol_exists("irq_stack_ptr") && + (sp = per_cpu_symbol_search("irq_stack_ptr")) && + get_symbol_type("irq_stack_ptr", NULL, req)) { + /* v4.14 and later with CONFIG_VMAP_STACK enabled */ + if (CRASHDEBUG(1)) { + fprintf(fp, "irq_stack_ptr: \n"); + fprintf(fp, " type: %x, %s\n", + (int)req->typecode, + (req->typecode == TYPE_CODE_PTR) ? + "TYPE_CODE_PTR" : "other"); + fprintf(fp, " target_typecode: %x, %s\n", + (int)req->target_typecode, + req->target_typecode == TYPE_CODE_INT ? + "TYPE_CODE_INT" : "other"); + fprintf(fp, " target_length: %ld\n", + req->target_length); + fprintf(fp, " length: %ld\n", req->length); + } + + if (!(ms->irq_stacks = (ulong *)malloc((size_t)(kt->cpus * sizeof(ulong))))) + error(FATAL, "cannot malloc irq_stack addresses\n"); + + /* + * Determining the IRQ_STACK_SIZE is tricky, but for now + * 4.14 kernel has: + * + * #define IRQ_STACK_SIZE THREAD_SIZE + * + * and finding a solid usage of THREAD_SIZE is hard, but: + * + * union thread_union { + * ... + * unsigned long stack[THREAD_SIZE/sizeof(long)]; + * }; + */ + if (MEMBER_EXISTS("thread_union", "stack")) { + if ((sz = MEMBER_SIZE("thread_union", "stack")) > 0) + ms->irq_stack_size = sz; + } else + ms->irq_stack_size = ARM64_IRQ_STACK_SIZE; + + machdep->flags |= IRQ_STACKS; + + for (i = 0; i < kt->cpus; i++) { + p = kt->__per_cpu_offset[i] + sp->value; + readmem(p, KVADDR, &(ms->irq_stacks[i]), sizeof(ulong), + "IRQ stack pointer", RETURN_ON_ERROR); + } + } +} /* * Gather and verify all of the backtrace requirements. @@ -901,26 +1440,52 @@ long task_struct_thread; long thread_struct_cpu_context; long context_sp, context_pc, context_fp; - struct syment *sp1, *sp1n, *sp2, *sp2n; + struct syment *sp1, *sp1n, *sp2, *sp2n, *sp3, *sp3n; STRUCT_SIZE_INIT(note_buf, "note_buf_t"); STRUCT_SIZE_INIT(elf_prstatus, "elf_prstatus"); MEMBER_OFFSET_INIT(elf_prstatus_pr_pid, "elf_prstatus", "pr_pid"); MEMBER_OFFSET_INIT(elf_prstatus_pr_reg, "elf_prstatus", "pr_reg"); + if (MEMBER_EXISTS("pt_regs", "stackframe")) { + machdep->machspec->user_eframe_offset = SIZE(pt_regs); + machdep->machspec->kern_eframe_offset = SIZE(pt_regs) - 16; + } else { + machdep->machspec->user_eframe_offset = SIZE(pt_regs) + 16; + machdep->machspec->kern_eframe_offset = SIZE(pt_regs); + } + machdep->machspec->__exception_text_start = 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)) && (sp2 = kernel_symbol_search("crash_save_cpu")) && - (sp2n = next_symbol(NULL, sp2))) { + (sp2n = next_symbol(NULL, sp2)) && + (sp3 = kernel_symbol_search("machine_kexec")) && + (sp3n = next_symbol(NULL, sp3))) { machdep->machspec->crash_kexec_start = sp1->value; machdep->machspec->crash_kexec_end = sp1n->value; machdep->machspec->crash_save_cpu_start = sp2->value; machdep->machspec->crash_save_cpu_end = sp2n->value; + machdep->machspec->machine_kexec_start = sp3->value; + machdep->machspec->machine_kexec_end = sp3n->value; machdep->flags |= KDUMP_ENABLED; } @@ -939,19 +1504,21 @@ */ if (offsetof(struct arm64_stackframe, sp) != MEMBER_OFFSET("stackframe", "sp")) { - error(INFO, "builtin stackframe.sp offset incorrect!\n"); - return; + if (CRASHDEBUG(1)) + error(INFO, "builtin stackframe.sp offset differs from kernel version\n"); } if (offsetof(struct arm64_stackframe, fp) != MEMBER_OFFSET("stackframe", "fp")) { - error(INFO, "builtin stackframe.fp offset incorrect!\n"); - return; + if (CRASHDEBUG(1)) + error(INFO, "builtin stackframe.fp offset differs from kernel version\n"); } if (offsetof(struct arm64_stackframe, pc) != MEMBER_OFFSET("stackframe", "pc")) { - error(INFO, "builtin stackframe.pc offset incorrect!\n"); - return; + if (CRASHDEBUG(1)) + error(INFO, "builtin stackframe.pc offset differs from kernel version\n"); } + if (!MEMBER_EXISTS("stackframe", "sp")) + machdep->flags |= UNW_4_14; context_sp = MEMBER_OFFSET("cpu_context", "sp"); context_fp = MEMBER_OFFSET("cpu_context", "fp"); @@ -979,7 +1546,8 @@ #define KERNEL_MODE (1) #define USER_MODE (2) -#define USER_EFRAME_OFFSET (304) +#define USER_EFRAME_OFFSET (machdep->machspec->user_eframe_offset) +#define KERN_EFRAME_OFFSET (machdep->machspec->kern_eframe_offset) /* * PSR bits @@ -993,6 +1561,25 @@ #define PSR_MODE_EL3h 0x0000000d #define PSR_MODE_MASK 0x0000000f +/* Architecturally defined mapping between AArch32 and AArch64 registers */ +#define compat_usr(x) regs[(x)] +#define compat_fp regs[11] +#define compat_sp regs[13] +#define compat_lr regs[14] + +#define user_mode(ptregs) \ + (((ptregs)->pstate & PSR_MODE_MASK) == PSR_MODE_EL0t) + +#define compat_user_mode(ptregs) \ + (((ptregs)->pstate & (PSR_MODE32_BIT | PSR_MODE_MASK)) == \ + (PSR_MODE32_BIT | PSR_MODE_EL0t)) + +#define user_stack_pointer(ptregs) \ + (!compat_user_mode(ptregs) ? (ptregs)->sp : (ptregs)->compat_sp) + +#define user_frame_pointer(ptregs) \ + (!compat_user_mode(ptregs) ? (ptregs)->regs[29] : (ptregs)->compat_fp) + static int arm64_is_kernel_exception_frame(struct bt_info *bt, ulong stkptr) { @@ -1008,6 +1595,8 @@ { case PSR_MODE_EL1t: case PSR_MODE_EL1h: + case PSR_MODE_EL2t: + case PSR_MODE_EL2h: return TRUE; } } @@ -1018,7 +1607,45 @@ static int arm64_eframe_search(struct bt_info *bt) { + int c; ulong ptr, count; + struct machine_specific *ms; + + if (bt->flags & BT_EFRAME_SEARCH2) { + if (!(machdep->flags & IRQ_STACKS)) + error(FATAL, "IRQ stacks do not exist in this kernel\n"); + + ms = machdep->machspec; + + for (c = 0; c < kt->cpus; c++) { + if ((bt->flags & BT_CPUMASK) && + !(NUM_IN_BITMAP(bt->cpumask, c))) + continue; + + fprintf(fp, "CPU %d IRQ STACK:", c); + bt->stackbase = ms->irq_stacks[c]; + bt->stacktop = bt->stackbase + ms->irq_stack_size; + alter_stackbuf(bt); + count = 0; + + for (ptr = bt->stackbase; ptr < bt->stacktop - SIZE(pt_regs); ptr++) { + if (arm64_is_kernel_exception_frame(bt, ptr)) { + fprintf(fp, "%s\nKERNEL-MODE EXCEPTION FRAME AT: %lx\n", + count ? "" : "\n", ptr); + arm64_print_exception_frame(bt, ptr, KERNEL_MODE, fp); + count++; + } + } + + if (count) + fprintf(fp, "\n"); + else + fprintf(fp, "(none found)\n\n"); + } + + return 0; + } + count = 0; for (ptr = bt->stackbase; ptr < bt->stacktop - SIZE(pt_regs); ptr++) { @@ -1045,8 +1672,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) @@ -1057,39 +1705,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); } @@ -1101,6 +1762,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) { @@ -1112,8 +1826,21 @@ if (bt->frameptr == sp) return; - if (!INSTACK(sp, bt) || !INSTACK(bt->frameptr, bt)) + if (INSTACK(bt->frameptr, bt)) { + if (INSTACK(sp, bt)) { + ; /* normal case */ + } else { + if (sp == 0) + /* interrupt in user mode */ + sp = bt->stacktop - USER_EFRAME_OFFSET; + else + /* interrupt in kernel mode */ + sp = bt->stacktop; + } + } else { + /* This is a transition case from irq to process stack. */ return; + } words = (sp - bt->frameptr) / sizeof(ulong); @@ -1131,12 +1858,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; + struct arm64_pt_regs *ptregs; + struct machine_specific *ms; + stack_mask = (unsigned long)(ARM64_STACK_SIZE) - 1; fp = frame->fp; @@ -1146,13 +1938,388 @@ if (fp < low || fp > high || fp & 0xf) return FALSE; - frame->sp = fp + 0x10; - frame->fp = GET_STACK_ULONG(fp); - frame->pc = GET_STACK_ULONG(fp + 8); + frame->sp = fp + 0x10; + frame->fp = GET_STACK_ULONG(fp); + frame->pc = GET_STACK_ULONG(fp + 8); + + if ((frame->fp == 0) && (frame->pc == 0)) + return FALSE; + + if (!(machdep->flags & IRQ_STACKS)) + return TRUE; + + if (!(machdep->flags & IRQ_STACKS)) + return TRUE; + + if (machdep->flags & UNW_4_14) { + if ((bt->flags & BT_IRQSTACK) && + !arm64_on_irq_stack(bt->tc->processor, frame->fp)) { + if (arm64_on_process_stack(bt, frame->fp)) { + arm64_set_process_stack(bt); + + frame->sp = frame->fp - KERN_EFRAME_OFFSET; + /* + * for switch_stack + * fp still points to irq stack + */ + bt->bptr = fp; + /* + * for display_full_frame + * sp points to process stack + * + * If we want to see pt_regs, + * comment out the below. + * bt->frameptr = frame->sp; + */ + } else { + /* irq -> user */ + return FALSE; + } + } + + return TRUE; + } + + /* + * The kernel's manner of determining the end of the IRQ stack: + * + * #define THREAD_SIZE 16384 + * #define THREAD_START_SP (THREAD_SIZE - 16) + * #define IRQ_STACK_START_SP THREAD_START_SP + * #define IRQ_STACK_PTR(cpu) ((unsigned long)per_cpu(irq_stack, cpu) + IRQ_STACK_START_SP) + * #define IRQ_STACK_TO_TASK_STACK(ptr) (*((unsigned long *)((ptr) - 0x08))) + * + * irq_stack_ptr = IRQ_STACK_PTR(raw_smp_processor_id()); + * orig_sp = IRQ_STACK_TO_TASK_STACK(irq_stack_ptr); (pt_regs pointer on process stack) + */ + ms = machdep->machspec; + irq_stack_ptr = ms->irq_stacks[bt->tc->processor] + ms->irq_stack_size - 16; + + 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) || (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 = fp; + if (CRASHDEBUG(1)) + error(INFO, + "arm64_unwind_frame: switch stacks: fp: %lx sp: %lx pc: %lx\n", + frame->fp, frame->sp, frame->pc); + } else { + error(WARNING, + "arm64_unwind_frame: on IRQ stack: oriq_sp: %lx%s fp: %lx%s\n", + orig_sp, INSTACK(orig_sp, bt) ? "" : " (?)", + frame->fp, INSTACK(frame->fp, bt) ? "" : " (?)"); + return FALSE; + } + } + + 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) { @@ -1161,6 +2328,16 @@ ulong exception_frame; FILE *ofp; + if (bt->flags & BT_OPT_BACK_TRACE) { + if (machdep->flags & UNW_4_14) { + option_not_supported('o'); + return; + } + + arm64_back_trace_cmd_v2(bt); + return; + } + ofp = BT_REFERENCE_CHECK(bt) ? pc->nullfp : fp; /* @@ -1172,17 +2349,29 @@ */ 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 - 8); stackframe.pc = GET_STACK_ULONG(bt->bptr); stackframe.sp = bt->bptr + 8; bt->frameptr = stackframe.sp; } else if (bt->hp && bt->hp->esp) { + if (arm64_on_irq_stack(bt->tc->processor, bt->hp->esp)) { + arm64_set_irq_stack(bt); + bt->flags |= BT_IRQSTACK; + } stackframe.fp = GET_STACK_ULONG(bt->hp->esp - 8); stackframe.pc = bt->hp->eip ? bt->hp->eip : GET_STACK_ULONG(bt->hp->esp); stackframe.sp = bt->hp->esp + 8; bt->flags &= ~BT_REGS_NOT_FOUND; } else { + if (arm64_on_irq_stack(bt->tc->processor, bt->frameptr)) { + arm64_set_irq_stack(bt); + bt->flags |= BT_IRQSTACK; + } stackframe.sp = bt->stkptr; stackframe.pc = bt->instptr; stackframe.fp = bt->frameptr; @@ -1206,7 +2395,7 @@ goto complete_user; if (DUMPFILE() && is_task_active(bt->task)) { - exception_frame = stackframe.fp - SIZE(pt_regs); + exception_frame = stackframe.fp - KERN_EFRAME_OFFSET; if (arm64_is_kernel_exception_frame(bt, exception_frame)) arm64_print_exception_frame(bt, exception_frame, KERNEL_MODE, ofp); @@ -1235,8 +2424,21 @@ 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)) { + if (arm64_is_kernel_exception_frame(bt, stackframe.fp - KERN_EFRAME_OFFSET)) + exception_frame = stackframe.fp - KERN_EFRAME_OFFSET; + } + } + + if ((bt->flags & BT_IRQSTACK) && + !arm64_on_irq_stack(bt->tc->processor, stackframe.fp)) { + bt->flags &= ~BT_IRQSTACK; + if (arm64_switch_stack(bt, &stackframe, ofp) == USER_MODE) + break; + } + level++; } @@ -1251,6 +2453,137 @@ 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 { + if (arm64_on_irq_stack(bt->tc->processor, bt->frameptr)) { + arm64_set_irq_stack(bt); + bt->flags |= BT_IRQSTACK; + } + 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) { @@ -1301,6 +2634,7 @@ { ulong *ptr, *start, *base; struct machine_specific *ms; + ulong crash_kexec_frame; if (!(machdep->flags & KDUMP_ENABLED)) return FALSE; @@ -1315,25 +2649,178 @@ start = (ulong *)&bt->stackbuf[(ulong)(STACK_OFFSET_TYPE(bt->stacktop))]; } + crash_kexec_frame = 0; 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 (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) + + task_to_stackbase(bt->tc->task); + 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) + + task_to_stackbase(bt->tc->task); + if (CRASHDEBUG(1)) + fprintf(fp, "%lx: %lx (crash_save_cpu)\n", bt->bptr, *ptr); + return TRUE; + } + } else { + if ((*ptr >= ms->machine_kexec_start) && (*ptr < ms->machine_kexec_end)) { + bt->bptr = ((ulong)ptr - (ulong)base) + + task_to_stackbase(bt->tc->task); + if (CRASHDEBUG(1)) + fprintf(fp, "%lx: %lx (machine_kexec)\n", bt->bptr, *ptr); + return TRUE; + } + if ((*ptr >= ms->crash_kexec_start) && (*ptr < ms->crash_kexec_end)) { + /* + * Stash the first crash_kexec frame in case the machine_kexec + * frame is not found. + */ + if (!crash_kexec_frame) { + crash_kexec_frame = ((ulong)ptr - (ulong)base) + + task_to_stackbase(bt->tc->task); + if (CRASHDEBUG(1)) + fprintf(fp, "%lx: %lx (crash_kexec)\n", + bt->bptr, *ptr); + } + continue; + } + if ((*ptr >= ms->crash_save_cpu_start) && (*ptr < ms->crash_save_cpu_end)) { + bt->bptr = ((ulong)ptr - (ulong)base) + + task_to_stackbase(bt->tc->task); + if (CRASHDEBUG(1)) + fprintf(fp, "%lx: %lx (crash_save_cpu)\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 (crash_kexec_frame) { + bt->bptr = crash_kexec_frame; + return TRUE; + } + + return FALSE; +} + +static int +arm64_in_kdump_text_on_irq_stack(struct bt_info *bt) +{ + int cpu; + ulong stackbase; + char *stackbuf; + ulong *ptr, *start, *base; + struct machine_specific *ms; + + if ((machdep->flags & (IRQ_STACKS|KDUMP_ENABLED)) != (IRQ_STACKS|KDUMP_ENABLED)) + return FALSE; + + ms = machdep->machspec; + cpu = bt->tc->processor; + stackbase = ms->irq_stacks[cpu]; + stackbuf = GETBUF(ms->irq_stack_size); + + if (!readmem(stackbase, KVADDR, stackbuf, + ms->irq_stack_size, "IRQ stack contents", RETURN_ON_ERROR)) { + error(INFO, "read of IRQ stack at %lx failed\n", stackbase); + FREEBUF(stackbuf); + return FALSE; + } + + base = (ulong *)stackbuf; + start = (ulong *)(stackbuf + ms->irq_stack_size); + + for (ptr = start - 8; ptr >= base; ptr--) { + 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; + } } } + FREEBUF(stackbuf); return FALSE; } +static int +arm64_switch_stack(struct bt_info *bt, struct arm64_stackframe *frame, FILE *ofp) +{ + int i; + ulong stacktop, words, addr; + ulong *stackbuf; + char buf[BUFSIZE]; + struct machine_specific *ms = machdep->machspec; + + if (bt->flags & BT_FULL) { + stacktop = ms->irq_stacks[bt->tc->processor] + ms->irq_stack_size; + words = (stacktop - bt->bptr) / sizeof(ulong); + stackbuf = (ulong *)GETBUF(words * sizeof(ulong)); + readmem(bt->bptr, KVADDR, stackbuf, words * sizeof(long), + "top of IRQ stack", FAULT_ON_ERROR); + + addr = bt->bptr; + for (i = 0; i < words; i++) { + if (!(i & 1)) + fprintf(ofp, "%s %lx: ", i ? "\n" : "", addr); + fprintf(ofp, "%s ", format_stack_entry(bt, buf, stackbuf[i], 0)); + addr += sizeof(ulong); + } + fprintf(ofp, "\n"); + FREEBUF(stackbuf); + } + fprintf(ofp, "--- ---\n"); + + if (frame->fp == 0) + return USER_MODE; + + if (!(machdep->flags & UNW_4_14)) + arm64_print_exception_frame(bt, frame->sp, KERNEL_MODE, ofp); + + return KERNEL_MODE; +} + static int arm64_get_dumpfile_stackframe(struct bt_info *bt, struct arm64_stackframe *frame) { @@ -1348,15 +2835,30 @@ } ptregs = &ms->panic_task_regs[bt->tc->processor]; - frame->sp = ptregs->sp; frame->pc = ptregs->pc; - frame->fp = ptregs->regs[29]; - - if (!is_kernel_text(frame->pc) && - in_user_stack(bt->tc->task, frame->sp)) + if (user_mode(ptregs)) { + frame->sp = user_stack_pointer(ptregs); + frame->fp = user_frame_pointer(ptregs); + if (is_kernel_text(frame->pc) || + !in_user_stack(bt->tc->task, frame->sp)) { + error(WARNING, + "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]; + } - if (arm64_in_kdump_text(bt, frame)) + if (arm64_in_kdump_text(bt, frame) || + arm64_in_kdump_text_on_irq_stack(bt)) bt->flags |= BT_KDUMP_ADJUST; return TRUE; @@ -1399,6 +2901,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; @@ -1407,10 +2928,11 @@ ulong LR, SP, offset; char buf[BUFSIZE]; - if (CRASHDEBUG(1)) + 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]; @@ -1473,18 +2995,21 @@ i < 10 ? " " : "", i); fprintf(ofp, is_64_bit ? "%016lx" : "%08lx", (ulong)regs->regs[i]); - if ((i == 0) || ((r % rows) == 0)) - fprintf(ofp, "\n "); + if ((i == 0) && !is_64_bit) + fprintf(ofp, "\n"); + else if ((i == 0) || ((r % rows) == 0)) + 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)) { @@ -1495,6 +3020,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); @@ -1659,6 +3186,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 */ @@ -1708,6 +3289,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; @@ -1743,11 +3340,12 @@ static void arm64_display_machine_stats(void) { + int i, pad; struct new_utsname *uts; char buf[BUFSIZE]; ulong mhz; - uts = &kt->utsname; + uts = &kt->utsname; fprintf(fp, " MACHINE TYPE: %s\n", uts->machine); fprintf(fp, " MEMORY SIZE: %s\n", get_memory_size(buf)); @@ -1761,6 +3359,16 @@ fprintf(fp, "KERNEL MODULES BASE: %lx\n", machdep->machspec->modules_vaddr); fprintf(fp, "KERNEL VMEMMAP BASE: %lx\n", machdep->machspec->vmemmap_vaddr); fprintf(fp, " KERNEL STACK SIZE: %ld\n", STACKSIZE()); + if (machdep->machspec->irq_stack_size) { + fprintf(fp, " IRQ STACK SIZE: %ld\n", + machdep->machspec->irq_stack_size); + fprintf(fp, " IRQ STACKS:\n"); + for (i = 0; i < kt->cpus; i++) { + pad = (i < 10) ? 3 : (i < 100) ? 2 : (i < 1000) ? 1 : 0; + fprintf(fp, "%s CPU %d: %lx\n", space(pad), i, + machdep->machspec->irq_stacks[i]); + } + } } static int @@ -1903,12 +3511,58 @@ } static int +arm64_on_process_stack(struct bt_info *bt, ulong stkptr) +{ + ulong stackbase, stacktop; + + stackbase = GET_STACKBASE(bt->task); + stacktop = GET_STACKTOP(bt->task); + + if ((stkptr >= stackbase) && (stkptr < stacktop)) + return TRUE; + + return FALSE; +} + +static int +arm64_on_irq_stack(int cpu, ulong stkptr) +{ + return arm64_in_alternate_stack(cpu, stkptr); +} + +static int arm64_in_alternate_stack(int cpu, ulong stkptr) { - NOT_IMPLEMENTED(INFO); + struct machine_specific *ms = machdep->machspec; + + if (!ms->irq_stack_size || (cpu >= kt->cpus)) + return FALSE; + + if ((stkptr >= ms->irq_stacks[cpu]) && + (stkptr < (ms->irq_stacks[cpu] + ms->irq_stack_size))) + return TRUE; + return FALSE; } +static void +arm64_set_irq_stack(struct bt_info *bt) +{ + struct machine_specific *ms = machdep->machspec; + + bt->stackbase = ms->irq_stacks[bt->tc->processor]; + bt->stacktop = bt->stackbase + ms->irq_stack_size; + alter_stackbuf(bt); +} + +static void +arm64_set_process_stack(struct bt_info *bt) +{ + bt->stackbase = GET_STACKBASE(bt->task); + bt->stacktop = GET_STACKTOP(bt->task); + alter_stackbuf(bt); +} + static int compare_kvaddr(const void *v1, const void *v2) @@ -1964,6 +3618,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)) || @@ -1976,6 +3635,7 @@ int bitval; struct syment *sp; ulong value; + char *string; if (!(sp = symbol_search("swapper_pg_dir")) && !(sp = symbol_search("idmap_pg_dir")) && @@ -1994,11 +3654,26 @@ 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; } } + /* + * Verify against dumpfiles that export VA_BITS in vmcoreinfo + */ + if ((string = pc->read_vmcoreinfo("NUMBER(VA_BITS)"))) { + value = atol(string); + free(string); + if (machdep->machspec->VA_BITS != value) + error(WARNING, "VA_BITS: calculated: %ld vmcoreinfo: %ld\n", + machdep->machspec->VA_BITS, value); + } + + if (CRASHDEBUG(1)) fprintf(fp, "VA_BITS: %ld\n", machdep->machspec->VA_BITS); @@ -2035,20 +3710,35 @@ STRUCT_SIZE_INIT(page, "page"); - switch (machdep->flags & (VM_L2_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: PUD_SIZE = PGDIR_SIZE_L2_64K; 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.4/bpf.c crash-7.2.3+real/bpf.c --- crash-7.1.4/bpf.c 1970-01-01 00:00:00.000000000 +0000 +++ crash-7.2.3+real/bpf.c 2018-05-17 17:39:38.000000000 +0000 @@ -0,0 +1,1258 @@ +/* bpf.c - core analysis suite + * + * Copyright (C) 2018 David Anderson + * Copyright (C) 2018 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "defs.h" + +struct bpf_info { + ulong status; + ulong progs, maps; + struct radix_tree_pair *proglist; + struct radix_tree_pair *maplist; + char *bpf_prog_buf; + char *bpf_prog_aux_buf; + char *bpf_map_buf; + char *bytecode_buf; + int bpf_prog_type_size; + int bpf_map_map_type_size; + char prog_hdr1[81]; + char map_hdr1[81]; +} bpf_info = { + .status = UNINITIALIZED, +}; + +static void do_bpf(ulong, ulong, ulong, int); +static void bpf_init(struct bpf_info *); +static int bpf_type_size_init(void); +static char *bpf_prog_type_string(int, char *); +static char *bpf_map_map_type_string(int, char *); +static char *bpf_prog_used_maps(int, char *); +static char *bpf_prog_tag_string(char *, char *); +static void bpf_prog_gpl_compatible(char *, ulong); + +static void dump_xlated_plain(void *, unsigned int, int); +static void print_boot_time(unsigned long long, char *, unsigned int); + + +#define PROG_ID (0x1) +#define MAP_ID (0x2) +#define DUMP_STRUCT (0x4) +#define JITED (0x8) +#define XLATED (0x10) +#define OPCODES (0x20) +#define PROG_VERBOSE (0x40) +#define MAP_VERBOSE (0x80) + +void +cmd_bpf(void) +{ + int c, radix; + ulong flags, prog_id, map_id; + + flags = prog_id = map_id = radix = 0; + + while ((c = getopt(argcnt, args, "PMtTjsxdm:p:")) != EOF) { + switch(c) + { + case 'j': + flags |= JITED; + break; + case 'T': + flags |= (XLATED|OPCODES); + break; + case 't': + flags |= XLATED; + break; + case 'm': + map_id = stol(optarg, FAULT_ON_ERROR, NULL); + flags |= MAP_ID; + break; + case 'p': + prog_id = stol(optarg, FAULT_ON_ERROR, NULL); + flags |= PROG_ID; + break; + case 's': + flags |= DUMP_STRUCT; + break; + case 'P': + flags |= PROG_VERBOSE; + break; + case 'M': + flags |= MAP_VERBOSE; + break; + case 'x': + if (radix == 10) + error(FATAL, "-d and -x are mutually exclusive\n"); + radix = 16; + break; + case 'd': + if (radix == 16) + error(FATAL, "-d and -x are mutually exclusive\n"); + radix = 16; + break; + default: + argerrs++; + break; + } + } + + if (argerrs) + cmd_usage(pc->curcmd, SYNOPSIS); + + if ((flags & JITED) && !(flags & (PROG_ID|PROG_VERBOSE))) + error(FATAL, "-j option only applicable with -p or -P\n"); + if ((flags & XLATED) && !(flags & (PROG_ID|PROG_VERBOSE))) + error(FATAL, "-t option only applicable with -p or -P\n"); + if ((flags & DUMP_STRUCT) && !(flags & (PROG_ID|PROG_VERBOSE|MAP_ID|MAP_VERBOSE))) + error(FATAL, "-s option requires either -p, -P, -m or -M\n"); + + if (radix && !(flags & (PROG_ID|PROG_VERBOSE|MAP_ID|MAP_VERBOSE))) + error(FATAL, "-%c option requires -s\n", radix == 10 ? 'd' : 'x'); + + while (args[optind]) { + error(FATAL, "invalid argument: %s\n", args[optind]); + optind++; + } + + do_bpf(flags, prog_id, map_id, radix); +} + +static void +bpf_init(struct bpf_info *bpf) +{ + long len; + char buf1[BUFSIZE]; + char buf2[BUFSIZE]; + char buf3[BUFSIZE]; + char buf4[BUFSIZE]; + + switch (bpf->status) + { + case UNINITIALIZED: + if (!kernel_symbol_exists("prog_idr") || + !kernel_symbol_exists("map_idr")) { + bpf->status = FALSE; + command_not_supported(); + } + + STRUCT_SIZE_INIT(bpf_prog, "bpf_prog"); + STRUCT_SIZE_INIT(bpf_prog_aux, "bpf_prog_aux"); + STRUCT_SIZE_INIT(bpf_map, "bpf_map"); + STRUCT_SIZE_INIT(bpf_insn, "bpf_insn"); + MEMBER_OFFSET_INIT(bpf_prog_aux, "bpf_prog", "aux"); + MEMBER_OFFSET_INIT(bpf_prog_type, "bpf_prog", "type"); + MEMBER_OFFSET_INIT(bpf_prog_tag, "bpf_prog", "tag"); + MEMBER_OFFSET_INIT(bpf_prog_jited_len, "bpf_prog", "jited_len"); + MEMBER_OFFSET_INIT(bpf_prog_bpf_func, "bpf_prog", "bpf_func"); + MEMBER_OFFSET_INIT(bpf_prog_len, "bpf_prog", "len"); + MEMBER_OFFSET_INIT(bpf_prog_insnsi, "bpf_prog", "insnsi"); + MEMBER_OFFSET_INIT(bpf_map_map_type, "bpf_map", "map_type"); + MEMBER_OFFSET_INIT(bpf_map_map_flags, "bpf_map", "map_flags"); + MEMBER_OFFSET_INIT(bpf_prog_aux_used_maps, "bpf_prog_aux", "used_maps"); + MEMBER_OFFSET_INIT(bpf_prog_aux_used_map_cnt, "bpf_prog_aux", "used_map_cnt"); + if (!VALID_STRUCT(bpf_prog) || + !VALID_STRUCT(bpf_prog_aux) || + !VALID_STRUCT(bpf_map) || + !VALID_STRUCT(bpf_insn) || + INVALID_MEMBER(bpf_prog_aux) || + INVALID_MEMBER(idr_idr_rt) || + INVALID_MEMBER(bpf_prog_type) || + INVALID_MEMBER(bpf_prog_tag) || + INVALID_MEMBER(bpf_prog_jited_len) || + INVALID_MEMBER(bpf_prog_bpf_func) || + INVALID_MEMBER(bpf_prog_len) || + INVALID_MEMBER(bpf_prog_insnsi) || + INVALID_MEMBER(bpf_map_map_flags) || + INVALID_MEMBER(bpf_map_map_type) || + INVALID_MEMBER(bpf_prog_aux_used_maps) || + INVALID_MEMBER(bpf_prog_aux_used_map_cnt)) { + bpf->status = FALSE; + command_not_supported(); + } + /* + * Not required for basic functionality + */ + MEMBER_OFFSET_INIT(bpf_prog_pages, "bpf_prog", "pages"); + MEMBER_OFFSET_INIT(bpf_prog_aux_load_time, "bpf_prog_aux", "load_time"); + MEMBER_OFFSET_INIT(bpf_prog_aux_user, "bpf_prog_aux", "user"); + MEMBER_OFFSET_INIT(bpf_map_key_size, "bpf_map", "key_size"); + MEMBER_OFFSET_INIT(bpf_map_value_size, "bpf_map", "value_size"); + MEMBER_OFFSET_INIT(bpf_map_max_entries, "bpf_map", "max_entries"); + MEMBER_OFFSET_INIT(bpf_map_pages, "bpf_map", "pages"); + MEMBER_OFFSET_INIT(bpf_map_name, "bpf_map", "name"); + MEMBER_OFFSET_INIT(bpf_map_user, "bpf_map", "user"); + MEMBER_OFFSET_INIT(user_struct_uid, "user_struct", "uid"); + + if (!bpf_type_size_init()) { + bpf->status = FALSE; + command_not_supported(); + } + sprintf(bpf->prog_hdr1, "%s %s %s %s ", + mkstring(buf1, 4, CENTER|LJUST, "ID"), + mkstring(buf2, VADDR_PRLEN, CENTER|LJUST, "BPF_PROG"), + mkstring(buf3, VADDR_PRLEN, CENTER|LJUST, "BPF_PROG_AUX"), + mkstring(buf4, bpf->bpf_prog_type_size, CENTER|LJUST, "BPF_PROG_TYPE")); + strcat(bpf->prog_hdr1, " TAG USED_MAPS"); + sprintf(bpf->map_hdr1, "%s %s %s MAP_FLAGS", + mkstring(buf1, 4, CENTER|LJUST, "ID"), + mkstring(buf2, VADDR_PRLEN, CENTER|LJUST, "BPF_MAP"), + mkstring(buf3, bpf->bpf_map_map_type_size, CENTER|LJUST, "BPF_MAP_TYPE")); + + bpf->status = TRUE; + break; + + case TRUE: + break; + + case FALSE: + command_not_supported(); + } + + bpf->progs = do_radix_tree(symbol_value("prog_idr") + OFFSET(idr_idr_rt), + RADIX_TREE_COUNT, NULL); + if (bpf->progs) { + len = sizeof(struct radix_tree_pair) * (bpf->progs+1); + bpf->proglist = (struct radix_tree_pair *)GETBUF(len); + bpf->proglist[0].index = bpf->progs; + bpf->progs = do_radix_tree(symbol_value("prog_idr") + OFFSET(idr_idr_rt), + RADIX_TREE_GATHER, bpf->proglist); + } + + bpf->maps = do_radix_tree(symbol_value("map_idr") + OFFSET(idr_idr_rt), + RADIX_TREE_COUNT, NULL); + if (bpf->maps) { + len = sizeof(struct radix_tree_pair) * (bpf->maps+1); + bpf->maplist = (struct radix_tree_pair *)GETBUF(len); + bpf->maplist[0].index = bpf->maps; + bpf->maps = do_radix_tree(symbol_value("map_idr") + OFFSET(idr_idr_rt), + RADIX_TREE_GATHER, bpf->maplist); + } + + bpf->bpf_prog_buf = GETBUF(SIZE(bpf_prog)); + bpf->bpf_prog_aux_buf = GETBUF(SIZE(bpf_prog_aux)); + bpf->bpf_map_buf = GETBUF(SIZE(bpf_map)); +} + +static void +do_bpf(ulong flags, ulong prog_id, ulong map_id, int radix) +{ + struct bpf_info *bpf; + int i, c, found, entries, type; + uint uid, map_pages, key_size, value_size, max_entries; + ulong bpf_prog_aux, bpf_func, end_func, addr, insnsi, user; + ulong do_progs, do_maps; + ulonglong load_time; + char *symbol; + ushort prog_pages; + int jited_len, len; + char *arglist[MAXARGS]; + char buf1[BUFSIZE]; + char buf2[BUFSIZE]; + char buf3[BUFSIZE]; + char buf4[BUFSIZE]; + char buf5[BUFSIZE/2]; + + bpf = &bpf_info; + bpf->proglist = bpf->maplist = NULL; + bpf->bpf_prog_buf = bpf->bpf_prog_aux_buf = bpf->bpf_map_buf = NULL; + bpf->bytecode_buf = NULL; + + bpf_init(bpf); + + if (flags & PROG_ID) { + for (i = found = 0; i < bpf->progs; i++) { + if (prog_id == bpf->proglist[i].index) { + found++; + break; + } + } + if (!found) { + error(INFO, "invalid program ID: %ld\n", prog_id); + goto bailout; + } + } + + if (flags & MAP_ID) { + for (i = found = 0; i < bpf->maps; i++) { + if (map_id == bpf->maplist[i].index) { + found++; + break; + } + } + if (!found) { + error(INFO, "invalid map ID: %ld\n", map_id); + goto bailout; + } + } + + if (!(flags & (PROG_ID|PROG_VERBOSE|MAP_ID|MAP_VERBOSE))) + do_progs = do_maps = TRUE; + else { + do_progs = do_maps = FALSE; + if (flags & (PROG_ID|PROG_VERBOSE)) + do_progs = TRUE; + if (flags & (MAP_ID|MAP_VERBOSE)) + do_maps = TRUE; + } + + if (!do_progs) + goto do_map_only; + + for (i = entries = 0; i < bpf->progs; i++) { + if (bpf->proglist[i].value == 0) + continue; + + if (((flags & (PROG_ID|PROG_VERBOSE)) == PROG_ID) && + (prog_id != bpf->proglist[i].index)) + continue; + + if (!readmem((ulong)bpf->proglist[i].value, KVADDR, bpf->bpf_prog_buf, + SIZE(bpf_prog), "struct bpf_prog", RETURN_ON_ERROR)) + goto bailout; + bpf_prog_aux = ULONG(bpf->bpf_prog_buf + OFFSET(bpf_prog_aux)); + if (!readmem(bpf_prog_aux, KVADDR, bpf->bpf_prog_aux_buf, + SIZE(bpf_prog_aux), "struct bpf_prog_aux", RETURN_ON_ERROR)) + goto bailout; + + if (entries && (flags & PROG_VERBOSE)) + fprintf(fp, "\n%s\n", bpf->prog_hdr1); + if (entries++ == 0) + fprintf(fp, "%s\n", bpf->prog_hdr1); + + fprintf(fp, "%s %s %s ", + mkstring(buf1, 4, CENTER|LJUST|LONG_DEC, MKSTR(bpf->proglist[i].index)), + mkstring(buf2, VADDR_PRLEN, CENTER|LJUST|LONG_HEX, MKSTR(bpf->proglist[i].value)), + mkstring(buf3, VADDR_PRLEN, CENTER|LJUST|LONG_HEX, MKSTR(bpf_prog_aux))); + type = INT(bpf->bpf_prog_buf + OFFSET(bpf_prog_type)); + fprintf(fp, "%s ", + mkstring(buf1, bpf->bpf_prog_type_size, CENTER|LJUST, bpf_prog_type_string(type, buf2))); + fprintf(fp, "%s ", bpf_prog_tag_string(bpf->bpf_prog_buf + OFFSET(bpf_prog_tag), buf1)); + fprintf(fp, "%s ", + mkstring(buf1, strlen("USED_MAPS"), CENTER|LJUST, bpf_prog_used_maps(i, buf2))); + fprintf(fp, "\n"); + + if (flags & (PROG_ID|PROG_VERBOSE)) { + jited_len = UINT(bpf->bpf_prog_buf + OFFSET(bpf_prog_jited_len)); + len = UINT(bpf->bpf_prog_buf + OFFSET(bpf_prog_len)); + len *= SIZE(bpf_insn); + if (VALID_MEMBER(bpf_prog_pages)) { + prog_pages = USHORT(bpf->bpf_prog_buf + OFFSET(bpf_prog_pages)); + prog_pages *= PAGESIZE(); + } else + prog_pages = 0; + + fprintf(fp, " XLATED: %d JITED: %d MEMLOCK: ", len, jited_len); + if (VALID_MEMBER(bpf_prog_pages)) { + fprintf(fp, "%d\n", prog_pages); + } else + fprintf(fp, "(unknown)\n"); + + fprintf(fp, " LOAD_TIME: "); + if (VALID_MEMBER(bpf_prog_aux_load_time)) { + load_time = ULONGLONG(bpf->bpf_prog_aux_buf + OFFSET(bpf_prog_aux_load_time)); + print_boot_time(load_time, buf5, BUFSIZE); + fprintf(fp, "%s\n", buf5); + } else + fprintf(fp, "(unknown)\n"); + + bpf_prog_gpl_compatible(buf1, (ulong)bpf->proglist[i].value); + fprintf(fp, " GPL_COMPATIBLE: %s", buf1); + + fprintf(fp, " UID: "); + if (VALID_MEMBER(bpf_prog_aux_user) && VALID_MEMBER(user_struct_uid)) { + user = ULONG(bpf->bpf_prog_aux_buf + OFFSET(bpf_prog_aux_user)); + if (readmem(user + OFFSET(user_struct_uid), KVADDR, &uid, sizeof(uint), + "user_struct.uid", QUIET|RETURN_ON_ERROR)) + fprintf(fp, "%d\n", uid); + else + fprintf(fp, "(unknown)\n"); + } else + fprintf(fp, "(unknown)\n"); + } + + if (flags & JITED) { + fprintf(fp, "\n"); + jited_len = UINT(bpf->bpf_prog_buf + OFFSET(bpf_prog_jited_len)); + bpf_func = ULONG(bpf->bpf_prog_buf + OFFSET(bpf_prog_bpf_func)); + end_func = bpf_func + jited_len; + + if (jited_len) { + open_tmpfile(); + pc->curcmd_private = (ulonglong)end_func; + sprintf(buf1, "x/%di 0x%lx", jited_len, bpf_func); + gdb_pass_through(buf1, NULL, GNU_RETURN_ON_ERROR); + rewind(pc->tmpfile); + while (fgets(buf1, BUFSIZE, pc->tmpfile)) { + strcpy(buf2, strip_linefeeds(buf1)); + c = parse_line(buf1, arglist); + if (STRNEQ(arglist[0], "0x") && + (LASTCHAR(arglist[0]) == ':')) { + addr = htol(strip_ending_char(arglist[0], ':'), + RETURN_ON_ERROR, NULL); + if (addr >= end_func) + break; + } + symbol = NULL; + if ((c > 1) && IS_A_NUMBER(arglist[c-1])) { + addr = htol(arglist[c-1], RETURN_ON_ERROR, NULL); + symbol = value_to_symstr(addr, buf3, radix); + if (strlen(symbol)) { + sprintf(buf4, "<%s>", symbol); + symbol = buf4; + } + } + fprintf(pc->saved_fp, "%s %s\n", buf2, symbol ? symbol : ""); + } + pc->curcmd_private = 0; + close_tmpfile(); + } else + fprintf(fp, "(program not jited)\n"); + } + + if (flags & XLATED) { + fprintf(fp, "\n"); + len = UINT(bpf->bpf_prog_buf + OFFSET(bpf_prog_len)); + insnsi = (ulong)bpf->proglist[i].value + OFFSET(bpf_prog_insnsi); + bpf->bytecode_buf = GETBUF(len * SIZE(bpf_insn)); + if (CRASHDEBUG(1)) + fprintf(fp, "bytecode_buf: [%lx] len %d * size %ld = %ld from: %lx\n", + (ulong)bpf->bytecode_buf, + len, SIZE(bpf_insn), len * SIZE(bpf_insn), insnsi); + if (!readmem(insnsi, KVADDR, bpf->bytecode_buf, len * SIZE(bpf_insn), + "bpf_prog.insnsi contents", RETURN_ON_ERROR)) + goto bailout; + dump_xlated_plain((void *)bpf->bytecode_buf, len * SIZE(bpf_insn), flags & OPCODES); + } + + if (flags & DUMP_STRUCT) { + fprintf(fp, "\n"); + dump_struct("bpf_prog", (ulong)bpf->proglist[i].value, radix); + fprintf(fp, "\n"); + dump_struct("bpf_prog_aux", bpf_prog_aux, radix); + } + } + + if (!do_maps) + goto bailout; + else + fprintf(fp, "\n"); + +do_map_only: + + for (i = entries = 0; i < bpf->maps; i++) { + if (bpf->maplist[i].value == 0) + continue; + + if (((flags & (MAP_ID|MAP_VERBOSE)) == MAP_ID) && + (map_id != bpf->maplist[i].index)) + continue; + + if (entries && (flags & MAP_VERBOSE)) + fprintf(fp, "\n%s\n", bpf->map_hdr1); + if (entries++ == 0) + fprintf(fp, "%s\n", bpf->map_hdr1); + + if (!readmem((ulong)bpf->maplist[i].value, KVADDR, bpf->bpf_map_buf, + SIZE(bpf_map), "struct bpf_map", RETURN_ON_ERROR)) + goto bailout; + + fprintf(fp, "%s %s ", + mkstring(buf1, 4, CENTER|LJUST|LONG_DEC, MKSTR(bpf->maplist[i].index)), + mkstring(buf2, VADDR_PRLEN, CENTER|LJUST|LONG_HEX, MKSTR(bpf->maplist[i].value))); + type = INT(bpf->bpf_map_buf + OFFSET(bpf_map_map_type)); + fprintf(fp, "%s ", + mkstring(buf1, bpf->bpf_map_map_type_size, CENTER|LJUST, bpf_map_map_type_string(type, buf2))); + fprintf(fp, " %08x ", UINT(bpf->bpf_map_buf + OFFSET(bpf_map_map_flags))); + fprintf(fp, "\n"); + + if (flags & (MAP_ID|MAP_VERBOSE)) { + fprintf(fp, " KEY_SIZE: "); + if (VALID_MEMBER(bpf_map_key_size)) { + key_size = UINT(bpf->bpf_map_buf + OFFSET(bpf_map_key_size)); + fprintf(fp, "%d", key_size); + } else + fprintf(fp, "(unknown)"); + + fprintf(fp, " VALUE_SIZE: "); + if (VALID_MEMBER(bpf_map_value_size)) { + value_size = UINT(bpf->bpf_map_buf + OFFSET(bpf_map_value_size)); + fprintf(fp, "%d", value_size); + } else + fprintf(fp, "(unknown)"); + + fprintf(fp, " MAX_ENTRIES: "); + if (VALID_MEMBER(bpf_map_max_entries)) { + max_entries = UINT(bpf->bpf_map_buf + OFFSET(bpf_map_max_entries)); + fprintf(fp, "%d", max_entries); + + } else + fprintf(fp, "(unknown)"); + + fprintf(fp, " MEMLOCK: "); + if (VALID_MEMBER(bpf_map_pages)) { + map_pages = UINT(bpf->bpf_map_buf + OFFSET(bpf_map_pages)); + fprintf(fp, "%d\n", map_pages * PAGESIZE()); + } else + fprintf(fp, "(unknown)\n"); + + fprintf(fp, " NAME: "); + if (VALID_MEMBER(bpf_map_name)) { + BCOPY(&bpf->bpf_map_buf[OFFSET(bpf_map_name)], buf1, 16); + buf1[17] = NULLCHAR; + if (strlen(buf1)) + fprintf(fp, "\"%s\"", buf1); + else + fprintf(fp, "(unused)"); + } else + fprintf(fp, "(unknown)\n"); + + fprintf(fp, " UID: "); + if (VALID_MEMBER(bpf_map_user) && VALID_MEMBER(user_struct_uid)) { + user = ULONG(bpf->bpf_map_buf + OFFSET(bpf_map_user)); + if (readmem(user + OFFSET(user_struct_uid), KVADDR, &uid, sizeof(uint), + "user_struct.uid", QUIET|RETURN_ON_ERROR)) + fprintf(fp, "%d\n", uid); + else + fprintf(fp, "(unknown)\n"); + } else + fprintf(fp, "(unknown)\n"); + } + + if (flags & DUMP_STRUCT) { + fprintf(fp, "\n"); + dump_struct("bpf_map", (ulong)bpf->maplist[i].value, radix); + } + + } + +bailout: + FREEBUF(bpf->proglist); + FREEBUF(bpf->maplist); + FREEBUF(bpf->bpf_prog_buf); + FREEBUF(bpf->bpf_prog_aux_buf); + FREEBUF(bpf->bpf_map_buf); + if (bpf->bytecode_buf) + FREEBUF(bpf->bytecode_buf); +} + +static int +bpf_type_size_init(void) +{ + int c ATTRIBUTE_UNUSED; + size_t max; + char *arglist[MAXARGS]; + char buf[BUFSIZE]; + struct bpf_info *bpf = &bpf_info; + + open_tmpfile(); + if (dump_enumerator_list("bpf_prog_type")) { + max = 0; + rewind(pc->tmpfile); + while (fgets(buf, BUFSIZE, pc->tmpfile)) { + if (!strstr(buf, " = ")) + continue; + c = parse_line(buf, arglist); + if (CRASHDEBUG(1)) + fprintf(pc->saved_fp, "%s\n", arglist[0]); + max = MAX(max, strlen(arglist[0])); + } + bpf->bpf_prog_type_size = max - strlen("BPF_PROG_TYPE_"); + } else { + close_tmpfile(); + return FALSE; + } + /* + * Keep bpf program header at 80 columns + */ + bpf->bpf_prog_type_size = MIN(13, bpf->bpf_prog_type_size); + close_tmpfile(); + + open_tmpfile(); + if (dump_enumerator_list("bpf_map_type")) { + max = 0; + rewind(pc->tmpfile); + while (fgets(buf, BUFSIZE, pc->tmpfile)) { + if (!strstr(buf, " = ")) + continue; + c = parse_line(buf, arglist); + if (CRASHDEBUG(1)) + fprintf(pc->saved_fp, "%s\n", arglist[0]); + max = MAX(max, strlen(arglist[0])); + } + bpf->bpf_map_map_type_size = max - strlen("BPF_PROG_TYPE_"); + } else { + close_tmpfile(); + return FALSE; + } + close_tmpfile(); + + return TRUE; +} + +static char * +bpf_prog_type_string(int type, char *retbuf) +{ + char *p; + int c ATTRIBUTE_UNUSED; + char *arglist[MAXARGS]; + char buf[BUFSIZE]; + + retbuf[0] = NULLCHAR; + + open_tmpfile(); + if (dump_enumerator_list("bpf_prog_type")) { + rewind(pc->tmpfile); + while (fgets(buf, BUFSIZE, pc->tmpfile)) { + if (!strstr(buf, " = ")) + continue; + c = parse_line(buf, arglist); + if (atoi(arglist[2]) == type) { + p = arglist[0]; + p += strlen("BPF_PROG_TYPE_"); + strcpy(retbuf, p); + break; + } + } + } + + close_tmpfile(); + return retbuf; +} + +static char * +bpf_map_map_type_string(int map_type, char *retbuf) +{ + char *p; + int c ATTRIBUTE_UNUSED; + char *arglist[MAXARGS]; + char buf[BUFSIZE]; + + retbuf[0] = NULLCHAR; + + open_tmpfile(); + if (dump_enumerator_list("bpf_map_type")) { + rewind(pc->tmpfile); + while (fgets(buf, BUFSIZE, pc->tmpfile)) { + if (!strstr(buf, " = ")) + continue; + c = parse_line(buf, arglist); + if (atoi(arglist[2]) == map_type) { + p = arglist[0]; + p += strlen("BPF_MAP_TYPE_"); + strcpy(retbuf, p); + break; + } + } + } + + close_tmpfile(); + return retbuf; +} + +static char * +bpf_prog_used_maps(int idx, char *retbuf) +{ + int i, m, cnt; + struct bpf_info *bpf = &bpf_info; + uint used_map_cnt; + ulong used_maps, map; + + retbuf[0] = NULLCHAR; + + used_map_cnt = UINT(bpf->bpf_prog_aux_buf + OFFSET(bpf_prog_aux_used_map_cnt)); + used_maps = ULONG(bpf->bpf_prog_aux_buf + OFFSET(bpf_prog_aux_used_maps)); + + for (i = cnt = 0; i < used_map_cnt; i++) { + if (!readmem(used_maps + (sizeof(ulong)*i), KVADDR, &map, + sizeof(ulong), "bpf_prog_aux.used_maps", RETURN_ON_ERROR)) + return retbuf; + for (m = 0; m < bpf->maps; m++) { + if (map == (ulong)bpf->maplist[m].value) { + sprintf(&retbuf[strlen(retbuf)], "%s%ld", + strlen(retbuf) ? "," : "", + bpf->maplist[m].index); + } + } + } + + return retbuf; +} + +static char * +bpf_prog_tag_string(char *tag, char *buf) +{ + int i; + + buf[0] = NULLCHAR; + for (i = 0; i < 8; i++) + sprintf(&buf[strlen(buf)], "%02x", (unsigned char)tag[i]); + + return buf; +} + +static void +bpf_prog_gpl_compatible(char *retbuf, ulong bpf_prog) +{ + char buf[BUFSIZE]; + + sprintf(retbuf, "(unknown)"); + + open_tmpfile(); + sprintf(buf, "p (*(struct bpf_prog *)0x%lx).gpl_compatible", bpf_prog); + gdb_pass_through(buf, NULL, GNU_RETURN_ON_ERROR); + rewind(pc->tmpfile); + while (fgets(buf, BUFSIZE, pc->tmpfile)) { + if (strstr(buf, " = 1")) { + sprintf(retbuf, "yes"); + break; + } else if (strstr(buf, " = 0")) { + sprintf(retbuf, "no"); + break; + } + } + close_tmpfile(); +} + + +// #include + +/* + * Taken from: "/usr/include/linux/bpf_common.h" + */ + +/* + * bpf_common.h + */ + +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_BPF_COMMON_H__ +#define __LINUX_BPF_COMMON_H__ + +/* Instruction classes */ +#define BPF_CLASS(code) ((code) & 0x07) +#define BPF_LD 0x00 +#define BPF_LDX 0x01 +#define BPF_ST 0x02 +#define BPF_STX 0x03 +#define BPF_ALU 0x04 +#define BPF_JMP 0x05 +#define BPF_RET 0x06 +#define BPF_MISC 0x07 + +/* ld/ldx fields */ +#define BPF_SIZE(code) ((code) & 0x18) +#define BPF_W 0x00 /* 32-bit */ +#define BPF_H 0x08 /* 16-bit */ +#define BPF_B 0x10 /* 8-bit */ +/* eBPF BPF_DW 0x18 64-bit */ +#define BPF_MODE(code) ((code) & 0xe0) +#define BPF_IMM 0x00 +#define BPF_ABS 0x20 +#define BPF_IND 0x40 +#define BPF_MEM 0x60 +#define BPF_LEN 0x80 +#define BPF_MSH 0xa0 + +/* alu/jmp fields */ +#define BPF_OP(code) ((code) & 0xf0) +#define BPF_ADD 0x00 +#define BPF_SUB 0x10 +#define BPF_MUL 0x20 +#define BPF_DIV 0x30 +#define BPF_OR 0x40 +#define BPF_AND 0x50 +#define BPF_LSH 0x60 +#define BPF_RSH 0x70 +#define BPF_NEG 0x80 +#define BPF_MOD 0x90 +#define BPF_XOR 0xa0 + +#define BPF_JA 0x00 +#define BPF_JEQ 0x10 +#define BPF_JGT 0x20 +#define BPF_JGE 0x30 +#define BPF_JSET 0x40 +#define BPF_SRC(code) ((code) & 0x08) +#define BPF_K 0x00 +#define BPF_X 0x08 + +#ifndef BPF_MAXINSNS +#define BPF_MAXINSNS 4096 +#endif + +#endif /* __LINUX_BPF_COMMON_H__ */ + + +/* + * Taken from: /usr/include/asm-generic/int-ll64.h + */ +typedef unsigned char __u8; +typedef __signed__ short __s16; +typedef __signed__ int __s32; + +/* + * Taken from: "/usr/include/linux/bpf.h" + */ + +/* Extended instruction set based on top of classic BPF */ + +/* instruction classes */ +#define BPF_ALU64 0x07 /* alu mode in double word width */ + +/* ld/ldx fields */ +#define BPF_DW 0x18 /* double word (64-bit) */ +#define BPF_XADD 0xc0 /* exclusive add */ + +/* alu/jmp fields */ +#define BPF_MOV 0xb0 /* mov reg to reg */ +#define BPF_ARSH 0xc0 /* sign extending arithmetic shift right */ + +/* change endianness of a register */ +#define BPF_END 0xd0 /* flags for endianness conversion: */ +#define BPF_TO_LE 0x00 /* convert to little-endian */ +#define BPF_TO_BE 0x08 /* convert to big-endian */ +#define BPF_FROM_LE BPF_TO_LE +#define BPF_FROM_BE BPF_TO_BE + +/* jmp encodings */ +#define BPF_JNE 0x50 /* jump != */ +#define BPF_JLT 0xa0 /* LT is unsigned, '<' */ +#define BPF_JLE 0xb0 /* LE is unsigned, '<=' */ +#define BPF_JSGT 0x60 /* SGT is signed '>', GT in x86 */ +#define BPF_JSGE 0x70 /* SGE is signed '>=', GE in x86 */ +#define BPF_JSLT 0xc0 /* SLT is signed, '<' */ +#define BPF_JSLE 0xd0 /* SLE is signed, '<=' */ +#define BPF_CALL 0x80 /* function call */ +#define BPF_EXIT 0x90 /* function return */ + +/* Register numbers */ +enum { + BPF_REG_0 = 0, + BPF_REG_1, + BPF_REG_2, + BPF_REG_3, + BPF_REG_4, + BPF_REG_5, + BPF_REG_6, + BPF_REG_7, + BPF_REG_8, + BPF_REG_9, + BPF_REG_10, + __MAX_BPF_REG, +}; + +struct bpf_insn { + __u8 code; /* opcode */ + __u8 dst_reg:4; /* dest register */ + __u8 src_reg:4; /* source register */ + __s16 off; /* signed offset */ + __s32 imm; /* signed immediate constant */ +}; + +/* instruction classes */ +#define BPF_ALU64 0x07 /* alu mode in double word width */ + +/* ld/ldx fields */ +#define BPF_DW 0x18 /* double word (64-bit) */ +#define BPF_XADD 0xc0 /* exclusive add */ + +/* alu/jmp fields */ +#define BPF_MOV 0xb0 /* mov reg to reg */ +#define BPF_ARSH 0xc0 /* sign extending arithmetic shift right */ + +/* change endianness of a register */ +#define BPF_END 0xd0 /* flags for endianness conversion: */ +#define BPF_TO_LE 0x00 /* convert to little-endian */ +#define BPF_TO_BE 0x08 /* convert to big-endian */ +#define BPF_FROM_LE BPF_TO_LE +#define BPF_FROM_BE BPF_TO_BE + +/* when bpf_ldimm64->src_reg == BPF_PSEUDO_MAP_FD, bpf_ldimm64->imm == fd */ +#define BPF_PSEUDO_MAP_FD 1 + +/* when bpf_call->src_reg == BPF_PSEUDO_CALL, bpf_call->imm == pc-relative + * offset to another bpf function + */ +#define BPF_PSEUDO_CALL 1 + + + + +static void fprint_hex(FILE *, void *, unsigned int, const char *); + +/* + * Taken from: tools/bpf/bpftool/main.c + */ +void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep) +{ + unsigned char *data = arg; + unsigned int i; + + for (i = 0; i < n; i++) { + const char *pfx = ""; + + if (!i) + /* nothing */; + else if (!(i % 16)) + fprintf(f, "\n"); + else if (!(i % 8)) + fprintf(f, " "); + else + pfx = sep; + + fprintf(f, "%s%02hhx", i ? pfx : "", data[i]); + } +} + + + +static void +dump_bpf_insn(struct bpf_insn *insn) +{ + fprintf(fp, " code: 0x%x / %d\n", insn->code, insn->code); + fprintf(fp, " dst_reg: 0x%x / %d\n", insn->dst_reg, insn->dst_reg); + fprintf(fp, " src_reg: 0x%x / %d\n", insn->src_reg, insn->src_reg); + fprintf(fp, " off: 0x%x / %d\n", insn->off, insn->off); + fprintf(fp, " imm: 0x%x / %d\n", insn->imm, insn->imm); +} + +static void print_bpf_insn(struct bpf_insn *, int); + +/* + * Adapted from: "tools/bpf/bpftool/prog.c" + */ +static void +dump_xlated_plain(void *buf, unsigned int len, int opcodes) +{ + struct bpf_insn *insn = buf; + int double_insn = FALSE; + unsigned int i; + + for (i = 0; i < len / sizeof(*insn); i++) { + if (double_insn) { + double_insn = FALSE; + continue; + } + + double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW); + + fprintf(fp, "% 4d: ", i); + print_bpf_insn(insn + i, TRUE); + + if (opcodes) { + fprintf(fp, " "); + fprint_hex(fp, insn + i, 8, " "); + if (double_insn && i < len - 1) { + fprintf(fp, " "); + fprint_hex(fp, insn + i + 1, 8, " "); + } + fprintf(fp, "\n"); + } + + if (CRASHDEBUG(1)) + dump_bpf_insn(insn + i); + } +} + + +/* + * Adapted from: kernel/bpf/disasm.c + */ + +const char *const bpf_class_string[8] = { + [BPF_LD] = "ld", + [BPF_LDX] = "ldx", + [BPF_ST] = "st", + [BPF_STX] = "stx", + [BPF_ALU] = "alu", + [BPF_JMP] = "jmp", + [BPF_RET] = "BUG", + [BPF_ALU64] = "alu64", +}; + +const char *const bpf_alu_string[16] = { + [BPF_ADD >> 4] = "+=", + [BPF_SUB >> 4] = "-=", + [BPF_MUL >> 4] = "*=", + [BPF_DIV >> 4] = "/=", + [BPF_OR >> 4] = "|=", + [BPF_AND >> 4] = "&=", + [BPF_LSH >> 4] = "<<=", + [BPF_RSH >> 4] = ">>=", + [BPF_NEG >> 4] = "neg", + [BPF_MOD >> 4] = "%=", + [BPF_XOR >> 4] = "^=", + [BPF_MOV >> 4] = "=", + [BPF_ARSH >> 4] = "s>>=", + [BPF_END >> 4] = "endian", +}; + +static const char *const bpf_ldst_string[] = { + [BPF_W >> 3] = "u32", + [BPF_H >> 3] = "u16", + [BPF_B >> 3] = "u8", + [BPF_DW >> 3] = "u64", +}; + +static const char *const bpf_jmp_string[16] = { + [BPF_JA >> 4] = "jmp", + [BPF_JEQ >> 4] = "==", + [BPF_JGT >> 4] = ">", + [BPF_JLT >> 4] = "<", + [BPF_JGE >> 4] = ">=", + [BPF_JLE >> 4] = "<=", + [BPF_JSET >> 4] = "&", + [BPF_JNE >> 4] = "!=", + [BPF_JSGT >> 4] = "s>", + [BPF_JSLT >> 4] = "s<", + [BPF_JSGE >> 4] = "s>=", + [BPF_JSLE >> 4] = "s<=", + [BPF_CALL >> 4] = "call", + [BPF_EXIT >> 4] = "exit", +}; + +typedef unsigned char u8; +typedef unsigned int u32; + +static const char *__func_imm_name(const struct bpf_insn *insn, + uint64_t full_imm, char *buff, size_t len) +{ + int m; + struct bpf_info *bpf = &bpf_info; + + for (m = 0; m < bpf->maps; m++) { + if (full_imm == (ulong)bpf->maplist[m].value) { + sprintf(buff, "map[id:%ld]", + bpf->maplist[m].index); + if (CRASHDEBUG(1)) + sprintf(&buff[strlen(buff)], " (%lx)", + (ulong)bpf->maplist[m].value); + return buff; + } + } + + snprintf(buff, len, "0x%llx", (unsigned long long)full_imm); + return buff; +} + +static char *__func_get_name(const struct bpf_insn *insn, + char *buff, size_t len) +{ + long __BPF_FUNC_MAX_ID; + char func_id_str[BUFSIZE]; + ulong func_id_ptr; + struct syment *sp; + ulong offset; + + if (!enumerator_value("__BPF_FUNC_MAX_ID", &__BPF_FUNC_MAX_ID)) + return buff; + + if (insn->src_reg != BPF_PSEUDO_CALL && + insn->imm >= 0 && insn->imm < __BPF_FUNC_MAX_ID && + func_id_str[insn->imm]) { +// return func_id_str[insn->imm]; + if (!readmem(symbol_value("func_id_str") + (insn->imm * sizeof(void *)), + KVADDR, &func_id_ptr, sizeof(void *), "func_id_str pointer", + QUIET|RETURN_ON_ERROR)) + error(FATAL, "cannot read func_id_str[]"); + if (!read_string(func_id_ptr, func_id_str, BUFSIZE-1)) + error(FATAL, "cannot read func_id_str[] string"); + sprintf(buff, "%s", func_id_str); + return buff; + } + + if ((insn->src_reg != BPF_PSEUDO_CALL) && + (sp = value_search(symbol_value("__bpf_call_base") + insn->imm, &offset)) && + !offset) + return(sp->name); + + if (insn->src_reg == BPF_PSEUDO_CALL) + snprintf(buff, len, "%+d", insn->imm); + + return buff; +} + + +static void +print_bpf_insn(struct bpf_insn *insn, int allow_ptr_leaks) +{ + __u8 class = BPF_CLASS(insn->code); + + if (class == BPF_ALU || class == BPF_ALU64) { + if (BPF_OP(insn->code) == BPF_END) { + if (class == BPF_ALU64) + fprintf(fp, "BUG_alu64_%02x\n", insn->code); + else +// print_bpf_end_insn(verbose, env, insn); + fprintf(fp, "(%02x) r%d = %s%d r%d\n", insn->code, insn->dst_reg, + BPF_SRC(insn->code) == BPF_TO_BE ? "be" : "le", + insn->imm, insn->dst_reg); + + } else if (BPF_OP(insn->code) == BPF_NEG) { + fprintf(fp, "(%02x) r%d = %s-r%d\n", + insn->code, insn->dst_reg, + class == BPF_ALU ? "(u32) " : "", + insn->dst_reg); + } else if (BPF_SRC(insn->code) == BPF_X) { + fprintf(fp, "(%02x) %sr%d %s %sr%d\n", + insn->code, class == BPF_ALU ? "(u32) " : "", + insn->dst_reg, + bpf_alu_string[BPF_OP(insn->code) >> 4], + class == BPF_ALU ? "(u32) " : "", + insn->src_reg); + } else { + fprintf(fp, "(%02x) %sr%d %s %s%d\n", + insn->code, class == BPF_ALU ? "(u32) " : "", + insn->dst_reg, + bpf_alu_string[BPF_OP(insn->code) >> 4], + class == BPF_ALU ? "(u32) " : "", + insn->imm); + } + } else if (class == BPF_STX) { + if (BPF_MODE(insn->code) == BPF_MEM) + fprintf(fp, "(%02x) *(%s *)(r%d %+d) = r%d\n", + insn->code, + bpf_ldst_string[BPF_SIZE(insn->code) >> 3], + insn->dst_reg, + insn->off, insn->src_reg); + else if (BPF_MODE(insn->code) == BPF_XADD) + fprintf(fp, "(%02x) lock *(%s *)(r%d %+d) += r%d\n", + insn->code, + bpf_ldst_string[BPF_SIZE(insn->code) >> 3], + insn->dst_reg, insn->off, + insn->src_reg); + else + fprintf(fp, "BUG_%02x\n", insn->code); + } else if (class == BPF_ST) { + if (BPF_MODE(insn->code) != BPF_MEM) { + fprintf(fp, "BUG_st_%02x\n", insn->code); + return; + } + fprintf(fp, "(%02x) *(%s *)(r%d %+d) = %d\n", + insn->code, + bpf_ldst_string[BPF_SIZE(insn->code) >> 3], + insn->dst_reg, + insn->off, insn->imm); + } else if (class == BPF_LDX) { + if (BPF_MODE(insn->code) != BPF_MEM) { + fprintf(fp, "BUG_ldx_%02x\n", insn->code); + return; + } + fprintf(fp, "(%02x) r%d = *(%s *)(r%d %+d)\n", + insn->code, insn->dst_reg, + bpf_ldst_string[BPF_SIZE(insn->code) >> 3], + insn->src_reg, insn->off); + } else if (class == BPF_LD) { + if (BPF_MODE(insn->code) == BPF_ABS) { + fprintf(fp, "(%02x) r0 = *(%s *)skb[%d]\n", + insn->code, + bpf_ldst_string[BPF_SIZE(insn->code) >> 3], + insn->imm); + } else if (BPF_MODE(insn->code) == BPF_IND) { + fprintf(fp, "(%02x) r0 = *(%s *)skb[r%d + %d]\n", + insn->code, + bpf_ldst_string[BPF_SIZE(insn->code) >> 3], + insn->src_reg, insn->imm); + } else if (BPF_MODE(insn->code) == BPF_IMM && + BPF_SIZE(insn->code) == BPF_DW) { + /* At this point, we already made sure that the second + * part of the ldimm64 insn is accessible. + */ + uint64_t imm = ((uint64_t)(insn + 1)->imm << 32) | (u32)insn->imm; + int map_ptr = insn->src_reg == BPF_PSEUDO_MAP_FD; + char tmp[64]; + + if (map_ptr && !allow_ptr_leaks) + imm = 0; + + fprintf(fp, "(%02x) r%d = %s\n", + insn->code, insn->dst_reg, + __func_imm_name(insn, imm, + tmp, sizeof(tmp))); + } else { + fprintf(fp, "BUG_ld_%02x\n", insn->code); + return; + } + } else if (class == BPF_JMP) { + u8 opcode = BPF_OP(insn->code); + + if (opcode == BPF_CALL) { + char tmp[64]; + + if (insn->src_reg == BPF_PSEUDO_CALL) { + fprintf(fp, "(%02x) call pc%s\n", + insn->code, + __func_get_name(insn, + tmp, sizeof(tmp))); + } else { + strcpy(tmp, "unknown"); + fprintf(fp, "(%02x) call %s#%d\n", insn->code, + __func_get_name(insn, + tmp, sizeof(tmp)), + insn->imm); + } + } else if (insn->code == (BPF_JMP | BPF_JA)) { + fprintf(fp, "(%02x) goto pc%+d\n", + insn->code, insn->off); + } else if (insn->code == (BPF_JMP | BPF_EXIT)) { + fprintf(fp, "(%02x) exit\n", insn->code); + } else if (BPF_SRC(insn->code) == BPF_X) { + fprintf(fp, "(%02x) if r%d %s r%d goto pc%+d\n", + insn->code, insn->dst_reg, + bpf_jmp_string[BPF_OP(insn->code) >> 4], + insn->src_reg, insn->off); + } else { + fprintf(fp, "(%02x) if r%d %s 0x%x goto pc%+d\n", + insn->code, insn->dst_reg, + bpf_jmp_string[BPF_OP(insn->code) >> 4], + insn->imm, insn->off); + } + } else { + fprintf(fp, "(%02x) %s\n", + insn->code, bpf_class_string[class]); + } +} + +static void +print_boot_time(unsigned long long nsecs, char *buf, unsigned int size) +{ +#ifdef CLOCK_BOOTTIME + struct timespec real_time_ts, boot_time_ts; + time_t wallclock_secs; + struct tm load_tm; + + buf[--size] = '\0'; + + if (clock_gettime(CLOCK_REALTIME, &real_time_ts) || + clock_gettime(CLOCK_BOOTTIME, &boot_time_ts)) { + perror("Can't read clocks"); + snprintf(buf, size, "%llu", nsecs / 1000000000); + return; + } + + wallclock_secs = (real_time_ts.tv_sec - boot_time_ts.tv_sec) + + nsecs / 1000000000; + + if (!localtime_r(&wallclock_secs, &load_tm)) { + snprintf(buf, size, "%llu", nsecs / 1000000000); + return; + } + +// strftime(buf, size, "%b %d/%H:%M", &load_tm); + strftime(buf, size, "%a %b %d %H:%M:%S %Y", &load_tm); +#else + sprintf(buf, "(unknown)"); +#endif +} diff -Nru crash-7.1.4/cmdline.c crash-7.2.3+real/cmdline.c --- crash-7.1.4/cmdline.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/cmdline.c 2018-05-17 17:39:38.000000000 +0000 @@ -1,8 +1,8 @@ /* cmdline.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-2015,2017 David Anderson + * Copyright (C) 2002-2015,2017 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 @@ -805,7 +805,7 @@ FILE *stp; char buf1[BUFSIZE]; char buf2[BUFSIZE]; - char lookfor[BUFSIZE]; + char lookfor[BUFSIZE+2]; char *pid, *name, *status, *p_pid, *pgrp; char *arglist[MAXARGS]; int argc; diff -Nru crash-7.1.4/configure.c crash-7.2.3+real/configure.c --- crash-7.1.4/configure.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/configure.c 2018-05-17 17:39:38.000000000 +0000 @@ -104,6 +104,7 @@ #undef X86_64 #undef ARM #undef ARM64 +#undef SPARC64 #define UNKNOWN 0 #define X86 1 @@ -117,6 +118,7 @@ #define ARM 9 #define ARM64 10 #define MIPS 11 +#define SPARC64 12 #define TARGET_X86 "TARGET=X86" #define TARGET_ALPHA "TARGET=ALPHA" @@ -129,6 +131,7 @@ #define TARGET_ARM "TARGET=ARM" #define TARGET_ARM64 "TARGET=ARM64" #define TARGET_MIPS "TARGET=MIPS" +#define TARGET_SPARC64 "TARGET=SPARC64" #define TARGET_CFLAGS_X86 "TARGET_CFLAGS=-D_FILE_OFFSET_BITS=64" #define TARGET_CFLAGS_ALPHA "TARGET_CFLAGS=" @@ -149,6 +152,7 @@ #define TARGET_CFLAGS_MIPS "TARGET_CFLAGS=-D_FILE_OFFSET_BITS=64" #define TARGET_CFLAGS_MIPS_ON_X86 "TARGET_CFLAGS=-D_FILE_OFFSET_BITS=64" #define TARGET_CFLAGS_MIPS_ON_X86_64 "TARGET_CFLAGS=-m32 -D_FILE_OFFSET_BITS=64" +#define TARGET_CFLAGS_SPARC64 "TARGET_CFLAGS=" #define GDB_TARGET_DEFAULT "GDB_CONF_FLAGS=" #define GDB_TARGET_ARM_ON_X86 "GDB_CONF_FLAGS=--target=arm-elf-linux" @@ -236,7 +240,7 @@ "7.6", "GDB_FILES=${GDB_7.6_FILES}", "GDB_OFILES=${GDB_7.6_OFILES}", - "GDB_PATCH_FILES=gdb-7.6.patch gdb-7.6-ppc64le-support.patch", + "GDB_PATCH_FILES=gdb-7.6.patch gdb-7.6-ppc64le-support.patch gdb-7.6-proc_service.h.patch", "GDB_FLAGS=-DGDB_7_6", "GPLv3" }, @@ -378,6 +382,9 @@ #ifdef __mips__ target_data.target = MIPS; #endif +#ifdef __sparc_v9__ + target_data.target = SPARC64; +#endif set_initial_target(sp); @@ -510,6 +517,10 @@ if ((target_data.target == PPC) && (target_data.initial_gdb_target != PPC)) arch_mismatch(sp); + + if ((target_data.target == SPARC64) && + (target_data.initial_gdb_target != SPARC64)) + arch_mismatch(sp); } if ((fp = fopen("Makefile", "r")) == NULL) { @@ -620,6 +631,9 @@ case MIPS: printf("TARGET: MIPS\n"); break; + case SPARC64: + printf("TARGET: SPARC64\n"); + break; } if (strlen(target_data.program)) { @@ -729,6 +743,10 @@ } else target_CFLAGS = TARGET_CFLAGS_MIPS; break; + case SPARC64: + target = TARGET_SPARC64; + target_CFLAGS = TARGET_CFLAGS_SPARC64; + break; } ldflags = get_extra_flags("LDFLAGS.extra", NULL); @@ -1259,8 +1277,7 @@ p = fgets(inbuf1, 79, fp1); p = fgets(inbuf2, 79, fp2); - p = strstr(inbuf2, ")"); - p++; + p = strstr(inbuf2, " "); *p = '\0'; p = fgets(inbuf3, 79, fp3); @@ -1268,7 +1285,9 @@ lower_case(target_data.program, progname); fprintf(fp4, "char *build_command = \"%s\";\n", progname); - if (strlen(hostname)) + if (getenv("SOURCE_DATE_EPOCH")) + fprintf(fp4, "char *build_data = \"reproducible build\";\n"); + else if (strlen(hostname)) fprintf(fp4, "char *build_data = \"%s by %s on %s\";\n", strip_linefeeds(inbuf1), inbuf2, hostname); else @@ -1325,7 +1344,7 @@ printf("Vendor: Red Hat, Inc.\n"); printf("Packager: Dave Anderson \n"); printf("ExclusiveOS: Linux\n"); - printf("ExclusiveArch: %%{ix86} alpha ia64 ppc ppc64 ppc64pseries ppc64iseries x86_64 s390 s390x arm aarch64 ppc64le mips mipsel\n"); + printf("ExclusiveArch: %%{ix86} alpha ia64 ppc ppc64 ppc64pseries ppc64iseries x86_64 s390 s390x arm aarch64 ppc64le mips mipsel sparc64\n"); printf("Buildroot: %%{_tmppath}/%%{name}-root\n"); printf("BuildRequires: ncurses-devel zlib-devel bison\n"); printf("Requires: binutils\n"); @@ -1554,6 +1573,8 @@ target_data.initial_gdb_target = ARM; else if (strncmp(buf, "MIPS", strlen("MIPS")) == 0) target_data.initial_gdb_target = MIPS; + else if (strncmp(buf, "SPARC64", strlen("SPARC64")) == 0) + target_data.initial_gdb_target = SPARC64; } char * @@ -1572,6 +1593,7 @@ case ARM: return("ARM"); case ARM64: return("ARM64"); case MIPS: return("MIPS"); + case SPARC64: return("SPARC64"); } return "UNKNOWN"; @@ -1630,6 +1652,8 @@ return MIPS; else if (strncmp(name, "MIPS", strlen("MIPS")) == 0) return MIPS; + else if (strncmp(name, "sparc64", strlen("sparc64")) == 0) + return SPARC64; return UNKNOWN; } diff -Nru crash-7.1.4/crash.8 crash-7.2.3+real/crash.8 --- crash-7.1.4/crash.8 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/crash.8 2018-05-17 17:39:38.000000000 +0000 @@ -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 @@ -255,11 +258,14 @@ X86_64: phys_base= irq_eframe_link= + irq_stack_gap= max_physmem_bits= + kernel_image_size= vm=orig (pre-2.6.11 virtual memory address ranges) vm=2.6.11 (2.6.11 and later virtual memory address ranges) vm=xen (Xen kernel virtual memory address ranges) vm=xen-rhel4 (RHEL4 Xen kernel virtual address ranges) + vm=5level (5-level page tables) PPC64: vm=orig vm=2.6.14 (4-level page tables) @@ -271,6 +277,9 @@ phys_base= ARM64: phys_offset= + kimage_voffset= +X86: + page_offset= .fi .RE .TP diff -Nru crash-7.1.4/debian/changelog crash-7.2.3+real/debian/changelog --- crash-7.1.4/debian/changelog 2018-07-20 09:16:28.000000000 +0000 +++ crash-7.2.3+real/debian/changelog 2018-06-26 17:32:30.000000000 +0000 @@ -1,71 +1,1674 @@ -crash (7.1.4-1ubuntu4.2) xenial; urgency=medium +crash (7.2.3+real-1~16.04.1) xenial; urgency=medium - [ Marcelo Henrique Cerri ] - * [Hyper-V] 16.04 kexec-tools doesn't match linux-azure (LP: #1712867) - d/p/0011-Fix-for-Linux-commit-0100301bfdf56a2a370c7157b5ab0fb.patch - d/p/0012-Fix-for-the-ps-t-option-in-3.17-and-later-kernels-th.patch - d/p/0013-Fix-for-the-irq-s-option-for-Linux-4.2-and-later-ker.patch - d/p/0014-Improvement-of-the-accuracy-of-the-allocated-objects.patch - d/p/0015-When-reading-a-task-s-task_struct.flags-field-check-.patch - d/p/0016-Fix-for-Linux-4.8-rc1-commit-500462a9de657f86edaa102.patch - d/p/0017-Improvement-of-the-dev-d-option-to-display-I-O-stati.patch - d/p/0018-Introduction-of-a-new-bt-v-option-that-checks-the-ke.patch - d/p/0019-Fix-for-Linux-4.9-rc1-commits-15f4eae70d365bba26854c.patch - d/p/0020-Fix-for-Linux-4.10-commit-7fd8329ba502ef76dd91db561c.patch - d/p/0021-Prepare-for-the-kernel-s-taint_flag.true-and-taint_f.patch - d/p/0022-Prevent-the-livepatch-taint-flag-check-during-the-sy.patch - - -- Stefan Bader Tue, 12 Sep 2017 09:15:25 +0200 - -crash (7.1.4-1ubuntu4.1) xenial; urgency=medium - - * Cherry-pick upstream commit to complete support for 4.5+ kernels : - d/p/0003-Fix-for-the-replacements-made-to-the-kernel-s-cpu_po.patch, - d/p/0004-With-the-introduction-of-radix-MMU-in-Power-ISA-3.0-.patch, - d/p/0005-Fix-for-Linux-commit-0139aa7b7fa12ceef095d99dc36606a.patch, - d/p/0006-Fix-for-Linux-commit-edf14cdbf9a0e5ab52698ca66d07a76.patch, - d/p/0007-Fix-to-recognize-and-support-x86_64-Linux-4.8-rc1-an.patch, - d/p/0008-Fix-for-a-possible-segmentation-violation-when-analy.patch, - d/p/0009-Fix-for-support-of-Linux-4.7-and-later-x86_64-ELF-kd.patch, - d/p/0010-Linux-3.15-and-later-kernels-configured-with-CONFIG_.patch - These commits are included in version 7.1.7 of crash and are required - in order to correctly open 4.5 kernels and newer (LP: #1655625). - - -- Louis Bouchard Fri, 20 Jan 2017 17:53:30 +0100 - -crash (7.1.4-1ubuntu4) xenial; urgency=medium - - * d/p/0002-Fix-for-the-changes-made-to-the-kernel-module-struct.patch: - Cherry-pick additional crash commit needed to fix issues related to - kernel commit 8244062e. (LP: #1564487) - - -- Chris J Arges Fri, 01 Apr 2016 08:46:07 -0500 - -crash (7.1.4-1ubuntu3) xenial; urgency=medium - - * debian/tests/live: Use the new ddebs.ubuntu.com key C8CAB6595FDFF622 - - -- Adam Conrad Mon, 21 Mar 2016 09:01:57 -0600 - -crash (7.1.4-1ubuntu2) xenial; urgency=medium - - * d/p/0001-Fix-for-the-changes-made-to-the-kernel-module-struct.patch: - Fix launch failure on kernel newer than 4.4 (LP: #1555244) - - -- Louis Bouchard Thu, 10 Mar 2016 15:38:01 +0100 - -crash (7.1.4-1ubuntu1) xenial; urgency=low - - * Merge from Debian unstable. Remaining changes: - - Build for armhf, arm64, 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. - * Dropped patches: - - 4_2_support.patch + * Backport to xenial. LP: #1746088 + - Build-Depends on debhelper 9. - -- Chris J Arges Tue, 09 Feb 2016 06:08:21 -0600 + -- Thadeu Lima de Souza Cascardo Tue, 26 Jun 2018 14:32:30 -0300 + +crash (7.2.3+real-1) unstable; urgency=medium + + * Revert wget addition and re-include embedded gdb (Closes: #901863) + + -- Troy Heber Fri, 22 Jun 2018 15:39:16 -0600 + +crash (7.2.3-2) unstable; urgency=medium + + * Add wget as a builddep + + -- Troy Heber Tue, 22 May 2018 09:16:03 -0600 + +crash (7.2.3-1) unstable; urgency=medium + + * Fix for a crash-7.2.2 regression that may cause the "mount" command to + generate a segmentation violation. The bug is dependent upon the compiler + version used to build the crash utility, where a buffer overrun is not + seen with more recent versions of gcc, which hide the bug due to a + different stack layout of a function's local variables. + + * Fix for a second crash-7.2.2 buffer overrun regression that may cause the + "rd -S" option to generate a segmentation violation if a displayed memory + location contains a slab object address. + + * Fix for a third, highly unlikely, crash-7.2.2 buffer overrun regression, + that could potentially occur during session initialization. + + -- Troy Heber Mon, 21 May 2018 14:01:30 -0600 + +crash (7.2.2-1) UNRELEASED; urgency=medium + + * Fix to support Linux 4.16-rc1 and later ARM64 kernels, which fail during + session initialization with the error message "crash: cannot determine + page size". The failure to determine the page size is due to the + combination of the following kernel commits: - Linux 4.6 commit + 6ad1fe5d9077a1ab40bf74b61994d2e770b00b14 arm64: avoid R_AARCH64_ABS64 + relocations for Image header fields - Linux 4.10 commit + 4b65a5db362783ab4b04ca1c1d2ad70ed9b0ba2a arm64: Introduce + uaccess_{disable,enable} functionality based on TTBR0_EL1 - Linux 4.16 + commit 1e1b8c04fa3451e2b7190930adae43c95f0fae31 arm64: entry: Move the + trampoline to be before PAN + + * Fix the search for the booted kernel on a live system to prevent selecting + the unusable "vmlinux.o" file found in private build directories. Without + the patch, the non-executable vmlinux.o file may be selected, and the + resulting fatal error message indicates a somewhat misleading "crash: + cannot resolve _stext". + + * Implemented a new "ps -A" option that restricts the task output to just + the active tasks on each cpu. + + * As the first step in optimizing the is_page_ptr() function, save the + maximum SPARSEMEM section number during initialization, and use it as the + topmost delimeter in subsequent mem_section searches. Also allow for + per-architecture machdep->is_page_ptr() plugin functions. + + * Implemented the x86_64 machdep->is_page_ptr() plugin function. If the + kernel is configured with CONFIG_SPARSEMEM_VMEMMAP, the plugin function + optimizes the mem_section search, reducing the computation effort and time + consumed by commands that repeatedly call the is_page_ptr() function on + large-memory systems. + + * Fixes for 32-bit X86 "bt" command on kernels that have been compiled with + retpoline gcc support. Without the patch, backtraces may fail with the + error message "bt: cannot resolve stack trace", followed by the text + symbols found on the stack and possible exception frames. + + * Fix the "help foreach" argument list to include the new "gleader" task + qualifier option that was added in version 7.1.2. + + * VMware VMSS dumpfiles contain the state of each vCPU at the time when the + VM was suspended. This patch enables crash to read the relevant registers + from each vCPU state for use as the starting hooks by the "bt" command. + Also, support for "help -[D|n]" to display dumpfile contents, and "help + -r" to display vCPU register sets has been implemented. This is also the + first step towards implementing automatic KASLR offset calculations for + VMSS dumpfiles. + + * Commit 45b74b89530d611b3fa95a1041e158fbb865fa84 added support for + calculating phys_base and the mapped kernel offset for KASLR-enabled + kernels on SADUMP dumpfiles by using a technique developed by Takao Indoh. + Originally, the patchset included support for kdumps, but this was dropped + in v2, as it was deemed unnecessary due to the upstream implementation of + the "vmcoreinfo device" in QEMU. However, there are still several reasons + for which the vmcoreinfo device may not be present at the time when a + memory dump is taken from a VM, ranging from a host running older + QEMU/libvirt versions, to misconfigured VMs or environments running + Hypervisors that doesn't support this device. This patchset generalizes + the KASLR-related functions from sadump.c and moves them to + kaslr_helper.c, and makes kdump analysis fall back to KASLR offset + calculation if vmcoreinfo data is missing. + + * Fix for the "bt" command on 4.16 and later kernels size in which the + "thread_union" data structure is not contained in the vmlinux file's + debuginfo data. Without the patch, the kernel stack size is not + calculated correctly, and defaults to 8K. As a result "bt" fails with the + message "bt: invalid RSP:
bt->stackbase/stacktop: +
/
cpu: ". + + * Fix for the x86_64 "bt" command for kernels that are configured with + CONFIG_FRAME_POINTER. Without the patch, the per-text-return-address + framesize cache may contain invalid entries for functions that have an + "and $0xfffffffffffffff0,%rsp" instruction in their prologue, which aligns + the stack on a 16-byte boundary; therefore any cached framesize for a + text-return-address in such a function may be incorrect depending upon the + alignment of the stack address of a calling function. If an invalid + cached framesize is utilized by "bt", the backtrace may skip over several + frames, or may display one or more invalid (stale) frames. The patch + introduces a new cache that contains functions for which framesize values + should not be cached. + + * Speed up the "bt" command by avoiding the text value cache that was put in + place many years ago when the crash utility supported the analysis of + remote dumpfiles using the deprecated "crash daemon" running on the remote + host. The performance improvement will be most noticable when running the + first instance of "foreach bt", where there would often be a "hitch" when + it was determining the framesize of kernel module text return addresses. + + * Optimization of the crash startup time and "ps" command processing time + when analyzing dumpfiles/systems with extremely large task counts. For + example, running with a dumpfile containing over a million tasks, startup + time and "ps" processing time was reduced from 90 minutes to less then 40 + seconds. + + * Speed up the "ps -r" option by stashing the length of the task_struct.rlim + or signal_struct.rlim array in the internal array_table[]. Without the + patch, the length of the array is determined by a call to the embedded gdb + module for each task, and as a result, the command takes a minute or more + per 1000 tasks. With the patch applied, it only takes about 0.5 seconds + per 1000 tasks. + + * Added a new "tree -l" option for the rbtree display, which dumps the tree + sorted in linear order, starting with the leftmost node and progressing to + the right. Also, if a corrupted rb_node pointer is encountered, do not + fail immediately, but rather display the rb_node address and the corrupt + pointer and continue. + + * Display a fatal error message if the "tree -l" option is attempted with + radix trees. Without the patch, the option would be silently ignored. + + * Introduction of a new "bpf" command that displays information about loaded + eBFP (extended Berkeley Packet Filter) programs and maps. Because of its + upstream fluidity, the capabilities of this command will be an ongoing + task. In its initial form, the command displays the addresses, basic + information, and key data structures of eBPF programs and maps. It also + translates the bytecode, and disassembles the jited code, of loaded eBPF + programs. + + * Fixes to address several gcc-8.0.1 compiler warnings that are generated + when building with "make warn". The warnings are all false alarm messages + of type [-Wformat-overflow=], [-Wformat-truncation=] and + [-Wstringop-truncation]; the affected files are extensions.c, task.c, + kernel.c, memory.c, remote.c, symbols.c, filesys.c and xen_hyper.c. + + * Fix for the "ps -a" option for a user task that has utilized + "prctl(PR_SET_MM, ...)" to self-modify its memory map such that the stack + locations of its command line arguments and environment variables such are + not contiguous. Without the patch, the command may fail with a dump of + the crash utility's internal buffer usage statistics followed by "ps: + cannot allocate any more memory!". + + * Fix for a compilation error on ARM64. Without the patch, the compilation + of the new bpf.c file fails with the error message "bpf.c:881:18: error: + conflicting types for 'u64'" + + * Fix for an s390x session initialization-time warning that indicates + "WARNING: cannot determine MAX_PHYSMEM_BITS" on Linux 4.15 and later + kernels containing commit 83e3c48729d9ebb7af5a31a504f3fd6aff0348c4, which + changed the data type of "mem_section" from an array to a pointer. + Without the patch, the s390x manner of determining MAX_PHYSMEM_BITS fails + because it presumes that "mem_section" is an array, and as a result, + displays the warning message. + + * Fix for the determination of the ARM64 phys_offset value when running live + against /proc/kcore. Without the patch, the message "WARNING: cannot + access vmalloc'd module memory" may be displayed during session + initialization, and vmalloc/module memory will be unaccessible. (It + should be noted that at the time of this patch, the upstream version of + /proc/kcore does not work correctly for ARM64, because PT_LOAD segments + for unity-mapped blocks of physical are not generated.) + + * For live system analysis, if both "/dev/mem" and the "/dev/crash" memory + driver do not exist, try to use "/proc/kcore". Without the patch, the + session fails immediately with the error message "crash: /dev/mem: No such + file or directory". + + * Fix, and an update, for the "ipcs" command. The fix addresses an error + where IPCS entries are not displayed because of a faulty read of the + "deleted" member of the embedded "kern_ipc_perm" data structure. The + "deleted" member was being read as a 4-byte integer, but since it is + declared as a "bool" type, only the lowest byte gets set to 1 or 0. Since + the structure is not zeroed-out when allocated, stale data may be left in + the upper 3 bytes, and the IPCS entry gets rejected. The update is + required for Linux 4.11 and greater kernels, which reimplemented the IDR + facility to use radix trees in kernel commit + 0a835c4f090af2c76fc2932c539c3b32fd21fbbb, titled "Reimplement IDR and IDA + using the radix tree". Without the patch, if any IPCS entry exists, the + command would fail with the message "ipcs: invalid structure member + offset: idr_top" + + * Second stage of the new "bpf" command. This patch adds additional + per-program and per-map data for the "bpf -p ID" and "bpf -m ID" options, + containing data items shown by the "bpftool prog list" and "bpftool map + list" options; new "bpf -P" and "bpf -M" options have been added that dump + the extra data for all loaded programs or tasks. + + * Fix for a compilation error of the new "bpf.c" file when building on older + host systems where CLOCK_BOOTTIME does not exist. + + * Fix for infrequent failures of the x86 "bt" command to handle cases where + a user space task with "resume_userspace" or "entry_INT80_32" at the top + of the stack, or which was interrupted by the crash NMI while handling a + timer interrupt. Without the patch, the backtrace would be proceeded with + the error message "bt: cannot resolve stack trace", and then dump the text + symbols found on the stack and all possible exception frames. + + * Trivial formatting fix to "bpf" help page. + + * Fix the "bpf" command display on Linux 4.17-rc1 and later kernels, which + contain two new program types, BPF_PROG_TYPE_RAW_TRACEPOINT and + BPF_PROG_TYPE_CGROUP_SOCK_ADDR. Without the patch, the dynamic header + string created for bpf programs overran into the bpf map header, creating + one long combined header string. + + * Updates for the presumption that system call names begin with "sys_". In + Linux 4.17, x86_64 system calls may begin with "__x64_sys", where, for + example, "sys_read" has been replaced by "__x64_sys_read". + + -- Troy Heber Mon, 21 May 2018 13:59:41 -0600 + +crash (7.2.1-2) unstable; urgency=medium + + * Do not git clone eppic extension. (Closes: #898707), from Thadeu Lima de + Souza Cascardo + * Remove generated files: CFLAGS.extra LDFLAGS.extra extensions/defs.h, from + Thadeu Lima de Souza Cascardo + + -- Troy Heber Mon, 21 May 2018 08:18:43 -0600 + +crash (7.2.1-1) unstable; urgency=medium + + * New upstream (closes: #890394) + + * Fix for the "runq" command on Linux 4.14 and later kernels that contain + commit cd9e61ed1eebbcd5dfad59475d41ec58d9b64b6a, titled "rbtree: cache + leftmost node internally". Without the patch, the command fails with the + error message "runq: invalid structure member offset: cfs_rq_rb_leftmost". + + * Fix to prevent a useless message during session inialization. Without the + patch, if the highest possible node bit in the node_states[N_ONLINE] + multi-word bitmask is set, then a message such as "crash: + next_online_node: 256 is too large!" will be displayed. + + * Additional fixes for the ARM64 "bt" command for Linux 4.14 kernels. The + patch corrects the contents of in-kernel exception frame register dumps, + and properly transitions the backtrace from the IRQ stack to the process + stack. + + * Implemented a new "search -T" option, which is identical to the "search + -t" option, except that the search is restricted to the kernel stacks of + active tasks. + + * Removal of the ARM64 "bt -o" option for Linux 4.14 and later kernels, + along with several cleanups/readability improvements. + + * Fix for support of KASLR enabled kernels captured by the SADUMP dumpfile + facility. SADUMP dumpfile headers do not contain phys_base or VMCOREINFO + notes, so without this patch, the crash session fails during + initialization with the message "crash: seek error: kernel virtual + address:
type: "page_offset_base". This patch calculates the + phys_base value and the KASLR offset using the IDTR and CR3 registers from + the dumpfile header. + + * Implemented a new "ps -y policy" option to filter the task display by + scheduling policy. Applicable to both standalone ps invocation as well as + via foreach. + + * Fix for the "kmem -[sS]" options on Linux 4.14 and later kernels that + contain commit 2482ddec670fb83717d129012bc558777cb159f7, titled "mm: add + SLUB free list pointer obfuscation". Without the patch, there will + numerous error messages of the type "kmem: slab:
+ invalid freepointer: " if the kernel is configured + with CONFIG_SLAB_FREELIST_HARDENED. + + * Fix for the validation of the bits located in the least significant bits of + mem_section.section_mem_map pointers. Without the patch, the validation + functions always returned valid, due to a coding error found by clang. + However, it was never really a problem because it is extremely unlikely + that an existing mem_section would ever be invalid. + + * Fix for the x86_64 kernel virtual address to physical address translation + mechanism. Without the patch, when verifying that the PAGE_PRESENT bit is + set in the top-level page table, it would always test positively, and the + translation would continue parsing the remainder of the page tables. This + would virtually never be a problem in practice because if the top-level + page table entry existed, its PAGE_PRESENT bit would be set. + + * Removed a check for a negative block_size value which is always a + non-negative unsigned value in the SADUMP header parsing function. + + * Removed a check for an impossible negative value when calculating the + beginning address when applying the context value specified by the "search + -x " option. + + * Implemented a new "timer -C " option that restricts the + timer or hrtimer output to the timer queue data associated with one or + more cpus. For multiple cpus, the cpu-specifier uses the standard comma + or dash separated list format. + + * Fix for a "ps -l" regression introduced by the new "ps -y" option + introduced above. Without the patch, the -l option generates a + segmentation violation if not accompanied by a -C cpu specifier option. + + * Fix for the "kmem -i" and "kmem -V" options in Linux 4.8 and later kernels + containing commit 75ef7184053989118d3814c558a9af62e7376a58, titled "mm, + vmstat: add infrastructure for per-node vmstats". Without the patch, the + CACHED line of "kmem -i" shows 0, and the VM_STAT section of "kmem -V" is + missing entirely. + + * Fix for Linux 4.11 and later kernels that contain kernel commit + 4b3ef9daa4fc0bba742a79faecb17fdaaead083b, titled "mm/swap: split swap + cache into 64MB trunks". Without the patch, the CACHED line of "kmem -i" + may show nonsensical data. + + * Implemented a new "dev -D" option that is the same as "dev -d", but + filters out the display of disks that have no I/O in progress. + + * If a line number request for a module text address initially fails, force + the embedded gdb module to complete its two-stage strategy used for + reading debuginfo symbol tables from module object files, and then retry + the line number extraction. This automatically does what the "mod -r" or + "crash --readnow" options accomplish. + + * Update for support of Linux 4.12 and later PPC64 kernels where the hash + page table geometry accommodates a larger virtual address range. Without + the patch, the virtual-to-physical translation of user space virtual + addresses by "vm -p", "vtop", and "rd -u" may generate an invalid + translation or otherwise fail. + + * Implemented a new "runq -T" option that displays the time lag of each CPU + relative to the most recent runqueue timestamp. + + * Fix to support Linux 4.15 and later kernels that contain kernel commit + e8cfbc245e24887e3c30235f71e9e9405e0cfc39, titled "pid: remove pidhash". + The kernel's traditional usage of a pid_hash[] array to store PIDs has + been replaced by an IDR radix tree, requiring a new crash plug-in function + to gather the system's task set. Without the patch, the crash session + fails during initialization with the error message "crash: cannot resolve + init_task_union". + + * Fix for the "net" command when the network device listing has an unusually + large number of IP addresses. In that case, without the patch, the + command may generate a segmentation violation. + + * Fix for Linux 4.15 and later kernels that are configured with + CONFIG_SPARSEMEM_EXTREME, and that contain kernel commit + 83e3c48729d9ebb7af5a31a504f3fd6aff0348c4, titled "mm/sparsemem: Allocate + mem_section at runtime for CONFIG_SPARSEMEM_EXTREME=y". Without the + patch, kernels configured with SPARSEMEM_EXTREME have changed the data + type of "mem_section" from an array to a pointer, leading to errors in + commands such as "kmem -p", "kmem -n", "kmem -s", and any other command + that translates a physical address to its page struct address. + + * With the latest PPC64 NMI IPI changes, crash_ipi_callback is found + multiple times on the stack of active non-panic tasks. Ensure that the + symbol reference relates to an actual backtrace stack frame. + + * Update the starting virtual address of vmalloc space for kernels + configured with CONFIG_X86_5LEVEL. + + * Update the X86_64 VSYSCALL_END address to reflect that it only contains 1 + page. + + * Prevent the X86_64 FILL_PML() macro from updating the internal + machdep->machspec->last_pml4_read address every time a vmalloc'd kernel + virtual address is translated. + + * Fix for the "bt" command in x86_64 kernels that contain, or have backports + of, kernel commit 4950d6d48a0c43cc61d0bbb76fb10e0214b79c66, titled + "x86/dumpstack: Remove 64-byte gap at end of irq stack". Without the + patch, backtraces fail to transition from the IRQ stack back to the + process stack, showing an error message such as "bt: cannot transition + exception stack to IRQ stack to current process stack". + + * Initial pass for support of kernel page table isolation. The x86_64 "bt" + command may indicate "bt: cannot transition from exception stack to + current process stack" if the crash callback NMI occurred while an active + task was running on the new entry trampoline stack. This has only been + tested on the RHEL7 backport of the upstream patch because as of this + commit, crash does not run on 4.15-rc kernels. Further changes may be + required for upstream kernels, and distributions that implement the kernel + changes differently than upstream. + + * Fix for the "bt" command and the "ps -s" option for zombie tasks whose + kernel stacks have been freed/detached. Without the patch, the "bt" + command indicates "bt: invalid kernel virtual address: 0 type: stack + contents" and "bt: read of stack at 0 failed"; it will be changed to + display "(no stack)". The "ps -s" option would fail prematurely upon + reaching such a task, indicating "ps: invalid kernel virtual address: 0 + type: stack contents" and "ps: read of stack at 0 failed". + + * Fix for running on live systems on 4.15-rc2 and later kernels that are + configured with CONFIG_RANDOMIZE_BASE and contain kernel commit + 668533dc0764b30c9dd2baf3ca800156f688326b, titled "kallsyms: take advantage + of the new '%px' format". Without the patch, a live crash session does + not show the "WARNING: kernel relocated ..." message expected with KASLR, + and then displays the message "crash: cannot set context for pid: " + prior to generating a SIGSEGV. + + * Fix for 4.15-rc5 and later x86_64 kernels that contain kernel commit + c482feefe1aeb150156248ba0fd3e029bc886605, titled "x86/entry/64: Make + cpu_entry_area.tss read-only". Without the patch, the addresses and sizes + of the x86_64 exception stacks cannot be determined; therefore if a + backtrace starts on one of the exception stacks, then the "bt" command + will fail. + + * Additional fix for support of KASLR enabled kernels captured by the SADUMP + dumpfile facility, where this patch fixes a problem when Page Table + Isolation(PTI) is enabled. When PTI is enabled, bit 12 of CR3 register is + used to split user space and kernel space. Also bit 11:0 is used for + Process Context IDentifiers(PCID). To open an SADUMP dumpfile, the value + of CR3 is used to calculate KASLR offset and phys_base; this patch masks + the CR3 register value correctly for a PTI enabled kernel. + + * Second phase of future support for x86_64 5-level page tables. This patch + is a cleanup/collaboration of the original logic used by the various vtop + functions, where several new common functions have been added for + extracting page table entries from PGD, P4D, PUD, PMD and PTE pages. The + usage of the former PML4 and UPML pages have been replaced with the use of + the common PGD page, and use the PUD page in 4-level page table + translation. Support for 5-level page tables has been incorporated into + the the existing x86_64_kvtop() and x86_64_uvtop_level4() functions. + Backwards compatibility for older legacy kernels has been maintained. The + third phase of support will automatically detect whether the kernel + proper, and whether an individual user task, is utilizing 5-level page + tables. This patch enables support for kernel-only 5-level page tables by + entering the command line option "--machdep vm=5level". + + * Xen commit 615588563e99a23aaf37037c3fee0c413b051f4d (Xen 4.0.0.) extended + the direct mapping to 5 TB. This area was previously reserved for future + use, so it is OK to simply change the upper bound unconditionally. + + * Add a new "foreach gleader" qualifier option, restricting the output to + user-space tasks that are thread group leaders. + + * Since Xen commit 666aca08175b ("sched: use the auto-generated list of + schedulers") crash cannot open Xen vmcores because the "schedulers" symbol + no longer exists. Xen 4.7 implemented schedulers as its own section in + "xen/arch/x86/xen.lds.S", delimited by the two symbols + "__start_schedulers_array" and "__end_schedulers_array". Without the + patch, the crash session fails during initialization with the error + message "crash: cannot resolve schedulers" + + * Fix the sample crash.ko memory driver to prevent an s390X kernel + addressing exception. Legitimate pages of RAM that successfully pass the + page_is_ram() and pfn_valid() verifier functions may not be provided by + the s390x hypervisor, and the memcpy() from the non-existent memory to the + bounce buffer panics the kernel. The patch replaces the the memcpy() call + with probe_kernel_read(). + + * Fix for the ARM64 "bt" command running against Linux 4.14 and later + kernels. Without the patch, the backtraces of the active tasks in a + kdump-generated dumpfile are truncated. Without the patch, the panic task + will just show the "crash_kexec" frame and the kernel-entry user-space + exception frame; the non-panic tasks will show their backtraces starting + from the stackframe addresses captured in the per-cpu NT_PRSTATUS notes, + and will not display the exception frame generated by the NMI callback, + nor any stackframes on the IRQ stack. + + * Fix for the ARM64 "bt" command in kernels that contain commit + 30d88c0e3ace625a92eead9ca0ad94093a8f59fe, titled "arm64: entry: Apply BP + hardening for suspicious interrupts from EL0". Without the patch, there + may be invalid kernel kernel exception frames displayed on an active + task's kernel stack, often below a stackframe of the + "do_el0_ia_bp_hardening" function; the address translation of the PC and + LR values in the the bogus exception frame will display "[unknown or + invalid address]". + + -- Troy Heber Fri, 16 Feb 2018 10:47:33 -0700 + +crash (7.2.0-1) unstable; urgency=medium + + * New upstream (closes: #884464) + + * Fix for the "snap.so" extension module to pass the KASLR relocation offset + value in the ELF header for x86_64 kernels that are compiled with + CONFIG_RANDOMIZE_BASE. Without the patch, it is necessary to use the + "--kaslr=" command line option, or the session fails with the + message "WARNING: cannot read linux_banner string", followed by "crash: + vmlinux and vmcore do not match!". + + * The native gdb "disassemble" command fails if the kernel has been compiled + with CONFIG_RANDOMIZE_BASE because the embedded gdb module still operates + under the assumption that the (non-relocated) text locations in the + vmlinux file are correct. The error message that is issued is somewhat + confusing, indicating "No function contains specified address". This + patch simply clarifies the error message to indicate "crash: the gdb + "disassemble" command is prohibited because the kernel text was relocated + by KASLR; use the crash "dis" command instead." + + * Fix for the "mach -m" command in Linux 4.9 and later kernels that contain + commit 475339684ef19e46f4702e2d185a869a5c454688, titled "x86/e820: Prepare + e280 code for switch to dynamic storage", in which the "e820" symbol was + changed from a static e820map structure to a pointer to an e820map + structure. Without the patch, the command either displays just the + header, or the header with several nonsensical entries. + + * Fix for Linux 4.10 and later kdump dumpfiles, or kernels that have + backported commit 401721ecd1dcb0a428aa5d6832ee05ffbdbffbbe, titled "kexec: + export the value of phys_base instead of symbol address". Without the + patch, if the x86_64 "phys_base" value in the VMCOREINFO note is a + negative decimal number, the crash session fails during session + intialization with a "page excluded" or "seek error" when reading + "page_offset_base". + + * Fix for the PPC64 "pte" command. Without the patch, if the target PTE + references a present page, the physical address is incorrect. + + * Fix for a 32-bit MIPS compilation error if glibc-2.25 or later has been + installed on the host build machine. Without the patch, the build fails + with the error message "mips-linux-nat.c:157:1: error: conflicting types + for 'ps_get_thread_area'". + + * Fix for the validity check of S390X virtual addresses for 5-level page + tables where user space memory is mapped above 8 Petabytes. Without the + patch, "rd -u" fails and indicates "invalid user virtual address", and + "vtop -u" indicates that the address is "(not mapped)". + + * Crash 7.1.5 commit c3413456599161cabc4e910a0ae91dfe5eec3c21 (xen: Add + support for dom0 with Linux kernel 3.19 and newer) from Daniel Kiper + implemented support for Xen dom0 vmcores after Linux 3.19 kernel commit + 054954eb051f35e74b75a566a96fe756015352c8 (xen: switch to linear virtual + mapped sparse p2m list). This patch can be deemed subsequent to Daniel's + patch, and implements support Xen PV domU dumpfiles for Linux 3.19 and + later kernels. + + * Fix for the "dis" command to detect duplicate symbols in the case of a + "symbol+offset" argument where the duplicates are not contiguous in the + symbol list. Without the patch, the first of multiple symbol instances is + used in the address evaluation. With the patch, the command will fail + with the error message "dis: : duplicate text symbols + found:", followed by a list of the duplicate symbols, and their file and + line numbers if available. + + * Enhancement to the error reporting mechanism for the "kmem -[sS]" options. + When a fatal error is encountered while gathering basic CONFIG_SLUB + statistics, it is possible that the slab cache name is not displayed in + the error message, and the line containing the slab cache name, address, + etc., is not displayed at all. With this patch, an extra error message + indicating "kmem: : cannot gather relevant slab data" will be + displayed under the fatal error message; and under that, the CACHE + address, cache NAME, OBJSIZE, and SSIZE columns will be displayed, but + with "?" under the ALLOCATED, TOTAL, and SLABS columns. + + * Fix to prevent the "tree -t radix" option from failing when it encounters + duplicate entries in a radix_tree_node[slots] array. Without the patch, + if a duplicate slot entry is found, the command fails with the message + "tree: duplicate tree entry: radix_tree_node: + slots[]: \n". (The error can be prevented if the command is + preceded by "set hash off".) However, certain radix trees contain + duplicate entries by design, such as the "pgmap_radix" radix tree, in + which a radix_tree_node may contain multiple instances of the same + page_map structure. With the patch, checks will only be made for + duplicate radix_tree_node structures. + + * First phase of future support for x86_64 5-level page tables. New sets of + virtual memory offsets have been #define'd and helper macros and + placeholder functions for the p4d page tables have been added. The only + functional changes with this patchset are dynamically-set PGDIR_SHIFT and + PHYSICAL_MASK_SHIFT values that are based upon the kernel configuration. + + * Fix for a build failure. Without the patch, if the build is done by a + user whose username cannot be determined from the user ID number, the + build fails immediately with a segmentation fault. + + * Fix for Linux 4.13-rc0 commit 7fd8329ba502ef76dd91db561c7aed696b2c7720 + "x86/boot/64: Rename init_level4_pgt and early_level4_pgt". Without the + patch, the crash session fails during initialization with the error + message "crash: cannot resolve "init_level4_pgt". + + * The internal "build_data" string contains the compile-time date, the user + id of the builder, and the build machine hostname, and is viewable by the + "crash --buildinfo" command line option or by the "help -B" option during + runtime. This patch replaces that string data with "reproducible build" + if the SOURCE_DATE_EPOCH environment variable contains a value string when + the crash binary is compiled. + + * Fix for Linux 4.13-rc1 commit 2d070eab2e8270c8a84d480bb91e4f739315f03d + "mm: consider zone which is not fully populated to have holes". Without + the patch, SPARSEMEM page struct addresses are incorrectly calculated + because a new section state, and an associated flag bit, has been added to + the low bits of the mem_section.section_mem_map address; the extra bit is + erroneously passed back as part of the section_mem_map and resultant page + struct address, leading to errors in commands such as "kmem -p", "kmem + -s", "kmem -n", and any other command that translates a physical address + to its page struct address. + + * Enhancement to the S390X "vtop" command to display page table walk + information, adding output showing the following page table contents: + + "Region-First-Table Entry" (RFTE) + "Region-Second-Table Entry" (RSTE) + "Region-Third-Table Entry" (RTTE) + "Segment Table Entry" (STE) + "Page Table Entry" (PTE) + "Read address of page" (PAGE) + + Depending on the size of the address space, the page tables can start at + different levels. For example: + + crash> vtop 3ff8000c000 + VIRTUAL PHYSICAL + 3ff8000c000 2e3832000 + + PAGE DIRECTORY: 0000000000aaa000 + RTTE: 0000000000aadff8 => 00000002e3c00007 + STE: 00000002e3c00000 => 00000002e3df7000 + PTE: 00000002e3df7060 => 00000002e383203d + PAGE: 00000002e3832000 + + PAGE PHYSICAL MAPPING INDEX CNT FLAGS + 3d10b8e0c80 2e3832000 0 0 1 7fffc0000000000 + + + * Fix the s390dbf time stamps for S390X kernel versions 4.11 and 4.14. With + kernel commit ea417aa8a38bc7db ("s390/debug: make debug event time stamps + relative to the boot TOD clock") for s390dbf time is stored relative to + the kernel boot time. In order to still show absolute time since 1970 we + have to detect those kernels and re-add the boot time before printing the + records. We can use the tod_to_timeval() symbol to check for those + kernels because the patch has removed the symbol. With kernel commit + 6e2ef5e4f6cc5734 ("s390/time: add support for the TOD clock epoch + extension") the symbol name for storing the boot time has changed from + "sched_clock_base_cc" to "tod_clock_base". This commit is currently on + the s390 features branch and will be integrated in Linux 4.14. + + * Further enhancement to the S390X "vtop" command to translate the binary + values of the hardware flags for region, segment and page table entries. + For example: + + crash> vtop -u 0x60000000000000 VIRTUAL PHYSICAL + 60000000000000 5b50a000 + + PAGE DIRECTORY: 000000005cea0000 RFTE: 000000005cea0018 => + 000000006612400f (flags = 00f) flags in binary : P=0; TF=00; I=0; TT=11; + TL=11 RSTE: 0000000066124000 => 000000005d91800b (flags = 00b) flags in + binary : P=0; TF=00; I=0; TT=10; TL=11 RTTE: 000000005d918000 => + 000000006615c007 (flags = 007) flags in binary : FC=0; P=0; TF=00; I=0; + CR=0; TT=01; TL=11 STE: 000000006615c000 => 000000005ce48800 (flags = + 800) flags in binary : FC=0; P=0; I=0; CS=0; TT=00 PTE: 000000005ce48800 + => 000000005b50a03f (flags = 03f) flags in binary : I=0; P=0 PAGE: + 000000005b50a000 + + or for large pages: + + crash> vtop -k 0x3d100000000 VIRTUAL PHYSICAL 3d100000000 + 77c00000 + + PAGE DIRECTORY: 0000000001210000 RTTE: 0000000001213d10 => + 0000000077dc4007 (flags = 007) flags in binary : FC=0; P=0; TF=00; I=0; + CR=0; TT=01; TL=11 STE: 0000000077dc4000 => 0000000077c03403 (flags = + 03403) flags in binary : AV=0, ACC=0011; F=0; FC=1; P=0; I=0; CS=0; + TT=00 + + + * PPC64 kernel commit 2f18d533757da3899f4bedab0b2c051b080079dc lowered the + max real address to 53 bits. Without this patch, the warning message + "WARNING: cannot access vmalloc'd module memory" appears during + initialization, and any command that attempts to read a vmalloc'd kernel + virtual address will fail and display "read error" messages. + + * Display the KASLR relocation value warning message whenever it is in use. + Without the patch, the message may not get displayed if the --kaslr option + is used, or if the dumpfile is a vmcore generated by the current snap.so + extension module, which now exports the relocation value in the header. + + * Fix to prevent an initialization-time failure when running a live session + on a host system that does not have a "/usr/src" directory. Without the + patch, the session fails with the message "*** Error in 'crash': free(): + invalid pointer:
***". (Lei Chen) + + * Fix for the ARM64 "bt" command's display of the user mode exception frame + at the top of the stack in Linux 4.7 and later kernels. Without the + patch, the contents of the user mode exception frame are invalid due to + the miscalculation of the starting address of the pt_regs structure on the + kernel stack. + + * Integrated support for usage of the Linux 4.14 ORC unwinder by the x86_64 + "bt" command. Kernels configured with CONFIG_ORC_UNWINDER contain + .orc_unwind and .orc_unwind_ip sections that can be queried to determine + the stack frame size of any text address within a kernel function. For + kernels not configured with CONFIG_FRAME_POINTER, the crash utility does + frame size calculation by disassembling a function from its beginning to + the specified text address, counting the push, pop, and add/sub rsp + instructions, accounting for retq instructions that occur in the middle of + a function. With this patch, access to the new ORC sections has been + plugged into the existing frame size calculator, resulting in a more + efficient and accurate manner of determining frame sizes, and as a result, + more accurate backtraces. + + * Fix for the ARM64 "bt" command when run against Linux 4.14-rc1. Without + the patch, a message indicating "crash: builtin stackframe.sp offset + incorrect!" is issued during session initialization, and the "bt" command + fails with the error message "bt: invalid structure member offset: + task_struct_thread_context_sp". + + * For for the "task -R " option on Linux 4.13 and later kernels + where the task_struct contains a "randomized_struct_fields_start" to + "randomized_struct_fields_end" section. Without the patch, a member + argument that is inside the randomized section is not found. + + * Fix for the "snap.so" extension module to pass the value of the ARM64 + "kimage_voffset" value in the ELF header. Without the patch, it is + necessary to use the "--machdep kvimage_offset=" command line + option, or the session fails with the message "crash: vmlinux and vmcore + do not match!". + + -- Troy Heber Sun, 17 Dec 2017 08:25:06 -0700 + +crash (7.1.9-1) unstable; urgency=medium + + * Patch from Balint Reczey : Build crash on all Linux + architectures (Closes: #763856, #757450) + + * Patch from Balint Reczey : Continuous integration + tests can fail due to missing packages for the running kernel and missing + *-updates packages (Closes: #869367) + + * Fixes to address three gcc-7.0.1 compiler warnings that are generated when + building with "make warn". The warning types are "[-Wnonnull]" in + filesys.c, and "[-Wformat-overflow=]" in kernel.c and cmdline.c. + + * Fix for the PPC64 "mach -o" option to update the OPAL console buffer size + from 256K to 1MB, based upon the latest skiboot firmware source. + + * Fix for the "mod -[sS]" option to prevent the erroneous reassignment of + one or more symbol values of a kernel module. Without the patch, when + loading a kernel module, a message may indicate "mod: : last + symbol: is not _MODULE_END_?" may be displayed, and one + or more symbols may be reassigned an incorrect symbol value. If none of + the erroneous symbol value reassignments are beyond the end of the + module's address space, then there will be no message. + + * Linux 4.10 commit 401721ecd1dcb0a428aa5d6832ee05ffbdbffbbe finally exports + the x86_64 "phys_base" value in the VMCOREINFO note, so utilize it + whenever it exists. + + * Implemented a new "log -a" option that dumps the audit logs remaining in + kernel audit buffers that have not been copied out to the user-space audit + daemon. + + * Fix for the "kmem
" option and the "search" command in x86_64 + kernels that contain, or have backports of, kernel commit + 7c1da8d0d046174a4188b5729d7579abf3d29427, titled "crypto: sha - SHA1 + transform x86_64 AVX2", which introduced an "_end" text symbol. Without + the patch, if a base kernel symbol address that is larger than the "_end" + text symbol is passed to "kmem
", its symbol/filename information + will not be displayed. Also, when the "search" command scans the + __START_KERNEL_map region that contains kernel text and static data, the + search will be truncated to stop at the "_end" text symbol address. + + * Enhancement for the determination of the ARM64 "kimage_voffset" value in + Linux 4.6 and later kernels if an ELF format dumpfile does not contain its + value in a VMCOREINFO note, or when running against live systems using + /dev/mem, /proc/kcore, or an older version of /dev/crash. + + * Optimization of the "kmem -f
" and "kmem " options to + significantly reduce the amount of time to complete the buddy allocator + free-list scan for the target address. On very large memory systems, the + patch may reduce the time spent by several orders of magnitude. + + * Fix for a compilation error if glibc-2.25 or later has been installed on + the host build machine. Without the patch, the build fails with the error + message "amd64-linux-nat.c:496:1: error: conflicting types for + 'ps_get_thread_area'". + + * Fix for the "list -[hH]" options if a list_head.next pointer is + encountered that contains an invalid NULL pointer. Without the patch, the + "list -[hH]" options would complete/continue as if the NULL were a + legitimate end-of-list indicator, and no error would be reported. + + * Provide basic Huge Page usage as part of "kmem -i" output, showing the + total amount of memory allocated for huge pages, and the amount of the + total that is free. + + * Fix for the determination of the x86_64 "phys_base" value when it is not + passed in the VMCOREINFO data of ELF vmcores. Without the patch, it is + possible that the base address of the vmalloc region is unknown and + initialized to an incorrect default address during the very early stages + of initialization, which causes the parsing of the PT_LOAD segments for + the START_KERNEL_map region to fail. + + * Fix for the "dis" command to detect duplicate symbols in the case of a + "symbol+offset" argument where the duplicates are contiguous in the symbol + list. In addition, reject "symbol+offset" arguments if the resultant + address goes beyond the end of the function. + + * Fix for the "set scope" option if the kernel was configured with + CONFIG_RANDOMIZE_BASE. Without the patch, the command fails with the + message "set: gdb cannot find text block for address: ". This + also affects extension modules that call gdb_set_crash_scope() when + running with KASLR kernels. + + * Fix for the extensions/trace.c extension module to account for Linux 4.7 + kernel commit 9b94a8fba501f38368aef6ac1b30e7335252a220, which changed the + ring_buffer_per_cpu.nr_pages member from an int to a long. Without the + patch, the trace.so extension module fails to load on big-endian machines, + indicating "extend: Num of pages is less than 0". + + * Fix for the extensions/trace.c extension module when running on the ppc64 + architecture. Without the patch, the trace.so extension module fails to + load, indicating "extend: invalid text address: ring_buffer_read". On the + ppc64 architecture, the text symbol is ".ring_buffer_read". + + * Fix for the ARM64 "bt" command. Without the patch, the backtrace of a + non-panicking active task generates a segmentation violation when + analyzing Android 4.4-based dumpfiles. + + -- Troy Heber Wed, 26 Jul 2017 12:37:08 -0600 + +crash (7.1.8-2) unstable; urgency=medium + + * Enable lzo and snappy compression (Closes: #860319) + + -- Troy Heber Mon, 17 Apr 2017 19:25:16 -0600 + +crash (7.1.8-1) unstable; urgency=medium + + * New upstream 7.1.8 (Closes: #851882) + + * Accepted patch from Chris J Arges to fix + autopkgtest failures (Closes: #788239) + + * Builds with gcc 7 (Closes: #853357) + + * Fix for Linux 4.6 commit b03a017bebc403d40aa53a092e79b3020786537d, which + introduced the new slab management type OBJFREELIST_SLAB. In this mode, + the freelist can be an object, and if the slab is full, there is no + freelist. On the next free, an object is recycled to be used as the + freelist but not cleaned-up. This patch will go through only known freed + objects, and will prevent "kmem -S" errors that indicate "invalid/corrupt + freelist entry" on kernels configured with CONFIG_SLAB. + + * Fix for the initialization-time loading of kernel module symbols if the + kernel crashed while running a module's initcall. Without the patch, the + crash session fails during initialation with a message similar to "crash: + store_module_symbols_v2: total: 7 mcnt: 8". + + * Fix for a segmentation violation during session inialization when running + against a 32-bit MIPS ELF kdump or compressed kdump if a per-cpu + NT_PRSTATUS note cannot be gathered from the dumpfile header. Without the + the patch, a segmentation violation occurs after the message "WARNING: + cannot find NT_PRSTATUS note for cpu: " is displayed. + + * The 32-bit MIPS PGD_ORDER() macro expects __PGD_ORDER to be signed, which + it isn't now since the internal machdep->pagesize is unsigned. Without + this patch, module loading fails during initialization on a kernel that + has a page size of 16KB, with messages that indicate "please wait... + (gathering module symbol data)" followed by "crash: invalid size request: + 0 type: pgd page". + + * For ARM64 dumpfiles with VMCOREINFO, verify the new "VA_BITS" number + against the calculated number. + + * Fix for the ARM64 "bt" command in Linux 4.10 and later kernels that are + configured with CONFIG_THREAD_INFO_IN_TASK. Without the patch, the "bt" + command will fail for active tasks in dumpfiles that were generated by the + kdump facility. + + * Fix for Linux 4.10 commit 7fd8329ba502ef76dd91db561c7aed696b2c7720 + "taint/module: Clean up global and module taint flags handling". Without + the patch, when running against Linux 4.10-rc1 and later kernels, the + crash utility fails during session initialization with the message "crash: + invalid structure size: tnt". + + * Fix for support of /proc/kcore as the live memory source in Linux 4.8 and + later x86_64 kernels configured with CONFIG_RANDOMIZE_BASE, which + randomizes the unity-mapping PAGE_OFFSET value. Without the patch, the + crash session fails during session initialization with the error message + "crash: seek error: kernel virtual address:
type: + page_offset_base". + + * Update to the module taint flags handling patch above to account for the + change in size of the module.taints flag from an int to a long, while + allowing for a kernel backport that keeps it as an int. + + * Prepare for the kernel's "taint_flag.true" and "taint_flag.false" member + names to be changed to "c_true" and "c_false", which fixes build problems + when an out-of-tree module defines "true" or "false". + + * Prevent the livepatch taint flag check during the system banner display + from generating a fatal session-killing error if relevant kernel symbol + names or data structures change in the future (again). + + * Fix for the PPC64 "bt" command for non-panicking active tasks in + FADUMP-generated dumpfiles (Firmware Assisted Dump facility). Without the + patch, backtraces of those tasks may be of the form "#0 [c0000000700b3a90] + (null) at c0000000700b3b50 (unreliable)". This patch uses and displays + the ptregs register set saved in the dumpfile header for the non-panicking + active tasks. + + * Fix for a possible segmentation violation when analyzing Linux 4.6 and + earlier x86_64 kernels configured with CONFIG_RANDOMIZE_BASE. A + segmentation violation may occur during session initialization, just after + the patching of the gdb minimal_symbol values message, depending upon the + value of KERNEL_IMAGE_SIZE, which was variable in the earlier KASLR + kernels. This patch sets the KERNEL_IMAGE_SIZE default value to 1GB for + those earlier kernels, and also adds a new "--machdep + kernel_image_size=" option that can be used to override the default + KERNEL_IMAGE_SIZE value if necessary. + + * Fix the bracketing of the x86_64 FILL_PML4() macro. + + * Fix for the "tree -t radix", "irq", and "files -p" command options in + Linux 4.6 and later kernels due to upstream changes in the radix tree + facility. Without the patch, the commands will fail with the message + "radix trees do not exist or have changed their format". + + * Fix for the "trace.c" extension module. The kernel buffer referenced by + "max_tr_ring_buffer" is not available in all configurations of the kernel + so the unitialized max_tr_ring_buffer variable should not be used. A + similar check existed previously before the recent rework of the trace + extension module to support multiple buffers. + + * Clarification in the display of CONFIG_SLUB object addresses that are + displayed by the "kmem" command when SLAB_RED_ZONE has been enabled. By + default, CONFIG_SLUB object addresses that are displayed by the "kmem" + command will point to the SLAB_RED_ZONE padding inserted at the beginning + of the object. As an alternative, a new "redzone" environment variable + has been addedd that can be toggled on or off. If "set redzone off" is + entered, the object addresses will point to the address that gets returned + to the allocator. + + * Fix for the "CURRENT" value displayed by the "timer -r" command. Without + the patch, if the target machine has been up for a long enough time, an + arithmetic overflow will occur and the time value displayed will be + incorrect. + + * Fix for 32-bit X86 kernels configured with CONFIG_RANDOMIZE_BASE. Without + the patch, an invalid kernel PAGE_OFFSET value is calculated and as a + result the session fails during session initialization just after the + patching of the gdb minimal_symbol values message, showing the warning + message "WARNING: cannot read linux_banner string", followed by "crash: + /vmlinux and /dev/crash do not match!". This patch also adds a new + "--machdep page_offset=" option that can be used if the + CONFIG_PAGE_OFFSET value is not the default address of 0xc0000000. + + * Introduction of a new PPC64-only "mach -o" option that dumps the OPAL + "Open Power Abstraction Layer" console buffer. + + * Fix for the "bt" command on Linux 4.9 and later 32-bit X86 kernels + containing kernel commit 0100301bfdf56a2a370c7157b5ab0fbf9313e1cd, subject + "sched/x86: Rewrite the switch_to() code". Without the patch, backtraces + for inactive (sleeping) tasks fail with the message "bt: invalid structure + member offset: task_struct_thread_eip". + + * Fix for a "[-Wmisleading-indentation]" compiler warning and the associated + bug that is generated by lkcd_x86_trace.c when building 32-bit X86 with + "make warn" with gcc-6.3.1. + + * Fix for an invalid "bt" warning on a 32-bit X86 idle/swapper task. + Without the patch, the backtrace displays the "cannot resolve stack trace" + warning, dumps the backtrace, and then the text symbols: + + crash> bt PID: 0 TASK: f0962180 CPU: 6 COMMAND: + "swapper/6" bt: cannot resolve stack trace: #0 [f095ff1c] + __schedule at c0b6ef8d #1 [f095ff58] schedule at c0b6f4a9 #2 + [f095ff64] schedule_preempt_disabled at c0b6f728 #3 [f095ff6c] + cpu_startup_entry at c04b0310 #4 [f095ff94] start_secondary at + c04468c0 bt: text symbols on stack: [f095ff1c] __schedule at + c0b6ef8d [f095ff58] schedule at c0b6f4ae [f095ff64] + schedule_preempt_disabled at c0b6f72d [f095ff6c] + cpu_startup_entry at c04b0315 [f095ff94] start_secondary at + c04468c5 crash> + + The backtrace shown is actually correct. + + * Another fix for a similar "bt: cannot resolve stack trace" warning on a + 32-bit X86 idle/swapper task, but when running on cpu 0. + + * Remove two one-time warning messages that are displayed when running the + "bt" command on Linux 4.2 and later 32-bit X86 kernels. Without the + patch, the first "bt" command that is executed will be preceded by "bt: + WARNING: "system_call" symbol does not exist", followed by "bt: WARNING: + neither "ret_from_sys_call" nor "syscall_badsys" symbols exist". + + * Fix for Linux 3.15 and later 32-bit X86 kernels containing kernel commit + 198d208df4371734ac4728f69cb585c284d20a15, titled "x86: Keep thread_info on + thread stack in x86_32". Without the patch, incorrect addresses of each + per-cpu hardirq_stack and softirq_stack were saved for usage by the "bt" + command. + + * Additional fix for Linux 3.15 and later 32-bit X86 kernels containing + kernel commit 198d208df4371734ac4728f69cb585c284d20a15, titled "x86: Keep + thread_info on thread stack in x86_32". The patch fixes the stack + transition symbol from "handle_IRQ" to "handle_irq" for usage by the "bt" + command. + + * Fix for 32-bit X86 kernels to determine the active task in a dumpfile in + the situation where the task was running on its soft IRQ stack, took a + hard IRQ, and then the system crashed while it was running on its hard IRQ + stack. + + * Allow the "--kaslr=" and/or "--kaslr=auto" command line options to + be used with the 32-bit X86 architecture. + + * Removed -Werror from the bfd and opcode library builds. + + -- Troy Heber Fri, 24 Feb 2017 10:41:52 -0700 + +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-1) unstable; urgency=medium + + * New upstream 7.1.5 (Closes: #817798): + + * Fix for the handling of Xen DomU ELF dumpfiles to prevent the + pre-gathering of p2m frames during session initialization, which is + unnecessary since ELF files contain the mapping information in their + ".xen_p2m" section. Without the patch, it is possible that the crash + session may be unnecessarily aborted if the p2m frame-gathering fails, for + example, if the CR3 value in the header is invalid. + + * Fix for the translation of X86_64 virtual addresses in the vsyscall region + between 0xffffffffff600000 and 0xffffffffffe00000. Without the patch, the + reading of addresses in that region returns invalid data; in addition, the + "vtop" command for an address in that region shows an invalid physical + address under the "PHYSICAL" column. + + * Make the "zero excluded" mode default behavior when analyzing SADUMP + dumpfiles because some Fujitsu troubleshooting software assumes the + behavior. Also, fix the "set -v" option to show the "zero_excluded" + internal variable as "on" if it has been set when analyzing SADUMP + dumpfiles. + + * Fix for the "bt" command to properly pull the stack and frame pointer + registers from the NT_PRSTATUS notes of 32-bit tasks running in user-mode + on ARM64. Without the patch, the "bt" command utilizes ptregs->sp and + ptregs->regs[29] for 32-bit tasks instead of the architecturally-mapped + ptregs->regs[13] and ptregs->regs[11], which yields unpredictable/invalid + results, and possibly a segmentation violation. + + * Fix for the "ps -t" option in 3.17 and later kernels that contain commit + ccbf62d8a284cf181ac28c8e8407dd077d90dd4b, which changed the + task_struct.start_time member from a struct timespec to a u64. Without + the patch, the "RUN TIME" value is nonsensical. + + * Fix for the changes made to the kernel module structure introduced by this + kernel commit for Linux 4.5 and later kernels: + + commit 7523e4dc5057e157212b4741abd6256e03404cf1 + module: use a structure to encapsulate layout. + + Without the patch, the crash session fails during initialization with the + error message: "crash: invalid structure member offset: module_core_size". + + * The crash utility has not supported Xen dom0 and domU dumpfiles since this + Linux 3.19 commit: + + commit 054954eb051f35e74b75a566a96fe756015352c8 xen: switch to linear + virtual mapped sparse p2m list + + This patch resurrects support for dom0 dumpfiles only. Without the patch, + the crash session fails during session initialization with the message + "crash: cannot resolve p2m_top". + + * Fix for the replacements made to the kernel's cpu_possible_mask, + cpu_online_mask, cpu_present_mask and cpu_active_mask symbols in this + kernel commit for Linux 4.5 and later kernels: + + commit 5aec01b834fd6f8ca49d1aeede665b950d0c148e kernel/cpu.c: eliminate + cpu_*_mask + + Without the patch, behavior is architecture-specific, dependent upon + whether the cpu mask values are used to calculate the number of cpus. For + example, ARM64 crash sessions fail during session initialization with the + error message "crash: zero-size memory allocation! (called from +
)", whereas X86_64 sessions come up normally, but invalid cpu + mask values of zero are stored internally. + + * Fixes for "[-Werror=misleading-indentation]" compiler warnings that are + generated by the following files, when building X86_64 in a Fedora Rawhide + environment with gcc-6.0.0: + + gdb-7.6/bfd/coff-i386.c gdb-7.6/bfd/coff-x86_64.c kernel.c x86_64.c + lkcd_common.c + + Without the patch, the warnings in the bfd library files are treated as + errors, and abort the build. The three instances in the top-level crash + source code directory are non-fatal. There are several other gdb-specific + instances that are non-fatal and are not addressed. + + * Fix for a "[-Werror=shift-negative-value]" compiler warning that is + generated by "gdb-7.6/opcodes/arm-dis.c" when building crash with "make + target=ARM64" on an x86_64 host with gcc-6.0.0. Without the patch, the + warning is treated as an error and the build is aborted. + + * Fix for a series of "[-Werror=shift-negative-value]" compiler warnings + that are generated by "gdb-7.6/bfd/elf64-ppc.c" and + "gdb-7.6/opcodes/ppc-opc.c" when building with "make target=PPC64" on an + x86_64 host with gcc-6.0.0. Without the patch, the warnings are treated + as errors and the build is aborted. + + * Fix for a "[-Werror=unused-const-variable]" compiler warning that is + generated by "gdb-7.6/opcodes/mips-dis.c" when building with "make + target=MIPS" on an x86_64 host with gcc-6.0.0. Without the patch, the + warning is treated as an error and the build is aborted. + + * Configure the embedded gdb module with "--disable-sim" in order to bypass + the unnecessary build of the libsim.a library. + + * Implement support for per-cpu IRQ stacks on the ARM64 architecture, which + were introduced in Linux 4.5 by this commit: + + commit 132cd887b5c54758d04bf25c52fa48f45e843a30 arm64: Modify stack + trace and dump for use with irq_stack + + Without the patch, if an active task was operating on its per-cpu IRQ + stack on dumpfiles generated by kdump, its backtrace would start at the + exception frame that was laid down on the process stack. This patch also + adds support for "bt -E" to search IRQ stacks for exception frames, and + the "mach" command displays the addresses of each per-cpu IRQ stack. + + * Fixes for "[-Werror=misleading-indentation]" compiler warnings that are + generated by the following files, when building X86_64 in a Fedora Rawhide + environment with gcc-6.0.0: + + gdb-7.6/gdb/ada-lang.c gdb-7.6/gdb/linux-record.c gdb-7.6/gdb/inflow.c + gdb-7.6/gdb/printcmd.c gdb-7.6/gdb/c-typeprint.c + + Without the patch, warnings in the gdb-7.6/gdb directory are not treated + as errors, and are non-fatal to the build. + + * Further fix for the symbol name changes made to the kernel's + cpu_online_mask, cpu_possible_mask, cpu_present_mask and cpu_active_mask + symbols in Linux 4.5 and later kernels for when the crash session is + brought up with "crash -d". Without the patch, the cpus + found in each mask are displayed like this example: + + cpu_possible_(null): cpus: 0 1 2 3 4 5 6 7 cpu_present_(null): cpus: 0 1 + cpu_online_(null): cpus: 0 1 cpu_active_(null): cpus: 0 1 + + The "(null)" string segments above should read "mask". + + * Fix for the changes made to the kernel module structure introduced by this + kernel commit for Linux 4.5 and later kernels: + + commit 8244062ef1e54502ef55f54cced659913f244c3e modules: fix + longstanding /proc/kallsyms vs module insertion race. + + Without the patch, the crash session fails during initialization with the + error message: "crash: invalid structure member offset: + module_num_symtab". + + * Fix for the "dis " option if the function or address + is the highest text symbol value in a kernel module. Without the patch, + the disassembly may continue past the end of the function, or may show + nothing at all. The patch utilizes in-kernel kallsyms symbol size + information instead of disassembling until reaching the address of the + next symbol in the module. + + * Fix for the "irq -s" option in Linux 4.2 and later kernels. Without the + patch, the irq_chip.name string (e.g. "IO-APIC", "PCI-MSI", etc.) is + missing from the display. + + * Improvement of the accuracy of the allocated objects count for each + kmem_cache shown by "kmem -s" in kernels configured with CONFIG_SLUB. + Without the patch, the values under the ALLOCATED column may be too large + because cached per-cpu objects are counted as allocated. + + * Fixes to address two gcc-4.1.2 compiler warnings introduced by the + previous patch: memory.c: In function ‘count_cpu_partial’: memory.c:17958: + warning: comparison is always false due to limited range of data type + memory.c: In function ‘count_partial’: memory.c:18729: warning: comparison + is always false due to limited range of data type + + * Introduction of the "whatis -r" and "whatis -m" options. The -r option + searches for data structures of a specified size or within a range of + specified sizes. The -m option searches for data structures that contain + a member of a given type. If a structure contains another structure, the + members of the embedded structure will also be subject to the search. The + type string may be a substring of the data type name. The output displays + the size and name of the data structure. + + * Apply a fuzz factor of zero to the re-application of a modified version of + the gdb-7.6.patch in a pre-existing build directory. Without the patch, + it is possible that a previously-applied patch could be applied a second + time without the fuzz restriction. + + * Include sys/macros.h explicitly in filesys.c for the definitions of + major(), minor() and makedev(). These functions are defined in the + sys/sysmacros.h header, not sys/types.h. Linux C libraries are updating + to drop the implicit include, so we need to include it explicitly. + + * Fix for "kmem -[sS]" options for kernels configured with CONFIG_SLUB. + Without the patch, the count displayed in the ALLOCATED column may be too + large, and the "kmem -S" display of allocated/free status of individual + objects may be incorrect. + + * Fix for "kmem -[sS]" options for kernels configured with CONFIG_SLUB. + Without the patch, if a freelist pointer is corrupt, the address of the + slab page being referenced may not be displayed by the error message, + showing something like: "kmem: kmalloc-32: slab: 0 invalid freepointer: + 6e652f323a302d74". + + * Fix for the "vm -p" option on kernels that are not configured with + CONFIG_SWAP. Without the patch, the command may fail prematurely with the + message "nr_swapfiles doesn't exist in this kernel". + + * Introduction of ARM64 support for 64K pages with 3-level page tables and + 48 VA bits. Until now, support has only existed for 64K pages with + 2-level page tables, and 4K pages with 3-level page tables. + + * Fix for the "vm -p" and "vtop " commands if a user + page is swapped out. Without the patch, the "/dev" component of the swap + file pathname may be missing from its display. + + * Fix for the x86_64 "vm -p" command to properly emulate the kernel's + pte_present() function, which checks for either _PAGE_PRESENT or + _PAGE_PROTNONE to be set. Without the patch, user pages whose PTE does + not have _PAGE_PRESENT bit set are misconstrued as SWAP pages with an + "(unknown swap location") along with a bogus OFFSET value. + + * When reading a task's task_struct.flags field, check for its size, which + was changed from an unsigned long to an unsigned int. + + * Introduction of support for the 64-bit SPARC V9 architecture. This + version supports running against a live kernel. Compressed kdump support + is also here, but the crash dump support for the kernel, kexec-tools, and + makedumpfile is still pending. Initial work was done by Karl Volz with + help from Bob Picco. + + * Account for the Linux 3.17 increase of the ARM64 MAX_PHYSMEM_BITS + definition from 40 to 48. + + + -- Troy Heber Sat, 30 Apr 2016 07:54:33 -0600 crash (7.1.4-1) unstable; urgency=medium @@ -325,7 +1928,7 @@ the common zero-based user and kernel virtual address space ranges of the s390x, causing the task to be mistakenly set as the "PANIC" task. - * Mark the "crash" task that generated a snapshot vmcore utilizing the the + * Mark the "crash" task that generated a snapshot vmcore utilizing the "snap.so" extension module as "(ACTIVE)" in the STATE field of the initial system banner and the "set" command. Without the patch, the task's STATE field shows it as the "(PANIC)" task. @@ -512,7 +2115,7 @@ option for ELF kdump and compressed kdump dumpfiles. * Implementation of two new "files" command options. The "files -c" option - is context-sensitive, similar to the the regular "files" command when used + is context-sensitive, similar to the regular "files" command when used without an argument, but replaces the FILE and DENTRY columns with I_MAPPING and NRPAGES columns that reflect each open file's inode.i_mapping address_space structure address, and the @@ -565,46 +2168,6 @@ -- Troy Heber Fri, 15 Jan 2016 09:40:34 -0700 -crash (7.1.1-1ubuntu5) xenial; urgency=medium - - * Fix for crash: page excluded: kernel virtual address: ffff881052fa8000 - type: "fill_task_struct" (LP: #1523606) - - -- Dave Chiluk Tue, 15 Dec 2015 16:31:54 -0600 - -crash (7.1.1-1ubuntu4) wily; urgency=medium - - * debian/tests/live: Enable -proposed apt sources. - - -- Martin Pitt Tue, 15 Sep 2015 10:12:50 +0200 - -crash (7.1.1-1ubuntu3) wily; urgency=medium - - * debian/tests/live: Make apt-get install non-interactive. - - -- Chris J Arges Wed, 03 Jun 2015 17:05:48 -0500 - -crash (7.1.1-1ubuntu2) wily; urgency=medium - - * debian/tests/live: Redirect crash stderr to stdout. The CONFIG_STRICT_DEVMEM - warning is known and expected. - - -- Chris J Arges Wed, 03 Jun 2015 13:26:47 -0500 - -crash (7.1.1-1ubuntu1) wily; urgency=low - - * Merge from Debian unstable. Remaining changes: - - Build for armhf, arm64, 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. - * Dropped patches: - - Fix-for-the-PPC64-bt-command-on-both-big-endian-and-.patch - - Linux-4.0.patch - - -- Chris J Arges Tue, 02 Jun 2015 09:12:52 -0500 - crash (7.1.1-1) unstable; urgency=medium * Autopkgtest fix from Martin Pitt (Closes: #756969): @@ -1378,31 +2941,6 @@ -- Troy Heber Mon, 27 Apr 2015 08:28:11 -0600 -crash (7.0.8-1ubuntu3) vivid; urgency=medium - - * Linux-4.0.patch: Support 4.0+ kernel versions. (LP: #1444528) - - -- Chris J Arges Wed, 15 Apr 2015 10:12:21 -0500 - -crash (7.0.8-1ubuntu2) vivid; urgency=medium - - * Fix-for-the-PPC64-bt-command-on-both-big-endian-and-.patch (LP: #1429250) - - -- Chris J Arges Tue, 07 Apr 2015 13:52:16 -0500 - -crash (7.0.8-1ubuntu1) utopic; urgency=medium - - * FFe LP: #1381999. - - 7.0.8 adds upstream support for new architectures - - fixing ftbfs on arm64. - * Merge with Debian; remaining changes: - - Build for armhf, arm64, ppc64el. - - Minor fixes for live autopkgtest. - - debian/tests/live: Redirect gpg stderr to stdout, as that's the only - known and expected stderr source. - - -- Matthias Klose Thu, 16 Oct 2014 12:42:59 +0200 - crash (7.0.8-1) unstable; urgency=medium * Fix for the handling of 32-bit ELF xendump dumpfiles if the guest was @@ -1532,17 +3070,6 @@ -- Troy Heber Wed, 17 Sep 2014 08:30:13 -0600 -crash (7.0.7-1ubuntu1) utopic; urgency=medium - - * Merge with Debian; remainging changes: - - Build for armhf, arm64. - - Minor fixes for live autopkgtest. - - debian/tests/live: Redirect gpg stderr to stdout, as that's the only - known and expected stderr source. - - Add ppc64el support. - - -- Matthias Klose Fri, 08 Aug 2014 14:03:57 +0200 - crash (7.0.7-1) unstable; urgency=low * Export the static ELF and compressed kdump vmcoreinfo_read_string() @@ -1805,17 +3332,6 @@ -- Troy Heber Thu, 05 Jun 2014 09:26:32 -0600 -crash (7.0.5-1ubuntu1) utopic; urgency=medium - - * Merge with Debian; remaining changes: - - Build for armhf, arm64. - - Minor fixes for live autopkgtest. - - debian/tests/live: Redirect gpg stderr to stdout, as that's the only - known and expected stderr source. - * Add ppc64el support (Bharata Rao, Mauricio Faria de Oliveira). - - -- Matthias Klose Thu, 15 May 2014 18:22:35 +0200 - crash (7.0.5-1) unstable; urgency=low * New upstream version 7.0.5 @@ -1881,22 +3397,6 @@ -- Troy Heber Mon, 14 Apr 2014 14:59:30 -0600 -crash (7.0.3-3ubuntu2) trusty; urgency=low - - * debian/tests/live: Previous merge dropped the "allow-stderr" restriction. - Redirect gpg stderr to stdout, as that's the only known and expected - stderr source. - - -- Martin Pitt Thu, 21 Nov 2013 09:47:25 +0100 - -crash (7.0.3-3ubuntu1) trusty; urgency=low - - * Merge with Debian; remaining changes: - - Build for armhf, arm64. - * Minor fixes for live autopkgtest. - - -- Chris J Arges Mon, 18 Nov 2013 08:22:03 -0600 - crash (7.0.3-3) unstable; urgency=low * Add autopkgtest and try to keep the Debian and Ubuntu crash packages in @@ -2019,15 +3519,6 @@ -- Troy Heber Wed, 30 Oct 2013 16:12:40 -0600 -crash (7.0.2-1ubuntu1) trusty; urgency=low - - * Merge with Debian; remaining changes: - - Build for armhf. - - Add a live autopkgtest to run crash on running kernel. - * Build for AArch64. - - -- Matthias Klose Sat, 19 Oct 2013 16:54:49 +0200 - crash (7.0.2-1) unstable; urgency=low * Added "bison" to the BuildRequires line of the crash.spec file. @@ -2322,23 +3813,6 @@ -- Troy Heber Mon, 13 May 2013 11:59:26 -0600 -crash (6.1.6-1ubuntu2) saucy; urgency=low - - * Add a live autopkgtest to run crash on running kernel. - - -- Chris J Arges Tue, 27 Aug 2013 12:37:03 -0500 - -crash (6.1.6-1ubuntu1) saucy; urgency=low - - * Merge from Debian unstable. Remaining changes: - - debian/rules: Always build extensions and package them. - - debian/rules: Cleanup for extensions. - * Dropped ubuntu changes: - - SPU extension support. - * debian/control: Add armhf to build architectures. - - -- Stefan Bader Thu, 09 May 2013 16:47:06 +0200 - crash (6.1.6-1) unstable; urgency=low * New upstream version 6.1.6 @@ -2849,25 +4323,6 @@ -- Troy Heber Wed, 13 Feb 2013 09:28:40 -0700 -crash (6.1.0-1ubuntu2) raring; urgency=low - - * Make crash depend on binutils. (LP: #251288) - Crash uses the program strings to match vmlinux and cores. - - -- Chris J Arges Fri, 04 Jan 2013 17:26:06 -0600 - -crash (6.1.0-1ubuntu1) raring; urgency=low - - * Merge from Debian unstable (LP: #1064475). Remaining changes: - - debian/patches/01_spu_commands.patch - + Provides SPU extension support - + Enable SPU extension only on PPC (using .mk logic) - - debian/rules: - + Always build extensions and package them. - + Cleanup for extensions - - -- Stefan Bader Thu, 25 Oct 2012 16:29:52 +0200 - crash (6.1.0-1) unstable; urgency=low * Fix for 32-bit SADUMP dumpfiles to correctly check whether a requested @@ -4144,16 +5599,6 @@ -- Troy Heber Thu, 14 Jul 2011 15:15:13 -0600 -crash (5.1.6-1ubuntu1) oneiric; urgency=low - - * Merge from debian unstable. Remaining changes: - - debian/patches/01_spu_commands.patch - + Provides SPU extension support - - debian/rules: - + Enable SPU on PPC - - -- Michael Vogt Fri, 17 Jun 2011 09:20:33 +0200 - crash (5.1.6-1) unstable; urgency=low * New upstream version 5.1.5 (see @@ -4526,18 +5971,6 @@ -- Troy Heber Tue, 08 Feb 2011 10:00:47 -0700 -crash (5.0.7-1ubuntu1) natty; urgency=low - - * Merge from debian unstable. Remaining changes: - - debian/patches/01_spu_commands.patch - + Provides SPU extension support - - debian/rules: - + Enable SPU on PPC - * converted 01_spu_commands.patch from dpatch to quilt - * convert changes from cdbs to dh7 - - -- Michael Vogt Thu, 25 Nov 2010 20:57:03 +0100 - crash (5.0.7-1) unstable; urgency=low * New upstream version 5.0.7 (see @@ -4652,18 +6085,6 @@ -- Troy Heber Mon, 21 Jun 2010 15:02:16 -0600 -crash (5.0.4-1ubuntu1) maverick; urgency=low - - * Merge from debian unstable. Remaining changes: - - debian/patches/01_spu_commands.patch - + Provides SPU extension support - - debian/rules: - + Enable SPU on PPC - * converted 01_spu_commands.patch from dpatch to quilt - * convert changes from cdbs to dh7 - - -- Michael Vogt Tue, 01 Jun 2010 13:32:14 +0200 - crash (5.0.4-1) unstable; urgency=low * New upstream version 5.0.4 (see @@ -4869,21 +6290,6 @@ -- Troy Heber Tue, 01 Dec 2009 12:52:40 -0700 -crash (4.1.0-1ubuntu1) lucid; urgency=low - - * Merge from debian testing, remaining changes: - - debian/patches/01_spu_commands.dpatch - + Provides SPU extension support - - debian/rules: - + Patch support - + Enable SPU on PPC - + Don't remove gdb-6.1. - - debian/control: - + Enable LPIA builds. - + Build depend on dpatch. - - -- Michael Vogt Tue, 10 Nov 2009 11:32:39 +0100 - crash (4.1.0-1) unstable; urgency=low * New upstream version 4.1.0 (see @@ -4971,20 +6377,6 @@ -- Troy Heber Tue, 30 Jun 2009 12:00:10 -0600 -crash (4.0-8.9-1ubuntu1) karmic; urgency=low - - * Merge from debian unstable, remaining changes: - - debian/patches/01_spu_commands.dpatch - + Provides SPU extension support - - debian/rules: - + Patch support - + Enable SPU on PPC - - debian/control: - + Enable LPIA builds. - + Build depend on dpatch. - - -- Michael Vogt Mon, 22 Jun 2009 16:01:53 +0200 - crash (4.0-8.9-1) unstable; urgency=low * New upstream version 4.0-8.9 (see @@ -5081,20 +6473,6 @@ -- Troy Heber Tue, 14 Oct 2008 08:24:54 -0600 -crash (4.0-7.2-1ubuntu1) jaunty; urgency=low - - * Merge from debian unstable, remaining changes: - - debian/patches/01_spu_commands.dpatch - + Provides SPU extension support - - debian/rules: - + Patch support - + Enable SPU on PPC - - debian/control: - + Enable LPIA builds. - + Build depend on dpatch. - - -- Mario Limonciello Tue, 04 Nov 2008 13:04:16 -0600 - crash (4.0-7.2-1) unstable; urgency=low * New upstream version 4.0-7.2 (see @@ -5135,23 +6513,6 @@ -- Troy Heber Wed, 20 Aug 2008 04:43:52 -0600 -crash (4.0-6.3-1ubuntu2) intrepid; urgency=low - - * debian/control: - - Enable lpia builds. - - -- Mario Limonciello Wed, 11 Jun 2008 17:00:07 -0500 - -crash (4.0-6.3-1ubuntu1) intrepid; urgency=low - - * Merge from debian unstable, remaining changes: - - debian/patches/01_spu_commands.dpatch: - + SPU extension support - - debian/rules: - + Build SPU on powerpc - - -- Stephan Hermann Mon, 05 May 2008 12:54:24 +0200 - crash (4.0-6.3-1) unstable; urgency=low * New upstream version 4.0-6.3 (see @@ -5204,16 +6565,6 @@ -- Troy Heber Thu, 21 Feb 2008 08:22:18 -0700 -crash (4.0-4.13-1ubuntu1) hardy; urgency=low - - * Merge from debian unstable, remaining changes: - - debian/patches/01_spu_commands.dpatch: - + SPU extension support - - debian/rules: - + Build SPU on powerpc - - -- Stephan Hermann Sat, 19 Jan 2008 12:49:55 +0100 - crash (4.0-4.13-1) unstable; urgency=low * New upstream version 4.0-4.13 (see @@ -5261,22 +6612,6 @@ -- Troy Heber Wed, 21 Nov 2007 09:10:29 -0700 -crash (4.0-4.9-1ubuntu1) hardy; urgency=low - - * Merge from debian unstable, remaining changes: - - LP: #164229 - - debian/patches/01_spu_commands.dpatch: - + SPU extension support - - debian/rules: - + Build SPU on powerpc - - 01_task_info_to_stack.dpatch: - + Applied upstream, removed. - - debian/control: - + Set Maintainer to Ubuntu MOTU Developers. - + Set XSBC-Original-Maintainer. - - -- Kenneth Drake Tue, 20 Nov 2007 21:57:10 +0000 - crash (4.0-4.9-1) unstable; urgency=low * New upstream version 4.0-4.9 (see @@ -5367,19 +6702,6 @@ -- Troy Heber Tue, 17 Jul 2007 12:40:37 -0600 -crash (4.0-4.1-2ubuntu2) gutsy; urgency=low - - * debian/rules(clean): Don't remove gdb-6.1. - - -- Matthias Klose Wed, 26 Sep 2007 08:38:33 +0200 - -crash (4.0-4.1-2ubuntu1) gutsy; urgency=low - - * debian/patches/02_spu_commands.dpatch: Add support for SPU. - * Build the spu extension on powerpc. - - -- Matthias Klose Tue, 25 Sep 2007 13:13:27 +0000 - crash (4.0-4.1-2) unstable; urgency=low * Added patch to support 2.6.22 kernels where task_struct.thread_info was diff -Nru crash-7.1.4/debian/control crash-7.2.3+real/debian/control --- crash-7.1.4/debian/control 2018-07-20 09:16:28.000000000 +0000 +++ crash-7.2.3+real/debian/control 2018-06-26 17:32:30.000000000 +0000 @@ -1,15 +1,14 @@ Source: crash Section: utils Priority: optional -Maintainer: Ubuntu Developers -XSBC-Original-Maintainer: Troy Heber +Maintainer: Troy Heber Uploaders: -Build-Depends: debhelper (>= 9), dpkg-dev (>= 1.16.1), quilt (>= 0.47), binutils, binutils-dev, bison, gawk, flex, zlib1g-dev, libncurses5-dev -Standards-Version: 3.9.6.1 +Build-Depends: debhelper (>= 9), dpkg-dev (>= 1.16.1), quilt (>= 0.47), binutils, binutils-dev, bison, gawk, flex, zlib1g-dev, libncurses5-dev, liblzo2-dev, libsnappy-dev +Standards-Version: 4.1.3.0 XS-Testsuite: autopkgtest Package: crash -Architecture: i386 ia64 alpha powerpc ppc64el amd64 armel armhf arm64 s390x +Architecture: linux-any Depends: ${shlibs:Depends}, ${misc:Depends}, binutils Suggests: kexec-tools, makedumpfile Description: kernel debugging utility, allowing gdb like syntax diff -Nru crash-7.1.4/debian/Makefile.ori crash-7.2.3+real/debian/Makefile.ori --- crash-7.1.4/debian/Makefile.ori 2018-07-20 09:16:28.000000000 +0000 +++ crash-7.2.3+real/debian/Makefile.ori 2018-06-26 17:32:30.000000000 +0000 @@ -3,8 +3,8 @@ # Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. # www.missioncriticallinux.com, info@missioncriticallinux.com # -# Copyright (C) 2002-2013 David Anderson -# Copyright (C) 2002-2013 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 @@ -20,7 +20,7 @@ PROGRAM=crash # -# Supported targets: X86 ALPHA PPC IA64 PPC64 +# Supported targets: X86 ALPHA PPC IA64 PPC64 SPARC64 # TARGET and GDB_CONF_FLAGS will be configured automatically by configure # TARGET= @@ -37,7 +37,7 @@ GDB=gdb-7.6 GDB_FILES=${GDB_7.6_FILES} GDB_OFILES= -GDB_PATCH_FILES=gdb-7.6.patch gdb-7.6-ppc64le-support.patch +GDB_PATCH_FILES=gdb-7.6.patch gdb-7.6-ppc64le-support.patch gdb-7.6-proc_service.h.patch # # Default installation directory @@ -60,9 +60,9 @@ VMWARE_HFILES=vmware_vmss.h CFILES=main.c tools.c global_data.c memory.c filesys.c help.c task.c \ - kernel.c test.c gdb_interface.c configure.c net.c dev.c \ + kernel.c test.c gdb_interface.c configure.c net.c dev.c bpf.c \ alpha.c x86.c ppc.c ia64.c s390.c s390x.c s390dbf.c ppc64.c x86_64.c \ - arm.c arm64.c mips.c \ + arm.c arm64.c mips.c sparc64.c \ extensions.c remote.c va_server.c va_server_v1.c symbols.c cmdline.c \ lkcd_common.c lkcd_v1.c lkcd_v2_v3.c lkcd_v5.c lkcd_v7.c lkcd_v8.c\ lkcd_fix_mem.c s390_dump.c lkcd_x86_trace.c \ @@ -71,7 +71,7 @@ xen_hyper.c xen_hyper_command.c xen_hyper_global_data.c \ xen_hyper_dump_tables.c kvmdump.c qemu.c qemu-load.c sadump.c ipcs.c \ ramdump.c vmware_vmss.c \ - xen_dom0.c + xen_dom0.c kaslr_helper.c SOURCE_FILES=${CFILES} ${GENERIC_HFILES} ${MCORE_HFILES} \ ${REDHAT_CFILES} ${REDHAT_HFILES} ${UNWIND_HFILES} \ @@ -79,9 +79,9 @@ ${IBM_HFILES} ${SADUMP_HFILES} ${VMWARE_HFILES} OBJECT_FILES=main.o tools.o global_data.o memory.o filesys.o help.o task.o \ - build_data.o kernel.o test.o gdb_interface.o net.o dev.o \ + build_data.o kernel.o test.o gdb_interface.o net.o dev.o bpf.o \ alpha.o x86.o ppc.o ia64.o s390.o s390x.o s390dbf.o ppc64.o x86_64.o \ - arm.o arm64.o mips.o \ + arm.o arm64.o mips.o sparc64.o \ extensions.o remote.o va_server.o va_server_v1.o symbols.o cmdline.o \ lkcd_common.o lkcd_v1.o lkcd_v2_v3.o lkcd_v5.o lkcd_v7.o lkcd_v8.o \ lkcd_fix_mem.o s390_dump.o netdump.o diskdump.o makedumpfile.o xendump.o \ @@ -90,7 +90,7 @@ xen_hyper.o xen_hyper_command.o xen_hyper_global_data.o \ xen_hyper_dump_tables.o kvmdump.o qemu.o qemu-load.o sadump.o ipcs.o \ ramdump.o vmware_vmss.o \ - xen_dom0.o + xen_dom0.o kaslr_helper.o MEMORY_DRIVER_FILES=memory_driver/Makefile memory_driver/crash.c memory_driver/README @@ -233,7 +233,7 @@ @rm -f ${PROGRAM} @if [ ! -f ${GDB}/config.status ]; then \ (cd ${GDB}; ./configure ${GDB_CONF_FLAGS} --with-separate-debug-dir=/usr/lib/debug \ - --with-bugurl="" --with-expat=no --with-python=no; \ + --with-bugurl="" --with-expat=no --with-python=no --disable-sim; \ make --no-print-directory CRASH_TARGET=${TARGET}; echo ${TARGET} > crash.target) \ else make --no-print-directory rebuild; fi @if [ ! -f ${PROGRAM} ]; then \ @@ -245,7 +245,7 @@ touch ${GDB}/${GDB}.patch; fi @if [ -f ${GDB}.patch ] && [ -s ${GDB}.patch ] && \ [ "`sum ${GDB}.patch`" != "`sum ${GDB}/${GDB}.patch`" ]; then \ - (patch -N -p0 -r- < ${GDB}.patch; cp ${GDB}.patch ${GDB}; cd ${GDB}; \ + (patch -N -p0 -r- --fuzz=0 < ${GDB}.patch; cp ${GDB}.patch ${GDB}; cd ${GDB}; \ make --no-print-directory CRASH_TARGET=${TARGET}) \ else (cd ${GDB}/gdb; make --no-print-directory CRASH_TARGET=${TARGET}); fi @@ -269,6 +269,12 @@ if [ "${ARCH}" = "x86_64" ] && [ "${TARGET}" = "PPC64" ] && [ -f ${GDB}-ppc64le-support.patch ]; then \ patch -d ${GDB} -p1 -F0 < ${GDB}-ppc64le-support.patch ; \ fi + if [ -f /usr/include/proc_service.h ]; then \ + grep 'extern ps_err_e ps_get_thread_area (struct' /usr/include/proc_service.h; \ + if [ $$? -eq 0 ]; then \ + patch -p0 < ${GDB}-proc_service.h.patch; \ + fi; \ + fi library: make_build_data ${OBJECT_FILES} ar -rs ${PROGRAM}lib.a ${OBJECT_FILES} @@ -422,6 +428,9 @@ mips.o: ${GENERIC_HFILES} ${REDHAT_HFILES} mips.c ${CC} -c ${CRASH_CFLAGS} mips.c ${WARNING_OPTIONS} ${WARNING_ERROR} +sparc64.o: ${GENERIC_HFILES} ${REDHAT_HFILES} sparc64.c + ${CC} -c ${CRASH_CFLAGS} sparc64.c ${WARNING_OPTIONS} ${WARNING_ERROR} + s390.o: ${GENERIC_HFILES} ${IBM_HFILES} s390.c ${CC} -c ${CRASH_CFLAGS} s390.c ${WARNING_OPTIONS} ${WARNING_ERROR} @@ -508,6 +517,12 @@ vmware_vmss.o: ${GENERIC_HFILES} ${VMWARE_HFILES} vmware_vmss.c ${CC} -c ${CRASH_CFLAGS} vmware_vmss.c ${WARNING_OPTIONS} ${WARNING_ERROR} +kaslr_helper.o: ${GENERIC_HFILES} kaslr_helper.c + ${CC} -c ${CRASH_CFLAGS} kaslr_helper.c ${WARNING_OPTIONS} ${WARNING_ERROR} + +bpf.o: ${GENERIC_HFILES} bpf.c + ${CC} -c ${CRASH_CFLAGS} bpf.c ${WARNING_OPTIONS} ${WARNING_ERROR} + ${PROGRAM}: force @make --no-print-directory all @@ -551,7 +566,7 @@ tar cvzf ${PROGRAM}.tar.gz ${TAR_FILES} ${GDB_FILES} ${GDB_PATCH_FILES} @echo; ls -l ${PROGRAM}.tar.gz -VERSION=7.1.4 +VERSION=7.2.3 RELEASE=0 release: make_configure diff -Nru crash-7.1.4/debian/patches/0001-dont-git-clone-eppic-extension.patch crash-7.2.3+real/debian/patches/0001-dont-git-clone-eppic-extension.patch --- crash-7.1.4/debian/patches/0001-dont-git-clone-eppic-extension.patch 1970-01-01 00:00:00.000000000 +0000 +++ crash-7.2.3+real/debian/patches/0001-dont-git-clone-eppic-extension.patch 2018-06-22 21:39:16.000000000 +0000 @@ -0,0 +1,25 @@ +From: Thadeu Lima de Souza Cascardo +Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=898707 +Origin: vendor, +Forwarded: not-needed +Last-Update: 2018-05-17 +Description: Build targets shoult not attempt network access + When git is installed, the eppic extensions makefile will try to access + github.com to clone the extension code. + + This patch simply fails to find the git binary by adding an extra + false condition to minimize the delta size. + +Index: crash-7.2.1/extensions/eppic.mk +=================================================================== +--- crash-7.2.1.orig/extensions/eppic.mk ++++ crash-7.2.1/extensions/eppic.mk +@@ -33,7 +33,7 @@ all: + then \ + if [ ! -f $(APPFILE) ]; \ + then \ +- if [ -f "$(GIT)" ]; \ ++ if [ -f "$(GIT)" -a 0 -gt 1 ]; \ + then \ + if [ -n "$(EPPIC_GIT_URL)" ]; then \ + git clone "$(EPPIC_GIT_URL)" eppic; \ diff -Nru crash-7.1.4/debian/patches/0001-Fix-for-the-changes-made-to-the-kernel-module-struct.patch crash-7.2.3+real/debian/patches/0001-Fix-for-the-changes-made-to-the-kernel-module-struct.patch --- crash-7.1.4/debian/patches/0001-Fix-for-the-changes-made-to-the-kernel-module-struct.patch 2018-07-20 09:16:28.000000000 +0000 +++ crash-7.2.3+real/debian/patches/0001-Fix-for-the-changes-made-to-the-kernel-module-struct.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,80 +0,0 @@ -From 6f1f78e33474d00d5f261d7ed9d835c558b34d61 Mon Sep 17 00:00:00 2001 -From: Dave Anderson -Date: Wed, 20 Jan 2016 09:56:36 -0500 -Subject: [PATCH 1/1] Fix for the changes made to the kernel module structure - introduced by this kernel commit for Linux 4.5 and later kernels: - - commit 7523e4dc5057e157212b4741abd6256e03404cf1 - module: use a structure to encapsulate layout. - -Without the patch, the crash session fails during initialization -with the error message: "crash: invalid structure member offset: -module_init_text_size". -(sebott@linux.vnet.ibm.com) ---- - kernel.c | 49 +++++++++++++++++++++++++++++++++++++++---------- - 1 file changed, 39 insertions(+), 10 deletions(-) - -diff --git a/kernel.c b/kernel.c -index 2d4188a..5ce2fb9 100644 ---- a/kernel.c -+++ b/kernel.c -@@ -3291,16 +3291,45 @@ module_init(void) - MEMBER_OFFSET_INIT(module_gpl_syms, "module", "gpl_syms"); - MEMBER_OFFSET_INIT(module_num_gpl_syms, "module", - "num_gpl_syms"); -- MEMBER_OFFSET_INIT(module_module_core, "module", -- "module_core"); -- MEMBER_OFFSET_INIT(module_core_size, "module", -- "core_size"); -- MEMBER_OFFSET_INIT(module_core_text_size, "module", -- "core_text_size"); -- MEMBER_OFFSET_INIT(module_module_init, "module", "module_init"); -- MEMBER_OFFSET_INIT(module_init_size, "module", "init_size"); -- MEMBER_OFFSET_INIT(module_init_text_size, "module", -- "init_text_size"); -+ -+ if (MEMBER_EXISTS("module", "module_core")) { -+ MEMBER_OFFSET_INIT(module_core_size, "module", -+ "core_size"); -+ MEMBER_OFFSET_INIT(module_init_size, "module", -+ "init_size"); -+ -+ MEMBER_OFFSET_INIT(module_core_text_size, "module", -+ "core_text_size"); -+ MEMBER_OFFSET_INIT(module_init_text_size, "module", -+ "init_text_size"); -+ -+ MEMBER_OFFSET_INIT(module_module_core, "module", -+ "module_core"); -+ MEMBER_OFFSET_INIT(module_module_init, "module", -+ "module_init"); -+ } else { -+ ASSIGN_OFFSET(module_core_size) = -+ MEMBER_OFFSET("module", "core_layout") + -+ MEMBER_OFFSET("module_layout", "size"); -+ ASSIGN_OFFSET(module_init_size) = -+ MEMBER_OFFSET("module", "init_layout") + -+ MEMBER_OFFSET("module_layout", "size"); -+ -+ ASSIGN_OFFSET(module_core_text_size) = -+ MEMBER_OFFSET("module", "core_layout") + -+ MEMBER_OFFSET("module_layout", "text_size"); -+ ASSIGN_OFFSET(module_init_text_size) = -+ MEMBER_OFFSET("module", "init_layout") + -+ MEMBER_OFFSET("module_layout", "text_size"); -+ -+ ASSIGN_OFFSET(module_module_core) = -+ MEMBER_OFFSET("module", "core_layout") + -+ MEMBER_OFFSET("module_layout", "base"); -+ ASSIGN_OFFSET(module_module_init) = -+ MEMBER_OFFSET("module", "init_layout") + -+ MEMBER_OFFSET("module_layout", "base"); -+ } -+ - MEMBER_OFFSET_INIT(module_percpu, "module", "percpu"); - - /* --- -2.7.0 - diff -Nru crash-7.1.4/debian/patches/0002-Fix-for-the-changes-made-to-the-kernel-module-struct.patch crash-7.2.3+real/debian/patches/0002-Fix-for-the-changes-made-to-the-kernel-module-struct.patch --- crash-7.1.4/debian/patches/0002-Fix-for-the-changes-made-to-the-kernel-module-struct.patch 2018-07-20 09:16:28.000000000 +0000 +++ crash-7.2.3+real/debian/patches/0002-Fix-for-the-changes-made-to-the-kernel-module-struct.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -From 098cdab16dfa6a85e9dad2cad604dee14ee15f66 Mon Sep 17 00:00:00 2001 -From: Dave Anderson -Date: Fri, 12 Feb 2016 14:32:53 -0500 -Subject: [PATCH] Fix for the changes made to the kernel module structure - introduced by this kernel commit for Linux 4.5 and later kernels: - - commit 8244062ef1e54502ef55f54cced659913f244c3e - modules: fix longstanding /proc/kallsyms vs module insertion race. - -Without the patch, the crash session fails during initialization -with the error message: "crash: invalid structure member offset: -module_num_symtab". -(anderson@redhat.com) ---- - kernel.c | 16 ++++++++++++++++ - 1 file changed, 16 insertions(+) - -Index: crash-7.1.4/kernel.c -=================================================================== ---- crash-7.1.4.orig/kernel.c -+++ crash-7.1.4/kernel.c -@@ -674,6 +674,22 @@ kernel_init() - kt->flags |= KALLSYMS_V2; - } - -+ if (INVALID_MEMBER(module_num_symtab) && -+ MEMBER_EXISTS("module", "core_kallsyms")) { -+ ASSIGN_OFFSET(module_num_symtab) = -+ MEMBER_OFFSET("module", "core_kallsyms") + -+ MEMBER_OFFSET("mod_kallsyms", "num_symtab"); -+ ASSIGN_OFFSET(module_symtab) = -+ MEMBER_OFFSET("module", "core_kallsyms") + -+ MEMBER_OFFSET("mod_kallsyms", "symtab"); -+ ASSIGN_OFFSET(module_strtab) = -+ MEMBER_OFFSET("module", "core_kallsyms") + -+ MEMBER_OFFSET("mod_kallsyms", "strtab"); -+ -+ if (!(kt->flags & NO_KALLSYMS)) -+ kt->flags |= KALLSYMS_V2; -+ } -+ - if (!(kt->flags & DWARF_UNWIND)) - kt->flags |= NO_DWARF_UNWIND; - diff -Nru crash-7.1.4/debian/patches/0003-Fix-for-the-replacements-made-to-the-kernel-s-cpu_po.patch crash-7.2.3+real/debian/patches/0003-Fix-for-the-replacements-made-to-the-kernel-s-cpu_po.patch --- crash-7.1.4/debian/patches/0003-Fix-for-the-replacements-made-to-the-kernel-s-cpu_po.patch 2018-07-20 09:16:28.000000000 +0000 +++ crash-7.2.3+real/debian/patches/0003-Fix-for-the-replacements-made-to-the-kernel-s-cpu_po.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,47 +0,0 @@ -From 1e92f9fad3a7e3042b16996306cb2335760ef8c8 Mon Sep 17 00:00:00 2001 -From: Dave Anderson -Date: Mon, 1 Feb 2016 16:10:49 -0500 -Subject: [PATCH] Fix for the replacements made to the kernel's - cpu_possible_mask, cpu_online_mask, cpu_present_mask and cpu_active_mask - symbols in this kernel commit for Linux 4.5 and later kernels: - - commit 5aec01b834fd6f8ca49d1aeede665b950d0c148e - kernel/cpu.c: eliminate cpu_*_mask - -Without the patch, behavior is architecture-specific, dependent upon -whether the cpu mask values are used to calculate the number of cpus. -For example, ARM64 crash sessions fail during session initialization -with the error message "crash: zero-size memory allocation! (called -from
)", whereas X86_64 sessions come up normally, but -cpu mask values of zero are stored internally. -(anderson@redhat.com) ---- - kernel.c | 8 ++++++-- - 1 file changed, 6 insertions(+), 2 deletions(-) - -Index: crash-7.1.4/kernel.c -=================================================================== ---- crash-7.1.4.orig/kernel.c -+++ crash-7.1.4/kernel.c -@@ -1,8 +1,8 @@ - /* kernel.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 -@@ -793,6 +793,10 @@ cpu_map_addr(const char *type) - return addr; - } - -+ sprintf(map_symbol, "__cpu_%s_mask", type); -+ if (kernel_symbol_exists(map_symbol)) -+ return symbol_value(map_symbol); -+ - return 0; - } - diff -Nru crash-7.1.4/debian/patches/0004-With-the-introduction-of-radix-MMU-in-Power-ISA-3.0-.patch crash-7.2.3+real/debian/patches/0004-With-the-introduction-of-radix-MMU-in-Power-ISA-3.0-.patch --- crash-7.1.4/debian/patches/0004-With-the-introduction-of-radix-MMU-in-Power-ISA-3.0-.patch 2018-07-20 09:16:28.000000000 +0000 +++ crash-7.2.3+real/debian/patches/0004-With-the-introduction-of-radix-MMU-in-Power-ISA-3.0-.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,812 +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(-) - -Index: crash-7.1.4/defs.h -=================================================================== ---- crash-7.1.4.orig/defs.h -+++ crash-7.1.4/defs.h -@@ -3660,7 +3660,7 @@ struct efi_memory_desc_t { - #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 -@@ -3682,7 +3682,11 @@ struct efi_memory_desc_t { - #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 */ -@@ -3693,24 +3697,44 @@ struct efi_memory_desc_t { - #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 */ -@@ -3721,6 +3745,9 @@ struct efi_memory_desc_t { - #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) -@@ -4543,6 +4570,7 @@ int calculate(char *, ulong *, ulonglong - 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); -@@ -5408,14 +5436,13 @@ struct machine_specific { - 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; -@@ -5425,13 +5452,17 @@ struct machine_specific { - 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; -@@ -5445,23 +5476,16 @@ struct machine_specific { - 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) -Index: crash-7.1.4/ppc64.c -=================================================================== ---- crash-7.1.4.orig/ppc64.c -+++ crash-7.1.4/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 uint get_ptetype(ulong pte); - 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 @@ static inline uint get_ptetype(ulong pte - 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 @@ struct machine_specific ppc64_machine_sp - .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 @@ struct machine_specific book3e_machine_s - .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 @@ ppc64_init(int when) - 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 @@ ppc64_init(int when) - 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 @@ ppc64_init(int when) - 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 @@ ppc64_init(int when) - } - 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 @@ ppc64_init(int when) - 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 @@ ppc64_init(int when) - 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 @@ ppc64_dump_machdep_table(ulong arg) - 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 @@ ppc64_dump_machdep_table(ulong arg) - 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 @@ ppc64_dump_machdep_table(ulong arg) - 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 @@ ppc64_vtop(ulong vaddr, ulong *pgd, phys - 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 @@ ppc64_vtop(ulong vaddr, ulong *pgd, phys - 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 @@ ppc64_vtop(ulong vaddr, ulong *pgd, phys - 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 @@ ppc64_vtop_level4(ulong vaddr, ulong *le - 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 @@ ppc64_vtop_level4(ulong vaddr, ulong *le - 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 @@ out: - * 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 @@ out: - 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 @@ ppc64_get_task_pgd(ulong task) - * 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 @@ ppc64_translate_pte(ulong pte, void *phy - 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 @@ ppc64_translate_pte(ulong pte, void *phy - 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 @@ ppc64_init_cpu_info(void) - 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 -Index: crash-7.1.4/tools.c -=================================================================== ---- crash-7.1.4.orig/tools.c -+++ crash-7.1.4/tools.c -@@ -5650,6 +5650,22 @@ swap32(uint32_t val, int swap) - 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.4/debian/patches/0005-Fix-for-Linux-commit-0139aa7b7fa12ceef095d99dc36606a.patch crash-7.2.3+real/debian/patches/0005-Fix-for-Linux-commit-0139aa7b7fa12ceef095d99dc36606a.patch --- crash-7.1.4/debian/patches/0005-Fix-for-Linux-commit-0139aa7b7fa12ceef095d99dc36606a.patch 2018-07-20 09:16:28.000000000 +0000 +++ crash-7.2.3+real/debian/patches/0005-Fix-for-Linux-commit-0139aa7b7fa12ceef095d99dc36606a.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +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(+) - -Index: crash-7.1.4/memory.c -=================================================================== ---- crash-7.1.4.orig/memory.c -+++ crash-7.1.4/memory.c -@@ -433,6 +433,10 @@ vm_init(void) - 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.4/debian/patches/0006-Fix-for-Linux-commit-edf14cdbf9a0e5ab52698ca66d07a76.patch crash-7.2.3+real/debian/patches/0006-Fix-for-Linux-commit-edf14cdbf9a0e5ab52698ca66d07a76.patch --- crash-7.1.4/debian/patches/0006-Fix-for-Linux-commit-edf14cdbf9a0e5ab52698ca66d07a76.patch 2018-07-20 09:16:28.000000000 +0000 +++ crash-7.2.3+real/debian/patches/0006-Fix-for-Linux-commit-edf14cdbf9a0e5ab52698ca66d07a76.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,33 +0,0 @@ -From 7136bf8495948cb059e5595b8503f8ae37019fa1 Mon Sep 17 00:00:00 2001 -From: Dave Anderson -Date: Thu, 19 May 2016 14:01:19 -0400 -Subject: [PATCH] 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. - (andrej.skvortzov@gmail.com) - ---- - memory.c | 7 ++++++- - 1 file changed, 6 insertions(+), 1 deletion(-) - -Index: crash-7.1.4/memory.c -=================================================================== ---- crash-7.1.4.orig/memory.c -+++ crash-7.1.4/memory.c -@@ -6353,8 +6353,13 @@ page_flags_init_from_pageflag_names(void - 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; - } diff -Nru crash-7.1.4/debian/patches/0007-Fix-to-recognize-and-support-x86_64-Linux-4.8-rc1-an.patch crash-7.2.3+real/debian/patches/0007-Fix-to-recognize-and-support-x86_64-Linux-4.8-rc1-an.patch --- crash-7.1.4/debian/patches/0007-Fix-to-recognize-and-support-x86_64-Linux-4.8-rc1-an.patch 2018-07-20 09:16:28.000000000 +0000 +++ crash-7.2.3+real/debian/patches/0007-Fix-to-recognize-and-support-x86_64-Linux-4.8-rc1-an.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,115 +0,0 @@ -From da79c1bffc287309ecebc9fe40f0a3cdf5a5d26d Mon Sep 17 00:00:00 2001 -From: Dave Anderson -Date: Wed, 21 Sep 2016 15:58:22 -0400 -Subject: [PATCH] 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. (anderson@redhat.com) - ---- - defs.h | 2 ++ - main.c | 1 + - x86_64.c | 43 +++++++++++++++++++++++++++++++++++++------ - 3 files changed, 40 insertions(+), 6 deletions(-) - -Index: crash-7.1.4/defs.h -=================================================================== ---- crash-7.1.4.orig/defs.h -+++ crash-7.1.4/defs.h -@@ -1070,6 +1070,7 @@ extern struct machdep_table *machdep; - #define POST_INIT (4) - #define POST_VM (5) - #define LOG_ONLY (6) -+#define POST_RELOC (7) - - #define FOREACH_BT (1) - #define FOREACH_VM (2) -@@ -5354,6 +5355,7 @@ struct machine_specific { - #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) - -Index: crash-7.1.4/main.c -=================================================================== ---- crash-7.1.4.orig/main.c -+++ crash-7.1.4/main.c -@@ -742,6 +742,7 @@ main_loop(void) - - if (!(pc->flags & GDB_INIT)) { - gdb_session_init(); -+ machdep_init(POST_RELOC); - show_untrusted_files(); - kdump_backup_region_init(); - if (XEN_HYPER_MODE()) { -Index: crash-7.1.4/x86_64.c -=================================================================== ---- crash-7.1.4.orig/x86_64.c -+++ crash-7.1.4/x86_64.c -@@ -282,6 +282,23 @@ x86_64_init(int when) - 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 +309,24 @@ x86_64_init(int when) - 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 + (32UL << 40) - 1; -+ 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 + 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; -+ 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 +643,8 @@ x86_64_dump_machdep_table(ulong arg) - 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); diff -Nru crash-7.1.4/debian/patches/0008-Fix-for-a-possible-segmentation-violation-when-analy.patch crash-7.2.3+real/debian/patches/0008-Fix-for-a-possible-segmentation-violation-when-analy.patch --- crash-7.1.4/debian/patches/0008-Fix-for-a-possible-segmentation-violation-when-analy.patch 2018-07-20 09:16:28.000000000 +0000 +++ crash-7.2.3+real/debian/patches/0008-Fix-for-a-possible-segmentation-violation-when-analy.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,33 +0,0 @@ -From 774a92f48fabb0f98eb14875e54c6d5f573f074c Mon Sep 17 00:00:00 2001 -From: Dave Anderson -Date: Tue, 25 Oct 2016 13:32:22 -0400 -Subject: [PATCH] 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. - (anderson@redhat.com) - ---- - x86_64.c | 5 ++++- - 1 file changed, 4 insertions(+), 1 deletion(-) - -diff --git a/x86_64.c b/x86_64.c -index 395cbee..90e9dea 100644 ---- a/x86_64.c -+++ b/x86_64.c -@@ -324,7 +324,10 @@ x86_64_init(int when) - 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; -+ if ((kt->flags2 & KASLR) && (THIS_KERNEL_VERSION >= LINUX(4,7,0))) -+ machdep->machspec->modules_vaddr = __START_KERNEL_map + GIGABYTES(1); -+ else -+ machdep->machspec->modules_vaddr = MODULES_VADDR_2_6_31; - machdep->machspec->modules_end = MODULES_END_2_6_31; - } - } --- -2.11.0 - diff -Nru crash-7.1.4/debian/patches/0009-Fix-for-support-of-Linux-4.7-and-later-x86_64-ELF-kd.patch crash-7.2.3+real/debian/patches/0009-Fix-for-support-of-Linux-4.7-and-later-x86_64-ELF-kd.patch --- crash-7.1.4/debian/patches/0009-Fix-for-support-of-Linux-4.7-and-later-x86_64-ELF-kd.patch 2018-07-20 09:16:28.000000000 +0000 +++ crash-7.2.3+real/debian/patches/0009-Fix-for-support-of-Linux-4.7-and-later-x86_64-ELF-kd.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,96 +0,0 @@ -From 0cdd12115000607b556cff3b48fd1b6438db56bb Mon Sep 17 00:00:00 2001 -From: Dave Anderson -Date: Thu, 27 Oct 2016 15:25:00 -0400 -Subject: [PATCH] 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!". (anderson@redhat.com) - ---- - defs.h | 1 + - x86_64.c | 21 ++++++++++++++++++--- - 2 files changed, 19 insertions(+), 3 deletions(-) - -Index: crash-7.1.4/defs.h -=================================================================== ---- crash-7.1.4.orig/defs.h -+++ crash-7.1.4/defs.h -@@ -5341,6 +5341,7 @@ struct machine_specific { - ulong page_protnone; - ulong GART_start; - ulong GART_end; -+ ulong kernel_image_size; - }; - - #define KSYMS_START (0x1) -Index: crash-7.1.4/x86_64.c -=================================================================== ---- crash-7.1.4.orig/x86_64.c -+++ crash-7.1.4/x86_64.c -@@ -126,6 +126,7 @@ void - x86_64_init(int when) - { - int len, dim; -+ char *string; - - if (XEN_HYPER_MODE()) { - x86_64_init_hyper(when); -@@ -172,6 +173,10 @@ x86_64_init(int when) - 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: -@@ -317,7 +322,9 @@ x86_64_init(int when) - machdep->machspec->vmalloc_start_addr + (32UL << 40) - 1; - 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 + GIGABYTES(1); -+ 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; -@@ -325,7 +332,9 @@ x86_64_init(int when) - 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 + GIGABYTES(1); -+ 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; -@@ -754,6 +763,7 @@ x86_64_dump_machdep_table(ulong arg) - 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); -@@ -6180,11 +6190,16 @@ x86_64_calc_phys_base(void) - 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)); diff -Nru crash-7.1.4/debian/patches/0010-Linux-3.15-and-later-kernels-configured-with-CONFIG_.patch crash-7.2.3+real/debian/patches/0010-Linux-3.15-and-later-kernels-configured-with-CONFIG_.patch --- crash-7.1.4/debian/patches/0010-Linux-3.15-and-later-kernels-configured-with-CONFIG_.patch 2018-07-20 09:16:28.000000000 +0000 +++ crash-7.2.3+real/debian/patches/0010-Linux-3.15-and-later-kernels-configured-with-CONFIG_.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -From cad8f4cddcbc0be1c83d560b158696260b038372 Mon Sep 17 00:00:00 2001 -From: Dave Anderson -Date: Wed, 15 Jun 2016 11:40:07 -0400 -Subject: [PATCH] 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. (anderson@redhat.com) - ---- - symbols.c | 8 ++++---- - 1 file changed, 4 insertions(+), 4 deletions(-) - -Index: crash-7.1.4/symbols.c -=================================================================== ---- crash-7.1.4.orig/symbols.c -+++ crash-7.1.4/symbols.c -@@ -578,7 +578,7 @@ strip_symbol_end(const char *name, char - * 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. -@@ -598,7 +598,7 @@ kaslr_init(void) - 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; - } -@@ -11878,8 +11878,8 @@ numeric_forward(const void *P_x, const v - 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.4/debian/patches/0011-Fix-for-Linux-commit-0100301bfdf56a2a370c7157b5ab0fb.patch crash-7.2.3+real/debian/patches/0011-Fix-for-Linux-commit-0100301bfdf56a2a370c7157b5ab0fb.patch --- crash-7.1.4/debian/patches/0011-Fix-for-Linux-commit-0100301bfdf56a2a370c7157b5ab0fb.patch 2018-07-20 09:16:28.000000000 +0000 +++ crash-7.2.3+real/debian/patches/0011-Fix-for-Linux-commit-0100301bfdf56a2a370c7157b5ab0fb.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ -From 7e0cb8b516788c7ba1ef9f32556df347ba0da187 Mon Sep 17 00:00:00 2001 -From: Dave Anderson -Date: Fri, 21 Oct 2016 14:31:59 -0400 -Subject: [PATCH] Fix for Linux commit - 0100301bfdf56a2a370c7157b5ab0fbf9313e1cd, which rewrote the X86_64 - switch_to() code by embedding the __switch_to() call 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". (anderson@redhat.com) - ---- - x86_64.c | 12 +++++++++--- - 1 file changed, 9 insertions(+), 3 deletions(-) - ---- a/x86_64.c -+++ b/x86_64.c -@@ -5620,19 +5620,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(); -@@ -5650,6 +5654,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(); - diff -Nru crash-7.1.4/debian/patches/0012-Fix-for-the-ps-t-option-in-3.17-and-later-kernels-th.patch crash-7.2.3+real/debian/patches/0012-Fix-for-the-ps-t-option-in-3.17-and-later-kernels-th.patch --- crash-7.1.4/debian/patches/0012-Fix-for-the-ps-t-option-in-3.17-and-later-kernels-th.patch 2018-07-20 09:16:28.000000000 +0000 +++ crash-7.2.3+real/debian/patches/0012-Fix-for-the-ps-t-option-in-3.17-and-later-kernels-th.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,84 +0,0 @@ -From 63f7707d2b534bab2a18c52db41daae7e9c5e505 Mon Sep 17 00:00:00 2001 -From: Dave Anderson -Date: Mon, 11 Jan 2016 15:53:44 -0500 -Subject: [PATCH] Fix for the "ps -t" option in 3.17 and later kernels that - contain commit ccbf62d8a284cf181ac28c8e8407dd077d90dd4b, which changed the - task_struct.start_time member from a struct timespec to a u64. Without the - patch, the "RUN TIME" value is nonsensical. (anderson@redhat.com) - ---- - defs.h | 1 + - task.c | 19 +++++++++++++++---- - 2 files changed, 16 insertions(+), 4 deletions(-) - ---- a/defs.h -+++ b/defs.h -@@ -844,6 +844,7 @@ - #define TIMESPEC (0x1000) - #define NO_TIMESPEC (0x2000) - #define ACTIVE_ONLY (0x4000) -+#define START_TIME_NSECS (0x8000) - - #define TASK_SLUSH (20) - ---- a/task.c -+++ b/task.c -@@ -1,8 +1,8 @@ - /* task.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 -@@ -4019,11 +4019,12 @@ - { - char buf[BUFSIZE]; - -- switch(tt->flags & (TIMESPEC | NO_TIMESPEC)) -+ switch(tt->flags & (TIMESPEC | NO_TIMESPEC | START_TIME_NSECS)) - { - case TIMESPEC: - return TRUE; - case NO_TIMESPEC: -+ case START_TIME_NSECS: - return FALSE; - default: - break; -@@ -4050,6 +4051,11 @@ - - close_tmpfile(); - -+ if ((tt->flags & NO_TIMESPEC) && (SIZE(task_struct_start_time) == 8)) { -+ tt->flags &= ~NO_TIMESPEC; -+ tt->flags |= START_TIME_NSECS; -+ } -+ - return (tt->flags & TIMESPEC ? TRUE : FALSE); - } - -@@ -4059,8 +4065,10 @@ - ulong tmp1, tmp2; - ulonglong wrapped; - -- switch(tt->flags & (TIMESPEC | NO_TIMESPEC)) -+ switch(tt->flags & (TIMESPEC | NO_TIMESPEC | START_TIME_NSECS)) - { -+ case START_TIME_NSECS: -+ start_time /= 1000000000ULL; /* FALLTHROUGH */ - case TIMESPEC: - if ((start_time * (ulonglong)machdep->hz) > current) - return 0; -@@ -6943,6 +6951,9 @@ - if (tt->flags & NO_TIMESPEC) - sprintf(&buf[strlen(buf)], - "%sNO_TIMESPEC", others++ ? "|" : ""); -+ if (tt->flags & START_TIME_NSECS) -+ sprintf(&buf[strlen(buf)], -+ "%sSTART_TIME_NSECS", others++ ? "|" : ""); - if (tt->flags & ACTIVE_ONLY) - sprintf(&buf[strlen(buf)], - "%sACTIVE_ONLY", others++ ? "|" : ""); diff -Nru crash-7.1.4/debian/patches/0013-Fix-for-the-irq-s-option-for-Linux-4.2-and-later-ker.patch crash-7.2.3+real/debian/patches/0013-Fix-for-the-irq-s-option-for-Linux-4.2-and-later-ker.patch --- crash-7.1.4/debian/patches/0013-Fix-for-the-irq-s-option-for-Linux-4.2-and-later-ker.patch 2018-07-20 09:16:28.000000000 +0000 +++ crash-7.2.3+real/debian/patches/0013-Fix-for-the-irq-s-option-for-Linux-4.2-and-later-ker.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,62 +0,0 @@ -From c1eb2b99e2d9201583aac5a664126d83039bddff Mon Sep 17 00:00:00 2001 -From: Dave Anderson -Date: Fri, 8 Apr 2016 12:00:55 -0400 -Subject: [PATCH] Fix for the "irq -s" option for Linux 4.2 and later kernels. - Without the patch, the irq_chip.name string (e.g. "IO-APIC", "PCI-MSI", etc.) - is missing from the display. (rabin.vincent@axis.com) - ---- - defs.h | 1 + - kernel.c | 12 ++++++++---- - symbols.c | 2 ++ - 3 files changed, 11 insertions(+), 4 deletions(-) - ---- a/defs.h -+++ b/defs.h -@@ -1951,6 +1951,7 @@ - long pt_regs_cp0_badvaddr; - long address_space_page_tree; - long page_compound_head; -+ long irq_desc_irq_data; - }; - - struct size_table { /* stash of commonly-used sizes */ ---- a/kernel.c -+++ b/kernel.c -@@ -518,6 +518,7 @@ - if (VALID_STRUCT(irq_data)) { - MEMBER_OFFSET_INIT(irq_data_chip, "irq_data", "chip"); - MEMBER_OFFSET_INIT(irq_data_affinity, "irq_data", "affinity"); -+ MEMBER_OFFSET_INIT(irq_desc_irq_data, "irq_desc", "irq_data"); - } - - STRUCT_SIZE_INIT(irq_cpustat_t, "irq_cpustat_t"); -@@ -6826,10 +6827,13 @@ - readmem(irq_desc_addr + OFFSET(irq_desc_t_chip), KVADDR, - &handler, sizeof(long), "irq_desc chip", - FAULT_ON_ERROR); -- else if (VALID_MEMBER(irq_data_chip)) -- readmem(irq_desc_addr + OFFSET(irq_data_chip), KVADDR, -- &handler, sizeof(long), "irq_data chip", -- FAULT_ON_ERROR); -+ else if (VALID_MEMBER(irq_data_chip)) { -+ tmp = irq_desc_addr + OFFSET(irq_data_chip); -+ if (VALID_MEMBER(irq_desc_irq_data)) -+ tmp += OFFSET(irq_desc_irq_data); -+ readmem(tmp, KVADDR, &handler, sizeof(long), "irq_data chip", -+ FAULT_ON_ERROR); -+ } - - fprintf(fp, "%3d: ", irq); - ---- a/symbols.c -+++ b/symbols.c -@@ -8724,6 +8724,8 @@ - OFFSET(irq_data_chip)); - fprintf(fp, " irq_data_affinity: %ld\n", - OFFSET(irq_data_affinity)); -+ fprintf(fp, " irq_desc_irq_data: %ld\n", -+ OFFSET(irq_desc_irq_data)); - fprintf(fp, " kernel_stat_irqs: %ld\n", - OFFSET(kernel_stat_irqs)); - diff -Nru crash-7.1.4/debian/patches/0014-Improvement-of-the-accuracy-of-the-allocated-objects.patch crash-7.2.3+real/debian/patches/0014-Improvement-of-the-accuracy-of-the-allocated-objects.patch --- crash-7.1.4/debian/patches/0014-Improvement-of-the-accuracy-of-the-allocated-objects.patch 2018-07-20 09:16:28.000000000 +0000 +++ crash-7.2.3+real/debian/patches/0014-Improvement-of-the-accuracy-of-the-allocated-objects.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,281 +0,0 @@ -From 76a71fed90c6304110dbce61d6c833543f2f1ac8 Mon Sep 17 00:00:00 2001 -From: Dave Anderson -Date: Tue, 12 Apr 2016 16:51:50 -0400 -Subject: [PATCH] Improvement of the accuracy of the allocated objects count - for each kmem_cache shown by "kmem -s" in kernels configured with - CONFIG_SLUB. Without the patch, the values under the ALLOCATED column may be - too large because cached per-cpu objects are counted as allocated. - (vinayakm.list@gmail.com) - ---- - defs.h | 1 + - memory.c | 151 +++++++++++++++++++++++++++++++++++++++++++++++++++++++------- - symbols.c | 2 + - 3 files changed, 138 insertions(+), 16 deletions(-) - ---- a/defs.h -+++ b/defs.h -@@ -1952,6 +1952,7 @@ - long address_space_page_tree; - long page_compound_head; - long irq_desc_irq_data; -+ long kmem_cache_node_total_objects; - }; - - struct size_table { /* stash of commonly-used sizes */ ---- a/memory.c -+++ b/memory.c -@@ -279,7 +279,8 @@ - static int get_kmem_cache_list(ulong **); - static int get_kmem_cache_slub_data(long, struct meminfo *); - static ulong compound_head(ulong); --static long count_partial(ulong, struct meminfo *); -+static long count_partial(ulong, struct meminfo *, ulong *); -+static short count_cpu_partial(struct meminfo *, int); - static ulong get_freepointer(struct meminfo *, void *); - static int count_free_objects(struct meminfo *, ulong); - char *is_slab_page(struct meminfo *, char *); -@@ -746,6 +747,8 @@ - "kmem_cache_node", "nr_partial"); - MEMBER_OFFSET_INIT(kmem_cache_node_nr_slabs, - "kmem_cache_node", "nr_slabs"); -+ MEMBER_OFFSET_INIT(kmem_cache_node_total_objects, -+ "kmem_cache_node", "total_objects"); - MEMBER_OFFSET_INIT(kmem_cache_node_partial, - "kmem_cache_node", "partial"); - MEMBER_OFFSET_INIT(kmem_cache_node_full, -@@ -17797,7 +17800,7 @@ - fprintf(fp, b1, si->inuse); - - fprintf(fp, "%8ld %5ld %4ldk\n", -- si->num_slabs * si->objects, -+ si->inuse + si->free, - si->num_slabs, si->slabsize/1024); - } - -@@ -17920,6 +17923,60 @@ - FREEBUF(si->cache_buf); - } - -+static short -+count_cpu_partial(struct meminfo *si, int cpu) -+{ -+ short cpu_partial_inuse, cpu_partial_objects, free_objects; -+ ulong cpu_partial, objects_vaddr; -+ -+ free_objects = 0; -+ -+ if (VALID_MEMBER(kmem_cache_cpu_partial)) { -+ readmem(ULONG(si->cache_buf + OFFSET(kmem_cache_cpu_slab)) + -+ kt->__per_cpu_offset[cpu] + OFFSET(kmem_cache_cpu_partial), -+ KVADDR, &cpu_partial, sizeof(ulong), -+ "kmem_cache_cpu.partial", RETURN_ON_ERROR); -+ -+ while (cpu_partial) { -+ if (!is_page_ptr(cpu_partial, NULL)) { -+ error(INFO, "%s: invalid partial list slab pointer: %lx\n", -+ si->curname, cpu_partial); -+ return 0; -+ } -+ if (!readmem(cpu_partial + OFFSET(page_inuse), KVADDR, &cpu_partial_inuse, -+ sizeof(ushort), "page.inuse", RETURN_ON_ERROR)) -+ return 0; -+ if (cpu_partial_inuse == -1) -+ return 0; -+ if (VALID_MEMBER(page_objects)) { -+ objects_vaddr = cpu_partial + OFFSET(page_objects); -+ if (si->flags & SLAB_BITFIELD) -+ objects_vaddr += sizeof(ushort); -+ if (!readmem(objects_vaddr, KVADDR, -+ &cpu_partial_objects, sizeof(ushort), -+ "page.objects", RETURN_ON_ERROR)) -+ return 0; -+ if (si->flags & SLAB_BITFIELD) { -+ if (__BYTE_ORDER == __LITTLE_ENDIAN) { -+ cpu_partial_objects <<= 1; -+ cpu_partial_objects >>= 1; -+ } -+ if (__BYTE_ORDER == __BIG_ENDIAN) -+ cpu_partial_objects >>= 1; -+ } -+ if (cpu_partial_objects == (ushort)(-1)) -+ return 0; -+ free_objects += -+ cpu_partial_objects - cpu_partial_inuse; -+ } -+ readmem(cpu_partial + OFFSET(page_next), KVADDR, -+ &cpu_partial, sizeof(ulong), "page.next", -+ RETURN_ON_ERROR); -+ } -+ } -+ return free_objects; -+} -+ - /* - * Emulate the total count calculation done by the - * slab_objects() sysfs function in slub.c. -@@ -17928,10 +17985,10 @@ - get_kmem_cache_slub_data(long cmd, struct meminfo *si) - { - int i, n, node; -- ulong total_objects, total_slabs; -- ulong cpu_slab_ptr, node_ptr; -- ulong node_nr_partial, node_nr_slabs; -- int full_slabs, objects; -+ ulong total_objects, total_slabs, free_objects; -+ ulong cpu_slab_ptr, node_ptr, cpu_freelist; -+ ulong node_nr_partial, node_nr_slabs, node_total_objects; -+ int full_slabs, objects, node_total_avail; - long p; - short inuse; - ulong *nodes, *per_cpu; -@@ -17944,10 +18001,11 @@ - nodes = (ulong *)GETBUF(2 * sizeof(ulong) * vt->numnodes); - per_cpu = nodes + vt->numnodes; - -- total_slabs = total_objects = 0; -+ total_slabs = total_objects = free_objects = cpu_freelist = 0; -+ node_total_avail = VALID_MEMBER(kmem_cache_node_total_objects) ? TRUE : FALSE; - - for (i = 0; i < kt->cpus; i++) { -- cpu_slab_ptr = get_cpu_slab_ptr(si, i, NULL); -+ cpu_slab_ptr = get_cpu_slab_ptr(si, i, &cpu_freelist); - - if (!cpu_slab_ptr) - continue; -@@ -17962,7 +18020,19 @@ - KVADDR, &inuse, sizeof(short), - "page inuse", RETURN_ON_ERROR)) - return FALSE; -- total_objects += inuse; -+ if (!cpu_freelist) -+ if (!readmem(cpu_slab_ptr + OFFSET(page_freelist), -+ KVADDR, &cpu_freelist, sizeof(ulong), -+ "page freelist", RETURN_ON_ERROR)) -+ return FALSE; -+ -+ free_objects += -+ count_free_objects(si, cpu_freelist); -+ free_objects += count_cpu_partial(si, i); -+ -+ if (!node_total_avail) -+ total_objects += inuse; -+ total_slabs++; - break; - - case GET_SLUB_SLABS: -@@ -17993,13 +18063,21 @@ - KVADDR, &node_nr_slabs, sizeof(ulong), - "kmem_cache_node nr_slabs", RETURN_ON_ERROR)) - goto bailout; -+ if (node_total_avail) { -+ if (!readmem(node_ptr + OFFSET(kmem_cache_node_total_objects), -+ KVADDR, &node_total_objects, sizeof(ulong), -+ "kmem_cache_node total_objects", RETURN_ON_ERROR)) -+ goto bailout; -+ } - - switch (cmd) - { - case GET_SLUB_OBJECTS: -- if ((p = count_partial(node_ptr, si)) < 0) -+ if ((p = count_partial(node_ptr, si, &free_objects)) < 0) - return FALSE; -- total_objects += p; -+ if (!node_total_avail) -+ total_objects += p; -+ total_slabs += node_nr_partial; - break; - - case GET_SLUB_SLABS: -@@ -18013,7 +18091,11 @@ - switch (cmd) - { - case GET_SLUB_OBJECTS: -- total_objects += (full_slabs * objects); -+ if (node_total_avail) -+ total_objects += node_total_objects; -+ else -+ total_objects += (full_slabs * objects); -+ total_slabs += full_slabs; - break; - - case GET_SLUB_SLABS: -@@ -18028,7 +18110,14 @@ - switch (cmd) - { - case GET_SLUB_OBJECTS: -- si->inuse = total_objects; -+ if (!node_total_avail) -+ si->inuse = total_objects; -+ else -+ si->inuse = total_objects - free_objects; -+ if (VALID_MEMBER(page_objects) && node_total_avail) -+ si->free = free_objects; -+ else -+ si->free = (total_slabs * si->objects) - si->inuse; - break; - - case GET_SLUB_SLABS: -@@ -18595,10 +18684,10 @@ - } - - long --count_partial(ulong node, struct meminfo *si) -+count_partial(ulong node, struct meminfo *si, ulong *free) - { -- ulong list_head, next, last; -- short inuse; -+ ulong list_head, next, last, objects_vaddr; -+ short inuse, objects; - ulong total_inuse; - ulong count = 0; - -@@ -18626,6 +18715,36 @@ - break; - } - total_inuse += inuse; -+ -+ if (VALID_MEMBER(page_objects)) { -+ objects_vaddr = last + OFFSET(page_objects); -+ if (si->flags & SLAB_BITFIELD) -+ objects_vaddr += sizeof(ushort); -+ if (!readmem(objects_vaddr, KVADDR, &objects, -+ sizeof(ushort), "page.objects", RETURN_ON_ERROR)) { -+ hq_close(); -+ return -1; -+ } -+ -+ if (si->flags & SLAB_BITFIELD) { -+ if (__BYTE_ORDER == __LITTLE_ENDIAN) { -+ objects <<= 1; -+ objects >>= 1; -+ } -+ if (__BYTE_ORDER == __BIG_ENDIAN) -+ objects >>= 1; -+ } -+ -+ if (objects == (ushort)(-1)) { -+ error(INFO, "%s: slab: %lx invalid page.objects: -1\n", -+ si->curname, last); -+ hq_close(); -+ return -1; -+ } -+ -+ *free += objects - inuse; -+ } -+ - if (!readmem(next, KVADDR, &next, sizeof(ulong), - "page.lru.next", RETURN_ON_ERROR)) { - hq_close(); ---- a/symbols.c -+++ b/symbols.c -@@ -9056,6 +9056,8 @@ - OFFSET(kmem_cache_node_partial)); - fprintf(fp, " kmem_cache_node_full: %ld\n", - OFFSET(kmem_cache_node_full)); -+ fprintf(fp, " kmem_cache_node_total_objects: %ld\n", -+ OFFSET(kmem_cache_node_total_objects)); - - fprintf(fp, " kmem_cache_cpu_freelist: %ld\n", - OFFSET(kmem_cache_cpu_freelist)); diff -Nru crash-7.1.4/debian/patches/0015-When-reading-a-task-s-task_struct.flags-field-check-.patch crash-7.2.3+real/debian/patches/0015-When-reading-a-task-s-task_struct.flags-field-check-.patch --- crash-7.1.4/debian/patches/0015-When-reading-a-task-s-task_struct.flags-field-check-.patch 2018-07-20 09:16:28.000000000 +0000 +++ crash-7.2.3+real/debian/patches/0015-When-reading-a-task-s-task_struct.flags-field-check-.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,61 +0,0 @@ -From 569002249b1d57162a1e94f529d295828d4e0253 Mon Sep 17 00:00:00 2001 -From: Dave Anderson -Date: Mon, 25 Apr 2016 16:06:52 -0400 -Subject: [PATCH] When reading a task's task_struct.flags field, check for its - size, which was changed from an unsigned long to an unsigned int. - (dave.kleikamp@oracle.com) - ---- - defs.h | 1 + - symbols.c | 1 + - task.c | 12 ++++++++++-- - 3 files changed, 12 insertions(+), 2 deletions(-) - ---- a/defs.h -+++ b/defs.h -@@ -2094,6 +2094,7 @@ - long hrtimer_base; - long tnt; - long trace_print_flags; -+ long task_struct_flags; - }; - - struct array_table { ---- a/symbols.c -+++ b/symbols.c -@@ -9849,6 +9849,7 @@ - SIZE(page_cache_bucket)); - fprintf(fp, " pt_regs: %ld\n", SIZE(pt_regs)); - fprintf(fp, " task_struct: %ld\n", SIZE(task_struct)); -+ fprintf(fp, " task_struct_flags: %ld\n", SIZE(task_struct_flags)); - fprintf(fp, " thread_info: %ld\n", SIZE(thread_info)); - fprintf(fp, " softirq_state: %ld\n", - SIZE(softirq_state)); ---- a/task.c -+++ b/task.c -@@ -249,6 +249,7 @@ - MEMBER_OFFSET_INIT(task_struct_active_mm, "task_struct", "active_mm"); - MEMBER_OFFSET_INIT(task_struct_next_run, "task_struct", "next_run"); - MEMBER_OFFSET_INIT(task_struct_flags, "task_struct", "flags"); -+ MEMBER_SIZE_INIT(task_struct_flags, "task_struct", "flags"); - MEMBER_OFFSET_INIT(task_struct_pidhash_next, - "task_struct", "pidhash_next"); - MEMBER_OFFSET_INIT(task_struct_pgrp, "task_struct", "pgrp"); -@@ -5266,8 +5267,15 @@ - - fill_task_struct(task); - -- flags = tt->last_task_read ? -- ULONG(tt->task_struct + OFFSET(task_struct_flags)) : 0; -+ if (tt->last_task_read) { -+ if (SIZE(task_struct_flags) == sizeof(unsigned int)) -+ flags = UINT(tt->task_struct + -+ OFFSET(task_struct_flags)); -+ else -+ flags = ULONG(tt->task_struct + -+ OFFSET(task_struct_flags)); -+ } else -+ flags = 0; - - return flags; - } diff -Nru crash-7.1.4/debian/patches/0016-Fix-for-Linux-4.8-rc1-commit-500462a9de657f86edaa102.patch crash-7.2.3+real/debian/patches/0016-Fix-for-Linux-4.8-rc1-commit-500462a9de657f86edaa102.patch --- crash-7.1.4/debian/patches/0016-Fix-for-Linux-4.8-rc1-commit-500462a9de657f86edaa102.patch 2018-07-20 09:16:28.000000000 +0000 +++ crash-7.2.3+real/debian/patches/0016-Fix-for-Linux-4.8-rc1-commit-500462a9de657f86edaa102.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,346 +0,0 @@ -From 10192898cf59b7b4bb102ef39c72ab65bd401471 Mon Sep 17 00:00:00 2001 -From: Dave Anderson -Date: Mon, 26 Sep 2016 11:41:31 -0400 -Subject: [PATCH] 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
)" (anderson@redhat.com) - ---- - defs.h | 3 + - kernel.c | 231 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- - symbols.c | 4 ++ - 3 files changed, 235 insertions(+), 3 deletions(-) - ---- a/defs.h -+++ b/defs.h -@@ -628,6 +628,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) -@@ -1953,6 +1954,7 @@ - long page_compound_head; - long irq_desc_irq_data; - long kmem_cache_node_total_objects; -+ long timer_base_vectors; - }; - - struct size_table { /* stash of commonly-used sizes */ -@@ -2095,6 +2097,7 @@ - long tnt; - long trace_print_flags; - long task_struct_flags; -+ long timer_base; - }; - - struct array_table { ---- a/kernel.c -+++ b/kernel.c -@@ -46,10 +46,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 *); -@@ -563,7 +566,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 -@@ -5632,6 +5639,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); -@@ -7533,7 +7542,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) { -@@ -7839,6 +7851,7 @@ - fprintf(fp, "TVEC_BASES[%d]: [OFFLINE]\n", cpu); - if (++cpu < kt->cpus) - goto next_cpu; -+ return; - } - - -@@ -7980,6 +7993,7 @@ - fprintf(fp, "TVEC_BASES[%d]: [OFFLINE]\n", cpu); - if (++cpu < kt->cpus) - goto next_cpu; -+ return; - } - - count = 0; -@@ -8420,6 +8434,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; -+ 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) { -+ data->timers = (struct timer_data *) -+ resizebuf((char *)data->timers, -+ data->total, data->total * 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(): - * ---- a/symbols.c -+++ b/symbols.c -@@ -9152,6 +9152,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)); -@@ -10042,6 +10044,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)); - diff -Nru crash-7.1.4/debian/patches/0017-Improvement-of-the-dev-d-option-to-display-I-O-stati.patch crash-7.2.3+real/debian/patches/0017-Improvement-of-the-dev-d-option-to-display-I-O-stati.patch --- crash-7.1.4/debian/patches/0017-Improvement-of-the-dev-d-option-to-display-I-O-stati.patch 2018-07-20 09:16:28.000000000 +0000 +++ crash-7.2.3+real/debian/patches/0017-Improvement-of-the-dev-d-option-to-display-I-O-stati.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,202 +0,0 @@ -From df08978f31ba39e94b3096804f4e0776373c8b53 Mon Sep 17 00:00:00 2001 -From: Dave Anderson -Date: Wed, 12 Oct 2016 11:28:40 -0400 -Subject: [PATCH] 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. (m.mizuma@jp.fujitsu.com) - ---- - defs.h | 4 +++ - dev.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++------ - help.c | 4 ++- - symbols.c | 8 ++++++ - 4 files changed, 104 insertions(+), 10 deletions(-) - ---- a/defs.h -+++ b/defs.h -@@ -1955,6 +1955,10 @@ - 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; - }; - - struct size_table { /* stash of commonly-used sizes */ ---- a/dev.c -+++ b/dev.c -@@ -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"); ---- a/help.c -+++ b/help.c -@@ -2683,7 +2683,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", ---- a/symbols.c -+++ b/symbols.c -@@ -9634,6 +9634,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", diff -Nru crash-7.1.4/debian/patches/0018-Introduction-of-a-new-bt-v-option-that-checks-the-ke.patch crash-7.2.3+real/debian/patches/0018-Introduction-of-a-new-bt-v-option-that-checks-the-ke.patch --- crash-7.1.4/debian/patches/0018-Introduction-of-a-new-bt-v-option-that-checks-the-ke.patch 2018-07-20 09:16:28.000000000 +0000 +++ crash-7.2.3+real/debian/patches/0018-Introduction-of-a-new-bt-v-option-that-checks-the-ke.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,247 +0,0 @@ -From db552975315fec06a957c937803935d8fbddfd2d Mon Sep 17 00:00:00 2001 -From: Dave Anderson -Date: Wed, 22 Jun 2016 15:28:11 -0400 -Subject: [PATCH] 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. - (anderson@redhat.com) - ---- - defs.h | 2 + - help.c | 12 +++++- - kernel.c | 8 +++- - task.c | 126 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - 4 files changed, 146 insertions(+), 2 deletions(-) - ---- a/defs.h -+++ b/defs.h -@@ -828,6 +828,7 @@ - ulong tgid_cache_hits; - long filepages; - long anonpages; -+ ulong stack_end_magic; - }; - - #define TASK_INIT_DONE (0x1) -@@ -4984,6 +4985,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 ---- a/help.c -+++ b/help.c -@@ -1745,7 +1745,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", -@@ -1783,6 +1783,10 @@ - " 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.", -+" -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 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", -@@ -2067,6 +2071,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 - }; - ---- a/kernel.c -+++ b/kernel.c -@@ -2329,7 +2329,7 @@ - if (kt->flags & USE_OLD_BT) - bt->flags |= BT_OLD_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': -@@ -2506,6 +2506,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') { ---- a/task.c -+++ b/task.c -@@ -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 -@@ -514,6 +515,8 @@ - if (pc->flags & SILENT) - initialize_task_state(); - -+ stack_overflow_check_init(); -+ - tt->flags |= TASK_INIT_DONE; - } - -@@ -6993,6 +6996,7 @@ - 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); - - - wrap = sizeof(void *) == SIZEOF_32BIT ? 8 : 4; -@@ -10062,4 +10066,126 @@ - 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 magic; -+ -+ if (!(tt->flags & THREAD_INFO)) -+ return; -+ -+ for (pid = 1; pid < 10; pid++) { -+ if (!(tc = pid_to_context(pid))) -+ continue; -+ -+ if (!readmem(tc->thread_info + SIZE(thread_info), 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; -+ 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++) { -+ if (!readmem(tc->thread_info, KVADDR, buf, -+ SIZE(thread_info) + sizeof(ulong), -+ "stack overflow check", RETURN_ON_ERROR)) -+ continue; -+ -+ overflow = 0; -+ -+ 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++; -+ } -+ } - -+ if (!tt->stack_end_magic) -+ continue; -+ -+ 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.4/debian/patches/0019-Fix-for-Linux-4.9-rc1-commits-15f4eae70d365bba26854c.patch crash-7.2.3+real/debian/patches/0019-Fix-for-Linux-4.9-rc1-commits-15f4eae70d365bba26854c.patch --- crash-7.1.4/debian/patches/0019-Fix-for-Linux-4.9-rc1-commits-15f4eae70d365bba26854c.patch 2018-07-20 09:16:28.000000000 +0000 +++ crash-7.2.3+real/debian/patches/0019-Fix-for-Linux-4.9-rc1-commits-15f4eae70d365bba26854c.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,228 +0,0 @@ -From c9f932440bd06f0a3d6d3ecc30d5c670021d5e5a Mon Sep 17 00:00:00 2001 -From: Dave Anderson -Date: Thu, 20 Oct 2016 11:47:08 -0400 -Subject: [PATCH] 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". (anderson@redhat.com) - ---- - defs.h | 2 ++ - symbols.c | 2 ++ - task.c | 108 ++++++++++++++++++++++++++++++++++++++++++++++---------------- - 3 files changed, 84 insertions(+), 28 deletions(-) - ---- a/defs.h -+++ b/defs.h -@@ -847,6 +847,7 @@ - #define NO_TIMESPEC (0x2000) - #define ACTIVE_ONLY (0x4000) - #define START_TIME_NSECS (0x8000) -+#define THREAD_INFO_IN_TASK (0x10000) - - #define TASK_SLUSH (20) - -@@ -1960,6 +1961,7 @@ - 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 */ ---- a/symbols.c -+++ b/symbols.c -@@ -8250,6 +8250,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", ---- a/task.c -+++ b/task.c -@@ -213,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; - } -@@ -2355,10 +2377,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)) -@@ -4474,7 +4502,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)); -@@ -6953,6 +6987,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++ ? "|" : ""); -@@ -10073,7 +10110,7 @@ - { - int pid; - struct task_context *tc; -- ulong magic; -+ ulong location, magic; - - if (!(tt->flags & THREAD_INFO)) - return; -@@ -10082,9 +10119,13 @@ - if (!(tc = pid_to_context(pid))) - continue; - -- if (!readmem(tc->thread_info + SIZE(thread_info), KVADDR, -- &magic, sizeof(long), "stack magic", -- RETURN_ON_ERROR|QUIET)) -+ 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) { -@@ -10103,7 +10144,7 @@ - { - int i, overflow, cpu_size, cpu, total; - char buf[BUFSIZE]; -- ulong magic, task; -+ ulong magic, task, stackbase; - struct task_context *tc; - - if (!tt->stack_end_magic && -@@ -10116,13 +10157,20 @@ - - tc = FIRST_CONTEXT(); - for (i = total = 0; i < RUNNING_TASKS(); i++, tc++) { -- if (!readmem(tc->thread_info, KVADDR, buf, -- SIZE(thread_info) + sizeof(ulong), -- "stack overflow check", RETURN_ON_ERROR)) -- continue; -- - 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) { -@@ -10160,10 +10208,14 @@ - } - } - -+check_stack_end_magic: - if (!tt->stack_end_magic) - continue; - -- magic = ULONG(buf + SIZE(thread_info)); -+ 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")) { diff -Nru crash-7.1.4/debian/patches/0020-Fix-for-Linux-4.10-commit-7fd8329ba502ef76dd91db561c.patch crash-7.2.3+real/debian/patches/0020-Fix-for-Linux-4.10-commit-7fd8329ba502ef76dd91db561c.patch --- crash-7.1.4/debian/patches/0020-Fix-for-Linux-4.10-commit-7fd8329ba502ef76dd91db561c.patch 2018-07-20 09:16:28.000000000 +0000 +++ crash-7.2.3+real/debian/patches/0020-Fix-for-Linux-4.10-commit-7fd8329ba502ef76dd91db561c.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,255 +0,0 @@ -From 24a696228c56fd4354d29abe05b206373e0c8bfb Mon Sep 17 00:00:00 2001 -From: Dave Anderson -Date: Thu, 5 Jan 2017 14:55:18 -0500 -Subject: [PATCH] Fix for Linux 4.10 commit - 7fd8329ba502ef76dd91db561c7aed696b2c7720 "taint/module: Clean up global and - module taint flags handling". Without the patch, when running against Linux - 4.10-rc1 and later kernels, the crash utility fails during session - initialization with the message "crash: invalid structure size: tnt". - (panand@redhat.com) - ---- - defs.h | 4 +- - kernel.c | 158 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- - symbols.c | 5 +- - 3 files changed, 164 insertions(+), 3 deletions(-) - ---- a/defs.h -+++ b/defs.h -@@ -1962,6 +1962,7 @@ - long blk_mq_ctx_rq_dispatched; - long blk_mq_ctx_rq_completed; - long task_struct_stack; -+ long tnt_mod; - }; - - struct size_table { /* stash of commonly-used sizes */ -@@ -2105,6 +2106,7 @@ - long trace_print_flags; - long task_struct_flags; - long timer_base; -+ long taint_flag; - }; - - struct array_table { ---- a/kernel.c -+++ b/kernel.c -@@ -2,7 +2,7 @@ - * - * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. - * Copyright (C) 2002-2016 David Anderson -- * Copyright (C) 2002-2016 Red Hat, Inc. All rights reserved. -+ * Copyright (C) 2002-2017 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 -@@ -20,6 +20,7 @@ - #include - #include - #include -+#include - - static void do_module_cmd(ulong, char *, ulong, char *, char *); - static void show_module_taint(void); -@@ -3942,6 +3943,99 @@ - } - - static void -+show_module_taint_4_10(void) -+{ -+ int i, j, bx; -+ struct load_module *lm; -+ int maxnamelen; -+ int found; -+ char buf1[BUFSIZE]; -+ char buf2[BUFSIZE]; -+ struct syment *sp; -+ uint *taintsp, taints; -+ bool tnt_mod; -+ char tnt_true; -+ int tnts_len; -+ ulong tnts_addr; -+ char *modbuf; -+ -+ if (INVALID_MEMBER(module_taints)) { -+ MEMBER_OFFSET_INIT(module_taints, "module", "taints"); -+ STRUCT_SIZE_INIT(taint_flag, "taint_flag"); -+ MEMBER_OFFSET_INIT(tnt_true, "taint_flag", "true"); -+ MEMBER_OFFSET_INIT(tnt_mod, "taint_flag", "module"); -+ } -+ -+ modbuf = GETBUF(SIZE(module)); -+ -+ for (i = found = maxnamelen = 0; i < kt->mods_installed; i++) { -+ lm = &st->load_modules[i]; -+ -+ readmem(lm->module_struct, KVADDR, modbuf, SIZE(module), -+ "module struct", FAULT_ON_ERROR); -+ -+ taints = ULONG(modbuf + OFFSET(module_taints)); -+ -+ if (taints) { -+ found++; -+ maxnamelen = strlen(lm->mod_name) > maxnamelen ? -+ strlen(lm->mod_name) : maxnamelen; -+ } -+ } -+ -+ if (!found) { -+ fprintf(fp, "no tainted modules\n"); -+ FREEBUF(modbuf); -+ return; -+ } -+ -+ tnts_len = get_array_length("taint_flags", NULL, 0); -+ sp = symbol_search("taint_flags"); -+ tnts_addr = sp->value; -+ -+ fprintf(fp, "%s %s\n", -+ mkstring(buf2, maxnamelen, LJUST, "NAME"), "TAINTS"); -+ -+ for (i = 0; i < st->mods_installed; i++) { -+ -+ lm = &st->load_modules[i]; -+ bx = 0; -+ buf1[0] = '\0'; -+ -+ readmem(lm->module_struct, KVADDR, modbuf, SIZE(module), -+ "module struct", FAULT_ON_ERROR); -+ -+ taints = ULONG(modbuf + OFFSET(module_taints)); -+ if (!taints) -+ continue; -+ taintsp = &taints; -+ -+ for (j = 0; j < tnts_len; j++) { -+ readmem((tnts_addr + j * SIZE(taint_flag)) + -+ OFFSET(tnt_mod), -+ KVADDR, &tnt_mod, sizeof(bool), -+ "tnt mod", FAULT_ON_ERROR); -+ if (!tnt_mod) -+ continue; -+ if (NUM_IN_BITMAP(taintsp, j)) { -+ readmem((tnts_addr + j * SIZE(taint_flag)) + -+ OFFSET(tnt_true), -+ KVADDR, &tnt_true, sizeof(char), -+ "tnt true", FAULT_ON_ERROR); -+ buf1[bx++] = tnt_true; -+ } -+ } -+ -+ buf1[bx++] = '\0'; -+ -+ fprintf(fp, "%s %s\n", mkstring(buf2, maxnamelen, -+ LJUST, lm->mod_name), buf1); -+ } -+ -+ FREEBUF(modbuf); -+} -+ -+static void - show_module_taint(void) - { - int i, j, bx; -@@ -3959,6 +4053,12 @@ - ulong tnts_addr; - char *modbuf; - -+ if (VALID_STRUCT(taint_flag) || -+ (kernel_symbol_exists("taint_flags") && STRUCT_EXISTS("taint_flag"))) { -+ show_module_taint_4_10(); -+ return; -+ } -+ - if (INVALID_MEMBER(module_taints) && - INVALID_MEMBER(module_license_gplok)) { - MEMBER_OFFSET_INIT(module_taints, "module", "taints"); -@@ -10335,6 +10435,56 @@ - } - - static void -+show_kernel_taints_v4_10(char *buf, int verbose) -+{ -+ int i, bx; -+ char tnt_true, tnt_false; -+ int tnts_len; -+ ulong tnts_addr; -+ ulong tainted_mask, *tainted_mask_ptr; -+ struct syment *sp; -+ -+ if (!VALID_STRUCT(taint_flag)) { -+ STRUCT_SIZE_INIT(taint_flag, "taint_flag"); -+ MEMBER_OFFSET_INIT(tnt_true, "taint_flag", "true"); -+ MEMBER_OFFSET_INIT(tnt_false, "taint_flag", "false"); -+ } -+ -+ tnts_len = get_array_length("taint_flags", NULL, 0); -+ sp = symbol_search("taint_flags"); -+ tnts_addr = sp->value; -+ -+ bx = 0; -+ buf[0] = '\0'; -+ -+ get_symbol_data("tainted_mask", sizeof(ulong), &tainted_mask); -+ tainted_mask_ptr = &tainted_mask; -+ -+ for (i = 0; i < tnts_len; i++) { -+ if (NUM_IN_BITMAP(tainted_mask_ptr, i)) { -+ readmem((tnts_addr + i * SIZE(taint_flag)) + -+ OFFSET(tnt_true), -+ KVADDR, &tnt_true, sizeof(char), -+ "tnt true", FAULT_ON_ERROR); -+ buf[bx++] = tnt_true; -+ } else { -+ readmem((tnts_addr + i * SIZE(taint_flag)) + -+ OFFSET(tnt_false), -+ KVADDR, &tnt_false, sizeof(char), -+ "tnt false", FAULT_ON_ERROR); -+ if (tnt_false != ' ' && tnt_false != '-' && -+ tnt_false != 'G') -+ buf[bx++] = tnt_false; -+ } -+ } -+ -+ buf[bx++] = '\0'; -+ -+ if (verbose) -+ fprintf(fp, "TAINTED_MASK: %lx %s\n", tainted_mask, buf); -+} -+ -+static void - show_kernel_taints(char *buf, int verbose) - { - int i, bx; -@@ -10346,6 +10496,12 @@ - int tainted; - struct syment *sp; - -+ if (VALID_STRUCT(taint_flag) || -+ (kernel_symbol_exists("taint_flags") && STRUCT_EXISTS("taint_flag"))) { -+ show_kernel_taints_v4_10(buf, verbose); -+ return; -+ } -+ - if (!VALID_STRUCT(tnt)) { - STRUCT_SIZE_INIT(tnt, "tnt"); - MEMBER_OFFSET_INIT(tnt_bit, "tnt", "bit"); ---- a/symbols.c -+++ b/symbols.c -@@ -8607,6 +8607,7 @@ - fprintf(fp, " tnt_bit: %ld\n", OFFSET(tnt_bit)); - fprintf(fp, " tnt_true: %ld\n", OFFSET(tnt_true)); - fprintf(fp, " tnt_false: %ld\n", OFFSET(tnt_false)); -+ fprintf(fp, " tnt_mod: %ld\n", OFFSET(tnt_mod)); - - fprintf(fp, " page_next: %ld\n", OFFSET(page_next)); - fprintf(fp, " page_prev: %ld\n", OFFSET(page_prev)); -@@ -10058,6 +10059,8 @@ - SIZE(timer_base)); - fprintf(fp, " tnt: %ld\n", - SIZE(tnt)); -+ fprintf(fp, " taint_flag: %ld\n", -+ SIZE(taint_flag)); - - fprintf(fp, "\n array_table:\n"); - /* diff -Nru crash-7.1.4/debian/patches/0021-Prepare-for-the-kernel-s-taint_flag.true-and-taint_f.patch crash-7.2.3+real/debian/patches/0021-Prepare-for-the-kernel-s-taint_flag.true-and-taint_f.patch --- crash-7.1.4/debian/patches/0021-Prepare-for-the-kernel-s-taint_flag.true-and-taint_f.patch 2018-07-20 09:16:28.000000000 +0000 +++ crash-7.2.3+real/debian/patches/0021-Prepare-for-the-kernel-s-taint_flag.true-and-taint_f.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ -From e37dd7852f590e3334420e5b41a833085ab1d0b0 Mon Sep 17 00:00:00 2001 -From: Dave Anderson -Date: Tue, 17 Jan 2017 14:15:11 -0500 -Subject: [PATCH] Prepare for the kernel's "taint_flag.true" and - "taint_flag.false" member names to be changed to "c_true" and "c_false", - which fixes build problems when an out-of-tree module defines "true" or - "false". (anderson@redhat.com) - ---- - kernel.c | 6 ++++++ - 1 file changed, 6 insertions(+) - ---- a/kernel.c -+++ b/kernel.c -@@ -3963,6 +3963,8 @@ - MEMBER_OFFSET_INIT(module_taints, "module", "taints"); - STRUCT_SIZE_INIT(taint_flag, "taint_flag"); - MEMBER_OFFSET_INIT(tnt_true, "taint_flag", "true"); -+ if (INVALID_MEMBER(tnt_true)) -+ MEMBER_OFFSET_INIT(tnt_true, "taint_flag", "c_true"); - MEMBER_OFFSET_INIT(tnt_mod, "taint_flag", "module"); - } - -@@ -10448,6 +10450,10 @@ - STRUCT_SIZE_INIT(taint_flag, "taint_flag"); - MEMBER_OFFSET_INIT(tnt_true, "taint_flag", "true"); - MEMBER_OFFSET_INIT(tnt_false, "taint_flag", "false"); -+ if (INVALID_MEMBER(tnt_true)) { -+ MEMBER_OFFSET_INIT(tnt_true, "taint_flag", "c_true"); -+ MEMBER_OFFSET_INIT(tnt_false, "taint_flag", "c_false"); -+ } - } - - tnts_len = get_array_length("taint_flags", NULL, 0); diff -Nru crash-7.1.4/debian/patches/0022-Prevent-the-livepatch-taint-flag-check-during-the-sy.patch crash-7.2.3+real/debian/patches/0022-Prevent-the-livepatch-taint-flag-check-during-the-sy.patch --- crash-7.1.4/debian/patches/0022-Prevent-the-livepatch-taint-flag-check-during-the-sy.patch 2018-07-20 09:16:28.000000000 +0000 +++ crash-7.2.3+real/debian/patches/0022-Prevent-the-livepatch-taint-flag-check-during-the-sy.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,42 +0,0 @@ -From 651c824ffe2fd7eb8e416dbf4ff5c9a1da6ef7c6 Mon Sep 17 00:00:00 2001 -From: Dave Anderson -Date: Thu, 19 Jan 2017 14:15:50 -0500 -Subject: [PATCH] Prevent the livepatch taint flag check during the system - banner display from generating a fatal session-killing error if relevant - kernel symbol names or data structures change in the future (again). - (anderson@redhat.com) - ---- - kernel.c | 17 ++++++++++++++--- - 1 file changed, 14 insertions(+), 3 deletions(-) - ---- a/kernel.c -+++ b/kernel.c -@@ -10456,13 +10456,24 @@ - } - } - -+ bx = 0; -+ buf[0] = '\0'; -+ -+ /* -+ * Make sure that all dependencies are valid to prevent -+ * a fatal error from killing the session during the -+ * pre-RUNTIME system banner display. -+ */ -+ if (!(pc->flags & RUNTIME)) { -+ if (INVALID_MEMBER(tnt_true) || INVALID_MEMBER(tnt_false) || -+ !kernel_symbol_exists("tainted_mask")) -+ return; -+ } -+ - tnts_len = get_array_length("taint_flags", NULL, 0); - sp = symbol_search("taint_flags"); - tnts_addr = sp->value; - -- bx = 0; -- buf[0] = '\0'; -- - get_symbol_data("tainted_mask", sizeof(ulong), &tainted_mask); - tainted_mask_ptr = &tainted_mask; - diff -Nru crash-7.1.4/debian/patches/series crash-7.2.3+real/debian/patches/series --- crash-7.1.4/debian/patches/series 2018-07-20 09:16:28.000000000 +0000 +++ crash-7.2.3+real/debian/patches/series 2018-06-22 21:39:16.000000000 +0000 @@ -1,22 +1 @@ -0001-Fix-for-the-changes-made-to-the-kernel-module-struct.patch -0002-Fix-for-the-changes-made-to-the-kernel-module-struct.patch -0003-Fix-for-the-replacements-made-to-the-kernel-s-cpu_po.patch -0004-With-the-introduction-of-radix-MMU-in-Power-ISA-3.0-.patch -0005-Fix-for-Linux-commit-0139aa7b7fa12ceef095d99dc36606a.patch -0006-Fix-for-Linux-commit-edf14cdbf9a0e5ab52698ca66d07a76.patch -0007-Fix-to-recognize-and-support-x86_64-Linux-4.8-rc1-an.patch -0008-Fix-for-a-possible-segmentation-violation-when-analy.patch -0009-Fix-for-support-of-Linux-4.7-and-later-x86_64-ELF-kd.patch -0010-Linux-3.15-and-later-kernels-configured-with-CONFIG_.patch -0011-Fix-for-Linux-commit-0100301bfdf56a2a370c7157b5ab0fb.patch -0012-Fix-for-the-ps-t-option-in-3.17-and-later-kernels-th.patch -0013-Fix-for-the-irq-s-option-for-Linux-4.2-and-later-ker.patch -0014-Improvement-of-the-accuracy-of-the-allocated-objects.patch -0015-When-reading-a-task-s-task_struct.flags-field-check-.patch -0016-Fix-for-Linux-4.8-rc1-commit-500462a9de657f86edaa102.patch -0017-Improvement-of-the-dev-d-option-to-display-I-O-stati.patch -0018-Introduction-of-a-new-bt-v-option-that-checks-the-ke.patch -0019-Fix-for-Linux-4.9-rc1-commits-15f4eae70d365bba26854c.patch -0020-Fix-for-Linux-4.10-commit-7fd8329ba502ef76dd91db561c.patch -0021-Prepare-for-the-kernel-s-taint_flag.true-and-taint_f.patch -0022-Prevent-the-livepatch-taint-flag-check-during-the-sy.patch +0001-dont-git-clone-eppic-extension.patch diff -Nru crash-7.1.4/debian/rules crash-7.2.3+real/debian/rules --- crash-7.1.4/debian/rules 2018-07-20 09:16:28.000000000 +0000 +++ crash-7.2.3+real/debian/rules 2018-06-22 21:39:16.000000000 +0000 @@ -3,19 +3,17 @@ # 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 -DEB_HOST_ARCH := $(shell dpkg-architecture -qDEB_HOST_ARCH) - %: - dh $@ --with quilt + dh $@ override_dh_auto_build: - dh_auto_build - $(MAKE) extensions + dh_auto_build + $(MAKE) extensions lzo snappy override_dh_auto_clean: cp $(CURDIR)/Makefile $(CURDIR)/debian/Makefile.ori @@ -29,6 +27,9 @@ rm -Rf $(CURDIR)/gdb.files rm -Rf $(CURDIR)/build_data.c rm -Rf $(CURDIR)/extensions/*.so + rm -Rf $(CURDIR)/CFLAGS.extra + rm -Rf $(CURDIR)/LDFLAGS.extra + rm -Rf $(CURDIR)/extensions/defs.h override_dh_auto_install: dh_auto_install diff -Nru crash-7.1.4/debian/source/format crash-7.2.3+real/debian/source/format --- crash-7.1.4/debian/source/format 1970-01-01 00:00:00.000000000 +0000 +++ crash-7.2.3+real/debian/source/format 2018-06-22 21:39:16.000000000 +0000 @@ -0,0 +1 @@ +3.0 (quilt) diff -Nru crash-7.1.4/debian/tests/control crash-7.2.3+real/debian/tests/control --- crash-7.1.4/debian/tests/control 2018-07-20 09:16:28.000000000 +0000 +++ crash-7.2.3+real/debian/tests/control 2018-06-22 21:39:16.000000000 +0000 @@ -1,3 +1,3 @@ Tests: live -Restrictions: needs-root -Depends: @, lsb-release, sudo +Restrictions: needs-root isolation-machine +Depends: @, lsb-release, software-properties-common, sudo diff -Nru crash-7.1.4/debian/tests/live crash-7.2.3+real/debian/tests/live --- crash-7.1.4/debian/tests/live 2018-07-20 09:16:28.000000000 +0000 +++ crash-7.2.3+real/debian/tests/live 2018-06-22 21:39:16.000000000 +0000 @@ -7,13 +7,13 @@ apt-get update apt-get install linux-image-$(uname -r)-dbg elif [ "$(lsb_release -is)" = "Ubuntu" ]; then - sudo tee /etc/apt/sources.list.d/ddebs.list << EOF - deb http://ddebs.ubuntu.com/ $(lsb_release -cs) main - deb http://ddebs.ubuntu.com/ $(lsb_release -cs)-updates main - deb http://ddebs.ubuntu.com/ $(lsb_release -cs)-proposed main -EOF - # avoid stderr output sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys C8CAB6595FDFF622 2>&1 + # add available pockets + for pocket in "" "-updates" "-proposed"; do + apt-add-repository "deb http://ddebs.ubuntu.com/ $(lsb_release -cs)${pocket} main" + apt-get update 2>&1 || apt-add-repository --remove "deb http://ddebs.ubuntu.com/ $(lsb_release -cs)${pocket} main" + done + # avoid stderr output sudo apt-get update sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -q linux-image-$(uname -r)-dbgsym fi diff -Nru crash-7.1.4/defs.h crash-7.2.3+real/defs.h --- crash-7.1.4/defs.h 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/defs.h 2018-05-17 17:39:38.000000000 +0000 @@ -1,8 +1,8 @@ /* defs.h - 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-2018 David Anderson + * Copyright (C) 2002-2018 Red Hat, Inc. All rights reserved. * Copyright (C) 2002 Silicon Graphics, Inc. * * This program is free software; you can redistribute it and/or modify @@ -71,7 +71,7 @@ #if !defined(X86) && !defined(X86_64) && !defined(ALPHA) && !defined(PPC) && \ !defined(IA64) && !defined(PPC64) && !defined(S390) && !defined(S390X) && \ - !defined(ARM) && !defined(ARM64) && !defined(MIPS) + !defined(ARM) && !defined(ARM64) && !defined(MIPS) && !defined(SPARC64) #ifdef __alpha__ #define ALPHA #endif @@ -106,6 +106,9 @@ #ifdef __mipsel__ #define MIPS #endif +#ifdef __sparc_v9__ +#define SPARC64 +#endif #endif #ifdef X86 @@ -141,6 +144,14 @@ #ifdef MIPS #define NR_CPUS (32) #endif +#ifdef SPARC64 +#define NR_CPUS (4096) +#endif + +/* Some architectures require memory accesses to be aligned. */ +#if defined(SPARC64) +#define NEED_ALIGNED_MEM_ACCESS +#endif #define BUFSIZE (1500) #define NULLCHAR ('\0') @@ -173,6 +184,7 @@ typedef uint64_t physaddr_t; #define PADDR_NOT_AVAILABLE (0x1ULL) +#define KCORE_USE_VADDR (-1ULL) typedef unsigned long long int ulonglong; struct number_option { @@ -212,7 +224,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) @@ -251,10 +263,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() \ @@ -271,6 +284,10 @@ #define LKCD_KERNTYPES() (pc->flags & KERNTYPES) #define KVMDUMP_DUMPFILE() (pc->flags & KVMDUMP) #define SADUMP_DUMPFILE() (pc->flags & SADUMP) +#define VMSS_DUMPFILE() (pc->flags & VMWARE_VMSS) +#define QEMU_MEM_DUMP_NO_VMCOREINFO() \ + ((pc->flags2 & (QEMU_MEM_DUMP_ELF|QEMU_MEM_DUMP_COMPRESSED)) && !(pc->flags2 & VMCOREINFO)) + #define NETDUMP_LOCAL (0x1) /* netdump_data flags */ #define NETDUMP_REMOTE (0x2) @@ -523,6 +540,8 @@ #define SNAP (0x20000ULL) #define EXCLUDED_VMEMMAP (0x40000ULL) #define is_excluded_vmemmap() (pc->flags2 & EXCLUDED_VMEMMAP) +#define MEMSRC_LOCAL (0x80000ULL) +#define REDZONE (0x100000ULL) char *cleanup; char *namelist_orig; char *namelist_debug_orig; @@ -603,6 +622,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) @@ -628,6 +648,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) @@ -827,6 +848,11 @@ ulong tgid_cache_hits; long filepages; long anonpages; + ulong stack_end_magic; + ulong pf_kthread; + ulong pid_radix_tree; + int callbacks; + struct task_context **context_by_task; /* task_context sorted by task addr */ }; #define TASK_INIT_DONE (0x1) @@ -844,6 +870,10 @@ #define TIMESPEC (0x1000) #define NO_TIMESPEC (0x2000) #define ACTIVE_ONLY (0x4000) +#define START_TIME_NSECS (0x8000) +#define THREAD_INFO_IN_TASK (0x10000) +#define PID_RADIX_TREE (0x20000) +#define INDEXED_CONTEXTS (0x40000) #define TASK_SLUSH (20) @@ -1007,6 +1037,7 @@ int (*verify_line_number)(ulong, ulong, ulong); void (*get_irq_affinity)(int); void (*show_interrupts)(int, ulong *); + int (*is_page_ptr)(ulong, physaddr_t *); }; /* @@ -1040,7 +1071,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)) { \ @@ -1054,7 +1085,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)) { \ @@ -1070,6 +1101,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) @@ -1118,6 +1150,8 @@ #define FOREACH_a_FLAG (0x4000000) #define FOREACH_G_FLAG (0x8000000) #define FOREACH_F_FLAG2 (0x10000000) +#define FOREACH_y_FLAG (0x20000000) +#define FOREACH_GLEADER (0x40000000) #define FOREACH_PS_EXCLUSIVE \ (FOREACH_g_FLAG|FOREACH_a_FLAG|FOREACH_t_FLAG|FOREACH_c_FLAG|FOREACH_p_FLAG|FOREACH_l_FLAG|FOREACH_r_FLAG|FOREACH_m_FLAG) @@ -1141,6 +1175,7 @@ int comms; int args; int regexs; + int policy; }; struct reference { @@ -1949,6 +1984,53 @@ long pt_regs_cp0_badvaddr; long address_space_page_tree; 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; + long tnt_mod; + long radix_tree_node_shift; + long kmem_cache_red_left_pad; + long inactive_task_frame_ret_addr; + long sk_buff_head_next; + long sk_buff_head_qlen; + long sk_buff_next; + long sk_buff_len; + long sk_buff_data; + long nlmsghdr_nlmsg_type; + long module_arch; + long mod_arch_specific_num_orcs; + long mod_arch_specific_orc_unwind_ip; + long mod_arch_specific_orc_unwind; + long task_struct_policy; + long kmem_cache_random; + long pid_namespace_idr; + long idr_idr_rt; + long bpf_prog_aux; + long bpf_prog_type; + long bpf_prog_tag; + long bpf_prog_jited_len; + long bpf_prog_bpf_func; + long bpf_prog_len; + long bpf_prog_insnsi; + long bpf_prog_pages; + long bpf_map_map_type; + long bpf_map_map_flags; + long bpf_map_pages; + long bpf_map_key_size; + long bpf_map_value_size; + long bpf_map_max_entries; + long bpf_map_user; + long bpf_map_name; + long bpf_prog_aux_used_map_cnt; + long bpf_prog_aux_used_maps; + long bpf_prog_aux_load_time; + long bpf_prog_aux_user; + long user_struct_uid; }; struct size_table { /* stash of commonly-used sizes */ @@ -2090,6 +2172,20 @@ long hrtimer_base; long tnt; long trace_print_flags; + long task_struct_flags; + long timer_base; + long taint_flag; + long nlmsghdr; + long nlmsghdr_nlmsg_type; + long sk_buff_head_qlen; + long sk_buff_len; + long orc_entry; + long task_struct_policy; + long pid; + long bpf_prog; + long bpf_prog_aux; + long bpf_map; + long bpf_insn; }; struct array_table { @@ -2120,6 +2216,9 @@ int kmem_cache_node; int kmem_cache_cpu_slab; int rt_prio_array_queue; + int height_to_maxnodes; + int task_struct_rlim; + int signal_struct_rlim; }; /* @@ -2183,6 +2282,45 @@ * Facilitators for pulling correctly-sized data out of a buffer at a * known address. */ + +#ifdef NEED_ALIGNED_MEM_ACCESS + +#define DEF_LOADER(TYPE) \ +static inline TYPE \ +load_##TYPE (char *addr) \ +{ \ + TYPE ret; \ + size_t i = sizeof(TYPE); \ + while (i--) \ + ((char *)&ret)[i] = addr[i]; \ + return ret; \ +} + +DEF_LOADER(int); +DEF_LOADER(uint); +DEF_LOADER(long); +DEF_LOADER(ulong); +DEF_LOADER(ulonglong); +DEF_LOADER(ushort); +DEF_LOADER(short); +typedef void *pointer_t; +DEF_LOADER(pointer_t); + +#define LOADER(TYPE) load_##TYPE + +#define INT(ADDR) LOADER(int) ((char *)(ADDR)) +#define UINT(ADDR) LOADER(uint) ((char *)(ADDR)) +#define LONG(ADDR) LOADER(long) ((char *)(ADDR)) +#define ULONG(ADDR) LOADER(ulong) ((char *)(ADDR)) +#define ULONGLONG(ADDR) LOADER(ulonglong) ((char *)(ADDR)) +#define ULONG_PTR(ADDR) ((ulong *) (LOADER(pointer_t) ((char *)(ADDR)))) +#define USHORT(ADDR) LOADER(ushort) ((char *)(ADDR)) +#define SHORT(ADDR) LOADER(short) ((char *)(ADDR)) +#define UCHAR(ADDR) *((unsigned char *)((char *)(ADDR))) +#define VOID_PTR(ADDR) ((void *) (LOADER(pointer_t) ((char *)(ADDR)))) + +#else + #define INT(ADDR) *((int *)((char *)(ADDR))) #define UINT(ADDR) *((uint *)((char *)(ADDR))) #define LONG(ADDR) *((long *)((char *)(ADDR))) @@ -2194,6 +2332,8 @@ #define UCHAR(ADDR) *((unsigned char *)((char *)(ADDR))) #define VOID_PTR(ADDR) *((void **)((char *)(ADDR))) +#endif /* NEED_ALIGNED_MEM_ACCESS */ + struct node_table { int node_id; ulong pgdat; @@ -2264,6 +2404,7 @@ ulong mask; char *name; } *pageflags_data; + ulong max_mem_section_nr; }; #define NODES (0x1) @@ -2347,6 +2488,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; @@ -2363,6 +2506,9 @@ #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 TREE_LINEAR_ORDER (VERBOSE << 9) #define ALIAS_RUNTIME (1) #define ALIAS_RCLOCAL (2) @@ -2494,6 +2640,11 @@ ulong last_section_end; ulong _stext_vmlinux; struct downsized downsized; + ulong divide_error_vmlinux; + ulong idt_table_vmlinux; + ulong saved_command_line_vmlinux; + ulong pti_init_vmlinux; + ulong kaiser_init_vmlinux; }; /* flags for st */ @@ -2583,6 +2734,7 @@ struct syment *mod_init_symend; ulong mod_percpu; ulong mod_percpu_size; + struct objfile *loaded_objfile; }; #define IN_MODULE(A,L) \ @@ -2656,6 +2808,29 @@ #define NR_SECTION_ROOTS() (DIV_ROUND_UP(NR_MEM_SECTIONS(), SECTIONS_PER_ROOT())) #define SECTION_ROOT_MASK() (SECTIONS_PER_ROOT() - 1) +struct QEMUCPUSegment { + uint32_t selector; + uint32_t limit; + uint32_t flags; + uint32_t pad; + uint64_t base; +}; + +typedef struct QEMUCPUSegment QEMUCPUSegment; + +struct QEMUCPUState { + uint32_t version; + uint32_t size; + uint64_t rax, rbx, rcx, rdx, rsi, rdi, rsp, rbp; + uint64_t r8, r9, r10, r11, r12, r13, r14, r15; + uint64_t rip, rflags; + QEMUCPUSegment cs, ds, es, fs, gs, ss; + QEMUCPUSegment ldt, tr, gdt, idt; + uint64_t cr[5]; +}; + +typedef struct QEMUCPUState QEMUCPUState; + /* * Machine specific stuff */ @@ -2788,8 +2963,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) @@ -2812,7 +2987,7 @@ typedef signed int s32; -/* +/* * 3-levels / 4K pages */ #define PTRS_PER_PGD_L3_4K (512) @@ -2820,10 +2995,46 @@ #define PTRS_PER_PTE_L3_4K (512) #define PGDIR_SHIFT_L3_4K (30) #define PGDIR_SIZE_L3_4K ((1UL) << PGDIR_SHIFT_L3_4K) -#define PGDIR_MASK_L3 4K (~(PGDIR_SIZE_L3_4K-1)) +#define PGDIR_MASK_L3_4K (~(PGDIR_SIZE_L3_4K-1)) #define PMD_SHIFT_L3_4K (21) -#define PMD_SIZE_L3_4K (1UL << PMD_SHIFT_4K) -#define PMD_MASK_L3 4K (~(PMD_SIZE_4K-1)) +#define PMD_SIZE_L3_4K (1UL << PMD_SHIFT_L3_4K) +#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) +#define PTRS_PER_PMD_L3_64K (8192) +#define PTRS_PER_PTE_L3_64K (8192) +#define PGDIR_SHIFT_L3_64K (42) +#define PGDIR_SIZE_L3_64K ((1UL) << PGDIR_SHIFT_L3_64K) +#define PGDIR_MASK_L3_64K (~(PGDIR_SIZE_L3_64K-1)) +#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 @@ -2865,20 +3076,34 @@ #define KSYMS_START (0x1) #define PHYS_OFFSET (0x2) #define VM_L2_64K (0x4) -#define VM_L3_4K (0x8) -#define KDUMP_ENABLED (0x10) +#define VM_L3_64K (0x8) +#define VM_L3_4K (0x10) +#define KDUMP_ENABLED (0x20) +#define IRQ_STACKS (0x40) +#define NEW_VMEMMAP (0x80) +#define VM_L4_4K (0x100) +#define UNW_4_14 (0x200) + +/* + * 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. @@ -2891,9 +3116,11 @@ #define ARM64_VMEMMAP_END (ARM64_VMEMMAP_VADDR + GIGABYTES(8UL) - 1) #define ARM64_STACK_SIZE (16384) +#define ARM64_IRQ_STACK_SIZE ARM64_STACK_SIZE #define _SECTION_SIZE_BITS 30 #define _MAX_PHYSMEM_BITS 40 +#define _MAX_PHYSMEM_BITS_3_17 48 typedef unsigned long long __u64; typedef unsigned long long u64; @@ -2954,6 +3181,25 @@ ulong crash_save_cpu_start; ulong crash_save_cpu_end; 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; + ulong user_eframe_offset; + /* for v4.14 or later */ + ulong kern_eframe_offset; + ulong machine_kexec_start; + ulong machine_kexec_end; }; struct arm64_stackframe { @@ -2993,6 +3239,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 @@ -3130,65 +3379,83 @@ #define MODULES_VADDR_2_6_31 0xffffffffa0000000 #define MODULES_END_2_6_31 0xffffffffff000000 +#define USERSPACE_TOP_5LEVEL 0x0100000000000000 +#define PAGE_OFFSET_5LEVEL 0xff10000000000000 +#define VMALLOC_START_ADDR_5LEVEL 0xffa0000000000000 +#define VMALLOC_END_5LEVEL 0xffd1ffffffffffff +#define MODULES_VADDR_5LEVEL 0xffffffffa0000000 +#define MODULES_END_5LEVEL 0xffffffffff5fffff +#define VMEMMAP_VADDR_5LEVEL 0xffd4000000000000 +#define VMEMMAP_END_5LEVEL 0xffd5ffffffffffff + +#define VSYSCALL_START 0xffffffffff600000 +#define VSYSCALL_END 0xffffffffff601000 + #define PTOV(X) ((unsigned long)(X)+(machdep->kvbase)) #define VTOP(X) x86_64_VTOP((ulong)(X)) #define IS_VMALLOC_ADDR(X) x86_64_IS_VMALLOC_ADDR((ulong)(X)) -#define PML4_SHIFT 39 -#define PTRS_PER_PML4 512 -#define PGDIR_SHIFT 30 +/* + * the default page table level for x86_64: + * 4 level page tables + */ +#define PGDIR_SHIFT 39 #define PTRS_PER_PGD 512 +#define PUD_SHIFT 30 +#define PTRS_PER_PUD 512 #define PMD_SHIFT 21 #define PTRS_PER_PMD 512 #define PTRS_PER_PTE 512 -#define pml4_index(address) (((address) >> PML4_SHIFT) & (PTRS_PER_PML4-1)) -#define pgd_index(address) (((address) >> PGDIR_SHIFT) & (PTRS_PER_PGD-1)) +/* 5 level page */ +#define PGDIR_SHIFT_5LEVEL 48 +#define PTRS_PER_PGD_5LEVEL 512 +#define P4D_SHIFT 39 +#define PTRS_PER_P4D 512 + +#define __PGDIR_SHIFT (machdep->machspec->pgdir_shift) +#define __PTRS_PER_PGD (machdep->machspec->ptrs_per_pgd) + +#define pgd_index(address) (((address) >> __PGDIR_SHIFT) & (__PTRS_PER_PGD-1)) +#define p4d_index(address) (((address) >> P4D_SHIFT) & (PTRS_PER_P4D - 1)) +#define pud_index(address) (((address) >> PUD_SHIFT) & (PTRS_PER_PUD - 1)) #define pmd_index(address) (((address) >> PMD_SHIFT) & (PTRS_PER_PMD-1)) #define pte_index(address) (((address) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)) -#define IS_LAST_PML4_READ(pml4) ((ulong)(pml4) == machdep->machspec->last_pml4_read) - -#define FILL_PML4() { \ - if (!(pc->flags & RUNTIME) || ACTIVE()) \ - if (!IS_LAST_PML4_READ(vt->kernel_pgd[0])) \ - readmem(vt->kernel_pgd[0], KVADDR, machdep->machspec->pml4, \ - PAGESIZE(), "init_level4_pgt", FAULT_ON_ERROR); \ - machdep->machspec->last_pml4_read = (ulong)(vt->kernel_pgd[0]); \ +#define FILL_TOP_PGD() \ + if (!(pc->flags & RUNTIME) || ACTIVE()) { \ + FILL_PGD(vt->kernel_pgd[0], KVADDR, PAGESIZE()); \ } -#define FILL_PML4_HYPER() { \ - if (!machdep->machspec->last_pml4_read) { \ - unsigned long idle_pg_table = \ - symbol_exists("idle_pg_table_4") ? symbol_value("idle_pg_table_4") : \ - symbol_value("idle_pg_table"); \ - readmem(idle_pg_table, KVADDR, \ - machdep->machspec->pml4, PAGESIZE(), "idle_pg_table", \ - FAULT_ON_ERROR); \ - machdep->machspec->last_pml4_read = idle_pg_table; \ - }\ -} - -#define IS_LAST_UPML_READ(pml) ((ulong)(pml) == machdep->machspec->last_upml_read) - -#define FILL_UPML(PML, TYPE, SIZE) \ - if (!IS_LAST_UPML_READ(PML)) { \ - readmem((ulonglong)((ulong)(PML)), TYPE, machdep->machspec->upml, \ - SIZE, "pml page", FAULT_ON_ERROR); \ - machdep->machspec->last_upml_read = (ulong)(PML); \ - } +#define FILL_TOP_PGD_HYPER() \ + unsigned long idle_pg_table = symbol_exists("idle_pg_table_4") ? \ + symbol_value("idle_pg_table_4") : \ + symbol_value("idle_pg_table"); \ + FILL_PGD(idle_pg_table, KVADDR, PAGESIZE()); + +#define IS_LAST_P4D_READ(p4d) ((ulong)(p4d) == machdep->machspec->last_p4d_read) + +#define FILL_P4D(P4D, TYPE, SIZE) \ + if (!IS_LAST_P4D_READ(P4D)) { \ + readmem((ulonglong)((ulong)(P4D)), TYPE, machdep->machspec->p4d, \ + SIZE, "p4d page", FAULT_ON_ERROR); \ + machdep->machspec->last_p4d_read = (ulong)(P4D); \ + } /* * PHYSICAL_PAGE_MASK changed (enlarged) between 2.4 and 2.6, so * for safety, use the 2.6 values to generate it. */ -#define __PHYSICAL_MASK_SHIFT 40 +#define __PHYSICAL_MASK_SHIFT_XEN 40 +#define __PHYSICAL_MASK_SHIFT_2_6 46 +#define __PHYSICAL_MASK_SHIFT_5LEVEL 52 +#define __PHYSICAL_MASK_SHIFT (machdep->machspec->physical_mask_shift) #define __PHYSICAL_MASK ((1UL << __PHYSICAL_MASK_SHIFT) - 1) #define __VIRTUAL_MASK_SHIFT 48 #define __VIRTUAL_MASK ((1UL << __VIRTUAL_MASK_SHIFT) - 1) #define PAGE_SHIFT 12 #define PAGE_SIZE (1UL << PAGE_SHIFT) -#define PHYSICAL_PAGE_MASK (~(PAGE_SIZE-1) & (__PHYSICAL_MASK << PAGE_SHIFT)) +#define PHYSICAL_PAGE_MASK (~(PAGE_SIZE-1) & __PHYSICAL_MASK ) #define _PAGE_BIT_NX 63 #define _PAGE_PRESENT 0x001 @@ -3247,6 +3514,7 @@ #define _MAX_PHYSMEM_BITS 40 #define _MAX_PHYSMEM_BITS_2_6_26 44 #define _MAX_PHYSMEM_BITS_2_6_31 46 +#define _MAX_PHYSMEM_BITS_5LEVEL 52 #endif /* X86_64 */ @@ -3660,7 +3928,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 @@ -3682,7 +3950,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 */ @@ -3693,24 +3969,56 @@ #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 PMD_INDEX_SIZE_L4_64K_4_12 10 +#define PUD_INDEX_SIZE_L4_64K_4_12 7 +#define PGD_INDEX_SIZE_L4_64K_4_12 8 +#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 PAGE_PA_MAX_L4_4_6 (THIS_KERNEL_VERSION >= LINUX(4,11,0) ? 53 : 57) +#define PTE_RPN_MASK_L4_4_6 \ + (((1UL << PAGE_PA_MAX_L4_4_6) - 1) & ~((1UL << PAGESHIFT()) - 1)) +#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 */ @@ -3721,6 +4029,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) @@ -3792,6 +4103,110 @@ #endif /* S390X */ +#ifdef SPARC64 +#define _64BIT_ +#define MACHINE_TYPE "SPARC64" + +#define PTOV(X) \ + ((unsigned long)(X) + machdep->machspec->page_offset) +#define VTOP(X) \ + ((unsigned long)(X) - machdep->machspec->page_offset) + +#define PAGE_OFFSET (machdep->machspec->page_offset) + +extern int sparc64_IS_VMALLOC_ADDR(ulong vaddr); +#define IS_VMALLOC_ADDR(X) sparc64_IS_VMALLOC_ADDR((ulong)(X)) +#define PAGE_SHIFT (13) +#define PAGE_SIZE (1UL << PAGE_SHIFT) +#define PAGE_MASK (~(PAGE_SIZE - 1)) +#define PAGEBASE(X) (((ulong)(X)) & (ulong)machdep->pagemask) +#define THREAD_SIZE (2 * PAGE_SIZE) + +/* S3 Core + * Core 48-bit physical address supported. + * Bit 47 distinguishes memory or I/O. When set to "1" it is I/O. + */ +#define PHYS_MASK_SHIFT (47) +#define PHYS_MASK (((1UL) << PHYS_MASK_SHIFT) - 1) + +typedef signed int s32; + +/* + * This next two defines are convenience defines for normal page table. + */ +#define PTES_PER_PAGE (1UL << (PAGE_SHIFT - 3)) +#define PTES_PER_PAGE_MASK (PTES_PER_PAGE - 1) + +/* 4-level page table */ +#define PMD_SHIFT (PAGE_SHIFT + (PAGE_SHIFT-3)) +#define PMD_SIZE (1UL << PMD_SHIFT) +#define PMD_MASK (~(PMD_SIZE - 1)) +#define PMD_BITS (PAGE_SHIFT - 3) + +#define PUD_SHIFT (PMD_SHIFT + PMD_BITS) +#define PUD_SIZE (1UL << PUD_SHIFT) +#define PUD_MASK (~(PUD_SIZE - 1)) +#define PUD_BITS (PAGE_SHIFT - 3) + +#define PGDIR_SHIFT (PUD_SHIFT + PUD_BITS) +#define PGDIR_SIZE (1UL << PGDIR_SHIFT) +#define PGDIR_MASK (~(PGDIR_SIZE - 1)) +#define PGDIR_BITS (PAGE_SHIFT - 3) + +#define PTRS_PER_PTE (1UL << (PAGE_SHIFT - 3)) +#define PTRS_PER_PMD (1UL << PMD_BITS) +#define PTRS_PER_PUD (1UL << PUD_BITS) +#define PTRS_PER_PGD (1UL << PGDIR_BITS) + +#define HPAGE_SHIFT (23) +/* Down one huge page */ +#define SPARC64_USERSPACE_TOP (-(1UL << HPAGE_SHIFT)) +#define PAGE_PMD_HUGE (0x0100000000000000UL) + +/* These are for SUN4V. */ +#define _PAGE_VALID (0x8000000000000000UL) +#define _PAGE_NFO_4V (0x4000000000000000UL) +#define _PAGE_MODIFIED_4V (0x2000000000000000UL) +#define _PAGE_ACCESSED_4V (0x1000000000000000UL) +#define _PAGE_READ_4V (0x0800000000000000UL) +#define _PAGE_WRITE_4V (0x0400000000000000UL) +#define _PAGE_PADDR_4V (0x00FFFFFFFFFFE000UL) +#define _PAGE_PFN_MASK (_PAGE_PADDR_4V) +#define _PAGE_P_4V (0x0000000000000100UL) +#define _PAGE_EXEC_4V (0x0000000000000080UL) +#define _PAGE_W_4V (0x0000000000000040UL) +#define _PAGE_PRESENT_4V (0x0000000000000010UL) +#define _PAGE_SZALL_4V (0x0000000000000007UL) +/* There are other page sizes. Some supported. */ +#define _PAGE_SZ4MB_4V (0x0000000000000003UL) +#define _PAGE_SZ512K_4V (0x0000000000000002UL) +#define _PAGE_SZ64K_4V (0x0000000000000001UL) +#define _PAGE_SZ8K_4V (0x0000000000000000UL) + +#define SPARC64_MODULES_VADDR (0x0000000010000000UL) +#define SPARC64_MODULES_END (0x00000000f0000000UL) +#define SPARC64_VMALLOC_START (0x0000000100000000UL) + +#define SPARC64_STACK_SIZE 0x4000 + +/* sparsemem */ +#define _SECTION_SIZE_BITS 30 +#define _MAX_PHYSMEM_BITS 53 + +#define STACK_BIAS 2047 + +struct machine_specific { + ulong page_offset; + ulong vmalloc_end; +}; + +#define TIF_SIGPENDING (2) +#define SWP_OFFSET(E) ((E) >> (PAGE_SHIFT + 8UL)) +#define SWP_TYPE(E) (((E) >> PAGE_SHIFT) & 0xffUL) +#define __swp_type(E) SWP_TYPE(E) +#define __swp_offset(E) SWP_OFFSET(E) +#endif /* SPARC64 */ + #ifdef PLATFORM #define SWP_TYPE(entry) (error("PLATFORM_SWP_TYPE: TBD\n")) @@ -3804,6 +4219,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) @@ -3856,6 +4273,10 @@ #define MAX_HEXADDR_STRLEN (8) #define UVADDR_PRLEN (8) #endif +#ifdef SPARC64 +#define MAX_HEXADDR_STRLEN (16) +#define UVADDR_PRLEN (16) +#endif #define BADADDR ((ulong)(-1)) #define BADVAL ((ulong)(-1)) @@ -4111,30 +4532,40 @@ ulong task; ulong debug; struct stack_hook *hookp; + struct global_iterator { + int finished; + int block_index; + struct symtab *symtab; + struct symbol *sym; + struct objfile *obj; + } global_iterator; + struct load_module *lm; }; /* * GNU commands */ -#define GNU_DATATYPE_INIT (1) -#define GNU_DISASSEMBLE (2) -#define GNU_GET_LINE_NUMBER (3) -#define GNU_PASS_THROUGH (4) -#define GNU_GET_DATATYPE (5) -#define GNU_COMMAND_EXISTS (6) -#define GNU_STACK_TRACE (7) -#define GNU_ALPHA_FRAME_OFFSET (8) -#define GNU_FUNCTION_NUMARGS (9) -#define GNU_RESOLVE_TEXT_ADDR (10) -#define GNU_ADD_SYMBOL_FILE (11) -#define GNU_DELETE_SYMBOL_FILE (12) -#define GNU_VERSION (13) -#define GNU_PATCH_SYMBOL_VALUES (14) -#define GNU_GET_SYMBOL_TYPE (15) -#define GNU_USER_PRINT_OPTION (16) -#define GNU_SET_CRASH_BLOCK (17) -#define GNU_GET_FUNCTION_RANGE (18) -#define GNU_DEBUG_COMMAND (100) +#define GNU_DATATYPE_INIT (1) +#define GNU_DISASSEMBLE (2) +#define GNU_GET_LINE_NUMBER (3) +#define GNU_PASS_THROUGH (4) +#define GNU_GET_DATATYPE (5) +#define GNU_COMMAND_EXISTS (6) +#define GNU_STACK_TRACE (7) +#define GNU_ALPHA_FRAME_OFFSET (8) +#define GNU_FUNCTION_NUMARGS (9) +#define GNU_RESOLVE_TEXT_ADDR (10) +#define GNU_ADD_SYMBOL_FILE (11) +#define GNU_DELETE_SYMBOL_FILE (12) +#define GNU_VERSION (13) +#define GNU_PATCH_SYMBOL_VALUES (14) +#define GNU_GET_SYMBOL_TYPE (15) +#define GNU_USER_PRINT_OPTION (16) +#define GNU_SET_CRASH_BLOCK (17) +#define GNU_GET_FUNCTION_RANGE (18) +#define GNU_GET_NEXT_DATATYPE (19) +#define GNU_LOOKUP_STRUCT_CONTENTS (20) +#define GNU_DEBUG_COMMAND (100) /* * GNU flags */ @@ -4206,7 +4637,18 @@ #endif }; +/* + * include/linux/sched.h + */ #define PF_EXITING 0x00000004 /* getting shut down */ +#define PF_KTHREAD 0x00200000 /* I am a kernel thread */ +#define SCHED_NORMAL 0 +#define SCHED_FIFO 1 +#define SCHED_RR 2 +#define SCHED_BATCH 3 +#define SCHED_ISO 4 +#define SCHED_IDLE 5 +#define SCHED_DEADLINE 6 extern long _ZOMBIE_; #define IS_ZOMBIE(task) (task_state(task) & _ZOMBIE_) @@ -4234,8 +4676,10 @@ #define PS_NO_HEADER (0x10000) #define PS_MSECS (0x20000) #define PS_SUMMARY (0x40000) +#define PS_POLICY (0x80000) +#define PS_ACTIVE (0x100000) -#define PS_EXCLUSIVE (PS_TGID_LIST|PS_ARGV_ENVP|PS_TIMES|PS_CHILD_LIST|PS_PPID_LIST|PS_LAST_RUN|PS_RLIMIT|PS_MSECS|PS_SUMMARY) +#define PS_EXCLUSIVE (PS_TGID_LIST|PS_ARGV_ENVP|PS_TIMES|PS_CHILD_LIST|PS_PPID_LIST|PS_LAST_RUN|PS_RLIMIT|PS_MSECS|PS_SUMMARY|PS_ACTIVE) #define MAX_PS_ARGS (100) /* maximum command-line specific requests */ @@ -4251,6 +4695,7 @@ } regex_data[MAX_PS_ARGS]; int regexs; ulong *cpus; + int policy; }; #define IS_A_NUMBER(X) (decimal(X, 0) || hexadecimal(X, 0)) @@ -4303,6 +4748,7 @@ void cmd_help(void); /* help.c */ void cmd_test(void); /* test.c */ void cmd_ascii(void); /* tools.c */ +void cmd_bpf(void); /* bfp.c */ void cmd_set(void); /* tools.c */ void cmd_eval(void); /* tools.c */ void cmd_list(void); /* tools.c */ @@ -4397,6 +4843,9 @@ #ifdef MIPS #define machdep_init(X) mips_init(X) #endif +#ifdef SPARC64 +#define machdep_init(X) sparc64_init(X) +#endif int clean_exit(int); int untrusted_file(FILE *, char *); char *readmem_function_name(void); @@ -4451,7 +4900,7 @@ char *strip_beginning_char(char *, char); char *strip_comma(char *); char *strip_hex(char *); -char *upper_case(char *, char *); +char *upper_case(const char *, char *); char *first_nonspace(char *); char *first_space(char *); char *replace_string(char *, char *, char); @@ -4490,6 +4939,13 @@ int bracketed(char *, char *, int); void backspace(int); int do_list(struct list_data *); +struct radix_tree_ops { + void (*entry)(ulong node, ulong slot, const char *path, + ulong index, void *private); + uint radix; + void *private; +}; +int do_radix_tree_traverse(ulong ptr, int is_root, struct radix_tree_ops *ops); int do_rdtree(struct tree_data *); int do_rbtree(struct tree_data *); int retrieve_list(ulong *, int); @@ -4523,7 +4979,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) @@ -4543,6 +4999,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); @@ -4673,6 +5130,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 @@ -4703,6 +5161,7 @@ char *fill_vma_cache(ulong); void clear_vma_cache(void); void dump_vma_cache(ulong); +int generic_is_page_ptr(ulong, physaddr_t *); int is_page_ptr(ulong, physaddr_t *); void dump_vm_table(int); int read_string(ulong, char *, int); @@ -4733,6 +5192,7 @@ char *format_stack_entry(struct bt_info *bt, char *, ulong, ulong); int in_user_stack(ulong, ulong); int dump_inode_page(ulong); +ulong valid_section_nr(ulong); /* @@ -4831,9 +5291,13 @@ #ifdef MIPS #define dump_machdep_table(X) mips_dump_machdep_table(X) #endif +#ifdef SPARC64 +#define dump_machdep_table(X) sparc64_dump_machdep_table(X) +#endif extern char *help_pointer[]; extern char *help_alias[]; extern char *help_ascii[]; +extern char *help_bpf[]; extern char *help_bt[]; extern char *help_btop[]; extern char *help_dev[]; @@ -4944,6 +5408,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 @@ -4985,6 +5450,7 @@ #define SHOW_LOG_LEVEL (0x1) #define SHOW_LOG_DICT (0x2) #define SHOW_LOG_TEXT (0x4) +#define SHOW_LOG_AUDIT (0x8) void set_cpu(int); void clear_machdep_cache(void); struct stack_hook *gather_text_list(struct bt_info *); @@ -5036,6 +5502,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) @@ -5185,6 +5652,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); @@ -5287,6 +5755,45 @@ char *exception_stacks[MAX_EXCEPTION_STACKS]; }; +typedef struct __attribute__((__packed__)) { + signed short sp_offset; + signed short bp_offset; + unsigned int sp_reg:4; + unsigned int bp_reg:4; + unsigned int type:2; +} kernel_orc_entry; + +struct ORC_data { + int module_ORC; + uint lookup_num_blocks; + ulong __start_orc_unwind_ip; + ulong __stop_orc_unwind_ip; + ulong __start_orc_unwind; + ulong __stop_orc_unwind; + ulong orc_lookup; + ulong ip_entry; + ulong orc_entry; + kernel_orc_entry kernel_orc_entry; +}; + +#define ORC_TYPE_CALL 0 +#define ORC_TYPE_REGS 1 +#define ORC_TYPE_REGS_IRET 2 +#define UNWIND_HINT_TYPE_SAVE 3 +#define UNWIND_HINT_TYPE_RESTORE 4 + +#define ORC_REG_UNDEFINED 0 +#define ORC_REG_PREV_SP 1 +#define ORC_REG_DX 2 +#define ORC_REG_DI 3 +#define ORC_REG_BP 4 +#define ORC_REG_SP 5 +#define ORC_REG_R10 6 +#define ORC_REG_R13 7 +#define ORC_REG_BP_INDIRECT 8 +#define ORC_REG_SP_INDIRECT 9 +#define ORC_REG_MAX 15 + struct machine_specific { ulong userspace_top; ulong page_offset; @@ -5297,7 +5804,7 @@ ulong modules_vaddr; ulong modules_end; ulong phys_base; - char *pml4; + char *pml4; char *upml; ulong last_upml_read; ulong last_pml4_read; @@ -5312,6 +5819,16 @@ ulong page_protnone; ulong GART_start; ulong GART_end; + ulong kernel_image_size; + ulong physical_mask_shift; + ulong pgdir_shift; + char *p4d; + ulong last_p4d_read; + struct ORC_data orc; + ulong irq_stack_gap; + ulong kpti_entry_stack; + ulong kpti_entry_stack_size; + ulong ptrs_per_pgd; }; #define KSYMS_START (0x1) @@ -5326,8 +5843,12 @@ #define FRAMEPOINTER (0x200) #define GART_REGION (0x400) #define NESTED_NMI (0x800) +#define RANDOMIZED (0x1000) +#define VM_5LEVEL (0x2000) +#define ORC (0x4000) +#define KPTI (0x8000) -#define VM_FLAGS (VM_ORIG|VM_2_6_11|VM_XEN|VM_XEN_RHEL4) +#define VM_FLAGS (VM_ORIG|VM_2_6_11|VM_XEN|VM_XEN_RHEL4|VM_5LEVEL) #define _2MB_PAGE_MASK (~((MEGABYTES(2))-1)) @@ -5408,14 +5929,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; @@ -5425,13 +5945,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; @@ -5445,23 +5969,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) @@ -5620,6 +6142,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); @@ -5679,10 +6206,23 @@ #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 */ /* + * sparc64.c + */ +#ifdef SPARC64 +void sparc64_init(int); +void sparc64_dump_machdep_table(ulong); +int sparc64_vmalloc_addr(ulong); +#define display_idt_table() \ + error(FATAL, "The -d option is not applicable to sparc64.\n") +#endif + +/* * netdump.c */ int is_netdump(char *, ulong); @@ -5723,6 +6263,8 @@ int exist_regs_in_elf_notes(struct task_context *); void *get_regs_from_elf_notes(struct task_context *); void map_cpus_to_prstatus(void); +int kdump_phys_base(ulong *); +int kdump_set_phys_base(ulong); int arm_kdump_phys_base(ulong *); int is_proc_kcore(char *, ulong); int proc_kcore_init(FILE *); @@ -5734,6 +6276,8 @@ void display_regs_from_elf_notes(int, FILE *); void display_ELF_note(int, int, void *, FILE *); void *netdump_get_prstatus_percpu(int); +int kdump_kaslr_check(void); +QEMUCPUState *kdump_get_qemucpustate(int); #define PRSTATUS_NOTE (1) #define QEMU_NOTE (2) @@ -5765,6 +6309,7 @@ FILE *set_diskdump_fp(FILE *); void get_diskdump_regs(struct bt_info *, ulong *, ulong *); int diskdump_phys_base(unsigned long *); +int diskdump_set_phys_base(unsigned long); ulong *diskdump_flags; int is_partial_diskdump(void); int dumpfile_is_split(void); @@ -5776,6 +6321,8 @@ void process_elf32_notes(void *, ulong); void process_elf64_notes(void *, ulong); void dump_registers_for_compressed_kdump(void); +int diskdump_kaslr_check(void); +QEMUCPUState *diskdump_get_qemucpustate(int); /* * makedumpfile.c @@ -5854,12 +6401,15 @@ void get_sadump_regs(struct bt_info *bt, ulong *ipp, ulong *spp); void sadump_display_regs(int, FILE *); int sadump_phys_base(ulong *); +int sadump_set_phys_base(ulong); void sadump_show_diskset(void); int sadump_is_zero_excluded(void); void sadump_set_zero_excluded(void); void sadump_unset_zero_excluded(void); struct sadump_data; struct sadump_data *get_sadump_data(void); +int sadump_calc_kaslr_offset(ulong *); +int sadump_get_cr3_idtr(ulong *, ulong *); /* * qemu.c @@ -5907,6 +6457,19 @@ uint vmware_vmss_page_size(void); int read_vmware_vmss(int, void *, int, ulong, physaddr_t); int write_vmware_vmss(int, void *, int, ulong, physaddr_t); +void vmware_vmss_display_regs(int, FILE *); +void get_vmware_vmss_regs(struct bt_info *, ulong *, ulong *); +int vmware_vmss_memory_dump(FILE *); +void dump_registers_for_vmss_dump(void); +int vmware_vmss_valid_regs(struct bt_info *); +int vmware_vmss_get_cr3_idtr(ulong *, ulong *); +int vmware_vmss_phys_base(ulong *phys_base); +int vmware_vmss_set_phys_base(ulong); + +/* + * kaslr_helper.c + */ +int calc_kaslr_offset(ulong *, ulong *); /* * gnu_binutils.c diff -Nru crash-7.1.4/dev.c crash-7.2.3+real/dev.c --- crash-7.1.4/dev.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/dev.c 2018-05-17 17:39:38.000000000 +0000 @@ -31,7 +31,7 @@ static const char *pci_strvendor(uint, char *); static const char *pci_strdev(uint, uint, char *); -static void diskio_option(void); +static void diskio_option(ulong flags); static struct dev_table { ulong flags; @@ -42,6 +42,9 @@ #define DEV_INIT 0x1 #define DISKIO_INIT 0x2 +#define DIOF_ALL 1 << 0 +#define DIOF_NONZERO 1 << 1 + void dev_init(void) { @@ -93,11 +96,15 @@ flags = 0; - while ((c = getopt(argcnt, args, "dpi")) != EOF) { + while ((c = getopt(argcnt, args, "dDpi")) != EOF) { switch(c) { case 'd': - diskio_option(); + diskio_option(DIOF_ALL); + return; + + case 'D': + diskio_option(DIOF_NONZERO); return; case 'i': @@ -3800,18 +3807,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 */ @@ -3936,7 +4009,7 @@ } static void -display_one_diskio(struct iter *i, unsigned long gendisk) +display_one_diskio(struct iter *i, unsigned long gendisk, ulong flags) { char disk_name[BUFSIZE + 1]; char buf0[BUFSIZE]; @@ -3961,9 +4034,12 @@ 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", + if ((flags & DIOF_NONZERO) + && (io.read + io.write == 0)) + return; + + 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,12 +4056,17 @@ 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 -display_all_diskio(void) +display_all_diskio(ulong flags) { struct iter i; unsigned long gendisk; @@ -4019,7 +4100,7 @@ mkstring(buf5, 5, RJUST, "DRV")); while ((gendisk = i.next_disk(&i)) != 0) - display_one_diskio(&i, gendisk); + display_one_diskio(&i, gendisk, flags); } static @@ -4056,6 +4137,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"); @@ -4069,8 +4160,8 @@ } static void -diskio_option(void) +diskio_option(ulong flags) { diskio_init(); - display_all_diskio(); + display_all_diskio(flags); } diff -Nru crash-7.1.4/diskdump.c crash-7.2.3+real/diskdump.c --- crash-7.1.4/diskdump.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/diskdump.c 2018-05-17 17:39:38.000000000 +0000 @@ -56,6 +56,7 @@ void **nt_prstatus_percpu; uint num_prstatus_notes; void **nt_qemu_percpu; + void **nt_qemucs_percpu; uint num_qemu_notes; /* page cache */ @@ -153,8 +154,14 @@ dd->num_qemu_notes * sizeof(void *))) == NULL) error(FATAL, "compressed kdump: cannot realloc QEMU note pointers\n"); - } else + if ((dd->nt_qemucs_percpu = realloc(dd->nt_qemucs_percpu, + dd->num_qemu_notes * sizeof(void *))) == NULL) + error(FATAL, + "compressed kdump: cannot realloc QEMU note pointers\n"); + } else { free(dd->nt_qemu_percpu); + free(dd->nt_qemucs_percpu); + } } } @@ -283,6 +290,10 @@ } len = sizeof(Elf32_Nhdr); if (STRNEQ((char *)nt + len, "QEMU")) { + ulong *ptr = + (ulong *)((char *)nt + sizeof(Elf32_Nhdr) + nt->n_namesz); + dd->nt_qemucs_percpu[qemu_num] = + (ulong *)roundup((ulong) ptr, 4); dd->nt_qemu_percpu[qemu_num] = nt; qemu_num++; } @@ -332,6 +343,10 @@ } len = sizeof(Elf64_Nhdr); if (STRNEQ((char *)nt + len, "QEMU")) { + ulong *ptr = + (ulong *)((char *)nt + sizeof(Elf64_Nhdr) + nt->n_namesz); + dd->nt_qemucs_percpu[qemu_num] = + (ulong *)roundup((ulong) ptr, 4); dd->nt_qemu_percpu[qemu_num] = nt; qemu_num++; } @@ -730,6 +745,8 @@ dd->machine_type = EM_S390; else if (machine_type("ARM64")) dd->machine_type = EM_AARCH64; + else if (machine_type("SPARC64")) + dd->machine_type = EM_SPARCV9; else { error(INFO, "%s: unsupported machine type: %s\n", DISKDUMP_VALID() ? "diskdump" : "compressed kdump", @@ -757,6 +774,10 @@ error(FATAL, "qemu mem dump compressed: cannot malloc pointer" " to QEMU notes\n"); + if ((dd->nt_qemucs_percpu = malloc(NR_CPUS * sizeof(void *))) == NULL) + error(FATAL, "qemu mem dump compressed: cannot malloc pointer" + " to QEMUCS notes\n"); + if (FLAT_FORMAT()) { if (!read_flattened_format(dd->dfd, offset, dd->notes_buf, size)) { error(INFO, "compressed kdump: cannot read notes data" @@ -852,6 +873,8 @@ free(dd->nt_prstatus_percpu); if (dd->nt_qemu_percpu) free(dd->nt_qemu_percpu); + if (dd->nt_qemucs_percpu) + free(dd->nt_qemucs_percpu); dd->flags &= ~(DISKDUMP_LOCAL|KDUMP_CMPRS_LOCAL); pc->flags2 &= ~ELF_NOTES; @@ -965,6 +988,17 @@ return FALSE; } +int +diskdump_set_phys_base(unsigned long phys_base) +{ + if (diskdump_kaslr_check()) { + dd->sub_header_kdump->phys_base = phys_base; + return TRUE; + } + + return FALSE; +} + /* * Check whether paddr is already cached. */ @@ -1364,8 +1398,30 @@ static void get_diskdump_regs_ppc64(struct bt_info *bt, ulong *eip, ulong *esp) { + int cpu; + Elf64_Nhdr *note; + size_t len; + if ((bt->task == tt->panic_task) && DISKDUMP_VALID()) bt->machdep = &dd->sub_header->elf_regs; + else if (KDUMP_CMPRS_VALID() && + (bt->task == tt->panic_task || + (is_task_active(bt->task) && dd->num_prstatus_notes > 1))) { + cpu = bt->tc->processor; + if (dd->nt_prstatus_percpu[cpu] == NULL) { + if(CRASHDEBUG(1)) + error(INFO, + "registers not collected for cpu %d\n", + cpu); + } else { + note = (Elf64_Nhdr *) + dd->nt_prstatus_percpu[cpu]; + len = sizeof(Elf64_Nhdr); + len = roundup(len + note->n_namesz, 4); + bt->machdep = (void *)((char *)note + len + + MEMBER_OFFSET("elf_prstatus", "pr_reg")); + } + } machdep->get_stack_frame(bt, eip, esp); } @@ -1382,6 +1438,31 @@ machdep->get_stack_frame(bt, eip, esp); } +static void +get_diskdump_regs_sparc64(struct bt_info *bt, ulong *eip, ulong *esp) +{ + Elf64_Nhdr *note; + int len; + + if (KDUMP_CMPRS_VALID() && + (bt->task == tt->panic_task || + (is_task_active(bt->task) && dd->num_prstatus_notes > 1))) { + note = (Elf64_Nhdr *)dd->nt_prstatus_percpu[bt->tc->processor]; + if (!note) + error(FATAL, + "cannot determine NT_PRSTATUS ELF note " + "for %s task: %lx\n", + (bt->task == tt->panic_task) ? + "panic" : "active", bt->task); + len = sizeof(Elf64_Nhdr); + len = roundup(len + note->n_namesz, 4); + bt->machdep = (void *)((char *)note + len + + MEMBER_OFFSET("elf_prstatus", "pr_reg")); + } + + machdep->get_stack_frame(bt, eip, esp); +} + /* * Send the request to the proper architecture hander. */ @@ -1432,6 +1513,10 @@ get_diskdump_regs_arm64(bt, eip, esp); break; + case EM_SPARCV9: + get_diskdump_regs_sparc64(bt, eip, esp); + break; + default: error(FATAL, "%s: unsupported machine type: %s\n", DISKDUMP_VALID() ? "diskdump" : "compressed kdump", @@ -1577,7 +1662,8 @@ for (tot = cnt = 0; tot < size; tot += len) { qemu = FALSE; if (machine_type("X86_64") || machine_type("S390X") || - machine_type("ARM64") || machine_type("PPC64")) { + machine_type("ARM64") || machine_type("PPC64") || + machine_type("SPARC64")) { note64 = (void *)dd->notes_buf + tot; len = sizeof(Elf64_Nhdr); if (STRNEQ((char *)note64 + len, "QEMU")) @@ -1684,6 +1770,8 @@ fprintf(fp, "(EM_S390)\n"); break; case EM_AARCH64: fprintf(fp, "(EM_AARCH64)\n"); break; + case EM_SPARCV9: + fprintf(fp, "(EM_SPARCV9)\n"); break; default: fprintf(fp, "(unknown)\n"); break; } @@ -2379,4 +2467,36 @@ } } +int +diskdump_kaslr_check() +{ + if (!QEMU_MEM_DUMP_NO_VMCOREINFO()) + return FALSE; + + if (dd->num_qemu_notes) + return TRUE; + + return FALSE; +} + +#ifdef X86_64 +QEMUCPUState * +diskdump_get_qemucpustate(int cpu) +{ + if (cpu >= dd->num_qemu_notes) { + if (CRASHDEBUG(1)) + error(INFO, + "Invalid index for QEMU Note: %d (>= %d)\n", + cpu, dd->num_qemu_notes); + return NULL; + } + if (dd->machine_type != EM_X86_64) { + if (CRASHDEBUG(1)) + error(INFO, "Only x86_64 64bit is supported.\n"); + return NULL; + } + + return (QEMUCPUState *)dd->nt_qemucs_percpu[cpu]; +} +#endif diff -Nru crash-7.1.4/extensions/snap.c crash-7.2.3+real/extensions/snap.c --- crash-7.1.4/extensions/snap.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/extensions/snap.c 2018-05-17 17:39:38.000000000 +0000 @@ -1,7 +1,7 @@ /* snap.c - capture live memory into a kdump or netdump dumpfile * - * Copyright (C) 2009, 2013 David Anderson - * Copyright (C) 2009, 2013 Red Hat, Inc. All rights reserved. + * Copyright (C) 2009, 2013, 2014, 2017 David Anderson + * Copyright (C) 2009, 2013, 2014, 2017 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 @@ -423,7 +423,10 @@ ushort e_machine; int num_segments; struct node_table *nt; - ulonglong task_struct; + struct SNAP_info { + ulonglong task_struct; + ulonglong arch_data; + } SNAP_info; num_segments = vt->numnodes; @@ -606,9 +609,16 @@ notes->p_filesz += len; /* NT_TASKSTRUCT note */ - task_struct = CURRENT_TASK(); + SNAP_info.task_struct = CURRENT_TASK(); +#ifdef X86_64 + SNAP_info.arch_data = kt->relocate; +#elif ARM64 + SNAP_info.arch_data = machdep->machspec->kimage_voffset; +#else + SNAP_info.arch_data = 0; +#endif len = dump_elf_note (ptr, NT_TASKSTRUCT, "SNAP", - (char *)&task_struct, sizeof(ulonglong)); + (char *)&SNAP_info, sizeof(struct SNAP_info)); offset += len; ptr += len; notes->p_filesz += len; diff -Nru crash-7.1.4/extensions/trace.c crash-7.2.3+real/extensions/trace.c --- crash-7.1.4/extensions/trace.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/extensions/trace.c 2018-05-17 17:39:38.000000000 +0000 @@ -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 { @@ -90,7 +96,7 @@ ulong real_head_page; int head_page_index; - unsigned int nr_pages; + unsigned long nr_pages; ulong *pages; ulong *linear_pages; @@ -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; @@ -183,7 +198,9 @@ fprintf(fp, "per cpu buffer sizes\n"); } - if (kernel_symbol_exists("ring_buffer_read")) + if (machine_type("PPC64") && kernel_symbol_exists(".ring_buffer_read")) + gdb_set_crash_scope(symbol_value(".ring_buffer_read"), ".ring_buffer_read"); + else if (kernel_symbol_exists("ring_buffer_read")) gdb_set_crash_scope(symbol_value("ring_buffer_read"), "ring_buffer_read"); if (!per_cpu_buffer_sizes) @@ -229,6 +246,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 } @@ -412,7 +432,13 @@ buffer_read_value(overrun); buffer_read_value(entries); if (per_cpu_buffer_sizes) { - buffer_read_value(nr_pages); + if (MEMBER_SIZE("ring_buffer_per_cpu", "nr_pages") == sizeof(unsigned int)) { + unsigned int tmp_nr_pages; + read_value(tmp_nr_pages, buffers[i].kaddr, ring_buffer_per_cpu, nr_pages); + buffers[i].nr_pages = (unsigned long) tmp_nr_pages; + } else { + buffer_read_value(nr_pages); + } pages = buffers[i].nr_pages; } else buffers[i].nr_pages = pages; @@ -435,61 +461,140 @@ 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; + + if (!ti->max_tr_ring_buffer) + return 0; + + 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 +609,7 @@ } else { read_value(addr, current_trace, POINTER_SYM, POINTER); } - + read_value(addr, addr, tracer, name); read_string(addr, tmp, 128); @@ -524,9 +629,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 +641,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 +678,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 +712,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 +771,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 +784,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 +799,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 +1135,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 +1142,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 +1186,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 +1595,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 +1691,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 +2322,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 +2396,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 +2416,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 +2435,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 +2449,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 +2474,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.4/extensions.c crash-7.2.3+real/extensions.c --- crash-7.1.4/extensions.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/extensions.c 2018-05-17 17:39:38.000000000 +0000 @@ -1,8 +1,8 @@ /* extensions.c - core analysis suite * * Copyright (C) 2001, 2002 Mission Critical Linux, Inc. - * Copyright (C) 2002-2013 David Anderson - * Copyright (C) 2002-2013 Red Hat, Inc. All rights reserved. + * Copyright (C) 2002-2013, 2018 David Anderson + * Copyright (C) 2002-2013, 2018 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 @@ -363,7 +363,7 @@ DIR *dirp; struct dirent *dp; char dirbuf[BUFSIZE]; - char filename[BUFSIZE]; + char filename[BUFSIZE*2]; int found; if (!get_extensions_directory(dirbuf)) diff -Nru crash-7.1.4/filesys.c crash-7.2.3+real/filesys.c --- crash-7.1.4/filesys.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/filesys.c 2018-05-17 17:39:38.000000000 +0000 @@ -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-2018 David Anderson + * Copyright (C) 2002-2018 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 @@ -16,6 +16,7 @@ */ #include "defs.h" +#include #include #include #include @@ -45,7 +46,6 @@ static int get_memory_driver_dev(dev_t *); static int memory_driver_init(void); static int create_memory_device(dev_t); -static void *radix_tree_lookup(ulong, ulong, int); static int match_file_string(char *, char *, char *); static ulong get_root_vfsmount(char *); static void check_live_arch_mismatch(void); @@ -133,7 +133,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(); } @@ -161,13 +161,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; @@ -199,9 +199,13 @@ if (!proc_kcore_init(fp)) error(FATAL, "/proc/kcore: initialization failed\n"); - } else - error(FATAL, "unknown memory device: %s\n", - pc->live_memsrc); + } else { + if (!pc->live_memsrc) + error(FATAL, "cannot find a live memory device\n"); + else + error(FATAL, "unknown memory device: %s\n", + pc->live_memsrc); + } return; } @@ -308,6 +312,7 @@ #define CREATE 1 #define DESTROY 0 #define DEFAULT_SEARCHDIRS 5 +#define EXTRA_SEARCHDIRS 5 static char ** build_searchdirs(int create, int *preferred) @@ -340,11 +345,15 @@ *preferred = 0; /* - * Allow, at a minimum, the defaults plus an extra three directories - * for the two possible /usr/src/redhat/BUILD/kernel-xxx locations - * plus the Red Hat debug directory. + * Allow, at a minimum, the defaults plus an extra four directories: + * + * /lib/modules + * /usr/src/redhat/BUILD/kernel-/linux + * /usr/src/redhat/BUILD/kernel-/linux- + * /usr/lib/debug/lib/modules + * */ - cnt = DEFAULT_SEARCHDIRS + 3; + cnt = DEFAULT_SEARCHDIRS + EXTRA_SEARCHDIRS; if ((dirp = opendir("/usr/src"))) { for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) @@ -394,7 +403,6 @@ if ((searchdirs = calloc(cnt, sizeof(char *))) == NULL) { error(INFO, "search directory list malloc: %s\n", strerror(errno)); - closedir(dirp); return default_searchdirs; } for (i = 0; i < DEFAULT_SEARCHDIRS; i++) @@ -621,7 +629,7 @@ if (mount_point(kernel) || !file_readable(kernel) || - !is_elf_file(kernel)) + !is_kernel(kernel)) continue; if (CRASHDEBUG(1)) @@ -1362,10 +1370,10 @@ long s_dirty; ulong devp, dirp, sbp, dirty, type, name; struct list_data list_data, *ld; - char buf1[BUFSIZE]; + char buf1[BUFSIZE*2]; char buf2[BUFSIZE]; char buf3[BUFSIZE]; - char buf4[BUFSIZE]; + char buf4[BUFSIZE/2]; ulong *dentry_list, *dp, *mntlist; ulong *vfsmnt; char *vfsmount_buf, *super_block_buf, *mount_buf; @@ -1490,8 +1498,8 @@ KVADDR, &name, sizeof(void *), "file_system_type name", FAULT_ON_ERROR); - if (read_string(name, buf1, BUFSIZE-1)) - sprintf(buf3, "%-6s ", buf1); + if (read_string(name, buf4, (BUFSIZE/2)-1)) + sprintf(buf3, "%-6s ", buf4); else sprintf(buf3, "unknown "); @@ -2079,25 +2087,6 @@ if (!(ft->inode_cache = (char *)malloc(SIZE(inode)*INODE_CACHE))) error(FATAL, "cannot malloc inode cache\n"); - if (symbol_exists("height_to_maxindex")) { - int tmp ATTRIBUTE_UNUSED; - if (LKCD_KERNTYPES()) - ARRAY_LENGTH_INIT_ALT(tmp, "height_to_maxindex", - "radix_tree_preload.nodes", NULL, 0); - else - ARRAY_LENGTH_INIT(tmp, height_to_maxindex, - "height_to_maxindex", NULL, 0); - STRUCT_SIZE_INIT(radix_tree_root, "radix_tree_root"); - STRUCT_SIZE_INIT(radix_tree_node, "radix_tree_node"); - MEMBER_OFFSET_INIT(radix_tree_root_height, - "radix_tree_root","height"); - MEMBER_OFFSET_INIT(radix_tree_root_rnode, - "radix_tree_root","rnode"); - MEMBER_OFFSET_INIT(radix_tree_node_slots, - "radix_tree_node","slots"); - MEMBER_OFFSET_INIT(radix_tree_node_height, - "radix_tree_node","height"); - } MEMBER_OFFSET_INIT(rb_root_rb_node, "rb_root","rb_node"); MEMBER_OFFSET_INIT(rb_node_rb_left, @@ -2404,7 +2393,7 @@ char buf2[BUFSIZE]; char buf3[BUFSIZE]; char buf4[BUFSIZE]; - char root_pwd[BUFSIZE]; + char root_pwd[BUFSIZE*4]; int root_pwd_printed = 0; int file_dump_flags = 0; @@ -3097,7 +3086,7 @@ break; if (tmp_dentry != dentry) { - strncpy(tmpname, pathname, BUFSIZE); + strncpy(tmpname, pathname, BUFSIZE-1); if (strlen(tmpname) + d_name_len < BUFSIZE) { if ((d_name_len > 1 || !STREQ(buf, "/")) && !STRNEQ(tmpname, "/")) { @@ -3628,8 +3617,8 @@ { FILE *pipe; char buf[BUFSIZE]; - char modname1[BUFSIZE]; - char modname2[BUFSIZE]; + char modname1[BUFSIZE/2]; + char modname2[BUFSIZE/2]; char *name; int use_module, crashbuiltin; struct stat stat1, stat2; @@ -3640,7 +3629,13 @@ if (pc->live_memsrc) goto live_report; - pc->live_memsrc = "/dev/mem"; + if (file_exists("/dev/mem", NULL)) + pc->live_memsrc = "/dev/mem"; + else if (file_exists("/proc/kcore", NULL)) { + pc->flags &= ~DEVMEM; + pc->flags |= PROC_KCORE; + pc->live_memsrc = "/proc/kcore"; + } use_module = crashbuiltin = FALSE; if (file_exists("/dev/mem", &stat1) && @@ -3699,7 +3694,7 @@ } if (use_module) { - pc->flags &= ~DEVMEM; + pc->flags &= ~(DEVMEM|PROC_KCORE); pc->flags |= MEMMOD; pc->readmem = read_memory_device; pc->writemem = write_memory_device; @@ -3707,7 +3702,7 @@ } if (crashbuiltin) { - pc->flags &= ~DEVMEM; + pc->flags &= ~(DEVMEM|PROC_KCORE); pc->flags |= CRASHBUILTIN; pc->readmem = read_memory_device; pc->writemem = write_memory_device; @@ -3968,15 +3963,64 @@ return errors ? FALSE : TRUE; } +struct do_radix_tree_info { + ulong maxcount; + ulong count; + void *data; +}; +static void do_radix_tree_count(ulong node, ulong slot, const char *path, + ulong index, void *private) +{ + struct do_radix_tree_info *info = private; + info->count++; +} +static void do_radix_tree_search(ulong node, ulong slot, const char *path, + ulong index, void *private) +{ + struct do_radix_tree_info *info = private; + struct radix_tree_pair *rtp = info->data; -/* - * Use the kernel's radix_tree_lookup() function as a template to dump - * a radix tree's entries. - */ + if (rtp->index == index) { + rtp->value = (void *)slot; + info->count = 1; + } +} +static void do_radix_tree_dump(ulong node, ulong slot, const char *path, + ulong index, void *private) +{ + struct do_radix_tree_info *info = private; + fprintf(fp, "[%ld] %lx\n", index, slot); + info->count++; +} +static void do_radix_tree_gather(ulong node, ulong slot, const char *path, + ulong index, void *private) +{ + struct do_radix_tree_info *info = private; + struct radix_tree_pair *rtp = info->data; + + if (info->maxcount) { + rtp[info->count].index = index; + rtp[info->count].value = (void *)slot; + + info->count++; + info->maxcount--; + } +} +static void do_radix_tree_dump_cb(ulong node, ulong slot, const char *path, + ulong index, void *private) +{ + struct do_radix_tree_info *info = private; + struct radix_tree_pair *rtp = info->data; + int (*cb)(ulong) = rtp->value; -ulong RADIX_TREE_MAP_SHIFT = UNINITIALIZED; -ulong RADIX_TREE_MAP_SIZE = UNINITIALIZED; -ulong RADIX_TREE_MAP_MASK = UNINITIALIZED; + /* Caller defined operation */ + if (!cb(slot)) { + error(FATAL, "do_radix_tree: callback " + "operation failed: entry: %ld item: %lx\n", + info->count, slot); + } + info->count++; +} /* * do_radix_tree argument usage: @@ -4010,116 +4054,39 @@ ulong do_radix_tree(ulong root, int flag, struct radix_tree_pair *rtp) { - int i, ilen, height; - long nlen; - ulong index, maxindex, count, maxcount; - long *height_to_maxindex; - char *radix_tree_root_buf; - struct radix_tree_pair *r; - ulong root_rnode; - void *ret; - int (*cb)(ulong) = NULL; - - count = 0; - - if (!VALID_STRUCT(radix_tree_root) || !VALID_STRUCT(radix_tree_node) || - !VALID_MEMBER(radix_tree_root_height) || - !VALID_MEMBER(radix_tree_root_rnode) || - !VALID_MEMBER(radix_tree_node_slots) || - !ARRAY_LENGTH(height_to_maxindex)) - error(FATAL, - "radix trees do not exist (or have changed their format)\n"); - - if (RADIX_TREE_MAP_SHIFT == UNINITIALIZED) { - if (!(nlen = MEMBER_SIZE("radix_tree_node", "slots"))) - error(FATAL, "cannot determine length of " - "radix_tree_node.slots[] array\n"); - nlen /= sizeof(void *); - RADIX_TREE_MAP_SHIFT = ffsl(nlen) - 1; - RADIX_TREE_MAP_SIZE = (1UL << RADIX_TREE_MAP_SHIFT); - RADIX_TREE_MAP_MASK = (RADIX_TREE_MAP_SIZE-1); - } - - ilen = ARRAY_LENGTH(height_to_maxindex); - height_to_maxindex = (long *)GETBUF(ilen * sizeof(long)); - readmem(symbol_value("height_to_maxindex"), KVADDR, - height_to_maxindex, ilen*sizeof(long), - "height_to_maxindex array", FAULT_ON_ERROR); - - if (CRASHDEBUG(1)) { - fprintf(fp, "radix_tree_node.slots[%ld]\n", - RADIX_TREE_MAP_SIZE); - fprintf(fp, "height_to_maxindex[%d]: ", ilen); - for (i = 0; i < ilen; i++) - fprintf(fp, "%lu ", height_to_maxindex[i]); - fprintf(fp, "\n"); - fprintf(fp, "radix_tree_root at %lx:\n", root); - dump_struct("radix_tree_root", (ulong)root, RADIX(16)); - } - - radix_tree_root_buf = GETBUF(SIZE(radix_tree_root)); - readmem(root, KVADDR, radix_tree_root_buf, SIZE(radix_tree_root), - "radix_tree_root", FAULT_ON_ERROR); - height = UINT(radix_tree_root_buf + OFFSET(radix_tree_root_height)); - - if ((height < 0) || (height > ilen)) { - error(INFO, "height_to_maxindex[] index: %ld\n", ilen); - fprintf(fp, "invalid height in radix_tree_root at %lx:\n", root); - dump_struct("radix_tree_root", (ulong)root, RADIX(16)); - return 0; - } - - maxindex = height_to_maxindex[height]; - FREEBUF(height_to_maxindex); - FREEBUF(radix_tree_root_buf); - - root_rnode = root + OFFSET(radix_tree_root_rnode); + struct do_radix_tree_info info = { + .count = 0, + .data = rtp, + }; + struct radix_tree_ops ops = { + .radix = 16, + .private = &info, + }; switch (flag) { case RADIX_TREE_COUNT: - for (index = count = 0; index <= maxindex; index++) { - if (radix_tree_lookup(root_rnode, index, height)) - count++; - } + ops.entry = do_radix_tree_count; break; case RADIX_TREE_SEARCH: - count = 0; - if (rtp->index > maxindex) - break; - - if ((ret = radix_tree_lookup(root_rnode, rtp->index, height))) { - rtp->value = ret; - count++; - } + /* + * FIXME: do_radix_tree_traverse() traverses whole + * radix tree, not binary search. So this search is + * not efficient. + */ + ops.entry = do_radix_tree_search; break; case RADIX_TREE_DUMP: - for (index = count = 0; index <= maxindex; index++) { - if ((ret = - radix_tree_lookup(root_rnode, index, height))) { - fprintf(fp, "[%ld] %lx\n", index, (ulong)ret); - count++; - } - } + ops.entry = do_radix_tree_dump; break; case RADIX_TREE_GATHER: - if (!(maxcount = rtp->index)) - maxcount = (ulong)(-1); /* caller beware */ + if (!(info.maxcount = rtp->index)) + info.maxcount = (ulong)(-1); /* caller beware */ - for (index = count = 0, r = rtp; index <= maxindex; index++) { - if ((ret = - radix_tree_lookup(root_rnode, index, height))) { - r->index = index; - r->value = ret; - count++; - if (--maxcount <= 0) - break; - r++; - } - } + ops.entry = do_radix_tree_gather; break; case RADIX_TREE_DUMP_CB: @@ -4127,62 +4094,15 @@ error(FATAL, "do_radix_tree: need set callback function"); return -EINVAL; } - cb = (int (*)(ulong))rtp->value; - for (index = count = 0; index <= maxindex; index++) { - if ((ret = - radix_tree_lookup(root_rnode, index, height))) { - /* Caller defined operation */ - if (!cb((ulong)ret)) { - error(FATAL, "do_radix_tree: callback " - "operation failed: entry: %ld item: %lx\n", - count, (ulong)ret); - } - count++; - } - } + ops.entry = do_radix_tree_dump_cb; break; default: error(FATAL, "do_radix_tree: invalid flag: %lx\n", flag); } - return count; -} - -static void * -radix_tree_lookup(ulong root_rnode, ulong index, int height) -{ - unsigned int shift; - ulong rnode; - ulong *slots; - - shift = (height-1) * RADIX_TREE_MAP_SHIFT; - - readmem(root_rnode, KVADDR, &rnode, sizeof(void *), - "radix_tree_root rnode", FAULT_ON_ERROR); - - if (rnode & 1) - rnode &= ~1; - - slots = (ulong *)GETBUF(sizeof(void *) * RADIX_TREE_MAP_SIZE); - - while (height > 0) { - if (rnode == 0) - break; - - readmem((ulong)rnode+OFFSET(radix_tree_node_slots), KVADDR, - &slots[0], sizeof(void *) * RADIX_TREE_MAP_SIZE, - "radix_tree_node.slots array", FAULT_ON_ERROR); - - rnode = slots[((index >> shift) & RADIX_TREE_MAP_MASK)]; - - shift -= RADIX_TREE_MAP_SHIFT; - height--; - } - - FREEBUF(slots); - - return (void *)rnode; + do_radix_tree_traverse(root, 1, &ops); + return info.count; } int diff -Nru crash-7.1.4/gdb-7.6.patch crash-7.2.3+real/gdb-7.6.patch --- crash-7.1.4/gdb-7.6.patch 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/gdb-7.6.patch 2018-05-17 17:39:38.000000000 +0000 @@ -1876,3 +1876,519 @@ for (i = strlen (obuf) + prefix_length; i < 6; i++) oappend (" "); oappend (" "); +--- gdb-7.6/bfd/coff-i386.c.orig ++++ gdb-7.6/bfd/coff-i386.c +@@ -141,7 +141,7 @@ coff_i386_reloc (bfd *abfd, + #define DOIT(x) \ + x = ((x & ~howto->dst_mask) | (((x & howto->src_mask) + diff) & howto->dst_mask)) + +- if (diff != 0) ++ if (diff != 0) + { + reloc_howto_type *howto = reloc_entry->howto; + unsigned char *addr = (unsigned char *) data + reloc_entry->address; +--- gdb-7.6/bfd/coff-x86_64.c.orig ++++ gdb-7.6/bfd/coff-x86_64.c +@@ -139,7 +139,7 @@ coff_amd64_reloc (bfd *abfd, + #define DOIT(x) \ + x = ((x & ~howto->dst_mask) | (((x & howto->src_mask) + diff) & howto->dst_mask)) + +- if (diff != 0) ++ if (diff != 0) + { + reloc_howto_type *howto = reloc_entry->howto; + unsigned char *addr = (unsigned char *) data + reloc_entry->address; +--- gdb-7.6/opcodes/arm-dis.c.orig ++++ gdb-7.6/opcodes/arm-dis.c +@@ -2103,7 +2103,7 @@ print_insn_coprocessor (bfd_vma pc, + + /* Is ``imm'' a negative number? */ + if (imm & 0x40) +- imm |= (-1 << 7); ++ imm -= 0x80; + + func (stream, "%d", imm); + } +diff -up gdb-7.6/bfd/elf64-ppc.c.orig gdb-7.6/bfd/elf64-ppc.c +--- gdb-7.6/bfd/elf64-ppc.c.orig 2016-02-02 11:04:25.436527347 -0500 ++++ gdb-7.6/bfd/elf64-ppc.c 2016-02-02 11:11:51.926468454 -0500 +@@ -11743,7 +11743,7 @@ ppc64_elf_size_stubs (struct bfd_link_in + stub_sec = stub_sec->next) + if ((stub_sec->flags & SEC_LINKER_CREATED) == 0) + stub_sec->size = ((stub_sec->size + (1 << htab->plt_stub_align) - 1) +- & (-1 << htab->plt_stub_align)); ++ & -(1 << htab->plt_stub_align)); + + for (stub_sec = htab->stub_bfd->sections; + stub_sec != NULL; +@@ -12093,7 +12093,7 @@ ppc64_elf_build_stubs (bfd_boolean emit_ + stub_sec = stub_sec->next) + if ((stub_sec->flags & SEC_LINKER_CREATED) == 0) + stub_sec->size = ((stub_sec->size + (1 << htab->plt_stub_align) - 1) +- & (-1 << htab->plt_stub_align)); ++ & -(1 << htab->plt_stub_align)); + + for (stub_sec = htab->stub_bfd->sections; + stub_sec != NULL; +--- gdb-7.6/include/opcode/ppc.h.orig ++++ gdb-7.6/include/opcode/ppc.h +@@ -278,7 +278,7 @@ extern const unsigned int num_powerpc_op + /* Use with the shift field of a struct powerpc_operand to indicate + that BITM and SHIFT cannot be used to determine where the operand + goes in the insn. */ +-#define PPC_OPSHIFT_INV (-1 << 31) ++#define PPC_OPSHIFT_INV (-1U << 31) + + /* Values defined for the flags field of a struct powerpc_operand. */ + +--- gdb-7.6/opcodes/mips-dis.c.orig ++++ gdb-7.6/opcodes/mips-dis.c +@@ -245,18 +245,6 @@ static const char * const mips_cp0_names + "c0_taglo", "c0_taghi", "c0_errorepc", "$31" + }; + +-static const struct mips_cp0sel_name mips_cp0sel_names_mipsr5900[] = +-{ +- { 24, 2, "c0_iab" }, +- { 24, 3, "c0_iabm" }, +- { 24, 4, "c0_dab" }, +- { 24, 5, "c0_dabm" }, +- { 24, 6, "c0_dvb" }, +- { 24, 7, "c0_dvbm" }, +- { 25, 1, "c0_perfcnt,1" }, +- { 25, 2, "c0_perfcnt,2" } +-}; +- + static const char * const mips_cp0_names_mips3264[32] = + { + "c0_index", "c0_random", "c0_entrylo0", "c0_entrylo1", +--- gdb-7.6/gdb/ada-lang.c.orig ++++ gdb-7.6/gdb/ada-lang.c +@@ -10503,7 +10503,7 @@ ada_evaluate_subexp (struct type *expect + } + else + arg1 = ada_value_struct_elt (arg1, &exp->elts[pc + 2].string, 0); +- arg1 = unwrap_value (arg1); ++ arg1 = unwrap_value (arg1); + return ada_to_fixed_value (arg1); + + case OP_TYPE: +--- gdb-7.6/gdb/linux-record.c.orig ++++ gdb-7.6/gdb/linux-record.c +@@ -112,7 +112,7 @@ record_linux_sockaddr (struct regcache * + "memory at addr = 0x%s len = %d.\n", + phex_nz (len, tdep->size_pointer), + tdep->size_int); +- return -1; ++ return -1; + } + addrlen = (int) extract_unsigned_integer (a, tdep->size_int, byte_order); + if (addrlen <= 0 || addrlen > tdep->size_sockaddr) +@@ -150,7 +150,7 @@ record_linux_msghdr (struct regcache *re + "len = %d.\n", + phex_nz (addr, tdep->size_pointer), + tdep->size_msghdr); +- return -1; ++ return -1; + } + + /* msg_name msg_namelen */ +@@ -186,7 +186,7 @@ record_linux_msghdr (struct regcache *re + "len = %d.\n", + phex_nz (addr,tdep->size_pointer), + tdep->size_iovec); +- return -1; ++ return -1; + } + tmpaddr = (CORE_ADDR) extract_unsigned_integer (iov, + tdep->size_pointer, +@@ -948,7 +948,7 @@ Do you want to stop the program?"), + "memory at addr = 0x%s len = %d.\n", + OUTPUT_REG (tmpulongest, tdep->arg2), + tdep->size_ulong); +- return -1; ++ return -1; + } + tmpulongest = extract_unsigned_integer (a, tdep->size_ulong, + byte_order); +--- gdb-7.6/gdb/inflow.c.orig ++++ gdb-7.6/gdb/inflow.c +@@ -391,7 +391,7 @@ terminal_ours_1 (int output_only) + if (tinfo->run_terminal != NULL || gdb_has_a_terminal () == 0) + return; + +- { ++ { + #ifdef SIGTTOU + /* Ignore this signal since it will happen when we try to set the + pgrp. */ +--- gdb-7.6/gdb/printcmd.c.orig ++++ gdb-7.6/gdb/printcmd.c +@@ -1045,7 +1045,7 @@ print_command_2 (char *exp, int inspect, + else + val = access_value_history (0); + +- printf_filtered ("%d %d %d %d %d %d\n", ++ printf_filtered ("%d %d %d %d %d %d\n", + TYPE_CODE (check_typedef(value_type (val))), + TYPE_UNSIGNED (check_typedef(value_type (val))), + TYPE_LENGTH (check_typedef(value_type(val))), +--- gdb-7.6/gdb/c-typeprint.c.orig ++++ gdb-7.6/gdb/c-typeprint.c +@@ -1293,7 +1293,7 @@ c_type_print_base (struct type *type, st + if (TYPE_NFIELDS (type) != 0 || TYPE_NFN_FIELDS (type) != 0) + fprintf_filtered (stream, "\n"); + +- for (i = 0; i < TYPE_TYPEDEF_FIELD_COUNT (type); i++) ++ for (i = 0; i < TYPE_TYPEDEF_FIELD_COUNT (type); i++) + { + struct type *target = TYPE_TYPEDEF_FIELD_TYPE (type, i); + +--- gdb-7.6/gdb/symtab.c.orig ++++ gdb-7.6/gdb/symtab.c +@@ -5139,6 +5139,8 @@ static void get_user_print_option_addres + extern int get_frame_offset(CORE_ADDR); + static void gdb_set_crash_block(struct gnu_request *); + void gdb_command_funnel(struct gnu_request *); ++static long lookup_struct_contents(struct gnu_request *); ++static void iterate_datatypes(struct gnu_request *); + + struct objfile *gdb_kernel_objfile = { 0 }; + +@@ -5242,6 +5244,14 @@ gdb_command_funnel(struct gnu_request *r + req->flags |= GNU_COMMAND_FAILED; + break; + ++ case GNU_LOOKUP_STRUCT_CONTENTS: ++ req->value = lookup_struct_contents(req); ++ break; ++ ++ case GNU_GET_NEXT_DATATYPE: ++ iterate_datatypes(req); ++ break; ++ + default: + req->flags |= GNU_COMMAND_FAILED; + break; +@@ -5779,4 +5789,135 @@ gdb_get_crash_block(void) + else + return NULL; + } ++ ++static long ++lookup_struct_contents(struct gnu_request *req) ++{ ++ int i; ++ long r; ++ struct field *f; ++ struct main_type *m; ++ const char *n; ++ struct main_type *top_m = (struct main_type *)req->addr; ++ char *type_name = req->type_name; ++ ++ if (!top_m || !type_name) ++ return 0; ++ ++ for (i = 0; i < top_m->nfields; i++) ++ { ++ f = top_m->flds_bnds.fields + i; ++ if (!f->type) ++ continue; ++ m = f->type->main_type; ++ ++ // If the field is an array, check the target type - ++ // it might be structure, or might not be. ++ // - struct request_sock *syn_table[0]; ++ // here m->target_type->main_type->code is expected ++ // to be TYPE_CODE_PTR ++ // - struct list_head vec[TVN_SIZE]; ++ // here m->target_type->main_type->code should be ++ // TYPE_CODE_STRUCT ++ if (m->code == TYPE_CODE_ARRAY && m->target_type) ++ m = m->target_type->main_type; ++ ++ /* Here is a recursion. ++ * If we have struct variable (not pointer), ++ * scan this inner structure ++ */ ++ if (m->code == TYPE_CODE_STRUCT) { ++ req->addr = (ulong)m; ++ r = lookup_struct_contents(req); ++ req->addr = (ulong)top_m; ++ if (r) ++ return 1; ++ } ++ ++ if (m->code == TYPE_CODE_PTR && m->target_type) ++ m = m->target_type->main_type; ++ if (m->name) ++ n = m->name; ++ else if (m->tag_name) ++ n = m->tag_name; ++ else ++ continue; ++ ++ if (strstr(n, type_name)) ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static void ++iterate_datatypes (struct gnu_request *req) ++{ ++ static struct block_iterator bi; // Keeping this static will simplify code ++ struct block *b; ++ int do_return = 0; ++ struct global_iterator *gi = &req->global_iterator; ++ ++ if (gi->finished) ++ return; ++ ++ if (gi->obj == NULL) ++ { ++ gi->obj = current_program_space->objfiles; ++ gi->symtab = NULL; ++ do_return = 1; // The initial case - we don't need to make next step. ++ } ++ ++ for (; gi->obj; gi->obj = gi->obj->next, gi->symtab = NULL) ++ { ++ if (gi->symtab == NULL) ++ { ++ // Symtab `symtab` is nullified for every objfile ++ if (gi->obj->sf) ++ gi->obj->sf->qf->expand_all_symtabs(gi->obj); ++ gi->symtab = gi->obj->symtabs; ++ gi->sym = NULL; ++ } ++ ++ for (; gi->symtab; gi->symtab = gi->symtab->next, gi->block_index = -1) ++ { ++ if (!gi->symtab->primary) ++ continue; ++ ++ if (gi->block_index == -1) ++ { ++ gi->block_index = GLOBAL_BLOCK; ++ gi->sym = NULL; ++ } ++ for (; gi->block_index <= STATIC_BLOCK; gi->block_index++, gi->sym = NULL) ++ { ++ if (!gi->sym) ++ { ++ b = BLOCKVECTOR_BLOCK(BLOCKVECTOR(gi->symtab), gi->block_index); ++ gi->sym = block_iterator_first(b, &bi); ++ } ++ for (; gi->sym; gi->sym = block_iterator_next(&bi)) ++ { ++ QUIT; ++ ++ if (SYMBOL_CLASS (gi->sym) != LOC_TYPEDEF) ++ continue; ++ ++ // Iteration 1 (do_return == 0): initialization ++ // Iteration 2 (do_return == 1): iterate symbol ++ if (do_return++ == 0) ++ continue; ++ ++ // Yield the current symbol and its size ++ req->addr = (ulong)(gi->sym->type->main_type); ++ req->name = (char *)(gi->sym->ginfo.name); ++ req->length = gi->sym->type->length; ++ ++ return; ++ } ++ } ++ } ++ } ++ 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 -up gdb-7.6/bfd/configure.orig gdb-7.6/bfd/configure +--- gdb-7.6/bfd/configure.orig 2017-02-17 17:19:51.654898822 -0500 ++++ gdb-7.6/bfd/configure 2017-02-17 17:19:57.922038757 -0500 +@@ -12193,7 +12193,7 @@ fi + + NO_WERROR= + if test "${ERROR_ON_WARNING}" = yes ; then +- GCC_WARN_CFLAGS="$GCC_WARN_CFLAGS -Werror" ++ GCC_WARN_CFLAGS="$GCC_WARN_CFLAGS" + NO_WERROR="-Wno-error" + fi + +diff -up gdb-7.6/opcodes/configure.orig gdb-7.6/opcodes/configure +--- gdb-7.6/opcodes/configure.orig 2017-02-17 17:19:08.849943016 -0500 ++++ gdb-7.6/opcodes/configure 2017-02-17 17:19:23.256264699 -0500 +@@ -11539,7 +11539,7 @@ fi + + NO_WERROR= + if test "${ERROR_ON_WARNING}" = yes ; then +- GCC_WARN_CFLAGS="$GCC_WARN_CFLAGS -Werror" ++ GCC_WARN_CFLAGS="$GCC_WARN_CFLAGS" + NO_WERROR="-Wno-error" + fi + +--- gdb-7.6/gdb/symtab.c.orig ++++ gdb-7.6/gdb/symtab.c +@@ -5266,6 +5266,7 @@ gdb_get_line_number(struct gnu_request * + { + struct symtab_and_line sal; + struct symbol *sym; ++ struct objfile *objfile; + CORE_ADDR pc; + + #define LASTCHAR(s) (s[strlen(s)-1]) +@@ -5281,8 +5282,22 @@ gdb_get_line_number(struct gnu_request * + sal = find_pc_line(pc, 0); + + if (!sal.symtab) { +- req->buf[0] = '\0'; +- return; ++ /* ++ * If a module address line number can't be found, it's typically ++ * due to its addrmap still containing offset values because its ++ * objfile doesn't have full symbols loaded. ++ */ ++ if (req->lm) { ++ objfile = req->lm->loaded_objfile; ++ if (!objfile_has_full_symbols(objfile) && objfile->sf) { ++ objfile->sf->qf->expand_all_symtabs(objfile); ++ sal = find_pc_line(pc, 0); ++ } ++ } ++ if (!sal.symtab) { ++ req->buf[0] = '\0'; ++ return; ++ } + } + + if (sal.symtab->filename && sal.symtab->dirname) { +@@ -5557,7 +5572,6 @@ struct load_module *gdb_current_load_mod + static void + gdb_add_symbol_file(struct gnu_request *req) + { +- register struct objfile *loaded_objfile = NULL; + register struct objfile *objfile; + register struct minimal_symbol *m; + struct load_module *lm; +@@ -5576,6 +5590,7 @@ gdb_add_symbol_file(struct gnu_request * + + req->name = lm->mod_namelist; + gdb_delete_symbol_file(req); ++ lm->loaded_objfile = NULL; + + if ((lm->mod_flags & MOD_NOPATCH) == 0) { + for (i = 0 ; i < lm->mod_sections; i++) { +@@ -5623,12 +5638,15 @@ gdb_add_symbol_file(struct gnu_request * + + ALL_OBJFILES(objfile) { + if (same_file(objfile->name, lm->mod_namelist)) { +- loaded_objfile = objfile; ++ if (objfile->separate_debug_objfile) ++ lm->loaded_objfile = objfile->separate_debug_objfile; ++ else ++ lm->loaded_objfile = objfile; + break; + } + } + +- if (!loaded_objfile) ++ if (!lm->loaded_objfile) + req->flags |= GNU_COMMAND_FAILED; + } + diff -Nru crash-7.1.4/gdb-7.6-proc_service.h.patch crash-7.2.3+real/gdb-7.6-proc_service.h.patch --- crash-7.1.4/gdb-7.6-proc_service.h.patch 1970-01-01 00:00:00.000000000 +0000 +++ crash-7.2.3+real/gdb-7.6-proc_service.h.patch 2018-05-17 17:39:38.000000000 +0000 @@ -0,0 +1,67 @@ +--- gdb-7.6/gdb/gdb_proc_service.h.orig ++++ gdb-7.6/gdb/gdb_proc_service.h +@@ -115,7 +115,7 @@ extern pid_t ps_getpid (struct ps_procha + /* Fetch the special per-thread address associated with the given LWP. + This call is only used on a few platforms (most use a normal register). + The meaning of the `int' parameter is machine-dependent. */ +-extern ps_err_e ps_get_thread_area (const struct ps_prochandle *, ++extern ps_err_e ps_get_thread_area (struct ps_prochandle *, + lwpid_t, int, psaddr_t *); + + +--- gdb-7.6/gdb/amd64-linux-nat.c.orig ++++ gdb-7.6/gdb/amd64-linux-nat.c +@@ -493,7 +493,7 @@ amd64_linux_new_fork (struct lwp_info *p + a request for a thread's local storage address. */ + + ps_err_e +-ps_get_thread_area (const struct ps_prochandle *ph, ++ps_get_thread_area (struct ps_prochandle *ph, + lwpid_t lwpid, int idx, void **base) + { + if (gdbarch_bfd_arch_info (target_gdbarch ())->bits_per_word == 32) +--- gdb-7.6/gdb/aarch64-linux-nat.c.orig ++++ gdb-7.6/gdb/aarch64-linux-nat.c +@@ -750,7 +750,7 @@ aarch64_linux_new_fork (struct lwp_info + storage (or its descriptor). */ + + ps_err_e +-ps_get_thread_area (const struct ps_prochandle *ph, ++ps_get_thread_area (struct ps_prochandle *ph, + lwpid_t lwpid, int idx, void **base) + { + struct iovec iovec; +--- gdb-7.6/gdb/arm-linux-nat.c.orig ++++ gdb-7.6/gdb/arm-linux-nat.c +@@ -613,7 +613,7 @@ supply_fpregset (struct regcache *regcac + /* Fetch the thread-local storage pointer for libthread_db. */ + + ps_err_e +-ps_get_thread_area (const struct ps_prochandle *ph, ++ps_get_thread_area (struct ps_prochandle *ph, + lwpid_t lwpid, int idx, void **base) + { + if (ptrace (PTRACE_GET_THREAD_AREA, lwpid, NULL, base) != 0) +--- gdb-7.6/gdb/i386-linux-nat.c.orig ++++ gdb-7.6/gdb/i386-linux-nat.c +@@ -849,7 +849,7 @@ i386_linux_new_fork (struct lwp_info *pa + storage (or its descriptor). */ + + ps_err_e +-ps_get_thread_area (const struct ps_prochandle *ph, ++ps_get_thread_area (struct ps_prochandle *ph, + lwpid_t lwpid, int idx, void **base) + { + /* NOTE: cagney/2003-08-26: The definition of this buffer is found +--- gdb-7.6/gdb/mips-linux-nat.c.orig ++++ gdb-7.6/gdb/mips-linux-nat.c +@@ -154,7 +154,7 @@ mips64_linux_register_addr (struct gdbarch *gdbarch, int regno, int store) + /* Fetch the thread-local storage pointer for libthread_db. */ + + ps_err_e +-ps_get_thread_area (const struct ps_prochandle *ph, ++ps_get_thread_area (struct ps_prochandle *ph, + lwpid_t lwpid, int idx, void **base) + { + if (ptrace (PTRACE_GET_THREAD_AREA, lwpid, NULL, base) != 0) + diff -Nru crash-7.1.4/gdb_interface.c crash-7.2.3+real/gdb_interface.c --- crash-7.1.4/gdb_interface.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/gdb_interface.c 2018-05-17 17:39:38.000000000 +0000 @@ -1,8 +1,8 @@ /* gdb_interface.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-2015,2018 David Anderson + * Copyright (C) 2002-2015,2018 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 @@ -737,6 +737,13 @@ newline, newline, pc->program_name); } } + + if (kt->relocate && + STRNEQ("disassemble", cmd) && STRNEQ(cmd, "disas")) + error(FATAL, + "the gdb \"disassemble\" command is prohibited because the kernel text\n" + "%swas relocated%s; use the crash \"dis\" command instead.\n", + space(strlen(pc->curcmd)+2), kt->flags2 & KASLR ? " by KASLR" : ""); return FALSE; } @@ -826,6 +833,10 @@ readflags = pc->curcmd_flags & PARTIAL_READ_OK ? RETURN_ON_ERROR|RETURN_PARTIAL : RETURN_ON_ERROR; + if (STREQ(pc->curcmd, "bpf") && pc->curcmd_private && + (addr > (ulong)pc->curcmd_private)) + readflags |= QUIET; + if (pc->curcmd_flags & MEMTYPE_UVADDR) memtype = UVADDR; else if (pc->curcmd_flags & MEMTYPE_FILEADDR) @@ -854,6 +865,12 @@ switch (len) { case SIZEOF_8BIT: + if (STREQ(pc->curcmd, "bt")) { + if (readmem(addr, memtype, buf, SIZEOF_8BIT, + "gdb_readmem_callback", readflags)) + return TRUE; + } + p1 = (char *)buf; if ((memtype == KVADDR) && text_value_cache_byte(addr, (unsigned char *)p1)) @@ -871,6 +888,12 @@ return TRUE; case SIZEOF_32BIT: + if (STREQ(pc->curcmd, "bt")) { + if (readmem(addr, memtype, buf, SIZEOF_32BIT, + "gdb_readmem_callback", readflags)) + return TRUE; + } + if ((memtype == KVADDR) && text_value_cache(addr, 0, buf)) return TRUE; @@ -995,7 +1018,8 @@ return FALSE; } } - } + } else if (kt->flags2 & KASLR) + vaddr -= (kt->relocate * -1); req->command = GNU_SET_CRASH_BLOCK; req->addr = vaddr; diff -Nru crash-7.1.4/global_data.c crash-7.2.3+real/global_data.c --- crash-7.1.4/global_data.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/global_data.c 2018-05-17 17:39:38.000000000 +0000 @@ -1,8 +1,8 @@ /* global_data.c - core analysis suite * * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. - * Copyright (C) 2002-2006, 2010, 2012-2013 David Anderson - * Copyright (C) 2002-2006, 2010, 2012-2013 Red Hat, Inc. All rights reserved. + * Copyright (C) 2002-2006, 2010, 2012-2013, 2018 David Anderson + * Copyright (C) 2002-2006, 2010, 2012-2013, 2018 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 @@ -72,6 +72,7 @@ {"*", cmd_pointer, help_pointer, 0}, {"alias", cmd_alias, help_alias, 0}, {"ascii", cmd_ascii, help_ascii, 0}, + {"bpf", cmd_bpf, help_bpf, 0}, {"bt", cmd_bt, help_bt, REFRESH_TASK_TABLE}, {"btop", cmd_btop, help_btop, 0}, {"dev", cmd_dev, help_dev, 0}, diff -Nru crash-7.1.4/help.c crash-7.2.3+real/help.c --- crash-7.1.4/help.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/help.c 2018-05-17 17:39:38.000000000 +0000 @@ -1,8 +1,8 @@ /* help.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-2018 David Anderson + * Copyright (C) 2002-2018 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 @@ -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", @@ -156,11 +159,14 @@ " X86_64:", " phys_base=", " irq_eframe_link=", + " irq_stack_gap=", " max_physmem_bits=", + " kernel_image_size=", " vm=orig (pre-2.6.11 virtual memory address ranges)", " vm=2.6.11 (2.6.11 and later virtual memory address ranges)", " vm=xen (Xen kernel virtual memory address ranges)", " vm=xen-rhel4 (RHEL4 Xen kernel virtual address ranges)", + " vm=5level (5-level page tables)", " PPC64:", " vm=orig", " vm=2.6.14 (4-level page tables)", @@ -172,6 +178,9 @@ " phys_base=", " ARM64:", " phys_offset=", + " kimage_voffset=", + " X86:", + " page_offset=", "", " -x ", " Automatically load extension modules from a particular directory.", @@ -324,8 +333,8 @@ " and verification. The default count is 32768.", "", " --kaslr offset | auto", - " If an x86_64 kernel was configured with CONFIG_RANDOMIZE_BASE, the", - " offset value is equal to the difference between the symbol values ", + " If an x86 or x86_64 kernel was configured with CONFIG_RANDOMIZE_BASE,", + " the offset value is equal to the difference between the symbol values ", " compiled into the vmlinux file and their relocated KASLR value. If", " set to auto, the KASLR offset value will be automatically calculated.", "", @@ -701,6 +710,9 @@ } else if (NETDUMP_DUMPFILE() || KDUMP_DUMPFILE()) { dump_registers_for_elf_dumpfiles(); return; + } else if (VMSS_DUMPFILE()) { + dump_registers_for_vmss_dump(); + return; } error(FATAL, "-r option not supported on %s\n", @@ -806,7 +818,7 @@ char *help_foreach[] = { "foreach", "display command data for multiple tasks in the system", -"[[pid | taskp | name | state | [kernel | user]] ...]\n" +"[[pid | taskp | name | state | [kernel | user | gleader]] ...]\n" " command [flag] [argument]", " This command allows for an examination of various kernel data associated", " with any, or all, tasks in the system, without having to set the context", @@ -821,6 +833,7 @@ " must be a POSIX extended regular expression that will be used", " to match task names.", " user perform the command(s) on all user (non-kernel) threads.", +" gleader perform the command(s) on all user (non-kernel) thread group leaders.", " kernel perform the command(s) on all kernel threads.", " active perform the command(s) on the active thread on each CPU.", " state perform the command(s) on all tasks in the specified state, which", @@ -837,7 +850,7 @@ " net run the \"net\" command (optional flags: -s -S -R -d -x)", " set run the \"set\" command", " ps run the \"ps\" command (optional flags: -G -s -p -c -t -l -a", -" -g -r)", +" -g -r -y)", " sig run the \"sig\" command (optional flag: -g)", " vtop run the \"vtop\" command (optional flags: -c -u -k)\n", " flag Pass this optional flag to the command selected.", @@ -1067,8 +1080,11 @@ " must be a kernel or module text address, which", " may be expressed symbolically or as a hexadecimal", " value.", -" offline show | hide Show or hide command output that is associated", +" offline show | hide show or hide command output that is associated", " with offline cpus.", +" redzone on | off if on, CONFIG_SLUB object addresses displayed by", +" the kmem command will point to the SLAB_RED_ZONE", +" padding inserted at the beginning of the object.", " ", " Internal variables may be set in four manners:\n", " 1. entering the set command in $HOME/.%src.", @@ -1119,6 +1135,7 @@ " gdb: off", " scope: (not set)", " offline: show", +" redzone: on", " ", " Show the current context:\n", " %s> set", @@ -1239,7 +1256,7 @@ char *help_ps[] = { "ps", "display process status information", -"[-k|-u|-G] [-s] [-p|-c|-t|-[l|m][-C cpu]|-a|-g|-r|-S]\n [pid | task | command] ...", +"[-k|-u|-G|-y policy] [-s] [-p|-c|-t|-[l|m][-C cpu]|-a|-g|-r|-S|-A]\n [pid | task | command] ...", " This command displays process status for selected, or all, processes" , " in the system. If no arguments are entered, the process data is", " is displayed for all processes. Specific processes may be selected", @@ -1256,6 +1273,16 @@ " -k restrict the output to only kernel threads.", " -u restrict the output to only user tasks.", " -G display only the thread group leader in a thread group.", +" -y policy restrict the output to tasks having a specified scheduling policy", +" expressed by its integer value or by its (case-insensitive) name;", +" multiple policies may be entered in a comma-separated list:", +" 0 or NORMAL", +" 1 or FIFO", +" 2 or RR", +" 3 or BATCH", +" 4 or ISO", +" 5 or IDLE", +" 6 or DEADLINE", " ", " The process identifier types may be mixed. For each task, the following", " items are displayed:", @@ -1307,6 +1334,7 @@ " -g display tasks by thread group, of selected, or all, tasks.", " -r display resource limits (rlimits) of selected, or all, tasks.", " -S display a summary consisting of the number of tasks in a task state.", +" -A display only the active task on each cpu.", "\nEXAMPLES", " Show the process status of all current tasks:\n", " %s> ps", @@ -1461,6 +1489,14 @@ " UN: 31", " ZO: 1", " ", +" Display only the active task, on each cpu:\n", +" %s> ps -A", +" PID PPID CPU TASK ST %MEM VSZ RSS COMM", +" > 10 2 1 ffff880212969710 IN 0.0 0 0 [migration/1]", +" > 0 0 3 ffff884026d43520 RU 0.0 0 0 [swapper]", +" > 6582 1 2 ffff880f49c52040 RU 0.0 42202472 33368 oracle", +" > 9497 1 0 ffff880549ec2ab0 RU 0.0 42314692 138664 oracle", +" ", " Show all tasks sorted by their task_struct's last_run, timestamp, or", " sched_entity last_arrival timestamp value, whichever applies:\n", " %s> ps -l", @@ -1745,7 +1781,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", @@ -1770,9 +1806,10 @@ " fails or the -t option starts too high in the process stack).", " -l show file and line number of each stack trace text location.", " -e search the stack for possible kernel and user mode exception frames.", -" -E search the IRQ stacks (x86, x86_64 and ppc64), and the exception", -" stacks (x86_64) for possible exception frames; all other arguments", -" will be ignored since this is not a context-sensitive operation.", +" -E search the IRQ stacks (x86, x86_64, arm64, and ppc64), and the", +" exception stacks (x86_64) for possible exception frames; all other", +" arguments except for -c will be ignored since this is not a context-", +" sensitive operation.", " -f display all stack data contained in a frame; this option can be", " used to determine the arguments passed to each function; on ia64,", " the argument register contents are dumped.", @@ -1783,11 +1820,19 @@ " 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; not supported on Linux 4.14 or", +" later kernels.", +" 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", @@ -2067,6 +2112,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 }; @@ -2265,7 +2316,7 @@ char *help_mach[] = { "mach", "machine specific data", -"[-m | -c -[xd]]", +"[-m | -c -[xd] | -o]", " This command displays data specific to a machine type.\n", " -m Display the physical memory map (x86, x86_64 and ia64 only).", " -c Display each cpu's cpuinfo structure (x86, x86_64 and ia64 only).", @@ -2273,6 +2324,7 @@ " Display the hwrpb_struct, and each cpu's percpu_struct (alpha only).", " -x override default output format with hexadecimal format.", " -d override default output format with decimal format.", +" -o Display the OPAL console log (ppc64 only).", "\nEXAMPLES", " %s> mach", " MACHINE TYPE: i686", @@ -2299,9 +2351,486 @@ " 00000000fec00000 - 00000000fec90000 E820_RESERVED", " 00000000fee00000 - 00000000fee10000 E820_RESERVED", " 00000000ffb00000 - 0000000100000000 E820_RESERVED", +" ", +" Display the OPAL console log:\n", +" %s> mach -o", +" [ 65.219056911,5] SkiBoot skiboot-5.4.0-218-ge0225cc-df9a248 starting...", +" [ 65.219065872,5] initial console log level: memory 7, driver 5", +" [ 65.219068917,6] CPU: P8 generation processor(max 8 threads/core)", +" [ 65.219071681,7] CPU: Boot CPU PIR is 0x0060 PVR is 0x004d0200", +" [ 65.219074685,7] CPU: Initial max PIR set to 0x1fff", +" [ 65.219607955,5] FDT: Parsing fdt @0xff00000", +" [ 494.026291523,7] BT: seq 0x25 netfn 0x0a cmd 0x48: Message sent to host", +" [ 494.027636927,7] BT: seq 0x25 netfn 0x0a cmd 0x48: IPMI MSG done", NULL }; +char *help_bpf[] = { +"bpf", +"extended Berkeley Packet Filter (eBPF)", +"[[-p ID | -P] [-tTj]] [[-m ID] | -M] [-s] [-xd]", +" ", +" This command provides information on currently-loaded eBPF programs and maps.", +" With no arguments, basic information about each loaded eBPF program and map", +" is displayed. For each eBPF program, its ID number, the addresses of its ", +" bpf_prog and bpf_prog_aux data structures, its type, tag, and the IDs of the", +" eBPF maps that it uses are displayed. For each eBPF map, its ID number, the", +" address of its bpf_map data structure, its type, and the hexadecimal value of", +" its map_flags are displayed.", +" ", +" -p ID displays the basic information specific to the program ID, plus the", +" size in bytes of its translated bytecode, the size in bytes of its", +" jited code, the number of bytes locked into memory, the time that", +" the program was loaded, whether it is GPL compatible, and its UID.", +" -P same as -p, but displays the basic and extra data for all programs.", +" -m ID displays the basic information specific to the map ID, plus the", +" size in bytes of its key and value, the maximum number of key-value", +" pairs that can be stored within the map, the number of bytes locked", +" into memory, its name string, and its UID.", +" -M same as -m, but displays the basic and extra data for all maps.", +" -t translate the bytecode of the specified program ID.", +" -T same as -t, but also dump the bytecode of each instruction.", +" -j disassemble the jited code of the specified program ID.", +" -s with -p or -P, dump the bpf_prog and bpf_prog_aux data structures.", +" with -m or -M, dump the bpf_map structure.", +" -x with -s, override default output format with hexadecimal format.", +" -d with -s, override default output format with decimal format.", +" ", +"EXAMPLES", +" Display all loaded eBPF programs and maps:\n", +" %s> bpf", +" ID BPF_PROG BPF_PROG_AUX BPF_PROG_TYPE TAG USED_MAPS", +" 13 ffffbc00c06d1000 ffff9ff260f0c400 CGROUP_SKB 7be49e3934a125ba 13,14", +" 14 ffffbc00c0761000 ffff9ff260f0f600 CGROUP_SKB 2a142ef67aaad174 13,14", +" 15 ffffbc00c001d000 ffff9ff2618f9e00 CGROUP_SKB 7be49e3934a125ba 15,16", +" 16 ffffbc00c06c9000 ffff9ff2618f9400 CGROUP_SKB 2a142ef67aaad174 15,16", +" 19 ffffbc00c0d39000 ffff9ff2610fa000 CGROUP_SKB 7be49e3934a125ba 19,20", +" 20 ffffbc00c0d41000 ffff9ff2610f8e00 CGROUP_SKB 2a142ef67aaad174 19,20", +" 30 ffffbc00c065f000 ffff9ff1b64de200 KPROBE 69fed6de18629d7a 32", +" 31 ffffbc00c065b000 ffff9ff1b64df200 KPROBE 69fed6de18629d7a 37", +" 32 ffffbc00c0733000 ffff9ff1b64dc600 KPROBE 69fed6de18629d7a 38", +" 33 ffffbc00c0735000 ffff9ff1b64dca00 KPROBE 69fed6de18629d7a 39", +" 34 ffffbc00c0737000 ffff9ff1b64dfc00 KPROBE 4abbddae72a6ee17 33,36,34", +" 36 ffffbc00c0839000 ffff9ff1b64dd000 KPROBE da4fc6a3f41761a2 32", +" 41 ffffbc00c07ec000 ffff9ff207b70400 TRACEPOINT e2094f9f46284bf6 55,54", +" 44 ffffbc00c07ee000 ffff9ff1b64dc800 PERF_EVENT 19578a12836c4115 62", +" 46 ffffbc00c07f0000 ffff9ff207b70400 SOCKET_FILTER 1fcfc04afd689133 64", +" ", +" ID BPF_MAP BPF_MAP_TYPE MAP_FLAGS", +" 13 ffff9ff260f0ec00 LPM_TRIE 00000001", +" 14 ffff9ff260f0de00 LPM_TRIE 00000001", +" 15 ffff9ff2618fbe00 LPM_TRIE 00000001", +" 16 ffff9ff2618fb800 LPM_TRIE 00000001", +" 19 ffff9ff2610faa00 LPM_TRIE 00000001", +" 20 ffff9ff2610fb800 LPM_TRIE 00000001", +" 32 ffff9ff260d74000 HASH 00000000", +" 33 ffff9ff260d76400 LRU_HASH 00000000", +" 34 ffff9ff260d70000 LRU_HASH 00000002", +" 35 ffff9ff260d73800 LRU_HASH 00000004", +" 36 ffff9ff1b4f44000 ARRAY_OF_MAPS 00000000", +" 37 ffff9ff260d77c00 PERCPU_HASH 00000000", +" 38 ffff9ff260d70800 HASH 00000001", +" 39 ffff9ff260d76c00 PERCPU_HASH 00000001", +" 54 ffff9ff260dd2c00 HASH 00000000", +" 55 ffff9ff260dd1400 HASH 00000000", +" 62 ffff9ff1ae784000 HASH 00000000", +" 64 ffff9ff1aea15000 ARRAY 00000000", +" ", +" Display additional data about program ID 20:\n", +" %s> bpf -p 20", +" ID BPF_PROG BPF_PROG_AUX BPF_PROG_TYPE TAG USED_MAPS", +" 20 ffffbc00c0d41000 ffff9ff2610f8e00 CGROUP_SKB 2a142ef67aaad174 19,20", +" XLATED: 296 JITED: 229 MEMLOCK: 4096", +" LOAD_TIME: Fri Apr 20 19:39:21 2018", +" GPL_COMPATIBLE: yes UID: 0", +" ", +" Display additional data about map ID 34:\n", +" %s> bpf -m 34", +" ID BPF_MAP BPF_MAP_TYPE MAP_FLAGS", +" 34 ffff9ff260d70000 LRU_HASH 00000000", +" KEY_SIZE: 4 VALUE_SIZE: 8 MAX_ENTRIES: 10000 MEMLOCK: 1953792", +" NAME: \"lru_hash_map\" UID: 0", +"", +" Disassemble the jited program of program ID 20:\n", +" %s> bpf -p 20 -j", +" ID BPF_PROG BPF_PROG_AUX BPF_PROG_TYPE TAG USED_MAPS", +" 20 ffffbc00c0d41000 ffff9ff2610f8e00 CGROUP_SKB 2a142ef67aaad174 19,20", +" XLATED: 296 JITED: 229 MEMLOCK: 4096", +" LOAD_TIME: Fri Apr 20 19:39:21 2018", +" GPL_COMPATIBLE: yes UID: 0", +" ", +" 0xffffffffc06887a2: push \%rbp ", +" 0xffffffffc06887a3: mov \%rsp,\%rbp ", +" 0xffffffffc06887a6: sub $0x40,\%rsp ", +" 0xffffffffc06887ad: sub $0x28,\%rbp ", +" 0xffffffffc06887b1: mov \%rbx,0x0(\%rbp) ", +" 0xffffffffc06887b5: mov \%r13,0x8(\%rbp) ", +" 0xffffffffc06887b9: mov \%r14,0x10(\%rbp) ", +" 0xffffffffc06887bd: mov \%r15,0x18(\%rbp) ", +" 0xffffffffc06887c1: xor \%eax,\%eax ", +" 0xffffffffc06887c3: mov \%rax,0x20(\%rbp) ", +" 0xffffffffc06887c7: mov \%rdi,\%rbx ", +" 0xffffffffc06887ca: movzwq 0xc0(\%rbx),\%r13 ", +" 0xffffffffc06887d2: xor \%r14d,\%r14d ", +" 0xffffffffc06887d5: cmp $0x8,\%r13 ", +" 0xffffffffc06887d9: jne 0xffffffffc068881b ", +" 0xffffffffc06887db: mov \%rbx,\%rdi ", +" 0xffffffffc06887de: mov $0xc,\%esi ", +" 0xffffffffc06887e3: mov \%rbp,\%rdx ", +" 0xffffffffc06887e6: add $0xfffffffffffffffc,\%rdx ", +" 0xffffffffc06887ea: mov $0x4,\%ecx ", +" 0xffffffffc06887ef: callq 0xffffffffb0865340 ", +" 0xffffffffc06887f4: movabs $0xffff9ff2610faa00,\%rdi ", +" 0xffffffffc06887fe: mov \%rbp,\%rsi ", +" 0xffffffffc0688801: add $0xfffffffffffffff8,\%rsi ", +" 0xffffffffc0688805: movl $0x20,0x0(\%rsi) ", +" 0xffffffffc068880c: callq 0xffffffffb01fcba0 ", +" 0xffffffffc0688811: cmp $0x0,\%rax ", +" 0xffffffffc0688815: je 0xffffffffc068881b ", +" 0xffffffffc0688817: or $0x2,\%r14d ", +" 0xffffffffc068881b: cmp $0xdd86,\%r13 ", +" 0xffffffffc0688822: jne 0xffffffffc0688864 ", +" 0xffffffffc0688824: mov \%rbx,\%rdi ", +" 0xffffffffc0688827: mov $0x8,\%esi ", +" 0xffffffffc068882c: mov \%rbp,\%rdx ", +" 0xffffffffc068882f: add $0xfffffffffffffff0,\%rdx ", +" 0xffffffffc0688833: mov $0x10,\%ecx ", +" 0xffffffffc0688838: callq 0xffffffffb0865340 ", +" 0xffffffffc068883d: movabs $0xffff9ff2610fb800,\%rdi ", +" 0xffffffffc0688847: mov \%rbp,\%rsi ", +" 0xffffffffc068884a: add $0xffffffffffffffec,\%rsi ", +" 0xffffffffc068884e: movl $0x80,0x0(\%rsi) ", +" 0xffffffffc0688855: callq 0xffffffffb01fcba0 ", +" 0xffffffffc068885a: cmp $0x0,\%rax ", +" 0xffffffffc068885e: je 0xffffffffc0688864 ", +" 0xffffffffc0688860: or $0x2,\%r14d ", +" 0xffffffffc0688864: mov $0x1,\%eax ", +" 0xffffffffc0688869: cmp $0x2,\%r14 ", +" 0xffffffffc068886d: jne 0xffffffffc0688871 ", +" 0xffffffffc068886f: xor \%eax,\%eax ", +" 0xffffffffc0688871: mov 0x0(\%rbp),\%rbx ", +" 0xffffffffc0688875: mov 0x8(\%rbp),\%r13 ", +" 0xffffffffc0688879: mov 0x10(\%rbp),\%r14 ", +" 0xffffffffc068887d: mov 0x18(\%rbp),\%r15 ", +" 0xffffffffc0688881: add $0x28,\%rbp ", +" 0xffffffffc0688885: leaveq ", +" 0xffffffffc0688886: retq ", +" ", +" Translate each bytecode instruction of program ID 13:\n", +" %s> bpf -p 13 -t", +" ID BPF_PROG BPF_PROG_AUX BPF_PROG_TYPE TAG USED_MAPS", +" 13 ffffbc00c06d1000 ffff9ff260f0c400 CGROUP_SKB 7be49e3934a125ba 13,14", +" XLATED: 296 JITED: 229 MEMLOCK: 4096", +" LOAD_TIME: Fri Apr 20 19:39:11 2018", +" GPL_COMPATIBLE: yes UID: 0", +" ", +" 0: (bf) r6 = r1", +" 1: (69) r7 = *(u16 *)(r6 +192)", +" 2: (b4) (u32) r8 = (u32) 0", +" 3: (55) if r7 != 0x8 goto pc+14", +" 4: (bf) r1 = r6", +" 5: (b4) (u32) r2 = (u32) 16", +" 6: (bf) r3 = r10", +" 7: (07) r3 += -4", +" 8: (b4) (u32) r4 = (u32) 4", +" 9: (85) call bpf_skb_load_bytes#6793152", +" 10: (18) r1 = map[id:13]", +" 12: (bf) r2 = r10", +" 13: (07) r2 += -8", +" 14: (62) *(u32 *)(r2 +0) = 32", +" 15: (85) call bpf_map_lookup_elem#73760", +" 16: (15) if r0 == 0x0 goto pc+1", +" 17: (44) (u32) r8 |= (u32) 2", +" 18: (55) if r7 != 0xdd86 goto pc+14", +" 19: (bf) r1 = r6", +" 20: (b4) (u32) r2 = (u32) 24", +" 21: (bf) r3 = r10", +" 22: (07) r3 += -16", +" 23: (b4) (u32) r4 = (u32) 16", +" 24: (85) call bpf_skb_load_bytes#6793152", +" 25: (18) r1 = map[id:14]", +" 27: (bf) r2 = r10", +" 28: (07) r2 += -20", +" 29: (62) *(u32 *)(r2 +0) = 128", +" 30: (85) call bpf_map_lookup_elem#73760", +" 31: (15) if r0 == 0x0 goto pc+1", +" 32: (44) (u32) r8 |= (u32) 2", +" 33: (b7) r0 = 1", +" 34: (55) if r8 != 0x2 goto pc+1", +" 35: (b7) r0 = 0", +" 36: (95) exit", +" ", +" Translate, and then dump each bytecode instruction of program ID 13:\n", +" %s> bpf -p 13 -T", +" ID BPF_PROG BPF_PROG_AUX BPF_PROG_TYPE TAG USED_MAPS", +" 13 ffffbc00c06d1000 ffff9ff260f0c400 CGROUP_SKB 7be49e3934a125ba 13,14", +" XLATED: 296 JITED: 229 MEMLOCK: 4096", +" LOAD_TIME: Fri Apr 20 19:39:11 2018", +" GPL_COMPATIBLE: yes UID: 0", +" ", +" 0: (bf) r6 = r1", +" bf 16 00 00 00 00 00 00", +" 1: (69) r7 = *(u16 *)(r6 +192)", +" 69 67 c0 00 00 00 00 00", +" 2: (b4) (u32) r8 = (u32) 0", +" b4 08 00 00 00 00 00 00", +" 3: (55) if r7 != 0x8 goto pc+14", +" 55 07 0e 00 08 00 00 00", +" 4: (bf) r1 = r6", +" bf 61 00 00 00 00 00 00", +" 5: (b4) (u32) r2 = (u32) 16", +" b4 02 00 00 10 00 00 00", +" 6: (bf) r3 = r10", +" bf a3 00 00 00 00 00 00", +" 7: (07) r3 += -4", +" 07 03 00 00 fc ff ff ff", +" 8: (b4) (u32) r4 = (u32) 4", +" b4 04 00 00 04 00 00 00", +" 9: (85) call bpf_skb_load_bytes#6793152", +" 85 00 00 00 c0 a7 67 00", +" 10: (18) r1 = map[id:13]", +" 18 01 00 00 00 7a 96 61 00 00 00 00 b2 9d ff ff", +" 12: (bf) r2 = r10", +" bf a2 00 00 00 00 00 00", +" 13: (07) r2 += -8", +" 07 02 00 00 f8 ff ff ff", +" 14: (62) *(u32 *)(r2 +0) = 32", +" 62 02 00 00 20 00 00 00", +" 15: (85) call bpf_map_lookup_elem#73760", +" 85 00 00 00 20 20 01 00", +" 16: (15) if r0 == 0x0 goto pc+1", +" 15 00 01 00 00 00 00 00", +" 17: (44) (u32) r8 |= (u32) 2", +" 44 08 00 00 02 00 00 00", +" 18: (55) if r7 != 0xdd86 goto pc+14", +" 55 07 0e 00 86 dd 00 00", +" 19: (bf) r1 = r6", +" bf 61 00 00 00 00 00 00", +" 20: (b4) (u32) r2 = (u32) 24", +" b4 02 00 00 18 00 00 00", +" 21: (bf) r3 = r10", +" bf a3 00 00 00 00 00 00", +" 22: (07) r3 += -16", +" 07 03 00 00 f0 ff ff ff", +" 23: (b4) (u32) r4 = (u32) 16", +" b4 04 00 00 10 00 00 00", +" 24: (85) call bpf_skb_load_bytes#6793152", +" 85 00 00 00 c0 a7 67 00", +" 25: (18) r1 = map[id:14]", +" 18 01 00 00 00 68 96 61 00 00 00 00 b2 9d ff ff", +" 27: (bf) r2 = r10", +" bf a2 00 00 00 00 00 00", +" 28: (07) r2 += -20", +" 07 02 00 00 ec ff ff ff", +" 29: (62) *(u32 *)(r2 +0) = 128", +" 62 02 00 00 80 00 00 00", +" 30: (85) call bpf_map_lookup_elem#73760", +" 85 00 00 00 20 20 01 00", +" 31: (15) if r0 == 0x0 goto pc+1", +" 15 00 01 00 00 00 00 00", +" 32: (44) (u32) r8 |= (u32) 2", +" 44 08 00 00 02 00 00 00", +" 33: (b7) r0 = 1", +" b7 00 00 00 01 00 00 00", +" 34: (55) if r8 != 0x2 goto pc+1", +" 55 08 01 00 02 00 00 00", +" 35: (b7) r0 = 0", +" b7 00 00 00 00 00 00 00", +" 36: (95) exit", +" 95 00 00 00 00 00 00 00", +" ", +" Display the bpf_map data structure for map ID 13:\n", +" %s> bpf -m 13 -s", +" ID BPF_MAP BPF_MAP_TYPE MAP_FLAGS", +" 13 ffff9ff260f0ec00 LPM_TRIE 00000001", +" KEY_SIZE: 8 VALUE_SIZE: 8 MAX_ENTRIES: 1 MEMLOCK: 4096", +" NAME: (unused) UID: 0", +" ", +" struct bpf_map {", +" ops = 0xffffffffb0e36720, ", +" inner_map_meta = 0x0, ", +" security = 0xffff9ff26873a158, ", +" map_type = BPF_MAP_TYPE_LPM_TRIE, ", +" key_size = 8, ", +" value_size = 8, ", +" max_entries = 1, ", +" map_flags = 1, ", +" pages = 1, ", +" id = 13, ", +" numa_node = -1, ", +" unpriv_array = false, ", +" user = 0xffffffffb14578a0, ", +" refcnt = {", +" counter = 3", +" }, ", +" usercnt = {", +" counter = 1", +" }, ", +" work = {", +" data = {", +" counter = 0", +" }, ", +" entry = {", +" next = 0x0, ", +" prev = 0x0", +" }, ", +" func = 0x0, ", +" lockdep_map = {", +" key = 0x0, ", +" class_cache = {0x0, 0x0}, ", +" name = 0x0, ", +" cpu = 0, ", +" ip = 0", +" }", +" }, ", +" name = \"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\"", +" }", +" ", +" Display the bpf_prog and bpf_prog_aux structures for program ID 13:\n", +" %s> bpf -p 13 -s", +" ID BPF_PROG BPF_PROG_AUX BPF_PROG_TYPE TAG USED_MAPS", +" 13 ffffbc00c06d1000 ffff9ff260f0c400 CGROUP_SKB 7be49e3934a125ba 13,14", +" XLATED: 296 JITED: 229 MEMLOCK: 4096", +" LOAD_TIME: Fri Apr 20 19:39:10 2018", +" GPL_COMPATIBLE: yes UID: 0", +" ", +" struct bpf_prog {", +" pages = 1, ", +" jited = 1, ", +" jit_requested = 1, ", +" locked = 1, ", +" gpl_compatible = 1, ", +" cb_access = 0, ", +" dst_needed = 0, ", +" blinded = 0, ", +" is_func = 0, ", +" kprobe_override = 0, ", +" type = BPF_PROG_TYPE_CGROUP_SKB, ", +" len = 37, ", +" jited_len = 229, ", +" tag = \"{\\344\\236\\071\\064\\241\%\\272\", ", +" aux = ffff9ff260f0c400,", +" orig_prog = 0x0, ", +" bpf_func = 0xffffffffc0218a59, ", +" {", +" insns = 0xffffb0cf406d1030, ", +" insnsi = 0xffffb0cf406d1030", +" }", +" }", +" ", +" struct bpf_prog_aux {", +" refcnt = {", +" counter = 2", +" }, ", +" used_map_cnt = 2, ", +" max_ctx_offset = 20, ", +" stack_depth = 20, ", +" id = 13, ", +" func_cnt = 0, ", +" offload_requested = false, ", +" func = 0x0, ", +" jit_data = 0x0, ", +" ksym_tnode = {", +" node = {{", +" __rb_parent_color = 18446635988194065457, ", +" rb_right = 0x0, ", +" rb_left = 0x0", +" }, {", +" __rb_parent_color = 18446635988194065481, ", +" rb_right = 0x0, ", +" rb_left = 0x0", +" }}", +" }, ", +" ksym_lnode = {", +" next = 0xffff9db261966460, ", +" prev = 0xffffffffb85d1150", +" }, ", +" ops = 0xffffffffb7f09060, ", +" used_maps = 0xffff9db261e03600, ", +" prog = 0xffffb0cf406d1000, ", +" user = 0xffffffffb84578a0, ", +" load_time = 23962237943, ", +" name = \"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\",", +" security = 0xffff9db266f9cf50, ", +" offload = 0x0, ", +" {", +" work = {", +" data = {", +" counter = 0", +" }, ", +" entry = {", +" next = 0x0, ", +" prev = 0x0", +" }, ", +" func = 0x0, ", +" lockdep_map = {", +" key = 0x0, ", +" class_cache = {0x0, 0x0}, ", +" name = 0x0, ", +" cpu = 0, ", +" ip = 0", +" }", +" }, ", +" rcu = {", +" next = 0x0, ", +" func = 0x0", +" }", +" }", +" }", +" ", +" Display the extra data about all programs:\n", +" %s> bpf -P", +" ID BPF_PROG BPF_PROG_AUX BPF_PROG_TYPE TAG USED_MAPS", +" 13 ffffbc00c06d1000 ffff9ff260f0c400 CGROUP_SKB 7be49e3934a125ba 13,14", +" XLATED: 296 JITED: 229 MEMLOCK: 4096", +" LOAD_TIME: Fri Apr 20 19:39:10 2018", +" GPL_COMPATIBLE: yes UID: 0", +" ", +" ID BPF_PROG BPF_PROG_AUX BPF_PROG_TYPE TAG USED_MAPS", +" 14 ffffbc00c0761000 ffff9ff260f0f600 CGROUP_SKB 2a142ef67aaad174 13,14", +" XLATED: 296 JITED: 229 MEMLOCK: 4096", +" LOAD_TIME: Fri Apr 20 19:39:10 2018", +" GPL_COMPATIBLE: yes UID: 0", +" ", +" ID BPF_PROG BPF_PROG_AUX BPF_PROG_TYPE TAG USED_MAPS", +" 15 ffffbc00c001d000 ffff9ff2618f9e00 CGROUP_SKB 7be49e3934a125ba 15,16", +" XLATED: 296 JITED: 229 MEMLOCK: 4096", +" LOAD_TIME: Fri Apr 20 19:39:11 2018", +" GPL_COMPATIBLE: yes UID: 0", +" ", +" ...", +" ", +" ID BPF_PROG BPF_PROG_AUX BPF_PROG_TYPE TAG USED_MAPS", +" 75 ffffbc00c0ed1000 ffff9ff2429c6400 KPROBE da4fc6a3f41761a2 107", +" XLATED: 5168 JITED: 2828 MEMLOCK: 8192", +" LOAD_TIME: Fri Apr 27 14:54:40 2018", +" GPL_COMPATIBLE: yes UID: 0", +" ", +" Display the extra data for all maps:\n", +" %s> bpf -M", +" ID BPF_MAP BPF_MAP_TYPE MAP_FLAGS", +" 13 ffff9ff260f0ec00 LPM_TRIE 00000001 ", +" KEY_SIZE: 8 VALUE_SIZE: 8 MAX_ENTRIES: 1 MEMLOCK: 4096", +" NAME: (unused) UID: 0", +" ", +" ID BPF_MAP BPF_MAP_TYPE MAP_FLAGS", +" 14 ffff9ff260f0de00 LPM_TRIE 00000001 ", +" KEY_SIZE: 20 VALUE_SIZE: 8 MAX_ENTRIES: 1 MEMLOCK: 4096", +" NAME: (unused) UID: 0", +" ", +" ...", +" ", +" ID BPF_MAP BPF_MAP_TYPE MAP_FLAGS", +" 108 ffff9ff1aeab9400 LRU_HASH 00000000 ", +" KEY_SIZE: 4 VALUE_SIZE: 8 MAX_ENTRIES: 1000 MEMLOCK: 147456", +" NAME: \"lru_hash_lookup\" UID: 0", +" ", +" To display all possible information that this command offers about", +" all programs and maps, enter:\n", +" %s> bpf -PM -jTs", +NULL +}; + char *help_map[] = { "map", "store KVM dumpfile memory map data", @@ -2339,7 +2868,7 @@ char *help_timer[] = { "timer", "timer queue data", -"[-r]", +"[-r][-C cpu]", " This command displays the timer queue entries, both old- and new-style,", " in chronological order. In the case of the old-style timers, the", " timer_table array index is shown; in the case of the new-style timers, ", @@ -2349,6 +2878,8 @@ " chronological order. In the case of the old-style hrtimers, the", " expiration time is a single value; in the new-style hrtimers, the", " expiration time is a range.", +" -C cpu Restrict the output to one or more CPUs, where multiple cpu[s] can", +" be specified, for example, as \"1,3,5\", \"1-3\", or \"1,3,5-7,10\".", "\nEXAMPLES", " %s> timer", " JIFFIES", @@ -2482,7 +3013,7 @@ char *help_runq[] = { "runq", "run queue", -"[-t] [-m] [-g] [-c cpu(s)]", +"[-t] [-T] [-m] [-g] [-c cpu(s)]", " With no argument, this command displays the tasks on the run queues", " of each cpu.", " ", @@ -2491,6 +3022,8 @@ " whichever applies; following each cpu timestamp is the last_run or ", " timestamp value of the active task on that cpu, whichever applies, ", " along with the task identification.", +" -T Display the time lag of each CPU relative to the most recent runqueue", +" timestamp.", " -m Display the amount of time that the active task on each cpu has been", " running, expressed in a format consisting of days, hours, minutes, ", " seconds and milliseconds.", @@ -2672,7 +3205,7 @@ char *help_dev[] = { "dev", "device data", -"[-i | -p | -d]", +"[-i | -p | -d | -D]", " If no argument is entered, this command dumps character and block", " device data.\n", " -i display I/O port usage; on 2.4 kernels, also display I/O memory usage.", @@ -2683,7 +3216,10 @@ " 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).", +" -D same as -d, but filter out disks with no in-progress I/O requests.", "\nEXAMPLES", " Display character and block device data:\n", " %s> dev", @@ -2823,7 +3359,7 @@ char *help_search[] = { "search", "search memory", -"[-s start] [ -[kKV] | -u | -p | -t ] [-e end | -l length] [-m mask]\n" +"[-s start] [ -[kKV] | -u | -p | -t | -T ] [-e end | -l length] [-m mask]\n" " [-x count] -[cwh] [value | (expression) | symbol | string] ...", " This command searches for a given value within a range of user virtual, kernel", " virtual, or physical memory space. If no end nor length value is entered, ", @@ -2854,6 +3390,7 @@ " -t Search only the kernel stack pages of every task. If one or more", " matches are found in a task's kernel stack, precede the output", " with a task-identifying header.", +" -T Same as -t, except only the active task(s) are considered.", " -e end Stop the search at this hexadecimal user or kernel virtual", " address, kernel symbol, or physical address. The end address", " must be appropriate for the memory type specified.", @@ -3348,7 +3885,7 @@ char *help_log[] = { "log", "dump system message buffer", -"[-tdm]", +"[-tdma]", " This command dumps the kernel log_buf contents in chronological order. The", " command supports the older log_buf formats, which may or may not contain a", " timestamp inserted prior to each message, as well as the newer variable-length", @@ -3363,6 +3900,8 @@ " the variable-length record format, the level will be displayed in ", " hexadecimal, and depending upon the kernel version, also contains the", " facility or flags bits.", +" -a Dump the audit logs remaining in kernel audit buffers that have not", +" been copied out to the user-space audit daemon.", " ", "\nEXAMPLES", " Dump the kernel message buffer:\n", @@ -3455,35 +3994,92 @@ " SUBSYSTEM=pci", " DEVICE=+pci:0000:ff:03.1", " ...", +" ", +" Dump the kernel audit logs:\n", +" %s> log -a", +" type=1320 audit(1489384479.809:4342):", +" type=1300 audit(1489384479.809:4343): arch=c000003e syscall=0 success=yes ", +" exit=0 a0=4 a1=7f84154a2000 a2=400 a3=22 items=0 ppid=2560 pid=2591 auid=0 ", +" uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=ttyS0 ses=1 ", +" comm=\"pidof\" exe=\"/usr/sbin/killall5\" ", +" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)", +" type=1320 audit(1489384479.809:4343):", +" type=1300 audit(1489384479.809:4344): arch=c000003e syscall=3 success=yes ", +" exit=0 a0=4 a1=1 a2=8 a3=0 items=0 ppid=2560 pid=2591 auid=0 uid=0 gid=0 ", +" euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=ttyS0 ses=1 comm=\"pidof\" ", +" exe=\"/usr/sbin/killall5\" ", +" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)", +" type=1320 audit(1489384479.809:4344):", +" type=1300 audit(1489384479.809:4345): arch=c000003e syscall=11 ", +" success=yes exit=0 a0=7f84154a2000 a1=1000 a2=0 a3=0 items=0 ppid=2560 ", +" pid=2591 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 ", +" tty=ttyS0 ses=1 comm=\"pidof\" exe=\"/usr/sbin/killall5\" ", +" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)", +" type=1320 audit(1489384479.809:4345):", +" type=1300 audit(1489384479.809:4346): arch=c000003e syscall=2 success=yes ", +" exit=4 a0=7ffcfd20f5a0 a1=0 a2=1b6 a3=24 items=1 ppid=2560 pid=2591 auid=0", +" uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=ttyS0 ses=1 ", +" comm=\"pidof\" exe=\"/usr/sbin/killall5\" ", +" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)", +" type=1307 audit(1489384479.809:4346): cwd=\"/proc\"", +" ...", +" ", NULL }; char *help_whatis[] = { "whatis", "search symbol table for data or type information", -"[struct | union | typedef | symbol] ", +"[[-o] [struct | union | typedef | symbol]] | \n [[-r [size|range]] [-m member]]", " This command displays the definition of structures, unions, typedefs or", -" text/data symbols.\n", +" text/data symbols:\n", " struct a structure name. The output is the same as if the \"struct\"", " command was used.", " union a union name. The output is the same as if the \"union\" command", " was used.", +" -o display the offsets of structure/union members.", " typedef a typedef name. If the typedef translates to a structure or union", " the output is the same as if the \"struct\" or \"union\" command", " was used. If the typedef is a primitive datatype, the one-line", " declaration is displayed.", " symbol a kernel symbol. ", +"", +" Alternatively, a search can be made for data structures of a given size or", +" size range, that contain a member of a given type, or contain a pointer to", +" given type. The -r and -m options may be used alone or in conjunction with", +" one another:\n", +" -r size search for structures of this exact size.", +" -r range search for structures of a range of sizes, expressed as \"low-high\".", +" -m member search for structures that contain a member of this data type, or", +" that contain a pointer to this data type; if a structure contains ", +" another structure, the members of the embedded structure will also", +" be subject to the search. The member argument may also be expressed", +" as a substring of a member's data type.", "\nEXAMPLES", " Display the definition of a linux_binfmt structure:\n", " %s> whatis linux_binfmt", " struct linux_binfmt {", -" struct linux_binfmt *next;", -" struct module *module;", -" int (*load_binary) ();", -" int (*load_shlib) ();", -" int (*core_dump) ();", -" };", -" ", +" struct list_head lh;", +" struct module *module;", +" int (*load_binary)(struct linux_binprm *);", +" int (*load_shlib)(struct file *);", +" int (*core_dump)(struct coredump_params *);", +" unsigned long min_coredump;", +" }", +" SIZE: 56", +"", +" Display the same structure with member offsets:\n", +" %s> whatis -o linux_binfmt", +" struct linux_binfmt {", +" [0] struct list_head lh;", +" [16] struct module *module;", +" [24] int (*load_binary)(struct linux_binprm *);", +" [32] int (*load_shlib)(struct file *);", +" [40] int (*core_dump)(struct coredump_params *);", +" [48] unsigned long min_coredump;", +" }", +" SIZE: 56", +"", " Since a kmem_bufctl_t is typedef'd to be a kmem_bufctl_s structure, the", " output of the following two commands is identical:\n", " %s> whatis kmem_bufctl_s", @@ -3514,7 +4110,82 @@ " Display definition of a kdev_t typedef:\n", " %s> whatis kdev_t", " typedef short unsigned int kdev_t;", -" SIZE: 2 (0x2)", +" SIZE: 2 (0x2)\n", +" Display all structures which have a size of 192 bytes:\n", +" %s> whatis -r 192", +" SIZE TYPE", +" 192 _intel_private", +" 192 blkcg_gq", +" 192 clock_event_device", +" 192 cper_sec_proc_generic", +" 192 dentry", +" 192 dst_ops", +" 192 ehci_itd", +" 192 ethtool_rxnfc", +" 192 fb_ops", +" 192 file_lock", +" 192 inode_operations", +" 192 input_device_id", +" 192 ip_vs_stats", +" 192 numa_group", +" 192 parallel_data", +" 192 pcie_port_service_driver", +" 192 pebs_record_hsw", +" 192 pnp_driver", +" 192 regmap_config", +" 192 sched_entity", +" 192 tcp_timewait_sock", +" 192 timerfd_ctx", +" 192 tpm_vendor_specific", +" 192 urb", +"", +" Display all structures that contain members that point to ", +" an mm_struct:\n", +" %s> whatis -m mm_struct", +" SIZE TYPE", +" 16 tlb_state", +" 24 flush_tlb_info", +" 24 ftrace_raw_xen_mmu_pgd", +" 24 futex_key", +" 24 map_info", +" 32 ftrace_raw_xen_mmu_alloc_ptpage", +" 32 ftrace_raw_xen_mmu_pte_clear", +" 40 ftrace_raw_xen_mmu_flush_tlb_others", +" 40 ftrace_raw_xen_mmu_ptep_modify_prot", +" 40 ftrace_raw_xen_mmu_set_pte_at", +" 40 mm_slot", +" 64 mm_walk", +" 64 rmap_item", +" 104 userfaultfd_ctx", +" 128 mmu_gather", +" 216 vm_area_struct", +" 256 linux_binprm", +" 2616 rq", +" 2936 task_struct", +"", +" Display all structures sized from 256 to 512 bytes that", +" contain members that point to a task_struct:\n", +" %s> whatis -r 256-512 -m task_struct", +" SIZE TYPE", +" 256 file", +" 256 od_cpu_dbs_info_s", +" 264 srcu_notifier_head", +" 272 protection_domain", +" 288 clk_notifier", +" 288 fsnotify_group", +" 296 quota_info", +" 312 tty_port", +" 320 workqueue_struct", +" 344 trace_array", +" 344 uart_state", +" 352 cpufreq_policy", +" 352 elf_thread_core_info", +" 376 perf_event_context", +" 384 rcu_data", +" 400 cgroup", +" 408 subsys_private", +" 424 hvc_struct", +" 496 psmouse", NULL }; @@ -5052,7 +5723,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", @@ -5134,6 +5805,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", @@ -5450,12 +6124,12 @@ 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] [-l] [-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 ", " \"radix\" or \"rbtree\", although only the first two characters", -" are required.", +" are required. If not specified, rbtree is the default type.", " -r offset If the \"start\" argument is the address of a data structure that", " contains the radix_tree_root or rb_root structure, then this is", " the offset to that structure member. If the offset is non-zero,", @@ -5480,13 +6154,19 @@ " or \"struct.member[index]\"; embedded member specifications may", " extend beyond one level deep by expressing the struct argument as", " \"struct.member.member.member...\".", -" -x Override default output format with hexadecimal format.", -" -d Override default output format with decimal format.", +" -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 For red-black trees, dump the tree sorted in linear order starting", +" with the leftmost node and progressing to the right. This option", +" does not apply to radix trees.", " -p Display the node's position information, showing the relationship", " between it and the root. For red-black trees, a position that", " indicates \"root/l/r\" means that the node is the right child", " of the left child of the root node. For radix trees, the height", " and slot index values are shown with respect to the root.", +" -x Override default output format with hexadecimal format.", +" -d Override default output format with decimal format.", " ", " The meaning of the \"start\" argument, which can be expressed either in", " hexadecimal format or symbolically, depends upon whether the -N option", @@ -5576,6 +6256,59 @@ " ffff880123e3ad40", " position: root/r/r/r/r", "", +" Given an mm_struct address of 0xffff880074b5be80, list the VMA tree in linear", +" order from the leftmost node progressing to the right using the -l option:\n", +" %s> tree -ls vm_area_struct.vm_start -o vm_area_struct.vm_rb \\", +" -r mm_struct.mm_rb 0xffff880074b5be80 | paste - -", +" ffff88001f2c50e0 vm_start = 0x400000", +" ffff88001f2c5290 vm_start = 0xceb000", +" ffff880074bfc6c0 vm_start = 0xcec000", +" ffff88001f2c4bd0 vm_start = 0xd10000", +" ffff880074bfc948 vm_start = 0x1fe9000", +" ffff880036e54510 vm_start = 0x7ff6aa296000", +" ffff88001f2c5bd8 vm_start = 0x7ff6aa298000", +" ffff880036e54af8 vm_start = 0x7ff6aa497000", +" ffff880036e54f30 vm_start = 0x7ff6aa498000", +" ffff88000e06aa20 vm_start = 0x7ff6aa499000", +" ffff88000e06b368 vm_start = 0x7ff6ab95f000", +" ...", +" ffff88001f2c5e60 vm_start = 0x7ff6bc1af000", +" ffff88001f2c4ca8 vm_start = 0x7ff6bc1b6000", +" ffff88001f2c5008 vm_start = 0x7ff6bc200000", +" ffff88001f2c5d88 vm_start = 0x7ff6bc205000", +" ffff880074bfd6c8 vm_start = 0x7ff6bc206000", +" ffff88001f2c4288 vm_start = 0x7ff6bc207000", +" ffff88001f2c4510 vm_start = 0x7ffc7a5fc000", +" ffff88001f2c5b00 vm_start = 0x7ffc7a6d1000", +"", +" Compared to the top/down root/leaves order:\n", +" %s> tree -s vm_area_struct.vm_start -o vm_area_struct.vm_rb \\", +" -r mm_struct.mm_rb 0xffff880074b5be80 | paste - -", +" ffff88001f2c5a28 vm_start = 0x7ff6bbbb9000", +" ffff88001f2c55f0 vm_start = 0x7ff6bb252000", +" ffff88000e06a360 vm_start = 0x7ff6ac6c3000", +" ffff88001f2c4bd0 vm_start = 0xd10000", +" ffff88001f2c5290 vm_start = 0xceb000", +" ffff88001f2c50e0 vm_start = 0x400000", +" ffff880074bfc6c0 vm_start = 0xcec000", +" ffff88000e06b368 vm_start = 0x7ff6ab95f000", +" ffff88001f2c5bd8 vm_start = 0x7ff6aa298000", +" ffff880074bfc948 vm_start = 0x1fe9000", +" ffff880036e54510 vm_start = 0x7ff6aa296000", +" ffff880036e54f30 vm_start = 0x7ff6aa498000", +" ffff880036e54af8 vm_start = 0x7ff6aa497000", +" ffff88000e06aa20 vm_start = 0x7ff6aa499000", +" ffff88000e06ae58 vm_start = 0x7ff6ac1df000", +" ffff88000e06ba28 vm_start = 0x7ff6abefc000", +" ffff88000e06a6c0 vm_start = 0x7ff6ac41b000", +" ffff88001f2c4000 vm_start = 0x7ff6bac75000", +" ffff88000e06bd88 vm_start = 0x7ff6b2d00000", +" ffff88000e06b440 vm_start = 0x7ff6b28de000", +" ...", +" ffff880074bfd6c8 vm_start = 0x7ff6bc206000", +" ffff88001f2c4510 vm_start = 0x7ffc7a5fc000", +" ffff88001f2c5b00 vm_start = 0x7ffc7a6d1000", +"", " Display a list of the page structs in the radix tree of an address_space", " structure located at ffff88012d364de0:\n", " %s> tree -t radix -r address_space.page_tree ffff88012d364de0", @@ -5802,6 +6535,9 @@ " CACHED 1276196 4.9 GB 64% of TOTAL MEM", " SLAB 120410 470.4 MB 6% of TOTAL MEM", " ", +" TOTAL HUGE 524288 2 GB ----", +" HUGE FREE 524288 2 GB 100% of TOTAL HUGE", +" ", " TOTAL SWAP 2498559 9.5 GB ----", " SWAP USED 81978 320.2 MB 3% of TOTAL SWAP", " SWAP FREE 2416581 9.2 GB 96% of TOTAL SWAP", @@ -7316,7 +8052,7 @@ static char *version_info[] = { -"Copyright (C) 2002-2015 Red Hat, Inc.", +"Copyright (C) 2002-2017 Red Hat, Inc.", "Copyright (C) 2004, 2005, 2006, 2010 IBM Corporation", "Copyright (C) 1999-2006 Hewlett-Packard Co", "Copyright (C) 2005, 2006, 2011, 2012 Fujitsu Limited", diff -Nru crash-7.1.4/ipcs.c crash-7.2.3+real/ipcs.c --- crash-7.1.4/ipcs.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/ipcs.c 2018-05-17 17:39:38.000000000 +0000 @@ -71,6 +71,8 @@ ulong shm_f_op_huge_addr; int use_shm_f_op; int seq_multiplier; + int rt_cnt; + struct radix_tree_pair *rtp; }; /* @@ -92,6 +94,7 @@ static void get_msg_info(struct msg_info *, ulong, int); static void add_rss_swap(ulong, int, ulong *, ulong *); static int is_file_hugepages(ulong); +static void gather_radix_tree_entries(ulong); /* * global data @@ -574,7 +577,7 @@ static int ipc_search_idr(ulong ipc_ids_p, int specified, ulong specified_value, int (*fn)(ulong, int, ulong, int, int), int verbose) { - int in_use; + int i, in_use; ulong ipcs_idr_p; ulong ipc; int next_id, total; @@ -591,16 +594,32 @@ return 0; } - for (total = 0, next_id = 0; total < in_use; next_id++) { - ipc = idr_find(ipcs_idr_p, next_id); - if (ipc == 0) - continue; - - total++; - if (fn(ipc, specified, specified_value, next_id, verbose)) { - found = 1; - if (specified != SPECIFIED_NOTHING) - break; + if (VALID_MEMBER(idr_idr_rt)) { + gather_radix_tree_entries(ipcs_idr_p); + + for (i = 0; i < ipcs_table.rt_cnt; i++) { + ipc = (ulong)ipcs_table.rtp[i].value; + if (fn(ipc, specified, specified_value, UNUSED, verbose)) { + found = 1; + if (specified != SPECIFIED_NOTHING) + break; + } + } + + if (ipcs_table.rtp) + FREEBUF(ipcs_table.rtp); + } else { + for (total = 0, next_id = 0; total < in_use; next_id++) { + ipc = idr_find(ipcs_idr_p, next_id); + if (ipc == 0) + continue; + + total++; + if (fn(ipc, specified, specified_value, next_id, verbose)) { + found = 1; + if (specified != SPECIFIED_NOTHING) + break; + } } } @@ -885,7 +904,7 @@ add_rss_swap(inodep, is_file_hugepages(filep), &shm_info->rss, &shm_info->swap); - shm_info->deleted = UINT(buf + OFFSET(shmid_kernel_shm_perm) + + shm_info->deleted = UCHAR(buf + OFFSET(shmid_kernel_shm_perm) + OFFSET(kern_ipc_perm_deleted)); } @@ -930,7 +949,7 @@ sem_info->nsems = ULONG(buf + OFFSET(sem_array_sem_nsems)); - sem_info->deleted = UINT(buf + OFFSET(sem_array_sem_perm) + + sem_info->deleted = UCHAR(buf + OFFSET(sem_array_sem_perm) + OFFSET(kern_ipc_perm_deleted)); } @@ -976,7 +995,7 @@ msg_info->messages = ULONG(buf + OFFSET(msg_queue_q_qnum)); - msg_info->deleted = UINT(buf + OFFSET(msg_queue_q_perm) + + msg_info->deleted = UCHAR(buf + OFFSET(msg_queue_q_perm) + OFFSET(kern_ipc_perm_deleted)); } @@ -1083,3 +1102,19 @@ return 0; } +static void +gather_radix_tree_entries(ulong ipcs_idr_p) +{ + long len; + + ipcs_table.rt_cnt = do_radix_tree(ipcs_idr_p, RADIX_TREE_COUNT, NULL); + + if (ipcs_table.rt_cnt) { + len = sizeof(struct radix_tree_pair) * (ipcs_table.rt_cnt+1); + ipcs_table.rtp = (struct radix_tree_pair *)GETBUF(len); + ipcs_table.rtp[0].index = ipcs_table.rt_cnt; + ipcs_table.rt_cnt = do_radix_tree(ipcs_idr_p, RADIX_TREE_GATHER, ipcs_table.rtp); + } else + ipcs_table.rtp = NULL; +} + diff -Nru crash-7.1.4/kaslr_helper.c crash-7.2.3+real/kaslr_helper.c --- crash-7.1.4/kaslr_helper.c 1970-01-01 00:00:00.000000000 +0000 +++ crash-7.2.3+real/kaslr_helper.c 2018-05-17 17:39:38.000000000 +0000 @@ -0,0 +1,494 @@ +/* + * kaslr_helper - helper for kaslr offset calculation + * + * Copyright (c) 2011 FUJITSU LIMITED + * Copyright (c) 2018 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Authors: HATAYAMA Daisuke + * INDOH Takao + * Sergio Lopez + */ + +#include "defs.h" +#include +#include + +#ifdef X86_64 +/* + * Get address of vector0 interrupt handler (Devide Error) from Interrupt + * Descriptor Table. + */ +static ulong +get_vec0_addr(ulong idtr) +{ + struct gate_struct64 { + uint16_t offset_low; + uint16_t segment; + uint32_t ist : 3, zero0 : 5, type : 5, dpl : 2, p : 1; + uint16_t offset_middle; + uint32_t offset_high; + uint32_t zero1; + } __attribute__((packed)) gate; + + readmem(idtr, PHYSADDR, &gate, sizeof(gate), "idt_table", FAULT_ON_ERROR); + + return ((ulong)gate.offset_high << 32) + + ((ulong)gate.offset_middle << 16) + + gate.offset_low; +} + +/* + * Parse a string of [size[KMG] ]offset[KMG] + * Import from Linux kernel(lib/cmdline.c) + */ +static ulong +memparse(char *ptr, char **retptr) +{ + char *endptr; + + unsigned long long ret = strtoull(ptr, &endptr, 0); + + switch (*endptr) { + case 'E': + case 'e': + ret <<= 10; + case 'P': + case 'p': + ret <<= 10; + case 'T': + case 't': + ret <<= 10; + case 'G': + case 'g': + ret <<= 10; + case 'M': + case 'm': + ret <<= 10; + case 'K': + case 'k': + ret <<= 10; + endptr++; + default: + break; + } + + if (retptr) + *retptr = endptr; + + return ret; +} + +/* + * Find "elfcorehdr=" in the boot parameter of kernel and return the address + * of elfcorehdr. + */ +static ulong +get_elfcorehdr(ulong kaslr_offset) +{ + char cmdline[BUFSIZE], *ptr; + ulong cmdline_vaddr; + ulong cmdline_paddr; + ulong buf_vaddr, buf_paddr; + char *end; + ulong elfcorehdr_addr = 0, elfcorehdr_size = 0; + int verbose = CRASHDEBUG(1)? 1: 0; + + cmdline_vaddr = st->saved_command_line_vmlinux + kaslr_offset; + if (!kvtop(NULL, cmdline_vaddr, &cmdline_paddr, verbose)) + return 0; + + if (CRASHDEBUG(1)) { + fprintf(fp, "cmdline vaddr=%lx\n", cmdline_vaddr); + fprintf(fp, "cmdline paddr=%lx\n", cmdline_paddr); + } + + if (!readmem(cmdline_paddr, PHYSADDR, &buf_vaddr, sizeof(ulong), + "saved_command_line", RETURN_ON_ERROR)) + return 0; + + if (!kvtop(NULL, buf_vaddr, &buf_paddr, verbose)) + return 0; + + if (CRASHDEBUG(1)) { + fprintf(fp, "cmdline buffer vaddr=%lx\n", buf_vaddr); + fprintf(fp, "cmdline buffer paddr=%lx\n", buf_paddr); + } + + memset(cmdline, 0, BUFSIZE); + if (!readmem(buf_paddr, PHYSADDR, cmdline, BUFSIZE, + "saved_command_line", RETURN_ON_ERROR)) + return 0; + + ptr = strstr(cmdline, "elfcorehdr="); + if (!ptr) + return 0; + + if (CRASHDEBUG(1)) + fprintf(fp, "2nd kernel detected\n"); + + ptr += strlen("elfcorehdr="); + elfcorehdr_addr = memparse(ptr, &end); + if (*end == '@') { + elfcorehdr_size = elfcorehdr_addr; + elfcorehdr_addr = memparse(end + 1, &end); + } + + if (CRASHDEBUG(1)) { + fprintf(fp, "elfcorehdr_addr=%lx\n", elfcorehdr_addr); + fprintf(fp, "elfcorehdr_size=%lx\n", elfcorehdr_size); + } + + return elfcorehdr_addr; +} + + /* + * Get vmcoreinfo from elfcorehdr. + * Some codes are imported from Linux kernel(fs/proc/vmcore.c) + */ +static int +get_vmcoreinfo(ulong elfcorehdr, ulong *addr, int *len) +{ + unsigned char e_ident[EI_NIDENT]; + Elf64_Ehdr ehdr; + Elf64_Phdr phdr; + Elf64_Nhdr nhdr; + ulong ptr; + ulong nhdr_offset = 0; + int i; + + if (!readmem(elfcorehdr, PHYSADDR, e_ident, EI_NIDENT, + "EI_NIDENT", RETURN_ON_ERROR)) + return FALSE; + + if (e_ident[EI_CLASS] != ELFCLASS64) { + error(INFO, "Only ELFCLASS64 is supportd\n"); + return FALSE; + } + + if (!readmem(elfcorehdr, PHYSADDR, &ehdr, sizeof(ehdr), + "Elf64_Ehdr", RETURN_ON_ERROR)) + return FALSE; + + /* Sanity Check */ + if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0 || + (ehdr.e_type != ET_CORE) || + ehdr.e_ident[EI_CLASS] != ELFCLASS64 || + ehdr.e_ident[EI_VERSION] != EV_CURRENT || + ehdr.e_version != EV_CURRENT || + ehdr.e_ehsize != sizeof(Elf64_Ehdr) || + ehdr.e_phentsize != sizeof(Elf64_Phdr) || + ehdr.e_phnum == 0) { + error(INFO, "Invalid elf header\n"); + return FALSE; + } + + ptr = elfcorehdr + ehdr.e_phoff; + for (i = 0; i < ehdr.e_phnum; i++) { + ulong offset; + char name[16]; + + if (!readmem(ptr, PHYSADDR, &phdr, sizeof(phdr), + "Elf64_Phdr", RETURN_ON_ERROR)) + return FALSE; + + ptr += sizeof(phdr); + if (phdr.p_type != PT_NOTE) + continue; + + offset = phdr.p_offset; + if (!readmem(offset, PHYSADDR, &nhdr, sizeof(nhdr), + "Elf64_Nhdr", RETURN_ON_ERROR)) + return FALSE; + + offset += DIV_ROUND_UP(sizeof(Elf64_Nhdr), sizeof(Elf64_Word))* + sizeof(Elf64_Word); + memset(name, 0, sizeof(name)); + if (!readmem(offset, PHYSADDR, name, sizeof(name), + "Elf64_Nhdr name", RETURN_ON_ERROR)) + return FALSE; + + if(!strcmp(name, "VMCOREINFO")) { + nhdr_offset = offset; + break; + } + } + + if (!nhdr_offset) + return FALSE; + + *addr = nhdr_offset + + DIV_ROUND_UP(nhdr.n_namesz, sizeof(Elf64_Word))* + sizeof(Elf64_Word); + *len = nhdr.n_descsz; + + if (CRASHDEBUG(1)) { + fprintf(fp, "vmcoreinfo addr=%lx\n", *addr); + fprintf(fp, "vmcoreinfo len=%d\n", *len); + } + + return TRUE; +} + +static int +qemu_get_cr3_idtr(ulong *cr3, ulong *idtr) +{ + QEMUCPUState *cpustat; + + if (DISKDUMP_DUMPFILE()) { + cpustat = diskdump_get_qemucpustate(0); + } else if (KDUMP_DUMPFILE()) { + cpustat = kdump_get_qemucpustate(0); + } else { + return FALSE; + } + + if (!cpustat) { + return FALSE; + } + + *cr3 = cpustat->cr[3]; + *idtr = cpustat->idt.base; + + return TRUE; +} + +/* + * Check if current kaslr_offset/phys_base is for 1st kernel or 2nd kernel. + * If we are in 2nd kernel, get kaslr_offset/phys_base from vmcoreinfo. + * + * 1. Get command line and try to retrieve "elfcorehdr=" boot parameter + * 2. If "elfcorehdr=" is not found in command line, we are in 1st kernel. + * There is nothing to do. + * 3. If "elfcorehdr=" is found, we are in 2nd kernel. Find vmcoreinfo + * using "elfcorehdr=" and retrieve kaslr_offset/phys_base from vmcoreinfo. + */ +static int +get_kaslr_offset_from_vmcoreinfo(ulong orig_kaslr_offset, + ulong *kaslr_offset, ulong *phys_base) +{ + ulong elfcorehdr_addr = 0; + ulong vmcoreinfo_addr; + int vmcoreinfo_len; + char *buf, *pos; + int ret = FALSE; + + /* Find "elfcorehdr=" in the kernel boot parameter */ + elfcorehdr_addr = get_elfcorehdr(orig_kaslr_offset); + if (!elfcorehdr_addr) + return FALSE; + + /* Get vmcoreinfo from the address of "elfcorehdr=" */ + if (!get_vmcoreinfo(elfcorehdr_addr, &vmcoreinfo_addr, &vmcoreinfo_len)) + return FALSE; + + if (!vmcoreinfo_len) + return FALSE; + + if (CRASHDEBUG(1)) + fprintf(fp, "Find vmcoreinfo in kdump memory\n"); + + buf = GETBUF(vmcoreinfo_len); + if (!readmem(vmcoreinfo_addr, PHYSADDR, buf, vmcoreinfo_len, + "vmcoreinfo", RETURN_ON_ERROR)) + goto quit; + + /* Get phys_base form vmcoreinfo */ + pos = strstr(buf, "NUMBER(phys_base)="); + if (!pos) + goto quit; + *phys_base = strtoull(pos + strlen("NUMBER(phys_base)="), NULL, 0); + + /* Get kaslr_offset form vmcoreinfo */ + pos = strstr(buf, "KERNELOFFSET="); + if (!pos) + goto quit; + *kaslr_offset = strtoull(pos + strlen("KERNELOFFSET="), NULL, 16); + + ret = TRUE; + +quit: + FREEBUF(buf); + return ret; +} + +/* + * Calculate kaslr_offset and phys_base + * + * kaslr_offset: + * The difference between original address in System.map or vmlinux and + * actual address placed randomly by kaslr feature. To be more accurate, + * kaslr_offset = actual address - original address + * + * phys_base: + * Physical address where the kerenel is placed. In other words, it's a + * physical address of __START_KERNEL_map. This is also decided randomly by + * kaslr. + * + * kaslr offset and phys_base are calculated as follows: + * + * kaslr_offset: + * 1) Get IDTR and CR3 value from the dump header. + * 2) Get a virtual address of IDT from IDTR value + * --- (A) + * 3) Translate (A) to physical address using CR3, the upper 52 bits + * of which points a top of page table. + * --- (B) + * 4) Get an address of vector0 (Devide Error) interrupt handler from + * IDT, which are pointed by (B). + * --- (C) + * 5) Get an address of symbol "divide_error" form vmlinux + * --- (D) + * + * Now we have two addresses: + * (C)-> Actual address of "divide_error" + * (D)-> Original address of "divide_error" in the vmlinux + * + * kaslr_offset can be calculated by the difference between these two + * value. + * + * phys_base; + * 1) Get IDT virtual address from vmlinux + * --- (E) + * + * So phys_base can be calculated using relationship of directly mapped + * address. + * + * phys_base = + * Physical address(B) - + * (Virtual address(E) + kaslr_offset - __START_KERNEL_map) + * + * Note that the address (A) cannot be used instead of (E) because (A) is + * not direct map address, it's a fixed map address. + * + * This solution works in most every case, but does not work in the + * following case. + * + * 1) If the dump is captured on early stage of kernel boot, IDTR points + * early IDT table(early_idts) instead of normal IDT(idt_table). + * 2) If the dump is captured whle kdump is working, IDTR points + * IDT table of 2nd kernel, not 1st kernel. + * + * Current implementation does not support the case 1), need + * enhancement in the future. For the case 2), get kaslr_offset and + * phys_base as follows. + * + * 1) Get kaslr_offset and phys_base using the above solution. + * 2) Get kernel boot parameter from "saved_command_line" + * 3) If "elfcorehdr=" is not included in boot parameter, we are in the + * first kernel, nothing to do any more. + * 4) If "elfcorehdr=" is included in boot parameter, we are in the 2nd + * kernel. Retrieve vmcoreinfo from address of "elfcorehdr=" and + * get kaslr_offset and phys_base from vmcoreinfo. + */ +#define PTI_USER_PGTABLE_BIT PAGE_SHIFT +#define PTI_USER_PGTABLE_MASK (1 << PTI_USER_PGTABLE_BIT) +#define CR3_PCID_MASK 0xFFFull +int +calc_kaslr_offset(ulong *kaslr_offset, ulong *phys_base) +{ + uint64_t cr3 = 0, idtr = 0, pgd = 0, idtr_paddr; + ulong divide_error_vmcore; + ulong kaslr_offset_kdump, phys_base_kdump; + int ret = FALSE; + int verbose = CRASHDEBUG(1)? 1: 0; + + if (!machine_type("X86_64")) + return FALSE; + + if (SADUMP_DUMPFILE()) { + if (!sadump_get_cr3_idtr(&cr3, &idtr)) + return FALSE; + } else if (QEMU_MEM_DUMP_NO_VMCOREINFO()) { + if (!qemu_get_cr3_idtr(&cr3, &idtr)) + return FALSE; + } else if (VMSS_DUMPFILE()) { + if (!vmware_vmss_get_cr3_idtr(&cr3, &idtr)) + return FALSE; + } else + return FALSE; + + if (st->pti_init_vmlinux || st->kaiser_init_vmlinux) + pgd = cr3 & ~(CR3_PCID_MASK|PTI_USER_PGTABLE_MASK); + else + pgd = cr3 & ~CR3_PCID_MASK; + + /* + * Set up for kvtop. + * + * calc_kaslr_offset() is called before machdep_init(PRE_GDB), so some + * variables are not initialized yet. Set up them here to call kvtop(). + * + * TODO: XEN and 5-level is not supported + */ + vt->kernel_pgd[0] = pgd; + machdep->last_pgd_read = vt->kernel_pgd[0]; + machdep->machspec->physical_mask_shift = __PHYSICAL_MASK_SHIFT_2_6; + machdep->machspec->pgdir_shift = PGDIR_SHIFT; + machdep->machspec->ptrs_per_pgd = PTRS_PER_PGD; + if (!readmem(pgd, PHYSADDR, machdep->pgd, PAGESIZE(), + "pgd", RETURN_ON_ERROR)) + goto quit; + + /* Convert virtual address of IDT table to physical address */ + if (!kvtop(NULL, idtr, &idtr_paddr, verbose)) + goto quit; + + /* Now we can calculate kaslr_offset and phys_base */ + divide_error_vmcore = get_vec0_addr(idtr_paddr); + *kaslr_offset = divide_error_vmcore - st->divide_error_vmlinux; + *phys_base = idtr_paddr - + (st->idt_table_vmlinux + *kaslr_offset - __START_KERNEL_map); + + if (CRASHDEBUG(1)) { + fprintf(fp, "calc_kaslr_offset: idtr=%lx\n", idtr); + fprintf(fp, "calc_kaslr_offset: pgd=%lx\n", pgd); + fprintf(fp, "calc_kaslr_offset: idtr(phys)=%lx\n", idtr_paddr); + fprintf(fp, "calc_kaslr_offset: divide_error(vmlinux): %lx\n", + st->divide_error_vmlinux); + fprintf(fp, "calc_kaslr_offset: divide_error(vmcore): %lx\n", + divide_error_vmcore); + } + + /* + * Check if current kaslr_offset/phys_base is for 1st kernel or 2nd + * kernel. If we are in 2nd kernel, get kaslr_offset/phys_base + * from vmcoreinfo + */ + if (get_kaslr_offset_from_vmcoreinfo( + *kaslr_offset, &kaslr_offset_kdump, &phys_base_kdump)) { + *kaslr_offset = kaslr_offset_kdump; + *phys_base = phys_base_kdump; + } else if (CRASHDEBUG(1)) { + fprintf(fp, "kaslr_helper: failed to determine which kernel was running at crash,\n"); + fprintf(fp, "kaslr_helper: asssuming the kdump 1st kernel.\n"); + } + + if (CRASHDEBUG(1)) { + fprintf(fp, "calc_kaslr_offset: kaslr_offset=%lx\n", + *kaslr_offset); + fprintf(fp, "calc_kaslr_offset: phys_base=%lx\n", *phys_base); + } + + ret = TRUE; +quit: + vt->kernel_pgd[0] = 0; + machdep->last_pgd_read = 0; + return ret; +} +#else +int +calc_kaslr_offset(ulong *kaslr_offset, ulong *phys_page) +{ + return FALSE; +} +#endif /* X86_64 */ diff -Nru crash-7.1.4/kernel.c crash-7.2.3+real/kernel.c --- crash-7.1.4/kernel.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/kernel.c 2018-05-17 17:39:38.000000000 +0000 @@ -1,8 +1,8 @@ /* kernel.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-2018 David Anderson + * Copyright (C) 2002-2018 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 @@ -17,9 +17,12 @@ #include "defs.h" #include "xen_hyper_defs.h" +#include "xen_dom0.h" #include #include #include +#include +#include "xendump.h" static void do_module_cmd(ulong, char *, ulong, char *, char *); static void show_module_taint(void); @@ -35,21 +38,24 @@ static void display_bh_2(void); static void display_bh_3(void); static void display_bh_4(void); -static void dump_hrtimer_data(void); +static void dump_hrtimer_data(const ulong *cpus); static void dump_hrtimer_clock_base(const void *, const int); static void dump_hrtimer_base(const void *, const int); static void dump_active_timers(const void *, ulonglong); static int get_expires_len(const int, const ulong *, const int); static void print_timer(const void *); static ulonglong ktime_to_ns(const void *); -static void dump_timer_data(void); -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(const ulong *cpus); +static void dump_timer_data_tvec_bases_v1(const ulong *cpus); +static void dump_timer_data_tvec_bases_v2(const ulong *cpus); +static void dump_timer_data_tvec_bases_v3(const ulong *cpus); +static void dump_timer_data_timer_bases(const ulong *cpus); 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 *); @@ -61,6 +67,10 @@ static ulong __xen_m2p(ulonglong, ulong); static ulong __xen_pvops_m2p_l2(ulonglong, ulong); static ulong __xen_pvops_m2p_l3(ulonglong, ulong); +static ulong __xen_pvops_m2p_hyper(ulonglong, ulong); +static ulong __xen_pvops_m2p_domU(ulonglong, ulong); +static int read_xc_p2m(ulonglong, void *, long); +static void read_p2m(ulong, int, void *); static int search_mapping_page(ulong, ulong *, ulong *, ulong *); static void read_in_kernel_config_err(int, char *); static void BUG_bytes_init(void); @@ -80,6 +90,9 @@ static void dump_dmi_info(void); static void list_source_code(struct gnu_request *, int); static void source_tree_init(void); +static ulong dump_audit_skb_queue(ulong); +static ulong __dump_audit(char *); +static void dump_audit(void); /* @@ -111,10 +124,9 @@ kt->init_begin = symbol_value("__init_begin"); kt->init_end = symbol_value("__init_end"); } - if (symbol_exists("_end")) - kt->end = symbol_value("_end"); - else - kt->end = highest_bss_symbol(); + kt->end = highest_bss_symbol(); + if ((sp1 = kernel_symbol_search("_end")) && (sp1->value > kt->end)) + kt->end = sp1->value; /* * For the traditional (non-pv_ops) Xen architecture, default to writable @@ -175,7 +187,7 @@ &kt->pvops_xen.p2m_mid_missing); get_symbol_data("p2m_missing", sizeof(ulong), &kt->pvops_xen.p2m_missing); - } else { + } else if (!symbol_exists("xen_p2m_addr")) { kt->pvops_xen.p2m_top_entries = get_array_length("p2m_top", NULL, 0); kt->pvops_xen.p2m_top = symbol_value("p2m_top"); kt->pvops_xen.p2m_missing = symbol_value("p2m_missing"); @@ -241,7 +253,9 @@ kt->utsname.domainname : "(not printable)"); } - strncpy(buf, kt->utsname.release, MIN(strlen(kt->utsname.release), 65)); + strncpy(buf, kt->utsname.release, 65); + if (buf[64]) + buf[64] = NULLCHAR; if (ascii_string(kt->utsname.release)) { char separator; @@ -518,6 +532,7 @@ if (VALID_STRUCT(irq_data)) { MEMBER_OFFSET_INIT(irq_data_chip, "irq_data", "chip"); MEMBER_OFFSET_INIT(irq_data_affinity, "irq_data", "affinity"); + MEMBER_OFFSET_INIT(irq_desc_irq_data, "irq_desc", "irq_data"); } STRUCT_SIZE_INIT(irq_cpustat_t, "irq_cpustat_t"); @@ -562,7 +577,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 @@ -674,6 +693,22 @@ kt->flags |= KALLSYMS_V2; } + if (INVALID_MEMBER(module_num_symtab) && + MEMBER_EXISTS("module", "core_kallsyms")) { + ASSIGN_OFFSET(module_num_symtab) = + MEMBER_OFFSET("module", "core_kallsyms") + + MEMBER_OFFSET("mod_kallsyms", "num_symtab"); + ASSIGN_OFFSET(module_symtab) = + MEMBER_OFFSET("module", "core_kallsyms") + + MEMBER_OFFSET("mod_kallsyms", "symtab"); + ASSIGN_OFFSET(module_strtab) = + MEMBER_OFFSET("module", "core_kallsyms") + + MEMBER_OFFSET("mod_kallsyms", "strtab"); + + if (!(kt->flags & NO_KALLSYMS)) + kt->flags |= KALLSYMS_V2; + } + if (!(kt->flags & DWARF_UNWIND)) kt->flags |= NO_DWARF_UNWIND; @@ -777,6 +812,10 @@ return addr; } + sprintf(map_symbol, "__cpu_%s_mask", type); + if (kernel_symbol_exists(map_symbol)) + return symbol_value(map_symbol); + return 0; } @@ -793,6 +832,14 @@ if (kernel_symbol_exists(map_symbol)) return "mask"; + sprintf(map_symbol, "__cpu_%s_map", name); + if (kernel_symbol_exists(map_symbol)) + return "map"; + + sprintf(map_symbol, "__cpu_%s_mask", name); + if (kernel_symbol_exists(map_symbol)) + return "mask"; + return NULL; } @@ -1213,11 +1260,11 @@ { int i; char command[BUFSIZE]; - char buffer[BUFSIZE]; - char buffer2[BUFSIZE]; - char buffer3[BUFSIZE]; - char buffer4[BUFSIZE]; - char buffer5[BUFSIZE]; + char buffer[BUFSIZE/2]; + char buffer2[BUFSIZE/2]; + char buffer3[BUFSIZE/2]; + char buffer4[BUFSIZE/2]; + char buffer5[BUFSIZE*2]; char *p1; FILE *pipe; int found; @@ -1247,7 +1294,7 @@ found = FALSE; sprintf(buffer3, "(unknown)"); - while (fgets(buffer, BUFSIZE-1, pipe)) { + while (fgets(buffer, (BUFSIZE/2)-1, pipe)) { if (!strstr(buffer, "Linux version 2.") && !strstr(buffer, "Linux version 3.") && !strstr(buffer, "Linux version 4.") && @@ -1334,12 +1381,12 @@ else sprintf(buffer, "%s", ACTIVE() ? "live system" : pc->dumpfile); - sprintf(buffer2, " %s is %s -- %s is %s\n", + sprintf(buffer5, " %s is %s -- %s is %s\n", namelist, namelist_smp ? "SMP" : "not SMP", buffer, target_smp ? "SMP" : "not SMP"); error(INFO, "incompatible arguments: %s%s", - strlen(buffer2) > 48 ? "\n " : "", buffer2); + strlen(buffer5) > 48 ? "\n " : "", buffer5); program_usage(SHORT_FORM); } @@ -1351,7 +1398,7 @@ source_tree_init(void) { FILE *pipe; - char command[BUFSIZE]; + char command[BUFSIZE*2]; char buf[BUFSIZE]; if (!is_directory(kt->source_tree)) { @@ -1384,7 +1431,7 @@ int argc, line, last, done, assembly; char buf1[BUFSIZE]; char buf2[BUFSIZE]; - char buf3[BUFSIZE]; + char buf3[BUFSIZE*2]; char file[BUFSIZE]; char *argv[MAXARGS]; struct syment *sp; @@ -1529,11 +1576,16 @@ /* * From either a syment pointer, or a virtual address evaluated * from a symbol name plus an offset value, determine whether - * there are multiple symbols with the same name. - + * there are multiple symbols with the same name, or if it is + * determined to be an invalid expression of a text address. + * * If there are multiple text symbols with the same name, then * display a "duplicate text symbols found" message followed by * a list of each symbol's information, and return FALSE. + * + * If a symbol name plus and offset value evaluates to an address + * that goes beyond the end of the text function, print an "invalid + * expression" message, and return FALSE; * * If there is one text symbol and one or more data symbols with * the same name, reset the incoming address based upon the @@ -1551,15 +1603,49 @@ resolve_text_symbol(char *arg, struct syment *sp_in, struct gnu_request *req, int radix) { int text_symbols; - struct syment *sp, *sp_orig, *first_text_sp; + struct syment *sp, *sp_orig, *first_text_sp, *sp_arg, *sp_addr; ulong offset, radix_flag; + char buf[BUFSIZE]; + char *op; + + sp_arg = NULL; + if (!sp_in && !IS_A_NUMBER(arg)) { + strcpy(buf, arg); + strip_beginning_char(buf, '('); + strip_ending_char(buf, ')'); + clean_line(buf); + if ((op = strpbrk(buf, "><+-&|*/%^"))) { + *op = NULLCHAR; + clean_line(buf); + if ((sp = symbol_search(buf)) && is_symbol_text(sp)) { + sp_arg = sp; + text_symbols = 1; + + while ((sp = symbol_search_next(sp->name, sp))) { + if (is_symbol_text(sp)) + text_symbols++; + } + + if (text_symbols > 1) { + sp_orig = sp_arg; + goto duplicates; + } + } + } + } if (sp_in) { sp_orig = sp_in; offset = 0; } else if ((sp_orig = value_search(req->addr, &offset))) { - if (!strstr(arg, sp_orig->name)) + if (!strstr(arg, sp_orig->name)) { + if (sp_arg && (sp_orig != sp_arg)) { + error(INFO, "invalid expression: %s evaluates to: %s+%lx\n", + arg, sp_orig->name, offset); + return FALSE; + } return TRUE; + } } else { if (CRASHDEBUG(1)) error(INFO, "%s: no text symbol found\n", arg); @@ -1569,6 +1655,19 @@ if (symbol_name_count(sp_orig->name) <= 1) return TRUE; + if (sp_arg) { + sp_addr = value_search(req->addr, &offset); + if (sp_arg != sp_addr) { + if (STREQ(sp_arg->name, sp_addr->name)) { + sp_orig = sp_arg; + goto duplicates; + } + error(INFO, "invalid expression: %s evaluates to %s: %s+%lx\n", + arg, sp_addr->name, offset); + return FALSE; + } + } + text_symbols = 0; first_text_sp = NULL; sp = sp_orig; @@ -1603,6 +1702,7 @@ return TRUE; } +duplicates: /* * Multiple text symbols with the same name exist. * Display them all and return FALSE. @@ -1615,9 +1715,9 @@ do { if (is_symbol_text(sp)) { if (module_symbol(sp->value, NULL, NULL, NULL, 0)) - show_symbol(sp, offset, SHOW_LINENUM|SHOW_MODULE|radix_flag); + show_symbol(sp, 0, SHOW_LINENUM|SHOW_MODULE|radix_flag); else - show_symbol(sp, offset, SHOW_LINENUM|radix_flag); + show_symbol(sp, 0, SHOW_LINENUM|radix_flag); } } while ((sp = symbol_search_next(sp->name, sp))); @@ -1783,7 +1883,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); @@ -2298,10 +2398,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': @@ -2316,16 +2416,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. @@ -2333,14 +2434,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; @@ -2423,6 +2527,8 @@ hook.esp = (ulong)-3; else if (STREQ(optarg, "noframepointer")) hook.esp = (ulong)-4; + else if (STREQ(optarg, "orc")) + hook.esp = (ulong)-5; else if (STREQ(optarg, "clear")) { kt->flags &= ~(RA_SEEK|NO_RA_SEEK); hook.esp = 0; @@ -2451,7 +2557,7 @@ } else { bt->flags |= BT_CPUMASK; BZERO(arg_buf, BUFSIZE); - strncpy(arg_buf, optarg, strlen(optarg)); + strcpy(arg_buf, optarg); cpus = get_cpumask_buf(); } break; @@ -2478,6 +2584,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') { @@ -2766,8 +2878,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)) @@ -2781,6 +2892,11 @@ return; } + if (bt->stackbase == 0) { + fprintf(fp, "(no stack)\n"); + return; + } + fill_stackbuf(bt); if (CRASHDEBUG(4)) { @@ -2855,6 +2971,8 @@ get_xendump_regs(bt, &eip, &esp); else if (SADUMP_DUMPFILE()) get_sadump_regs(bt, &eip, &esp); + else if (VMSS_DUMPFILE()) + get_vmware_vmss_regs(bt, &eip, &esp); else if (REMOTE_PAUSED()) { if (!is_task_active(bt->task) || !get_remote_regs(bt, &eip, &esp)) machdep->get_stack_frame(bt, &eip, &esp); @@ -2867,6 +2985,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"); @@ -2910,14 +3032,19 @@ switch (bt->flags & (BT_HARDIRQ|BT_SOFTIRQ)) { case BT_HARDIRQ: - btloc.hp->eip = symbol_value("do_IRQ"); - if (symbol_exists("__do_IRQ")) - btloc.hp->esp = ULONG(bt->stackbuf + - OFFSET(thread_info_previous_esp)); - else - btloc.hp->esp = ULONG(bt->stackbuf + - SIZE(irq_ctx) - - (sizeof(char *)*2)); + if (kernel_symbol_exists("hardirq_stack") && + STRUCT_EXISTS("irq_stack")) { + btloc.hp->eip = symbol_value("handle_irq"); + btloc.hp->esp = ULONG(bt->stackbuf); + } else { + btloc.hp->eip = symbol_value("do_IRQ"); + if (symbol_exists("__do_IRQ")) + btloc.hp->esp = ULONG(bt->stackbuf + + OFFSET(thread_info_previous_esp)); + else + btloc.hp->esp = ULONG(bt->stackbuf + + SIZE(irq_ctx) - (sizeof(char *)*2)); + } fprintf(fp, "--- ---\n"); if (in_irq_ctx(BT_SOFTIRQ, bt->tc->processor, btloc.hp->esp)) { btloc.flags |= BT_SOFTIRQ; @@ -2928,8 +3055,14 @@ case BT_SOFTIRQ: btloc.hp->eip = symbol_value("do_softirq"); - btloc.hp->esp = ULONG(bt->stackbuf + - OFFSET(thread_info_previous_esp)); + if (kernel_symbol_exists("softirq_stack") && + STRUCT_EXISTS("irq_stack")) { + if (kernel_symbol_exists("do_softirq_own_stack")) + btloc.hp->eip = symbol_value("do_softirq_own_stack"); + btloc.hp->esp = ULONG(bt->stackbuf); + } else + btloc.hp->esp = ULONG(bt->stackbuf + + OFFSET(thread_info_previous_esp)); fprintf(fp, "--- ---\n"); break; } @@ -2990,32 +3123,47 @@ switch (bt->flags & (BT_HARDIRQ|BT_SOFTIRQ)) { case BT_HARDIRQ: - retvaddr = ULONG(bt->stackbuf + - SIZE(irq_ctx) - sizeof(char *)); - if ((sp = value_search(retvaddr, NULL)) && - STREQ(sp->name, "do_IRQ")) - bt->instptr = retvaddr; - else - bt->instptr = symbol_value("do_IRQ"); - if (symbol_exists("__do_IRQ")) - bt->stkptr = ULONG(bt->stackbuf + - OFFSET(thread_info_previous_esp)); - else - bt->stkptr = ULONG(bt->stackbuf + - SIZE(irq_ctx) - (sizeof(char *)*2)); + if (kernel_symbol_exists("hardirq_stack") && + STRUCT_EXISTS("irq_stack")) { + bt->instptr = symbol_value("handle_irq"); + bt->stkptr = ULONG(bt->stackbuf); + } else { + retvaddr = ULONG(bt->stackbuf + + SIZE(irq_ctx) - sizeof(char *)); + if ((sp = value_search(retvaddr, NULL)) && + STREQ(sp->name, "do_IRQ")) + bt->instptr = retvaddr; + else + bt->instptr = symbol_value("do_IRQ"); + if (symbol_exists("__do_IRQ")) + bt->stkptr = ULONG(bt->stackbuf + + OFFSET(thread_info_previous_esp)); + else + bt->stkptr = ULONG(bt->stackbuf + + SIZE(irq_ctx) - (sizeof(char *)*2)); + } type = BT_HARDIRQ; break; case BT_SOFTIRQ: - retvaddr = ULONG(bt->stackbuf + - SIZE(irq_ctx) - sizeof(char *)); - if ((sp = value_search(retvaddr, NULL)) && - STREQ(sp->name, "do_softirq")) - bt->instptr = retvaddr; - else - bt->instptr = symbol_value("do_softirq"); - bt->stkptr = ULONG(bt->stackbuf + - OFFSET(thread_info_previous_esp)); + if (kernel_symbol_exists("softirq_stack") && + STRUCT_EXISTS("irq_stack")) { + if (kernel_symbol_exists("do_softirq_own_stack")) + bt->instptr = symbol_value("do_softirq_own_stack"); + else + bt->instptr = symbol_value("do_softirq"); + bt->stkptr = ULONG(bt->stackbuf); + } else { + retvaddr = ULONG(bt->stackbuf + + SIZE(irq_ctx) - sizeof(char *)); + if ((sp = value_search(retvaddr, NULL)) && + STREQ(sp->name, "do_softirq")) + bt->instptr = retvaddr; + else + bt->instptr = symbol_value("do_softirq"); + bt->stkptr = ULONG(bt->stackbuf + + OFFSET(thread_info_previous_esp)); + } type = BT_SOFTIRQ; break; } @@ -3291,16 +3439,45 @@ MEMBER_OFFSET_INIT(module_gpl_syms, "module", "gpl_syms"); MEMBER_OFFSET_INIT(module_num_gpl_syms, "module", "num_gpl_syms"); - MEMBER_OFFSET_INIT(module_module_core, "module", - "module_core"); - MEMBER_OFFSET_INIT(module_core_size, "module", - "core_size"); - MEMBER_OFFSET_INIT(module_core_text_size, "module", - "core_text_size"); - MEMBER_OFFSET_INIT(module_module_init, "module", "module_init"); - MEMBER_OFFSET_INIT(module_init_size, "module", "init_size"); - MEMBER_OFFSET_INIT(module_init_text_size, "module", - "init_text_size"); + + if (MEMBER_EXISTS("module", "module_core")) { + MEMBER_OFFSET_INIT(module_core_size, "module", + "core_size"); + MEMBER_OFFSET_INIT(module_init_size, "module", + "init_size"); + + MEMBER_OFFSET_INIT(module_core_text_size, "module", + "core_text_size"); + MEMBER_OFFSET_INIT(module_init_text_size, "module", + "init_text_size"); + + MEMBER_OFFSET_INIT(module_module_core, "module", + "module_core"); + MEMBER_OFFSET_INIT(module_module_init, "module", + "module_init"); + } else { + ASSIGN_OFFSET(module_core_size) = + MEMBER_OFFSET("module", "core_layout") + + MEMBER_OFFSET("module_layout", "size"); + ASSIGN_OFFSET(module_init_size) = + MEMBER_OFFSET("module", "init_layout") + + MEMBER_OFFSET("module_layout", "size"); + + ASSIGN_OFFSET(module_core_text_size) = + MEMBER_OFFSET("module", "core_layout") + + MEMBER_OFFSET("module_layout", "text_size"); + ASSIGN_OFFSET(module_init_text_size) = + MEMBER_OFFSET("module", "init_layout") + + MEMBER_OFFSET("module_layout", "text_size"); + + ASSIGN_OFFSET(module_module_core) = + MEMBER_OFFSET("module", "core_layout") + + MEMBER_OFFSET("module_layout", "base"); + ASSIGN_OFFSET(module_module_init) = + MEMBER_OFFSET("module", "init_layout") + + MEMBER_OFFSET("module_layout", "base"); + } + MEMBER_OFFSET_INIT(module_percpu, "module", "percpu"); /* @@ -3392,6 +3569,7 @@ total += nsyms; total += 2; /* store the module's start/ending addresses */ + total += 2; /* and the init start/ending addresses */ /* * If the module has kallsyms, set up to grab them as well. @@ -3879,6 +4057,108 @@ } static void +show_module_taint_4_10(void) +{ + int i, j, bx; + struct load_module *lm; + int maxnamelen; + int found; + char buf1[BUFSIZE]; + char buf2[BUFSIZE]; + struct syment *sp; + ulong *taintsp, taints; + bool tnt_mod; + char tnt_true; + int tnts_len; + ulong tnts_addr; + char *modbuf; + + if (INVALID_MEMBER(module_taints)) { + MEMBER_OFFSET_INIT(module_taints, "module", "taints"); + STRUCT_SIZE_INIT(taint_flag, "taint_flag"); + MEMBER_OFFSET_INIT(tnt_true, "taint_flag", "true"); + if (INVALID_MEMBER(tnt_true)) + MEMBER_OFFSET_INIT(tnt_true, "taint_flag", "c_true"); + MEMBER_OFFSET_INIT(tnt_mod, "taint_flag", "module"); + } + + modbuf = GETBUF(SIZE(module)); + + for (i = found = maxnamelen = 0; i < kt->mods_installed; i++) { + lm = &st->load_modules[i]; + + readmem(lm->module_struct, KVADDR, modbuf, SIZE(module), + "module struct", FAULT_ON_ERROR); + + if (MEMBER_SIZE("module", "taints") == sizeof(ulong)) + taints = ULONG(modbuf + OFFSET(module_taints)); + else + taints = UINT(modbuf + OFFSET(module_taints)); + + if (taints) { + found++; + maxnamelen = strlen(lm->mod_name) > maxnamelen ? + strlen(lm->mod_name) : maxnamelen; + } + } + + if (!found) { + fprintf(fp, "no tainted modules\n"); + FREEBUF(modbuf); + return; + } + + tnts_len = get_array_length("taint_flags", NULL, 0); + sp = symbol_search("taint_flags"); + tnts_addr = sp->value; + + fprintf(fp, "%s %s\n", + mkstring(buf2, maxnamelen, LJUST, "NAME"), "TAINTS"); + + for (i = 0; i < st->mods_installed; i++) { + + lm = &st->load_modules[i]; + bx = 0; + buf1[0] = '\0'; + + readmem(lm->module_struct, KVADDR, modbuf, SIZE(module), + "module struct", FAULT_ON_ERROR); + + if (MEMBER_SIZE("module", "taints") == sizeof(ulong)) + taints = ULONG(modbuf + OFFSET(module_taints)); + else + taints = UINT(modbuf + OFFSET(module_taints)); + + if (!taints) + continue; + taintsp = &taints; + + for (j = 0; j < tnts_len; j++) { + readmem((tnts_addr + j * SIZE(taint_flag)) + + OFFSET(tnt_mod), + KVADDR, &tnt_mod, sizeof(bool), + "tnt mod", FAULT_ON_ERROR); + if (!tnt_mod) + continue; + if (NUM_IN_BITMAP(taintsp, j)) { + readmem((tnts_addr + j * SIZE(taint_flag)) + + OFFSET(tnt_true), + KVADDR, &tnt_true, sizeof(char), + "tnt true", FAULT_ON_ERROR); + buf1[bx++] = tnt_true; + } + } + + buf1[bx++] = '\0'; + + fprintf(fp, "%s %s\n", mkstring(buf2, maxnamelen, + LJUST, lm->mod_name), buf1); + } + + FREEBUF(modbuf); +} + +static void show_module_taint(void) { int i, j, bx; @@ -3896,6 +4176,12 @@ ulong tnts_addr; char *modbuf; + if (VALID_STRUCT(taint_flag) || + (kernel_symbol_exists("taint_flags") && STRUCT_EXISTS("taint_flag"))) { + show_module_taint_4_10(); + return; + } + if (INVALID_MEMBER(module_taints) && INVALID_MEMBER(module_license_gplok)) { MEMBER_OFFSET_INIT(module_taints, "module", "taints"); @@ -4464,7 +4750,7 @@ retbuf = module_objfile_search(modref, filename, tree); if (!retbuf) { - strncpy(tmpref, modref, BUFSIZE); + strncpy(tmpref, modref, BUFSIZE-1); for (c = 0; c < BUFSIZE && tmpref[c]; c++) if (tmpref[c] == '_') tmpref[c] = '-'; @@ -4541,7 +4827,7 @@ msg_flags = 0; - while ((c = getopt(argcnt, args, "tdm")) != EOF) { + while ((c = getopt(argcnt, args, "tdma")) != EOF) { switch(c) { case 't': @@ -4553,6 +4839,9 @@ case 'm': msg_flags |= SHOW_LOG_LEVEL; break; + case 'a': + msg_flags |= SHOW_LOG_AUDIT; + break; default: argerrs++; break; @@ -4562,6 +4851,11 @@ if (argerrs) cmd_usage(pc->curcmd, SYNOPSIS); + if (msg_flags & SHOW_LOG_AUDIT) { + dump_audit(); + return; + } + dump_log(msg_flags); } @@ -5537,8 +5831,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) @@ -5582,6 +5876,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); @@ -5821,12 +6117,14 @@ else fprintf(fp, "\n"); - fprintf(fp, " pvops_xen:\n"); - fprintf(fp, " p2m_top: %lx\n", kt->pvops_xen.p2m_top); - fprintf(fp, " p2m_top_entries: %d\n", kt->pvops_xen.p2m_top_entries); - if (symbol_exists("p2m_mid_missing")) - fprintf(fp, " p2m_mid_missing: %lx\n", kt->pvops_xen.p2m_mid_missing); - fprintf(fp, " p2m_missing: %lx\n", kt->pvops_xen.p2m_missing); + if (!symbol_exists("xen_p2m_addr")) { + fprintf(fp, " pvops_xen:\n"); + fprintf(fp, " p2m_top: %lx\n", kt->pvops_xen.p2m_top); + fprintf(fp, " p2m_top_entries: %d\n", kt->pvops_xen.p2m_top_entries); + if (symbol_exists("p2m_mid_missing")) + fprintf(fp, " p2m_mid_missing: %lx\n", kt->pvops_xen.p2m_mid_missing); + fprintf(fp, " p2m_missing: %lx\n", kt->pvops_xen.p2m_missing); + } } /* @@ -5865,7 +6163,7 @@ int nr_irqs; ulong *cpus; int show_intr, choose_cpu; - char buf[10]; + char buf[15]; char arg_buf[BUFSIZE]; cpus = NULL; @@ -5948,7 +6246,7 @@ } else { choose_cpu = 1; BZERO(arg_buf, BUFSIZE); - strncpy(arg_buf, optarg, strlen(optarg)); + strcpy(arg_buf, optarg); } break; @@ -5980,7 +6278,7 @@ } fprintf(fp, " "); - BZERO(buf, 10); + BZERO(buf, 15); for (i = 0; i < kt->cpus; i++) { if (hide_offline_cpu(i)) @@ -6692,8 +6990,8 @@ BZERO(buf, BUFSIZE); if (read_string(name, buf, BUFSIZE-1)) { if (strlen(name_buf) != 0) - strncat(name_buf, ",", 2); - strncat(name_buf, buf, strlen(buf)); + strcat(name_buf, ","); + strcat(name_buf, buf); } readmem(action+OFFSET(irqaction_next), KVADDR, @@ -6777,10 +7075,13 @@ readmem(irq_desc_addr + OFFSET(irq_desc_t_chip), KVADDR, &handler, sizeof(long), "irq_desc chip", FAULT_ON_ERROR); - else if (VALID_MEMBER(irq_data_chip)) - readmem(irq_desc_addr + OFFSET(irq_data_chip), KVADDR, - &handler, sizeof(long), "irq_data chip", - FAULT_ON_ERROR); + else if (VALID_MEMBER(irq_data_chip)) { + tmp = irq_desc_addr + OFFSET(irq_data_chip); + if (VALID_MEMBER(irq_desc_irq_data)) + tmp += OFFSET(irq_desc_irq_data); + readmem(tmp, KVADDR, &handler, sizeof(long), "irq_data chip", + FAULT_ON_ERROR); + } fprintf(fp, "%3d: ", irq); @@ -6829,8 +7130,8 @@ BZERO(buf2, BUFSIZE); if (read_string(name, buf2, BUFSIZE-1)) { if (strlen(name_buf) != 0) - strncat(name_buf, ",", 2); - strncat(name_buf, buf2, strlen(buf2)); + strcat(name_buf, ","); + strcat(name_buf, buf2); } readmem(action+OFFSET(irqaction_next), KVADDR, @@ -7061,16 +7362,24 @@ { int c; int rflag; + char *cpuspec; + ulong *cpus = NULL; rflag = 0; - while ((c = getopt(argcnt, args, "r")) != EOF) { + while ((c = getopt(argcnt, args, "rC:")) != EOF) { switch(c) { case 'r': rflag = 1; break; + case 'C': + cpuspec = optarg; + cpus = get_cpumask_buf(); + make_cpumask(cpuspec, cpus, FAULT_ON_ERROR, NULL); + break; + default: argerrs++; break; @@ -7081,15 +7390,18 @@ cmd_usage(pc->curcmd, SYNOPSIS); if (rflag) - dump_hrtimer_data(); + dump_hrtimer_data(cpus); else - dump_timer_data(); + dump_timer_data(cpus); + + if (cpus) + FREEBUF(cpus); } static void -dump_hrtimer_data(void) +dump_hrtimer_data(const ulong *cpus) { - int i, j; + int i, j, k = 0; int hrtimer_max_clock_bases, max_hrtimer_bases; struct syment * hrtimer_bases; @@ -7113,7 +7425,10 @@ hrtimer_bases = per_cpu_symbol_search("hrtimer_bases"); for (i = 0; i < kt->cpus; i++) { - if (i) + if (cpus && !NUM_IN_BITMAP(cpus, i)) + continue; + + if (k++) fprintf(fp, "\n"); if (hide_offline_cpu(i)) { @@ -7173,7 +7488,7 @@ offset = 0; if (VALID_MEMBER(hrtimer_clock_base_offset)) offset = ktime_to_ns(base + OFFSET(hrtimer_clock_base_offset)); - now = current_time * 1000000000LL / machdep->hz + offset; + now = current_time * (1000000000LL / machdep->hz) + offset; dump_active_timers(base, now); } @@ -7195,7 +7510,7 @@ /* get current time(uptime) */ get_uptime(NULL, ¤t_time); - now = current_time * 1000000000LL / machdep->hz; + now = current_time * (1000000000LL / machdep->hz); dump_active_timers(base, now); } @@ -7460,7 +7775,7 @@ #define TVN (6) static void -dump_timer_data(void) +dump_timer_data(const ulong *cpus) { int i; ulong timer_active; @@ -7480,14 +7795,17 @@ int flen, tdx, old_timers_exist; struct tv_range tv[TVN]; - if (kt->flags2 & TVEC_BASES_V3) { - dump_timer_data_tvec_bases_v3(); + if (kt->flags2 & TIMER_BASES) { + dump_timer_data_timer_bases(cpus); + return; + } else if (kt->flags2 & TVEC_BASES_V3) { + dump_timer_data_tvec_bases_v3(cpus); return; } else if (kt->flags & TVEC_BASES_V2) { - dump_timer_data_tvec_bases_v2(); + dump_timer_data_tvec_bases_v2(cpus); return; } else if (kt->flags & TVEC_BASES_V1) { - dump_timer_data_tvec_bases_v1(); + dump_timer_data_tvec_bases_v1(cpus); return; } @@ -7629,7 +7947,7 @@ */ static void -dump_timer_data_tvec_bases_v1(void) +dump_timer_data_tvec_bases_v1(const ulong *cpus) { int i, cpu, tdx, flen; struct timer_data *td; @@ -7652,6 +7970,11 @@ cpu = 0; next_cpu: + if (cpus && !NUM_IN_BITMAP(cpus, cpu)) { + if (++cpu < kt->cpus) + goto next_cpu; + return; + } count = 0; td = (struct timer_data *)NULL; @@ -7744,7 +8067,7 @@ */ static void -dump_timer_data_tvec_bases_v2(void) +dump_timer_data_tvec_bases_v2(const ulong *cpus) { int i, cpu, tdx, flen; struct timer_data *td; @@ -7778,6 +8101,11 @@ cpu = 0; next_cpu: + if (cpus && !NUM_IN_BITMAP(cpus, cpu)) { + if (++cpu < kt->cpus) + goto next_cpu; + return; + } /* * hide data of offline cpu and goto next cpu */ @@ -7786,6 +8114,7 @@ fprintf(fp, "TVEC_BASES[%d]: [OFFLINE]\n", cpu); if (++cpu < kt->cpus) goto next_cpu; + return; } @@ -7889,7 +8218,7 @@ * Linux 4.2 timers use new tvec_root, tvec and timer_list structures */ static void -dump_timer_data_tvec_bases_v3(void) +dump_timer_data_tvec_bases_v3(const ulong *cpus) { int i, cpu, tdx, flen; struct timer_data *td; @@ -7920,6 +8249,11 @@ cpu = 0; next_cpu: + if (cpus && !NUM_IN_BITMAP(cpus, cpu)) { + if (++cpu < kt->cpus) + goto next_cpu; + return; + } /* * hide data of offline cpu and goto next cpu */ @@ -7927,6 +8261,7 @@ fprintf(fp, "TVEC_BASES[%d]: [OFFLINE]\n", cpu); if (++cpu < kt->cpus) goto next_cpu; + return; } count = 0; @@ -8139,7 +8474,7 @@ int timer_cnt; struct list_data list_data, *ld; long sz; - ulong offset; + ulong offset = 0; tdx = 0; td = option ? (struct timer_data *)option : NULL; @@ -8168,7 +8503,7 @@ goto new_timer_list_format; } - if (VALID_MEMBER(timer_list_next) >= 0) + if (VALID_MEMBER(timer_list_next)) offset = OFFSET(timer_list_next); else error(FATAL, "no timer_list next, list, or entry members?\n"); @@ -8367,6 +8702,222 @@ 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(const ulong *cpus) +{ + int i, cpu, flen, base, nr_bases, found, display, j = 0; + 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: + if (cpus && !NUM_IN_BITMAP(cpus, cpu)) { + if (++cpu < kt->cpus) + goto next_cpu; + goto done; + } + /* + * 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 (j++) + 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(): * @@ -8381,7 +8932,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)) @@ -8844,6 +9395,12 @@ ulong c, i, kmfn, mapping, p, pfn; ulong start, end; ulong *mp = (ulong *)kt->m2p_page; + int memtype; + + if (XEN_CORE_DUMPFILE() && symbol_exists("xen_p2m_addr")) + memtype = PHYSADDR; + else + memtype = KVADDR; /* * Check the FIFO cache first. @@ -8854,13 +9411,13 @@ (mfn <= kt->p2m_mapping_cache[c].end))) { if (kt->p2m_mapping_cache[c].mapping != kt->last_mapping_read) { - if (!readmem(kt->p2m_mapping_cache[c].mapping, KVADDR, - mp, PAGESIZE(), "phys_to_machine_mapping page (cached)", - RETURN_ON_ERROR)) - error(FATAL, "cannot access " - "phys_to_machine_mapping page\n"); - else - kt->last_mapping_read = kt->p2m_mapping_cache[c].mapping; + if (memtype == PHYSADDR) + pc->curcmd_flags |= XEN_MACHINE_ADDR; + + read_p2m(c, memtype, mp); + + if (memtype == PHYSADDR) + pc->curcmd_flags &= ~XEN_MACHINE_ADDR; } else kt->p2m_page_cache_hits++; @@ -8890,12 +9447,17 @@ if (PVOPS_XEN()) { /* * The machine address was not cached, so search from the - * beginning of the p2m_top array, caching the contiguous + * beginning of the p2m tree/array, caching the contiguous * range containing the found machine address. */ if (symbol_exists("p2m_mid_missing")) pfn = __xen_pvops_m2p_l3(machine, mfn); - else + else if (symbol_exists("xen_p2m_addr")) { + if (XEN_CORE_DUMPFILE()) + pfn = __xen_pvops_m2p_hyper(machine, mfn); + else + pfn = __xen_pvops_m2p_domU(machine, mfn); + } else pfn = __xen_pvops_m2p_l2(machine, mfn); if (pfn != XEN_MFN_NOT_FOUND) @@ -9059,6 +9621,175 @@ return XEN_MFN_NOT_FOUND; } +static ulong +__xen_pvops_m2p_hyper(ulonglong machine, ulong mfn) +{ + ulong c, end, i, mapping, p, pfn, start; + + for (p = 0; + p < xkd->p2m_frames; + ++p) { + + mapping = PTOB(xkd->p2m_mfn_frame_list[p]); + + if (mapping != kt->last_mapping_read) { + pc->curcmd_flags |= XEN_MACHINE_ADDR; + if (!readmem(mapping, PHYSADDR, (void *)kt->m2p_page, + PAGESIZE(), "p2m_mfn_frame_list page", RETURN_ON_ERROR)) + error(FATAL, "cannot access p2m_mfn_frame_list[] page\n"); + + pc->curcmd_flags &= ~XEN_MACHINE_ADDR; + kt->last_mapping_read = mapping; + } + + kt->p2m_pages_searched++; + + if (search_mapping_page(mfn, &i, &start, &end)) { + pfn = p * XEN_PFNS_PER_PAGE + i; + if (CRASHDEBUG(1)) + console("pages: %d mfn: %lx (%llx) p: %ld" + " i: %ld pfn: %lx (%llx)\n", p + 1, mfn, machine, + p, i, pfn, XEN_PFN_TO_PSEUDO(pfn)); + + c = kt->p2m_cache_index; + kt->p2m_mapping_cache[c].start = start; + kt->p2m_mapping_cache[c].end = end; + kt->p2m_mapping_cache[c].mapping = mapping; + kt->p2m_mapping_cache[c].pfn = p * XEN_PFNS_PER_PAGE; + kt->p2m_cache_index = (c+1) % P2M_MAPPING_CACHE; + + return pfn; + } + } + + return XEN_MFN_NOT_FOUND; +} + +static void read_p2m(ulong cache_index, int memtype, void *buffer) +{ + /* + * Use special read function for PV domain p2m reading. + * See the comments of read_xc_p2m(). + */ + if (symbol_exists("xen_p2m_addr") && !XEN_CORE_DUMPFILE()) { + if (!read_xc_p2m(kt->p2m_mapping_cache[cache_index].mapping, + buffer, PAGESIZE())) + error(FATAL, "cannot access phys_to_machine_mapping page\n"); + } else if (!readmem(kt->p2m_mapping_cache[cache_index].mapping, memtype, + buffer, PAGESIZE(), "phys_to_machine_mapping page (cached)", + RETURN_ON_ERROR)) + error(FATAL, "cannot access phys_to_machine_mapping page\n"); + + kt->last_mapping_read = kt->p2m_mapping_cache[cache_index].mapping; +} + +/* + * PV domain p2m mapping info is stored in xd->xfd at xch_index_offset. It + * is organized as struct xen_dumpcore_p2m and the pfns are progressively + * increased by 1 from 0. + * + * This is a special p2m reading function for xen PV domain vmcores after + * kernel commit 054954eb051f35e74b75a566a96fe756015352c8 (xen: switch + * to linear virtual mapped sparse p2m list). It is invoked for reading + * p2m associate stuff by read_p2m(). + */ +static int read_xc_p2m(ulonglong addr, void *buffer, long size) +{ + ulong i, new_p2m_buf_size; + off_t offset; + struct xen_dumpcore_p2m *new_p2m_buf; + static struct xen_dumpcore_p2m *p2m_buf; + static ulong p2m_buf_size = 0; + + if (size <= 0) { + if ((CRASHDEBUG(1) && !STREQ(pc->curcmd, "search")) || + CRASHDEBUG(2)) + error(INFO, "invalid size request: %ld\n", size); + return FALSE; + } + + /* + * We extract xen_dumpcore_p2m.gmfn and copy them into the + * buffer. So, we need temporary p2m_buf whose size is + * (size * (sizeof(struct xen_dumpcore_p2m) / sizeof(ulong))) + * to put xen_dumpcore_p2m structures read from xd->xfd. + */ + new_p2m_buf_size = size * (sizeof(struct xen_dumpcore_p2m) / sizeof(ulong)); + + if (p2m_buf_size != new_p2m_buf_size) { + p2m_buf_size = new_p2m_buf_size; + + new_p2m_buf = realloc(p2m_buf, p2m_buf_size); + if (new_p2m_buf == NULL) { + free(p2m_buf); + error(FATAL, "cannot realloc p2m buffer\n"); + } + p2m_buf = new_p2m_buf; + } + + offset = addr * (sizeof(struct xen_dumpcore_p2m) / sizeof(ulong)); + offset += xd->xc_core.header.xch_index_offset; + + if (lseek(xd->xfd, offset, SEEK_SET) == -1) + error(FATAL, + "cannot lseek to xch_index_offset offset 0x%lx\n", offset); + if (read(xd->xfd, (void*)p2m_buf, p2m_buf_size) != p2m_buf_size) + error(FATAL, + "cannot read from xch_index_offset offset 0x%lx\n", offset); + + for (i = 0; i < size / sizeof(ulong); i++) + *((ulong *)buffer + i) = p2m_buf[i].gmfn; + + return TRUE; +} + +static ulong +__xen_pvops_m2p_domU(ulonglong machine, ulong mfn) +{ + ulong c, end, i, mapping, p, pfn, start; + + /* + * xch_nr_pages is the number of pages of p2m mapping. It is composed + * of struct xen_dumpcore_p2m. The stuff we want to copy into the mapping + * page is mfn whose type is unsigned long. + * So actual number of p2m pages should be: + * + * xch_nr_pages / (sizeof(struct xen_dumpcore_p2m) / sizeof(ulong)) + */ + for (p = 0; + p < xd->xc_core.header.xch_nr_pages / + (sizeof(struct xen_dumpcore_p2m) / sizeof(ulong)); + ++p) { + + mapping = p * PAGESIZE(); + + if (mapping != kt->last_mapping_read) { + if (!read_xc_p2m(mapping, (void *)kt->m2p_page, PAGESIZE())) + error(FATAL, "cannot read the last mapping page\n"); + kt->last_mapping_read = mapping; + } + kt->p2m_pages_searched++; + + if (search_mapping_page(mfn, &i, &start, &end)) { + pfn = p * XEN_PFNS_PER_PAGE + i; + c = kt->p2m_cache_index; + if (CRASHDEBUG (1)) + console("mfn: %lx (%llx) i: %ld pfn: %lx (%llx)\n", + mfn, machine, i, pfn, XEN_PFN_TO_PSEUDO(pfn)); + + kt->p2m_mapping_cache[c].start = start; + kt->p2m_mapping_cache[c].end = end; + kt->p2m_mapping_cache[c].mapping = mapping; + kt->p2m_mapping_cache[c].pfn = p * XEN_PFNS_PER_PAGE; + kt->p2m_cache_index = (c+1) % P2M_MAPPING_CACHE; + + return pfn; + } + } + + return XEN_MFN_NOT_FOUND; +} + /* * Search for an mfn in the current mapping page, and if found, * determine the range of contiguous mfns that it's contained @@ -10051,6 +10782,71 @@ } static void +show_kernel_taints_v4_10(char *buf, int verbose) +{ + int i, bx; + char tnt_true, tnt_false; + int tnts_len; + ulong tnts_addr; + ulong tainted_mask, *tainted_mask_ptr; + struct syment *sp; + + if (!VALID_STRUCT(taint_flag)) { + STRUCT_SIZE_INIT(taint_flag, "taint_flag"); + MEMBER_OFFSET_INIT(tnt_true, "taint_flag", "true"); + MEMBER_OFFSET_INIT(tnt_false, "taint_flag", "false"); + if (INVALID_MEMBER(tnt_true)) { + MEMBER_OFFSET_INIT(tnt_true, "taint_flag", "c_true"); + MEMBER_OFFSET_INIT(tnt_false, "taint_flag", "c_false"); + } + } + + bx = 0; + buf[0] = '\0'; + + /* + * Make sure that all dependencies are valid to prevent + * a fatal error from killing the session during the + * pre-RUNTIME system banner display. + */ + if (!(pc->flags & RUNTIME)) { + if (INVALID_MEMBER(tnt_true) || INVALID_MEMBER(tnt_false) || + !kernel_symbol_exists("tainted_mask")) + return; + } + + tnts_len = get_array_length("taint_flags", NULL, 0); + sp = symbol_search("taint_flags"); + tnts_addr = sp->value; + + get_symbol_data("tainted_mask", sizeof(ulong), &tainted_mask); + tainted_mask_ptr = &tainted_mask; + + for (i = 0; i < tnts_len; i++) { + if (NUM_IN_BITMAP(tainted_mask_ptr, i)) { + readmem((tnts_addr + i * SIZE(taint_flag)) + + OFFSET(tnt_true), + KVADDR, &tnt_true, sizeof(char), + "tnt true", FAULT_ON_ERROR); + buf[bx++] = tnt_true; + } else { + readmem((tnts_addr + i * SIZE(taint_flag)) + + OFFSET(tnt_false), + KVADDR, &tnt_false, sizeof(char), + "tnt false", FAULT_ON_ERROR); + if (tnt_false != ' ' && tnt_false != '-' && + tnt_false != 'G') + buf[bx++] = tnt_false; + } + } + + buf[bx++] = '\0'; + + if (verbose) + fprintf(fp, "TAINTED_MASK: %lx %s\n", tainted_mask, buf); +} + +static void show_kernel_taints(char *buf, int verbose) { int i, bx; @@ -10062,6 +10858,12 @@ int tainted; struct syment *sp; + if (VALID_STRUCT(taint_flag) || + (kernel_symbol_exists("taint_flags") && STRUCT_EXISTS("taint_flag"))) { + show_kernel_taints_v4_10(buf, verbose); + return; + } + if (!VALID_STRUCT(tnt)) { STRUCT_SIZE_INIT(tnt, "tnt"); MEMBER_OFFSET_INIT(tnt_bit, "tnt", "bit"); @@ -10181,3 +10983,133 @@ close_tmpfile(); } + +#define NLMSG_ALIGNTO 4 +#define NLMSG_DATA(nlh) (nlh + roundup(SIZE(nlmsghdr), NLMSG_ALIGNTO)) + +static ulong +dump_audit_skb_queue(ulong audit_skb_queue) +{ + ulong skb_buff_head_next = 0, p; + uint32_t qlen = 0; + + if (INVALID_SIZE(nlmsghdr)) { + STRUCT_SIZE_INIT(nlmsghdr, "nlmsghdr"); + MEMBER_OFFSET_INIT(nlmsghdr_nlmsg_type, "nlmsghdr", "nlmsg_type"); + MEMBER_SIZE_INIT(nlmsghdr_nlmsg_type, "nlmsghdr", "nlmsg_type"); + MEMBER_OFFSET_INIT(sk_buff_head_next, "sk_buff_head", "next"); + MEMBER_OFFSET_INIT(sk_buff_head_qlen, "sk_buff_head", "qlen"); + MEMBER_SIZE_INIT(sk_buff_head_qlen, "sk_buff_head", "qlen"); + MEMBER_OFFSET_INIT(sk_buff_data, "sk_buff", "data"); + MEMBER_OFFSET_INIT(sk_buff_len, "sk_buff", "len"); + MEMBER_OFFSET_INIT(sk_buff_next, "sk_buff", "next"); + MEMBER_SIZE_INIT(sk_buff_len, "sk_buff", "len"); + } + + readmem(audit_skb_queue + OFFSET(sk_buff_head_qlen), + KVADDR, + &qlen, + SIZE(sk_buff_head_qlen), + "audit_skb_queue.qlen", + FAULT_ON_ERROR); + + if (!qlen) + return 0; + + readmem(audit_skb_queue + OFFSET(sk_buff_head_next), + KVADDR, + &skb_buff_head_next, + sizeof(void *), + "audit_skb_queue.next", + FAULT_ON_ERROR); + + if (!skb_buff_head_next) + error(FATAL, "audit_skb_queue.next: NULL\n"); + + p = skb_buff_head_next; + do { + ulong data, len, data_len; + uint16_t nlmsg_type; + char *buf = NULL; + + if (CRASHDEBUG(2)) + fprintf(fp, "%#016lx\n", p); + + readmem(p + OFFSET(sk_buff_len), + KVADDR, + &len, + SIZE(sk_buff_len), + "sk_buff.data", + FAULT_ON_ERROR); + + data_len = len - roundup(SIZE(nlmsghdr), NLMSG_ALIGNTO); + + readmem(p + OFFSET(sk_buff_data), + KVADDR, + &data, + sizeof(void *), + "sk_buff.data", + FAULT_ON_ERROR); + + if (!data) + error(FATAL, "sk_buff.data: NULL\n"); + + readmem(data + OFFSET(nlmsghdr_nlmsg_type), + KVADDR, + &nlmsg_type, + SIZE(nlmsghdr_nlmsg_type), + "nlmsghdr.nlmsg_type", + FAULT_ON_ERROR); + + buf = GETBUF(data_len + 1); + readmem(NLMSG_DATA(data), + KVADDR, + buf, + data_len, + "sk_buff.data + sizeof(struct nlmsghdr)", + FAULT_ON_ERROR); + buf[data_len] = '\0'; + + fprintf(fp, "type=%u %s\n", nlmsg_type, buf); + FREEBUF(buf); + + readmem(p + OFFSET(sk_buff_next), + KVADDR, + &p, + sizeof(void *), + "skb_buff.next", + FAULT_ON_ERROR); + } while (p != audit_skb_queue); + + return qlen; +} + +static ulong +__dump_audit(char *symname) +{ + if (symbol_exists(symname)) { + if (CRASHDEBUG(1)) + fprintf(fp, "# %s:\n", symname); + return dump_audit_skb_queue(symbol_value(symname)); + } + return 0; +} + +static void +dump_audit(void) +{ + ulong qlen = 0; + + if (symbol_exists("audit_skb_queue")) { + qlen += __dump_audit("audit_skb_hold_queue"); + qlen += __dump_audit("audit_skb_queue"); + } else if (symbol_exists("audit_queue")) { + qlen += __dump_audit("audit_hold_queue"); + qlen += __dump_audit("audit_retry_queue"); + qlen += __dump_audit("audit_queue"); + } else + option_not_supported('a'); + + if (!qlen) + error(INFO, "kernel audit log is empty\n"); +} diff -Nru crash-7.1.4/lkcd_common.c crash-7.2.3+real/lkcd_common.c --- crash-7.1.4/lkcd_common.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/lkcd_common.c 2018-05-17 17:39:38.000000000 +0000 @@ -1240,9 +1240,10 @@ switch (uncompress(dest, &retlen, source, sourcelen)) { case Z_OK: - if (retlen == destlen) + if (retlen == destlen) { rc = TRUE; break; + } lkcd_print("uncompress: returned length not page size: %ld\n", retlen); diff -Nru crash-7.1.4/lkcd_vmdump_v2_v3.h crash-7.2.3+real/lkcd_vmdump_v2_v3.h --- crash-7.1.4/lkcd_vmdump_v2_v3.h 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/lkcd_vmdump_v2_v3.h 2018-05-17 17:39:38.000000000 +0000 @@ -36,7 +36,7 @@ #endif #if defined(ARM) || defined(X86) || defined(PPC) || defined(S390) || \ - defined(S390X) || defined(ARM64) || defined(MIPS) + defined(S390X) || defined(ARM64) || defined(MIPS) || defined(SPARC64) /* * Kernel header file for Linux crash dumps. diff -Nru crash-7.1.4/lkcd_x86_trace.c crash-7.2.3+real/lkcd_x86_trace.c --- crash-7.1.4/lkcd_x86_trace.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/lkcd_x86_trace.c 2018-05-17 17:39:38.000000000 +0000 @@ -5,8 +5,8 @@ /* * lkcd_x86_trace.c * - * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 David Anderson - * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Red Hat, Inc. All rights reserved. + * Copyright (C) 2002-2012, 2017-2018 David Anderson + * Copyright (C) 2002-2012, 2017-2018 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 @@ -44,6 +44,7 @@ static k_error_t kl_get_task_struct(kaddr_t, int, void *); static kaddr_t kl_kernelstack(kaddr_t); static kaddr_t get_call_pc(kaddr_t); +static kaddr_t get_call_pc_v2(kaddr_t); static int get_jmp_instr(kaddr_t, kaddr_t, kaddr_t *, char *, char **); static int is_push(unsigned int); static int is_pop(unsigned int); @@ -278,9 +279,75 @@ addr = irp->prev->addr; } free_instr_stream(irp); + + /* + * If the old LKCD code fails, try disassembling... + */ + if (!addr) + return get_call_pc_v2(ra); + return(addr); } +kaddr_t +get_call_pc_v2(kaddr_t ra) +{ + int c ATTRIBUTE_UNUSED; + int line, len; + kaddr_t addr, addr2; + ulong offset; + struct syment *sp; + char *arglist[MAXARGS]; + char buf[BUFSIZE]; + + if ((sp = value_search(ra, &offset))) { + if (offset == 0) + return 0; + } else + return 0; + + addr = 0; + + for (len = 2; len < 8; len++) { + open_tmpfile2(); + sprintf(buf, "x/2i 0x%x", ra - len); + if (!gdb_pass_through(buf, pc->tmpfile2, GNU_RETURN_ON_ERROR)) { + close_tmpfile2(); + return 0; + } + + rewind(pc->tmpfile2); + line = 1; + while (fgets(buf, BUFSIZE, pc->tmpfile2)) { + c = parse_line(buf, arglist); + if ((line == 1) && !STREQ(arglist[2], "call")) + break; + if (line == 2) { + addr2 = (kaddr_t)htol(arglist[0], RETURN_ON_ERROR|QUIET, 0); + if (addr2 == ra) { + addr = ra - len; + break; + } + } + line++; + } + + close_tmpfile2(); + + if (addr) { + if (CRASHDEBUG(1)) { + fprintf(fp, "get_call_pc_v2(ra: %x) -> %x -> ", ra, addr); + if (value_to_symstr(addr, buf, 0)) + fprintf(fp, "%s", buf); + fprintf(fp, "\n"); + } + break; + } + } + + return addr; +} + /* * get_jmp_instr() */ @@ -581,7 +648,7 @@ fc->bp_adjust = fmp->post_adjust; if (fmp->called_function) { - if (STREQ(fmp->called_function,x86_function_called_by(fc->pc))); + if (STREQ(fmp->called_function,x86_function_called_by(fc->pc))) fc->flags |= FRAMESIZE_VALIDATE; } @@ -1341,7 +1408,7 @@ console(" next: %lx size: %d opcode: 0x%x insn: \"%s\"\n", next, size, irp.opcode, irp.opcodep->name); - if (STREQ(irp.opcodep->name, "jmp")) + if (STREQ(irp.opcodep->name, "jmp") || STREQ(irp.opcodep->name, "nop")) val = 4; else val = 12; @@ -1718,7 +1785,9 @@ ra, sp, bp, asp, 0, 0, 0, EX_FRAME|SET_EX_FRAME_ADDR); return(trace->nframes); #ifdef REDHAT - } else if (STREQ(func_name, "cpu_idle")) { + } else if (STREQ(func_name, "cpu_idle") || + STREQ(func_name, "cpu_startup_entry") || + STREQ(func_name, "start_secondary")) { ra = 0; bp = sp = saddr - 4; asp = curframe->asp; @@ -1884,13 +1953,24 @@ static int kernel_entry_from_user_space(sframe_t *curframe, struct bt_info *bt) { + ulong stack_segment; + if (is_kernel_thread(bt->tc->task)) return FALSE; - if (((curframe->fp + 4 + SIZE(pt_regs)) == GET_STACKTOP(bt->task)) && - !is_kernel_thread(bt->tc->task)) - return TRUE; - else if (userspace_return(curframe->fp+4, bt)) + stack_segment = GET_STACK_ULONG(curframe->fp + 4 + SIZE(pt_regs) - sizeof(kaddr_t)); + + if ((curframe->fp + 4 + SIZE(pt_regs)) == GET_STACKTOP(bt->task)) { + if ((stack_segment == 0x7b) || (stack_segment == 0x2b)) + return TRUE; + } + + if ((curframe->fp + 4 + SIZE(pt_regs) + 8) == GET_STACKTOP(bt->task)) { + if ((stack_segment == 0x7b) || (stack_segment == 0x2b)) + return TRUE; + } + + if (userspace_return(curframe->fp+4, bt)) return TRUE; else return FALSE; @@ -2137,6 +2217,7 @@ { void *tsp; kaddr_t saddr, eip, esp; + ulong contents; trace_t *trace; #ifdef REDHAT @@ -2259,6 +2340,14 @@ } } + if (STREQ(kl_funcname(bt->instptr), "crash_kexec") || + STREQ(kl_funcname(bt->instptr), "crash_nmi_callback")) { + if (readmem(bt->stkptr-4, KVADDR, &contents, sizeof(ulong), + "stkptr-4 contents", RETURN_ON_ERROR|QUIET) && + (contents == bt->instptr)) + bt->stkptr -= 4; + } + if (!verify_back_trace(bt) && !recoverable(bt, ofp) && !BT_REFERENCE_CHECK(bt)) error(INFO, "cannot resolve stack trace:\n"); @@ -2732,16 +2821,20 @@ efp = &eframe_labels; if (!efp->init) { - if (!(efp->syscall = symbol_search("system_call"))) - error(WARNING, - "\"system_call\" symbol does not exist\n"); + if (!(efp->syscall = symbol_search("system_call"))) { + if (CRASHDEBUG(1)) + error(WARNING, + "\"system_call\" symbol does not exist\n"); + } if ((sp = symbol_search("ret_from_sys_call"))) efp->syscall_end = sp; else if ((sp = symbol_search("syscall_badsys"))) efp->syscall_end = sp; - else - error(WARNING, + else { + if (CRASHDEBUG(1)) + error(WARNING, "neither \"ret_from_sys_call\" nor \"syscall_badsys\" symbols exist\n"); + } if (efp->syscall) { efp->tracesys = symbol_search("tracesys"); diff -Nru crash-7.1.4/main.c crash-7.2.3+real/main.c --- crash-7.1.4/main.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/main.c 2018-05-17 17:39:38.000000000 +0000 @@ -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-2018 David Anderson + * Copyright (C) 2002-2018 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") && !machine_type("X86")) + 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()) { @@ -1087,16 +1098,23 @@ pc->curcmd = pc->program_name; pc->flags = (HASH|SCROLL); pc->flags |= DATADEBUG; /* default until unnecessary */ + pc->flags2 |= REDZONE; pc->confd = -2; pc->machine_type = MACHINE_TYPE; - pc->readmem = read_dev_mem; /* defaults until argv[] is parsed */ - pc->writemem = write_dev_mem; + if (file_exists("/dev/mem", NULL)) { /* defaults until argv[] is parsed */ + pc->readmem = read_dev_mem; + pc->writemem = write_dev_mem; + } else if (file_exists("/proc/kcore", NULL)) { + pc->readmem = read_proc_kcore; + pc->writemem = write_proc_kcore; + } pc->read_vmcoreinfo = no_vmcoreinfo; pc->memory_module = NULL; pc->memory_device = MEMORY_DRIVER_DEVICE; machdep->bits = sizeof(long) * 8; machdep->verify_paddr = generic_verify_paddr; machdep->get_kvaddr_ranges = generic_get_kvaddr_ranges; + machdep->is_page_ptr = generic_is_page_ptr; pc->redhat_debug_loc = DEFAULT_REDHAT_DEBUG_LOCATION; pc->cmdgencur = 0; pc->cmd_table = linux_command_table; @@ -1294,9 +1312,6 @@ if (pc->flags & REM_LIVE_SYSTEM) sprintf(&buf[strlen(buf)], "%sREM_LIVE_SYSTEM", others++ ? "|" : ""); - if (pc->flags & MEMSRC_LOCAL) - sprintf(&buf[strlen(buf)], - "%sMEMSRC_LOCAL", others++ ? "|" : ""); if (pc->flags & NAMELIST_LOCAL) sprintf(&buf[strlen(buf)], "%sNAMELIST_LOCAL", others++ ? "|" : ""); @@ -1351,6 +1366,9 @@ if (pc->flags & DISKDUMP) sprintf(&buf[strlen(buf)], "%sDISKDUMP", others++ ? "|" : ""); + if (pc->flags & VMWARE_VMSS) + sprintf(&buf[strlen(buf)], + "%sVMWARE_VMSS", others++ ? "|" : ""); if (pc->flags & SYSMAP) sprintf(&buf[strlen(buf)], "%sSYSMAP", others++ ? "|" : ""); @@ -1458,6 +1476,10 @@ fprintf(fp, "%sSNAP", others++ ? "|" : ""); if (pc->flags2 & EXCLUDED_VMEMMAP) fprintf(fp, "%sEXCLUDED_VMEMMAP", others++ ? "|" : ""); + if (pc->flags2 & MEMSRC_LOCAL) + fprintf(fp, "%sMEMSRC_LOCAL", others++ ? "|" : ""); + if (pc->flags2 & REDZONE) + fprintf(fp, "%sREDZONE", others++ ? "|" : ""); fprintf(fp, ")\n"); fprintf(fp, " namelist: %s\n", pc->namelist); diff -Nru crash-7.1.4/Makefile crash-7.2.3+real/Makefile --- crash-7.1.4/Makefile 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/Makefile 2018-05-17 17:39:38.000000000 +0000 @@ -3,8 +3,8 @@ # Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. # www.missioncriticallinux.com, info@missioncriticallinux.com # -# Copyright (C) 2002-2013 David Anderson -# Copyright (C) 2002-2013 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 @@ -20,7 +20,7 @@ PROGRAM=crash # -# Supported targets: X86 ALPHA PPC IA64 PPC64 +# Supported targets: X86 ALPHA PPC IA64 PPC64 SPARC64 # TARGET and GDB_CONF_FLAGS will be configured automatically by configure # TARGET= @@ -37,7 +37,7 @@ GDB=gdb-7.6 GDB_FILES=${GDB_7.6_FILES} GDB_OFILES= -GDB_PATCH_FILES=gdb-7.6.patch gdb-7.6-ppc64le-support.patch +GDB_PATCH_FILES=gdb-7.6.patch gdb-7.6-ppc64le-support.patch gdb-7.6-proc_service.h.patch # # Default installation directory @@ -60,9 +60,9 @@ VMWARE_HFILES=vmware_vmss.h CFILES=main.c tools.c global_data.c memory.c filesys.c help.c task.c \ - kernel.c test.c gdb_interface.c configure.c net.c dev.c \ + kernel.c test.c gdb_interface.c configure.c net.c dev.c bpf.c \ alpha.c x86.c ppc.c ia64.c s390.c s390x.c s390dbf.c ppc64.c x86_64.c \ - arm.c arm64.c mips.c \ + arm.c arm64.c mips.c sparc64.c \ extensions.c remote.c va_server.c va_server_v1.c symbols.c cmdline.c \ lkcd_common.c lkcd_v1.c lkcd_v2_v3.c lkcd_v5.c lkcd_v7.c lkcd_v8.c\ lkcd_fix_mem.c s390_dump.c lkcd_x86_trace.c \ @@ -71,7 +71,7 @@ xen_hyper.c xen_hyper_command.c xen_hyper_global_data.c \ xen_hyper_dump_tables.c kvmdump.c qemu.c qemu-load.c sadump.c ipcs.c \ ramdump.c vmware_vmss.c \ - xen_dom0.c + xen_dom0.c kaslr_helper.c SOURCE_FILES=${CFILES} ${GENERIC_HFILES} ${MCORE_HFILES} \ ${REDHAT_CFILES} ${REDHAT_HFILES} ${UNWIND_HFILES} \ @@ -79,9 +79,9 @@ ${IBM_HFILES} ${SADUMP_HFILES} ${VMWARE_HFILES} OBJECT_FILES=main.o tools.o global_data.o memory.o filesys.o help.o task.o \ - build_data.o kernel.o test.o gdb_interface.o net.o dev.o \ + build_data.o kernel.o test.o gdb_interface.o net.o dev.o bpf.o \ alpha.o x86.o ppc.o ia64.o s390.o s390x.o s390dbf.o ppc64.o x86_64.o \ - arm.o arm64.o mips.o \ + arm.o arm64.o mips.o sparc64.o \ extensions.o remote.o va_server.o va_server_v1.o symbols.o cmdline.o \ lkcd_common.o lkcd_v1.o lkcd_v2_v3.o lkcd_v5.o lkcd_v7.o lkcd_v8.o \ lkcd_fix_mem.o s390_dump.o netdump.o diskdump.o makedumpfile.o xendump.o \ @@ -90,7 +90,7 @@ xen_hyper.o xen_hyper_command.o xen_hyper_global_data.o \ xen_hyper_dump_tables.o kvmdump.o qemu.o qemu-load.o sadump.o ipcs.o \ ramdump.o vmware_vmss.o \ - xen_dom0.o + xen_dom0.o kaslr_helper.o MEMORY_DRIVER_FILES=memory_driver/Makefile memory_driver/crash.c memory_driver/README @@ -233,7 +233,7 @@ @rm -f ${PROGRAM} @if [ ! -f ${GDB}/config.status ]; then \ (cd ${GDB}; ./configure ${GDB_CONF_FLAGS} --with-separate-debug-dir=/usr/lib/debug \ - --with-bugurl="" --with-expat=no --with-python=no; \ + --with-bugurl="" --with-expat=no --with-python=no --disable-sim; \ make --no-print-directory CRASH_TARGET=${TARGET}; echo ${TARGET} > crash.target) \ else make --no-print-directory rebuild; fi @if [ ! -f ${PROGRAM} ]; then \ @@ -245,7 +245,7 @@ touch ${GDB}/${GDB}.patch; fi @if [ -f ${GDB}.patch ] && [ -s ${GDB}.patch ] && \ [ "`sum ${GDB}.patch`" != "`sum ${GDB}/${GDB}.patch`" ]; then \ - (patch -N -p0 -r- < ${GDB}.patch; cp ${GDB}.patch ${GDB}; cd ${GDB}; \ + (patch -N -p0 -r- --fuzz=0 < ${GDB}.patch; cp ${GDB}.patch ${GDB}; cd ${GDB}; \ make --no-print-directory CRASH_TARGET=${TARGET}) \ else (cd ${GDB}/gdb; make --no-print-directory CRASH_TARGET=${TARGET}); fi @@ -269,6 +269,12 @@ if [ "${ARCH}" = "x86_64" ] && [ "${TARGET}" = "PPC64" ] && [ -f ${GDB}-ppc64le-support.patch ]; then \ patch -d ${GDB} -p1 -F0 < ${GDB}-ppc64le-support.patch ; \ fi + if [ -f /usr/include/proc_service.h ]; then \ + grep 'extern ps_err_e ps_get_thread_area (struct' /usr/include/proc_service.h; \ + if [ $$? -eq 0 ]; then \ + patch -p0 < ${GDB}-proc_service.h.patch; \ + fi; \ + fi library: make_build_data ${OBJECT_FILES} ar -rs ${PROGRAM}lib.a ${OBJECT_FILES} @@ -422,6 +428,9 @@ mips.o: ${GENERIC_HFILES} ${REDHAT_HFILES} mips.c ${CC} -c ${CRASH_CFLAGS} mips.c ${WARNING_OPTIONS} ${WARNING_ERROR} +sparc64.o: ${GENERIC_HFILES} ${REDHAT_HFILES} sparc64.c + ${CC} -c ${CRASH_CFLAGS} sparc64.c ${WARNING_OPTIONS} ${WARNING_ERROR} + s390.o: ${GENERIC_HFILES} ${IBM_HFILES} s390.c ${CC} -c ${CRASH_CFLAGS} s390.c ${WARNING_OPTIONS} ${WARNING_ERROR} @@ -508,6 +517,12 @@ vmware_vmss.o: ${GENERIC_HFILES} ${VMWARE_HFILES} vmware_vmss.c ${CC} -c ${CRASH_CFLAGS} vmware_vmss.c ${WARNING_OPTIONS} ${WARNING_ERROR} +kaslr_helper.o: ${GENERIC_HFILES} kaslr_helper.c + ${CC} -c ${CRASH_CFLAGS} kaslr_helper.c ${WARNING_OPTIONS} ${WARNING_ERROR} + +bpf.o: ${GENERIC_HFILES} bpf.c + ${CC} -c ${CRASH_CFLAGS} bpf.c ${WARNING_OPTIONS} ${WARNING_ERROR} + ${PROGRAM}: force @make --no-print-directory all @@ -551,7 +566,7 @@ tar cvzf ${PROGRAM}.tar.gz ${TAR_FILES} ${GDB_FILES} ${GDB_PATCH_FILES} @echo; ls -l ${PROGRAM}.tar.gz -VERSION=7.1.4 +VERSION=7.2.3 RELEASE=0 release: make_configure diff -Nru crash-7.1.4/memory.c crash-7.2.3+real/memory.c --- crash-7.1.4/memory.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/memory.c 2018-05-17 17:39:38.000000000 +0000 @@ -1,8 +1,8 @@ /* memory.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-2018 David Anderson + * Copyright (C) 2002-2018 Red Hat, Inc. All rights reserved. * Copyright (C) 2002 Silicon Graphics, Inc. * * This program is free software; you can redistribute it and/or modify @@ -75,7 +75,7 @@ ulong container; int *freelist; int freelist_index_size; - + ulong random; }; /* @@ -237,7 +237,7 @@ struct reference *); static void rss_page_types_init(void); static int dump_swap_info(ulong, ulong *, ulong *); -static int get_hugetlb_total_pages(ulong *); +static int get_hugetlb_total_pages(ulong *, ulong *); static void swap_info_init(void); static char *get_swapdev(ulong, char *); static void fill_swap_info(ulong); @@ -255,7 +255,7 @@ static void PG_slab_flag_init(void); static ulong nr_blockdev_pages(void); void sparse_mem_init(void); -void dump_mem_sections(void); +void dump_mem_sections(int); void list_mem_sections(void); ulong sparse_decode_mem_map(ulong, ulong); char *read_mem_section(ulong); @@ -279,7 +279,8 @@ static int get_kmem_cache_list(ulong **); static int get_kmem_cache_slub_data(long, struct meminfo *); static ulong compound_head(ulong); -static long count_partial(ulong, struct meminfo *); +static long count_partial(ulong, struct meminfo *, ulong *); +static short count_cpu_partial(struct meminfo *, int); static ulong get_freepointer(struct meminfo *, void *); static int count_free_objects(struct meminfo *, ulong); char *is_slab_page(struct meminfo *, char *); @@ -292,6 +293,7 @@ static void dump_page_flags(ulonglong); static ulong kmem_cache_nodelists(ulong); static void dump_hstates(void); +static ulong freelist_ptr(struct meminfo *, ulong, ulong); /* * Memory display modes specific to this file. @@ -433,6 +435,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"); @@ -689,9 +695,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")) { @@ -712,22 +724,40 @@ MEMBER_OFFSET_INIT(kmem_cache_node, "kmem_cache", "node"); MEMBER_OFFSET_INIT(kmem_cache_cpu_slab, "kmem_cache", "cpu_slab"); MEMBER_OFFSET_INIT(kmem_cache_list, "kmem_cache", "list"); + MEMBER_OFFSET_INIT(kmem_cache_red_left_pad, "kmem_cache", "red_left_pad"); MEMBER_OFFSET_INIT(kmem_cache_name, "kmem_cache", "name"); MEMBER_OFFSET_INIT(kmem_cache_flags, "kmem_cache", "flags"); + MEMBER_OFFSET_INIT(kmem_cache_random, "kmem_cache", "random"); MEMBER_OFFSET_INIT(kmem_cache_cpu_freelist, "kmem_cache_cpu", "freelist"); 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)) { @@ -742,6 +772,8 @@ "kmem_cache_node", "nr_partial"); MEMBER_OFFSET_INIT(kmem_cache_node_nr_slabs, "kmem_cache_node", "nr_slabs"); + MEMBER_OFFSET_INIT(kmem_cache_node_total_objects, + "kmem_cache_node", "total_objects"); MEMBER_OFFSET_INIT(kmem_cache_node_partial, "kmem_cache_node", "partial"); MEMBER_OFFSET_INIT(kmem_cache_node_full, @@ -1405,7 +1437,7 @@ char hexchars[MAX_HEXCHARS_PER_LINE+1]; char ch; int linelen; - char buf[BUFSIZE]; + char buf[BUFSIZE*2]; char slab[BUFSIZE]; int ascii_start; ulong error_handle; @@ -1919,7 +1951,7 @@ char * format_stack_entry(struct bt_info *bt, char *retbuf, ulong value, ulong limit) { - char buf[BUFSIZE]; + char buf[BUFSIZE*2]; char slab[BUFSIZE]; if (BITS32()) { @@ -3982,7 +4014,7 @@ int display; char buf1[BUFSIZE]; char buf2[BUFSIZE]; - char buf3[BUFSIZE]; + char buf3[BUFSIZE*2]; char buf4[BUFSIZE]; if (mm == symbol_value("init_mm")) @@ -4614,7 +4646,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)) / @@ -4678,6 +4710,7 @@ #define CACHE_SET (ADDRESS_SPECIFIED << 23) #define SLAB_OVERLOAD_PAGE_PTR (ADDRESS_SPECIFIED << 24) #define SLAB_BITFIELD (ADDRESS_SPECIFIED << 25) +#define SLAB_GATHER_FAILURE (ADDRESS_SPECIFIED << 26) #define GET_ALL \ (GET_SHARED_PAGES|GET_TOTALRAM_PAGES|GET_BUFFERS_PAGES|GET_SLAB_PAGES) @@ -5077,7 +5110,13 @@ return; } - vaddr = kt->stext ? kt->stext : symbol_value("sys_read"); + vaddr = kt->stext; + if (!vaddr) { + if (kernel_symbol_exists("sys_read")) + vaddr = symbol_value("sys_read"); + else if (kernel_symbol_exists("__x64_sys_read")) + vaddr = symbol_value("__x64_sys_read"); + } if (!phys_to_page((physaddr_t)VTOP(vaddr), &pageptr)) return; @@ -6349,8 +6388,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; } @@ -7392,8 +7436,8 @@ * Callback function for free-list search for a specific page. */ struct free_page_callback_data { - physaddr_t searchphys; - long block_size; + ulong searchpage; + long chunk_size; ulong page; int found; }; @@ -7402,13 +7446,12 @@ free_page_callback(void *page, void *arg) { struct free_page_callback_data *cbd = arg; - physaddr_t this_phys; + ulong first_page, last_page; - if (!page_to_phys((ulong)page, &this_phys)) - return FALSE; + first_page = (ulong)page; + last_page = first_page + (cbd->chunk_size * SIZE(page)); - if ((cbd->searchphys >= this_phys) && - (cbd->searchphys < (this_phys + cbd->block_size))) { + if ((cbd->searchpage >= first_page) && (cbd->searchpage <= last_page)) { cbd->page = (ulong)page; cbd->found = TRUE; return TRUE; @@ -7434,6 +7477,7 @@ ulong offset, verbose, value, sum, found; ulong this_addr; physaddr_t phys, this_phys, searchphys, end_paddr; + ulong searchpage; struct free_page_callback_data callback_data; ulong pp; ulong zone_mem_map; @@ -7470,8 +7514,12 @@ error(FATAL, "dump_free_pages_zones_v2: no memtype specified\n"); } + if (!phys_to_page(searchphys, &searchpage)) { + error(INFO, "cannot determine page for %lx\n", fi->spec_addr); + return; + } do_search = TRUE; - callback_data.searchphys = searchphys; + callback_data.searchpage = searchpage; callback_data.found = FALSE; } else { searchphys = 0; @@ -8017,7 +8065,7 @@ ld->flags |= (LIST_CALLBACK|CALLBACK_RETURN); ld->callback_func = free_page_callback; ld->callback_data = (void *)callback_data; - callback_data->block_size = chunk_size * PAGESIZE(); + callback_data->chunk_size = chunk_size; } cnt = do_list(ld); if (cnt < 0) { @@ -8072,7 +8120,8 @@ long committed; ulong overcommit_kbytes = 0; int overcommit_ratio; - ulong hugetlb_total_pages; + ulong hugetlb_total_pages, hugetlb_total_free_pages = 0; + int done_hugetlb_calc = 0; long nr_file_pages, nr_slab; ulong swapper_space_nrpages; ulong pct; @@ -8193,7 +8242,44 @@ char *swapper_space = GETBUF(SIZE(address_space)); swapper_space_nrpages = 0; - if (symbol_exists("swapper_spaces") && + if (symbol_exists("nr_swapper_spaces") && + (len = get_array_length("nr_swapper_spaces", + NULL, 0))) { + char *nr_swapper_space = + GETBUF(len * sizeof(unsigned int)); + readmem(symbol_value("nr_swapper_spaces"), KVADDR, + nr_swapper_space, len * sizeof(unsigned int), + "nr_swapper_space", RETURN_ON_ERROR); + for (i = 0; i < len; i++) { + int j; + unsigned long sa; + unsigned int banks = UINT(nr_swapper_space + + (i * sizeof(unsigned int))); + + if (!banks) + continue; + + readmem(symbol_value("swapper_spaces") + + (i * sizeof(void *)),KVADDR, + &sa, sizeof(void *), + "swapper_space", RETURN_ON_ERROR); + + if (!sa) + continue; + + for (j = 0; j < banks; j++) { + readmem(sa + j * SIZE(address_space), + KVADDR, swapper_space, + SIZE(address_space), + "swapper_space", + RETURN_ON_ERROR); + swapper_space_nrpages += + ULONG(swapper_space + + OFFSET(address_space_nrpages)); + } + } + FREEBUF(nr_swapper_space); + } else if (symbol_exists("swapper_spaces") && (len = get_array_length("swapper_spaces", NULL, 0))) { for (i = 0; i < len; i++) { if (!readmem(symbol_value("swapper_spaces") + @@ -8210,7 +8296,7 @@ RETURN_ON_ERROR)) swapper_space_nrpages = ULONG(swapper_space + OFFSET(address_space_nrpages)); - + page_cache_size = nr_file_pages - swapper_space_nrpages - buffer_pages; FREEBUF(swapper_space); @@ -8280,6 +8366,22 @@ pages_to_size(freelowmem_pages, buf), pct); } + if (get_hugetlb_total_pages(&hugetlb_total_pages, + &hugetlb_total_free_pages)) { + done_hugetlb_calc = 1; + + fprintf(fp, "\n%13s %7ld %11s ----\n", + "TOTAL HUGE", hugetlb_total_pages, + pages_to_size(hugetlb_total_pages, buf)); + pct = hugetlb_total_free_pages ? + (hugetlb_total_free_pages * 100) / + hugetlb_total_pages : 0; + fprintf(fp, "%13s %7ld %11s %3ld%% of TOTAL HUGE\n", + "HUGE FREE", + hugetlb_total_free_pages, + pages_to_size(hugetlb_total_free_pages, buf), pct); + } + /* * get swap data from dump_swap_info(). */ @@ -8290,12 +8392,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", @@ -8308,6 +8410,7 @@ "swap_info[%ld].swap_map at %lx is inaccessible\n", totalused_pages, totalswap_pages); } + /* * Show committed memory */ @@ -8325,7 +8428,7 @@ get_symbol_data("sysctl_overcommit_ratio", sizeof(int), &overcommit_ratio); - if (!get_hugetlb_total_pages(&hugetlb_total_pages)) + if (!done_hugetlb_calc) goto bailout; allowed = ((totalram_pages - hugetlb_total_pages) @@ -9846,6 +9949,7 @@ #define SLAB_MAGIC_DESTROYED 0xB2F23C5AUL /* slab has been destroyed */ #define SLAB_CFLGS_BUFCTL 0x020000UL /* bufctls in own cache */ +#define SLAB_CFLGS_OBJFREELIST 0x40000000UL /* Freelist as an object */ #define KMEM_SLAB_ADDR (1) #define KMEM_BUFCTL_ADDR (2) @@ -12405,11 +12509,13 @@ static void gather_slab_free_list_slab_overload_page(struct meminfo *si) { - int i, active; + int i, active, start_offset; ulong obj, objnr, cnt, freelist; unsigned char *ucharptr; unsigned short *ushortptr; unsigned int *uintptr; + unsigned int cache_flags, overload_active; + ulong slab_overload_page; if (CRASHDEBUG(1)) fprintf(fp, "slab page: %lx active: %ld si->c_num: %ld\n", @@ -12418,12 +12524,19 @@ if (si->s_inuse == si->c_num ) return; - readmem(si->slab - OFFSET(page_lru) + OFFSET(page_freelist), + slab_overload_page = si->slab - OFFSET(page_lru); + readmem(slab_overload_page + OFFSET(page_freelist), KVADDR, &freelist, sizeof(void *), "page freelist", FAULT_ON_ERROR); readmem(freelist, KVADDR, si->freelist, si->freelist_index_size * si->c_num, "freelist array", FAULT_ON_ERROR); + readmem(si->cache+OFFSET(kmem_cache_s_flags), + KVADDR, &cache_flags, sizeof(uint), + "kmem_cache_s flags", FAULT_ON_ERROR); + readmem(slab_overload_page + OFFSET(page_active), + KVADDR, &overload_active, sizeof(uint), + "active", FAULT_ON_ERROR); BNEG(si->addrlist, sizeof(ulong) * (si->c_num+1)); cnt = objnr = 0; @@ -12432,14 +12545,22 @@ uintptr = NULL; active = si->s_inuse; + /* + * On an OBJFREELIST slab, the object might have been recycled + * and everything before the active count can be random data. + */ + start_offset = 0; + if (cache_flags & SLAB_CFLGS_OBJFREELIST) + start_offset = overload_active; + switch (si->freelist_index_size) { - case 1: ucharptr = (unsigned char *)si->freelist; break; - case 2: ushortptr = (unsigned short *)si->freelist; break; - case 4: uintptr = (unsigned int *)si->freelist; break; + case 1: ucharptr = (unsigned char *)si->freelist + start_offset; break; + case 2: ushortptr = (unsigned short *)si->freelist + start_offset; break; + case 4: uintptr = (unsigned int *)si->freelist + start_offset; break; } - for (i = 0; i < si->c_num; i++) { + for (i = start_offset; i < si->c_num; i++) { switch (si->freelist_index_size) { case 1: objnr = (ulong)*ucharptr++; break; @@ -13218,6 +13339,12 @@ mi->spec_addr, memtype_string(mi->memtype, 0)); } +int +generic_is_page_ptr(ulong addr, physaddr_t *phys) +{ + return FALSE; +} + /* * Determine whether an address is a page pointer from the mem_map[] array. * If the caller requests it, return the associated physical address. @@ -13234,8 +13361,11 @@ ulong coded_mem_map, mem_map, end_mem_map; physaddr_t section_paddr; + if (machdep->is_page_ptr(addr, phys)) + return TRUE; + if (IS_SPARSEMEM()) { - nr_mem_sections = NR_MEM_SECTIONS(); + nr_mem_sections = vt->max_mem_section_nr+1; for (nr = 0; nr < nr_mem_sections ; nr++) { if ((sec_addr = valid_section_nr(nr))) { coded_mem_map = section_mem_map_addr(sec_addr); @@ -13553,6 +13683,7 @@ fprintf(fp, " swap_info_struct: %lx\n", (ulong)vt->swap_info_struct); fprintf(fp, " mem_sec: %lx\n", (ulong)vt->mem_sec); fprintf(fp, " mem_section: %lx\n", (ulong)vt->mem_section); + fprintf(fp, " max_mem_section_nr: %ld\n", (ulong)vt->max_mem_section_nr); fprintf(fp, " ZONE_HIGHMEM: %d\n", vt->ZONE_HIGHMEM); fprintf(fp, "node_online_map_len: %d\n", vt->node_online_map_len); if (vt->node_online_map_len) { @@ -13656,7 +13787,7 @@ for (i = vhits = 0; i < VMA_CACHE; i++) vhits += vt->cached_vma_hits[i]; - fprintf(stderr, "%s vma hit rate: %2ld%% (%ld of %ld)\n", + fprintf(fp, "%s vma hit rate: %2ld%% (%ld of %ld)\n", verbose ? "" : " ", (vhits * 100)/vt->vma_cache_fills, vhits, vt->vma_cache_fills); @@ -13806,7 +13937,7 @@ ulong value, mask, len; ulong uvaddr_start, uvaddr_end; ulong kvaddr_start, kvaddr_end, range_end; - int sflag, Kflag, Vflag, pflag, tflag; + int sflag, Kflag, Vflag, pflag, Tflag, tflag; struct searchinfo searchinfo; struct syment *sp; struct node_table *nt; @@ -13820,7 +13951,7 @@ context = max = 0; start = end = 0; - value = mask = sflag = pflag = Kflag = Vflag = memtype = len = tflag = 0; + value = mask = sflag = pflag = Kflag = Vflag = memtype = len = Tflag = tflag = 0; kvaddr_start = kvaddr_end = 0; uvaddr_start = UNINITIALIZED; uvaddr_end = COMMON_VADDR_SPACE() ? (ulong)(-1) : machdep->kvbase; @@ -13857,7 +13988,7 @@ searchinfo.mode = SEARCH_ULONG; /* default search */ - while ((c = getopt(argcnt, args, "tl:ukKVps:e:v:m:hwcx:")) != EOF) { + while ((c = getopt(argcnt, args, "Ttl:ukKVps:e:v:m:hwcx:")) != EOF) { switch(c) { case 'u': @@ -13962,12 +14093,19 @@ context = dtoi(optarg, FAULT_ON_ERROR, NULL); break; + case 'T': case 't': if (XEN_HYPER_MODE()) error(FATAL, - "-t option is not applicable to the " - "Xen hypervisor\n"); - tflag++; + "-%c option is not applicable to the " + "Xen hypervisor\n", c); + if (c == 'T') + Tflag++; + else if (c == 't') + tflag++; + if (tflag && Tflag) + error(FATAL, + "-t and -T options are mutually exclusive\n"); break; default: @@ -13976,10 +14114,11 @@ } } - if (tflag && (memtype || start || end || len)) + if ((tflag || Tflag) && (memtype || start || end || len)) error(FATAL, - "-t option cannot be used with other " - "memory-selection options\n"); + "-%c option cannot be used with other " + "memory-selection options\n", + tflag ? 't' : 'T'); if (XEN_HYPER_MODE()) { memtype = KVADDR; @@ -14252,10 +14391,12 @@ break; } - if (tflag) { + if (tflag || Tflag) { searchinfo.tasks_found = 0; tc = FIRST_CONTEXT(); for (i = 0; i < RUNNING_TASKS(); i++, tc++) { + if (Tflag && !is_task_active(tc->task)) + continue; searchinfo.vaddr_start = GET_STACKBASE(tc->task); searchinfo.vaddr_end = GET_STACKTOP(tc->task); searchinfo.task_context = tc; @@ -14379,7 +14520,7 @@ } amount = ctx * t; - addr_d = addr - amount < 0 ? 0 : addr - amount; + addr_d = addr - amount; display_memory(addr_d, ctx, flag, memtype, NULL); @@ -15236,19 +15377,21 @@ } static int -get_hugetlb_total_pages(ulong *nr_total_pages) +get_hugetlb_total_pages(ulong *nr_total_pages, ulong *nr_total_free_pages) { ulong hstate_p, vaddr; int i, len; ulong nr_huge_pages; + ulong free_huge_pages; uint horder; - *nr_total_pages = 0; + *nr_total_pages = *nr_total_free_pages = 0; if (kernel_symbol_exists("hstates")) { if (INVALID_SIZE(hstate) || INVALID_MEMBER(hstate_order) || - INVALID_MEMBER(hstate_nr_huge_pages)) + INVALID_MEMBER(hstate_nr_huge_pages) || + INVALID_MEMBER(hstate_free_huge_pages)) return FALSE; len = get_array_length("hstates", NULL, 0); @@ -15268,7 +15411,12 @@ KVADDR, &nr_huge_pages, sizeof(ulong), "hstate_nr_huge_pages", FAULT_ON_ERROR); + readmem(vaddr + OFFSET(hstate_free_huge_pages), + KVADDR, &free_huge_pages, sizeof(ulong), + "hstate_free_huge_pages", FAULT_ON_ERROR); + *nr_total_pages += nr_huge_pages * (1 << horder); + *nr_total_free_pages += free_huge_pages * (1 << horder); } } else if (kernel_symbol_exists("nr_huge_pages")) { unsigned long hpage_shift = 21; @@ -15277,8 +15425,12 @@ hpage_shift = 22; get_symbol_data("nr_huge_pages", sizeof(ulong), &nr_huge_pages); + get_symbol_data("free_huge_pages", + sizeof(ulong), &free_huge_pages); *nr_total_pages = nr_huge_pages * ((1 << hpage_shift) / machdep->pagesize); + *nr_total_free_pages = free_huge_pages * + ((1 << hpage_shift) / machdep->pagesize); } return TRUE; } @@ -15547,6 +15699,9 @@ if (!pte) return NULL; + if (!symbol_exists("nr_swapfiles") || !symbol_exists("swap_info")) + return NULL; + if (THIS_KERNEL_VERSION >= LINUX(2,6,0)) sprintf(buf, "%s OFFSET: %lld", get_swapdev(__swp_type(pte), swapdev), (ulonglong)__swp_offset(pte)); @@ -15566,12 +15721,8 @@ unsigned int i, swap_info_len; ulong swap_info, swap_info_ptr, swap_file; ulong vfsmnt; - - if (!symbol_exists("nr_swapfiles")) - error(FATAL, "nr_swapfiles doesn't exist in this kernel!\n"); - - if (!symbol_exists("swap_info")) - error(FATAL, "swap_info doesn't exist in this kernel!\n"); + char *devname; + char buf1[BUFSIZE]; swap_info_init(); @@ -15613,8 +15764,13 @@ OFFSET(swap_info_struct_swap_vfsmnt)); get_pathname(swap_file, buf, BUFSIZE, 1, vfsmnt); } else if (VALID_MEMBER (swap_info_struct_old_block_size)) { - get_pathname(file_to_dentry(swap_file), - buf, BUFSIZE, 1, 0); + devname = vfsmount_devname(file_to_vfsmnt(swap_file), + buf1, BUFSIZE); + get_pathname(file_to_dentry(swap_file), + buf, BUFSIZE, 1, file_to_vfsmnt(swap_file)); + if ((STREQ(devname, "devtmpfs") || STREQ(devname, "udev")) + && !STRNEQ(buf, "/dev/")) + string_insert("/dev", buf); } else { get_pathname(swap_file, buf, BUFSIZE, 1, 0); } @@ -16155,8 +16311,8 @@ vt->numnodes = n; } - if (!initialize && IS_SPARSEMEM()) - dump_mem_sections(); + if (IS_SPARSEMEM()) + dump_mem_sections(initialize); } /* @@ -16463,6 +16619,7 @@ case CRASHBUILTIN: case KVMDUMP: case PROC_KCORE: + case LIVE_RAMDUMP: psz = (uint)getpagesize(); break; @@ -16758,6 +16915,8 @@ retval = kcore_memory_dump(fp); else if (pc->flags & SADUMP) retval = sadump_memory_dump(fp); + else if (pc->flags & VMWARE_VMSS) + retval = vmware_vmss_memory_dump(fp); break; case DUMPFILE_ENVIRONMENT: @@ -16787,7 +16946,7 @@ { ulong addr; ulong mem_section_size; - int len, dimension; + int len, dimension, mem_section_is_ptr; if (!IS_SPARSEMEM()) return; @@ -16799,8 +16958,19 @@ error(FATAL, "CONFIG_SPARSEMEM kernels not supported for this architecture\n"); + /* + * The kernel's mem_section changed from array to pointer in this commit: + * + * commit 83e3c48729d9ebb7af5a31a504f3fd6aff0348c4 + * mm/sparsemem: Allocate mem_section at runtime for CONFIG_SPARSEMEM_EXTREME=y + */ + mem_section_is_ptr = + get_symbol_type("mem_section", NULL, NULL) == TYPE_CODE_PTR ? + TRUE : FALSE; + if (((len = get_array_length("mem_section", &dimension, 0)) == - (NR_MEM_SECTIONS() / _SECTIONS_PER_ROOT_EXTREME())) || !dimension) + (NR_MEM_SECTIONS() / _SECTIONS_PER_ROOT_EXTREME())) || + mem_section_is_ptr || !dimension) vt->flags |= SPARSEMEM_EX; if (IS_SPARSEMEM_EX()) { @@ -16819,7 +16989,7 @@ fprintf(fp, "SECTIONS_PER_ROOT = %ld\n", SECTIONS_PER_ROOT() ); fprintf(fp, "SECTION_ROOT_MASK = 0x%lx\n", SECTION_ROOT_MASK()); fprintf(fp, "PAGES_PER_SECTION = %ld\n", PAGES_PER_SECTION()); - if (IS_SPARSEMEM_EX() && !len) + if (!mem_section_is_ptr && IS_SPARSEMEM_EX() && !len) error(WARNING, "SPARSEMEM_EX: questionable section values\n"); } @@ -16828,8 +16998,12 @@ if (!(vt->mem_section = (char *)malloc(SIZE(mem_section)))) error(FATAL, "cannot malloc mem_section cache\n"); - addr = symbol_value("mem_section"); - readmem(addr, KVADDR,vt->mem_sec ,mem_section_size, + if (mem_section_is_ptr) + get_symbol_data("mem_section", sizeof(void *), &addr); + else + addr = symbol_value("mem_section"); + + readmem(addr, KVADDR, vt->mem_sec, mem_section_size, "memory section root table", FAULT_ON_ERROR); } @@ -16887,7 +17061,8 @@ */ #define SECTION_MARKED_PRESENT (1UL<<0) #define SECTION_HAS_MEM_MAP (1UL<<1) -#define SECTION_MAP_LAST_BIT (1UL<<2) +#define SECTION_IS_ONLINE (1UL<<2) +#define SECTION_MAP_LAST_BIT (1UL<<3) #define SECTION_MAP_MASK (~(SECTION_MAP_LAST_BIT-1)) @@ -16898,8 +17073,8 @@ if ((mem_section = read_mem_section(addr))) return (ULONG(mem_section + - OFFSET(mem_section_section_mem_map)) && - SECTION_MARKED_PRESENT); + OFFSET(mem_section_section_mem_map)) + & SECTION_MARKED_PRESENT); return 0; } @@ -16907,11 +17082,17 @@ section_has_mem_map(ulong addr) { char *mem_section; + ulong kernel_version_bit; + + if (THIS_KERNEL_VERSION >= LINUX(2,6,24)) + kernel_version_bit = SECTION_HAS_MEM_MAP; + else + kernel_version_bit = SECTION_MARKED_PRESENT; if ((mem_section = read_mem_section(addr))) return (ULONG(mem_section + OFFSET(mem_section_section_mem_map)) - && SECTION_HAS_MEM_MAP); + & kernel_version_bit); return 0; } @@ -16965,9 +17146,9 @@ } void -dump_mem_sections(void) +dump_mem_sections(int initialize) { - ulong nr,addr; + ulong nr, max, addr; ulong nr_mem_sections; ulong coded_mem_map, mem_map, pfn; char buf1[BUFSIZE]; @@ -16977,6 +17158,15 @@ nr_mem_sections = NR_MEM_SECTIONS(); + if (initialize) { + for (nr = max = 0; nr < nr_mem_sections ; nr++) { + if (valid_section_nr(nr)) + max = nr; + } + vt->max_mem_section_nr = max; + return; + } + fprintf(fp, "\n"); pad_line(fp, BITS32() ? 59 : 67, '-'); fprintf(fp, "\n\nNR %s %s %s PFN\n", @@ -17107,10 +17297,8 @@ int i, j, node; ulong mask, *maskptr; - if ((first/BITS_PER_LONG) >= vt->node_online_map_len) { - error(INFO, "next_online_node: %d is too large!\n", first); + if ((first/BITS_PER_LONG) >= vt->node_online_map_len) return -1; - } maskptr = (ulong *)vt->node_online_map; for (i = node = 0; i < vt->node_online_map_len; i++, maskptr++) { @@ -17231,30 +17419,43 @@ int c ATTRIBUTE_UNUSED; struct gnu_request *req; char *start; - long enum_value; + long enum_value, zc = -1; + int split_vmstat = 0, ni = 0; if (vt->flags & VM_STAT) return TRUE; - if ((vt->nr_vm_stat_items == -1) || !symbol_exists("vm_stat")) + if ((vt->nr_vm_stat_items == -1) || + (!symbol_exists("vm_stat") && !symbol_exists("vm_zone_stat"))) goto bailout; /* * look for type: type = atomic_long_t [] */ if (LKCD_KERNTYPES()) { - if (!symbol_exists("vm_stat")) + if ((!symbol_exists("vm_stat") && + !symbol_exists("vm_zone_stat"))) goto bailout; /* * Just assume that vm_stat is an array; there is * no symbol info in a kerntypes file. */ } else { - if (!symbol_exists("vm_stat") || - get_symbol_type("vm_stat", NULL, NULL) != TYPE_CODE_ARRAY) + if (symbol_exists("vm_stat") && + get_symbol_type("vm_stat", NULL, NULL) == TYPE_CODE_ARRAY) { + vt->nr_vm_stat_items = + get_array_length("vm_stat", NULL, 0); + } else if (symbol_exists("vm_zone_stat") && + get_symbol_type("vm_zone_stat", + NULL, NULL) == TYPE_CODE_ARRAY) { + vt->nr_vm_stat_items = + get_array_length("vm_zone_stat", NULL, 0) + + get_array_length("vm_node_stat", NULL, 0); + split_vmstat = 1; + enumerator_value("NR_VM_ZONE_STAT_ITEMS", &zc); + } else { goto bailout; - - vt->nr_vm_stat_items = get_array_length("vm_stat", NULL, 0); + } } open_tmpfile(); @@ -17263,6 +17464,14 @@ req->name = "zone_stat_item"; req->flags = GNU_PRINT_ENUMERATORS; gdb_interface(req); + + if (split_vmstat) { + req->command = GNU_GET_DATATYPE; + req->name = "node_stat_item"; + req->flags = GNU_PRINT_ENUMERATORS; + gdb_interface(req); + } + FREEBUF(req); stringlen = 1; @@ -17274,11 +17483,17 @@ continue; clean_line(buf); c = parse_line(buf, arglist); - if (STREQ(arglist[0], "NR_VM_ZONE_STAT_ITEMS")) { + if ((!split_vmstat && + STREQ(arglist[0], "NR_VM_ZONE_STAT_ITEMS")) || + (split_vmstat && + STREQ(arglist[0], "NR_VM_NODE_STAT_ITEMS"))) { if (LKCD_KERNTYPES()) vt->nr_vm_stat_items = MAX(atoi(arglist[2]), count); break; + } else if (split_vmstat && + STREQ(arglist[0], "NR_VM_ZONE_STAT_ITEMS")) { + continue; } else { stringlen += strlen(arglist[0]); count++; @@ -17300,18 +17515,24 @@ if (strstr(buf, "{") || strstr(buf, "}")) continue; c = parse_line(buf, arglist); - if (enumerator_value(arglist[0], &enum_value)) - i = enum_value; - else { + if (!enumerator_value(arglist[0], &enum_value)) { close_tmpfile(); goto bailout; } + + i = ni + enum_value; + if (!ni && (enum_value == zc)) { + ni = zc; + continue; + } + if (i < vt->nr_vm_stat_items) { vt->vm_stat_items[i] = start; strcpy(start, arglist[0]); start += strlen(arglist[0]) + 1; } } + close_tmpfile(); vt->flags |= VM_STAT; @@ -17334,39 +17555,61 @@ ulong *vp; ulong location; int i, maxlen, len; + long tc, zc = 0, nc = 0; + int split_vmstat = 0; if (!vm_stat_init()) { if (!item) if (CRASHDEBUG(1)) - error(INFO, + error(INFO, "vm_stat not available in this kernel\n"); return FALSE; } buf = GETBUF(sizeof(ulong) * vt->nr_vm_stat_items); - location = zone ? zone : symbol_value("vm_stat"); + if (symbol_exists("vm_node_stat") && symbol_exists("vm_zone_stat")) + split_vmstat = 1; + else + location = zone ? zone : symbol_value("vm_stat"); - readmem(location, KVADDR, buf, - sizeof(ulong) * vt->nr_vm_stat_items, - "vm_stat", FAULT_ON_ERROR); + if (split_vmstat) { + enumerator_value("NR_VM_ZONE_STAT_ITEMS", &zc); + location = zone ? zone : symbol_value("vm_zone_stat"); + readmem(location, KVADDR, buf, + sizeof(ulong) * zc, + "vm_zone_stat", FAULT_ON_ERROR); + if (!zone) { + location = symbol_value("vm_node_stat"); + enumerator_value("NR_VM_NODE_STAT_ITEMS", &nc); + readmem(location, KVADDR, buf + (sizeof(ulong) * zc), + sizeof(ulong) * nc, + "vm_node_stat", FAULT_ON_ERROR); + } + tc = zc + nc; + } else { + readmem(location, KVADDR, buf, + sizeof(ulong) * vt->nr_vm_stat_items, + "vm_stat", FAULT_ON_ERROR); + tc = vt->nr_vm_stat_items; + } if (!item) { if (!zone) fprintf(fp, " VM_STAT:\n"); - for (i = maxlen = 0; i < vt->nr_vm_stat_items; i++) + for (i = maxlen = 0; i < tc; i++) if ((len = strlen(vt->vm_stat_items[i])) > maxlen) maxlen = len; vp = (ulong *)buf; - for (i = 0; i < vt->nr_vm_stat_items; i++) - fprintf(fp, "%s%s: %ld\n", + for (i = 0; i < tc; i++) + fprintf(fp, "%s%s: %ld\n", space(maxlen - strlen(vt->vm_stat_items[i])), vt->vm_stat_items[i], vp[i]); return TRUE; } vp = (ulong *)buf; - for (i = 0; i < vt->nr_vm_stat_items; i++) { + for (i = 0; i < tc; i++) { if (STREQ(vt->vm_stat_items[i], item)) { *retval = vp[i]; return TRUE; @@ -17765,6 +18008,9 @@ char b2[BUFSIZE]; int namelen, sizelen, spacelen; + if (si->flags & SLAB_GATHER_FAILURE) + error(INFO, "%s: cannot gather relevant slab data\n", si->curname); + fprintf(fp, "%s ", mkstring(b1, VADDR_PRLEN, LJUST|LONG_HEX, MKSTR(si->cache))); @@ -17779,17 +18025,28 @@ space(spacelen <= 0 ? 1 : spacelen), si->objsize); if (spacelen > 0) spacelen = 1; - sprintf(b1, "%c%dld ", '%', 9 + spacelen - 1); + if (si->flags & SLAB_GATHER_FAILURE) + sprintf(b1, "%c%ds ", '%', 9 + spacelen - 1); + else + sprintf(b1, "%c%dld ", '%', 9 + spacelen - 1); } else { fprintf(fp, "%-18s %8ld ", si->curname, si->objsize); - sprintf(b1, "%c%dld ", '%', 9); + if (si->flags & SLAB_GATHER_FAILURE) + sprintf(b1, "%c%ds ", '%', 9); + else + sprintf(b1, "%c%dld ", '%', 9); } - fprintf(fp, b1, si->inuse); - - fprintf(fp, "%8ld %5ld %4ldk\n", - si->num_slabs * si->objects, - si->num_slabs, si->slabsize/1024); + if (si->flags & SLAB_GATHER_FAILURE) { + fprintf(fp, b1, "?"); + fprintf(fp, "%8s %5s %4ldk\n", + "?", "?", si->slabsize/1024); + } else { + fprintf(fp, b1, si->inuse); + fprintf(fp, "%8ld %5ld %4ldk\n", + si->inuse + si->free, + si->num_slabs, si->slabsize/1024); + } } static void @@ -17885,12 +18142,20 @@ si->slabsize = (PAGESIZE() << order); si->inuse = si->num_slabs = 0; si->slab_offset = offset; + si->random = VALID_MEMBER(kmem_cache_random) ? + ULONG(si->cache_buf + OFFSET(kmem_cache_random)) : 0; + if (!get_kmem_cache_slub_data(GET_SLUB_SLABS, si) || !get_kmem_cache_slub_data(GET_SLUB_OBJECTS, si)) - goto next_cache; + si->flags |= SLAB_GATHER_FAILURE; DUMP_KMEM_CACHE_INFO_SLUB(); + if (si->flags & SLAB_GATHER_FAILURE) { + si->flags &= ~SLAB_GATHER_FAILURE; + goto next_cache; + } + if (si->flags & ADDRESS_SPECIFIED) { if (!si->slab) si->slab = vaddr_to_slab(si->spec_addr); @@ -17911,6 +18176,94 @@ FREEBUF(si->cache_buf); } +static ushort +slub_page_objects(struct meminfo *si, ulong page) +{ + ulong objects_vaddr; + ushort objects; + + /* + * Pre-2.6.27, the object count and order were fixed in the + * kmem_cache structure. Now they may change, say if a high + * order slab allocation fails, so the per-slab object count + * is kept in the slab. + */ + if (VALID_MEMBER(page_objects)) { + objects_vaddr = page + OFFSET(page_objects); + if (si->flags & SLAB_BITFIELD) + objects_vaddr += sizeof(ushort); + if (!readmem(objects_vaddr, KVADDR, &objects, + sizeof(ushort), "page.objects", RETURN_ON_ERROR)) + return 0; + /* + * Strip page.frozen bit. + */ + if (si->flags & SLAB_BITFIELD) { + if (__BYTE_ORDER == __LITTLE_ENDIAN) { + objects <<= 1; + objects >>= 1; + } + if (__BYTE_ORDER == __BIG_ENDIAN) + objects >>= 1; + } + + if (CRASHDEBUG(1) && (objects != si->objects)) + error(NOTE, "%s: slab: %lx oo objects: %ld " + "slab objects: %d\n", + si->curname, page, + si->objects, objects); + + if (objects == (ushort)(-1)) { + error(INFO, "%s: slab: %lx invalid page.objects: -1\n", + si->curname, page); + return 0; + } + } else + objects = (ushort)si->objects; + + return objects; +} + +static short +count_cpu_partial(struct meminfo *si, int cpu) +{ + short cpu_partial_inuse, cpu_partial_objects, free_objects; + ulong cpu_partial; + + free_objects = 0; + + if (VALID_MEMBER(kmem_cache_cpu_partial) && VALID_MEMBER(page_objects)) { + readmem(ULONG(si->cache_buf + OFFSET(kmem_cache_cpu_slab)) + + kt->__per_cpu_offset[cpu] + OFFSET(kmem_cache_cpu_partial), + KVADDR, &cpu_partial, sizeof(ulong), + "kmem_cache_cpu.partial", RETURN_ON_ERROR); + + while (cpu_partial) { + if (!is_page_ptr(cpu_partial, NULL)) { + error(INFO, "%s: invalid partial list slab pointer: %lx\n", + si->curname, cpu_partial); + return 0; + } + if (!readmem(cpu_partial + OFFSET(page_inuse), KVADDR, &cpu_partial_inuse, + sizeof(ushort), "page.inuse", RETURN_ON_ERROR)) + return 0; + if (cpu_partial_inuse == -1) + return 0; + + cpu_partial_objects = slub_page_objects(si, + cpu_partial); + if (!cpu_partial_objects) + return 0; + free_objects += cpu_partial_objects - cpu_partial_inuse; + + readmem(cpu_partial + OFFSET(page_next), KVADDR, + &cpu_partial, sizeof(ulong), "page.next", + RETURN_ON_ERROR); + } + } + return free_objects; +} + /* * Emulate the total count calculation done by the * slab_objects() sysfs function in slub.c. @@ -17919,10 +18272,10 @@ get_kmem_cache_slub_data(long cmd, struct meminfo *si) { int i, n, node; - ulong total_objects, total_slabs; - ulong cpu_slab_ptr, node_ptr; - ulong node_nr_partial, node_nr_slabs; - int full_slabs, objects; + ulong total_objects, total_slabs, free_objects; + ulong cpu_slab_ptr, node_ptr, cpu_freelist, orig_slab; + ulong node_nr_partial, node_nr_slabs, node_total_objects; + int full_slabs, objects, node_total_avail; long p; short inuse; ulong *nodes, *per_cpu; @@ -17935,10 +18288,11 @@ nodes = (ulong *)GETBUF(2 * sizeof(ulong) * vt->numnodes); per_cpu = nodes + vt->numnodes; - total_slabs = total_objects = 0; + total_slabs = total_objects = free_objects = cpu_freelist = 0; + node_total_avail = VALID_MEMBER(kmem_cache_node_total_objects) ? TRUE : FALSE; for (i = 0; i < kt->cpus; i++) { - cpu_slab_ptr = get_cpu_slab_ptr(si, i, NULL); + cpu_slab_ptr = get_cpu_slab_ptr(si, i, &cpu_freelist); if (!cpu_slab_ptr) continue; @@ -17948,12 +18302,33 @@ switch (cmd) { - case GET_SLUB_OBJECTS: + case GET_SLUB_OBJECTS: { + /* For better error report, set cur slab to si->slab. */ + orig_slab = si->slab; + si->slab = cpu_slab_ptr; + if (!readmem(cpu_slab_ptr + OFFSET(page_inuse), - KVADDR, &inuse, sizeof(short), - "page inuse", RETURN_ON_ERROR)) + KVADDR, &inuse, sizeof(short), + "page inuse", RETURN_ON_ERROR)) { + si->slab = orig_slab; return FALSE; - total_objects += inuse; + } + objects = slub_page_objects(si, cpu_slab_ptr); + if (!objects) { + si->slab = orig_slab; + return FALSE; + } + + free_objects += objects - inuse; + free_objects += count_free_objects(si, cpu_freelist); + free_objects += count_cpu_partial(si, i); + + if (!node_total_avail) + total_objects += inuse; + total_slabs++; + + si->slab = orig_slab; + } break; case GET_SLUB_SLABS: @@ -17984,13 +18359,21 @@ KVADDR, &node_nr_slabs, sizeof(ulong), "kmem_cache_node nr_slabs", RETURN_ON_ERROR)) goto bailout; + if (node_total_avail) { + if (!readmem(node_ptr + OFFSET(kmem_cache_node_total_objects), + KVADDR, &node_total_objects, sizeof(ulong), + "kmem_cache_node total_objects", RETURN_ON_ERROR)) + goto bailout; + } switch (cmd) { case GET_SLUB_OBJECTS: - if ((p = count_partial(node_ptr, si)) < 0) + if ((p = count_partial(node_ptr, si, &free_objects)) < 0) return FALSE; - total_objects += p; + if (!node_total_avail) + total_objects += p; + total_slabs += node_nr_partial; break; case GET_SLUB_SLABS: @@ -18004,7 +18387,11 @@ switch (cmd) { case GET_SLUB_OBJECTS: - total_objects += (full_slabs * objects); + if (node_total_avail) + total_objects += node_total_objects; + else + total_objects += (full_slabs * objects); + total_slabs += full_slabs; break; case GET_SLUB_SLABS: @@ -18019,7 +18406,14 @@ switch (cmd) { case GET_SLUB_OBJECTS: - si->inuse = total_objects; + if (!node_total_avail) + si->inuse = total_objects; + else + si->inuse = total_objects - free_objects; + if (VALID_MEMBER(page_objects) && node_total_avail) + si->free = free_objects; + else + si->free = (total_slabs * si->objects) - si->inuse; break; case GET_SLUB_SLABS: @@ -18166,11 +18560,13 @@ do_slab_slub(struct meminfo *si, int verbose) { physaddr_t paddr; - ulong vaddr, objects_vaddr; + ulong vaddr; ushort inuse, objects; ulong freelist, cpu_freelist, cpu_slab_ptr; int i, free_objects, cpu_slab, is_free, node; ulong p, q; +#define SLAB_RED_ZONE 0x00000400UL + ulong flags, red_left_pad; if (!si->slab) { if (CRASHDEBUG(1)) @@ -18198,50 +18594,17 @@ if (!readmem(si->slab + OFFSET(page_freelist), KVADDR, &freelist, sizeof(void *), "page.freelist", RETURN_ON_ERROR)) return FALSE; - /* - * Pre-2.6.27, the object count and order were fixed in the - * kmem_cache structure. Now they may change, say if a high - * order slab allocation fails, so the per-slab object count - * is kept in the slab. - */ - if (VALID_MEMBER(page_objects)) { - objects_vaddr = si->slab + OFFSET(page_objects); - if (si->flags & SLAB_BITFIELD) - objects_vaddr += sizeof(ushort); - if (!readmem(objects_vaddr, KVADDR, &objects, - sizeof(ushort), "page.objects", RETURN_ON_ERROR)) - return FALSE; - /* - * Strip page.frozen bit. - */ - if (si->flags & SLAB_BITFIELD) { - if (__BYTE_ORDER == __LITTLE_ENDIAN) { - objects <<= 1; - objects >>= 1; - } - if (__BYTE_ORDER == __BIG_ENDIAN) - objects >>= 1; - } - if (CRASHDEBUG(1) && (objects != si->objects)) - error(NOTE, "%s: slab: %lx oo objects: %ld " - "slab objects: %d\n", - si->curname, si->slab, - si->objects, objects); - - if (objects == (ushort)(-1)) { - error(INFO, "%s: slab: %lx invalid page.objects: -1\n", - si->curname, si->slab); - return FALSE; - } - } else - objects = (ushort)si->objects; + objects = slub_page_objects(si, si->slab); + if (!objects) + return FALSE; if (!verbose) { DUMP_SLAB_INFO_SLUB(); return TRUE; } + cpu_freelist = 0; for (i = 0, cpu_slab = -1; i < kt->cpus; i++) { cpu_slab_ptr = get_cpu_slab_ptr(si, i, &cpu_freelist); @@ -18253,11 +18616,15 @@ * Later slub scheme uses the per-cpu freelist * so count the free objects by hand. */ - if (cpu_freelist) - freelist = cpu_freelist; - if ((free_objects = count_free_objects(si, freelist)) < 0) + if ((free_objects = count_free_objects(si, cpu_freelist)) < 0) return FALSE; - inuse = si->objects - free_objects; + /* + * If the object is freed on foreign cpu, the + * object is liked to page->freelist. + */ + if (freelist) + free_objects += objects - inuse; + inuse = objects - free_objects; break; } } @@ -18285,31 +18652,41 @@ fprintf(fp, "< SLUB: free list END (%d found) >\n", i); } + red_left_pad = 0; + if (VALID_MEMBER(kmem_cache_red_left_pad)) { + flags = ULONG(si->cache_buf + OFFSET(kmem_cache_flags)); + if (flags & SLAB_RED_ZONE) + red_left_pad = ULONG(si->cache_buf + OFFSET(kmem_cache_red_left_pad)); + } + for (p = vaddr; p < vaddr + objects * si->size; p += si->size) { hq_open(); is_free = FALSE; - for (is_free = 0, q = freelist; q; - q = get_freepointer(si, (void *)q)) { + /* Search an object on both of freelist and cpu_freelist */ + ulong lists[] = { freelist, cpu_freelist, }; + for (i = 0; i < sizeof(lists) / sizeof(lists[0]); i++) { + for (is_free = 0, q = lists[i]; q; + q = get_freepointer(si, (void *)q)) { - if (q == BADADDR) { - hq_close(); - return FALSE; - } - if (q & PAGE_MAPPING_ANON) - break; - if (p == q) { - is_free = TRUE; - break; - } - if (!hq_enter(q)) { - hq_close(); - error(INFO, - "%s: slab: %lx duplicate freelist object: %lx\n", - si->curname, si->slab, q); - return FALSE; + if (q == BADADDR) { + hq_close(); + return FALSE; + } + if (q & PAGE_MAPPING_ANON) + break; + if ((p + red_left_pad) == q) { + is_free = TRUE; + goto found_object; + } + if (!hq_enter(q)) { + hq_close(); + error(INFO, "%s: slab: %lx duplicate freelist object: %lx\n", + si->curname, si->slab, q); + return FALSE; + } } - } + found_object: hq_close(); if (si->flags & ADDRESS_SPECIFIED) { @@ -18322,7 +18699,8 @@ fprintf(fp, " %s%lx%s", is_free ? " " : "[", - p, is_free ? " " : "]"); + pc->flags2 & REDZONE ? p : p + red_left_pad, + is_free ? " " : "]"); if (is_free && (cpu_slab >= 0)) fprintf(fp, "(cpu %d cache)", cpu_slab); fprintf(fp, "\n"); @@ -18354,6 +18732,15 @@ return c; } +static ulong +freelist_ptr(struct meminfo *si, ulong ptr, ulong ptr_addr) +{ + if (si->random) + /* CONFIG_SLAB_FREELIST_HARDENED */ + return (ptr ^ si->random ^ ptr_addr); + else + return ptr; +} static ulong get_freepointer(struct meminfo *si, void *object) @@ -18368,7 +18755,7 @@ return BADADDR; } - return nextfree; + return (freelist_ptr(si, nextfree, vaddr)); } static void @@ -18586,10 +18973,10 @@ } long -count_partial(ulong node, struct meminfo *si) +count_partial(ulong node, struct meminfo *si, ulong *free) { ulong list_head, next, last; - short inuse; + short inuse, objects; ulong total_inuse; ulong count = 0; @@ -18617,6 +19004,16 @@ break; } total_inuse += inuse; + + if (VALID_MEMBER(page_objects)) { + objects = slub_page_objects(si, last); + if (!objects) { + hq_close(); + return -1; + } + *free += objects - inuse; + } + if (!readmem(next, KVADDR, &next, sizeof(ulong), "page.lru.next", RETURN_ON_ERROR)) { hq_close(); diff -Nru crash-7.1.4/memory_driver/crash.c crash-7.2.3+real/memory_driver/crash.c --- crash-7.1.4/memory_driver/crash.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/memory_driver/crash.c 2018-05-17 17:39:38.000000000 +0000 @@ -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,24 @@ 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. + */ + if (probe_kernel_read(buffer, vaddr, count)) { + unmap_virtual(page); + return -EFAULT; + } + if (copy_to_user(buf, buffer, count)) { unmap_virtual(page); return -EFAULT; } @@ -138,10 +199,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 +274,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.4/memory_driver/README crash-7.2.3+real/memory_driver/README --- crash-7.1.4/memory_driver/README 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/memory_driver/README 2018-05-17 17:39:38.000000000 +0000 @@ -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.4/mips.c crash-7.2.3+real/mips.c --- crash-7.1.4/mips.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/mips.c 2018-05-17 17:39:38.000000000 +0000 @@ -28,7 +28,7 @@ #define PGD_T_LOG2 (__builtin_ffs(sizeof(pgd_t)) - 1) #define PTE_T_LOG2 (__builtin_ffs(sizeof(pte_t)) - 1) -#define __PGD_ORDER (32 - 3 * PAGESHIFT() + PGD_T_LOG2 + PTE_T_LOG2) +#define __PGD_ORDER (32 - 3 * (int)PAGESHIFT() + PGD_T_LOG2 + PTE_T_LOG2) #define PGD_ORDER (__PGD_ORDER >= 0 ? __PGD_ORDER : 0) #define PGD_SIZE (PAGESIZE() << PGD_ORDER) @@ -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,180 @@ 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); + continue; + } + + 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 +998,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 +1124,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" : ""); + } +} + +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 */ + -#endif /* MIPS */ diff -Nru crash-7.1.4/net.c crash-7.2.3+real/net.c --- crash-7.1.4/net.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/net.c 2018-05-17 17:39:38.000000000 +0000 @@ -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 @@ -70,7 +70,7 @@ static void print_neighbour_q(ulong, int); static void get_netdev_info(ulong, struct devinfo *); static void get_device_name(ulong, char *); -static void get_device_address(ulong, char *); +static long get_device_address(ulong, char **, long); static void get_sock_info(ulong, char *); static void dump_arp(void); static void arp_state_to_flags(unsigned char); @@ -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"); + } } } @@ -434,7 +441,8 @@ { ulong next; long flen; - char buf[BUFSIZE]; + char *buf; + long buflen = BUFSIZE; if (symbol_exists("dev_base_head")) { show_net_devices_v2(task); @@ -452,6 +460,7 @@ if (!net->netdevice || !next) return; + buf = GETBUF(buflen); flen = MAX(VADDR_PRLEN, strlen(net->netdevice)); fprintf(fp, "%s NAME IP ADDRESS(ES)\n", @@ -465,12 +474,14 @@ get_device_name(next, buf); fprintf(fp, "%-6s ", buf); - get_device_address(next, buf); + buflen = get_device_address(next, &buf, buflen); fprintf(fp, "%s\n", buf); readmem(next+net->dev_next, KVADDR, &next, sizeof(void *), "(net_)device.next", FAULT_ON_ERROR); } while (next); + + FREEBUF(buf); } static void @@ -478,13 +489,15 @@ { struct list_data list_data, *ld; char *net_device_buf; - char buf[BUFSIZE]; + char *buf; + long buflen = BUFSIZE; int ndevcnt, i; long flen; if (!net->netdevice) /* initialized in net_init() */ return; + buf = GETBUF(buflen); flen = MAX(VADDR_PRLEN, strlen(net->netdevice)); fprintf(fp, "%s NAME IP ADDRESS(ES)\n", @@ -514,12 +527,13 @@ get_device_name(ld->list_ptr[i], buf); fprintf(fp, "%-6s ", buf); - get_device_address(ld->list_ptr[i], buf); + buflen = get_device_address(ld->list_ptr[i], &buf, buflen); fprintf(fp, "%s\n", buf); } FREEBUF(ld->list_ptr); FREEBUF(net_device_buf); + FREEBUF(buf); } static void @@ -528,13 +542,15 @@ ulong nsproxy_p, net_ns_p; struct list_data list_data, *ld; char *net_device_buf; - char buf[BUFSIZE]; + char *buf; + long buflen = BUFSIZE; int ndevcnt, i; long flen; if (!net->netdevice) /* initialized in net_init() */ return; + buf = GETBUF(buflen); flen = MAX(VADDR_PRLEN, strlen(net->netdevice)); fprintf(fp, "%s NAME IP ADDRESS(ES)\n", @@ -574,12 +590,13 @@ get_device_name(ld->list_ptr[i], buf); fprintf(fp, "%-6s ", buf); - get_device_address(ld->list_ptr[i], buf); + buflen = get_device_address(ld->list_ptr[i], &buf, buflen); fprintf(fp, "%s\n", buf); } FREEBUF(ld->list_ptr); FREEBUF(net_device_buf); + FREEBUF(buf); } /* @@ -862,19 +879,24 @@ * in_ifaddr->ifa_next points to the next in_ifaddr in the list (if any). * */ -static void -get_device_address(ulong devaddr, char *buf) +static long +get_device_address(ulong devaddr, char **bufp, long buflen) { ulong ip_ptr, ifa_list; struct in_addr ifa_address; - - BZERO(buf, BUFSIZE); + char *buf; + char buf2[BUFSIZE]; + long pos = 0; + + buf = *bufp; + BZERO(buf, buflen); + BZERO(buf2, BUFSIZE); readmem(devaddr + net->dev_ip_ptr, KVADDR, &ip_ptr, sizeof(ulong), "ip_ptr", FAULT_ON_ERROR); if (!ip_ptr) - return; + return buflen; readmem(ip_ptr + OFFSET(in_device_ifa_list), KVADDR, &ifa_list, sizeof(ulong), "ifa_list", FAULT_ON_ERROR); @@ -884,13 +906,20 @@ &ifa_address, sizeof(struct in_addr), "ifa_address", FAULT_ON_ERROR); - sprintf(&buf[strlen(buf)], "%s%s", - strlen(buf) ? ", " : "", - inet_ntoa(ifa_address)); + sprintf(buf2, "%s%s", pos ? ", " : "", inet_ntoa(ifa_address)); + if (pos + strlen(buf2) >= buflen) { + RESIZEBUF(*bufp, buflen, buflen * 2); + buf = *bufp; + BZERO(buf + buflen, buflen); + buflen *= 2; + } + BCOPY(buf2, &buf[pos], strlen(buf2)); + pos += strlen(buf2); readmem(ifa_list + OFFSET(in_ifaddr_ifa_next), KVADDR, &ifa_list, sizeof(ulong), "ifa_next", FAULT_ON_ERROR); } + return buflen; } /* diff -Nru crash-7.1.4/netdump.c crash-7.2.3+real/netdump.c --- crash-7.1.4/netdump.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/netdump.c 2018-05-17 17:39:38.000000000 +0000 @@ -1,7 +1,7 @@ /* netdump.c * - * Copyright (C) 2002-2015 David Anderson - * Copyright (C) 2002-2015 Red Hat, Inc. All rights reserved. + * Copyright (C) 2002-2018 David Anderson + * Copyright (C) 2002-2018 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 @@ -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); @@ -1171,8 +1172,16 @@ netdump_print(" nt_prpsinfo: %lx\n", nd->nt_prpsinfo); netdump_print(" nt_taskstruct: %lx\n", nd->nt_taskstruct); netdump_print(" task_struct: %lx\n", nd->task_struct); - netdump_print(" page_size: %d\n", nd->page_size); + netdump_print(" arch_data: "); + if (nd->arch_data) { + if (machine_type("X86_64")) + netdump_print("%lx (relocate)\n", nd->arch_data); + else if (machine_type("ARM64")) + netdump_print("%lx (kimage_voffset)\n", nd->arch_data); + } else + netdump_print("(unused)\n"); netdump_print(" switch_stack: %lx\n", nd->switch_stack); + netdump_print(" page_size: %d\n", nd->page_size); dump_xen_kdump_data(fp); netdump_print(" num_prstatus_notes: %d\n", nd->num_prstatus_notes); netdump_print(" num_qemu_notes: %d\n", nd->num_qemu_notes); @@ -1768,6 +1777,24 @@ char *vmcoreinfo = (char *)nd->vmcoreinfo; char *value = NULL; + /* + * Borrow this function for ELF vmcores created by the snap.so + * extension module, where arch-specific data may be passed in + * the NT_TASKSTRUCT note. + */ + if ((pc->flags2 & SNAP)) { + if (STREQ(key, "NUMBER(kimage_voffset)") && nd->arch_data) { + value = calloc(VADDR_PRLEN+1, sizeof(char)); + sprintf(value, "%lx", nd->arch_data); + return value; + } + if (STREQ(key, "relocate") && nd->arch_data) { + value = calloc(VADDR_PRLEN+1, sizeof(char)); + sprintf(value, "%lx", nd->arch_data); + return value; + } + } + if (!nd->vmcoreinfo) return NULL; @@ -1911,8 +1938,6 @@ if (store) { nd->nt_taskstruct = (void *)note; nd->task_struct = *((ulong *)(ptr + note->n_namesz)); - nd->switch_stack = *((ulong *) - (ptr + note->n_namesz + sizeof(ulong))); } break; case NT_DISKDUMP: @@ -2159,8 +2184,13 @@ if (store) { nd->nt_taskstruct = (void *)note; nd->task_struct = *((ulong *)(ptr + note->n_namesz)); - nd->switch_stack = *((ulong *) - (ptr + note->n_namesz + sizeof(ulong))); + if (pc->flags2 & SNAP) { + if (note->n_descsz == 16) + nd->arch_data = *((ulong *) + (ptr + note->n_namesz + sizeof(ulong))); + } else if (machine_type("IA64")) + nd->switch_stack = *((ulong *) + (ptr + note->n_namesz + sizeof(ulong))); } break; case NT_DISKDUMP: @@ -2436,7 +2466,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 +2534,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 +2721,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 +2732,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 +3662,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) { @@ -3959,6 +3999,28 @@ return pt_regs; } +int +kdump_phys_base(ulong *phys_base) +{ + if (!kdump_kaslr_check()) + return FALSE; + + *phys_base = nd->phys_base; + + return TRUE; +} + +int +kdump_set_phys_base(ulong phys_base) +{ + if (!kdump_kaslr_check()) + return FALSE; + + nd->phys_base = phys_base; + + return TRUE; +} + /* * In case of ARM we need to determine correct PHYS_OFFSET from the kdump file. * This is done by taking lowest physical address (LMA) from given load @@ -4036,22 +4098,26 @@ Elf64_Phdr *lp64; off_t offset; - if (!machdep->verify_paddr(paddr)) { - if (CRASHDEBUG(1)) - error(INFO, "verify_paddr(%lx) failed\n", paddr); - return READ_ERROR; + if (paddr != KCORE_USE_VADDR) { + if (!machdep->verify_paddr(paddr)) { + if (CRASHDEBUG(1)) + error(INFO, "verify_paddr(%lx) failed\n", paddr); + return READ_ERROR; + } } /* - * Turn the physical address into a unity-mapped kernel - * virtual address, which should work for 64-bit architectures, - * and for lowmem access for 32-bit architectures. + * Unless specified otherwise, turn the physical address into + * a unity-mapped kernel virtual address, which should work + * for 64-bit architectures, and for lowmem access for 32-bit + * architectures. */ - offset = UNINITIALIZED; - if (machine_type("ARM64")) - kvaddr = PTOV((ulong)paddr); + if (paddr == KCORE_USE_VADDR) + kvaddr = addr; else - kvaddr = (ulong)paddr | machdep->kvbase; + kvaddr = PTOV((ulong)paddr); + + offset = UNINITIALIZED; readcnt = cnt; switch (pkd->flags & (KCORE_ELF32|KCORE_ELF64)) @@ -4089,6 +4155,25 @@ break; case KCORE_ELF64: + /* + * If KASLR, the PAGE_OFFSET may be unknown early on, so try + * the (hopefully) mapped kernel address first. + */ + if ((pc->curcmd_flags & MEMTYPE_KVADDR) && (kvaddr != addr)) { + pc->curcmd_flags &= ~MEMTYPE_KVADDR; + for (i = 0; i < pkd->segments; i++) { + lp64 = pkd->load64 + i; + if ((addr >= lp64->p_vaddr) && + (addr < (lp64->p_vaddr + lp64->p_memsz))) { + offset = (off_t)(addr - lp64->p_vaddr) + + (off_t)lp64->p_offset; + break; + } + } + if (offset != UNINITIALIZED) + break; + } + for (i = 0; i < pkd->segments; i++) { lp64 = pkd->load64 + i; if ((kvaddr >= lp64->p_vaddr) && @@ -4458,11 +4543,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)) @@ -4654,3 +4742,38 @@ error: error(WARNING, "failed to init kexec backup region\n"); } + +int +kdump_kaslr_check(void) +{ + if (!QEMU_MEM_DUMP_NO_VMCOREINFO()) + return FALSE; + + /* If vmcore has QEMU note, need to calculate kaslr offset */ + if (nd->num_qemu_notes) + return TRUE; + else + return FALSE; +} + +#ifdef X86_64 +QEMUCPUState * +kdump_get_qemucpustate(int cpu) +{ + if (cpu >= nd->num_qemu_notes) { + if (CRASHDEBUG(1)) + error(INFO, + "Invalid index for QEMU Note: %d (>= %d)\n", + cpu, nd->num_qemu_notes); + return NULL; + } + + if (!nd->elf64 || (nd->elf64->e_machine != EM_X86_64)) { + if (CRASHDEBUG(1)) + error(INFO, "Only x86_64 64bit is supported.\n"); + return NULL; + } + + return (QEMUCPUState *)nd->nt_qemu_percpu[cpu]; +} +#endif diff -Nru crash-7.1.4/netdump.h crash-7.2.3+real/netdump.h --- crash-7.1.4/netdump.h 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/netdump.h 2018-05-17 17:39:38.000000000 +0000 @@ -1,7 +1,7 @@ /* netdump.h * - * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 David Anderson - * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Red Hat, Inc. All rights reserved. + * Copyright (C) 2002-2009, 2017 David Anderson + * Copyright (C) 2002-2009, 2017 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 @@ -77,6 +77,8 @@ ulonglong backup_src_start; ulong backup_src_size; ulonglong backup_offset; + ulong arch_data; + ulong phys_base; }; #define DUMP_ELF_INCOMPLETE 0x1 /* dumpfile is incomplete */ @@ -141,26 +143,3 @@ Elf32_Ehdr *elf32; Elf32_Phdr *load32; }; - -struct QEMUCPUSegment { - uint32_t selector; - uint32_t limit; - uint32_t flags; - uint32_t pad; - uint64_t base; -}; - -typedef struct QEMUCPUSegment QEMUCPUSegment; - -struct QEMUCPUState { - uint32_t version; - uint32_t size; - uint64_t rax, rbx, rcx, rdx, rsi, rdi, rsp, rbp; - uint64_t r8, r9, r10, r11, r12, r13, r14, r15; - uint64_t rip, rflags; - QEMUCPUSegment cs, ds, es, fs, gs, ss; - QEMUCPUSegment ldt, tr, gdt, idt; - uint64_t cr[5]; -}; - -typedef struct QEMUCPUState QEMUCPUState; diff -Nru crash-7.1.4/ppc64.c crash-7.2.3+real/ppc64.c --- crash-7.1.4/ppc64.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/ppc64.c 2018-05-17 17:39:38.000000000 +0000 @@ -1,7 +1,7 @@ /* ppc64.c -- core analysis suite * - * Copyright (C) 2004-2015 David Anderson - * Copyright (C) 2004-2015 Red Hat, Inc. All rights reserved. + * Copyright (C) 2004-2015,2017 David Anderson + * Copyright (C) 2004-2015,2017 Red Hat, Inc. All rights reserved. * Copyright (C) 2004, 2006 Haren Myneni, IBM Corporation * * This program is free software; you can redistribute it and/or modify @@ -15,7 +15,10 @@ * GNU General Public License for more details. */ #ifdef PPC64 + #include "defs.h" +#include +#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 +62,60 @@ 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); +void opalmsg(void); + +static inline int is_hugepage(ulong pte) +{ + 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 ((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)); + } +} static inline uint get_ptetype(ulong pte) { @@ -66,39 +123,67 @@ 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 ulong hugepage_dir(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))) + 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 int is_hugepd(ulong pte) +static inline ulong pgd_page_vaddr_l4(ulong pgd) { - if (THIS_KERNEL_VERSION >= LINUX(3,10,0)) { + ulong pgd_val; + + pgd_val = (pgd & ~machdep->machspec->pgd_masked_bits); + if (machdep->flags & PHYS_ENTRY_L4) { /* - * 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); + * physical address is stored starting from kernel v4.6 + */ + pgd_val = PTOV(pgd_val); + } + + return pgd_val; } -static inline ulong hugepage_dir(ulong pte) +static inline ulong pud_page_vaddr_l4(ulong pud) { - if (THIS_KERNEL_VERSION >= LINUX(3,10,0)) - return (ulong)(pte & ~HUGEPD_SHIFT_MASK); - else - return (ulong)((pte & ~HUGEPD_SHIFT_MASK) | PD_HUGE); + 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 +207,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 +226,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 +266,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 +301,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 +311,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 +340,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 +430,34 @@ } 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; + + if (THIS_KERNEL_VERSION >= LINUX(4,12,0)) { + m->l2_index_size = PMD_INDEX_SIZE_L4_64K_4_12; + m->l3_index_size = PUD_INDEX_SIZE_L4_64K_4_12; + m->l4_index_size = PGD_INDEX_SIZE_L4_64K_4_12; + } else { + 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 +469,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 +537,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 +697,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 +750,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 +794,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 +806,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 +896,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 +904,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 +921,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 +985,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 +1005,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 +1023,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 +1047,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 +1502,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 +1513,10 @@ char *arglist[MAXARGS]; ulong paddr; - paddr = PTOB(pte >> pte_shift); - page_present = (pte & _PAGE_PRESENT); + if (STREQ(pc->curcmd, "pte")) + pte_rpn_shift = machdep->machspec->pte_rpn_shift; + paddr = PTOB(pte >> pte_rpn_shift); + page_present = !!(pte & _PAGE_PRESENT); if (physaddr) { *((ulong *)physaddr) = paddr; @@ -1268,12 +1525,12 @@ sprintf(ptebuf, "%lx", pte); len1 = MAX(strlen(ptebuf), strlen("PTE")); - fprintf(fp, "%s ", mkstring(buf, len1, CENTER|LJUST, "PTE")); if (!page_present && pte) { swap_location(pte, buf); if ((c = parse_line(buf, arglist)) != 3) error(FATAL, "cannot determine swap location\n"); + fprintf(fp, "%s ", mkstring(buf2, len1, CENTER|LJUST, "PTE")); len2 = MAX(strlen(arglist[0]), strlen("SWAP")); len3 = MAX(strlen(arglist[2]), strlen("OFFSET")); @@ -1292,6 +1549,7 @@ return page_present; } + fprintf(fp, "%s ", mkstring(buf, len1, CENTER|LJUST, "PTE")); sprintf(physbuf, "%lx", paddr); len2 = MAX(strlen(physbuf), strlen("PHYSICAL")); fprintf(fp, "%s ", mkstring(buf, len2, CENTER|LJUST, "PHYSICAL")); @@ -1305,6 +1563,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) @@ -1970,66 +2230,11 @@ } /* - * get SP and IP from the saved ptregs. - */ -static int -ppc64_kdump_stack_frame(struct bt_info *bt_in, ulong *nip, ulong *ksp) -{ - struct ppc64_pt_regs *pt_regs; - unsigned long unip; - - pt_regs = (struct ppc64_pt_regs *)bt_in->machdep; - if (!pt_regs || !pt_regs->gpr[1]) { - /* - * Not collected regs. May be the corresponding CPU not - * responded to an IPI. - */ - fprintf(fp, "%0lx: GPR1 register value (SP) was not saved\n", - bt_in->task); - return FALSE; - } - *ksp = pt_regs->gpr[1]; - if (IS_KVADDR(*ksp)) { - readmem(*ksp+16, KVADDR, &unip, sizeof(ulong), "Regs NIP value", - FAULT_ON_ERROR); - *nip = unip; - } else { - if (IN_TASK_VMA(bt_in->task, *ksp)) - fprintf(fp, "%0lx: Task is running in user space\n", - bt_in->task); - else - fprintf(fp, "%0lx: Invalid Stack Pointer %0lx\n", - bt_in->task, *ksp); - *nip = pt_regs->nip; - } - - if (bt_in->flags && - ((BT_TEXT_SYMBOLS|BT_TEXT_SYMBOLS_PRINT|BT_TEXT_SYMBOLS_NOPRINT))) - return TRUE; - - /* - * Print the collected regs for the active task - */ - ppc64_print_regs(pt_regs); - if (!IS_KVADDR(*ksp)) - return FALSE; - - fprintf(fp, " NIP [%016lx] %s\n", pt_regs->nip, - closest_symbol(pt_regs->nip)); - if (unip != pt_regs->link) - fprintf(fp, " LR [%016lx] %s\n", pt_regs->link, - closest_symbol(pt_regs->link)); - - return TRUE; -} - -/* * Get the starting point for the active cpus in a diskdump/netdump. */ static int ppc64_get_dumpfile_stack_frame(struct bt_info *bt_in, ulong *nip, ulong *ksp) { - int panic_task; int i; char *sym; ulong *up; @@ -2042,25 +2247,13 @@ struct ppc64_pt_regs *pt_regs; struct syment *sp; - /* - * For the kdump vmcore, Use SP and IP values that are saved in ptregs. - */ - if (pc->flags & KDUMP) - return ppc64_kdump_stack_frame(bt_in, nip, ksp); - bt = &bt_local; BCOPY(bt_in, bt, sizeof(struct bt_info)); ms = machdep->machspec; - ur_nip = ur_ksp = 0; - - panic_task = tt->panic_task == bt->task ? TRUE : FALSE; check_hardirq = check_softirq = tt->flags & IRQSTACKS ? TRUE : FALSE; - if (panic_task && bt->machdep) { - pt_regs = (struct ppc64_pt_regs *)bt->machdep; - ur_nip = pt_regs->nip; - ur_ksp = pt_regs->gpr[1]; - } else if (bt->task != tt->panic_task) { + + if (bt->task != tt->panic_task) { char cpu_frozen = FALSE; /* * Determine whether the CPU responded to an IPI. @@ -2144,6 +2337,14 @@ *nip = *up; *ksp = bt->stackbase + ((char *)(up) - 16 - bt->stackbuf); + /* + * Check whether this symbol relates to a + * backtrace or not + */ + ur_ksp = *(ulong *)&bt->stackbuf[(*ksp) - bt->stackbase]; + if (!INSTACK(ur_ksp, bt)) + continue; + return TRUE; } } @@ -2179,15 +2380,39 @@ alter_stackbuf(bt); check_intrstack = FALSE; goto retry; - } + } + /* - * We didn't find what we were looking for, so just use what was - * passed in the ELF header. + * We didn't find what we were looking for, so try to use + * the SP and IP values saved in ptregs. */ - if (ur_nip && ur_ksp) { - *nip = ur_nip; - *ksp = ur_ksp; - return TRUE; + pt_regs = (struct ppc64_pt_regs *)bt_in->machdep; + if (!pt_regs || !pt_regs->gpr[1]) { + /* + * Not collected regs. May be the corresponding CPU did not + * respond to an IPI. + */ + if (CRASHDEBUG(1)) + fprintf(fp, "%0lx: GPR1(SP) register value not saved\n", + bt_in->task); + } else { + *ksp = pt_regs->gpr[1]; + if (IS_KVADDR(*ksp)) { + readmem(*ksp+16, KVADDR, nip, sizeof(ulong), + "Regs NIP value", FAULT_ON_ERROR); + ppc64_print_regs(pt_regs); + return TRUE; + } else { + if (IN_TASK_VMA(bt_in->task, *ksp)) + fprintf(fp, "%0lx: Task is running in user space\n", + bt_in->task); + else + fprintf(fp, "%0lx: Invalid Stack Pointer %0lx\n", + bt_in->task, *ksp); + *nip = pt_regs->nip; + ppc64_print_regs(pt_regs); + return FALSE; + } } console("ppc64_get_dumpfile_stack_frame: cannot find SP for panic task\n"); @@ -2527,6 +2752,102 @@ return get_cpus_online(); } + +/* + * Definitions derived from OPAL. These need to track corresponding values in + * https://github.com/open-power/skiboot/blob/master/include/mem-map.h + */ +#define SKIBOOT_CONSOLE_DUMP_START 0x31000000 +#define SKIBOOT_CONSOLE_DUMP_SIZE 0x100000 +#define SKIBOOT_BASE 0x30000000 +#define ASCII_UNLIMITED ((ulong)(-1) >> 1) + +void +opalmsg(void) +{ + struct memloc { + uint8_t u8; + uint16_t u16; + uint32_t u32; + uint64_t u64; + uint64_t limit64; + }; + struct opal { + unsigned long long base; + unsigned long long entry; + } opal; + int i, a; + size_t typesz; + void *location; + char readtype[20]; + struct memloc mem; + int displayed, per_line; + int lost; + ulong error_handle; + long count = SKIBOOT_CONSOLE_DUMP_SIZE; + ulonglong addr = SKIBOOT_CONSOLE_DUMP_START; + + if (CRASHDEBUG(4)) + fprintf(fp, "\n", + addr, count, "PHYSADDR"); + + /* + * OPAL based platform check + * struct opal of BSS section and hence default value will be ZERO(0) + * opal_init() in the kernel initializes this structure based on + * the platform. Use it as a key to determine whether the dump + * was taken on an OPAL based system or not. + */ + if (symbol_exists("opal")) { + get_symbol_data("opal", sizeof(struct opal), &opal); + if (opal.base != SKIBOOT_BASE) + error(FATAL, "dump was captured on non-PowerNV machine"); + } else { + error(FATAL, "dump was captured on non-PowerNV machine"); + } + + BZERO(&mem, sizeof(struct memloc)); + lost = typesz = per_line = 0; + location = NULL; + + /* ASCII */ + typesz = SIZEOF_8BIT; + location = &mem.u8; + sprintf(readtype, "ascii"); + per_line = 256; + displayed = 0; + + error_handle = FAULT_ON_ERROR; + + for (i = a = 0; i < count; i++) { + if (!readmem(addr, PHYSADDR, location, typesz, + readtype, error_handle)) { + addr += typesz; + lost += 1; + continue; + } + + if (isprint(mem.u8)) { + if ((a % per_line) == 0) { + if (displayed && i) + fprintf(fp, "\n"); + } + fprintf(fp, "%c", mem.u8); + displayed++; + a++; + } else { + if (count == ASCII_UNLIMITED) + return; + a = 0; + } + + addr += typesz; + } + + if (lost != count) + fprintf(fp, "\n"); +} + /* * Machine dependent command. */ @@ -2535,7 +2856,7 @@ { int c; - while ((c = getopt(argcnt, args, "cm")) != EOF) { + while ((c = getopt(argcnt, args, "cmo")) != EOF) { switch(c) { case 'c': @@ -2543,6 +2864,8 @@ fprintf(fp, "PPC64: '-%c' option is not supported\n", c); break; + case 'o': + return opalmsg(); default: argerrs++; break; @@ -2963,8 +3286,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.4/qemu-load.c crash-7.2.3+real/qemu-load.c --- crash-7.1.4/qemu-load.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/qemu-load.c 2018-05-17 17:39:38.000000000 +0000 @@ -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.4/qemu-load.h crash-7.2.3+real/qemu-load.h --- crash-7.1.4/qemu-load.h 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/qemu-load.h 2018-05-17 17:39:38.000000000 +0000 @@ -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.4/ramdump.c crash-7.2.3+real/ramdump.c --- crash-7.1.4/ramdump.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/ramdump.c 2018-05-17 17:39:38.000000000 +0000 @@ -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.4/README crash-7.2.3+real/README --- crash-7.1.4/README 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/README 2018-05-17 17:39:38.000000000 +0000 @@ -74,8 +74,8 @@ To build the crash utility: - $ tar -xf crash-7.1.4.tar.gz - $ cd crash-7.1.4 + $ tar -xf crash-7.2.3.tar.gz + $ cd crash-7.2.3 $ 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.4/memory_driver subdirectory. + is included in the crash-7.2.3/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,8 +127,8 @@ $ crash - crash 7.1.4 - Copyright (C) 2002-2015 Red Hat, Inc. + crash 7.2.3 + Copyright (C) 2002-2017 Red Hat, Inc. Copyright (C) 2004, 2005, 2006, 2010 IBM Corporation Copyright (C) 1999-2006 Hewlett-Packard Co Copyright (C) 2005, 2006, 2011, 2012 Fujitsu Limited @@ -152,7 +152,7 @@ KERNEL: /boot/vmlinux DUMPFILE: /dev/mem CPUS: 1 - DATE: Wed Dec 16 10:59:36 2015 + DATE: Thu May 17 13:39:38 2018 UPTIME: 10 days, 22:55:18 LOAD AVERAGE: 0.08, 0.03, 0.01 TASKS: 42 @@ -169,18 +169,18 @@ crash> help - * files mach repeat timer - alias foreach mod runq tree - ascii fuser mount search union + * extend log rd task + alias files mach repeat timer + ascii foreach mod runq tree + bpf fuser mount search union bt gdb net set vm btop help p sig vtop dev ipcs ps struct waitq dis irq pte swap whatis eval kmem ptob sym wr exit list ptov sys q - extend log rd task - crash version: 7.1.4 gdb version: 7.6 + crash version: 7.2.3 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,8 +193,8 @@ $ crash vmlinux vmcore - crash 7.1.4 - Copyright (C) 2002-2015 Red Hat, Inc. + crash 7.2.3 + Copyright (C) 2002-2017 Red Hat, Inc. Copyright (C) 2004, 2005, 2006, 2010 IBM Corporation Copyright (C) 1999-2006 Hewlett-Packard Co Copyright (C) 2005, 2006, 2011, 2012 Fujitsu Limited diff -Nru crash-7.1.4/remote.c crash-7.2.3+real/remote.c --- crash-7.1.4/remote.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/remote.c 2018-05-17 17:39:38.000000000 +0000 @@ -1,8 +1,8 @@ /* remote.c - core analysis suite * * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. - * Copyright (C) 2002, 2003, 2004, 2005, 2009, 2011 David Anderson - * Copyright (C) 2002, 2003, 2004, 2005, 2009, 2011 Red Hat, Inc. All rights reserved. + * Copyright (C) 2002, 2003, 2004, 2005, 2009, 2011, 2018 David Anderson + * Copyright (C) 2002, 2003, 2004, 2005, 2009, 2011, 2018 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 @@ -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", @@ -2759,7 +2759,7 @@ long csum; char sendbuf[BUFSIZE]; char recvbuf[BUFSIZE]; - char readbuf[BUFSIZE]; + char readbuf[BUFSIZE*2]; if (stat(file, &sbuf) < 0) return FALSE; @@ -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; } @@ -3264,7 +3264,7 @@ copy_remote_file(struct remote_file *rfp, int fd, char *file, char *ttystr) { char sendbuf[BUFSIZE]; - char recvbuf[BUFSIZE]; + char recvbuf[BUFSIZE*2]; char readbuf[READBUFSIZE]; char *bufptr; long pct, last; @@ -3399,7 +3399,7 @@ } if (STRNEQ(bufptr, DONEMSG) || STRNEQ(bufptr, DATAMSG)) { - strncpy(gziphdr, bufptr, DATA_HDRSIZE); + BCOPY(bufptr, gziphdr, DATA_HDRSIZE); if (CRASHDEBUG(1)) fprintf(fp, "copy_remote_gzip_file: [%s]\n", @@ -3647,7 +3647,7 @@ } if (STRNEQ(bufptr, DONEMSG) || STRNEQ(bufptr, DATAMSG)) { - strncpy(datahdr, bufptr, DATA_HDRSIZE); + BCOPY(bufptr, datahdr, DATA_HDRSIZE); if (CRASHDEBUG(1)) fprintf(fp, "remote_memory_dump: [%s]\n", @@ -3788,7 +3788,7 @@ remote_execute(void) { char command[BUFSIZE]; - char sendbuf[BUFSIZE]; + char sendbuf[BUFSIZE*2]; char readbuf[READBUFSIZE]; char datahdr[DATA_HDRSIZE]; char *bufptr, *p1; @@ -3833,7 +3833,7 @@ } if (STRNEQ(bufptr, DONEMSG) || STRNEQ(bufptr, DATAMSG)) { - strncpy(datahdr, bufptr, DATA_HDRSIZE); + BCOPY(bufptr, datahdr, DATA_HDRSIZE); if (CRASHDEBUG(1)) fprintf(fp, "remote_execute: [%s]\n", diff -Nru crash-7.1.4/.rh_rpm_package crash-7.2.3+real/.rh_rpm_package --- crash-7.1.4/.rh_rpm_package 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/.rh_rpm_package 2018-05-17 17:39:38.000000000 +0000 @@ -1 +1 @@ -7.1.4 +7.2.3 diff -Nru crash-7.1.4/s390dbf.c crash-7.2.3+real/s390dbf.c --- crash-7.1.4/s390dbf.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/s390dbf.c 2018-05-17 17:39:38.000000000 +0000 @@ -57,6 +57,9 @@ #define KL_PTRSZ 4 #endif +/* Start TOD time of kernel in usecs for relative time stamps */ +static uint64_t tod_clock_base_us; + typedef unsigned long uaddr_t; typedef unsigned long kaddr_t; @@ -162,13 +165,23 @@ return 0; } +/* Time of day clock value for 1970/01/01 */ +#define TOD_UNIX_EPOCH (0x8126d60e46000000LL - (0x3c26700LL * 1000000 * 4096)) +/* Time of day clock value for 1970/01/01 in usecs */ +#define TOD_UNIX_EPOCH_US (TOD_UNIX_EPOCH >> 12) + static inline void kl_s390tod_to_timeval(uint64_t todval, struct timeval *xtime) { - todval -= 0x8126d60e46000000LL - (0x3c26700LL * 1000000 * 4096); + uint64_t todval_us; - todval >>= 12; - xtime->tv_sec = todval / 1000000; - xtime->tv_usec = todval % 1000000; + /* Convert TOD to usec (51th bit of TOD is us) */ + todval_us = todval >> 12; + /* Add base if we have relative time stamps */ + todval_us += tod_clock_base_us; + /* Subtract EPOCH that we get time in usec since 1970 */ + todval_us -= TOD_UNIX_EPOCH_US; + xtime->tv_sec = todval_us / 1000000; + xtime->tv_usec = todval_us % 1000000; } static inline int kl_struct_len(char* struct_name) @@ -846,10 +859,38 @@ return NULL; } +static void tod_clock_base_init(void) +{ + if (kernel_symbol_exists("tod_clock_base")) { + /* + * Kernels >= 4.14 that contain 6e2ef5e4f6cc5734 ("s390/time: + * add support for the TOD clock epoch extension") + */ + get_symbol_data("tod_clock_base", sizeof(tod_clock_base_us), + &tod_clock_base_us); + /* Bit for usecs is at position 59 - therefore shift 4 */ + tod_clock_base_us >>= 4; + } else if (kernel_symbol_exists("sched_clock_base_cc") && + !kernel_symbol_exists("tod_to_timeval")) { + /* + * Kernels >= 4.11 that contain ea417aa8a38bc7db ("s390/debug: + * make debug event time stamps relative to the boot TOD clock") + */ + get_symbol_data("sched_clock_base_cc", + sizeof(tod_clock_base_us), &tod_clock_base_us); + /* Bit for usecs is at position 51 - therefore shift 12 */ + tod_clock_base_us >>= 12; + } else { + /* All older kernels use absolute time stamps */ + tod_clock_base_us = 0; + } +} + static void dbf_init(void) { if (!initialized) { + tod_clock_base_init(); if(dbf_version >= DBF_VERSION_V2) add_lcrash_debug_view(&pages_view); add_lcrash_debug_view(&ascii_view); diff -Nru crash-7.1.4/s390x.c crash-7.2.3+real/s390x.c --- crash-7.1.4/s390x.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/s390x.c 2018-05-17 17:39:38.000000000 +0000 @@ -26,7 +26,8 @@ /* Flags used in entries of page dirs and page tables. */ -#define S390X_PAGE_PRESENT 0x001ULL /* set: loaded in physical memory +#define S390X_PTE_FLAG_BITS 0xfffULL /* Page table entry flag bits */ +#define S390X_PAGE_PRESENT 0x001ULL /* set: loaded in physical memory * clear: not loaded in physical mem */ #define S390X_PAGE_RO 0x200ULL /* HW read-only */ #define S390X_PAGE_INVALID 0x400ULL /* HW invalid */ @@ -46,6 +47,49 @@ #define S390X_PSW_MASK_PSTATE 0x0001000000000000UL /* + * Flags for Region and Segment table entries. + */ +#define S390X_RTE_FLAG_BITS_FC0 0xfffULL +#define S390X_RTE_FLAG_BITS_FC1 0x7fffffffULL +#define S390X_RTE_TL 0x3ULL +#define S390X_RTE_TL_10 0x2ULL +#define S390X_RTE_TL_01 0x1ULL +#define S390X_RTE_TT 0xcULL +#define S390X_RTE_TT_10 0x8ULL +#define S390X_RTE_TT_01 0x4ULL +#define S390X_RTE_CR 0x10ULL +#define S390X_RTE_I 0x20ULL +#define S390X_RTE_TF 0xc0ULL +#define S390X_RTE_TF_10 0x80ULL +#define S390X_RTE_TF_01 0x40ULL +#define S390X_RTE_P 0x200ULL +#define S390X_RTE_FC 0x400ULL +#define S390X_RTE_F 0x800ULL +#define S390X_RTE_ACC 0xf000ULL +#define S390X_RTE_ACC_1000 0x8000ULL +#define S390X_RTE_ACC_0100 0x4000ULL +#define S390X_RTE_ACC_0010 0x2000ULL +#define S390X_RTE_ACC_0001 0x1000ULL +#define S390X_RTE_AV 0x10000ULL + +#define S390X_STE_FLAG_BITS_FC0 0x7ffULL +#define S390X_STE_FLAG_BITS_FC1 0xfffffULL +#define S390X_STE_TT 0xcULL +#define S390X_STE_TT_10 0x8ULL +#define S390X_STE_TT_01 0x4ULL +#define S390X_STE_CS 0x10ULL +#define S390X_STE_I 0x20ULL +#define S390X_STE_P 0x200ULL +#define S390X_STE_FC 0x400ULL +#define S390X_STE_F 0x800ULL +#define S390X_STE_ACC 0xf000ULL +#define S390X_STE_ACC_1000 0x8000ULL +#define S390X_STE_ACC_0100 0x4000ULL +#define S390X_STE_ACC_0010 0x2000ULL +#define S390X_STE_ACC_0001 0x1000ULL +#define S390X_STE_AV 0x10000ULL + +/* * S390x prstatus ELF Note */ struct s390x_nt_prstatus { @@ -202,6 +246,15 @@ if (!kernel_symbol_exists("mem_section")) return TRUE; + /* + * The mem_section was changed to be a pointer in 4.15, so it's + * guaranteed to be a newer kernel. + */ + if (get_symbol_type("mem_section", NULL, NULL) == TYPE_CODE_PTR) { + machdep->max_physmem_bits = _MAX_PHYSMEM_BITS_NEW; + return TRUE; + } + if (!(array_len = get_array_length("mem_section", &dimension, 0))) return FALSE; @@ -611,26 +664,149 @@ * page table traversal functions */ +/* Print flags of Segment-Table entry with format control = 1 */ +static void print_segment_entry_fc1(ulong val) +{ + fprintf(fp, "AV=%u; ACC=%u%u%u%u; F=%u; FC=%u; P=%u; I=%u; CS=%u; TT=%u%u\n", + !!(val & S390X_STE_AV), + !!(val & S390X_STE_ACC_1000), + !!(val & S390X_STE_ACC_0100), + !!(val & S390X_STE_ACC_0010), + !!(val & S390X_STE_ACC_0001), + !!(val & S390X_STE_F), + !!(val & S390X_STE_FC), + !!(val & S390X_STE_P), + !!(val & S390X_STE_I), + !!(val & S390X_STE_CS), + !!(val & S390X_STE_TT_10), + !!(val & S390X_STE_TT_01)); +} + +/* Print flags of Segment-Table entry with format control = 0 */ +static void print_segment_entry_fc0(ulong val) +{ + fprintf(fp, "FC=%u; P=%u; I=%u; CS=%u; TT=%u%u\n", + !!(val & S390X_STE_FC), + !!(val & S390X_STE_P), + !!(val & S390X_STE_I), + !!(val & S390X_STE_CS), + !!(val & S390X_STE_TT_10), + !!(val & S390X_STE_TT_01)); +} + +/* Print flags of Region-Third-Table entry with format control = 1 */ +static void print_region_third_entry_fc1(ulong val) +{ + fprintf(fp, "AV=%u; ACC=%u%u%u%u; F=%u; FC=%u; P=%u; I=%u; CR=%u; TT=%u%u\n", + !!(val & S390X_RTE_AV), + !!(val & S390X_RTE_ACC_1000), + !!(val & S390X_RTE_ACC_0100), + !!(val & S390X_RTE_ACC_0010), + !!(val & S390X_RTE_ACC_0001), + !!(val & S390X_RTE_F), + !!(val & S390X_RTE_FC), + !!(val & S390X_RTE_P), + !!(val & S390X_RTE_I), + !!(val & S390X_RTE_CR), + !!(val & S390X_RTE_TT_10), + !!(val & S390X_RTE_TT_01)); +} + +/* Print flags of Region-Third-Table entry with format control = 0 */ +static void print_region_third_entry_fc0(ulong val) +{ + fprintf(fp, "FC=%u; P=%u; TF=%u%u; I=%u; CR=%u; TT=%u%u; TL=%u%u\n", + !!(val & S390X_RTE_FC), + !!(val & S390X_RTE_P), + !!(val & S390X_RTE_TF_10), + !!(val & S390X_RTE_TF_01), + !!(val & S390X_RTE_I), + !!(val & S390X_RTE_CR), + !!(val & S390X_RTE_TT_10), + !!(val & S390X_RTE_TT_01), + !!(val & S390X_RTE_TL_10), + !!(val & S390X_RTE_TL_01)); +} + +/* Print flags of Region-First/Second-Table entry */ +static void print_region_first_second_entry(ulong val) +{ + fprintf(fp, "P=%u; TF=%u%u; I=%u; TT=%u%u; TL=%u%u\n", + !!(val & S390X_RTE_P), + !!(val & S390X_RTE_TF_10), + !!(val & S390X_RTE_TF_01), + !!(val & S390X_RTE_I), + !!(val & S390X_RTE_TT_10), + !!(val & S390X_RTE_TT_01), + !!(val & S390X_RTE_TL_10), + !!(val & S390X_RTE_TL_01)); +} + +/* Print the binary flags for Region or Segment table entry */ +static void s390x_print_te_binary_flags(ulong val, int level) +{ + fprintf(fp, " flags in binary : "); + switch (level) { + case 0: + if (val & S390X_STE_FC) + print_segment_entry_fc1(val); + else + print_segment_entry_fc0(val); + break; + case 1: + if (val & S390X_RTE_FC) + print_region_third_entry_fc1(val); + else + print_region_third_entry_fc0(val); + break; + case 2: + case 3: + print_region_first_second_entry(val); + break; + } +} + /* Region or segment table traversal function */ static ulong _kl_rsg_table_deref_s390x(ulong vaddr, ulong table, - int len, int level) + int len, int level, int verbose) { - ulong offset, entry; + const char *name_vec[] = {"STE", "RTTE", "RSTE", "RFTE"}; + ulong offset, entry, flags, addr; + int flags_prt_len; offset = ((vaddr >> (11*level + 20)) & 0x7ffULL) * 8; if (offset >= (len + 1)*4096) /* Offset is over the table limit. */ return 0; - readmem(table + offset, KVADDR, &entry, sizeof(entry), "entry", - FAULT_ON_ERROR); + addr = table + offset; + readmem(addr, KVADDR, &entry, sizeof(entry), "entry", FAULT_ON_ERROR); + if (verbose) { + flags_prt_len = 3; + if (entry & S390X_RTE_FC) + if (level) { + flags = entry & S390X_RTE_FLAG_BITS_FC1; + flags_prt_len = 8; + } else { + flags = entry & S390X_STE_FLAG_BITS_FC1; + flags_prt_len = 5; + } + else + if (level) + flags = entry & S390X_RTE_FLAG_BITS_FC0; + else + flags = entry & S390X_STE_FLAG_BITS_FC0; + fprintf(fp, "%5s: %016lx => %016lx (flags = %0*lx)\n", + name_vec[level], addr, entry, flags_prt_len, flags); + s390x_print_te_binary_flags(entry, level); + } /* * Check if the segment table entry could be read and doesn't have * any of the reserved bits set. */ - if ((entry & 0xcULL) != (level << 2)) + if ((entry & S390X_RTE_TT) != (level << 2)) return 0; /* Check if the region table entry has the invalid bit set. */ - if (entry & 0x20ULL) + if (entry & S390X_RTE_I) return 0; /* Region table entry is valid and well formed. */ return entry; @@ -653,13 +829,20 @@ } /* Page table traversal function */ -static ulong _kl_pg_table_deref_s390x(ulong vaddr, ulong table) +static ulong _kl_pg_table_deref_s390x(ulong vaddr, ulong table, int verbose) { - ulong offset, entry; + ulong offset, entry, addr; offset = ((vaddr >> 12) & 0xffULL) * 8; - readmem(table + offset, KVADDR, &entry, sizeof(entry), "entry", - FAULT_ON_ERROR); + addr = table + offset; + readmem(addr, KVADDR, &entry, sizeof(entry), "entry", FAULT_ON_ERROR); + if (verbose) { + fprintf(fp, "%5s: %016lx => %016lx (flags = %03llx)\n", + "PTE", addr, entry, entry & S390X_PTE_FLAG_BITS); + fprintf(fp, " flags in binary : I=%u; P=%u\n", + !!(entry & S390X_PAGE_INVALID), !!(entry & S390X_PAGE_RO)); + fprintf(fp, "%5s: %016llx\n", "PAGE", entry & ~S390X_PTE_FLAG_BITS); + } /* * Return zero if the page table entry has the reserved (0x800) or * the invalid (0x400) bit set and it is not a swap entry. @@ -676,6 +859,9 @@ ulong entry, paddr; int level, len; + if (verbose) + fprintf(fp, "PAGE DIRECTORY: %016lx\n", table); + *phys_addr = 0; /* * Walk the region and segment tables. @@ -688,12 +874,13 @@ /* Read the first entry to find the number of page table levels. */ readmem(table, KVADDR, &entry, sizeof(entry), "entry", FAULT_ON_ERROR); level = (entry & 0xcULL) >> 2; - if ((vaddr >> (31 + 11*level)) != 0ULL) { + if ((level < 3) && (vaddr >> (31 + 11*level)) != 0ULL) { /* Address too big for the number of page table levels. */ return FALSE; } while (level >= 0) { - entry = _kl_rsg_table_deref_s390x(vaddr, table, len, level); + entry = _kl_rsg_table_deref_s390x(vaddr, table, len, level, + verbose); if (!entry) return FALSE; table = entry & ~0xfffULL; @@ -717,7 +904,7 @@ } /* Get the page table entry */ - entry = _kl_pg_table_deref_s390x(vaddr, entry & ~0x7ffULL); + entry = _kl_pg_table_deref_s390x(vaddr, entry & ~0x7ffULL, verbose); if (!entry) return FALSE; @@ -1304,8 +1491,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.4/sadump.c crash-7.2.3+real/sadump.c --- crash-7.1.4/sadump.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/sadump.c 2018-05-17 17:39:38.000000000 +0000 @@ -157,9 +157,6 @@ } restart: - if (block_size < 0) - return FALSE; - if (!read_device(sph, block_size, &offset)) { error(INFO, "sadump: cannot read partition header\n"); goto err; @@ -394,7 +391,13 @@ } sd->filename = file; - sd->flags = flags; + + /* + * Switch to zero excluded mode by default on sadump-related + * formats because some Fujitsu troubleshooting software + * assumes the behavior. + */ + sd->flags = flags | SADUMP_ZERO_EXCLUDED; if (machine_type("X86")) sd->machine_type = EM_386; @@ -1552,15 +1555,28 @@ */ int sadump_phys_base(ulong *phys_base) { - if (SADUMP_VALID()) { + if (SADUMP_VALID() && !sd->phys_base) { if (CRASHDEBUG(1)) error(NOTE, "sadump: does not save phys_base.\n"); return FALSE; } + if (sd->phys_base) { + *phys_base = sd->phys_base; + return TRUE; + } + return FALSE; } +int +sadump_set_phys_base(ulong phys_base) +{ + sd->phys_base = phys_base; + + return TRUE; +} + /* * Used by "sys" command to show diskset disk names. */ @@ -1643,3 +1659,47 @@ { return sd; } + +#ifdef X86_64 +static int +get_sadump_smram_cpu_state_any(struct sadump_smram_cpu_state *smram) +{ + ulong offset; + struct sadump_header *sh = sd->dump_header; + int apicid; + struct sadump_smram_cpu_state scs, zero; + + offset = sd->sub_hdr_offset + sizeof(uint32_t) + + sd->dump_header->nr_cpus * sizeof(struct sadump_apic_state); + + memset(&zero, 0, sizeof(zero)); + + for (apicid = 0; apicid < sh->nr_cpus; ++apicid) { + if (!read_device(&scs, sizeof(scs), &offset)) { + error(INFO, "sadump: cannot read sub header " + "cpu_state\n"); + return FALSE; + } + if (memcmp(&scs, &zero, sizeof(scs)) != 0) { + *smram = scs; + return TRUE; + } + } + + return FALSE; +} + +int +sadump_get_cr3_idtr(ulong *cr3, ulong *idtr) +{ + struct sadump_smram_cpu_state scs; + + memset(&scs, 0, sizeof(scs)); + get_sadump_smram_cpu_state_any(&scs); + + *cr3 = scs.Cr3; + *idtr = ((uint64_t)scs.IdtUpper)<<32 | (uint64_t)scs.IdtLower; + + return TRUE; +} +#endif /* X86_64 */ diff -Nru crash-7.1.4/sadump.h crash-7.2.3+real/sadump.h --- crash-7.1.4/sadump.h 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/sadump.h 2018-05-17 17:39:38.000000000 +0000 @@ -219,6 +219,7 @@ ulonglong backup_offset; uint64_t max_mapnr; + ulong phys_base; }; struct sadump_data *sadump_get_sadump_data(void); diff -Nru crash-7.1.4/sparc64.c crash-7.2.3+real/sparc64.c --- crash-7.1.4/sparc64.c 1970-01-01 00:00:00.000000000 +0000 +++ crash-7.2.3+real/sparc64.c 2018-05-17 17:39:38.000000000 +0000 @@ -0,0 +1,1253 @@ +/* sparc64.c - core analysis suite + * + * Copyright (C) 2016 Oracle Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifdef SPARC64 + +#include "defs.h" +#include +#include +#include +#include + +/* TT (Trap Type) is encoded into magic pt_regs field */ +#define MAGIC_TT_MASK (0x1ff) + +static const unsigned long not_valid_pte = ~0UL; +static struct machine_specific sparc64_machine_specific; +static unsigned long sparc64_ksp_offset; + +static unsigned long +__va(unsigned long paddr) +{ + return paddr + PAGE_OFFSET; +} + +static unsigned long +__pa(unsigned long vaddr) +{ + return vaddr - PAGE_OFFSET; +} + +static void +sparc64_parse_cmdline_args(void) +{ +} + +/* This interface might not be required. */ +static void +sparc64_clear_machdep_cache(void) +{ +} + +/* + * "mach" command output. + */ +static void +sparc64_display_machine_stats(void) +{ + int c; + struct new_utsname *uts; + char buf[BUFSIZE]; + ulong mhz; + + uts = &kt->utsname; + + fprintf(fp, " MACHINE TYPE: %s\n", uts->machine); + fprintf(fp, " MEMORY SIZE: %s\n", get_memory_size(buf)); + fprintf(fp, " CPUS: %d\n", kt->cpus); + fprintf(fp, " PROCESSOR SPEED: "); + if ((mhz = machdep->processor_speed())) + fprintf(fp, "%ld Mhz\n", mhz); + else + fprintf(fp, "(unknown)\n"); + fprintf(fp, " HZ: %d\n", machdep->hz); + fprintf(fp, " PAGE SIZE: %ld\n", PAGE_SIZE); + fprintf(fp, " KERNEL VIRTUAL BASE: %lx\n", machdep->kvbase); + fprintf(fp, " KERNEL VMALLOC BASE: %lx\n", SPARC64_VMALLOC_START); + fprintf(fp, " KERNEL MODULES BASE: %lx\n", SPARC64_MODULES_VADDR); + fprintf(fp, " KERNEL STACK SIZE: %ld\n", STACKSIZE()); + + fprintf(fp, "HARD IRQ STACK SIZE: %ld\n", THREAD_SIZE); + fprintf(fp, " HARD IRQ STACKS:\n"); + + for (c = 0; c < kt->cpus; c++) { + if (!tt->hardirq_ctx[c]) + continue; + sprintf(buf, "CPU %d", c); + fprintf(fp, "%19s: %lx\n", buf, tt->hardirq_ctx[c]); + } + + fprintf(fp, "SOFT IRQ STACK SIZE: %ld\n", THREAD_SIZE); + fprintf(fp, " SOFT IRQ STACKS:\n"); + for (c = 0; c < kt->cpus; c++) { + if (!tt->softirq_ctx[c]) + continue; + sprintf(buf, "CPU %d", c); + fprintf(fp, "%19s: %lx\n", buf, tt->softirq_ctx[c]); + } +} + +static void +sparc64_display_memmap(void) +{ + unsigned long iomem_resource; + unsigned long resource; + unsigned long start, end, nameptr; + int size = STRUCT_SIZE("resource"); + char *buf; + char name[32]; + + buf = GETBUF(size); + iomem_resource = symbol_value("iomem_resource"); + + readmem(iomem_resource + MEMBER_OFFSET("resource", "child"), KVADDR, + &resource, sizeof(resource), "iomem_resource", FAULT_ON_ERROR); + + fprintf(fp, " PHYSICAL ADDRESS RANGE TYPE\n"); + + while (resource) { + readmem(resource, KVADDR, buf, size, "resource", + FAULT_ON_ERROR); + start = ULONG(buf + MEMBER_OFFSET("resource", "start")); + end = ULONG(buf + MEMBER_OFFSET("resource", "end")); + nameptr = ULONG(buf + MEMBER_OFFSET("resource", "name")); + + readmem(nameptr, KVADDR, name, sizeof(name), "resource.name", + FAULT_ON_ERROR); + + fprintf(fp, "%016lx - %016lx %-32s\n", start, end, name); + + resource = ULONG(buf + MEMBER_OFFSET("resource", "sibling")); + } + FREEBUF(buf); +} + +static void +sparc64_cmd_mach(void) +{ + int c; + int mflag = 0; + + while ((c = getopt(argcnt, args, "cdmx")) != EOF) { + switch (c) { + case 'm': + mflag++; + sparc64_display_memmap(); + break; + case 'c': + fprintf(fp, "SPARC64: '-%c' option is not supported\n", + c); + return; + case 'd': + case 'x': + /* Just ignore these */ + break; + default: + argerrs++; + break; + } + } + + if (argerrs) + cmd_usage(pc->curcmd, SYNOPSIS); + + if (!mflag) + sparc64_display_machine_stats(); +} + +struct sparc64_mem_ranges { + unsigned long start; + unsigned long end; +}; + +#define NR_PHYS_RANGES (128) +static unsigned int nr_phys_ranges; +struct sparc64_mem_ranges phys_ranges[NR_PHYS_RANGES]; + +#define NR_IMAGE_RANGES (16) +static unsigned int nr_kimage_ranges; +struct sparc64_mem_ranges kimage_ranges[NR_IMAGE_RANGES]; + +/* There are three live cases: + * one) normal kernel + * two) --load-panic kernel + * and + * three) --load kernel + * One and two can be treated the same because the kernel is physically + * contiguous. Three isn't contiguous. The kernel is allocated in order + * nine allocation pages. We don't handle case three yet. + */ + +static int +sparc64_phys_live_valid(unsigned long paddr) +{ + unsigned int nr; + int rc = FALSE; + + for (nr = 0; nr != nr_phys_ranges; nr++) { + if (paddr >= phys_ranges[nr].start && + paddr < phys_ranges[nr].end) { + rc = TRUE; + break; + } + } + return rc; +} + +static int +sparc64_phys_kdump_valid(unsigned long paddr) +{ + return TRUE; +} + +static int +sparc64_verify_paddr(unsigned long paddr) +{ + int rc; + + if (ACTIVE()) + rc = sparc64_phys_live_valid(paddr); + else + rc = sparc64_phys_kdump_valid(paddr); + + return rc; +} + +static void +sparc6_phys_base_live_limits(void) +{ + if (nr_phys_ranges >= NR_PHYS_RANGES) + error(FATAL, "sparc6_phys_base_live_limits: " + "NR_PHYS_RANGES exceeded.\n"); + else if (nr_kimage_ranges >= NR_IMAGE_RANGES) + error(FATAL, "sparc6_phys_base_live_limits: " + "NR_IMAGE_RANGES exceeded.\n"); +} + +static void +sparc64_phys_base_live_valid(void) +{ + if (!nr_phys_ranges) + error(FATAL, "No physical memory ranges."); + else if (!nr_kimage_ranges) + error(FATAL, "No vmlinux memory ranges."); +} + +static void +sparc64_phys_base_live(void) +{ + char line[BUFSIZE]; + FILE *fp; + + fp = fopen("/proc/iomem", "r"); + if (fp == NULL) + error(FATAL, "Can't open /proc/iomem. We can't proceed."); + + while (fgets(line, sizeof(line), fp) != 0) { + unsigned long start, end; + int count, consumed; + char *ch; + + sparc6_phys_base_live_limits(); + count = sscanf(line, "%lx-%lx : %n", &start, &end, &consumed); + if (count != 2) + continue; + ch = line + consumed; + if (memcmp(ch, "System RAM\n", 11) == 0) { + end = end + 1; + phys_ranges[nr_phys_ranges].start = start; + phys_ranges[nr_phys_ranges].end = end; + nr_phys_ranges++; + } else if ((memcmp(ch, "Kernel code\n", 12) == 0) || + (memcmp(ch, "Kernel data\n", 12) == 0) || + (memcmp(ch, "Kernel bss\n", 11) == 0)) { + kimage_ranges[nr_kimage_ranges].start = start; + kimage_ranges[nr_kimage_ranges].end = end; + nr_kimage_ranges++; + } + } + + (void) fclose(fp); + sparc64_phys_base_live_valid(); +} + +static void +sparc64_phys_base_kdump(void) +{ +} + +static void +sparc64_phys_base(void) +{ + if (ACTIVE()) + return sparc64_phys_base_live(); + else + return sparc64_phys_base_kdump(); +} + +static unsigned long kva_start, kva_end; +static unsigned long kpa_start, kpa_end; + +static void +sparc64_kimage_limits_live(void) +{ + kpa_start = kimage_ranges[0].start; + kpa_end = kpa_start + (kva_end - kva_start); +} + +static void +sparc64_kimage_limits_kdump(void) +{ + unsigned long phys_base; + + if (DISKDUMP_DUMPFILE()) { + if (diskdump_phys_base(&phys_base)) { + kpa_start = phys_base | (kva_start & 0xffff); + kpa_end = kpa_start + (kva_end - kva_start); + return; + } + } + fprintf(stderr, "Can't determine phys_base\n"); +} + +static unsigned long +kimage_va_translate(unsigned long addr) +{ + unsigned long paddr = (addr - kva_start) + kpa_start; + + return paddr; +} + +static int +kimage_va_range(unsigned long addr) +{ + if (addr >= kva_start && addr < kva_end) + return TRUE; + else + return FALSE; +} + +static void +sparc64_kimage_limits(void) +{ + kva_start = symbol_value("_stext"); + kva_end = symbol_value("_end"); + + if (ACTIVE()) + sparc64_kimage_limits_live(); + else + sparc64_kimage_limits_kdump(); +} + +static int +sparc64_is_linear_mapped(unsigned long vaddr) +{ + return (vaddr & PAGE_OFFSET) == PAGE_OFFSET; +} + +static unsigned long +pte_to_pa(unsigned long pte) +{ + unsigned long paddr = pte & _PAGE_PFN_MASK; + + return paddr; +} + +static unsigned long +fetch_page_table_level(unsigned long pte_kva, unsigned long vaddr, + unsigned int shift, unsigned int mask, const char *name, + int verbose) +{ + unsigned int pte_index = (vaddr >> shift) & mask; + unsigned long page_table[PTES_PER_PAGE]; + unsigned long pte = 0UL; + int rc; + + rc = readmem(pte_kva, KVADDR, page_table, sizeof(page_table), + (char *)name, RETURN_ON_ERROR); + if (!rc) + goto out; + pte = page_table[pte_index]; + if (verbose) + fprintf(fp, + "%s(0x%.16lx) fetch of pte @index[0x%.4x]=0x%.16lx\n", + name, pte_kva, pte_index, pte); +out: + return pte; +} + +static unsigned long +pmd_is_huge(unsigned long pmd, unsigned long vaddr, int verbose) +{ + unsigned long hpage_mask; + unsigned long paddr = 0UL; + + if ((pmd & PAGE_PMD_HUGE) == 0UL) + goto out; + hpage_mask = ~((1UL << HPAGE_SHIFT) - 1UL); + paddr = pte_to_pa(pmd) + (vaddr & ~hpage_mask); + if (verbose) + fprintf(fp, "Huge Page/THP pmd=0x%.16lx paddr=0x%.16lx\n", + pmd, paddr); +out: + return paddr; +} + +static unsigned long +sparc64_page_table_walk(unsigned long pgd, unsigned long vaddr, int verbose) +{ + static const char *pgd_text = "pgd fetch"; + static const char *pud_text = "pud fetch"; + static const char *pmd_text = "pmd fetch"; + static const char *pte_text = "pte fetch"; + unsigned long kva = pgd; + unsigned long paddr; + unsigned long pte; + + if (!sparc64_is_linear_mapped(kva)) + error(FATAL, + "sparc64_page_table_walk: pgd must be identity mapped" + " but isn't (0xlx).", pgd); + + pte = fetch_page_table_level(kva, vaddr, PGDIR_SHIFT, + PTES_PER_PAGE_MASK, pgd_text, verbose); + if (!pte) + goto bad; + kva = __va(pte); + + pte = fetch_page_table_level(kva, vaddr, PUD_SHIFT, PTES_PER_PAGE_MASK, + pud_text, verbose); + if (!pte) + goto bad; + + kva = __va(pte); + pte = fetch_page_table_level(kva, vaddr, PMD_SHIFT, + PTES_PER_PAGE_MASK, pmd_text, verbose); + if (!pte) + goto bad; + /* Check for a huge/THP page */ + paddr = pmd_is_huge(pte, vaddr, verbose); + if (paddr) + goto out; + kva = __va(pte); + pte = fetch_page_table_level(kva, vaddr, PAGE_SHIFT, + PTRS_PER_PTE - 1, pte_text, verbose); + if ((pte & _PAGE_VALID) == 0UL) + goto bad; + paddr = pte_to_pa(pte); + paddr = paddr | (vaddr & ~PAGE_MASK); +out: + return paddr; +bad: + return not_valid_pte; +} + +static void +sparc64_init_kernel_pgd(void) +{ + int cpu, rc; + ulong v; + + v = symbol_value("init_mm"); + rc = readmem(v + OFFSET(mm_struct_pgd), KVADDR, &v, sizeof(v), + "init_mm.pgd", RETURN_ON_ERROR); + if (!rc) { + error(WARNING, "Can not determine pgd location.\n"); + goto out; + } + + for (cpu = 0; cpu < NR_CPUS; cpu++) + vt->kernel_pgd[cpu] = v; +out: + return; +} + +static int +sparc64_get_smp_cpus(void) +{ + int ncpu = MAX(get_cpus_online(), get_highest_cpu_online() + 1); + + return ncpu; +} + +static ulong +sparc64_vmalloc_start(void) +{ + return SPARC64_VMALLOC_START; +} + +int +sparc64_IS_VMALLOC_ADDR(ulong vaddr) +{ + return (vaddr >= SPARC64_VMALLOC_START) && + (vaddr < machdep->machspec->vmalloc_end); +} + +static void +pt_clear_cache(void) +{ + machdep->last_pgd_read = 0UL; + machdep->last_pud_read = 0UL; + machdep->last_pmd_read = 0UL; + machdep->last_ptbl_read = 0UL; +} + +static void +pt_level_alloc(char **lvl, char *name) +{ + size_t sz = PAGE_SIZE; + void *pointer = malloc(sz); + + if (!pointer) + error(FATAL, name); + *lvl = pointer; +} + +static int +sparc64_verify_symbol(const char *name, unsigned long value, char type) +{ + return TRUE; +} + +static int +sparc64_verify_line_number(unsigned long pc, unsigned long low, + unsigned long high) +{ + return TRUE; +} + +static int +sparc64_dis_filter(ulong vaddr, char *inbuf, unsigned int radix) +{ + return FALSE; +} + +struct eframe { + struct sparc_stackf sf; + struct pt_regs pr; +}; + +/* Need to handle hardirq and softirq stacks. */ +static int +kstack_valid(struct bt_info *bt, unsigned long sp) +{ + unsigned long thread_info = SIZE(thread_info); + unsigned long base = bt->stackbase + thread_info; + unsigned long top = bt->stacktop - sizeof(struct eframe); + int rc = FALSE; + + if (sp & (16U - 1)) + goto out; + + if ((sp >= base) && (sp <= top)) + rc = TRUE; +out: + return rc; +} + +static void +sparc64_print_eframe(struct bt_info *bt) +{ + struct eframe k_entry; + struct pt_regs *regs = &k_entry.pr; + unsigned long efp; + unsigned int tt; + int rc; + struct reg_window window; + unsigned long rw; + + efp = bt->stkptr + STACK_BIAS - TRACEREG_SZ - STACKFRAME_SZ; + if (!kstack_valid(bt, efp)) + goto try_stacktop; + + rc = readmem(efp, KVADDR, &k_entry, sizeof(k_entry), + "Stack frame and pt_regs.", RETURN_ON_ERROR); + if (rc && ((regs->magic & ~MAGIC_TT_MASK) == PT_REGS_MAGIC)) + goto print_frame; + +try_stacktop: + efp = bt->stacktop - sizeof(struct eframe); + rc = readmem(efp, KVADDR, &k_entry, sizeof(k_entry), + "Stack frame and pt_regs.", RETURN_ON_ERROR); + if (!rc) + goto out; + /* Kernel thread or not in kernel any longer? */ + if ((regs->magic & ~MAGIC_TT_MASK) != PT_REGS_MAGIC) + goto out; + +print_frame: + tt = regs->magic & MAGIC_TT_MASK; + fprintf(fp, "TSTATE=0x%lx TT=0x%x TPC=0x%lx TNPC=0x%lx\n", + regs->tstate, tt, regs->tpc, regs->tnpc); + fprintf(fp, " g0=0x%.16lx g1=0x%.16lx g2=0x%.16lx\n", + regs->u_regs[0], + regs->u_regs[1], + regs->u_regs[2]); + fprintf(fp, " g3=0x%.16lx g4=0x%.16lx g5=0x%.16lx\n", + regs->u_regs[3], + regs->u_regs[4], + regs->u_regs[5]); +#define ___INS (8) + fprintf(fp, " g6=0x%.16lx g7=0x%.16lx\n", + regs->u_regs[6], + regs->u_regs[7]); + fprintf(fp, " o0=0x%.16lx o1=0x%.16lx o2=0x%.16lx\n", + regs->u_regs[___INS+0], + regs->u_regs[___INS+1], + regs->u_regs[___INS+2]); + fprintf(fp, " o3=0x%.16lx o4=0x%.16lx o5=0x%.16lx\n", + regs->u_regs[___INS+3], + regs->u_regs[___INS+4], + regs->u_regs[___INS+5]); + fprintf(fp, " sp=0x%.16lx ret_pc=0x%.16lx\n", + regs->u_regs[___INS+6], + regs->u_regs[___INS+7]); +#undef ___INS + rw = bt->stkptr + STACK_BIAS; + if (!kstack_valid(bt, rw)) + goto out; + rc = readmem(rw, KVADDR, &window, sizeof(window), + "Register window.", RETURN_ON_ERROR); + if (!rc) + goto out; + fprintf(fp, " l0=0x%.16lx l1=0x%.16lx l2=0x%.16lx\n", + window.locals[0], window.locals[1], window.locals[2]); + fprintf(fp, " l3=0x%.16lx l4=0x%.16lx l5=0x%.16lx\n", + window.locals[3], window.locals[4], window.locals[5]); + fprintf(fp, " l6=0x%.16lx l7=0x%.16lx\n", + window.locals[6], window.locals[7]); + fprintf(fp, " i0=0x%.16lx i1=0x%.16lx i2=0x%.16lx\n", + window.ins[0], window.ins[1], window.ins[2]); + fprintf(fp, " i3=0x%.16lx i4=0x%.16lx i5=0x%.16lx\n", + window.ins[3], window.ins[4], window.ins[5]); + fprintf(fp, " i6=0x%.16lx i7=0x%.16lx\n", + window.ins[6], window.ins[7]); +out: + return; +} + +static int +sparc64_eframe_search(struct bt_info *bt) +{ + sparc64_print_eframe(bt); + return 0; +} + +static void +sparc64_print_frame(struct bt_info *bt, int cnt, unsigned long ip, + unsigned long ksp) +{ + char *symbol = closest_symbol(ip); + + fprintf(fp, "#%d [%lx] %s at %lx\n", cnt, ksp, symbol, ip); + + if (bt->flags & BT_LINE_NUMBERS) { + char buf[BUFSIZE]; + + get_line_number(ip, buf, FALSE); + if (strlen(buf)) + fprintf(fp, "\t%s\n", buf); + } +} + +static void +sparc64_back_trace(struct bt_info *bt) +{ + unsigned long ip = bt->instptr; + unsigned long ksp = bt->stkptr; + struct reg_window window; + int cnt = 0; + int rc; + + do { + if (!kstack_valid(bt, ksp + STACK_BIAS)) + break; + rc = readmem(ksp + STACK_BIAS, KVADDR, &window, sizeof(window), + "KSP window fetch.", RETURN_ON_ERROR); + if (!rc) + goto out; + sparc64_print_frame(bt, cnt, ip, ksp); + ksp = window.ins[6]; + ip = window.ins[7]; + cnt++; + } while (cnt != 50); + sparc64_print_eframe(bt); +out: + return; +} + +static ulong +sparc64_processor_speed(void) +{ + int cpu; + unsigned long clock_tick; + struct syment *sp; + + if (!MEMBER_EXISTS("cpuinfo_sparc", "clock_tick")) { + error(WARNING, "sparc64 expects clock_tick\n"); + return 0UL; + } + + sp = per_cpu_symbol_search("__cpu_data"); + if (!sp) + return 0UL; + for (cpu = 0; cpu < kt->cpus; cpu++) { + if (!in_cpu_map(ONLINE, cpu)) + continue; + if (!readmem(sp->value + kt->__per_cpu_offset[cpu] + + MEMBER_OFFSET("cpuinfo_sparc", "clock_tick"), + KVADDR, &clock_tick, sizeof(clock_tick), + "clock_tick", QUIET|RETURN_ON_ERROR)) + continue; + return clock_tick/1000000; + } + return 0UL; +} + +static ulong +sparc64_get_task_pgd(ulong task) +{ + struct task_context *tc = task_to_context(task); + ulong pgd = NO_TASK; + + if (!tc) + goto out; + readmem(tc->mm_struct + OFFSET(mm_struct_pgd), KVADDR, + &pgd, sizeof(unsigned long), "User pgd.", RETURN_ON_ERROR); +out: + return pgd; +} + +static int +sparc64_uvtop(struct task_context *tc, ulong va, physaddr_t *ppaddr, + int verbose) +{ + unsigned long pgd = sparc64_get_task_pgd(tc->task); + unsigned long paddr; + int rc = FALSE; + + if (pgd == NO_TASK) + goto out; + paddr = sparc64_page_table_walk(pgd, va, verbose); + /* For now not_valid_pte skips checking for swap pte. */ + if (paddr == not_valid_pte) { + *ppaddr = 0UL; + goto out; + } + *ppaddr = paddr; + rc = TRUE; +out: + return rc; +} + +static unsigned long +sparc64_vmalloc_translate(unsigned long vaddr, int verbose) +{ + unsigned long paddr = sparc64_page_table_walk(vt->kernel_pgd[0], + vaddr, verbose); + + return paddr; +} + +static unsigned long +sparc64_linear_translate(unsigned long vaddr) +{ + unsigned long paddr = __pa(vaddr); + + if (sparc64_verify_paddr(paddr) == FALSE) + error(FATAL, + "sparc64_linear_translate: This physical address" + " (0x%lx) is invalid.", paddr); + + return paddr; +} + +static int +sparc64_is_vmalloc_mapped(unsigned long vaddr) +{ + struct machine_specific *ms = &sparc64_machine_specific; + int rc = 0; + + if ((vaddr >= SPARC64_MODULES_VADDR && vaddr < SPARC64_MODULES_END) || + (vaddr >= SPARC64_VMALLOC_START && vaddr < ms->vmalloc_end)) + rc = 1; + return rc; +} + +static int +sparc64_is_kvaddr(ulong vaddr) +{ + return kimage_va_range(vaddr) || + sparc64_is_linear_mapped(vaddr) || + sparc64_is_vmalloc_mapped(vaddr); +} + +static int +sparc64_kvtop(struct task_context *tc, ulong vaddr, physaddr_t *paddr, + int verbose) +{ + unsigned long phys_addr; + int rc = FALSE; + + if (kimage_va_range(vaddr)) { + phys_addr = kimage_va_translate(vaddr); + } else if (sparc64_is_vmalloc_mapped(vaddr)) { + phys_addr = sparc64_vmalloc_translate(vaddr, verbose); + if (phys_addr == not_valid_pte) + goto out; + } else if (sparc64_is_linear_mapped(vaddr)) { + phys_addr = sparc64_linear_translate(vaddr); + } else { + error(WARNING, + "This is an invalid kernel virtual address=0x%lx.", + vaddr); + goto out; + } + + *paddr = phys_addr; + rc = TRUE; +out: + return rc; +} + +static int +sparc64_is_task_addr(ulong task) +{ + int rc = FALSE; + int cpu; + + if (sparc64_is_linear_mapped(task) || kimage_va_range(task)) + rc = TRUE; + else { + for (cpu = 0; cpu < kt->cpus; cpu++) + if (task == tt->idle_threads[cpu]) { + rc = TRUE; + break; + } + } + return rc; +} + +static int +sparc64_is_uvaddr(ulong vaddr, struct task_context *tc) +{ + return vaddr < SPARC64_USERSPACE_TOP; +} + +static const char +*pte_page_size(unsigned long pte) +{ + static const char *_4Mb = "4Mb"; + static const char *_64Kb = "64Kb"; + static const char *_8Kb = "8Kb"; + static const char *_ns = "Not Supported"; + const char *result; + + switch (pte & _PAGE_SZALL_4V) { + case _PAGE_SZ8K_4V: + result = _8Kb; + break; + case _PAGE_SZ64K_4V: + result = _64Kb; + break; + case _PAGE_SZ4MB_4V: + result = _4Mb; + break; + default: + result = _ns; + } + return result; +} + +static int +sparc64_translate_pte(unsigned long pte, void *physaddr, ulonglong unused) +{ + unsigned long paddr = pte_to_pa(pte); + int rc = FALSE; + int cnt = 0; + + /* Once again not handling swap pte.*/ + if ((pte & _PAGE_VALID) == 0UL) + goto out; + if (pte & _PAGE_NFO_4V) + fprintf(fp, "%sNoFaultOn", cnt++ ? "|" : ""); + if (pte & _PAGE_MODIFIED_4V) + fprintf(fp, "%sModified", cnt++ ? "|" : ""); + if (pte & _PAGE_ACCESSED_4V) + fprintf(fp, "%sAccessed", cnt++ ? "|" : ""); + if (pte & _PAGE_READ_4V) + fprintf(fp, "%sReadSoftware", cnt++ ? "|" : ""); + if (pte & _PAGE_WRITE_4V) + fprintf(fp, "%sWriteSoftware", cnt++ ? "|" : ""); + if (pte & _PAGE_P_4V) + fprintf(fp, "%sPriv", cnt++ ? "|" : ""); + if (pte & _PAGE_EXEC_4V) + fprintf(fp, "%sExecute", cnt++ ? "|" : ""); + if (pte & _PAGE_W_4V) + fprintf(fp, "%sWritable", cnt++ ? "|" : ""); + if (pte & _PAGE_PRESENT_4V) + fprintf(fp, "%sPresent", cnt++ ? "|" : ""); + fprintf(fp, "|PageSize(%s)\n", pte_page_size(pte)); + if (physaddr) + *(unsigned long *)physaddr = paddr; + rc = TRUE; +out: + return rc; +} + +static void +sparc64_get_frame(struct bt_info *bt, unsigned long *r14, unsigned long *r15) +{ + unsigned long ksp_offset = sparc64_ksp_offset + bt->tc->thread_info; + unsigned long ksp; + int rc; + + /* We need thread_info's ksp. This is the stack for sleeping threads + * and captured during switch_to. The rest is fetchable from there. + */ + rc = readmem(ksp_offset, KVADDR, &ksp, sizeof(ksp), "KSP Fetch.", + RETURN_ON_ERROR); + if (!rc) + goto out; + *r14 = ksp; + *r15 = symbol_value("switch_to_pc"); +out: + return; +} + +static void +sparc64_get_dumpfile_stack_frame(struct bt_info *bt, unsigned long *psp, + unsigned long *ppc) +{ + unsigned long *pt_regs; + + pt_regs = (unsigned long *)bt->machdep; + + if (!pt_regs) + fprintf(fp, "0%lx: registers not saved\n", bt->task); + + /* pt_regs can be unaligned */ + BCOPY(&pt_regs[30], psp, sizeof(ulong)); + BCOPY(&pt_regs[33], ppc, sizeof(ulong)); +} + +static void +sparc64_get_stack_frame(struct bt_info *bt, unsigned long *pcp, + unsigned long *psp) +{ + unsigned long r14, r15; + + if (DUMPFILE() && is_task_active(bt->task)) + sparc64_get_dumpfile_stack_frame(bt, &r14, &r15); + else + sparc64_get_frame(bt, &r14, &r15); + if (pcp) + *pcp = r15; + if (psp) + *psp = r14; +} + +static int +sparc64_get_kvaddr_ranges(struct vaddr_range *vrp) +{ + struct machine_specific *ms = &sparc64_machine_specific; + + vrp[0].type = KVADDR_UNITY_MAP; + vrp[0].start = ms->page_offset; + vrp[0].end = ~0ULL; + vrp[1].type = KVADDR_VMALLOC; + vrp[1].start = SPARC64_VMALLOC_START; + vrp[1].end = ms->vmalloc_end; + vrp[2].type = KVADDR_START_MAP; + vrp[2].start = symbol_value("_start"); + vrp[2].end = symbol_value("_end"); + vrp[3].type = KVADDR_MODULES; + vrp[3].start = SPARC64_MODULES_VADDR; + vrp[3].end = SPARC64_MODULES_END; + return 4; +} + +static void +sparc64_get_crash_notes(void) +{ + unsigned long *notes_ptrs, size, crash_notes_address; + int ret; + + if (!symbol_exists("crash_notes")) { + error(WARNING, "Could not retrieve crash_notes."); + goto out; + } + + crash_notes_address = symbol_value("crash_notes"); + size = kt->cpus * sizeof(notes_ptrs[0]); + notes_ptrs = (unsigned long *) GETBUF(size); + ret = readmem(crash_notes_address, KVADDR, notes_ptrs, size, + "crash_notes", RETURN_ON_ERROR); + if (!ret) + goto out2; +out2: + FREEBUF(notes_ptrs); +out: + return; +} + +static void +sparc64_init_kstack_info(void) +{ + sparc64_ksp_offset = MEMBER_OFFSET("thread_info", "ksp"); +} + +static void +sparc64_init_irq_stacks(void) +{ + void *irq_stack; + unsigned long stack_size; + + stack_size = get_array_length("hardirq_stack", NULL, 0) * + sizeof(unsigned long); + irq_stack = malloc(stack_size); + if (!irq_stack) + error(FATAL, "malloc failure in sparc64_init_irq_stacks"); + + get_symbol_data("hardirq_stack", stack_size, irq_stack); + tt->hardirq_ctx = irq_stack; + + stack_size = get_array_length("softirq_stack", NULL, 0) * + sizeof(unsigned long); + irq_stack = malloc(stack_size); + if (!irq_stack) + error(FATAL, "malloc failure in sparc64_init_irq_stacks"); + + get_symbol_data("softirq_stack", stack_size, irq_stack); + tt->softirq_ctx = irq_stack; +} + +static void +sparc64_init_vmemmap_info(void) +{ + struct machine_specific *ms = &sparc64_machine_specific; + unsigned long page_struct_size = STRUCT_SIZE("page"); + + /* + * vmemmap memory is addressed as vmalloc memory, so we + * treat it as an etension of the latter. + */ + ms->vmalloc_end += + ((1UL << (machdep->max_physmem_bits - PAGE_SHIFT)) * + page_struct_size); +} + +static void +sparc64_init_cpu_info(void) +{ + unsigned long trap_block, per_cpu_base_offset, per_cpu_base; + unsigned long trap_per_cpu; + int cpu; + + if (!symbol_exists("trap_block")) + error(FATAL, "sparc64 requires trap_block symbol.\n"); + + trap_block = symbol_value("trap_block"); + if (!MEMBER_EXISTS("trap_per_cpu", "__per_cpu_base")) + error(FATAL, "sparc64 requires __per_cpu_base.\n"); + trap_per_cpu = STRUCT_SIZE("trap_per_cpu"); + per_cpu_base_offset = MEMBER_OFFSET("trap_per_cpu", "__per_cpu_base"); + for (cpu = 0; cpu < NR_CPUS; cpu++, + trap_block = trap_block + trap_per_cpu) { + + if (!in_cpu_map(POSSIBLE, cpu)) + continue; + readmem(trap_block + per_cpu_base_offset, KVADDR, + &per_cpu_base, sizeof(per_cpu_base), + "sparc64: per_cpu_base", FAULT_ON_ERROR); + kt->__per_cpu_offset[cpu] = per_cpu_base; + } +} + +void +sparc64_init(int when) +{ + struct machine_specific *ms = &sparc64_machine_specific; + + switch (when) { + case SETUP_ENV: + machdep->process_elf_notes = process_elf64_notes; + break; + case PRE_SYMTAB: + machdep->machspec = ms; + machdep->verify_paddr = sparc64_verify_paddr; + machdep->verify_symbol = sparc64_verify_symbol; + machdep->verify_line_number = sparc64_verify_line_number; + + if (pc->flags & KERNEL_DEBUG_QUERY) + return; + machdep->flags |= MACHDEP_BT_TEXT; + if (machdep->cmdline_args[0]) + sparc64_parse_cmdline_args(); + break; + + case PRE_GDB: + machdep->max_physmem_bits = _MAX_PHYSMEM_BITS; + + machdep->pagesize = memory_page_size(); + machdep->pageshift = ffs(machdep->pagesize) - 1; + machdep->pageoffset = machdep->pagesize - 1; + machdep->pagemask = ~((ulonglong) machdep->pageoffset); + machdep->stacksize = machdep->pagesize * 2; + + machdep->eframe_search = sparc64_eframe_search; + machdep->back_trace = sparc64_back_trace; + machdep->processor_speed = sparc64_processor_speed; + + machdep->uvtop = sparc64_uvtop; + machdep->kvtop = sparc64_kvtop; + machdep->get_task_pgd = sparc64_get_task_pgd; + + machdep->dump_irq = generic_dump_irq; + + machdep->get_stack_frame = sparc64_get_stack_frame; + machdep->get_stackbase = generic_get_stackbase; + machdep->get_stacktop = generic_get_stacktop; + machdep->translate_pte = sparc64_translate_pte; + machdep->memory_size = generic_memory_size; + + machdep->vmalloc_start = sparc64_vmalloc_start; + machdep->is_task_addr = sparc64_is_task_addr; + machdep->is_kvaddr = sparc64_is_kvaddr; + machdep->is_uvaddr = sparc64_is_uvaddr; + machdep->dis_filter = sparc64_dis_filter; + machdep->get_smp_cpus = sparc64_get_smp_cpus; + machdep->clear_machdep_cache = sparc64_clear_machdep_cache; + machdep->get_kvaddr_ranges = sparc64_get_kvaddr_ranges; + machdep->cmd_mach = sparc64_cmd_mach; + machdep->init_kernel_pgd = sparc64_init_kernel_pgd; + machdep->value_to_symbol = generic_machdep_value_to_symbol; + machdep->get_irq_affinity = generic_get_irq_affinity; + machdep->show_interrupts = generic_show_interrupts; + + pt_level_alloc(&machdep->pgd, "Can't malloc pgd space."); + pt_level_alloc(&machdep->pud, "Can't malloc pud space."); + pt_level_alloc(&machdep->pmd, "Can't malloc pmd space."); + pt_level_alloc(&machdep->ptbl, "Can't malloc ptbl space."); + pt_clear_cache(); + sparc64_phys_base(); + sparc64_kimage_limits(); + break; + + case POST_GDB: + get_symbol_data("PAGE_OFFSET", sizeof(unsigned long), + &ms->page_offset); + machdep->kvbase = symbol_value("_stext"); + machdep->identity_map_base = (ulong) PAGE_OFFSET; + machdep->ptrs_per_pgd = PTRS_PER_PGD; + get_symbol_data("VMALLOC_END", sizeof(unsigned long), + &ms->vmalloc_end); + machdep->section_size_bits = _SECTION_SIZE_BITS; + if (kernel_symbol_exists("nr_irqs")) + get_symbol_data("nr_irqs", sizeof(unsigned int), + &machdep->nr_irqs); + sparc64_init_vmemmap_info(); + sparc64_init_cpu_info(); + sparc64_init_kstack_info(); + sparc64_init_irq_stacks(); + break; + case POST_VM: + if (!ACTIVE()) + sparc64_get_crash_notes(); + break; + case POST_INIT: + break; + + case LOG_ONLY: + machdep->machspec = ms; + machdep->kvbase = kt->vmcoreinfo._stext_SYMBOL; + break; + } +} + +void +sparc64_dump_machdep_table(ulong arg) +{ + int i, others; + + others = 0; + fprintf(fp, " flags: %lx (", machdep->flags); + if (machdep->flags & MACHDEP_BT_TEXT) + fprintf(fp, "%sMACHDEP_BT_TEXT", others++ ? "|" : ""); + fprintf(fp, ")\n"); + + fprintf(fp, " kvbase: %lx\n", machdep->kvbase); + fprintf(fp, " identity_map_base: %lx\n", machdep->identity_map_base); + fprintf(fp, " pagesize: %d\n", machdep->pagesize); + fprintf(fp, " pageshift: %d\n", machdep->pageshift); + fprintf(fp, " pagemask: %llx\n", machdep->pagemask); + fprintf(fp, " pageoffset: %lx\n", machdep->pageoffset); + fprintf(fp, " stacksize: %ld\n", machdep->stacksize); + fprintf(fp, " hz: %d\n", machdep->hz); + fprintf(fp, " mhz: %ld\n", machdep->mhz); + fprintf(fp, " memsize: %ld (0x%lx)\n", + machdep->memsize, machdep->memsize); + fprintf(fp, " bits: %d\n", machdep->bits); + fprintf(fp, " nr_irqs: %d\n", machdep->nr_irqs); + fprintf(fp, " eframe_search: sparc64_eframe_search()\n"); + fprintf(fp, " back_trace: sparc64_back_trace()\n"); + fprintf(fp, " processor_speed: sparc64_processor_speed()\n"); + fprintf(fp, " uvtop: sparc64_uvtop()\n"); + fprintf(fp, " kvtop: sparc64_kvtop()\n"); + fprintf(fp, " get_task_pgd: sparc64_get_task_pgd()\n"); + fprintf(fp, " dump_irq: generic_dump_irq()\n"); + fprintf(fp, " get_stack_frame: sparc64_get_stack_frame()\n"); + fprintf(fp, " get_stackbase: generic_get_stackbase()\n"); + fprintf(fp, " get_stacktop: generic_get_stacktop()\n"); + fprintf(fp, " translate_pte: sparc64_translate_pte()\n"); + fprintf(fp, " memory_size: generic_memory_size()\n"); + fprintf(fp, " vmalloc_start: sparc64_vmalloc_start()\n"); + fprintf(fp, " is_task_addr: sparc64_is_task_addr()\n"); + fprintf(fp, " verify_symbol: sparc64_verify_symbol()\n"); + fprintf(fp, " dis_filter: sparc64_dis_filter()\n"); + fprintf(fp, " cmd_mach: sparc64_cmd_mach()\n"); + fprintf(fp, " get_smp_cpus: sparc64_get_smp_cpus()\n"); + fprintf(fp, " is_kvaddr: sparc64_is_kvaddr()\n"); + fprintf(fp, " is_uvaddr: sparc64_is_uvaddr()\n"); + fprintf(fp, " verify_paddr: sparc64_verify_paddr()\n"); + fprintf(fp, " get_kvaddr_ranges: sparc64_get_kvaddr_ranges()\n"); + fprintf(fp, " get_irq_affinity: generic_get_irq_affinity()\n"); + fprintf(fp, " show_interrupts: generic_show_interrupts()\n"); + fprintf(fp, " xendump_p2m_create: NULL\n"); + fprintf(fp, "xen_kdump_p2m_create: NULL\n"); + fprintf(fp, " line_number_hooks: NULL\n"); + fprintf(fp, " last_pgd_read: %lx\n", machdep->last_pgd_read); + fprintf(fp, " last_pmd_read: %lx\n", machdep->last_pmd_read); + fprintf(fp, " last_ptbl_read: %lx\n", machdep->last_ptbl_read); + fprintf(fp, "clear_machdep_cache: sparc64_clear_machdep_cache()\n"); + fprintf(fp, " pgd: %lx\n", (ulong)machdep->pgd); + fprintf(fp, " pmd: %lx\n", (ulong)machdep->pmd); + fprintf(fp, " ptbl: %lx\n", (ulong)machdep->ptbl); + fprintf(fp, " ptrs_per_pgd: %d\n", machdep->ptrs_per_pgd); + fprintf(fp, " section_size_bits: %ld\n", machdep->section_size_bits); + fprintf(fp, " max_physmem_bits: %ld\n", machdep->max_physmem_bits); + fprintf(fp, " sections_per_root: %ld\n", machdep->sections_per_root); + for (i = 0; i < MAX_MACHDEP_ARGS; i++) { + fprintf(fp, " cmdline_args[%d]: %s\n", i, + machdep->cmdline_args[i] ? + machdep->cmdline_args[i] : "(unused)"); + } + fprintf(fp, " machspec: %lx\n", (ulong)machdep->machspec); + fprintf(fp, " page_offset: %lx\n", + machdep->machspec->page_offset); + fprintf(fp, " vmalloc_end: %lx\n", + machdep->machspec->vmalloc_end); +} + +#endif /* SPARC64 */ diff -Nru crash-7.1.4/symbols.c crash-7.2.3+real/symbols.c --- crash-7.1.4/symbols.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/symbols.c 2018-05-17 17:39:38.000000000 +0000 @@ -1,8 +1,8 @@ /* symbols.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-2018 David Anderson + * Copyright (C) 2002-2018 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 @@ -33,11 +33,16 @@ static int compare_syms(const void *, const void *); static int compare_mods(const void *, const void *); static int compare_prios(const void *v1, const void *v2); +static int compare_size_name(const void *, const void *); +struct type_request; +static void append_struct_symbol (struct type_request *, struct gnu_request *); +static void request_types(ulong, ulong, char *); static asection *get_kernel_section(char *); static char * get_section(ulong vaddr, char *buf); static void symbol_dump(ulong, char *); static void check_for_dups(struct load_module *); static struct syment *kallsyms_module_symbol(struct load_module *, symbol_info *); +static int kallsyms_module_function_size(struct syment *, struct load_module *, ulong *); static void store_load_module_symbols \ (bfd *, int, void *, long, uint, ulong, char *); static int load_module_index(struct syment *); @@ -76,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 *); @@ -578,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. @@ -588,7 +594,8 @@ { char *string; - if (!machine_type("X86_64") || (kt->flags & RELOC_SET)) + if ((!machine_type("X86_64") && !machine_type("ARM64") && !machine_type("X86")) || + (kt->flags & RELOC_SET)) return; /* @@ -598,12 +605,18 @@ 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; } - if (KDUMP_DUMPFILE() || DISKDUMP_DUMPFILE()) { + if (QEMU_MEM_DUMP_NO_VMCOREINFO()) { + if (KDUMP_DUMPFILE() && kdump_kaslr_check()) { + kt->flags2 |= KASLR_CHECK; + } else if (DISKDUMP_DUMPFILE() && diskdump_kaslr_check()) { + kt->flags2 |= KASLR_CHECK; + } + } else if (KDUMP_DUMPFILE() || DISKDUMP_DUMPFILE()) { if ((string = pc->read_vmcoreinfo("SYMBOL(_stext)"))) { kt->vmcoreinfo._stext_SYMBOL = htol(string, RETURN_ON_ERROR, NULL); @@ -617,6 +630,9 @@ st->_stext_vmlinux = UNINITIALIZED; } } + + if (SADUMP_DUMPFILE() || VMSS_DUMPFILE()) + kt->flags2 |= KASLR_CHECK; } /* @@ -630,6 +646,31 @@ unsigned long relocate; ulong _stext_relocated; + if (SADUMP_DUMPFILE() || QEMU_MEM_DUMP_NO_VMCOREINFO() || VMSS_DUMPFILE()) { + ulong kaslr_offset = 0; + ulong phys_base = 0; + + calc_kaslr_offset(&kaslr_offset, &phys_base); + + if (kaslr_offset) { + kt->relocate = kaslr_offset * -1; + kt->flags |= RELOC_SET; + } + + if (phys_base) { + if (SADUMP_DUMPFILE()) + sadump_set_phys_base(phys_base); + else if (KDUMP_DUMPFILE()) + kdump_set_phys_base(phys_base); + else if (DISKDUMP_DUMPFILE()) + diskdump_set_phys_base(phys_base); + else if (VMSS_DUMPFILE()) + vmware_vmss_set_phys_base(phys_base); + } + + return; + } + if (ACTIVE()) { _stext_relocated = symbol_value_from_proc_kallsyms("_stext"); if (_stext_relocated == BADVAL) @@ -705,9 +746,13 @@ fromend = from + symcount * size; if (machine_type("X86")) { - if (!(kt->flags & RELOC_SET)) + if (kt->flags2 & KASLR) { + if ((kt->flags2 & RELOC_AUTO) && !(kt->flags & RELOC_SET)) + derive_kaslr_offset(abfd, dynamic, from, + fromend, size, store); + } else 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); @@ -778,7 +823,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; @@ -828,7 +874,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 @@ -976,10 +1022,9 @@ found = FALSE; while (!found && fgets(buf, BUFSIZE, kp) && - (parse_line(buf, kallsyms) == 3) && - hexadecimal(kallsyms[0], 0)) { - - if (STREQ(kallsyms[2], symname)) { + (parse_line(buf, kallsyms) == 3)) { + if (hexadecimal(kallsyms[0], 0) && + STREQ(kallsyms[2], symname)) { kallsym = htol(kallsyms[0], RETURN_ON_ERROR, NULL); found = TRUE; break; @@ -1319,9 +1364,9 @@ struct module_symbol *modsym; struct load_module *lm; char buf1[BUFSIZE]; - char buf2[BUFSIZE]; + char buf2[BUFSIZE*2]; char name[BUFSIZE]; - char rodata[BUFSIZE]; + char rodata[BUFSIZE*2]; char *strbuf, *modbuf, *modsymbuf; struct syment *sp; ulong first, last; @@ -1387,7 +1432,7 @@ error(INFO, "module name greater than MAX_MOD_NAME: %s\n", name); - strncpy(lm->mod_name, name, MAX_MOD_NAME-1); + BCOPY(name, lm->mod_name, MAX_MOD_NAME-1); } lm->mod_flags = MOD_EXT_SYMS; @@ -1413,9 +1458,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) { @@ -1436,15 +1483,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) @@ -1717,9 +1760,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) { @@ -1740,15 +1785,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) @@ -1792,9 +1834,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) { @@ -1815,15 +1859,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) @@ -2104,22 +2144,27 @@ ulong st_value; ulong st_shndx; unsigned char st_info; + ulong st_size; }; -static void Elf32_Sym_to_common(Elf32_Sym *e32, struct elf_common *ec) +static void +Elf32_Sym_to_common(Elf32_Sym *e32, struct elf_common *ec) { ec->st_name = (ulong)e32->st_name; ec->st_value = (ulong)e32->st_value; ec->st_shndx = (ulong)e32->st_shndx; ec->st_info = e32->st_info; + ec->st_size = (ulong)e32->st_size; } -static void Elf64_Sym_to_common(Elf64_Sym *e64, struct elf_common *ec) +static void +Elf64_Sym_to_common(Elf64_Sym *e64, struct elf_common *ec) { ec->st_name = (ulong)e64->st_name; ec->st_value = (ulong)e64->st_value; ec->st_shndx = (ulong)e64->st_shndx; ec->st_info = e64->st_info; + ec->st_size = (ulong)e64->st_size; } static int @@ -2239,10 +2284,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; @@ -2832,10 +2878,20 @@ { struct syment *sp; struct gnu_request gnu_request, *req = &gnu_request; + struct load_module *lm; + ulong size; if (!(sp = value_search(vaddr, NULL))) return FALSE; + if (module_symbol(vaddr, NULL, &lm, NULL, 0)) { + if (kallsyms_module_function_size(sp, lm, &size)) { + *low = sp->value; + *high = sp->value + size; + return TRUE; + } + } + BZERO(req, sizeof(struct gnu_request)); req->command = GNU_GET_FUNCTION_RANGE; req->pc = sp->value; @@ -2854,6 +2910,97 @@ } /* + * Get the text size of a module function from kallsyms. + */ +static int +kallsyms_module_function_size(struct syment *sp, struct load_module *lm, ulong *size) +{ + int i; + ulong nksyms, ksymtab, st_size; + char *ptr, *module_buf, *module_buf_init, *modbuf, *locsymtab; + struct elf_common elf_common, *ec; + + if (!(lm->mod_flags & MOD_KALLSYMS) || !(kt->flags & KALLSYMS_V2)) + return FALSE; + + module_buf = GETBUF(lm->mod_size); + modbuf = module_buf + (lm->module_struct - lm->mod_base); + + if (!readmem(lm->mod_base, KVADDR, module_buf, lm->mod_size, + "module (kallsyms)", RETURN_ON_ERROR|QUIET)) { + FREEBUF(module_buf); + return FALSE; + } + + if (lm->mod_init_size > 0) { + module_buf_init = GETBUF(lm->mod_init_size); + if (!readmem(lm->mod_init_module_ptr, KVADDR, module_buf_init, + lm->mod_init_size, "module init (kallsyms)", + RETURN_ON_ERROR|QUIET)) { + FREEBUF(module_buf_init); + module_buf_init = NULL; + } + } else + module_buf_init = NULL; + + if (THIS_KERNEL_VERSION >= LINUX(2,6,27)) + nksyms = UINT(modbuf + OFFSET(module_num_symtab)); + else + nksyms = ULONG(modbuf + OFFSET(module_num_symtab)); + + ksymtab = ULONG(modbuf + OFFSET(module_symtab)); + if (!IN_MODULE(ksymtab, lm) && !IN_MODULE_INIT(ksymtab, lm)) { + FREEBUF(module_buf); + if (module_buf_init) + FREEBUF(module_buf_init); + return FALSE; + } + + if (IN_MODULE(ksymtab, lm)) + locsymtab = module_buf + (ksymtab - lm->mod_base); + else + locsymtab = module_buf_init + (ksymtab - lm->mod_init_module_ptr); + + st_size = 0; + ec = &elf_common; + BZERO(&elf_common, sizeof(struct elf_common)); + + for (i = 1; i < nksyms; i++) { /* ELF starts real symbols at 1 */ + switch (BITS()) + { + case 32: + ptr = locsymtab + (i * sizeof(Elf32_Sym)); + Elf32_Sym_to_common((Elf32_Sym *)ptr, ec); + break; + case 64: + ptr = locsymtab + (i * sizeof(Elf64_Sym)); + Elf64_Sym_to_common((Elf64_Sym *)ptr, ec); + break; + } + + if (sp->value == ec->st_value) { + if (CRASHDEBUG(1)) + fprintf(fp, "kallsyms_module_function_size: " + "st_value: %lx st_size: %ld\n", + ec->st_value, ec->st_size); + st_size = ec->st_size; + break; + } + } + + if (module_buf_init) + FREEBUF(module_buf_init); + FREEBUF(module_buf); + + if (st_size) { + *size = st_size; + return TRUE; + } + + return FALSE; +} + +/* * "help -s" output */ void @@ -2938,6 +3085,20 @@ else fprintf(fp, "\n"); + if (SADUMP_DUMPFILE() || QEMU_MEM_DUMP_NO_VMCOREINFO() || VMSS_DUMPFILE()) { + fprintf(fp, "divide_error_vmlinux: %lx\n", st->divide_error_vmlinux); + fprintf(fp, " idt_table_vmlinux: %lx\n", st->idt_table_vmlinux); + fprintf(fp, "saved_command_line_vmlinux: %lx\n", st->saved_command_line_vmlinux); + fprintf(fp, " pti_init_vmlinux: %lx\n", st->pti_init_vmlinux); + fprintf(fp, " kaiser_init_vmlinux: %lx\n", st->kaiser_init_vmlinux); + } else { + fprintf(fp, "divide_error_vmlinux: (unused)\n"); + fprintf(fp, " idt_table_vmlinux: (unused)\n"); + fprintf(fp, "saved_command_line_vmlinux: (unused)\n"); + fprintf(fp, " pti_init_vmlinux: (unused)\n"); + fprintf(fp, " kaiser_init_vmlinux: (unused)\n"); + } + fprintf(fp, " symval_hash[%d]: %lx\n", SYMVAL_HASH, (ulong)&st->symval_hash[0]); @@ -3144,6 +3305,8 @@ lm->mod_section_data[s].size); } + fprintf(fp, " loaded_objfile: %lx\n", (ulong)lm->loaded_objfile); + if (CRASHDEBUG(1)) { for (sp = lm->mod_load_symtable; sp < lm->mod_load_symend; sp++) { @@ -3287,6 +3450,11 @@ goto bailout; break; + case EM_SPARCV9: + if (machine_type_mismatch(file, "SPARC64", NULL, 0)) + goto bailout; + break; + default: if (machine_type_mismatch(file, "(unknown)", NULL, 0)) goto bailout; @@ -3338,7 +3506,8 @@ if (endian_mismatch(file, elf64->e_ident[EI_DATA], 0)) goto bailout; - } + } else + return FALSE; bailout: return(is_bfd_format(file)); @@ -3558,6 +3727,11 @@ if (machine_type("ARM64")) return TRUE; break; + + case EM_SPARCV9: + if (machine_type("SPARC64")) + return TRUE; + break; } if (CRASHDEBUG(1)) @@ -3804,7 +3978,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); @@ -3949,6 +4124,7 @@ struct load_module *lm; buf[0] = NULLCHAR; + lm = NULL; if (NO_LINE_NUMBERS() || !is_kernel_text(addr)) return(buf); @@ -3978,6 +4154,8 @@ req->command = GNU_GET_LINE_NUMBER; req->addr = addr; req->buf = buf; + if (lm && lm->loaded_objfile) + req->lm = lm; if ((sp = value_search(addr, NULL))) req->name = sp->name; gdb_interface(req); @@ -4560,7 +4738,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) @@ -5754,7 +5932,7 @@ dereference_pointer(ulong addr, struct datatype_member *dm, ulong flags) { char buf1[BUFSIZE]; - char buf2[BUFSIZE]; + char buf2[BUFSIZE*2]; char *typeptr, *member, *charptr, *voidptr, *p1, *sym; int found, ptrptr, funcptr, typedef_is_ptr, use_symbol; ulong target, value; @@ -5993,6 +6171,7 @@ char *separator; char *structname, *members; char *memberlist[MAXARGS]; + char *typename; dm = &datatype_member; count = 0xdeadbeef; @@ -6126,6 +6305,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 { @@ -6317,6 +6505,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 @@ -6529,6 +6748,107 @@ fprintf(ofp, "\n"); } +struct type_request { + int cnt; /* current number of entries in types array */ + int idx; /* index to next entry in types array */ + struct type_info { /* dynamically-sized array of collected types */ + char *name; + ulong size; + } *types; +}; + +static int +compare_size_name(const void *va, const void *vb) { + struct type_info *a, *b; + + a = (struct type_info *)va; + b = (struct type_info *)vb; + + if (a->size == b->size) + return strcmp(a->name, b->name); + else + return a->size < b->size ? -1 : 1; +} + +static void +append_struct_symbol (struct type_request *treq, struct gnu_request *req) +{ + int i; + long s; + + for (i = 0; i < treq->idx; i++) + if (treq->types[i].name == req->name) + break; + + if (i < treq->idx) // We've already collected this type + return; + + if (treq->idx == treq->cnt) { + s = sizeof(struct type_info) * treq->cnt; + RESIZEBUF(treq->types, s, s * 3); + treq->cnt *= 3; + } + + treq->types[treq->idx].name = req->name; + treq->types[treq->idx].size = req->length; + treq->idx++; +} + +static void +request_types(ulong lowest, ulong highest, char *member_name) +{ + int i, len; + char buf[BUFSIZE]; + struct type_request typereq; + struct gnu_request request = {0}; + + typereq.idx = 0; + typereq.cnt = 16; + typereq.types = (void *)GETBUF(16 * sizeof(struct type_info)); + +#if defined(GDB_5_3) || defined(GDB_6_0) || defined(GDB_6_1) || defined(GDB_7_0) + error(FATAL, "-r option not supported with this version of gdb\n"); +#else + request.type_name = member_name; +#endif + + while (!request.global_iterator.finished) { + request.command = GNU_GET_NEXT_DATATYPE; + gdb_interface(&request); + if (highest && + !(lowest <= request.length && request.length <= highest)) + continue; + + if (member_name) { + request.command = GNU_LOOKUP_STRUCT_CONTENTS; + gdb_interface(&request); + if (!request.value) + continue; + } + + append_struct_symbol(&typereq, &request); + } + + qsort(typereq.types, typereq.idx, sizeof(struct type_info), compare_size_name); + + if (typereq.idx == 0) + fprintf(fp, "(none found)\n"); + else { + sprintf(buf, "%ld", typereq.types[typereq.idx-1].size); + len = MAX(strlen(buf), strlen("SIZE")); + fprintf(fp, "%s TYPE\n", + mkstring(buf, len, RJUST, "SIZE")); + + for (i = 0; i < typereq.idx; i++) + fprintf(fp, "%s %s\n", + mkstring(buf, len, RJUST|LONG_DEC, + MKSTR(typereq.types[i].size)), + typereq.types[i].name); + } + + FREEBUF(typereq.types); +} + /* * This command displays the definition of structures, unions, typedefs or * text/data symbols: @@ -6543,33 +6863,60 @@ * declaration is displayed. * 5. For a kernel symbol name, the output is the same as if the "sym" command * was used. + * 6. If the -r and -m are given, then the structures/unions of specified size + * and/or contain a member type. */ void cmd_whatis(void) { + int c, do_request; struct datatype_member datatype_member, *dm; struct syment *sp; - char buf[BUFSIZE]; + char buf[BUFSIZE], *pl, *ph, *member; long len; - int c; + ulong lowest, highest; ulong flags; dm = &datatype_member; flags = 0; + lowest = highest = 0; + pl = buf; + member = NULL; + do_request = FALSE; - while ((c = getopt(argcnt, args, "o")) != EOF) { + while ((c = getopt(argcnt, args, "om:r:")) != EOF) { switch(c) { case 'o': flags |= SHOW_OFFSET; break; + case 'm': + member = optarg; + do_request = TRUE; + break; + + case 'r': + strncpy(buf, optarg, 15); + if ((ph = strstr(buf, "-")) != NULL) + *(ph++) = '\0'; + highest = lowest = stol(pl, FAULT_ON_ERROR, NULL); + if (ph) + highest = stol(ph, FAULT_ON_ERROR, NULL); + do_request = TRUE; + break; + default: argerrs++; break; } } + if (!argerrs && do_request) { + request_types(lowest, highest, member); + return; + } + if (argerrs || !args[optind]) cmd_usage(pc->curcmd, SYNOPSIS); @@ -8079,6 +8426,8 @@ lenptr = &array_table.prio_array_queue; else if (STREQ(s, "height_to_maxindex")) lenptr = &array_table.height_to_maxindex; + else if (STREQ(s, "height_to_maxnodes")) + lenptr = &array_table.height_to_maxnodes; else if (STREQ(s, "pid_hash")) lenptr = &array_table.pid_hash; else if (STREQ(s, "free_area")) { @@ -8091,6 +8440,10 @@ lenptr = &array_table.kmem_cache_cpu_slab; else if (STREQ(s, "rt_prio_array.queue")) lenptr = &array_table.rt_prio_array_queue; + else if (STREQ(s, "task_struct.rlim")) + lenptr = &array_table.task_struct_rlim; + else if (STREQ(s, "signal_struct.rlim")) + lenptr = &array_table.signal_struct_rlim; if (!lenptr) /* not stored */ return(len); @@ -8172,6 +8525,8 @@ OFFSET(task_struct_tss_ksp)); fprintf(fp, " task_struct_thread_eip: %ld\n", OFFSET(task_struct_thread_eip)); + fprintf(fp, " inactive_task_frame_ret_addr: %ld\n", + OFFSET(inactive_task_frame_ret_addr)); fprintf(fp, " task_struct_thread_esp: %ld\n", OFFSET(task_struct_thread_esp)); fprintf(fp, " task_struct_thread_ksp: %ld\n", @@ -8250,6 +8605,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", @@ -8258,6 +8615,8 @@ OFFSET(task_struct_prio)); fprintf(fp, " task_struct_on_rq: %ld\n", OFFSET(task_struct_on_rq)); + fprintf(fp, " task_struct_policy: %ld\n", + OFFSET(task_struct_policy)); fprintf(fp, " thread_info_task: %ld\n", OFFSET(thread_info_task)); @@ -8275,6 +8634,10 @@ fprintf(fp, " mnt_namespace_list: %ld\n", OFFSET(mnt_namespace_list)); + fprintf(fp, " pid_namespace_idr: %ld\n", + OFFSET(pid_namespace_idr)); + fprintf(fp, " idr_idr_rt: %ld\n", + OFFSET(idr_idr_rt)); fprintf(fp, " pid_link_pid: %ld\n", OFFSET(pid_link_pid)); fprintf(fp, " pid_hash_chain: %ld\n", @@ -8605,6 +8968,7 @@ fprintf(fp, " tnt_bit: %ld\n", OFFSET(tnt_bit)); fprintf(fp, " tnt_true: %ld\n", OFFSET(tnt_true)); fprintf(fp, " tnt_false: %ld\n", OFFSET(tnt_false)); + fprintf(fp, " tnt_mod: %ld\n", OFFSET(tnt_mod)); fprintf(fp, " page_next: %ld\n", OFFSET(page_next)); fprintf(fp, " page_prev: %ld\n", OFFSET(page_prev)); @@ -8724,6 +9088,8 @@ OFFSET(irq_data_chip)); fprintf(fp, " irq_data_affinity: %ld\n", OFFSET(irq_data_affinity)); + fprintf(fp, " irq_desc_irq_data: %ld\n", + OFFSET(irq_desc_irq_data)); fprintf(fp, " kernel_stat_irqs: %ld\n", OFFSET(kernel_stat_irqs)); @@ -9035,6 +9401,8 @@ OFFSET(kmem_cache_name)); fprintf(fp, " kmem_cache_list: %ld\n", OFFSET(kmem_cache_list)); + fprintf(fp, " kmem_cache_red_left_pad: %ld\n", + OFFSET(kmem_cache_red_left_pad)); fprintf(fp, " kmem_cache_node: %ld\n", OFFSET(kmem_cache_node)); fprintf(fp, " kmem_cache_cpu_slab: %ld\n", @@ -9045,6 +9413,8 @@ OFFSET(kmem_cache_cpu_cache)); fprintf(fp, " kmem_cache_oo: %ld\n", OFFSET(kmem_cache_oo)); + fprintf(fp, " kmem_cache_random: %ld\n", + OFFSET(kmem_cache_random)); fprintf(fp, " kmem_cache_node_nr_partial: %ld\n", OFFSET(kmem_cache_node_nr_partial)); @@ -9054,6 +9424,8 @@ OFFSET(kmem_cache_node_partial)); fprintf(fp, " kmem_cache_node_full: %ld\n", OFFSET(kmem_cache_node_full)); + fprintf(fp, " kmem_cache_node_total_objects: %ld\n", + OFFSET(kmem_cache_node_total_objects)); fprintf(fp, " kmem_cache_cpu_freelist: %ld\n", OFFSET(kmem_cache_cpu_freelist)); @@ -9148,6 +9520,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)); @@ -9470,6 +9844,8 @@ OFFSET(radix_tree_node_slots)); fprintf(fp, " radix_tree_node_height: %ld\n", OFFSET(radix_tree_node_height)); + fprintf(fp, " radix_tree_node_shift: %ld\n", + OFFSET(radix_tree_node_shift)); fprintf(fp, " rb_root_rb_node: %ld\n", OFFSET(rb_root_rb_node)); @@ -9628,6 +10004,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", @@ -9811,6 +10195,69 @@ OFFSET(atomic_t_counter)); fprintf(fp, " percpu_counter_count: %ld\n", OFFSET(percpu_counter_count)); + fprintf(fp, " sk_buff_head_next: %ld\n", + OFFSET(sk_buff_head_next)); + fprintf(fp, " sk_buff_head_qlen: %ld\n", + OFFSET(sk_buff_head_qlen)); + fprintf(fp, " sk_buff_next: %ld\n", + OFFSET(sk_buff_next)); + fprintf(fp, " sk_buff_len: %ld\n", + OFFSET(sk_buff_len)); + fprintf(fp, " sk_buff_data: %ld\n", + OFFSET(sk_buff_data)); + fprintf(fp, " nlmsghdr_nlmsg_type: %ld\n", + OFFSET(nlmsghdr_nlmsg_type)); + fprintf(fp, " module_arch: %ld\n", + OFFSET(module_arch)); + fprintf(fp, " mod_arch_specific_num_orcs: %ld\n", + OFFSET(mod_arch_specific_num_orcs)); + fprintf(fp, "mod_arch_specific_orc_unwind_ip: %ld\n", + OFFSET(mod_arch_specific_orc_unwind_ip)); + fprintf(fp, " mod_arch_specific_orc_unwind: %ld\n", + OFFSET(mod_arch_specific_orc_unwind)); + fprintf(fp, " bpf_prog_aux: %ld\n", + OFFSET(bpf_prog_aux)); + fprintf(fp, " bpf_prog_type: %ld\n", + OFFSET(bpf_prog_type)); + fprintf(fp, " bpf_prog_tag: %ld\n", + OFFSET(bpf_prog_tag)); + fprintf(fp, " bpf_prog_jited_len: %ld\n", + OFFSET(bpf_prog_jited_len)); + fprintf(fp, " bpf_prog_bpf_func: %ld\n", + OFFSET(bpf_prog_bpf_func)); + fprintf(fp, " bpf_prog_len: %ld\n", + OFFSET(bpf_prog_len)); + fprintf(fp, " bpf_prog_pages: %ld\n", + OFFSET(bpf_prog_pages)); + fprintf(fp, " bpf_prog_insnsi: %ld\n", + OFFSET(bpf_prog_insnsi)); + fprintf(fp, " bpf_map_map_flags: %ld\n", + OFFSET(bpf_map_map_flags)); + fprintf(fp, " bpf_map_map_type: %ld\n", + OFFSET(bpf_map_map_type)); + fprintf(fp, " bpf_map_pages: %ld\n", + OFFSET(bpf_map_pages)); + fprintf(fp, " bpf_map_key_size: %ld\n", + OFFSET(bpf_map_key_size)); + fprintf(fp, " bpf_map_value_size: %ld\n", + OFFSET(bpf_map_value_size)); + fprintf(fp, " bpf_map_max_entries: %ld\n", + OFFSET(bpf_map_max_entries)); + fprintf(fp, " bpf_map_name: %ld\n", + OFFSET(bpf_map_name)); + fprintf(fp, " bpf_map_user: %ld\n", + OFFSET(bpf_map_user)); + + fprintf(fp, " bpf_prog_aux_used_map_cnt: %ld\n", + OFFSET(bpf_prog_aux_used_map_cnt)); + fprintf(fp, " bpf_prog_aux_used_maps: %ld\n", + OFFSET(bpf_prog_aux_used_maps)); + fprintf(fp, " bpf_prog_aux_load_time: %ld\n", + OFFSET(bpf_prog_aux_load_time)); + fprintf(fp, " bpf_prog_aux_user: %ld\n", + OFFSET(bpf_prog_aux_user)); + fprintf(fp, " user_struct_uid: %ld\n", + OFFSET(user_struct_uid)); fprintf(fp, "\n size_table:\n"); fprintf(fp, " page: %ld\n", SIZE(page)); @@ -9845,6 +10292,8 @@ SIZE(page_cache_bucket)); fprintf(fp, " pt_regs: %ld\n", SIZE(pt_regs)); fprintf(fp, " task_struct: %ld\n", SIZE(task_struct)); + fprintf(fp, " task_struct_flags: %ld\n", SIZE(task_struct_flags)); + fprintf(fp, " task_struct_policy: %ld\n", SIZE(task_struct_policy)); fprintf(fp, " thread_info: %ld\n", SIZE(thread_info)); fprintf(fp, " softirq_state: %ld\n", SIZE(softirq_state)); @@ -9973,6 +10422,8 @@ SIZE(pid_link)); fprintf(fp, " upid: %ld\n", SIZE(upid)); + fprintf(fp, " pid: %ld\n", + SIZE(pid)); fprintf(fp, " unwind_table: %ld\n", SIZE(unwind_table)); fprintf(fp, " rlimit: %ld\n", @@ -10037,8 +10488,30 @@ 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)); + fprintf(fp, " taint_flag: %ld\n", + SIZE(taint_flag)); + fprintf(fp, " nlmsghdr: %ld\n", + SIZE(nlmsghdr)); + fprintf(fp, " nlmsghdr_nlmsg_type: %ld\n", + SIZE(nlmsghdr_nlmsg_type)); + fprintf(fp, " sk_buff_head_qlen: %ld\n", + SIZE(sk_buff_head_qlen)); + fprintf(fp, " sk_buff_len: %ld\n", + SIZE(sk_buff_len)); + fprintf(fp, " orc_entry: %ld\n", + SIZE(orc_entry)); + fprintf(fp, " bpf_prog: %ld\n", + SIZE(bpf_prog)); + fprintf(fp, " bpf_prog_aux: %ld\n", + SIZE(bpf_prog_aux)); + fprintf(fp, " bpf_map: %ld\n", + SIZE(bpf_map)); + fprintf(fp, " bpf_insn: %ld\n", + SIZE(bpf_insn)); fprintf(fp, "\n array_table:\n"); /* @@ -10092,6 +10565,8 @@ get_array_length("prio_array.queue", NULL, SIZE(list_head))); fprintf(fp, " height_to_maxindex: %d\n", ARRAY_LENGTH(height_to_maxindex)); + fprintf(fp, " height_to_maxnodes: %d\n", + ARRAY_LENGTH(height_to_maxnodes)); fprintf(fp, " pid_hash: %d\n", ARRAY_LENGTH(pid_hash)); fprintf(fp, " kmem_cache_node: %d\n", @@ -10100,6 +10575,10 @@ ARRAY_LENGTH(kmem_cache_cpu_slab)); fprintf(fp, " rt_prio_array_queue: %d\n", ARRAY_LENGTH(rt_prio_array_queue)); + fprintf(fp, " task_struct_rlim: %d\n", + ARRAY_LENGTH(task_struct_rlim)); + fprintf(fp, " signal_struct_rlim: %d\n", + ARRAY_LENGTH(signal_struct_rlim)); if (spec) { int in_size_table, in_array_table, arrays, offsets, sizes; @@ -10649,6 +11128,11 @@ continue; } + if (lm->mod_section_data[i].flags & SEC_FOUND) { + s1++; + continue; + } + /* Update the offset information for the section */ sec_start = s1->value - syminfo.value; // sec_end = sec_start + lm->mod_section_data[i].size; @@ -10955,7 +11439,7 @@ ulong vaddr, array_entry, attribute, owner, name, address; long name_type; char buf[BUFSIZE]; - char section_name[BUFSIZE]; + char section_name[BUFSIZE/2]; ulong section_vaddr; #if defined(GDB_5_3) || defined(GDB_6_0) || defined(GDB_6_1) @@ -11114,7 +11598,7 @@ } } - BZERO(section_name, BUFSIZE); + BZERO(section_name, BUFSIZE/2); if (!read_string(name, section_name, 32)) { done = TRUE; retval = FALSE; @@ -11142,7 +11626,7 @@ buflen *= 2; } shift_string_right(req->buf, strlen(buf)); - strncpy(req->buf, buf, strlen(buf)); + BCOPY(buf, req->buf, strlen(buf)); retval = TRUE; } else { sprintf(buf, " -s %s 0x%lx", section_name, section_vaddr); @@ -11633,6 +12117,7 @@ if (lm->mod_section_data) free(lm->mod_section_data); lm->mod_section_data = (struct mod_section_data *)0; + lm->loaded_objfile = NULL; } st->flags &= ~LOAD_MODULE_SYMS; return; @@ -11669,6 +12154,7 @@ if (lm->mod_section_data) free(lm->mod_section_data); lm->mod_section_data = (struct mod_section_data *)0; + lm->loaded_objfile = NULL; } else if (lm->mod_flags & MOD_LOAD_SYMS) st->flags |= LOAD_MODULE_SYMS; } @@ -11878,13 +12364,36 @@ 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); } } + if (SADUMP_DUMPFILE() || QEMU_MEM_DUMP_NO_VMCOREINFO() || VMSS_DUMPFILE()) { + /* Need for kaslr_offset and phys_base */ + if (STREQ(x->name, "divide_error")) + st->divide_error_vmlinux = valueof(x); + else if (STREQ(y->name, "divide_error")) + st->divide_error_vmlinux = valueof(y); + + if (STREQ(x->name, "idt_table")) + st->idt_table_vmlinux = valueof(x); + else if (STREQ(y->name, "idt_table")) + st->idt_table_vmlinux = valueof(y); + + if (STREQ(x->name, "saved_command_line")) + st->saved_command_line_vmlinux = valueof(x); + else if (STREQ(y->name, "saved_command_line")) + st->saved_command_line_vmlinux = valueof(y); + + if (STREQ(x->name, "pti_init")) + st->pti_init_vmlinux = valueof(x); + else if (STREQ(y->name, "kaiser_init")) + st->kaiser_init_vmlinux = valueof(y); + } + xs = bfd_get_section(x); ys = bfd_get_section(y); @@ -12126,17 +12635,19 @@ patch_kernel_symbol(struct gnu_request *req) { int i, c; + long relocate_display; struct syment *sp_array[1000], *sp; if (req->name == PATCH_KERNEL_SYMBOLS_START) { - if (kt->flags & RELOC_FORCE) - error(WARNING, - "\nkernel relocated [%ldMB]: patching %ld gdb minimal_symbol values\n", - kt->relocate >> 20, st->symcnt); - if (kt->flags2 & RELOC_AUTO) + if (kt->relocate) { + if ((long)kt->relocate < 0) + relocate_display = (kt->relocate * -1) >> 20; + else + relocate_display = kt->relocate >> 20; error(WARNING, "\nkernel relocated [%ldMB]: patching %ld gdb minimal_symbol values\n", - (kt->relocate * -1) >> 20, st->symcnt); + relocate_display, st->symcnt); + } fprintf(fp, (pc->flags & SILENT) || !(pc->flags & TTY) ? "" : "\nplease wait... (patching %ld gdb minimal_symbol values) ", st->symcnt); diff -Nru crash-7.1.4/task.c crash-7.2.3+real/task.c --- crash-7.1.4/task.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/task.c 2018-05-17 17:39:38.000000000 +0000 @@ -1,8 +1,8 @@ /* task.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-2018 David Anderson + * Copyright (C) 2002-2018 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 @@ -30,7 +30,9 @@ static void refresh_hlist_task_table_v2(void); static void refresh_hlist_task_table_v3(void); static void refresh_active_task_table(void); -static struct task_context *store_context(struct task_context *, ulong, char *); +static int radix_tree_task_callback(ulong); +static void refresh_radix_tree_task_table(void); +static struct task_context *add_context(ulong, char *); static void refresh_context(ulong, ulong); static ulong parent_of(ulong); static void parent_list(ulong); @@ -55,6 +57,7 @@ static long cpu_idx(int); static void dump_runq(void); static void dump_on_rq_timestamp(void); +static void dump_on_rq_lag(void); static void dump_on_rq_milliseconds(void); static void dump_runqueues(void); static void dump_prio_array(int, ulong, char *); @@ -108,6 +111,25 @@ 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); +static int has_sched_policy(ulong, ulong); +static ulong task_policy(ulong); +static ulong sched_policy_bit_from_str(const char *); +static ulong make_sched_policy(const char *); + +static struct sched_policy_info { + ulong value; + char *name; +} sched_policy_info[] = { + { SCHED_NORMAL, "NORMAL" }, + { SCHED_FIFO, "FIFO" }, + { SCHED_RR, "RR" }, + { SCHED_BATCH, "BATCH" }, + { SCHED_ISO, "ISO" }, + { SCHED_IDLE, "IDLE" }, + { SCHED_DEADLINE, "DEADLINE" }, + { ULONG_MAX, NULL } +}; /* * Figure out how much space will be required to hold the task context @@ -212,21 +234,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)) { + 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)) { - 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"); + 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; } @@ -249,6 +293,9 @@ MEMBER_OFFSET_INIT(task_struct_active_mm, "task_struct", "active_mm"); MEMBER_OFFSET_INIT(task_struct_next_run, "task_struct", "next_run"); MEMBER_OFFSET_INIT(task_struct_flags, "task_struct", "flags"); + MEMBER_SIZE_INIT(task_struct_flags, "task_struct", "flags"); + MEMBER_OFFSET_INIT(task_struct_policy, "task_struct", "policy"); + MEMBER_SIZE_INIT(task_struct_policy, "task_struct", "policy"); MEMBER_OFFSET_INIT(task_struct_pidhash_next, "task_struct", "pidhash_next"); MEMBER_OFFSET_INIT(task_struct_pgrp, "task_struct", "pgrp"); @@ -391,15 +438,67 @@ len = SIZE(task_union)); machdep->stacksize = len; } else if (VALID_SIZE(thread_union) && - ((len = SIZE(thread_union)) != STACKSIZE())) + ((len = SIZE(thread_union)) != STACKSIZE())) { machdep->stacksize = len; + } else { + if (kernel_symbol_exists("__start_init_task") && + kernel_symbol_exists("__end_init_task")) { + len = symbol_value("__end_init_task"); + len -= symbol_value("__start_init_task"); + ASSIGN_SIZE(thread_union) = len; + machdep->stacksize = len; + } + } + + MEMBER_OFFSET_INIT(pid_namespace_idr, "pid_namespace", "idr"); + MEMBER_OFFSET_INIT(idr_idr_rt, "idr", "idr_rt"); + + if (symbol_exists("height_to_maxindex") || + symbol_exists("height_to_maxnodes")) { + int newver = symbol_exists("height_to_maxnodes"); + int tmp ATTRIBUTE_UNUSED; + if (!newver) { + if (LKCD_KERNTYPES()) + ARRAY_LENGTH_INIT_ALT(tmp, "height_to_maxindex", + "radix_tree_preload.nodes", NULL, 0); + else + ARRAY_LENGTH_INIT(tmp, height_to_maxindex, + "height_to_maxindex", NULL, 0); + } else { + if (LKCD_KERNTYPES()) + ARRAY_LENGTH_INIT_ALT(tmp, "height_to_maxnodes", + "radix_tree_preload.nodes", NULL, 0); + else + ARRAY_LENGTH_INIT(tmp, height_to_maxnodes, + "height_to_maxnodes", NULL, 0); + } + STRUCT_SIZE_INIT(radix_tree_root, "radix_tree_root"); + STRUCT_SIZE_INIT(radix_tree_node, "radix_tree_node"); + MEMBER_OFFSET_INIT(radix_tree_root_height, + "radix_tree_root","height"); + MEMBER_OFFSET_INIT(radix_tree_root_rnode, + "radix_tree_root","rnode"); + MEMBER_OFFSET_INIT(radix_tree_node_slots, + "radix_tree_node","slots"); + MEMBER_OFFSET_INIT(radix_tree_node_height, + "radix_tree_node","height"); + MEMBER_OFFSET_INIT(radix_tree_node_shift, + "radix_tree_node","shift"); + } if (symbol_exists("pidhash") && symbol_exists("pid_hash") && !symbol_exists("pidhash_shift")) error(FATAL, "pidhash and pid_hash both exist -- cannot distinquish between them\n"); - if (symbol_exists("pid_hash") && symbol_exists("pidhash_shift")) { + if (VALID_MEMBER(pid_namespace_idr)) { + STRUCT_SIZE_INIT(pid, "pid"); + tt->refresh_task_table = refresh_radix_tree_task_table; + tt->pid_radix_tree = symbol_value("init_pid_ns") + + OFFSET(pid_namespace_idr) + OFFSET(idr_idr_rt); + tt->flags |= PID_RADIX_TREE; + + } else if (symbol_exists("pid_hash") && symbol_exists("pidhash_shift")) { int pidhash_shift; if (get_symbol_type("PIDTYPE_PID", NULL, &req) != @@ -476,11 +575,7 @@ tt->flags |= PIDHASH; } - /* - * Get the IRQ stacks info if it's configured. - */ - if (VALID_STRUCT(irq_ctx)) - irqstacks_init(); + tt->pf_kthread = UNINITIALIZED; get_active_set(); @@ -492,8 +587,15 @@ if (tt->flags & TASK_REFRESH_OFF) tt->flags &= ~(TASK_REFRESH|TASK_REFRESH_OFF); + /* + * Get the IRQ stacks info if it's configured. + */ + if (VALID_STRUCT(irq_ctx)) + irqstacks_init(); + 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); } @@ -513,6 +615,8 @@ if (pc->flags & SILENT) initialize_task_state(); + stack_overflow_check_init(); + tt->flags |= TASK_INIT_DONE; } @@ -526,6 +630,7 @@ int i; char *thread_info_buf; struct syment *hard_sp, *soft_sp; + ulong ptr, hardirq_next_sp = 0; if (!(tt->hardirq_ctx = (ulong *)calloc(NR_CPUS, sizeof(ulong)))) error(FATAL, "cannot malloc hardirq_ctx space."); @@ -538,13 +643,22 @@ thread_info_buf = GETBUF(SIZE(irq_ctx)); - if ((hard_sp = per_cpu_symbol_search("per_cpu__hardirq_ctx"))) { + if ((hard_sp = per_cpu_symbol_search("per_cpu__hardirq_ctx")) || + (hard_sp = per_cpu_symbol_search("per_cpu__hardirq_stack"))) { if ((kt->flags & SMP) && (kt->flags & PER_CPU_OFF)) { for (i = 0; i < NR_CPUS; i++) { if (!kt->__per_cpu_offset[i]) continue; - tt->hardirq_ctx[i] = hard_sp->value + - kt->__per_cpu_offset[i]; + ptr = hard_sp->value + kt->__per_cpu_offset[i]; + + if (!readmem(ptr, KVADDR, &ptr, + sizeof(void *), "hardirq ctx", + RETURN_ON_ERROR)) { + error(INFO, "cannot read hardirq_ctx[%d] at %lx\n", + i, ptr); + continue; + } + tt->hardirq_ctx[i] = ptr; } } else tt->hardirq_ctx[0] = hard_sp->value; @@ -557,28 +671,42 @@ error(WARNING, "cannot determine hardirq_ctx addresses\n"); for (i = 0; i < NR_CPUS; i++) { - if (!(tt->hardirq_ctx[i])) - continue; + if (!(tt->hardirq_ctx[i])) + continue; - if (!readmem(tt->hardirq_ctx[i], KVADDR, thread_info_buf, + if (!readmem(tt->hardirq_ctx[i], KVADDR, thread_info_buf, SIZE(irq_ctx), "hardirq thread_union", RETURN_ON_ERROR)) { - error(INFO, "cannot read hardirq_ctx[%d] at %lx\n", - i, tt->hardirq_ctx[i]); - continue; - } + error(INFO, "cannot read hardirq_ctx[%d] at %lx\n", + i, tt->hardirq_ctx[i]); + continue; + } - tt->hardirq_tasks[i] = - ULONG(thread_info_buf+OFFSET(thread_info_task)); + if (MEMBER_EXISTS("irq_ctx", "tinfo")) + tt->hardirq_tasks[i] = + ULONG(thread_info_buf+OFFSET(thread_info_task)); + else { + hardirq_next_sp = ULONG(thread_info_buf); + tt->hardirq_tasks[i] = stkptr_to_task(hardirq_next_sp); + } } - if ((soft_sp = per_cpu_symbol_search("per_cpu__softirq_ctx"))) { + if ((soft_sp = per_cpu_symbol_search("per_cpu__softirq_ctx")) || + (soft_sp = per_cpu_symbol_search("per_cpu__softirq_stack"))) { if ((kt->flags & SMP) && (kt->flags & PER_CPU_OFF)) { for (i = 0; i < NR_CPUS; i++) { if (!kt->__per_cpu_offset[i]) continue; - tt->softirq_ctx[i] = soft_sp->value + - kt->__per_cpu_offset[i]; + ptr = soft_sp->value + kt->__per_cpu_offset[i]; + + if (!readmem(ptr, KVADDR, &ptr, + sizeof(void *), "softirq ctx", + RETURN_ON_ERROR)) { + error(INFO, "cannot read softirq_ctx[%d] at %lx\n", + i, ptr); + continue; + } + tt->softirq_ctx[i] = ptr; } } else tt->softirq_ctx[0] = soft_sp->value; @@ -591,21 +719,30 @@ error(WARNING, "cannot determine softirq_ctx addresses\n"); for (i = 0; i < NR_CPUS; i++) { - if (!(tt->softirq_ctx[i])) - continue; + if (!(tt->softirq_ctx[i])) + continue; - if (!readmem(tt->softirq_ctx[i], KVADDR, thread_info_buf, - SIZE(irq_ctx), "softirq thread_union", - RETURN_ON_ERROR)) { + if (!readmem(tt->softirq_ctx[i], KVADDR, thread_info_buf, + SIZE(irq_ctx), "softirq thread_union", + RETURN_ON_ERROR)) { error(INFO, "cannot read softirq_ctx[%d] at %lx\n", - i, tt->hardirq_ctx[i]); - continue; - } - - tt->softirq_tasks[i] = - ULONG(thread_info_buf+OFFSET(thread_info_task)); - } + i, tt->hardirq_ctx[i]); + continue; + } + if (MEMBER_EXISTS("irq_ctx", "tinfo")) + tt->softirq_tasks[i] = + ULONG(thread_info_buf+OFFSET(thread_info_task)); + else { + tt->softirq_tasks[i] = stkptr_to_task(ULONG(thread_info_buf)); + /* Checking if softirq => hardirq nested stack */ + if ((tt->softirq_tasks[i] != NO_TASK) && hardirq_next_sp) { + if ((tt->softirq_ctx[i] <= hardirq_next_sp) && + (hardirq_next_sp < tt->softirq_ctx[i] + STACKSIZE())) + tt->hardirq_tasks[i] = tt->softirq_tasks[i]; + } + } + } tt->flags |= IRQSTACKS; @@ -655,6 +792,10 @@ malloc(cnt * sizeof(struct task_context)))) error(FATAL, "cannot malloc context array (%d tasks)", cnt); + if (!(tt->context_by_task = (struct task_context **) + malloc(cnt * sizeof(struct task_context*)))) + error(FATAL, "cannot malloc context_by_task array (%d tasks)", + cnt); if (!(tt->tgid_array = (struct tgid_context *) malloc(cnt * sizeof(struct tgid_context)))) error(FATAL, "cannot malloc tgid array (%d tasks)", @@ -674,6 +815,13 @@ "%scannot realloc context array (%d tasks)", (pc->flags & RUNTIME) ? "" : "\n", cnt); + if (!(tt->context_by_task = (struct task_context **) + realloc(tt->context_by_task, + cnt * sizeof(struct task_context*)))) + error(FATAL, + "%scannot realloc context_by_task array (%d tasks)", + (pc->flags & RUNTIME) ? "" : "\n", cnt); + if (!(tt->tgid_array = (struct tgid_context *) realloc(tt->tgid_array, cnt * sizeof(struct tgid_context)))) @@ -694,7 +842,6 @@ { int i; ulong *tlp; - struct task_context *tc; ulong curtask; ulong retries; ulong curpid; @@ -740,8 +887,7 @@ clear_task_cache(); - for (i = 0, tlp = (ulong *)tt->task_local, - tt->running_tasks = 0, tc = tt->context_array; + for (i = 0, tlp = (ulong *)tt->task_local, tt->running_tasks = 0; i < tt->max_tasks; i++, tlp++) { if (TASK_IN_USE(*tlp)) { if (!(tp = fill_task_struct(*tlp))) { @@ -751,10 +897,7 @@ goto retry; } - if (store_context(tc, *tlp, tp)) { - tc++; - tt->running_tasks++; - } + add_context(*tlp, tp); } } @@ -838,7 +981,6 @@ { int i; ulong *tlp; - struct task_context *tc; ulong curtask; ulong curpid; struct list_data list_data, *ld; @@ -952,8 +1094,7 @@ clear_task_cache(); - for (i = 0, tlp = (ulong *)tt->task_local, - tt->running_tasks = 0, tc = tt->context_array; + for (i = 0, tlp = (ulong *)tt->task_local, tt->running_tasks = 0; i < tt->max_tasks; i++, tlp++) { if (!(*tlp)) continue; @@ -965,14 +1106,6 @@ goto retry; } - if (task_exists(*tlp)) { - error(INFO, - "\nduplicate task address in task list: %lx\n", - *tlp); - retries++; - goto retry; - } - if (!(tp = fill_task_struct(*tlp))) { if (DUMPFILE()) continue; @@ -980,10 +1113,7 @@ goto retry; } - if (store_context(tc, *tlp, tp)) { - tc++; - tt->running_tasks++; - } + add_context(*tlp, tp); } if (DUMPFILE()) { @@ -1015,7 +1145,6 @@ char *pidhash, *tp; ulong *pp, next, pnext; int len, cnt; - struct task_context *tc; ulong curtask; ulong curpid; ulong retries; @@ -1156,8 +1285,7 @@ clear_task_cache(); - for (i = 0, tlp = (ulong *)tt->task_local, - tt->running_tasks = 0, tc = tt->context_array; + for (i = 0, tlp = (ulong *)tt->task_local, tt->running_tasks = 0; i < tt->max_tasks; i++, tlp++) { if (!(*tlp)) continue; @@ -1172,16 +1300,6 @@ goto retry_pidhash; } - if (task_exists(*tlp)) { - error(WARNING, - "%sduplicate task address found in task list: %lx\n", - DUMPFILE() ? "\n" : "", *tlp); - if (DUMPFILE()) - continue; - retries++; - goto retry_pidhash; - } - if (!(tp = fill_task_struct(*tlp))) { if (DUMPFILE()) continue; @@ -1189,10 +1307,7 @@ goto retry_pidhash; } - if (store_context(tc, *tlp, tp)) { - tc++; - tt->running_tasks++; - } + add_context(*tlp, tp); } FREEBUF(pidhash); @@ -1233,7 +1348,6 @@ char *tp; ulong next, pnext; int len, cnt; - struct task_context *tc; ulong curtask; ulong curpid; ulong retries; @@ -1368,8 +1482,7 @@ clear_task_cache(); - for (i = 0, tlp = (ulong *)tt->task_local, - tt->running_tasks = 0, tc = tt->context_array; + for (i = 0, tlp = (ulong *)tt->task_local, tt->running_tasks = 0; i < tt->max_tasks; i++, tlp++) { if (!(*tlp)) continue; @@ -1384,16 +1497,6 @@ goto retry_pid_hash; } - if (task_exists(*tlp)) { - error(WARNING, - "%sduplicate task address found in task list: %lx\n", - DUMPFILE() ? "\n" : "", *tlp); - if (DUMPFILE()) - continue; - retries++; - goto retry_pid_hash; - } - if (!(tp = fill_task_struct(*tlp))) { if (DUMPFILE()) continue; @@ -1401,10 +1504,7 @@ goto retry_pid_hash; } - if (store_context(tc, *tlp, tp)) { - tc++; - tt->running_tasks++; - } + add_context(*tlp, tp); } FREEBUF(pid_hash); @@ -1437,7 +1537,6 @@ char *nodebuf; int plen, len, cnt; long value; - struct task_context *tc; ulong curtask; ulong curpid; ulong retries; @@ -1640,8 +1739,7 @@ clear_task_cache(); - for (i = 0, tlp = (ulong *)tt->task_local, - tt->running_tasks = 0, tc = tt->context_array; + for (i = 0, tlp = (ulong *)tt->task_local, tt->running_tasks = 0; i < tt->max_tasks; i++, tlp++) { if (!(*tlp)) continue; @@ -1656,16 +1754,6 @@ goto retry_pid_hash; } - if (task_exists(*tlp)) { - error(WARNING, - "%sduplicate task address found in task list: %lx\n", - DUMPFILE() ? "\n" : "", *tlp); - if (DUMPFILE()) - continue; - retries++; - goto retry_pid_hash; - } - if (!(tp = fill_task_struct(*tlp))) { if (DUMPFILE()) continue; @@ -1673,10 +1761,7 @@ goto retry_pid_hash; } - if (store_context(tc, *tlp, tp)) { - tc++; - tt->running_tasks++; - } + add_context(*tlp, tp); } FREEBUF(pid_hash); @@ -1707,7 +1792,6 @@ ulong next, pnext, pprev; char *nodebuf; int len, cnt; - struct task_context *tc; ulong curtask; ulong curpid; ulong retries; @@ -1875,8 +1959,7 @@ clear_task_cache(); - for (i = 0, tlp = (ulong *)tt->task_local, - tt->running_tasks = 0, tc = tt->context_array; + for (i = 0, tlp = (ulong *)tt->task_local, tt->running_tasks = 0; i < tt->max_tasks; i++, tlp++) { if (!(*tlp)) continue; @@ -1891,16 +1974,6 @@ goto retry_pid_hash; } - if (task_exists(*tlp)) { - error(WARNING, - "%sduplicate task address found in task list: %lx\n", - DUMPFILE() ? "\n" : "", *tlp); - if (DUMPFILE()) - continue; - retries++; - goto retry_pid_hash; - } - if (!(tp = fill_task_struct(*tlp))) { if (DUMPFILE()) continue; @@ -1908,10 +1981,7 @@ goto retry_pid_hash; } - if (store_context(tc, *tlp, tp)) { - tc++; - tt->running_tasks++; - } + add_context(*tlp, tp); } FREEBUF(pid_hash); @@ -1942,7 +2012,6 @@ ulong upid; char *nodebuf; int len, cnt; - struct task_context *tc; ulong curtask; ulong curpid; ulong retries; @@ -2138,8 +2207,7 @@ clear_task_cache(); - for (i = 0, tlp = (ulong *)tt->task_local, - tt->running_tasks = 0, tc = tt->context_array; + for (i = 0, tlp = (ulong *)tt->task_local, tt->running_tasks = 0; i < tt->max_tasks; i++, tlp++) { if (!(*tlp)) continue; @@ -2154,16 +2222,6 @@ goto retry_pid_hash; } - if (task_exists(*tlp)) { - error(WARNING, - "%sduplicate task address found in task list: %lx\n", - DUMPFILE() ? "\n" : "", *tlp); - if (DUMPFILE()) - continue; - retries++; - goto retry_pid_hash; - } - if (!(tp = fill_task_struct(*tlp))) { if (DUMPFILE()) continue; @@ -2171,10 +2229,7 @@ goto retry_pid_hash; } - if (store_context(tc, *tlp, tp)) { - tc++; - tt->running_tasks++; - } + add_context(*tlp, tp); } FREEBUF(pid_hash); @@ -2188,13 +2243,224 @@ tt->retries = MAX(tt->retries, retries); } +/* + * Linux 4.15: pid_hash[] replaced by IDR/radix_tree + */ +static int +radix_tree_task_callback(ulong task) +{ + ulong *tlp; + + if (tt->callbacks < tt->max_tasks) { + tlp = (ulong *)tt->task_local; + tlp += tt->callbacks++; + *tlp = task; + } + + return TRUE; +} + +static void +refresh_radix_tree_task_table(void) +{ + int i, cnt; + ulong count, retries, next, curtask, curpid, upid_ns, pid_tasks_0, task; + ulong *tlp; + char *tp; + struct radix_tree_pair rtp; + char *pidbuf; + + if (DUMPFILE() && (tt->flags & TASK_INIT_DONE)) /* impossible */ + return; + + if (DUMPFILE()) { /* impossible */ + please_wait("gathering task table data"); + if (!symbol_exists("panic_threads")) + tt->flags |= POPULATE_PANIC; + } + + if (ACTIVE() && !(tt->flags & TASK_REFRESH)) + return; + + curpid = NO_PID; + curtask = NO_TASK; + + /* + * The current task's task_context entry may change, + * or the task may not even exist anymore. + */ + if (ACTIVE() && (tt->flags & TASK_INIT_DONE)) { + curtask = CURRENT_TASK(); + curpid = CURRENT_PID(); + } + + count = do_radix_tree(tt->pid_radix_tree, RADIX_TREE_COUNT, NULL); + if (CRASHDEBUG(1)) + console("do_radix_tree: count: %ld\n", count); + + retries = 0; + pidbuf = GETBUF(SIZE(pid)); + +retry_radix_tree: + if (retries && DUMPFILE()) + error(FATAL, + "\ncannot gather a stable task list via radix tree\n"); + + if ((retries == MAX_UNLIMITED_TASK_RETRIES) && + !(tt->flags & TASK_INIT_DONE)) + error(FATAL, + "\ncannot gather a stable task list via radix tree (%d retries)\n", + retries); + + if (count > tt->max_tasks) { + tt->max_tasks = count + TASK_SLUSH; + allocate_task_space(tt->max_tasks); + } + + BZERO(tt->task_local, tt->max_tasks * sizeof(void *)); + tt->callbacks = 0; + rtp.index = 0; + rtp.value = (void *)&radix_tree_task_callback; + count = do_radix_tree(tt->pid_radix_tree, RADIX_TREE_DUMP_CB, &rtp); + if (CRASHDEBUG(1)) + console("do_radix_tree: count: %ld tt->callbacks: %d\n", count, tt->callbacks); + + if (count > tt->max_tasks) { + retries++; + goto retry_radix_tree; + } + + if (!hq_open()) { + error(INFO, "cannot hash task_struct entries\n"); + if (!(tt->flags & TASK_INIT_DONE)) + clean_exit(1); + error(INFO, "using stale task_structs\n"); + return; + } + + /* + * Get the idle threads first. + */ + cnt = 0; + for (i = 0; i < kt->cpus; i++) { + if (!tt->idle_threads[i]) + continue; + if (hq_enter(tt->idle_threads[i])) + cnt++; + else + error(WARNING, "%sduplicate idle tasks?\n", + DUMPFILE() ? "\n" : ""); + } + + for (i = 0; i < tt->max_tasks; i++) { + tlp = (ulong *)tt->task_local; + tlp += i; + if ((next = *tlp) == 0) + break; + + /* + * Translate radix tree contents to PIDTYPE_PID task. + * - the radix tree contents are struct pid pointers + * - upid is contained in pid.numbers[0] + * - upid.ns should point to init->init_pid_ns + * - pid->tasks[0] is first hlist_node in task->pids[3] + * - get task from address of task->pids[0] + */ + if (!readmem(next, KVADDR, pidbuf, + SIZE(pid), "pid", RETURN_ON_ERROR|QUIET)) { + error(INFO, "\ncannot read pid struct from radix tree\n"); + if (DUMPFILE()) + continue; + hq_close(); + retries++; + goto retry_radix_tree; + } + + upid_ns = ULONG(pidbuf + OFFSET(pid_numbers) + OFFSET(upid_ns)); + if (upid_ns != tt->init_pid_ns) + continue; + pid_tasks_0 = ULONG(pidbuf + OFFSET(pid_tasks)); + if (!pid_tasks_0) + continue; + task = pid_tasks_0 - OFFSET(task_struct_pids); + + if (CRASHDEBUG(1)) + console("pid: %lx ns: %lx tasks[0]: %lx task: %lx\n", + next, upid_ns, pid_tasks_0, task); + + if (is_idle_thread(task)) + continue; + + if (!IS_TASK_ADDR(task)) { + error(INFO, "%s: IDR radix tree: invalid task address: %lx\n", + DUMPFILE() ? "\n" : "", task); + if (DUMPFILE()) + break; + hq_close(); + retries++; + goto retry_radix_tree; + } + + if (!hq_enter(task)) { + error(INFO, "%s: IDR radix tree: duplicate task: %lx\n", + DUMPFILE() ? "\n" : "", task); + if (DUMPFILE()) + break; + hq_close(); + retries++; + goto retry_radix_tree; + } + + cnt++; + } + + BZERO(tt->task_local, tt->max_tasks * sizeof(void *)); + cnt = retrieve_list((ulong *)tt->task_local, cnt); + hq_close(); + + clear_task_cache(); + + for (i = 0, tlp = (ulong *)tt->task_local, tt->running_tasks = 0; + i < tt->max_tasks; i++, tlp++) { + if (!(*tlp)) + continue; + + if (!IS_TASK_ADDR(*tlp)) { + error(WARNING, + "%sinvalid task address found in task list: %lx\n", + DUMPFILE() ? "\n" : "", *tlp); + if (DUMPFILE()) + continue; + retries++; + goto retry_radix_tree; + } + + if (!(tp = fill_task_struct(*tlp))) { + if (DUMPFILE()) + continue; + retries++; + goto retry_radix_tree; + } + + add_context(*tlp, tp); + } + + FREEBUF(pidbuf); + + please_wait_done(); + + if (ACTIVE() && (tt->flags & TASK_INIT_DONE)) + refresh_context(curtask, curpid); + + tt->retries = MAX(tt->retries, retries); +} + static void refresh_active_task_table(void) { int i; char *tp; int cnt; - struct task_context *tc; ulong curtask; ulong curpid; ulong retries; @@ -2255,8 +2521,7 @@ clear_task_cache(); - for (i = 0, tlp = (ulong *)tt->task_local, - tt->running_tasks = 0, tc = tt->context_array; + for (i = 0, tlp = (ulong *)tt->task_local, tt->running_tasks = 0; i < tt->max_tasks; i++, tlp++) { if (!(*tlp)) continue; @@ -2271,16 +2536,6 @@ goto retry_active; } - if (task_exists(*tlp)) { - error(WARNING, - "%sduplicate task address found in task list: %lx\n", - DUMPFILE() ? "\n" : "", *tlp); - if (DUMPFILE()) - continue; - retries++; - goto retry_active; - } - if (!(tp = fill_task_struct(*tlp))) { if (DUMPFILE()) continue; @@ -2288,10 +2543,7 @@ goto retry_active; } - if (store_context(tc, *tlp, tp)) { - tc++; - tt->running_tasks++; - } else if (DUMPFILE()) + if (!add_context(*tlp, tp) && DUMPFILE()) error(WARNING, "corrupt/invalid active task: %lx\n", *tlp); } @@ -2312,11 +2564,11 @@ } /* - * Fill a task_context structure with the data from a task. If a NULL - * task_context pointer is passed in, use the next available one. + * Initialize and return a new task_context structure with data from a task. + * NULL is returned on error. */ static struct task_context * -store_context(struct task_context *tc, ulong task, char *tp) +add_context(ulong task, char *tp) { pid_t *pid_addr, *tgid_addr; char *comm_addr; @@ -2325,6 +2577,7 @@ ulong *mm_addr; int has_cpu; int do_verify; + struct task_context *tc; struct tgid_context *tg; processor_addr = NULL; @@ -2344,17 +2597,22 @@ else do_verify = 0; - if (!tc) - tc = tt->context_array + tt->running_tasks; + tc = tt->context_array + tt->running_tasks; pid_addr = (pid_t *)(tp + OFFSET(task_struct_pid)); 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)) @@ -2368,7 +2626,10 @@ tc->pid = (ulong)(*pid_addr); strlcpy(tc->comm, comm_addr, TASK_COMM_LEN); - tc->processor = *processor_addr; + if (machine_type("SPARC64")) + tc->processor = *(unsigned short *)processor_addr; + else + tc->processor = *processor_addr; tc->ptask = *parent_addr; tc->mm_struct = *mm_addr; tc->task = task; @@ -2391,6 +2652,8 @@ if (has_cpu && (tt->flags & POPULATE_PANIC)) tt->panic_threads[tc->processor] = tc->task; + tt->flags &= ~INDEXED_CONTEXTS; + tt->running_tasks++; return tc; } @@ -2437,6 +2700,33 @@ } } +static int +sort_by_task(const void *arg1, const void *arg2) +{ + const struct task_context *t1, *t2; + + t1 = *(const struct task_context **)arg1; + t2 = *(const struct task_context **)arg2; + + if (t1->task == t2->task) + return 0; + + return (t1->task < t2->task) ? -1 : 1; +} + +/* sort context_by_task by task address */ +static void +sort_context_by_task(void) +{ + int i; + + for (i = 0; i < tt->running_tasks; i++) + tt->context_by_task[i] = &tt->context_array[i]; + qsort(tt->context_by_task, tt->running_tasks, + sizeof(*tt->context_by_task), sort_by_task); + tt->flags |= INDEXED_CONTEXTS; +} + /* * Sort the task_context array by PID number; for PID 0, sort by processor. */ @@ -2449,6 +2739,8 @@ qsort((void *)tt->context_array, (size_t)tt->running_tasks, sizeof(struct task_context), sort_by_pid); set_context(curtask, NO_PID); + + sort_context_by_task(); } static int @@ -2494,6 +2786,8 @@ qsort((void *)tt->context_array, (size_t)tt->running_tasks, sizeof(struct task_context), sort_by_last_run); set_context(curtask, NO_PID); + + sort_context_by_task(); } /* @@ -2824,15 +3118,21 @@ char lookfor1[BUFSIZE]; char lookfor2[BUFSIZE]; char lookfor3[BUFSIZE]; - int i; + int i, cnt, randomized; rewind(pc->tmpfile); BZERO(lookfor1, BUFSIZE); BZERO(lookfor2, BUFSIZE); BZERO(lookfor3, BUFSIZE); + randomized = FALSE; while (fgets(buf, BUFSIZE, pc->tmpfile)) { + if (STREQ(buf, " {\n")) + randomized = TRUE; + else if (randomized && STREQ(buf, " }, \n")) + randomized = FALSE; + if (strlen(lookfor2)) { fprintf(pc->saved_fp, "%s", buf); if (STRNEQ(buf, lookfor2)) @@ -2851,15 +3151,18 @@ BZERO(lookfor1, BUFSIZE); BZERO(lookfor2, BUFSIZE); BZERO(lookfor3, BUFSIZE); - sprintf(lookfor1, " %s = ", arglist[i]); + sprintf(lookfor1, "%s %s = ", + randomized ? " " : "", arglist[i]); if (STRNEQ(buf, lookfor1)) { fprintf(pc->saved_fp, "%s", buf); - if (strstr(buf, "{{\n")) - sprintf(lookfor2, " }},"); - else if (strstr(buf, "{\n")) - sprintf(lookfor2, " },"); - else if (strstr(buf, "{")) - sprintf(lookfor3, "},"); + if (strstr(buf, "{{\n")) + sprintf(lookfor2, "%s }},", + randomized ? " " : ""); + else if (strstr(buf, " = {\n")) { + cnt = count_leading_spaces(buf); + sprintf(lookfor2, "%s}", space(cnt)); + } else if (strstr(buf, "{")) + sprintf(lookfor3, "},"); break; } } @@ -2867,7 +3170,7 @@ } static char *ps_exclusive = - "-a, -t, -c, -p, -g, -l, -m, -S and -r flags are all mutually-exclusive\n"; + "-a, -t, -c, -p, -g, -l, -m, -S, -r and -A flags are all mutually-exclusive\n"; static void check_ps_exclusive(ulong flag, ulong thisflag) @@ -2894,7 +3197,7 @@ cpuspec = NULL; flag = 0; - while ((c = getopt(argcnt, args, "SgstcpkuGlmarC:")) != EOF) { + while ((c = getopt(argcnt, args, "ASgstcpkuGlmarC:y:")) != EOF) { switch(c) { case 'k': @@ -2995,6 +3298,16 @@ make_cpumask(cpuspec, psinfo.cpus, FAULT_ON_ERROR, NULL); break; + case 'y': + flag |= PS_POLICY; + psinfo.policy = make_sched_policy(optarg); + break; + + case 'A': + check_ps_exclusive(flag, PS_ACTIVE); + flag |= PS_ACTIVE; + break; + default: argerrs++; break; @@ -3138,6 +3451,8 @@ return; if ((flag & PS_KERNEL) && !is_kernel_thread(tc->task)) return; + if ((flag & PS_POLICY) && !has_sched_policy(tc->task, psi->policy)) + return; if (flag & PS_GROUP) { if (flag & (PS_LAST_RUN|PS_MSECS)) error(FATAL, "-G not supported with -%c option\n", @@ -3196,6 +3511,9 @@ task_active = is_task_active(tc->task); + if ((flag & PS_ACTIVE) && (flag & PS_SHOW_ALL) && !task_active) + return; + if (task_active) { if (hide_offline_cpu(tc->processor)) fprintf(fp, "- "); @@ -3230,7 +3548,7 @@ int print; char buf[BUFSIZE]; - if (!(flag & (PS_EXCLUSIVE|PS_NO_HEADER))) + if (!(flag & ((PS_EXCLUSIVE & ~PS_ACTIVE)|PS_NO_HEADER))) fprintf(fp, " PID PPID CPU %s ST %%MEM VSZ RSS COMM\n", flag & PS_KSTACKP ? @@ -3256,7 +3574,7 @@ tc = FIRST_CONTEXT(); for (i = 0; i < RUNNING_TASKS(); i++, tc++) - show_ps_data(flag, tc, NULL); + show_ps_data(flag, tc, psi); return; } @@ -3311,7 +3629,7 @@ if (flag & PS_TIMES) show_task_times(tc, flag); else - show_ps_data(flag, tc, NULL); + show_ps_data(flag, tc, psi); } } } @@ -3378,7 +3696,7 @@ sprintf(format, "[%c%dll%c] ", '%', c, pc->output_radix == 10 ? 'u' : 'x'); - if (psi) { + if (psi && psi->cpus) { for (c = others = 0; c < kt->cpus; c++) { if (!NUM_IN_BITMAP(psi->cpus, c)) continue; @@ -3466,7 +3784,7 @@ sprintf(format, "[%c%dll%c] ", '%', c, pc->output_radix == 10 ? 'u' : 'x'); - if (psi) { + if (psi && psi->cpus) { for (c = others = 0; c < kt->cpus; c++) { if (!NUM_IN_BITMAP(psi->cpus, c)) continue; @@ -3547,6 +3865,44 @@ } } +static char * +read_arg_string(struct task_context *tc, char *buf, ulong start, ulong end) +{ + physaddr_t paddr; + ulong uvaddr, size, cnt; + char *bufptr; + + uvaddr = start; + size = end - start; + bufptr = buf; + + while (size > 0) { + if (!uvtop(tc, uvaddr, &paddr, 0)) { + error(INFO, "cannot access user stack address: %lx\n\n", + uvaddr); + return NULL; + } + + cnt = PAGESIZE() - PAGEOFFSET(uvaddr); + + if (cnt > size) + cnt = size; + + if (!readmem(paddr, PHYSADDR, bufptr, cnt, + "user stack contents", RETURN_ON_ERROR|QUIET)) { + error(INFO, "cannot access user stack address: %lx\n\n", + uvaddr); + return NULL; + } + + uvaddr += cnt; + bufptr += cnt; + size -= cnt; + } + + return bufptr; +} + /* * Show the argv and envp strings pointed to by mm_struct->arg_start * and mm_struct->env_start. The user addresses need to broken up @@ -3557,10 +3913,7 @@ show_task_args(struct task_context *tc) { ulong arg_start, arg_end, env_start, env_end; - char *buf, *bufptr, *p1; - char *as, *ae, *es, *ee; - physaddr_t paddr; - ulong uvaddr, size, cnt; + char *buf, *p1, *end; int c, d; print_task_header(fp, tc, 0); @@ -3592,43 +3945,13 @@ env_start, env_end, env_end - env_start); } - buf = GETBUF(env_end - arg_start + 1); - - uvaddr = arg_start; - size = env_end - arg_start; - bufptr = buf; - - while (size > 0) { - if (!uvtop(tc, uvaddr, &paddr, 0)) { - error(INFO, "cannot access user stack address: %lx\n\n", - uvaddr); - goto bailout; - } - - cnt = PAGESIZE() - PAGEOFFSET(uvaddr); - - if (cnt > size) - cnt = size; - - if (!readmem(paddr, PHYSADDR, bufptr, cnt, - "user stack contents", RETURN_ON_ERROR|QUIET)) { - error(INFO, "cannot access user stack address: %lx\n\n", - uvaddr); - goto bailout; - } - - uvaddr += cnt; - bufptr += cnt; - size -= cnt; - } - - as = buf; - ae = &buf[arg_end - arg_start]; - es = &buf[env_start - arg_start]; - ee = &buf[env_end - arg_start]; + buf = GETBUF(arg_end - arg_start + 1); + end = read_arg_string(tc, buf, arg_start, arg_end); + if (!end) + goto bailout; fprintf(fp, "ARG: "); - for (p1 = as, c = 0; p1 < ae; p1++) { + for (p1 = buf, c = 0; p1 < end; p1++) { if (*p1 == NULLCHAR) { if (c) fprintf(fp, " "); @@ -3639,14 +3962,21 @@ } } + FREEBUF(buf); + + buf = GETBUF(env_end - env_start + 1); + end = read_arg_string(tc, buf, env_start, env_end); + if (!end) + goto bailout; + fprintf(fp, "\nENV: "); - for (p1 = es, c = d = 0; p1 < ee; p1++) { + for (p1 = buf, c = d = 0; p1 < end; p1++) { if (*p1 == NULLCHAR) { if (c) fprintf(fp, "\n"); c = 0; } else { - fprintf(fp, "%s%c", !c && (p1 != es) ? " " : "", *p1); + fprintf(fp, "%s%c", !c && (p1 != buf) ? " " : "", *p1); c++, d++; } } @@ -3709,12 +4039,14 @@ in_task_struct = in_signal_struct = FALSE; if (VALID_MEMBER(task_struct_rlim)) { - rlimit_index = get_array_length("task_struct.rlim", NULL, 0); + rlimit_index = (i = ARRAY_LENGTH(task_struct_rlim)) ? + i : get_array_length("task_struct.rlim", NULL, 0); in_task_struct = TRUE; } else if (VALID_MEMBER(signal_struct_rlim)) { if (!VALID_MEMBER(task_struct_signal)) error(FATAL, "cannot determine rlimit array location\n"); - rlimit_index = get_array_length("signal_struct.rlim", NULL, 0); + rlimit_index = (i = ARRAY_LENGTH(signal_struct_rlim)) ? + i : get_array_length("signal_struct.rlim", NULL, 0); in_signal_struct = TRUE; } @@ -3802,12 +4134,14 @@ KVADDR, &bt->stkptr, sizeof(void *), "thread_struct ksp", FAULT_ON_ERROR); } else { - bt->task = tc->task; - bt->tc = tc; - bt->stackbase = GET_STACKBASE(tc->task); - bt->stacktop = GET_STACKTOP(tc->task); - bt->flags |= BT_KSTACKP; - back_trace(bt); + if ((bt->stackbase = GET_STACKBASE(tc->task))) { + bt->stacktop = GET_STACKTOP(tc->task); + bt->task = tc->task; + bt->tc = tc; + bt->flags |= BT_KSTACKP; + back_trace(bt); + } else + bt->stkptr = 0; } if (bt->stkptr) @@ -4019,11 +4353,12 @@ { char buf[BUFSIZE]; - switch(tt->flags & (TIMESPEC | NO_TIMESPEC)) + switch(tt->flags & (TIMESPEC | NO_TIMESPEC | START_TIME_NSECS)) { case TIMESPEC: return TRUE; case NO_TIMESPEC: + case START_TIME_NSECS: return FALSE; default: break; @@ -4050,6 +4385,11 @@ close_tmpfile(); + if ((tt->flags & NO_TIMESPEC) && (SIZE(task_struct_start_time) == 8)) { + tt->flags &= ~NO_TIMESPEC; + tt->flags |= START_TIME_NSECS; + } + return (tt->flags & TIMESPEC ? TRUE : FALSE); } @@ -4059,8 +4399,10 @@ ulong tmp1, tmp2; ulonglong wrapped; - switch(tt->flags & (TIMESPEC | NO_TIMESPEC)) + switch(tt->flags & (TIMESPEC | NO_TIMESPEC | START_TIME_NSECS)) { + case START_TIME_NSECS: + start_time /= 1000000000ULL; /* FALLTHROUGH */ case TIMESPEC: if ((start_time * (ulonglong)machdep->hz) > current) return 0; @@ -4149,7 +4491,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; @@ -4264,15 +4606,12 @@ ulong task_to_pid(ulong task) { - int i; struct task_context *tc; - - tc = FIRST_CONTEXT(); - for (i = 0; i < RUNNING_TASKS(); i++, tc++) - if (tc->task == task) - return(tc->pid); - + tc = task_to_context(task); + if (tc != NULL) + return tc->pid; + return(NO_PID); } @@ -4299,15 +4638,24 @@ struct task_context * task_to_context(ulong task) { - int i; - struct task_context *tc; + struct task_context key, *tc, **found; + int i; + + /* Binary search the context_by_task array. */ + if (tt->flags & INDEXED_CONTEXTS) { + key.task = task; + tc = &key; + found = bsearch(&tc, tt->context_by_task, tt->running_tasks, + sizeof(*tt->context_by_task), sort_by_task); + return found ? *found : NULL; + } tc = FIRST_CONTEXT(); - for (i = 0; i < RUNNING_TASKS(); i++, tc++) + for (i = 0; i < RUNNING_TASKS(); i++, tc++) if (tc->task == task) - return tc; - - return NULL; + return tc; + + return NULL; } /* @@ -4462,7 +4810,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)); @@ -5258,13 +5612,41 @@ fill_task_struct(task); - flags = tt->last_task_read ? - ULONG(tt->task_struct + OFFSET(task_struct_flags)) : 0; + if (tt->last_task_read) { + if (SIZE(task_struct_flags) == sizeof(unsigned int)) + flags = UINT(tt->task_struct + + OFFSET(task_struct_flags)); + else + flags = ULONG(tt->task_struct + + OFFSET(task_struct_flags)); + } else + flags = 0; return flags; } /* + * Return task's policy as bitmask bit. + */ +static ulong +task_policy(ulong task) +{ + ulong policy = 0; + + fill_task_struct(task); + + if (!tt->last_task_read) + return policy; + + if (SIZE(task_struct_policy) == sizeof(unsigned int)) + policy = 1 << UINT(tt->task_struct + OFFSET(task_struct_policy)); + else + policy = 1 << ULONG(tt->task_struct + OFFSET(task_struct_policy)); + + return policy; +} + +/* * Return a task's tgid. */ ulong @@ -5350,18 +5732,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); } @@ -5477,12 +5857,8 @@ error(WARNING, "active task %lx on cpu %d not found in PID hash\n\n", task, i); - if ((tp = fill_task_struct(task))) { - if ((tc = store_context(NULL, task, tp))) - tt->running_tasks++; - else - continue; - } + if ((tp = fill_task_struct(task))) + add_context(task, tp); } } @@ -5698,7 +6074,7 @@ BZERO(&foreach_data, sizeof(struct foreach_data)); fd = &foreach_data; - while ((c = getopt(argcnt, args, "R:vomlgersStTpukcfFxhdaG")) != EOF) { + while ((c = getopt(argcnt, args, "R:vomlgersStTpukcfFxhdaGy:")) != EOF) { switch(c) { case 'R': @@ -5793,6 +6169,11 @@ fd->flags |= FOREACH_G_FLAG; break; + case 'y': + fd->flags |= FOREACH_y_FLAG; + fd->policy = make_sched_policy(optarg); + break; + default: argerrs++; break; @@ -5941,6 +6322,18 @@ continue; } + /* + * Select only user-space thread group leaders + */ + if (STREQ(args[optind], "gleader")) { + if (fd->flags & FOREACH_KERNEL) + error(FATAL, + "gleader and kernel are mutually exclusive!\n"); + fd->flags |= (FOREACH_USER|FOREACH_GLEADER); + optind++; + continue; + } + /* * Select only active tasks (dumpfile only) */ @@ -6275,6 +6668,9 @@ if ((fd->flags & FOREACH_USER) && is_kernel_thread(tc->task)) continue; + if ((fd->flags & FOREACH_GLEADER) && tc->pid != task_tgid(tc->task)) + continue; + if ((fd->flags & FOREACH_KERNEL) && !is_kernel_thread(tc->task)) continue; @@ -6362,8 +6758,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 @@ -6455,6 +6851,10 @@ cmdflags |= PS_GROUP; if (fd->flags & FOREACH_s_FLAG) cmdflags |= PS_KSTACKP; + if (fd->flags & FOREACH_y_FLAG) { + cmdflags |= PS_POLICY; + psinfo.policy = fd->policy; + } /* * mutually exclusive flags */ @@ -6651,6 +7051,8 @@ fd->keyword_array[0] = FOREACH_BT; if (machine_type("S390X")) fd->flags |= FOREACH_o_FLAG; + else if (machine_type("ARM64")) + fd->flags |= FOREACH_t_FLAG; else fd->flags |= (FOREACH_t_FLAG|FOREACH_o_FLAG); @@ -6724,10 +7126,8 @@ * If the task list was corrupted, add this one in. */ if ((tp = fill_task_struct(lasttask))) { - if ((tc = store_context(NULL, lasttask, tp))) { - tt->running_tasks++; + if ((tc = add_context(lasttask, tp))) return tc; - } } } @@ -6872,6 +7272,7 @@ fprintf(fp, " .tc_next: %lx\n", (ulong)tc->tc_next); } fprintf(fp, " context_array: %lx\n", (ulong)tt->context_array); + fprintf(fp, " context_by_task: %lx\n", (ulong)tt->context_by_task); fprintf(fp, " tgid_array: %lx\n", (ulong)tt->tgid_array); fprintf(fp, " tgid_searches: %ld\n", tt->tgid_searches); fprintf(fp, " tgid_cache_hits: %ld (%ld%%)\n", tt->tgid_cache_hits, @@ -6895,6 +7296,8 @@ fprintf(fp, "refresh_hlist_task_table_v3()\n"); else if (tt->refresh_task_table == refresh_active_task_table) fprintf(fp, "refresh_active_task_table()\n"); + else if (tt->refresh_task_table == refresh_radix_tree_task_table) + fprintf(fp, "refresh_radix_tree_task_table()\n"); else fprintf(fp, "%lx\n", (ulong)tt->refresh_task_table); @@ -6931,9 +7334,15 @@ if (tt->flags & PID_HASH) sprintf(&buf[strlen(buf)], "%sPID_HASH", others++ ? "|" : ""); + if (tt->flags & PID_RADIX_TREE) + sprintf(&buf[strlen(buf)], + "%sPID_RADIX_TREE", others++ ? "|" : ""); 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++ ? "|" : ""); @@ -6943,9 +7352,15 @@ if (tt->flags & NO_TIMESPEC) sprintf(&buf[strlen(buf)], "%sNO_TIMESPEC", others++ ? "|" : ""); + if (tt->flags & START_TIME_NSECS) + sprintf(&buf[strlen(buf)], + "%sSTART_TIME_NSECS", others++ ? "|" : ""); if (tt->flags & ACTIVE_ONLY) sprintf(&buf[strlen(buf)], "%sACTIVE_ONLY", others++ ? "|" : ""); + if (tt->flags & INDEXED_CONTEXTS) + sprintf(&buf[strlen(buf)], + "%sINDEXED_CONTEXTS", others++ ? "|" : ""); sprintf(&buf[strlen(buf)], ")"); if (strlen(buf) > 54) @@ -6957,6 +7372,8 @@ fprintf(fp, " task_end: %lx\n", tt->task_end); fprintf(fp, " task_local: %lx\n", (ulong)tt->task_local); fprintf(fp, " max_tasks: %d\n", tt->max_tasks); + fprintf(fp, " pid_radix_tree: %lx\n", tt->pid_radix_tree); + fprintf(fp, " callbacks: %d\n", tt->callbacks); fprintf(fp, " nr_threads: %d\n", tt->nr_threads); fprintf(fp, " running_tasks: %ld\n", tt->running_tasks); fprintf(fp, " retries: %ld\n", tt->retries); @@ -6974,7 +7391,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; @@ -7172,7 +7602,7 @@ if (tt->flags & THREAD_INFO) fprintf(fp, - "\nINDEX TASK/THREAD_INFO PID CPU PTASK MM_STRUCT COMM\n"); + "\nINDEX TASK/THREAD_INFO PID CPU PTASK MM_STRUCT COMM\n"); else fprintf(fp, "\nINDEX TASK PID CPU PTASK MM_STRUCT COMM\n"); @@ -7180,7 +7610,7 @@ for (i = 0; i < RUNNING_TASKS(); i++, tc++) { if (tt->flags & THREAD_INFO) fprintf(fp, - "[%3d] %08lx/%08lx %5ld %d %08lx %08lx %s\n", + "[%3d] %08lx/%08lx %5ld %d %08lx %016lx %s\n", i, tc->task, tc->thread_info, tc->pid, tc->processor, tc->ptask, (ulong)tc->mm_struct, tc->comm); @@ -7196,6 +7626,12 @@ tc = task_to_context(tg->task); fprintf(fp, "[%3d] %lx %ld (%s)\n", i, tg->task, tg->tgid, tc->comm); } + + fprintf(fp, "\nINDEX TASK (COMM)\n"); + for (i = 0; i < RUNNING_TASKS(); i++) { + tc = tt->context_by_task[i]; + fprintf(fp, "[%3d] %lx (%s)\n", i, tc->task, tc->comm); + } } /* @@ -7208,6 +7644,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)) @@ -7247,6 +7705,82 @@ } /* + * Checks if task policy corresponds to given mask. + */ +static int +has_sched_policy(ulong task, ulong policy) +{ + return !!(task_policy(task) & policy); +} + +/* + * Converts sched policy name into mask bit. + */ +static ulong +sched_policy_bit_from_str(const char *policy_str) +{ + struct sched_policy_info *info = NULL; + ulong policy = 0; + int found = 0; + char *upper = NULL; + /* + * Once kernel gets more than 10 scheduling policies, + * sizes of these arrays should be adjusted + */ + char digit[2] = { 0, 0 }; + char hex[4] = { 0, 0, 0, 0 }; + + upper = GETBUF(strlen(policy_str) + 1); + upper_case(policy_str, upper); + + for (info = sched_policy_info; info->name; info++) { + snprintf(digit, sizeof digit, "%lu", info->value); + /* + * Not using %#lX format here since "0X" prefix + * is not prepended if 0 value is given + */ + snprintf(hex, sizeof hex, "0X%lX", info->value); + if (strncmp(upper, info->name, strlen(info->name)) == 0 || + strncmp(upper, digit, sizeof digit) == 0 || + strncmp(upper, hex, sizeof hex) == 0) { + policy = 1 << info->value; + found = 1; + break; + } + } + + FREEBUF(upper); + + if (!found) + error(FATAL, + "%s: invalid scheduling policy\n", policy_str); + + return policy; +} + +/* + * Converts sched policy string set into bitmask. + */ +static ulong +make_sched_policy(const char *policy_str) +{ + ulong policy = 0; + char *iter = NULL; + char *orig = NULL; + char *cur = NULL; + + iter = STRDUPBUF(policy_str); + orig = iter; + + while ((cur = strsep(&iter, ","))) + policy |= sched_policy_bit_from_str(cur); + + FREEBUF(orig); + + return policy; +} + +/* * Gather an arry of pointers to the per-cpu idle tasks. The tasklist * argument must be at least the size of ulong[NR_CPUS]. There may be * junk in everything after the first entry on a single CPU box, so the @@ -7268,6 +7802,11 @@ VALID_MEMBER(runqueue_idle)) { runqbuf = GETBUF(SIZE(runqueue)); for (i = 0; i < nr_cpus; i++) { + if (machine_type("SPARC64") && + cpu_map_addr("possible") && + !(in_cpu_map(POSSIBLE, i))) + continue; + if ((kt->flags & SMP) && (kt->flags & PER_CPU_OFF)) runq = rq_sp->value + kt->__per_cpu_offset[i]; else @@ -7765,10 +8304,11 @@ ulong *cpus = NULL; int sched_debug = 0; int dump_timestamp_flag = 0; + int dump_lag_flag = 0; int dump_task_group_flag = 0; int dump_milliseconds_flag = 0; - while ((c = getopt(argcnt, args, "dtgmc:")) != EOF) { + while ((c = getopt(argcnt, args, "dtTgmc:")) != EOF) { switch(c) { case 'd': @@ -7777,6 +8317,9 @@ case 't': dump_timestamp_flag = 1; break; + case 'T': + dump_lag_flag = 1; + break; case 'm': dump_milliseconds_flag = 1; break; @@ -7794,7 +8337,7 @@ } else { pc->curcmd_flags |= CPUMASK; BZERO(arg_buf, BUFSIZE); - strncpy(arg_buf, optarg, strlen(optarg)); + strcpy(arg_buf, optarg); cpus = get_cpumask_buf(); make_cpumask(arg_buf, cpus, FAULT_ON_ERROR, NULL); pc->curcmd_private = (ulong)cpus; @@ -7812,6 +8355,8 @@ if (dump_timestamp_flag) dump_on_rq_timestamp(); + else if (dump_lag_flag) + dump_on_rq_lag(); else if (dump_milliseconds_flag) dump_on_rq_milliseconds(); else if (sched_debug) @@ -7897,6 +8442,90 @@ } /* + * Runqueue timestamp struct for dump_on_rq_lag(). + */ +struct runq_ts_info { + int cpu; + ulonglong ts; +}; + +/* + * Comparison function for dump_on_rq_lag(). + * Sorts runqueue timestamps in a descending order. + */ +static int +compare_runq_ts(const void *p1, const void *p2) +{ + const struct runq_ts_info *ts1 = p1; + const struct runq_ts_info *ts2 = p2; + + if (ts1->ts > ts2->ts) + return -1; + + if (ts1->ts < ts2->ts) + return 1; + + return 0; +} + +/* + * Calculates integer log10 + */ +static ulong +__log10ul(ulong x) +{ + ulong ret = 1; + + while (x > 9) { + ret++; + x /= 10; + } + + return ret; +} + +/* + * Displays relative CPU lag. + */ +static void +dump_on_rq_lag(void) +{ + struct syment *rq_sp; + int cpu; + ulong runq; + ulonglong timestamp; + struct runq_ts_info runq_ts[kt->cpus]; + + if (!(rq_sp = per_cpu_symbol_search("per_cpu__runqueues"))) + error(FATAL, "per-cpu runqueues do not exist\n"); + if (INVALID_MEMBER(rq_timestamp)) + option_not_supported('T'); + + for (cpu = 0; cpu < kt->cpus; cpu++) { + if ((kt->flags & SMP) && (kt->flags &PER_CPU_OFF)) + runq = rq_sp->value + kt->__per_cpu_offset[cpu]; + else + runq = rq_sp->value; + + readmem(runq + OFFSET(rq_timestamp), KVADDR, ×tamp, + sizeof(ulonglong), "per-cpu rq timestamp", + FAULT_ON_ERROR); + + runq_ts[cpu].cpu = cpu; + runq_ts[cpu].ts = timestamp; + } + + qsort(runq_ts, (size_t)kt->cpus, sizeof(struct runq_ts_info), compare_runq_ts); + + for (cpu = 0; cpu < kt->cpus; cpu++) { + fprintf(fp, "%sCPU %d: %.2lf secs\n", + space(2 + __log10ul(kt->cpus) - __log10ul(runq_ts[cpu].cpu)), + runq_ts[cpu].cpu, + ((double)runq_ts[0].ts - (double)runq_ts[cpu].ts) / 1000000000.0); + } +} + +/* * Displays the runqueue and active task timestamps of each cpu. */ static void @@ -8620,10 +9249,15 @@ MEMBER_OFFSET_INIT(sched_rt_entity_my_q, "sched_rt_entity", "my_q"); MEMBER_OFFSET_INIT(sched_entity_on_rq, "sched_entity", "on_rq"); - MEMBER_OFFSET_INIT(cfs_rq_rb_leftmost, "cfs_rq", "rb_leftmost"); - MEMBER_OFFSET_INIT(cfs_rq_nr_running, "cfs_rq", "nr_running"); MEMBER_OFFSET_INIT(cfs_rq_tasks_timeline, "cfs_rq", "tasks_timeline"); + MEMBER_OFFSET_INIT(cfs_rq_rb_leftmost, "cfs_rq", "rb_leftmost"); + if (INVALID_MEMBER(cfs_rq_rb_leftmost) && + VALID_MEMBER(cfs_rq_tasks_timeline) && + MEMBER_EXISTS("rb_root_cached", "rb_leftmost")) + ASSIGN_OFFSET(cfs_rq_rb_leftmost) = OFFSET(cfs_rq_tasks_timeline) + + MEMBER_OFFSET("rb_root_cached", "rb_leftmost"); + MEMBER_OFFSET_INIT(cfs_rq_nr_running, "cfs_rq", "nr_running"); MEMBER_OFFSET_INIT(cfs_rq_curr, "cfs_rq", "curr"); MEMBER_OFFSET_INIT(rt_rq_active, "rt_rq", "active"); MEMBER_OFFSET_INIT(task_struct_run_list, "task_struct", @@ -10043,4 +10677,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.4/tools.c crash-7.2.3+real/tools.c --- crash-7.1.4/tools.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/tools.c 2018-05-17 17:39:38.000000000 +0000 @@ -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-2017 David Anderson + * Copyright (C) 2002-2017 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 @@ -25,9 +25,19 @@ static void show_options(void); static void dump_struct_members(struct list_data *, int, ulong); static void rbtree_iteration(ulong, struct tree_data *, char *); -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 @@ -413,9 +423,10 @@ * Turn a string into upper-case. */ char * -upper_case(char *s, char *buf) +upper_case(const char *s, char *buf) { - char *p1, *p2; + const char *p1; + char *p2; p1 = s; p2 = buf; @@ -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; } @@ -2425,6 +2436,31 @@ return; + } else if (STREQ(args[optind], "redzone")) { + if (args[optind+1]) { + optind++; + if (STREQ(args[optind], "on")) + pc->flags2 |= REDZONE; + else if (STREQ(args[optind], "off")) + pc->flags2 &= ~REDZONE; + else if (IS_A_NUMBER(args[optind])) { + value = stol(args[optind], + FAULT_ON_ERROR, NULL); + if (value) + pc->flags2 |= REDZONE; + else + pc->flags2 &= ~REDZONE; + } else + goto invalid_set_command; + } + + if (runtime) { + fprintf(fp, "redzone: %s\n", + pc->flags2 & REDZONE ? + "on" : "off"); + } + return; + } else if (XEN_HYPER_MODE()) { error(FATAL, "invalid argument for the Xen hypervisor\n"); } else if (pc->flags & MINIMAL_MODE) { @@ -2520,7 +2556,9 @@ fprintf(fp, " namelist: %s\n", pc->namelist); fprintf(fp, " dumpfile: %s\n", pc->dumpfile); fprintf(fp, " unwind: %s\n", kt->flags & DWARF_UNWIND ? "on" : "off"); - fprintf(fp, " zero_excluded: %s\n", *diskdump_flags & ZERO_EXCLUDED ? "on" : "off"); + fprintf(fp, " zero_excluded: %s\n", + (*diskdump_flags & ZERO_EXCLUDED) || sadump_is_zero_excluded() ? + "on" : "off"); fprintf(fp, " null-stop: %s\n", *gdb_stop_print_at_null ? "on" : "off"); fprintf(fp, " gdb: %s\n", pc->flags2 & GDB_CMD_MODE ? "on" : "off"); fprintf(fp, " scope: %lx ", pc->scope); @@ -2529,6 +2567,7 @@ else fprintf(fp, "(not set)\n"); fprintf(fp, " offline: %s\n", pc->flags2 & OFFLINE_HIDE ? "hide" : "show"); + fprintf(fp, " redzone: %s\n", pc->flags2 & REDZONE ? "on" : "off"); } @@ -3227,7 +3266,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': @@ -3244,9 +3283,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': @@ -3317,7 +3360,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); } @@ -3481,6 +3524,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 @@ -3489,10 +3654,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; @@ -3578,6 +3744,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); @@ -3587,12 +3761,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; } } @@ -3632,6 +3809,12 @@ } if (next == 0) { + if (ld->flags & LIST_HEAD_FORMAT) { + error(INFO, "\ninvalid list entry: 0\n"); + if (close_hq_on_return) + hq_close(); + return -1; + } if (CRASHDEBUG(1)) console("do_list end: next:%lx\n", next); break; @@ -3743,7 +3926,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:plN")) != EOF) { switch (c) { case 't': @@ -3763,6 +3946,10 @@ break; + case 'l': + td->flags |= TREE_LINEAR_ORDER; + break; + case 'r': if (td->flags & TREE_ROOT_OFFSET_ENTERED) error(FATAL, @@ -3797,9 +3984,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': @@ -3832,12 +4023,15 @@ if (argerrs) cmd_usage(pc->curcmd, SYNOPSIS); + if ((type_flag & RADIXTREE_REQUEST) && (td->flags & TREE_LINEAR_ORDER)) + error(FATAL, "-l option is not applicable to radix trees\n"); + if ((type_flag & RADIXTREE_REQUEST) && (td->flags & TREE_NODE_OFFSET_ENTERED)) error(FATAL, "-o option is not applicable to radix trees\n"); if ((td->flags & TREE_ROOT_OFFSET_ENTERED) && (td->flags & TREE_NODE_POINTER)) - error(INFO, "-r and -N options are mutually exclusive\n"); + error(FATAL, "-r and -N options are mutually exclusive\n"); if (!args[optind]) { error(INFO, "a starting address is required\n"); @@ -3936,144 +4130,228 @@ static ulong RADIX_TREE_MAP_SIZE = UNINITIALIZED; static ulong RADIX_TREE_MAP_MASK = UNINITIALIZED; -int -do_rdtree(struct tree_data *td) +#define RADIX_TREE_ENTRY_MASK 3UL +#define RADIX_TREE_INTERNAL_NODE 1UL + +static void do_radix_tree_iter(ulong node, uint height, char *path, + ulong index, struct radix_tree_ops *ops) { - long nlen; + uint off; + + if (!hq_enter(node)) + error(FATAL, + "\nduplicate tree node: %lx\n", node); + + for (off = 0; off < RADIX_TREE_MAP_SIZE; off++) { + ulong slot; + ulong shift = (height - 1) * RADIX_TREE_MAP_SHIFT; + + readmem(node + OFFSET(radix_tree_node_slots) + + sizeof(void *) * off, KVADDR, &slot, sizeof(void *), + "radix_tree_node.slot[off]", FAULT_ON_ERROR); + if (!slot) + continue; + + if (slot & RADIX_TREE_INTERNAL_NODE) + slot &= ~RADIX_TREE_INTERNAL_NODE; + + if (height == 1) + ops->entry(node, slot, path, index | off, ops->private); + else { + ulong child_index = index | (off << shift); + char child_path[BUFSIZE]; + sprintf(child_path, "%s/%d", path, off); + do_radix_tree_iter(slot, height - 1, + child_path, child_index, ops); + } + } +} + +int do_radix_tree_traverse(ulong ptr, int is_root, struct radix_tree_ops *ops) +{ + static ulong max_height = UNINITIALIZED; ulong node_p; - uint print_radix, height; - char pos[BUFSIZE]; + long nlen; + uint height, is_internal; + unsigned char shift; + char path[BUFSIZE]; if (!VALID_STRUCT(radix_tree_root) || !VALID_STRUCT(radix_tree_node) || - !VALID_MEMBER(radix_tree_root_height) || - !VALID_MEMBER(radix_tree_root_rnode) || - !VALID_MEMBER(radix_tree_node_slots) || - !ARRAY_LENGTH(height_to_maxindex)) + ((!VALID_MEMBER(radix_tree_root_height) || + !VALID_MEMBER(radix_tree_root_rnode) || + !VALID_MEMBER(radix_tree_node_slots) || + !ARRAY_LENGTH(height_to_maxindex)) && + (!VALID_MEMBER(radix_tree_root_rnode) || + !VALID_MEMBER(radix_tree_node_shift) || + !VALID_MEMBER(radix_tree_node_slots) || + !ARRAY_LENGTH(height_to_maxnodes)))) error(FATAL, "radix trees do not exist or have changed " "their format\n"); if (RADIX_TREE_MAP_SHIFT == UNINITIALIZED) { if (!(nlen = MEMBER_SIZE("radix_tree_node", "slots"))) - error(FATAL, "cannot determine length of " + error(FATAL, "cannot determine length of " "radix_tree_node.slots[] array\n"); nlen /= sizeof(void *); RADIX_TREE_MAP_SHIFT = ffsl(nlen) - 1; RADIX_TREE_MAP_SIZE = (1UL << RADIX_TREE_MAP_SHIFT); RADIX_TREE_MAP_MASK = (RADIX_TREE_MAP_SIZE-1); - } - if (td->flags & TREE_STRUCT_RADIX_10) - print_radix = 10; - else if (td->flags & TREE_STRUCT_RADIX_16) - print_radix = 16; - else - print_radix = 0; + if (ARRAY_LENGTH(height_to_maxindex)) + max_height = ARRAY_LENGTH(height_to_maxindex); + else + max_height = ARRAY_LENGTH(height_to_maxnodes); + } - if (td->flags & TREE_NODE_POINTER) { - node_p = td->start; + height = 0; + if (!is_root) { + node_p = ptr; - if (node_p & 1) - node_p &= ~1; + if (node_p & RADIX_TREE_INTERNAL_NODE) + node_p &= ~RADIX_TREE_INTERNAL_NODE; if (VALID_MEMBER(radix_tree_node_height)) { readmem(node_p + OFFSET(radix_tree_node_height), KVADDR, &height, sizeof(uint), "radix_tree_node height", FAULT_ON_ERROR); - - if (height > ARRAY_LENGTH(height_to_maxindex)) { - fprintf(fp, "radix_tree_node at %lx\n", node_p); - dump_struct("radix_tree_node", node_p, print_radix); - error(FATAL, "height %d is greater than " - "height_to_maxindex[] index %ld\n", - height, ARRAY_LENGTH(height_to_maxindex)); - } - } else + } else if (VALID_MEMBER(radix_tree_node_shift)) { + readmem(node_p + OFFSET(radix_tree_node_shift), KVADDR, + &shift, sizeof(shift), "radix_tree_node shift", + FAULT_ON_ERROR); + height = (shift / RADIX_TREE_MAP_SHIFT) + 1; + } else error(FATAL, "-N option is not supported or applicable" " for radix trees on this architecture or kernel\n"); + if (height > max_height) + goto error_height; } else { - readmem(td->start + OFFSET(radix_tree_root_height), KVADDR, &height, - sizeof(uint), "radix_tree_root height", FAULT_ON_ERROR); - - if (height > ARRAY_LENGTH(height_to_maxindex)) { - fprintf(fp, "radix_tree_root at %lx\n", td->start); - dump_struct("radix_tree_root", (ulong)td->start, print_radix); - error(FATAL, "height %d is greater than " - "height_to_maxindex[] index %ld\n", - height, ARRAY_LENGTH(height_to_maxindex)); + if (VALID_MEMBER(radix_tree_root_height)) { + readmem(ptr + OFFSET(radix_tree_root_height), KVADDR, &height, + sizeof(uint), "radix_tree_root height", FAULT_ON_ERROR); } - readmem(td->start + OFFSET(radix_tree_root_rnode), KVADDR, &node_p, + readmem(ptr + OFFSET(radix_tree_root_rnode), KVADDR, &node_p, sizeof(void *), "radix_tree_root rnode", FAULT_ON_ERROR); + is_internal = (node_p & RADIX_TREE_INTERNAL_NODE); + if (node_p & RADIX_TREE_INTERNAL_NODE) + node_p &= ~RADIX_TREE_INTERNAL_NODE; + + if (is_internal && VALID_MEMBER(radix_tree_node_shift)) { + readmem(node_p + OFFSET(radix_tree_node_shift), KVADDR, &shift, + sizeof(shift), "radix_tree_node shift", FAULT_ON_ERROR); + height = (shift / RADIX_TREE_MAP_SHIFT) + 1; + } + + if (height > max_height) { + node_p = ptr; + goto error_height; + } } - if (node_p & 1) - node_p &= ~1; + if (CRASHDEBUG(1)) { + fprintf(fp, "radix_tree_node.slots[%ld]\n", + RADIX_TREE_MAP_SIZE); + fprintf(fp, "max_height %ld: ", max_height); + fprintf(fp, "\n"); + fprintf(fp, "pointer at %lx (is_root? %s):\n", + node_p, is_root ? "yes" : "no"); + if (is_root) + dump_struct("radix_tree_root", ptr, RADIX(ops->radix)); + else + dump_struct("radix_tree_node", node_p, RADIX(ops->radix)); + } - sprintf(pos, "root"); + if (height == 0) { + strcpy(path, "direct"); + ops->entry(node_p, node_p, path, 0, ops->private); + } else { + strcpy(path, "root"); + do_radix_tree_iter(node_p, height, path, 0, ops); + } - rdtree_iteration(node_p, td, pos, -1, height); + return 0; - return td->count; +error_height: + fprintf(fp, "radix_tree_node at %lx\n", node_p); + dump_struct("radix_tree_node", node_p, RADIX(ops->radix)); + error(FATAL, "height %d is greater than " + "maximum radix tree height index %ld\n", + height, max_height); + return -1; } -void -rdtree_iteration(ulong node_p, struct tree_data *td, char *ppos, ulong indexnum, uint height) +static void do_rdtree_entry(ulong node, ulong slot, const char *path, + ulong index, void *private) { - ulong slot; - int i, index; + struct tree_data *td = private; + static struct req_entry **e = NULL; uint print_radix; - char pos[BUFSIZE]; + int i; - if (indexnum != -1) - sprintf(pos, "%s/%ld", ppos, indexnum); - else - sprintf(pos, "%s", ppos); + 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]); + } - for (index = 0; index < RADIX_TREE_MAP_SIZE; index++) { - readmem((ulong)node_p + OFFSET(radix_tree_node_slots) + - sizeof(void *) * index, KVADDR, &slot, sizeof(void *), - "radix_tree_node.slot[index]", FAULT_ON_ERROR); - if (!slot) - continue; - if (height == 1) { - if (hq_enter(slot)) - td->count++; - else - error(FATAL, "\nduplicate tree entry: %lx\n", node_p); + td->count++; - if (td->flags & VERBOSE) - fprintf(fp, "%lx\n",slot); + if (td->flags & VERBOSE) + fprintf(fp, "%lx\n", slot); - if (td->flags & TREE_POSITION_DISPLAY) - fprintf(fp, " position: %s/%d\n", pos, index); + if (td->flags & TREE_POSITION_DISPLAY) { + fprintf(fp, " position: %s/%ld\n", + path, index & RADIX_TREE_MAP_MASK); + } - if (td->structname) { - if (td->flags & TREE_STRUCT_RADIX_10) - print_radix = 10; - else if (td->flags & TREE_STRUCT_RADIX_16) - print_radix = 16; - else - print_radix = 0; + if (td->structname) { + if (td->flags & TREE_STRUCT_RADIX_10) + print_radix = 10; + else if (td->flags & TREE_STRUCT_RADIX_16) + print_radix = 16; + else + print_radix = 0; - for (i = 0; i < td->structname_args; i++) { - switch(count_chars(td->structname[i], '.')) - { - case 0: - dump_struct(td->structname[i], - slot, print_radix); - break; - default: - dump_struct_members_for_tree(td, i, - slot); - break; - } - } + for (i = 0; i < td->structname_args; i++) { + switch (count_chars(td->structname[i], '.')) { + case 0: + dump_struct(td->structname[i], slot, print_radix); + break; + 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; } - } else - rdtree_iteration(slot, td, pos, index, height-1); + } } } +int do_rdtree(struct tree_data *td) +{ + struct radix_tree_ops ops = { + .entry = do_rdtree_entry, + .private = td, + }; + int is_root = !(td->flags & TREE_NODE_POINTER); + + if (td->flags & TREE_STRUCT_RADIX_10) + ops.radix = 10; + else if (td->flags & TREE_STRUCT_RADIX_16) + ops.radix = 16; + else + ops.radix = 0; + + do_radix_tree_traverse(td->start, is_root, &ops); + + return 0; +} + int do_rbtree(struct tree_data *td) { @@ -4103,17 +4381,41 @@ { int i; uint print_radix; - ulong struct_p, left_p, right_p; - char left_pos[BUFSIZE], right_pos[BUFSIZE]; + ulong struct_p, new_p, test_p; + char new_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 error(FATAL, "\nduplicate tree entry: %lx\n", node_p); + if ((td->flags & TREE_LINEAR_ORDER) && + readmem(node_p+OFFSET(rb_node_rb_left), KVADDR, &new_p, + sizeof(void *), "rb_node rb_left", RETURN_ON_ERROR) && new_p) { + if (readmem(new_p+OFFSET(rb_node_rb_left), KVADDR, &test_p, + sizeof(void *), "rb_node rb_left", RETURN_ON_ERROR|QUIET)) { + sprintf(new_pos, "%s/l", pos); + rbtree_iteration(new_p, td, new_pos); + } else + error(INFO, "rb_node: %lx: corrupted rb_left pointer: %lx\n", + node_p, new_p); + } + struct_p = node_p - td->node_member_offset; if (td->flags & VERBOSE) @@ -4136,23 +4438,39 @@ 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; } } } - readmem(node_p+OFFSET(rb_node_rb_left), KVADDR, &left_p, - sizeof(void *), "rb_node rb_left", FAULT_ON_ERROR); - readmem(node_p+OFFSET(rb_node_rb_right), KVADDR, &right_p, - sizeof(void *), "rb_node rb_right", FAULT_ON_ERROR); - - sprintf(left_pos, "%s/l", pos); - sprintf(right_pos, "%s/r", pos); + if (!(td->flags & TREE_LINEAR_ORDER) && + readmem(node_p+OFFSET(rb_node_rb_left), KVADDR, &new_p, + sizeof(void *), "rb_node rb_left", RETURN_ON_ERROR) && new_p) { + if (readmem(new_p+OFFSET(rb_node_rb_left), KVADDR, &test_p, + sizeof(void *), "rb_node rb_left", RETURN_ON_ERROR|QUIET)) { + sprintf(new_pos, "%s/l", pos); + rbtree_iteration(new_p, td, new_pos); + } else + error(INFO, "rb_node: %lx: corrupted rb_left pointer: %lx\n", + node_p, new_p); + } - rbtree_iteration(left_p, td, left_pos); - rbtree_iteration(right_p, td, right_pos); + if (readmem(node_p+OFFSET(rb_node_rb_right), KVADDR, &new_p, + sizeof(void *), "rb_node rb_right", RETURN_ON_ERROR) && new_p) { + if (readmem(new_p+OFFSET(rb_node_rb_left), KVADDR, &test_p, + sizeof(void *), "rb_node rb_left", RETURN_ON_ERROR|QUIET)) { + sprintf(new_pos, "%s/r", pos); + rbtree_iteration(new_p, td, new_pos); + } else + error(INFO, "rb_node: %lx: corrupted rb_right pointer: %lx\n", + node_p, new_p); + } } void @@ -5649,6 +5967,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.4/vmware_vmss.c crash-7.2.3+real/vmware_vmss.c --- crash-7.1.4/vmware_vmss.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/vmware_vmss.c 2018-05-17 17:39:38.000000000 +0000 @@ -2,6 +2,7 @@ * vmware_vmss.c * * Copyright (c) 2015 VMware, Inc. + * Copyright (c) 2018 Red Hat Inc. * * 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 @@ -13,7 +14,8 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * Author: Dyno Hongjun Fu + * Authors: Dyno Hongjun Fu + * Sergio Lopez */ #include "defs.h" @@ -25,6 +27,8 @@ #define VMW_PAGE_SIZE (4096) #define VMW_PAGE_SHIFT (12) +#define MAX_BLOCK_DUMP (128) + static vmssdata vmss = { 0 }; int @@ -128,7 +132,8 @@ DEBUG_PARSE_PRINT((ofp, LOGPRX"Group: %-20s offset=%#llx size=0x%#llx.\n", grps[i].name, (ulonglong)grps[i].position, (ulonglong)grps[i].size)); - if (strcmp(grps[i].name, "memory") != 0) { + if (strcmp(grps[i].name, "memory") != 0 && + (strcmp(grps[i].name, "cpu") != 0 || !machine_type("X86_64"))) { continue; } @@ -198,16 +203,11 @@ } blockpos += padsize; - if (fseek(fp, blockpos + nbytes, SEEK_SET) == -1) { - error(INFO, LOGPRX"Cannot seek past block at %#llx.\n", - (ulonglong)(blockpos + nbytes)); - break; - } - if (strcmp(name, "Memory") == 0) { /* The things that we really care about...*/ vmss.memoffset = blockpos; vmss.memsize = nbytesinmem; + vmss.separate_vmem = FALSE; DEBUG_PARSE_PRINT((ofp, "\t=> %sBLOCK: position=%#llx size=%#llx memsize=%#llx\n", compressed ? "COMPRESSED " : "", (ulonglong)blockpos, (ulonglong)nbytes, (ulonglong)nbytesinmem)); @@ -217,11 +217,61 @@ result = FALSE; goto exit; } + + if (fseek(fp, blockpos + nbytes, SEEK_SET) == -1) { + error(INFO, LOGPRX"Cannot seek past block at %#llx.\n", + (ulonglong)(blockpos + nbytes)); + break; + } + } else if (strcmp(name, "gpregs") == 0 && + nbytes == VMW_GPREGS_SIZE && + idx[0] < vmss.num_vcpus) { + int cpu = idx[0]; + if (fread(vmss.regs64[cpu], VMW_GPREGS_SIZE, 1, fp) != 1) { + error(INFO, LOGPRX"Failed to read '%s': [Error %d] %s\n", + filename, errno, strerror(errno)); + break; + } + vmss.vcpu_regs[cpu] |= REGS_PRESENT_GPREGS; + } else if (strcmp(name, "CR64") == 0 && + nbytes == VMW_CR64_SIZE && + idx[0] < vmss.num_vcpus) { + int cpu = idx[0]; + if (fread(&vmss.regs64[cpu]->cr[0], VMW_CR64_SIZE, 1, fp) != 1) { + error(INFO, LOGPRX"Failed to read '%s': [Error %d] %s\n", + filename, errno, strerror(errno)); + break; + } + vmss.vcpu_regs[cpu] |= REGS_PRESENT_CRS; + } else if (strcmp(name, "IDTR") == 0 && + nbytes == VMW_IDTR_SIZE && + idx[0] < vmss.num_vcpus) { + int cpu = idx[0]; + uint64_t idtr; + if (fseek(fp, blockpos + 2, SEEK_SET) == -1) { + error(INFO, LOGPRX"Cannot seek past block at %#llx.\n", + (ulonglong)(blockpos + 2)); + break; + } + if (fread(&idtr, sizeof(idtr), 1, fp) != 1) { + error(INFO, LOGPRX"Failed to read '%s': [Error %d] %s\n", + filename, errno, strerror(errno)); + break; + } + vmss.regs64[cpu]->idtr = idtr; + vmss.vcpu_regs[cpu] |= REGS_PRESENT_IDTR; + } else { + if (fseek(fp, blockpos + nbytes, SEEK_SET) == -1) { + error(INFO, LOGPRX"Cannot seek past block at %#llx.\n", + (ulonglong)(blockpos + nbytes)); + break; + } } } else { union { uint8_t val[TAG_VALSIZE_MASK]; uint32_t val32; + uint64_t val64; } u; unsigned k; unsigned valsize = TAG_VALSIZE(tag); @@ -253,6 +303,131 @@ if (strcmp(name, "align_mask") == 0) { vmss.alignmask = u.val32; } + } else if (strcmp(grps[i].name, "cpu") == 0) { + if (strcmp(name, "cpu:numVCPUs") == 0) { + if (vmss.regs64 != NULL) { + error(INFO, LOGPRX"Duplicated cpu:numVCPUs entry.\n"); + break; + } + + vmss.num_vcpus = u.val32; + vmss.regs64 = malloc(vmss.num_vcpus * sizeof(void *)); + vmss.vcpu_regs = malloc(vmss.num_vcpus * sizeof(uint32_t)); + + for (k = 0; k < vmss.num_vcpus; k++) { + vmss.regs64[k] = malloc(sizeof(vmssregs64)); + memset(vmss.regs64[k], 0, sizeof(vmssregs64)); + vmss.vcpu_regs[k] = 0; + } + } else if (strcmp(name, "rax") == 0) { + int cpu = idx[0]; + vmss.regs64[cpu]->rax = u.val64; + vmss.vcpu_regs[cpu] |= REGS_PRESENT_RAX; + } else if (strcmp(name, "rcx") == 0) { + int cpu = idx[0]; + vmss.regs64[cpu]->rcx = u.val64; + vmss.vcpu_regs[cpu] |= REGS_PRESENT_RCX; + } else if (strcmp(name, "rdx") == 0) { + int cpu = idx[0]; + vmss.regs64[cpu]->rdx = u.val64; + vmss.vcpu_regs[cpu] |= REGS_PRESENT_RDX; + } else if (strcmp(name, "rbx") == 0) { + int cpu = idx[0]; + vmss.regs64[cpu]->rbx = u.val64; + vmss.vcpu_regs[cpu] |= REGS_PRESENT_RBX; + } else if (strcmp(name, "rbp") == 0) { + int cpu = idx[0]; + vmss.regs64[cpu]->rbp = u.val64; + vmss.vcpu_regs[cpu] |= REGS_PRESENT_RBP; + } else if (strcmp(name, "rsp") == 0) { + int cpu = idx[0]; + vmss.regs64[cpu]->rsp = u.val64; + vmss.vcpu_regs[cpu] |= REGS_PRESENT_RSP; + } else if (strcmp(name, "rsi") == 0) { + int cpu = idx[0]; + vmss.regs64[cpu]->rsi = u.val64; + vmss.vcpu_regs[cpu] |= REGS_PRESENT_RSI; + } else if (strcmp(name, "rdi") == 0) { + int cpu = idx[0]; + vmss.regs64[cpu]->rdi = u.val64; + vmss.vcpu_regs[cpu] |= REGS_PRESENT_RDI; + } else if (strcmp(name, "r8") == 0) { + int cpu = idx[0]; + vmss.regs64[cpu]->r8 = u.val64; + vmss.vcpu_regs[cpu] |= REGS_PRESENT_R8; + } else if (strcmp(name, "r9") == 0) { + int cpu = idx[0]; + vmss.regs64[cpu]->r9 = u.val64; + vmss.vcpu_regs[cpu] |= REGS_PRESENT_R9; + } else if (strcmp(name, "r10") == 0) { + int cpu = idx[0]; + vmss.regs64[cpu]->r10 = u.val64; + vmss.vcpu_regs[cpu] |= REGS_PRESENT_R10; + } else if (strcmp(name, "r11") == 0) { + int cpu = idx[0]; + vmss.regs64[cpu]->r11 = u.val64; + vmss.vcpu_regs[cpu] |= REGS_PRESENT_R11; + } else if (strcmp(name, "r12") == 0) { + int cpu = idx[0]; + vmss.regs64[cpu]->r12 = u.val64; + vmss.vcpu_regs[cpu] |= REGS_PRESENT_R12; + } else if (strcmp(name, "r13") == 0) { + int cpu = idx[0]; + vmss.regs64[cpu]->r13 = u.val64; + vmss.vcpu_regs[cpu] |= REGS_PRESENT_R13; + } else if (strcmp(name, "r14") == 0) { + int cpu = idx[0]; + vmss.regs64[cpu]->r14 = u.val64; + vmss.vcpu_regs[cpu] |= REGS_PRESENT_R14; + } else if (strcmp(name, "r15") == 0) { + int cpu = idx[0]; + vmss.regs64[cpu]->r15 = u.val64; + vmss.vcpu_regs[cpu] |= REGS_PRESENT_R15; + } else if (strcmp(name, "CR64") == 0) { + int cpu = idx[0]; + switch (idx[1]) { + case 0: + vmss.regs64[cpu]->cr[0] = u.val64; + vmss.vcpu_regs[cpu] |= REGS_PRESENT_CR0; + break; + case 1: + vmss.regs64[cpu]->cr[1] = u.val64; + vmss.vcpu_regs[cpu] |= REGS_PRESENT_CR1; + break; + case 2: + vmss.regs64[cpu]->cr[2] = u.val64; + vmss.vcpu_regs[cpu] |= REGS_PRESENT_CR2; + break; + case 3: + vmss.regs64[cpu]->cr[3] = u.val64; + vmss.vcpu_regs[cpu] |= REGS_PRESENT_CR3; + break; + case 4: + vmss.regs64[cpu]->cr[4] = u.val64; + vmss.vcpu_regs[cpu] |= REGS_PRESENT_CR4; + break; + } + } else if (strcmp(name, "IDTR") == 0) { + int cpu = idx[0]; + if (idx[1] == 1) + vmss.regs64[cpu]->idtr = u.val32; + else if (idx[1] == 2) { + vmss.regs64[cpu]->idtr |= (uint64_t) u.val32 << 32; + vmss.vcpu_regs[cpu] |= REGS_PRESENT_IDTR; + } + } else if (strcmp(name, "rip") == 0) { + int cpu = idx[0]; + vmss.regs64[cpu]->rip = u.val64; + vmss.vcpu_regs[cpu] |= REGS_PRESENT_RIP; + } else if (strcmp(name, "eflags") == 0) { + int cpu = idx[0]; + vmss.regs64[cpu]->rflags |= u.val32; + vmss.vcpu_regs[cpu] |= REGS_PRESENT_RFLAGS; + } else if (strcmp(name, "EFLAGS") == 0) { + int cpu = idx[0]; + vmss.regs64[cpu]->rflags |= u.val32; + vmss.vcpu_regs[cpu] |= REGS_PRESENT_RFLAGS; + } } DEBUG_PARSE_PRINT((ofp, "\n")); @@ -288,6 +463,9 @@ vmss.memsize = ftell(fp); fseek(fp, 0L, SEEK_SET); + vmss.separate_vmem = TRUE; + vmss.filename = filename; + fprintf(ofp, LOGPRX"vmem file: %s\n\n", vmem_filename); free(vmem_filename); } @@ -350,3 +528,368 @@ return SEEK_ERROR; } +void +vmware_vmss_display_regs(int cpu, FILE *ofp) +{ + if (cpu >= vmss.num_vcpus) + return; + + if (machine_type("X86_64")) { + fprintf(ofp, + " RIP: %016llx RSP: %016llx RFLAGS: %08llx\n" + " RAX: %016llx RBX: %016llx RCX: %016llx\n" + " RDX: %016llx RSI: %016llx RDI: %016llx\n" + " RBP: %016llx R8: %016llx R9: %016llx\n" + " R10: %016llx R11: %016llx R12: %016llx\n" + " R13: %016llx R14: %016llx R15: %016llx\n", + (ulonglong)vmss.regs64[cpu]->rip, + (ulonglong)vmss.regs64[cpu]->rsp, + (ulonglong)vmss.regs64[cpu]->rflags, + (ulonglong)vmss.regs64[cpu]->rax, + (ulonglong)vmss.regs64[cpu]->rbx, + (ulonglong)vmss.regs64[cpu]->rcx, + (ulonglong)vmss.regs64[cpu]->rdx, + (ulonglong)vmss.regs64[cpu]->rsi, + (ulonglong)vmss.regs64[cpu]->rdi, + (ulonglong)vmss.regs64[cpu]->rbp, + (ulonglong)vmss.regs64[cpu]->r8, + (ulonglong)vmss.regs64[cpu]->r9, + (ulonglong)vmss.regs64[cpu]->r10, + (ulonglong)vmss.regs64[cpu]->r11, + (ulonglong)vmss.regs64[cpu]->r12, + (ulonglong)vmss.regs64[cpu]->r13, + (ulonglong)vmss.regs64[cpu]->r14, + (ulonglong)vmss.regs64[cpu]->r15 + ); + } +} + +void +get_vmware_vmss_regs(struct bt_info *bt, ulong *ipp, ulong *spp) +{ + ulong ip, sp; + + ip = sp = 0; + + if (bt->tc->processor >= vmss.num_vcpus || + vmss.regs64 == NULL || + vmss.vcpu_regs[bt->tc->processor] != REGS_PRESENT_ALL) { + machdep->get_stack_frame(bt, ipp, spp); + return; + } + + if (!is_task_active(bt->task)) { + machdep->get_stack_frame(bt, ipp, spp); + return; + } + + bt->flags |= BT_DUMPFILE_SEARCH; + if (machine_type("X86_64")) + machdep->get_stack_frame(bt, ipp, spp); + else if (machine_type("X86")) + get_netdump_regs_x86(bt, ipp, spp); + if (bt->flags & BT_DUMPFILE_SEARCH) + return; + + ip = (ulong)vmss.regs64[bt->tc->processor]->rip; + sp = (ulong)vmss.regs64[bt->tc->processor]->rsp; + if (is_kernel_text(ip) && + (((sp >= GET_STACKBASE(bt->task)) && + (sp < GET_STACKTOP(bt->task))) || + in_alternate_stack(bt->tc->processor, sp))) { + *ipp = ip; + *spp = sp; + bt->flags |= BT_KERNEL_SPACE; + return; + } + + if (!is_kernel_text(ip) && + in_user_stack(bt->tc->task, sp)) + bt->flags |= BT_USER_SPACE; +} + +int +vmware_vmss_memory_dump(FILE *ofp) +{ + cptdumpheader hdr; + cptgroupdesc *grps = NULL; + unsigned grpsize; + unsigned i; + int result = TRUE; + FILE *fp = vmss.dfp; + + if (vmss.separate_vmem) { + if ((fp = fopen(vmss.filename, "r")) == NULL) { + error(INFO, LOGPRX"Failed to open '%s': %s [Error %d] %s\n", + vmss.filename, errno, strerror(errno)); + return FALSE; + } + } + + if (fseek(fp, 0, SEEK_SET) != 0) { + fprintf(ofp, "Error seeking to position 0.\n"); + fclose(fp); + return FALSE; + } + + if (fread(&hdr, sizeof(cptdumpheader), 1, fp) != 1) { + fprintf(ofp, "Failed to read vmss file: [Error %d] %s\n", + errno, strerror(errno)); + fclose(fp); + return FALSE; + } + + fprintf(ofp, "vmware_vmss:\n"); + fprintf(ofp, " Header: id=%x version=%d numgroups=%d\n", + hdr.id, hdr.version, hdr.numgroups); + + vmss.cpt64bit = (hdr.id != CPTDUMP_OLD_MAGIC_NUMBER); + fprintf(ofp, " Checkpoint is %d-bit\n", vmss.cpt64bit ? 64 : 32); + + grpsize = hdr.numgroups * sizeof (cptgroupdesc); + grps = (cptgroupdesc *) malloc(grpsize * sizeof(cptgroupdesc)); + if (grps == NULL) { + fprintf(ofp, "Failed to allocate memory! [Error %d] %s\n", + errno, strerror(errno)); + fclose(fp); + return FALSE; + } + + if (fread(grps, sizeof(cptgroupdesc), grpsize, fp) != grpsize) { + fprintf(ofp, "Failed to read vmss file: [Error %d] %s\n", + errno, strerror(errno)); + result = FALSE; + goto exit; + } + + for (i = 0; i < hdr.numgroups; i++) { + if (fseek(fp, grps[i].position, SEEK_SET) == -1) { + fprintf(ofp, "Bad offset of VMSS Group['%s'] in vmss file at %#llx.\n", + grps[i].name, (ulonglong)grps[i].position); + continue; + } + fprintf(ofp, "\nGroup: %s offset=%#llx size=0x%#llx\n", + grps[i].name, (ulonglong)grps[i].position, (ulonglong)grps[i].size); + + for (;;) { + uint16_t tag; + char name[TAG_NAMELEN_MASK + 1]; + unsigned nameLen; + unsigned nindx; + int idx[3]; + unsigned j; + int nextgroup = FALSE; + + if (fread(&tag, sizeof(tag), 1, fp) != 1) { + fprintf(ofp, "Cannot read tag.\n"); + break; + } + if (tag == NULL_TAG) + break; + + nameLen = TAG_NAMELEN(tag); + if (fread(name, nameLen, 1, fp) != 1) { + fprintf(ofp, "Cannot read tag name.\n"); + break; + } + name[nameLen] = 0; + fprintf(ofp, " Item %20s", name); + + nindx = TAG_NINDX(tag); + if (nindx > 3) { + fprintf(ofp, "Too many indexes %d (> 3).\n", nindx); + break; + } + idx[0] = idx[1] = idx[2] = NO_INDEX; + for (j= 0; j < 3; j++) { + if (j < nindx) { + if (fread(&idx[j], sizeof(idx[0]), 1, fp) != 1) { + fprintf(ofp, "Cannot read index.\n"); + nextgroup = TRUE; + break; + } + fprintf(ofp, "[%d]", idx[j]); + } else + fprintf(ofp, " "); + } + if (nextgroup) + break; + + if (IS_BLOCK_TAG(tag)) { + uint64_t nbytes; + uint64_t blockpos; + uint64_t nbytesinmem; + int compressed = IS_BLOCK_COMPRESSED_TAG(tag); + uint16_t padsize; + unsigned k, l; + char byte; + + if (fread(&nbytes, sizeof(nbytes), 1, fp) != 1) { + fprintf(ofp, "Cannot read block size.\n"); + break; + } + if (fread(&nbytesinmem, sizeof(nbytesinmem), 1, fp) != 1) { + fprintf(ofp, "Cannot read block memory size.\n"); + break; + } + if (fread(&padsize, sizeof(padsize), 1, fp) != 1) { + fprintf(ofp, "Cannot read block padding size.\n"); + break; + } + if ((blockpos = ftell(fp)) == -1) { + fprintf(ofp, "Cannot determine location within VMSS file.\n"); + break; + } + blockpos += padsize; + + fprintf(ofp, " => %sBLOCK: position=%#llx size=%#llx memsize=%#llx\n", + compressed ? "COMPRESSED " : "", + (ulonglong)blockpos, (ulonglong)nbytes, (ulonglong)nbytesinmem); + + if (nbytes && nbytes <= MAX_BLOCK_DUMP && !compressed) { + fprintf(ofp, "Hex dump: \n"); + l = 0; + for (k = 0; k < nbytes; k++) { + if (fread(&byte, 1, 1, fp) != 1) { + fprintf(ofp, "Cannot read byte.\n"); + result = FALSE; + goto exit; + } + + fprintf(ofp, " %02hhX", byte); + + if (l++ == 15) { + fprintf(ofp, "\n"); + l = 0; + } + } + if (l) + fprintf(ofp, "\n\n"); + else + fprintf(ofp, "\n"); + } else { + if (fseek(fp, blockpos + nbytes, SEEK_SET) == -1) { + fprintf(ofp, "Cannot seek past block at %#llx.\n", + (ulonglong)(blockpos + nbytes)); + result = FALSE; + goto exit; + } + } + } else { + union { + uint8_t val[TAG_VALSIZE_MASK]; + uint32_t val32; + uint64_t val64; + } u; + unsigned k; + unsigned valsize = TAG_VALSIZE(tag); + uint64_t blockpos = ftell(fp); + + fprintf(ofp, " => position=%#llx size=%#x: ", + (ulonglong)blockpos, valsize); + + if (fread(u.val, sizeof(u.val[0]), valsize, fp) != valsize) { + fprintf(ofp, "Cannot read item.\n"); + break; + } + for (k = 0; k < valsize; k++) { + /* Assume Little Endian */ + fprintf(ofp, "%02X", u.val[valsize - k - 1]); + } + + + fprintf(ofp, "\n"); + } + } + } + +exit: + if (vmss.separate_vmem) + fclose(fp); + if (grps) + free(grps); + + return result; +} + +void +dump_registers_for_vmss_dump(void) +{ + int i; + vmssregs64 *regs; + + if (!machine_type("X86_64")) { + fprintf(fp, "-r option not supported on this dumpfile type\n"); + return; + } + + for (i = 0; i < vmss.num_vcpus; i++) { + regs = vmss.regs64[i]; + + if (i) + fprintf(fp, "\n"); + + fprintf(fp, "CPU %d:\n", i); + + if (vmss.vcpu_regs[i] != REGS_PRESENT_ALL) { + fprintf(fp, "Missing registers for this CPU: 0x%x\n", vmss.vcpu_regs[i]); + continue; + } + + fprintf(fp, " RAX: %016llx RBX: %016llx RCX: %016llx\n", + (ulonglong)regs->rax, (ulonglong)regs->rbx, (ulonglong)regs->rcx); + fprintf(fp, " RDX: %016llx RSI: %016llx RDI: %016llx\n", + (ulonglong)regs->rdx, (ulonglong)regs->rsi, (ulonglong)regs->rdi); + fprintf(fp, " RSP: %016llx RBP: %016llx R8: %016llx\n", + (ulonglong)regs->rsp, (ulonglong)regs->rbp, (ulonglong)regs->r8); + fprintf(fp, " R9: %016llx R10: %016llx R11: %016llx\n", + (ulonglong)regs->r9, (ulonglong)regs->r10, (ulonglong)regs->r11); + fprintf(fp, " R12: %016llx R13: %016llx R14: %016llx\n", + (ulonglong)regs->r12, (ulonglong)regs->r13, (ulonglong)regs->r14); + fprintf(fp, " R15: %016llx RIP: %016llx RFLAGS: %08llx\n", + (ulonglong)regs->r15, (ulonglong)regs->rip, (ulonglong)regs->rflags); + fprintf(fp, " IDT: base: %016llx\n", + (ulonglong)regs->idtr); + fprintf(fp, " CR0: %016llx CR1: %016llx CR2: %016llx\n", + (ulonglong)regs->cr[0], (ulonglong)regs->cr[1], (ulonglong)regs->cr[2]); + fprintf(fp, " CR3: %016llx CR4: %016llx\n", + (ulonglong)regs->cr[3], (ulonglong)regs->cr[4]); + } +} + +int +vmware_vmss_valid_regs(struct bt_info *bt) +{ + if (vmss.vcpu_regs[bt->tc->processor] == REGS_PRESENT_ALL) + return TRUE; + + return FALSE; +} + +int +vmware_vmss_get_cr3_idtr(ulong *cr3, ulong *idtr) +{ + if (vmss.num_vcpus == 0 || vmss.vcpu_regs[0] != REGS_PRESENT_ALL) + return FALSE; + + *cr3 = vmss.regs64[0]->cr[3]; + *idtr = vmss.regs64[0]->idtr; + + return TRUE; +} + +int +vmware_vmss_phys_base(ulong *phys_base) +{ + *phys_base = vmss.phys_base; + + return TRUE; +} + +int +vmware_vmss_set_phys_base(ulong phys_base) +{ + vmss.phys_base = phys_base; + + return TRUE; +} diff -Nru crash-7.1.4/vmware_vmss.h crash-7.2.3+real/vmware_vmss.h --- crash-7.1.4/vmware_vmss.h 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/vmware_vmss.h 2018-05-17 17:39:38.000000000 +0000 @@ -89,16 +89,79 @@ }; typedef struct memregion memregion; +#define VMW_GPREGS_SIZE (128) +#define VMW_CR64_SIZE (72) +#define VMW_IDTR_SIZE (10) +struct vmssregs64 { + /* read from vmss */ + uint64_t rax; + uint64_t rcx; + uint64_t rdx; + uint64_t rbx; + uint64_t rbp; + uint64_t rsp; + uint64_t rsi; + uint64_t rdi; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + /* manually managed */ + uint64_t idtr; + uint64_t cr[VMW_CR64_SIZE / 8]; + uint64_t rip; + uint64_t rflags; +}; +typedef struct vmssregs64 vmssregs64; + +#define REGS_PRESENT_RAX 1<<0 +#define REGS_PRESENT_RCX 1<<1 +#define REGS_PRESENT_RDX 1<<2 +#define REGS_PRESENT_RBX 1<<3 +#define REGS_PRESENT_RBP 1<<4 +#define REGS_PRESENT_RSP 1<<5 +#define REGS_PRESENT_RSI 1<<6 +#define REGS_PRESENT_RDI 1<<7 +#define REGS_PRESENT_R8 1<<8 +#define REGS_PRESENT_R9 1<<9 +#define REGS_PRESENT_R10 1<<10 +#define REGS_PRESENT_R11 1<<11 +#define REGS_PRESENT_R12 1<<12 +#define REGS_PRESENT_R13 1<<13 +#define REGS_PRESENT_R14 1<<14 +#define REGS_PRESENT_R15 1<<15 +#define REGS_PRESENT_IDTR 1<<16 +#define REGS_PRESENT_CR0 1<<17 +#define REGS_PRESENT_CR1 1<<18 +#define REGS_PRESENT_CR2 1<<19 +#define REGS_PRESENT_CR3 1<<20 +#define REGS_PRESENT_CR4 1<<21 +#define REGS_PRESENT_RIP 1<<22 +#define REGS_PRESENT_RFLAGS 1<<23 +#define REGS_PRESENT_GPREGS 65535 +#define REGS_PRESENT_CRS 4063232 +#define REGS_PRESENT_ALL 16777215 + #define MAX_REGIONS 3 struct vmssdata { int32_t cpt64bit; FILE *dfp; + char *filename; /* about the memory */ uint32_t alignmask; uint32_t regionscount; memregion regions[MAX_REGIONS]; uint64_t memoffset; uint64_t memsize; + ulong phys_base; + int separate_vmem; + uint32_t *vcpu_regs; + uint64_t num_vcpus; + vmssregs64 **regs64; }; typedef struct vmssdata vmssdata; diff -Nru crash-7.1.4/x86_64.c crash-7.2.3+real/x86_64.c --- crash-7.1.4/x86_64.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/x86_64.c 2018-05-17 17:39:38.000000000 +0000 @@ -1,7 +1,7 @@ /* x86_64.c -- core analysis suite * - * Copyright (C) 2004-2015 David Anderson - * Copyright (C) 2004-2015 Red Hat, Inc. All rights reserved. + * Copyright (C) 2004-2018 David Anderson + * Copyright (C) 2004-2018 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 @@ -24,6 +24,7 @@ static int x86_64_uvtop_level4(struct task_context *, ulong, physaddr_t *, int); static int x86_64_uvtop_level4_xen_wpt(struct task_context *, ulong, physaddr_t *, int); static int x86_64_uvtop_level4_rhel4_xen_wpt(struct task_context *, ulong, physaddr_t *, int); +static int x86_64_task_uses_5level(struct task_context *); static ulong x86_64_vmalloc_start(void); static int x86_64_is_task_addr(ulong); static int x86_64_verify_symbol(const char *, ulong, char); @@ -45,6 +46,7 @@ static ulong x86_64_in_exception_stack(struct bt_info *, int *); static ulong x86_64_in_irqstack(struct bt_info *); static int x86_64_in_alternate_stack(int, ulong); +static ulong x86_64_in_kpti_entry_stack(int, ulong); static ulong __schedule_frame_adjust(ulong, struct bt_info *); static void x86_64_low_budget_back_trace_cmd(struct bt_info *); static void x86_64_dwarf_back_trace_cmd(struct bt_info *); @@ -75,11 +77,21 @@ static int x86_64_is_module_addr(ulong); static int x86_64_is_kvaddr(ulong); static int x86_64_is_uvaddr(ulong, struct task_context *); +static int x86_64_is_page_ptr(ulong, physaddr_t *); +static ulong *x86_64_kpgd_offset(ulong, int, int); +static ulong x86_64_upgd_offset(struct task_context *, ulong, int, int); +static ulong x86_64_upgd_offset_legacy(struct task_context *, ulong, int, int); +static ulong x86_64_p4d_offset(ulong, ulong, int, int); +static ulong x86_64_pud_offset(ulong, ulong, int, int); +static ulong x86_64_pmd_offset(ulong, ulong, int, int); +static ulong x86_64_pte_offset(ulong, ulong, int, int); void x86_64_compiler_warning_stub(void); static void x86_64_init_kernel_pgd(void); static void x86_64_cpu_pda_init(void); static void x86_64_per_cpu_init(void); static void x86_64_ist_init(void); +static void x86_64_irq_stack_gap_init(void); +static void x86_64_entry_trampoline_init(void); static void x86_64_post_init(void); static void parse_cmdline_args(void); static void x86_64_clear_machdep_cache(void); @@ -88,6 +100,7 @@ static ulong search_for_switch_to(ulong, ulong); static void x86_64_thread_return_init(void); static void x86_64_framepointer_init(void); +static void x86_64_ORC_init(void); static int x86_64_virt_phys_base(void); static int x86_64_xendump_p2m_create(struct xendump_data *); static int x86_64_pvops_xendump_p2m_create(struct xendump_data *); @@ -105,7 +118,8 @@ static ulong x86_64_get_stackbase_hyper(ulong); static ulong x86_64_get_stacktop_hyper(ulong); static int x86_64_framesize_cache_resize(void); -static int x86_64_framesize_cache_func(int, ulong, int *, int); +static int x86_64_do_not_cache_framesize(struct syment *, ulong); +static int x86_64_framesize_cache_func(int, ulong, int *, int, struct syment *); static ulong x86_64_get_framepointer(struct bt_info *, ulong); int search_for_eframe_target_caller(struct bt_info *, ulong, int *); static int x86_64_get_framesize(struct bt_info *, ulong, ulong); @@ -115,6 +129,13 @@ static int x86_64_verify_paddr(uint64_t); static void GART_init(void); static void x86_64_exception_stacks_init(void); +static int in_START_KERNEL_map(ulong); +static ulong orc_ip(ulong); +static kernel_orc_entry *__orc_find(ulong, ulong, uint, ulong); +static kernel_orc_entry *orc_find(ulong); +static kernel_orc_entry *orc_module_find(ulong); +static ulong ip_table_to_vaddr(ulong); +static void orc_dump(ulong); struct machine_specific x86_64_machine_specific = { 0 }; @@ -126,6 +147,7 @@ x86_64_init(int when) { int len, dim; + char *string; if (XEN_HYPER_MODE()) { x86_64_init_hyper(when); @@ -136,6 +158,7 @@ { case SETUP_ENV: machdep->process_elf_notes = x86_process_elf_notes; + machdep->is_page_ptr = x86_64_is_page_ptr; break; case PRE_SYMTAB: machdep->verify_symbol = x86_64_verify_symbol; @@ -148,20 +171,17 @@ machdep->pageoffset = machdep->pagesize - 1; machdep->pagemask = ~((ulonglong)machdep->pageoffset); machdep->stacksize = machdep->pagesize * 2; - if ((machdep->machspec->upml = (char *)malloc(PAGESIZE())) == NULL) - error(FATAL, "cannot malloc upml space."); - if ((machdep->pgd = (char *)malloc(PAGESIZE())) == NULL) + 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->pml4 = - (char *)malloc(PAGESIZE()*2)) == NULL) - error(FATAL, "cannot malloc pml4 space."); - machdep->machspec->last_upml_read = 0; - machdep->machspec->last_pml4_read = 0; + machdep->last_pgd_read = 0; + machdep->last_pud_read = 0; machdep->last_pmd_read = 0; machdep->last_ptbl_read = 0; machdep->verify_paddr = x86_64_verify_paddr; @@ -169,9 +189,24 @@ machdep->flags |= MACHDEP_BT_TEXT; machdep->flags |= FRAMESIZE_DEBUG; machdep->machspec->irq_eframe_link = UNINITIALIZED; + machdep->machspec->irq_stack_gap = UNINITIALIZED; machdep->get_kvaddr_ranges = x86_64_get_kvaddr_ranges; if (machdep->cmdline_args[0]) parse_cmdline_args(); + if ((string = pc->read_vmcoreinfo("relocate"))) { + kt->relocate = htol(string, QUIET, NULL); + kt->flags |= RELOC_SET; + kt->flags2 |= KASLR; + free(string); + } + if ((string = pc->read_vmcoreinfo("NUMBER(KERNEL_IMAGE_SIZE)"))) { + machdep->machspec->kernel_image_size = dtol(string, QUIET, NULL); + free(string); + } + if (SADUMP_DUMPFILE() || QEMU_MEM_DUMP_NO_VMCOREINFO() || + VMSS_DUMPFILE()) + /* Need for calculation of kaslr_offset and phys_base */ + machdep->kvtop = x86_64_kvtop; break; case PRE_GDB: @@ -201,10 +236,10 @@ machdep->machspec->modules_vaddr = MODULES_VADDR_ORIG; machdep->machspec->modules_end = MODULES_END_ORIG; - free(machdep->machspec->upml); - machdep->machspec->upml = NULL; - - machdep->uvtop = x86_64_uvtop; + machdep->uvtop = x86_64_uvtop; + machdep->machspec->physical_mask_shift = __PHYSICAL_MASK_SHIFT_2_6; + machdep->machspec->pgdir_shift = PGDIR_SHIFT; + machdep->machspec->ptrs_per_pgd = PTRS_PER_PGD; break; case VM_2_6_11: @@ -228,7 +263,10 @@ /* 2.6.27 layout */ machdep->machspec->page_offset = PAGE_OFFSET_2_6_27; - machdep->uvtop = x86_64_uvtop_level4; + machdep->uvtop = x86_64_uvtop_level4; + machdep->machspec->physical_mask_shift = __PHYSICAL_MASK_SHIFT_2_6; + machdep->machspec->pgdir_shift = PGDIR_SHIFT; + machdep->machspec->ptrs_per_pgd = PTRS_PER_PGD; break; case VM_XEN: @@ -239,6 +277,9 @@ machdep->machspec->vmalloc_end = VMALLOC_END_XEN; machdep->machspec->modules_vaddr = MODULES_VADDR_XEN; machdep->machspec->modules_end = MODULES_END_XEN; + machdep->machspec->physical_mask_shift = __PHYSICAL_MASK_SHIFT_XEN; + machdep->machspec->pgdir_shift = PGDIR_SHIFT; + machdep->machspec->ptrs_per_pgd = PTRS_PER_PGD; break; case VM_XEN_RHEL4: @@ -249,7 +290,29 @@ machdep->machspec->vmalloc_end = VMALLOC_END_XEN_RHEL4; machdep->machspec->modules_vaddr = MODULES_VADDR_XEN_RHEL4; machdep->machspec->modules_end = MODULES_END_XEN_RHEL4; + machdep->machspec->physical_mask_shift = __PHYSICAL_MASK_SHIFT_XEN; + machdep->machspec->pgdir_shift = PGDIR_SHIFT; + machdep->machspec->ptrs_per_pgd = PTRS_PER_PGD; break; + + case VM_5LEVEL: + machdep->machspec->userspace_top = USERSPACE_TOP_5LEVEL; + machdep->machspec->page_offset = PAGE_OFFSET_5LEVEL; + machdep->machspec->vmalloc_start_addr = VMALLOC_START_ADDR_5LEVEL; + machdep->machspec->vmalloc_end = VMALLOC_END_5LEVEL; + machdep->machspec->modules_vaddr = MODULES_VADDR_5LEVEL; + machdep->machspec->modules_end = MODULES_END_5LEVEL; + machdep->machspec->vmemmap_vaddr = VMEMMAP_VADDR_5LEVEL; + machdep->machspec->vmemmap_end = VMEMMAP_END_5LEVEL; + if (symbol_exists("vmemmap_populate")) + machdep->flags |= VMEMMAP; + machdep->machspec->physical_mask_shift = __PHYSICAL_MASK_SHIFT_5LEVEL; + machdep->machspec->pgdir_shift = PGDIR_SHIFT_5LEVEL; + machdep->machspec->ptrs_per_pgd = PTRS_PER_PGD_5LEVEL; + if ((machdep->machspec->p4d = (char *)malloc(PAGESIZE())) == NULL) + error(FATAL, "cannot malloc p4d space."); + machdep->machspec->last_p4d_read = 0; + machdep->uvtop = x86_64_uvtop_level4; /* 5-level is optional per-task */ } machdep->kvbase = (ulong)PAGE_OFFSET; machdep->identity_map_base = (ulong)PAGE_OFFSET; @@ -282,6 +345,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 +372,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) + 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"); /* @@ -499,7 +607,10 @@ } machdep->section_size_bits = _SECTION_SIZE_BITS; if (!machdep->max_physmem_bits) { - if (THIS_KERNEL_VERSION >= LINUX(2,6,31)) + if (machdep->flags & VM_5LEVEL) + machdep->max_physmem_bits = + _MAX_PHYSMEM_BITS_5LEVEL; + else if (THIS_KERNEL_VERSION >= LINUX(2,6,31)) machdep->max_physmem_bits = _MAX_PHYSMEM_BITS_2_6_31; else if (THIS_KERNEL_VERSION >= LINUX(2,6,26)) @@ -530,8 +641,10 @@ machdep->uvtop = x86_64_uvtop_level4_rhel4_xen_wpt; break; } - } else - machdep->uvtop = x86_64_uvtop_level4; + machdep->machspec->physical_mask_shift = __PHYSICAL_MASK_SHIFT_XEN; + } else { + machdep->uvtop = x86_64_uvtop_level4; + } MEMBER_OFFSET_INIT(vcpu_guest_context_user_regs, "vcpu_guest_context", "user_regs"); ASSIGN_OFFSET(cpu_user_regs_rsp) = @@ -540,7 +653,10 @@ MEMBER_OFFSET("cpu_user_regs", "cs") - sizeof(ulong); } x86_64_irq_eframe_link_init(); + x86_64_irq_stack_gap_init(); + x86_64_entry_trampoline_init(); x86_64_framepointer_init(); + x86_64_ORC_init(); x86_64_thread_return_init(); if (THIS_KERNEL_VERSION >= LINUX(2,6,28)) @@ -598,6 +714,8 @@ fprintf(fp, "%sVM_XEN", others++ ? "|" : ""); if (machdep->flags & VM_XEN_RHEL4) fprintf(fp, "%sVM_XEN_RHEL4", others++ ? "|" : ""); + if (machdep->flags & VM_5LEVEL) + fprintf(fp, "%sVM_5LEVEL", others++ ? "|" : ""); if (machdep->flags & VMEMMAP) fprintf(fp, "%sVMEMMAP", others++ ? "|" : ""); if (machdep->flags & NO_TSS) @@ -608,12 +726,18 @@ fprintf(fp, "%sPHYS_BASE", others++ ? "|" : ""); if (machdep->flags & FRAMESIZE_DEBUG) fprintf(fp, "%sFRAMESIZE_DEBUG", others++ ? "|" : ""); + if (machdep->flags & ORC) + fprintf(fp, "%sORC", others++ ? "|" : ""); if (machdep->flags & FRAMEPOINTER) fprintf(fp, "%sFRAMEPOINTER", others++ ? "|" : ""); if (machdep->flags & GART_REGION) fprintf(fp, "%sGART_REGION", others++ ? "|" : ""); if (machdep->flags & NESTED_NMI) fprintf(fp, "%sNESTED_NMI", others++ ? "|" : ""); + if (machdep->flags & RANDOMIZED) + fprintf(fp, "%sRANDOMIZED", others++ ? "|" : ""); + if (machdep->flags & KPTI) + fprintf(fp, "%sKPTI", others++ ? "|" : ""); fprintf(fp, ")\n"); fprintf(fp, " kvbase: %lx\n", machdep->kvbase); @@ -648,17 +772,22 @@ fprintf(fp, " processor_speed: x86_64_processor_speed()\n"); if (machdep->uvtop == x86_64_uvtop) fprintf(fp, " uvtop: x86_64_uvtop()\n"); - else if (machdep->uvtop == x86_64_uvtop_level4) - fprintf(fp, " uvtop: x86_64_uvtop_level4()\n"); - else if (machdep->uvtop == x86_64_uvtop_level4_xen_wpt) + else if (machdep->uvtop == x86_64_uvtop_level4) { + fprintf(fp, " uvtop: x86_64_uvtop_level4()"); + if (machdep->flags & VM_5LEVEL) + fprintf(fp, " or x86_64_uvtop_5level()"); + fprintf(fp, "\n"); + } else if (machdep->uvtop == x86_64_uvtop_level4_xen_wpt) fprintf(fp, " uvtop: x86_64_uvtop_level4_xen_wpt()\n"); else if (machdep->uvtop == x86_64_uvtop_level4_rhel4_xen_wpt) fprintf(fp, " uvtop: x86_64_uvtop_level4_rhel4_xen_wpt()\n"); else fprintf(fp, " uvtop: %lx\n", (ulong)machdep->uvtop); fprintf(fp, " kvtop: x86_64_kvtop()"); - if (XEN() && (kt->xen_flags & WRITABLE_PAGE_TABLES)) - fprintf(fp, " -> x86_64_kvtop_xen_wpt()"); + if (machdep->flags & VM_5LEVEL) + fprintf(fp, " -> x86_64_kvtop_5level()"); + else if (XEN() && (kt->xen_flags & WRITABLE_PAGE_TABLES)) + fprintf(fp, " -> x86_64_kvtop_xen_wpt()"); fprintf(fp, "\n"); fprintf(fp, " get_task_pgd: x86_64_get_task_pgd()\n"); fprintf(fp, " dump_irq: x86_64_dump_irq()\n"); @@ -677,6 +806,7 @@ fprintf(fp, " get_smp_cpus: x86_64_get_smp_cpus()\n"); fprintf(fp, " is_kvaddr: x86_64_is_kvaddr()\n"); fprintf(fp, " is_uvaddr: x86_64_is_uvaddr()\n"); + fprintf(fp, " is_page_ptr: x86_64_is_page_ptr()\n"); fprintf(fp, " verify_paddr: x86_64_verify_paddr()\n"); fprintf(fp, " get_kvaddr_ranges: x86_64_get_kvaddr_ranges()\n"); fprintf(fp, " init_kernel_pgd: x86_64_init_kernel_pgd()\n"); @@ -693,9 +823,11 @@ fprintf(fp, " value_to_symbol: x86_64_value_to_symbol()\n"); fprintf(fp, " in_alternate_stack: x86_64_in_alternate_stack()\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, " 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); @@ -720,19 +852,61 @@ 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: "); + if (ms->kernel_image_size) + fprintf(fp, "%lx (%ldMB)\n", ms->kernel_image_size, + ms->kernel_image_size/MEGABYTES(1)); + else + fprintf(fp, "(uninitialized)\n"); + fprintf(fp, " physical_mask_shift: %ld\n", ms->physical_mask_shift); + fprintf(fp, " pgdir_shift: %ld\n", ms->pgdir_shift); 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); - fprintf(fp, " last_pml4_read: %lx\n", (ulong)ms->last_pml4_read); + + /* pml4 and upml is legacy for extension modules */ + if (ms->pml4) { + fprintf(fp, " pml4: %lx\n", (ulong)ms->pml4); + fprintf(fp, " last_pml4_read: %lx\n", (ulong)ms->last_pml4_read); + + } else { + fprintf(fp, " pml4: (unused)\n"); + fprintf(fp, " last_pml4_read: (unused)\n"); + } + if (ms->upml) { - fprintf(fp, " upml: %lx\n", (ulong)ms->upml); - fprintf(fp, " last_upml_read: %lx\n", (ulong)ms->last_upml_read); + fprintf(fp, " upml: %lx\n", (ulong)ms->upml); + fprintf(fp, " last_upml_read: %lx\n", (ulong)ms->last_upml_read); } else { - fprintf(fp, " upml: (unused)\n"); - fprintf(fp, " last_upml_read: (unused)\n"); + fprintf(fp, " upml: (unused)\n"); + fprintf(fp, " last_upml_read: (unused)\n"); } - fprintf(fp, " irqstack: %lx\n", (ulong)ms->irqstack); - fprintf(fp, " irq_eframe_link: %ld\n", ms->irq_eframe_link); + + if (ms->p4d) { + fprintf(fp, " p4d: %lx\n", (ulong)ms->p4d); + fprintf(fp, " last_p4d_read: %lx\n", (ulong)ms->last_p4d_read); + } else { + fprintf(fp, " p4d: (unused)\n"); + fprintf(fp, " last_p4d_read: (unused)\n"); + } + + fprintf(fp, " ORC_data: %s", machdep->flags & ORC ? "\n" : "(unused)\n"); + if (machdep->flags & ORC) { + fprintf(fp, " module_ORC: %s\n", ms->orc.module_ORC ? "TRUE" : "FALSE"); + fprintf(fp, " lookup_num_blocks: %d\n", ms->orc.lookup_num_blocks); + fprintf(fp, " __start_orc_unwind_ip: %lx\n", ms->orc.__start_orc_unwind_ip); + fprintf(fp, " __stop_orc_unwind_ip: %lx\n", ms->orc.__stop_orc_unwind_ip); + fprintf(fp, " __start_orc_unwind: %lx\n", ms->orc.__start_orc_unwind); + fprintf(fp, " __stop_orc_unwind: %lx\n", ms->orc.__stop_orc_unwind); + fprintf(fp, " orc_lookup: %lx\n", ms->orc.orc_lookup); + fprintf(fp, " ip_entry: %lx\n", ms->orc.ip_entry); + fprintf(fp, " orc_entry: %lx\n", ms->orc.orc_entry); + fprintf(fp, " kernel_orc_entry:\n"); + fprintf(fp, " sp_offset: %d\n", ms->orc.kernel_orc_entry.sp_offset); + fprintf(fp, " bp_offset: %d\n", ms->orc.kernel_orc_entry.bp_offset); + fprintf(fp, " sp_reg: %d\n", ms->orc.kernel_orc_entry.sp_reg); + fprintf(fp, " bp_reg: %d\n", ms->orc.kernel_orc_entry.bp_reg); + fprintf(fp, " type: %d\n", ms->orc.kernel_orc_entry.type); + } fprintf(fp, " pto: %s", machdep->flags & PT_REGS_INIT ? "\n" : "(uninitialized)\n"); if (machdep->flags & PT_REGS_INIT) { @@ -787,6 +961,9 @@ fprintf(fp, " thread_return: %lx\n", ms->thread_return); fprintf(fp, " page_protnone: %lx\n", ms->page_protnone); + fprintf(fp, " irqstack: %lx\n", (ulong)ms->irqstack); + fprintf(fp, " irq_eframe_link: %ld\n", ms->irq_eframe_link); + fprintf(fp, " irq_stack_gap: %ld\n", ms->irq_stack_gap); fprintf(fp, " stkinfo: isize: %d\n", ms->stkinfo.isize); fprintf(fp, " esize[%d]: %d,%d,%d,%d,%d,%d,%d%s\n", @@ -825,7 +1002,18 @@ fprintf(fp, "\n "); fprintf(fp, "%016lx ", ms->stkinfo.ibase[c]); } - fprintf(fp, "\n"); + fprintf(fp, "\n kpti_entry_stack_size: %ld", ms->kpti_entry_stack_size); + fprintf(fp, "\n kpti_entry_stack: "); + if (machdep->flags & KPTI) { + fprintf(fp, "%lx\n ", ms->kpti_entry_stack); + for (c = 0; c < cpus; c++) { + if (c && !(c%4)) + fprintf(fp, "\n "); + fprintf(fp, "%016lx ", ms->kpti_entry_stack + kt->__per_cpu_offset[c]); + } + fprintf(fp, "\n"); + } else + fprintf(fp, "(unused)\n"); } /* @@ -1081,7 +1269,10 @@ 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"))) { + if (!(tss_sp = per_cpu_symbol_search("per_cpu__cpu_tss"))) + tss_sp = per_cpu_symbol_search("per_cpu__cpu_tss_rw"); + } ist_sp = per_cpu_symbol_search("per_cpu__orig_ist"); x86_64_exception_stacks_init(); @@ -1132,7 +1323,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], @@ -1192,6 +1384,71 @@ } } +/* + * Determine whether the unused gap at the top of the IRQ stack exists, + * and store its size (either 0 or 64 bytes). + */ +static void +x86_64_irq_stack_gap_init(void) +{ + int c, cpus; + struct syment *sp; + ulong irq_stack_ptr; + struct machine_specific *ms = machdep->machspec; + + if (ms->irq_stack_gap != UNINITIALIZED) + return; + + if (THIS_KERNEL_VERSION >= LINUX(4,9,0)) { + ms->irq_stack_gap = 0; + return; + } + + ms->irq_stack_gap = 64; + + /* + * Check for backports of this commit: + * + * commit 4950d6d48a0c43cc61d0bbb76fb10e0214b79c66 + * Author: Josh Poimboeuf + * Date: Thu Aug 18 10:59:08 2016 -0500 + * + * x86/dumpstack: Remove 64-byte gap at end of irq stack + */ + + if (!(sp = per_cpu_symbol_search("per_cpu__irq_stack_ptr"))) + return; + + /* + * CONFIG_SMP=n + */ + if (!(kt->flags & PER_CPU_OFF)) { + get_symbol_data(sp->name, sizeof(ulong), &irq_stack_ptr); + if ((irq_stack_ptr & 0xfff) == 0) + ms->irq_stack_gap = 0; + return; + } + + /* + * Check the per-cpu irq_stack_ptr of the first possible cpu. + */ + if (!cpu_map_addr("possible")) + return; + + cpus = kt->kernel_NR_CPUS ? kt->kernel_NR_CPUS : NR_CPUS; + for (c = 0; c < cpus; c++) { + if (!in_cpu_map(POSSIBLE, c)) + continue; + if (readmem(sp->value + kt->__per_cpu_offset[c], + KVADDR, &irq_stack_ptr, sizeof(void *), "irq_stack_ptr", + QUIET|RETURN_ON_ERROR)) { + if ((irq_stack_ptr & 0xfff) == 0) + ms->irq_stack_gap = 0; + break; + } + } +} + static void x86_64_post_init(void) { @@ -1279,14 +1536,19 @@ x86_64_init_kernel_pgd(void) { int i; - ulong init_level4_pgt; + ulong kernel_pgt = 0; - init_level4_pgt = symbol_value("init_level4_pgt"); + if (kernel_symbol_exists("init_level4_pgt")) + kernel_pgt = symbol_value("init_level4_pgt"); + else if (kernel_symbol_exists("init_top_pgt")) + kernel_pgt = symbol_value("init_top_pgt"); + else + error(WARNING, "neither \"init_level4_pgt\" or \"init_top_pgt\" exist\n"); for (i = 0; i < NR_CPUS; i++) - vt->kernel_pgd[i] = init_level4_pgt; + vt->kernel_pgd[i] = kernel_pgt; - FILL_PML4(); + FILL_TOP_PGD(); } /* @@ -1309,7 +1571,8 @@ return ((vaddr >= VMALLOC_START && vaddr <= VMALLOC_END) || ((machdep->flags & VMEMMAP) && (vaddr >= VMEMMAP_VADDR && vaddr <= VMEMMAP_END)) || - (vaddr >= MODULES_VADDR && vaddr <= MODULES_END)); + (vaddr >= MODULES_VADDR && vaddr <= MODULES_END) || + (vaddr >= VSYSCALL_START && vaddr < VSYSCALL_END)); } static int @@ -1336,6 +1599,235 @@ return (addr < USERSPACE_TOP); } +static int +x86_64_is_page_ptr(ulong addr, physaddr_t *phys) +{ + ulong pfn, nr; + + if (IS_SPARSEMEM() && (machdep->flags & VMEMMAP) && + (addr >= VMEMMAP_VADDR && addr <= VMEMMAP_END) && + !((addr - VMEMMAP_VADDR) % SIZE(page))) { + + pfn = (addr - VMEMMAP_VADDR) / SIZE(page); + nr = pfn_to_section_nr(pfn); + if (valid_section_nr(nr)) { + if (phys) + *phys = PTOB(pfn); + return TRUE; + } + } + return FALSE; +} + +/* + * Find the kernel pgd entry.. + * pgd = pgd_offset_k(addr); + */ +static ulong * +x86_64_kpgd_offset(ulong kvaddr, int verbose, int IS_XEN) +{ + ulong *pgd; + + FILL_TOP_PGD(); + pgd = ((ulong *)machdep->pgd) + pgd_index(kvaddr); + if (verbose) { + fprintf(fp, "PGD DIRECTORY: %lx\n", vt->kernel_pgd[0]); + if (IS_XEN) + fprintf(fp, "PAGE DIRECTORY: %lx [machine]\n", *pgd); + else + fprintf(fp, "PAGE DIRECTORY: %lx\n", *pgd); + } + + return pgd; +} + +/* + * In x86 64 bit system, Linux uses the 4-level page table as the default both + * in Kernel page tables and user page tables. + * + * But in some old versions(pre-2.6.11), the 3-level page table is used for + * user page tables. + * + * So reuse the PUD and find the user pgd entry for this older version Linux.. + * pgd = pgd_offset(mm, address); + */ +static ulong +x86_64_upgd_offset_legacy(struct task_context *tc, ulong uvaddr, int verbose, int IS_XEN) +{ + ulong *pud; + ulong pud_paddr; + ulong pud_pte; + + if (task_mm(tc->task, TRUE)) + pud = ULONG_PTR(tt->mm_struct + OFFSET(mm_struct_pgd)); + else + readmem(tc->mm_struct + OFFSET(mm_struct_pgd), KVADDR, &pud, + sizeof(long), "mm_struct pgd", FAULT_ON_ERROR); + + pud_paddr = x86_64_VTOP((ulong)pud); + FILL_PUD(pud_paddr, PHYSADDR, PAGESIZE()); + pud = ((ulong *)pud_paddr) + pud_index(uvaddr); + pud_pte = ULONG(machdep->pud + PAGEOFFSET(pud)); + if (verbose) { + if (IS_XEN) + fprintf(fp, " PGD: %lx => %lx [machine]\n", (ulong)pud, pud_pte); + else + fprintf(fp, " PGD: %lx => %lx\n", (ulong)pud, pud_pte); + } + + return pud_pte; +} + +/* + * Find the user pgd entry.. + * pgd = pgd_offset(mm, address); + */ +static ulong +x86_64_upgd_offset(struct task_context *tc, ulong uvaddr, int verbose, int IS_XEN) +{ + ulong *pgd; + ulong pgd_paddr; + ulong pgd_pte; + + if (task_mm(tc->task, TRUE)) + pgd = ULONG_PTR(tt->mm_struct + OFFSET(mm_struct_pgd)); + else + readmem(tc->mm_struct + OFFSET(mm_struct_pgd), KVADDR, &pgd, + sizeof(long), "mm_struct pgd", FAULT_ON_ERROR); + + pgd_paddr = x86_64_VTOP((ulong)pgd); + FILL_PGD(pgd_paddr, PHYSADDR, PAGESIZE()); + pgd = ((ulong *)pgd_paddr) + pgd_index(uvaddr); + pgd_pte = ULONG(machdep->pgd + PAGEOFFSET(pgd)); + if (verbose) { + if (IS_XEN) + fprintf(fp, " PGD: %lx => %lx [machine]\n", (ulong)pgd, pgd_pte); + else + fprintf(fp, " PGD: %lx => %lx\n", (ulong)pgd, pgd_pte); + } + + return pgd_pte; +} + +/* + * Find an entry in the fourth-level page table.. + * p4d = p4d_offset(pgd, address); + */ +static ulong +x86_64_p4d_offset(ulong pgd_pte, ulong vaddr, int verbose, int IS_XEN) +{ + ulong *p4d; + ulong p4d_paddr; + ulong p4d_pte; + + p4d_paddr = pgd_pte & PHYSICAL_PAGE_MASK; + FILL_P4D(p4d_paddr, PHYSADDR, PAGESIZE()); + p4d = ((ulong *)p4d_paddr) + p4d_index(vaddr); + p4d_pte = ULONG(machdep->machspec->p4d + PAGEOFFSET(p4d)); + if (verbose) { + if (IS_XEN) + fprintf(fp, " P4D: %lx => %lx [machine]\n", (ulong)p4d, p4d_pte); + else + fprintf(fp, " P4D: %lx => %lx\n", (ulong)p4d, p4d_pte); + } + + return p4d_pte; +} + +/* + * Find an entry in the third-level page table.. + * pud = pud_offset(pgd, address); + */ +static ulong +x86_64_pud_offset(ulong pgd_pte, ulong vaddr, int verbose, int IS_XEN) +{ + ulong *pud; + ulong pud_paddr; + ulong pud_pte; + + pud_paddr = pgd_pte & PHYSICAL_PAGE_MASK; + + if (IS_XEN) { + pud_paddr = xen_m2p(pud_paddr); + if (verbose) + fprintf(fp, " PGD: %lx\n", pud_paddr); + } + + FILL_PUD(pud_paddr, PHYSADDR, PAGESIZE()); + pud = ((ulong *)pud_paddr) + pud_index(vaddr); + pud_pte = ULONG(machdep->pud + PAGEOFFSET(pud)); + if (verbose) { + if (IS_XEN) + fprintf(fp, " PUD: %lx => %lx [machine]\n", (ulong)pud, pud_pte); + else + fprintf(fp, " PUD: %lx => %lx\n", (ulong)pud, pud_pte); + } + + return pud_pte; +} + +/* + * Find an entry in the middle page table.. + * pmd = pmd_offset(pud, address); + */ +static ulong +x86_64_pmd_offset(ulong pud_pte, ulong vaddr, int verbose, int IS_XEN) +{ + ulong *pmd; + ulong pmd_paddr; + ulong pmd_pte; + + pmd_paddr = pud_pte & PHYSICAL_PAGE_MASK; + + if (IS_XEN) { + pmd_paddr = xen_m2p(pmd_paddr); + if (verbose) + fprintf(fp, " PUD: %lx\n", pmd_paddr); + } + + FILL_PMD(pmd_paddr, PHYSADDR, PAGESIZE()); + pmd = ((ulong *)pmd_paddr) + pmd_index(vaddr); + pmd_pte = ULONG(machdep->pmd + PAGEOFFSET(pmd)); + if (verbose) { + if (IS_XEN) + fprintf(fp, " PMD: %lx => %lx [machine]\n", (ulong)pmd, pmd_pte); + else + fprintf(fp, " PMD: %lx => %lx\n", (ulong)pmd, pmd_pte); + } + return pmd_pte; +} + +/* + * Find an entry in the pet page table.. + * pmd = pmd_offset(pud, address); + */ +static ulong +x86_64_pte_offset(ulong pmd_pte, ulong vaddr, int verbose, int IS_XEN) +{ + ulong *ptep; + ulong pte_paddr; + ulong pte; + + pte_paddr = pmd_pte & PHYSICAL_PAGE_MASK; + + if (IS_XEN) { + pte_paddr = xen_m2p(pte_paddr); + if (verbose) + fprintf(fp, " PMD: %lx\n", pte_paddr); + } + + FILL_PTBL(pte_paddr, PHYSADDR, PAGESIZE()); + ptep = ((ulong *)pte_paddr) + pte_index(vaddr); + pte = ULONG(machdep->ptbl + PAGEOFFSET(ptep)); + if (verbose) { + if (IS_XEN) + fprintf(fp, " PTE: %lx => %lx [machine]\n", (ulong)ptep, pte); + else + fprintf(fp, " PTE: %lx => %lx\n", (ulong)ptep, pte); + } + + return pte; +} /* * Translates a user virtual address to its physical address. cmd_vtop() @@ -1349,18 +1841,9 @@ static int x86_64_uvtop_level4(struct task_context *tc, ulong uvaddr, physaddr_t *paddr, int verbose) { - ulong mm; - ulong *pml; - ulong pml_paddr; - ulong pml_pte; - ulong *pgd; - ulong pgd_paddr; ulong pgd_pte; - ulong *pmd; - ulong pmd_paddr; + ulong pud_pte; ulong pmd_pte; - ulong *ptep; - ulong pte_paddr; ulong pte; physaddr_t physpage; @@ -1372,43 +1855,41 @@ if (IS_KVADDR(uvaddr)) return x86_64_kvtop(tc, uvaddr, paddr, verbose); - if ((mm = task_mm(tc->task, TRUE))) - pml = ULONG_PTR(tt->mm_struct + OFFSET(mm_struct_pgd)); - else - readmem(tc->mm_struct + OFFSET(mm_struct_pgd), KVADDR, &pml, - sizeof(long), "mm_struct pgd", FAULT_ON_ERROR); - - pml_paddr = x86_64_VTOP((ulong)pml); - FILL_UPML(pml_paddr, PHYSADDR, PAGESIZE()); - pml = ((ulong *)pml_paddr) + pml4_index(uvaddr); - pml_pte = ULONG(machdep->machspec->upml + PAGEOFFSET(pml)); - if (verbose) - fprintf(fp, " PML: %lx => %lx\n", (ulong)pml, pml_pte); - if (!(pml_pte & _PAGE_PRESENT)) + pgd_pte = x86_64_upgd_offset(tc, uvaddr, verbose, FALSE); + if (!(pgd_pte & _PAGE_PRESENT)) goto no_upage; - pgd_paddr = pml_pte & PHYSICAL_PAGE_MASK; - FILL_PGD(pgd_paddr, PHYSADDR, PAGESIZE()); - pgd = ((ulong *)pgd_paddr) + pgd_index(uvaddr); - pgd_pte = ULONG(machdep->pgd + PAGEOFFSET(pgd)); - if (verbose) - fprintf(fp, " PUD: %lx => %lx\n", (ulong)pgd, pgd_pte); - if (!(pgd_pte & _PAGE_PRESENT)) + /* If the VM is in 5-level page table */ + if (machdep->flags & VM_5LEVEL && x86_64_task_uses_5level(tc)) { + ulong p4d_pte; + /* + * p4d = p4d_offset(pgd, address); + */ + p4d_pte = x86_64_p4d_offset(pgd_pte, uvaddr, verbose, FALSE); + if (!(p4d_pte & _PAGE_PRESENT)) + goto no_upage; + /* + * pud = pud_offset(p4d, address); + */ + pud_pte = x86_64_pud_offset(p4d_pte, uvaddr, verbose, FALSE); + } else { + /* + * pud = pud_offset(pgd, address); + */ + pud_pte = x86_64_pud_offset(pgd_pte, uvaddr, verbose, FALSE); + } + + if (!(pud_pte & _PAGE_PRESENT)) goto no_upage; /* - * pmd = pmd_offset(pgd, address); + * pmd = pmd_offset(pud, address); */ - pmd_paddr = pgd_pte & PHYSICAL_PAGE_MASK; - FILL_PMD(pmd_paddr, PHYSADDR, PAGESIZE()); - pmd = ((ulong *)pmd_paddr) + pmd_index(uvaddr); - pmd_pte = ULONG(machdep->pmd + PAGEOFFSET(pmd)); - if (verbose) - fprintf(fp, " PMD: %lx => %lx\n", (ulong)pmd, pmd_pte); + pmd_pte = x86_64_pmd_offset(pud_pte, uvaddr, verbose, FALSE); if (!(pmd_pte & (_PAGE_PRESENT | _PAGE_PROTNONE))) goto no_upage; if (pmd_pte & _PAGE_PSE) { - if (verbose) { + if (verbose) { fprintf(fp, " PAGE: %lx (2MB)\n\n", PAGEBASE(pmd_pte) & PHYSICAL_PAGE_MASK); x86_64_translate_pte(pmd_pte, 0, 0); @@ -1424,13 +1905,8 @@ * ptep = pte_offset_map(pmd, address); * pte = *ptep; */ - pte_paddr = pmd_pte & PHYSICAL_PAGE_MASK; - FILL_PTBL(pte_paddr, PHYSADDR, PAGESIZE()); - ptep = ((ulong *)pte_paddr) + pte_index(uvaddr); - pte = ULONG(machdep->ptbl + PAGEOFFSET(ptep)); - if (verbose) - fprintf(fp, " PTE: %lx => %lx\n", (ulong)ptep, pte); - if (!(pte & (_PAGE_PRESENT))) { + pte = x86_64_pte_offset(pmd_pte, uvaddr, verbose, FALSE); + if (!(pte & (_PAGE_PRESENT | _PAGE_PROTNONE))) { *paddr = pte; if (pte && verbose) { @@ -1456,21 +1932,18 @@ } static int +x86_64_task_uses_5level(struct task_context *tc) +{ + return FALSE; +} + +static int x86_64_uvtop_level4_xen_wpt(struct task_context *tc, ulong uvaddr, physaddr_t *paddr, int verbose) { - ulong mm; - ulong *pml; - ulong pml_paddr; - ulong pml_pte; - ulong *pgd; - ulong pgd_paddr; ulong pgd_pte; - ulong *pmd; - ulong pmd_paddr; + ulong pud_pte; ulong pmd_pte; ulong pseudo_pmd_pte; - ulong *ptep; - ulong pte_paddr; ulong pte; ulong pseudo_pte; physaddr_t physpage; @@ -1484,45 +1957,18 @@ if (IS_KVADDR(uvaddr)) return x86_64_kvtop(tc, uvaddr, paddr, verbose); - if ((mm = task_mm(tc->task, TRUE))) - pml = ULONG_PTR(tt->mm_struct + OFFSET(mm_struct_pgd)); - else - readmem(tc->mm_struct + OFFSET(mm_struct_pgd), KVADDR, &pml, - sizeof(long), "mm_struct pgd", FAULT_ON_ERROR); - - pml_paddr = x86_64_VTOP((ulong)pml); - FILL_UPML(pml_paddr, PHYSADDR, PAGESIZE()); - pml = ((ulong *)pml_paddr) + pml4_index(uvaddr); - pml_pte = ULONG(machdep->machspec->upml + PAGEOFFSET(pml)); - if (verbose) - fprintf(fp, " PML: %lx => %lx [machine]\n", (ulong)pml, pml_pte); - if (!(pml_pte & _PAGE_PRESENT)) + pgd_pte = x86_64_upgd_offset(tc, uvaddr, verbose, TRUE); + if (!(pgd_pte & _PAGE_PRESENT)) goto no_upage; - pgd_paddr = pml_pte & PHYSICAL_PAGE_MASK; - pgd_paddr = xen_m2p(pgd_paddr); - if (verbose) - fprintf(fp, " PML: %lx\n", pgd_paddr); - FILL_PGD(pgd_paddr, PHYSADDR, PAGESIZE()); - pgd = ((ulong *)pgd_paddr) + pgd_index(uvaddr); - pgd_pte = ULONG(machdep->pgd + PAGEOFFSET(pgd)); - if (verbose) - fprintf(fp, " PUD: %lx => %lx [machine]\n", (ulong)pgd, pgd_pte); - if (!(pgd_pte & _PAGE_PRESENT)) + pud_pte = x86_64_pud_offset(pgd_pte, uvaddr, verbose, TRUE); + if (!(pud_pte & _PAGE_PRESENT)) goto no_upage; /* - * pmd = pmd_offset(pgd, address); + * pmd = pmd_offset(pud, address); */ - pmd_paddr = pgd_pte & PHYSICAL_PAGE_MASK; - pmd_paddr = xen_m2p(pmd_paddr); - if (verbose) - fprintf(fp, " PUD: %lx\n", pmd_paddr); - FILL_PMD(pmd_paddr, PHYSADDR, PAGESIZE()); - pmd = ((ulong *)pmd_paddr) + pmd_index(uvaddr); - pmd_pte = ULONG(machdep->pmd + PAGEOFFSET(pmd)); - if (verbose) - fprintf(fp, " PMD: %lx => %lx [machine]\n", (ulong)pmd, pmd_pte); + pmd_pte = x86_64_pmd_offset(pud_pte, uvaddr, verbose, TRUE); if (!(pmd_pte & _PAGE_PRESENT)) goto no_upage; if (pmd_pte & _PAGE_PSE) { @@ -1561,15 +2007,7 @@ * ptep = pte_offset_map(pmd, address); * pte = *ptep; */ - pte_paddr = pmd_pte & PHYSICAL_PAGE_MASK; - pte_paddr = xen_m2p(pte_paddr); - if (verbose) - fprintf(fp, " PMD: %lx\n", pte_paddr); - FILL_PTBL(pte_paddr, PHYSADDR, PAGESIZE()); - ptep = ((ulong *)pte_paddr) + pte_index(uvaddr); - pte = ULONG(machdep->ptbl + PAGEOFFSET(ptep)); - if (verbose) - fprintf(fp, " PTE: %lx => %lx [machine]\n", (ulong)ptep, pte); + pte = x86_64_pte_offset(pmd_pte, uvaddr, verbose, TRUE); if (!(pte & (_PAGE_PRESENT))) { *paddr = pte; @@ -1579,7 +2017,7 @@ } goto no_upage; } - + pseudo_pte = xen_m2p(pte & PHYSICAL_PAGE_MASK); if (verbose) fprintf(fp, " PTE: %lx\n", pseudo_pte + PAGEOFFSET(pte)); @@ -1604,16 +2042,9 @@ static int x86_64_uvtop_level4_rhel4_xen_wpt(struct task_context *tc, ulong uvaddr, physaddr_t *paddr, int verbose) { - ulong mm; - ulong *pgd; - ulong pgd_paddr; ulong pgd_pte; - ulong *pmd; - ulong pmd_paddr; ulong pmd_pte; ulong pseudo_pmd_pte; - ulong *ptep; - ulong pte_paddr; ulong pte; ulong pseudo_pte; physaddr_t physpage; @@ -1627,33 +2058,14 @@ if (IS_KVADDR(uvaddr)) return x86_64_kvtop(tc, uvaddr, paddr, verbose); - if ((mm = task_mm(tc->task, TRUE))) - pgd = ULONG_PTR(tt->mm_struct + OFFSET(mm_struct_pgd)); - else - readmem(tc->mm_struct + OFFSET(mm_struct_pgd), KVADDR, &pgd, - sizeof(long), "mm_struct pgd", FAULT_ON_ERROR); - - pgd_paddr = x86_64_VTOP((ulong)pgd); - FILL_PGD(pgd_paddr, PHYSADDR, PAGESIZE()); - pgd = ((ulong *)pgd_paddr) + pgd_index(uvaddr); - pgd_pte = ULONG(machdep->pgd + PAGEOFFSET(pgd)); - if (verbose) - fprintf(fp, " PGD: %lx => %lx [machine]\n", (ulong)pgd, pgd_pte); + pgd_pte = x86_64_upgd_offset_legacy(tc, uvaddr, verbose, TRUE); if (!(pgd_pte & _PAGE_PRESENT)) goto no_upage; /* * pmd = pmd_offset(pgd, address); */ - pmd_paddr = pgd_pte & PHYSICAL_PAGE_MASK; - pmd_paddr = xen_m2p(pmd_paddr); - if (verbose) - fprintf(fp, " PGD: %lx\n", pmd_paddr); - FILL_PMD(pmd_paddr, PHYSADDR, PAGESIZE()); - pmd = ((ulong *)pmd_paddr) + pmd_index(uvaddr); - pmd_pte = ULONG(machdep->pmd + PAGEOFFSET(pmd)); - if (verbose) - fprintf(fp, " PMD: %lx => %lx [machine]\n", (ulong)pmd, pmd_pte); + pmd_pte = x86_64_pmd_offset(pgd_pte, uvaddr, verbose, TRUE); if (!(pmd_pte & _PAGE_PRESENT)) goto no_upage; if (pmd_pte & _PAGE_PSE) { @@ -1692,15 +2104,7 @@ * ptep = pte_offset_map(pmd, address); * pte = *ptep; */ - pte_paddr = pmd_pte & PHYSICAL_PAGE_MASK; - pte_paddr = xen_m2p(pte_paddr); - if (verbose) - fprintf(fp, " PMD: %lx\n", pte_paddr); - FILL_PTBL(pte_paddr, PHYSADDR, PAGESIZE()); - ptep = ((ulong *)pte_paddr) + pte_index(uvaddr); - pte = ULONG(machdep->ptbl + PAGEOFFSET(ptep)); - if (verbose) - fprintf(fp, " PTE: %lx => %lx [machine]\n", (ulong)ptep, pte); + pte = x86_64_pte_offset(pmd_pte, uvaddr, verbose, TRUE); if (!(pte & (_PAGE_PRESENT))) { *paddr = pte; @@ -1735,15 +2139,8 @@ static int x86_64_uvtop(struct task_context *tc, ulong uvaddr, physaddr_t *paddr, int verbose) { - ulong mm; - ulong *pgd; - ulong pgd_paddr; ulong pgd_pte; - ulong *pmd; - ulong pmd_paddr; ulong pmd_pte; - ulong *ptep; - ulong pte_paddr; ulong pte; physaddr_t physpage; @@ -1755,33 +2152,17 @@ if (IS_KVADDR(uvaddr)) return x86_64_kvtop(tc, uvaddr, paddr, verbose); - /* - * pgd = pgd_offset(mm, address); - */ - if ((mm = task_mm(tc->task, TRUE))) - pgd = ULONG_PTR(tt->mm_struct + OFFSET(mm_struct_pgd)); - else - readmem(tc->mm_struct + OFFSET(mm_struct_pgd), KVADDR, &pgd, - sizeof(long), "mm_struct pgd", FAULT_ON_ERROR); - - pgd_paddr = x86_64_VTOP((ulong)pgd); - FILL_PGD(pgd_paddr, PHYSADDR, PAGESIZE()); - pgd = ((ulong *)pgd_paddr) + pgd_index(uvaddr); - pgd_pte = ULONG(machdep->pgd + PAGEOFFSET(pgd)); - if (verbose) - fprintf(fp, " PGD: %lx => %lx\n", (ulong)pgd, pgd_pte); + /* + * pgd = pgd_offset(mm, address); + */ + pgd_pte = x86_64_upgd_offset_legacy(tc, uvaddr, verbose, FALSE); if (!(pgd_pte & _PAGE_PRESENT)) goto no_upage; /* * pmd = pmd_offset(pgd, address); */ - pmd_paddr = pgd_pte & PHYSICAL_PAGE_MASK; - FILL_PMD(pmd_paddr, PHYSADDR, PAGESIZE()); - pmd = ((ulong *)pmd_paddr) + pmd_index(uvaddr); - pmd_pte = ULONG(machdep->pmd + PAGEOFFSET(pmd)); - if (verbose) - fprintf(fp, " PMD: %lx => %lx\n", (ulong)pmd, pmd_pte); + pmd_pte = x86_64_pmd_offset(pgd_pte, uvaddr, verbose, FALSE); if (!(pmd_pte & _PAGE_PRESENT)) goto no_upage; if (pmd_pte & _PAGE_PSE) { @@ -1801,12 +2182,7 @@ * ptep = pte_offset_map(pmd, address); * pte = *ptep; */ - pte_paddr = pmd_pte & PHYSICAL_PAGE_MASK; - FILL_PTBL(pte_paddr, PHYSADDR, PAGESIZE()); - ptep = ((ulong *)pte_paddr) + pte_index(uvaddr); - pte = ULONG(machdep->ptbl + PAGEOFFSET(ptep)); - if (verbose) - fprintf(fp, " PTE: %lx => %lx\n", (ulong)ptep, pte); + pte = x86_64_pte_offset(pmd_pte, uvaddr, verbose, FALSE); if (!(pte & (_PAGE_PRESENT))) { *paddr = pte; @@ -1832,7 +2208,6 @@ return FALSE; } - /* * Translates a kernel virtual address to its physical address. cmd_vtop() * sets the verbose flag so that the pte translation gets displayed; all @@ -1841,18 +2216,24 @@ static int x86_64_kvtop(struct task_context *tc, ulong kvaddr, physaddr_t *paddr, int verbose) { - ulong *pml4; - ulong *pgd; - ulong pgd_paddr; - ulong pgd_pte; - ulong *pmd; - ulong pmd_paddr; + ulong *pgd; + ulong pud_pte; ulong pmd_pte; - ulong *ptep; - ulong pte_paddr; ulong pte; physaddr_t physpage; + if ((SADUMP_DUMPFILE() || QEMU_MEM_DUMP_NO_VMCOREINFO() || VMSS_DUMPFILE()) + && !(machdep->flags & KSYMS_START)) { + /* + * In the case of sadump, to calculate kaslr_offset and + * phys_base, kvtop is called during symtab_init(). In this + * stage phys_base is not initialized yet and x86_64_VTOP() + * does not work. Jump to the code of pagetable translation. + */ + pgd = x86_64_kpgd_offset(kvaddr, verbose, FALSE); + goto start_vtop_with_pagetable; + } + if (!IS_KVADDR(kvaddr)) return FALSE; @@ -1865,11 +2246,11 @@ *paddr = kvaddr - DIRECTMAP_VIRT_START; return TRUE; } - FILL_PML4_HYPER(); - pml4 = ((ulong *)machdep->machspec->pml4) + pml4_index(kvaddr); + FILL_TOP_PGD_HYPER(); + pgd = ((ulong *)machdep->pgd) + pgd_index(kvaddr); if (verbose) { - fprintf(fp, "PML4 DIRECTORY: %lx\n", vt->kernel_pgd[0]); - fprintf(fp, "PAGE DIRECTORY: %lx\n", *pml4); + fprintf(fp, "PGD DIRECTORY: %lx\n", vt->kernel_pgd[0]); + fprintf(fp, "PAGE DIRECTORY: %lx\n", *pgd); } } else { if (!vt->vmalloc_start) { @@ -1889,33 +2270,37 @@ /* * pgd = pgd_offset_k(addr); */ - FILL_PML4(); - pml4 = ((ulong *)machdep->machspec->pml4) + pml4_index(kvaddr); - if (verbose) { - fprintf(fp, "PML4 DIRECTORY: %lx\n", vt->kernel_pgd[0]); - fprintf(fp, "PAGE DIRECTORY: %lx\n", *pml4); - } + pgd = x86_64_kpgd_offset(kvaddr, verbose, FALSE); } - if (!(*pml4) & _PAGE_PRESENT) + +start_vtop_with_pagetable: + if (!(*pgd & _PAGE_PRESENT)) goto no_kpage; - pgd_paddr = (*pml4) & PHYSICAL_PAGE_MASK; - FILL_PGD(pgd_paddr, PHYSADDR, PAGESIZE()); - pgd = ((ulong *)pgd_paddr) + pgd_index(kvaddr); - pgd_pte = ULONG(machdep->pgd + PAGEOFFSET(pgd)); - if (verbose) - fprintf(fp, " PUD: %lx => %lx\n", (ulong)pgd, pgd_pte); - if (!(pgd_pte & _PAGE_PRESENT)) + + /* If the VM is in 5-level page table */ + if (machdep->flags & VM_5LEVEL) { + ulong p4d_pte; + /* + * p4d = p4d_offset(pgd, address); + */ + p4d_pte = x86_64_p4d_offset(*pgd, kvaddr, verbose, FALSE); + if (!(p4d_pte & _PAGE_PRESENT)) + goto no_kpage; + /* + * pud = pud_offset(p4d, address); + */ + pud_pte = x86_64_pud_offset(p4d_pte, kvaddr, verbose, FALSE); + } else { + pud_pte = x86_64_pud_offset(*pgd, kvaddr, verbose, FALSE); + } + + if (!(pud_pte & _PAGE_PRESENT)) goto no_kpage; /* - * pmd = pmd_offset(pgd, addr); + * pmd = pmd_offset(pud, address); */ - pmd_paddr = pgd_pte & PHYSICAL_PAGE_MASK; - FILL_PMD(pmd_paddr, PHYSADDR, PAGESIZE()); - pmd = ((ulong *)pmd_paddr) + pmd_index(kvaddr); - pmd_pte = ULONG(machdep->pmd + PAGEOFFSET(pmd)); - if (verbose) - fprintf(fp, " PMD: %lx => %lx\n", (ulong)pmd, pmd_pte); + pmd_pte = x86_64_pmd_offset(pud_pte, kvaddr, verbose, FALSE); if (!(pmd_pte & _PAGE_PRESENT)) goto no_kpage; if (pmd_pte & _PAGE_PSE) { @@ -1935,12 +2320,7 @@ * ptep = pte_offset_map(pmd, addr); * pte = *ptep; */ - pte_paddr = pmd_pte & PHYSICAL_PAGE_MASK; - FILL_PTBL(pte_paddr, PHYSADDR, PAGESIZE()); - ptep = ((ulong *)pte_paddr) + pte_index(kvaddr); - pte = ULONG(machdep->ptbl + PAGEOFFSET(ptep)); - if (verbose) - fprintf(fp, " PTE: %lx => %lx\n", (ulong)ptep, pte); + pte = x86_64_pte_offset(pmd_pte, kvaddr, verbose, FALSE); if (!(pte & (_PAGE_PRESENT))) { if (pte && verbose) { fprintf(fp, "\n"); @@ -1963,20 +2343,13 @@ return FALSE; } - static int x86_64_kvtop_xen_wpt(struct task_context *tc, ulong kvaddr, physaddr_t *paddr, int verbose) { - ulong *pml4; ulong *pgd; - ulong pgd_paddr; - ulong pgd_pte; - ulong *pmd; - ulong pmd_paddr; + ulong pud_pte; ulong pmd_pte; ulong pseudo_pmd_pte; - ulong *ptep; - ulong pte_paddr; ulong pte; ulong pseudo_pte; physaddr_t physpage; @@ -1985,38 +2358,18 @@ /* * pgd = pgd_offset_k(addr); */ - FILL_PML4(); - pml4 = ((ulong *)machdep->machspec->pml4) + pml4_index(kvaddr); - if (verbose) { - fprintf(fp, "PML4 DIRECTORY: %lx\n", vt->kernel_pgd[0]); - fprintf(fp, "PAGE DIRECTORY: %lx [machine]\n", *pml4); - } - if (!(*pml4) & _PAGE_PRESENT) + pgd = x86_64_kpgd_offset(kvaddr, verbose, TRUE); + if (!(*pgd & _PAGE_PRESENT)) goto no_kpage; - pgd_paddr = (*pml4) & PHYSICAL_PAGE_MASK; - pgd_paddr = xen_m2p(pgd_paddr); - if (verbose) - fprintf(fp, "PAGE DIRECTORY: %lx\n", pgd_paddr); - FILL_PGD(pgd_paddr, PHYSADDR, PAGESIZE()); - pgd = ((ulong *)pgd_paddr) + pgd_index(kvaddr); - pgd_pte = ULONG(machdep->pgd + PAGEOFFSET(pgd)); - if (verbose) - fprintf(fp, " PUD: %lx => %lx [machine]\n", (ulong)pgd, pgd_pte); - if (!(pgd_pte & _PAGE_PRESENT)) + + pud_pte = x86_64_pud_offset(*pgd, kvaddr, verbose, TRUE); + if (!(pud_pte & _PAGE_PRESENT)) goto no_kpage; /* * pmd = pmd_offset(pgd, addr); */ - pmd_paddr = pgd_pte & PHYSICAL_PAGE_MASK; - pmd_paddr = xen_m2p(pmd_paddr); - if (verbose) - fprintf(fp, " PUD: %lx\n", pmd_paddr); - FILL_PMD(pmd_paddr, PHYSADDR, PAGESIZE()); - pmd = ((ulong *)pmd_paddr) + pmd_index(kvaddr); - pmd_pte = ULONG(machdep->pmd + PAGEOFFSET(pmd)); - if (verbose) - fprintf(fp, " PMD: %lx => %lx [machine]\n", (ulong)pmd, pmd_pte); + pmd_pte = x86_64_pmd_offset(pud_pte, kvaddr, verbose, TRUE); if (!(pmd_pte & _PAGE_PRESENT)) goto no_kpage; if (pmd_pte & _PAGE_PSE) { @@ -2055,15 +2408,7 @@ * ptep = pte_offset_map(pmd, addr); * pte = *ptep; */ - pte_paddr = pmd_pte & PHYSICAL_PAGE_MASK; - pte_paddr = xen_m2p(pte_paddr); - if (verbose) - fprintf(fp, " PMD: %lx\n", pte_paddr); - FILL_PTBL(pte_paddr, PHYSADDR, PAGESIZE()); - ptep = ((ulong *)pte_paddr) + pte_index(kvaddr); - pte = ULONG(machdep->ptbl + PAGEOFFSET(ptep)); - if (verbose) - fprintf(fp, " PTE: %lx => %lx [machine]\n", (ulong)ptep, pte); + pte = x86_64_pte_offset(pmd_pte, kvaddr, verbose, TRUE); if (!(pte & (_PAGE_PRESENT))) { if (pte && verbose) { fprintf(fp, "\n"); @@ -2317,19 +2662,19 @@ continue; if (ms->stkinfo.ibase[c] == 0) break; - bt->hp->esp = ms->stkinfo.ibase[c]; - fprintf(fp, "CPU %d IRQ STACK:", c); + bt->hp->esp = ms->stkinfo.ibase[c]; + fprintf(fp, "CPU %d IRQ STACK:", c); - if (hide_offline_cpu(c)) { - fprintf(fp, " [OFFLINE]\n\n"); - continue; - } else - fprintf(fp, "\n"); + if (hide_offline_cpu(c)) { + fprintf(fp, " [OFFLINE]\n\n"); + continue; + } else + fprintf(fp, "\n"); - if ((cnt = x86_64_eframe_search(bt))) - fprintf(fp, "\n"); - else - fprintf(fp, "(none found)\n\n"); + if ((cnt = x86_64_eframe_search(bt))) + fprintf(fp, "\n"); + else + fprintf(fp, "(none found)\n\n"); } for (c = 0; c < kt->cpus; c++) { @@ -2882,7 +3227,7 @@ struct syment *sp, *spt; FILE *ofp; ulong estack, irqstack; - ulong irq_eframe; + ulong irq_eframe, kpti_eframe; struct bt_info bt_local, *bt; struct machine_specific *ms; ulong last_process_stack_eframe; @@ -2931,6 +3276,8 @@ diskdump_display_regs(bt->tc->processor, ofp); else if (SADUMP_DUMPFILE()) sadump_display_regs(bt->tc->processor, ofp); + else if (VMSS_DUMPFILE()) + vmware_vmss_display_regs(bt->tc->processor, ofp); return; } @@ -2953,13 +3300,16 @@ diskdump_display_regs(bt->tc->processor, ofp); else if (SADUMP_DUMPFILE()) sadump_display_regs(bt->tc->processor, ofp); + else if (VMSS_DUMPFILE()) + vmware_vmss_display_regs(bt->tc->processor, ofp); else if (pc->flags2 & QEMU_MEM_DUMP_ELF) display_regs_from_elf_notes(bt->tc->processor, ofp); return; } else if ((bt->flags & BT_KERNEL_SPACE) && (KVMDUMP_DUMPFILE() || (ELF_NOTES_VALID() && DISKDUMP_DUMPFILE()) || - SADUMP_DUMPFILE() || (pc->flags2 & QEMU_MEM_DUMP_ELF))) { + SADUMP_DUMPFILE() || (pc->flags2 & QEMU_MEM_DUMP_ELF) || + VMSS_DUMPFILE())) { fprintf(ofp, " [exception RIP: "); if ((sp = value_search(bt->instptr, &offset))) { fprintf(ofp, "%s", sp->name); @@ -2975,6 +3325,8 @@ diskdump_display_regs(bt->tc->processor, ofp); else if (SADUMP_DUMPFILE()) sadump_display_regs(bt->tc->processor, ofp); + else if (VMSS_DUMPFILE()) + vmware_vmss_display_regs(bt->tc->processor, ofp); else if (pc->flags2 & QEMU_MEM_DUMP_ELF) display_regs_from_elf_notes(bt->tc->processor, ofp); @@ -3156,7 +3508,7 @@ error(FATAL, "read of IRQ stack at %lx failed\n", bt->stackbase); - stacktop = bt->stacktop - 64; /* from kernel code */ + stacktop = bt->stacktop - ms->irq_stack_gap; bt->flags &= ~BT_FRAMESIZE_DISABLE; @@ -3228,6 +3580,16 @@ bt->stacktop = GET_STACKTOP(bt->tc->task); if (!INSTACK(rsp, bt)) { + /* + * If the exception occurred while on the KPTI entry trampoline stack, + * just print the entry exception frame and bail out. + */ + if ((kpti_eframe = x86_64_in_kpti_entry_stack(bt->tc->processor, rsp))) { + x86_64_exception_frame(EFRAME_PRINT, kpti_eframe, 0, bt, ofp); + fprintf(fp, "--- ---\n"); + return; + } + switch (bt->flags & (BT_EXCEPTION_STACK|BT_IRQSTACK)) { case (BT_EXCEPTION_STACK|BT_IRQSTACK): @@ -3455,7 +3817,7 @@ struct syment *sp; FILE *ofp; ulong estack, irqstack; - ulong irq_eframe; + ulong irq_eframe, kpti_eframe; struct bt_info bt_local, *bt; struct machine_specific *ms; ulong last_process_stack_eframe; @@ -3633,7 +3995,7 @@ error(FATAL, "read of IRQ stack at %lx failed\n", bt->stackbase); - stacktop = bt->stacktop - 64; /* from kernel code */ + stacktop = bt->stacktop - ms->irq_stack_gap; if (!done) { level = dwarf_backtrace(bt, level, stacktop); @@ -3675,6 +4037,16 @@ bt->stacktop = GET_STACKTOP(bt->tc->task); if (!INSTACK(rsp, bt)) { + /* + * If the exception occurred while on the KPTI entry trampoline stack, + * just print the entry exception frame and bail out. + */ + if ((kpti_eframe = x86_64_in_kpti_entry_stack(bt->tc->processor, rsp))) { + x86_64_exception_frame(EFRAME_PRINT, kpti_eframe, 0, bt, ofp); + fprintf(fp, "--- ---\n"); + return; + } + switch (bt->flags & (BT_EXCEPTION_STACK|BT_IRQSTACK)) { case (BT_EXCEPTION_STACK|BT_IRQSTACK): @@ -4071,6 +4443,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); @@ -4577,7 +4951,8 @@ if (halt_rip && halt_rsp) { *rip = halt_rip; *rsp = halt_rsp; - if (KVMDUMP_DUMPFILE() || SADUMP_DUMPFILE()) + if (KVMDUMP_DUMPFILE() || SADUMP_DUMPFILE() || + (VMSS_DUMPFILE() && vmware_vmss_valid_regs(bt))) bt_in->flags &= ~(ulonglong)BT_DUMPFILE_SEARCH; return; } @@ -4622,7 +4997,8 @@ machdep->get_stack_frame(bt, rip, rsp); - if (KVMDUMP_DUMPFILE() || SADUMP_DUMPFILE()) + if (KVMDUMP_DUMPFILE() || SADUMP_DUMPFILE() || + (VMSS_DUMPFILE() && vmware_vmss_valid_regs(bt))) bt_in->flags &= ~(ulonglong)BT_DUMPFILE_SEARCH; } @@ -4923,24 +5299,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) && @@ -5266,7 +5643,10 @@ ulonglong addr, size; uint type; - e820 = symbol_value("e820"); + if (get_symbol_type("e820", NULL, NULL) == TYPE_CODE_PTR) + get_symbol_data("e820", sizeof(void *), &e820); + else + e820 = symbol_value("e820"); if (CRASHDEBUG(1)) dump_struct("e820map", e820, RADIX(16)); buf = (char *)GETBUF(SIZE(e820map)); @@ -5373,6 +5753,10 @@ * * --machdep irq_eframe_link= * + * Force the IRQ stack gap size via: + * + * --machdep irq_stack_gap= + * * Force max_physmem_bits via: * * --machdep max_physmem_bits= @@ -5385,7 +5769,7 @@ char *p; char buf[BUFSIZE]; char *arglist[MAXARGS]; - int megabytes; + int megabytes, gigabytes; int lines = 0; int vm_flag; ulong value; @@ -5429,6 +5813,9 @@ } else if (STREQ(p, "xen-rhel4")) { machdep->flags |= VM_XEN_RHEL4; continue; + } else if (STREQ(p, "5level")) { + machdep->flags |= VM_5LEVEL; + continue; } } } else if (STRNEQ(arglist[i], "phys_base=")) { @@ -5457,6 +5844,39 @@ continue; } } + } else if (STRNEQ(arglist[i], "kernel_image_size=")) { + megabytes = gigabytes = FALSE; + if ((LASTCHAR(arglist[i]) == 'm') || + (LASTCHAR(arglist[i]) == 'M')) { + LASTCHAR(arglist[i]) = NULLCHAR; + megabytes = TRUE; + } + if ((LASTCHAR(arglist[i]) == 'g') || + (LASTCHAR(arglist[i]) == 'G')) { + LASTCHAR(arglist[i]) = NULLCHAR; + gigabytes = TRUE; + } + + p = arglist[i] + strlen("kernel_image_size="); + if (strlen(p)) { + if (megabytes || gigabytes) { + value = dtol(p, RETURN_ON_ERROR|QUIET, + &errflag); + } else + value = htol(p, RETURN_ON_ERROR|QUIET, + &errflag); + if (!errflag) { + if (megabytes) + value = MEGABYTES(value); + else if (gigabytes) + value = GIGABYTES(value); + machdep->machspec->kernel_image_size = value; + error(NOTE, + "setting kernel_image_size to: 0x%lx\n\n", + machdep->machspec->kernel_image_size); + continue; + } + } } else if (STRNEQ(arglist[i], "irq_eframe_link=")) { p = arglist[i] + strlen("irq_eframe_link="); if (strlen(p)) { @@ -5466,6 +5886,15 @@ continue; } } + } else if (STRNEQ(arglist[i], "irq_stack_gap=")) { + p = arglist[i] + strlen("irq_stack_gap="); + if (strlen(p)) { + value = stol(p, RETURN_ON_ERROR|QUIET, &errflag); + if (!errflag) { + machdep->machspec->irq_stack_gap = value; + continue; + } + } } else if (STRNEQ(arglist[i], "max_physmem_bits=")) { p = arglist[i] + strlen("max_physmem_bits="); if (strlen(p)) { @@ -5510,6 +5939,11 @@ lines++; break; + case VM_5LEVEL: + error(NOTE, "using 5-level pagetable x86_64 VM address ranges\n"); + lines++; + break; + default: error(WARNING, "cannot set multiple vm values\n"); lines++; @@ -5526,7 +5960,8 @@ void x86_64_clear_machdep_cache(void) { - machdep->machspec->last_upml_read = 0; + if (machdep->last_pgd_read != vt->kernel_pgd[0]) + machdep->last_pgd_read = 0; } #define PUSH_RBP_MOV_RSP_RBP 0xe5894855 @@ -5537,13 +5972,14 @@ unsigned int push_rbp_mov_rsp_rbp; int i, check; char *checkfuncs[] = {"sys_open", "sys_fork", "sys_read", + "__x64_sys_open", "__x64_sys_fork", "__x64_sys_read", "do_futex", "do_fork", "_do_fork", "sys_write", "vfs_read", "__schedule"}; if (pc->flags & KERNEL_DEBUG_QUERY) return; - for (i = check = 0; i < 9; i++) { + for (i = check = 0; i < 12; i++) { if (!kernel_symbol_exists(checkfuncs[i])) continue; @@ -5569,6 +6005,82 @@ } } +static void +x86_64_ORC_init(void) +{ + int i; + char *ORC_symbols[] = { + "lookup_num_blocks", + "__start_orc_unwind_ip", + "__stop_orc_unwind_ip", + "__start_orc_unwind", + "__stop_orc_unwind", + "orc_lookup", + NULL + }; + struct ORC_data *orc; + + if (machdep->flags & FRAMEPOINTER) + return; + + STRUCT_SIZE_INIT(orc_entry, "orc_entry"); + if (!VALID_STRUCT(orc_entry)) + return; + + if (!MEMBER_EXISTS("orc_entry", "sp_offset") || + !MEMBER_EXISTS("orc_entry", "bp_offset") || + !MEMBER_EXISTS("orc_entry", "sp_reg") || + !MEMBER_EXISTS("orc_entry", "bp_reg") || + !MEMBER_EXISTS("orc_entry", "type") || + SIZE(orc_entry) != sizeof(kernel_orc_entry)) { + error(WARNING, "ORC unwinder: orc_entry structure has changed\n"); + return; + } + + for (i = 0; ORC_symbols[i]; i++) { + if (!symbol_exists(ORC_symbols[i])) { + error(WARNING, + "ORC unwinder: %s does not exist in this kernel\n", + ORC_symbols[i]); + return; + } + } + + orc = &machdep->machspec->orc; + + MEMBER_OFFSET_INIT(module_arch, "module", "arch"); + MEMBER_OFFSET_INIT(mod_arch_specific_num_orcs, "mod_arch_specific", "num_orcs"); + MEMBER_OFFSET_INIT(mod_arch_specific_orc_unwind_ip, "mod_arch_specific", "orc_unwind_ip"); + MEMBER_OFFSET_INIT(mod_arch_specific_orc_unwind, "mod_arch_specific", "orc_unwind"); + /* + * Nice to have, but not required. + */ + if (VALID_MEMBER(module_arch) && + VALID_MEMBER(mod_arch_specific_num_orcs) && + VALID_MEMBER(mod_arch_specific_orc_unwind_ip) && + VALID_MEMBER(mod_arch_specific_orc_unwind)) { + orc->module_ORC = TRUE; + } else { + orc->module_ORC = FALSE; + error(WARNING, "ORC unwinder: module orc_entry structures have changed\n"); + } + + if (!readmem(symbol_value("lookup_num_blocks"), KVADDR, &orc->lookup_num_blocks, + sizeof(unsigned int), "lookup_num_blocks", RETURN_ON_ERROR|QUIET)) { + error(WARNING, "ORC unwinder: cannot read lookup_num_blocks\n"); + return; + } + + orc->__start_orc_unwind_ip = symbol_value("__start_orc_unwind_ip"); + orc->__stop_orc_unwind_ip = symbol_value("__stop_orc_unwind_ip"); + orc->__start_orc_unwind = symbol_value("__start_orc_unwind"); + orc->__stop_orc_unwind = symbol_value("__stop_orc_unwind"); + orc->orc_lookup = symbol_value("orc_lookup"); + + machdep->flags |= ORC; +} + + static ulong search_for_switch_to(ulong start, ulong end) { @@ -5576,19 +6088,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(); @@ -5606,6 +6122,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(); @@ -5823,12 +6341,12 @@ if (CRASHDEBUG(1)) fprintf(fp, "x86_64_xen_kdump_p2m_create: cr3: %lx\n", xkd->cr3); - if (!readmem(PTOB(xkd->cr3), PHYSADDR, machdep->machspec->pml4, + if (!readmem(PTOB(xkd->cr3), PHYSADDR, machdep->pgd, PAGESIZE(), "xen kdump cr3 page", RETURN_ON_ERROR)) error(FATAL, "cannot read xen kdump cr3 page\n"); if (CRASHDEBUG(7)) - x86_64_debug_dump_page(fp, machdep->machspec->pml4, + x86_64_debug_dump_page(fp, machdep->pgd, "contents of PML4 page:"); /* @@ -5863,7 +6381,7 @@ if (CRASHDEBUG(1)) fprintf(fp, "phys_to_machine_mapping: %lx\n", kvaddr); - machdep->last_pgd_read = BADADDR; + machdep->last_pud_read = BADADDR; machdep->last_pmd_read = BADADDR; machdep->last_ptbl_read = BADADDR; @@ -5878,7 +6396,7 @@ fprintf(fp, "\n"); } - machdep->last_pgd_read = 0; + machdep->last_pud_read = 0; machdep->last_ptbl_read = 0; machdep->last_pmd_read = 0; pc->curcmd_flags &= ~XEN_MACHINE_ADDR; @@ -5892,33 +6410,33 @@ x86_64_xen_kdump_load_page(ulong kvaddr, char *pgbuf) { ulong mfn; - ulong *pml4, *pgd, *pmd, *ptep; + ulong *pgd, *pud, *pmd, *ptep; - pml4 = ((ulong *)machdep->machspec->pml4) + pml4_index(kvaddr); - mfn = ((*pml4) & PHYSICAL_PAGE_MASK) >> PAGESHIFT(); + pgd = ((ulong *)machdep->pgd) + pgd_index(kvaddr); + mfn = ((*pgd) & PHYSICAL_PAGE_MASK) >> PAGESHIFT(); if (CRASHDEBUG(3)) fprintf(fp, - "[%lx] pml4: %lx mfn: %lx pml4_index: %lx\n", - kvaddr, *pml4, mfn, pml4_index(kvaddr)); + "[%lx] pgd: %lx mfn: %lx pgd_index: %lx\n", + kvaddr, *pgd, mfn, pgd_index(kvaddr)); - if (!readmem(PTOB(mfn), PHYSADDR, machdep->pgd, PAGESIZE(), + if (!readmem(PTOB(mfn), PHYSADDR, machdep->pud, PAGESIZE(), "xen kdump pud page", RETURN_ON_ERROR)) error(FATAL, "cannot read/find pud page\n"); - machdep->last_pgd_read = mfn; + machdep->last_pud_read = mfn; if (CRASHDEBUG(7)) - x86_64_debug_dump_page(fp, machdep->pgd, + x86_64_debug_dump_page(fp, machdep->pud, "contents of page upper directory page:"); - pgd = ((ulong *)machdep->pgd) + pgd_index(kvaddr); - mfn = ((*pgd) & PHYSICAL_PAGE_MASK) >> PAGESHIFT(); + pud = ((ulong *)machdep->pud) + pud_index(kvaddr); + mfn = ((*pud) & PHYSICAL_PAGE_MASK) >> PAGESHIFT(); if (CRASHDEBUG(3)) fprintf(fp, - "[%lx] pgd: %lx mfn: %lx pgd_index: %lx\n", - kvaddr, *pgd, mfn, pgd_index(kvaddr)); + "[%lx] pud: %lx mfn: %lx pud_index: %lx\n", + kvaddr, *pgd, mfn, pud_index(kvaddr)); if (!readmem(PTOB(mfn), PHYSADDR, machdep->pmd, PAGESIZE(), "xen kdump pmd page", RETURN_ON_ERROR)) @@ -5971,21 +6489,21 @@ x86_64_xen_kdump_page_mfn(ulong kvaddr) { ulong mfn; - ulong *pml4, *pgd, *pmd, *ptep; + ulong *pgd, *pud, *pmd, *ptep; - pml4 = ((ulong *)machdep->machspec->pml4) + pml4_index(kvaddr); - mfn = ((*pml4) & PHYSICAL_PAGE_MASK) >> PAGESHIFT(); + pgd = ((ulong *)machdep->pgd) + pgd_index(kvaddr); + mfn = ((*pgd) & PHYSICAL_PAGE_MASK) >> PAGESHIFT(); - if ((mfn != machdep->last_pgd_read) && - !readmem(PTOB(mfn), PHYSADDR, machdep->pgd, PAGESIZE(), + if ((mfn != machdep->last_pud_read) && + !readmem(PTOB(mfn), PHYSADDR, machdep->pud, PAGESIZE(), "xen kdump pud entry", RETURN_ON_ERROR)) error(FATAL, "cannot read/find pud page\n"); - machdep->last_pgd_read = mfn; + machdep->last_pud_read = mfn; - pgd = ((ulong *)machdep->pgd) + pgd_index(kvaddr); - mfn = ((*pgd) & PHYSICAL_PAGE_MASK) >> PAGESHIFT(); + pud = ((ulong *)machdep->pud) + pud_index(kvaddr); + mfn = ((*pud) & PHYSICAL_PAGE_MASK) >> PAGESHIFT(); - if ((mfn != machdep->last_pmd_read) && + if ((mfn != machdep->last_pmd_read) && !readmem(PTOB(mfn), PHYSADDR, machdep->pmd, PAGESIZE(), "xen kdump pmd entry", RETURN_ON_ERROR)) error(FATAL, "cannot read/find pmd page\n"); @@ -6008,6 +6526,21 @@ #include "xendump.h" +static int +in_START_KERNEL_map(ulong vaddr) +{ + if (machdep->machspec->kernel_image_size && + ((vaddr >= __START_KERNEL_map) && + (vaddr < (__START_KERNEL_map + machdep->machspec->kernel_image_size)))) + return TRUE; + + if ((vaddr >= __START_KERNEL_map) && + (vaddr < highest_bss_symbol())) + return TRUE; + + return FALSE; +} + /* * Determine the physical address base for relocatable kernels. */ @@ -6052,7 +6585,7 @@ } } - if (ACTIVE()) { + if (LOCAL_ACTIVE()) { if ((iomem = fopen("/proc/iomem", "r")) == NULL) return; @@ -6093,9 +6626,52 @@ } /* + * Linux 4.10 exports it in VMCOREINFO (finally). + */ + if ((p1 = pc->read_vmcoreinfo("NUMBER(phys_base)"))) { + if (*p1 == '-') + machdep->machspec->phys_base = dtol(p1+1, QUIET, NULL) * -1; + else + machdep->machspec->phys_base = dtol(p1, QUIET, NULL); + if (CRASHDEBUG(1)) + fprintf(fp, "VMCOREINFO: NUMBER(phys_base): %s -> %lx\n", + p1, machdep->machspec->phys_base); + free(p1); + return; + } + + /* * Get relocation value from whatever dumpfile format is being used. */ + if (QEMU_MEM_DUMP_NO_VMCOREINFO()) { + if ((KDUMP_DUMPFILE() && kdump_phys_base(&phys_base)) || + (DISKDUMP_DUMPFILE() && diskdump_phys_base(&phys_base))) + machdep->machspec->phys_base = phys_base; + + if (!x86_64_virt_phys_base()) + error(WARNING, + "cannot determine physical base address:" + " defaulting to %lx\n\n", + machdep->machspec->phys_base); + return; + } + + if (VMSS_DUMPFILE()) { + if (vmware_vmss_phys_base(&phys_base)) { + machdep->machspec->phys_base = phys_base; + if (!x86_64_virt_phys_base()) + error(WARNING, + "cannot determine physical base address:" + " defaulting to %lx\n\n", + machdep->machspec->phys_base); + if (CRASHDEBUG(1)) + fprintf(fp, "compressed kdump: phys_base: %lx\n", + phys_base); + } + return; + } + if (DISKDUMP_DUMPFILE()) { if (diskdump_phys_base(&phys_base)) { machdep->machspec->phys_base = phys_base; @@ -6150,7 +6726,8 @@ 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_START_KERNEL_map(phdr->p_vaddr) || + !(IS_VMALLOC_ADDR(phdr->p_vaddr)))) { machdep->machspec->phys_base = phdr->p_paddr - (phdr->p_vaddr & ~(__START_KERNEL_map)); @@ -6309,12 +6886,12 @@ mfn = ctrlreg[3] >> PAGESHIFT(); - if (!xc_core_mfn_to_page(mfn, machdep->machspec->pml4)) + if (!xc_core_mfn_to_page(mfn, machdep->pgd)) error(FATAL, "cannot read/find cr3 page\n"); if (CRASHDEBUG(7)) - x86_64_debug_dump_page(xd->ofp, machdep->machspec->pml4, - "contents of PML4 page:"); + x86_64_debug_dump_page(xd->ofp, machdep->pgd, + "contents of PGD page:"); /* * kernel version < 2.6.27 => end_pfn @@ -6395,12 +6972,12 @@ mfn = ctrlreg[3] >> PAGESHIFT(); - if (!xc_core_mfn_to_page(mfn, machdep->machspec->pml4)) + if (!xc_core_mfn_to_page(mfn, machdep->pgd)) error(FATAL, "cannot read/find cr3 page\n"); if (CRASHDEBUG(7)) - x86_64_debug_dump_page(xd->ofp, machdep->machspec->pml4, - "contents of PML4 page:"); + x86_64_debug_dump_page(xd->ofp, machdep->pgd, + "contents of PGD page:"); /* * kernel version < 2.6.27 => end_pfn @@ -6576,32 +7153,32 @@ x86_64_xendump_load_page(ulong kvaddr, struct xendump_data *xd) { ulong mfn; - ulong *pml4, *pgd, *pmd, *ptep; + ulong *pgd, *pud, *pmd, *ptep; - pml4 = ((ulong *)machdep->machspec->pml4) + pml4_index(kvaddr); - mfn = ((*pml4) & PHYSICAL_PAGE_MASK) >> PAGESHIFT(); + pgd = ((ulong *)machdep->pgd) + pgd_index(kvaddr); + mfn = ((*pgd) & PHYSICAL_PAGE_MASK) >> PAGESHIFT(); if (CRASHDEBUG(3)) fprintf(xd->ofp, - "[%lx] pml4: %lx mfn: %lx pml4_index: %lx\n", - kvaddr, *pml4, mfn, pml4_index(kvaddr)); + "[%lx] pgd: %lx mfn: %lx pgd_index: %lx\n", + kvaddr, *pgd, mfn, pgd_index(kvaddr)); - if (!xc_core_mfn_to_page(mfn, machdep->pgd)) + if (!xc_core_mfn_to_page(mfn, machdep->pud)) error(FATAL, "cannot read/find pud page\n"); - machdep->last_pgd_read = mfn; + machdep->last_pud_read = mfn; if (CRASHDEBUG(7)) - x86_64_debug_dump_page(xd->ofp, machdep->pgd, + x86_64_debug_dump_page(xd->ofp, machdep->pud, "contents of page upper directory page:"); - pgd = ((ulong *)machdep->pgd) + pgd_index(kvaddr); - mfn = ((*pgd) & PHYSICAL_PAGE_MASK) >> PAGESHIFT(); + pud = ((ulong *)machdep->pud) + pud_index(kvaddr); + mfn = ((*pud) & PHYSICAL_PAGE_MASK) >> PAGESHIFT(); if (CRASHDEBUG(3)) fprintf(xd->ofp, - "[%lx] pgd: %lx mfn: %lx pgd_index: %lx\n", - kvaddr, *pgd, mfn, pgd_index(kvaddr)); + "[%lx] pud: %lx mfn: %lx pud_index: %lx\n", + kvaddr, *pud, mfn, pud_index(kvaddr)); if (!xc_core_mfn_to_page(mfn, machdep->pmd)) error(FATAL, "cannot read/find pmd page\n"); @@ -6655,18 +7232,18 @@ { int idx; ulong mfn; - ulong *pml4, *pgd, *pmd, *ptep; + ulong *pgd, *pud, *pmd, *ptep; - pml4 = ((ulong *)machdep->machspec->pml4) + pml4_index(kvaddr); - mfn = ((*pml4) & PHYSICAL_PAGE_MASK) >> PAGESHIFT(); + pgd = ((ulong *)machdep->pgd) + pgd_index(kvaddr); + mfn = ((*pgd) & PHYSICAL_PAGE_MASK) >> PAGESHIFT(); - if ((mfn != machdep->last_pgd_read) && - !xc_core_mfn_to_page(mfn, machdep->pgd)) + if ((mfn != machdep->last_pud_read) && + !xc_core_mfn_to_page(mfn, machdep->pud)) error(FATAL, "cannot read/find pud page\n"); - machdep->last_pgd_read = mfn; + machdep->last_pud_read = mfn; - pgd = ((ulong *)machdep->pgd) + pgd_index(kvaddr); - mfn = ((*pgd) & PHYSICAL_PAGE_MASK) >> PAGESHIFT(); + pud = ((ulong *)machdep->pud) + pud_index(kvaddr); + mfn = ((*pud) & PHYSICAL_PAGE_MASK) >> PAGESHIFT(); if ((mfn != machdep->last_pmd_read) && !xc_core_mfn_to_page(mfn, machdep->pmd)) @@ -7157,20 +7734,17 @@ machdep->pageoffset = machdep->pagesize - 1; machdep->pagemask = ~((ulonglong)machdep->pageoffset); machdep->stacksize = machdep->pagesize * 2; - if ((machdep->machspec->upml = (char *)malloc(PAGESIZE())) == NULL) - error(FATAL, "cannot malloc upml space."); 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->pml4 = - (char *)malloc(PAGESIZE()*2)) == NULL) - error(FATAL, "cannot malloc pml4 space."); - machdep->machspec->last_upml_read = 0; - machdep->machspec->last_pml4_read = 0; + machdep->last_pgd_read = 0; + machdep->last_pud_read = 0; machdep->last_pmd_read = 0; machdep->last_ptbl_read = 0; machdep->verify_paddr = generic_verify_paddr; @@ -7271,8 +7845,90 @@ return TRUE; } +ulong *x86_64_framesize_no_cache = NULL; +static int framesize_no_cache_entries = 0; +#define FRAMESIZE_NO_CACHE_INCR (10) + +static int +x86_64_do_not_cache_framesize(struct syment *sp, ulong textaddr) +{ + int c, instr, arg; + char buf[BUFSIZE]; + char *arglist[MAXARGS]; + ulong *new_fnc; + + if (x86_64_framesize_no_cache[framesize_no_cache_entries-1]) { + if ((new_fnc = realloc(x86_64_framesize_no_cache, + (framesize_no_cache_entries+FRAMESIZE_NO_CACHE_INCR) * + sizeof(ulong))) == NULL) { + error(INFO, "cannot realloc x86_64_framesize_no_cache space!\n"); + return FALSE; + } + x86_64_framesize_no_cache = new_fnc; + for (c = framesize_no_cache_entries; + c < framesize_no_cache_entries + FRAMESIZE_NO_CACHE_INCR; c++) + x86_64_framesize_no_cache[c] = 0; + framesize_no_cache_entries += FRAMESIZE_NO_CACHE_INCR; + } + + for (c = 0; c < framesize_no_cache_entries; c++) + if (x86_64_framesize_no_cache[c] == sp->value) + return TRUE; + + if (!accessible(sp->value)) + return FALSE; + + sprintf(buf, "disassemble 0x%lx,0x%lx", sp->value, textaddr); + + open_tmpfile2(); + + if (!gdb_pass_through(buf, pc->tmpfile2, GNU_RETURN_ON_ERROR)) { + close_tmpfile2(); + return FALSE; + } + + rewind(pc->tmpfile2); + instr = arg = -1; + while (fgets(buf, BUFSIZE, pc->tmpfile2)) { + if (STRNEQ(buf, "Dump of assembler code")) + continue; + else if (STRNEQ(buf, "End of assembler dump.")) + break; + else if ((c = parse_line(buf, arglist)) < 3) + continue; + + if (instr == -1) { + if (LASTCHAR(arglist[0]) == ':') { + instr = 1; + arg = 2; + } else { + instr = 2; + arg = 3; + } + } + + if (STREQ(arglist[instr], "and") && + STREQ(arglist[arg], "$0xfffffffffffffff0,%rsp")) { + close_tmpfile2(); + for (c = 0; c < framesize_no_cache_entries; c++) { + if (x86_64_framesize_no_cache[c] == 0) { + x86_64_framesize_no_cache[c] = sp->value; + break; + } + } + return TRUE; + } + + if (STREQ(arglist[instr], "callq")) + break; + } + close_tmpfile2(); + + return FALSE; +} + static int -x86_64_framesize_cache_func(int cmd, ulong textaddr, int *framesize, int exception) +x86_64_framesize_cache_func(int cmd, ulong textaddr, int *framesize, int exception, struct syment *sp) { int i, n; struct framesize_cache *fc; @@ -7284,6 +7940,10 @@ sizeof(struct framesize_cache))) == NULL) error(FATAL, "cannot calloc x86_64_framesize_cache space!\n"); + framesize_no_cache_entries = FRAMESIZE_NO_CACHE_INCR; + if ((x86_64_framesize_no_cache = calloc(framesize_no_cache_entries, + sizeof(ulong))) == NULL) + error(FATAL, "cannot calloc x86_64_framesize_no_cache space!\n"); } switch (cmd) @@ -7301,6 +7961,8 @@ return FALSE; case FRAMESIZE_ENTER: + if (sp && x86_64_do_not_cache_framesize(sp, textaddr)) + return *framesize; retry: fc = &x86_64_framesize_cache[0]; for (i = 0; i < framesize_cache_entries; i++, fc++) { @@ -7329,21 +7991,36 @@ return *framesize; case FRAMESIZE_DUMP: + fprintf(fp, "framesize_cache_entries:\n"); fc = &x86_64_framesize_cache[0]; for (i = 0; i < framesize_cache_entries; i++, fc++) { if (fc->textaddr == 0) { if (i < (framesize_cache_entries-1)) { - fprintf(fp, "[%d-%d]: (unused)\n", + fprintf(fp, " [%d-%d]: (unused)\n", i, framesize_cache_entries-1); } break; } - fprintf(fp, "[%3d]: %lx %3d %s (%s)\n", i, + fprintf(fp, " [%3d]: %lx %3d %s (%s)\n", i, fc->textaddr, fc->framesize, fc->exception ? "EX" : "CF", value_to_symstr(fc->textaddr, buf, 0)); } + + fprintf(fp, "\nframesize_no_cache_entries:\n"); + for (i = 0; i < framesize_no_cache_entries; i++) { + if (x86_64_framesize_no_cache[i]) + fprintf(fp, " [%3d]: %lx (%s)\n", + i, x86_64_framesize_no_cache[i], + value_to_symstr(x86_64_framesize_no_cache[i], buf, 0)); + else { + fprintf(fp, " [%d-%d]: (unused)\n", + i, framesize_no_cache_entries-1); + break; + } + } + break; } @@ -7450,6 +8127,7 @@ int reterror; int arg_exists; int exception; + kernel_orc_entry *korc; if (!(bt->flags & BT_FRAMESIZE_DEBUG)) { if ((bt->flags & BT_FRAMESIZE_IGNORE_MASK) || @@ -7471,7 +8149,7 @@ if (!(bt->flags & BT_FRAMESIZE_DEBUG) && x86_64_framesize_cache_func(FRAMESIZE_QUERY, textaddr, &framesize, - exception)) { + exception, NULL)) { if (framesize == -1) bt->flags |= BT_FRAMESIZE_DISABLE; return framesize; @@ -7508,7 +8186,7 @@ else framesize = 8; return (x86_64_framesize_cache_func(FRAMESIZE_ENTER, textaddr, - &framesize, exception)); + &framesize, exception, NULL)); } if ((machdep->flags & FRAMEPOINTER) && @@ -7526,13 +8204,28 @@ if (framepointer) { framesize = framepointer - rsp; return (x86_64_framesize_cache_func(FRAMESIZE_ENTER, - textaddr, &framesize, 0)); + textaddr, &framesize, 0, sp)); } } if ((sp->value >= kt->init_begin) && (sp->value < kt->init_end)) return 0; + if ((machdep->flags & ORC) && (korc = orc_find(textaddr))) { + if (CRASHDEBUG(1)) + fprintf(fp, + "rsp: %lx textaddr: %lx framesize: %d -> spo: %d bpo: %d spr: %d bpr: %d type: %d %s\n", + rsp, textaddr, framesize, korc->sp_offset, korc->bp_offset, + korc->sp_reg, korc->bp_reg, korc->type, + (korc->type == ORC_TYPE_CALL) && (korc->sp_reg == ORC_REG_SP) ? "" : "(UNUSED)"); + + if ((korc->type == ORC_TYPE_CALL) && (korc->sp_reg == ORC_REG_SP)) { + framesize = (korc->sp_offset - 8); + return (x86_64_framesize_cache_func(FRAMESIZE_ENTER, textaddr, + &framesize, exception, NULL)); + } + } + framesize = max = 0; max_instructions = textaddr - sp->value; instr = arg = -1; @@ -7650,7 +8343,7 @@ textaddr = textaddr_save; return (x86_64_framesize_cache_func(FRAMESIZE_ENTER, textaddr, - &framesize, exception)); + &framesize, exception, NULL)); } static void @@ -7664,18 +8357,20 @@ switch (bt->hp->esp) { case 1: /* "dump" */ - x86_64_framesize_cache_func(FRAMESIZE_DUMP, 0, NULL, 0); + x86_64_framesize_cache_func(FRAMESIZE_DUMP, 0, NULL, 0, NULL); break; case 0: if (bt->hp->eip) { /* clear one entry */ framesize = -1; x86_64_framesize_cache_func(FRAMESIZE_ENTER, bt->hp->eip, - &framesize, exception); + &framesize, exception, NULL); } else { /* clear all entries */ BZERO(&x86_64_framesize_cache[0], sizeof(struct framesize_cache)*framesize_cache_entries); - fprintf(fp, "framesize cache cleared\n"); + BZERO(&x86_64_framesize_no_cache[0], + sizeof(ulong)*framesize_no_cache_entries); + fprintf(fp, "framesize caches cleared\n"); } break; @@ -7690,16 +8385,27 @@ machdep->flags |= FRAMEPOINTER; BZERO(&x86_64_framesize_cache[0], sizeof(struct framesize_cache)*framesize_cache_entries); + BZERO(&x86_64_framesize_no_cache[0], + sizeof(ulong)*framesize_no_cache_entries); fprintf(fp, - "framesize cache cleared and FRAMEPOINTER turned ON\n"); + "framesize caches cleared and FRAMEPOINTER turned ON\n"); break; case -4: machdep->flags &= ~FRAMEPOINTER; BZERO(&x86_64_framesize_cache[0], sizeof(struct framesize_cache)*framesize_cache_entries); + BZERO(&x86_64_framesize_no_cache[0], + sizeof(ulong)*framesize_no_cache_entries); fprintf(fp, - "framesize cache cleared and FRAMEPOINTER turned OFF\n"); + "framesize caches cleared and FRAMEPOINTER turned OFF\n"); + break; + + case -5: + if (!bt->hp->eip) + error(INFO, "x86_64_framesize_debug: ignoring command (no ip)\n"); + else + orc_dump(bt->hp->eip); break; default: @@ -7707,7 +8413,7 @@ framesize = bt->hp->esp; if (bt->hp->eip) x86_64_framesize_cache_func(FRAMESIZE_ENTER, bt->hp->eip, - &framesize, exception); + &framesize, exception, NULL); } else error(INFO, "x86_64_framesize_debug: ignoring command\n"); break; @@ -7734,7 +8440,7 @@ rsp_in, bt->stackbase, bt->stacktop, bt->tc->processor); if (x86_64_framesize_cache_func(FRAMESIZE_QUERY, - machdep->machspec->thread_return, &framesize, 0)) { + machdep->machspec->thread_return, &framesize, 0, NULL)) { rsp = rsp_in + framesize; i = (rsp - bt->stackbase)/sizeof(ulong); up = (ulong *)(&bt->stackbuf[i*sizeof(ulong)]); @@ -7762,7 +8468,7 @@ bt->instptr = *up; x86_64_framesize_cache_func(FRAMESIZE_ENTER, machdep->machspec->thread_return, - &framesize, 0); + &framesize, 0, NULL); bt->instptr = *up; found = TRUE; break; @@ -7958,4 +8664,313 @@ return TRUE; } +static ulong +orc_ip(ulong ip) +{ + int ip_entry; + + if (!readmem((ulong)ip, KVADDR, &ip_entry, sizeof(int), + "orc_ip", QUIET|RETURN_ON_ERROR)) + return 0; + + return (ip + ip_entry); +} + +static kernel_orc_entry * +__orc_find(ulong ip_table_ptr, ulong u_table_ptr, uint num_entries, ulong ip) +{ + int index; + int *first = (int *)ip_table_ptr; + int *last = (int *)ip_table_ptr + num_entries - 1; + int *mid = first, *found = first; + int *ip_table = (int *)ip_table_ptr; + struct ORC_data *orc = &machdep->machspec->orc; + ulong vaddr; + kernel_orc_entry *korc; + + if (CRASHDEBUG(2)) { + int i, ip_entry; + ulong ptr; + ulong offset; + struct syment *sp; + + fprintf(fp, "__orc_find:\n ip: %lx num_entries: %d\n", + ip, num_entries); + + for (i = 0; i < num_entries; i++) { + ptr = ip_table_ptr + (i*4); + if (!readmem((ulong)ptr, KVADDR, &ip_entry, sizeof(int), + "ip entry", RETURN_ON_ERROR)) + return NULL; + if (!(vaddr = orc_ip(ptr))) + return NULL; + fprintf(fp, " orc_ip(%lx): %x -> %lx / ", ptr, ip_entry, vaddr); + if ((sp = value_search(vaddr, &offset))) { + fprintf(fp, "%s+%ld -> ", sp->name, offset); + fprintf(fp, "%lx\n", u_table_ptr + (i * SIZE(orc_entry))); + } else + fprintf(fp, "(unknown symbol value)\n"); + } + } + + while (first <= last) { + mid = first + ((last - first) / 2); + + if (!(vaddr = orc_ip((ulong)mid))) + return NULL; + + if (vaddr <= ip) { + found = mid; + first = mid + 1; + } else + last = mid - 1; + } + + index = found - ip_table; + + orc->ip_entry = (ulong)found; + orc->orc_entry = u_table_ptr + (index * SIZE(orc_entry)); + if (!readmem(orc->orc_entry, KVADDR, &orc->kernel_orc_entry, + sizeof(kernel_orc_entry), "kernel orc_entry", RETURN_ON_ERROR|QUIET)) + return NULL; + + korc = &orc->kernel_orc_entry; + + if (CRASHDEBUG(2)) { + fprintf(fp, " found: %lx index: %d\n", (ulong)found, index); + fprintf(fp, + " orc_entry: %lx sp_offset: %d bp_offset: %d sp_reg: %d bp_reg: %d type: %d\n", + orc->orc_entry, korc->sp_offset, korc->bp_offset, korc->sp_reg, korc->bp_reg, korc->type); + } + + return korc; +} + +#define LOOKUP_BLOCK_ORDER 8 +#define LOOKUP_BLOCK_SIZE (1 << LOOKUP_BLOCK_ORDER) +#define LOOKUP_START_IP (unsigned long)kt->stext +#define LOOKUP_STOP_IP (unsigned long)kt->etext + +static kernel_orc_entry * +orc_find(ulong ip) +{ + unsigned int idx, start, stop; + struct ORC_data *orc = &machdep->machspec->orc; + + if ((ip < LOOKUP_START_IP) || (ip >= LOOKUP_STOP_IP)) { + if ((ip >= MODULES_VADDR) && (ip < MODULES_END)) + return orc_module_find(ip); + error(WARNING, + "%lx: ip is outside kernel and module text ranges\n", ip); + return NULL; + } + + idx = (ip - LOOKUP_START_IP) / LOOKUP_BLOCK_SIZE; + + if (idx >= orc->lookup_num_blocks-1) { + if (CRASHDEBUG(1)) { + error(INFO, "bad lookup: idx: %u lookup_num_blocks: %u ip: %lx\n", + idx, orc->lookup_num_blocks, ip); + } + return NULL; + } + + if (!readmem(orc->orc_lookup + (sizeof(unsigned int) * idx), KVADDR, + &start, sizeof(unsigned int), "orc_lookup start", RETURN_ON_ERROR|QUIET)) { + if (CRASHDEBUG(1)) + error(INFO, "cannot read \"start\" orc_lookup entry at %lx\n", + orc->orc_lookup + (sizeof(unsigned int) * idx)); + return NULL; + } + if (!readmem(orc->orc_lookup + (sizeof(unsigned int) * (idx+1)), KVADDR, + &stop, sizeof(unsigned int), "orc_lookup stop", RETURN_ON_ERROR|QUIET)) { + if (CRASHDEBUG(1)) + error(INFO, "cannot read \"stop\" orc_lookup entry at %lx\n", + orc->orc_lookup + (sizeof(unsigned int) * (idx+1))); + return NULL; + } + stop += 1; + + if (CRASHDEBUG(2)) { + fprintf(fp, "orc_find:\n ip: %lx idx: %d\n", ip, idx); + fprintf(fp, " start = orc_lookup[%d]: %d\n" + " stop = orc_lookup[%d] + 1: %d\n", + idx, start, idx+1, stop); + fprintf(fp, " ip table start: %lx\n", + orc->__start_orc_unwind_ip + (start * sizeof(int))); + fprintf(fp, " unwind table start: %lx\n", + orc->__start_orc_unwind + (start * SIZE(orc_entry))); + } + + if ((orc->__start_orc_unwind + (start * SIZE(orc_entry))) >= orc->__stop_orc_unwind) { + if (CRASHDEBUG(1)) + error(INFO, + "bad unwind lookup start: idx: %u num: %u start: %u stop: %u ip: %lx\n", + idx, orc->lookup_num_blocks, start, stop, ip); + return NULL; + } + if ((orc->__start_orc_unwind + (stop * SIZE(orc_entry))) > orc->__stop_orc_unwind) { + if (CRASHDEBUG(1)) + error(INFO, + "bad unwind lookup stop: idx: %u num: %u start: %u stop: %u ip: %lx\n", + idx, orc->lookup_num_blocks, start, stop, ip); + return NULL; + } + + return __orc_find(orc->__start_orc_unwind_ip + (start * sizeof(int)), + orc->__start_orc_unwind + (start * SIZE(orc_entry)), stop - start, ip); +} + +static kernel_orc_entry * +orc_module_find(ulong ip) +{ + struct load_module *lm; + uint num_orcs; + ulong orc_unwind_ip, orc_unwind, module_arch; + struct ORC_data *orc = &machdep->machspec->orc; + + if (!(orc->module_ORC) || !module_symbol(ip, NULL, &lm, NULL, 0)) + return NULL; + + module_arch = lm->module_struct + OFFSET(module_arch); + + if (!readmem(module_arch + OFFSET(mod_arch_specific_num_orcs), KVADDR, + &num_orcs, sizeof(int), "module num_orcs", RETURN_ON_ERROR|QUIET)) + return NULL; + if (!readmem(module_arch + OFFSET(mod_arch_specific_orc_unwind_ip), KVADDR, + &orc_unwind_ip, sizeof(void *), "module orc_unwind_ip", RETURN_ON_ERROR|QUIET)) + return NULL; + if (!readmem(module_arch + OFFSET(mod_arch_specific_orc_unwind), KVADDR, + &orc_unwind, sizeof(void *), "module orc_unwind", RETURN_ON_ERROR|QUIET)) + return NULL; + + if (CRASHDEBUG(2)) { + fprintf(fp, "orc_module_find:\n"); + fprintf(fp, " num_orcs: %d orc_unwind_ip: %lx orc_unwind: %lx\n", + num_orcs, orc_unwind_ip, orc_unwind); + } + + return __orc_find(orc_unwind_ip, orc_unwind, num_orcs, ip); +} + +static ulong +ip_table_to_vaddr(ulong ip_table) +{ + int ip_entry; + + if (!readmem((ulong)ip_table, KVADDR, &ip_entry, sizeof(int), "ip entry", RETURN_ON_ERROR)) + error(FATAL, "ip_table_to_vaddr: cannot read ip_table: %lx\n", ip_table); + + return (ip_table + ip_entry); +} + +static void +orc_dump(ulong ip) +{ + struct ORC_data *orc = &machdep->machspec->orc; + kernel_orc_entry *korc; + ulong vaddr, offset; + struct syment *sp, *orig; + + fprintf(fp, "orc_dump: %lx / ", ip); + if ((sp = value_search(ip, &offset))) + fprintf(fp, "%s+%ld\n--------\n", sp->name, offset); + else + fprintf(fp, "(unresolved)\n--------\n"); + orig = sp; + + if (!orc_find(ip)) { + fprintf(fp, "%lx: ip not found\n", ip); + return; + } + +next_in_func: + fprintf(fp, "ip: %lx -> %lx / ", orc->ip_entry, + vaddr = ip_table_to_vaddr(orc->ip_entry)); + if ((sp = value_search(vaddr, &offset))) + fprintf(fp, "%s+%ld -> ", sp->name, offset); + else + fprintf(fp, "(unresolved) -> "); + if (!readmem(orc->orc_entry, KVADDR, &orc->kernel_orc_entry, sizeof(kernel_orc_entry), + "kernel orc_entry", RETURN_ON_ERROR)) + error(FATAL, "cannot read orc_entry\n"); + korc = &orc->kernel_orc_entry; + fprintf(fp, "orc: %lx spo: %d bpo: %d spr: %d bpr: %d type: %d\n", + orc->orc_entry, korc->sp_offset, korc->bp_offset, korc->sp_reg, korc->bp_reg, korc->type); + + orc->ip_entry += sizeof(int); + orc->orc_entry += sizeof(kernel_orc_entry); + vaddr = ip_table_to_vaddr(orc->ip_entry); + if ((sp = value_search(vaddr, &offset))) + if (sp == orig) + goto next_in_func; +} + +/* + * KPTI entry stack initialization. May vary signficantly + * between upstream and distribution backports. + */ +static void +x86_64_entry_trampoline_init(void) +{ + struct machine_specific *ms; + struct syment *sp; + + ms = machdep->machspec; + + if (!kernel_symbol_exists("pti_init") && + !kernel_symbol_exists("kaiser_init")) + return; + + /* + * 4.15 + */ + if (MEMBER_EXISTS("entry_stack", "words") && + MEMBER_EXISTS("entry_stack_page", "stack") && + (sp = per_cpu_symbol_search("per_cpu__entry_stack_storage"))) { + ms->kpti_entry_stack = sp->value + MEMBER_OFFSET("entry_stack_page", "stack"); + ms->kpti_entry_stack_size = MEMBER_SIZE("entry_stack", "words"); + machdep->flags |= KPTI; + return; + } + + /* + * RHEL + */ + if (MEMBER_EXISTS("tss_struct", "stack")) { + if (!(sp = per_cpu_symbol_search("per_cpu__init_tss"))) + sp = per_cpu_symbol_search("per_cpu__cpu_tss"); + ms->kpti_entry_stack = sp->value + MEMBER_OFFSET("tss_struct", "stack"); + ms->kpti_entry_stack_size = MEMBER_SIZE("tss_struct", "stack"); + machdep->flags |= KPTI; + return; + } +} + +static ulong +x86_64_in_kpti_entry_stack(int cpu, ulong rsp) +{ + ulong stack_base, stack_end; + struct machine_specific *ms; + + if (!(machdep->flags & KPTI)) + return 0; + + ms = machdep->machspec; + + if ((kt->flags & SMP) && (kt->flags & PER_CPU_OFF)) { + if (kt->__per_cpu_offset[cpu] == 0) + return 0; + stack_base = ms->kpti_entry_stack + kt->__per_cpu_offset[cpu]; + } else + stack_base = ms->kpti_entry_stack; + + stack_end = stack_base + + (ms->kpti_entry_stack_size > 0 ? ms->kpti_entry_stack_size : 512); + + if ((rsp >= stack_base) && (rsp < stack_end)) + return(stack_end - SIZE(pt_regs)); + + return 0; +} #endif /* X86_64 */ diff -Nru crash-7.1.4/x86.c crash-7.2.3+real/x86.c --- crash-7.1.4/x86.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/x86.c 2018-05-17 17:39:38.000000000 +0000 @@ -1,8 +1,8 @@ /* x86.c - core analysis suite * * Portions Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. - * Copyright (C) 2002-2014 David Anderson - * Copyright (C) 2002-2014 Red Hat, Inc. All rights reserved. + * Copyright (C) 2002-2014,2017 David Anderson + * Copyright (C) 2002-2014,2017 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 @@ -178,6 +178,7 @@ static int db_sym_numargs(db_sym_t, int *, char **); static void x86_dump_line_number(ulong); static void x86_clear_machdep_cache(void); +static void x86_parse_cmdline_args(void); static ulong mach_debug = 0; @@ -1774,6 +1775,7 @@ machdep->last_ptbl_read = 0; machdep->machspec = &x86_machine_specific; machdep->verify_paddr = generic_verify_paddr; + x86_parse_cmdline_args(); break; case PRE_GDB: @@ -1797,7 +1799,12 @@ machdep->pmd = machdep->pgd; } machdep->ptrs_per_pgd = PTRS_PER_PGD; - machdep->kvbase = symbol_value("_stext") & ~KVBASE_MASK; + if (!machdep->kvbase) { + if (kernel_symbol_exists("module_kaslr_mutex")) + machdep->kvbase = 0xc0000000; + else + machdep->kvbase = symbol_value("_stext") & ~KVBASE_MASK; + } if (machdep->kvbase & 0x80000000) machdep->is_uvaddr = generic_is_uvaddr; else { @@ -1963,6 +1970,8 @@ STRUCT_SIZE_INIT(e820map, "e820map"); STRUCT_SIZE_INIT(e820entry, "e820entry"); STRUCT_SIZE_INIT(irq_ctx, "irq_ctx"); + if (!VALID_STRUCT(irq_ctx)) + STRUCT_SIZE_INIT(irq_ctx, "irq_stack"); MEMBER_OFFSET_INIT(e820map_nr_map, "e820map", "nr_map"); MEMBER_OFFSET_INIT(e820entry_addr, "e820entry", "addr"); MEMBER_OFFSET_INIT(e820entry_size, "e820entry", "size"); @@ -2031,6 +2040,8 @@ if (!remap_init()) machdep->machspec->max_numnodes = -1; + MEMBER_OFFSET_INIT(inactive_task_frame_ret_addr, + "inactive_task_frame", "ret_addr"); break; case POST_INIT: @@ -2044,6 +2055,63 @@ } /* + * Handle non-default (c0000000) values of CONFIG_PAGE_OFFSET + * with "--machdep page_offset=
" + */ +static void +x86_parse_cmdline_args(void) +{ + int index, i, c, err; + char *arglist[MAXARGS]; + char buf[BUFSIZE]; + char *p; + ulong value = 0; + + for (index = 0; index < MAX_MACHDEP_ARGS; index++) { + if (!machdep->cmdline_args[index]) + break; + + if (!strstr(machdep->cmdline_args[index], "=")) { + error(WARNING, "ignoring --machdep option: %x\n", + machdep->cmdline_args[index]); + continue; + } + + strcpy(buf, machdep->cmdline_args[index]); + + for (p = buf; *p; p++) { + if (*p == ',') + *p = ' '; + } + + c = parse_line(buf, arglist); + + for (i = 0; i < c; i++) { + err = 0; + + if (STRNEQ(arglist[i], "page_offset=")) { + int flags = RETURN_ON_ERROR | QUIET; + + p = arglist[i] + strlen("page_offset="); + if (strlen(p)) + value = htol(p, flags, &err); + + if (!err) { + machdep->kvbase = value; + + error(NOTE, "setting PAGE_OFFSET to: 0x%lx\n\n", + machdep->kvbase); + continue; + } + } + + error(WARNING, "ignoring --machdep option: %s\n", + arglist[i]); + } + } +} + +/* * Account for addition of pt_regs.xgs field in 2.6.20+ kernels. */ static void @@ -3640,12 +3708,22 @@ x86_get_pc(struct bt_info *bt) { ulong offset; - ulong eip; + ulong eip, inactive_task_frame; if (tt->flags & THREAD_INFO) { - readmem(bt->task + OFFSET(task_struct_thread_eip), KVADDR, - &eip, sizeof(void *), - "thread_struct eip", FAULT_ON_ERROR); + if (VALID_MEMBER(task_struct_thread_eip)) + readmem(bt->task + OFFSET(task_struct_thread_eip), KVADDR, + &eip, sizeof(void *), + "thread_struct eip", FAULT_ON_ERROR); + else if (VALID_MEMBER(inactive_task_frame_ret_addr)) { + readmem(bt->task + OFFSET(task_struct_thread_esp), KVADDR, + &inactive_task_frame, sizeof(void *), + "task_struct.inactive_task_frame", FAULT_ON_ERROR); + readmem(inactive_task_frame + OFFSET(inactive_task_frame_ret_addr), + KVADDR, &eip, sizeof(void *), + "inactive_task_frame.ret_addr", FAULT_ON_ERROR); + } else + error(FATAL, "cannot determine ip address\n"); return eip; } @@ -3670,6 +3748,8 @@ readmem(bt->task + OFFSET(task_struct_thread_esp), KVADDR, &ksp, sizeof(void *), "thread_struct esp", FAULT_ON_ERROR); + if (VALID_MEMBER(inactive_task_frame_ret_addr)) + ksp += OFFSET(inactive_task_frame_ret_addr); return ksp; } diff -Nru crash-7.1.4/xen_dom0.c crash-7.2.3+real/xen_dom0.c --- crash-7.1.4/xen_dom0.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/xen_dom0.c 2018-05-17 17:39:38.000000000 +0000 @@ -20,7 +20,8 @@ #include "xen_dom0.h" static struct xen_kdump_data xen_kdump_data = { 0 }; -static struct xen_kdump_data *xkd = &xen_kdump_data; + +struct xen_kdump_data *xkd = &xen_kdump_data; void dump_xen_kdump_data(FILE *fp) diff -Nru crash-7.1.4/xen_dom0.h crash-7.2.3+real/xen_dom0.h --- crash-7.1.4/xen_dom0.h 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/xen_dom0.h 2018-05-17 17:39:38.000000000 +0000 @@ -68,6 +68,8 @@ #define P2M_FAILURE ((physaddr_t)(0xffffffffffffffffLL)) +extern struct xen_kdump_data *xkd; + void dump_xen_kdump_data(FILE *); struct xen_kdump_data *get_xen_kdump_data(void); diff -Nru crash-7.1.4/xendump.c crash-7.2.3+real/xendump.c --- crash-7.1.4/xendump.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/xendump.c 2018-05-17 17:39:38.000000000 +0000 @@ -19,7 +19,7 @@ #include "xendump.h" static struct xendump_data xendump_data = { 0 }; -static struct xendump_data *xd = &xendump_data; +struct xendump_data *xd = &xendump_data; static int xc_save_verify(char *); static int xc_core_verify(char *, char *); @@ -1260,7 +1260,7 @@ "cannot create xen pfn-to-mfn mapping\n"); } - if (xd->flags & XC_CORE_ELF) + if (xd->flags & XC_CORE_PFN_CREATE) xc_core_elf_pfn_init(); xd->flags &= ~(XC_CORE_P2M_CREATE|XC_CORE_PFN_CREATE); @@ -2557,7 +2557,7 @@ if (STREQ(name, ".xen_p2m")) { xd->xc_core.header.xch_index_offset = (off_t)shdr.sh_offset; - xd->flags |= XC_CORE_P2M_CREATE; + xd->flags |= XC_CORE_PFN_CREATE; } if (STREQ(name, ".xen_pages")) @@ -2659,7 +2659,7 @@ if (STREQ(name, ".xen_p2m")) { xd->xc_core.header.xch_index_offset = (off_t)shdr.sh_offset; - xd->flags |= XC_CORE_P2M_CREATE; + xd->flags |= XC_CORE_PFN_CREATE; } if (STREQ(name, ".xen_pages")) diff -Nru crash-7.1.4/xendump.h crash-7.2.3+real/xendump.h --- crash-7.1.4/xendump.h 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/xendump.h 2018-05-17 17:39:38.000000000 +0000 @@ -192,3 +192,5 @@ uint64_t pfn; uint64_t gmfn; }; + +extern struct xendump_data *xd; diff -Nru crash-7.1.4/xen_hyper.c crash-7.2.3+real/xen_hyper.c --- crash-7.1.4/xen_hyper.c 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/xen_hyper.c 2018-05-17 17:39:38.000000000 +0000 @@ -435,19 +435,30 @@ /* * Do initialization for scheduler of Xen Hyper system here. */ -#define XEN_HYPER_SCHEDULERS_ARRAY_CNT 10 #define XEN_HYPER_SCHEDULER_NAME 1024 +static int section_size(char *start_section, char *end_section) +{ + ulong sp_start, sp_end; + + sp_start = symbol_value(start_section); + sp_end = symbol_value(end_section); + + return (sp_end - sp_start) / sizeof(long); +} + static void xen_hyper_schedule_init(void) { ulong addr, opt_sched, schedulers, opt_name; long scheduler_opt_name; - long schedulers_buf[XEN_HYPER_SCHEDULERS_ARRAY_CNT]; + long *schedulers_buf; + int nr_schedulers; struct xen_hyper_sched_context *schc; char *buf; char opt_name_buf[XEN_HYPER_OPT_SCHED_SIZE]; int i, cpuid, flag; + char *sp_name; /* get scheduler information */ if((xhscht->scheduler_struct = @@ -469,15 +480,27 @@ XEN_HYPER_OPT_SCHED_SIZE, "opt_sched,", RETURN_ON_ERROR)) { error(FATAL, "cannot read opt_sched,.\n"); } - schedulers = symbol_value("schedulers"); + + /* symbol exists since Xen 4.7 */ + if (symbol_exists("__start_schedulers_array")) { + sp_name = "__start_schedulers_array"; + nr_schedulers = section_size("__start_schedulers_array", + "__end_schedulers_array"); + } else { + sp_name = "schedulers"; + nr_schedulers = get_array_length("schedulers", 0, 0); + } + + schedulers_buf = (long *)GETBUF(nr_schedulers * sizeof(long)); + schedulers = symbol_value(sp_name); addr = schedulers; while (xhscht->name == NULL) { if (!readmem(addr, KVADDR, schedulers_buf, - sizeof(long) * XEN_HYPER_SCHEDULERS_ARRAY_CNT, - "schedulers", RETURN_ON_ERROR)) { + sizeof(long) * nr_schedulers, + "schedulers", RETURN_ON_ERROR)) { error(FATAL, "cannot read schedulers.\n"); } - for (i = 0; i < XEN_HYPER_SCHEDULERS_ARRAY_CNT; i++) { + for (i = 0; i < nr_schedulers; i++) { if (schedulers_buf[i] == 0) { error(FATAL, "schedule data not found.\n"); } @@ -511,12 +534,13 @@ error(FATAL, "cannot malloc scheduler_name space.\n"); } BZERO(xhscht->name, strlen(buf) + 1); - strncpy(xhscht->name, buf, strlen(buf)); + BCOPY(buf, xhscht->name, strlen(buf)); break; } - addr += sizeof(long) * XEN_HYPER_SCHEDULERS_ARRAY_CNT; + addr += sizeof(long) * nr_schedulers; } FREEBUF(buf); + FREEBUF(schedulers_buf); /* get schedule_data information */ if((xhscht->sched_context_array = diff -Nru crash-7.1.4/xen_hyper_defs.h crash-7.2.3+real/xen_hyper_defs.h --- crash-7.1.4/xen_hyper_defs.h 2015-12-16 15:59:36.000000000 +0000 +++ crash-7.2.3+real/xen_hyper_defs.h 2018-05-17 17:39:38.000000000 +0000 @@ -63,7 +63,7 @@ #define HYPERVISOR_VIRT_START (0xffff800000000000) #define HYPERVISOR_VIRT_END (0xffff880000000000) #define DIRECTMAP_VIRT_START (0xffff830000000000) -#define DIRECTMAP_VIRT_END (0xffff840000000000) +#define DIRECTMAP_VIRT_END (0xffff880000000000) #define PAGE_OFFSET_XEN_HYPER DIRECTMAP_VIRT_START #define XEN_VIRT_START (xht->xen_virt_start) #define XEN_VIRT_ADDR(vaddr) \