Crash encounters a bug like the following: ... SECTION_SIZE_BITS: 30 CONFIG_ARM64_VA_BITS: 52 VA_BITS_ACTUAL: 48 (calculated) VA_BITS: 48 PAGE_OFFSET: ffff000000000000 VA_START: ffff800000000000 modules: ffff800008000000 - ffff80000fffffff vmalloc: ffff800010000000 - ffffffdfdffeffff kernel image: ffff800010000000 - ffff800012750000 vmemmap: ffffffdfffe00000 - ffffffffffffffff <readmem: ffff800011c53bc8, KVADDR, "nr_irqs", 4, (FOE), b47bdc> <read_kdump: addr: ffff800011c53bc8 paddr: eb453bc8 cnt: 4> read_netdump: addr: ffff800011c53bc8 paddr: eb453bc8 cnt: 4 offset: 1c73bc8 irq_stack_ptr: type: 1, TYPE_CODE_PTR target_typecode: 8, TYPE_CODE_INT target_length: 8 length: 8 GNU_GET_DATATYPE[thread_union]: returned via gdb_error_hook <readmem: ffff000b779c0050, KVADDR, "IRQ stack pointer", 8, (ROE), 3a37bea0> <read_kdump: addr: ffff000b779c0050 paddr: fff1000bf79c0050 cnt: 8> read_netdump: READ_ERROR: offset not found for paddr: fff1000bf79c0050 crash: read error: kernel virtual address: ffff000b779c0050 type: "IRQ stack pointer" <readmem: ffff000b77a60050, KVADDR, "IRQ stack pointer", 8, (ROE), 3a37bea8> <read_kdump: addr: ffff000b77a60050 paddr: fff1000bf7a60050 cnt: 8> read_netdump: READ_ERROR: offset not found for paddr: fff1000bf7a60050 ... Apparently, for a normal system, the 'paddr: fff1000bf79c0050' is unreasonable. This bug connects with kernel commit 7bc1a0f9e176 ("arm64: mm: use single quantity to represent the PA to VA translation"), memstart_addr can be negative, which makes it different from real phys_offset. If using memstart_addr to calculate the real paddr, the unreasonable paddr will be got. Furthermore, in crash utility, PTOV() needs memstart_addr to calculate VA from PA, while getting PFN offset in a dumpfile, phys_offset is required. To serve the different purpose, using phys_offset_nominal and phys_offset to store them. Signed-off-by: Pingfan Liu <piliu@xxxxxxxxxx> Cc: HAGIO KAZUHITO <k-hagio-ab@xxxxxxx> Cc: Lianbo Jiang <lijiang@xxxxxxxxxx> Cc: Bhupesh Sharma <bhupesh.sharma@xxxxxxxxxx> To: crash-utility@xxxxxxxxxx --- v5 -> v6: introduce arm64_PTO() to tackle compability. v4 -> v5: change PTOV() based on kernel formula: #define __phys_to_virt(x) ((unsigned long)((x) - PHYS_OFFSET) | PAGE_OFFSET) v3 -> v4: use _PAGE_OFFSET(va) to define MEMSTART_ADDR_OFFSET v2 -> v3: rename ms->memstart_addr as ms->phys_offset_nominal ( I keep the name as phys_offset* since it is in accordance with other platform conventions) --- arm64.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- defs.h | 17 ++++++++++---- 2 files changed, 79 insertions(+), 10 deletions(-) diff --git a/arm64.c b/arm64.c index e678982..e4ded3c 100644 --- a/arm64.c +++ b/arm64.c @@ -23,6 +23,10 @@ #include <sys/ioctl.h> #define NOT_IMPLEMENTED(X) error((X), "%s: function not implemented\n", __func__) +/* + * _PAGE_OFFSET() refers to arch/arm64/include/asm/memory.h + */ +#define _PAGE_OFFSET(va) (-1UL << (va)) static struct machine_specific arm64_machine_specific = { 0 }; static int arm64_verify_symbol(const char *, ulong, char); @@ -691,6 +695,7 @@ arm64_dump_machdep_table(ulong arg) fprintf(fp, " kimage_voffset: %016lx\n", ms->kimage_voffset); } fprintf(fp, " phys_offset: %lx\n", ms->phys_offset); + fprintf(fp, " phys_offset_nominal: %lx\n", ms->phys_offset_nominal); 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); @@ -991,7 +996,17 @@ arm64_calc_physvirt_offset(void) ulong physvirt_offset; struct syment *sp; - ms->physvirt_offset = ms->phys_offset - ms->page_offset; + /* if flipped but having 'physvirt_offset', ms->physvirt_offset is overwritten in this func */ + if (ms->mmlayout_flags & MMLAYOUT_FLAGS_FLIP) { + /* + * source arch/arm64/include/asm/memory.h + * #define __lm_to_phys(addr) (((addr) & ~PAGE_OFFSET) + PHYS_OFFSET) + * the part "addr & ~PAGE_OFFSET" is done in arm64_VTOP() + */ + ms->physvirt_offset = ms->phys_offset_nominal; + } else { + ms->physvirt_offset = ms->phys_offset - ms->page_offset; + } if ((sp = kernel_symbol_search("physvirt_offset")) && machdep->machspec->kimage_voffset) { @@ -1007,6 +1022,8 @@ arm64_calc_physvirt_offset(void) static void arm64_calc_phys_offset(void) { +#define MEMSTART_ADDR_OFFSET _PAGE_OFFSET(48) - _PAGE_OFFSET(52) + struct machine_specific *ms = machdep->machspec; ulong phys_offset; @@ -1033,7 +1050,11 @@ arm64_calc_phys_offset(void) ms->kimage_voffset && (sp = kernel_symbol_search("memstart_addr"))) { if (pc->flags & PROC_KCORE) { if ((string = pc->read_vmcoreinfo("NUMBER(PHYS_OFFSET)"))) { - ms->phys_offset = htol(string, QUIET, NULL); + ms->phys_offset_nominal = htol(string, QUIET, NULL); + if (ms->phys_offset_nominal < 0) + ms->phys_offset = ms->phys_offset_nominal + MEMSTART_ADDR_OFFSET; + else + ms->phys_offset = ms->phys_offset_nominal; free(string); return; } @@ -1085,7 +1106,18 @@ arm64_calc_phys_offset(void) } else if (DISKDUMP_DUMPFILE() && diskdump_phys_base(&phys_offset)) { ms->phys_offset = phys_offset; } else if (KDUMP_DUMPFILE() && arm64_kdump_phys_base(&phys_offset)) { - ms->phys_offset = phys_offset; + /* + * When running a 52bits kernel on 48bits hardware. Kernel plays a trick: + * if (IS_ENABLED(CONFIG_ARM64_VA_BITS_52) && (vabits_actual != 52)) + * memstart_addr -= _PAGE_OFFSET(48) - _PAGE_OFFSET(52); + * + * In crash, this should be detected to get a real physical start address. + */ + ms->phys_offset_nominal = phys_offset; + if ((long)phys_offset < 0) + ms->phys_offset = phys_offset + MEMSTART_ADDR_OFFSET; + else + ms->phys_offset = phys_offset; } else { error(WARNING, "phys_offset cannot be determined from the dumpfile.\n"); @@ -1175,6 +1207,23 @@ arm64_init_kernel_pgd(void) vt->kernel_pgd[i] = value; } +ulong arm64_PTOV(ulong paddr) +{ + ulong v; + struct machine_specific *ms = machdep->machspec; + + /* + * Either older kernel before kernel has 'physvirt_offset' or newer kernel which + * removes 'physvirt_offset' has the same formula + */ + if (!(ms->mmlayout_flags & MMLAYOUT_FLAGS_HAS_PHYSVIRT_OFFSET)) + v = (paddr - ms->physvirt_offset) | PAGE_OFFSET; + else + v = paddr - ms->physvirt_offset; + + return v; +} + ulong arm64_VTOP(ulong addr) { @@ -1185,8 +1234,21 @@ arm64_VTOP(ulong addr) return addr - machdep->machspec->kimage_voffset; } - if (addr >= machdep->machspec->page_offset) - return addr + machdep->machspec->physvirt_offset; + if (addr >= machdep->machspec->page_offset) { + struct machine_specific *ms = machdep->machspec; + ulong paddr; + + if (!(ms->mmlayout_flags & MMLAYOUT_FLAGS_FLIP) || (ms->mmlayout_flags & MMLAYOUT_FLAGS_HAS_PHYSVIRT_OFFSET)) { + paddr = addr; + } else { + /* + * #define __lm_to_phys(addr) (((addr) & ~PAGE_OFFSET) + PHYS_OFFSET) + */ + paddr = addr & ~ _PAGE_OFFSET(machdep->machspec->CONFIG_ARM64_VA_BITS); + } + paddr += machdep->machspec->physvirt_offset; + return paddr; + } else if (machdep->machspec->kimage_voffset) return addr - machdep->machspec->kimage_voffset; else /* no randomness */ diff --git a/defs.h b/defs.h index 6da3e75..edc2200 100644 --- a/defs.h +++ b/defs.h @@ -3078,11 +3078,6 @@ typedef u64 pte_t; #define _64BIT_ #define MACHINE_TYPE "ARM64" -#define PTOV(X) \ - ((unsigned long)(X) - (machdep->machspec->physvirt_offset)) - -#define VTOP(X) arm64_VTOP((ulong)(X)) - #define USERSPACE_TOP (machdep->machspec->userspace_top) #define PAGE_OFFSET (machdep->machspec->page_offset) #define VMALLOC_START (machdep->machspec->vmalloc_start_addr) @@ -3092,6 +3087,14 @@ typedef u64 pte_t; #define MODULES_VADDR (machdep->machspec->modules_vaddr) #define MODULES_END (machdep->machspec->modules_end) +/* + * The formula: + * #define __phys_to_virt(x) ((unsigned long)((x) - PHYS_OFFSET) | PAGE_OFFSET) + */ +#define PTOV(X) arm64_PTOV((ulong)(X)) + +#define VTOP(X) arm64_VTOP((ulong)(X)) + #define IS_VMALLOC_ADDR(X) arm64_IS_VMALLOC_ADDR((ulong)(X)) #define PAGEBASE(X) (((ulong)(X)) & (ulong)machdep->pagemask) @@ -3290,7 +3293,10 @@ struct machine_specific { ulong vmemmap_end; ulong modules_vaddr; ulong modules_end; + /* real physical offset */ ulong phys_offset; + /* read from kernel symbol memstart_addr */ + long phys_offset_nominal; ulong __exception_text_start; ulong __exception_text_end; struct arm64_pt_regs *panic_task_regs; @@ -5844,6 +5850,7 @@ void unwind_backtrace(struct bt_info *); void arm64_init(int); void arm64_dump_machdep_table(ulong); ulong arm64_VTOP(ulong); +ulong arm64_PTOV(ulong); int arm64_IS_VMALLOC_ADDR(ulong); ulong arm64_swp_type(ulong); ulong arm64_swp_offset(ulong); -- 2.29.2 -- Crash-utility mailing list Crash-utility@xxxxxxxxxx https://listman.redhat.com/mailman/listinfo/crash-utility