commit 5fe78861ea1589084f6a2956a6ff63677c9269e1 Author: Dave Anderson Date: Fri Sep 7 16:05:52 2018 -0400 Commit 3db3d3992d781c1e42587d2d2bf81e785408e0c2 in crash-7.1.8 was aimed at making the PPC64 "bt" command work for dumpfiles saved with the FADUMP facility, but it introduced a bit of unwarranted complexity in "bt" command processing. Reworked the "bt" command processing for PPC64 arch to make it a little less compilated and also to print symbols for NIP and LR registers in exception frames. Without the patch, "bt" on non-panic active tasks may fail with the message "bt: invalid kernel virtual address:
type: Regs NIP value". (hbathini@linux.ibm.com) diff --git a/ppc64.c b/ppc64.c index f5d0dac..03fecd3 100644 --- a/ppc64.c +++ b/ppc64.c @@ -2093,15 +2093,10 @@ ppc64_print_stack_entry(int frame, lr); return; } - if (req->pc != lr) { - fprintf(fp, "\n%s[Link Register] ", - frame < 10 ? " " : ""); - fprintf(fp, "[%lx] %s at %lx", - req->sp, lrname, lr); - } req->ra = lr; } - if (!req->name || STREQ(req->name,lrname)) + if (!req->name || STREQ(req->name, lrname) || + !is_kernel_text(req->pc)) fprintf(fp, " (unreliable)"); fprintf(fp, "\n"); @@ -2219,6 +2214,22 @@ ppc64_print_regs(struct ppc64_pt_regs *regs) fprintf(fp, " Syscall Result: %016lx\n", regs->result); } +static void ppc64_print_nip_lr(struct ppc64_pt_regs *regs, int print_lr) +{ + char buf[BUFSIZE]; + char *sym_buf; + + sym_buf = value_to_symstr(regs->nip, buf, 0); + if (sym_buf[0] != NULLCHAR) + fprintf(fp, " [NIP : %s]\n", sym_buf); + + if (print_lr) { + sym_buf = value_to_symstr(regs->link, buf, 0); + if (sym_buf[0] != NULLCHAR) + fprintf(fp, " [LR : %s]\n", sym_buf); + } +} + /* * Print the exception frame information */ @@ -2231,6 +2242,59 @@ ppc64_print_eframe(char *efrm_str, struct ppc64_pt_regs *regs, fprintf(fp, " %s [%lx] exception frame:\n", efrm_str, regs->trap); ppc64_print_regs(regs); + ppc64_print_nip_lr(regs, 1); +} + +/* + * For vmcore typically saved with KDump or FADump, get SP and IP values + * from the saved ptregs. + */ +static int +ppc64_vmcore_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 in case of KDump OR f/w has not + * not provided the register info in case of FADump. + */ + 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; + + ppc64_print_nip_lr(pt_regs, (unip != pt_regs->link) ? 1 : 0); + + return TRUE; } /* @@ -2239,7 +2303,7 @@ ppc64_print_eframe(char *efrm_str, struct ppc64_pt_regs *regs, static int ppc64_get_dumpfile_stack_frame(struct bt_info *bt_in, ulong *nip, ulong *ksp) { - int i; + int i, ret, panic_task; char *sym; ulong *up; struct bt_info bt_local, *bt; @@ -2251,11 +2315,29 @@ ppc64_get_dumpfile_stack_frame(struct bt_info *bt_in, ulong *nip, ulong *ksp) struct ppc64_pt_regs *pt_regs; struct syment *sp; - bt = &bt_local; - BCOPY(bt_in, bt, sizeof(struct bt_info)); - ms = machdep->machspec; + 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 ((pc->flags & KDUMP) || + ((pc->flags & DISKDUMP) && + (*diskdump_flags & KDUMP_CMPRS_LOCAL))) { + /* + * For the KDump or FADump vmcore, use SP and IP values + * that are saved in ptregs. + */ + ret = ppc64_vmcore_stack_frame(bt_in, nip, ksp); + if (ret) + return TRUE; + } if (bt->task != tt->panic_task) { char cpu_frozen = FALSE; @@ -2385,38 +2467,14 @@ retry: check_intrstack = FALSE; goto retry; } - /* - * We didn't find what we were looking for, so try to use - * the SP and IP values saved in ptregs. + * We didn't find what we were looking for, so just use what was + * passed in the ELF header. */ - 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; - } + if (ur_nip && ur_ksp) { + *nip = ur_nip; + *ksp = ur_ksp; + return TRUE; } console("ppc64_get_dumpfile_stack_frame: cannot find SP for panic task\n"); commit 7e3936895386ea6e85a6dc01bc5027f8133d12bb Author: Dave Anderson Date: Mon Sep 17 14:33:08 2018 -0400 An addendum to crash commit 5fe78861ea1589084f6a2956a6ff63677c9269e1, this patch for the PPC64 "bt" command prevents an invalid error message from being displayed when an active non-panic task is interrupted while running in user space. Without the patch, the command correctly indicates "Task is running in user space", dumps the user-space exception frame, but then prints the invalid error message "bt: invalid kernel virtual address: ffffffffffffff90 type: Regs NIP value". (anderson@redhat.com) diff --git a/ppc64.c b/ppc64.c index 03fecd3..8badcde 100644 --- a/ppc64.c +++ b/ppc64.c @@ -2254,6 +2254,7 @@ ppc64_vmcore_stack_frame(struct bt_info *bt_in, ulong *nip, ulong *ksp) { struct ppc64_pt_regs *pt_regs; unsigned long unip; + int in_user_space = FALSE; pt_regs = (struct ppc64_pt_regs *)bt_in->machdep; if (!pt_regs || !pt_regs->gpr[1]) { @@ -2272,10 +2273,11 @@ ppc64_vmcore_stack_frame(struct bt_info *bt_in, ulong *nip, ulong *ksp) FAULT_ON_ERROR); *nip = unip; } else { - if (IN_TASK_VMA(bt_in->task, *ksp)) + if (IN_TASK_VMA(bt_in->task, *ksp)) { fprintf(fp, "%0lx: Task is running in user space\n", bt_in->task); - else + in_user_space = TRUE; + } else fprintf(fp, "%0lx: Invalid Stack Pointer %0lx\n", bt_in->task, *ksp); *nip = pt_regs->nip; @@ -2289,6 +2291,8 @@ ppc64_vmcore_stack_frame(struct bt_info *bt_in, ulong *nip, ulong *ksp) * Print the collected regs for the active task */ ppc64_print_regs(pt_regs); + if (in_user_space) + return TRUE; if (!IS_KVADDR(*ksp)) return FALSE; commit 599a6579aa916df7800f8e889d68e4287e4520dd Author: Dave Anderson Date: Thu Sep 27 14:14:31 2018 -0400 With Linux 4.19-rc1 commit 7d4340bb92a9df78e6e28152f3dd89d9bd82146b, titled "powerpc/mm: Increase MAX_PHYSMEM_BITS to 128TB with SPARSEMEM_VMEMMAP config", the PPC64 MAX_PHYSMEM_BITS value has been bumped up to 47. The appropriate update has been made in this patch. (hbathini@linux.ibm.com) diff --git a/defs.h b/defs.h index 80c61ef..5b64bb7 100644 --- a/defs.h +++ b/defs.h @@ -4054,6 +4054,7 @@ struct efi_memory_desc_t { #define _SECTION_SIZE_BITS 24 #define _MAX_PHYSMEM_BITS 44 #define _MAX_PHYSMEM_BITS_3_7 46 +#define _MAX_PHYSMEM_BITS_4_19 47 #endif /* PPC64 */ diff --git a/ppc64.c b/ppc64.c index 8badcde..ee2f76f 100644 --- a/ppc64.c +++ b/ppc64.c @@ -554,7 +554,10 @@ ppc64_init(int when) ppc64_vmemmap_init(); machdep->section_size_bits = _SECTION_SIZE_BITS; - if (THIS_KERNEL_VERSION >= LINUX(3,7,0)) + if ((machdep->flags & VMEMMAP) && + (THIS_KERNEL_VERSION >= LINUX(4,19,0))) + machdep->max_physmem_bits = _MAX_PHYSMEM_BITS_4_19; + else if (THIS_KERNEL_VERSION >= LINUX(3,7,0)) machdep->max_physmem_bits = _MAX_PHYSMEM_BITS_3_7; else machdep->max_physmem_bits = _MAX_PHYSMEM_BITS; commit 72cc0cba8a6cab14ca0961dff062d0384d307ce5 Author: Dave Anderson Date: Tue Oct 2 10:56:28 2018 -0400 Fix for the PPC64 "bt" command to recognize when a thread is running in OPAL firmware. Without the patch, the "bt" command indicates : Invalid Stack Pointer " (hbathini@linux.ibm.com) --- a/defs.h +++ b/defs.h @@ -5934,6 +5934,12 @@ struct ppc64_elf_prstatus { #ifdef PPC64 +struct ppc64_opal { + uint64_t base; + uint64_t entry; + uint64_t size; +}; + struct ppc64_vmemmap { unsigned long phys; unsigned long virt; @@ -5984,6 +5990,7 @@ struct machine_specific { ulong _page_accessed; int (*is_kvaddr)(ulong); int (*is_vmaddr)(ulong); + struct ppc64_opal opal; }; void ppc64_init(int); @@ -6001,6 +6008,7 @@ void ppc64_dump_machdep_table(ulong); * in the kernel is also 0x40. */ #define RADIX_MMU (0x40) +#define OPAL_FW (0x80) #define REGION_SHIFT (60UL) #define REGION_ID(addr) (((unsigned long)(addr)) >> REGION_SHIFT) --- a/ppc64.c +++ b/ppc64.c @@ -65,8 +65,26 @@ 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 int is_opal_context(ulong sp, ulong nip); void opalmsg(void); +static int is_opal_context(ulong sp, ulong nip) +{ + uint64_t opal_start, opal_end; + + if (!(machdep->flags & OPAL_FW)) + return FALSE; + + opal_start = machdep->machspec->opal.base; + opal_end = opal_start + machdep->machspec->opal.size; + + if (((sp >= opal_start) && (sp < opal_end)) || + ((nip >= opal_start) && (nip < opal_end))) + return TRUE; + + return FALSE; +} + static inline int is_hugepage(ulong pte) { if ((machdep->flags & BOOK3E) || @@ -241,6 +259,7 @@ struct machine_specific book3e_machine_s .is_vmaddr = book3e_is_vmaddr, }; +#define SKIBOOT_BASE 0x30000000 /* * Do all necessary machine-specific setup here. This is called several @@ -362,6 +381,16 @@ ppc64_init(int when) struct machine_specific *m = machdep->machspec; /* + * To determine if the kernel was running on OPAL based platform, + * use struct opal, which is populated with relevant values. + */ + if (symbol_exists("opal")) { + get_symbol_data("opal", sizeof(struct ppc64_opal), &(m->opal)); + if (m->opal.base == SKIBOOT_BASE) + machdep->flags |= OPAL_FW; + } + + /* * 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. @@ -712,6 +741,8 @@ ppc64_dump_machdep_table(ulong arg) fprintf(fp, "%sSWAP_ENTRY_L4", others++ ? "|" : ""); if (machdep->flags & RADIX_MMU) fprintf(fp, "%sRADIX_MMU", others++ ? "|" : ""); + if (machdep->flags & OPAL_FW) + fprintf(fp, "%sOPAL_FW", others++ ? "|" : ""); fprintf(fp, ")\n"); fprintf(fp, " kvbase: %lx\n", machdep->kvbase); @@ -2257,7 +2288,11 @@ ppc64_vmcore_stack_frame(struct bt_info { struct ppc64_pt_regs *pt_regs; unsigned long unip; - int in_user_space = FALSE; + /* + * TRUE: task is running in a different context (userspace, OPAL..) + * FALSE: task is probably running in kernel space. + */ + int out_of_context = FALSE; pt_regs = (struct ppc64_pt_regs *)bt_in->machdep; if (!pt_regs || !pt_regs->gpr[1]) { @@ -2270,20 +2305,25 @@ ppc64_vmcore_stack_frame(struct bt_info 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 { + *nip = pt_regs->nip; if (IN_TASK_VMA(bt_in->task, *ksp)) { fprintf(fp, "%0lx: Task is running in user space\n", bt_in->task); - in_user_space = TRUE; + out_of_context = TRUE; + } else if (is_opal_context(*ksp, *nip)) { + fprintf(fp, "%0lx: Task is running in OPAL (firmware) context\n", + bt_in->task); + out_of_context = TRUE; } else fprintf(fp, "%0lx: Invalid Stack Pointer %0lx\n", bt_in->task, *ksp); - *nip = pt_regs->nip; } if (bt_in->flags && @@ -2294,7 +2334,8 @@ ppc64_vmcore_stack_frame(struct bt_info * Print the collected regs for the active task */ ppc64_print_regs(pt_regs); - if (in_user_space) + + if (out_of_context) return TRUE; if (!IS_KVADDR(*ksp)) return FALSE; @@ -2828,7 +2869,6 @@ ppc64_get_smp_cpus(void) */ #define SKIBOOT_CONSOLE_DUMP_START 0x31000000 #define SKIBOOT_CONSOLE_DUMP_SIZE 0x100000 -#define SKIBOOT_BASE 0x30000000 #define ASCII_UNLIMITED ((ulong)(-1) >> 1) void @@ -2841,10 +2881,6 @@ opalmsg(void) 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; @@ -2856,25 +2892,13 @@ opalmsg(void) long count = SKIBOOT_CONSOLE_DUMP_SIZE; ulonglong addr = SKIBOOT_CONSOLE_DUMP_START; + if (!(machdep->flags & OPAL_FW)) + error(FATAL, "dump was not captured on OPAL based system"); + 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;