[Crash-utility] [PATCHv4] crash-utility/arm64: store phy_offset and memstart_addr separately

Pingfan Liu piliu at redhat.com
Tue Apr 27 02:42:59 UTC 2021


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 at redhat.com>
Cc: HAGIO KAZUHITO <k-hagio-ab at nec.com>
Cc: Lianbo Jiang <lijiang at redhat.com>
Cc: Bhupesh Sharma <bhupesh.sharma at linaro.org>
To: crash-utility at redhat.com
---
v3 -> v4:
  use _PAGE_OFFSET(va)  to define MEMSTART_ADDR_OFFSET
  and use the formula "#define __lm_to_phys(addr)    (((addr) & ~PAGE_OFFSET) + PHYS_OFFSET)"
  for arm64_VTOP()

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 | 45 ++++++++++++++++++++++++++++++++++++++++-----
 defs.h  |  3 +++
 2 files changed, 43 insertions(+), 5 deletions(-)

diff --git a/arm64.c b/arm64.c
index 4787fa6..84addde 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);
@@ -687,6 +691,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);
@@ -987,7 +992,12 @@ arm64_calc_physvirt_offset(void)
 	ulong physvirt_offset;
 	struct syment *sp;
 
-	ms->physvirt_offset = ms->phys_offset - ms->page_offset;
+	/*
+	 * 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;
 
 	if ((sp = kernel_symbol_search("physvirt_offset")) &&
 			machdep->machspec->kimage_voffset) {
@@ -1002,6 +1012,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;
 
@@ -1028,7 +1040,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;
 				}
@@ -1080,7 +1096,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");
@@ -1180,8 +1207,16 @@ 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) {
+			ulong paddr;
+
+			/*
+			 * #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 f9c711c..0a152f1 100644
--- a/defs.h
+++ b/defs.h
@@ -3289,7 +3289,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;
-- 
2.29.2




More information about the Crash-utility mailing list